From 3834e45facfe784da73dd34e3906266b108ed5cf Mon Sep 17 00:00:00 2001 From: Micah Richert Date: Fri, 31 Jan 2014 09:06:02 -0800 Subject: [PATCH] add ath6k wifi driver to the kernel Note that building as =y requires compiling the firmware into the kernel or initrd and softmac is not loaded, so always defaults to one address. --- drivers/net/wireless/ath/Kconfig | 2 + drivers/net/wireless/ath/Makefile | 2 +- drivers/net/wireless/ath/ath6kl-3.5/Kconfig | 40 + drivers/net/wireless/ath/ath6kl-3.5/Makefile | 84 + .../ath/ath6kl-3.5/android_sdio/Android.mk | 34 + .../ath/ath6kl-3.5/android_sdio/Kbuild | 71 + drivers/net/wireless/ath/ath6kl-3.5/ap.c | 1450 ++++ drivers/net/wireless/ath/ath6kl-3.5/ap.h | 207 + .../net/wireless/ath/ath6kl-3.5/ath_netlink.c | 174 + .../net/wireless/ath/ath6kl-3.5/ath_netlink.h | 31 + .../net/wireless/ath/ath6kl-3.5/bdata.bin.c | 562 ++ drivers/net/wireless/ath/ath6kl-3.5/bmi.c | 622 ++ drivers/net/wireless/ath/ath6kl-3.5/bmi.h | 279 + .../net/wireless/ath/ath6kl-3.5/cfg80211.c | 6775 +++++++++++++++++ .../net/wireless/ath/ath6kl-3.5/cfg80211.h | 130 + .../wireless/ath/ath6kl-3.5/cfg80211_btcoex.c | 82 + .../wireless/ath/ath6kl-3.5/cfg80211_btcoex.h | 17 + drivers/net/wireless/ath/ath6kl-3.5/common.h | 89 + drivers/net/wireless/ath/ath6kl-3.5/core.h | 1885 +++++ drivers/net/wireless/ath/ath6kl-3.5/debug.c | 5220 +++++++++++++ drivers/net/wireless/ath/ath6kl-3.5/debug.h | 326 + drivers/net/wireless/ath/ath6kl-3.5/diag.c | 1443 ++++ .../net/wireless/ath/ath6kl-3.5/diagnose.h | 375 + drivers/net/wireless/ath/ath6kl-3.5/epping.h | 113 + .../net/wireless/ath/ath6kl-3.5/fw.ram.bin.c | 4408 +++++++++++ drivers/net/wireless/ath/ath6kl-3.5/hif-ops.h | 287 + drivers/net/wireless/ath/ath6kl-3.5/hif.c | 730 ++ drivers/net/wireless/ath/ath6kl-3.5/hif.h | 314 + drivers/net/wireless/ath/ath6kl-3.5/htc-ops.h | 122 + drivers/net/wireless/ath/ath6kl-3.5/htc.c | 3088 ++++++++ drivers/net/wireless/ath/ath6kl-3.5/htc.h | 692 ++ .../net/wireless/ath/ath6kl-3.5/htc_pipe.c | 2537 ++++++ drivers/net/wireless/ath/ath6kl-3.5/htcoex.c | 664 ++ drivers/net/wireless/ath/ath6kl-3.5/htcoex.h | 122 + drivers/net/wireless/ath/ath6kl-3.5/init.c | 3302 ++++++++ drivers/net/wireless/ath/ath6kl-3.5/main.c | 2399 ++++++ drivers/net/wireless/ath/ath6kl-3.5/msm.c | 526 ++ drivers/net/wireless/ath/ath6kl-3.5/p2p.c | 2270 ++++++ drivers/net/wireless/ath/ath6kl-3.5/p2p.h | 343 + drivers/net/wireless/ath/ath6kl-3.5/pm.c | 56 + drivers/net/wireless/ath/ath6kl-3.5/pm.h | 20 + .../wireless/ath/ath6kl-3.5/qcomwlan_pwrif.c | 256 + drivers/net/wireless/ath/ath6kl-3.5/reg.c | 820 ++ drivers/net/wireless/ath/ath6kl-3.5/reg.h | 368 + drivers/net/wireless/ath/ath6kl-3.5/regdb.c | 3430 +++++++++ drivers/net/wireless/ath/ath6kl-3.5/rttapi.h | 32 + drivers/net/wireless/ath/ath6kl-3.5/rttm.c | 186 + drivers/net/wireless/ath/ath6kl-3.5/rttm.h | 45 + drivers/net/wireless/ath/ath6kl-3.5/sdio.c | 1529 ++++ drivers/net/wireless/ath/ath6kl-3.5/target.h | 411 + .../net/wireless/ath/ath6kl-3.5/testmode.c | 827 ++ .../net/wireless/ath/ath6kl-3.5/testmode.h | 55 + drivers/net/wireless/ath/ath6kl-3.5/txrx.c | 3379 ++++++++ drivers/net/wireless/ath/ath6kl-3.5/usb.c | 2631 +++++++ .../ath/ath6kl-3.5/wlan_location_defs.h | 320 + drivers/net/wireless/ath/ath6kl-3.5/wmi.c | 5854 ++++++++++++++ drivers/net/wireless/ath/ath6kl-3.5/wmi.h | 3486 +++++++++ .../net/wireless/ath/ath6kl-3.5/wmi_btcoex.c | 317 + .../net/wireless/ath/ath6kl-3.5/wmi_btcoex.h | 169 + include/linux/qca6234_pwrif.h | 42 + 60 files changed, 66049 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/Kconfig create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/Makefile create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/android_sdio/Android.mk create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/android_sdio/Kbuild create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/ap.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/ap.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/bdata.bin.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/bmi.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/bmi.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/cfg80211.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/cfg80211.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/common.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/core.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/debug.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/debug.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/diag.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/diagnose.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/epping.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/fw.ram.bin.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/hif-ops.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/hif.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/hif.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/htc-ops.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/htc.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/htc.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/htc_pipe.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/htcoex.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/htcoex.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/init.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/main.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/msm.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/p2p.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/p2p.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/pm.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/pm.h create mode 100755 drivers/net/wireless/ath/ath6kl-3.5/qcomwlan_pwrif.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/reg.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/reg.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/regdb.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/rttapi.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/rttm.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/rttm.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/sdio.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/target.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/testmode.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/testmode.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/txrx.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/usb.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/wlan_location_defs.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/wmi.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/wmi.h create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.c create mode 100644 drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.h create mode 100644 include/linux/qca6234_pwrif.h diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 09602241901b..694baa7e81b1 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -1,5 +1,7 @@ menuconfig ATH_COMMON tristate "Atheros Wireless Cards" + select WIRELESS_EXT + select CFG80211_WEXT depends on CFG80211 && (!UML || BROKEN) ---help--- This will enable the support for the Atheros wireless drivers. diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index d716b748e574..d0c2f0d65987 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -1,7 +1,7 @@ obj-$(CONFIG_ATH5K) += ath5k/ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ -obj-$(CONFIG_ATH6KL) += ath6kl/ +obj-$(CONFIG_ATH6KL) += ath6kl-3.5/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/ath6kl-3.5/Kconfig b/drivers/net/wireless/ath/ath6kl-3.5/Kconfig new file mode 100644 index 000000000000..ca05015ae818 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/Kconfig @@ -0,0 +1,40 @@ +config ATH6KL + tristate "Atheros mobile chipsets support" + +config ATH6KL_SDIO + tristate "Atheros ath6kl SDIO support" + depends on ATH6KL + depends on MMC + depends on CFG80211 + select PM_SLEEP + select + ---help--- + This module adds support for wireless adapters based on + Atheros AR6003 and AR6004 chipsets running over SDIO. If you + choose to build it as a module, it will be called ath6kl_sdio. + Please note that AR6002 and AR6001 are not supported by this + driver. + +config ATH6KL_USB + tristate "Atheros ath6kl USB support" + depends on ATH6KL + depends on USB + depends on CFG80211 + depends on EXPERIMENTAL + ---help--- + This module adds support for wireless adapters based on + Atheros AR6004 chipset running over USB. This is still under + implementation and it isn't functional. If you choose to + build it as a module, it will be called ath6kl_usb. + +config ATH6KL_DEBUG + bool "Atheros ath6kl debugging" + depends on ATH6KL + ---help--- + Enables debug support + +config ATH6KL_ENABLE_FW_API2 + bool "Enable Atheros ath6kl FW API2 " + depends on ATH6KL + ---help--- + Enables Atheros ath6kl FW API2 diff --git a/drivers/net/wireless/ath/ath6kl-3.5/Makefile b/drivers/net/wireless/ath/ath6kl-3.5/Makefile new file mode 100644 index 000000000000..32aa6ea1d186 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/Makefile @@ -0,0 +1,84 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2004-2010 Atheros Communications 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. +# +# +# +# Author(s): ="Atheros" +#------------------------------------------------------------------------------ + +obj-$(CONFIG_ATH6KL_SDIO) := ath6kl_sdio.o +ath6kl_sdio-y += debug.o +ath6kl_sdio-y += hif.o +ath6kl_sdio-y += htc.o +ath6kl_sdio-y += bmi.o +ath6kl_sdio-y += cfg80211.o +ath6kl_sdio-y += cfg80211_btcoex.o +ath6kl_sdio-y += init.o +ath6kl_sdio-y += main.o +ath6kl_sdio-y += txrx.o +ath6kl_sdio-y += wmi.o +ath6kl_sdio-y += wmi_btcoex.o +ath6kl_sdio-y += sdio.o +ath6kl_sdio-$(CONFIG_NL80211_TESTMODE) += testmode.o +ath6kl_sdio-y += rttm.o +ath6kl_sdio-y += diag.o +ath6kl_sdio-y += htcoex.o +ath6kl_sdio-y += ath_netlink.o +ath6kl_sdio-$(CONFIG_QC_INTERNAL) += intra_reg.o +ath6kl_sdio-y += pm.o +ath6kl_sdio-y += p2p.o +ath6kl_sdio-y += ap.o +ath6kl_sdio-y += reg.o +ath6kl_sdio-y += regdb.o +ath6kl_sdio-y += qcomwlan_pwrif.o + +obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o +ath6kl_usb-y += debug.o +ath6kl_usb-y += htc_pipe.o +ath6kl_usb-y += bmi.o +ath6kl_usb-y += cfg80211.o +ath6kl_usb-y += cfg80211_btcoex.o +ath6kl_usb-y += init.o +ath6kl_usb-y += main.o +ath6kl_usb-y += txrx.o +ath6kl_usb-y += wmi.o +ath6kl_usb-y += wmi_btcoex.o +ath6kl_usb-y += usb.o +ath6kl_usb-$(CONFIG_NL80211_TESTMODE) += testmode.o +ath6kl_usb-y += rttm.o +ath6kl_usb-y += diag.o +ath6kl_usb-y += htcoex.o +ath6kl_usb-y += ath_netlink.o +ath6kl_usb-$(CONFIG_QC_INTERNAL) += intra_reg.o +ath6kl_usb-y += pm.o +ath6kl_usb-y += p2p.o +ath6kl_usb-y += ap.o +ath6kl_usb-y += reg.o +ath6kl_usb-y += regdb.o +ath6kl_usb-$(CONFIG_ATHTST_SUPPORT) += ce_athtst.o +ath6kl_usb-$(CONFIG_ACS_SUPPORT) += acs.o +ath6kl_usb-$(CONFIG_ACL_SUPPORT) += acl_ioctl.o +ath6kl_usb-$(CONFIG_TX99_SUPPORT) += tx99_ioctl.o + +ccflags-$(CONFIG_QC_INTERNAL) += -DCONFIG_QC_INTERNAL +ccflags-$(CONFIG_FPGA) += -DCONFIG_FPGA +ccflags-y += -D__CHECK_ENDIAN__ +ccflags-y += -DCONFIG_BTCOEX_OLCA_3_5 +ccflags-$(CONFIG_ATH6KL_UB134) += -DCONFIG_ATH6KL_UDP_TPUT_WAR + +EXTRA_CFLAGS += -I${KBUILDPATH}/drivers/net/wireless/ath/ath6kl diff --git a/drivers/net/wireless/ath/ath6kl-3.5/android_sdio/Android.mk b/drivers/net/wireless/ath/ath6kl-3.5/android_sdio/Android.mk new file mode 100644 index 000000000000..4ca30706cac1 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/android_sdio/Android.mk @@ -0,0 +1,34 @@ +LOCAL_PATH := $(call my-dir) + +ifeq ($(call is-android-codename,JELLY_BEAN),true) + DLKM_DIR := $(TOP)/device/qcom/common/dlkm +else + DLKM_DIR := build/dlkm +endif + +# cfg80211_sdio.ko will only use at build time, +# NOT include in the output image. +# Therefore keep this LOCAL_MODULE_TAGS as optional +# ath6kl_sdio modules will use the cfg80211.ko on top level +ifeq ($(BOARD_HAS_CFG80211_KERNEL3_4), true) +# Do nothing +else +ifeq ($(BOARD_HAS_CFG80211_KERNEL3_7), true) +# Do nothing +else +include $(CLEAR_VARS) +LOCAL_MODULE := cfg80211_sdio.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_KBUILD_NAME := cfg80211.ko +LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/modules/ath6kl-3.5 +include $(DLKM_DIR)/AndroidKernelModule.mk +endif +endif + +$(shell ln -s ../../../../../../ $(LOCAL_PATH)/src) +include $(CLEAR_VARS) +LOCAL_MODULE := ath6kl_sdio.ko +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE_KBUILD_NAME := wlan.ko +LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/modules/ath6kl-3.5 +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/net/wireless/ath/ath6kl-3.5/android_sdio/Kbuild b/drivers/net/wireless/ath/ath6kl-3.5/android_sdio/Kbuild new file mode 100644 index 000000000000..f62defded69d --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/android_sdio/Kbuild @@ -0,0 +1,71 @@ +ifeq ($(BUILD_ATH6KL_VER_35), 1) +obj-m += wlan.o + +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/debug.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/bmi.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/cfg80211.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/init.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/main.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/txrx.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/wmi.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/testmode.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/rttm.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/diag.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/htcoex.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/pm.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/p2p.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/ap.o + +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/msm.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/htc.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/hif.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/sdio.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/reg.o +wlan-y += src/drivers/net/wireless/ath/ath6kl-3.5/regdb.o +endif + +ifeq ($(HAVE_CFG80211), 1) + +obj-$(CONFIG_CFG80211) += cfg80211.o + +cfg80211-y += src/net/wireless/core.o src/net/wireless/sysfs.o src/net/wireless/radiotap.o src/net/wireless/util.o src/net/wireless/reg.o src/net/wireless/scan.o src/net/wireless/nl80211.o +cfg80211-y += src/net/wireless/mlme.o src/net/wireless/ibss.o src/net/wireless/sme.o src/net/wireless/chan.o src/net/wireless/ethtool.o src/net/wireless/mesh.o +cfg80211-$(CONFIG_CFG80211_DEBUGFS) += src/net/wireless/debugfs.o +cfg80211-y += src/net/wireless/wext-compat.o src/net/wireless/wext-sme.o +cfg80211-y += src/net/wireless/regdb.o + +$(obj)/src/net/wireless/regdb.c: $(PWD)/$(src)/src/net/wireless/db.txt $(PWD)/$(src)/src/net/wireless/genregdb.awk + @$(AWK) -f $(PWD)/$(src)/src/net/wireless/genregdb.awk < $< > $@ + +clean-files := src/net/wireless/regdb.c + +ccflags-y += -DCONFIG_ATH6KL_DEBUG +ccflags-y += -DCONFIG_NL80211_TESTMODE +ccflags-y += -DCONFIG_CFG80211_DEFAULT_PS +ccflags-y += -DCONFIG_CFG80211_WEXT +ccflags-y += -DCONFIG_CFG80211_INTERNAL_REGDB +ccflags-y += -D__CHECK_ENDIAN__ + +ccflags-y += -I../external/compat-wireless/include +ccflags-y += -I../external/compat-wireless/net/wireless +ccflags-y += -include ../../include/linux/ieee80211.h +ccflags-y += -include ../../include/linux/nl80211.h +ccflags-y += -include ../../include/net/cfg80211.h +ccflags-y += -include ../../include/linux/compat-2.6.h +ccflags-y += -include ../../$(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include/linux/version.h +else +ifeq ($(HAVE_CFG80211_KERNEL3_4), 1) +ccflags-y += -DATH6KL_SUPPORT_NL80211_KERNEL3_4 +endif +ifeq ($(HAVE_CFG80211_KERNEL3_7), 1) +ccflags-y += -DATH6KL_SUPPORT_NL80211_KERNEL3_6 +ccflags-y += -DATH6KL_SUPPORT_NETLINK_KERNEL3_6 +ccflags-y += -DATH6KL_SUPPORT_NETLINK_KERNEL3_7 +endif +ccflags-y += -DCONFIG_ATH6KL_DEBUG +ccflags-y += -DCONFIG_ATH6KL_REGDOMAIN +ccflags-y += -D__CHECK_ENDIAN__ +endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/ap.c b/drivers/net/wireless/ath/ath6kl-3.5/ap.c new file mode 100644 index 000000000000..de258f2dbd00 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/ap.c @@ -0,0 +1,1450 @@ +/* + * Copyright (c) 2004-2012 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "debug.h" + +static inline enum ap_keepalive_adjust __ap_keepalive_adjust_txrx_time( + struct ath6kl_sta *conn, u16 last_txrx_time, unsigned long now) +{ + u32 diff_ms; + enum ap_keepalive_adjust adjust_result = AP_KA_ADJ_ERROR; + + if (conn->last_txrx_time_tgt) { + if (last_txrx_time >= conn->last_txrx_time_tgt) + diff_ms = (last_txrx_time - + conn->last_txrx_time_tgt) << 10; + else /* wrap */ + diff_ms = (0xffff - + (conn->last_txrx_time_tgt - last_txrx_time)) + << 10; + + /* Update to last one. */ + conn->last_txrx_time_tgt = last_txrx_time; + conn->last_txrx_time += msecs_to_jiffies(diff_ms); + + if (conn->last_txrx_time > now) + conn->last_txrx_time = now; + + adjust_result = AP_KA_ADJ_ADJUST; + } else { + /* First updated, treat as base time. */ + conn->last_txrx_time_tgt = last_txrx_time; + conn->last_txrx_time = now; + + adjust_result = AP_KA_ADJ_BASESET; + } + + return adjust_result; +} + +static int _ap_keepalive_update_check_txrx_time(struct ath6kl_vif *vif, + struct ath6kl_sta *conn, + u16 last_txrx_time) +{ + struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; + unsigned long now = jiffies; + enum ap_keepalive_adjust adjust_result; + int action = AP_KA_ACTION_NONE; + + spin_lock_bh(&conn->lock); + adjust_result = __ap_keepalive_adjust_txrx_time(conn, + last_txrx_time, + now); + if (adjust_result == AP_KA_ADJ_ADJUST) { + if ((now - conn->last_txrx_time) >= + msecs_to_jiffies(ap_keepalive->ap_ka_interval)) + action = AP_KA_ACTION_POLL; + + if ((now - conn->last_txrx_time) >= + msecs_to_jiffies(ap_keepalive->ap_ka_remove_time)) + action = AP_KA_ACTION_REMOVE; + } + spin_unlock_bh(&conn->lock); + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive check (aid %d tgt/hst/now %d %ld %ld %s\n", + conn->aid, + conn->last_txrx_time_tgt, + conn->last_txrx_time, + now, + (action == AP_KA_ACTION_NONE) ? "NONE" : + ((action == AP_KA_ACTION_POLL) ? "POLL" : "REMOVE")); + + return action; +} + +static int ap_keepalive_preload_txrx_time(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + int ret = 0; + + /* Get AP stats only at least one station associated. */ + if (vif->sta_list_index) + ret = ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx); + + return ret; +} + +static int ap_keepalive_update_check_txrx_time(struct ath6kl_vif *vif) +{ +#define _KICKOUT_CAUSE (WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY) + struct ath6kl *ar = vif->ar; + struct wmi_ap_mode_stat *ap_stats = &vif->ap_stats; + struct wmi_per_sta_stat *per_sta_stat; + struct ath6kl_sta *conn; + int i, action; + + if (test_bit(STATS_UPDATE_PEND, &vif->flags)) { + ath6kl_info("somebody still query now and ignore it.\n"); + return -EBUSY; + } + + /* Now, tranfer to host time and update to vif->sta_list[]. */ + for (i = 0; i < AP_MAX_NUM_STA; i++) { + per_sta_stat = &ap_stats->sta[i]; + if (per_sta_stat->aid) { + conn = ath6kl_find_sta_by_aid(vif, per_sta_stat->aid); + if (conn) { + action = _ap_keepalive_update_check_txrx_time( + vif, + conn, + per_sta_stat->last_txrx_time); + + if (action == AP_KA_ACTION_POLL) { + ath6kl_wmi_ap_poll_sta(ar->wmi, + vif->fw_vif_idx, + conn->aid); + } else if (action == AP_KA_ACTION_REMOVE) { + ath6kl_wmi_ap_set_mlme(ar->wmi, + vif->fw_vif_idx, + WMI_AP_DEAUTH, + conn->mac, + _KICKOUT_CAUSE); + } + } else + ath6kl_err("can't find this AID %d\n", + per_sta_stat->aid); + } + } + + return 0; +#undef _KICKOUT_CAUSE +} + +static void ap_keepalive_start(unsigned long arg) +{ + struct ap_keepalive_info *ap_keepalive = + (struct ap_keepalive_info *) arg; + struct ath6kl_vif *vif = ap_keepalive->vif; + int ret; + + BUG_ON(!vif); + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive timer (vif idx %d) sta_list_index %x %s\n", + vif->fw_vif_idx, + vif->sta_list_index, + (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_PRELOAD_STAT) ? + "preload" : "update check"); + + if ((vif->nw_type == AP_NETWORK) && + test_bit(CONNECTED, &vif->flags)) { + if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_PRELOAD_STAT) { + ret = ap_keepalive_preload_txrx_time(vif); + if (ret) + ath6kl_err("preload last_txrx_time fail\n"); + } else { + /* Update and check last TXRX time each stations. */ + ret = ap_keepalive_update_check_txrx_time(vif); + if (ret) + ath6kl_err("updatecheck last_txrx_time fail\n"); + } + + if ((ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_START) && + (ap_keepalive->ap_ka_interval)) { + if (ap_keepalive->flags & + ATH6KL_AP_KA_FLAGS_PRELOAD_STAT) { + mod_timer(&ap_keepalive->ap_ka_timer, + jiffies + + msecs_to_jiffies( + ATH6KL_AP_KA_PRELOAD_LEADTIME)); + + ap_keepalive->flags &= + ~ATH6KL_AP_KA_FLAGS_PRELOAD_STAT; + } else { + mod_timer(&ap_keepalive->ap_ka_timer, + jiffies + + msecs_to_jiffies( + ap_keepalive->ap_ka_interval) - + msecs_to_jiffies( + ATH6KL_AP_KA_PRELOAD_LEADTIME)); + + ap_keepalive->flags |= + ATH6KL_AP_KA_FLAGS_PRELOAD_STAT; + } + } + } + + return; +} + +struct ap_keepalive_info *ath6kl_ap_keepalive_init(struct ath6kl_vif *vif, + enum ap_keepalive_mode mode) +{ + struct ap_keepalive_info *ap_keepalive; + + ap_keepalive = kzalloc(sizeof(struct ap_keepalive_info), GFP_KERNEL); + if (!ap_keepalive) { + ath6kl_err("failed to alloc memory for ap_keepalive\n"); + return NULL; + } + + ap_keepalive->vif = vif; + ap_keepalive->ap_ka_interval = 0; + ap_keepalive->ap_ka_reclaim_cycle = 0; + if ((mode == AP_KA_MODE_ENABLE) || + (mode == AP_KA_MODE_CONFIG_BYSUPP)) { + ap_keepalive->flags |= ATH6KL_AP_KA_FLAGS_ENABLED; + ap_keepalive->ap_ka_interval = ATH6KL_AP_KA_INTERVAL_DEFAULT; + ap_keepalive->ap_ka_reclaim_cycle = ATH6KL_AP_KA_RECLAIM_CYCLE; + + if (mode == AP_KA_MODE_CONFIG_BYSUPP) + ap_keepalive->flags |= + ATH6KL_AP_KA_FLAGS_CONFIG_BY_SUPP; + } else if (mode == AP_KA_MODE_BYSUPP) + ap_keepalive->flags |= ATH6KL_AP_KA_FLAGS_BY_SUPP; + + ap_keepalive->ap_ka_remove_time = ap_keepalive->ap_ka_interval * + ap_keepalive->ap_ka_reclaim_cycle; + + /* Init. periodic scan timer. */ + init_timer(&ap_keepalive->ap_ka_timer); + ap_keepalive->ap_ka_timer.function = ap_keepalive_start; + ap_keepalive->ap_ka_timer.data = (unsigned long) ap_keepalive; + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive init (vif idx %d) interval %d cycle %d %s\n", + vif->fw_vif_idx, + ap_keepalive->ap_ka_interval, + ap_keepalive->ap_ka_reclaim_cycle, + (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_ENABLED) ? + ((ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_CONFIG_BY_SUPP) ? + "ON-SUPP" : "ON") : + ((ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_BY_SUPP) ? + "SUPP" : "OFF")); + + return ap_keepalive; +} + +void ath6kl_ap_keepalive_deinit(struct ath6kl_vif *vif) +{ + struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; + + if (ap_keepalive) { + if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_START) + del_timer(&ap_keepalive->ap_ka_timer); + + kfree(ap_keepalive); + } + + vif->ap_keepalive_ctx = NULL; + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive deinit (vif idx %d)\n", + vif->fw_vif_idx); + + return; +} + +int ath6kl_ap_keepalive_start(struct ath6kl_vif *vif) +{ + struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; + + BUG_ON(!ap_keepalive); + BUG_ON(vif->nw_type != AP_NETWORK); + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive start (vif idx %d) flags %x\n", + vif->fw_vif_idx, + ap_keepalive->flags); + + if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_ENABLED) { + mod_timer(&ap_keepalive->ap_ka_timer, + jiffies + + msecs_to_jiffies(ap_keepalive->ap_ka_interval) - + msecs_to_jiffies(ATH6KL_AP_KA_PRELOAD_LEADTIME)); + ap_keepalive->flags |= (ATH6KL_AP_KA_FLAGS_START | + ATH6KL_AP_KA_FLAGS_PRELOAD_STAT); + ath6kl_wmi_set_inact_cmd(vif->ar->wmi, + DISALBE_AP_INACTIVE_TIMEMER); + } + + return 0; +} + +void ath6kl_ap_keepalive_stop(struct ath6kl_vif *vif) +{ + struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; + + if (!ap_keepalive) + return; + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive stop (vif idx %d) flags %x\n", + vif->fw_vif_idx, + ap_keepalive->flags); + + if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_START) { + del_timer(&ap_keepalive->ap_ka_timer); + ap_keepalive->flags &= ~(ATH6KL_AP_KA_FLAGS_START | + ATH6KL_AP_KA_FLAGS_PRELOAD_STAT); + } + + return; +} + +int ath6kl_ap_keepalive_config(struct ath6kl_vif *vif, + u32 ap_ka_interval, + u32 ap_ka_reclaim_cycle) +{ + struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; + int restart = 0; + + if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_BY_SUPP) { + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive offlad to supplicant/hostapd.\n"); + return 0; + } else if (!(ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_ENABLED)) { + return 0; + } else if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_CONFIG_BY_SUPP) { + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive config offlad to supplicant/hostapd.\n"); + return 0; + } + + if ((ap_ka_interval != 0) && + (ap_ka_interval < ATH6KL_AP_KA_INTERVAL_MIN)) + ap_ka_interval = ATH6KL_AP_KA_INTERVAL_MIN; + + if (ap_ka_reclaim_cycle == 0) + ap_ka_reclaim_cycle = 1; + + if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_START) { + del_timer(&ap_keepalive->ap_ka_timer); + ap_keepalive->flags &= ~(ATH6KL_AP_KA_FLAGS_START | + ATH6KL_AP_KA_FLAGS_PRELOAD_STAT); + restart = 1; + } + + if (ap_ka_interval == 0) { + ap_keepalive->ap_ka_interval = 0; + ap_keepalive->ap_ka_reclaim_cycle = 0; + ap_keepalive->flags &= ~ATH6KL_AP_KA_FLAGS_ENABLED; + } else { + if (ap_ka_interval * ap_ka_reclaim_cycle < + ATH6KL_AP_KA_RECLAIM_TIME_MAX) { + ap_keepalive->ap_ka_interval = ap_ka_interval; + ap_keepalive->ap_ka_reclaim_cycle = ap_ka_reclaim_cycle; + } else { + ap_keepalive->ap_ka_interval = + ATH6KL_AP_KA_INTERVAL_DEFAULT; + ap_keepalive->ap_ka_reclaim_cycle = + ATH6KL_AP_KA_RECLAIM_CYCLE; + } + + ap_keepalive->ap_ka_remove_time = + ap_keepalive->ap_ka_interval * + ap_keepalive->ap_ka_reclaim_cycle; + ap_keepalive->flags |= ATH6KL_AP_KA_FLAGS_ENABLED; + + if (restart) { + mod_timer(&ap_keepalive->ap_ka_timer, + jiffies + + msecs_to_jiffies( + ap_keepalive->ap_ka_interval) - + msecs_to_jiffies( + ATH6KL_AP_KA_PRELOAD_LEADTIME * 1000)); + + ap_keepalive->flags |= + (ATH6KL_AP_KA_FLAGS_START | + ATH6KL_AP_KA_FLAGS_PRELOAD_STAT); + } + } + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive config (%d intvl %d cycle %d %s restart %d)\n", + vif->fw_vif_idx, + ap_keepalive->ap_ka_interval, + ap_keepalive->ap_ka_reclaim_cycle, + (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_ENABLED) ? + "ON" : "OFF", + restart); + + + return 0; +} + +int ath6kl_ap_keepalive_config_by_supp(struct ath6kl_vif *vif, + u16 inactive_time) +{ + struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; + u32 ap_ka_interval; + u32 timeout = inactive_time * 1000; /* to ms. */ + + if (!(ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_ENABLED) || + !(ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_CONFIG_BY_SUPP)) + return 0; + + if (timeout == 0) { + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive config wrong inactive_time!\n"); + return 0; + } + + /* Min. at lease 1 cycle */ + if (timeout < (ATH6KL_AP_KA_INTERVAL_MIN * 1)) + timeout = (ATH6KL_AP_KA_INTERVAL_MIN * 1); + else if (timeout > ATH6KL_AP_KA_RECLAIM_TIME_MAX) + timeout = ATH6KL_AP_KA_RECLAIM_TIME_MAX; + + if (timeout < (ATH6KL_AP_KA_INTERVAL_DEFAULT * + ATH6KL_AP_KA_RECLAIM_CYCLE)) { + if (timeout <= ATH6KL_AP_KA_INTERVAL_DEFAULT) + ap_ka_interval = ATH6KL_AP_KA_INTERVAL_MIN; + else + ap_ka_interval = ATH6KL_AP_KA_INTERVAL_DEFAULT; + } else + ap_ka_interval = ATH6KL_AP_KA_INTERVAL_DEFAULT; + + /* Update the config */ + ap_keepalive->ap_ka_interval = ap_ka_interval; + ap_keepalive->ap_ka_reclaim_cycle = timeout / ap_ka_interval; + ap_keepalive->ap_ka_remove_time = + ap_ka_interval * ap_keepalive->ap_ka_reclaim_cycle; + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive config_by_supp (%d supp %d intvl %d cycle %d)\n", + vif->fw_vif_idx, + inactive_time, + ap_keepalive->ap_ka_interval, + ap_keepalive->ap_ka_reclaim_cycle); + + return 0; +} + +/* Offload to supplicant/hostapd */ +static u32 ap_keepalive_get_inactive_time(struct ath6kl_vif *vif, + struct ath6kl_sta *conn) +{ + struct wmi_ap_mode_stat *ap_stats = &vif->ap_stats; + u32 inact_time = 0; + int i; + + for (i = 0; i < AP_MAX_NUM_STA; i++) { + struct wmi_per_sta_stat *per_sta_stat = &ap_stats->sta[i]; + + if (per_sta_stat->aid == conn->aid) { + unsigned long now = jiffies; + + spin_lock_bh(&conn->lock); + __ap_keepalive_adjust_txrx_time(conn, + per_sta_stat->last_txrx_time, + now); + + /* get inactive time. */ + inact_time = jiffies_to_msecs(now - + conn->last_txrx_time); + spin_unlock_bh(&conn->lock); + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive inact tgt/hst/now %d %ld %ld\n", + conn->last_txrx_time_tgt, + conn->last_txrx_time, + now); + } + } + + return inact_time; +} + +u32 ath6kl_ap_keepalive_get_inactive_time(struct ath6kl_vif *vif, u8 *mac) +{ + struct ath6kl_sta *conn; + u32 inact_time; + + conn = ath6kl_find_sta(vif, mac); + + if (conn) + inact_time = ap_keepalive_get_inactive_time(vif, conn); + else { + inact_time = 0; /* return -1 ? */ + + ath6kl_err("can't find %02x:%02x:%02x:%02x:%02x:%02x vif %d\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + vif->fw_vif_idx); + } + + ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, + "ap_keepalive inact aid %d inact_time %d ms\n", + (conn ? (conn->aid) : 0), + inact_time); + + return inact_time; +} + +bool ath6kl_ap_keepalive_by_supp(struct ath6kl_vif *vif) +{ + struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; + + if (!ap_keepalive) + return false; + + if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_BY_SUPP) + return true; + else + return false; +} + +struct ap_acl_info *ath6kl_ap_acl_init(struct ath6kl_vif *vif) +{ + struct ap_acl_info *ap_acl; + + ap_acl = kzalloc(sizeof(struct ap_acl_info), GFP_KERNEL); + if (!ap_acl) { + ath6kl_err("failed to alloc memory for ap_acl\n"); + return NULL; + } + + ap_acl->vif = vif; + + /* Default is OFF and configure it from debugfs or others */ + ap_acl->acl_mode = AP_ACL_MODE_DISABLE; + + ath6kl_dbg(ATH6KL_DBG_ACL, + "ap_acl init (vif idx %d)\n", + vif->fw_vif_idx); + + return ap_acl; +} + +void ath6kl_ap_acl_deinit(struct ath6kl_vif *vif) +{ + struct ap_acl_info *ap_acl = vif->ap_acl_ctx; + + if (ap_acl != NULL) { + if (ap_acl->last_acl_config != NULL) + kfree(ap_acl->last_acl_config); + kfree(ap_acl); + } + + vif->ap_acl_ctx = NULL; + + ath6kl_dbg(ATH6KL_DBG_ACL, + "ap_acl deinit (vif idx %d)\n", + vif->fw_vif_idx); + + return; +} + +int ath6kl_ap_acl_start(struct ath6kl_vif *vif) +{ + struct ap_acl_info *ap_acl = vif->ap_acl_ctx; + + if (ap_acl) { + if (ap_acl->last_acl_config != NULL) + kfree(ap_acl->last_acl_config); + + ap_acl->last_acl_config = NULL; + } + + ath6kl_dbg(ATH6KL_DBG_ACL, + "ap_acl start (vif idx %d)\n", + vif->fw_vif_idx); + + return 0; +} + +int ath6kl_ap_acl_stop(struct ath6kl_vif *vif) +{ + struct ap_acl_info *ap_acl = vif->ap_acl_ctx; + + if (ap_acl) { + ath6kl_ap_acl_config_mac_list_reset(vif); + ath6kl_ap_acl_config_policy(vif, AP_ACL_MODE_DISABLE); + + if (ap_acl->last_acl_config != NULL) + kfree(ap_acl->last_acl_config); + + ap_acl->last_acl_config = NULL; + } + + ath6kl_dbg(ATH6KL_DBG_ACL, + "ap_acl stop (vif idx %d)\n", + vif->fw_vif_idx); + + return 0; +} + +int ath6kl_ap_acl_config_policy(struct ath6kl_vif *vif, + enum ap_acl_mode mode) +{ + struct ap_acl_info *ap_acl = vif->ap_acl_ctx; + struct ap_acl_entry *ap_acl_entry; + u8 i, policy; + + if (ap_acl == NULL) + return 0; + + switch (mode) { + case AP_ACL_MODE_DISABLE: + policy = AP_ACL_DISABLE; + break; + case AP_ACL_MODE_ALLOW: + policy = AP_ACL_ALLOW_MAC; + break; + case AP_ACL_MODE_DENY: + policy = AP_ACL_DENY_MAC; + break; + default: + BUG_ON(1); + } + + ap_acl->acl_mode = mode; + ath6kl_wmi_ap_acl_policy(vif->ar->wmi, vif->fw_vif_idx, policy); + + ath6kl_dbg(ATH6KL_DBG_ACL, + "ap_acl config (vif idx %d mode %s)\n", + vif->fw_vif_idx, + (ap_acl->acl_mode ? + (ap_acl->acl_mode == AP_ACL_MODE_ALLOW ? + "ALLOW" : "DENY") : + "DISABLED")); + + if (ap_acl->acl_mode != AP_ACL_MODE_DISABLE) { + for (i = 0; i < ATH6KL_AP_ACL_MAX_NUM; i++) { + ap_acl_entry = &ap_acl->acl_list[i]; + + if (ap_acl_entry->flags & ATH6KL_AP_ACL_FLAGS_USED) { + ath6kl_wmi_ap_acl_mac_list(vif->ar->wmi, + vif->fw_vif_idx, + i, + ap_acl_entry->mac_addr, + ADD_MAC_ADDR); + ath6kl_dbg(ATH6KL_DBG_ACL, + "ap_acl config ADD " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + ap_acl_entry->mac_addr[0], + ap_acl_entry->mac_addr[1], + ap_acl_entry->mac_addr[2], + ap_acl_entry->mac_addr[3], + ap_acl_entry->mac_addr[4], + ap_acl_entry->mac_addr[5]); + } + } + } + + return 0; +} + +int ath6kl_ap_acl_config_mac_list(struct ath6kl_vif *vif, + u8 *mac_addr, bool removed) +{ + struct ap_acl_info *ap_acl = vif->ap_acl_ctx; + struct ap_acl_entry *ap_acl_entry; + int i, slot_idx, first_not_used = 0xff; + u8 action; + + if (ap_acl == NULL) + return 0; + + ath6kl_dbg(ATH6KL_DBG_ACL, + "ap_acl config %02x:%02x:%02x:%02x:%02x:%02x ", + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + + for (i = 0; i < ATH6KL_AP_ACL_MAX_NUM; i++) { + ap_acl_entry = &ap_acl->acl_list[i]; + if (ap_acl_entry->flags & ATH6KL_AP_ACL_FLAGS_USED) { + if (memcmp(ap_acl_entry->mac_addr, + mac_addr, + ETH_ALEN) == 0) { + ath6kl_dbg(ATH6KL_DBG_ACL, + "found in slot %d\n", + i); + break; + } + } else if (first_not_used == 0xff) + first_not_used = i; + } + + if (removed) { + action = DEL_MAC_ADDR; + slot_idx = i; + + if (i == ATH6KL_AP_ACL_MAX_NUM) { + ath6kl_dbg(ATH6KL_DBG_ACL, + "not found and ignore it.\n"); + + return 0; + } + + ap_acl_entry = &ap_acl->acl_list[slot_idx]; + ap_acl_entry->flags &= ~ATH6KL_AP_ACL_FLAGS_USED; + } else { + action = ADD_MAC_ADDR; + slot_idx = first_not_used; + + if (i != ATH6KL_AP_ACL_MAX_NUM) + return 0; + + if (first_not_used == 0xff) { + ath6kl_dbg(ATH6KL_DBG_ACL, + "no availabe ACL slot!\n"); + + return 0; + } + + ap_acl_entry = &ap_acl->acl_list[slot_idx]; + ap_acl_entry->flags |= ATH6KL_AP_ACL_FLAGS_USED; + memcpy(ap_acl_entry->mac_addr, mac_addr, ETH_ALEN); + } + + if (ap_acl->acl_mode != AP_ACL_MODE_DISABLE) { + ath6kl_wmi_ap_acl_mac_list(vif->ar->wmi, + vif->fw_vif_idx, + slot_idx, + ap_acl_entry->mac_addr, + action); + } + + ath6kl_dbg(ATH6KL_DBG_ACL, + "ap_acl config %s %02x:%02x:%02x:%02x:%02x:%02x, slot %d\n", + ((removed) ? "DEL" : "ADD"), + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5], + slot_idx); + + return 0; +} + +int ath6kl_ap_acl_config_mac_list_reset(struct ath6kl_vif *vif) +{ + struct ap_acl_info *ap_acl = vif->ap_acl_ctx; + struct ap_acl_entry *ap_acl_entry; + int i; + + if (ap_acl == NULL) + return 0; + + ath6kl_dbg(ATH6KL_DBG_ACL, "ap_acl reset\n"); + + for (i = 0; i < ATH6KL_AP_ACL_MAX_NUM; i++) { + ap_acl_entry = &ap_acl->acl_list[i]; + if (ap_acl_entry->flags & ATH6KL_AP_ACL_FLAGS_USED) + ath6kl_ap_acl_config_mac_list(vif, + ap_acl_entry->mac_addr, + true); + } + + return 0; +} + +int ath6kl_ap_acl_dump(struct ath6kl *ar, u8 *buf, int buf_len) +{ + int i, len = 0; + + if (!buf) + return 0; + + for (i = 0; i < ar->vif_max; i++) { + struct ath6kl_vif *vif; + + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->nw_type == AP_NETWORK)) { + struct ap_acl_info *ap_acl; + + len += snprintf(buf + len, buf_len - len, + "\nVAP%d - ", + vif->fw_vif_idx); + + ap_acl = vif->ap_acl_ctx; + if (!ap_acl) + return 0; + + len += snprintf(buf + len, buf_len - len, "mode %d\n", + ap_acl->acl_mode); + + for (i = 0; i < ATH6KL_AP_ACL_MAX_NUM; i++) { + struct ap_acl_entry *ap_acl_entry; + + ap_acl_entry = &ap_acl->acl_list[i]; + + len += snprintf(buf + len, buf_len - len, + " %02d/%08x - " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + i, + ap_acl_entry->flags, + ap_acl_entry->mac_addr[0], + ap_acl_entry->mac_addr[1], + ap_acl_entry->mac_addr[2], + ap_acl_entry->mac_addr[3], + ap_acl_entry->mac_addr[4], + ap_acl_entry->mac_addr[5]); + } + } + } + + return len; +} + +static inline void _ap_amdc_assoc_req_free(struct ap_admc_info *ap_admc, + struct ap_admc_assoc_req *admc_assoc_req) +{ + if (admc_assoc_req) { + /* + * TODO: Check admc_assoc_req->action then do something. + */ + kfree(admc_assoc_req); + } else { + /* + * This STA reject by Admission-Control check rule + * or firmware's frame parser. + */ + ; + } + + return; +} + +static void _ap_amdc_assoc_req_timeout(unsigned long arg) +{ + struct ap_admc_assoc_req *admc_assoc_req; + struct ap_admc_info *ap_admc; + + admc_assoc_req = (struct ap_admc_assoc_req *)arg; + ap_admc = admc_assoc_req->ap_admc; + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc timeout assoResp %p len %d depth %d\n", + admc_assoc_req, + admc_assoc_req->frame_len, + ap_admc->assoc_req_cnt); + + admc_assoc_req->action = AP_ADMC_ACT_TIMEOUT; + list_del(&admc_assoc_req->list); + ap_admc->assoc_req_cnt--; + + _ap_amdc_assoc_req_free(ap_admc, admc_assoc_req); + + return; +} + +static inline void _ap_amdc_assoc_req_add(struct ap_admc_info *ap_admc, + u8 *frm, + u16 len) +{ + struct ieee80211_mgmt *assocReq; + struct ap_admc_assoc_req *admc_assoc_req; + + if (len > ATH6KL_AP_ADMC_ASSOC_REQ_MAX_LEN) { + /* Is it possible? */ + ath6kl_err("Not support so large assoc_req frame\n"); + return; + } + + admc_assoc_req = kzalloc(sizeof(struct ap_admc_assoc_req), GFP_KERNEL); + if (!admc_assoc_req) { + ath6kl_err("failed to alloc memory for admc_assoc_req\n"); + return; + } + + if (ap_admc->assoc_req_cnt) + ath6kl_info("Last assoc_req not yet finish!"); + + /* Add assocReq info. */ + admc_assoc_req->ap_admc = ap_admc; + admc_assoc_req->frame_len = len; + memcpy(admc_assoc_req->raw_frame, frm, len); + assocReq = (struct ieee80211_mgmt *)(admc_assoc_req->raw_frame); + admc_assoc_req->sta_mac = assocReq->sa; + admc_assoc_req->action = AP_ADMC_ACT_ACCPET; + + /* + * It still possbile that the host accept the STA but something wrong + * when sending assocResp. In this case, the target will not send + * connection event and need a timer to reclaim this item. + */ + init_timer(&admc_assoc_req->reclaim_timer); + admc_assoc_req->reclaim_timer.function = _ap_amdc_assoc_req_timeout; + admc_assoc_req->reclaim_timer.data = (unsigned long) admc_assoc_req; + mod_timer(&admc_assoc_req->reclaim_timer, + jiffies + msecs_to_jiffies(ap_admc->assoc_req_timeout)); + + spin_lock_bh(&ap_admc->assoc_req_lock); + list_add_tail(&admc_assoc_req->list, &ap_admc->assoc_req_list); + ap_admc->assoc_req_cnt++; + spin_unlock_bh(&ap_admc->assoc_req_lock); + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc add assoResp len %d depth %d\n", + len, + ap_admc->assoc_req_cnt); + + return; +} + +static inline struct ap_admc_assoc_req *_ap_amdc_assoc_req_search( + struct ap_admc_info *ap_admc, + u8 *sta_mac) +{ + struct ap_admc_assoc_req *admc_assoc_req, *tmp; + + /* + * Here assume the first matched address is the STA we want + * to handle if more than one the same addresses in the list. + */ + admc_assoc_req = NULL; + if (!list_empty(&ap_admc->assoc_req_list)) { + list_for_each_entry_safe(admc_assoc_req, + tmp, + &ap_admc->assoc_req_list, + list) { + if (memcmp(admc_assoc_req->sta_mac, + sta_mac, + ETH_ALEN) == 0) + break; + } + } + + if (admc_assoc_req) { + spin_lock_bh(&ap_admc->assoc_req_lock); + del_timer(&admc_assoc_req->reclaim_timer); + list_del(&admc_assoc_req->list); + ap_admc->assoc_req_cnt--; + spin_unlock_bh(&ap_admc->assoc_req_lock); + } + + return admc_assoc_req; +} + +static inline void _ap_amdc_assoc_req_flush(struct ap_admc_info *ap_admc) +{ + struct ap_admc_assoc_req *admc_assoc_req, *tmp; + int freed = 0; + + spin_lock_bh(&ap_admc->assoc_req_lock); + list_for_each_entry_safe(admc_assoc_req, + tmp, + &ap_admc->assoc_req_list, + list) { + admc_assoc_req->action = AP_ADMC_ACT_FLUSH; + del_timer(&admc_assoc_req->reclaim_timer); + list_del(&admc_assoc_req->list); + ap_admc->assoc_req_cnt--; + + _ap_amdc_assoc_req_free(ap_admc, admc_assoc_req); + freed++; + } + spin_unlock_bh(&ap_admc->assoc_req_lock); + + WARN_ON(ap_admc->assoc_req_cnt); + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc flush %d\n", + freed); + + return; +} + +static bool _ap_amdc_check(struct ap_admc_info *ap_admc, + u8 *frm, + u8 req_type, + u8 **sta_mac, + u8 *reason_code) +{ + struct ieee80211_mgmt *assocReq = (struct ieee80211_mgmt *)frm; + bool accept = true; + + *reason_code = WLAN_STATUS_SUCCESS; + + if (ap_admc->admc_mode == AP_ADMC_MODE_ACCEPT_ALWAYS) { + /* Assume the target already check DA & BSSID already. */ + *sta_mac = assocReq->sa; + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc check %02x:%02x:%02x:%02x:%02x:%02x\n", + assocReq->sa[0], assocReq->sa[1], assocReq->sa[2], + assocReq->sa[3], assocReq->sa[4], assocReq->sa[5]); + } else { + ; /* TODO */ + } + + return accept; +} + +struct ap_admc_info *ath6kl_ap_admc_init(struct ath6kl_vif *vif, + enum ap_admc_mode mode) +{ + struct ap_admc_info *ap_admc; + + ap_admc = kzalloc(sizeof(struct ap_admc_info), GFP_KERNEL); + if (!ap_admc) { + ath6kl_err("failed to alloc memory for ap_admc\n"); + return NULL; + } + + ap_admc->vif = vif; + ap_admc->admc_mode = mode; + ap_admc->assoc_req_timeout = ATH6KL_AP_ADMC_ASSOC_REQ_TIMEOUT; + spin_lock_init(&ap_admc->assoc_req_lock); + INIT_LIST_HEAD(&ap_admc->assoc_req_list); + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc init (vif%d) mode %d timeout %d\n", + vif->fw_vif_idx, + ap_admc->admc_mode, + ap_admc->assoc_req_timeout); + + return ap_admc; +} + +void ath6kl_ap_admc_deinit(struct ath6kl_vif *vif) +{ + struct ap_admc_info *ap_admc = vif->ap_admc_ctx; + + if (ap_admc != NULL) { + _ap_amdc_assoc_req_flush(ap_admc); + kfree(ap_admc); + } + + vif->ap_admc_ctx = NULL; + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc deinit (vif%d)\n", + vif->fw_vif_idx); + + return; +} + +int ath6kl_ap_admc_start(struct ath6kl_vif *vif) +{ + struct ap_admc_info *ap_admc = vif->ap_admc_ctx; + bool enabled = false; + int ret; + + if (!ap_admc) + return -ENOENT; + + if (ap_admc->admc_mode != AP_ADMC_MODE_DISABLE) + enabled = true; + + ret = ath6kl_wmi_set_assoc_req_relay_cmd(vif->ar->wmi, + vif->fw_vif_idx, + enabled); + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc start %s (vif%d) ret %d\n", + (enabled ? "enable" : "disable"), + vif->fw_vif_idx, + ret); + + return ret; +} + +int ath6kl_ap_admc_stop(struct ath6kl_vif *vif) +{ + struct ap_admc_info *ap_admc = vif->ap_admc_ctx; + int ret; + + if (!ap_admc) + return -ENOENT; + + _ap_amdc_assoc_req_flush(ap_admc); + + ret = ath6kl_wmi_set_assoc_req_relay_cmd(vif->ar->wmi, + vif->fw_vif_idx, + false); + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc stop (vif%d) ret %d\n", + vif->fw_vif_idx, + ret); + + return ret; +} + +void ath6kl_ap_admc_assoc_req(struct ath6kl_vif *vif, + u8 *assocReq, + u16 assocReq_len, + u8 req_type, + u8 fw_status) +{ + struct ap_admc_info *ap_admc = vif->ap_admc_ctx; + u8 reason_code, *sta_mac = NULL; + bool accept; + + BUG_ON(!ap_admc); + BUG_ON(ap_admc->admc_mode == AP_ADMC_MODE_DISABLE); + + if (vif->nw_type == AP_NETWORK) { + accept = _ap_amdc_check(ap_admc, + assocReq, + req_type, + &sta_mac, + &reason_code); + if (ath6kl_wmi_send_assoc_resp_cmd(vif->ar->wmi, + vif->fw_vif_idx, + accept, + reason_code, + fw_status, + sta_mac, + req_type)) + ath6kl_err("ap_admc assoResp fail (vif%d)\n", + vif->fw_vif_idx); + + /* Only add into list for successful case. */ + if ((fw_status == WLAN_STATUS_SUCCESS) && + accept) + _ap_amdc_assoc_req_add(ap_admc, + assocReq, + assocReq_len); + else + _ap_amdc_assoc_req_free(ap_admc, NULL); + } else + WARN_ON(1); + + return; +} + +void ath6kl_ap_admc_assoc_req_fetch(struct ath6kl_vif *vif, + struct wmi_connect_event *ev, + u8 **assocReq, + u16 *assocReq_len) +{ + struct ap_admc_info *ap_admc = vif->ap_admc_ctx; + struct ap_admc_assoc_req *admc_assoc_req = NULL; + u8 *sta_mac = ev->u.ap_sta.mac_addr; + + BUG_ON(!ap_admc); + + *assocReq = NULL; + *assocReq_len = 0; + + if (ap_admc->admc_mode == AP_ADMC_MODE_DISABLE) { + *assocReq = ev->assoc_info + ev->beacon_ie_len; + *assocReq_len = ev->assoc_req_len; + + return; + } + + admc_assoc_req = _ap_amdc_assoc_req_search(ap_admc, sta_mac); + if (admc_assoc_req) { + *assocReq = admc_assoc_req->raw_frame; + *assocReq_len = admc_assoc_req->frame_len; + } else + ath6kl_err("No asoc_req found!"); + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc assoResp fetch %p %p len %d\n", + *assocReq, + admc_assoc_req, + *assocReq_len); + + return; +} + +void ath6kl_ap_admc_assoc_req_release(struct ath6kl_vif *vif, + u8 *assocReq) +{ + struct ap_admc_info *ap_admc = vif->ap_admc_ctx; + struct ap_admc_assoc_req *admc_assoc_req; + + BUG_ON(!ap_admc); + + if (ap_admc->admc_mode == AP_ADMC_MODE_DISABLE) + return; + + admc_assoc_req = container_of(assocReq, + struct ap_admc_assoc_req, + raw_frame[0]); + + ath6kl_dbg(ATH6KL_DBG_ADMC, + "ap_admc assoResp release %p %p\n", + assocReq, + admc_assoc_req); + + _ap_amdc_assoc_req_free(ap_admc, admc_assoc_req); + + return; +} + +int ath6kl_ap_admc_dump(struct ath6kl *ar, u8 *buf, int buf_len) +{ + int i, len = 0; + + if (!buf) + return 0; + + for (i = 0; i < ar->vif_max; i++) { + struct ath6kl_vif *vif; + + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->nw_type == AP_NETWORK)) { + struct ap_admc_info *ap_admc; + + len += snprintf(buf + len, buf_len - len, + "\nVAP%d - ", + vif->fw_vif_idx); + + ap_admc = vif->ap_admc_ctx; + if (!ap_admc) + return 0; + + len += snprintf(buf + len, buf_len - len, + "mode %d timeout %d depth %d\n", + ap_admc->admc_mode, + ap_admc->assoc_req_timeout, + ap_admc->assoc_req_cnt); + } + } + + return len; +} + +int ath6kl_ap_ht_update_ies(struct ath6kl_vif *vif) +{ + int ret = 0; + + if (vif->nw_type != AP_NETWORK) + return 0; + + /* EV117149 WAR : Only overwrite it for 5G band. */ + if (vif->next_chan > 4000) { + u8 opmode = HT_INFO_OPMODE_PROT_PURE; + + if (vif->sta_no_ht_num > 0) + opmode = HT_INFO_OPMODE_PROT_MIXED; + + /* TODO : RIFS mode & OBSS-nonHT-STA-Present */ + + ret = ath6kl_wmi_set_ht_op_cmd(vif->ar->wmi, + vif->fw_vif_idx, + 0, + opmode); + } + + return ret; +} + +static inline bool _ap_is_11b_rate(u8 rate) +{ + u8 rates[] = { 2, 4, 11, 22 }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(rates); i++) + if (rate == rates[i]) + return true; + + return false; +} + +void ath6kl_ap_beacon_info(struct ath6kl_vif *vif, u8 *beacon, u8 beacon_len) +{ + u8 *pie, *peie; + u8 *rates_ie = NULL; + u8 *ext_rates_ie = NULL; + struct ieee80211_ht_cap *ht_cap_ie = NULL; + struct ieee80211_ht_oper *ht_oper_ie = NULL; + + /* + * Get host interesting AP configuration by parsing the beacon content + * from AP CONNECTED event. + */ + if (vif->nw_type != AP_NETWORK) + return; + + /* bypass timestamp, beacon interval and capability fields */ + pie = beacon + 8 + 2 + 2; + peie = beacon + beacon_len; + + while (pie < peie) { + switch (*pie) { + case WLAN_EID_SUPP_RATES: + if (pie[1]) + rates_ie = pie; + break; + case WLAN_EID_EXT_SUPP_RATES: + if (pie[1]) + ext_rates_ie = pie; + break; + case WLAN_EID_HT_CAPABILITY: + if (pie[1] >= sizeof(struct ieee80211_ht_cap)) + ht_cap_ie = + (struct ieee80211_ht_cap *)(pie + 2); + break; + case WLAN_EID_HT_OPER: + if (pie[1] >= sizeof(struct ieee80211_ht_oper)) + ht_oper_ie = + (struct ieee80211_ht_oper *)(pie + 2); + break; + } + pie += pie[1] + 2; + } + + if (ht_cap_ie && ht_oper_ie) { /* 11N */ + u16 cap_info = le16_to_cpu(ht_cap_ie->cap_info); + u8 second_chan = (ht_oper_ie->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET); + bool ext_chan = false; + + vif->chan_type = ATH6KL_CHAN_TYPE_HT20; + + if (second_chan && + (cap_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) { + ext_chan = true; + if (second_chan == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) + vif->chan_type = ATH6KL_CHAN_TYPE_HT40PLUS; + else + vif->chan_type = ATH6KL_CHAN_TYPE_HT40MINUS; + } + + if (vif->bss_ch > 5000) { /* 11NA */ + if (ext_chan) + vif->phymode = ATH6KL_PHY_MODE_11NA_HT40; + else + vif->phymode = ATH6KL_PHY_MODE_11NA_HT20; + } else { /* 11NG */ + if (ext_chan) + vif->phymode = ATH6KL_PHY_MODE_11NG_HT40; + else + vif->phymode = ATH6KL_PHY_MODE_11NG_HT20; + } + } else { /* not 11N */ + vif->chan_type = ATH6KL_CHAN_TYPE_NONE; + + if (vif->bss_ch > 5000) /* 11A */ + vif->phymode = ATH6KL_PHY_MODE_11A; + else { /* 11B/G/GONLY */ + u8 i, rate; + bool b_rates, g_rates; + + b_rates = g_rates = false; + if (rates_ie) { + for (i = 0; i < rates_ie[1]; i++) { + rate = rates_ie[2 + i] & 0x7f; + if (_ap_is_11b_rate(rate)) + b_rates = true; + else + g_rates = true; + } + } + if (ext_rates_ie) { + for (i = 0; i < ext_rates_ie[1]; i++) { + rate = ext_rates_ie[2 + i] & 0x7f; + if (_ap_is_11b_rate(rate)) + b_rates = true; + else + g_rates = true; + } + } + + if (g_rates) + if (b_rates) + vif->phymode = ATH6KL_PHY_MODE_11G; + else + vif->phymode = ATH6KL_PHY_MODE_11GONLY; + else + if (b_rates) + vif->phymode = ATH6KL_PHY_MODE_11B; + else + ath6kl_err("Unknown AP phymode\n"); + } + } + + return; +} + +void ath6kl_ap_ch_switch(struct ath6kl_vif *vif) +{ + if (vif->nw_type != AP_NETWORK) + return; + + /* + * If target use the different channel setting as the host and use this + * helper API to update the working channel information to the user. + * + * If target support WMI_CHANNEL_CHANGE_EVENTID (DFS?) and this helper + * API may also useful. + */ + +#ifdef NL80211_CMD_OPER_CH_SWITCH_NOTIFY + /* + * The next_chan_type is only valid when + * ATH6KL_MODULE_ENABLE_P2P_CHANMODE is on. + */ + if ((vif->next_chan != vif->bss_ch) || + (vif->next_chan_type != vif->chan_type)) { + if (ath6kl_mod_debug_quirks(vif->ar, + ATH6KL_MODULE_ENABLE_P2P_CHANMODE)) { + enum nl80211_channel_type type = NL80211_CHAN_NO_HT; +#ifdef CFG80211_NEW_CHAN_DEFINITION + struct ieee80211_channel *chan; + struct cfg80211_chan_def chandef; +#endif + + ath6kl_info("AP Channel switch from %d/%d to %d/%d\n", + vif->next_chan, vif->next_chan_type, + vif->bss_ch, vif->chan_type); + + if (vif->chan_type == ATH6KL_CHAN_TYPE_NONE) + type = NL80211_CHAN_NO_HT; + else if (vif->chan_type == ATH6KL_CHAN_TYPE_HT40PLUS) + type = NL80211_CHAN_HT40PLUS; + else if (vif->chan_type == ATH6KL_CHAN_TYPE_HT40MINUS) + type = NL80211_CHAN_HT40MINUS; + else if (vif->chan_type == ATH6KL_CHAN_TYPE_HT20) + type = NL80211_CHAN_HT20; + else + WARN_ON(1); + + /* + * TODO: Better to check channel information is valid + * or not for P2P-GO mode before report to the user. + */ +#ifdef CFG80211_NEW_CHAN_DEFINITION + chan = ieee80211_get_channel(vif->ar->wiphy, + vif->bss_ch); + if (chan == NULL) { + WARN_ON(1); + return; + } + + cfg80211_chandef_create(&chandef, chan, type); + cfg80211_ch_switch_notify(vif->ndev, + &chandef); +#else + cfg80211_ch_switch_notify(vif->ndev, + vif->bss_ch, + type); +#endif + } + } +#endif + return; +} + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/ap.h b/drivers/net/wireless/ath/ath6kl-3.5/ap.h new file mode 100644 index 000000000000..60dca075e109 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/ap.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2004-2012 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 AP_H +#define AP_H + +/* Time defines */ +#define ATH6KL_AP_KA_INTERVAL_DEFAULT (15 * 1000) /* in ms. */ +#define ATH6KL_AP_KA_INTERVAL_MIN (5 * 1000) /* in ms. */ +#define ATH6KL_AP_KA_RECLAIM_CYCLE (4) /* 1 min. */ +#define ATH6KL_AP_KA_RECLAIM_TIME_MAX ((15 * 60) * 1000) + +/* Do some fine tune to overwrite the config in P2P cases. */ +#define ATH6KL_AP_KA_RECLAIM_CYCLE_SCC (4) /* 1 min. */ +#define ATH6KL_AP_KA_RECLAIM_CYCLE_MCC (12) /* 3 min. */ + +/* At least WMI_TIMEOUT */ +#define ATH6KL_AP_KA_PRELOAD_LEADTIME (2 * 1000) + +/* flags */ +#define ATH6KL_AP_KA_FLAGS_ENABLED BIT(0) +#define ATH6KL_AP_KA_FLAGS_BY_SUPP BIT(1) /* offload to user */ +#define ATH6KL_AP_KA_FLAGS_START BIT(2) +#define ATH6KL_AP_KA_FLAGS_PRELOAD_STAT BIT(3) /* for preload state */ +#define ATH6KL_AP_KA_FLAGS_CONFIG_BY_SUPP BIT(4) + +/* Next action */ +#define AP_KA_ACTION_NONE (0) +#define AP_KA_ACTION_POLL (1) +#define AP_KA_ACTION_REMOVE (2) + +/* Operation mode */ +enum ap_keepalive_mode { + /* Disabled and using the target's mechanism. */ + AP_KA_MODE_DISABLE = 0, + + /* Using driver's mechanism and setting from the driver or debugfs. */ + AP_KA_MODE_ENABLE, + + /* Using supplicant/hostapd's mechanism. */ + AP_KA_MODE_BYSUPP, + + /* Using driver's mechanism but config from the supplicant/hostapd. */ + AP_KA_MODE_CONFIG_BYSUPP, +}; + +enum ap_keepalive_adjust { + AP_KA_ADJ_ERROR = -1, + AP_KA_ADJ_BASESET = 0, + AP_KA_ADJ_ADJUST, +}; + +struct ap_keepalive_info { + struct ath6kl_vif *vif; + u32 flags; + + struct timer_list ap_ka_timer; + /* In ms., run checking per ap_ka_interval */ + u32 ap_ka_interval; + + /* Reclaim STA after N checking fails */ + u32 ap_ka_reclaim_cycle; + + /* Remove this station after (ap_ka_interval * ap_ka_reclaim_cycle) */ + u32 ap_ka_remove_time; +}; + +/* ACL defines */ +#define ATH6KL_AP_ACL_MAX_NUM (10) /* limited by AP_ACL_SIZE */ + +#define ATH6KL_AP_ACL_FLAGS_USED (1 << 0) + +/* Operation mode */ +enum ap_acl_mode { + AP_ACL_MODE_DISABLE = 0, + AP_ACL_MODE_ALLOW, + AP_ACL_MODE_DENY, +}; + +struct ap_acl_entry { + u32 flags; + u8 mac_addr[ETH_ALEN]; +}; + +struct ap_acl_info { + struct ath6kl_vif *vif; + + enum ap_acl_mode acl_mode; + struct ap_acl_entry acl_list[ATH6KL_AP_ACL_MAX_NUM]; + + /* + * Cache last ACL setting from hostapd. + * Need special hostapd to support this. + */ + u8 *last_acl_config; +}; + +/* Admission-Control mode */ +#define ATH6KL_AP_ADMC_ASSOC_REQ_MAX_LEN (1200) +#define ATH6KL_AP_ADMC_ASSOC_REQ_TIMEOUT (500) /* in ms. */ + +enum ap_admc_mode { + AP_ADMC_MODE_DISABLE = 0, /* by firmware */ + AP_ADMC_MODE_ACCEPT_ALWAYS = 1, /* always accept */ +}; + +enum ap_admc_action { + AP_ADMC_ACT_ACCPET = 0, + AP_ADMC_ACT_TIMEOUT = 1, + AP_ADMC_ACT_FLUSH = 2, +}; + +struct ap_admc_assoc_req { + struct list_head list; + + struct ap_admc_info *ap_admc; + struct timer_list reclaim_timer; + u8 raw_frame[ATH6KL_AP_ADMC_ASSOC_REQ_MAX_LEN]; + u16 frame_len; + u8 *sta_mac; + enum ap_admc_action action; +}; + +struct ap_admc_info { + struct ath6kl_vif *vif; + + enum ap_admc_mode admc_mode; + spinlock_t assoc_req_lock; + struct list_head assoc_req_list; + int assoc_req_timeout; + + u32 assoc_req_cnt; +}; + +/* + * WLAN_EID_HT_INFORMATION & struct ieee80211_ht_info in ieee80211.h + * is changed to WLAN_EID_HT_OPERATION & struct ieee80211_ht_operation + * from kernel3.5. Using local defines instead of dirty compiler flag. + */ +#define WLAN_EID_HT_OPER (61) + +struct ieee80211_ht_oper { + u8 primary_chan; + u8 ht_param; + __le16 operation_mode; + __le16 stbc_param; + u8 basic_set[16]; +} __packed; + + +struct ap_keepalive_info *ath6kl_ap_keepalive_init(struct ath6kl_vif *vif, + enum ap_keepalive_mode mode); +void ath6kl_ap_keepalive_deinit(struct ath6kl_vif *vif); +int ath6kl_ap_keepalive_start(struct ath6kl_vif *vif); +void ath6kl_ap_keepalive_stop(struct ath6kl_vif *vif); +int ath6kl_ap_keepalive_config(struct ath6kl_vif *vif, + u32 ap_ka_interval, + u32 ap_ka_reclaim_cycle); +int ath6kl_ap_keepalive_config_by_supp(struct ath6kl_vif *vif, + u16 inactive_time); +u32 ath6kl_ap_keepalive_get_inactive_time(struct ath6kl_vif *vif, u8 *mac); +bool ath6kl_ap_keepalive_by_supp(struct ath6kl_vif *vif); +struct ap_acl_info *ath6kl_ap_acl_init(struct ath6kl_vif *vif); +void ath6kl_ap_acl_deinit(struct ath6kl_vif *vif); +int ath6kl_ap_acl_start(struct ath6kl_vif *vif); +int ath6kl_ap_acl_stop(struct ath6kl_vif *vif); +int ath6kl_ap_acl_config_policy(struct ath6kl_vif *vif, + enum ap_acl_mode mode); +int ath6kl_ap_acl_config_mac_list(struct ath6kl_vif *vif, + u8 *mac_addr, bool removed); +int ath6kl_ap_acl_config_mac_list_reset(struct ath6kl_vif *vif); +int ath6kl_ap_acl_dump(struct ath6kl *ar, u8 *buf, int buf_len); +struct ap_admc_info *ath6kl_ap_admc_init(struct ath6kl_vif *vif, + enum ap_admc_mode mode); +void ath6kl_ap_admc_deinit(struct ath6kl_vif *vif); +int ath6kl_ap_admc_start(struct ath6kl_vif *vif); +int ath6kl_ap_admc_stop(struct ath6kl_vif *vif); +void ath6kl_ap_admc_assoc_req(struct ath6kl_vif *vif, + u8 *assocReq, + u16 assocReq_len, + u8 req_type, + u8 fw_status); +void ath6kl_ap_admc_assoc_req_fetch(struct ath6kl_vif *vif, + struct wmi_connect_event *ev, + u8 **assocReq, + u16 *assocReq_len); +void ath6kl_ap_admc_assoc_req_release(struct ath6kl_vif *vif, + u8 *assocReq); +int ath6kl_ap_admc_dump(struct ath6kl *ar, u8 *buf, int buf_len); +int ath6kl_ap_ht_update_ies(struct ath6kl_vif *vif); +void ath6kl_ap_beacon_info(struct ath6kl_vif *vif, u8 *beacon, u8 beacon_len); +void ath6kl_ap_ch_switch(struct ath6kl_vif *vif); +#endif + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.c b/drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.c new file mode 100644 index 000000000000..b379f576a8bb --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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/if.h" +#include "linux/socket.h" +#include "linux/netlink.h" +#include + +#include +#include +#include +#include +#include +#include +#include "ath_netlink.h" +#include "rttapi.h" +#include "core.h" +#include "debug.h" +#define MAX_PAYLOAD 1024 + +static struct sock *ath_nl_sock; +static u32 gpid; + +void ath_netlink_send(char *event_data, u32 event_datalen) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh; + + skb = nlmsg_new(NLMSG_SPACE(event_datalen), GFP_ATOMIC); + if (!skb) { + ath6kl_err("%s: No memory,\n", __func__); + return; + } + + nlh = nlmsg_put(skb, gpid, 0, 0 , NLMSG_SPACE(event_datalen), 0); + if (!nlh) { + ath6kl_err("%s: nlmsg_put() failed\n", __func__); + return; + } + + memcpy(NLMSG_DATA(nlh), event_data, event_datalen); + +#ifdef ATH6KL_SUPPORT_NETLINK_KERNEL3_7 + NETLINK_CB(skb).portid = 0; /* from kernel */ +#else + NETLINK_CB(skb).pid = 0; /* from kernel */ +#endif + NETLINK_CB(skb).dst_group = 0; /* unicast */ + netlink_unicast(ath_nl_sock, skb, gpid, MSG_DONTWAIT); +} + +/* --adhoc_netlink_data_ready-- + * Stub function that does nothing */ +void ath_netlink_reply(int pid, int seq, void *payload) +{ + return; +} + +/* Receive messages from netlink socket. */ +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 +static void ath_netlink_receive(struct sk_buff *__skb, int len) +#else +static void ath_netlink_receive(struct sk_buff *__skb) +#endif +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + u_int8_t *data = NULL; + u_int32_t uid, pid, seq; + + skb = skb_get(__skb); + if (skb) { + /* process netlink message pointed by skb->data */ + nlh = (struct nlmsghdr *)skb->data; + pid = NETLINK_CREDS(skb)->pid; + pid = nlh->nlmsg_pid; + uid = NETLINK_CREDS(skb)->uid; + seq = nlh->nlmsg_seq; + data = NLMSG_DATA(nlh); + + gpid = pid; + ath_netlink_reply(pid, seq, data); + rttm_issue_request(data, nlmsg_len(nlh)); + kfree_skb(skb); + } + return ; +} + +int ath_netlink_init() +{ +#ifdef ATH6KL_SUPPORT_NETLINK_KERNEL3_7 + struct netlink_kernel_cfg netlink_cfg; + + if (ath_nl_sock == NULL) { + netlink_cfg.groups = 1; + netlink_cfg.input = ath_netlink_receive; + netlink_cfg.cb_mutex = NULL; + netlink_cfg.bind = NULL; + netlink_cfg.flags = 0; + + ath_nl_sock = (struct sock *)netlink_kernel_create( + &init_net, NETLINK_ATH_EVENT, + &netlink_cfg); + if (ath_nl_sock == NULL) { + ath6kl_err("%s NetLink Create Failed\n", __func__); + return -ENODEV; + } + } +#elif defined(ATH6KL_SUPPORT_NETLINK_KERNEL3_6) + struct netlink_kernel_cfg netlink_cfg; + + if (ath_nl_sock == NULL) { + netlink_cfg.groups = 1; + netlink_cfg.input = ath_netlink_receive; + netlink_cfg.cb_mutex = NULL; + netlink_cfg.bind = NULL; + + ath_nl_sock = (struct sock *)netlink_kernel_create( + &init_net, NETLINK_ATH_EVENT, + THIS_MODULE, + &netlink_cfg); + if (ath_nl_sock == NULL) { + ath6kl_err("%s NetLink Create Failed\n", __func__); + return -ENODEV; + } + } +#else + if (ath_nl_sock == NULL) { +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + ath_nl_sock = (struct sock *)netlink_kernel_create( + NETLINK_ATH_EVENT, + 1, &ath_netlink_receive, NULL, + THIS_MODULE); +#else + ath_nl_sock = (struct sock *)netlink_kernel_create( + &init_net, NETLINK_ATH_EVENT, + 1, &ath_netlink_receive, NULL, + THIS_MODULE); +#endif + if (ath_nl_sock == NULL) { + ath6kl_err("%s NetLink Create Failed\n", __func__); + return -ENODEV; + } + } +#endif + + return 0; +} + +int ath_netlink_free(void) +{ + if (ath_nl_sock) { +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + sock_release(ath_nl_sock->sk_socket); +#else + netlink_kernel_release(ath_nl_sock); +#endif + ath_nl_sock = NULL; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.h b/drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.h new file mode 100644 index 000000000000..02f241dccb52 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/ath_netlink.h @@ -0,0 +1,31 @@ + +/* + * Copyright (c) 2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 _ATH_NETLINK_H_ +#define _ATH_NETLINK_H_ +#include "linux/if.h" +#include "linux/socket.h" +#include "linux/netlink.h" +#define NETLINK_ATH_EVENT (NETLINK_GENERIC + 4) + +#define MAC_ADDR_LEN 6 + +int ath_netlink_init(void); +int ath_netlink_delete(void); +void ath_netlink_send(char *event_data, u32 event_datalen); +int ath_netlink_free(void); + +#endif /* _ATH_NETLINK_H_ */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/bdata.bin.c b/drivers/net/wireless/ath/ath6kl-3.5/bdata.bin.c new file mode 100644 index 000000000000..11f1c9537d3a --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/bdata.bin.c @@ -0,0 +1,562 @@ +#define BDATA_LEN 6144 +unsigned char BDATA[BDATA_LEN] = { + 0x00, 0x18, 0x00, 0x00, 0x8e, 0xaa, 0x02, 0x01, 0x00, 0x03, 0x7f, + 0x20, 0xa2, 0x74, 0x00, 0x03, 0x7f, 0x12, 0x34, 0x56, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x44, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x64, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x57, 0x42, 0x31, 0x34, 0x34, 0x2d, 0x30, 0x31, 0x31, 0x2d, + 0x4e, 0x36, 0x33, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, + 0x2c, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x00, 0x00, 0x75, + 0x75, 0x00, 0x00, 0x00, 0x25, 0x82, 0x00, 0x00, 0x55, 0x55, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x0e, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xdb, 0x5c, + 0xdb, 0x5c, 0x0f, 0xa3, 0x00, 0x00, 0x89, 0x89, 0xfd, 0xfd, 0xf4, + 0xf4, 0x0e, 0x0e, 0x03, 0x2d, 0x2d, 0xe2, 0x00, 0x02, 0x0e, 0x21, + 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x89, 0xfd, 0xfd, + 0xf4, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, 0xcc, 0xac, 0x6d, 0xcc, 0x00, 0xc0, 0xa5, 0xf0, + 0x0f, 0x00, 0xda, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x05, + 0x06, 0x06, 0x88, 0x88, 0x87, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x89, 0x89, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x8e, 0xac, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd4, 0x00, 0x00, 0x00, 0x24, 0xff, 0x89, 0x00, 0x0e, 0x45, + 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0xd3, 0x00, 0x00, + 0x00, 0x1f, 0xff, 0x87, 0x00, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, + 0x41, 0x50, 0x4b, 0x41, 0xd4, 0x00, 0x00, 0x00, 0x24, 0xff, 0x87, + 0x00, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, + 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, 0x45, 0x48, + 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, + 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, + 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, + 0x00, 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, + 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, + 0xff, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, + 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, + 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, + 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, + 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, + 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, + 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, 0x45, + 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0xcd, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x8b, 0x00, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, + 0x41, 0x50, 0x4b, 0x41, 0xce, 0x00, 0x00, 0x00, 0x06, 0xff, 0x89, + 0x00, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, + 0xcf, 0x00, 0x00, 0x00, 0x0b, 0xff, 0x89, 0x00, 0x0e, 0x45, 0x48, + 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, + 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, + 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, + 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, + 0xff, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, + 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, + 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, + 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, + 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, + 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, + 0x41, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, 0x45, + 0x48, 0x49, 0x47, 0x4b, 0x41, 0x50, 0x4b, 0x41, 0x01, 0x00, 0x00, + 0x00, 0x9c, 0xff, 0x79, 0x64, 0x0e, 0x45, 0x48, 0x49, 0x47, 0x4b, + 0x41, 0x50, 0x4b, 0x41, 0x70, 0x75, 0x00, 0x00, 0x70, 0x8e, 0xac, + 0x00, 0x70, 0x8e, 0xac, 0x00, 0x70, 0x75, 0x7a, 0x00, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x20, 0x20, 0x20, 0x1f, 0x20, + 0x20, 0x20, 0x1f, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, + 0x1c, 0x18, 0x1c, 0x1c, 0x1c, 0x18, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, + 0x18, 0x1c, 0x1c, 0x1c, 0x18, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x18, + 0x1c, 0x1c, 0x1c, 0x18, 0x00, 0x00, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, + 0x18, 0x1c, 0x1c, 0x1c, 0x18, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x18, + 0x1c, 0x1c, 0x1c, 0x18, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, 0x18, 0x1c, + 0x1c, 0x1c, 0x18, 0x00, 0x00, 0x11, 0x12, 0x15, 0x17, 0x41, 0x42, + 0x45, 0x47, 0x31, 0x32, 0x35, 0x37, 0x70, 0x75, 0x9d, 0xa2, 0x70, + 0x75, 0xa2, 0xff, 0x70, 0x75, 0xa2, 0xff, 0x7a, 0x7f, 0x93, 0x98, + 0x70, 0x75, 0xac, 0xb8, 0x70, 0x75, 0xac, 0x00, 0x70, 0x75, 0xac, + 0x00, 0x7a, 0x7f, 0x93, 0xa2, 0x70, 0x75, 0xac, 0x00, 0x70, 0x75, + 0xac, 0x00, 0x70, 0x75, 0xac, 0x00, 0x7a, 0x7f, 0x93, 0xa2, 0x3c, + 0x3d, 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, 0x3c, 0x3d, 0x3c, 0x3c, 0x3d, + 0x3d, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, + 0x3c, 0x3c, 0x3d, 0x3d, 0x3c, 0x3c, 0x3d, 0x3c, 0x3c, 0x3c, 0x3d, + 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3c, + 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, + 0x04, 0x00, 0x00, 0x07, 0x06, 0x00, 0x00, 0x0b, 0x40, 0x00, 0x00, + 0x10, 0x42, 0x00, 0x00, 0x14, 0x44, 0x00, 0x00, 0x18, 0x46, 0x00, + 0x00, 0x1c, 0x40, 0x04, 0x00, 0x23, 0x41, 0x04, 0x00, 0x25, 0x42, + 0x04, 0x00, 0x27, 0x43, 0x04, 0x00, 0x29, 0x20, 0x0c, 0x00, 0x2c, + 0x21, 0x0c, 0x00, 0x2e, 0x22, 0x0c, 0x00, 0x30, 0x23, 0x0c, 0x00, + 0x32, 0x20, 0x10, 0x00, 0x3b, 0x21, 0x10, 0x00, 0x3d, 0x22, 0x10, + 0x00, 0x3f, 0x23, 0x10, 0x00, 0x41, 0x20, 0x14, 0x00, 0x44, 0x22, + 0x14, 0x00, 0x48, 0x24, 0x14, 0x00, 0x4c, 0x82, 0x1e, 0x00, 0x5c, + 0x84, 0x1e, 0x00, 0x60, 0x86, 0x1e, 0x00, 0x64, 0x8a, 0x1e, 0x00, + 0x68, 0x8c, 0x1e, 0x00, 0x6a, 0x8e, 0x1e, 0x00, 0x6d, 0x90, 0x1e, + 0x00, 0x72, 0x92, 0x1e, 0x00, 0x74, 0x94, 0x1e, 0x00, 0x76, 0x0a, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, + 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x00, + 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, + 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x70, 0x89, 0x9d, 0xac, 0x70, + 0x89, 0x9d, 0xac, 0x1d, 0x1c, 0x24, 0x26, 0x1d, 0x1c, 0x24, 0x26, + 0x1d, 0x1c, 0x24, 0x26, 0x1a, 0x1c, 0x24, 0x21, 0x1d, 0x1c, 0x2c, + 0x1d, 0x1d, 0x1c, 0x2c, 0x1d, 0x1d, 0x1c, 0x2c, 0x1d, 0x1a, 0x1c, + 0x2c, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x0d, 0x21, 0x44, + 0x71, 0x9a, 0xab, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x06, 0x06, 0x06, + 0x06, 0xc6, 0x18, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x66, 0x00, 0x24, + 0x24, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xdb, 0x04, 0xdb, 0x04, 0x0f, 0xa3, 0x00, 0x00, + 0x89, 0x89, 0xfd, 0xfd, 0xf4, 0xf4, 0x0e, 0x0e, 0x03, 0x2d, 0x2d, + 0xe2, 0x00, 0x02, 0x0e, 0x1c, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x19, 0x19, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x2a, 0x2a, 0x16, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x8e, 0x8e, 0xfd, 0xfd, 0xf4, 0xf4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x48, 0x00, 0x02, 0x02, + 0x8b, 0xc0, 0x02, 0x02, 0x8b, 0xc0, 0x02, 0x03, 0xd1, 0x40, 0x04, + 0x03, 0x11, 0xc1, 0x05, 0x04, 0x17, 0xc1, 0x05, 0x04, 0x17, 0xc1, + 0x05, 0x04, 0x17, 0xc1, 0x05, 0x04, 0x17, 0xc1, 0x05, 0x04, 0x17, + 0xc1, 0x05, 0x04, 0x17, 0xc1, 0x05, 0x04, 0x17, 0xc1, 0x05, 0x04, + 0x17, 0xc1, 0x05, 0x04, 0x17, 0xc1, 0x05, 0xd4, 0xaa, 0xff, 0xff, + 0xe8, 0x2c, 0x15, 0xf8, 0x00, 0xcf, 0xea, 0xff, 0x00, 0x10, 0x00, + 0xf8, 0x00, 0x00, 0x00, 0x05, 0x05, 0x05, 0x8a, 0x8a, 0x8e, 0x8e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8e, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x58, 0x5c, 0x68, 0x8c, 0xa0, + 0xb4, 0xbd, 0xc9, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x00, 0x00, 0x00, + 0x7e, 0xff, 0x85, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, + 0x4e, 0x4b, 0x4f, 0xea, 0x00, 0x00, 0x00, 0x92, 0xff, 0x85, 0x00, + 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xeb, + 0x00, 0x00, 0x00, 0x97, 0xff, 0x84, 0x00, 0x0e, 0x45, 0x4a, 0x4c, + 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xee, 0x00, 0x00, 0x00, 0xa6, + 0xff, 0x84, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, + 0x4b, 0x4f, 0xef, 0x00, 0x00, 0x00, 0xab, 0xff, 0x85, 0x00, 0x0e, + 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xeb, 0x00, + 0x00, 0x00, 0x97, 0xff, 0x85, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, + 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xe4, 0x00, 0x00, 0x00, 0x74, 0xff, + 0x83, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, + 0x4f, 0xe1, 0x00, 0x00, 0x00, 0x65, 0xff, 0x83, 0x00, 0x0e, 0x45, + 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xdc, 0x00, 0x00, + 0x00, 0x4c, 0xff, 0x83, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, + 0x4f, 0x4e, 0x4b, 0x4f, 0xdb, 0x00, 0x00, 0x00, 0x47, 0xff, 0x83, + 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, + 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, + 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, + 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, + 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, + 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, + 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, + 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, + 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, + 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, + 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, + 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, + 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, + 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, + 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, + 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, + 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, + 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, + 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, + 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, + 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xe9, + 0x00, 0x00, 0x00, 0x8d, 0xff, 0x88, 0x00, 0x0e, 0x45, 0x4a, 0x4c, + 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xef, 0x00, 0x00, 0x00, 0xab, + 0xff, 0x88, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, + 0x4b, 0x4f, 0xf1, 0x00, 0x00, 0x00, 0xb5, 0xff, 0x87, 0x00, 0x0e, + 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xf3, 0x00, + 0x00, 0x00, 0xbf, 0xff, 0x87, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, + 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xeb, 0x00, 0x00, 0x00, 0x97, 0xff, + 0x87, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, + 0x4f, 0xe6, 0x00, 0x00, 0x00, 0x7e, 0xff, 0x87, 0x00, 0x0e, 0x45, + 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xe0, 0x00, 0x00, + 0x00, 0x60, 0xff, 0x87, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, + 0x4f, 0x4e, 0x4b, 0x4f, 0xdd, 0x00, 0x00, 0x00, 0x51, 0xff, 0x84, + 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, + 0xda, 0x00, 0x00, 0x00, 0x42, 0xff, 0x86, 0x00, 0x0e, 0x45, 0x4a, + 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0xda, 0x00, 0x00, 0x00, + 0x42, 0xff, 0x87, 0x00, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, + 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, + 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, + 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, + 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, + 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, + 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, + 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, + 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, + 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, + 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, + 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, + 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, + 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, + 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, + 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, + 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, + 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, + 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, + 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, + 0x00, 0x00, 0x00, 0x52, 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, + 0x4e, 0x4b, 0x4f, 0x4e, 0x4b, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x52, + 0xff, 0x81, 0x62, 0x0e, 0x45, 0x4a, 0x4c, 0x4e, 0x4b, 0x4f, 0x4e, + 0x4b, 0x4f, 0x4c, 0x58, 0x5c, 0x68, 0x8c, 0xa0, 0xb4, 0xbd, 0x4c, + 0x58, 0x5c, 0x68, 0x8c, 0xa0, 0xb4, 0xbd, 0x4c, 0x58, 0x5c, 0x68, + 0x8c, 0xa0, 0xb4, 0xbd, 0x1e, 0x1e, 0x1a, 0x1a, 0x1e, 0x1e, 0x1a, + 0x1a, 0x1e, 0x1e, 0x1a, 0x1a, 0x1e, 0x1e, 0x1a, 0x1a, 0x1e, 0x1e, + 0x1a, 0x1a, 0x1e, 0x1e, 0x1a, 0x1a, 0x1e, 0x1e, 0x1a, 0x1a, 0x1e, + 0x1e, 0x1a, 0x1a, 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, + 0x16, 0x14, 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, + 0x14, 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, + 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, 0x20, + 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, 0x20, 0x1c, + 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, 0x20, 0x1c, 0x1c, + 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, 0x20, 0x1c, 0x1c, 0x1c, + 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, 0x20, 0x1c, 0x1c, 0x1c, 0x18, + 0x16, 0x1c, 0x1c, 0x16, 0x14, 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, + 0x1c, 0x1c, 0x16, 0x14, 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, + 0x1c, 0x16, 0x14, 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, + 0x16, 0x14, 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, + 0x14, 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, + 0x20, 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, 0x20, + 0x1c, 0x1c, 0x1c, 0x18, 0x16, 0x1c, 0x1c, 0x16, 0x14, 0x30, 0x36, + 0x38, 0x31, 0x32, 0x35, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, + 0x5c, 0x60, 0x8c, 0xa0, 0xb4, 0xbd, 0xcd, 0x4c, 0x5c, 0x60, 0x8c, + 0x90, 0xb4, 0xbd, 0xcd, 0x4e, 0x56, 0x5e, 0x66, 0x8e, 0x96, 0xae, + 0xbf, 0x4c, 0x50, 0x5c, 0x68, 0x8c, 0xb4, 0xff, 0xff, 0x4c, 0x5c, + 0x8c, 0xb4, 0xff, 0xff, 0xff, 0xff, 0x4e, 0x5e, 0x66, 0x8e, 0x9e, + 0xae, 0xff, 0xff, 0x4c, 0x50, 0x54, 0x5c, 0x8c, 0xa0, 0xb4, 0xbd, + 0x4c, 0x5c, 0x68, 0x8c, 0x98, 0xb4, 0xbd, 0xcd, 0x4e, 0x56, 0x5e, + 0x8e, 0x96, 0xae, 0xbf, 0xc7, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, + 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3c, 0x3d, 0x3d, 0x3c, + 0x3d, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3c, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3c, 0x3d, + 0x20, 0x40, 0x00, 0x05, 0x21, 0x40, 0x00, 0x07, 0x22, 0x40, 0x00, + 0x09, 0x24, 0x40, 0x00, 0x0d, 0x03, 0x20, 0x06, 0x10, 0x04, 0x20, + 0x06, 0x12, 0xe2, 0x4e, 0x00, 0x3a, 0xe3, 0x4e, 0x00, 0x3c, 0xe4, + 0x4e, 0x00, 0x3e, 0xe6, 0x4e, 0x00, 0x42, 0xea, 0x4e, 0x00, 0x46, + 0xec, 0x4e, 0x00, 0x4a, 0xee, 0x4e, 0x00, 0x4e, 0xf2, 0x6e, 0x00, + 0x56, 0xf4, 0x6e, 0x00, 0x5a, 0xf6, 0x6e, 0x00, 0x5e, 0xf3, 0x6e, + 0x02, 0x62, 0xf6, 0x6e, 0x02, 0x68, 0xf3, 0x6e, 0x04, 0x6b, 0xf6, + 0x6e, 0x04, 0x71, 0xf3, 0x8e, 0x06, 0x74, 0xf6, 0x8e, 0x06, 0x7a, + 0xf6, 0x8e, 0x06, 0x7a, 0xf6, 0x8e, 0x06, 0x7a, 0xf6, 0x8e, 0x06, + 0x7a, 0xf6, 0x8e, 0x06, 0x7a, 0xf6, 0x8e, 0x06, 0x7a, 0xf6, 0x8e, + 0x06, 0x7a, 0xf6, 0x8e, 0x06, 0x7a, 0xf6, 0x8e, 0x06, 0x7a, 0xf6, + 0x8e, 0x06, 0x7a, 0xf6, 0x8e, 0x06, 0x7a, 0x3b, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, + 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x4c, 0x5c, 0x68, 0x8c, 0x4c, 0x5c, 0x68, 0x8c, + 0x3a, 0x44, 0x4a, 0x4e, 0x46, 0x44, 0x46, 0x50, 0x50, 0x44, 0x4a, + 0x50, 0x50, 0x46, 0x50, 0x50, 0x1e, 0x14, 0x4a, 0x4a, 0x2d, 0x14, + 0x46, 0x50, 0x2d, 0x14, 0x4a, 0x4e, 0x50, 0x50, 0x50, 0x49, 0xa0, + 0xb4, 0xc5, 0xcd, 0xa0, 0xb4, 0xc5, 0xcd, 0x50, 0x4b, 0x46, 0x50, + 0x46, 0x4b, 0x46, 0x50, 0x46, 0x4b, 0x46, 0x50, 0x46, 0x4b, 0x4a, + 0x49, 0x28, 0x28, 0x46, 0x49, 0x28, 0x28, 0x46, 0x49, 0x28, 0x28, + 0x46, 0x49, 0x28, 0x28, 0x4a, 0x49, 0x24, 0x28, 0x00, 0x00, 0x01, + 0x02, 0x00, 0x00, 0x36, 0xfd, 0x36, 0xfd, 0x36, 0xfd, 0x36, 0xfe, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xa2, 0x62, 0x03, 0x07, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x10, 0x02, 0x10, 0x02, 0x10, + 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x03, 0x10, 0x02, + 0x10, 0x0b, 0x10, 0x2a, 0x10, 0x22, 0x58, 0x00, 0x69, 0x01, 0x69, + 0x01, 0x69, 0x01, 0x69, 0x01, 0x10, 0x01, 0x10, 0x03, 0x10, 0x01, + 0x10, 0x03, 0x10, 0x01, 0x10, 0x03, 0x10, 0x03, 0x10, 0x01, 0x10, + 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, + 0x41, 0x96, 0x00, 0x10, 0x49, 0xd6, 0x00, 0x10, 0x01, 0x10, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x13, 0x02, 0x19, 0x30, 0x13, + 0x02, 0x53, 0x01, 0x10, 0x03, 0x96, 0x00, 0x96, 0x00, 0x10, 0x01, + 0x10, 0x03, 0x10, 0x01, 0x10, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, + 0x02, 0x10, 0x03, 0x10, 0x02, 0x10, 0x0b, 0x10, 0x2a, 0x10, 0x22, + 0x58, 0x00, 0x55, 0x01, 0x15, 0x00, 0x15, 0x00, 0x55, 0x01, 0x10, + 0x01, 0x10, 0x03, 0x10, 0x01, 0x10, 0x03, 0x10, 0x01, 0x10, 0x03, + 0x10, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, + 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x41, 0x96, 0x00, 0x10, 0x49, 0xd6, + 0x00, 0x10, 0x01, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, + 0x13, 0x00, 0x19, 0x30, 0x13, 0x00, 0x53, 0x01, 0x10, 0x03, 0x96, + 0x00, 0x96, 0x00, 0x10, 0x01, 0x10, 0x03, 0x10, 0x01, 0x10, 0x03, + 0x10, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0xf1, 0xf9, 0xfd, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf1, 0xf9, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, + 0xf9, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0xfb, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf4, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf6, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xfd, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xfd, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x4c, 0x68, + 0xa0, 0xc9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x61, 0x61, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x27, + 0x26, 0x25, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x3b, 0x14, 0x04, 0xda, + 0x08, 0x03, 0x70, 0x8e, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x00, 0x3b, 0x14, 0x04, 0xda, 0x08, 0x03, 0x70, 0x8e, 0xac, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x06, 0x00, 0x00, 0x80, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x3b, 0x14, 0x04, 0xda, 0x08, + 0x08, 0x4c, 0x5c, 0x68, 0x8c, 0xa0, 0xb4, 0xc9, 0xcd, 0x75, 0x00, + 0x3b, 0x14, 0x04, 0xda, 0x08, 0x08, 0x4c, 0x5c, 0x68, 0x8c, 0xa0, + 0xb4, 0xc9, 0xcd, 0x80, 0x06, 0x00, 0x00, 0x80, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/bmi.c b/drivers/net/wireless/ath/ath6kl-3.5/bmi.c new file mode 100644 index 000000000000..f795017e3aaa --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/bmi.c @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "hif-ops.h" +#include "target.h" +#include "debug.h" + +int ath6kl_bmi_done(struct ath6kl *ar) +{ + int ret; + u32 cid = BMI_DONE; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n"); + return 0; + } + + ar->bmi.done_sent = true; + endianBuf32 = cpu_to_le32(cid); + + ret = ath6kl_hif_bmi_write(ar, (u8 *)&endianBuf32, sizeof(endianBuf32)); + if (ret) { + ath6kl_err("Unable to send bmi done: %d\n", ret); + return ret; + } + + return 0; +} + +int ath6kl_bmi_get_target_info(struct ath6kl *ar, + struct ath6kl_bmi_target_info *targ_info) +{ + int ret; + u32 cid = BMI_GET_TARGET_INFO; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + endianBuf32 = cpu_to_le32(cid); + ret = ath6kl_hif_bmi_write(ar, (u8 *)&endianBuf32, sizeof(endianBuf32)); + if (ret) { + ath6kl_err("Unable to send get target info: %d\n", ret); + return ret; + } + + if (ar->hif_type == ATH6KL_HIF_TYPE_USB) { + ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info, + sizeof(*targ_info)); + } else { + ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version, + sizeof(targ_info->version)); + } + + if (ret) { + ath6kl_err("Unable to recv target info: %d\n", ret); + return ret; + } + + if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) { + /* Determine how many bytes are in the Target's targ_info */ + ret = ath6kl_hif_bmi_read(ar, + (u8 *)&targ_info->byte_count, + sizeof(targ_info->byte_count)); + if (ret) { + ath6kl_err("unable to read target info byte count: %d\n", + ret); + return ret; + } + + /* + * The target's targ_info doesn't match the host's targ_info. + * We need to do some backwards compatibility to make this work. + */ + if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { + WARN_ON(1); + return -EINVAL; + } + + /* Read the remainder of the targ_info */ + ret = ath6kl_hif_bmi_read(ar, + ((u8 *)targ_info) + + sizeof(targ_info->byte_count), + sizeof(*targ_info) - + sizeof(targ_info->byte_count)); + + if (ret) { + ath6kl_err("Unable to read target info (%d bytes): %d\n", + le32_to_cpu(targ_info->byte_count), ret); + return ret; + } + } + + ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", + le32_to_cpu(targ_info->version), le32_to_cpu(targ_info->type)); + + return 0; +} + +int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) +{ + u32 cid = BMI_READ_MEMORY; + int ret; + u32 offset; + u32 len_remain, rx_len; + u16 size; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + + size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len); + if (size > ar->bmi.max_cmd_size) { + WARN_ON(1); + return -EINVAL; + } + memset(ar->bmi.cmd_buf, 0, size); + + ath6kl_dbg(ATH6KL_DBG_BMI, + "bmi read memory: device: addr: 0x%x, len: %d\n", + addr, len); + + len_remain = len; + + while (len_remain) { + rx_len = (len_remain < ar->bmi.max_data_size) ? + len_remain : ar->bmi.max_data_size; + offset = 0; + endianBuf32 = cpu_to_le32(cid); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + offset += sizeof(cid); + endianBuf32 = cpu_to_le32(addr); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + offset += sizeof(addr); + endianBuf32 = cpu_to_le32(rx_len); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + offset += sizeof(len); + + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); + if (ret) { + ath6kl_err("Unable to write to the device: %d\n", + ret); + return ret; + } + ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len); + if (ret) { + ath6kl_err("Unable to read from the device: %d\n", + ret); + return ret; + } + memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len); + len_remain -= rx_len; addr += rx_len; + } + +#ifdef __BIG_ENDIAN + if (len == 4) { + u32 buf_tmp; + + memcpy(&buf_tmp, buf, sizeof(u32)); + buf_tmp = le32_to_cpu(buf_tmp); + memcpy(buf, &buf_tmp, sizeof(u32)); + } +#endif + + return 0; +} + +int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) +{ + u32 cid = BMI_WRITE_MEMORY; + int ret; + u32 offset; + u32 len_remain, tx_len; + const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len); + u8 aligned_buf[400]; + u8 *src; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + + if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) { + WARN_ON(1); + return -EINVAL; + } + + if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf))) + return -E2BIG; + + memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header); + + ath6kl_dbg(ATH6KL_DBG_BMI, + "bmi write memory: addr: 0x%x, len: %d\n", addr, len); + + len_remain = len; + while (len_remain) { + src = &buf[len - len_remain]; + + if (len_remain < (ar->bmi.max_data_size - header)) { + if (len_remain & 3) { + /* align it with 4 bytes */ + len_remain = len_remain + + (4 - (len_remain & 3)); + memcpy(aligned_buf, src, len_remain); + src = aligned_buf; + } + tx_len = len_remain; + } else { + tx_len = (ar->bmi.max_data_size - header); + } + + offset = 0; + endianBuf32 = cpu_to_le32(cid); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + offset += sizeof(cid); + endianBuf32 = cpu_to_le32(addr); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + offset += sizeof(addr); + endianBuf32 = cpu_to_le32(tx_len); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + offset += sizeof(tx_len); + if (len == 4) { + endianBuf32 = cpu_to_le32(*((u32 *)src)); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + } else { + memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); + } + offset += tx_len; + + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); + if (ret) { + ath6kl_err("Unable to write to the device: %d\n", + ret); + return ret; + } + len_remain -= tx_len; addr += tx_len; + } + + return 0; +} + +int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) +{ + u32 cid = BMI_EXECUTE; + int ret; + u32 offset; + u16 size; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + + size = sizeof(cid) + sizeof(addr) + sizeof(param); + if (size > ar->bmi.max_cmd_size) { + WARN_ON(1); + return -EINVAL; + } + memset(ar->bmi.cmd_buf, 0, size); + + ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", + addr, *param); + + offset = 0; + endianBuf32 = cpu_to_le32(cid); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(cid); + endianBuf32 = cpu_to_le32(addr); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(addr); + endianBuf32 = cpu_to_le32(*param); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(*param); + + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); + if (ret) { + ath6kl_err("Unable to write to the device: %d\n", ret); + return ret; + } + + ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); + if (ret) { + ath6kl_err("Unable to read from the device: %d\n", ret); + return ret; + } + + memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); + +#ifdef __BIG_ENDIAN + { + u32 buf_tmp; + + memcpy(&buf_tmp, param, sizeof(u32)); + buf_tmp = le32_to_cpu(buf_tmp); + memcpy(param, &buf_tmp, sizeof(u32)); + } +#endif + + return 0; +} + +int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) +{ + u32 cid = BMI_SET_APP_START; + int ret; + u32 offset; + u16 size; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + + size = sizeof(cid) + sizeof(addr); + if (size > ar->bmi.max_cmd_size) { + WARN_ON(1); + return -EINVAL; + } + memset(ar->bmi.cmd_buf, 0, size); + + ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); + + offset = 0; + endianBuf32 = cpu_to_le32(cid); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(cid); + endianBuf32 = cpu_to_le32(addr); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(addr); + + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); + if (ret) { + ath6kl_err("Unable to write to the device: %d\n", ret); + return ret; + } + + return 0; +} + +int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) +{ + u32 cid = BMI_READ_SOC_REGISTER; + int ret; + u32 offset; + u16 size; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + + size = sizeof(cid) + sizeof(addr); + if (size > ar->bmi.max_cmd_size) { + WARN_ON(1); + return -EINVAL; + } + memset(ar->bmi.cmd_buf, 0, size); + + ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); + + offset = 0; + endianBuf32 = cpu_to_le32(cid); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(cid); + endianBuf32 = cpu_to_le32(addr); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(addr); + + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); + if (ret) { + ath6kl_err("Unable to write to the device: %d\n", ret); + return ret; + } + + ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); + if (ret) { + ath6kl_err("Unable to read from the device: %d\n", ret); + return ret; + } + memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); + +#ifdef __BIG_ENDIAN + { + u32 buf_tmp; + + memcpy(&buf_tmp, param, sizeof(u32)); + buf_tmp = le32_to_cpu(buf_tmp); + memcpy(param, &buf_tmp, sizeof(u32)); + } +#endif + + return 0; +} + +int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) +{ + u32 cid = BMI_WRITE_SOC_REGISTER; + int ret; + u32 offset; + u16 size; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + + size = sizeof(cid) + sizeof(addr) + sizeof(param); + if (size > ar->bmi.max_cmd_size) { + WARN_ON(1); + return -EINVAL; + } + memset(ar->bmi.cmd_buf, 0, size); + + ath6kl_dbg(ATH6KL_DBG_BMI, + "bmi write SOC reg: addr: 0x%x, param: %d\n", + addr, param); + + offset = 0; + endianBuf32 = cpu_to_le32(cid); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(cid); + endianBuf32 = cpu_to_le32(addr); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(addr); + endianBuf32 = cpu_to_le32(param); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(param); + + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); + if (ret) { + ath6kl_err("Unable to write to the device: %d\n", ret); + return ret; + } + + return 0; +} + +int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) +{ + u32 cid = BMI_LZ_DATA; + int ret; + u32 offset; + u32 len_remain, tx_len; + const u32 header = sizeof(cid) + sizeof(len); + u16 size; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + + size = ar->bmi.max_data_size + header; + if (size > ar->bmi.max_cmd_size) { + WARN_ON(1); + return -EINVAL; + } + memset(ar->bmi.cmd_buf, 0, size); + + ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", + len); + + len_remain = len; + while (len_remain) { + tx_len = (len_remain < (ar->bmi.max_data_size - header)) ? + len_remain : (ar->bmi.max_data_size - header); + + offset = 0; + endianBuf32 = cpu_to_le32(cid); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + offset += sizeof(cid); + endianBuf32 = cpu_to_le32(tx_len); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, + sizeof(endianBuf32)); + offset += sizeof(tx_len); + memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], + tx_len); + offset += tx_len; + + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); + if (ret) { + ath6kl_err("Unable to write to the device: %d\n", + ret); + return ret; + } + + len_remain -= tx_len; + } + + return 0; +} + +int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) +{ + u32 cid = BMI_LZ_STREAM_START; + int ret; + u32 offset; + u16 size; + u32 endianBuf32; + + if (ar->bmi.done_sent) { + ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); + return -EACCES; + } + + size = sizeof(cid) + sizeof(addr); + if (size > ar->bmi.max_cmd_size) { + WARN_ON(1); + return -EINVAL; + } + memset(ar->bmi.cmd_buf, 0, size); + + ath6kl_dbg(ATH6KL_DBG_BMI, + "bmi LZ stream start: addr: 0x%x)\n", + addr); + + offset = 0; + endianBuf32 = cpu_to_le32(cid); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(cid); + endianBuf32 = cpu_to_le32(addr); + memcpy(&(ar->bmi.cmd_buf[offset]), &endianBuf32, sizeof(endianBuf32)); + offset += sizeof(addr); + + ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); + if (ret) { + ath6kl_err("Unable to start LZ stream to the device: %d\n", + ret); + return ret; + } + + return 0; +} + +int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) +{ + int ret; + u32 last_word = 0; + u32 last_word_offset = len & ~0x3; + u32 unaligned_bytes = len & 0x3; + + ret = ath6kl_bmi_lz_stream_start(ar, addr); + if (ret) + return ret; + + if (unaligned_bytes) { + /* copy the last word into a zero padded buffer */ + memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); + } + + ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); + if (ret) + return ret; + + if (unaligned_bytes) + ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); + + if (!ret) { + /* Close compressed stream and open a new (fake) one. + * This serves mainly to flush Target caches. */ + ret = ath6kl_bmi_lz_stream_start(ar, 0x00); + } + return ret; +} + +void ath6kl_bmi_reset(struct ath6kl *ar) +{ + ar->bmi.done_sent = false; +} + +int ath6kl_bmi_init(struct ath6kl *ar) +{ + if (WARN_ON(ar->bmi.max_data_size == 0)) + return -EINVAL; + + /* cmd + addr + len + data_size */ + ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3); + + ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_ATOMIC); + if (!ar->bmi.cmd_buf) + return -ENOMEM; + + return 0; +} + +void ath6kl_bmi_cleanup(struct ath6kl *ar) +{ + kfree(ar->bmi.cmd_buf); + ar->bmi.cmd_buf = NULL; +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/bmi.h b/drivers/net/wireless/ath/ath6kl-3.5/bmi.h new file mode 100644 index 000000000000..23c22a55435b --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/bmi.h @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 BMI_H +#define BMI_H + +/* + * Bootloader Messaging Interface (BMI) + * + * BMI is a very simple messaging interface used during initialization + * to read memory, write memory, execute code, and to define an + * application entry PC. + * + * It is used to download an application to ATH6KL, to provide + * patches to code that is already resident on ATH6KL, and generally + * to examine and modify state. The Host has an opportunity to use + * BMI only once during bootup. Once the Host issues a BMI_DONE + * command, this opportunity ends. + * + * The Host writes BMI requests to mailbox0, and reads BMI responses + * from mailbox0. BMI requests all begin with a command + * (see below for specific commands), and are followed by + * command-specific data. + * + * Flow control: + * The Host can only issue a command once the Target gives it a + * "BMI Command Credit", using ATH6KL Counter #4. As soon as the + * Target has completed a command, it issues another BMI Command + * Credit (so the Host can issue the next command). + * + * BMI handles all required Target-side cache flushing. + */ + +/* BMI Commands */ + +#define BMI_NO_COMMAND 0 + +#define BMI_DONE 1 +/* + * Semantics: Host is done using BMI + * Request format: + * u32 command (BMI_DONE) + * Response format: none + */ + +#define BMI_READ_MEMORY 2 +/* + * Semantics: Host reads ATH6KL memory + * Request format: + * u32 command (BMI_READ_MEMORY) + * u32 address + * u32 length, at most BMI_DATASZ_MAX + * Response format: + * u8 data[length] + */ + +#define BMI_WRITE_MEMORY 3 +/* + * Semantics: Host writes ATH6KL memory + * Request format: + * u32 command (BMI_WRITE_MEMORY) + * u32 address + * u32 length, at most BMI_DATASZ_MAX + * u8 data[length] + * Response format: none + */ + +#define BMI_EXECUTE 4 +/* + * Semantics: Causes ATH6KL to execute code + * Request format: + * u32 command (BMI_EXECUTE) + * u32 address + * u32 parameter + * Response format: + * u32 return value + */ + +#define BMI_SET_APP_START 5 +/* + * Semantics: Set Target application starting address + * Request format: + * u32 command (BMI_SET_APP_START) + * u32 address + * Response format: none + */ + +#define BMI_READ_SOC_REGISTER 6 +/* + * Semantics: Read a 32-bit Target SOC register. + * Request format: + * u32 command (BMI_READ_REGISTER) + * u32 address + * Response format: + * u32 value + */ + +#define BMI_WRITE_SOC_REGISTER 7 +/* + * Semantics: Write a 32-bit Target SOC register. + * Request format: + * u32 command (BMI_WRITE_REGISTER) + * u32 address + * u32 value + * + * Response format: none + */ + +#define BMI_GET_TARGET_ID 8 +#define BMI_GET_TARGET_INFO 8 +/* + * Semantics: Fetch the 4-byte Target information + * Request format: + * u32 command (BMI_GET_TARGET_ID/INFO) + * Response format1 (old firmware): + * u32 TargetVersionID + * Response format2 (newer firmware): + * u32 TARGET_VERSION_SENTINAL + * struct bmi_target_info; + */ + +#define TARGET_VERSION_SENTINAL 0xffffffff +#define TARGET_TYPE_AR6003 3 +#define TARGET_TYPE_AR6004 5 +#define TARGET_TYPE_AR6006 6 + +/* + * Only for AR6004v4 or later & AR6006 + * + * It's better not to use this define in code. + * Instead, using ATH6KL_VIF_MAX in the host driver scope. + */ +#define TARGET_VIF_MAX (4) + +/* + * BIT0 : Single or Dual + * BIT1 : 1SS or 2SS + * BIT2 : HT20-Only or HT20/40 + */ +#define TARGET_SUBTYPE_DUAL BIT(0) +#define TARGET_SUBTYPE_2SS BIT(1) +#define TARGET_SUBTYPE_HT40 BIT(2) + +#define TARGET_SUBTYPE_HT20_1SS_SING_BAND (0) +#define TARGET_SUBTYPE_HT40_1SS_SING_BAND (TARGET_SUBTYPE_HT40) +#define TARGET_SUBTYPE_HT20_1SS_DUAL_BAND (TARGET_SUBTYPE_DUAL) +#define TARGET_SUBTYPE_HT40_1SS_DUAL_BAND (TARGET_SUBTYPE_HT40 | \ + TARGET_SUBTYPE_DUAL) +#define TARGET_SUBTYPE_HT20_2SS_SING_BAND (TARGET_SUBTYPE_2SS) +#define TARGET_SUBTYPE_HT40_2SS_SING_BAND (TARGET_SUBTYPE_HT40 | \ + TARGET_SUBTYPE_2SS) +#define TARGET_SUBTYPE_HT20_2SS_DUAL_BAND (TARGET_SUBTYPE_DUAL | \ + TARGET_SUBTYPE_2SS) +#define TARGET_SUBTYPE_HT40_2SS_DUAL_BAND (TARGET_SUBTYPE_HT40 | \ + TARGET_SUBTYPE_DUAL | \ + TARGET_SUBTYPE_2SS) + +#define BMI_ROMPATCH_INSTALL 9 +/* + * Semantics: Install a ROM Patch. + * Request format: + * u32 command (BMI_ROMPATCH_INSTALL) + * u32 Target ROM Address + * u32 Target RAM Address or Value (depending on Target Type) + * u32 Size, in bytes + * u32 Activate? 1-->activate; + * 0-->install but do not activate + * Response format: + * u32 PatchID + */ + +#define BMI_ROMPATCH_UNINSTALL 10 +/* + * Semantics: Uninstall a previously-installed ROM Patch, + * automatically deactivating, if necessary. + * Request format: + * u32 command (BMI_ROMPATCH_UNINSTALL) + * u32 PatchID + * + * Response format: none + */ + +#define BMI_ROMPATCH_ACTIVATE 11 +/* + * Semantics: Activate a list of previously-installed ROM Patches. + * Request format: + * u32 command (BMI_ROMPATCH_ACTIVATE) + * u32 rompatch_count + * u32 PatchID[rompatch_count] + * + * Response format: none + */ + +#define BMI_ROMPATCH_DEACTIVATE 12 +/* + * Semantics: Deactivate a list of active ROM Patches. + * Request format: + * u32 command (BMI_ROMPATCH_DEACTIVATE) + * u32 rompatch_count + * u32 PatchID[rompatch_count] + * + * Response format: none + */ + + +#define BMI_LZ_STREAM_START 13 +/* + * Semantics: Begin an LZ-compressed stream of input + * which is to be uncompressed by the Target to an + * output buffer at address. The output buffer must + * be sufficiently large to hold the uncompressed + * output from the compressed input stream. This BMI + * command should be followed by a series of 1 or more + * BMI_LZ_DATA commands. + * u32 command (BMI_LZ_STREAM_START) + * u32 address + * Note: Not supported on all versions of ROM firmware. + */ + +#define BMI_LZ_DATA 14 +/* + * Semantics: Host writes ATH6KL memory with LZ-compressed + * data which is uncompressed by the Target. This command + * must be preceded by a BMI_LZ_STREAM_START command. A series + * of BMI_LZ_DATA commands are considered part of a single + * input stream until another BMI_LZ_STREAM_START is issued. + * Request format: + * u32 command (BMI_LZ_DATA) + * u32 length (of compressed data), + * at most BMI_DATASZ_MAX + * u8 CompressedData[length] + * Response format: none + * Note: Not supported on all versions of ROM firmware. + */ + +#define BMI_COMMUNICATION_TIMEOUT 1000 /* in msec */ + +struct ath6kl; +struct ath6kl_bmi_target_info { + __le32 byte_count; /* size of this structure */ + __le32 version; /* target version id */ + __le32 type; /* target type */ +} __packed; + +int ath6kl_bmi_init(struct ath6kl *ar); +void ath6kl_bmi_cleanup(struct ath6kl *ar); +void ath6kl_bmi_reset(struct ath6kl *ar); + +int ath6kl_bmi_done(struct ath6kl *ar); +int ath6kl_bmi_get_target_info(struct ath6kl *ar, + struct ath6kl_bmi_target_info *targ_info); +int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len); +int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len); +int ath6kl_bmi_execute(struct ath6kl *ar, + u32 addr, u32 *param); +int ath6kl_bmi_set_app_start(struct ath6kl *ar, + u32 addr); +int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param); +int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param); +int ath6kl_bmi_lz_data(struct ath6kl *ar, + u8 *buf, u32 len); +int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, + u32 addr); +int ath6kl_bmi_fast_download(struct ath6kl *ar, + u32 addr, u8 *buf, u32 len); +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/cfg80211.c b/drivers/net/wireless/ath/ath6kl-3.5/cfg80211.c new file mode 100644 index 000000000000..97a351d2a277 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/cfg80211.c @@ -0,0 +1,6775 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 +#ifndef CE_OLD_KERNEL_SUPPORT_2_6_23 +#include +#endif +#include +#include +#include "core.h" +#include "cfg80211.h" +#include "debug.h" +#include "hif-ops.h" +#include "testmode.h" +#include "cfg80211_btcoex.h" +#ifdef ATH6KL_DIAGNOSTIC +#include "diagnose.h" +#endif +#include "pm.h" +#include "rttm.h" + +unsigned int debug_quirks = ATH6KL_MODULE_DEF_DEBUG_QUIRKS; + + +module_param(debug_quirks, uint, 0644); + + + +#define RATETAB_ENT(_rate, _rateid, _flags) { \ + .bitrate = (_rate), \ + .flags = (_flags), \ + .hw_value = (_rateid), \ +} + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .hw_value = (_channel), \ + .center_freq = 5000 + (5 * (_channel)), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_rate ath6kl_rates[] = { + RATETAB_ENT(10, 0x1, 0), + RATETAB_ENT(20, 0x2, 0), + RATETAB_ENT(55, 0x4, 0), + RATETAB_ENT(110, 0x8, 0), + RATETAB_ENT(60, 0x10, 0), + RATETAB_ENT(90, 0x20, 0), + RATETAB_ENT(120, 0x40, 0), + RATETAB_ENT(180, 0x80, 0), + RATETAB_ENT(240, 0x100, 0), + RATETAB_ENT(360, 0x200, 0), + RATETAB_ENT(480, 0x400, 0), + RATETAB_ENT(540, 0x800, 0), +}; + +#define ath6kl_a_rates (ath6kl_rates + 4) +#define ath6kl_a_rates_size 8 +#define ath6kl_g_rates (ath6kl_rates + 0) +#define ath6kl_g_rates_size 12 +#define ath6kl_b_rates_size 4 + +/* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */ +static const u8 up_to_ac[] = { + WMM_AC_BE, + WMM_AC_BK, + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO, +}; + +static struct ieee80211_channel ath6kl_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static struct ieee80211_channel ath6kl_5ghz_a_channels[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(144, 0), + CHAN5G(149, 0), CHAN5G(153, 0), + CHAN5G(157, 0), CHAN5G(161, 0), + CHAN5G(165, 0), +}; + +static struct ieee80211_supported_band ath6kl_band_2ghz = { + .n_channels = ARRAY_SIZE(ath6kl_2ghz_channels), + .channels = ath6kl_2ghz_channels, + .n_bitrates = ath6kl_g_rates_size, + .bitrates = ath6kl_g_rates, + .ht_cap = { + .ht_supported = true, + .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_TX_STBC | + 0x100 | /* FIXME : One chain RX STBC */ + IEEE80211_HT_CAP_SM_PS, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, + .mcs = { + .rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + }, +}; + +static struct ieee80211_supported_band ath6kl_band_5ghz = { + .n_channels = ARRAY_SIZE(ath6kl_5ghz_a_channels), + .channels = ath6kl_5ghz_a_channels, + .n_bitrates = ath6kl_a_rates_size, + .bitrates = ath6kl_a_rates, + .ht_cap = { + .ht_supported = true, + .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_TX_STBC | + 0x100 | /* FIXME : One chain RX STBC */ + IEEE80211_HT_CAP_SM_PS, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, + .mcs = { + .rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + }, +}; + +/* Max. 2 devices = 1STA + 1SOFTAP */ +static const struct ieee80211_iface_limit ath6kl_limits_sta_ap[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static struct ieee80211_iface_combination + ath6kl_iface_combinations_sta_ap[] = { + { + .num_different_channels = 1, + .max_interfaces = 2, + .limits = ath6kl_limits_sta_ap, + .n_limits = ARRAY_SIZE(ath6kl_limits_sta_ap), + }, +}; + +/* Max. 2 devices = 1STA&P2P-DEVICE + 1P2P-GO|P2P-CLIENT */ +static const struct ieee80211_iface_limit ath6kl_limits_p2p_concurrent2[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), + }, +}; + +static struct ieee80211_iface_combination + ath6kl_iface_combinations_p2p_concurrent2[] = { + { + .num_different_channels = 1, + .max_interfaces = 2, + .limits = ath6kl_limits_p2p_concurrent2, + .n_limits = ARRAY_SIZE(ath6kl_limits_p2p_concurrent2), + }, +}; + +/* Max. 3 devices = 1STA + 1P2P-DEVICE + 1P2P-GO|P2P-CLIENT */ +static const struct ieee80211_iface_limit ath6kl_limits_p2p_concurrent3[] = { + { + .max = 3, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), + }, +}; + +static struct ieee80211_iface_combination + ath6kl_iface_combinations_p2p_concurrent3[] = { + { + .num_different_channels = 1, + .max_interfaces = 3, + .limits = ath6kl_limits_p2p_concurrent3, + .n_limits = ARRAY_SIZE(ath6kl_limits_p2p_concurrent3), + }, +}; + +/* Max. 4 devices = 1STA + 1P2P-DEVICE + 2P2P-GO|P2P-CLIENT */ +static const struct ieee80211_iface_limit ath6kl_limits_p2p_concurrent4[] = { + { + .max = 4, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 2, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), + }, +}; + +static struct ieee80211_iface_combination + ath6kl_iface_combinations_p2p_concurrent4[] = { + { + .num_different_channels = 1, + .max_interfaces = 4, + .limits = ath6kl_limits_p2p_concurrent4, + .n_limits = ARRAY_SIZE(ath6kl_limits_p2p_concurrent4), + }, +}; + +/* Max. 4 devices = 1STA + 1P2P-DEVICE + 1P2P-GO|P2P-CLIENT + 1SOFTAP */ +static const struct ieee80211_iface_limit ath6kl_limits_p2p_concurrent4_1[] = { + { + .max = 3, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), + }, +}; + +static struct ieee80211_iface_combination + ath6kl_iface_combinations_p2p_concurrent4_1[] = { + { + .num_different_channels = 1, + .max_interfaces = 4, + .limits = ath6kl_limits_p2p_concurrent4_1, + .n_limits = ARRAY_SIZE(ath6kl_limits_p2p_concurrent4_1), + }, +}; + +#define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */ + +#ifdef PMF_SUPPORT +#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05 +#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06 + +static int ath6kl_set_akm_suites(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); +static int ath6kl_set_rsn_cap(struct wiphy *wiphy, struct net_device *dev, + const u8 *ies, int ies_len); +#endif + +static int ath6kl_set_wpa_version(struct ath6kl_vif *vif, + enum nl80211_wpa_versions wpa_version) +{ + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %u\n", __func__, wpa_version); + + if (!wpa_version) { + vif->auth_mode = NONE_AUTH; + } else if (wpa_version & NL80211_WPA_VERSION_2) { + vif->auth_mode = WPA2_AUTH; + } else if (wpa_version & NL80211_WPA_VERSION_1) { + vif->auth_mode = WPA_AUTH; + } else { + ath6kl_err("%s: %u not supported\n", __func__, wpa_version); + return -ENOTSUPP; + } + + return 0; +} + +static int ath6kl_set_auth_type(struct ath6kl_vif *vif, + enum nl80211_auth_type auth_type) +{ + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, auth_type); + + switch (auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + vif->dot11_auth_mode = OPEN_AUTH; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + vif->dot11_auth_mode = SHARED_AUTH; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + vif->dot11_auth_mode = LEAP_AUTH; + break; + + case NL80211_AUTHTYPE_AUTOMATIC: + vif->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH; + break; + + default: + ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type); + return -ENOTSUPP; + } + + return 0; +} + +static int ath6kl_set_cipher(struct ath6kl_vif *vif, u32 cipher, bool ucast) +{ + u8 *ar_cipher = ucast ? &vif->prwise_crypto : &vif->grp_crypto; + u8 *ar_cipher_len = ucast ? &vif->prwise_crypto_len : + &vif->grp_crypto_len; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n", + __func__, cipher, ucast); + + switch (cipher) { + case 0: + /* our own hack to use value 0 as no crypto used */ + *ar_cipher = NONE_CRYPT; + *ar_cipher_len = 0; + break; + case WLAN_CIPHER_SUITE_WEP40: + *ar_cipher = WEP_CRYPT; + *ar_cipher_len = 5; + break; + case WLAN_CIPHER_SUITE_WEP104: + *ar_cipher = WEP_CRYPT; + *ar_cipher_len = 13; + break; + case WLAN_CIPHER_SUITE_TKIP: + *ar_cipher = TKIP_CRYPT; + *ar_cipher_len = 0; + break; + case WLAN_CIPHER_SUITE_CCMP: + *ar_cipher = AES_CRYPT; + *ar_cipher_len = 0; + break; + case WLAN_CIPHER_SUITE_SMS4: + *ar_cipher = WAPI_CRYPT; + *ar_cipher_len = 0; + break; + default: + ath6kl_err("cipher 0x%x not supported\n", cipher); + return -ENOTSUPP; + } + + return 0; +} + +static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt) +{ + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: 0x%x\n", __func__, key_mgmt); + + if (key_mgmt == WLAN_AKM_SUITE_PSK) { + if (vif->auth_mode == WPA_AUTH) + vif->auth_mode = WPA_PSK_AUTH; + else if (vif->auth_mode == WPA2_AUTH) + vif->auth_mode = WPA2_PSK_AUTH; + } else if (key_mgmt == 0x00409600) { + if (vif->auth_mode == WPA_AUTH) + vif->auth_mode = WPA_AUTH_CCKM; + else if (vif->auth_mode == WPA2_AUTH) + vif->auth_mode = WPA2_AUTH_CCKM; +#ifdef PMF_SUPPORT + } else if (key_mgmt == WLAN_AKM_SUITE_PSK_SHA256) { + vif->auth_mode = WPA2_PSK_SHA256_AUTH; + } else if ((key_mgmt != WLAN_AKM_SUITE_8021X) && + (key_mgmt != WLAN_AKM_SUITE_8021X_SHA256)) { +#else + } else if (key_mgmt != WLAN_AKM_SUITE_8021X) { +#endif + vif->auth_mode = NONE_AUTH; + } +} + +#ifdef ATH6KL_SUPPORT_WIFI_KTK +static void ath6kl_install_ktk_ptk(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + struct ath6kl_key *ptk = NULL; + enum wmi_sync_flag sync_flag = SYNC_BOTH_WMIFLAG; + int status; + char buff[32]; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s\n", __func__); + + memset(buff, 0, 32); + memcpy(buff, ar->ktk_passphrase, 16); + + /* set ptk at index 0 */ + ptk = &vif->keys[0]; + memset(ptk, 0, sizeof(struct ath6kl_key)); + + ptk->key_len = 16; + memcpy(ptk->key, ar->ktk_passphrase, ptk->key_len); + ptk->seq_len = 6; + memset(ptk->seq, 0, ptk->seq_len); + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WMI_SYC)) + sync_flag = NO_SYNC_WMIFLAG; + + status = ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, 0, + KTK_CRYPT, GROUP_USAGE | TX_USAGE, + ptk->key_len, + ptk->seq, + ptk->seq_len, + ptk->key, + KEY_OP_INIT_VAL, + NULL, + sync_flag); + + if (status) + ath6kl_err("%s: set ptk at index 0 failed\n", __func__); +} +#endif + +static bool __ath6kl_cfg80211_ready(struct ath6kl *ar) +{ + if (!test_bit(WMI_READY, &ar->flag)) { + ath6kl_err("wmi is not ready\n"); + return false; + } + + return true; +} + +static bool ath6kl_cfg80211_ready(struct ath6kl_vif *vif) +{ + if (!__ath6kl_cfg80211_ready(vif->ar)) + return false; + +#if defined(USB_AUTO_SUSPEND) + if ((vif->ar->state == ATH6KL_STATE_WOW) || + (vif->ar->state == ATH6KL_STATE_DEEPSLEEP) || + (vif->ar->state == ATH6KL_STATE_PRE_SUSPEND)) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "ignore wlan disabled in AUTO suspend mode!\n"); + } else { + if (!test_bit(WLAN_ENABLED, &vif->flags)) { + ath6kl_err("wlan disabled\n"); + return false; + } + } +#else + if (!test_bit(WLAN_ENABLED, &vif->flags)) { + ath6kl_err("wlan disabled\n"); + return false; + } +#endif + + return true; +} + +static bool ath6kl_is_wpa_ie(const u8 *pos) +{ + return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && + pos[4] == 0xf2 && pos[5] == 0x01; +} + +static bool ath6kl_is_rsn_ie(const u8 *pos) +{ + return pos[0] == WLAN_EID_RSN; +} + +static bool ath6kl_is_wps_ie(const u8 *pos) +{ + return (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 && + pos[5] == 0x04); +} + +static bool ath6kl_is_wmm_ie(const u8 *pos) +{ + return (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 && + pos[5] == 0x02); +} + +static int ath6kl_set_assoc_req_ies(struct ath6kl_vif *vif, const u8 *ies, + size_t ies_len) +{ + struct ath6kl *ar = vif->ar; + const u8 *pos; + u8 *buf = NULL; + size_t len = 0; + int ret; + + /* + * Clear previously set flag + */ + + vif->connect_ctrl_flags &= ~CONNECT_WPS_FLAG; + + /* + * Filter out RSN/WPA or P2P/WFD IE(s) + */ + if (ies && ies_len) { + buf = kmalloc(ies_len, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + pos = ies; + + while (pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + if (!(ath6kl_is_wpa_ie(pos) || + ath6kl_is_rsn_ie(pos))) { + if ((ath6kl_is_p2p_ie(pos) || + ath6kl_is_wfd_ie(pos)) && + !ath6kl_p2p_ie_append(vif, + P2P_IE_IN_ASSOC_REQ)) + ath6kl_info("Remove Asso's P2P IE\n"); + else { + memcpy(buf + len, pos, 2 + pos[1]); + len += 2 + pos[1]; + } + } + + if (ath6kl_is_wps_ie(pos)) + vif->connect_ctrl_flags |= CONNECT_WPS_FLAG; + + pos += 2 + pos[1]; + } + } + + ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_ASSOC_REQ, buf, len); + kfree(buf); + return ret; +} + +static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type) +{ + switch (type) { + case NL80211_IFTYPE_STATION: + *nw_type = INFRA_NETWORK; + break; + case NL80211_IFTYPE_ADHOC: + *nw_type = ADHOC_NETWORK; + break; + case NL80211_IFTYPE_AP: + *nw_type = AP_NETWORK; + break; + case NL80211_IFTYPE_P2P_CLIENT: + *nw_type = INFRA_NETWORK; + break; + case NL80211_IFTYPE_P2P_GO: + *nw_type = AP_NETWORK; + break; + default: + ath6kl_err("invalid interface type %u\n", type); + return -ENOTSUPP; + } + + return 0; +} + +static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type, + u8 *if_idx, u8 *nw_type) +{ + int i; + + if (ath6kl_nliftype_to_drv_iftype(type, nw_type)) + return false; + + if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) && + ar->num_vif)) + return false; + + /* we use firmware index in 1, 4, 3, 2 order to align with + * firmware design in the dedicate interface case + */ + if (ar->p2p_concurrent && ar->p2p_dedicate) { + if ((ar->p2p_concurrent_ap) && + (type == NL80211_IFTYPE_AP)) { + for (i = 0; i < ar->max_norm_iface; i++) { + if ((ar->avail_idx_map >> i) & BIT(0)) { + *if_idx = i; + return true; + } + } + return false; + } + + if (type == NL80211_IFTYPE_STATION || + type == NL80211_IFTYPE_AP || + type == NL80211_IFTYPE_ADHOC) { + for (i = (ar->vif_max - 1); i > 0; i--) { + if ((ar->avail_idx_map >> i) & BIT(0)) { + *if_idx = i; + return true; + } + } + } + + if (type == NL80211_IFTYPE_P2P_CLIENT || + type == NL80211_IFTYPE_P2P_GO) { + for (i = (ar->vif_max - 1); i > + (ar->max_norm_iface - 1); i--) { + if ((ar->avail_idx_map >> i) & BIT(0)) { + *if_idx = i; + return true; + } + } + } + } else { + + if (type == NL80211_IFTYPE_STATION || + type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) { + for (i = 0; i < ar->vif_max; i++) { + if ((ar->avail_idx_map >> i) & BIT(0)) { + *if_idx = i; + return true; + } + } + } + + if (type == NL80211_IFTYPE_P2P_CLIENT || + type == NL80211_IFTYPE_P2P_GO) { + for (i = ar->max_norm_iface; i < ar->vif_max; i++) { + if ((ar->avail_idx_map >> i) & BIT(0)) { + *if_idx = i; + return true; + } + } + } + + } + return false; +} + +/* avoid using roam in p2p, ap, adhoc mode, and current USB devices */ +/* use in connect_cmd, disconnect event, ap_start */ +/* only support lrssi roam at this time*/ +void ath6kl_judge_roam_parameter( + struct ath6kl_vif *vif, + bool call_on_disconnect) +{ + struct ath6kl *ar = vif->ar; + u8 connected_count = 0; + bool lrssi_scan_enable = true; + struct ath6kl_vif *vif_temp; + + if (ar->roam_mode == ATH6KL_MODULEROAM_DISABLE || + ar->roam_mode == ATH6KL_MODULEROAM_DISABLE_LRSSI_SCAN) { + lrssi_scan_enable = false; + goto DONE; + } else if (ar->roam_mode == ATH6KL_MODULEROAM_NO_LRSSI_SCAN_AT_MULTI) { + + lrssi_scan_enable = true; + list_for_each_entry(vif_temp, &ar->vif_list, list) { + if (vif->fw_vif_idx == vif_temp->fw_vif_idx) + continue; + if (test_bit(CONNECTED, &vif_temp->flags)) + connected_count++; + + if (call_on_disconnect) { + if ((connected_count == 1) && + (vif_temp->wdev.iftype == + NL80211_IFTYPE_STATION)) { + lrssi_scan_enable = true; + } else { + lrssi_scan_enable = false; + } + } else if (connected_count) { + lrssi_scan_enable = false; + goto DONE; + } + } + } else { /* ATH6KL_MODULEROAM_ENABLE_ALL */ + lrssi_scan_enable = true; + } + + /* disable roam when it is in any other mode */ + if (!call_on_disconnect && + vif->wdev.iftype != NL80211_IFTYPE_STATION) + lrssi_scan_enable = false; + +DONE: + if (lrssi_scan_enable) { + ath6kl_wmi_set_roam_ctrl_cmd_for_lowerrssi(ar->wmi, + ar->low_rssi_roam_params.lrssi_scan_period, + ar->low_rssi_roam_params.lrssi_scan_threshold, + ar->low_rssi_roam_params.lrssi_roam_threshold, + ar->low_rssi_roam_params.roam_rssi_floor); + } else { + ath6kl_wmi_set_roam_ctrl_cmd_for_lowerrssi(ar->wmi, + 0xFFFF, 0, 0, 100); + } + +} + +static void switch_tid_rx_timeout( + struct ath6kl_vif *vif, + bool mcc) +{ + u8 i, j = 0; + struct aggr_conn_info *aggr_conn; + struct rxtid *rxtid; + + for (i = 0; i < AP_MAX_NUM_STA; i++) { + aggr_conn = vif->sta_list[i].aggr_conn_cntxt; + for (j = 0; j < NUM_OF_TIDS; j++) { + rxtid = AGGR_GET_RXTID(aggr_conn, j); + spin_lock_bh(&rxtid->lock); + switch (up_to_ac[j]) { + case WMM_AC_BK: + case WMM_AC_BE: + case WMM_AC_VI: + if (mcc) + aggr_conn->tid_timeout_setting[j] = + MCC_AGGR_RX_TIMEOUT; + else + aggr_conn->tid_timeout_setting[j] = + AGGR_RX_TIMEOUT; + break; + case WMM_AC_VO: + if (mcc) + aggr_conn->tid_timeout_setting[j] = + MCC_AGGR_RX_TIMEOUT_VO; + else + aggr_conn->tid_timeout_setting[j] = + AGGR_RX_TIMEOUT_VO; + break; + } + spin_unlock_bh(&rxtid->lock); + } + } +} + +#ifdef USB_AUTO_SUSPEND +void ath6kl_check_autopm_onoff(struct ath6kl *ar) +{ + struct ath6kl_vif *vif_temp; + /* + * Switch Auto PM On/Off + * In folloing case, we will turn off AUTOPM + * 1. p2p0-P2P-xxx is connected + * 2. any nw_type is AP network + */ + int vif_cnt = 0; + int autopm_turn_on = 1; + list_for_each_entry(vif_temp, &ar->vif_list, list) { + vif_cnt++; + if (test_bit(CONNECTED, &vif_temp->flags)) { + if (vif_cnt > 1) { + autopm_turn_on = 0; + break; + } + if (vif_temp->nw_type == AP_NETWORK) { + autopm_turn_on = 0; + break; + } + } + } + if (autopm_turn_on) + ath6kl_hif_auto_pm_turnon(ar); + else + ath6kl_hif_auto_pm_turnoff(ar); +} +#endif + +/* This function is used by sta/p2pclient/go interface to switch paramters + * needed for MCC/connection specific parameters + */ +void ath6kl_switch_parameter_based_on_connection( + struct ath6kl_vif *vif, + bool call_on_disconnect) +{ + struct ath6kl *ar = vif->ar; + u8 connected_count = 0; + struct ath6kl_vif *vif_temp; + bool mcc = false; + u16 pre_vifch = 0; + + list_for_each_entry(vif_temp, &ar->vif_list, list) { + if (test_bit(CONNECTED, &vif_temp->flags)) { + connected_count++; + if (pre_vifch == 0) { + pre_vifch = vif_temp->bss_ch; + } else if (vif_temp->bss_ch != 0 && + pre_vifch != vif_temp->bss_ch) { + mcc = true; + } + } + } + + if (connected_count) { + list_for_each_entry(vif_temp, &ar->vif_list, list) { + if (test_bit(CONNECTED, &vif_temp->flags)) { + if (call_on_disconnect && + vif->fw_vif_idx == vif_temp->fw_vif_idx) + continue; + switch_tid_rx_timeout(vif_temp, mcc); + } + } + } + + /* + * Switch scan parameter + * Currently, when there are more than one vif connected, + * revise the passive dwell time as the default + * ATH6KL_SCAN_PAS_DEWELL_TIME + */ + + if (connected_count) { + list_for_each_entry(vif_temp, &ar->vif_list, list) { + vif_temp->sc_params.pas_chdwell_time = + ATH6KL_SCAN_PAS_DEWELL_TIME; + } + } else { + list_for_each_entry(vif_temp, &ar->vif_list, list) { + memcpy(&vif->sc_params, + &vif->sc_params_default, + sizeof(struct wmi_scan_params_cmd)); + } + } + + if (mcc) + set_bit(MCC_ENABLED, &ar->flag); + else + clear_bit(MCC_ENABLED, &ar->flag); + + if (ar->conf_flags & ATH6KL_CONF_ENABLE_FLOWCTRL) { + if (ar->conf_flags & ATH6KL_CONF_DISABLE_SKIP_FLOWCTRL) { + clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag); + } else { + if (test_bit(MCC_ENABLED, &ar->flag)) + clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag); + else + set_bit(SKIP_FLOWCTRL_EVENT, &ar->flag); + } + } else { + clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag); + } + + if (mcc) { + list_for_each_entry(vif_temp, &ar->vif_list, list) { + if (test_bit(CONNECTED, &vif_temp->flags)) { + if (call_on_disconnect && + vif->fw_vif_idx == vif_temp->fw_vif_idx) + continue; + if (vif_temp->nw_type == AP_NETWORK) { + ath6kl_ap_keepalive_config(vif_temp, + ATH6KL_AP_KA_INTERVAL_DEFAULT, + ATH6KL_AP_KA_RECLAIM_CYCLE_MCC); + } + } + } + } else { + list_for_each_entry(vif_temp, &ar->vif_list, list) { + if (test_bit(CONNECTED, &vif_temp->flags)) { + if (call_on_disconnect && + vif->fw_vif_idx == vif_temp->fw_vif_idx) + continue; + if (vif_temp->nw_type == AP_NETWORK) { + ath6kl_ap_keepalive_config(vif_temp, + ATH6KL_AP_KA_INTERVAL_DEFAULT, + ATH6KL_AP_KA_RECLAIM_CYCLE_SCC); + } + } + } + } + +#ifdef USB_AUTO_SUSPEND + ath6kl_check_autopm_onoff(ar); +#endif /* USB_AUTO_SUSPEND */ +} + +static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + int status, left; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { + ath6kl_err("destroy in progress\n"); + return -EBUSY; + } + + if (test_bit(SKIP_SCAN, &ar->flag) && + ((sme->channel && sme->channel->center_freq == 0) || + (sme->bssid && is_zero_ether_addr(sme->bssid)))) { + ath6kl_err("SkipScan: channel or bssid invalid\n"); + return -EINVAL; + } + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + vif->sme_state = SME_CONNECTING; + + if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) { + /* + * sleep until the command queue drains + */ + left = wait_event_interruptible_timeout(ar->event_wq, + ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0, + WMI_TIMEOUT); + if (left == 0) { + ath6kl_warn("clear wmi ctrl data timeout connect\n"); + up(&ar->sem); + return -ETIMEDOUT; + } else if (signal_pending(current)) { + ath6kl_err("cmd queue drain timeout\n"); + up(&ar->sem); + return -EINTR; + } + } + + /* Diable background scan */ + vif->sc_params.bg_period = 0xFFFF; + ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + vif->sc_params.fg_start_period, + vif->sc_params.fg_end_period, + vif->sc_params.bg_period, + vif->sc_params.minact_chdwell_time, + vif->sc_params.maxact_chdwell_time, + vif->sc_params.pas_chdwell_time, + vif->sc_params.short_scan_ratio, + vif->sc_params.scan_ctrl_flags, + vif->sc_params.max_dfsch_act_time, + vif->sc_params.maxact_scan_per_ssid); + + ath6kl_judge_roam_parameter(vif, false); + + if (vif->wdev.iftype == NL80211_IFTYPE_STATION) + ath6kl_wmi_set_green_tx_params(ar->wmi, &ar->green_tx_params); + + if (ath6kl_wmi_set_rate_ctrl_cmd(ar->wmi, + vif->fw_vif_idx, RATECTRL_MODE_PERONLY)) + ath6kl_err("set rate_ctrl failed\n"); + + if (sme->ie && (sme->ie_len > 0)) { + status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len); + if (status) { + up(&ar->sem); + return status; + } + } else + vif->connect_ctrl_flags &= ~CONNECT_WPS_FLAG; + +#ifdef PMF_SUPPORT + /* Update AKM suites. */ + if (ath6kl_set_akm_suites(wiphy, dev, sme)) { + up(&ar->sem); + return -EIO; + } + + /* Update RSN Capabilities. */ + if (ath6kl_set_rsn_cap(wiphy, dev, sme->ie, sme->ie_len)) { + up(&ar->sem); + return -EIO; + } +#endif + + vif->connect_ctrl_flags |= CONNECT_IGNORE_WPAx_GROUP_CIPHER; + + if (test_bit(CONNECTED, &vif->flags) && + vif->ssid_len == sme->ssid_len && + !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) { + vif->reconnect_flag = true; + status = ath6kl_wmi_reconnect_cmd(ar->wmi, vif->fw_vif_idx, + vif->req_bssid, + vif->ch_hint); + + up(&ar->sem); + if (status) { + ath6kl_err("wmi_reconnect_cmd failed\n"); + return -EIO; + } + return 0; + } else if (vif->ssid_len == sme->ssid_len && + !memcmp(vif->ssid, sme->ssid, vif->ssid_len)) { + ath6kl_disconnect(vif); + } + + memset(vif->ssid, 0, sizeof(vif->ssid)); + vif->ssid_len = sme->ssid_len; + memcpy(vif->ssid, sme->ssid, sme->ssid_len); + + if (sme->channel) + vif->ch_hint = sme->channel->center_freq; + + memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); + if (sme->bssid && !is_broadcast_ether_addr(sme->bssid)) + memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid)); + + ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions); + + status = ath6kl_set_auth_type(vif, sme->auth_type); + if (status) { + up(&ar->sem); + return status; + } + + if (sme->crypto.n_ciphers_pairwise) + ath6kl_set_cipher(vif, sme->crypto.ciphers_pairwise[0], true); + else + ath6kl_set_cipher(vif, 0, true); + + ath6kl_set_cipher(vif, sme->crypto.cipher_group, false); + + if (sme->crypto.n_akm_suites) + ath6kl_set_key_mgmt(vif, sme->crypto.akm_suites[0]); + + if ((sme->key_len) && + (vif->auth_mode == NONE_AUTH) && + (vif->prwise_crypto == WEP_CRYPT)) { + struct ath6kl_key *key = NULL; + + if (sme->key_idx < WMI_MIN_KEY_INDEX || + sme->key_idx > WMI_MAX_KEY_INDEX) { + ath6kl_err("key index %d out of bounds\n", + sme->key_idx); + up(&ar->sem); + return -ENOENT; + } + + key = &vif->keys[sme->key_idx]; + key->key_len = sme->key_len; + memcpy(key->key, sme->key, key->key_len); + key->cipher = vif->prwise_crypto; + vif->def_txkey_index = sme->key_idx; + + ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, sme->key_idx, + vif->prwise_crypto, + GROUP_USAGE | TX_USAGE, + key->key_len, + NULL, 0, + key->key, KEY_OP_INIT_VAL, NULL, + NO_SYNC_WMIFLAG); + } + + if (!vif->usr_bss_filter) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); + if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + ALL_BSS_FILTER, 0) != 0) { + ath6kl_err("couldn't set bss filtering\n"); + up(&ar->sem); + return -EIO; + } + } + + vif->nw_type = vif->next_mode; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: connect called with authmode %d dot11 auth %d" + " PW crypto %d PW crypto len %d GRP crypto %d" + " GRP crypto len %d channel hint %u\n", + __func__, + vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto, + vif->prwise_crypto_len, vif->grp_crypto, + vif->grp_crypto_len, vif->ch_hint); + + vif->reconnect_flag = 0; + + /* + * Set disconnection timeout to 0 to cause firmware report + * disconnection event immediately rather than waiting defualt timer + * timeout (10sec) or supplicant trigger connection timeout (10sec). + */ + ath6kl_wmi_disctimeout_cmd(ar->wmi, vif->fw_vif_idx, 0); + + status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type, + vif->dot11_auth_mode, vif->auth_mode, + vif->prwise_crypto, + vif->prwise_crypto_len, + vif->grp_crypto, vif->grp_crypto_len, + vif->ssid_len, vif->ssid, + vif->req_bssid, vif->ch_hint, + vif->connect_ctrl_flags); + + up(&ar->sem); + + if (status == -EINVAL) { + memset(vif->ssid, 0, sizeof(vif->ssid)); + vif->ssid_len = 0; + ath6kl_err("invalid request\n"); + return -ENOENT; + } else if (status) { + ath6kl_err("ath6kl_wmi_connect_cmd failed\n"); + return -EIO; + } + + if ((!(vif->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) && + ((vif->auth_mode == WPA_PSK_AUTH) + || (vif->auth_mode == WPA2_PSK_AUTH))) { + mod_timer(&vif->disconnect_timer, + jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL)); + } + + vif->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD; + set_bit(CONNECT_PEND, &vif->flags); + + return 0; +} + +static struct cfg80211_bss *ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, + enum network_type nw_type, + const u8 *bssid, + struct ieee80211_channel *chan, + const u8 *beacon_ie, size_t beacon_ie_len) +{ + struct ath6kl *ar = vif->ar; + struct cfg80211_bss *bss; + u16 cap_mask, cap_val; + u8 *ie; + + if (nw_type & ADHOC_NETWORK) { + cap_mask = WLAN_CAPABILITY_IBSS; + cap_val = WLAN_CAPABILITY_IBSS; + } else { + cap_mask = WLAN_CAPABILITY_ESS; + cap_val = WLAN_CAPABILITY_ESS; + } + + bss = cfg80211_get_bss(ar->wiphy, chan, bssid, + vif->ssid, vif->ssid_len, + cap_mask, cap_val); + if (bss == NULL) { + /* + * Since cfg80211 may not yet know about the BSS, + * generate a partial entry until the first BSS info + * event becomes available. + * + * Prepend SSID element since it is not included in the Beacon + * IEs from the target. + */ + ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL); + if (ie == NULL) + return NULL; + ie[0] = WLAN_EID_SSID; + ie[1] = vif->ssid_len; + memcpy(ie + 2, vif->ssid, vif->ssid_len); + memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len); + bss = cfg80211_inform_bss(ar->wiphy, chan, + bssid, 0, cap_val, 100, + ie, 2 + vif->ssid_len + beacon_ie_len, + 0, GFP_KERNEL); + if (bss) + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to " + "cfg80211\n", bssid); + kfree(ie); + } else + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n"); + + return bss; +} + +static bool ath6kl_roamed_indicate(struct ath6kl_vif *vif, + u8 *bssid, bool reset_hk_prot) +{ + struct ath6kl *ar = vif->ar; + if ((vif->sme_state == SME_CONNECTED) && + (ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && + ((!test_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags)) || + (reset_hk_prot == true))) { + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %pM roam, vif->flags 0x%lu," + "reset_hk_prot %x\n", + __func__, bssid, vif->flags, reset_hk_prot); + return true; + } + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: %pM notroam\n", __func__, bssid); + return false; +} + +static bool ath6kl_handshake_protect(struct ath6kl_vif *vif, u8 *bssid) +{ + bool reset_handshake_pro = false; + + if ((vif->auth_mode > NONE_AUTH) && + (vif->prwise_crypto > WEP_CRYPT) && + (vif->nw_type == INFRA_NETWORK)) { + + if ((memcmp(bssid, vif->bssid, ETH_ALEN) != 0) || + (!test_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags))) { + spin_lock_bh(&vif->if_lock); + set_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags); + if (vif->pend_skb) + flush_delayed_work(&vif->work_eapol_send); + set_bit(FIRST_EAPOL_PENDSENT, &vif->flags); + mod_timer(&vif->shprotect_timer, + jiffies + ATH6KL_HANDSHAKE_PROC_TIMEOUT); + spin_unlock_bh(&vif->if_lock); + reset_handshake_pro = true; + } else { + reset_handshake_pro = false; + } + + } else { + spin_lock_bh(&vif->if_lock); + clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags); + if (vif->pend_skb) + flush_delayed_work(&vif->work_eapol_send); + del_timer(&vif->shprotect_timer); + spin_unlock_bh(&vif->if_lock); + reset_handshake_pro = false; + } + + return reset_handshake_pro; +} + +void ath6kl_cfg80211_connect_result(struct ath6kl_vif *vif, + const u8 *bssid, + const u8 *req_ie, + size_t req_ie_len, + const u8 *resp_ie, + size_t resp_ie_len, + u16 status, + gfp_t gfp) +{ + bool need_pending; + + need_pending = ath6kl_p2p_pending_connect_event(vif, + bssid, + req_ie, + req_ie_len, + resp_ie, + resp_ie_len, + status, + gfp); + if (!need_pending) + cfg80211_connect_result(vif->ndev, + bssid, + req_ie, + req_ie_len, + resp_ie, + resp_ie_len, + status, + gfp); + + return; +} + +void ath6kl_cfg80211_disconnected(struct ath6kl_vif *vif, + u16 reason, + u8 *ie, + size_t ie_len, + gfp_t gfp) +{ + ath6kl_p2p_pending_disconnect_event(vif, reason, ie, ie_len, gfp); + cfg80211_disconnected(vif->ndev, + reason, + ie, + ie_len, + gfp); + + return; +} + +void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, + u8 *bssid, u16 listen_intvl, + u16 beacon_intvl, + enum network_type nw_type, + u8 beacon_ie_len, u8 assoc_req_len, + u8 assoc_resp_len, u8 *assoc_info) +{ + struct ieee80211_channel *chan; + struct ath6kl *ar = vif->ar; + struct cfg80211_bss *bss; + bool reset_hk_prot = false; + + /* capinfo + listen interval */ + u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16); + + /* capinfo + status code + associd */ + u8 assoc_resp_ie_offset = sizeof(u16) + sizeof(u16) + sizeof(u16); + + u8 *assoc_req_ie = assoc_info + beacon_ie_len + assoc_req_ie_offset; + u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len + + assoc_resp_ie_offset; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: bssid %pM\n", __func__, bssid); + + assoc_req_len -= assoc_req_ie_offset; + assoc_resp_len -= assoc_resp_ie_offset; + + /* + * Store Beacon interval here; DTIM period will be available only once + * a Beacon frame from the AP is seen. + */ + vif->assoc_bss_beacon_int = beacon_intvl; + clear_bit(DTIM_PERIOD_AVAIL, &vif->flags); + + if (nw_type & ADHOC_NETWORK) { + if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: ath6k not in ibss mode\n", __func__); + return; + } +#ifdef ATH6KL_SUPPORT_WIFI_KTK + if (ar->ktk_active) + ath6kl_install_ktk_ptk(vif); +#endif + } + + if (nw_type & INFRA_NETWORK) { + if (vif->wdev.iftype != NL80211_IFTYPE_STATION && + vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: ath6k not in station mode\n", __func__); + return; + } + } + + chan = ieee80211_get_channel(ar->wiphy, (int) channel); + + if (chan == NULL) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: ath6k could not get channel information\n", + __func__); + return; + } + + bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, + assoc_info, beacon_ie_len); + if (!bss) { + ath6kl_err("could not add cfg80211 bss entry\n"); + return; + } + + if (nw_type & ADHOC_NETWORK) { +#ifdef ATH6KL_DIAGNOSTIC + wifi_diag_mac_fsm_event(vif, + (enum wifi_diag_mac_fsm_t)WIFI_DIAG_MAC_FSM_CONNECTED, + vif->diag.connect_seq_num); +#endif + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n", + nw_type & ADHOC_CREATOR ? "creator" : "joiner"); + cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); + cfg80211_put_bss(bss); + return; + } + + if (nw_type & INFRA_NETWORK) + vif->phymode = ((u16)nw_type >> 8) & 0xff; + + reset_hk_prot = ath6kl_handshake_protect(vif, bssid); + + if (vif->sme_state == SME_CONNECTING) { + /* inform connect result to cfg80211 */ + vif->sme_state = SME_CONNECTED; +#ifdef ATH6KL_DIAGNOSTIC + wifi_diag_mac_fsm_event(vif, + (enum wifi_diag_mac_fsm_t)WIFI_DIAG_MAC_FSM_CONNECTED, + vif->diag.connect_seq_num); +#endif + cfg80211_put_bss(bss); + ath6kl_cfg80211_connect_result(vif, bssid, + assoc_req_ie, assoc_req_len, + assoc_resp_ie, assoc_resp_len, + WLAN_STATUS_SUCCESS, GFP_KERNEL); + } else if (ath6kl_roamed_indicate(vif, bssid, reset_hk_prot) == true) { + cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len, + assoc_resp_ie, assoc_resp_len, GFP_KERNEL); + } +} + +static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *dev, u16 reason_code) +{ + struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + int ret; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__, + reason_code); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { + ath6kl_err("busy, destroy in progress\n"); + return -EBUSY; + } + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + vif->reconnect_flag = 0; + ret = ath6kl_disconnect(vif); + memset(vif->ssid, 0, sizeof(vif->ssid)); + vif->ssid_len = 0; + + if (!test_bit(SKIP_SCAN, &ar->flag)) + memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); + + up(&ar->sem); + + vif->sme_state = SME_DISCONNECTED; + + /* + * To avoid race condition between driver and supplicant, waiting + * until received disconnect event. + */ + if ((!ret) && + (vif->nw_type == INFRA_NETWORK) && + (test_bit(CONNECTED, &vif->flags))) { + if (test_bit(DISCONNECT_PEND, &vif->flags)) { + /* + * Already be called by other commands + * (ex, interface down), so ignore. + */ + return 0; + } + set_bit(DISCONNECT_PEND, &vif->flags); + wait_event_interruptible_timeout(ar->event_wq, + !test_bit(DISCONNECT_PEND, + &vif->flags), + (HZ/2)); + + if (signal_pending(current)) { + ath6kl_err("wait DISCONNECT timeout!\n"); + return -EINTR; + } + } + + return 0; +} + +void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, + u8 *bssid, u8 assoc_resp_len, + u8 *assoc_info, u16 proto_reason) +{ + struct ath6kl *ar = vif->ar; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u, proto_reason %u\n", + __func__, reason, proto_reason); + + if (vif->scan_req) { + del_timer(&vif->vifscan_timer); + ath6kl_wmi_abort_scan_cmd(ar->wmi, vif->fw_vif_idx); + cfg80211_scan_done(vif->scan_req, true); + vif->scan_req = NULL; + clear_bit(SCANNING, &vif->flags); +#if defined(USB_AUTO_SUSPEND) + /* + In Disconnected state, the driver will enter deep sleep, + such that the scan response will be lost, thus here + prevent driver goes into deep sleep mode + */ + + if (test_and_clear_bit(SCANNING, &ar->usb_autopm_scan)) { + if (ath6kl_hif_auto_pm_get_usage_cnt(ar) == 0) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: warnning pm_usage_cnt =0\n", __func__); + } else { + ath6kl_hif_auto_pm_enable(ar); + } + } + +#endif + } + + if (vif->nw_type & ADHOC_NETWORK) { + if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: ath6k not in ibss mode\n", __func__); + } + return; + } + + if (vif->nw_type & INFRA_NETWORK) { + if (vif->wdev.iftype != NL80211_IFTYPE_STATION && + vif->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: ath6k not in station mode\n", __func__); + return; + } + } + + if (vif->pend_skb) + flush_delayed_work(&vif->work_eapol_send); + + /* + * Send a disconnect command to target when a disconnect event is + * received with reason code other than 3 (DISCONNECT_CMD - disconnect + * request from host) to make the firmware stop trying to connect even + * after giving disconnect event. There will be one more disconnect + * event for this disconnect command with reason code DISCONNECT_CMD + * which will be notified to cfg80211. + */ + + if (reason != DISCONNECT_CMD) { + ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); + return; + } + + clear_bit(CONNECT_PEND, &vif->flags); + + if (vif->sme_state == SME_CONNECTING) { + ath6kl_cfg80211_connect_result(vif, + bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } else if (vif->sme_state == SME_CONNECTED) { + ath6kl_cfg80211_disconnected(vif, proto_reason, + NULL, 0, GFP_KERNEL); + } +#ifdef ATH6KL_DIAGNOSTIC + wifi_diag_mac_fsm_event(vif, + (enum wifi_diag_mac_fsm_t)WIFI_DIAG_MAC_FSM_DISCONNECTED, + vif->diag.disconnect_seq_num); +#endif + + vif->sme_state = SME_DISCONNECTED; + + if (reason == DISCONNECT_CMD) { + if (test_bit(DISCONNECT_PEND, &vif->flags) && + (vif->nw_type == INFRA_NETWORK)) { + clear_bit(DISCONNECT_PEND, &vif->flags); + wake_up(&ar->event_wq); + } + } +} + +static int ath6kl_cfg80211_change_bss(struct wiphy *wiphy, + struct net_device *ndev, struct bss_parameters *params) +{ + struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); + struct ath6kl_vif *vif = netdev_priv(ndev); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (params->ap_isolate >= 0) + vif->intra_bss = !params->ap_isolate; + + /* FIXME : support others. */ + + up(&ar->sem); + + return 0; +} + +void ath6kl_scan_timer_handler(unsigned long ptr) +{ + struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr; + struct ath6kl *ar = vif->ar; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s scan timer hit\n", __func__); + if (vif->scan_req) { + ath6kl_wmi_abort_scan_cmd(ar->wmi, vif->fw_vif_idx); + cfg80211_scan_done(vif->scan_req, true); + vif->scan_req = NULL; + clear_bit(SCANNING, &vif->flags); +#if defined(USB_AUTO_SUSPEND) + /* + In Disconnected state, the driver will enter deep sleep, + such that the scan response will be lost, thus here + prevent driver goes into deep sleep mode + */ + + if (test_and_clear_bit(SCANNING, &ar->usb_autopm_scan)) { + if (ath6kl_hif_auto_pm_get_usage_cnt(ar) == 0) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: warnning pm_usage_cnt =0\n", __func__); + } else { + ath6kl_hif_auto_pm_enable(ar); + } + } + +#endif + } +} + +/* assume we support not more than two differnet channels */ +static int ath6kl_scan_timeout_cal(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + u16 connected_count = 0; + + if (!(ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) + return ATH6KL_SCAN_TIMEOUT_WITHOUT_ROAM; + + if (ath6kl_scan_timeout && + (ath6kl_scan_timeout < ATH6KL_SCAN_TIMEOUT_SHORT)) + return ATH6KL_SCAN_TIMEOUT_SHORT; + + list_for_each_entry(vif, &ar->vif_list, list) { + if (test_bit(CONNECTED, &vif->flags)) { + connected_count++; + if (connected_count > 1) + return ATH6KL_SCAN_TIMEOUT_LONG; + } + } + + return ATH6KL_SCAN_TIMEOUT_SHORT; +} + +static int ath6kl_set_probe_req_ies(struct ath6kl_vif *vif, const u8 *ies, + size_t ies_len) +{ + struct ath6kl *ar = vif->ar; + const u8 *pos; + u8 *buf = NULL; + size_t len = 0; + int ret; + + /* + * Filter out P2P/WFD IE(s) + */ + if (ies && ies_len) { + buf = kmalloc(ies_len, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + pos = ies; + + while (pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + if ((ath6kl_is_p2p_ie(pos) || + ath6kl_is_wfd_ie(pos)) && + !ath6kl_p2p_ie_append(vif, + P2P_IE_IN_PROBE_REQ)) + ath6kl_info("Remove Probe's P2P IE\n"); + else { + memcpy(buf + len, pos, 2 + pos[1]); + len += 2 + pos[1]; + } + pos += 2 + pos[1]; + } + } + + ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_PROBE_REQ, buf, len); + kfree(buf); + + return ret; +} + +static int _ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request) +{ + struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); + struct ath6kl_vif *vif = netdev_priv(ndev); + s8 n_channels = 0; + u16 *channels = NULL; + int ret = 0; + u32 force_fg_scan = 0; + u8 skip_chan_num = 0; + bool sche_scan_trig, left; + + if (test_bit(DISABLE_SCAN, &ar->flag)) { + ath6kl_err("scan is disabled temporarily\n"); + return -EIO; + } + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s\n", __func__); + + /* + * Last Cancel-RoC not yet finished. To update vif->last_cancel_roc_id + * first to avoid wrong cookie report to supplicant. + */ + if (test_bit(ROC_CANCEL_PEND, &vif->flags)) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : Scan but Cancel-RoC not yet back, wait it finish %x\n", + vif->last_cancel_roc_id); + + wait_event_interruptible_timeout(ar->event_wq, + !test_bit(ROC_ONGOING, &vif->flags), + WMI_TIMEOUT); + if (signal_pending(current)) { + ath6kl_err("RoC : target did not respond\n"); + up(&ar->sem); + return -EINTR; + } + } + + /* RoC is ongoing and stop it first. */ + if (test_bit(ROC_ONGOING, &vif->flags)) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : Scan but On-going-RoC, cancel it first %x\n", + vif->last_roc_id); + + set_bit(ROC_CANCEL_PEND, &vif->flags); + if (ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, + vif->fw_vif_idx) != 0) { + ath6kl_err("RoC : cancel ROC failed\n"); + clear_bit(ROC_CANCEL_PEND, &vif->flags); + up(&ar->sem); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(ROC_ONGOING, &vif->flags), + WMI_TIMEOUT); + + if (left == 0) { + ath6kl_err("RoC : wait cancel RoC timeout\n"); + clear_bit(ROC_CANCEL_PEND, &vif->flags); + clear_bit(ROC_ONGOING, &vif->flags); + up(&ar->sem); + return -EINTR; + } + + if (signal_pending(current)) { + ath6kl_err("RoC : target did not respond\n"); + up(&ar->sem); + return -EINTR; + } + } + + sche_scan_trig = ath6kl_sched_scan_trigger(vif); + + if (!vif->usr_bss_filter) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); + + /* + * EV: 109005 + * Fix bug that Probe response from the connected AP + * will be filtered by setting this filter , + * thus the AP's information won't be updated. + */ + ret = ath6kl_wmi_bssfilter_cmd( + ar->wmi, vif->fw_vif_idx, ALL_BSS_FILTER , 0); + + + if (ret) { + ath6kl_err("couldn't set bss filtering\n"); + up(&ar->sem); + return ret; + } + } + + if ((request->n_ssids && request->ssids[0].ssid_len) && + (!sche_scan_trig)) { + u8 i; + + if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1)) + request->n_ssids = MAX_PROBED_SSID_INDEX - 1; + + for (i = 0; i < request->n_ssids; i++) + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, + i + 1, SPECIFIC_SSID_FLAG, + request->ssids[i].ssid_len, + request->ssids[i].ssid); + } else if (ar->p2p) + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, + MAX_PROBED_SSID_INDEX, ANY_SSID_FLAG, + 0, + NULL); + + if ((request->ie) && + (!sche_scan_trig)) { + ret = ath6kl_set_probe_req_ies(vif, + request->ie, + request->ie_len); + if (ret) { + ath6kl_err("failed to set Probe Request appie for " + "scan"); + up(&ar->sem); + return ret; + } + } + + /* + * Scan only the requested channels if the request specifies a set of + * channels. If the list is longer than the target supports, do not + * configure the list and instead, scan all available channels. + */ + if ((request->n_channels > 0 && + request->n_channels <= WMI_MAX_CHANNELS) && + (!sche_scan_trig)) { + u8 i; + + n_channels = request->n_channels; + + channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL); + if (channels == NULL) { + ath6kl_warn("failed to set scan channels, " + "scan all channels"); + n_channels = 0; + } + + if (n_channels) { + switch (vif->scanband_type) { + case SCANBAND_TYPE_CHAN_ONLY: + channels[0] = vif->scanband_chan; + ath6kl_dbg(ATH6KL_DBG_INFO, + "Only signal channel scan, channel %d\n", + channels[0]); + n_channels = 1; + break; + case SCANBAND_TYPE_5G: + ath6kl_dbg(ATH6KL_DBG_INFO, + "Only 5G channels scan, channel list - "); + for (i = 0; i < n_channels; i++) { + if (request->channels[i]->center_freq <= + 2484) { + skip_chan_num++; + continue; + } + channels[i - skip_chan_num] = + request->channels[i]->center_freq; + ath6kl_info("%d ", + channels[i - skip_chan_num]); + } + n_channels -= skip_chan_num; + break; + case SCANBAND_TYPE_2G: + ath6kl_dbg(ATH6KL_DBG_INFO, + "Only 2G channels scan, channel list - "); + for (i = 0; i < n_channels; i++) { + if (request->channels[i]->center_freq > + 2484) { + skip_chan_num++; + continue; + } + channels[i - skip_chan_num] = + request->channels[i]->center_freq; + ath6kl_dbg(ATH6KL_DBG_INFO, + "%d ", channels[i - skip_chan_num]); + } + n_channels -= skip_chan_num; + break; + case SCANBAND_TYPE_P2PCHAN: + ath6kl_dbg(ATH6KL_DBG_INFO, + "Only P2P channels scan, channel list - "); + for (i = 0; i < n_channels; i++) { + if (!ath6kl_reg_is_p2p_channel(ar, + request->channels[i]->center_freq)) { + skip_chan_num++; + continue; + } + channels[i - skip_chan_num] = + request->channels[i]->center_freq; + ath6kl_dbg(ATH6KL_DBG_INFO, + "%d ", channels[i - skip_chan_num]); + } + n_channels -= skip_chan_num; + break; + default: + for (i = 0; i < n_channels; i++) + channels[i] = + request->channels[i]->center_freq; + break; + } + } + } + + if (test_bit(CONNECTED, &vif->flags)) + force_fg_scan = 1; + + if (test_and_set_bit(SCANNING, &vif->flags)) { + kfree(channels); + up(&ar->sem); + return -EBUSY; + } + + if (test_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags)) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s connect_handshake_protect reject scan\n", __func__); + clear_bit(SCANNING, &vif->flags); + kfree(channels); + up(&ar->sem); + return -EBUSY; + } + + ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + vif->sc_params.fg_start_period, + vif->sc_params.fg_end_period, + vif->sc_params.bg_period, + vif->sc_params.minact_chdwell_time, + vif->sc_params.maxact_chdwell_time, + vif->sc_params.pas_chdwell_time, + vif->sc_params.short_scan_ratio, + vif->sc_params.scan_ctrl_flags, + vif->sc_params.max_dfsch_act_time, + vif->sc_params.maxact_scan_per_ssid); + + if (ret) { + ath6kl_err("ath6kl_cfg80211_scan: set scan parameter failed\n"); + clear_bit(SCANNING, &vif->flags); + kfree(channels); + up(&ar->sem); + return ret; + } + + ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN, + force_fg_scan, false, 0, + ATH6KL_FG_SCAN_INTERVAL, + n_channels, channels); + if (ret) { + ath6kl_err("wmi_startscan_cmd failed\n"); + clear_bit(SCANNING, &vif->flags); + } else { + vif->scan_req = request; + mod_timer(&vif->vifscan_timer, + jiffies + ath6kl_scan_timeout_cal(ar)); +#ifdef USB_AUTO_SUSPEND + set_bit(SCANNING, &ar->usb_autopm_scan); + ath6kl_hif_auto_pm_disable(ar); +#endif + } + + kfree(channels); + + ath6kl_p2p_rc_scan_start(vif); + + up(&ar->sem); + + + + return ret; +} + +#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV +static int ath6kl_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + BUG_ON(!request->wdev); + BUG_ON(!request->wdev->netdev); + + return _ath6kl_cfg80211_scan(wiphy, + request->wdev->netdev, + request); +} +#else +static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request) +{ + return _ath6kl_cfg80211_scan(wiphy, ndev, request); +} +#endif + +void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted) +{ + struct ath6kl *ar = vif->ar; + int i; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__, + aborted ? " aborted" : ""); +#if defined(USB_AUTO_SUSPEND) + /* + In Disconnected state, the driver will enter deep sleep, + such that the scan response will be lost, thus here + prevent driver goes into deep sleep mode + */ + + if (test_and_clear_bit(SCANNING, &ar->usb_autopm_scan)) { + if (ath6kl_hif_auto_pm_get_usage_cnt(ar) == 0) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: warnning pm_usage_cnt =0\n", __func__); + } else { + ath6kl_hif_auto_pm_enable(ar); + } + } + +#endif + del_timer(&vif->vifscan_timer); + + if (!vif->scan_req) + return; + + if (aborted) + goto out; + + if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) { + for (i = 0; i < vif->scan_req->n_ssids; i++) { + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, + i + 1, DISABLE_SSID_FLAG, + 0, NULL); + } + } else if (ar->p2p) + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, + MAX_PROBED_SSID_INDEX, + DISABLE_SSID_FLAG, + 0, NULL); + +out: + cfg80211_scan_done(vif->scan_req, aborted); + vif->scan_req = NULL; + clear_bit(SCANNING, &vif->flags); +} + +#ifdef PMF_SUPPORT +static int ath6kl_cfg80211_add_mgmt_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); + struct ath6kl_vif *vif = netdev_priv(ndev); + struct ath6kl_key *key = NULL; + enum wmi_sync_flag sync_flag = NO_SYNC_WMIFLAG; + int ret; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (!params) + return -EINVAL; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (key_index < WMI_MIN_IGTK_INDEX || key_index > WMI_MAX_IGTK_INDEX) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: key index %d out of bounds\n", __func__, + key_index); + up(&ar->sem); + return -ENOENT; + } + + key = &vif->keys[key_index]; + memset(key, 0, sizeof(struct ath6kl_key)); + + if (params) { + int seq_len = params->seq_len; + if (params->key_len > WMI_IGTK_KEY_LEN || + seq_len > sizeof(key->seq)) { + up(&ar->sem); + return -EINVAL; + } + + key->key_len = params->key_len; + memcpy(key->key, params->key, key->key_len); + key->seq_len = seq_len; + memcpy(key->seq, params->seq, key->seq_len); + key->cipher = params->cipher; + } + + if (vif->nw_type == AP_NETWORK && !pairwise && params) { + //Do something here for AP/GO mode. + } + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WMI_SYC)) + sync_flag = NO_SYNC_WMIFLAG; + + ret = ath6kl_wmi_addkey_igtk_cmd(ar->wmi, vif->fw_vif_idx, key_index, + key->key_len, key->seq, key->key, sync_flag); + + up(&ar->sem); + return ret; +} +#endif + +static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); + struct ath6kl_vif *vif = netdev_priv(ndev); + struct ath6kl_key *key = NULL; + enum wmi_sync_flag sync_flag = SYNC_BOTH_WMIFLAG; + u8 key_usage; + u8 key_type; + int ret; + +#ifdef PMF_SUPPORT + if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) + return ath6kl_cfg80211_add_mgmt_key(wiphy, ndev, key_index, + pairwise, mac_addr, params); +#endif + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (!params) + return -EINVAL; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (params->cipher == CCKM_KRK_CIPHER_SUITE) { + if (params->key_len != WMI_KRK_LEN) { + up(&ar->sem); + return -EINVAL; + } + + ret = ath6kl_wmi_add_krk_cmd(ar->wmi, vif->fw_vif_idx, + params->key); + up(&ar->sem); + return ret; + } + + if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: key index %d out of bounds\n", __func__, + key_index); + up(&ar->sem); + return -ENOENT; + } + + key = &vif->keys[key_index]; + memset(key, 0, sizeof(struct ath6kl_key)); + + if (pairwise) + key_usage = PAIRWISE_USAGE; + else + key_usage = GROUP_USAGE; + + if (params) { + int seq_len = params->seq_len; + if (params->cipher == WLAN_CIPHER_SUITE_SMS4 && + seq_len > ATH6KL_KEY_SEQ_LEN) { + /* Only first half of the WPI PN is configured */ + seq_len = ATH6KL_KEY_SEQ_LEN; + } + if (params->key_len > WLAN_MAX_KEY_LEN || + seq_len > sizeof(key->seq)) { + up(&ar->sem); + return -EINVAL; + } + + key->key_len = params->key_len; + memcpy(key->key, params->key, key->key_len); + key->seq_len = seq_len; + memcpy(key->seq, params->seq, key->seq_len); + key->cipher = params->cipher; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + key_type = WEP_CRYPT; + break; + + case WLAN_CIPHER_SUITE_TKIP: + key_type = TKIP_CRYPT; + break; + + case WLAN_CIPHER_SUITE_CCMP: + key_type = AES_CRYPT; + break; + case WLAN_CIPHER_SUITE_SMS4: + key_type = WAPI_CRYPT; + break; + + default: + up(&ar->sem); + return -ENOTSUPP; + } + + if (((vif->auth_mode == WPA_PSK_AUTH) + || (vif->auth_mode == WPA2_PSK_AUTH)) + && (key_usage & GROUP_USAGE)) + del_timer(&vif->disconnect_timer); + + if (key_usage & GROUP_USAGE) { + if (vif->pend_skb) { + ath6kl_err("eapol protect shall be off already\n"); + flush_delayed_work(&vif->work_eapol_send); + } + + spin_lock_bh(&vif->if_lock); + clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags); + del_timer(&vif->shprotect_timer); + spin_unlock_bh(&vif->if_lock); + } + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n", + __func__, key_index, key->key_len, key_type, + key_usage, key->seq_len); + + if (vif->nw_type == AP_NETWORK && !pairwise && + (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) { + vif->ap_mode_bkey.valid = true; + vif->ap_mode_bkey.key_index = key_index; + vif->ap_mode_bkey.key_type = key_type; + vif->ap_mode_bkey.key_len = key->key_len; + memcpy(vif->ap_mode_bkey.key, key->key, key->key_len); + if (!test_bit(CONNECTED, &vif->flags)) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group " + "key configuration until AP mode has been " + "started\n"); + /* + * The key will be set in ath6kl_connect_ap_mode() once + * the connected event is received from the target. + */ + up(&ar->sem); + return 0; + } + } + + if (vif->next_mode == AP_NETWORK && key_type == WEP_CRYPT && + !test_bit(CONNECTED, &vif->flags)) { + /* + * Store the key locally so that it can be re-configured after + * the AP mode has properly started + * (ath6kl_install_statioc_wep_keys). + */ + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration " + "until AP mode has been started\n"); + vif->wep_key_list[key_index].key_len = key->key_len; + memcpy(vif->wep_key_list[key_index].key, key->key, + key->key_len); + up(&ar->sem); + return 0; + } + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WMI_SYC)) + sync_flag = NO_SYNC_WMIFLAG; + + ret = ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index, + key_type, key_usage, key->key_len, + key->seq, key->seq_len, key->key, + KEY_OP_INIT_VAL, + (u8 *) mac_addr, sync_flag); + + up(&ar->sem); + + return ret; +} + +static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); + struct ath6kl_vif *vif = netdev_priv(ndev); + int ret; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: key index %d out of bounds\n", __func__, + key_index); + up(&ar->sem); + return -ENOENT; + } + + if (!vif->keys[key_index].key_len) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: index %d is empty\n", __func__, key_index); + up(&ar->sem); + return 0; + } + + vif->keys[key_index].key_len = 0; + + ret = ath6kl_wmi_deletekey_cmd(ar->wmi, vif->fw_vif_idx, key_index); + + up(&ar->sem); + + return ret; +} + +static int ath6kl_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 ath6kl_vif *vif = netdev_priv(ndev); + struct ath6kl_key *key = NULL; + struct key_params params; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&vif->ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: key index %d out of bounds\n", __func__, + key_index); + up(&vif->ar->sem); + return -ENOENT; + } + + key = &vif->keys[key_index]; + memset(¶ms, 0, sizeof(params)); + params.cipher = key->cipher; + params.key_len = key->key_len; + params.seq_len = key->seq_len; + params.seq = key->seq; + params.key = key->key; + + callback(cookie, ¶ms); + + up(&vif->ar->sem); + + return key->key_len ? 0 : -ENOENT; +} + +static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool unicast, + bool multicast) +{ + struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); + struct ath6kl_vif *vif = netdev_priv(ndev); + struct ath6kl_key *key = NULL; + u8 key_usage; + enum crypto_type key_type = NONE_CRYPT; + enum wmi_sync_flag sync_flag = SYNC_BOTH_WMIFLAG; + int ret; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: key index %d out of bounds\n", + __func__, key_index); + up(&ar->sem); + return -ENOENT; + } + + if (!vif->keys[key_index].key_len) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n", + __func__, key_index); + up(&ar->sem); + return -EINVAL; + } + + vif->def_txkey_index = key_index; + key = &vif->keys[vif->def_txkey_index]; + key_usage = GROUP_USAGE; + if (vif->prwise_crypto == WEP_CRYPT) + key_usage |= TX_USAGE; + if (unicast) + key_type = vif->prwise_crypto; + if (multicast) + key_type = vif->grp_crypto; + + if (vif->next_mode == + AP_NETWORK && !test_bit(CONNECTED, &vif->flags)) { + up(&ar->sem); + return 0; /* Delay until AP mode has been started */ + } + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WMI_SYC)) + sync_flag = NO_SYNC_WMIFLAG; + + ret = ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, + vif->def_txkey_index, + key_type, key_usage, + key->key_len, key->seq, key->seq_len, + key->key, + KEY_OP_INIT_VAL, NULL, + sync_flag); + + up(&ar->sem); + + return ret; +} + +void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, + bool ismcast) +{ + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: keyid %d, ismcast %d\n", __func__, keyid, ismcast); + + cfg80211_michael_mic_failure(vif->ndev, vif->bssid, + (ismcast ? NL80211_KEYTYPE_GROUP : + NL80211_KEYTYPE_PAIRWISE), keyid, NULL, + GFP_KERNEL); +} + +static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct ath6kl_vif *vif; + int ret; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__, + changed); + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + ret = ath6kl_wmi_set_rts_cmd(ar->wmi, 0, wiphy->rts_threshold); + if (ret != 0) { + ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n"); + up(&ar->sem); + return -EIO; + } + } + + up(&ar->sem); + + return 0; +} + +/* + * The type nl80211_tx_power_setting replaces the following + * data type from 2.6.36 onwards +*/ +static int _ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct ath6kl_vif *vif = netdev_priv(ndev); + u8 ath6kl_dbm; + int dbm = MBM_TO_DBM(mbm); + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__, + type, dbm); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + switch (type) { + case NL80211_TX_POWER_AUTOMATIC: + up(&ar->sem); + return 0; + case NL80211_TX_POWER_LIMITED: + ar->tx_pwr = ath6kl_dbm = dbm; + break; + default: + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n", + __func__, type); + up(&ar->sem); + return -EOPNOTSUPP; + } + + ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm); + + up(&ar->sem); + + return 0; +} + +static int _ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, + struct net_device *ndev, + int *dbm) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct ath6kl_vif *vif = netdev_priv(ndev); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (test_bit(CONNECTED, &vif->flags)) { + ar->tx_pwr = 0; + + if (ath6kl_wmi_get_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx) != 0) { + ath6kl_err("ath6kl_wmi_get_tx_pwr_cmd failed\n"); + up(&ar->sem); + return -EIO; + } + + wait_event_interruptible_timeout(ar->event_wq, ar->tx_pwr != 0, + 5 * HZ); + + if (signal_pending(current)) { + ath6kl_err("target did not respond\n"); + up(&ar->sem); + return -EINTR; + } + } + + *dbm = ar->tx_pwr; + + up(&ar->sem); + + return 0; +} + +#ifdef CFG80211_TX_POWER_PER_WDEV +static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct net_device *ndev; + + if (wdev == NULL) { + struct ath6kl_vif *vif = ath6kl_vif_first(ar); + + if (!vif) + return -EIO; + ndev = vif->ndev; + } else + ndev = wdev->netdev; + + return _ath6kl_cfg80211_set_txpower(wiphy, ndev, type, mbm); +} + +static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct net_device *ndev; + + if (wdev == NULL) { + struct ath6kl_vif *vif = ath6kl_vif_first(ar); + + if (!vif) + return -EIO; + ndev = vif->ndev; + } else + ndev = wdev->netdev; + + return _ath6kl_cfg80211_get_txpower(wiphy, ndev , dbm); +} +#else +static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct ath6kl_vif *vif = ath6kl_vif_first(ar); + + if (!vif) + return -EIO; + + return _ath6kl_cfg80211_set_txpower(wiphy, vif->ndev, type, mbm); +} + +static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct ath6kl_vif *vif = ath6kl_vif_first(ar); + + if (!vif) + return -EIO; + + return _ath6kl_cfg80211_get_txpower(wiphy, vif->ndev, dbm); +} +#endif + +static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool pmgmt, int timeout) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct wmi_power_mode_cmd mode; + struct ath6kl_vif *vif = netdev_priv(dev); + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n", + __func__, pmgmt, timeout); + + if (test_bit(PS_STICK, &vif->flags)) { + ath6kl_info("PS mode already stick (%d).\n", + vif->last_pwr_mode); + return 0; + } + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (pmgmt) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__); + mode.pwr_mode = REC_POWER; + } else { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__); + mode.pwr_mode = MAX_PERF_POWER; + } + + if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx, + mode.pwr_mode) != 0) { + ath6kl_err("wmi_powermode_cmd failed\n"); + up(&ar->sem); + return -EIO; + } + + up(&ar->sem); + + return 0; +} + +static struct net_device *_ath6kl_cfg80211_add_iface(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + struct net_device *ndev; + u8 if_idx, nw_type; + + if (!__ath6kl_cfg80211_ready(ar)) + return NULL; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return NULL; + } + + if (ar->num_vif == ar->vif_max) { + ath6kl_err("Reached maximum number of supported vif\n"); + up(&ar->sem); + return NULL; + } + + if (!ath6kl_is_valid_iftype(ar, type, &if_idx, &nw_type)) { + ath6kl_err("Not a supported interface type\n"); + up(&ar->sem); + return NULL; + } + + ndev = ath6kl_interface_add(ar, name, type, if_idx, nw_type); + if (!ndev) { + up(&ar->sem); + return NULL; + } + + ar->num_vif++; + + up(&ar->sem); + + return ndev; +} + +static int _ath6kl_cfg80211_del_iface(struct wiphy *wiphy, + struct net_device *ndev) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + struct ath6kl_vif *vif = netdev_priv(ndev); + + if (!__ath6kl_cfg80211_ready(ar)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + /* fix EV110820 */ + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + NONE_BSS_FILTER, 0); + + ath6kl_judge_roam_parameter(vif, true); + ath6kl_switch_parameter_based_on_connection(vif, true); + + spin_lock_bh(&ar->list_lock); + list_del(&vif->list); + spin_unlock_bh(&ar->list_lock); + + ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); + + ath6kl_deinit_if_data(vif); + + up(&ar->sem); + + return 0; +} + +#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV +static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, + const char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct net_device *dev; + + dev = _ath6kl_cfg80211_add_iface(wiphy, + (char *)name, + type, + flags, + params); + + if (dev) + return dev->ieee80211_ptr; + else + return ERR_PTR(-EINVAL); +} + +static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + BUG_ON(!wdev->netdev); + + return _ath6kl_cfg80211_del_iface(wiphy, wdev->netdev); +} +#else +static struct net_device *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct net_device *dev; + + dev = _ath6kl_cfg80211_add_iface(wiphy, + name, + type, + flags, + params); + + if (dev) + return dev; + else + return ERR_PTR(-EINVAL); +} + +static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, + struct net_device *ndev) +{ + return _ath6kl_cfg80211_del_iface(wiphy, ndev); +} +#endif + +static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct ath6kl_vif *vif = netdev_priv(ndev); + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); + + if (down_interruptible(&vif->ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + switch (type) { + case NL80211_IFTYPE_STATION: + vif->next_mode = INFRA_NETWORK; + break; + case NL80211_IFTYPE_ADHOC: + vif->next_mode = ADHOC_NETWORK; + break; + case NL80211_IFTYPE_AP: + vif->next_mode = AP_NETWORK; + break; + case NL80211_IFTYPE_P2P_CLIENT: + vif->next_mode = INFRA_NETWORK; + break; + case NL80211_IFTYPE_P2P_GO: + vif->next_mode = AP_NETWORK; + break; + default: + ath6kl_err("invalid interface type %u\n", type); + up(&vif->ar->sem); + return -EOPNOTSUPP; + } + + vif->wdev.iftype = type; + + up(&vif->ar->sem); + + return 0; +} + +static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ibss_params *ibss_param) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + struct ieee80211_channel *chan; + int status; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + vif->ssid_len = ibss_param->ssid_len; + memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len); + +#ifdef CFG80211_NEW_CHAN_DEFINITION + chan = ibss_param->chandef.chan; +#else + chan = ibss_param->channel; +#endif + + if (chan) + vif->ch_hint = chan->center_freq; + + if (ibss_param->channel_fixed) { + /* + * TODO: channel_fixed: The channel should be fixed, do not + * search for IBSSs to join on other channels. Target + * firmware does not support this feature, needs to be + * updated. + */ + up(&ar->sem); + return -EOPNOTSUPP; + } + /* Diable background scan */ + vif->sc_params.bg_period = 0xFFFF; +#ifdef ATH6KL_SUPPORT_WIFI_KTK + vif->sc_params.minact_chdwell_time = 0; + vif->sc_params.maxact_chdwell_time = 105; +#endif + ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + vif->sc_params.fg_start_period, + vif->sc_params.fg_end_period, + vif->sc_params.bg_period, + vif->sc_params.minact_chdwell_time, + vif->sc_params.maxact_chdwell_time, + vif->sc_params.pas_chdwell_time, + vif->sc_params.short_scan_ratio, + vif->sc_params.scan_ctrl_flags, + vif->sc_params.max_dfsch_act_time, + vif->sc_params.maxact_scan_per_ssid); + + memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); + if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid)) + memcpy(vif->req_bssid, ibss_param->bssid, + sizeof(vif->req_bssid)); + + ath6kl_set_wpa_version(vif, 0); + + status = ath6kl_set_auth_type(vif, NL80211_AUTHTYPE_OPEN_SYSTEM); + if (status) { + up(&ar->sem); + return status; + } + +#ifdef ATH6KL_SUPPORT_WIFI_KTK + if (!ar->ktk_active) { +#endif + if (ibss_param->privacy) { + ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, true); + ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_WEP40, false); + } else { + ath6kl_set_cipher(vif, 0, true); + ath6kl_set_cipher(vif, 0, false); + } +#ifdef ATH6KL_SUPPORT_WIFI_KTK + } else { + ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_CCMP, true); + ath6kl_set_cipher(vif, WLAN_CIPHER_SUITE_CCMP, false); + } +#endif + + vif->nw_type = vif->next_mode; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "%s: connect called with authmode %d dot11 auth %d" + " PW crypto %d PW crypto len %d GRP crypto %d" + " GRP crypto len %d channel hint %u\n", + __func__, + vif->auth_mode, vif->dot11_auth_mode, vif->prwise_crypto, + vif->prwise_crypto_len, vif->grp_crypto, + vif->grp_crypto_len, vif->ch_hint); + + status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type, + vif->dot11_auth_mode, vif->auth_mode, + vif->prwise_crypto, + vif->prwise_crypto_len, + vif->grp_crypto, vif->grp_crypto_len, + vif->ssid_len, vif->ssid, + vif->req_bssid, vif->ch_hint, + vif->connect_ctrl_flags); + set_bit(CONNECT_PEND, &vif->flags); + + up(&ar->sem); + + return 0; +} + +static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy, + struct net_device *dev) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&vif->ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + ath6kl_disconnect(vif); + memset(vif->ssid, 0, sizeof(vif->ssid)); + vif->ssid_len = 0; + + up(&vif->ar->sem); + + return 0; +} + +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + CCKM_KRK_CIPHER_SUITE, + WLAN_CIPHER_SUITE_SMS4, +#ifdef PMF_SUPPORT + WLAN_CIPHER_SUITE_AES_CMAC, +#endif +}; + +static bool is_rate_legacy(s32 rate) +{ + static const s32 legacy[] = { 1000, 2000, 5500, 11000, + 6000, 9000, 12000, 18000, 24000, + 36000, 48000, 54000 + }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(legacy); i++) + if (rate == legacy[i]) + return true; + + return false; +} + +static bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi) +{ + static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000, + 52000, 58500, 65000, 72200 + }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(ht20); i++) { + if (rate == ht20[i]) { + if (i == ARRAY_SIZE(ht20) - 1) + /* last rate uses sgi */ + *sgi = true; + else + *sgi = false; + + *mcs = i; + return true; + } + } + return false; +} + +static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi) +{ + static const s32 ht40[] = { 13500, 27000, 40500, 54000, + 81000, 108000, 121500, 135000, + 150000 + }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(ht40); i++) { + if (rate == ht40[i]) { + if (i == ARRAY_SIZE(ht40) - 1) + /* last rate uses sgi */ + *sgi = true; + else + *sgi = false; + + *mcs = i; + return true; + } + } + + return false; +} + +static int __get_rate_info(struct rate_info *txrate, s32 rate, s8 rate_idx) +{ + s8 rate_id = rate_idx & 0x7f; + + txrate->flags = 0; + if (rate_id <= 11) { + txrate->legacy = rate / 100; + } else if (rate_id <= 43) { + txrate->flags |= RATE_INFO_FLAGS_MCS; + if (rate_idx >> 7) + txrate->flags |= RATE_INFO_FLAGS_SHORT_GI; + + if (rate_id <= 27) + txrate->mcs = rate_id - 12; + else { + txrate->mcs = rate_id - 28; + txrate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + } + } else { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "invalid rate from ar6004 stats: rate %d idx %d\n", + rate, rate_idx); + + return -1; + } + + return 0; +} + +static void _get_sta_info(struct ath6kl_vif *vif, + struct station_info *sinfo) +{ + struct target_stats *stats = &vif->target_stats; + s32 rate; + s8 rateid; + u8 mcs; + bool sgi; + + /* Not only STA but also IBSS mode. */ + + if (stats->rx_byte) { + sinfo->rx_bytes = stats->rx_byte; + sinfo->filled |= STATION_INFO_RX_BYTES; + sinfo->rx_packets = stats->rx_pkt; + sinfo->filled |= STATION_INFO_RX_PACKETS; + } + + if (stats->tx_byte) { + sinfo->tx_bytes = stats->tx_byte; + sinfo->filled |= STATION_INFO_TX_BYTES; + sinfo->tx_packets = stats->tx_pkt; + sinfo->filled |= STATION_INFO_TX_PACKETS; + } + + sinfo->signal = stats->cs_rssi; + sinfo->filled |= STATION_INFO_SIGNAL; + + rate = stats->tx_ucast_rate; + + if (vif->ar->target_type == TARGET_TYPE_AR6004) { + rateid = stats->tx_rate_index; + if (__get_rate_info(&sinfo->txrate, rate, rateid)) + ath6kl_debug_war(vif->ar, ATH6KL_WAR_INVALID_RATE); + else + sinfo->filled |= STATION_INFO_TX_BITRATE; + } else { + sinfo->txrate.flags = 0; + if (is_rate_legacy(rate)) { + sinfo->txrate.legacy = rate / 100; + sinfo->filled |= STATION_INFO_TX_BITRATE; + } else if (is_rate_ht20(rate, &mcs, &sgi)) { + if (sgi) { + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->txrate.mcs = mcs - 1; + } else + sinfo->txrate.mcs = mcs; + + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + sinfo->filled |= STATION_INFO_TX_BITRATE; + } else if (is_rate_ht40(rate, &mcs, &sgi)) { + if (sgi) { + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->txrate.mcs = mcs - 1; + } else + sinfo->txrate.mcs = mcs; + + sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + sinfo->filled |= STATION_INFO_TX_BITRATE; + } else { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "invalid rate from stats: %d\n", rate); + ath6kl_debug_war(vif->ar, ATH6KL_WAR_INVALID_RATE); + } + } + + if ((vif->nw_type == INFRA_NETWORK) && + test_bit(CONNECTED, &vif->flags) && + test_bit(DTIM_PERIOD_AVAIL, &vif->flags) && + vif->nw_type == INFRA_NETWORK) { + sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->bss_param.flags = 0; + sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period; + sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int; + } + + return; +} + +static void _get_ap_sta_info(struct ath6kl_vif *vif, + struct wmi_per_sta_stat *sta, + u8 *mac_addr, + struct station_info *sinfo) +{ + s32 rate; + s8 rateid; + + if (ath6kl_ap_keepalive_by_supp(vif)) + sinfo->inactive_time = + ath6kl_ap_keepalive_get_inactive_time(vif, mac_addr); + else { + /* + * Always report 1 sec. to let supplicant bypass + * its keep-alive mechanims. + */ + sinfo->inactive_time = 1 * 1000; + } + sinfo->filled |= STATION_INFO_INACTIVE_TIME; + + sinfo->rx_bytes = sta->rx_bytes; + sinfo->filled |= STATION_INFO_RX_BYTES; + sinfo->rx_packets = sta->rx_pkts; + sinfo->filled |= STATION_INFO_RX_PACKETS; + + sinfo->tx_bytes = sta->tx_bytes; + sinfo->filled |= STATION_INFO_TX_BYTES; + sinfo->tx_packets = sta->tx_pkts; + sinfo->filled |= STATION_INFO_TX_PACKETS; + + if (vif->ar->target_type == TARGET_TYPE_AR6004) { + rate = ath6kl_wmi_get_rate_ar6004(sta->tx_ucast_rate); + rateid = sta->tx_ucast_rate; + if (__get_rate_info(&sinfo->txrate, rate, rateid)) + ath6kl_debug_war(vif->ar, ATH6KL_WAR_INVALID_RATE); + else + sinfo->filled |= STATION_INFO_TX_BITRATE; + } + + return; +} + +static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_info *sinfo) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl_sta *conn = NULL; + long left; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (vif->nw_type == AP_NETWORK) { + conn = ath6kl_find_sta(vif, mac); + if (conn == NULL) { + ath6kl_err("Can't find %02x%02x%02x%02x%02x%02x\n", + mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + + return -EINVAL; + } + } else { + if (memcmp(mac, vif->bssid, ETH_ALEN) != 0) + return -ENOENT; + } + + if (down_interruptible(&ar->sem)) + return -EBUSY; + + set_bit(STATS_UPDATE_PEND, &vif->flags); + + if (ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx)) { + up(&ar->sem); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(STATS_UPDATE_PEND, + &vif->flags), + WMI_TIMEOUT); + + clear_bit(STATS_UPDATE_PEND, &vif->flags); + up(&ar->sem); + + if (left == 0) + return -ETIMEDOUT; + else if (left < 0) + return left; + + if (vif->nw_type == AP_NETWORK) { + struct wmi_ap_mode_stat *ap = &vif->ap_stats; + struct wmi_per_sta_stat *sta = NULL; + + for (left = 0; left < AP_MAX_NUM_STA; left++) { + if (conn->aid == ap->sta[left].aid) { + sta = &ap->sta[left]; + break; + } + } + + if (!sta) + return -EINVAL; + + _get_ap_sta_info(vif, sta, mac, sinfo); + + return 0; + } + + _get_sta_info(vif, sinfo); + + return 0; +} + +static int ath6kl_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl_sta *conn; + struct wmi_ap_mode_stat *ap; + struct wmi_per_sta_stat *sta; + long left; + int next; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (vif->nw_type == AP_NETWORK) { + /* Get AP stats only at least one station associated. */ + if ((vif->sta_list_index == 0) || + (idx >= AP_MAX_NUM_STA)) + return -ENOENT; + + /* Only need to update it when 1st STA dump. */ + if (idx == 0) + vif->last_dump_ap_stats_idx = 0; + else + goto update_done; + } else { + if ((idx != 0) || + !test_bit(CONNECTED, &vif->flags)) + return -ENOENT; + } + + if (down_interruptible(&ar->sem)) + return -EBUSY; + + set_bit(STATS_UPDATE_PEND, &vif->flags); + + if (ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx)) { + up(&ar->sem); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(STATS_UPDATE_PEND, + &vif->flags), + WMI_TIMEOUT); + + clear_bit(STATS_UPDATE_PEND, &vif->flags); + up(&ar->sem); + + if (left == 0) + return -ETIMEDOUT; + else if (left < 0) + return left; + +update_done: + if (vif->nw_type == AP_NETWORK) { + if (vif->last_dump_ap_stats_idx >= AP_MAX_NUM_STA) + return -ENOENT; + + /* Find next STA */ + ap = &vif->ap_stats; + for (next = vif->last_dump_ap_stats_idx; + next < AP_MAX_NUM_STA; + next++) { + sta = &(ap->sta[next]); + if (sta->aid) { + conn = ath6kl_find_sta_by_aid(vif, sta->aid); + if (conn) + break; + } + } + + /* All STAs reported */ + if (next == AP_MAX_NUM_STA) + return -ENOENT; + + /* Set to the next one. */ + vif->last_dump_ap_stats_idx = next + 1; + + memcpy(mac, conn->mac, ETH_ALEN); + _get_ap_sta_info(vif, sta, conn->mac, sinfo); + + return 0; + } + + memcpy(mac, vif->ndev->dev_addr, ETH_ALEN); + _get_sta_info(vif, sinfo); + + return 0; +} + +static int ath6kl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + int ret, disabled; + + WARN_ON(peer); + if ((peer) || + ((mask->control[NL80211_BAND_2GHZ].legacy != 0xfff) && + (mask->control[NL80211_BAND_2GHZ].legacy != 0xff0))) { + /* FIXME : not support yet */ + return -ENOTSUPP; + } + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (mask->control[NL80211_BAND_2GHZ].legacy & + ((1 << ath6kl_b_rates_size) - 1)) + disabled = 0; + else + disabled = 1; + + ret = ath6kl_wmi_disable_11b_rates_cmd(ar->wmi, vif->fw_vif_idx, + disabled); + + up(&ar->sem); + + return ret; +} + +static int ath6kl_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa) +{ + struct ath6kl *ar = ath6kl_priv(netdev); + struct ath6kl_vif *vif = netdev_priv(netdev); + int ret; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + ret = ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid, + pmksa->pmkid, true); + + up(&ar->sem); + + return ret; +} + +static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa) +{ + struct ath6kl *ar = ath6kl_priv(netdev); + struct ath6kl_vif *vif = netdev_priv(netdev); + int ret; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + ret = ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, pmksa->bssid, + pmksa->pmkid, false); + + up(&ar->sem); + + return ret; +} + +static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) +{ + struct ath6kl *ar = ath6kl_priv(netdev); + struct ath6kl_vif *vif = netdev_priv(netdev); + int ret; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (test_bit(CONNECTED, &vif->flags)) { + ret = ath6kl_wmi_setpmkid_cmd(ar->wmi, vif->fw_vif_idx, + vif->bssid, NULL, false); + up(&ar->sem); + return ret; + } + + up(&ar->sem); + + return 0; +} + +static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + struct ath6kl_vif *vif; + int ret, left; +#ifndef CONFIG_ANDROID + int pos; + u32 filter = 0; + u16 i; + u8 mask[WOW_MASK_SIZE]; +#endif + + /*if already in wow state just return without error*/ + if (ar->state == ATH6KL_STATE_WOW) + return 0; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + +#if (!defined(CONFIG_ANDROID) && !defined(USB_AUTO_SUSPEND)) + + /* Clear existing WOW patterns */ + for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++) + ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, + WOW_LIST_ID, i); + /* Configure new WOW patterns */ + for (i = 0; i < wow->n_patterns; i++) { + + if (ath6kl_wow_ext) { + ret = ath6kl_wmi_add_wow_ext_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + wow->patterns[i].pattern_len, + i, + wow->patterns[i].pattern, + wow->patterns[i].mask); + /* filter for wow ext pattern */ + filter |= WOW_FILTER_OPTION_PATTERNS; + } else { + /* + * Convert given nl80211 specific mask value to + * equivalent driver specific mask value and send it + * to the chip along with patterns. For example, + * If the mask value defined in struct cfg80211_wowlan + * is 0xA (equivalent binary is 1010), then equivalent + * driver specific mask value is "0xFF 0x00 0xFF 0x00". + */ + memset(&mask, 0, sizeof(mask)); + for (pos = 0; pos < wow->patterns[i].pattern_len; + pos++) { + if (wow->patterns[i].mask[pos / 8] & + (0x1 << (pos % 8))) + mask[pos] = 0xFF; + } + /* + * Note: Pattern's offset is not passed as part of + * wowlan parameter from CFG layer. So it's always + * passed as ZERO to the firmware. It means, given + * WOW patterns are always matched from the first byte + * of received pkt in the firmware. + */ + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + wow->patterns[i].pattern_len, + 0 /* pattern offset */, + wow->patterns[i].pattern, mask); + } + if (ret) + return ret; + } + + if (wow->disconnect || wow->any) + filter |= WOW_FILTER_OPTION_NWK_DISASSOC; + + if (wow->magic_pkt || wow->any) + filter |= WOW_FILTER_OPTION_MAGIC_PACKET; + + if (wow->gtk_rekey_failure || wow->any) { + filter |= (WOW_FILTER_OPTION_EAP_REQ | + WOW_FILTER_OPTION_8021X_4WAYHS | + WOW_FILTER_OPTION_GTK_ERROR | + WOW_FILTER_OPTION_OFFLOAD_GTK); + } + + if (wow->eap_identity_req || wow->any) + filter |= WOW_FILTER_OPTION_EAP_REQ; + + if (wow->four_way_handshake || wow->any) + filter |= WOW_FILTER_OPTION_8021X_4WAYHS; + + /*Do GTK offload in WPA/WPA2 auth mode connection.*/ + if (vif->auth_mode == WPA2_AUTH_CCKM || vif->auth_mode == WPA2_PSK_AUTH + || vif->auth_mode == WPA_AUTH_CCKM || vif->auth_mode == WPA_PSK_AUTH){ + filter |= WOW_FILTER_OPTION_OFFLOAD_GTK; + } + + if (vif->arp_offload_ip_set || wow->any) + filter |= WOW_FILTER_OPTION_OFFLOAD_ARP; + + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_WOW_MODE_ENABLE, + filter, + WOW_HOST_REQ_DELAY); + if (ret) + return ret; +#endif /*!CONFIG_ANDROID*/ + + clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_ASLEEP); + if (ret) + return ret; + + left = wait_event_interruptible_timeout(ar->event_wq, + test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags), + WMI_TIMEOUT); + + if (left == 0) { + ath6kl_warn("timeout, didn't get host sleep cmd " + "processed event\n"); + ret = -ETIMEDOUT; + } else if (left < 0) { + ath6kl_warn("error while waiting for host sleep cmd " + "processed event %d\n", left); + ret = left; + } + + if (ar->tx_pending[ar->ctrl_ep]) { + left = wait_event_interruptible_timeout(ar->event_wq, + ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT); + if (left == 0) { + ath6kl_warn("clear wmi ctrl data timeout\n"); + ret = -ETIMEDOUT; + } else if (left < 0) { + ath6kl_warn("clear wmi ctrl data failed: %d\n", left); + ret = left; + } + } +#ifdef CONFIG_ANDROID + if (ret) + ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); +#endif + return ret; +} + +static int ath6kl_wow_resume(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + int ret; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); + return ret; +} + +#ifdef CONFIG_ANDROID +static irqreturn_t ath6kl_wow_irq(int irq, void *dev_id) +{ + struct rttm_context *prttm = NULL; + + prttm = DEV_GETRTT_HDL(); + + if ((prttm) && (prttm->rttdhclkcal_active)) { + struct timespec ts; + + getnstimeofday(&ts); + prttm->rttd2h2_clk.tabs_h2[prttm->dhclkcal_index].sec = + ts.tv_sec; + prttm->rttd2h2_clk.tabs_h2[prttm->dhclkcal_index].nsec = + ts.tv_nsec; + prttm->dhclkcal_index++; + } + + return IRQ_HANDLED; +} +#endif + +static bool ath6kl_cfg80211_need_suspend(struct ath6kl *ar, u32 *suspend_vif) +{ + struct ath6kl_vif *vif; + int i; + + vif = ath6kl_vif_first(ar); + if (!vif) + return false; + + if (!test_bit(WMI_READY, &ar->flag)) { + ath6kl_err("deepsleep failed as wmi is not ready\n"); + return false; + } + + /* + * If one of all virtual interfaces is AP mode then + * force to awake. + */ + *suspend_vif = 0; + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) { + if (vif->nw_type == AP_NETWORK) { + *suspend_vif = 0; + return false; + } + *suspend_vif |= (1 << i); + } + } + + return true; +} + +static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + int i, ret = 0, left; + u32 need_suspend_vif = 0; +WARN_ON(1); + if (!ath6kl_cfg80211_need_suspend(ar, &need_suspend_vif)) + return -EOPNOTSUPP; + + + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend %x\n", + need_suspend_vif); + +#ifdef CONFIG_ANDROID + if (ar->wow_irq) { + if (disable_irq_wake(ar->wow_irq)) + ath6kl_err("Couldn't disable hostwake IRQ wakeup mode\n"); + + free_irq(ar->wow_irq, ar); + } +#endif + + for (i = 0; i < ar->vif_max; i++) { + if (need_suspend_vif & (1 << i)) { + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) { + clear_bit(WLAN_ENABLED, &vif->flags); +pr_err("%s %d disabling wlan\n",__func__,__LINE__); + netif_dormant_on(vif->ndev); + set_bit(DORMANT, &vif->flags); + } + } + } + +#ifdef USB_AUTO_SUSPEND + /* in mck3.0, we won't have credit full problem as before, + so we could move ATH6KL_STATE_DEEPSLEEP after stop_all() + */ + ath6kl_cfg80211_stop_all(ar); + + spin_lock_bh(&ar->state_lock); + ar->state = ATH6KL_STATE_DEEPSLEEP; + spin_unlock_bh(&ar->state_lock); +#else + spin_lock_bh(&ar->state_lock); + ar->state = ATH6KL_STATE_DEEPSLEEP; + spin_unlock_bh(&ar->state_lock); + + ath6kl_cfg80211_stop_all(ar); +#endif + + /* + * Flush data packets and wait for all control packets + * to be cleared in TX path before deep sleep suspend. + */ + ath6kl_tx_data_cleanup(ar); + + if (ar->tx_pending[ar->ctrl_ep]) { + left = wait_event_interruptible_timeout(ar->event_wq, + ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT); + if (left == 0) { + ath6kl_warn("clear wmi ctrl data timeout txpend %d\n", + ar->tx_pending[ar->ctrl_ep]); + ret = -ETIMEDOUT; + } else if (left < 0) { + ath6kl_warn("clear wmi ctrl data failed:%d tx_pend=%d\n", + left, ar->tx_pending[ar->ctrl_ep]); + ret = left; + } + } + + return ret; +} + +int ath6kl_cfg80211_suspend(struct ath6kl *ar, + enum ath6kl_cfg_suspend_mode mode, + struct cfg80211_wowlan *wow) +{ + int ret; + + switch (mode) { + case ATH6KL_CFG_SUSPEND_WOW: + + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode suspend\n"); + + /* Flush all non control pkts in TX path */ + ath6kl_tx_data_cleanup(ar); + +#ifdef USB_AUTO_SUSPEND + ar->state = ATH6KL_STATE_PRE_SUSPEND; +#endif + ret = ath6kl_wow_suspend(ar, wow); + if (ret) { + ath6kl_err("wow suspend failed: %d\n", ret); +#ifdef USB_AUTO_SUSPEND + ar->state = ATH6KL_STATE_WOW; +#endif + return ret; + } + spin_lock_bh(&ar->state_lock); + ar->state = ATH6KL_STATE_WOW; + spin_unlock_bh(&ar->state_lock); + break; + + case ATH6KL_CFG_SUSPEND_DEEPSLEEP: + if (ar->state == ATH6KL_STATE_DEEPSLEEP) + break; + + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n"); + +#ifdef USB_AUTO_SUSPEND + ar->state = ATH6KL_STATE_PRE_SUSPEND_DEEPSLEEP; +#endif + ret = ath6kl_cfg80211_deepsleep_suspend(ar); + if (ret) { + ath6kl_err("deepsleep suspend failed: %d\n", ret); + return ret; + } + + break; + + case ATH6KL_CFG_SUSPEND_CUTPOWER: + +#ifdef CONFIG_ANDROID + if (ar->wow_irq) { + if (disable_irq_wake(ar->wow_irq)) + ath6kl_err("Couldn't disable hostwake IRQ wakeup mode\n"); + + free_irq(ar->wow_irq, ar); + } +#endif + + ath6kl_cfg80211_stop_all(ar); + + if (ar->state == ATH6KL_STATE_OFF) { + ath6kl_dbg(ATH6KL_DBG_SUSPEND, + "suspend hw off, no action for cutpower\n"); + break; + } + + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "suspend cutting power\n"); + + ret = ath6kl_init_hw_stop(ar); + if (ret) { + ath6kl_warn("failed to stop hw during suspend: %d\n", + ret); + } + + ar->state = ATH6KL_STATE_CUTPOWER; + + break; + + default: + break; + } + + return 0; +} + +int ath6kl_cfg80211_resume(struct ath6kl *ar) +{ + int i, ret; + struct ath6kl_vif *vif; + + switch (ar->state) { + case ATH6KL_STATE_WOW: + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "wow mode resume\n"); + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&ar->wake_lock, 3*HZ); +#else + /* TODO: What should I do if there is no wake lock?? */ +#endif + + + + spin_lock_bh(&ar->state_lock); + ar->state = ATH6KL_STATE_ON; + spin_unlock_bh(&ar->state_lock); + + ret = ath6kl_wow_resume(ar); + if (ret) { + ath6kl_warn("wow mode resume failed: %d\n", ret); + return ret; + } + +#ifdef USB_AUTO_SUSPEND + spin_lock_bh(&ar->usb_pm_lock); + ath6kl_auto_pm_wakeup_resume(ar); + spin_unlock_bh(&ar->usb_pm_lock); +#endif + break; + + case ATH6KL_STATE_DEEPSLEEP: + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n"); + + spin_lock_bh(&ar->state_lock); + ar->state = ATH6KL_STATE_ON; + spin_unlock_bh(&ar->state_lock); +#ifdef USB_AUTO_SUSPEND + spin_lock_bh(&ar->usb_pm_lock); + ath6kl_auto_pm_wakeup_resume(ar); + spin_unlock_bh(&ar->usb_pm_lock); +#endif + +#ifdef CONFIG_ANDROID + if (ar->wow_irq) { + int ret; + ret = request_irq(ar->wow_irq, ath6kl_wow_irq, + IRQF_SHARED | IRQF_TRIGGER_RISING, + "ar6000" "sdiowakeup", ar); + if (!ret) { + ret = enable_irq_wake(ar->wow_irq); + if (ret < 0) { + ath6kl_err("Couldn't enable WoW IRQ as wakeup interrupt"); + return ret; + } + ath6kl_info("ath6kl: WoW IRQ %d\n", + ar->wow_irq); + } + } +#endif + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) { + if (test_bit(DORMANT, &vif->flags)) { + set_bit(WLAN_ENABLED, &vif->flags); +pr_err("%s %d enabling wlan\n",__func__,__LINE__); + netif_dormant_off(vif->ndev); + clear_bit(DORMANT, &vif->flags); + } + + /* restore previous power mode */ + if (vif->last_pwr_mode != vif->saved_pwr_mode) { + if (ath6kl_wmi_powermode_cmd(ar->wmi, i, + vif->saved_pwr_mode) != 0) { + ath6kl_err("wmi powermode command failed during resume\n"); + } + } + } + } + + break; + + case ATH6KL_STATE_CUTPOWER: + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "resume restoring power\n"); + +#ifdef CONFIG_ANDROID + if (ar->wow_irq) { + int ret; + ret = request_irq(ar->wow_irq, ath6kl_wow_irq, + IRQF_SHARED | IRQF_TRIGGER_RISING, + "ar6000" "sdiowakeup", ar); + if (!ret) { + ret = enable_irq_wake(ar->wow_irq); + if (ret < 0) { + ath6kl_err("Couldn't enable WoW IRQ as wakeup interrupt"); + return ret; + } + ath6kl_info("ath6kl: WoW IRQ %d\n", + ar->wow_irq); + } + } +#endif + + ret = ath6kl_init_hw_start(ar); + if (ret) { + ath6kl_warn("Failed to boot hw in resume: %d\n", ret); + return ret; + } + break; + + default: + break; + } + + return 0; +} + +#ifdef CONFIG_PM + +/* hif layer decides what suspend mode to use */ +static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wow) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + +#if defined(USB_AUTO_SUSPEND) + if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode)) + return 0; +#endif + + return ath6kl_hif_suspend(ar, wow); +} + +static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + + if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode)) + return 0; + + return ath6kl_hif_resume(ar); +} + +/* + * FIXME: WOW suspend mode is selected if the host sdio controller supports + * both sdio irq wake up and keep power. The target pulls sdio data line to + * wake up the host when WOW pattern matches. This causes sdio irq handler + * is being called in the host side which internally hits ath6kl's RX path. + * + * Since sdio interrupt is not disabled, RX path executes even before + * the host executes the actual resume operation from PM module. + * + * In the current scenario, WOW resume should happen before start processing + * any data from the target. So It's required to perform WOW resume in RX path. + * Ideally we should perform WOW resume only in the actual platform + * resume path. This area needs bit rework to avoid WOW resume in RX path. + * + * ath6kl_check_wow_status() is called from ath6kl_rx(). + */ +void ath6kl_check_wow_status(struct ath6kl *ar) +{ + if (ar->state == ATH6KL_STATE_WOW) + ath6kl_cfg80211_resume(ar); +} + +#else + +void ath6kl_check_wow_status(struct ath6kl *ar) +{ +} +#endif + +#ifndef CFG80211_NO_SET_CHAN_OPERATION +static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + struct ath6kl_vif *vif; + + if (dev == NULL) + return -EBUSY; + + vif = netdev_priv(dev); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&vif->ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u channel_type=%d\n", + __func__, chan->center_freq, chan->hw_value, channel_type); + vif->next_chan = chan->center_freq; + + if (ath6kl_mod_debug_quirks(vif->ar, + ATH6KL_MODULE_ENABLE_P2P_CHANMODE)) { + if (channel_type == NL80211_CHAN_HT40PLUS) + vif->next_chan_type = ATH6KL_CHAN_TYPE_HT40PLUS; + else if (channel_type == NL80211_CHAN_HT40MINUS) + vif->next_chan_type = ATH6KL_CHAN_TYPE_HT40MINUS; + else if (channel_type == NL80211_CHAN_HT20) + vif->next_chan_type = ATH6KL_CHAN_TYPE_HT20; + else + vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE; + } else + vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE; + + up(&vif->ar->sem); + + return 0; +} +#endif + +static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif, + const u8 *ies, size_t ies_len) +{ + struct ath6kl *ar = vif->ar; + const u8 *pos; + u8 *buf = NULL; + size_t len = 0; + int ret; + + /* + * Filter out P2P/WFD IE(s) since they will be included depending on + * the Probe Request frame in ath6kl_wmi_send_go_probe_response_cmd(). + */ + + if (ies && ies_len) { + buf = kmalloc(ies_len, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + pos = ies; + while (pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + if ((!ath6kl_is_p2p_ie(pos)) && + (!ath6kl_is_wfd_ie(pos))) { + memcpy(buf + len, pos, 2 + pos[1]); + len += 2 + pos[1]; + } + pos += 2 + pos[1]; + } + } + + ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_PROBE_RESP, buf, len); + kfree(buf); + return ret; +} + +static int ath6kl_set_uapsd(struct wiphy *wiphy, struct net_device *dev, + const u8 *ies, int ies_len) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + const u8 *pos; + int uapsd = 0; /* default is OFF */ + bool found = false; + + if (ies && ies_len) { + pos = ies; + + while (pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + + if (ath6kl_is_wmm_ie(pos)) { + found = true; + if (pos[8] & 0x80) /* QOS-INFO, BIT(7) */ + uapsd = 1; + break; + } + + pos += 2 + pos[1]; + } + } + + if (!found) + clear_bit(WMM_ENABLED, &vif->flags); + + return ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, uapsd); +} + +#ifdef PMF_SUPPORT +static int ath6kl_set_akm_suites(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + const u8 *pos, *ies = sme->ie; + int ies_len = sme->ie_len; + int ret = 0; + + if (ies && ies_len) { + pos = ies; + + while (pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + + if (ath6kl_is_rsn_ie(pos)) { + int offset, i; + + /* + * VER [2] + + * 1 GROUP Cipher [4] + + * n Pairwise Ciphers [2 + 4*n] + + * n AKM suites [2 + 4*n] + + * RSN-Capabilities [2] + + * n PMKID Count/List [2 + 16*n] + + * 1 Group Mgmt Cipher [4] + */ + if (pos[1] <= 18) + break; + + /* Get/Set AKM value */ + offset = 1 + 1 + 2 + 4 + 2 + (pos[8] * 4); + if (pos[offset]) { + sme->crypto.n_akm_suites = pos[offset]; + sme->crypto.akm_suites[0] = pos[offset + 2]; + for (i = 1; i < 4; i++) { + sme->crypto.akm_suites[0] = sme->crypto.akm_suites[0] << 8; + sme->crypto.akm_suites[0] += pos[offset + 2 + i]; + } + break; + } + } + + pos += 2 + pos[1]; + } + } + + return ret; +} +#endif + +static int ath6kl_set_rsn_cap(struct wiphy *wiphy, struct net_device *dev, + const u8 *ies, int ies_len) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + const u8 *pos; + u16 rsn_cap = ATH6KL_RSN_CAP_NULLCONF; + int ret = 0; + + if (ies && ies_len) { + pos = ies; + + while (pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + + if (ath6kl_is_rsn_ie(pos)) { + int offset; + + /* + * VER[2] + + * 1 GROUP Cipher[4] + + * 1 Pairwise Cipher[2+4] + + * 1 Auth suit[2+4] = 18 + */ + if (pos[1] <= 18) + break; + + /* Get RSN-CAP offset */ + offset = 1 + 1 + 2 + 4 + 2 + (pos[8] * 4); + offset += 2 + (pos[offset] * 4); + if (offset > (pos[1] + 2)) + break; + + rsn_cap = (pos[offset] | + (pos[offset + 1] << 8)); + break; + } + + pos += 2 + pos[1]; + } + } + + vif->last_rsn_cap = ATH6KL_RSN_CAP_NULLCONF; + if (rsn_cap != ATH6KL_RSN_CAP_NULLCONF) { + ret = ath6kl_wmi_get_rsn_cap(ar->wmi, vif->fw_vif_idx); + if (!ret) + ret = ath6kl_wmi_set_rsn_cap(ar->wmi, + vif->fw_vif_idx, rsn_cap); + } + + return ret; +} + +static u16 _ath6kl_ap_get_channel(struct ath6kl_vif *vif, + struct ath6kl_beacon_parameters *info) +{ + u16 chan = 0; + + /* The wpa_supplicant will set-channel first then start AP. */ + +#ifdef CFG80211_NO_SET_CHAN_OPERATION + /* + * New cfg80211 implementation will cache channel information and + * setup it when start AP operation. + * Here get the channel information from the parameter of start AP + * operation then set to driver. + */ + if (ath6kl_mod_debug_quirks(vif->ar, + ATH6KL_MODULE_ENABLE_P2P_CHANMODE)) { + if (info->channel_type == NL80211_CHAN_HT40PLUS) + vif->next_chan_type = ATH6KL_CHAN_TYPE_HT40PLUS; + else if (info->channel_type == NL80211_CHAN_HT40MINUS) + vif->next_chan_type = ATH6KL_CHAN_TYPE_HT40MINUS; + else if (info->channel_type == NL80211_CHAN_HT20) + vif->next_chan_type = ATH6KL_CHAN_TYPE_HT20; + else + vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE; + } else + vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE; + + WARN_ON(!info->channel); + if (info->channel) + vif->next_chan = info->channel->center_freq; + else { + vif->next_chan = 0; + vif->next_chan_type = ATH6KL_CHAN_TYPE_NONE; + } +#endif + + /* + * The driver need to cache channel information in the set-channel + * operation for old cfg80211 implemenation. + * Here just report the cached channel information. + */ + chan = vif->next_chan; + if (vif->next_chan_type != ATH6KL_CHAN_TYPE_NONE) { + enum wmi_connect_ap_channel_type chan_type = + AP_CHANNEL_TYPE_NONE; + + if (vif->next_chan_type == ATH6KL_CHAN_TYPE_HT40PLUS) + chan_type = AP_CHANNEL_TYPE_HT40PLUS; + else if (vif->next_chan_type == ATH6KL_CHAN_TYPE_HT40MINUS) + chan_type = AP_CHANNEL_TYPE_HT40MINUS; + else if (vif->next_chan_type == ATH6KL_CHAN_TYPE_HT20) + chan_type = AP_CHANNEL_TYPE_HT20; + + chan |= (chan_type << WMI_CONNECT_AP_CHAN_SELECT_OFFSET); + } + + return chan; +} + +static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, + struct ath6kl_beacon_parameters *info, bool add) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + struct ieee80211_mgmt *mgmt; + u8 *ies; + int ies_len; + struct wmi_connect_cmd p; + int res; + int i; + u16 chan; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (vif->next_mode != AP_NETWORK) { + up(&ar->sem); + return -EOPNOTSUPP; + } + + if (info->beacon_ies) { + u8 *beacon_ies = (u8 *)info->beacon_ies; + size_t beacon_ies_len = info->beacon_ies_len; + + ath6kl_p2p_ps_user_app_ie(vif->p2p_ps_info_ctx, + WMI_FRAME_BEACON, + &beacon_ies, + &beacon_ies_len); + + res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_BEACON, + beacon_ies, + beacon_ies_len); + if (res) { + up(&ar->sem); + return res; + } + } + + /* + * IOT : Set default DTIM period to 1 . + * wpa_supplicant default is 2 and target default is 5. + */ + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: set DTIM from %d to 1.\n", + __func__, info->dtim_period); + ath6kl_wmi_set_dtim_cmd(ar->wmi, vif->fw_vif_idx, 1); + + if (info->proberesp_ies) { + res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies, + info->proberesp_ies_len); + if (res) { + up(&ar->sem); + return res; + } + } + if (info->assocresp_ies) { + res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_ASSOC_RESP, + info->assocresp_ies, + info->assocresp_ies_len); + if (res) { + up(&ar->sem); + return res; + } + } + + if (!add) { + up(&ar->sem); + return 0; + } + + /* Config keep-alive */ + if (info->inactivity_timeout) + ath6kl_ap_keepalive_config_by_supp(vif, + info->inactivity_timeout); + + /* Turn-on/off uAPS. */ + if (ath6kl_set_uapsd(wiphy, dev, info->tail, info->tail_len)) { + up(&ar->sem); + return -EIO; + } + + /* Update RSN Capabilities. */ + if (ath6kl_set_rsn_cap(wiphy, dev, info->tail, info->tail_len)) { + up(&ar->sem); + return -EIO; + } + + /* Turn off power saving mode, if the first interface is AP mode */ + if (vif == ath6kl_vif_first(ar)) { + if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx, + MAX_PERF_POWER)) { + up(&ar->sem); + return -EIO; + } + } + + vif->ap_mode_bkey.valid = false; + + if (info->beacon_interval) { + res = ath6kl_wmi_set_beacon_interval_cmd(ar->wmi, + vif->fw_vif_idx, info->beacon_interval); + if (res) { + up(&ar->sem); + return res; + } + } + + if (info->head == NULL) { + up(&ar->sem); + return -EINVAL; + } + mgmt = (struct ieee80211_mgmt *) info->head; + ies = mgmt->u.beacon.variable; + if (ies > info->head + info->head_len) { + up(&ar->sem); + return -EINVAL; + } + ies_len = info->head + info->head_len - ies; + + if (info->ssid == NULL) { + up(&ar->sem); + return -EINVAL; + } + memcpy(vif->ssid, info->ssid, info->ssid_len); + vif->ssid_len = info->ssid_len; + + if (info->hidden_ssid == NL80211_HIDDEN_SSID_ZERO_LEN) { + up(&ar->sem); + return -ENOTSUPP; + } else if (info->hidden_ssid == NL80211_HIDDEN_SSID_ZERO_CONTENTS) { + res = ath6kl_wmi_set_hidden_ssid_cmd( + ar->wmi, vif->fw_vif_idx, 1); + } else { + res = ath6kl_wmi_set_hidden_ssid_cmd( + ar->wmi, vif->fw_vif_idx, 0); + } + if (res) { + up(&ar->sem); + return res; + } + + res = ath6kl_set_auth_type(vif, info->auth_type); + if (res) { + up(&ar->sem); + return res; + } + + memset(&p, 0, sizeof(p)); + + for (i = 0; i < info->crypto.n_akm_suites; i++) { + switch (info->crypto.akm_suites[i]) { + case WLAN_AKM_SUITE_8021X: + if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) + p.auth_mode |= WPA_AUTH; + if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) + p.auth_mode |= WPA2_AUTH; + break; + case WLAN_AKM_SUITE_PSK: + if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) + p.auth_mode |= WPA_PSK_AUTH; + if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) + p.auth_mode |= WPA2_PSK_AUTH; + break; + } + } + if (p.auth_mode == 0) + p.auth_mode = NONE_AUTH; + vif->auth_mode = p.auth_mode; + + for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) { + switch (info->crypto.ciphers_pairwise[i]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + p.prwise_crypto_type |= WEP_CRYPT; + break; + case WLAN_CIPHER_SUITE_TKIP: + p.prwise_crypto_type |= TKIP_CRYPT; + break; + case WLAN_CIPHER_SUITE_CCMP: + p.prwise_crypto_type |= AES_CRYPT; + break; + case WLAN_CIPHER_SUITE_SMS4: + p.prwise_crypto_type |= WAPI_CRYPT; + break; + } + } + if (p.prwise_crypto_type == 0) { + p.prwise_crypto_type = NONE_CRYPT; + ath6kl_set_cipher(vif, 0, true); + } else if (info->crypto.n_ciphers_pairwise == 1) + ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true); + + switch (info->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + p.grp_crypto_type = WEP_CRYPT; + break; + case WLAN_CIPHER_SUITE_TKIP: + p.grp_crypto_type = TKIP_CRYPT; + break; + case WLAN_CIPHER_SUITE_CCMP: + p.grp_crypto_type = AES_CRYPT; + break; + case WLAN_CIPHER_SUITE_SMS4: + p.grp_crypto_type = WAPI_CRYPT; + break; + default: + p.grp_crypto_type = NONE_CRYPT; + break; + } + ath6kl_set_cipher(vif, info->crypto.cipher_group, false); + + p.nw_type = AP_NETWORK; + vif->nw_type = vif->next_mode; + + p.ssid_len = vif->ssid_len; + memcpy(p.ssid, vif->ssid, vif->ssid_len); + p.dot11_auth_mode = vif->dot11_auth_mode; + + chan = _ath6kl_ap_get_channel(vif, info); + p.ch = cpu_to_le16(chan); + + res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); + if (res < 0) { + up(&ar->sem); + return res; + } + + up(&ar->sem); + + return 0; +} + +#ifdef NL80211_CMD_START_STOP_AP +static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *settings) +{ + struct ath6kl_beacon_parameters *info; + int ret = 0; + + info = kzalloc(sizeof(struct ath6kl_beacon_parameters), GFP_ATOMIC); + if (info == NULL) + return -ENOMEM; + + /* Fetch to local setting */ + info->beacon_interval = settings->beacon_interval; + info->dtim_period = settings->dtim_period; + info->ssid = settings->ssid; + info->ssid_len = settings->ssid_len; + info->hidden_ssid = settings->hidden_ssid; + memcpy(&info->crypto, + &settings->crypto, + sizeof(struct cfg80211_crypto_settings)); + info->privacy = settings->privacy; + info->auth_type = settings->auth_type; + info->inactivity_timeout = settings->inactivity_timeout; +#ifdef CFG80211_NO_SET_CHAN_OPERATION +#ifdef CFG80211_NEW_CHAN_DEFINITION + info->channel = settings->chandef.chan; + info->channel_type = cfg80211_get_chandef_type(&settings->chandef); +#else + info->channel = settings->channel; + info->channel_type = settings->channel_type; +#endif +#endif +#ifdef NL80211_ATTR_P2P_CTWINDOW_OPPPS + info->p2p_ctwindow = settings->p2p_ctwindow; + info->p2p_opp_ps = settings->p2p_opp_ps; +#else + info->p2p_ctwindow = 0; + info->p2p_opp_ps = false; +#endif + + info->head = settings->beacon.head; + info->tail = settings->beacon.tail; + info->head_len = settings->beacon.head_len; + info->tail_len = settings->beacon.tail_len; + info->beacon_ies = settings->beacon.beacon_ies; + info->beacon_ies_len = settings->beacon.beacon_ies_len; + info->proberesp_ies = settings->beacon.proberesp_ies; + info->proberesp_ies_len = settings->beacon.proberesp_ies_len; + info->assocresp_ies = settings->beacon.assocresp_ies; + info->assocresp_ies_len = settings->beacon.assocresp_ies_len; + info->probe_resp = settings->beacon.probe_resp; + info->probe_resp_len = settings->beacon.probe_resp_len; + + ret = ath6kl_ap_beacon(wiphy, dev, info, true); + kfree(info); + + return ret; +} + +static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *settings) +{ + struct ath6kl_beacon_parameters *info; + int ret = 0; + + info = kzalloc(sizeof(struct ath6kl_beacon_parameters), GFP_ATOMIC); + if (info == NULL) + return -ENOMEM; + + /* Fetch to local setting */ + info->head = settings->head; + info->tail = settings->tail; + info->head_len = settings->head_len; + info->tail_len = settings->tail_len; + info->beacon_ies = settings->beacon_ies; + info->beacon_ies_len = settings->beacon_ies_len; + info->proberesp_ies = settings->proberesp_ies; + info->proberesp_ies_len = settings->proberesp_ies_len; + info->assocresp_ies = settings->assocresp_ies; + info->assocresp_ies_len = settings->assocresp_ies_len; + info->probe_resp = settings->probe_resp; + info->probe_resp_len = settings->probe_resp_len; + + ret = ath6kl_ap_beacon(wiphy, dev, info, false); + kfree(info); + + return ret; +} +#else +static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *settings) +{ + struct ath6kl_beacon_parameters *info; + int ret = 0; + + info = kzalloc(sizeof(struct ath6kl_beacon_parameters), GFP_ATOMIC); + if (info == NULL) + return -ENOMEM; + + /* Fetch to local setting */ + info->beacon_interval = settings->interval; + info->dtim_period = settings->dtim_period; + info->ssid = settings->ssid; + info->ssid_len = settings->ssid_len; + info->hidden_ssid = settings->hidden_ssid; + memcpy(&info->crypto, + &settings->crypto, + sizeof(struct cfg80211_crypto_settings)); + info->privacy = settings->privacy; + info->auth_type = settings->auth_type; + + info->head = settings->head; + info->tail = settings->tail; + info->head_len = settings->head_len; + info->tail_len = settings->tail_len; + info->beacon_ies = settings->beacon_ies; + info->beacon_ies_len = settings->beacon_ies_len; + info->proberesp_ies = settings->proberesp_ies; + info->proberesp_ies_len = settings->proberesp_ies_len; + info->assocresp_ies = settings->assocresp_ies; + info->assocresp_ies_len = settings->assocresp_ies_len; + info->probe_resp = settings->probe_resp; + info->probe_resp_len = settings->probe_resp_len; + + ret = ath6kl_ap_beacon(wiphy, dev, info, true); + kfree(info); + + return ret; +} + +static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *settings) +{ + struct ath6kl_beacon_parameters *info; + int ret = 0; + + info = kzalloc(sizeof(struct ath6kl_beacon_parameters), GFP_ATOMIC); + if (info == NULL) + return -ENOMEM; + + /* Fetch to local setting */ + info->head = settings->head; + info->tail = settings->tail; + info->head_len = settings->head_len; + info->tail_len = settings->tail_len; + info->beacon_ies = settings->beacon_ies; + info->beacon_ies_len = settings->beacon_ies_len; + info->proberesp_ies = settings->proberesp_ies; + info->proberesp_ies_len = settings->proberesp_ies_len; + info->assocresp_ies = settings->assocresp_ies; + info->assocresp_ies_len = settings->assocresp_ies_len; + info->probe_resp = settings->probe_resp; + info->probe_resp_len = settings->probe_resp_len; + + ret = ath6kl_ap_beacon(wiphy, dev, info, false); + kfree(info); + + return ret; +} +#endif +static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (vif->nw_type != AP_NETWORK) { + up(&ar->sem); + return -EOPNOTSUPP; + } + if (!test_bit(CONNECTED, &vif->flags)) { + up(&ar->sem); + return -ENOTCONN; + } + + /* Back to origional value. */ + if (vif->last_rsn_cap != ATH6KL_RSN_CAP_NULLCONF) + ath6kl_wmi_set_rsn_cap(ar->wmi, vif->fw_vif_idx, + vif->last_rsn_cap); + + /* Stop keep-alive. */ + ath6kl_ap_keepalive_stop(vif); + + /* Stop ACL. */ + ath6kl_ap_acl_stop(vif); + + /* Stop Admission-Control */ + ath6kl_ap_admc_stop(vif); + + ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); + clear_bit(CONNECTED, &vif->flags); + ath6kl_judge_roam_parameter(vif, true); + ath6kl_switch_parameter_based_on_connection(vif, true); + + up(&ar->sem); + + return 0; +} + +static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + const u8 *addr = mac ? mac : bcast_addr; + int ret; + struct ath6kl_sta *sta_conn = NULL; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (!is_broadcast_ether_addr(addr)) { + sta_conn = ath6kl_find_sta(vif, (u8 *)addr); + if (sta_conn == NULL) { + up(&ar->sem); + ret = 0; + return ret; + } + } + + ret = ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH, + addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + + up(&ar->sem); + + return ret; +} + +static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_parameters *params) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + int ret = 0; + struct ath6kl_sta *sta_conn = NULL; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if (vif->nw_type != AP_NETWORK) { + up(&ar->sem); + return -EOPNOTSUPP; + } + + /* Use this only for authorizing/unauthorizing a station */ + if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) { + up(&ar->sem); + return -EOPNOTSUPP; + } + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { + ret = ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, + WMI_AP_MLME_AUTHORIZE, mac, 0); + + up(&ar->sem); + + return ret; + } + + if (!is_broadcast_ether_addr(mac)) { + sta_conn = ath6kl_find_sta(vif, mac); + if (sta_conn == NULL) { + up(&ar->sem); + return ret; + } + } + + ret = ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, + WMI_AP_MLME_UNAUTHORIZE, mac, 0); + + up(&ar->sem); + + return ret; +} + +static int _ath6kl_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, + u64 *cookie) +{ +#define MAX_ROC_PERIOD \ + (ATH6KL_ROC_MAX_PERIOD * HZ + HZ / ATH6KL_ROC_MAX_PERIOD) +#define MAX_SCAN_PERIOD (ATH6KL_SCAN_FG_MAX_PERIOD * HZ) + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + u32 id; + int ret; + long left; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + /* If already ongoing scan then wait it finish. */ + if (vif->scan_req) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : Schedule a RoC but on-going Scan %x\n", + vif->last_roc_id); + + wait_event_interruptible_timeout(ar->event_wq, + !vif->scan_req, + MAX_SCAN_PERIOD); + + if (signal_pending(current)) { + ath6kl_err("RoC : last scan not yet finish?\n"); + up(&ar->sem); + return -EBUSY; + } + } + + /* If already pending remain-on-channel then reject request. */ + if (test_bit(ROC_PEND, &vif->flags)) { + ath6kl_err("RoC : Receive duplicate ROC.\n"); + up(&ar->sem); + return -EBUSY; + } + + /* If ongoing remain-on-channel and wait it finish. */ + if (test_bit(ROC_ONGOING, &vif->flags)) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : Last RoC not yet finish, %x %d %d", + vif->last_roc_id, + ((test_bit(ROC_PEND, &vif->flags)) ? 1 : 0), + ((test_bit(ROC_ONGOING, &vif->flags)) ? 1 : 0)); + + set_bit(ROC_WAIT_EVENT, &vif->flags); + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(ROC_ONGOING, &vif->flags), + MAX_ROC_PERIOD); + + if (left == 0) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : wait ROC_WAIT_EVENT timeout\n"); + clear_bit(ROC_WAIT_EVENT, &vif->flags); + clear_bit(ROC_ONGOING, &vif->flags); + } + + if (signal_pending(current)) { + ath6kl_err("RoC : last RoC not yet finish?\n"); + up(&ar->sem); + return -EBUSY; + } + } + + spin_lock_bh(&vif->if_lock); + id = ++vif->last_roc_id; + if (id == 0) { + /* Do not use 0 as the cookie value */ + id = ++vif->last_roc_id; + } + *cookie = id; + spin_unlock_bh(&vif->if_lock); + + /* Cache request channel and report to cfg80211 when target reject. */ + vif->last_roc_channel = chan; + vif->last_roc_duration = duration; + + set_bit(ROC_PEND, &vif->flags); + ret = ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx, + chan->center_freq, duration); + up(&ar->sem); + + return ret; +#undef MAX_ROC_PERIOD +#undef MAX_SCAN_PERIOD +} + +static int _ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + int ret; + long left; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + /* + * RoC not yet start but be cancelled. Wait it started then cancel + * it by order to avoid wrong cookie report to supplicant. + */ + if (test_bit(ROC_PEND, &vif->flags)) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : RoC not start but canceled %x\n", + vif->last_roc_id); + + set_bit(ROC_WAIT_EVENT, &vif->flags); + left = wait_event_interruptible_timeout(ar->event_wq, + test_bit(ROC_ONGOING, &vif->flags), + WMI_TIMEOUT); + + /* + * Another corner case is that last RoC not yet start & also + * be rejected by target, but be canceled. In this case, + * treat WMI_TIMEOUT as target reject then return. + */ + if (left == 0) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : wait ROC_WAIT_EVENT timeout, %lld %d\n", + cookie, + vif->last_roc_id); + clear_bit(ROC_WAIT_EVENT, &vif->flags); + clear_bit(ROC_PEND, &vif->flags); + up(&ar->sem); + return 0; + } + + if (signal_pending(current)) { + ath6kl_err("RoC : target did not respond\n"); + up(&ar->sem); + return -EINTR; + } + } + + spin_lock_bh(&vif->if_lock); + if (cookie != vif->last_roc_id) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : Cancel-RoC %llx but current is %x\n", cookie, + vif->last_roc_id); + + spin_unlock_bh(&vif->if_lock); + up(&ar->sem); + return -ENOENT; + } + + vif->last_cancel_roc_id = cookie; + set_bit(ROC_CANCEL_PEND, &vif->flags); + spin_unlock_bh(&vif->if_lock); + + ret = ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx); + + up(&ar->sem); + + return ret; +} + +static int _ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + u32 id; + u32 wmi_data_flags = 0; + const struct ieee80211_mgmt *mgmt; + int ret = 0; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + if ((ar->p2p) && !ar->p2p_compat && + (ar->p2p_concurrent) && + (vif->fw_vif_idx == (ar->vif_max - 1)) && + !test_bit(ROC_ONGOING, &vif->flags)) { + + if (ath6kl_p2p_is_p2p_frame(ar, buf, len)) { + ath6kl_err("RoC : RoC channel closed, ignore action frame\n"); + up(&ar->sem); + return -EINVAL; + } + + } + + id = vif->send_action_id++; + if (id == 0) { + /* + * 0 is a reserved value in the WMI command and shall not be + * used for the command. + */ + id = vif->send_action_id++; + } + + *cookie = id; + + /* AP mode Power saving processing */ + if (vif->nw_type == AP_NETWORK) { + if (ath6kl_mgmt_powersave_ap(vif, + id, + chan->center_freq, + wait, + buf, + len, + no_cck, + dont_wait_for_ack, + &wmi_data_flags)) { + up(&ar->sem); + return ret; + } + } + + mgmt = (const struct ieee80211_mgmt *) buf; + if (buf + len >= mgmt->u.probe_resp.variable && + vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && + ieee80211_is_probe_resp(mgmt->frame_control)) { + /* + * Send Probe Response frame in AP mode using a separate WMI + * command to allow the target to fill in the generic IEs. + */ + ret = ath6kl_wmi_send_go_probe_response_cmd(ar->wmi, vif, + buf, len, + chan->center_freq); + up(&ar->sem); + return ret; + } + + ret = ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id, + chan->center_freq, wait, + buf, len); + up(&ar->sem); + return ret; +} + +static void _ath6kl_mgmt_frame_register(struct wiphy *wiphy, + struct net_device *dev, + u16 frame_type, bool reg) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + + if (!ath6kl_cfg80211_ready(vif)) + return; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n", + __func__, frame_type, reg); + if (frame_type == IEEE80211_STYPE_PROBE_REQ) { + /* + * Note: This notification callback is not allowed to sleep, so + * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we + * hardcode target to report Probe Request frames all the time. + */ + vif->probe_req_report = reg; + } +#ifdef CE_SUPPORT + if (frame_type == IEEE80211_STYPE_PROBE_RESP) + vif->probe_resp_report = reg; +#endif +} + +#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV +#ifdef CFG80211_REMOVE_ROC_CHAN_TYPE +static int ath6kl_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, + u64 *cookie) +{ + BUG_ON(!wdev->netdev); + + return _ath6kl_remain_on_channel(wiphy, + wdev->netdev, + chan, + NL80211_CHAN_NO_HT, + duration, + cookie); +} +#else +static int ath6kl_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, + u64 *cookie) +{ + BUG_ON(!wdev->netdev); + + return _ath6kl_remain_on_channel(wiphy, + wdev->netdev, + chan, + channel_type, + duration, + cookie); +} +#endif + +static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +{ + BUG_ON(!wdev->netdev); + + return _ath6kl_cancel_remain_on_channel(wiphy, + wdev->netdev, + cookie); +} + +#ifdef CFG80211_REMOVE_ROC_CHAN_TYPE +static int ath6kl_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) +{ + BUG_ON(!wdev->netdev); + + return _ath6kl_mgmt_tx(wiphy, + wdev->netdev, + chan, + offchan, + NL80211_CHAN_NO_HT, + true, + wait, + buf, + len, + no_cck, + dont_wait_for_ack, + cookie); +} +#else +static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) +{ + BUG_ON(!wdev->netdev); + + return _ath6kl_mgmt_tx(wiphy, + wdev->netdev, + chan, + offchan, + channel_type, + channel_type_valid, + wait, + buf, + len, + no_cck, + dont_wait_for_ack, + cookie); +} +#endif + +static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + u16 frame_type, bool reg) +{ + BUG_ON(!wdev->netdev); + + return _ath6kl_mgmt_frame_register(wiphy, + wdev->netdev, + frame_type, + reg); +} +#else +static int ath6kl_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, + u64 *cookie) +{ + return _ath6kl_remain_on_channel(wiphy, + dev, + chan, + channel_type, + duration, + cookie); +} + +static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie) +{ + return _ath6kl_cancel_remain_on_channel(wiphy, + dev, + cookie); +} + +static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) +{ + return _ath6kl_mgmt_tx(wiphy, + dev, + chan, + offchan, + channel_type, + channel_type_valid, + wait, + buf, + len, + no_cck, + dont_wait_for_ack, + cookie); +} + +static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, + struct net_device *dev, + u16 frame_type, bool reg) +{ + return _ath6kl_mgmt_frame_register(wiphy, + dev, + frame_type, + reg); +} +#endif + +int ath6kl_set_gtk_rekey_offload(struct wiphy *wiphy, + struct net_device *dev, struct cfg80211_gtk_rekey_data *data) +{ + int ret = 0; + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct wmi_gtk_offload_op cmd; + struct ath6kl_vif *vif = ath6kl_vif_first(ar); + + ath6kl_dbg(ATH6KL_DBG_TRC, "+++\n"); + if (!vif) + return -EIO; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (!data) + return ret; + + + memset(&cmd, 0, sizeof(struct wmi_gtk_offload_op)); + + memcpy(cmd.kek, data->kek, GTK_OFFLOAD_KEK_BYTES); + memcpy(cmd.kck, data->kck, GTK_OFFLOAD_KCK_BYTES); + memcpy(cmd.replay_counter, data->replay_ctr, GTK_REPLAY_COUNTER_BYTES); + + ret = ath6kl_wm_set_gtk_offload(ar->wmi, vif->fw_vif_idx, cmd.kek, + cmd.kck, cmd.replay_counter); + + return ret; +} + +static int ath6kl_ap_probe_client(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl_sta *conn; + int ret; + + BUG_ON(vif->nw_type != AP_NETWORK); + + conn = ath6kl_find_sta(vif, (u8 *)peer); + if (conn) + ret = ath6kl_wmi_ap_poll_sta(ar->wmi, + vif->fw_vif_idx, + conn->aid); + else { + ret = -EINTR; + + ath6kl_dbg(ATH6KL_DBG_INFO, + "can't find sta %02x:%02x:%02x:%02x:%02x:%02x, vif-idx %d\n", + peer[0], peer[1], peer[2], peer[3], peer[4], peer[5], + vif->fw_vif_idx); + } + + return ret; +} + +bool ath6kl_sched_scan_trigger(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + int i; + + /* NOT YET */ + + if (!ar->sche_scan) + return false; + + for (i = 0; i < ar->vif_max; i++) { + struct ath6kl_vif *vif_trig; + + vif_trig = ath6kl_get_vif_by_index(ar, i); + if ((vif_trig) && + (vif_trig != vif) && + (!test_bit(CONNECTED, &vif_trig->flags)) && + (vif_trig->sche_scan_interval)) { + ath6kl_dbg(ATH6KL_DBG_INFO, "sche scan triggered\n"); + + return true; + } + } + + return false; +} + +static void ath6kl_sched_scan_timer(unsigned long ptr) +{ + struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr; + + /* NOT YET */ + + ath6kl_dbg(ATH6KL_DBG_INFO, "report sche scan\n"); + + cfg80211_sched_scan_results(vif->ar->wiphy); + + mod_timer(&vif->sche_scan_timer, + jiffies + msecs_to_jiffies(vif->sche_scan_interval)); + + return; +} + +static int ath6kl_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + int ret = 0; + + /* NOT YET */ + + ath6kl_dbg(ATH6KL_DBG_INFO, + "start sche scan, interval %d, n_ssids %d, n_channels %d\n", + request->interval, + request->n_ssids, + request->n_channels); + + if ((vif->ar->sche_scan) && + (vif->nw_type == INFRA_NETWORK)) { + vif->sche_scan_interval = request->interval; + + mod_timer(&vif->sche_scan_timer, + jiffies + + msecs_to_jiffies(vif->sche_scan_interval)); + } else + ret = -EOPNOTSUPP; + + return ret; +} + +static int ath6kl_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + + /* NOT YET */ + + ath6kl_dbg(ATH6KL_DBG_INFO, "stop sche scan\n"); + + if (vif->sche_scan_interval) { + del_timer(&vif->sche_scan_timer); + vif->sche_scan_interval = 0; + } + + return 0; +} + +static int ath6kl_sched_scan_init(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + + /* NOT YET */ + + if (ar->sche_scan) { + vif->sche_scan_interval = 0; + + init_timer(&vif->sche_scan_timer); + vif->sche_scan_timer.function = ath6kl_sched_scan_timer; + vif->sche_scan_timer.data = (unsigned long)(vif); + } + + return 0; +} + +static int ath6kl_sched_scan_deinit(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + + ath6kl_sched_scan_stop(ar->wiphy, vif->ndev); + + return 0; +} + +#if defined(CONFIG_ANDROID) || defined(USB_AUTO_SUSPEND) +int ath6kl_set_wow_mode(struct wiphy *wiphy, struct cfg80211_wowlan *wow) +{ + struct ath6kl_vif *vif; + int ret = 0, pos, i; + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + u32 filter = 0; + u8 mask[WOW_MASK_SIZE]; + u32 host_req_delay = WOW_HOST_REQ_DELAY; + + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "%s: +++\n", __func__); + + vif = ath6kl_vif_first(ar); + if (!vif) { + ret = -EIO; + goto FAIL; + } + + if (!ath6kl_cfg80211_ready(vif)) { + ret = -EIO; + goto FAIL; + } + + if (WARN_ON(!wow)) + goto FAIL; + + /* Reset wakeup delay time to 2 secs for sdio, keep 5 secs for + * usb now + */ + if (ar->hif_type == ATH6KL_HIF_TYPE_SDIO) + host_req_delay = 2000; + + /* for hsic mode, reduce the wakeup time to 2 secs */ + if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode)) + host_req_delay = 2000; + + /* Clear existing WOW patterns */ + for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++) + ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, + WOW_LIST_ID, i); + + /* Configure new WOW patterns */ + for (i = 0; i < wow->n_patterns; i++) { + + if (ath6kl_wow_ext) { + ret = ath6kl_wmi_add_wow_ext_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + wow->patterns[i].pattern_len, + i, + wow->patterns[i].pattern, + wow->patterns[i].mask); + /* filter for wow ext pattern */ + filter |= WOW_FILTER_OPTION_PATTERNS; + } else { + /* + * Convert given nl80211 specific mask value to + * equivalent driver specific mask value and send + * it to the chip along with patterns. For example, + * If the mask value defined in struct cfg80211_wowlan + * is 0xA (equivalent binary is 1010), then equivalent + * driver specific mask value is "0xFF 0x00 0xFF 0x00". + */ + memset(&mask, 0, sizeof(mask)); + for (pos = 0; pos < wow->patterns[i].pattern_len; + pos++) { + if (wow->patterns[i].mask[pos / 8] & + (0x1 << (pos % 8))) + mask[pos] = 0xFF; + } + /* + * Note: Pattern's offset is not passed as part of + * wowlan parameter from CFG layer. So it's always + * passed as ZERO to the firmware. It means, given + * WOW patterns are always matched from the first + * byte of received pkt in the firmware. + */ + ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi, + vif->fw_vif_idx, WOW_LIST_ID, + wow->patterns[i].pattern_len, + 0 /* pattern offset */, + wow->patterns[i].pattern, mask); + } + if (ret) + return ret; + } + + if (wow->disconnect || wow->any) { + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_NWK_DISASSOC\n"); + filter |= WOW_FILTER_OPTION_NWK_DISASSOC; + } + + if (wow->magic_pkt || wow->any) { + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_MAGIC_PACKET\n"); + filter |= WOW_FILTER_OPTION_MAGIC_PACKET; + } + if (wow->gtk_rekey_failure || wow->any) { + ath6kl_dbg(ATH6KL_DBG_WOWLAN, + "filter: WOW_FILTER_OPTION_OFFLOAD_GTK/GTK_ERROR\n"); + filter |= (WOW_FILTER_OPTION_EAP_REQ | + WOW_FILTER_OPTION_8021X_4WAYHS | + WOW_FILTER_OPTION_GTK_ERROR | + WOW_FILTER_OPTION_OFFLOAD_GTK); + } + if (wow->eap_identity_req || wow->any) { + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_EAP_REQ\n"); + filter |= WOW_FILTER_OPTION_EAP_REQ; + + } + + if (wow->four_way_handshake || wow->any) { + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "filter: WOW_FILTER_OPTION_8021X_4WAYHS\n"); + filter |= WOW_FILTER_OPTION_8021X_4WAYHS; + } + + /*Do GTK offload in WPA/WPA2 auth mode connection.*/ + if (vif->auth_mode == WPA2_AUTH_CCKM || vif->auth_mode == WPA2_PSK_AUTH + || vif->auth_mode == WPA_AUTH_CCKM || vif->auth_mode == WPA_PSK_AUTH){ + filter |= WOW_FILTER_OPTION_OFFLOAD_GTK; + } + + if (vif->arp_offload_ip_set || wow->any) + filter |= WOW_FILTER_OPTION_OFFLOAD_ARP; + + if (filter || wow->n_patterns) { + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "Set filter: 0x%x ", filter); + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_WOW_MODE_ENABLE, + filter, + host_req_delay); + } + + set_bit(WLAN_WOW_ENABLE, &vif->flags); + +FAIL: + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "%s: --- return %d\n", __func__, ret); + return ret; +} + +int ath6kl_clear_wow_mode(struct wiphy *wiphy) +{ + struct ath6kl_vif *vif; + struct ath6kl *ar = wiphy_priv(wiphy); + int ret = 0; + + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "%s: +++\n", __func__); + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_WOW_MODE_DISABLE, + 0, + 0); + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); + + clear_bit(WLAN_WOW_ENABLE, &vif->flags); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "%s: ---\n", __func__); + + return ret; +} +#endif /*CONFIG_ANDROID*/ + +static const struct ieee80211_txrx_stypes +ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) +#ifdef CE_SUPPORT + | BIT(IEEE80211_STYPE_PROBE_RESP >> 4) +#endif + }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) +#ifdef CE_SUPPORT + | BIT(IEEE80211_STYPE_PROBE_RESP >> 4) +#endif + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) +#ifdef CE_SUPPORT + | BIT(IEEE80211_STYPE_PROBE_RESP >> 4) +#endif + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) +#ifdef CE_SUPPORT + |BIT(IEEE80211_STYPE_PROBE_RESP >> 4) +#endif + }, +}; + +/* NOTE : this table may be over-wrote by ath6kl_change_cfg80211_ops() call. */ +static struct cfg80211_ops ath6kl_cfg80211_ops = { + .add_virtual_intf = ath6kl_cfg80211_add_iface, + .del_virtual_intf = ath6kl_cfg80211_del_iface, + .change_virtual_intf = ath6kl_cfg80211_change_iface, + .change_bss = ath6kl_cfg80211_change_bss, + .scan = ath6kl_cfg80211_scan, + .connect = ath6kl_cfg80211_connect, + .disconnect = ath6kl_cfg80211_disconnect, + .add_key = ath6kl_cfg80211_add_key, + .get_key = ath6kl_cfg80211_get_key, + .del_key = ath6kl_cfg80211_del_key, + .set_default_key = ath6kl_cfg80211_set_default_key, + .set_wiphy_params = ath6kl_cfg80211_set_wiphy_params, + .set_tx_power = ath6kl_cfg80211_set_txpower, + .get_tx_power = ath6kl_cfg80211_get_txpower, + .set_power_mgmt = ath6kl_cfg80211_set_power_mgmt, + .join_ibss = ath6kl_cfg80211_join_ibss, + .leave_ibss = ath6kl_cfg80211_leave_ibss, + .get_station = ath6kl_get_station, + .dump_station = ath6kl_dump_station, + .set_bitrate_mask = ath6kl_cfg80211_set_bitrate_mask, + .set_pmksa = ath6kl_set_pmksa, + .del_pmksa = ath6kl_del_pmksa, + .flush_pmksa = ath6kl_flush_pmksa, + CFG80211_TESTMODE_CMD(ath6kl_tm_cmd) +#ifdef CONFIG_PM + .suspend = __ath6kl_cfg80211_suspend, + .resume = __ath6kl_cfg80211_resume, +#ifdef NL80211_CMD_GET_WOWLAN_QCA + .set_wow_mode = ath6kl_set_wow_mode, + .clr_wow_mode = ath6kl_clear_wow_mode, +#endif +#endif + .set_rekey_data = ath6kl_set_gtk_rekey_offload, +#ifndef CFG80211_NO_SET_CHAN_OPERATION + .set_channel = ath6kl_set_channel, +#endif +#ifdef NL80211_CMD_START_STOP_AP + .start_ap = ath6kl_start_ap, + .change_beacon = ath6kl_change_beacon, + .stop_ap = ath6kl_del_beacon, +#else + .add_beacon = ath6kl_add_beacon, + .set_beacon = ath6kl_set_beacon, + .del_beacon = ath6kl_del_beacon, +#endif + .del_station = ath6kl_del_station, + .change_station = ath6kl_change_station, + .remain_on_channel = ath6kl_remain_on_channel, + .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, + .mgmt_tx = ath6kl_mgmt_tx, + .mgmt_frame_register = ath6kl_mgmt_frame_register, +#ifdef NL80211_CMD_BTCOEX_QCA + .notify_btcoex = ath6kl_notify_btcoex, +#endif +}; + +void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) +{ + vif->saved_pwr_mode = vif->last_pwr_mode; + + /* put all interfaces to power save */ + if (ath6kl_wmi_powermode_cmd(vif->ar->wmi, + vif->fw_vif_idx, REC_POWER) != 0) { + ath6kl_err("wmi powermode command failed during suspend\n"); + } + + switch (vif->sme_state) { + case SME_CONNECTING: + ath6kl_cfg80211_connect_result(vif, vif->bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + break; + case SME_CONNECTED: + default: + /* + * FIXME: oddly enough smeState is in DISCONNECTED during + * suspend, why? Need to send disconnected event in that + * state. + */ + ath6kl_cfg80211_disconnected(vif, 0, NULL, 0, GFP_KERNEL); + break; + } + + if (test_bit(CONNECTED, &vif->flags) || + test_bit(CONNECT_PEND, &vif->flags)) + ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx); + + vif->sme_state = SME_DISCONNECTED; + clear_bit(CONNECTED, &vif->flags); + clear_bit(CONNECT_PEND, &vif->flags); + clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags); + clear_bit(PS_STICK, &vif->flags); + del_timer(&vif->shprotect_timer); + + /* disable scanning */ + if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, + 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0) + printk(KERN_WARNING "ath6kl: failed to disable scan " + "during suspend\n"); + + ath6kl_cfg80211_scan_complete_event(vif, true); + + return; +} + +void ath6kl_cfg80211_stop_all(struct ath6kl *ar) +{ + struct ath6kl_vif *vif, *tmp_vif; + + list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) + ath6kl_cfg80211_stop(vif); +} + +static void ath6kl_change_cfg80211_ops(struct cfg80211_ops *cfg80211_ops) +{ + /* Offload AP keep-alive function to user. */ + if (debug_quirks & ATH6KL_MODULE_KEEPALIVE_BY_SUPP) { + WARN_ON(cfg80211_ops->probe_client); + + cfg80211_ops->probe_client = ath6kl_ap_probe_client; + + ath6kl_info("Offload AP Keep-alive to supplicant/hostapd\n"); + } + + /* Support Scheduled-Scan (a.k.a. PNO) */ + if (debug_quirks & ATH6KL_MODULE_ENABLE_SCHE_SCAN) { + cfg80211_ops->sched_scan_start = ath6kl_sched_scan_start; + cfg80211_ops->sched_scan_stop = ath6kl_sched_scan_stop; + } + + return; +} + +static void ath6kl_reset_cfg80211_ops(struct cfg80211_ops *cfg80211_ops) +{ + cfg80211_ops->probe_client = NULL; + + cfg80211_ops->sched_scan_start = NULL; + cfg80211_ops->sched_scan_stop = NULL; + + return; +} + +static void _judge_p2p_framework(struct ath6kl *ar, unsigned int p2p_config) +{ + ar->p2p = !!p2p_config; + + ar->p2p_concurrent = + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_ENABLE_DEDICATE) || + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_NO_DEDICATE) || + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_MULTICHAN) || + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_COMPAT); + + ar->p2p_dedicate = + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_ENABLE_DEDICATE) || + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_COMPAT); + + ar->p2p_multichan_concurrent = + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_MULTICHAN); + + ar->p2p_compat = + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_COMPAT); + + ar->p2p_concurrent_ap = + !!(p2p_config & ATH6KL_MODULEP2P_CONCURRENT_AP); + + ar->p2p_in_pasv_chan = + !!(p2p_config & ATH6KL_MODULEP2P_P2P_IN_PASSIVE_CHAN); + + ar->p2p_wise_scan = + !!(p2p_config & ATH6KL_MODULEP2P_P2P_WISE_SCAN); + + WARN_ON((!ar->p2p_concurrent) && (ar->p2p_multichan_concurrent)); + WARN_ON((ar->p2p_concurrent_ap) && + ((!ar->p2p_concurrent) || (!ar->p2p_dedicate))); + WARN_ON((!ar->p2p) && (ar->p2p_wise_scan)); + + if (ar->p2p_concurrent) { + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_P2P_MAX_FW_VIF)) + ar->vif_max = TARGET_VIF_MAX; /* TODO */ + else { + if (ar->p2p_dedicate) + ar->vif_max = 3; + else + ar->vif_max = 2; /*currently we only support 2*/ + + } + } else + ar->vif_max = 1; + + /* + * P2P-Concurrent w/ softAP: + * STA + AP : vif_max == 3 && ar->p2p_concurrent_ap + * STA + P2P + AP : vif_max >= 4 && ar->p2p_concurrent_ap (not yet) + * w/ MCC : (not yet) + */ + WARN_ON((ar->p2p_concurrent_ap) && (ar->vif_max >= TARGET_VIF_MAX)); + WARN_ON((ar->p2p_concurrent_ap) && (ar->p2p_multichan_concurrent)); + + ar->max_norm_iface = 1; + if (ar->p2p_concurrent_ap) + ar->max_norm_iface++; + + ar->p2p_frame_retry = false; + + if (ar->p2p_compat) + ar->p2p_frame_not_report = true; + else + ar->p2p_frame_not_report = false; + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_WAR_BAD_P2P_GO)) { + ar->p2p_war_bad_intel_go = true; + ar->p2p_war_bad_broadcom_go = true; + ar->p2p_war_p2p_client_awake = true; + } else { + ar->p2p_war_bad_intel_go = false; + ar->p2p_war_bad_broadcom_go = false; + ar->p2p_war_p2p_client_awake = false; + } + + ar->p2p_ie_not_append = 0; + + ath6kl_info("%dVAP/%d, P2P %s, concurrent %s %s," + " %s dedicate p2p-device," + " multi-channel-concurrent %s, p2p-compat %s%s%s\n", + ar->vif_max, + ar->max_norm_iface, + (ar->p2p ? "enable" : "disable"), + (ar->p2p_concurrent ? "on" : "off"), + (ar->p2p_concurrent_ap ? "with softAP" : ""), + (ar->p2p_dedicate ? "with" : "without"), + (ar->p2p_multichan_concurrent ? "enable" : "disable"), + (ar->p2p_compat ? "enable (ignore p2p_dedicate)" : "disable"), + (ar->p2p_in_pasv_chan ? ", p2p_in_pasv_chan enable" : ""), + (ar->p2p_wise_scan ? ", p2p_wise_scan enable" : "")); + + return; +} + +static void _judge_vap_framework(struct ath6kl *ar, unsigned int vap_config) +{ + int i; + int ap_num = 0, sta_num = 0; + + /* + * TODO : To replace older ath6kl_p2p, in case of vap_config have + * P2P related VAP mode and transfer vap_config to XXX here + * then feed to _judge_p2p_framework() to configure P2P's + * framework. + */ + + for (i = 0; i < ATH6KL_VIF_MAX; i++) { + ar->next_mode[i] = (vap_config >> + (i * ATH6KL_VAPMODE_OFFSET)) & + ATH6KL_VAPMODE_MASK; + + if (ar->next_mode[i] == ATH6KL_VAPMODE_DISABLED) + break; + + if (ar->next_mode[i] == ATH6KL_VAPMODE_STA) + sta_num++; + else if (ar->next_mode[i] == ATH6KL_VAPMODE_AP) + ap_num++; + } + + /* Now, only support 1STA+1AP or 2AP */ + WARN_ON((sta_num > 1) || + (ap_num > 2) || + (sta_num + ap_num > 2)); + + ar->vif_max = i; + ar->max_norm_iface = i; + + ath6kl_info("%dVAP/%d, NO P2P, vapmode %d, %d, %d, %d\n", + ar->vif_max, + ar->max_norm_iface, + ar->next_mode[0], ar->next_mode[1], + ar->next_mode[2], ar->next_mode[3]); + +} + +struct ath6kl *ath6kl_core_alloc(struct device *dev) +{ + struct ath6kl *ar; + struct wiphy *wiphy; + + /* + * Overwrite cfg80211_ops function table if we need to + * enable/disable some features. + */ + ath6kl_change_cfg80211_ops(&ath6kl_cfg80211_ops); + + /* create a new wiphy for use with cfg80211 */ + wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl)); + + if (!wiphy) { + ath6kl_err("couldn't allocate wiphy device\n"); + return NULL; + } + + ar = wiphy_priv(wiphy); + ar->mod_debug_quirks = debug_quirks; + + ar->wiphy = wiphy; + ar->dev = dev; + + BUG_ON((ath6kl_p2p && ath6kl_vap)); + + /* + * For backward compatible, keep ATH6KL_MODULE_TESTMODE_ENABLE as + * testmode = 1. Note that if want to configure as testmode=2, + * Must not configure ATH6KL_MODULE_TESTMODE_ENABLE, + * And configure testmode = 2 as module parameter directly. + */ + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_TESTMODE_ENABLE)) + testmode = 1; + + if (testmode || + ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_ENABLE_EPPING)) { + ath6kl_p2p = 0; + ath6kl_vap = 0; + ath6kl_roam_mode = ATH6KL_MODULEROAM_DISABLE; + } + + if (ath6kl_p2p) + _judge_p2p_framework(ar, ath6kl_p2p); + else if (ath6kl_vap) + _judge_vap_framework(ar, ath6kl_vap); + else { + ar->vif_max = 1; + ar->max_norm_iface = 1; + } + + spin_lock_init(&ar->lock); + spin_lock_init(&ar->list_lock); + spin_lock_init(&ar->state_lock); + + init_waitqueue_head(&ar->event_wq); + sema_init(&ar->sem, 1); + sema_init(&ar->wmi_evt_sem, 1); + + INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue); + INIT_LIST_HEAD(&ar->vif_list); + + clear_bit(WMI_ENABLED, &ar->flag); + clear_bit(SKIP_SCAN, &ar->flag); + clear_bit(DESTROY_IN_PROGRESS, &ar->flag); + + ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL; + ar->listen_intvl_b = 0; + ar->tx_pwr = 0; + + ar->low_rssi_roam_params.lrssi_scan_period = WMI_ROAM_LRSSI_SCAN_PERIOD; + ar->low_rssi_roam_params.lrssi_scan_threshold = \ + WMI_ROAM_LRSSI_SCAN_THRESHOLD; + ar->low_rssi_roam_params.lrssi_roam_threshold = \ + WMI_ROAM_LRSSI_ROAM_THRESHOLD; + ar->low_rssi_roam_params.roam_rssi_floor = WMI_ROAM_LRSSI_ROAM_FLOOR; + + ar->state = ATH6KL_STATE_OFF; + + if ((ar->p2p_concurrent) && + (ar->p2p_dedicate)) + ar->sche_scan = ath6kl_mod_debug_quirks(ar, + ATH6KL_MODULE_ENABLE_SCHE_SCAN); + + ar->roam_mode = ath6kl_roam_mode; + + if (ar->sche_scan && + ar->roam_mode != ATH6KL_MODULEROAM_DISABLE) { + ath6kl_err("Roam shall be exclusive with schedule scan. Disabled\n"); + ar->roam_mode = ATH6KL_MODULEROAM_DISABLE; + } + + return ar; +} + +int ath6kl_register_ieee80211_hw(struct ath6kl *ar) +{ + struct wiphy *wiphy = ar->wiphy; + int ret; + + wiphy->mgmt_stypes = ath6kl_mgmt_stypes; + + wiphy->max_remain_on_channel_duration = ATH6KL_ROC_MAX_PERIOD * 1000; + + /* set device pointer for wiphy */ + set_wiphy_dev(wiphy, ar->dev); + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + if (ar->p2p) { + if (!IS_STA_AP_ONLY(ar)) + wiphy->interface_modes |= + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT); + } + + if (ar->p2p_concurrent) { + struct ieee80211_iface_combination + *ieee80211_iface_combination = NULL; + + switch (ar->vif_max) { + case 4: + ieee80211_iface_combination = + ath6kl_iface_combinations_p2p_concurrent4; + wiphy->n_iface_combinations = ARRAY_SIZE( + ath6kl_iface_combinations_p2p_concurrent4); + break; + case 3: + ieee80211_iface_combination = + ath6kl_iface_combinations_p2p_concurrent3; + wiphy->n_iface_combinations = ARRAY_SIZE( + ath6kl_iface_combinations_p2p_concurrent3); + break; + case 2: + ieee80211_iface_combination = + ath6kl_iface_combinations_p2p_concurrent2; + wiphy->n_iface_combinations = ARRAY_SIZE( + ath6kl_iface_combinations_p2p_concurrent2); + break; + default: + WARN_ON(1); + } + + /* Overwrite it if P2P-Concurrent w/ softAP mode. */ + if (ar->p2p_concurrent_ap) { + if (IS_STA_AP_ONLY(ar)) { + ieee80211_iface_combination = + ath6kl_iface_combinations_sta_ap; + wiphy->n_iface_combinations = ARRAY_SIZE( + ath6kl_iface_combinations_sta_ap); + } else { + ieee80211_iface_combination = + ath6kl_iface_combinations_p2p_concurrent4_1; + wiphy->n_iface_combinations = ARRAY_SIZE( + ath6kl_iface_combinations_p2p_concurrent4_1); + } + } + + /* Update max. channel support */ + if (ar->p2p_multichan_concurrent) { + ieee80211_iface_combination[0].num_different_channels = + ieee80211_iface_combination[0].max_interfaces; + } + + wiphy->iface_combinations = ieee80211_iface_combination; + + if (ar->p2p_compat) { + wiphy->n_iface_combinations = 0; + wiphy->iface_combinations = NULL; + } + } + + /* update MCS rate mask & TX STBC. */ + if (!(ar->target_subtype & TARGET_SUBTYPE_2SS)) { + ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0; + ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0; + + ath6kl_band_2ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC; + ath6kl_band_5ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC; + } + + /* update 2G-HT40 capability. */ + if (!(ar->target_subtype & TARGET_SUBTYPE_HT40)) { + ath6kl_band_2ghz.ht_cap.cap &= + ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ath6kl_band_2ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; + ath6kl_band_2ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_DSSSCCK40; + } + + /* max num of ssids that can be probed during scanning */ + wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; + wiphy->max_scan_ie_len = MAX_APP_IE_LEN; + if (ar->sche_scan) { + wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX; + wiphy->max_sched_scan_ie_len = MAX_APP_IE_LEN; + } + + wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; + if ((!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_5G)) && + (ar->target_subtype & TARGET_SUBTYPE_DUAL)) + wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; + else { + ath6kl_info("Disable 5G support by %s!\n", + (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_5G) ? + "driver" : "board-data")); + } + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->cipher_suites = cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + wiphy->flags |= WIPHY_FLAG_AP_UAPSD; +#ifdef CFG80211_WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL + if ((ath6kl_cfg80211_ops.remain_on_channel) && + (ath6kl_cfg80211_ops.cancel_remain_on_channel)) + wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; +#endif + + if (ar->roam_mode != ATH6KL_MODULEROAM_DISABLE) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + +#ifdef NL80211_ATTR_AP_INACTIVITY_TIMEOUT + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_KEEPALIVE_CONFIG_BY_SUPP)) + wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; +#endif + +#ifdef NL80211_WIPHY_FEATURE_SCAN_FLUSH + /* + * The cfg80211 support it by default but we disabled here. + * To speed up the scan time and the channel dewell time is not really + * longer enough. Cache BSS information may be helpful to ath6kl. + */ + wiphy->features &= ~NL80211_FEATURE_SCAN_FLUSH; +#endif + +#ifdef CFG80211_TX_POWER_PER_WDEV + wiphy->features |= NL80211_FEATURE_VIF_TXPOWER; +#endif + + + 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; + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "wow attr: flags: %x, n_patterns: %d, ", + wiphy->wowlan.flags, + wiphy->wowlan.n_patterns); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "min_len: %d, max_len: %d\n", + wiphy->wowlan.pattern_min_len, + wiphy->wowlan.pattern_max_len); + +#ifdef CONFIG_ANDROID +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&ar->wake_lock, WAKE_LOCK_SUSPEND, "ath6kl_suspend_wl"); +#endif + + if (ar->wow_irq) { + int ret; + ret = request_irq(ar->wow_irq, ath6kl_wow_irq, + IRQF_SHARED | IRQF_TRIGGER_RISING, + "ar6000" "sdiowakeup", ar); + if (!ret) { + ret = enable_irq_wake(ar->wow_irq); + if (ret < 0) { + ath6kl_err("Couldn't enable WoW IRQ as wakeup interrupt"); + return ret; + } + printk("ath6kl: WoW IRQ %d\n", ar->wow_irq); + } + } + + ath6kl_setup_android_resource(ar); +#endif + + if (test_bit(INTERNAL_REGDB, &ar->flag)) { + wiphy->reg_notifier = ath6kl_reg_notifier; + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; + wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS; + } + + ret = wiphy_register(wiphy); + if (ret < 0) { + ath6kl_err("couldn't register wiphy device\n"); + return ret; + } + + return 0; +} + +static int ath6kl_init_if_data(struct ath6kl_vif *vif) +{ + int i; + struct ath6kl *ar = vif->ar; + + vif->aggr_cntxt = aggr_init(vif); + if (!vif->aggr_cntxt) { + ath6kl_err("failed to initialize aggr\n"); + return -ENOMEM; + } + + for (i = 0; i < AP_MAX_NUM_STA; i++) { + vif->sta_list[i].aggr_conn_cntxt = aggr_init_conn(vif); + if (!vif->sta_list[i].aggr_conn_cntxt) { + ath6kl_err("failed to initialize aggr_node\n"); + return -ENOMEM; + } + } +#ifdef ACS_SUPPORT + vif->acs_ctx = ath6kl_acs_init(vif); + if (!vif->acs_ctx) { + ath6kl_err("failed to initialize acs"); + return -ENOMEM; + } +#endif + vif->htcoex_ctx = ath6kl_htcoex_init(vif); + if (!vif->htcoex_ctx) { + ath6kl_err("failed to initialize htcoex\n"); + return -ENOMEM; + } + +#ifdef CONFIG_ANDROID + /*Enable htcoex for wlan0 in Android. Scan period is 60s*/ + if ((vif->fw_vif_idx == 0) && + (vif->ar->target_subtype & TARGET_SUBTYPE_HT40)) + ath6kl_htcoex_config(vif, + ATH6KL_HTCOEX_SCAN_PERIOD, 0); +#endif + + vif->p2p_ps_info_ctx = ath6kl_p2p_ps_init(vif); + if (!vif->p2p_ps_info_ctx) { + ath6kl_err("failed to initialize p2p_ps\n"); + return -ENOMEM; + } + + if (!test_bit(TESTMODE_EPPING, &ar->flag)) { + if (ath6kl_mod_debug_quirks(vif->ar, + ATH6KL_MODULE_KEEPALIVE_BY_SUPP)) { + vif->ap_keepalive_ctx = + ath6kl_ap_keepalive_init(vif, + AP_KA_MODE_BYSUPP); + } else if (ath6kl_mod_debug_quirks(vif->ar, + ATH6KL_MODULE_KEEPALIVE_CONFIG_BY_SUPP)) { + vif->ap_keepalive_ctx = + ath6kl_ap_keepalive_init(vif, + AP_KA_MODE_CONFIG_BYSUPP); + } else { + vif->ap_keepalive_ctx = + ath6kl_ap_keepalive_init(vif, + AP_KA_MODE_ENABLE); + } + + if (!vif->ap_keepalive_ctx) { + ath6kl_err("failed to initialize ap_keepalive\n"); + return -ENOMEM; + } + } + + vif->ap_acl_ctx = ath6kl_ap_acl_init(vif); + if (!vif->ap_acl_ctx) { + ath6kl_err("failed to initialize ap_acl\n"); + return -ENOMEM; + } + + vif->ap_admc_ctx = ath6kl_ap_admc_init(vif, AP_ADMC_MODE_ACCEPT_ALWAYS); + if (!vif->ap_admc_ctx) { + ath6kl_err("failed to initialize ap_admc\n"); + return -ENOMEM; + } + + setup_timer(&vif->disconnect_timer, disconnect_timer_handler, + (unsigned long) vif->ndev); + set_bit(WMM_ENABLED, &vif->flags); + spin_lock_init(&vif->if_lock); + + setup_timer(&vif->vifscan_timer, ath6kl_scan_timer_handler, + (unsigned long) vif); + + setup_timer(&vif->shprotect_timer, ath6kl_shprotect_timer_handler, + (unsigned long) vif); + spin_lock_init(&vif->pend_skb_lock); + + vif->scan_req = NULL; + vif->pend_skb = NULL; + + vif->scanband_chan = 0; + if (ar->p2p_wise_scan && + (vif->fw_vif_idx != 0)) { + /* Only apply to P2P related interfaces. */ + vif->scanband_type = SCANBAND_TYPE_P2PCHAN; + } else + vif->scanband_type = SCANBAND_TYPE_ALL; + + vif->last_pwr_mode = REC_POWER; + + return 0; +} + +void ath6kl_deinit_if_data(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + int ctr; + +#ifdef ATH6KL_DIAGNOSTIC + wifi_diag_timer_destroy(vif); +#endif + + for (ctr = 0; ctr < AP_MAX_NUM_STA ; ctr++) { + if (!ath6kl_ps_queue_empty(&vif->sta_list[ctr].psq_data)) + ath6kl_ps_queue_purge(&vif->sta_list[ctr].psq_data); + + if (!ath6kl_ps_queue_empty(&vif->sta_list[ctr].psq_mgmt)) + ath6kl_ps_queue_purge(&vif->sta_list[ctr].psq_mgmt); + + aggr_module_destroy_conn(vif->sta_list[ctr].aggr_conn_cntxt); + } + + netif_carrier_off(vif->ndev); + netif_stop_queue(vif->ndev); + ath6kl_tx_data_cleanup_by_if(vif); + + aggr_module_destroy(vif->aggr_cntxt); + + ath6kl_htcoex_deinit(vif); + + ath6kl_p2p_ps_deinit(vif); + + ath6kl_ap_keepalive_deinit(vif); + + ath6kl_ap_acl_deinit(vif); + + ath6kl_ap_admc_deinit(vif); + + ath6kl_sched_scan_deinit(vif); + + ar->avail_idx_map |= BIT(vif->fw_vif_idx); + + if (vif->nw_type == ADHOC_NETWORK) + ar->ibss_if_active = false; + + del_timer(&vif->vifscan_timer); + + unregister_netdevice(vif->ndev); + + ar->num_vif--; +} + +struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, + enum nl80211_iftype type, u8 fw_vif_idx, + u8 nw_type) +{ + struct net_device *ndev; + struct ath6kl_vif *vif; + int i; + + ndev = alloc_netdev(sizeof(*vif), name, ether_setup); + if (!ndev) + return NULL; + + vif = netdev_priv(ndev); + ndev->ieee80211_ptr = &vif->wdev; + vif->wdev.wiphy = ar->wiphy; + vif->ar = ar; + vif->ndev = ndev; + SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy)); + vif->wdev.netdev = ndev; + vif->wdev.iftype = type; + vif->fw_vif_idx = fw_vif_idx; + vif->nw_type = vif->next_mode = nw_type; + + memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); + if (fw_vif_idx != 0) + ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) | + 0x2; + + init_netdev(ndev); + + ath6kl_init_control_info(vif); + + /* ATH6KL_MODULE_ENABLE_EPPING is enable will skip + * the following procedure */ + if (!ath6kl_mod_debug_quirks(vif->ar, ATH6KL_MODULE_ENABLE_EPPING)) { + /* TODO: Pass interface specific pointer instead of ar */ + if (ath6kl_init_if_data(vif)) + goto err; + } + + if (register_netdevice(ndev)) + goto err; + + ar->avail_idx_map &= ~BIT(fw_vif_idx); + vif->sme_state = SME_DISCONNECTED; + set_bit(WLAN_ENABLED, &vif->flags); +pr_err("%s %d enabling wlan\n",__func__,__LINE__); + ar->wlan_pwr_state = WLAN_POWER_STATE_ON; + set_bit(NETDEV_REGISTERED, &vif->flags); + + if (type == NL80211_IFTYPE_ADHOC) + ar->ibss_if_active = true; + + spin_lock_bh(&ar->list_lock); + list_add_tail(&vif->list, &ar->vif_list); + spin_unlock_bh(&ar->list_lock); + + ath6kl_p2p_utils_init_port(vif, type); + + ath6kl_sched_scan_init(vif); + + return ndev; + +err: + for (i = 0; i < AP_MAX_NUM_STA ; i++) + aggr_module_destroy_conn(vif->sta_list[i].aggr_conn_cntxt); + aggr_module_destroy(vif->aggr_cntxt); + ath6kl_htcoex_deinit(vif); + ath6kl_p2p_ps_deinit(vif); + ath6kl_ap_keepalive_deinit(vif); + ath6kl_ap_acl_deinit(vif); + ath6kl_ap_admc_deinit(vif); + free_netdev(ndev); + return NULL; +} + +void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar) +{ +#ifdef CONFIG_ANDROID + if (ar->wow_irq) { + if (disable_irq_wake(ar->wow_irq)) + ath6kl_err("Couldn't disable hostwake IRQ wakeup mode\n"); + + free_irq(ar->wow_irq, ar); + ar->wow_irq = 0; + } + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&ar->wake_lock); +#endif + + ath6kl_cleanup_android_resource(ar); +#endif + wiphy_unregister(ar->wiphy); + wiphy_free(ar->wiphy); + + ath6kl_reset_cfg80211_ops(&ath6kl_cfg80211_ops); +} + +void ath6kl_core_init_defer(struct work_struct *wk) +{ +#define MAX_RD_WAIT_CNT (20) /* 20 * 100 = 2 sec. */ + struct ath6kl *ar; + struct vif_params params; + int i; + + ar = container_of(wk, struct ath6kl, + init_defer_wk); + + /* Automatically create virtual interface. */ + if (!ath6kl_mod_debug_quirks(ar, + ATH6KL_MODULE_DISABLE_AUTO_ADD_INF) && + (ar->p2p_concurrent) && + (ar->p2p_dedicate)) { + if (!IS_STA_AP_ONLY(ar)) { + ath6kl_info("Create dedicated p2p interface\n"); + + rtnl_lock(); + params.use_4addr = 0; + if (ath6kl_cfg80211_add_iface(ar->wiphy, + ATH6KL_DEVNAME_DEF_P2P, + NL80211_IFTYPE_STATION, + NULL, + ¶ms) == NULL) { + ath6kl_err("Create dedicated p2p interface fail!\n"); + } + rtnl_unlock(); + } + + if (ar->p2p_concurrent_ap) { + ath6kl_info("Create concurrent ap interface\n"); + + rtnl_lock(); + params.use_4addr = 0; + if (ath6kl_cfg80211_add_iface(ar->wiphy, + ATH6KL_DEVNAME_DEF_AP, + NL80211_IFTYPE_AP, + NULL, + ¶ms) == NULL) { + ath6kl_err("Create concurrent ap interface fail!\n"); + } + rtnl_unlock(); + } + } + + /* Automatically create virtual interface. */ + if (!ath6kl_mod_debug_quirks(ar, + ATH6KL_MODULE_DISABLE_AUTO_ADD_INF) && + (!ar->p2p)) { + for (i = 1; i < ATH6KL_VIF_MAX; i++) { + rtnl_lock(); + params.use_4addr = 0; + if (ar->next_mode[i] == ATH6KL_VAPMODE_STA) { + if (ath6kl_cfg80211_add_iface( + ar->wiphy, + ATH6KL_DEVNAME_DEF_STA, + NL80211_IFTYPE_STATION, + NULL, + ¶ms) == NULL) { + ath6kl_err("Create sta interface fail!\n"); + } + } else if (ar->next_mode[i] == ATH6KL_VAPMODE_AP) { + if (ath6kl_cfg80211_add_iface( + ar->wiphy, + ATH6KL_DEVNAME_DEF_AP, + NL80211_IFTYPE_AP, + NULL, + ¶ms) == NULL) { + ath6kl_err("Create ap interface fail!\n"); + } + } + rtnl_unlock(); + } + } + + /* Wait target report WMI_REGDOMAIN_EVENTID done */ + for (i = 0; i < MAX_RD_WAIT_CNT; i++) { + if (ath6kl_reg_is_init_done(ar)) { + /* wait more 2 jiffies for regdb updated */ + schedule_timeout_interruptible(2); + break; + } + + schedule_timeout_interruptible(msecs_to_jiffies(100)); + } + + clear_bit(INIT_DEFER_PROGRESS, &ar->flag); + if (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_WAIT_DEFER)) + wake_up(&ar->init_defer_wait_wq); + + return; +#undef MAX_RD_WAIT_CNT +} + +/* we use this flag to protect 4 way handshake */ +void ath6kl_shprotect_timer_handler(unsigned long ptr) +{ + struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr; + struct ath6kl *ar = vif->ar; + u8 bssid[ETH_ALEN]; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s\n", __func__); + clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags); + + if (vif->pend_skb) { + ath6kl_err("%s, shall not have pend skb\n", __func__); + flush_delayed_work(&vif->work_eapol_send); + } + + if (ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) { + /*trigger roam, only work if firmware support*/ + memset(bssid, 0, ETH_ALEN); + bssid[0] = 0xFF; + ath6kl_wmi_force_roam_cmd(ar->wmi, (const u8 *)bssid); + } +} + + +#if defined(CONFIG_ANDROID) || defined(USB_AUTO_SUSPEND) +int ath6kl_android_enable_wow_default(struct ath6kl *ar) +{ + unsigned char mask = 0x3F; + int mask_len; + int rv = 0, i; + struct cfg80211_wowlan wow; + + if (test_bit(TESTMODE_EPPING, &ar->flag)) + return 0; + + memset(&wow, 0, sizeof(wow)); + + /*default filters : ANY + all self MACs*/ + wow.any = true; + wow.patterns = kcalloc(ar->vif_max, + sizeof(wow.patterns[0]), GFP_KERNEL); + if (!wow.patterns) + return -ENOMEM; + + mask_len = DIV_ROUND_UP(ETH_ALEN, 8); + for (i = 0; i < ar->vif_max; i++) { + + wow.patterns[i].mask = kmalloc(mask_len + ETH_ALEN, GFP_KERNEL); + if (!wow.patterns[i].mask) { + rv = -ENOMEM; + goto failed; + } + wow.patterns[i].pattern = wow.patterns[i].mask + mask_len; + memcpy(wow.patterns[i].mask, &mask, mask_len); + wow.patterns[i].pattern_len = ETH_ALEN; + memcpy(wow.patterns[i].pattern, ar->mac_addr, ETH_ALEN); + if (i != 0) + wow.patterns[i].pattern[0] = + (wow.patterns[i].pattern[0] ^ (1 << i)) | 0x2; + + wow.n_patterns++; + + ath6kl_dbg(ATH6KL_DBG_WOWLAN, + "Add wow pattern:%x:%x:%x:%x:%x:%x\n", + wow.patterns[i].pattern[0], wow.patterns[i].pattern[1], + wow.patterns[i].pattern[2], wow.patterns[i].pattern[3], + wow.patterns[i].pattern[4], wow.patterns[i].pattern[5]); + + } + + /*Set wow mode to target firmware*/ + rv = ath6kl_set_wow_mode(ar->wiphy, &wow); +failed: + for (i = 0; i < ar->vif_max; i++) + kfree(wow.patterns[i].mask); + + kfree(wow.patterns); + + return rv; +} + +bool ath6kl_android_need_wow_suspend(struct ath6kl *ar) +{ + struct ath6kl_vif *vif = NULL; + int i; + bool isConnected = false; + + vif = ath6kl_vif_first(ar); + if (!vif) + return false; + + if (!test_bit(WLAN_WOW_ENABLE, &vif->flags)) + return false; + +#ifdef ATH6KL_SUPPORT_WIFI_DISC + /* Always allow WoW suspend in discovery mode */ + if (ar->disc_active) + return true; +#endif + + /*If p2p-GO or softAP interface is connected, we don't do Wow suspend. + *Otherwise, if one of the interfaces is connected, we do WoW + *to save power. + */ + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) { + /*if p2p-GO or softAP is connect, don't do wow*/ + if (test_bit(CONNECTED, &vif->flags) && + (vif->nw_type == AP_NETWORK)) + return false; + else if (test_bit(CONNECTED, &vif->flags)) + isConnected = true; + } + } + + return isConnected; +} +#endif + +#ifdef ATH6KL_SUPPORT_WLAN_HB +int ath6kl_enable_wow_hb(struct ath6kl *ar) +{ + u32 filter = 0; + int i, ret; + struct ath6kl_vif *vif; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + /* Clear existing WOW patterns */ + for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++) + ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, + WOW_LIST_ID, i); + + filter |= WOW_FILTER_OPTION_MAGIC_PACKET | + WOW_FILTER_OPTION_NWK_DISASSOC; + + /*Do GTK offload in WPA/WPA2 auth mode connection.*/ + if (vif->auth_mode == WPA2_AUTH_CCKM || + vif->auth_mode == WPA2_PSK_AUTH || + vif->auth_mode == WPA_AUTH_CCKM || + vif->auth_mode == WPA_PSK_AUTH) { + filter |= WOW_FILTER_OPTION_OFFLOAD_GTK; + } + + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_WOW_MODE_ENABLE, + filter, + WOW_HOST_REQ_DELAY); + + return ret; +} +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/cfg80211.h b/drivers/net/wireless/ath/ath6kl-3.5/cfg80211.h new file mode 100644 index 000000000000..ef412891875a --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/cfg80211.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 ATH6KL_CFG80211_H +#define ATH6KL_CFG80211_H + +enum ath6kl_cfg_suspend_mode { + ATH6KL_CFG_SUSPEND_DEEPSLEEP, + ATH6KL_CFG_SUSPEND_CUTPOWER, + ATH6KL_CFG_SUSPEND_WOW +}; + +extern unsigned int testmode; +extern unsigned int ath6kl_p2p; +extern unsigned int ath6kl_vap; +extern unsigned int ath6kl_wow_ext; +extern unsigned int ath6kl_scan_timeout; +extern unsigned int ath6kl_roam_mode; + +struct ath6kl_beacon_parameters { + /* Settings */ + int beacon_interval; + int dtim_period; + const u8 *ssid; + size_t ssid_len; + enum nl80211_hidden_ssid hidden_ssid; + struct cfg80211_crypto_settings crypto; + bool privacy; + enum nl80211_auth_type auth_type; + int inactivity_timeout; + struct ieee80211_channel *channel; /* After kernel 3.6 */ + enum nl80211_channel_type channel_type; /* After kernel 3.6 */ + u8 p2p_ctwindow; /* After kernel 3.8 */ + bool p2p_opp_ps; /* After kernel 3.8 */ + + /* IEs */ + const u8 *head, *tail; + int head_len, tail_len; + const u8 *beacon_ies; + size_t beacon_ies_len; + const u8 *proberesp_ies; + size_t proberesp_ies_len; + const u8 *assocresp_ies; + size_t assocresp_ies_len; + const u8 *probe_resp; + int probe_resp_len; +}; + +struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, + enum nl80211_iftype type, + u8 fw_vif_idx, u8 nw_type); +int ath6kl_register_ieee80211_hw(struct ath6kl *ar); +struct ath6kl *ath6kl_core_alloc(struct device *dev); +void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar); + +void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); + +void ath6kl_cfg80211_connect_result(struct ath6kl_vif *vif, + const u8 *bssid, + const u8 *req_ie, + size_t req_ie_len, + const u8 *resp_ie, + size_t resp_ie_len, + u16 status, + gfp_t gfp); + +void ath6kl_cfg80211_disconnected(struct ath6kl_vif *vif, + u16 reason, + u8 *ie, + size_t ie_len, + gfp_t gfp); + +void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, + u8 *bssid, u16 listen_intvl, + u16 beacon_intvl, + enum network_type nw_type, + u8 beacon_ie_len, u8 assoc_req_len, + u8 assoc_resp_len, u8 *assoc_info); + +void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, + u8 *bssid, u8 assoc_resp_len, + u8 *assoc_info, u16 proto_reason); + +void ath6kl_cfg80211_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, + bool ismcast); + +int ath6kl_cfg80211_suspend(struct ath6kl *ar, + enum ath6kl_cfg_suspend_mode mode, + struct cfg80211_wowlan *wow); + +int ath6kl_cfg80211_resume(struct ath6kl *ar); + +void ath6kl_cfg80211_stop(struct ath6kl_vif *vif); +void ath6kl_cfg80211_stop_all(struct ath6kl *ar); + +#if defined(CONFIG_ANDROID) || defined(USB_AUTO_SUSPEND) +int ath6kl_set_wow_mode(struct wiphy *wiphy, struct cfg80211_wowlan *wow); +int ath6kl_clear_wow_mode(struct wiphy *wiphy); +#endif /* defined(CONFIG_ANDROID) || defined(USB_AUTO_SUSPEND) */ + +bool ath6kl_sched_scan_trigger(struct ath6kl_vif *vif); + +void ath6kl_scan_timer_handler(unsigned long ptr); + +void ath6kl_shprotect_timer_handler(unsigned long ptr); + +void ath6kl_judge_roam_parameter(struct ath6kl_vif *vif, + bool call_on_disconnect); + +void ath6kl_switch_parameter_based_on_connection( + struct ath6kl_vif *vif, + bool call_on_disconnect); + +#if defined(USB_AUTO_SUSPEND) +void ath6kl_auto_pm_wakeup_resume(struct ath6kl *wk); +#endif +#endif /* ATH6KL_CFG80211_H */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.c b/drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.c new file mode 100644 index 000000000000..f54bbd0fb7d4 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 + +#ifdef CONFIG_HAS_WAKELOCK +#include +#endif + +#include "core.h" +#include "cfg80211.h" +#include "debug.h" +#include "hif-ops.h" +#include "testmode.h" +#include "cfg80211_btcoex.h" + +bool __ath6kl_btcoex_cfg80211_ready(struct ath6kl *ar) +{ + if (!test_bit(WMI_READY, &ar->flag)) { + ath6kl_err("wmi is not ready\n"); + return false; + } + + return true; +} + +bool ath6kl_btcoex_cfg80211_ready(struct ath6kl_vif *vif) +{ + if (!__ath6kl_btcoex_cfg80211_ready(vif->ar)) + return false; + + if (!test_bit(WLAN_ENABLED, &vif->flags)) { + ath6kl_err("wlan disabled\n"); + return false; + } + + return true; +} + +#ifdef NL80211_CMD_BTCOEX_QCA +int ath6kl_notify_btcoex(struct wiphy *wiphy, u8 *buf, + int len) +{ + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct ath6kl_vif *vif; + int ret; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "BT coex wmi command:%p\n", buf); + + if (!ath6kl_btcoex_cfg80211_ready(vif)) + return -EIO; + + if (down_interruptible(&ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + ret = ath6kl_wmi_send_btcoex_cmd(ar, buf, len); + + up(&ar->sem); + + return ret; +} +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.h b/drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.h new file mode 100644 index 000000000000..9610e882f005 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/cfg80211_btcoex.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +int ath6kl_notify_btcoex(struct wiphy *wiphy, u8 *buf, + int len); diff --git a/drivers/net/wireless/ath/ath6kl-3.5/common.h b/drivers/net/wireless/ath/ath6kl-3.5/common.h new file mode 100644 index 000000000000..b5e1d9b70721 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/common.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 COMMON_H +#define COMMON_H + +#include + +#define ATH6KL_MAX_IE 256 + +extern int ath6kl_printk(const char *level, const char *fmt, ...); + +/* + * Reflects the version of binary interface exposed by ATH6KL target + * firmware. Needs to be incremented by 1 for any change in the firmware + * that requires upgrade of the driver on the host side for the change to + * work correctly + */ +#define ATH6KL_ABI_VERSION 1 + +#define SIGNAL_QUALITY_METRICS_NUM_MAX 2 + +enum { + SIGNAL_QUALITY_METRICS_SNR = 0, + SIGNAL_QUALITY_METRICS_RSSI, + SIGNAL_QUALITY_METRICS_ALL, +}; + +/* + * Data Path + */ + +#define WMI_MAX_TX_DATA_FRAME_LENGTH \ + (1500 + sizeof(struct wmi_data_hdr) + \ + sizeof(struct ethhdr) + \ + sizeof(struct ath6kl_llc_snap_hdr)) + +/* An AMSDU frame */ /* The MAX AMSDU length of AR6003 is 3839 */ +#define WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH \ + (3840 + sizeof(struct wmi_data_hdr) + \ + sizeof(struct ethhdr) + \ + sizeof(struct ath6kl_llc_snap_hdr)) + +#define EPPING_ALIGNMENT_PAD \ + (((sizeof(struct htc_frame_hdr) + 3) & (~0x3)) \ + - sizeof(struct htc_frame_hdr)) + +struct ath6kl_llc_snap_hdr { + u8 dsap; + u8 ssap; + u8 cntl; + u8 org_code[3]; + __be16 eth_type; +} __packed; + +enum crypto_type { + NONE_CRYPT = 0x01, + WEP_CRYPT = 0x02, + TKIP_CRYPT = 0x04, + AES_CRYPT = 0x08, + WAPI_CRYPT = 0x10, + BIP_CRYPT = 0x20, + KTK_CRYPT = 0x40, +}; + +struct htc_endpoint_credit_dist; +struct ath6kl; +enum htc_credit_dist_reason; +struct ath6kl_htc_credit_info; + +struct ath6kl *ath6kl_core_alloc(struct device *sdev); +int ath6kl_core_init(struct ath6kl *ar); +void ath6kl_core_init_defer(struct work_struct *wk); +void ath6kl_core_cleanup(struct ath6kl *ar); +struct sk_buff *ath6kl_buf_alloc(int size); +#endif /* COMMON_H */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/core.h b/drivers/net/wireless/ath/ath6kl-3.5/core.h new file mode 100644 index 000000000000..104be5c8795c --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/core.h @@ -0,0 +1,1885 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 CORE_H +#define CORE_H + +#define ATH6KL_SUPPORT_NL80211_KERNEL3_4 + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ANDROID +#ifdef CONFIG_HAS_WAKELOCK +#include +#endif +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include "htc.h" +#include "wmi.h" +#include "bmi.h" +#include "target.h" +#include "wmi_btcoex.h" +#ifdef ACS_SUPPORT +#include "acs.h" +#endif +#include "htcoex.h" +#include "p2p.h" +#include "ap.h" +#include "reg.h" +#include +#include + +#define MAKE_STR(symbol) #symbol +#define TO_STR(symbol) MAKE_STR(symbol) + +/* The script (used for release builds) modifies the following line. */ +#define __BUILD_VERSION_ (3.5.0.337) + +#define DRV_VERSION TO_STR(__BUILD_VERSION_) + +/* enable diagnostic by default in this version */ +#define ATH6KL_DIAGNOSTIC 1 +#ifdef ATH6KL_DIAGNOSTIC +#include "diagnose.h" +#endif + +/* for WMM issues, we might need to enlarge the number of cookies */ +#define ATH6KL_USE_LARGE_COOKIE 1 + +#ifndef CONFIG_ATH6KL_MCC +#define CONFIG_ATH6KL_MCC +#endif + +#ifdef CONFIG_ATH6KL_UB134 +#ifndef CONFIG_ATH6KL_UDP_TPUT_WAR +#define CONFIG_ATH6KL_UDP_TPUT_WAR +#endif +#endif + +#ifdef CONFIG_ATH6KL_MCC +#define ATH6KL_MODULEP2P_DEF_MODE \ + (ATH6KL_MODULEP2P_P2P_ENABLE | \ + ATH6KL_MODULEP2P_CONCURRENT_ENABLE_DEDICATE | \ + ATH6KL_MODULEP2P_CONCURRENT_MULTICHAN) +#else +#define ATH6KL_MODULEP2P_DEF_MODE \ + (ATH6KL_MODULEP2P_P2P_ENABLE | \ + ATH6KL_MODULEP2P_CONCURRENT_ENABLE_DEDICATE) +#endif + +#ifdef CONFIG_ANDROID +#define ATH6KL_MODULE_DEF_DEBUG_QUIRKS \ + (ATH6KL_MODULE_DISABLE_WMI_SYC | \ + ATH6KL_MODULE_DISABLE_RX_AGGR_DROP | \ + ATH6KL_MODULE_WAR_BAD_P2P_GO | \ + /* ATH6KL_MODULE_ENABLE_P2P_CHANMODE | */ \ + /* ATH6KL_MODULE_ENABLE_FW_CRASH_NOTIFY | */ \ + 0) +#else +#define ATH6KL_MODULE_DEF_DEBUG_QUIRKS \ + (ATH6KL_MODULE_DISABLE_WMI_SYC | \ + ATH6KL_MODULE_WAR_BAD_P2P_GO | \ + /* ATH6KL_MODULE_ENABLE_P2P_CHANMODE | */ \ + /* ATH6KL_MODULE_ENABLE_FW_CRASH_NOTIFY | */ \ + 0) +#endif + +#ifndef ATH6KL_MODULEP2P_DEF_MODE +#define ATH6KL_MODULEP2P_DEF_MODE (0) +#endif + +#ifndef ATH6KL_MODULEVAP_DEF_MODE +#define ATH6KL_MODULEVAP_DEF_MODE (0) +#endif + +#ifndef ATH6KL_MODULE_DEF_DEBUG_QUIRKS +#define ATH6KL_MODULE_DEF_DEBUG_QUIRKS (0) +#endif + +#ifndef ATH6KL_DEVNAME_DEF_P2P +#define ATH6KL_DEVNAME_DEF_P2P "p2p%d" +#endif + +#ifndef ATH6KL_DEVNAME_DEF_AP +#define ATH6KL_DEVNAME_DEF_AP "ap%d" +#endif + +#ifndef ATH6KL_DEVNAME_DEF_STA +#define ATH6KL_DEVNAME_DEF_STA "sta%d" +#endif + + +/* + * Native kernel cfg80211 support. + * Current ath6kl code base is kernel 3.2 + * + * For ATH6KL_SUPPORT_NETLINK_KERNEL3_6 & ATH6KL_SUPPORT_NETLINK_KERNEL3_7, + * not really need if compat.ko used. + * + * PLEASE CHANGE THESE FLAGS TO MEET YOUR BSP IF THE BSP USE + * SPECIFIC CFG80211 CODE. + */ +#ifdef ATH6KL_SUPPORT_NL80211_KERNEL3_8 /* Kernel 3.8 series */ +#ifndef ATH6KL_SUPPORT_NL80211_KERNEL3_7 +#define ATH6KL_SUPPORT_NL80211_KERNEL3_7 +#endif +#endif + +#ifdef ATH6KL_SUPPORT_NL80211_KERNEL3_7 /* Kernel 3.7 series */ +#ifndef ATH6KL_SUPPORT_NETLINK_KERNEL3_7 +#define ATH6KL_SUPPORT_NETLINK_KERNEL3_7 +#endif + +#ifndef ATH6KL_SUPPORT_NL80211_KERNEL3_6 +#define ATH6KL_SUPPORT_NL80211_KERNEL3_6 +#endif +#endif + +#ifdef ATH6KL_SUPPORT_NL80211_KERNEL3_6 /* Kernel 3.6 series */ +#ifndef ATH6KL_SUPPORT_NETLINK_KERNEL3_6 +#define ATH6KL_SUPPORT_NETLINK_KERNEL3_6 +#endif + +#ifndef ATH6KL_SUPPORT_NL80211_KERNEL3_5 +#define ATH6KL_SUPPORT_NL80211_KERNEL3_5 +#endif +#endif + +#ifdef ATH6KL_SUPPORT_NL80211_KERNEL3_5 /* Kernel 3.5 series */ +#ifndef ATH6KL_SUPPORT_NL80211_KERNEL3_4 +#define ATH6KL_SUPPORT_NL80211_KERNEL3_4 +#endif +#endif + +/* + * NOT to touch origional branches's Makefile and therefore turn-on + * ATH6KL_SUPPORT_NL80211_QCA by default for all Android releases. + */ +#ifdef CONFIG_ANDROID +#define ATH6KL_SUPPORT_NL80211_QCA + +#if defined(ATH6KL_SUPPORT_NL80211_KERNEL3_4) || \ + defined(ATH6KL_SUPPORT_NL80211_KERNEL3_6) +/* + * New Android (after JB_2.5/JB_MR1) use build-in cfg80211.ko and + * need to remove QCA's cfg80211 implementations. + */ +#undef ATH6KL_SUPPORT_NL80211_QCA +#endif +#endif + +/* + * No good way to support every version of cfg80211.ko so far, especially + * we sometimes change the default behavior of public cfg80211.ko. + * + * To let driver code could compiler between different cfg80211.ko and using + * these flags now. + * Newer NL80211.h may has the similiar definitions to used by application + * or the driver but, unfortunately, all are not we want. + * + * Please add ATH6KL_SUPPORT_NL80211_QCA or ATH6KL_SUPPORT_NL80211_KERNEL3_x + * in your BSP's Makefiles based on the cfg80211.ko you used. + */ +#ifdef ATH6KL_SUPPORT_NL80211_QCA +/* + * Means the cfg80211.ko is specific version and had QCA's special + * implementations. + * + * NL80211_CMD_GET_WOWLAN_QCA: for QCA WoW command. + * NL80211_CMD_BTCOEX_QCA: for QCA BTCoext NL80211 command and event. + * + * TODO : remove these special implementations. + */ +#define NL80211_CMD_GET_WOWLAN_QCA +#define NL80211_CMD_BTCOEX_QCA +#endif +#ifdef ATH6KL_SUPPORT_NL80211_KERNEL3_4 +/* + * NL80211_CMD_START_STOP_AP: for new call-back and structures. + * NL80211_ATTR_WIPHY_RX_SIGNAL_DBM: for new API's parameter. + * CFG80211_WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: new flag consist w/ RoC oper. + * NL80211_ATTR_AP_INACTIVITY_TIMEOUT: new attribute to config AP keep-alive. + */ +#define NL80211_CMD_START_STOP_AP +#define NL80211_ATTR_WIPHY_RX_SIGNAL_DBM +#define CFG80211_WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL +#define NL80211_ATTR_AP_INACTIVITY_TIMEOUT +#endif +#ifdef ATH6KL_SUPPORT_NL80211_KERNEL3_5 +/* + * NL80211_CMD_OPER_CH_SWITCH_NOTIFY: report working frequency back to user. + */ +#define NL80211_CMD_OPER_CH_SWITCH_NOTIFY +#endif +#ifdef ATH6KL_SUPPORT_NL80211_KERNEL3_6 +/* + * CFG80211_NETDEV_REPLACED_BY_WDEV: using wireless_dev pointer instead + * of net_device. + * CFG80211_NO_SET_CHAN_OPERATION: no support set_channel call-back. + */ +#define CFG80211_NETDEV_REPLACED_BY_WDEV +#define CFG80211_NO_SET_CHAN_OPERATION +#endif +#ifdef ATH6KL_SUPPORT_NL80211_KERNEL3_8 +/* + * NL80211_WIPHY_FEATURE_SCAN_FLUSH: flush scan cache before scan if need. + * CFG80211_TX_POWER_PER_WDEV: support set/get TX power per wdev context. + * CFG80211_REMOVE_ROC_CHAN_TYPE: remove channel type of RoC & MgmtTx related + * call-back and APIs. + * CFG80211_NEW_CHAN_DEFINITION: summary origional channel definition and + * channel type into new channel definitaion. + * CFG80211_SAFE_BSS_INFO_ACCESS: to fix BSS IE race access problem. + * NL80211_CMD_TDLS_OPER_REQ: new cfg80211_tdls_oper_request() API for driver's + * automatic TDLS link. + * NL80211_ATTR_P2P_CTWINDOW_OPPPS: P2P-GO support CTWindow/OppPS setting. + */ +#define NL80211_WIPHY_FEATURE_SCAN_FLUSH +#define CFG80211_TX_POWER_PER_WDEV +#define CFG80211_REMOVE_ROC_CHAN_TYPE +#define CFG80211_NEW_CHAN_DEFINITION +#define CFG80211_SAFE_BSS_INFO_ACCESS +#define NL80211_CMD_TDLS_OPER_REQ /* TODO */ +#define NL80211_ATTR_P2P_CTWINDOW_OPPPS /* TODO */ +#endif + +#define ATH6KL_SUPPORT_WIFI_DISC 1 +#define ATH6KL_SUPPORT_WIFI_KTK 1 +#define ATH6KL_SUPPORT_WLAN_HB 1 + +#define MAX_ATH6KL 1 +#define ATH6KL_MAX_RX_BUFFERS 16 +#define ATH6KL_BUFFER_SIZE 1664 +#define ATH6KL_MAX_AMSDU_RX_BUFFERS 4 +#define ATH6KL_AMSDU_REFILL_THRESHOLD 3 +#define ATH6KL_AMSDU_BUFFER_SIZE (WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH + 128) +#define MAX_MSDU_SUBFRAME_PAYLOAD_LEN 1508 +#define MIN_MSDU_SUBFRAME_PAYLOAD_LEN 46 + +#define USER_SAVEDKEYS_STAT_INIT 0 +#define USER_SAVEDKEYS_STAT_RUN 1 + +#define ATH6KL_TX_TIMEOUT 10 +#define ATH6KL_MAX_ENDPOINTS 4 +#define MAX_NODE_NUM 15 + +/* Extra bytes for htc header alignment */ +#define ATH6KL_HTC_ALIGN_BYTES 3 + +/* MAX_HI_COOKIE_NUM are reserved for high priority traffic */ +#ifdef ATH6KL_USE_LARGE_COOKIE +#define MAX_DEF_COOKIE_NUM 270 +#define MAX_HI_COOKIE_NUM 27 /* 10% of MAX_COOKIE_NUM */ +#else +#define MAX_DEF_COOKIE_NUM 180 +#define MAX_HI_COOKIE_NUM 18 /* 10% of MAX_COOKIE_NUM */ +#endif + +#define MAX_COOKIE_DATA_NUM (MAX_DEF_COOKIE_NUM + MAX_HI_COOKIE_NUM) +#define MAX_COOKIE_CTRL_NUM ((MAX_DEF_COOKIE_NUM / WMM_NUM_AC) + 2) + +#define MAX_DEFAULT_SEND_QUEUE_DEPTH (MAX_DEF_COOKIE_NUM / WMM_NUM_AC) + +#define DISCON_TIMER_INTVAL 10000 /* in msec */ +#define A_DEFAULT_LISTEN_INTERVAL 100 +#define A_MAX_WOW_LISTEN_INTERVAL 1000 + +#define ATH6KL_DISCONNECT_TIMEOUT 3 +#define ATH6KL_SEAMLESS_ROAMING_DISCONNECT_TIMEOUT 10 + +/* Channel dwell time in fg scan */ +#define ATH6KL_FG_SCAN_INTERVAL 100 /* in msec */ + +#define ATH6KL_SCAN_ACT_DEWELL_TIME 20 /* in ms. */ +#define ATH6KL_SCAN_PAS_DEWELL_TIME 50 /* in ms. */ +#define ATH6KL_SCAN_PROBE_PER_SSID 1 +#define ATH6KL_SCAN_FG_MAX_PERIOD (5) /* in sec. */ +#define ATH6KL_SCAN_PAS_DEWELL_TIME_WITHOUT_ROAM 100 /* in ms. */ + +/* Remain-on-channel */ +#define ATH6KL_ROC_MAX_PERIOD (5) /* in sec. */ + +/* scan time out */ +#define ATH6KL_SCAN_TIMEOUT_LONG (8 * HZ) /* in sec. */ +#define ATH6KL_SCAN_TIMEOUT_SHORT (5 * HZ) /* in sec. */ +#define ATH6KL_SCAN_TIMEOUT_WITHOUT_ROAM (20 * HZ) /* in sec. */ + +/* 4 way-handshake protect */ +#define ATH6KL_HANDSHAKE_PROC_TIMEOUT (3 * HZ) /* in sec. */ + +/* includes also the null byte */ +#define ATH6KL_FIRMWARE_MAGIC "QCA-ATH6KL" + +/* Default HT CAP Parameters */ +#define ATH6KL_24GHZ_HT40_DEF_STA_IDX (0) /* only STA interface */ +#define ATH6KL_24GHZ_HT40_DEF_WIDTH (1) /* HT40 enabled */ +#define ATH6KL_24GHZ_HT40_DEF_SGI (1) /* SGI enabled */ +#define ATH6KL_24GHZ_HT40_DEF_INTOLR40 (0) /* disabled */ +#define ATH6KL_5GHZ_HT40_DEF_WIDTH (1) /* HT40 enabled */ +#define ATH6KL_5GHZ_HT40_DEF_SGI (1) /* SGI enabled */ +#define ATH6KL_5GHZ_HT40_DEF_INTOLR40 (0) /* disabled */ + +/* default parameters for GeenTX */ +#define ATH6KL_GTX_NEXT_PROBE_COUNT 20 +#define ATH6KL_GTX_MAX_BACK_OFF 6 +#define ATH6KL_GTX_MIN_RSSI 35 +#define ATH6KL_GTX_FORCE_BACKOFF 0 + +/* delay around 29ms on 1/4 msg in wpa/wpa2 to avoid racing with roam +* event in certain platform +*/ +#define ATH6KL_EAPOL_DELAY_REPORT_IN_HANDSHAKE (msecs_to_jiffies(30)) + +/* default roam mode for different situation */ +#ifdef CONFIG_ANDROID +#define ATH6KL_SDIO_DEFAULT_ROAM_MODE \ + ATH6KL_MODULEROAM_NO_LRSSI_SCAN_AT_MULTI +#define ATH6KL_USB_DEFAULT_ROAM_MODE \ + ATH6KL_MODULEROAM_NO_LRSSI_SCAN_AT_MULTI +#else +#define ATH6KL_SDIO_DEFAULT_ROAM_MODE \ + ATH6KL_MODULEROAM_DISABLE_LRSSI_SCAN +#define ATH6KL_USB_DEFAULT_ROAM_MODE \ + ATH6KL_MODULEROAM_DISABLE +#endif + +enum ath6kl_fw_ie_type { + ATH6KL_FW_IE_FW_VERSION = 0, + ATH6KL_FW_IE_TIMESTAMP = 1, + ATH6KL_FW_IE_OTP_IMAGE = 2, + ATH6KL_FW_IE_FW_IMAGE = 3, + ATH6KL_FW_IE_PATCH_IMAGE = 4, + ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5, + ATH6KL_FW_IE_CAPABILITIES = 6, + ATH6KL_FW_IE_PATCH_ADDR = 7, + ATH6KL_FW_IE_BOARD_ADDR = 8, + ATH6KL_FW_IE_VIF_MAX = 9, +}; + +enum ath6kl_fw_capability { + ATH6KL_FW_CAPABILITY_HOST_P2P = 0, + + /* this needs to be last */ + ATH6KL_FW_CAPABILITY_MAX, +}; + +#define ATH6KL_CAPABILITY_LEN (ALIGN(ATH6KL_FW_CAPABILITY_MAX, 32) / 32) + +struct ath6kl_fw_ie { + __le32 id; + __le32 len; + u8 data[0]; +}; + +/* Android privacy command */ +#define ATH6KL_IOCTL_STANDARD01 (SIOCDEVPRIVATE+1) + +/* Standard do_ioctl() ioctl interface */ +#define ATH6KL_IOCTL_STANDARD02 (SIOCDEVPRIVATE+2) + +/* BTC command */ +#define ATH6KL_IOCTL_STANDARD03 (SIOCDEVPRIVATE+3) + +/* hole, please reserved */ +#define ATH6KL_IOCTL_STANDARD12 (SIOCDEVPRIVATE+12) + +/* TX99 */ +#define ATH6KL_IOCTL_STANDARD13 (SIOCDEVPRIVATE+13) + +/* hole, please reserved */ +#define ATH6KL_IOCTL_STANDARD15 (SIOCDEVPRIVATE+15) + +/* ATH6KL_IOCTL_EXTENDED - extended ioctl */ +#define ATH6KL_IOCTL_WEXT_PRIV26 (SIOCIWFIRSTPRIV+26) + +/* reserved for QCSAP (old) */ +#define ATH6KL_IOCTL_WEXT_PRIV27 (SIOCIWFIRSTPRIV+27) + +/* reserved for QCSAP */ +#define ATH6KL_IOCTL_WEXT_PRIV31 (SIOCIWFIRSTPRIV+31) + +#define ATH6KL_IOCTL_AP_APSD (0) +#define ATH6KL_IOCTL_AP_INTRABSS (1) + +/* TBD: ioctl number is aligned to olca branch + * will refine one the loopback tool is ready for native ath6kl + */ +enum ath6kl_xioctl { + ATH6KL_XIOCTL_TRAFFIC_ACTIVITY_CHANGE = 80, +}; + +struct ath6kl_traffic_activity_change { + u32 stream_id; /* stream ID to indicate activity change */ + u32 active; /* active (1) or inactive (0) */ +}; + +struct ath6kl_ioctl_cmd { + u32 subcmd; + u32 options; +}; + +/* Android-specific IOCTL */ +#define ANDROID_SETBAND_ALL 0 +#define ANDROID_SETBAND_5G 1 +#define ANDROID_SETBAND_2G 2 + +struct ath6kl_android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +}; + +struct btcoex_ioctl { + char *cmd; + unsigned int cmd_len; +}; + +enum ath6kl_recovery_mode { + ATH6KL_RECOVERY_MODE_NONE = 0, + ATH6KL_RECOVERY_MODE_WARM, + ATH6KL_RECOVERY_MODE_COLD, +}; + +#ifdef CONFIG_ATH6KL_RECOVERY_MODE_USER +#define ATH6KL_RECOVERY_MODE_DEFAULT CONFIG_ATH6KL_RECOVERY_MODE_USER +#else +#define ATH6KL_RECOVERY_MODE_DEFAULT ATH6KL_RECOVERY_MODE_NONE +#endif + +#define ATH6KL_FW_API2_FILE "fw-2.bin" + +/* AR6003 1.0 definitions */ +#define AR6003_HW_1_0_VERSION 0x300002ba + +/* AR6003 2.0 definitions */ +#define AR6003_HW_2_0_VERSION 0x30000384 +#define AR6003_HW_2_0_PATCH_DOWNLOAD_ADDRESS 0x57e910 +#define AR6003_HW_2_0_FW_DIR "ath6k/AR6003/hw2.0" +#define AR6003_HW_2_0_OTP_FILE "otp.bin.z77" +#define AR6003_HW_2_0_FIRMWARE_FILE "athwlan.bin.z77" +#define AR6003_HW_2_0_TCMD_FIRMWARE_FILE "athtcmd_ram.bin" +#define AR6003_HW_2_0_PATCH_FILE "data.patch.bin" +#define AR6003_HW_2_0_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin" +#define AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6003/hw2.0/bdata.SD31.bin" + +/* AR6003 3.0 definitions */ +#define AR6003_HW_2_1_1_VERSION 0x30000582 +#define AR6003_HW_2_1_1_FW_DIR "ath6k/AR6003/hw2.1.1" +#define AR6003_HW_2_1_1_OTP_FILE "otp.bin" +#define AR6003_HW_2_1_1_FIRMWARE_FILE "athwlan.bin" +#define AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE "athtcmd_ram.bin" +#define AR6003_HW_2_1_1_UTF_FIRMWARE_FILE "utf.bin" +#define AR6003_HW_2_1_1_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6003_HW_2_1_1_PATCH_FILE "data.patch.bin" +#define AR6003_HW_2_1_1_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin" +#define AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6003/hw2.1.1/bdata.SD31.bin" + +/* AR6004 1.0 definitions */ +#define AR6004_HW_1_0_VERSION 0x30000623 +#define AR6004_HW_1_0_FW_DIR "ath6k/AR6004/hw1.0" +#define AR6004_HW_1_0_OTP_FILE "otp.bin" +#define AR6004_HW_1_0_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_1_0_BOARD_DATA_FILE "ath6k/AR6004/hw1.0/bdata.bin" +#define AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6004/hw1.0/bdata.DB132.bin" + +/* AR6004 1.1 definitions */ +#define AR6004_HW_1_1_VERSION 0x30000001 +#define AR6004_HW_1_1_FW_DIR "ath6k/AR6004/hw1.1" +#define AR6004_HW_1_1_OTP_FILE "otp.bin" +#define AR6004_HW_1_1_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_1_1_TCMD_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_1_1_UTF_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_1_1_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6004_HW_1_1_BOARD_DATA_FILE "ath6k/AR6004/hw1.1/bdata.bin" +#define AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6004/hw1.1/bdata.DB132.bin" +#define AR6004_HW_1_1_EPPING_FILE "ath6k/AR6004/hw1.1/epping.bin" +#define AR6004_HW_1_1_SOFTMAC_FILE "ath6k/AR6004/hw1.1/softmac.bin" + +/* AR6004 1.2 definitions */ +#define AR6004_HW_1_2_VERSION 0x300007e8 +#define AR6004_HW_1_2_FW_DIR "ath6k/AR6004/hw1.2" +#define AR6004_HW_1_2_OTP_FILE "otp.bin" +#define AR6004_HW_1_2_FIRMWARE_2_FILE "fw-2.bin" +#define AR6004_HW_1_2_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_1_2_TCMD_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_1_2_UTF_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_1_2_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6004_HW_1_2_BOARD_DATA_FILE "ath6k/AR6004/hw1.2/bdata.bin" +#define AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6004/hw1.2/bdata.bin" +#define AR6004_HW_1_2_EPPING_FILE "ath6k/AR6004/hw1.2/epping.bin" +#define AR6004_HW_1_2_SOFTMAC_FILE "ath6k/AR6004/hw1.2/softmac.bin" + +/* AR6004 1.3 definitions */ +#define AR6004_HW_1_3_VERSION 0x31c8088a +#define AR6004_HW_1_3_FW_DIR "ath6k/AR6004/hw1.3" +#define AR6004_HW_1_3_OTP_FILE "otp.bin" +#define AR6004_HW_1_3_FIRMWARE_2_FILE "fw-2.bin" +#define AR6004_HW_1_3_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_1_3_FIRMWARE_EXT_FILE "fw_ext.ram.bin" +#define AR6004_HW_1_3_TCMD_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_1_3_UTF_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_1_3_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6004_HW_1_3_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.bin" +#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6004/hw1.3/bdata.bin" +#define AR6004_HW_1_3_EPPING_FILE "ath6k/AR6004/hw1.3/epping.bin" +#define AR6004_HW_1_3_SOFTMAC_FILE "ath6k/AR6004/hw1.3/softmac.bin" + +/* AR6004 2.0 definitions */ +#define AR6004_HW_2_0_VERSION 0x31c80958 +#define AR6004_HW_2_0_FW_DIR "ath6k/AR6004/hw2.0" +#define AR6004_HW_2_0_OTP_FILE "otp.bin" +#define AR6004_HW_2_0_FIRMWARE_2_FILE "fw-2.bin" +#define AR6004_HW_2_0_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_2_0_TCMD_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_2_0_UTF_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_2_0_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6004_HW_2_0_BOARD_DATA_FILE "ath6k/AR6004/hw2.0/bdata.bin" +#define AR6004_HW_2_0_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6004/hw2.0/bdata.bin" +#define AR6004_HW_2_0_EPPING_FILE "ath6k/AR6004/hw2.0/epping.bin" +#define AR6004_HW_2_0_SOFTMAC_FILE "ath6k/AR6004/hw2.0/softmac.bin" + +/* AR6004 3.0 definitions */ +#define AR6004_HW_3_0_VERSION 0x31C809F8 +#define AR6004_HW_3_0_FW_DIR "ath6k/AR6004/hw3.0" +#define AR6004_HW_3_0_OTP_FILE "otp.bin" +#define AR6004_HW_3_0_FIRMWARE_2_FILE "fw-2.bin" +#define AR6004_HW_3_0_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_3_0_TCMD_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_3_0_UTF_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_3_0_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6004_HW_3_0_BOARD_DATA_FILE "ath6k/AR6004/hw3.0/bdata.bin" +#define AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6004/hw3.0/bdata.bin" +#define AR6004_HW_3_0_EPPING_FILE "ath6k/AR6004/hw3.0/epping.bin" +#define AR6004_HW_3_0_SOFTMAC_FILE "ath6k/AR6004/hw3.0/softmac.bin" + +/* AR6006 1.0 definitions */ +#define AR6006_HW_1_0_VERSION 0x31c80997 +#define AR6006_HW_1_0_FW_DIR "ath6k/AR6006/hw1.0" +#define AR6006_HW_1_0_FIRMWARE_2_FILE "fw-2.bin" +#define AR6006_HW_1_0_FIRMWARE_FILE "fw.ram.bin" +#define AR6006_HW_1_0_BOARD_DATA_FILE "ath6k/AR6006/hw1.0/bdata.bin" +#define AR6006_HW_1_0_EPPING_FILE "ath6k/AR6006/hw1.0/epping.bin" +#define AR6006_HW_1_0_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6006/hw1.0/bdata.bin" +#define AR6006_HW_1_0_SOFTMAC_FILE "ath6k/AR6006/hw1.0/softmac.bin" + +/* AR6006 1.1 definitions */ +#define AR6006_HW_1_1_VERSION 0x31c80002 +#define AR6006_HW_1_1_FW_DIR "ath6k/AR6006/hw1.1" +#define AR6006_HW_1_1_FIRMWARE_2_FILE "fw-2.bin" +#define AR6006_HW_1_1_FIRMWARE_FILE "fw.ram.bin" +#define AR6006_HW_1_1_BOARD_DATA_FILE "ath6k/AR6006/hw1.1/bdata.bin" +#define AR6006_HW_1_1_EPPING_FILE "ath6k/AR6006/hw1.1/epping.bin" +#define AR6006_HW_1_1_DEFAULT_BOARD_DATA_FILE \ + "ath6k/AR6006/hw1.1/bdata.bin" +#define AR6006_HW_1_1_SOFTMAC_FILE "ath6k/AR6006/hw1.1/softmac.bin" + +#define AR6004_MAX_64K_FW_SIZE 65536 + +#define BDATA_CHECKSUM_OFFSET 4 +#define BDATA_MAC_ADDR_OFFSET 8 +#define BDATA_OPFLAGS_OFFSET 24 +#define BDATA_TXRXMASK_OFFSET 40 + +/* Per STA data, used in AP mode */ +#define STA_PS_AWAKE BIT(0) +#define STA_PS_SLEEP BIT(1) +#define STA_PS_POLLED BIT(2) +#define STA_PS_APSD_TRIGGER BIT(3) +#define STA_PS_APSD_EOSP BIT(4) +#define STA_HT_SUPPORT BIT(5) + +/* HTC TX packet tagging definitions */ +#define ATH6KL_CONTROL_PKT_TAG HTC_TX_PACKET_TAG_USER_DEFINED +#define ATH6KL_DATA_PKT_TAG (ATH6KL_CONTROL_PKT_TAG + 1) +#define ATH6KL_PRI_DATA_PKT_TAG (ATH6KL_CONTROL_PKT_TAG + 2) + +#define AR6003_CUST_DATA_SIZE 16 + +#define AGGR_WIN_IDX(x, y) ((x) % (y)) +#define AGGR_INCR_IDX(x, y) AGGR_WIN_IDX(((x) + 1), (y)) +#define AGGR_DCRM_IDX(x, y) AGGR_WIN_IDX(((x) - 1), (y)) +#define ATH6KL_MAX_SEQ_NO 0xFFF +#define ATH6KL_NEXT_SEQ_NO(x) (((x) + 1) & ATH6KL_MAX_SEQ_NO) + +#define NUM_OF_TIDS 8 +#define AGGR_SZ_DEFAULT 8 + +#define AGGR_WIN_SZ_MIN 2 +#define AGGR_WIN_SZ_MAX 8 + +#define TID_WINDOW_SZ(_x) ((_x) << 1) + +#define AGGR_NUM_OF_FREE_NETBUFS 16 + +#define AGGR_RX_TIMEOUT 100 /* in ms */ +#define AGGR_RX_TIMEOUT_VO 50 /* in ms */ +#define MCC_AGGR_RX_TIMEOUT 300 /* in ms */ +#define MCC_AGGR_RX_TIMEOUT_VO 150 /* in ms */ + +#define AGGR_GET_RXTID_STATS(_p, _x) (&(_p->stat[(_x)])) +#define AGGR_GET_RXTID(_p, _x) (&(_p->rx_tid[(_x)])) + +#define AGGR_BA_EVT_GET_CONNID(_conn) ((_conn) >> 4) +#define AGGR_BA_EVT_GET_TID(_tid) ((_tid) & 0xF) + +#define AGGR_TX_MAX_AGGR_SIZE 1600 /* Sync to max. PDU size of host. */ +#define AGGR_TX_MAX_PDU_SIZE 120 +#define AGGR_TX_MIN_PDU_SIZE 64 /* 802.3(14) + LLC(8) + IP/TCP(20) = 42 */ +#define AGGR_TX_MAX_NUM 6 +#define AGGR_TX_TIMEOUT 4 /* in ms */ + +#define AGGR_TX_PROG_HS_THRESH 1200 +#define AGGR_TX_PROG_HS_FACTOR 2 +#define AGGR_TX_PROG_NS_THRESH 250 +#define AGGR_TX_PROG_NS_FACTOR 4 +#define AGGR_TX_PROG_CHECK_INTVAL (1 * HZ) +#define AGGR_TX_PROG_HS_MAX_NUM 22 +#define AGGR_TX_PROG_HS_TIMEOUT 8 /* in ms */ + +#define AGGR_GET_TXTID(_p, _x) (&(_p->tx_tid[(_x)])) + +#define WMI_TIMEOUT (2 * HZ) + +#define MBOX_YIELD_LIMIT 99 + +/* AP-PS */ +#define ATH6KL_PS_QUEUE_CHECK_AGE (1 * 1000) /* 1 sec. */ +#define ATH6KL_PS_QUEUE_MAX_AGE (5) /* 5 cycles */ +#define ATH6KL_PS_QUEUE_NO_AGE (0) /* no aging */ + +#define ATH6KL_PS_QUEUE_MAX_DEPTH (65535) +#define ATH6KL_PS_QUEUE_NO_DEPTH (0) /* unlimit */ + +enum ps_queue_type { + PS_QUEUE_TYPE_NONE, + PS_QUEUE_TYPE_STA_UNICAST, + PS_QUEUE_TYPE_STA_MGMT, + PS_QUEUE_TYPE_AP_MULTICAST, +}; + +/* Scanband */ +enum scanband_type { + SCANBAND_TYPE_ALL, /* Scan all supported channel */ + SCANBAND_TYPE_2G, /* Scan 2GHz channel only */ + SCANBAND_TYPE_5G, /* Scan 5GHz channel only */ + SCANBAND_TYPE_CHAN_ONLY, /* Scan single channel only */ + SCANBAND_TYPE_P2PCHAN, /* Scan P2P channel only */ +}; + +#define ATH6KL_RSN_CAP_NULLCONF (0xffff) + +/* configuration lags */ +/* + * ATH6KL_CONF_IGNORE_ERP_BARKER: Ignore the barker premable in + * ERP IE of beacon to determine the short premable support when + * sending (Re)Assoc req. + * ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN: Don't send the power + * module state transition failure events which happen during + * scan, to the host. + */ +#define ATH6KL_CONF_IGNORE_ERP_BARKER BIT(0) +#define ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN BIT(1) +#define ATH6KL_CONF_ENABLE_11N BIT(2) +#define ATH6KL_CONF_ENABLE_TX_BURST BIT(3) +#define ATH6KL_CONF_SUSPEND_CUTPOWER BIT(4) +#define ATH6KL_CONF_ENABLE_FLOWCTRL BIT(5) +#define ATH6KL_CONF_DISABLE_SKIP_FLOWCTRL BIT(6) +#define ATH6KL_CONF_DISABLE_RX_AGGR_DROP BIT(7) + +enum wlan_low_pwr_state { + WLAN_POWER_STATE_ON, + WLAN_POWER_STATE_CUT_PWR, + WLAN_POWER_STATE_DEEP_SLEEP, + WLAN_POWER_STATE_WOW +}; + +enum sme_state { + SME_DISCONNECTED, + SME_CONNECTING, + SME_CONNECTED +}; + +struct skb_hold_q { + struct sk_buff *skb; + bool is_amsdu; + u16 seq_no; +}; + +/* + * ATH6KL_MAX_WAIT_CONTINUS_PKT: the maximum number of rx + * continuous packets to be waited, the first packet will + * wait tid_timeout_setting time, the second will wait + * tid_timeout_setting / 2 etc. +*/ +#define ATH6KL_MAX_WAIT_CONTINUOUS_PKT 3 + +struct rxtid { + bool aggr; + u16 win_sz; + u16 seq_next; + u32 hold_q_sz; + struct skb_hold_q *hold_q; + struct sk_buff_head q; + spinlock_t lock; + u16 timerwait_seq_num; /* current wait seq_no next */ + bool sync_next_seq; + struct timer_list tid_timer; + u8 tid_timer_scheduled; + u8 tid; + u16 issue_timer_seq; + struct aggr_conn_info *aggr_conn; + u8 continuous_count; +}; + +struct rxtid_stats { + u32 num_into_aggr; + u32 num_dups; + u32 num_oow; + u32 num_mpdu; + u32 num_amsdu; + u32 num_delivered; + u32 num_timeouts; + u32 num_hole; + u32 num_bar; +}; + +enum { + AGGR_TX_OK = 0, + AGGR_TX_DONE, + AGGR_TX_BYPASS, + AGGR_TX_DROP, + AGGR_TX_UNKNOW, +}; + +struct txtid { + u8 tid; + u16 aid; + u16 max_aggr_sz; /* 0 means disable */ + struct timer_list timer; + struct sk_buff *amsdu_skb; + u8 amsdu_cnt; /* current aggr count */ + u8 *amsdu_start; /* start pointer of amsdu frame */ + u16 amsdu_len; /* current aggr length */ + u16 amsdu_lastpdu_len; /* last PDU length */ + spinlock_t lock; + struct ath6kl_vif *vif; + + /* TX progressive */ + unsigned long last_check_time; + u32 last_num_amsdu; + u32 last_num_timeout; + + /* Statistics */ + u32 num_pdu; + u32 num_amsdu; + u32 num_timeout; + u32 num_flush; + u32 num_tx_null; + u32 num_overflow; +}; + +struct aggr_info { + struct ath6kl_vif *vif; + struct sk_buff_head free_q; + + /* RX */ + u16 rx_aggr_timeout; /* in ms */ + + /* TX A-MSDU */ + bool tx_amsdu_enable; /* IOT : treat A-MPDU & A-MSDU are exclusive. */ + bool tx_amsdu_seq_pkt; + bool tx_amsdu_progressive; + bool tx_amsdu_progressive_hispeed; /* in high speed or not */ + + u8 tx_amsdu_max_aggr_num; + u32 tx_amsdu_max_aggr_len; + u16 tx_amsdu_max_pdu_len; + u16 tx_amsdu_timeout; /* in ms */ +}; + +struct aggr_conn_info { + u8 aggr_sz; + struct aggr_info *aggr_cntxt; + struct net_device *dev; + struct rxtid rx_tid[NUM_OF_TIDS]; + struct rxtid_stats stat[NUM_OF_TIDS]; + u32 tid_timeout_setting[NUM_OF_TIDS]; + /* TX A-MSDU */ + struct txtid tx_tid[NUM_OF_TIDS]; +}; + +struct ath6kl_wep_key { + u8 key_index; + u8 key_len; + u8 key[64]; +}; + +#define ATH6KL_KEY_SEQ_LEN 8 + +struct ath6kl_key { + u8 key[WLAN_MAX_KEY_LEN]; + u8 key_len; + u8 seq[ATH6KL_KEY_SEQ_LEN]; + u8 seq_len; + u32 cipher; +}; + +struct ath6kl_node_mapping { + u8 mac_addr[ETH_ALEN]; + u8 ep_id; + u8 tx_pend; +}; + +enum cookie_type { + COOKIE_TYPE_NONE, + COOKIE_TYPE_DATA, + COOKIE_TYPE_CTRL, +}; + +struct ath6kl_cookie { + struct ath6kl_cookie_pool *cookie_pool; + struct sk_buff *skb; + u32 map_no; + struct htc_packet htc_pkt; + struct ath6kl_cookie *arc_list_next; +}; + +struct ath6kl_cookie_pool { + enum cookie_type cookie_type; + u32 cookie_num; /* total number */ + + struct ath6kl_cookie *cookie_list; + u32 cookie_count; /* current available number */ + struct ath6kl_cookie *cookie_mem; + + /* stats */ + u32 cookie_alloc_cnt; + u32 cookie_alloc_fail_cnt; + u32 cookie_free_cnt; + u32 cookie_peak_cnt; +}; + +struct ath6kl_ps_buf_desc { + struct list_head list; + + u32 age; + size_t len; + + /* For DATA */ + struct sk_buff *skb; + + /* For MGMT */ + u32 freq; + u32 wait; + u32 id; + bool no_cck; + bool dont_wait_for_ack; + u8 buf[1]; +}; + +struct ath6kl_ps_buf_head { + struct list_head list; + spinlock_t lock; + + enum ps_queue_type queue_type; + int depth; + + u32 age_cycle; + u32 max_depth; + + /* stat */ + u32 enqueued; + u32 enqueued_err; + u32 dequeued; + u32 aged; +}; + +#ifdef ATHTST_SUPPORT +#define ATH_RSSI_LPF_LEN 10 +#define ATH_RSSI_DUMMY_MARKER 0x127 +#define RSSI_LPF_THRESHOLD -20 +#define ATH_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out */ +#define HAL_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out */ +#define ATH_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) + +#define ATH_EP_MUL(x, mul) ((x) * (mul)) + +#define ATH_RSSI_IN(x) \ + (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER)) + +#define ATH_LPF_RSSI(x, y, len) \ +((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) + +#define ATH_RSSI_LPF(x, y) do { \ + if ((y) >= RSSI_LPF_THRESHOLD) \ + x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ +} while (0) +#endif + +enum ath6kl_phy_mode { + ATH6KL_PHY_MODE_11A = 0, /* 11a Mode */ + ATH6KL_PHY_MODE_11G = 1, /* 11b/g Mode */ + ATH6KL_PHY_MODE_11B = 2, /* 11b Mode */ + ATH6KL_PHY_MODE_11GONLY = 3, /* 11g only Mode */ + ATH6KL_PHY_MODE_11NA_HT20 = 4, /* 11na HT20 Mode */ + ATH6KL_PHY_MODE_11NG_HT20 = 5, /* 11ng HT20 Mode */ + ATH6KL_PHY_MODE_11NA_HT40 = 6, /* 11na HT40 Mode */ + ATH6KL_PHY_MODE_11NG_HT40 = 7, /* 11ng HT40 Mode */ + ATH6KL_PHY_MODE_UNKNOWN = 8, /* Unknown */ + ATH6KL_PHY_MODE_MAX = 8, +}; + +struct ath6kl_sta { + u16 sta_flags; + u8 mac[ETH_ALEN]; + u8 aid; + u8 keymgmt; + u8 ucipher; + u8 auth; + u8 wpa_ie[ATH6KL_MAX_IE]; + enum ath6kl_phy_mode phymode; + struct ath6kl_vif *vif; + + /* ath6kl_sta global lock, psq_data & psq_mgmt also use it. */ + spinlock_t lock; + + /* AP-PS */ + struct ath6kl_ps_buf_head psq_data; + struct ath6kl_ps_buf_head psq_mgmt; + struct timer_list psq_age_timer; + u8 apsd_info; + + /* TX/RX-AMSDU */ + struct aggr_conn_info *aggr_conn_cntxt; + + /* AP-Keepalive */ + u16 last_txrx_time_tgt; /* target time. */ + unsigned long last_txrx_time; /* in jiffies., host time. */ +#ifdef ATHTST_SUPPORT + int avg_data_rssi; +#endif +}; + +struct ath6kl_version { + u32 target_ver; + u32 wlan_ver; + u32 abi_ver; +}; + +struct ath6kl_bmi { + u32 cmd_credits; + bool done_sent; + u8 *cmd_buf; + u32 max_data_size; + u32 max_cmd_size; +}; + +struct target_stats { + u64 tx_pkt; + u64 tx_byte; + u64 tx_ucast_pkt; + u64 tx_ucast_byte; + u64 tx_mcast_pkt; + u64 tx_mcast_byte; + u64 tx_bcast_pkt; + u64 tx_bcast_byte; + u64 tx_rts_success_cnt; + u64 tx_pkt_per_ac[4]; + + u64 tx_err; + u64 tx_fail_cnt; + u64 tx_retry_cnt; + u64 tx_mult_retry_cnt; + u64 tx_rts_fail_cnt; + + u64 rx_pkt; + u64 rx_byte; + u64 rx_ucast_pkt; + u64 rx_ucast_byte; + u64 rx_mcast_pkt; + u64 rx_mcast_byte; + u64 rx_bcast_pkt; + u64 rx_bcast_byte; + u64 rx_frgment_pkt; + + u64 rx_err; + u64 rx_crc_err; + u64 rx_key_cache_miss; + u64 rx_decrypt_err; + u64 rx_dupl_frame; + + u64 tkip_local_mic_fail; + u64 tkip_cnter_measures_invoked; + u64 tkip_replays; + u64 tkip_fmt_err; + u64 ccmp_fmt_err; + u64 ccmp_replays; + + u64 pwr_save_fail_cnt; + + u64 cs_bmiss_cnt; + u64 cs_low_rssi_cnt; + u64 cs_connect_cnt; + u64 cs_discon_cnt; + + s32 tx_ucast_rate; + s8 tx_rate_index; + s32 rx_ucast_rate; + + u32 lq_val; + + u32 wow_pkt_dropped; + u16 wow_evt_discarded; + + s16 noise_floor_calib; + s16 cs_rssi; + s16 cs_ave_beacon_rssi; + u8 cs_ave_beacon_snr; + u8 cs_last_roam_msec; + u8 cs_snr; + + u8 wow_host_pkt_wakeups; + u8 wow_host_evt_wakeups; + + u32 arp_received; + u32 arp_matched; + u32 arp_replied; +}; + +struct ath6kl_mbox_info { + u32 htc_addr; + u32 htc_ext_addr; + u32 htc_ext_sz; + + u32 block_size; + + u32 gmbox_addr; + + u32 gmbox_sz; +}; + +enum ath6kl_hw_flags { + ATH6KL_HW_TGT_ALIGN_PADDING = BIT(0), + ATH6KL_HW_SINGLE_PIPE_SCHED = BIT(1), + ATH6KL_HW_FIRMWARE_EXT_SUPPORT = BIT(2), + ATH6KL_HW_USB_FLOWCTRL = BIT(3), + ATH6KL_HW_XTAL_40MHZ = BIT(4), +}; + +/* + * 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 ATH6KL_KEYBUF_SIZE 16 +#define ATH6KL_MICBUF_SIZE (8+8) /* space for both tx and rx */ + +#define ATH6KL_KEY_XMIT 0x01 +#define ATH6KL_KEY_RECV 0x02 +#define ATH6KL_KEY_DEFAULT 0x80 /* default xmit key */ + +/* Initial group key for AP mode */ +struct ath6kl_req_key { + bool valid; + u8 key_index; + int key_type; + u8 key[WLAN_MAX_KEY_LEN]; + u8 key_len; +}; + +/* + * Bluetooth WiFi co-ex information. + * This structure keeps track of the Bluetooth related status. + * This involves the Bluetooth ACL link role, Bluetooth remote lmp version. + */ +struct ath6kl_btcoex { + u32 acl_role; /* Master/slave role of Bluetooth ACL link */ + u32 remote_lmp_ver; /* LMP version of the remote device. */ + u32 bt_vendor; /* Keeps track of the Bluetooth chip vendor */ +}; +enum ath6kl_hif_type { + ATH6KL_HIF_TYPE_SDIO, + ATH6KL_HIF_TYPE_USB, +}; + +enum ath6kl_chan_type { + ATH6KL_CHAN_TYPE_NONE, /* by target or 11abg */ + ATH6KL_CHAN_TYPE_HT40PLUS, + ATH6KL_CHAN_TYPE_HT40MINUS, + ATH6KL_CHAN_TYPE_HT20, +}; + +/* + * Driver's maximum limit, note that some firmwares support only one vif + * and the runtime (current) limit must be checked from ar->vif_max. + */ +#define ATH6KL_VIF_MAX 8 + +/* vif flags info */ +enum ath6kl_vif_state { + CONNECTED, + CONNECT_PEND, + CONNECT_HANDSHAKE_PROTECT, + FIRST_EAPOL_PENDSENT, + WMM_ENABLED, + NETQ_STOPPED, + DTIM_EXPIRED, + NETDEV_REGISTERED, + CLEAR_BSSFILTER_ON_BEACON, + DTIM_PERIOD_AVAIL, + WLAN_ENABLED, + STATS_UPDATE_PEND, + AMSDU_ENABLED, + HOST_SLEEP_MODE_CMD_PROCESSED, + ROC_PEND, + ROC_ONGOING, + ROC_CANCEL_PEND, + ROC_WAIT_EVENT, + DISCONNECT_PEND, + PMKLIST_GET_PEND, + PORT_STATUS_PEND, + WLAN_WOW_ENABLE, + SCANNING, + DORMANT, + PS_STICK, +#ifdef ATHTST_SUPPORT + CE_WMI_UPDATE, + CE_WMI_SCAN, + CE_WMI_TESTMODE_RX, + CE_WMI_TESTMODE_GET, +#endif +}; +#ifdef ATHTST_SUPPORT +/* IOCTL structure to configure the wireless interface. */ +struct athr_cmd { + int cmd; /* CMD Type */ + int data[4]; /* CMD Data */ +}; + +#define ATHR_WLAN_SCAN_BAND 1 +#define ATHR_WLAN_FIND_BEST_CHANNEL 2 + +#define ATHR_CMD_SCANBAND_ALL 0 /* Scan all supported channel */ +#define ATHR_CMD_SCANBAND_2G 1 /* Scan 2GHz channel only */ +#define ATHR_CMD_SCANBAND_5G 2 /* Scan 5GHz channel only */ +#define ATHR_CMD_SCANBAND_CHAN_ONLY 3 /* Scan single channel only */ +#endif + +#ifdef ACL_SUPPORT +#define AP_ACL_SIZE 10 +#define ATH_MAC_LEN 6 + +struct WMI_AP_ACL { + u16 index; + u8 acl_mac[AP_ACL_SIZE][ATH_MAC_LEN]; + u8 wildcard[AP_ACL_SIZE]; + u8 policy; +}; +#endif + +struct ath6kl_vif { + struct list_head list; + struct wireless_dev wdev; + struct net_device *ndev; + struct ath6kl *ar; + /* Lock to protect vif specific net_stats and flags */ + spinlock_t if_lock; + u8 fw_vif_idx; + unsigned long flags; + int ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 dot11_auth_mode; + u8 auth_mode; + u8 prwise_crypto; + u8 prwise_crypto_len; + u8 grp_crypto; + u8 grp_crypto_len; + u8 def_txkey_index; + u8 next_mode; + u8 nw_type; + u8 bssid[ETH_ALEN]; + u8 req_bssid[ETH_ALEN]; + u16 ch_hint; + u16 bss_ch; + enum ath6kl_phy_mode phymode; /* Working PhyMode for AP&STA modes */ + enum ath6kl_chan_type chan_type;/* Working ChanType for AP mode */ + struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; +#ifdef PMF_SUPPORT + struct ath6kl_key keys[WMI_MAX_IGTK_INDEX + 1]; +#else + struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1]; +#endif + struct aggr_info *aggr_cntxt; + struct timer_list disconnect_timer; + u32 connect_ctrl_flags; + u8 usr_bss_filter; + struct cfg80211_scan_request *scan_req; + struct timer_list vifscan_timer; + struct timer_list shprotect_timer; + enum sme_state sme_state; + u8 intra_bss; + u8 ap_apsd; + struct wmi_ap_mode_stat ap_stats; + int last_dump_ap_stats_idx; /* for dump_station call-back */ + u8 ap_country_code[3]; + int sta_no_ht_num; + struct ath6kl_sta sta_list[AP_MAX_NUM_STA]; + u16 sta_list_index; /* at least AP_MAX_NUM_STA bits */ + struct ath6kl_req_key ap_mode_bkey; + struct ath6kl_ps_buf_head psq_mcast; + spinlock_t psq_mcast_lock; + int reconnect_flag; + u32 last_roc_id; + struct ieee80211_channel *last_roc_channel; + unsigned int last_roc_duration; + u32 last_cancel_roc_id; + u32 send_action_id; + bool probe_req_report; +#ifdef CE_SUPPORT + bool probe_resp_report; +#endif + u16 next_chan; /* Setting Channel */ + enum ath6kl_chan_type next_chan_type; /* Setting Channel-Type */ + u16 assoc_bss_beacon_int; + u8 assoc_bss_dtim_period; + struct net_device_stats net_stats; + struct target_stats target_stats; + struct htcoex *htcoex_ctx; + struct wmi_scan_params_cmd sc_params; + struct wmi_scan_params_cmd sc_params_default; + u8 pmkid_list_buf[MAX_PMKID_LIST_SIZE]; + u16 last_rsn_cap; +#ifdef ATH6KL_DIAGNOSTIC + struct wifi_diag diag; +#endif + struct p2p_ps_info *p2p_ps_info_ctx; + enum scanband_type scanband_type; + u32 scanband_chan; + struct ap_keepalive_info *ap_keepalive_ctx; + struct ap_acl_info *ap_acl_ctx; + struct ap_admc_info *ap_admc_ctx; + struct timer_list sche_scan_timer; + int sche_scan_interval; /* in ms. */ + + u8 last_pwr_mode; + u8 saved_pwr_mode; + u8 arp_offload_ip_set; + struct delayed_work work_eapol_send; + struct sk_buff *pend_skb; + spinlock_t pend_skb_lock; + + int needed_headroom; +#ifdef CE_SUPPORT + u8 scan_band; + u32 scan_chan; +#endif + +#ifdef ACL_SUPPORT + struct WMI_AP_ACL acl_db; +#endif + +#ifdef ACS_SUPPORT + struct acs *acs_ctx; + int best_chan[4]; +#endif + struct wmi_ant_div_stat ant_div_stat; + + struct delayed_work work_pending_connect; + struct p2p_pending_connect_info *pending_connect_info; + +}; + +#define WOW_LIST_ID 0 +#define WOW_HOST_REQ_DELAY 5000 /* ms */ + +/* Flag info */ +enum ath6kl_dev_state { + WMI_ENABLED, + WMI_READY, + WMI_CTRL_EP_FULL, + TESTMODE, + TESTMODE_EPPING, + DESTROY_IN_PROGRESS, + SKIP_SCAN, + ROAM_TBL_PEND, + FIRST_BOOT, + USB_REMOTE_WKUP, + INIT_DEFER_PROGRESS, + DOWNLOAD_FIRMWARE_EXT, + MCC_ENABLED, + SKIP_FLOWCTRL_EVENT, + DISABLE_SCAN, + INTERNAL_REGDB, +}; + +enum ath6kl_state { + ATH6KL_STATE_OFF, + ATH6KL_STATE_ON, + ATH6KL_STATE_DEEPSLEEP, + ATH6KL_STATE_CUTPOWER, + ATH6KL_STATE_WOW, + ATH6KL_STATE_PRE_SUSPEND, + ATH6KL_STATE_PRE_SUSPEND_DEEPSLEEP, +}; + +#define ATH6KL_VAPMODE_MASK (0xf) /* each VAP use 4 bits */ +#define ATH6KL_VAPMODE_OFFSET (4) + +enum ath6kl_vap_mode { + ATH6KL_VAPMODE_DISABLED = 0x0, + ATH6KL_VAPMODE_STA, + ATH6KL_VAPMODE_AP, /* w/o 4 address */ + + /* NOT YET */ + ATH6KL_VAPMODE_ADHOC, + ATH6KL_VAPMODE_WDS, /* AP w/ 4 address */ + ATH6KL_VAPMODE_P2PDEV, /* Dedicaded P2P-Device */ + ATH6KL_VAPMODE_P2P, /* P2P-GO or P2P-Client */ + + ATH6KL_VAPMODE_LAST = 0xf, +}; + + +#ifdef USB_AUTO_SUSPEND + +struct usb_pm_skb_queue_t { + struct list_head list; + struct sk_buff *skb; + int pipeID; + struct ath6kl *ar; +}; + +#endif + +struct ath6kl { + struct device *dev; + struct wiphy *wiphy; + + spinlock_t state_lock; + enum ath6kl_state state; + unsigned int testmode; + + unsigned int starving_prevention; + + struct ath6kl_bmi bmi; + const struct ath6kl_hif_ops *hif_ops; + const struct ath6kl_htc_ops *htc_ops; + struct wmi *wmi; + int tx_pending[ENDPOINT_MAX]; + int total_tx_data_pend; + struct htc_target *htc_target; + enum ath6kl_hif_type hif_type; + void *hif_priv; + struct list_head vif_list; + /* Lock to avoid race in vif_list entries among add/del/traverse */ + spinlock_t list_lock; + u8 num_vif; + unsigned int vif_max; + u8 max_norm_iface; + u8 avail_idx_map; + enum ath6kl_vap_mode next_mode[ATH6KL_VIF_MAX]; + spinlock_t lock; + struct semaphore sem; + struct semaphore wmi_evt_sem; + u16 listen_intvl_b; + u16 listen_intvl_t; + struct low_rssi_scan_params low_rssi_roam_params; + struct ath6kl_version version; + u32 target_type; + u32 target_subtype; + u8 tx_pwr; + struct ath6kl_node_mapping node_map[MAX_NODE_NUM]; + u8 ibss_ps_enable; + bool ibss_if_active; + u8 node_num; + u8 next_ep_id; + struct ath6kl_cookie_pool cookie_data; + struct ath6kl_cookie_pool cookie_ctrl; + enum htc_endpoint_id ac2ep_map[WMM_NUM_AC]; + bool ac_stream_active[WMM_NUM_AC]; + u8 ac_stream_pri_map[WMM_NUM_AC]; + u8 hiac_stream_active_pri; + u8 ac_stream_active_num; + u8 ep2ac_map[ENDPOINT_MAX]; + enum htc_endpoint_id ctrl_ep; + struct ath6kl_htc_credit_info credit_state_info; + u32 user_key_ctrl; /* FIXME : NEED? */ + struct list_head amsdu_rx_buffer_queue; + u8 rx_meta_ver; + enum wlan_low_pwr_state wlan_pwr_state; + u8 mac_addr[ETH_ALEN]; +#define AR_MCAST_FILTER_MAC_ADDR_SIZE 4 + struct { + void *rx_report; + size_t rx_report_len; + } tm; + + struct ath6kl_hw { + u32 id; + const char *name; + u32 dataset_patch_addr; + u32 app_load_addr; + u32 app_start_override_addr; + u32 app_load_ext_addr; + u32 board_ext_data_addr; + u32 reserved_ram_size; + u32 board_addr; + u32 testscript_addr; + u32 flags; + + struct ath6kl_hw_fw { + const char *dir; + const char *otp; + const char *fw; + const char *tcmd; + const char *patch; + const char *api2; + const char *utf; + const char *testscript; + const char *fw_ext; + } fw; + + const char *fw_board; + const char *fw_default_board; + const char *fw_epping; + const char *fw_softmac; + } hw; + + u16 conf_flags; + wait_queue_head_t event_wq; + struct ath6kl_mbox_info mbox_info; + + unsigned long flag; + + u8 *fw_board; + size_t fw_board_len; + + u8 *fw_otp; + size_t fw_otp_len; + + u8 *fw; + size_t fw_len; + + u8 *fw_patch; + size_t fw_patch_len; + + u8 *fw_testscript; + size_t fw_testscript_len; + + u8 *fw_softmac; + size_t fw_softmac_len; + + u8 *fw_ext; + size_t fw_ext_len; + + unsigned long fw_capabilities[ATH6KL_CAPABILITY_LEN]; + + struct workqueue_struct *ath6kl_wq; + + struct dentry *debugfs_phy; + + /* Support P2P or not */ + bool p2p; + + /* Support P2P-Concurrent or not */ + bool p2p_concurrent; + + /* Support P2P-Multi-Channel-Concurrent or not */ + bool p2p_multichan_concurrent; + + /* Need Dedicated-P2P-Device interface or not */ + bool p2p_dedicate; + + /* Support ath6kl-3.2's P2P-Concurrent or not */ + bool p2p_compat; + + /* + * STA + AP is a special mode. + * Reuse P2P framwork but no P2P function. + * At least 4VAPs to support STA(1) + P2P(2) + AP(1) mode. + */ +#define IS_STA_AP_ONLY(_ar) \ + ((_ar)->p2p_concurrent_ap && ((_ar)->vif_max < TARGET_VIF_MAX)) + + /* Support P2P-Concurrent with softAP or not */ + bool p2p_concurrent_ap; + + /* Retry P2P Action frame or not */ + bool p2p_frame_retry; + + /* Not to report P2P Frame to user if not in RoC period */ + bool p2p_frame_not_report; + + /* Allow P2P operate in PASSIVE/IBSS channels */ + bool p2p_in_pasv_chan; + + /* Only scan P2P channels for all P2P interfaces */ + bool p2p_wise_scan; + + /* WAR EV119712 */ + bool p2p_war_bad_intel_go; + + /* WAR CR468120 */ + bool p2p_war_bad_broadcom_go; + + /* WAR CR479897 */ + bool p2p_war_p2p_client_awake; + + /* IOT : Not to append P2P IE in concurrent STA interface */ +#define P2P_IE_IN_PROBE_REQ (1 << 0) +#define P2P_IE_IN_ASSOC_REQ (1 << 1) + u8 p2p_ie_not_append; + + bool sche_scan; + +#ifdef ATH6KL_SUPPORT_WIFI_KTK + /* ktk feature is started ot not */ + bool ktk_active; + + /* ktk cipher key */ + u8 ktk_passphrase[16]; +#endif + +#ifdef ATH6KL_SUPPORT_WIFI_DISC + /* discovery feature is started ot not */ + bool disc_active; +#endif + + struct ath6kl_btcoex btcoex_info; + u32 mod_debug_quirks; + +#ifdef CONFIG_ATH6KL_DEBUG + struct { + struct sk_buff_head fwlog_queue; + struct completion fwlog_completion; + bool fwlog_open; + u32 fwlog_mask; + unsigned int dbgfs_diag_reg; + u32 diag_reg_addr_wr; + u32 diag_reg_val_wr; + u64 set_tx_series; + + struct { + unsigned int invalid_rate; + } war_stats; + + u8 *roam_tbl; + unsigned int roam_tbl_len; + + u8 keepalive; + u8 disc_timeout; + u8 mimo_ps_enable; + u8 force_passive; + u16 bgscan_int; + enum wmi_roam_mode roam_mode; + + struct smps_param { + u8 flags; + u8 rssi_thresh; + u8 data_thresh; + u8 mode; + u8 automatic; + } smps_params; + + struct lpl_force_enable_param { + u8 lpl_policy; + u8 no_blocker_detect; + u8 no_rfb_detect; + u8 rsvd; + } lpl_force_enable_params; + + struct power_param { + u16 idle_period; + u16 ps_poll_num; + u16 dtim; + u16 tx_wakeup; + u16 num_tx; + } power_params; + + struct ht_cap_param { + u8 isConfig; + u8 band; + u8 chan_width_40M_supported; + u8 short_GI; + u8 intolerance_40MHz; + } ht_cap_param[IEEE80211_NUM_BANDS]; + } debug; +#endif /* CONFIG_ATH6KL_DEBUG */ + + int wow_irq; +#ifdef CONFIG_ANDROID +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock wake_lock; +#endif /* CONFIG_HAS_WAKELOCK */ +#endif + struct ath6kl_p2p_flowctrl *p2p_flowctrl_ctx; + struct ath6kl_p2p_rc_info *p2p_rc_info_ctx; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +#define INIT_DEFER_WAIT_TIMEOUT (5 * HZ) + struct work_struct init_defer_wk; + wait_queue_head_t init_defer_wait_wq; + + u32 tx_on_vif; + + struct reg_info *reg_ctx; + + void (*fw_crash_notify)(struct ath6kl *ar); + + u32 roam_mode; + u32 bootstrap_mode; +#ifdef USB_AUTO_SUSPEND + struct usb_pm_skb_queue_t usb_pm_skb_queue; + spinlock_t usb_pm_lock; + unsigned long usb_autopm_scan; + int auto_pm_cnt; + +#endif + struct wmi_green_tx_params green_tx_params; +}; + +static inline void *ath6kl_priv(struct net_device *dev) +{ + return ((struct ath6kl_vif *) netdev_priv(dev))->ar; +} + +static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar, + u32 item_offset) +{ + u32 addr = 0; + + if (ar->target_type == TARGET_TYPE_AR6003) + addr = ATH6KL_AR6003_HI_START_ADDR + item_offset; + else if (ar->target_type == TARGET_TYPE_AR6004) + addr = ATH6KL_AR6004_HI_START_ADDR + item_offset; + else if (ar->target_type == TARGET_TYPE_AR6006) + addr = ATH6KL_AR6006_HI_START_ADDR + item_offset; + + return addr; +} + +static inline u32 ath6kl_ps_queue_get_age(struct ath6kl_ps_buf_desc *ps_buf) +{ + return ps_buf->age; +} + +static inline void ath6kl_ps_queue_set_age(struct ath6kl_ps_buf_desc *ps_buf, + u32 age) +{ + ps_buf->age = age; +} + +static inline void ath6kl_fw_crash_trap(struct ath6kl *ar) +{ + /* Notify to usr */ + if (ar->fw_crash_notify) + ar->fw_crash_notify(ar); +} + +static inline bool ath6kl_is_p2p_ie(const u8 *pos) +{ + return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + pos[2] == 0x50 && pos[3] == 0x6f && + pos[4] == 0x9a && pos[5] == 0x09; +} + +static inline bool ath6kl_is_wfd_ie(const u8 *pos) +{ + return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + pos[2] == 0x50 && pos[3] == 0x6f && + pos[4] == 0x9a && pos[5] == 0x0a; +} + +int ath6kl_configure_target(struct ath6kl *ar); +void ath6kl_detect_error(unsigned long ptr); +void disconnect_timer_handler(unsigned long ptr); +void init_netdev(struct net_device *dev); +int ath6kl_cookie_init(struct ath6kl *ar); +void ath6kl_cookie_cleanup(struct ath6kl *ar); +void ath6kl_rx(struct htc_target *target, struct htc_packet *packet); +void ath6kl_tx_complete(struct htc_target *context, + struct list_head *packet_queue); +enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, + struct htc_packet *packet); +void ath6kl_stop_txrx(struct ath6kl *ar); +void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar); +int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value); +int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length); +int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value); +int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length); +int ath6kl_read_fwlogs(struct ath6kl *ar); +void ath6kl_init_profile_info(struct ath6kl_vif *vif); +void ath6kl_tx_data_cleanup(struct ath6kl *ar); +void ath6kl_tx_data_cleanup_by_if(struct ath6kl_vif *vif); + +struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar, + enum cookie_type cookie_type); +void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie); +bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, u32 id, u32 freq, + u32 wait, const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u32 *flags); +int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev, + bool bypass_tx_aggr); +int ath6kl_start_tx(struct sk_buff *skb, struct net_device *dev); + +void aggr_tx_config(struct ath6kl_vif *vif, + bool tx_amsdu_seq_pkt, + bool tx_amsdu_progressive, + u8 tx_amsdu_max_aggr_num, + u16 tx_amsdu_max_pdu_len, + u16 tx_amsdu_timeout); +void aggr_config(struct ath6kl_vif *vif, + u16 rx_aggr_timeout); +struct aggr_info *aggr_init(struct ath6kl_vif *vif); +struct aggr_conn_info *aggr_init_conn(struct ath6kl_vif *vif); + +void ath6kl_rx_refill(struct htc_target *target, + enum htc_endpoint_id endpoint); +void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count); +struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target, + enum htc_endpoint_id endpoint, + int len); + +void aggr_module_destroy(struct aggr_info *aggr); +void aggr_module_destroy_conn(struct aggr_conn_info *aggr_conn); +void aggr_reset_state(struct aggr_conn_info *aggr_conn); + +struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 * node_addr); +struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl_vif *vif, u8 aid); + +void ath6kl_ready_event(void *devt, u8 * datap, u32 sw_ver, u32 abi_ver); +int ath6kl_control_tx(void *devt, struct sk_buff *skb, + enum htc_endpoint_id eid); +void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, + u8 *bssid, u16 listen_int, + u16 beacon_int, enum network_type net_type, + u8 beacon_ie_len, u8 assoc_req_len, + u8 assoc_resp_len, u8 *assoc_info); +void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel, + u8 *beacon, u8 beacon_len); +void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u8 aid, u8 *mac_addr, + u8 keymgmt, u8 ucipher, u8 auth, + u16 assoc_req_len, u8 *assoc_info, + u8 apsd_info, u8 phymode); +void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, + u8 *bssid, u8 assoc_resp_len, + u8 *assoc_info, u16 prot_reason_status); +void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast); +void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr); +void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status); +void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len); +void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active); +enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac); + +void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid); + +void ath6kl_dtimexpiry_event(struct ath6kl_vif *vif); +int ath6kl_disconnect(struct ath6kl_vif *vif); +void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid, u8 initiator); +void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid, u16 seq_no, + u8 win_sz); +void aggr_recv_addba_resp_evt(struct ath6kl_vif *vif, u8 tid, + u16 amsdu_sz, u8 status); +void ath6kl_wakeup_event(void *dev); + +void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, + bool wait_fot_compltn, bool cold_reset); +void ath6kl_init_control_info(struct ath6kl_vif *vif); +void ath6kl_deinit_if_data(struct ath6kl_vif *vif); +void ath6kl_core_free(struct ath6kl *ar); +struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar); +void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready); +int ath6kl_init_hw_start(struct ath6kl *ar); +int ath6kl_init_hw_stop(struct ath6kl *ar); +void ath6kl_check_wow_status(struct ath6kl *ar); +void ath6kl_htc_pipe_attach(struct ath6kl *ar); +void ath6kl_htc_mbox_attach(struct ath6kl *ar); + +void ath6kl_ps_queue_init(struct ath6kl_ps_buf_head *psq, + enum ps_queue_type queue_type, + u32 age_cycle, + u32 max_depth); +void ath6kl_ps_queue_purge(struct ath6kl_ps_buf_head *psq); +int ath6kl_ps_queue_empty(struct ath6kl_ps_buf_head *psq); +int ath6kl_ps_queue_depth(struct ath6kl_ps_buf_head *psq); +void ath6kl_ps_queue_stat(struct ath6kl_ps_buf_head *psq, int *depth, + u32 *enqueued, u32 *enqueued_err, u32 *dequeued, u32 *aged); +struct ath6kl_ps_buf_desc *ath6kl_ps_queue_dequeue( + struct ath6kl_ps_buf_head *psq); +int ath6kl_ps_queue_enqueue_mgmt(struct ath6kl_ps_buf_head *psq, const u8 *buf, + u16 len, u32 id, u32 freq, u32 wait, bool no_cck, + bool dont_wait_for_ack); +int ath6kl_ps_queue_enqueue_data(struct ath6kl_ps_buf_head *psq, + struct sk_buff *skb); +void ath6kl_ps_queue_age_handler(unsigned long ptr); +void ath6kl_ps_queue_age_start(struct ath6kl_sta *conn); +void ath6kl_ps_queue_age_stop(struct ath6kl_sta *conn); + +#ifdef CONFIG_ANDROID_8960_SDIO +void ath6kl_sdio_init_msm(void); +void ath6kl_sdio_exit_msm(void); +#endif + +#ifdef ATH6KL_BUS_VOTE +int ath6kl_hsic_init_msm(void); +void ath6kl_hsic_exit_msm(void); +#endif + +void ath6kl_fw_crash_notify(struct ath6kl *ar); +void ath6kl_indicate_wmm_schedule_change(void *devt, bool active); +int _string_to_mac(char *string, int len, u8 *macaddr); + +#if defined(CONFIG_ANDROID) || defined(USB_AUTO_SUSPEND) +int ath6kl_android_enable_wow_default(struct ath6kl *ar); +bool ath6kl_android_need_wow_suspend(struct ath6kl *ar); +#endif + +#ifdef ATH6KL_SUPPORT_WLAN_HB +int ath6kl_enable_wow_hb(struct ath6kl *ar); +#endif + +int ath6kl_fw_watchdog_enable(struct ath6kl *ar); +int ath6kl_fw_crash_cold_reset_enable(struct ath6kl *ar); + +extern unsigned int htc_bundle_recv; +extern unsigned int htc_bundle_send; +extern unsigned int htc_bundle_send_timer; +extern unsigned int htc_bundle_send_th; +#ifdef CE_SUPPORT +extern unsigned int ath6kl_ce_flags; +#endif +#endif /* CORE_H */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/debug.c b/drivers/net/wireless/ath/ath6kl-3.5/debug.c new file mode 100644 index 000000000000..6e1833308e7d --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/debug.c @@ -0,0 +1,5220 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" + +#include +#include +#include +#include + +#include "debug.h" +#include "target.h" +#include "wlan_location_defs.h" +#include "htc-ops.h" +#include "hif-ops.h" +#include + + +struct ath6kl_fwlog_slot { + __le32 timestamp; + __le32 length; + + /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */ + u8 payload[0]; +}; + +#define SKIP_SPACE \ + while (*p == ' ' && *p != '\0') \ + p++; + +#define SEEK_SPACE \ + while (*p != ' ' && *p != '\0') \ + p++; + +#define ATH6KL_FWLOG_MAX_ENTRIES 20 +#define ATH6KL_FWLOG_VALID_MASK 0x1ffff + +#define ATH6KL_FWLOG_NUM_ARGS_OFFSET 30 +#define ATH6KL_FWLOG_NUM_ARGS_MASK 0xC0000000 /* Bit 30-31 */ +#define AT6HKL_FWLOG_NUM_ARGS_MAX 2 /* Upper limit is width of mask */ + +#define ATH6KL_FWGLOG_GET_NUMARGS(arg) \ + ((arg & ATH6KL_FWLOG_NUM_ARGS_MASK) >> ATH6KL_FWLOG_NUM_ARGS_OFFSET) + +int ath6kl_printk(const char *level, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int rtn; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + rtn = printk(KERN_INFO "%sath6kl: %pV", level, &vaf); + + va_end(args); + + return rtn; +} + +#define EVENT_ID_LEN 2 +#define INTF_ID_LEN 1 + +void ath6kl_send_genevent_to_app(struct net_device *dev, + u16 event_id, u8 ifid, + u8 *datap, int len) +{ + char *buf; + u16 size; + union iwreq_data wrqu; + + size = len + EVENT_ID_LEN + INTF_ID_LEN; + + if (size > IW_GENERIC_IE_MAX) + return; + + buf = kmalloc(size, GFP_ATOMIC); + if (buf == NULL) + return; + + memset(buf, 0, size); + memcpy(buf, &event_id, EVENT_ID_LEN); + memcpy(buf + EVENT_ID_LEN, &ifid, INTF_ID_LEN); + memcpy(buf + EVENT_ID_LEN + INTF_ID_LEN, datap, len); + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = size; + wireless_send_event(dev, IWEVGENIE, &wrqu, buf); + kfree(buf); + +} + +void ath6kl_send_event_to_app(struct net_device *dev, + u16 event_id, u8 ifid, + u8 *datap, int len) +{ + char *buf; + u16 size; + union iwreq_data wrqu; + + size = len + EVENT_ID_LEN + INTF_ID_LEN; + + if (size > IW_CUSTOM_MAX) + return; + + buf = kmalloc(size, GFP_ATOMIC); + if (buf == NULL) + return; + + memset(buf, 0, size); + memcpy(buf, &event_id, EVENT_ID_LEN); + memcpy(buf + EVENT_ID_LEN, &ifid, INTF_ID_LEN); + memcpy(buf + EVENT_ID_LEN + INTF_ID_LEN, datap, len); + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = size; + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); + kfree(buf); +} + + +#ifdef CONFIG_ATH6KL_DEBUG + +#define REG_OUTPUT_LEN_PER_LINE 25 +#define REGTYPE_STR_LEN 100 + +struct ath6kl_diag_reg_info { + u32 reg_start; + u32 reg_end; + const char *reg_info; +}; + +static const struct ath6kl_diag_reg_info diag_reg[] = { + { 0x20000, 0x200fc, "General DMA and Rx registers" }, + { 0x28000, 0x28900, "MAC PCU register & keycache" }, + { 0x20800, 0x20a40, "QCU" }, + { 0x21000, 0x212f0, "DCU" }, + { 0x4000, 0x42e4, "RTC" }, + { 0x540000, 0x540000 + (256 * 1024), "RAM" }, + { 0x29800, 0x2B210, "Base Band" }, + { 0x1C000, 0x1C748, "Analog" }, + { 0x5000, 0x5160, "WLAN RTC" }, + { 0x14000, 0x14170, "GPIO" }, + { 0x7000, 0x7090, "BTC" }, +}; + +void ath6kl_dump_registers(struct ath6kl_device *dev, + struct ath6kl_irq_proc_registers *irq_proc_reg, + struct ath6kl_irq_enable_reg *irq_enable_reg) +{ + + ath6kl_dbg(ATH6KL_DBG_ANY, ("<------- Register Table -------->\n")); + + if (irq_proc_reg != NULL) { + ath6kl_dbg(ATH6KL_DBG_ANY, + "Host Int status: 0x%x\n", + irq_proc_reg->host_int_status); + ath6kl_dbg(ATH6KL_DBG_ANY, + "CPU Int status: 0x%x\n", + irq_proc_reg->cpu_int_status); + ath6kl_dbg(ATH6KL_DBG_ANY, + "Error Int status: 0x%x\n", + irq_proc_reg->error_int_status); + ath6kl_dbg(ATH6KL_DBG_ANY, + "Counter Int status: 0x%x\n", + irq_proc_reg->counter_int_status); + ath6kl_dbg(ATH6KL_DBG_ANY, + "Mbox Frame: 0x%x\n", + irq_proc_reg->mbox_frame); + ath6kl_dbg(ATH6KL_DBG_ANY, + "Rx Lookahead Valid: 0x%x\n", + irq_proc_reg->rx_lkahd_valid); + ath6kl_dbg(ATH6KL_DBG_ANY, + "Rx Lookahead 0: 0x%x\n", + le32_to_cpu(irq_proc_reg->rx_lkahd[0])); + ath6kl_dbg(ATH6KL_DBG_ANY, + "Rx Lookahead 1: 0x%x\n", + le32_to_cpu(irq_proc_reg->rx_lkahd[1])); + + if (dev->ar->mbox_info.gmbox_addr != 0) { + /* + * If the target supports GMBOX hardware, dump some + * additional state. + */ + ath6kl_dbg(ATH6KL_DBG_ANY, + "GMBOX Host Int status 2: 0x%x\n", + irq_proc_reg->host_int_status2); + ath6kl_dbg(ATH6KL_DBG_ANY, + "GMBOX RX Avail: 0x%x\n", + irq_proc_reg->gmbox_rx_avail); + ath6kl_dbg(ATH6KL_DBG_ANY, + "GMBOX lookahead alias 0: 0x%x\n", + le32_to_cpu( + irq_proc_reg->rx_gmbox_lkahd_alias[0])); + ath6kl_dbg(ATH6KL_DBG_ANY, + "GMBOX lookahead alias 1: 0x%x\n", + le32_to_cpu( + irq_proc_reg->rx_gmbox_lkahd_alias[1])); + } + + } + + if (irq_enable_reg != NULL) { + ath6kl_dbg(ATH6KL_DBG_ANY, + "Int status Enable: 0x%x\n", + irq_enable_reg->int_status_en); + ath6kl_dbg(ATH6KL_DBG_ANY, "Counter Int status Enable: 0x%x\n", + irq_enable_reg->cntr_int_status_en); + } + ath6kl_dbg(ATH6KL_DBG_ANY, "<------------------------------->\n"); +} + +static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist) +{ + ath6kl_dbg(ATH6KL_DBG_CREDIT, + "--- endpoint: %d svc_id: 0x%X ---\n", + ep_dist->endpoint, ep_dist->svc_id); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " dist_flags : 0x%X\n", + ep_dist->dist_flags); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_norm : %d\n", + ep_dist->cred_norm); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_min : %d\n", + ep_dist->cred_min); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " credits : %d\n", + ep_dist->credits); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_assngd : %d\n", + ep_dist->cred_assngd); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " seek_cred : %d\n", + ep_dist->seek_cred); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_sz : %d\n", + ep_dist->cred_sz); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_per_msg : %d\n", + ep_dist->cred_per_msg); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_to_dist : %d\n", + ep_dist->cred_to_dist); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_alloc_max : %d\n", + ep_dist->cred_alloc_max); + ath6kl_dbg(ATH6KL_DBG_CREDIT, " txq_depth : %d\n", + get_queue_depth(&ep_dist->htc_ep->txq)); + ath6kl_dbg(ATH6KL_DBG_CREDIT, + "----------------------------------\n"); +} + +/* FIXME: move to htc.c */ +void dump_cred_dist_stats(struct htc_target *target) +{ + struct htc_endpoint_credit_dist *ep_list; + + if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_CREDIT)) + return; + + list_for_each_entry(ep_list, &target->cred_dist_list, list) + dump_cred_dist(ep_list); + + ath6kl_dbg(ATH6KL_DBG_CREDIT, + "credit distribution total %d free %d\n", + target->credit_info->total_avail_credits, + target->credit_info->cur_free_credits); +} + +static int ath6kl_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) +{ + switch (war) { + case ATH6KL_WAR_INVALID_RATE: + ar->debug.war_stats.invalid_rate++; + break; + } +} + +static ssize_t read_file_war_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char *buf; + unsigned int len = 0, buf_len = 1500; + ssize_t ret_cnt; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Workaround stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n\n", + "================="); + len += scnprintf(buf + len, buf_len - len, "%20s %10u\n", + "Invalid rates", ar->debug.war_stats.invalid_rate); + + if (WARN_ON(len > buf_len)) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_war_stats = { + .read = read_file_war_stats, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + + +static u32 +ath6kl_fwlog_fragment(const u8 *datap, u32 len, u32 limit) +{ + u32 *buffer; + u32 count; + u32 numargs; + u32 length; + u32 fraglen; + + count = fraglen = 0; + buffer = (u32 *)datap; + length = (limit >> 2); + + if (len <= limit) { + fraglen = len; + } else { + while (count < length) { + numargs = ATH6KL_FWGLOG_GET_NUMARGS(buffer[count]); + fraglen = (count << 2); + count += numargs + 1; + } + } + + return fraglen; +} + + +static void +ath6kl_debug_fwlog_event_send(struct ath6kl *ar, const u8 *buffer, u32 length) +{ +#define MAX_WIRELESS_EVENT_SIZE 252 + + /* + * Break it up into chunks of MAX_WIRELESS_EVENT_SIZE bytes of messages. + * There seems to be a limitation on the length of message that could be + * transmitted to the user app via this mechanism. + */ + u32 send, sent; + struct ath6kl_vif *vif; + struct net_device *dev; + + vif = ath6kl_vif_first(ar); + + /* should always get value by ath6kl_vif_first */ + if (!vif) + return; + + dev = vif->ndev; + + sent = 0; + send = ath6kl_fwlog_fragment(&buffer[sent], length - sent, + MAX_WIRELESS_EVENT_SIZE); + while (send) { + ath6kl_send_event_to_app(dev, WMIX_DBGLOG_EVENTID, + vif->fw_vif_idx, (u8 *)&buffer[sent], send); + sent += send; + send = ath6kl_fwlog_fragment(&buffer[sent], length - sent, + MAX_WIRELESS_EVENT_SIZE); + } +} + + +void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) +{ + struct ath6kl_fwlog_slot *slot; + struct sk_buff *skb; + size_t slot_len; + + if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE)) + return; + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_ENABLE_FWLOG_EXT)) + ath6kl_debug_fwlog_event_send(ar, buf, len); + + slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE; + + skb = alloc_skb(slot_len, GFP_KERNEL); + if (!skb) + return; + + slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len); + + slot->timestamp = cpu_to_le32(jiffies); + slot->length = cpu_to_le32(len); + memcpy(slot->payload, buf, len); + + /* Need to pad each record to fixed length ATH6KL_FWLOG_PAYLOAD_SIZE */ + memset(slot->payload + len, 0, ATH6KL_FWLOG_PAYLOAD_SIZE - len); + + spin_lock(&ar->debug.fwlog_queue.lock); + + __skb_queue_tail(&ar->debug.fwlog_queue, skb); + complete(&ar->debug.fwlog_completion); + + /* drop oldest entries */ + while (skb_queue_len(&ar->debug.fwlog_queue) > + ATH6KL_FWLOG_MAX_ENTRIES) { + skb = __skb_dequeue(&ar->debug.fwlog_queue); + kfree_skb(skb); + } + + spin_unlock(&ar->debug.fwlog_queue.lock); + +} + +static int ath6kl_fwlog_open(struct inode *inode, struct file *file) +{ + struct ath6kl *ar = inode->i_private; + + if (ar->debug.fwlog_open) + return -EBUSY; + + ar->debug.fwlog_open = true; + + file->private_data = inode->i_private; + return 0; +} + +static int ath6kl_fwlog_release(struct inode *inode, struct file *file) +{ + struct ath6kl *ar = inode->i_private; + + ar->debug.fwlog_open = false; + + return 0; +} + + +static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct sk_buff *skb; + size_t len = 0; + + ssize_t ret_cnt; + char *buf; + + buf = vmalloc(count); + if (!buf) + return -ENOMEM; + + /* read undelivered logs from firmware */ + ath6kl_read_fwlogs(ar); + + spin_lock(&ar->debug.fwlog_queue.lock); + + while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { + if (skb->len > count - len) { + /* not enough space, put skb back and leave */ + __skb_queue_head(&ar->debug.fwlog_queue, skb); + break; + } + + memcpy(buf + len, skb->data, skb->len); + len += skb->len; + + kfree_skb(skb); + } + + spin_unlock(&ar->debug.fwlog_queue.lock); + + /* FIXME: what to do if len == 0? */ + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + vfree(buf); + + return ret_cnt; +} + +static const struct file_operations fops_fwlog = { + .open = ath6kl_fwlog_open, + .read = ath6kl_fwlog_read, + .release = ath6kl_fwlog_release, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_fwlog_block_read(struct file *file, + char __user *user_buf, + size_t count, + loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct sk_buff *skb; + ssize_t ret_cnt; + size_t len = 0, not_copied; + char *buf; + int ret; + + buf = vmalloc(count); + if (!buf) + return -ENOMEM; + + spin_lock(&ar->debug.fwlog_queue.lock); + + if (skb_queue_len(&ar->debug.fwlog_queue) == 0) { + /* we must init under queue lock */ + init_completion(&ar->debug.fwlog_completion); + + spin_unlock(&ar->debug.fwlog_queue.lock); + + ret = wait_for_completion_interruptible( + &ar->debug.fwlog_completion); + if (ret == -ERESTARTSYS) { + vfree(buf); + return ret; + } + + spin_lock(&ar->debug.fwlog_queue.lock); + } + + while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { + if (skb->len > count - len) { + /* not enough space, put skb back and leave */ + __skb_queue_head(&ar->debug.fwlog_queue, skb); + break; + } + + + memcpy(buf + len, skb->data, skb->len); + len += skb->len; + + kfree_skb(skb); + } + + spin_unlock(&ar->debug.fwlog_queue.lock); + + /* FIXME: what to do if len == 0? */ + not_copied = copy_to_user(user_buf, buf, len); + if (not_copied != 0) { + ret_cnt = -EFAULT; + goto out; + } + + *ppos = *ppos + len; + + ret_cnt = len; + +out: + vfree(buf); + + return ret_cnt; +} + +static const struct file_operations fops_fwlog_block = { + .open = ath6kl_fwlog_open, + .release = ath6kl_fwlog_release, + .read = ath6kl_fwlog_block_read, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + + +static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[16]; + int len; + + len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_fwlog_mask_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + + ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask); + if (ret) + return ret; + + ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi, + ATH6KL_FWLOG_VALID_MASK, + ar->debug.fwlog_mask); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_fwlog_mask = { + .open = ath6kl_debugfs_open, + .read = ath6kl_fwlog_mask_read, + .write = ath6kl_fwlog_mask_write, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + struct target_stats *tgt_stats; + char *buf; + unsigned int len = 0, buf_len = 1500; + int i; + long left; + ssize_t ret_cnt; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + tgt_stats = &vif->target_stats; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) { + kfree(buf); + return -ENOMEM; + } + + if (down_interruptible(&ar->sem)) { + kfree(buf); + return -EBUSY; + } + + set_bit(STATS_UPDATE_PEND, &vif->flags); + + if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) { + up(&ar->sem); + kfree(buf); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(STATS_UPDATE_PEND, + &vif->flags), WMI_TIMEOUT); + + up(&ar->sem); + + if (left <= 0) { + kfree(buf); + return -ETIMEDOUT; + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Target Tx stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n\n", + "================="); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Ucast packets", tgt_stats->tx_ucast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Bcast packets", tgt_stats->tx_bcast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Mcast packets", tgt_stats->tx_mcast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Ucast byte", tgt_stats->tx_ucast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Bcast byte", tgt_stats->tx_bcast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Mcast byte", tgt_stats->tx_mcast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Rts success cnt", tgt_stats->tx_rts_success_cnt); + for (i = 0; i < 4; i++) + len += scnprintf(buf + len, buf_len - len, + "%18s %d %10llu\n", "PER on ac", + i, tgt_stats->tx_pkt_per_ac[i]); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Error", tgt_stats->tx_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Fail count", tgt_stats->tx_fail_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Retry count", tgt_stats->tx_retry_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Multi retry cnt", tgt_stats->tx_mult_retry_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Rts fail cnt", tgt_stats->tx_rts_fail_cnt); + len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n", + "TKIP counter measure used", + tgt_stats->tkip_cnter_measures_invoked); + + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Target Rx stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Ucast packets", tgt_stats->rx_ucast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "Ucast Rate", tgt_stats->rx_ucast_rate); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Bcast packets", tgt_stats->rx_bcast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Mcast packets", tgt_stats->rx_mcast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Ucast byte", tgt_stats->rx_ucast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Bcast byte", tgt_stats->rx_bcast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Mcast byte", tgt_stats->rx_mcast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Fragmented pkt", tgt_stats->rx_frgment_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Error", tgt_stats->rx_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "CRC Err", tgt_stats->rx_crc_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Key chache miss", tgt_stats->rx_key_cache_miss); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Decrypt Err", tgt_stats->rx_decrypt_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Duplicate frame", tgt_stats->rx_dupl_frame); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Tkip Mic failure", tgt_stats->tkip_local_mic_fail); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "TKIP format err", tgt_stats->tkip_fmt_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "CCMP format Err", tgt_stats->ccmp_fmt_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n", + "CCMP Replay Err", tgt_stats->ccmp_replays); + + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Misc Target stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "================="); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Beacon Miss count", tgt_stats->cs_bmiss_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Num Connects", tgt_stats->cs_connect_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Num disconnects", tgt_stats->cs_discon_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi); + + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Wow stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "================="); + len += scnprintf(buf + len, buf_len - len, "%20s %10u\n", + "Wow pkt dropped", tgt_stats->wow_pkt_dropped); + len += scnprintf(buf + len, buf_len - len, "%20s %10u\n", + "Wow evt discarded", tgt_stats->wow_evt_discarded); + len += scnprintf(buf + len, buf_len - len, "%20s %10u\n", + "Wow host pkt wakeup", tgt_stats->wow_host_pkt_wakeups); + len += scnprintf(buf + len, buf_len - len, "%20s %10u\n", + "Wow host evt wakeups", tgt_stats->wow_host_evt_wakeups); + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_tgt_stats = { + .read = read_file_tgt_stats, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +#define print_credit_info(fmt_str, ep_list_field) \ + (len += scnprintf(buf + len, buf_len - len, fmt_str, \ + ep_list->ep_list_field)) +#define CREDIT_INFO_DISPLAY_STRING_LEN 200 +#define CREDIT_INFO_LEN 128 + +static ssize_t read_file_credit_dist_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct htc_target *target = ar->htc_target; + struct htc_endpoint_credit_dist *ep_list; + char *buf; + unsigned int buf_len, len = 0; + ssize_t ret_cnt; + struct list_head *list_head = &target->cred_dist_list; + + if (WARN_ON(list_head->next == NULL)) + return -EFAULT; + + buf_len = CREDIT_INFO_DISPLAY_STRING_LEN + + get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN; + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, buf_len - len, "%25s%5d\n", + "Total Avail Credits: ", + target->credit_info->total_avail_credits); + len += scnprintf(buf + len, buf_len - len, "%25s%5d\n", + "Free credits :", + target->credit_info->cur_free_credits); + + len += scnprintf(buf + len, buf_len - len, + " Epid Flags Cred_norm Cred_min Credits Cred_assngd" + " Seek_cred Cred_sz Cred_per_msg Cred_to_dist" + " Cred_alloc_max qdepth\n"); + + list_for_each_entry(ep_list, &target->cred_dist_list, list) { + print_credit_info(" %2d", endpoint); + print_credit_info("%10x", dist_flags); + print_credit_info("%8d", cred_norm); + print_credit_info("%9d", cred_min); + print_credit_info("%9d", credits); + print_credit_info("%10d", cred_assngd); + print_credit_info("%13d", seek_cred); + print_credit_info("%12d", cred_sz); + print_credit_info("%9d", cred_per_msg); + print_credit_info("%14d", cred_to_dist); + print_credit_info("%17d", cred_alloc_max); + len += scnprintf(buf + len, buf_len - len, "%12d\n", + get_queue_depth(&ep_list->htc_ep->txq)); + } + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_credit_dist_stats = { + .read = read_file_credit_dist_stats, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static unsigned int print_endpoint_stat(struct htc_target *target, char *buf, + unsigned int buf_len, unsigned int len, + int offset, const char *name) +{ + int i; + struct htc_endpoint_stats *ep_st; + u32 *counter; + + len += scnprintf(buf + len, buf_len - len, "%s:", name); + for (i = 0; i < ENDPOINT_MAX; i++) { + ep_st = &target->endpoint[i].ep_st; + counter = ((u32 *) ep_st) + (offset / 4); + len += scnprintf(buf + len, buf_len - len, " %u", *counter); + } + len += scnprintf(buf + len, buf_len - len, "\n"); + + return len; +} + +static ssize_t ath6kl_endpoint_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct htc_target *target = ar->htc_target; + char *buf; + unsigned int buf_len, len = 0; + ssize_t ret_cnt; + + buf_len = sizeof(struct htc_endpoint_stats) / sizeof(u32) * + (25 + ENDPOINT_MAX * 11); + buf = kmalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + +#define EPSTAT(name) \ + len = print_endpoint_stat(target, buf, buf_len, len, \ + offsetof(struct htc_endpoint_stats, name), \ + #name) + EPSTAT(cred_low_indicate); + EPSTAT(tx_issued); + EPSTAT(tx_pkt_bundled); + EPSTAT(tx_bundles); + EPSTAT(tx_dropped); + EPSTAT(tx_cred_rpt); + EPSTAT(cred_rpt_from_rx); + EPSTAT(cred_rpt_from_other); + EPSTAT(cred_rpt_ep0); + EPSTAT(cred_from_rx); + EPSTAT(cred_from_other); + EPSTAT(cred_from_ep0); + EPSTAT(cred_cosumd); + EPSTAT(cred_retnd); + EPSTAT(rx_pkts); + EPSTAT(rx_lkahds); + EPSTAT(rx_bundl); + EPSTAT(rx_bundle_lkahd); + EPSTAT(rx_bundle_from_hdr); + EPSTAT(rx_alloc_thresh_hit); + EPSTAT(rxalloc_thresh_byte); +#undef EPSTAT + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret_cnt; +} + +static ssize_t ath6kl_endpoint_stats_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct htc_target *target = ar->htc_target; + int ret, i; + u32 val; + struct htc_endpoint_stats *ep_st; + + ret = kstrtou32_from_user(user_buf, count, 0, &val); + if (ret) + return ret; + if (val == 0) { + for (i = 0; i < ENDPOINT_MAX; i++) { + ep_st = &target->endpoint[i].ep_st; + memset(ep_st, 0, sizeof(*ep_st)); + } + } + + return count; +} + +static const struct file_operations fops_endpoint_stats = { + .open = ath6kl_debugfs_open, + .read = ath6kl_endpoint_stats_read, + .write = ath6kl_endpoint_stats_write, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static unsigned long ath6kl_get_num_reg(void) +{ + int i; + unsigned long n_reg = 0; + + for (i = 0; i < ARRAY_SIZE(diag_reg); i++) + n_reg = n_reg + + (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1; + + return n_reg; +} + +static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { + if (reg_addr >= diag_reg[i].reg_start && + reg_addr <= diag_reg[i].reg_end) + return true; + } + + return true; +} + +static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[50]; + unsigned int len = 0; + + if (ar->debug.dbgfs_diag_reg) + len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", + ar->debug.dbgfs_diag_reg); + else + len += scnprintf(buf + len, sizeof(buf) - len, + "All diag registers\n"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_regread_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[50]; + unsigned int len; + unsigned long reg_addr; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (kstrtoul(buf, 0, ®_addr)) + return -EINVAL; + + if ((reg_addr % 4) != 0) + return -EINVAL; + + if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr)) + return -EINVAL; + + ar->debug.dbgfs_diag_reg = reg_addr; + + return count; +} + +static const struct file_operations fops_diag_reg_read = { + .read = ath6kl_regread_read, + .write = ath6kl_regread_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_regdump_open(struct inode *inode, struct file *file) +{ + struct ath6kl *ar = inode->i_private; + u8 *buf; + unsigned long int reg_len; + unsigned int len = 0, n_reg; + u32 addr; + __le32 reg_val; + int i, status; + + /* Dump all the registers if no register is specified */ + if (!ar->debug.dbgfs_diag_reg) + n_reg = ath6kl_get_num_reg(); + else + n_reg = 1; + + reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE; + if (n_reg > 1) + reg_len += REGTYPE_STR_LEN; + + buf = vmalloc(reg_len); + if (!buf) + return -ENOMEM; + + if (n_reg == 1) { + addr = ar->debug.dbgfs_diag_reg; + + status = ath6kl_diag_read32(ar, + TARG_VTOP(ar->target_type, addr), + (u32 *)®_val); + if (status) + goto fail_reg_read; + + len += scnprintf(buf + len, reg_len - len, + "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val)); + goto done; + } + + for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { + len += scnprintf(buf + len, reg_len - len, + "%s\n", diag_reg[i].reg_info); + for (addr = diag_reg[i].reg_start; + addr <= diag_reg[i].reg_end; addr += 4) { + status = ath6kl_diag_read32(ar, + TARG_VTOP(ar->target_type, addr), + (u32 *)®_val); + if (status) + goto fail_reg_read; + + len += scnprintf(buf + len, reg_len - len, + "0x%06x 0x%08x\n", + addr, le32_to_cpu(reg_val)); + } + } + +done: + file->private_data = buf; + return 0; + +fail_reg_read: + ath6kl_warn("Unable to read memory:%u\n", addr); + vfree(buf); + return -EIO; +} + +static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + u8 *buf = file->private_data; + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +static int ath6kl_regdump_release(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + return 0; +} + +static const struct file_operations fops_reg_dump = { + .open = ath6kl_regdump_open, + .read = ath6kl_regdump_read, + .release = ath6kl_regdump_release, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_set_roam_params(const char __user *user_buf, + size_t count, + struct low_rssi_scan_params *low_rssi_roam_params) +{ + char buf[64]; + char *p; + unsigned int len; + int value; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + low_rssi_roam_params->lrssi_scan_period = (u16) value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + low_rssi_roam_params->lrssi_scan_threshold = (u16) value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + low_rssi_roam_params->lrssi_roam_threshold = (u16) value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + low_rssi_roam_params->roam_rssi_floor = (u8) value; + + return 0; +} + + +static ssize_t ath6kl_lrssi_roam_write(struct file *file, + const char __user *user_buf, + size_t count, + loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + + ret = ath6kl_set_roam_params(user_buf, count, + &ar->low_rssi_roam_params); + + if (ret) + return ret; + + ath6kl_wmi_set_roam_ctrl_cmd_for_lowerrssi(ar->wmi, + ar->low_rssi_roam_params.lrssi_scan_period, + ar->low_rssi_roam_params.lrssi_scan_threshold, + ar->low_rssi_roam_params.lrssi_roam_threshold, + ar->low_rssi_roam_params.roam_rssi_floor); + + return count; +} + +static ssize_t ath6kl_lrssi_roam_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (128) + struct ath6kl *ar = file->private_data; + u8 *buf; + unsigned int len = 0; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, _BUF_SIZE - len, + "scan_period(%d ms, scan_th %d, roam_th %d, rssi_flo %d)\n ", + ar->low_rssi_roam_params.lrssi_scan_period, + ar->low_rssi_roam_params.lrssi_scan_threshold, + ar->low_rssi_roam_params.lrssi_roam_threshold, + ar->low_rssi_roam_params.roam_rssi_floor); + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +static const struct file_operations fops_lrssi_roam_param = { + .read = ath6kl_lrssi_roam_read, + .write = ath6kl_lrssi_roam_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_driver_version_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[64]; + unsigned int len; + + len = snprintf(buf, sizeof(buf), "%s\n", DRV_VERSION); + len += snprintf(buf + len, sizeof(buf) - len, "%s %s\n", + ar->hw.name, + (ar->hif_type == ATH6KL_HIF_TYPE_SDIO) ? + "SDIO" : + ((ar->hif_type == ATH6KL_HIF_TYPE_USB) ? + "USB" : "UNKNOW")); + len += snprintf(buf + len, sizeof(buf) - len, "%s\n", + ar->wiphy->fw_version); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_driver_version = { + .read = ath6kl_driver_version_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_rx_drop_operation_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[64]; + unsigned int len; + + len = snprintf(buf, sizeof(buf), "RX aggregation drop packets %s\n", + (ar->conf_flags & ATH6KL_CONF_DISABLE_RX_AGGR_DROP) + ? "disabled" : "enabled"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_rx_drop_operation_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[20]; + size_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + if (strcasecmp(buf, "enable") == 0) + ar->conf_flags &= + ~ATH6KL_CONF_DISABLE_RX_AGGR_DROP; + else if (strcasecmp(buf, "disable") == 0) + ar->conf_flags |= + ATH6KL_CONF_DISABLE_RX_AGGR_DROP; + else + return -EINVAL; + + return count; +} + +static const struct file_operations fops_rx_drop_operation = { + .read = ath6kl_rx_drop_operation_read, + .write = ath6kl_rx_drop_operation_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_regwrite_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[32]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n", + ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_regwrite_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[32]; + char *sptr, *token; + unsigned int len = 0; + u32 reg_addr, reg_val; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, "="); + if (!token) + return -EINVAL; + + if (kstrtou32(token, 0, ®_addr)) + return -EINVAL; + + if (!ath6kl_dbg_is_diag_reg_valid(reg_addr)) + return -EINVAL; + + if (kstrtou32(sptr, 0, ®_val)) + return -EINVAL; + + ar->debug.diag_reg_addr_wr = reg_addr; + ar->debug.diag_reg_val_wr = reg_val; + + if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr, + cpu_to_le32(ar->debug.diag_reg_val_wr))) + return -EIO; + + return count; +} + +static const struct file_operations fops_diag_reg_write = { + .read = ath6kl_regwrite_read, + .write = ath6kl_regwrite_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, + size_t len) +{ + const struct wmi_target_roam_tbl *tbl; + u16 num_entries; + + if (len < sizeof(*tbl)) + return -EINVAL; + + tbl = (const struct wmi_target_roam_tbl *) buf; + num_entries = le16_to_cpu(tbl->num_entries); + if (sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) > + len) + return -EINVAL; + + if (ar->debug.roam_tbl == NULL || + ar->debug.roam_tbl_len < (unsigned int) len) { + kfree(ar->debug.roam_tbl); + ar->debug.roam_tbl = kmalloc(len, GFP_ATOMIC); + if (ar->debug.roam_tbl == NULL) + return -ENOMEM; + } + + memcpy(ar->debug.roam_tbl, buf, len); + ar->debug.roam_tbl_len = len; + + if (test_bit(ROAM_TBL_PEND, &ar->flag)) { + clear_bit(ROAM_TBL_PEND, &ar->flag); + wake_up(&ar->event_wq); + } + + return 0; +} + +static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + long left; + struct wmi_target_roam_tbl *tbl; + u16 num_entries, i; + char *buf; + unsigned int len, buf_len; + ssize_t ret_cnt; + + if (down_interruptible(&ar->sem)) + return -EBUSY; + + set_bit(ROAM_TBL_PEND, &ar->flag); + + ret = ath6kl_wmi_get_roam_tbl_cmd(ar->wmi); + if (ret) { + up(&ar->sem); + return ret; + } + + left = wait_event_interruptible_timeout( + ar->event_wq, !test_bit(ROAM_TBL_PEND, &ar->flag), WMI_TIMEOUT); + up(&ar->sem); + + if (left <= 0) + return -ETIMEDOUT; + + if (ar->debug.roam_tbl == NULL) + return -ENOMEM; + + tbl = (struct wmi_target_roam_tbl *) ar->debug.roam_tbl; + num_entries = le16_to_cpu(tbl->num_entries); + + buf_len = 100 + num_entries * 100; + buf = kzalloc(buf_len, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + len = 0; + len += scnprintf(buf + len, buf_len - len, + "roam_mode=%u\n\n" + "# roam_util bssid rssi rssidt last_rssi util bias\n", + le16_to_cpu(tbl->roam_mode)); + + for (i = 0; i < num_entries; i++) { + struct wmi_bss_roam_info *info = &tbl->info[i]; + len += scnprintf(buf + len, buf_len - len, + "%d %pM %d %d %d %d %d\n", + a_sle32_to_cpu(info->roam_util), info->bssid, + info->rssi, info->rssidt, info->last_rssi, + info->util, info->bias); + } + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_roam_table = { + .read = ath6kl_roam_table_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_force_roam_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + char buf[20]; + size_t len; + u8 bssid[ETH_ALEN]; + int i; + int addr[ETH_ALEN]; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) + != ETH_ALEN) + return -EINVAL; + for (i = 0; i < ETH_ALEN; i++) + bssid[i] = addr[i]; + + ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_force_roam = { + .write = ath6kl_force_roam_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_roam_mode_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[32]; + unsigned int len = 0; + char *str; + + if (ar->debug.roam_mode == WMI_DEFAULT_ROAM_MODE) + str = "default"; + else if (ar->debug.roam_mode == WMI_HOST_BIAS_ROAM_MODE) + str = "bssbias"; + else if (ar->debug.roam_mode == WMI_LOCK_BSS_MODE) + str = "lock"; + else + str = "NONE"; + + len = scnprintf(buf, sizeof(buf), "roam mode: %s\n", + str); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_roam_mode_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + char buf[20]; + size_t len; + enum wmi_roam_mode mode; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + if (strcasecmp(buf, "default") == 0) + mode = WMI_DEFAULT_ROAM_MODE; + else if (strcasecmp(buf, "bssbias") == 0) + mode = WMI_HOST_BIAS_ROAM_MODE; + else if (strcasecmp(buf, "lock") == 0) + mode = WMI_LOCK_BSS_MODE; + else + return -EINVAL; + + ar->debug.roam_mode = mode; + + ret = ath6kl_wmi_set_roam_mode_cmd(ar->wmi, mode); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_roam_mode = { + .read = ath6kl_roam_mode_read, + .write = ath6kl_roam_mode_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive) +{ + ar->debug.keepalive = keepalive; +} + +static ssize_t ath6kl_keepalive_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[16]; + int len; + + len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.keepalive); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_keepalive_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + u8 val; + + ret = kstrtou8_from_user(user_buf, count, 0, &val); + if (ret) + return ret; + + ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, 0, val); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_keepalive = { + .open = ath6kl_debugfs_open, + .read = ath6kl_keepalive_read, + .write = ath6kl_keepalive_write, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout) +{ + ar->debug.disc_timeout = timeout; +} + +static ssize_t ath6kl_disconnect_timeout_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[16]; + int len; + + len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.disc_timeout); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_disconnect_timeout_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + u8 val; + + ret = kstrtou8_from_user(user_buf, count, 0, &val); + if (ret) + return ret; + + ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, 0, val); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_disconnect_timeout = { + .open = ath6kl_debugfs_open, + .read = ath6kl_disconnect_timeout_read, + .write = ath6kl_disconnect_timeout_write, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_create_qos_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + char buf[200]; + ssize_t len; + char *sptr, *token; + struct wmi_create_pstream_cmd pstream; + u32 val32; + u16 val16; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + memset(&pstream, 0x00, sizeof(struct wmi_create_pstream_cmd)); + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou8(token, 0, &pstream.user_pri)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou8(token, 0, &pstream.traffic_direc)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou8(token, 0, &pstream.traffic_class)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou8(token, 0, &pstream.traffic_type)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou8(token, 0, &pstream.voice_psc_cap)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.min_service_int = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.max_service_int = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.inactivity_int = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.suspension_int = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.service_start_time = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou8(token, 0, &pstream.tsid)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou16(token, 0, &val16)) + return -EINVAL; + pstream.nominal_msdu = cpu_to_le16(val16); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou16(token, 0, &val16)) + return -EINVAL; + pstream.max_msdu = cpu_to_le16(val16); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.min_data_rate = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.mean_data_rate = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.peak_data_rate = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.max_burst_size = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.delay_bound = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.min_phy_rate = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.sba = cpu_to_le32(val32); + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val32)) + return -EINVAL; + pstream.medium_time = cpu_to_le32(val32); + + ath6kl_wmi_create_pstream_cmd(ar->wmi, vif->fw_vif_idx, &pstream); + + return count; +} + +static const struct file_operations fops_create_qos = { + .write = ath6kl_create_qos_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_delete_qos_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + char buf[100]; + ssize_t len; + char *sptr, *token; + u8 traffic_class; + u8 tsid; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou8(token, 0, &traffic_class)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou8(token, 0, &tsid)) + return -EINVAL; + + ath6kl_wmi_delete_pstream_cmd(ar->wmi, vif->fw_vif_idx, + traffic_class, tsid); + + return count; +} + +static const struct file_operations fops_delete_qos = { + .write = ath6kl_delete_qos_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_bgscan_int_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[128]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), "bgscan interval: %d\n", + ar->debug.bgscan_int); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_bgscan_int_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u16 bgscan_int; + char buf[32]; + ssize_t len; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou16(buf, 0, &bgscan_int)) + return -EINVAL; + + if (bgscan_int == 0) + bgscan_int = 0xffff; + + ar->debug.bgscan_int = bgscan_int; + + ath6kl_wmi_scanparams_cmd(ar->wmi, 0, + vif->sc_params.fg_start_period, + vif->sc_params.fg_end_period, + bgscan_int, + vif->sc_params.minact_chdwell_time, + vif->sc_params.maxact_chdwell_time, + vif->sc_params.pas_chdwell_time, + vif->sc_params.short_scan_ratio, + vif->sc_params.scan_ctrl_flags, + vif->sc_params.max_dfsch_act_time, + vif->sc_params.maxact_scan_per_ssid); + + return count; +} + +static const struct file_operations fops_bgscan_int = { + .read = ath6kl_bgscan_int_read, + .write = ath6kl_bgscan_int_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_listen_int_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u16 listen_int_t, listen_int_b; + char buf[32]; + char *sptr, *token; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + + if (kstrtou16(token, 0, &listen_int_t)) + return -EINVAL; + + if (kstrtou16(sptr, 0, &listen_int_b)) + return -EINVAL; + + if ((listen_int_t < 15) || (listen_int_t > 5000)) + return -EINVAL; + + if ((listen_int_b < 1) || (listen_int_b > 50)) + return -EINVAL; + + ar->listen_intvl_t = listen_int_t; + ar->listen_intvl_b = listen_int_b; + + ath6kl_wmi_listeninterval_cmd(ar->wmi, 0, ar->listen_intvl_t, + ar->listen_intvl_b); + + return count; +} + +static ssize_t ath6kl_listen_int_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[32]; + int len; + + len = scnprintf(buf, sizeof(buf), "%u %u\n", ar->listen_intvl_t, + ar->listen_intvl_b); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_listen_int = { + .read = ath6kl_listen_int_read, + .write = ath6kl_listen_int_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_power_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[100]; + unsigned int len = 0; + char *sptr, *token; + u16 idle_period, ps_poll_num, dtim, + tx_wakeup, num_tx; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou16(token, 0, &idle_period)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou16(token, 0, &ps_poll_num)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou16(token, 0, &dtim)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou16(token, 0, &tx_wakeup)) + return -EINVAL; + + token = strsep(&sptr, " "); + if (!token) + return -EINVAL; + if (kstrtou16(token, 0, &num_tx)) + return -EINVAL; + + ar->debug.power_params.idle_period = idle_period; + ar->debug.power_params.ps_poll_num = ps_poll_num; + ar->debug.power_params.dtim = dtim; + ar->debug.power_params.tx_wakeup = tx_wakeup; + ar->debug.power_params.num_tx = num_tx; + + ath6kl_wmi_pmparams_cmd(ar->wmi, 0, idle_period, ps_poll_num, + dtim, tx_wakeup, num_tx, 0); + + return count; +} + +static ssize_t ath6kl_power_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[256]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), + "idle_period: %d, ps_poll_num: %d, dtim: %d, tx_wakeup:%d , num_tx: %d\n", + ar->debug.power_params.idle_period, + ar->debug.power_params.ps_poll_num, + ar->debug.power_params.dtim, + ar->debug.power_params.tx_wakeup, + ar->debug.power_params.num_tx); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_power_params = { + .read = ath6kl_power_params_read, + .write = ath6kl_power_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_parse_lpl_force_enable_params(const char __user *user_buf, + size_t count, + struct lpl_force_enable_param *param) +{ + char buf[64]; + char *p; + unsigned int len; + int value; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + param->lpl_policy = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + param->no_blocker_detect = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + param->no_rfb_detect = value; + + return 0; +} + +/* File operation functions for low power listen */ +static ssize_t ath6kl_lpl_force_enable_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + + ret = ath6kl_parse_lpl_force_enable_params(user_buf, count, + &ar->debug.lpl_force_enable_params); + if (ret) + return ret; + + if (ath6kl_wmi_lpl_enable_cmd(ar->wmi, + (struct wmi_lpl_force_enable_cmd *) + &ar->debug.lpl_force_enable_params)) + return -EIO; + + return count; +} + +static ssize_t ath6kl_lpl_force_enable_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[128]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), + "lplPolicy: %d, noBlockerDetect: %d, noRfbDetect: %d\n", + ar->debug.lpl_force_enable_params.lpl_policy, + ar->debug.lpl_force_enable_params.no_blocker_detect, + ar->debug.lpl_force_enable_params.no_rfb_detect); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +/* debug fs for low power listen */ +static const struct file_operations fops_lpl_force_enable = { + .read = ath6kl_lpl_force_enable_read, + .write = ath6kl_lpl_force_enable_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for mimo power saving */ +static ssize_t ath6kl_mimo_ps_enable_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &ar->debug.mimo_ps_enable); + if (ret) + return ret; + + if (ath6kl_wmi_smps_enable(ar->wmi, + (struct wmi_config_enable_cmd *)&ar->debug.mimo_ps_enable)) + return -EIO; + + return count; +} + +static ssize_t ath6kl_mimo_ps_enable_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[32]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), "MIMO Power Saving: %d\n", + ar->debug.mimo_ps_enable); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +/* debug fs for mimo power saving */ +static const struct file_operations fops_mimo_ps_enable = { + .read = ath6kl_mimo_ps_enable_read, + .write = ath6kl_mimo_ps_enable_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_parse_mimo_ps_params(const char __user *user_buf, + size_t count, + struct smps_param *param) +{ + char buf[64]; + char *p; + unsigned int len; + int value; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + + if (value == 1) + param->mode = WMI_SMPS_MODE_STATIC; + else if (value == 2) + param->mode = WMI_SMPS_MODE_DYNAMIC; + else + return -EINVAL; + + param->flags = WMI_SMPS_OPTION_MODE; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + param->automatic = value; + + if (value == 0 || value == 1) + param->flags |= WMI_SMPS_OPTION_AUTO; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + + if (value != -1) { + param->data_thresh = value; + param->flags |= WMI_SMPS_OPTION_DATATHRESH; + } + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + + if (value != -1) { + param->rssi_thresh = value; + param->flags |= WMI_SMPS_OPTION_RSSITHRESH; + } + + return 0; +} + +/* File operation functions for mimo power saving */ +static ssize_t ath6kl_mimo_ps_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + + ret = ath6kl_parse_mimo_ps_params(user_buf, + count, &ar->debug.smps_params); + if (ret) + return ret; + + if (ath6kl_wmi_smps_config(ar->wmi, + (struct wmi_config_smps_cmd *)&ar->debug.smps_params)) + return -EIO; + + return count; +} + +static ssize_t ath6kl_mimo_ps_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[128]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), + "flags: %d, mode: %d, automatic: %d, dataThresh: %d, rssiThresh: %d\n", + ar->debug.smps_params.flags, + ar->debug.smps_params.mode, + ar->debug.smps_params.automatic, + ar->debug.smps_params.data_thresh, + ar->debug.smps_params.rssi_thresh); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +/* debug fs for mimo power saving */ +static const struct file_operations fops_mimo_ps_params = { + .read = ath6kl_mimo_ps_params_read, + .write = ath6kl_mimo_ps_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + + +/* File operation functions for Green Tx */ +static ssize_t ath6kl_green_tx_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + + ret = kstrtou32_from_user(user_buf, count, 0, + &ar->green_tx_params.enable); + + if (ret) + return ret; + + if (ath6kl_wmi_set_green_tx_params(ar->wmi, + (struct wmi_green_tx_params *) &ar->green_tx_params)) + return -EIO; + + return count; +} + +static ssize_t ath6kl_green_tx_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[32]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), "Green Tx: %d\n", + ar->green_tx_params.enable); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +/* debug fs for green tx */ +static const struct file_operations fops_green_tx = { + .read = ath6kl_green_tx_read, + .write = ath6kl_green_tx_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_parse_green_tx_params(const char __user *user_buf, + size_t count, + struct wmi_green_tx_params *param) +{ + char buf[64]; + char *p; + unsigned int len; + int value; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + param->next_probe_count = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + param->max_back_off = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + param->min_gtx_rssi = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + param->force_back_off = value; + + return 0; +} + +static ssize_t ath6kl_green_tx_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + + ret = ath6kl_parse_green_tx_params(user_buf, + count, &ar->green_tx_params); + + if (ret) + return ret; + + if (ath6kl_wmi_set_green_tx_params(ar->wmi, + (struct wmi_green_tx_params *) &ar->green_tx_params)) + return -EIO; + + return count; +} + +static ssize_t ath6kl_green_tx_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[128]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), + "next_probe_count: %d, max_back_off: %d, min_gtx_rssi: %d, force_back_off: %d\n", + ar->green_tx_params.next_probe_count, + ar->green_tx_params.max_back_off, + ar->green_tx_params.min_gtx_rssi, + ar->green_tx_params.force_back_off); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_green_tx_params = { + .read = ath6kl_green_tx_params_read, + .write = ath6kl_green_tx_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + + +static int ath6kl_parse_ht_cap_params(const char __user *user_buf, size_t count, + struct ht_cap_param *htCapParam) +{ + char buf[64]; + char *p; + unsigned int len; + int value; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + + if (value >= IEEE80211_NUM_BANDS) + return -EFAULT; + + htCapParam->band = (u8) value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + htCapParam->chan_width_40M_supported = (u8) value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + htCapParam->short_GI = (u8) value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + htCapParam->intolerance_40MHz = (u8) value; + + return 0; +} + +/* File operation functions for HT Cap */ +static ssize_t ath6kl_ht_cap_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ht_cap_param tempHtCap; + int i, ret; + + memset(&tempHtCap, 0, sizeof(struct ht_cap_param)); + ret = ath6kl_parse_ht_cap_params(user_buf, count, &tempHtCap); + + if (ret) + return ret; + + for (i = 0; i < ar->vif_max; i++) { + if (ath6kl_wmi_set_ht_cap_cmd(ar->wmi, i, + tempHtCap.band, + tempHtCap.chan_width_40M_supported, + tempHtCap.short_GI, + tempHtCap.intolerance_40MHz)) + return -EIO; + } + + tempHtCap.isConfig = 1; + memcpy(&ar->debug.ht_cap_param[tempHtCap.band], + &tempHtCap, sizeof(struct ht_cap_param)); + + return count; +} + +static ssize_t ath6kl_ht_cap_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (128) + struct ath6kl *ar = file->private_data; + u8 *buf; + unsigned int len = 0; + int band; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (band = 0; band < A_NUM_BANDS; band++) { + if (ar->debug.ht_cap_param[band].isConfig) + len += scnprintf(buf + len, _BUF_SIZE - len, + "%s chan_width_40M_supported: %d, short_GI: %d, intolerance_40MHz: %d\n", + (ar->debug.ht_cap_param[band].band == A_BAND_24GHZ ? + "24GHZ" : "5GHZ"), + ar->debug.ht_cap_param[band].chan_width_40M_supported, + ar->debug.ht_cap_param[band].short_GI, + ar->debug.ht_cap_param[band].intolerance_40MHz); + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for HT Cap */ +static const struct file_operations fops_ht_cap_params = { + .read = ath6kl_ht_cap_params_read, + .write = ath6kl_ht_cap_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_debug_mask_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int ret; + + ret = kstrtou32_from_user(user_buf, count, 0, &debug_mask); + + if (ret) + return ret; + + return count; +} + +static ssize_t ath6kl_debug_mask_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + int len; + + len = snprintf(buf, sizeof(buf), "debug_mask: 0x%x\n", debug_mask); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_debug_mask = { + .read = ath6kl_debug_mask_read, + .write = ath6kl_debug_mask_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for HT Coex */ +static ssize_t ath6kl_ht_coex_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + char *p; + int scan_interval, rate_interval; + char buf[32]; + ssize_t len; + int i; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &scan_interval); + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &rate_interval); + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) + ath6kl_htcoex_config(vif, + (u32)scan_interval, + (u8)rate_interval); + } + + return count; +} + +static ssize_t ath6kl_ht_coex_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (128) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + unsigned int len = 0; + int i; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->htcoex_ctx)) + len += scnprintf(buf + len, _BUF_SIZE - len, + "int-%d flags %x, scan %d ms, num_scan %d, rate %d, t_cnt %d\n", + i, + vif->htcoex_ctx->flags, + vif->htcoex_ctx->scan_interval, + vif->htcoex_ctx->num_scan, + vif->htcoex_ctx->rate_rollback_interval, + vif->htcoex_ctx->tolerant40_cnt); + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for HT Coex */ +static const struct file_operations fops_ht_coex_params = { + .read = ath6kl_ht_coex_params_read, + .write = ath6kl_ht_coex_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_parse_tx_amsdu_params(const char __user *user_buf, + size_t count, + struct ath6kl_vif *vif) +{ + char buf[64]; + char *p; + unsigned int len; + int value; + u8 tx_amsdu_max_aggr_num; + u16 tx_amsdu_max_pdu_len, tx_amsdu_timeout; + bool tx_amsdu_seq_pkt; + bool tx_amsdu_progressive; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + tx_amsdu_max_aggr_num = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + tx_amsdu_max_pdu_len = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + tx_amsdu_timeout = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + if (value) + tx_amsdu_seq_pkt = true; + else + tx_amsdu_seq_pkt = false; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + if (value) + tx_amsdu_progressive = true; + else + tx_amsdu_progressive = false; + + aggr_tx_config(vif, + tx_amsdu_seq_pkt, + tx_amsdu_progressive, + tx_amsdu_max_aggr_num, + tx_amsdu_max_pdu_len, + tx_amsdu_timeout); + + return 0; +} + +/* File operation functions for TX A-MSDU Params */ +static ssize_t ath6kl_tx_amsdu_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + int i, ret; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->aggr_cntxt)) { + ret = ath6kl_parse_tx_amsdu_params(user_buf, + count, vif); + + if (ret) + return ret; + } + } + + return count; +} + +static ssize_t ath6kl_tx_amsdu_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (8192) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + struct ath6kl_sta *conn; + struct txtid *txtid; + u8 *buf; + unsigned int len = 0; + int i, j, k, num_sta; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->aggr_cntxt)) { + len += scnprintf(buf + len, _BUF_SIZE - len, + "DEV-%d %s max_aggr_len: %d max_aggr_num: %d, max_pdu_len: %d, timeout = %d ms, seq_pkt %s, tx_prog %s\n", + i, + (vif->aggr_cntxt->tx_amsdu_enable == true ? + "Enable" : "Disable"), + vif->aggr_cntxt->tx_amsdu_max_aggr_len, + vif->aggr_cntxt->tx_amsdu_max_aggr_num, + vif->aggr_cntxt->tx_amsdu_max_pdu_len, + vif->aggr_cntxt->tx_amsdu_timeout, + (vif->aggr_cntxt->tx_amsdu_seq_pkt == true ? + "Yes" : "No"), + (vif->aggr_cntxt->tx_amsdu_progressive == true ? + "Yes" : "No")); + + num_sta = (vif->nw_type == INFRA_NETWORK) ? + 1 : AP_MAX_NUM_STA; + for (j = 0; j < num_sta; j++) { + conn = &vif->sta_list[j]; + len += scnprintf(buf + len, _BUF_SIZE - len, + " [%02x:%02x:%02x:%02x:%02x:%02x]\n", + conn->mac[0], conn->mac[1], + conn->mac[2], conn->mac[3], + conn->mac[4], conn->mac[5]); + for (k = 0; k < NUM_OF_TIDS; k++) { + txtid = AGGR_GET_TXTID( + conn->aggr_conn_cntxt, k); + len += scnprintf(buf + len, + _BUF_SIZE - len, + " %d-%d-%s AMSDU: %d PDU: %d PROG: %d/%d TIMEOUT/FLUSH/NULL/OVERFLOW: %d/%d/%d/%d\n", + txtid->tid, + txtid->aid, + (txtid->max_aggr_sz ? + "ON " : "OFF"), + txtid->num_amsdu, + txtid->num_pdu, + txtid->last_num_amsdu, + txtid->last_num_timeout, + txtid->num_timeout, + txtid->num_flush, + txtid->num_tx_null, + txtid->num_overflow); + } + } + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for TX A-MSDU Params */ +static const struct file_operations fops_tx_amsdu_params = { + .read = ath6kl_tx_amsdu_params_read, + .write = ath6kl_tx_amsdu_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_parse_tx_amsdu(const char __user *user_buf, size_t count, + struct ath6kl_vif *vif) +{ + char buf[64]; + char *p; + unsigned int len; + int value; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + if (value) + set_bit(AMSDU_ENABLED, &vif->flags); + else + clear_bit(AMSDU_ENABLED, &vif->flags); + + return 0; +} + +/* File operation functions for TX A-MSDU */ +static ssize_t ath6kl_tx_amsdu_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + int i, ret; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->aggr_cntxt)) { + ret = ath6kl_parse_tx_amsdu(user_buf, count, vif); + + if (ret) + return ret; + } + } + + return count; +} + +static ssize_t ath6kl_tx_amsdu_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (128) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + unsigned int len = 0; + int i; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->aggr_cntxt)) + len += scnprintf(buf + len, _BUF_SIZE - len, + "DEV-%d %s\n", + i, + (test_bit(AMSDU_ENABLED, &vif->flags) + ? "ON" : "OFF")); + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for TX A-MSDU */ +static const struct file_operations fops_tx_amsdu = { + .read = ath6kl_tx_amsdu_read, + .write = ath6kl_tx_amsdu_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_htc_stat_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (8192) + struct ath6kl *ar = file->private_data; + struct ath6kl_cookie_pool *cookie_pool; + u8 *buf; + unsigned int len = 0; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + /* HTC cookie stats */ + cookie_pool = &ar->cookie_data; + len += scnprintf(buf + len, _BUF_SIZE - len, + "DATA Cookie : num %d avail %d, alloc %d fail %d free %d peak %d\n", + cookie_pool->cookie_num, + cookie_pool->cookie_count, + cookie_pool->cookie_alloc_cnt, + cookie_pool->cookie_alloc_fail_cnt, + cookie_pool->cookie_free_cnt, + cookie_pool->cookie_peak_cnt); + + cookie_pool = &ar->cookie_ctrl; + len += scnprintf(buf + len, _BUF_SIZE - len, + "CTRL Cookie : num %d avail %d, alloc %d fail %d free %d peak %d\n", + cookie_pool->cookie_num, + cookie_pool->cookie_count, + cookie_pool->cookie_alloc_cnt, + cookie_pool->cookie_alloc_fail_cnt, + cookie_pool->cookie_free_cnt, + cookie_pool->cookie_peak_cnt); + + + len += ath6kl_htc_stat(ar->htc_target, buf + len, _BUF_SIZE - len); + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + + +/* debug fs for HTC EP Stats. */ +static const struct file_operations fops_htc_stat = { + .read = ath6kl_htc_stat_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_hif_stat_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (4096) + struct ath6kl *ar = file->private_data; + u8 *buf; + unsigned int len; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + len = ath6kl_hif_stat(ar, buf, _BUF_SIZE); + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + + +/* debug fs for HIF Stats. */ +static const struct file_operations fops_hif_stat = { + .read = ath6kl_hif_stat_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for HIF-PIPE Max. Schedule packages */ +static ssize_t ath6kl_hif_pipe_max_sche_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[64]; + char *p; + unsigned int len; + u32 max_sche_tx, max_sche_rx; + + if (ar->hif_type != ATH6KL_HIF_TYPE_USB) + return -EIO; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &max_sche_tx); + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &max_sche_rx); + + ath6kl_hif_pipe_set_max_sche(ar, max_sche_tx, max_sche_rx); + + return count; +} + +/* debug fs for HIF-PIPE Max. Schedule packages */ +static const struct file_operations fops_hif_pipe_max_sche = { + .write = ath6kl_hif_pipe_max_sche_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for AP-APSD */ +static ssize_t ath6kl_ap_apsd_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (128) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + unsigned int len = 0; + int i; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && (vif->nw_type == AP_NETWORK)) { + len += scnprintf(buf, _BUF_SIZE, + "VAP(%d), apsd: %d\n", i, + vif->ap_apsd); + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +static ssize_t ath6kl_ap_apsd_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u32 ap_apsd; + char buf[32]; + ssize_t len; + int i; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &ap_apsd)) + return -EINVAL; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->nw_type == AP_NETWORK)) { + ath6kl_wmi_ap_set_apsd(vif->ar->wmi, vif->fw_vif_idx, + ap_apsd ? + WMI_AP_APSD_ENABLED : WMI_AP_APSD_DISABLED); + vif->ap_apsd = ap_apsd; + } + } + + return count; +} + +/* debug fs for AP-APSD */ +static const struct file_operations fops_ap_apsd = { + .read = ath6kl_ap_apsd_read, + .write = ath6kl_ap_apsd_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for AP-IntraBss */ +static ssize_t ath6kl_ap_intrabss_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (256) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + unsigned int len = 0; + int i; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && (vif->nw_type == AP_NETWORK)) { + len += scnprintf(buf, _BUF_SIZE, + "VAP(%d), ap_intrabss: %d\n", i, + vif->intra_bss); + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +static ssize_t ath6kl_ap_intrabss_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u32 ap_intrabss; + char buf[32]; + ssize_t len; + int i; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &ap_intrabss)) + return -EINVAL; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->nw_type == AP_NETWORK)) + vif->intra_bss = (ap_intrabss ? 1 : 0); + } + + return count; +} + +/* debug fs for AP-IntraBss */ +static const struct file_operations fops_ap_intrabss = { + .read = ath6kl_ap_intrabss_read, + .write = ath6kl_ap_intrabss_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_get_force_passcan(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[128]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), + "force passive: %d\n", ar->debug.force_passive); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_set_force_passcan(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + unsigned int len = 0; + u8 buf[16]; + u32 force_passcan; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &force_passcan)) + return -EINVAL; + + if (force_passcan == 1) + vif->sc_params.scan_ctrl_flags &= ~ACTIVE_SCAN_CTRL_FLAGS; + else + vif->sc_params.scan_ctrl_flags |= ACTIVE_SCAN_CTRL_FLAGS; + + ar->debug.force_passive = force_passcan; + + ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + vif->sc_params.fg_start_period, + vif->sc_params.fg_end_period, + vif->sc_params.bg_period, + vif->sc_params.minact_chdwell_time, + vif->sc_params.maxact_chdwell_time, + vif->sc_params.pas_chdwell_time, + vif->sc_params.short_scan_ratio, + vif->sc_params.scan_ctrl_flags, + vif->sc_params.max_dfsch_act_time, + vif->sc_params.maxact_scan_per_ssid); + + return count; +} + +static const struct file_operations fops_force_passcan = { + .read = ath6kl_get_force_passcan, + .write = ath6kl_set_force_passcan, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_get_pmkid_list(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define PMKID_LIST_NUM_SIZE 4 + + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + unsigned int len = 0, buf_len; + u8 *p; + u8 buf[512]; + struct wmi_pmkid_list_reply *reply; + long left; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if (down_interruptible(&ar->sem)) + return -EBUSY; + + set_bit(PMKLIST_GET_PEND, &vif->flags); + + if (ath6kl_wmi_get_pmkid_list(ar->wmi, 0)) { + up(&ar->sem); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(PMKLIST_GET_PEND, + &vif->flags), WMI_TIMEOUT); + + up(&ar->sem); + + if (left <= 0) + return -ETIMEDOUT; + + reply = (struct wmi_pmkid_list_reply *) vif->pmkid_list_buf; + p = buf; + buf_len = sizeof(buf); + + len += scnprintf(p + len, buf_len - len, "PMKID List\n"); + + if (reply->num_pmkid != 0 && reply->num_pmkid <= WMI_MAX_PMKID_CACHE) { + int i; + char *bssid = (char *)reply + PMKID_LIST_NUM_SIZE; + char *pmkid = (char *)reply + PMKID_LIST_NUM_SIZE + ETH_ALEN; + + for (i = 0; i < reply->num_pmkid; i++) { + len += scnprintf(p + len, buf_len - len, + "bssid: %02x:%02x:%02x:%02x:%02x:%02x\n", + (bssid[0] & 0xff), (bssid[1] & 0xff), + (bssid[2] & 0xff), (bssid[3] & 0xff), + (bssid[4] & 0xff), (bssid[5] & 0xff)); + len += scnprintf(p + len, buf_len - len, + "pmkid: %02x:%02x:%02x:%02x:%02x:%02x%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + (pmkid[0] & 0xff), (pmkid[1] & 0xff), + (pmkid[2] & 0xff), (pmkid[3] & 0xff), + (pmkid[4] & 0xff), (pmkid[5] & 0xff), + (pmkid[6] & 0xff), (pmkid[7] & 0xff), + (pmkid[8] & 0xff), (pmkid[9] & 0xff), + (pmkid[10] & 0xff), (pmkid[11] & 0xff), + (pmkid[12] & 0xff), (pmkid[13] & 0xff), + (pmkid[14] & 0xff), (pmkid[15] & 0xff)); + + bssid += (sizeof(struct wmi_pmkid) + ETH_ALEN); + pmkid += (sizeof(struct wmi_pmkid) + ETH_ALEN); + } + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_pmkid_list = { + .read = ath6kl_get_pmkid_list, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_txseries_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u64 tx_series; + int ret; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + ret = kstrtou64_from_user(user_buf, count, 0, &tx_series); + if (ret) + return ret; + + ar->debug.set_tx_series = tx_series; + + if (ath6kl_wmi_set_tx_select_rates_on_all_mode(ar->wmi, vif->fw_vif_idx, + tx_series)) + return -EIO; + + return count; +} + +static ssize_t ath6kl_txseries_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[32]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), "Txratemask is 0x%llx\n", + ar->debug.set_tx_series); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_txseries_write = { + .read = ath6kl_txseries_read, + .write = ath6kl_txseries_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_antdivcfg_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 div_control; + int ret; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + ret = kstrtou8_from_user(user_buf, count, 0, &div_control); + if (ret) + return ret; + + if (ath6kl_wmi_set_antdivcfg(ar->wmi, vif->fw_vif_idx, div_control)) + return -EIO; + + return count; +} + +static const struct file_operations fops_antdiv_write = { + .write = ath6kl_antdivcfg_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath6kl_antdiv_stat(struct ath6kl_vif *vif, + u8 *buf, int buf_len) +{ + int len = 0; + len += snprintf(buf + len, buf_len - len, "\n Scan start time : %d", + vif->ant_div_stat.scan_start_time); + len += snprintf(buf + len, buf_len - len, "\n Total packet count : %d", + vif->ant_div_stat.total_pkt_count); + len += snprintf(buf + len, buf_len - len, "\n Main Recv count : %d", + vif->ant_div_stat.main_recv_cnt); + len += snprintf(buf + len, buf_len - len, "\n Alt Recv count : %d", + vif->ant_div_stat.alt_recv_cnt); + len += snprintf(buf + len, buf_len - len, "\n Main Rssi Avg : %d", + vif->ant_div_stat.main_rssi_avg); + len += snprintf(buf + len, buf_len - len, "\n Alt Rssi Avg : %d", + vif->ant_div_stat.alt_rssi_avg); + len += snprintf(buf + len, buf_len - len, "\n Current Main Set : %d", + vif->ant_div_stat.curr_main_set); + len += snprintf(buf + len, buf_len - len, "\n Current Alt Set : %d", + vif->ant_div_stat.curr_alt_set); + len += snprintf(buf + len, buf_len - len, "\n Current Bias : %d", + vif->ant_div_stat.curr_bias); + len += snprintf(buf + len, buf_len - len, "\n Main LNA conf : %d", + vif->ant_div_stat.main_lna_conf); + len += snprintf(buf + len, buf_len - len, "\n Alt LNA conf : %d", + vif->ant_div_stat.alt_lna_conf); + len += snprintf(buf + len, buf_len - len, "\n Fast Div Bias: %d", + vif->ant_div_stat.fast_div_bias); + len += snprintf(buf + len, buf_len - len, "\n End state : %d", + vif->ant_div_stat.end_st); + len += snprintf(buf + len, buf_len - len, "\n Scan: %d", + vif->ant_div_stat.scan); + len += snprintf(buf + len, buf_len - len, "\n Scan not start : %d\n", + vif->ant_div_stat.scan_not_start); + return len; +} + +static ssize_t ath6kl_antdivstat_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (4096) + struct ath6kl *ar = file->private_data; + u8 *buf; + unsigned int len; + ssize_t ret_cnt; + struct ath6kl_vif *vif; + vif = ath6kl_vif_first(ar); + + /* should always get value by ath6kl_vif_first */ + if (!vif) + return -EIO; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + len = ath6kl_antdiv_stat(vif, buf, _BUF_SIZE); + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret_cnt; +#undef _BUF_SIZE +} + +static const struct file_operations fops_antdiv_state_read = { + .read = ath6kl_antdivstat_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_p2p_flowctrl_stat_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (4096) + struct ath6kl *ar = file->private_data; + u8 *buf; + unsigned int len; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + len = ath6kl_p2p_flowctrl_stat(ar, buf, _BUF_SIZE); + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for P2P Flowctrl Stats. */ +static const struct file_operations fops_p2p_flowctrl_stat = { + .read = ath6kl_p2p_flowctrl_stat_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_ap_ps_stat_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (2048) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + struct ath6kl_sta *conn; + u8 *buf, *p; + unsigned int len = 0; + int i, j, buf_len, depth; + u32 enq, enq_err, deq, age; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + p = buf; + buf_len = _BUF_SIZE; + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->nw_type == AP_NETWORK)) { + + ath6kl_ps_queue_stat(&vif->psq_mcast, + &depth, &enq, &enq_err, &deq, &age); + len += scnprintf(p + len, buf_len - len, + "VIF[%d] = psq_mcast %d/%d/%d/%d/%d\n", + vif->fw_vif_idx, + depth, enq, enq_err, deq, age); + + for (j = 0; j < AP_MAX_NUM_STA; j++) { + conn = &vif->sta_list[j]; + + len += scnprintf(p + len, buf_len - len, + " STA - %02x:%02x:%02x:%02x:%02x:%02x aid %02d apsd %d state %02x phy %d", + conn->mac[0], conn->mac[1], + conn->mac[2], conn->mac[3], + conn->mac[4], conn->mac[5], + conn->aid, + conn->apsd_info, + conn->sta_flags, + conn->phymode); + + ath6kl_ps_queue_stat(&conn->psq_data, + &depth, &enq, &enq_err, + &deq, &age); + len += scnprintf(p + len, buf_len - len, + " psq_data %d/%d/%d/%d/%d", + depth, enq, enq_err, deq, age); + + ath6kl_ps_queue_stat(&conn->psq_mgmt, + &depth, &enq, &enq_err, + &deq, &age); + len += scnprintf(p + len, buf_len - len, + " psq_mgmt %d/%d/%d/%d/%d\n", + depth, enq, enq_err, deq, age); + } + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for AP-PS Stats. */ +static const struct file_operations fops_ap_ps_stat = { + .read = ath6kl_ap_ps_stat_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_parse_rx_aggr_params(const char __user *user_buf, + size_t count, + struct ath6kl_vif *vif) +{ + char buf[64]; + char *p; + unsigned int len; + int value; + u16 rx_aggr_timeout; + + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + rx_aggr_timeout = value; + + aggr_config(vif, rx_aggr_timeout); + + return 0; +} + +/* File operation functions for RX Aggr. Params */ +static ssize_t ath6kl_rx_aggr_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + int i, ret; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->aggr_cntxt)) { + ret = ath6kl_parse_rx_aggr_params(user_buf, count, vif); + + if (ret) + return ret; + } + } + + return count; +} + +static ssize_t ath6kl_rx_aggr_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (512) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + unsigned int len = 0; + int i; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->aggr_cntxt)) { + len += scnprintf(buf + len, _BUF_SIZE - len, + "DEV-%d rx_aggr_timeout = %d ms\n", + i, + vif->aggr_cntxt->rx_aggr_timeout); + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for RX Aggr. Params */ +static const struct file_operations fops_rx_aggr_params = { + .read = ath6kl_rx_aggr_params_read, + .write = ath6kl_rx_aggr_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for SCAN Params */ +static int ath6kl_parse_scan_params(const char __user *user_buf, size_t count, + int *maxact_chdwell_time, + int *pas_chdwell_time, + int *maxact_scan_per_ssid) +{ + char buf[16]; + char *p; + int value; + int len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &value); + if (value < ATH6KL_SCAN_ACT_DEWELL_TIME) + value = ATH6KL_SCAN_ACT_DEWELL_TIME; + *maxact_chdwell_time = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + if (value < ATH6KL_SCAN_PAS_DEWELL_TIME) + value = ATH6KL_SCAN_PAS_DEWELL_TIME; + *pas_chdwell_time = value; + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &value); + if (value <= ATH6KL_SCAN_PROBE_PER_SSID) + value = 1; + *maxact_scan_per_ssid = value; + + return 0; +} + +static ssize_t ath6kl_scan_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + int i, ret; + int maxact_chdwell_time, pas_chdwell_time, maxact_scan_per_ssid; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) { + ret = ath6kl_parse_scan_params(user_buf, count, + &maxact_chdwell_time, + &pas_chdwell_time, + &maxact_scan_per_ssid); + if (!ret) { + vif->sc_params.maxact_chdwell_time = + maxact_chdwell_time; + vif->sc_params.pas_chdwell_time = + pas_chdwell_time; + vif->sc_params.maxact_scan_per_ssid = + maxact_scan_per_ssid; + memcpy(&vif->sc_params_default, &vif->sc_params, + sizeof(struct wmi_scan_params_cmd)); + } else + return ret; + } + } + + return count; +} + +static ssize_t ath6kl_scan_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (512) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + unsigned int len = 0; + int i; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) { + len += scnprintf(buf + len, _BUF_SIZE - len, + "DEV-%d Flags = 0x%x ActDwellTime = %d PasDwellTime = %d ScanPerSsid = %d\n", + i, + vif->sc_params.scan_ctrl_flags, + (vif->sc_params.maxact_chdwell_time == 0 ? + ATH6KL_SCAN_ACT_DEWELL_TIME : + vif->sc_params.maxact_chdwell_time), + (vif->sc_params.pas_chdwell_time == 0 ? + ATH6KL_SCAN_PAS_DEWELL_TIME : + vif->sc_params.pas_chdwell_time), + (vif->sc_params.maxact_scan_per_ssid == 0 ? + ATH6KL_SCAN_PROBE_PER_SSID : + vif->sc_params.maxact_scan_per_ssid)); + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for SCAN Params */ +static const struct file_operations fops_scan_params = { + .read = ath6kl_scan_params_read, + .write = ath6kl_scan_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for AP Keep-alive */ +static ssize_t ath6kl_ap_keepalive_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + char *p; + int internal, cycle; + char buf[32]; + ssize_t len; + int i; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &internal); + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &cycle); + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) + ath6kl_ap_keepalive_config(vif, + (u32)internal, + (u32)cycle); + } + + return count; +} + +static ssize_t ath6kl_ap_keepalive_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (256) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + unsigned int len = 0; + int i; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->ap_keepalive_ctx)) { + if (vif->ap_keepalive_ctx->flags & + ATH6KL_AP_KA_FLAGS_BY_SUPP) + len += scnprintf(buf + len, _BUF_SIZE - len, + "int-%d offload to supplicant/hostapd\n", + i); + else + len += scnprintf(buf + len, _BUF_SIZE - len, + "int-%d flags %x, interval %d ms., cycle %d\n", + i, + vif->ap_keepalive_ctx->flags, + vif->ap_keepalive_ctx->ap_ka_interval, + vif->ap_keepalive_ctx->ap_ka_reclaim_cycle); + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for AP Keep-alive */ +static const struct file_operations fops_ap_keepalive_params = { + .read = ath6kl_ap_keepalive_params_read, + .write = ath6kl_ap_keepalive_params_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_tgt_ap_stat_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (2048) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + u8 *p; + unsigned int len = 0; + int i, j, buf_len; + long left; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + p = buf; + buf_len = _BUF_SIZE; + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + + if ((vif) && + (vif->nw_type == AP_NETWORK)) { + struct wmi_ap_mode_stat *ap_stats; + + if (down_interruptible(&ar->sem)) { + ret_cnt = -EBUSY; + goto FAIL; + } + + set_bit(STATS_UPDATE_PEND, &vif->flags); + + if (ath6kl_wmi_get_stats_cmd(ar->wmi, + vif->fw_vif_idx)) { + up(&ar->sem); + ret_cnt = -EIO; + goto FAIL; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(STATS_UPDATE_PEND, + &vif->flags), WMI_TIMEOUT); + up(&ar->sem); + + if (left <= 0) { + ret_cnt = -ETIMEDOUT; + goto FAIL; + } + + len += scnprintf(p + len, buf_len - len, "VIF[%d]\n", + vif->fw_vif_idx); + + ap_stats = &vif->ap_stats; + for (j = 0; j < AP_MAX_NUM_STA; j++) { + struct wmi_per_sta_stat *per_sta_stat = + &ap_stats->sta[j]; + + if (per_sta_stat->aid) { + len += scnprintf(p + len, buf_len - len, + " STA - AID %02d tx_bytes/pkts/error %d/%d/%d rx_bytes/pkts/error %d/%d/%d tx_ucast_rate %d last_txrx_time %d\n", + per_sta_stat->aid, + per_sta_stat->tx_bytes, + per_sta_stat->tx_pkts, + per_sta_stat->tx_error, + per_sta_stat->rx_bytes, + per_sta_stat->rx_pkts, + per_sta_stat->rx_error, + per_sta_stat->tx_ucast_rate, + per_sta_stat->last_txrx_time); + } + } + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + +FAIL: + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for AP Stats. */ +static const struct file_operations fops_tgt_ap_stats = { + .read = ath6kl_tgt_ap_stat_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static void __chan_flag_to_string(u32 flags, u8 *string, int str_len) +{ + u8 *p = string; + int len = 0; + + string[0] = '\0'; + + if (flags & IEEE80211_CHAN_DISABLED) + len += scnprintf(p + len, str_len - len, + "%s", + "[DISABLE]"); + + if (flags & IEEE80211_CHAN_PASSIVE_SCAN) + len += scnprintf(p + len, str_len - len, + "%s", + "[PASSIVE_SCAN]"); + + if (flags & IEEE80211_CHAN_NO_IBSS) + len += scnprintf(p + len, str_len - len, + "%s", + "[NO_IBSS]"); + + if (flags & IEEE80211_CHAN_RADAR) + len += scnprintf(p + len, str_len - len, + "%s", + "[RADAR]"); + + len += scnprintf(p + len, str_len - len, + "%s", + "[HT20]"); + + if ((flags & IEEE80211_CHAN_NO_HT40PLUS) && + (flags & IEEE80211_CHAN_NO_HT40MINUS)) { + ; + } else { + if (flags & IEEE80211_CHAN_NO_HT40PLUS) + len += scnprintf(p + len, str_len - len, + "%s", + "[HT40-]"); + else if (flags & IEEE80211_CHAN_NO_HT40MINUS) + len += scnprintf(p + len, str_len - len, + "%s", + "[HT40+]"); + else + len += scnprintf(p + len, str_len - len, + "%s", + "[HT40+][HT40-]"); + } + + return; +} + +static ssize_t ath6kl_chan_list_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (2048) + struct ath6kl *ar = file->private_data; + struct wiphy *wiphy = ar->wiphy; + struct reg_info *reg = ar->reg_ctx; + u8 *buf, *p; + u8 flag_string[96]; + unsigned int len = 0; + int i, buf_len; + enum ieee80211_band band; + ssize_t ret_cnt; + + if (!reg) + return 0; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + p = buf; + buf_len = _BUF_SIZE; + + if (reg->current_regd) { + len += scnprintf(p + len, buf_len - len, + "\nCurrent Regulatory - %08x %c%c %d rules\n", + reg->current_reg_code, + reg->current_regd->alpha2[0], + reg->current_regd->alpha2[1], + reg->current_regd->n_reg_rules); + + for (i = 0; i < reg->current_regd->n_reg_rules; i++) { + struct ieee80211_reg_rule *regRule; + + regRule = ®->current_regd->reg_rules[i]; + len += scnprintf(p + len, buf_len - len, + "\t[%d - %d, HT%d]\n", + regRule->freq_range.start_freq_khz / 1000, + regRule->freq_range.end_freq_khz / 1000, + regRule->freq_range.max_bandwidth_khz / 1000); + } + } + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wiphy->bands[band]) + continue; + + len += scnprintf(p + len, buf_len - len, "\n%s\n", + (band == IEEE80211_BAND_2GHZ ? + "2.4GHz" : "5GHz")); + + for (i = 0; i < wiphy->bands[band]->n_channels; i++) { + struct ieee80211_channel *chan; + + chan = &wiphy->bands[band]->channels[i]; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + __chan_flag_to_string(chan->flags, flag_string, 96); + len += scnprintf(p + len, buf_len - len, + " CH%4d - %4d %s\n", + chan->hw_value, + chan->center_freq, + flag_string); + } + } + + len += scnprintf(p + len, buf_len - len, + "\nCurrent operation channel\n"); + + for (i = 0; i < ar->vif_max; i++) { + struct ath6kl_vif *vif = ath6kl_get_vif_by_index(ar, i); + + if (vif) + len += scnprintf(p + len, buf_len - len, + " VIF%d [%s] - ch %d phy %d type %d\n", + i, + (test_bit(CONNECTED, &vif->flags) ? + "CONN" : "IDLE"), + vif->bss_ch, + vif->phymode, + vif->chan_type); + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for Channel List. */ +static const struct file_operations fops_chan_list = { + .read = ath6kl_chan_list_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for AP ACL */ +static ssize_t ath6kl_ap_acl_policy_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + char *p; + int policy; + char buf[32]; + ssize_t len; + int i; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "%d", &policy); + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + + /* Now, only for softap mode, not P2P-GO mode */ + if ((vif) && + (vif->wdev.iftype == NL80211_IFTYPE_AP)) + ath6kl_ap_acl_config_policy(vif, (u8)policy); + } + + return count; +} + +static ssize_t ath6kl_ap_acl_policy_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (256) + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u8 *buf; + unsigned int len = 0; + int i; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + if ((vif) && + (vif->ap_acl_ctx)) { + struct ap_acl_info *ap_acl = vif->ap_acl_ctx; + + len += scnprintf(buf + len, _BUF_SIZE - len, + "int-%d acl_policy %s\n", + vif->fw_vif_idx, + (ap_acl->acl_mode ? + (ap_acl->acl_mode == AP_ACL_MODE_DENY ? + "DENY" : "ALLOW") : + "DISABLED")); + } + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for AP ACL */ +static const struct file_operations fops_ap_acl_policy = { + .read = ath6kl_ap_acl_policy_read, + .write = ath6kl_ap_acl_policy_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for AP ACL MAC-LIST */ +static ssize_t ath6kl_ap_acl_mac_list_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + char *p; + bool removed; + char buf[32]; + u8 mac_addr[ETH_ALEN]; + int addr[ETH_ALEN]; + ssize_t len; + int i; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + if (sscanf(p, "%02x:%02x:%02x:%02x:%02x:%02x", + &addr[0], &addr[1], &addr[2], + &addr[3], &addr[4], &addr[5]) != ETH_ALEN) + return -EINVAL; + + SEEK_SPACE; + SKIP_SPACE; + + if (strstr(p, "r")) + removed = true; + else + removed = false; + + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = (u8)addr[i]; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + + /* Now, only for softap mode, not P2P-GO mode */ + if ((vif) && + (vif->wdev.iftype == NL80211_IFTYPE_AP)) + ath6kl_ap_acl_config_mac_list(vif, + mac_addr, + removed); + } + + return count; +} + +static ssize_t ath6kl_ap_acl_mac_list_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (2048) + struct ath6kl *ar = file->private_data; + u8 *buf; + unsigned int len = 0; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + len = ath6kl_ap_acl_dump(ar, buf, _BUF_SIZE); + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for AP ACL MAC-LIST */ +static const struct file_operations fops_ap_acl_mac_list = { + .read = ath6kl_ap_acl_mac_list_read, + .write = ath6kl_ap_acl_mac_list_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation functions for AMPDU */ +static ssize_t ath6kl_ampdu_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + u32 ampdu; + char buf[32]; + ssize_t len; + int i; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &du)) + return -EINVAL; + + for (i = 0; i < ar->vif_max; i++) { + vif = ath6kl_get_vif_by_index(ar, i); + /* only support 8 TIDs now. */ + if (vif) + ath6kl_wmi_allow_aggr_cmd(vif->ar->wmi, + vif->fw_vif_idx, + (ampdu ? 0xff : 0), + (ampdu ? 0xff : 0)); + } + + return count; +} + +/* debug fs for AMPDU */ +static const struct file_operations fops_ampdu = { + .write = ath6kl_ampdu_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_parse_arp_offload_ip_addrs(const char __user *user_buf, + size_t count, + u8 *ip_addrs) +{ + char buf[64]; + char *p; + unsigned int len; + int value, i; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + for (i = 0 ; i < 4; i++) { + sscanf(p, "%d", &value); + if (value > 0xff) { + /*Wrong value should be less than 255*/ + return -EFAULT; + } + *(ip_addrs+i) = value; + if (i < 3) { + SEEK_SPACE; + SKIP_SPACE; + } + } + + return 0; +} + +static ssize_t ath6kl_arp_offload_ipaddrs_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + int ret; + u8 ip_addrs[4]; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + vif->arp_offload_ip_set = 0; + ret = ath6kl_parse_arp_offload_ip_addrs(user_buf, count, ip_addrs); + + if (ret) + return ret; + + if (ath6kl_wmi_set_arp_offload_ip_cmd(ar->wmi, ip_addrs)) + return -EIO; + vif->arp_offload_ip_set = 1; + + return count; +} + +/* debug fs for ARP OFFLOAD */ +static const struct file_operations fops_arp_offload_ip_addrs = { + .write = ath6kl_arp_offload_ipaddrs_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +#define TBTT_MCC_STA_LOWER_BOUND 20 +#define TBTT_MCC_STA_UPPER_BOUND 80 +/* File operation for mcc profile */ +static ssize_t ath6kl_mcc_profile(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[10]; + ssize_t len; + u32 mcc_profile; + u32 sta_time; + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &mcc_profile)) + return -EINVAL; + sta_time = (mcc_profile & 0x0F)*10; + if(mcc_profile & WMI_MCC_DUAL_TIME_MASK){ + if (sta_time > TBTT_MCC_STA_UPPER_BOUND || + sta_time < TBTT_MCC_STA_LOWER_BOUND) { + return -EINVAL; + } + } + if (ath6kl_wmi_set_mcc_profile_cmd(ar->wmi, mcc_profile)) + return -EIO; + return count; +} + +/* debug fs for mcc profile */ +static const struct file_operations fops_mcc_profile = { + .write = ath6kl_mcc_profile, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation for seamless mcc/scc switch */ +static ssize_t ath6kl_seamless_mcc_scc_switch(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[10]; + ssize_t len; + u32 freq; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &freq)) + return -EINVAL; + + if (ath6kl_wmi_set_seamless_mcc_scc_switch_freq_cmd(ar->wmi, freq)) + return -EIO; + + return count; +} + +/* debug fs for seamless mcc/scc switch */ +static const struct file_operations fops_seamless_mcc_scc_switch = { + .write = ath6kl_seamless_mcc_scc_switch, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation for P2P Frame Retry */ +static ssize_t ath6kl_p2p_frame_retry_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u32 p2p_frame_retry; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &p2p_frame_retry)) + return -EINVAL; + + if (p2p_frame_retry) + ar->p2p_frame_retry = true; + else + ar->p2p_frame_retry = false; + + return count; +} + +/* debug fs for P2P Frame Retry */ +static const struct file_operations fops_p2p_frame_retry = { + .write = ath6kl_p2p_frame_retry_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation for P2P Frame Conditional Reject */ +static ssize_t ath6kl_p2p_frame_cond_reject_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u32 p2p_frame_cond_reject; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &p2p_frame_cond_reject)) + return -EINVAL; + + if (p2p_frame_cond_reject) + ar->p2p_frame_not_report = true; + else + ar->p2p_frame_not_report = false; + + return count; +} + +/* debug fs for P2P Frame Conditional Reject */ +static const struct file_operations p2p_frame_cond_reject = { + .write = ath6kl_p2p_frame_cond_reject_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_debug_quirks_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[32]; + ssize_t len; + char *p; + u32 quirks, reset; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + p = buf; + + SKIP_SPACE; + + sscanf(p, "0x%x", &quirks); + + SEEK_SPACE; + SKIP_SPACE; + + sscanf(p, "%d", &reset); + + debug_quirks = quirks; + ar->mod_debug_quirks = debug_quirks; + + if (reset) + ath6kl_reset_device(ar, ar->target_type, true, true); + + return count; +} + +static ssize_t ath6kl_debug_quirks_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + int len; + + len = snprintf(buf, sizeof(buf), "debug_quirks: 0x%x\n", debug_quirks); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_debug_quirks = { + .read = ath6kl_debug_quirks_read, + .write = ath6kl_debug_quirks_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath6kl_debug_disable_scan(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[32]; + ssize_t len; + u32 value; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &value)) + return -EINVAL; + + if (value) + set_bit(DISABLE_SCAN, &ar->flag); + else + clear_bit(DISABLE_SCAN, &ar->flag); + + return count; +} + +static const struct file_operations fops_disable_scan = { + .write = ath6kl_debug_disable_scan, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation for P2P Frame Conditional Reject */ +static ssize_t ath6kl_disable_runtime_flowctrl_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u32 value; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &value)) + return -EINVAL; + + if (value) + ar->conf_flags |= ATH6KL_CONF_DISABLE_SKIP_FLOWCTRL; + else + ar->conf_flags &= ~ATH6KL_CONF_DISABLE_SKIP_FLOWCTRL; + + if (ar->conf_flags & ATH6KL_CONF_ENABLE_FLOWCTRL) { + if (ar->conf_flags & ATH6KL_CONF_DISABLE_SKIP_FLOWCTRL) + clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag); + else { + if (test_bit(MCC_ENABLED, &ar->flag)) + clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag); + else + set_bit(SKIP_FLOWCTRL_EVENT, &ar->flag); + } + } else + clear_bit(SKIP_FLOWCTRL_EVENT, &ar->flag); + + return count; +} + +static ssize_t ath6kl_disable_runtime_flowctrl_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[16]; + int len; + + len = snprintf(buf, sizeof(buf), "%d %d\n", + (ar->conf_flags & + ATH6KL_CONF_DISABLE_SKIP_FLOWCTRL) ? 1 : 0, + test_bit(SKIP_FLOWCTRL_EVENT, &ar->flag)); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + + +static const struct file_operations fops_disable_runtime_flowctrl = { + .read = ath6kl_disable_runtime_flowctrl_read, + .write = ath6kl_disable_runtime_flowctrl_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; +#ifdef USB_AUTO_SUSPEND + +int debugfs_get_pm_state(struct ath6kl *usbpm_ar) +{ + return usbpm_ar->state; +} + +static ssize_t ath6kl_usb_autopm_usagecnt_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int ret; + int value; + struct ath6kl *ar = file->private_data; + + ret = kstrtou32_from_user(user_buf, count, 0, &value); + + if (ret) + return ret; + + if (value == 0) { + ath6kl_hif_auto_pm_enable(ar); + ath6kl_dbg(ATH6KL_DBG_ANY, ("auto pm -1\n")); + } else if (value == 1) { + ath6kl_hif_auto_pm_disable(ar); + ath6kl_dbg(ATH6KL_DBG_ANY, ("auto pm +1\n")); + } + + return count; + +} +static ssize_t ath6kl_usb_autopm_usagecnt_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + int len; + int usb_auto_usagecnt; + struct ath6kl *ar = file->private_data; + + usb_auto_usagecnt = ath6kl_hif_auto_pm_get_usage_cnt(ar); + + len = snprintf(buf, sizeof(buf), + "usbautopm: 0x%x\n", usb_auto_usagecnt); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + + +static const struct file_operations fops_usb_autopm_usagecnt = { + .read = ath6kl_usb_autopm_usagecnt_read, + .write = ath6kl_usb_autopm_usagecnt_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + + + + +static ssize_t ath6kl_usb_pm_state_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + int len; + int usb_pm_state; + struct ath6kl *ar = file->private_data; + + usb_pm_state = debugfs_get_pm_state(ar); + + len = snprintf(buf, sizeof(buf), "pm_state: 0x%x\n", usb_pm_state); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + + +static const struct file_operations fops_usb_pm_state = { + .read = ath6kl_usb_pm_state_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +#endif /* USB_AUTO_SUSPEND */ + +/* File operation for P2P IE not append */ +static ssize_t ath6kl_p2p_ie_not_append_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u32 p2p_ie_not_append; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtou32(buf, 0, &p2p_ie_not_append)) + return -EINVAL; + + ar->p2p_ie_not_append = 0; + if (p2p_ie_not_append & P2P_IE_IN_PROBE_REQ) + ar->p2p_ie_not_append |= P2P_IE_IN_PROBE_REQ; + + if (p2p_ie_not_append & P2P_IE_IN_ASSOC_REQ) + ar->p2p_ie_not_append |= P2P_IE_IN_ASSOC_REQ; + + return count; +} + +static ssize_t ath6kl_p2p_ie_not_append_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; + char buf[64]; + int len = 0; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if ((ar->p2p_concurrent) && + (vif->nw_type == INFRA_NETWORK)) { + len = snprintf(buf, sizeof(buf), + "ProbeReq %s append, AssocReq %s append\n", + ((ar->p2p_ie_not_append & P2P_IE_IN_PROBE_REQ) ? + "NOT" : ""), + ((ar->p2p_ie_not_append & P2P_IE_IN_ASSOC_REQ) ? + "NOT" : "")); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +/* debug fs for P2P IE not append */ +static const struct file_operations fops_p2p_ie_not_append = { + .read = ath6kl_p2p_ie_not_append_read, + .write = ath6kl_p2p_ie_not_append_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation for AP Admission-Control */ +static ssize_t ath6kl_ap_admc_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (256) + struct ath6kl *ar = file->private_data; + u8 *buf; + unsigned int len = 0; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + len = ath6kl_ap_admc_dump(ar, buf, _BUF_SIZE); + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for AP Admission-Control. */ +static const struct file_operations fops_ap_admc = { + .read = ath6kl_ap_admc_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/* File operation for P2P RecommendChannel */ +static ssize_t ath6kl_p2p_rc_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define _BUF_SIZE (1500) + struct ath6kl *ar = file->private_data; + u8 *buf; + unsigned int len = 0; + ssize_t ret_cnt; + + buf = kmalloc(_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + len = ath6kl_p2p_rc_dump(ar, buf, _BUF_SIZE); + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + return ret_cnt; +#undef _BUF_SIZE +} + +/* debug fs for P2P RecommendChannel. */ +static const struct file_operations fops_p2p_rc = { + .read = ath6kl_p2p_rc_read, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath6kl_debug_init(struct ath6kl *ar) +{ + skb_queue_head_init(&ar->debug.fwlog_queue); + init_completion(&ar->debug.fwlog_completion); + + /* + * Actually we are lying here but don't know how to read the mask + * value from the firmware. + */ + ar->debug.fwlog_mask = 0; + + ar->debugfs_phy = debugfs_create_dir("ath6kl", + ar->wiphy->debugfsdir); + if (!ar->debugfs_phy) + return -ENOMEM; + + debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, + &fops_tgt_stats); + + debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar, + &fops_credit_dist_stats); + + debugfs_create_file("endpoint_stats", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_endpoint_stats); + + debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar, + &fops_fwlog); + + debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar, + &fops_fwlog_block); + + debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy, + ar, &fops_fwlog_mask); + + debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, + &fops_diag_reg_read); + + debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar, + &fops_reg_dump); + + debugfs_create_file("lrssi_roam_param", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_lrssi_roam_param); + + debugfs_create_file("driver_version", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_driver_version); + + debugfs_create_file("rx_drop_operation", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_rx_drop_operation); + + debugfs_create_file("reg_write", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_diag_reg_write); + + debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar, + &fops_war_stats); + + debugfs_create_file("roam_table", S_IRUSR, ar->debugfs_phy, ar, + &fops_roam_table); + + debugfs_create_file("force_roam", S_IWUSR, ar->debugfs_phy, ar, + &fops_force_roam); + + debugfs_create_file("roam_mode", S_IWUSR, ar->debugfs_phy, ar, + &fops_roam_mode); + + debugfs_create_file("keepalive", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, + &fops_keepalive); + + debugfs_create_file("disconnect_timeout", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_disconnect_timeout); + + debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar, + &fops_create_qos); + + debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar, + &fops_delete_qos); + + debugfs_create_file("bgscan_interval", S_IWUSR, + ar->debugfs_phy, ar, &fops_bgscan_int); + + debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar, + &fops_power_params); + + debugfs_create_file("lpl_force_enable", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_lpl_force_enable); + + debugfs_create_file("mimo_ps_enable", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_mimo_ps_enable); + + debugfs_create_file("mimo_ps_params", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_mimo_ps_params); + + debugfs_create_file("green_tx_enable", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_green_tx); + + debugfs_create_file("green_tx_params", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_green_tx_params); + + debugfs_create_file("ht_cap_params", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_ht_cap_params); + + debugfs_create_file("ht_coex_params", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_ht_coex_params); + + debugfs_create_file("debug_mask", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_debug_mask); + + debugfs_create_file("tx_amsdu", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_tx_amsdu); + + debugfs_create_file("tx_amsdu_params", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_tx_amsdu_params); + + debugfs_create_file("htc_stat", S_IRUSR, + ar->debugfs_phy, ar, &fops_htc_stat); + + debugfs_create_file("hif_stat", S_IRUSR, + ar->debugfs_phy, ar, &fops_hif_stat); + + debugfs_create_file("hif_max_sche", S_IWUSR, + ar->debugfs_phy, ar, &fops_hif_pipe_max_sche); + + debugfs_create_file("ap_apsd", S_IWUSR, + ar->debugfs_phy, ar, &fops_ap_apsd); + + debugfs_create_file("ap_intrabss", S_IWUSR, + ar->debugfs_phy, ar, &fops_ap_intrabss); + + debugfs_create_file("force_passcan", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_force_passcan); + + debugfs_create_file("pmkid_list", S_IRUSR, + ar->debugfs_phy, ar, &fops_pmkid_list); + + debugfs_create_file("txrate_sereies", S_IWUSR, + ar->debugfs_phy, ar, &fops_txseries_write); + + debugfs_create_file("p2p_flowctrl_stat", S_IRUSR, + ar->debugfs_phy, ar, &fops_p2p_flowctrl_stat); + + debugfs_create_file("ap_ps_stat", S_IRUSR, + ar->debugfs_phy, ar, &fops_ap_ps_stat); + + debugfs_create_file("rx_aggr_params", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_rx_aggr_params); + + debugfs_create_file("scan_params", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_scan_params); + + debugfs_create_file("ap_keepalive_params", S_IRUSR, + ar->debugfs_phy, ar, &fops_ap_keepalive_params); + + debugfs_create_file("tgt_ap_stats", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_tgt_ap_stats); + + debugfs_create_file("chan_list", S_IRUSR, + ar->debugfs_phy, ar, &fops_chan_list); + + debugfs_create_file("ap_acl_policy", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_ap_acl_policy); + + debugfs_create_file("ap_acl_mac_list", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_ap_acl_mac_list); + + debugfs_create_file("ampdu", S_IWUSR, + ar->debugfs_phy, ar, &fops_ampdu); + + debugfs_create_file("arp_ip_addrs", S_IWUSR, + ar->debugfs_phy, ar, &fops_arp_offload_ip_addrs); + + debugfs_create_file("mcc_profile", S_IWUSR, + ar->debugfs_phy, ar, &fops_mcc_profile); + + debugfs_create_file("seamless_mcc_scc_switch", S_IWUSR, + ar->debugfs_phy, ar, &fops_seamless_mcc_scc_switch); + + debugfs_create_file("p2p_frame_retry", S_IWUSR, + ar->debugfs_phy, ar, &fops_p2p_frame_retry); + + debugfs_create_file("p2p_frame_cond_reject", S_IWUSR, + ar->debugfs_phy, ar, &p2p_frame_cond_reject); + + debugfs_create_file("debug_quirks", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_debug_quirks); + + debugfs_create_file("disable_scan", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_disable_scan); + + debugfs_create_file("disable_runtime_flowctrl", S_IWUSR, + ar->debugfs_phy, ar, &fops_disable_runtime_flowctrl); +#ifdef USB_AUTO_SUSPEND + debugfs_create_file("usb_autopm_usagecnt", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_usb_autopm_usagecnt); + + debugfs_create_file("usb_pm_state", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_usb_pm_state); +#endif + + debugfs_create_file("p2p_ie_not_append", S_IWUSR, + ar->debugfs_phy, ar, &fops_p2p_ie_not_append); + + debugfs_create_file("ap_admc", S_IRUSR, + ar->debugfs_phy, ar, &fops_ap_admc); + + debugfs_create_file("antdivcfg", S_IWUSR, + ar->debugfs_phy, ar, &fops_antdiv_write); + + debugfs_create_file("antdivstat", S_IRUSR, + ar->debugfs_phy, ar, &fops_antdiv_state_read); + + debugfs_create_file("p2p_rc", S_IRUSR, + ar->debugfs_phy, ar, &fops_p2p_rc); + + return 0; +} + + +void ath6kl_debug_cleanup(struct ath6kl *ar) +{ + skb_queue_purge(&ar->debug.fwlog_queue); + complete(&ar->debug.fwlog_completion); + kfree(ar->debug.roam_tbl); +} + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/debug.h b/drivers/net/wireless/ath/ath6kl-3.5/debug.h new file mode 100644 index 000000000000..99f1f108ea74 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/debug.h @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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_H +#define DEBUG_H + +#include "hif.h" + +enum ATH6KL_MODULE_QUIRKS { + /* enable suspend cutpower */ + ATH6KL_MODULE_SUSPEND_CUTPOWER = BIT(0), + + /* enable lpl */ + AT6HKL_MODULE_LPL_ENABLE = BIT(1), + + /* enable mimo ps */ + ATH6KL_MODULE_MIMO_PS_ENABLE = BIT(2), + + /* disable RX aggregation drop packets */ + ATH6KL_MODULE_DISABLE_RX_AGGR_DROP = BIT(3), + + /* enable test mode */ + ATH6KL_MODULE_TESTMODE_ENABLE = BIT(4), + + /* anti-noise-immunity */ + ATH6KL_MODULES_ANI_ENABLE = BIT(5), + + /* enable endpoing loopback */ + ATH6KL_MODULE_ENABLE_EPPING = BIT(6), + + /* disable 5G support */ + ATH6KL_MODULE_DISABLE_5G = BIT(7), + + /* enable max. fw vif */ + ATH6KL_MODULE_P2P_MAX_FW_VIF = BIT(8), + + /* hole */ + + /* enable usb remote wakeup support */ + ATH6KL_MODULE_ENABLE_USB_REMOTE_WKUP = BIT(10), + + /* disable 2.4G HT40 in default */ + ATH6KL_MODULE_DISABLE_2G_HT40 = BIT(11), + + /* enable fwlog by default */ + ATH6KL_MODULE_ENABLE_FWLOG = BIT(12), + + /* Disable USB Auto-suspend */ + ATH6KL_MODULE_DISABLE_USB_AUTO_SUSPEND = BIT(13), + + /* hole */ + + /* offload AP keep-alive to supplicant */ + ATH6KL_MODULE_KEEPALIVE_BY_SUPP = BIT(15), + + /* disable create virtual interface automatically */ + ATH6KL_MODULE_DISABLE_AUTO_ADD_INF = BIT(16), + + /* enable sche-scan */ + ATH6KL_MODULE_ENABLE_SCHE_SCAN = BIT(17), + + /* enable extensive fwlog */ + ATH6KL_MODULE_ENABLE_FWLOG_EXT = BIT(18), + + /* enable channel-mode select for P2P-GO */ + ATH6KL_MODULE_ENABLE_P2P_CHANMODE = BIT(19), + + /* not to wait init defer function completed */ + ATH6KL_MODULE_DISABLE_WAIT_DEFER = BIT(20), + + /* enable fw crash notify function */ + ATH6KL_MODULE_ENABLE_FW_CRASH_NOTIFY = BIT(21), + + /* disable usb auto power management feature */ + ATH6KL_MODULE_DISABLE_USB_AUTO_PM = BIT(22), + + /* disable wmi sync mechanism */ + ATH6KL_MODULE_DISABLE_WMI_SYC = BIT(23), + + /* workaround for EV119712/CR468120 */ + ATH6KL_MODULE_WAR_BAD_P2P_GO = BIT(24), + + /* Config AP keep-alive from supplicant */ + ATH6KL_MODULE_KEEPALIVE_CONFIG_BY_SUPP = BIT(25), + + /* hole */ +}; + +enum ATH6KL_MODULE_P2P { + /* enable single interface p2p */ + ATH6KL_MODULEP2P_P2P_ENABLE = BIT(0), + + /* enable p2p_concurrent, with dedicate */ + ATH6KL_MODULEP2P_CONCURRENT_ENABLE_DEDICATE = BIT(1), + + /* enable p2p_concurrent, no dedicate */ + ATH6KL_MODULEP2P_CONCURRENT_NO_DEDICATE = BIT(2), + + /* enable p2p_concurrent, ath6kl-3.2 compatible */ + ATH6KL_MODULEP2P_CONCURRENT_COMPAT = BIT(3), + + /* enable p2p_concurrent with multi-channel */ + ATH6KL_MODULEP2P_CONCURRENT_MULTICHAN = BIT(4), + + /* hole */ + + /* enable p2p_concurrent with softAP */ + ATH6KL_MODULEP2P_CONCURRENT_AP = BIT(8), + + /* enable p2p_in_pasv_chan */ + ATH6KL_MODULEP2P_P2P_IN_PASSIVE_CHAN = BIT(9), + + /* enable p2p_wise_scan */ + ATH6KL_MODULEP2P_P2P_WISE_SCAN = BIT(10), +}; + +enum ATH6KL_MODULE_ROAM { + /* Not set, obey driver default */ + ATH6KL_MODULEROAM_DEFAULT = BIT(0), + + /* Enable roam but disable at multiple connection */ + ATH6KL_MODULEROAM_NO_LRSSI_SCAN_AT_MULTI = BIT(1), + + /* Enabel roam but disable lrssi scan */ + ATH6KL_MODULEROAM_DISABLE_LRSSI_SCAN = BIT(2), + + /* Enable roam at all time */ + ATH6KL_MODULEROAM_ENABLE_ALL = BIT(3), + + /* Disable roam */ + ATH6KL_MODULEROAM_DISABLE = BIT(4), +}; + +enum ATH6K_DEBUG_MASK { + ATH6KL_DBG_CREDIT = BIT(0), + ATH6KL_DBG_REGDB = BIT(1), + ATH6KL_DBG_WLAN_TX = BIT(2), /* wlan tx */ + ATH6KL_DBG_WLAN_RX = BIT(3), /* wlan rx */ + ATH6KL_DBG_BMI = BIT(4), /* bmi tracing */ + ATH6KL_DBG_HTC = BIT(5), + ATH6KL_DBG_HIF = BIT(6), + ATH6KL_DBG_IRQ = BIT(7), /* interrupt processing */ + ATH6KL_DBG_ACL = BIT(8), /* access control list */ + ATH6KL_DBG_ADMC = ATH6KL_DBG_ACL, /* admission control */ + ATH6KL_DBG_RC = BIT(9), /* P2P recommend channel */ + ATH6KL_DBG_WMI = BIT(10), /* wmi tracing */ + ATH6KL_DBG_TRC = BIT(11), /* generic func tracing */ + ATH6KL_DBG_SCATTER = BIT(12), /* hif scatter tracing */ + ATH6KL_DBG_WLAN_CFG = BIT(13), /* cfg80211 i/f file tracing */ + ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx frames */ + ATH6KL_DBG_AGGR = BIT(15), /* aggregation */ + ATH6KL_DBG_SDIO = BIT(16), + ATH6KL_DBG_SDIO_DUMP = BIT(17), + ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */ + ATH6KL_DBG_WMI_DUMP = BIT(19), + ATH6KL_DBG_SUSPEND = BIT(20), + ATH6KL_DBG_USB = BIT(21), + ATH6KL_DBG_USB_BULK = BIT(22), + ATH6KL_DBG_HTCOEX = BIT(23), /* HT20/40 Coexist */ + ATH6KL_DBG_WLAN_TX_AMSDU = BIT(24), /* wlan tx a-msdu */ + ATH6KL_DBG_POWERSAVE = BIT(25), /* power-saving */ + ATH6KL_DBG_WOWLAN = BIT(26), /* wowlan tracing */ + ATH6KL_DBG_RTT = BIT(27), + ATH6KL_DBG_FLOWCTRL = BIT(28), /* P2P flowctrl */ + ATH6KL_DBG_KEEPALIVE = BIT(29), /* AP keep-alive */ +#ifdef ACS_SUPPORT + ATH6KL_DBG_ACS = BIT(30), /* ACS */ +#else + /* hole */ +#endif + ATH6KL_DBG_INFO = BIT(31), /* keep last */ + ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ +}; + +extern unsigned int debug_mask; +extern unsigned int debug_quirks; +extern __printf(2, 3) +int ath6kl_printk(const char *level, const char *fmt, ...); + +#define ath6kl_info(fmt, ...) \ + ath6kl_printk(KERN_INFO, fmt, ##__VA_ARGS__) +#define ath6kl_err(fmt, ...) \ + ath6kl_printk(KERN_ERR, fmt, ##__VA_ARGS__) +#define ath6kl_warn(fmt, ...) \ + ath6kl_printk(KERN_WARNING, fmt, ##__VA_ARGS__) + +#define AR_DBG_LVL_CHECK(mask) (debug_mask & mask) + +enum ath6kl_war { + ATH6KL_WAR_INVALID_RATE, +}; + +static inline int ath6kl_mod_debug_quirks(struct ath6kl *ar, + enum ATH6KL_MODULE_QUIRKS mask) +{ + if (ar->mod_debug_quirks & mask) + return 1; + else + return 0; +} + +void ath6kl_send_event_to_app(struct net_device *dev, + u16 event_id, u8 ifid, + u8 *datap, int len); + +void ath6kl_send_genevent_to_app(struct net_device *dev, + u16 event_id, u8 ifid, + u8 *datap, int len); + +#ifdef CONFIG_QC_INTERNAL +int ath6kl_set_rd(struct ath6kl *ar); +#else +static inline int ath6kl_set_rd(struct ath6kl *ar) +{ + return 0; +} +#endif + +#ifdef CONFIG_ATH6KL_DEBUG +#define ath6kl_dbg(mask, fmt, ...) \ + ({ \ + int rtn; \ + if (debug_mask & mask) \ + rtn = ath6kl_printk(KERN_DEBUG, fmt, ##__VA_ARGS__); \ + else \ + rtn = 0; \ + \ + rtn; \ + }) + +static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ + if (debug_mask & mask) { + if (msg) + ath6kl_dbg(mask, "%s\n", msg); + + print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); + } +} + +void ath6kl_dump_registers(struct ath6kl_device *dev, + struct ath6kl_irq_proc_registers *irq_proc_reg, + struct ath6kl_irq_enable_reg *irq_en_reg); +void dump_cred_dist_stats(struct htc_target *target); +void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len); +void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war); +int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, + size_t len); +void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive); +void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout); +int ath6kl_debug_init(struct ath6kl *ar); +void ath6kl_debug_cleanup(struct ath6kl *ar); + +#else +static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, + const char *fmt, ...) +{ + return 0; +} + +static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ +} + +static inline void ath6kl_dump_registers(struct ath6kl_device *dev, + struct ath6kl_irq_proc_registers *irq_proc_reg, + struct ath6kl_irq_enable_reg *irq_en_reg) +{ + +} +static inline void dump_cred_dist_stats(struct htc_target *target) +{ +} + +static inline void ath6kl_debug_fwlog_event(struct ath6kl *ar, + const void *buf, size_t len) +{ +} + +static inline void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) +{ +} + +static inline int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, + const void *buf, size_t len) +{ + return 0; +} + +static inline void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive) +{ +} + +static inline void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, + u8 timeout) +{ +} + +static inline int ath6kl_debug_init(struct ath6kl *ar) +{ + return 0; +} + +static inline void ath6kl_debug_cleanup(struct ath6kl *ar) +{ +} + +#endif +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/diag.c b/drivers/net/wireless/ath/ath6kl-3.5/diag.c new file mode 100644 index 000000000000..70ab0a045c72 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/diag.c @@ -0,0 +1,1443 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "wmi.h" +#include "diagnose.h" + + +#ifdef ATH6KL_DIAGNOSTIC + +struct wifi_diag_callbacks diag_evt_callback; +struct sk_buff_head diag_events; +static DEFINE_MUTEX(diag_event_mutex); + + +enum WIFI_DIAG_PM_STATE { + WIFI_DIAG_PM_STATE_SLEEP = 1, + WIFI_DIAG_PM_STATE_AWAKE = 2, + WIFI_DIAG_PM_STATE_FAKE_SLEEP = 3, +}; + + +/* diag wmi command */ +int +ath6kl_wmi_cmd_send_diag(struct wmi *wmi, struct sk_buff *skb, + enum wmid_command_id cmd_id, + enum wmi_sync_flag sync_flag) +{ + struct wmid_cmd_hdr *cmd_hdr; + int ret; + + skb_push(skb, sizeof(struct wmid_cmd_hdr)); + + cmd_hdr = (struct wmid_cmd_hdr *) skb->data; + cmd_hdr->cmd_id = cpu_to_le32(cmd_id); + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_DIAGNOSTIC_CMDID, sync_flag); + + return ret; +} + +int +ath6kl_wmi_macfilter_cmd(struct wmi *wmi, int type, u32 low, u32 high) +{ + struct sk_buff *skb; + int ret; + struct wmid_macfilter_cmd *macfilter; + + skb = ath6kl_wmi_get_new_buf( + sizeof(struct wmid_cmd_hdr)+sizeof(*macfilter)); + if (!skb) + return -ENOMEM; + macfilter = (struct wmid_macfilter_cmd *)skb->data; + macfilter->type = type; + macfilter->low = low; + macfilter->high = high; + + ret = ath6kl_wmi_cmd_send_diag(wmi, skb, WMID_MACFILTER_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int +ath6kl_wmi_fsm_cmd(struct wmi *wmi, bool enable) +{ + struct sk_buff *skb; + int ret; + struct wmid_event_set_cmd *fsm; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmid_cmd_hdr)+sizeof(*fsm)); + if (!skb) + return -ENOMEM; + fsm = (struct wmid_event_set_cmd *)skb->data; + fsm->enable = enable; + + ret = ath6kl_wmi_cmd_send_diag(wmi, skb, WMID_FSM_EVENT_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int +ath6kl_wmi_pwrsave_cmd(struct wmi *wmi, bool enable) +{ + struct sk_buff *skb; + int ret; + struct wmid_event_set_cmd *ps; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmid_cmd_hdr)+sizeof(*ps)); + if (!skb) + return -ENOMEM; + ps = (struct wmid_event_set_cmd *)skb->data; + ps->enable = enable; + + ret = ath6kl_wmi_cmd_send_diag(wmi, skb, WMID_PWR_SAVE_EVENT_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int +ath6kl_wmi_interference_cmd(struct wmi *wmi) +{ + struct sk_buff *skb; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmid_cmd_hdr)); + if (!skb) + return -ENOMEM; + + ret = ath6kl_wmi_cmd_send_diag(wmi, skb, WMID_INTERFERENCE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int +ath6kl_wmi_rxtime_cmd(struct wmi *wmi) +{ + struct sk_buff *skb; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmid_cmd_hdr)); + if (!skb) + return -ENOMEM; + + ret = ath6kl_wmi_cmd_send_diag(wmi, skb, WMID_RXTIME_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int +ath6kl_wmi_pktlog_enable_cmd(struct wmi *wmi, + struct wmi_enable_pktlog_cmd *options) +{ + struct sk_buff *skb; + struct wmi_enable_pktlog_cmd *cmd; + int status = 0; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_enable_pktlog_cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_enable_pktlog_cmd *) skb->data; + cmd->evlist = options->evlist; + cmd->option = options->option; + cmd->trigger_thresh = cpu_to_le32(options->trigger_thresh); + cmd->trigger_interval = cpu_to_le32(options->trigger_interval); + cmd->trigger_tail_count = cpu_to_le32(options->trigger_tail_count); + cmd->buffer_size = cpu_to_le32(options->buffer_size); + + status = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_PKTLOG_ENABLE_CMDID, + NO_SYNC_WMIFLAG); + + return status; +} + +int +ath6kl_wmi_pktlog_disable_cmd(struct wmi *wmi) +{ + return ath6kl_wmi_simple_cmd(wmi, 0, WMI_PKTLOG_DISABLE_CMDID); +} + + +/* diag wmi event */ +#if 0 +static void +dump_hex(u8 *frm, u32 len) +{ + u32 i; + printk(KERN_INFO "\n-----------------------------\n"); + for (i = 0; i < len; i++) { + printk(KERN_INFO "0x%02x ", frm[i]); + if ((i+1) % 16 == 0) + printk(KERN_INFO "\n"); + } + printk(KERN_INFO "\n"); + printk(KERN_INFO "\n=============================\n\n"); +} +#endif + +int +ath6kl_wmi_pktlog_event_rx(struct wmi *wmi, u8 *datap, u32 len) +{ + u8 *pdata = NULL; + struct ath_pktlog_hdr *log_hdr = (struct ath_pktlog_hdr *)datap ; + u32 offset = 0; + struct ath6kl_vif *vif = ath6kl_get_vif_by_index(wmi->parent_dev, 0); + + if (vif == NULL) + return -EINVAL; + + while (len - offset > 0) { + switch (le16_to_cpu(log_hdr->log_type)) { + case PKTLOG_TYPE_TXSTATUS: + pdata = (u8 *)log_hdr + sizeof(struct ath_pktlog_hdr); + wifi_diag_mac_tx_frame_event(vif, + (struct ath_pktlog_txstatus *)pdata); + break; + case PKTLOG_TYPE_TXCTL: + pdata = (u8 *)log_hdr + sizeof(struct ath_pktlog_hdr); + wifi_diag_mac_txctrl_event(vif, + (struct ath_pktlog_txctl *)pdata); + break; + case PKTLOG_TYPE_RX: + pdata = (u8 *)log_hdr + sizeof(struct ath_pktlog_hdr); + wifi_diag_mac_rx_frame_event(vif, + (struct ath_pktlog_rx *)pdata); + break; + default: + break; + } + offset += le16_to_cpu(log_hdr->size) + + sizeof(struct ath_pktlog_hdr); + log_hdr = (struct ath_pktlog_hdr *) + ((u8 *)log_hdr + le16_to_cpu(log_hdr->size) + + sizeof(struct ath_pktlog_hdr)); + } + + return 0; +} + +int +ath6kl_wmi_start_scan_event(struct ath6kl_vif *vif, u32 seq_num) +{ + enum wifi_diag_mac_fsm_t fsm_event = WIFI_DIAG_MAC_FSM_SCANNING; + printk(KERN_INFO "ath6kl_wmi_start_scan_event\n"); + wifi_diag_mac_fsm_event(vif, fsm_event, seq_num); + return 0; +} + +int +ath6kl_wmi_fsm_auth_event(struct ath6kl_vif *vif, u32 seq_num) +{ + enum wifi_diag_mac_fsm_t fsm_event = WIFI_DIAG_MAC_FSM_AUTH; + printk(KERN_INFO "ath6kl_wmi_fsm_auth_event\n"); + wifi_diag_mac_fsm_event(vif, fsm_event, seq_num); + return 0; +} + +int +ath6kl_wmi_fsm_assoc_event(struct ath6kl_vif *vif, u32 seq_num) +{ + enum wifi_diag_mac_fsm_t fsm_event = WIFI_DIAG_MAC_FSM_ASSOC; + printk(KERN_INFO "ath6kl_wmi_fsm_assoc_event\n"); + wifi_diag_mac_fsm_event(vif, fsm_event, seq_num); + return 0; +} + +int +ath6kl_wmi_fsm_deauth_event(struct ath6kl_vif *vif, u32 seq_num) +{ + enum wifi_diag_mac_fsm_t fsm_event = WIFI_DIAG_MAC_FSM_DEAUTH; + printk(KERN_INFO "ath6kl_wmi_fsm_deauth_event\n"); + wifi_diag_mac_fsm_event(vif, fsm_event, seq_num); + return 0; +} + +int +ath6kl_wmi_fsm_disassoc_event(struct ath6kl_vif *vif, u32 seq_num) +{ + enum wifi_diag_mac_fsm_t fsm_event = WIFI_DIAG_MAC_FSM_DISASSOC; + printk(KERN_INFO "ath6kl_wmi_fsm_disassoc_event\n"); + wifi_diag_mac_fsm_event(vif, fsm_event, seq_num); + return 0; +} + +int +ath6kl_wmi_pwrsave_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num) +{ + struct wmid_pwr_save_event *state = + (struct wmid_pwr_save_event *)datap; + enum wifi_diag_pwrsave_t oldpwrsave, pmpwrsave; + + printk(KERN_INFO "ath6kl_wmi_pwrsave_event\n"); + + switch (state->oldState) { + case WIFI_DIAG_PM_STATE_SLEEP: + oldpwrsave = WIFI_DIAG_DEEPSLEEP_STOP; + break; + case WIFI_DIAG_PM_STATE_AWAKE: + oldpwrsave = WIFI_DIAG_MAXPERF_STOP; + break; + case WIFI_DIAG_PM_STATE_FAKE_SLEEP: + oldpwrsave = WIFI_DIAG_FAKESLEEP_STOP; + break; + default: + return 0; + break; + } + wifi_diag_send_pwrsave_event(vif, oldpwrsave, seq_num); + + switch (state->pmState) { + case WIFI_DIAG_PM_STATE_SLEEP: + pmpwrsave = WIFI_DIAG_DEEPSLEEP_START; + break; + case WIFI_DIAG_PM_STATE_AWAKE: + pmpwrsave = WIFI_DIAG_MAXPERF_START; + break; + case WIFI_DIAG_PM_STATE_FAKE_SLEEP: + pmpwrsave = WIFI_DIAG_FAKESLEEP_START; + break; + default: + return 0; + break; + } + wifi_diag_send_pwrsave_event(vif, pmpwrsave, seq_num); + + return 0; +} + +int +ath6kl_wmi_diag_event(struct ath6kl_vif *vif, + struct wmi *wmi, struct sk_buff *skb) +{ + struct wmid_cmd_hdr *cmd; + u32 len; + u16 id; + u32 seq_num; + u8 *datap; + int ret = 0; + + if (skb->len < sizeof(struct wmid_cmd_hdr)) { + printk(KERN_INFO "bad packet 1\n"); + return -EINVAL; + } + + cmd = (struct wmid_cmd_hdr *) skb->data; + id = le32_to_cpu(cmd->cmd_id); + seq_num = le32_to_cpu(cmd->seq_num); + + skb_pull(skb, sizeof(struct wmid_cmd_hdr)); + + datap = skb->data; + len = skb->len; + + switch (id) { + case WMID_START_SCAN_EVENTID: + ret = ath6kl_wmi_start_scan_event(vif, seq_num); + break; + case WMID_FSM_AUTH_EVENTID: + ret = ath6kl_wmi_fsm_auth_event(vif, seq_num); + break; + case WMID_FSM_ASSOC_EVENTID: + ret = ath6kl_wmi_fsm_assoc_event(vif, seq_num); + break; + case WMID_FSM_DEAUTH_EVENTID: + ret = ath6kl_wmi_fsm_deauth_event(vif, seq_num); + break; + case WMID_FSM_DISASSOC_EVENTID: + ret = ath6kl_wmi_fsm_disassoc_event(vif, seq_num); + break; + case WMID_STAT_RX_RATE_EVENTID: + ret = ath6kl_wmi_stat_rx_rate_event(vif, wmi, + datap, len, seq_num); + break; + case WMID_STAT_TX_RATE_EVENTID: + ret = ath6kl_wmi_stat_tx_rate_event(vif, wmi, + datap, len, seq_num); + break; + case WMID_INTERFERENCE_EVENTID: + ath6kl_wmi_interference_event(vif, wmi, datap, len, seq_num); + break; + case WMID_RXTIME_EVENTID: + ath6kl_wmi_rxtime_event(vif, wmi, datap, len, seq_num); + break; + case WMID_PWR_SAVE_EVENTID: + ath6kl_wmi_pwrsave_event(vif, wmi, datap, len, seq_num); + break; + case WMID_FSM_CONNECT_EVENTID: + vif->diag.connect_seq_num = seq_num; + break; + case WMID_FSM_DISCONNECT_EVENTID: + vif->diag.disconnect_seq_num = seq_num; + break; + default: + printk(KERN_INFO "unknown cmd id 0x%x\n", id); + ret = -EINVAL; + break; + } + + return ret; +} + +/* diag driver handler table */ +static struct wifi_drv_hdl_list wifi_drv_hdl_table = { + .wifi_register = wifi_diag_drv_register, + .wifi_unregister = wifi_diag_drv_unregister, + .wifi_diag_reg_event_callback = wifi_diag_register_event_callback, + .wifi_diag_cmd = wifi_diag_cmd_send, +}; + + +void * +wifi_diag_drv_register(void *diag_hdl) +{ + return (void *)&wifi_drv_hdl_table; +} +EXPORT_SYMBOL(wifi_diag_drv_register); + +enum wifi_diag_status_t +wifi_diag_drv_unregister(void *drv_hdl) +{ + struct ath6kl_vif *vif = ath6kl_get_vif_by_index( + globalwmi->parent_dev, 0); + + enum wifi_diag_status_t diag_status = WIFI_DIAG_EOK; + ath6kl_wmi_macfilter_cmd(globalwmi, + WMI_PKTLOG_EVENT_TX | WMI_PKTLOG_EVENT_RX, + WIFI_DIAG_MACFILTER_DISABLEALL, + WIFI_DIAG_MACFILTER_DISABLEALL); + ath6kl_wmi_pktlog_disable_cmd(globalwmi); + ath6kl_wmi_fsm_cmd(globalwmi, false); + ath6kl_wmi_pwrsave_cmd(globalwmi, false); + + if (vif != NULL) + wifi_diag_timer_destroy(vif); + + return diag_status; +} + +enum wifi_diag_status_t +wifi_diag_register_event_callback(void *drv_hdl, + struct wifi_diag_callbacks *evt_callback) +{ + enum wifi_diag_status_t diag_status = WIFI_DIAG_EOK; + struct ath6kl_vif *vif = ath6kl_get_vif_by_index( + globalwmi->parent_dev, 0); + + if (vif == NULL) + return WIFI_DIAG_ENXIO; + + if (!vif->diag.diag_event_init) { + skb_queue_head_init(&diag_events); + vif->diag.diag_event_init = true; + } + diag_evt_callback.diag_event_callback = + evt_callback->diag_event_callback; + + return diag_status; +} + +enum wifi_diag_status_t +wifi_diag_cmd_send(void *drv_hdl, struct wifi_diag_cmd_t *cmd) +{ + enum wifi_diag_status_t diag_status = WIFI_DIAG_EOK; + struct ath6kl_vif *vif = ath6kl_get_vif_by_index( + globalwmi->parent_dev, 0); + + switch (cmd->cmd_id) { + case WIFI_DIAG_MAC_TX_FILTER_CMDID: + { + if (cmd->len == sizeof(struct wifi_diag_mac_tx_filter_cmd_t)) { + struct wifi_diag_mac_tx_filter_cmd_t *ptxfilter = + (struct wifi_diag_mac_tx_filter_cmd_t *) + cmd->cmd_data; + ath6kl_wmi_macfilter_cmd(globalwmi, + WMI_PKTLOG_EVENT_TX, + ptxfilter->filter_mask_low & + WIFI_DIAG_MACFILTER_LOW_MASK, + ptxfilter->filter_mask_high & + WIFI_DIAG_MACFILTER_HIGH_MASK); + } + break; + } + case WIFI_DIAG_MAC_RX_FILTER_CMDID: + { + if (cmd->len == sizeof(struct wifi_diag_mac_rx_filter_cmd_t)) { + struct wifi_diag_mac_rx_filter_cmd_t *prxfilter = + (struct wifi_diag_mac_rx_filter_cmd_t *) + cmd->cmd_data; + ath6kl_wmi_macfilter_cmd(globalwmi, WMI_PKTLOG_EVENT_RX, + prxfilter->filter_mask_low & + WIFI_DIAG_MACFILTER_LOW_MASK, + prxfilter->filter_mask_high & + WIFI_DIAG_MACFILTER_HIGH_MASK); + } + break; + } + case WIFI_DIAG_CFG_CMDID: + { + if (cmd->len == sizeof(struct wifi_diag_cfg_cmd_t)) { + struct wifi_diag_cfg_cmd_t *pcfg = + (struct wifi_diag_cfg_cmd_t *)cmd->cmd_data; + + if (vif == NULL) + return WIFI_DIAG_ENXIO; + + if (pcfg->cfg != vif->diag.cfg_mask) { + if (pcfg->cfg & + (WIFI_DIAG_MAC_TX_FRAME_EVENTENABLE | + WIFI_DIAG_MAC_RX_FRAME_EVENTENABLE)) { + ath6kl_wmi_pktlog_disable_cmd(globalwmi); + { + struct wmi_enable_pktlog_cmd cmd; + cmd.option = WMI_PKTLOG_OPTION_LOG_DIAGNOSTIC; + cmd.evlist = 0; + cmd.trigger_interval = 0; + cmd.trigger_tail_count = 0; + cmd.trigger_thresh = 0; + cmd.buffer_size = 1500; + + if (pcfg->cfg & + WIFI_DIAG_MAC_TX_FRAME_EVENTENABLE) + cmd.evlist |= WMI_PKTLOG_EVENT_TX; + + if (pcfg->cfg & + WIFI_DIAG_MAC_RX_FRAME_EVENTENABLE) + cmd.evlist |= + WMI_PKTLOG_EVENT_RX; + + ath6kl_wmi_pktlog_enable_cmd( + globalwmi, &cmd); + } + } else { + ath6kl_wmi_pktlog_disable_cmd( + globalwmi); + } + + if (pcfg->cfg & WIFI_DIAG_MAC_FSM_EVENTENABLE) + ath6kl_wmi_fsm_cmd(globalwmi, true); + else + ath6kl_wmi_fsm_cmd(globalwmi, false); + + if (pcfg->cfg & + WIFI_DIAG_INTERFERENCE_EVENTENABLE) { + del_timer(&vif->diag.interference_timer); + init_timer(&vif->diag.interference_timer); + setup_timer(&vif->diag.interference_timer, + wifi_diag_interference_timer_handler, + (unsigned long) vif); + mod_timer( + &vif->diag.interference_timer, + jiffies + + msecs_to_jiffies(1000)); + } else { + del_timer(&vif->diag. + interference_timer); + } + + if (pcfg->cfg & WIFI_DIAG_RX_TIME_EVENTENABLE) { + del_timer(&vif->diag.rxtime_timer); + init_timer(&vif->diag.rxtime_timer); + setup_timer(&vif->diag.rxtime_timer, + wifi_diag_rxtime_timer_handler, + (unsigned long) vif); + mod_timer(&vif->diag.rxtime_timer, + jiffies + + msecs_to_jiffies(1000)); + } else { + del_timer(&vif->diag.rxtime_timer); + } + + if (pcfg->cfg & WIFI_DIAG_PWR_SAVE_EVENTENABLE) + ath6kl_wmi_pwrsave_cmd(globalwmi, true); + else + ath6kl_wmi_pwrsave_cmd(globalwmi, + false); + + if (pcfg->cfg & WIFI_DIAG_TX_STAT_EVENTENABLE) { + vif->diag.tx_timer_val = pcfg->value; + + del_timer(&vif->diag.tx_stat_timer); + init_timer(&vif->diag.tx_stat_timer); + setup_timer(&vif->diag.tx_stat_timer, + wifi_diag_tx_stat_timer_handler, + (unsigned long) vif); + mod_timer(&vif->diag.tx_stat_timer, + jiffies + + msecs_to_jiffies( + vif->diag.tx_timer_val)); + } else { + del_timer(&vif->diag.tx_stat_timer); + } + + if (pcfg->cfg & WIFI_DIAG_RX_STAT_EVENTENABLE) { + vif->diag.rx_timer_val = pcfg->value; + + del_timer(&vif->diag.rx_stat_timer); + init_timer(&vif->diag.rx_stat_timer); + setup_timer(&vif->diag.rx_stat_timer, + wifi_diag_rx_stat_timer_handler, + (unsigned long) vif); + mod_timer(&vif->diag.rx_stat_timer, + jiffies + + msecs_to_jiffies( + vif->diag.rx_timer_val)); + } else { + del_timer(&vif->diag.rx_stat_timer); + } + + vif->diag.cfg_mask = pcfg->cfg; + } + } + + break; + } + default: + break; + } + + return diag_status; +} + + +/* wifi diag event */ +static const s32 _rate_tbl_11[][2] = { + /* {W/O SGI, with SGI} */ + {1000, 1000}, + {2000, 2000}, + {5500, 5500}, + {11000, 11000}, + {6000, 6000}, + {9000, 9000}, + {12000, 12000}, + {18000, 18000}, + {24000, 24000}, + {36000, 36000}, + {48000, 48000}, + {54000, 54000}, + {6500, 7200}, /* HT 20, MCS 0 */ + {13000, 14400}, + {19500, 21700}, + {26000, 28900}, + {39000, 43300}, + {52000, 57800}, + {58500, 65000}, + {65000, 72200}, + {13000, 14400}, /* HT 20, MCS 8 */ + {26000, 28900}, + {39000, 43300}, + {52000, 57800}, + {78000, 86700}, + {104000, 115600}, + {117000, 130000}, + {130000, 144400}, /* HT 20, MCS 15 */ + {13500, 15000}, /* HT 40, MCS 0 */ + {27000, 30000}, + {40500, 45000}, + {54000, 60000}, + {81000, 90000}, + {108000, 120000}, + {121500, 135000}, + {135000, 150000}, + {27000, 30000}, /*HT 40, MCS 8 */ + {54000, 60000}, + {81000, 90000}, + {108000, 120000}, + {162000, 180000}, + {216000, 240000}, + {243000, 270000}, + {270000, 300000}, /*HT 40, MCS 15 */ + {0, 0} +}; + + +static void wifi_diag_event_process(struct work_struct *work) +{ + struct sk_buff *skb; + struct wifi_diag_event_t *pwifi_diag_event; + + mutex_lock(&diag_event_mutex); + + while ((skb = skb_dequeue(&diag_events))) { + pwifi_diag_event = (struct wifi_diag_event_t *) skb->data; + + if (!diag_local_test) { + diag_evt_callback.diag_event_callback( + (void *)&wifi_drv_hdl_table, skb); + } else { + printk(KERN_INFO "eventid=%d seq_num=%d\n", + pwifi_diag_event->event_id, + pwifi_diag_event->seq_num); + switch (pwifi_diag_event->event_id) { + case WIFI_DIAG_MAC_TX_FRAME_EVENTID: + { + struct wifi_diag_mac_tx_frame_event_t + *ptx_frame_event_data; + ptx_frame_event_data = + (struct wifi_diag_mac_tx_frame_event_t *) + pwifi_diag_event->event_data; + + if (ptx_frame_event_data->frame_data != 0) { + printk(KERN_INFO "tx wh[0]=%x wh[%d]=%x\n", + ptx_frame_event_data->frame_data[0], + ptx_frame_event_data->frame_length-1, + ptx_frame_event_data-> + frame_data[ptx_frame_event_data-> + frame_length-1]); + } + + printk(KERN_INFO + "frame_type 0x%x frame_subtype 0x%x flen 0x%x pwr %d mcs %d bitrate %d\n", + ptx_frame_event_data->frame_type, + ptx_frame_event_data->frame_sub_type, + ptx_frame_event_data->frame_length, + ptx_frame_event_data->tx_pwr, + ptx_frame_event_data->tx_mcs, + ptx_frame_event_data->tx_bitrate); + } + break; + case WIFI_DIAG_MAC_RX_FRAME_EVENTID: + { + struct wifi_diag_mac_rx_frame_event_t + *prx_frame_event_data; + prx_frame_event_data = + (struct wifi_diag_mac_rx_frame_event_t *) + pwifi_diag_event->event_data; + + if (prx_frame_event_data->frame_data != 0) { + printk(KERN_INFO "rx wh[0]=%x wh[%d]=%x\n", + prx_frame_event_data->frame_data[0], + prx_frame_event_data->frame_length-1, + prx_frame_event_data-> + frame_data[prx_frame_event_data-> + frame_length-1]); + } + + printk(KERN_INFO + "rssi %d, frame_type 0x%x frame_subtype 0x%x flen 0x%x rateindex %d bitrate %d\n", + prx_frame_event_data->rssi, + prx_frame_event_data->frame_type, + prx_frame_event_data->frame_sub_type, + prx_frame_event_data->frame_length, + prx_frame_event_data->rx_mcs, + prx_frame_event_data->rx_bitrate); + } + break; + case WIFI_DIAG_MAC_FSM_EVENTID: + { + struct wifi_diag_mac_fsm_event_t + *pfsm_event_data; + pfsm_event_data = + (struct wifi_diag_mac_fsm_event_t *) + pwifi_diag_event->event_data; + printk(KERN_INFO + "fsm event %d\n", pfsm_event_data->fsm); + } + break; + case WIFI_DIAG_INTERFERENCE_EVENTID: + { + struct wifi_diag_interference_event_t + *pinterference_event_data; + pinterference_event_data = + (struct wifi_diag_interference_event_t *) + pwifi_diag_event->event_data; + printk(KERN_INFO + "MAC_PCU_RX_CLEAR_CNT = 0x%x\n", + pinterference_event_data->rx_clear_cnt); + } + break; + case WIFI_DIAG_RX_TIME_EVENTID: + { + struct wifi_diag_rxtime_event_t + *prxtime_event_data; + prxtime_event_data = + (struct wifi_diag_rxtime_event_t *) + pwifi_diag_event->event_data; + printk(KERN_INFO + "MAC_PCU_RX_FRAME_CNT = 0x%x\n", + prxtime_event_data->rx_frame_cnt); + } + break; + case WIFI_DIAG_PWR_SAVE_EVENTID: + { + struct wifi_diag_pwrsave_event_t + *ppwrsave_event_data; + ppwrsave_event_data = + (struct wifi_diag_pwrsave_event_t *) + pwifi_diag_event->event_data; + printk(KERN_INFO + "enum wifi_diag_pwrsave_t = %d\n", + ppwrsave_event_data->pwrsave); + } + break; + case WIFI_DIAG_TX_STAT_EVENTID: + { + u32 i; + struct wifi_diag_tx_stat_event_t + *ptx_stat_event_data; + ptx_stat_event_data = + (struct wifi_diag_tx_stat_event_t *) + pwifi_diag_event->event_data; + + printk(KERN_INFO "tx_pkt=%lld, tx_ucast_pkt=%lld, tx_retry_cnt=%lld, tx_fail_cnt=%lld\n", + ptx_stat_event_data->tx_pkt, + ptx_stat_event_data->tx_ucast_pkt, + ptx_stat_event_data->tx_retry_cnt, + ptx_stat_event_data->tx_fail_cnt); + for (i = 0; i < 44; i++) + printk(KERN_INFO "tx_rate_pkt[%d]=%d\n", + i, + ptx_stat_event_data-> + tx_rate_pkt[i] + ); + } + break; + case WIFI_DIAG_RX_STAT_EVENTID: + { + u32 i; + struct wifi_diag_rx_stat_event_t + *prx_stat_event_data; + prx_stat_event_data = + (struct wifi_diag_rx_stat_event_t *) + pwifi_diag_event->event_data; + + printk(KERN_INFO "rx_pkt=%lld, rx_ucast_pkt=%lld, rx_dupl_frame=%lld\n", + prx_stat_event_data->rx_pkt, + prx_stat_event_data->rx_ucast_pkt, + prx_stat_event_data->rx_dupl_frame); + for (i = 0; i < 44; i++) + printk(KERN_INFO + "rx_rate_pkt[%d]=%d\n", + i, + prx_stat_event_data-> + rx_rate_pkt[i] + ); + } + break; + default: + break; + } + dev_kfree_skb(skb); + } + } + mutex_unlock(&diag_event_mutex); +} + +static DECLARE_WORK(wifi_diag_event_work, wifi_diag_event_process); + + + +void +wifi_diag_mac_tx_frame_event(struct ath6kl_vif *vif, + struct ath_pktlog_txstatus *txstatus_log) +{ + struct wifi_diag_event_t *pwifi_diag_txframe_event; + struct wifi_diag_mac_tx_frame_event_t *ptx_frame_event_data; + struct sk_buff *skb; + u32 size; + u32 tx_mcs; + u8 sgi = 0; + u32 tx_buf_len = 0; + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_MAC_TX_FRAME_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return; + } + + size = sizeof(*pwifi_diag_txframe_event) + + sizeof(*ptx_frame_event_data) + 512; + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return ; + + pwifi_diag_txframe_event = (struct wifi_diag_event_t *) skb->data; + pwifi_diag_txframe_event->event_id = WIFI_DIAG_MAC_TX_FRAME_EVENTID; + pwifi_diag_txframe_event->seq_num = le32_to_cpu(txstatus_log->misc[2]); + ptx_frame_event_data = (struct wifi_diag_mac_tx_frame_event_t *) + pwifi_diag_txframe_event->event_data; + + ptx_frame_event_data->tx_pwr = txstatus_log->misc[1]; + tx_mcs = le16_to_cpu(txstatus_log->misc[0]); + if (tx_mcs > MAX11_RATE_INDEX) + tx_mcs = MAX11_RATE_INDEX; + ptx_frame_event_data->tx_mcs = tx_mcs; + sgi = (txstatus_log->misc[0]>>8) & WHAL_RC_FLAG_SGI; + ptx_frame_event_data->tx_bitrate = _rate_tbl_11[tx_mcs][sgi]; + ptx_frame_event_data->frame_type = vif->diag.tx_frame_type; + ptx_frame_event_data->frame_sub_type = vif->diag.tx_frame_subtype; + ptx_frame_event_data->frame_length = vif->diag.tx_frame_len; + + tx_buf_len = le32_to_cpu(txstatus_log->buf_len); + pwifi_diag_txframe_event->len = tx_buf_len + + sizeof(*ptx_frame_event_data) - 1; + + if (ptx_frame_event_data->frame_data != 0) { + u16 framectrl = vif->diag.tx_frame_subtype << 4 | + vif->diag.tx_frame_type; + + memset(ptx_frame_event_data->frame_data, 0, 512); + /* skip 2 pad bytes after qos header, assume there are no addr4 */ + if (IEEE80211_QOS_HAS_SEQ(framectrl)) { + tx_buf_len -= IEEE80211_QOS_PADLEN; + pwifi_diag_txframe_event->len -= IEEE80211_QOS_PADLEN; + ptx_frame_event_data->frame_length -= + IEEE80211_QOS_PADLEN; + memcpy(ptx_frame_event_data->frame_data, + txstatus_log->buf, IEEE80211_QOS_HEADERLEN); + memcpy(ptx_frame_event_data->frame_data+ + IEEE80211_QOS_HEADERLEN, + txstatus_log->buf+ + IEEE80211_QOS_HEADERLEN+ + IEEE80211_QOS_PADLEN, + tx_buf_len - IEEE80211_QOS_HEADERLEN); + } else { + memcpy(ptx_frame_event_data->frame_data, + txstatus_log->buf, tx_buf_len); + } + } + + if (vif->diag.diag_event_init) { + skb_queue_tail(&diag_events, skb); + schedule_work(&wifi_diag_event_work); + } +} + +/* record last descriptor's type, subtype and len for tx log*/ +void +wifi_diag_mac_txctrl_event(struct ath6kl_vif *vif, + struct ath_pktlog_txctl *txctrl_log) +{ + struct tx_ctrl_desc *txctrl; + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_MAC_TX_FRAME_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return; + } + + vif->diag.tx_frame_type = le16_to_cpu(txctrl_log->framectrl) & + IEEE80211_FC0_TYPE_MASK; + vif->diag.tx_frame_subtype = (le16_to_cpu(txctrl_log->framectrl) & + IEEE80211_FC0_SUBTYPE_MASK) >> 4; + txctrl = (struct tx_ctrl_desc *)txctrl_log->txdesc_ctl; + vif->diag.tx_frame_len = le16_to_cpu(WHAL_TXDESC_GET_FRAME_LEN(txctrl)); +} + +void +wifi_diag_mac_rx_frame_event(struct ath6kl_vif *vif, + struct ath_pktlog_rx *rx_log) +{ + struct wifi_diag_event_t *pwifi_diag_rxframe_event; + struct wifi_diag_mac_rx_frame_event_t *prx_frame_event_data; + struct sk_buff *skb; + u32 size; + struct rx_desc_status *rxstatus; + u8 sgi = 0; + u32 rx_mcs = 0, rx_buf_len = 0; + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_MAC_RX_FRAME_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return; + } + + size = sizeof(*pwifi_diag_rxframe_event) + + sizeof(*prx_frame_event_data) + 512; + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return ; + + pwifi_diag_rxframe_event = (struct wifi_diag_event_t *) skb->data; + pwifi_diag_rxframe_event->event_id = WIFI_DIAG_MAC_RX_FRAME_EVENTID; + pwifi_diag_rxframe_event->seq_num = rx_log->seq_num; + prx_frame_event_data = (struct wifi_diag_mac_rx_frame_event_t *) + pwifi_diag_rxframe_event->event_data; + rxstatus = (struct rx_desc_status *)&rx_log->rxstatus[0]; + prx_frame_event_data->rssi = le16_to_cpu(rxstatus->rsRssi) + + rx_log->calibratednf; + /* this needs to be compensated withnosie floor later */ + prx_frame_event_data->snr = le16_to_cpu(rxstatus->rsRssi); + rx_mcs = le32_to_cpu(rx_log->rxmcs); + if (rx_mcs > MAX11_RATE_INDEX) + rx_mcs = MAX11_RATE_INDEX; + prx_frame_event_data->rx_mcs = rx_mcs; + sgi = rxstatus->rsRate.flags & WHAL_RC_FLAG_SGI; + prx_frame_event_data->rx_bitrate = _rate_tbl_11[rx_mcs][sgi]; + prx_frame_event_data->fcs = (rxstatus->rsStatus & WHAL_RXERR_CRC) ? + 1 : 0; + prx_frame_event_data->frame_type = + le16_to_cpu(rx_log->framectrl) & IEEE80211_FC0_TYPE_MASK; + prx_frame_event_data->frame_sub_type = + (le16_to_cpu(rx_log->framectrl) & + IEEE80211_FC0_SUBTYPE_MASK) >> 4; + prx_frame_event_data->frame_length = le16_to_cpu(rxstatus->rsDataLen); + + rx_buf_len = le32_to_cpu(rx_log->buf_len); + pwifi_diag_rxframe_event->len = rx_buf_len + + sizeof(*prx_frame_event_data) - 1; + + if (prx_frame_event_data->frame_data != 0) { + memset(prx_frame_event_data->frame_data, 0, 512); + /* skip 2 pad bytes after qos header, + assume there are no addr4 */ + if (IEEE80211_QOS_HAS_SEQ(le16_to_cpu(rx_log->framectrl))) { + rx_buf_len -= IEEE80211_QOS_PADLEN; + pwifi_diag_rxframe_event->len -= IEEE80211_QOS_PADLEN; + prx_frame_event_data->frame_length -= + IEEE80211_QOS_PADLEN; + memcpy(prx_frame_event_data->frame_data, + rx_log->buf, IEEE80211_QOS_HEADERLEN); + memcpy(prx_frame_event_data->frame_data+ + IEEE80211_QOS_HEADERLEN, + rx_log->buf+IEEE80211_QOS_HEADERLEN+ + IEEE80211_QOS_PADLEN, + rx_buf_len - IEEE80211_QOS_HEADERLEN); + } else { + memcpy(prx_frame_event_data->frame_data, + rx_log->buf, rx_buf_len); + } + } + + if (vif->diag.diag_event_init) { + skb_queue_tail(&diag_events, skb); + schedule_work(&wifi_diag_event_work); + } +} + +void +wifi_diag_mac_fsm_event(struct ath6kl_vif *vif, + enum wifi_diag_mac_fsm_t eventtype, u32 seq_num) +{ + struct wifi_diag_event_t *pwifi_diag_event; + struct wifi_diag_mac_fsm_event_t *pfsm_event_data; + struct sk_buff *skb; + u32 size; + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_MAC_FSM_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return; + } + + size = sizeof(*pwifi_diag_event) + sizeof(*pfsm_event_data) ; + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return; + + pwifi_diag_event = (struct wifi_diag_event_t *) skb->data; + pwifi_diag_event->event_id = WIFI_DIAG_MAC_FSM_EVENTID; + pwifi_diag_event->seq_num = seq_num; + pwifi_diag_event->len = sizeof(*pfsm_event_data); + pfsm_event_data = (struct wifi_diag_mac_fsm_event_t *) + pwifi_diag_event->event_data; + pfsm_event_data->fsm = eventtype; + + if (vif->diag.diag_event_init) { + skb_queue_tail(&diag_events, skb); + schedule_work(&wifi_diag_event_work); + } +} + +void +wifi_diag_send_pwrsave_event(struct ath6kl_vif *vif, + enum wifi_diag_pwrsave_t pwrsave, u32 seq_num) +{ + struct wifi_diag_event_t *pwifi_diag_pwrsave_event; + struct wifi_diag_pwrsave_event_t *ppwrsave_event_data; + struct sk_buff *skb; + u32 size; + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_PWR_SAVE_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return; + } + + size = sizeof(*pwifi_diag_pwrsave_event) + sizeof(*ppwrsave_event_data); + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return; + + pwifi_diag_pwrsave_event = (struct wifi_diag_event_t *) skb->data; + pwifi_diag_pwrsave_event->event_id = WIFI_DIAG_PWR_SAVE_EVENTID; + pwifi_diag_pwrsave_event->seq_num = seq_num; + pwifi_diag_pwrsave_event->len = sizeof(*ppwrsave_event_data); + ppwrsave_event_data = (struct wifi_diag_pwrsave_event_t *) + pwifi_diag_pwrsave_event->event_data; + ppwrsave_event_data->pwrsave = pwrsave; + + if (vif->diag.diag_event_init) { + skb_queue_tail(&diag_events, skb); + schedule_work(&wifi_diag_event_work); + } +} + +int +ath6kl_wmi_stat_rx_rate_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num) +{ + struct wifi_diag_event_t *pwifi_diag_rxstat_event; + struct wifi_diag_rx_stat_event_t *prx_stat_event_data; + struct sk_buff *skb; + u32 size, i; + u32 rx_rate_pkt[44]; + + memcpy(&rx_rate_pkt[0], datap, sizeof(u32)*44); + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_RX_STAT_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return -EINVAL; + } + + printk(KERN_INFO "ath6kl_wmi_stat_rx_rate_event\n"); + + size = sizeof(*pwifi_diag_rxstat_event) + sizeof(*prx_stat_event_data); + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return -ENOMEM; + + pwifi_diag_rxstat_event = (struct wifi_diag_event_t *) skb->data; + pwifi_diag_rxstat_event->event_id = WIFI_DIAG_RX_STAT_EVENTID; + pwifi_diag_rxstat_event->seq_num = seq_num; + pwifi_diag_rxstat_event->len = sizeof(*prx_stat_event_data); + prx_stat_event_data = (struct wifi_diag_rx_stat_event_t *) + pwifi_diag_rxstat_event->event_data; + prx_stat_event_data->rx_pkt = vif->target_stats.rx_pkt; + prx_stat_event_data->rx_ucast_pkt = vif->target_stats.rx_ucast_pkt; + prx_stat_event_data->rx_dupl_frame = vif->target_stats.rx_dupl_frame; + for (i = 0; i < 44; i++) + prx_stat_event_data->rx_rate_pkt[i] = rx_rate_pkt[i]; + + if (vif->diag.diag_event_init) { + skb_queue_tail(&diag_events, skb); + schedule_work(&wifi_diag_event_work); + } + + return 0; +} + +int +ath6kl_wmi_stat_tx_rate_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num) +{ + struct wifi_diag_event_t *pwifi_diag_txstat_event; + struct wifi_diag_tx_stat_event_t *ptx_stat_event_data; + struct sk_buff *skb; + u32 size, i; + u32 tx_rate_pkt[44]; + + memcpy(&tx_rate_pkt[0], datap, sizeof(u32)*44); + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_TX_STAT_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return -EINVAL; + } + + printk(KERN_INFO "ath6kl_wmi_stat_tx_rate_event\n"); + + size = sizeof(*pwifi_diag_txstat_event) + sizeof(*ptx_stat_event_data); + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return -ENOMEM; + + pwifi_diag_txstat_event = (struct wifi_diag_event_t *) skb->data; + pwifi_diag_txstat_event->event_id = WIFI_DIAG_TX_STAT_EVENTID; + pwifi_diag_txstat_event->seq_num = seq_num; + pwifi_diag_txstat_event->len = sizeof(*ptx_stat_event_data); + ptx_stat_event_data = (struct wifi_diag_tx_stat_event_t *) + pwifi_diag_txstat_event->event_data; + ptx_stat_event_data->tx_pkt = vif->target_stats.tx_pkt; + ptx_stat_event_data->tx_ucast_pkt = vif->target_stats.tx_ucast_pkt; + ptx_stat_event_data->tx_retry_cnt = vif->target_stats.tx_retry_cnt; + ptx_stat_event_data->tx_fail_cnt = vif->target_stats.tx_fail_cnt; + for (i = 0; i < 44; i++) + ptx_stat_event_data->tx_rate_pkt[i] = tx_rate_pkt[i]; + + if (vif->diag.diag_event_init) { + skb_queue_tail(&diag_events, skb); + schedule_work(&wifi_diag_event_work); + } + + return 0; +} + +int +ath6kl_wmi_interference_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num) +{ + struct wifi_diag_event_t *pwifi_diag_interference_event; + struct wifi_diag_interference_event_t *pinterference_event_data; + struct sk_buff *skb; + u32 size, rx_clear_cnt; + + printk(KERN_INFO "ath6kl_wmi_interference_event\n"); + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_INTERFERENCE_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return -EINVAL; + } + + size = sizeof(*pwifi_diag_interference_event) + + sizeof(*pinterference_event_data); + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return -ENOMEM; + + pwifi_diag_interference_event = (struct wifi_diag_event_t *) skb->data; + pwifi_diag_interference_event->event_id = + WIFI_DIAG_INTERFERENCE_EVENTID; + pwifi_diag_interference_event->seq_num = seq_num; + pwifi_diag_interference_event->len = sizeof(*pinterference_event_data); + pinterference_event_data = (struct wifi_diag_interference_event_t *) + pwifi_diag_interference_event->event_data; + + rx_clear_cnt = *((u32 *)datap); + if (rx_clear_cnt >= vif->diag.pre_rx_clear_cnt) + pinterference_event_data->rx_clear_cnt = + rx_clear_cnt - vif->diag.pre_rx_clear_cnt; + else + pinterference_event_data->rx_clear_cnt = + 0xFFFFFFFF - (vif->diag.pre_rx_clear_cnt - rx_clear_cnt); + vif->diag.pre_rx_clear_cnt = rx_clear_cnt; + + if (vif->diag.diag_event_init) { + skb_queue_tail(&diag_events, skb); + schedule_work(&wifi_diag_event_work); + } + return 0; +} + +int +ath6kl_wmi_rxtime_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num) +{ + struct wifi_diag_event_t *pwifi_diag_rxtime_event; + struct wifi_diag_rxtime_event_t *prxtime_event_data; + struct sk_buff *skb; + u32 size, rx_frame_cnt; + + printk(KERN_INFO KERN_INFO KERN_INFO "ath6kl_wmi_rxtime_event\n"); + + if (!diag_local_test) { + if (!(vif->diag.cfg_mask & WIFI_DIAG_RX_TIME_EVENTENABLE) + || diag_evt_callback.diag_event_callback == NULL) + return -EINVAL; + } + + size = sizeof(*pwifi_diag_rxtime_event) + sizeof(*prxtime_event_data); + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return -ENOMEM; + + pwifi_diag_rxtime_event = (struct wifi_diag_event_t *) skb->data; + pwifi_diag_rxtime_event->event_id = WIFI_DIAG_RX_TIME_EVENTID; + pwifi_diag_rxtime_event->seq_num = seq_num; + pwifi_diag_rxtime_event->len = sizeof(*prxtime_event_data); + prxtime_event_data = (struct wifi_diag_rxtime_event_t *) + pwifi_diag_rxtime_event->event_data; + + rx_frame_cnt = *((u32 *)datap); + if (rx_frame_cnt >= vif->diag.pre_rx_frame_cnt) + prxtime_event_data->rx_frame_cnt = + rx_frame_cnt - vif->diag.pre_rx_frame_cnt; + else + prxtime_event_data->rx_frame_cnt = + 0xFFFFFFFF - (vif->diag.pre_rx_frame_cnt - rx_frame_cnt); + vif->diag.pre_rx_frame_cnt = rx_frame_cnt; + + if (vif->diag.diag_event_init) { + skb_queue_tail(&diag_events, skb); + schedule_work(&wifi_diag_event_work); + } + return 0; +} + +void +wifi_diag_tx_stat_timer_handler(unsigned long ptr) +{ + struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr; + struct sk_buff *skb; + + ath6kl_wmi_get_stats_cmd(globalwmi, 0); + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmid_cmd_hdr)); + if (skb) + ath6kl_wmi_cmd_send_diag(globalwmi, skb, + WMID_STAT_TX_RATE_CMDID, + NO_SYNC_WMIFLAG); + + mod_timer(&vif->diag.tx_stat_timer, + jiffies + msecs_to_jiffies(vif->diag.tx_timer_val)); +} + +void +wifi_diag_rx_stat_timer_handler(unsigned long ptr) +{ + struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr; + struct sk_buff *skb; + + ath6kl_wmi_get_stats_cmd(globalwmi, 0); + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmid_cmd_hdr)); + if (skb) + ath6kl_wmi_cmd_send_diag(globalwmi, skb, + WMID_STAT_RX_RATE_CMDID, + NO_SYNC_WMIFLAG); + + mod_timer(&vif->diag.rx_stat_timer, + jiffies + msecs_to_jiffies(vif->diag.rx_timer_val)); +} + +void +wifi_diag_interference_timer_handler(unsigned long ptr) +{ + struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr; + + ath6kl_wmi_interference_cmd(globalwmi); + mod_timer(&vif->diag.interference_timer, + jiffies + msecs_to_jiffies(1000)); + + return; +} + +void +wifi_diag_rxtime_timer_handler(unsigned long ptr) +{ + struct ath6kl_vif *vif = (struct ath6kl_vif *)ptr; + + ath6kl_wmi_rxtime_cmd(globalwmi); + mod_timer(&vif->diag.rxtime_timer, jiffies + msecs_to_jiffies(1000)); + + return; +} + +void +wifi_diag_timer_destroy(struct ath6kl_vif *vif) +{ + del_timer(&vif->diag.tx_stat_timer); + del_timer(&vif->diag.rx_stat_timer); + del_timer(&vif->diag.interference_timer); + del_timer(&vif->diag.rxtime_timer); + + if (vif->diag.diag_event_init) { + skb_queue_purge(&diag_events); + vif->diag.diag_event_init = false; + } +} + +void +wifi_diag_init(void) +{ + struct ath6kl_vif *vif = ath6kl_get_vif_by_index( + globalwmi->parent_dev, 0); + + if (vif == NULL) + return; + + skb_queue_head_init(&diag_events); + vif->diag.diag_event_init = true; + + if (diag_local_test & WIFI_DIAG_MAC_FSM_EVENTENABLE) + ath6kl_wmi_fsm_cmd(globalwmi, true); + + if (diag_local_test & WIFI_DIAG_PWR_SAVE_EVENTENABLE) + ath6kl_wmi_pwrsave_cmd(globalwmi, true); + + if (diag_local_test & WIFI_DIAG_INTERFERENCE_EVENTENABLE) { + del_timer(&vif->diag.interference_timer); + init_timer(&vif->diag.interference_timer); + setup_timer(&vif->diag.interference_timer, + wifi_diag_interference_timer_handler, + (unsigned long) vif); + mod_timer(&vif->diag.interference_timer, + jiffies + msecs_to_jiffies(1000)); + } + + if (diag_local_test & WIFI_DIAG_RX_TIME_EVENTENABLE) { + del_timer(&vif->diag.rxtime_timer); + init_timer(&vif->diag.rxtime_timer); + setup_timer(&vif->diag.rxtime_timer, + wifi_diag_rxtime_timer_handler, (unsigned long) vif); + mod_timer(&vif->diag.rxtime_timer, + jiffies + msecs_to_jiffies(1000)); + } + + if (diag_local_test & WIFI_DIAG_TX_STAT_EVENTENABLE) { + del_timer(&vif->diag.tx_stat_timer); + init_timer(&vif->diag.tx_stat_timer); + setup_timer(&vif->diag.tx_stat_timer, + wifi_diag_tx_stat_timer_handler, (unsigned long) vif); + vif->diag.tx_timer_val = 2000; + mod_timer(&vif->diag.tx_stat_timer, + jiffies + msecs_to_jiffies(vif->diag.tx_timer_val)); + } + + if (diag_local_test & WIFI_DIAG_RX_STAT_EVENTENABLE) { + del_timer(&vif->diag.rx_stat_timer); + init_timer(&vif->diag.rx_stat_timer); + setup_timer(&vif->diag.rx_stat_timer, + wifi_diag_rx_stat_timer_handler, (unsigned long) vif); + vif->diag.rx_timer_val = 2000; + mod_timer(&vif->diag.rx_stat_timer, + jiffies + msecs_to_jiffies(vif->diag.rx_timer_val)); + } + + if ((diag_local_test & WIFI_DIAG_MAC_TX_FRAME_EVENTENABLE) || + (diag_local_test & WIFI_DIAG_MAC_RX_FRAME_EVENTENABLE)) { + if ((diag_local_test & 0x00000200) && + (diag_local_test & 0xFFFF0000)) { + ath6kl_wmi_macfilter_cmd(globalwmi, + WMI_PKTLOG_EVENT_TX | WMI_PKTLOG_EVENT_RX, + ((diag_local_test & 0xFFFF0000)>>16) & + WIFI_DIAG_MACFILTER_LOW_MASK, + WIFI_DIAG_MACFILTER_DISABLEALL & + WIFI_DIAG_MACFILTER_HIGH_MASK); + } else if ((diag_local_test & 0x00000800) && + (diag_local_test & 0xFFFF0000)) { + ath6kl_wmi_macfilter_cmd(globalwmi, + WMI_PKTLOG_EVENT_TX | WMI_PKTLOG_EVENT_RX, + WIFI_DIAG_MACFILTER_DISABLEALL & + WIFI_DIAG_MACFILTER_LOW_MASK, + ((diag_local_test & 0xFFFF0000)>>16) & + WIFI_DIAG_MACFILTER_HIGH_MASK); + } else { + ath6kl_wmi_macfilter_cmd(globalwmi, + WMI_PKTLOG_EVENT_TX | WMI_PKTLOG_EVENT_RX, + WIFI_DIAG_MACFILTER_ENABLEALL & + WIFI_DIAG_MACFILTER_LOW_MASK, + WIFI_DIAG_MACFILTER_ENABLEALL & + WIFI_DIAG_MACFILTER_HIGH_MASK); + } + { + struct wmi_enable_pktlog_cmd cmd; + cmd.option = WMI_PKTLOG_OPTION_LOG_DIAGNOSTIC; + cmd.evlist = WMI_PKTLOG_EVENT_TX | WMI_PKTLOG_EVENT_RX; + cmd.trigger_interval = 0; + cmd.trigger_tail_count = 0; + cmd.trigger_thresh = 0; + cmd.buffer_size = 1500; + ath6kl_wmi_pktlog_enable_cmd(globalwmi, &cmd); + } + } +} + +#endif /* ATH6KL_DIAGNOSTIC */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/diagnose.h b/drivers/net/wireless/ath/ath6kl-3.5/diagnose.h new file mode 100644 index 000000000000..fe4dc6fc1aca --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/diagnose.h @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 DIAGNOSE_H +#define DIAGNOSE_H + + +#ifdef ATH6KL_DIAGNOSTIC +#include + + +/* *********************/ +/* Types of packet log events */ +#define PKTLOG_TYPE_TXCTL 0 +#define PKTLOG_TYPE_TXSTATUS 1 +#define PKTLOG_TYPE_RX 2 + +#define PKTLOG_MAX_TXCTL_WORDS 13 +#define PKTLOG_MAX_TXSTATUS_WORDS 10 + +#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_QOS 0x80 +#define IEEE80211_FC0_SUBTYPE_MASK 0xf0 + +/* does frame have QoS sequence control data */ +#define IEEE80211_QOS_HAS_SEQ(framectrl) \ + ((framectrl & \ + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \ + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) + +#define IEEE80211_QOS_HEADERLEN 26 +#define IEEE80211_QOS_PADLEN 2 + +/* Diagnostic command ID */ + +#define WIFI_DIAG_MAC_TX_FILTER_CMDID 0x0001 +#define WIFI_DIAG_MAC_RX_FILTER_CMDID 0x0002 +#define WIFI_DIAG_CFG_CMDID 0x0003 + +/* MAC TX/RX filter type mask */ +#define WIFI_DIAG_MACFILTER_MGT 0x00003F3F +#define WIFI_DIAG_MACFILTER_CTL 0xFF000000 +#define WIFI_DIAG_MACFILTER_DATA 0x0000DFFF +#define WIFI_DIAG_MACFILTER_LOW_MASK 0xFF003F3F +#define WIFI_DIAG_MACFILTER_HIGH_MASK 0x0000DFFF +#define WIFI_DIAG_MACFILTER_ENABLEALL 0xFFFFFFFF +#define WIFI_DIAG_MACFILTER_DISABLEALL 0x00000000 + +/* command enable/disable mask */ +#define WIFI_DIAG_MAC_TX_FRAME_EVENTENABLE 0x00000001 +#define WIFI_DIAG_MAC_RX_FRAME_EVENTENABLE 0x00000002 +#define WIFI_DIAG_MAC_FSM_EVENTENABLE 0x00000004 +#define WIFI_DIAG_INTERFERENCE_EVENTENABLE 0x00000008 +#define WIFI_DIAG_RX_TIME_EVENTENABLE 0x00000010 +#define WIFI_DIAG_PWR_SAVE_EVENTENABLE 0x00000020 +#define WIFI_DIAG_TX_STAT_EVENTENABLE 0x00000040 +#define WIFI_DIAG_RX_STAT_EVENTENABLE 0x00000080 +#define WIFI_DIAG_BT_TIME_EVENTENABLE 0x00000100 + +/* for rx status */ +#define WHAL_RC_FLAG_SGI 0x08 /* use HT SGI if set */ +#define MAX11_RATE_INDEX 43 +#define WHAL_RXERR_CRC 0x01 + +extern unsigned int diag_local_test; +extern struct wmi *globalwmi; + +struct ath_pktlog_txctl { + u16 framectrl; /* frame control field from header */ + u16 seqctrl; /* frame control field from header */ + u16 bssid_tail; /* last two octets of bssid */ + u16 sa_tail; /* last two octets of TA */ + u16 da_tail; /* last two octets of RA */ + u16 resvd; + u32 txdesc_ctl[PKTLOG_MAX_TXCTL_WORDS]; /* Tx descriptor words */ + u32 *proto_hdr; /* Protocol header words (variable length!) */ + u32 *misc; /* Can be used for HT specific or other misc info */ +} __packed; + +struct ath_pktlog_txstatus { + /* Tx descriptor status words */ + u32 txdesc_status[PKTLOG_MAX_TXSTATUS_WORDS]; + + /* Can be used for HT specific or other misc info */ + /* 0 is rsrate, 1 is txpower */ + u32 misc[3]; + + u32 buf_len; + u8 buf[1]; +} __packed; + +#define PKTLOG_MAX_RXSTATUS_WORDS 12 + +struct ath_pktlog_rx { + u16 framectrl; /* frame control field from header */ + u16 seqctrl; /* sequence control field */ + u16 bssid_tail; /* last two octets of bssid */ + u16 sa_tail; /* last two octets of TA */ + u16 da_tail; /* last two octets of RA */ + u16 resvd; + u32 rxdesc_status[PKTLOG_MAX_RXSTATUS_WORDS]; /* Rx descriptor words */ + u32 *proto_hdr; /* Protocol header words (variable length!) */ + u32 *misc; /* Can be used for HT specific or other misc info */ + u32 rxmcs; + u32 calibratednf; + u32 rxstatus[5]; + u32 seq_num; + u32 buf_len; + u8 buf[1]; +} __packed; + +/* 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 { + u32 flags; /* See flags defined below */ + u16 log_type; /* Type of log information foll this header */ + u16 size; /* Size of variable length log information in bytes */ + u32 timestamp; +} __packed; + +struct whal_rate_code { + u8 rateCode; + u8 flags; +} __packed; + + +/* Rx status is in first ds, this shall be only used by AR6004_REV6 for now */ + +struct rx_desc_status { + u16 rsDataLen; /* rx frame length */ + u16 reserved0; /* for alignment */ + u8 rsStatus; /* rx status, 0 => recv ok */ + u8 reserved1; /* for alignment */ + u8 rsRssi; /* rx frame RSSI */ + u8 reserved2; /* for alignment */ + + /* h/w receive rate code + flags WHAL_RATE_CODE */ + struct whal_rate_code rsRate; +} __packed; + +/* tx ctrl descriptor */ +struct tx_ctrl_desc { + u32 dsCtl0; /* opaque DMA control 0 */ +} __packed; + +#define WHAL_TXDESC_GET_FRAME_LEN(x) ((x)->dsCtl0 & 0xFFF) + + + +/* DIANOSTIC API */ + +/* event ID */ +enum wifi_diag_event_id { + WIFI_DIAG_MAC_TX_FRAME_EVENTID = 0x0001, + WIFI_DIAG_MAC_RX_FRAME_EVENTID, + WIFI_DIAG_MAC_FSM_EVENTID, + WIFI_DIAG_INTERFERENCE_EVENTID, + WIFI_DIAG_RX_TIME_EVENTID, + WIFI_DIAG_PWR_SAVE_EVENTID, + WIFI_DIAG_TX_STAT_EVENTID, + WIFI_DIAG_RX_STAT_EVENTID, + WIFI_DIAG_BT_TIME_EVENTID, +}; + + +enum wifi_diag_status_t { + WIFI_DIAG_EOK = 0x0, + WIFI_DIAG_ENXIO, + WIFI_DIAG_ENOMEM, + WIFI_DIAG_EINVAL, + WIFI_DIAG_ENOTSUPP, + WIFI_DIAG_EINPROGRESS, + WIFI_DIAG_EBUSY, + WIFI_DIAG_EEXIST, + WIFI_DIAG_ENOSPC, + WIFI_DIAG_ASYNC, +}; + + +struct wifi_diag_event_t { + u16 event_id; + u16 len; + u32 seq_num; + u8 event_data[1]; +} __packed; + +enum wifi_diag_mac_fsm_t { + WIFI_DIAG_MAC_FSM_SCANNING = 0x0, + WIFI_DIAG_MAC_FSM_AUTH, + WIFI_DIAG_MAC_FSM_ASSOC, + WIFI_DIAG_MAC_FSM_CONNECTED, + WIFI_DIAG_MAC_FSM_DEAUTH, + WIFI_DIAG_MAC_FSM_DISASSOC, + WIFI_DIAG_MAC_FSM_DISCONNECTED, +}; + +struct wifi_diag_mac_fsm_event_t { + enum wifi_diag_mac_fsm_t fsm; +} __packed; + + +/* txrx frame event*/ +struct wifi_diag_mac_rx_frame_event_t { + u32 rssi; + u8 snr; + u32 rx_mcs; + u32 rx_bitrate; + bool fcs; + u8 frame_type; + u8 frame_sub_type; + u16 frame_length; + u8 frame_data[1]; +} __packed; + +struct wifi_diag_mac_tx_frame_event_t { + u32 tx_pwr; + u8 tx_mcs; + u32 tx_bitrate; + u8 frame_type; + u8 frame_sub_type; + u16 frame_length; + u8 frame_data[1]; +} __packed; + +/* txrx statistics event */ +struct wifi_diag_tx_stat_event_t { + u64 tx_pkt; + u64 tx_ucast_pkt; + u64 tx_retry_cnt; + u64 tx_fail_cnt; + u32 tx_rate_pkt[44]; +} __packed; + +struct wifi_diag_rx_stat_event_t { + u64 rx_pkt; + u64 rx_ucast_pkt; + u64 rx_dupl_frame; + u32 rx_rate_pkt[44]; +} __packed; + +/* interference time period event */ +struct wifi_diag_interference_event_t { + u32 rx_clear_cnt; +} __packed; + +/* RX time period event */ +struct wifi_diag_rxtime_event_t { + u32 rx_frame_cnt; +} __packed; + +/* power save event */ +enum wifi_diag_pwrsave_t { + WIFI_DIAG_DEEPSLEEP_START = 0x0, + WIFI_DIAG_DEEPSLEEP_STOP, + WIFI_DIAG_FAKESLEEP_START, + WIFI_DIAG_FAKESLEEP_STOP, + WIFI_DIAG_MAXPERF_START, + WIFI_DIAG_MAXPERF_STOP, +}; + +struct wifi_diag_pwrsave_event_t { + enum wifi_diag_pwrsave_t pwrsave; +} __packed; + + +struct wifi_diag_cmd_t { + u16 cmd_id; + u16 len; + u8 cmd_data[1]; +} __packed; + +struct wifi_diag_mac_tx_filter_cmd_t { + u32 filter_mask_low; + u32 filter_mask_high; +} __packed; + +struct wifi_diag_mac_rx_filter_cmd_t { + u32 filter_mask_low; + u32 filter_mask_high; +} __packed; + +struct wifi_diag_cfg_cmd_t { + u32 cfg; + u16 value; +} __packed; + +struct wifi_diag_callbacks { + enum wifi_diag_status_t (*diag_event_callback) + (void *diag_hdl, struct sk_buff *skb); +}; + +struct wifi_drv_hdl_list { + void * (*wifi_register)(void *diag_hdl); + enum wifi_diag_status_t (*wifi_unregister)(void *drv_hdl); + enum wifi_diag_status_t (*wifi_diag_reg_event_callback) + (void *drv_hdl, struct wifi_diag_callbacks *evt_callback); + enum wifi_diag_status_t (*wifi_diag_cmd) + (void *drv_hdl, struct wifi_diag_cmd_t *cmd); +} __packed; + + +struct wifi_diag { + u32 cfg_mask; + bool diag_event_init; + u8 tx_frame_type; + u8 tx_frame_subtype; + u16 tx_frame_len; + u32 pre_rx_clear_cnt; + u32 pre_rx_frame_cnt; + u32 connect_seq_num; + u32 disconnect_seq_num; + struct timer_list tx_stat_timer; + struct timer_list rx_stat_timer; + struct timer_list interference_timer; + struct timer_list rxtime_timer; + u32 tx_timer_val; + u32 rx_timer_val; +} __packed; + + +void * +wifi_diag_drv_register(void *diag_hdl); +enum wifi_diag_status_t +wifi_diag_drv_unregister(void *drv_hdl); +enum wifi_diag_status_t wifi_diag_register_event_callback(void *drv_hdl, + struct wifi_diag_callbacks *evt_callback); +enum wifi_diag_status_t wifi_diag_cmd_send(void *drv_hdl, + struct wifi_diag_cmd_t *cmd); +void wifi_diag_mac_tx_frame_event(struct ath6kl_vif *vif, + struct ath_pktlog_txstatus *txstatus_log); +void wifi_diag_mac_txctrl_event(struct ath6kl_vif *vif, + struct ath_pktlog_txctl *txctrl_log); +void wifi_diag_mac_rx_frame_event(struct ath6kl_vif *vif, + struct ath_pktlog_rx *rx_log); +void wifi_diag_mac_fsm_event(struct ath6kl_vif *vif, + enum wifi_diag_mac_fsm_t eventtype, u32 seq_num); +void wifi_diag_send_pwrsave_event(struct ath6kl_vif *vif, + enum wifi_diag_pwrsave_t pwrsave, u32 seq_num); +void wifi_diag_tx_stat_timer_handler(unsigned long ptr); +void wifi_diag_rx_stat_timer_handler(unsigned long ptr); +void wifi_diag_interference_timer_handler(unsigned long ptr); +void wifi_diag_rxtime_timer_handler(unsigned long ptr); +void wifi_diag_timer_destroy(struct ath6kl_vif *vif); +void wifi_diag_init(void); + +int ath6kl_wmi_stat_rx_rate_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num); +int ath6kl_wmi_stat_tx_rate_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num); +int ath6kl_wmi_interference_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num); +int ath6kl_wmi_rxtime_event(struct ath6kl_vif *vif, + struct wmi *wmi, u8 *datap, int len, u32 seq_num); +int ath6kl_wmi_diag_event(struct ath6kl_vif *vif, + struct wmi *wmi, struct sk_buff *skb); + +#endif /* ATH6KL_DIAGNOSTIC */ +#endif /* DIAGNOSE_H */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/epping.h b/drivers/net/wireless/ath/ath6kl-3.5/epping.h new file mode 100644 index 000000000000..fa7c4616bdd3 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/epping.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 EPPING_H +#define EPPING_H + +#include "htc.h" + +/* alignment to 4-bytes */ +#define EPPING_ALIGNMENT_PAD (((sizeof(struct htc_frame_hdr) + 3) & (~0x3)) \ + - sizeof(struct htc_frame_hdr)) + +#define HCI_RSVD_EXPECTED_PKT_TYPE_RECV_OFFSET 7 + +struct epping_header { + /* reserved for HCI packet header (GMBOX) testing */ + u8 hcirsvd[8]; + + /* stream no. to echo this packet on (filled by host) */ + u8 stream_echo_h; + + /* stream no. packet was echoed to (filled by target) + When echoed: stream_echo_sent == stream_echo */ + u8 stream_echo_sent_t; + + /* stream no. that target received this packet on (filled by target) */ + u8 stream_recv_t; + + /* stream number to send on (filled by host) */ + u8 stream_no_h; + + /* magic number to filter for this packet on the host*/ + u8 magic_h[4]; + + /* reserved fields that must be set to a "reserved" value + since this packet maps to a 14-byte ethernet frame we want + to make sure ethertype field is set to something unknown */ + u8 srsvd[6]; + + /* padding for alignment */ + u8 pad[2]; + + /* timestamp of packet (host or target) */ + u8 time_stamp[8]; + + /* 4 byte host context, target echos this back */ + u32 host_context; + + /* sequence number (set by host or target) */ + u32 seq_no; + + /* ping command (filled by host) */ + u16 cmd_id_h; + + /* optional flags */ + u16 cmd_flags_h; + + /* buffer for command (host -> target) */ + u8 cmd_buffer_h[8]; + + /* buffer for command (target -> host) */ + u8 cmd_buffer_t[8]; + + /* length of data */ + u16 data_length; + + /* 16 bit CRC of data */ + u16 crc; + + /* header CRC (fields : StreamNo_h to end, minus HeaderCRC) */ + u16 header_crc; +} __packed; + +#define EPPING_PING_MAGIC_0 0xAA +#define EPPING_PING_MAGIC_1 0x55 +#define EPPING_PING_MAGIC_2 0xCE +#define EPPING_PING_MAGIC_3 0xEC + +#define IS_EPPING_PACKET(pPkt) \ + (((pPkt)->magic_h[0] == EPPING_PING_MAGIC_0) && \ + ((pPkt)->magic_h[1] == EPPING_PING_MAGIC_1) && \ + ((pPkt)->magic_h[2] == EPPING_PING_MAGIC_2) && \ + ((pPkt)->magic_h[3] == EPPING_PING_MAGIC_3)) + +/* DataCRC field is valid */ +#define CMD_FLAGS_DATA_CRC (1 << 0) + +/* delay the echo of the packet */ +#define CMD_FLAGS_DELAY_ECHO (1 << 1) + +/* do not drop at HTC layer no matter what the stream is */ +#define CMD_FLAGS_NO_DROP (1 << 2) + +#define IS_EPING_PACKET_NO_DROP(pPkt) ((pPkt)->cmd_flags_h & CMD_FLAGS_NO_DROP) + +/* this number is higher than the define WMM AC classes so we + can use this to distinguish packets */ +#define HCI_TRANSPORT_STREAM_NUM 16 + +#endif /*EPPING_H*/ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/fw.ram.bin.c b/drivers/net/wireless/ath/ath6kl-3.5/fw.ram.bin.c new file mode 100644 index 000000000000..c0bbac384f4e --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/fw.ram.bin.c @@ -0,0 +1,4408 @@ +#define FW_RAM_BIN_LEN 48454 +unsigned char FW_RAM_BIN[FW_RAM_BIN_LEN] = { + 0x53, 0x47, 0x4d, 0x54, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x99, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x10, + 0xad, 0x01, 0x04, 0x04, 0x40, 0x00, 0x00, 0x06, 0x01, 0x07, 0x08, + 0x22, 0x00, 0x00, 0xff, 0x7f, 0x9a, 0x00, 0x38, 0x7d, 0x43, 0x00, + 0x00, 0xad, 0x40, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x10, 0xad, + 0x40, 0x00, 0x01, 0x04, 0x04, 0x01, 0x08, 0x08, 0x00, 0x9a, 0x99, + 0x00, 0x6c, 0x00, 0x00, 0x00, 0x09, 0x36, 0x61, 0x00, 0x21, 0x00, + 0x70, 0xa1, 0x01, 0x70, 0x88, 0x12, 0xb1, 0x02, 0x70, 0xa7, 0x18, + 0x10, 0x9d, 0x0a, 0xa0, 0xbb, 0xc0, 0xa2, 0xa0, 0x00, 0x76, 0xab, + 0x05, 0xa2, 0x49, 0x00, 0x92, 0xc9, 0x01, 0x82, 0x22, 0x00, 0xa1, + 0x03, 0x70, 0x91, 0x04, 0x70, 0xc2, 0xa6, 0x64, 0xb2, 0xa0, 0x04, + 0xb9, 0x01, 0xc9, 0x11, 0xbd, 0x01, 0x92, 0x6a, 0xa6, 0x0c, 0x8a, + 0xe0, 0x08, 0x00, 0x8c, 0x7a, 0x82, 0x22, 0x76, 0xe0, 0x08, 0x00, + 0x65, 0x00, 0x00, 0x1d, 0xf0, 0x36, 0x41, 0x00, 0xa1, 0x06, 0x70, + 0x21, 0x03, 0x70, 0x81, 0x00, 0x70, 0x91, 0x05, 0x70, 0x82, 0x28, + 0x53, 0x92, 0x62, 0x9a, 0xe0, 0x08, 0x00, 0xa1, 0x07, 0x70, 0xa2, + 0x62, 0x86, 0x1d, 0xf0, 0x00, 0x9a, 0x99, 0x00, 0xfc, 0xff, 0xff, + 0xff, 0x00, 0x80, 0x99, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x40, 0x00, 0x20, 0xad, 0x40, 0x00, 0x40, 0xb3, 0x40, 0x00, + 0x00, 0x06, 0x01, 0x06, 0x08, 0x01, 0x04, 0x10, 0x30, 0x27, 0x40, + 0x00, 0x8c, 0x27, 0x40, 0x00, 0x24, 0x72, 0x91, 0x00, 0x38, 0x74, + 0x91, 0x00, 0x00, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xe0, + 0xa2, 0x99, 0x00, 0x00, 0x08, 0x00, 0x00, 0x68, 0x0b, 0x00, 0x00, + 0x58, 0x0d, 0x00, 0x00, 0x8c, 0x08, 0x00, 0x00, 0x12, 0x08, 0x00, + 0x00, 0x20, 0xf7, 0x96, 0x00, 0xd0, 0xf6, 0x96, 0x00, 0x0d, 0x08, + 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x40, 0xad, 0x40, 0x00, 0x22, + 0xa0, 0x99, 0x00, 0xc0, 0xae, 0x40, 0x00, 0x41, 0xaf, 0x40, 0x00, + 0x2c, 0xad, 0x40, 0x00, 0x00, 0xad, 0x40, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x01, 0x10, 0xad, 0x40, 0x00, 0x01, 0x04, 0x04, 0x20, 0xad, + 0x40, 0x00, 0x40, 0xb3, 0x40, 0x00, 0x10, 0xad, 0x40, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x99, + 0x00, 0x01, 0x09, 0x00, 0x00, 0x2e, 0x36, 0x61, 0x00, 0x31, 0x00, + 0x70, 0xa1, 0x01, 0x70, 0x88, 0x13, 0xb1, 0x02, 0x70, 0xa7, 0x18, + 0x10, 0x9d, 0x0a, 0xa0, 0xbb, 0xc0, 0xa2, 0xa0, 0x00, 0x76, 0xab, + 0x05, 0xa2, 0x49, 0x00, 0x92, 0xc9, 0x01, 0x82, 0x23, 0x00, 0xa1, + 0x03, 0x70, 0x91, 0x04, 0x70, 0xc2, 0xa6, 0x64, 0xb2, 0xa0, 0x04, + 0xb9, 0x01, 0xc9, 0x11, 0xbd, 0x01, 0x92, 0x6a, 0xa6, 0x0c, 0x8a, + 0xe0, 0x08, 0x00, 0x8c, 0xda, 0x82, 0x23, 0x75, 0x0c, 0x6a, 0xe0, + 0x08, 0x00, 0xad, 0x02, 0xa5, 0x00, 0x00, 0x2d, 0x0a, 0x1d, 0xf0, + 0x00, 0x00, 0x36, 0x41, 0x00, 0x31, 0x05, 0x70, 0x91, 0x03, 0x70, + 0x29, 0x03, 0x82, 0x29, 0xb3, 0x0c, 0x1a, 0x67, 0x68, 0x04, 0xa0, + 0xa2, 0x20, 0xa9, 0x03, 0xb2, 0x29, 0x96, 0x22, 0xa0, 0x01, 0x16, + 0x9b, 0x03, 0xa1, 0x06, 0x70, 0xa2, 0x2a, 0x00, 0x81, 0x08, 0x70, + 0xe0, 0x08, 0x00, 0xa1, 0x07, 0x70, 0xa8, 0x0a, 0x81, 0x09, 0x2e, + 0x04, 0x0b, 0x81, 0x00, 0x70, 0x88, 0x78, 0xe0, 0x08, 0x00, 0xa9, + 0x13, 0xe5, 0x01, 0x00, 0x66, 0x9a, 0x05, 0x0c, 0xa2, 0x29, 0x23, + 0x1d, 0xf0, 0x25, 0x6d, 0x00, 0xa5, 0x76, 0x00, 0x28, 0x23, 0x2e, + 0x04, 0x5a, 0x00, 0x2e, 0x04, 0x11, 0x2e, 0x04, 0x60, 0x0c, 0x0a, + 0xc1, 0x0b, 0x70, 0x91, 0x05, 0x70, 0x81, 0x0a, 0x70, 0x92, 0x29, + 0x01, 0x76, 0xa8, 0x07, 0xb2, 0x19, 0x00, 0x2b, 0x99, 0xa0, 0xab, + 0x30, 0xc7, 0x1a, 0x0b, 0x81, 0x00, 0x70, 0x82, 0x28, 0x5e, 0xa1, + 0x0c, 0x2e, 0x04, 0x53, 0x0c, 0x02, 0x2e, 0x07, 0x30, 0x03, 0x7c, + 0xf9, 0x51, 0x05, 0x70, 0x21, 0x0d, 0x70, 0x58, 0x15, 0x0c, 0x04, + 0x42, 0x45, 0x04, 0x42, 0x45, 0x05, 0x4d, 0x05, 0x76, 0xa2, 0x07, + 0x82, 0x14, 0x00, 0x2b, 0x44, 0x30, 0x38, 0x30, 0x90, 0x93, 0x30, + 0x92, 0x45, 0x04, 0x90, 0x98, 0x41, 0x92, 0x45, 0x05, 0x2e, 0x07, + 0x81, 0x44, 0x9c, 0x03, 0x0c, 0x05, 0x3d, 0xf0, 0x76, 0x93, 0x07, + 0x42, 0x02, 0x00, 0x1b, 0x22, 0x50, 0x54, 0x30, 0x46, 0x00, 0x00, + 0x0c, 0x05, 0x7c, 0xf2, 0x20, 0x25, 0x30, 0x20, 0x20, 0x74, 0x2e, + 0x06, 0x58, 0x1c, 0x48, 0x37, 0xb8, 0x03, 0x7c, 0xf2, 0x1d, 0xf0, + 0xcd, 0x03, 0xbd, 0x02, 0xa1, 0x05, 0x70, 0x81, 0x00, 0x70, 0xa8, + 0x1a, 0x82, 0x28, 0x3f, 0xa2, 0xca, 0x38, 0x2e, 0x08, 0x7e, 0x2e, + 0x05, 0x4c, 0x2e, 0x08, 0x81, 0x08, 0x3c, 0x08, 0x87, 0x13, 0x2e, + 0x09, 0x30, 0xd2, 0xa3, 0xe8, 0x2e, 0x0b, 0x33, 0xda, 0xaa, 0x2e, + 0x0b, 0x81, 0x30, 0x4c, 0x88, 0x2e, 0x0b, 0x28, 0xd1, 0x0e, 0x70, + 0x2e, 0x18, 0x28, 0x82, 0xa0, 0xc8, 0x2e, 0x0b, 0x81, 0x01, 0xd1, + 0x0f, 0x2e, 0x15, 0x29, 0x36, 0x41, 0x00, 0xf6, 0x63, 0x2e, 0x05, + 0x25, 0xa1, 0x05, 0x70, 0x88, 0x0a, 0x07, 0xe8, 0x10, 0xbd, 0x02, + 0x0c, 0x6c, 0x2e, 0x08, 0x27, 0x8b, 0x2e, 0x0c, 0x50, 0x41, 0x05, + 0x70, 0x82, 0x04, 0x0c, 0x5d, 0x02, 0xb6, 0x88, 0x06, 0x56, 0x53, + 0x0d, 0x2e, 0x04, 0x33, 0xad, 0x03, 0x0c, 0x5b, 0x81, 0x12, 0x70, + 0xe0, 0x08, 0x00, 0x56, 0x0a, 0x0c, 0x2e, 0x05, 0x0d, 0x13, 0x2e, + 0x06, 0x82, 0x59, 0x0c, 0x0c, 0x1c, 0x7f, 0xd2, 0xa6, 0x44, 0x31, + 0x10, 0x70, 0xb2, 0xa6, 0xdc, 0xe1, 0x11, 0x70, 0xa7, 0xbc, 0x66, + 0xc7, 0xbf, 0x02, 0xc6, 0x26, 0x00, 0x82, 0x05, 0x00, 0xd8, 0x14, + 0x62, 0xa6, 0xc4, 0xca, 0x9d, 0x6a, 0xdd, 0x1b, 0xcc, 0x62, 0xa6, + 0x44, 0x6a, 0x99, 0x82, 0x49, 0x80, 0x62, 0x05, 0x02, 0x78, 0x14, + 0x92, 0xa6, 0x62, 0x2a, 0x77, 0xba, 0x87, 0x9a, 0x77, 0x62, 0x47, + 0x80, 0x98, 0x14, 0x62, 0x05, 0x01, 0x2a, 0x99, 0x00, 0x66, 0x23, + 0x22, 0xc2, 0x12, 0x3a, 0x79, 0x60, 0x66, 0xa0, 0x62, 0x48, 0x04, + 0xea, 0x99, 0x60, 0x68, 0x41, 0x62, 0x48, 0x05, 0x82, 0x05, 0x04, + 0x2e, 0x05, 0x34, 0x03, 0x5b, 0x55, 0x00, 0x66, 0x23, 0x2e, 0x04, + 0x1c, 0x47, 0x04, 0x2e, 0x04, 0x1a, 0x47, 0x05, 0x46, 0xe5, 0xff, + 0xc7, 0x3f, 0x37, 0x61, 0x00, 0x70, 0x72, 0xcd, 0x18, 0xca, 0x5d, + 0x0c, 0x0b, 0x1c, 0x2c, 0x7c, 0xfd, 0xd2, 0x45, 0x00, 0x82, 0x26, + 0x3e, 0xa8, 0x14, 0xd2, 0xa6, 0xdc, 0x2a, 0xaa, 0x2e, 0x06, 0x81, + 0x77, 0x0b, 0x1c, 0x2c, 0xa8, 0x14, 0x82, 0x26, 0x3e, 0x2a, 0xaa, + 0x3a, 0x2e, 0x04, 0x10, 0x22, 0xc2, 0x12, 0x1b, 0x55, 0x77, 0x95, + 0xcf, 0x0c, 0x02, 0x1d, 0xf0, 0xb6, 0x63, 0xf8, 0x5b, 0x52, 0x32, + 0xc3, 0xfb, 0xc8, 0x14, 0xb2, 0x02, 0x04, 0xc2, 0xdc, 0x06, 0xb2, + 0x4c, 0x20, 0xc6, 0xc5, 0xff, 0x2e, 0x06, 0x83, 0x28, 0x2e, 0x0c, + 0x82, 0x00, 0x23, 0x2e, 0x10, 0x82, 0x00, 0xda, 0x0b, 0x2e, 0x0e, + 0x82, 0x00, 0xb2, 0xa2, 0x70, 0xd2, 0xa1, 0x0c, 0xf2, 0xa1, 0x8c, + 0x72, 0xa1, 0x98, 0xa7, 0xbc, 0x66, 0xb6, 0xac, 0x02, 0x86, 0x26, + 0x00, 0x62, 0xa1, 0x9e, 0x2e, 0x05, 0x82, 0x01, 0xe2, 0xa1, 0x0c, + 0xca, 0x9d, 0xfa, 0xdd, 0x1b, 0xcc, 0xea, 0x99, 0x82, 0x49, 0x80, + 0x92, 0x05, 0x02, 0x38, 0x14, 0x82, 0xa1, 0x1e, 0x2a, 0x33, 0x7a, + 0xe3, 0x8a, 0x33, 0x92, 0x43, 0x80, 0x88, 0x14, 0x92, 0x05, 0x01, + 0x2a, 0x88, 0x00, 0x99, 0x23, 0x22, 0xc2, 0x12, 0xba, 0x38, 0x90, + 0x99, 0xa0, 0x92, 0x4e, 0x04, 0x6a, 0x88, 0x90, 0x98, 0x41, 0x92, + 0x4e, 0x05, 0x62, 0x05, 0x04, 0x62, 0x48, 0xd8, 0xe2, 0x2e, 0x05, + 0x81, 0x7e, 0xee, 0x23, 0xe0, 0xee, 0xa0, 0xe2, 0x43, 0x04, 0xe0, + 0xe8, 0x41, 0xe2, 0x43, 0x05, 0x46, 0xe5, 0xff, 0xf6, 0xac, 0x36, + 0x51, 0x00, 0x70, 0xcb, 0x6d, 0xca, 0x3d, 0x2e, 0x07, 0x81, 0x7d, + 0x43, 0x00, 0xa8, 0x14, 0x82, 0x25, 0x3e, 0x2a, 0xaa, 0x7a, 0x2e, + 0x08, 0x81, 0x7a, 0x82, 0x25, 0x3e, 0xa8, 0x14, 0xd2, 0xa2, 0x70, + 0x2e, 0x07, 0x82, 0x0d, 0x22, 0xc2, 0x12, 0x1b, 0x33, 0x67, 0x93, + 0x2e, 0x12, 0x81, 0x7d, 0xb2, 0x4c, 0xe8, 0x46, 0xc7, 0xff, 0x00, + 0x36, 0x41, 0x00, 0xad, 0x03, 0x0c, 0x7b, 0x2e, 0x07, 0x81, 0x66, + 0x4a, 0x0b, 0x2e, 0x05, 0x0d, 0x2e, 0x06, 0x81, 0x66, 0x03, 0x0c, + 0x0c, 0x1c, 0x7f, 0x41, 0x10, 0x70, 0x62, 0xa6, 0xc4, 0xb2, 0xa6, + 0xdc, 0x51, 0x05, 0x70, 0xe1, 0x14, 0x70, 0xa7, 0xbc, 0x57, 0xc7, + 0xbf, 0x02, 0x06, 0x23, 0x00, 0x82, 0x02, 0x00, 0xd8, 0x15, 0x72, + 0xa6, 0x44, 0x2e, 0x06, 0x83, 0x69, 0x7a, 0x99, 0x82, 0x49, 0x80, + 0x82, 0x02, 0x01, 0x78, 0x15, 0x92, 0xa6, 0x5d, 0x3a, 0x77, 0x9a, + 0x97, 0xba, 0x77, 0x2e, 0x05, 0x11, 0x02, 0x82, 0x47, 0x02, 0x92, + 0x02, 0x03, 0x92, 0x47, 0x03, 0x88, 0x15, 0x92, 0x02, 0x04, 0x3a, + 0x88, 0xea, 0x78, 0x32, 0xc3, 0x12, 0x4a, 0x88, 0x92, 0x47, 0x80, + 0x92, 0x02, 0x05, 0x92, 0x48, 0x02, 0x72, 0x02, 0x06, 0x72, 0x48, + 0x03, 0x7b, 0x22, 0x06, 0xe9, 0x2e, 0x0b, 0x83, 0x5a, 0x2d, 0x2e, + 0x07, 0x81, 0x5d, 0x42, 0x2e, 0x05, 0x83, 0x5a, 0x15, 0xd2, 0xa6, + 0xdc, 0x3a, 0x2e, 0x0b, 0x83, 0x5a, 0x15, 0x82, 0x26, 0x3e, 0x3a, + 0xaa, 0x4a, 0x2e, 0x04, 0x10, 0x32, 0xc3, 0x12, 0x1b, 0x22, 0x77, + 0x92, 0x2e, 0x05, 0x81, 0x5d, 0x2e, 0x06, 0x83, 0x44, 0x2e, 0x0b, + 0x81, 0x4c, 0x1a, 0x2e, 0x0f, 0x81, 0x4c, 0xb2, 0xa2, 0x70, 0xf2, + 0xa1, 0x19, 0xe2, 0x2e, 0x05, 0x83, 0x32, 0x41, 0x05, 0x70, 0xa7, + 0xbc, 0x57, 0xb6, 0xac, 0x02, 0xc6, 0x22, 0x00, 0x82, 0xa1, 0x99, + 0x62, 0x02, 0x00, 0xd8, 0x14, 0x52, 0xa1, 0x0c, 0xca, 0x9d, 0xea, + 0xdd, 0x1b, 0xcc, 0x5a, 0x99, 0x62, 0x49, 0x80, 0x58, 0x14, 0x62, + 0x02, 0x01, 0x3a, 0x55, 0x7a, 0x95, 0xfa, 0x55, 0x62, 0x45, 0x80, + 0x52, 0x02, 0x02, 0x52, 0x49, 0x02, 0x62, 0x02, 0x03, 0x62, 0x49, + 0x03, 0x58, 0x14, 0x92, 0x02, 0x04, 0x3a, 0x55, 0xba, 0x65, 0x32, + 0xc3, 0x12, 0x8a, 0x55, 0x92, 0x45, 0xd8, 0x82, 0x02, 0x05, 0x82, + 0x46, 0x02, 0x52, 0x02, 0x06, 0x52, 0x46, 0x2e, 0x06, 0x81, 0x4a, + 0x2e, 0x09, 0x83, 0x26, 0x2e, 0x0a, 0x81, 0x49, 0x2e, 0x05, 0x83, + 0x26, 0x3a, 0x2e, 0x12, 0x83, 0x26, 0x2e, 0x07, 0x81, 0x59, 0x2e, + 0x05, 0x81, 0x49, 0x67, 0x2e, 0x08, 0x81, 0x49, 0x2e, 0x09, 0x88, + 0x34, 0x61, 0x00, 0x71, 0x05, 0x70, 0x0c, 0x2a, 0x92, 0x07, 0x0c, + 0x0c, 0x28, 0x26, 0x19, 0x05, 0x26, 0x29, 0x02, 0x66, 0x89, 0x0a, + 0x62, 0x02, 0x01, 0xb2, 0x02, 0x00, 0x89, 0x04, 0xf6, 0x23, 0x07, + 0x7c, 0xf2, 0x1d, 0xf0, 0x28, 0x01, 0x1d, 0xf0, 0x16, 0x86, 0xff, + 0x92, 0xc3, 0xfe, 0x67, 0x39, 0x29, 0xb0, 0x30, 0x54, 0x37, 0x55, + 0x27, 0x2b, 0xc2, 0x67, 0x6b, 0x32, 0xbd, 0x06, 0x0c, 0x0d, 0xe2, + 0xa2, 0x00, 0xad, 0x0c, 0x81, 0x15, 0x70, 0x21, 0x16, 0x70, 0x82, + 0x28, 0xa2, 0xcd, 0x02, 0xe0, 0x08, 0x00, 0xcd, 0x02, 0xa0, 0xb0, + 0x74, 0x46, 0x05, 0x00, 0x2e, 0x04, 0x3b, 0x0c, 0x02, 0xb2, 0x07, + 0x0c, 0xaa, 0x96, 0xf6, 0x8b, 0x01, 0x1b, 0x99, 0x99, 0x04, 0x1d, + 0xf0, 0xbd, 0x06, 0x0c, 0xed, 0xd7, 0xb3, 0x34, 0xf1, 0x17, 0x70, + 0x30, 0xe3, 0x90, 0xfa, 0xee, 0xa0, 0x0e, 0x00, 0x86, 0x09, 0x00, + 0x46, 0x2a, 0x00, 0x06, 0x27, 0x00, 0x46, 0x07, 0x00, 0x06, 0x23, + 0x00, 0xc6, 0x1f, 0x00, 0x06, 0x1b, 0x00, 0x46, 0x16, 0x00, 0x46, + 0x05, 0x00, 0x46, 0x12, 0x00, 0x06, 0x0f, 0x00, 0xc6, 0x0b, 0x00, + 0x86, 0x08, 0x2e, 0x04, 0x0f, 0x0c, 0x02, 0xa8, 0x04, 0x86, 0xeb, + 0xff, 0xc0, 0xac, 0x20, 0x50, 0xc5, 0x20, 0x65, 0x0a, 0x00, 0xa0, + 0x2a, 0x20, 0xc6, 0xfa, 0xff, 0xad, 0x0c, 0xa5, 0xe6, 0xff, 0x2d, + 0x0a, 0x46, 0xf8, 0xff, 0xad, 0x0c, 0x65, 0xd9, 0xff, 0x2d, 0x0a, + 0xc6, 0xf5, 0xff, 0xad, 0x0c, 0x25, 0xb4, 0x2e, 0x04, 0x14, 0xf3, + 0x2e, 0x04, 0x1e, 0xae, 0x2e, 0x04, 0x14, 0xf0, 0x2e, 0x04, 0x1e, + 0xb0, 0x2e, 0x04, 0x14, 0xee, 0xff, 0x82, 0x07, 0x0c, 0xb6, 0x28, + 0x41, 0xad, 0x0c, 0xe5, 0xc6, 0x2e, 0x04, 0x10, 0xea, 0xff, 0x92, + 0x07, 0x0c, 0xb6, 0x29, 0x36, 0xad, 0x0c, 0xe5, 0xb5, 0x2e, 0x04, + 0x10, 0xe6, 0xff, 0xad, 0x0c, 0xe5, 0xaa, 0x2e, 0x04, 0x34, 0xe3, + 0x2e, 0x04, 0x48, 0xb2, 0x2e, 0x04, 0x14, 0xe1, 0x2e, 0x04, 0x0a, + 0xa7, 0x2e, 0x04, 0x14, 0xde, 0x2e, 0x04, 0x0a, 0xec, 0x2e, 0x04, + 0x14, 0xdc, 0xff, 0x0c, 0x02, 0x06, 0xdb, 0xff, 0x0c, 0x02, 0xc6, + 0xd9, 0xff, 0x2e, 0x05, 0x82, 0x50, 0x6d, 0x02, 0x8c, 0x83, 0x0c, + 0x02, 0x52, 0x06, 0x00, 0x52, 0x44, 0x00, 0x1d, 0xf0, 0x7c, 0xf2, + 0x2e, 0x08, 0x84, 0x30, 0xf6, 0xb3, 0x2e, 0x05, 0x8a, 0x1c, 0x07, + 0x64, 0x3a, 0x31, 0x05, 0x70, 0x98, 0x03, 0x41, 0x00, 0x70, 0x17, + 0xe9, 0x10, 0xbd, 0x02, 0x0c, 0x4c, 0xa8, 0x13, 0x82, 0x24, 0x3f, + 0xa2, 0xca, 0x14, 0xe0, 0x08, 0x00, 0x98, 0x03, 0x07, 0xe9, 0x0d, + 0x4b, 0xb2, 0x0c, 0x6c, 0x2e, 0x05, 0x14, 0x2e, 0x05, 0x8a, 0x33, + 0xab, 0x2e, 0x08, 0x0e, 0xeb, 0x2e, 0x09, 0x8a, 0x68, 0x61, 0x00, + 0x9c, 0xc3, 0x0c, 0x05, 0xdd, 0x04, 0x59, 0x01, 0xad, 0x02, 0xbd, + 0x03, 0xcd, 0x01, 0x25, 0xe4, 0xff, 0xcc, 0xba, 0xb8, 0x01, 0xb7, + 0x33, 0x07, 0xba, 0x22, 0xb0, 0x33, 0xc0, 0x56, 0x33, 0xfe, 0x2e, + 0x05, 0x8e, 0x21, 0x36, 0x61, 0x00, 0x21, 0x06, 0x70, 0x88, 0x02, + 0x88, 0x08, 0x2e, 0x04, 0x39, 0x0a, 0x88, 0x02, 0xbd, 0x01, 0x88, + 0x38, 0x0c, 0x6c, 0xe0, 0x08, 0x00, 0x88, 0x02, 0x88, 0x18, 0xe0, + 0x08, 0x00, 0x92, 0x01, 0x02, 0xa2, 0xa0, 0x7f, 0x90, 0xb7, 0x41, + 0xcc, 0x5b, 0x90, 0xc0, 0x64, 0xcc, 0x6c, 0x9c, 0x9b, 0xd2, 0x01, + 0x08, 0xd7, 0x0a, 0x14, 0x92, 0x01, 0x04, 0xf2, 0x01, 0x09, 0x2e, + 0x06, 0x18, 0xe0, 0x64, 0xcc, 0x5e, 0x8c, 0x1b, 0xf7, 0x8a, 0x01, + 0x1d, 0xf0, 0x91, 0x05, 0x70, 0x98, 0x19, 0x88, 0x79, 0xa2, 0xaf, + 0xbf, 0xa0, 0x88, 0x10, 0x82, 0x49, 0x1c, 0x80, 0x88, 0x41, 0x82, + 0x49, 0x1d, 0x2e, 0x05, 0x06, 0x1e, 0x2e, 0x05, 0x06, 0x1f, 0x2e, + 0x05, 0x8c, 0x04, 0x31, 0x07, 0x70, 0x88, 0x03, 0x2e, 0x05, 0x74, + 0x88, 0x03, 0x88, 0x28, 0xad, 0x02, 0xe0, 0x08, 0x00, 0x2e, 0x05, + 0x18, 0x81, 0x07, 0x70, 0x88, 0x08, 0x2e, 0x05, 0x78, 0x2e, 0x06, + 0x81, 0x1c, 0x0c, 0x1a, 0x25, 0xfd, 0xff, 0x61, 0x07, 0x70, 0x71, + 0x18, 0x70, 0x41, 0x19, 0x70, 0x0c, 0x19, 0x21, 0x05, 0x70, 0x0c, + 0x0a, 0xa9, 0x42, 0xa9, 0x22, 0x92, 0x42, 0x0c, 0x1b, 0x54, 0x0b, + 0x34, 0x72, 0xc7, 0x40, 0xad, 0x03, 0x88, 0x06, 0xb2, 0xa4, 0x00, + 0x88, 0x38, 0xcd, 0x01, 0xe0, 0x08, 0x00, 0xac, 0x4a, 0x0c, 0xe9, + 0x97, 0x1a, 0x03, 0x0c, 0x3a, 0xa9, 0x22, 0x98, 0x22, 0x8c, 0x49, + 0x26, 0x49, 0x02, 0x66, 0x59, 0x0d, 0xa5, 0x88, 0xff, 0xd8, 0x42, + 0xc8, 0x22, 0x0c, 0x2b, 0xd0, 0xbc, 0x93, 0xb9, 0x22, 0xa5, 0xf9, + 0xff, 0x1d, 0xf0, 0xb8, 0x01, 0x16, 0x4b, 0xfc, 0xf8, 0x42, 0xe2, + 0x07, 0x40, 0x1b, 0xff, 0xf9, 0x42, 0x66, 0x1e, 0xb8, 0xad, 0x04, + 0x0b, 0xbb, 0xc1, 0x1a, 0x70, 0x25, 0xe6, 0xff, 0x56, 0x4a, 0x04, + 0x92, 0x02, 0x0c, 0x26, 0x19, 0x39, 0x26, 0x29, 0x36, 0x66, 0x89, + 0x3f, 0x0c, 0x19, 0xb8, 0x01, 0x9c, 0xc9, 0xad, 0x03, 0x0b, 0xbb, + 0xb0, 0xb0, 0x74, 0x65, 0x87, 0xff, 0xc1, 0x18, 0x70, 0xb8, 0x01, + 0xca, 0xcb, 0xc2, 0x0c, 0x7f, 0xa0, 0xcc, 0xc0, 0x56, 0xfc, 0xf7, + 0x0b, 0xbb, 0xb9, 0x01, 0xad, 0x05, 0xb2, 0xcb, 0xfe, 0x7c, 0xfc, + 0xa5, 0xe8, 0xff, 0x16, 0xea, 0xf6, 0x06, 0x01, 0x00, 0x0c, 0x09, + 0x06, 0xf2, 0xff, 0x0c, 0x39, 0x99, 0x22, 0x86, 0xde, 0xff, 0x0c, + 0x69, 0x46, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, + 0x52, 0x4e, 0x20, 0x3a, 0x20, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, + 0x64, 0x61, 0x74, 0x61, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, + 0x75, 0x6d, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x0a, 0x00, 0x00, 0x9a, + 0x99, 0x00, 0xfc, 0xff, 0xff, 0xff, 0x00, 0x80, 0x99, 0x00, 0x6c, + 0x0b, 0x00, 0x00, 0x29, 0x00, 0x00, 0x40, 0x00, 0x10, 0xb0, 0x40, + 0x00, 0x60, 0xbe, 0x40, 0x00, 0x00, 0x06, 0x29, 0x06, 0x08, 0x68, + 0x7a, 0x40, 0x00, 0x74, 0x9a, 0x99, 0x00, 0x24, 0x1a, 0x40, 0x00, + 0x98, 0xa6, 0x99, 0x00, 0x5c, 0x7a, 0x40, 0x00, 0x40, 0x9b, 0x99, + 0x00, 0x08, 0xff, 0x91, 0x00, 0x76, 0x01, 0x00, 0x35, 0xa4, 0xb6, + 0x40, 0x00, 0x15, 0xb6, 0x40, 0x00, 0xac, 0xb6, 0x40, 0x00, 0x20, + 0x2b, 0x40, 0x00, 0x00, 0x00, 0x10, 0x90, 0x70, 0x2d, 0x40, 0x00, + 0x40, 0x8c, 0x40, 0x00, 0xdc, 0x16, 0x40, 0x00, 0xa0, 0x88, 0x40, + 0x00, 0xd0, 0x8a, 0x40, 0x00, 0x60, 0x2d, 0x40, 0x00, 0x64, 0x2d, + 0x40, 0x00, 0x31, 0x5b, 0x91, 0x29, 0x05, 0x64, 0x10, 0x2f, 0x40, + 0x00, 0x30, 0x92, 0x40, 0x00, 0xf0, 0x29, 0x04, 0x2c, 0xb0, 0x40, + 0x00, 0xf0, 0xb0, 0x40, 0x00, 0x80, 0x7a, 0x40, 0x00, 0x60, 0xb0, + 0x40, 0x00, 0x70, 0xa3, 0x99, 0x00, 0xd4, 0x8c, 0x40, 0x00, 0x54, + 0x7a, 0x40, 0x00, 0xc0, 0x8c, 0x40, 0x00, 0xc4, 0x8c, 0x40, 0x00, + 0xfc, 0x9e, 0x99, 0x00, 0x94, 0x9f, 0x99, 0x00, 0x10, 0x9f, 0x99, + 0x00, 0xd4, 0x9f, 0x99, 0x00, 0x6c, 0xa0, 0x99, 0x00, 0xdc, 0xa0, + 0x99, 0x00, 0x48, 0x9b, 0x99, 0x00, 0xf0, 0xa0, 0x99, 0x00, 0x88, + 0xa4, 0x99, 0x00, 0xa0, 0x89, 0x40, 0x00, 0x20, 0x9f, 0x99, 0x00, + 0x80, 0x9f, 0x99, 0x00, 0x1c, 0xa4, 0x99, 0x00, 0x20, 0x80, 0x40, + 0x00, 0xb0, 0xf0, 0x00, 0x00, 0xb4, 0xf0, 0x00, 0x00, 0x50, 0xb1, + 0x40, 0x00, 0x38, 0xb7, 0x40, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1c, + 0x90, 0x00, 0x00, 0x1d, 0x90, 0x00, 0x00, 0x60, 0x94, 0x40, 0x00, + 0xb0, 0x93, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x44, 0xa7, 0x99, + 0x00, 0x55, 0xaa, 0x00, 0x00, 0x8c, 0xa8, 0x99, 0x00, 0x0b, 0x10, + 0x00, 0x00, 0xd0, 0xf0, 0x00, 0x00, 0x00, 0x5a, 0x91, 0x00, 0x70, + 0xb1, 0x40, 0x00, 0xfd, 0xff, 0x00, 0x00, 0xb0, 0xad, 0x40, 0x00, + 0x3c, 0x8d, 0x40, 0x00, 0xb4, 0xad, 0x40, 0x00, 0xc4, 0xad, 0x40, + 0x00, 0xd4, 0xad, 0x40, 0x00, 0xb4, 0x96, 0x40, 0x00, 0xb0, 0xb8, + 0x40, 0x00, 0xf4, 0xb6, 0x40, 0x00, 0x78, 0xa8, 0x99, 0x00, 0x0c, + 0xa7, 0x99, 0x00, 0xf8, 0xa6, 0x99, 0x00, 0x5c, 0x80, 0x40, 0x00, + 0x24, 0x2b, 0x40, 0x00, 0x64, 0xa7, 0x99, 0x00, 0x90, 0xaa, 0x99, + 0x00, 0xd0, 0xaa, 0x99, 0x00, 0x00, 0x02, 0x40, 0x00, 0x6c, 0xaa, + 0x99, 0x00, 0x48, 0xaa, 0x99, 0x00, 0xb0, 0xb5, 0x40, 0x00, 0xf0, + 0xad, 0x40, 0x00, 0xfc, 0x27, 0x40, 0x00, 0x64, 0xab, 0x99, 0x00, + 0x8c, 0xab, 0x99, 0x00, 0x28, 0x2b, 0x40, 0x00, 0x2f, 0xc1, 0x82, + 0x01, 0x90, 0x5f, 0x40, 0x00, 0x00, 0x04, 0x0c, 0x00, 0xf8, 0xf5, + 0x92, 0x00, 0x58, 0x7a, 0x40, 0x00, 0xb8, 0xb5, 0x40, 0x00, 0xe0, + 0x54, 0x9a, 0x00, 0x30, 0x7a, 0x40, 0x00, 0xec, 0xab, 0x99, 0x00, + 0x78, 0xad, 0x99, 0x00, 0xc0, 0xb5, 0x40, 0x00, 0x00, 0x96, 0x02, + 0x00, 0x00, 0xbe, 0x01, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, 0x7e, + 0x02, 0x00, 0x00, 0x82, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0xd0, + 0xad, 0x99, 0x00, 0x80, 0x76, 0x40, 0x00, 0x3c, 0x1f, 0x40, 0x00, + 0x50, 0xae, 0x99, 0x00, 0xcc, 0xa2, 0x40, 0x00, 0x00, 0x6e, 0x00, + 0x00, 0x60, 0xa4, 0x40, 0x00, 0xc8, 0x29, 0x04, 0x38, 0x00, 0xb9, + 0x7c, 0x00, 0x00, 0x9b, 0x3c, 0x00, 0x00, 0x51, 0xbc, 0x00, 0x08, + 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x3e, 0x01, 0x00, 0xa4, + 0xa5, 0x40, 0x00, 0xc4, 0xae, 0x99, 0x00, 0xf4, 0xae, 0x99, 0x00, + 0xa8, 0xaf, 0x99, 0x00, 0x4c, 0xaf, 0x99, 0x00, 0x00, 0x00, 0x8a, + 0xbc, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, + 0xff, 0xfb, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xac, 0xbc, 0x00, + 0x00, 0xac, 0x7c, 0x98, 0x3a, 0x00, 0x00, 0x98, 0xa1, 0x40, 0x00, + 0x5f, 0x09, 0x00, 0x00, 0xb4, 0x09, 0x00, 0x00, 0x54, 0xa5, 0x40, + 0x00, 0xcc, 0xb5, 0x40, 0x00, 0xf4, 0xa1, 0x40, 0x00, 0x38, 0xb0, + 0x99, 0x00, 0x74, 0xb2, 0x99, 0x00, 0xdc, 0xb2, 0x99, 0x00, 0x0c, + 0xb3, 0x99, 0x00, 0xec, 0xb3, 0x99, 0x00, 0xc8, 0x7d, 0x40, 0x00, + 0xe8, 0xef, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xd0, 0xb5, 0x40, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xbc, 0xbc, 0x00, 0x00, + 0x13, 0x7c, 0x00, 0x00, 0x23, 0xbc, 0x00, 0x00, 0xf7, 0xbc, 0x00, + 0x10, 0x00, 0x00, 0xff, 0xff, 0xff, 0xdf, 0x29, 0x04, 0x6f, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x39, 0x7c, 0x00, 0x00, 0x39, 0xbc, + 0x01, 0x00, 0xff, 0xff, 0x02, 0x29, 0x04, 0x1a, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x40, 0x98, 0xb8, 0x96, 0x00, 0x64, 0xb8, 0x96, + 0x00, 0x00, 0x00, 0xd4, 0xbc, 0x00, 0x00, 0xe8, 0xbc, 0x00, 0x00, + 0xd3, 0x7c, 0x00, 0xae, 0x40, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x7c, 0xd4, 0x7f, 0x40, 0x29, 0x04, 0x09, 0x50, 0x00, + 0x00, 0x00, 0x02, 0x38, 0x91, 0x40, 0x00, 0x3c, 0xb7, 0x99, 0x00, + 0xb8, 0xb7, 0x99, 0x00, 0x98, 0xb8, 0x99, 0x00, 0x2c, 0xba, 0x99, + 0x00, 0x64, 0xbb, 0x99, 0x00, 0x5c, 0xbc, 0x99, 0x00, 0x14, 0xbd, + 0x99, 0x00, 0xc8, 0xbd, 0x99, 0x00, 0x74, 0xbd, 0x99, 0x00, 0xcc, + 0xa3, 0x40, 0x00, 0xdc, 0xb4, 0x99, 0x00, 0x40, 0xb5, 0x99, 0x00, + 0x00, 0xb6, 0x99, 0x00, 0x74, 0xb6, 0x99, 0x00, 0x40, 0xb4, 0x99, + 0x00, 0x80, 0xb4, 0x99, 0x00, 0xdc, 0xbd, 0x99, 0x00, 0x00, 0x00, + 0xc9, 0x3c, 0x00, 0x00, 0xc8, 0x3c, 0x6c, 0xbe, 0x99, 0x00, 0x74, + 0xa2, 0x40, 0x00, 0xe0, 0xbe, 0x29, 0x04, 0x14, 0xfd, 0x7c, 0xff, + 0xff, 0xef, 0xff, 0x00, 0x00, 0x10, 0x00, 0x00, 0x9e, 0x02, 0x00, + 0x00, 0xa6, 0x29, 0x05, 0x83, 0x04, 0x08, 0x00, 0x00, 0x00, 0x01, + 0xff, 0xff, 0xfd, 0xff, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x7f, 0xff, 0xd0, 0xa4, + 0x40, 0x00, 0xb4, 0xc0, 0x99, 0x00, 0x50, 0x7e, 0x40, 0x00, 0x08, + 0xae, 0x40, 0x00, 0xe0, 0xb5, 0x40, 0x00, 0x0c, 0xc1, 0x99, 0x00, + 0x30, 0x7d, 0x40, 0x00, 0x54, 0xc1, 0x99, 0x00, 0x8c, 0xc1, 0x99, + 0x00, 0x78, 0xc1, 0x99, 0x00, 0xf0, 0xb5, 0x40, 0x00, 0x01, 0x90, + 0x00, 0x00, 0x70, 0x00, 0x40, 0x00, 0xf8, 0xc1, 0x99, 0x00, 0x00, + 0xa2, 0x02, 0x00, 0x88, 0x71, 0x94, 0x00, 0x06, 0x90, 0x00, 0x00, + 0x47, 0x10, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x02, 0x10, 0x00, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x43, 0x29, 0x04, 0x08, 0x90, 0x00, + 0x00, 0x08, 0x93, 0x40, 0x00, 0x18, 0x2f, 0x40, 0x00, 0x1c, 0x92, + 0x40, 0x00, 0x7c, 0xf3, 0x91, 0x00, 0x1c, 0x2f, 0x40, 0x00, 0x20, + 0x7a, 0x40, 0x00, 0xa4, 0xcf, 0x91, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x10, 0xae, 0x40, 0x00, 0x60, 0xae, 0x40, 0x00, 0x99, 0x79, 0x82, + 0x5a, 0xb0, 0xae, 0x40, 0x00, 0xa1, 0xeb, 0xd9, 0x6e, 0xdc, 0xbc, + 0x1b, 0x8f, 0xd6, 0xc1, 0x62, 0xca, 0xa8, 0xc2, 0x99, 0x00, 0xdc, + 0xc2, 0x99, 0x00, 0xf4, 0xb5, 0x40, 0x00, 0xf4, 0xc3, 0x99, 0x00, + 0x3c, 0xc4, 0x99, 0x00, 0x7c, 0x92, 0x40, 0x00, 0xd0, 0xc4, 0x99, + 0x00, 0xf4, 0x79, 0x40, 0x00, 0xb4, 0xc4, 0x99, 0x00, 0xc4, 0x79, + 0x40, 0x00, 0xa0, 0xc6, 0x99, 0x00, 0x2c, 0x92, 0x40, 0x00, 0x28, + 0x2f, 0x40, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xa8, + 0x00, 0x90, 0x01, 0x00, 0xff, 0xef, 0x00, 0x00, 0x30, 0xac, 0x93, + 0x29, 0x04, 0x10, 0x68, 0x24, 0x93, 0x40, 0x00, 0x18, 0x92, 0x94, + 0x00, 0x30, 0xb6, 0x40, 0x00, 0x10, 0x00, 0xff, 0xff, 0x60, 0xaf, + 0x40, 0x00, 0x00, 0xb6, 0x40, 0x00, 0xb8, 0xb6, 0x40, 0x00, 0xb0, + 0xb6, 0x40, 0x00, 0x30, 0x75, 0x00, 0x00, 0xa0, 0x8e, 0x40, 0x00, + 0x90, 0xb6, 0x40, 0x29, 0x05, 0x83, 0x22, 0x11, 0x29, 0x05, 0x83, + 0x58, 0x00, 0x00, 0xb8, 0x0b, 0x00, 0x00, 0xff, 0x8f, 0x01, 0x00, + 0x00, 0x00, 0x23, 0xa8, 0xb8, 0x9a, 0x40, 0x29, 0x04, 0x08, 0x68, + 0x5c, 0xaf, 0x40, 0x00, 0xff, 0x7f, 0x00, 0x00, 0x50, 0xaf, 0x40, + 0x00, 0x50, 0xb6, 0x40, 0x00, 0x48, 0x94, 0x40, 0x00, 0xe4, 0x91, + 0x94, 0x00, 0x00, 0x00, 0x08, 0x68, 0x11, 0x11, 0x29, 0x04, 0x36, + 0x09, 0xa8, 0x00, 0x00, 0x18, 0x28, 0x2d, 0x2f, 0x40, 0x00, 0x44, + 0x94, 0x40, 0x00, 0x70, 0xb6, 0x40, 0x00, 0x7c, 0x91, 0x94, 0x00, + 0x94, 0x29, 0x04, 0x5c, 0x00, 0x10, 0xa8, 0x00, 0x00, 0x15, 0xa8, + 0x98, 0x29, 0x04, 0x0c, 0x60, 0x00, 0x00, 0x20, 0x4e, 0x00, 0x00, + 0x24, 0x2e, 0x40, 0x00, 0xac, 0x7a, 0x40, 0x00, 0x7c, 0x8d, 0x29, + 0x04, 0x20, 0x12, 0x68, 0x38, 0x94, 0x29, 0x04, 0x08, 0x11, 0x68, + 0xac, 0x29, 0x05, 0x88, 0x04, 0x11, 0xa8, 0x3c, 0x94, 0x40, 0x00, + 0x20, 0x93, 0x40, 0x00, 0x40, 0x94, 0x40, 0x00, 0x60, 0x91, 0x94, + 0x00, 0xc8, 0xc9, 0x99, 0x00, 0x64, 0xca, 0x99, 0x00, 0x14, 0xca, + 0x99, 0x00, 0x00, 0x00, 0x16, 0xa8, 0x5c, 0xc8, 0x99, 0x00, 0x9c, + 0xcb, 0x99, 0x00, 0x1c, 0xd1, 0x99, 0x00, 0xe4, 0xd3, 0x99, 0x00, + 0x8c, 0xde, 0x99, 0x00, 0xc0, 0xd7, 0x99, 0x00, 0x30, 0xdd, 0x99, + 0x00, 0x94, 0xdf, 0x99, 0x00, 0xb8, 0xd0, 0x99, 0x00, 0x84, 0xd0, + 0x99, 0x00, 0x3c, 0xd7, 0x99, 0x00, 0xc4, 0xdc, 0x99, 0x00, 0xa0, + 0xb6, 0x40, 0x00, 0x58, 0xaf, 0x40, 0x00, 0x88, 0x05, 0x92, 0x00, + 0x7c, 0x06, 0x92, 0x00, 0x88, 0xe1, 0x99, 0x00, 0xba, 0xf0, 0x00, + 0x00, 0x64, 0xaf, 0x40, 0x00, 0xe8, 0x8c, 0x40, 0x00, 0x40, 0x42, + 0x0f, 0x00, 0x0d, 0xb6, 0x40, 0x00, 0x16, 0xb6, 0x40, 0x00, 0x14, + 0xb6, 0x40, 0x00, 0x10, 0xb6, 0x40, 0x00, 0x08, 0xb6, 0x40, 0x00, + 0x0c, 0x29, 0x05, 0x81, 0x40, 0x7a, 0x88, 0x80, 0xe7, 0x29, 0x04, + 0x78, 0x78, 0x88, 0x14, 0xe7, 0x99, 0x00, 0x10, 0xe2, 0x99, 0x00, + 0x5c, 0xe8, 0x99, 0x00, 0x94, 0xe7, 0x99, 0x00, 0xcc, 0x7b, 0x40, + 0x00, 0xc0, 0xb6, 0x40, 0x00, 0xfc, 0xe8, 0x99, 0x00, 0x68, 0xe9, + 0x99, 0x00, 0x64, 0xac, 0x93, 0x00, 0xc4, 0xb6, 0x40, 0x00, 0xa8, + 0xe9, 0x99, 0x00, 0xa0, 0x77, 0x40, 0x00, 0xf8, 0x7f, 0x40, 0x00, + 0xf0, 0x2e, 0x40, 0x00, 0x20, 0xad, 0x40, 0x00, 0x28, 0x2e, 0x40, + 0x00, 0x74, 0xaf, 0x40, 0x00, 0x60, 0xf8, 0x96, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x08, 0xea, 0x99, 0x00, 0x8c, 0xee, 0x99, 0x00, 0xd8, + 0xe9, 0x99, 0x00, 0x7b, 0x15, 0x00, 0x00, 0xc8, 0x29, 0x04, 0x74, + 0x0d, 0x00, 0x00, 0xaa, 0xec, 0xff, 0xff, 0x00, 0xa5, 0x00, 0x00, + 0x00, 0x29, 0x05, 0x84, 0x2e, 0x29, 0x05, 0x86, 0x6e, 0x00, 0xff, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x00, 0xa6, 0x00, 0x00, + 0xdc, 0xa2, 0x00, 0x00, 0xdc, 0xb2, 0x00, 0x00, 0xe8, 0x1e, 0x40, + 0x00, 0xb0, 0x0b, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0xb2, + 0x02, 0x00, 0x48, 0xf2, 0x99, 0x00, 0xb4, 0xef, 0x99, 0x00, 0xbc, + 0xf1, 0x99, 0x00, 0x74, 0xf2, 0x99, 0x00, 0xb8, 0x1c, 0x40, 0x00, + 0xec, 0xa2, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x0f, 0xdc, 0xb6, 0x40, + 0x00, 0x84, 0x75, 0x40, 0x00, 0x98, 0xf4, 0x99, 0x00, 0xa0, 0xf5, + 0x99, 0x00, 0xbc, 0xf5, 0x99, 0x00, 0x90, 0xb7, 0x40, 0x00, 0x08, + 0x2f, 0x40, 0x00, 0x3c, 0xf7, 0x99, 0x00, 0x80, 0xf6, 0x99, 0x00, + 0xdc, 0x17, 0x40, 0x00, 0x18, 0xf8, 0x99, 0x00, 0xec, 0xb6, 0x40, + 0x00, 0x0f, 0x02, 0x40, 0x00, 0x84, 0xe1, 0x94, 0x00, 0xe8, 0xff, + 0x00, 0x00, 0x88, 0x7b, 0x40, 0x00, 0x00, 0x00, 0x17, 0x24, 0x00, + 0x00, 0x11, 0x64, 0x00, 0x00, 0xa5, 0xa5, 0x00, 0x00, 0x11, 0x24, + 0x00, 0x00, 0x18, 0x64, 0x10, 0x7d, 0x40, 0x00, 0x03, 0x00, 0xa5, + 0xa5, 0x04, 0x00, 0xa5, 0xa5, 0x05, 0x00, 0xa5, 0xa5, 0x01, 0x00, + 0xa5, 0xa5, 0x02, 0x00, 0xa5, 0xa5, 0x06, 0x29, 0x05, 0x24, 0x13, + 0xa4, 0x00, 0x00, 0x12, 0xa4, 0xb6, 0x95, 0x40, 0x00, 0x80, 0xaf, + 0x40, 0x00, 0x48, 0xf8, 0x99, 0x00, 0x48, 0xf9, 0x99, 0x00, 0xe0, + 0xfa, 0x99, 0x00, 0xd0, 0xfc, 0x99, 0x00, 0x30, 0xf8, 0x99, 0x00, + 0x98, 0xf8, 0x99, 0x00, 0x34, 0xf9, 0x99, 0x00, 0x7c, 0xf8, 0x99, + 0x00, 0x00, 0xb7, 0x40, 0x00, 0x74, 0x88, 0x40, 0x00, 0xd0, 0x89, + 0x40, 0x00, 0x60, 0xfe, 0x99, 0x00, 0x78, 0xfe, 0x99, 0x00, 0x8c, + 0xfe, 0x99, 0x00, 0xd0, 0x29, 0x04, 0x0c, 0x00, 0x9a, 0x00, 0x00, + 0x00, 0x49, 0x88, 0x00, 0x00, 0x4a, 0x08, 0x0a, 0x90, 0x00, 0x00, + 0xa0, 0x02, 0x9a, 0x00, 0xbc, 0x84, 0x95, 0x00, 0x00, 0x00, 0x44, + 0x48, 0x0c, 0x90, 0x00, 0x00, 0xf0, 0x2f, 0x40, 0x00, 0x78, 0x9b, + 0x40, 0x00, 0xf8, 0x9b, 0x40, 0x00, 0x04, 0x09, 0x09, 0x01, 0x4c, + 0x01, 0x9a, 0x00, 0x6c, 0x07, 0x9a, 0x00, 0xdc, 0x05, 0x9a, 0x00, + 0x78, 0x88, 0x40, 0x00, 0x58, 0x2d, 0x40, 0x00, 0x14, 0xb7, 0x40, + 0x00, 0x28, 0x08, 0x9a, 0x00, 0x78, 0x0a, 0x9a, 0x00, 0xc0, 0x0a, + 0x9a, 0x00, 0x20, 0xb7, 0x40, 0x00, 0x60, 0xb7, 0x40, 0x00, 0xa4, + 0x0d, 0x9a, 0x00, 0x7c, 0x78, 0x40, 0x00, 0x20, 0x55, 0x9a, 0x00, + 0x40, 0x55, 0x9a, 0x00, 0xd0, 0x95, 0x40, 0x00, 0x60, 0x55, 0x9a, + 0x00, 0x4c, 0xb7, 0x40, 0x00, 0xf8, 0x0d, 0x9a, 0x00, 0xa5, 0xf0, + 0x00, 0x00, 0xa6, 0xf0, 0x00, 0x00, 0xa0, 0xaf, 0x40, 0x00, 0x78, + 0xb7, 0x40, 0x00, 0x98, 0x13, 0x9a, 0x00, 0x98, 0x10, 0x9a, 0x00, + 0xf0, 0x10, 0x9a, 0x00, 0x00, 0x15, 0x9a, 0x00, 0xc0, 0x12, 0x9a, + 0x00, 0xc0, 0x13, 0x9a, 0x00, 0x30, 0xad, 0x40, 0x00, 0x88, 0xb7, + 0x40, 0x00, 0x40, 0xad, 0x40, 0x00, 0x20, 0x7c, 0x40, 0x00, 0x00, + 0x00, 0x04, 0x74, 0x40, 0x88, 0x40, 0x00, 0x40, 0x7c, 0x40, 0x00, + 0x80, 0x15, 0x9a, 0x00, 0xa4, 0x15, 0x9a, 0x00, 0x34, 0x90, 0x40, + 0x00, 0xb0, 0xb7, 0x40, 0x00, 0xc4, 0x1b, 0x9a, 0x00, 0x38, 0x1c, + 0x9a, 0x00, 0xec, 0x1b, 0x9a, 0x00, 0x7c, 0x1c, 0x9a, 0x00, 0x04, + 0x8e, 0x40, 0x00, 0xd0, 0xb7, 0x40, 0x00, 0x00, 0xb8, 0x40, 0x00, + 0xc0, 0xaf, 0x40, 0x00, 0xa0, 0x29, 0x05, 0x8f, 0x3c, 0x00, 0x5a, + 0x00, 0x00, 0x0c, 0xb4, 0x40, 0xb8, 0x40, 0x00, 0x74, 0x1e, 0x9a, + 0x00, 0xd4, 0xb7, 0x40, 0x00, 0xbc, 0x1e, 0x9a, 0x00, 0x00, 0x2f, + 0x40, 0x00, 0x00, 0x50, 0xf2, 0x01, 0x20, 0xb8, 0x40, 0x00, 0xcc, + 0x1c, 0x9a, 0x00, 0xd4, 0x21, 0x9a, 0x00, 0xa8, 0x22, 0x9a, 0x00, + 0xf4, 0x21, 0x9a, 0x00, 0x28, 0x1e, 0x9a, 0x00, 0xe8, 0x1e, 0x9a, + 0x00, 0x08, 0x20, 0x9a, 0x00, 0x44, 0x20, 0x9a, 0x00, 0xbc, 0x29, + 0x04, 0x18, 0x22, 0x9a, 0x00, 0x00, 0x52, 0x00, 0x00, 0xa4, 0x23, + 0x9a, 0x00, 0x44, 0xb8, 0x40, 0x29, 0x05, 0x8c, 0x36, 0x00, 0xc0, + 0x29, 0x06, 0x8b, 0x52, 0x88, 0x13, 0x00, 0x00, 0xf0, 0x24, 0x9a, + 0x00, 0x1c, 0x24, 0x9a, 0x00, 0xcc, 0x24, 0x9a, 0x00, 0xb8, 0x25, + 0x9a, 0x00, 0x58, 0xb8, 0x40, 0x00, 0x5c, 0xb8, 0x40, 0x00, 0x70, + 0x7b, 0x40, 0x00, 0x04, 0x26, 0x9a, 0x00, 0x20, 0x7b, 0x40, 0x00, + 0x4c, 0x27, 0x9a, 0x00, 0x38, 0x26, 0x9a, 0x00, 0xcc, 0x26, 0x9a, + 0x00, 0x00, 0x00, 0x0e, 0xb4, 0x74, 0xb8, 0x40, 0x00, 0xe8, 0x27, + 0x9a, 0x00, 0x78, 0xb8, 0x40, 0x00, 0x30, 0x8f, 0x40, 0x00, 0x9c, + 0xad, 0x40, 0x00, 0x34, 0x8f, 0x40, 0x00, 0x0f, 0xf0, 0x00, 0x00, + 0xa0, 0x5f, 0x40, 0x00, 0x5c, 0x28, 0x9a, 0x00, 0x70, 0x28, 0x9a, + 0x00, 0xa0, 0x28, 0x9a, 0x00, 0x8c, 0x28, 0x9a, 0x00, 0x0c, 0x2b, + 0x9a, 0x00, 0x90, 0x29, 0x04, 0x7c, 0x00, 0x0a, 0xac, 0x00, 0x00, + 0x1b, 0xac, 0x00, 0x00, 0x1c, 0x6c, 0x00, 0x00, 0x19, 0x6c, 0x00, + 0x00, 0x02, 0xac, 0xa0, 0x0f, 0x00, 0x00, 0xd0, 0x94, 0x40, 0x00, + 0x6c, 0x2d, 0x9a, 0x00, 0x44, 0x2c, 0x9a, 0x00, 0x64, 0x2b, 0x9a, + 0x00, 0x4c, 0x2d, 0x9a, 0x00, 0x14, 0x2d, 0x9a, 0x00, 0xf4, 0x2f, + 0x9a, 0x00, 0x5c, 0x30, 0x9a, 0x00, 0xb8, 0xb8, 0x40, 0x00, 0x0c, + 0x31, 0x9a, 0x00, 0xbc, 0xb8, 0x40, 0x00, 0xa8, 0x7f, 0x40, 0x00, + 0x34, 0x31, 0x9a, 0x00, 0x48, 0x31, 0x9a, 0x00, 0x5c, 0x31, 0x9a, + 0x00, 0x70, 0x31, 0x9a, 0x00, 0x84, 0x31, 0x9a, 0x00, 0x00, 0x06, + 0x02, 0x00, 0xd0, 0xb8, 0x40, 0x00, 0xd0, 0x29, 0x04, 0x0c, 0x00, + 0xff, 0xff, 0xd4, 0xb8, 0x40, 0x00, 0xbc, 0x8c, 0x40, 0x00, 0x10, + 0x8d, 0x40, 0x00, 0x3c, 0x33, 0x9a, 0x00, 0xd0, 0x32, 0x9a, 0x00, + 0x1c, 0x34, 0x9a, 0x00, 0xdc, 0xb8, 0x40, 0x00, 0x60, 0x9f, 0x40, + 0x00, 0x7c, 0x34, 0x9a, 0x00, 0xb0, 0x34, 0x9a, 0x00, 0xc4, 0x34, + 0x9a, 0x00, 0xec, 0x34, 0x9a, 0x00, 0x00, 0x01, 0x40, 0x00, 0x04, + 0x35, 0x9a, 0x00, 0xd8, 0x34, 0x9a, 0x00, 0x20, 0x35, 0x9a, 0x00, + 0x00, 0x3e, 0x00, 0x00, 0x20, 0xb9, 0x40, 0x00, 0x78, 0x56, 0x34, + 0x12, 0x1c, 0x3b, 0x40, 0x00, 0x24, 0x9f, 0x40, 0x00, 0x24, 0xb9, + 0x40, 0x00, 0x14, 0xba, 0x40, 0x00, 0x18, 0xba, 0x40, 0x00, 0x88, + 0x35, 0x9a, 0x00, 0x37, 0x50, 0x40, 0x00, 0x37, 0x46, 0x40, 0x00, + 0x00, 0x10, 0x40, 0x00, 0x00, 0x3b, 0x40, 0x00, 0x0c, 0x47, 0x05, + 0x00, 0x08, 0x3e, 0x05, 0x00, 0x00, 0x00, 0x04, 0x04, 0x18, 0x30, + 0x40, 0x00, 0x00, 0xb9, 0x40, 0x00, 0x00, 0xce, 0x01, 0x00, 0x80, + 0x55, 0x9a, 0x00, 0x50, 0x9c, 0x40, 0x00, 0x18, 0xb7, 0x95, 0x00, + 0x00, 0x01, 0x02, 0x04, 0x2e, 0x32, 0x40, 0x00, 0xd8, 0x14, 0x40, + 0x00, 0x9c, 0x39, 0x9a, 0x00, 0x84, 0x31, 0x40, 0x00, 0x78, 0x39, + 0x9a, 0x00, 0x4c, 0x39, 0x9a, 0x00, 0xf0, 0xbd, 0x40, 0x00, 0xec, + 0x8b, 0x40, 0x00, 0x38, 0x3a, 0x9a, 0x00, 0xcc, 0x2b, 0x40, 0x00, + 0xe8, 0x7f, 0x40, 0x00, 0xac, 0x3a, 0x9a, 0x00, 0xf4, 0xbd, 0x40, + 0x00, 0xa4, 0x98, 0x40, 0x00, 0xe8, 0x3d, 0x9a, 0x00, 0x64, 0x97, + 0x40, 0x00, 0x70, 0x3d, 0x9a, 0x00, 0x98, 0x3d, 0x9a, 0x00, 0xc0, + 0x3d, 0x9a, 0x00, 0xa0, 0x55, 0x9a, 0x00, 0xc0, 0x55, 0x9a, 0x00, + 0x04, 0xbe, 0x40, 0x00, 0x54, 0x3e, 0x9a, 0x00, 0x70, 0x3e, 0x9a, + 0x00, 0x8f, 0x3e, 0x9a, 0x00, 0xbc, 0x3e, 0x9a, 0x00, 0x6c, 0x4a, + 0x90, 0x00, 0x3c, 0x3e, 0x9a, 0x00, 0x08, 0xbe, 0x40, 0x00, 0x34, + 0x3f, 0x9a, 0x00, 0x0c, 0x3f, 0x9a, 0x00, 0x20, 0x3f, 0x9a, 0x00, + 0x98, 0xa6, 0x40, 0x00, 0x14, 0xbe, 0x40, 0x00, 0x48, 0xca, 0x96, + 0x00, 0x48, 0xad, 0x40, 0x00, 0x04, 0x92, 0x40, 0x00, 0xea, 0xff, + 0x00, 0x00, 0x50, 0xad, 0x40, 0x00, 0x9f, 0xf0, 0x00, 0x00, 0xa0, + 0xf0, 0x00, 0x00, 0xa1, 0xf0, 0x00, 0x00, 0xa2, 0xf0, 0x00, 0x00, + 0xa3, 0xf0, 0x00, 0x00, 0x54, 0xcb, 0x96, 0x00, 0xac, 0x3f, 0x9a, + 0x00, 0x68, 0xcb, 0x96, 0x00, 0xe0, 0x3f, 0x9a, 0x00, 0x10, 0x40, + 0x9a, 0x00, 0xc0, 0x40, 0x9a, 0x00, 0x58, 0x41, 0x9a, 0x00, 0xec, + 0x43, 0x9a, 0x00, 0xe4, 0x43, 0x9a, 0x00, 0xd4, 0x44, 0x9a, 0x00, + 0x28, 0x46, 0x9a, 0x00, 0x98, 0x46, 0x9a, 0x00, 0x18, 0xbe, 0x40, + 0x00, 0x00, 0x78, 0x40, 0x00, 0x34, 0x48, 0x9a, 0x00, 0x1c, 0x29, + 0x04, 0x0c, 0xf0, 0x03, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x3f, 0xf0, + 0xff, 0xff, 0x00, 0xfe, 0x00, 0x00, 0xff, 0x01, 0xff, 0xff, 0xd0, + 0x16, 0x40, 0x00, 0xd4, 0x16, 0x40, 0x00, 0x2c, 0xbe, 0x40, 0x00, + 0x54, 0xbe, 0x40, 0x00, 0xe0, 0xab, 0x00, 0x00, 0xc0, 0x57, 0x01, + 0x00, 0x80, 0x38, 0x01, 0x00, 0x40, 0x9c, 0x00, 0x00, 0x70, 0x69, + 0x90, 0x00, 0x00, 0x7e, 0x01, 0x00, 0xf4, 0x4a, 0x9a, 0x00, 0xdc, + 0x4a, 0x9a, 0x00, 0xc4, 0x4a, 0x9a, 0x00, 0x70, 0x48, 0x9a, 0x00, + 0xe8, 0x48, 0x9a, 0x00, 0x08, 0x46, 0x40, 0x00, 0x98, 0x2b, 0x40, + 0x00, 0xa0, 0x7a, 0x40, 0x00, 0x0c, 0x4d, 0x9a, 0x00, 0xae, 0xf0, + 0x00, 0x00, 0xd0, 0xaf, 0x40, 0x00, 0x50, 0x62, 0x40, 0x00, 0x58, + 0xbe, 0x40, 0x00, 0x60, 0x4e, 0x9a, 0x00, 0xf4, 0x3a, 0x40, 0x00, + 0x20, 0x9f, 0x40, 0x00, 0xfc, 0x3a, 0x40, 0x00, 0xf8, 0x3a, 0x40, + 0x00, 0x0c, 0xb9, 0x40, 0x00, 0x00, 0x00, 0x90, 0x00, 0xa8, 0x4e, + 0x9a, 0x00, 0x54, 0xcf, 0x95, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x5c, + 0xbe, 0x40, 0x00, 0x3c, 0x4f, 0x9a, 0x00, 0x00, 0xad, 0x40, 0x00, + 0xb0, 0x00, 0x00, 0x00, 0x05, 0x50, 0xa9, 0x99, 0x00, 0xd0, 0xf0, + 0x08, 0x00, 0xe8, 0xa9, 0x99, 0x00, 0xfd, 0xff, 0x01, 0x00, 0x64, + 0xe2, 0x99, 0x00, 0xba, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x04, 0x04, 0x05, 0x08, 0x08, 0x05, 0x09, 0x09, 0x99, 0x36, 0xb6, + 0xd3, 0xb2, 0xe9, 0xc3, 0x85, 0xb4, 0xda, 0xd2, 0x85, 0xd6, 0xef, + 0x2c, 0x92, 0x36, 0x5c, 0x36, 0x5c, 0x05, 0x04, 0x04, 0xaa, 0xaa, + 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x57, 0x4c, 0x41, 0x4e, 0x5f, + 0x48, 0x42, 0x00, 0x38, 0x4d, 0x9a, 0x00, 0xae, 0xf0, 0x08, 0x00, + 0x36, 0x41, 0x00, 0x00, 0xa0, 0x20, 0xc5, 0x05, 0x0d, 0x44, 0x02, + 0xc0, 0x03, 0x92, 0x20, 0x00, 0x82, 0x20, 0x01, 0x92, 0x61, 0x03, + 0x92, 0x28, 0x00, 0x90, 0x9c, 0xb4, 0xd0, 0x99, 0x11, 0x92, 0xc9, + 0xe0, 0x90, 0x91, 0xc0, 0x10, 0x19, 0x00, 0xa0, 0x0a, 0x20, 0x82, + 0xc8, 0x03, 0xa0, 0x08, 0x00, 0x00, 0x46, 0x46, 0x00, 0x00, 0xb0, + 0xad, 0x40, 0x05, 0x04, 0x04, 0x00, 0x10, 0xb0, 0x40, 0x00, 0x60, + 0xbe, 0x40, 0x00, 0xb0, 0xad, 0x40, 0x00, 0x54, 0x02, 0x00, 0x00, + 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x04, 0x04, 0x00, 0xff, + 0xff, 0x05, 0x04, 0x0c, 0xe8, 0xad, 0x40, 0x05, 0x0d, 0x10, 0x00, + 0x05, 0x0f, 0x10, 0x08, 0x05, 0x07, 0x10, 0x94, 0xa8, 0x99, 0x00, + 0x10, 0x05, 0x0b, 0x3c, 0x05, 0x04, 0x06, 0x10, 0xad, 0x40, 0x00, + 0x05, 0x08, 0x50, 0x7f, 0xf0, 0x00, 0x00, 0x1f, 0xf8, 0x05, 0x09, + 0x21, 0x05, 0x09, 0x09, 0x05, 0x12, 0x12, 0x05, 0x24, 0x24, 0x05, + 0x48, 0x48, 0x05, 0x81, 0x10, 0x81, 0x10, 0x05, 0x22, 0x22, 0x04, + 0x00, 0x00, 0x05, 0x06, 0x82, 0x54, 0x44, 0x00, 0x00, 0x32, 0x05, + 0x08, 0x0c, 0x05, 0x0b, 0x83, 0x10, 0x10, 0xad, 0x40, 0x00, 0x02, + 0x05, 0x0b, 0x30, 0x58, 0x16, 0x71, 0x16, 0x85, 0x16, 0x99, 0x16, + 0xad, 0x16, 0xc1, 0x16, 0x05, 0x1a, 0x56, 0xff, 0xff, 0x05, 0x04, + 0x34, 0xb0, 0xaf, 0x40, 0x00, 0x38, 0x10, 0x9a, 0x00, 0xa5, 0xf0, + 0x04, 0x05, 0x05, 0x08, 0xa6, 0xf0, 0x18, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x96, 0x05, 0x07, 0x78, 0x05, 0x0c, 0x30, 0x05, 0x04, 0x6c, + 0x58, 0xad, 0x40, 0x00, 0x0c, 0x05, 0x07, 0x12, 0x01, 0x00, 0x01, + 0x7c, 0x00, 0x0c, 0x01, 0x05, 0x05, 0x84, 0x04, 0x05, 0x04, 0x34, + 0xa8, 0x4f, 0x9a, 0x00, 0x53, 0x03, 0x00, 0x00, 0x0e, 0xa0, 0x01, + 0x00, 0x00, 0x9a, 0x99, 0x00, 0xd6, 0xbb, 0x00, 0x00, 0x8e, 0x36, + 0x61, 0x00, 0x21, 0x00, 0x70, 0xa1, 0x01, 0x70, 0x88, 0x12, 0xb1, + 0x02, 0x70, 0xa7, 0x18, 0x0f, 0x9d, 0x0a, 0xa0, 0xbb, 0xc0, 0xa2, + 0xa0, 0x00, 0x76, 0xab, 0x04, 0xa2, 0x49, 0x00, 0x1b, 0x99, 0x88, + 0x02, 0x31, 0x03, 0x70, 0x91, 0x04, 0x70, 0xb2, 0xa6, 0x64, 0x0c, + 0x4a, 0xa9, 0x01, 0xb9, 0x11, 0x0c, 0x8a, 0xbd, 0x01, 0x92, 0x63, + 0xa6, 0xe0, 0x08, 0x00, 0xac, 0xfa, 0x82, 0x22, 0x75, 0x0c, 0x0a, + 0xe0, 0x08, 0x00, 0xa1, 0x09, 0x70, 0x91, 0x0a, 0x70, 0xe1, 0x07, + 0x70, 0xd1, 0x08, 0x70, 0x81, 0x05, 0x70, 0xf1, 0x06, 0x70, 0xb2, + 0x23, 0xb3, 0x0c, 0x8c, 0xc0, 0xbb, 0x20, 0xf9, 0x08, 0xd9, 0x0e, + 0xb2, 0x63, 0xb3, 0x99, 0x0a, 0x81, 0x0b, 0x70, 0xe0, 0x08, 0x00, + 0x1d, 0xf0, 0x00, 0x00, 0x00, 0x36, 0x41, 0x00, 0x0c, 0x1b, 0x91, + 0x03, 0x70, 0x81, 0x0c, 0x70, 0x82, 0x69, 0xb4, 0xa2, 0x29, 0xbc, + 0xd2, 0x29, 0xb3, 0x07, 0x6a, 0x07, 0x7c, 0xec, 0xc0, 0xca, 0x10, + 0xc2, 0x69, 0xbc, 0x97, 0x7d, 0x11, 0xe1, 0x0d, 0x70, 0x81, 0x0e, + 0x70, 0xf1, 0x0f, 0x70, 0xb2, 0x48, 0x00, 0xb2, 0x4f, 0x00, 0xb2, + 0x4e, 0x00, 0xa1, 0x10, 0x70, 0xa2, 0x2a, 0x00, 0xa2, 0x2a, 0x0f, + 0x65, 0xbb, 0x00, 0x25, 0x9a, 0x09, 0x25, 0xac, 0x00, 0xe5, 0x10, + 0x01, 0x65, 0xd4, 0x05, 0x25, 0x33, 0x02, 0x25, 0x95, 0x01, 0xe5, + 0x54, 0x01, 0xa5, 0x46, 0x02, 0x65, 0x60, 0x02, 0x65, 0x33, 0x06, + 0x25, 0x6e, 0x02, 0x65, 0x78, 0x02, 0xa5, 0xd2, 0x02, 0x25, 0x55, + 0x04, 0x65, 0x61, 0x06, 0xa5, 0xd2, 0x06, 0xe5, 0x04, 0x07, 0x25, + 0xaf, 0x08, 0x65, 0xa2, 0x07, 0xa5, 0x0b, 0x08, 0x25, 0x1a, 0x08, + 0xa5, 0x80, 0x0b, 0x25, 0x84, 0x08, 0x65, 0xca, 0x08, 0x25, 0x62, + 0x09, 0xa5, 0x69, 0x09, 0x25, 0xd4, 0x08, 0xe5, 0x01, 0x09, 0x65, + 0xdd, 0x04, 0x25, 0x41, 0x05, 0xe5, 0xe7, 0x04, 0x25, 0x58, 0x09, + 0xe5, 0xa1, 0x09, 0x65, 0xfd, 0x00, 0x65, 0x24, 0x0a, 0x65, 0x2e, + 0x0a, 0x25, 0x3d, 0x0a, 0xa5, 0xd3, 0x0a, 0xa5, 0x42, 0x0a, 0x25, + 0xcd, 0x0a, 0xa5, 0xf6, 0x09, 0xa5, 0x1f, 0x0b, 0x65, 0xea, 0x09, + 0xe5, 0x35, 0x0b, 0x65, 0x3e, 0x0b, 0x8e, 0x08, 0x81, 0x4c, 0xe5, + 0x26, 0x01, 0x1d, 0xf0, 0x36, 0xa1, 0x00, 0x29, 0x91, 0xa1, 0x11, + 0x70, 0x41, 0x00, 0x70, 0xb8, 0x03, 0x82, 0x24, 0xa1, 0xc8, 0x63, + 0xe0, 0x08, 0x00, 0xb2, 0xa1, 0xec, 0xa8, 0x03, 0x91, 0x12, 0x70, + 0xc8, 0x91, 0x98, 0x09, 0xc2, 0x0c, 0x03, 0x90, 0xda, 0xa0, 0xd2, + 0xdd, 0x01, 0xd2, 0x0d, 0xa4, 0xd9, 0xa1, 0x66, 0x3c, 0x11, 0xc8, + 0x91, 0xc2, 0x0c, 0x01, 0xba, 0xb9, 0x26, 0x1c, 0x09, 0xd2, 0x29, + 0x81, 0xa7, 0x9d, 0x03, 0x1d, 0xf0, 0xba, 0xb9, 0x92, 0x0b, 0xe1, + 0x81, 0x13, 0x70, 0x90, 0x90, 0x04, 0x88, 0x48, 0x99, 0x81, 0xe0, + 0x08, 0x00, 0x0c, 0x09, 0xf2, 0xc3, 0x10, 0xf9, 0x51, 0x99, 0x61, + 0xd8, 0x43, 0x48, 0x53, 0xcc, 0x3d, 0x0c, 0x04, 0x86, 0x00, 0x00, + 0x42, 0xc4, 0xec, 0x56, 0x54, 0x04, 0x88, 0x81, 0x98, 0x61, 0x92, + 0x43, 0x0e, 0x99, 0x13, 0x99, 0x23, 0x56, 0x38, 0xfc, 0xa8, 0xa1, + 0xf6, 0x4a, 0xbe, 0xad, 0x01, 0x81, 0x00, 0x70, 0x0c, 0x0b, 0x82, + 0x28, 0x3e, 0x1c, 0x0c, 0xe0, 0x08, 0x00, 0x3d, 0x01, 0x19, 0x11, + 0xa8, 0x61, 0x98, 0xa1, 0xa9, 0x01, 0x92, 0xc9, 0xfd, 0x16, 0xd9, + 0x2b, 0xb8, 0xa1, 0xb2, 0xcb, 0xfe, 0x16, 0x0b, 0x2b, 0xc8, 0xa1, + 0x0b, 0xcc, 0x56, 0xac, 0x29, 0x0c, 0x1a, 0xc6, 0xaa, 0x00, 0xa8, + 0x63, 0x68, 0x44, 0x0b, 0xaa, 0xd7, 0x94, 0x0c, 0xe8, 0x5d, 0xe9, + 0x43, 0xdc, 0xce, 0xf8, 0x51, 0xf9, 0x53, 0x86, 0x05, 0x00, 0xc8, + 0x5d, 0xc7, 0x14, 0x06, 0xdd, 0x0c, 0xc8, 0x5c, 0xc7, 0x94, 0xf8, + 0x88, 0x5c, 0x89, 0x5d, 0xcc, 0x38, 0x92, 0xcd, 0x14, 0x99, 0x53, + 0xa9, 0x63, 0xb2, 0x06, 0x7a, 0xb2, 0x41, 0x10, 0x52, 0x26, 0x20, + 0xc2, 0xa2, 0xdc, 0x16, 0x25, 0x04, 0xca, 0xc5, 0xb2, 0x0c, 0x7d, + 0x0b, 0xbb, 0xb0, 0xb0, 0x74, 0xb2, 0x4c, 0x7d, 0xec, 0xfb, 0x22, + 0xa1, 0x9c, 0xd2, 0x25, 0xe0, 0x2a, 0x25, 0x77, 0x6d, 0x25, 0x0c, + 0x0b, 0x1c, 0xfc, 0x81, 0x14, 0x70, 0xa2, 0x02, 0x25, 0x82, 0x28, + 0xc8, 0x00, 0xaa, 0x23, 0xe0, 0x08, 0x00, 0xa2, 0x22, 0x25, 0xbd, + 0x05, 0x81, 0x15, 0x70, 0xc2, 0xa0, 0xff, 0x88, 0x38, 0xd2, 0xa0, + 0x80, 0xe0, 0x08, 0x00, 0xb2, 0x01, 0x10, 0x71, 0x16, 0x70, 0x82, + 0x27, 0x3b, 0xad, 0x05, 0xe0, 0x08, 0x00, 0x98, 0x81, 0x2d, 0x0a, + 0x16, 0x99, 0x09, 0xa8, 0xa1, 0xb6, 0x4a, 0x02, 0x46, 0x24, 0x00, + 0xb2, 0x26, 0x1f, 0xc7, 0x7b, 0x02, 0x06, 0x22, 0x00, 0x0c, 0x07, + 0xd2, 0x26, 0x26, 0x0c, 0x0e, 0xe9, 0x71, 0xd2, 0x0d, 0x00, 0x0c, + 0xce, 0xe0, 0xcd, 0x10, 0xe7, 0x0d, 0x05, 0x26, 0x4c, 0x02, 0x06, + 0x20, 0x00, 0x16, 0xd5, 0x07, 0x92, 0x25, 0xde, 0xe2, 0xa2, 0xdc, + 0xf2, 0x25, 0xdf, 0xea, 0xe5, 0xcc, 0x1f, 0x16, 0xd9, 0x06, 0x99, + 0x54, 0x0c, 0x17, 0xcc, 0x49, 0xf2, 0xc4, 0x14, 0xf2, 0x65, 0xdf, + 0x42, 0x6e, 0x27, 0x16, 0x57, 0xec, 0x9c, 0x25, 0xa2, 0x06, 0x7a, + 0x92, 0xa1, 0x9c, 0x9a, 0x95, 0x92, 0x19, 0xeb, 0x00, 0x0a, 0x40, + 0x90, 0x90, 0xb1, 0x07, 0xe9, 0x03, 0x0c, 0x1a, 0xa9, 0x71, 0xf8, + 0x71, 0xad, 0x06, 0xb2, 0x16, 0x4b, 0x0c, 0x1c, 0x41, 0x14, 0x70, + 0x0c, 0x1d, 0x82, 0x24, 0xb8, 0xed, 0x06, 0xe0, 0x08, 0x00, 0x98, + 0x61, 0x90, 0xa8, 0x41, 0x92, 0x46, 0x04, 0xa2, 0x46, 0x05, 0xa0, + 0xa8, 0x41, 0xa2, 0x46, 0x06, 0x8e, 0x05, 0x06, 0x07, 0xc6, 0x9e, + 0xff, 0xbd, 0x04, 0x81, 0x15, 0x70, 0xa8, 0x91, 0x82, 0x28, 0x17, + 0x0c, 0xbc, 0xe0, 0x08, 0x00, 0x46, 0x9a, 0xff, 0x92, 0xcc, 0xf8, + 0x56, 0x49, 0x09, 0x16, 0x12, 0x09, 0xa2, 0xa0, 0xf0, 0xa0, 0xad, + 0x10, 0xa0, 0xa6, 0x04, 0x56, 0x5a, 0x08, 0x81, 0x17, 0x70, 0x88, + 0x08, 0xad, 0x05, 0x88, 0xc8, 0xb2, 0x06, 0x7a, 0xe0, 0x08, 0x00, + 0x16, 0xda, 0x0d, 0x81, 0x17, 0x70, 0xad, 0x04, 0x88, 0x08, 0xb2, + 0xc1, 0x12, 0x88, 0x68, 0xc2, 0xc1, 0x10, 0xe0, 0x08, 0x00, 0x81, + 0x18, 0x8e, 0x04, 0x24, 0x02, 0x88, 0x08, 0xb2, 0x11, 0x09, 0xe0, + 0x08, 0x00, 0xa0, 0x90, 0x04, 0x16, 0x69, 0x0f, 0xd2, 0x12, 0x00, + 0xc2, 0x11, 0x09, 0xe1, 0x19, 0x70, 0xd0, 0xcc, 0xc0, 0xc0, 0xc0, + 0xb4, 0xb6, 0xdc, 0x07, 0xc0, 0x20, 0x00, 0xe8, 0x0e, 0xc0, 0x20, + 0x00, 0xb2, 0xa0, 0x00, 0xa2, 0xa0, 0x01, 0x65, 0x6a, 0x0b, 0xc2, + 0xa0, 0x01, 0xf8, 0x22, 0x88, 0x32, 0xa0, 0xff, 0x10, 0xb0, 0x88, + 0x10, 0x80, 0x8c, 0x93, 0xf0, 0xfc, 0x93, 0x80, 0xff, 0x20, 0xac, + 0x2f, 0x8e, 0x0f, 0x81, 0x1b, 0x06, 0x10, 0x00, 0x8e, 0x0f, 0x12, + 0xc6, 0xbc, 0xff, 0x7d, 0x0c, 0x98, 0x72, 0x99, 0x54, 0xcc, 0x39, + 0xc2, 0xc4, 0x14, 0xc9, 0x82, 0x49, 0x72, 0x92, 0x02, 0x1a, 0xf8, + 0x52, 0xd8, 0x42, 0x7c, 0xfe, 0x7c, 0xf8, 0x80, 0x8b, 0x30, 0xe0, + 0xea, 0x30, 0xe0, 0xdd, 0x10, 0x80, 0xff, 0x10, 0x1b, 0x99, 0x92, + 0x42, 0x1a, 0xf9, 0x52, 0xd9, 0x42, 0xa2, 0x25, 0x8c, 0x16, 0xea, + 0xeb, 0x82, 0x0a, 0x01, 0x82, 0xc8, 0xfe, 0x56, 0x58, 0xeb, 0x16, + 0x25, 0xeb, 0x92, 0x25, 0xe0, 0x90, 0x91, 0x04, 0x16, 0x99, 0xea, + 0x81, 0x16, 0x70, 0x82, 0x28, 0x11, 0xbd, 0x05, 0x8e, 0x04, 0x58, + 0xa6, 0xff, 0x8e, 0x04, 0x56, 0x82, 0x02, 0x1a, 0x0c, 0x17, 0x1b, + 0x88, 0xcc, 0x39, 0xa2, 0xc4, 0x14, 0xa9, 0x82, 0x49, 0x72, 0x82, + 0x42, 0x1a, 0x8e, 0x05, 0x40, 0xe7, 0x92, 0x0a, 0x01, 0x92, 0xc9, + 0xfe, 0x56, 0x59, 0xe7, 0x16, 0x25, 0xe7, 0xb2, 0x25, 0xe0, 0xb0, + 0xb1, 0x04, 0x16, 0x9b, 0xe6, 0x8e, 0x0c, 0x40, 0x96, 0x8e, 0x11, + 0x82, 0x57, 0x92, 0xff, 0x0c, 0x79, 0xb8, 0xa1, 0xa8, 0x61, 0xb2, + 0xcb, 0xfc, 0xb0, 0xa9, 0x83, 0x86, 0x01, 0x00, 0x0c, 0x5a, 0x46, + 0x00, 0x00, 0x0c, 0x6a, 0x8e, 0x05, 0x82, 0x58, 0x88, 0x88, 0xbd, + 0x01, 0xe0, 0x08, 0x00, 0x98, 0xa1, 0x26, 0x39, 0x13, 0x66, 0x29, + 0x04, 0x0c, 0x4a, 0x46, 0x03, 0x00, 0x0b, 0xc9, 0x0c, 0x2b, 0x0c, + 0x3a, 0xc0, 0xab, 0x83, 0x8e, 0x04, 0x27, 0x7a, 0x8e, 0x0c, 0x27, + 0xb8, 0x01, 0x16, 0x7b, 0xca, 0x98, 0x5b, 0x99, 0x01, 0xcc, 0x09, + 0x39, 0x11, 0x8e, 0x0d, 0x66, 0x86, 0xf8, 0xff, 0x8e, 0x05, 0x87, + 0x3c, 0xcd, 0x04, 0x81, 0x1a, 0x70, 0xbd, 0x03, 0x88, 0x08, 0xad, + 0x02, 0x8e, 0x06, 0x89, 0x1e, 0x36, 0x41, 0x00, 0x81, 0x1a, 0x70, + 0x88, 0x18, 0x8e, 0x0b, 0x10, 0x31, 0x12, 0x70, 0x82, 0x23, 0x00, + 0x82, 0x28, 0x68, 0x82, 0x28, 0x06, 0x16, 0x68, 0x04, 0x20, 0xa2, + 0x20, 0xa5, 0x2f, 0x09, 0xc8, 0x03, 0xc2, 0x2c, 0x68, 0xa8, 0x4c, + 0xb8, 0x5c, 0xcc, 0x3a, 0x0c, 0x0b, 0x86, 0x00, 0x00, 0xb2, 0xcb, + 0xec, 0x9c, 0xba, 0x2c, 0x0c, 0xa7, 0x1b, 0x0d, 0xe8, 0x4a, 0xe2, + 0x2e, 0x26, 0xd2, 0x0e, 0x01, 0xc0, 0xdd, 0x20, 0xd2, 0x4e, 0x01, + 0xa8, 0x5a, 0x56, 0x9a, 0xfe, 0x8e, 0x05, 0x2d, 0x81, 0x14, 0x70, + 0x82, 0x28, 0xb6, 0xa8, 0x0c, 0xe0, 0x08, 0x00, 0x0c, 0x12, 0x1d, + 0xf0, 0x0c, 0x02, 0x8e, 0x08, 0x88, 0x40, 0x8e, 0x06, 0x81, 0x02, + 0x28, 0x8e, 0x05, 0x72, 0x2d, 0x0a, 0x8e, 0x07, 0x81, 0x04, 0x03, + 0x70, 0x91, 0x1b, 0x70, 0x82, 0x28, 0xb3, 0x92, 0x29, 0x00, 0xb7, + 0x78, 0x1d, 0x16, 0xa9, 0x01, 0x81, 0x1c, 0x70, 0x82, 0x28, 0x0e, + 0xe0, 0x08, 0x00, 0x8c, 0xea, 0x91, 0x10, 0x70, 0x98, 0x09, 0x92, + 0x09, 0x54, 0x66, 0x29, 0x04, 0x0c, 0x3a, 0xa5, 0x2c, 0x02, 0x8e, + 0x06, 0x41, 0x38, 0x8e, 0x09, 0x41, 0x36, 0x41, 0x00, 0x16, 0x85, + 0x04, 0x71, 0x12, 0x70, 0x98, 0x07, 0x82, 0x09, 0x14, 0x0c, 0x06, + 0xf6, 0x28, 0x22, 0xb2, 0x29, 0x12, 0x00, 0x06, 0x40, 0xb0, 0xb0, + 0xb1, 0x07, 0x6b, 0x0e, 0x81, 0x16, 0x70, 0xad, 0x02, 0x82, 0x28, + 0x16, 0xbd, 0x8e, 0x05, 0x85, 0x6f, 0x07, 0x1b, 0x66, 0x60, 0x60, + 0x74, 0x66, 0x96, 0xdc, 0x88, 0x39, 0xdc, 0x38, 0x81, 0x1d, 0x8e, + 0x06, 0x85, 0x18, 0x68, 0x0c, 0x3b, 0xe0, 0x08, 0x00, 0x68, 0x07, + 0x68, 0x36, 0x16, 0x16, 0x04, 0xdd, 0x05, 0x8e, 0x08, 0x82, 0x26, + 0x48, 0x8e, 0x05, 0x63, 0x6d, 0x0a, 0xac, 0xca, 0x81, 0x00, 0x70, + 0xc2, 0xa0, 0xd8, 0xa8, 0x4a, 0xb2, 0xa0, 0x89, 0xca, 0xca, 0xc2, + 0x4a, 0x98, 0xc0, 0xc8, 0x41, 0xc2, 0x4a, 0x99, 0x8e, 0x05, 0x06, + 0x9a, 0x8e, 0x05, 0x06, 0x9b, 0x0c, 0x3c, 0xba, 0xaa, 0x82, 0x28, + 0x3e, 0x0c, 0x0b, 0xe0, 0x08, 0x00, 0x2d, 0x06, 0x8e, 0x08, 0x81, + 0x6c, 0x82, 0x02, 0x03, 0x68, 0x45, 0x66, 0x38, 0x4b, 0x16, 0x83, + 0x04, 0x92, 0x26, 0x1f, 0x37, 0x69, 0x18, 0x71, 0x16, 0x70, 0xad, + 0x03, 0x82, 0x27, 0x3b, 0xbd, 0x04, 0xe0, 0x08, 0x00, 0xcc, 0x8a, + 0x8e, 0x04, 0x0c, 0x39, 0x8e, 0x05, 0x0c, 0x92, 0x22, 0xb7, 0x0c, + 0xcb, 0xac, 0x19, 0xa2, 0x26, 0x26, 0xa2, 0x0a, 0x00, 0xb0, 0xca, + 0x10, 0x26, 0x4c, 0x16, 0xb7, 0x0a, 0x05, 0xb2, 0x26, 0x1f, 0x27, + 0x6b, 0x11, 0xc2, 0xa0, 0xf0, 0xc0, 0xaa, 0x10, 0x26, 0xea, 0x04, + 0x5c, 0x0d, 0xd7, 0x9a, 0x03, 0x0c, 0x02, 0x1d, 0xf0, 0x8e, 0x0a, + 0x81, 0x24, 0x58, 0x8e, 0x0d, 0x82, 0x48, 0x8e, 0x08, 0x16, 0x68, + 0x8e, 0x09, 0x83, 0x50, 0x61, 0x00, 0xad, 0x03, 0x39, 0x01, 0x0c, + 0x0b, 0x81, 0x00, 0x70, 0x40, 0xc4, 0xb0, 0x82, 0x28, 0x3e, 0xe0, + 0xcc, 0x11, 0xe0, 0x08, 0x00, 0x0c, 0x06, 0x0c, 0x05, 0x32, 0xa1, + 0x40, 0xb2, 0x02, 0x0c, 0x72, 0xa3, 0x88, 0xb7, 0x35, 0x02, 0x46, + 0x46, 0x00, 0x16, 0x64, 0x11, 0xd2, 0x22, 0x1a, 0x6a, 0xdd, 0x3a, + 0xed, 0xf2, 0x2e, 0x3c, 0x20, 0xff, 0xc0, 0x56, 0xff, 0x0f, 0xf8, + 0x2d, 0xf2, 0xcf, 0xfd, 0x56, 0x7f, 0x0f, 0x82, 0x22, 0x19, 0xd7, + 0x98, 0x07, 0x92, 0x02, 0x01, 0x0b, 0x99, 0x56, 0x99, 0x0e, 0x0c, + 0x0b, 0x1c, 0x8c, 0xa2, 0xa2, 0x40, 0xf8, 0x01, 0x92, 0x2d, 0x90, + 0x92, 0x4f, 0x00, 0x90, 0x98, 0x41, 0xaa, 0xad, 0x92, 0x4f, 0x01, + 0x90, 0x98, 0x41, 0x92, 0x4f, 0x02, 0x8e, 0x05, 0x06, 0x03, 0x82, + 0x2d, 0x91, 0x82, 0x4f, 0x04, 0x80, 0x88, 0x41, 0x82, 0x4f, 0x05, + 0x8e, 0x05, 0x06, 0x06, 0x8e, 0x05, 0x06, 0x07, 0x92, 0x2d, 0x92, + 0x92, 0x4f, 0x08, 0x92, 0x4f, 0x0c, 0x8e, 0x05, 0x27, 0x09, 0x8d, + 0x09, 0x82, 0x4f, 0x0d, 0x8e, 0x05, 0x0b, 0x0a, 0x8e, 0x04, 0x0b, + 0x0e, 0x8e, 0x05, 0x0b, 0x0b, 0x92, 0x4f, 0x0f, 0x82, 0x2d, 0x93, + 0x82, 0x4f, 0x10, 0x8e, 0x05, 0x34, 0x11, 0x8e, 0x05, 0x06, 0x12, + 0x8e, 0x05, 0x06, 0x13, 0x92, 0x2d, 0x94, 0x92, 0x4f, 0x14, 0x8e, + 0x05, 0x27, 0x15, 0x8e, 0x05, 0x06, 0x16, 0x8e, 0x05, 0x06, 0x17, + 0x82, 0x2d, 0x95, 0x82, 0x4f, 0x18, 0x82, 0x4f, 0x1c, 0x8e, 0x05, + 0x27, 0x19, 0x9d, 0x08, 0x92, 0x4f, 0x1d, 0x8e, 0x05, 0x0b, 0x1a, + 0x8e, 0x04, 0x0b, 0x1e, 0x8e, 0x05, 0x0b, 0x1b, 0x82, 0x4f, 0x1f, + 0x81, 0x00, 0x70, 0xe2, 0x0e, 0x80, 0xe2, 0x4f, 0x20, 0x92, 0x1d, + 0x1e, 0x92, 0x4f, 0x21, 0x98, 0xdd, 0x82, 0x28, 0x3e, 0x90, 0x9a, + 0x41, 0x92, 0x4f, 0x22, 0x8e, 0x05, 0x48, 0x23, 0x8e, 0x05, 0x86, + 0x45, 0x0b, 0x44, 0xb2, 0xcb, 0x24, 0xb9, 0x01, 0xb2, 0x02, 0x0c, + 0x1b, 0x55, 0x7a, 0x66, 0xc6, 0xb7, 0xff, 0x8e, 0x06, 0x82, 0x5c, + 0x9c, 0x42, 0xa8, 0x42, 0x9c, 0x0a, 0x0c, 0x0b, 0x0c, 0xcc, 0x81, + 0x00, 0x70, 0x92, 0xa0, 0x9c, 0x82, 0x28, 0x3e, 0x9a, 0xaa, 0x8e, + 0x0b, 0x8f, 0x64, 0x16, 0xe2, 0x05, 0xa8, 0x42, 0x16, 0x9a, 0x05, + 0x82, 0x1a, 0x4b, 0xc2, 0xa0, 0xde, 0x16, 0x08, 0x05, 0xb2, 0xa0, + 0x9c, 0x92, 0x0a, 0x9c, 0xba, 0xba, 0xc7, 0x99, 0x45, 0xc2, 0x0a, + 0x9d, 0xd2, 0xa0, 0xad, 0xd7, 0x9c, 0x3c, 0xe2, 0x0a, 0x9e, 0xf2, + 0xa0, 0xbe, 0xf7, 0x9e, 0x33, 0x82, 0x0a, 0x9f, 0x92, 0xa0, 0xef, + 0x97, 0x98, 0x2a, 0xa2, 0x0a, 0xa2, 0xf6, 0x6a, 0x13, 0xc2, 0x1b, + 0x02, 0xac, 0x2c, 0xb2, 0x1b, 0x04, 0x9c, 0xdb, 0xd2, 0xa4, 0x00, + 0xb7, 0x3d, 0x18, 0x0c, 0x12, 0x1d, 0xf0, 0xa2, 0xa0, 0x06, 0xa5, + 0x07, 0x00, 0x20, 0xa2, 0x20, 0x25, 0xf8, 0xff, 0xe2, 0xa0, 0x00, + 0xe9, 0x02, 0x0c, 0x02, 0x1d, 0xf0, 0xf6, 0x6a, 0xe7, 0x25, 0x06, + 0x00, 0xc6, 0xf9, 0x8e, 0x06, 0x87, 0x50, 0xb1, 0x1e, 0x70, 0xb0, + 0x32, 0xa0, 0xa8, 0x03, 0x16, 0x8a, 0x04, 0x58, 0x4a, 0x42, 0xcb, + 0x20, 0x61, 0x00, 0x70, 0x20, 0x22, 0x90, 0x82, 0x26, 0x6e, 0x40, + 0x22, 0xb0, 0xad, 0x02, 0x42, 0xa0, 0x9c, 0x4a, 0x45, 0xe0, 0x08, + 0x00, 0x82, 0x26, 0x6f, 0x8e, 0x05, 0x84, 0x10, 0x92, 0x04, 0x07, + 0x77, 0xe9, 0x12, 0xa8, 0x03, 0x25, 0xf3, 0xff, 0x81, 0x15, 0x70, + 0xb2, 0x05, 0x74, 0x82, 0x28, 0x22, 0xa8, 0x03, 0xe0, 0x08, 0x00, + 0xa8, 0x03, 0xe5, 0xf1, 0xff, 0xa8, 0x03, 0x0c, 0x09, 0x99, 0x0a, + 0x99, 0x03, 0x8e, 0x08, 0x81, 0x50, 0x20, 0x20, 0x74, 0xb6, 0x62, + 0x10, 0x0c, 0x02, 0xad, 0x02, 0x65, 0xf9, 0xff, 0x1b, 0x22, 0x20, + 0x20, 0x74, 0x66, 0x62, 0xf2, 0x1d, 0xf0, 0xad, 0x02, 0x65, 0xf8, + 0xff, 0x8e, 0x08, 0x24, 0x5d, 0x02, 0xcc, 0x32, 0x8e, 0x05, 0x87, + 0x5a, 0xa6, 0x13, 0x10, 0x0c, 0x02, 0x76, 0x93, 0x09, 0x42, 0x05, + 0x00, 0x1b, 0x55, 0x2a, 0x24, 0x20, 0x20, 0xf4, 0x8e, 0x06, 0x87, + 0x73, 0x36, 0x61, 0x00, 0x16, 0xf3, 0x08, 0x48, 0x03, 0x51, 0x1e, + 0x70, 0x49, 0x01, 0x48, 0x43, 0x22, 0xa0, 0x9c, 0x2a, 0x24, 0x92, + 0x04, 0xa3, 0x72, 0x04, 0xa2, 0x62, 0x14, 0x4b, 0x50, 0x57, 0xa0, + 0x39, 0x05, 0x42, 0x24, 0x26, 0x07, 0xe9, 0x27, 0x0c, 0xec, 0xb1, + 0x1f, 0x70, 0x70, 0xa7, 0xf0, 0x81, 0x00, 0x70, 0xb0, 0xaa, 0x90, + 0x82, 0x28, 0x3f, 0x8e, 0x05, 0x86, 0x15, 0xad, 0x04, 0xbd, 0x06, + 0x0c, 0x1c, 0xc2, 0x42, 0x07, 0x65, 0xf9, 0xff, 0x92, 0x02, 0x07, + 0xa2, 0x52, 0x05, 0x77, 0xe9, 0x42, 0xa8, 0x05, 0x25, 0xe9, 0xff, + 0xbc, 0xca, 0xbd, 0x06, 0xad, 0x04, 0xe5, 0xf7, 0xff, 0xd2, 0x12, + 0x05, 0xe8, 0x01, 0xa7, 0x9d, 0x2e, 0x8c, 0x2e, 0x0c, 0x0f, 0xf9, + 0x03, 0xa1, 0x20, 0x70, 0xc2, 0xa0, 0x80, 0xb2, 0x02, 0x07, 0x81, + 0x15, 0x70, 0xc0, 0xbb, 0x20, 0x0c, 0x0c, 0xb2, 0x42, 0x07, 0xb8, + 0x05, 0x82, 0x28, 0x42, 0xa8, 0x0a, 0xe0, 0x08, 0x00, 0x8c, 0x5a, + 0x0c, 0x1d, 0xd2, 0x42, 0x07, 0x1d, 0xf0, 0x1d, 0xf0, 0xb1, 0x21, + 0x70, 0x81, 0x00, 0x70, 0x70, 0xa7, 0x90, 0x82, 0x28, 0x6e, 0xb0, + 0xaa, 0xb0, 0x8e, 0x05, 0x83, 0x47, 0x36, 0x41, 0x00, 0xad, 0x03, + 0x65, 0xe3, 0xff, 0x16, 0xba, 0x04, 0x58, 0x43, 0x82, 0xa0, 0x9c, + 0x8a, 0x55, 0x82, 0x05, 0x07, 0xfc, 0xe8, 0x41, 0x1e, 0x70, 0x22, + 0x05, 0x06, 0x40, 0x42, 0xa0, 0x98, 0x04, 0x8c, 0x39, 0xad, 0x02, + 0x65, 0xee, 0xff, 0xb1, 0x22, 0x70, 0x81, 0x21, 0x70, 0x20, 0x22, + 0x90, 0x39, 0x04, 0xcd, 0x03, 0x31, 0x00, 0x70, 0x80, 0x22, 0xb0, + 0x82, 0x23, 0x70, 0x8e, 0x05, 0x82, 0x63, 0xad, 0x02, 0xc2, 0x15, + 0x02, 0x0c, 0x0b, 0x82, 0x23, 0x6d, 0x0c, 0x1d, 0xe0, 0x08, 0x00, + 0x0c, 0x02, 0x1d, 0xf0, 0x8e, 0x08, 0x87, 0x14, 0x78, 0x8e, 0x0a, + 0x87, 0x2a, 0x00, 0x00, 0x36, 0x81, 0x00, 0xad, 0x01, 0x0c, 0x0b, + 0x41, 0x00, 0x70, 0x0c, 0xcc, 0x82, 0x24, 0x3e, 0x39, 0x31, 0xe0, + 0x08, 0x00, 0x19, 0x11, 0x6d, 0x02, 0x0c, 0x05, 0x59, 0x01, 0x4d, + 0x02, 0x28, 0x02, 0x16, 0x64, 0x0b, 0xad, 0x04, 0xa5, 0xda, 0xff, + 0x16, 0xea, 0x0a, 0x0c, 0xee, 0xd8, 0x44, 0x32, 0xa0, 0x9c, 0x72, + 0x0d, 0xa2, 0xc2, 0x1d, 0x52, 0xa2, 0x1d, 0x4b, 0x3a, 0x3d, 0xc0, + 0xca, 0xc0, 0x80, 0xcc, 0x23, 0xe7, 0x9c, 0x6c, 0x70, 0xb7, 0xf0, + 0xf2, 0x2d, 0x26, 0xc0, 0x8a, 0xc0, 0xe2, 0xcd, 0x74, 0xe9, 0x41, + 0x82, 0x4d, 0x96, 0xfa, 0xfc, 0x80, 0x88, 0x41, 0xc1, 0x1f, 0x70, + 0x82, 0x4d, 0x97, 0xf2, 0x4d, 0x98, 0x81, 0x00, 0x70, 0xf0, 0xf8, + 0x41, 0xc0, 0xbb, 0x90, 0xf2, 0x4d, 0x99, 0x0c, 0xec, 0xf0, 0xf8, + 0x41, 0xf2, 0x4d, 0x9a, 0x8e, 0x05, 0x06, 0x9b, 0x82, 0x28, 0x3f, + 0xa2, 0x2d, 0x26, 0xe0, 0x08, 0x00, 0xb8, 0x44, 0xa2, 0x2b, 0x26, + 0xb2, 0x1b, 0x4b, 0x25, 0xe3, 0xff, 0xc2, 0x13, 0x05, 0xa7, 0x1c, + 0x04, 0xad, 0x07, 0x65, 0xe0, 0xff, 0x0c, 0x1d, 0xf8, 0x41, 0x0c, + 0x0e, 0xe2, 0x4f, 0x07, 0xe2, 0x5f, 0x01, 0xe2, 0x4f, 0x05, 0xd2, + 0x43, 0x07, 0x06, 0x01, 0x00, 0xad, 0x07, 0xa5, 0xde, 0xff, 0x67, + 0x94, 0x0a, 0x68, 0x04, 0xac, 0xa6, 0x0c, 0x08, 0x89, 0x04, 0x46, + 0x05, 0x00, 0xcc, 0x95, 0xad, 0x06, 0x65, 0xd0, 0xff, 0xa0, 0x56, + 0x83, 0x16, 0xb5, 0xfe, 0x98, 0x04, 0x99, 0x05, 0x06, 0xf9, 0xff, + 0x5d, 0x04, 0x56, 0xc2, 0xf3, 0x81, 0x1a, 0x70, 0xad, 0x06, 0x88, + 0x88, 0xb8, 0x31, 0x8e, 0x0b, 0x86, 0x20, 0xe1, 0x27, 0x70, 0x91, + 0x23, 0x70, 0x51, 0x24, 0x70, 0x71, 0x25, 0x70, 0x61, 0x26, 0x70, + 0x38, 0x07, 0x48, 0x06, 0x52, 0x05, 0x00, 0x88, 0x09, 0x0b, 0x25, + 0x82, 0xc8, 0xed, 0x89, 0x09, 0x16, 0x32, 0x0b, 0x82, 0xc5, 0xfe, + 0x16, 0x88, 0x0b, 0x2b, 0x33, 0xa2, 0xc5, 0xfd, 0x92, 0xc4, 0xe9, + 0x42, 0xc4, 0xe3, 0xa0, 0x49, 0x83, 0x49, 0x06, 0x81, 0x03, 0x70, + 0x39, 0x07, 0x52, 0x28, 0xb3, 0x91, 0x29, 0x70, 0x57, 0xe5, 0x08, + 0x32, 0xc3, 0xfe, 0x0b, 0x44, 0x49, 0x06, 0x39, 0x07, 0xb2, 0x28, + 0x84, 0x67, 0xeb, 0x07, 0x0b, 0xc3, 0x0b, 0xd4, 0xd9, 0x06, 0xc9, + 0x07, 0x41, 0x1a, 0x70, 0x61, 0x15, 0x70, 0x31, 0x2b, 0x70, 0x72, + 0xd6, 0xff, 0xa2, 0x26, 0x22, 0xb2, 0x26, 0x42, 0xc2, 0x26, 0x1f, + 0xf2, 0x26, 0x26, 0xd8, 0x66, 0x39, 0x66, 0xd9, 0x54, 0xf9, 0x04, + 0xe2, 0x66, 0x26, 0xc9, 0x64, 0xb9, 0x74, 0xa9, 0x84, 0xb1, 0x32, + 0x70, 0xa1, 0x30, 0x70, 0xc1, 0x31, 0x70, 0xe2, 0x26, 0x18, 0xf8, + 0x06, 0xd1, 0x2f, 0x70, 0xd2, 0x67, 0x62, 0x99, 0x06, 0xf9, 0x14, + 0xe9, 0x44, 0xc9, 0x0a, 0xe1, 0x2e, 0x70, 0xf1, 0x2d, 0x70, 0x91, + 0x33, 0x70, 0x99, 0x2a, 0xf2, 0x67, 0x71, 0xe2, 0x67, 0x70, 0xb7, + 0x75, 0x0a, 0x21, 0x28, 0x70, 0x82, 0x26, 0x15, 0x89, 0x34, 0x22, + 0x66, 0x15, 0x22, 0x26, 0x41, 0x81, 0x2a, 0x70, 0x82, 0x66, 0x18, + 0x29, 0x24, 0xb9, 0x1a, 0x21, 0x2c, 0x70, 0x22, 0x66, 0x1f, 0x1d, + 0xf0, 0x32, 0xc3, 0xfb, 0x42, 0xc4, 0xef, 0x49, 0x06, 0x86, 0xd5, + 0xff, 0x2b, 0x33, 0x42, 0xc4, 0xe9, 0x49, 0x06, 0x06, 0xd3, 0x8e, + 0x06, 0x87, 0x1c, 0xad, 0x02, 0xe5, 0x81, 0x00, 0xad, 0x02, 0x25, + 0xfe, 0x04, 0xad, 0x02, 0xa5, 0x1a, 0x05, 0xad, 0x02, 0xe5, 0xd8, + 0x07, 0xad, 0x02, 0x25, 0xc8, 0x0a, 0xad, 0x02, 0x25, 0xb9, 0x08, + 0xad, 0x02, 0x25, 0x61, 0x0a, 0xad, 0x02, 0x65, 0x8f, 0x8e, 0x07, + 0x84, 0x10, 0x8e, 0x04, 0x30, 0x65, 0xc6, 0x04, 0x8e, 0x04, 0x0d, + 0x36, 0x41, 0x8e, 0x09, 0x82, 0x34, 0x8e, 0x08, 0x08, 0x8e, 0x08, + 0x08, 0xcd, 0x06, 0xad, 0x03, 0x81, 0x20, 0x70, 0x40, 0x20, 0x34, + 0x80, 0x22, 0xa0, 0x81, 0x34, 0x70, 0xbd, 0x05, 0x88, 0x78, 0x28, + 0x02, 0xe0, 0x08, 0x00, 0x91, 0x35, 0x70, 0xa1, 0x36, 0x70, 0x97, + 0x13, 0x0b, 0xa7, 0x93, 0x0f, 0xad, 0x02, 0xbd, 0x05, 0x25, 0xfc, + 0xff, 0x1d, 0xf0, 0xbd, 0x05, 0xad, 0x02, 0x25, 0xfb, 0x8e, 0x05, + 0x87, 0x2d, 0x8e, 0x06, 0x8c, 0x1c, 0x37, 0x8e, 0x11, 0x8f, 0x7c, + 0x38, 0x70, 0x88, 0x08, 0x91, 0x39, 0x70, 0x9c, 0x18, 0x97, 0x13, + 0x0f, 0xa1, 0x3a, 0x70, 0xb1, 0x3b, 0x70, 0xa7, 0x13, 0x06, 0xb7, + 0x13, 0x03, 0x0c, 0x12, 0x1d, 0xf0, 0xed, 0x06, 0x8e, 0x05, 0x8c, + 0x67, 0x8e, 0x05, 0x35, 0x8e, 0x06, 0x90, 0x21, 0x8e, 0x06, 0x85, + 0x3d, 0x8e, 0x04, 0x38, 0x00, 0x70, 0xbd, 0x03, 0x82, 0x28, 0xe2, + 0xa2, 0x02, 0x00, 0xe0, 0x08, 0x00, 0x81, 0x3c, 0x70, 0x88, 0x88, + 0x8e, 0x08, 0x56, 0x00, 0x00, 0x36, 0x61, 0x00, 0xd1, 0x24, 0x70, + 0x0c, 0x0a, 0xb2, 0x0d, 0x00, 0xe2, 0xa0, 0x64, 0xbc, 0x2b, 0xc1, + 0x20, 0x70, 0xc0, 0x9a, 0xa0, 0x98, 0x09, 0x1b, 0xaa, 0x82, 0x09, + 0x03, 0xa0, 0xa0, 0x74, 0x66, 0x38, 0x1d, 0xb2, 0x29, 0x11, 0xe0, + 0xf8, 0x41, 0x17, 0x6b, 0x11, 0xf2, 0x43, 0x0d, 0xe2, 0x43, 0x0c, + 0xf0, 0xf8, 0x41, 0xf2, 0x43, 0x0e, 0x8e, 0x05, 0x06, 0x0f, 0xb2, + 0x0d, 0x00, 0xb7, 0x3a, 0xce, 0xad, 0x02, 0x61, 0x3c, 0x70, 0x0c, + 0x0b, 0x82, 0x26, 0x2e, 0xcd, 0x8e, 0x05, 0x92, 0x01, 0x01, 0xc2, + 0x03, 0x10, 0x0c, 0xfb, 0x81, 0x3d, 0x70, 0x0c, 0xca, 0x88, 0xc8, + 0xc0, 0xab, 0x83, 0x0c, 0x0b, 0x4b, 0xc1, 0xa0, 0x99, 0x20, 0x99, + 0x01, 0x98, 0x23, 0x3c, 0x2a, 0x90, 0xa9, 0x93, 0xa9, 0x11, 0x8e, + 0x05, 0x81, 0x00, 0x98, 0x03, 0xa8, 0x01, 0x8c, 0x69, 0xb2, 0xa1, + 0x00, 0xb0, 0xaa, 0x20, 0xa9, 0x01, 0xa8, 0x33, 0x1c, 0xeb, 0x9c, + 0x0a, 0xc8, 0x01, 0xd2, 0xa2, 0x00, 0xd0, 0xcc, 0x20, 0xc9, 0x01, + 0xb7, 0xba, 0x01, 0xad, 0x0b, 0x86, 0x00, 0x00, 0xa2, 0xa0, 0x64, + 0x0c, 0x7b, 0x8b, 0xc1, 0xa9, 0x21, 0x82, 0x26, 0x2d, 0x8e, 0x06, + 0x8a, 0x19, 0x03, 0x11, 0xac, 0x39, 0x82, 0x26, 0x25, 0x8e, 0x08, + 0x0d, 0x4d, 0x03, 0x9c, 0x49, 0x0c, 0x05, 0xad, 0x02, 0x88, 0x76, + 0xb2, 0x14, 0x09, 0xe0, 0x08, 0x00, 0x2b, 0x44, 0x92, 0x03, 0x11, + 0x1b, 0x55, 0x97, 0x35, 0xeb, 0xd1, 0x3f, 0x70, 0xed, 0x02, 0x88, + 0x36, 0x0c, 0x49, 0xb1, 0x3e, 0x70, 0xa8, 0x01, 0xc2, 0xa3, 0x00, + 0xb0, 0xba, 0x20, 0xc0, 0xaa, 0x10, 0x0c, 0x3c, 0xa0, 0xc9, 0x93, + 0x8e, 0x05, 0x3e, 0xbd, 0x0a, 0x0c, 0xdc, 0xc7, 0x9a, 0x0b, 0x81, + 0x00, 0x70, 0x8e, 0x09, 0x82, 0x1f, 0x8e, 0x08, 0x82, 0x34, 0x8e, + 0x05, 0x82, 0x49, 0x8e, 0x06, 0x91, 0x78, 0x8e, 0x12, 0x83, 0x58, + 0xbd, 0x05, 0x8e, 0x0c, 0x83, 0x58, 0x30, 0xa0, 0xf4, 0x8e, 0x07, + 0x83, 0x59, 0x82, 0xa1, 0x00, 0x61, 0x00, 0x70, 0x92, 0x02, 0x01, + 0xc2, 0x26, 0xd0, 0x66, 0x29, 0x0b, 0x32, 0x02, 0x0c, 0x30, 0x33, + 0xb0, 0xe0, 0x33, 0x11, 0x86, 0x00, 0x00, 0x32, 0xa0, 0xed, 0x37, + 0x38, 0x1a, 0x0c, 0x3a, 0xbd, 0x03, 0xe0, 0x0c, 0x00, 0x4d, 0x0a, + 0xcc, 0x9a, 0x91, 0x19, 0x70, 0xc0, 0x20, 0x00, 0x98, 0x09, 0xc0, + 0x20, 0x00, 0x58, 0x44, 0x86, 0x0a, 0x00, 0x0c, 0x0a, 0xb1, 0x40, + 0x70, 0x8e, 0x05, 0x1c, 0x16, 0x0a, 0x05, 0xcd, 0x03, 0x91, 0x41, + 0x70, 0x51, 0x37, 0x70, 0x0c, 0x08, 0xbd, 0x08, 0x58, 0x35, 0x99, + 0x4a, 0x89, 0x5a, 0xcb, 0x55, 0x59, 0x1a, 0x82, 0x26, 0x3e, 0x8e, + 0x05, 0x99, 0x0e, 0x82, 0x02, 0x01, 0xa1, 0x15, 0x70, 0x66, 0x28, + 0x0f, 0x4b, 0xb5, 0xc2, 0x02, 0x0c, 0x82, 0x2a, 0x30, 0x8e, 0x05, + 0x81, 0x27, 0x46, 0x02, 0x00, 0xbd, 0x05, 0x82, 0x2a, 0x2f, 0x8e, + 0x05, 0x0d, 0xdd, 0x03, 0xad, 0x04, 0xc2, 0x02, 0x00, 0x82, 0x26, + 0xd1, 0xb1, 0x42, 0x8e, 0x0c, 0x9d, 0x5c, 0x8e, 0x05, 0x81, 0x3c, + 0x34, 0x70, 0x30, 0x20, 0xf4, 0x8e, 0x07, 0x89, 0x63, 0x91, 0x43, + 0x70, 0x97, 0x92, 0x30, 0xb1, 0x44, 0x70, 0xa2, 0x05, 0x05, 0x4c, + 0x0c, 0xa7, 0xbc, 0x0a, 0xd1, 0x8e, 0x05, 0x81, 0x18, 0xd8, 0x0d, + 0xc0, 0x20, 0x00, 0x0c, 0x0d, 0xc1, 0x45, 0x70, 0xf2, 0xa0, 0x88, + 0x40, 0xe0, 0x34, 0xf0, 0xee, 0xc1, 0x92, 0x05, 0x04, 0xea, 0xcc, + 0xcc, 0x69, 0xd2, 0x4c, 0x04, 0x86, 0x8e, 0x04, 0x50, 0x66, 0x19, + 0x07, 0x0c, 0x19, 0x92, 0x4c, 0x04, 0x46, 0x05, 0x00, 0x66, 0x29, + 0x04, 0x0c, 0x29, 0x46, 0xfc, 0xff, 0x82, 0xdb, 0x01, 0x82, 0xc8, + 0x31, 0xc0, 0x20, 0x00, 0x88, 0x08, 0xc0, 0x20, 0x00, 0xbd, 0x05, + 0xa2, 0x4c, 0x05, 0x92, 0xa0, 0x40, 0xa2, 0xa0, 0x00, 0x76, 0xa9, + 0x13, 0xe2, 0x05, 0x05, 0x9d, 0x0d, 0xe7, 0xaa, 0x0d, 0x92, 0x1b, + 0x03, 0x1b, 0xaa, 0x92, 0x5c, 0x03, 0x2b, 0xbb, 0x2b, 0xcc, 0x1d, + 0xf0, 0x46, 0xfc, 0xff, 0x8e, 0x20, 0x86, 0x2c, 0x46, 0x70, 0x97, + 0x93, 0x17, 0x81, 0x48, 0x70, 0xa1, 0x47, 0x70, 0x92, 0x05, 0x00, + 0x0c, 0x1b, 0x90, 0x9b, 0x83, 0x92, 0x4a, 0x00, 0x88, 0xa8, 0x8e, + 0x07, 0x83, 0x1b, 0x36, 0x41, 0x00, 0x21, 0x00, 0x70, 0x82, 0x22, + 0xcf, 0xa1, 0x49, 0x70, 0xe0, 0x08, 0x00, 0x8e, 0x04, 0x09, 0x4a, + 0x8e, 0x08, 0x09, 0x4b, 0x8e, 0x07, 0x81, 0x7a, 0x36, 0x41, 0x00, + 0xa2, 0xa0, 0x01, 0xb2, 0xa0, 0x02, 0x65, 0x20, 0x0a, 0x70, 0xf7, + 0x20, 0x8e, 0x0c, 0x86, 0x2b, 0x8e, 0x06, 0x94, 0x36, 0x8e, 0x0c, + 0x24, 0x25, 0x1e, 0x0a, 0x60, 0xe6, 0x20, 0x8e, 0x0a, 0x22, 0x8e, + 0x06, 0x93, 0x34, 0x8e, 0x08, 0x82, 0x40, 0xa2, 0x03, 0x08, 0x82, + 0x03, 0x00, 0x66, 0x1a, 0x12, 0x92, 0xa0, 0xff, 0x97, 0x98, 0x0c, + 0x81, 0x4c, 0x70, 0x88, 0xb8, 0x8e, 0x05, 0x21, 0xa2, 0x03, 0x08, + 0x66, 0x5a, 0x0f, 0xb1, 0x4d, 0x70, 0xa2, 0x02, 0x00, 0x92, 0x03, + 0x00, 0xba, 0xaa, 0x92, 0x4a, 0x00, 0x1d, 0xf0, 0x8e, 0x06, 0x43, + 0x8e, 0x08, 0x93, 0x61, 0x8e, 0x05, 0x88, 0x38, 0xbd, 0x03, 0xc1, + 0x4e, 0x70, 0x92, 0x03, 0x0b, 0x81, 0x37, 0x70, 0x90, 0x97, 0x04, + 0x88, 0x78, 0x99, 0x0c, 0x8e, 0x08, 0x1c, 0x0c, 0x0a, 0x1c, 0x0c, + 0x41, 0x00, 0x70, 0xd1, 0x4f, 0x70, 0x61, 0x50, 0x70, 0x21, 0x37, + 0x70, 0x91, 0x51, 0x70, 0x31, 0x52, 0x70, 0xb1, 0x53, 0x70, 0xe1, + 0x34, 0x70, 0xb8, 0x0b, 0xf8, 0x5e, 0x58, 0x4e, 0x88, 0xc3, 0x99, + 0xc3, 0x89, 0x02, 0x69, 0x4e, 0x59, 0x12, 0xf9, 0x22, 0xd9, 0x5e, + 0xb2, 0x0b, 0x0c, 0x88, 0x44, 0xb0, 0xbb, 0xb0, 0xc0, 0xbb, 0xa0, + 0xe0, 0x08, 0x00, 0xe1, 0x58, 0x70, 0xf1, 0x56, 0x70, 0x81, 0x55, + 0x70, 0xa9, 0x32, 0xd2, 0x24, 0xe6, 0xc2, 0x24, 0xe7, 0x91, 0x54, + 0x70, 0xb2, 0x23, 0x17, 0xb9, 0x62, 0x99, 0x03, 0xc9, 0x42, 0xd9, + 0x52, 0xa8, 0x23, 0xa9, 0x72, 0x82, 0x63, 0x17, 0xf9, 0x23, 0xd1, + 0x57, 0x70, 0xc1, 0x59, 0x70, 0xe2, 0x6d, 0x66, 0xc2, 0x6d, 0x67, + 0x8e, 0x07, 0x88, 0x58, 0x5a, 0x8e, 0x05, 0x96, 0x59, 0xe0, 0x08, + 0x00, 0x8e, 0x05, 0x86, 0x09, 0xcf, 0xa1, 0x5b, 0x70, 0xe0, 0x08, + 0x00, 0x65, 0x50, 0x06, 0x25, 0xea, 0xff, 0xa5, 0x7b, 0x03, 0xa5, + 0x22, 0x8e, 0x07, 0x95, 0x30, 0x8c, 0x82, 0x82, 0xa1, 0x00, 0x37, + 0xa8, 0x8e, 0x05, 0x95, 0x54, 0x81, 0x5a, 0x8e, 0x0f, 0x88, 0x69, + 0x36, 0x41, 0x00, 0x31, 0x5c, 0x70, 0x38, 0x03, 0x30, 0x32, 0xb0, + 0x58, 0x63, 0x28, 0x73, 0x57, 0xa2, 0x04, 0x20, 0x25, 0xc0, 0x8e, + 0x07, 0x98, 0x4a, 0x36, 0x41, 0x00, 0x41, 0x5d, 0x70, 0x21, 0x5e, + 0x70, 0x31, 0x00, 0x70, 0x91, 0x5a, 0x70, 0xa2, 0x23, 0xce, 0x82, + 0x23, 0xd0, 0x89, 0x19, 0x22, 0x63, 0xd0, 0xa9, 0x09, 0x42, 0x63, + 0xce, 0x8e, 0x08, 0x89, 0x08, 0x61, 0x00, 0x70, 0x51, 0x53, 0x70, + 0x71, 0x20, 0x70, 0x49, 0x01, 0x9d, 0x03, 0x41, 0x24, 0x70, 0x31, + 0x10, 0x70, 0x70, 0xb2, 0xa0, 0xb9, 0x11, 0xb8, 0x0b, 0xb9, 0x05, + 0xec, 0xc2, 0xa8, 0x03, 0x99, 0x21, 0xdc, 0x9a, 0x0c, 0x0a, 0x88, + 0x46, 0xb2, 0xa0, 0x78, 0xe0, 0x08, 0x00, 0xa9, 0x03, 0x0c, 0x0b, + 0x82, 0x26, 0x3e, 0xc2, 0x8e, 0x05, 0x0d, 0xa8, 0x03, 0x98, 0x21, + 0x92, 0x4a, 0x00, 0xc8, 0x03, 0xb2, 0x04, 0x00, 0xb2, 0x4c, 0x01, + 0xb8, 0x05, 0x99, 0x21, 0xdc, 0x9b, 0x8e, 0x05, 0x2c, 0xa3, 0x38, + 0x8e, 0x04, 0x2c, 0x05, 0x8e, 0x06, 0x2c, 0x8e, 0x05, 0x0d, 0x98, + 0x21, 0xb8, 0x05, 0xf8, 0x11, 0xa8, 0x01, 0xb9, 0x0f, 0x22, 0x4b, + 0x00, 0xe8, 0x05, 0xd8, 0x03, 0x92, 0x4e, 0x01, 0xc8, 0x05, 0xd2, + 0x6e, 0x15, 0xa2, 0x4c, 0x02, 0xc2, 0x04, 0x00, 0xb8, 0x05, 0xb6, + 0x2c, 0x0f, 0x82, 0x0b, 0x02, 0x0b, 0x9c, 0x66, 0x28, 0x07, 0x97, + 0x92, 0x04, 0x0c, 0x5a, 0xa2, 0x4b, 0x02, 0xb8, 0x05, 0xc2, 0x0b, + 0x02, 0x66, 0x5c, 0x0f, 0x8e, 0x04, 0x53, 0x1c, 0x4b, 0xe0, 0x08, + 0x00, 0x91, 0x5f, 0x70, 0xb8, 0x05, 0xa9, 0x09, 0xc1, 0x63, 0x70, + 0x31, 0x61, 0x70, 0x81, 0x62, 0x70, 0x91, 0x60, 0x70, 0xd1, 0x10, + 0x70, 0x0c, 0x0e, 0xe2, 0x4b, 0x03, 0xa8, 0x0d, 0xf8, 0x05, 0x99, + 0x5a, 0xa1, 0x03, 0x70, 0x82, 0x6f, 0x10, 0x38, 0x03, 0x39, 0xff, + 0xe2, 0x4f, 0x0f, 0x32, 0xa2, 0x4c, 0xd8, 0x0d, 0xb8, 0x05, 0xc2, + 0x6d, 0x19, 0xe2, 0x4b, 0x33, 0xb8, 0x05, 0x0c, 0xfc, 0xe2, 0x6b, + 0x11, 0xa2, 0x2a, 0xb3, 0xe2, 0x6b, 0x9b, 0xa0, 0xee, 0x41, 0xa0, + 0xda, 0x41, 0xd7, 0x8c, 0x02, 0x06, 0x20, 0x00, 0xa0, 0xda, 0x34, + 0xd2, 0x4b, 0x0c, 0xe7, 0x0c, 0x09, 0x88, 0x05, 0xa0, 0xfe, 0x34, + 0x3a, 0x88, 0xf2, 0x48, 0xdd, 0xa8, 0x05, 0x92, 0x04, 0x00, 0xb2, + 0x0a, 0x0c, 0x81, 0x64, 0x70, 0xba, 0x99, 0x92, 0x4a, 0x0c, 0x8e, + 0x07, 0x83, 0x28, 0xec, 0x52, 0x8e, 0x06, 0x81, 0x5c, 0x00, 0xe0, + 0x08, 0x00, 0x88, 0x46, 0xc8, 0x05, 0xb2, 0xa3, 0x88, 0xa2, 0x6c, + 0x16, 0xc2, 0x0c, 0x0c, 0x0c, 0x0a, 0xc0, 0xbb, 0xc1, 0xe0, 0x08, + 0x00, 0xd8, 0x05, 0xa2, 0x6d, 0x1a, 0xc6, 0x03, 0x00, 0x88, 0x07, + 0xf8, 0x05, 0xe2, 0x28, 0x1a, 0x82, 0x28, 0x16, 0x82, 0x6f, 0x16, + 0xe2, 0x6f, 0x1a, 0x8e, 0x05, 0x37, 0xa1, 0x80, 0xe0, 0x08, 0x00, + 0x0c, 0x0b, 0xc2, 0xa1, 0x80, 0xd8, 0x05, 0x82, 0x26, 0x3e, 0x3a, + 0xdd, 0xa2, 0x6d, 0x2b, 0x8e, 0x05, 0x85, 0x04, 0x0c, 0xad, 0x06, + 0xdf, 0x8e, 0x07, 0x87, 0x10, 0x81, 0x65, 0x8e, 0x0a, 0x8c, 0x7e, + 0x21, 0x00, 0x70, 0x88, 0x52, 0x8e, 0x05, 0xa6, 0x4b, 0xe2, 0xa7, + 0x34, 0xf2, 0xa7, 0x34, 0x82, 0x22, 0x5e, 0xd1, 0x26, 0x70, 0xbd, + 0x0a, 0xc1, 0x25, 0x70, 0xa1, 0x66, 0x70, 0xc8, 0x0c, 0xd8, 0x0d, + 0x8e, 0x0a, 0x8a, 0x38, 0x41, 0x68, 0x70, 0x21, 0x69, 0x70, 0x31, + 0x67, 0x70, 0x91, 0x65, 0x70, 0xa8, 0x33, 0x88, 0x03, 0x89, 0x09, + 0x29, 0x03, 0xa9, 0x19, 0x49, 0x33, 0x8e, 0x08, 0x86, 0x40, 0x8e, + 0x05, 0x86, 0x58, 0x6a, 0x8e, 0x0a, 0x5c, 0x2d, 0x0a, 0x91, 0x6b, + 0x70, 0xc0, 0x20, 0x00, 0x92, 0x29, 0xaf, 0x07, 0xe9, 0x30, 0x7c, + 0xd3, 0xa1, 0x6c, 0x8e, 0x05, 0x0e, 0x2a, 0x93, 0x30, 0x99, 0x10, + 0xc0, 0x20, 0x00, 0x81, 0x00, 0x70, 0x92, 0x6a, 0x93, 0x82, 0x28, + 0x15, 0x0c, 0xaa, 0xe0, 0x08, 0x00, 0xb1, 0x6d, 0x8e, 0x04, 0x1d, + 0xa2, 0x2b, 0x93, 0x30, 0xaa, 0x8e, 0x04, 0x1d, 0xa2, 0x6b, 0x93, + 0x82, 0xa0, 0x80, 0xc1, 0x6e, 0x70, 0xf1, 0x6f, 0x8e, 0x04, 0x18, + 0xe2, 0x2f, 0x51, 0x80, 0xee, 0x20, 0x8e, 0x04, 0x09, 0x6f, 0x51, + 0xd1, 0x70, 0x8e, 0x04, 0x12, 0xb2, 0x2c, 0xc8, 0xd0, 0xbb, 0x8e, + 0x04, 0x12, 0xb2, 0x6c, 0xc8, 0x8e, 0x08, 0x8b, 0x58, 0x8e, 0x0d, + 0x9d, 0x3c, 0x1e, 0x16, 0xb9, 0x8e, 0x0b, 0x9d, 0x3c, 0xfa, 0xb1, + 0x10, 0x70, 0xb8, 0x0b, 0xb2, 0x0b, 0x54, 0x66, 0x2b, 0x05, 0x25, + 0x3f, 0x01, 0x26, 0x1a, 0x07, 0x81, 0x6a, 0x70, 0x88, 0x18, 0x8e, + 0x09, 0x88, 0x20, 0x21, 0x03, 0x70, 0x91, 0x71, 0x70, 0xa1, 0x14, + 0x70, 0x81, 0x72, 0x70, 0x0c, 0x05, 0x59, 0x08, 0x32, 0x2a, 0x88, + 0x92, 0x6a, 0x88, 0x51, 0x6a, 0x70, 0x22, 0x22, 0xb3, 0x39, 0x05, + 0xb7, 0x72, 0x0f, 0xc1, 0x73, 0x70, 0xc8, 0x0c, 0xb1, 0x74, 0x70, + 0xd2, 0x2c, 0x10, 0xd9, 0x15, 0xb2, 0x6c, 0x10, 0x8e, 0x07, 0x74, + 0xa2, 0x22, 0xc6, 0xa2, 0x2a, 0x93, 0x82, 0x2a, 0xc1, 0x66, 0x38, + 0x1b, 0xb2, 0x2a, 0x34, 0xd2, 0x2a, 0x32, 0xc7, 0x6b, 0x12, 0xc2, + 0x2a, 0x51, 0xd7, 0xbc, 0x0c, 0x81, 0x3d, 0x70, 0x82, 0x28, 0x1e, + 0x8e, 0x07, 0x88, 0x1f, 0x8e, 0x08, 0x82, 0x24, 0x92, 0x22, 0xc6, + 0x92, 0x29, 0x93, 0xb1, 0x75, 0x70, 0x82, 0x29, 0xc1, 0xa2, 0x29, + 0xc4, 0x66, 0x38, 0x05, 0x26, 0x1a, 0x0d, 0x26, 0x4a, 0x0a, 0xad, + 0x02, 0x88, 0xab, 0xb8, 0x03, 0x8e, 0x05, 0x2b, 0xa8, 0x03, 0x26, + 0x1a, 0x22, 0x91, 0x76, 0x70, 0xc0, 0x20, 0x00, 0xad, 0x02, 0x92, + 0x29, 0x8e, 0x00, 0x82, 0x2b, 0x62, 0xbd, 0x09, 0xe0, 0x08, 0x00, + 0x16, 0x2a, 0xfe, 0x81, 0x77, 0x70, 0x82, 0x28, 0x1a, 0x8e, 0x07, + 0x53, 0xa2, 0x29, 0x5b, 0x1b, 0xaa, 0xa2, 0x69, 0x5b, 0x8e, 0x05, + 0x88, 0x60, 0x42, 0x22, 0xc6, 0x42, 0x24, 0x93, 0x82, 0x24, 0xc1, + 0x92, 0x24, 0x8e, 0x05, 0x55, 0x19, 0x10, 0x26, 0x49, 0x0d, 0x81, + 0x78, 0x70, 0xad, 0x02, 0x88, 0x08, 0xbd, 0x8e, 0x06, 0x58, 0x98, + 0x03, 0xa1, 0x00, 0x70, 0x66, 0x19, 0x15, 0xb2, 0x24, 0x5b, 0x82, + 0x2a, 0xa0, 0xa1, 0x79, 0x70, 0xe0, 0x08, 0x00, 0x92, 0x24, 0x5b, + 0x1b, 0x99, 0x92, 0x64, 0x5b, 0x1d, 0xf0, 0x82, 0x2a, 0x9f, 0xa1, + 0x7a, 0x8e, 0x04, 0x13, 0x81, 0x75, 0x70, 0x82, 0x28, 0x2e, 0x8e, + 0x09, 0x8a, 0x19, 0x36, 0x41, 0x00, 0xa1, 0x7b, 0x70, 0x32, 0x22, + 0xc6, 0x81, 0x00, 0x70, 0x32, 0x23, 0x93, 0x82, 0x28, 0xa1, 0xb2, + 0x23, 0x95, 0xc2, 0x23, 0x33, 0x8e, 0x04, 0x3d, 0x23, 0xc1, 0x66, + 0x39, 0x26, 0xa2, 0x23, 0x33, 0xb2, 0x23, 0x51, 0x1b, 0xaa, 0xa2, + 0x63, 0x33, 0xb7, 0x3a, 0x18, 0xb2, 0x23, 0x32, 0xc2, 0x23, 0x34, + 0xb6, 0x3b, 0x0f, 0xc7, 0x6c, 0x8e, 0x12, 0x82, 0x01, 0x36, 0x41, + 0x00, 0x81, 0x7c, 0x70, 0x91, 0x6e, 0x70, 0xc0, 0x20, 0x00, 0x82, + 0x69, 0xdc, 0x21, 0x7d, 0x70, 0x31, 0x7e, 0x8e, 0x04, 0x0c, 0x22, + 0x63, 0xd7, 0x8e, 0x08, 0x82, 0x20, 0xb1, 0x80, 0x70, 0x21, 0x83, + 0x70, 0x81, 0x78, 0x70, 0x91, 0x82, 0x70, 0x31, 0x7f, 0x70, 0xa1, + 0x81, 0x70, 0xa9, 0x53, 0x99, 0xa3, 0x48, 0x63, 0x49, 0x08, 0x29, + 0x63, 0xb9, 0x93, 0x8e, 0x06, 0x83, 0x2c, 0x52, 0x22, 0xc6, 0x0c, + 0x96, 0x42, 0x25, 0x93, 0x32, 0x65, 0x40, 0x82, 0x24, 0x96, 0x92, + 0xc3, 0xf8, 0xdc, 0x48, 0x16, 0xc9, 0x08, 0x67, 0x93, 0x60, 0xa2, + 0x02, 0x01, 0x66, 0x2a, 0x5a, 0xb2, 0x24, 0xc3, 0x0b, 0xbb, 0xb2, + 0x64, 0xc3, 0x1d, 0xf0, 0xa1, 0x84, 0x70, 0x8e, 0x07, 0x92, 0x20, + 0xa1, 0xc2, 0x25, 0x3d, 0xe0, 0x08, 0x00, 0xa1, 0x77, 0x70, 0x26, + 0x83, 0x16, 0x67, 0x93, 0x24, 0x92, 0x02, 0x01, 0x66, 0x29, 0x1e, + 0x0c, 0x1b, 0x0c, 0x0c, 0x88, 0x3a, 0x8e, 0x05, 0x81, 0x21, 0x06, + 0x04, 0x00, 0x8e, 0x05, 0x14, 0x0a, 0x0c, 0x0b, 0x8e, 0x09, 0x14, + 0x92, 0x24, 0xc1, 0x66, 0x39, 0x0b, 0x92, 0x24, 0xc4, 0x71, 0x85, + 0x70, 0x26, 0x19, 0x04, 0x26, 0x49, 0x01, 0x1d, 0xf0, 0x61, 0x75, + 0x70, 0x26, 0x13, 0x30, 0x26, 0x23, 0x6b, 0xa2, 0xc3, 0xfd, 0x16, + 0xea, 0x08, 0x26, 0x43, 0x57, 0x66, 0x53, 0xe8, 0xad, 0x02, 0x88, + 0x26, 0x0c, 0x2b, 0x8e, 0x05, 0xaa, 0x37, 0x92, 0x64, 0x18, 0x99, + 0xe4, 0x1d, 0xf0, 0x8e, 0x05, 0x81, 0x0a, 0xd0, 0xb2, 0x24, 0xc3, + 0x1b, 0x8e, 0x06, 0x81, 0x0a, 0x92, 0x25, 0x3d, 0xe7, 0x79, 0x12, + 0x88, 0x66, 0x8e, 0x05, 0x58, 0xa1, 0x86, 0x70, 0x92, 0x25, 0x3d, + 0xa0, 0x99, 0x10, 0x92, 0x65, 0x3d, 0xa7, 0x79, 0xaa, 0xad, 0x02, + 0x88, 0x96, 0x8e, 0x05, 0xa1, 0x2e, 0xa1, 0x87, 0x8e, 0x0a, 0x18, + 0x1d, 0xf0, 0x8e, 0x05, 0x55, 0x3b, 0x8e, 0x05, 0x82, 0x3c, 0x8e, + 0x05, 0x22, 0x1b, 0x8e, 0x04, 0x60, 0x2b, 0xa2, 0xaf, 0x7f, 0x92, + 0x25, 0x3d, 0x88, 0x26, 0xa0, 0x99, 0x10, 0xa1, 0x88, 0x70, 0x70, + 0x99, 0x20, 0xa0, 0x99, 0x20, 0x92, 0x65, 0x3d, 0x8e, 0x07, 0x82, + 0x65, 0x8e, 0x09, 0x34, 0x0c, 0x19, 0x92, 0x64, 0x18, 0x8e, 0x08, + 0x82, 0x54, 0xa1, 0x89, 0x70, 0x81, 0x00, 0x70, 0x52, 0x22, 0xc6, + 0x00, 0xb3, 0x11, 0xb0, 0xb4, 0x20, 0x52, 0x25, 0x93, 0x8e, 0x05, + 0x82, 0x11, 0xac, 0x8e, 0x05, 0x28, 0x7c, 0xfc, 0x66, 0x53, 0x28, + 0xb2, 0x25, 0x83, 0xd2, 0xc4, 0xfe, 0x56, 0xdd, 0x09, 0x16, 0x6b, + 0x08, 0xa2, 0x25, 0xac, 0x0b, 0xeb, 0x00, 0x13, 0x40, 0x00, 0xf9, + 0xa1, 0xe2, 0x65, 0x83, 0xc0, 0xdf, 0x30, 0xf0, 0xfa, 0x20, 0xd0, + 0xaa, 0x10, 0xe0, 0xaf, 0x93, 0x46, 0x12, 0x00, 0x66, 0x73, 0x28, + 0xb2, 0x25, 0xad, 0x82, 0xc4, 0xfe, 0x56, 0x58, 0x08, 0x16, 0xab, + 0x05, 0x00, 0x13, 0x40, 0xa2, 0x25, 0xac, 0x00, 0xe9, 0xa1, 0xc0, + 0xde, 0x30, 0xe0, 0xea, 0x8e, 0x04, 0x27, 0x0b, 0xdb, 0xd0, 0xae, + 0x93, 0xd2, 0x65, 0xad, 0x46, 0x07, 0x00, 0x8e, 0x04, 0x1a, 0x13, + 0x40, 0x00, 0xb9, 0xa1, 0x66, 0x24, 0x0b, 0xb7, 0x0a, 0x2e, 0xc0, + 0xfb, 0x30, 0xf0, 0xaa, 0x10, 0x46, 0x01, 0x00, 0xb7, 0x8a, 0x22, + 0xb0, 0xaa, 0x20, 0xd1, 0x77, 0x70, 0xa2, 0x65, 0xac, 0xd8, 0x3d, + 0x37, 0x6a, 0x18, 0x0c, 0x3c, 0xcc, 0x8c, 0xad, 0x02, 0x0c, 0x3b, + 0xe0, 0x0d, 0x00, 0x86, 0x01, 0x00, 0xad, 0x02, 0x0c, 0x2b, 0xe0, + 0x0d, 0x8e, 0x05, 0x9b, 0x31, 0x27, 0x6a, 0x04, 0x0c, 0x2c, 0x06, + 0xf8, 0xff, 0x17, 0x6a, 0x2c, 0x0c, 0x1c, 0x06, 0xf6, 0xff, 0x8e, + 0x06, 0x52, 0x1b, 0xdb, 0xd2, 0x65, 0x83, 0x00, 0xc9, 0xa1, 0xc0, + 0xaa, 0x20, 0xc6, 0xed, 0x8e, 0x0b, 0x14, 0xad, 0x8e, 0x07, 0x14, + 0xe8, 0xff, 0x47, 0x6a, 0x04, 0x0c, 0x4c, 0x06, 0xea, 0xff, 0x0c, + 0x5e, 0x0c, 0x0c, 0x2c, 0x0f, 0xf0, 0xfa, 0x10, 0xf0, 0xce, 0x93, + 0x46, 0xe6, 0xff, 0x8e, 0x05, 0x82, 0x0c, 0x8a, 0x70, 0xb8, 0x13, + 0x41, 0x8e, 0x05, 0x82, 0x0e, 0xc8, 0x03, 0x52, 0x25, 0x93, 0x00, + 0xcc, 0x11, 0x82, 0x24, 0xa0, 0xc0, 0xbb, 0x20, 0xe0, 0x08, 0x00, + 0xb8, 0x03, 0xc8, 0x13, 0x66, 0x8b, 0x05, 0x0c, 0x5b, 0x0c, 0x5d, + 0xd9, 0x03, 0xb6, 0x9b, 0x03, 0x7c, 0xf2, 0x1d, 0xf0, 0x66, 0x3b, + 0x26, 0x82, 0x24, 0x6e, 0x92, 0xa2, 0xd4, 0x9a, 0x55, 0x8e, 0x05, + 0x93, 0x1f, 0xc8, 0x13, 0xb8, 0x03, 0x66, 0x2c, 0x1a, 0xad, 0x05, + 0x0c, 0x0b, 0xc1, 0x8b, 0x70, 0x82, 0x24, 0x6d, 0x0c, 0x0d, 0x8e, + 0x07, 0x9c, 0x5e, 0xad, 0x02, 0x65, 0xe9, 0xff, 0xc6, 0xfc, 0xff, + 0x06, 0xfd, 0xff, 0x36, 0x41, 0x00, 0x82, 0x22, 0xc6, 0x22, 0x28, + 0x93, 0x52, 0x22, 0x96, 0x9c, 0x75, 0x9c, 0x53, 0x92, 0x22, 0xc1, + 0x38, 0x43, 0x66, 0x39, 0x0e, 0xa2, 0x22, 0xc4, 0x66, 0x1a, 0x08, + 0x22, 0x23, 0x1f, 0x37, 0x62, 0x02, 0x27, 0x62, 0x01, 0x1d, 0xf0, + 0x0c, 0x1b, 0xb2, 0x44, 0x04, 0x8e, 0x05, 0x9f, 0x1c, 0x72, 0x22, + 0xc6, 0xa2, 0xa0, 0x05, 0x62, 0x27, 0x93, 0xa2, 0x61, 0x00, 0x82, + 0x26, 0x96, 0x50, 0x95, 0x20, 0x16, 0x38, 0x0c, 0x51, 0x75, 0x70, + 0x9c, 0xc9, 0xad, 0x02, 0x92, 0x26, 0x99, 0x88, 0x15, 0x92, 0x67, + 0x89, 0xe0, 0x08, 0x00, 0xb2, 0x27, 0x89, 0x8c, 0xab, 0x88, 0x8e, + 0x06, 0x96, 0x1f, 0x0c, 0x09, 0x92, 0x67, 0x89, 0x56, 0x53, 0x09, + 0x31, 0x8c, 0x70, 0xb1, 0x8d, 0x70, 0x71, 0x14, 0x70, 0x47, 0xbb, + 0x47, 0xc1, 0x8e, 0x00, 0x70, 0x47, 0x3c, 0x41, 0x88, 0x33, 0x0c, + 0x1a, 0xe0, 0x08, 0x00, 0x81, 0x8f, 0x8e, 0x05, 0xa6, 0x70, 0x11, + 0x8e, 0x05, 0x84, 0x5d, 0x92, 0x26, 0x96, 0x26, 0x49, 0x02, 0x66, + 0x59, 0x66, 0x92, 0x26, 0x95, 0x97, 0x79, 0x60, 0xad, 0x02, 0x0c, + 0x1b, 0xa5, 0xbc, 0x00, 0xad, 0x02, 0x0c, 0x8b, 0x88, 0x35, 0x0c, + 0x1c, 0x8e, 0x04, 0x4f, 0x1a, 0x82, 0x27, 0xa6, 0x8e, 0x05, 0xa9, + 0x3e, 0x46, 0x10, 0x8e, 0x09, 0x2e, 0x24, 0x8e, 0x05, 0x2e, 0x1e, + 0x20, 0xa2, 0x20, 0xb2, 0xa0, 0x00, 0xa5, 0xb9, 0x00, 0x20, 0xa2, + 0x20, 0x0c, 0x1b, 0x88, 0x35, 0x0c, 0x8e, 0x05, 0xa8, 0x4d, 0x0a, + 0x8e, 0x08, 0x31, 0x8e, 0x09, 0x69, 0x1b, 0xe0, 0x08, 0x00, 0x88, + 0x33, 0x8e, 0x05, 0x8c, 0x4f, 0x82, 0x25, 0x60, 0x8e, 0x0c, 0x88, + 0x44, 0xad, 0x02, 0xbd, 0x03, 0x0c, 0x09, 0x81, 0x90, 0x70, 0x0c, + 0x0c, 0x0c, 0x0d, 0x0c, 0x2e, 0xe2, 0x43, 0x20, 0xd2, 0x43, 0x21, + 0xc2, 0x43, 0x22, 0x88, 0x08, 0x92, 0x43, 0x23, 0x8e, 0x0b, 0x90, + 0x68, 0xc1, 0x92, 0x70, 0x21, 0x96, 0x70, 0x81, 0x90, 0x70, 0x91, + 0x95, 0x70, 0xa1, 0x94, 0x70, 0x31, 0x91, 0x70, 0xb1, 0x93, 0x70, + 0xb2, 0x63, 0x16, 0xa9, 0xb3, 0x99, 0x73, 0x42, 0x23, 0x13, 0x49, + 0x08, 0x22, 0x63, 0x13, 0xc9, 0x23, 0x8e, 0x06, 0x2c, 0x42, 0x22, + 0xc6, 0x51, 0x97, 0x70, 0x9c, 0xf3, 0x88, 0xf5, 0x8e, 0x06, 0x95, + 0x6e, 0x98, 0x70, 0xa2, 0x64, 0x72, 0x90, 0xca, 0x10, 0x97, 0x8a, + 0x01, 0xcd, 0x0a, 0xad, 0x02, 0x82, 0x25, 0x10, 0x8e, 0x05, 0x83, + 0x1d, 0x1d, 0xf0, 0xc2, 0x24, 0x72, 0x8c, 0x8c, 0x8e, 0x0c, 0x11, + 0x8e, 0x07, 0x8b, 0x0c, 0xa0, 0x00, 0xd1, 0x20, 0x70, 0x81, 0x24, + 0x70, 0xc2, 0x22, 0xc6, 0x82, 0x08, 0x00, 0xe2, 0x2c, 0x93, 0x76, + 0x98, 0x12, 0xd0, 0xc9, 0xa0, 0xc8, 0x0c, 0xc2, 0x2c, 0xc6, 0x1b, + 0x99, 0xa2, 0x2c, 0x14, 0x90, 0x90, 0x74, 0x97, 0xea, 0x2e, 0xa2, + 0x2e, 0xc1, 0x92, 0x2e, 0xc4, 0x66, 0x3a, 0x13, 0x26, 0x19, 0x02, + 0x66, 0x49, 0x0d, 0xd1, 0x99, 0x70, 0xb2, 0x2c, 0x3d, 0x0c, 0x13, + 0xd0, 0xbb, 0x10, 0xb2, 0x6c, 0x3d, 0xf2, 0x2e, 0x99, 0x37, 0x1f, + 0x0b, 0x81, 0x9a, 0x8e, 0x0c, 0x9b, 0x51, 0x8e, 0x09, 0x8b, 0x10, + 0x92, 0x24, 0xc1, 0x31, 0x75, 0x70, 0x66, 0x39, 0x27, 0x81, 0x77, + 0x8e, 0x05, 0x82, 0x22, 0x13, 0xb2, 0x24, 0xc4, 0x8e, 0x05, 0x88, + 0x59, 0xc4, 0x8c, 0x79, 0x26, 0x79, 0x05, 0x8e, 0x05, 0x50, 0x33, + 0xad, 0x02, 0x82, 0x23, 0x17, 0x8e, 0x05, 0x82, 0x3b, 0x1d, 0xf0, + 0x66, 0x49, 0x24, 0xad, 0x02, 0x41, 0x8f, 0x70, 0x0c, 0x1b, 0x88, + 0xa4, 0xc1, 0x9b, 0x70, 0x8e, 0x05, 0xa1, 0x48, 0x0c, 0x1b, 0x88, + 0xc4, 0x8e, 0x08, 0x0c, 0x8e, 0x0a, 0x2a, 0x36, 0x41, 0x00, 0x22, + 0x23, 0xc6, 0x22, 0x22, 0x93, 0x82, 0x22, 0xac, 0x42, 0x22, 0x73, + 0x57, 0x68, 0x02, 0x42, 0x22, 0x89, 0xa1, 0x9c, 0x70, 0x51, 0x00, + 0x70, 0xb2, 0x22, 0x5b, 0x82, 0x25, 0xa1, 0xcd, 0x04, 0xe0, 0x08, + 0x00, 0xa1, 0x9c, 0x70, 0xb2, 0x22, 0x5c, 0x82, 0x25, 0xa1, 0xc2, + 0x22, 0x5d, 0xe0, 0x08, 0x00, 0xb1, 0x77, 0x70, 0x92, 0x22, 0x5b, + 0x0c, 0x05, 0x47, 0xb9, 0x05, 0xa2, 0x22, 0xc5, 0xb6, 0x5a, 0x34, + 0xc2, 0x22, 0x5c, 0xd2, 0x22, 0x74, 0x1b, 0xcc, 0xc2, 0x62, 0x5c, + 0xd7, 0x3c, 0x1b, 0x92, 0x22, 0xc4, 0x8c, 0x19, 0x66, 0x59, 0x0d, + 0xad, 0x03, 0x0c, 0x7c, 0x88, 0x3b, 0x8e, 0x05, 0x89, 0x52, 0x92, + 0x22, 0xc4, 0x26, 0x19, 0x45, 0x26, 0x49, 0x42, 0x52, 0x62, 0x5d, + 0x52, 0x62, 0x5b, 0x52, 0x62, 0xc5, 0x1d, 0xf0, 0x52, 0x62, 0x5c, + 0x92, 0x22, 0x5d, 0xa2, 0x22, 0x74, 0x1b, 0x99, 0x92, 0x62, 0x5d, + 0xa7, 0x39, 0xe6, 0x92, 0x22, 0xc4, 0x66, 0x79, 0x8e, 0x08, 0x36, + 0x3b, 0x8e, 0x08, 0x36, 0x02, 0x66, 0x49, 0xcc, 0x98, 0xa2, 0xa2, + 0xaf, 0xbf, 0xa0, 0x99, 0x10, 0x99, 0xa2, 0xc6, 0xef, 0xff, 0xb8, + 0xa2, 0x4c, 0x0c, 0xc0, 0xbb, 0x20, 0xb9, 0xa2, 0x06, 0xec, 0x8e, + 0x06, 0x87, 0x0c, 0x9d, 0x70, 0x42, 0x8e, 0x05, 0x8c, 0x58, 0x42, + 0x24, 0x93, 0x82, 0x28, 0xa0, 0xb8, 0xa4, 0xe0, 0x08, 0x00, 0xa8, + 0xa4, 0x0c, 0x1d, 0x07, 0x6a, 0x0b, 0x39, 0xc4, 0x7c, 0xe9, 0x90, + 0x9a, 0x10, 0x99, 0xa4, 0x86, 0x07, 0x00, 0xb8, 0xb4, 0xc8, 0xc4, + 0xab, 0xeb, 0xc0, 0xc3, 0xc0, 0xe7, 0xbc, 0x16, 0xe2, 0xcb, 0xf6, + 0xc7, 0xbe, 0x10, 0x51, 0x77, 0x70, 0x82, 0x25, 0x18, 0x39, 0xc4, + 0xe0, 0x08, 0x00, 0x8c, 0xba, 0x0c, 0x02, 0x1d, 0xf0, 0xc7, 0xbb, + 0xf8, 0xd0, 0x9a, 0x20, 0x86, 0xf3, 0xff, 0xb8, 0xa4, 0x57, 0x6b, + 0x08, 0x82, 0x25, 0x19, 0xe0, 0x08, 0x00, 0x56, 0x4a, 0xfe, 0x81, + 0x75, 0x70, 0xad, 0x02, 0x88, 0x28, 0x8e, 0x06, 0x8b, 0x1b, 0x12, + 0x8e, 0x05, 0x86, 0x68, 0xd1, 0x24, 0x70, 0x0c, 0x02, 0xb2, 0x0d, + 0x00, 0x51, 0x20, 0x70, 0x16, 0x4b, 0x0b, 0x0c, 0x0a, 0x72, 0xaf, + 0xdf, 0x61, 0x00, 0x70, 0x50, 0x92, 0xa0, 0x92, 0x29, 0x00, 0x32, + 0x29, 0xc6, 0x82, 0x09, 0x03, 0x42, 0x23, 0x93, 0x66, 0x38, 0x6f, + 0xc2, 0x09, 0x01, 0x66, 0x1c, 0x69, 0x92, 0x23, 0x77, 0x16, 0x19, + 0x05, 0xf2, 0x23, 0x3d, 0x92, 0x61, 0x01, 0xe7, 0x7f, 0x48, 0xa2, + 0x23, 0x75, 0xb2, 0xa0, 0x64, 0xb0, 0xaa, 0x82, 0x90, 0xb9, 0x20, + 0xe5, 0xcf, 0x09, 0xc8, 0x11, 0xb2, 0x23, 0x76, 0xa9, 0x01, 0x82, + 0x26, 0xa1, 0xa1, 0x9e, 0x70, 0xe0, 0x08, 0x00, 0xa1, 0x9e, 0x70, + 0xb2, 0x23, 0x75, 0x82, 0x26, 0xa1, 0xc2, 0x23, 0x74, 0xe0, 0x08, + 0x8e, 0x04, 0x6b, 0x98, 0x01, 0x5c, 0xae, 0x97, 0xbe, 0x30, 0xb2, + 0x23, 0x77, 0x0c, 0xaa, 0xb7, 0xba, 0x28, 0x98, 0xa4, 0x70, 0x99, + 0x10, 0x0c, 0x0a, 0x99, 0xa4, 0xa2, 0x63, 0x74, 0xa2, 0x63, 0x76, + 0xa2, 0x63, 0x75, 0xa2, 0x63, 0x73, 0xa2, 0x63, 0x77, 0xb2, 0x0d, + 0x00, 0x8e, 0x05, 0xa7, 0x57, 0xb7, 0xb2, 0x02, 0x86, 0xdc, 0xff, + 0x06, 0x07, 0x00, 0x82, 0x23, 0x74, 0x2c, 0x0a, 0xf6, 0x48, 0x04, + 0x92, 0x23, 0x76, 0x8c, 0x69, 0x98, 0xa4, 0xa0, 0x99, 0x20, 0x86, + 0xf1, 0xff, 0x8e, 0x05, 0x3e, 0x86, 0xef, 0xff, 0x8e, 0x0e, 0x8f, + 0x70, 0xc2, 0x24, 0xc4, 0x66, 0x38, 0x4b, 0x26, 0x1c, 0x48, 0x26, + 0x4c, 0x45, 0x81, 0x00, 0x70, 0xa1, 0x9f, 0x70, 0x82, 0x28, 0xa1, + 0x8e, 0x05, 0x8f, 0x72, 0x51, 0x75, 0x70, 0x66, 0x13, 0x33, 0xad, + 0x02, 0x88, 0x25, 0x8e, 0x06, 0x87, 0x18, 0x55, 0xa1, 0xa0, 0x70, + 0x92, 0x24, 0x34, 0x0c, 0x0b, 0xb2, 0x64, 0x33, 0xb2, 0x64, 0x32, + 0xa0, 0x99, 0x20, 0x92, 0x64, 0x34, 0x8e, 0x07, 0x85, 0x32, 0x66, + 0x43, 0x08, 0x8e, 0x05, 0x29, 0x8e, 0x06, 0x94, 0x2c, 0x66, 0x23, + 0x0a, 0x8e, 0x05, 0x0e, 0x8e, 0x06, 0x8c, 0x7e, 0x66, 0x33, 0xe0, + 0x8e, 0x09, 0x45, 0x8e, 0x07, 0x99, 0x68, 0x0c, 0x09, 0x32, 0x22, + 0xc6, 0x51, 0x77, 0x70, 0x42, 0x23, 0x93, 0x82, 0x25, 0x14, 0x92, + 0x64, 0x33, 0x92, 0x64, 0x32, 0x92, 0x64, 0x34, 0x8e, 0x06, 0x85, + 0x5e, 0x8e, 0x05, 0x8f, 0x77, 0x12, 0x8e, 0x06, 0x8e, 0x42, 0x23, + 0x71, 0x61, 0x75, 0x70, 0x8c, 0x59, 0x8e, 0x07, 0x8d, 0x79, 0x8e, + 0x05, 0x8d, 0x1f, 0x8e, 0x04, 0x47, 0x92, 0x24, 0x95, 0xd7, 0x79, + 0x13, 0x82, 0x25, 0x15, 0x2c, 0x8e, 0x05, 0xbb, 0x45, 0xa1, 0x70, + 0x92, 0x24, 0x95, 0xa0, 0x99, 0x10, 0x92, 0x64, 0x95, 0x81, 0x00, + 0x70, 0xa2, 0xa1, 0x44, 0x82, 0x28, 0x6e, 0xaa, 0xa3, 0x8e, 0x04, + 0x1a, 0x24, 0x70, 0x0c, 0x02, 0x92, 0x0a, 0x00, 0x71, 0x20, 0x70, + 0x16, 0xf9, 0x05, 0x51, 0x3e, 0x70, 0x70, 0x42, 0xa0, 0x48, 0x04, + 0x32, 0x24, 0xc6, 0x92, 0x23, 0x3d, 0xe7, 0x79, 0x22, 0x88, 0x66, + 0xad, 0x8e, 0x06, 0xa9, 0x1d, 0x0c, 0x0b, 0x0c, 0x1c, 0x88, 0x76, + 0x8e, 0x05, 0x87, 0x5d, 0xb1, 0x86, 0x70, 0x92, 0x23, 0x3d, 0xa1, + 0x24, 0x70, 0xb0, 0x99, 0x10, 0x92, 0x63, 0x3d, 0x50, 0xc9, 0x20, + 0xc2, 0x63, 0x3d, 0xa7, 0x7c, 0x17, 0xad, 0x04, 0x8e, 0x07, 0x8e, + 0x66, 0xb1, 0x87, 0x8e, 0x0d, 0x21, 0xc2, 0x0a, 0x8e, 0x06, 0x82, + 0x7c, 0xc7, 0x32, 0xa2, 0x8e, 0x08, 0x99, 0x2c, 0x71, 0x20, 0x70, + 0x42, 0x22, 0xc6, 0x61, 0x24, 0x70, 0x32, 0x24, 0x93, 0x39, 0x01, + 0x82, 0x06, 0x00, 0x32, 0x23, 0xc4, 0x9c, 0xf8, 0xa1, 0xa2, 0x70, + 0x0c, 0x05, 0x70, 0x45, 0xa0, 0x48, 0x04, 0x42, 0x24, 0xc6, 0x82, + 0x24, 0x3d, 0x1b, 0x55, 0xa0, 0x88, 0x10, 0x82, 0x64, 0x3d, 0x82, + 0x06, 0x00, 0x50, 0x50, 0x74, 0x87, 0x35, 0xe3, 0x51, 0x75, 0x70, + 0xad, 0x02, 0x82, 0x25, 0x17, 0x8e, 0x06, 0x8f, 0x4c, 0x77, 0x70, + 0x66, 0x23, 0x06, 0x0c, 0x04, 0x0c, 0x05, 0x86, 0x15, 0x00, 0x66, + 0x33, 0x06, 0x0c, 0x14, 0x5c, 0x05, 0x06, 0x13, 0x00, 0x66, 0x53, + 0x04, 0x0c, 0x04, 0x86, 0xfc, 0xff, 0x26, 0x13, 0x77, 0x26, 0x43, + 0x74, 0xec, 0x53, 0x88, 0x55, 0x8e, 0x06, 0x9b, 0x1c, 0x77, 0x8e, + 0x05, 0x91, 0x26, 0x13, 0x8e, 0x05, 0x0d, 0x88, 0x65, 0x8e, 0x07, + 0x82, 0x2d, 0x8e, 0x04, 0x4c, 0x8e, 0x07, 0x88, 0x17, 0x73, 0x0e, + 0x98, 0x01, 0x92, 0x29, 0xac, 0x57, 0x69, 0x29, 0x8e, 0x04, 0x49, + 0x86, 0x09, 0x00, 0x3c, 0xc5, 0x0c, 0x04, 0xbd, 0x03, 0x82, 0x2a, + 0x8e, 0x06, 0x33, 0xad, 0x02, 0xbd, 0x05, 0xdd, 0x04, 0x81, 0x3d, + 0x70, 0x0c, 0x3c, 0x82, 0x28, 0x11, 0x1c, 0xee, 0x8e, 0x06, 0x97, + 0x72, 0x24, 0x3c, 0xc5, 0x8e, 0x05, 0x86, 0x02, 0x82, 0x28, 0x8e, + 0x06, 0x48, 0xa1, 0x77, 0x70, 0x46, 0xf2, 0xff, 0x8e, 0x0a, 0x73, + 0x8e, 0x05, 0x89, 0x12, 0x8e, 0x05, 0x84, 0x2b, 0x8e, 0x07, 0x73, + 0x66, 0x13, 0x18, 0x0c, 0x1a, 0xc1, 0xa3, 0x70, 0xb8, 0x01, 0x81, + 0x77, 0x70, 0x92, 0x2b, 0x95, 0x82, 0x28, 0x15, 0xc0, 0x99, 0x20, + 0x92, 0x6b, 0x95, 0xe0, 0x08, 0x00, 0x32, 0xa1, 0x44, 0x81, 0x00, + 0x70, 0x3a, 0x34, 0x82, 0x28, 0x6e, 0xad, 0x8e, 0x04, 0x33, 0xad, + 0x03, 0x8e, 0x05, 0xb1, 0x4b, 0xc2, 0xa0, 0x64, 0x82, 0x28, 0x8e, + 0x06, 0xaa, 0x63, 0xb2, 0x06, 0x00, 0x16, 0x2b, 0x04, 0x0c, 0x05, + 0x31, 0x85, 0x70, 0x70, 0xa5, 0xa0, 0xa8, 0x0a, 0xc2, 0x0a, 0x03, + 0x42, 0x2a, 0xc6, 0x66, 0x3c, 0x27, 0xb2, 0x0a, 0x01, 0x66, 0x1b, + 0x0b, 0xd1, 0x88, 0x70, 0xc2, 0x24, 0x3d, 0xd0, 0xcc, 0x20, 0xc2, + 0x64, 0x3d, 0x81, 0x75, 0x70, 0x88, 0x98, 0x8e, 0x07, 0x84, 0x0a, + 0x3d, 0x30, 0x99, 0x20, 0x92, 0x64, 0x3d, 0xb2, 0x06, 0x00, 0x1b, + 0x55, 0x50, 0x50, 0x74, 0xb7, 0x35, 0xc1, 0x8e, 0x0d, 0x81, 0x35, + 0x8e, 0x07, 0x8c, 0x40, 0x0c, 0x0a, 0x41, 0x3d, 0x70, 0x52, 0x22, + 0xc6, 0x82, 0x24, 0x15, 0x52, 0x25, 0x93, 0xe0, 0x08, 0x00, 0xb8, + 0xa5, 0x61, 0x00, 0x70, 0x3d, 0x0a, 0x82, 0x26, 0xa0, 0xa1, 0xa4, + 0x70, 0xe0, 0x08, 0x00, 0x16, 0x53, 0x0a, 0xa1, 0xa5, 0x70, 0xb8, + 0xa5, 0x82, 0x26, 0xa1, 0xc2, 0x03, 0x00, 0x8e, 0x05, 0x81, 0x21, + 0x88, 0xa4, 0x42, 0x23, 0xc6, 0xe0, 0x08, 0x00, 0x98, 0x2a, 0x90, + 0x90, 0x14, 0x16, 0x59, 0x08, 0xa2, 0x03, 0x01, 0x66, 0x1a, 0x51, + 0xa1, 0xa5, 0x70, 0xb2, 0x24, 0x3d, 0x82, 0x26, 0xa1, 0xc1, 0xa6, + 0x8e, 0x06, 0x96, 0x02, 0x3d, 0x71, 0x75, 0x70, 0xe7, 0xe9, 0x67, + 0xd6, 0x69, 0x06, 0xd2, 0x26, 0xa1, 0x90, 0xbe, 0x05, 0x16, 0x2b, + 0x09, 0xa1, 0xa5, 0x70, 0x0c, 0x1b, 0xc1, 0xa7, 0x70, 0xe0, 0x0d, + 0x00, 0x88, 0x67, 0x8e, 0x06, 0x82, 0x62, 0x03, 0x8e, 0x05, 0x84, + 0x64, 0x77, 0x8e, 0x05, 0x84, 0x64, 0xa1, 0x86, 0x70, 0x92, 0x24, + 0x8e, 0x05, 0x93, 0x29, 0x64, 0x3d, 0x46, 0x12, 0x00, 0x38, 0xc5, + 0x92, 0x25, 0x20, 0x82, 0x25, 0x1f, 0x22, 0xa2, 0xbc, 0x2a, 0x25, + 0x9a, 0x88, 0x92, 0xa2, 0x71, 0x90, 0x88, 0x82, 0xad, 0x02, 0x8a, + 0x33, 0x82, 0xae, 0x0c, 0x8a, 0x33, 0x81, 0xaa, 0x8e, 0x06, 0x8b, + 0x3b, 0xbd, 0x03, 0x81, 0xab, 0x8e, 0x06, 0xa1, 0x34, 0xe7, 0x79, + 0xfa, 0xa8, 0xa5, 0x47, 0x6a, 0x1c, 0x98, 0x35, 0x9c, 0x79, 0x0b, + 0xb9, 0xb9, 0x35, 0x1d, 0xf0, 0x92, 0x24, 0x3d, 0x0c, 0xbc, 0xc2, + 0x64, 0x78, 0xd1, 0xa8, 0x70, 0xd0, 0xd9, 0x10, 0xd2, 0x64, 0x3d, + 0x1d, 0xf0, 0x8e, 0x06, 0x71, 0x82, 0x27, 0x13, 0x8e, 0x07, 0x8d, + 0x33, 0xa1, 0xa5, 0x70, 0xc1, 0xa7, 0x70, 0x0c, 0x0b, 0xb9, 0x35, + 0xe0, 0x0d, 0x00, 0x88, 0x57, 0x8e, 0x08, 0x81, 0x15, 0x1b, 0x8e, + 0x09, 0x81, 0x15, 0x1c, 0x89, 0x97, 0x1a, 0xb6, 0xa1, 0xa9, 0x8e, + 0x06, 0x81, 0x1a, 0x20, 0x86, 0xd8, 0x8e, 0x06, 0xa9, 0x7c, 0xa1, + 0xac, 0x70, 0xbd, 0x03, 0xcd, 0x04, 0x61, 0x8e, 0x05, 0x91, 0x72, + 0x82, 0x26, 0xa1, 0x8e, 0x06, 0x82, 0x3d, 0xac, 0x33, 0x0b, 0x83, + 0x16, 0x38, 0x0c, 0x92, 0xc3, 0xfe, 0x16, 0x89, 0x0c, 0x66, 0x33, + 0x1e, 0x0c, 0x04, 0x0c, 0x0b, 0xa2, 0x25, 0xac, 0xb2, 0x65, 0xc4, + 0x57, 0x6a, 0x14, 0x0c, 0x54, 0x0c, 0x5c, 0xc2, 0x65, 0xc4, 0x86, + 0x02, 0x00, 0xd2, 0x25, 0xc3, 0x1b, 0xdd, 0xd2, 0x65, 0xc3, 0x42, + 0x25, 0xc4, 0x0c, 0x3a, 0x92, 0x25, 0x96, 0x66, 0x54, 0x0a, 0xe2, + 0xc9, 0xfb, 0x56, 0xfe, 0x07, 0x0c, 0x23, 0x46, 0x0a, 0x00, 0x66, + 0x59, 0x16, 0xf2, 0x25, 0xc3, 0x0c, 0x23, 0x8c, 0x0f, 0xcc, 0x14, + 0x66, 0x34, 0x02, 0xc6, 0x05, 0x00, 0x92, 0x25, 0xac, 0x1c, 0x28, + 0x97, 0x88, 0xf4, 0x66, 0x24, 0x0a, 0xc2, 0x25, 0xac, 0x7c, 0xbb, + 0xc7, 0x0b, 0x02, 0xa2, 0x65, 0xc4, 0x0c, 0x33, 0xa1, 0xad, 0x70, + 0xbd, 0x03, 0x82, 0x26, 0xa1, 0xc2, 0x25, 0xc1, 0xe0, 0x08, 0x00, + 0xa2, 0x25, 0xc1, 0x41, 0x9a, 0x70, 0x37, 0x9a, 0x05, 0x66, 0x3a, + 0x31, 0x37, 0x1a, 0x26, 0x0c, 0x19, 0x9c, 0xd9, 0x8e, 0x0a, 0x21, + 0xc4, 0xe0, 0x08, 0x00, 0xbd, 0x03, 0xad, 0x02, 0x81, 0x77, 0x70, + 0x92, 0x25, 0xc4, 0x88, 0x48, 0x99, 0x14, 0x8e, 0x07, 0x8b, 0x3c, + 0xb2, 0x25, 0xc4, 0xa8, 0x14, 0xb7, 0x9a, 0xd0, 0x0c, 0x09, 0x46, + 0xf3, 0xff, 0xc2, 0xc9, 0xfc, 0x0c, 0x43, 0xc0, 0x3a, 0x83, 0xc6, + 0xe8, 0xff, 0xd2, 0x25, 0xc3, 0x0b, 0xdd, 0xd2, 0x65, 0xc3, 0x06, + 0xd6, 0xff, 0x42, 0x65, 0xc4, 0x46, 0xd5, 0x8e, 0x07, 0x9d, 0x64, + 0xa1, 0xae, 0x70, 0x51, 0xaf, 0x70, 0x22, 0x23, 0xc6, 0x41, 0x00, + 0x70, 0x22, 0x22, 0x93, 0x82, 0x24, 0xa0, 0xb8, 0x05, 0xe0, 0x08, + 0x00, 0x71, 0x77, 0x70, 0x88, 0x17, 0xe0, 0x08, 0x00, 0x66, 0x4a, + 0x19, 0xb8, 0x05, 0x61, 0x75, 0x70, 0x66, 0x1b, 0x30, 0xad, 0x03, + 0x8e, 0x07, 0x96, 0x34, 0x1c, 0x43, 0x0c, 0x09, 0x99, 0x05, 0x46, + 0x00, 0x00, 0x0c, 0x03, 0x8e, 0x05, 0x93, 0x73, 0xec, 0x9a, 0x22, + 0x8e, 0x05, 0x82, 0x66, 0xcd, 0x03, 0xad, 0x02, 0x0c, 0x0b, 0x8e, + 0x08, 0x93, 0x6b, 0x1d, 0xf0, 0x0c, 0x19, 0x88, 0x27, 0x99, 0x8e, + 0x05, 0x94, 0x0c, 0x26, 0x66, 0x4a, 0x0c, 0xad, 0x03, 0x0c, 0x2b, + 0xe0, 0x0c, 0x00, 0x32, 0xa1, 0xf4, 0xc6, 0xf1, 0xff, 0xad, 0x03, + 0x0c, 0x1b, 0xe0, 0x0c, 0x00, 0x5c, 0x03, 0xc6, 0xee, 0x8e, 0x07, + 0x81, 0x08, 0xa2, 0xa2, 0xec, 0x92, 0x22, 0xc6, 0xb1, 0xaf, 0x70, + 0x92, 0x29, 0x93, 0x39, 0x0b, 0x31, 0x00, 0x70, 0xaa, 0x29, 0x82, + 0x23, 0x6e, 0x8e, 0x07, 0x87, 0x35, 0x0c, 0x0b, 0x0c, 0x0c, 0x82, + 0x23, 0x8e, 0x08, 0x58, 0x8e, 0x05, 0x30, 0x21, 0x20, 0x70, 0x28, + 0x02, 0x22, 0x22, 0xc6, 0x41, 0xb0, 0x70, 0x22, 0x22, 0x93, 0xc0, + 0x20, 0x00, 0x81, 0x3d, 0x70, 0x42, 0x24, 0x98, 0x82, 0x28, 0x15, + 0x8e, 0x05, 0x92, 0x56, 0x3d, 0x0a, 0xbc, 0x5a, 0xa1, 0xb1, 0x70, + 0xc2, 0xa2, 0x71, 0x81, 0x00, 0x70, 0xb2, 0x22, 0x1f, 0x82, 0x28, + 0xa0, 0xc0, 0xbb, 0x82, 0x8e, 0x05, 0x84, 0x0b, 0xb1, 0xb3, 0x70, + 0x0c, 0x1c, 0xf2, 0xa1, 0xf4, 0x81, 0xb2, 0x70, 0xd2, 0x22, 0x1f, + 0xe2, 0xa2, 0x71, 0xe0, 0xdd, 0x82, 0x88, 0x18, 0x4a, 0xed, 0xfa, + 0xdd, 0xd0, 0xd0, 0xf4, 0x8e, 0x09, 0x9d, 0x68, 0x32, 0x22, 0xc6, + 0x32, 0x23, 0x93, 0x41, 0xb4, 0x70, 0x92, 0x23, 0x96, 0x82, 0x23, + 0x95, 0x8e, 0x05, 0x93, 0x65, 0x37, 0x97, 0xf8, 0x2a, 0x81, 0x3d, + 0x70, 0x8e, 0x07, 0xa6, 0x77, 0x8e, 0x05, 0x86, 0x2e, 0xdc, 0x89, + 0xad, 0x02, 0x81, 0xb5, 0x70, 0x0c, 0x1b, 0x82, 0x28, 0x32, 0x8e, + 0x05, 0x93, 0x6d, 0x92, 0x23, 0x95, 0x40, 0x99, 0x20, 0x92, 0x63, + 0x95, 0x1d, 0xf0, 0x81, 0x9a, 0x70, 0x8e, 0x0b, 0xaa, 0x3d, 0x36, + 0x41, 0x00, 0x30, 0xa3, 0x20, 0xb2, 0xa0, 0x03, 0xc2, 0xa0, 0x02, + 0x25, 0x39, 0x8e, 0x09, 0xb5, 0x10, 0x8e, 0x05, 0xa0, 0x0c, 0x9a, + 0x70, 0xad, 0x02, 0x88, 0x38, 0x8e, 0x05, 0x87, 0x47, 0x8e, 0x08, + 0x93, 0x60, 0xa1, 0xc0, 0x70, 0xc1, 0xbe, 0x70, 0xf1, 0xbc, 0x70, + 0x81, 0xb7, 0x70, 0x31, 0xba, 0x70, 0x21, 0xbb, 0x70, 0x41, 0xb9, + 0x70, 0x91, 0xb6, 0x70, 0x51, 0xb8, 0x70, 0xd1, 0x77, 0x70, 0xe1, + 0xbd, 0x70, 0xe9, 0xcd, 0x59, 0xdd, 0x92, 0x6d, 0x10, 0x42, 0x6d, + 0x1a, 0x29, 0xbd, 0xb8, 0x7d, 0x39, 0x3d, 0x89, 0xed, 0x81, 0xc2, + 0x70, 0xf2, 0x6d, 0x16, 0xc9, 0x7d, 0x21, 0xc6, 0x70, 0x41, 0xc3, + 0x70, 0x31, 0xbf, 0x70, 0x91, 0xc1, 0x70, 0xa9, 0x43, 0x82, 0x63, + 0x22, 0x99, 0x13, 0x52, 0xd3, 0xfe, 0x42, 0x65, 0x78, 0x91, 0x9a, + 0x70, 0x41, 0xc5, 0x70, 0x51, 0xc4, 0x70, 0xb9, 0x29, 0x32, 0xd3, + 0xff, 0x59, 0x93, 0x82, 0x23, 0x13, 0xa8, 0x23, 0xa9, 0x09, 0x89, + 0x39, 0x49, 0x23, 0x22, 0x63, 0x13, 0x8e, 0x07, 0x78, 0xc7, 0x70, + 0x41, 0x00, 0x70, 0x32, 0x22, 0xc6, 0x82, 0x24, 0x9f, 0x52, 0x23, + 0x93, 0xe0, 0x08, 0x00, 0x92, 0x25, 0xc1, 0x61, 0x75, 0x70, 0x66, + 0x49, 0x14, 0x20, 0xa2, 0x20, 0x82, 0x26, 0x02, 0xb2, 0xa0, 0x8e, + 0x05, 0x89, 0x56, 0x02, 0x0c, 0x1b, 0xa5, 0xe4, 0xff, 0x1d, 0xf0, + 0x66, 0x39, 0x3a, 0x92, 0x25, 0xc4, 0x8c, 0x49, 0x8e, 0x05, 0x91, + 0x47, 0x2f, 0x82, 0x24, 0x6e, 0x52, 0xa0, 0x9c, 0x5a, 0x53, 0x8e, + 0x05, 0x98, 0x03, 0xad, 0x05, 0x0c, 0x0b, 0x0c, 0xac, 0x8e, 0x08, + 0x84, 0x10, 0x8e, 0x09, 0x9a, 0x73, 0x92, 0x23, 0x14, 0xa2, 0xa2, + 0x00, 0xa0, 0x99, 0x20, 0x92, 0x63, 0x14, 0x8e, 0x07, 0xa6, 0x7c, + 0x00, 0x70, 0x22, 0x23, 0xc6, 0x82, 0x28, 0x9f, 0xa1, 0xc8, 0x70, + 0xe0, 0x08, 0x00, 0xa2, 0xad, 0xff, 0x92, 0x22, 0x14, 0x41, 0x77, + 0x70, 0xa0, 0x99, 0x10, 0x88, 0x24, 0x92, 0x62, 0x14, 0xe0, 0x08, + 0x00, 0x21, 0x75, 0x70, 0xcc, 0x9a, 0xad, 0x03, 0x88, 0x22, 0x8e, + 0x07, 0x89, 0x6c, 0x88, 0x24, 0xe0, 0x08, 0x00, 0x26, 0x1a, 0x09, + 0x8e, 0x06, 0x08, 0x4a, 0x01, 0x1d, 0xf0, 0x8e, 0x05, 0x1d, 0x8e, + 0x06, 0x8f, 0x17, 0x8e, 0x05, 0x84, 0x20, 0xcb, 0x70, 0x31, 0xca, + 0x70, 0x91, 0x91, 0x70, 0x81, 0xc9, 0x70, 0x82, 0x69, 0x1d, 0x8e, + 0x09, 0x81, 0x60, 0x22, 0x8e, 0x05, 0x92, 0x49, 0xa1, 0xcc, 0x70, + 0x82, 0x28, 0xa0, 0x30, 0xb3, 0x20, 0xe0, 0x08, 0x00, 0x51, 0xcd, + 0x70, 0xf1, 0xce, 0x70, 0xe1, 0x6e, 0x70, 0xd1, 0x7e, 0x70, 0xb1, + 0xcf, 0x70, 0xa1, 0xd0, 0x70, 0xc1, 0x14, 0x70, 0x41, 0x20, 0x70, + 0xc2, 0x2c, 0xa9, 0x16, 0x23, 0x0a, 0x92, 0xaf, 0xfe, 0xc0, 0x20, + 0x00, 0x82, 0x2b, 0x09, 0x90, 0x88, 0x10, 0xc0, 0x20, 0x00, 0x89, + 0x9b, 0x0c, 0x49, 0x8e, 0x04, 0x10, 0x2d, 0xdb, 0x90, 0x88, 0x20, + 0x8e, 0x04, 0x09, 0x6d, 0xdb, 0x7c, 0xdb, 0x8e, 0x05, 0xa3, 0x2d, + 0x73, 0xb0, 0x8e, 0x05, 0xa3, 0x2d, 0x92, 0x6a, 0x73, 0x8e, 0x04, + 0x17, 0x2e, 0xc8, 0x50, 0x8e, 0x05, 0x30, 0x82, 0x6e, 0xc8, 0xd1, + 0xd1, 0x8e, 0x05, 0xa3, 0x07, 0x2e, 0xf5, 0x8e, 0x07, 0xa3, 0x07, + 0x6e, 0xf5, 0xc0, 0x20, 0x00, 0xa2, 0x2e, 0xf7, 0xf0, 0xaa, 0x8e, + 0x04, 0x0f, 0xa2, 0x6e, 0xf7, 0x91, 0xd2, 0x8e, 0x05, 0x9f, 0x68, + 0x2e, 0xdf, 0x8e, 0x07, 0x53, 0x6e, 0xdf, 0xf1, 0xd3, 0x8e, 0x04, + 0x12, 0xd2, 0x2e, 0xbf, 0xf0, 0xdd, 0x8e, 0x04, 0x45, 0xd2, 0x6e, + 0xbf, 0x0c, 0x1a, 0x0c, 0x0b, 0xe0, 0x0c, 0x00, 0x0c, 0x1b, 0xd1, + 0xd4, 0x70, 0xc2, 0x22, 0x3d, 0xa8, 0x04, 0xd0, 0xcc, 0x20, 0xc2, + 0x62, 0x3d, 0x65, 0x0c, 0x00, 0x1d, 0xf0, 0x82, 0xa0, 0x01, 0x8e, + 0x04, 0x79, 0x2b, 0x09, 0x80, 0x99, 0x8e, 0x04, 0x40, 0x92, 0x6b, + 0x09, 0x7c, 0xb8, 0x8e, 0x04, 0x69, 0x2d, 0xdb, 0x80, 0xbb, 0x8e, + 0x04, 0x3f, 0xb2, 0x6d, 0xdb, 0x0c, 0x29, 0x8e, 0x04, 0x59, 0x2a, + 0x73, 0x8e, 0x07, 0x62, 0x8e, 0x05, 0x81, 0x24, 0xd2, 0x2e, 0xc8, + 0xf0, 0xdd, 0x8e, 0x04, 0x0f, 0xd2, 0x6e, 0xc8, 0xb1, 0xd5, 0x8e, + 0x05, 0xa4, 0x55, 0x2e, 0xf5, 0xb0, 0x8e, 0x06, 0xa4, 0x55, 0x8e, + 0x05, 0x81, 0x24, 0x92, 0x2e, 0xf7, 0x50, 0x8e, 0x06, 0x81, 0x54, + 0x6e, 0xf7, 0x81, 0xd6, 0x8e, 0x04, 0x21, 0xf2, 0x2e, 0xdf, 0x80, + 0xff, 0x8e, 0x04, 0x12, 0xf2, 0x6e, 0xdf, 0x0c, 0x0a, 0x8e, 0x06, + 0x81, 0x12, 0x0b, 0xc1, 0xd7, 0x70, 0x92, 0x22, 0x3d, 0xa8, 0x04, + 0xc0, 0x99, 0x10, 0x92, 0x62, 0x3d, 0x65, 0x03, 0x8e, 0x0a, 0xab, + 0x64, 0x81, 0xb5, 0x70, 0xc1, 0xd8, 0x70, 0x82, 0x28, 0x32, 0xc2, + 0x2c, 0x16, 0x8e, 0x07, 0x89, 0x2f, 0xe5, 0xe7, 0x8e, 0x07, 0xbd, + 0x1c, 0x31, 0xd8, 0x70, 0x21, 0xd9, 0x70, 0x22, 0x63, 0x15, 0x8e, + 0x08, 0xa6, 0x34, 0xda, 0x70, 0x9c, 0x13, 0x52, 0xa0, 0xff, 0x38, + 0x14, 0x28, 0x04, 0x50, 0x33, 0x20, 0x50, 0x22, 0x20, 0x29, 0x04, + 0x39, 0x14, 0x1d, 0xf0, 0x31, 0xdb, 0x70, 0x88, 0x03, 0x89, 0x04, + 0x38, 0x13, 0xc6, 0xfb, 0x8e, 0x05, 0x96, 0x0c, 0xcd, 0x04, 0xbd, + 0x03, 0xd2, 0x22, 0x2d, 0xad, 0x02, 0xbc, 0x3d, 0x26, 0x4d, 0x31, + 0x26, 0x6d, 0x2e, 0x0c, 0x09, 0x90, 0x90, 0x74, 0x17, 0x6c, 0x10, + 0xe2, 0xa1, 0xf3, 0x90, 0x89, 0xf0, 0xa0, 0x88, 0xa0, 0xea, 0x88, + 0x82, 0x08, 0x7f, 0x17, 0xe8, 0x08, 0x0b, 0xf9, 0x0c, 0x4e, 0x0c, + 0x5d, 0xf0, 0xde, 0x83, 0x81, 0xdc, 0x70, 0x88, 0x08, 0xd2, 0x6b, + 0x88, 0x8e, 0x07, 0x89, 0x15, 0x46, 0xf3, 0xff, 0x36, 0x61, 0x00, + 0xfd, 0x07, 0x8e, 0x07, 0xae, 0x02, 0xdc, 0x70, 0xad, 0x02, 0x98, + 0xd1, 0x99, 0x11, 0x28, 0xc1, 0x29, 0x01, 0x88, 0x18, 0x8e, 0x05, + 0x8f, 0x66, 0x8e, 0x09, 0xc3, 0x64, 0xdc, 0x8e, 0x12, 0xb2, 0x00, + 0xdd, 0x05, 0xbd, 0x03, 0xcd, 0x04, 0xed, 0x06, 0x8e, 0x05, 0x38, + 0x88, 0x38, 0x28, 0x43, 0xe0, 0x08, 0x00, 0xcc, 0x96, 0x92, 0x02, + 0x75, 0x66, 0x49, 0x04, 0x0c, 0x3a, 0xa2, 0x44, 0x8e, 0x09, 0xa8, + 0x04, 0xe0, 0x70, 0xd1, 0xdd, 0x70, 0x21, 0xdb, 0x70, 0xa1, 0xdf, + 0x70, 0x81, 0xdc, 0x70, 0xb1, 0x97, 0x70, 0x31, 0xda, 0x70, 0xe2, + 0x2b, 0x11, 0xf8, 0x13, 0xc8, 0x3b, 0xc9, 0x18, 0xa9, 0x3b, 0x38, + 0x03, 0xf9, 0x12, 0x39, 0x02, 0x31, 0xde, 0x70, 0x21, 0xe1, 0x70, + 0x98, 0x43, 0x58, 0x93, 0x59, 0x28, 0x29, 0x93, 0xd2, 0x6b, 0x11, + 0xe9, 0x08, 0x49, 0x43, 0x99, 0x38, 0x8e, 0x08, 0x8f, 0x4c, 0x19, + 0x40, 0x70, 0xf4, 0x81, 0xe2, 0x70, 0x50, 0xb6, 0x80, 0x42, 0x28, + 0x00, 0x76, 0x96, 0x0d, 0x90, 0xcb, 0xc0, 0x1b, 0x99, 0xa2, 0x0c, + 0x00, 0xa2, 0x4c, 0x0e, 0x90, 0x90, 0xf4, 0x81, 0x00, 0x70, 0x0c, + 0x0a, 0x82, 0x28, 0xd0, 0x8e, 0x06, 0x93, 0x2e, 0x00, 0x70, 0x8e, + 0x0d, 0xb2, 0x4f, 0x2b, 0xd6, 0xcb, 0xc5, 0x32, 0x45, 0x0d, 0x91, + 0x57, 0x70, 0x22, 0x45, 0x0c, 0x92, 0x29, 0xa0, 0x49, 0x5a, 0x99, + 0x4a, 0xc9, 0x1a, 0xcd, 0x07, 0x82, 0x2b, 0xd1, 0xb1, 0xe3, 0x8e, + 0x08, 0xb2, 0x0d, 0x36, 0x41, 0x00, 0x31, 0xe4, 0x70, 0x21, 0xe5, + 0x70, 0x22, 0x63, 0xeb, 0x8e, 0x08, 0x8a, 0x58, 0xe6, 0x8e, 0x04, + 0x43, 0x32, 0x22, 0x28, 0xc0, 0x20, 0x00, 0x22, 0x22, 0x30, 0x66, + 0x33, 0x02, 0x26, 0x32, 0x8e, 0x05, 0xb6, 0x5c, 0x0c, 0x32, 0x8e, + 0x05, 0x83, 0x58, 0xe5, 0xfd, 0xff, 0xa7, 0x12, 0x0e, 0x81, 0x8e, + 0x05, 0x26, 0x22, 0x68, 0x8e, 0x05, 0x26, 0x68, 0x30, 0x8e, 0x10, + 0xa8, 0x58, 0x98, 0x09, 0xb7, 0x78, 0x16, 0x9c, 0x39, 0xa8, 0x29, + 0x8c, 0xaa, 0xb8, 0x13, 0x8c, 0x6b, 0x0c, 0x1a, 0x65, 0xfc, 0xff, + 0x06, 0x01, 0x00, 0x0c, 0x3a, 0xe5, 0xfb, 0xff, 0x8e, 0x05, 0x8d, + 0x35, 0xe7, 0x8e, 0x06, 0x81, 0x02, 0x36, 0x61, 0x00, 0x0c, 0xbb, + 0x4b, 0xc1, 0x81, 0x3c, 0x70, 0x41, 0x1b, 0x70, 0x0c, 0x09, 0x99, + 0x01, 0x99, 0x11, 0xa8, 0x04, 0x82, 0x28, 0x2e, 0x8e, 0x05, 0xbd, + 0x7c, 0x61, 0xe8, 0x70, 0xb8, 0x04, 0x67, 0x92, 0x10, 0xd8, 0x11, + 0xc8, 0x4b, 0xcc, 0xbd, 0x26, 0x1c, 0x09, 0xe8, 0x2b, 0x8c, 0x4e, + 0x0c, 0x02, 0x1d, 0xf0, 0xc8, 0x4b, 0x66, 0x1c, 0x0f, 0x60, 0xf2, + 0xc0, 0x56, 0xef, 0x0b, 0x38, 0x11, 0x0c, 0x18, 0x30, 0x38, 0x93, + 0x86, 0x2d, 0x00, 0x91, 0xe9, 0x70, 0x0c, 0x03, 0x97, 0x92, 0x8e, + 0x05, 0x81, 0x2b, 0x71, 0x1c, 0x70, 0x0b, 0xdc, 0x16, 0x4d, 0x08, + 0xe8, 0x2b, 0xf1, 0xea, 0x70, 0x16, 0xce, 0x07, 0x0c, 0x15, 0x27, + 0x2f, 0x08, 0x81, 0xeb, 0x70, 0x87, 0x22, 0x02, 0x46, 0x21, 0x00, + 0x91, 0xec, 0x70, 0xa1, 0x42, 0x70, 0x97, 0x12, 0x20, 0xa0, 0xa2, + 0xc0, 0x16, 0x6a, 0x07, 0xb1, 0xed, 0x70, 0xc1, 0xee, 0x70, 0xb7, + 0x12, 0x6d, 0xc7, 0x12, 0x6a, 0x67, 0x92, 0x2a, 0xd8, 0x11, 0x98, + 0x01, 0xd0, 0x95, 0x93, 0x99, 0x01, 0x86, 0x07, 0x00, 0xa8, 0x3b, + 0x98, 0x01, 0x82, 0x27, 0x11, 0xa0, 0x8e, 0x04, 0x0f, 0xe0, 0x08, + 0x00, 0x8c, 0xca, 0xb8, 0x11, 0x8c, 0x8b, 0x82, 0x27, 0x10, 0x8e, + 0x05, 0x8c, 0x62, 0x0c, 0x13, 0x81, 0xef, 0x70, 0xbd, 0x02, 0x88, + 0x18, 0xad, 0x8e, 0x06, 0xb7, 0x6f, 0x90, 0x35, 0x93, 0xdc, 0x33, + 0xb8, 0x04, 0xa2, 0x1b, 0x0e, 0x1b, 0xaa, 0xa2, 0x4b, 0x1c, 0xa0, + 0xa8, 0x41, 0xa2, 0x4b, 0x1d, 0x46, 0x00, 0x00, 0x98, 0x01, 0x8c, + 0xd9, 0xa8, 0x04, 0x92, 0x0a, 0x1f, 0x1b, 0x99, 0x92, 0x4a, 0x1f, + 0x88, 0x67, 0xe0, 0x08, 0x00, 0x2d, 0x03, 0x1d, 0xf0, 0x9d, 0x05, + 0x46, 0xe6, 0xff, 0x8e, 0x05, 0x44, 0x0c, 0x0a, 0x88, 0x18, 0xbd, + 0x02, 0xe0, 0x08, 0x00, 0xb8, 0x04, 0xc8, 0x4b, 0x46, 0xd0, 0x8e, + 0x07, 0x8e, 0x10, 0x91, 0x1b, 0x70, 0x98, 0x09, 0x82, 0x09, 0x14, + 0xa2, 0x09, 0x15, 0xbc, 0x48, 0xbc, 0x2a, 0x41, 0xf0, 0x70, 0x22, + 0x19, 0x0b, 0xb8, 0x04, 0x31, 0x00, 0x70, 0x8c, 0xab, 0x82, 0x23, + 0x5c, 0x8e, 0x06, 0x81, 0x03, 0x09, 0x99, 0x04, 0xa1, 0x03, 0x70, + 0xa2, 0x2a, 0xb3, 0x67, 0x7a, 0x09, 0x82, 0x23, 0x94, 0x8e, 0x07, + 0x85, 0x29, 0x82, 0x23, 0x8e, 0x06, 0xb7, 0x59, 0x8e, 0x05, 0x83, + 0x30, 0x8e, 0x0c, 0x48, 0x38, 0xbc, 0x1a, 0xb1, 0x03, 0x70, 0xa2, + 0x19, 0x0b, 0xb2, 0x2b, 0xb3, 0x21, 0x00, 0x70, 0x67, 0x7b, 0x08, + 0x82, 0x22, 0x93, 0xe0, 0x08, 0x00, 0x46, 0x01, 0x00, 0x82, 0x22, + 0x94, 0xe0, 0x08, 0x00, 0x31, 0xf0, 0x70, 0x98, 0x03, 0xcc, 0xc9, + 0x82, 0x22, 0x5c, 0x8e, 0x05, 0xa2, 0x18, 0x0c, 0x19, 0x99, 0x03, + 0x8e, 0x05, 0xa9, 0x11, 0x8e, 0x0e, 0x48, 0x9c, 0x98, 0x9c, 0x7a, + 0xad, 0x02, 0x8e, 0x05, 0x95, 0x56, 0x0c, 0x0c, 0x82, 0x28, 0x90, + 0x8e, 0x05, 0x8b, 0x5f, 0x81, 0xf1, 0x70, 0x88, 0x38, 0x8e, 0x0b, + 0xba, 0x50, 0xed, 0x05, 0xad, 0x02, 0x4b, 0xc1, 0x39, 0x11, 0x8b, + 0xd1, 0x49, 0x21, 0x0c, 0x1b, 0x81, 0xf2, 0x8e, 0x0a, 0xb7, 0x00, + 0xc1, 0x00, 0xb2, 0xa0, 0x80, 0xad, 0x03, 0xcd, 0x06, 0xd1, 0xf3, + 0x70, 0x9d, 0x05, 0x40, 0xe0, 0x24, 0x51, 0x00, 0x70, 0x82, 0xce, + 0xfe, 0x68, 0x0d, 0x3d, 0x04, 0xba, 0x66, 0x4d, 0x06, 0x66, 0x3e, + 0x01, 0x0b, 0x33, 0x16, 0x38, 0x18, 0xa9, 0xb1, 0xc9, 0xa1, 0x99, + 0xc1, 0xf2, 0xce, 0xfd, 0x16, 0x7f, 0x17, 0xad, 0x01, 0x0c, 0x0b, + 0x82, 0x25, 0x3e, 0x2c, 0x8e, 0x05, 0xa2, 0x4e, 0x0b, 0xc2, 0xa0, + 0x80, 0xa1, 0xf3, 0x70, 0xd2, 0xa0, 0x80, 0xa8, 0x0a, 0x82, 0x25, + 0x3e, 0xda, 0xaa, 0xe0, 0x08, 0x00, 0xd8, 0xc1, 0x8c, 0xad, 0xbd, + 0x0d, 0xad, 0x01, 0x82, 0x25, 0x3f, 0x8e, 0x05, 0xd2, 0x5b, 0xb8, + 0xb1, 0xa2, 0xc1, 0x10, 0x8e, 0x09, 0x0d, 0xa1, 0xa1, 0xf3, 0x70, + 0xc2, 0x8e, 0x06, 0x2f, 0x3f, 0xca, 0xaa, 0xcd, 0x07, 0xe0, 0x08, + 0x00, 0xad, 0x01, 0x2c, 0x0b, 0xd1, 0xf3, 0x70, 0xe2, 0xa0, 0x80, + 0xd8, 0x0d, 0xc2, 0xa1, 0x00, 0xea, 0xdd, 0xed, 0x07, 0x81, 0xf5, + 0x70, 0xe0, 0x08, 0x00, 0xe2, 0xc2, 0xfe, 0x56, 0x5e, 0x0b, 0xe6, + 0x67, 0x02, 0x86, 0x45, 0x00, 0x0c, 0x03, 0x0c, 0x0c, 0x0c, 0x0a, + 0x0c, 0x1d, 0x0c, 0x02, 0xe2, 0xa0, 0xdd, 0x92, 0x04, 0x00, 0xb2, + 0x04, 0x01, 0xe7, 0x19, 0x02, 0x06, 0x21, 0x00, 0x1c, 0x4f, 0xb7, + 0xbf, 0x7f, 0x82, 0x04, 0x02, 0x56, 0x98, 0x07, 0x92, 0x04, 0x03, + 0x0c, 0xff, 0xf7, 0x99, 0x71, 0x82, 0x04, 0x04, 0x92, 0xa0, 0xac, + 0x97, 0x98, 0x68, 0xf2, 0x04, 0x05, 0x66, 0x1f, 0x10, 0x8b, 0x24, + 0xd2, 0x04, 0x06, 0xa2, 0xcb, 0xfa, 0xa0, 0xa0, 0x74, 0xd0, 0xd0, + 0x14, 0x46, 0x14, 0x00, 0xd9, 0x91, 0xa9, 0x81, 0x0c, 0x98, 0x87, + 0x9f, 0x48, 0xeb, 0xb4, 0xa2, 0xa1, 0x5e, 0xd2, 0xa0, 0xee, 0xc1, + 0xf3, 0x70, 0x32, 0x04, 0x06, 0x88, 0x0c, 0x30, 0x30, 0x14, 0xda, + 0x88, 0x32, 0x48, 0x6f, 0xf8, 0x0c, 0xe2, 0x04, 0x01, 0xda, 0xff, + 0xe2, 0xce, 0xfa, 0xe2, 0x4f, 0x90, 0x82, 0x25, 0x3f, 0xc8, 0x0c, + 0x32, 0x04, 0x01, 0xaa, 0xac, 0x32, 0xc3, 0xfa, 0x30, 0x30, 0x74, + 0xda, 0xcc, 0xc2, 0x0c, 0x90, 0xe0, 0x08, 0x00, 0xa8, 0x81, 0xd8, + 0x91, 0xe2, 0xa0, 0xdd, 0xb2, 0x04, 0x01, 0x8b, 0xc4, 0x4a, 0x4b, + 0x2b, 0x44, 0x60, 0x94, 0xc0, 0x5b, 0x99, 0x77, 0xa9, 0x02, 0xc6, + 0xd7, 0xff, 0x46, 0x06, 0x00, 0x0c, 0x0c, 0x82, 0xa0, 0xfe, 0x87, + 0x92, 0x09, 0x2d, 0x06, 0xad, 0x07, 0x30, 0xd4, 0x14, 0x46, 0x01, + 0x00, 0x0c, 0x1d, 0x0c, 0x0a, 0x0c, 0x02, 0x0c, 0x03, 0xac, 0x82, + 0xa9, 0x81, 0xd9, 0x91, 0x2c, 0x09, 0xa7, 0x39, 0x20, 0xbd, 0x02, + 0xa2, 0x21, 0x19, 0xe2, 0x21, 0x1a, 0xd8, 0x81, 0x82, 0x21, 0x18, + 0xf8, 0x91, 0xf2, 0x48, 0x00, 0xd2, 0x4e, 0x00, 0x82, 0x25, 0x3f, + 0xcd, 0x8e, 0x05, 0xa7, 0x20, 0x12, 0x1d, 0xf0, 0x8c, 0x3c, 0x2c, + 0x0b, 0x37, 0xbb, 0xf4, 0x8e, 0x05, 0xcb, 0x35, 0x06, 0x72, 0xc7, + 0xf8, 0x81, 0xf4, 0x70, 0x70, 0x70, 0x74, 0x88, 0x18, 0x70, 0xb3, + 0x41, 0xe0, 0x08, 0x00, 0x06, 0xb7, 0xff, 0x8e, 0x0a, 0x82, 0x17, + 0x46, 0xe9, 0xff, 0x8e, 0x05, 0xb5, 0x34, 0xf6, 0x70, 0x10, 0x4f, + 0x40, 0xc1, 0xf7, 0x70, 0x1c, 0x04, 0xc0, 0x9c, 0x20, 0x76, 0xa4, + 0x22, 0x58, 0x03, 0x4b, 0x33, 0x50, 0x88, 0x75, 0x80, 0x75, 0x01, + 0x60, 0xa5, 0x10, 0x80, 0xaa, 0x11, 0xa0, 0x77, 0x20, 0x50, 0x58, + 0x41, 0x60, 0x55, 0x10, 0x80, 0x55, 0x20, 0x70, 0x55, 0x20, 0x59, + 0x09, 0x4b, 0x99, 0x9d, 0x0c, 0xf1, 0xf9, 0x70, 0x1c, 0x44, 0x82, + 0xa0, 0x40, 0x76, 0xa8, 0x18, 0xd8, 0x29, 0xe8, 0xd9, 0xb8, 0x89, + 0xa8, 0x09, 0xe0, 0xbb, 0x30, 0xd0, 0xaa, 0x30, 0xb0, 0xaa, 0x30, + 0xa0, 0xaa, 0x81, 0xa2, 0x69, 0x10, 0x8e, 0x04, 0x26, 0xa1, 0xf8, + 0x70, 0x10, 0x4b, 0x40, 0xd8, 0x42, 0xe8, 0x32, 0x78, 0x12, 0x88, + 0x02, 0x68, 0x22, 0x69, 0x01, 0x89, 0x21, 0x79, 0x11, 0x5d, 0x0e, + 0x3d, 0x0d, 0x76, 0xa4, 0x28, 0x50, 0xb6, 0x30, 0x80, 0x48, 0x81, + 0xb0, 0xb7, 0x10, 0xb0, 0xb5, 0x30, 0xba, 0x44, 0x4a, 0x43, 0x3d, + 0x05, 0x70, 0xb2, 0x41, 0x5d, 0x06, 0x20, 0x67, 0x01, 0x60, 0x6b, + 0x20, 0x7d, 0x08, 0xb8, 0x09, 0x4b, 0x99, 0x4a, 0xbb, 0xfa, 0xbb, + 0x8d, 0x0b, 0x91, 0xfa, 0x70, 0xb1, 0xfb, 0x8e, 0x04, 0x49, 0xf2, + 0xa0, 0x14, 0x76, 0xaf, 0x25, 0x60, 0xf7, 0x8e, 0x04, 0x38, 0xf0, + 0xf5, 0x30, 0xfa, 0x8e, 0x06, 0x35, 0xf2, 0x8e, 0x07, 0x35, 0x6f, + 0x20, 0x7d, 0x08, 0xf8, 0x0a, 0x4b, 0xaa, 0x4a, 0xff, 0xba, 0xff, + 0x8d, 0x0f, 0xb1, 0xfc, 0x8e, 0x04, 0x32, 0x1c, 0x44, 0x76, 0xa4, + 0x2a, 0x20, 0x47, 0x01, 0x80, 0xa8, 0x81, 0x60, 0xf7, 0x10, 0xaa, + 0xa3, 0x60, 0x37, 0x30, 0x30, 0x35, 0x10, 0x3a, 0xff, 0xfa, 0xaa, + 0x8e, 0x07, 0x39, 0x7d, 0x08, 0x40, 0x6f, 0x20, 0x48, 0x09, 0x4b, + 0x99, 0xaa, 0x44, 0xba, 0x44, 0x8d, 0x04, 0xa8, 0x01, 0xb8, 0x11, + 0xf1, 0xfd, 0x8e, 0x06, 0x3a, 0x3c, 0xc9, 0xc0, 0x99, 0xa0, 0x76, + 0xa4, 0x25, 0x60, 0xc7, 0x8e, 0x04, 0x70, 0xc0, 0xc5, 0x30, 0xca, + 0x8e, 0x06, 0x70, 0xc2, 0x8e, 0x07, 0x70, 0x6c, 0x20, 0x7d, 0x08, + 0xc8, 0x09, 0x4b, 0x99, 0x4a, 0xcc, 0xfa, 0xcc, 0x8d, 0x0c, 0x3a, + 0x4d, 0x5a, 0x9e, 0x7a, 0xfb, 0x6a, 0xca, 0xc9, 0x22, 0xf9, 0x12, + 0x99, 0x32, 0xf8, 0x21, 0x49, 0x42, 0x8a, 0xff, 0xf9, 0x8e, 0x05, + 0xd1, 0x05, 0x36, 0x41, 0x00, 0x81, 0x02, 0x71, 0xa1, 0x01, 0x71, + 0xc1, 0x00, 0x71, 0x91, 0xf1, 0x70, 0x21, 0xfe, 0x70, 0x41, 0x03, + 0x71, 0x31, 0x04, 0x71, 0xf1, 0x1c, 0x70, 0xe1, 0xff, 0x70, 0xe9, + 0x7f, 0x32, 0x64, 0x11, 0x29, 0x2f, 0xb8, 0x29, 0xd8, 0x39, 0xd9, + 0x0c, 0xb9, 0x1c, 0xa9, 0x29, 0x89, 0x39, 0x21, 0x00, 0x70, 0x41, + 0x05, 0x71, 0x31, 0x06, 0x71, 0x39, 0xa4, 0x22, 0x22, 0x56, 0x41, + 0x07, 0x71, 0x31, 0x08, 0x71, 0x28, 0x12, 0x39, 0x74, 0x66, 0x62, + 0x07, 0x81, 0x09, 0x71, 0x1c, 0x03, 0x32, 0x58, 0x8e, 0x06, 0x8c, + 0x00, 0x61, 0x00, 0x61, 0x0a, 0x71, 0x58, 0x06, 0x52, 0x25, 0x26, + 0x81, 0x0f, 0x71, 0xe0, 0x08, 0x00, 0x7c, 0xbc, 0x71, 0x00, 0x70, + 0x4d, 0x0a, 0xb8, 0x06, 0xa2, 0xa1, 0x24, 0xaa, 0xab, 0x92, 0x2b, + 0x1f, 0x82, 0x27, 0x6e, 0xc0, 0x99, 0x10, 0x92, 0x6b, 0x1f, 0xe0, + 0x08, 0x00, 0xa1, 0x0b, 0x71, 0x49, 0x01, 0x16, 0xa2, 0x08, 0xb1, + 0x00, 0x70, 0x48, 0x52, 0x78, 0x62, 0x3d, 0x04, 0xcc, 0x47, 0xc2, + 0xa0, 0xff, 0x86, 0x00, 0x00, 0xc2, 0x07, 0x00, 0xa1, 0x0c, 0x71, + 0x82, 0x2b, 0xa1, 0xd2, 0x02, 0x02, 0xb2, 0x02, 0x01, 0x00, 0xdd, + 0x11, 0x80, 0xbb, 0x11, 0xd0, 0xbb, 0x20, 0xb0, 0xbc, 0x20, 0x8e, + 0x05, 0xa6, 0x5d, 0xa8, 0x06, 0xf2, 0x07, 0x00, 0xe2, 0x2a, 0x38, + 0x0c, 0x4c, 0xf7, 0x5e, 0x18, 0x82, 0x02, 0x31, 0xdc, 0x28, 0x92, + 0x2a, 0x50, 0xf0, 0x99, 0x11, 0x97, 0x34, 0x66, 0xb2, 0x2a, 0x1f, + 0xc0, 0xbb, 0x20, 0xb2, 0x6a, 0x1f, 0x1d, 0xf0, 0x98, 0x72, 0x8c, + 0x69, 0xd8, 0x29, 0xd0, 0xd0, 0x14, 0x16, 0xfd, 0x04, 0x92, 0x02, + 0x31, 0xd2, 0xa0, 0xff, 0x66, 0x29, 0x48, 0xe2, 0x2a, 0x48, 0xb8, + 0x85, 0x07, 0xee, 0x20, 0xf2, 0x2a, 0x51, 0xb0, 0xc1, 0x41, 0xd0, + 0xff, 0xc0, 0x16, 0x5f, 0x09, 0x47, 0x3c, 0x02, 0x86, 0x21, 0x00, + 0x3d, 0x0c, 0x46, 0x02, 0x00, 0x16, 0x33, 0x06, 0x37, 0xba, 0x6f, + 0xa8, 0x06, 0xb8, 0x85, 0x81, 0x0d, 0x71, 0xc2, 0x2a, 0x2b, 0x98, + 0x01, 0x99, 0x45, 0xb0, 0x38, 0x83, 0x39, 0x55, 0x8c, 0x8c, 0xa2, + 0x2a, 0x2c, 0x8e, 0x05, 0x98, 0x64, 0xa8, 0x06, 0x22, 0x2a, 0x26, + 0x1d, 0xf0, 0xb1, 0x0e, 0x71, 0x66, 0x19, 0x13, 0xe2, 0x2a, 0x51, + 0xd7, 0x9e, 0x43, 0x47, 0xbb, 0xee, 0xf2, 0x2a, 0x1f, 0xc0, 0xff, + 0x20, 0xf2, 0x8e, 0x04, 0x78, 0x47, 0xbb, 0x0c, 0x82, 0x2a, 0x1f, + 0xc0, 0x88, 0x20, 0x82, 0x8e, 0x04, 0x0e, 0x1d, 0xf0, 0xc2, 0x2a, + 0x4f, 0x98, 0x01, 0xb2, 0x2a, 0x50, 0xc0, 0x99, 0xc0, 0xb7, 0x39, + 0xef, 0xb8, 0x85, 0x86, 0xe8, 0xff, 0xa8, 0x06, 0xc2, 0x2a, 0x27, + 0x8c, 0x6c, 0xa2, 0x2a, 0x28, 0x0c, 0x3b, 0xe0, 0x0c, 0x8e, 0x05, + 0xad, 0x46, 0xb8, 0x85, 0x40, 0x3b, 0x63, 0xc6, 0xe1, 0xff, 0xd1, + 0x0b, 0x71, 0x47, 0x3d, 0x81, 0x86, 0xf1, 0xff, 0x47, 0xbc, 0x0e, + 0xc0, 0xe4, 0xc0, 0xe9, 0x52, 0xa2, 0x2a, 0x50, 0xa7, 0x3e, 0xb8, + 0xa9, 0x52, 0x1d, 0xf0, 0xf1, 0x0b, 0x71, 0x47, 0xbf, 0xae, 0x32, + 0xd4, 0xd8, 0x06, 0x8e, 0x07, 0x9c, 0x64, 0x41, 0x24, 0x70, 0x0c, + 0x03, 0xb2, 0x04, 0x00, 0x21, 0x20, 0x70, 0xa6, 0x1b, 0x1c, 0xa8, + 0x02, 0x82, 0x2a, 0x1e, 0x82, 0x08, 0x03, 0x47, 0x68, 0x0a, 0x1c, + 0x0b, 0x81, 0x12, 0x71, 0xe0, 0x08, 0x00, 0xb2, 0x04, 0x00, 0x4b, + 0x22, 0x1b, 0x33, 0xb7, 0x23, 0xe2, 0x81, 0x00, 0x70, 0xa1, 0x10, + 0x71, 0x82, 0x28, 0xa0, 0xb1, 0x8e, 0x05, 0x9e, 0x7d, 0x81, 0x11, + 0x71, 0x82, 0x28, 0x17, 0x0c, 0x3a, 0x8e, 0x0b, 0xc3, 0x1c, 0x8e, + 0x06, 0x12, 0xea, 0x8e, 0x08, 0x8b, 0x68, 0x41, 0x00, 0x70, 0xa1, + 0x13, 0x71, 0x82, 0x24, 0x6e, 0x52, 0x22, 0x1e, 0xe0, 0x08, 0x00, + 0x92, 0x05, 0x03, 0x47, 0x69, 0x22, 0xad, 0x02, 0x8e, 0x08, 0x5a, + 0x8e, 0x0b, 0x41, 0xcd, 0x03, 0xa1, 0x0c, 0x71, 0x82, 0x24, 0xa1, + 0xb1, 0x14, 0x8e, 0x04, 0x19, 0x8e, 0x08, 0x91, 0x58, 0x88, 0x43, + 0x82, 0x28, 0x1f, 0xc7, 0x78, 0x06, 0xbd, 0x04, 0x8e, 0x07, 0xc7, + 0x02, 0x36, 0x41, 0x00, 0xbc, 0x82, 0xb8, 0x62, 0xbc, 0x4b, 0x82, + 0x0b, 0x01, 0x92, 0x0b, 0x02, 0x66, 0x18, 0x2c, 0xec, 0x99, 0xc1, + 0x15, 0x71, 0xc8, 0x0c, 0xa2, 0xa0, 0xc0, 0xc7, 0x0a, 0x1f, 0xa1, + 0x16, 0x71, 0xc8, 0x0a, 0xd1, 0x17, 0x71, 0xac, 0x0c, 0xb2, 0x0b, + 0x00, 0xda, 0xbb, 0xb2, 0x0b, 0x00, 0x0c, 0x09, 0x66, 0x3b, 0x05, + 0xe2, 0x0a, 0x04, 0x66, 0x3e, 0x06, 0x46, 0x01, 0x8e, 0x05, 0x82, + 0x24, 0x0c, 0x19, 0xb2, 0x4a, 0x04, 0x46, 0x00, 0x00, 0x0c, 0x09, + 0x0c, 0x0e, 0xd1, 0x18, 0x71, 0x66, 0x19, 0x1f, 0xf8, 0x2a, 0x92, + 0x0a, 0x0c, 0xdc, 0xaf, 0xdc, 0xa9, 0x9c, 0x6c, 0xb2, 0x0a, 0x0d, + 0xf6, 0x9b, 0x11, 0xe9, 0x0d, 0x0c, 0xa9, 0x1b, 0x8b, 0x82, 0x4a, + 0x0d, 0x92, 0x4a, 0x0c, 0x86, 0x00, 0x00, 0x92, 0x0a, 0x0c, 0x8c, + 0x89, 0xcc, 0x6c, 0x1b, 0x99, 0x90, 0x90, 0x74, 0x92, 0x4a, 0x0c, + 0x0c, 0x1f, 0xac, 0xe9, 0xb8, 0x4a, 0xec, 0xab, 0xac, 0x8c, 0x32, + 0xa1, 0x08, 0xf9, 0x4a, 0x41, 0x0a, 0x71, 0x21, 0x00, 0x70, 0xa8, + 0x04, 0x82, 0x22, 0x6e, 0x3a, 0xaa, 0x8e, 0x05, 0x8c, 0x0b, 0xc1, + 0x19, 0x71, 0x0c, 0x0d, 0x8e, 0x04, 0x11, 0x6d, 0x8e, 0x06, 0x11, + 0x12, 0x1d, 0xf0, 0xc8, 0x0d, 0xb2, 0x0a, 0x14, 0x8c, 0x6c, 0x1b, + 0x8e, 0x05, 0xdd, 0x76, 0x4a, 0x14, 0xb6, 0x6b, 0x06, 0xf9, 0x2a, + 0x0c, 0x09, 0x92, 0x4a, 0x0c, 0xe9, 0x0d, 0x0c, 0x02, 0xcc, 0x49, + 0xe2, 0x4a, 0x14, 0x06, 0x01, 0x00, 0x0b, 0xc9, 0xc2, 0x4a, 0x0c, + 0x8e, 0x05, 0x81, 0x64, 0x61, 0x24, 0x70, 0x21, 0x20, 0x70, 0xb2, + 0x06, 0x00, 0x0c, 0x03, 0xac, 0x5b, 0x51, 0x1a, 0x71, 0x0c, 0x04, + 0xa8, 0x02, 0x82, 0x0a, 0x03, 0x66, 0x38, 0x12, 0x82, 0x25, 0x1c, + 0xe0, 0x08, 0x00, 0xcc, 0x34, 0x4d, 0x0a, 0x86, 0x00, 0x00, 0x47, + 0x9a, 0x0d, 0xb2, 0x06, 0x8e, 0x06, 0x83, 0x1d, 0x33, 0xdd, 0x8e, + 0x05, 0x81, 0x5c, 0x12, 0x1d, 0xf0, 0x00, 0x36, 0xa1, 0x00, 0x0c, + 0x09, 0x61, 0x20, 0x70, 0xe1, 0x0d, 0x70, 0x71, 0x00, 0x70, 0xd1, + 0x1b, 0x71, 0x82, 0xc3, 0xfb, 0x0c, 0x1f, 0xc1, 0x0a, 0x71, 0x0c, + 0x25, 0x59, 0x51, 0xc8, 0x0c, 0xf9, 0x81, 0x52, 0x2c, 0x13, 0xa2, + 0x2c, 0x12, 0xa9, 0x61, 0x16, 0x88, 0x31, 0xc1, 0x24, 0x70, 0x2d, + 0x06, 0xc2, 0x0c, 0x00, 0x3d, 0x09, 0x16, 0x8c, 0x29, 0x0c, 0x0b, + 0xb9, 0x91, 0x48, 0x02, 0xd2, 0x04, 0x03, 0xd2, 0xcd, 0xfd, 0x56, + 0xfd, 0x1c, 0xad, 0x04, 0x81, 0x1a, 0x71, 0x92, 0x24, 0x1e, 0x82, + 0x28, 0x1c, 0x99, 0x71, 0xe0, 0x08, 0x00, 0xc2, 0x05, 0x01, 0x6d, + 0x0a, 0x66, 0x2c, 0x04, 0x0c, 0x1e, 0x46, 0x07, 0x00, 0x26, 0x1c, + 0x09, 0xb8, 0x75, 0x0c, 0x0e, 0xa7, 0x9b, 0x02, 0x06, 0x04, 0x00, + 0xf1, 0x0a, 0x71, 0xf8, 0x0f, 0x0c, 0x1d, 0xf8, 0xff, 0x0c, 0x0e, + 0x60, 0xff, 0xc0, 0xf0, 0xed, 0x83, 0x26, 0x2c, 0x02, 0x86, 0x26, + 0x00, 0x81, 0x0a, 0x71, 0x88, 0x08, 0x88, 0xf8, 0x67, 0x18, 0x02, + 0x46, 0x23, 0x00, 0x92, 0x04, 0x01, 0xe9, 0xa1, 0x26, 0x29, 0x02, + 0x86, 0x20, 0x00, 0x8e, 0x06, 0x87, 0x56, 0xe8, 0xa1, 0xf8, 0x45, + 0xc8, 0x55, 0xf0, 0xda, 0xc0, 0xf1, 0x0d, 0x71, 0xb1, 0x1c, 0x71, + 0xf0, 0xfc, 0xc0, 0xf0, 0xcb, 0x83, 0xc7, 0xbd, 0x61, 0xa1, 0x0c, + 0x71, 0xb1, 0x1d, 0x71, 0xd0, 0xcc, 0xc0, 0x82, 0x27, 0xa1, 0xc9, + 0x41, 0x8e, 0x05, 0xa8, 0x21, 0xb1, 0xb3, 0x70, 0xe1, 0x1e, 0x71, + 0xd8, 0x41, 0x2c, 0x0c, 0xe0, 0xdd, 0x63, 0xd0, 0xd0, 0xf4, 0xc0, + 0x20, 0x00, 0xf1, 0x1f, 0x71, 0xe1, 0xb0, 0x70, 0x81, 0xb2, 0x70, + 0xe2, 0x2e, 0x98, 0x88, 0x18, 0xfa, 0xee, 0x8e, 0x05, 0x28, 0x1c, + 0x0b, 0x81, 0x29, 0x8e, 0x04, 0x5b, 0x82, 0x27, 0x6e, 0xa1, 0x13, + 0x8e, 0x04, 0x09, 0xa1, 0x13, 0x71, 0x0c, 0x0b, 0xc1, 0x1f, 0x71, + 0x82, 0x27, 0x8e, 0x07, 0xb3, 0x6a, 0x0e, 0x0c, 0x19, 0x99, 0x91, + 0xc2, 0x05, 0x01, 0xf1, 0x16, 0x71, 0xf2, 0x0f, 0x15, 0x66, 0x1c, + 0x14, 0xb1, 0x0a, 0x71, 0xb8, 0x0b, 0xb8, 0xfb, 0x67, 0x9b, 0x0a, + 0xd2, 0x04, 0x02, 0x66, 0x4d, 0x04, 0x0c, 0x1d, 0x46, 0x00, 0x00, + 0x0c, 0x0d, 0x9c, 0xdf, 0xdc, 0xbc, 0x88, 0x75, 0x92, 0x16, 0x00, + 0x82, 0x18, 0x00, 0x97, 0x18, 0x11, 0x91, 0x0a, 0x71, 0x98, 0x09, + 0x98, 0xf9, 0x67, 0x99, 0x07, 0xb2, 0x04, 0x02, 0x66, 0x4b, 0x01, + 0x0c, 0x1d, 0x16, 0xcd, 0x0b, 0x8e, 0x06, 0x81, 0x47, 0xa0, 0xe8, + 0x41, 0x8b, 0xd1, 0xa2, 0x4d, 0x00, 0xe2, 0x4d, 0x01, 0xe0, 0xe8, + 0x41, 0xe2, 0x4d, 0x02, 0x8e, 0x05, 0x06, 0x03, 0xc2, 0x05, 0x01, + 0xbd, 0x0a, 0xec, 0xbc, 0xf8, 0x75, 0x82, 0x16, 0x00, 0xf2, 0x1f, + 0x00, 0x87, 0x1f, 0x21, 0x8e, 0x06, 0x2f, 0xc1, 0x1c, 0x71, 0xd8, + 0x61, 0xb8, 0x21, 0xd8, 0x3d, 0x81, 0x20, 0x71, 0xa0, 0xdd, 0xc0, + 0xd7, 0x38, 0x01, 0xcd, 0x0d, 0xc2, 0xdc, 0x28, 0xc9, 0x01, 0x86, + 0x01, 0x00, 0xc8, 0x55, 0xc2, 0xdc, 0x10, 0xc9, 0x01, 0xc9, 0x11, + 0xa1, 0x21, 0x71, 0x98, 0x81, 0x82, 0x27, 0xa1, 0x92, 0x41, 0x0c, + 0xe0, 0x08, 0x00, 0xa1, 0x21, 0x71, 0xb8, 0x11, 0x82, 0x27, 0xa1, + 0xc2, 0x01, 0x8e, 0x04, 0x0e, 0xad, 0x04, 0x61, 0x22, 0x71, 0xbd, + 0x01, 0x82, 0x26, 0x1f, 0xc2, 0x8e, 0x05, 0xe2, 0x0e, 0xbd, 0x0a, + 0x82, 0x27, 0xa0, 0xa1, 0x23, 0x8e, 0x04, 0x5c, 0x61, 0x22, 0x71, + 0x82, 0x26, 0x2b, 0x8e, 0x08, 0xaa, 0x55, 0x8b, 0x8e, 0x06, 0x82, + 0x0c, 0x0c, 0x19, 0x99, 0x91, 0xc1, 0x24, 0x70, 0xc2, 0x0c, 0x8e, + 0x05, 0x84, 0x28, 0xc7, 0xb3, 0x02, 0x86, 0x86, 0xff, 0x06, 0x2c, + 0x00, 0x16, 0x9e, 0xfe, 0x98, 0x71, 0x92, 0x09, 0x03, 0x17, 0xe9, + 0xe1, 0x07, 0xe9, 0xde, 0xad, 0x04, 0x0c, 0x1b, 0x0c, 0x0c, 0x81, + 0xb5, 0x70, 0x0c, 0x0d, 0x82, 0x28, 0x1a, 0x0c, 0x6e, 0x8e, 0x06, + 0xa5, 0x11, 0x9a, 0x10, 0x8e, 0x04, 0x18, 0x8e, 0x07, 0x47, 0x1b, + 0xb9, 0x91, 0x46, 0x02, 0x8e, 0x04, 0x58, 0x2b, 0x8e, 0x06, 0x11, + 0xe1, 0x16, 0x71, 0xc1, 0x0d, 0x70, 0x0c, 0x0d, 0xc2, 0x0c, 0x00, + 0xd9, 0x0e, 0x16, 0xbc, 0xf9, 0xa5, 0xd4, 0xff, 0x16, 0x5a, 0xf9, + 0xd1, 0x15, 0x71, 0xe1, 0x16, 0x71, 0xd8, 0x0d, 0xe2, 0x0e, 0x16, + 0x47, 0xed, 0x02, 0x07, 0x6e, 0x84, 0xf2, 0x04, 0x01, 0x82, 0x04, + 0x02, 0x66, 0x2f, 0x02, 0x06, 0xde, 0xff, 0x66, 0x38, 0x02, 0x86, + 0xdc, 0xff, 0x8e, 0x05, 0x82, 0x4c, 0x92, 0x09, 0x72, 0x16, 0x79, + 0xf6, 0xad, 0x04, 0x0c, 0x0c, 0x91, 0x1c, 0x71, 0xd1, 0x25, 0x71, + 0xb1, 0x24, 0x71, 0x81, 0xb2, 0x70, 0xb2, 0x1b, 0x00, 0x88, 0x38, + 0xb2, 0xcb, 0xb4, 0xb0, 0xd9, 0x83, 0xb1, 0x70, 0x70, 0x8e, 0x04, + 0x76, 0x1c, 0xc9, 0x91, 0xc6, 0xcf, 0xff, 0x0c, 0x0d, 0xd9, 0x91, + 0xe8, 0x91, 0x81, 0x0d, 0x70, 0xfc, 0x0e, 0xc1, 0x0a, 0x71, 0xc8, + 0x0c, 0xf2, 0x2c, 0x13, 0xcc, 0xaf, 0xd2, 0x2c, 0x26, 0xd2, 0x0d, + 0x01, 0xd2, 0xcd, 0xfe, 0x56, 0x5d, 0x11, 0xd2, 0x2c, 0x2d, 0x9c, + 0x1d, 0xa2, 0x2c, 0x2e, 0xe8, 0x81, 0xf2, 0x05, 0x01, 0xb8, 0x51, + 0xf2, 0xcf, 0xfe, 0xf0, 0xbe, 0x83, 0xe0, 0x0d, 0x00, 0x0c, 0x62, + 0x1d, 0xf0, 0x82, 0x08, 0x00, 0x21, 0x26, 0x71, 0xac, 0x88, 0x98, + 0x02, 0x9c, 0xc9, 0x82, 0x27, 0x6e, 0xa1, 0x27, 0x8e, 0x05, 0x84, + 0x19, 0x27, 0x71, 0x0c, 0x0b, 0xd2, 0xa3, 0xe8, 0xc8, 0x02, 0x82, + 0x27, 0x6d, 0xd0, 0xcc, 0x82, 0x8e, 0x05, 0x84, 0x1e, 0xf1, 0x1b, + 0x71, 0x0c, 0x0e, 0xe2, 0x4f, 0x00, 0x0c, 0x52, 0x1d, 0xf0, 0x42, + 0xa1, 0x08, 0x31, 0x28, 0x71, 0x66, 0x32, 0x0f, 0x8d, 0x0d, 0x82, + 0x08, 0x00, 0x9d, 0x0e, 0x16, 0x88, 0x0b, 0x92, 0x09, 0x00, 0x16, + 0x29, 0x0b, 0x0c, 0xea, 0xa7, 0x92, 0x5a, 0xb1, 0x1b, 0x71, 0xb2, + 0x0b, 0x00, 0x56, 0x1b, 0x05, 0xe1, 0x1b, 0x71, 0xd8, 0x81, 0xd2, + 0x4e, 0x00, 0x16, 0x15, 0x16, 0xf8, 0x03, 0x16, 0x8f, 0x15, 0x82, + 0x0c, 0x72, 0x16, 0x28, 0x15, 0x82, 0x27, 0x6e, 0x4a, 0x8e, 0x05, + 0xbb, 0x20, 0x0b, 0x0c, 0x0d, 0x82, 0x27, 0x6d, 0xe2, 0xa3, 0xe8, + 0xa1, 0x0a, 0x71, 0xc8, 0x03, 0xa8, 0x0a, 0xe0, 0xcc, 0x82, 0x4a, + 0x8e, 0x05, 0x88, 0x13, 0x52, 0x0c, 0x0d, 0xf2, 0xa0, 0xc0, 0xe1, + 0x15, 0x71, 0xc1, 0x16, 0x71, 0xe8, 0x0e, 0xb8, 0x4c, 0xf0, 0xee, + 0x10, 0xe0, 0xbd, 0x93, 0xb9, 0x4c, 0x1d, 0xf0, 0x0c, 0xdf, 0xf7, + 0x92, 0x41, 0xa8, 0x61, 0x65, 0xb1, 0xff, 0x56, 0x1a, 0x04, 0x88, + 0x61, 0xac, 0xb8, 0x91, 0x0d, 0x70, 0x92, 0x09, 0x00, 0xac, 0x39, + 0x21, 0x15, 0x70, 0x0c, 0x1a, 0x82, 0x22, 0x4b, 0x8e, 0x06, 0xbc, + 0x4b, 0x0b, 0x0c, 0x1c, 0xa8, 0x61, 0x82, 0x22, 0x12, 0xa8, 0x6a, + 0x8e, 0x06, 0xb7, 0x7d, 0x22, 0x4b, 0x8e, 0x05, 0x9a, 0x71, 0x28, + 0x81, 0x0c, 0x69, 0x50, 0x29, 0x93, 0x1d, 0xf0, 0x0c, 0x52, 0x8e, + 0x06, 0x88, 0x0e, 0x8e, 0x04, 0x08, 0x8e, 0x06, 0x83, 0x7f, 0xac, + 0x5c, 0x2d, 0x06, 0x0c, 0x09, 0x76, 0x9c, 0x1c, 0xc8, 0x02, 0xd2, + 0x0c, 0x03, 0x4b, 0x22, 0x66, 0x3d, 0x10, 0xc2, 0x2c, 0x1e, 0xc2, + 0x0c, 0x03, 0x07, 0xec, 0x05, 0x37, 0xec, 0x02, 0x47, 0x6c, 0x01, + 0x0c, 0x19, 0x3d, 0xf0, 0x8e, 0x05, 0x8a, 0x19, 0x56, 0xb9, 0x08, + 0xd1, 0x0d, 0x70, 0xd2, 0x0d, 0x00, 0x8c, 0xfd, 0xa1, 0x1b, 0x8e, + 0x05, 0x85, 0x0b, 0x6e, 0x92, 0x4a, 0x00, 0xa2, 0xca, 0xc0, 0xe0, + 0x08, 0x00, 0x16, 0xb5, 0x07, 0x8e, 0x05, 0x83, 0x04, 0x8e, 0x18, + 0x82, 0x73, 0x88, 0x03, 0x16, 0xd8, 0x05, 0x91, 0x0d, 0x70, 0xc1, + 0x0a, 0x71, 0x92, 0x09, 0x00, 0xc8, 0x0c, 0x8c, 0x49, 0xa2, 0x0c, + 0x72, 0x16, 0xaa, 0x04, 0x8e, 0x21, 0x82, 0x14, 0x8e, 0x05, 0x8b, + 0x47, 0xb2, 0xa0, 0xc0, 0xc7, 0x0b, 0x0a, 0x0c, 0x0d, 0xe1, 0x16, + 0x71, 0xf8, 0x81, 0xf9, 0x0e, 0xd9, 0x4e, 0x8e, 0x05, 0x81, 0x48, + 0x8e, 0x04, 0x04, 0x62, 0x8e, 0x07, 0x81, 0x50, 0x8e, 0x04, 0x04, + 0x62, 0x8e, 0x09, 0xbe, 0x1c, 0x0a, 0x71, 0xa8, 0x0a, 0x92, 0x2a, + 0x26, 0x82, 0x2a, 0x12, 0x97, 0x98, 0x1f, 0x8e, 0x05, 0xcc, 0x6e, + 0x6e, 0xe0, 0x08, 0x00, 0xa1, 0x2a, 0x71, 0x82, 0x22, 0xa0, 0xb1, + 0x2b, 0x8e, 0x0b, 0x8c, 0x64, 0x4a, 0x8e, 0x0a, 0xd3, 0x2c, 0x0a, + 0x71, 0x58, 0x92, 0x88, 0x08, 0x38, 0xb2, 0x82, 0x28, 0x12, 0x48, + 0x12, 0x27, 0x98, 0x0b, 0x81, 0x16, 0x71, 0x8e, 0x09, 0xcc, 0x08, + 0x92, 0x02, 0x00, 0xa8, 0x22, 0x8c, 0x69, 0x0c, 0x1b, 0xa0, 0xab, + 0x93, 0xa2, 0x42, 0x8e, 0x04, 0x3d, 0x88, 0xb8, 0x8e, 0x04, 0x51, + 0x2c, 0x71, 0xc2, 0x02, 0x00, 0x81, 0x00, 0x70, 0xb8, 0x62, 0x82, + 0x28, 0xa1, 0xb2, 0x0b, 0x00, 0x00, 0xcc, 0x11, 0x80, 0xbb, 0x01, + 0xc0, 0xbb, 0x20, 0xb0, 0xb4, 0x20, 0xcd, 0x03, 0xe0, 0x08, 0x00, + 0x8c, 0x53, 0xbd, 0x04, 0xad, 0x05, 0xe0, 0x8e, 0x05, 0xa0, 0x68, + 0x81, 0x00, 0xad, 0x02, 0x29, 0x31, 0x8e, 0x06, 0x87, 0x70, 0x21, + 0x00, 0x70, 0xa9, 0x21, 0x82, 0x22, 0x9f, 0xa1, 0x2d, 0x8e, 0x04, + 0x0e, 0x41, 0x15, 0x70, 0x0c, 0x0b, 0x61, 0x0a, 0x71, 0xd1, 0x16, + 0x71, 0xa2, 0xc3, 0xfc, 0xe1, 0x0d, 0x70, 0x0c, 0x13, 0xe2, 0x0e, + 0x00, 0xc2, 0x0d, 0x1c, 0x98, 0x06, 0xe0, 0xcb, 0x93, 0xc2, 0x4d, + 0x1c, 0x52, 0x29, 0x12, 0x72, 0x29, 0x13, 0x16, 0xba, 0x16, 0xa8, + 0x67, 0xf1, 0x20, 0x70, 0x8c, 0x9a, 0x0c, 0x2b, 0x81, 0x31, 0x8e, + 0x04, 0x39, 0xc6, 0x02, 0x00, 0x0c, 0x2b, 0xad, 0x0f, 0xa8, 0x0a, + 0x8e, 0x06, 0x0f, 0xb1, 0x8e, 0x05, 0x8c, 0x20, 0xa2, 0x0b, 0x00, + 0x39, 0x11, 0x16, 0x2a, 0x08, 0x0c, 0x03, 0x0c, 0x0d, 0xd9, 0x41, + 0x48, 0x02, 0xe2, 0x04, 0x03, 0x66, 0x3e, 0x6a, 0x8e, 0x0c, 0x8b, + 0x37, 0x51, 0xe0, 0x08, 0x00, 0xb8, 0x51, 0xe8, 0x41, 0xd2, 0x1a, + 0x00, 0x81, 0x2e, 0x71, 0xa8, 0x06, 0x82, 0x08, 0x00, 0x1b, 0xfe, + 0xf0, 0xf0, 0x74, 0xc8, 0xfa, 0x80, 0xef, 0x93, 0xc2, 0x1c, 0x00, + 0xe9, 0x41, 0xd7, 0x9c, 0x13, 0x82, 0x1a, 0x80, 0xc2, 0x04, 0x00, + 0x98, 0x11, 0x00, 0x1c, 0x40, 0x00, 0x99, 0xa1, 0x90, 0x88, 0x20, + 0x82, 0x5a, 0x80, 0xd2, 0x0b, 0x03, 0x27, 0xed, 0x19, 0xad, 0x04, + 0x0c, 0x0b, 0x81, 0x15, 0x70, 0x0c, 0x0c, 0x88, 0x28, 0x8e, 0x05, + 0xb0, 0x24, 0xad, 0x04, 0x0c, 0x4b, 0x8e, 0x06, 0x87, 0x7e, 0xb1, + 0x24, 0x8e, 0x04, 0x7e, 0x4b, 0x22, 0x1b, 0x33, 0xa7, 0x33, 0x85, + 0xc6, 0x00, 0x00, 0x0c, 0x09, 0x99, 0x41, 0xa8, 0x06, 0x41, 0x15, + 0x70, 0xa2, 0x0a, 0x72, 0x21, 0x00, 0x70, 0x9c, 0x4a, 0x48, 0x67, + 0xcc, 0x34, 0x41, 0x20, 0x70, 0x48, 0x04, 0x81, 0x15, 0x70, 0x88, + 0xa8, 0x8e, 0x05, 0x89, 0x14, 0x41, 0x15, 0x70, 0x31, 0x2f, 0x71, + 0x9c, 0xb5, 0x92, 0x05, 0x01, 0x66, 0x19, 0x7c, 0x0c, 0x0a, 0x78, + 0x03, 0x82, 0x24, 0x4c, 0x7a, 0xaa, 0xe0, 0x08, 0x00, 0x8c, 0x75, + 0x92, 0x05, 0x01, 0x92, 0xc9, 0xfe, 0x16, 0x39, 0x09, 0xa1, 0x2e, + 0x71, 0xa2, 0x0a, 0x00, 0xb8, 0x41, 0x8c, 0x1a, 0x16, 0x6b, 0x08, + 0x52, 0xa0, 0xe8, 0xa8, 0x06, 0x82, 0x22, 0x6e, 0x5a, 0x8e, 0x06, + 0x8e, 0x58, 0x82, 0x22, 0x6d, 0xa8, 0x06, 0xc8, 0x03, 0xd2, 0xa3, + 0xe8, 0xd0, 0xcc, 0x82, 0x5a, 0xaa, 0x8e, 0x06, 0x8b, 0x52, 0x42, + 0x86, 0x04, 0x00, 0x66, 0x2e, 0xf7, 0xc2, 0x09, 0x36, 0x16, 0x1c, + 0xff, 0x70, 0x3a, 0x93, 0x0c, 0x0d, 0xd2, 0x49, 0x36, 0x2d, 0x03, + 0x26, 0x42, 0x18, 0x82, 0x24, 0x4c, 0x8e, 0x06, 0x9d, 0x11, 0x1a, + 0x0c, 0x0b, 0xc8, 0x06, 0x0c, 0x09, 0x82, 0x24, 0x4b, 0x92, 0x5c, + 0x80, 0x8e, 0x06, 0xc6, 0x4d, 0x06, 0xd2, 0x09, 0x72, 0x16, 0x1d, + 0x04, 0xa2, 0x19, 0x19, 0x86, 0xdd, 0xff, 0x0c, 0x5a, 0xe8, 0x31, + 0xa9, 0x01, 0x66, 0xae, 0xb7, 0x0c, 0x1a, 0x82, 0x24, 0x8e, 0x06, + 0x86, 0x60, 0x28, 0x06, 0x92, 0x22, 0x13, 0x56, 0x09, 0x06, 0x0c, + 0x12, 0xc6, 0xec, 0xff, 0x31, 0x30, 0x71, 0x82, 0x22, 0x8e, 0x0a, + 0xb2, 0x38, 0x0c, 0x1c, 0x82, 0x22, 0x8e, 0x06, 0x8c, 0x4f, 0xc6, + 0xdf, 0xff, 0xa2, 0x29, 0x26, 0xa2, 0x0a, 0x01, 0x66, 0x2a, 0x05, + 0xa2, 0x29, 0x41, 0xc6, 0xca, 0xff, 0xb2, 0x17, 0x19, 0x47, 0x6b, + 0x24, 0xa2, 0x29, 0x16, 0x72, 0x23, 0x00, 0x16, 0x5a, 0x01, 0xc2, + 0x21, 0x02, 0xb2, 0xa3, 0xe8, 0xb0, 0xd7, 0x82, 0xa8, 0x3a, 0xda, + 0xcc, 0xc0, 0xaa, 0xc0, 0xa5, 0x08, 0x08, 0x06, 0xc2, 0xff, 0xa2, + 0x19, 0x18, 0x86, 0xc0, 0x8e, 0x05, 0x06, 0xbe, 0xff, 0x16, 0x45, + 0x05, 0xe2, 0x05, 0x01, 0x66, 0x1e, 0x1a, 0x78, 0x01, 0xa8, 0x65, + 0x0c, 0x0b, 0x82, 0x24, 0x12, 0x8e, 0x05, 0xbf, 0x4f, 0x98, 0x06, + 0x92, 0x29, 0x13, 0x90, 0x73, 0x83, 0x2d, 0x07, 0xc6, 0xcc, 0xff, + 0xa2, 0x02, 0x72, 0xbc, 0x2a, 0xb2, 0x12, 0x19, 0x86, 0x00, 0x00, + 0xb2, 0x12, 0x18, 0xc2, 0x02, 0x36, 0xdc, 0xac, 0xa8, 0x65, 0x8e, + 0x08, 0x2a, 0x28, 0x06, 0x26, 0x5a, 0x4b, 0xa2, 0x22, 0x13, 0x98, + 0x01, 0xa0, 0x39, 0x93, 0x2d, 0x03, 0x06, 0xc1, 0xff, 0x0c, 0x42, + 0xc6, 0xbf, 0xff, 0x0c, 0x52, 0x86, 0xbe, 0xff, 0xb2, 0x22, 0x26, + 0xb2, 0x0b, 0x01, 0x66, 0x2b, 0x05, 0xb2, 0x22, 0x41, 0x46, 0xf1, + 0xff, 0xc2, 0x17, 0x19, 0x47, 0x6c, 0xbc, 0xa2, 0x22, 0x16, 0x16, + 0x3a, 0x01, 0xc2, 0x21, 0x02, 0xa2, 0x2a, 0x03, 0xb2, 0xa3, 0xe8, + 0xc0, 0xaa, 0xc0, 0x25, 0xff, 0x07, 0xbd, 0x0a, 0x46, 0xe9, 0xff, + 0xb2, 0x12, 0x18, 0xc6, 0xe7, 0xff, 0x32, 0x42, 0x36, 0x0c, 0x42, + 0x46, 0xaf, 0x8e, 0x05, 0x9a, 0x44, 0x8e, 0x06, 0x85, 0x44, 0x0c, + 0x0c, 0x71, 0x00, 0x70, 0x0c, 0x84, 0xb1, 0x32, 0x71, 0x6d, 0x0a, + 0x82, 0xc3, 0xf8, 0x0c, 0x9a, 0x31, 0x0a, 0x71, 0x4b, 0xeb, 0x16, + 0xb8, 0x18, 0x98, 0x03, 0x22, 0x29, 0x13, 0x92, 0x09, 0x72, 0x48, + 0x52, 0x58, 0x62, 0xf8, 0xa2, 0xd8, 0x92, 0xd9, 0x01, 0xf9, 0x11, + 0xd2, 0x02, 0x01, 0xcc, 0x19, 0x66, 0x2d, 0x0f, 0xf8, 0x42, 0xf0, + 0xf6, 0xc0, 0x47, 0xbf, 0x05, 0xf0, 0x44, 0xc0, 0x46, 0x00, 0x00, + 0x0c, 0x14, 0xcc, 0x45, 0xf2, 0x8e, 0x05, 0x97, 0x17, 0xf2, 0x05, + 0x00, 0xa1, 0x33, 0x71, 0x82, 0x27, 0xa1, 0xb2, 0x02, 0x02, 0x80, + 0xcd, 0x11, 0x00, 0xbb, 0x11, 0xc0, 0xbb, 0x20, 0xb0, 0xbf, 0x8e, + 0x06, 0x97, 0x14, 0xc8, 0x82, 0xcc, 0x2c, 0xd8, 0x52, 0xac, 0x1d, + 0x82, 0x27, 0x6e, 0x8e, 0x05, 0xe2, 0x51, 0xa1, 0x34, 0x71, 0xb8, + 0x42, 0x82, 0x27, 0xa1, 0xcd, 0x06, 0x8e, 0x05, 0xd0, 0x4a, 0x0c, + 0x0b, 0xcd, 0x04, 0x8e, 0x09, 0x8f, 0x4b, 0x2b, 0xd8, 0x03, 0x4a, + 0x96, 0x92, 0x6d, 0x18, 0x62, 0x6d, 0x19, 0xc2, 0x2d, 0x27, 0x0c, + 0x16, 0x8c, 0xfc, 0xe2, 0x02, 0x01, 0xa2, 0x2d, 0x28, 0xd2, 0xce, + 0xfe, 0xd0, 0xb6, 0x83, 0xe0, 0x0c, 0x00, 0xd8, 0x03, 0x98, 0x62, + 0xcc, 0x49, 0x8e, 0x07, 0x98, 0x0d, 0x09, 0x00, 0xc2, 0x6d, 0x51, + 0xb1, 0x32, 0x71, 0x0c, 0x0c, 0x69, 0x0b, 0x69, 0x1b, 0x8c, 0xf9, + 0x98, 0x72, 0x8c, 0xb9, 0xf8, 0x29, 0xf0, 0xf0, 0x14, 0xcc, 0x4f, + 0x82, 0xa0, 0xff, 0x82, 0x6d, 0x51, 0x22, 0x6d, 0x12, 0xc2, 0x6d, + 0x13, 0x98, 0x11, 0xc9, 0x12, 0x8c, 0x79, 0xa8, 0x01, 0x0c, 0x0b, + 0xe0, 0x09, 0x00, 0xd8, 0x03, 0xa2, 0x2d, 0x17, 0x8c, 0x8a, 0x0c, + 0x1b, 0x8e, 0x06, 0x86, 0x75, 0xd8, 0x03, 0x0c, 0x9a, 0x0c, 0x84, + 0xf1, 0x35, 0x71, 0x0c, 0x0c, 0x92, 0x2d, 0x1f, 0xc9, 0x0f, 0xe2, + 0xcf, 0xfc, 0xb8, 0x0e, 0xc9, 0x0e, 0xb0, 0x4a, 0x83, 0x27, 0x69, + 0x20, 0x82, 0x27, 0x6e, 0x22, 0xa1, 0x24, 0x0c, 0x09, 0x92, 0x6d, + 0x52, 0x2a, 0xad, 0x8e, 0x07, 0x89, 0x67, 0xc8, 0x03, 0x82, 0x27, + 0x6d, 0x2a, 0xac, 0xc2, 0x2c, 0x53, 0xe0, 0x08, 0x00, 0x8e, 0x06, + 0x8a, 0x51, 0x16, 0x2d, 0x05, 0x16, 0xf5, 0x04, 0x8e, 0x05, 0x83, + 0x72, 0x49, 0xf2, 0x05, 0x02, 0x56, 0x3f, 0x04, 0x81, 0x15, 0x71, + 0x88, 0x08, 0x21, 0x16, 0x71, 0x57, 0xe8, 0x05, 0x92, 0x02, 0x16, + 0x17, 0x69, 0x32, 0xa8, 0x03, 0xa2, 0x0a, 0x72, 0xac, 0xaa, 0xad, + 0x05, 0x0c, 0x4b, 0x81, 0xb2, 0x70, 0xc1, 0x36, 0x71, 0x88, 0x28, + 0x0c, 0x2d, 0xe0, 0x08, 0x00, 0x62, 0x42, 0x1c, 0x06, 0x05, 0x00, + 0xa1, 0x34, 0x71, 0xb2, 0xa0, 0xff, 0x82, 0x27, 0xa1, 0x0c, 0x8c, + 0x8e, 0x05, 0x83, 0x1a, 0xb1, 0x32, 0x71, 0xc9, 0x0b, 0x2d, 0x04, + 0x1d, 0xf0, 0x0c, 0xbf, 0x26, 0x42, 0x38, 0xa7, 0x12, 0x35, 0x26, + 0x72, 0x32, 0x26, 0x92, 0x2f, 0xf7, 0x12, 0x2c, 0x66, 0x12, 0xe7, + 0xd8, 0x03, 0xb2, 0x2d, 0x14, 0x16, 0xfb, 0xfd, 0xa2, 0x2d, 0x12, + 0xe2, 0x0b, 0x02, 0x92, 0x0a, 0x02, 0xe7, 0xb9, 0xd3, 0xf2, 0x2d, + 0x18, 0x98, 0x3b, 0xf0, 0xf9, 0xc0, 0xd6, 0x8f, 0xfc, 0x60, 0xb9, + 0xc0, 0xe6, 0x1b, 0x3b, 0xbd, 0x0c, 0x86, 0x0d, 0x8e, 0x05, 0x81, + 0x6b, 0x12, 0x66, 0x92, 0x0b, 0x82, 0x2d, 0x48, 0x67, 0xe8, 0x05, + 0x92, 0x0a, 0x31, 0x16, 0x39, 0x05, 0xf7, 0x92, 0x05, 0xf2, 0x2d, + 0x26, 0xa7, 0x9f, 0x4a, 0x88, 0x0b, 0x56, 0x68, 0xf8, 0x98, 0x0e, + 0x0c, 0x94, 0x16, 0x29, 0xf9, 0xa1, 0x8e, 0x05, 0xa7, 0x4d, 0xa8, + 0x0a, 0xc0, 0x20, 0x00, 0x06, 0xe1, 0xff, 0xc8, 0x5a, 0x81, 0x11, + 0x71, 0xd2, 0x2d, 0x19, 0x82, 0x28, 0x1a, 0xd0, 0xd6, 0xc0, 0x8e, + 0x05, 0xa9, 0x25, 0x8e, 0x05, 0x83, 0x3b, 0x2a, 0x96, 0x92, 0x6a, + 0x18, 0x8e, 0x05, 0x83, 0x33, 0xcd, 0x02, 0x0c, 0x0b, 0x8e, 0x08, + 0x83, 0x33, 0x46, 0xd5, 0xff, 0x0c, 0x82, 0x8e, 0x05, 0x95, 0x60, + 0x81, 0x97, 0x70, 0x88, 0x98, 0x8e, 0x05, 0x8a, 0x73, 0x5d, 0x0a, + 0x0c, 0x27, 0x41, 0x16, 0x71, 0x61, 0x15, 0x71, 0x16, 0xa2, 0x08, + 0x92, 0x02, 0x01, 0xa2, 0x02, 0x02, 0x66, 0x19, 0x2c, 0xec, 0x9a, + 0xb8, 0x06, 0x57, 0xeb, 0x05, 0xc2, 0x04, 0x16, 0x17, 0x6c, 0x1f, + 0xd2, 0x04, 0x1c, 0x9c, 0x9d, 0xe1, 0x0a, 0x71, 0xe8, 0x0e, 0xe2, + 0x0e, 0x72, 0x8c, 0xfe, 0xad, 0x02, 0x8e, 0x0f, 0x82, 0x19, 0x16, + 0xf2, 0x04, 0x92, 0x02, 0x01, 0xa8, 0x24, 0x66, 0x19, 0x47, 0x16, + 0x4a, 0x04, 0xa2, 0x04, 0x16, 0xb8, 0x06, 0xfc, 0xca, 0xc2, 0xa0, + 0xc0, 0xb7, 0x0c, 0x37, 0xe2, 0x23, 0x12, 0xe8, 0xee, 0xd1, 0x37, + 0x71, 0xe0, 0xee, 0xa0, 0x50, 0xee, 0xb0, 0xe8, 0x6e, 0xe7, 0x3d, + 0x24, 0x67, 0x6b, 0x25, 0x91, 0x38, 0x71, 0x0c, 0x18, 0x80, 0x8a, + 0x20, 0xa1, 0x24, 0x71, 0x98, 0x09, 0xf2, 0x1a, 0x00, 0x82, 0x44, + 0x16, 0xf0, 0xf9, 0xc0, 0xb6, 0xcf, 0x15, 0xb2, 0xc9, 0xe1, 0xb2, + 0x5a, 0x00, 0x06, 0x03, 0x8e, 0x05, 0xcd, 0x71, 0x77, 0x6b, 0x05, + 0x70, 0xca, 0x20, 0xc2, 0x44, 0x16, 0x41, 0x24, 0x70, 0x0c, 0x02, + 0xd2, 0x04, 0x00, 0x51, 0x39, 0x71, 0x16, 0x4d, 0xfe, 0x31, 0x20, + 0x70, 0x61, 0x3a, 0x71, 0x30, 0xa2, 0xa0, 0xa8, 0x0a, 0xe2, 0x0a, + 0x01, 0x92, 0x0a, 0x02, 0x66, 0x2e, 0x0f, 0xb2, 0x0a, 0x03, 0x66, + 0x3b, 0x09, 0x8e, 0x07, 0xbe, 0x73, 0x46, 0x03, 0x00, 0x66, 0x39, + 0x0a, 0x8e, 0x05, 0x13, 0x04, 0x88, 0x36, 0x8e, 0x05, 0xe8, 0x2f, + 0x8e, 0x05, 0xbd, 0x24, 0xf4, 0x97, 0x32, 0x8e, 0x05, 0xd1, 0x6b, + 0x8e, 0x05, 0xa9, 0x50, 0x0a, 0x71, 0x88, 0x02, 0x82, 0x28, 0x26, + 0x82, 0x08, 0x01, 0x26, 0x28, 0x65, 0x81, 0x00, 0x70, 0xa1, 0x3b, + 0x71, 0x82, 0x28, 0xa0, 0x8e, 0x05, 0xab, 0x68, 0x60, 0xc3, 0x11, + 0x0c, 0x2d, 0x0c, 0x1b, 0x88, 0x02, 0x0c, 0x0f, 0xe2, 0x28, 0x26, + 0x42, 0x68, 0x41, 0xf2, 0x4e, 0x30, 0xb2, 0x4e, 0x00, 0xd2, 0x4e, + 0x01, 0xa8, 0x02, 0xc9, 0x8e, 0x00, 0x92, 0x2a, 0x1f, 0x52, 0x6a, + 0x48, 0xb0, 0x99, 0x20, 0x92, 0x6a, 0x1f, 0x17, 0x65, 0x2c, 0x0c, + 0xab, 0x5c, 0x09, 0x92, 0x6a, 0x53, 0x60, 0x96, 0x11, 0xb2, 0x6a, + 0x54, 0xc2, 0x0a, 0x75, 0x31, 0x11, 0x71, 0xd0, 0xbc, 0x20, 0xb2, + 0x4a, 0x75, 0xa8, 0x02, 0x88, 0xb3, 0x92, 0x6a, 0x50, 0xe0, 0x08, + 0x00, 0x82, 0x23, 0x17, 0x8e, 0x05, 0xd3, 0x23, 0x1d, 0xf0, 0x1c, + 0xeb, 0xb2, 0x6a, 0x53, 0x46, 0xf4, 0x8e, 0x05, 0xa7, 0x4c, 0xa1, + 0x00, 0x6d, 0x02, 0x0c, 0x07, 0x65, 0x39, 0xff, 0xa9, 0x8e, 0x08, + 0x8d, 0x29, 0x0a, 0x71, 0xe8, 0x02, 0xc2, 0x2e, 0x12, 0xa9, 0x61, + 0x9c, 0x0c, 0xd8, 0x6c, 0x8c, 0xcd, 0x91, 0x17, 0x71, 0x82, 0x0d, + 0x00, 0x9a, 0x88, 0x82, 0x08, 0x00, 0x82, 0x41, 0x00, 0x0c, 0x19, + 0xa1, 0x3c, 0x71, 0x0c, 0x0d, 0xd9, 0x06, 0xa8, 0x0a, 0xd2, 0x6e, + 0x16, 0xbc, 0xda, 0xb2, 0x0e, 0x76, 0xbc, 0x8b, 0x92, 0x4e, 0x38, + 0x2d, 0x0d, 0x1d, 0xf0, 0xc8, 0x41, 0x8c, 0x9c, 0xe8, 0x61, 0xd8, + 0x3c, 0xe0, 0xdd, 0xc0, 0xe8, 0x11, 0xd9, 0x0e, 0xe8, 0x81, 0xdc, + 0xae, 0xf8, 0x02, 0xf2, 0x2f, 0x26, 0xf2, 0x0f, 0x01, 0x66, 0x2f, + 0x10, 0xad, 0x0e, 0x81, 0x11, 0x71, 0xb8, 0x11, 0x82, 0x28, 0x1b, + 0xb8, 0x0b, 0xe0, 0x08, 0x00, 0xa9, 0x81, 0x28, 0x81, 0x1d, 0xf0, + 0x92, 0x0e, 0x38, 0xa1, 0x24, 0x70, 0x16, 0x39, 0x04, 0x32, 0x2e, + 0x15, 0x4d, 0x0a, 0x42, 0x04, 0x00, 0x0c, 0x05, 0x40, 0xf4, 0x90, + 0xbc, 0x0f, 0x82, 0x03, 0x00, 0xc8, 0x23, 0x9c, 0xf8, 0x9c, 0xdc, + 0x98, 0x61, 0xa8, 0x33, 0x90, 0x9a, 0xc0, 0xd6, 0x19, 0x01, 0xb8, + 0x61, 0x81, 0x43, 0x8e, 0x05, 0x8e, 0x3b, 0x24, 0x70, 0xa9, 0x33, + 0x8e, 0x04, 0x2a, 0x0d, 0x40, 0xf4, 0x90, 0x32, 0xc3, 0x34, 0x1b, + 0x55, 0xf7, 0x35, 0xcf, 0xe8, 0x02, 0xd2, 0x4e, 0x38, 0x31, 0x00, + 0x70, 0xa1, 0x3d, 0x71, 0x82, 0x23, 0xa0, 0xb8, 0x61, 0xe0, 0x08, + 0x00, 0x0c, 0x05, 0x79, 0x21, 0x69, 0x11, 0x0c, 0x09, 0x7c, 0xfa, + 0x0c, 0x0b, 0x0c, 0x0c, 0xc9, 0x81, 0xb9, 0x41, 0xa9, 0x51, 0x99, + 0x71, 0x66, 0x25, 0x04, 0xd8, 0x81, 0x56, 0x2d, 0x19, 0x0c, 0x07, + 0x41, 0x24, 0x70, 0x68, 0x02, 0x42, 0x04, 0x00, 0x5a, 0x66, 0x62, + 0x06, 0x68, 0x16, 0x34, 0x17, 0xa2, 0xc6, 0x01, 0x40, 0xb4, 0x20, + 0xe5, 0xb3, 0x07, 0x92, 0xa0, 0x34, 0x38, 0x02, 0x50, 0x84, 0x82, + 0x32, 0x23, 0x15, 0x8a, 0x8a, 0x90, 0x88, 0x82, 0x6d, 0x0a, 0x8a, + 0x33, 0x82, 0x03, 0x00, 0xd2, 0x03, 0x03, 0x16, 0x88, 0x0f, 0xd0, + 0x90, 0x04, 0x56, 0x29, 0x0f, 0xd0, 0xa2, 0x04, 0x56, 0xca, 0x0e, + 0xb8, 0x63, 0xb2, 0x0b, 0x00, 0xc1, 0x19, 0x70, 0x67, 0x1b, 0x07, + 0xc0, 0x20, 0x00, 0xc8, 0x0c, 0xc0, 0x20, 0x00, 0x66, 0x15, 0x39, + 0x91, 0x3e, 0x71, 0x98, 0x09, 0x57, 0x69, 0x18, 0x8e, 0x05, 0x90, + 0x70, 0x1d, 0x8e, 0x05, 0x8c, 0x2d, 0x92, 0x03, 0x03, 0x90, 0x91, + 0x04, 0x56, 0x49, 0x0b, 0x8e, 0x05, 0x1c, 0x17, 0x69, 0x15, 0xd2, + 0x03, 0x02, 0xc8, 0x02, 0xda, 0xcc, 0xc2, 0x0c, 0x6b, 0xa2, 0xa0, + 0xff, 0xa7, 0x1c, 0x05, 0x67, 0x1c, 0x02, 0x86, 0x25, 0x00, 0x98, + 0x33, 0x0c, 0x0c, 0x9c, 0x89, 0xe2, 0x13, 0x19, 0x48, 0x61, 0x27, + 0x6e, 0x16, 0xf8, 0x31, 0x82, 0x01, 0x00, 0x8c, 0x9f, 0x26, 0x18, + 0x07, 0xa1, 0x0d, 0x70, 0xa2, 0x0a, 0x00, 0xcc, 0x3a, 0x0c, 0x1c, + 0x86, 0x11, 0x00, 0x40, 0x49, 0xc0, 0xe6, 0x14, 0x06, 0x0c, 0x04, + 0xc9, 0x91, 0x46, 0x00, 0x00, 0xc9, 0x91, 0xa1, 0x3f, 0x71, 0xbd, + 0x04, 0xd2, 0x03, 0x01, 0xe2, 0x03, 0x02, 0x81, 0x00, 0x70, 0xc8, + 0x63, 0x82, 0x28, 0xa1, 0xc2, 0x0c, 0x00, 0x00, 0xee, 0x11, 0x80, + 0xdd, 0x11, 0xe0, 0xdd, 0x20, 0xd0, 0xcc, 0x20, 0xe0, 0x08, 0x00, + 0x91, 0x40, 0x71, 0xc8, 0x91, 0x98, 0x09, 0xa8, 0x51, 0x47, 0xb9, + 0xba, 0xa7, 0xb4, 0x03, 0x39, 0x41, 0x49, 0x51, 0xac, 0x7c, 0xc2, + 0x03, 0x02, 0xe8, 0x71, 0xc7, 0x3e, 0x37, 0x66, 0x15, 0x1d, 0xe7, + 0x9c, 0x0f, 0x88, 0x81, 0x82, 0x08, 0x01, 0xcc, 0x78, 0xa8, 0x81, + 0x98, 0x63, 0xa8, 0x6a, 0xa7, 0x19, 0x1b, 0x66, 0x25, 0x07, 0xb1, + 0x41, 0x71, 0xb8, 0x0b, 0x37, 0xeb, 0x1d, 0x41, 0x24, 0x70, 0x42, + 0x04, 0x00, 0x1b, 0x77, 0x47, 0xb7, 0x02, 0x06, 0xb6, 0xff, 0x46, + 0x12, 0x00, 0x82, 0x13, 0x19, 0x07, 0x68, 0xdd, 0x39, 0x81, 0xc9, + 0x71, 0x86, 0xf5, 0xff, 0x8e, 0x05, 0x84, 0x2c, 0xbc, 0x4c, 0xd8, + 0x6c, 0xbc, 0x0d, 0xe2, 0x2e, 0x15, 0x62, 0x0d, 0x00, 0x3c, 0x4d, + 0xd0, 0xb6, 0xc1, 0xba, 0xbe, 0xb7, 0x9c, 0x21, 0x31, 0x24, 0x70, + 0x32, 0x03, 0x00, 0x60, 0x33, 0x90, 0xd0, 0x33, 0x82, 0x3a, 0x3e, + 0xc2, 0x03, 0x00, 0x8c, 0xdc, 0xd2, 0x03, 0x03, 0x07, 0xed, 0x08, + 0x27, 0xed, 0x05, 0x39, 0x81, 0x0c, 0x18, 0x89, 0x21, 0x1b, 0x55, + 0xa2, 0xc5, 0xfd, 0x56, 0x9a, 0xe6, 0xb8, 0x81, 0x16, 0x2b, 0x0a, + 0xc8, 0x21, 0x56, 0x9c, 0x09, 0xd8, 0x81, 0xd8, 0x2d, 0x16, 0x2d, + 0x09, 0xe1, 0x41, 0x71, 0xe8, 0x0e, 0x07, 0xee, 0x02, 0xc6, 0x21, + 0x00, 0xc8, 0x81, 0x48, 0x61, 0x98, 0x8c, 0xc8, 0x3c, 0x9a, 0x44, + 0x9c, 0x0c, 0xd8, 0x81, 0xd2, 0x1d, 0x19, 0x27, 0xed, 0x09, 0x81, + 0x40, 0x71, 0x88, 0x08, 0xca, 0x49, 0x80, 0x44, 0xc0, 0x0c, 0x0c, + 0xe8, 0x81, 0x38, 0x02, 0x91, 0x24, 0x70, 0x32, 0x23, 0x15, 0x92, + 0x09, 0x00, 0xe2, 0x2e, 0x04, 0x90, 0x99, 0x90, 0x76, 0xa9, 0x39, + 0xa2, 0x8e, 0x05, 0x83, 0x31, 0xac, 0xda, 0x07, 0xed, 0x2b, 0x17, + 0xed, 0x28, 0x27, 0xed, 0x25, 0xb8, 0x81, 0xb7, 0x13, 0x20, 0xd8, + 0x23, 0xf2, 0x03, 0x02, 0x9c, 0x8d, 0x82, 0x0b, 0x02, 0x87, 0x9f, + 0x13, 0x98, 0x43, 0xe0, 0xa9, 0xc0, 0xd6, 0xba, 0x00, 0xb8, 0x33, + 0x40, 0xbb, 0xc0, 0xe6, 0x1b, 0x03, 0xed, 0x09, 0xcd, 0x03, 0x32, + 0xc3, 0x34, 0x56, 0xbc, 0x1d, 0x46, 0x04, 0x00, 0x0c, 0x0e, 0xd8, + 0x41, 0xe9, 0x81, 0x8c, 0x6d, 0xf8, 0x3d, 0xf0, 0xf9, 0xc0, 0xd6, + 0x1f, 0x00, 0xc9, 0x41, 0x98, 0x81, 0xec, 0x09, 0xa8, 0x41, 0x9c, + 0x7a, 0x98, 0x02, 0x92, 0x29, 0x12, 0x9c, 0x09, 0xa2, 0x09, 0x01, + 0x66, 0x1a, 0x0b, 0xc8, 0x41, 0xb8, 0x81, 0xd2, 0x0c, 0x01, 0xd0, + 0xbc, 0x83, 0xb9, 0x81, 0xe8, 0x81, 0x16, 0xbe, 0xcf, 0x48, 0x61, + 0xe8, 0x02, 0x98, 0x81, 0x0c, 0x0a, 0x7c, 0xfb, 0xb9, 0x51, 0xa9, + 0x41, 0xc8, 0x39, 0x98, 0x89, 0x32, 0x2e, 0x15, 0x8e, 0x16, 0x81, + 0x3b, 0xf1, 0x24, 0x70, 0xf2, 0x0f, 0x00, 0x0c, 0x05, 0xf0, 0xff, + 0x90, 0x16, 0xef, 0x06, 0x8e, 0x07, 0x84, 0x62, 0x78, 0x05, 0x07, + 0xed, 0x54, 0x17, 0xed, 0x51, 0x98, 0x81, 0x97, 0x13, 0x4c, 0xc8, + 0x23, 0xa8, 0x33, 0x16, 0x5c, 0x04, 0xf2, 0x03, 0x02, 0xb2, 0x09, + 0x02, 0xf7, 0x3b, 0x0c, 0x40, 0xda, 0xc0, 0xe6, 0x1d, 0x2d, 0xca, + 0xfa, 0xf9, 0x33, 0x86, 0x09, 0x00, 0x27, 0xed, 0x12, 0x88, 0x61, + 0x80, 0x8a, 0xc0, 0xd6, 0xa8, 0x00, 0x8e, 0x0b, 0xb2, 0x59, 0xc8, + 0x61, 0xb8, 0x51, 0xc0, 0xca, 0xc0, 0xb7, 0xbc, 0x06, 0xc9, 0x51, + 0x32, 0x6e, 0x16, 0x39, 0x41, 0x8e, 0x06, 0x66, 0xf0, 0xff, 0x8e, + 0x08, 0x86, 0x2b, 0x96, 0xc8, 0x81, 0x98, 0x8c, 0xc8, 0x3c, 0xad, + 0x09, 0xdd, 0x09, 0x0c, 0x04, 0xac, 0x7c, 0xe8, 0x81, 0xe2, 0x1e, + 0x19, 0x0c, 0x4f, 0xf7, 0x0e, 0x11, 0xf8, 0x31, 0x9c, 0x9f, 0x82, + 0x01, 0x00, 0x26, 0x18, 0x14, 0x8e, 0x06, 0x98, 0x69, 0x8c, 0xb9, + 0x98, 0x61, 0x90, 0x9c, 0xc0, 0xe6, 0x19, 0x02, 0xc6, 0x2c, 0x00, + 0x4d, 0x09, 0xc1, 0x26, 0x71, 0xc2, 0x0c, 0x04, 0xb2, 0xac, 0x18, + 0xc0, 0xbb, 0xd1, 0xba, 0xaa, 0xcc, 0x2d, 0xd8, 0x41, 0x8c, 0xfd, + 0xb8, 0x51, 0x0c, 0x0d, 0x81, 0x11, 0x71, 0x4a, 0xca, 0x82, 0x28, + 0x1a, 0xa8, 0x81, 0xe0, 0x08, 0x00, 0xb8, 0x81, 0x81, 0x11, 0x71, + 0xa9, 0x5b, 0x82, 0x28, 0x1e, 0xa8, 0x6b, 0x8e, 0x05, 0xff, 0x13, + 0x92, 0x09, 0x31, 0x66, 0x29, 0x19, 0xa8, 0x81, 0xc1, 0x42, 0x71, + 0xa8, 0x5a, 0xc8, 0x0c, 0xa7, 0xbc, 0x0d, 0xb8, 0x81, 0xb8, 0x6b, + 0xb2, 0x0b, 0x01, 0x66, 0x1b, 0x03, 0xd8, 0x81, 0xc9, 0x5d, 0x98, + 0x81, 0x98, 0x29, 0x8c, 0x69, 0xb8, 0x81, 0xa8, 0x3b, 0x9a, 0xaa, + 0xa9, 0x3b, 0xe8, 0x02, 0xc2, 0x2e, 0x26, 0xb2, 0x0c, 0x01, 0x66, + 0x2b, 0x13, 0x81, 0x11, 0x71, 0xa8, 0x81, 0x82, 0x28, 0x1b, 0x8e, + 0x05, 0x99, 0x4d, 0xe8, 0x02, 0xa9, 0x81, 0xc2, 0x2e, 0x26, 0x98, + 0x81, 0x97, 0x1c, 0x20, 0xa2, 0x09, 0x31, 0xb8, 0x69, 0xea, 0xca, + 0xb2, 0x0b, 0x00, 0xb2, 0x4c, 0x68, 0x66, 0x1a, 0x10, 0xc8, 0x81, + 0xe8, 0x02, 0xd2, 0x0c, 0x02, 0xc8, 0x6c, 0xea, 0xdd, 0xc2, 0x0c, + 0x00, 0xc2, 0x4d, 0x6b, 0xe8, 0x81, 0xd8, 0x61, 0xd9, 0x4e, 0xc6, + 0xdf, 0xfe, 0xe8, 0x61, 0xc0, 0xce, 0xc0, 0xc0, 0xc0, 0x74, 0xd7, + 0xbc, 0x05, 0xc0, 0xad, 0xc0, 0x06, 0xcf, 0xff, 0x0c, 0x0a, 0xc6, + 0xcd, 0xff, 0xf8, 0x61, 0x98, 0x3c, 0xf0, 0xf9, 0xc0, 0xa6, 0x1f, + 0x02, 0x46, 0x86, 0xff, 0xc9, 0x81, 0x86, 0x89, 0x8e, 0x05, 0xb7, + 0x38, 0x81, 0x16, 0x71, 0x88, 0x88, 0x8e, 0x06, 0x8e, 0x48, 0x11, + 0x71, 0x2d, 0x0a, 0x31, 0x00, 0x70, 0xa1, 0x30, 0x71, 0x82, 0x23, + 0x70, 0xb2, 0x2b, 0x1f, 0xe0, 0x08, 0x00, 0xb1, 0x44, 0x71, 0x0c, + 0x0c, 0x41, 0x13, 0x8e, 0x04, 0x11, 0x8e, 0x07, 0x9e, 0x55, 0x82, + 0x23, 0x72, 0xb1, 0xd2, 0x8e, 0x05, 0xa5, 0x7b, 0x15, 0x70, 0xa1, + 0x45, 0x71, 0x82, 0x28, 0x14, 0x2c, 0x8e, 0x05, 0xb5, 0x67, 0x46, + 0x8e, 0x04, 0x24, 0xc1, 0x0a, 0x71, 0x41, 0x27, 0x71, 0xc8, 0x0c, + 0x8e, 0x10, 0x2c, 0x8e, 0x07, 0xa6, 0x1c, 0x82, 0xc3, 0xf9, 0x16, + 0xe8, 0x0c, 0x61, 0x0a, 0x71, 0xc8, 0x06, 0x0c, 0x82, 0x72, 0x2c, + 0x13, 0xb1, 0x1a, 0x71, 0x92, 0x07, 0x01, 0xa8, 0x67, 0x56, 0x79, + 0x0b, 0x66, 0x63, 0x11, 0xd2, 0x2a, 0x15, 0xd2, 0x0d, 0x02, 0x8c, + 0x8d, 0x8d, 0x0b, 0x88, 0xa8, 0xb2, 0x0c, 0x34, 0x8e, 0x08, 0xca, + 0x3e, 0xc2, 0x0a, 0x00, 0x41, 0x20, 0x70, 0x16, 0xac, 0x09, 0x40, + 0x32, 0xa0, 0x38, 0x03, 0x82, 0x03, 0x03, 0x52, 0x23, 0x1e, 0x66, + 0x38, 0x78, 0x81, 0x1a, 0x71, 0x82, 0x28, 0x1c, 0x8e, 0x05, 0x88, + 0x65, 0xc8, 0x06, 0x98, 0xfc, 0xa2, 0x1a, 0x00, 0x92, 0x19, 0x00, + 0xa7, 0x99, 0x5a, 0x92, 0x05, 0x03, 0x17, 0xe9, 0x02, 0x07, 0x69, + 0x51, 0x8e, 0x05, 0xc6, 0x2d, 0x24, 0xb2, 0x03, 0x02, 0xdc, 0xeb, + 0xd1, 0x15, 0x71, 0xd8, 0x0d, 0x57, 0xed, 0x0a, 0xe1, 0x16, 0x71, + 0xe2, 0x0e, 0x16, 0x0c, 0x2f, 0xf7, 0x0e, 0x0c, 0xf2, 0x0c, 0x72, + 0x8c, 0x6f, 0x81, 0x0d, 0x70, 0x82, 0x08, 0x00, 0xec, 0x58, 0x8e, + 0x05, 0x95, 0x53, 0x2c, 0x8e, 0x0d, 0x9f, 0x7c, 0xad, 0x03, 0x0c, + 0x1b, 0x8e, 0x06, 0xa7, 0x22, 0xad, 0x03, 0x0c, 0x2b, 0x8e, 0x06, + 0x0a, 0xa1, 0x24, 0x70, 0x8e, 0x09, 0xca, 0x6d, 0xb2, 0x02, 0x46, + 0xdb, 0xff, 0x46, 0x01, 0x00, 0x1d, 0xf0, 0x0c, 0x72, 0x1d, 0xf0, + 0xb1, 0x15, 0x70, 0x16, 0x8c, 0x05, 0x0c, 0x02, 0x8e, 0x0d, 0x81, + 0x23, 0x40, 0x8e, 0x0b, 0x81, 0x23, 0x98, 0x06, 0x98, 0xf9, 0x8e, + 0x08, 0x81, 0x23, 0x1f, 0xa2, 0x05, 0x03, 0x27, 0x6a, 0x19, 0x8e, + 0x05, 0xc9, 0x00, 0x8e, 0x05, 0x98, 0x5a, 0x38, 0x8e, 0x06, 0x98, + 0x5a, 0x03, 0x0c, 0x4b, 0x8e, 0x09, 0x68, 0xb1, 0x15, 0x8e, 0x0a, + 0x6b, 0x32, 0xa8, 0xc8, 0x06, 0x82, 0x2c, 0x13, 0x0c, 0x12, 0xcc, + 0x18, 0x86, 0x02, 0x00, 0x88, 0xab, 0xa8, 0x67, 0x8e, 0x05, 0x81, + 0x74, 0x0c, 0x82, 0x0c, 0x09, 0x92, 0x4c, 0x34, 0x8e, 0x08, 0x8e, + 0x50, 0x41, 0xb0, 0x70, 0x31, 0x0a, 0x71, 0x62, 0xa1, 0x24, 0x88, + 0x03, 0x51, 0x00, 0x70, 0x22, 0x28, 0x12, 0x71, 0x15, 0x70, 0x28, + 0x62, 0xc0, 0x20, 0x00, 0x42, 0x24, 0x98, 0x6a, 0xa8, 0x82, 0x28, + 0x52, 0xb2, 0x25, 0x6e, 0x16, 0x88, 0x06, 0xe0, 0x0b, 0x8e, 0x08, + 0x93, 0x18, 0x25, 0x6d, 0x6a, 0x8e, 0x07, 0x93, 0x18, 0xa8, 0x03, + 0xc2, 0x2a, 0x29, 0x8c, 0x8c, 0xa2, 0x2a, 0x2a, 0x8e, 0x05, 0xaa, + 0x46, 0xa8, 0x03, 0xbc, 0x92, 0xd2, 0x02, 0x03, 0x66, 0x3d, 0x34, + 0xb2, 0x2a, 0x1f, 0x37, 0x6b, 0x14, 0x0c, 0x0c, 0x4c, 0x0d, 0x88, + 0x37, 0x7c, 0x79, 0x90, 0x9b, 0x10, 0x92, 0x6a, 0x1f, 0x0c, 0x0b, + 0x8e, 0x06, 0xca, 0x7e, 0x3d, 0x70, 0x82, 0x28, 0x20, 0x8e, 0x05, + 0x0b, 0x8c, 0x9a, 0x8e, 0x05, 0xdf, 0x56, 0x17, 0x8e, 0x05, 0x0d, + 0xa8, 0x03, 0x0c, 0x09, 0x92, 0x6a, 0x52, 0x1d, 0xf0, 0x8e, 0x10, + 0x69, 0x54, 0xe0, 0x08, 0x00, 0x16, 0x42, 0x04, 0xad, 0x02, 0x8e, + 0x05, 0xc3, 0x67, 0x81, 0xb2, 0x70, 0xf2, 0xa3, 0xe8, 0xd8, 0x03, + 0xe2, 0xa7, 0xd0, 0xd2, 0x2d, 0x54, 0xea, 0xe4, 0xf0, 0xdd, 0x82, + 0x88, 0x18, 0x8e, 0x06, 0xc3, 0x67, 0xf2, 0x02, 0x03, 0x66, 0x3f, + 0x1c, 0xa8, 0x03, 0x8e, 0x04, 0x7d, 0xeb, 0x8e, 0x06, 0x7d, 0x27, + 0x0c, 0x89, 0x90, 0x9b, 0x20, 0x8e, 0x0a, 0x7d, 0xa8, 0x03, 0xc2, + 0x2a, 0x27, 0x8c, 0x8c, 0x8e, 0x08, 0xab, 0x77, 0xa8, 0x03, 0x0c, + 0x19, 0x06, 0xe2, 0x8e, 0x05, 0x8f, 0x54, 0x61, 0x00, 0xa1, 0x47, + 0x71, 0x51, 0x00, 0x70, 0xbd, 0x02, 0x82, 0x25, 0xa1, 0x8e, 0x05, + 0x9d, 0x16, 0x41, 0x0a, 0x71, 0x26, 0x13, 0x3f, 0xb8, 0x04, 0xa2, + 0x2b, 0x8e, 0x0b, 0x95, 0x48, 0xb8, 0x04, 0x82, 0x25, 0x6e, 0xa2, + 0xcb, 0x18, 0x8e, 0x08, 0x9f, 0x0d, 0x25, 0x6d, 0xa8, 0x04, 0xc1, + 0x26, 0x71, 0xa2, 0xca, 0x18, 0xc2, 0x0c, 0x04, 0x8e, 0x08, 0x81, + 0x80, 0x67, 0x32, 0xb8, 0x04, 0x0c, 0x0c, 0xc9, 0x1a, 0xa2, 0x6b, + 0x13, 0x1d, 0xf0, 0x26, 0x82, 0x0a, 0xd8, 0x04, 0xd2, 0x2d, 0x26, + 0xd2, 0x0d, 0x01, 0x66, 0x2d, 0xe0, 0x81, 0x11, 0x71, 0x88, 0xa8, + 0x8e, 0x05, 0xb8, 0x5f, 0x56, 0x7a, 0xfd, 0x98, 0x01, 0x9c, 0x89, + 0x82, 0x25, 0x6e, 0xa8, 0x8e, 0x05, 0xae, 0x51, 0x04, 0x0c, 0x0b, + 0xc8, 0x01, 0x82, 0x25, 0x8e, 0x07, 0x97, 0x20, 0x22, 0x8e, 0x08, + 0x81, 0x81, 0x2d, 0x36, 0x41, 0x00, 0xc1, 0x50, 0x71, 0xf1, 0x4e, + 0x71, 0x81, 0x4a, 0x71, 0xa1, 0x48, 0x71, 0x21, 0x4d, 0x71, 0x41, + 0x4b, 0x71, 0x31, 0x4c, 0x71, 0x91, 0x49, 0x71, 0xd1, 0x11, 0x71, + 0xe1, 0x4f, 0x71, 0xe9, 0xed, 0x92, 0x6d, 0x12, 0x32, 0x6d, 0x21, + 0x42, 0x6d, 0x15, 0x29, 0xad, 0xa2, 0x6d, 0x1b, 0xb8, 0x4d, 0x82, + 0x6d, 0x11, 0x81, 0x51, 0x71, 0xf2, 0x6d, 0x14, 0xc9, 0x4d, 0x21, + 0x53, 0x71, 0x41, 0x52, 0x71, 0xa1, 0x16, 0x71, 0x31, 0x3d, 0x70, + 0xb9, 0x6a, 0x82, 0x63, 0x1e, 0x42, 0x63, 0x11, 0x98, 0x03, 0x29, + 0x03, 0x99, 0x8a, 0x8e, 0x07, 0xb0, 0x2c, 0xb1, 0x54, 0x71, 0x5d, + 0x02, 0xb8, 0x0b, 0x30, 0x23, 0x20, 0x16, 0xbb, 0x0e, 0xc2, 0x05, + 0x02, 0xc2, 0xcc, 0xfd, 0x56, 0x2c, 0x0e, 0xd1, 0x24, 0x70, 0x92, + 0xa0, 0x00, 0xd2, 0x0d, 0x00, 0xa1, 0x20, 0x70, 0x76, 0x9d, 0xd1, + 0xa0, 0x49, 0xa0, 0x48, 0x04, 0x1b, 0x99, 0xe2, 0x04, 0x01, 0x90, + 0x90, 0xf4, 0x0b, 0xee, 0x56, 0xde, 0x0b, 0x82, 0x04, 0x02, 0xc2, + 0x04, 0x03, 0x56, 0x48, 0x0b, 0x8e, 0x04, 0x30, 0xec, 0x0a, 0x61, + 0x38, 0x71, 0x38, 0x06, 0xcc, 0x63, 0x32, 0x24, 0x18, 0x32, 0x13, + 0x14, 0x39, 0x06, 0x71, 0x55, 0x71, 0xb0, 0xaa, 0x41, 0x88, 0x07, + 0x89, 0x01, 0x8a, 0x22, 0x20, 0x2a, 0x41, 0xa7, 0xb2, 0x27, 0xf0, + 0xc3, 0x11, 0x20, 0x9a, 0xc0, 0xc7, 0xb9, 0x04, 0xa9, 0x11, 0x06, + 0x0a, 0x00, 0xb0, 0xa0, 0x60, 0x30, 0xb3, 0x20, 0xa0, 0xaa, 0x41, + 0xa0, 0xa2, 0x80, 0x25, 0x30, 0x07, 0x30, 0xb2, 0x80, 0xa0, 0xbb, + 0xc0, 0xb9, 0x11, 0x86, 0x03, 0x00, 0xa0, 0xa2, 0xc0, 0x30, 0xb3, + 0x20, 0xe5, 0x2e, 0x07, 0xa0, 0xc2, 0xc0, 0xc2, 0x61, 0x01, 0x22, + 0x21, 0x01, 0xa2, 0x17, 0x02, 0x1c, 0x8d, 0xd7, 0x9a, 0x15, 0xad, + 0x8e, 0x05, 0xcf, 0x63, 0x2b, 0x82, 0x28, 0x10, 0x8e, 0x05, 0x9b, + 0x39, 0xa2, 0x17, 0x02, 0x98, 0x07, 0x99, 0x01, 0x3c, 0x2b, 0xb7, + 0x9a, 0x04, 0x0c, 0x0a, 0xa2, 0x57, 0x02, 0x88, 0x01, 0xaa, 0x32, + 0x60, 0x33, 0x11, 0x80, 0x33, 0xc0, 0x81, 0x14, 0x70, 0xa2, 0x25, + 0x15, 0x88, 0xc8, 0xa2, 0x0a, 0x0e, 0x8e, 0x04, 0x29, 0xda, 0x14, + 0x37, 0x3a, 0x09, 0xb8, 0x06, 0x60, 0xbb, 0x11, 0x3a, 0x3b, 0x37, + 0xba, 0xfa, 0x2d, 0x03, 0x1d, 0xf0, 0x3d, 0xf0, 0x8e, 0x09, 0xe3, + 0x14, 0x22, 0x23, 0xbf, 0x0c, 0x04, 0x88, 0x62, 0x51, 0x3d, 0x70, + 0xf6, 0x28, 0x20, 0x81, 0x56, 0x71, 0xe0, 0x08, 0x00, 0x16, 0x6a, + 0x05, 0x91, 0x54, 0x71, 0x92, 0x09, 0x04, 0x56, 0xd9, 0x04, 0xad, + 0x03, 0x0c, 0x2b, 0x82, 0x25, 0x10, 0x8e, 0x05, 0xc8, 0x07, 0x86, + 0x12, 0x8e, 0x05, 0x87, 0x51, 0x82, 0x25, 0x10, 0x8e, 0x05, 0x9a, + 0x6e, 0x8e, 0x07, 0xba, 0x2f, 0xc2, 0xa0, 0xc8, 0x82, 0x28, 0x8e, + 0x06, 0x83, 0x35, 0x92, 0xa0, 0x64, 0xc1, 0x17, 0x71, 0xa2, 0x03, + 0x00, 0xb8, 0x62, 0xca, 0xaa, 0xb7, 0xb9, 0x08, 0x0c, 0x39, 0x92, + 0x4a, 0x00, 0x49, 0x62, 0x1d, 0xf0, 0xb6, 0x3b, 0x16, 0x0c, 0x29, + 0x06, 0xfc, 0xff, 0x8e, 0x08, 0x4e, 0x1c, 0xe0, 0x08, 0x00, 0x42, + 0x42, 0x1c, 0x06, 0xf3, 0xff, 0x0c, 0x19, 0x46, 0xf6, 0x8e, 0x06, + 0x8a, 0x4c, 0x24, 0x70, 0x0c, 0x02, 0x82, 0x08, 0x00, 0x41, 0x00, + 0x70, 0xbc, 0xd8, 0x61, 0x57, 0x71, 0x71, 0x20, 0x70, 0x0c, 0x0a, + 0x3c, 0x8b, 0x88, 0x44, 0x70, 0x32, 0xa0, 0x38, 0x03, 0x8e, 0x05, + 0x97, 0x6b, 0xb1, 0x58, 0x71, 0xa2, 0x63, 0xbf, 0x82, 0x24, 0x70, + 0x8e, 0x05, 0x85, 0x1d, 0xbd, 0x06, 0xcd, 0x03, 0x82, 0x24, 0x70, + 0xa2, 0xc5, 0x8e, 0x05, 0x91, 0x2c, 0x24, 0x70, 0x1b, 0x22, 0x92, + 0x09, 0x00, 0x20, 0x20, 0x74, 0x97, 0x32, 0xc6, 0x8e, 0x09, 0x81, + 0x85, 0x64, 0x59, 0x71, 0x80, 0x83, 0xc0, 0x56, 0xa8, 0x09, 0xb8, + 0x05, 0xd1, 0x55, 0x71, 0xb0, 0x98, 0x04, 0xb9, 0x2d, 0x16, 0xf9, + 0x08, 0x5c, 0x0e, 0xb0, 0xc0, 0x34, 0xc0, 0xac, 0xa0, 0xf0, 0xaa, + 0x11, 0xa7, 0x3e, 0x7f, 0x1c, 0x3e, 0xa7, 0xbe, 0x7a, 0xf2, 0xcc, + 0xfe, 0x56, 0x6f, 0x08, 0x4b, 0x9a, 0x90, 0xa0, 0xf4, 0x41, 0x24, + 0x70, 0xa2, 0x5d, 0x02, 0x47, 0x6b, 0x1e, 0x81, 0x38, 0x71, 0x1c, + 0xf9, 0x88, 0x08, 0x90, 0x9a, 0x73, 0x80, 0xb0, 0xf4, 0x97, 0xb8, + 0x02, 0xb2, 0xa0, 0x64, 0xa0, 0x9b, 0xc0, 0xa6, 0xc9, 0x05, 0xa2, + 0xcb, 0xe3, 0xa2, 0x5d, 0x02, 0xb2, 0x04, 0x00, 0x16, 0x1b, 0x04, + 0x61, 0x3a, 0x71, 0x51, 0x39, 0x71, 0x31, 0x20, 0x70, 0x0c, 0x02, + 0x8e, 0x05, 0x97, 0x54, 0xc2, 0x8e, 0x06, 0x97, 0x54, 0x2c, 0x8e, + 0x2c, 0x97, 0x54, 0x07, 0x6b, 0x04, 0x3c, 0x2a, 0x46, 0xe1, 0xff, + 0x17, 0x6b, 0x12, 0x1c, 0x8a, 0x46, 0xdf, 0xff, 0x66, 0x8c, 0x05, + 0x92, 0xca, 0xfc, 0x46, 0xdc, 0xff, 0x9d, 0x0a, 0x06, 0xdb, 0xff, + 0x4c, 0xc8, 0x3c, 0x2a, 0x0c, 0x49, 0x90, 0x9b, 0x10, 0x90, 0xa8, + 0x93, 0x8e, 0x08, 0xb2, 0x74, 0x8e, 0x07, 0xef, 0x4e, 0x5a, 0x8e, + 0x0a, 0xb1, 0x6c, 0x81, 0x00, 0x62, 0x22, 0x18, 0x51, 0x54, 0x71, + 0x32, 0x16, 0x14, 0x82, 0x02, 0x01, 0x60, 0x33, 0x11, 0x66, 0x18, + 0x06, 0x82, 0x02, 0x02, 0xcc, 0x08, 0x39, 0x25, 0xad, 0x01, 0x71, + 0x00, 0x70, 0x0c, 0x0b, 0x82, 0x27, 0x3e, 0x1c, 0x8c, 0xe0, 0x08, + 0x00, 0xc2, 0xa0, 0xf7, 0x98, 0x46, 0x88, 0xe2, 0x42, 0xa2, 0x5c, + 0x4a, 0x42, 0x82, 0x41, 0x14, 0x29, 0x21, 0x99, 0x01, 0xb2, 0x04, + 0x7e, 0xa2, 0x22, 0xbf, 0xc0, 0xbb, 0x10, 0xb2, 0x44, 0x7e, 0x82, + 0x27, 0x6e, 0xa2, 0xca, 0x20, 0xe0, 0x08, 0x00, 0xa1, 0x5b, 0x71, + 0x0c, 0x1c, 0xb2, 0x04, 0x7e, 0x71, 0x55, 0x71, 0xc0, 0xbb, 0x20, + 0xb2, 0x44, 0x7e, 0x41, 0x3d, 0x70, 0xb0, 0xb1, 0x04, 0x56, 0xfb, + 0x0c, 0xad, 0x02, 0x88, 0x94, 0x8e, 0x06, 0xd5, 0x67, 0x1c, 0x71, + 0x92, 0x05, 0x04, 0xa9, 0x11, 0xac, 0xa9, 0xb8, 0x05, 0xac, 0x6b, + 0xc2, 0x02, 0x02, 0x66, 0x4c, 0x21, 0x8e, 0x05, 0xcd, 0x58, 0x1c, + 0xd1, 0x38, 0x71, 0x81, 0x3a, 0x71, 0xf8, 0x25, 0x82, 0x28, 0x10, + 0xf0, 0x3f, 0x93, 0x30, 0x9a, 0xf4, 0x30, 0xea, 0x41, 0xe2, 0x56, + 0x14, 0x99, 0x8e, 0x06, 0xca, 0x34, 0x61, 0x5b, 0x71, 0x0c, 0x0b, + 0x88, 0x56, 0x8e, 0x05, 0x84, 0x06, 0xf8, 0x07, 0x92, 0x05, 0x04, + 0xf0, 0xfa, 0xc0, 0x8c, 0x79, 0xbd, 0x0f, 0xad, 0x02, 0xe5, 0xc6, + 0xff, 0xfd, 0x0a, 0x82, 0x05, 0x0c, 0x98, 0x77, 0x16, 0x58, 0x05, + 0x66, 0x19, 0x52, 0xa2, 0x02, 0x02, 0xc1, 0x24, 0x70, 0x66, 0x4a, + 0x49, 0xb2, 0x0c, 0x00, 0xac, 0xeb, 0x61, 0x20, 0x70, 0x0c, 0x05, + 0x60, 0xa5, 0xa0, 0xa8, 0x0a, 0xd2, 0x0a, 0x01, 0x66, 0x1d, 0x17, + 0xd2, 0x0a, 0x02, 0xdc, 0x1d, 0xe2, 0x0a, 0x03, 0x66, 0x3e, 0x0c, + 0x8e, 0x07, 0x81, 0x12, 0xc1, 0x24, 0x70, 0xb2, 0x0c, 0x8e, 0x08, + 0xd4, 0x4b, 0xd4, 0x81, 0x14, 0x70, 0xa2, 0x22, 0x8e, 0x09, 0x86, + 0x08, 0xf2, 0xda, 0x04, 0x0c, 0x09, 0x99, 0x77, 0xad, 0x02, 0x0c, + 0x0b, 0xcd, 0x01, 0x0c, 0x4d, 0x88, 0x44, 0xed, 0x8e, 0x05, 0xa0, + 0x23, 0x5b, 0x71, 0x0c, 0x0b, 0x0c, 0x1c, 0x0c, 0x49, 0x88, 0x5a, + 0x92, 0x51, 0x0b, 0xa1, 0x0d, 0x71, 0x8e, 0x07, 0xf9, 0x43, 0xed, + 0x03, 0x0c, 0x2b, 0xcd, 0x01, 0x0c, 0x1d, 0xf8, 0x07, 0x88, 0x44, + 0xf0, 0xfa, 0xc0, 0xad, 0x02, 0xf2, 0xdf, 0x8e, 0x05, 0x9a, 0x0e, + 0x24, 0x1a, 0x8e, 0x0b, 0x82, 0x74, 0x32, 0x22, 0x18, 0x8e, 0x0b, + 0x81, 0x91, 0x7f, 0x8c, 0xe0, 0x08, 0x00, 0xb8, 0xe2, 0xc8, 0x43, + 0xa2, 0xa2, 0x5a, 0x31, 0x3d, 0x70, 0xaa, 0xa2, 0xb2, 0x41, 0x14, + 0x29, 0x21, 0xc9, 0x01, 0x92, 0x0a, 0x80, 0x0c, 0x2b, 0xb0, 0x99, + 0x20, 0x0c, 0x0b, 0x92, 0x4a, 0x80, 0x88, 0x8e, 0x06, 0xc1, 0x46, + 0x8e, 0x07, 0x81, 0x05, 0x6d, 0x0c, 0x0e, 0x0c, 0x0f, 0x91, 0x5c, + 0x71, 0x88, 0x43, 0x99, 0x11, 0x8e, 0x06, 0xb5, 0x74, 0x8e, 0x06, + 0x81, 0x81, 0x10, 0x1c, 0x8c, 0x32, 0x22, 0x18, 0x81, 0x00, 0x70, + 0x52, 0x13, 0x14, 0x82, 0x28, 0x3e, 0x60, 0x55, 0x8e, 0x04, 0x1d, + 0x78, 0x43, 0x68, 0xe2, 0x32, 0xa2, 0x5a, 0x62, 0x8e, 0x04, 0x55, + 0x3a, 0x32, 0x79, 0x01, 0x72, 0xa0, 0xfd, 0xa2, 0x03, 0x80, 0x61, + 0x3d, 0x70, 0xa0, 0x90, 0x04, 0x16, 0x49, 0x0d, 0x8e, 0x09, 0xe8, + 0x48, 0xad, 0x02, 0x81, 0x8e, 0x05, 0x82, 0x5f, 0x58, 0x8e, 0x05, + 0x82, 0x5f, 0x91, 0x54, 0x71, 0x41, 0x55, 0x71, 0x81, 0x1c, 0x71, + 0x89, 0x11, 0x48, 0x04, 0x92, 0x09, 0x04, 0x40, 0x4a, 0xc0, 0x8c, + 0x79, 0xbd, 0x04, 0xad, 0x02, 0x65, 0xb0, 0xff, 0x4d, 0x0a, 0x8e, + 0x08, 0x82, 0x0c, 0xed, 0x05, 0x88, 0x46, 0xfd, 0x04, 0x8e, 0x06, + 0x2f, 0xa2, 0x09, 0x04, 0xb1, 0x39, 0x71, 0x16, 0xfa, 0x07, 0xd2, + 0x03, 0x80, 0xc2, 0x02, 0x01, 0x70, 0xdd, 0x10, 0xd2, 0x43, 0x80, + 0x66, 0x1c, 0x67, 0xe2, 0x02, 0x02, 0x56, 0x1e, 0x06, 0x81, 0x55, + 0x71, 0xc1, 0x24, 0x70, 0x88, 0x08, 0xf2, 0x0c, 0x00, 0x4a, 0x88, + 0x89, 0x09, 0x16, 0xff, 0x04, 0xb1, 0x20, 0x70, 0x0c, 0x04, 0xb0, + 0xa4, 0x8e, 0x07, 0x83, 0x15, 0x2d, 0x17, 0xd2, 0x0a, 0x03, 0x66, + 0x3d, 0x11, 0x0c, 0x3b, 0x81, 0x39, 0x71, 0x91, 0x54, 0x71, 0x88, + 0x28, 0x59, 0x29, 0xe0, 0x08, 0x00, 0xc6, 0x04, 0x00, 0xd2, 0x0a, + 0x02, 0xe2, 0x8e, 0x04, 0x1b, 0x0a, 0x66, 0x3e, 0x07, 0x81, 0x3a, + 0x71, 0x8e, 0x05, 0xc2, 0x58, 0xb1, 0x20, 0x70, 0xc1, 0x24, 0x70, + 0x1b, 0x44, 0x92, 0x0c, 0x00, 0x40, 0x40, 0xf4, 0x97, 0x34, 0xb7, + 0xb1, 0x39, 0x71, 0xad, 0x02, 0x88, 0x5b, 0x8e, 0x05, 0xa6, 0x44, + 0xa2, 0x03, 0x80, 0x70, 0x9a, 0x10, 0x92, 0x43, 0x80, 0x82, 0x26, + 0x8e, 0x0a, 0x82, 0x74, 0x41, 0x00, 0x31, 0x3d, 0x70, 0xad, 0x02, + 0x88, 0x93, 0x8e, 0x07, 0x81, 0x6c, 0x88, 0x93, 0x8e, 0x06, 0xe1, + 0x29, 0xd2, 0x02, 0x0c, 0x04, 0x42, 0x49, 0xda, 0x82, 0x23, 0x8e, + 0x04, 0x2b, 0x91, 0x55, 0x71, 0x98, 0x29, 0xa1, 0x5d, 0x71, 0x67, + 0xe9, 0x02, 0x77, 0x69, 0x23, 0x42, 0x4a, 0x00, 0xb1, 0x5e, 0x71, + 0xc1, 0x16, 0x71, 0xd1, 0x5f, 0x71, 0xe1, 0x60, 0x71, 0x81, 0x61, + 0x71, 0xf1, 0x62, 0x71, 0x49, 0x08, 0x42, 0x4f, 0x00, 0x49, 0x0e, + 0x42, 0x4d, 0x00, 0x49, 0x0c, 0x42, 0x4b, 0x00, 0x8e, 0x08, 0xa0, + 0x3a, 0x08, 0xcc, 0x5a, 0xb1, 0x54, 0x71, 0x49, 0x0b, 0x49, 0x2b, + 0x8e, 0x11, 0x83, 0x0c, 0x42, 0x8e, 0x06, 0x83, 0x0c, 0x44, 0x11, + 0x8e, 0x06, 0x6f, 0xb8, 0x43, 0xa8, 0xe2, 0xa2, 0x41, 0x14, 0x29, + 0x21, 0xb9, 0x01, 0x92, 0x09, 0xda, 0x31, 0x3d, 0x70, 0x07, 0x69, + 0x34, 0x8e, 0x0b, 0x81, 0x18, 0x8e, 0x0c, 0x83, 0x04, 0xed, 0x04, + 0x8e, 0x06, 0x82, 0x66, 0x91, 0x1c, 0x71, 0x81, 0x55, 0x71, 0xfd, + 0x0a, 0x88, 0x08, 0xad, 0x02, 0x80, 0xff, 0xc0, 0x8e, 0x07, 0x83, + 0x75, 0x8e, 0x06, 0x81, 0x33, 0x8e, 0x07, 0xe5, 0x54, 0xa2, 0x5a, + 0x26, 0x13, 0x26, 0x26, 0x23, 0x16, 0x26, 0x33, 0x0b, 0x26, 0x43, + 0x3f, 0x26, 0x53, 0x44, 0x26, 0x63, 0x49, 0x66, 0x73, 0x0c, 0xad, + 0x02, 0xe5, 0xc1, 0xff, 0x06, 0x01, 0x00, 0xad, 0x02, 0xe5, 0xef, + 0xff, 0x4a, 0xa2, 0xa2, 0x0a, 0x80, 0x06, 0x02, 0x00, 0x0c, 0x0a, + 0x4a, 0xc2, 0x0c, 0x0b, 0xb2, 0x4c, 0x80, 0x81, 0x00, 0x70, 0x80, + 0xca, 0x11, 0xb2, 0x02, 0x00, 0xa1, 0x63, 0x71, 0xc0, 0xbb, 0x20, + 0x82, 0x28, 0x8e, 0x06, 0x8f, 0x3d, 0x1d, 0xf0, 0xad, 0x02, 0xa5, + 0xd5, 0xff, 0x46, 0xf3, 0xff, 0xad, 0x02, 0x65, 0xda, 0xff, 0x46, + 0xf1, 0xff, 0xad, 0x02, 0xa5, 0xf2, 0xff, 0x46, 0xef, 0x8e, 0x06, + 0x88, 0x44, 0x8c, 0xb2, 0x48, 0xe2, 0x31, 0xd5, 0x70, 0xb7, 0x74, + 0x04, 0x30, 0x34, 0x10, 0x39, 0xe2, 0x8e, 0x05, 0x81, 0x70, 0x0c, + 0x09, 0xa2, 0x26, 0x8c, 0xb1, 0x44, 0x70, 0x49, 0x71, 0x39, 0x61, + 0x42, 0x22, 0xbf, 0x32, 0xa2, 0x5c, 0x3a, 0x32, 0x8c, 0x36, 0x0c, + 0x18, 0xa0, 0x98, 0x93, 0xcc, 0x99, 0x8e, 0x0b, 0x98, 0x2f, 0x27, + 0x1a, 0x0d, 0xa2, 0xdb, 0x01, 0xa2, 0xca, 0x31, 0x8e, 0x08, 0xa3, + 0x4c, 0x81, 0x8e, 0x05, 0x83, 0x15, 0x98, 0x8e, 0x06, 0x81, 0x7d, + 0x8e, 0x0e, 0x88, 0x70, 0x69, 0x21, 0x59, 0x31, 0x8e, 0x08, 0x85, + 0x6b, 0x81, 0x3d, 0x70, 0x98, 0x71, 0xb1, 0x5c, 0x71, 0xa8, 0x61, + 0xa9, 0x01, 0xb9, 0x11, 0x92, 0x41, 0x14, 0x0c, 0x0b, 0x88, 0x48, + 0x0c, 0x29, 0xa1, 0x64, 0x71, 0xa9, 0x41, 0x92, 0x51, 0x8e, 0x06, + 0x91, 0x20, 0x42, 0xc4, 0x20, 0x92, 0x03, 0x7e, 0x0c, 0x8b, 0xb0, + 0x99, 0x20, 0x92, 0x43, 0x7e, 0x3d, 0x0a, 0x82, 0x27, 0x6e, 0x8e, + 0x09, 0xdf, 0x68, 0xc2, 0xa3, 0xe8, 0x8e, 0x08, 0xa4, 0x0d, 0xbd, + 0x05, 0xa1, 0x65, 0x71, 0x00, 0xd3, 0x11, 0xc2, 0x02, 0x00, 0x82, + 0x27, 0xa1, 0x8e, 0x06, 0x9d, 0x31, 0x2d, 0x03, 0x8e, 0x07, 0x8d, + 0x54, 0x51, 0x00, 0x70, 0x82, 0x25, 0x18, 0x42, 0x22, 0xbf, 0xe0, + 0x08, 0x00, 0x20, 0xb3, 0xa0, 0xa0, 0xd8, 0x41, 0xd0, 0xc8, 0x41, + 0xb2, 0xdb, 0x03, 0xd2, 0x4b, 0x01, 0xa2, 0x4b, 0x00, 0xc2, 0x4b, + 0x02, 0xc0, 0xc8, 0x41, 0xc2, 0x4b, 0x03, 0x92, 0x02, 0x01, 0xa2, + 0x62, 0xc4, 0x66, 0x19, 0x4b, 0xc2, 0xd2, 0x02, 0xc2, 0x0c, 0xda, + 0x0c, 0x12, 0x07, 0x6c, 0x40, 0xf8, 0x64, 0xe1, 0x55, 0x71, 0x1b, + 0xff, 0xf9, 0x64, 0xe8, 0x2e, 0xd2, 0xa0, 0xc0, 0xe7, 0x0d, 0x0e, + 0x81, 0x60, 0x71, 0x88, 0x08, 0x8c, 0x68, 0x66, 0x13, 0x04, 0x91, + 0x54, 0x71, 0x29, 0x49, 0xa2, 0x04, 0x1c, 0xdc, 0xaa, 0x82, 0x25, + 0x8e, 0x0b, 0x81, 0x14, 0xa0, 0xc8, 0x8e, 0x08, 0x91, 0x34, 0x22, + 0x44, 0x1c, 0x8e, 0x0a, 0xf3, 0x6c, 0xa1, 0x66, 0x71, 0x21, 0x69, + 0x71, 0x81, 0x68, 0x71, 0x31, 0x39, 0x71, 0x91, 0x67, 0x71, 0x99, + 0x43, 0x89, 0x53, 0x29, 0x63, 0xa9, 0x23, 0x8e, 0x05, 0xa5, 0x40, + 0xdd, 0x05, 0x8e, 0x05, 0xcc, 0x31, 0x6a, 0x71, 0xcd, 0x04, 0x88, + 0xa8, 0x42, 0x22, 0x19, 0x8e, 0x05, 0xd7, 0x5c, 0xcc, 0x3a, 0x0c, + 0x24, 0x46, 0x11, 0x00, 0x98, 0x4a, 0x0c, 0x4c, 0x42, 0x69, 0x20, + 0xc2, 0x49, 0x78, 0xb2, 0x02, 0x00, 0xa2, 0x09, 0x87, 0xb0, 0xb0, + 0x34, 0xb0, 0xaa, 0x20, 0xa2, 0x49, 0x87, 0x8c, 0xc6, 0x62, 0x69, + 0x24, 0xd2, 0x29, 0x23, 0x0c, 0x1e, 0xe0, 0xdd, 0x20, 0xd2, 0x69, + 0x23, 0x51, 0x15, 0x8e, 0x05, 0xe0, 0x72, 0x15, 0x8e, 0x05, 0xa3, + 0x76, 0x4d, 0x0a, 0x8c, 0xaa, 0x8e, 0x04, 0x54, 0x82, 0x25, 0x17, + 0x7c, 0xfc, 0xe0, 0x08, 0x00, 0x2d, 0x04, 0x8e, 0x09, 0x81, 0x91, + 0x7c, 0x22, 0x19, 0xcc, 0x28, 0x0c, 0x02, 0x8e, 0x09, 0x81, 0x84, + 0x48, 0x6b, 0x71, 0x8e, 0x0b, 0xf7, 0x23, 0x8e, 0x07, 0x82, 0x30, + 0x91, 0x6c, 0x71, 0x21, 0x6d, 0x71, 0x31, 0xb2, 0x70, 0x81, 0x6b, + 0x71, 0x48, 0x03, 0x49, 0x08, 0x29, 0x03, 0x99, 0x13, 0x8e, 0x09, + 0x81, 0x9e, 0x34, 0x2b, 0x1c, 0x0c, 0x31, 0xb5, 0x70, 0x41, 0x53, + 0x70, 0x82, 0x23, 0x18, 0x8e, 0x05, 0x93, 0x29, 0xad, 0x02, 0x81, + 0x6e, 0x8e, 0x06, 0xb7, 0x54, 0x1c, 0x0c, 0x2d, 0x0a, 0x8e, 0x08, + 0x16, 0x8e, 0x09, 0x8e, 0x74, 0x6f, 0x71, 0x88, 0x08, 0xe0, 0x08, + 0x00, 0xc1, 0x70, 0x71, 0xb1, 0x71, 0x71, 0x81, 0x72, 0x71, 0x2c, + 0xcd, 0xe1, 0x73, 0x71, 0xa1, 0x53, 0x70, 0xe2, 0x0e, 0x00, 0xa8, + 0x0a, 0xe0, 0xdd, 0xd1, 0x88, 0x08, 0xda, 0xbb, 0xb8, 0x6b, 0x8e, + 0x07, 0x85, 0x74, 0xe1, 0x00, 0x71, 0x24, 0x70, 0x0c, 0x03, 0xad, + 0x01, 0xb1, 0x74, 0x71, 0x0c, 0xcc, 0x0c, 0x16, 0x0c, 0x09, 0x49, + 0xd1, 0x59, 0xc1, 0x0c, 0x04, 0x0c, 0x05, 0x92, 0x61, 0x10, 0x69, + 0xf1, 0x81, 0x77, 0x8e, 0x04, 0x69, 0x92, 0x21, 0x10, 0xa8, 0xf1, + 0xb1, 0x20, 0x70, 0xc2, 0x07, 0x00, 0xed, 0x01, 0x76, 0x9c, 0x3b, + 0xc8, 0x0b, 0xd2, 0x0c, 0x03, 0x4b, 0xbb, 0x66, 0x3d, 0x2f, 0xd2, + 0x0c, 0x01, 0x66, 0x1d, 0x0c, 0xe0, 0x85, 0xa0, 0xc9, 0x08, 0x8e, + 0x05, 0x8b, 0x75, 0xc6, 0x01, 0x00, 0x66, 0x2d, 0x04, 0x8e, 0x05, + 0xbe, 0x6a, 0xc2, 0x2c, 0x18, 0xc8, 0x4c, 0xc2, 0x1c, 0x00, 0xcc, + 0x34, 0x4d, 0x0c, 0x46, 0x01, 0x00, 0x40, 0xdc, 0xc0, 0xd0, 0x36, + 0x93, 0x3d, 0xf0, 0xb2, 0x02, 0x01, 0x59, 0xb1, 0x66, 0x1b, 0x05, + 0x8c, 0x03, 0xcc, 0x09, 0x0c, 0x0a, 0x16, 0xea, 0x1d, 0xe2, 0x22, + 0x15, 0x0c, 0x06, 0xe2, 0x0e, 0x07, 0x51, 0x38, 0x71, 0xcc, 0x9e, + 0x88, 0xd1, 0x0c, 0x0f, 0xf9, 0x08, 0xf9, 0x05, 0x86, 0x6e, 0x00, + 0xad, 0x02, 0x41, 0xb5, 0x70, 0x0c, 0x2b, 0x82, 0x24, 0x18, 0x8e, + 0x05, 0xca, 0x70, 0x81, 0x14, 0x70, 0x88, 0xc8, 0xa2, 0x02, 0x31, + 0xe0, 0x08, 0x00, 0x1c, 0x0c, 0x82, 0x24, 0x18, 0xa9, 0x81, 0xb9, + 0xa1, 0xad, 0x02, 0x8e, 0x05, 0x89, 0x29, 0x48, 0x05, 0xcc, 0xe4, + 0xb2, 0x02, 0x01, 0x26, 0x2b, 0x01, 0xcc, 0x6b, 0x42, 0x22, 0x18, + 0x42, 0x14, 0x14, 0x49, 0x05, 0x51, 0x75, 0x71, 0x16, 0xb3, 0x18, + 0x88, 0xb1, 0x82, 0xc8, 0xff, 0x56, 0xe8, 0x0c, 0x92, 0x07, 0x00, + 0xb1, 0x20, 0x70, 0x76, 0x99, 0x10, 0xc8, 0x0b, 0xa2, 0x0c, 0x01, + 0x4b, 0xbb, 0x66, 0x2a, 0x04, 0x6d, 0x0c, 0x46, 0x00, 0x00, 0x3d, + 0xf0, 0x41, 0x14, 0x70, 0x60, 0xa6, 0x20, 0x31, 0xb5, 0x70, 0xb2, + 0xa0, 0x02, 0x82, 0x23, 0x8e, 0x06, 0x6b, 0xa2, 0x22, 0x15, 0x88, + 0xc4, 0x8e, 0x06, 0x8d, 0x34, 0x0c, 0x0b, 0x0c, 0x1c, 0x81, 0x5b, + 0x71, 0xa9, 0x41, 0x88, 0x58, 0xa8, 0x8e, 0x06, 0xcf, 0x12, 0xb8, + 0x41, 0x92, 0x29, 0x18, 0xb0, 0xaa, 0xc0, 0x92, 0x19, 0x14, 0xa0, + 0xaa, 0x41, 0x5b, 0x99, 0xa7, 0xb9, 0x0a, 0xb1, 0x8e, 0x05, 0x87, + 0x19, 0xb8, 0x0b, 0xc0, 0x20, 0x00, 0x41, 0x38, 0x71, 0x48, 0x04, + 0xbd, 0x04, 0x25, 0x8b, 0x06, 0x69, 0x51, 0x40, 0x92, 0x41, 0x40, + 0x81, 0x41, 0xb2, 0x22, 0x15, 0xa9, 0x61, 0xb2, 0x0b, 0x07, 0xad, + 0x04, 0x0b, 0xcb, 0xc0, 0x89, 0x93, 0x89, 0x71, 0x65, 0x84, 0x06, + 0x6d, 0x0a, 0x81, 0x14, 0x70, 0xa8, 0x51, 0x88, 0xc8, 0xa2, 0x0a, + 0x8e, 0x07, 0x81, 0x50, 0x23, 0x18, 0x4d, 0x0b, 0x3d, 0x0a, 0x0c, + 0x1b, 0xa8, 0x51, 0xe0, 0x08, 0x00, 0xc8, 0x61, 0xd8, 0x71, 0x00, + 0x4a, 0x40, 0x30, 0xb4, 0x81, 0xca, 0x9d, 0x41, 0x38, 0x71, 0xda, + 0xdb, 0x48, 0x04, 0xda, 0xcc, 0x97, 0x34, 0x02, 0x86, 0x6d, 0x00, + 0x40, 0x3c, 0xc0, 0x86, 0x6c, 0x00, 0x16, 0x13, 0x0b, 0xe8, 0xb1, + 0xe2, 0xce, 0xfe, 0x56, 0x9e, 0x0a, 0xf2, 0x8e, 0x06, 0x81, 0x5a, + 0x9f, 0x70, 0xc8, 0x0b, 0x82, 0x0c, 0x01, 0xb2, 0xcb, 0x04, 0x66, + 0x28, 0x63, 0xc0, 0x6c, 0x20, 0x71, 0x14, 0x70, 0x31, 0x5b, 0x71, + 0x60, 0xa6, 0x20, 0x51, 0x8e, 0x05, 0x82, 0x44, 0x25, 0x8e, 0x0a, + 0x81, 0x59, 0xc7, 0x8e, 0x0a, 0x81, 0x59, 0x4d, 0x0a, 0x88, 0x53, + 0x8e, 0x05, 0x81, 0x56, 0x8e, 0x05, 0xe2, 0x5d, 0x53, 0x3d, 0x0a, + 0xa8, 0x11, 0xe0, 0x08, 0x00, 0x40, 0xc3, 0xc0, 0x40, 0xba, 0xc0, + 0xb0, 0xba, 0x41, 0x41, 0x38, 0x71, 0xc0, 0xca, 0x41, 0x48, 0x04, + 0xb7, 0xbc, 0x02, 0xc6, 0x30, 0x00, 0xb0, 0xac, 0xc0, 0xc9, 0x91, + 0xbd, 0x04, 0x65, 0x7d, 0x06, 0x3d, 0x0a, 0x86, 0x30, 0x00, 0x3d, + 0xf0, 0x46, 0xe6, 0xff, 0xa1, 0x38, 0x71, 0xb2, 0x0c, 0x07, 0xa2, + 0x2a, 0x00, 0x25, 0x77, 0x06, 0xc2, 0x21, 0x0d, 0x60, 0xba, 0x11, + 0xb2, 0x6c, 0x01, 0xb2, 0x02, 0x01, 0x0c, 0x1a, 0xd8, 0xc1, 0xcc, + 0x8b, 0x92, 0x02, 0x31, 0x92, 0x4d, 0x8e, 0x05, 0x85, 0x64, 0x0c, + 0x19, 0xc6, 0xfc, 0xff, 0x40, 0xa4, 0x20, 0xb2, 0x22, 0x15, 0x32, + 0x21, 0x08, 0xb2, 0x0b, 0x07, 0x25, 0x74, 0x06, 0x00, 0x4a, 0x40, + 0xc8, 0xa1, 0x6d, 0x0a, 0xbd, 0x06, 0x30, 0xcc, 0x81, 0xad, 0x0c, + 0xe5, 0x72, 0x06, 0x81, 0x3a, 0x71, 0xc2, 0xa2, 0x00, 0xb8, 0xd1, + 0x1b, 0x9a, 0xd1, 0xa0, 0x70, 0x60, 0xa6, 0x11, 0xd2, 0x5b, 0x04, + 0xc2, 0x5b, 0x05, 0xa0, 0x99, 0x82, 0x8e, 0x05, 0xfa, 0x74, 0x99, + 0x0b, 0xe0, 0x08, 0x00, 0xd2, 0x07, 0x00, 0x0c, 0x0b, 0x16, 0xcd, + 0x09, 0x0c, 0x0e, 0xa1, 0x20, 0x70, 0xc2, 0x22, 0x15, 0x98, 0x0a, + 0xf2, 0x0c, 0x0f, 0x82, 0x09, 0x00, 0x4b, 0xaa, 0x00, 0x08, 0x40, + 0xf0, 0xf0, 0xb1, 0x07, 0x6f, 0x14, 0x60, 0xde, 0x11, 0x0c, 0x0c, + 0x6a, 0xee, 0xc2, 0x69, 0x93, 0xd2, 0x69, 0x92, 0xc2, 0x22, 0x15, + 0xd2, 0x07, 0x00, 0xb9, 0x05, 0x1b, 0xbb, 0xd7, 0x3b, 0xd2, 0xc6, + 0xd3, 0xff, 0xc0, 0xab, 0xc0, 0xb2, 0x61, 0x09, 0x40, 0xb4, 0x20, + 0x25, 0x71, 0x06, 0x3d, 0x0a, 0x88, 0xc7, 0xa2, 0x06, 0x31, 0xe0, + 0x08, 0x00, 0x78, 0x91, 0x1c, 0x0c, 0x82, 0x25, 0x18, 0x4d, 0x0a, + 0x5d, 0x0b, 0xad, 0x06, 0x8e, 0x05, 0x84, 0x51, 0x1c, 0xe9, 0x00, + 0x4a, 0x40, 0x40, 0xa5, 0x81, 0xaa, 0xa7, 0x37, 0x39, 0x35, 0x32, + 0xca, 0x28, 0x8e, 0x05, 0x83, 0x56, 0xad, 0x03, 0xbd, 0x04, 0xa5, + 0x6d, 0x06, 0x0c, 0x08, 0xe2, 0xa2, 0x00, 0xd1, 0xa0, 0x70, 0xc8, + 0xd1, 0x60, 0xb3, 0x11, 0xa0, 0x94, 0xc0, 0x60, 0x99, 0x11, 0xb9, + 0x0c, 0xd2, 0x5c, 0x04, 0xe2, 0x5c, 0x05, 0x92, 0x66, 0x92, 0x82, + 0x66, 0x93, 0xc2, 0x22, 0x15, 0x06, 0xb9, 0xff, 0x2c, 0x8f, 0x37, + 0xbf, 0x02, 0x06, 0x36, 0x00, 0x32, 0xca, 0x1e, 0x86, 0xef, 0xff, + 0x3d, 0x0c, 0x8e, 0x06, 0x9f, 0x2e, 0x16, 0x68, 0x05, 0xd1, 0x54, + 0x71, 0xd8, 0x0d, 0xb9, 0xe1, 0xd0, 0xca, 0x41, 0xc7, 0xb3, 0x1a, + 0xf8, 0x01, 0xf2, 0x2f, 0x18, 0xb9, 0xe1, 0xf2, 0x1f, 0x14, 0x30, + 0xec, 0xc0, 0xf0, 0xff, 0x11, 0xf7, 0x3e, 0x02, 0x86, 0x2e, 0x00, + 0x3d, 0x0c, 0x46, 0x04, 0x00, 0xc0, 0xa3, 0xc0, 0x40, 0xb4, 0x20, + 0x65, 0x66, 0x06, 0xb2, 0x21, 0x0e, 0x40, 0x33, 0x80, 0xa0, 0x33, + 0xc0, 0x91, 0x24, 0x71, 0x92, 0x19, 0x00, 0x3a, 0x39, 0x40, 0x83, + 0xc0, 0x82, 0xc8, 0xec, 0x87, 0xbb, 0x0b, 0x40, 0x33, 0xc0, 0x40, + 0xa3, 0xc0, 0xa2, 0xca, 0xec, 0xa7, 0x3b, 0xf3, 0xc1, 0x0f, 0x70, + 0xc2, 0x0c, 0x00, 0x16, 0xbc, 0x00, 0xd1, 0x76, 0x71, 0xd2, 0x2d, + 0x00, 0x66, 0x1d, 0x02, 0x32, 0xcb, 0x14, 0x30, 0xa3, 0x20, 0xbd, + 0x04, 0x65, 0x62, 0x06, 0xa0, 0xe4, 0xc0, 0xb2, 0xa2, 0x00, 0x91, + 0xa0, 0x70, 0x88, 0xd1, 0x60, 0xf3, 0x11, 0xf9, 0x08, 0x92, 0x58, + 0x04, 0xb2, 0x58, 0x05, 0x8e, 0x06, 0x82, 0x4e, 0x6d, 0x07, 0x8e, + 0x34, 0x82, 0x4c, 0x80, 0xff, 0x3c, 0x2e, 0x37, 0x3e, 0x05, 0x32, + 0xca, 0x19, 0x06, 0xb8, 0xff, 0x3c, 0xcf, 0x37, 0x3f, 0x1f, 0x32, + 0xca, 0xe7, 0x46, 0xb5, 0xff, 0x40, 0xb4, 0x20, 0xd0, 0xa0, 0x60, + 0xa0, 0xaa, 0x41, 0xa0, 0xa3, 0x80, 0xa5, 0x5a, 0x06, 0xb2, 0x21, + 0x0e, 0x4a, 0x33, 0xa0, 0x33, 0xc0, 0x46, 0xd0, 0xff, 0x4c, 0x68, + 0x37, 0x38, 0x05, 0x32, 0xca, 0xe2, 0x06, 0xac, 0xff, 0x32, 0xca, + 0xd8, 0x86, 0xaa, 0xff, 0xc2, 0x22, 0x15, 0x06, 0x6f, 0x8e, 0x07, + 0x9d, 0x78, 0x20, 0x62, 0x20, 0x21, 0x75, 0x71, 0x32, 0x02, 0x8e, + 0x07, 0xb5, 0x31, 0x41, 0x24, 0x70, 0x52, 0xa0, 0x00, 0x42, 0x04, + 0x00, 0x0c, 0x02, 0x16, 0x34, 0x06, 0x71, 0x78, 0x71, 0xa9, 0x01, + 0x1b, 0xa3, 0xbd, 0x04, 0xe5, 0x4b, 0x06, 0xb1, 0x20, 0x70, 0xa0, + 0x30, 0x74, 0xb0, 0xb3, 0xa0, 0xb8, 0x0b, 0xc2, 0x26, 0x15, 0xa2, + 0x0b, 0x00, 0x92, 0x0c, 0x0f, 0x8e, 0x07, 0x81, 0xa3, 0x5e, 0x69, + 0x2f, 0xd2, 0x0c, 0x07, 0x2d, 0x0b, 0x26, 0x1d, 0x36, 0x0c, 0x1c, + 0xad, 0x0b, 0x81, 0x5b, 0x71, 0x42, 0x2b, 0x18, 0x88, 0x58, 0x8e, + 0x05, 0x8e, 0x12, 0xc8, 0x01, 0xb2, 0x14, 0x14, 0xc0, 0xaa, 0xc0, + 0x60, 0xbb, 0x11, 0x65, 0x51, 0x06, 0xa7, 0xb7, 0x14, 0x8e, 0x07, + 0xaa, 0x1c, 0x55, 0x50, 0x50, 0x74, 0x47, 0x35, 0xa3, 0x46, 0x00, + 0x8e, 0x05, 0xc8, 0x59, 0x81, 0x75, 0x71, 0x39, 0x08, 0x8e, 0x07, + 0xe1, 0x30, 0x79, 0x71, 0x21, 0x7b, 0x71, 0x81, 0x6f, 0x71, 0x31, + 0x5b, 0x71, 0x91, 0x7a, 0x71, 0x99, 0x73, 0x48, 0x83, 0x49, 0x08, + 0x29, 0x83, 0xa9, 0x63, 0x8e, 0x08, 0x81, 0x80, 0x78, 0x12, 0x00, + 0x81, 0x7c, 0x71, 0xa7, 0x38, 0x69, 0xb2, 0xa0, 0x64, 0x21, 0x7d, + 0x71, 0x81, 0x7e, 0x71, 0x22, 0x22, 0x00, 0x91, 0x7f, 0x8e, 0x04, + 0x06, 0x90, 0xaa, 0x80, 0x8a, 0x22, 0xe5, 0x3a, 0x06, 0x31, 0x81, + 0x71, 0xb1, 0x82, 0x71, 0x0c, 0x04, 0x2c, 0x08, 0x0c, 0x4c, 0xc0, + 0xca, 0x63, 0x20, 0xcc, 0x90, 0xa1, 0x80, 0x71, 0x21, 0x83, 0x71, + 0x76, 0xa8, 0x34, 0xb0, 0xfa, 0x20, 0xc0, 0x20, 0x00, 0xe8, 0x0f, + 0x92, 0x0c, 0x00, 0xe0, 0xd8, 0x75, 0x00, 0x8d, 0x23, 0x97, 0xa8, + 0x1e, 0x92, 0x0c, 0x01, 0x20, 0x8e, 0x00, 0x10, 0x00, 0x99, 0x23, + 0xda, 0x99, 0x00, 0x99, 0x23, 0x40, 0x99, 0x53, 0x80, 0x99, 0x01, + 0x30, 0x99, 0x10, 0x8e, 0x06, 0xde, 0x5b, 0x89, 0x0f, 0x4b, 0xaa, + 0x8e, 0x05, 0x8d, 0x38, 0x92, 0x02, 0x03, 0x0c, 0x05, 0x8c, 0x69, + 0x26, 0x49, 0x04, 0x26, 0x69, 0x01, 0x0c, 0x15, 0x81, 0x7d, 0x71, + 0xad, 0x02, 0x48, 0x08, 0x88, 0x18, 0x38, 0x04, 0x8e, 0x05, 0xad, + 0x2f, 0x2d, 0x50, 0xa0, 0x74, 0xcc, 0x9a, 0x57, 0x69, 0x07, 0xad, + 0x02, 0xa5, 0xf5, 0xff, 0x92, 0x03, 0x2d, 0x90, 0xb7, 0x04, 0x16, + 0xeb, 0x0e, 0x92, 0xa3, 0xe0, 0x82, 0xa4, 0x80, 0xb2, 0xae, 0x00, + 0xf8, 0x04, 0xd1, 0xe6, 0x70, 0xf2, 0xdf, 0x05, 0xf2, 0x1f, 0x28, + 0xa2, 0xac, 0x00, 0xf0, 0xf0, 0x84, 0xc0, 0x20, 0x00, 0xe2, 0x2d, + 0x69, 0xb0, 0xee, 0x10, 0xf0, 0xee, 0x8e, 0x04, 0x68, 0xc8, 0x04, + 0xe2, 0x6d, 0x69, 0x8a, 0xcc, 0xb2, 0x0c, 0xd4, 0xe2, 0x0c, 0xd7, + 0xf2, 0x0c, 0xd5, 0x80, 0xee, 0x01, 0x80, 0xff, 0x11, 0xc2, 0x0c, + 0xd6, 0xf0, 0xbb, 0x20, 0x00, 0xcc, 0x11, 0xe0, 0xcc, 0x20, 0xc0, + 0x8e, 0x05, 0xe0, 0x6e, 0xf8, 0x04, 0xb2, 0x6d, 0x6a, 0x8a, 0xff, + 0xe2, 0x0f, 0xd8, 0xb2, 0x0f, 0xdb, 0xc2, 0x0f, 0xd9, 0x80, 0xbb, + 0x01, 0x80, 0xcc, 0x11, 0xf2, 0x0f, 0xda, 0xc0, 0xee, 0x20, 0x00, + 0xff, 0x11, 0xb0, 0xff, 0x20, 0x8e, 0x06, 0x50, 0xe2, 0x6d, 0x6b, + 0xc0, 0x20, 0x00, 0xc2, 0x2d, 0x87, 0xb8, 0x04, 0xa0, 0xcc, 0x10, + 0x8a, 0xbb, 0xe2, 0x0b, 0xdd, 0xb2, 0x0b, 0xdc, 0xb0, 0xee, 0x11, + 0x90, 0xee, 0x10, 0xb0, 0xb0, 0x44, 0xe0, 0xbb, 0x8e, 0x07, 0x50, + 0xb2, 0x6d, 0x87, 0xc0, 0x20, 0x00, 0xf2, 0x2d, 0x89, 0xe8, 0x04, + 0xa0, 0xff, 0x10, 0x8a, 0xee, 0x82, 0x0e, 0xdf, 0xe2, 0x0e, 0xde, + 0xb0, 0x88, 0x11, 0x90, 0x88, 0x10, 0xe0, 0xe0, 0x44, 0x80, 0xee, + 0x8e, 0x09, 0x50, 0x89, 0xc0, 0x20, 0x00, 0xf1, 0x84, 0x71, 0x81, + 0x85, 0x71, 0xc8, 0x04, 0xe2, 0x2d, 0x85, 0xc2, 0xdc, 0x05, 0xc2, + 0x2c, 0x18, 0x80, 0xee, 0x10, 0xf0, 0xcc, 0x10, 0xe0, 0xcc, 0x8e, + 0x04, 0x23, 0xc2, 0x6d, 0x85, 0x8e, 0x07, 0x83, 0x24, 0x92, 0x02, + 0x03, 0x0c, 0x0a, 0x8e, 0x09, 0x82, 0x2c, 0x1a, 0x91, 0x7d, 0x71, + 0x98, 0x09, 0x98, 0x09, 0x82, 0x09, 0x06, 0xb2, 0xd9, 0x0f, 0xf6, + 0x28, 0x02, 0x86, 0x29, 0x00, 0xb2, 0x0b, 0x18, 0xa0, 0xc0, 0x74, + 0x0b, 0xbb, 0x56, 0xbb, 0x09, 0xe1, 0x80, 0x71, 0xd1, 0x82, 0x71, + 0x16, 0x4c, 0x09, 0xb2, 0xa4, 0x98, 0xa2, 0xa4, 0x18, 0xaa, 0xa9, + 0xba, 0xf9, 0x2c, 0x0c, 0xbd, 0x0a, 0x76, 0xac, 0x12, 0xc8, 0x0b, + 0xd0, 0x8e, 0x00, 0x20, 0xcc, 0x1c, 0xa7, 0x9b, 0x08, 0xc0, 0x20, + 0x00, 0xc9, 0x08, 0x4b, 0xee, 0x4b, 0xbb, 0x9d, 0x0f, 0xb2, 0xa2, + 0xcc, 0xa1, 0xd0, 0x70, 0x2c, 0x0c, 0x76, 0xac, 0x13, 0xc8, 0x09, + 0x8c, 0xec, 0xc0, 0x20, 0x00, 0xb2, 0x6a, 0x7c, 0x8e, 0x04, 0x7e, + 0x6a, 0x7d, 0x4b, 0xbb, 0x4b, 0x99, 0xb1, 0x86, 0x71, 0x1c, 0x0e, + 0x3d, 0xf0, 0x76, 0xae, 0x0e, 0xd0, 0x8b, 0x20, 0xf2, 0x29, 0x21, + 0xc0, 0x20, 0x00, 0xf9, 0x08, 0x8e, 0x05, 0x19, 0x87, 0x71, 0xa1, + 0x88, 0x71, 0x0c, 0x4c, 0x3d, 0xf0, 0x76, 0xac, 0x1b, 0xd0, 0xcb, + 0x20, 0x82, 0x29, 0x31, 0xc0, 0x20, 0x00, 0x89, 0x0c, 0xd0, 0xfa, + 0x20, 0xe2, 0x8e, 0x05, 0x0b, 0xe9, 0x0f, 0x4b, 0xaa, 0x8e, 0x04, + 0x29, 0x81, 0x89, 0x71, 0x88, 0x08, 0x8e, 0x07, 0x81, 0x90, 0x48, + 0x1d, 0xf0, 0xa1, 0x8a, 0x71, 0xb1, 0x8b, 0x71, 0xaa, 0xa9, 0xc6, + 0xd9, 0x8e, 0x08, 0xe9, 0x58, 0x02, 0x03, 0x0c, 0x09, 0x8c, 0x6a, + 0x26, 0x4a, 0x04, 0x26, 0x6a, 0x01, 0x0c, 0x19, 0x90, 0x80, 0x74, + 0x56, 0xe8, 0x06, 0x81, 0x7d, 0x71, 0x8e, 0x07, 0xe0, 0x57, 0x0c, + 0x03, 0x71, 0x8c, 0x71, 0x41, 0xe6, 0x70, 0x61, 0x6b, 0x70, 0x81, + 0x14, 0x70, 0x82, 0x28, 0xab, 0x8e, 0x05, 0xa0, 0x1b, 0xff, 0xb2, + 0xa0, 0x80, 0xc0, 0x20, 0x00, 0xd2, 0x24, 0x8f, 0xd0, 0xd0, 0x74, + 0xec, 0xa3, 0xc0, 0x20, 0x00, 0xc2, 0x24, 0x90, 0x00, 0x5c, 0x23, + 0xd0, 0xfa, 0xc0, 0x50, 0xff, 0x82, 0xba, 0xff, 0x9a, 0xef, 0xf0, + 0xef, 0xb3, 0xe0, 0xe8, 0x21, 0xe6, 0x3e, 0x1b, 0xc0, 0x20, 0x00, + 0x82, 0x26, 0xaf, 0x07, 0xe8, 0x12, 0xad, 0x02, 0x65, 0xeb, 0xff, + 0x1d, 0xf0, 0x66, 0x13, 0xd9, 0x8e, 0x04, 0x2f, 0x27, 0x90, 0x46, + 0xf3, 0xff, 0x1b, 0x33, 0x30, 0x30, 0x74, 0x66, 0x23, 0xa5, 0x8e, + 0x0d, 0xe8, 0x6c, 0x7d, 0x71, 0x8e, 0x09, 0x81, 0xa5, 0x0b, 0x31, + 0x89, 0x71, 0x88, 0x8e, 0x08, 0x81, 0x8f, 0x5c, 0x88, 0x8e, 0x0b, + 0x81, 0xa3, 0x03, 0x00, 0x8e, 0x09, 0xe2, 0x20, 0xad, 0x02, 0x92, + 0x02, 0x03, 0x0c, 0x1b, 0x8e, 0x09, 0x83, 0x1c, 0x0b, 0xc8, 0xd1, + 0x81, 0x7d, 0x71, 0x52, 0xa0, 0x88, 0x92, 0xa5, 0xc0, 0xb0, 0x95, + 0x83, 0x28, 0x08, 0xb8, 0xe1, 0x58, 0xc1, 0x28, 0x02, 0x59, 0x01, + 0xb9, 0x21, 0xc9, 0x11, 0xbd, 0x03, 0x9a, 0x22, 0x98, 0xf1, 0x99, + 0x31, 0x88, 0x48, 0x8e, 0x05, 0xa1, 0x74, 0x66, 0x23, 0x17, 0x82, + 0x02, 0x2b, 0x67, 0x68, 0x11, 0xad, 0x04, 0x0c, 0x2c, 0x81, 0x00, + 0x70, 0xb2, 0xa0, 0xe4, 0x82, 0x28, 0x3f, 0xba, 0xb2, 0x8e, 0x0a, + 0xab, 0x28, 0x62, 0xa0, 0x88, 0x41, 0x7d, 0x71, 0x52, 0x02, 0x03, + 0x48, 0x04, 0x0c, 0x12, 0x48, 0x04, 0x8c, 0x65, 0x26, 0x45, 0x04, + 0x26, 0x65, 0x01, 0x0c, 0x02, 0x52, 0xa5, 0xc0, 0x20, 0x56, 0x83, + 0x5a, 0x44, 0x42, 0x04, 0xe6, 0x42, 0x43, 0x8e, 0x06, 0xd1, 0x60, + 0x31, 0x90, 0x71, 0x81, 0x8f, 0x71, 0xd1, 0x8d, 0x71, 0x61, 0x7d, + 0x71, 0xb8, 0x22, 0x91, 0x89, 0x71, 0x41, 0x14, 0x70, 0x98, 0x09, + 0xa2, 0x24, 0x1b, 0xb9, 0x06, 0xa9, 0x36, 0xd2, 0x64, 0x1b, 0xa1, + 0x8e, 0x00, 0x71, 0xc8, 0x69, 0xc9, 0x26, 0x89, 0x69, 0xb8, 0x19, + 0xb9, 0x16, 0xa9, 0x19, 0x52, 0x24, 0x30, 0x59, 0x46, 0x32, 0x64, + 0x30, 0x8e, 0x07, 0x81, 0x9f, 0x50, 0x14, 0x61, 0x80, 0x71, 0xe0, + 0x52, 0x11, 0x59, 0x01, 0x41, 0x82, 0x71, 0x6a, 0x55, 0x40, 0x55, + 0x20, 0xc0, 0x20, 0x00, 0x58, 0x05, 0x81, 0x91, 0x71, 0x50, 0xa8, + 0x75, 0x88, 0x08, 0x30, 0xaa, 0xc0, 0x82, 0x28, 0x10, 0xa0, 0xa0, + 0xf4, 0xe0, 0x08, 0x00, 0xa0, 0x82, 0xc0, 0x89, 0x31, 0x27, 0xba, + 0x0f, 0x60, 0x9a, 0xa0, 0x76, 0x98, 0x09, 0x40, 0xc9, 0x8e, 0x04, + 0x2a, 0x59, 0x0c, 0x4b, 0x99, 0x61, 0x88, 0x71, 0x7c, 0xf5, 0x71, + 0x87, 0x71, 0x0c, 0x13, 0x40, 0xc7, 0x20, 0xc9, 0x11, 0xdd, 0x0a, + 0xc0, 0x20, 0x00, 0x40, 0xb6, 0x20, 0xb9, 0x21, 0x8e, 0x05, 0xb5, + 0x16, 0xb8, 0x0b, 0x00, 0x02, 0x40, 0xc0, 0xf0, 0x91, 0xf0, 0xf0, + 0x04, 0x00, 0x02, 0x40, 0xb0, 0xe0, 0x91, 0xe0, 0xe0, 0x04, 0x27, + 0xba, 0x21, 0x88, 0x31, 0x76, 0x98, 0x1c, 0x00, 0x1d, 0x40, 0x1b, + 0xdd, 0x00, 0x8f, 0xa1, 0x00, 0x93, 0xa1, 0x50, 0x99, 0x30, 0xc0, + 0xc9, 0x10, 0xb0, 0xb9, 0x10, 0x80, 0xcc, 0x20, 0x00, 0x8e, 0x00, + 0xa1, 0x80, 0x8e, 0x06, 0x87, 0x23, 0x11, 0xc9, 0x0f, 0xc0, 0x20, + 0x00, 0x4b, 0x66, 0x4b, 0x77, 0xe8, 0x21, 0xd1, 0x92, 0x71, 0xb9, + 0x0e, 0xd7, 0x97, 0x9a, 0x20, 0xe1, 0x41, 0xd1, 0x86, 0x71, 0xe0, + 0xee, 0x11, 0xda, 0xee, 0x40, 0x8e, 0x05, 0x86, 0x51, 0xe8, 0x0e, + 0x07, 0xe2, 0x05, 0xe0, 0xe0, 0xd4, 0x86, 0x00, 0x00, 0xe0, 0xee, + 0xd4, 0xb8, 0x01, 0xcd, 0x0a, 0x27, 0xba, 0x27, 0xf1, 0x93, 0x71, + 0x20, 0x9e, 0x11, 0xf0, 0x99, 0x10, 0xf8, 0x31, 0x76, 0x9f, 0x19, + 0x1b, 0x8c, 0xc0, 0x8c, 0xb3, 0x80, 0x81, 0x21, 0xd0, 0x88, 0xa0, + 0x40, 0x8e, 0x05, 0x89, 0x0d, 0xe8, 0x08, 0xc0, 0x20, 0x00, 0x99, + 0x08, 0x1b, 0xcc, 0xd2, 0xa2, 0xcc, 0xda, 0x9b, 0xb1, 0xd0, 0x70, + 0x8e, 0x05, 0xe8, 0x1e, 0x8e, 0x05, 0x85, 0x7e, 0x2b, 0x7d, 0x27, + 0xba, 0x16, 0xe2, 0x21, 0x03, 0xd0, 0x9a, 0xa0, 0x76, 0x9e, 0x0d, + 0x8e, 0x0a, 0x18, 0x6b, 0x7d, 0x4b, 0x99, 0x92, 0xa2, 0xcc, 0x7c, + 0xed, 0x3d, 0xf0, 0x76, 0xaa, 0x19, 0x8e, 0x0c, 0x30, 0x07, 0x6c, + 0x08, 0xd0, 0xfc, 0x8e, 0x05, 0xe7, 0x7c, 0x8e, 0x04, 0x24, 0x8e, + 0x05, 0x9a, 0x04, 0x71, 0x94, 0x71, 0x98, 0x07, 0x99, 0x51, 0x92, + 0x09, 0x39, 0x0c, 0x06, 0x8e, 0x09, 0x84, 0x23, 0x16, 0xb2, 0xa1, + 0xea, 0xd2, 0xa2, 0x76, 0xe2, 0xa2, 0x83, 0x92, 0xa2, 0x85, 0x82, + 0xa2, 0x84, 0xf8, 0x51, 0xc2, 0xa2, 0x68, 0xa2, 0xcf, 0x36, 0xca, + 0xcf, 0x8a, 0x8f, 0x9a, 0x9f, 0xea, 0xef, 0xda, 0xdf, 0xba, 0xbf, + 0xb2, 0x0b, 0x85, 0xd9, 0x01, 0xe9, 0x11, 0x99, 0x31, 0x89, 0x21, + 0x92, 0xa2, 0x67, 0x81, 0x14, 0x70, 0xe2, 0xa2, 0x6c, 0xd2, 0xa2, + 0x6a, 0xda, 0xdf, 0xea, 0xef, 0x82, 0x28, 0x30, 0x9a, 0xff, 0xe0, + 0x08, 0x8e, 0x07, 0x97, 0x78, 0x88, 0x17, 0x8e, 0x05, 0x84, 0x4b, + 0x8e, 0x06, 0x86, 0x1b, 0x32, 0xa1, 0xea, 0xe0, 0x08, 0x00, 0xe1, + 0x8e, 0x05, 0xe4, 0x7b, 0x0c, 0x09, 0xf1, 0x8c, 0x71, 0x52, 0xa0, + 0xff, 0x0c, 0x2d, 0xc2, 0x2e, 0x8f, 0xb8, 0x07, 0xc0, 0xc0, 0x74, + 0xc0, 0xca, 0xc0, 0x3a, 0xab, 0x76, 0xad, 0x46, 0x82, 0x1a, 0x3d, + 0x00, 0x09, 0x40, 0x80, 0x80, 0xb1, 0x07, 0x68, 0x37, 0xcc, 0xa9, + 0x8e, 0x05, 0xe9, 0x68, 0x90, 0x00, 0xdd, 0x23, 0xc6, 0x02, 0x00, + 0x66, 0x19, 0x08, 0x8e, 0x04, 0x0f, 0x2f, 0x90, 0x46, 0xfb, 0xff, + 0xdc, 0x86, 0xd0, 0x8c, 0x82, 0xb2, 0xa0, 0x80, 0xba, 0x88, 0x5a, + 0xb8, 0x80, 0xb8, 0xb3, 0xb0, 0xb8, 0x21, 0xa6, 0x5b, 0x04, 0x0c, + 0x28, 0x82, 0x4a, 0x85, 0xb8, 0x07, 0x3a, 0xab, 0x92, 0xc9, 0x01, + 0x92, 0x0a, 0x7d, 0x77, 0x69, 0x20, 0xa2, 0xcb, 0x36, 0xb2, 0xc1, + 0x10, 0x65, 0xd5, 0xff, 0xa2, 0x22, 0x00, 0x16, 0x7a, 0x00, 0xb2, + 0x01, 0x10, 0x25, 0xdb, 0xff, 0x1d, 0xf0, 0xa8, 0x04, 0x8c, 0x4a, + 0xb2, 0x01, 0x10, 0x65, 0xda, 0x8e, 0x06, 0xd6, 0x28, 0x21, 0x94, + 0x71, 0x88, 0x22, 0x8e, 0x05, 0xe3, 0x10, 0xb8, 0x02, 0x2d, 0x0a, + 0xb2, 0xdb, 0x02, 0x92, 0x5b, 0x2f, 0x8e, 0x09, 0x99, 0x5c, 0x24, + 0x70, 0x91, 0x20, 0x70, 0xa2, 0x0a, 0x00, 0x0c, 0x0c, 0xac, 0xea, + 0x0c, 0x1d, 0xb2, 0xa0, 0x00, 0x76, 0x9a, 0x24, 0xa8, 0x09, 0xac, + 0x4a, 0xe2, 0x0a, 0x03, 0x4b, 0x99, 0x66, 0x3e, 0x16, 0xa2, 0x2a, + 0x18, 0x9c, 0x7a, 0xa8, 0x4a, 0xa2, 0x1a, 0x00, 0xcc, 0x3b, 0xbd, + 0x0a, 0x46, 0x01, 0x00, 0xb0, 0xea, 0xc0, 0xe0, 0xcd, 0x93, 0x8e, + 0x06, 0xcc, 0x20, 0x0c, 0x21, 0x94, 0x71, 0x16, 0x8c, 0x04, 0x31, + 0x91, 0x71, 0x88, 0x03, 0x88, 0xf8, 0x8e, 0x06, 0x87, 0x27, 0x08, + 0xe0, 0x08, 0x00, 0xa8, 0x02, 0x92, 0x0a, 0x01, 0x8c, 0x79, 0x88, + 0x03, 0x88, 0x78, 0x8e, 0x05, 0x0e, 0x0c, 0x03, 0x92, 0xca, 0x6c, + 0xb2, 0x0a, 0x02, 0x32, 0x6a, 0x1e, 0x9c, 0x4b, 0x8e, 0x05, 0xa5, + 0x73, 0x6e, 0xa2, 0xca, 0x60, 0x8e, 0x05, 0x1b, 0x32, 0x4a, 0x02, + 0x98, 0x02, 0x92, 0xc9, 0x6c, 0x32, 0x59, 0xf9, 0x1d, 0xf0, 0x88, + 0x32, 0x8e, 0x08, 0xd8, 0x30, 0x31, 0x98, 0x71, 0x51, 0x97, 0x71, + 0xd8, 0x52, 0x81, 0x96, 0x71, 0x41, 0x95, 0x71, 0xa1, 0x94, 0x71, + 0xb8, 0x54, 0xc8, 0x44, 0x98, 0x94, 0x99, 0x3a, 0x89, 0x44, 0x59, + 0x54, 0x39, 0x94, 0xd9, 0x0a, 0xc9, 0x1a, 0xb9, 0x2a, 0x8e, 0x07, + 0x88, 0x0c, 0xa1, 0x1e, 0x71, 0x7c, 0xfd, 0x88, 0x12, 0xc8, 0x02, + 0xc9, 0x11, 0x9d, 0x0c, 0xbd, 0x08, 0xf2, 0xcc, 0x1f, 0xd0, 0x7f, + 0x10, 0x79, 0x21, 0x79, 0x31, 0xc7, 0xbf, 0x01, 0x1b, 0xb8, 0xa0, + 0xeb, 0x10, 0x0c, 0x0b, 0x87, 0x3e, 0x0b, 0xe7, 0x98, 0x06, 0x68, + 0x21, 0xf8, 0x11, 0x67, 0xbf, 0x01, 0x0c, 0x1b, 0x9c, 0xdb, 0xc9, + 0x01, 0x0c, 0x0b, 0x57, 0x38, 0x07, 0x87, 0x95, 0x02, 0xc7, 0xb4, + 0x01, 0x0c, 0x1b, 0x8c, 0xcb, 0x0c, 0x02, 0x81, 0x99, 0x71, 0x88, + 0x08, 0x0c, 0x16, 0x80, 0x26, 0x93, 0x1d, 0xf0, 0xf8, 0x31, 0x0c, + 0x0b, 0xe7, 0x35, 0x07, 0xe7, 0x95, 0x02, 0x47, 0x8e, 0x04, 0x30, + 0xcc, 0x3b, 0xb8, 0x03, 0xc6, 0x03, 0x00, 0x8d, 0x05, 0x0c, 0x0b, + 0x9d, 0x04, 0xcd, 0x04, 0x59, 0x12, 0x49, 0x02, 0x0c, 0x0e, 0xe9, + 0x03, 0xc0, 0x64, 0xc0, 0x0c, 0x14, 0x60, 0x60, 0x74, 0x00, 0x16, + 0x40, 0x00, 0x44, 0xa1, 0xb7, 0x84, 0xba, 0xb0, 0xb4, 0x20, 0xb9, + 0x03, 0x07, 0x6b, 0x1e, 0x1b, 0x59, 0x4d, 0x08, 0xb0, 0xb1, 0x41, + 0x97, 0xb5, 0x01, 0x1b, 0x48, 0xd0, 0x95, 0x10, 0xa0, 0x84, 0x10, + 0x4d, 0x08, 0x1b, 0x59, 0x89, 0x12, 0x99, 0x02, 0xb9, 0x03, 0x07, + 0xeb, 0xe4, 0x0c, 0x12, 0x06, 0xe4, 0x8e, 0x07, 0x91, 0x30, 0xa2, + 0x12, 0x00, 0xa2, 0x51, 0x00, 0xb2, 0x12, 0x00, 0x51, 0x9a, 0x71, + 0xb2, 0xcb, 0x3f, 0xb0, 0xb0, 0xb4, 0xb2, 0x51, 0x01, 0xb7, 0xba, + 0x2c, 0xa7, 0x34, 0x02, 0x47, 0xbb, 0x26, 0xc2, 0xc4, 0x3f, 0xc0, + 0xc0, 0xb4, 0xc7, 0x3b, 0x06, 0xa7, 0x3c, 0x03, 0x8e, 0x06, 0x81, + 0x89, 0x1c, 0xbd, 0x03, 0xcd, 0x01, 0x88, 0x05, 0x2b, 0xd1, 0x88, + 0x38, 0xed, 0x04, 0xe0, 0x08, 0x00, 0xb2, 0x11, 0x01, 0xa2, 0x11, + 0x00, 0xa7, 0xbb, 0x1f, 0x47, 0xbb, 0x1c, 0xa7, 0xb4, 0x19, 0x92, + 0xca, 0xc1, 0x97, 0xa4, 0xd5, 0x8e, 0x11, 0x26, 0x8e, 0x04, 0x23, + 0xb4, 0x05, 0x91, 0xa0, 0x70, 0x90, 0x44, 0x20, 0xa0, 0x64, 0xc0, + 0x52, 0x23, 0x01, 0x42, 0x23, 0x00, 0x60, 0x60, 0x74, 0xbd, 0x05, + 0xcd, 0x06, 0xad, 0x04, 0x65, 0xca, 0x05, 0x07, 0xea, 0x34, 0x0c, + 0x0b, 0x0c, 0x1a, 0xcd, 0x06, 0x25, 0xc8, 0x05, 0xa0, 0x44, 0x20, + 0xb0, 0x55, 0x20, 0x59, 0x13, 0x49, 0x03, 0x07, 0x64, 0x1a, 0x00, + 0x41, 0x40, 0x92, 0x12, 0x00, 0x40, 0x45, 0x81, 0x1b, 0x99, 0x50, + 0x51, 0x41, 0x90, 0x90, 0xb4, 0x92, 0x52, 0x00, 0x8e, 0x05, 0x1b, + 0xe4, 0xea, 0x8e, 0x0e, 0x81, 0xb1, 0x04, 0x21, 0x9c, 0x71, 0x31, + 0x9a, 0x71, 0x81, 0x9b, 0x71, 0x38, 0x03, 0x29, 0x13, 0x89, 0x03, + 0x8e, 0x05, 0x83, 0x44, 0x8e, 0x09, 0xa3, 0x68, 0x9d, 0x71, 0x21, + 0x9e, 0x71, 0x22, 0x8e, 0x05, 0xf2, 0x4f, 0x8e, 0x07, 0x81, 0xa2, + 0x38, 0x9f, 0x8e, 0x10, 0x9d, 0x3b, 0x8e, 0x06, 0xad, 0x40, 0x20, + 0x82, 0x22, 0xd7, 0x66, 0x18, 0x24, 0x81, 0x1c, 0x70, 0x82, 0x28, + 0x10, 0x8e, 0x05, 0xe7, 0x64, 0x92, 0x22, 0xd7, 0x66, 0x19, 0x13, + 0xed, 0x03, 0xad, 0x03, 0xb1, 0xa0, 0x71, 0x81, 0x3c, 0x70, 0x0c, + 0x3c, 0x88, 0x38, 0xd1, 0xa1, 0x8e, 0x07, 0xaa, 0x2e, 0x36, 0x41, + 0x8e, 0x0a, 0xee, 0x28, 0x8e, 0x05, 0x52, 0x8e, 0x0e, 0x81, 0x99, + 0x6c, 0x51, 0x3c, 0x70, 0x62, 0xa1, 0x10, 0x72, 0xa2, 0x80, 0x42, + 0x22, 0x20, 0x56, 0x13, 0x04, 0xb2, 0x04, 0x85, 0x4a, 0xbb, 0xb2, + 0x0b, 0x43, 0xc2, 0x22, 0x16, 0xb0, 0xab, 0x90, 0xc0, 0xaa, 0xa0, + 0xa8, 0x2a, 0x67, 0xca, 0x2b, 0xb7, 0xea, 0x28, 0xc8, 0x04, 0x7a, + 0xa4, 0xe7, 0xec, 0x21, 0xe2, 0x24, 0xc2, 0xd2, 0x24, 0xc1, 0xe7, + 0x1d, 0x18, 0xf2, 0x0a, 0x7f, 0xc2, 0x0a, 0x7e, 0xf7, 0xbc, 0x0f, + 0x1b, 0x9c, 0x0c, 0x1c, 0x92, 0x4a, 0x7e, 0x88, 0xb5, 0x8e, 0x07, + 0x8c, 0x7a, 0x88, 0xa5, 0x8e, 0x07, 0x81, 0xa1, 0x12, 0x92, 0xa0, + 0xff, 0x97, 0x1a, 0x2d, 0xc2, 0x22, 0x16, 0xa0, 0xaa, 0x8e, 0x06, + 0x49, 0xd1, 0x9f, 0x71, 0x67, 0xca, 0x02, 0xb7, 0x6a, 0x04, 0xd8, + 0x2d, 0x26, 0x1d, 0x1d, 0xad, 0x02, 0x7a, 0x94, 0x0c, 0x1c, 0xc2, + 0x49, 0x7e, 0x0c, 0x0c, 0x88, 0xb5, 0xc2, 0x69, 0x21, 0x8e, 0x08, + 0x81, 0x8f, 0x54, 0x55, 0x8e, 0x05, 0x94, 0x44, 0x8e, 0x05, 0x82, + 0x1c, 0x8e, 0x08, 0x81, 0x32, 0x8e, 0x06, 0x8d, 0x6a, 0x1d, 0xf0, + 0x00, 0x36, 0x81, 0x00, 0x0c, 0x07, 0x3c, 0xdf, 0x49, 0x51, 0x62, + 0x22, 0x20, 0xb8, 0x43, 0xb9, 0x61, 0x62, 0x26, 0xcc, 0xe2, 0x2b, + 0x26, 0xb2, 0x1b, 0x4e, 0xa2, 0xce, 0x24, 0xea, 0xbb, 0x2b, 0x8a, + 0xb7, 0xb8, 0x1f, 0xd2, 0x0a, 0x01, 0xc2, 0x0a, 0x00, 0xaa, 0xdd, + 0x2b, 0xdd, 0xd7, 0x3b, 0x12, 0x26, 0x3c, 0x02, 0xf7, 0x9c, 0x02, + 0x72, 0x0a, 0x02, 0x8c, 0x17, 0x26, 0x3c, 0x04, 0xad, 0x0d, 0x86, + 0xf6, 0xff, 0xb2, 0xce, 0x10, 0x41, 0x00, 0x70, 0xad, 0x01, 0x82, + 0x24, 0x3f, 0x0c, 0x6c, 0xe0, 0x08, 0x00, 0x92, 0x06, 0x8c, 0x16, + 0xc9, 0x09, 0x8b, 0xa1, 0x0c, 0x0b, 0x82, 0x24, 0x3e, 0x0c, 0xcc, + 0xe0, 0x08, 0x00, 0xa8, 0x61, 0xc1, 0xa2, 0x71, 0x92, 0x2a, 0x26, + 0xb2, 0x1a, 0x4e, 0x92, 0xc9, 0x18, 0xca, 0xbb, 0xb2, 0x4a, 0x9c, + 0x92, 0x4a, 0x98, 0xb0, 0xb8, 0x41, 0x90, 0x98, 0x41, 0x92, 0x4a, + 0x99, 0xb2, 0x4a, 0x9d, 0x8e, 0x05, 0x09, 0x9a, 0x8e, 0x05, 0x06, + 0x9b, 0x16, 0x77, 0x06, 0x81, 0xa3, 0x71, 0x88, 0x48, 0xad, 0x07, + 0xe0, 0x08, 0x00, 0x52, 0x41, 0x0b, 0xbd, 0x01, 0x0c, 0x6c, 0x82, + 0x24, 0x3f, 0xa2, 0x51, 0x04, 0x98, 0x51, 0x92, 0x41, 0x0a, 0xcb, + 0xa1, 0x8e, 0x05, 0xdf, 0x2e, 0x8b, 0xb1, 0x0c, 0x08, 0xa2, 0x06, + 0x8c, 0x92, 0x11, 0x09, 0x40, 0xaa, 0x11, 0xa0, 0x99, 0x20, 0x92, + 0x51, 0x09, 0x82, 0x46, 0x8c, 0xa2, 0x02, 0x00, 0xf8, 0x43, 0x82, + 0xaf, 0x18, 0xd2, 0x2f, 0x26, 0xe2, 0x1f, 0x4e, 0xf0, 0xfd, 0xc0, + 0x8a, 0xff, 0x82, 0x24, 0xea, 0xf0, 0xf0, 0xf4, 0xe0, 0x08, 0x00, + 0x8c, 0xba, 0x82, 0x24, 0x9f, 0xa1, 0xa4, 0x71, 0xe0, 0x08, 0x00, + 0x1c, 0x02, 0x1d, 0xf0, 0x1c, 0xc2, 0x8e, 0x05, 0xd5, 0x19, 0x0c, + 0x09, 0xc2, 0x0c, 0x00, 0x0c, 0x1b, 0x9c, 0xac, 0xa2, 0x06, 0x8c, + 0x76, 0x9c, 0x20, 0x00, 0x19, 0x40, 0x00, 0xdb, 0xa1, 0xd0, 0xda, + 0x10, 0xd0, 0xd0, 0x74, 0x8c, 0xcd, 0x21, 0x20, 0x70, 0x20, 0x29, + 0xa0, 0x28, 0x02, 0xa2, 0x16, 0x47, 0xc6, 0xdc, 0xff, 0x8e, 0x05, + 0xa0, 0x16, 0x46, 0xfc, 0xff, 0x36, 0x41, 0x00, 0x28, 0x74, 0x16, + 0x62, 0x05, 0x42, 0x02, 0x01, 0x92, 0x02, 0x09, 0xb6, 0x24, 0x4d, + 0x42, 0xc4, 0xfa, 0xa6, 0x14, 0x47, 0x82, 0x02, 0x08, 0x80, 0x99, + 0x11, 0x90, 0x88, 0x20, 0x80, 0x80, 0xf4, 0x20, 0x58, 0xa0, 0xab, + 0x25, 0xe0, 0x88, 0x11, 0x80, 0x44, 0xc0, 0x42, 0xc4, 0xfe, 0xa6, + 0x14, 0x2a, 0xb2, 0x05, 0x0b, 0xa2, 0x05, 0x0a, 0x80, 0xbb, 0x11, + 0xb0, 0xaa, 0x20, 0xa0, 0xa0, 0xf4, 0x20, 0x2a, 0xa0, 0xe0, 0xaa, + 0x11, 0xa0, 0xa4, 0xc0, 0xa2, 0xca, 0xfe, 0xa6, 0x2a, 0x0c, 0xd2, + 0x02, 0x02, 0xc2, 0xa0, 0xc0, 0xd7, 0x0c, 0x8e, 0x06, 0xf0, 0x53, + 0x8e, 0x07, 0x81, 0x9e, 0x18, 0x52, 0x22, 0x20, 0x8e, 0x05, 0x86, + 0x53, 0x04, 0xe5, 0xf8, 0xff, 0xcc, 0x7a, 0x82, 0xd2, 0x02, 0x82, + 0x18, 0x6c, 0x67, 0xe8, 0x3d, 0x62, 0xa2, 0xa4, 0x6a, 0x65, 0x92, + 0x06, 0x80, 0x71, 0x3c, 0x70, 0x90, 0x91, 0x04, 0x56, 0x39, 0x15, + 0x51, 0x00, 0x70, 0x82, 0x02, 0x01, 0x92, 0x14, 0x18, 0x66, 0x18, + 0x14, 0x90, 0xa1, 0x04, 0x16, 0xfa, 0x07, 0xa1, 0xa5, 0x71, 0x82, + 0x25, 0xa0, 0xb1, 0xa6, 0x8e, 0x07, 0xd3, 0x39, 0x07, 0x69, 0x0c, + 0x82, 0x25, 0x9f, 0xa1, 0xa7, 0x8e, 0x05, 0xa2, 0x73, 0x02, 0x1d, + 0xf0, 0x81, 0xb5, 0x8e, 0x08, 0xfb, 0x2d, 0x66, 0x2a, 0x0a, 0xc8, + 0xb4, 0xb2, 0x14, 0x24, 0x16, 0x2c, 0x16, 0x16, 0xfb, 0x15, 0x66, + 0x3a, 0x19, 0xb2, 0x14, 0x24, 0xdc, 0x3b, 0x82, 0x25, 0xa0, 0x0c, + 0x1a, 0x0c, 0x09, 0xb0, 0x9a, 0x83, 0xbd, 0x09, 0xa1, 0xa8, 0x8e, + 0x04, 0x36, 0x86, 0xf1, 0xff, 0x8e, 0x05, 0x83, 0x13, 0xa2, 0x14, + 0x00, 0x8e, 0x07, 0xdb, 0x72, 0x23, 0x8e, 0x05, 0x40, 0xb2, 0x8e, + 0x05, 0x85, 0x0b, 0xb0, 0xaa, 0xa0, 0xc8, 0x2a, 0x16, 0x0a, 0x11, + 0xd2, 0xa1, 0x20, 0xd7, 0x4c, 0x02, 0xc6, 0x41, 0x00, 0xe2, 0x24, + 0x15, 0x16, 0xfe, 0x0b, 0xf2, 0x22, 0x11, 0xf0, 0xf0, 0x05, 0x16, + 0x6f, 0x0b, 0xb2, 0x22, 0x3b, 0x26, 0x2b, 0x05, 0x26, 0xcb, 0x02, + 0x66, 0x8b, 0x29, 0xad, 0x02, 0xcd, 0x03, 0xdd, 0x04, 0xe2, 0x06, + 0x80, 0xb2, 0xa0, 0xc0, 0x81, 0xa9, 0x71, 0xba, 0xb2, 0x88, 0x18, + 0xe0, 0xe0, 0x04, 0xe0, 0x08, 0x00, 0xcc, 0xaa, 0x8e, 0x07, 0x81, + 0x36, 0xaa, 0x8e, 0x05, 0xe3, 0x73, 0x22, 0x3b, 0x26, 0x4b, 0x05, + 0x26, 0xdb, 0x02, 0x66, 0xbb, 0x26, 0x8e, 0x12, 0x33, 0x28, 0x8e, + 0x0f, 0x33, 0xab, 0x8e, 0x05, 0xa3, 0x64, 0x02, 0x01, 0x26, 0x19, + 0x1a, 0xad, 0x02, 0x82, 0x27, 0x14, 0x8e, 0x06, 0x81, 0xb7, 0x15, + 0xda, 0x8e, 0x07, 0x1e, 0xac, 0x8e, 0x04, 0x1e, 0x06, 0xc1, 0xff, + 0x92, 0x06, 0x80, 0x27, 0xe9, 0x25, 0xa2, 0x24, 0x10, 0x9c, 0xfa, + 0xbd, 0x03, 0xd2, 0x0a, 0x06, 0xc2, 0x0a, 0x05, 0x81, 0x8e, 0x05, + 0xa6, 0x02, 0x28, 0x2d, 0x80, 0xdd, 0x11, 0xd0, 0xcc, 0x20, 0xc0, + 0xc0, 0xf4, 0x8e, 0x05, 0xa7, 0x06, 0x66, 0x1a, 0x36, 0x0c, 0x12, + 0x1d, 0xf0, 0xe2, 0x22, 0x3c, 0x92, 0x14, 0x18, 0x66, 0x1e, 0x14, + 0x90, 0xf4, 0x04, 0x16, 0x9f, 0xf3, 0x8e, 0x07, 0x4e, 0xad, 0x8e, + 0x05, 0x82, 0x10, 0xad, 0xff, 0x90, 0xa4, 0x04, 0x56, 0x4a, 0xf2, + 0x8e, 0x07, 0x15, 0xae, 0x8e, 0x04, 0x15, 0x46, 0xa8, 0xff, 0x8e, + 0x07, 0x0f, 0xaf, 0x8e, 0x05, 0x24, 0xa4, 0xff, 0xb2, 0x1a, 0x00, + 0x82, 0x25, 0xa1, 0xa1, 0xb0, 0x8e, 0x05, 0x83, 0x08, 0xa0, 0xff, + 0x0c, 0x1a, 0x0c, 0x09, 0x82, 0x25, 0xa1, 0xb0, 0x9a, 0x83, 0x0c, + 0x0b, 0xc0, 0xba, 0x83, 0xcd, 0x09, 0xa1, 0xb1, 0x8e, 0x05, 0x38, + 0x9a, 0xff, 0x8e, 0x05, 0x8b, 0x14, 0x32, 0x22, 0x20, 0x41, 0x45, + 0x70, 0xa2, 0x02, 0x00, 0x81, 0x1a, 0x71, 0x92, 0xa0, 0x88, 0x82, + 0x28, 0x1c, 0xa0, 0x99, 0xc1, 0xad, 0x02, 0x9a, 0x44, 0xe0, 0x08, + 0x00, 0xdc, 0x3a, 0xb2, 0xd3, 0x03, 0xb2, 0x0b, 0x0c, 0xc2, 0xa0, + 0xff, 0xc7, 0x1b, 0x08, 0x8e, 0x09, 0x88, 0x4b, 0xd2, 0x04, 0x04, + 0x66, 0x2d, 0x09, 0x0c, 0x0f, 0x0c, 0x0e, 0xe2, 0x43, 0x84, 0x46, + 0x01, 0x00, 0xf2, 0x02, 0x34, 0xf2, 0x43, 0x84, 0x82, 0x04, 0x04, + 0x62, 0xa0, 0xef, 0x66, 0x28, 0x72, 0xb2, 0x04, 0x05, 0xe6, 0x1b, + 0x02, 0x06, 0x33, 0x00, 0xed, 0x04, 0xd2, 0x02, 0x34, 0x0c, 0x07, + 0x0c, 0x05, 0x0c, 0x09, 0x99, 0x01, 0x16, 0xfd, 0x04, 0x0c, 0x0b, + 0x82, 0x22, 0x16, 0xb0, 0xcb, 0x90, 0x80, 0xcc, 0xa0, 0xbc, 0x6c, + 0x82, 0x1e, 0x03, 0x92, 0x1c, 0x00, 0x87, 0x99, 0x2e, 0xcc, 0xa5, + 0x8c, 0x8a, 0xc7, 0x9a, 0x06, 0x0c, 0x15, 0x70, 0x90, 0x74, 0x99, + 0x01, 0x3a, 0xcf, 0xb2, 0x4c, 0x44, 0x92, 0x03, 0x84, 0x3a, 0x99, + 0x82, 0x09, 0x04, 0x60, 0x88, 0x10, 0x82, 0x49, 0x04, 0xf2, 0x03, + 0x84, 0x1b, 0xff, 0xf0, 0xf0, 0x74, 0xf2, 0x43, 0x84, 0xd2, 0x02, + 0x34, 0x8e, 0x05, 0xe4, 0x4c, 0xd7, 0x3b, 0xb4, 0xb2, 0x04, 0x05, + 0x2b, 0xee, 0x1b, 0x77, 0xb7, 0x27, 0xa5, 0x86, 0x13, 0x00, 0x16, + 0x5f, 0x04, 0x0c, 0x0b, 0x0c, 0x05, 0x0c, 0x0c, 0xc9, 0x01, 0x82, + 0x04, 0x04, 0xba, 0xc3, 0xcc, 0xe5, 0xe2, 0x22, 0x16, 0xb0, 0xdb, + 0x90, 0xe0, 0xdd, 0xa0, 0xd7, 0x9a, 0x03, 0xb9, 0x01, 0x0c, 0x15, + 0xcc, 0x48, 0xb2, 0x4c, 0x44, 0xc6, 0x01, 0x00, 0xb0, 0x9f, 0xc0, + 0x0b, 0x99, 0x92, 0x4c, 0x44, 0xd2, 0x0c, 0x04, 0x1b, 0xbb, 0x60, + 0xdd, 0x10, 0xd2, 0x4c, 0x8e, 0x04, 0x60, 0xb0, 0xb0, 0x74, 0xf7, + 0x3b, 0xc4, 0x46, 0x01, 0x00, 0x0c, 0x05, 0x0c, 0x0e, 0xe9, 0x01, + 0x8c, 0xe5, 0x88, 0x01, 0xf2, 0x03, 0x44, 0x8a, 0x83, 0x92, 0x08, + 0x44, 0x92, 0x43, 0x44, 0xf2, 0x48, 0x44, 0x1d, 0xf0, 0x8e, 0x06, + 0x81, 0x46, 0x86, 0xf8, 0xff, 0x36, 0x41, 0x00, 0xa1, 0xb2, 0x71, + 0x81, 0x00, 0x70, 0xb1, 0xb3, 0x71, 0x82, 0x28, 0x3f, 0x1c, 0xac, + 0xe0, 0x08, 0x00, 0xc1, 0x9f, 0x71, 0x91, 0xb4, 0x71, 0xa1, 0x3c, + 0x70, 0x81, 0xb5, 0x71, 0x82, 0x6a, 0x35, 0x92, 0x6a, 0x39, 0xb2, + 0x2a, 0x16, 0xd8, 0xba, 0xe8, 0x9a, 0xf2, 0x2a, 0x22, 0xf9, 0x0c, + 0xe9, 0x4c, 0xd9, 0x3c, 0xb9, 0x1c, 0x91, 0xbb, 0x71, 0x92, 0x6a, + 0x16, 0xb1, 0xba, 0x71, 0xd1, 0xb8, 0x71, 0xe1, 0xb7, 0x71, 0xf1, + 0xb6, 0x71, 0xf2, 0x6a, 0x13, 0xe9, 0x8a, 0xd2, 0x6a, 0x22, 0xb9, + 0xba, 0xc1, 0xb9, 0x71, 0xc9, 0x9a, 0x8e, 0x08, 0x81, 0xbb, 0x04, + 0xbd, 0x03, 0x81, 0xbc, 0x71, 0xa1, 0x20, 0x70, 0x88, 0x08, 0x8e, + 0x05, 0xf6, 0x7b, 0x8e, 0x07, 0x8c, 0x48, 0x8e, 0x09, 0x16, 0x18, + 0x8e, 0x07, 0x16, 0x36, 0x41, 0x00, 0x8e, 0x06, 0x8c, 0x0e, 0x98, + 0x43, 0xad, 0x02, 0x92, 0x29, 0x20, 0xbd, 0x03, 0xcc, 0x49, 0x92, + 0x22, 0x18, 0x86, 0x00, 0x00, 0x92, 0x29, 0x8d, 0x9c, 0x59, 0xf2, + 0x2a, 0x2d, 0x8c, 0x4f, 0x26, 0x4f, 0x02, 0x66, 0x6f, 0x0b, 0xf2, + 0x09, 0x56, 0x82, 0xa0, 0xfd, 0x80, 0xff, 0x10, 0xf2, 0x49, 0x56, + 0x81, 0xbc, 0x71, 0x88, 0x28, 0x8e, 0x0c, 0xaa, 0x44, 0xad, 0x02, + 0x71, 0x8e, 0x05, 0xa5, 0x6c, 0x27, 0x18, 0x0c, 0x3c, 0xe0, 0x08, + 0x8e, 0x07, 0x53, 0x81, 0xbc, 0x8e, 0x0a, 0x8b, 0x2f, 0x0c, 0x1b, + 0x0c, 0x3c, 0x3d, 0x0a, 0x82, 0x27, 0x8e, 0x07, 0x8c, 0x6f, 0x8e, + 0x06, 0xad, 0x2c, 0x8e, 0x09, 0x81, 0xb5, 0x00, 0x1c, 0x0c, 0x82, + 0x24, 0x3e, 0x29, 0x41, 0xe0, 0x08, 0x00, 0x71, 0x44, 0x70, 0x62, + 0xa1, 0x4d, 0x51, 0x12, 0x70, 0xb8, 0x03, 0x0c, 0xf2, 0x4d, 0x0b, + 0x16, 0xab, 0x13, 0xf8, 0x05, 0xf8, 0xcf, 0x16, 0x3f, 0x13, 0x4c, + 0x8b, 0x92, 0xa0, 0xa0, 0x81, 0x00, 0x70, 0x28, 0x44, 0x29, 0x51, + 0x82, 0x28, 0x10, 0x8e, 0x07, 0x81, 0x85, 0x21, 0x8e, 0x05, 0x94, + 0x47, 0x8e, 0x06, 0x81, 0x84, 0x54, 0x0c, 0xf2, 0x92, 0xca, 0xf1, + 0x16, 0xa9, 0x10, 0xd2, 0xa0, 0xe8, 0xf8, 0x21, 0xe8, 0x01, 0xc8, + 0x51, 0xe0, 0xe4, 0x83, 0xb2, 0x1c, 0x50, 0x49, 0x21, 0xda, 0xdc, + 0xe9, 0x01, 0xf0, 0xef, 0x93, 0xe9, 0x11, 0xd2, 0x4c, 0x98, 0xb2, + 0x4c, 0x9c, 0xd0, 0xd8, 0x41, 0xb0, 0xb8, 0x41, 0xd2, 0x4c, 0x99, + 0xd0, 0xd8, 0x41, 0xd2, 0x4c, 0x9a, 0x8e, 0x05, 0x06, 0x9b, 0x48, + 0x54, 0xb2, 0x4c, 0x9d, 0x8c, 0x44, 0x26, 0xba, 0x97, 0x46, 0x01, + 0x00, 0x82, 0xca, 0xf0, 0x16, 0x28, 0x0c, 0x0c, 0x19, 0x48, 0x01, + 0xc8, 0x21, 0xcc, 0x74, 0xcc, 0x5c, 0xb8, 0x11, 0x0c, 0x0a, 0xb0, + 0x9a, 0x83, 0xcc, 0xc9, 0xd2, 0xd7, 0x01, 0xd2, 0xcd, 0x31, 0x8e, + 0x09, 0x81, 0xac, 0x47, 0x0e, 0xe2, 0x51, 0x06, 0xe2, 0x51, 0x07, + 0x47, 0x1c, 0x42, 0xb8, 0x03, 0xcc, 0xc4, 0xf2, 0xd7, 0x01, 0xf2, + 0xcf, 0x8e, 0x04, 0x1d, 0xf8, 0x0f, 0xc0, 0x20, 0x00, 0xb8, 0x5b, + 0x92, 0x11, 0x06, 0xd8, 0x05, 0x88, 0x44, 0x48, 0x54, 0x82, 0x18, + 0x50, 0x6a, 0xdd, 0x9a, 0x88, 0x82, 0x51, 0x06, 0xb9, 0x03, 0xcc, + 0x0b, 0x39, 0x13, 0xa2, 0x0d, 0x80, 0x0b, 0xaa, 0xa2, 0x4d, 0x80, + 0x92, 0x11, 0x07, 0x1b, 0x99, 0x92, 0x51, 0x07, 0x47, 0x9c, 0xc1, + 0x46, 0x00, 0x00, 0xb8, 0x03, 0x88, 0x05, 0xb8, 0x5b, 0xb9, 0x03, + 0x6a, 0x88, 0x8e, 0x04, 0x24, 0xf2, 0x08, 0x80, 0x0b, 0xff, 0xf2, + 0x48, 0x80, 0xe2, 0x11, 0x07, 0x1b, 0xee, 0xe2, 0x51, 0x07, 0xcc, + 0xc4, 0x92, 0xd7, 0x01, 0x92, 0xc9, 0x8e, 0x04, 0x5f, 0x8e, 0x06, + 0xc9, 0x33, 0x44, 0xd2, 0x11, 0x06, 0xc2, 0x1c, 0x50, 0xa8, 0x01, + 0xda, 0xcc, 0xc2, 0x51, 0x06, 0x16, 0x3a, 0xed, 0xa8, 0x41, 0x81, + 0x15, 0x70, 0xbd, 0x01, 0x82, 0x28, 0x1b, 0x8e, 0x05, 0xbd, 0x2e, + 0xb8, 0x03, 0xc6, 0xaf, 0xff, 0x81, 0x16, 0x70, 0xa8, 0x41, 0x8e, + 0x07, 0xa3, 0x07, 0x8e, 0x06, 0x84, 0x18, 0x48, 0x03, 0x81, 0x12, + 0x70, 0x16, 0xe4, 0x04, 0x88, 0x08, 0x88, 0xc8, 0x71, 0x00, 0x70, + 0x16, 0x48, 0x04, 0x4c, 0x8b, 0x82, 0x27, 0x10, 0x58, 0x44, 0x62, + 0xa0, 0xa0, 0x6a, 0x65, 0xad, 0x06, 0x8e, 0x0a, 0x82, 0x53, 0x8e, + 0x04, 0x0b, 0x0c, 0xfb, 0xb7, 0x1a, 0x40, 0xc2, 0x25, 0x26, 0x92, + 0x0c, 0x04, 0x07, 0xe9, 0x12, 0x4b, 0xac, 0xab, 0xbc, 0x82, 0x27, + 0x41, 0x8e, 0x05, 0x8e, 0x28, 0xcc, 0x3a, 0x0c, 0x19, 0x46, 0x02, + 0x00, 0x48, 0x54, 0x56, 0xd4, 0xfb, 0x06, 0x07, 0x00, 0x0c, 0x09, + 0x8c, 0x79, 0xad, 0x02, 0xbd, 0x03, 0xe5, 0xe2, 0xff, 0x1d, 0xf0, + 0x8e, 0x06, 0x83, 0x77, 0x8e, 0x06, 0x81, 0xad, 0x02, 0x8e, 0x05, + 0xa9, 0x01, 0x09, 0xc6, 0xf7, 0xff, 0x36, 0x41, 0x00, 0xd1, 0xc1, + 0x71, 0xe1, 0xc0, 0x71, 0xf1, 0xbf, 0x71, 0xc1, 0xc2, 0x71, 0xb1, + 0xbe, 0x71, 0x31, 0x00, 0x70, 0x22, 0x2b, 0x6c, 0xa2, 0x2b, 0x44, + 0x32, 0x23, 0x56, 0x42, 0x2b, 0x42, 0x38, 0x13, 0xc2, 0x6b, 0x42, + 0xf2, 0x6b, 0x44, 0x26, 0x53, 0x02, 0x66, 0x63, 0x06, 0x91, 0xbd, + 0x71, 0x0c, 0xa8, 0x89, 0x09, 0x31, 0xbc, 0x71, 0x82, 0x2b, 0x67, + 0x92, 0x2b, 0x45, 0xe2, 0x6b, 0x45, 0xd2, 0x6b, 0x67, 0x99, 0x13, + 0x89, 0x23, 0x49, 0x33, 0xa9, 0x03, 0x29, 0x43, 0xa1, 0xc3, 0x71, + 0xa2, 0x6b, 0x6c, 0x8e, 0x06, 0x81, 0x54, 0x61, 0x15, 0x70, 0xb2, + 0xa1, 0x64, 0x71, 0x00, 0x70, 0x52, 0x22, 0x2c, 0xc2, 0x13, 0x06, + 0xbc, 0x95, 0xa1, 0xc4, 0x71, 0xb8, 0x03, 0x82, 0x27, 0xa1, 0x42, + 0xd5, 0x04, 0x48, 0x44, 0xe0, 0x08, 0x00, 0x88, 0x34, 0x0b, 0x88, + 0x56, 0x58, 0x0c, 0xa8, 0x24, 0x98, 0x13, 0xa0, 0x99, 0xc0, 0x16, + 0xb9, 0x0b, 0x82, 0x27, 0x9f, 0xa1, 0xc5, 0x8e, 0x05, 0xc7, 0x22, + 0x02, 0x81, 0x22, 0x71, 0xc8, 0x03, 0x82, 0x28, 0x12, 0x8e, 0x07, + 0x82, 0x24, 0xc7, 0xbb, 0x10, 0xad, 0x02, 0x0c, 0x1b, 0x82, 0x26, + 0x46, 0x8e, 0x05, 0xab, 0x34, 0x4d, 0x0a, 0xc6, 0x03, 0x00, 0x8e, + 0x04, 0x11, 0x1c, 0x0c, 0x82, 0x26, 0x18, 0x8e, 0x05, 0x8a, 0x61, + 0x4d, 0x0a, 0x16, 0x84, 0x07, 0xbc, 0x45, 0x8e, 0x05, 0x3a, 0xb8, + 0x03, 0x88, 0x08, 0x8e, 0x05, 0x99, 0x4e, 0xec, 0x4a, 0x8e, 0x09, + 0x53, 0xc8, 0x03, 0x81, 0x22, 0x71, 0xad, 0x02, 0x8e, 0x08, 0x53, + 0xad, 0x02, 0xbd, 0x04, 0x82, 0x26, 0x8e, 0x06, 0xb1, 0x17, 0x1d, + 0xf0, 0xeb, 0xb3, 0xc2, 0x13, 0x06, 0xa8, 0x44, 0x82, 0x27, 0x3f, + 0xa2, 0x2a, 0x26, 0xe0, 0x08, 0x00, 0x82, 0x26, 0x4a, 0xa8, 0x44, + 0x92, 0x13, 0x06, 0xbd, 0x04, 0x92, 0x4a, 0x96, 0x8e, 0x05, 0x90, + 0x42, 0x97, 0x8e, 0x05, 0x82, 0x37, 0x9c, 0x5a, 0x8c, 0x75, 0x8e, + 0x0b, 0x81, 0x25, 0x8e, 0x0c, 0x43, 0x8e, 0x08, 0x81, 0x5d, 0xc2, + 0x13, 0x06, 0xe0, 0x08, 0x00, 0xa2, 0x13, 0x06, 0x92, 0xa1, 0x64, + 0xa7, 0xb9, 0x8e, 0x08, 0x81, 0x36, 0x4c, 0x8e, 0x0d, 0x81, 0x36, + 0x4c, 0x8e, 0x0b, 0x81, 0x36, 0x8e, 0x0e, 0x38, 0x56, 0xb4, 0xf3, + 0x8e, 0x1a, 0x82, 0x05, 0x8e, 0x09, 0x95, 0x08, 0xa1, 0x00, 0x39, + 0x31, 0xb1, 0x24, 0x70, 0x40, 0x60, 0x74, 0x88, 0x43, 0x7d, 0x02, + 0x50, 0x90, 0x74, 0x99, 0x51, 0x0c, 0x02, 0x92, 0x28, 0x26, 0x99, + 0x81, 0x82, 0x18, 0x4e, 0x89, 0x61, 0x9a, 0x98, 0x92, 0xc9, 0xff, + 0x76, 0x98, 0x07, 0xa2, 0x09, 0x00, 0xa2, 0x49, 0x12, 0x0b, 0x99, + 0xb2, 0x0b, 0x00, 0x0c, 0x04, 0x76, 0x9b, 0x0e, 0x00, 0x04, 0x40, + 0x1b, 0x44, 0x60, 0xc0, 0xb1, 0x07, 0x6c, 0x01, 0x1b, 0x22, 0x3d, + 0xf0, 0xa2, 0xa0, 0x00, 0xa5, 0x8b, 0xfa, 0xd2, 0xca, 0xfc, 0x27, + 0xad, 0x05, 0x22, 0xa0, 0x00, 0x90, 0x00, 0x00, 0xc1, 0x24, 0x70, + 0x0c, 0x04, 0xc2, 0x0c, 0x00, 0x0c, 0x02, 0x16, 0x8c, 0x19, 0x79, + 0x01, 0xd8, 0x61, 0xf8, 0x81, 0x4b, 0xe7, 0xe9, 0x21, 0xcb, 0x5f, + 0x6b, 0xdd, 0xd9, 0x41, 0xf2, 0xcf, 0x22, 0x59, 0x71, 0x51, 0x20, + 0x70, 0xf9, 0x11, 0x78, 0x05, 0x00, 0x04, 0x40, 0x60, 0x80, 0xb1, + 0x80, 0x80, 0x04, 0x16, 0xe8, 0x0c, 0x8e, 0x0d, 0x81, 0x82, 0x1c, + 0x3d, 0x8e, 0x0e, 0x81, 0xb4, 0x6a, 0x92, 0x07, 0x02, 0x26, 0x29, + 0x08, 0x26, 0x49, 0x05, 0x26, 0x39, 0x02, 0x66, 0x59, 0x3f, 0x92, + 0x27, 0x2c, 0x92, 0xd9, 0x04, 0x98, 0x49, 0x16, 0x59, 0x0a, 0x98, + 0x29, 0xb8, 0x71, 0x66, 0x09, 0x02, 0xc6, 0x26, 0x00, 0x92, 0x4b, + 0x00, 0x90, 0xa8, 0x41, 0xa2, 0x4b, 0x01, 0x98, 0x81, 0x8e, 0x05, + 0xff, 0x4a, 0x02, 0x8e, 0x05, 0x06, 0x03, 0x98, 0x39, 0x26, 0x09, + 0x32, 0xb2, 0x07, 0x03, 0xd2, 0x07, 0x02, 0x66, 0x3b, 0x29, 0x66, + 0x4d, 0x26, 0x06, 0x2e, 0x00, 0x81, 0x3d, 0x70, 0x88, 0xa8, 0x8e, + 0x05, 0xaf, 0x1d, 0x92, 0x1a, 0x00, 0xa8, 0x81, 0x92, 0x4a, 0x0c, + 0x8e, 0x05, 0x83, 0x1c, 0x0d, 0x8e, 0x05, 0x06, 0x0e, 0x8e, 0x05, + 0x06, 0x0f, 0xa8, 0x51, 0xc8, 0x81, 0xb8, 0x61, 0xb2, 0x4c, 0x10, + 0xa0, 0xa4, 0xc0, 0xb0, 0xb8, 0x41, 0xb2, 0x4c, 0x11, 0x56, 0x4a, + 0x0c, 0xc1, 0x57, 0x70, 0xd8, 0x31, 0xc2, 0x2c, 0xa0, 0xd9, 0x53, + 0xad, 0x03, 0x1b, 0x22, 0xb1, 0xc6, 0x71, 0xd8, 0x41, 0xc9, 0x43, + 0x81, 0x00, 0x70, 0x98, 0x71, 0x99, 0x13, 0x82, 0x28, 0xd1, 0xc2, + 0x07, 0x00, 0x8e, 0x06, 0xbf, 0x27, 0xc2, 0x0c, 0x00, 0x4b, 0x55, + 0x1b, 0x44, 0xc7, 0xb4, 0x02, 0x06, 0xc6, 0xff, 0x06, 0x25, 0x00, + 0xd2, 0x07, 0x03, 0xf8, 0x81, 0x66, 0x3d, 0x29, 0x8e, 0x07, 0xcb, + 0x78, 0x07, 0x8e, 0x08, 0x7a, 0x90, 0xc8, 0x41, 0x92, 0x4a, 0x0c, + 0xc2, 0x4a, 0x0d, 0xc0, 0xc8, 0x41, 0xc2, 0x4a, 0x0e, 0xe8, 0x81, + 0xc0, 0xd8, 0x41, 0xd2, 0x4e, 0x0f, 0x86, 0xd2, 0xff, 0x7c, 0xfc, + 0x7c, 0xf9, 0xc2, 0x4f, 0x0c, 0xc1, 0x83, 0x71, 0xc2, 0x4f, 0x0d, + 0xc1, 0x1e, 0x71, 0xc2, 0x4f, 0x0e, 0x86, 0xf7, 0xff, 0xc8, 0x81, + 0xc2, 0x0c, 0x22, 0xe2, 0xa0, 0xff, 0xe7, 0x9c, 0x02, 0x06, 0xd7, + 0xff, 0x07, 0x6c, 0x02, 0x86, 0xd5, 0xff, 0xa8, 0x11, 0x81, 0x00, + 0x70, 0xb8, 0x21, 0x82, 0x28, 0x8e, 0x06, 0x87, 0x3a, 0x56, 0x4a, + 0xf4, 0x8e, 0x0b, 0x66, 0x16, 0x6a, 0xf3, 0x0c, 0x0c, 0xbd, 0x0a, + 0x0c, 0x0d, 0xad, 0x07, 0x81, 0xc8, 0x8e, 0x05, 0x8f, 0x51, 0xc9, + 0xff, 0xc1, 0xc7, 0x71, 0x79, 0x53, 0x06, 0xcf, 0xff, 0x20, 0x20, + 0x74, 0x8e, 0x05, 0x84, 0x0c, 0xa1, 0xc9, 0x71, 0x62, 0x22, 0x2c, + 0x71, 0x00, 0x70, 0x58, 0x43, 0x82, 0x27, 0xa0, 0x42, 0x25, 0x26, + 0x62, 0xd6, 0x04, 0x52, 0x15, 0x4e, 0x68, 0x46, 0xbd, 0x05, 0x8e, + 0x05, 0xee, 0x23, 0x4a, 0xb5, 0x76, 0x95, 0x0e, 0x90, 0xcb, 0xc0, + 0x92, 0xc9, 0x01, 0xa2, 0x0c, 0x00, 0xa2, 0x4c, 0x12, 0x90, 0x90, + 0xf4, 0xa2, 0xa0, 0x00, 0x65, 0x6c, 0xfa, 0xd2, 0xca, 0xfc, 0xe6, + 0x1d, 0x05, 0x22, 0xa0, 0xff, 0x90, 0x00, 0x00, 0xe2, 0x02, 0x01, + 0xf2, 0x02, 0x02, 0x66, 0x1e, 0x15, 0x66, 0x3f, 0x12, 0xad, 0x02, + 0xb2, 0xc4, 0x12, 0x81, 0x22, 0x71, 0xcd, 0x05, 0x82, 0x28, 0x1b, + 0xd2, 0xa0, 0xd0, 0x8e, 0x07, 0x81, 0xa2, 0x52, 0x8e, 0x06, 0x83, + 0x5b, 0x7d, 0x8e, 0x0e, 0x83, 0x5b, 0x16, 0x76, 0x04, 0x98, 0x26, + 0x26, 0x09, 0x42, 0x92, 0x44, 0x0c, 0x90, 0xa8, 0x41, 0xa2, 0x44, + 0x0d, 0xa0, 0x8e, 0x04, 0x06, 0x0e, 0xa0, 0xa8, 0x41, 0x6b, 0xd5, + 0x81, 0x00, 0x70, 0xcb, 0x94, 0x52, 0x44, 0x10, 0xa2, 0x44, 0x0f, + 0x50, 0xb8, 0x41, 0xb2, 0x44, 0x11, 0xa1, 0x57, 0x70, 0xb1, 0xca, + 0x71, 0xa2, 0x2a, 0xa0, 0xa9, 0x47, 0x39, 0x57, 0xad, 0x07, 0x99, + 0x17, 0x82, 0x28, 0xd1, 0xc2, 0x02, 0x00, 0x8e, 0x07, 0x94, 0x3c, + 0xc2, 0x02, 0x03, 0x66, 0x3c, 0x1f, 0x8e, 0x07, 0x81, 0x7e, 0x02, + 0xe0, 0x08, 0x00, 0xa2, 0x1a, 0x00, 0xa2, 0x44, 0x0c, 0x8e, 0x05, + 0x51, 0x8e, 0x07, 0x57, 0x46, 0xe9, 0xff, 0x8e, 0x0a, 0x81, 0x90, + 0x0f, 0x9c, 0x6a, 0x8e, 0x12, 0x21, 0xa0, 0xa8, 0x41, 0x06, 0xe1, + 0xff, 0xa2, 0xa0, 0xff, 0xb1, 0x1e, 0x71, 0xc1, 0x83, 0x71, 0x7c, + 0xfd, 0xd2, 0x44, 0x0c, 0xc2, 0x44, 0x0d, 0xb2, 0x44, 0x0e, 0x46, + 0xdb, 0x8e, 0x07, 0x9d, 0x20, 0x52, 0xc2, 0x10, 0xc1, 0xcb, 0x71, + 0xb1, 0x00, 0x70, 0x88, 0x0c, 0xd2, 0x2b, 0x6e, 0x8c, 0x68, 0xad, + 0x05, 0xe0, 0x0d, 0x00, 0x06, 0x01, 0x00, 0x8e, 0x05, 0x08, 0xa8, + 0x12, 0x31, 0x8e, 0x05, 0x8e, 0x29, 0x23, 0x19, 0x1c, 0x1c, 0x8e, + 0x06, 0x8a, 0x6d, 0xa8, 0x12, 0x88, 0xb8, 0x8e, 0x06, 0xb5, 0x0b, + 0x0c, 0x1b, 0x1c, 0x1c, 0x4d, 0x0a, 0x82, 0x23, 0x19, 0xa8, 0x12, + 0xe0, 0x08, 0x00, 0x42, 0x62, 0x35, 0x61, 0xcc, 0x71, 0xad, 0x02, + 0x88, 0xf6, 0x8e, 0x05, 0x94, 0x13, 0xa9, 0x01, 0x92, 0x02, 0x00, + 0x0c, 0x37, 0xec, 0x29, 0xa2, 0x02, 0x02, 0xdc, 0xda, 0x0c, 0x0b, + 0xc8, 0x22, 0xb2, 0x62, 0x35, 0x16, 0xbc, 0x0b, 0xd2, 0x02, 0x03, + 0xd2, 0xcd, 0xfd, 0x16, 0x2d, 0x0b, 0xad, 0x07, 0xb8, 0x32, 0xe0, + 0x0c, 0x00, 0x72, 0x42, 0x03, 0x1d, 0xf0, 0x20, 0xa2, 0x20, 0x40, + 0xb4, 0x20, 0xc2, 0xa0, 0x00, 0x88, 0xc6, 0x8e, 0x06, 0xbc, 0x30, + 0x04, 0xdd, 0x01, 0x3d, 0x0a, 0x82, 0x26, 0x10, 0xad, 0x02, 0x8e, + 0x05, 0xbe, 0x1e, 0xcd, 0x0a, 0x16, 0x73, 0x04, 0x26, 0x23, 0x44, + 0xd8, 0x22, 0x92, 0x02, 0x03, 0x8c, 0xbd, 0x37, 0x19, 0x09, 0xad, + 0x03, 0xb8, 0x32, 0xe0, 0x0d, 0x00, 0x32, 0x42, 0x03, 0xc8, 0x01, + 0xa2, 0x22, 0x35, 0xb2, 0xa3, 0xe8, 0xca, 0xaa, 0xa2, 0x62, 0x35, + 0xa8, 0x01, 0x25, 0xd1, 0x04, 0xbd, 0x0a, 0xd1, 0xcb, 0x71, 0xe1, + 0x00, 0x70, 0xd8, 0x0d, 0xe2, 0x2e, 0x6d, 0xbc, 0xdd, 0xad, 0x05, + 0xc2, 0xa3, 0xe8, 0x0c, 0x1d, 0xca, 0xcb, 0x0c, 0x0b, 0xe0, 0x0e, + 0x00, 0x1d, 0xf0, 0x7d, 0x0c, 0x26, 0x1c, 0x02, 0x66, 0x3c, 0xb2, + 0xe1, 0xcd, 0x71, 0xb8, 0x01, 0xe8, 0x0e, 0xd8, 0x22, 0xb7, 0xbe, + 0x27, 0xe0, 0xfb, 0xc0, 0xf9, 0x01, 0x16, 0x2d, 0xfb, 0x82, 0x02, + 0x03, 0x37, 0x18, 0xac, 0x8e, 0x0a, 0x5d, 0xc6, 0xe7, 0xff, 0xad, + 0x05, 0x1b, 0xcb, 0x0c, 0x0d, 0x8e, 0x07, 0x3c, 0x9c, 0x4d, 0x92, + 0x02, 0x03, 0xc9, 0x21, 0x77, 0x19, 0x0d, 0x3d, 0x0c, 0x8e, 0x05, + 0x81, 0x41, 0x8e, 0x05, 0x26, 0xb8, 0x01, 0xad, 0x02, 0x88, 0xf6, + 0xba, 0x34, 0x8e, 0x05, 0xbb, 0x5d, 0xa9, 0x11, 0x92, 0x02, 0x00, + 0xdc, 0xc9, 0xa2, 0x02, 0x02, 0xdc, 0x7a, 0xc8, 0x22, 0x16, 0x9c, + 0xfc, 0xb2, 0x02, 0x03, 0x26, 0x3b, 0xc3, 0x0c, 0x3a, 0x8e, 0x05, + 0x81, 0x6f, 0x0c, 0x3c, 0xc2, 0x42, 0x03, 0x1d, 0xf0, 0xbd, 0x03, + 0xcd, 0x07, 0xad, 0x02, 0x82, 0x26, 0x10, 0x4b, 0xd1, 0xe0, 0x08, + 0x00, 0xa8, 0x11, 0x98, 0x01, 0xaa, 0x99, 0x99, 0x01, 0x46, 0xcc, + 0xff, 0x36, 0x81, 0x00, 0x0c, 0x0b, 0xc2, 0xa4, 0x08, 0x41, 0x00, + 0x70, 0x32, 0x22, 0x2c, 0x82, 0x24, 0x3e, 0x4b, 0xa3, 0x8e, 0x05, + 0xbf, 0x26, 0xb2, 0xa0, 0xff, 0x0c, 0x6c, 0x92, 0xa4, 0x00, 0x82, + 0x24, 0x3e, 0x99, 0x13, 0xe0, 0x08, 0x00, 0x8e, 0x0c, 0x9b, 0x73, + 0x0c, 0x0c, 0xd2, 0xc1, 0x14, 0xcb, 0x43, 0x51, 0xcc, 0x71, 0x0c, + 0x0b, 0x91, 0xce, 0x71, 0x99, 0x21, 0xb2, 0x41, 0x14, 0x82, 0x25, + 0x1e, 0x8e, 0x05, 0xbd, 0x75, 0xc2, 0xc1, 0x14, 0x82, 0x25, 0x1f, + 0xbd, 0x0a, 0x92, 0x01, 0x14, 0xad, 0x02, 0x99, 0x23, 0x8e, 0x06, + 0xc5, 0x4e, 0x2b, 0x1c, 0x1c, 0xd8, 0x23, 0x51, 0xb5, 0x70, 0x92, + 0x01, 0x14, 0x82, 0x25, 0x18, 0xda, 0x99, 0x99, 0x23, 0x49, 0x41, + 0x92, 0x51, 0x06, 0x8e, 0x06, 0x8b, 0x3c, 0x01, 0x81, 0x1a, 0x71, + 0xc2, 0xa0, 0xd0, 0x82, 0x28, 0x22, 0x8b, 0xd1, 0x8e, 0x07, 0x81, + 0x92, 0x67, 0x82, 0x25, 0x18, 0x8e, 0x05, 0x83, 0x7e, 0x8e, 0x07, + 0xb1, 0x70, 0xcf, 0x71, 0x21, 0xd1, 0x71, 0x31, 0xcc, 0x71, 0x91, + 0x22, 0x71, 0x81, 0xd0, 0x71, 0x82, 0x69, 0x2b, 0x22, 0x63, 0x11, + 0xa9, 0xf9, 0x1d, 0xf0, 0x36, 0xe1, 0x00, 0x50, 0x50, 0xf4, 0xad, + 0x02, 0x79, 0xa1, 0x69, 0x71, 0x81, 0x16, 0x70, 0x30, 0xb0, 0x74, + 0x82, 0x28, 0x3b, 0xb2, 0x41, 0x34, 0xe0, 0x08, 0x00, 0x19, 0x11, + 0x0c, 0x09, 0x29, 0x51, 0x59, 0x61, 0x3d, 0x0a, 0x0c, 0x0c, 0xc9, + 0x01, 0xc9, 0x21, 0x8b, 0xa1, 0xa9, 0x31, 0x16, 0x13, 0x09, 0x72, + 0xc3, 0x10, 0x8b, 0x63, 0x51, 0x17, 0x70, 0x21, 0x9a, 0x71, 0xc9, + 0x91, 0xb2, 0x21, 0x1c, 0x99, 0x81, 0x0b, 0xbb, 0x56, 0xcb, 0x07, + 0x28, 0x04, 0xbc, 0xc2, 0xc8, 0x52, 0xc9, 0x04, 0xcc, 0x0c, 0x49, + 0x14, 0xc2, 0xc1, 0x34, 0x88, 0x05, 0xad, 0x02, 0x88, 0x68, 0xb2, + 0xc1, 0x10, 0xe0, 0x08, 0x00, 0xcd, 0x07, 0xbd, 0x06, 0xad, 0x03, + 0x81, 0x9a, 0x71, 0xd2, 0x11, 0x08, 0x88, 0x08, 0x0c, 0x0f, 0x88, + 0x28, 0x0c, 0x8e, 0x05, 0xfc, 0x6d, 0xc2, 0x14, 0xa8, 0x11, 0x0c, + 0x0b, 0xb9, 0x52, 0x29, 0x0a, 0x99, 0x11, 0x46, 0xef, 0xff, 0x98, + 0x01, 0x0c, 0x0c, 0x8c, 0xa9, 0xf8, 0x14, 0xe8, 0x11, 0x99, 0x0f, + 0xe9, 0x14, 0x19, 0x11, 0xc9, 0x01, 0x98, 0x21, 0xac, 0x19, 0xa8, + 0x73, 0x82, 0xc3, 0x1c, 0x8c, 0xea, 0x0c, 0x09, 0xc8, 0x31, 0xb8, + 0x83, 0xa9, 0x0c, 0xb9, 0x31, 0x99, 0x73, 0x98, 0x21, 0x89, 0x83, + 0x8c, 0x89, 0xe8, 0x83, 0xd8, 0x31, 0x99, 0x0e, 0xd9, 0x83, 0x1d, + 0xf0, 0x1d, 0xf0, 0xd8, 0x61, 0xad, 0x03, 0xbd, 0x06, 0xcd, 0x07, + 0xe8, 0x71, 0x88, 0x02, 0x58, 0xa1, 0x88, 0x28, 0xfd, 0x8e, 0x05, + 0x88, 0x36, 0x1a, 0x98, 0x71, 0x50, 0x5a, 0x93, 0x90, 0x9a, 0x93, + 0x50, 0x99, 0x20, 0xcc, 0xc9, 0x92, 0x03, 0x24, 0x8e, 0x06, 0xfc, + 0x21, 0x43, 0x24, 0x86, 0x01, 0x00, 0x0c, 0x09, 0x0c, 0x0a, 0xa2, + 0x43, 0x24, 0xb6, 0x59, 0x5d, 0x0c, 0x0c, 0xb2, 0x03, 0x19, 0xc2, + 0x43, 0x24, 0x16, 0x2b, 0x05, 0xd1, 0xd2, 0x71, 0xd2, 0x0d, 0x00, + 0x16, 0x9d, 0x04, 0x58, 0x04, 0xac, 0x95, 0x81, 0x17, 0x70, 0xad, + 0x05, 0x88, 0x08, 0xb2, 0xc1, 0x10, 0x88, 0x68, 0xc2, 0xc1, 0x12, + 0x8e, 0x09, 0x81, 0x48, 0xd2, 0x11, 0x08, 0x88, 0x02, 0x8e, 0x09, + 0x81, 0x45, 0x58, 0x55, 0x56, 0x45, 0xfd, 0xe2, 0x01, 0x34, 0xc1, + 0xa3, 0x70, 0xd2, 0xa0, 0x00, 0xf2, 0x13, 0x00, 0x81, 0xb2, 0x70, + 0xb2, 0x21, 0x05, 0x82, 0x28, 0x04, 0xa2, 0x2b, 0x8c, 0xe0, 0x08, + 0x00, 0x21, 0x17, 0x70, 0x68, 0x91, 0x78, 0x81, 0x58, 0x04, 0x16, + 0xe5, 0xf1, 0xad, 0x05, 0x88, 0x02, 0x8e, 0x0b, 0x4f, 0x8e, 0x06, + 0x81, 0xd8, 0x2d, 0x03, 0x88, 0x08, 0xb2, 0x11, 0x08, 0x8e, 0x05, + 0x89, 0x65, 0x07, 0x6a, 0x39, 0x0c, 0x0b, 0xd2, 0x13, 0x00, 0xc2, + 0x11, 0x08, 0x0c, 0x1a, 0x8e, 0x08, 0x81, 0xd8, 0x2d, 0x0a, 0xe1, + 0x8e, 0x05, 0x89, 0x28, 0xe8, 0x0e, 0xc0, 0x20, 0x00, 0xa5, 0xa7, + 0x04, 0x6d, 0x0b, 0x7d, 0x0a, 0x98, 0x23, 0xa8, 0x33, 0x70, 0x99, + 0x10, 0xb0, 0xaa, 0x10, 0x8e, 0x05, 0xf2, 0x07, 0x90, 0x9b, 0x93, + 0xa0, 0x99, 0x20, 0xb8, 0x04, 0xe2, 0xc5, 0x14, 0xb8, 0x5b, 0xb9, + 0x04, 0xcc, 0x0b, 0x49, 0x14, 0x66, 0x19, 0x0c, 0xc8, 0x11, 0x0c, + 0x0d, 0xd9, 0x55, 0x59, 0x0c, 0xe9, 0x11, 0x06, 0xe0, 0xff, 0x0c, + 0x8b, 0x98, 0x31, 0xf8, 0x43, 0x0c, 0x0a, 0x7c, 0xfd, 0xc8, 0x53, + 0xd0, 0xd6, 0x30, 0xd0, 0xcc, 0x10, 0x0c, 0x1d, 0xa9, 0x55, 0x59, + 0x09, 0xe9, 0x31, 0x82, 0x03, 0x1a, 0x7c, 0xfe, 0xe0, 0xe7, 0x30, + 0xe0, 0xef, 0x10, 0x1b, 0x88, 0x82, 0x43, 0x1a, 0x0c, 0x1f, 0x81, + 0x14, 0x70, 0x58, 0x45, 0xe9, 0x43, 0xed, 0x05, 0xa2, 0x25, 0x26, + 0xc9, 0x53, 0x92, 0x0a, 0x01, 0x0c, 0x1c, 0xb0, 0x99, 0x20, 0x92, + 0x4a, 0x01, 0xad, 0x05, 0x82, 0x28, 0xb8, 0xb2, 0x15, 0x4b, 0x8e, + 0x06, 0xbc, 0x50, 0x0c, 0xc2, 0x45, 0x05, 0xb2, 0x45, 0x04, 0x8e, + 0x04, 0x08, 0x06, 0x8e, 0x04, 0x05, 0x07, 0x46, 0xc6, 0x8e, 0x07, + 0xb1, 0x3c, 0x0c, 0x2b, 0x0c, 0x3c, 0x31, 0x20, 0x70, 0x41, 0xb5, + 0x70, 0x38, 0x03, 0x82, 0x24, 0x18, 0x8e, 0x05, 0xd8, 0x62, 0x8e, + 0x05, 0x93, 0x71, 0xbb, 0x8e, 0x06, 0x96, 0x4f, 0x13, 0x70, 0x5d, + 0x0a, 0x8e, 0x07, 0xb0, 0x3f, 0x0c, 0x1b, 0x0c, 0x3c, 0x6d, 0x0a, + 0x8e, 0x08, 0x25, 0x0c, 0x02, 0xcc, 0x35, 0x0c, 0x19, 0x60, 0x29, + 0x93, 0x8e, 0x08, 0x81, 0xd3, 0x50, 0x91, 0xd3, 0x71, 0x71, 0x18, + 0x70, 0x98, 0x09, 0x88, 0x07, 0x98, 0x09, 0x88, 0x48, 0x99, 0x01, + 0xe0, 0x08, 0x00, 0x9c, 0xba, 0xbd, 0x03, 0xcd, 0x04, 0x88, 0x07, + 0xdd, 0x05, 0x88, 0x78, 0xed, 0x06, 0xe0, 0x08, 0x00, 0x88, 0x07, + 0x88, 0x48, 0x8e, 0x05, 0x3e, 0x56, 0x4a, 0xfe, 0x1d, 0xf0, 0xb8, + 0x01, 0x0c, 0x02, 0xb2, 0x0b, 0x0c, 0xc2, 0xa3, 0x88, 0xac, 0xcb, + 0xa8, 0x01, 0xc0, 0xc2, 0xc1, 0xa2, 0x2a, 0x1a, 0xca, 0xaa, 0xc8, + 0x2a, 0x66, 0x3c, 0x13, 0xed, 0x06, 0xdd, 0x05, 0x88, 0x07, 0xcd, + 0x04, 0x88, 0x78, 0x8e, 0x05, 0x87, 0x73, 0xb8, 0x01, 0x8e, 0x06, + 0x28, 0x8e, 0x06, 0x81, 0xa8, 0x1a, 0x32, 0xd1, 0x8e, 0x07, 0x81, + 0x98, 0x00, 0xd7, 0x71, 0x41, 0xd6, 0x71, 0x91, 0xd5, 0x71, 0xb1, + 0xd4, 0x71, 0x31, 0x17, 0x70, 0x81, 0x18, 0x70, 0x38, 0x03, 0x88, + 0x08, 0xa8, 0x33, 0xa9, 0x0b, 0x99, 0x33, 0x49, 0x18, 0x29, 0xd3, + 0x8e, 0x08, 0x81, 0x8d, 0x28, 0x0c, 0x2a, 0x29, 0x11, 0x41, 0x00, + 0x70, 0x1b, 0x33, 0x82, 0x24, 0xd0, 0x8e, 0x05, 0x4f, 0x39, 0x01, + 0x7d, 0x0a, 0x16, 0x4a, 0x06, 0x0c, 0x04, 0x0c, 0x03, 0x5c, 0x0a, + 0x68, 0x47, 0x21, 0xd8, 0x71, 0x5b, 0x56, 0x98, 0x02, 0x92, 0x46, + 0x00, 0xe8, 0x12, 0x3a, 0xee, 0xb8, 0x0e, 0xf8, 0x02, 0xac, 0xfb, + 0xf7, 0xb4, 0x33, 0xcb, 0xbe, 0xad, 0x05, 0x0c, 0x6c, 0x81, 0x00, + 0x70, 0x98, 0x1e, 0x92, 0x46, 0x01, 0x90, 0x98, 0x41, 0x92, 0x46, + 0x02, 0x82, 0x28, 0x3f, 0x8e, 0x05, 0x09, 0x03, 0x8e, 0x05, 0x06, + 0x04, 0xe0, 0x08, 0x00, 0x5c, 0x0a, 0x1b, 0x44, 0xab, 0x66, 0xab, + 0x55, 0x32, 0xc3, 0x14, 0xa7, 0x93, 0xbe, 0xad, 0x07, 0xc8, 0x11, + 0x81, 0x00, 0x70, 0xd8, 0x01, 0x82, 0x28, 0xd1, 0xb1, 0x3b, 0x8e, + 0x09, 0xdd, 0x30, 0x61, 0x00, 0x61, 0xd8, 0x71, 0x29, 0x01, 0x88, + 0x26, 0xb8, 0x06, 0x16, 0xd8, 0x04, 0x16, 0xab, 0x04, 0x51, 0xb0, + 0x70, 0xc0, 0x20, 0x00, 0x52, 0x25, 0x95, 0x0c, 0x04, 0x5c, 0x07, + 0x21, 0x1e, 0x71, 0xa8, 0x16, 0x4a, 0xaa, 0x88, 0x0a, 0xb8, 0x2a, + 0xac, 0x98, 0x57, 0xbb, 0x07, 0xd8, 0x26, 0xb0, 0xc5, 0xc0, 0xd7, + 0xbc, 0x0c, 0xb7, 0xb5, 0x1c, 0xf8, 0x26, 0xb0, 0xe5, 0xc0, 0x2a, + 0xee, 0xf7, 0x3e, 0x12, 0x8e, 0x09, 0xce, 0x58, 0x4c, 0x8e, 0x05, + 0xdc, 0x04, 0x0b, 0x99, 0x99, 0x06, 0x42, 0xc4, 0x14, 0x77, 0x94, + 0xc4, 0xac, 0x93, 0xb8, 0x36, 0xa8, 0x06, 0xb7, 0x3a, 0x23, 0xa0, + 0xba, 0xa0, 0xa8, 0x01, 0xe0, 0xbb, 0x11, 0xb0, 0xb0, 0xf4, 0xa2, + 0x0a, 0x00, 0x65, 0xf0, 0xff, 0xa8, 0x16, 0x8e, 0x08, 0x35, 0x5c, + 0x0c, 0x8e, 0x06, 0x81, 0x90, 0x4b, 0x06, 0x8e, 0x09, 0xd8, 0x44, + 0xad, 0x02, 0x0c, 0x0b, 0xa5, 0xf6, 0xff, 0x0c, 0x02, 0x71, 0x00, + 0x70, 0x51, 0xd8, 0x71, 0x61, 0xb0, 0x70, 0xb8, 0x15, 0x2a, 0xbb, + 0x98, 0x0b, 0xbc, 0x39, 0x0c, 0x6c, 0xcb, 0xab, 0x82, 0x27, 0x41, + 0x8e, 0x05, 0x8c, 0x64, 0xdc, 0xaa, 0x8e, 0x05, 0x84, 0x0c, 0x91, + 0xe0, 0x08, 0x00, 0xc8, 0x15, 0x3a, 0xba, 0x2a, 0xac, 0xb9, 0x1a, + 0xc0, 0x20, 0x00, 0x92, 0x26, 0x95, 0x99, 0x2a, 0x46, 0x0f, 0x00, + 0x5c, 0x0a, 0x22, 0xc2, 0x14, 0xa7, 0x92, 0xc5, 0x86, 0x0c, 0x00, + 0x8e, 0x06, 0x35, 0x8e, 0x06, 0x81, 0xd2, 0x20, 0x0c, 0x19, 0xa8, + 0x15, 0x81, 0x14, 0x70, 0x2a, 0xaa, 0x82, 0x28, 0x91, 0x99, 0x0a, + 0xe0, 0x08, 0x00, 0xb8, 0x15, 0x3a, 0xca, 0x2a, 0xbb, 0xc9, 0x1b, + 0xc0, 0x20, 0x00, 0xa2, 0x26, 0x95, 0xa9, 0x2b, 0x98, 0x05, 0x1b, + 0x99, 0x99, 0x05, 0x8e, 0x0a, 0xa9, 0x78, 0xad, 0x04, 0x68, 0x43, + 0x41, 0xd8, 0x71, 0x72, 0x26, 0x26, 0x88, 0x44, 0x62, 0x16, 0x4e, + 0x32, 0xc7, 0x18, 0x7a, 0x66, 0x9c, 0x78, 0x88, 0x54, 0x9c, 0x38, + 0x26, 0xda, 0x0a, 0x5c, 0x09, 0x97, 0x1a, 0x02, 0x66, 0xea, 0x05, + 0x32, 0xc7, 0x24, 0x67, 0x33, 0x14, 0x8e, 0x04, 0x35, 0xa8, 0x64, + 0x16, 0x9a, 0x05, 0xbd, 0x05, 0xad, 0x02, 0xab, 0xc7, 0x65, 0xf3, + 0xff, 0x46, 0x13, 0x00, 0xa2, 0xa0, 0xdd, 0xd2, 0xa0, 0x7f, 0xc2, + 0x03, 0x00, 0xe2, 0x03, 0x01, 0xa7, 0x9c, 0x4b, 0xb8, 0x74, 0xc8, + 0x44, 0xb0, 0xfe, 0xc0, 0xf2, 0xcf, 0xfc, 0xf0, 0xf0, 0x74, 0xc7, + 0x3f, 0x3b, 0x82, 0x03, 0x02, 0x92, 0x03, 0x03, 0xfc, 0x28, 0x66, + 0x39, 0x30, 0xf2, 0x03, 0x04, 0xd7, 0x9f, 0x2a, 0xba, 0xa3, 0x81, + 0x00, 0x70, 0x6b, 0xaa, 0x82, 0x28, 0x41, 0xb8, 0x54, 0xe0, 0x08, + 0x00, 0xcc, 0xfa, 0xb8, 0x64, 0x8c, 0x7b, 0x8e, 0x07, 0x51, 0xee, + 0xff, 0x8e, 0x06, 0x81, 0xd5, 0x65, 0xdd, 0xe2, 0x03, 0x01, 0xd2, + 0xa0, 0x7f, 0x3a, 0x3e, 0x2b, 0x33, 0x67, 0x33, 0xa3, 0x46, 0xe1, + 0xff, 0x36, 0x41, 0x00, 0x56, 0xb3, 0x00, 0x81, 0x3c, 0x70, 0x82, + 0x28, 0x08, 0x20, 0xa2, 0x20, 0xe0, 0x08, 0x00, 0x51, 0xd8, 0x71, + 0x98, 0x65, 0x66, 0x19, 0x37, 0xad, 0x02, 0x0c, 0x1b, 0xe5, 0xe1, + 0xff, 0x8e, 0x05, 0xc6, 0x13, 0x8e, 0x05, 0xf6, 0x23, 0x41, 0xd9, + 0x71, 0x31, 0x00, 0x70, 0xc8, 0x85, 0x0c, 0x0d, 0xe5, 0x1c, 0x04, + 0x2d, 0x0a, 0x82, 0x23, 0x8e, 0x0a, 0xca, 0x1f, 0x0c, 0x0d, 0xc8, + 0x95, 0x82, 0x23, 0x6d, 0x2a, 0xcc, 0x8e, 0x09, 0x81, 0x87, 0x54, + 0xd8, 0x71, 0x88, 0x64, 0x2d, 0x03, 0x66, 0x18, 0x39, 0xad, 0x03, + 0x81, 0x3a, 0x71, 0x0c, 0x1b, 0x82, 0x28, 0x19, 0x8e, 0x05, 0xce, + 0x27, 0x31, 0x3c, 0x70, 0x82, 0x23, 0x8e, 0x06, 0x81, 0xb5, 0x5d, + 0xad, 0x02, 0x88, 0x73, 0xb2, 0x14, 0x14, 0xe0, 0x08, 0x00, 0xed, + 0x02, 0xad, 0x02, 0xd1, 0xda, 0x71, 0xc1, 0x3e, 0x70, 0xb8, 0xb4, + 0x88, 0x33, 0xc0, 0xbb, 0x20, 0x8e, 0x05, 0x9e, 0x5d, 0x8e, 0x09, + 0xac, 0x40, 0xd8, 0x71, 0x88, 0x62, 0x56, 0x48, 0x04, 0x0c, 0x04, + 0x51, 0xdb, 0x71, 0x92, 0x03, 0x00, 0xa8, 0x52, 0x16, 0x39, 0x04, + 0xb2, 0x03, 0x01, 0xb9, 0x72, 0xc2, 0x03, 0x02, 0xc9, 0x42, 0x8c, + 0x9a, 0x88, 0x05, 0x88, 0x48, 0xe0, 0x08, 0x00, 0x49, 0x52, 0xc8, + 0x42, 0x88, 0x05, 0x88, 0x18, 0xad, 0x0c, 0xe0, 0x08, 0x00, 0xa9, + 0x52, 0xd1, 0x00, 0x70, 0xc8, 0x42, 0xcc, 0xfa, 0xbd, 0x0c, 0x82, + 0x2d, 0x5e, 0xa1, 0xdc, 0x71, 0x8e, 0x04, 0x21, 0x42, 0x1d, 0xf0, + 0x1d, 0xf0, 0x82, 0x2d, 0x3f, 0x3b, 0xb3, 0x8e, 0x05, 0x5f, 0x49, + 0x72, 0x49, 0x42, 0x16, 0xca, 0xfe, 0x8e, 0x09, 0x3f, 0x8e, 0x07, + 0x85, 0x54, 0x41, 0xd8, 0x71, 0xa2, 0x13, 0x00, 0x88, 0x64, 0x52, + 0x22, 0x18, 0xa7, 0x18, 0x5b, 0x0c, 0x0b, 0x3c, 0x2d, 0xf2, 0xa3, + 0xe8, 0xa9, 0x64, 0x98, 0x23, 0xa2, 0x13, 0x01, 0xa2, 0x54, 0x14, + 0x99, 0x94, 0x88, 0x33, 0xad, 0x02, 0x89, 0x84, 0x78, 0x43, 0x79, + 0x34, 0x71, 0x3c, 0x70, 0xe8, 0x53, 0x82, 0x27, 0x11, 0xf0, 0xee, + 0x82, 0xe9, 0x24, 0xc8, 0x13, 0x0c, 0x0e, 0xd0, 0xcc, 0x73, 0x8e, + 0x05, 0x91, 0x09, 0x61, 0x8e, 0x05, 0x82, 0x2c, 0xb8, 0x64, 0xa1, + 0xdb, 0x71, 0x16, 0x7b, 0x0e, 0x88, 0x0a, 0x88, 0x18, 0x5c, 0x0a, + 0xe0, 0x08, 0x00, 0xa9, 0x14, 0xcc, 0xba, 0x82, 0x23, 0x5e, 0xa1, + 0xdd, 0x8e, 0x06, 0xad, 0x26, 0x1d, 0xf0, 0x0c, 0x0b, 0x82, 0x23, + 0x8e, 0x07, 0x85, 0x47, 0x0b, 0x0c, 0x0c, 0xa2, 0x02, 0x01, 0x81, + 0x34, 0x70, 0xa2, 0x44, 0x30, 0xad, 0x02, 0x92, 0x02, 0x02, 0x88, + 0x88, 0x92, 0x44, 0x31, 0x8e, 0x06, 0x81, 0xac, 0x26, 0x27, 0x23, + 0x8e, 0x06, 0x82, 0x29, 0xc2, 0x22, 0x16, 0xa0, 0xba, 0x90, 0xc0, + 0xbb, 0xa0, 0xb9, 0x11, 0xdc, 0xa5, 0x81, 0xde, 0x8e, 0x05, 0x9b, + 0x00, 0x14, 0x8e, 0x05, 0x94, 0x4e, 0x5d, 0x0a, 0xdc, 0x0a, 0x8e, + 0x04, 0x5a, 0xdf, 0x8e, 0x06, 0x5a, 0xb9, 0x45, 0x59, 0x21, 0x46, + 0x07, 0x00, 0x4b, 0xb2, 0x0c, 0x6c, 0x82, 0x23, 0x3f, 0x92, 0xa4, + 0x30, 0xa2, 0xa0, 0x64, 0xa2, 0x55, 0x14, 0x92, 0x55, 0x17, 0x6b, + 0xa5, 0xe0, 0x08, 0x00, 0x59, 0x21, 0xb8, 0x11, 0xb9, 0x45, 0xad, + 0x02, 0x0c, 0x0b, 0xc1, 0xe0, 0x71, 0x98, 0x21, 0x82, 0x27, 0x2e, + 0x92, 0x62, 0x18, 0xe0, 0x08, 0x00, 0xcd, 0x01, 0x82, 0x27, 0x2d, + 0x0c, 0xfa, 0xb2, 0xa0, 0x64, 0x98, 0xb4, 0xb9, 0x01, 0x0c, 0x7b, + 0xa0, 0x99, 0x20, 0x99, 0xb4, 0x8e, 0x06, 0x81, 0xa6, 0x22, 0x02, + 0xad, 0x06, 0x82, 0x23, 0x70, 0xb1, 0xe1, 0x71, 0x8e, 0x05, 0xd1, + 0x4e, 0x8e, 0x06, 0x9e, 0x38, 0xad, 0x06, 0x8e, 0x05, 0xf9, 0x69, + 0x8e, 0x09, 0x81, 0xa5, 0x5f, 0x8e, 0x06, 0x81, 0xa5, 0x6f, 0x0c, + 0x0d, 0x82, 0x27, 0x11, 0x0c, 0x0e, 0x8e, 0x0b, 0x28, 0x82, 0x23, + 0x6f, 0x8e, 0x06, 0x30, 0x02, 0x81, 0x34, 0x70, 0xb2, 0x04, 0x30, + 0x88, 0x88, 0xc2, 0x04, 0x31, 0xe0, 0x08, 0x00, 0xa8, 0x14, 0x0c, + 0x02, 0x8c, 0xaa, 0x81, 0xdb, 0x71, 0x88, 0x08, 0x8e, 0x05, 0x83, + 0x03, 0x29, 0x14, 0x29, 0x04, 0x8e, 0x07, 0xa3, 0x58, 0x06, 0xbd, + 0x05, 0x30, 0x20, 0xf4, 0xa1, 0x20, 0x70, 0x81, 0x34, 0x70, 0x40, + 0x90, 0x34, 0x88, 0x78, 0xa0, 0x99, 0xa0, 0x48, 0x09, 0x8e, 0x05, + 0x81, 0x0b, 0xb1, 0xe2, 0x71, 0xc1, 0xe3, 0x71, 0xb7, 0x12, 0x18, + 0xc7, 0x12, 0x0c, 0x8e, 0x0b, 0x81, 0xcd, 0x6e, 0x1d, 0xf0, 0xad, + 0x04, 0xbd, 0x05, 0xe5, 0xe3, 0x8e, 0x06, 0x81, 0xd3, 0x0c, 0x04, + 0x65, 0xdc, 0x8e, 0x08, 0x81, 0xd3, 0x0c, 0x8e, 0x07, 0xda, 0x48, + 0xe4, 0x8e, 0x0c, 0x81, 0x8c, 0x34, 0x81, 0x38, 0x70, 0x72, 0x24, + 0x04, 0x82, 0x28, 0x00, 0x72, 0x27, 0x26, 0x16, 0xc8, 0x02, 0xad, + 0x02, 0xbd, 0x04, 0x4c, 0x0c, 0xdd, 0x05, 0xed, 0x06, 0x65, 0xc4, + 0xff, 0x9c, 0x0a, 0xad, 0x02, 0x81, 0x3a, 0x71, 0xab, 0xb7, 0x82, + 0x28, 0x13, 0x8e, 0x05, 0x9d, 0x76, 0xc6, 0x01, 0x00, 0x92, 0x22, + 0x4d, 0x1b, 0x99, 0x92, 0x62, 0x4d, 0x8e, 0x0b, 0xce, 0x63, 0xe5, + 0x8e, 0x12, 0xb1, 0x28, 0xa1, 0x00, 0x40, 0x94, 0x20, 0x81, 0x38, + 0x70, 0x42, 0xa0, 0x50, 0x82, 0x28, 0x00, 0x99, 0x01, 0x9c, 0x18, + 0x47, 0x95, 0x0f, 0x8e, 0x05, 0xac, 0x20, 0x05, 0xdd, 0x06, 0xed, + 0x07, 0xa5, 0xbe, 0xff, 0x16, 0xba, 0x19, 0x91, 0x99, 0x71, 0x98, + 0x09, 0x9c, 0xc9, 0xa2, 0x02, 0x01, 0xdc, 0x7a, 0x47, 0x15, 0x02, + 0x66, 0xe5, 0x12, 0x20, 0xa2, 0x20, 0x30, 0xb3, 0x20, 0x50, 0xc5, + 0x20, 0x8e, 0x04, 0x25, 0x65, 0xbc, 0xff, 0x16, 0x6a, 0x17, 0xb1, + 0x24, 0x70, 0x0c, 0x1c, 0x0c, 0xad, 0xd9, 0x21, 0xc9, 0x51, 0x26, + 0xd5, 0x33, 0x40, 0xe5, 0xc0, 0x16, 0x7e, 0x10, 0xf2, 0xc5, 0x80, + 0x16, 0x6f, 0x10, 0x0c, 0x02, 0x06, 0x07, 0x00, 0x42, 0xa0, 0x00, + 0x22, 0xa0, 0x00, 0x22, 0x61, 0x04, 0x0c, 0x02, 0x8c, 0xe4, 0xcd, + 0x04, 0xbd, 0x03, 0xa8, 0x11, 0xd8, 0x41, 0xa5, 0x13, 0xff, 0x88, + 0x21, 0xa0, 0x28, 0x93, 0xad, 0x02, 0x2d, 0x0a, 0x1d, 0xf0, 0x9d, + 0x0b, 0x92, 0x09, 0x00, 0x29, 0x11, 0x16, 0x29, 0xfd, 0x0c, 0x02, + 0x0c, 0x04, 0x8e, 0x05, 0xa7, 0x6e, 0x41, 0xbd, 0x03, 0x4c, 0x0c, + 0xa1, 0x20, 0x70, 0x81, 0x22, 0x71, 0x98, 0x43, 0x99, 0x31, 0x82, + 0x28, 0x16, 0xa0, 0xa5, 0xa0, 0xa8, 0x0a, 0xa9, 0x61, 0xe0, 0x08, + 0x00, 0x9d, 0x0a, 0xe8, 0x61, 0xf8, 0x31, 0xe2, 0x0e, 0x02, 0xf2, + 0x2f, 0x26, 0x26, 0x2e, 0x4b, 0x26, 0x4e, 0x26, 0x26, 0x3e, 0x23, + 0xf9, 0x71, 0xa9, 0x81, 0x26, 0x5e, 0x1c, 0xa8, 0x61, 0xb8, 0x01, + 0xcd, 0x03, 0x81, 0x48, 0x70, 0xdd, 0x06, 0x88, 0x48, 0xed, 0x07, + 0x8e, 0x05, 0x81, 0x71, 0xe8, 0x61, 0x98, 0x81, 0xe2, 0x0e, 0x02, + 0xf8, 0x71, 0x26, 0x2e, 0x1e, 0x26, 0x4e, 0x1b, 0x26, 0x3e, 0x18, + 0x26, 0x5e, 0x15, 0x17, 0x69, 0x34, 0x59, 0x41, 0x88, 0x51, 0x00, + 0x15, 0x40, 0x00, 0x88, 0xa1, 0x80, 0x44, 0x20, 0x40, 0x40, 0x74, + 0x46, 0x08, 0x00, 0x07, 0xe9, 0x0e, 0x27, 0xe9, 0x0b, 0x17, 0xe9, + 0x08, 0x92, 0x0f, 0x10, 0xa2, 0xa0, 0xff, 0xa7, 0x99, 0x23, 0x8e, + 0x10, 0x25, 0xa1, 0x24, 0x70, 0x1b, 0x55, 0xa2, 0x0a, 0x00, 0x50, + 0x50, 0x74, 0xa7, 0xb5, 0x02, 0x86, 0xd5, 0xff, 0xc6, 0xc9, 0xff, + 0x66, 0x4e, 0xe8, 0x8e, 0x14, 0x78, 0x46, 0xf4, 0xff, 0x0c, 0x14, + 0x46, 0x00, 0x00, 0x0c, 0x04, 0x91, 0x24, 0x70, 0x92, 0x09, 0x00, + 0x0c, 0x05, 0xac, 0x49, 0xdd, 0x07, 0xbd, 0x03, 0xcd, 0x06, 0xed, + 0x04, 0xa1, 0x20, 0x70, 0x81, 0x48, 0x70, 0xa0, 0xa5, 0xa0, 0x88, + 0x38, 0x8e, 0x05, 0xa7, 0x76, 0xb1, 0x8e, 0x04, 0x57, 0xb2, 0x0b, + 0x8e, 0x04, 0x57, 0xb7, 0x35, 0xd9, 0xbd, 0x03, 0xad, 0x02, 0x60, + 0xd0, 0x74, 0x0c, 0x29, 0x81, 0x3c, 0x70, 0xc8, 0x51, 0x82, 0x28, + 0x35, 0x40, 0xc9, 0x93, 0xe0, 0x08, 0x00, 0xb2, 0xca, 0xe4, 0x28, + 0x21, 0x7c, 0xfa, 0xb0, 0x2a, 0x93, 0x06, 0xb1, 0xff, 0xc2, 0x22, + 0x4d, 0x7c, 0xfa, 0x1b, 0xcc, 0xc2, 0x62, 0x4d, 0x46, 0xae, 0x8e, + 0x05, 0xe1, 0x30, 0x61, 0x10, 0x70, 0x98, 0x06, 0x42, 0x22, 0x24, + 0x58, 0x89, 0x8c, 0xc3, 0x81, 0xe5, 0x71, 0xad, 0x02, 0x8e, 0x07, + 0x81, 0xa2, 0x68, 0x1d, 0xf0, 0xa2, 0x22, 0x19, 0x7c, 0xfe, 0xa2, + 0xda, 0x02, 0xa2, 0x1a, 0x1c, 0x0c, 0x1d, 0x67, 0x6a, 0x1e, 0x82, + 0x09, 0x07, 0x0b, 0x88, 0x82, 0x49, 0x07, 0xc8, 0x06, 0xf2, 0x02, + 0x00, 0xb2, 0x0c, 0x0f, 0x00, 0x1f, 0x40, 0x00, 0xdd, 0xa1, 0xe0, + 0xdd, 0x30, 0xd0, 0xbb, 0x10, 0xb2, 0x4c, 0x0f, 0x71, 0x48, 0x70, + 0x8e, 0x07, 0x81, 0xb0, 0x67, 0x31, 0x3a, 0x71, 0x88, 0x43, 0x8e, + 0x06, 0x81, 0xb6, 0x3b, 0x02, 0x01, 0x26, 0x19, 0x0b, 0xad, 0x02, + 0x0c, 0x0b, 0x82, 0x23, 0x19, 0x8e, 0x07, 0x81, 0xbe, 0x70, 0xa2, + 0x54, 0x09, 0xa2, 0x54, 0x07, 0xa2, 0x54, 0x05, 0xa2, 0x54, 0x06, + 0x92, 0x02, 0x02, 0x26, 0x19, 0x10, 0xa2, 0x22, 0x11, 0x27, 0xea, + 0x0a, 0x92, 0x02, 0x01, 0x8c, 0x49, 0x66, 0x19, 0x0f, 0x17, 0xfa, + 0x0c, 0x81, 0xb5, 0x8e, 0x05, 0xae, 0x11, 0x1e, 0x8e, 0x05, 0xcc, + 0x46, 0x8e, 0x06, 0x3e, 0x10, 0x0c, 0x2c, 0x8e, 0x05, 0xb6, 0x11, + 0x19, 0x92, 0xd9, 0x02, 0x92, 0x19, 0x1c, 0x67, 0x69, 0x19, 0x88, + 0xd7, 0x8e, 0x06, 0x81, 0xab, 0x6a, 0x06, 0x92, 0x09, 0x07, 0xcc, + 0xa9, 0xa8, 0x05, 0x26, 0x0a, 0x06, 0x88, 0xc7, 0x8e, 0x0a, 0x81, + 0x9e, 0x5c, 0x92, 0x22, 0x19, 0x9c, 0xa9, 0x88, 0x29, 0x66, 0x38, + 0x16, 0x8e, 0x05, 0x8b, 0x7b, 0x8e, 0x06, 0x81, 0xb8, 0x24, 0xcc, + 0x8a, 0x8e, 0x05, 0xdb, 0x33, 0x8e, 0x06, 0x92, 0x16, 0x02, 0x1d, + 0xf0, 0x36, 0xa1, 0x00, 0x81, 0x0f, 0x70, 0x82, 0x08, 0x00, 0x52, + 0x22, 0x24, 0x9c, 0x18, 0x92, 0x02, 0x02, 0xa1, 0x76, 0x71, 0x66, + 0x49, 0x09, 0xb2, 0xc4, 0xfe, 0x56, 0x0b, 0x11, 0x0c, 0x29, 0x99, + 0x0a, 0x92, 0x02, 0x03, 0x26, 0x39, 0x05, 0xc2, 0xc9, 0xfc, 0x56, + 0xec, 0x0f, 0x8e, 0x06, 0x81, 0xac, 0x4d, 0x2b, 0x82, 0x28, 0x18, + 0x8e, 0x05, 0x81, 0x0c, 0x61, 0x48, 0x70, 0x92, 0x02, 0x01, 0x71, + 0x3a, 0x71, 0x66, 0x19, 0x62, 0x26, 0x24, 0x5f, 0xad, 0x02, 0xbd, + 0x03, 0x82, 0x27, 0x1d, 0xcd, 0x01, 0xe0, 0x08, 0x00, 0x91, 0x47, + 0x70, 0x92, 0x09, 0x00, 0xa8, 0x31, 0x9c, 0x89, 0xd8, 0x21, 0xc8, + 0x41, 0xb8, 0x51, 0x90, 0xaa, 0x82, 0xa9, 0x31, 0x90, 0xbb, 0x82, + 0x90, 0xcc, 0x82, 0x90, 0xdd, 0x82, 0xd9, 0x21, 0xc9, 0x41, 0xb9, + 0x51, 0xbd, 0x01, 0x81, 0x72, 0x71, 0xa2, 0x65, 0x11, 0x8e, 0x07, + 0x81, 0xd9, 0x6b, 0xad, 0x02, 0x88, 0xb6, 0x8e, 0x05, 0x89, 0x67, + 0x8e, 0x07, 0x81, 0xb8, 0x66, 0x92, 0x02, 0x02, 0x66, 0x39, 0x0a, + 0x81, 0x22, 0x71, 0x8e, 0x08, 0x81, 0xc8, 0x7d, 0xbd, 0x04, 0x8e, + 0x05, 0xdb, 0x0e, 0xc2, 0xc1, 0x1c, 0x88, 0x68, 0xd2, 0xc1, 0x28, + 0xe0, 0x08, 0x00, 0x16, 0x3a, 0x05, 0x98, 0x71, 0x16, 0x79, 0x04, + 0xad, 0x02, 0xb2, 0x01, 0x28, 0x88, 0xe6, 0xc2, 0xc1, 0x1c, 0x8e, + 0x06, 0xa1, 0x13, 0x41, 0x20, 0x70, 0xb2, 0x0c, 0x00, 0x0c, 0x05, + 0xbc, 0x3b, 0x61, 0x39, 0x71, 0xa8, 0x04, 0x92, 0x0a, 0x03, 0x66, + 0x39, 0x18, 0xd2, 0x2a, 0x19, 0xd2, 0xdd, 0x02, 0xd2, 0x1d, 0x1c, + 0x67, 0x6d, 0x0c, 0x88, 0x26, 0x0c, 0x6b, 0x8e, 0x09, 0xe0, 0x65, + 0x4b, 0x44, 0x1b, 0x55, 0xb7, 0x35, 0xd7, 0x86, 0x01, 0x00, 0x88, + 0xf6, 0x8e, 0x06, 0xeb, 0x4b, 0x53, 0x88, 0x47, 0x8e, 0x07, 0x81, + 0x13, 0x8e, 0x07, 0x81, 0xae, 0x3d, 0x8e, 0x06, 0x81, 0x70, 0x1d, + 0xf0, 0x0b, 0x94, 0x56, 0xd9, 0xee, 0xb8, 0x0a, 0xb2, 0xcb, 0xfe, + 0x56, 0x5b, 0xee, 0x0c, 0x19, 0x86, 0xb7, 0xff, 0x36, 0x41, 0x00, + 0x81, 0xe5, 0x8e, 0x08, 0xc6, 0x2f, 0x8e, 0x07, 0x81, 0xd2, 0x48, + 0xea, 0x71, 0x81, 0xe9, 0x71, 0x91, 0xe8, 0x71, 0xf1, 0xe7, 0x71, + 0xb1, 0xe5, 0x71, 0x21, 0x48, 0x70, 0x31, 0xe6, 0x71, 0x39, 0xa2, + 0xe8, 0x42, 0xe9, 0x0b, 0x31, 0x3a, 0x71, 0xf9, 0x42, 0x92, 0x63, + 0x16, 0xc2, 0x23, 0x15, 0xd8, 0x23, 0xa2, 0x23, 0x10, 0xa9, 0x3b, + 0x89, 0x23, 0xd9, 0x2b, 0x42, 0x63, 0x15, 0xc9, 0x1b, 0x21, 0xeb, + 0x71, 0x22, 0x63, 0x10, 0x8e, 0x08, 0x93, 0x78, 0xad, 0x01, 0xb1, + 0xec, 0x71, 0x1c, 0x0c, 0x8e, 0x06, 0xd6, 0x36, 0x9d, 0x01, 0x82, + 0xa0, 0x10, 0x76, 0xa8, 0x0f, 0xa2, 0x09, 0x00, 0xb2, 0x02, 0x00, + 0x1b, 0x99, 0xb0, 0xaa, 0x30, 0xa2, 0x42, 0x00, 0x1b, 0x22, 0x8e, + 0x08, 0xf0, 0x50, 0x03, 0x01, 0x66, 0xd8, 0x09, 0xcb, 0xa3, 0x0c, + 0x89, 0x92, 0x43, 0x01, 0x25, 0xfc, 0xff, 0x81, 0xed, 0x8e, 0x0a, + 0x89, 0x36, 0x8e, 0x06, 0x50, 0x21, 0x01, 0xad, 0x01, 0xb1, 0xee, + 0x71, 0x0c, 0x8c, 0x79, 0xe1, 0x62, 0x61, 0x15, 0x42, 0x61, 0x18, + 0x62, 0x22, 0x25, 0x59, 0xd1, 0x48, 0x06, 0x8e, 0x06, 0x5f, 0x69, + 0xc1, 0x49, 0xb1, 0xcc, 0x53, 0x0c, 0x07, 0x0c, 0x05, 0x06, 0x01, + 0x00, 0x52, 0x23, 0x71, 0x78, 0x23, 0x41, 0xef, 0x71, 0x88, 0x04, + 0x8e, 0x06, 0x83, 0x05, 0x21, 0x18, 0x0c, 0x16, 0x98, 0x09, 0xcc, + 0x6a, 0x66, 0x37, 0x04, 0x0c, 0x06, 0xc6, 0xff, 0xff, 0xb2, 0x22, + 0x11, 0xcc, 0x79, 0x8c, 0x33, 0xa8, 0xe3, 0x67, 0xea, 0x01, 0x0c, + 0x16, 0x67, 0x6b, 0x33, 0x8c, 0x73, 0xd8, 0xe3, 0x2c, 0x0e, 0xe0, + 0xdd, 0x20, 0xd9, 0xe3, 0x8e, 0x07, 0x34, 0x0c, 0x0c, 0x92, 0x02, + 0x03, 0xcc, 0x8a, 0x26, 0x39, 0x0b, 0xa2, 0x21, 0x18, 0xa8, 0x0a, + 0xcc, 0x3a, 0x0c, 0x16, 0x46, 0x00, 0x00, 0x0c, 0x06, 0xd2, 0xc9, + 0xfd, 0x0c, 0x3b, 0xd0, 0xcb, 0x83, 0x7d, 0x0c, 0x92, 0xa1, 0x41, + 0xd2, 0xa0, 0xff, 0xac, 0x45, 0xe2, 0x05, 0x01, 0xf2, 0x21, 0x24, + 0x9c, 0xce, 0x9a, 0xa3, 0xf2, 0x61, 0x17, 0x16, 0x1f, 0x1c, 0x52, + 0x61, 0x19, 0x0c, 0x19, 0x42, 0x0a, 0x80, 0x0c, 0x08, 0x00, 0x44, + 0x23, 0x60, 0x89, 0x83, 0x82, 0x61, 0x16, 0xc6, 0x04, 0x00, 0x52, + 0x61, 0x19, 0x42, 0x21, 0x15, 0x82, 0x21, 0x24, 0x0c, 0x09, 0x92, + 0x61, 0x16, 0x82, 0x61, 0x17, 0x00, 0x44, 0x23, 0x51, 0x00, 0x70, + 0x92, 0x21, 0x15, 0x1b, 0x84, 0x90, 0x90, 0x14, 0x92, 0x61, 0x14, + 0x16, 0x98, 0x12, 0x8e, 0x0b, 0x85, 0x1b, 0xcc, 0xe0, 0x08, 0x00, + 0x66, 0x37, 0x16, 0x81, 0x15, 0x70, 0x82, 0x28, 0x32, 0x8e, 0x06, + 0x98, 0x0b, 0x1d, 0x70, 0x88, 0x08, 0x8e, 0x07, 0xab, 0x46, 0x92, + 0x21, 0x16, 0x81, 0x14, 0x70, 0xe2, 0x21, 0x14, 0xb2, 0x21, 0x18, + 0xd2, 0x21, 0x17, 0xa8, 0xb1, 0xc2, 0xa1, 0xb3, 0xca, 0xaa, 0xd2, + 0x4b, 0x26, 0xe2, 0x4b, 0x27, 0x0c, 0x0d, 0x82, 0x28, 0x3d, 0xc2, + 0x0a, 0x80, 0xc9, 0xf1, 0xa2, 0x0a, 0x7f, 0xa9, 0x71, 0x0c, 0x0c, + 0x90, 0xc3, 0x93, 0x40, 0x8e, 0x05, 0xc6, 0x78, 0x92, 0x21, 0x19, + 0x26, 0x1a, 0x02, 0x86, 0x27, 0x00, 0xd2, 0x21, 0x18, 0xc8, 0xb1, + 0xe2, 0x21, 0x14, 0xc0, 0xc4, 0xb0, 0xe2, 0x4c, 0x2c, 0x0c, 0x1e, + 0xe2, 0x4c, 0x2d, 0xd8, 0x0d, 0xd9, 0xcc, 0xad, 0x0d, 0x16, 0x29, + 0x39, 0xf2, 0x09, 0x01, 0x16, 0xcf, 0x38, 0x16, 0xad, 0x28, 0xe2, + 0xa1, 0x80, 0xc9, 0x41, 0x88, 0xe1, 0xa8, 0x39, 0xb8, 0x29, 0xb2, + 0x61, 0x10, 0xa9, 0xa1, 0x80, 0x81, 0x04, 0x16, 0xc8, 0x16, 0xc2, + 0x21, 0x17, 0x16, 0x0c, 0x3d, 0xf8, 0xb1, 0xea, 0xff, 0xf2, 0x61, + 0x11, 0xf2, 0x0f, 0xb3, 0x16, 0x9f, 0x15, 0x0c, 0x08, 0x92, 0x21, + 0x10, 0x92, 0x61, 0x12, 0x82, 0x61, 0x13, 0xa2, 0x21, 0x12, 0xb8, + 0xd1, 0x82, 0x25, 0x3f, 0x0c, 0x8c, 0xe0, 0x08, 0x00, 0xe2, 0xa1, + 0x80, 0xa2, 0x21, 0x11, 0x92, 0x21, 0x13, 0x0c, 0x0d, 0xb2, 0x21, + 0x12, 0x0c, 0x0c, 0xc2, 0x4b, 0x10, 0xd2, 0x4b, 0x11, 0x1b, 0x99, + 0x0c, 0x0d, 0x92, 0x61, 0x13, 0xd2, 0x4b, 0x12, 0x0c, 0x0d, 0xd2, + 0x4b, 0x13, 0xb2, 0xcb, 0x14, 0xa2, 0x0a, 0xb3, 0xb2, 0x61, 0x12, + 0xa7, 0x39, 0xc1, 0xd2, 0x21, 0x18, 0xd8, 0x0d, 0x06, 0x42, 0x00, + 0x66, 0x37, 0x0b, 0x8e, 0x06, 0x81, 0x6d, 0x8e, 0x06, 0x81, 0xd9, + 0x72, 0x7c, 0xf7, 0x8e, 0x0b, 0x85, 0x43, 0xcc, 0xe0, 0x08, 0x00, + 0x46, 0x00, 0x00, 0x7c, 0xf7, 0xa1, 0xf0, 0x71, 0x82, 0x25, 0xa0, + 0xb2, 0x21, 0x16, 0xd2, 0x21, 0x14, 0xc2, 0x21, 0x17, 0x00, 0xdd, + 0x11, 0x80, 0xcc, 0x01, 0xf0, 0xbb, 0x11, 0xb0, 0xb6, 0x20, 0xd0, + 0xcc, 0x20, 0x80, 0xd4, 0x8e, 0x05, 0xb7, 0x04, 0xbb, 0x20, 0xe0, + 0x08, 0x00, 0xac, 0x93, 0x8e, 0x06, 0x2c, 0xd8, 0xe3, 0xb2, 0xa1, + 0x41, 0xba, 0xb3, 0xc2, 0x0b, 0x7f, 0x80, 0xdd, 0x01, 0x00, 0xcc, + 0x8e, 0x04, 0x21, 0xd2, 0x0b, 0x80, 0xb2, 0x0b, 0x81, 0x80, 0xdd, + 0x11, 0xd0, 0x8e, 0x05, 0xce, 0x7d, 0xe0, 0x08, 0x00, 0x2d, 0x07, + 0x1d, 0xf0, 0xe2, 0x0a, 0x81, 0x00, 0x4e, 0x23, 0xd7, 0x1e, 0x36, + 0xfc, 0x66, 0xf8, 0xb1, 0xf0, 0xf4, 0xb0, 0x82, 0x0f, 0x2d, 0x9c, + 0xe8, 0x92, 0x21, 0x15, 0x82, 0x0f, 0x2c, 0x97, 0x18, 0x16, 0xb2, + 0xa2, 0xf0, 0xba, 0xb3, 0xf2, 0x0b, 0x80, 0xd7, 0x1f, 0x0b, 0xf2, + 0x4a, 0x81, 0xe2, 0x4b, 0x80, 0x42, 0x0a, 0x81, 0x00, 0x44, 0x23, + 0x52, 0x61, 0x19, 0x0c, 0x18, 0x82, 0x61, 0x16, 0xc6, 0x89, 0xff, + 0x16, 0x76, 0x34, 0x8e, 0x06, 0x83, 0x6f, 0x8e, 0x05, 0x83, 0x6c, + 0x00, 0x44, 0x23, 0xc6, 0x84, 0xff, 0x8e, 0x05, 0x82, 0x17, 0xa8, + 0xf1, 0xc2, 0x21, 0x10, 0xa0, 0xaa, 0xa0, 0xc0, 0xaa, 0xa0, 0xa9, + 0x61, 0x8e, 0x05, 0x82, 0x24, 0xc8, 0x61, 0x0c, 0x0b, 0xb2, 0x4c, + 0x10, 0x8e, 0x04, 0x05, 0x11, 0x8e, 0x04, 0x05, 0x12, 0x8e, 0x04, + 0x05, 0x13, 0xd2, 0x21, 0x18, 0xe2, 0xa1, 0x80, 0xd8, 0x0d, 0xf8, + 0xe1, 0x07, 0x6f, 0x2b, 0x0c, 0x0c, 0x82, 0x21, 0x17, 0x0c, 0x1a, + 0x9c, 0x48, 0xb8, 0xb1, 0xea, 0xbb, 0xb2, 0x0b, 0xb2, 0x98, 0xa1, + 0x76, 0x9b, 0x06, 0xc9, 0x19, 0xa9, 0x09, 0x92, 0xc9, 0x10, 0x06, + 0x03, 0x00, 0x88, 0x71, 0xf8, 0xa1, 0xc0, 0x88, 0x11, 0x8a, 0xff, + 0xc9, 0x1f, 0xa9, 0x0f, 0x0b, 0x9d, 0x56, 0xd9, 0x0c, 0xa2, 0x21, + 0x17, 0x16, 0x8a, 0x06, 0x8e, 0x05, 0x2e, 0x61, 0x11, 0xb2, 0x0b, + 0xb2, 0x16, 0xbb, 0x1c, 0xd8, 0xa1, 0x0c, 0x0c, 0xc2, 0x61, 0x13, + 0xd9, 0x21, 0x8b, 0xdd, 0xd9, 0x31, 0xa8, 0x21, 0xbd, 0x01, 0x8e, + 0x08, 0x83, 0x24, 0xa8, 0x31, 0x8e, 0x0a, 0x0c, 0xc8, 0x21, 0xa8, + 0x31, 0x92, 0x02, 0x01, 0xa2, 0xca, 0x10, 0x66, 0x29, 0x0e, 0xb8, + 0x0c, 0xd8, 0x1c, 0x1b, 0xeb, 0xb7, 0xbe, 0x01, 0x1b, 0xdd, 0xd9, + 0x1c, 0xe9, 0x0c, 0xa9, 0x31, 0xe2, 0x21, 0x11, 0xd2, 0x21, 0x13, + 0xc2, 0xcc, 0x10, 0xc9, 0x21, 0x1b, 0xdd, 0xe2, 0x0e, 0xb2, 0xd2, + 0x61, 0x13, 0xe7, 0x3d, 0xb3, 0xc6, 0x5b, 0x00, 0x0c, 0x8c, 0x82, + 0x25, 0x3f, 0xb8, 0x71, 0xa8, 0xa1, 0xc0, 0xbb, 0x11, 0xba, 0xaa, + 0xa9, 0x51, 0xbd, 0x01, 0xe0, 0x08, 0x00, 0xbd, 0x01, 0x0c, 0x8c, + 0xa8, 0x51, 0x82, 0x25, 0x3f, 0x8b, 0xaa, 0xe0, 0x08, 0x8e, 0x06, + 0x23, 0xa8, 0xf1, 0xb2, 0x8e, 0x05, 0x81, 0x7d, 0xb0, 0xaa, 0xa0, + 0xa9, 0x61, 0x8e, 0x0a, 0x25, 0x61, 0x8e, 0x08, 0x25, 0xb2, 0x02, + 0x01, 0xa8, 0x51, 0x66, 0x2b, 0x0e, 0xe8, 0x0a, 0xc8, 0x1a, 0x1b, + 0xde, 0xe7, 0xbd, 0x01, 0x1b, 0xcc, 0xc9, 0x1a, 0xd9, 0x0a, 0xa8, + 0x41, 0xa8, 0xca, 0xb2, 0x21, 0x17, 0x0c, 0x8c, 0x16, 0x4b, 0x05, + 0xd2, 0xca, 0xfc, 0xe8, 0xe3, 0x4c, 0x0a, 0xa0, 0xae, 0x20, 0xa9, + 0xe3, 0x56, 0xcd, 0x0e, 0x82, 0x25, 0x3f, 0xb2, 0x21, 0x18, 0xa2, + 0xa2, 0x26, 0xaa, 0xa3, 0xb2, 0xcb, 0x1e, 0xe0, 0x08, 0x00, 0xa8, + 0xe3, 0x8e, 0x07, 0x81, 0xe3, 0x65, 0xe3, 0xc2, 0x02, 0x01, 0x66, + 0x1c, 0x4e, 0x81, 0xa9, 0x71, 0xad, 0x02, 0x8e, 0x07, 0x9d, 0x43, + 0xbc, 0xfa, 0xad, 0x02, 0xb1, 0xb4, 0x70, 0x81, 0xf1, 0x71, 0x98, + 0xe3, 0x88, 0x88, 0xb0, 0x99, 0x20, 0x99, 0xe3, 0xe0, 0x08, 0x00, + 0x46, 0x0a, 0x00, 0x8c, 0x96, 0x98, 0xc1, 0xb2, 0x21, 0x15, 0x92, + 0x09, 0xb8, 0xb7, 0x99, 0x0e, 0xb2, 0xa1, 0x41, 0xd8, 0xe3, 0xba, + 0xb3, 0xc0, 0xdd, 0x20, 0xd9, 0xe3, 0x42, 0x4b, 0x81, 0x66, 0x1a, + 0x0a, 0x8e, 0x05, 0x9b, 0x01, 0x41, 0x8e, 0x05, 0xc3, 0x48, 0x8e, + 0x0f, 0x85, 0x18, 0x92, 0x02, 0x01, 0xa8, 0xe3, 0x66, 0x19, 0x26, + 0x4c, 0x0b, 0xb7, 0x0a, 0x5b, 0x37, 0xea, 0x05, 0xc2, 0x22, 0x19, + 0x37, 0x1c, 0x52, 0xbd, 0x03, 0xa2, 0xc7, 0xfd, 0x0c, 0x19, 0x0c, + 0x0c, 0x81, 0xef, 0x71, 0xa0, 0xc9, 0x83, 0x8e, 0x07, 0x8a, 0x55, + 0x46, 0x0e, 0x00, 0x4c, 0x09, 0x97, 0x8a, 0x0d, 0x8e, 0x05, 0x85, + 0x02, 0xb2, 0x0b, 0x80, 0xc2, 0xa0, 0xff, 0xc7, 0x9b, 0x26, 0x37, + 0xea, 0x0d, 0xc2, 0xa1, 0x41, 0xca, 0xc3, 0xc2, 0x0c, 0x81, 0xd2, + 0xa0, 0xff, 0xd7, 0x9c, 0x15, 0x8e, 0x16, 0x3d, 0x0c, 0x07, 0xc6, + 0x3e, 0xff, 0x92, 0xae, 0xff, 0x90, 0xaa, 0x10, 0x86, 0xc8, 0xff, + 0xa2, 0x21, 0x11, 0xa2, 0x0a, 0xb3, 0x16, 0x5a, 0xee, 0xc2, 0x21, + 0x10, 0x0c, 0x0b, 0xb2, 0x61, 0x13, 0xc2, 0x61, 0x12, 0x8b, 0xcc, + 0xc9, 0x81, 0xa2, 0x21, 0x12, 0x8e, 0x0b, 0x83, 0x58, 0x81, 0x8e, + 0x0a, 0x0c, 0xc2, 0x21, 0x12, 0xb8, 0x81, 0x8e, 0x06, 0x87, 0x0a, + 0xa2, 0x0a, 0xb3, 0x1b, 0x99, 0xb2, 0xcb, 0x14, 0xc2, 0xcc, 0x14, + 0xc2, 0x61, 0x12, 0xb9, 0x81, 0x92, 0x61, 0x13, 0xa7, 0x39, 0xc5, + 0x46, 0xa6, 0xff, 0xd2, 0x02, 0x01, 0x0b, 0xdd, 0x56, 0x8d, 0xd5, + 0xa8, 0xc1, 0xb2, 0xa0, 0x80, 0xba, 0xaa, 0xa9, 0x91, 0xe2, 0x0a, + 0x80, 0xa2, 0x0a, 0x82, 0xcc, 0x6e, 0xc8, 0x91, 0xc2, 0x0c, 0x81, + 0xa7, 0x9c, 0x53, 0xf2, 0x21, 0x10, 0xd8, 0x91, 0xe8, 0xf1, 0xa2, + 0x4d, 0x81, 0xe0, 0xee, 0xa0, 0x82, 0x2d, 0x1f, 0xf0, 0xee, 0xa0, + 0x80, 0xf8, 0x41, 0x82, 0x4e, 0x04, 0xf2, 0x4e, 0x05, 0xf0, 0xf8, + 0x41, 0x0c, 0x08, 0xf2, 0x4e, 0x06, 0xf0, 0xf8, 0x41, 0xf2, 0x4e, + 0x07, 0x0c, 0x0f, 0xd2, 0x2d, 0x1e, 0x82, 0x4e, 0x10, 0xd2, 0x4e, + 0x00, 0xf2, 0x4e, 0x11, 0xd0, 0xd8, 0x41, 0xd2, 0x4e, 0x01, 0x0c, + 0x0f, 0xf2, 0x4e, 0x12, 0x8e, 0x05, 0x0b, 0x02, 0x8e, 0x04, 0x0b, + 0x13, 0x8e, 0x05, 0x0b, 0x03, 0xb8, 0xd1, 0xd2, 0x21, 0x14, 0xc2, + 0xa0, 0xf8, 0xa8, 0xc1, 0xe8, 0x91, 0xca, 0xaa, 0xd2, 0x4e, 0x82, + 0x8e, 0x08, 0x81, 0x32, 0x88, 0x91, 0x0c, 0x1f, 0xf2, 0x48, 0x80, + 0x46, 0x3d, 0xff, 0x42, 0x02, 0x00, 0x4b, 0x44, 0x00, 0x44, 0x23, + 0x86, 0x27, 0x8e, 0x07, 0xa2, 0x30, 0x41, 0xf3, 0x71, 0xa1, 0xed, + 0x71, 0x81, 0xf2, 0x71, 0x31, 0xef, 0x71, 0x21, 0xf4, 0x71, 0x29, + 0x33, 0x98, 0xa8, 0x99, 0x0a, 0x49, 0xa8, 0x8e, 0x09, 0x81, 0xb6, + 0x10, 0x98, 0x03, 0xc1, 0x99, 0x71, 0x66, 0x59, 0x0a, 0x0c, 0x38, + 0x0c, 0x19, 0x99, 0x0c, 0x89, 0x03, 0x86, 0x01, 0x00, 0x66, 0x19, + 0x03, 0x0c, 0x0d, 0xd9, 0x0c, 0x88, 0x8e, 0x06, 0xa7, 0x61, 0x8e, + 0x07, 0x28, 0x51, 0x99, 0x71, 0x42, 0x22, 0x1b, 0x88, 0x25, 0x42, + 0x24, 0x95, 0xe0, 0x08, 0x00, 0xac, 0xd3, 0x92, 0xa0, 0xb4, 0x31, + 0x00, 0x70, 0x9a, 0x44, 0x8e, 0x08, 0x9c, 0x2d, 0x82, 0x23, 0x6f, + 0x8e, 0x05, 0x08, 0xcd, 0x02, 0xa1, 0xf6, 0x71, 0xb1, 0xf5, 0x8e, + 0x06, 0xfe, 0x47, 0x1e, 0x8e, 0x06, 0x81, 0x9d, 0x02, 0x35, 0x1d, + 0xf0, 0x0c, 0x09, 0x86, 0xfd, 0x8e, 0x05, 0x92, 0x78, 0x92, 0x22, + 0x1b, 0x82, 0x09, 0xf7, 0x0c, 0x0a, 0xac, 0x88, 0xb1, 0x99, 0x71, + 0xc8, 0x3b, 0xa2, 0x49, 0xeb, 0x9c, 0xac, 0x31, 0x00, 0x70, 0x22, + 0xcb, 0x20, 0x8e, 0x0d, 0x81, 0xbe, 0x5b, 0xac, 0x8e, 0x08, 0x98, + 0x7c, 0x0c, 0x02, 0x1d, 0xf0, 0xa2, 0x19, 0x74, 0x8e, 0x05, 0xa5, + 0x0c, 0x86, 0xf2, 0x8e, 0x06, 0xe9, 0x7c, 0x82, 0x22, 0x1b, 0x82, + 0x08, 0xf9, 0x8c, 0x88, 0x81, 0x99, 0x71, 0x8e, 0x0d, 0x81, 0xe4, + 0x28, 0xd1, 0xf7, 0x71, 0x41, 0xf9, 0x71, 0x51, 0xf8, 0x71, 0xe1, + 0xb5, 0x70, 0x21, 0xfa, 0x71, 0x31, 0xf5, 0x71, 0x91, 0x99, 0x71, + 0x0c, 0x0b, 0xb9, 0x39, 0xa2, 0x23, 0x20, 0x88, 0xa3, 0x89, 0x49, + 0x29, 0xa3, 0x52, 0x63, 0x1d, 0xa9, 0x29, 0x42, 0x63, 0x20, 0xc8, + 0xfe, 0xd9, 0xfe, 0xc9, 0x19, 0x8e, 0x09, 0x81, 0xe1, 0x60, 0xfb, + 0x71, 0x6b, 0x73, 0x0c, 0x29, 0x88, 0x94, 0xb2, 0x22, 0x1c, 0xb9, + 0x11, 0xb2, 0x2b, 0x4b, 0xb9, 0x01, 0x17, 0x68, 0x1c, 0xc2, 0x24, + 0x10, 0x0c, 0x05, 0x0b, 0xcc, 0x56, 0xcc, 0x12, 0xad, 0x07, 0x81, + 0x00, 0x70, 0xb2, 0xc4, 0x44, 0x82, 0x28, 0x8e, 0x06, 0xc6, 0x64, + 0x46, 0x00, 0x00, 0x5d, 0x09, 0x81, 0x1a, 0x8e, 0x05, 0x9b, 0x25, + 0x16, 0xbd, 0x07, 0xe0, 0x08, 0x00, 0xd8, 0x01, 0x6d, 0x8e, 0x0e, + 0xaf, 0x75, 0xad, 0x02, 0x0c, 0x2b, 0x0c, 0x2c, 0x92, 0xa2, 0x40, + 0x81, 0xb5, 0x70, 0xf2, 0x0d, 0x00, 0xe2, 0xa1, 0x3a, 0xea, 0xe6, + 0xe9, 0x21, 0x1b, 0xff, 0xf2, 0x4d, 0x00, 0x82, 0x28, 0x18, 0x92, + 0x5e, 0x7f, 0x8e, 0x06, 0x90, 0x69, 0x97, 0x70, 0xb2, 0xc3, 0x58, + 0x82, 0x28, 0x13, 0x8e, 0x05, 0x94, 0x24, 0x81, 0xfb, 0x71, 0xad, + 0x02, 0x88, 0x08, 0xb8, 0xa4, 0xe0, 0x08, 0x00, 0xec, 0xc5, 0xad, + 0x02, 0xc1, 0x78, 0x71, 0x81, 0xb5, 0x70, 0xb8, 0x21, 0x0c, 0x29, + 0x92, 0x53, 0x17, 0x92, 0x1b, 0x7f, 0x88, 0xa8, 0xc0, 0x99, 0x20, + 0x92, 0x5b, 0x8e, 0x04, 0x3b, 0x66, 0x2a, 0x36, 0xa2, 0x13, 0x17, + 0x2c, 0x0b, 0xb0, 0xaa, 0x20, 0xa2, 0x53, 0x17, 0x06, 0x0a, 0x00, + 0x66, 0x25, 0x25, 0xad, 0x07, 0x4b, 0xb2, 0x8e, 0x05, 0xa3, 0x7f, + 0x2c, 0x19, 0x82, 0x28, 0x3f, 0x92, 0x53, 0x17, 0x8e, 0x05, 0x94, + 0x39, 0x2d, 0x26, 0x29, 0x0b, 0xa2, 0x13, 0x17, 0xb2, 0xa4, 0x00, + 0x8e, 0x06, 0x2c, 0x8e, 0x06, 0xc5, 0x55, 0x81, 0xa9, 0x71, 0x78, + 0x11, 0x88, 0x08, 0xd2, 0xc7, 0x4b, 0xe0, 0x08, 0x00, 0xa2, 0x57, + 0x00, 0xa2, 0x43, 0x50, 0x98, 0xc4, 0x1c, 0x0b, 0x26, 0x19, 0x08, + 0xa2, 0x13, 0x17, 0x8e, 0x06, 0x29, 0x81, 0x3a, 0x71, 0x71, 0x1a, + 0x71, 0x8e, 0x07, 0x93, 0x27, 0xdd, 0x05, 0xcd, 0x03, 0x41, 0xfb, + 0x71, 0xbd, 0x06, 0x0c, 0x09, 0x82, 0x27, 0x17, 0xa2, 0x53, 0x14, + 0x99, 0x93, 0x99, 0x83, 0x8e, 0x06, 0x93, 0x20, 0x06, 0xad, 0x02, + 0x0c, 0x3c, 0x0c, 0x0d, 0x88, 0x94, 0x8e, 0x05, 0x9c, 0x19, 0x8e, + 0x05, 0xc9, 0x73, 0x8e, 0x06, 0x82, 0x14, 0xc6, 0xb6, 0xff, 0x36, + 0x41, 0x00, 0xa8, 0x44, 0xa2, 0x2a, 0x26, 0xa2, 0x0a, 0x18, 0xb2, + 0xa0, 0x7f, 0x26, 0x4a, 0x16, 0xb7, 0x1a, 0x13, 0xad, 0x02, 0xbd, + 0x03, 0x81, 0xfc, 0x71, 0xcd, 0x04, 0x88, 0x08, 0xdd, 0x05, 0x8e, + 0x07, 0x9a, 0x63, 0x92, 0x02, 0x8e, 0x0c, 0xb5, 0x7e, 0xdc, 0x40, + 0xb4, 0x20, 0x20, 0xa2, 0x20, 0x65, 0x64, 0xfe, 0x92, 0xa0, 0x00, + 0x0c, 0x32, 0xa0, 0x8e, 0x05, 0xa7, 0x33, 0x8e, 0x05, 0xa6, 0x18, + 0x0c, 0x0c, 0x8d, 0x0c, 0x49, 0x81, 0x1a, 0x71, 0xb8, 0x13, 0xa8, + 0x03, 0x19, 0x31, 0x88, 0xd8, 0x92, 0x51, 0x04, 0xd2, 0x41, 0x00, + 0xc2, 0x41, 0x01, 0xd9, 0x11, 0xc2, 0xa0, 0xd0, 0x8e, 0x05, 0xae, + 0x3a, 0x21, 0x00, 0x70, 0x31, 0xfd, 0x8e, 0x0e, 0x81, 0x97, 0x31, + 0x0d, 0xc1, 0xfe, 0x71, 0x82, 0x22, 0x6d, 0xc8, 0x8e, 0x07, 0x81, + 0xe7, 0x50, 0x61, 0x8e, 0x06, 0x9c, 0x38, 0x6e, 0xa1, 0xfd, 0x8e, + 0x05, 0xc7, 0x13, 0x4c, 0x0c, 0x0d, 0x0c, 0x1e, 0x81, 0xfb, 0x8e, + 0x05, 0x54, 0x0c, 0x09, 0x99, 0x01, 0x88, 0xa8, 0x0c, 0x0f, 0x8e, + 0x0b, 0xf4, 0x24, 0x9d, 0x03, 0x81, 0xff, 0x71, 0x31, 0x00, 0x70, + 0x88, 0x08, 0x99, 0x41, 0xac, 0xe8, 0x26, 0xd5, 0x2c, 0x5c, 0x09, + 0x97, 0x15, 0x27, 0xcc, 0x44, 0xa1, 0xf6, 0x70, 0x06, 0x02, 0x00, + 0xa2, 0xd4, 0x01, 0xa2, 0x0a, 0xc0, 0x80, 0xaa, 0x11, 0x82, 0x23, + 0xa1, 0xb2, 0x02, 0x00, 0xc1, 0x00, 0x72, 0xb0, 0xba, 0x20, 0xc0, + 0xbb, 0x20, 0xa1, 0x01, 0x72, 0xcd, 0x05, 0xe0, 0x08, 0x00, 0x81, + 0xf1, 0x71, 0xad, 0x02, 0x88, 0x38, 0x8e, 0x05, 0xa4, 0x67, 0x9d, + 0x0a, 0xa1, 0xfc, 0x71, 0x8c, 0xa9, 0xb2, 0xa0, 0xa0, 0xb7, 0x15, + 0x1d, 0xc2, 0xa0, 0xc0, 0xc7, 0x15, 0x17, 0xad, 0x02, 0xb8, 0x41, + 0xcd, 0x04, 0xdd, 0x05, 0x81, 0xfc, 0x71, 0xed, 0x06, 0x88, 0x38, + 0xfd, 0x8e, 0x06, 0x9a, 0x0a, 0x1d, 0xf0, 0x92, 0x02, 0x01, 0x66, + 0x19, 0xe1, 0x98, 0x41, 0x98, 0x49, 0x92, 0x29, 0x26, 0xb2, 0x09, + 0x01, 0x67, 0xeb, 0xd4, 0x92, 0x19, 0x0c, 0x71, 0x02, 0x72, 0x26, + 0x69, 0x05, 0xb2, 0xc9, 0xf9, 0x56, 0x0b, 0x08, 0x52, 0xc7, 0xc0, + 0xc8, 0x07, 0x62, 0xc7, 0xe0, 0xec, 0x2c, 0xb1, 0x03, 0x72, 0xc2, + 0xc7, 0x94, 0x29, 0x1a, 0x49, 0x2a, 0x82, 0x23, 0x70, 0xad, 0x8e, + 0x04, 0x75, 0xc1, 0x04, 0x72, 0x8e, 0x06, 0x9f, 0x53, 0x05, 0x72, + 0x8e, 0x06, 0x87, 0x00, 0x07, 0x82, 0x23, 0x6e, 0x8e, 0x06, 0x81, + 0xed, 0x20, 0x8e, 0x07, 0x9f, 0x37, 0x0c, 0x0b, 0xad, 0x05, 0x0c, + 0x0d, 0x51, 0xfe, 0x71, 0x82, 0x23, 0x6d, 0xc8, 0x05, 0x8e, 0x05, + 0x9f, 0x70, 0xc8, 0x15, 0x8e, 0x06, 0x81, 0xf6, 0x70, 0x8e, 0x06, + 0xb2, 0x6d, 0xad, 0x02, 0x19, 0x31, 0xc2, 0xa0, 0xd0, 0x81, 0x1a, + 0x71, 0x0c, 0x0d, 0x0c, 0x49, 0x0c, 0x8e, 0x00, 0xe2, 0x41, 0x00, + 0xe9, 0x11, 0x8e, 0x05, 0x82, 0x6e, 0x01, 0x88, 0xd8, 0x8e, 0x05, + 0x82, 0x68, 0x8e, 0x06, 0xca, 0x28, 0x61, 0x00, 0x91, 0xd7, 0x70, + 0x82, 0x22, 0x11, 0x90, 0x88, 0x10, 0x82, 0x62, 0x11, 0x8c, 0xf3, + 0xa8, 0xe3, 0xb7, 0x7a, 0x0b, 0x81, 0x8e, 0x05, 0xf4, 0x06, 0x28, + 0x0c, 0x7b, 0xe0, 0x08, 0x8e, 0x09, 0xcf, 0x2b, 0x8e, 0x05, 0x83, + 0x74, 0x98, 0xc1, 0x99, 0x01, 0x8e, 0x09, 0x87, 0x31, 0x36, 0x81, + 0x00, 0xcd, 0x04, 0x48, 0x44, 0xed, 0x06, 0x92, 0x24, 0x26, 0xd2, + 0x14, 0x4e, 0x72, 0xc9, 0x1c, 0x9a, 0xad, 0x66, 0xc5, 0x02, 0x72, + 0xc9, 0x22, 0xa7, 0xb7, 0x19, 0x1b, 0x87, 0xa7, 0x38, 0x02, 0x46, + 0x53, 0x00, 0xb2, 0x07, 0x01, 0xba, 0xb7, 0x2b, 0xbb, 0xb7, 0xba, + 0x02, 0x06, 0x50, 0x00, 0x7d, 0x0b, 0xa7, 0x3b, 0xe5, 0x0c, 0x09, + 0x81, 0x06, 0x72, 0x8c, 0xc9, 0xa0, 0xf7, 0xc0, 0xfa, 0xfd, 0xf2, + 0x44, 0x9c, 0xf0, 0xf8, 0x41, 0xf2, 0x44, 0x9d, 0x82, 0x08, 0x00, + 0x9c, 0x88, 0x8c, 0xf3, 0x98, 0x23, 0x66, 0x29, 0x0b, 0xa1, 0xe2, + 0x70, 0x0c, 0x1b, 0xb9, 0x31, 0xc9, 0x0a, 0x86, 0x02, 0x00, 0x0c, + 0x0d, 0xd9, 0x31, 0xc6, 0x00, 0x00, 0x0c, 0x0f, 0xf9, 0x31, 0xad, + 0x02, 0x81, 0xfc, 0x71, 0xbd, 0x03, 0x88, 0x58, 0x8e, 0x05, 0x84, + 0x75, 0x6d, 0x0a, 0xa9, 0x21, 0x92, 0x24, 0x26, 0x42, 0x8e, 0x06, + 0x78, 0x44, 0x8e, 0x06, 0x78, 0x47, 0x37, 0x02, 0x46, 0x25, 0x00, + 0x3c, 0x2b, 0x3c, 0x05, 0x82, 0xa0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0d, + 0xd9, 0x51, 0xc9, 0x41, 0x8a, 0x82, 0x89, 0x11, 0xe2, 0x07, 0x00, + 0xf2, 0x07, 0x01, 0x26, 0x1e, 0x74, 0x57, 0x1e, 0x5c, 0xb7, 0x1e, + 0x4a, 0x82, 0xa0, 0xdd, 0x87, 0x9e, 0x49, 0x0c, 0x0e, 0xb6, 0x4f, + 0x25, 0xc2, 0x07, 0x05, 0xa2, 0x07, 0x03, 0x92, 0x07, 0x02, 0x80, + 0x8e, 0x05, 0xce, 0x07, 0xa2, 0x07, 0x04, 0x80, 0xcc, 0x01, 0x00, + 0xaa, 0x11, 0xc0, 0xaa, 0x20, 0xa0, 0x99, 0x20, 0xa1, 0x07, 0x72, + 0xa7, 0x99, 0x01, 0x0c, 0x1e, 0x9c, 0xbe, 0xad, 0x02, 0xbd, 0x07, + 0x81, 0xa9, 0x71, 0xc8, 0x11, 0x88, 0x58, 0xdd, 0x01, 0xe0, 0x08, + 0x00, 0x3c, 0x2b, 0xf2, 0x07, 0x01, 0x06, 0x01, 0x00, 0x79, 0x41, + 0xf2, 0x07, 0x01, 0x7a, 0x7f, 0x2b, 0x77, 0x47, 0x37, 0x99, 0x46, + 0x8e, 0x05, 0xb2, 0x6d, 0x8e, 0x07, 0x27, 0x68, 0x8e, 0x07, 0x27, + 0x46, 0xf7, 0xff, 0x79, 0x51, 0x06, 0xf6, 0xff, 0x0c, 0x0a, 0x0c, + 0x0b, 0xb9, 0x51, 0xa9, 0x41, 0xc8, 0x21, 0xd2, 0x01, 0x00, 0xec, + 0x5c, 0x66, 0x4d, 0x23, 0x92, 0x23, 0x88, 0xe1, 0xfb, 0x71, 0x26, + 0x49, 0x2e, 0x26, 0x69, 0x2b, 0x26, 0x59, 0x02, 0x66, 0x79, 0x11, + 0xbd, 0x03, 0xad, 0x02, 0xc8, 0x51, 0xd8, 0x41, 0x0c, 0x19, 0x88, + 0xbe, 0x92, 0x63, 0x88, 0x8e, 0x05, 0x91, 0x2e, 0x8c, 0x5a, 0xc8, + 0x21, 0x0c, 0x3b, 0xc0, 0x6b, 0x83, 0x2d, 0x06, 0x1d, 0xf0, 0x0c, + 0x19, 0x06, 0xb0, 0xff, 0x8e, 0x09, 0x26, 0x09, 0x8e, 0x08, 0x26, + 0x86, 0xf5, 0x8e, 0x07, 0x8c, 0x2c, 0xad, 0x02, 0xbd, 0x03, 0x21, + 0x10, 0x70, 0x81, 0xfc, 0x71, 0x28, 0x02, 0x88, 0x68, 0x32, 0xc2, + 0x40, 0x22, 0x02, 0x55, 0xe0, 0x08, 0x00, 0x22, 0x43, 0x15, 0x8e, + 0x06, 0x83, 0x6c, 0xa1, 0xfb, 0x71, 0x82, 0x11, 0x18, 0x89, 0x31, + 0xec, 0xd8, 0x92, 0x02, 0x02, 0x66, 0x49, 0x28, 0xc8, 0xe2, 0xed, + 0x03, 0x0c, 0x0f, 0xb2, 0x22, 0x18, 0xd2, 0x2a, 0x26, 0x8e, 0x06, + 0x83, 0x75, 0x68, 0xb8, 0x4b, 0x8e, 0x05, 0x85, 0x43, 0x56, 0xea, + 0x07, 0xd1, 0xd1, 0x70, 0xc8, 0xe3, 0xd0, 0xcc, 0x20, 0xc9, 0xe3, + 0x1d, 0xf0, 0x66, 0x16, 0x45, 0x8e, 0x0a, 0xfa, 0x69, 0x8e, 0x05, + 0x95, 0x13, 0x72, 0x51, 0x01, 0x98, 0x31, 0x1b, 0xa5, 0xa2, 0x51, + 0x02, 0x92, 0x51, 0x00, 0xa1, 0x1a, 0x71, 0xdc, 0x29, 0xbd, 0x03, + 0xc2, 0xa0, 0xb0, 0xdd, 0x01, 0x88, 0xda, 0x8e, 0x06, 0x81, 0xe2, + 0x67, 0xfb, 0x71, 0x06, 0x04, 0x00, 0xab, 0xb4, 0x8e, 0x06, 0x14, + 0xea, 0x8e, 0x08, 0x14, 0x98, 0x31, 0xdc, 0x89, 0xbd, 0x03, 0x0c, + 0x2c, 0x0c, 0x0d, 0x0c, 0x0e, 0x88, 0x9a, 0x8e, 0x06, 0x9e, 0x09, + 0x31, 0xcc, 0x59, 0xcc, 0x3a, 0x0c, 0x0a, 0x46, 0x02, 0x00, 0xb2, + 0x22, 0x56, 0x0c, 0x2a, 0x1b, 0xbb, 0xb2, 0x62, 0x56, 0x8e, 0x0a, + 0xd4, 0x60, 0x8e, 0x06, 0x83, 0x76, 0x8e, 0x08, 0xe2, 0x0b, 0x8e, + 0x07, 0x81, 0xb0, 0x58, 0x44, 0x82, 0x28, 0x26, 0x82, 0x08, 0x19, + 0x66, 0x18, 0x14, 0x8e, 0x05, 0xf4, 0x6b, 0x8e, 0x07, 0x88, 0x0b, + 0x8e, 0x04, 0x09, 0x08, 0x72, 0xe0, 0x08, 0x00, 0xcd, 0x04, 0x8e, + 0x06, 0x36, 0x8e, 0x06, 0x89, 0x7c, 0x8e, 0x0a, 0x4c, 0xec, 0x74, + 0xfc, 0x8e, 0x05, 0xac, 0x06, 0xa2, 0x05, 0x01, 0x90, 0x80, 0x74, + 0xf6, 0xb8, 0xf2, 0xa0, 0x90, 0x74, 0xf6, 0xb9, 0xec, 0x8e, 0x05, + 0xa4, 0x35, 0x8e, 0x05, 0x31, 0x8e, 0x06, 0x81, 0x99, 0x55, 0x8e, + 0x05, 0x87, 0x42, 0x04, 0x01, 0x56, 0x65, 0xfd, 0x0c, 0x0a, 0x06, + 0xf5, 0xff, 0x56, 0x14, 0xff, 0x0c, 0x09, 0xc6, 0xfb, 0x8e, 0x07, + 0x82, 0x64, 0xb1, 0x0a, 0x72, 0x81, 0x0b, 0x72, 0xd1, 0x09, 0x72, + 0x31, 0xfb, 0x71, 0x91, 0x1a, 0x71, 0x42, 0x23, 0x1b, 0x52, 0x23, + 0x1a, 0x28, 0xa3, 0xe2, 0x23, 0x24, 0xf2, 0x23, 0x11, 0xa8, 0x89, + 0xd9, 0x29, 0xc2, 0x29, 0x1e, 0xd1, 0xfc, 0x71, 0xb2, 0x69, 0x1e, + 0x89, 0x89, 0xb1, 0x0c, 0x72, 0x81, 0x0f, 0x72, 0x89, 0xa3, 0xf9, + 0x5d, 0xe9, 0x8d, 0xb9, 0xd3, 0xc9, 0x6d, 0xa9, 0x7d, 0x59, 0x0d, + 0x29, 0x4d, 0x49, 0x3d, 0x21, 0x12, 0x72, 0x41, 0x11, 0x72, 0x51, + 0x10, 0x72, 0xa1, 0x0d, 0x72, 0xc8, 0xb3, 0xc9, 0x9d, 0xa2, 0x63, + 0x1a, 0x52, 0x63, 0x11, 0x42, 0x63, 0x24, 0x91, 0x0e, 0x72, 0x92, + 0x63, 0x1b, 0x29, 0xb3, 0x8e, 0x08, 0xa6, 0x20, 0x15, 0x70, 0x82, + 0x28, 0x1d, 0x1c, 0xfa, 0xe0, 0x08, 0x00, 0x7c, 0xec, 0x91, 0x13, + 0x72, 0xbd, 0x0a, 0xa1, 0xb0, 0x70, 0xac, 0x6b, 0xc0, 0x20, 0x00, + 0x82, 0x29, 0x13, 0xc0, 0x8e, 0x06, 0x81, 0xc8, 0x0c, 0x69, 0x13, + 0x0c, 0x1f, 0xc0, 0x20, 0x00, 0xe2, 0x2a, 0x8c, 0x8e, 0x07, 0xe6, + 0x25, 0x6a, 0x8c, 0xc0, 0x20, 0x00, 0xd2, 0x2a, 0x8c, 0x1d, 0xf0, + 0x8e, 0x04, 0x0e, 0x29, 0x13, 0xc0, 0xee, 0x8e, 0x04, 0x28, 0xe2, + 0x69, 0x13, 0x8e, 0x06, 0x17, 0xc0, 0x8e, 0x06, 0x81, 0xc7, 0x7e, + 0x8e, 0x05, 0x26, 0xb2, 0x8e, 0x04, 0x26, 0x36, 0x41, 0x00, 0x31, + 0x14, 0x70, 0x21, 0x14, 0x72, 0x22, 0x63, 0x60, 0x8e, 0x07, 0xca, + 0x14, 0x9d, 0x05, 0x52, 0x22, 0x1f, 0x39, 0x51, 0xc2, 0x25, 0x42, + 0x99, 0x41, 0x16, 0xfc, 0x08, 0x26, 0x14, 0x21, 0x26, 0x34, 0x1e, + 0x0c, 0xb8, 0x87, 0x14, 0x19, 0x26, 0xb4, 0x16, 0x1c, 0x19, 0x97, + 0x14, 0x11, 0x66, 0x44, 0x45, 0x81, 0x1c, 0x70, 0x88, 0xe8, 0xe0, + 0x08, 0x00, 0xbc, 0xaa, 0x0c, 0x03, 0x06, 0x01, 0x00, 0x16, 0x7c, + 0x06, 0x0c, 0x03, 0x8c, 0xa3, 0x92, 0x25, 0x4a, 0xa2, 0xaf, 0x7f, + 0xa0, 0x99, 0x10, 0x92, 0x65, 0x4a, 0xfd, 0x07, 0xed, 0x06, 0xcd, + 0x04, 0xad, 0x02, 0x81, 0x15, 0x72, 0xb8, 0x51, 0x88, 0x08, 0xd8, + 0x41, 0xe0, 0x08, 0x00, 0x8e, 0x06, 0x22, 0xa0, 0x80, 0xa0, 0x99, + 0x20, 0x92, 0x65, 0x4a, 0x1d, 0xf0, 0xb2, 0x02, 0x01, 0xc2, 0x02, + 0x02, 0x66, 0x1b, 0xba, 0x66, 0x3c, 0xb7, 0x66, 0x44, 0xb4, 0x66, + 0x47, 0xb1, 0xad, 0x02, 0xcd, 0x04, 0xfd, 0x06, 0xe8, 0x41, 0x81, + 0x4c, 0x70, 0xd8, 0x51, 0xb2, 0x02, 0x00, 0x79, 0x01, 0x6b, 0xdd, + 0x82, 0x28, 0x27, 0xe0, 0xe0, 0x74, 0xe0, 0x08, 0x00, 0x0c, 0x13, + 0x46, 0xe6, 0xff, 0x26, 0x54, 0x02, 0x66, 0x64, 0x91, 0x0c, 0x13, + 0x86, 0xe3, 0x8e, 0x07, 0x83, 0x14, 0x8e, 0x0a, 0x89, 0x25, 0xad, + 0x02, 0x21, 0x15, 0x72, 0x0c, 0x19, 0x88, 0x22, 0x99, 0x12, 0x8e, + 0x05, 0xa3, 0x35, 0xa9, 0x12, 0x8e, 0x09, 0x90, 0x24, 0x3c, 0x70, + 0x52, 0x22, 0x1f, 0xac, 0x44, 0xbd, 0x04, 0x82, 0x26, 0x8e, 0x06, + 0xd2, 0x7f, 0x0c, 0x4b, 0xcd, 0x01, 0xa2, 0x41, 0x00, 0x8e, 0x08, + 0x81, 0xf9, 0x7e, 0x88, 0x86, 0x8e, 0x05, 0x07, 0x41, 0x16, 0x72, + 0x06, 0x02, 0x8e, 0x08, 0x0d, 0x0c, 0x04, 0x98, 0xc3, 0x66, 0x19, + 0x09, 0x81, 0xf2, 0x71, 0x8e, 0x07, 0xd9, 0x26, 0xad, 0x02, 0x31, + 0xde, 0x71, 0x0c, 0x0b, 0x88, 0xb3, 0x8e, 0x05, 0x8f, 0x71, 0xad, + 0x02, 0x8e, 0x05, 0x0b, 0x1c, 0x8e, 0x08, 0x81, 0xe0, 0x2a, 0x8e, + 0x06, 0x0b, 0xa2, 0xa6, 0x0f, 0x92, 0x25, 0x49, 0xa0, 0x44, 0x20, + 0x07, 0x69, 0x05, 0xb1, 0x17, 0x72, 0xb0, 0x44, 0x20, 0x8e, 0x05, + 0xa2, 0x04, 0x4c, 0x70, 0x91, 0x18, 0x72, 0x88, 0xa8, 0x92, 0x65, + 0x4c, 0xe0, 0x08, 0x00, 0x22, 0xa1, 0x0c, 0x31, 0x00, 0x70, 0x2a, + 0x25, 0x8e, 0x08, 0x92, 0x34, 0xc2, 0x25, 0x42, 0x8c, 0xfc, 0x8e, + 0x06, 0xa4, 0x32, 0x8e, 0x07, 0x81, 0x87, 0x4b, 0x25, 0x42, 0xcc, + 0xd9, 0x8e, 0x05, 0xac, 0x07, 0x19, 0x72, 0x8e, 0x0a, 0xab, 0x47, + 0x36, 0x41, 0x00, 0xa2, 0xa0, 0x00, 0xb2, 0xa0, 0x01, 0x65, 0x69, + 0x02, 0x81, 0x15, 0x72, 0x30, 0xb3, 0x20, 0x8e, 0x0b, 0x82, 0x8c, + 0x01, 0x8e, 0x07, 0x94, 0x30, 0x1c, 0x72, 0x81, 0x1b, 0x72, 0x91, + 0x1a, 0x72, 0x21, 0x1d, 0x72, 0x31, 0x4c, 0x70, 0xb1, 0x15, 0x72, + 0xc8, 0x43, 0xd8, 0x73, 0xa8, 0x23, 0xa9, 0x3b, 0x29, 0x23, 0x99, + 0x13, 0x89, 0x73, 0xd9, 0x0b, 0x49, 0x43, 0xc9, 0x2b, 0x8e, 0x06, + 0x92, 0x70, 0x82, 0x02, 0x01, 0x91, 0x0d, 0x70, 0x66, 0x18, 0x15, + 0x92, 0x09, 0x00, 0x16, 0xf9, 0x00, 0xa2, 0x24, 0x06, 0x56, 0x9a, + 0x8e, 0x05, 0xc7, 0x6d, 0xc2, 0x13, 0x06, 0xa5, 0x01, 0xfb, 0xcd, + 0x04, 0x81, 0x1e, 0x72, 0x8e, 0x0d, 0xaa, 0x4b, 0x8e, 0x06, 0xa1, + 0x38, 0x41, 0x1e, 0x72, 0x56, 0x78, 0x07, 0xd8, 0x93, 0x71, 0x00, + 0x70, 0x62, 0x1d, 0x0b, 0x8b, 0x54, 0x60, 0xc4, 0xf4, 0xc2, 0x53, + 0x20, 0x92, 0x0d, 0x01, 0x60, 0x60, 0x34, 0x37, 0x69, 0x47, 0x82, + 0x14, 0x02, 0x92, 0x14, 0x03, 0xc7, 0x98, 0x3e, 0x97, 0x96, 0x3b, + 0xab, 0xad, 0xbd, 0x05, 0x8e, 0x08, 0xcb, 0x32, 0xec, 0x7a, 0xb2, + 0x23, 0x12, 0xa8, 0xdb, 0x1b, 0xaa, 0xa2, 0x4b, 0x34, 0x8e, 0x05, + 0xc5, 0x7a, 0x35, 0x8e, 0x05, 0x06, 0x36, 0x8e, 0x05, 0x06, 0x37, + 0x92, 0x22, 0x49, 0x0c, 0x0a, 0x1b, 0x99, 0x92, 0x62, 0x49, 0x06, + 0x09, 0x00, 0xd8, 0x93, 0xc2, 0x13, 0x20, 0xad, 0x05, 0x62, 0x54, + 0x03, 0xab, 0xbd, 0xc2, 0x54, 0x02, 0x82, 0x27, 0x8e, 0x06, 0x93, + 0x38, 0x0c, 0x1a, 0x06, 0x02, 0x00, 0xbd, 0x03, 0x88, 0x44, 0x8e, + 0x0f, 0x81, 0x74, 0x82, 0x22, 0x21, 0x82, 0x08, 0x71, 0x66, 0x18, + 0x2e, 0xb2, 0x22, 0x18, 0xc1, 0x00, 0x70, 0xac, 0x5b, 0xa8, 0x95, + 0x92, 0x0a, 0x01, 0x90, 0x90, 0x14, 0xac, 0xf9, 0x26, 0x19, 0x4f, + 0x26, 0x29, 0x38, 0x0c, 0x09, 0x9c, 0x19, 0xa2, 0x02, 0x32, 0x0c, + 0x0b, 0x81, 0x14, 0x70, 0x0c, 0x0c, 0x82, 0x28, 0x16, 0xd2, 0xa5, + 0xdc, 0x8e, 0x06, 0x92, 0x16, 0x8e, 0x07, 0x81, 0x63, 0x8e, 0x0a, + 0x82, 0x8c, 0x40, 0xa2, 0xca, 0x10, 0x82, 0x2c, 0x8e, 0x06, 0x81, + 0x31, 0x46, 0x02, 0x00, 0xab, 0xaa, 0x8e, 0x08, 0x0d, 0x0c, 0x1b, + 0x0c, 0x09, 0xa0, 0x9b, 0x83, 0x06, 0xed, 0xff, 0x4b, 0x8e, 0x09, + 0x14, 0x06, 0xfa, 0x8e, 0x06, 0x95, 0x50, 0x52, 0xa0, 0xa0, 0xc2, + 0x04, 0x02, 0x62, 0xa0, 0xc0, 0x67, 0x1c, 0x1a, 0x57, 0x1c, 0x17, + 0x82, 0xa0, 0xd0, 0x87, 0x1c, 0x11, 0x8e, 0x08, 0x5b, 0x8e, 0x06, + 0xea, 0x02, 0x8e, 0x04, 0x5b, 0x98, 0x64, 0x56, 0x89, 0xfe, 0x8e, + 0x07, 0x90, 0x52, 0xb8, 0xc4, 0xe0, 0x08, 0x00, 0x16, 0x9a, 0xfd, + 0x98, 0x74, 0x56, 0x49, 0xfd, 0xc2, 0x04, 0x02, 0x67, 0x1c, 0x02, + 0x57, 0x9c, 0xcb, 0x0c, 0x1a, 0xa9, 0x74, 0x06, 0xf1, 0x8e, 0x06, + 0x81, 0xe3, 0x20, 0x1f, 0x72, 0x8e, 0x09, 0x8a, 0x71, 0xac, 0xe0, + 0x08, 0x00, 0x81, 0x23, 0x72, 0xb1, 0x21, 0x72, 0xc1, 0x1e, 0x72, + 0x91, 0x20, 0x72, 0xe1, 0x22, 0x72, 0xa8, 0x09, 0xd8, 0x49, 0xf8, + 0x0e, 0xf9, 0x4c, 0xb9, 0x49, 0x89, 0x09, 0xa9, 0x6c, 0xd9, 0x0c, + 0xa1, 0xa3, 0x71, 0xd1, 0x24, 0x72, 0xd9, 0x0e, 0x91, 0x25, 0x72, + 0xb8, 0x0a, 0xb9, 0x5c, 0x99, 0x8e, 0x09, 0x82, 0x1c, 0xc8, 0x44, + 0xd2, 0xa0, 0xf0, 0xc2, 0x2c, 0x26, 0x81, 0xff, 0x71, 0xc2, 0x0c, + 0x00, 0x88, 0x08, 0xd0, 0xcc, 0x10, 0xac, 0xf8, 0x26, 0xdc, 0x2d, + 0x5c, 0x0d, 0xd7, 0x1c, 0x28, 0xcc, 0x43, 0x8e, 0x07, 0x92, 0x07, + 0xd3, 0x8e, 0x07, 0x92, 0x07, 0xd1, 0x00, 0x8e, 0x04, 0x76, 0xb2, + 0x02, 0x00, 0x82, 0x28, 0xa1, 0xb0, 0xba, 0x20, 0xd0, 0xbb, 0x20, + 0xa1, 0x26, 0x8e, 0x07, 0x8a, 0x52, 0x27, 0x8e, 0x0e, 0x84, 0x0b, + 0x8e, 0x05, 0xb4, 0x00, 0x28, 0x72, 0x31, 0xfb, 0x71, 0x81, 0x27, + 0x72, 0x42, 0x23, 0x1d, 0x49, 0x08, 0x22, 0x63, 0x1d, 0x8e, 0x08, + 0x89, 0x38, 0x29, 0x8e, 0x0a, 0x2c, 0x8e, 0x08, 0x81, 0x08, 0x8e, + 0x07, 0xaf, 0x1c, 0x8e, 0x05, 0x1a, 0x8e, 0x06, 0x86, 0x4c, 0x8e, + 0x0b, 0x82, 0x92, 0x0c, 0x29, 0x72, 0x8e, 0x0c, 0x82, 0x92, 0x0a, + 0x8e, 0x05, 0xaf, 0x30, 0xa2, 0x22, 0x15, 0x0c, 0x09, 0x52, 0x22, + 0x97, 0x59, 0x81, 0x58, 0x35, 0x92, 0x4a, 0x74, 0x82, 0x02, 0x03, + 0x66, 0x38, 0x0e, 0x81, 0x2a, 0x72, 0x88, 0x08, 0x8e, 0x07, 0x3f, + 0xa2, 0x22, 0x15, 0xb1, 0x2b, 0x72, 0x0c, 0x2c, 0x62, 0x0a, 0x06, + 0x71, 0x00, 0x70, 0xa2, 0xc5, 0x69, 0x82, 0x27, 0x41, 0xa9, 0x71, + 0x8e, 0x05, 0xdd, 0x2a, 0xa8, 0x71, 0x82, 0x27, 0xbf, 0x8e, 0x05, + 0x9e, 0x72, 0x06, 0x01, 0x00, 0x92, 0xa0, 0x60, 0x99, 0x01, 0xad, + 0x02, 0x81, 0x3c, 0x70, 0x0c, 0x8b, 0x82, 0x28, 0x2e, 0x4b, 0xc1, + 0x8e, 0x04, 0x23, 0x76, 0xa8, 0x11, 0x98, 0x01, 0xa7, 0x19, 0x42, + 0xfc, 0x06, 0x8e, 0x0a, 0x1a, 0xc2, 0xc5, 0x50, 0xe0, 0x08, 0x00, + 0x8e, 0x06, 0x8e, 0x08, 0x38, 0xb2, 0x25, 0x14, 0xe0, 0x08, 0x00, + 0x9c, 0x1a, 0xad, 0x02, 0x0c, 0x9b, 0x8b, 0xc1, 0x8e, 0x04, 0x23, + 0x09, 0x82, 0x28, 0x2d, 0x99, 0x21, 0x8e, 0x06, 0x81, 0x8a, 0x41, + 0x3c, 0x70, 0xb8, 0x01, 0x82, 0x26, 0x1c, 0x8e, 0x05, 0x88, 0x05, + 0x82, 0x27, 0xc4, 0xe0, 0x08, 0x00, 0x61, 0x2c, 0x72, 0x8e, 0x07, + 0x88, 0x34, 0x92, 0x02, 0x03, 0xb1, 0xa2, 0x70, 0xa2, 0x22, 0x11, + 0x0c, 0x0c, 0xc2, 0x62, 0x9a, 0xb0, 0xaa, 0x10, 0xa2, 0x62, 0x11, + 0x66, 0x39, 0x0d, 0xad, 0x02, 0x0c, 0x3b, 0xc2, 0xa0, 0xc0, 0x88, + 0x26, 0x0c, 0x1d, 0x8e, 0x04, 0x2f, 0x20, 0x70, 0xc8, 0x81, 0xa2, + 0xa0, 0xff, 0xc2, 0x0c, 0x07, 0xc2, 0x43, 0x50, 0x92, 0x05, 0x6f, + 0xc1, 0x24, 0x70, 0xa0, 0xb9, 0xc0, 0x16, 0x5b, 0x0f, 0xd2, 0xa0, + 0x80, 0xd0, 0xd9, 0x20, 0xb8, 0x71, 0xa2, 0xc3, 0x53, 0xd2, 0x43, + 0x51, 0x82, 0x27, 0x3f, 0x8e, 0x05, 0xb6, 0x72, 0x0c, 0x19, 0xa8, + 0x81, 0x0c, 0x0b, 0xc2, 0x0a, 0x08, 0xc2, 0x43, 0x56, 0xb2, 0x5a, + 0x02, 0xb2, 0x45, 0x68, 0x92, 0x4a, 0x06, 0xf6, 0x24, 0x02, 0x06, + 0x36, 0x00, 0x99, 0x61, 0x06, 0x02, 0x00, 0xd2, 0x11, 0x0a, 0x40, + 0xdd, 0xc0, 0x16, 0x0d, 0x0f, 0x82, 0x27, 0xc3, 0x8e, 0x05, 0xdf, + 0x63, 0x16, 0xba, 0xfe, 0x0c, 0x0e, 0x16, 0x5e, 0x05, 0xd1, 0x24, + 0x70, 0x0c, 0x0b, 0xd2, 0x0d, 0x00, 0x0c, 0x03, 0x16, 0x8d, 0x0d, + 0x60, 0xab, 0xa0, 0xa8, 0x0a, 0x92, 0x0a, 0x01, 0x66, 0x19, 0x32, + 0xe9, 0x91, 0xc2, 0x0a, 0x03, 0xb9, 0xa1, 0x66, 0x3c, 0x28, 0x8e, + 0x06, 0xc9, 0x0e, 0xe0, 0x08, 0x00, 0xe8, 0x91, 0xd1, 0x24, 0x70, + 0x92, 0x1a, 0x00, 0xd2, 0x0d, 0x00, 0x47, 0x99, 0x0f, 0xb8, 0x2a, + 0x67, 0x6b, 0x0a, 0xc1, 0x03, 0x70, 0xc2, 0x2c, 0xb3, 0x17, 0xec, + 0x01, 0x0c, 0x13, 0xb8, 0xa1, 0x8e, 0x07, 0xd9, 0x32, 0xb9, 0x46, + 0x00, 0x00, 0x0c, 0x03, 0x8c, 0xfe, 0x26, 0x13, 0x0d, 0x81, 0x2c, + 0x72, 0xad, 0x02, 0x88, 0xa8, 0x8e, 0x05, 0x96, 0x1e, 0x1d, 0xf0, + 0xa2, 0x02, 0x00, 0xb1, 0x2d, 0x72, 0x0c, 0x59, 0xc8, 0x61, 0x82, + 0x27, 0xe0, 0x30, 0x8e, 0x05, 0xaf, 0x43, 0x9c, 0x33, 0xa2, 0x02, + 0x00, 0x0c, 0x4b, 0xc1, 0x2e, 0x72, 0x0c, 0x0d, 0x0c, 0x0e, 0x82, + 0x27, 0xe7, 0xf2, 0xa0, 0x6b, 0x8e, 0x06, 0x9a, 0x39, 0x3c, 0x70, + 0xb2, 0x25, 0x14, 0x82, 0x28, 0x8e, 0x06, 0x82, 0x43, 0x1d, 0xf0, + 0xd8, 0x81, 0xd2, 0x0d, 0x0a, 0xa7, 0x1d, 0x02, 0x06, 0xc1, 0xff, + 0xd2, 0x0c, 0x00, 0x16, 0x9d, 0x05, 0xc6, 0x0b, 0x8e, 0x04, 0x26, + 0x2c, 0x72, 0x1b, 0x94, 0x92, 0x45, 0x6e, 0x88, 0x08, 0x8e, 0x06, + 0x81, 0x89, 0x12, 0x02, 0x35, 0x66, 0x1a, 0x0c, 0xa2, 0xc5, 0x58, + 0x0c, 0x0b, 0x82, 0x27, 0x3e, 0x0c, 0x8e, 0x06, 0xd1, 0x0e, 0x0c, + 0x1e, 0x86, 0xc5, 0xff, 0x0c, 0x03, 0x06, 0xdb, 0xff, 0x0c, 0x0f, + 0x0c, 0x0e, 0x76, 0x9d, 0x1c, 0x60, 0xcf, 0xa0, 0xc8, 0x0c, 0x1b, + 0xff, 0x92, 0x0c, 0x01, 0xf0, 0xf0, 0x74, 0x66, 0x29, 0x0a, 0xc2, + 0x2c, 0x97, 0xc2, 0x0c, 0x0a, 0xa7, 0x1c, 0x01, 0xea, 0xec, 0x8e, + 0x06, 0xea, 0x00, 0x0e, 0xf2, 0x02, 0x0c, 0xd0, 0xdf, 0xc0, 0xe0, + 0xdd, 0xc0, 0x86, 0xa5, 0x8e, 0x06, 0x87, 0x40, 0x8e, 0x0a, 0x85, + 0x1a, 0x8e, 0x09, 0xe3, 0x5a, 0x8e, 0x06, 0x85, 0x18, 0x32, 0x72, + 0x91, 0x2c, 0x72, 0xb1, 0x31, 0x72, 0xd1, 0x30, 0x72, 0xf1, 0x2f, + 0x72, 0x31, 0x2a, 0x72, 0x51, 0x29, 0x72, 0x38, 0x03, 0x22, 0x23, + 0x17, 0x29, 0x05, 0x21, 0x33, 0x72, 0xf2, 0x63, 0x17, 0xe8, 0xc3, + 0xe9, 0x15, 0xd9, 0xc3, 0xc8, 0x03, 0xc9, 0x45, 0xb9, 0x03, 0xa8, + 0x49, 0x89, 0x49, 0xa9, 0x25, 0x48, 0xb3, 0x49, 0x35, 0x8e, 0x05, + 0x8f, 0x41, 0x8e, 0x09, 0x89, 0x18, 0x51, 0x34, 0x72, 0x66, 0x18, + 0x32, 0x92, 0x02, 0x02, 0xb1, 0x14, 0x70, 0x26, 0x39, 0x29, 0xfc, + 0x34, 0xa2, 0x02, 0x32, 0x0c, 0x0c, 0xd2, 0xa5, 0xdc, 0x82, 0x2b, + 0x16, 0x8e, 0x05, 0xae, 0x3b, 0x06, 0x05, 0x8e, 0x05, 0xb8, 0x64, + 0xd2, 0xa0, 0x64, 0x0c, 0x49, 0x90, 0x9a, 0x20, 0x92, 0x45, 0x00, + 0xa2, 0x02, 0x32, 0xe0, 0x0e, 0x8e, 0x05, 0xda, 0x43, 0x88, 0x15, + 0x8e, 0x08, 0x81, 0xf8, 0x71, 0x05, 0x00, 0xe2, 0x2b, 0x16, 0x07, + 0xea, 0x02, 0x17, 0x6a, 0xe6, 0x92, 0x05, 0x01, 0xc2, 0xa0, 0xfc, + 0xc0, 0xaa, 0x10, 0xa2, 0x45, 0x00, 0xf6, 0x39, 0xc2, 0x8e, 0x05, + 0x89, 0x5a, 0x0c, 0x0c, 0x0c, 0xad, 0xe0, 0x0e, 0x00, 0x06, 0xf2, + 0x8e, 0x07, 0xcc, 0x04, 0xad, 0x02, 0xcd, 0x01, 0x81, 0x3c, 0x8e, + 0x05, 0x88, 0x41, 0x2e, 0xb9, 0x8e, 0x06, 0x81, 0x81, 0x3c, 0xe7, + 0xe9, 0x46, 0xa2, 0x02, 0xc0, 0x92, 0xc2, 0x65, 0xec, 0x1a, 0xb2, + 0x09, 0x9f, 0xdc, 0xcb, 0xc2, 0x09, 0xa0, 0xdc, 0x7c, 0xd2, 0x09, + 0xa1, 0xdc, 0x2d, 0xe2, 0x09, 0xa2, 0xcc, 0xde, 0xf2, 0x09, 0xa3, + 0xcc, 0x8f, 0x82, 0x09, 0xa4, 0xcc, 0x38, 0x0c, 0x0d, 0x06, 0x01, + 0x00, 0xd2, 0xa0, 0xc0, 0xda, 0xd2, 0xad, 0x02, 0xbd, 0x03, 0x6b, + 0xc3, 0x81, 0x3a, 0x71, 0xfd, 0x04, 0x82, 0x28, 0x14, 0x8e, 0x05, + 0x9c, 0x22, 0x8e, 0x05, 0xd6, 0x51, 0x8e, 0x06, 0x82, 0x91, 0x54, + 0x0c, 0x05, 0x82, 0x22, 0x21, 0x42, 0x22, 0x18, 0x82, 0x08, 0x71, + 0x31, 0x34, 0x72, 0x26, 0x18, 0x25, 0x52, 0x43, 0x00, 0x52, 0x43, + 0x01, 0x8e, 0x05, 0xb0, 0x04, 0x0e, 0xbd, 0x04, 0xad, 0x02, 0x0c, + 0x8c, 0x65, 0xf7, 0xff, 0x86, 0x00, 0x00, 0x52, 0x43, 0x00, 0x88, + 0x8e, 0x06, 0x8e, 0x75, 0x8e, 0x07, 0x9c, 0x2e, 0xa2, 0x03, 0x00, + 0x26, 0x39, 0xe8, 0x27, 0xea, 0xe5, 0x8e, 0x05, 0x84, 0x0b, 0xbd, + 0x05, 0x82, 0x28, 0x8e, 0x08, 0x82, 0x89, 0x62, 0xb2, 0xa1, 0x10, + 0xe7, 0xe9, 0x57, 0xa8, 0x44, 0xa8, 0x2a, 0xb7, 0xca, 0x50, 0xb7, + 0xea, 0x4d, 0x8e, 0x06, 0x47, 0xe5, 0xf2, 0xff, 0x0c, 0x1a, 0x8e, + 0x07, 0x82, 0x25, 0x81, 0x14, 0x70, 0x92, 0x03, 0x00, 0x82, 0x28, + 0x16, 0xa0, 0x99, 0x20, 0x92, 0x43, 0x8e, 0x05, 0x82, 0x2c, 0x08, + 0x00, 0xa1, 0x35, 0x72, 0xb2, 0x03, 0x00, 0x81, 0x00, 0x70, 0xc2, + 0x03, 0x01, 0x82, 0x28, 0xa1, 0x1b, 0xcc, 0xc0, 0xc0, 0x74, 0xc2, + 0x43, 0x8e, 0x05, 0xd2, 0x41, 0x03, 0x01, 0xb6, 0x69, 0x02, 0x86, + 0xde, 0xff, 0x8e, 0x06, 0xeb, 0x0c, 0x0c, 0x3b, 0x0c, 0x0c, 0x81, + 0xa3, 0x71, 0x0c, 0x8d, 0x88, 0x78, 0xe2, 0x22, 0x19, 0xe0, 0x08, + 0x00, 0x0c, 0x2a, 0x06, 0xe9, 0x8e, 0x08, 0x81, 0xff, 0x1c, 0x34, + 0x72, 0x8e, 0x07, 0x84, 0x02, 0xe1, 0x24, 0x70, 0x2d, 0x0a, 0x92, + 0x0e, 0x00, 0x0c, 0x0b, 0x9c, 0xa9, 0xd1, 0x20, 0x70, 0xc1, 0x37, + 0x71, 0xd0, 0xfb, 0xa0, 0xf8, 0x0f, 0xf2, 0x2f, 0x21, 0x1b, 0xbb, + 0xc2, 0x5f, 0x60, 0xa2, 0x0e, 0x00, 0xb0, 0xb0, 0x74, 0xa7, 0x3b, + 0xe9, 0x8e, 0x06, 0x83, 0x68, 0xad, 0x02, 0x82, 0x02, 0x02, 0xb1, + 0x34, 0x72, 0x26, 0x38, 0x07, 0x0c, 0x09, 0x92, 0x4b, 0x00, 0x92, + 0x4b, 0x01, 0x88, 0x4b, 0x8e, 0x0b, 0x81, 0xd1, 0x38, 0x52, 0x22, + 0x21, 0x82, 0x05, 0x74, 0x39, 0x11, 0x16, 0x08, 0x27, 0xb2, 0x02, + 0x03, 0xc2, 0xa0, 0x65, 0x16, 0x7b, 0x26, 0x37, 0xbc, 0x02, 0x46, + 0x98, 0x00, 0xd1, 0x91, 0x70, 0x71, 0x3d, 0x70, 0x0c, 0x24, 0x62, + 0x95, 0x76, 0x29, 0x01, 0x16, 0x36, 0x25, 0xe2, 0x15, 0x74, 0xf8, + 0x11, 0x16, 0xbe, 0x24, 0xf7, 0x26, 0x44, 0x8d, 0x0d, 0x8e, 0x07, + 0x89, 0x69, 0xd2, 0x05, 0xc2, 0x26, 0x2a, 0x38, 0x17, 0xed, 0x35, + 0xa8, 0x01, 0x88, 0xb7, 0x8e, 0x06, 0x81, 0xe7, 0x38, 0x05, 0xc2, + 0x81, 0x4c, 0x70, 0x40, 0x99, 0x20, 0x92, 0x45, 0xc2, 0x82, 0x28, + 0x1a, 0x8e, 0x06, 0x81, 0x83, 0x45, 0xbc, 0x0c, 0x0d, 0x81, 0xde, + 0x71, 0xbd, 0x0a, 0x88, 0xf8, 0x8e, 0x05, 0x10, 0xd2, 0x05, 0xc2, + 0x86, 0x00, 0x8e, 0x04, 0x06, 0x27, 0x6d, 0x0c, 0x81, 0xde, 0x71, + 0x82, 0x28, 0x26, 0x8e, 0x05, 0x17, 0x1d, 0xf0, 0x4d, 0x05, 0xd2, + 0xc5, 0x78, 0x0c, 0x06, 0x28, 0x11, 0xa8, 0xc5, 0x20, 0x20, 0xf4, + 0x07, 0x6a, 0x07, 0x0c, 0x09, 0x92, 0x45, 0x82, 0x46, 0x00, 0x00, + 0x0c, 0x19, 0x1b, 0x39, 0xe8, 0xc5, 0x7d, 0x0d, 0x37, 0x6e, 0x05, + 0x82, 0x24, 0x1f, 0x16, 0xe8, 0x04, 0xf2, 0x14, 0x3c, 0xd2, 0x04, + 0x82, 0xf7, 0x32, 0x05, 0x16, 0xed, 0x04, 0xf7, 0x3d, 0x4b, 0xf2, + 0x14, 0x3d, 0xd2, 0x04, 0x82, 0x27, 0x3f, 0x36, 0x8c, 0x1d, 0xd7, + 0xbf, 0x31, 0x92, 0x15, 0x74, 0xb2, 0x95, 0x75, 0x8c, 0xa9, 0xa2, + 0x25, 0x31, 0x26, 0x1a, 0x05, 0x07, 0x6e, 0x02, 0x56, 0xbb, 0x04, + 0xa8, 0x01, 0x81, 0xde, 0x71, 0xbd, 0x06, 0x82, 0x28, 0x24, 0xc8, + 0x11, 0x8e, 0x05, 0xce, 0x05, 0xa2, 0x44, 0x82, 0x92, 0x15, 0x74, + 0x0c, 0x1b, 0x8c, 0x19, 0xb2, 0x65, 0x32, 0xcb, 0x44, 0xcb, 0xd7, + 0x1b, 0x66, 0x67, 0x93, 0x9a, 0x46, 0x08, 0x00, 0x8e, 0x09, 0x2a, + 0x23, 0x8e, 0x0e, 0x2a, 0x0b, 0x16, 0x79, 0xfd, 0xb2, 0x65, 0x31, + 0x46, 0xf4, 0xff, 0xc2, 0x25, 0x32, 0x41, 0xde, 0x71, 0x66, 0x1c, + 0x07, 0xd2, 0x25, 0x31, 0x0b, 0xdd, 0x16, 0x6d, 0x13, 0xe2, 0x15, + 0x74, 0x82, 0x95, 0x75, 0x16, 0xde, 0x12, 0xf2, 0x25, 0x31, 0x98, + 0x11, 0x26, 0x1f, 0x2f, 0x97, 0x28, 0x2c, 0x81, 0x91, 0x70, 0x88, + 0x18, 0xa8, 0x05, 0xe0, 0x08, 0x00, 0x26, 0x2a, 0x1f, 0xa8, 0x01, + 0x0c, 0x2b, 0xc2, 0x05, 0xec, 0x92, 0x05, 0xc2, 0x0c, 0x1d, 0xd0, + 0x8e, 0x06, 0x82, 0x1c, 0x24, 0x27, 0xd2, 0x05, 0xee, 0xe0, 0x08, + 0x00, 0x0c, 0x1e, 0xe2, 0x65, 0x31, 0x21, 0x00, 0x70, 0x88, 0x11, + 0xf2, 0x05, 0xee, 0xd2, 0x05, 0xc2, 0x87, 0xbf, 0x68, 0x0c, 0x09, + 0x92, 0x65, 0x32, 0x27, 0x6d, 0x1a, 0xa1, 0x36, 0x72, 0xb8, 0x11, + 0x82, 0x22, 0xa1, 0xc2, 0x95, 0x75, 0x8e, 0x06, 0x81, 0xa0, 0x1b, + 0x88, 0x88, 0x8e, 0x08, 0x82, 0x40, 0x17, 0x6d, 0x42, 0xa1, 0x37, + 0x72, 0x92, 0xa0, 0xfd, 0x90, 0x9d, 0x10, 0x8e, 0x04, 0x4e, 0x22, + 0xa0, 0xb8, 0x11, 0xe0, 0x08, 0x00, 0x82, 0x24, 0x33, 0x8e, 0x05, + 0x22, 0x81, 0x3d, 0x70, 0xa8, 0x01, 0x88, 0xb8, 0x8e, 0x05, 0x87, + 0x43, 0x81, 0x4c, 0x70, 0x8e, 0x09, 0x83, 0x09, 0xfc, 0x0c, 0x0d, + 0xbd, 0x0a, 0x88, 0xf4, 0x8e, 0x08, 0x46, 0x07, 0x6d, 0x57, 0x27, + 0xed, 0x54, 0x92, 0x15, 0x74, 0x16, 0xe9, 0x04, 0xa8, 0x01, 0x0c, + 0x0b, 0x31, 0x3c, 0x70, 0xc2, 0xa0, 0xcc, 0x82, 0x23, 0x2e, 0xca, + 0xc5, 0xe0, 0x08, 0x00, 0xa1, 0x38, 0x72, 0x8e, 0x08, 0x58, 0x88, + 0x83, 0x8e, 0x05, 0x33, 0xb2, 0xa2, 0x0f, 0x0c, 0x4c, 0xd8, 0x74, + 0xe8, 0x01, 0x88, 0x33, 0xad, 0x0e, 0xe0, 0x08, 0x00, 0xcc, 0xca, + 0xd2, 0x05, 0xc2, 0x0c, 0x4e, 0xe0, 0xdd, 0x20, 0xd2, 0x45, 0xc2, + 0x86, 0x02, 0x8e, 0x09, 0x7f, 0x8e, 0x05, 0x81, 0x21, 0x20, 0x92, + 0x25, 0x32, 0x26, 0x19, 0x1a, 0x27, 0xed, 0x17, 0xa8, 0x01, 0xbd, + 0x06, 0x82, 0x24, 0x8e, 0x06, 0x82, 0x79, 0x0c, 0x19, 0xa8, 0x11, + 0xa2, 0x47, 0x0a, 0x92, 0x65, 0x32, 0x1d, 0xf0, 0x1d, 0xf0, 0xd2, + 0x05, 0xc2, 0x06, 0x7e, 0x8e, 0x05, 0x90, 0x54, 0x42, 0x22, 0x21, + 0x52, 0x24, 0x2f, 0xcc, 0x95, 0x81, 0x8e, 0x05, 0xa5, 0x68, 0x88, + 0x08, 0xc0, 0x20, 0x00, 0xa1, 0x39, 0x72, 0x8e, 0x09, 0x81, 0xff, + 0x26, 0x04, 0x91, 0x8e, 0x07, 0x82, 0x02, 0x8e, 0x06, 0xd4, 0x7d, + 0xb8, 0x45, 0xb7, 0x9a, 0x0f, 0x92, 0x04, 0x91, 0xc2, 0xa1, 0x10, + 0x8c, 0x69, 0xa8, 0x2b, 0xc7, 0xca, 0x10, 0xb7, 0xea, 0x0d, 0x81, + 0x34, 0x72, 0xad, 0x02, 0x88, 0x58, 0x8e, 0x07, 0xba, 0x6b, 0xbd, + 0x05, 0xad, 0x02, 0x81, 0xde, 0x71, 0x7c, 0xfc, 0x88, 0xd8, 0x8e, + 0x05, 0x8d, 0x57, 0x8e, 0x08, 0x90, 0x00, 0x8e, 0x05, 0x25, 0x68, + 0x8e, 0x05, 0x25, 0xc8, 0x43, 0xbd, 0x0a, 0x9c, 0xac, 0xd2, 0x1c, + 0x00, 0x91, 0x3a, 0x72, 0xe2, 0xa0, 0xff, 0xd7, 0xb9, 0x0f, 0xd1, + 0x4d, 0x70, 0xc2, 0x02, 0x00, 0xda, 0xcc, 0xc2, 0x0c, 0x00, 0xe7, + 0x1c, 0x01, 0xaa, 0xbc, 0x2d, 0x8e, 0x09, 0x82, 0xaa, 0x54, 0x51, + 0x3d, 0x72, 0x21, 0x3e, 0x72, 0xa1, 0x41, 0x72, 0xc1, 0x40, 0x72, + 0xe1, 0x3f, 0x72, 0x91, 0x34, 0x72, 0x81, 0x3b, 0x72, 0x71, 0x3c, + 0x72, 0x62, 0x28, 0x60, 0x32, 0x28, 0x44, 0xb2, 0x28, 0x43, 0x72, + 0x68, 0x6b, 0xd2, 0x28, 0x40, 0xf2, 0x28, 0x5f, 0xf9, 0x49, 0xd9, + 0x39, 0x0c, 0x37, 0xb9, 0x59, 0xe2, 0x68, 0x5f, 0xc2, 0x68, 0x40, + 0xa2, 0x68, 0x43, 0x22, 0x68, 0x44, 0x52, 0x68, 0x60, 0x39, 0x19, + 0x69, 0x29, 0x0c, 0x03, 0x7c, 0xf6, 0x52, 0xc9, 0x20, 0x0c, 0x82, + 0xa1, 0x42, 0x72, 0x82, 0xd8, 0x01, 0xb2, 0x28, 0x10, 0xb9, 0x69, + 0xa2, 0x68, 0x10, 0x76, 0xa2, 0x11, 0x5a, 0x43, 0xcc, 0x43, 0x72, + 0x44, 0x00, 0x86, 0x00, 0x00, 0x62, 0x44, 0x00, 0x8e, 0x05, 0xfd, + 0x48, 0x8e, 0x08, 0x81, 0x30, 0x43, 0x8e, 0x13, 0x91, 0x5c, 0x44, + 0x72, 0x31, 0xa3, 0x71, 0x81, 0x43, 0x72, 0x48, 0x13, 0x49, 0x08, + 0x29, 0x13, 0x8e, 0x06, 0x81, 0xb5, 0x78, 0x45, 0x8e, 0x12, 0x28, + 0x8e, 0x06, 0x14, 0x8e, 0x0e, 0xf1, 0x44, 0x8e, 0x06, 0x14, 0x8e, + 0x0f, 0x82, 0xa3, 0x5c, 0x45, 0x8e, 0x08, 0x88, 0x5c, 0x8e, 0x0b, + 0x9d, 0x5c, 0x8e, 0x05, 0x28, 0x8e, 0x09, 0xaa, 0x02, 0x8e, 0x06, + 0x97, 0x40, 0x4a, 0x72, 0x81, 0x49, 0x72, 0x91, 0x48, 0x72, 0xa1, + 0x47, 0x72, 0x31, 0x46, 0x72, 0xc1, 0x45, 0x72, 0xb8, 0x43, 0xb9, + 0x4c, 0xd8, 0x03, 0xe8, 0x33, 0xf8, 0x23, 0x28, 0x13, 0xa9, 0x13, + 0x99, 0x23, 0xf9, 0x1c, 0x89, 0x33, 0xe9, 0x2c, 0x49, 0x03, 0x29, + 0x0c, 0xd9, 0x3c, 0x21, 0x4b, 0x72, 0x29, 0x43, 0x8e, 0x05, 0x81, + 0x1c, 0xbd, 0x03, 0xad, 0x02, 0xd1, 0x4c, 0x72, 0x00, 0x12, 0x40, + 0x0c, 0x1c, 0x00, 0xcc, 0xa1, 0xc0, 0x20, 0x00, 0x82, 0x2d, 0x90, + 0xc7, 0x88, 0x22, 0x8e, 0x05, 0x09, 0xa0, 0xc0, 0x8e, 0x07, 0x81, + 0xe4, 0x59, 0xa0, 0x7c, 0xff, 0xf0, 0xfc, 0x30, 0xc0, 0x20, 0x00, + 0xe2, 0x2d, 0xa0, 0xf0, 0x8e, 0x06, 0x9c, 0x19, 0x6d, 0xa0, 0x81, + 0x4d, 0x72, 0x8e, 0x05, 0xf8, 0x01, 0x8e, 0x06, 0xf8, 0x78, 0x4e, + 0x72, 0x31, 0x14, 0x70, 0x81, 0x4d, 0x72, 0x42, 0x23, 0xb5, 0x49, + 0x08, 0x22, 0x63, 0xb5, 0x8e, 0x08, 0x81, 0xdf, 0x54, 0x12, 0x70, + 0x68, 0x03, 0x62, 0x26, 0x68, 0x0c, 0x12, 0x68, 0x46, 0xd1, 0x4f, + 0x72, 0x9d, 0x06, 0x16, 0x66, 0x08, 0xb8, 0x03, 0xa8, 0x49, 0xb2, + 0x2b, 0x68, 0x48, 0xaa, 0x82, 0x1b, 0x06, 0x40, 0x40, 0xf4, 0x40, + 0xe8, 0x41, 0x42, 0x4a, 0x28, 0x1b, 0x58, 0xe2, 0x4a, 0x29, 0x00, + 0x88, 0x11, 0xe0, 0xe8, 0x41, 0xe2, 0x4a, 0x2a, 0xd0, 0x88, 0x10, + 0xe0, 0xe8, 0x41, 0x80, 0x44, 0x20, 0x40, 0xf8, 0x41, 0xe2, 0x4a, + 0x2b, 0x52, 0x5b, 0x06, 0x42, 0x4a, 0x28, 0xf2, 0x4a, 0x29, 0xf0, + 0x8e, 0x05, 0x1e, 0xe0, 0x88, 0x41, 0x82, 0x4a, 0x2b, 0x66, 0x12, + 0x04, 0x0c, 0x02, 0x06, 0x05, 0x00, 0xa0, 0xe8, 0x41, 0xa2, 0x4c, + 0x04, 0xe2, 0x4c, 0x05, 0x8e, 0x04, 0x3b, 0x4c, 0x06, 0x8e, 0x05, + 0x06, 0x07, 0xf2, 0x0b, 0x0e, 0x1b, 0xff, 0xf2, 0x4b, 0x0e, 0x98, + 0x59, 0xcd, 0x0a, 0x56, 0xc9, 0xf8, 0x9c, 0x06, 0xb8, 0x46, 0xa8, + 0x03, 0x81, 0x14, 0x70, 0xa2, 0x2a, 0x68, 0x82, 0x28, 0xb5, 0x8e, + 0x0a, 0xe8, 0x44, 0x81, 0x12, 0x70, 0x88, 0x08, 0x82, 0x28, 0x68, + 0x98, 0x05, 0x88, 0x08, 0x97, 0x98, 0x44, 0x16, 0x13, 0x05, 0xd8, + 0x03, 0x9c, 0xb4, 0x9d, 0x0d, 0xac, 0x7d, 0xc2, 0xa2, 0x80, 0xa8, + 0x49, 0xa2, 0x2a, 0x20, 0x8c, 0x8a, 0xca, 0xea, 0xb2, 0x0e, 0xd9, + 0x1b, 0xbb, 0xb2, 0x4e, 0xd9, 0x98, 0x59, 0x56, 0x99, 0xfe, 0x8c, + 0xcd, 0x0c, 0x0c, 0xf8, 0x55, 0xe8, 0x13, 0xd9, 0x0f, 0xe9, 0x55, + 0xc9, 0x03, 0x39, 0x13, 0x92, 0x03, 0x08, 0x88, 0x75, 0xa8, 0x65, + 0x9a, 0x88, 0x9a, 0xaa, 0xa9, 0x65, 0x89, 0x75, 0x8e, 0x07, 0x82, + 0xa4, 0x60, 0x50, 0x8e, 0x10, 0x95, 0x4c, 0x61, 0x00, 0x71, 0x51, + 0x72, 0x61, 0x14, 0x70, 0x0c, 0xf5, 0x9d, 0x02, 0x31, 0x12, 0x70, + 0x0c, 0x02, 0x99, 0x11, 0xb8, 0x03, 0x2b, 0xc1, 0x88, 0xab, 0x42, + 0x0b, 0x2c, 0xbd, 0x01, 0x40, 0x44, 0xb0, 0x80, 0x44, 0xa0, 0x82, + 0x26, 0xc4, 0x8e, 0x05, 0xae, 0x4e, 0x50, 0x8a, 0xc0, 0x16, 0x68, + 0x0a, 0xb8, 0x03, 0xa2, 0x0b, 0x2c, 0x1b, 0xaa, 0xa2, 0x4b, 0x2c, + 0xb8, 0x03, 0x98, 0x07, 0xa2, 0x0b, 0x2c, 0x0b, 0x99, 0xa7, 0xa9, + 0x04, 0x22, 0x8e, 0x04, 0x0f, 0xd2, 0x01, 0x00, 0xc2, 0x2b, 0x12, + 0x00, 0x0d, 0x40, 0xc0, 0xc0, 0xb1, 0x07, 0x6c, 0xb8, 0xf2, 0x2b, + 0x81, 0xb0, 0xed, 0x11, 0xea, 0xeb, 0xc2, 0xce, 0x4c, 0xd7, 0x9f, + 0x14, 0xbd, 0x04, 0x81, 0x3a, 0x71, 0xa8, 0x11, 0x82, 0x28, 0x18, + 0xc2, 0x11, 0x01, 0x8e, 0x05, 0xe6, 0x53, 0x46, 0xe5, 0xff, 0x92, + 0x2e, 0x14, 0xcc, 0x49, 0xa2, 0x2e, 0x17, 0xa2, 0x6e, 0x14, 0xd8, + 0x1c, 0x16, 0x5d, 0xf8, 0xe8, 0x4d, 0xf2, 0x2e, 0x1f, 0x0c, 0x09, + 0x37, 0x6f, 0x07, 0x98, 0xce, 0x90, 0x9e, 0x05, 0xc6, 0xff, 0xff, + 0x90, 0xa0, 0x74, 0x8c, 0x4a, 0xd8, 0x7d, 0xac, 0xdd, 0xe8, 0x4d, + 0xf8, 0xae, 0xa2, 0x11, 0x01, 0xf0, 0xf0, 0xf5, 0xa7, 0x1f, 0x05, + 0xa7, 0x3f, 0x02, 0xc6, 0xd5, 0xff, 0x88, 0x5d, 0x40, 0x98, 0x41, + 0x42, 0x4e, 0x70, 0x92, 0x4e, 0x71, 0x90, 0x98, 0x41, 0x92, 0x4e, + 0x72, 0x8e, 0x05, 0x06, 0x73, 0x89, 0x1c, 0xc6, 0xce, 0x8e, 0x05, + 0x8e, 0x21, 0x8e, 0x06, 0x85, 0x18, 0x50, 0x72, 0x8e, 0x08, 0x97, + 0x24, 0x2a, 0x21, 0x52, 0x72, 0x0c, 0x1b, 0x88, 0x02, 0x0c, 0x1c, + 0x88, 0x18, 0x8e, 0x06, 0x82, 0x9f, 0x4c, 0x2a, 0x88, 0x02, 0xb1, + 0x15, 0x70, 0x88, 0x28, 0xb2, 0x2b, 0x47, 0xe0, 0x08, 0x00, 0x88, + 0x02, 0x0c, 0x2a, 0x88, 0x58, 0x8e, 0x08, 0x81, 0xf4, 0x2d, 0x36, + 0x41, 0x00, 0x21, 0x55, 0x72, 0x91, 0x54, 0x72, 0xa1, 0x53, 0x72, + 0x31, 0x1d, 0x70, 0x81, 0x50, 0x72, 0x38, 0x03, 0xb8, 0x73, 0xb9, + 0x08, 0x99, 0x73, 0xa9, 0x83, 0x48, 0x13, 0x49, 0x18, 0x8e, 0x08, + 0x86, 0x48, 0x56, 0x72, 0x8e, 0x07, 0x82, 0x52, 0xc2, 0x22, 0xc7, + 0xb2, 0xc2, 0x24, 0xb6, 0x2c, 0x1b, 0x0c, 0x4d, 0x0c, 0x1a, 0x98, + 0x0b, 0x1b, 0xaa, 0x8c, 0xa9, 0xe2, 0x0b, 0x15, 0xb6, 0x2e, 0x05, + 0xd2, 0x5b, 0x10, 0x8e, 0x04, 0x1c, 0xcb, 0x24, 0xc7, 0x3a, 0xe7, + 0x8e, 0x08, 0x34, 0x8e, 0x09, 0x86, 0x68, 0x8e, 0x09, 0x86, 0x40, + 0x56, 0x8e, 0x10, 0x98, 0x38, 0x8e, 0x05, 0xf7, 0x24, 0x8e, 0x05, + 0x2a, 0x8e, 0x0a, 0xf7, 0x24, 0x8e, 0x07, 0x93, 0x60, 0x8e, 0x05, + 0x16, 0x8e, 0x0e, 0x86, 0x6c, 0x8e, 0x07, 0x99, 0x14, 0x8e, 0x05, + 0x1a, 0x8e, 0x0b, 0x82, 0xa8, 0x42, 0x8e, 0x0d, 0x48, 0x8e, 0x0a, + 0x82, 0xa8, 0x44, 0x41, 0x00, 0xf1, 0x5a, 0x72, 0xa1, 0x5d, 0x72, + 0xd1, 0x5b, 0x72, 0x31, 0x59, 0x72, 0x51, 0x58, 0x72, 0xb1, 0x57, + 0x72, 0x81, 0x56, 0x72, 0x28, 0xeb, 0xc8, 0x7b, 0xe8, 0x8b, 0x98, + 0xcb, 0x48, 0xdb, 0x49, 0x18, 0x59, 0xcb, 0x99, 0x08, 0x39, 0xdb, + 0x31, 0x5c, 0x72, 0xd9, 0x8b, 0xe9, 0x48, 0xa9, 0x7b, 0xc9, 0x58, + 0xf9, 0xeb, 0x41, 0x5e, 0x72, 0x29, 0x28, 0x52, 0x23, 0x77, 0x92, + 0x23, 0x76, 0x21, 0x5f, 0x72, 0x99, 0x38, 0x59, 0x68, 0x42, 0x63, + 0x76, 0x22, 0x63, 0x77, 0x8e, 0x0b, 0x82, 0x93, 0x1c, 0x82, 0x26, + 0x48, 0xe0, 0x08, 0x00, 0x41, 0x03, 0x70, 0xc1, 0x60, 0x72, 0xa9, + 0x01, 0xbd, 0x00, 0xb9, 0x11, 0x0c, 0x03, 0x71, 0x61, 0x72, 0x2d, + 0x01, 0xc0, 0x20, 0x00, 0x2c, 0x0a, 0x81, 0x62, 0x72, 0x32, 0x6c, + 0x8d, 0x92, 0x24, 0xad, 0x82, 0x64, 0xae, 0xa0, 0x99, 0x20, 0x92, + 0x64, 0xad, 0x42, 0x24, 0x81, 0xc0, 0x20, 0x00, 0xf1, 0xd2, 0x70, + 0xe2, 0x2c, 0x80, 0x8e, 0x07, 0xa3, 0x75, 0x6c, 0x80, 0xd1, 0xd6, + 0x8e, 0x06, 0x82, 0x8f, 0x19, 0x80, 0xd0, 0xbb, 0x8e, 0x05, 0xa4, + 0x18, 0x26, 0x3f, 0xa1, 0x63, 0x72, 0xb2, 0x6c, 0x80, 0xb1, 0x64, + 0x72, 0x32, 0x4a, 0x00, 0x32, 0x4a, 0x01, 0x32, 0x4a, 0x02, 0x32, + 0x4a, 0x03, 0x0c, 0x4c, 0x8e, 0x05, 0xe2, 0x76, 0x51, 0x65, 0x72, + 0x8c, 0xe4, 0xad, 0x05, 0xbd, 0x04, 0x82, 0x26, 0x3f, 0xc2, 0xa0, + 0xf0, 0xe0, 0x08, 0x00, 0x06, 0x1e, 0x00, 0xad, 0x8e, 0x07, 0x82, + 0x93, 0x4d, 0x8e, 0x05, 0x10, 0xad, 0x05, 0xbd, 0x01, 0x82, 0x26, + 0x3f, 0x0c, 0x4c, 0xe0, 0x08, 0x00, 0xd0, 0xb1, 0x03, 0xd0, 0xc8, + 0x41, 0xb0, 0xb2, 0x03, 0xa0, 0xb3, 0x03, 0x90, 0xb4, 0x03, 0xd2, + 0x47, 0x44, 0xb2, 0x47, 0x48, 0xa2, 0x47, 0x4c, 0x92, 0x47, 0x50, + 0xc2, 0x47, 0x45, 0x90, 0x98, 0x41, 0xa0, 0xa8, 0x41, 0xb0, 0xb8, + 0x41, 0xb2, 0x47, 0x49, 0xa2, 0x47, 0x4d, 0x92, 0x47, 0x51, 0xc0, + 0xc8, 0x41, 0xc2, 0x47, 0x46, 0x8e, 0x0b, 0x18, 0x4a, 0xa2, 0x47, + 0x4e, 0x92, 0x47, 0x52, 0x8e, 0x05, 0x18, 0x47, 0x8e, 0x0b, 0x18, + 0x4b, 0xa2, 0x47, 0x4f, 0x92, 0x47, 0x53, 0xa1, 0x66, 0x72, 0xd2, + 0x26, 0x3f, 0x16, 0x84, 0x05, 0x4b, 0xb1, 0x0c, 0x4c, 0xe0, 0x0d, + 0x00, 0x31, 0x67, 0x72, 0x0c, 0x14, 0xe1, 0x68, 0x72, 0x71, 0x69, + 0x72, 0xe8, 0x0e, 0x51, 0x6a, 0x72, 0xe0, 0xec, 0xb4, 0x20, 0x2e, + 0xb0, 0x27, 0xb5, 0x1e, 0x27, 0x37, 0x1b, 0xad, 0x03, 0x8e, 0x04, + 0x25, 0x82, 0x26, 0x3f, 0x22, 0xd2, 0xfe, 0x92, 0x22, 0x7c, 0x99, + 0x11, 0xe0, 0x08, 0x00, 0x4b, 0x33, 0x22, 0x22, 0x7d, 0x46, 0x04, + 0x00, 0x8e, 0x06, 0x1c, 0x0c, 0x09, 0x82, 0x26, 0x3f, 0x8e, 0x07, + 0x18, 0x1b, 0x44, 0x66, 0xb4, 0xc6, 0x06, 0x02, 0x00, 0xb1, 0x6b, + 0x72, 0xc2, 0xa4, 0xd0, 0xe0, 0x0d, 0x00, 0xb2, 0xa3, 0x00, 0x0c, + 0x3c, 0x0c, 0x0d, 0x91, 0x61, 0x72, 0xa2, 0xa4, 0xd0, 0x81, 0x6c, + 0x72, 0x0c, 0x1f, 0x82, 0x48, 0x08, 0x82, 0x48, 0x10, 0xf2, 0x48, + 0x00, 0xa2, 0x48, 0x06, 0xa2, 0x48, 0x04, 0x92, 0x48, 0x0c, 0x80, + 0xe8, 0x41, 0xd2, 0x48, 0x01, 0xc2, 0x48, 0x03, 0xb2, 0x48, 0x02, + 0xc1, 0x6e, 0x72, 0xb1, 0x6f, 0x72, 0x0c, 0x2d, 0xe2, 0x48, 0x11, + 0xe2, 0x48, 0x09, 0x90, 0x98, 0x41, 0x92, 0x48, 0x0d, 0x0c, 0x4a, + 0x90, 0x98, 0x41, 0xa2, 0x48, 0x07, 0xe0, 0xe8, 0x41, 0xa1, 0x6d, + 0x72, 0xe2, 0x48, 0x12, 0x92, 0x48, 0x0e, 0xe2, 0x48, 0x0a, 0x8e, + 0x05, 0x1d, 0x0f, 0xe0, 0xe8, 0x41, 0xe2, 0x48, 0x13, 0x0c, 0x49, + 0xe2, 0x48, 0x0b, 0x92, 0x48, 0x05, 0x1c, 0x19, 0x0c, 0x0e, 0xf2, + 0x6a, 0x83, 0xe2, 0x6a, 0x7f, 0x82, 0x6a, 0x7d, 0xd2, 0x6a, 0x80, + 0x82, 0x26, 0x75, 0xb2, 0x6c, 0x7f, 0x92, 0x6a, 0x7e, 0x0c, 0x4a, + 0x8e, 0x05, 0xea, 0x7d, 0x7a, 0x8e, 0x05, 0xba, 0x68, 0x06, 0xff, + 0x8e, 0x06, 0x99, 0x08, 0x21, 0x70, 0x72, 0x32, 0xa1, 0x3e, 0xa8, + 0x02, 0x0c, 0x1b, 0x3a, 0xaa, 0xc0, 0x20, 0x00, 0x92, 0x0a, 0x80, + 0xb0, 0x99, 0x20, 0x8e, 0x07, 0x82, 0x93, 0x2d, 0x4a, 0x80, 0x82, + 0x28, 0x15, 0xa2, 0xa0, 0xc8, 0xe0, 0x08, 0x00, 0xc8, 0x02, 0xd2, + 0xa0, 0xfe, 0x3a, 0xcc, 0xc0, 0x20, 0x00, 0xb2, 0x0c, 0x8e, 0x07, + 0x83, 0x6f, 0xb2, 0x4c, 0x80, 0x8e, 0x08, 0x81, 0xea, 0x6c, 0x8e, + 0x06, 0x82, 0x8f, 0x55, 0x22, 0xd5, 0x20, 0x20, 0x44, 0x8e, 0x09, + 0x86, 0x28, 0x75, 0x72, 0xe0, 0x08, 0x00, 0x91, 0x71, 0x72, 0x88, + 0x09, 0x56, 0xe8, 0x08, 0x0c, 0x15, 0x59, 0x09, 0x41, 0xce, 0x70, + 0x51, 0x72, 0x72, 0xc0, 0x20, 0x00, 0x32, 0x25, 0x11, 0x40, 0x33, + 0x8e, 0x04, 0x64, 0x32, 0x65, 0x11, 0xc0, 0x20, 0x00, 0x0c, 0x03, + 0x41, 0x00, 0x70, 0x72, 0xa1, 0x27, 0x7c, 0xf6, 0x22, 0x25, 0x11, + 0x0c, 0x02, 0x91, 0x70, 0x72, 0x98, 0x09, 0x7a, 0x99, 0xc0, 0x20, + 0x00, 0x62, 0x49, 0x80, 0x82, 0x24, 0x8e, 0x07, 0x81, 0x00, 0xa5, + 0xf9, 0xff, 0x1c, 0x1c, 0xc7, 0x9a, 0x07, 0xe5, 0xf4, 0xff, 0x1b, + 0x22, 0x46, 0x05, 0x00, 0x1c, 0x2b, 0xb7, 0x1a, 0x7f, 0xbd, 0x0a, + 0xc0, 0x20, 0x00, 0xc2, 0x25, 0x19, 0x82, 0x24, 0x5e, 0xa1, 0x73, + 0x8e, 0x04, 0x6f, 0x3c, 0x19, 0x27, 0x29, 0x32, 0x3c, 0x2a, 0xbd, + 0x03, 0x1b, 0x33, 0xb7, 0xaa, 0xb5, 0x86, 0x09, 0x00, 0x7c, 0xde, + 0xc0, 0x20, 0x00, 0xd2, 0x22, 0x8b, 0xe0, 0x8e, 0x06, 0xa9, 0x02, + 0x62, 0x8b, 0x8e, 0x04, 0x31, 0x22, 0x8b, 0x0c, 0xca, 0x81, 0x74, + 0x72, 0x0c, 0x1b, 0x88, 0x48, 0x8e, 0x07, 0x9c, 0x0b, 0x0c, 0x19, + 0x21, 0xb0, 0x70, 0x66, 0x19, 0xd0, 0x8e, 0x10, 0x7c, 0x0c, 0x5a, + 0xe0, 0x08, 0x00, 0x0c, 0x2c, 0xc0, 0x20, 0x00, 0xb2, 0x22, 0x8b, + 0x8e, 0x07, 0x81, 0x90, 0x39, 0x8e, 0x05, 0x44, 0xa2, 0x22, 0x8b, + 0x06, 0xee, 0xff, 0x0c, 0x09, 0x46, 0xf1, 0x8e, 0x05, 0xc7, 0x58, + 0xb0, 0x70, 0x0c, 0x5e, 0xb1, 0x7e, 0x70, 0x31, 0x8e, 0x05, 0x81, + 0x5f, 0xe2, 0x63, 0x18, 0x0c, 0x1d, 0x8e, 0x04, 0x6e, 0x6b, 0xd3, + 0x0c, 0x9c, 0xc0, 0x20, 0x00, 0xc2, 0x6b, 0xd4, 0x0c, 0x0a, 0xc0, + 0x20, 0x00, 0xa2, 0x6b, 0xcd, 0x91, 0x16, 0x8e, 0x04, 0x21, 0x52, + 0x28, 0x82, 0x90, 0x55, 0x8e, 0x04, 0x4c, 0x52, 0x68, 0x82, 0x41, + 0x70, 0x8e, 0x05, 0x82, 0x3b, 0x23, 0x19, 0x40, 0x22, 0x8e, 0x04, + 0x12, 0x22, 0x63, 0x8e, 0x05, 0xb8, 0x61, 0x36, 0x41, 0x00, 0x31, + 0x71, 0x72, 0x81, 0x76, 0x72, 0x51, 0x60, 0x8e, 0x04, 0x31, 0x0c, + 0x14, 0x52, 0x25, 0xbc, 0x0c, 0x02, 0x80, 0x55, 0xc0, 0x50, 0x24, + 0x83, 0x8e, 0x07, 0x89, 0x50, 0x21, 0x71, 0x72, 0x88, 0x22, 0x8e, + 0x05, 0x9f, 0x7b, 0x0c, 0x66, 0x19, 0x17, 0x8e, 0x05, 0xb5, 0x1e, + 0x15, 0xa2, 0xa3, 0xe8, 0x8e, 0x04, 0x12, 0xa1, 0x00, 0xa1, 0x8e, + 0x05, 0x37, 0x92, 0x6a, 0x80, 0x8e, 0x09, 0x81, 0xf5, 0x18, 0x82, + 0x28, 0x56, 0xa1, 0x71, 0x72, 0x88, 0x18, 0x98, 0x1a, 0x66, 0x68, + 0x07, 0x66, 0x19, 0x04, 0xe5, 0xe5, 0xff, 0x1d, 0xf0, 0x88, 0x8e, + 0x07, 0x81, 0xd1, 0x63, 0x8e, 0x05, 0x24, 0x77, 0x72, 0x82, 0x08, + 0x00, 0xa1, 0x71, 0x72, 0x80, 0x80, 0x34, 0xdc, 0xd8, 0x0c, 0x0d, + 0xd9, 0x0a, 0xc1, 0xcd, 0x70, 0x91, 0x8e, 0x05, 0x81, 0x51, 0xb2, + 0x29, 0x11, 0xc0, 0x8e, 0x06, 0x83, 0x72, 0x69, 0x11, 0xc0, 0x20, + 0x00, 0x92, 0x29, 0x11, 0x88, 0x5a, 0x8e, 0x0d, 0x89, 0x38, 0x31, + 0x00, 0x70, 0x92, 0x23, 0x56, 0x98, 0x19, 0x21, 0x71, 0x72, 0x26, + 0x59, 0x02, 0x66, 0x69, 0x14, 0xa1, 0x68, 0x72, 0x91, 0x78, 0x72, + 0x82, 0x23, 0x52, 0x98, 0x09, 0x99, 0x62, 0xe0, 0x08, 0x00, 0x8e, + 0x05, 0x1e, 0xb1, 0x79, 0x72, 0xa1, 0x7a, 0x72, 0xd1, 0x7b, 0x72, + 0xc2, 0x2a, 0x12, 0xe2, 0x2a, 0x23, 0xe9, 0x42, 0xd2, 0x6a, 0x23, + 0xc9, 0x52, 0xb2, 0x6a, 0x12, 0x66, 0x69, 0x15, 0xf1, 0x7c, 0x72, + 0x82, 0x2a, 0x24, 0x89, 0x22, 0xf2, 0x6a, 0x24, 0x25, 0xf0, 0xff, + 0x98, 0x12, 0x66, 0x19, 0x02, 0x65, 0xea, 0x8e, 0x09, 0x81, 0xf8, + 0x5c, 0xcc, 0x32, 0x0c, 0x09, 0x86, 0x00, 0x00, 0x92, 0x22, 0x1b, + 0x51, 0xb5, 0x70, 0xcc, 0x39, 0x0c, 0x0a, 0x86, 0x00, 0x00, 0xa2, + 0x29, 0x95, 0x8c, 0xc9, 0x8c, 0xaa, 0xad, 0x02, 0x8e, 0x06, 0x81, + 0x9c, 0x6c, 0x5c, 0x8e, 0x06, 0xa4, 0x31, 0x7d, 0x8e, 0x0a, 0x8e, + 0x39, 0x8e, 0x0a, 0x35, 0x8e, 0x11, 0x32, 0x8e, 0x05, 0xe5, 0x0b, + 0x8e, 0x04, 0x32, 0x8e, 0x08, 0x85, 0x40, 0x7f, 0x72, 0x31, 0x7e, + 0x72, 0x81, 0x7d, 0x72, 0x48, 0x43, 0x49, 0x08, 0x8e, 0x05, 0x91, + 0x5c, 0x61, 0x00, 0x0c, 0x1c, 0xa1, 0x24, 0x70, 0x42, 0x22, 0x23, + 0xa2, 0x0a, 0x00, 0xbd, 0x03, 0x16, 0xea, 0x04, 0x0c, 0x0e, 0x0c, + 0x03, 0x0c, 0x0f, 0x0c, 0x06, 0x52, 0xa0, 0x00, 0x91, 0x20, 0x70, + 0x76, 0x9a, 0x3a, 0xa8, 0x09, 0xd2, 0x0a, 0x03, 0x4b, 0x99, 0x66, + 0x3d, 0x2e, 0xa2, 0x2a, 0x18, 0xac, 0x8a, 0xd8, 0x4a, 0xd8, 0x2d, + 0xa2, 0x2a, 0x23, 0xd0, 0xd0, 0x14, 0xd0, 0xec, 0x83, 0x26, 0x1d, + 0x05, 0x26, 0x3d, 0x02, 0x66, 0x2d, 0x01, 0x0c, 0x13, 0x66, 0x2a, + 0x04, 0x0c, 0x15, 0x46, 0x02, 0x00, 0x66, 0x3a, 0x04, 0x8e, 0x06, + 0xc9, 0x5e, 0x1f, 0x3d, 0xf0, 0x46, 0x02, 0x00, 0x0c, 0x05, 0x0c, + 0x06, 0x0c, 0x0f, 0x0c, 0x03, 0x0c, 0x0e, 0x71, 0x81, 0x72, 0x8c, + 0x53, 0x8c, 0x3e, 0x0c, 0x1d, 0x86, 0x02, 0x00, 0x0c, 0x1d, 0x6a, + 0xe5, 0xea, 0xef, 0xe6, 0x2e, 0x01, 0x0c, 0x0d, 0x0c, 0x03, 0x61, + 0x72, 0x71, 0x51, 0x00, 0x70, 0xe1, 0x80, 0x72, 0x16, 0xeb, 0x19, + 0xb8, 0x0e, 0xdc, 0x1b, 0x66, 0x1d, 0x0d, 0x0c, 0x1a, 0x9d, 0x0e, + 0x88, 0x37, 0xc9, 0x09, 0x8e, 0x06, 0x81, 0xb0, 0x60, 0x8c, 0xab, + 0x8b, 0xa4, 0xb8, 0x27, 0x82, 0x25, 0x70, 0xcd, 0x8e, 0x05, 0xa4, + 0x04, 0x22, 0x11, 0x17, 0xf9, 0x3b, 0xad, 0x04, 0x0c, 0x0b, 0x82, + 0x25, 0x3e, 0x4c, 0x4c, 0x8e, 0x06, 0xc2, 0x3c, 0x0b, 0x99, 0x56, + 0xf9, 0x15, 0xb8, 0x17, 0xcd, 0x02, 0x32, 0xc4, 0x20, 0x82, 0x25, + 0x70, 0x8e, 0x07, 0xb9, 0x60, 0x82, 0x25, 0x8e, 0x07, 0x81, 0xbc, + 0x68, 0x8b, 0xa4, 0xb8, 0x07, 0x8e, 0x08, 0x40, 0x1d, 0xf0, 0x92, + 0x02, 0x03, 0x92, 0xc9, 0xfb, 0x56, 0xe9, 0x12, 0x82, 0x25, 0x6e, + 0x8b, 0xa4, 0x8e, 0x05, 0xc2, 0x14, 0xc8, 0xe4, 0x31, 0x71, 0x71, + 0x9c, 0x3c, 0xad, 0x02, 0xd2, 0x02, 0x32, 0x2c, 0xcb, 0xd0, 0xbb, + 0xd1, 0x88, 0x06, 0xba, 0xb3, 0xb8, 0x0b, 0xe0, 0x08, 0x00, 0x79, + 0xe4, 0xe2, 0x04, 0x04, 0x07, 0x6e, 0x55, 0xf8, 0x04, 0x16, 0x0f, + 0x05, 0x8e, 0x09, 0xca, 0x54, 0x19, 0x1c, 0x3c, 0x8e, 0x05, 0xa2, + 0x35, 0x8e, 0x0b, 0xa9, 0x77, 0xd8, 0x8e, 0x07, 0x81, 0x8d, 0x79, + 0xb2, 0x02, 0x32, 0x2c, 0xca, 0xb0, 0xaa, 0xd1, 0x82, 0x28, 0x38, + 0xaa, 0xa3, 0x8e, 0x05, 0x92, 0x44, 0x8e, 0x09, 0xc8, 0x72, 0x8e, + 0x06, 0x36, 0x92, 0x04, 0x04, 0xa2, 0xa0, 0xfe, 0xa0, 0x99, 0x10, + 0x92, 0x44, 0x04, 0x82, 0x25, 0x6e, 0xa2, 0xc4, 0x20, 0xe0, 0x08, + 0x00, 0xc8, 0xf4, 0x8e, 0x11, 0x7e, 0x4b, 0x8e, 0x04, 0x7e, 0xf4, + 0x92, 0x04, 0x04, 0x17, 0x69, 0x20, 0x8e, 0x05, 0x58, 0x31, 0x8e, + 0x0b, 0x58, 0x4a, 0x8e, 0x08, 0x49, 0xfd, 0x8e, 0x06, 0x49, 0xc2, + 0x24, 0x10, 0x9c, 0x7c, 0x8e, 0x0f, 0x41, 0x5b, 0x8e, 0x06, 0x24, + 0x72, 0x64, 0x10, 0x27, 0x69, 0x42, 0x8e, 0x11, 0x42, 0x5a, 0xe0, + 0x08, 0x00, 0xc2, 0x04, 0x04, 0xd2, 0xa0, 0xfb, 0xd0, 0xcc, 0x10, + 0xc2, 0x44, 0x04, 0x1d, 0xf0, 0x8e, 0x09, 0x81, 0x0d, 0x92, 0x04, + 0x05, 0x9c, 0x09, 0x8e, 0x0a, 0xca, 0x28, 0x8e, 0x05, 0x81, 0x36, + 0x32, 0x44, 0x05, 0x8e, 0x07, 0xba, 0x7c, 0xf7, 0xb8, 0x0e, 0x66, + 0x1b, 0x27, 0xa2, 0x22, 0x11, 0xd9, 0x01, 0x17, 0x7a, 0x0e, 0x8e, + 0x08, 0x82, 0x44, 0xe1, 0x80, 0x72, 0xd8, 0x01, 0x8e, 0x04, 0x1a, + 0x0d, 0xcc, 0xad, 0x0c, 0x0a, 0x91, 0x80, 0x72, 0x88, 0x37, 0x39, + 0x09, 0x8e, 0x06, 0xaf, 0x3d, 0x8b, 0x88, 0x46, 0x8e, 0x08, 0xaf, + 0x53, 0x9b, 0x8e, 0x0a, 0x0b, 0x0b, 0x8e, 0x0a, 0x0b, 0x4b, 0x8e, + 0x0a, 0x0b, 0x5b, 0x8e, 0x07, 0x0b, 0x92, 0x22, 0x11, 0x90, 0x91, + 0x05, 0x16, 0xa9, 0xf6, 0x8e, 0x05, 0xc6, 0x33, 0x17, 0x8e, 0x06, + 0x81, 0xa5, 0x1e, 0x8e, 0x09, 0x87, 0x08, 0x72, 0x71, 0x21, 0x82, + 0x72, 0x29, 0x53, 0x8e, 0x08, 0x10, 0x91, 0x12, 0x70, 0x98, 0x09, + 0x82, 0x29, 0x9f, 0xa2, 0x29, 0xa0, 0xcc, 0x08, 0xcc, 0x2a, 0x0c, + 0x22, 0x1d, 0xf0, 0x81, 0x83, 0x8e, 0x0f, 0x98, 0x4f, 0x8e, 0x17, + 0x28, 0xcd, 0x04, 0x8e, 0x06, 0x2a, 0x8e, 0x0a, 0x98, 0x65, 0x8e, + 0x1e, 0x28, 0x8e, 0x0a, 0x92, 0x13, 0x36, 0x41, 0x00, 0x81, 0x83, + 0x8e, 0x08, 0x98, 0x78, 0x92, 0x22, 0x11, 0x7c, 0xeb, 0xb0, 0x99, + 0x10, 0x92, 0x62, 0x11, 0x8e, 0x07, 0x1c, 0x21, 0x89, 0x72, 0x41, + 0x88, 0x72, 0x81, 0x87, 0x72, 0xe1, 0x85, 0x72, 0xa1, 0x83, 0x72, + 0x31, 0x86, 0x72, 0xf1, 0x84, 0x72, 0x92, 0x23, 0x43, 0xb2, 0x23, + 0x40, 0xc2, 0x23, 0x3d, 0xd8, 0x3f, 0xd9, 0x3a, 0xe9, 0x3f, 0x82, + 0x63, 0x3d, 0xc9, 0x0a, 0x42, 0x63, 0x40, 0xb9, 0x1a, 0x22, 0x63, + 0x43, 0x99, 0x2a, 0x8e, 0x05, 0x87, 0x10, 0x82, 0x22, 0x00, 0x66, + 0x08, 0x02, 0x86, 0x25, 0x8e, 0x06, 0x89, 0x4d, 0x5e, 0xa1, 0x8a, + 0x72, 0xe0, 0x08, 0x00, 0xa1, 0x8d, 0x72, 0xa0, 0xae, 0x15, 0x20, + 0xaa, 0x01, 0xe0, 0x90, 0x11, 0x90, 0x92, 0x41, 0xa0, 0x99, 0x20, + 0x99, 0x01, 0x65, 0x61, 0x01, 0xb2, 0xc1, 0xf0, 0x78, 0x0b, 0xb1, + 0x8e, 0x00, 0x72, 0xb0, 0xbe, 0x15, 0x20, 0xbb, 0x01, 0xe0, 0x77, + 0x11, 0x70, 0x72, 0x41, 0xb0, 0x77, 0x20, 0xe5, 0x5f, 0x01, 0xc2, + 0xc1, 0xf4, 0xc8, 0x0c, 0xc2, 0xcc, 0xf0, 0x68, 0x0c, 0xc1, 0x8f, + 0x72, 0xc0, 0xce, 0x15, 0x20, 0xcc, 0x01, 0xe0, 0x66, 0x11, 0x60, + 0x62, 0x41, 0xc0, 0x66, 0x20, 0xe5, 0x5d, 0x01, 0xdd, 0x06, 0xcd, + 0x07, 0xb8, 0x01, 0x81, 0x00, 0x70, 0x92, 0xc1, 0xf4, 0x98, 0x09, + 0x92, 0xc9, 0x8e, 0x05, 0x05, 0xf0, 0xe8, 0x09, 0x91, 0x90, 0x72, + 0x90, 0x9e, 0x15, 0x20, 0x99, 0x01, 0xe0, 0xee, 0x11, 0xe0, 0xe2, + 0x41, 0x90, 0xee, 0x20, 0x82, 0x28, 0x5e, 0xa1, 0x8b, 0x72, 0x8e, + 0x06, 0x85, 0x45, 0x91, 0x8e, 0x04, 0x08, 0x8e, 0x05, 0x93, 0x56, + 0x8c, 0x8e, 0x12, 0xad, 0x04, 0x21, 0x92, 0x72, 0x31, 0x00, 0x70, + 0x81, 0x8c, 0x72, 0x42, 0x23, 0x6d, 0x49, 0x08, 0x22, 0x63, 0x6d, + 0x8e, 0x08, 0x8a, 0x70, 0x93, 0x8e, 0x12, 0x2c, 0xcd, 0x04, 0x8e, + 0x06, 0x16, 0x8e, 0x0c, 0x82, 0xc0, 0x14, 0x8e, 0x09, 0x81, 0x8d, + 0x38, 0x8e, 0x05, 0x1a, 0x8e, 0x0e, 0x9b, 0x60, 0x41, 0x95, 0x72, + 0x81, 0x94, 0x72, 0x21, 0x96, 0x72, 0x31, 0x6a, 0x71, 0xa1, 0x93, + 0x72, 0xb8, 0x23, 0xc8, 0x53, 0x98, 0x13, 0x99, 0x1a, 0x29, 0x13, + 0xc9, 0x2a, 0x89, 0x53, 0xb9, 0x0a, 0x49, 0x23, 0x8e, 0x09, 0xe6, + 0x08, 0x41, 0x97, 0x72, 0x0c, 0x0b, 0x88, 0x24, 0x8e, 0x05, 0x84, + 0x43, 0x3d, 0x0a, 0x9c, 0xaa, 0xad, 0x02, 0xbd, 0x03, 0x88, 0x34, + 0x8e, 0x05, 0xb4, 0x3b, 0x8c, 0xda, 0xbd, 0x03, 0x8e, 0x07, 0x81, + 0x86, 0x56, 0x17, 0x8e, 0x07, 0x8e, 0x0f, 0x8e, 0x05, 0x34, 0x03, + 0x65, 0xfc, 0xff, 0x32, 0xa0, 0x8c, 0x41, 0x98, 0x72, 0x8e, 0x0f, + 0x81, 0xe9, 0x2c, 0xd8, 0x04, 0xc2, 0xa3, 0xe8, 0x3a, 0xad, 0xd2, + 0x1d, 0x06, 0x82, 0x22, 0x6d, 0xd0, 0xcc, 0xc1, 0x8e, 0x0a, 0xb4, + 0x28, 0x21, 0x98, 0x72, 0x88, 0x02, 0xdc, 0xe8, 0x31, 0x00, 0x70, + 0x0c, 0x0a, 0x88, 0x43, 0xb2, 0xa0, 0xbc, 0xe0, 0x08, 0x00, 0xa9, + 0x02, 0x8e, 0x06, 0x84, 0x29, 0x8e, 0x05, 0xe1, 0x5c, 0xc2, 0x8e, + 0x05, 0x13, 0x8e, 0x09, 0x97, 0x74, 0x51, 0x97, 0x72, 0x88, 0x75, + 0x8e, 0x07, 0x81, 0x52, 0xcc, 0x2a, 0x8e, 0x04, 0x17, 0x1c, 0x1c, + 0x88, 0x65, 0xb2, 0xc4, 0x1c, 0x58, 0x4a, 0xb0, 0xb0, 0xf4, 0x8e, + 0x05, 0xc0, 0x7e, 0xe1, 0x98, 0x72, 0xf1, 0x7c, 0x70, 0xe8, 0x0e, + 0x7d, 0x0a, 0x82, 0x1e, 0x03, 0xa2, 0x25, 0x26, 0x80, 0x68, 0x74, + 0x80, 0x80, 0x74, 0x80, 0x88, 0x11, 0x80, 0x66, 0x20, 0x62, 0x5a, + 0x0e, 0xe2, 0x1e, 0x04, 0xf2, 0x5a, 0x10, 0xe0, 0xd8, 0x74, 0xe0, + 0xe0, 0x74, 0x80, 0xee, 0x11, 0xe0, 0xdd, 0x20, 0xd2, 0x5a, 0x0f, + 0xc2, 0x15, 0x4b, 0x62, 0xca, 0x1c, 0x8b, 0xcc, 0xc2, 0x45, 0x96, + 0xc0, 0xc8, 0x41, 0xc2, 0x45, 0x97, 0x9c, 0xc4, 0xbd, 0x03, 0x81, + 0x00, 0x70, 0xa2, 0xca, 0x24, 0x82, 0x28, 0x3f, 0x8e, 0x05, 0xdf, + 0x45, 0x92, 0x15, 0x4b, 0x4a, 0x99, 0x92, 0x45, 0x8e, 0x05, 0xfc, + 0x7e, 0x45, 0x97, 0xad, 0x07, 0x0c, 0x2c, 0x4a, 0xb4, 0xb2, 0xcb, + 0x1c, 0xb0, 0xb0, 0xf4, 0x81, 0x99, 0x72, 0xe0, 0x08, 0x00, 0xa0, + 0xc8, 0x74, 0xa0, 0xd0, 0x74, 0x8e, 0x06, 0x81, 0x88, 0x65, 0xc2, + 0x56, 0x8e, 0x08, 0x81, 0xb0, 0x64, 0x40, 0x50, 0x74, 0xb8, 0x43, + 0x82, 0xa0, 0x9c, 0xa2, 0x2b, 0x26, 0x8a, 0x8b, 0xa2, 0xca, 0xf2, + 0x87, 0xba, 0x0a, 0x8e, 0x0b, 0xc7, 0x3f, 0x0c, 0x6c, 0x61, 0x00, + 0x70, 0xa2, 0x4b, 0x98, 0x92, 0x1b, 0x4b, 0x8e, 0x05, 0xb4, 0x5f, + 0x99, 0xeb, 0x99, 0x92, 0x4b, 0x96, 0x8e, 0x05, 0x0b, 0x9a, 0x8e, + 0x04, 0x68, 0x4b, 0x97, 0x8e, 0x05, 0x0c, 0x9b, 0x82, 0x26, 0x3f, + 0x42, 0x2b, 0x26, 0x4b, 0xb2, 0x6b, 0xa4, 0xe0, 0x08, 0x00, 0xa1, + 0x98, 0x72, 0xd2, 0x26, 0x3f, 0xa8, 0x0a, 0x66, 0x15, 0x0c, 0xb2, + 0xca, 0x26, 0x0c, 0x6c, 0xad, 0x04, 0xe0, 0x0d, 0x00, 0x46, 0x02, + 0x00, 0xb2, 0xca, 0x20, 0x8e, 0x07, 0x0d, 0xbd, 0x03, 0x81, 0xa3, + 0x71, 0x0c, 0x8a, 0x0c, 0x09, 0x92, 0x44, 0x0d, 0xa2, 0x44, 0x0c, + 0x8e, 0x07, 0x84, 0x1d, 0x7c, 0xfb, 0x0c, 0x02, 0xa0, 0x2b, 0x8e, + 0x05, 0xed, 0x18, 0xe1, 0x00, 0x81, 0x03, 0x70, 0xa8, 0x43, 0x82, + 0x28, 0xb3, 0x61, 0x98, 0x72, 0xc7, 0xf8, 0x03, 0x0c, 0x12, 0x1d, + 0xf0, 0xb8, 0x06, 0x92, 0x0b, 0x70, 0xc2, 0x0b, 0x71, 0xcc, 0x19, + 0x16, 0xec, 0xfe, 0xb1, 0x9a, 0x72, 0x0c, 0x6c, 0x72, 0x1a, 0x4e, + 0x41, 0x00, 0x70, 0x32, 0x2a, 0x26, 0x82, 0x24, 0x41, 0xeb, 0x53, + 0x2d, 0x05, 0x8e, 0x05, 0x82, 0x5e, 0x59, 0xf1, 0xbc, 0xba, 0x0c, + 0x6c, 0x82, 0x24, 0x41, 0xb8, 0x06, 0x92, 0x13, 0x06, 0x6b, 0xa3, + 0xa9, 0xe1, 0x90, 0x58, 0x74, 0xb2, 0xcb, 0x20, 0x90, 0x90, 0x74, + 0x80, 0x99, 0x11, 0x90, 0x55, 0x20, 0x80, 0x55, 0x23, 0xe0, 0x08, + 0x00, 0x16, 0x0a, 0x0c, 0xa8, 0xe1, 0x0c, 0x6c, 0xb8, 0x06, 0x82, + 0x24, 0x41, 0xb2, 0xcb, 0x26, 0x8e, 0x04, 0x12, 0xea, 0x0a, 0x0c, + 0x09, 0x46, 0x00, 0x00, 0x0c, 0x09, 0xa2, 0x02, 0x06, 0xb2, 0x02, + 0x07, 0x66, 0x8a, 0x88, 0x99, 0xd1, 0xec, 0xfb, 0x1c, 0x4b, 0x8b, + 0xa2, 0xc2, 0x12, 0x09, 0xc2, 0x61, 0x11, 0xa2, 0x61, 0x10, 0x0c, + 0x0c, 0x8e, 0x06, 0x82, 0x4c, 0xb2, 0x21, 0x11, 0xc2, 0x21, 0x10, + 0xb0, 0xd8, 0x74, 0xb0, 0x8e, 0x08, 0x83, 0x24, 0xd0, 0xd0, 0xf4, + 0xd7, 0x1a, 0x78, 0x0c, 0x02, 0x1d, 0xf0, 0xf2, 0xca, 0xf8, 0x56, + 0xdf, 0xf4, 0x82, 0xcb, 0xfa, 0x56, 0x78, 0xf4, 0xad, 0x01, 0x8e, + 0x05, 0xf5, 0x0d, 0x3c, 0x8e, 0x07, 0x89, 0x72, 0x1c, 0xdd, 0x01, + 0x51, 0x9b, 0x72, 0xb2, 0xc7, 0xf2, 0x88, 0x05, 0xb0, 0xb0, 0xf4, + 0x8e, 0x04, 0x74, 0xaa, 0x12, 0xc8, 0xb1, 0x0b, 0xcc, 0x56, 0xec, + 0x11, 0xb8, 0x06, 0xa8, 0x11, 0xf1, 0xf6, 0x70, 0xa0, 0xc8, 0x75, + 0xa0, 0xe8, 0x41, 0xf0, 0xda, 0x10, 0xf0, 0xee, 0x10, 0xe0, 0xcc, + 0x20, 0x80, 0xdd, 0x11, 0x80, 0xaa, 0x01, 0xd0, 0xaa, 0x20, 0xd8, + 0x4b, 0xc0, 0xaa, 0x20, 0xa7, 0x1d, 0x07, 0x88, 0x6b, 0xa0, 0x88, + 0xc0, 0x56, 0x38, 0x16, 0x0c, 0x19, 0x46, 0x3c, 0x00, 0x28, 0xf1, + 0x0c, 0x19, 0x3d, 0x02, 0xeb, 0x22, 0x06, 0xd3, 0xff, 0x92, 0x02, + 0x11, 0xb2, 0x52, 0x09, 0x66, 0x69, 0x42, 0x88, 0xd1, 0x21, 0x97, + 0x72, 0x0b, 0x88, 0x56, 0x58, 0x13, 0xe6, 0x15, 0x02, 0x46, 0xdb, + 0xff, 0xb2, 0x13, 0x06, 0x88, 0x82, 0xb0, 0xa8, 0x74, 0xb0, 0xb0, + 0x74, 0x8e, 0x09, 0x81, 0x8f, 0x78, 0xaa, 0x33, 0xa0, 0x55, 0xc0, + 0x52, 0xc5, 0xf2, 0x80, 0x55, 0x23, 0xa0, 0xa3, 0xc0, 0xa2, 0xca, + 0x16, 0xe0, 0x08, 0x00, 0x0b, 0xca, 0x16, 0x8c, 0xe9, 0xe6, 0x15, + 0xcf, 0x86, 0xce, 0xff, 0xd2, 0xc9, 0xef, 0x56, 0xcd, 0xe8, 0x32, + 0x12, 0x11, 0xad, 0x0c, 0xb1, 0x9c, 0x72, 0x0c, 0x2c, 0xba, 0xb7, + 0x8e, 0x15, 0x84, 0x44, 0xc0, 0xc0, 0xf4, 0xc0, 0xc3, 0xc0, 0x56, + 0xac, 0xf0, 0x32, 0x52, 0x11, 0x82, 0x12, 0x0e, 0xb8, 0x06, 0x80, + 0xf8, 0x74, 0xe2, 0x1b, 0x04, 0x8e, 0x07, 0x85, 0x4b, 0xff, 0x20, + 0xf0, 0xf0, 0xf4, 0xf0, 0xee, 0xc0, 0x56, 0x2e, 0xe4, 0xc2, 0x12, + 0x0f, 0x92, 0x1b, 0x03, 0xc0, 0xa8, 0x74, 0xc0, 0xc0, 0x74, 0x80, + 0xcc, 0x8e, 0x05, 0xc4, 0x14, 0xa0, 0xf4, 0xa0, 0x99, 0xc0, 0x56, + 0x79, 0xe2, 0xc2, 0x0b, 0x6f, 0x9c, 0x4c, 0xa2, 0xcb, 0x4f, 0xb2, + 0x0b, 0x4e, 0x82, 0x24, 0x41, 0x2a, 0xbb, 0xb2, 0xcb, 0x24, 0xe0, + 0x08, 0x00, 0x56, 0x6a, 0xeb, 0xb8, 0x06, 0xa2, 0xa0, 0x8c, 0x82, + 0x24, 0x6e, 0xaa, 0xab, 0xe0, 0x08, 0x00, 0x86, 0xa9, 0xff, 0x8e, + 0x07, 0x83, 0x1a, 0x0b, 0xb9, 0x56, 0x2b, 0xdf, 0xb2, 0xc1, 0x30, + 0x21, 0x20, 0x70, 0x61, 0x03, 0x71, 0x28, 0x02, 0x88, 0x36, 0x8e, + 0x05, 0x84, 0x3a, 0x3d, 0x0a, 0x16, 0x3a, 0xe8, 0xa2, 0xc1, 0x22, + 0x4b, 0xb2, 0x0c, 0x6c, 0x98, 0xc1, 0x82, 0x24, 0x3f, 0x42, 0x29, + 0x26, 0x8e, 0x06, 0xc7, 0x40, 0x01, 0x88, 0x15, 0x8e, 0x05, 0x88, + 0x12, 0xbd, 0x03, 0x0c, 0x0c, 0xd2, 0xc1, 0x1c, 0xe2, 0xa6, 0x08, + 0x88, 0x06, 0xf8, 0xc1, 0x4d, 0x0a, 0x92, 0x1f, 0x4b, 0xad, 0x02, + 0x4a, 0x99, 0x92, 0x4f, 0x8e, 0x05, 0x86, 0x2f, 0x4f, 0x97, 0x8e, + 0x04, 0x76, 0x0a, 0xe4, 0x46, 0x65, 0xff, 0x88, 0x82, 0x8e, 0x05, + 0xea, 0x5e, 0x8e, 0x06, 0x81, 0x85, 0x6f, 0x46, 0xe3, 0x8e, 0x05, + 0xa7, 0x70, 0x8e, 0x08, 0x81, 0x92, 0x0c, 0x91, 0x98, 0x72, 0xe2, + 0xa0, 0xf0, 0x30, 0xa8, 0x74, 0xb2, 0x22, 0x26, 0x0c, 0x0f, 0x30, + 0x8e, 0x05, 0x81, 0x4e, 0xf2, 0x5b, 0x06, 0xf2, 0x5b, 0x07, 0xf2, + 0x4b, 0x09, 0x42, 0x4b, 0x11, 0xc0, 0xaa, 0x20, 0xd2, 0x0b, 0x08, + 0xa2, 0x5b, 0x05, 0xa1, 0xf6, 0x70, 0xe0, 0xdd, 0x10, 0x8b, 0x3b, + 0x0c, 0x5e, 0x8e, 0x05, 0x84, 0x1a, 0x34, 0x4c, 0x0e, 0xe2, 0x4b, + 0x10, 0xe0, 0xdd, 0x20, 0xd2, 0x4b, 0x08, 0x98, 0x09, 0x66, 0x64, + 0x6b, 0x88, 0x49, 0xa0, 0xd8, 0x10, 0x80, 0xc8, 0x01, 0x8e, 0x06, + 0x82, 0x49, 0x80, 0xd8, 0x75, 0x80, 0x88, 0x41, 0xa0, 0x88, 0x10, + 0xd0, 0x88, 0x20, 0xc0, 0x88, 0x20, 0x89, 0x5b, 0xf8, 0x59, 0xa0, + 0xef, 0x10, 0x80, 0xdf, 0x01, 0xf0, 0xc8, 0x75, 0x80, 0x8e, 0x00, + 0x11, 0xf0, 0xe8, 0x41, 0x80, 0x8d, 0x20, 0xa0, 0xee, 0x10, 0xad, + 0x03, 0xc0, 0xee, 0x20, 0x80, 0xee, 0x20, 0x0c, 0x0c, 0xe9, 0x6b, + 0x1c, 0x4b, 0x8e, 0x07, 0x83, 0x11, 0xb8, 0x74, 0xa0, 0xa0, 0x74, + 0x80, 0xaa, 0x11, 0xa0, 0xab, 0x20, 0xa2, 0x53, 0x05, 0x92, 0x12, + 0x4b, 0x92, 0xc9, 0x14, 0x92, 0x42, 0x8e, 0x05, 0x81, 0x56, 0x42, + 0x97, 0x2d, 0x03, 0x1d, 0xf0, 0xc8, 0x69, 0xa0, 0xec, 0x10, 0x80, + 0xdc, 0x01, 0x8e, 0x06, 0x85, 0x27, 0xc0, 0xe8, 0x75, 0xc0, 0xc8, + 0x41, 0xa0, 0xcc, 0x10, 0xe0, 0xcc, 0x20, 0xd0, 0xcc, 0x20, 0xc9, + 0x5b, 0xf8, 0x79, 0x8e, 0x09, 0x6c, 0x8e, 0x07, 0x81, 0x9b, 0x18, + 0x81, 0x00, 0x32, 0x12, 0x12, 0xad, 0x02, 0x0c, 0x1c, 0x72, 0x12, + 0x0c, 0x62, 0x12, 0x0e, 0x92, 0x12, 0x0f, 0xb2, 0x12, 0x0d, 0x41, + 0x20, 0x70, 0x82, 0x12, 0x01, 0x48, 0x04, 0x80, 0x58, 0x74, 0x00, + 0xbb, 0x11, 0x00, 0x99, 0x11, 0x9a, 0x66, 0xba, 0x77, 0x8e, 0x07, + 0x83, 0x6b, 0x55, 0x20, 0x50, 0x50, 0xf4, 0xbd, 0x05, 0x8e, 0x06, + 0x81, 0x11, 0xb1, 0x98, 0x72, 0xa0, 0x98, 0x74, 0xd8, 0x0b, 0xa0, + 0xc0, 0x74, 0x82, 0x0d, 0x70, 0x80, 0xcc, 0x11, 0x66, 0x18, 0x69, + 0xc0, 0x99, 0x20, 0x90, 0x90, 0xf4, 0x97, 0x93, 0x60, 0xb2, 0x12, + 0x0a, 0xe2, 0x1d, 0x01, 0xb0, 0xf8, 0x74, 0xb0, 0x8e, 0x0b, 0x84, + 0x25, 0xf7, 0x9e, 0x48, 0xc2, 0x12, 0x0b, 0x92, 0x1d, 0x00, 0xc0, + 0xa8, 0x74, 0xc0, 0x8e, 0x06, 0x86, 0x4f, 0x8e, 0x05, 0x84, 0x22, + 0xa7, 0x99, 0x30, 0xf2, 0x02, 0x21, 0x37, 0x6f, 0x2a, 0x32, 0x52, + 0x12, 0x82, 0x02, 0x20, 0x6c, 0xc3, 0x80, 0x84, 0x41, 0xe0, 0xa8, + 0x11, 0x30, 0x38, 0xa0, 0x30, 0x30, 0x74, 0xa0, 0xa5, 0xc0, 0xa2, + 0xca, 0xec, 0xa0, 0xa0, 0xf4, 0xa6, 0x68, 0x11, 0x52, 0xc2, 0x28, + 0x2a, 0xe3, 0xe2, 0xce, 0x28, 0x86, 0x02, 0x00, 0x32, 0x52, 0x12, + 0x0c, 0x12, 0x1d, 0xf0, 0x0c, 0x0e, 0x0c, 0x05, 0xa9, 0x41, 0xf2, + 0x0d, 0x4d, 0x81, 0x00, 0x70, 0x9c, 0xcf, 0xcd, 0x0f, 0xa2, 0xcd, + 0x2d, 0xb2, 0x0d, 0x2c, 0x82, 0x28, 0x41, 0xea, 0xbb, 0xe0, 0x08, + 0x00, 0x8c, 0x8e, 0x05, 0x8b, 0x17, 0xa8, 0x41, 0xb2, 0x12, 0x0a, + 0xc2, 0x12, 0x0b, 0xfd, 0x05, 0xed, 0x06, 0xdd, 0x07, 0xa9, 0x11, + 0x21, 0x97, 0x72, 0x39, 0x01, 0x88, 0x12, 0x8e, 0x05, 0xa4, 0x6f, + 0x8e, 0x05, 0x8c, 0x4f, 0x04, 0xbd, 0x03, 0x88, 0x32, 0x8e, 0x05, + 0xe1, 0x7b, 0x8e, 0x08, 0x8c, 0x4f, 0x04, 0x8e, 0x08, 0x8c, 0x4f, + 0x32, 0xa0, 0xa4, 0x21, 0x98, 0x72, 0x81, 0x00, 0x70, 0xa8, 0x02, + 0x82, 0x28, 0x8e, 0x08, 0x8c, 0x44, 0xc2, 0xa3, 0xe8, 0xd8, 0x02, + 0x81, 0x00, 0x70, 0x3a, 0xad, 0xd2, 0x1d, 0x02, 0x82, 0x28, 0x8e, + 0x09, 0x8c, 0x47, 0xc6, 0xe3, 0xff, 0x36, 0x61, 0x00, 0x51, 0x00, + 0x70, 0xad, 0x01, 0xb1, 0x9d, 0x72, 0x0c, 0x7c, 0x8e, 0x04, 0x3a, + 0x8e, 0x05, 0xe0, 0x7b, 0xa8, 0x02, 0x82, 0x25, 0x6e, 0xa2, 0xca, + 0x74, 0xe0, 0x08, 0x00, 0xb2, 0xa0, 0x8c, 0x8e, 0x05, 0x0e, 0xba, + 0x8e, 0x05, 0xd9, 0x51, 0xa0, 0xa4, 0x8e, 0x0a, 0x0d, 0xe8, 0x02, + 0x0c, 0x0c, 0xc2, 0x4e, 0x70, 0xd8, 0x02, 0xc2, 0x4d, 0x71, 0xac, + 0x34, 0xbd, 0x03, 0x0c, 0xaa, 0x81, 0x03, 0x71, 0xdd, 0x01, 0x82, + 0x28, 0x10, 0x0c, 0x7e, 0xe0, 0x08, 0x00, 0xdc, 0x0a, 0x91, 0x1b, + 0x70, 0x98, 0x09, 0x98, 0x49, 0x66, 0x29, 0x07, 0x81, 0x1c, 0x70, + 0x88, 0x68, 0x8e, 0x09, 0xa7, 0x48, 0x03, 0x70, 0x4d, 0x05, 0x82, + 0x28, 0xb3, 0x51, 0x98, 0x72, 0xc7, 0x78, 0x5e, 0x91, 0x9e, 0x72, + 0x61, 0x00, 0x70, 0x97, 0x13, 0x57, 0xa1, 0x9f, 0x72, 0xb1, 0xa0, + 0x72, 0xa0, 0xa3, 0xc0, 0x16, 0xba, 0x0a, 0xb0, 0xb3, 0xc0, 0x16, + 0xdb, 0x07, 0xc1, 0xa1, 0x72, 0xd1, 0xa2, 0x72, 0xc0, 0xc3, 0xc0, + 0x16, 0x6c, 0x0d, 0xd7, 0x93, 0x34, 0xf8, 0x05, 0xe2, 0x04, 0x01, + 0xe2, 0x4f, 0x4e, 0xc2, 0x04, 0x00, 0xac, 0x6c, 0xf6, 0xcc, 0x24, + 0x3b, 0xb4, 0xa8, 0x05, 0x82, 0x26, 0x3f, 0xa2, 0xca, 0x4f, 0xe0, + 0x08, 0x00, 0xc8, 0x05, 0xb2, 0x04, 0x00, 0xb2, 0x4c, 0x6f, 0x1d, + 0xf0, 0xad, 0x02, 0x81, 0x8e, 0x05, 0x8e, 0x7f, 0x98, 0x8e, 0x07, + 0x8e, 0x5f, 0x92, 0x04, 0x00, 0x16, 0x99, 0xfe, 0x92, 0x04, 0x01, + 0x0c, 0x12, 0x66, 0x19, 0x1f, 0x0c, 0x0b, 0xd8, 0x05, 0xc2, 0xa3, + 0xe8, 0xa2, 0xcd, 0x74, 0xd2, 0x1d, 0x05, 0x82, 0x26, 0x6d, 0xd0, + 0xcc, 0xc1, 0xdd, 0x02, 0xe0, 0x08, 0x00, 0xe8, 0x05, 0x22, 0x4e, + 0x71, 0x92, 0x04, 0x01, 0x66, 0x29, 0xcc, 0xf8, 0x05, 0x22, 0x4f, + 0x70, 0x1d, 0xf0, 0x98, 0x05, 0x82, 0x04, 0x01, 0x82, 0x49, 0x2c, + 0xc2, 0x04, 0x00, 0x16, 0x7c, 0xfb, 0xf6, 0xcc, 0xb4, 0x8e, 0x09, + 0x70, 0x2d, 0x8e, 0x0a, 0x70, 0x4d, 0x1d, 0xf0, 0x82, 0x26, 0x3f, + 0xc8, 0x14, 0xa8, 0x05, 0xf2, 0x14, 0x04, 0xf2, 0x5a, 0x00, 0xe2, + 0x14, 0x05, 0xe2, 0x5a, 0x01, 0xd2, 0x14, 0x06, 0xd2, 0x5a, 0x02, + 0xc9, 0x4a, 0xb8, 0x04, 0x0c, 0x6c, 0xb9, 0x5a, 0xfb, 0xb4, 0x8e, + 0x06, 0x81, 0xc7, 0x70, 0xcd, 0x02, 0xb2, 0xa0, 0xa4, 0x8e, 0x04, + 0x43, 0x70, 0xba, 0xaa, 0xb1, 0xa3, 0x72, 0x8e, 0x06, 0x82, 0xb1, + 0x0d, 0x05, 0x82, 0x14, 0x04, 0xc8, 0x14, 0x82, 0x5a, 0x03, 0x82, + 0x26, 0x3f, 0xf2, 0x14, 0x05, 0xf2, 0x5a, 0x04, 0xe2, 0x14, 0x06, + 0xe2, 0x5a, 0x05, 0xd2, 0x14, 0x07, 0xd2, 0x5a, 0x06, 0xc9, 0x6a, + 0x8e, 0x05, 0x43, 0x7a, 0xb2, 0xc4, 0x11, 0xa2, 0xca, 0x26, 0xe0, + 0x08, 0x00, 0xb1, 0xa4, 0x72, 0xcd, 0x02, 0x8e, 0x05, 0x44, 0x8e, + 0x06, 0x83, 0x1e, 0x8e, 0x04, 0x54, 0x8c, 0x8e, 0x08, 0x54, 0xa5, + 0x8e, 0x06, 0x54, 0x8e, 0x05, 0xa5, 0x48, 0xa6, 0x72, 0x21, 0xae, + 0x72, 0x81, 0xad, 0x72, 0x91, 0xac, 0x72, 0xa1, 0xab, 0x72, 0xb1, + 0xaa, 0x72, 0xc1, 0xa9, 0x72, 0xd1, 0xa8, 0x72, 0x31, 0x97, 0x72, + 0xe1, 0xa7, 0x72, 0xe9, 0x23, 0xd9, 0x33, 0xc9, 0x43, 0xb9, 0x63, + 0xa9, 0x53, 0x99, 0x83, 0x89, 0x93, 0x29, 0xa3, 0xf9, 0x8e, 0x09, + 0x82, 0xca, 0x0c, 0x98, 0x03, 0x82, 0x19, 0x07, 0xa2, 0x09, 0x0b, + 0x07, 0x68, 0x06, 0x66, 0x1a, 0x8e, 0x06, 0x82, 0xb9, 0x2e, 0xaf, + 0x8e, 0x10, 0x95, 0x43, 0x8e, 0x05, 0x92, 0x68, 0xb1, 0x72, 0x31, + 0xb0, 0x72, 0x81, 0xaf, 0x72, 0x48, 0xd3, 0x49, 0x08, 0x29, 0xd3, + 0x8e, 0x06, 0x83, 0x58, 0xb2, 0x8e, 0x0a, 0x2a, 0x91, 0x6b, 0x70, + 0x66, 0x42, 0x30, 0xd1, 0xb3, 0x72, 0xc1, 0xb4, 0x72, 0xb1, 0xcf, + 0x70, 0xc0, 0x20, 0x00, 0xb8, 0x4b, 0xd0, 0xbb, 0x10, 0xb0, 0xbc, + 0x41, 0xa0, 0xbb, 0x11, 0xc0, 0xbb, 0x10, 0xc1, 0xb5, 0x72, 0xc0, + 0x20, 0x00, 0xa2, 0x29, 0x88, 0xc0, 0xaa, 0x10, 0xb0, 0x8e, 0x06, + 0x82, 0x91, 0x52, 0x69, 0x88, 0x1d, 0xf0, 0x66, 0x52, 0x2c, 0xf1, + 0xb6, 0x72, 0x82, 0xa0, 0xfe, 0xc0, 0x20, 0x00, 0xd2, 0x29, 0x84, + 0xe1, 0xb7, 0x72, 0x80, 0xdd, 0x10, 0xd0, 0xd1, 0x41, 0x70, 0xdd, + 0x11, 0x8e, 0x06, 0x82, 0x91, 0x54, 0xc2, 0x29, 0x8c, 0xe0, 0xcc, + 0x10, 0xd0, 0x8e, 0x06, 0x81, 0xb0, 0x0a, 0x69, 0x8e, 0x07, 0xc9, + 0x5c, 0xb8, 0x72, 0x38, 0x03, 0x41, 0xb9, 0x72, 0x28, 0x03, 0x52, + 0xc3, 0x1c, 0x92, 0x02, 0x00, 0x32, 0x03, 0x70, 0x90, 0xa0, 0x24, + 0xe6, 0x4a, 0x0c, 0x88, 0x04, 0x1b, 0xba, 0x88, 0x08, 0x8e, 0x05, + 0xa3, 0x00, 0x1d, 0xf0, 0x90, 0xa3, 0x24, 0xe6, 0x7a, 0x8e, 0x08, + 0x13, 0x5a, 0x8e, 0x05, 0x13, 0x61, 0x14, 0x70, 0x88, 0xa6, 0x8e, + 0x05, 0xf6, 0x5f, 0xa0, 0x90, 0x74, 0xcc, 0xd9, 0x88, 0xa6, 0x8e, + 0x05, 0x2b, 0x8e, 0x04, 0x0c, 0x19, 0xa2, 0x05, 0x38, 0xb8, 0x65, + 0x98, 0x55, 0xa7, 0xbb, 0x20, 0xc2, 0x02, 0x01, 0xc0, 0xc1, 0x04, + 0x56, 0x7c, 0x07, 0x88, 0x04, 0x0c, 0x2a, 0x88, 0x08, 0x8e, 0x06, + 0xea, 0x0a, 0x04, 0x0c, 0x5a, 0x8e, 0x07, 0x0b, 0x1d, 0xf0, 0xa7, + 0xb9, 0x26, 0xa2, 0x02, 0x01, 0x17, 0x6a, 0x0a, 0x8e, 0x07, 0x21, + 0x8e, 0x05, 0xbd, 0x48, 0x12, 0x00, 0xa0, 0xa6, 0x24, 0xe6, 0x7a, + 0x40, 0x8e, 0x07, 0x70, 0x8e, 0x06, 0x9f, 0x7a, 0x26, 0x13, 0x0b, + 0x26, 0x53, 0x08, 0x26, 0x73, 0x05, 0x26, 0x23, 0x02, 0x66, 0x33, + 0x24, 0x92, 0x02, 0x01, 0x17, 0xe9, 0x8e, 0x08, 0x36, 0x0b, 0xe0, + 0x08, 0x00, 0x92, 0x12, 0x00, 0x90, 0x96, 0x24, 0xa6, 0x19, 0x8e, + 0x04, 0x14, 0x4a, 0x8e, 0x09, 0x60, 0x8e, 0x08, 0x4c, 0xf4, 0x8e, + 0x0d, 0x4c, 0x36, 0x41, 0x00, 0x51, 0x6b, 0x70, 0x61, 0xba, 0x72, + 0x41, 0xcf, 0x70, 0x4b, 0xf6, 0x8b, 0xe6, 0xcb, 0xd6, 0xc2, 0xc6, + 0x10, 0xb2, 0xc6, 0x14, 0xa2, 0xc6, 0x18, 0x92, 0xc6, 0x1c, 0x82, + 0xc6, 0x20, 0x72, 0xc6, 0x24, 0x62, 0xc6, 0x28, 0x16, 0x92, 0x05, + 0x31, 0xba, 0x72, 0x38, 0x03, 0xc0, 0x20, 0x00, 0x39, 0x34, 0x38, + 0x0a, 0x8e, 0x04, 0x07, 0x54, 0x38, 0x0f, 0x8e, 0x04, 0x07, 0x44, + 0x38, 0x09, 0x8e, 0x05, 0xa4, 0x14, 0x8a, 0x38, 0x0e, 0x8e, 0x05, + 0x08, 0x89, 0x38, 0x08, 0x8e, 0x05, 0x08, 0x8b, 0x38, 0x0d, 0x8e, + 0x04, 0x08, 0x64, 0x70, 0x38, 0x07, 0x8e, 0x05, 0x08, 0x74, 0x38, + 0x0c, 0x8e, 0x05, 0x18, 0x84, 0x38, 0x06, 0x8e, 0x05, 0x08, 0x88, + 0x38, 0x0b, 0x8e, 0x05, 0x08, 0x8e, 0x06, 0xcc, 0x7e, 0x61, 0xba, + 0x72, 0x38, 0x34, 0x39, 0x8e, 0x04, 0x1a, 0x68, 0x54, 0x69, 0x8e, + 0x04, 0x5f, 0x38, 0x44, 0x39, 0x8e, 0x04, 0x5f, 0x62, 0x25, 0x8a, + 0x69, 0x8e, 0x05, 0x60, 0x25, 0x89, 0x39, 0x8e, 0x04, 0x60, 0x62, + 0x25, 0x8b, 0x69, 0x8e, 0x05, 0x60, 0x24, 0x70, 0x39, 0x8e, 0x04, + 0x60, 0x62, 0x24, 0x74, 0x69, 0x8e, 0x05, 0x60, 0x25, 0x84, 0x39, + 0x8e, 0x04, 0x60, 0x61, 0xbb, 0x72, 0x32, 0x25, 0x88, 0x8e, 0x05, + 0x49, 0x62, 0x25, 0x8c, 0x69, 0x8e, 0x09, 0xb4, 0x30, 0x0c, 0x0a, + 0x65, 0xf1, 0xff, 0xcd, 0x04, 0x8e, 0x06, 0x84, 0x5b, 0x8e, 0x08, + 0x97, 0x29, 0x8e, 0x06, 0xa1, 0x24, 0x1a, 0xe5, 0xef, 0xff, 0x81, + 0xb2, 0x8e, 0x08, 0xac, 0x1f, 0x8e, 0x08, 0x9a, 0x5c, 0xb8, 0x72, + 0x98, 0x09, 0x22, 0xc9, 0x1c, 0x82, 0x09, 0x73, 0x42, 0x09, 0x70, + 0x16, 0x28, 0x11, 0xb2, 0x09, 0x71, 0x16, 0xcb, 0x10, 0x81, 0xc0, + 0x72, 0xe0, 0x08, 0x00, 0xbd, 0x0a, 0x31, 0xb9, 0x72, 0xd6, 0x8a, + 0x00, 0x88, 0x03, 0x8e, 0x07, 0x82, 0x8c, 0x74, 0x66, 0x74, 0x11, + 0xb1, 0xbc, 0x72, 0x25, 0x7e, 0x00, 0x91, 0xbd, 0x72, 0x90, 0xaa, + 0x82, 0x86, 0x06, 0x00, 0x00, 0x00, 0x00, 0x16, 0x64, 0x10, 0xc2, + 0xc4, 0xfc, 0x16, 0x0c, 0x10, 0x66, 0x64, 0x0b, 0x8e, 0x04, 0x1e, + 0x7c, 0x00, 0xd1, 0xbe, 0x72, 0xd0, 0xaa, 0x82, 0xe2, 0x22, 0x00, + 0x41, 0x20, 0x70, 0xa0, 0xee, 0x80, 0xa2, 0x24, 0x00, 0xe9, 0x02, + 0xcc, 0x3a, 0x8e, 0x06, 0xa1, 0x70, 0x2a, 0x1b, 0x61, 0x8e, 0x05, + 0xa2, 0x25, 0x0b, 0x86, 0x00, 0x00, 0xb2, 0x8e, 0x05, 0xa1, 0x73, + 0xab, 0x0c, 0x2b, 0x82, 0x26, 0x8e, 0x06, 0xa1, 0x71, 0xa8, 0x04, + 0x41, 0x6e, 0x8e, 0x05, 0xff, 0x15, 0x24, 0xcb, 0xc0, 0x20, 0x00, + 0x42, 0x24, 0xcd, 0x8e, 0x0a, 0x36, 0x8e, 0x0a, 0x33, 0x16, 0xc9, + 0x00, 0x16, 0x9b, 0x00, 0x0c, 0x1b, 0x8e, 0x08, 0x35, 0x92, 0xa1, + 0xf4, 0x68, 0xb2, 0xa8, 0xa2, 0x60, 0x64, 0xc0, 0xa0, 0x55, 0xc0, + 0x59, 0x82, 0x48, 0x02, 0x69, 0x92, 0x47, 0xb9, 0x4c, 0xa8, 0x22, + 0xb2, 0xa3, 0xe8, 0x40, 0xaa, 0x82, 0x65, 0x7e, 0x00, 0x57, 0x3a, + 0x14, 0xa8, 0x42, 0x8e, 0x06, 0x0e, 0xa5, 0x7d, 0x00, 0x67, 0x3a, + 0x06, 0x88, 0x03, 0x8e, 0x05, 0xf7, 0x42, 0x8e, 0x09, 0x81, 0x57, + 0xa2, 0x22, 0x03, 0x8e, 0x07, 0x1f, 0x7b, 0x00, 0x67, 0xba, 0x0f, + 0x82, 0x23, 0x00, 0x82, 0x28, 0x8e, 0x07, 0x81, 0xb3, 0x2d, 0x8e, + 0x06, 0x21, 0x92, 0xa0, 0x64, 0x47, 0xb9, 0xf7, 0xa2, 0x22, 0x01, + 0x8e, 0x06, 0x27, 0x25, 0x79, 0x00, 0x57, 0xba, 0xc9, 0x8e, 0x05, + 0x27, 0x01, 0x8e, 0x0c, 0x27, 0xad, 0x8e, 0x05, 0x82, 0x00, 0x6c, + 0x00, 0x91, 0xbf, 0x72, 0x90, 0xaa, 0x82, 0x06, 0xbf, 0x8e, 0x08, + 0xbe, 0x44, 0xc1, 0x72, 0xc0, 0x20, 0x00, 0x82, 0x28, 0xb0, 0xa1, + 0x14, 0x70, 0x57, 0xe8, 0x22, 0x9c, 0x83, 0x21, 0xb2, 0x72, 0x26, + 0x13, 0x1c, 0x66, 0x23, 0x17, 0x98, 0x32, 0xdc, 0x29, 0x88, 0x0a, + 0x8e, 0x08, 0x82, 0x90, 0x05, 0x32, 0x1d, 0xf0, 0x88, 0x0a, 0x8e, + 0x08, 0x81, 0xa7, 0x23, 0x0a, 0x8e, 0x08, 0x82, 0x90, 0x7b, 0x8e, + 0x06, 0x82, 0x94, 0x10, 0x31, 0xb8, 0x72, 0x98, 0x02, 0x99, 0x03, + 0x8e, 0x09, 0x4b, 0x57, 0xe8, 0x54, 0x21, 0x14, 0x70, 0x88, 0x02, + 0x8e, 0x06, 0xfd, 0x6d, 0x00, 0x70, 0xa8, 0x03, 0x82, 0x24, 0x6f, + 0xa2, 0xca, 0x58, 0xe0, 0x08, 0x00, 0xb1, 0xc2, 0x72, 0xc8, 0x03, + 0x82, 0x24, 0x70, 0xa2, 0xcc, 0x8e, 0x04, 0x0e, 0x88, 0x02, 0x8e, + 0x05, 0x5f, 0x91, 0xc6, 0x72, 0xe1, 0xc3, 0x72, 0xb8, 0x52, 0xa1, + 0xb9, 0x72, 0xd1, 0xb2, 0x72, 0xc8, 0x02, 0xc9, 0x2d, 0xa8, 0x0a, + 0xb9, 0x1d, 0xe9, 0x02, 0xb1, 0xc5, 0x72, 0xe1, 0xc4, 0x72, 0xe9, + 0x52, 0xc8, 0x0a, 0xc9, 0x0d, 0xb9, 0x0a, 0x99, 0x1a, 0x8e, 0x08, + 0x84, 0x48, 0x9c, 0x32, 0xbd, 0x02, 0xa1, 0xc8, 0x72, 0x81, 0xc7, + 0x72, 0x0c, 0x09, 0x88, 0x08, 0x99, 0x32, 0x8e, 0x0c, 0x81, 0x9d, + 0x1c, 0x31, 0xc9, 0x72, 0x21, 0xca, 0x72, 0x29, 0x8e, 0x05, 0x9b, + 0x3f, 0x00, 0x36, 0x61, 0x8e, 0x1b, 0x82, 0xc6, 0x50, 0xcb, 0x72, + 0x97, 0x93, 0x41, 0xe2, 0x22, 0x1a, 0x82, 0x05, 0x00, 0xf2, 0xa3, + 0x88, 0x80, 0xff, 0xc1, 0xa2, 0x02, 0x01, 0xfa, 0xee, 0x66, 0x2a, + 0x2d, 0x92, 0x22, 0x19, 0xe7, 0x19, 0x27, 0xa8, 0x2e, 0x66, 0x3a, + 0x22, 0xc2, 0x22, 0x9a, 0xa2, 0xde, 0x01, 0xa2, 0x0a, 0xc0, 0x0c, + 0x1b, 0xa7, 0x5c, 0x16, 0xb9, 0x01, 0x81, 0x2a, 0x72, 0xa2, 0x51, + 0x8e, 0x05, 0x8a, 0x17, 0x88, 0x58, 0x8e, 0x05, 0xc9, 0x31, 0x1d, + 0xf0, 0x8e, 0x06, 0xfb, 0x2d, 0x81, 0xa3, 0x71, 0x0c, 0x0c, 0x88, + 0x78, 0x8e, 0x0a, 0x9b, 0x50, 0x8e, 0x07, 0xfa, 0x2c, 0xcc, 0x8e, + 0x07, 0x8b, 0x46, 0x8e, 0x06, 0xc3, 0x64, 0x03, 0x21, 0x00, 0x70, + 0xb1, 0xcd, 0x72, 0x82, 0x22, 0x41, 0x8e, 0x05, 0xe4, 0x47, 0xec, + 0x6a, 0x92, 0xc4, 0xf8, 0xb6, 0x49, 0x21, 0xa2, 0x03, 0x08, 0xb2, + 0x03, 0x09, 0x9c, 0xca, 0x66, 0x3b, 0x1a, 0xa2, 0x03, 0x0c, 0x26, + 0xa4, 0x10, 0x42, 0xc4, 0xf4, 0x5c, 0xfc, 0x47, 0xbc, 0x08, 0x26, + 0x2a, 0x0d, 0xd2, 0xa0, 0xfe, 0xd7, 0x1a, 0x07, 0x0c, 0x02, 0x8e, + 0x06, 0xc3, 0x45, 0xad, 0x01, 0xb2, 0xc3, 0x69, 0x82, 0x22, 0x3f, + 0x8e, 0x05, 0x90, 0x2f, 0xdb, 0xb3, 0x2b, 0xa1, 0x0c, 0x2c, 0xd2, + 0x11, 0x00, 0x82, 0x22, 0x3f, 0xd0, 0x98, 0x74, 0xd0, 0x8e, 0x06, + 0x96, 0x31, 0x99, 0x20, 0x92, 0x51, 0x00, 0xe0, 0x08, 0x00, 0xe2, + 0x11, 0x00, 0xe2, 0xce, 0x5f, 0xe7, 0x34, 0xc3, 0xa2, 0x11, 0x01, + 0xa0, 0x98, 0x8e, 0x08, 0x93, 0x3e, 0x99, 0x20, 0x37, 0xe9, 0x0c, + 0x97, 0x69, 0x09, 0x87, 0x69, 0x06, 0x77, 0x69, 0x8e, 0x09, 0x81, + 0xa7, 0x01, 0x36, 0x41, 0x00, 0x81, 0xf3, 0x70, 0x88, 0x08, 0x88, + 0x18, 0x77, 0x68, 0x8e, 0x06, 0xfa, 0x67, 0x04, 0x25, 0xf5, 0xff, + 0x8e, 0x07, 0x8c, 0x34, 0x8e, 0x05, 0x9e, 0x42, 0xce, 0x8e, 0x15, + 0x8c, 0x38, 0xcf, 0x72, 0x31, 0x03, 0x71, 0x81, 0xce, 0x72, 0x48, + 0xb3, 0x49, 0x08, 0x29, 0xb3, 0x8e, 0x06, 0xa1, 0x68, 0xd0, 0x72, + 0x98, 0x09, 0x0c, 0x1a, 0x07, 0x69, 0x24, 0x17, 0x69, 0x23, 0x81, + 0x00, 0x70, 0xa1, 0xd1, 0x72, 0x82, 0x28, 0x15, 0x8e, 0x05, 0x83, + 0x20, 0x91, 0xd2, 0x72, 0xa1, 0xd3, 0x72, 0x98, 0x09, 0xa8, 0x0a, + 0xc0, 0x20, 0x00, 0x99, 0x0a, 0x1d, 0xf0, 0xa2, 0x4b, 0x00, 0x1d, + 0xf0, 0x37, 0x69, 0xfa, 0xc1, 0x03, 0x70, 0xd1, 0x62, 0x72, 0xc2, + 0x2c, 0xae, 0xb1, 0xd4, 0x72, 0xd7, 0x9c, 0xe8, 0xe1, 0x8e, 0x05, + 0xab, 0x02, 0x91, 0xd2, 0x70, 0x82, 0x2e, 0x80, 0x8e, 0x08, 0x82, + 0x9e, 0x0f, 0x80, 0xc0, 0x20, 0x00, 0xf1, 0xd6, 0x70, 0xd2, 0x2e, + 0x80, 0x8e, 0x08, 0x82, 0x9e, 0x0f, 0x80, 0xe5, 0x0d, 0xf6, 0x81, + 0xd5, 0x8e, 0x06, 0x82, 0x5f, 0x8e, 0x09, 0xab, 0x28, 0x88, 0x18, + 0x66, 0x68, 0x08, 0xa1, 0xd6, 0x72, 0x81, 0xd7, 0x8e, 0x0a, 0x82, + 0x78, 0x41, 0x00, 0x31, 0xd8, 0x8e, 0x04, 0x51, 0x81, 0x03, 0x70, + 0x32, 0x23, 0xb7, 0x82, 0x28, 0xb3, 0x91, 0x1b, 0x70, 0xb7, 0x78, + 0x23, 0x92, 0x29, 0x00, 0x16, 0xd9, 0x8e, 0x0a, 0x82, 0xc1, 0x75, + 0x9c, 0x1a, 0x8e, 0x0a, 0x82, 0xdf, 0x31, 0x07, 0x57, 0x63, 0x04, + 0x0c, 0x1a, 0x65, 0x31, 0xf7, 0x81, 0xd9, 0x8e, 0x08, 0xb5, 0x79, + 0x8e, 0x09, 0xa3, 0x70, 0x21, 0x03, 0x70, 0x31, 0xda, 0x72, 0x22, + 0x22, 0xb3, 0x41, 0x14, 0x70, 0xb7, 0x72, 0x0a, 0x82, 0x24, 0x39, + 0x91, 0xd9, 0x72, 0x32, 0x64, 0x39, 0x89, 0x09, 0x8e, 0x05, 0x8f, + 0x2c, 0x01, 0x6d, 0x03, 0x7d, 0x02, 0x39, 0x11, 0x29, 0x01, 0x49, + 0x21, 0x59, 0x31, 0x50, 0xc5, 0x20, 0x40, 0x54, 0x20, 0x16, 0xbc, + 0x14, 0xc7, 0xb3, 0x02, 0x86, 0x50, 0x00, 0x50, 0xfc, 0x40, 0xdc, + 0xd5, 0x37, 0x3c, 0x05, 0x47, 0xb2, 0x02, 0x06, 0xc9, 0x00, 0x40, + 0xb2, 0xc0, 0xb7, 0x32, 0x06, 0xc0, 0x33, 0xc0, 0x2d, 0x0b, 0x1d, + 0xf0, 0x2d, 0x0b, 0xc0, 0x33, 0xc0, 0x0b, 0x33, 0x1d, 0xf0, 0x2c, + 0x06, 0x50, 0x66, 0xc0, 0x62, 0x61, 0x14, 0x00, 0x06, 0x40, 0x30, + 0xa0, 0x91, 0xa2, 0x61, 0x12, 0x00, 0x15, 0x40, 0x40, 0x7c, 0x81, + 0x70, 0x60, 0xf5, 0xbd, 0x06, 0xa5, 0x3b, 0x00, 0xbd, 0x06, 0x92, + 0x21, 0x14, 0xa2, 0x61, 0x15, 0xa2, 0x61, 0x1b, 0x00, 0x15, 0x40, + 0x00, 0x33, 0xa1, 0xa2, 0x21, 0x12, 0x00, 0x09, 0x40, 0x20, 0x80, + 0x91, 0x80, 0x33, 0x20, 0x32, 0x61, 0x13, 0x30, 0x30, 0xf5, 0x25, + 0x3e, 0x00, 0xb2, 0x21, 0x1b, 0x00, 0xca, 0x11, 0xc0, 0xc3, 0x20, + 0x70, 0xa0, 0xf4, 0xa2, 0x61, 0x16, 0xb0, 0xaa, 0x82, 0x3d, 0x0c, + 0xa7, 0xbc, 0x14, 0xca, 0x37, 0x0b, 0xdb, 0xd2, 0x61, 0x15, 0x77, + 0x33, 0x0a, 0xa7, 0xb3, 0x07, 0x3a, 0x37, 0xe2, 0xcb, 0xfe, 0xe2, + 0x61, 0x15, 0xbd, 0x06, 0xa0, 0x33, 0xc0, 0xad, 0x03, 0xe5, 0x35, + 0x00, 0xbd, 0x06, 0xa2, 0x61, 0x1d, 0xa2, 0x61, 0x1c, 0xf2, 0x21, + 0x13, 0x30, 0xa3, 0x20, 0xf0, 0xf0, 0xf4, 0xf2, 0x61, 0x17, 0x65, + 0x39, 0x00, 0xb2, 0x21, 0x14, 0xc2, 0x21, 0x1d, 0xe2, 0x21, 0x15, + 0xd2, 0x21, 0x1c, 0x00, 0x8a, 0x11, 0x32, 0x21, 0x17, 0xf2, 0x21, + 0x16, 0x80, 0x33, 0x20, 0xd0, 0xff, 0x82, 0xad, 0x03, 0xf7, 0xb3, + 0x0f, 0x0b, 0xcd, 0x3a, 0xa7, 0x77, 0x3a, 0x08, 0xf7, 0xba, 0x05, + 0xc2, 0xcd, 0xfe, 0xa0, 0xa7, 0x80, 0x00, 0xde, 0x11, 0x00, 0x15, + 0x40, 0x00, 0x34, 0xa1, 0xd0, 0xdc, 0x20, 0x30, 0xcd, 0x82, 0x30, + 0xdd, 0xa2, 0xf0, 0xaa, 0xc0, 0xfd, 0x0d, 0xd7, 0x3a, 0x0d, 0x00, + 0xe2, 0xa1, 0xd7, 0x9a, 0x1c, 0xc7, 0xbe, 0x19, 0x06, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xe2, 0xa1, 0x0c, 0x1d, 0x70, 0x8f, 0xc0, 0x30, + 0x2c, 0xc0, 0x27, 0x3c, 0x01, 0x0c, 0x0d, 0xcd, 0x02, 0xd0, 0xd8, + 0xc0, 0xd0, 0xda, 0xc0, 0xc0, 0xae, 0xc0, 0xa7, 0xbe, 0x02, 0xc6, + 0x2d, 0x00, 0x00, 0x1b, 0x40, 0x00, 0x8d, 0xa1, 0x00, 0x05, 0x40, + 0xd0, 0x30, 0x91, 0xa0, 0x20, 0x91, 0x80, 0x22, 0x20, 0x1d, 0xf0, + 0x1d, 0xf0, 0x00, 0x47, 0x33, 0x02, 0xc6, 0x2c, 0x00, 0x90, 0xf4, + 0x40, 0x92, 0x61, 0x19, 0x8c, 0xd9, 0x92, 0x61, 0x19, 0x00, 0x19, + 0x40, 0x00, 0x54, 0xa1, 0x20, 0x63, 0x81, 0x00, 0x72, 0xa1, 0x70, + 0x20, 0xf5, 0xad, 0x06, 0x50, 0x30, 0xf5, 0xbd, 0x03, 0xe5, 0x2d, + 0x00, 0x00, 0x4a, 0x11, 0xbd, 0x03, 0x60, 0xa6, 0x20, 0x40, 0x22, + 0x20, 0x22, 0x61, 0x0d, 0x50, 0x40, 0xf4, 0xa5, 0x27, 0x00, 0x62, + 0x21, 0x19, 0xa0, 0xa4, 0x82, 0x49, 0xc1, 0xa7, 0xb2, 0x0c, 0x28, + 0xd1, 0x5a, 0x22, 0x57, 0x32, 0x05, 0xa7, 0xb2, 0x02, 0x20, 0x25, + 0x80, 0x70, 0x40, 0xf4, 0xbd, 0x03, 0xa0, 0x22, 0xc0, 0xad, 0x02, + 0x25, 0x2a, 0x00, 0x00, 0x7a, 0x8e, 0x05, 0xdf, 0x5c, 0x70, 0x74, + 0x20, 0x4d, 0x07, 0x65, 0x24, 0x00, 0xbd, 0x0a, 0xa8, 0xc1, 0xb0, + 0xaa, 0x82, 0xa7, 0xb7, 0x17, 0x5a, 0x47, 0x57, 0x34, 0x12, 0xa7, + 0xb4, 0x0f, 0x0c, 0x03, 0x00, 0x06, 0x40, 0x4a, 0x45, 0xa0, 0x24, + 0xc0, 0x20, 0x20, 0x91, 0x1d, 0xf0, 0x00, 0x8e, 0x05, 0x10, 0x8e, + 0x09, 0x0e, 0x1b, 0x40, 0x0b, 0x3d, 0x00, 0x83, 0xa1, 0x00, 0x05, + 0x40, 0x30, 0x8e, 0x0a, 0x81, 0x3a, 0x00, 0x00, 0x00, 0x56, 0xa4, + 0x00, 0xa2, 0xa0, 0x01, 0x40, 0xb4, 0x20, 0x25, 0x1f, 0x00, 0x5d, + 0x0a, 0x60, 0xf5, 0x40, 0xdc, 0x16, 0x50, 0x43, 0xc0, 0x50, 0x20, + 0xf5, 0x42, 0x61, 0x11, 0x50, 0x40, 0xf4, 0x32, 0x21, 0x11, 0x06, + 0x23, 0x00, 0x00, 0x2c, 0x08, 0x00, 0x16, 0x40, 0x00, 0x55, 0xa1, + 0x60, 0x88, 0xc0, 0x00, 0x08, 0x40, 0x30, 0xa0, 0x91, 0xa9, 0xa1, + 0x00, 0x16, 0x40, 0x20, 0x43, 0x81, 0x00, 0x72, 0xa1, 0x42, 0x61, + 0x11, 0x40, 0x40, 0xf5, 0x50, 0x20, 0xf5, 0xbd, 0x02, 0xe5, 0x1f, + 0x00, 0x00, 0x3a, 0x11, 0xbd, 0x02, 0xa8, 0xa1, 0x30, 0x34, 0x20, + 0x39, 0x81, 0x50, 0x40, 0xf4, 0xa5, 0x19, 0x00, 0xa0, 0xb4, 0x82, + 0xb7, 0xb3, 0x0b, 0x38, 0x81, 0x5a, 0x33, 0x57, 0x33, 0x04, 0xb7, + 0xb3, 0x01, 0x3a, 0x35, 0x82, 0x21, 0x11, 0xb0, 0x33, 0xc0, 0xad, + 0x03, 0xbd, 0x02, 0x80, 0x80, 0xf4, 0x89, 0xb1, 0x65, 0x1c, 0x00, + 0xbd, 0x02, 0x98, 0xb1, 0x00, 0x8e, 0x05, 0x88, 0x0a, 0x99, 0x91, + 0x92, 0x61, 0x18, 0xad, 0x03, 0x25, 0x16, 0x00, 0xa0, 0xa4, 0x82, + 0xb2, 0x21, 0x18, 0xa7, 0xbb, 0x0b, 0xb8, 0x91, 0x5a, 0xbb, 0x57, + 0x3b, 0x04, 0xa7, 0xbb, 0x01, 0xba, 0xb5, 0xa0, 0x3b, 0x8e, 0x05, + 0x38, 0x70, 0xc0, 0xf5, 0xc2, 0x61, 0x10, 0xe5, 0x18, 0x00, 0xbd, + 0x02, 0x00, 0xea, 0x11, 0xd2, 0x21, 0x10, 0xad, 0x03, 0xe0, 0xdd, + 0x20, 0xd9, 0xe1, 0xd2, 0x61, 0x1a, 0xa5, 0x12, 0x8e, 0x04, 0x3a, + 0x32, 0x21, 0x1a, 0xa7, 0xb3, 0x0b, 0x38, 0xe1, 0x8e, 0x05, 0x75, + 0xa7, 0x8e, 0x04, 0x75, 0x70, 0x70, 0xf4, 0xbd, 0x02, 0x8e, 0x05, + 0x84, 0x57, 0x65, 0x15, 0x00, 0x00, 0x8a, 0x11, 0xbd, 0x02, 0xad, + 0x03, 0x80, 0x77, 0x20, 0x72, 0x61, 0x0f, 0x65, 0x0f, 0x8e, 0x04, + 0x32, 0xa7, 0xb7, 0x19, 0x78, 0xf1, 0x5a, 0x77, 0x57, 0x37, 0x12, + 0xa7, 0xb7, 0x8e, 0x06, 0x82, 0x4c, 0x7a, 0x75, 0xa0, 0x27, 0x8e, + 0x0d, 0x82, 0x4c, 0x8e, 0x07, 0x0e, 0x8e, 0x06, 0x96, 0x00, 0x8e, + 0x0e, 0x82, 0xd4, 0x78, 0x21, 0x00, 0x30, 0x72, 0x30, 0x20, 0x61, + 0x60, 0x30, 0x31, 0x60, 0xb6, 0x23, 0x34, 0x60, 0x51, 0x41, 0x37, + 0x35, 0x3a, 0x50, 0xf6, 0x40, 0x40, 0xf3, 0x40, 0x50, 0x44, 0xc0, + 0x00, 0x14, 0x40, 0x00, 0x33, 0xa1, 0x0c, 0x02, 0x76, 0x94, 0x0d, + 0x37, 0x36, 0x04, 0x30, 0x66, 0xc0, 0x1b, 0x22, 0xf0, 0x22, 0x11, + 0x30, 0x31, 0x41, 0x37, 0x36, 0x01, 0x1b, 0x22, 0x20, 0x50, 0x60, + 0x70, 0x25, 0xa3, 0x1d, 0xf0, 0x9c, 0x53, 0x60, 0x20, 0x60, 0x70, + 0x26, 0x8e, 0x05, 0xdf, 0x38, 0x37, 0x36, 0x10, 0x0c, 0x12, 0x7c, + 0xf4, 0x70, 0x24, 0xa3, 0x8e, 0x05, 0x5f, 0x00, 0x44, 0x49, 0x56, + 0x30, 0x8e, 0x05, 0x8a, 0x14, 0x21, 0x00, 0x20, 0x72, 0x20, 0x20, + 0x21, 0x8e, 0x06, 0x68, 0x2c, 0x50, 0xf2, 0x8e, 0x04, 0x62, 0x47, + 0xb5, 0x14, 0x8e, 0x09, 0x65, 0x76, 0x94, 0x08, 0x37, 0x32, 0x02, + 0x30, 0x22, 0xc0, 0x8e, 0x04, 0x5e, 0x8e, 0x05, 0x09, 0xd6, 0x27, + 0x00, 0x20, 0x20, 0x60, 0x1d, 0xf0, 0x00, 0xcc, 0x53, 0x8e, 0x0b, + 0x49, 0x8e, 0x06, 0x81, 0x34, 0xb6, 0x23, 0x2f, 0x6d, 0x02, 0x8e, + 0x06, 0x81, 0x27, 0x47, 0xb5, 0x2a, 0x8e, 0x09, 0x45, 0x22, 0xa0, + 0x00, 0x8e, 0x13, 0x81, 0x2b, 0x02, 0x22, 0xc2, 0x01, 0x1d, 0xf0, + 0x8c, 0x8e, 0x07, 0x81, 0x20, 0x0c, 0x0c, 0x8e, 0x06, 0xde, 0x16, + 0x8e, 0x0d, 0x81, 0x1c, 0xb6, 0x23, 0x29, 0x8e, 0x08, 0x81, 0x13, + 0x16, 0x8e, 0x09, 0x4e, 0x3d, 0xf0, 0x8e, 0x12, 0x81, 0x15, 0x8e, + 0x04, 0x3b, 0x8e, 0x13, 0x81, 0x10, 0x00, 0x14, 0x40, 0xe6, 0xc4, + 0x07, 0x20, 0x33, 0x81, 0x00, 0x22, 0xa1, 0x1d, 0xf0, 0x00, 0x32, + 0xa1, 0x8e, 0x07, 0x58, 0x00, 0x04, 0x8e, 0x05, 0x18, 0x23, 0x81, + 0x30, 0x30, 0x91, 0x1d, 0xf0, 0x30, 0x20, 0x91, 0x0c, 0x8e, 0x06, + 0x81, 0xb8, 0x68, 0x0c, 0x02, 0x00, 0x50, 0x8e, 0x08, 0x93, 0x18, + 0x5d, 0x02, 0x8e, 0x05, 0x0e, 0x2d, 0x05, 0x52, 0xc1, 0xf0, 0x68, + 0x15, 0x8d, 0x00, 0x62, 0xc6, 0xf0, 0x86, 0x01, 0x00, 0x88, 0x05, + 0x5d, 0x06, 0x62, 0xc7, 0xf0, 0x78, 0x16, 0x77, 0xb2, 0xf3, 0x72, + 0xc6, 0xfc, 0x49, 0x07, 0x42, 0xc1, 0xf0, 0x68, 0x05, 0x78, 0x15, + 0x69, 0x04, 0x79, 0x14, 0x68, 0x25, 0x78, 0x35, 0x69, 0x24, 0x79, + 0x34, 0x80, 0x8e, 0x00, 0x15, 0xe0, 0x33, 0x11, 0x00, 0x42, 0x40, + 0x30, 0x08, 0x81, 0x8e, 0x06, 0x83, 0x5c, 0x00, 0x20, 0x00, 0x1d, + 0xf0, 0x52, 0x65, 0x6d, 0x4d, 0x65, 0x6d, 0x3a, 0x25, 0x64, 0x20, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x43, 0x75, 0x72, 0x20, 0x74, + 0x78, 0x62, 0x3a, 0x25, 0x64, 0x2c, 0x72, 0x8e, 0x05, 0x07, 0x28, + 0x65, 0x61, 0x63, 0x68, 0x20, 0x25, 0x64, 0x2c, 0x8e, 0x08, 0x23, + 0x29, 0x0a, 0x8e, 0x05, 0x82, 0x11, 0x8e, 0x05, 0x05, 0x00, 0x00, + 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x20, 0x64, 0x69, 0x73, 0x63, 0x20, + 0x69, 0x65, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x25, 0x64, 0x20, + 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x8e, 0x04, 0x29, 0x8e, 0x0b, + 0x20, 0x70, 0x65, 0x65, 0x72, 0x8e, 0x0b, 0x1a, 0x8e, 0x11, 0x40, + 0x62, 0x73, 0x73, 0x8e, 0x11, 0x1f, 0x00, 0x73, 0x74, 0x3a, 0x20, + 0x25, 0x30, 0x38, 0x78, 0x2c, 0x20, 0x70, 0x68, 0x79, 0x8e, 0x06, + 0x0b, 0x8e, 0x0d, 0x81, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x20, + 0x72, 0x65, 0x61, 0x72, 0x6d, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x8e, 0x0d, 0x1e, 0x00, 0x00, 0x20, 0x30, 0x78, 0x25, 0x78, 0x8e, + 0x05, 0x05, 0x8e, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x9a, 0x99, 0x00, + 0xfd, 0xff, 0xff, 0xff, 0x38, 0x7d, 0x43, 0x00, 0xc8, 0x02, 0x00, + 0x00, 0x0c, 0x4c, 0x7d, 0x43, 0x00, 0x02, 0x00, 0x30, 0x11, 0x68, + 0x81, 0x97, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x10, 0x60, + 0x7d, 0x43, 0x00, 0x00, 0x00, 0x4d, 0x00, 0xa8, 0x7f, 0x0c, 0x04, + 0x08, 0x00, 0x0c, 0x04, 0x04, 0x00, 0x74, 0x7d, 0x43, 0x00, 0x27, + 0x01, 0xf4, 0x00, 0xb4, 0x7e, 0x0c, 0x0a, 0x14, 0x88, 0x7d, 0x43, + 0x00, 0x28, 0x01, 0xf4, 0x00, 0xc0, 0x0c, 0x05, 0x30, 0x0c, 0x06, + 0x14, 0x9c, 0x7d, 0x43, 0x00, 0x43, 0x01, 0x08, 0x00, 0xb8, 0x0c, + 0x0b, 0x14, 0x0c, 0x04, 0x5c, 0x44, 0x01, 0x08, 0x00, 0xb0, 0x0c, + 0x0b, 0x14, 0xb6, 0x0d, 0xc0, 0x36, 0x0c, 0x04, 0x04, 0x00, 0xc0, + 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0xe4, 0xb2, 0x6d, 0x03, 0x86, + 0x10, 0x48, 0x8e, 0x0c, 0x08, 0x08, 0xd4, 0xac, 0x0a, 0xf8, 0xe8, + 0xc8, 0x50, 0xfd, 0x00, 0xf7, 0xfc, 0xff, 0x00, 0x00, 0x03, 0xf8, + 0xda, 0x50, 0x00, 0x00, 0x20, 0x40, 0x00, 0x05, 0x21, 0x40, 0x00, + 0x07, 0x22, 0x40, 0x00, 0x09, 0x24, 0x40, 0x00, 0x0d, 0x03, 0x20, + 0x06, 0x10, 0x04, 0x20, 0x06, 0x12, 0xe0, 0x2e, 0x00, 0x2c, 0xe1, + 0x2e, 0x00, 0x2e, 0xe2, 0x2e, 0x00, 0x30, 0xe3, 0x2e, 0x00, 0x32, + 0xe4, 0x2e, 0x00, 0x34, 0xe0, 0x4e, 0x00, 0x36, 0xe2, 0x4e, 0x00, + 0x3a, 0xe4, 0x4e, 0x00, 0x3e, 0xe6, 0x4e, 0x00, 0x42, 0xea, 0x4e, + 0x00, 0x46, 0xec, 0x4e, 0x00, 0x4a, 0xee, 0x4e, 0x00, 0x4e, 0xf2, + 0x6e, 0x00, 0x56, 0xf4, 0x6e, 0x00, 0x5a, 0xf6, 0x6e, 0x00, 0x5e, + 0xf3, 0x6e, 0x02, 0x62, 0xf6, 0x6e, 0x02, 0x68, 0xf3, 0x6e, 0x04, + 0x6b, 0xf6, 0x6e, 0x04, 0x71, 0xf3, 0x8e, 0x06, 0x74, 0xf6, 0x8e, + 0x06, 0x7a, 0x0c, 0x04, 0x04, 0x0c, 0x08, 0x08, 0x0c, 0x08, 0x08, + 0x0c, 0x08, 0x81, 0x3c, 0x01, 0x48, 0x00, 0x02, 0x02, 0x8b, 0xc0, + 0x0c, 0x04, 0x04, 0x0c, 0x08, 0x08, 0x02, 0x02, 0xcb, 0x40, 0x04, + 0x03, 0xd1, 0x40, 0x04, 0x04, 0x17, 0xc1, 0x05, 0x0c, 0x04, 0x04, + 0x0c, 0x08, 0x08, 0x0c, 0x0c, 0x0c, 0x0c, 0x10, 0x81, 0x54, 0x0c, + 0x81, 0x74, 0x81, 0x74, 0x42, 0x44, 0x49, 0x46, 0x46, 0x61, 0x00, + 0x10, 0x0c, 0x09, 0x84, 0x09, 0x04, 0xe2, 0x81, 0xd4, 0x00, 0x4a, + 0x50, 0x00, 0x0f, 0x0c, 0x04, 0x0c, 0xe8, 0x00, 0x00, 0x04, 0x04, + 0x0b, 0x00, 0x00, 0x08, 0xac, 0x00, 0x00, 0x00, 0x08, 0xfc, 0x00, + 0x00, 0x05, 0xbd, 0x81, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x11, + 0x00, 0x00, 0x0e, 0xbf, 0x0c, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x0e, + 0xdb, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x07, + 0x84, 0x54, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, }; + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/hif-ops.h b/drivers/net/wireless/ath/ath6kl-3.5/hif-ops.h new file mode 100644 index 000000000000..03f9b7056ede --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/hif-ops.h @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 HIF_OPS_H +#define HIF_OPS_H + +#include "hif.h" +#include "debug.h" + +static inline int hif_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, + u32 len, u32 request) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, + "hif %s sync addr 0x%x buf 0x%p len %d request 0x%x\n", + (request & HIF_WRITE) ? "write" : "read", + addr, buf, len, request); + + return ar->hif_ops->read_write_sync(ar, addr, buf, len, request); +} + +static inline int hif_write_async(struct ath6kl *ar, u32 address, u8 *buffer, + u32 length, u32 request, + struct htc_packet *packet) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, + "hif write async addr 0x%x buf 0x%p len %d request 0x%x\n", + address, buffer, length, request); + + return ar->hif_ops->write_async(ar, address, buffer, length, + request, packet); +} +static inline void ath6kl_hif_irq_enable(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif irq enable\n"); + + return ar->hif_ops->irq_enable(ar); +} + +static inline void ath6kl_hif_irq_disable(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif irq disable\n"); + + return ar->hif_ops->irq_disable(ar); +} + +static inline struct hif_scatter_req *hif_scatter_req_get(struct ath6kl *ar) +{ + return ar->hif_ops->scatter_req_get(ar); +} + +static inline void hif_scatter_req_add(struct ath6kl *ar, + struct hif_scatter_req *s_req) +{ + return ar->hif_ops->scatter_req_add(ar, s_req); +} + +static inline int ath6kl_hif_enable_scatter(struct ath6kl *ar) +{ + return ar->hif_ops->enable_scatter(ar); +} + +static inline int ath6kl_hif_scat_req_rw(struct ath6kl *ar, + struct hif_scatter_req *scat_req) +{ + return ar->hif_ops->scat_req_rw(ar, scat_req); +} + +static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar) +{ + return ar->hif_ops->cleanup_scatter(ar); +} + +static inline int ath6kl_hif_suspend(struct ath6kl *ar, + struct cfg80211_wowlan *wow) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif suspend\n"); + + return ar->hif_ops->suspend(ar, wow); +} + +/* + * Read from the ATH6KL through its diagnostic window. No cooperation from + * the Target is required for this. + */ +static inline int ath6kl_hif_diag_read32(struct ath6kl *ar, u32 address, + u32 *value) +{ + return ar->hif_ops->diag_read32(ar, address, value); +} + +/* + * Write to the ATH6KL through its diagnostic window. No cooperation from + * the Target is required for this. + */ +static inline int ath6kl_hif_diag_write32(struct ath6kl *ar, u32 address, + __le32 value) +{ + return ar->hif_ops->diag_write32(ar, address, value); +} + +static inline int ath6kl_hif_bmi_read(struct ath6kl *ar, u8 *buf, u32 len) +{ + return ar->hif_ops->bmi_read(ar, buf, len); +} + +static inline int ath6kl_hif_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) +{ + return ar->hif_ops->bmi_write(ar, buf, len); +} + +static inline int ath6kl_hif_resume(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif resume\n"); + + return ar->hif_ops->resume(ar); +} + +static inline int ath6kl_hif_power_on(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif power on\n"); + + return ar->hif_ops->power_on(ar); +} + +static inline int ath6kl_hif_power_off(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif power off\n"); + + return ar->hif_ops->power_off(ar); +} + +static inline void ath6kl_hif_stop(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif stop\n"); + + ar->hif_ops->stop(ar); +} + +static inline int ath6kl_hif_stat(struct ath6kl *ar, u8 *buf, + int buf_len) +{ + return ar->hif_ops->get_stat(ar, buf, buf_len); +} + +static inline void ath6kl_hif_pipe_register_callback(struct ath6kl *ar, + void *htc_context, + struct ath6kl_hif_pipe_callbacks *callbacks) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe register callback\n"); + + ar->hif_ops->pipe_register_callback(ar, htc_context, callbacks); +} + +static inline int ath6kl_hif_pipe_send_bundle(struct ath6kl *ar, u8 pid, + struct sk_buff **msg_bundle, int num_msgs) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe send bundle\n"); + + return ar->hif_ops->pipe_send_bundle(ar, pid, msg_bundle, num_msgs); +} + +static inline int ath6kl_hif_pipe_send(struct ath6kl *ar, + u8 pipe, struct sk_buff *hdr_buf, struct sk_buff *buf) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe send\n"); + + return ar->hif_ops->pipe_send(ar, pipe, hdr_buf, buf); +} + +static inline void ath6kl_hif_pipe_get_default(struct ath6kl *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n"); + + ar->hif_ops->pipe_get_default(ar, ul_pipe, dl_pipe); +} + +static inline int ath6kl_hif_pipe_map_service(struct ath6kl *ar, + u16 service_id, u8 *ul_pipe, u8 *dl_pipe) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n"); + + return ar->hif_ops->pipe_map_service(ar, service_id, ul_pipe, dl_pipe); +} + +static inline u16 ath6kl_hif_pipe_get_free_queue_number(struct ath6kl *ar, + u8 pipe) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get free queue number\n"); + + return ar->hif_ops->pipe_get_free_queue_number(ar, pipe); +} + +static inline u16 ath6kl_hif_pipe_get_max_queue_number(struct ath6kl *ar, + u8 pipe) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get max queue number\n"); + + return ar->hif_ops->pipe_get_max_queue_number(ar, pipe); +} + +static inline u16 ath6kl_hif_pipe_set_max_sche(struct ath6kl *ar, + u32 max_sche_tx, u32 max_sche_rx) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe set max scheduled packages\n"); + + return ar->hif_ops->pipe_set_max_sche(ar, max_sche_tx, max_sche_rx); +} + +static inline int ath6kl_hif_diag_warm_reset(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif diag warm reset\n"); + + return ar->hif_ops->diag_warm_reset(ar); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static inline void ath6kl_hif_early_suspend(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif early_suspend\n"); + + ar->hif_ops->early_suspend(ar); +} +static inline void ath6kl_hif_late_resume(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif late_resume\n"); + + ar->hif_ops->late_resume(ar); +} +#endif + +static inline int ath6kl_hif_bus_config(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif buf config\n"); + + return ar->hif_ops->bus_config(ar); +} + +#ifdef USB_AUTO_SUSPEND +static inline void ath6kl_hif_auto_pm_disable(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif ath6kl_hif_auto_pm_disable\n"); + + ar->hif_ops->auto_pm_disable(ar); +} + +static inline void ath6kl_hif_auto_pm_enable(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif ath6kl_hif_auto_pm_enable\n"); + + ar->hif_ops->auto_pm_enable(ar); +} + +static inline void ath6kl_hif_auto_pm_turnon(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif ath6kl_hif_auto_pm_turnon\n"); + ar->hif_ops->auto_pm_turnon(ar); +} + +static inline void ath6kl_hif_auto_pm_turnoff(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif ath6kl_hif_auto_pm_turnoff\n"); + ar->hif_ops->auto_pm_turnoff(ar); +} + +static inline int ath6kl_hif_auto_pm_get_usage_cnt(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif auto_pm_get_usage_cnt\n"); + + return ar->hif_ops->auto_pm_get_usage_cnt(ar); +} +#endif + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/hif.c b/drivers/net/wireless/ath/ath6kl-3.5/hif.c new file mode 100644 index 000000000000..175475be4545 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/hif.c @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2007-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "hif.h" + +#include "core.h" +#include "target.h" +#include "hif-ops.h" +#include "debug.h" + +#define MAILBOX_FOR_BLOCK_SIZE 1 + +#define ATH6KL_TIME_QUANTUM 10 /* in ms */ + +static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req, + bool from_dma) +{ + u8 *buf; + int i; + + buf = req->virt_dma_buf; + + for (i = 0; i < req->scat_entries; i++) { + + if (from_dma) + memcpy(req->scat_list[i].buf, buf, + req->scat_list[i].len); + else + memcpy(buf, req->scat_list[i].buf, + req->scat_list[i].len); + + buf += req->scat_list[i].len; + } + + return 0; +} + +int ath6kl_hif_rw_comp_handler(void *context, int status) +{ + struct htc_packet *packet = context; + + ath6kl_dbg(ATH6KL_DBG_HIF, "hif rw completion pkt 0x%p status %d\n", + packet, status); + + packet->status = status; + packet->completion(packet->context, packet); + + return 0; +} +#define REG_DUMP_COUNT_AR6003 60 +#define REGISTER_DUMP_LEN_MAX 60 +#define REG_DUMP_COUNT_AR6004 60 + +static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) +{ + __le32 regdump_val[REGISTER_DUMP_LEN_MAX]; + u32 i, address, regdump_addr = 0; + int ret; + int dumpcount = REG_DUMP_COUNT_AR6004; + + if (ar->target_type != TARGET_TYPE_AR6003 && + ar->target_type != TARGET_TYPE_AR6004) + return; + + switch (ar->target_type) { + case TARGET_TYPE_AR6003: + dumpcount = REG_DUMP_COUNT_AR6003; + break; + case TARGET_TYPE_AR6004: + default: + dumpcount = REG_DUMP_COUNT_AR6004; + break; + } + + /* the reg dump pointer is copied to the host interest area */ + address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); + address = TARG_VTOP(ar->target_type, address); + + /* read RAM location through diagnostic window */ + ret = ath6kl_diag_read32(ar, address, ®dump_addr); + + if (ret || !regdump_addr) { + ath6kl_warn("failed to get ptr to register dump area: %d\n", + ret); + return; + } + + ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n", + regdump_addr); + regdump_addr = TARG_VTOP(ar->target_type, regdump_addr); + + /* fetch register dump data */ + ret = ath6kl_diag_read(ar, regdump_addr, (u8 *)®dump_val[0], + dumpcount * (sizeof(u32))); + if (ret) { + ath6kl_warn("failed to get register dump: %d\n", ret); + return; + } + + ath6kl_info("crash dump:\n"); + ath6kl_info("hw 0x%x fw %s\n", ar->wiphy->hw_version, + ar->wiphy->fw_version); + + BUILD_BUG_ON(REG_DUMP_COUNT_AR6004 % 4); + + for (i = 0; i < dumpcount / 4; i++) { + ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + 4 * i, + le32_to_cpu(regdump_val[i]), + le32_to_cpu(regdump_val[i + 1]), + le32_to_cpu(regdump_val[i + 2]), + le32_to_cpu(regdump_val[i + 3])); + } + +} + +static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) +{ + u32 dummy; + int ret; + + ath6kl_warn("firmware crashed\n"); + + /* + * read counter to clear the interrupt, the debug error interrupt is + * counter 0. + */ + ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS, + (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC); + if (ret) + ath6kl_warn("Failed to clear debug interrupt: %d\n", ret); + + ath6kl_hif_dump_fw_crash(dev->ar); + + ath6kl_fw_crash_trap(dev->ar); + + return ret; +} + +/* mailbox recv message polling */ +int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd, + int timeout) +{ + struct ath6kl_irq_proc_registers *rg; + int status = 0, i; + u8 htc_mbox = 1 << HTC_MAILBOX; + + for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) { + /* this is the standard HIF way, load the reg table */ + status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS, + (u8 *) &dev->irq_proc_reg, + sizeof(dev->irq_proc_reg), + HIF_RD_SYNC_BYTE_INC); + + if (status) { + ath6kl_err("failed to read reg table\n"); + return status; + } + + /* check for MBOX data and valid lookahead */ + if (dev->irq_proc_reg.host_int_status & htc_mbox) { + if (dev->irq_proc_reg.rx_lkahd_valid & + htc_mbox) { + /* + * Mailbox has a message and the look ahead + * is valid. + */ + rg = &dev->irq_proc_reg; + *lk_ahd = + le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); + break; + } + } + + /* delay a little */ + mdelay(ATH6KL_TIME_QUANTUM); + ath6kl_dbg(ATH6KL_DBG_HIF, "hif retry mbox poll try %d\n", i); + } + + if (i == 0) { + ath6kl_err("timeout waiting for recv message\n"); + status = -ETIME; + /* check if the target asserted */ + if (dev->irq_proc_reg.counter_int_status & + ATH6KL_TARGET_DEBUG_INTR_MASK) + /* + * Target failure handler will be called in case of + * an assert. + */ + ath6kl_hif_proc_dbg_intr(dev); + } + + return status; +} + +/* + * Disable packet reception (used in case the host runs out of buffers) + * using the interrupt enable registers through the host I/F + */ +int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx) +{ + struct ath6kl_irq_enable_reg regs; + int status = 0; + + ath6kl_dbg(ATH6KL_DBG_HIF, "hif rx %s\n", + enable_rx ? "enable" : "disable"); + + /* take the lock to protect interrupt enable shadows */ + spin_lock_bh(&dev->lock); + + if (enable_rx) + dev->irq_en_reg.int_status_en |= + SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); + else + dev->irq_en_reg.int_status_en &= + ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); + + memcpy(®s, &dev->irq_en_reg, sizeof(regs)); + + spin_unlock_bh(&dev->lock); + + status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, + ®s.int_status_en, + sizeof(struct ath6kl_irq_enable_reg), + HIF_WR_SYNC_BYTE_INC); + + return status; +} + +int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev, + struct hif_scatter_req *scat_req, bool read) +{ + int status = 0; + + if (read) { + scat_req->req = HIF_RD_SYNC_BLOCK_FIX; + scat_req->addr = dev->ar->mbox_info.htc_addr; + } else { + scat_req->req = HIF_WR_ASYNC_BLOCK_INC; + + scat_req->addr = + (scat_req->len > HIF_MBOX_WIDTH) ? + dev->ar->mbox_info.htc_ext_addr : + dev->ar->mbox_info.htc_addr; + } + + ath6kl_dbg(ATH6KL_DBG_HIF, + "hif submit scatter request entries %d len %d mbox 0x%x %s %s\n", + scat_req->scat_entries, scat_req->len, + scat_req->addr, !read ? "async" : "sync", + (read) ? "rd" : "wr"); + + if (!read && scat_req->virt_scat) { + status = ath6kl_hif_cp_scat_dma_buf(scat_req, false); + if (status) { + scat_req->status = status; + scat_req->complete(dev->ar->htc_target, scat_req); + return 0; + } + } + + status = ath6kl_hif_scat_req_rw(dev->ar, scat_req); + + if (read) { + /* in sync mode, we can touch the scatter request */ + scat_req->status = status; + if (!status && scat_req->virt_scat) + scat_req->status = + ath6kl_hif_cp_scat_dma_buf(scat_req, true); + } + + return status; +} + +static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev) +{ + u8 counter_int_status; + + ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n"); + + counter_int_status = dev->irq_proc_reg.counter_int_status & + dev->irq_en_reg.cntr_int_status_en; + + ath6kl_dbg(ATH6KL_DBG_IRQ, + "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", + counter_int_status); + + /* + * NOTE: other modules like GMBOX may use the counter interrupt for + * credit flow control on other counters, we only need to check for + * the debug assertion counter interrupt. + */ + if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK) + return ath6kl_hif_proc_dbg_intr(dev); + + return 0; +} + +static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev) +{ + int status; + u8 error_int_status; + u8 reg_buf[4]; + + ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n"); + + error_int_status = dev->irq_proc_reg.error_int_status & 0x0F; + if (!error_int_status) { + WARN_ON(1); + return -EIO; + } + + ath6kl_dbg(ATH6KL_DBG_IRQ, + "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n", + error_int_status); + + if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status)) + ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n"); + + if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status)) + ath6kl_err("rx underflow\n"); + + if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status)) + ath6kl_err("tx overflow\n"); + + /* Clear the interrupt */ + dev->irq_proc_reg.error_int_status &= ~error_int_status; + + /* set W1C value to clear the interrupt, this hits the register first */ + reg_buf[0] = error_int_status; + reg_buf[1] = 0; + reg_buf[2] = 0; + reg_buf[3] = 0; + + status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS, + reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); + + if (status) + WARN_ON(1); + + return status; +} + +static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev) +{ + int status; + u8 cpu_int_status; + u8 reg_buf[4]; + + ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n"); + + cpu_int_status = dev->irq_proc_reg.cpu_int_status & + dev->irq_en_reg.cpu_int_status_en; + if (!cpu_int_status) { + WARN_ON(1); + return -EIO; + } + + ath6kl_dbg(ATH6KL_DBG_IRQ, + "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", + cpu_int_status); + + /* Clear the interrupt */ + dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status; + + /* + * Set up the register transfer buffer to hit the register 4 times , + * this is done to make the access 4-byte aligned to mitigate issues + * with host bus interconnects that restrict bus transfer lengths to + * be a multiple of 4-bytes. + */ + + /* set W1C value to clear the interrupt, this hits the register first */ + reg_buf[0] = cpu_int_status; + /* the remaining are set to zero which have no-effect */ + reg_buf[1] = 0; + reg_buf[2] = 0; + reg_buf[3] = 0; + + status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS, + reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); + + if (status) + WARN_ON(1); + + return status; +} + +/* process pending interrupts synchronously */ +static int proc_pending_irqs(struct ath6kl_device *dev, bool *done) +{ + struct ath6kl_irq_proc_registers *rg; + int status = 0; + u8 host_int_status = 0; + u32 lk_ahd = 0; + u8 htc_mbox = 1 << HTC_MAILBOX; + + ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev); + + /* + * NOTE: HIF implementation guarantees that the context of this + * call allows us to perform SYNCHRONOUS I/O, that is we can block, + * sleep or call any API that can block or switch thread/task + * contexts. This is a fully schedulable context. + */ + + /* + * Process pending intr only when int_status_en is clear, it may + * result in unnecessary bus transaction otherwise. Target may be + * unresponsive at the time. + */ + if (dev->irq_en_reg.int_status_en) { + /* + * Read the first 28 bytes of the HTC register table. This + * will yield us the value of different int status + * registers and the lookahead registers. + * + * length = sizeof(int_status) + sizeof(cpu_int_status) + * + sizeof(error_int_status) + + * sizeof(counter_int_status) + + * sizeof(mbox_frame) + sizeof(rx_lkahd_valid) + * + sizeof(hole) + sizeof(rx_lkahd) + + * sizeof(int_status_en) + + * sizeof(cpu_int_status_en) + + * sizeof(err_int_status_en) + + * sizeof(cntr_int_status_en); + */ + status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS, + (u8 *) &dev->irq_proc_reg, + sizeof(dev->irq_proc_reg), + HIF_RD_SYNC_BYTE_INC); + if (status) + goto out; + + if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ)) + ath6kl_dump_registers(dev, &dev->irq_proc_reg, + &dev->irq_en_reg); + + /* Update only those registers that are enabled */ + host_int_status = dev->irq_proc_reg.host_int_status & + dev->irq_en_reg.int_status_en; + + /* Look at mbox status */ + if (host_int_status & htc_mbox) { + /* + * Mask out pending mbox value, we use "lookAhead as + * the real flag for mbox processing. + */ + host_int_status &= ~htc_mbox; + if (dev->irq_proc_reg.rx_lkahd_valid & + htc_mbox) { + rg = &dev->irq_proc_reg; + lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); + if (!lk_ahd) + ath6kl_err("lookAhead is zero!\n"); + } + } + } + + if (!host_int_status && !lk_ahd) { + *done = true; + goto out; + } + + if (lk_ahd) { + int fetched = 0; + + ath6kl_dbg(ATH6KL_DBG_IRQ, + "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd); + /* + * Mailbox Interrupt, the HTC layer may issue async + * requests to empty the mailbox. When emptying the recv + * mailbox we use the async handler above called from the + * completion routine of the callers read request. This can + * improve performance by reducing context switching when + * we rapidly pull packets. + */ + status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt, + lk_ahd, &fetched); + if (status) + goto out; + + if (!fetched) + /* + * HTC could not pull any messages out due to lack + * of resources. + */ + dev->htc_cnxt->chk_irq_status_cnt = 0; + } + + /* now handle the rest of them */ + ath6kl_dbg(ATH6KL_DBG_IRQ, + "valid interrupt source(s) for other interrupts: 0x%x\n", + host_int_status); + + if (MS(HOST_INT_STATUS_CPU, host_int_status)) { + /* CPU Interrupt */ + status = ath6kl_hif_proc_cpu_intr(dev); + if (status) + goto out; + } + + if (MS(HOST_INT_STATUS_ERROR, host_int_status)) { + /* Error Interrupt */ + status = ath6kl_hif_proc_err_intr(dev); + if (status) + goto out; + } + + if (MS(HOST_INT_STATUS_COUNTER, host_int_status)) + /* Counter Interrupt */ + status = ath6kl_hif_proc_counter_intr(dev); + +out: +#ifdef BYPASS_IRQ_RECHECK + /* + * An optimization to bypass reading the IRQ status registers + * unecessarily which can re-wake the target, if upper layers + * determine that we are in a low-throughput mode, we can rely on + * taking another interrupt rather than re-checking the status + * registers which can re-wake the target. + * + * NOTE : for host interfaces that makes use of detecting pending + * mbox messages at hif can not use this optimization due to + * possible side effects, SPI requires the host to drain all + * messages from the mailbox before exiting the ISR routine. + */ + + ath6kl_dbg(ATH6KL_DBG_IRQ, + "bypassing irq status re-check, forcing done\n"); + + if (!dev->htc_cnxt->chk_irq_status_cnt) + *done = true; +#else + /* + * The benefit is that if packet happens to arrive during that extra + * read (bursty traffic), we maybe able to catch the packet and + * continue RX without waiting for the next interrupt. This seems to + * improve throughput on slower processors. + * + * NOTE : There might be a chance to hold CPU time to let other SDIO + * devices starvation. In sdio_irq.c, the SDIO irq thread run each + * SDIO device¡¦s call-back one bye one. + */ +#endif + + ath6kl_dbg(ATH6KL_DBG_IRQ, + "proc_pending_irqs: (done:%d, status=%d\n", *done, status); + + return status; +} + +/* interrupt handler, kicks off all interrupt processing */ +int ath6kl_hif_intr_bh_handler(struct ath6kl *ar) +{ + struct ath6kl_device *dev = ar->htc_target->dev; + unsigned long timeout; + int status = 0; + bool done = false; + + /* + * Reset counter used to flag a re-scan of IRQ status registers on + * the target. + */ + dev->htc_cnxt->chk_irq_status_cnt = 0; + + /* + * IRQ processing is synchronous, interrupt status registers can be + * re-read. + */ + timeout = jiffies + msecs_to_jiffies(ATH6KL_HIF_COMMUNICATION_TIMEOUT); + while (time_before(jiffies, timeout) && !done) { + status = proc_pending_irqs(dev, &done); + if (status) + break; + } + + return status; +} + +static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev) +{ + struct ath6kl_irq_enable_reg regs; + int status; + + spin_lock_bh(&dev->lock); + + /* Enable all but ATH6KL CPU interrupts */ + dev->irq_en_reg.int_status_en = + SM(INT_STATUS_ENABLE_ERROR, 0x01) | + SM(INT_STATUS_ENABLE_CPU, 0x01) | + SM(INT_STATUS_ENABLE_COUNTER, 0x01); + + /* + * NOTE: There are some cases where HIF can do detection of + * pending mbox messages which is disabled now. + */ + dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); + + /* Set up the CPU Interrupt status Register */ + dev->irq_en_reg.cpu_int_status_en = 0; + + /* Set up the Error Interrupt status Register */ + dev->irq_en_reg.err_int_status_en = + SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) | + SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1); + + /* + * Enable Counter interrupt status register to get fatal errors for + * debugging. + */ + dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT, + ATH6KL_TARGET_DEBUG_INTR_MASK); + memcpy(®s, &dev->irq_en_reg, sizeof(regs)); + + spin_unlock_bh(&dev->lock); + + status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, + ®s.int_status_en, sizeof(regs), + HIF_WR_SYNC_BYTE_INC); + + if (status) + ath6kl_err("failed to update interrupt ctl reg err: %d\n", + status); + + return status; +} + +int ath6kl_hif_disable_intrs(struct ath6kl_device *dev) +{ + struct ath6kl_irq_enable_reg regs; + + spin_lock_bh(&dev->lock); + /* Disable all interrupts */ + dev->irq_en_reg.int_status_en = 0; + dev->irq_en_reg.cpu_int_status_en = 0; + dev->irq_en_reg.err_int_status_en = 0; + dev->irq_en_reg.cntr_int_status_en = 0; + memcpy(®s, &dev->irq_en_reg, sizeof(regs)); + spin_unlock_bh(&dev->lock); + + return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, + ®s.int_status_en, sizeof(regs), + HIF_WR_SYNC_BYTE_INC); +} + +/* enable device interrupts */ +int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev) +{ + int status = 0; + + /* + * Make sure interrupt are disabled before unmasking at the HIF + * layer. The rationale here is that between device insertion + * (where we clear the interrupts the first time) and when HTC + * is finally ready to handle interrupts, other software can perform + * target "soft" resets. The ATH6KL interrupt enables reset back to an + * "enabled" state when this happens. + */ + ath6kl_hif_disable_intrs(dev); + + /* unmask the host controller interrupts */ + ath6kl_hif_irq_enable(dev->ar); + status = ath6kl_hif_enable_intrs(dev); + + return status; +} + +/* disable all device interrupts */ +int ath6kl_hif_mask_intrs(struct ath6kl_device *dev) +{ + /* + * Mask the interrupt at the HIF layer to avoid any stray interrupt + * taken while we zero out our shadow registers in + * ath6kl_hif_disable_intrs(). + */ + ath6kl_hif_irq_disable(dev->ar); + + return ath6kl_hif_disable_intrs(dev); +} + +int ath6kl_hif_setup(struct ath6kl_device *dev) +{ + int status = 0; + + spin_lock_init(&dev->lock); + + /* + * NOTE: we actually get the block size of a mailbox other than 0, + * for SDIO the block size on mailbox 0 is artificially set to 1. + * So we use the block size that is set for the other 3 mailboxes. + */ + dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size; + + /* must be a power of 2 */ + if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) { + WARN_ON(1); + status = -EINVAL; + goto fail_setup; + } + + /* assemble mask, used for padding to a block */ + dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1; + + ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n", + dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr); + + /* usb doesn't support enabling interrupts */ + /* FIXME: remove check once USB support is implemented */ + if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) + return 0; + + status = ath6kl_hif_disable_intrs(dev); + +fail_setup: + return status; + +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/hif.h b/drivers/net/wireless/ath/ath6kl-3.5/hif.h new file mode 100644 index 000000000000..700e1e02ec7c --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/hif.h @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 HIF_H +#define HIF_H + +#include "common.h" +#include "core.h" + +#include + +#define BUS_REQUEST_MAX_NUM 64 +#define HIF_MBOX_BLOCK_SIZE 128 +#define HIF_MBOX0_BLOCK_SIZE 1 + +#define HIF_DMA_BUFFER_SIZE (32 * 1024) +#define CMD53_FIXED_ADDRESS 1 +#define CMD53_INCR_ADDRESS 2 + +#define MAX_SCATTER_REQUESTS 4 +#define MAX_SCATTER_ENTRIES_PER_REQ 16 +#define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024) + +#define MANUFACTURER_ID_AR6003_BASE 0x300 +#define MANUFACTURER_ID_AR6004_BASE 0x400 +/*#define MANUFACTURER_ID_AR6006_BASE 0x600*/ +#define MANUFACTURER_ID_AR6006_BASE 0xD00 + /* SDIO manufacturer ID and Codes */ +#define MANUFACTURER_ID_ATH6KL_BASE_MASK 0xFF00 +#define MANUFACTURER_CODE 0x271 /* Atheros */ + +/* Mailbox address in SDIO address space */ +#define HIF_MBOX_BASE_ADDR 0x800 +#define HIF_MBOX_WIDTH 0x800 + +#define HIF_MBOX_END_ADDR (HTC_MAILBOX_NUM_MAX * HIF_MBOX_WIDTH - 1) + +/* version 1 of the chip has only a 12K extended mbox range */ +#define HIF_MBOX0_EXT_BASE_ADDR 0x4000 +#define HIF_MBOX0_EXT_WIDTH (12*1024) + +/* GMBOX addresses */ +#define HIF_GMBOX_BASE_ADDR 0x7000 +#define HIF_GMBOX_WIDTH 0x4000 + +/* interrupt mode register */ +#define CCCR_SDIO_IRQ_MODE_REG 0xF0 + +/* mode to enable special 4-bit interrupt assertion without clock */ +#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ (1 << 0) + +/* HTC runs over mailbox 0 */ +#define HTC_MAILBOX 0 + +#define ATH6KL_TARGET_DEBUG_INTR_MASK 0x01 + +/* FIXME: are these duplicates with MAX_SCATTER_ values in hif.h? */ +#define ATH6KL_SCATTER_ENTRIES_PER_REQ 16 +#define ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER (16 * 1024) +#define ATH6KL_SCATTER_REQS 4 + +#define ATH6KL_HIF_COMMUNICATION_TIMEOUT 1000 + +struct bus_request { + struct list_head list; + + /* request data */ + u32 address; + + u8 *buffer; + u32 length; + u32 request; + struct htc_packet *packet; + int status; + + /* this is a scatter request */ + struct hif_scatter_req *scat_req; +}; + +/* direction of transfer (read/write) */ +#define HIF_READ 0x00000001 +#define HIF_WRITE 0x00000002 +#define HIF_DIR_MASK (HIF_READ | HIF_WRITE) + +/* + * emode - This indicates the whether the command is to be executed in a + * blocking or non-blocking fashion (HIF_SYNCHRONOUS/ + * HIF_ASYNCHRONOUS). The read/write data paths in HTC have been + * implemented using the asynchronous mode allowing the the bus + * driver to indicate the completion of operation through the + * registered callback routine. The requirement primarily comes + * from the contexts these operations get called from (a driver's + * transmit context or the ISR context in case of receive). + * Support for both of these modes is essential. + */ +#define HIF_SYNCHRONOUS 0x00000010 +#define HIF_ASYNCHRONOUS 0x00000020 +#define HIF_EMODE_MASK (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS) + +/* + * dmode - An interface may support different kinds of commands based on + * the tradeoff between the amount of data it can carry and the + * setup time. Byte and Block modes are supported (HIF_BYTE_BASIS/ + * HIF_BLOCK_BASIS). In case of latter, the data is rounded off + * to the nearest block size by padding. The size of the block is + * configurable at compile time using the HIF_BLOCK_SIZE and is + * negotiated with the target during initialization after the + * ATH6KL interrupts are enabled. + */ +#define HIF_BYTE_BASIS 0x00000040 +#define HIF_BLOCK_BASIS 0x00000080 +#define HIF_DMODE_MASK (HIF_BYTE_BASIS | HIF_BLOCK_BASIS) + +/* + * amode - This indicates if the address has to be incremented on ATH6KL + * after every read/write operation (HIF?FIXED_ADDRESS/ + * HIF_INCREMENTAL_ADDRESS). + */ +#define HIF_FIXED_ADDRESS 0x00000100 +#define HIF_INCREMENTAL_ADDRESS 0x00000200 +#define HIF_AMODE_MASK (HIF_FIXED_ADDRESS | HIF_INCREMENTAL_ADDRESS) + +#define HIF_WR_ASYNC_BYTE_INC \ + (HIF_WRITE | HIF_ASYNCHRONOUS | \ + HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS) + +#define HIF_WR_ASYNC_BLOCK_INC \ + (HIF_WRITE | HIF_ASYNCHRONOUS | \ + HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS) + +#define HIF_WR_SYNC_BYTE_FIX \ + (HIF_WRITE | HIF_SYNCHRONOUS | \ + HIF_BYTE_BASIS | HIF_FIXED_ADDRESS) + +#define HIF_WR_SYNC_BYTE_INC \ + (HIF_WRITE | HIF_SYNCHRONOUS | \ + HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS) + +#define HIF_WR_SYNC_BLOCK_INC \ + (HIF_WRITE | HIF_SYNCHRONOUS | \ + HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS) + +#define HIF_RD_SYNC_BYTE_INC \ + (HIF_READ | HIF_SYNCHRONOUS | \ + HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS) + +#define HIF_RD_SYNC_BYTE_FIX \ + (HIF_READ | HIF_SYNCHRONOUS | \ + HIF_BYTE_BASIS | HIF_FIXED_ADDRESS) + +#define HIF_RD_ASYNC_BLOCK_FIX \ + (HIF_READ | HIF_ASYNCHRONOUS | \ + HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS) + +#define HIF_RD_SYNC_BLOCK_FIX \ + (HIF_READ | HIF_SYNCHRONOUS | \ + HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS) + +struct hif_scatter_item { + u8 *buf; + int len; + struct htc_packet *packet; +}; + +struct hif_scatter_req { + struct list_head list; + /* address for the read/write operation */ + u32 addr; + + /* request flags */ + u32 req; + + /* total length of entire transfer */ + u32 len; + + bool virt_scat; + + void (*complete) (struct htc_target *, struct hif_scatter_req *); + int status; + int scat_entries; + + struct bus_request *busrequest; + struct scatterlist *sgentries; + + /* bounce buffer for upper layers to copy to/from */ + u8 *virt_dma_buf; + + u32 scat_q_depth; + + struct hif_scatter_item scat_list[1]; +}; + +struct ath6kl_irq_proc_registers { + u8 host_int_status; + u8 cpu_int_status; + u8 error_int_status; + u8 counter_int_status; + u8 mbox_frame; + u8 rx_lkahd_valid; + u8 host_int_status2; + u8 gmbox_rx_avail; + __le32 rx_lkahd[2]; + __le32 rx_gmbox_lkahd_alias[2]; +} __packed; + +struct ath6kl_irq_enable_reg { + u8 int_status_en; + u8 cpu_int_status_en; + u8 err_int_status_en; + u8 cntr_int_status_en; +} __packed; + +/** + * @brief List of callbacks - filled in by HTC. + */ +struct ath6kl_hif_pipe_callbacks { + int (*tx_completion) (struct htc_target *context, struct sk_buff * skb); + int (*rx_completion) (struct htc_target *context, + struct sk_buff *skb, u8 pipe); + void (*tx_resource_available) (struct htc_target *context, u8 pipe); +}; + +struct ath6kl_device { + spinlock_t lock; + struct ath6kl_irq_proc_registers irq_proc_reg; + struct ath6kl_irq_enable_reg irq_en_reg; + struct htc_target *htc_cnxt; + struct ath6kl *ar; +}; + +struct ath6kl_hif_ops { + int (*read_write_sync)(struct ath6kl *ar, u32 addr, u8 *buf, + u32 len, u32 request); + int (*write_async)(struct ath6kl *ar, u32 address, u8 *buffer, + u32 length, u32 request, struct htc_packet *packet); + + void (*irq_enable)(struct ath6kl *ar); + void (*irq_disable)(struct ath6kl *ar); + + struct hif_scatter_req *(*scatter_req_get)(struct ath6kl *ar); + void (*scatter_req_add)(struct ath6kl *ar, + struct hif_scatter_req *s_req); + int (*enable_scatter)(struct ath6kl *ar); + int (*scat_req_rw) (struct ath6kl *ar, + struct hif_scatter_req *scat_req); + void (*cleanup_scatter)(struct ath6kl *ar); + int (*suspend)(struct ath6kl *ar, struct cfg80211_wowlan *wow); + int (*resume)(struct ath6kl *ar); + int (*diag_read32)(struct ath6kl *ar, u32 address, u32 *value); + int (*diag_write32)(struct ath6kl *ar, u32 address, __le32 value); + int (*bmi_read)(struct ath6kl *ar, u8 *buf, u32 len); + int (*bmi_write)(struct ath6kl *ar, u8 *buf, u32 len); + int (*power_on)(struct ath6kl *ar); + int (*power_off)(struct ath6kl *ar); + void (*stop)(struct ath6kl *ar); + int (*get_stat)(struct ath6kl *ar, u8 *buf, int buf_len); + void (*pipe_register_callback)(struct ath6kl *ar, + void *htc_context, struct ath6kl_hif_pipe_callbacks *callbacks); + int (*pipe_send)(struct ath6kl *ar, u8 pipe, struct sk_buff *hdr_buf, + struct sk_buff *buf); + void (*pipe_get_default)(struct ath6kl *ar, u8 *pipe_ul, u8 *pipe_dl); + int (*pipe_map_service)(struct ath6kl *ar, u16 service_id, u8 *pipe_ul, + u8 *pipe_dl); + u16 (*pipe_get_free_queue_number)(struct ath6kl *ar, u8 pipe); + int (*pipe_send_bundle)(struct ath6kl *ar, u8 pid, + struct sk_buff **msg_bundle, int num_msgs); + u16 (*pipe_get_max_queue_number)(struct ath6kl *ar, u8 pipe); + int (*pipe_set_max_sche)(struct ath6kl *ar, u32 max_sche_tx, + u32 max_sche_rx); + int (*diag_warm_reset)(struct ath6kl *ar); +#ifdef CONFIG_HAS_EARLYSUSPEND + void (*early_suspend)(struct ath6kl *ar); + void (*late_resume)(struct ath6kl *ar); +#endif + int (*bus_config)(struct ath6kl *ar); + +#ifdef USB_AUTO_SUSPEND + int (*auto_pm_get_usage_cnt)(struct ath6kl *ar); + void (*auto_pm_disable)(struct ath6kl *ar); + void (*auto_pm_enable)(struct ath6kl *ar); + void (*auto_pm_turnon)(struct ath6kl *ar); + void (*auto_pm_turnoff)(struct ath6kl *ar); +#endif +}; + +int ath6kl_hif_setup(struct ath6kl_device *dev); +int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev); +int ath6kl_hif_mask_intrs(struct ath6kl_device *dev); +int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, + u32 *lk_ahd, int timeout); +int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx); +int ath6kl_hif_disable_intrs(struct ath6kl_device *dev); + +int ath6kl_hif_rw_comp_handler(void *context, int status); +int ath6kl_hif_intr_bh_handler(struct ath6kl *ar); + +/* Scatter Function and Definitions */ +int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev, + struct hif_scatter_req *scat_req, bool read); + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/htc-ops.h b/drivers/net/wireless/ath/ath6kl-3.5/htc-ops.h new file mode 100644 index 000000000000..b6ee0b9e71ad --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/htc-ops.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 HTC_OPS_H +#define HTC_OPS_H + +#include "htc.h" +#include "debug.h" + +static inline void *ath6kl_htc_create(struct ath6kl *ar) +{ + return ar->htc_ops->create(ar); +} + +static inline int ath6kl_htc_wait_target(struct htc_target *target) +{ + return target->dev->ar->htc_ops->wait_target(target); +} + +static inline int ath6kl_htc_start(struct htc_target *target) +{ + return target->dev->ar->htc_ops->start(target); +} + +static inline int ath6kl_htc_conn_service(struct htc_target *target, + struct htc_service_connect_req *req, + struct htc_service_connect_resp *resp) +{ + return target->dev->ar->htc_ops->conn_service(target, req, resp); +} + +static inline int ath6kl_htc_tx(struct htc_target *target, + struct htc_packet *packet) +{ + return target->dev->ar->htc_ops->tx(target, packet); +} + +static inline void ath6kl_htc_stop(struct htc_target *target) +{ + return target->dev->ar->htc_ops->stop(target); +} + +static inline void ath6kl_htc_cleanup(struct htc_target *target) +{ + return target->dev->ar->htc_ops->cleanup(target); +} + +static inline void ath6kl_htc_flush_txep(struct htc_target *target, + enum htc_endpoint_id endpoint, u16 tag) +{ + return target->dev->ar->htc_ops->flush_txep(target, endpoint, tag); +} + +static inline void ath6kl_htc_flush_rx_buf(struct htc_target *target) +{ + return target->dev->ar->htc_ops->flush_rx_buf(target); +} + +static inline void ath6kl_htc_indicate_activity_change( + struct htc_target *target, enum htc_endpoint_id endpoint, bool active) +{ + return target->dev->ar->htc_ops->indicate_activity_change(target, + endpoint, active); +} + +static inline int ath6kl_htc_get_rxbuf_num(struct htc_target *target, + enum htc_endpoint_id endpoint) +{ + return target->dev->ar->htc_ops->get_rxbuf_num(target, endpoint); +} + +static inline int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, + struct list_head *pktq) +{ + return target->dev->ar->htc_ops->add_rxbuf_multiple(target, pktq); +} + +static inline int ath6kl_htc_credit_setup(struct htc_target *target, + struct ath6kl_htc_credit_info *cred_info) +{ + return target->dev->ar->htc_ops->credit_setup(target, cred_info); +} + +static inline int ath6kl_htc_stat(struct htc_target *target, + u8 *buf, int buf_len) +{ + return target->dev->ar->htc_ops->get_stat(target, buf, buf_len); +} + +static inline int ath6kl_htc_stop_netif_queue_full(struct htc_target *target) +{ + return target->dev->ar->htc_ops->stop_netif_queue_full(target); +} + +static inline int ath6kl_htc_wmm_schedule_change(struct htc_target *target, + bool change) +{ + return target->dev->ar->htc_ops->indicate_wmm_schedule_change(target, + change); +} + +static inline int ath6kl_htc_change_credit_bypass(struct htc_target *target, + u8 traffic_class) +{ + return target->dev->ar->htc_ops->change_credit_bypass(target, + traffic_class); +} + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/htc.c b/drivers/net/wireless/ath/ath6kl-3.5/htc.c new file mode 100644 index 000000000000..5c5ee644e4de --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/htc.c @@ -0,0 +1,3088 @@ +/* + * Copyright (c) 2007-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "hif.h" +#include "debug.h" +#include "hif-ops.h" +#include + +#define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) + +/* threshold to re-enable Tx bundling for an AC*/ +#define TX_RESUME_BUNDLE_THRESHOLD 1500 + +static void ath6kl_htc_mbox_cleanup(struct htc_target *target); +static void ath6kl_htc_mbox_stop(struct htc_target *target); +static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, + struct list_head *pkt_queue); +static void ath6kl_htc_set_credit_dist(struct htc_target *target, + struct ath6kl_htc_credit_info *cred_info, + u16 svc_pri_order[], int len); + +/* Functions for Tx credit handling */ +static void ath6kl_credit_deposit(struct ath6kl_htc_credit_info *cred_info, + struct htc_endpoint_credit_dist *ep_dist, + int credits) +{ + ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit deposit ep %d credits %d\n", + ep_dist->endpoint, credits); + + ep_dist->credits += credits; + ep_dist->cred_assngd += credits; + cred_info->cur_free_credits -= credits; +} + +static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, + struct list_head *ep_list, + int tot_credits) +{ + struct htc_endpoint_credit_dist *cur_ep_dist; + int count; + + ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit init total %d\n", tot_credits); + + cred_info->cur_free_credits = tot_credits; + cred_info->total_avail_credits = tot_credits; + + list_for_each_entry(cur_ep_dist, ep_list, list) { + if (cur_ep_dist->endpoint == ENDPOINT_0) + continue; + + cur_ep_dist->cred_min = cur_ep_dist->cred_per_msg; + + if (tot_credits > 4) { + if ((cur_ep_dist->svc_id == WMI_DATA_BK_SVC) || + (cur_ep_dist->svc_id == WMI_DATA_BE_SVC)) { + ath6kl_credit_deposit(cred_info, + cur_ep_dist, + cur_ep_dist->cred_min); + cur_ep_dist->dist_flags |= HTC_EP_ACTIVE; + } + } + + if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) { + ath6kl_credit_deposit(cred_info, cur_ep_dist, + cur_ep_dist->cred_min); + /* + * Control service is always marked active, it + * never goes inactive EVER. + */ + cur_ep_dist->dist_flags |= HTC_EP_ACTIVE; + } + /* + * Streams have to be created (explicit | implicit) for all + * kinds of traffic. BE endpoints are also inactive in the + * beginning. When BE traffic starts it creates implicit + * streams that redistributes credits. + * + * Note: all other endpoints have minimums set but are + * initially given NO credits. credits will be distributed + * as traffic activity demands + */ + } + /* + * For ath6kl_credit_seek function, + * it use list_for_each_entry_reverse to walk around the whole ep list. + * Therefore assign this lowestpri_ep_dist after walk around the ep_list + */ + cred_info->lowestpri_ep_dist = cur_ep_dist->list; + + WARN_ON(cred_info->cur_free_credits <= 0); + + list_for_each_entry(cur_ep_dist, ep_list, list) { + if (cur_ep_dist->endpoint == ENDPOINT_0) + continue; + + if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) + cur_ep_dist->cred_norm = cur_ep_dist->cred_per_msg; + else { + /* + * For the remaining data endpoints, we assume that + * each cred_per_msg are the same. We use a simple + * calculation here, we take the remaining credits + * and determine how many max messages this can + * cover and then set each endpoint's normal value + * equal to 3/4 this amount. + */ + count = (cred_info->cur_free_credits / + cur_ep_dist->cred_per_msg) + * cur_ep_dist->cred_per_msg; + count = (count * 3) >> 2; + count = max(count, cur_ep_dist->cred_per_msg); + cur_ep_dist->cred_norm = count; + + } + + ath6kl_dbg(ATH6KL_DBG_CREDIT, + "credit ep %d svc_id %d credits %d per_msg %d norm %d min %d\n", + cur_ep_dist->endpoint, + cur_ep_dist->svc_id, + cur_ep_dist->credits, + cur_ep_dist->cred_per_msg, + cur_ep_dist->cred_norm, + cur_ep_dist->cred_min); + } +} + +/* initialize and setup credit distribution */ +static int ath6kl_htc_mbox_credit_setup(struct htc_target *htc_target, + struct ath6kl_htc_credit_info *cred_info) +{ + u16 servicepriority[5]; + + memset(cred_info, 0, sizeof(struct ath6kl_htc_credit_info)); + + servicepriority[0] = WMI_CONTROL_SVC; /* highest */ + servicepriority[1] = WMI_DATA_VO_SVC; + servicepriority[2] = WMI_DATA_VI_SVC; + servicepriority[3] = WMI_DATA_BE_SVC; + servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */ + + /* set priority list */ + ath6kl_htc_set_credit_dist(htc_target, cred_info, servicepriority, 5); + + return 0; +} + +/* reduce an ep's credits back to a set limit */ +static void ath6kl_credit_reduce(struct ath6kl_htc_credit_info *cred_info, + struct htc_endpoint_credit_dist *ep_dist, + int limit) +{ + int credits; + + ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit reduce ep %d limit %d\n", + ep_dist->endpoint, limit); + + ep_dist->cred_assngd = limit; + + if (ep_dist->credits <= limit) + return; + + credits = ep_dist->credits - limit; + ep_dist->credits -= credits; + cred_info->cur_free_credits += credits; +} + +static void ath6kl_credit_update(struct ath6kl_htc_credit_info *cred_info, + struct list_head *epdist_list) +{ + struct htc_endpoint_credit_dist *cur_dist_list; + + list_for_each_entry(cur_dist_list, epdist_list, list) { + if (cur_dist_list->endpoint == ENDPOINT_0) + continue; + + if (cur_dist_list->cred_to_dist > 0) { + cur_dist_list->credits += + cur_dist_list->cred_to_dist; + cur_dist_list->cred_to_dist = 0; + if (cur_dist_list->credits > + cur_dist_list->cred_assngd) + ath6kl_credit_reduce(cred_info, + cur_dist_list, + cur_dist_list->cred_assngd); + + if (cur_dist_list->credits > + cur_dist_list->cred_norm) + ath6kl_credit_reduce(cred_info, cur_dist_list, + cur_dist_list->cred_norm); + + if (!(cur_dist_list->dist_flags & HTC_EP_ACTIVE)) { + if (cur_dist_list->txq_depth == 0) + ath6kl_credit_reduce(cred_info, + cur_dist_list, 0); + } + } + } +} + +/* + * HTC has an endpoint that needs credits, ep_dist is the endpoint in + * question. + */ +static void ath6kl_credit_seek(struct ath6kl_htc_credit_info *cred_info, + struct htc_endpoint_credit_dist *ep_dist) +{ + struct htc_endpoint_credit_dist *curdist_list; + int credits = 0; + int need; + + if (ep_dist->svc_id == WMI_CONTROL_SVC) + goto out; + /* + * For WMM test case 5.2.27 step8 on 1x1 solution, + * it has two streams VI and VO. + * If VO don't seek credit from VI, + * VO throughput can't pass the criterion. + */ + if (ep_dist->svc_id == WMI_DATA_VI_SVC) { + if (ep_dist->cred_alloc_max != 0) { + if (ep_dist->cred_assngd >= ep_dist->cred_alloc_max) + goto out; + } + + if ((ep_dist->cred_assngd >= ep_dist->cred_norm)) + goto out; + } + + /* + * For all other services, we follow a simple algorithm of: + * + * 1. checking the free pool for credits + * 2. checking lower priority endpoints for credits to take + */ + + credits = min(cred_info->cur_free_credits, ep_dist->seek_cred); + + if (credits >= ep_dist->seek_cred) + goto out; + + /* + * We don't have enough in the free pool, try taking away from + * lower priority services The rule for taking away credits: + * + * 1. Only take from lower priority endpoints + * 2. Only take what is allocated above the minimum (never + * starve an endpoint completely) + * 3. Only take what you need. + */ + + list_for_each_entry_reverse(curdist_list, + &cred_info->lowestpri_ep_dist, + list) { + if (curdist_list == ep_dist) + break; + + need = ep_dist->seek_cred - cred_info->cur_free_credits; + + if ((curdist_list->cred_assngd - need) >= + curdist_list->cred_min) { + /* + * The current one has been allocated more than + * it's minimum and it has enough credits assigned + * above it's minimum to fulfill our need try to + * take away just enough to fulfill our need. + */ + ath6kl_credit_reduce(cred_info, curdist_list, + curdist_list->cred_assngd - need); + + if (cred_info->cur_free_credits >= + ep_dist->seek_cred) + break; + } + + if (curdist_list->endpoint == ENDPOINT_0) + break; + } + + credits = min(cred_info->cur_free_credits, ep_dist->seek_cred); + +out: + /* did we find some credits? */ + if (credits) + ath6kl_credit_deposit(cred_info, ep_dist, credits); + + ep_dist->seek_cred = 0; +} + +/* redistribute credits based on activity change */ +static void ath6kl_credit_redistribute(struct ath6kl_htc_credit_info *info, + struct list_head *ep_dist_list) +{ + struct htc_endpoint_credit_dist *curdist_list; + + list_for_each_entry(curdist_list, ep_dist_list, list) { + if (curdist_list->endpoint == ENDPOINT_0) + continue; + + if ((curdist_list->svc_id == WMI_DATA_BK_SVC) || + (curdist_list->svc_id == WMI_DATA_BE_SVC)) + curdist_list->dist_flags |= HTC_EP_ACTIVE; + + if ((curdist_list->svc_id != WMI_CONTROL_SVC) && + !(curdist_list->dist_flags & HTC_EP_ACTIVE)) { + if (curdist_list->txq_depth == 0) + ath6kl_credit_reduce(info, curdist_list, 0); + else + ath6kl_credit_reduce(info, + curdist_list, + curdist_list->cred_min); + } + } +} + +/* + * + * This function is invoked whenever endpoints require credit + * distributions. A lock is held while this function is invoked, this + * function shall NOT block. The ep_dist_list is a list of distribution + * structures in prioritized order as defined by the call to the + * htc_set_credit_dist() api. + */ +static void ath6kl_credit_distribute(struct ath6kl_htc_credit_info *cred_info, + struct list_head *ep_dist_list, + enum htc_credit_dist_reason reason) +{ + switch (reason) { + case HTC_CREDIT_DIST_SEND_COMPLETE: + ath6kl_credit_update(cred_info, ep_dist_list); + break; + case HTC_CREDIT_DIST_ACTIVITY_CHANGE: + ath6kl_credit_redistribute(cred_info, ep_dist_list); + break; + default: + break; + } + + WARN_ON(cred_info->cur_free_credits > cred_info->total_avail_credits); + WARN_ON(cred_info->cur_free_credits < 0); +} + +static void ath6kl_htc_tx_buf_align(u8 **buf, unsigned long len) +{ + u8 *align_addr; + + if (!IS_ALIGNED((unsigned long) *buf, 4)) { + align_addr = PTR_ALIGN(*buf - 4, 4); + memmove(align_addr, *buf, len); + *buf = align_addr; + } +} + +static void ath6kl_htc_tx_prep_pkt(struct htc_packet *packet, u8 flags, + int ctrl0, int ctrl1) +{ + struct htc_frame_hdr *hdr; + + packet->buf -= HTC_HDR_LENGTH; + hdr = (struct htc_frame_hdr *)packet->buf; + + /* Endianess? */ + put_unaligned((u16)packet->act_len, &hdr->payld_len); + hdr->payld_len = cpu_to_le16(hdr->payld_len); + hdr->flags = flags; + hdr->eid = packet->endpoint; + hdr->ctrl[0] = ctrl0; + hdr->ctrl[1] = ctrl1; +} + +static void htc_reclaim_txctrl_buf(struct htc_target *target, + struct htc_packet *pkt) +{ + spin_lock_bh(&target->htc_lock); + list_add_tail(&pkt->list, &target->free_ctrl_txbuf); + spin_unlock_bh(&target->htc_lock); +} + +static struct htc_packet *htc_get_control_buf(struct htc_target *target, + bool tx) +{ + struct htc_packet *packet = NULL; + struct list_head *buf_list; + + buf_list = tx ? &target->free_ctrl_txbuf : &target->free_ctrl_rxbuf; + + spin_lock_bh(&target->htc_lock); + + if (list_empty(buf_list)) { + spin_unlock_bh(&target->htc_lock); + return NULL; + } + + packet = list_first_entry(buf_list, struct htc_packet, list); + list_del(&packet->list); + spin_unlock_bh(&target->htc_lock); + + if (tx) + packet->buf = packet->buf_start + HTC_HDR_LENGTH; + + return packet; +} + +static void htc_tx_comp_update(struct htc_target *target, + struct htc_endpoint *endpoint, + struct htc_packet *packet) +{ + packet->completion = NULL; + packet->buf += HTC_HDR_LENGTH; + + if (!packet->status) + return; + + ath6kl_err("req failed (status:%d, ep:%d, len:%d creds:%d)\n", + packet->status, packet->endpoint, packet->act_len, + packet->info.tx.cred_used); + + /* on failure to submit, reclaim credits for this packet */ + spin_lock_bh(&target->tx_lock); + endpoint->cred_dist.cred_to_dist += + packet->info.tx.cred_used; + endpoint->cred_dist.txq_depth = get_queue_depth(&endpoint->txq); + + ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx ctxt 0x%p dist 0x%p\n", + target->credit_info, &target->cred_dist_list); + + ath6kl_credit_distribute(target->credit_info, + &target->cred_dist_list, + HTC_CREDIT_DIST_SEND_COMPLETE); + + spin_unlock_bh(&target->tx_lock); +} + +static void htc_tx_complete(struct htc_endpoint *endpoint, + struct list_head *txq) +{ + if (list_empty(txq)) + return; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx complete ep %d pkts %d\n", + endpoint->eid, get_queue_depth(txq)); + + ath6kl_tx_complete(endpoint->target, txq); +} + +static void htc_tx_comp_handler(struct htc_target *target, + struct htc_packet *packet) +{ + struct htc_endpoint *endpoint = &target->endpoint[packet->endpoint]; + struct list_head container; + + ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx complete seqno %d\n", + packet->info.tx.seqno); + + htc_tx_comp_update(target, endpoint, packet); + INIT_LIST_HEAD(&container); + list_add_tail(&packet->list, &container); + /* do completion */ + htc_tx_complete(endpoint, &container); +} + +static void htc_async_tx_scat_complete(struct htc_target *target, + struct hif_scatter_req *scat_req) +{ + struct htc_endpoint *endpoint; + struct htc_packet *packet; + struct list_head tx_compq; + int i; + + INIT_LIST_HEAD(&tx_compq); + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx scat complete len %d entries %d\n", + scat_req->len, scat_req->scat_entries); + + if (scat_req->status) + ath6kl_err("send scatter req failed: %d\n", scat_req->status); + + packet = scat_req->scat_list[0].packet; + endpoint = &target->endpoint[packet->endpoint]; + + /* walk through the scatter list and process */ + for (i = 0; i < scat_req->scat_entries; i++) { + packet = scat_req->scat_list[i].packet; + if (!packet) { + WARN_ON(1); + return; + } + + packet->status = scat_req->status; + htc_tx_comp_update(target, endpoint, packet); + list_add_tail(&packet->list, &tx_compq); + } + + /* free scatter request */ + hif_scatter_req_add(target->dev->ar, scat_req); + + /* complete all packets */ + htc_tx_complete(endpoint, &tx_compq); +} + +static int ath6kl_htc_tx_issue(struct htc_target *target, + struct htc_packet *packet) +{ + int status; + bool sync = false; + u32 padded_len, send_len; + + if (!packet->completion) + sync = true; + + send_len = packet->act_len + HTC_HDR_LENGTH; + + padded_len = CALC_TXRX_PADDED_LEN(target, send_len); + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx issue len %d seqno %d padded_len %d mbox 0x%X %s\n", + send_len, packet->info.tx.seqno, padded_len, + target->dev->ar->mbox_info.htc_addr, + sync ? "sync" : "async"); + + if (sync) { + status = hif_read_write_sync(target->dev->ar, + target->dev->ar->mbox_info.htc_addr, + packet->buf, padded_len, + HIF_WR_SYNC_BLOCK_INC); + + packet->status = status; + packet->buf += HTC_HDR_LENGTH; + } else + status = hif_write_async(target->dev->ar, + target->dev->ar->mbox_info.htc_addr, + packet->buf, padded_len, + HIF_WR_ASYNC_BLOCK_INC, packet); + + return status; +} + +static int htc_check_credits(struct htc_target *target, + struct htc_endpoint *ep, u8 *flags, + enum htc_endpoint_id eid, unsigned int len, + int *req_cred) +{ + + *req_cred = (len > target->tgt_cred_sz) ? + DIV_ROUND_UP(len, target->tgt_cred_sz) : 1; + + ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit check need %d got %d\n", + *req_cred, ep->cred_dist.credits); + + if (ep->cred_dist.credits < *req_cred) { + if (eid == ENDPOINT_0) + return -EINVAL; + + /* Seek more credits */ + ep->cred_dist.seek_cred = *req_cred - ep->cred_dist.credits; + + ath6kl_credit_seek(target->credit_info, &ep->cred_dist); + + ep->cred_dist.seek_cred = 0; + + if (ep->cred_dist.credits < *req_cred) { + ath6kl_dbg(ATH6KL_DBG_CREDIT, + "credit not found for ep %d\n", + eid); + return -EINVAL; + } + } + + ep->cred_dist.credits -= *req_cred; + ep->ep_st.cred_cosumd += *req_cred; + + /* When we are getting low on credits, ask for more */ + if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) { + ep->cred_dist.seek_cred = + ep->cred_dist.cred_per_msg - ep->cred_dist.credits; + + ath6kl_credit_seek(target->credit_info, &ep->cred_dist); + + /* see if we were successful in getting more */ + if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) { + /* tell the target we need credits ASAP! */ + *flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; + ep->ep_st.cred_low_indicate += 1; + ath6kl_dbg(ATH6KL_DBG_CREDIT, + "credit we need credits asap\n"); + } + } + + return 0; +} + +static void ath6kl_htc_tx_pkts_get(struct htc_target *target, + struct htc_endpoint *endpoint, + struct list_head *queue) +{ + int req_cred; + u8 flags; + struct htc_packet *packet; + unsigned int len; + + while (true) { + + flags = 0; + + if (list_empty(&endpoint->txq)) + break; + packet = list_first_entry(&endpoint->txq, struct htc_packet, + list); + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx got packet 0x%p queue depth %d\n", + packet, get_queue_depth(&endpoint->txq)); + + len = CALC_TXRX_PADDED_LEN(target, + packet->act_len + HTC_HDR_LENGTH); + + if (htc_check_credits(target, endpoint, &flags, + packet->endpoint, len, &req_cred)) + break; + + /* now we can fully move onto caller's queue */ + packet = list_first_entry(&endpoint->txq, struct htc_packet, + list); + list_move_tail(&packet->list, queue); + + /* save the number of credits this packet consumed */ + packet->info.tx.cred_used = req_cred; + + /* all TX packets are handled asynchronously */ + packet->completion = htc_tx_comp_handler; + packet->context = target; + endpoint->ep_st.tx_issued += 1; + + /* save send flags */ + packet->info.tx.flags = flags; + packet->info.tx.seqno = endpoint->seqno; + endpoint->seqno++; + } +} + +/* See if the padded tx length falls on a credit boundary */ +static int htc_get_credit_padding(unsigned int cred_sz, int *len, + struct htc_endpoint *ep) +{ + int rem_cred, cred_pad; + + rem_cred = *len % cred_sz; + + /* No padding needed */ + if (!rem_cred) + return 0; + + if (!(ep->conn_flags & HTC_FLGS_TX_BNDL_PAD_EN)) + return -1; + + /* + * The transfer consumes a "partial" credit, this + * packet cannot be bundled unless we add + * additional "dummy" padding (max 255 bytes) to + * consume the entire credit. + */ + cred_pad = *len < cred_sz ? (cred_sz - *len) : rem_cred; + + if ((cred_pad > 0) && (cred_pad <= 255)) + *len += cred_pad; + else + /* The amount of padding is too large, send as non-bundled */ + return -1; + + return cred_pad; +} + +static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, + struct htc_endpoint *endpoint, + struct hif_scatter_req *scat_req, + int n_scat, + struct list_head *queue) +{ + struct htc_packet *packet; + int i, len, rem_scat, cred_pad; + int status = 0; + + rem_scat = target->max_tx_bndl_sz; + + for (i = 0; i < n_scat; i++) { + scat_req->scat_list[i].packet = NULL; + + if (list_empty(queue)) + break; + + packet = list_first_entry(queue, struct htc_packet, list); + len = CALC_TXRX_PADDED_LEN(target, + packet->act_len + HTC_HDR_LENGTH); + + cred_pad = htc_get_credit_padding(target->tgt_cred_sz, + &len, endpoint); + if (cred_pad < 0 || rem_scat < len) { + status = -ENOSPC; + break; + } + + rem_scat -= len; + /* now remove it from the queue */ + list_del(&packet->list); + + scat_req->scat_list[i].packet = packet; + /* prepare packet and flag message as part of a send bundle */ + ath6kl_htc_tx_prep_pkt(packet, + packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE, + cred_pad, packet->info.tx.seqno); + /* Make sure the buffer is 4-byte aligned */ + ath6kl_htc_tx_buf_align(&packet->buf, + packet->act_len + HTC_HDR_LENGTH); + scat_req->scat_list[i].buf = packet->buf; + scat_req->scat_list[i].len = len; + + scat_req->len += len; + scat_req->scat_entries++; + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx adding (%d) pkt 0x%p seqno %d len %d remaining %d\n", + i, packet, packet->info.tx.seqno, len, rem_scat); + } + + /* Roll back scatter setup in case of any failure */ + if (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE) { + for (i = scat_req->scat_entries - 1; i >= 0; i--) { + packet = scat_req->scat_list[i].packet; + if (packet) { + packet->buf += HTC_HDR_LENGTH; + list_add(&packet->list, queue); + } + } + return -EAGAIN; + } + + return status; +} + +/* + * Drain a queue and send as bundles this function may return without fully + * draining the queue when + * + * 1. scatter resources are exhausted + * 2. a message that will consume a partial credit will stop the + * bundling process early + * 3. we drop below the minimum number of messages for a bundle + */ +static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, + struct list_head *queue, + int *sent_bundle, int *n_bundle_pkts) +{ + struct htc_target *target = endpoint->target; + struct hif_scatter_req *scat_req = NULL; + int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0; + int status; + u32 txb_mask; + u8 ac = WMM_NUM_AC; + + if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) || + (WMI_CONTROL_SVC != endpoint->svc_id)) + ac = target->dev->ar->ep2ac_map[endpoint->eid]; + + while (true) { + status = 0; + n_scat = get_queue_depth(queue); + n_scat = min(n_scat, target->msg_per_bndl_max); + + if (n_scat < HTC_MIN_HTC_MSGS_TO_BUNDLE) + /* not enough to bundle */ + break; + + scat_req = hif_scatter_req_get(target->dev->ar); + + if (!scat_req) { + /* no scatter resources */ + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx no more scatter resources\n"); + break; + } + + if ((ac < WMM_NUM_AC) && (ac != WMM_AC_BK)) { + if (WMM_AC_BE == ac) + /* + * BE, BK have priorities and bit + * positions reversed + */ + txb_mask = (1 << WMM_AC_BK); + else + /* + * any AC with priority lower than + * itself + */ + txb_mask = ((1 << ac) - 1); + /* + * when the scatter request resources drop below a + * certain threshold, disable Tx bundling for all + * AC's with priority lower than the current requesting + * AC. Otherwise re-enable Tx bundling for them + */ + if (scat_req->scat_q_depth < ATH6KL_SCATTER_REQS) + target->tx_bndl_mask &= ~txb_mask; + else + target->tx_bndl_mask |= txb_mask; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx pkts to scatter: %d\n", + n_scat); + + scat_req->len = 0; + scat_req->scat_entries = 0; + + status = ath6kl_htc_tx_setup_scat_list(target, endpoint, + scat_req, n_scat, + queue); + if (status == -EAGAIN) { + hif_scatter_req_add(target->dev->ar, scat_req); + break; + } + + /* send path is always asynchronous */ + scat_req->complete = htc_async_tx_scat_complete; + n_sent_bundle++; + tot_pkts_bundle += scat_req->scat_entries; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx scatter bytes %d entries %d\n", + scat_req->len, scat_req->scat_entries); + ath6kl_hif_submit_scat_req(target->dev, scat_req, false); + + if (status) + break; + } + + *sent_bundle = n_sent_bundle; + *n_bundle_pkts = tot_pkts_bundle; + ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx bundle sent %d pkts\n", + n_sent_bundle); + + return; +} + +static void ath6kl_htc_tx_from_queue(struct htc_target *target, + struct htc_endpoint *endpoint) +{ + struct list_head txq; + struct htc_packet *packet; + int bundle_sent; + int n_pkts_bundle; + u8 ac = WMM_NUM_AC; + + spin_lock_bh(&target->tx_lock); + + endpoint->tx_proc_cnt++; + if (endpoint->tx_proc_cnt > 1) { + endpoint->tx_proc_cnt--; + spin_unlock_bh(&target->tx_lock); + ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx busy\n"); + return; + } + + /* + * drain the endpoint TX queue for transmission as long + * as we have enough credits. + */ + INIT_LIST_HEAD(&txq); + + if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) || + (WMI_CONTROL_SVC != endpoint->svc_id)) + ac = target->dev->ar->ep2ac_map[endpoint->eid]; + + while (true) { + + if (list_empty(&endpoint->txq)) + break; + + ath6kl_htc_tx_pkts_get(target, endpoint, &txq); + + if (list_empty(&txq)) + break; + + spin_unlock_bh(&target->tx_lock); + + bundle_sent = 0; + n_pkts_bundle = 0; + + while (true) { + /* try to send a bundle on each pass */ + if ((target->tx_bndl_mask) && + (get_queue_depth(&txq) >= + HTC_MIN_HTC_MSGS_TO_BUNDLE)) { + int temp1 = 0, temp2 = 0; + + /* check if bundling is enabled for an AC */ + if (target->tx_bndl_mask & (1 << ac)) { + ath6kl_htc_tx_bundle(endpoint, &txq, + &temp1, &temp2); + bundle_sent += temp1; + n_pkts_bundle += temp2; + } + } + + if (list_empty(&txq)) + break; + + packet = list_first_entry(&txq, struct htc_packet, + list); + list_del(&packet->list); + + ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags, + 0, packet->info.tx.seqno); + ath6kl_htc_tx_issue(target, packet); + } + + spin_lock_bh(&target->tx_lock); + + endpoint->ep_st.tx_bundles += bundle_sent; + endpoint->ep_st.tx_pkt_bundled += n_pkts_bundle; + + /* + * if an AC has bundling disabled and no tx bundling + * has occured continously for a certain number of TX, + * enable tx bundling for this AC + */ + if (!bundle_sent) { + if (!(target->tx_bndl_mask & (1 << ac)) && + (ac < WMM_NUM_AC)) { + if (++target->ac_tx_count[ac] >= + TX_RESUME_BUNDLE_THRESHOLD) { + target->ac_tx_count[ac] = 0; + target->tx_bndl_mask |= (1 << ac); + } + } + } else { + /* tx bundling will reset the counter */ + if (ac < WMM_NUM_AC) + target->ac_tx_count[ac] = 0; + } + } + + endpoint->tx_proc_cnt = 0; + spin_unlock_bh(&target->tx_lock); +} + +static bool ath6kl_htc_tx_try(struct htc_target *target, + struct htc_endpoint *endpoint, + struct htc_packet *tx_pkt) +{ + struct htc_ep_callbacks ep_cb; + int txq_depth; + bool overflow = false; + + ep_cb = endpoint->ep_cb; + + spin_lock_bh(&target->tx_lock); + txq_depth = get_queue_depth(&endpoint->txq); + spin_unlock_bh(&target->tx_lock); + + if (txq_depth >= endpoint->max_txq_depth) + overflow = true; + + if (overflow) + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx overflow ep %d depth %d max %d\n", + endpoint->eid, txq_depth, + endpoint->max_txq_depth); + + if (overflow && ep_cb.tx_full) { + if (ep_cb.tx_full(endpoint->target, tx_pkt) == + HTC_SEND_FULL_DROP) { + endpoint->ep_st.tx_dropped += 1; + return false; + } + } + + spin_lock_bh(&target->tx_lock); + list_add_tail(&tx_pkt->list, &endpoint->txq); + spin_unlock_bh(&target->tx_lock); + + ath6kl_htc_tx_from_queue(target, endpoint); + + return true; +} + +static void htc_chk_ep_txq(struct htc_target *target) +{ + struct htc_endpoint *endpoint; + struct htc_endpoint_credit_dist *cred_dist; + + /* + * Run through the credit distribution list to see if there are + * packets queued. NOTE: no locks need to be taken since the + * distribution list is not dynamic (cannot be re-ordered) and we + * are not modifying any state. + */ + list_for_each_entry(cred_dist, &target->cred_dist_list, list) { + endpoint = cred_dist->htc_ep; + + spin_lock_bh(&target->tx_lock); + if (!list_empty(&endpoint->txq)) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc creds ep %d credits %d pkts %d\n", + cred_dist->endpoint, + endpoint->cred_dist.credits, + get_queue_depth(&endpoint->txq)); + spin_unlock_bh(&target->tx_lock); + /* + * Try to start the stalled queue, this list is + * ordered by priority. If there are credits + * available the highest priority queue will get a + * chance to reclaim credits from lower priority + * ones. + */ + ath6kl_htc_tx_from_queue(target, endpoint); + spin_lock_bh(&target->tx_lock); + } + spin_unlock_bh(&target->tx_lock); + } +} + +static int htc_setup_tx_complete(struct htc_target *target) +{ + struct htc_packet *send_pkt = NULL; + int status; + + send_pkt = htc_get_control_buf(target, true); + + if (!send_pkt) + return -ENOMEM; + + if (target->htc_tgt_ver >= HTC_VERSION_2P1) { + struct htc_setup_comp_ext_msg *setup_comp_ext; + u32 flags = 0; + + setup_comp_ext = + (struct htc_setup_comp_ext_msg *)send_pkt->buf; + memset(setup_comp_ext, 0, sizeof(*setup_comp_ext)); + setup_comp_ext->msg_id = + cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID); + + if (target->msg_per_bndl_max > 0) { + /* Indicate HTC bundling to the target */ + flags |= HTC_SETUP_COMP_FLG_RX_BNDL_EN; + setup_comp_ext->msg_per_rxbndl = + target->msg_per_bndl_max; + } + flags = cpu_to_le32(flags); + memcpy(&setup_comp_ext->flags, &flags, + sizeof(setup_comp_ext->flags)); + set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp_ext, + sizeof(struct htc_setup_comp_ext_msg), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + + } else { + struct htc_setup_comp_msg *setup_comp; + setup_comp = (struct htc_setup_comp_msg *)send_pkt->buf; + memset(setup_comp, 0, sizeof(struct htc_setup_comp_msg)); + setup_comp->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_ID); + set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp, + sizeof(struct htc_setup_comp_msg), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + } + + /* we want synchronous operation */ + send_pkt->completion = NULL; + ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0); + status = ath6kl_htc_tx_issue(target, send_pkt); + + if (send_pkt != NULL) + htc_reclaim_txctrl_buf(target, send_pkt); + + return status; +} + +static void ath6kl_htc_set_credit_dist(struct htc_target *target, + struct ath6kl_htc_credit_info *credit_info, + u16 srvc_pri_order[], int list_len) +{ + struct htc_endpoint *endpoint; + int i, ep; + + target->credit_info = credit_info; + + list_add_tail(&target->endpoint[ENDPOINT_0].cred_dist.list, + &target->cred_dist_list); + + for (i = 0; i < list_len; i++) { + for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) { + endpoint = &target->endpoint[ep]; + if (endpoint->svc_id == srvc_pri_order[i]) { + list_add_tail(&endpoint->cred_dist.list, + &target->cred_dist_list); + break; + } + } + if (ep >= ENDPOINT_MAX) { + WARN_ON(1); + return; + } + } +} + +static int ath6kl_htc_mbox_tx(struct htc_target *target, + struct htc_packet *packet) +{ + struct htc_endpoint *endpoint; + struct list_head queue; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx ep id %d buf 0x%p len %d\n", + packet->endpoint, packet->buf, packet->act_len); + + if (packet->endpoint >= ENDPOINT_MAX) { + WARN_ON(1); + return -EINVAL; + } + + endpoint = &target->endpoint[packet->endpoint]; + + if (!ath6kl_htc_tx_try(target, endpoint, packet)) { + packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ? + -ECANCELED : -ENOSPC; + INIT_LIST_HEAD(&queue); + list_add(&packet->list, &queue); + htc_tx_complete(endpoint, &queue); + } + + return 0; +} + +/* flush endpoint TX queue */ +static void ath6kl_htc_mbox_flush_txep(struct htc_target *target, + enum htc_endpoint_id eid, u16 tag) +{ + struct htc_packet *packet, *tmp_pkt; + struct list_head discard_q, container; + struct htc_endpoint *endpoint = &target->endpoint[eid]; + + if (!endpoint->svc_id) { + WARN_ON(1); + return; + } + + /* initialize the discard queue */ + INIT_LIST_HEAD(&discard_q); + + spin_lock_bh(&target->tx_lock); + + list_for_each_entry_safe(packet, tmp_pkt, &endpoint->txq, list) { + if ((tag == HTC_TX_PACKET_TAG_ALL) || + (tag == packet->info.tx.tag)) + list_move_tail(&packet->list, &discard_q); + } + + spin_unlock_bh(&target->tx_lock); + + list_for_each_entry_safe(packet, tmp_pkt, &discard_q, list) { + packet->status = -ECANCELED; + list_del(&packet->list); + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx flushing pkt 0x%p len %d ep %d tag 0x%x\n", + packet, packet->act_len, + packet->endpoint, packet->info.tx.tag); + + INIT_LIST_HEAD(&container); + list_add_tail(&packet->list, &container); + htc_tx_complete(endpoint, &container); + } + +} + +static void ath6kl_htc_flush_txep_all(struct htc_target *target) +{ + struct htc_endpoint *endpoint; + int i; + + dump_cred_dist_stats(target); + + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { + endpoint = &target->endpoint[i]; + if (endpoint->svc_id == 0) + /* not in use.. */ + continue; + ath6kl_htc_mbox_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL); + } +} + +static void ath6kl_htc_mbox_indicate_activity_change(struct htc_target *target, + enum htc_endpoint_id eid, bool active) +{ + struct htc_endpoint *endpoint = &target->endpoint[eid]; + bool dist = false; + + if (endpoint->svc_id == 0) { + WARN_ON(1); + return; + } + + spin_lock_bh(&target->tx_lock); + + if (active) { + if (!(endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE)) { + endpoint->cred_dist.dist_flags |= HTC_EP_ACTIVE; + dist = true; + } + } else { + if (endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE) { + endpoint->cred_dist.dist_flags &= ~HTC_EP_ACTIVE; + dist = true; + } + } + + if (dist) { + endpoint->cred_dist.txq_depth = + get_queue_depth(&endpoint->txq); + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc tx activity ctxt 0x%p dist 0x%p\n", + target->credit_info, &target->cred_dist_list); + + ath6kl_credit_distribute(target->credit_info, + &target->cred_dist_list, + HTC_CREDIT_DIST_ACTIVITY_CHANGE); + } + + spin_unlock_bh(&target->tx_lock); + + if (dist && !active) + htc_chk_ep_txq(target); +} + +/* HTC Rx */ + +static inline void ath6kl_htc_rx_update_stats(struct htc_endpoint *endpoint, + int n_look_ahds) +{ + endpoint->ep_st.rx_pkts++; + if (n_look_ahds == 1) + endpoint->ep_st.rx_lkahds++; + else if (n_look_ahds > 1) + endpoint->ep_st.rx_bundle_lkahd++; +} + +static inline bool htc_valid_rx_frame_len(struct htc_target *target, + enum htc_endpoint_id eid, int len) +{ + return (eid == target->dev->ar->ctrl_ep) ? + len <= ATH6KL_BUFFER_SIZE : len <= ATH6KL_AMSDU_BUFFER_SIZE; +} + +static int htc_add_rxbuf(struct htc_target *target, struct htc_packet *packet) +{ + struct list_head queue; + + INIT_LIST_HEAD(&queue); + list_add_tail(&packet->list, &queue); + return ath6kl_htc_mbox_add_rxbuf_multiple(target, &queue); +} + +static void htc_reclaim_rxbuf(struct htc_target *target, + struct htc_packet *packet, + struct htc_endpoint *ep) +{ + if (packet->info.rx.rx_flags & HTC_RX_PKT_NO_RECYCLE) { + htc_rxpkt_reset(packet); + packet->status = -ECANCELED; + ep->ep_cb.rx(ep->target, packet); + } else { + htc_rxpkt_reset(packet); + htc_add_rxbuf((void *)(target), packet); + } +} + +static void reclaim_rx_ctrl_buf(struct htc_target *target, + struct htc_packet *packet) +{ + spin_lock_bh(&target->htc_lock); + list_add_tail(&packet->list, &target->free_ctrl_rxbuf); + spin_unlock_bh(&target->htc_lock); +} + +static int ath6kl_htc_rx_packet(struct htc_target *target, + struct htc_packet *packet, + u32 rx_len) +{ + struct ath6kl_device *dev = target->dev; + u32 padded_len; + int status; + + padded_len = CALC_TXRX_PADDED_LEN(target, rx_len); + + if (padded_len > packet->buf_len) { + ath6kl_err("not enough receive space for packet - padlen %d recvlen %d bufferlen %d\n", + padded_len, rx_len, packet->buf_len); + return -ENOMEM; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx 0x%p hdr x%x len %d mbox 0x%x\n", + packet, packet->info.rx.exp_hdr, + padded_len, dev->ar->mbox_info.htc_addr); + + status = hif_read_write_sync(dev->ar, + dev->ar->mbox_info.htc_addr, + packet->buf, padded_len, + HIF_RD_SYNC_BLOCK_FIX); + + packet->status = status; + + return status; +} + +/* + * optimization for recv packets, we can indicate a + * "hint" that there are more single-packets to fetch + * on this endpoint. + */ +static void ath6kl_htc_rx_set_indicate(u32 lk_ahd, + struct htc_endpoint *endpoint, + struct htc_packet *packet) +{ + struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)&lk_ahd; + + if (htc_hdr->eid == packet->endpoint) { + if (!list_empty(&endpoint->rx_bufq)) + packet->info.rx.indicat_flags |= + HTC_RX_FLAGS_INDICATE_MORE_PKTS; + } +} + +static void ath6kl_htc_rx_chk_water_mark(struct htc_endpoint *endpoint) +{ + struct htc_ep_callbacks ep_cb = endpoint->ep_cb; + + if (ep_cb.rx_refill_thresh > 0) { + spin_lock_bh(&endpoint->target->rx_lock); + if (get_queue_depth(&endpoint->rx_bufq) + < ep_cb.rx_refill_thresh) { + spin_unlock_bh(&endpoint->target->rx_lock); + ep_cb.rx_refill(endpoint->target, endpoint->eid); + return; + } + spin_unlock_bh(&endpoint->target->rx_lock); + } +} + +/* This function is called with rx_lock held */ +static int ath6kl_htc_rx_setup(struct htc_target *target, + struct htc_endpoint *ep, + u32 *lk_ahds, struct list_head *queue, int n_msg) +{ + struct htc_packet *packet; + /* FIXME: type of lk_ahds can't be right */ + struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)lk_ahds; + struct htc_ep_callbacks ep_cb; + int status = 0, j, full_len; + bool no_recycle; + + full_len = CALC_TXRX_PADDED_LEN(target, + le16_to_cpu(htc_hdr->payld_len) + + sizeof(*htc_hdr)); + + if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) { + ath6kl_warn("Rx buffer requested with invalid length\n"); + return -EINVAL; + } + + ep_cb = ep->ep_cb; + for (j = 0; j < n_msg; j++) { + + /* + * Reset flag, any packets allocated using the + * rx_alloc() API cannot be recycled on + * cleanup,they must be explicitly returned. + */ + no_recycle = false; + + if (ep_cb.rx_allocthresh && + (full_len > ep_cb.rx_alloc_thresh)) { + ep->ep_st.rx_alloc_thresh_hit += 1; + ep->ep_st.rxalloc_thresh_byte += + le16_to_cpu(htc_hdr->payld_len); + + spin_unlock_bh(&target->rx_lock); + no_recycle = true; + + packet = ep_cb.rx_allocthresh(ep->target, ep->eid, + full_len); + spin_lock_bh(&target->rx_lock); + } else { + /* refill handler is being used */ + if (list_empty(&ep->rx_bufq)) { + if (ep_cb.rx_refill) { + spin_unlock_bh(&target->rx_lock); + ep_cb.rx_refill(ep->target, ep->eid); + spin_lock_bh(&target->rx_lock); + } + } + + if (list_empty(&ep->rx_bufq)) + packet = NULL; + else { + packet = list_first_entry(&ep->rx_bufq, + struct htc_packet, list); + list_del(&packet->list); + } + } + + if (!packet) { + target->rx_st_flags |= HTC_RECV_WAIT_BUFFERS; + target->ep_waiting = ep->eid; + return -ENOSPC; + } + + /* clear flags */ + packet->info.rx.rx_flags = 0; + packet->info.rx.indicat_flags = 0; + packet->status = 0; + + if (no_recycle) + /* + * flag that these packets cannot be + * recycled, they have to be returned to + * the user + */ + packet->info.rx.rx_flags |= HTC_RX_PKT_NO_RECYCLE; + + /* Caller needs to free this upon any failure */ + list_add_tail(&packet->list, queue); + + if (target->htc_flags & HTC_OP_STATE_STOPPING) { + status = -ECANCELED; + break; + } + + if (j) { + packet->info.rx.rx_flags |= HTC_RX_PKT_REFRESH_HDR; + packet->info.rx.exp_hdr = 0xFFFFFFFF; + } else + /* set expected look ahead */ + packet->info.rx.exp_hdr = *lk_ahds; + + packet->act_len = le16_to_cpu(htc_hdr->payld_len) + + HTC_HDR_LENGTH; + } + + return status; +} + +static int ath6kl_htc_rx_alloc(struct htc_target *target, + u32 lk_ahds[], int msg, + struct htc_endpoint *endpoint, + struct list_head *queue) +{ + int status = 0; + struct htc_packet *packet, *tmp_pkt; + struct htc_frame_hdr *htc_hdr; + int i, n_msg; + + spin_lock_bh(&target->rx_lock); + + for (i = 0; i < msg; i++) { + + htc_hdr = (struct htc_frame_hdr *)&lk_ahds[i]; + + if (htc_hdr->eid >= ENDPOINT_MAX) { + ath6kl_err("invalid ep in look-ahead: %d\n", + htc_hdr->eid); + status = -ENOMEM; + break; + } + + if (htc_hdr->eid != endpoint->eid) { + ath6kl_err("invalid ep in look-ahead: %d should be : %d (index:%d)\n", + htc_hdr->eid, endpoint->eid, i); + status = -ENOMEM; + break; + } + + if (le16_to_cpu(htc_hdr->payld_len) > HTC_MAX_PAYLOAD_LENGTH) { + ath6kl_err("payload len %d exceeds max htc : %d !\n", + le16_to_cpu(htc_hdr->payld_len), + (u32) HTC_MAX_PAYLOAD_LENGTH); + status = -ENOMEM; + break; + } + + if (endpoint->svc_id == 0) { + ath6kl_err("ep %d is not connected !\n", htc_hdr->eid); + status = -ENOMEM; + break; + } + + if (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) { + /* + * HTC header indicates that every packet to follow + * has the same padded length so that it can be + * optimally fetched as a full bundle. + */ + n_msg = (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) >> + HTC_FLG_RX_BNDL_CNT_S; + + /* the count doesn't include the starter frame */ + n_msg++; + if (n_msg > target->msg_per_bndl_max) { + status = -ENOMEM; + break; + } + + endpoint->ep_st.rx_bundle_from_hdr += 1; + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx bundle pkts %d\n", + n_msg); + } else + /* HTC header only indicates 1 message to fetch */ + n_msg = 1; + + /* Setup packet buffers for each message */ + status = ath6kl_htc_rx_setup(target, endpoint, &lk_ahds[i], + queue, n_msg); + + /* + * This is due to unavailabilty of buffers to rx entire data. + * Return no error so that free buffers from queue can be used + * to receive partial data. + */ + if (status == -ENOSPC) { + spin_unlock_bh(&target->rx_lock); + return 0; + } + + if (status) + break; + } + + spin_unlock_bh(&target->rx_lock); + + if (status) { + list_for_each_entry_safe(packet, tmp_pkt, queue, list) { + list_del(&packet->list); + htc_reclaim_rxbuf(target, packet, + &target->endpoint[packet->endpoint]); + } + } + + return status; +} + +static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets) +{ + if (packets->endpoint != ENDPOINT_0) { + WARN_ON(1); + return; + } + + if (packets->status == -ECANCELED) { + reclaim_rx_ctrl_buf(context, packets); + return; + } + + if (packets->act_len > 0) { + ath6kl_err("htc_ctrl_rx, got message with len:%zu\n", + packets->act_len + HTC_HDR_LENGTH); + + ath6kl_dbg_dump(ATH6KL_DBG_HTC, + "htc rx unexpected endpoint 0 message", "", + packets->buf - HTC_HDR_LENGTH, + packets->act_len + HTC_HDR_LENGTH); + } + + htc_reclaim_rxbuf(context, packets, &context->endpoint[0]); +} + +static void htc_proc_cred_rpt(struct htc_target *target, + struct htc_credit_report *rpt, + int n_entries, + enum htc_endpoint_id from_ep) +{ + struct htc_endpoint *endpoint; + int tot_credits = 0, i; + bool dist = false; + + spin_lock_bh(&target->tx_lock); + + for (i = 0; i < n_entries; i++, rpt++) { + if (rpt->eid >= ENDPOINT_MAX) { + WARN_ON(1); + spin_unlock_bh(&target->tx_lock); + return; + } + + endpoint = &target->endpoint[rpt->eid]; + + ath6kl_dbg(ATH6KL_DBG_CREDIT, + "credit report ep %d credits %d\n", + rpt->eid, rpt->credits); + + endpoint->ep_st.tx_cred_rpt += 1; + endpoint->ep_st.cred_retnd += rpt->credits; + + if (from_ep == rpt->eid) { + /* + * This credit report arrived on the same endpoint + * indicating it arrived in an RX packet. + */ + endpoint->ep_st.cred_from_rx += rpt->credits; + endpoint->ep_st.cred_rpt_from_rx += 1; + } else if (from_ep == ENDPOINT_0) { + /* credit arrived on endpoint 0 as a NULL message */ + endpoint->ep_st.cred_from_ep0 += rpt->credits; + endpoint->ep_st.cred_rpt_ep0 += 1; + } else { + endpoint->ep_st.cred_from_other += rpt->credits; + endpoint->ep_st.cred_rpt_from_other += 1; + } + + if (rpt->eid == ENDPOINT_0) + /* always give endpoint 0 credits back */ + endpoint->cred_dist.credits += rpt->credits; + else { + endpoint->cred_dist.cred_to_dist += rpt->credits; + dist = true; + } + + /* + * Refresh tx depth for distribution function that will + * recover these credits NOTE: this is only valid when + * there are credits to recover! + */ + endpoint->cred_dist.txq_depth = + get_queue_depth(&endpoint->txq); + + tot_credits += rpt->credits; + } + + if (dist) { + /* + * This was a credit return based on a completed send + * operations note, this is done with the lock held + */ + ath6kl_credit_distribute(target->credit_info, + &target->cred_dist_list, + HTC_CREDIT_DIST_SEND_COMPLETE); + } + + spin_unlock_bh(&target->tx_lock); + + if (tot_credits) + htc_chk_ep_txq(target); +} + +static int htc_parse_trailer(struct htc_target *target, + struct htc_record_hdr *record, + u8 *record_buf, u32 *next_lk_ahds, + enum htc_endpoint_id endpoint, + int *n_lk_ahds) +{ + struct htc_bundle_lkahd_rpt *bundle_lkahd_rpt; + struct htc_lookahead_report *lk_ahd; + int len; + + switch (record->rec_id) { + case HTC_RECORD_CREDITS: + len = record->len / sizeof(struct htc_credit_report); + if (!len) { + WARN_ON(1); + return -EINVAL; + } + + htc_proc_cred_rpt(target, + (struct htc_credit_report *) record_buf, + len, endpoint); + break; + case HTC_RECORD_LOOKAHEAD: + len = record->len / sizeof(*lk_ahd); + if (!len) { + WARN_ON(1); + return -EINVAL; + } + + lk_ahd = (struct htc_lookahead_report *) record_buf; + if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) + && next_lk_ahds) { + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx lk_ahd found pre_valid 0x%x post_valid 0x%x\n", + lk_ahd->pre_valid, lk_ahd->post_valid); + + /* look ahead bytes are valid, copy them over */ + memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4); + + ath6kl_dbg_dump(ATH6KL_DBG_HTC, + "htc rx next look ahead", + "", next_lk_ahds, 4); + + *n_lk_ahds = 1; + } + break; + case HTC_RECORD_LOOKAHEAD_BUNDLE: + len = record->len / sizeof(*bundle_lkahd_rpt); + if (!len || (len > HTC_HOST_MAX_MSG_PER_BUNDLE)) { + WARN_ON(1); + return -EINVAL; + } + + if (next_lk_ahds) { + int i; + + bundle_lkahd_rpt = + (struct htc_bundle_lkahd_rpt *) record_buf; + + ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bundle lk_ahd", + "", record_buf, record->len); + + for (i = 0; i < len; i++) { + memcpy((u8 *)&next_lk_ahds[i], + bundle_lkahd_rpt->lk_ahd, 4); + bundle_lkahd_rpt++; + } + + *n_lk_ahds = i; + } + break; + default: + ath6kl_err("unhandled record: id:%d len:%d\n", + record->rec_id, record->len); + break; + } + + return 0; + +} + +static int htc_proc_trailer(struct htc_target *target, + u8 *buf, int len, u32 *next_lk_ahds, + int *n_lk_ahds, enum htc_endpoint_id endpoint) +{ + struct htc_record_hdr *record; + int orig_len; + int status; + u8 *record_buf; + u8 *orig_buf; + + ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx trailer len %d\n", len); + ath6kl_dbg_dump(ATH6KL_DBG_HTC, NULL, "", buf, len); + + orig_buf = buf; + orig_len = len; + status = 0; + + while (len > 0) { + + if (len < sizeof(struct htc_record_hdr)) { + status = -ENOMEM; + break; + } + /* these are byte aligned structs */ + record = (struct htc_record_hdr *) buf; + len -= sizeof(struct htc_record_hdr); + buf += sizeof(struct htc_record_hdr); + + if (record->len > len) { + ath6kl_err("invalid record len: %d (id:%d) buf has: %d bytes left\n", + record->len, record->rec_id, len); + status = -ENOMEM; + break; + } + record_buf = buf; + + status = htc_parse_trailer(target, record, record_buf, + next_lk_ahds, endpoint, n_lk_ahds); + + if (status) + break; + + /* advance buffer past this record for next time around */ + buf += record->len; + len -= record->len; + } + + if (status) + ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bad trailer", + "", orig_buf, orig_len); + + return status; +} + +static int ath6kl_htc_rx_process_hdr(struct htc_target *target, + struct htc_packet *packet, + u32 *next_lkahds, int *n_lkahds) +{ + int status = 0; + u16 payload_len; + u32 lk_ahd; + struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)packet->buf; + + if (n_lkahds != NULL) + *n_lkahds = 0; + + /* + * NOTE: we cannot assume the alignment of buf, so we use the safe + * macros to retrieve 16 bit fields. + */ + payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len)); + + memcpy((u8 *)&lk_ahd, packet->buf, sizeof(lk_ahd)); + + if (packet->info.rx.rx_flags & HTC_RX_PKT_REFRESH_HDR) { + /* + * Refresh the expected header and the actual length as it + * was unknown when this packet was grabbed as part of the + * bundle. + */ + packet->info.rx.exp_hdr = lk_ahd; + packet->act_len = payload_len + HTC_HDR_LENGTH; + + /* validate the actual header that was refreshed */ + if (packet->act_len > packet->buf_len) { + ath6kl_err("refreshed hdr payload len (%d) in bundled recv is invalid (hdr: 0x%X)\n", + payload_len, lk_ahd); + /* + * Limit this to max buffer just to print out some + * of the buffer. + */ + packet->act_len = min(packet->act_len, packet->buf_len); + status = -ENOMEM; + goto fail_rx; + } + + if (packet->endpoint != htc_hdr->eid) { + ath6kl_err("refreshed hdr ep (%d) does not match expected ep (%d)\n", + htc_hdr->eid, packet->endpoint); + status = -ENOMEM; + goto fail_rx; + } + } + + if (lk_ahd != packet->info.rx.exp_hdr) { + ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n", + __func__, packet, packet->info.rx.rx_flags); + ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx expected lk_ahd", + "", &packet->info.rx.exp_hdr, 4); + ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx current header", + "", (u8 *)&lk_ahd, sizeof(lk_ahd)); + status = -ENOMEM; + goto fail_rx; + } + + if (htc_hdr->flags & HTC_FLG_RX_TRAILER) { + if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) || + htc_hdr->ctrl[0] > payload_len) { + ath6kl_err("%s(): invalid hdr (payload len should be :%d, CB[0] is:%d)\n", + __func__, payload_len, htc_hdr->ctrl[0]); + status = -ENOMEM; + goto fail_rx; + } + + if (packet->info.rx.rx_flags & HTC_RX_PKT_IGNORE_LOOKAHEAD) { + next_lkahds = NULL; + n_lkahds = NULL; + } + + status = htc_proc_trailer(target, packet->buf + HTC_HDR_LENGTH + + payload_len - htc_hdr->ctrl[0], + htc_hdr->ctrl[0], next_lkahds, + n_lkahds, packet->endpoint); + + if (status) + goto fail_rx; + + packet->act_len -= htc_hdr->ctrl[0]; + } + + packet->buf += HTC_HDR_LENGTH; + packet->act_len -= HTC_HDR_LENGTH; + +fail_rx: + if (status) + ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bad packet", + "", packet->buf, packet->act_len); + + return status; +} + +static void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint, + struct htc_packet *packet) +{ + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx complete ep %d packet 0x%p\n", + endpoint->eid, packet); + endpoint->ep_cb.rx(endpoint->target, packet); +} + +static int ath6kl_htc_rx_bundle(struct htc_target *target, + struct list_head *rxq, + struct list_head *sync_compq, + int *n_pkt_fetched, bool part_bundle) +{ + struct hif_scatter_req *scat_req; + struct htc_packet *packet; + int rem_space = target->max_rx_bndl_sz; + int n_scat_pkt, status = 0, i, len; + + n_scat_pkt = get_queue_depth(rxq); + n_scat_pkt = min(n_scat_pkt, target->msg_per_bndl_max); + + if ((get_queue_depth(rxq) - n_scat_pkt) > 0) { + /* + * We were forced to split this bundle receive operation + * all packets in this partial bundle must have their + * lookaheads ignored. + */ + part_bundle = true; + + /* + * This would only happen if the target ignored our max + * bundle limit. + */ + ath6kl_warn("%s(): partial bundle detected num:%d , %d\n", + __func__, get_queue_depth(rxq), n_scat_pkt); + } + + len = 0; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx bundle depth %d pkts %d\n", + get_queue_depth(rxq), n_scat_pkt); + + scat_req = hif_scatter_req_get(target->dev->ar); + + if (scat_req == NULL) + goto fail_rx_pkt; + + for (i = 0; i < n_scat_pkt; i++) { + int pad_len; + + packet = list_first_entry(rxq, struct htc_packet, list); + list_del(&packet->list); + + pad_len = CALC_TXRX_PADDED_LEN(target, + packet->act_len); + + if ((rem_space - pad_len) < 0) { + list_add(&packet->list, rxq); + break; + } + + rem_space -= pad_len; + + if (part_bundle || (i < (n_scat_pkt - 1))) + /* + * Packet 0..n-1 cannot be checked for look-aheads + * since we are fetching a bundle the last packet + * however can have it's lookahead used + */ + packet->info.rx.rx_flags |= + HTC_RX_PKT_IGNORE_LOOKAHEAD; + + /* NOTE: 1 HTC packet per scatter entry */ + scat_req->scat_list[i].buf = packet->buf; + scat_req->scat_list[i].len = pad_len; + + packet->info.rx.rx_flags |= HTC_RX_PKT_PART_OF_BUNDLE; + + list_add_tail(&packet->list, sync_compq); + + WARN_ON(!scat_req->scat_list[i].len); + len += scat_req->scat_list[i].len; + } + + scat_req->len = len; + scat_req->scat_entries = i; + + status = ath6kl_hif_submit_scat_req(target->dev, scat_req, true); + + if (!status) + *n_pkt_fetched = i; + + /* free scatter request */ + hif_scatter_req_add(target->dev->ar, scat_req); + +fail_rx_pkt: + + return status; +} + +static int ath6kl_htc_rx_process_packets(struct htc_target *target, + struct list_head *comp_pktq, + u32 lk_ahds[], + int *n_lk_ahd) +{ + struct htc_packet *packet, *tmp_pkt; + struct htc_endpoint *ep; + int status = 0; + + list_for_each_entry_safe(packet, tmp_pkt, comp_pktq, list) { + ep = &target->endpoint[packet->endpoint]; + + /* process header for each of the recv packet */ + status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds, + n_lk_ahd); + if (status) + return status; + + list_del(&packet->list); + + if (list_empty(comp_pktq)) { + /* + * Last packet's more packet flag is set + * based on the lookahead. + */ + if (*n_lk_ahd > 0) + ath6kl_htc_rx_set_indicate(lk_ahds[0], + ep, packet); + } else + /* + * Packets in a bundle automatically have + * this flag set. + */ + packet->info.rx.indicat_flags |= + HTC_RX_FLAGS_INDICATE_MORE_PKTS; + + ath6kl_htc_rx_update_stats(ep, *n_lk_ahd); + + if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE) + ep->ep_st.rx_bundl += 1; + + ath6kl_htc_rx_complete(ep, packet); + } + + return status; +} + +static int ath6kl_htc_rx_fetch(struct htc_target *target, + struct list_head *rx_pktq, + struct list_head *comp_pktq) +{ + int fetched_pkts; + bool part_bundle = false; + int status = 0; + struct list_head tmp_rxq; + struct htc_packet *packet, *tmp_pkt; + + /* now go fetch the list of HTC packets */ + while (!list_empty(rx_pktq)) { + fetched_pkts = 0; + + INIT_LIST_HEAD(&tmp_rxq); + + if (target->rx_bndl_enable && (get_queue_depth(rx_pktq) > 1)) { + /* + * There are enough packets to attempt a + * bundle transfer and recv bundling is + * allowed. + */ + status = ath6kl_htc_rx_bundle(target, rx_pktq, + &tmp_rxq, + &fetched_pkts, + part_bundle); + if (status) + goto fail_rx; + + if (!list_empty(rx_pktq)) + part_bundle = true; + + list_splice_tail_init(&tmp_rxq, comp_pktq); + } + + if (!fetched_pkts) { + + packet = list_first_entry(rx_pktq, struct htc_packet, + list); + + /* fully synchronous */ + packet->completion = NULL; + + if (!list_is_singular(rx_pktq)) + /* + * look_aheads in all packet + * except the last one in the + * bundle must be ignored + */ + packet->info.rx.rx_flags |= + HTC_RX_PKT_IGNORE_LOOKAHEAD; + + /* go fetch the packet */ + status = ath6kl_htc_rx_packet(target, packet, + packet->act_len); + + list_move_tail(&packet->list, &tmp_rxq); + + if (status) + goto fail_rx; + + list_splice_tail_init(&tmp_rxq, comp_pktq); + } + } + + return 0; + +fail_rx: + + /* + * Cleanup any packets we allocated but didn't use to + * actually fetch any packets. + */ + + list_for_each_entry_safe(packet, tmp_pkt, rx_pktq, list) { + list_del(&packet->list); + htc_reclaim_rxbuf(target, packet, + &target->endpoint[packet->endpoint]); + } + + list_for_each_entry_safe(packet, tmp_pkt, &tmp_rxq, list) { + list_del(&packet->list); + htc_reclaim_rxbuf(target, packet, + &target->endpoint[packet->endpoint]); + } + + return status; +} + +int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, + u32 msg_look_ahead, int *num_pkts) +{ + struct htc_packet *packets, *tmp_pkt; + struct htc_endpoint *endpoint; + struct list_head rx_pktq, comp_pktq; + int status = 0; + u32 look_aheads[HTC_HOST_MAX_MSG_PER_BUNDLE]; + int num_look_ahead = 1; + enum htc_endpoint_id id; + int n_fetched = 0; + + *num_pkts = 0; + + /* + * On first entry copy the look_aheads into our temp array for + * processing + */ + look_aheads[0] = msg_look_ahead; + + while (true) { + + /* + * First lookahead sets the expected endpoint IDs for all + * packets in a bundle. + */ + id = ((struct htc_frame_hdr *)&look_aheads[0])->eid; + endpoint = &target->endpoint[id]; + + if (id >= ENDPOINT_MAX) { + ath6kl_err("MsgPend, invalid endpoint in look-ahead: %d\n", + id); + status = -ENOMEM; + break; + } + + INIT_LIST_HEAD(&rx_pktq); + INIT_LIST_HEAD(&comp_pktq); + + /* + * Try to allocate as many HTC RX packets indicated by the + * look_aheads. + */ + status = ath6kl_htc_rx_alloc(target, look_aheads, + num_look_ahead, endpoint, + &rx_pktq); + if (status) + break; + + if (get_queue_depth(&rx_pktq) >= 2) + /* + * A recv bundle was detected, force IRQ status + * re-check again + */ + target->chk_irq_status_cnt = 1; + + n_fetched += get_queue_depth(&rx_pktq); + + num_look_ahead = 0; + + status = ath6kl_htc_rx_fetch(target, &rx_pktq, &comp_pktq); + + if (!status) + ath6kl_htc_rx_chk_water_mark(endpoint); + + /* Process fetched packets */ + status = ath6kl_htc_rx_process_packets(target, &comp_pktq, + look_aheads, + &num_look_ahead); + + if (!num_look_ahead || status) + break; + + /* + * For SYNCH processing, if we get here, we are running + * through the loop again due to a detected lookahead. Set + * flag that we should re-check IRQ status registers again + * before leaving IRQ processing, this can net better + * performance in high throughput situations. + */ + target->chk_irq_status_cnt = 1; + } + + if (status) { + ath6kl_err("failed to get pending recv messages: %d\n", + status); + + /* cleanup any packets in sync completion queue */ + list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) { + list_del(&packets->list); + htc_reclaim_rxbuf(target, packets, + &target->endpoint[packets->endpoint]); + } + + if (target->htc_flags & HTC_OP_STATE_STOPPING) { + ath6kl_warn("host is going to stop blocking receiver for htc_stop\n"); + ath6kl_hif_rx_control(target->dev, false); + } + } + + /* + * Before leaving, check to see if host ran out of buffers and + * needs to stop the receiver. + */ + if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) { + ath6kl_warn("host has no rx buffers blocking receiver to prevent overrun\n"); + ath6kl_hif_rx_control(target->dev, false); + } + *num_pkts = n_fetched; + + return status; +} + +/* + * Synchronously wait for a control message from the target, + * This function is used at initialization time ONLY. At init messages + * on ENDPOINT 0 are expected. + */ +static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target) +{ + struct htc_packet *packet = NULL; + struct htc_frame_hdr *htc_hdr; + u32 look_ahead; + + if (ath6kl_hif_poll_mboxmsg_rx(target->dev, &look_ahead, + HTC_TARGET_RESPONSE_TIMEOUT)) + return NULL; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx wait ctrl look_ahead 0x%X\n", look_ahead); + + htc_hdr = (struct htc_frame_hdr *)&look_ahead; + + if (htc_hdr->eid != ENDPOINT_0) + return NULL; + + packet = htc_get_control_buf(target, false); + + if (!packet) + return NULL; + + packet->info.rx.rx_flags = 0; + packet->info.rx.exp_hdr = look_ahead; + packet->act_len = le16_to_cpu(htc_hdr->payld_len) + HTC_HDR_LENGTH; + + if (packet->act_len > packet->buf_len) + goto fail_ctrl_rx; + + /* we want synchronous operation */ + packet->completion = NULL; + + /* get the message from the device, this will block */ + if (ath6kl_htc_rx_packet(target, packet, packet->act_len)) + goto fail_ctrl_rx; + + /* process receive header */ + packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL); + + if (packet->status) { + ath6kl_err("htc_wait_for_ctrl_msg, ath6kl_htc_rx_process_hdr failed (status = %d)\n", + packet->status); + goto fail_ctrl_rx; + } + + return packet; + +fail_ctrl_rx: + if (packet != NULL) { + htc_rxpkt_reset(packet); + reclaim_rx_ctrl_buf(target, packet); + } + + return NULL; +} + +static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, + struct list_head *pkt_queue) +{ + struct htc_endpoint *endpoint; + struct htc_packet *first_pkt; + bool rx_unblock = false; + int status = 0, depth; + + if (list_empty(pkt_queue)) + return -ENOMEM; + + first_pkt = list_first_entry(pkt_queue, struct htc_packet, list); + + if (first_pkt->endpoint >= ENDPOINT_MAX) + return status; + + depth = get_queue_depth(pkt_queue); + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx add multiple ep id %d cnt %d len %d\n", + first_pkt->endpoint, depth, first_pkt->buf_len); + + endpoint = &target->endpoint[first_pkt->endpoint]; + + if (target->htc_flags & HTC_OP_STATE_STOPPING) { + struct htc_packet *packet, *tmp_pkt; + + /* walk through queue and mark each one canceled */ + list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { + packet->status = -ECANCELED; + list_del(&packet->list); + ath6kl_htc_rx_complete(endpoint, packet); + } + + return status; + } + + spin_lock_bh(&target->rx_lock); + + list_splice_tail_init(pkt_queue, &endpoint->rx_bufq); + + /* check if we are blocked waiting for a new buffer */ + if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) { + if (target->ep_waiting == first_pkt->endpoint) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx blocked on ep %d, unblocking\n", + target->ep_waiting); + target->rx_st_flags &= ~HTC_RECV_WAIT_BUFFERS; + target->ep_waiting = ENDPOINT_MAX; + rx_unblock = true; + } + } + + spin_unlock_bh(&target->rx_lock); + + if (rx_unblock && !(target->htc_flags & HTC_OP_STATE_STOPPING)) + /* TODO : implement a buffer threshold count? */ + ath6kl_hif_rx_control(target->dev, true); + + return status; +} + +static void ath6kl_htc_mbox_flush_rx_buf(struct htc_target *target) +{ + struct htc_endpoint *endpoint; + struct htc_packet *packet, *tmp_pkt; + int i; + + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { + endpoint = &target->endpoint[i]; + if (!endpoint->svc_id) + /* not in use.. */ + continue; + + spin_lock_bh(&target->rx_lock); + list_for_each_entry_safe(packet, tmp_pkt, + &endpoint->rx_bufq, list) { + list_del(&packet->list); + spin_unlock_bh(&target->rx_lock); + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx flush pkt 0x%p len %d ep %d\n", + packet, packet->buf_len, + packet->endpoint); + dev_kfree_skb(packet->pkt_cntxt); + /* free rx control bufs allocated in ath6kl_htc_reset */ + if (packet->endpoint == ENDPOINT_0 && + packet->buf_len != ATH6KL_BUFFER_SIZE) { + kfree(packet->buf_start); + kfree(packet); + } + spin_lock_bh(&target->rx_lock); + } + spin_unlock_bh(&target->rx_lock); + } +} + +static int ath6kl_htc_mbox_conn_service(struct htc_target *target, + struct htc_service_connect_req *conn_req, + struct htc_service_connect_resp *conn_resp) +{ + struct htc_packet *rx_pkt = NULL; + struct htc_packet *tx_pkt = NULL; + struct htc_conn_service_resp *resp_msg; + struct htc_conn_service_msg *conn_msg; + struct htc_endpoint *endpoint; + enum htc_endpoint_id assigned_ep = ENDPOINT_MAX; + unsigned int max_msg_sz = 0; + int status = 0; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc connect service target 0x%p service id 0x%x\n", + target, conn_req->svc_id); + + if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) { + /* special case for pseudo control service */ + assigned_ep = ENDPOINT_0; + max_msg_sz = HTC_MAX_CTRL_MSG_LEN; + } else { + /* allocate a packet to send to the target */ + tx_pkt = htc_get_control_buf(target, true); + + if (!tx_pkt) + return -ENOMEM; + + conn_msg = (struct htc_conn_service_msg *)tx_pkt->buf; + memset(conn_msg, 0, sizeof(*conn_msg)); + conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID); + conn_msg->svc_id = cpu_to_le16(conn_req->svc_id); + conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags); + + set_htc_pkt_info(tx_pkt, NULL, (u8 *) conn_msg, + sizeof(*conn_msg) + conn_msg->svc_meta_len, + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + + /* we want synchronous operation */ + tx_pkt->completion = NULL; + ath6kl_htc_tx_prep_pkt(tx_pkt, 0, 0, 0); + status = ath6kl_htc_tx_issue(target, tx_pkt); + + if (status) + goto fail_tx; + + /* wait for response */ + rx_pkt = htc_wait_for_ctrl_msg(target); + + if (!rx_pkt) { + status = -ENOMEM; + goto fail_tx; + } + + resp_msg = (struct htc_conn_service_resp *)rx_pkt->buf; + + if ((le16_to_cpu(resp_msg->msg_id) != HTC_MSG_CONN_SVC_RESP_ID) + || (rx_pkt->act_len < sizeof(*resp_msg))) { + status = -ENOMEM; + goto fail_tx; + } + + conn_resp->resp_code = resp_msg->status; + /* check response status */ + if (resp_msg->status != HTC_SERVICE_SUCCESS) { + ath6kl_err("target failed service 0x%X " + "connect request (status:%d)\n", + le16_to_cpu(resp_msg->svc_id), + resp_msg->status); + status = -ENOMEM; + goto fail_tx; + } + + assigned_ep = (enum htc_endpoint_id)resp_msg->eid; + max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz); + } + + if (assigned_ep >= ENDPOINT_MAX || assigned_ep <= ENDPOINT_UNUSED + || !max_msg_sz) { + status = -ENOMEM; + goto fail_tx; + } + + endpoint = &target->endpoint[assigned_ep]; + endpoint->eid = assigned_ep; + if (endpoint->svc_id) { + status = -ENOMEM; + goto fail_tx; + } + + /* return assigned endpoint to caller */ + conn_resp->endpoint = assigned_ep; + conn_resp->len_max = max_msg_sz; + + /* setup the endpoint */ + + /* this marks the endpoint in use */ + endpoint->svc_id = conn_req->svc_id; + + endpoint->max_txq_depth = conn_req->max_txq_depth; + endpoint->len_max = max_msg_sz; + endpoint->ep_cb = conn_req->ep_cb; + endpoint->cred_dist.svc_id = conn_req->svc_id; + endpoint->cred_dist.htc_ep = endpoint; + endpoint->cred_dist.endpoint = assigned_ep; + endpoint->cred_dist.cred_sz = target->tgt_cred_sz; + + switch (endpoint->svc_id) { + case WMI_DATA_BK_SVC: + endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 3; + break; + case WMI_DATA_BE_SVC: + endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 4; + break; + case WMI_DATA_VI_SVC: + endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 6; + break; + default: + endpoint->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM; + break; + } + + if (conn_req->max_rxmsg_sz) { + /* + * Override cred_per_msg calculation, this optimizes + * the credit-low indications since the host will actually + * issue smaller messages in the Send path. + */ + if (conn_req->max_rxmsg_sz > max_msg_sz) { + status = -ENOMEM; + goto fail_tx; + } + endpoint->cred_dist.cred_per_msg = + conn_req->max_rxmsg_sz / target->tgt_cred_sz; + } else + endpoint->cred_dist.cred_per_msg = + max_msg_sz / target->tgt_cred_sz; + + if (!endpoint->cred_dist.cred_per_msg) + endpoint->cred_dist.cred_per_msg = 1; + + /* save local connection flags */ + endpoint->conn_flags = conn_req->flags; + +fail_tx: + if (tx_pkt) + htc_reclaim_txctrl_buf(target, tx_pkt); + + if (rx_pkt) { + htc_rxpkt_reset(rx_pkt); + reclaim_rx_ctrl_buf(target, rx_pkt); + } + + return status; +} + +static void reset_ep_state(struct htc_target *target) +{ + struct htc_endpoint *endpoint; + int i; + + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { + endpoint = &target->endpoint[i]; + memset(&endpoint->cred_dist, 0, sizeof(endpoint->cred_dist)); + endpoint->svc_id = 0; + endpoint->len_max = 0; + endpoint->max_txq_depth = 0; + memset(&endpoint->ep_st, 0, + sizeof(endpoint->ep_st)); + INIT_LIST_HEAD(&endpoint->rx_bufq); + INIT_LIST_HEAD(&endpoint->txq); + endpoint->target = target; + } + + /* reset distribution list */ + /* FIXME: free existing entries */ + INIT_LIST_HEAD(&target->cred_dist_list); +} + +static int ath6kl_htc_mbox_get_rxbuf_num(struct htc_target *target, + enum htc_endpoint_id endpoint) +{ + int num; + + spin_lock_bh(&target->rx_lock); + num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq)); + spin_unlock_bh(&target->rx_lock); + return num; +} + +static void htc_setup_msg_bndl(struct htc_target *target) +{ + /* limit what HTC can handle */ + target->msg_per_bndl_max = min(HTC_HOST_MAX_MSG_PER_BUNDLE, + target->msg_per_bndl_max); + + if (ath6kl_hif_enable_scatter(target->dev->ar)) { + target->msg_per_bndl_max = 0; + return; + } + + /* limit bundle what the device layer can handle */ + target->msg_per_bndl_max = min(target->max_scat_entries, + target->msg_per_bndl_max); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "htc bundling allowed msg_per_bndl_max %d\n", + target->msg_per_bndl_max); + + /* Max rx bundle size is limited by the max tx bundle size */ + target->max_rx_bndl_sz = target->max_xfer_szper_scatreq; + /* Max tx bundle size if limited by the extended mbox address range */ + target->max_tx_bndl_sz = min(HIF_MBOX0_EXT_WIDTH, + target->max_xfer_szper_scatreq); + + ath6kl_dbg(ATH6KL_DBG_BOOT, "htc max_rx_bndl_sz %d max_tx_bndl_sz %d\n", + target->max_rx_bndl_sz, target->max_tx_bndl_sz); + + if (target->max_tx_bndl_sz) + /* tx_bndl_mask is enabled per AC, each has 1 bit */ + target->tx_bndl_mask = (1 << WMM_NUM_AC) - 1; + + if (target->max_rx_bndl_sz) + target->rx_bndl_enable = true; + + if ((target->tgt_cred_sz % target->block_sz) != 0) { + ath6kl_warn("credit size: %d is not block aligned! Disabling send bundling\n", + target->tgt_cred_sz); + + /* + * Disallow send bundling since the credit size is + * not aligned to a block size the I/O block + * padding will spill into the next credit buffer + * which is fatal. + */ + target->tx_bndl_mask = 0; + } +} + +static int ath6kl_htc_mbox_wait_target(struct htc_target *target) +{ + struct htc_packet *packet = NULL; + struct htc_ready_ext_msg *rdy_msg; + struct htc_service_connect_req connect; + struct htc_service_connect_resp resp; + int status; + + /* FIXME: remove once USB support is implemented */ + if (target->dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) { + ath6kl_err("HTC doesn't support USB yet. Patience!\n"); + return -EOPNOTSUPP; + } + + /* we should be getting 1 control message that the target is ready */ + packet = htc_wait_for_ctrl_msg(target); + + if (!packet) + return -ENOMEM; + + /* we controlled the buffer creation so it's properly aligned */ + rdy_msg = (struct htc_ready_ext_msg *)packet->buf; + + if ((le16_to_cpu(rdy_msg->ver2_0_info.msg_id) != HTC_MSG_READY_ID) || + (packet->act_len < sizeof(struct htc_ready_msg))) { + status = -ENOMEM; + goto fail_wait_target; + } + + if (!rdy_msg->ver2_0_info.cred_cnt || !rdy_msg->ver2_0_info.cred_sz) { + status = -ENOMEM; + goto fail_wait_target; + } + + target->tgt_creds = le16_to_cpu(rdy_msg->ver2_0_info.cred_cnt); + target->tgt_cred_sz = le16_to_cpu(rdy_msg->ver2_0_info.cred_sz); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "htc target ready credits %d size %d\n", + target->tgt_creds, target->tgt_cred_sz); + + /* check if this is an extended ready message */ + if (packet->act_len >= sizeof(struct htc_ready_ext_msg)) { + /* this is an extended message */ + target->htc_tgt_ver = rdy_msg->htc_ver; + target->msg_per_bndl_max = rdy_msg->msg_per_htc_bndl; + } else { + /* legacy */ + target->htc_tgt_ver = HTC_VERSION_2P0; + target->msg_per_bndl_max = 0; + } + + ath6kl_dbg(ATH6KL_DBG_BOOT, "htc using protocol %s (%d) bndl_size %d\n", + (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1", + target->htc_tgt_ver, + target->msg_per_bndl_max); + + if (target->msg_per_bndl_max > 0) + htc_setup_msg_bndl(target); + + /* setup our pseudo HTC control endpoint connection */ + memset(&connect, 0, sizeof(connect)); + memset(&resp, 0, sizeof(resp)); + connect.ep_cb.rx = htc_ctrl_rx; + connect.ep_cb.rx_refill = NULL; + connect.ep_cb.tx_full = NULL; + connect.max_txq_depth = NUM_CONTROL_BUFFERS; + connect.svc_id = HTC_CTRL_RSVD_SVC; + + /* connect fake service */ + status = ath6kl_htc_mbox_conn_service((void *)target, &connect, &resp); + + if (status) + /* + * FIXME: this call doesn't make sense, the caller should + * call ath6kl_htc_mbox_cleanup() when it wants remove htc + */ + ath6kl_hif_cleanup_scatter(target->dev->ar); + +fail_wait_target: + if (packet) { + htc_rxpkt_reset(packet); + reclaim_rx_ctrl_buf(target, packet); + } + + return status; +} + +/* + * Start HTC, enable interrupts and let the target know + * host has finished setup. + */ +static int ath6kl_htc_mbox_start(struct htc_target *target) +{ + struct htc_packet *packet; + int status; + + memset(&target->dev->irq_proc_reg, 0, + sizeof(target->dev->irq_proc_reg)); + + /* Disable interrupts at the chip level */ + ath6kl_hif_disable_intrs(target->dev); + + target->htc_flags = 0; + target->rx_st_flags = 0; + + /* Push control receive buffers into htc control endpoint */ + while ((packet = htc_get_control_buf(target, false)) != NULL) { + status = htc_add_rxbuf(target, packet); + if (status) + return status; + } + + /* NOTE: the first entry in the distribution list is ENDPOINT_0 */ + ath6kl_credit_init(target->credit_info, &target->cred_dist_list, + target->tgt_creds); + + dump_cred_dist_stats(target); + + /* Indicate to the target of the setup completion */ + status = htc_setup_tx_complete(target); + + if (status) + return status; + + /* unmask interrupts */ + status = ath6kl_hif_unmask_intrs(target->dev); + + if (status) + ath6kl_htc_mbox_stop(target); + + return status; +} + +static int ath6kl_htc_reset(struct htc_target *target) +{ + u32 block_size, ctrl_bufsz; + struct htc_packet *packet; + int i; + + reset_ep_state(target); + + block_size = target->dev->ar->mbox_info.block_size; + + ctrl_bufsz = (block_size > HTC_MAX_CTRL_MSG_LEN) ? + (block_size + HTC_HDR_LENGTH) : + (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH); + + for (i = 0; i < NUM_CONTROL_BUFFERS; i++) { + packet = kzalloc(sizeof(*packet), GFP_KERNEL); + if (!packet) + return -ENOMEM; + + packet->buf_start = kzalloc(ctrl_bufsz, GFP_KERNEL); + if (!packet->buf_start) { + kfree(packet); + return -ENOMEM; + } + + packet->buf_len = ctrl_bufsz; + if (i < NUM_CONTROL_RX_BUFFERS) { + packet->act_len = 0; + packet->buf = packet->buf_start; + packet->endpoint = ENDPOINT_0; + list_add_tail(&packet->list, &target->free_ctrl_rxbuf); + } else + list_add_tail(&packet->list, &target->free_ctrl_txbuf); + } + + return 0; +} + +/* htc_stop: stop interrupt reception, and flush all queued buffers */ +static void ath6kl_htc_mbox_stop(struct htc_target *target) +{ + spin_lock_bh(&target->htc_lock); + target->htc_flags |= HTC_OP_STATE_STOPPING; + spin_unlock_bh(&target->htc_lock); + + /* + * Masking interrupts is a synchronous operation, when this + * function returns all pending HIF I/O has completed, we can + * safely flush the queues. + */ + ath6kl_hif_mask_intrs(target->dev); + + ath6kl_htc_flush_txep_all(target); + + ath6kl_htc_mbox_flush_rx_buf(target); + + ath6kl_htc_reset(target); +} + +static void *ath6kl_htc_mbox_create(struct ath6kl *ar) +{ + struct htc_target *target = NULL; + int status = 0; + + target = kzalloc(sizeof(*target), GFP_KERNEL); + if (!target) { + ath6kl_err("unable to allocate memory\n"); + return NULL; + } + + target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL); + if (!target->dev) { + ath6kl_err("unable to allocate memory\n"); + status = -ENOMEM; + goto err_htc_cleanup; + } + + spin_lock_init(&target->htc_lock); + spin_lock_init(&target->rx_lock); + spin_lock_init(&target->tx_lock); + + INIT_LIST_HEAD(&target->free_ctrl_txbuf); + INIT_LIST_HEAD(&target->free_ctrl_rxbuf); + INIT_LIST_HEAD(&target->cred_dist_list); + + target->dev->ar = ar; + target->dev->htc_cnxt = target; + target->ep_waiting = ENDPOINT_MAX; + + status = ath6kl_hif_setup(target->dev); + if (status) + goto err_htc_cleanup; + + status = ath6kl_htc_reset(target); + if (status) + goto err_htc_cleanup; + + return target; + +err_htc_cleanup: + ath6kl_htc_mbox_cleanup(target); + + return NULL; +} + +/* cleanup the HTC instance */ +static void ath6kl_htc_mbox_cleanup(struct htc_target *target) +{ + struct htc_packet *packet, *tmp_packet; + + /* FIXME: remove check once USB support is implemented */ + if (target->dev->ar->hif_type != ATH6KL_HIF_TYPE_USB) + ath6kl_hif_cleanup_scatter(target->dev->ar); + + list_for_each_entry_safe(packet, tmp_packet, + &target->free_ctrl_txbuf, list) { + list_del(&packet->list); + kfree(packet->buf_start); + kfree(packet); + } + + list_for_each_entry_safe(packet, tmp_packet, + &target->free_ctrl_rxbuf, list) { + list_del(&packet->list); + kfree(packet->buf_start); + kfree(packet); + } + + kfree(target->dev); + kfree(target); +} + +int ath6kl_htc_mbox_stat(struct htc_target *target, + u8 *buf, int buf_len) +{ + struct htc_endpoint *ep; + struct htc_endpoint_stats *ep_st; + struct ath6kl_htc_credit_info *credit_info; + struct list_head *tx_queue; + int i, tmp, len = 0; + + if ((!target) || (!buf)) + return 0; + + credit_info = target->credit_info; + if (!credit_info) + return 0; + + len += snprintf(buf + len, buf_len - len, + " " "\nCredit Size : %d Avail Credit : %d/%d\n", + target->tgt_cred_sz, + credit_info->cur_free_credits, + credit_info->total_avail_credits); + + for (i = ENDPOINT_2; i <= ENDPOINT_5; i++) { + len += snprintf(buf + len, buf_len - len, "EP-%d\n", i); + + ep = &target->endpoint[i]; + tx_queue = &ep->txq; + ep_st = &ep->ep_st; + + spin_lock_bh(&target->tx_lock); + tmp = get_queue_depth(tx_queue); + spin_unlock_bh(&target->tx_lock); + + len += snprintf(buf + len, buf_len - len, + " tx_proc/rx_proc : %d/%d\n", + ep->tx_proc_cnt, ep->rx_proc_cnt); + len += snprintf(buf + len, buf_len - len, + " tx_queue : %d/%d\n", + tmp, ep->max_txq_depth); + len += snprintf(buf + len, buf_len - len, + " seq_no : %d\n", + ep->seqno); + len += snprintf(buf + len, buf_len - len, + " cred_low_indicate : %d\n", + ep_st->cred_low_indicate); + len += snprintf(buf + len, buf_len - len, + " cred_rpt_from_rx : %d\n", + ep_st->cred_rpt_from_rx); + len += snprintf(buf + len, buf_len - len, + " cred_rpt_from_other : %d\n", + ep_st->cred_rpt_from_other); + len += snprintf(buf + len, buf_len - len, + " cred_rpt_ep0 : %d\n", + ep_st->cred_rpt_ep0); + len += snprintf(buf + len, buf_len - len, + " cred_from_rx : %d\n", + ep_st->cred_from_rx); + len += snprintf(buf + len, buf_len - len, + " cred_from_other : %d\n", + ep_st->cred_from_other); + len += snprintf(buf + len, buf_len - len, + " cred_from_ep0 : %d\n", + ep_st->cred_from_ep0); + len += snprintf(buf + len, buf_len - len, + " cred_cosumd : %d\n", + ep_st->cred_cosumd); + len += snprintf(buf + len, buf_len - len, + " cred_retnd : %d\n", + ep_st->cred_retnd); + len += snprintf(buf + len, buf_len - len, + " tx_issued : %d\n", + ep_st->tx_issued); + len += snprintf(buf + len, buf_len - len, + " tx_dropped : %d\n", + ep_st->tx_dropped); + len += snprintf(buf + len, buf_len - len, + " tx_cred_rpt : %d\n", + ep_st->tx_cred_rpt); + len += snprintf(buf + len, buf_len - len, + " rx_pkts : %d\n", + ep_st->rx_pkts); + len += snprintf(buf + len, buf_len - len, + " rx_lkahds : %d\n", + ep_st->rx_lkahds); + len += snprintf(buf + len, buf_len - len, + " rx_alloc_thresh_hit : %d\n", + ep_st->rx_alloc_thresh_hit); + len += snprintf(buf + len, buf_len - len, + " rxalloc_thresh_byte : %d\n", + ep_st->rxalloc_thresh_byte); + len += snprintf(buf + len, buf_len - len, + " tx_pkt_bundled : %d\n", + ep_st->tx_pkt_bundled); + len += snprintf(buf + len, buf_len - len, + " tx_bundles : %d\n", + ep_st->tx_bundles); + len += snprintf(buf + len, buf_len - len, + " rx_bundl : %d\n", + ep_st->rx_bundl); + len += snprintf(buf + len, buf_len - len, + " rx_bundle_lkahd : %d\n", + ep_st->rx_bundle_lkahd); + len += snprintf(buf + len, buf_len - len, + " rx_bundle_from_hdr : %d\n", + ep_st->rx_bundle_from_hdr); + } + + return len; +} + +int ath6kl_htc_mbox_stop_netif_queue_full(struct htc_target *target) +{ + return 0; +} + +int ath6kl_htc_mbox_wmm_schedule_change(struct htc_target *target, + bool change) +{ + struct list_head *ep_list = &target->cred_dist_list; + struct htc_endpoint_credit_dist *cur_ep_dist; + + list_for_each_entry(cur_ep_dist, ep_list, list) { + cur_ep_dist->cred_alloc_max = 0; + + if (cur_ep_dist->svc_id == WMI_DATA_VI_SVC) { + struct htc_endpoint *endpoint = cur_ep_dist->htc_ep; + + if (change == true) { + cur_ep_dist->cred_alloc_max = + cur_ep_dist->cred_norm - 2; + endpoint->tx_drop_packet_threshold = + MAX_DEF_COOKIE_NUM / 4; + } else { + endpoint->tx_drop_packet_threshold = + MAX_DEF_COOKIE_NUM / 6; + } + } + } + + return 1; +} + +int ath6kl_htc_mbox_change_credit_bypass(struct htc_target *target, + u8 traffic_class) +{ + if (traffic_class == WMM_AC_BK) + return 1; + + return 0; +} + +static const struct ath6kl_htc_ops ath6kl_htc_mbox_ops = { + .create = ath6kl_htc_mbox_create, + .wait_target = ath6kl_htc_mbox_wait_target, + .start = ath6kl_htc_mbox_start, + .conn_service = ath6kl_htc_mbox_conn_service, + .tx = ath6kl_htc_mbox_tx, + .stop = ath6kl_htc_mbox_stop, + .cleanup = ath6kl_htc_mbox_cleanup, + .flush_txep = ath6kl_htc_mbox_flush_txep, + .flush_rx_buf = ath6kl_htc_mbox_flush_rx_buf, + .indicate_activity_change = ath6kl_htc_mbox_indicate_activity_change, + .get_rxbuf_num = ath6kl_htc_mbox_get_rxbuf_num, + .add_rxbuf_multiple = ath6kl_htc_mbox_add_rxbuf_multiple, + .credit_setup = ath6kl_htc_mbox_credit_setup, + .get_stat = ath6kl_htc_mbox_stat, + .stop_netif_queue_full = ath6kl_htc_mbox_stop_netif_queue_full, + .indicate_wmm_schedule_change = ath6kl_htc_mbox_wmm_schedule_change, + .change_credit_bypass = ath6kl_htc_mbox_change_credit_bypass, +}; + +void ath6kl_htc_mbox_attach(struct ath6kl *ar) +{ + ar->htc_ops = &ath6kl_htc_mbox_ops; +} + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/htc.h b/drivers/net/wireless/ath/ath6kl-3.5/htc.h new file mode 100644 index 000000000000..c0462f886517 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/htc.h @@ -0,0 +1,692 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 HTC_H +#define HTC_H + +#include "core.h" +#include "common.h" + +/* frame header flags */ + +/* send direction */ +#define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0) +#define HTC_FLAGS_SEND_BUNDLE (1 << 1) +#define HTC_FLAGS_TX_FIXUP_NETBUF (1 << 2) + +/* receive direction */ +#define HTC_FLG_RX_UNUSED (1 << 0) +#define HTC_FLG_RX_TRAILER (1 << 1) +/* Bundle count maske and shift */ +#define HTC_FLG_RX_BNDL_CNT (0xF0) +#define HTC_FLG_RX_BNDL_CNT_S 4 + +#define HTC_HDR_LENGTH (sizeof(struct htc_frame_hdr)) +#define HTC_MAX_PAYLOAD_LENGTH (4096 - sizeof(struct htc_frame_hdr)) + +/* HTC control message IDs */ + +#define HTC_MSG_READY_ID 1 +#define HTC_MSG_CONN_SVC_ID 2 +#define HTC_MSG_CONN_SVC_RESP_ID 3 +#define HTC_MSG_SETUP_COMPLETE_ID 4 +#define HTC_MSG_SETUP_COMPLETE_EX_ID 5 + +#define HTC_MAX_CTRL_MSG_LEN 256 + +#define HTC_VERSION_2P0 0x00 +#define HTC_VERSION_2P1 0x01 + +#define HTC_SERVICE_META_DATA_MAX_LENGTH 128 + +#define HTC_CONN_FLGS_THRESH_LVL_QUAT 0x0 +#define HTC_CONN_FLGS_THRESH_LVL_HALF 0x1 +#define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2 +#define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4 +#define HTC_CONN_FLGS_THRESH_MASK 0x3 +/* disable credit flow control on a specific service */ +#define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3) +#define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8 +#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00 + +/* connect response status codes */ +#define HTC_SERVICE_SUCCESS 0 +#define HTC_SERVICE_NOT_FOUND 1 +#define HTC_SERVICE_FAILED 2 + +/* no resources (i.e. no more endpoints) */ +#define HTC_SERVICE_NO_RESOURCES 3 + +/* specific service is not allowing any more endpoints */ +#define HTC_SERVICE_NO_MORE_EP 4 + +/* report record IDs */ +#define HTC_RECORD_NULL 0 +#define HTC_RECORD_CREDITS 1 +#define HTC_RECORD_LOOKAHEAD 2 +#define HTC_RECORD_LOOKAHEAD_BUNDLE 3 + +#define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0) +#define HTC_SETUP_COMP_FLG_DISABLE_TX_CREDIT_FLOW (1 << 1) + +#define MAKE_SERVICE_ID(group, index) \ + (int)(((int)group << 8) | (int)(index)) + +/* NOTE: service ID of 0x0000 is reserved and should never be used */ +#define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1) +#define WMI_CONTROL_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0) +#define WMI_DATA_BE_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1) +#define WMI_DATA_BK_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2) +#define WMI_DATA_VI_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3) +#define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4) +#define WMI_MAX_SERVICES 5 + +#define WMM_NUM_AC 4 + +/* reserved and used to flush ALL packets */ +#define HTC_TX_PACKET_TAG_ALL 0 +#define HTC_SERVICE_TX_PACKET_TAG 1 +#define HTC_TX_PACKET_TAG_USER_DEFINED (HTC_SERVICE_TX_PACKET_TAG + 9) + +/* more packets on this endpoint are being fetched */ +#define HTC_RX_FLAGS_INDICATE_MORE_PKTS (1 << 0) + +/* TODO.. for BMI */ +#define ENDPOINT1 0 +/* TODO -remove me, but we have to fix BMI first */ +#define HTC_MAILBOX_NUM_MAX 4 + +/* enable send bundle padding for this endpoint */ +#define HTC_FLGS_TX_BNDL_PAD_EN (1 << 0) +#define HTC_EP_ACTIVE ((u32) (1u << 31)) + +/* HTC operational parameters */ +#define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */ +#define HTC_TARGET_RESPONSE_POLL_WAIT 10 +#define HTC_TARGET_RESPONSE_POLL_MS (HTC_TARGET_RESPONSE_POLL_WAIT) +#define HTC_TARGET_RESPONSE_POLL_COUNT 200 +#define HTC_TARGET_DEBUG_INTR_MASK 0x01 +#define HTC_TARGET_CREDIT_INTR_MASK 0xF0 + +#define HTC_HOST_MAX_MSG_PER_BUNDLE 8 +#define HTC_MIN_HTC_MSGS_TO_BUNDLE 2 + +/* packet flags */ + +#define HTC_RX_PKT_IGNORE_LOOKAHEAD (1 << 0) +#define HTC_RX_PKT_REFRESH_HDR (1 << 1) +#define HTC_RX_PKT_PART_OF_BUNDLE (1 << 2) +#define HTC_RX_PKT_NO_RECYCLE (1 << 3) + +#define NUM_CONTROL_BUFFERS 8 +#define NUM_CONTROL_TX_BUFFERS 2 +#define NUM_CONTROL_RX_BUFFERS (NUM_CONTROL_BUFFERS - NUM_CONTROL_TX_BUFFERS) + +#define HTC_RECV_WAIT_BUFFERS (1 << 0) +#define HTC_OP_STATE_STOPPING (1 << 0) +#define HTC_OP_STATE_SETUP_COMPLETE (1 << 1) + +/* + * The frame header length and message formats defined herein were selected + * to accommodate optimal alignment for target processing. This reduces + * code size and improves performance. Any changes to the header length may + * alter the alignment and cause exceptions on the target. When adding to + * the messagestructures insure that fields are properly aligned. + */ + +/* HTC frame header + * + * NOTE: do not remove or re-arrange the fields, these are minimally + * required to take advantage of 4-byte lookaheads in some hardware + * implementations. + */ +struct htc_frame_hdr { + u8 eid; + u8 flags; + + /* length of data (including trailer) that follows the header */ + __le16 payld_len; + + /* end of 4-byte lookahead */ + + u8 ctrl[2]; +} __packed; + +/* HTC ready message */ +struct htc_ready_msg { + __le16 msg_id; + __le16 cred_cnt; + __le16 cred_sz; + u8 max_ep; + u8 pad; +} __packed; + +/* extended HTC ready message */ +struct htc_ready_ext_msg { + struct htc_ready_msg ver2_0_info; + u8 htc_ver; + u8 msg_per_htc_bndl; +} __packed; + +/* connect service */ +struct htc_conn_service_msg { + __le16 msg_id; + __le16 svc_id; + __le16 conn_flags; + u8 svc_meta_len; + u8 pad; +} __packed; + +/* connect response */ +struct htc_conn_service_resp { + __le16 msg_id; + __le16 svc_id; + u8 status; + u8 eid; + __le16 max_msg_sz; + u8 svc_meta_len; + u8 pad; +} __packed; + +struct htc_setup_comp_msg { + __le16 msg_id; +} __packed; + +/* extended setup completion message */ +struct htc_setup_comp_ext_msg { + __le16 msg_id; + __le32 flags; + u8 msg_per_rxbndl; + u8 Rsvd[3]; +} __packed; + +struct htc_record_hdr { + u8 rec_id; + u8 len; +} __packed; + +struct htc_credit_report { + u8 eid; + u8 credits; +} __packed; + +/* + * NOTE: The lk_ahd array is guarded by a pre_valid + * and Post Valid guard bytes. The pre_valid bytes must + * equal the inverse of the post_valid byte. + */ +struct htc_lookahead_report { + u8 pre_valid; + u8 lk_ahd[4]; + u8 post_valid; +} __packed; + +struct htc_bundle_lkahd_rpt { + u8 lk_ahd[4]; +} __packed; + +/* Current service IDs */ + +enum htc_service_grp_ids { + RSVD_SERVICE_GROUP = 0, + WMI_SERVICE_GROUP = 1, + + HTC_TEST_GROUP = 254, + HTC_SERVICE_GROUP_LAST = 255 +}; + +/* ------ endpoint IDS ------ */ + +enum htc_endpoint_id { + ENDPOINT_UNUSED = -1, + ENDPOINT_0 = 0, + ENDPOINT_1 = 1, + ENDPOINT_2 = 2, + ENDPOINT_3, + ENDPOINT_4, + ENDPOINT_5, + ENDPOINT_6, + ENDPOINT_7, + ENDPOINT_8, + ENDPOINT_MAX, +}; + +struct htc_tx_packet_info { + u16 tag; + int cred_used; + u8 flags; + int seqno; +}; + +struct htc_rx_packet_info { + u32 exp_hdr; + u32 rx_flags; + u32 indicat_flags; +}; + +struct htc_target; + +/* wrapper around endpoint-specific packets */ +struct htc_packet { + struct list_head list; + + /* caller's per packet specific context */ + void *pkt_cntxt; + + /* + * the true buffer start , the caller can store the real + * buffer start here. In receive callbacks, the HTC layer + * sets buf to the start of the payload past the header. + * This field allows the caller to reset buf when it recycles + * receive packets back to HTC. + */ + u8 *buf_start; + + /* + * Pointer to the start of the buffer. In the transmit + * direction this points to the start of the payload. In the + * receive direction, however, the buffer when queued up + * points to the start of the HTC header but when returned + * to the caller points to the start of the payload + */ + u8 *buf; + u32 buf_len; + + /* actual length of payload */ + u32 act_len; + + /* endpoint that this packet was sent/recv'd from */ + enum htc_endpoint_id endpoint; + + /* completion status */ + + int status; + union { + struct htc_tx_packet_info tx; + struct htc_rx_packet_info rx; + } info; + + void (*completion) (struct htc_target *, struct htc_packet *); + struct htc_target *context; + + /* + * optimization for network-oriented data, the HTC packet + * can pass the network buffer corresponding to the HTC packet + * lower layers may optimized the transfer knowing this is + * a network buffer + */ + struct sk_buff *skb; + + /* P2P flowctrl */ + u8 connid; + u8 recycle_count; + + struct ath6kl_vif *vif; +}; + +enum htc_send_full_action { + HTC_SEND_FULL_KEEP = 0, + HTC_SEND_FULL_DROP = 1, +}; + +struct htc_ep_callbacks { + void (*tx_complete) (struct htc_target *, struct htc_packet *); + void (*rx) (struct htc_target *, struct htc_packet *); + void (*rx_refill) (struct htc_target *, enum htc_endpoint_id endpoint); + enum htc_send_full_action (*tx_full) (struct htc_target *, + struct htc_packet *); + struct htc_packet *(*rx_allocthresh) (struct htc_target *, + enum htc_endpoint_id, int); + void (*tx_comp_multi) (struct htc_target *, struct list_head *); + int rx_alloc_thresh; + int rx_refill_thresh; +}; + +/* service connection information */ +struct htc_service_connect_req { + u16 svc_id; + u16 conn_flags; + struct htc_ep_callbacks ep_cb; + int max_txq_depth; + u32 flags; + unsigned int max_rxmsg_sz; +}; + +/* service connection response information */ +struct htc_service_connect_resp { + u8 buf_len; + u8 act_len; + enum htc_endpoint_id endpoint; + unsigned int len_max; + u8 resp_code; +}; + +/* endpoint distributionstructure */ +struct htc_endpoint_credit_dist { + struct list_head list; + + /* Service ID (set by HTC) */ + u16 svc_id; + + /* endpoint for this distributionstruct (set by HTC) */ + enum htc_endpoint_id endpoint; + + u32 dist_flags; + + /* + * credits for normal operation, anything above this + * indicates the endpoint is over-subscribed. + */ + int cred_norm; + + /* floor for credit distribution */ + int cred_min; + + int cred_assngd; + + /* current credits available */ + int credits; + + /* + * pending credits to distribute on this endpoint, this + * is set by HTC when credit reports arrive. The credit + * distribution functions sets this to zero when it distributes + * the credits. + */ + int cred_to_dist; + + /* + * the number of credits that the current pending TX packet needs + * to transmit. This is set by HTC when endpoint needs credits in + * order to transmit. + */ + int seek_cred; + + /* size in bytes of each credit */ + int cred_sz; + + /* credits required for a maximum sized messages */ + int cred_per_msg; + + /* reserved for HTC use */ + struct htc_endpoint *htc_ep; + + /* + * current depth of TX queue , i.e. messages waiting for credits + * This field is valid only when HTC_CREDIT_DIST_ACTIVITY_CHANGE + * or HTC_CREDIT_DIST_SEND_COMPLETE is indicated on an endpoint + * that has non-zero credits to recover. + */ + int txq_depth; + + /* maximum credits the ep can allocate */ + int cred_alloc_max; +}; + +/* + * credit distibution code that is passed into the distrbution function, + * there are mandatory and optional codes that must be handled + */ +enum htc_credit_dist_reason { + HTC_CREDIT_DIST_SEND_COMPLETE = 0, + HTC_CREDIT_DIST_ACTIVITY_CHANGE = 1, + HTC_CREDIT_DIST_SEEK_CREDITS, +}; + +struct ath6kl_htc_credit_info { + int total_avail_credits; + int cur_free_credits; + + /* list of lowest priority endpoints */ + struct list_head lowestpri_ep_dist; +}; + +/* endpoint statistics */ +struct htc_endpoint_stats { + /* + * number of times the host set the credit-low flag in a send + * message on this endpoint + */ + u32 cred_low_indicate; + + u32 tx_issued; + u32 tx_pkt_bundled; + u32 tx_bundles; + u32 tx_dropped; + + /* running count of total credit reports received for this endpoint */ + u32 tx_cred_rpt; + + /* credit reports received from this endpoint's RX packets */ + u32 cred_rpt_from_rx; + + /* credit reports received from RX packets of other endpoints */ + u32 cred_rpt_from_other; + + /* credit reports received from endpoint 0 RX packets */ + u32 cred_rpt_ep0; + + /* count of credits received via Rx packets on this endpoint */ + u32 cred_from_rx; + + /* count of credits received via another endpoint */ + u32 cred_from_other; + + /* count of credits received via another endpoint */ + u32 cred_from_ep0; + + /* count of consummed credits */ + u32 cred_cosumd; + + /* count of credits returned */ + u32 cred_retnd; + + u32 rx_pkts; + + /* count of lookahead records found in Rx msg */ + u32 rx_lkahds; + + /* count of recv packets received in a bundle */ + u32 rx_bundl; + + /* count of number of bundled lookaheads */ + u32 rx_bundle_lkahd; + + /* count of the number of bundle indications from the HTC header */ + u32 rx_bundle_from_hdr; + + /* the number of times the recv allocation threshold was hit */ + u32 rx_alloc_thresh_hit; + + /* total number of bytes */ + u32 rxalloc_thresh_byte; +}; + +struct htc_endpoint { + enum htc_endpoint_id eid; + u16 svc_id; + struct list_head txq; + struct list_head rx_bufq; + struct htc_endpoint_credit_dist cred_dist; + struct htc_ep_callbacks ep_cb; + int max_txq_depth; + int len_max; + int tx_proc_cnt; + int rx_proc_cnt; + struct htc_target *target; + u8 seqno; + u32 conn_flags; + struct htc_endpoint_stats ep_st; + u16 tx_drop_packet_threshold; + + u8 pipeid_ul; + u8 pipeid_dl; + struct list_head tx_lookup_queue; + bool tx_credit_flow_enabled; + + struct timer_list timer; + u8 call_by_timer; + u8 timer_init; + u8 pass_th; + u8 starving; + unsigned long last_sent; +}; + +struct htc_control_buffer { + struct htc_packet packet; + u8 *buf; +}; + +struct htc_pipe_txcredit_alloc { + u16 service_id; + u8 credit_alloc; +}; + +enum htc_send_queue_result { + HTC_SEND_QUEUE_OK = 0, /* packet was queued */ + HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */ +}; + +struct ath6kl_htc_ops { + void* (*create)(struct ath6kl *ar); + int (*wait_target)(struct htc_target *target); + int (*start)(struct htc_target *target); + int (*conn_service)(struct htc_target *target, + struct htc_service_connect_req *req, + struct htc_service_connect_resp *resp); + int (*tx)(struct htc_target *target, struct htc_packet *packet); + void (*stop)(struct htc_target *target); + void (*cleanup)(struct htc_target *target); + void (*flush_txep)(struct htc_target *target, + enum htc_endpoint_id endpoint, u16 tag); + void (*flush_rx_buf)(struct htc_target *target); + void (*indicate_activity_change)(struct htc_target *target, + enum htc_endpoint_id endpoint, + bool active); + int (*get_rxbuf_num)(struct htc_target *target, + enum htc_endpoint_id endpoint); + int (*add_rxbuf_multiple)(struct htc_target *target, + struct list_head *pktq); + int (*credit_setup)(struct htc_target *target, + struct ath6kl_htc_credit_info *cred_info); + int (*get_stat)(struct htc_target *target, + u8 *buf, int buf_len); + int (*stop_netif_queue_full)(struct htc_target *target); + int (*indicate_wmm_schedule_change)(struct htc_target *target, + bool change); + int (*change_credit_bypass)(struct htc_target *target, + u8 traffic_class); +}; + +struct ath6kl_device; + +/* our HTC target state */ +struct htc_target { + struct htc_endpoint endpoint[ENDPOINT_MAX]; + + /* contains struct htc_endpoint_credit_dist */ + struct list_head cred_dist_list; + + struct list_head free_ctrl_txbuf; + struct list_head free_ctrl_rxbuf; + struct ath6kl_htc_credit_info *credit_info; + int tgt_creds; + unsigned int tgt_cred_sz; + spinlock_t htc_lock; + spinlock_t rx_lock; + spinlock_t tx_lock; + struct ath6kl_device *dev; + u32 htc_flags; + u32 rx_st_flags; + enum htc_endpoint_id ep_waiting; + u8 htc_tgt_ver; + + /* max messages per bundle for HTC */ + int msg_per_bndl_max; + + u32 tx_bndl_mask; + int rx_bndl_enable; + int max_rx_bndl_sz; + int max_tx_bndl_sz; + + u32 block_sz; + u32 block_mask; + + int max_scat_entries; + int max_xfer_szper_scatreq; + + int chk_irq_status_cnt; + + /* counts the number of Tx without bundling continously per AC */ + u32 ac_tx_count[WMM_NUM_AC]; + + struct htc_packet *htc_packet_pool; /* pool of HTC packets */ + u8 ctrl_response_buf[HTC_MAX_CTRL_MSG_LEN]; + int ctrl_response_len; + bool ctrl_response_valid; + struct htc_pipe_txcredit_alloc txcredit_alloc[ENDPOINT_MAX]; + int avail_tx_credits; + + struct sk_buff_head rx_sg_q; + bool rx_sg_in_progress; + u32 rx_sg_total_len_cur; /* current total length */ + u32 rx_sg_total_len_exp; /* expected total length */ + +}; + +int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, + u32 msg_look_ahead, int *n_pkts); + +static inline void set_htc_pkt_info(struct htc_packet *packet, void *context, + u8 *buf, unsigned int len, + enum htc_endpoint_id eid, u16 tag) +{ + packet->pkt_cntxt = context; + packet->buf = buf; + packet->act_len = len; + packet->endpoint = eid; + packet->info.tx.tag = tag; +} + +static inline void htc_rxpkt_reset(struct htc_packet *packet) +{ + packet->buf = packet->buf_start; + packet->act_len = 0; +} + +static inline void set_htc_rxpkt_info(struct htc_packet *packet, void *context, + u8 *buf, unsigned long len, + enum htc_endpoint_id eid) +{ + packet->pkt_cntxt = context; + packet->buf = buf; + packet->buf_start = buf; + packet->buf_len = len; + packet->endpoint = eid; +} + +static inline int get_queue_depth(struct list_head *queue) +{ + struct list_head *tmp_list; + int depth = 0; + + list_for_each(tmp_list, queue) + depth++; + + return depth; +} + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/htc_pipe.c b/drivers/net/wireless/ath/ath6kl-3.5/htc_pipe.c new file mode 100644 index 000000000000..eba0bc8ff8ba --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/htc_pipe.c @@ -0,0 +1,2537 @@ +/* + * Copyright (c) 2007-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "debug.h" +#include "hif-ops.h" + +#include + +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 +#include +#endif + +#define HTC_PACKET_CONTAINER_ALLOCATION 32 +#define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH) +#define DATA_EP_SIZE 4 + +static int ath6kl_htc_pipe_tx(struct htc_target *handle, + struct htc_packet *packet); +static void ath6kl_htc_pipe_cleanup(struct htc_target *handle); +static void htc_tx_bundle_timer_handler(unsigned long ptr); + +/* htc pipe tx path */ +static inline void restore_tx_packet(struct htc_packet *packet) +{ + if (packet->info.tx.flags & HTC_FLAGS_TX_FIXUP_NETBUF) { + skb_pull(packet->skb, sizeof(struct htc_frame_hdr)); + packet->info.tx.flags &= ~HTC_FLAGS_TX_FIXUP_NETBUF; + } +} + +static void do_send_completion(struct htc_endpoint *ep, + struct list_head *queue_to_indicate) +{ + if (list_empty(queue_to_indicate)) { + /* nothing to indicate */ + return; + } + + if (ep->ep_cb.tx_comp_multi != NULL) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: calling ep %d, send complete multiple callback" + "(%d pkts)\n", __func__, + ep->eid, get_queue_depth(queue_to_indicate)); + /* + * a multiple send complete handler is being used, + * pass the queue to the handler + */ + ep->ep_cb.tx_comp_multi( + (struct htc_target *) ep->target, + queue_to_indicate); + /* + * all packets are now owned by the callback, + * reset queue to be safe + */ + INIT_LIST_HEAD(queue_to_indicate); + } else { + struct htc_packet *packet; + /* using legacy EpTxComplete */ + do { + packet = list_first_entry(queue_to_indicate, + struct htc_packet, list); + + list_del(&packet->list); + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: calling ep %d send complete callback on " + "packet 0x%lX\n", __func__, + ep->eid, (unsigned long)(packet)); + ep->ep_cb.tx_complete( + (struct htc_target *) ep->target, + packet); + } while (!list_empty(queue_to_indicate)); + } + +} + +static void send_packet_completion(struct htc_target *target, + struct htc_packet *packet) +{ + struct htc_endpoint *ep = &target->endpoint[packet->endpoint]; + struct list_head container; + + restore_tx_packet(packet); + INIT_LIST_HEAD(&container); + list_add_tail(&packet->list, &container); + /* do completion */ + do_send_completion(ep, &container); +} + +static void get_htc_packet_credit_based(struct htc_target *target, + struct htc_endpoint *ep, + struct list_head *queue) +{ + int credits_required; + int remainder; + u8 send_flags; + struct htc_packet *packet; + unsigned int transfer_len; + int starving = ep->starving; + int msgs_upper_limit = + (htc_bundle_send) ? HTC_HOST_MAX_MSG_PER_BUNDLE : 1; + + if (target->tgt_cred_sz == 0) + return; + + /* NOTE : the TX lock is held when this function is called */ + /* loop until we can grab as many packets out of the queue as we can */ + while (true) { + send_flags = 0; + if (list_empty(&ep->txq)) + break; + + /* get packet at head, but don't remove it */ + packet = list_first_entry(&ep->txq, + struct htc_packet, list); + if (packet == NULL) + break; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: got head packet:0x%lX , queue depth: %d\n", + __func__, (unsigned long)packet, + get_queue_depth(&ep->txq)); + + transfer_len = packet->act_len + HTC_HDR_LENGTH; + if (transfer_len <= target->tgt_cred_sz) { + credits_required = 1; + } else { + /* figure out how many credits this message requires */ + credits_required = + transfer_len / target->tgt_cred_sz; + remainder = transfer_len % target->tgt_cred_sz; + + if (remainder) + credits_required++; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, "%s: creds required:%d" + "got:%d\n", __func__, credits_required, + ep->cred_dist.credits); + + if (ep->eid == ENDPOINT_0) { + /* + * endpoint 0 is special, it always has a credit and + * does not require credit based flow control + */ + credits_required = 0; + + } else if (ep->eid >= ENDPOINT_2 && ep->eid <= ENDPOINT_5) { + + if (target->avail_tx_credits < credits_required) + break; + + if (htc_bundle_send && !msgs_upper_limit) + break; + + target->avail_tx_credits -= credits_required; + ep->ep_st.cred_cosumd += credits_required; + + ep->starving = 0; + ep->last_sent = jiffies; + + if (target->avail_tx_credits < 1) { + /* tell the target we need credits ASAP! */ + send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; + ep->ep_st.cred_low_indicate += 1; + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: host needs credits\n", __func__); + } + + } else { + + if (ep->cred_dist.credits < credits_required) + break; + + if (htc_bundle_send && !msgs_upper_limit) + break; + + ep->cred_dist.credits -= credits_required; + ep->ep_st.cred_cosumd += credits_required; + + /* check if we need credits back from the target */ + if (ep->cred_dist.credits < + ep->cred_dist.cred_per_msg) { + /* tell the target we need credits ASAP! */ + send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; + ep->ep_st.cred_low_indicate += 1; + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: host needs credits\n", __func__); + } + } + + /* now we can fully dequeue */ + packet = list_first_entry(&ep->txq, + struct htc_packet, list); + + list_del(&packet->list); + /* save the number of credits this packet consumed */ + packet->info.tx.cred_used = credits_required; + /* save send flags */ + packet->info.tx.flags = send_flags; + packet->info.tx.seqno = ep->seqno; + ep->seqno++; + /* queue this packet into the caller's queue */ + list_add_tail(&packet->list, queue); + + if (htc_bundle_send) + msgs_upper_limit--; + + /* if it is a starving EP, consumes only one credit */ + if (starving) + break; + } + +} + +static void get_htc_packet(struct htc_target *target, + struct htc_endpoint *ep, + struct list_head *queue, int resources) +{ + + struct htc_packet *packet; + + int msgs_upper_limit = + (htc_bundle_send) ? HTC_HOST_MAX_MSG_PER_BUNDLE : resources; + + /* NOTE : the TX lock is held when this function is called */ + if (htc_bundle_send && !resources) + return; + + /* loop until we can grab as many packets out of the queue as we can */ + while (msgs_upper_limit) { + if (list_empty(&ep->txq)) + break; + + packet = list_first_entry(&ep->txq, + struct htc_packet, list); + list_del(&packet->list); + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: got packet:0x%lX , new queue depth: %d\n", + __func__, (unsigned long)packet, + get_queue_depth(&ep->txq)); + packet->info.tx.seqno = ep->seqno; + packet->info.tx.flags = 0; + packet->info.tx.cred_used = 0; + ep->seqno++; + /* queue this packet into the caller's queue */ + list_add_tail(&packet->list, queue); + msgs_upper_limit--; + } + +} + +static int htc_issue_packets(struct htc_target *target, + struct htc_endpoint *ep, + struct list_head *pkt_queue) +{ + int status = 0; + u16 payload_len; + struct sk_buff *nbuf; + struct htc_frame_hdr *htc_hdr; + struct htc_packet *packet = NULL; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: queue: 0x%lX, pkts %d\n", __func__, + (unsigned long)pkt_queue, get_queue_depth(pkt_queue)); + if (htc_bundle_send && (ep->pipeid_ul != 0 /* HIF_TX_CTRL_PIPE */)) { + /* only for HIF data pipes */ + struct sk_buff *msg_bundle[HTC_HOST_MAX_MSG_PER_BUNDLE] = {}; + int msgs_to_bundle = 0; + int bundle_credit_used = 0; + + while (!list_empty(pkt_queue)) { + packet = list_first_entry(pkt_queue, + struct htc_packet, list); + list_del(&packet->list); + + nbuf = packet->skb; + if (!nbuf) { + WARN_ON(1); + status = -EINVAL; + break; + } + + payload_len = packet->act_len; + /* setup HTC frame header */ + htc_hdr = (struct htc_frame_hdr *)skb_push(nbuf, + sizeof(struct htc_frame_hdr)); + if (!htc_hdr) { + WARN_ON(1); + status = -EINVAL; + break; + } + packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF; + packet->info.tx.flags |= HTC_FLAGS_SEND_BUNDLE; + + /* Endianess? */ + put_unaligned((u16) payload_len, &htc_hdr->payld_len); + htc_hdr->payld_len = cpu_to_le16(htc_hdr->payld_len); + htc_hdr->flags = packet->info.tx.flags; + htc_hdr->eid = (u8) packet->endpoint; + htc_hdr->ctrl[0] = 0; + htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno; + + spin_lock_bh(&target->tx_lock); + /* store in look up queue to match completions */ + list_add_tail(&packet->list, &ep->tx_lookup_queue); + ep->ep_st.tx_issued += 1; + spin_unlock_bh(&target->tx_lock); + + /* pkt_queue is less than + * HTC_HOST_MAX_MSG_PER_BUNDLE + */ + msg_bundle[msgs_to_bundle] = nbuf; + msgs_to_bundle++; + bundle_credit_used += packet->info.tx.cred_used; + } + + status = ath6kl_hif_pipe_send_bundle(target->dev->ar, + ep->pipeid_ul, msg_bundle, msgs_to_bundle); + + if (status != 0) { + spin_lock_bh(&target->tx_lock); + /* reclaim credits */ + if (ep->eid >= ENDPOINT_2 && ep->eid <= ENDPOINT_5) + target->avail_tx_credits += bundle_credit_used; + else + ep->cred_dist.credits += bundle_credit_used; + + ep->ep_st.cred_retnd += bundle_credit_used; + ep->ep_st.tx_dropped += bundle_credit_used; + + spin_unlock_bh(&target->tx_lock); + } + } else { + + while (!list_empty(pkt_queue)) { + packet = list_first_entry(pkt_queue, + struct htc_packet, list); + list_del(&packet->list); + + nbuf = packet->skb; + if (!nbuf) { + WARN_ON(1); + status = -EINVAL; + break; + } + + payload_len = packet->act_len; + /* setup HTC frame header */ + htc_hdr = (struct htc_frame_hdr *)skb_push(nbuf, + sizeof(struct htc_frame_hdr)); + if (!htc_hdr) { + WARN_ON(1); + status = -EINVAL; + break; + } + + packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF; + + /* Endianess? */ + put_unaligned((u16) payload_len, &htc_hdr->payld_len); + htc_hdr->payld_len = cpu_to_le16(htc_hdr->payld_len); + htc_hdr->flags = packet->info.tx.flags; + htc_hdr->eid = (u8) packet->endpoint; + htc_hdr->ctrl[0] = 0; + htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno; + + spin_lock_bh(&target->tx_lock); + /* store in look up queue to match completions */ + list_add_tail(&packet->list, &ep->tx_lookup_queue); + ep->ep_st.tx_issued += 1; + spin_unlock_bh(&target->tx_lock); + + status = ath6kl_hif_pipe_send(target->dev->ar, + ep->pipeid_ul, NULL, nbuf); + + if (status != 0) { + if (status != -ENOMEM) { + /* TODO: if more than 1 endpoint maps + * to the same PipeID, it is possible + * to run out of resources in the HIF + * layer. Don't emit the error + */ + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: failed status:%d\n", + __func__, status); + } + spin_lock_bh(&target->tx_lock); + list_del(&packet->list); + /* reclaim credits */ + if (ep->eid >= ENDPOINT_2 && + ep->eid <= ENDPOINT_5) + target->avail_tx_credits += + packet->info.tx.cred_used; + else + ep->cred_dist.credits += + packet->info.tx.cred_used; + + ep->ep_st.tx_dropped += + packet->info.tx.cred_used; + ep->ep_st.cred_retnd += + packet->info.tx.cred_used; + spin_unlock_bh(&target->tx_lock); + /* put it back into the callers queue */ + list_add(&packet->list, pkt_queue); + break; + } + } + } + + if (status != 0) { + while (!list_empty(pkt_queue)) { + if (status != -ENOMEM) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: failed pkt:0x%p status:%d\n", + __func__, packet, status); + } + packet = list_first_entry(pkt_queue, + struct htc_packet, list); + list_del(&packet->list); + packet->status = status; + send_packet_completion(target, packet); + } + } + + return status; +} + +static enum htc_send_queue_result htc_try_send(struct htc_target *target, + struct htc_endpoint *ep, + struct list_head *callers_send_queue) +{ + struct list_head send_queue; /* temp queue to hold packets */ + struct htc_packet *packet, *tmp_pkt; + struct ath6kl *ar = target->dev->ar; + int tx_resources; + int starving; + int overflow, txqueue_depth; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: (queue:0x%lX depth:%d)\n", + __func__, (unsigned long)callers_send_queue, + (callers_send_queue == + NULL) ? 0 : get_queue_depth(callers_send_queue)); + + if (htc_bundle_send && htc_bundle_send_timer && + ep->eid == ENDPOINT_2) { + /* init bundle send timer */ + if (ep->timer_init == 0) { + setup_timer(&ep->timer, htc_tx_bundle_timer_handler, + (unsigned long) target); + mod_timer(&ep->timer, + jiffies + msecs_to_jiffies(htc_bundle_send_timer)); + ep->timer_init = 1; + } + /* check if we need to queue packet for bundle send */ + if (ep->call_by_timer == 0 && ep->pass_th) { + spin_lock_bh(&target->tx_lock); + if (get_queue_depth(&ep->txq) < + HTC_HOST_MAX_MSG_PER_BUNDLE) { + spin_unlock_bh(&target->tx_lock); + return 0; + } + spin_unlock_bh(&target->tx_lock); + } + } + + /* init the local send queue */ + INIT_LIST_HEAD(&send_queue); + /* + * callers_send_queue equals to NULL means + * caller didn't provide a queue, just wants us to + * check queues and send + */ + if (callers_send_queue != NULL) { + if (list_empty(callers_send_queue)) { + /* empty queue */ + return HTC_SEND_QUEUE_DROP; + } + + spin_lock_bh(&target->tx_lock); + txqueue_depth = get_queue_depth(&ep->txq); + spin_unlock_bh(&target->tx_lock); + + if (txqueue_depth >= ep->max_txq_depth) { + /* we've already overflowed */ + overflow = get_queue_depth(callers_send_queue); + } else { + /* get how much we will overflow by */ + overflow = txqueue_depth; + overflow += get_queue_depth(callers_send_queue); + /* get how much we will overflow the TX queue by */ + overflow -= ep->max_txq_depth; + } + + /* if overflow is negative or zero, we are okay */ + if (overflow > 0) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: Endpoint %d, TX queue will overflow :%d, " + "Tx Depth:%d, Max:%d\n", __func__, + ep->eid, overflow, txqueue_depth, + ep->max_txq_depth); + } + if ((overflow <= 0) + || (ep->ep_cb.tx_full == NULL)) { + /* + * all packets will fit or caller did not provide send + * full indication handler -- just move all of them + * to the local send_queue object + */ + list_splice_tail_init(callers_send_queue, + &send_queue); + } else { + int i; + int good_pkts = + get_queue_depth(callers_send_queue) - overflow; + if (good_pkts < 0) { + WARN_ON(1); + return HTC_SEND_QUEUE_DROP; + } + + /* we have overflowed, and a callback is provided */ + /* dequeue all non-overflow packets to the sendqueue */ + for (i = 0; i < good_pkts; i++) { + /* pop off caller's queue */ + packet = list_first_entry( + callers_send_queue, + struct htc_packet, list); + list_del(&packet->list); + /* insert into local queue */ + list_add_tail(&packet->list, &send_queue); + } + + /* + * the caller's queue has all the packets that won't fit + * walk through the caller's queue and indicate each to + * the send full handler + */ + list_for_each_entry_safe(packet, tmp_pkt, + callers_send_queue, list) { + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: Indicat overflowed TX pkts: %lX\n", + __func__, (unsigned long)packet); + if (ep->ep_cb. + tx_full((struct htc_target *) + ep->target, + packet) == HTC_SEND_FULL_DROP) { + /* callback wants the packet dropped */ + ep->ep_st.tx_dropped += 1; + + /* leave this one in the caller's queue + * for cleanup */ + } else { + /* callback wants to keep this packet, + * remove from caller's queue */ + list_del(&packet->list); + /* put it in the send queue */ + list_add_tail(&packet->list, + &send_queue); + } + + } + + if (list_empty(&send_queue)) { + /* no packets made it in, caller will cleanup */ + return HTC_SEND_QUEUE_DROP; + } + } + } + + if (!ep->tx_credit_flow_enabled) { + tx_resources = + ath6kl_hif_pipe_get_free_queue_number(ar, ep->pipeid_ul); + } else { + tx_resources = 0; + } + + spin_lock_bh(&target->tx_lock); + if (!list_empty(&send_queue)) { + /* transfer packets to tail */ + list_splice_tail_init(&send_queue, &ep->txq); + if (!list_empty(&send_queue)) { + WARN_ON(1); + spin_unlock_bh(&target->tx_lock); + return HTC_SEND_QUEUE_DROP; + } + INIT_LIST_HEAD(&send_queue); + } + + /* increment tx processing count on entry */ + ep->tx_proc_cnt++; + + if (ep->tx_proc_cnt > 1) { + /* + * another thread or task is draining the TX queues on this + * endpoint that thread will reset the tx processing count when + * the queue is drained + */ + ep->tx_proc_cnt--; + spin_unlock_bh(&target->tx_lock); + return HTC_SEND_QUEUE_OK; + } + + /***** beyond this point only 1 thread may enter ******/ + + /* + * now drain the endpoint TX queue for transmission as long as we have + * enough transmit resources + */ + starving = ep->starving; + while (true) { + + if (get_queue_depth(&ep->txq) == 0) + break; + + if (ep->tx_credit_flow_enabled) { + /* credit based mechanism provides flow control based on + * target transmit resource availability, we assume that + * the HIF layer will always have bus resources greater + * than target transmit resources + */ + get_htc_packet_credit_based(target, ep, &send_queue); + } else { + /* get all packets for this endpoint that we can for + * this pass + */ + get_htc_packet(target, ep, &send_queue, tx_resources); + } + + if (get_queue_depth(&send_queue) == 0) { + /* didn't get packets due to out of resources or + * TX queue was drained + */ + break; + } + + spin_unlock_bh(&target->tx_lock); + + /* send what we can */ + htc_issue_packets(target, ep, &send_queue); + + if (!ep->tx_credit_flow_enabled) { + tx_resources = + ath6kl_hif_pipe_get_free_queue_number(ar, + ep->pipeid_ul); + } + + spin_lock_bh(&target->tx_lock); + /* if it is a starving EP, consumes only one credit */ + if (starving) + break; + } + /* done with this endpoint, we can clear the count */ + ep->tx_proc_cnt = 0; + spin_unlock_bh(&target->tx_lock); + + return HTC_SEND_QUEUE_OK; +} + +static void htc_tx_bundle_timer_handler(unsigned long ptr) +{ + struct htc_target *target = (struct htc_target *)ptr; + struct htc_endpoint *endpoint = &target->endpoint[ENDPOINT_2]; + static u32 count = 0; + static u32 tx_issued = 0; + + endpoint->call_by_timer = 1; + count++; + + if ((count%(1000/htc_bundle_send_timer)) == 0) { + /* printk("timer tx_issued=%d\n", + * endpoint->ep_st.tx_issued-tx_issued); + */ + if ((endpoint->ep_st.tx_issued - tx_issued) + > htc_bundle_send_th) + endpoint->pass_th = 1; + else + endpoint->pass_th = 0; + tx_issued = endpoint->ep_st.tx_issued; + } + htc_try_send(target, endpoint, NULL); + endpoint->call_by_timer = 0; + + mod_timer(&endpoint->timer, + jiffies + msecs_to_jiffies(htc_bundle_send_timer)); +} + +static void htc_tx_resource_available(struct htc_target *context, u8 pipeid) +{ + int i; + struct htc_target *target = (struct htc_target *) context; + struct htc_endpoint *ep = NULL; + + for (i = 0; i < ENDPOINT_MAX; i++) { + ep = &target->endpoint[i]; + if (ep->svc_id != 0) { + if (ep->pipeid_ul == pipeid) + break; + } + } + + if (i >= ENDPOINT_MAX) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "Invalid pipe indicated for TX resource avail : %d!\n", + pipeid); + return; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: hIF indicated more resources for pipe:%d\n", + __func__, pipeid); + + htc_try_send(target, ep, NULL); +} + +/* htc control packet manipulation */ +static void destroy_htc_txctrl_packet(struct htc_packet *packet) +{ + struct sk_buff *netbuf; + netbuf = (struct sk_buff *) packet->skb; + if (netbuf != NULL) + dev_kfree_skb(netbuf); + + kfree(packet); +} + +static struct htc_packet *build_htc_txctrl_packet(void) +{ + struct htc_packet *packet = NULL; + struct sk_buff *netbuf; + + packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL); + if (packet == NULL) + return NULL; + + memset(packet, 0, sizeof(struct htc_packet)); + netbuf = __dev_alloc_skb(HTC_CONTROL_BUFFER_SIZE, GFP_KERNEL); + + if (netbuf == NULL) { + kfree(packet); + return NULL; + } + packet->skb = netbuf; + + return packet; +} + +static void htc_free_txctrl_packet(struct htc_target *target, + struct htc_packet *packet) +{ + destroy_htc_txctrl_packet(packet); +} + +static struct htc_packet *htc_alloc_txctrl_packet(struct htc_target + *target) +{ + return build_htc_txctrl_packet(); +} + +static void htc_txctrl_complete(struct htc_target *context, + struct htc_packet *packet) +{ + + struct htc_target *target = + (struct htc_target *) context; + htc_free_txctrl_packet(target, packet); +} + +#define MAX_MESSAGE_SIZE 1536 + +static int htc_setup_target_buffer_assignments(struct htc_target *target) +{ + struct htc_pipe_txcredit_alloc *entry; + int status; + int credits; + int credit_per_maxmsg; + unsigned int hif_usbaudioclass = 0; + + if (target->tgt_cred_sz == 0) + return -ECOMM; + + credit_per_maxmsg = MAX_MESSAGE_SIZE / target->tgt_cred_sz; + if (MAX_MESSAGE_SIZE % target->tgt_cred_sz) + credit_per_maxmsg++; + + /* TODO, this should be configured by the caller! */ + + credits = target->tgt_creds; + entry = &target->txcredit_alloc[0]; + + status = -ENOMEM; + + if (hif_usbaudioclass) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc_setup_target_buffer_assignments " + "For USB Audio Class- Total:%d\n", credits); + entry++; + entry++; + /* Setup VO Service To have Max Credits */ + entry->service_id = WMI_DATA_VO_SVC; + entry->credit_alloc = (credits - 6); + if (entry->credit_alloc == 0) + entry->credit_alloc++; + + credits -= (int)entry->credit_alloc; + if (credits <= 0) + return status; + + entry++; + entry->service_id = WMI_CONTROL_SVC; + entry->credit_alloc = credit_per_maxmsg; + credits -= (int)entry->credit_alloc; + if (credits <= 0) + return status; + + /* leftovers go to best effort */ + entry++; + entry++; + entry->service_id = WMI_DATA_BE_SVC; + entry->credit_alloc = (u8) credits; + status = 0; + } else { + entry++; + entry->service_id = WMI_DATA_VI_SVC; + entry->credit_alloc = credits / 4; + if (entry->credit_alloc == 0) + entry->credit_alloc++; + + credits -= (int)entry->credit_alloc; + if (credits <= 0) + return status; + + entry++; + entry->service_id = WMI_DATA_VO_SVC; + entry->credit_alloc = credits / 4; + if (entry->credit_alloc == 0) + entry->credit_alloc++; + + credits -= (int)entry->credit_alloc; + if (credits <= 0) + return status; + + entry++; + entry->service_id = WMI_CONTROL_SVC; + entry->credit_alloc = credit_per_maxmsg; + credits -= (int)entry->credit_alloc; + if (credits <= 0) + return status; + + entry++; + entry->service_id = WMI_DATA_BK_SVC; + entry->credit_alloc = credit_per_maxmsg; + credits -= (int)entry->credit_alloc; + if (credits <= 0) + return status; + + /* leftovers go to best effort */ + entry++; + entry->service_id = WMI_DATA_BE_SVC; + entry->credit_alloc = (u8) credits; + status = 0; + } + + if (status == 0) { + if (AR_DBG_LVL_CHECK(ATH6KL_DBG_HTC)) { + int i; + for (i = 0; i < ENDPOINT_MAX; i++) { + if (target->txcredit_alloc[i].service_id != 0) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC Service Index : %d TX : 0x%2.2X :" + "alloc:%d\n", i, + target->txcredit_alloc[i]. + service_id, + target->txcredit_alloc[i]. + credit_alloc); + } + } + } + } + return status; +} + +/* process credit reports and call distribution function */ +static void htc_process_credit_report(struct htc_target *target, + struct htc_credit_report *rpt, + int num_entries, + enum htc_endpoint_id from_ep) +{ + int i; + struct htc_endpoint *ep; + int total_credits = 0; + + /* lock out TX while we update credits */ + spin_lock_bh(&target->tx_lock); + + for (i = 0; i < num_entries; i++, rpt++) { + if (rpt->eid >= ENDPOINT_MAX) { + WARN_ON(1); + spin_unlock_bh(&target->tx_lock); + return; + } + + ep = &target->endpoint[rpt->eid]; + + if (ep->eid >= ENDPOINT_2 && ep->eid <= ENDPOINT_5) { + enum htc_endpoint_id eid[DATA_EP_SIZE] = { + ENDPOINT_5, ENDPOINT_4, ENDPOINT_2, ENDPOINT_3}; + int epid_idx; + + target->avail_tx_credits += rpt->credits; + ep->ep_st.cred_retnd += rpt->credits; + ep->ep_st.tx_cred_rpt++; + + for (epid_idx = 0; epid_idx < DATA_EP_SIZE; + epid_idx++) { + ep = &target->endpoint[eid[epid_idx]]; + if (get_queue_depth(&ep->txq)) + break; + } + spin_unlock_bh(&target->tx_lock); + + for (epid_idx = 0; epid_idx < DATA_EP_SIZE; + epid_idx++) { + struct htc_endpoint *starving_ep; + starving_ep = &target->endpoint[eid[epid_idx]]; + if (ep == starving_ep) + continue; + + if (starving_ep->starving) + htc_try_send(target, starving_ep, NULL); + } + htc_try_send(target, ep, NULL); + spin_lock_bh(&target->tx_lock); + } else { + ep->cred_dist.credits += rpt->credits; + ep->ep_st.cred_retnd += rpt->credits; + ep->ep_st.tx_cred_rpt++; + + if (ep->cred_dist.credits && + get_queue_depth(&ep->txq)) { + spin_unlock_bh(&target->tx_lock); + htc_try_send(target, ep, NULL); + spin_lock_bh(&target->tx_lock); + } + } + + total_credits += rpt->credits; + } + ath6kl_dbg(ATH6KL_DBG_HTC, + " Report indicated %d credits to distribute\n", + total_credits); + + spin_unlock_bh(&target->tx_lock); +} + +/* flush endpoint TX queue */ +static void htc_flush_tx_endpoint(struct htc_target *target, + struct htc_endpoint *ep, u16 Tag) +{ + struct htc_packet *packet; + + spin_lock_bh(&target->tx_lock); + while (get_queue_depth(&ep->txq)) { + packet = list_first_entry(&ep->txq, + struct htc_packet, list); + list_del(&packet->list); + packet->status = 0; + send_packet_completion(target, packet); + } + spin_unlock_bh(&target->tx_lock); +} + +/* + * In the adapted HIF layer, struct sk_buff * are passed between HIF and HTC, + * since upper layers expects struct htc_packet containers we use the completed + * netbuf and lookup it's corresponding HTC packet buffer from a lookup list. + * This is extra overhead that can be fixed by re-aligning HIF interfaces with + * HTC. + */ +static struct htc_packet *htc_lookup_tx_packet(struct htc_target *target, + struct htc_endpoint *ep, + struct sk_buff *netbuf) +{ + struct htc_packet *packet, *tmp_pkt; + struct htc_packet *found_packet = NULL; + + spin_lock_bh(&target->tx_lock); + + /* + * interate from the front of tx lookup queue + * this lookup should be fast since lower layers completes in-order and + * so the completed packet should be at the head of the list generally + */ + list_for_each_entry_safe(packet, tmp_pkt, &ep->tx_lookup_queue, list) { + /* check for removal */ + if (netbuf == packet->skb) { + /* found it */ + list_del(&packet->list); + found_packet = packet; + break; + } + + } + + spin_unlock_bh(&target->tx_lock); + + return found_packet; +} + +static int htc_tx_completion(struct htc_target *context, struct sk_buff *netbuf) +{ + struct htc_target *target = (struct htc_target *)context; + u8 *netdata; + u32 netlen; + struct htc_frame_hdr *htc_hdr; + u8 EpID; + struct htc_endpoint *ep; + struct htc_packet *packet; + struct ath6kl *ar; + enum htc_endpoint_id eid[DATA_EP_SIZE] = { + ENDPOINT_5, ENDPOINT_4, ENDPOINT_2, ENDPOINT_3}; + int epid_idx; + u16 resources_thresh[DATA_EP_SIZE]; /* urb resources */ + u16 resources; + u16 resources_max; + + netdata = netbuf->data; + netlen = netbuf->len; + + htc_hdr = (struct htc_frame_hdr *)netdata; + + EpID = htc_hdr->eid; + ep = &target->endpoint[EpID]; + + packet = htc_lookup_tx_packet(target, ep, netbuf); + if (packet == NULL) { + /* may have already been flushed and freed */ + ath6kl_err("HTC TX lookup failed!\n"); + } else { + /* will be giving this buffer back to upper layers */ + packet->status = 0; + send_packet_completion(target, packet); + } + netbuf = NULL; + + for (epid_idx = 0; epid_idx < DATA_EP_SIZE; epid_idx++) { + struct htc_endpoint *starving_ep; + starving_ep = &target->endpoint[eid[epid_idx]]; + if (ep == starving_ep) + continue; + if (starving_ep->starving) + htc_try_send(target, starving_ep, NULL); + } + + ar = target->dev->ar; + + if (!ep->tx_credit_flow_enabled) { + if ((ar->hw.flags & ATH6KL_HW_SINGLE_PIPE_SCHED) && + (enum htc_endpoint_id)EpID >= ENDPOINT_2 && + (enum htc_endpoint_id)EpID <= ENDPOINT_5) { + + resources_max = ath6kl_hif_pipe_get_max_queue_number( + ar, ep->pipeid_ul); + + resources_thresh[0] = 0; /* VO */ + resources_thresh[1] = + (resources_max * 2) / 32; /* VI */ + resources_thresh[2] = + (resources_max * 3) / 32; /* BE */ + resources_thresh[3] = + (resources_max * 4) / 32; /* BK */ + + resources = ath6kl_hif_pipe_get_free_queue_number( + ar, ep->pipeid_ul); + spin_lock_bh(&target->tx_lock); + for (epid_idx = 0; epid_idx < DATA_EP_SIZE; + epid_idx++) { + ep = &target->endpoint[eid[epid_idx]]; + + if (!get_queue_depth(&ep->txq)) + continue; + + if (resources >= resources_thresh[epid_idx]) + break; + } + spin_unlock_bh(&target->tx_lock); + + if (epid_idx == DATA_EP_SIZE) + /* + #if 0 + if (resources) { + for (epid_idx = 0; + epid_idx < DATA_EP_SIZE; + epid_idx++) { + ep = + &target->endpoint[eid[epid_idx]]; + if (get_queue_depth(&ep->tx_queue)) + break; + } + } else + #endif + */ + return 0; + } + + /* + * note: when using TX credit flow, the re-checking of queues + * happens when credits flow back from the target. in the + * non-TX credit case, we recheck after the packet completes + */ + htc_try_send(target, ep, NULL); + } + + return 0; +} + +static int htc_send_pkts_sched_check(struct htc_target *target, + enum htc_endpoint_id id) +{ + struct htc_endpoint *endpoint; + enum htc_endpoint_id eid; + struct list_head *tx_queue; + u16 ac_queue_status[DATA_EP_SIZE] = {0, 0, 0, 0}; + u16 status; + + if (id < ENDPOINT_2 || id > ENDPOINT_5) + return 1; + + spin_lock_bh(&target->tx_lock); + for (eid = ENDPOINT_2; eid <= ENDPOINT_5; eid++) { + endpoint = &target->endpoint[eid]; + tx_queue = &endpoint->txq; + + if (list_empty(tx_queue)) + ac_queue_status[eid - 2] = 1; + + if (htc_bundle_send && htc_bundle_send_timer && + eid == ENDPOINT_2) { + /* init bundle send timer */ + if (endpoint->timer_init == 0) { + setup_timer(&endpoint->timer, + htc_tx_bundle_timer_handler, + (unsigned long) target); + mod_timer(&endpoint->timer, jiffies + + msecs_to_jiffies(htc_bundle_send_timer)); + endpoint->timer_init = 1; + } + /* check if we need to queue packet for bundle send */ + if (endpoint->pass_th && + get_queue_depth(&endpoint->txq) < + HTC_HOST_MAX_MSG_PER_BUNDLE) { + spin_unlock_bh(&target->tx_lock); + return 0; + } + } + } + spin_unlock_bh(&target->tx_lock); + + switch (id) { + case ENDPOINT_2: /* BE */ + status = ac_queue_status[0] && ac_queue_status[2] && + ac_queue_status[3]; + break; + case ENDPOINT_3: /* BK */ + status = ac_queue_status[0] && ac_queue_status[1] && + ac_queue_status[2] && ac_queue_status[3]; + break; + case ENDPOINT_4: /* VI */ + status = ac_queue_status[2] && ac_queue_status[3]; + break; + case ENDPOINT_5: /* VO */ + status = ac_queue_status[3]; + break; + default: + status = 0; + break; + } + return status; +} + +static int htc_send_pkts_sched_queue(struct htc_target *target, + struct list_head *pkt_queue, + enum htc_endpoint_id eid) +{ + struct htc_endpoint *endpoint; + struct list_head *tx_queue; + struct htc_packet *packet, *tmp_pkt; + int good_pkts; + struct ath6kl *ar = target->dev->ar; + + endpoint = &target->endpoint[eid]; + tx_queue = &endpoint->txq; + + spin_lock_bh(&target->tx_lock); + + good_pkts = endpoint->max_txq_depth - get_queue_depth(tx_queue); + + if (good_pkts > 0) { + while (!list_empty(pkt_queue)) { + packet = list_first_entry(pkt_queue, + struct htc_packet, list); + list_del(&packet->list); + list_add_tail(&packet->list, tx_queue); + + good_pkts--; + if (good_pkts <= 0) + break; + } + } + + if (get_queue_depth(pkt_queue)) { + list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { + if (endpoint->ep_cb.tx_full((struct htc_target *) + endpoint->target, + packet) == HTC_SEND_FULL_DROP) { + endpoint->ep_st.tx_dropped += 1; + } else { + list_del(&packet->list); + list_add_tail(&packet->list, tx_queue); + } + } + } else if (test_bit(MCC_ENABLED, &ar->flag)) { + if (get_queue_depth(tx_queue) > ATH6KL_P2P_FLOWCTRL_TXQ_LIMIT) + ath6kl_p2p_flowctrl_netif_transition( + ar, ATH6KL_P2P_FLOWCTRL_NETIF_STOP); + } + + spin_unlock_bh(&target->tx_lock); + + return 0; +} + + +static int htc_send_packets_multiple(struct htc_target *handle, + struct list_head *pkt_queue) +{ + struct htc_target *target = (struct htc_target *) handle; + struct htc_endpoint *ep; + struct htc_packet *packet, *tmp_pkt; + struct ath6kl *ar = target->dev->ar; + + if (list_empty(pkt_queue)) + return -EINVAL; + + /* get first packet to find out which ep the packets will go into */ + packet = list_first_entry(pkt_queue, struct htc_packet, list); + if (packet == NULL) + return -EINVAL; + + if (packet->endpoint >= ENDPOINT_MAX) { + WARN_ON(1); + return -EINVAL; + } + ep = &target->endpoint[packet->endpoint]; + + if (ar->hw.flags & ATH6KL_HW_SINGLE_PIPE_SCHED) { + if (!htc_send_pkts_sched_check(target, ep->eid)) { + htc_send_pkts_sched_queue(target, pkt_queue, ep->eid); + + /* checking for starving */ + if (ar->starving_prevention && + time_after(jiffies, ep->last_sent + (HZ >> 3))) + ep->starving = 1; + } else + htc_try_send(target, ep, pkt_queue); + } else { + htc_try_send(target, ep, pkt_queue); + } + + /* do completion on any packets that couldn't get in */ + if (!list_empty(pkt_queue)) { + + list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { + packet->status = -ENOMEM; + } + + do_send_completion(ep, pkt_queue); + } + + return 0; +} + +/* htc pipe rx path */ +static struct htc_packet *alloc_htc_packet_container(struct htc_target + *target) +{ + struct htc_packet *packet; + spin_lock_bh(&target->rx_lock); + + if (target->htc_packet_pool == NULL) { + spin_unlock_bh(&target->rx_lock); + return NULL; + } + + packet = target->htc_packet_pool; + target->htc_packet_pool = (struct htc_packet *)packet->list.next; + + spin_unlock_bh(&target->rx_lock); + + packet->list.next = NULL; + return packet; +} + +static void free_htc_packet_container(struct htc_target *target, + struct htc_packet *packet) +{ + spin_lock_bh(&target->rx_lock); + + if (target->htc_packet_pool == NULL) { + target->htc_packet_pool = packet; + packet->list.next = NULL; + } else { + packet->list.next = (struct list_head *)target->htc_packet_pool; + target->htc_packet_pool = packet; + } + + spin_unlock_bh(&target->rx_lock); +} + +static int htc_process_trailer(struct htc_target *target, + u8 *buffer, + int len, enum htc_endpoint_id from_ep) +{ + struct htc_record_hdr *record; + u8 *record_buf; + u8 *orig_buf; + int orig_len; + int status; + + orig_buf = buffer; + orig_len = len; + status = 0; + + while (len > 0) { + + if (len < sizeof(struct htc_record_hdr)) { + status = -EINVAL; + break; + } + /* these are byte aligned structs */ + record = (struct htc_record_hdr *)buffer; + len -= sizeof(struct htc_record_hdr); + buffer += sizeof(struct htc_record_hdr); + + if (record->len > len) { + /* no room left in buffer for record */ + ath6kl_dbg(ATH6KL_DBG_HTC, + " invalid length: %d (id:%d) buffer has: %d bytes left\n", + record->len, record->rec_id, len); + status = -EINVAL; + break; + } + /* start of record follows the header */ + record_buf = buffer; + + switch (record->rec_id) { + case HTC_RECORD_CREDITS: + if (record->len < + sizeof(struct htc_credit_report)) { + WARN_ON(1); + return -EINVAL; + } + + htc_process_credit_report(target, + (struct htc_credit_report *) + record_buf, + record->len / + (sizeof + (struct htc_credit_report)), + from_ep); + break; + default: + ath6kl_dbg(ATH6KL_DBG_HTC, + " unhandled record: id:%d length:%d\n", + record->rec_id, record->len); + break; + } + + if (status != 0) + break; + + /* advance buffer past this record for next time around */ + buffer += record->len; + len -= record->len; + } + + return status; +} + +static void do_recv_completion(struct htc_endpoint *ep, + struct list_head *queue_to_indicate) +{ + struct htc_packet *packet; + if (list_empty(queue_to_indicate)) { + /* nothing to indicate */ + return; + } + + /* using legacy EpRecv */ + while (!list_empty(queue_to_indicate)) { + packet = list_first_entry(queue_to_indicate, + struct htc_packet, list); + list_del(&packet->list); + ep->ep_cb.rx((struct htc_target *) + ep->target, packet); + } + + return; +} + +static void recv_packet_completion(struct htc_target *target, + struct htc_endpoint *ep, + struct htc_packet *packet) +{ + struct list_head container; + INIT_LIST_HEAD(&container); + list_add_tail(&packet->list, &container); + /* do completion */ + do_recv_completion(ep, &container); +} + +struct sk_buff *rx_sg_to_single_netbuf(struct htc_target *target) +{ + struct sk_buff *skb; + u8 *anbdata; + u8 *anbdata_new; + u32 anblen; + struct sk_buff *new_skb = NULL; + struct sk_buff_head *rx_sg_queue = &target->rx_sg_q; + + if (skb_queue_empty(rx_sg_queue)) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: invalid sg queue len\n", __func__); + goto _failed; + } + + new_skb = __dev_alloc_skb(target->rx_sg_total_len_exp, GFP_ATOMIC); + if (new_skb == NULL) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: can't allocate %u size netbuf\n", + __func__, target->rx_sg_total_len_exp); + goto _failed; + } + + anbdata_new = new_skb->data; + anblen = new_skb->len; + + while ((skb = skb_dequeue(rx_sg_queue))) { + anbdata = skb->data; + memcpy(anbdata_new, anbdata, skb->len); + skb_put(new_skb, skb->len); + anbdata_new += skb->len; + dev_kfree_skb(skb); + }; + + target->rx_sg_total_len_exp = 0; + target->rx_sg_total_len_cur = 0; + target->rx_sg_in_progress = false; + + return new_skb; + +_failed: + + while ((skb = skb_dequeue(rx_sg_queue))) + dev_kfree_skb(skb); + target->rx_sg_total_len_exp = 0; + target->rx_sg_total_len_cur = 0; + target->rx_sg_in_progress = false; + return NULL; +} + +static int htc_rx_completion(struct htc_target *context, + struct sk_buff *netbuf, u8 pipeid) +{ + int status = 0; + struct htc_frame_hdr *htc_hdr; + struct htc_target *target = (struct htc_target *)context; + u8 *netdata; + u8 hdr_info; + u32 netlen; + struct htc_endpoint *ep; + struct htc_packet *packet; + u16 payload_len; + u32 trailerlen = 0; + + int i; + + static u32 assert_pattern = cpu_to_be32(0x0000c600); + + spin_lock_bh(&target->rx_lock); + if (target->rx_sg_in_progress) { + target->rx_sg_total_len_cur += netbuf->len; + skb_queue_tail(&target->rx_sg_q, netbuf); + if (target->rx_sg_total_len_cur == + target->rx_sg_total_len_exp) { + netbuf = rx_sg_to_single_netbuf(target); + if (netbuf == NULL) { + spin_unlock_bh(&target->rx_lock); + goto free_netbuf; + } + } else { + netbuf = NULL; + spin_unlock_bh(&target->rx_lock); + goto free_netbuf; + } + } + + spin_unlock_bh(&target->rx_lock); + + netdata = netbuf->data; + netlen = netbuf->len; + +#define CONFIG_CRASH_DUMP 1 +#if CONFIG_CRASH_DUMP + if (!memcmp(netdata, &assert_pattern, sizeof(assert_pattern))) { + +#define REG_DUMP_COUNT_AR6004 76 + netdata += 4; + + ath6kl_info("Firmware crash detected...\n"); + for (i = 0; i < REG_DUMP_COUNT_AR6004 * 4; i += 16) { + ath6kl_info("%d: 0x%08x 0x%08x 0x%08x 0x%08x\n", i/4, + be32_to_cpu(*(u32 *)(netdata+i)), + be32_to_cpu(*(u32 *)(netdata+i + 4)), + be32_to_cpu(*(u32 *)(netdata+i + 8)), + be32_to_cpu(*(u32 *)(netdata+i + 12))); + } + dev_kfree_skb(netbuf); + netbuf = NULL; + + ath6kl_fw_crash_trap(target->dev->ar); + + goto free_netbuf; + } +#endif + + htc_hdr = (struct htc_frame_hdr *)netdata; + + ep = &target->endpoint[htc_hdr->eid]; + + if (htc_hdr->eid >= ENDPOINT_MAX) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC Rx: invalid EndpointID=%d\n", + htc_hdr->eid); + status = -EINVAL; + goto free_netbuf; + } + + payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len)); + + if (netlen < (payload_len + HTC_HDR_LENGTH)) { + spin_lock_bh(&target->rx_lock); + target->rx_sg_in_progress = true; + skb_queue_tail(&target->rx_sg_q, netbuf); + target->rx_sg_total_len_exp = (payload_len + HTC_HDR_LENGTH); + target->rx_sg_total_len_cur += netlen; + spin_unlock_bh(&target->rx_lock); + netbuf = NULL; + goto free_netbuf; + } + + /* get flags to check for trailer */ + hdr_info = htc_hdr->flags; + if (hdr_info & HTC_FLG_RX_TRAILER) { + /* extract the trailer length */ + hdr_info = htc_hdr->ctrl[0]; + if ((hdr_info < sizeof(struct htc_record_hdr)) + || (hdr_info > payload_len)) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "invalid header: payloadlen" + "should be %d, CB[0]: %d\n", + payload_len, hdr_info); + status = -EINVAL; + goto free_netbuf; + } + + trailerlen = hdr_info; + /* process trailer after hdr/apps payload */ + status = htc_process_trailer(target, + ((u8 *) htc_hdr + + HTC_HDR_LENGTH + + payload_len - + hdr_info), hdr_info, + htc_hdr->eid); + if (status != 0) + goto free_netbuf; + } + + if (((int)payload_len - (int)trailerlen) <= 0) { + /* zero length packet with trailer, just drop these */ + goto free_netbuf; + } + + if (htc_hdr->eid == ENDPOINT_0) { + /* handle HTC control message */ + if (target->htc_flags & HTC_OP_STATE_SETUP_COMPLETE) { + /* + * fatal: target should not send unsolicited + * messageson the endpoint 0 + */ + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC ignores Rx Ctrl after setup complete\n"); + status = -EINVAL; + goto free_netbuf; + } + + /* remove HTC header */ + skb_pull(netbuf, HTC_HDR_LENGTH); + + netdata = netbuf->data; + netlen = netbuf->len; + + spin_lock_bh(&target->rx_lock); + + target->ctrl_response_valid = true; + target->ctrl_response_len = + min_t(int, netlen, HTC_MAX_CTRL_MSG_LEN); + memcpy(target->ctrl_response_buf, netdata, + target->ctrl_response_len); + + spin_unlock_bh(&target->rx_lock); + + dev_kfree_skb(netbuf); + netbuf = NULL; + goto free_netbuf; + } + + /* + * TODO: the message based HIF architecture allocates net bufs + * for recv packets since it bridges that HIF to upper layers, + * which expects HTC packets, we form the packets here + */ + packet = alloc_htc_packet_container(target); + if (packet == NULL) { + status = -ENOMEM; + goto free_netbuf; + } + packet->status = 0; + packet->endpoint = htc_hdr->eid; + packet->pkt_cntxt = netbuf; + /* TODO: for backwards compatibility */ + packet->buf = skb_push(netbuf, 0) + HTC_HDR_LENGTH; + packet->act_len = netlen - HTC_HDR_LENGTH - trailerlen; + ep->ep_st.rx_pkts++; + + /* + * TODO: this is a hack because the driver layer will set the + * actual len of the skb again which will just double the len + */ + skb_trim(netbuf, 0); + + recv_packet_completion(target, ep, packet); + /* recover the packet container */ + free_htc_packet_container(target, packet); + netbuf = NULL; + +free_netbuf: + if (netbuf != NULL) + dev_kfree_skb(netbuf); + + return status; + +} + +static void htc_flush_rx_queue(struct htc_target *target, + struct htc_endpoint *ep) +{ + struct htc_packet *packet; + struct list_head container; + + spin_lock_bh(&target->rx_lock); + + while (1) { + if (list_empty(&ep->rx_bufq)) + break; + packet = list_first_entry(&ep->rx_bufq, + struct htc_packet, list); + list_del(&packet->list); + + spin_unlock_bh(&target->rx_lock); + packet->status = -ECANCELED; + packet->act_len = 0; + ath6kl_dbg(ATH6KL_DBG_HTC, + " Flushing RX packet:0x%lX, length:%d, ep:%d\n", + (unsigned long)packet, packet->buf_len, + packet->endpoint); + INIT_LIST_HEAD(&container); + list_add_tail(&packet->list, &container); + /* give the packet back */ + do_recv_completion(ep, &container); + spin_lock_bh(&target->rx_lock); + } + + spin_unlock_bh(&target->rx_lock); +} + +/* polling routine to wait for a control packet to be received */ +static int htc_wait_recv_ctrl_message(struct htc_target *target) +{ + int count = HTC_TARGET_RESPONSE_POLL_COUNT; + + while (count > 0) { + spin_lock_bh(&target->rx_lock); + + if (target->ctrl_response_valid) { + target->ctrl_response_valid = false; + spin_unlock_bh(&target->rx_lock); + break; + } + + spin_unlock_bh(&target->rx_lock); + + count--; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(HTC_TARGET_RESPONSE_POLL_MS)); + set_current_state(TASK_RUNNING); + } + if (count <= 0) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: Timeout!\n", __func__); + return -ECOMM; + } + + return 0; +} + +static void htc_rxctrl_complete(struct htc_target *context, + struct htc_packet *packet) +{ + struct sk_buff *skb = packet->skb; + + if (packet->endpoint == ENDPOINT_0) { + if (packet->status == -ECANCELED) { + if (skb) + dev_kfree_skb(skb); + return; + } + } + + ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function, ep = %d\n", + __func__, packet->endpoint); + + WARN_ON(1); + + return; +} + +/* htc pipe initialization */ +static void reset_endpoint_states(struct htc_target *target) +{ + struct htc_endpoint *ep; + int i; + + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { + ep = &target->endpoint[i]; + ep->svc_id = 0; + ep->len_max = 0; + ep->max_txq_depth = 0; + ep->eid = i; + INIT_LIST_HEAD(&ep->txq); + INIT_LIST_HEAD(&ep->tx_lookup_queue); + INIT_LIST_HEAD(&ep->rx_bufq); + ep->target = target; + ep->tx_credit_flow_enabled = (bool) 1; +#ifdef USB_AUTO_SUSPEND + if (i == ENDPOINT_1) + ep->tx_credit_flow_enabled = (bool) 0; +#endif + } +} + +/* start HTC, this is called after all services are connected */ +static int htc_config_target_hif_pipe(struct htc_target *target) +{ + return 0; +} + +/* htc service functions */ +static u8 htc_get_credit_alloc(struct htc_target *target, u16 service_id) +{ + u8 allocation = 0; + int i; + + for (i = 0; i < ENDPOINT_MAX; i++) { + if (target->txcredit_alloc[i].service_id == service_id) + allocation = target->txcredit_alloc[i].credit_alloc; + } + + if (allocation == 0) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC Service TX : 0x%2.2X : allocation is zero!\n", + service_id); + } + + return allocation; +} + +static int ath6kl_htc_pipe_conn_service(struct htc_target *handle, + struct htc_service_connect_req *conn_req, + struct htc_service_connect_resp *conn_resp) +{ + struct ath6kl *ar = handle->dev->ar; + struct htc_target *target = (struct htc_target *) handle; + int status = 0; + struct htc_packet *packet = NULL; + struct htc_conn_service_resp *resp_msg; + struct htc_conn_service_msg *conn_msg; + enum htc_endpoint_id assigned_epid = ENDPOINT_MAX; + struct htc_endpoint *ep; + unsigned int max_msg_size = 0; + struct sk_buff *netbuf; + u8 tx_alloc; + int length; + bool disable_credit_flowctrl = false; + + if (conn_req->svc_id == 0) { + WARN_ON(1); + status = -EINVAL; + goto free_packet; + } + + if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) { + /* special case for pseudo control service */ + assigned_epid = ENDPOINT_0; + max_msg_size = HTC_MAX_CTRL_MSG_LEN; + tx_alloc = 0; + + } else { + + tx_alloc = + htc_get_credit_alloc(target, conn_req->svc_id); + if (tx_alloc == 0) { + status = -ENOMEM; + goto free_packet; + } + + /* allocate a packet to send to the target */ + packet = htc_alloc_txctrl_packet(target); + + if (packet == NULL) { + WARN_ON(1); + status = -ENOMEM; + goto free_packet; + } + + netbuf = packet->skb; + length = sizeof(struct htc_conn_service_msg); + + /* assemble connect service message */ + conn_msg = + (struct htc_conn_service_msg *)skb_put(netbuf, + length); + if (conn_msg == NULL) { + WARN_ON(1); + status = -EINVAL; + goto free_packet; + } + + memset(conn_msg, 0, + sizeof(struct htc_conn_service_msg)); + conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID); + conn_msg->svc_id = cpu_to_le16(conn_req->svc_id); + conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags & + ~HTC_CONN_FLGS_SET_RECV_ALLOC_MASK); + + /* tell target desired recv alloc for this ep */ + conn_msg->conn_flags |= cpu_to_le16( + tx_alloc << HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT); + + if (conn_req->conn_flags & + HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL) { + disable_credit_flowctrl = true; + } + + set_htc_pkt_info(packet, NULL, (u8 *) conn_msg, + length, + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + + status = ath6kl_htc_pipe_tx(handle, packet); + /* we don't own it anymore */ + packet = NULL; + if (status != 0) + goto free_packet; + + /* wait for response */ + status = htc_wait_recv_ctrl_message(target); + if (status != 0) + goto free_packet; + + /* we controlled the buffer creation so it has to be + * properly aligned + */ + resp_msg = (struct htc_conn_service_resp *) + target->ctrl_response_buf; + + if ((resp_msg->msg_id != cpu_to_le16(HTC_MSG_CONN_SVC_RESP_ID)) + || (target->ctrl_response_len < + sizeof(struct htc_conn_service_resp))) { + /* this message is not valid */ + WARN_ON(1); + status = -EINVAL; + goto free_packet; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, + "%s: service 0x%X conn resp: " + "status: %d ep: %d\n", __func__, + le16_to_cpu(resp_msg->svc_id), resp_msg->status, + resp_msg->eid); + + conn_resp->resp_code = resp_msg->status; + /* check response status */ + if (resp_msg->status != HTC_SERVICE_SUCCESS) { + ath6kl_dbg(ATH6KL_DBG_HTC, + " Target failed service 0x%X connect" + " request (status:%d)\n", + le16_to_cpu(resp_msg->svc_id), + resp_msg->status); + status = -EINVAL; + goto free_packet; + } + + assigned_epid = (enum htc_endpoint_id)resp_msg->eid; + max_msg_size = le16_to_cpu(resp_msg->max_msg_sz); + } + + /* the rest are parameter checks so set the error status */ + status = -EINVAL; + + if (assigned_epid >= ENDPOINT_MAX + || assigned_epid <= ENDPOINT_UNUSED) { + WARN_ON(1); + goto free_packet; + } + + if (max_msg_size == 0) { + WARN_ON(1); + goto free_packet; + } + + ep = &target->endpoint[assigned_epid]; + ep->eid = assigned_epid; + if (ep->svc_id != 0) { + /* endpoint already in use! */ + WARN_ON(1); + goto free_packet; + } + + if (target->tgt_cred_sz == 0) + goto free_packet; + + /* return assigned endpoint to caller */ + conn_resp->endpoint = assigned_epid; + conn_resp->len_max = max_msg_size; + + /* setup the endpoint */ + ep->svc_id = conn_req->svc_id; /* this marks ep in use */ + ep->max_txq_depth = conn_req->max_txq_depth; + ep->len_max = max_msg_size; + ep->cred_dist.credits = tx_alloc; + ep->cred_dist.cred_sz = target->tgt_cred_sz; + ep->cred_dist.cred_per_msg = + max_msg_size / target->tgt_cred_sz; + if (max_msg_size % target->tgt_cred_sz) + ep->cred_dist.cred_per_msg++; + + /* copy all the callbacks */ + ep->ep_cb = conn_req->ep_cb; + + /* initialize tx_drop_packet_threshold */ + ep->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM; + + status = ath6kl_hif_pipe_map_service(ar, ep->svc_id, + &ep->pipeid_ul, &ep->pipeid_dl); + if (status != 0) + goto free_packet; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "SVC Ready: 0x%4.4X: ULpipe:%d DLpipe:%d id:%d\n", + ep->svc_id, ep->pipeid_ul, + ep->pipeid_dl, ep->eid); + + if (disable_credit_flowctrl && ep->tx_credit_flow_enabled) { + ep->tx_credit_flow_enabled = false; + ath6kl_dbg(ATH6KL_DBG_HTC, + "SVC: 0x%4.4X ep:%d TX flow control off\n", + ep->svc_id, assigned_epid); + } + +free_packet: + if (packet != NULL) + htc_free_txctrl_packet(target, packet); + return status; +} + +/* htc export functions */ +void *ath6kl_htc_pipe_create(struct ath6kl *ar) +{ + int status = 0; + struct ath6kl_hif_pipe_callbacks htc_callbacks; + struct htc_endpoint *ep = NULL; + struct htc_target *target = NULL; + struct htc_packet *packet; + int i; + + target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); + if (target == NULL) { + ath6kl_err("htc create unable to allocate memory\n"); + status = -ENOMEM; + goto fail_htc_create; + } + + memset(target, 0, sizeof(struct htc_target)); + + spin_lock_init(&target->htc_lock); + spin_lock_init(&target->rx_lock); + spin_lock_init(&target->tx_lock); + + skb_queue_head_init(&target->rx_sg_q); + + reset_endpoint_states(target); + + for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) { + packet = (struct htc_packet *) + kzalloc(sizeof(struct htc_packet), GFP_KERNEL); + if (packet != NULL) { + memset(packet, 0, sizeof(struct htc_packet)); + free_htc_packet_container(target, packet); + } + } + /* setup HIF layer callbacks */ + memset(&htc_callbacks, 0, sizeof(struct ath6kl_hif_pipe_callbacks)); + htc_callbacks.rx_completion = htc_rx_completion; + htc_callbacks.tx_completion = htc_tx_completion; + htc_callbacks.tx_resource_available = htc_tx_resource_available; + + target->dev = + kzalloc(sizeof(*target->dev), GFP_KERNEL); + if (!target->dev) { + ath6kl_err("unable to allocate memory\n"); + status = -ENOMEM; + goto fail_htc_create; + } + target->dev->ar = ar; + target->dev->htc_cnxt = (struct htc_target *)target; + + /* Get HIF default pipe for HTC message exchange */ + ep = &target->endpoint[ENDPOINT_0]; + + ath6kl_hif_pipe_register_callback(ar, target, &htc_callbacks); + ath6kl_hif_pipe_get_default(ar, &ep->pipeid_ul, &ep->pipeid_dl); + + return (void *)target; + +fail_htc_create: + if (status != 0) { + if (target != NULL) + ath6kl_htc_pipe_cleanup((struct htc_target *)target); + target = NULL; + } + return (void *)target; +} + +/* cleanup the HTC instance */ +static void ath6kl_htc_pipe_cleanup(struct htc_target *handle) +{ + struct htc_packet *packet; + struct htc_target *target = (struct htc_target *) handle; + + while (true) { + packet = alloc_htc_packet_container(target); + if (packet == NULL) + break; + kfree(packet); + } + kfree(target->dev); + /* kfree our instance */ + kfree(target); +} + +static int ath6kl_htc_pipe_start(struct htc_target *handle) +{ + struct sk_buff *netbuf; + struct htc_target *target = (struct htc_target *) handle; + struct htc_setup_comp_ext_msg *setup_comp; + struct htc_packet *packet; + + htc_config_target_hif_pipe(target); + /* allocate a buffer to send */ + packet = htc_alloc_txctrl_packet(target); + if (packet == NULL) { + WARN_ON(1); + return -ENOMEM; + } + + netbuf = packet->skb; + /* assemble setup complete message */ + setup_comp = + (struct htc_setup_comp_ext_msg *)skb_put(netbuf, + sizeof(struct htc_setup_comp_ext_msg)); + memset(setup_comp, 0, sizeof(struct htc_setup_comp_ext_msg)); + setup_comp->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID); + + if (0) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC will not use TX credit flow control\n"); + setup_comp->flags |= + HTC_SETUP_COMP_FLG_DISABLE_TX_CREDIT_FLOW; + } else { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC using TX credit flow control\n"); + } + + if (htc_bundle_recv) { + ath6kl_dbg(ATH6KL_DBG_HTC, "HTC will use bundle recv\n"); + setup_comp->flags |= HTC_SETUP_COMP_FLG_RX_BNDL_EN; + } else { + ath6kl_dbg(ATH6KL_DBG_HTC, "HTC will not use bundle recv\n"); + } + + setup_comp->flags = cpu_to_le32(setup_comp->flags); + + set_htc_pkt_info(packet, NULL, (u8 *) setup_comp, + sizeof(struct htc_setup_comp_ext_msg), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + + target->htc_flags |= HTC_OP_STATE_SETUP_COMPLETE; + return ath6kl_htc_pipe_tx(handle, packet); +} + +static void ath6kl_htc_pipe_stop(struct htc_target *handle) +{ + struct htc_target *target = (struct htc_target *) handle; + int i; + struct htc_endpoint *ep; + struct sk_buff *skb; + struct sk_buff_head *rx_sg_queue = &target->rx_sg_q; + + /* cleanup endpoints */ + for (i = 0; i < ENDPOINT_MAX; i++) { + ep = &target->endpoint[i]; + htc_flush_rx_queue(target, ep); + htc_flush_tx_endpoint(target, ep, HTC_TX_PACKET_TAG_ALL); + if (i == ENDPOINT_2 && ep->timer_init) { + del_timer(&ep->timer); + ep->call_by_timer = 0; + ep->timer_init = 0; + } + } + + spin_lock_bh(&target->rx_lock); + + while ((skb = skb_dequeue(rx_sg_queue))) + dev_kfree_skb(skb); + target->rx_sg_total_len_exp = 0; + target->rx_sg_total_len_cur = 0; + target->rx_sg_in_progress = false; + + spin_unlock_bh(&target->rx_lock); + + reset_endpoint_states(target); + target->htc_flags &= ~HTC_OP_STATE_SETUP_COMPLETE; +} + +static int ath6kl_htc_pipe_get_rxbuf_num(struct htc_target *htc_context, + enum htc_endpoint_id endpoint) +{ + struct htc_target *target = + (struct htc_target *) htc_context; + int num; + + spin_lock_bh(&target->rx_lock); + num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq)); + spin_unlock_bh(&target->rx_lock); + + return num; +} + +static int ath6kl_htc_pipe_tx(struct htc_target *handle, + struct htc_packet *packet) +{ + struct list_head queue; + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: endPointId: %d, buffer: 0x%lX, length: %d\n", + __func__, packet->endpoint, + (unsigned long)packet->buf, + packet->act_len); + INIT_LIST_HEAD(&queue); + list_add_tail(&packet->list, &queue); + return htc_send_packets_multiple(handle, &queue); +} + +static int ath6kl_htc_pipe_wait_target(struct htc_target *handle) +{ + int status = 0; + struct htc_target *target = (struct htc_target *) handle; + struct htc_ready_ext_msg *ready_msg; + struct htc_service_connect_req connect; + struct htc_service_connect_resp resp; + + status = htc_wait_recv_ctrl_message(target); + + if (status != 0) + return status; + + if (target->ctrl_response_len < + (sizeof(struct htc_ready_ext_msg))) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "invalid htc ready msg len:%d!\n", + target->ctrl_response_len); + return -ECOMM; + } + ready_msg = + (struct htc_ready_ext_msg *)target->ctrl_response_buf; + + if (ready_msg->ver2_0_info.msg_id != cpu_to_le16(HTC_MSG_READY_ID)) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "invalid htc ready msg : 0x%X !\n", + le16_to_cpu(ready_msg->ver2_0_info.msg_id)); + return -ECOMM; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, + "Target Ready! : transmit resources : %d size:%d\n", + le16_to_cpu(ready_msg->ver2_0_info.cred_cnt), + le16_to_cpu(ready_msg->ver2_0_info.cred_sz)); + + target->tgt_creds = + le16_to_cpu(ready_msg->ver2_0_info.cred_cnt); + /*reserve one for control path*/ + target->avail_tx_credits = target->tgt_creds - 1; + target->tgt_cred_sz = + le16_to_cpu(ready_msg->ver2_0_info.cred_sz); + if ((target->tgt_creds == 0) + || (target->tgt_cred_sz == 0)) { + return -ECOMM; + } + + htc_setup_target_buffer_assignments(target); + + /* setup our pseudo HTC control endpoint connection */ + memset(&connect, 0, sizeof(connect)); + memset(&resp, 0, sizeof(resp)); + connect.ep_cb.tx_complete = htc_txctrl_complete; + connect.ep_cb.rx = htc_rxctrl_complete; + connect.max_txq_depth = NUM_CONTROL_TX_BUFFERS; + connect.svc_id = HTC_CTRL_RSVD_SVC; + + /* connect fake service */ + status = ath6kl_htc_pipe_conn_service((void *)target, &connect, &resp); + return status; +} + +static void ath6kl_htc_pipe_flush_txep(struct htc_target *handle, + enum htc_endpoint_id Endpoint, u16 Tag) +{ + struct htc_target *target = (struct htc_target *) handle; + struct htc_endpoint *ep = &target->endpoint[Endpoint]; + + if (ep->svc_id == 0) { + WARN_ON(1); + /* not in use.. */ + return; + } + + htc_flush_tx_endpoint(target, ep, Tag); +} + +static int ath6kl_htc_pipe_add_rxbuf_multiple(struct htc_target *handle, + struct list_head *pkt_queue) +{ + struct htc_target *target = (struct htc_target *) handle; + struct htc_endpoint *ep; + struct htc_packet *pFirstPacket; + int status = 0; + struct htc_packet *packet, *tmp_pkt; + + if (list_empty(pkt_queue)) + return -EINVAL; + + pFirstPacket = list_first_entry(pkt_queue, struct htc_packet, list); + if (pFirstPacket == NULL) { + WARN_ON(1); + return -EINVAL; + } + + if (pFirstPacket->endpoint >= ENDPOINT_MAX) { + WARN_ON(1); + return -EINVAL; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: epid: %d, cnt:%d, len: %d\n", + __func__, pFirstPacket->endpoint, + get_queue_depth(pkt_queue), pFirstPacket->buf_len); + + ep = &target->endpoint[pFirstPacket->endpoint]; + + spin_lock_bh(&target->rx_lock); + + /* store receive packets */ + list_splice_tail_init(pkt_queue, &ep->rx_bufq); + + spin_unlock_bh(&target->rx_lock); + + if (status != 0) { + /* walk through queue and mark each one canceled */ + list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { + packet->status = -ECANCELED; + } + + do_recv_completion(ep, pkt_queue); + } + + return status; +} + +void ath6kl_htc_pipe_indicate_activity_change( + struct htc_target *handle, + enum htc_endpoint_id Endpoint, + bool Active) +{ + /* TODO */ +} + +static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target) +{ + struct htc_endpoint *endpoint; + struct htc_packet *packet, *tmp_pkt; + int i; + + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { + endpoint = &target->endpoint[i]; + + spin_lock_bh(&target->rx_lock); + list_for_each_entry_safe(packet, tmp_pkt, + &endpoint->rx_bufq, list) { + list_del(&packet->list); + spin_unlock_bh(&target->rx_lock); + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx flush pkt 0x%p len %d ep %d\n", + packet, packet->buf_len, + packet->endpoint); + dev_kfree_skb(packet->pkt_cntxt); + spin_lock_bh(&target->rx_lock); + } + spin_unlock_bh(&target->rx_lock); + } +} + +void ath6kl_htc_set_credit_dist(struct htc_target *target, + struct ath6kl_htc_credit_info *cred_info, + u16 svc_pri_order[], int len) +{ + /* + * not supported since this transport does not use a credit + * based flow control mechanism + */ +} + +static int ath6kl_htc_pipe_credit_setup(struct htc_target *target, + struct ath6kl_htc_credit_info *cred_info) +{ + return 0; +} + +int ath6kl_htc_pipe_stat(struct htc_target *target, + u8 *buf, int buf_len) +{ + struct htc_endpoint *ep; + struct htc_endpoint_stats *ep_st; + struct list_head *tx_queue; + int i, tmp, len = 0; + + if ((!target) || (!buf)) + return 0; + + len += snprintf(buf + len, buf_len - len, + " " "\nCredit Size : %d Avail Credit : %d/%d\n", + target->tgt_cred_sz, + target->avail_tx_credits, + target->tgt_creds); + + for (i = ENDPOINT_1; i <= ENDPOINT_5; i++) { + len += snprintf(buf + len, buf_len - len, "EP-%d\n", i); + + ep = &target->endpoint[i]; + tx_queue = &ep->txq; + ep_st = &ep->ep_st; + + spin_lock_bh(&target->tx_lock); + tmp = get_queue_depth(tx_queue); + spin_unlock_bh(&target->tx_lock); + + len += snprintf(buf + len, buf_len - len, + " tx_queue : %d/%d\n", + tmp, ep->max_txq_depth); + len += snprintf(buf + len, buf_len - len, + " pipeid_ul/dl : %d/%d\n", + ep->pipeid_ul, ep->pipeid_dl); + len += snprintf(buf + len, buf_len - len, + " seq_no : %d\n", + ep->seqno); + len += snprintf(buf + len, buf_len - len, + " cred_low_indicate : %d\n", + ep_st->cred_low_indicate); + len += snprintf(buf + len, buf_len - len, + " cred_rpt_from_rx : %d\n", + ep_st->cred_rpt_from_rx); + len += snprintf(buf + len, buf_len - len, + " cred_rpt_from_other : %d\n", + ep_st->cred_rpt_from_other); + len += snprintf(buf + len, buf_len - len, + " cred_rpt_ep0 : %d\n", + ep_st->cred_rpt_ep0); + len += snprintf(buf + len, buf_len - len, + " cred_from_rx : %d\n", + ep_st->cred_from_rx); + len += snprintf(buf + len, buf_len - len, + " cred_from_other : %d\n", + ep_st->cred_from_other); + len += snprintf(buf + len, buf_len - len, + " cred_from_ep0 : %d\n", + ep_st->cred_from_ep0); + len += snprintf(buf + len, buf_len - len, + " cred_cosumd : %d\n", + ep_st->cred_cosumd); + len += snprintf(buf + len, buf_len - len, + " cred_retnd : %d\n", + ep_st->cred_retnd); + len += snprintf(buf + len, buf_len - len, + " tx_issued : %d\n", + ep_st->tx_issued); + len += snprintf(buf + len, buf_len - len, + " tx_dropped : %d\n", + ep_st->tx_dropped); + len += snprintf(buf + len, buf_len - len, + " tx_cred_rpt : %d\n", + ep_st->tx_cred_rpt); + len += snprintf(buf + len, buf_len - len, + " rx_pkts : %d\n", + ep_st->rx_pkts); + len += snprintf(buf + len, buf_len - len, + " rx_lkahds : %d\n", + ep_st->rx_lkahds); + len += snprintf(buf + len, buf_len - len, + " rx_alloc_thresh_hit : %d\n", + ep_st->rx_alloc_thresh_hit); + len += snprintf(buf + len, buf_len - len, + " rxalloc_thresh_byte : %d\n", + ep_st->rxalloc_thresh_byte); + + /* Bundle mode */ + if (htc_bundle_recv || htc_bundle_send) { + len += snprintf(buf + len, buf_len - len, + " tx_pkt_bundled : %d\n", + ep_st->tx_pkt_bundled); + len += snprintf(buf + len, buf_len - len, + " tx_bundles : %d\n", + ep_st->tx_bundles); + len += snprintf(buf + len, buf_len - len, + " rx_bundl : %d\n", + ep_st->rx_bundl); + len += snprintf(buf + len, buf_len - len, + " rx_bundle_lkahd : %d\n", + ep_st->rx_bundle_lkahd); + len += snprintf(buf + len, buf_len - len, + " rx_bundle_from_hdr : %d\n", + ep_st->rx_bundle_from_hdr); + } + } + + return len; +} + +int ath6kl_htc_pipe_stop_netif_queue_full(struct htc_target *target) +{ + return 1; +} + +int ath6kl_htc_pipe_wmm_schedule_change(struct htc_target *target, + bool change) +{ + struct ath6kl *ar = target->dev->ar; + + if (ar->target_type == TARGET_TYPE_AR6006) /* For KingFisher Only */ + return 1; + else + return 0; +} + +int ath6kl_htc_pipe_change_credit_bypass(struct htc_target *target, + u8 traffic_class) +{ + return 0; +} + +static const struct ath6kl_htc_ops ath6kl_htc_pipe_ops = { + .create = ath6kl_htc_pipe_create, + .wait_target = ath6kl_htc_pipe_wait_target, + .start = ath6kl_htc_pipe_start, + .conn_service = ath6kl_htc_pipe_conn_service, + .tx = ath6kl_htc_pipe_tx, + .stop = ath6kl_htc_pipe_stop, + .cleanup = ath6kl_htc_pipe_cleanup, + .flush_txep = ath6kl_htc_pipe_flush_txep, + .flush_rx_buf = ath6kl_htc_pipe_flush_rx_buf, + .indicate_activity_change = ath6kl_htc_pipe_indicate_activity_change, + .get_rxbuf_num = ath6kl_htc_pipe_get_rxbuf_num, + .add_rxbuf_multiple = ath6kl_htc_pipe_add_rxbuf_multiple, + .credit_setup = ath6kl_htc_pipe_credit_setup, + .get_stat = ath6kl_htc_pipe_stat, + .stop_netif_queue_full = ath6kl_htc_pipe_stop_netif_queue_full, + .indicate_wmm_schedule_change = ath6kl_htc_pipe_wmm_schedule_change, + .change_credit_bypass = ath6kl_htc_pipe_change_credit_bypass, +}; + +void ath6kl_htc_pipe_attach(struct ath6kl *ar) +{ + ar->htc_ops = &ath6kl_htc_pipe_ops; +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/htcoex.c b/drivers/net/wireless/ath/ath6kl-3.5/htcoex.c new file mode 100644 index 000000000000..b88869f3a669 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/htcoex.c @@ -0,0 +1,664 @@ +/* + * Copyright (c) 2004-2012 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "debug.h" + +static u8 *htcoex_get_elem(int elem_id, u8 *start, u16 len) +{ + size_t left = len; + u8 *pos = start; + u8 *elem_start = NULL; + + if ((start == NULL) || (len == 0)) + return NULL; + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + left -= 2; + + if (elen > left) + break; + + if (id == elem_id) { + elem_start = (void *)pos; + break; + } + + left -= elen; + pos += elen; + } + + return elem_start; +} + +static void htcoex_flush_bss_info(struct htcoex *coex) +{ + struct htcoex_bss_info *coex_bss, *tmp; + + spin_lock(&coex->bss_info_lock); + + list_for_each_entry_safe(coex_bss, tmp, &coex->bss_info_list, list) { + list_del(&coex_bss->list); + kfree(coex_bss->raw_frame); + kfree(coex_bss); + } + + spin_unlock(&coex->bss_info_lock); + + return; +} + +static void htcoex_get_coexinfo(struct htcoex_bss_info *coex_bss, + struct htcoex_coex_info *coex_info) +{ + struct ieee80211_ht_cap *htcap; + struct ieee80211_mgmt *frame; + u16 cap_info; + int chan_id = 0; + + frame = (struct ieee80211_mgmt *)(coex_bss->raw_frame); + chan_id = ieee80211_frequency_to_channel( + (int)(coex_bss->channel->center_freq)); + htcap = (struct ieee80211_ht_cap *)htcoex_get_elem( + WLAN_EID_HT_CAPABILITY, frame->u.beacon.variable, + coex_bss->frame_len - (24 + 12)); + + if (htcap) { + cap_info = le16_to_cpu(htcap->cap_info); + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, "htcoex BSSID " + "%02x:%02x:%02x:%02x:%02x:%02x htcap %04x\n", + frame->bssid[0], frame->bssid[1], frame->bssid[2], + frame->bssid[3], frame->bssid[4], frame->bssid[5], + cap_info); + + if (cap_info & IEEE80211_HT_CAP_40MHZ_INTOLERANT) + coex_info->intolerant40++; + } else if (coex_bss->channel->band == IEEE80211_BAND_2GHZ) { + int i; + + for (i = 0; i < coex_info->num_chans; i++) { + if (chan_id == coex_info->chans[i]) + break; + } + + if (i == coex_info->num_chans) { + /* chans only get size 14 */ + BUG_ON(coex_info->num_chans >= 14); + coex_info->chans[coex_info->num_chans++] = chan_id; + } + } + + return; +} + +static void htcoex_scan_start(unsigned long arg) +{ + struct htcoex *coex = (struct htcoex *) arg; + struct ath6kl_vif *vif = coex->vif; + struct ath6kl *ar; + int ret; + + BUG_ON(!vif); + + if ((vif->nw_type != INFRA_NETWORK) || + !test_bit(CONNECTED, &vif->flags) || + vif->scan_req) + goto resche; + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex scan (vif %p) num_scan %d\n", + vif, + coex->num_scan); + + ar = vif->ar; + if (!vif->usr_bss_filter) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); + ret = ath6kl_wmi_bssfilter_cmd( + ar->wmi, + vif->fw_vif_idx, + ALL_BUT_BSS_FILTER, + 0); + if (ret) { + ath6kl_err("couldn't set bss filtering\n"); + goto resche; + } + } + + /* bypass this time if ROC is ongoing. */ + if (test_bit(ROC_PEND, &vif->flags)) + goto resche; + + ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN, + 0, false, 0, 0, + coex->num_scan_channels, + coex->scan_channels); + if (ret) + ath6kl_err("wmi_startscan_cmd failed\n"); + else { + vif->scan_req = &coex->request; + coex->num_scan++; + } + +resche: + if ((coex->flags & ATH6KL_HTCOEX_FLAGS_START) && + (coex->scan_interval)) + mod_timer(&coex->scan_timer, + jiffies + msecs_to_jiffies(coex->scan_interval)); + + return; +} + +static void htcoex_send_action(struct ath6kl_vif *vif, + struct htcoex_coex_info *coex_info) +{ + struct ath6kl *ar = vif->ar; + struct ieee80211_mgmt *action_frame; + int len = 0; + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex send action (vif %p) intolerant40 %d num_chans %d bss_ch %d\n", + vif, + coex_info->intolerant40, + coex_info->num_chans, + vif->bss_ch); + + len = 24 + + sizeof(struct ieee80211_action_public) + + sizeof(struct ieee80211_bss_coex_ie) + + sizeof(struct ieee80211_intolerant_chan_report_ie) + + coex_info->num_chans; + action_frame = kzalloc(len, GFP_KERNEL); + if (action_frame) { + struct ieee80211_action_public *action_public; + struct ieee80211_bss_coex_ie *bss_coex_ie; + struct ieee80211_intolerant_chan_report_ie *into_chan_report_ie; + int i; + + /* Build action frame. */ + action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + memcpy(action_frame->da, vif->bssid, ETH_ALEN); + memcpy(action_frame->sa, vif->ndev->dev_addr, ETH_ALEN); + memcpy(action_frame->bssid, vif->bssid, ETH_ALEN); + + action_public = (struct ieee80211_action_public *) + (&action_frame->u.action); + action_public->category = WLAN_CATEGORY_PUBLIC; + action_public->action_code = WLAN_COEX_ACTION_2040COEX_MGMT; + + bss_coex_ie = (struct ieee80211_bss_coex_ie *) + (action_public->variable); + bss_coex_ie->element_id = WLAN_EID_BSS_COEX_2040; + bss_coex_ie->len = 1; + if (coex_info->intolerant40) + bss_coex_ie->value |= IEEE80211_COEX_IE_20_WIDTH_REQ; + + into_chan_report_ie = + (struct ieee80211_intolerant_chan_report_ie *) + (++bss_coex_ie); + into_chan_report_ie->element_id = + WLAN_EID_INTOLERANT_CHAN_REPORT; + into_chan_report_ie->len = coex_info->num_chans + 1; + into_chan_report_ie->reg_class = 0; + for (i = 0; i < coex_info->num_chans; i++) + into_chan_report_ie->chan_variable[i] = + coex_info->chans[i]; + + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx-htcoex ", + (u8 *)action_frame, len); + + BUG_ON(vif->bss_ch == 0); + ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, + vif->send_action_id++, + vif->bss_ch, 0, + (u8 *)action_frame, len); + kfree(action_frame); + } else { + ath6kl_err("failed to alloc memory for action_frame\n"); + } + + return; + +} + +static void htcoex_ht40_rateset(struct ath6kl_vif *vif, + struct htcoex *coex, + bool enabled) +{ + struct ath6kl *ar = vif->ar; + u64 ratemask; + + if (enabled) + ratemask = ATH6KL_HTCOEX_RATEMASK_HT40; + else + ratemask = ATH6KL_HTCOEX_RATEMASK_HT20; + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex rateset (vif %p) HT40 rates %s, %llx -> %llx\n", + vif, + (enabled ? "Enabled" : "Disabled"), + coex->current_ratemask, + ratemask); + + if (coex->current_ratemask != ratemask) + ath6kl_wmi_set_fix_rates(ar->wmi, vif->fw_vif_idx, + ratemask); + + coex->current_ratemask = ratemask; + + return; +} + +static int +htcoex_clear_scan_channels(struct ath6kl_vif *vif) +{ + struct htcoex *coex = vif->htcoex_ctx; + + if (!coex) + return -EINVAL; + + kfree(coex->scan_channels); + coex->scan_channels = NULL; + coex->num_scan_channels = 0; + return 0; +} + +static int +htcoex_set_scan_channels(struct ath6kl_vif *vif) +{ + struct htcoex *coex = vif->htcoex_ctx; + struct wiphy *wiphy = coex->request.wiphy; + int i; + + if (!wiphy->bands[IEEE80211_BAND_2GHZ]) + return -EINVAL; + + /*If the scan channel is already set, + * clear it and apply new channel list. + */ + if (coex->num_scan_channels != 0 || coex->scan_channels) + htcoex_clear_scan_channels(vif); + + /*scan_channels may be larger than what we actually need because some + * of them might be disabled so we will filter them later. + */ + coex->scan_channels = + kzalloc(wiphy->bands[IEEE80211_BAND_2GHZ]->n_channels * + sizeof(u16), GFP_KERNEL); + + if (!coex->scan_channels) + return -ENOMEM; + + /*Copy all enabled 2G channels.*/ + for (i = 0; i < wiphy->bands[IEEE80211_BAND_2GHZ]->n_channels; i++) { + struct ieee80211_channel *chan; + + chan = &wiphy->bands[IEEE80211_BAND_2GHZ]->channels[i]; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + coex->scan_channels[coex->num_scan_channels++] = + chan->center_freq; + } + + return 0; +} + +struct htcoex *ath6kl_htcoex_init(struct ath6kl_vif *vif) +{ + struct htcoex *coex; + + coex = kzalloc(sizeof(struct htcoex), GFP_KERNEL); + if (!coex) { + ath6kl_err("failed to alloc memory for htcoex\n"); + return NULL; + } + + coex->vif = vif; + + /* Disable by default. */ + coex->flags &= ~ATH6KL_HTCOEX_FLAGS_ENABLED; + coex->scan_interval = ATH6KL_HTCOEX_SCAN_PERIOD; + coex->rate_rollback_interval = ATH6KL_HTCOEX_RATE_ROLLBACK; + + /* Init. periodic scan timer. */ + init_timer(&coex->scan_timer); + coex->scan_timer.function = htcoex_scan_start; + coex->scan_timer.data = (unsigned long) coex; + +#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV + coex->request.wdev = &vif->wdev; +#else + coex->request.dev = vif->ndev; +#endif + coex->request.wiphy = vif->ar->wiphy; + + coex->num_scan_channels = 0; + coex->scan_channels = NULL; + + spin_lock_init(&coex->bss_info_lock); + INIT_LIST_HEAD(&coex->bss_info_list); + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex init (vif %p interval %d)\n", + vif, + coex->scan_interval); + + return coex; +} + +void ath6kl_htcoex_deinit(struct ath6kl_vif *vif) +{ + struct htcoex *coex = vif->htcoex_ctx; + + if (coex) { + if (coex->flags & ATH6KL_HTCOEX_FLAGS_START) { + del_timer(&coex->scan_timer); + htcoex_clear_scan_channels(vif); + } + htcoex_flush_bss_info(coex); + + kfree(coex); + } + + vif->htcoex_ctx = NULL; + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex deinit (vif %p)\n", + vif); + + return; +} + +void ath6kl_htcoex_bss_info(struct ath6kl_vif *vif, + struct ieee80211_mgmt *mgmt, + int len, + struct ieee80211_channel *channel) +{ + struct htcoex *coex = vif->htcoex_ctx; + struct htcoex_bss_info *coex_bss, *tmp; + int match = 0; + + /* Add bss even scan not issue by htcoex. */ + if (!(coex->flags & ATH6KL_HTCOEX_FLAGS_START) || + (vif->nw_type != INFRA_NETWORK) || + (memcmp(mgmt->bssid, vif->bssid, ETH_ALEN) == 0)) + return; + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex bssinfo (vif %p) BSSID " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + vif, + mgmt->bssid[0], mgmt->bssid[1], mgmt->bssid[2], + mgmt->bssid[3], mgmt->bssid[4], mgmt->bssid[5]); + + if (!list_empty(&coex->bss_info_list)) { + list_for_each_entry_safe(coex_bss, tmp, + &coex->bss_info_list, list) { + if (memcmp(coex_bss->bssid, + mgmt->bssid, ETH_ALEN) == 0) { + match = 1; + break; + } + } + } + + if (!match) { + coex_bss = kzalloc(sizeof(struct htcoex_bss_info), GFP_KERNEL); + if (!coex_bss) { + ath6kl_err("failed to alloc memory for coex_bss\n"); + return; + } + + coex_bss->raw_frame = kmalloc(len, GFP_KERNEL); + if (!coex_bss->raw_frame) { + kfree(coex_bss); + ath6kl_err("failed to alloc memory for coex_bss->raw_frame\n"); + return; + } + + /* Add BSS info. */ + coex_bss->frame_len = len; + memcpy(coex_bss->raw_frame, mgmt, len); + memcpy(coex_bss->bssid, mgmt->bssid, ETH_ALEN); + coex_bss->channel = channel; + + spin_lock(&coex->bss_info_lock); + list_add_tail(&coex_bss->list, &coex->bss_info_list); + spin_unlock(&coex->bss_info_lock); + } + + return; +} + +int ath6kl_htcoex_scan_complete_event(struct ath6kl_vif *vif, bool aborted) +{ + struct htcoex *coex = vif->htcoex_ctx; + struct htcoex_bss_info *coex_bss, *tmp; + struct htcoex_coex_info coex_info; + int ret = HTCOEX_PASS_SCAN_DONE; + + /* Send Action frame even scan issue by user. */ + if (!(coex->flags & ATH6KL_HTCOEX_FLAGS_START) || + (vif->nw_type != INFRA_NETWORK) || + (!vif->scan_req)) + goto done; + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex scan done (vif %p aborted %d) flags %x\n", + vif, + aborted, + coex->flags); + + memset(&coex_info, 0, sizeof(struct htcoex_coex_info)); + + /* Parse all Beacon/ProbeResp's IEs to find 20/40 coex. information. */ + list_for_each_entry_safe(coex_bss, tmp, &coex->bss_info_list, list) { + htcoex_get_coexinfo(coex_bss, &coex_info); + } + + /* Send action frame to AP. */ + if (coex_info.intolerant40 || coex_info.num_chans) + htcoex_send_action(vif, &coex_info); + + /* Disable HT40 rates. */ + if (coex_info.intolerant40) { + htcoex_ht40_rateset(vif, coex, false); + coex->tolerant40_cnt = 0; + } else { + /* Roll-back to HT40 rate if acceptable. */ + if (coex->rate_rollback_interval && + (++coex->tolerant40_cnt > coex->rate_rollback_interval)) + htcoex_ht40_rateset(vif, coex, true); + } + + /* Issue by htcoex. */ + if (vif->scan_req == &coex->request) { + vif->scan_req = NULL; + ret = HTCOEX_PORC_SCAN_DONE; + } + +done: + /* Always flush it. */ + htcoex_flush_bss_info(coex); + + return ret; +} + +void ath6kl_htcoex_connect_event(struct ath6kl_vif *vif) +{ + struct htcoex *coex = vif->htcoex_ctx; + struct cfg80211_bss *bss; + u8 *ies = NULL; + u16 ies_len = 0; + + if (vif->nw_type != INFRA_NETWORK) + return; + + bss = cfg80211_get_bss(vif->wdev.wiphy, + NULL, + vif->bssid, + vif->ssid, + vif->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + if (!bss) { + WARN_ON(1); + return; + } + +#ifdef CFG80211_SAFE_BSS_INFO_ACCESS + rcu_read_lock(); + if (bss->ies) { + ies = (u8 *)(bss->ies->data); + ies_len = bss->ies->len; + } +#else + if (bss->information_elements) { + ies = bss->information_elements; + ies_len = bss->len_information_elements; + } +#endif + + /* Start if BSS is 11n and 2G channel. */ + if ((coex->flags & ATH6KL_HTCOEX_FLAGS_ENABLED) && + (bss->channel->band == IEEE80211_BAND_2GHZ) && + (htcoex_get_elem(WLAN_EID_HT_CAPABILITY, + ies, + ies_len) != NULL)){ + if (coex->flags & ATH6KL_HTCOEX_FLAGS_START) + del_timer(&coex->scan_timer); + + if (coex->scan_interval < ATH6KL_HTCOEX_SCAN_PERIOD) + coex->scan_interval = ATH6KL_HTCOEX_SCAN_PERIOD; + + /*htcoex set scan channels*/ + if (htcoex_set_scan_channels(vif) == 0) { + mod_timer(&coex->scan_timer, jiffies + + msecs_to_jiffies(coex->scan_interval)); + coex->flags |= ATH6KL_HTCOEX_FLAGS_START; + } else { + ath6kl_err("htcoex set scan channels failed\n"); + WARN_ON(1); + } + + } else + coex->flags &= ~ATH6KL_HTCOEX_FLAGS_START; + +#ifdef CFG80211_SAFE_BSS_INFO_ACCESS + rcu_read_unlock(); +#endif + + coex->num_scan = 0; + coex->tolerant40_cnt = 0; + coex->current_ratemask = ATH6KL_HTCOEX_RATEMASK_FULL; + htcoex_flush_bss_info(coex); + + cfg80211_put_bss(bss); + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex connect (vif %p) flags %x interval %d cycle %d\n", + vif, + coex->flags, + coex->scan_interval, + coex->rate_rollback_interval); + + return; +} + +void ath6kl_htcoex_disconnect_event(struct ath6kl_vif *vif) +{ + struct htcoex *coex = vif->htcoex_ctx; + + if (vif->nw_type != INFRA_NETWORK) + return; + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex disconnect (vif %p) flags %x\n", + vif, + coex->flags); + + if (coex->flags & ATH6KL_HTCOEX_FLAGS_START) { + del_timer(&coex->scan_timer); + coex->flags &= ~ATH6KL_HTCOEX_FLAGS_START; + + /* Back to full rates */ + ath6kl_wmi_set_fix_rates(vif->ar->wmi, vif->fw_vif_idx, + ATH6KL_HTCOEX_RATEMASK_FULL); + /*clear the scan channels*/ + htcoex_clear_scan_channels(vif); + } + + htcoex_flush_bss_info(coex); + + return; +} + +int ath6kl_htcoex_config(struct ath6kl_vif *vif, u32 interval, u8 rate_rollback) +{ + struct htcoex *coex = vif->htcoex_ctx; + int restart = 0; + + coex->scan_interval = interval; + + if (coex->flags & ATH6KL_HTCOEX_FLAGS_START) { + del_timer(&coex->scan_timer); + htcoex_clear_scan_channels(vif); + coex->flags &= ~ATH6KL_HTCOEX_FLAGS_START; + restart = 1; + } + + if (coex->scan_interval == 0) + coex->flags &= ~ATH6KL_HTCOEX_FLAGS_ENABLED; + else { + if (coex->scan_interval < ATH6KL_HTCOEX_SCAN_PERIOD) + coex->scan_interval = ATH6KL_HTCOEX_SCAN_PERIOD; + + coex->flags |= ATH6KL_HTCOEX_FLAGS_ENABLED; + + if (restart) { + if (htcoex_set_scan_channels(vif) == 0) { + mod_timer(&coex->scan_timer, jiffies + + msecs_to_jiffies(coex->scan_interval)); + coex->flags |= ATH6KL_HTCOEX_FLAGS_START; + } else { + ath6kl_err("htcoex set scan channels failed\n"); + WARN_ON(1); + } + } + + coex->rate_rollback_interval = rate_rollback; + } + + htcoex_flush_bss_info(coex); + + ath6kl_dbg(ATH6KL_DBG_HTCOEX, + "htcoex config (vif %p interval %d %s rate_rollback %d restart %d)\n", + vif, + coex->scan_interval, + (coex->flags & ATH6KL_HTCOEX_FLAGS_ENABLED) ? "ON" : "OFF", + coex->rate_rollback_interval, + restart); + + + return 0; +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/htcoex.h b/drivers/net/wireless/ath/ath6kl-3.5/htcoex.h new file mode 100644 index 000000000000..b1264a26c7b5 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/htcoex.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2004-2012 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 HTCOEX_H +#define HTCOEX_H + +#define ATH6KL_HTCOEX_SCAN_PERIOD (60 * 1000) /* in ms */ + +/* in scan cycle */ +#define ATH6KL_HTCOEX_RATE_ROLLBACK (0) + +/* htcoex enable/disable */ +#define ATH6KL_HTCOEX_FLAGS_ENABLED BIT(0) + +/* htcoex start/stop */ +#define ATH6KL_HTCOEX_FLAGS_START BIT(1) + +/* 1x1/2x2 solution only */ +#define ATH6KL_HTCOEX_RATEMASK_FULL (0x00000fffffffffffULL) +#define ATH6KL_HTCOEX_RATEMASK_HT20 (0x000000000fffffffULL) +#define ATH6KL_HTCOEX_RATEMASK_HT40 (0x00000fffffffffffULL) + +struct htcoex_coex_info { + u32 intolerant40; + int num_chans; + u8 chans[14]; +}; + +struct htcoex_bss_info { + struct list_head list; + + u8 *raw_frame; + u16 frame_len; + u8 bssid[ETH_ALEN]; + struct ieee80211_channel *channel; +}; + +struct htcoex { + struct ath6kl_vif *vif; + u32 flags; + u32 scan_interval; /* in ms, 0 means htcoex disable. */ + u32 num_scan; + + u8 rate_rollback_interval; /* in scan cycle, 0 means no roll-back. */ + u64 current_ratemask; + u32 tolerant40_cnt; + + struct timer_list scan_timer; + struct cfg80211_scan_request request; + + spinlock_t bss_info_lock; + struct list_head bss_info_list; + + s8 num_scan_channels; + u16 *scan_channels; +}; + +enum { + HTCOEX_PORC_SCAN_DONE = 0, + HTCOEX_PASS_SCAN_DONE, +}; + +/* COEX action codes */ +enum ieee80211_coex_actioncode { + WLAN_COEX_ACTION_2040COEX_MGMT = 0, +}; + +/* Public action frame format */ +struct ieee80211_action_public { + u8 category; + u8 action_code; + u8 variable[0]; +} __packed; + +/* 20/40 BSS Coexistence IE */ +#define IEEE80211_COEX_IE_INFO_REQ (1 << 0) +#define IEEE80211_COEX_IE_40_INTOLERANT (1 << 1) +#define IEEE80211_COEX_IE_20_WIDTH_REQ (1 << 2) +#define IEEE80211_COEX_IE_OBSS_SCAN_REQ (1 << 3) +#define IEEE80211_COEX_IE_OBSS_SCAN_GRANT (1 << 4) + +struct ieee80211_bss_coex_ie { + u8 element_id; + u8 len; + u8 value; +} __packed; + +/* 20/40 BSS Intolerant Channel Report IE */ +#define WLAN_EID_INTOLERANT_CHAN_REPORT 73 + +struct ieee80211_intolerant_chan_report_ie { + u8 element_id; + u8 len; + u8 reg_class; + u8 chan_variable[0]; +} __packed; + +struct htcoex *ath6kl_htcoex_init(struct ath6kl_vif *vif); +void ath6kl_htcoex_deinit(struct ath6kl_vif *vif); +void ath6kl_htcoex_bss_info(struct ath6kl_vif *vif, + struct ieee80211_mgmt *mgmt, + int len, + struct ieee80211_channel *channel); +int ath6kl_htcoex_scan_complete_event(struct ath6kl_vif *vif, bool aborted); +void ath6kl_htcoex_connect_event(struct ath6kl_vif *vif); +void ath6kl_htcoex_disconnect_event(struct ath6kl_vif *vif); +int ath6kl_htcoex_config(struct ath6kl_vif *vif, + u32 interval, u8 rate_rollback); +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/init.c b/drivers/net/wireless/ath/ath6kl-3.5/init.c new file mode 100644 index 000000000000..942124599206 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/init.c @@ -0,0 +1,3302 @@ + +/* + * Copyright (c) 2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 +#ifndef CE_OLD_KERNEL_SUPPORT_2_6_23 +#include +#include +#include +#endif +#include "core.h" +#include "cfg80211.h" +#include "target.h" +#include "debug.h" +#include "hif-ops.h" +#include "htc-ops.h" +#include "rttapi.h" +#ifdef ATH6KL_DIAGNOSTIC +#include "diagnose.h" +#endif +#include "pm.h" + +#include "bdata.bin.c" +#include "fw.ram.bin.c" + +unsigned int debug_mask;//=ATH6KL_DBG_WLAN_TX|ATH6KL_DBG_WLAN_RX;//ATH6KL_DBG_ANY; +unsigned int htc_bundle_recv; +unsigned int htc_bundle_send; +unsigned int htc_bundle_send_timer; +unsigned int htc_bundle_send_th = 6000; +unsigned int testmode; +unsigned int ath6kl_wow_ext = 1; +unsigned int ath6kl_wow_gpio = 9; +unsigned int ath6kl_p2p = ATH6KL_MODULEP2P_DEF_MODE; +unsigned int ath6kl_vap = ATH6KL_MODULEVAP_DEF_MODE; +unsigned int ath6kl_scan_timeout; +unsigned int ath6kl_roam_mode = ATH6KL_MODULEROAM_DEFAULT; +static unsigned int recovery_enable_mode = ATH6KL_RECOVERY_MODE_NONE; +unsigned int starving_prevention; +unsigned int ath6kl_ath0_name; +#ifdef CE_SUPPORT +unsigned int ath6kl_ce_flags = 1; +#endif + +#ifdef CONFIG_QC_INTERNAL +unsigned short reg_domain = 0xffff; +module_param(reg_domain, ushort, 0644); +#endif + +#ifdef ATH6KL_DIAGNOSTIC +unsigned int diag_local_test; +module_param(diag_local_test, uint, 0644); +#endif + +/* assume string is "00:11:22:33:44:55". + used to override the default MAC of MAC from softmac.bin file */ +char *ath6kl_wifi_mac; + +/* for android frame work, we need to add fwpath module parameter, + to avoid the problem that create softap mode will fail. */ +char *fwpath = "android_fw_path_compatible_str"; + +module_param(debug_mask, uint, 0644); +module_param(htc_bundle_recv, uint, 0644); +module_param(htc_bundle_send, uint, 0644); +module_param(htc_bundle_send_timer, uint, 0644); +module_param(testmode, uint, 0644); +module_param(ath6kl_wow_ext, uint, 0644); +module_param(ath6kl_wow_gpio, uint, 0644); +module_param(ath6kl_p2p, uint, 0644); +module_param(ath6kl_vap, uint, 0644); +module_param(ath6kl_wifi_mac, charp, 0000); +module_param(ath6kl_scan_timeout, uint, 0644); +module_param(ath6kl_roam_mode, uint, 0644); +module_param(recovery_enable_mode, uint, 0644); +module_param(fwpath, charp, 0644); +module_param(ath6kl_ath0_name, uint, 0644); +#ifdef CE_SUPPORT +module_param(ath6kl_ce_flags, uint, 0644); +char *bdatapath; +module_param(bdatapath, charp, 0644); +char *fwdatapath; +module_param(fwdatapath, charp, 0644); +#endif +module_param(starving_prevention, uint, 0644); + +static const struct ath6kl_hw hw_list[] = { + { + .id = AR6003_HW_2_0_VERSION, + .name = "ar6003 hw 2.0", + .dataset_patch_addr = 0x57e884, + .app_load_addr = 0x543180, + .board_ext_data_addr = 0x57e500, + .reserved_ram_size = 6912, + + /* hw2.0 needs override address hardcoded */ + .app_start_override_addr = 0x944C00, + .flags = 0, + + .fw = { + .dir = AR6003_HW_2_0_FW_DIR, + .otp = AR6003_HW_2_0_OTP_FILE, + .fw = AR6003_HW_2_0_FIRMWARE_FILE, + .tcmd = AR6003_HW_2_0_TCMD_FIRMWARE_FILE, + .patch = AR6003_HW_2_0_PATCH_FILE, + .api2 = ATH6KL_FW_API2_FILE, + }, + + .fw_board = AR6003_HW_2_0_BOARD_DATA_FILE, + .fw_default_board = AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE, + }, + { + .id = AR6003_HW_2_1_1_VERSION, + .name = "ar6003 hw 2.1.1", + .dataset_patch_addr = 0x57ff74, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x542330, + .reserved_ram_size = 512, + .testscript_addr = 0x57ef74, + .flags = 0, + + .fw = { + .dir = AR6003_HW_2_1_1_FW_DIR, + .otp = AR6003_HW_2_1_1_OTP_FILE, + .fw = AR6003_HW_2_1_1_FIRMWARE_FILE, + .tcmd = AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE, + .patch = AR6003_HW_2_1_1_PATCH_FILE, + .api2 = ATH6KL_FW_API2_FILE, + .utf = AR6003_HW_2_1_1_UTF_FIRMWARE_FILE, + .testscript = AR6003_HW_2_1_1_TESTSCRIPT_FILE, + }, + + .fw_board = AR6003_HW_2_1_1_BOARD_DATA_FILE, + .fw_default_board = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE, + }, + { + .id = AR6004_HW_1_0_VERSION, + .name = "ar6004 hw 1.0", + .dataset_patch_addr = 0x57e884, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 19456, + .board_addr = 0x433900, + .testscript_addr = 0x432900, + .flags = ATH6KL_HW_TGT_ALIGN_PADDING | + ATH6KL_HW_SINGLE_PIPE_SCHED, + + .fw = { + .dir = AR6004_HW_1_0_FW_DIR, + .otp = AR6004_HW_1_0_OTP_FILE, + .fw = AR6004_HW_1_0_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + }, + + .fw_board = AR6004_HW_1_0_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE, + }, + { + .id = AR6004_HW_1_1_VERSION, + .name = "ar6004 hw 1.1", + .dataset_patch_addr = 0x57e884, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 7168, + .board_addr = 0x436400, + .testscript_addr = 0x435400, + .flags = ATH6KL_HW_TGT_ALIGN_PADDING | + ATH6KL_HW_SINGLE_PIPE_SCHED, + + .fw = { + .dir = AR6004_HW_1_1_FW_DIR, + .otp = AR6004_HW_1_1_OTP_FILE, + .fw = AR6004_HW_1_1_FIRMWARE_FILE, + .tcmd = AR6004_HW_1_1_TCMD_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + .utf = AR6004_HW_1_1_UTF_FIRMWARE_FILE, + .testscript = AR6004_HW_1_1_TESTSCRIPT_FILE, + }, + + .fw_board = AR6004_HW_1_1_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE, + .fw_epping = AR6004_HW_1_1_EPPING_FILE, + .fw_softmac = AR6004_HW_1_1_SOFTMAC_FILE, + }, + { + .id = AR6004_HW_1_2_VERSION, + .name = "ar6004 hw 1.2", + .dataset_patch_addr = 0x436ecc, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 9216, + .board_addr = 0x435c00, + .testscript_addr = 0x434c00, + .flags = ATH6KL_HW_TGT_ALIGN_PADDING | + ATH6KL_HW_SINGLE_PIPE_SCHED, + + .fw = { + .dir = AR6004_HW_1_2_FW_DIR, + .otp = AR6004_HW_1_2_OTP_FILE, + .fw = AR6004_HW_1_2_FIRMWARE_FILE, + .tcmd = AR6004_HW_1_2_TCMD_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + .utf = AR6004_HW_1_2_UTF_FIRMWARE_FILE, + .testscript = AR6004_HW_1_2_TESTSCRIPT_FILE, + }, + + .fw_board = AR6004_HW_1_2_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE, + .fw_epping = AR6004_HW_1_2_EPPING_FILE, + .fw_softmac = AR6004_HW_1_2_SOFTMAC_FILE, + }, + { + .id = AR6004_HW_1_3_VERSION, + .name = "ar6004 hw 1.3", + .dataset_patch_addr = 0x437860, + .app_load_addr = 0x1234, + .app_load_ext_addr = 0x9a7000, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 7168, + .board_addr = 0x436400, + .testscript_addr = 0x434c00, + .flags = ATH6KL_HW_TGT_ALIGN_PADDING | + ATH6KL_HW_SINGLE_PIPE_SCHED | + ATH6KL_HW_FIRMWARE_EXT_SUPPORT, + + .fw = { + .dir = AR6004_HW_1_3_FW_DIR, + .otp = AR6004_HW_1_3_OTP_FILE, + .fw = AR6004_HW_1_3_FIRMWARE_FILE, + .tcmd = AR6004_HW_1_3_TCMD_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + .utf = AR6004_HW_1_3_UTF_FIRMWARE_FILE, + .testscript = AR6004_HW_1_3_TESTSCRIPT_FILE, + .fw_ext = AR6004_HW_1_3_FIRMWARE_EXT_FILE, + }, + + .fw_board = AR6004_HW_1_3_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE, + .fw_epping = AR6004_HW_1_3_EPPING_FILE, + .fw_softmac = AR6004_HW_1_3_SOFTMAC_FILE, + }, + { + .id = AR6004_HW_1_3_VERSION, + .name = "ar6004 hw 1.3 96K", + .dataset_patch_addr = 0x42f860, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x42f000, + .reserved_ram_size = 7168, + .board_addr = 0x42e400, + .testscript_addr = 0x434c00, + .flags = ATH6KL_HW_TGT_ALIGN_PADDING | + ATH6KL_HW_SINGLE_PIPE_SCHED, + + .fw = { + .dir = AR6004_HW_1_3_FW_DIR, + .otp = AR6004_HW_1_3_OTP_FILE, + .fw = AR6004_HW_1_3_FIRMWARE_FILE, + .tcmd = AR6004_HW_1_3_TCMD_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + .utf = AR6004_HW_1_3_UTF_FIRMWARE_FILE, + .testscript = AR6004_HW_1_3_TESTSCRIPT_FILE, + }, + + .fw_board = AR6004_HW_1_3_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE, + .fw_epping = AR6004_HW_1_3_EPPING_FILE, + .fw_softmac = AR6004_HW_1_3_SOFTMAC_FILE, + }, + { + .id = AR6004_HW_2_0_VERSION, + .name = "ar6004 hw 2.0", + .dataset_patch_addr = 0, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0, + .reserved_ram_size = 7168, + .board_addr = 0x43e400, + .testscript_addr = 0x43d400, + .flags = ATH6KL_HW_SINGLE_PIPE_SCHED | + ATH6KL_HW_USB_FLOWCTRL, + .fw = { + .dir = AR6004_HW_2_0_FW_DIR, + .otp = AR6004_HW_2_0_OTP_FILE, + .fw = AR6004_HW_2_0_FIRMWARE_FILE, + .tcmd = AR6004_HW_2_0_TCMD_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + .utf = AR6004_HW_2_0_UTF_FIRMWARE_FILE, + .testscript = AR6004_HW_2_0_TESTSCRIPT_FILE, + }, + + .fw_board = AR6004_HW_2_0_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_2_0_DEFAULT_BOARD_DATA_FILE, + .fw_epping = AR6004_HW_2_0_EPPING_FILE, + .fw_softmac = AR6004_HW_2_0_SOFTMAC_FILE, + }, + { + .id = AR6004_HW_3_0_VERSION, + .name = "ar6004 hw 3.0", + .dataset_patch_addr = 0, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0, + .reserved_ram_size = 7168, + .board_addr = 0x436400, + .testscript_addr = 0, + .flags = ATH6KL_HW_SINGLE_PIPE_SCHED | + ATH6KL_HW_USB_FLOWCTRL, + + .fw = { + .dir = AR6004_HW_3_0_FW_DIR, + .fw = AR6004_HW_3_0_FIRMWARE_FILE, + .tcmd = AR6004_HW_3_0_TCMD_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + .utf = AR6004_HW_3_0_UTF_FIRMWARE_FILE, + }, + + .fw_board = AR6004_HW_3_0_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE, + .fw_epping = AR6004_HW_3_0_EPPING_FILE, + .fw_softmac = AR6004_HW_3_0_SOFTMAC_FILE, + }, + { + .id = AR6006_HW_1_0_VERSION, + .name = "ar6006 hw 1.0", + .dataset_patch_addr = 0, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0, + .reserved_ram_size = 18432, + .board_addr = 0x46B800, + .flags = ATH6KL_HW_SINGLE_PIPE_SCHED| + ATH6KL_HW_USB_FLOWCTRL| + ATH6KL_HW_XTAL_40MHZ, + + .fw = { + .dir = AR6006_HW_1_0_FW_DIR, + .fw = AR6006_HW_1_0_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + }, + + .fw_board = AR6006_HW_1_0_BOARD_DATA_FILE, + .fw_default_board = AR6006_HW_1_0_DEFAULT_BOARD_DATA_FILE, + .fw_epping = AR6006_HW_1_0_EPPING_FILE, + .fw_softmac = AR6006_HW_1_0_SOFTMAC_FILE, + }, + { + .id = AR6006_HW_1_1_VERSION, + .name = "ar6006 hw 1.1", + .dataset_patch_addr = 0, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0, + .reserved_ram_size = 7168, + .board_addr = 0x46e400, + .flags = ATH6KL_HW_SINGLE_PIPE_SCHED| + ATH6KL_HW_USB_FLOWCTRL| + ATH6KL_HW_XTAL_40MHZ, + + .fw = { + .dir = AR6006_HW_1_1_FW_DIR, + .fw = AR6006_HW_1_1_FIRMWARE_FILE, + .api2 = ATH6KL_FW_API2_FILE, + }, + + .fw_board = AR6006_HW_1_1_BOARD_DATA_FILE, + .fw_default_board = AR6006_HW_1_1_DEFAULT_BOARD_DATA_FILE, + .fw_epping = AR6006_HW_1_1_EPPING_FILE, + .fw_softmac = AR6006_HW_1_1_SOFTMAC_FILE, + }, +}; + +/* + * Include definitions here that can be used to tune the WLAN module + * behavior. Different customers can tune the behavior as per their needs, + * here. + */ + +/* + * This configuration item enable/disable keepalive support. + * Keepalive support: In the absence of any data traffic to AP, null + * frames will be sent to the AP at periodic interval, to keep the association + * active. This configuration item defines the periodic interval. + * Use value of zero to disable keepalive support + * Default: 60 seconds + */ +#define WLAN_CONFIG_KEEP_ALIVE_INTERVAL 60 + +/* + * This configuration item sets the value of disconnect timeout + * Firmware delays sending the disconnec event to the host for this + * timeout after is gets disconnected from the current AP. + * If the firmware successly roams within the disconnect timeout + * it sends a new connect event + */ +#define WLAN_CONFIG_DISCONNECT_TIMEOUT 10 + +#define CONFIG_AR600x_DEBUG_UART_TX_PIN 8 +#define CONFIG_AR6004_DEBUG_UART_TX_PIN 11 +#define CONFIG_AR6006_DEBUG_UART_TX_PIN 11 +#define CONFIG_AR6006_FPGA_DEBUG_UART_TX_PIN 24 + +#define ATH6KL_DATA_OFFSET 64 +struct sk_buff *ath6kl_buf_alloc(int size) +{ + struct sk_buff *skb; + u16 reserved; + + /* Add chacheline space at front and back of buffer */ + reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + + sizeof(struct htc_packet); + skb = dev_alloc_skb(size + reserved); + + if (skb) + skb_reserve(skb, reserved - L1_CACHE_BYTES); + return skb; +} + +void ath6kl_init_profile_info(struct ath6kl_vif *vif) +{ + vif->ssid_len = 0; + memset(vif->ssid, 0, sizeof(vif->ssid)); + + vif->dot11_auth_mode = OPEN_AUTH; + vif->auth_mode = NONE_AUTH; + vif->prwise_crypto = NONE_CRYPT; + vif->prwise_crypto_len = 0; + vif->grp_crypto = NONE_CRYPT; + vif->grp_crypto_len = 0; + memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); + memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); + memset(vif->bssid, 0, sizeof(vif->bssid)); + vif->bss_ch = 0; + vif->phymode = ATH6KL_PHY_MODE_UNKNOWN; + vif->chan_type = ATH6KL_CHAN_TYPE_NONE; +} + + +static int ath6kl_set_host_app_area(struct ath6kl *ar) +{ + u32 address, data; + struct host_app_area host_app_area; + + /* Fetch the address of the host_app_area_s + * instance in the host interest area */ + address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); + address = TARG_VTOP(ar->target_type, address); + + if (ath6kl_diag_read32(ar, address, &data)) + return -EIO; + + address = TARG_VTOP(ar->target_type, data); + host_app_area.wmi_protocol_ver = cpu_to_le32(WMI_PROTOCOL_VERSION); + if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area, + sizeof(struct host_app_area))) + return -EIO; + + return 0; +} + +static u32 ath6kl_get_host_app_area(struct ath6kl *ar) +{ + u32 address, data; + + address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); + address = TARG_VTOP(ar->target_type, address); + + if (ath6kl_diag_read32(ar, address, &data)) + return -EIO; + + address = TARG_VTOP(ar->target_type, data); + + return address; +} + +static inline void set_ac2_ep_map(struct ath6kl *ar, + u8 ac, + enum htc_endpoint_id ep) +{ + ar->ac2ep_map[ac] = ep; + ar->ep2ac_map[ep] = ac; +} + +/* connect to a service */ +static int ath6kl_connectservice(struct ath6kl *ar, + struct htc_service_connect_req *con_req, + char *desc) +{ + int status; + struct htc_service_connect_resp response; + + memset(&response, 0, sizeof(response)); + + status = ath6kl_htc_conn_service(ar->htc_target, con_req, &response); + if (status) { + ath6kl_err("failed to connect to %s service status:%d\n", + desc, status); + return status; + } + + if (response.endpoint >= ENDPOINT_MAX + || response.endpoint <= ENDPOINT_UNUSED) { + ath6kl_err("Invalid endpoint: %d\n", response.endpoint); + return -EINVAL; + } + + switch (con_req->svc_id) { + case WMI_CONTROL_SVC: + if (test_bit(WMI_ENABLED, &ar->flag)) + ath6kl_wmi_set_control_ep(ar->wmi, response.endpoint); + ar->ctrl_ep = response.endpoint; + break; + case WMI_DATA_BE_SVC: + set_ac2_ep_map(ar, WMM_AC_BE, response.endpoint); + break; + case WMI_DATA_BK_SVC: + set_ac2_ep_map(ar, WMM_AC_BK, response.endpoint); + break; + case WMI_DATA_VI_SVC: + set_ac2_ep_map(ar, WMM_AC_VI, response.endpoint); + break; + case WMI_DATA_VO_SVC: + set_ac2_ep_map(ar, WMM_AC_VO, response.endpoint); + break; + default: + ath6kl_err("service id is not mapped %d\n", con_req->svc_id); + return -EINVAL; + } + + return 0; +} + +static int ath6kl_init_service_ep(struct ath6kl *ar) +{ + struct htc_service_connect_req connect; + + memset(&connect, 0, sizeof(connect)); + + if (ar->hw.flags & ATH6KL_HW_USB_FLOWCTRL) + connect.conn_flags |= HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL; + + /* these fields are the same for all service endpoints */ + connect.ep_cb.tx_comp_multi = ath6kl_tx_complete; + connect.ep_cb.rx = ath6kl_rx; + connect.ep_cb.rx_refill = ath6kl_rx_refill; + connect.ep_cb.tx_full = ath6kl_tx_queue_full; + + /* + * Set the max queue depth so that our ath6kl_tx_queue_full handler + * gets called. + */ + connect.max_txq_depth = MAX_DEFAULT_SEND_QUEUE_DEPTH; + connect.ep_cb.rx_refill_thresh = ATH6KL_MAX_RX_BUFFERS / 4; + if (!connect.ep_cb.rx_refill_thresh) + connect.ep_cb.rx_refill_thresh++; + + /* connect to control service */ + connect.svc_id = WMI_CONTROL_SVC; + if (ath6kl_connectservice(ar, &connect, "WMI CONTROL")) + return -EIO; + + connect.flags |= HTC_FLGS_TX_BNDL_PAD_EN; + + /* + * Limit the HTC message size on the send path, although e can + * receive A-MSDU frames of 4K, we will only send ethernet-sized + * (802.3) frames on the send path. + */ + connect.max_rxmsg_sz = WMI_MAX_TX_DATA_FRAME_LENGTH; + + /* + * To reduce the amount of committed memory for larger A_MSDU + * frames, use the recv-alloc threshold mechanism for larger + * packets. + */ + connect.ep_cb.rx_alloc_thresh = ATH6KL_BUFFER_SIZE; + connect.ep_cb.rx_allocthresh = ath6kl_alloc_amsdu_rxbuf; + + /* + * For the remaining data services set the connection flag to + * reduce dribbling, if configured to do so. + */ + connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB; + connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK; + connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF; + + connect.svc_id = WMI_DATA_BE_SVC; + + if (ath6kl_connectservice(ar, &connect, "WMI DATA BE")) + return -EIO; + + /* connect to back-ground map this to WMI LOW_PRI */ + connect.svc_id = WMI_DATA_BK_SVC; + if (ath6kl_connectservice(ar, &connect, "WMI DATA BK")) + return -EIO; + + /* connect to Video service, map this to to HI PRI */ + connect.svc_id = WMI_DATA_VI_SVC; + if (ath6kl_connectservice(ar, &connect, "WMI DATA VI")) + return -EIO; + + /* + * Connect to VO service, this is currently not mapped to a WMI + * priority stream due to historical reasons. WMI originally + * defined 3 priorities over 3 mailboxes We can change this when + * WMI is reworked so that priorities are not dependent on + * mailboxes. + */ + connect.svc_id = WMI_DATA_VO_SVC; + if (ath6kl_connectservice(ar, &connect, "WMI DATA VO")) + return -EIO; + + return 0; +} + +void ath6kl_init_control_info(struct ath6kl_vif *vif) +{ + u8 ctr; + struct ath6kl *ar = vif->ar; + + ath6kl_init_profile_info(vif); + vif->def_txkey_index = 0; + memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); + vif->ch_hint = 0; + + vif->intra_bss = 1; + + memset((u8 *)vif->sta_list, 0, + AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); + + /* Init the PS queues */ + for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { + spin_lock_init(&vif->sta_list[ctr].lock); + ath6kl_ps_queue_init(&vif->sta_list[ctr].psq_data, + PS_QUEUE_TYPE_STA_UNICAST, + ATH6KL_PS_QUEUE_MAX_AGE, + ATH6KL_PS_QUEUE_MAX_DEPTH); + ath6kl_ps_queue_init(&vif->sta_list[ctr].psq_mgmt, + PS_QUEUE_TYPE_STA_MGMT, + ATH6KL_PS_QUEUE_MAX_AGE, + ATH6KL_PS_QUEUE_NO_DEPTH); + vif->sta_list[ctr].aggr_conn_cntxt = NULL; + } + + spin_lock_init(&vif->psq_mcast_lock); + ath6kl_ps_queue_init(&vif->psq_mcast, + PS_QUEUE_TYPE_AP_MULTICAST, + ATH6KL_PS_QUEUE_NO_AGE, + ATH6KL_PS_QUEUE_NO_DEPTH); + + memcpy(vif->ap_country_code, DEF_AP_COUNTRY_CODE, 3); + + /* + * For CE release, + * 1. Disable auto-reconnect. + * 2. Disable roaming/low rssi scan. + * 3. Enlarge channel dewell-time in active channel. + */ + memset(&vif->sc_params, 0, sizeof(vif->sc_params)); + vif->sc_params.short_scan_ratio = 3; + if (ar->roam_mode == ATH6KL_MODULEROAM_DISABLE || + (vif->wdev.iftype != NL80211_IFTYPE_STATION)) { + vif->sc_params.scan_ctrl_flags = (CONNECT_SCAN_CTRL_FLAGS | + SCAN_CONNECTED_CTRL_FLAGS | + ACTIVE_SCAN_CTRL_FLAGS); + } else { + vif->sc_params.fg_start_period = 1; + vif->sc_params.scan_ctrl_flags = (CONNECT_SCAN_CTRL_FLAGS | + SCAN_CONNECTED_CTRL_FLAGS | + ACTIVE_SCAN_CTRL_FLAGS | + ENABLE_AUTO_CTRL_FLAGS); + } + + if ((ar->hif_type == ATH6KL_HIF_TYPE_USB) && + (ar->p2p_compat) && + (vif->fw_vif_idx)) { + vif->sc_params.scan_ctrl_flags &= ~CONNECT_SCAN_CTRL_FLAGS; + ath6kl_info("Disable connect-scan, vif_idx = %d\n", + vif->fw_vif_idx); + } + + if (ar->roam_mode != ATH6KL_MODULEROAM_DISABLE) + vif->sc_params.scan_ctrl_flags |= ROAM_SCAN_CTRL_FLAGS; + +#ifdef CE_SUPPORT + /* avoid association reject by AP not found */ + vif->sc_params.maxact_chdwell_time = 60; + vif->sc_params.maxact_scan_per_ssid = 2; +#else + vif->sc_params.maxact_chdwell_time = (2 * ATH6KL_SCAN_ACT_DEWELL_TIME); +#endif + + if (!(ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) + vif->sc_params.pas_chdwell_time = + ATH6KL_SCAN_PAS_DEWELL_TIME_WITHOUT_ROAM; + + /* + * restore the initial scan parameters for usage + * Note that debugfs may revise sc_params_default as well + */ + memcpy(&vif->sc_params_default, &vif->sc_params, + sizeof(struct wmi_scan_params_cmd)); +} + +/* + * Set HTC/Mbox operational parameters, this can only be called when the + * target is in the BMI phase. + */ +static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, + u8 htc_ctrl_buf) +{ + int status; + u32 blk_size; + + blk_size = ar->mbox_info.block_size; + + if (htc_ctrl_buf) + blk_size |= ((u32)htc_ctrl_buf) << 16; + + /* set the host interest area for the block size */ + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_mbox_io_block_sz)), + (u8 *)&blk_size, + 4); + if (status) { + ath6kl_err("bmi_write_memory for IO block size failed\n"); + goto out; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, "block size set: %d (target addr:0x%X)\n", + blk_size, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz))); + + if (mbox_isr_yield_val) { + /* set the host interest area for the mbox ISR yield limit */ + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_mbox_isr_yield_limit)), + (u8 *)&mbox_isr_yield_val, + 4); + if (status) { + ath6kl_err("bmi_write_memory for yield limit failed\n"); + goto out; + } + } + +out: + return status; +} + +static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) +{ + int status = 0; + int ret; + + /* + * Configure the device for rx dot11 header rules. "0,0" are the + * default values. Required if checksum offload is needed. Set + * RxMetaVersion to 2. + */ + if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx, + ar->rx_meta_ver, 0, 0)) { + ath6kl_err("unable to set the rx frame format\n"); + status = -EIO; + } + + if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) { + if ((ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1, + IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN)) != 0) { + ath6kl_err("unable to set power save " + "fail event policy\n"); + status = -EIO; + } + } else { + if ((ath6kl_wmi_pmparams_cmd( + ar->wmi, idx, 0, 3, 3, 1, 1, 0)) != 0) { + ath6kl_err("unable to set power save params\n"); + status = -EIO; + } + } + + if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) + if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0, + WMI_DONOT_IGNORE_BARKER_IN_ERP)) != 0) { + ath6kl_err("unable to set barker preamble policy\n"); + status = -EIO; + } + + if (idx < ar->max_norm_iface && + ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx, + WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) { + ath6kl_err("unable to set keep alive interval\n"); + status = -EIO; + } + + if (ath6kl_wmi_disctimeout_cmd(ar->wmi, idx, + WLAN_CONFIG_DISCONNECT_TIMEOUT)) { + ath6kl_err("unable to set disconnect timeout\n"); + status = -EIO; + } + + if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) + if (ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED)) { + ath6kl_err("unable to set txop bursting\n"); + status = -EIO; + } + + if (ar->p2p && (ar->vif_max == 1 || + (idx >= ar->max_norm_iface) || + !(ar->p2p_dedicate))) { + ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx, + P2P_FLAG_CAPABILITIES_REQ | + P2P_FLAG_MACADDR_REQ | + P2P_FLAG_HMODEL_REQ); + if (ret) { + ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P " + "capabilities (%d) - assuming P2P not " + "supported\n", ret); + ar->p2p = 0; + } + } + + if (ar->p2p && (ar->vif_max == 1 || + (idx >= ar->max_norm_iface) || + !(ar->p2p_dedicate))) { + /* Enable Probe Request reporting for P2P */ + ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true); + if (ret) { + ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe " + "Request reporting (%d)\n", ret); + } +#ifdef CE_SUPPORT + ret = ath6kl_wmi_probe_resp_report_req_cmd(ar->wmi, idx, true); + if (ret) { + printk(KERN_DEBUG "ath6l: Failed to enable Probe Resp " + "reporting (%d)\n", ret); + } + + if (idx < 2) { + /* set max connected stas */ + ret = ath6kl_wmi_set_ap_num_sta_cmd(ar->wmi, idx, 8); + if (ret) { + printk(KERN_DEBUG "ath6l: Failed to set max connected sta " + "(%d)\n", ret); + } + } +#endif + } +#ifdef CE_SUPPORT +/* clear bssfilter */ +{ + ath6kl_wmi_bssfilter_cmd(ar->wmi, idx, + NONE_BSS_FILTER, 0); +} +#endif + + if ((ar->target_subtype & TARGET_SUBTYPE_HT40) && + (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_2G_HT40))) { + /* + * Don't allow HT40 in 2.4Ghz for P2P related inerfaces + * in current design. + */ + if ((((ar->vif_max == 1) && (!ar->p2p)) || + (ar->vif_max > 1)) && + (idx < ar->max_norm_iface)) { + if (ath6kl_wmi_set_ht_cap_cmd(ar->wmi, + idx, + A_BAND_24GHZ, + ATH6KL_24GHZ_HT40_DEF_WIDTH, + ATH6KL_24GHZ_HT40_DEF_SGI, + ATH6KL_24GHZ_HT40_DEF_INTOLR40)) { + ath6kl_err("unable to set HT CAP\n"); + status = -EIO; + } + } + } + + return status; +} + +int ath6kl_configure_target(struct ath6kl *ar) +{ + u32 param, ram_reserved_size; + u8 fw_iftype, fw_mode = 0, fw_submode = 0; + int i; + int status; + + /* + * Note: Even though the firmware interface type is + * chosen as BSS_STA for all three interfaces, can + * be configured to IBSS/AP as long as the fw submode + * remains normal mode (0 - AP, STA and IBSS). But + * due to an target assert in firmware only one interface is + * configured for now. + */ + fw_iftype = HI_OPTION_FW_MODE_BSS_STA; + + for (i = 0; i < ar->vif_max; i++) + fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); + + /* + * p2p_concurrent & p2p_dedicate, submodes : + * vif[0] - AP/STA/IBSS + * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" + * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" + * vif[3] - "P2P dev"/"P2P GO"/"P2P Client" (if VAP == 4) + * + * p2p_concurrent_ap & p2p_dedicate, submodes : + * vif[0] - AP/STA/IBSS + * vif[1] - AP/STA/IBSS + * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" + * vif[3] - "P2P dev"/"P2P GO"/"P2P Client" (if VAP == 4) + * + * !p2p_dedicate, submodes: + * vif[0] - "AP/STA/IBSS/P2P dev" + * vif[1]- "P2P dev"/"P2P GO"/"P2P Client" + */ + + for (i = 0; i < ar->max_norm_iface; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_NONE << + (i * HI_OPTION_FW_SUBMODE_BITS); + + if (ar->p2p_dedicate) { + for (i = ar->max_norm_iface; i < ar->vif_max; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << + (i * HI_OPTION_FW_SUBMODE_BITS); + } else if (ar->p2p) { + for (i = 0; i < ar->vif_max; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << + (i * HI_OPTION_FW_SUBMODE_BITS); + } + +#ifdef ATH6KL_DIAGNOSTIC + param = 115200; + if (ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_desired_baud_rate)), + (u8 *)¶m, 4) != 0) { + ath6kl_err("%s: failed to set hprx traffic ratio in target\n", + __func__); + return -EIO; + } + + /* Number of buffers used on the target for logging packets; use + * zero to disable logging */ + if ((ar->hif_type == ATH6KL_HIF_TYPE_USB) + && (ar->version.target_ver != AR6004_HW_3_0_VERSION)) + param = 0; + else if (ar->hif_type == ATH6KL_HIF_TYPE_USB) + param = 2; + else /* sdio */ + param = 3; + + if (ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_pktlog_num_buffers)), + (u8 *)¶m, 4) != 0) { + ath6kl_err("%s: failed to set pktlog buffers in target\n", + __func__); + return -EIO; + } + +#endif + + param = HTC_PROTOCOL_VERSION; + if (ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_app_host_interest)), + (u8 *)¶m, 4) != 0) { + ath6kl_err("bmi_write_memory for htc version failed\n"); + return -EIO; + } + + /* start configuring the hi_option_flag2 */ + param = 0; + if (ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag2)), + (u8 *)¶m, 4) != 0) { + ath6kl_err("bmi_read_memory for hi_option_flag2 failed\n"); + return -EIO; + } + + /* Check if we shall disable p2p dedicate mode in firmware */ + if (ar->p2p_concurrent && !ar->p2p_dedicate) + param |= HI_OPTION_DISABLE_P2P_DEDICATE; + +#ifndef CONFIG_ANDROID + if (ar->version.target_ver == AR6004_HW_1_3_VERSION) + param |= HI_OPTION_DISABLE_RTT; +#endif + +#ifdef ATH6KL_SUPPORT_WLAN_HB + /* set WLAN HB mode */ + param |= HI_OPTION_ENABLE_WLAN_HB; +#endif + + /* Set firmware to support MCC */ + if (ar->p2p_concurrent) + param |= HI_OPTION_MCC_ENABLE ; + + /* set one shot noa enable to firmware */ + param |= HI_OPTION_ONE_SHOT_NOA_ENABLE ; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "Set hi_option_flag2 to 0x%08x\n", + param); + if (ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag2)), + (u8 *)¶m, 4) != 0) { + ath6kl_err("bmi_write_memory for hi_option_flag2 flag failed\n"); + return -EIO; + }; + /* end configuring hi_option_flag2 */ + + + /* set the firmware mode to STA/IBSS/AP */ + param = 0; + if (ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag)), + (u8 *)¶m, 4) != 0) { + ath6kl_err("bmi_read_memory for setting fwmode failed\n"); + return -EIO; + } + + param |= (ar->vif_max << HI_OPTION_NUM_DEV_SHIFT); + param |= fw_mode << HI_OPTION_FW_MODE_SHIFT; + param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT; + + param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); + param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); + + if (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_ENABLE_FWLOG)) + param |= HI_OPTION_DISABLE_DBGLOG; + + if (ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag)), + (u8 *)¶m, + 4) != 0) { + ath6kl_err("bmi_write_memory for setting fwmode failed\n"); + return -EIO; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, "firmware mode set\n"); + + /* + * Hardcode the address use for the extended board data + * Ideally this should be pre-allocate by the OS at boot time + * But since it is a new feature and board data is loaded + * at init time, we have to workaround this from host. + * It is difficult to patch the firmware boot code, + * but possible in theory. + */ + param = ar->hw.board_ext_data_addr; + ram_reserved_size = ar->hw.reserved_ram_size; + + if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_ext_data)), + (u8 *)¶m, 4) != 0) { + ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); + return -EIO; + } + + if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_end_ram_reserve_sz)), + (u8 *)&ram_reserved_size, 4) != 0) { + ath6kl_err("bmi_write_memory for " + "hi_end_ram_reserve_sz failed\n"); + return -EIO; + } + + /* enable serial console prints */ + param = 1; + status = ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_serial_enable)), (u8 *)¶m, 4); + if (status) + return status; + ath6kl_dbg(ATH6KL_DBG_BOOT, "serial console prints enabled\n"); + + /* set the block size for the target */ + if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0)) + /* use default number of control buffers */ + return -EIO; + + return 0; +} + +void ath6kl_core_free(struct ath6kl *ar) +{ + wiphy_free(ar->wiphy); +} + +void ath6kl_core_cleanup(struct ath6kl *ar) +{ + ath6kl_hif_power_off(ar); + + destroy_workqueue(ar->ath6kl_wq); + + if (ar->p2p_flowctrl_ctx) + ath6kl_p2p_flowctrl_conn_list_cleanup(ar); + + if (ar->htc_target) + ath6kl_htc_cleanup(ar->htc_target); + + ath6kl_cookie_cleanup(ar); + + ath6kl_cleanup_amsdu_rxbufs(ar); + + ath6kl_bmi_cleanup(ar); + + ath6kl_debug_cleanup(ar); + + ath6kl_p2p_flowctrl_conn_list_deinit(ar); + + ath6kl_p2p_rc_deinit(ar); + + ath6kl_reg_deinit(ar); + + kfree(ar->fw_board); + kfree(ar->fw_otp); + kfree(ar->fw); + kfree(ar->fw_ext); + kfree(ar->fw_patch); + kfree(ar->fw_testscript); + kfree(ar->fw_softmac); + + ath6kl_deinit_ieee80211_hw(ar); + + rttm_free(); +} + +/* firmware upload */ +#ifdef CE_SUPPORT +#include +#include +#include +#include +#include +#include +#include +#include + +static mm_segment_t oldfs; + +static struct file *openFile(char *path, int flag, int mode) +{ + struct file *fp; + fp = filp_open(path, flag, 0); + if (IS_ERR(fp)) + return NULL; + return fp; +} + +static int readFile(struct file *fp, char *buf, int readlen) +{ + if (fp->f_op && fp->f_op->read) + return fp->f_op->read(fp, buf, readlen, &fp->f_pos); + else + return -1; +} + +static int closeFile(struct file *fp) +{ + filp_close(fp, NULL); + return 0; +} + +static void initKernelEnv(void) +{ + oldfs = get_fs(); + set_fs(KERNEL_DS); +} +#endif + +static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, + u8 **fw, size_t *fw_len) +{ +#ifdef CE_SUPPORT +#define MAX_BUFFER_SIZE 128 + struct file *fp = NULL; + int ret; + char buf[MAX_BUFFER_SIZE]; + char full_patch[90]; + int total_len = 0, temp_len = 0; + u8 *temp_buf; + int status = -1; + initKernelEnv(); + if (filename[0] == '/') { + /* assign directory */ + sprintf(full_patch, "%s", filename); + ath6kl_info("%s\n\r", full_patch); + } else { + sprintf(full_patch, "/lib/firmware/%s", filename); + ath6kl_info("%s\n\r", full_patch); + } + fp = openFile(full_patch, O_RDONLY, 0); + if (fp != NULL) { + memset(buf, 0, MAX_BUFFER_SIZE); + ret = readFile(fp, buf, MAX_BUFFER_SIZE); + if (ret > 0) { + total_len = ret; + while ((ret = readFile(fp, buf, MAX_BUFFER_SIZE)) > 0) + total_len += ret; + ath6kl_info("total_len=%d\n", total_len); + } else + ath6kl_err("read file error %d\n", ret); + closeFile(fp); + } else + goto fail; + + fp = openFile(full_patch, O_RDONLY, 0); + if (fp != NULL) { + temp_buf = kmalloc(total_len, GFP_KERNEL); + if (temp_buf != NULL) { + memset(buf, 0, MAX_BUFFER_SIZE); + ret = readFile(fp, buf, MAX_BUFFER_SIZE); + if (ret > 0) { + memcpy(&temp_buf[temp_len], buf, ret); + temp_len = ret; + while ((ret = readFile(fp, buf, + MAX_BUFFER_SIZE)) > 0) { + memcpy(&temp_buf[temp_len], buf, ret); + temp_len += ret; + } + status = 0; + } else + ath6kl_err("read file error %d\n", ret); + } + closeFile(fp); + } else + goto fail; + if (status == 0) { + if (temp_buf != NULL) { + *fw_len = total_len; + *fw = kmemdup(temp_buf, total_len, GFP_KERNEL); + if (*fw == NULL) + status = -ENOMEM; + } + } + if (temp_buf != NULL) + kfree(temp_buf); +fail: + set_fs(oldfs); +#undef MAX_BUFFER_SIZE + return status; +#else + const struct firmware *fw_entry; + int ret; + +pr_err("%s %d %s!!!!!!!!!!!!!!!!\n",__func__,__LINE__,filename); +return 0; + ret = request_firmware(&fw_entry, filename, ar->dev); + if (ret) + return ret; + + *fw_len = fw_entry->size; + *fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); + + if (*fw == NULL) + ret = -ENOMEM; + + release_firmware(fw_entry); + + return ret; +#endif +} + +#ifdef CONFIG_OF +/* + * Check the device tree for a board-id and use it to construct + * the pathname to the firmware file. Used (for now) to find a + * fallback to the "bdata.bin" file--typically a symlink to the + * appropriate board-specific file. + */ +static bool check_device_tree(struct ath6kl *ar) +{ + static const char *board_id_prop = "atheros,board-id"; + struct device_node *node; + char board_filename[64]; + const char *board_id; + int ret; + + for_each_compatible_node(node, NULL, "atheros,ath6kl") { + board_id = of_get_property(node, board_id_prop, NULL); + if (board_id == NULL) { + ath6kl_warn("No \"%s\" property on %s node.\n", + board_id_prop, node->name); + continue; + } + snprintf(board_filename, sizeof(board_filename), + "%s/bdata.%s.bin", ar->hw.fw.dir, board_id); + +// ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board, +// &ar->fw_board_len); + ar->fw_board_len = BDATA_LEN; + ar->fw_board = kmemdup(BDATA, BDATA_LEN, GFP_KERNEL); + if (ar->fw_board == NULL) ret = -ENOMEM; + else ret = 0; + + if (ret) { + ath6kl_err("Failed to get DT board file %s: %d\n", + board_filename, ret); + continue; + } + return true; + } + return false; +} +#else +static bool check_device_tree(struct ath6kl *ar) +{ + return false; +} +#endif /* CONFIG_OF */ + +static void ath6kl_replace_with_softmac(struct ath6kl *ar) +{ + int i, ret; + u16 *p; + u32 sum = 0; + u32 param; + + if (ar->fw_board == NULL || ar->fw_softmac == NULL) + return; + + /* set checksum filed in the board data to zero */ + ar->fw_board[BDATA_CHECKSUM_OFFSET] = 0; + ar->fw_board[BDATA_CHECKSUM_OFFSET+1] = 0; + + /* replace the mac address with softmac */ + memcpy(&ar->fw_board[BDATA_MAC_ADDR_OFFSET], ar->fw_softmac, ETH_ALEN); + + p = (u16 *) ar->fw_board; + + /* calculate check sum */ + for (i = 0; i < (ar->fw_board_len / 2); i++) + sum ^= *p++; + + sum = ~sum; + + ar->fw_board[BDATA_CHECKSUM_OFFSET] = (sum & 0xff); + ar->fw_board[BDATA_CHECKSUM_OFFSET+1] = ((sum >> 8) & 0xff); + + ret = ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag2)), + (u8 *) ¶m, 4); + + if (ret) { + ath6kl_err("failed to do bmi read %d\n", ret); + return; + } + + param |= HI_OPTION_DISABLE_MAC_OTP; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag2)), + (u8 *)¶m, 4); + +} + +static int ath6kl_replace_with_module_param(struct ath6kl *ar, char *str_mac) +{ + int i, ret; + u16 *p; + u32 sum = 0; + u32 param; + u8 macaddr[ETH_ALEN] = {0,}; + + if (ar->fw_board == NULL || str_mac == NULL) + return -EINVAL; + + if (_string_to_mac(str_mac, strlen(str_mac), macaddr) < 0) + return -EINVAL; + + /* set checksum filed in the board data to zero */ + ar->fw_board[BDATA_CHECKSUM_OFFSET] = 0; + ar->fw_board[BDATA_CHECKSUM_OFFSET+1] = 0; + + /* replace the mac address with module parameter input */ + memcpy(&ar->fw_board[BDATA_MAC_ADDR_OFFSET], macaddr, ETH_ALEN); + + p = (u16 *) ar->fw_board; + + /* calculate check sum */ + for (i = 0; i < (ar->fw_board_len / 2); i++) + sum ^= *p++; + + + sum = ~sum; + + ar->fw_board[BDATA_CHECKSUM_OFFSET] = (sum & 0xff); + ar->fw_board[BDATA_CHECKSUM_OFFSET+1] = ((sum >> 8) & 0xff); + + ret = ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag2)), + (u8 *) ¶m, 4); + + if (ret) { + ath6kl_err("failed to do bmi read %d\n", ret); + return ret; + } + + param |= HI_OPTION_DISABLE_MAC_OTP; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_option_flag2)), + (u8 *)¶m, 4); + + return 0; +} + +static int ath6kl_fetch_board_file(struct ath6kl *ar) +{ + const char *filename; + int ret; + + if (ar->fw_board != NULL) + return 0; + + if (WARN_ON(ar->hw.fw_board == NULL)) + return -EINVAL; + +#ifdef CE_SUPPORT + if (bdatapath != NULL) + filename = bdatapath; + else + filename = ar->hw.fw_board; +#else + filename = ar->hw.fw_board; +#endif + +// ret = ath6kl_get_fw(ar, filename, &ar->fw_board, +// &ar->fw_board_len); + ar->fw_board_len = BDATA_LEN; + ar->fw_board = kmemdup(BDATA, BDATA_LEN, GFP_KERNEL); + if (ar->fw_board == NULL) ret = -ENOMEM; + else ret = 0; + if (ret == 0) { + /*if valid MAC from module_param, then use it */ + if (ath6kl_replace_with_module_param(ar, ath6kl_wifi_mac) == 0) + return 0; + + ret = ath6kl_get_fw(ar, ar->hw.fw_softmac, &ar->fw_softmac, + &ar->fw_softmac_len); + + /* softmac bin file exists */ + if (ret == 0) + ath6kl_replace_with_softmac(ar); + + /* managed to get proper board file */ + return 0; + } + + if (check_device_tree(ar)) { + /* got board file from device tree */ + return 0; + } + + /* there was no proper board file, try to use default instead */ + ath6kl_warn("Failed to get board file %s (%d), " + "trying to find default board file.\n", + filename, ret); + + filename = ar->hw.fw_default_board; + +// ret = ath6kl_get_fw(ar, filename, &ar->fw_board, +// &ar->fw_board_len); + ar->fw_board_len = BDATA_LEN; + ar->fw_board = kmemdup(BDATA, BDATA_LEN, GFP_KERNEL); + if (ar->fw_board == NULL) ret = -ENOMEM; + else ret = 0; + if (ret) { + ath6kl_err("Failed to get default board file %s: %d\n", + filename, ret); + return ret; + } + + ath6kl_warn("WARNING! No proper board file was not found, " + "instead using a default board file.\n"); + ath6kl_warn("Most likely your hardware won't work as specified. " + "Install correct board file!\n"); + + return 0; +} + +static int ath6kl_fetch_otp_file(struct ath6kl *ar) +{ + char filename[100]; + int ret; + + if ((ar->target_type == TARGET_TYPE_AR6004) && (ar->testmode == 0)) + return 0; + + if (ar->fw_otp != NULL) + return 0; + + if (ar->hw.fw.otp == NULL) { + ath6kl_dbg(ATH6KL_DBG_BOOT, + "no OTP file configured for this hw\n"); + return 0; + } + + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.otp); + + ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, + &ar->fw_otp_len); + if (ret) { + ath6kl_err("Failed to get OTP file %s: %d\n", + filename, ret); + return ret; + } + + return 0; +} + +static int ath6kl_fetch_testmode_file(struct ath6kl *ar) +{ + char filename[100]; + int ret; + + if (ar->testmode == 0) + return 0; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "testmode %d\n", ar->testmode); + + if (ar->testmode == 2) { + if (ar->hw.fw.utf == NULL) { + ath6kl_warn("testmode 2 not supported\n"); + return -EOPNOTSUPP; + } + + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.utf); + } else { + if (ar->hw.fw.tcmd == NULL) { + ath6kl_warn("testmode 1 not supported\n"); + return -EOPNOTSUPP; + } + + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.tcmd); + } + + set_bit(TESTMODE, &ar->flag); + +// ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); + ar->fw_len = FW_RAM_BIN_LEN; + ar->fw = kmemdup(FW_RAM_BIN, FW_RAM_BIN_LEN, GFP_KERNEL); + if (ar->fw == NULL) ret = -ENOMEM; + else ret = 0; + if (ret) { + ath6kl_err("Failed to get testmode %d firmware file %s: %d\n", + ar->testmode, filename, ret); + return ret; + } + + return 0; +} + +static int ath6kl_fetch_fw_file(struct ath6kl *ar) +{ + char filename[100]; + int ret; + + if (ar->fw != NULL) + return 0; + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_ENABLE_EPPING)) { + if (ar->hw.fw_epping == NULL) { + ath6kl_warn("testmode-epping not supported\n"); + return -EOPNOTSUPP; + } + + snprintf(filename, sizeof(filename), "%s", + ar->hw.fw_epping); + + set_bit(TESTMODE_EPPING, &ar->flag); + + goto get_fw; + } + + /* FIXME: remove WARN_ON() as we won't support FW API 1 for long */ + if (WARN_ON(ar->hw.fw.fw == NULL)) + return -EINVAL; + +#ifdef CE_SUPPORT + if (fwdatapath != NULL) + strcpy(filename, fwdatapath); + else + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.fw); +#else + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.fw); +#endif + +get_fw: +// ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); + ar->fw_len = FW_RAM_BIN_LEN; + ar->fw = kmemdup(FW_RAM_BIN, FW_RAM_BIN_LEN, GFP_KERNEL); + if (ar->fw == NULL) ret = -ENOMEM; + else ret = 0; + if (ret) { + ath6kl_err("Failed to get firmware file %s: %d\n", + filename, ret); + return ret; + } + + if (ar->version.target_ver == AR6004_HW_1_3_VERSION && + ar->fw_len > AR6004_MAX_64K_FW_SIZE) { + ar->hw.flags &= ~ATH6KL_HW_FIRMWARE_EXT_SUPPORT; + } + + if (ar->hw.flags & ATH6KL_HW_FIRMWARE_EXT_SUPPORT) { + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.fw_ext); + ret = ath6kl_get_fw(ar, filename, &ar->fw_ext, &ar->fw_ext_len); + if (ret) { + /*ath6kl_err("Failed to get firmware ext file %s: %d\n", + filename, ret); + return ret; + */ + ath6kl_err("Disable firmware ext feature\n"); + goto get_fw_done; + } + + set_bit(DOWNLOAD_FIRMWARE_EXT, &ar->flag); + } + +get_fw_done: + return 0; +} + +static int ath6kl_fetch_patch_file(struct ath6kl *ar) +{ + char filename[100]; + int ret; + + if (ar->fw_patch != NULL) + return 0; + + if (ar->hw.fw.patch == NULL) + return 0; + + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.patch); + + ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, + &ar->fw_patch_len); + if (ret) { + ath6kl_err("Failed to get patch file %s: %d\n", + filename, ret); + return ret; + } + + return 0; +} + +static int ath6kl_fetch_testscript_file(struct ath6kl *ar) +{ + char filename[100]; + int ret; + + if (ar->testmode != 2) + return 0; + + if (ar->fw_testscript != NULL) + return 0; + + if (ar->hw.fw.testscript == NULL) + return 0; + + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.testscript); + + ret = ath6kl_get_fw(ar, filename, &ar->fw_testscript, + &ar->fw_testscript_len); + if (ret) { + ath6kl_err("Failed to get testscript file %s: %d\n", + filename, ret); + return ret; + } + + return 0; +} + +static int ath6kl_fetch_fw_api1(struct ath6kl *ar) +{ + int ret; + + ret = ath6kl_fetch_otp_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_fw_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_patch_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_testscript_file(ar); + if (ret) + return ret; + + return 0; +} + +#ifdef CONFIG_ATH6KL_ENABLE_FW_API2 +static int ath6kl_fetch_fw_api2(struct ath6kl *ar) +{ + size_t magic_len, len, ie_len; + const struct firmware *fw; + struct ath6kl_fw_ie *hdr; + char filename[100]; + const u8 *data; + int ret, ie_id, i, index, bit; + __le32 *val; + + if (ar->hw.fw.api2 == NULL) + return -EOPNOTSUPP; + + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw.fw.dir, ar->hw.fw.api2); + + ret = request_firmware(&fw, filename, ar->dev); + if (ret) + return ret; + + data = fw->data; + len = fw->size; + + /* magic also includes the null byte, check that as well */ + magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1; + + if (len < magic_len) { + ret = -EINVAL; + goto out; + } + + if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) { + ret = -EINVAL; + goto out; + } + + len -= magic_len; + data += magic_len; + + /* loop elements */ + while (len > sizeof(struct ath6kl_fw_ie)) { + /* hdr is unaligned! */ + hdr = (struct ath6kl_fw_ie *) data; + + ie_id = le32_to_cpup(&hdr->id); + ie_len = le32_to_cpup(&hdr->len); + + len -= sizeof(*hdr); + data += sizeof(*hdr); + + if (len < ie_len) { + ret = -EINVAL; + goto out; + } + + switch (ie_id) { + case ATH6KL_FW_IE_OTP_IMAGE: + ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie " + "(%zd B)\n", ie_len); + + ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); + + if (ar->fw_otp == NULL) { + ret = -ENOMEM; + goto out; + } + + ar->fw_otp_len = ie_len; + break; + case ATH6KL_FW_IE_FW_IMAGE: + ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie " + "(%zd B)\n", ie_len); + + /* in testmode we already might have a fw file */ + if (ar->fw != NULL) + break; + + ar->fw = kmemdup(data, ie_len, GFP_KERNEL); + + if (ar->fw == NULL) { + ret = -ENOMEM; + goto out; + } + + ar->fw_len = ie_len; + break; + case ATH6KL_FW_IE_PATCH_IMAGE: + ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image " + "ie (%zd B)\n", ie_len); + + ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); + + if (ar->fw_patch == NULL) { + ret = -ENOMEM; + goto out; + } + + ar->fw_patch_len = ie_len; + break; + case ATH6KL_FW_IE_RESERVED_RAM_SIZE: + val = (__le32 *) data; + ar->hw.reserved_ram_size = le32_to_cpup(val); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found reserved ram size ie 0x%d\n", + ar->hw.reserved_ram_size); + break; + case ATH6KL_FW_IE_CAPABILITIES: + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found firmware capabilities ie (%zd B)\n", + ie_len); + + for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { + index = ALIGN(i, 8) / 8; + bit = i % 8; + + if (data[index] & (1 << bit)) + __set_bit(i, ar->fw_capabilities); + } + + ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "", + ar->fw_capabilities, + sizeof(ar->fw_capabilities)); + break; + case ATH6KL_FW_IE_PATCH_ADDR: + if (ie_len != sizeof(*val)) + break; + + val = (__le32 *) data; + ar->hw.dataset_patch_addr = le32_to_cpup(val); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found patch address ie 0x%x\n", + ar->hw.dataset_patch_addr); + break; + case ATH6KL_FW_IE_BOARD_ADDR: + if (ie_len != sizeof(*val)) + break; + + val = (__le32 *) data; + ar->hw.board_addr = le32_to_cpup(val); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found board address ie 0x%x\n", + ar->hw.board_addr); + break; + case ATH6KL_FW_IE_VIF_MAX: + if (ie_len != sizeof(*val)) + break; + + val = (__le32 *) data; + ar->vif_max = min_t(unsigned int, le32_to_cpup(val), + ATH6KL_VIF_MAX); + + if (ar->vif_max > 1 && !ar->p2p) + ar->max_norm_iface = 2; + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found vif max ie %d\n", ar->vif_max); + break; + default: + ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n", + le32_to_cpup(&hdr->id)); + break; + } + + len -= ie_len; + data += ie_len; + }; + + ret = 0; +out: + release_firmware(fw); + + return ret; +} +#endif + +static int ath6kl_fetch_firmwares(struct ath6kl *ar) +{ + int ret; + + ret = ath6kl_fetch_board_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_testmode_file(ar); + if (ret) + return ret; +#ifdef CONFIG_ATH6KL_ENABLE_FW_API2 + ret = ath6kl_fetch_fw_api2(ar); + if (ret == 0) { + ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n"); + return 0; + } +#endif + ret = ath6kl_fetch_fw_api1(ar); + if (ret) + return ret; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n"); + + return 0; +} + +static int ath6kl_upload_board_file(struct ath6kl *ar) +{ + u32 board_address, board_ext_address, param; + u32 board_data_size, board_ext_data_size; + int ret; + + /* + * Determine where in Target RAM to write Board Data. + * For AR6004, host determine Target RAM address for + * writing board data. + */ + if (ar->hw.board_addr != 0) { + board_address = ar->hw.board_addr; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_data)), + (u8 *) &board_address, 4); + } else { + ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_data)), + (u8 *) &board_address, 4); + } + + /* determine where in target ram to write extended board data */ + ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_ext_data)), + (u8 *) &board_ext_address, 4); + + if (ar->target_type == TARGET_TYPE_AR6003 && + board_ext_address == 0) { + ath6kl_err("Failed to get board file target address.\n"); + return -EINVAL; + } + + switch (ar->target_type) { + case TARGET_TYPE_AR6003: + board_data_size = AR6003_BOARD_DATA_SZ; + board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ; + break; + case TARGET_TYPE_AR6004: + board_data_size = AR6004_BOARD_DATA_SZ; + board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ; + break; + case TARGET_TYPE_AR6006: + board_data_size = AR6006_BOARD_DATA_SZ; + board_ext_data_size = AR6006_BOARD_EXT_DATA_SZ; + break; + default: + WARN_ON(1); + return -EINVAL; + break; + } + + if (board_ext_address && + ar->fw_board_len == (board_data_size + board_ext_data_size)) { + + /* write extended board data */ + ath6kl_dbg(ATH6KL_DBG_BOOT, + "writing extended board data to 0x%x (%d B)\n", + board_ext_address, board_ext_data_size); + + ret = ath6kl_bmi_write(ar, board_ext_address, + ar->fw_board + board_data_size, + board_ext_data_size); + if (ret) { + ath6kl_err("Failed to write extended board data: %d\n", + ret); + return ret; + } + + /* record that extended board data is initialized */ + param = (board_ext_data_size << 16) | 1; + + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_ext_data_config)), + (unsigned char *) ¶m, 4); + } + + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n", + board_address, board_data_size); + + ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, + board_data_size); + + if (ret) { + ath6kl_err("Board file bmi write failed: %d\n", ret); + return ret; + } + + /* record the fact that Board Data IS initialized */ + param = board_data_size; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_data_initialized)), + (u8 *)¶m, 4); + + return ret; +} + +static int ath6kl_upload_otp(struct ath6kl *ar) +{ + u32 address, param; + bool from_hw = false; + int ret; + + if (ar->fw_otp == NULL) + return 0; + + address = ar->hw.app_load_addr; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address, + ar->fw_otp_len); + + ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, + ar->fw_otp_len); + if (ret) { + ath6kl_err("Failed to upload OTP file: %d\n", ret); + return ret; + } + + /* read firmware start address */ + ret = ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_app_start)), + (u8 *) &address, sizeof(address)); + + if (ret) { + ath6kl_err("Failed to read hi_app_start: %d\n", ret); + return ret; + } + + if (ar->hw.app_start_override_addr == 0) { + ar->hw.app_start_override_addr = address; + from_hw = true; + } + + ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr%s 0x%x\n", + from_hw ? " (from hw)" : "", + ar->hw.app_start_override_addr); + + /* execute the OTP code */ + ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", + ar->hw.app_start_override_addr); + param = 0; + ath6kl_bmi_execute(ar, ar->hw.app_start_override_addr, ¶m); + + return ret; +} + +static int ath6kl_upload_firmware(struct ath6kl *ar) +{ + u32 address; + int ret; + + if (WARN_ON(ar->fw == NULL)) + return 0; + + address = ar->hw.app_load_addr; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n", + address, ar->fw_len); + + ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); + + if (ret) { + ath6kl_err("Failed to write firmware: %d\n", ret); + return ret; + } + + /* + * Set starting address for firmware + * Don't need to setup app_start override addr on AR6004 + */ + if (ar->target_type != TARGET_TYPE_AR6004 && + ar->target_type != TARGET_TYPE_AR6006) { + address = ar->hw.app_start_override_addr; + ath6kl_bmi_set_app_start(ar, address); + } + return ret; +} + +static int ath6kl_upload_firmware_ext(struct ath6kl *ar) +{ + u32 address; + int ret = -1; + u32 param; + u32 fileSize = 0, sectionAddr = 0, sectionLen = 0, readLen = 0; + u32 i; + u32 value; + + if (WARN_ON(ar->fw_ext == NULL)) + return 0; + + address = ar->hw.app_load_ext_addr; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware ext to 0x%x (%zd B)\n", + address, ar->fw_len); + + fileSize = ar->fw_ext_len; + while (readLen < fileSize) { + sectionAddr = ar->fw_ext[readLen] | + ((ar->fw_ext[readLen+1]<<8)&0x0000ff00) | + ((ar->fw_ext[readLen+2]<<16)&0x00ff0000) | + ((ar->fw_ext[readLen+3]<<24)&0xff000000); + sectionLen = ar->fw_ext[readLen+4] | + ((ar->fw_ext[readLen+5]<<8)&0x0000ff00) | + ((ar->fw_ext[readLen+6]<<16)&0x00ff0000) | + ((ar->fw_ext[readLen+7]<<24)&0xff000000); + + for (i = (readLen+8); i < (readLen+8+sectionLen); i += 4) { + value = 0; + if ((readLen+8+sectionLen-i)/4 > 0) { + value = ar->fw_ext[i]| + ((ar->fw_ext[i+1]<<8)&0x0000ff00) | + ((ar->fw_ext[i+2]<<16)&0x00ff0000) | + ((ar->fw_ext[i+3]<<24)&0xff000000); + } else { + switch (readLen+8+sectionLen-i) { + case 1: + value = ar->fw_ext[i]; + break; + case 2: + value = ar->fw_ext[i] | + ((ar->fw_ext[i+1]<<8)&0x0000ff00); + break; + case 3: + value = ar->fw_ext[i] | + ((ar->fw_ext[i+1]<<8)&0x0000ff00) | + ((ar->fw_ext[i+2]<<16)&0x00ff0000); + break; + default: + break; + } + } + + ret = ath6kl_diag_write32(ar, sectionAddr, value); + + if (ret) + break; + + sectionAddr += 4; + } + + if (ret) + break; + + readLen += (sectionLen+8); + } + + if (ret) { + ath6kl_err("Failed to write firmware ext: %d\n", ret); + return ret; + } + + param = 0; + if (ath6kl_diag_read32(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag2)), + (u32 *)¶m) != 0) { + ath6kl_err("read_memory for setting fwmode failed\n"); + return -EIO; + } + + param |= HI_OPTION_EXT_FW_DOWNLOAD_DONE; + if (ath6kl_diag_write32(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag2)), + param) != 0) { + ath6kl_err("write_memory for " + "hi_option_flag2 failed\n"); + return -EIO; + } + + param = 0; + if (ath6kl_diag_read32(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag2)), + (u32 *)¶m) != 0) { + ath6kl_err("read_memory for setting fwmode failed\n"); + return -EIO; + } + + return ret; +} + +static int ath6kl_upload_patch(struct ath6kl *ar) +{ + u32 address, param; + int ret; + + if (ar->fw_patch == NULL) + return 0; + + address = ar->hw.dataset_patch_addr; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n", + address, ar->fw_patch_len); + + ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); + if (ret) { + ath6kl_err("Failed to write patch file: %d\n", ret); + return ret; + } + + param = address; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_dset_list_head)), + (unsigned char *) ¶m, 4); + + return 0; +} + +static int ath6kl_upload_testscript(struct ath6kl *ar) +{ + u32 address, param; + int ret; + + if (ar->testmode != 2) + return 0; + + if (ar->fw_testscript == NULL) + return 0; + + address = ar->hw.testscript_addr; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing testscript to 0x%x (%zd B)\n", + address, ar->fw_testscript_len); + + ret = ath6kl_bmi_write(ar, address, ar->fw_testscript, + ar->fw_testscript_len); + if (ret) { + ath6kl_err("Failed to write testscript file: %d\n", ret); + return ret; + } + + param = address; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_ota_testscript)), + (unsigned char *) ¶m, 4); + + if (ar->target_type == TARGET_TYPE_AR6003) { + param = 4096; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_end_ram_reserve_sz)), + (unsigned char *) ¶m, 4); + } + + param = 1; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_test_apps_related)), + (unsigned char *) ¶m, 4); + + return 0; +} + +static int ath6kl_init_upload(struct ath6kl *ar) +{ + u32 param, options, sleep, address; + int status = 0; + + if (ar->target_type != TARGET_TYPE_AR6003 && + ar->target_type != TARGET_TYPE_AR6004 && + ar->target_type != TARGET_TYPE_AR6006) + return -EINVAL; + + /* temporarily disable system sleep */ + address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; + status = ath6kl_bmi_reg_read(ar, address, ¶m); + if (status) + return status; + + options = param; + + param |= ATH6KL_OPTION_SLEEP_DISABLE; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + + address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; + status = ath6kl_bmi_reg_read(ar, address, ¶m); + if (status) + return status; + + sleep = param; + + param |= SM(SYSTEM_SLEEP_DISABLE, 1); + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + + ath6kl_dbg(ATH6KL_DBG_TRC, "old options: %d, old sleep: %d\n", + options, sleep); + + /* program analog PLL register */ + /* no need to control 40/44MHz clock on AR6004 */ + if (ar->target_type != TARGET_TYPE_AR6004 && + ar->target_type != TARGET_TYPE_AR6006) { + status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, + 0xF9104001); + + if (status) + return status; + + /* Run at 80/88MHz by default */ + param = SM(CPU_CLOCK_STANDARD, 1); + + address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + } + + if (ar->hw.flags & ATH6KL_HW_XTAL_40MHZ) { + param = 40*1000*1000; + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_refclk_hz)), + (u8 *)¶m, 4); + } + + if (ath6kl_mod_debug_quirks(ar, AT6HKL_MODULE_LPL_ENABLE)) { + status = ath6kl_bmi_reg_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_pwr_save_flags)), + ¶m); + + if (status) { + ath6kl_dbg(ATH6KL_DBG_BOOT, + "Unable to read power save flags\n"); + return status; + } + + /* change the byte order */ + param = le32_to_cpu(param); + param |= HI_PWR_SAVE_LPL_ENABLED | + (HI_PWR_SAVE_LPL_MODE_RPL<version.target_ver == AR6003_HW_2_0_VERSION) { + ath6kl_err("temporary war to avoid sdio crc error\n"); + + param = 0x20; + + address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + + address = GPIO_BASE_ADDRESS + GPIO_PIN11_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + + address = GPIO_BASE_ADDRESS + GPIO_PIN12_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + + address = GPIO_BASE_ADDRESS + GPIO_PIN13_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + } + + /* write EEPROM data to Target RAM */ + status = ath6kl_upload_board_file(ar); + if (status) + return status; + + /* transfer One time Programmable data */ + status = ath6kl_upload_otp(ar); + if (status) + return status; + + /* Download Target firmware */ + status = ath6kl_upload_firmware(ar); + if (status) + return status; + + status = ath6kl_upload_patch(ar); + if (status) + return status; + + /* Download the test script */ + status = ath6kl_upload_testscript(ar); + if (status) + return status; + + /* Restore system sleep */ + address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, sleep); + if (status) + return status; + + address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; + if (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULES_ANI_ENABLE) || + ((ar->version.target_ver != AR6004_HW_1_1_VERSION) && + (ar->version.target_ver != AR6004_HW_1_3_VERSION) && + (ar->version.target_ver != AR6004_HW_3_0_VERSION))) { + ath6kl_dbg(ATH6KL_DBG_BOOT, "NO ANI\n"); + param = options | 0x20; + } else { + ath6kl_dbg(ATH6KL_DBG_BOOT, "ANI Enabled\n"); + param = options; + } + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + + /* Configure GPIO AR600x UART */ + if (ar->target_type == TARGET_TYPE_AR6004) + param = CONFIG_AR6004_DEBUG_UART_TX_PIN; + else if (ar->target_type == TARGET_TYPE_AR6006) + param = CONFIG_AR6006_DEBUG_UART_TX_PIN; + else + param = CONFIG_AR600x_DEBUG_UART_TX_PIN; + + status = ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_dbg_uart_txpin)), + (u8 *)¶m, 4); + + return status; +} + +static int ath6kl_init_hw_params(struct ath6kl *ar) +{ + const struct ath6kl_hw *hw; + int i; + + for (i = 0; i < ARRAY_SIZE(hw_list); i++) { + hw = &hw_list[i]; + + if (hw->id == ar->version.target_ver) + break; + } + + if (i == ARRAY_SIZE(hw_list)) { + ath6kl_err("Unsupported hardware version: 0x%x\n", + ar->version.target_ver); + return -EINVAL; + } + + ar->hw = *hw; + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "target_ver 0x%x target_type 0x%x dataset_patch " + "0x%x app_load_addr 0x%x\n", + ar->version.target_ver, ar->target_type, + ar->hw.dataset_patch_addr, ar->hw.app_load_addr); + ath6kl_dbg(ATH6KL_DBG_BOOT, + "app_start_override_addr 0x%x board_ext_data_addr " + "0x%x reserved_ram_size 0x%x", + ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr, + ar->hw.reserved_ram_size); + + return 0; +} + +static int ath6kl_change_hw_params(struct ath6kl *ar) +{ + const struct ath6kl_hw *hw; + int i; + + /* Currently, we only apply hw params change for AR6004 1.3 */ + if (ar->version.target_ver != AR6004_HW_1_3_VERSION || + ar->fw_len < AR6004_MAX_64K_FW_SIZE) { + return 0; + } + + for (i = 0; i < ARRAY_SIZE(hw_list) - 1; i++) { + hw = &hw_list[i]; + + if (hw->id == ar->version.target_ver) + break; + } + + if (i == ARRAY_SIZE(hw_list) - 1) { + ath6kl_err("Unsupported hardware version: 0x%x\n", + ar->version.target_ver); + return -EINVAL; + } + + ar->hw = *(hw+1); + + return 0; +} + +static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type) +{ + switch (type) { + case ATH6KL_HIF_TYPE_SDIO: + return "sdio"; + case ATH6KL_HIF_TYPE_USB: + return "usb"; + } + + return NULL; +} + +static int __ath6kl_init_hw_start(struct ath6kl *ar) +{ + long timeleft; + int ret, i; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "hw start\n"); + + if (recovery_enable_mode == ATH6KL_RECOVERY_MODE_COLD) { + + ath6kl_info("Firmware recovery mode cold\n"); + + ret = ath6kl_fw_watchdog_enable(ar); + if (ret != 0) { + ath6kl_err("Failed enable fw watchdog%d\n", + ret); + return ret; + } + + ret = ath6kl_fw_crash_cold_reset_enable(ar); + if (ret != 0) { + ath6kl_err("Failed enable fw code reset %d\n", + ret); + return ret; + } + } + + if ((ar->hif_type == ATH6KL_HIF_TYPE_USB) && + ath6kl_hif_bus_config(ar)) { + set_bit(USB_REMOTE_WKUP, &ar->flag); + } else { + clear_bit(USB_REMOTE_WKUP, &ar->flag); + } + + ret = ath6kl_hif_power_on(ar); + if (ret) + return ret; + + ret = ath6kl_change_hw_params(ar); + if (ret) { + ath6kl_dbg(ATH6KL_DBG_BOOT, "change hw params failed\n"); + goto err_power_off; + } + + ret = ath6kl_configure_target(ar); + if (ret) + goto err_power_off; + + ret = ath6kl_init_upload(ar); + if (ret) + goto err_power_off; + +#ifdef CONFIG_QC_INTERNAL + if (reg_domain != 0xffff) { + ret = ath6kl_set_rd(ar); + if (ret) + goto err_power_off; + } +#endif + + /* Do we need to finish the BMI phase */ + /* FIXME: return error from ath6kl_bmi_done() */ + if (ath6kl_bmi_done(ar)) { + ret = -EIO; + goto err_power_off; + } + + /* + * The reason we have to wait for the target here is that the + * driver layer has to init BMI in order to set the host block + * size. + */ + if (ath6kl_htc_wait_target(ar->htc_target)) { + ret = -EIO; + goto err_power_off; + } + + if (ath6kl_init_service_ep(ar)) { + ret = -EIO; + goto err_cleanup_scatter; + } + + /* setup credit distribution */ + ath6kl_htc_credit_setup(ar->htc_target, &ar->credit_state_info); + + /* start HTC */ + ret = ath6kl_htc_start(ar->htc_target); + if (ret) { + /* FIXME: call this */ + ath6kl_cookie_cleanup(ar); + goto err_cleanup_scatter; + } + + if (!test_bit(TESTMODE_EPPING, &ar->flag)) { + /* Wait for Wmi event to be ready */ + timeleft = wait_event_interruptible_timeout(ar->event_wq, + test_bit(WMI_READY, + &ar->flag), + WMI_TIMEOUT); + + ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); + + + if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { + ath6kl_info("%s %s fw %s%s\n", + ar->hw.name, + ath6kl_init_get_hif_name(ar->hif_type), + ar->wiphy->fw_version, + test_bit(TESTMODE, &ar->flag) ? + " testmode_tcmd" : ""); + } + + if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { + ath6kl_err("abi version mismatch: " + "host(0x%x), target(0x%x)\n", + ATH6KL_ABI_VERSION, ar->version.abi_ver); + ret = -EIO; + goto err_htc_stop; + } + + if (!timeleft || signal_pending(current)) { + ath6kl_err("wmi is not ready or wait " + "was interrupted\n"); + ret = -EIO; + goto err_htc_stop; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__); + if (test_bit(DOWNLOAD_FIRMWARE_EXT, &ar->flag)) { + ret = ath6kl_upload_firmware_ext(ar); + if (ret) + goto err_htc_stop; + } + + rttm_init(ar); + /* communicate the wmi protocol verision to the target */ + /* FIXME: return error */ + if ((ath6kl_set_host_app_area(ar)) != 0) + ath6kl_err("unable to set the host app area\n"); + + for (i = 0; i < ar->vif_max; i++) { + ret = ath6kl_target_config_wlan_params(ar, i); + if (ret) + goto err_htc_stop; + } + } else { + if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { + ath6kl_info("%s %s fw %s%s\n", + ar->hw.name, + ath6kl_init_get_hif_name(ar->hif_type), + ar->wiphy->fw_version, + test_bit(TESTMODE_EPPING, &ar->flag) ? + " testmode_epping" : ""); + } + + ar->mac_addr[0] = 0x00; + ar->mac_addr[1] = 0x01; + ar->mac_addr[2] = 0x02; + ar->mac_addr[3] = 0x03; + ar->mac_addr[4] = 0x04; + ar->mac_addr[5] = 0x05; + } + + return 0; + +err_htc_stop: + ath6kl_htc_stop(ar->htc_target); +err_cleanup_scatter: + ath6kl_hif_cleanup_scatter(ar); +err_power_off: + ath6kl_hif_power_off(ar); + + return ret; +} + +int ath6kl_init_hw_start(struct ath6kl *ar) +{ + int err; + + err = __ath6kl_init_hw_start(ar); + if (err) + return err; + ar->state = ATH6KL_STATE_ON; + return 0; +} + + +static int __ath6kl_init_hw_stop(struct ath6kl *ar) +{ + int ret; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "hw stop\n"); + + ath6kl_htc_stop(ar->htc_target); + + ath6kl_hif_stop(ar); + + ath6kl_bmi_reset(ar); + + ret = ath6kl_hif_power_off(ar); + if (ret) + ath6kl_warn("failed to power off hif: %d\n", ret); + + rttm_free(); + return 0; +} + +int ath6kl_init_hw_stop(struct ath6kl *ar) +{ + int err; + + err = __ath6kl_init_hw_stop(ar); + if (err) + return err; + + ar->state = ATH6KL_STATE_OFF; + + return 0; +} + +static u32 ath6kl_init_get_subtype(struct ath6kl *ar) +{ + /* default is min. capability */ + u32 subtype = TARGET_SUBTYPE_HT20_1SS_SING_BAND; + int is_2ss, is_dual_band, is_ht40; + + /* + * WARNING : Please load the correct board-data for each device to + * avoid to harm the device. + */ + is_2ss = is_dual_band = 0; + is_ht40 = 1; + if (ar->fw_board) { + if ((ar->fw_board[BDATA_TXRXMASK_OFFSET] & 0x03) == 0x03) + is_2ss = 1; + if (ar->fw_board[BDATA_OPFLAGS_OFFSET] & (1 << 0)) + is_dual_band = 1; + + /* Don't expect to support 5G-HT40-disabled case. */ + if (ar->fw_board[BDATA_OPFLAGS_OFFSET] & (1 << 3)) + is_ht40 = 0; + + if (is_ht40) + subtype |= TARGET_SUBTYPE_HT40; + if (is_2ss) + subtype |= TARGET_SUBTYPE_2SS; + if (is_dual_band) + subtype |= TARGET_SUBTYPE_DUAL; + } else { + WARN_ON(1); + } + + ath6kl_info("target's subtype is 0x%x, %s %s %s\n", + subtype, + (is_ht40 ? "HT20/40" : "HT20-only"), + (is_2ss ? "2SS" : "1SS"), + (is_dual_band ? "Dual-band" : "Single-band")); + + return subtype; +} + +int ath6kl_get_bootstrap_mode(struct ath6kl *ar) +{ + u32 address = WLAN_BOOTSTRAP_ADDRESS; + u32 bootstrap; + + /* Currently, we only check for AR6004 */ + if (ar->target_type != TARGET_TYPE_AR6004) { + ar->bootstrap_mode = 0; + return 0; + } + + if (ath6kl_diag_read32(ar, address, &bootstrap)) + return -EIO; + + ath6kl_info("target bootstrap: 0x%08x\n", bootstrap); + ar->bootstrap_mode = bootstrap; + + return 0; +} + +int ath6kl_core_init(struct ath6kl *ar) +{ + struct ath6kl_bmi_target_info targ_info; + struct net_device *ndev; + int ret = 0, i; + + ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); + if (!ar->ath6kl_wq) + return -ENOMEM; + + ret = ath6kl_bmi_init(ar); + if (ret) + goto err_wq; + + /* + * Turn on power to get hardware (target) version and leave power + * on delibrately as we will boot the hardware anyway within few + * seconds. + */ + ret = ath6kl_hif_power_on(ar); + if (ret) + goto err_bmi_cleanup; + + ret = ath6kl_bmi_get_target_info(ar, &targ_info); + if (ret) + goto err_power_off; + + ar->version.target_ver = le32_to_cpu(targ_info.version); + ar->target_type = le32_to_cpu(targ_info.type); + ar->wiphy->hw_version = le32_to_cpu(targ_info.version); + + if (ath6kl_get_host_app_area(ar) != 0) { + ath6kl_err("Firmware already uploaded, reset target\n"); + ath6kl_reset_device(ar, ar->target_type, true, true); + ret = -EAGAIN; + goto err_power_off; + } + + if (ath6kl_get_bootstrap_mode(ar) != 0) { + ath6kl_err("can't get bootstrap mode\n"); + goto err_power_off; + } + + ret = ath6kl_init_hw_params(ar); + if (ret) + goto err_power_off; + + ar->htc_target = ath6kl_htc_create(ar); + + if (!ar->htc_target) { + ret = -ENOMEM; + goto err_power_off; + } + +#ifdef ATH6KL_SUPPORT_WIFI_KTK + ar->ktk_active = false; +#endif + +#ifdef ATH6KL_SUPPORT_WIFI_DISC + ar->disc_active = false; +#endif + + ar->testmode = testmode; + + ar->starving_prevention = starving_prevention; + + ret = ath6kl_fetch_firmwares(ar); + if (ret) + goto err_htc_cleanup; + + ar->target_subtype = ath6kl_init_get_subtype(ar); + + /* FIXME: we should free all firmwares in the error cases below */ + + if (test_bit(TESTMODE_EPPING, &ar->flag)) { + ath6kl_info("%s: endpoint loopback mode, " + "ignore wmi init!\n", __func__); + } else { + /* Indicate that WMI is enabled (although not ready yet) */ + set_bit(WMI_ENABLED, &ar->flag); + ar->wmi = ath6kl_wmi_init(ar); + if (!ar->wmi) { + ath6kl_err("failed to initialize wmi\n"); + ret = -EIO; + goto err_htc_cleanup; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", + __func__, ar->wmi); + } + + if (ar->roam_mode == ATH6KL_MODULEROAM_DEFAULT) { + + if (ar->hif_type == ATH6KL_HIF_TYPE_SDIO) + ar->roam_mode = ATH6KL_SDIO_DEFAULT_ROAM_MODE; + else + ar->roam_mode = ATH6KL_USB_DEFAULT_ROAM_MODE; + + } + + /* Always use internal-regdb by default. */ + set_bit(INTERNAL_REGDB, &ar->flag); + + ret = ath6kl_register_ieee80211_hw(ar); + if (ret) + goto err_node_cleanup; + + ret = ath6kl_debug_init(ar); + if (ret) { + wiphy_unregister(ar->wiphy); + goto err_node_cleanup; + } + + for (i = 0; i < ar->vif_max; i++) + ar->avail_idx_map |= BIT(i); + + if (ar->p2p_compat) + ar->avail_idx_map = 0x3; + + rtnl_lock(); + if (ath6kl_ath0_name == 1) { + /* Add an initial station interface */ + ndev = ath6kl_interface_add(ar, "ath%d", + NL80211_IFTYPE_STATION, 0, + INFRA_NETWORK); + } else { + /* Add an initial station interface */ + ndev = ath6kl_interface_add(ar, "wlan%d", + NL80211_IFTYPE_STATION, 0, + INFRA_NETWORK); + } + rtnl_unlock(); + + if (!ndev) { + ath6kl_err("Failed to instantiate a network device\n"); + ret = -ENOMEM; + wiphy_unregister(ar->wiphy); + goto err_debug_init; + } + + + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", + __func__, ndev->name, ndev, ar); + + for (i = 0; i < WMM_NUM_AC; i++) + ar->ac_stream_active[i] = false; + + /* setup access class priority mappings */ + ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ + ar->ac_stream_pri_map[WMM_AC_BE] = 1; + ar->ac_stream_pri_map[WMM_AC_VI] = 2; + ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ + + /* give our connected endpoints some buffers */ + ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); + ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); + + if (test_bit(TESTMODE_EPPING, &ar->flag)) { + ath6kl_info("bypass wmi, and post receive buffer " + "for each endpoint here!\n"); + ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BK]); + ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_VI]); + ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_VO]); + } + + /* allocate some buffers that handle larger AMSDU frames */ + ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); + + ret = ath6kl_cookie_init(ar); + if (ret) + goto err_rxbuf_cleanup; + + ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | + ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; + + ar->p2p_flowctrl_ctx = ath6kl_p2p_flowctrl_conn_list_init(ar); + if (ar->p2p_multichan_concurrent) + ar->conf_flags |= ATH6KL_CONF_ENABLE_FLOWCTRL; + + ath6kl_info("P2P flowctrl %s\n", + ar->conf_flags & ATH6KL_CONF_ENABLE_FLOWCTRL ? + "enabled" : "disabled"); + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_SUSPEND_CUTPOWER)) + ar->conf_flags |= ATH6KL_CONF_SUSPEND_CUTPOWER; + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_RX_AGGR_DROP)) + ar->conf_flags |= ATH6KL_CONF_DISABLE_RX_AGGR_DROP; + + ath6kl_info("RX aggregation drop %s\n", + ar->conf_flags & ATH6KL_CONF_DISABLE_RX_AGGR_DROP ? + "disabled" : "enabled"); + + ar->wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + + ar->wiphy->probe_resp_offload = + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U; + + if (test_bit(INTERNAL_REGDB, &ar->flag)) { + /* Disable P2P-in-passive-chan channels by default. */ + ar->reg_ctx = ath6kl_reg_init(ar, true, ar->p2p_in_pasv_chan); + + /* P2P recommend channel works only internal regdb turns on. */ + ar->p2p_rc_info_ctx = ath6kl_p2p_rc_init(ar); + } else + ar->reg_ctx = ath6kl_reg_init(ar, false, false); + + set_bit(FIRST_BOOT, &ar->flag); + + /* init green tx params */ + ar->green_tx_params.enable = true; + ar->green_tx_params.next_probe_count = + ATH6KL_GTX_NEXT_PROBE_COUNT; + ar->green_tx_params.max_back_off = + ATH6KL_GTX_MAX_BACK_OFF; + ar->green_tx_params.min_gtx_rssi = + ATH6KL_GTX_MIN_RSSI; + ar->green_tx_params.force_back_off = + ATH6KL_GTX_FORCE_BACKOFF; + + ret = ath6kl_init_hw_start(ar); + if (ret) { + ath6kl_err("Failed to start hardware: %d\n", ret); + goto err_rxbuf_cleanup; + } + + /* + * Set mac address which is received in ready event + * FIXME: Move to ath6kl_interface_add() + */ + memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); + +#ifdef ATH6KL_DIAGNOSTIC + if (!test_bit(TESTMODE_EPPING, &ar->flag)) + wifi_diag_init(); +#endif + +#if defined(CONFIG_ANDROID) || defined(USB_AUTO_SUSPEND) + ret = ath6kl_android_enable_wow_default(ar); + if (ret != 0) + goto err_rxbuf_cleanup; +#endif + +#ifndef CONFIG_ANDROID + if ((ar->hif_type == ATH6KL_HIF_TYPE_SDIO) && + (!test_bit(TESTMODE_EPPING, &ar->flag))) + if (ath6kl_wmi_set_mcc_profile_cmd(ar->wmi, + WMI_MCC_PROFILE_STA50 | WMI_MCC_CTS_ENABLE)) + ath6kl_dbg(ATH6KL_DBG_TRC, "failed to set mcc profile"); +#endif + + /* Defer some tasks to worker after driver init. */ + if (!ret) { + init_waitqueue_head(&ar->init_defer_wait_wq); + INIT_WORK(&ar->init_defer_wk, ath6kl_core_init_defer); + + set_bit(INIT_DEFER_PROGRESS, &ar->flag); + schedule_work(&ar->init_defer_wk); + + /* Wait defer function to complete. */ + if (!ath6kl_mod_debug_quirks(ar, + ATH6KL_MODULE_DISABLE_WAIT_DEFER)) { + ath6kl_info("Wait defer tasks done...\n"); + wait_event_interruptible_timeout( + ar->init_defer_wait_wq, + !test_bit(INIT_DEFER_PROGRESS, &ar->flag), + INIT_DEFER_WAIT_TIMEOUT); + } + } + + if (ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_ENABLE_FW_CRASH_NOTIFY)) { + ath6kl_info("Enable Firmware crash notiry.\n"); + ar->fw_crash_notify = ath6kl_fw_crash_notify; + } + + return ret; + +err_rxbuf_cleanup: + ath6kl_htc_flush_rx_buf(ar->htc_target); + ath6kl_cleanup_amsdu_rxbufs(ar); + spin_lock_bh(&ar->list_lock); + list_del(&(((struct ath6kl_vif *)(netdev_priv(ndev)))->list)); + spin_unlock_bh(&ar->list_lock); + rtnl_lock(); + ath6kl_deinit_if_data(netdev_priv(ndev)); + rtnl_unlock(); + wiphy_unregister(ar->wiphy); +err_debug_init: + ath6kl_debug_cleanup(ar); +err_node_cleanup: +#ifdef CONFIG_ANDROID + if (ar->wow_irq) { + if (disable_irq_wake(ar->wow_irq)) + ath6kl_err("Couldn't disable hostwake IRQ wakeup mode\n"); + + free_irq(ar->wow_irq, ar); + ar->wow_irq = 0; + } + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&ar->wake_lock); +#endif + + ath6kl_cleanup_android_resource(ar); +#endif + ath6kl_wmi_shutdown(ar->wmi); + clear_bit(WMI_ENABLED, &ar->flag); + ar->wmi = NULL; +err_htc_cleanup: + ath6kl_htc_cleanup(ar->htc_target); +err_power_off: + ath6kl_hif_power_off(ar); +err_bmi_cleanup: + ath6kl_bmi_cleanup(ar); +err_wq: + destroy_workqueue(ar->ath6kl_wq); + + return ret; +} + +void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) +{ + static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + bool discon_issued; + + if (vif->pend_skb) + flush_delayed_work(&vif->work_eapol_send); + + netif_stop_queue(vif->ndev); + + clear_bit(WLAN_ENABLED, &vif->flags); + + if (wmi_ready) { + discon_issued = test_bit(CONNECTED, &vif->flags) || + test_bit(CONNECT_PEND, &vif->flags); + ath6kl_disconnect(vif); + del_timer(&vif->disconnect_timer); + + if (discon_issued) + ath6kl_disconnect_event(vif, DISCONNECT_CMD, + (vif->nw_type & AP_NETWORK) ? + bcast_mac : vif->bssid, + 0, NULL, 0); + } + + if (vif->scan_req) { + del_timer(&vif->vifscan_timer); + ath6kl_wmi_abort_scan_cmd(vif->ar->wmi, vif->fw_vif_idx); + cfg80211_scan_done(vif->scan_req, true); + vif->scan_req = NULL; + clear_bit(SCANNING, &vif->flags); + } +} + +void ath6kl_stop_txrx(struct ath6kl *ar) +{ + struct ath6kl_vif *vif, *tmp_vif; + + set_bit(DESTROY_IN_PROGRESS, &ar->flag); + + if (down_interruptible(&ar->sem)) { + ath6kl_err("down_interruptible failed\n"); + return; + } + + spin_lock_bh(&ar->list_lock); + list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) { + list_del(&vif->list); + spin_unlock_bh(&ar->list_lock); + ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); + rtnl_lock(); + ath6kl_deinit_if_data(vif); + rtnl_unlock(); + spin_lock_bh(&ar->list_lock); + } + spin_unlock_bh(&ar->list_lock); + + clear_bit(WMI_READY, &ar->flag); + + /* + * After wmi_shudown all WMI events will be dropped. We + * need to cleanup the buffers allocated in AP mode and + * give disconnect notification to stack, which usually + * happens in the disconnect_event. Simulate the disconnect + * event by calling the function directly. Sometimes + * disconnect_event will be received when the debug logs + * are collected. + */ + ath6kl_wmi_shutdown(ar->wmi); + + clear_bit(WMI_ENABLED, &ar->flag); + if (ar->htc_target) { + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__); + ath6kl_htc_stop(ar->htc_target); + } + + /* + * Try to reset the device if we can. The driver may have been + * configure NOT to reset the target during a debug session. + */ + ath6kl_dbg(ATH6KL_DBG_TRC, + "attempting to reset target on instance destroy\n"); + ath6kl_reset_device(ar, ar->target_type, true, true); + + up(&ar->sem); +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/main.c b/drivers/net/wireless/ath/ath6kl-3.5/main.c new file mode 100644 index 000000000000..c4f3030f63d3 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/main.c @@ -0,0 +1,2399 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "hif-ops.h" +#include "htc-ops.h" +#include "cfg80211.h" +#include "target.h" +#include "debug.h" +#ifdef ATHTST_SUPPORT +#include "ieee80211_ioctl.h" +#include "ce_athtst.h" +#endif + +int _string_to_mac(char *string, int len, u8 *macaddr) +{ + int i, k, ret; + char temp[3] = {0}; + unsigned long value; + + /* assume string is "00:11:22:33:44:55". */ + if (!string || (len < 17)) + return -1; + + + i = k = 0; + while (i < len) { + memcpy(temp, string + i, 2); + ret = kstrtoul(temp, 16, &value); + if (ret < 0) + return -1; + macaddr[k++] = (u8)value; + i += 3; + } + + return 0; +} + +struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr) +{ + struct ath6kl_sta *conn = NULL; + u8 i, max_conn; + + if (vif->nw_type != AP_NETWORK) + return &vif->sta_list[0]; + + max_conn = (vif->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0; + + for (i = 0; i < max_conn; i++) { + if (memcmp(node_addr, vif->sta_list[i].mac, ETH_ALEN) == 0) { + conn = &vif->sta_list[i]; + break; + } + } + + return conn; +} + +struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl_vif *vif, u8 aid) +{ + struct ath6kl_sta *conn = NULL; + u8 ctr; + + if (vif->nw_type != AP_NETWORK) { + conn = &vif->sta_list[0]; + } else { + for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { + if (vif->sta_list[ctr].aid == aid) { + conn = &vif->sta_list[ctr]; + break; + } + } + } + + return conn; +} + +static void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u8 aid, + u8 *wpaie, u8 ielen, u8 keymgmt, u8 ucipher, + u8 auth, u8 apsd_info, bool ht_support, + u8 phymode) +{ + struct ath6kl_sta *sta; + u8 free_slot; + + BUG_ON(aid > AP_MAX_NUM_STA); + + free_slot = aid - 1; + + sta = &vif->sta_list[free_slot]; + + spin_lock_bh(&sta->lock); + memcpy(sta->mac, mac, ETH_ALEN); + if (ielen <= ATH6KL_MAX_IE) + memcpy(sta->wpa_ie, wpaie, ielen); + sta->aid = aid; + sta->keymgmt = keymgmt; + sta->ucipher = ucipher; + sta->auth = auth; + sta->apsd_info = apsd_info; + sta->phymode = phymode; + sta->vif = vif; + init_timer(&sta->psq_age_timer); + sta->psq_age_timer.function = ath6kl_ps_queue_age_handler; + sta->psq_age_timer.data = (unsigned long)sta; + aggr_reset_state(sta->aggr_conn_cntxt); + sta->last_txrx_time_tgt = 0; + sta->last_txrx_time = 0; + + vif->sta_list_index = vif->sta_list_index | (1 << free_slot); + vif->ap_stats.sta[free_slot].aid = aid; + + if (ht_support) + sta->sta_flags |= STA_HT_SUPPORT; + else + vif->sta_no_ht_num++; + spin_unlock_bh(&sta->lock); +} + +static void ath6kl_sta_cleanup(struct ath6kl_vif *vif, u8 i) +{ + struct ath6kl_sta *sta = &vif->sta_list[i]; + struct ath6kl *ar = vif->ar; + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + struct list_head container; + int reclaim = 0; + + del_timer_sync(&sta->psq_age_timer); + + if (p2p_flowctrl && + p2p_flowctrl->sche_type == P2P_FLOWCTRL_SCHE_TYPE_CONNECTION) { + + INIT_LIST_HEAD(&container); + + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + fw_conn = &p2p_flowctrl->fw_conn_list[0]; + for (i = 0; i < NUM_CONN; i++, fw_conn++) { + if (fw_conn->connId == ATH6KL_P2P_FLOWCTRL_NULL_CONNID) + continue; + + if (memcmp(fw_conn->mac_addr, + sta->mac, + ETH_ALEN) == 0) { + + ath6kl_p2p_flowctrl_conn_collect_by_conn( + fw_conn, &container, &reclaim); + break; + } + } + + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + ath6kl_tx_complete(ar->htc_target, &container); + } + + spin_lock_bh(&sta->lock); + sta->vif = NULL; + + /* empty the queued pkts in the PS queue if any */ + ath6kl_ps_queue_purge(&sta->psq_data); + ath6kl_ps_queue_purge(&sta->psq_mgmt); + + memset(&vif->ap_stats.sta[sta->aid - 1], 0, + sizeof(struct wmi_per_sta_stat)); + memset(sta->mac, 0, ETH_ALEN); + memset(sta->wpa_ie, 0, ATH6KL_MAX_IE); + if (!(sta->sta_flags & STA_HT_SUPPORT)) + vif->sta_no_ht_num--; + sta->aid = 0; + sta->sta_flags = 0; + aggr_reset_state(sta->aggr_conn_cntxt); + + vif->sta_list_index = vif->sta_list_index & ~(1 << i); + spin_unlock_bh(&sta->lock); +} + +static u8 ath6kl_remove_sta(struct ath6kl_vif *vif, u8 *mac, u16 reason) +{ + u8 i, removed = 0; + + if (is_zero_ether_addr(mac)) + return removed; + + if (is_broadcast_ether_addr(mac)) { + ath6kl_dbg(ATH6KL_DBG_TRC, "deleting all station\n"); + + for (i = 0; i < AP_MAX_NUM_STA; i++) { + if (!is_zero_ether_addr(vif->sta_list[i].mac)) { + ath6kl_sta_cleanup(vif, i); + removed = 1; + } + } + } else { + for (i = 0; i < AP_MAX_NUM_STA; i++) { + if (memcmp(vif->sta_list[i].mac, mac, ETH_ALEN) == 0) { + ath6kl_dbg(ATH6KL_DBG_TRC, + "deleting station %pM aid=%d reason=%d\n", + mac, vif->sta_list[i].aid, reason); + ath6kl_sta_cleanup(vif, i); + removed = 1; + break; + } + } + } + + return removed; +} + +void ath6kl_ps_queue_init(struct ath6kl_ps_buf_head *psq, + enum ps_queue_type queue_type, + u32 age_cycle, + u32 max_depth) +{ + INIT_LIST_HEAD(&psq->list); + psq->queue_type = queue_type; + psq->age_cycle = age_cycle; + psq->max_depth = max_depth; + + psq->depth = 0; + psq->enqueued = 0; + psq->enqueued_err = 0; + psq->dequeued = 0; + psq->aged = 0; + spin_lock_init(&psq->lock); + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: init psq %p age_cycle %d\n", + psq, + psq->age_cycle); + + return; +} + +void ath6kl_ps_queue_purge(struct ath6kl_ps_buf_head *psq) +{ + unsigned long flags; + struct ath6kl_ps_buf_desc *ps_buf, *ps_buf_n; + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: purge psq %p depth %d\n", + psq, + psq->depth); + + WARN_ON(psq->depth < 0); + + spin_lock_irqsave(&psq->lock, flags); + if (psq->depth == 0) { + spin_unlock_irqrestore(&psq->lock, flags); + return; + } + list_for_each_entry_safe(ps_buf, ps_buf_n, &psq->list, list) { + list_del(&ps_buf->list); + if (ps_buf->skb) + dev_kfree_skb(ps_buf->skb); + kfree(ps_buf); + psq->depth--; + psq->dequeued++; + } + + WARN_ON(psq->depth != 0); + WARN_ON(psq->enqueued != psq->dequeued); + + /* call ath6kl_ps_queue_init() instead? */ + psq->depth = 0; + psq->enqueued = 0; + psq->enqueued_err = 0; + psq->dequeued = 0; + psq->aged = 0; + + spin_unlock_irqrestore(&psq->lock, flags); + + return; +} + +int ath6kl_ps_queue_empty(struct ath6kl_ps_buf_head *psq) +{ + unsigned long flags; + int empty; + + WARN_ON(psq->depth < 0); + + spin_lock_irqsave(&psq->lock, flags); + empty = (psq->depth == 0); + spin_unlock_irqrestore(&psq->lock, flags); + + return empty; +} + +int ath6kl_ps_queue_depth(struct ath6kl_ps_buf_head *psq) +{ + unsigned long flags; + int depth; + + WARN_ON(psq->depth < 0); + + spin_lock_irqsave(&psq->lock, flags); + depth = psq->depth; + spin_unlock_irqrestore(&psq->lock, flags); + + return depth; +} + +void ath6kl_ps_queue_stat(struct ath6kl_ps_buf_head *psq, + int *depth, + u32 *enqueued, + u32 *enqueued_err, + u32 *dequeued, + u32 *aged) +{ + unsigned long flags; + + spin_lock_irqsave(&psq->lock, flags); + *depth = psq->depth; + *enqueued = psq->enqueued; + *enqueued_err = psq->enqueued_err; + *dequeued = psq->dequeued; + *aged = psq->aged; + spin_unlock_irqrestore(&psq->lock, flags); + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: stat psq %p depth %d enqueued %d enqueued_err %d dequeued %d aged %d\n", + psq, + *depth, + *enqueued, + *enqueued_err, + *dequeued, + *aged); + + return; +} + +struct ath6kl_ps_buf_desc *ath6kl_ps_queue_dequeue( + struct ath6kl_ps_buf_head *psq) +{ + unsigned long flags; + struct ath6kl_ps_buf_desc *ps_buf; + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: dequeue psq %p depth %d\n", + psq, + psq->depth); + + WARN_ON(psq->depth < 0); + + spin_lock_irqsave(&psq->lock, flags); + if (psq->depth == 0) { + spin_unlock_irqrestore(&psq->lock, flags); + return NULL; + } + + ps_buf = list_first_entry(&psq->list, struct ath6kl_ps_buf_desc, list); + list_del(&ps_buf->list); + psq->depth--; + psq->dequeued++; + spin_unlock_irqrestore(&psq->lock, flags); + + return ps_buf; +} + +int ath6kl_ps_queue_enqueue_mgmt(struct ath6kl_ps_buf_head *psq, + const u8 *buf, + u16 len, + u32 id, + u32 freq, + u32 wait, + bool no_cck, + bool dont_wait_for_ack) +{ + unsigned long flags; + struct ath6kl_ps_buf_desc *ps_buf; + int mgmt_buf_size; + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: enqueue_mgmt psq %p depth %d buf %p\n", + psq, + psq->depth, + buf); + + if ((psq->max_depth != ATH6KL_PS_QUEUE_NO_DEPTH) && + (psq->depth > psq->max_depth)) { + psq->enqueued_err++; + return -EBUSY; + } + + mgmt_buf_size = len + sizeof(struct ath6kl_ps_buf_desc) - 1; + ps_buf = kmalloc(mgmt_buf_size, GFP_ATOMIC); + if (!ps_buf) { + psq->enqueued_err++; + return -ENOMEM; + } + + INIT_LIST_HEAD(&ps_buf->list); + ps_buf->id = id; + ps_buf->freq = freq; + ps_buf->wait = wait; + ps_buf->no_cck = no_cck; + ps_buf->dont_wait_for_ack = dont_wait_for_ack; + ps_buf->len = len; + ps_buf->age = 0; + ps_buf->skb = NULL; + memcpy(ps_buf->buf, buf, len); + + spin_lock_irqsave(&psq->lock, flags); + list_add_tail(&ps_buf->list, &psq->list); + psq->depth++; + psq->enqueued++; + spin_unlock_irqrestore(&psq->lock, flags); + + return 0; +} + +int ath6kl_ps_queue_enqueue_data(struct ath6kl_ps_buf_head *psq, + struct sk_buff *skb) +{ + unsigned long flags; + struct ath6kl_ps_buf_desc *ps_buf; + int data_buf_size; + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: enqueue_data psq %p depth %d skb %p\n", + psq, + psq->depth, + skb); + + if ((psq->max_depth != ATH6KL_PS_QUEUE_NO_DEPTH) && + (psq->depth > psq->max_depth)) { + psq->enqueued_err++; + return -EBUSY; + } + + data_buf_size = sizeof(struct ath6kl_ps_buf_desc); + ps_buf = kmalloc(data_buf_size, GFP_ATOMIC); + if (!ps_buf) { + psq->enqueued_err++; + return -ENOMEM; + } + INIT_LIST_HEAD(&ps_buf->list); + ps_buf->age = 0; + ps_buf->skb = skb; + ps_buf->len = skb->len; + + spin_lock_irqsave(&psq->lock, flags); + list_add_tail(&ps_buf->list, &psq->list); + psq->depth++; + psq->enqueued++; + spin_unlock_irqrestore(&psq->lock, flags); + + return 0; +} + +static int ath6kl_ps_queue_aging(struct ath6kl_ps_buf_head *psq) +{ + struct ath6kl_ps_buf_desc *ps_buf, *ps_buf_n; + unsigned long flags; + u32 age; + int is_empty = 1; + + spin_lock_irqsave(&psq->lock, flags); + if (psq->depth == 0) { + spin_unlock_irqrestore(&psq->lock, flags); + return is_empty; + } + list_for_each_entry_safe(ps_buf, ps_buf_n, &psq->list, list) { + age = ath6kl_ps_queue_get_age(ps_buf); + age++; + if ((psq->age_cycle != ATH6KL_PS_QUEUE_NO_AGE) && + (age >= psq->age_cycle)) { + list_del(&ps_buf->list); + if (ps_buf->skb) + dev_kfree_skb(ps_buf->skb); + kfree(ps_buf); + + psq->aged++; + psq->dequeued++; + psq->depth--; + } else + ath6kl_ps_queue_set_age(ps_buf, age); + } + is_empty = (psq->depth == 0); + spin_unlock_irqrestore(&psq->lock, flags); + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: aging psq %p depth %d aged %d\n", + psq, + psq->depth, + psq->aged); + + return is_empty; +} + +void ath6kl_ps_queue_age_handler(unsigned long ptr) +{ + struct ath6kl_sta *conn = (struct ath6kl_sta *)ptr; + struct ath6kl_vif *vif = conn->vif; + + spin_lock_bh(&conn->lock); + if (ath6kl_ps_queue_aging(&conn->psq_data) && + ath6kl_ps_queue_aging(&conn->psq_mgmt)) + ath6kl_wmi_set_pvb_cmd(vif->ar->wmi, + vif->fw_vif_idx, conn->aid, 0); + spin_unlock_bh(&conn->lock); + + mod_timer(&conn->psq_age_timer, jiffies + + msecs_to_jiffies(ATH6KL_PS_QUEUE_CHECK_AGE)); + + return; +} + +void ath6kl_ps_queue_age_start(struct ath6kl_sta *conn) +{ + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: aging_timer_start conn %p aid %d\n", + conn, + conn->aid); + + mod_timer(&conn->psq_age_timer, jiffies + + msecs_to_jiffies(ATH6KL_PS_QUEUE_CHECK_AGE)); + + return; +} + +void ath6kl_ps_queue_age_stop(struct ath6kl_sta *conn) +{ + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "ps: aging_timer_stop conn %p aid %d\n", + conn, + conn->aid); + + del_timer_sync(&conn->psq_age_timer); + + return; +} + +enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac) +{ + struct ath6kl *ar = devt; + return ar->ac2ep_map[ac]; +} + +static int ath6kl_cookie_pool_init(struct ath6kl *ar, + struct ath6kl_cookie_pool *cookie_pool, + enum cookie_type cookie_type, + u32 cookie_num) +{ + u32 i, mem_size; + + WARN_ON(!cookie_num); + + cookie_pool->cookie_num = cookie_num; + cookie_pool->cookie_type = cookie_type; + + cookie_pool->cookie_list = NULL; + cookie_pool->cookie_count = 0; + + mem_size = sizeof(struct ath6kl_cookie) * cookie_num; + cookie_pool->cookie_mem = kzalloc(mem_size, GFP_ATOMIC); + if (!cookie_pool->cookie_mem) { + ath6kl_err("unable to allocate cookie, type %d num %d\n", + cookie_type, + cookie_num); + return -ENOMEM; + } + + for (i = 0; i < cookie_num; i++) { + /* Assign the parent then insert to free queue */ + cookie_pool->cookie_mem[i].cookie_pool = cookie_pool; + ath6kl_free_cookie(ar, &cookie_pool->cookie_mem[i]); + } + + /* Reset stats */ + cookie_pool->cookie_alloc_cnt = 0; + cookie_pool->cookie_alloc_fail_cnt = 0; + cookie_pool->cookie_free_cnt = 0; + cookie_pool->cookie_peak_cnt = 0; + + ath6kl_info("Create HTC cookie, type %d num %d\n", + cookie_type, + cookie_num); + return 0; +} + +static void ath6kl_cookie_pool_cleanup(struct ath6kl *ar, + struct ath6kl_cookie_pool *cookie_pool) +{ + if (cookie_pool->cookie_num != cookie_pool->cookie_count) + ath6kl_err("Cookie unmber unsync, type %d num %d, %d\n", + cookie_pool->cookie_type, + cookie_pool->cookie_num, + cookie_pool->cookie_count); + + ath6kl_info("Free HTC cookie, type %d curr_num %d\n", + cookie_pool->cookie_type, + cookie_pool->cookie_count); + + cookie_pool->cookie_list = NULL; + cookie_pool->cookie_count = 0; + + kfree(cookie_pool->cookie_mem); + cookie_pool->cookie_mem = NULL; + + cookie_pool->cookie_alloc_cnt = 0; + cookie_pool->cookie_alloc_fail_cnt = 0; + cookie_pool->cookie_free_cnt = 0; + cookie_pool->cookie_peak_cnt = 0; + + return; +} + +struct ath6kl_cookie *ath6kl_alloc_cookie(struct ath6kl *ar, + enum cookie_type cookie_type) +{ + struct ath6kl_cookie_pool *cookie_pool; + struct ath6kl_cookie *cookie; + u32 alloced; + + if (cookie_type == COOKIE_TYPE_DATA) + cookie_pool = &ar->cookie_data; + else if (cookie_type == COOKIE_TYPE_CTRL) + cookie_pool = &ar->cookie_ctrl; + else + BUG_ON(1); + + cookie = cookie_pool->cookie_list; + if (cookie != NULL) { + cookie_pool->cookie_list = cookie->arc_list_next; + cookie_pool->cookie_count--; + + alloced = cookie_pool->cookie_num - cookie_pool->cookie_count; + cookie_pool->cookie_alloc_cnt++; + if (alloced > cookie_pool->cookie_peak_cnt) + cookie_pool->cookie_peak_cnt = alloced; + } else + cookie_pool->cookie_alloc_fail_cnt++; + + return cookie; +} + +int ath6kl_cookie_init(struct ath6kl *ar) +{ + /* Create HTC cookies pool for DATA frame */ + if (ath6kl_cookie_pool_init(ar, + &ar->cookie_data, + COOKIE_TYPE_DATA, + MAX_COOKIE_DATA_NUM)) + goto fail; + + /* Create HTC cookies pool for CTRL command */ + if (ath6kl_cookie_pool_init(ar, + &ar->cookie_ctrl, + COOKIE_TYPE_CTRL, + MAX_COOKIE_CTRL_NUM)) + goto fail; + + return 0; + +fail: + ath6kl_cookie_cleanup(ar); + + return -ENOMEM; +} + +void ath6kl_cookie_cleanup(struct ath6kl *ar) +{ + ath6kl_cookie_pool_cleanup(ar, + &ar->cookie_data); + ath6kl_cookie_pool_cleanup(ar, + &ar->cookie_ctrl); + + return; +} + +void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie) +{ + struct ath6kl_cookie_pool *cookie_pool; + + if (!ar || !cookie) + return; + + cookie_pool = cookie->cookie_pool; + BUG_ON(!cookie_pool); + + cookie->arc_list_next = cookie_pool->cookie_list; + cookie_pool->cookie_list = cookie; + cookie_pool->cookie_count++; + + cookie_pool->cookie_free_cnt++; + + return; +} + +int ath6kl_diag_warm_reset(struct ath6kl *ar) +{ + int ret; + + ret = ath6kl_hif_diag_warm_reset(ar); + + if (ret) { + ath6kl_err("failed to issue warm reset command\n"); + return ret; + } + + return 0; +} + +/* + * Read from the hardware through its diagnostic window. No cooperation + * from the firmware is required for this. + */ +int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value) +{ + int ret; + + ret = ath6kl_hif_diag_read32(ar, address, value); + if (ret) { + ath6kl_warn("failed to read32 through diagnose window: %d\n", + ret); + return ret; + } + + return 0; +} + +/* + * Write to the ATH6KL through its diagnostic window. No cooperation from + * the Target is required for this. + */ +int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value) +{ + int ret; + + ret = ath6kl_hif_diag_write32(ar, address, value); + + if (ret) { + ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n", + address, value); + return ret; + } + + return 0; +} + +int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length) +{ + u32 count, *buf = data; + int ret; + + if (WARN_ON(length % 4)) + return -EINVAL; + + for (count = 0; count < length / 4; count++, address += 4) { + ret = ath6kl_diag_read32(ar, address, &buf[count]); + if (ret) + return ret; + } + + return 0; +} + +int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length) +{ + u32 count; + __le32 *buf = data; + int ret; + + if (WARN_ON(length % 4)) + return -EINVAL; + + for (count = 0; count < length / 4; count++, address += 4) { + ret = ath6kl_diag_write32(ar, address, buf[count]); + if (ret) + return ret; + } + + return 0; +} + +int ath6kl_read_fwlogs(struct ath6kl *ar) +{ + struct ath6kl_dbglog_hdr debug_hdr; + struct ath6kl_dbglog_buf debug_buf; + u32 address, length, dropped, firstbuf, debug_hdr_addr; + int ret = 0, loop; + u8 *buf; + + buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + address = TARG_VTOP(ar->target_type, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_dbglog_hdr))); + + ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr); + if (ret) + goto out; + + /* Get the contents of the ring buffer */ + if (debug_hdr_addr == 0) { + ath6kl_warn("Invalid address for debug_hdr_addr\n"); + ret = -EINVAL; + goto out; + } + + address = TARG_VTOP(ar->target_type, debug_hdr_addr); + ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr)); + + address = TARG_VTOP(ar->target_type, + le32_to_cpu(debug_hdr.dbuf_addr)); + firstbuf = address; + dropped = le32_to_cpu(debug_hdr.dropped); + ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + + loop = 100; + + do { + address = TARG_VTOP(ar->target_type, + le32_to_cpu(debug_buf.buffer_addr)); + length = le32_to_cpu(debug_buf.length); + + if (length != 0 && (le32_to_cpu(debug_buf.length) <= + le32_to_cpu(debug_buf.bufsize))) { + length = ALIGN(length, 4); + + ret = ath6kl_diag_read(ar, address, + buf, length); + if (ret) + goto out; + + ath6kl_debug_fwlog_event(ar, buf, length); + } + + address = TARG_VTOP(ar->target_type, + le32_to_cpu(debug_buf.next)); + ret = ath6kl_diag_read(ar, address, &debug_buf, + sizeof(debug_buf)); + if (ret) + goto out; + + loop--; + + if (WARN_ON(loop == 0)) { + ret = -ETIMEDOUT; + goto out; + } + } while (address != firstbuf); + +out: + kfree(buf); + + return ret; +} + +/* FIXME: move to a better place, target.h? */ +#define AR6003_RESET_CONTROL_ADDRESS 0x00004000 +#define AR6004_RESET_CONTROL_ADDRESS 0x00004000 +#define AR6006_RESET_CONTROL_ADDRESS 0x00004000 + +void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, + bool wait_fot_compltn, bool cold_reset) +{ + int status = 0; + u32 address; + __le32 data; + + if (target_type != TARGET_TYPE_AR6003 && + target_type != TARGET_TYPE_AR6004 && + target_type != TARGET_TYPE_AR6006) + return; + + data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) : + cpu_to_le32(RESET_CONTROL_MBOX_RST); + + switch (target_type) { + case TARGET_TYPE_AR6003: + address = AR6003_RESET_CONTROL_ADDRESS; + break; + case TARGET_TYPE_AR6004: + address = AR6004_RESET_CONTROL_ADDRESS; + break; + case TARGET_TYPE_AR6006: + address = AR6006_RESET_CONTROL_ADDRESS; + break; + default: + address = AR6003_RESET_CONTROL_ADDRESS; + break; + } + + /* If the bootstrap mode is HSIC, do warm reset */ + if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode)) { + status = ath6kl_diag_warm_reset(ar); + ath6kl_info("%s: warm reset\n", __func__); + } else { + status = ath6kl_diag_write32(ar, address, data); + } + + if (status) + ath6kl_err("failed to reset target\n"); +} + +void ath6kl_fw_crash_notify(struct ath6kl *ar) +{ + struct ath6kl_vif *vif = ath6kl_vif_first(ar); + + BUG_ON(!vif); + + ath6kl_info("notify firmware crash to user %p\n", ar); + +#ifdef CONFIG_ANDROID /* Only for specific Android-JB */ + if (1) { + u8 *buf; + int len; + + len = 26; + buf = kzalloc(len, GFP_ATOMIC); + if (buf == NULL) + return; + + /* hint */ + buf[24] = 34; + cfg80211_send_unprot_disassoc(vif->ndev, + buf, + len); + + kfree(buf); + } +#endif + + return; +} + +int ath6kl_fw_watchdog_enable(struct ath6kl *ar) +{ + u32 param; + int ret; + + ret = ath6kl_diag_read32(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag2)), + (u32 *)¶m); + + if (ret != 0) + return ret; + + param |= HI_OPTION_FW_WATCHDOG_ENABLE; + + ret = ath6kl_diag_write32(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag2)), + param); + + return ret; +} + +int ath6kl_fw_crash_cold_reset_enable(struct ath6kl *ar) +{ + u32 param; + int ret; + + ret = ath6kl_diag_read32(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag2)), + (u32 *)¶m); + + if (ret != 0) + return ret; + + param |= HI_OPTION_FW_CRASH_COLD_RESET; + + ret = ath6kl_diag_write32(ar, + ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag2)), + param); + + return ret; +} + +static void ath6kl_install_static_wep_keys(struct ath6kl_vif *vif) +{ + u8 index; + u8 keyusage; + + for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) { + if (vif->wep_key_list[index].key_len) { + keyusage = GROUP_USAGE; + if (index == vif->def_txkey_index) + keyusage |= TX_USAGE; + + ath6kl_wmi_addkey_cmd(vif->ar->wmi, vif->fw_vif_idx, + index, + WEP_CRYPT, + keyusage, + vif->wep_key_list[index].key_len, + NULL, 0, + vif->wep_key_list[index].key, + KEY_OP_INIT_VAL, NULL, + NO_SYNC_WMIFLAG); + } + } +} + +void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel, + u8 *beacon, u8 beacon_len) +{ + struct ath6kl *ar = vif->ar; + struct ath6kl_req_key *ik; + int res; + u8 key_rsc[ATH6KL_KEY_SEQ_LEN]; + + ik = &vif->ap_mode_bkey; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel); + + vif->bss_ch = channel; + ath6kl_ap_beacon_info(vif, beacon, beacon_len); + + switch (vif->auth_mode) { + case NONE_AUTH: + if (vif->prwise_crypto == WEP_CRYPT) + ath6kl_install_static_wep_keys(vif); + break; + case WPA_PSK_AUTH: + case WPA2_PSK_AUTH: + case (WPA_PSK_AUTH | WPA2_PSK_AUTH): + if (!ik->valid) + break; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for " + "the initial group key for AP mode\n"); + memset(key_rsc, 0, sizeof(key_rsc)); + res = ath6kl_wmi_addkey_cmd( + ar->wmi, vif->fw_vif_idx, ik->key_index, ik->key_type, + GROUP_USAGE, ik->key_len, key_rsc, ATH6KL_KEY_SEQ_LEN, + ik->key, + KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); + if (res) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed " + "addkey failed: %d\n", res); + } + break; + } + + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); + + set_bit(CONNECTED, &vif->flags); + ath6kl_judge_roam_parameter(vif, false); + if (ath6kl_wmi_set_rate_ctrl_cmd(ar->wmi, + vif->fw_vif_idx, RATECTRL_MODE_PERONLY)) + ath6kl_err("set rate_ctrl failed\n"); + ath6kl_switch_parameter_based_on_connection(vif, false); + netif_carrier_on(vif->ndev); +} + +void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u8 aid, u8 *mac_addr, + u8 keymgmt, u8 ucipher, u8 auth, + u16 assoc_req_len, u8 *assoc_info, + u8 apsd_info, u8 phymode) +{ + u8 *ies = NULL, *wpa_ie = NULL, *pos; + size_t ies_len = 0; + struct station_info sinfo; + bool is_ht_sta = false; + + ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid); + + if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) { + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *) assoc_info; + if (ieee80211_is_assoc_req(mgmt->frame_control) && + assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) + + sizeof(mgmt->u.assoc_req)) { + ies = mgmt->u.assoc_req.variable; + ies_len = assoc_info + assoc_req_len - ies; + } else if (ieee80211_is_reassoc_req(mgmt->frame_control) && + assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) + + sizeof(mgmt->u.reassoc_req)) { + ies = mgmt->u.reassoc_req.variable; + ies_len = assoc_info + assoc_req_len - ies; + } + } + + pos = ies; + while (pos && pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + if (pos[0] == WLAN_EID_RSN) + wpa_ie = pos; /* RSN IE */ + else if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2) { + if (pos[5] == 0x01) + wpa_ie = pos; /* WPA IE */ + else if (pos[5] == 0x04) { + wpa_ie = pos; /* WPS IE */ + break; /* overrides WPA/RSN IE */ + } + } else if (pos[0] == WLAN_EID_HT_CAPABILITY && + pos[1] >= 22) + is_ht_sta = true; + else if (pos[0] == 0x44 && wpa_ie == NULL) { + /* + * Note: WAPI Parameter Set IE re-uses Element ID that + * was officially allocated for BSS AC Access Delay. As + * such, we need to be a bit more careful on when + * parsing the frame. However, BSS AC Access Delay + * element is not supposed to be included in + * (Re)Association Request frames, so this should not + * cause problems. + */ + wpa_ie = pos; /* WAPI IE */ + break; + } + pos += 2 + pos[1]; + } + + ath6kl_add_new_sta(vif, mac_addr, aid, wpa_ie, + wpa_ie ? 2 + wpa_ie[1] : 0, + keymgmt, ucipher, auth, apsd_info, + is_ht_sta, phymode); + + /* send event to application */ + memset(&sinfo, 0, sizeof(sinfo)); + + /* TODO: sinfo.generation */ + + sinfo.assoc_req_ies = ies; + sinfo.assoc_req_ies_len = ies_len; + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; + + cfg80211_new_sta(vif->ndev, mac_addr, &sinfo, GFP_KERNEL); + + netif_wake_queue(vif->ndev); +} + +void disconnect_timer_handler(unsigned long ptr) +{ + struct net_device *dev = (struct net_device *)ptr; + struct ath6kl_vif *vif = netdev_priv(dev); + + ath6kl_init_profile_info(vif); + ath6kl_disconnect(vif); +} + +int ath6kl_disconnect(struct ath6kl_vif *vif) +{ + int ret = 0; + + if (test_bit(CONNECTED, &vif->flags) || + test_bit(CONNECT_PEND, &vif->flags)) { + ret = ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx); + /* + * Disconnect command is issued, clear the connect pending + * flag. The connected flag will be cleared in + * disconnect event notification. + */ + clear_bit(CONNECT_PEND, &vif->flags); + } + + return ret; +} + +/* WMI Event handlers */ + +void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) +{ + struct ath6kl *ar = devt; + + memcpy(ar->mac_addr, datap, ETH_ALEN); + ath6kl_dbg(ATH6KL_DBG_TRC, "%s: mac addr = %pM\n", + __func__, ar->mac_addr); + + ar->version.wlan_ver = sw_ver; + ar->version.abi_ver = abi_ver; + + snprintf(ar->wiphy->fw_version, + sizeof(ar->wiphy->fw_version), + "%u.%u.%u.%u", + (ar->version.wlan_ver & 0xf0000000) >> 28, + (ar->version.wlan_ver & 0x0f000000) >> 24, + (ar->version.wlan_ver & 0x00ff0000) >> 16, + (ar->version.wlan_ver & 0x0000ffff)); + + /* indicate to the waiting thread that the ready event was received */ + set_bit(WMI_READY, &ar->flag); + wake_up(&ar->event_wq); +} + +void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) +{ + struct ath6kl *ar = vif->ar; + bool aborted = false; + + if (status != WMI_SCAN_STATUS_SUCCESS) + aborted = true; +#ifdef ACS_SUPPORT + /* FIXME : bad, may use call-back instead. */ + ath6kl_acs_scan_complete_event(vif, aborted); +#endif + + ath6kl_p2p_rc_scan_complete_event(vif, aborted); + + if (ath6kl_htcoex_scan_complete_event(vif, aborted) == + HTCOEX_PASS_SCAN_DONE) + ath6kl_cfg80211_scan_complete_event(vif, aborted); + + if (!vif->usr_bss_filter) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + NONE_BSS_FILTER, 0); + } + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); +} + +void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, + u16 listen_int, u16 beacon_int, + enum network_type net_type, u8 beacon_ie_len, + u8 assoc_req_len, u8 assoc_resp_len, + u8 *assoc_info) +{ + struct ath6kl *ar = vif->ar; + + ath6kl_cfg80211_connect_event(vif, channel, bssid, + listen_int, beacon_int, + net_type, beacon_ie_len, + assoc_req_len, assoc_resp_len, + assoc_info); + + memcpy(vif->bssid, bssid, sizeof(vif->bssid)); + vif->bss_ch = channel; + + if ((vif->nw_type == INFRA_NETWORK)) { + if (ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM && + vif->wdev.iftype == NL80211_IFTYPE_STATION) { + ath6kl_wmi_disctimeout_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_SEAMLESS_ROAMING_DISCONNECT_TIMEOUT); + } else { + ath6kl_wmi_disctimeout_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_DISCONNECT_TIMEOUT); + } + ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, + ar->listen_intvl_t, + ar->listen_intvl_b); + } + + netif_wake_queue(vif->ndev); + + /* Update connect & link status atomically */ + spin_lock_bh(&vif->if_lock); + set_bit(CONNECTED, &vif->flags); + clear_bit(CONNECT_PEND, &vif->flags); + netif_carrier_on(vif->ndev); + spin_unlock_bh(&vif->if_lock); + + aggr_reset_state(vif->sta_list[0].aggr_conn_cntxt); + vif->reconnect_flag = 0; + + if ((vif->nw_type == ADHOC_NETWORK) && ar->ibss_ps_enable) { + memset(ar->node_map, 0, sizeof(ar->node_map)); + ar->node_num = 0; + ar->next_ep_id = ENDPOINT_2; + } + + if (!vif->usr_bss_filter) { + set_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + CURRENT_BSS_FILTER, 0); + } + + /* Hook connection event */ + ath6kl_htcoex_connect_event(vif); + ath6kl_p2p_connect_event(vif, + beacon_ie_len, + assoc_req_len, + assoc_resp_len, + assoc_info); + ath6kl_switch_parameter_based_on_connection(vif, false); +} + +void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast) +{ + struct ath6kl_sta *sta; + u8 tsc[6]; + + /* + * For AP case, keyid will have aid of STA which sent pkt with + * MIC error. Use this aid to get MAC & send it to hostapd. + */ + if (vif->nw_type == AP_NETWORK) { + sta = ath6kl_find_sta_by_aid(vif, (keyid >> 2)); + if (!sta) + return; + + ath6kl_dbg(ATH6KL_DBG_TRC, + "ap tkip mic error received from aid=%d\n", keyid); + + memset(tsc, 0, sizeof(tsc)); /* FIX: get correct TSC */ + cfg80211_michael_mic_failure(vif->ndev, sta->mac, + NL80211_KEYTYPE_PAIRWISE, keyid, + tsc, GFP_KERNEL); + } else + ath6kl_cfg80211_tkip_micerr_event(vif, keyid, ismcast); + +} + +static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) +{ + struct wmi_target_stats *tgt_stats = + (struct wmi_target_stats *) ptr; + struct ath6kl *ar = vif->ar; + struct target_stats *stats = &vif->target_stats; + struct tkip_ccmp_stats *ccmp_stats; + u8 ac; + + if (len < sizeof(*tgt_stats)) + return; + + ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n"); + + stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt); + stats->tx_byte += le32_to_cpu(tgt_stats->stats.tx.byte); + stats->tx_ucast_pkt += le32_to_cpu(tgt_stats->stats.tx.ucast_pkt); + stats->tx_ucast_byte += le32_to_cpu(tgt_stats->stats.tx.ucast_byte); + stats->tx_mcast_pkt += le32_to_cpu(tgt_stats->stats.tx.mcast_pkt); + stats->tx_mcast_byte += le32_to_cpu(tgt_stats->stats.tx.mcast_byte); + stats->tx_bcast_pkt += le32_to_cpu(tgt_stats->stats.tx.bcast_pkt); + stats->tx_bcast_byte += le32_to_cpu(tgt_stats->stats.tx.bcast_byte); + stats->tx_rts_success_cnt += + le32_to_cpu(tgt_stats->stats.tx.rts_success_cnt); + + for (ac = 0; ac < WMM_NUM_AC; ac++) + stats->tx_pkt_per_ac[ac] += + le32_to_cpu(tgt_stats->stats.tx.pkt_per_ac[ac]); + + stats->tx_err += le32_to_cpu(tgt_stats->stats.tx.err); + stats->tx_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.fail_cnt); + stats->tx_retry_cnt += le32_to_cpu(tgt_stats->stats.tx.retry_cnt); + stats->tx_mult_retry_cnt += + le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt); + stats->tx_rts_fail_cnt += + le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt); + if (ar->target_type == TARGET_TYPE_AR6004) { + stats->tx_ucast_rate = + ath6kl_wmi_get_rate_ar6004( + a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate)); + stats->tx_rate_index = tgt_stats->stats.tx.ucast_rate; + } else { + stats->tx_ucast_rate = + ath6kl_wmi_get_rate( + a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate)); + } + + stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt); + stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte); + stats->rx_ucast_pkt += le32_to_cpu(tgt_stats->stats.rx.ucast_pkt); + stats->rx_ucast_byte += le32_to_cpu(tgt_stats->stats.rx.ucast_byte); + stats->rx_mcast_pkt += le32_to_cpu(tgt_stats->stats.rx.mcast_pkt); + stats->rx_mcast_byte += le32_to_cpu(tgt_stats->stats.rx.mcast_byte); + stats->rx_bcast_pkt += le32_to_cpu(tgt_stats->stats.rx.bcast_pkt); + stats->rx_bcast_byte += le32_to_cpu(tgt_stats->stats.rx.bcast_byte); + stats->rx_frgment_pkt += le32_to_cpu(tgt_stats->stats.rx.frgment_pkt); + stats->rx_err += le32_to_cpu(tgt_stats->stats.rx.err); + stats->rx_crc_err += le32_to_cpu(tgt_stats->stats.rx.crc_err); + stats->rx_key_cache_miss += + le32_to_cpu(tgt_stats->stats.rx.key_cache_miss); + stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err); + stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame); + stats->rx_ucast_rate = + ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate)); + + ccmp_stats = &tgt_stats->stats.tkip_ccmp_stats; + + stats->tkip_local_mic_fail += + le32_to_cpu(ccmp_stats->tkip_local_mic_fail); + stats->tkip_cnter_measures_invoked += + le32_to_cpu(ccmp_stats->tkip_cnter_measures_invoked); + stats->tkip_fmt_err += le32_to_cpu(ccmp_stats->tkip_fmt_err); + + stats->ccmp_fmt_err += le32_to_cpu(ccmp_stats->ccmp_fmt_err); + stats->ccmp_replays += le32_to_cpu(ccmp_stats->ccmp_replays); + + stats->pwr_save_fail_cnt += + le32_to_cpu(tgt_stats->pm_stats.pwr_save_failure_cnt); + stats->noise_floor_calib = + a_sle32_to_cpu(tgt_stats->noise_floor_calib); + + stats->cs_bmiss_cnt += + le32_to_cpu(tgt_stats->cserv_stats.cs_bmiss_cnt); + stats->cs_low_rssi_cnt += + le32_to_cpu(tgt_stats->cserv_stats.cs_low_rssi_cnt); + stats->cs_connect_cnt += + le16_to_cpu(tgt_stats->cserv_stats.cs_connect_cnt); + stats->cs_discon_cnt += + le16_to_cpu(tgt_stats->cserv_stats.cs_discon_cnt); + + stats->cs_ave_beacon_rssi = + a_sle16_to_cpu(tgt_stats->cserv_stats.cs_ave_beacon_rssi); + + stats->cs_last_roam_msec = + tgt_stats->cserv_stats.cs_last_roam_msec; + stats->cs_snr = tgt_stats->cserv_stats.cs_snr; + stats->cs_rssi = a_sle16_to_cpu(tgt_stats->cserv_stats.cs_rssi); + + stats->lq_val = le32_to_cpu(tgt_stats->lq_val); + + stats->wow_pkt_dropped += + le32_to_cpu(tgt_stats->wow_stats.wow_pkt_dropped); + stats->wow_host_pkt_wakeups += + tgt_stats->wow_stats.wow_host_pkt_wakeups; + stats->wow_host_evt_wakeups += + tgt_stats->wow_stats.wow_host_evt_wakeups; + stats->wow_evt_discarded += + le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded); + +} + +static void ath6kl_add_le32(__le32 *var, __le32 val) +{ + *var = cpu_to_le32(le32_to_cpu(*var) + le32_to_cpu(val)); +} + +void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len) +{ + struct wmi_ap_mode_stat *p = (struct wmi_ap_mode_stat *) ptr; + struct wmi_ap_mode_stat *ap = &vif->ap_stats; + struct wmi_per_sta_stat *st_ap, *st_p; + u8 ac, slot; + u16 updated = 0; + + if (vif->nw_type == AP_NETWORK) { + if ((len + 4) >= sizeof(*p)) { + for (ac = 0; ac < AP_MAX_NUM_STA; ac++) { + st_p = &p->sta[ac]; + + /* + * Target may insert garbage data and only + * update the associated stations. + */ + if ((st_p->aid == 0) || + (st_p->aid > AP_MAX_NUM_STA)) + continue; + + st_ap = &ap->sta[st_p->aid - 1]; + slot = (1 << (st_p->aid - 1)); + if ((vif->sta_list_index & slot) && + (!(updated & slot))) { + WARN_ON(st_ap->aid != st_p->aid); + updated |= slot; + ath6kl_add_le32(&st_ap->tx_bytes, + st_p->tx_bytes); + ath6kl_add_le32(&st_ap->tx_pkts, + st_p->tx_pkts); + ath6kl_add_le32(&st_ap->tx_error, + st_p->tx_error); + ath6kl_add_le32(&st_ap->tx_discard, + st_p->tx_discard); + ath6kl_add_le32(&st_ap->rx_bytes, + st_p->rx_bytes); + ath6kl_add_le32(&st_ap->rx_pkts, + st_p->rx_pkts); + ath6kl_add_le32(&st_ap->rx_error, + st_p->rx_error); + ath6kl_add_le32(&st_ap->rx_discard, + st_p->rx_discard); + st_ap->tx_ucast_rate = + st_p->tx_ucast_rate; + st_ap->last_txrx_time = + le16_to_cpu(st_p->last_txrx_time); + } + } + } + } else { + ath6kl_update_target_stats(vif, ptr, len); + } + + if (test_bit(STATS_UPDATE_PEND, &vif->flags)) { + clear_bit(STATS_UPDATE_PEND, &vif->flags); + wake_up(&vif->ar->event_wq); + } +} + +void ath6kl_wakeup_event(void *dev) +{ + struct ath6kl *ar = (struct ath6kl *) dev; + + wake_up(&ar->event_wq); +} + +void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr) +{ + struct ath6kl *ar = (struct ath6kl *) devt; + + ar->tx_pwr = tx_pwr; + wake_up(&ar->event_wq); +} + +void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid) +{ + struct ath6kl_sta *conn; + bool psq_empty = false; + bool is_data_psq_empty, is_mgmt_psq_empty; + struct ath6kl *ar = vif->ar; + struct ath6kl_ps_buf_desc *ps_buf; + + conn = ath6kl_find_sta_by_aid(vif, aid); + + if (!conn) + return; + /* + * Send out a packet queued on ps queue. When the ps queue + * becomes empty update the PVB for this station. + */ + spin_lock_bh(&conn->lock); + is_data_psq_empty = ath6kl_ps_queue_empty(&conn->psq_data); + is_mgmt_psq_empty = ath6kl_ps_queue_empty(&conn->psq_mgmt); + psq_empty = is_data_psq_empty && is_mgmt_psq_empty; + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: aid %d sta_flags %x psq_data %d psq_mgmt %d\n", + __func__, conn->aid, conn->sta_flags, !is_data_psq_empty, + !is_mgmt_psq_empty); + + if (psq_empty) { + spin_unlock_bh(&conn->lock); + return; + } + + if (!is_mgmt_psq_empty) { + struct ieee80211_mgmt *mgmt; + + ps_buf = ath6kl_ps_queue_dequeue(&conn->psq_mgmt); + spin_unlock_bh(&conn->lock); + + WARN_ON(!ps_buf); + conn->sta_flags |= STA_PS_POLLED; + + mgmt = (struct ieee80211_mgmt *) ps_buf->buf; + if (ps_buf->buf + ps_buf->len >= mgmt->u.probe_resp.variable && + ieee80211_is_probe_resp(mgmt->frame_control)) + ath6kl_wmi_send_go_probe_response_cmd(ar->wmi, + vif, + ps_buf->buf, + ps_buf->len, + ps_buf->freq); + else + ath6kl_wmi_send_action_cmd(ar->wmi, + vif->fw_vif_idx, + ps_buf->id, + ps_buf->freq, + ps_buf->wait, + ps_buf->buf, + ps_buf->len); + + conn->sta_flags &= ~STA_PS_POLLED; + kfree(ps_buf); + } else { + ps_buf = ath6kl_ps_queue_dequeue(&conn->psq_data); + spin_unlock_bh(&conn->lock); + + if (ps_buf) { + WARN_ON(!ps_buf->skb); + if (ps_buf->skb) { + conn->sta_flags |= STA_PS_POLLED; + ath6kl_data_tx(ps_buf->skb, vif->ndev, true); + conn->sta_flags &= ~STA_PS_POLLED; + } + kfree(ps_buf); + } + } + + spin_lock_bh(&conn->lock); + psq_empty = ath6kl_ps_queue_empty(&conn->psq_data) && + ath6kl_ps_queue_empty(&conn->psq_mgmt); + spin_unlock_bh(&conn->lock); + + if (psq_empty) + ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, conn->aid, 0); +} + +void ath6kl_dtimexpiry_event(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + struct ath6kl_ps_buf_desc *ps_buf; + + /* + * If there are no associated STAs, ignore the DTIM expiry event. + * There can be potential race conditions where the last associated + * STA may disconnect & before the host could clear the 'Indicate + * DTIM' request to the firmware, the firmware would have just + * indicated a DTIM expiry event. The race is between 'clear DTIM + * expiry cmd' going from the host to the firmware & the DTIM + * expiry event happening from the firmware to the host. + */ + if (!vif->sta_list_index) + return; + + spin_lock_bh(&vif->psq_mcast_lock); + if (ath6kl_ps_queue_empty(&vif->psq_mcast)) { + spin_unlock_bh(&vif->psq_mcast_lock); + return; + } + spin_unlock_bh(&vif->psq_mcast_lock); + + /* set the STA flag to dtim_expired for the frame to go out */ + set_bit(DTIM_EXPIRED, &vif->flags); + + spin_lock_bh(&vif->psq_mcast_lock); + while ((ps_buf = ath6kl_ps_queue_dequeue(&vif->psq_mcast)) != NULL) { + spin_unlock_bh(&vif->psq_mcast_lock); + ath6kl_data_tx(ps_buf->skb, vif->ndev, true); + kfree(ps_buf); + + spin_lock_bh(&vif->psq_mcast_lock); + } + spin_unlock_bh(&vif->psq_mcast_lock); + + clear_bit(DTIM_EXPIRED, &vif->flags); + + /* clear the LSB of the BitMapCtl field of the TIM IE */ + ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, MCAST_AID, 0); +} + +void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, + u8 assoc_resp_len, u8 *assoc_info, + u16 prot_reason_status) +{ + struct ath6kl *ar = vif->ar; + + if (vif->nw_type == AP_NETWORK) { + if (!ath6kl_remove_sta(vif, bssid, prot_reason_status)) + return; + + ath6kl_ap_ht_update_ies(vif); + + /* if no more associated STAs, empty the mcast PS q */ + if (vif->sta_list_index == 0) { + ath6kl_ps_queue_purge(&vif->psq_mcast); + + /* clear the LSB of the TIM IE's BitMapCtl field */ + if (test_bit(WMI_READY, &ar->flag)) + ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx, + MCAST_AID, 0); + } + + if (!is_broadcast_ether_addr(bssid)) { + /* send event to application */ + cfg80211_del_sta(vif->ndev, bssid, GFP_KERNEL); + } + + if (memcmp(vif->ndev->dev_addr, bssid, ETH_ALEN) == 0) { + vif->bss_ch = 0; + vif->phymode = ATH6KL_PHY_MODE_UNKNOWN; + vif->chan_type = ATH6KL_CHAN_TYPE_NONE; + memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); + clear_bit(CONNECTED, &vif->flags); + netif_carrier_off(vif->ndev); + netif_stop_queue(vif->ndev); + ath6kl_tx_data_cleanup_by_if(vif); + ath6kl_judge_roam_parameter(vif, true); + ath6kl_switch_parameter_based_on_connection(vif, true); + } + return; + } else if (vif->nw_type == INFRA_NETWORK) { + ath6kl_wmi_disctimeout_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_DISCONNECT_TIMEOUT); + + /* Support to triger supplicant to have another try. */ + if (!is_valid_ether_addr(bssid) && + is_valid_ether_addr(vif->req_bssid)) + bssid = vif->req_bssid; + } + + ath6kl_cfg80211_disconnect_event(vif, reason, bssid, + assoc_resp_len, assoc_info, + prot_reason_status); + + aggr_reset_state(vif->sta_list[0].aggr_conn_cntxt); + + del_timer(&vif->disconnect_timer); + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "disconnect reason is %d\n", reason); + + /* + * If the event is due to disconnect cmd from the host, only they + * the target would stop trying to connect. Under any other + * condition, target would keep trying to connect. + */ + if (reason == DISCONNECT_CMD) { + if (!vif->usr_bss_filter && test_bit(WMI_READY, &ar->flag)) + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + NONE_BSS_FILTER, 0); + } else { + set_bit(CONNECT_PEND, &vif->flags); + if (((reason == ASSOC_FAILED) && + (prot_reason_status == 0x11)) || + ((reason == ASSOC_FAILED) && (prot_reason_status == 0x0) + && (vif->reconnect_flag == 1))) { + set_bit(CONNECTED, &vif->flags); + ath6kl_judge_roam_parameter(vif, false); + ath6kl_switch_parameter_based_on_connection(vif, false); + return; + } + } + + /* update connect & link status atomically */ + spin_lock_bh(&vif->if_lock); + clear_bit(CONNECTED, &vif->flags); + clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags); + clear_bit(PS_STICK, &vif->flags); + del_timer(&vif->shprotect_timer); + netif_carrier_off(vif->ndev); + spin_unlock_bh(&vif->if_lock); + + if ((reason != CSERV_DISCONNECT) || (vif->reconnect_flag != 1)) + vif->reconnect_flag = 0; + + if (reason != CSERV_DISCONNECT) + ar->user_key_ctrl = 0; + + netif_stop_queue(vif->ndev); + memset(vif->bssid, 0, sizeof(vif->bssid)); + vif->bss_ch = 0; + vif->phymode = ATH6KL_PHY_MODE_UNKNOWN; + vif->chan_type = ATH6KL_CHAN_TYPE_NONE; + + ath6kl_tx_data_cleanup_by_if(vif); + + /* Hook disconnection event */ + ath6kl_htcoex_disconnect_event(vif); + ath6kl_judge_roam_parameter(vif, true); + ath6kl_switch_parameter_based_on_connection(vif, true); +} + +struct ath6kl_vif *ath6kl_vif_first(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + + spin_lock_bh(&ar->list_lock); + if (list_empty(&ar->vif_list)) { + spin_unlock_bh(&ar->list_lock); + return NULL; + } + + vif = list_first_entry(&ar->vif_list, struct ath6kl_vif, list); + + spin_unlock_bh(&ar->list_lock); + + return vif; +} + +static int ath6kl_open(struct net_device *dev) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl *ar = ath6kl_priv(dev); + + set_bit(WLAN_ENABLED, &vif->flags); + + if (test_bit(CONNECTED, &vif->flags) || + test_bit(TESTMODE_EPPING, &ar->flag)) { + netif_carrier_on(dev); + netif_wake_queue(dev); + } else + netif_carrier_off(dev); + + return 0; +} + +static int ath6kl_close(struct net_device *dev) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + + netif_stop_queue(dev); + + switch (vif->sme_state) { + case SME_CONNECTING: + ath6kl_cfg80211_connect_result(vif, vif->bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + break; + case SME_CONNECTED: + ath6kl_cfg80211_disconnected(vif, 0, NULL, 0, GFP_KERNEL); + break; + case SME_DISCONNECTED: + default: + break; + } + + /* Stop keep-alive. */ + ath6kl_ap_keepalive_stop(vif); + + /* Stop ACL. */ + ath6kl_ap_acl_stop(vif); + + /* Stop Admission-Control */ + ath6kl_ap_admc_stop(vif); + + ath6kl_disconnect(vif); + + vif->sme_state = SME_DISCONNECTED; + clear_bit(CONNECTED, &vif->flags); + clear_bit(CONNECT_PEND, &vif->flags); + clear_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags); + clear_bit(PS_STICK, &vif->flags); + del_timer(&vif->shprotect_timer); + + if (test_bit(WMI_READY, &ar->flag)) { + if (ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0xFFFF, + 0, 0, 0, 0, 0, 0, 0, 0, 0)) + return -EIO; + + } + + ath6kl_cfg80211_scan_complete_event(vif, true); + + clear_bit(WLAN_ENABLED, &vif->flags); + + return 0; +} + +static struct net_device_stats *ath6kl_get_stats(struct net_device *dev) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + + return &vif->net_stats; +} + +static int ath6kl_ioctl_p2p_set_ps(struct ath6kl_vif *vif, + char *user_cmd, + int len) +{ + int ret = 0; + u8 pwr_mode; + + /* SET::P2P_SET_PS {legacy_ps} {opp_ps} {ctwindow} */ + if (len > 1) { + if (down_interruptible(&vif->ar->sem)) { + ath6kl_err("busy, couldn't get access\n"); + return -EIO; + } + + ret = 0; + pwr_mode = (user_cmd[0] != '0' ? + REC_POWER : MAX_PERF_POWER); + + if (ath6kl_wmi_powermode_cmd(vif->ar->wmi, + vif->fw_vif_idx, + pwr_mode)) + ret = -EIO; + + up(&vif->ar->sem); + } else + ret = -EFAULT; + + return ret; +} + +static int ath6kl_ioctl_setband(struct ath6kl_vif *vif, + char *user_cmd, + int len) +{ + int ret = 0, scanband_type = 0; + u8 not_allow_ch; + + /* SET::SETBAND {band} */ + if (len > 1) { + ret = 0; + sscanf(user_cmd, "%d", &scanband_type); + + if (scanband_type == ANDROID_SETBAND_ALL) + vif->scanband_type = SCANBAND_TYPE_ALL; + else if (scanband_type == ANDROID_SETBAND_5G) + vif->scanband_type = SCANBAND_TYPE_5G; + else if (scanband_type == ANDROID_SETBAND_2G) + vif->scanband_type = SCANBAND_TYPE_2G; + else if ((scanband_type >= 2412) && (scanband_type <= 5825)) { + vif->scanband_type = SCANBAND_TYPE_CHAN_ONLY; + vif->scanband_chan = scanband_type; + } else + ret = -ENOTSUPP; + + /* Disconnect if AP is in not allowed band. */ + not_allow_ch = 0; + if ((vif->bss_ch) && + (vif->scanband_type != SCANBAND_TYPE_ALL) && + (((vif->scanband_type == SCANBAND_TYPE_5G) && + (vif->bss_ch < 2484)) || + ((vif->scanband_type == SCANBAND_TYPE_2G) && + (vif->bss_ch > 2484)))) + not_allow_ch = 1; + + if ((!ret) && + (vif->nw_type == INFRA_NETWORK) && + (not_allow_ch) && + (test_bit(CONNECTED, &vif->flags))) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "Disconnect because of band changed!"); + vif->reconnect_flag = 0; + ret = ath6kl_disconnect(vif); + memset(vif->ssid, 0, sizeof(vif->ssid)); + vif->ssid_len = 0; + } + } else + ret = -EFAULT; + + return ret; +} + +static int ath6kl_ioctl_setrts(struct ath6kl_vif *vif, + char *user_cmd, + int len) +{ + int ret = 0, rts_threshold = 0; + + /* SET::SETRTS {threshold, 0~2347} */ + if (len > 1) { + ret = 0; + sscanf(user_cmd, "%d", &rts_threshold); + + if (rts_threshold < 0 || rts_threshold > 2347) + return -EINVAL; + + ath6kl_wmi_set_rts_cmd(vif->ar->wmi, + vif->fw_vif_idx, + rts_threshold); + } else + ret = -EFAULT; + + return ret; +} + +static int ath6kl_ioctl_p2p_dev_addr(struct ath6kl_vif *vif, + char *user_cmd, + u8 *buf) +{ + int ret = 0; + struct ath6kl_vif *p2p_vif; + + /* GET::P2P_DEV_ADDR */ + /* In current design, the last vif is always be P2P-device. */ + p2p_vif = ath6kl_get_vif_by_index(vif->ar, (vif->ar->vif_max - 1)); + if (p2p_vif) { + if (copy_to_user(buf, p2p_vif->ndev->dev_addr, ETH_ALEN)) + ret = -EFAULT; + else + ret = 0; + } else + ret = -EFAULT; + + return ret; +} + +static int ath6kl_ioctl_p2p_best_chan(struct ath6kl_vif *vif, + char *user_cmd, + u8 *buf) +{ + char result[20]; + u16 rc_2g, rc_5g, rc_all; + int ret = 0; + + /* GET::P2P_BEST_CHANNEL */ + + rc_2g = rc_5g = rc_all = 0; + + /* + * Current wpa_supplicant only uses best channel for P2P purpose. + * Hence, here just get P2P channels. + */ + ret = ath6kl_p2p_rc_get(vif->ar, + NULL, + NULL, + NULL, + NULL, + &rc_2g, + &rc_5g, + &rc_all); + + if (ret == 0) { + memset(result, 0, 20); + snprintf(result, 20, + "%d %d %d", + rc_2g, + rc_5g, + rc_all); + result[strlen(result)] = '\0'; + if (copy_to_user(buf, result, strlen(result))) + ret = -EFAULT; + else + ret = 0; + } + + return ret; +} + +static int ath6kl_ioctl_ap_acl(struct ath6kl_vif *vif, + char *user_cmd, + u8 *buf, /* reserved for GET op */ + int len) +{ + int ret = 0; + + /* SET::ACL {MACCMD|ADDMAC|DELMAC} {{[0|1|2]}|{MAC ADDRESS}} */ + if (len > 1) { + int i, policy, addr[ETH_ALEN]; + u8 mac_addr[ETH_ALEN]; + + if (strstr(user_cmd, "MACCMD ")) { + user_cmd += 7; + sscanf(user_cmd, "%d", &policy); + ret = ath6kl_ap_acl_config_policy(vif, policy); + } else if (strstr(user_cmd, "ADDMAC ")) { + user_cmd += 7; + if (sscanf(user_cmd, "%02x:%02x:%02x:%02x:%02x:%02x", + &addr[0], &addr[1], &addr[2], + &addr[3], &addr[4], &addr[5]) != ETH_ALEN) + return -EFAULT; + + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = (u8)addr[i]; + + ret = ath6kl_ap_acl_config_mac_list(vif, + mac_addr, + false); + } else if (strstr(user_cmd, "DELMAC ")) { + user_cmd += 7; + if (sscanf(user_cmd, "%02x:%02x:%02x:%02x:%02x:%02x", + &addr[0], &addr[1], &addr[2], + &addr[3], &addr[4], &addr[5]) != ETH_ALEN) + return -EFAULT; + + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = (u8)addr[i]; + + ret = ath6kl_ap_acl_config_mac_list(vif, + mac_addr, + true); + } else + ret = -EFAULT; + } else + ret = -EFAULT; + + return ret; +} + +static int ath6kl_ioctl_standard(struct net_device *dev, + struct ifreq *rq, int cmd) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + void *data = (void *)(rq->ifr_data); + int ret = 0; + + switch (cmd) { + case ATH6KL_IOCTL_STANDARD01: + { + struct ath6kl_android_wifi_priv_cmd android_cmd; + char *user_cmd; + + if (copy_from_user(&android_cmd, + data, + sizeof(struct ath6kl_android_wifi_priv_cmd))) + ret = -EIO; + else { + user_cmd = kzalloc(android_cmd.total_len, GFP_KERNEL); + if (!user_cmd) { + ret = -ENOMEM; + break; + } + + if (copy_from_user(user_cmd, + android_cmd.buf, + android_cmd.used_len)) + ret = -EIO; + else { + if (strstr(user_cmd, "P2P_SET_PS ")) + ret = ath6kl_ioctl_p2p_set_ps(vif, + (user_cmd + 11), + (android_cmd.used_len - 11)); + else if (strstr(user_cmd, "SETBAND ")) + ret = ath6kl_ioctl_setband(vif, + (user_cmd + 8), + (android_cmd.used_len - 8)); + else if (strstr(user_cmd, "SETRTS ")) + ret = ath6kl_ioctl_setrts(vif, + (user_cmd + 7), + (android_cmd.used_len - 7)); + else if (strstr(user_cmd, "P2P_DEV_ADDR")) + ret = ath6kl_ioctl_p2p_dev_addr(vif, + user_cmd, + android_cmd.buf); + else if (strstr(user_cmd, "P2P_BEST_CHANNEL")) + ret = ath6kl_ioctl_p2p_best_chan(vif, + user_cmd, + android_cmd.buf); + else if (strstr(user_cmd, "ACL ")) + ret = ath6kl_ioctl_ap_acl(vif, + (user_cmd + 4), + NULL, + (android_cmd.used_len - 4)); + else if (strstr(user_cmd, "SET_AP_WPS_P2P_IE")) + ret = 0; /* To avoid AP/GO up stuck. */ + else { + ath6kl_dbg(ATH6KL_DBG_TRC, + "not yet support \"%s\"\n", + user_cmd); + + ret = -EOPNOTSUPP; + } + } + + kfree(user_cmd); + } + break; + } + case ATH6KL_IOCTL_STANDARD02: + { + struct ath6kl_ioctl_cmd ioctl_cmd; + + if (copy_from_user(&ioctl_cmd, + data, + sizeof(struct ath6kl_ioctl_cmd))) + ret = -EIO; + else { + switch (ioctl_cmd.subcmd) { + case ATH6KL_IOCTL_AP_APSD: + ath6kl_wmi_ap_set_apsd(vif->ar->wmi, + vif->fw_vif_idx, + ioctl_cmd.options); + break; + case ATH6KL_IOCTL_AP_INTRABSS: + vif->intra_bss = ioctl_cmd.options; + break; + default: + ret = -EOPNOTSUPP; + break; + } + } + break; + } + case ATH6KL_IOCTL_STANDARD03: + { + struct btcoex_ioctl btcoex_cmd; + char *user_cmd; + + if (copy_from_user(&btcoex_cmd, + data, + sizeof(struct btcoex_ioctl))) + ret = -EIO; + else { + user_cmd = kzalloc(btcoex_cmd.cmd_len, GFP_KERNEL); + if (!user_cmd) { + ret = -ENOMEM; + break; + } + if (copy_from_user(user_cmd, + btcoex_cmd.cmd, + btcoex_cmd.cmd_len)) + ret = -EIO; + else { + ret = ath6kl_wmi_send_btcoex_cmd(vif->ar, + (u8 *)user_cmd, btcoex_cmd.cmd_len); + } + kfree(user_cmd); + } + break; + } + default: +#if defined(ATHTST_SUPPORT) || defined(ACL_SUPPORT) + return ath6kl_ce_ioctl(dev, rq, cmd); +#else + ret = -EOPNOTSUPP; + break; +#endif + } + + return ret; +} + +static int ath6kl_ioctl_linkspeed(struct net_device *dev, + struct ifreq *rq, + int cmd) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + struct iwreq *req = (struct iwreq *)(rq); + char user_cmd[32]; + u8 macaddr[6]; + long left; + s32 rate = 0; + + /* Only AR6004 now */ + if (ar->target_type != TARGET_TYPE_AR6004) + return -EOPNOTSUPP; + + if ((!req->u.data.pointer) || (!req->u.data.length)) + return -EFAULT; + + memset(user_cmd, 0, 32); + if (copy_from_user(user_cmd, + req->u.data.pointer, + req->u.data.length)) + return -EFAULT; + + if (_string_to_mac(user_cmd, req->u.data.length, macaddr)) + return -EFAULT; + + if (down_interruptible(&ar->sem)) + return -EBUSY; + +#ifdef CONFIG_ANDROID + /* + * WAR : Framework always use p2p0 to query linkspeed and here transfer + * to correct P2P-GO/P2P-Client interface. + */ + if ((ar->p2p) && + (!ar->p2p_compat) && + (ar->p2p_concurrent) && + (ar->p2p_dedicate)) { + vif = ath6kl_get_vif_by_index(ar, ar->vif_max - 2); + if (!vif) { + up(&ar->sem); + return -EFAULT; + } + } +#endif + + set_bit(STATS_UPDATE_PEND, &vif->flags); + + if (ath6kl_wmi_get_stats_cmd(ar->wmi, vif->fw_vif_idx) != 0) { + up(&ar->sem); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(STATS_UPDATE_PEND, + &vif->flags), + WMI_TIMEOUT); + + up(&ar->sem); + + if (left == 0) + return -ETIMEDOUT; + else if (left < 0) + return left; + + memset(user_cmd, 0, 32); + if (vif->nw_type == AP_NETWORK) { + struct wmi_ap_mode_stat *ap = &vif->ap_stats; + struct ath6kl_sta *conn; + + conn = ath6kl_find_sta(vif, macaddr); + if (conn) { + for (left = 0; left < AP_MAX_NUM_STA; left++) { + if (conn->aid == ap->sta[left].aid) { + rate = ath6kl_wmi_get_rate_ar6004( + ap->sta[left].tx_ucast_rate); + break; + } + } + + WARN_ON(left == AP_MAX_NUM_STA); + } + } else + rate = vif->target_stats.tx_ucast_rate; + + snprintf(user_cmd, 32, "%u", rate / 1000); + req->u.data.length = strlen(user_cmd); + user_cmd[req->u.data.length] = '\0'; + + return copy_to_user(req->u.data.pointer, + user_cmd, + req->u.data.length + 1) ? -EFAULT : 0; +} + +int ath6kl_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct ath6kl *ar = ath6kl_priv(dev); + int ret = true; + s8 *userdata; + + /* + * ioctl operations may have to wait for the Target, so we cannot + * hold rtnl. Prevent the device from disappearing under us and + * release the lock during the ioctl operation. + */ + dev_hold(dev); + rtnl_unlock(); + + switch (cmd) { + case ATH6KL_IOCTL_STANDARD01: /* Android privacy command */ + case ATH6KL_IOCTL_STANDARD02: /* supplicant escape purpose to + support WiFi-Direct Cert. */ + case ATH6KL_IOCTL_STANDARD03: /* BTC command */ + case ATH6KL_IOCTL_STANDARD12: /* hole, please reserved */ +#ifdef ATHTST_SUPPORT + case ATHCFG_WCMD_IOCTL: /* athtst */ +#else + case ATH6KL_IOCTL_STANDARD15: /* hole, please reserved */ +#endif +#ifdef CE_SUPPORT +case IEEE80211_IOCTL_KICKMAC: +#endif +#ifdef ACL_SUPPORT + case IEEE80211_IOCTL_SETPARAM: + case IEEE80211_IOCTL_GETPARAM: + case IEEE80211_IOCTL_SETMLME: + case IEEE80211_IOCTL_ADDMAC: + case IEEE80211_IOCTL_DELMAC: + case IEEE80211_IOCTL_GET_MACADDR: +#endif +#ifdef TX99_SUPPORT + case SIOCIOCTLTX99:/* TX99 */ +#else + case ATH6KL_IOCTL_STANDARD13: /* TX99 */ +#endif + ret = ath6kl_ioctl_standard(dev, rq, cmd); + break; + case ATH6KL_IOCTL_WEXT_PRIV26: /* endpoint loopback purpose */ + get_user(cmd, (int *)rq->ifr_data); + userdata = (char *)(((unsigned int *)rq->ifr_data)+1); + + switch (cmd) { + case ATH6KL_XIOCTL_TRAFFIC_ACTIVITY_CHANGE: + if (ar->htc_target != NULL) { + struct ath6kl_traffic_activity_change data; + if (copy_from_user(&data, + userdata, + sizeof(data))) { + ret = -EFAULT; + goto ioctl_done; + } + ath6kl_htc_indicate_activity_change( + ar->htc_target, + (u8)data.stream_id, + data.active ? + true : false); + } + break; + } + break; + case ATH6KL_IOCTL_WEXT_PRIV27: /* QCSAP (old) */ + case ATH6KL_IOCTL_WEXT_PRIV31: /* QCSAP */ + ret = ath6kl_ioctl_linkspeed(dev, rq, cmd); + break; + default: + ret = -EOPNOTSUPP; + goto ioctl_done; + } + +ioctl_done: + rtnl_lock(); /* restore rtnl state */ + dev_put(dev); + + return ret; +} + +static struct net_device_ops ath6kl_netdev_ops = { + .ndo_open = ath6kl_open, + .ndo_stop = ath6kl_close, + .ndo_start_xmit = ath6kl_start_tx, + .ndo_get_stats = ath6kl_get_stats, + .ndo_do_ioctl = ath6kl_ioctl, +}; + +void init_netdev(struct net_device *dev) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + + vif->needed_headroom = ETH_HLEN + + sizeof(struct ath6kl_llc_snap_hdr) + + sizeof(struct wmi_data_hdr) + + HTC_HDR_LENGTH + + WMI_MAX_TX_META_SZ + + ATH6KL_HTC_ALIGN_BYTES; + +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + dev->open = ath6kl_open; + dev->stop = ath6kl_close; + dev->hard_start_xmit = ath6kl_start_tx; + dev->get_stats = ath6kl_get_stats; + dev->do_ioctl = ath6kl_ioctl; +#else + dev->netdev_ops = &ath6kl_netdev_ops; + dev->needed_headroom = vif->needed_headroom; +#endif + + dev->destructor = free_netdev; + dev->watchdog_timeo = ATH6KL_TX_TIMEOUT; + + return; +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/msm.c b/drivers/net/wireless/ath/ath6kl-3.5/msm.c new file mode 100644 index 000000000000..d5ba9409fdd1 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/msm.c @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2004-2012 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 CONFIG_ANDROID_8960_SDIO +#include "core.h" + +/* BeginMMC polling stuff */ +#ifdef CONFIG_MMC_MSM_SDC3_POLLING +#define MMC_MSM_DEV "msm_sdcc.3" +#else +#define MMC_MSM_DEV "msm_sdcc.4" +#endif +/* End MMC polling stuff */ +#endif /* #ifdef CONFIG_ANDROID_8960_SDIO */ + +#include "core.h" +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ANDROID_8960_SDIO +#define GET_INODE_FROM_FILEP(filp) ((filp)->f_path.dentry->d_inode) + +int android_readwrite_file(const char *filename, char *rbuf, + const char *wbuf, size_t length) +{ + int ret = 0; + struct file *filp = (struct file *)-ENOENT; + mm_segment_t oldfs; + oldfs = get_fs(); + set_fs(KERNEL_DS); + + do { + int mode = (wbuf) ? O_RDWR : O_RDONLY; + filp = filp_open(filename, mode, S_IRUSR); + + if (IS_ERR(filp) || !filp->f_op) { + ret = -ENOENT; + break; + } + + if (!filp->f_op->write || !filp->f_op->read) { + filp_close(filp, NULL); + ret = -ENOENT; + break; + } + + if (length == 0) { + /* Read the length of the file only */ + struct inode *inode; + + inode = GET_INODE_FROM_FILEP(filp); + if (!inode) { + printk(KERN_ERR + "android_readwrite_file: Error 2\n"); + ret = -ENOENT; + break; + } + ret = i_size_read(inode->i_mapping->host); + break; + } + + if (wbuf) { + ret = filp->f_op->write( + filp, wbuf, length, &filp->f_pos); + if (ret < 0) { + printk(KERN_ERR + "android_readwrite_file: Error 3\n"); + break; + } + } else { + ret = filp->f_op->read( + filp, rbuf, length, &filp->f_pos); + if (ret < 0) { + printk(KERN_ERR + "android_readwrite_file: Error 4\n"); + break; + } + } + } while (0); + + if (!IS_ERR(filp)) + filp_close(filp, NULL); + + set_fs(oldfs); + printk(KERN_ERR "android_readwrite_file: ret=%d\n", ret); + + return ret; +} + + +static struct wifi_platform_data *wifi_control_data; +struct semaphore wifi_control_sem; + +int wifi_set_power(int on, unsigned long msec) +{ + if (wifi_control_data && wifi_control_data->set_power) + wifi_control_data->set_power(on); + + if (msec) + mdelay(msec); + return 0; +} + +static int wifi_probe(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *)(pdev->dev.platform_data); + + wifi_control_data = wifi_ctrl; + + wifi_set_power(1, 0); /* Power On */ + + up(&wifi_control_sem); + return 0; +} + +static int wifi_remove(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *)(pdev->dev.platform_data); + + wifi_control_data = wifi_ctrl; + + wifi_set_power(0, 0); /* Power Off */ + + up(&wifi_control_sem); + return 0; +} + +static struct platform_driver wifi_device = { + .probe = wifi_probe, + .remove = wifi_remove, + .driver = { + .name = "ath6kl_power", + } +}; + +void __init ath6kl_sdio_init_msm(void) +{ + char buf[3]; + int length; + int ret; + + sema_init(&wifi_control_sem, 1); + down(&wifi_control_sem); + + ret = platform_driver_probe(&wifi_device, wifi_probe); + if (ret) { + printk(KERN_INFO "platform_driver_register failed\n"); + return; + } + + /* Waiting callback after platform_driver_register */ + if (down_timeout(&wifi_control_sem, msecs_to_jiffies(5000)) != 0) { + ret = -EINVAL; + printk(KERN_INFO "platform_driver_register timeout\n"); + return; + } + + length = snprintf(buf, sizeof(buf), "%d\n", 1 ? 1 : 0); + android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV + "/polling", NULL, buf, length); + length = snprintf(buf, sizeof(buf), "%d\n", 0 ? 1 : 0); + android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV + "/polling", NULL, buf, length); + + mdelay(500); +} + +void __exit ath6kl_sdio_exit_msm(void) +{ + char buf[3]; + int length; + int ret; + + platform_driver_unregister(&wifi_device); + + /* Waiting callback after platform_driver_register */ + if (down_timeout(&wifi_control_sem, msecs_to_jiffies(5000)) != 0) { + ret = -EINVAL; + printk(KERN_INFO "platform_driver_unregister timeout\n"); + return; + } + + length = snprintf(buf, sizeof(buf), "%d\n", 1 ? 1 : 0); + /* fall back to polling */ + android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV + "/polling", NULL, buf, length); + length = snprintf(buf, sizeof(buf), "%d\n", 0 ? 1 : 0); + /* fall back to polling */ + android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV + "/polling", NULL, buf, length); + mdelay(1000); + +} +#else + +#ifdef ATH6KL_BUS_VOTE +static u32 bus_perf_client; +static struct msm_bus_scale_pdata *ath6kl_bus_scale_pdata; + +struct ath6kl_power_vreg_data { + /* voltage regulator handle */ + struct regulator *reg; + + /* regulator name */ + const char *name; + + /* voltage levels to be set */ + unsigned int low_vol_level; + unsigned int high_vol_level; + + /* + * is set voltage supported for this regulator? + * false => set voltage is not supported + * true => set voltage is supported + * + * Some regulators (like gpio-regulators, LVS (low voltage swtiches) + * PMIC regulators) dont have the capability to call + * regulator_set_voltage or regulator_set_optimum_mode + * Use this variable to indicate if its a such regulator or not + */ + bool set_voltage_sup; + + /* is this regulator enabled? */ + bool is_enabled; +}; + +struct ath6kl_platform_data { + struct platform_device *pdev; + struct ath6kl_power_vreg_data *wifi_chip_pwd; +}; + +struct ath6kl_platform_data *gpdata; + +#define MAX_PROP_SIZE 32 +static int ath6kl_dt_parse_vreg_info(struct device *dev, + struct ath6kl_power_vreg_data **vreg_data, const char *vreg_name) +{ + int len, ret = 0; + const __be32 *prop; + char prop_name[MAX_PROP_SIZE]; + struct ath6kl_power_vreg_data *vreg; + struct device_node *np = dev->of_node; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "vreg dev tree parse for %s\n", vreg_name); + + snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name); + if (of_parse_phandle(np, prop_name, 0)) { + vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + ath6kl_err("No memory for vreg: %s\n", vreg_name); + ret = -ENOMEM; + goto err; + } + + vreg->name = vreg_name; + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-voltage-level", vreg_name); + prop = of_get_property(np, prop_name, &len); + if (!prop || (len != (2 * sizeof(__be32)))) { + ath6kl_dbg(ATH6KL_DBG_BOOT, "%s %s property\n", + prop ? "invalid format" : "no", prop_name); + } else { + vreg->low_vol_level = be32_to_cpup(&prop[0]); + vreg->high_vol_level = be32_to_cpup(&prop[1]); + } + + *vreg_data = vreg; + ath6kl_dbg(ATH6KL_DBG_BOOT, "%s: vol=[%d %d]uV\n", + vreg->name, vreg->low_vol_level, + vreg->high_vol_level); + } else + ath6kl_info("%s: is not provided in device tree\n", vreg_name); + +err: + return ret; +} + +static int ath6kl_vreg_disable(struct ath6kl_power_vreg_data *vreg) +{ + int rc = 0; + + if (!vreg) + return rc; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "vreg_disable for : %s\n", vreg->name); + + if (vreg->is_enabled) { + rc = regulator_disable(vreg->reg); + if (rc) { + ath6kl_err("regulator_disable(%s) failed. rc=%d\n", + vreg->name, rc); + goto out; + } + vreg->is_enabled = false; + + if (vreg->set_voltage_sup) { + /* Set the min voltage to 0 */ + rc = regulator_set_voltage(vreg->reg, + 0, + vreg->high_vol_level); + if (rc) { + ath6kl_err("vreg_set_vol(%s) failed rc=%d\n", + vreg->name, rc); + goto out; + + } + } + } + +out: + return rc; +} + +static int ath6kl_vreg_init(struct ath6kl_power_vreg_data *vreg) +{ + int rc = 0; + struct device *dev = &(gpdata->pdev->dev); + + ath6kl_dbg(ATH6KL_DBG_BOOT, "vreg_get for : %s\n", vreg->name); + + /* Get the regulator handle */ + vreg->reg = regulator_get(dev, vreg->name); + if (IS_ERR(vreg->reg)) { + rc = PTR_ERR(vreg->reg); + ath6kl_err("%s: regulator_get(%s) failed. rc=%d\n", + __func__, vreg->name, rc); + goto out; + } + + if ((regulator_count_voltages(vreg->reg) > 0) + && (vreg->low_vol_level) && (vreg->high_vol_level)) + vreg->set_voltage_sup = 1; + +out: + return rc; +} + +static int ath6kl_vreg_enable(struct ath6kl_power_vreg_data *vreg) +{ + int rc = 0; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "vreg_en for : %s\n", vreg->name); + + if (!vreg->is_enabled) { + if (vreg->set_voltage_sup) { + rc = regulator_set_voltage(vreg->reg, + vreg->low_vol_level, + vreg->high_vol_level); + if (rc) { + ath6kl_err("vreg_set_vol(%s) failed rc=%d\n", + vreg->name, rc); + goto out; + } + } + + rc = regulator_enable(vreg->reg); + if (rc) { + ath6kl_err("regulator_enable(%s) failed. rc=%d\n", + vreg->name, rc); + goto out; + } + vreg->is_enabled = true; + } +out: + return rc; +} + +static int ath6kl_configure_vreg(struct ath6kl_power_vreg_data *vreg) +{ + int rc = 0; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "config %s\n", vreg->name); + + /* Get the regulator handle for vreg */ + if (!(vreg->reg)) { + rc = ath6kl_vreg_init(vreg); + if (rc < 0) + return rc; + } + + rc = ath6kl_vreg_enable(vreg); + if (rc < 0) + return rc; + + return rc; +} + +static int ath6kl_platform_power(struct ath6kl_platform_data *pdata, int on) +{ + int rc = 0; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "%s on: %d\n", __func__, on); + + if (on) { + rc = ath6kl_configure_vreg(pdata->wifi_chip_pwd); + if (rc < 0) { + ath6kl_err("power on chip_pwd error\n"); + goto chip_pwd_fail; + } + } + else { + rc = ath6kl_vreg_disable(pdata->wifi_chip_pwd); + } + + return rc; + +chip_pwd_fail: + ath6kl_vreg_disable(pdata->wifi_chip_pwd); + + return rc; +} + +static int ath6kl_hsic_probe(struct platform_device *pdev) +{ + struct ath6kl_platform_data *pdata = NULL; + struct device *dev = &pdev->dev; + int ret = 0; + + ath6kl_bus_scale_pdata = msm_bus_cl_get_pdata(pdev); + bus_perf_client = msm_bus_scale_register_client(ath6kl_bus_scale_pdata); + msm_bus_scale_client_update_request(bus_perf_client, 4); + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + + if (!pdata) { + ath6kl_err("%s: Could not allocate memory for platform data\n", + __func__); + return -ENOMEM; + } + + if (ath6kl_dt_parse_vreg_info(dev, &pdata->wifi_chip_pwd, + "qca,wifi-chip-pwd") != 0) { + ath6kl_err("%s: parse vreg info error\n", __func__); + goto err; + } + + pdata->pdev = pdev; + platform_set_drvdata(pdev, pdata); + gpdata = pdata; + + if (pdata->wifi_chip_pwd != NULL) + ret = ath6kl_platform_power(pdata, 1); + + return ret; + +err: + if (pdata != NULL) + devm_kfree(dev, pdata); + + return -EINVAL; +} + +static int ath6kl_hsic_remove(struct platform_device *pdev) +{ + struct ath6kl_platform_data *pdata = platform_get_drvdata(pdev); + + msm_bus_scale_client_update_request(bus_perf_client, 1); + if (bus_perf_client) + msm_bus_scale_unregister_client(bus_perf_client); + + if (pdata->wifi_chip_pwd != NULL) { + ath6kl_platform_power(pdata, 0); + + if (pdata->wifi_chip_pwd->reg) + regulator_put(pdata->wifi_chip_pwd->reg); + } + + return 0; +} + +static const struct of_device_id ath6kl_hsic_dt_match[] = { + { .compatible = "qca,ar6004-hsic",}, + {} +}; + +MODULE_DEVICE_TABLE(of, ath6kl_hsic_dt_match); + +static struct platform_driver ath6kl_hsic_device = { + .probe = ath6kl_hsic_probe, + .remove = ath6kl_hsic_remove, + .driver = { + .name = "ath6kl_hsic", + .of_match_table = ath6kl_hsic_dt_match, + } +}; + +int ath6kl_hsic_init_msm(void) +{ + int ret; + + ret = platform_driver_register(&ath6kl_hsic_device); + + return ret; +} + +void ath6kl_hsic_exit_msm(void) +{ + platform_driver_unregister(&ath6kl_hsic_device); +} + +#endif /* #ifdef ATH6KL_BUS_VOTE */ + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/p2p.c b/drivers/net/wireless/ath/ath6kl-3.5/p2p.c new file mode 100644 index 000000000000..3c9d68355466 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/p2p.c @@ -0,0 +1,2270 @@ +/* + * Copyright (c) 2004-2012 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "htc-ops.h" +#include "debug.h" + +struct p2p_ps_info *ath6kl_p2p_ps_init(struct ath6kl_vif *vif) +{ + struct p2p_ps_info *p2p_ps; + + p2p_ps = kzalloc(sizeof(struct p2p_ps_info), GFP_KERNEL); + if (!p2p_ps) { + ath6kl_err("failed to alloc memory for p2p_ps\n"); + return NULL; + } + + p2p_ps->vif = vif; + spin_lock_init(&p2p_ps->p2p_ps_lock); + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps init %p type %d\n", + vif, + vif->wdev.iftype); + + return p2p_ps; +} + +void ath6kl_p2p_ps_deinit(struct ath6kl_vif *vif) +{ + struct p2p_ps_info *p2p_ps = vif->p2p_ps_info_ctx; + + if (p2p_ps) { + spin_lock(&p2p_ps->p2p_ps_lock); + if (p2p_ps->go_last_beacon_app_ie != NULL) + kfree(p2p_ps->go_last_beacon_app_ie); + + if (p2p_ps->go_last_noa_ie != NULL) + kfree(p2p_ps->go_last_noa_ie); + + if (p2p_ps->go_working_buffer != NULL) + kfree(p2p_ps->go_working_buffer); + spin_unlock(&p2p_ps->p2p_ps_lock); + + kfree(p2p_ps); + } + + vif->p2p_ps_info_ctx = NULL; + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps deinit %p\n", + vif); + + return; +} + +int ath6kl_p2p_ps_reset_noa(struct p2p_ps_info *p2p_ps) +{ + if (!p2p_ps) + return -1; + + /* + * This could happend if NOA_INFO event is later than + * GO's DISCONNECT event. + */ + if (p2p_ps->vif->wdev.iftype != NL80211_IFTYPE_P2P_GO) { + /* + * For P2P-Compat mode, need to clear target's + * NOA if the target not to reset it after + * P2P-GO teardown. + */ + if (!p2p_ps->vif->ar->p2p_compat) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "failed to reset P2P-GO noa, %p\n", p2p_ps); + + return -1; + } + } + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps reset NoA %p index %d\n", + p2p_ps->vif, + p2p_ps->go_noa.index); + + spin_lock(&p2p_ps->p2p_ps_lock); + p2p_ps->go_flags &= ~ATH6KL_P2P_PS_FLAGS_NOA_ENABLED; + p2p_ps->go_noa.index++; + p2p_ps->go_noa_enable_idx = 0; + memset(p2p_ps->go_noa.noas, + 0, + sizeof(struct ieee80211_p2p_noa_descriptor) * + ATH6KL_P2P_PS_MAX_NOA_DESCRIPTORS); + spin_unlock(&p2p_ps->p2p_ps_lock); + + return 0; +} + +int ath6kl_p2p_ps_setup_noa(struct p2p_ps_info *p2p_ps, + int noa_id, + u8 count_type, + u32 interval, + u32 start_offset, + u32 duration) +{ + struct ieee80211_p2p_noa_descriptor *noa_descriptor; + + if ((!p2p_ps) || + (p2p_ps->vif->wdev.iftype != NL80211_IFTYPE_P2P_GO)) { + ath6kl_err("failed to setup P2P-GO noa\n"); + return -1; + } + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps setup NoA %p idx %d ct %d intval %x so %x dur %x\n", + p2p_ps->vif, + noa_id, + count_type, + interval, + start_offset, + duration); + + spin_lock(&p2p_ps->p2p_ps_lock); + if (noa_id < ATH6KL_P2P_PS_MAX_NOA_DESCRIPTORS) { + noa_descriptor = &p2p_ps->go_noa.noas[noa_id]; + + noa_descriptor->count_or_type = count_type; + noa_descriptor->interval = interval; + noa_descriptor->start_or_offset = start_offset; + noa_descriptor->duration = duration; + } else { + spin_unlock(&p2p_ps->p2p_ps_lock); + ath6kl_err("wrong NoA index %d\n", noa_id); + + return -2; + } + + p2p_ps->go_noa_enable_idx |= (1 << noa_id); + p2p_ps->go_flags |= ATH6KL_P2P_PS_FLAGS_NOA_ENABLED; + spin_unlock(&p2p_ps->p2p_ps_lock); + + return 0; +} + +int ath6kl_p2p_ps_reset_opps(struct p2p_ps_info *p2p_ps) +{ + if ((!p2p_ps) || + (p2p_ps->vif->wdev.iftype != NL80211_IFTYPE_P2P_GO)) { + ath6kl_err("failed to reset P2P-GO OppPS\n"); + return -1; + } + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps reset OppPS %p index %d\n", + p2p_ps->vif, + p2p_ps->go_noa.index); + + spin_lock(&p2p_ps->p2p_ps_lock); + p2p_ps->go_flags &= ~ATH6KL_P2P_PS_FLAGS_OPPPS_ENABLED; + p2p_ps->go_noa.index++; + p2p_ps->go_noa.ctwindow_opps_param = 0; + spin_unlock(&p2p_ps->p2p_ps_lock); + + return 0; +} + +int ath6kl_p2p_ps_setup_opps(struct p2p_ps_info *p2p_ps, + u8 enabled, + u8 ctwindows) +{ + if ((!p2p_ps) || + (p2p_ps->vif->wdev.iftype != NL80211_IFTYPE_P2P_GO)) { + ath6kl_err("failed to setup P2P-GO noa\n"); + return -1; + } + + WARN_ON(enabled && (!(ctwindows & 0x7f))); + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps setup OppPS %p enabled %d ctwin %d\n", + p2p_ps->vif, + enabled, + ctwindows); + + spin_lock(&p2p_ps->p2p_ps_lock); + if (enabled) + p2p_ps->go_noa.ctwindow_opps_param = (0x80 | + (ctwindows & 0x7f)); + else + p2p_ps->go_noa.ctwindow_opps_param = 0; + p2p_ps->go_flags |= ATH6KL_P2P_PS_FLAGS_OPPPS_ENABLED; + spin_unlock(&p2p_ps->p2p_ps_lock); + + return 0; +} + +int ath6kl_p2p_ps_update_notif(struct p2p_ps_info *p2p_ps) +{ + struct ath6kl_vif *vif; + struct ieee80211_p2p_noa_ie *noa_ie; + struct ieee80211_p2p_noa_descriptor *noa_descriptor; + int i, idx, len, ret = 0; + u8 *buf; + + WARN_ON(!p2p_ps); + + vif = p2p_ps->vif; + + p2p_ps->go_noa_notif_cnt++; + + spin_lock(&p2p_ps->p2p_ps_lock); + if ((p2p_ps->go_flags & ATH6KL_P2P_PS_FLAGS_NOA_ENABLED) || + (p2p_ps->go_flags & ATH6KL_P2P_PS_FLAGS_OPPPS_ENABLED)) { + WARN_ON((p2p_ps->go_flags & ATH6KL_P2P_PS_FLAGS_NOA_ENABLED) && + (!p2p_ps->go_noa_enable_idx)); + + len = p2p_ps->go_last_beacon_app_ie_len + + sizeof(struct ieee80211_p2p_noa_ie); + + buf = kmalloc(len, GFP_ATOMIC); + if (buf == NULL) { + spin_unlock(&p2p_ps->p2p_ps_lock); + + return -ENOMEM; + } + + /* Append NoA IE after user's IEs. */ + memcpy(buf, + p2p_ps->go_last_beacon_app_ie, + p2p_ps->go_last_beacon_app_ie_len); + + noa_ie = (struct ieee80211_p2p_noa_ie *) + (buf + p2p_ps->go_last_beacon_app_ie_len); + noa_ie->element_id = WLAN_EID_VENDOR_SPECIFIC; + noa_ie->oui = cpu_to_be32((WLAN_OUI_WFA << 8) | + (WLAN_OUI_TYPE_WFA_P2P)); + noa_ie->attr = IEEE80211_P2P_ATTR_NOTICE_OF_ABSENCE; + noa_ie->noa_info.index = p2p_ps->go_noa.index; + noa_ie->noa_info.ctwindow_opps_param = + p2p_ps->go_noa.ctwindow_opps_param; + + idx = 0; + for (i = 0; i < ATH6KL_P2P_PS_MAX_NOA_DESCRIPTORS; i++) { + if (p2p_ps->go_noa_enable_idx & (1 << i)) { + noa_descriptor = + &noa_ie->noa_info.noas[idx++]; + noa_descriptor->count_or_type = + p2p_ps->go_noa.noas[i].count_or_type; + noa_descriptor->duration = + cpu_to_le32( + p2p_ps->go_noa.noas[i].duration); + noa_descriptor->interval = + cpu_to_le32( + p2p_ps->go_noa.noas[i].interval); + noa_descriptor->start_or_offset = + cpu_to_le32( + p2p_ps->go_noa.noas[i].start_or_offset); + } + + } + + /* Update length */ + noa_ie->attr_len = cpu_to_le16(2 + + (sizeof(struct ieee80211_p2p_noa_descriptor) * idx)); + noa_ie->len = noa_ie->attr_len + 4 + 1 + 2; /* OUI, attr, len */ + len = p2p_ps->go_last_beacon_app_ie_len + (noa_ie->len + 2); + + /* Backup NoA IE for origional code path if need. */ + p2p_ps->go_last_noa_ie_len = 0; + + if (p2p_ps->go_last_noa_ie != NULL) + kfree(p2p_ps->go_last_noa_ie); + p2p_ps->go_last_noa_ie = kmalloc(noa_ie->len + 2, GFP_ATOMIC); + if (p2p_ps->go_last_noa_ie) { + p2p_ps->go_last_noa_ie_len = noa_ie->len + 2; + memcpy(p2p_ps->go_last_noa_ie, + noa_ie, + p2p_ps->go_last_noa_ie_len); + } + + spin_unlock(&p2p_ps->p2p_ps_lock); + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps update app IE %p f %x idx %d ie %d len %d\n", + vif, + p2p_ps->go_flags, + idx, + noa_ie->len, + len); + } else { + /* + * Ignore if FW disable NoA/OppPS but actually no NoA/OppPS + * currently. + */ + if (p2p_ps->go_last_beacon_app_ie_len == 0) { + spin_unlock(&p2p_ps->p2p_ps_lock); + + return ret; + } + + /* Remove NoA IE. */ + p2p_ps->go_last_noa_ie_len = 0; + + if (p2p_ps->go_last_noa_ie != NULL) { + kfree(p2p_ps->go_last_noa_ie); + p2p_ps->go_last_noa_ie = NULL; + } + + buf = kmalloc(p2p_ps->go_last_beacon_app_ie_len, GFP_ATOMIC); + if (buf == NULL) { + spin_unlock(&p2p_ps->p2p_ps_lock); + + return -ENOMEM; + } + + /* Back to origional Beacon IEs. */ + len = p2p_ps->go_last_beacon_app_ie_len; + memcpy(buf, + p2p_ps->go_last_beacon_app_ie, + len); + + spin_unlock(&p2p_ps->p2p_ps_lock); + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps update app IE %p f %x beacon_ie %p len %d\n", + vif, + p2p_ps->go_flags, + p2p_ps->go_last_beacon_app_ie, + p2p_ps->go_last_beacon_app_ie_len); + } + + /* + * Only need to update Beacon's IE. The ProbeResp'q IE is settled + * while sending. + */ + ret = ath6kl_wmi_set_appie_cmd(vif->ar->wmi, + vif->fw_vif_idx, + WMI_FRAME_BEACON, + buf, + len); + + kfree(buf); + + return ret; +} + +void ath6kl_p2p_ps_user_app_ie(struct p2p_ps_info *p2p_ps, + u8 mgmt_frm_type, + u8 **ie, + int *len) +{ + if ((!p2p_ps) || (p2p_ps->vif->wdev.iftype != NL80211_IFTYPE_P2P_GO)) + return; + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps hook app IE %p f %x mgmt_frm_type %d len %d\n", + p2p_ps->vif, + p2p_ps->go_flags, + mgmt_frm_type, + *len); + + if (mgmt_frm_type == WMI_FRAME_BEACON) { + WARN_ON((*len) == 0); + + spin_lock(&p2p_ps->p2p_ps_lock); + p2p_ps->go_last_beacon_app_ie_len = 0; + + if (p2p_ps->go_last_beacon_app_ie != NULL) + kfree(p2p_ps->go_last_beacon_app_ie); + + p2p_ps->go_last_beacon_app_ie = kmalloc(*len, GFP_ATOMIC); + if (p2p_ps->go_last_beacon_app_ie == NULL) { + spin_unlock(&p2p_ps->p2p_ps_lock); + return; + } + + /* Update to the latest one. */ + p2p_ps->go_last_beacon_app_ie_len = *len; + memcpy(p2p_ps->go_last_beacon_app_ie, *ie, *len); + + spin_unlock(&p2p_ps->p2p_ps_lock); + } else if (mgmt_frm_type == WMI_FRAME_PROBE_RESP) { + /* Assume non-zero means P2P node. */ + if ((*len) == 0) + return; + } + + /* Hack : Change ie/len to let caller use the new one. */ + spin_lock(&p2p_ps->p2p_ps_lock); + if ((p2p_ps->go_flags & ATH6KL_P2P_PS_FLAGS_NOA_ENABLED) || + (p2p_ps->go_flags & ATH6KL_P2P_PS_FLAGS_OPPPS_ENABLED)) { + /* + * Append the last NoA IE to *ie and + * also update *len to let caller + * use the new one. + */ + WARN_ON(!p2p_ps->go_last_noa_ie); + + if (p2p_ps->go_working_buffer != NULL) + kfree(p2p_ps->go_working_buffer); + + p2p_ps->go_working_buffer = + kmalloc((p2p_ps->go_last_noa_ie_len + *len), + GFP_ATOMIC); + + if (p2p_ps->go_working_buffer) { + if (*len) + memcpy(p2p_ps->go_working_buffer, *ie, *len); + memcpy(p2p_ps->go_working_buffer + (*len), + p2p_ps->go_last_noa_ie, + p2p_ps->go_last_noa_ie_len); + + if (mgmt_frm_type == WMI_FRAME_PROBE_RESP) { + /* caller will release it. */ + kfree(*ie); + *ie = p2p_ps->go_working_buffer; + p2p_ps->go_working_buffer = NULL; + } else + *ie = p2p_ps->go_working_buffer; + *len += p2p_ps->go_last_noa_ie_len; + } + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "p2p_ps change app IE len -> %d\n", + *len); + } + spin_unlock(&p2p_ps->p2p_ps_lock); + + return; +} + +int ath6kl_p2p_utils_trans_porttype(enum nl80211_iftype type, + u8 *opmode, + u8 *subopmode) +{ + int ret = 0; + + /* + * nl80211.h support NL80211_IFTYPE_P2P_DEVICE from kernel 3.7, + * but not yet see any applications to use it now. + * To avoid conflict with old nl80211.h and change namming + * with postfix _QCA. + */ + if (type == NL80211_IFTYPE_P2P_DEVICE_QCA) { + *opmode = HI_OPTION_FW_MODE_BSS_STA; + *subopmode = HI_OPTION_FW_SUBMODE_P2PDEV; + } else { + switch (type) { + case NL80211_IFTYPE_AP: + *opmode = HI_OPTION_FW_MODE_AP; + *subopmode = HI_OPTION_FW_SUBMODE_NONE; + break; + case NL80211_IFTYPE_STATION: + *opmode = HI_OPTION_FW_MODE_BSS_STA; + *subopmode = HI_OPTION_FW_SUBMODE_NONE; + break; + case NL80211_IFTYPE_P2P_CLIENT: + *opmode = HI_OPTION_FW_MODE_BSS_STA; + *subopmode = HI_OPTION_FW_SUBMODE_P2PCLIENT; + break; + case NL80211_IFTYPE_P2P_GO: + *opmode = HI_OPTION_FW_MODE_BSS_STA; + *subopmode = HI_OPTION_FW_SUBMODE_P2PGO; + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + default: + ath6kl_err("error interface type %d\n", type); + ret = -1; + break; + } + } + + return ret; +} + +static inline void _revert_ht_cap(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; +#ifdef CONFIG_ATH6KL_DEBUG + struct ht_cap_param *htCapParam; + + htCapParam = &ar->debug.ht_cap_param[IEEE80211_BAND_5GHZ]; + if (htCapParam->isConfig) + ath6kl_wmi_set_ht_cap_cmd(ar->wmi, + vif->fw_vif_idx, + htCapParam->band, + htCapParam->chan_width_40M_supported, + htCapParam->short_GI, + htCapParam->intolerance_40MHz); + else +#endif + ath6kl_wmi_set_ht_cap_cmd(ar->wmi, + vif->fw_vif_idx, + A_BAND_5GHZ, + ATH6KL_5GHZ_HT40_DEF_WIDTH, + ATH6KL_5GHZ_HT40_DEF_SGI, + ATH6KL_5GHZ_HT40_DEF_INTOLR40); + + return; +} + +int ath6kl_p2p_utils_init_port(struct ath6kl_vif *vif, + enum nl80211_iftype type) +{ + struct ath6kl *ar = vif->ar; + u8 fw_vif_idx = vif->fw_vif_idx; + u8 opmode, subopmode; + long left; + u8 skip_vif = 1; + + if (ar->p2p_compat) + return 0; + + /* + * Only need to do this if virtual interface used but bypass + * vif_max=2 case. + * This case suppose be used only for special P2P purpose that is + * without dedicated P2P-Device. + * + * 1st interface always be created by driver init phase and WMI + * interface not yet ready. Actually, we don't need to reset it + * because current design + * always STA interface in firmware and host sides. + */ + if (ar->p2p_dedicate) + skip_vif = 2; + + if ((ar->vif_max > skip_vif) && fw_vif_idx) { + if (ar->p2p_dedicate && (fw_vif_idx == (ar->vif_max - 1))) + type = NL80211_IFTYPE_P2P_DEVICE_QCA; + + if (ath6kl_p2p_utils_trans_porttype(type, + &opmode, + &subopmode) == 0) { + /* Delete it first. */ + if (type != NL80211_IFTYPE_P2P_DEVICE_QCA) { + set_bit(PORT_STATUS_PEND, &vif->flags); + if (ath6kl_wmi_del_port_cmd(ar->wmi, + fw_vif_idx, + fw_vif_idx)) + return -EIO; + + left = wait_event_interruptible_timeout( + ar->event_wq, + !test_bit(PORT_STATUS_PEND, + &vif->flags), + WMI_TIMEOUT/10); + WARN_ON(left <= 0); + } + + /* Only support exectly id-to-id mapping. */ + set_bit(PORT_STATUS_PEND, &vif->flags); + if (ath6kl_wmi_add_port_cmd(ar->wmi, + vif, + opmode, + subopmode)) + return -EIO; + + left = wait_event_interruptible_timeout( + ar->event_wq, + !test_bit(PORT_STATUS_PEND, + &vif->flags), + WMI_TIMEOUT/10); + WARN_ON(left <= 0); + + /* WAR: Revert HT CAP, only for AP/P2P-GO cases. */ + if ((type == NL80211_IFTYPE_AP) || + (type == NL80211_IFTYPE_P2P_GO)) + _revert_ht_cap(vif); + } else + return -ENOTSUPP; + } + + return 0; +} + +int ath6kl_p2p_utils_check_port(struct ath6kl_vif *vif, + u8 port_id) +{ + if (test_bit(PORT_STATUS_PEND, &vif->flags)) { + WARN_ON(vif->fw_vif_idx != port_id); + + clear_bit(PORT_STATUS_PEND, &vif->flags); + wake_up(&vif->ar->event_wq); + } + + return 0; +} + +struct ath6kl_p2p_flowctrl *ath6kl_p2p_flowctrl_conn_list_init( + struct ath6kl *ar) +{ + struct ath6kl_p2p_flowctrl *p2p_flowctrl; + struct ath6kl_fw_conn_list *fw_conn; + int i; + + p2p_flowctrl = kzalloc(sizeof(struct ath6kl_p2p_flowctrl), GFP_KERNEL); + if (!p2p_flowctrl) { + ath6kl_err("failed to alloc memory for p2p_flowctrl\n"); + return NULL; + } + + p2p_flowctrl->ar = ar; + spin_lock_init(&p2p_flowctrl->p2p_flowctrl_lock); + p2p_flowctrl->sche_type = P2P_FLOWCTRL_SCHE_TYPE_CONNECTION; + + for (i = 0; i < NUM_CONN; i++) { + fw_conn = &p2p_flowctrl->fw_conn_list[i]; + INIT_LIST_HEAD(&fw_conn->conn_queue); + INIT_LIST_HEAD(&fw_conn->re_queue); + fw_conn->connect_status = 0; + fw_conn->previous_can_send = true; + fw_conn->connId = ATH6KL_P2P_FLOWCTRL_NULL_CONNID; + fw_conn->parent_connId = ATH6KL_P2P_FLOWCTRL_NULL_CONNID; + memset(fw_conn->mac_addr, 0, ETH_ALEN); + + fw_conn->sche_tx = 0; + fw_conn->sche_re_tx = 0; + fw_conn->sche_re_tx_aging = 0; + fw_conn->sche_tx_queued = 0; + } + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl init %p NUM_CONN %d type %d\n", + ar, + NUM_CONN, + p2p_flowctrl->sche_type); + + return p2p_flowctrl; +} + +void ath6kl_p2p_flowctrl_conn_list_deinit(struct ath6kl *ar) +{ + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + int i; + + if (p2p_flowctrl) { + /* check memory leakage */ + for (i = 0; i < NUM_CONN; i++) { + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + fw_conn = &p2p_flowctrl->fw_conn_list[i]; + if (fw_conn->sche_tx_queued != 0) { + ath6kl_err("memory leakage? %d,%d,%d,%d,%d\n", + i, + fw_conn->sche_tx, + fw_conn->sche_re_tx, + fw_conn->sche_re_tx_aging, + fw_conn->sche_tx_queued); + WARN_ON(1); + } + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + } + + kfree(p2p_flowctrl); + } + + ar->p2p_flowctrl_ctx = NULL; + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl deinit %p\n", + ar); + + return; +} + +/* before calling this, p2p_flowctrl_lock shall be acquired, + * pcontainer, preclaim shall be init by caller. + */ +void ath6kl_p2p_flowctrl_conn_collect_by_conn( + struct ath6kl_fw_conn_list *fw_conn, + struct list_head *pcontainer, + int *preclaim) +{ + struct htc_packet *packet, *tmp_pkt; + + if (!list_empty(&fw_conn->re_queue)) { + list_for_each_entry_safe(packet, + tmp_pkt, + &fw_conn->re_queue, + list) { + list_del(&packet->list); + packet->status = 0; + list_add_tail(&packet->list, pcontainer); + fw_conn->sche_tx_queued--; + *preclaim += 1; + } + } + + if (!list_empty(&fw_conn->conn_queue)) { + list_for_each_entry_safe(packet, + tmp_pkt, + &fw_conn->conn_queue, + list) { + list_del(&packet->list); + packet->status = 0; + list_add_tail(&packet->list, pcontainer); + fw_conn->sche_tx_queued--; + *preclaim += 1; + } + } +} + +void ath6kl_p2p_flowctrl_conn_list_cleanup(struct ath6kl *ar) +{ + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + struct list_head container; + int i, reclaim = 0; + + WARN_ON(!p2p_flowctrl); + + INIT_LIST_HEAD(&container); + + for (i = 0; i < NUM_CONN; i++) { + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + fw_conn = &p2p_flowctrl->fw_conn_list[i]; + ath6kl_p2p_flowctrl_conn_collect_by_conn(fw_conn, + &container, + &reclaim); + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + } + + ath6kl_tx_complete(ar->htc_target, &container); + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl cleanup %p reclaim %d\n", + ar, + reclaim); + + return; +} + +static u8 _find_parent_conn_id(struct ath6kl_p2p_flowctrl *p2p_flowctrl, + struct ath6kl_vif *hint_vif) +{ + struct ath6kl_fw_conn_list *fw_conn; + u8 connId = ATH6KL_P2P_FLOWCTRL_NULL_CONNID; + int i; + + /* Need protected in p2p_flowctrl_lock by caller. */ + fw_conn = &p2p_flowctrl->fw_conn_list[0]; + for (i = 0; i < NUM_CONN; i++, fw_conn++) { + if (fw_conn->connId == ATH6KL_P2P_FLOWCTRL_NULL_CONNID) + continue; + + if ((fw_conn->vif == hint_vif) && + (fw_conn->parent_connId == fw_conn->connId)) { + connId = fw_conn->connId; + break; + } + } + + return connId; +} + +void ath6kl_p2p_flowctrl_conn_list_cleanup_by_if( + struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + struct list_head container; + int i, reclaim = 0; + u8 vif_conn_id = ATH6KL_P2P_FLOWCTRL_NULL_CONNID; + + if (!p2p_flowctrl) + return; + + INIT_LIST_HEAD(&container); + + vif_conn_id = _find_parent_conn_id(p2p_flowctrl, vif); + + if (vif_conn_id == ATH6KL_P2P_FLOWCTRL_NULL_CONNID) + return; + + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + for (i = 0; i < NUM_CONN; i++) { + fw_conn = &p2p_flowctrl->fw_conn_list[i]; + + if (fw_conn->parent_connId != vif_conn_id) + continue; + + ath6kl_p2p_flowctrl_conn_collect_by_conn(fw_conn, + &container, + &reclaim); + } + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + ath6kl_tx_complete(ar->htc_target, &container); + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrlif cleanup %p reclaim %d\n", + ar, + reclaim); + + return; +} + +static inline bool _check_can_send(struct ath6kl *ar, + struct ath6kl_fw_conn_list *fw_conn) +{ + bool can_send = false; + + do { + if ((fw_conn->ocs) && !test_bit(SKIP_FLOWCTRL_EVENT, &ar->flag)) + break; + + can_send = true; + } while (false); + + return can_send; +} + +void ath6kl_p2p_flowctrl_netif_transition( + struct ath6kl *ar, u8 new_state) +{ + struct ath6kl_vif *vif; + + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + spin_unlock_bh(&ar->list_lock); + + if (new_state == ATH6KL_P2P_FLOWCTRL_NETIF_STOP && + test_bit(CONNECTED, &vif->flags)) + netif_stop_queue(vif->ndev); + else if (new_state == ATH6KL_P2P_FLOWCTRL_NETIF_WAKE && + (test_bit(CONNECTED, &vif->flags) || + test_bit(TESTMODE_EPPING, &ar->flag))) + netif_wake_queue(vif->ndev); + + spin_lock_bh(&ar->list_lock); + } + spin_unlock_bh(&ar->list_lock); + + return; +} + +void ath6kl_p2p_flowctrl_tx_schedule(struct ath6kl *ar) +{ + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + struct htc_packet *packet, *tmp_pkt; + int i; + + WARN_ON(!p2p_flowctrl); + + for (i = 0; i < NUM_CONN; i++) { + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + fw_conn = &p2p_flowctrl->fw_conn_list[i]; + /* Bypass this fw_conn if it not yet used. */ + if (fw_conn->connId == ATH6KL_P2P_FLOWCTRL_NULL_CONNID) { + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + continue; + } + + if (_check_can_send(ar, fw_conn)) { + if (!list_empty(&fw_conn->re_queue)) { + list_for_each_entry_safe(packet, + tmp_pkt, + &fw_conn->re_queue, + list) { + if (packet == NULL) + continue; + + list_del(&packet->list); + + if (packet->endpoint >= ENDPOINT_MAX) + continue; + + fw_conn->sche_re_tx--; + fw_conn->sche_tx_queued--; + + ath6kl_htc_tx(ar->htc_target, packet); + } + } + + if (!list_empty(&fw_conn->conn_queue)) { + list_for_each_entry_safe(packet, + tmp_pkt, + &fw_conn->conn_queue, + list) { + list_del(&packet->list); + if (packet == NULL) + continue; + + if (packet->endpoint >= ENDPOINT_MAX) + continue; + + fw_conn->sche_tx++; + fw_conn->sche_tx_queued--; + + ath6kl_htc_tx(ar->htc_target, packet); + } + } + } + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl schedule %p conId %d tx %d re_tx %d\n", + ar, + i, + fw_conn->sche_tx, + fw_conn->sche_re_tx); + } + + return; +} + +int ath6kl_p2p_flowctrl_tx_schedule_pkt(struct ath6kl *ar, + void *pkt) +{ + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + struct ath6kl_cookie *cookie = (struct ath6kl_cookie *)pkt; + int connId = cookie->htc_pkt.connid; + int ret = 0; + + WARN_ON(!p2p_flowctrl); + + if (connId == ATH6KL_P2P_FLOWCTRL_NULL_CONNID) { + ath6kl_err("p2p_flowctrl fail, NULL connId, just send??\n"); + + return 1; /* Just send it */ + /*return -1;*/ /* Drop it */ + } + + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + fw_conn = &p2p_flowctrl->fw_conn_list[connId]; + if (!_check_can_send(ar, fw_conn)) { + if (cookie->htc_pkt.info.tx.tag != + ATH6KL_PRI_DATA_PKT_TAG) { + list_add_tail(&cookie->htc_pkt.list, + &fw_conn->conn_queue); + } else { + list_add(&cookie->htc_pkt.list, + &fw_conn->re_queue); + fw_conn->sche_re_tx++; + } + fw_conn->sche_tx_queued++; + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + goto result; + } else if (!list_empty(&fw_conn->conn_queue) || + !list_empty(&fw_conn->re_queue)) { + if (cookie->htc_pkt.info.tx.tag != + ATH6KL_PRI_DATA_PKT_TAG) { + list_add_tail(&cookie->htc_pkt.list, + &fw_conn->conn_queue); + } else { + list_add(&cookie->htc_pkt.list, &fw_conn->re_queue); + fw_conn->sche_re_tx++; + } + + fw_conn->sche_tx_queued++; + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + ath6kl_p2p_flowctrl_tx_schedule(ar); + + goto result; + } else + ret = 1; + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + +result: + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl schedule pkt %p %s\n", + ar, + ((ret == 0) ? "queue" : "send")); + + return ret; +} + +void ath6kl_p2p_flowctrl_state_change(struct ath6kl *ar) +{ + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + struct htc_packet *packet, *tmp_pkt; + struct htc_endpoint *endpoint; + struct list_head *tx_queue, container; + int i, eid; + bool flowctrl_allowed; + + WARN_ON(!p2p_flowctrl); + + INIT_LIST_HEAD(&container); + + for (i = 0; i < NUM_CONN; i++) { + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + fw_conn = &p2p_flowctrl->fw_conn_list[i]; + flowctrl_allowed = _check_can_send(ar, fw_conn); + if (!flowctrl_allowed && fw_conn->previous_can_send) { + spin_lock_bh(&ar->htc_target->tx_lock); + for (eid = ENDPOINT_5; eid >= ENDPOINT_2; eid--) { + endpoint = &ar->htc_target->endpoint[eid]; + tx_queue = &endpoint->txq; + if (list_empty(tx_queue)) + continue; + + list_for_each_entry_safe(packet, + tmp_pkt, + tx_queue, + list) { + if (packet->connid != i) + continue; + + list_del(&packet->list); + if (packet->recycle_count > + ATH6KL_P2P_FLOWCTRL_RECYCLE_LIMIT) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "recycle cnt exceed\n"); + + packet->status = 0; + list_add_tail( + &packet->list, + &container); + + fw_conn->sche_re_tx_aging++; + } else { + packet->recycle_count++; + list_add_tail( + &packet->list, + &fw_conn->re_queue); + + fw_conn->sche_re_tx++; + fw_conn->sche_tx_queued++; + } + } + } + spin_unlock_bh(&ar->htc_target->tx_lock); + } + fw_conn->previous_can_send = flowctrl_allowed; + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + } + + ath6kl_p2p_flowctrl_netif_transition( + ar, ATH6KL_P2P_FLOWCTRL_NETIF_WAKE); + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl state_change %p re_tx %d re_tx_aging %d\n", + ar, + fw_conn->sche_re_tx, + fw_conn->sche_re_tx_aging); + + ath6kl_tx_complete(ar->htc_target, &container); + + return; +} + +void ath6kl_p2p_flowctrl_state_update(struct ath6kl *ar, + u8 numConn, + u8 ac_map[], + u8 ac_queue_depth[]) +{ + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + int i; + + WARN_ON(!p2p_flowctrl); + WARN_ON(numConn > NUM_CONN); + + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + p2p_flowctrl->p2p_flowctrl_event_cnt++; + for (i = 0; i < numConn; i++) { + fw_conn = &p2p_flowctrl->fw_conn_list[i]; + fw_conn->connect_status = ac_map[i]; + } + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl state_update %p ac_map %02x %02x %02x %02x\n", + ar, + ac_map[0], ac_map[1], ac_map[2], ac_map[3]); + + return; +} + +void ath6kl_p2p_flowctrl_set_conn_id(struct ath6kl_vif *vif, + u8 mac_addr[], + u8 connId) +{ + struct ath6kl *ar = vif->ar; + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + struct list_head container; + int reclaim = 0; + + WARN_ON(!p2p_flowctrl); + + INIT_LIST_HEAD(&container); + + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + fw_conn = &p2p_flowctrl->fw_conn_list[connId]; + if (mac_addr) { + if (fw_conn->sche_tx_queued != 0) { + ath6kl_p2p_flowctrl_conn_collect_by_conn(fw_conn, + &container, + &reclaim); + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + ath6kl_tx_complete(ar->htc_target, &container); + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + } + + fw_conn->vif = vif; + fw_conn->connId = connId; + memcpy(fw_conn->mac_addr, mac_addr, ETH_ALEN); + + /* + * Parent's connId of P2P-GO/P2P-Client/STA is self. + * Parent's connId of P2P-GO's Clients is P2P-GO. + */ + if ((vif->nw_type == AP_NETWORK) && + (memcmp(vif->ndev->dev_addr, mac_addr, ETH_ALEN))) { + /* P2P-GO's Client connection event. */ + fw_conn->parent_connId = + _find_parent_conn_id(p2p_flowctrl, vif); + } else { + /* P2P-GO/P2P-Client/STA connection event. */ + fw_conn->parent_connId = fw_conn->connId; + } + } else { + fw_conn->connId = ATH6KL_P2P_FLOWCTRL_NULL_CONNID; + fw_conn->parent_connId = ATH6KL_P2P_FLOWCTRL_NULL_CONNID; + fw_conn->connect_status = 0; + fw_conn->previous_can_send = true; + memset(fw_conn->mac_addr, 0, ETH_ALEN); + } + + fw_conn->sche_tx = 0; + fw_conn->sche_re_tx = 0; + fw_conn->sche_re_tx_aging = 0; + fw_conn->sche_tx_queued = 0; + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl set conn_id %p mode %d conId %d p_conId %d\n", + ar, + vif->nw_type, + connId, + fw_conn->parent_connId); + + return; +} + +u8 ath6kl_p2p_flowctrl_get_conn_id(struct ath6kl_vif *vif, + struct sk_buff *skb) +{ + struct ath6kl *ar = vif->ar; + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + struct ethhdr *ethhdr; + u8 *hint; + u8 connId = ATH6KL_P2P_FLOWCTRL_NULL_CONNID; + int i; + + if (!p2p_flowctrl) + return connId; + + ethhdr = (struct ethhdr *)(skb->data + sizeof(struct wmi_data_hdr)); + + if (vif->nw_type != AP_NETWORK) + hint = ethhdr->h_source; + else { + if (is_multicast_ether_addr(ethhdr->h_dest)) + hint = ethhdr->h_source; + else + hint = ethhdr->h_dest; + } + + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + fw_conn = &p2p_flowctrl->fw_conn_list[0]; + for (i = 0; i < NUM_CONN; i++, fw_conn++) { + if (fw_conn->connId == ATH6KL_P2P_FLOWCTRL_NULL_CONNID) + continue; + + if (memcmp(fw_conn->mac_addr, hint, ETH_ALEN) == 0) { + connId = fw_conn->connId; + break; + } + } + + /* Change to parent's. */ + if (p2p_flowctrl->sche_type == P2P_FLOWCTRL_SCHE_TYPE_INTERFACE) + connId = fw_conn->parent_connId; + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + ath6kl_dbg(ATH6KL_DBG_FLOWCTRL, + "p2p_flowctrl get con_id %d %02x:%02x:%02x:%02x:%02x:%02x\n", + connId, + hint[0], hint[1], hint[2], hint[3], hint[4], hint[5]); + + return connId; +} + +int ath6kl_p2p_flowctrl_stat(struct ath6kl *ar, + u8 *buf, int buf_len) +{ + struct ath6kl_p2p_flowctrl *p2p_flowctrl = ar->p2p_flowctrl_ctx; + struct ath6kl_fw_conn_list *fw_conn; + int i, len = 0; + + if ((!p2p_flowctrl) || (!buf)) + return 0; + + len += snprintf(buf + len, buf_len - len, "\n NUM_CONN : %d", + NUM_CONN); + len += snprintf(buf + len, buf_len - len, "\n SCHE_TYPE : %s", + (p2p_flowctrl->sche_type == P2P_FLOWCTRL_SCHE_TYPE_CONNECTION ? + "CONNECTION" : "INTERFACE")); + len += snprintf(buf + len, buf_len - len, "\n EVENT_CNT : %d", + p2p_flowctrl->p2p_flowctrl_event_cnt); + + len += snprintf(buf + len, buf_len - len, "\n NOA_UPDATE :"); + for (i = 0; i < ar->vif_max; i++) { + struct ath6kl_vif *vif; + struct p2p_ps_info *p2p_ps; + + vif = ath6kl_get_vif_by_index(ar, i); + if (vif) { + p2p_ps = vif->p2p_ps_info_ctx; + len += snprintf(buf + len, buf_len - len, + " %d", p2p_ps->go_noa_notif_cnt); + } + } + len += snprintf(buf + len, buf_len - len, "\n"); + + spin_lock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + for (i = 0; i < NUM_CONN; i++) { + fw_conn = &p2p_flowctrl->fw_conn_list[i]; + + if (fw_conn->connId == ATH6KL_P2P_FLOWCTRL_NULL_CONNID) + continue; + + len += snprintf(buf + len, buf_len - len, + "\n[%d]===============================\n", i); + len += snprintf(buf + len, buf_len - len, + " vif : %p\n", fw_conn->vif); + len += snprintf(buf + len, buf_len - len, + " connId : %d\n", fw_conn->connId); + len += snprintf(buf + len, buf_len - len, + " parent_connId: %d\n", fw_conn->parent_connId); + len += snprintf(buf + len, buf_len - len, + " macAddr : %02x:%02x:%02x:%02x:%02x:%02x\n", + fw_conn->mac_addr[0], + fw_conn->mac_addr[1], + fw_conn->mac_addr[2], + fw_conn->mac_addr[3], + fw_conn->mac_addr[4], + fw_conn->mac_addr[5]); + len += snprintf(buf + len, buf_len - len, + " status : %02x\n", fw_conn->connect_status); + len += snprintf(buf + len, buf_len - len, + " preCanSend : %d\n", fw_conn->previous_can_send); + len += snprintf(buf + len, buf_len - len, + " tx_queued : %d\n", fw_conn->sche_tx_queued); + len += snprintf(buf + len, buf_len - len, + " tx : %d\n", fw_conn->sche_tx); + len += snprintf(buf + len, buf_len - len, + " rx_tx : %d\n", fw_conn->sche_re_tx); + len += snprintf(buf + len, buf_len - len, + " re_tx_aging : %d\n", fw_conn->sche_re_tx_aging); + } + spin_unlock_bh(&p2p_flowctrl->p2p_flowctrl_lock); + + return len; +} + +struct ath6kl_p2p_rc_info *ath6kl_p2p_rc_init(struct ath6kl *ar) +{ + struct ath6kl_p2p_rc_info *p2p_rc; + + p2p_rc = kzalloc(sizeof(struct ath6kl_p2p_rc_info), GFP_KERNEL); + if (!p2p_rc) { + ath6kl_err("failed to alloc memory for p2p_rc\n"); + return NULL; + } + + p2p_rc->ar = ar; + p2p_rc->flags = ATH6KL_RC_FLAGS_HIGH_CHAN | + ATH6KL_RC_FLAGS_IGNORE_DFS_CHAN; + spin_lock_init(&p2p_rc->p2p_rc_lock); + p2p_rc->snr_compensation = P2P_RC_DEF_SNR_COMP; + + ath6kl_dbg(ATH6KL_DBG_RC, "p2p_rc init, flags %x\n", + p2p_rc->flags); + + return p2p_rc; +} + +void ath6kl_p2p_rc_deinit(struct ath6kl *ar) +{ + struct ath6kl_p2p_rc_info *p2p_rc = ar->p2p_rc_info_ctx; + + kfree(p2p_rc); + + ar->p2p_rc_info_ctx = NULL; + + ath6kl_dbg(ATH6KL_DBG_RC, "p2p_rc deinit\n"); + + return; +} + +static inline int __p2p_rc_get_chan_slot(struct ieee80211_channel *channel) +{ +#define _MIN_5G_CHAN_ID 34 + int chid = ieee80211_frequency_to_channel(channel->center_freq); + + if (channel->band == (u32)NL80211_BAND_2GHZ) + return chid - 1; + else if (channel->band == (u32)NL80211_BAND_5GHZ) + return ATH6KL_RC_MAX_2G_CHAN_RECORD + + ((chid - _MIN_5G_CHAN_ID) / 2); + else { + ath6kl_err("p2p_rc slot fail chid %d!\n", chid); + BUG_ON(1); + return -1; + } +#undef _MIN_5G_CHAN_ID +} + +void ath6kl_p2p_rc_fetch_chan(struct ath6kl *ar) +{ + struct ath6kl_p2p_rc_info *p2p_rc = ar->p2p_rc_info_ctx; + enum ieee80211_band band; + struct wiphy *wiphy = p2p_rc->ar->wiphy; + int i, slot; + + if (!p2p_rc) + return; + + spin_lock_bh(&p2p_rc->p2p_rc_lock); + p2p_rc->chan_record_cnt = 0; + for (i = 0; i < ATH6KL_RC_MAX_CHAN_RECORD; i++) { + p2p_rc->chan_record[i].channel = NULL; + p2p_rc->chan_record[i].best_snr = P2P_RC_NULL_SNR; + p2p_rc->chan_record[i].aver_snr = P2P_RC_NULL_SNR; + } + + /* Fetch channel record from wiphy */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband = wiphy->bands[band]; + + if (!sband) + continue; + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + slot = __p2p_rc_get_chan_slot(chan); + if ((slot >= 0) && (slot < ATH6KL_RC_MAX_CHAN_RECORD)) { + p2p_rc->chan_record[slot].channel = chan; + p2p_rc->chan_record_cnt++; + } else + ath6kl_err("p2p_rc fetch fail, f %d s %d!\n", + chan->center_freq, + slot); + } + } + + p2p_rc->flags |= ATH6KL_RC_FLAGS_CHAN_RECORD_FETCHED; + spin_unlock_bh(&p2p_rc->p2p_rc_lock); + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc fetch chan, chan_record_cnt %d\n", + p2p_rc->chan_record_cnt); + + return; +} + +static void _p2p_rc_reset_chan_record(struct ath6kl_p2p_rc_info *p2p_rc) +{ + int i; + + /* Clear all channel record */ + spin_lock_bh(&p2p_rc->p2p_rc_lock); + for (i = 0; i < ATH6KL_RC_MAX_CHAN_RECORD; i++) { + p2p_rc->chan_record[i].best_snr = P2P_RC_NULL_SNR; + p2p_rc->chan_record[i].aver_snr = P2P_RC_NULL_SNR; + } + + if (p2p_rc->flags & ATH6KL_RC_FLAGS_CHAN_RECORD_FETCHED) { + spin_unlock_bh(&p2p_rc->p2p_rc_lock); + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc chan already fetched, chan_record_cnt %d\n", + p2p_rc->chan_record_cnt); + + return; + } + spin_unlock_bh(&p2p_rc->p2p_rc_lock); + + ath6kl_p2p_rc_fetch_chan(p2p_rc->ar); + + return; +} + +void ath6kl_p2p_rc_scan_start(struct ath6kl_vif *vif) +{ + struct ath6kl_p2p_rc_info *p2p_rc = vif->ar->p2p_rc_info_ctx; + + if (!p2p_rc) + return; + + /* Assume only one scan behavior between all VIFs at the same time. */ + + /* Only full-scan is valid. */ + if ((vif->scan_req) && + (vif->scanband_type == SCANBAND_TYPE_ALL)) { + /* Reset the channel record. */ + _p2p_rc_reset_chan_record(p2p_rc); + + if ((vif->scan_req->n_channels == 0) || + (vif->scan_req->n_channels == p2p_rc->chan_record_cnt)) { + p2p_rc->flags |= ATH6KL_RC_FLAGS_NEED_UPDATED; + p2p_rc->last_update = jiffies; + } + } + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc scan_start %s, chan %d type %d cnt %d\n", + (p2p_rc->flags & ATH6KL_RC_FLAGS_NEED_UPDATED ? + "need-update" : ""), + (vif->scan_req ? + vif->scan_req->n_channels : -1), + vif->scanband_type, + p2p_rc->chan_record_cnt); + + return; +} + +int ath6kl_p2p_rc_scan_complete_event(struct ath6kl_vif *vif, bool aborted) +{ + struct ath6kl_p2p_rc_info *p2p_rc = vif->ar->p2p_rc_info_ctx; + int i; + + if (!p2p_rc) + return 0; + + p2p_rc->flags &= ~ATH6KL_RC_FLAGS_NEED_UPDATED; + + /* Only flush the last_p2p_rc if it's a successful scan. */ + spin_lock_bh(&p2p_rc->p2p_rc_lock); + if (!aborted) { + p2p_rc->flags &= ~ATH6KL_RC_FLAGS_DONE; + for (i = 0; i < P2P_RC_TYPE_MAX; i++) + p2p_rc->last_p2p_rc[i] = NULL; + } + spin_unlock_bh(&p2p_rc->p2p_rc_lock); + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc scan_comp %s\n", + (aborted ? "aborted" : "")); + + return 0; +} + +static inline void _p2p_rc_bss_update(struct ath6kl_p2p_rc_info *p2p_rc, + struct ieee80211_channel *channel, + u8 snr) +{ + int slot; + + slot = __p2p_rc_get_chan_slot(channel); + if ((slot >= 0) && (slot < ATH6KL_RC_MAX_CHAN_RECORD)) { + struct p2p_rc_chan_record *p2p_rc_chan; + + spin_lock_bh(&p2p_rc->p2p_rc_lock); + p2p_rc_chan = &(p2p_rc->chan_record[slot]); + + if (channel != p2p_rc_chan->channel) { + spin_unlock_bh(&p2p_rc->p2p_rc_lock); + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc bssinfo chan unsync! rx %d rc %d\n", + channel->center_freq, + p2p_rc_chan->channel->center_freq); + return; + } + + BUG_ON(!p2p_rc_chan->channel); + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc bssinfo %s freq %d snr %d slot %d\n", + ((p2p_rc_chan->best_snr != P2P_RC_NULL_SNR) ? + "update" : "found"), + channel->center_freq, + snr, + slot); + + if (snr > P2P_RC_MAX_SNR) + snr = P2P_RC_MAX_SNR; + + /* Only keep the max. SNR for calculation. */ + if (p2p_rc_chan->best_snr != P2P_RC_NULL_SNR) { + if (snr > p2p_rc_chan->best_snr) + p2p_rc_chan->best_snr = snr; + } else + p2p_rc_chan->best_snr = snr; + spin_unlock_bh(&p2p_rc->p2p_rc_lock); + } else + ath6kl_err("p2p_rc bssinfo invalid frequency %d!\n", + channel->center_freq); + + return; +} + +void ath6kl_p2p_rc_bss_info(struct ath6kl_vif *vif, + u8 snr, + struct ieee80211_channel *channel) +{ + struct ath6kl_p2p_rc_info *p2p_rc = vif->ar->p2p_rc_info_ctx; + + if (!p2p_rc) + return; + + if (p2p_rc->flags & ATH6KL_RC_FLAGS_NEED_UPDATED) + _p2p_rc_bss_update(p2p_rc, channel, snr); + + return; +} + +static inline u8 __p2p_rc_average_snr(struct p2p_rc_chan_record *chan[], + int count) +{ + struct p2p_rc_chan_record *p2p_rc_chan; + int i, aver_snr = 0, compensate = 0; + + for (i = 0; i < ATH6KL_RC_AVERAGE_CHAN_CNT; i++) { + p2p_rc_chan = chan[i]; + if ((i < count) && + (p2p_rc_chan->channel)) + aver_snr += p2p_rc_chan->best_snr; + else + aver_snr += P2P_RC_NULL_SNR; + } + + /* To round off the aver_snr to get more accurate average. */ + if ((aver_snr % ATH6KL_RC_AVERAGE_CHAN_CNT) > + (ATH6KL_RC_AVERAGE_CHAN_CNT >> 1)) + compensate++; + + aver_snr = (aver_snr / ATH6KL_RC_AVERAGE_CHAN_CNT) + compensate; + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc get_aver freq %d aver_snr %d count %d/%d\n", + (chan[0]->channel ? chan[0]->channel->center_freq : 0), + aver_snr, + count, + ATH6KL_RC_AVERAGE_CHAN_CNT); + + return aver_snr; +} + +static struct p2p_rc_chan_record *_p2p_rc_get_2g_chan( + struct ath6kl_p2p_rc_info *p2p_rc, + bool only_p2p_social, + bool only_p2p) +{ + struct p2p_rc_chan_record *p2p_rc_chan, *best_p2p_rc_chan = NULL; + struct p2p_rc_chan_record *adj_chan[ATH6KL_RC_AVERAGE_CHAN_CNT]; + struct ieee80211_channel *chan; + int i, j, count; + u8 average_h; + + BUG_ON((only_p2p_social && only_p2p)); + + /* Start from 2G channel */ + p2p_rc_chan = &(p2p_rc->chan_record[0]); + + for (i = 0; i < ATH6KL_RC_MAX_2G_CHAN_RECORD; i++, p2p_rc_chan++) { + chan = p2p_rc_chan->channel; + if (chan) { + if ((only_p2p_social) && + !ath6kl_p2p_is_social_channel(chan->center_freq)) + continue; + else if ((only_p2p) && + (chan->flags & (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_NO_IBSS)) && + !ath6kl_p2p_is_p2p_channel(chan->center_freq)) + continue; + + /* Get adjacence channels in 20MHz width. */ + count = 0; + adj_chan[count++] = p2p_rc_chan; + for (j = 1; j <= ATH6KL_RC_AVERAGE_CHAN_OFFSET; j++) { + if (chan->center_freq - (j * 5) >= + ATH6KL_RC_AVERAGE_CHAN_START) + adj_chan[count++] = p2p_rc_chan - j; + if (chan->center_freq + (j * 5) <= + ATH6KL_RC_AVERAGE_CHAN_END) + adj_chan[count++] = p2p_rc_chan + j; + } + + if ((count) && + (chan->center_freq <= ATH6KL_RC_AVERAGE_CHAN_END)) + p2p_rc_chan->aver_snr = __p2p_rc_average_snr( + adj_chan, + count); + else + p2p_rc_chan->aver_snr = p2p_rc_chan->best_snr; + + if (best_p2p_rc_chan) { + average_h = p2p_rc_chan->aver_snr; + if (p2p_rc->flags & ATH6KL_RC_FLAGS_HIGH_CHAN) + average_h++; + + if (average_h > best_p2p_rc_chan->aver_snr) + best_p2p_rc_chan = p2p_rc_chan; + } else + best_p2p_rc_chan = p2p_rc_chan; + } + } + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc get_2g freq %d snr %d\n", + (best_p2p_rc_chan ? + best_p2p_rc_chan->channel->center_freq : 0), + (best_p2p_rc_chan ? + best_p2p_rc_chan->best_snr : 0)); + + return best_p2p_rc_chan; +} + +static struct p2p_rc_chan_record *_p2p_rc_get_5g_chan( + struct ath6kl_p2p_rc_info *p2p_rc, + bool only_p2p) +{ + struct p2p_rc_chan_record *p2p_rc_chan, *best_p2p_rc_chan = NULL; + struct ieee80211_channel *chan; + int i, snr_h; + + /* Start from 5G channel */ + p2p_rc_chan = &(p2p_rc->chan_record[ATH6KL_RC_MAX_2G_CHAN_RECORD]); + + for (i = 0; i < ATH6KL_RC_MAX_5G_CHAN_RECORD; i++, p2p_rc_chan++) { + chan = p2p_rc_chan->channel; + if (chan) { + if ((only_p2p) && + (chan->flags & (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_NO_IBSS)) && + !ath6kl_p2p_is_p2p_channel(chan->center_freq)) + continue; + + /* If DFS channel but any AP exist and still use it. */ + if ((p2p_rc->flags & ATH6KL_RC_FLAGS_IGNORE_DFS_CHAN) && + (chan->flags & IEEE80211_CHAN_RADAR) && + (p2p_rc_chan->best_snr == P2P_RC_NULL_SNR)) + continue; + + if (best_p2p_rc_chan) { + snr_h = (int)p2p_rc_chan->best_snr; + if (p2p_rc->flags & ATH6KL_RC_FLAGS_HIGH_CHAN) + snr_h++; + + if (snr_h > (int)best_p2p_rc_chan->best_snr) + best_p2p_rc_chan = p2p_rc_chan; + } else + best_p2p_rc_chan = p2p_rc_chan; + } + } + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc get_5g freq %d snr %d\n", + (best_p2p_rc_chan ? + best_p2p_rc_chan->channel->center_freq : 0), + (best_p2p_rc_chan ? + best_p2p_rc_chan->best_snr : 0)); + + return best_p2p_rc_chan; +} + +int ath6kl_p2p_rc_get(struct ath6kl *ar, + u16 *rc_2g, + u16 *rc_5g, + u16 *rc_all, + u16 *rc_p2p_social, + u16 *rc_p2p_2g, + u16 *rc_p2p_5g, + u16 *rc_p2p_all) +{ + struct ath6kl_p2p_rc_info *p2p_rc = ar->p2p_rc_info_ctx; + struct p2p_rc_chan_record **p2p_rc_chan; + + if (!p2p_rc) + return -ENOTSUPP; + + /* Updating */ + if (p2p_rc->flags & ATH6KL_RC_FLAGS_NEED_UPDATED) + return -EINPROGRESS; + + if ((p2p_rc->flags & ATH6KL_RC_FLAGS_ALWAYS_FRESH) && + time_after(jiffies, p2p_rc->last_update + ATH6KL_RC_FRESH_TIME)) + return -EAGAIN; + + spin_lock_bh(&p2p_rc->p2p_rc_lock); + + p2p_rc_chan = &(p2p_rc->last_p2p_rc[0]); + + /* + * No further channel information need to be parsed and just + * return the last results. + */ + if (p2p_rc->flags & ATH6KL_RC_FLAGS_DONE) + goto done; + + /* Get 2G recommand channel */ + p2p_rc_chan[P2P_RC_TYPE_2GALL] = _p2p_rc_get_2g_chan(p2p_rc, + false, + false); + /* Get 5G recommand channel */ + p2p_rc_chan[P2P_RC_TYPE_5GALL] = _p2p_rc_get_5g_chan(p2p_rc, + false); + + /* Get overall recommand channel. */ + if (p2p_rc_chan[P2P_RC_TYPE_2GALL]) { + if (p2p_rc_chan[P2P_RC_TYPE_5GALL]) { + if (p2p_rc_chan[P2P_RC_TYPE_2GALL]->aver_snr > + (p2p_rc_chan[P2P_RC_TYPE_5GALL]->best_snr + + p2p_rc->snr_compensation)) + p2p_rc_chan[P2P_RC_TYPE_OVERALL] = + p2p_rc_chan[P2P_RC_TYPE_2GALL]; + else /* Prefer to use 5G if has the same value. */ + p2p_rc_chan[P2P_RC_TYPE_OVERALL] = + p2p_rc_chan[P2P_RC_TYPE_5GALL]; + } else + p2p_rc_chan[P2P_RC_TYPE_OVERALL] = + p2p_rc_chan[P2P_RC_TYPE_2GALL]; + } else if (p2p_rc_chan[P2P_RC_TYPE_5GALL]) + p2p_rc_chan[P2P_RC_TYPE_OVERALL] = + p2p_rc_chan[P2P_RC_TYPE_5GALL]; + + /* Get P2P-Social recommand channel */ + p2p_rc_chan[P2P_RC_TYPE_SOCAIL] = _p2p_rc_get_2g_chan(p2p_rc, + true, + false); + + /* Get P2P-2G recommand channel */ + p2p_rc_chan[P2P_RC_TYPE_2GP2P] = _p2p_rc_get_2g_chan(p2p_rc, + false, + true); + + /* Get P2P-5G recommand channel */ + p2p_rc_chan[P2P_RC_TYPE_5GP2P] = _p2p_rc_get_5g_chan(p2p_rc, + true); + + /* Get P2P-All recommand channel. */ + if (p2p_rc_chan[P2P_RC_TYPE_2GP2P]) { + if (p2p_rc_chan[P2P_RC_TYPE_5GP2P]) { + if (p2p_rc_chan[P2P_RC_TYPE_2GP2P]->aver_snr > + (p2p_rc_chan[P2P_RC_TYPE_5GP2P]->best_snr + + p2p_rc->snr_compensation)) + p2p_rc_chan[P2P_RC_TYPE_ALLP2P] = + p2p_rc_chan[P2P_RC_TYPE_2GP2P]; + else /* Prefer to use 5G if has the same value. */ + p2p_rc_chan[P2P_RC_TYPE_ALLP2P] = + p2p_rc_chan[P2P_RC_TYPE_5GP2P]; + } else + p2p_rc_chan[P2P_RC_TYPE_ALLP2P] = + p2p_rc_chan[P2P_RC_TYPE_2GP2P]; + } else if (p2p_rc_chan[P2P_RC_TYPE_5GP2P]) + p2p_rc_chan[P2P_RC_TYPE_ALLP2P] = + p2p_rc_chan[P2P_RC_TYPE_5GP2P]; + + p2p_rc->flags |= ATH6KL_RC_FLAGS_DONE; + +done: + /* Get all results back to the caller */ + if ((p2p_rc_chan[P2P_RC_TYPE_2GALL]) && rc_2g) + *rc_2g = + p2p_rc_chan[P2P_RC_TYPE_2GALL]->channel->center_freq; + + if ((p2p_rc_chan[P2P_RC_TYPE_5GALL]) && rc_5g) + *rc_5g = + p2p_rc_chan[P2P_RC_TYPE_5GALL]->channel->center_freq; + + if ((p2p_rc_chan[P2P_RC_TYPE_OVERALL]) && rc_all) + *rc_all = + p2p_rc_chan[P2P_RC_TYPE_OVERALL]->channel->center_freq; + + if ((p2p_rc_chan[P2P_RC_TYPE_SOCAIL]) && rc_p2p_social) + *rc_p2p_social = + p2p_rc_chan[P2P_RC_TYPE_SOCAIL]->channel->center_freq; + + if ((p2p_rc_chan[P2P_RC_TYPE_2GP2P]) && rc_p2p_2g) + *rc_p2p_2g = + p2p_rc_chan[P2P_RC_TYPE_2GP2P]->channel->center_freq; + + if ((p2p_rc_chan[P2P_RC_TYPE_5GP2P]) && rc_p2p_5g) + *rc_p2p_5g = + p2p_rc_chan[P2P_RC_TYPE_5GP2P]->channel->center_freq; + + if ((p2p_rc_chan[P2P_RC_TYPE_ALLP2P]) && rc_p2p_all) + *rc_p2p_all = + p2p_rc_chan[P2P_RC_TYPE_ALLP2P]->channel->center_freq; + + ath6kl_dbg(ATH6KL_DBG_RC, + "p2p_rc get 2/5/A %p %p %p P2P-S/2/5/A %p %p %p %p\n", + p2p_rc->last_p2p_rc[P2P_RC_TYPE_2GALL], + p2p_rc->last_p2p_rc[P2P_RC_TYPE_5GALL], + p2p_rc->last_p2p_rc[P2P_RC_TYPE_OVERALL], + p2p_rc->last_p2p_rc[P2P_RC_TYPE_SOCAIL], + p2p_rc->last_p2p_rc[P2P_RC_TYPE_2GP2P], + p2p_rc->last_p2p_rc[P2P_RC_TYPE_5GP2P], + p2p_rc->last_p2p_rc[P2P_RC_TYPE_ALLP2P]); + + spin_unlock_bh(&p2p_rc->p2p_rc_lock); + + return 0; +} + +int ath6kl_p2p_rc_dump(struct ath6kl *ar, u8 *buf, int buf_len) +{ + struct ath6kl_p2p_rc_info *p2p_rc = ar->p2p_rc_info_ctx; + struct p2p_rc_chan_record *p2p_rc_chan; + int i, idx = 0, len = 0; + u16 rc_2g, rc_5g, rc_all, rc_p2p_so, rc_p2p_2g, rc_p2p_5g, rc_p2p_all; + + if ((!p2p_rc) || (!buf)) + return 0; + + len += snprintf(buf + len, buf_len - len, + "\nflags 0x%x snr_comp %d last_update %lld now %lld\n", + p2p_rc->flags, + p2p_rc->snr_compensation, + (long long int)p2p_rc->last_update, + (long long int)jiffies); + + rc_2g = rc_5g = rc_all = 0; + rc_p2p_so = rc_p2p_2g = rc_p2p_5g = rc_p2p_all = 0; + if (ath6kl_p2p_rc_get(ar, + &rc_2g, + &rc_5g, + &rc_all, + &rc_p2p_so, + &rc_p2p_2g, + &rc_p2p_5g, + &rc_p2p_all) != 0) + return 0; + + len += snprintf(buf + len, buf_len - len, + "\n2G/5G/ALL %d %d %d P2P-So/2G/5G/ALL %d %d %d %d\n", + rc_2g, + rc_5g, + rc_all, + rc_p2p_so, + rc_p2p_2g, + rc_p2p_5g, + rc_p2p_all); + + spin_lock_bh(&p2p_rc->p2p_rc_lock); + len += snprintf(buf + len, buf_len - len, + "\nchan_record %d\n", + p2p_rc->chan_record_cnt); + for (i = 0, p2p_rc_chan = p2p_rc->chan_record; + i < ATH6KL_RC_MAX_CHAN_RECORD; + i++, p2p_rc_chan++) { + if (p2p_rc_chan->channel) + len += snprintf(buf + len, buf_len - len, + "%02d - %4d SNR %3d AVER_SNR %3d\n", + idx, + p2p_rc_chan->channel->center_freq, + (p2p_rc_chan->best_snr == + P2P_RC_NULL_SNR) ? + -1 : p2p_rc_chan->best_snr, + (p2p_rc_chan->aver_snr == + P2P_RC_NULL_SNR) ? + -1 : p2p_rc_chan->aver_snr); + idx++; + } + spin_unlock_bh(&p2p_rc->p2p_rc_lock); + + return len; +} + +bool ath6kl_p2p_frame_retry(struct ath6kl *ar, u8 *frm, int len) +{ + struct ieee80211_p2p_action_public *action_frame = + (struct ieee80211_p2p_action_public *)frm; + + if (!ar->p2p_frame_retry) + return false; + + /* + * WAR : Except P2P-Neg-Confirm frame and other P2P action frames + * could be recovery by supplicant's state machine. + */ + if (len < 8) + return false; + + return ((action_frame->category == WLAN_CATEGORY_PUBLIC) && + (action_frame->action_code == + WLAN_PUB_ACTION_VENDER_SPECIFIC) && + (action_frame->oui == cpu_to_be32((WLAN_OUI_WFA << 8) | + (WLAN_OUI_TYPE_WFA_P2P))) && + (action_frame->action_subtype == WLAN_P2P_GO_NEG_CONF)); +} + +bool ath6kl_p2p_is_p2p_frame(struct ath6kl *ar, const u8 *frm, size_t len) +{ + struct ieee80211_mgmt *action = (struct ieee80211_mgmt *)frm; + struct ieee80211_p2p_action_public *action_public; + struct ieee80211_p2p_action_vendor *action_vendor; + u8 *action_start = (u8 *)(&action->u.action); + + if (len < sizeof(struct ieee80211_p2p_action_vendor)) + return false; + + action_public = (struct ieee80211_p2p_action_public *)action_start; + if ((action_public->category == WLAN_CATEGORY_PUBLIC) && + (action_public->action_code == + WLAN_PUB_ACTION_VENDER_SPECIFIC) && + (action_public->oui == cpu_to_be32((WLAN_OUI_WFA << 8) | + (WLAN_OUI_TYPE_WFA_P2P)))) + return true; + + action_vendor = (struct ieee80211_p2p_action_vendor *)action_start; + if ((action_vendor->category == WLAN_CATEGORY_VENDOR_SPECIFIC) && + (action_vendor->oui == cpu_to_be32((WLAN_OUI_WFA << 8) | + (WLAN_OUI_TYPE_WFA_P2P)))) + return true; + + return false; +} + +void ath6kl_p2p_connect_event(struct ath6kl_vif *vif, + u8 beacon_ie_len, + u8 assoc_req_len, + u8 assoc_resp_len, + u8 *assoc_info) +{ + u8 *pie, *peie; + struct ieee80211_ht_cap *ht_cap_ie = NULL; + bool vendor_spec_ie_intel = false; + + if (vif->nw_type != INFRA_NETWORK) + return; + + if ((vif->ar->p2p_war_p2p_client_awake) && + (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) { + set_bit(PS_STICK, &vif->flags); + ath6kl_wmi_powermode_cmd(vif->ar->wmi, + vif->fw_vif_idx, + MAX_PERF_POWER); + return; + } + + /* Now, only p2p_war_bad_intel_go need to do something here. */ + if (!vif->ar->p2p_war_bad_intel_go) + return; + + /* AssocResp IEs */ + pie = assoc_info + beacon_ie_len + assoc_req_len + + (sizeof(u16) * 3); /* capinfo + status code + associd */ + peie = assoc_info + beacon_ie_len + assoc_req_len + assoc_resp_len; + + while (pie < peie) { + switch (*pie) { + case WLAN_EID_HT_CAPABILITY: + if (pie[1] >= sizeof(struct ieee80211_ht_cap)) + ht_cap_ie = + (struct ieee80211_ht_cap *)(pie + 2); + break; + case WLAN_EID_VENDOR_SPECIFIC: + if (pie[1] > 0) { + if (pie[1] == 0x0b && + pie[2] == 0x00 && + pie[3] == 0x17 && + pie[4] == 0x35 && + pie[5] == 0x01) + vendor_spec_ie_intel = true; + } + break; + } + pie += pie[1] + 2; + } + + /* WAR EV119712 */ + if (ht_cap_ie && + vendor_spec_ie_intel && + (vif->ar->target_subtype & TARGET_SUBTYPE_2SS)) { + if (((ht_cap_ie->cap_info & IEEE80211_HT_CAP_SM_PS) == + (WLAN_HT_CAP_SM_PS_DYNAMIC << + IEEE80211_HT_CAP_SM_PS_SHIFT)) && + (ht_cap_ie->mcs.rx_mask[1])) { + ath6kl_info("Enable Intel-GO compatibility.\n"); + ath6kl_wmi_set_fix_rates(vif->ar->wmi, + vif->fw_vif_idx, + (0x00000000000fffffULL)); + + set_bit(PS_STICK, &vif->flags); + ath6kl_wmi_powermode_cmd(vif->ar->wmi, + vif->fw_vif_idx, + MAX_PERF_POWER); + } + } + + return; +} + +static void _p2p_pending_connect_work(struct work_struct *work) +{ + struct ath6kl_vif *vif = NULL; + struct p2p_pending_connect_info *pending_connect_info = NULL; + const u8 *bssid, *req_ie, *resp_ie; + + if (!work) + goto err; + + vif = container_of(work, struct ath6kl_vif, + work_pending_connect.work); + if (!vif) + goto err; + + pending_connect_info = vif->pending_connect_info; + if (!vif->pending_connect_info) + goto err; + + bssid = req_ie = resp_ie = NULL; + if (!is_zero_ether_addr(pending_connect_info->bssid)) + bssid = pending_connect_info->bssid; + if (pending_connect_info->req_ie_len) + req_ie = pending_connect_info->req_ie; + if (pending_connect_info->resp_ie_len) + resp_ie = pending_connect_info->resp_ie; + + /* Send connect event to cfg80211 */ + cfg80211_connect_result(vif->ndev, + bssid, + req_ie, + pending_connect_info->req_ie_len, + resp_ie, + pending_connect_info->resp_ie_len, + pending_connect_info->status, + pending_connect_info->gfp); + + kfree(vif->pending_connect_info); + vif->pending_connect_info = NULL; + + return; +err: + ath6kl_err("P2P pending connect work fail! vif %p work %p info %p\n", + vif, work, pending_connect_info); + + return; +} + +static inline bool _p2p_need_pending_connect(struct ath6kl_vif *vif, + const u8 *bssid) +{ + bool need_pending = false; + + /* WAR CR468120 */ + if ((vif->ar->p2p_war_bad_broadcom_go) && + (vif->sme_state == SME_CONNECTED) && + (vif->connect_ctrl_flags & CONNECT_WPS_FLAG) && + (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) { + struct cfg80211_bss *bss; + + bss = cfg80211_get_bss(vif->wdev.wiphy, + NULL, + vif->bssid, + vif->ssid, + vif->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + if (bss) { + u8 *pie, *peie; + + pie = peie = NULL; + +#ifdef CFG80211_SAFE_BSS_INFO_ACCESS + rcu_read_lock(); + if (bss->ies) { + pie = (u8 *)(bss->ies->data); + peie = pie + bss->ies->len; + } +#else + if (bss->information_elements) { + pie = bss->information_elements; + peie = pie + bss->len_information_elements; + } +#endif + + while (pie < peie) { + if (*pie == WLAN_EID_VENDOR_SPECIFIC) { + if ((pie[1] > 0) && + (pie[2] == 0x00 && + pie[3] == 0x10 && + pie[4] == 0x18)) + need_pending = true; + } + pie += pie[1] + 2; + } + +#ifdef CFG80211_SAFE_BSS_INFO_ACCESS + rcu_read_unlock(); +#endif + cfg80211_put_bss(bss); + } + } + + return need_pending; +} + +static inline bool _p2p_flush_pending_connect(struct ath6kl_vif *vif) +{ + if ((vif->ar->p2p_war_bad_broadcom_go) && + (vif->sme_state == SME_CONNECTED) && + (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) && + (vif->pending_connect_info)) { + ath6kl_info("Flush pending connect work first.\n"); + flush_delayed_work(&vif->work_pending_connect); + } + + return false; +} + +bool ath6kl_p2p_pending_connect_event(struct ath6kl_vif *vif, + const u8 *bssid, + const u8 *req_ie, + size_t req_ie_len, + const u8 *resp_ie, + size_t resp_ie_len, + u16 status, + gfp_t gfp) +{ +#define _P2P_PENDING_CONNECT_TIME 800 /* in ms. */ + struct p2p_pending_connect_info *pending_connect_info; + + if (_p2p_need_pending_connect(vif, bssid)) { + if ((req_ie_len > ATH6KL_P2P_MAX_PENDING_INFO_IELEN) || + (resp_ie_len > ATH6KL_P2P_MAX_PENDING_INFO_IELEN)) + return false; + + vif->pending_connect_info = + kzalloc(sizeof(struct p2p_pending_connect_info), + GFP_ATOMIC); + if (vif->pending_connect_info == NULL) + return false; + + pending_connect_info = vif->pending_connect_info; + if (bssid) + memcpy(pending_connect_info->bssid, bssid, ETH_ALEN); + if (req_ie) { + memcpy(pending_connect_info->req_ie, + req_ie, + req_ie_len); + pending_connect_info->req_ie_len = req_ie_len; + } + if (resp_ie) { + memcpy(pending_connect_info->resp_ie, + resp_ie, + resp_ie_len); + pending_connect_info->resp_ie_len = resp_ie_len; + } + pending_connect_info->status = status; + pending_connect_info->gfp = gfp; + + INIT_DELAYED_WORK(&vif->work_pending_connect, + _p2p_pending_connect_work); + schedule_delayed_work(&vif->work_pending_connect, + (msecs_to_jiffies(_P2P_PENDING_CONNECT_TIME))); + + ath6kl_info("Enable Broadcom-GO compatibility, delay %d ms.\n", + _P2P_PENDING_CONNECT_TIME); + + return true; + } else + return false; +#undef _P2P_PENDING_CONNECT_TIME +} + +void ath6kl_p2p_pending_disconnect_event(struct ath6kl_vif *vif, + u16 reason, + u8 *ie, + size_t ie_len, + gfp_t gfp) +{ + /* Flush pending work first if already fired. */ + _p2p_flush_pending_connect(vif); + + return; +} + +bool ath6kl_p2p_ie_append(struct ath6kl_vif *vif, u8 mgmt_frame_type) +{ + struct ath6kl *ar = vif->ar; + + /* + * IOT : Some older APs' implementation may reject connection if + * concuurrent STA interface include P2P IEs even these APs + * don't understand P2P IEs. + */ + if (ar->p2p_concurrent && + ar->p2p_dedicate && + (vif->fw_vif_idx == 0) && + (vif->nw_type == INFRA_NETWORK) && + (ar->p2p_ie_not_append & mgmt_frame_type)) + return false; + + return true; +} + +/* P802.11REVmb */ +static struct p2p_oper_chan ath6kl_p2p_oper_chan[] = { + { 81, 2412, 2472, 5, P2P_OPER_CHAN_BW_HT20}, /* CH1 - CH13 */ + { 115, 5180, 5240, 20, P2P_OPER_CHAN_BW_HT20}, /* CH36 - CH48 */ + { 116, 5180, 5220, 20, P2P_OPER_CHAN_BW_HT40_PLUS}, /* CH36 - CH44 */ + { 117, 5200, 5240, 20, P2P_OPER_CHAN_BW_HT40_MINUS}, /* CH40 - CH48 */ + { 124, 5745, 5805, 20, P2P_OPER_CHAN_BW_HT20}, /* CH149 - CH161 */ + { 125, 5745, 5825, 20, P2P_OPER_CHAN_BW_HT20}, /* CH149 - CH165 */ + { 126, 5745, 5785, 20, P2P_OPER_CHAN_BW_HT40_PLUS}, /* CH149 - CH157 */ + { 127, 5765, 5805, 20, P2P_OPER_CHAN_BW_HT40_MINUS}, /* CH153 - CH161 */ + { 0, 0, 0, 0, P2P_OPER_CHAN_BW_NULL}, +}; + +bool ath6kl_p2p_is_p2p_channel(u32 freq) +{ + struct p2p_oper_chan *p2p_oper_chan_map, *p2p_oper_chan; + u32 op_class, p2p_freq; + + p2p_oper_chan_map = ath6kl_p2p_oper_chan; + + for (op_class = 0; p2p_oper_chan_map[op_class].oper_class; op_class++) { + p2p_oper_chan = &p2p_oper_chan_map[op_class]; + for (p2p_freq = p2p_oper_chan->min_chan_freq; + p2p_freq <= p2p_oper_chan->max_chan_freq; + p2p_freq += p2p_oper_chan->inc_freq) { + if (freq == p2p_freq) { + /* TODO : check bandwidth */ + return true; + } + } + } + + return false; +} + +bool ath6kl_p2p_is_social_channel(u32 freq) +{ + if ((freq == 2412) || + (freq == 2437) || + (freq == 2462)) + return true; + + return false; +} + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/p2p.h b/drivers/net/wireless/ath/ath6kl-3.5/p2p.h new file mode 100644 index 000000000000..eab251ba0901 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/p2p.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2004-2012 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 + +#define ATH6KL_P2P_PS_MAX_NOA_DESCRIPTORS 4 + +#define ATH6KL_P2P_PS_FLAGS_NOA_ENABLED BIT(0) +#define ATH6KL_P2P_PS_FLAGS_OPPPS_ENABLED BIT(1) + +#define NL80211_IFTYPE_P2P_DEVICE_QCA (0xff) + +enum { + IEEE80211_P2P_ATTR_NOTICE_OF_ABSENCE = 12, +}; + +enum { + WLAN_PUB_ACTION_VENDER_SPECIFIC = 9, +}; + +enum { + WLAN_P2P_GO_NEG_REG = 0, + WLAN_P2P_GO_NEG_RESP = 1, + WLAN_P2P_GO_NEG_CONF = 2, + WLAN_P2P_INVITATION_REQ = 3, + WLAN_P2P_INVITATION_RESP = 4, + WLAN_P2P_DEV_DISC_REQ = 5, + WLAN_P2P_DEV_DISC_RESP = 6, + WLAN_P2P_PROV_DISC_REQ = 7, + WLAN_P2P_PROV_DISC_RESP = 8 +}; + +struct ieee80211_p2p_action_public { + u8 category; + u8 action_code; + u32 oui; + u8 action_subtype; + u8 variable[0]; +} __packed; + +struct ieee80211_p2p_action_vendor { + u8 category; + u32 oui; + u8 action_subtype; + u8 variable[0]; +} __packed; + +struct ieee80211_p2p_noa_descriptor { + u8 count_or_type; /* 255: continuous schedule, 0: reserved */ + __le32 duration; + __le32 interval; + __le32 start_or_offset; +} __packed; + +struct ieee80211_p2p_noa_info { + u8 index; + u8 ctwindow_opps_param; + struct ieee80211_p2p_noa_descriptor + noas[ATH6KL_P2P_PS_MAX_NOA_DESCRIPTORS]; +} __packed; + +struct ieee80211_p2p_noa_ie { + u8 element_id; + u8 len; + u32 oui; + u8 attr; + u16 attr_len; + struct ieee80211_p2p_noa_info noa_info; +} __packed; + +struct p2p_ps_info { + struct ath6kl_vif *vif; + spinlock_t p2p_ps_lock; + + /* P2P-GO */ + u32 go_flags; + u8 go_noa_enable_idx; + struct ieee80211_p2p_noa_info go_noa; + + /* Cached information */ + u8 *go_last_beacon_app_ie; + u16 go_last_beacon_app_ie_len; + u8 *go_last_noa_ie; + u16 go_last_noa_ie_len; + u8 *go_working_buffer; + + /* counter */ + u32 go_noa_notif_cnt; +}; + +/* P2P Flowctrl */ +#define ATH6KL_P2P_FLOWCTRL_NULL_CONNID (0xff) +#define ATH6KL_P2P_FLOWCTRL_RECYCLE_LIMIT (10) + +#define ATH6KL_P2P_FLOWCTRL_NETIF_STOP (0x00) +#define ATH6KL_P2P_FLOWCTRL_NETIF_WAKE (0x01) + +#define ATH6KL_P2P_FLOWCTRL_REQ_STEP (30) +#define ATH6KL_P2P_FLOWCTRL_TXQ_LIMIT (50) + +struct ath6kl_fw_conn_list { + struct list_head conn_queue; + struct list_head re_queue; + + u8 connId; /* ID sync between host and target. */ + u8 parent_connId; /* For P2P_FLOWCTRL_SCHE_TYPE_INTERFACE */ + u8 mac_addr[ETH_ALEN]; + struct ath6kl_vif *vif; + + union { + struct { + u8 bk_uapsd:1; + u8 be_uapsd:1; + u8 vi_uapsd:1; + u8 vo_uapsd:1; + u8 ps:1; /* 1 means power saved */ + u8 ocs:1; /* 1 means off channel */ + u8 res:2; + }; + u8 connect_status; + }; + + bool previous_can_send; + + /* stat */ + int sche_tx_queued; + u32 sche_tx; + u32 sche_re_tx; + u32 sche_re_tx_aging; +}; + +enum p2p_flowctrl_sche_type { + P2P_FLOWCTRL_SCHE_TYPE_CONNECTION, + P2P_FLOWCTRL_SCHE_TYPE_INTERFACE, +}; + +struct ath6kl_p2p_flowctrl { + struct ath6kl *ar; + spinlock_t p2p_flowctrl_lock; + + enum p2p_flowctrl_sche_type sche_type; + struct ath6kl_fw_conn_list fw_conn_list[NUM_CONN]; + + u32 p2p_flowctrl_event_cnt; +}; + +/* P2P RecommendChannel */ +#define ATH6KL_RC_FLAGS_NEED_UPDATED (1 << 0) +#define ATH6KL_RC_FLAGS_DONE (1 << 1) +#define ATH6KL_RC_FLAGS_HIGH_CHAN (1 << 2) +#define ATH6KL_RC_FLAGS_ALWAYS_FRESH (1 << 3) +#define ATH6KL_RC_FLAGS_IGNORE_DFS_CHAN (1 << 4) +#define ATH6KL_RC_FLAGS_CHAN_RECORD_FETCHED (1 << 5) + +#define ATH6KL_RC_MAX_2G_CHAN_RECORD (14) +#define ATH6KL_RC_MAX_5G_CHAN_RECORD (66) +#define ATH6KL_RC_MAX_CHAN_RECORD (ATH6KL_RC_MAX_2G_CHAN_RECORD + \ + ATH6KL_RC_MAX_5G_CHAN_RECORD) + +#define ATH6KL_RC_AVERAGE_CHAN_CNT (5) /* 20MHz width */ +#define ATH6KL_RC_AVERAGE_CHAN_OFFSET (2) /* 10MHz plus or minus */ +#define ATH6KL_RC_AVERAGE_CHAN_START (2412) /* Channel 1 */ +#define ATH6KL_RC_AVERAGE_CHAN_END (2472) /* Channel 13 */ + +#define ATH6KL_RC_FRESH_TIME msecs_to_jiffies(5 * 60 * 1000) + +enum p2p_rc_type { + P2P_RC_TYPE_2GALL, /* the best in all 2G channels */ + P2P_RC_TYPE_5GALL, /* the best in all 5G channels */ + P2P_RC_TYPE_OVERALL, /* the best in all channels */ + P2P_RC_TYPE_SOCAIL, /* the best in 1, 6 abd 11 channels */ + P2P_RC_TYPE_2GP2P, /* the best in P2P 2G channels */ + P2P_RC_TYPE_5GP2P, /* the best in P2P 5G channels */ + P2P_RC_TYPE_ALLP2P, /* the best in P2P channels */ + + P2P_RC_TYPE_MAX, /* keep last */ +}; + +struct p2p_rc_bss_info { + struct list_head list; + struct ieee80211_channel *channel; + u8 rssi; +}; + +struct p2p_rc_chan_record { + struct ieee80211_channel *channel; + +#define P2P_RC_MAX_SNR (96) +#define P2P_RC_NULL_SNR (P2P_RC_MAX_SNR + 1) + u8 best_snr; + u8 aver_snr; /* for 2G */ +}; + +struct ath6kl_p2p_rc_info { + struct ath6kl *ar; + u32 flags; + spinlock_t p2p_rc_lock; + int chan_record_cnt; + struct p2p_rc_chan_record chan_record[ATH6KL_RC_MAX_CHAN_RECORD]; + unsigned long last_update; /* in jiffies */ + +#define P2P_RC_DEF_SNR_COMP (0) + int snr_compensation; + + /* Keep the latest result. */ + struct p2p_rc_chan_record *last_p2p_rc[P2P_RC_TYPE_MAX]; +}; + +struct p2p_oper_chan { + u16 oper_class; + u32 min_chan_freq; + u32 max_chan_freq; + u32 inc_freq; + +#define P2P_OPER_CHAN_BW_NULL 0 +#define P2P_OPER_CHAN_BW_HT20 1 +#define P2P_OPER_CHAN_BW_HT40_PLUS 2 +#define P2P_OPER_CHAN_BW_HT40_MINUS 3 + u8 bw; +}; + +struct p2p_pending_connect_info { +#define ATH6KL_P2P_MAX_PENDING_INFO_IELEN 512 + u8 bssid[ETH_ALEN]; + u8 req_ie[ATH6KL_P2P_MAX_PENDING_INFO_IELEN]; + size_t req_ie_len; + u8 resp_ie[ATH6KL_P2P_MAX_PENDING_INFO_IELEN]; + size_t resp_ie_len; + u16 status; + gfp_t gfp; +}; + +struct p2p_ps_info *ath6kl_p2p_ps_init(struct ath6kl_vif *vif); +void ath6kl_p2p_ps_deinit(struct ath6kl_vif *vif); + +int ath6kl_p2p_ps_reset_noa(struct p2p_ps_info *p2p_ps); +int ath6kl_p2p_ps_setup_noa(struct p2p_ps_info *p2p_ps, + int noa_id, + u8 count_type, + u32 interval, + u32 start_offset, + u32 duration); + +int ath6kl_p2p_ps_reset_opps(struct p2p_ps_info *p2p_ps); +int ath6kl_p2p_ps_setup_opps(struct p2p_ps_info *p2p_ps, + u8 enabled, + u8 ctwindows); + +int ath6kl_p2p_ps_update_notif(struct p2p_ps_info *p2p_ps); +void ath6kl_p2p_ps_user_app_ie(struct p2p_ps_info *p2p_ps, u8 mgmt_frm_type, + u8 **ie, int *len); + +int ath6kl_p2p_utils_trans_porttype(enum nl80211_iftype type, + u8 *opmode, + u8 *subopmode); +int ath6kl_p2p_utils_init_port(struct ath6kl_vif *vif, + enum nl80211_iftype type); +int ath6kl_p2p_utils_check_port(struct ath6kl_vif *vif, + u8 port_id); + +struct ath6kl_p2p_flowctrl *ath6kl_p2p_flowctrl_conn_list_init( + struct ath6kl *ar); +void ath6kl_p2p_flowctrl_conn_collect_by_conn( + struct ath6kl_fw_conn_list *fw_conn, struct list_head *pcontainer, + int *preclaim); +void ath6kl_p2p_flowctrl_conn_list_deinit(struct ath6kl *ar); +void ath6kl_p2p_flowctrl_conn_list_cleanup(struct ath6kl *ar); +void ath6kl_p2p_flowctrl_conn_list_cleanup_by_if(struct ath6kl_vif *vif); +void ath6kl_p2p_flowctrl_netif_transition(struct ath6kl *ar, + u8 new_state); +void ath6kl_p2p_flowctrl_tx_schedule(struct ath6kl *ar); +int ath6kl_p2p_flowctrl_tx_schedule_pkt(struct ath6kl *ar, void *pkt); +void ath6kl_p2p_flowctrl_state_change(struct ath6kl *ar); +void ath6kl_p2p_flowctrl_state_update(struct ath6kl *ar, + u8 numConn, + u8 ac_map[], + u8 ac_queue_depth[]); +void ath6kl_p2p_flowctrl_set_conn_id(struct ath6kl_vif *vif, + u8 mac_addr[], + u8 connId); +u8 ath6kl_p2p_flowctrl_get_conn_id(struct ath6kl_vif *vif, + struct sk_buff *skb); +int ath6kl_p2p_flowctrl_stat(struct ath6kl *ar, + u8 *buf, int buf_len); + + +struct ath6kl_p2p_rc_info *ath6kl_p2p_rc_init(struct ath6kl *ar); +void ath6kl_p2p_rc_deinit(struct ath6kl *ar); +void ath6kl_p2p_rc_fetch_chan(struct ath6kl *ar); +void ath6kl_p2p_rc_bss_info(struct ath6kl_vif *vif, + u8 snr, + struct ieee80211_channel *channel); +void ath6kl_p2p_rc_scan_start(struct ath6kl_vif *vif); +int ath6kl_p2p_rc_scan_complete_event(struct ath6kl_vif *vif, bool aborted); +int ath6kl_p2p_rc_get(struct ath6kl *ar, + u16 *rc_2g, + u16 *rc_5g, + u16 *rc_all, + u16 *rc_p2p_social, + u16 *rc_p2p_2g, + u16 *rc_p2p_5g, + u16 *rc_p2p_all); +int ath6kl_p2p_rc_dump(struct ath6kl *ar, u8 *buf, int buf_len); + +bool ath6kl_p2p_frame_retry(struct ath6kl *ar, u8 *frm, int len); +bool ath6kl_p2p_is_p2p_frame(struct ath6kl *ar, const u8 *frm, size_t len); +void ath6kl_p2p_connect_event(struct ath6kl_vif *vif, + u8 beacon_ie_len, + u8 assoc_req_len, + u8 assoc_resp_len, + u8 *assoc_info); +bool ath6kl_p2p_pending_connect_event(struct ath6kl_vif *vif, + const u8 *bssid, + const u8 *req_ie, + size_t req_ie_len, + const u8 *resp_ie, + size_t resp_ie_len, + u16 status, + gfp_t gfp); +void ath6kl_p2p_pending_disconnect_event(struct ath6kl_vif *vif, + u16 reason, + u8 *ie, + size_t ie_len, + gfp_t gfp); +bool ath6kl_p2p_ie_append(struct ath6kl_vif *vif, u8 mgmt_frame_type); +bool ath6kl_p2p_is_p2p_channel(u32 freq); +bool ath6kl_p2p_is_social_channel(u32 freq); +#endif + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/pm.c b/drivers/net/wireless/ath/ath6kl-3.5/pm.c new file mode 100644 index 000000000000..9086d875c8c2 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/pm.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "hif-ops.h" + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ath6kl_early_suspend(struct early_suspend *handler) +{ + struct ath6kl *ar = container_of(handler, struct ath6kl, early_suspend); + + if (ar) + ath6kl_hif_early_suspend(ar); +} + +static void ath6kl_late_resume(struct early_suspend *handler) +{ + struct ath6kl *ar = container_of(handler, struct ath6kl, early_suspend); + + if (ar) + ath6kl_hif_late_resume(ar); +} +#endif + +#ifdef CONFIG_ANDROID +void ath6kl_setup_android_resource(struct ath6kl *ar) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + ar->early_suspend.suspend = ath6kl_early_suspend; + ar->early_suspend.resume = ath6kl_late_resume; + ar->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; + register_early_suspend(&ar->early_suspend); +#endif +} + +void ath6kl_cleanup_android_resource(struct ath6kl *ar) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ar->early_suspend); +#endif +} + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/pm.h b/drivers/net/wireless/ath/ath6kl-3.5/pm.h new file mode 100644 index 000000000000..d7664e5f94fa --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/pm.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 CONFIG_ANDROID +void ath6kl_setup_android_resource(struct ath6kl *ar); +void ath6kl_cleanup_android_resource(struct ath6kl *ar); +#endif + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/qcomwlan_pwrif.c b/drivers/net/wireless/ath/ath6kl-3.5/qcomwlan_pwrif.c new file mode 100755 index 000000000000..ed94a9e12fba --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/qcomwlan_pwrif.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#define GPIO_WLAN_DEEP_SLEEP_N 89 +#define GPIO_WLAN_DEEP_SLEEP_N_DRAGON 89 +#define WLAN_RESET_OUT 1 +#define WLAN_RESET 0 + +#define GPIO_BT_SLEEP_N 121 +#define BT_RESET_OUT 1 +#define BT_RESET 0 + +static const char *id = "WLAN"; + +/** + * vos_chip_power_qca6234() - WLAN Power Up Seq for WCN1314 rev 2.0 on qca6234 + * @on - Turn WLAN ON/OFF (1 or 0) + * + * Power up/down WLAN by turning on/off various regs and asserting/deasserting + * Power-on-reset pin. Also, put XO A0 buffer as slave to wlan_clk_pwr_req while + * turning ON WLAN and vice-versa. + * + * This function returns 0 on success or a non-zero value on failure. + */ +int vos_chip_power_qca6234(int on) +{ + static char wlan_on; + static const char *vregs_qwlan_name[] = { + "8921_l10", + "8921_s4", + }; + + static const int vregs_qwlan_val_min[] = { + 3300000, + 1800000, + }; + static const int vregs_qwlan_val_max[] = { + 3300000, + 1800000, + }; + static const int vregs_qwlan_peek_current[] = { + 150000, + 150000, + }; + + static struct regulator *vregs_qwlan[ARRAY_SIZE(vregs_qwlan_name)]; + static struct msm_xo_voter *wlan_clock; + int ret, i, rc = 0; + unsigned wlan_gpio_deep_sleep = GPIO_WLAN_DEEP_SLEEP_N; + /* */ + + rc = gpio_request(128, "GPIO 10"); + if (rc) { + pr_err("WLAN GPIO 10 request failed\n"); + goto fail; + } + rc = gpio_direction_output(128, 1); + + /* WLAN RESET and CLK settings */ + if (on && !wlan_on) { + /* + * Program U12 GPIO expander pin IO1 to de-assert (drive 0) + * WLAN_EXT_POR_N to put WLAN in reset + */ + rc = gpio_request(wlan_gpio_deep_sleep, "WLAN_DEEP_SLEEP_N"); + if (rc) { + pr_err("WLAN reset GPIO %d request failed\n", + wlan_gpio_deep_sleep); + goto fail; + } + rc = gpio_direction_output(wlan_gpio_deep_sleep, + WLAN_RESET); + if (rc < 0) { + pr_err("WLAN reset GPIO %d set output direction failed", + wlan_gpio_deep_sleep); + goto fail_gpio_dir_out; + } + + rc = gpio_request(GPIO_BT_SLEEP_N, "BT_DEEP_SLEEP_N"); + if (rc) { + pr_err("BT reset GPIO %d request failed\n", + GPIO_BT_SLEEP_N); + goto fail; + } + rc = gpio_direction_output(GPIO_BT_SLEEP_N, + BT_RESET); + if (rc < 0) { + pr_err("BT reset GPIO %d set output direction failed", + GPIO_BT_SLEEP_N); + goto fail_gpio_dir_out; + } + + + /* Configure TCXO to be slave to WLAN_CLK_PWR_REQ */ + if (wlan_clock == NULL) { + wlan_clock = msm_xo_get(MSM_XO_TCXO_A0, id); + if (IS_ERR(wlan_clock)) { + pr_err("Failed to get TCXO_A0 voter (%ld)\n", + PTR_ERR(wlan_clock)); + goto fail_gpio_dir_out; + } + } + + rc = msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_PIN_CTRL); + if (rc < 0) { + pr_err("Configuring TCXO to Pin controllable failed" + "(%d)\n", rc); + goto fail_xo_mode_vote; + } + } else if (!on && wlan_on) { + if (wlan_clock != NULL) + msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_OFF); + gpio_set_value_cansleep(wlan_gpio_deep_sleep, WLAN_RESET); + gpio_free(wlan_gpio_deep_sleep); + gpio_set_value_cansleep(GPIO_BT_SLEEP_N, BT_RESET); + gpio_free(GPIO_BT_SLEEP_N); + } + + /* WLAN VREG settings */ + for (i = 0; i < ARRAY_SIZE(vregs_qwlan_name); i++) { + if (on && !wlan_on) { + vregs_qwlan[i] = regulator_get(NULL, + vregs_qwlan_name[i]); + if (IS_ERR(vregs_qwlan[i])) { + pr_err("regulator get of %s failed (%ld)\n", + vregs_qwlan_name[i], + PTR_ERR(vregs_qwlan[i])); + rc = PTR_ERR(vregs_qwlan[i]); + goto vreg_get_fail; + } + if (vregs_qwlan_val_min[i] || vregs_qwlan_val_max[i]) { + rc = regulator_set_voltage(vregs_qwlan[i], + vregs_qwlan_val_min[i], + vregs_qwlan_val_max[i]); + if (rc) { + pr_err("regulator_set_voltage(%s) failed\n", + vregs_qwlan_name[i]); + goto vreg_fail; + } + } + + if (vregs_qwlan_peek_current[i]) { + rc = regulator_set_optimum_mode(vregs_qwlan[i], + vregs_qwlan_peek_current[i]); + if (rc < 0) + pr_err("vreg %s set optimum mode" + " failed to %d (%d)\n", + vregs_qwlan_name[i], rc, + vregs_qwlan_peek_current[i]); + } + rc = regulator_enable(vregs_qwlan[i]); + if (rc < 0) { + pr_err("vreg %s enable failed (%d)\n", + vregs_qwlan_name[i], rc); + goto vreg_fail; + } + + } else if (!on && wlan_on) { + + if (vregs_qwlan_peek_current[i]) { + /* For legacy reasons we pass 1mA current to + * put regulator in LPM mode. + */ + rc = regulator_set_optimum_mode(vregs_qwlan[i], + 1000); + if (rc < 0) + pr_info("vreg %s set optimum mode" + "failed (%d)\n", + vregs_qwlan_name[i], rc); + rc = regulator_set_voltage(vregs_qwlan[i], 0 , + vregs_qwlan_val_max[i]); + if (rc) + pr_err("regulator_set_voltage(%s)" + "failed (%d)\n", + vregs_qwlan_name[i], rc); + + } + + rc = regulator_disable(vregs_qwlan[i]); + if (rc < 0) { + pr_err("vreg %s disable failed (%d)\n", + vregs_qwlan_name[i], rc); + goto vreg_fail; + } + regulator_put(vregs_qwlan[i]); + } + } + if (on) { + gpio_set_value_cansleep(wlan_gpio_deep_sleep, WLAN_RESET_OUT); + wlan_on = true; + mdelay(6); + gpio_set_value_cansleep(GPIO_BT_SLEEP_N, BT_RESET_OUT); + + } + else + wlan_on = false; + return 0; + +vreg_fail: + regulator_put(vregs_qwlan[i]); + +vreg_get_fail: + i--; + while (i >= 0) { + ret = !on ? regulator_enable(vregs_qwlan[i]) : + regulator_disable(vregs_qwlan[i]); + if (ret < 0) { + pr_err("vreg %s %s failed (%d) in err path\n", + vregs_qwlan_name[i], + !on ? "enable" : "disable", ret); + } + + regulator_put(vregs_qwlan[i]); + + i--; + } + if (!on) + goto fail; +fail_xo_mode_vote: + msm_xo_put(wlan_clock); +fail_gpio_dir_out: + gpio_free(wlan_gpio_deep_sleep); +fail: + return rc; +} +EXPORT_SYMBOL(vos_chip_power_qca6234); + +int WIFI_RF_status; +EXPORT_SYMBOL(WIFI_RF_status); + +int BT_RF_status; +EXPORT_SYMBOL(BT_RF_status); + +void qca6234_wifi_gpio(bool on) +{ + gpio_direction_output(GPIO_WLAN_DEEP_SLEEP_N, on); +} +EXPORT_SYMBOL(qca6234_wifi_gpio); + +void qca6234_bt_gpio(bool on) +{ + gpio_direction_output(GPIO_BT_SLEEP_N, on); +} +EXPORT_SYMBOL(qca6234_bt_gpio); diff --git a/drivers/net/wireless/ath/ath6kl-3.5/reg.c b/drivers/net/wireless/ath/ath6kl-3.5/reg.c new file mode 100644 index 000000000000..202dbccedee1 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/reg.c @@ -0,0 +1,820 @@ +/* + * Copyright (c) 2004-2013 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "debug.h" + +/* Default Regulatory for invalid reg_code. */ +static struct ieee80211_regdomain ath6kl_regd_NA = { + .n_reg_rules = 1, + .alpha2 = "NA", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 20, 3, 20, 0), + } +}; + +/* + * Naming from ISO 3166 + */ +static const struct reg_code_to_isoname ath6kl_country_code_to_iso_name[] = { + /* Country Code */ + {COUNTRY_ALBANIA, "AL", NULL}, + {COUNTRY_ALGERIA, "DZ", NULL}, + {COUNTRY_ARGENTINA, "AR", NULL}, + {COUNTRY_ARMENIA, "AM", NULL}, + {COUNTRY_ARUBA, "AW", NULL}, + {COUNTRY_AUSTRALIA, "AU", NULL}, + {COUNTRY_AUSTRALIA_AP, "A2", NULL}, + {COUNTRY_AUSTRIA, "AT", NULL}, + {COUNTRY_AZERBAIJAN, "AZ", NULL}, + {COUNTRY_BAHRAIN, "BH", NULL}, + {COUNTRY_BANGLADESH, "BD", NULL}, + {COUNTRY_BARBADOS, "BB", NULL}, + {COUNTRY_BELARUS, "BY", NULL}, + {COUNTRY_BELGIUM, "BE", NULL}, + {COUNTRY_BELIZE, "BZ", NULL}, + {COUNTRY_BOLIVIA, "BO", NULL}, + {COUNTRY_BOSNIA_HERZEGOWANIA, "BA", NULL}, + {COUNTRY_BRAZIL, "BR", NULL}, + {COUNTRY_BRUNEI_DARUSSALAM, "BN", NULL}, + {COUNTRY_BULGARIA, "BG", NULL}, + {COUNTRY_CAMBODIA, "KH", NULL}, + {COUNTRY_CANADA, "CA", NULL}, + {COUNTRY_CANADA_AP, "C2", NULL}, + {COUNTRY_CHILE, "CL", NULL}, + {COUNTRY_CHINA, "CN", NULL}, + {COUNTRY_COLOMBIA, "CO", NULL}, + {COUNTRY_COSTA_RICA, "CR", NULL}, + {COUNTRY_CROATIA, "HR", NULL}, + {COUNTRY_CYPRUS, "CY", NULL}, + {COUNTRY_CZECH, "CZ", NULL}, + {COUNTRY_DENMARK, "DK", NULL}, + {COUNTRY_DOMINICAN_REPUBLIC, "DO", NULL}, + {COUNTRY_ECUADOR, "EC", NULL}, + {COUNTRY_EGYPT, "EG", NULL}, + {COUNTRY_EL_SALVADOR, "SV", NULL}, + {COUNTRY_ESTONIA, "EE", NULL}, + {COUNTRY_FAEROE_ISLANDS, "FO", NULL}, /* NOT SUPPORT */ + {COUNTRY_FINLAND, "FI", NULL}, + {COUNTRY_FRANCE, "FR", NULL}, + {COUNTRY_FRANCE2, "F2", NULL}, + {COUNTRY_GEORGIA, "GE", NULL}, + {COUNTRY_GERMANY, "DE", NULL}, + {COUNTRY_GREECE, "GR", NULL}, + {COUNTRY_GREENLAND, "GL", NULL}, + {COUNTRY_GRENADA, "GD", NULL}, + {COUNTRY_GUAM, "GU", NULL}, + {COUNTRY_GUATEMALA, "GT", NULL}, + {COUNTRY_HAITI, "HT", NULL}, + {COUNTRY_HONDURAS, "HN", NULL}, + {COUNTRY_HONG_KONG, "KH", NULL}, + {COUNTRY_HUNGARY, "HU", NULL}, + {COUNTRY_ICELAND, "IS", NULL}, + {COUNTRY_INDIA, "IN", NULL}, + {COUNTRY_INDONESIA, "ID", NULL}, + {COUNTRY_IRAN, "IR", NULL}, + {COUNTRY_IRAQ, "IQ", NULL}, /* NOT SUPPORT */ + {COUNTRY_IRELAND, "IE", NULL}, + {COUNTRY_ISRAEL, "IL", NULL}, + {COUNTRY_ITALY, "IT", NULL}, + {COUNTRY_JAMAICA, "JM", NULL}, + {COUNTRY_JAPAN, "JP", NULL}, + {COUNTRY_JAPAN1, "J2", NULL}, /* NOT SUPPORT */ + {COUNTRY_JAPAN2, "J3", NULL}, /* NOT SUPPORT */ + {COUNTRY_JAPAN3, "J4", NULL}, /* NOT SUPPORT */ + {COUNTRY_JAPAN4, "J5", NULL}, /* NOT SUPPORT */ + {COUNTRY_JAPAN5, "J6", NULL}, /* NOT SUPPORT */ + {COUNTRY_JAPAN6, "J7", NULL}, /* NOT SUPPORT */ + {COUNTRY_JORDAN, "JO", NULL}, + {COUNTRY_KAZAKHSTAN, "KZ", NULL}, + {COUNTRY_KENYA, "KE", NULL}, + {COUNTRY_KOREA_NORTH, "KP", NULL}, + {COUNTRY_KOREA_ROC, "KR", NULL}, + {COUNTRY_KOREA_ROC2, "K2", NULL}, + {COUNTRY_KOREA_ROC3, "K3", NULL}, + {COUNTRY_KUWAIT, "KW", NULL}, + {COUNTRY_LATVIA, "LV", NULL}, + {COUNTRY_LEBANON, "LE", NULL}, + {COUNTRY_LIBYA, "LY", NULL}, /* NOT SUPPORT */ + {COUNTRY_LIECHTENSTEIN, "LI", NULL}, + {COUNTRY_LITHUANIA, "LT", NULL}, + {COUNTRY_LUXEMBOURG, "LU", NULL}, + {COUNTRY_MACAU, "MO", NULL}, + {COUNTRY_MACEDONIA, "MK", NULL}, + {COUNTRY_MALAYSIA, "MY", NULL}, + {COUNTRY_MALTA, "MT", NULL}, + {COUNTRY_MEXICO, "MX", NULL}, + {COUNTRY_MONACO, "MC", NULL}, + {COUNTRY_MOROCCO, "MA", NULL}, + {COUNTRY_NEPAL, "NP", NULL}, + {COUNTRY_NETHERLANDS, "NL", NULL}, + {COUNTRY_NETHERLAND_ANTILLES, "AN", NULL}, + {COUNTRY_NEW_ZEALAND, "NZ", NULL}, + {COUNTRY_NICARAGUA, "NI", NULL}, /* NOT SUPPORT */ + {COUNTRY_NORWAY, "NO", NULL}, + {COUNTRY_OMAN, "OM", NULL}, + {COUNTRY_PAKISTAN, "PK", NULL}, + {COUNTRY_PANAMA, "PA", NULL}, + {COUNTRY_PARAGUAY, "PY", NULL}, /* NOT SUPPORT */ + {COUNTRY_PERU, "PE", NULL}, + {COUNTRY_PHILIPPINES, "PH", NULL}, + {COUNTRY_POLAND, "PL", NULL}, + {COUNTRY_PORTUGAL, "PT", NULL}, + {COUNTRY_PUERTO_RICO, "PR", NULL}, + {COUNTRY_QATAR, "QA", NULL}, + {COUNTRY_ROMANIA, "RO", NULL}, + {COUNTRY_RUSSIA, "RU", NULL}, + {COUNTRY_RWANDA, "RW", NULL}, + {COUNTRY_SAUDI_ARABIA, "SA", NULL}, + {COUNTRY_SERBIA, "RS", NULL}, /* NOT SUPPORT */ + {COUNTRY_MONTENEGRO, "ME", NULL}, + {COUNTRY_SINGAPORE, "SG", NULL}, + {COUNTRY_SLOVAKIA, "SK", NULL}, + {COUNTRY_SLOVENIA, "SI", NULL}, + {COUNTRY_SOUTH_AFRICA, "ZA", NULL}, + {COUNTRY_SPAIN, "ES", NULL}, + {COUNTRY_SRILANKA, "LK", NULL}, + {COUNTRY_SWEDEN, "SE", NULL}, + {COUNTRY_SWITZERLAND, "CH", NULL}, + {COUNTRY_SYRIA, "SY", NULL}, + {COUNTRY_TAIWAN, "TW", NULL}, + {COUNTRY_THAILAND, "TH", NULL}, + {COUNTRY_TRINIDAD_Y_TOBAGO, "TT", NULL}, + {COUNTRY_TUNISIA, "TN", NULL}, + {COUNTRY_TURKEY, "TR", NULL}, + {COUNTRY_UAE, "AE", NULL}, + {COUNTRY_UGANDA, "UG", NULL}, + {COUNTRY_UKRAINE, "UA", NULL}, + {COUNTRY_UNITED_KINGDOM, "GB", NULL}, + {COUNTRY_UNITED_STATES, "US", NULL}, + {COUNTRY_UNITED_STATES_AP, "U2", NULL}, + {COUNTRY_UNITED_STATES_PS, "PS", NULL}, + {COUNTRY_URUGUAY, "UY", NULL}, + {COUNTRY_UZBEKISTAN, "UZ", NULL}, + {COUNTRY_VENEZUELA, "VE", NULL}, + {COUNTRY_VIET_NAM, "VN", NULL}, + {COUNTRY_YEMEN, "YE", NULL}, + {COUNTRY_ZIMBABWE, "ZW", NULL}, + /* keep last */ + {NULL_REG_CODE, NULL}, +}; + +static const struct reg_code_to_isoname ath6kl_region_code_to_name[] = { + /* Region Code */ + {REGION_NO_ENUMRD, "00", NULL}, /* DEBUG */ + {REGION_NULL1_WORLD, "03", "AL"}, + {REGION_NULL1_ETSIB, "07", NULL}, + {REGION_NULL1_ETSIC, "08", NULL}, + {REGION_FCC1_FCCA, "10", "CO"}, + {REGION_FCC1_WORLD, "11", "CR"}, + {REGION_FCC2_FCCA, "20", NULL}, + {REGION_FCC2_WORLD, "21", "BB"}, + {REGION_FCC2_ETSIC, "22", NULL}, + {REGION_FCC3_FCCA, "3a", "CA"}, + {REGION_FCC3_WORLD, "3b", "AR"}, + {REGION_FCC3_ETSIC, "3f", "NZ"}, + {REGION_FCC4_FCCA, "12", "PS"}, + {REGION_FCC5_FCCA, "13", NULL}, + {REGION_FCC5_WORLD, "16", NULL}, + {REGION_FCC6_FCCA, "14", "CA"}, + {REGION_FCC6_WORLD, "23", "AU"}, + {REGION_ETSI1_WORLD, "37", "AW"}, + {REGION_ETSI2_WORLD, "35", "JO"}, + {REGION_ETSI3_WORLD, "36", "CY"}, + {REGION_ETSI4_WORLD, "30", "AM"}, + {REGION_ETSI4_ETSIC, "38", NULL}, /* NOT SUPPORT */ + {REGION_ETSI5_WORLD, "39", NULL}, + {REGION_ETSI6_WORLD, "34", NULL}, + {REGION_ETSI8_WORLD, "3d", "RU"}, + {REGION_ETSI9_WORLD, "3e", "UA"}, + {REGION_ETSI_RESERVED, "33", NULL}, /* NOT SUPPORT */ + {REGION_FRANCE_RES, "31", NULL}, + {REGION_APL6_WORLD, "5b", "BH"}, + {REGION_APL4_WORLD, "42", "MA"}, + {REGION_APL3_FCCA, "50", NULL}, + {REGION_APL_RESERVED, "44", NULL}, /* NOT SUPPORT */ + {REGION_APL2_WORLD, "45", "ID"}, + {REGION_APL2_APLC, "46", NULL}, /* NOT SUPPORT */ + {REGION_APL3_WORLD, "47", NULL}, + {REGION_APL2_APLD, "49", "KR"}, + {REGION_APL2_FCCA, "4d", NULL}, + {REGION_APL1_WORLD, "52", "CN"}, + {REGION_APL1_FCCA, "53", NULL}, /* NOT SUPPORT */ + {REGION_APL1_ETSIC, "55", "BZ"}, + {REGION_APL2_ETSIC, "56", NULL}, + {REGION_APL5_WORLD, "58", NULL}, + {REGION_APL7_FCCA, "5c", "TW"}, + {REGION_APL8_WORLD, "5d", NULL}, + {REGION_APL9_WORLD, "5e", "KP"}, + {REGION_APL10_WORLD, "5f", "KR"}, + {REGION_MKK5_MKKA, "99", NULL}, + {REGION_MKK5_FCCA, "9a", NULL}, + {REGION_MKK5_MKKC, "88", "JP"}, + {REGION_MKK11_MKKA, "d4", NULL}, + {REGION_MKK11_FCCA, "d5", NULL}, + {REGION_MKK11_MKKC, "d7", NULL}, + {REGION_WOR0_WORLD, "60", "00"}, + {REGION_WOR1_WORLD, "61", "00"}, + {REGION_WOR2_WORLD, "62", "00"}, + {REGION_WOR3_WORLD, "63", "00"}, + {REGION_WOR4_WORLD, "64", "00"}, + {REGION_WOR5_ETSIC, "65", "00"}, + {REGION_WOR01_WORLD, "66", "00"}, + {REGION_WOR02_WORLD, "67", "00"}, + {REGION_EU1_WORLD, "68", "00"}, + {REGION_WOR9_WORLD, "69", "00"}, + {REGION_WORA_WORLD, "6a", "00"}, /* Default region code */ + {REGION_WORB_WORLD, "6b", "00"}, + {REGION_WORC_WORLD, "6c", "00"}, + + /* keep last */ + {NULL_REG_CODE, NULL}, +}; + +static inline bool __is_ht40_not_allowed(struct ieee80211_channel *chan) +{ + if (!chan) + return true; + + /* HT40 is not allowed in CH12, CH13 and CH14. */ + if ((chan->center_freq == 2467) || + (chan->center_freq == 2472) || + (chan->center_freq == 2484)) + return true; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + return true; + + if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) + return true; + + return false; +} + +static void _reg_process_chan(struct ieee80211_supported_band *sband, + int chan_idx) +{ + struct ieee80211_channel *channel; + struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; + unsigned int i; + u16 start_freq = 0; + + channel = &sband->channels[chan_idx]; + + if (__is_ht40_not_allowed(channel)) { + channel->flags |= IEEE80211_CHAN_NO_HT40; + return; + } + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *c = &sband->channels[i]; + + if (c->center_freq == (channel->center_freq - 20)) + channel_before = c; + if (c->center_freq == (channel->center_freq + 20)) + channel_after = c; + } + + /* Only update it the channel still not yet marked as HT40+/HT40-.*/ + if (channel->center_freq < 4000) { + if (!(channel->flags & IEEE80211_CHAN_NO_HT40MINUS)) { + if (__is_ht40_not_allowed(channel_before)) + channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; + else + channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; + } + + if (!(channel->flags & IEEE80211_CHAN_NO_HT40PLUS)) { + if (__is_ht40_not_allowed(channel_after)) + channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; + else + channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; + } + } else { /* No overlap HT40 in 5G. */ + /* + * Don't care 5170(CH34) - 5230(CH46) because no HT40 + * channel in it. + */ + if (channel->center_freq >= 5745) + start_freq = 5745; /* Upper U-NII */ + else + start_freq = 5180; + + if (((channel->center_freq - start_freq) % 40) == 0) { + /* HT40+ only channel */ + channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; + + if (!(channel->flags & IEEE80211_CHAN_NO_HT40PLUS)) { + if (__is_ht40_not_allowed(channel_after)) + channel->flags |= + IEEE80211_CHAN_NO_HT40PLUS; + else + channel->flags &= + ~IEEE80211_CHAN_NO_HT40PLUS; + } + } else { + /* HT40- only channel */ + channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; + + if (!(channel->flags & IEEE80211_CHAN_NO_HT40MINUS)) { + if (__is_ht40_not_allowed(channel_before)) + channel->flags |= + IEEE80211_CHAN_NO_HT40MINUS; + else + channel->flags &= + ~IEEE80211_CHAN_NO_HT40MINUS; + } + } + } + + return; +} + +static void ath6kl_reg_chan_flags_update(struct reg_info *reg) +{ + struct wiphy *wiphy = reg->wiphy; + struct ieee80211_supported_band *sband; + enum ieee80211_band band; + int i; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (wiphy->bands[band]) { + sband = wiphy->bands[band]; + for (i = 0; i < sband->n_channels; i++) + _reg_process_chan(sband, i); + } + } + + return; +} + +static const char *_reg_find_iso_name(u16 reg_code, + bool isCountry, + bool reg2Ctry) +{ + const struct reg_code_to_isoname *name; + + if (isCountry) + reg2Ctry = false; + + if (isCountry) + name = &ath6kl_country_code_to_iso_name[0]; + else + name = &ath6kl_region_code_to_name[0]; + while (name) { + if ((name->reg_code == reg_code) && + (name->iso_name)) { + if (reg2Ctry) + return name->reg_to_ctry_iso_name; + else + return name->iso_name; + } + + name++; + } + + ath6kl_err("reg iosname search fail! code is 0x%x\n", reg_code); + + return NULL; +} + +static struct ieee80211_regdomain *_reg_find_regd(u16 code, + bool isCountry) +{ + struct ieee80211_regdomain *regd; + struct ieee80211_regdomain **reg_regdb; + const char *alpha2 = _reg_find_iso_name(code, isCountry, false); + + if (alpha2 == NULL) + return NULL; + + if (isCountry) + reg_regdb = (struct ieee80211_regdomain **) + ath6kl_reg_regdb_country; + else + reg_regdb = (struct ieee80211_regdomain **) + ath6kl_reg_regdb_region; + + while (*reg_regdb) { + regd = *reg_regdb; + if ((regd->alpha2[0] == alpha2[0]) && + (regd->alpha2[1] == alpha2[1])) + return regd; + + reg_regdb++; + } + + ath6kl_err("reg regd search fail! code is 0x%x\n", code); + + return NULL; +} + +static struct ieee80211_regdomain *ath6kl_reg_get_regd(struct reg_info *reg, + u32 reg_code) +{ + struct ieee80211_regdomain *regd = NULL; + u16 code, code_type; + + /* + * If it's a country code and search country code list first, + * then region domain list. And, assume it must be a valid code. + * If return is NULL and back to use the default's. + */ + code = (u16)(reg_code & ATH6KL_REG_CODE_MASK); + code_type = (u16)(reg_code >> ATH6KL_COUNTRY_RD_SHIFT); + if (code_type & ATH6KL_COUNTRY_ERD_FLAG) + regd = _reg_find_regd(code, true); + else + regd = _reg_find_regd(code, false); + + if (regd == NULL) { + regd = &ath6kl_regd_NA; + + ath6kl_err("reg get regd fail, using default NA.\n"); + } + + ath6kl_dbg(ATH6KL_DBG_REGDB, + "reg code 0x%0x, %s code, WWR %s --> %c%c, %d rules\n", + reg_code, + (code_type & ATH6KL_COUNTRY_ERD_FLAG) ? + "Country" : "Region", + (code_type & ATH6KL_WORLDWIDE_ROAMING_FLAG) ? + "ON" : "OFF", + regd->alpha2[0], regd->alpha2[1], + regd->n_reg_rules); + + return regd; +} + +static int __reg_freq_reg_info_regd(u32 center_freq, + u32 desired_bw_khz, + const struct ieee80211_reg_rule **reg_rule, + const struct ieee80211_regdomain *custom_regd, + bool *is_freq_start, + bool *is_freq_end) +{ +#define ONE_GHZ_IN_KHZ 1000000 + int i; + bool band_rule_found = false; + bool bw_fits = false; + u32 start_freq_khz, end_freq_khz; + + if (!desired_bw_khz) + desired_bw_khz = MHZ_TO_KHZ(20); + + if (!custom_regd) + return -EINVAL; + + for (i = 0; i < custom_regd->n_reg_rules; i++) { + const struct ieee80211_reg_rule *rr; + const struct ieee80211_freq_range *fr = NULL; + + rr = &custom_regd->reg_rules[i]; + fr = &rr->freq_range; + + if (!band_rule_found) { + if (abs(center_freq - fr->start_freq_khz) <= + (2 * ONE_GHZ_IN_KHZ)) + band_rule_found = true; + if (abs(center_freq - fr->end_freq_khz) <= + (2 * ONE_GHZ_IN_KHZ)) + band_rule_found = true; + } + + start_freq_khz = center_freq - (desired_bw_khz/2); + end_freq_khz = center_freq + (desired_bw_khz/2); + + if (band_rule_found && + fr->start_freq_khz > MHZ_TO_KHZ(4000) && + start_freq_khz >= fr->start_freq_khz && + end_freq_khz <= fr->end_freq_khz && + ((center_freq - fr->start_freq_khz) % MHZ_TO_KHZ(20)) == 0) + return -ERANGE; + + if (start_freq_khz >= fr->start_freq_khz && + end_freq_khz <= fr->end_freq_khz) + bw_fits = true; + else + bw_fits = false; + + if (band_rule_found && bw_fits) { + if (center_freq > MHZ_TO_KHZ(4000)) { + if (start_freq_khz == fr->start_freq_khz) + *is_freq_start = true; + if (end_freq_khz == fr->end_freq_khz) + *is_freq_end = true; + } + *reg_rule = rr; + return 0; + } + } + + if (!band_rule_found) + return -ERANGE; + + return -EINVAL; +#undef ONE_GHZ_IN_KHZ +} + +static void _reg_handle_channel(struct ieee80211_channel *chan, + const struct ieee80211_regdomain *regd) +{ + int r; + u32 desired_bw_khz = MHZ_TO_KHZ(20); + u32 bw_flags = 0; + u32 channel_flags = 0; + const struct ieee80211_reg_rule *reg_rule = NULL; + const struct ieee80211_power_rule *power_rule = NULL; + const struct ieee80211_freq_range *freq_range = NULL; + bool is_freq_start = false, is_freq_end = false; + + r = __reg_freq_reg_info_regd(MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, + ®_rule, + regd, + &is_freq_start, + &is_freq_end); + + if (r) { + chan->flags = + chan->orig_flags = IEEE80211_CHAN_DISABLED; + return; + } + + power_rule = ®_rule->power_rule; + freq_range = ®_rule->freq_range; + + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) + bw_flags = IEEE80211_CHAN_NO_HT40; + else { + /* + * A hint to let _reg_process_chan() know that this channel + * is a boundry channel and HT40+/HT40- already known. + */ + if (is_freq_start) + bw_flags |= IEEE80211_CHAN_NO_HT40MINUS; + + if (is_freq_end) + bw_flags |= IEEE80211_CHAN_NO_HT40PLUS; + } + + if (reg_rule->flags & NL80211_RRF_PASSIVE_SCAN) + channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; + if (reg_rule->flags & NL80211_RRF_NO_IBSS) + channel_flags |= IEEE80211_CHAN_NO_IBSS; + if (reg_rule->flags & NL80211_RRF_DFS) + channel_flags |= IEEE80211_CHAN_RADAR; + + chan->flags = (channel_flags | bw_flags); + chan->orig_flags = chan->flags; + chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); + chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); + + return; +} + +static void ath6kl_reg_apply_regulatory(struct reg_info *reg, + const struct ieee80211_regdomain *regd) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + enum ieee80211_band band; + int i; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!reg->wiphy->bands[band]) + continue; + + sband = reg->wiphy->bands[band]; + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + _reg_handle_channel(chan, regd); + + /* + * If this channel is P2P allowed and not marked + * as PASSIVE/IBSS to let wpa_supplicant use it. + */ + if ((reg->flags & ATH6KL_REG_FALGS_P2P_IN_PASV_CHAN) && + (!(chan->flags & IEEE80211_CHAN_RADAR)) && + ath6kl_p2p_is_p2p_channel(chan->center_freq)) { + chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + chan->flags &= ~IEEE80211_CHAN_NO_IBSS; + } + } + } +} + +static struct ieee80211_regdomain *ath6kl_reg_update(struct reg_info *reg, + u32 reg_code, + bool target_update) +{ + struct ieee80211_regdomain *regd; + + BUG_ON(!reg); + + ath6kl_dbg(ATH6KL_DBG_REGDB, + "reg update reg_code %x %sfrom target\n", + reg_code, + target_update ? "" : "not "); + + + /* Query the local regulatory database from alpha2 words. */ + regd = ath6kl_reg_get_regd(reg, reg_code); + + /* HACK: Fetch local regulatory database to support channel list. */ + ath6kl_reg_apply_regulatory(reg, regd); + + /* Update the channel flags. */ + ath6kl_reg_chan_flags_update(reg); + + if (target_update && regd) + regulatory_hint(reg->wiphy, regd->alpha2); + + /* Notify to update the channel record. */ + ath6kl_p2p_rc_fetch_chan(reg->ar); + + return regd; +} + +int ath6kl_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + char initiatorString[4][16] = { + "driver", + "core", + "user", + "country-ie", + }; + struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); + struct reg_info *reg = ar->reg_ctx; + struct ieee80211_regdomain *regd = NULL; + + if (reg == NULL) + return 0; + + if (!(reg->flags & ATH6KL_REG_FALGS_INTERNAL_REGDB)) + return 0; + + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + ath6kl_dbg(ATH6KL_DBG_REGDB, + "reg notify by %s %c%c\n", + initiatorString[request->initiator], + request->alpha2[0], + request->alpha2[1]); + + /* HACK: always use ours */ + regd = ath6kl_reg_update(reg, reg->current_reg_code, false); + if (regd != reg->current_regd) { + ath6kl_err("reg notifier fail, %x %p %p\n", + reg->current_reg_code, + reg->current_regd, + regd); + WARN_ON(1); + } + break; + } + + return 0; +} + +int ath6kl_reg_target_notify(struct ath6kl *ar, u32 reg_code) +{ + struct reg_info *reg = ar->reg_ctx; + struct ieee80211_regdomain *regd = NULL; + + BUG_ON(!reg); + + if (reg->flags & ATH6KL_REG_FALGS_INTERNAL_REGDB) + regd = ath6kl_reg_update(reg, reg_code, true); + else { + u16 code; + const char *iso_name; + char alpha2[2]; + + code = (u16)(reg_code & ATH6KL_REG_CODE_MASK); + if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & + ATH6KL_COUNTRY_ERD_FLAG) + iso_name = _reg_find_iso_name(reg_code, true, false); + else + iso_name = _reg_find_iso_name(reg_code, false, true); + + if (iso_name) { + alpha2[0] = iso_name[0]; + alpha2[1] = iso_name[1]; + + ath6kl_dbg(ATH6KL_DBG_REGDB, + "Country alpha2 being used: %c%c\n", + alpha2[0], alpha2[1]); + + /* Update to cfg80211 & CRDA */ + regulatory_hint(ar->wiphy, alpha2); + } + } + + /* Cache the latest regulatory */ + reg->current_regd = regd; + reg->current_reg_code = reg_code; + + return 0; +} + +bool ath6kl_reg_is_init_done(struct ath6kl *ar) +{ + struct reg_info *reg = ar->reg_ctx; + + if (!((reg->current_reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & + ATH6KL_WORLDWIDE_ROAMING_FLAG)) + return true; + else + return false; +} + +bool ath6kl_reg_is_p2p_channel(struct ath6kl *ar, u32 freq) +{ +#define _REG_CHAN_P2P_NOT_ALLOWED ( \ + IEEE80211_CHAN_RADAR | \ + IEEE80211_CHAN_PASSIVE_SCAN | \ + IEEE80211_CHAN_NO_IBSS) + if (ath6kl_p2p_is_p2p_channel(freq)) { + struct wiphy *wiphy = ar->wiphy; + enum ieee80211_band band = NL80211_BAND_2GHZ; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + int i; + + if (freq > 4000) + band = NL80211_BAND_5GHZ; + + sband = wiphy->bands[band]; + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + if (chan->center_freq == freq) { + if (chan->flags & _REG_CHAN_P2P_NOT_ALLOWED) + return false; + else + return true; + } + } + return false; + } else + return false; +#undef _REG_CHAN_P2P_NOT_ALLOWED +} + +struct reg_info *ath6kl_reg_init(struct ath6kl *ar, + bool intRegdb, + bool p2pInPasvCh) +{ + struct reg_info *reg; + + reg = kzalloc(sizeof(struct reg_info), GFP_KERNEL); + if (!reg) { + ath6kl_err("failed to alloc memory for reg\n"); + return NULL; + } + + reg->ar = ar; + reg->wiphy = ar->wiphy; + if (intRegdb) { + reg->flags |= ATH6KL_REG_FALGS_INTERNAL_REGDB; + if (p2pInPasvCh) + reg->flags |= ATH6KL_REG_FALGS_P2P_IN_PASV_CHAN; + + ath6kl_info("Using driver's regdb%s.\n", + (p2pInPasvCh ? " & p2p-in-passive-chan" : "")); + } + + ath6kl_dbg(ATH6KL_DBG_REGDB, + "reg init flags %x\n", + reg->flags); + + return reg; +} + +void ath6kl_reg_deinit(struct ath6kl *ar) +{ + kfree(ar->reg_ctx); + ar->reg_ctx = NULL; + + ath6kl_dbg(ATH6KL_DBG_REGDB, + "reg deinit"); + + return; +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/reg.h b/drivers/net/wireless/ath/ath6kl-3.5/reg.h new file mode 100644 index 000000000000..570989b62ccf --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/reg.h @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2010-2013 Qualcomm Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 REG_H +#define REG_H + +/* + * NOTE: Please DO NOT modify any code in this header file unless + * you know what you want to do. + */ + +/* + * Numbering from ISO 3166 + */ +enum ath6kl_CountryCode { + COUNTRY_ALBANIA = 8, /* Albania */ + COUNTRY_ALGERIA = 12, /* Algeria */ + COUNTRY_ARGENTINA = 32, /* Argentina */ + COUNTRY_ARMENIA = 51, /* Armenia */ + COUNTRY_ARUBA = 533, /* Aruba */ + COUNTRY_AUSTRALIA = 36, /* Australia (for STA) */ + COUNTRY_AUSTRALIA_AP = 5000, /* Australia (for AP) */ + COUNTRY_AUSTRIA = 40, /* Austria */ + COUNTRY_AZERBAIJAN = 31, /* Azerbaijan */ + COUNTRY_BAHRAIN = 48, /* Bahrain */ + COUNTRY_BANGLADESH = 50, /* Bangladesh */ + COUNTRY_BARBADOS = 52, /* Barbados */ + COUNTRY_BELARUS = 112, /* Belarus */ + COUNTRY_BELGIUM = 56, /* Belgium */ + COUNTRY_BELIZE = 84, /* Belize */ + COUNTRY_BOLIVIA = 68, /* Bolivia */ + COUNTRY_BOSNIA_HERZEGOWANIA = 70, /* Bosnia & Herzegowania */ + COUNTRY_BRAZIL = 76, /* Brazil */ + COUNTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */ + COUNTRY_BULGARIA = 100, /* Bulgaria */ + COUNTRY_CAMBODIA = 116, /* Cambodia */ + COUNTRY_CANADA = 124, /* Canada (for STA) */ + COUNTRY_CANADA_AP = 5001, /* Canada (for AP) */ + COUNTRY_CHILE = 152, /* Chile */ + COUNTRY_CHINA = 156, /* People's Republic of China */ + COUNTRY_COLOMBIA = 170, /* Colombia */ + COUNTRY_COSTA_RICA = 188, /* Costa Rica */ + COUNTRY_CROATIA = 191, /* Croatia */ + COUNTRY_CYPRUS = 196, + COUNTRY_CZECH = 203, /* Czech Republic */ + COUNTRY_DENMARK = 208, /* Denmark */ + COUNTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */ + COUNTRY_ECUADOR = 218, /* Ecuador */ + COUNTRY_EGYPT = 818, /* Egypt */ + COUNTRY_EL_SALVADOR = 222, /* El Salvador */ + COUNTRY_ESTONIA = 233, /* Estonia */ + COUNTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */ + COUNTRY_FINLAND = 246, /* Finland */ + COUNTRY_FRANCE = 250, /* France */ + COUNTRY_FRANCE2 = 255, /* France2 */ + COUNTRY_GEORGIA = 268, /* Georgia */ + COUNTRY_GERMANY = 276, /* Germany */ + COUNTRY_GREECE = 300, /* Greece */ + COUNTRY_GREENLAND = 304, /* Greenland */ + COUNTRY_GRENADA = 308, /* Grenada */ + COUNTRY_GUAM = 316, /* Guam */ + COUNTRY_GUATEMALA = 320, /* Guatemala */ + COUNTRY_HAITI = 332, /* Haiti */ + COUNTRY_HONDURAS = 340, /* Honduras */ + COUNTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */ + COUNTRY_HUNGARY = 348, /* Hungary */ + COUNTRY_ICELAND = 352, /* Iceland */ + COUNTRY_INDIA = 356, /* India */ + COUNTRY_INDONESIA = 360, /* Indonesia */ + COUNTRY_IRAN = 364, /* Iran */ + COUNTRY_IRAQ = 368, /* Iraq */ + COUNTRY_IRELAND = 372, /* Ireland */ + COUNTRY_ISRAEL = 376, /* Israel */ + COUNTRY_ITALY = 380, /* Italy */ + COUNTRY_JAMAICA = 388, /* Jamaica */ + COUNTRY_JAPAN = 392, /* Japan */ + COUNTRY_JAPAN1 = 393, /* Japan (JP1) */ + COUNTRY_JAPAN2 = 394, /* Japan (JP0) */ + COUNTRY_JAPAN3 = 395, /* Japan (JP1-1) */ + COUNTRY_JAPAN4 = 396, /* Japan (JE1) */ + COUNTRY_JAPAN5 = 397, /* Japan (JE2) */ + COUNTRY_JAPAN6 = 399, /* Japan (JP6) */ + COUNTRY_JORDAN = 400, /* Jordan */ + COUNTRY_KAZAKHSTAN = 398, /* Kazakhstan */ + COUNTRY_KENYA = 404, /* Kenya */ + COUNTRY_KOREA_NORTH = 408, /* North Korea */ + COUNTRY_KOREA_ROC = 410, /* South Korea (for STA) */ + COUNTRY_KOREA_ROC2 = 411, /* South Korea */ + COUNTRY_KOREA_ROC3 = 412, /* South Korea (for AP) */ + COUNTRY_KUWAIT = 414, /* Kuwait */ + COUNTRY_LATVIA = 428, /* Latvia */ + COUNTRY_LEBANON = 422, /* Lebanon */ + COUNTRY_LIBYA = 434, /* Libya */ + COUNTRY_LIECHTENSTEIN = 438, /* Liechtenstein */ + COUNTRY_LITHUANIA = 440, /* Lithuania */ + COUNTRY_LUXEMBOURG = 442, /* Luxembourg */ + COUNTRY_MACAU = 446, /* Macau */ + COUNTRY_MACEDONIA = 807, /* Yugoslav Republic of Macedonia */ + COUNTRY_MALAYSIA = 458, /* Malaysia */ + COUNTRY_MALTA = 470, /* Malta */ + COUNTRY_MEXICO = 484, /* Mexico */ + COUNTRY_MONACO = 492, /* Principality of Monaco */ + COUNTRY_MOROCCO = 504, /* Morocco */ + COUNTRY_NEPAL = 524, /* Nepal */ + COUNTRY_NETHERLANDS = 528, /* Netherlands */ + COUNTRY_NETHERLAND_ANTILLES = 530, /* Netherlands-Antilles */ + COUNTRY_NEW_ZEALAND = 554, /* New Zealand */ + COUNTRY_NICARAGUA = 558, /* Nicaragua */ + COUNTRY_NORWAY = 578, /* Norway */ + COUNTRY_OMAN = 512, /* Oman */ + COUNTRY_PAKISTAN = 586, /* Islamic Republic of Pakistan */ + COUNTRY_PANAMA = 591, /* Panama */ + COUNTRY_PARAGUAY = 600, /* Paraguay */ + COUNTRY_PERU = 604, /* Peru */ + COUNTRY_PHILIPPINES = 608, /* Republic of the Philippines */ + COUNTRY_POLAND = 616, /* Poland */ + COUNTRY_PORTUGAL = 620, /* Portugal */ + COUNTRY_PUERTO_RICO = 630, /* Puerto Rico */ + COUNTRY_QATAR = 634, /* Qatar */ + COUNTRY_ROMANIA = 642, /* Romania */ + COUNTRY_RUSSIA = 643, /* Russia */ + COUNTRY_RWANDA = 646, /* Rwanda */ + COUNTRY_SAUDI_ARABIA = 682, /* Saudi Arabia */ + COUNTRY_SERBIA = 688, /* Serbia */ + COUNTRY_MONTENEGRO = 499, /* Montenegro */ + COUNTRY_SINGAPORE = 702, /* Singapore */ + COUNTRY_SLOVAKIA = 703, /* Slovak Republic */ + COUNTRY_SLOVENIA = 705, /* Slovenia */ + COUNTRY_SOUTH_AFRICA = 710, /* South Africa */ + COUNTRY_SPAIN = 724, /* Spain */ + COUNTRY_SRILANKA = 144, /* Sri Lanka */ + COUNTRY_SWEDEN = 752, /* Sweden */ + COUNTRY_SWITZERLAND = 756, /* Switzerland */ + COUNTRY_SYRIA = 760, /* Syria */ + COUNTRY_TAIWAN = 158, /* Taiwan */ + COUNTRY_THAILAND = 764, /* Thailand */ + COUNTRY_TRINIDAD_Y_TOBAGO = 780, /* Trinidad y Tobago */ + COUNTRY_TUNISIA = 788, /* Tunisia */ + COUNTRY_TURKEY = 792, /* Turkey */ + COUNTRY_UAE = 784, /* U.A.E. */ + COUNTRY_UGANDA = 800, /* Uganda */ + COUNTRY_UKRAINE = 804, /* Ukraine */ + COUNTRY_UNITED_KINGDOM = 826, /* United Kingdom */ + COUNTRY_UNITED_STATES = 840, /* United States (for STA) */ + COUNTRY_UNITED_STATES_AP = 841, /* United States (for AP) */ + COUNTRY_UNITED_STATES_PS = 842, /* United States - public safety */ + COUNTRY_URUGUAY = 858, /* Uruguay */ + COUNTRY_UZBEKISTAN = 860, /* Uzbekistan */ + COUNTRY_VENEZUELA = 862, /* Venezuela */ + COUNTRY_VIET_NAM = 704, /* Viet Nam */ + COUNTRY_YEMEN = 887, /* Yemen */ + COUNTRY_ZIMBABWE = 716 /* Zimbabwe */ +}; + +enum ath6kl_RegionCode { + REGION_NO_ENUMRD = 0x00, + REGION_NULL1_WORLD = 0x03, /* For 11b-only countries + (no 11a allowed) */ + REGION_NULL1_ETSIB = 0x07, /* Israel */ + REGION_NULL1_ETSIC = 0x08, + + REGION_FCC1_FCCA = 0x10, /* USA */ + REGION_FCC1_WORLD = 0x11, /* Hong Kong */ + REGION_FCC2_FCCA = 0x20, /* Canada */ + REGION_FCC2_WORLD = 0x21, /* Australia & HK */ + REGION_FCC2_ETSIC = 0x22, + REGION_FCC3_FCCA = 0x3A, /* USA & Canada w/5470 band, 11h, + DFS enabled */ + REGION_FCC3_WORLD = 0x3B, /* USA & Canada w/5470 band, 11h, + DFS enabled */ + REGION_FCC3_ETSIC = 0x3F, + REGION_FCC4_FCCA = 0x12, /* FCC public safety plus UNII bands */ + REGION_FCC5_FCCA = 0x13, /* US with no DFS */ + REGION_FCC5_WORLD = 0x16, /* US with no DFS */ + REGION_FCC6_FCCA = 0x14, /* Same as FCC2_FCCA but with + 5600-5650MHz channels disabled for + US & Canada APs */ + REGION_FCC6_WORLD = 0x23, /* Same as FCC2_FCCA but with + 5600-5650MHz channels disabled for + Australia APs */ + + REGION_ETSI1_WORLD = 0x37, + + REGION_ETSI2_WORLD = 0x35, /* Hungary & others */ + REGION_ETSI3_WORLD = 0x36, /* France & others */ + REGION_ETSI4_WORLD = 0x30, + REGION_ETSI4_ETSIC = 0x38, + REGION_ETSI5_WORLD = 0x39, + REGION_ETSI6_WORLD = 0x34, /* Bulgaria */ + REGION_ETSI8_WORLD = 0x3D, /* Russia */ + REGION_ETSI9_WORLD = 0x3E, /* Ukraine */ + REGION_ETSI_RESERVED = 0x33, /* Reserved (Do not used) */ + REGION_FRANCE_RES = 0x31, /* Legacy France for OEM */ + + REGION_APL6_WORLD = 0x5B, /* Singapore */ + REGION_APL4_WORLD = 0x42, /* Singapore */ + REGION_APL3_FCCA = 0x50, + REGION_APL_RESERVED = 0x44, /* Reserved (Do not used) */ + REGION_APL2_WORLD = 0x45, /* Korea */ + REGION_APL2_APLC = 0x46, + REGION_APL3_WORLD = 0x47, + REGION_APL2_APLD = 0x49, /* Korea with 2.3G channels */ + REGION_APL2_FCCA = 0x4D, /* Specific Mobile Customer */ + REGION_APL1_WORLD = 0x52, /* Latin America */ + REGION_APL1_FCCA = 0x53, + REGION_APL1_ETSIC = 0x55, + REGION_APL2_ETSIC = 0x56, /* Venezuela */ + REGION_APL5_WORLD = 0x58, /* Chile */ + REGION_APL7_FCCA = 0x5C, + REGION_APL8_WORLD = 0x5D, + REGION_APL9_WORLD = 0x5E, + REGION_APL10_WORLD = 0x5F, /* Korea 5GHz for STA */ + + + REGION_MKK5_MKKA = 0x99, /* This is a temporary value. + MG and DQ have to give official one */ + REGION_MKK5_FCCA = 0x9A, /* This is a temporary value. + MG and DQ have to give official one */ + REGION_MKK5_MKKC = 0x88, + REGION_MKK11_MKKA = 0xD4, + REGION_MKK11_FCCA = 0xD5, + REGION_MKK11_MKKC = 0xD7, + + /* + * World mode SKUs + */ + REGION_WOR0_WORLD = 0x60, /* World0 (WO0 SKU) */ + REGION_WOR1_WORLD = 0x61, /* World1 (WO1 SKU) */ + REGION_WOR2_WORLD = 0x62, /* World2 (WO2 SKU) */ + REGION_WOR3_WORLD = 0x63, /* World3 (WO3 SKU) */ + REGION_WOR4_WORLD = 0x64, /* World4 (WO4 SKU) */ + REGION_WOR5_ETSIC = 0x65, /* World5 (WO5 SKU) */ + + REGION_WOR01_WORLD = 0x66, /* World0-1 (WW0-1 SKU) */ + REGION_WOR02_WORLD = 0x67, /* World0-2 (WW0-2 SKU) */ + REGION_EU1_WORLD = 0x68, /* Same as World0-2 (WW0-2 SKU), + except active scan ch1-13. No ch14 */ + + REGION_WOR9_WORLD = 0x69, /* World9 (WO9 SKU) */ + REGION_WORA_WORLD = 0x6A, /* WorldA (WOA SKU) */ + REGION_WORB_WORLD = 0x6B, /* WorldB (WOA SKU) */ + REGION_WORC_WORLD = 0x6C, /* WorldC (WOA SKU) */ + + /* + * Regulator domains ending in a number (e.g. APL1, + * MK1, ETSI4, etc) apply to 5GHz channel and power + * information. Regulator domains ending in a letter + * (e.g. APLA, FCCA, etc) apply to 2.4GHz channel and + * power information. + */ + REGION_APL1 = 0x0150, /* LAT & Asia */ + REGION_APL2 = 0x0250, /* LAT & Asia */ + REGION_APL3 = 0x0350, /* Taiwan */ + REGION_APL4 = 0x0450, /* Jordan */ + REGION_APL5 = 0x0550, /* Chile */ + REGION_APL6 = 0x0650, /* Singapore */ + REGION_APL7 = 0x0750, /* Taiwan */ + REGION_APL8 = 0x0850, /* Malaysia */ + REGION_APL9 = 0x0950, /* Korea */ + REGION_APL10 = 0x1050, /* Korea 5GHz */ + + REGION_ETSI1 = 0x0130, /* Europe & others */ + REGION_ETSI2 = 0x0230, /* Europe & others */ + REGION_ETSI3 = 0x0330, /* Europe & others */ + REGION_ETSI4 = 0x0430, /* Europe & others */ + REGION_ETSI5 = 0x0530, /* Europe & others */ + REGION_ETSI6 = 0x0630, /* Europe & others */ + REGION_ETSI8 = 0x0830, /* Russia - only by APs */ + REGION_ETSI9 = 0x0930, /* Ukraine - only by APs */ + REGION_ETSIB = 0x0B30, /* Israel */ + REGION_ETSIC = 0x0C30, /* Latin America */ + + REGION_FCC1 = 0x0110, /* US & others */ + REGION_FCC2 = 0x0120, /* Canada, Australia & New Zealand */ + REGION_FCC3 = 0x0160, /* US w/new middle band & DFS */ + REGION_FCC4 = 0x0165, + REGION_FCC5 = 0x0180, + REGION_FCC6 = 0x0610, + REGION_FCCA = 0x0A10, + + REGION_APLD = 0x0D50, /* South Korea */ + + REGION_MKK1 = 0x0140, /* Japan */ + REGION_MKK2 = 0x0240, /* Japan Extended */ + REGION_MKK3 = 0x0340, /* Japan new 5GHz */ + REGION_MKK4 = 0x0440, /* Japan new 5GHz */ + REGION_MKK5 = 0x0540, /* Japan new 5GHz */ + REGION_MKK6 = 0x0640, /* Japan new 5GHz */ + REGION_MKK7 = 0x0740, /* Japan new 5GHz */ + REGION_MKK8 = 0x0840, /* Japan new 5GHz */ + REGION_MKK9 = 0x0940, /* Japan new 5GHz */ + REGION_MKK10 = 0x1040, /* Japan new 5GHz */ + REGION_MKK11 = 0x1140, /* Japan new 5GHz */ + REGION_MKK12 = 0x1240, /* Japan new 5GHz */ + + REGION_MKKA = 0x0A40, /* Japan */ + REGION_MKKC = 0x0A50, + + REGION_NULL1 = 0x0198, + REGION_WORLD = 0x0199, + REGION_DEBUG_REG_DMN = 0x01ff, + REGION_UNINIT_REG_DMN = 0x0fff, +}; + +#define ATH6KL_COUNTRY_ERD_FLAG 0x8000 +#define ATH6KL_WORLDWIDE_ROAMING_FLAG 0x4000 + +#define ATH6KL_REG_CODE_MASK (0xffff) + +#define NULL_REG_CODE (0xFFFF) + +struct reg_code_to_isoname { + u16 reg_code; + char *iso_name; + + /* + * Map to CRDA's alpha2 and map to "00" for all super domains. + * + * "00" in cfg80211.ko or CRDA is world domain and the rules are + * CH1 - CH13 @ HT40 + * CH12 - CH13 @ HT20, PASSIVE-SCAN | NO-IBSS + * CH14 - CH14 @ HT20, PASSIVE-SCAN | NO-IBSS | NO-OFDM + * CH36 - CH48 @ HT40, PASSIVE-SCAN | NO-IBSS + * CH149 - CH165 @ HT40, PASSIVE-SCAN | NO-IBSS + * + * This domain meet most ath6kl super domains except + * 1.no DFS channels support (5260 MHz - 5700 MHz) + * 2.all CH36 - CH48 of ath6kl treat as DFS + */ + char *reg_to_ctry_iso_name; +}; + +struct reg_info { +#define ATH6KL_REG_FALGS_INTERNAL_REGDB (1 << 0) +#define ATH6KL_REG_FALGS_P2P_IN_PASV_CHAN (1 << 1) + u32 flags; + struct ath6kl *ar; + struct wiphy *wiphy; + struct ieee80211_regdomain *current_regd; + u32 current_reg_code; +}; + +extern const struct ieee80211_regdomain *ath6kl_reg_regdb_country[]; +extern const struct ieee80211_regdomain *ath6kl_reg_regdb_region[]; + +int ath6kl_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request); +int ath6kl_reg_target_notify(struct ath6kl *ar, u32 reg_code); +bool ath6kl_reg_is_init_done(struct ath6kl *ar); +bool ath6kl_reg_is_p2p_channel(struct ath6kl *ar, u32 freq); +struct reg_info *ath6kl_reg_init(struct ath6kl *ar, + bool intRegdb, + bool p2pInPasvCh); +void ath6kl_reg_deinit(struct ath6kl *ar); +#endif /* REG_H */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/regdb.c b/drivers/net/wireless/ath/ath6kl-3.5/regdb.c new file mode 100644 index 000000000000..fa2292032b54 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/regdb.c @@ -0,0 +1,3430 @@ +/* + * Copyright (c) 2004-2013 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 + +/* + * Another solution is to use CONFIG_CFG80211_INTERNAL_REGDB to merge ath6kl's + * regdb into cfg80211.ko. + * + * Please copy this file to cfg80211 module path and turn on + * CONFIG_ATH6KL_REGDB_AS_CFG80211_REGDB then rebuilt cfg80211.ko. + */ +#ifdef CONFIG_ATH6KL_REGDB_AS_CFG80211_REGDB +#include "regdb.h" +#endif + +/* + * Regulatory data: Not exactly sync to the target's because the host + * just need to know the channel-list, bandwidth, DFS, + * IBSS and Passive. Others (like powers, rates...) are + * handled by the target. + * + * WARN: Generated from program and please not to modify it. + */ +static const struct ieee80211_regdomain ath6kl_regd_AL = { + .n_reg_rules = 4, + .alpha2 = "AL", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_DZ = { + .n_reg_rules = 4, + .alpha2 = "DZ", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_AR = { + .n_reg_rules = 5, + .alpha2 = "AR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_AM = { + .n_reg_rules = 3, + .alpha2 = "AM", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 18, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_AW = { + .n_reg_rules = 4, + .alpha2 = "AW", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_AU = { + .n_reg_rules = 5, + .alpha2 = "AU", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_A2 = { + .n_reg_rules = 6, + .alpha2 = "A2", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5580 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_AT = { + .n_reg_rules = 4, + .alpha2 = "AT", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_AZ = { + .n_reg_rules = 3, + .alpha2 = "AZ", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 18, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BH = { + .n_reg_rules = 4, + .alpha2 = "BH", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BD = { + .n_reg_rules = 1, + .alpha2 = "BD", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BB = { + .n_reg_rules = 4, + .alpha2 = "BB", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BY = { + .n_reg_rules = 4, + .alpha2 = "BY", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BE = { + .n_reg_rules = 4, + .alpha2 = "BE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BZ = { + .n_reg_rules = 2, + .alpha2 = "BZ", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 30, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BO = { + .n_reg_rules = 2, + .alpha2 = "BO", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 30, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BA = { + .n_reg_rules = 4, + .alpha2 = "BA", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BR = { + .n_reg_rules = 5, + .alpha2 = "BR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BN = { + .n_reg_rules = 4, + .alpha2 = "BN", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_BG = { + .n_reg_rules = 4, + .alpha2 = "BG", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_KH = { + .n_reg_rules = 4, + .alpha2 = "KH", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_CA = { + .n_reg_rules = 5, + .alpha2 = "CA", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_C2 = { + .n_reg_rules = 6, + .alpha2 = "C2", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5580 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_CL = { + .n_reg_rules = 4, + .alpha2 = "CL", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_CN = { + .n_reg_rules = 2, + .alpha2 = "CN", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_CO = { + .n_reg_rules = 5, + .alpha2 = "CO", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_CR = { + .n_reg_rules = 4, + .alpha2 = "CR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_HR = { + .n_reg_rules = 4, + .alpha2 = "HR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_CY = { + .n_reg_rules = 3, + .alpha2 = "CY", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_CZ = { + .n_reg_rules = 4, + .alpha2 = "CZ", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_DK = { + .n_reg_rules = 4, + .alpha2 = "DK", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_DO = { + .n_reg_rules = 4, + .alpha2 = "DO", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_EC = { + .n_reg_rules = 4, + .alpha2 = "EC", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_EG = { + .n_reg_rules = 3, + .alpha2 = "EG", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_SV = { + .n_reg_rules = 4, + .alpha2 = "SV", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_EE = { + .n_reg_rules = 4, + .alpha2 = "EE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_FI = { + .n_reg_rules = 4, + .alpha2 = "FI", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_FR = { + .n_reg_rules = 4, + .alpha2 = "FR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_F2 = { + .n_reg_rules = 3, + .alpha2 = "F2", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_GE = { + .n_reg_rules = 3, + .alpha2 = "GE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 18, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_DE = { + .n_reg_rules = 4, + .alpha2 = "DE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_GR = { + .n_reg_rules = 4, + .alpha2 = "GR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_GL = { + .n_reg_rules = 4, + .alpha2 = "GL", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_GD = { + .n_reg_rules = 5, + .alpha2 = "GD", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_GU = { + .n_reg_rules = 4, + .alpha2 = "GU", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_GT = { + .n_reg_rules = 4, + .alpha2 = "GT", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_HT = { + .n_reg_rules = 4, + .alpha2 = "HT", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_HN = { + .n_reg_rules = 5, + .alpha2 = "HN", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_HK = { + .n_reg_rules = 5, + .alpha2 = "HK", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_HU = { + .n_reg_rules = 4, + .alpha2 = "HU", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_IS = { + .n_reg_rules = 4, + .alpha2 = "IS", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_IN = { + .n_reg_rules = 4, + .alpha2 = "IN", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_ID = { + .n_reg_rules = 2, + .alpha2 = "ID", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5805 + 10, 20, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_IR = { + .n_reg_rules = 2, + .alpha2 = "IR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_IE = { + .n_reg_rules = 4, + .alpha2 = "IE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_IL = { + .n_reg_rules = 3, + .alpha2 = "IL", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_IT = { + .n_reg_rules = 4, + .alpha2 = "IT", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_JM = { + .n_reg_rules = 5, + .alpha2 = "JM", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_JP = { + .n_reg_rules = 6, + .alpha2 = "JP", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 20, 0), + REG_RULE(2467 - 10, 2472 + 10, 20, 3, 20, 0), + REG_RULE(2484 - 10, 2484 + 10, 20, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_JO = { + .n_reg_rules = 3, + .alpha2 = "JO", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_KZ = { + .n_reg_rules = 1, + .alpha2 = "KZ", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_KE = { + .n_reg_rules = 4, + .alpha2 = "KE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5500 - 10, 5560 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5765 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_KP = { + .n_reg_rules = 5, + .alpha2 = "KP", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 20, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5620 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5805 + 10, 20, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_KR = { + .n_reg_rules = 5, + .alpha2 = "KR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_K2 = { + .n_reg_rules = 3, + .alpha2 = "K2", + .reg_rules = { + REG_RULE(2312 - 10, 2372 + 10, 20, 3, 20, 0), + REG_RULE(2412 - 10, 2472 + 10, 20, 3, 20, 0), + REG_RULE(5745 - 10, 5805 + 10, 20, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_K3 = { + .n_reg_rules = 5, + .alpha2 = "K3", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5620 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_KW = { + .n_reg_rules = 3, + .alpha2 = "KW", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_LV = { + .n_reg_rules = 4, + .alpha2 = "LV", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_LB = { + .n_reg_rules = 5, + .alpha2 = "LB", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_LI = { + .n_reg_rules = 4, + .alpha2 = "LI", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_LT = { + .n_reg_rules = 4, + .alpha2 = "LT", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_LU = { + .n_reg_rules = 4, + .alpha2 = "LU", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_MO = { + .n_reg_rules = 2, + .alpha2 = "MO", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_MK = { + .n_reg_rules = 4, + .alpha2 = "MK", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_MW = { + .n_reg_rules = 4, + .alpha2 = "MW", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_MY = { + .n_reg_rules = 4, + .alpha2 = "MY", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_MT = { + .n_reg_rules = 4, + .alpha2 = "MT", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_MX = { + .n_reg_rules = 4, + .alpha2 = "MX", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_MC = { + .n_reg_rules = 3, + .alpha2 = "MC", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 18, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_MA = { + .n_reg_rules = 3, + .alpha2 = "MA", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_NP = { + .n_reg_rules = 4, + .alpha2 = "NP", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_NZ = { + .n_reg_rules = 5, + .alpha2 = "NZ", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_NL = { + .n_reg_rules = 4, + .alpha2 = "NL", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_AN = { + .n_reg_rules = 4, + .alpha2 = "AN", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_NO = { + .n_reg_rules = 4, + .alpha2 = "NO", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_OM = { + .n_reg_rules = 5, + .alpha2 = "OM", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_PK = { + .n_reg_rules = 2, + .alpha2 = "PK", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_PA = { + .n_reg_rules = 4, + .alpha2 = "PA", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_PE = { + .n_reg_rules = 5, + .alpha2 = "PE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_PH = { + .n_reg_rules = 5, + .alpha2 = "PH", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_PL = { + .n_reg_rules = 4, + .alpha2 = "PL", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_PT = { + .n_reg_rules = 4, + .alpha2 = "PT", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_PR = { + .n_reg_rules = 5, + .alpha2 = "PR", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_QA = { + .n_reg_rules = 2, + .alpha2 = "QA", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_RO = { + .n_reg_rules = 4, + .alpha2 = "RO", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_RU = { + .n_reg_rules = 5, + .alpha2 = "RU", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_RW = { + .n_reg_rules = 5, + .alpha2 = "RW", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_SA = { + .n_reg_rules = 5, + .alpha2 = "SA", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_ME = { + .n_reg_rules = 4, + .alpha2 = "ME", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_RS = { + .n_reg_rules = 4, + .alpha2 = "RS", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_SG = { + .n_reg_rules = 5, + .alpha2 = "SG", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_SK = { + .n_reg_rules = 4, + .alpha2 = "SK", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_SI = { + .n_reg_rules = 4, + .alpha2 = "SI", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_ZA = { + .n_reg_rules = 5, + .alpha2 = "ZA", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_ES = { + .n_reg_rules = 4, + .alpha2 = "ES", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_LK = { + .n_reg_rules = 5, + .alpha2 = "LK", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 20, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_SE = { + .n_reg_rules = 4, + .alpha2 = "SE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_CH = { + .n_reg_rules = 4, + .alpha2 = "CH", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_SY = { + .n_reg_rules = 1, + .alpha2 = "SY", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_TW = { + .n_reg_rules = 5, + .alpha2 = "TW", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5280 - 10, 5320 + 10, 40, 3, 17, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5580 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_TH = { + .n_reg_rules = 5, + .alpha2 = "TH", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_TT = { + .n_reg_rules = 5, + .alpha2 = "TT", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_TN = { + .n_reg_rules = 3, + .alpha2 = "TN", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_TR = { + .n_reg_rules = 4, + .alpha2 = "TR", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_UG = { + .n_reg_rules = 5, + .alpha2 = "UG", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_UA = { + .n_reg_rules = 5, + .alpha2 = "UA", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5660 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_AE = { + .n_reg_rules = 5, + .alpha2 = "AE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_GB = { + .n_reg_rules = 4, + .alpha2 = "GB", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_US = { + .n_reg_rules = 5, + .alpha2 = "US", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_U2 = { + .n_reg_rules = 6, + .alpha2 = "U2", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5580 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_PS = { + .n_reg_rules = 4, + .alpha2 = "PS", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 23, 0 + | NL80211_RRF_DFS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_UY = { + .n_reg_rules = 5, + .alpha2 = "UY", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_UZ = { + .n_reg_rules = 5, + .alpha2 = "UZ", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_VE = { + .n_reg_rules = 4, + .alpha2 = "VE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_VN = { + .n_reg_rules = 5, + .alpha2 = "VN", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_YE = { + .n_reg_rules = 1, + .alpha2 = "YE", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_ZW = { + .n_reg_rules = 4, + .alpha2 = "ZW", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_00 = { + .n_reg_rules = 6, + .alpha2 = "00", + .reg_rules = { + REG_RULE(2312 - 10, 2372 + 10, 20, 3, 5, 0), + REG_RULE(2412 - 10, 2472 + 10, 20, 3, 5, 0), + REG_RULE(2484 - 10, 2484 + 10, 20, 3, 5, 0), + REG_RULE(5120 - 10, 5240 + 10, 20, 3, 5, 0), + REG_RULE(5260 - 10, 5700 + 10, 20, 3, 5, 0 + | NL80211_RRF_DFS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 5, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_03 = { + .n_reg_rules = 1, + .alpha2 = "03", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_07 = { + .n_reg_rules = 1, + .alpha2 = "07", + .reg_rules = { + REG_RULE(2432 - 10, 2442 + 10, 20, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_08 = { + .n_reg_rules = 1, + .alpha2 = "08", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_20 = { + .n_reg_rules = 4, + .alpha2 = "20", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_21 = { + .n_reg_rules = 4, + .alpha2 = "21", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_22 = { + .n_reg_rules = 4, + .alpha2 = "22", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_3a = { + .n_reg_rules = 5, + .alpha2 = "3a", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_3b = { + .n_reg_rules = 5, + .alpha2 = "3b", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_3f = { + .n_reg_rules = 5, + .alpha2 = "3f", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5720 + 10, 40, 3, 24, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_12 = { + .n_reg_rules = 4, + .alpha2 = "12", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 23, 0 + | NL80211_RRF_DFS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_13 = { + .n_reg_rules = 2, + .alpha2 = "13", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_16 = { + .n_reg_rules = 2, + .alpha2 = "16", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_14 = { + .n_reg_rules = 6, + .alpha2 = "14", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5580 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_23 = { + .n_reg_rules = 6, + .alpha2 = "23", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5580 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_37 = { + .n_reg_rules = 4, + .alpha2 = "37", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 27, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_35 = { + .n_reg_rules = 2, + .alpha2 = "35", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 18, 0 + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_36 = { + .n_reg_rules = 3, + .alpha2 = "36", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_30 = { + .n_reg_rules = 3, + .alpha2 = "30", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 18, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_39 = { + .n_reg_rules = 2, + .alpha2 = "39", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 15, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_34 = { + .n_reg_rules = 4, + .alpha2 = "34", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5280 + 10, 20, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_3d = { + .n_reg_rules = 5, + .alpha2 = "3d", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_3e = { + .n_reg_rules = 5, + .alpha2 = "3e", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5660 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_31 = { + .n_reg_rules = 3, + .alpha2 = "31", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0 + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_11 = { + .n_reg_rules = 4, + .alpha2 = "11", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_10 = { + .n_reg_rules = 4, + .alpha2 = "10", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 17, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 23, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_52 = { + .n_reg_rules = 2, + .alpha2 = "52", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_45 = { + .n_reg_rules = 2, + .alpha2 = "45", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_4d = { + .n_reg_rules = 2, + .alpha2 = "4d", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_47 = { + .n_reg_rules = 3, + .alpha2 = "47", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5280 - 10, 5320 + 10, 40, 3, 17, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 17, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_50 = { + .n_reg_rules = 3, + .alpha2 = "50", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5280 - 10, 5320 + 10, 40, 3, 17, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 17, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_42 = { + .n_reg_rules = 3, + .alpha2 = "42", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_58 = { + .n_reg_rules = 2, + .alpha2 = "58", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 17, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_5b = { + .n_reg_rules = 4, + .alpha2 = "5b", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 20, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_5c = { + .n_reg_rules = 5, + .alpha2 = "5c", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5280 - 10, 5320 + 10, 40, 3, 17, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5580 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5660 - 10, 5700 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_5d = { + .n_reg_rules = 3, + .alpha2 = "5d", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_5e = { + .n_reg_rules = 5, + .alpha2 = "5e", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5620 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_5f = { + .n_reg_rules = 5, + .alpha2 = "5f", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_51 = { + .n_reg_rules = 4, + .alpha2 = "51", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 23, 0), + REG_RULE(5500 - 10, 5560 + 10, 40, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5765 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_55 = { + .n_reg_rules = 2, + .alpha2 = "55", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 30, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 3, 30, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_56 = { + .n_reg_rules = 2, + .alpha2 = "56", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 30, 0), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_49 = { + .n_reg_rules = 3, + .alpha2 = "49", + .reg_rules = { + REG_RULE(2312 - 10, 2372 + 10, 20, 3, 20, 0), + REG_RULE(2412 - 10, 2472 + 10, 20, 3, 20, 0), + REG_RULE(5745 - 10, 5805 + 10, 40, 3, 23, 0), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_99 = { + .n_reg_rules = 6, + .alpha2 = "99", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 20, 0), + REG_RULE(2467 - 10, 2472 + 10, 20, 3, 20, 0), + REG_RULE(2484 - 10, 2484 + 10, 20, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_9a = { + .n_reg_rules = 4, + .alpha2 = "9a", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_88 = { + .n_reg_rules = 4, + .alpha2 = "88", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_d4 = { + .n_reg_rules = 8, + .alpha2 = "d4", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 20, 0), + REG_RULE(2467 - 10, 2472 + 10, 20, 3, 20, 0), + REG_RULE(2484 - 10, 2484 + 10, 20, 3, 20, 0), + REG_RULE(4920 - 10, 4980 + 10, 20, 3, 23, 0), + REG_RULE(5040 - 10, 5080 + 10, 20, 3, 23, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_d5 = { + .n_reg_rules = 6, + .alpha2 = "d5", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 30, 0), + REG_RULE(4920 - 10, 4980 + 10, 20, 3, 23, 0), + REG_RULE(5040 - 10, 5080 + 10, 20, 3, 23, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_d7 = { + .n_reg_rules = 6, + .alpha2 = "d7", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 3, 20, 0), + REG_RULE(4920 - 10, 4980 + 10, 20, 3, 23, 0), + REG_RULE(5040 - 10, 5080 + 10, 20, 3, 23, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_87 = { + .n_reg_rules = 6, + .alpha2 = "87", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 3, 20, 0), + REG_RULE(2467 - 10, 2472 + 10, 20, 3, 20, 0), + REG_RULE(2484 - 10, 2484 + 10, 20, 3, 20, 0), + REG_RULE(5180 - 10, 5240 + 10, 40, 3, 20, 0), + REG_RULE(5260 - 10, 5320 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 40, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_60 = { + .n_reg_rules = 13, + .alpha2 = "60", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2484 - 10, 2484 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5170 - 10, 5230 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_61 = { + .n_reg_rules = 13, + .alpha2 = "61", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2484 - 10, 2484 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5170 - 10, 5230 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_62 = { + .n_reg_rules = 13, + .alpha2 = "62", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0), + REG_RULE(2484 - 10, 2484 + 10, 20, 3, 18, 0), + REG_RULE(5170 - 10, 5230 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_63 = { + .n_reg_rules = 11, + .alpha2 = "63", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0), + REG_RULE(5170 - 10, 5230 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_64 = { + .n_reg_rules = 8, + .alpha2 = "64", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_65 = { + .n_reg_rules = 10, + .alpha2 = "65", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_66 = { + .n_reg_rules = 10, + .alpha2 = "66", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(5170 - 10, 5230 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_67 = { + .n_reg_rules = 12, + .alpha2 = "67", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5170 - 10, 5230 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_68 = { + .n_reg_rules = 12, + .alpha2 = "68", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0), + REG_RULE(5170 - 10, 5230 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_69 = { + .n_reg_rules = 9, + .alpha2 = "69", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_6a = { + .n_reg_rules = 11, + .alpha2 = "6a", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_6b = { + .n_reg_rules = 10, + .alpha2 = "6b", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +static const struct ieee80211_regdomain ath6kl_regd_6c = { + .n_reg_rules = 11, + .alpha2 = "6c", + .reg_rules = { + REG_RULE(2412 - 10, 2412 + 10, 20, 3, 18, 0), + REG_RULE(2417 - 10, 2432 + 10, 20, 3, 18, 0), + REG_RULE(2437 - 10, 2442 + 10, 20, 3, 18, 0), + REG_RULE(2447 - 10, 2457 + 10, 20, 3, 18, 0), + REG_RULE(2462 - 10, 2462 + 10, 20, 3, 18, 0), + REG_RULE(2467 - 10, 2467 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(2472 - 10, 2472 + 10, 20, 3, 18, 0 + | NL80211_RRF_PASSIVE_SCAN), + REG_RULE(5180 - 10, 5240 + 10, 20, 3, 30, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5260 - 10, 5320 + 10, 20, 3, 18, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5745 - 10, 5825 + 10, 20, 3, 20, 0 + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + REG_RULE(5500 - 10, 5700 + 10, 20, 3, 20, 0 + | NL80211_RRF_DFS + | NL80211_RRF_PASSIVE_SCAN + | NL80211_RRF_NO_IBSS), + } +}; + +#ifndef CONFIG_ATH6KL_REGDB_AS_CFG80211_REGDB +const struct ieee80211_regdomain *ath6kl_reg_regdb_country[] = { + &ath6kl_regd_AL, + &ath6kl_regd_DZ, + &ath6kl_regd_AR, + &ath6kl_regd_AM, + &ath6kl_regd_AW, + &ath6kl_regd_AU, + &ath6kl_regd_A2, + &ath6kl_regd_AT, + &ath6kl_regd_AZ, + &ath6kl_regd_BH, + &ath6kl_regd_BD, + &ath6kl_regd_BB, + &ath6kl_regd_BY, + &ath6kl_regd_BE, + &ath6kl_regd_BZ, + &ath6kl_regd_BO, + &ath6kl_regd_BA, + &ath6kl_regd_BR, + &ath6kl_regd_BN, + &ath6kl_regd_BG, + &ath6kl_regd_KH, + &ath6kl_regd_CA, + &ath6kl_regd_C2, + &ath6kl_regd_CL, + &ath6kl_regd_CN, + &ath6kl_regd_CO, + &ath6kl_regd_CR, + &ath6kl_regd_HR, + &ath6kl_regd_CY, + &ath6kl_regd_CZ, + &ath6kl_regd_DK, + &ath6kl_regd_DO, + &ath6kl_regd_EC, + &ath6kl_regd_EG, + &ath6kl_regd_SV, + &ath6kl_regd_EE, + &ath6kl_regd_FI, + &ath6kl_regd_FR, + &ath6kl_regd_F2, + &ath6kl_regd_GE, + &ath6kl_regd_DE, + &ath6kl_regd_GR, + &ath6kl_regd_GL, + &ath6kl_regd_GD, + &ath6kl_regd_GU, + &ath6kl_regd_GT, + &ath6kl_regd_HT, + &ath6kl_regd_HN, + &ath6kl_regd_HK, + &ath6kl_regd_HU, + &ath6kl_regd_IS, + &ath6kl_regd_IN, + &ath6kl_regd_ID, + &ath6kl_regd_IR, + &ath6kl_regd_IE, + &ath6kl_regd_IL, + &ath6kl_regd_IT, + &ath6kl_regd_JM, + &ath6kl_regd_JP, + &ath6kl_regd_JO, + &ath6kl_regd_KZ, + &ath6kl_regd_KE, + &ath6kl_regd_KP, + &ath6kl_regd_KR, + &ath6kl_regd_K2, + &ath6kl_regd_K3, + &ath6kl_regd_KW, + &ath6kl_regd_LV, + &ath6kl_regd_LB, + &ath6kl_regd_LI, + &ath6kl_regd_LT, + &ath6kl_regd_LU, + &ath6kl_regd_MO, + &ath6kl_regd_MK, + &ath6kl_regd_MW, + &ath6kl_regd_MY, + &ath6kl_regd_MT, + &ath6kl_regd_MX, + &ath6kl_regd_MC, + &ath6kl_regd_MA, + &ath6kl_regd_NP, + &ath6kl_regd_NZ, + &ath6kl_regd_NL, + &ath6kl_regd_AN, + &ath6kl_regd_NO, + &ath6kl_regd_OM, + &ath6kl_regd_PK, + &ath6kl_regd_PA, + &ath6kl_regd_PE, + &ath6kl_regd_PH, + &ath6kl_regd_PL, + &ath6kl_regd_PT, + &ath6kl_regd_PR, + &ath6kl_regd_QA, + &ath6kl_regd_RO, + &ath6kl_regd_RU, + &ath6kl_regd_RW, + &ath6kl_regd_SA, + &ath6kl_regd_ME, + &ath6kl_regd_RS, + &ath6kl_regd_SG, + &ath6kl_regd_SK, + &ath6kl_regd_SI, + &ath6kl_regd_ZA, + &ath6kl_regd_ES, + &ath6kl_regd_LK, + &ath6kl_regd_SE, + &ath6kl_regd_CH, + &ath6kl_regd_SY, + &ath6kl_regd_TW, + &ath6kl_regd_TH, + &ath6kl_regd_TT, + &ath6kl_regd_TN, + &ath6kl_regd_TR, + &ath6kl_regd_UG, + &ath6kl_regd_UA, + &ath6kl_regd_AE, + &ath6kl_regd_GB, + &ath6kl_regd_US, + &ath6kl_regd_U2, + &ath6kl_regd_PS, + &ath6kl_regd_UY, + &ath6kl_regd_UZ, + &ath6kl_regd_VE, + &ath6kl_regd_VN, + &ath6kl_regd_YE, + &ath6kl_regd_ZW, + NULL, /* keep last */ +}; + +const struct ieee80211_regdomain *ath6kl_reg_regdb_region[] = { + &ath6kl_regd_00, + &ath6kl_regd_03, + &ath6kl_regd_07, + &ath6kl_regd_08, + &ath6kl_regd_20, + &ath6kl_regd_21, + &ath6kl_regd_22, + &ath6kl_regd_3a, + &ath6kl_regd_3b, + &ath6kl_regd_3f, + &ath6kl_regd_12, + &ath6kl_regd_13, + &ath6kl_regd_16, + &ath6kl_regd_14, + &ath6kl_regd_23, + &ath6kl_regd_37, + &ath6kl_regd_35, + &ath6kl_regd_36, + &ath6kl_regd_30, + &ath6kl_regd_39, + &ath6kl_regd_34, + &ath6kl_regd_3d, + &ath6kl_regd_3e, + &ath6kl_regd_31, + &ath6kl_regd_11, + &ath6kl_regd_10, + &ath6kl_regd_52, + &ath6kl_regd_45, + &ath6kl_regd_4d, + &ath6kl_regd_47, + &ath6kl_regd_50, + &ath6kl_regd_42, + &ath6kl_regd_58, + &ath6kl_regd_5b, + &ath6kl_regd_5c, + &ath6kl_regd_5d, + &ath6kl_regd_5e, + &ath6kl_regd_5f, + &ath6kl_regd_51, + &ath6kl_regd_55, + &ath6kl_regd_56, + &ath6kl_regd_49, + &ath6kl_regd_99, + &ath6kl_regd_9a, + &ath6kl_regd_88, + &ath6kl_regd_d4, + &ath6kl_regd_d5, + &ath6kl_regd_d7, + &ath6kl_regd_87, + &ath6kl_regd_60, + &ath6kl_regd_61, + &ath6kl_regd_62, + &ath6kl_regd_63, + &ath6kl_regd_64, + &ath6kl_regd_65, + &ath6kl_regd_66, + &ath6kl_regd_67, + &ath6kl_regd_68, + &ath6kl_regd_69, + &ath6kl_regd_6a, + &ath6kl_regd_6b, + &ath6kl_regd_6c, + NULL, /* keep last */ +}; +#else +const struct ieee80211_regdomain *reg_regdb[] = { + &ath6kl_regd_AL, + &ath6kl_regd_DZ, + &ath6kl_regd_AR, + &ath6kl_regd_AM, + &ath6kl_regd_AW, + &ath6kl_regd_AU, + &ath6kl_regd_A2, + &ath6kl_regd_AT, + &ath6kl_regd_AZ, + &ath6kl_regd_BH, + &ath6kl_regd_BD, + &ath6kl_regd_BB, + &ath6kl_regd_BY, + &ath6kl_regd_BE, + &ath6kl_regd_BZ, + &ath6kl_regd_BO, + &ath6kl_regd_BA, + &ath6kl_regd_BR, + &ath6kl_regd_BN, + &ath6kl_regd_BG, + &ath6kl_regd_KH, + &ath6kl_regd_CA, + &ath6kl_regd_C2, + &ath6kl_regd_CL, + &ath6kl_regd_CN, + &ath6kl_regd_CO, + &ath6kl_regd_CR, + &ath6kl_regd_HR, + &ath6kl_regd_CY, + &ath6kl_regd_CZ, + &ath6kl_regd_DK, + &ath6kl_regd_DO, + &ath6kl_regd_EC, + &ath6kl_regd_EG, + &ath6kl_regd_SV, + &ath6kl_regd_EE, + &ath6kl_regd_FI, + &ath6kl_regd_FR, + &ath6kl_regd_F2, + &ath6kl_regd_GE, + &ath6kl_regd_DE, + &ath6kl_regd_GR, + &ath6kl_regd_GL, + &ath6kl_regd_GD, + &ath6kl_regd_GU, + &ath6kl_regd_GT, + &ath6kl_regd_HT, + &ath6kl_regd_HN, + &ath6kl_regd_HK, + &ath6kl_regd_HU, + &ath6kl_regd_IS, + &ath6kl_regd_IN, + &ath6kl_regd_ID, + &ath6kl_regd_IR, + &ath6kl_regd_IE, + &ath6kl_regd_IL, + &ath6kl_regd_IT, + &ath6kl_regd_JM, + &ath6kl_regd_JP, + &ath6kl_regd_JO, + &ath6kl_regd_KZ, + &ath6kl_regd_KE, + &ath6kl_regd_KP, + &ath6kl_regd_KR, + &ath6kl_regd_K2, + &ath6kl_regd_K3, + &ath6kl_regd_KW, + &ath6kl_regd_LV, + &ath6kl_regd_LB, + &ath6kl_regd_LI, + &ath6kl_regd_LT, + &ath6kl_regd_LU, + &ath6kl_regd_MO, + &ath6kl_regd_MK, + &ath6kl_regd_MW, + &ath6kl_regd_MY, + &ath6kl_regd_MT, + &ath6kl_regd_MX, + &ath6kl_regd_MC, + &ath6kl_regd_MA, + &ath6kl_regd_NP, + &ath6kl_regd_NZ, + &ath6kl_regd_NL, + &ath6kl_regd_AN, + &ath6kl_regd_NO, + &ath6kl_regd_OM, + &ath6kl_regd_PK, + &ath6kl_regd_PA, + &ath6kl_regd_PE, + &ath6kl_regd_PH, + &ath6kl_regd_PL, + &ath6kl_regd_PT, + &ath6kl_regd_PR, + &ath6kl_regd_QA, + &ath6kl_regd_RO, + &ath6kl_regd_RU, + &ath6kl_regd_RW, + &ath6kl_regd_SA, + &ath6kl_regd_ME, + &ath6kl_regd_RS, + &ath6kl_regd_SG, + &ath6kl_regd_SK, + &ath6kl_regd_SI, + &ath6kl_regd_ZA, + &ath6kl_regd_ES, + &ath6kl_regd_LK, + &ath6kl_regd_SE, + &ath6kl_regd_CH, + &ath6kl_regd_SY, + &ath6kl_regd_TW, + &ath6kl_regd_TH, + &ath6kl_regd_TT, + &ath6kl_regd_TN, + &ath6kl_regd_TR, + &ath6kl_regd_UG, + &ath6kl_regd_UA, + &ath6kl_regd_AE, + &ath6kl_regd_GB, + &ath6kl_regd_US, + &ath6kl_regd_U2, + &ath6kl_regd_PS, + &ath6kl_regd_UY, + &ath6kl_regd_UZ, + &ath6kl_regd_VE, + &ath6kl_regd_VN, + &ath6kl_regd_YE, + &ath6kl_regd_ZW, + &ath6kl_regd_00, + &ath6kl_regd_03, + &ath6kl_regd_07, + &ath6kl_regd_08, + &ath6kl_regd_20, + &ath6kl_regd_21, + &ath6kl_regd_22, + &ath6kl_regd_3a, + &ath6kl_regd_3b, + &ath6kl_regd_3f, + &ath6kl_regd_12, + &ath6kl_regd_13, + &ath6kl_regd_16, + &ath6kl_regd_14, + &ath6kl_regd_23, + &ath6kl_regd_37, + &ath6kl_regd_35, + &ath6kl_regd_36, + &ath6kl_regd_30, + &ath6kl_regd_39, + &ath6kl_regd_34, + &ath6kl_regd_3d, + &ath6kl_regd_3e, + &ath6kl_regd_31, + &ath6kl_regd_11, + &ath6kl_regd_10, + &ath6kl_regd_52, + &ath6kl_regd_45, + &ath6kl_regd_4d, + &ath6kl_regd_47, + &ath6kl_regd_50, + &ath6kl_regd_42, + &ath6kl_regd_58, + &ath6kl_regd_5b, + &ath6kl_regd_5c, + &ath6kl_regd_5d, + &ath6kl_regd_5e, + &ath6kl_regd_5f, + &ath6kl_regd_51, + &ath6kl_regd_55, + &ath6kl_regd_56, + &ath6kl_regd_49, + &ath6kl_regd_99, + &ath6kl_regd_9a, + &ath6kl_regd_88, + &ath6kl_regd_d4, + &ath6kl_regd_d5, + &ath6kl_regd_d7, + &ath6kl_regd_87, + &ath6kl_regd_60, + &ath6kl_regd_61, + &ath6kl_regd_62, + &ath6kl_regd_63, + &ath6kl_regd_64, + &ath6kl_regd_65, + &ath6kl_regd_66, + &ath6kl_regd_67, + &ath6kl_regd_68, + &ath6kl_regd_69, + &ath6kl_regd_6a, + &ath6kl_regd_6b, + &ath6kl_regd_6c, +}; + +int reg_regdb_size = ARRAY_SIZE(reg_regdb); +#endif + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/rttapi.h b/drivers/net/wireless/ath/ath6kl-3.5/rttapi.h new file mode 100644 index 000000000000..d18ab0752338 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/rttapi.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 _HOST_RTT_API_H_ +#define _HOST_RTT_API_H_ + + +int rttm_init(void *); + +int rttm_getbuf(void **buf, u32 *len); + +int rttm_recv(void *buf, u32 len); + +void rttm_free(void); + +int rttm_issue_request(void *buf, u32 len); + + +#endif /* _HOST_PKTLOG_API_H_ */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/rttm.c b/drivers/net/wireless/ath/ath6kl-3.5/rttm.c new file mode 100644 index 000000000000..c4bbeb5094cb --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/rttm.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "debug.h" +#include "wlan_location_defs.h" +#include "rttm.h" +#include "ath_netlink.h" + +struct rttm_context *g_pRttmContext; + +void DumpRttResp(void *data) +{ + int i = 0, j = 0; + struct nsp_mresphdr *presphdr = (struct nsp_mresphdr *)data; + struct nsp_cir_resp *pcirresp = + (struct nsp_cir_resp *)((u8 *)data + sizeof(struct nsp_mresphdr)); + int explen = (presphdr->no_of_responses * sizeof(struct nsp_cir_resp)) + + sizeof(struct nsp_mresphdr); + ath6kl_dbg(ATH6KL_DBG_RTT, "RTT Response Size Expected : %d ", explen); + if (presphdr) + ath6kl_dbg(ATH6KL_DBG_RTT, "NSP Response ReqId : %x " + "RespType : %x NoResp : %x Result : %x\n", + presphdr->request_id, presphdr->response_type, + presphdr->no_of_responses, presphdr->result); + else + return; + + pcirresp->no_of_chains = 2; + for (i = 0; i < presphdr->no_of_responses; i++) { + ath6kl_dbg(ATH6KL_DBG_RTT, "TOD : %x\n", pcirresp->tod); + ath6kl_dbg(ATH6KL_DBG_RTT, "TOA : %x\n", pcirresp->toa); + ath6kl_dbg(ATH6KL_DBG_RTT, "TotalChains : %x\n", + pcirresp->no_of_chains); + ath6kl_dbg(ATH6KL_DBG_RTT, "RSSI0 : %x RSSI1 : %x ", + pcirresp->rssi[0], pcirresp->rssi[1]); + ath6kl_dbg(ATH6KL_DBG_RTT, "SendRate : %x\n", + pcirresp->sendrate); + ath6kl_dbg(ATH6KL_DBG_RTT, "RecvRate : %x\n", + pcirresp->recvrate); + ath6kl_dbg(ATH6KL_DBG_RTT, "ChannelDump :\n"); + for (j = 0; j < RTTM_CDUMP_SIZE(pcirresp->no_of_chains, + pcirresp->isht40); j++) { + u8 k = 0; + k++; + ath6kl_dbg(ATH6KL_DBG_RTT, "%x ", + pcirresp->channel_dump[j]); + if (k > 15) + ath6kl_dbg(ATH6KL_DBG_RTT, ("\n")); + } + pcirresp++; + } +} + +int rttm_init(void *ar) +{ + struct rttm_context *prttm = NULL; + ath6kl_dbg(ATH6KL_DBG_RTT, "rttm init "); + prttm = kmalloc(sizeof(struct rttm_context), GFP_KERNEL); + if (NULL == prttm) + return -ENOMEM; + memset(prttm, 0, sizeof(struct rttm_context)); + prttm->ar = ar; + DEV_SETRTT_HDL(prttm); + /* Initialize NL For RTT */ + if (0 != ath_netlink_init()) { + ath6kl_err("RTT Init Failed to Initialize NetLink Interface\n"); + return -ENODEV; + } + return 0; +} + +int rttm_recv(void *buf, u32 len) +{ +#define RTTM_CONTEXT_PREFIX_OFFSET \ + (sizeof(u32) + sizeof(u32) + sizeof(struct ath6kl *)) + + struct rttm_context *prttm = NULL; + struct nsp_mresphdr *presphdr = (struct nsp_mresphdr *)buf; + int resptype = presphdr->response_type; + prttm = DEV_GETRTT_HDL(); + + /* printk("RTT Recv Len : %d %d\n", len, resptype); */ + if ((resptype == MRESP_CLKOFFSETCAL_START) || + (resptype == MRESP_CLKOFFSETCAL_END)) { + ath6kl_dbg(ATH6KL_DBG_RTT, + "RTT ClkCal Request %d\n", resptype); + if (resptype == MRESP_CLKOFFSETCAL_START) { + prttm->rttdhclkcal_active = 1; + prttm->dhclkcal_index = 0; + } else if (resptype == MRESP_CLKOFFSETCAL_END) { + prttm->rttdhclkcal_active = 0; + prttm->dhclkcal_index = 0; + /* Post Response of measurements to Device */ + prttm->mresphdr.frame_type = NSP_RTTCLKCAL_INFO; + rttm_issue_request( + (char *)prttm + RTTM_CONTEXT_PREFIX_OFFSET, + NSP_HDR_LEN + sizeof(struct nsp_rtt_clkoffset)); + } + + } else { + if (buf && len) { + /* Pass up Recv RTT Resp by NL */ + /* DumpRttResp(buf); */ + ath6kl_dbg(ATH6KL_DBG_RTT, "NLSend Len : %d ", len); + ath_netlink_send(buf, len); + } + } + return 0; +} + +void rttm_free() +{ + struct rttm_context *prttm = NULL; + prttm = DEV_GETRTT_HDL(); + if (prttm != NULL) + kfree(prttm); + ath_netlink_free(); +} + + +int rttm_issue_request(void *buf, u32 len) +{ + struct rttm_context *prttm = NULL; + struct nsp_header hdr; + u32 ftype; + struct ath6kl *ar = NULL; + enum wmi_cmd_id cmd_id = WMI_RTT_MEASREQ_CMDID; + prttm = DEV_GETRTT_HDL(); + + ar = prttm->ar; + memcpy(&hdr, buf, NSP_HDR_LEN); + ftype = hdr.frame_type; + ath6kl_dbg(ATH6KL_DBG_RTT, "RTT Req Type : %d Len : %d ", ftype, len); + if (ftype == NSP_MRQST) { + struct nsp_mrqst *pstmrqst = + (struct nsp_mrqst *)(buf + NSP_HDR_LEN); + ath6kl_dbg(ATH6KL_DBG_RTT, "NSP Request ID:%d mode:%d " + "channel : %d NoMeas : %d Rate : %x\n ", + pstmrqst->request_id, pstmrqst->mode, + pstmrqst->channel, pstmrqst->no_of_measurements, + pstmrqst->transmit_rate); + if (pstmrqst->no_of_measurements > 10) { + ath6kl_dbg(ATH6KL_DBG_RTT, + "RTTREQ No Measurements >10 : %d ", + pstmrqst->no_of_measurements); + return -EINVAL; + } + cmd_id = WMI_RTT_MEASREQ_CMDID; + } else if (ftype == NSP_RTTCONFIG) { + struct nsp_rtt_config *pstrttcfg = + (struct nsp_rtt_config *)(buf + NSP_HDR_LEN); + ath6kl_dbg(ATH6KL_DBG_RTT, "NSP RTTCFG 2gCal%d 5gCal:%d " + "FFTScale: %d RngScale:%d " + "CLKSpeed2g : %d\n CLKSpeed5g : %d", + pstrttcfg->ClkCal[0], pstrttcfg->ClkCal[1], + pstrttcfg->FFTScale, pstrttcfg->RangeScale, + pstrttcfg->ClkSpeed[0], pstrttcfg->ClkSpeed[1]); + cmd_id = WMI_RTT_CONFIG_CMDID; + } else if (ftype == NSP_RTTCLKCAL_INFO) { + ath6kl_dbg(ATH6KL_DBG_RTT, "NSP CLK CALINFO CMD\n"); + cmd_id = WMI_RTT_CLKCALINFO_CMDID; + } + + if (wmi_rtt_req(ar->wmi, cmd_id, buf + NSP_HDR_LEN, + len - NSP_HDR_LEN)) { + ath6kl_dbg(ATH6KL_DBG_RTT, "RTT Req Fail "); + return -EIO; + } + return 0; +} + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/rttm.h b/drivers/net/wireless/ath/ath6kl-3.5/rttm.h new file mode 100644 index 000000000000..1994d9390e7f --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/rttm.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 _RTTM_H_ +#define _RTTM_H_ +#include "wlan_location_defs.h" +#include "rttapi.h" +#define MAX_NSPMRESP_CIR 10 + +struct rtt_priv_hdr { + u8 done; +}; + +#define MAX_RTT_CIRRESP_RECORDS 100 +#define RTTM_CIR_BUF_SIZE (MAX_RTT_CIRRESP_RECORDS * \ + (sizeof(struct rtt_priv_hdr) + \ + MRES_LEN + CIR_RES_LEN)) + +struct rttm_context { + u32 ts1; + u32 ts2; + struct ath6kl *ar; + struct nsp_header mresphdr ; + struct nsp_rttd2h2_clkoffset rttd2h2_clk; + u8 rttdhclkcal_active; + u8 dhclkcal_index; +}; + +extern struct rttm_context *g_pRttmContext; +#define DEV_GETRTT_HDL() g_pRttmContext +#define DEV_SETRTT_HDL(rtt) (g_pRttmContext = rtt) +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/sdio.c b/drivers/net/wireless/ath/ath6kl-3.5/sdio.c new file mode 100644 index 000000000000..9d49dc4bb50a --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/sdio.c @@ -0,0 +1,1529 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 +#include +#include +#include +#include +#include +#include "hif.h" +#include "hif-ops.h" +#include "target.h" +#include "debug.h" +#include "cfg80211.h" +#include +/* +extern void qca6234_wifi_gpio(bool on); +extern void qca6234_bt_gpio(bool on); +extern int BT_RF_status; +*/ + +/* + * Define GPIO number for WoW in your platform other than zero + * Wake lock will be called when GPIO asserted. + */ +#ifdef CONFIG_ANDROID +#define PLAT_WOW_GPIO_PIN 26 +#endif + +struct ath6kl_sdio { + struct sdio_func *func; + + spinlock_t lock; + + /* free list */ + struct list_head bus_req_freeq; + + /* available bus requests */ + struct bus_request bus_req[BUS_REQUEST_MAX_NUM]; + + struct ath6kl *ar; + + u8 *dma_buffer; + + /* protects access to dma_buffer */ + struct mutex dma_buffer_mutex; + + /* scatter request list head */ + struct list_head scat_req; + + spinlock_t scat_lock; + bool scatter_enabled; + + bool is_disabled; + atomic_t irq_handling; + const struct sdio_device_id *id; + struct work_struct wr_async_work; + struct list_head wr_asyncq; + spinlock_t wr_async_lock; +}; + +#define CMD53_ARG_READ 0 +#define CMD53_ARG_WRITE 1 +#define CMD53_ARG_BLOCK_BASIS 1 +#define CMD53_ARG_FIXED_ADDRESS 0 +#define CMD53_ARG_INCR_ADDRESS 1 + +static inline struct ath6kl_sdio *ath6kl_sdio_priv(struct ath6kl *ar) +{ + return ar->hif_priv; +} + +/* + * Macro to check if DMA buffer is WORD-aligned and DMA-able. + * Most host controllers assume the buffer is DMA'able and will + * bug-check otherwise (i.e. buffers on the stack). virt_addr_valid + * check fails on stack memory. + */ +static inline bool buf_needs_bounce(u8 *buf) +{ + return ((unsigned long) buf & 0x3) || !virt_addr_valid(buf); +} + +static void ath6kl_sdio_set_mbox_info(struct ath6kl *ar) +{ + struct ath6kl_mbox_info *mbox_info = &ar->mbox_info; + + /* EP1 has an extended range */ + mbox_info->htc_addr = HIF_MBOX_BASE_ADDR; + mbox_info->htc_ext_addr = HIF_MBOX0_EXT_BASE_ADDR; + mbox_info->htc_ext_sz = HIF_MBOX0_EXT_WIDTH; + mbox_info->block_size = HIF_MBOX_BLOCK_SIZE; + mbox_info->gmbox_addr = HIF_GMBOX_BASE_ADDR; + mbox_info->gmbox_sz = HIF_GMBOX_WIDTH; +} + +static inline void ath6kl_sdio_set_cmd53_arg(u32 *arg, u8 rw, u8 func, + u8 mode, u8 opcode, u32 addr, + u16 blksz) +{ + *arg = (((rw & 1) << 31) | + ((func & 0x7) << 28) | + ((mode & 1) << 27) | + ((opcode & 1) << 26) | + ((addr & 0x1FFFF) << 9) | + (blksz & 0x1FF)); +} + +static inline void ath6kl_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw, + unsigned int address, + unsigned char val) +{ + const u8 func = 0; + + *arg = ((write & 1) << 31) | + ((func & 0x7) << 28) | + ((raw & 1) << 27) | + (1 << 26) | + ((address & 0x1FFFF) << 9) | + (1 << 8) | + (val & 0xFF); +} + +static int ath6kl_sdio_func0_cmd52_wr_byte(struct mmc_card *card, + unsigned int address, + unsigned char byte) +{ + struct mmc_command io_cmd; + + memset(&io_cmd, 0, sizeof(io_cmd)); + ath6kl_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte); + io_cmd.opcode = SD_IO_RW_DIRECT; + io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; + + return mmc_wait_for_cmd(card->host, &io_cmd, 0); +} + +static int ath6kl_sdio_func0_cmd52_rd_byte(struct mmc_card *card, + unsigned int address, + unsigned char *byte) +{ + struct mmc_command io_cmd; + u32 err; + + *byte = 0; + memset(&io_cmd, 0, sizeof(io_cmd)); + ath6kl_sdio_set_cmd52_arg(&io_cmd.arg, 0, 0, address, *byte); + io_cmd.opcode = SD_IO_RW_DIRECT; + io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &io_cmd, 0); + if (!err) + *byte = io_cmd.resp[0] & 0xff; + + return err; +} + +static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr, + u8 *buf, u32 len) +{ + int ret = 0; + + sdio_claim_host(func); + + if (request & HIF_WRITE) { + /* FIXME: looks like ugly workaround for something */ + if (addr >= HIF_MBOX_BASE_ADDR && + addr <= HIF_MBOX_END_ADDR) + addr += (HIF_MBOX_WIDTH - len); + + /* FIXME: this also looks like ugly workaround */ + if (addr == HIF_MBOX0_EXT_BASE_ADDR) + addr += HIF_MBOX0_EXT_WIDTH - len; + + if (request & HIF_FIXED_ADDRESS) + ret = sdio_writesb(func, addr, buf, len); + else + ret = sdio_memcpy_toio(func, addr, buf, len); + } else { + if (request & HIF_FIXED_ADDRESS) + ret = sdio_readsb(func, buf, addr, len); + else + ret = sdio_memcpy_fromio(func, buf, addr, len); + } + + sdio_release_host(func); + + ath6kl_dbg(ATH6KL_DBG_SDIO, "%s addr 0x%x%s buf 0x%p len %d\n", + request & HIF_WRITE ? "wr" : "rd", addr, + request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len); + ath6kl_dbg_dump(ATH6KL_DBG_SDIO_DUMP, NULL, "sdio ", buf, len); + + return ret; +} + +static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio) +{ + struct bus_request *bus_req; + + spin_lock_bh(&ar_sdio->lock); + + if (list_empty(&ar_sdio->bus_req_freeq)) { + spin_unlock_bh(&ar_sdio->lock); + return NULL; + } + + bus_req = list_first_entry(&ar_sdio->bus_req_freeq, + struct bus_request, list); + list_del(&bus_req->list); + + spin_unlock_bh(&ar_sdio->lock); + ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n", + __func__, bus_req); + + return bus_req; +} + +static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio, + struct bus_request *bus_req) +{ + ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n", + __func__, bus_req); + + spin_lock_bh(&ar_sdio->lock); + list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq); + spin_unlock_bh(&ar_sdio->lock); +} + +static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req, + struct mmc_data *data) +{ + struct scatterlist *sg; + int i; + + data->blksz = HIF_MBOX_BLOCK_SIZE; + data->blocks = scat_req->len / HIF_MBOX_BLOCK_SIZE; + + ath6kl_dbg(ATH6KL_DBG_SCATTER, + "hif-scatter: (%s) addr: 0x%X, (block len: %d, block count: %d) , (tot:%d,sg:%d)\n", + (scat_req->req & HIF_WRITE) ? "WR" : "RD", scat_req->addr, + data->blksz, data->blocks, scat_req->len, + scat_req->scat_entries); + + data->flags = (scat_req->req & HIF_WRITE) ? MMC_DATA_WRITE : + MMC_DATA_READ; + + /* fill SG entries */ + sg = scat_req->sgentries; + sg_init_table(sg, scat_req->scat_entries); + + /* assemble SG list */ + for (i = 0; i < scat_req->scat_entries; i++, sg++) { + ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n", + i, scat_req->scat_list[i].buf, + scat_req->scat_list[i].len); + + sg_set_buf(sg, scat_req->scat_list[i].buf, + scat_req->scat_list[i].len); + } + + /* set scatter-gather table for request */ + data->sg = scat_req->sgentries; + data->sg_len = scat_req->scat_entries; +} + +static int ath6kl_sdio_scat_rw(struct ath6kl_sdio *ar_sdio, + struct bus_request *req) +{ + struct mmc_request mmc_req; + struct mmc_command cmd; + struct mmc_data data; + struct hif_scatter_req *scat_req; + u8 opcode, rw; + int status, len; + + scat_req = req->scat_req; + + if (scat_req->virt_scat) { + len = scat_req->len; + if (scat_req->req & HIF_BLOCK_BASIS) + len = round_down(len, HIF_MBOX_BLOCK_SIZE); + + status = ath6kl_sdio_io(ar_sdio->func, scat_req->req, + scat_req->addr, scat_req->virt_dma_buf, + len); + goto scat_complete; + } + + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + ath6kl_sdio_setup_scat_data(scat_req, &data); + + opcode = (scat_req->req & HIF_FIXED_ADDRESS) ? + CMD53_ARG_FIXED_ADDRESS : CMD53_ARG_INCR_ADDRESS; + + rw = (scat_req->req & HIF_WRITE) ? CMD53_ARG_WRITE : CMD53_ARG_READ; + + /* Fixup the address so that the last byte will fall on MBOX EOM */ + if (scat_req->req & HIF_WRITE) { + if (scat_req->addr == HIF_MBOX_BASE_ADDR) + scat_req->addr += HIF_MBOX_WIDTH - scat_req->len; + else + /* Uses extended address range */ + scat_req->addr += HIF_MBOX0_EXT_WIDTH - scat_req->len; + } + + /* set command argument */ + ath6kl_sdio_set_cmd53_arg(&cmd.arg, rw, ar_sdio->func->num, + CMD53_ARG_BLOCK_BASIS, opcode, scat_req->addr, + data.blocks); + + cmd.opcode = SD_IO_RW_EXTENDED; + cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + mmc_req.cmd = &cmd; + mmc_req.data = &data; + + sdio_claim_host(ar_sdio->func); + + mmc_set_data_timeout(&data, ar_sdio->func->card); + /* synchronous call to process request */ + mmc_wait_for_req(ar_sdio->func->card->host, &mmc_req); + + sdio_release_host(ar_sdio->func); + + status = cmd.error ? cmd.error : data.error; + +scat_complete: + scat_req->status = status; + + if (scat_req->status) + ath6kl_err("Scatter write request failed:%d\n", + scat_req->status); + + if (scat_req->req & HIF_ASYNCHRONOUS) + scat_req->complete(ar_sdio->ar->htc_target, scat_req); + + return status; +} + +static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio, + int n_scat_entry, int n_scat_req, + bool virt_scat) +{ + struct hif_scatter_req *s_req; + struct bus_request *bus_req; + int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz; + u8 *virt_buf; + + scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item); + scat_req_sz = sizeof(*s_req) + scat_list_sz; + + if (!virt_scat) + sg_sz = sizeof(struct scatterlist) * n_scat_entry; + else + buf_sz = 2 * L1_CACHE_BYTES + + ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER; + + for (i = 0; i < n_scat_req; i++) { + /* allocate the scatter request */ + s_req = kzalloc(scat_req_sz, GFP_KERNEL); + if (!s_req) + return -ENOMEM; + + if (virt_scat) { + virt_buf = kzalloc(buf_sz, GFP_KERNEL); + if (!virt_buf) { + kfree(s_req); + return -ENOMEM; + } + + s_req->virt_dma_buf = + (u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf); + } else { + /* allocate sglist */ + s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL); + + if (!s_req->sgentries) { + kfree(s_req); + return -ENOMEM; + } + } + + /* allocate a bus request for this scatter request */ + bus_req = ath6kl_sdio_alloc_busreq(ar_sdio); + if (!bus_req) { + kfree(s_req->sgentries); + kfree(s_req->virt_dma_buf); + kfree(s_req); + return -ENOMEM; + } + + /* assign the scatter request to this bus request */ + bus_req->scat_req = s_req; + s_req->busrequest = bus_req; + + s_req->virt_scat = virt_scat; + + /* add it to the scatter pool */ + hif_scatter_req_add(ar_sdio->ar, s_req); + } + + return 0; +} + +static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, + u32 len, u32 request) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + u8 *tbuf = NULL; + int ret; + bool bounced = false; + + if (request & HIF_BLOCK_BASIS) + len = round_down(len, HIF_MBOX_BLOCK_SIZE); + + if (buf_needs_bounce(buf)) { + if (!ar_sdio->dma_buffer) + return -ENOMEM; + mutex_lock(&ar_sdio->dma_buffer_mutex); + tbuf = ar_sdio->dma_buffer; + memcpy(tbuf, buf, len); + bounced = true; + } else + tbuf = buf; + + ret = ath6kl_sdio_io(ar_sdio->func, request, addr, tbuf, len); + if ((request & HIF_READ) && bounced) + memcpy(buf, tbuf, len); + + if (bounced) + mutex_unlock(&ar_sdio->dma_buffer_mutex); + + return ret; +} + +static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio, + struct bus_request *req) +{ + if (req->scat_req) + ath6kl_sdio_scat_rw(ar_sdio, req); + else { + void *context; + int status; + + status = ath6kl_sdio_read_write_sync(ar_sdio->ar, req->address, + req->buffer, req->length, + req->request); + context = req->packet; + ath6kl_sdio_free_bus_req(ar_sdio, req); + ath6kl_hif_rw_comp_handler(context, status); + } +} + +static void ath6kl_sdio_write_async_work(struct work_struct *work) +{ + struct ath6kl_sdio *ar_sdio; + struct bus_request *req, *tmp_req; + + ar_sdio = container_of(work, struct ath6kl_sdio, wr_async_work); + + spin_lock_bh(&ar_sdio->wr_async_lock); + list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) { + list_del(&req->list); + spin_unlock_bh(&ar_sdio->wr_async_lock); + __ath6kl_sdio_write_async(ar_sdio, req); + spin_lock_bh(&ar_sdio->wr_async_lock); + } + spin_unlock_bh(&ar_sdio->wr_async_lock); +} + +static void ath6kl_sdio_irq_handler(struct sdio_func *func) +{ + int status; + struct ath6kl_sdio *ar_sdio; + + ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n"); + + ar_sdio = sdio_get_drvdata(func); + atomic_set(&ar_sdio->irq_handling, 1); + + /* + * Release the host during interrups so we can pick it back up when + * we process commands. + */ + sdio_release_host(ar_sdio->func); + + status = ath6kl_hif_intr_bh_handler(ar_sdio->ar); + sdio_claim_host(ar_sdio->func); + atomic_set(&ar_sdio->irq_handling, 0); + WARN_ON(status && status != -ECANCELED); +} + +static int ath6kl_sdio_power_on(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct sdio_func *func = ar_sdio->func; + int ret = 0; + + if (!ar_sdio->is_disabled) + return 0; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "sdio power on\n"); + qca6234_wifi_gpio(1); + + sdio_claim_host(func); + + ret = sdio_enable_func(func); + if (ret) { + ath6kl_err("Unable to enable sdio func: %d)\n", ret); + sdio_release_host(func); + return ret; + } + + sdio_release_host(func); + + /* + * Wait for hardware to initialise. It should take a lot less than + * 10 ms but let's be conservative here. + */ + usleep_range(10000, 10000); + + ar_sdio->is_disabled = false; + + return ret; +} + +static int ath6kl_sdio_power_off(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + int ret; + int num; + + if (ar_sdio->is_disabled) + return 0; + + ath6kl_dbg(ATH6KL_DBG_BOOT, "sdio power off\n"); + /* Disable the card */ + sdio_claim_host(ar_sdio->func); + + num = ar_sdio->func->num; + ar_sdio->func->num = 0; + sdio_writeb(ar_sdio->func, 0x05, 0xf0, &ret); + ar_sdio->func->num = num; + + ret = sdio_disable_func(ar_sdio->func); + sdio_release_host(ar_sdio->func); + + if (ret) + return ret; + + ar_sdio->is_disabled = true; + /* if BT is in use, then no wifi reset */ + if (BT_RF_status) + qca6234_wifi_gpio(0); + return ret; +} + +static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer, + u32 length, u32 request, + struct htc_packet *packet) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct bus_request *bus_req; + + bus_req = ath6kl_sdio_alloc_busreq(ar_sdio); + + if (!bus_req) + return -ENOMEM; + + bus_req->address = address; + bus_req->buffer = buffer; + bus_req->length = length; + bus_req->request = request; + bus_req->packet = packet; + + spin_lock_bh(&ar_sdio->wr_async_lock); + list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq); + spin_unlock_bh(&ar_sdio->wr_async_lock); + queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work); + + return 0; +} + +static void ath6kl_sdio_irq_enable(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + int ret; + + sdio_claim_host(ar_sdio->func); + + /* Register the isr */ + ret = sdio_claim_irq(ar_sdio->func, ath6kl_sdio_irq_handler); + if (ret) + ath6kl_err("Failed to claim sdio irq: %d\n", ret); + + sdio_release_host(ar_sdio->func); +} + +static void ath6kl_sdio_irq_disable(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + int ret; + + sdio_claim_host(ar_sdio->func); + + /* Mask our function IRQ */ + while (atomic_read(&ar_sdio->irq_handling)) { + sdio_release_host(ar_sdio->func); + schedule_timeout(HZ / 10); + sdio_claim_host(ar_sdio->func); + } + + ret = sdio_release_irq(ar_sdio->func); + if (ret) + ath6kl_err("Failed to release sdio irq: %d\n", ret); + + sdio_release_host(ar_sdio->func); +} + +static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct hif_scatter_req *node = NULL; + + spin_lock_bh(&ar_sdio->scat_lock); + + if (!list_empty(&ar_sdio->scat_req)) { + node = list_first_entry(&ar_sdio->scat_req, + struct hif_scatter_req, list); + list_del(&node->list); + + node->scat_q_depth = get_queue_depth(&ar_sdio->scat_req); + } + + spin_unlock_bh(&ar_sdio->scat_lock); + + return node; +} + +static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar, + struct hif_scatter_req *s_req) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + + spin_lock_bh(&ar_sdio->scat_lock); + + list_add_tail(&s_req->list, &ar_sdio->scat_req); + + spin_unlock_bh(&ar_sdio->scat_lock); + +} + +/* scatter gather read write request */ +static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar, + struct hif_scatter_req *scat_req) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + u32 request = scat_req->req; + int status = 0; + + if (!scat_req->len) + return -EINVAL; + + ath6kl_dbg(ATH6KL_DBG_SCATTER, + "hif-scatter: total len: %d scatter entries: %d\n", + scat_req->len, scat_req->scat_entries); + + if (request & HIF_SYNCHRONOUS) + status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest); + else { + spin_lock_bh(&ar_sdio->wr_async_lock); + list_add_tail(&scat_req->busrequest->list, &ar_sdio->wr_asyncq); + spin_unlock_bh(&ar_sdio->wr_async_lock); + queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work); + } + + return status; +} + +/* clean up scatter support */ +static void ath6kl_sdio_cleanup_scatter(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct hif_scatter_req *s_req, *tmp_req; + + /* empty the free list */ + spin_lock_bh(&ar_sdio->scat_lock); + list_for_each_entry_safe(s_req, tmp_req, &ar_sdio->scat_req, list) { + list_del(&s_req->list); + spin_unlock_bh(&ar_sdio->scat_lock); + + /* + * FIXME: should we also call completion handler with + * ath6kl_hif_rw_comp_handler() with status -ECANCELED so + * that the packet is properly freed? + */ + if (s_req->busrequest) + ath6kl_sdio_free_bus_req(ar_sdio, s_req->busrequest); + kfree(s_req->virt_dma_buf); + kfree(s_req->sgentries); + kfree(s_req); + + spin_lock_bh(&ar_sdio->scat_lock); + } + spin_unlock_bh(&ar_sdio->scat_lock); +} + +/* setup of HIF scatter resources */ +static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct htc_target *target = ar->htc_target; + int ret; + bool virt_scat = false; + + if (ar_sdio->scatter_enabled) + return 0; + + ar_sdio->scatter_enabled = true; + + /* check if host supports scatter and it meets our requirements */ + if (ar_sdio->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) { + ath6kl_err("host only supports scatter of :%d entries, need: %d\n", + ar_sdio->func->card->host->max_segs, + MAX_SCATTER_ENTRIES_PER_REQ); + virt_scat = true; + } + + if (!virt_scat) { + ret = ath6kl_sdio_alloc_prep_scat_req(ar_sdio, + MAX_SCATTER_ENTRIES_PER_REQ, + MAX_SCATTER_REQUESTS, virt_scat); + + if (!ret) { + ath6kl_dbg(ATH6KL_DBG_BOOT, + "hif-scatter enabled requests %d entries %d\n", + MAX_SCATTER_REQUESTS, + MAX_SCATTER_ENTRIES_PER_REQ); + + target->max_scat_entries = MAX_SCATTER_ENTRIES_PER_REQ; + target->max_xfer_szper_scatreq = + MAX_SCATTER_REQ_TRANSFER_SIZE; + } else { + ath6kl_sdio_cleanup_scatter(ar); + ath6kl_warn("hif scatter resource setup failed, trying virtual scatter method\n"); + } + } + + if (virt_scat || ret) { + ret = ath6kl_sdio_alloc_prep_scat_req(ar_sdio, + ATH6KL_SCATTER_ENTRIES_PER_REQ, + ATH6KL_SCATTER_REQS, virt_scat); + + if (ret) { + ath6kl_err("failed to alloc virtual scatter resources !\n"); + ath6kl_sdio_cleanup_scatter(ar); + return ret; + } + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "virtual scatter enabled requests %d entries %d\n", + ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ); + + target->max_scat_entries = ATH6KL_SCATTER_ENTRIES_PER_REQ; + target->max_xfer_szper_scatreq = + ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER; + } + + return 0; +} + +static int ath6kl_sdio_config(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct sdio_func *func = ar_sdio->func; + int ret; + + sdio_claim_host(func); + + if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >= + MANUFACTURER_ID_AR6003_BASE) { + unsigned char sdio_irq_mode; + + ret = ath6kl_sdio_func0_cmd52_rd_byte(func->card, + CCCR_SDIO_IRQ_MODE_REG, + &sdio_irq_mode); + if (ret) { + ath6kl_err("Failed to read CCCR SDIO IRQ mode reg %d\n", + ret); + goto out; + } else if (!(sdio_irq_mode & SDIO_IRQ_MODE_ASYNC_4BIT_IRQ)) { + /* enable 4-bit ASYNC interrupt on AR6003 or later */ + ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card, + CCCR_SDIO_IRQ_MODE_REG, + (sdio_irq_mode | SDIO_IRQ_MODE_ASYNC_4BIT_IRQ)); + if (ret) { + ath6kl_err("Failed to enable 4-bit async irq mode %d\n", + ret); + goto out; + } + + ath6kl_dbg(ATH6KL_DBG_BOOT, "4-bit async irq mode enabled\n"); + } + } + + /* give us some time to enable, in ms */ + func->enable_timeout = 100; + + ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE); + if (ret) { + ath6kl_err("Set sdio block size %d failed: %d)\n", + HIF_MBOX_BLOCK_SIZE, ret); + sdio_release_host(func); + goto out; + } + +out: + sdio_release_host(func); + + return ret; +} + +static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct sdio_func *func = ar_sdio->func; + mmc_pm_flag_t flags; + int ret; + + flags = sdio_get_host_pm_caps(func); + + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags); + + if (!(flags & MMC_PM_KEEP_POWER) || + (ar->conf_flags & ATH6KL_CONF_SUSPEND_CUTPOWER)) { + /* as host doesn't support keep power we need to cut power */ + return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, + NULL); + } + + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n", + ret); + return ret; + } +#ifdef CONFIG_ANDROID + if ((flags & MMC_PM_WAKE_SDIO_IRQ) + && ath6kl_android_need_wow_suspend(ar)) { +#else + if ((flags & MMC_PM_WAKE_SDIO_IRQ) && wow) { +#endif + /* + * The host sdio controller is capable of keep power and + * sdio irq wake up at this point. It's fine to continue + * wow suspend operation. + */ + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow); + if (ret) + return ret; + + ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); + if (ret) + ath6kl_err("set sdio wake irq flag failed: %d\n", ret); + + return ret; + } + + return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL); +} + +static int ath6kl_sdio_resume(struct ath6kl *ar) +{ + switch (ar->state) { + case ATH6KL_STATE_OFF: + case ATH6KL_STATE_CUTPOWER: + ath6kl_dbg(ATH6KL_DBG_SUSPEND, + "sdio resume configuring sdio\n"); + + /* need to set sdio settings after power is cut from sdio */ + ath6kl_sdio_config(ar); + break; + + case ATH6KL_STATE_ON: + break; + + case ATH6KL_STATE_DEEPSLEEP: + break; + + case ATH6KL_STATE_WOW: + break; + + case ATH6KL_STATE_PRE_SUSPEND: + break; + + case ATH6KL_STATE_PRE_SUSPEND_DEEPSLEEP: + break; + } + + ath6kl_cfg80211_resume(ar); + + return 0; +} + +/* set the window address register (using 4-byte register access ). */ +static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) +{ + int status; + u8 addr_val[4]; + s32 i; + + /* + * Write bytes 1,2,3 of the register to set the upper address bytes, + * the LSB is written last to initiate the access cycle + */ + + for (i = 1; i <= 3; i++) { + /* + * Fill the buffer with the address byte value we want to + * hit 4 times. + */ + memset(addr_val, ((u8 *)&addr)[i], 4); + + /* + * Hit each byte of the register address with a 4-byte + * write operation to the same address, this is a harmless + * operation. + */ + status = ath6kl_sdio_read_write_sync(ar, reg_addr + i, addr_val, + 4, HIF_WR_SYNC_BYTE_FIX); + if (status) + break; + } + + if (status) { + ath6kl_err("%s: failed to write initial bytes of 0x%x " + "to window reg: 0x%X\n", __func__, + addr, reg_addr); + return status; + } + + /* + * Write the address register again, this time write the whole + * 4-byte value. The effect here is that the LSB write causes the + * cycle to start, the extra 3 byte write to bytes 1,2,3 has no + * effect since we are writing the same values again + */ + status = ath6kl_sdio_read_write_sync(ar, reg_addr, (u8 *)(&addr), + 4, HIF_WR_SYNC_BYTE_INC); + + if (status) { + ath6kl_err("%s: failed to write 0x%x to window reg: 0x%X\n", + __func__, addr, reg_addr); + return status; + } + + return 0; +} + +static int ath6kl_sdio_diag_read32(struct ath6kl *ar, u32 address, u32 *data) +{ + int status; + + /* set window register to start read cycle */ + status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, + address); + + if (status) + return status; + + /* read the data */ + status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS, + (u8 *)data, sizeof(u32), HIF_RD_SYNC_BYTE_INC); + if (status) { + ath6kl_err("%s: failed to read from window data addr\n", + __func__); + return status; + } + + return status; +} + +static int ath6kl_sdio_diag_write32(struct ath6kl *ar, u32 address, + __le32 data) +{ + int status; + u32 val = (__force u32) data; + + /* set write data */ + status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS, + (u8 *) &val, sizeof(u32), HIF_WR_SYNC_BYTE_INC); + if (status) { + ath6kl_err("%s: failed to write 0x%x to window data addr\n", + __func__, data); + return status; + } + + /* set window register, which starts the write cycle */ + return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS, + address); +} + +static int ath6kl_sdio_bmi_credits(struct ath6kl *ar) +{ + u32 addr; + unsigned long timeout; + int ret; + + ar->bmi.cmd_credits = 0; + + /* Read the counter register to get the command credits */ + addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4; + + timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); + while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) { + + /* + * Hit the credit counter with a 4-byte access, the first byte + * read will hit the counter and cause a decrement, while the + * remaining 3 bytes has no effect. The rationale behind this + * is to make all HIF accesses 4-byte aligned. + */ + ret = ath6kl_sdio_read_write_sync(ar, addr, + (u8 *)&ar->bmi.cmd_credits, 4, + HIF_RD_SYNC_BYTE_INC); + if (ret) { + ath6kl_err("Unable to decrement the command credit " + "count register: %d\n", ret); + return ret; + } + + /* The counter is only 8 bits. + * Ignore anything in the upper 3 bytes + */ + ar->bmi.cmd_credits &= 0xFF; + } + + if (!ar->bmi.cmd_credits) { + ath6kl_err("bmi communication timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar) +{ + unsigned long timeout; + u32 rx_word = 0; + int ret = 0; + + timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); + while ((time_before(jiffies, timeout)) && !rx_word) { + ret = ath6kl_sdio_read_write_sync(ar, + RX_LOOKAHEAD_VALID_ADDRESS, + (u8 *)&rx_word, sizeof(rx_word), + HIF_RD_SYNC_BYTE_INC); + if (ret) { + ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n"); + return ret; + } + + /* all we really want is one bit */ + rx_word &= (1 << ENDPOINT1); + } + + if (!rx_word) { + ath6kl_err("bmi_recv_buf FIFO empty\n"); + return -EINVAL; + } + + return ret; +} + +static int ath6kl_sdio_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) +{ + int ret; + u32 addr; + + ret = ath6kl_sdio_bmi_credits(ar); + if (ret) + return ret; + + addr = ar->mbox_info.htc_addr; + + ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len, + HIF_WR_SYNC_BYTE_INC); + if (ret) + ath6kl_err("unable to send the bmi data to the device\n"); + + return ret; +} + +static int ath6kl_sdio_bmi_read(struct ath6kl *ar, u8 *buf, u32 len) +{ + int ret; + u32 addr; + + /* + * During normal bootup, small reads may be required. + * Rather than issue an HIF Read and then wait as the Target + * adds successive bytes to the FIFO, we wait here until + * we know that response data is available. + * + * This allows us to cleanly timeout on an unexpected + * Target failure rather than risk problems at the HIF level. + * In particular, this avoids SDIO timeouts and possibly garbage + * data on some host controllers. And on an interconnect + * such as Compact Flash (as well as some SDIO masters) which + * does not provide any indication on data timeout, it avoids + * a potential hang or garbage response. + * + * Synchronization is more difficult for reads larger than the + * size of the MBOX FIFO (128B), because the Target is unable + * to push the 129th byte of data until AFTER the Host posts an + * HIF Read and removes some FIFO data. So for large reads the + * Host proceeds to post an HIF Read BEFORE all the data is + * actually available to read. Fortunately, large BMI reads do + * not occur in practice -- they're supported for debug/development. + * + * So Host/Target BMI synchronization is divided into these cases: + * CASE 1: length < 4 + * Should not happen + * + * CASE 2: 4 <= length <= 128 + * Wait for first 4 bytes to be in FIFO + * If CONSERVATIVE_BMI_READ is enabled, also wait for + * a BMI command credit, which indicates that the ENTIRE + * response is available in the the FIFO + * + * CASE 3: length > 128 + * Wait for the first 4 bytes to be in FIFO + * + * For most uses, a small timeout should be sufficient and we will + * usually see a response quickly; but there may be some unusual + * (debug) cases of BMI_EXECUTE where we want an larger timeout. + * For now, we use an unbounded busy loop while waiting for + * BMI_EXECUTE. + * + * If BMI_EXECUTE ever needs to support longer-latency execution, + * especially in production, this code needs to be enhanced to sleep + * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently + * a function of Host processor speed. + */ + if (len >= 4) { /* NB: Currently, always true */ + ret = ath6kl_bmi_get_rx_lkahd(ar); + if (ret) + return ret; + } + + addr = ar->mbox_info.htc_addr; + ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len, + HIF_RD_SYNC_BYTE_INC); + if (ret) { + ath6kl_err("Unable to read the bmi data from the device: %d\n", + ret); + return ret; + } + + return 0; +} + +static void ath6kl_sdio_stop(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct bus_request *req, *tmp_req; + void *context; + + /* FIXME: make sure that wq is not queued again */ + + cancel_work_sync(&ar_sdio->wr_async_work); + + spin_lock_bh(&ar_sdio->wr_async_lock); + + list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) { + list_del(&req->list); + + if (req->scat_req) { + /* this is a scatter gather request */ + req->scat_req->status = -ECANCELED; + req->scat_req->complete(ar_sdio->ar->htc_target, + req->scat_req); + } else { + context = req->packet; + ath6kl_sdio_free_bus_req(ar_sdio, req); + ath6kl_hif_rw_comp_handler(context, -ECANCELED); + } + } + + spin_unlock_bh(&ar_sdio->wr_async_lock); + + WARN_ON(get_queue_depth(&ar_sdio->scat_req) != 4); +} + +static int ath6kl_sdio_stat(struct ath6kl *ar, u8 *buf, int buf_len) +{ + /* TBD */ + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ath6kl_sdio_early_suspend(struct ath6kl *ar) +{ + /* TBD */ +} + +static void ath6kl_sdio_late_resume(struct ath6kl *ar) +{ + /* TBD */ +} +#endif + +static int ath6kl_sdio_bus_config(struct ath6kl *ar) +{ + return 0; +} + +static int ath6kl_sdio_diag_warm_reset(struct ath6kl *ar) +{ + return 0; +} + +#ifdef USB_AUTO_SUSPEND +static void sdio_auto_pm_disable(struct ath6kl *ar) +{ + +} + +static void sdio_auto_pm_enable(struct ath6kl *ar) +{ + +} + +static void sdio_auto_pm_turnon(struct ath6kl *ar) +{ + +} + + +static void sdio_auto_pm_turnoff(struct ath6kl *ar) +{ + +} + +int sdio_debugfs_get_pm_usage_cnt(struct ath6kl *ar) +{ + return 0; +} +#endif + +static const struct ath6kl_hif_ops ath6kl_sdio_ops = { + .read_write_sync = ath6kl_sdio_read_write_sync, + .write_async = ath6kl_sdio_write_async, + .irq_enable = ath6kl_sdio_irq_enable, + .irq_disable = ath6kl_sdio_irq_disable, + .scatter_req_get = ath6kl_sdio_scatter_req_get, + .scatter_req_add = ath6kl_sdio_scatter_req_add, + .enable_scatter = ath6kl_sdio_enable_scatter, + .scat_req_rw = ath6kl_sdio_async_rw_scatter, + .cleanup_scatter = ath6kl_sdio_cleanup_scatter, + .suspend = ath6kl_sdio_suspend, + .resume = ath6kl_sdio_resume, + .diag_read32 = ath6kl_sdio_diag_read32, + .diag_write32 = ath6kl_sdio_diag_write32, + .bmi_read = ath6kl_sdio_bmi_read, + .bmi_write = ath6kl_sdio_bmi_write, + .power_on = ath6kl_sdio_power_on, + .power_off = ath6kl_sdio_power_off, + .stop = ath6kl_sdio_stop, + .get_stat = ath6kl_sdio_stat, + .diag_warm_reset = ath6kl_sdio_diag_warm_reset, +#ifdef CONFIG_HAS_EARLYSUSPEND + .early_suspend = ath6kl_sdio_early_suspend, + .late_resume = ath6kl_sdio_late_resume, +#endif + .bus_config = ath6kl_sdio_bus_config, +#ifdef USB_AUTO_SUSPEND + .auto_pm_disable = sdio_auto_pm_disable, + .auto_pm_enable = sdio_auto_pm_enable, + .auto_pm_turnon = sdio_auto_pm_turnon, + .auto_pm_turnoff = sdio_auto_pm_turnoff, + .auto_pm_get_usage_cnt = sdio_debugfs_get_pm_usage_cnt, +#endif +}; + +#ifdef CONFIG_PM_SLEEP + +/* + * Empty handlers so that mmc subsystem doesn't remove us entirely during + * suspend. We instead follow cfg80211 suspend/resume handlers. + */ +static int ath6kl_sdio_pm_suspend(struct device *device) +{ + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio pm suspend\n"); + + return 0; +} + +static int ath6kl_sdio_pm_resume(struct device *device) +{ + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio pm resume\n"); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(ath6kl_sdio_pm_ops, ath6kl_sdio_pm_suspend, + ath6kl_sdio_pm_resume); + +#define ATH6KL_SDIO_PM_OPS (&ath6kl_sdio_pm_ops) + +#else + +#define ATH6KL_SDIO_PM_OPS NULL + +#endif /* CONFIG_PM_SLEEP */ + +static int ath6kl_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + int ret; + struct ath6kl_sdio *ar_sdio; + struct ath6kl *ar; + int count; + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n", + func->num, func->vendor, func->device, + func->max_blksize, func->cur_blksize); + + ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL); + if (!ar_sdio) + return -ENOMEM; + + ar_sdio->dma_buffer = kzalloc(HIF_DMA_BUFFER_SIZE, GFP_KERNEL); + if (!ar_sdio->dma_buffer) { + ret = -ENOMEM; + goto err_hif; + } + + ar_sdio->func = func; + sdio_set_drvdata(func, ar_sdio); + + ar_sdio->id = id; + ar_sdio->is_disabled = true; + + spin_lock_init(&ar_sdio->lock); + spin_lock_init(&ar_sdio->scat_lock); + spin_lock_init(&ar_sdio->wr_async_lock); + mutex_init(&ar_sdio->dma_buffer_mutex); + + INIT_LIST_HEAD(&ar_sdio->scat_req); + INIT_LIST_HEAD(&ar_sdio->bus_req_freeq); + INIT_LIST_HEAD(&ar_sdio->wr_asyncq); + + INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work); + + for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) + ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]); + + ar = ath6kl_core_alloc(&ar_sdio->func->dev); + if (!ar) { + ath6kl_err("Failed to alloc ath6kl core\n"); + ret = -ENOMEM; + goto err_dma; + } + + ar_sdio->ar = ar; + ar->hif_type = ATH6KL_HIF_TYPE_SDIO; + ar->hif_priv = ar_sdio; + ar->hif_ops = &ath6kl_sdio_ops; + ar->bmi.max_data_size = 256; +#ifdef CONFIG_ANDROID + if (PLAT_WOW_GPIO_PIN) + ar->wow_irq = gpio_to_irq(PLAT_WOW_GPIO_PIN); +#endif + + ath6kl_sdio_set_mbox_info(ar); + + ret = ath6kl_sdio_config(ar); + if (ret) { + ath6kl_err("Failed to config sdio: %d\n", ret); + goto err_core_alloc; + } + + ath6kl_htc_mbox_attach(ar); + ret = ath6kl_core_init(ar); + if (ret) { + ath6kl_err("Failed to init ath6kl core\n"); + goto err_core_alloc; + } + + return ret; + +err_core_alloc: + ath6kl_core_free(ar_sdio->ar); +err_dma: + kfree(ar_sdio->dma_buffer); +err_hif: + kfree(ar_sdio); + + return ret; +} + +static void ath6kl_sdio_remove(struct sdio_func *func) +{ + struct ath6kl_sdio *ar_sdio; + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "sdio removed func %d vendor 0x%x device 0x%x\n", + func->num, func->vendor, func->device); + + ar_sdio = sdio_get_drvdata(func); + + ath6kl_stop_txrx(ar_sdio->ar); + cancel_work_sync(&ar_sdio->wr_async_work); + + ath6kl_core_cleanup(ar_sdio->ar); + + kfree(ar_sdio->dma_buffer); + kfree(ar_sdio); +} + +static const struct sdio_device_id ath6kl_sdio_devices[] = { + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x2))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6006_BASE | 0x0))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6006_BASE | 0x1))}, + {}, +}; + +MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices); + +static struct sdio_driver ath6kl_sdio_driver = { + .name = "ath6kl_sdio", + .id_table = ath6kl_sdio_devices, + .probe = ath6kl_sdio_probe, + .remove = ath6kl_sdio_remove, + .drv.pm = ATH6KL_SDIO_PM_OPS, +}; + +extern int vos_chip_power_qca6234(int on); +static int __init ath6kl_sdio_init(void) +{ + int ret; + +#ifdef CONFIG_ANDROID + /*ath6kl_sdio_init_msm();*/ +#endif + vos_chip_power_qca6234(1); + ret = sdio_register_driver(&ath6kl_sdio_driver); + if (ret) + ath6kl_err("sdio driver registration failed: %d\n", ret); + + return ret; +} + +static void __exit ath6kl_sdio_exit(void) +{ + sdio_unregister_driver(&ath6kl_sdio_driver); +#ifdef CONFIG_ANDROID_8960_SDIO + ath6kl_sdio_exit_msm(); + vos_chip_power_qca6234(0); +#endif +} + +module_init(ath6kl_sdio_init); +module_exit(ath6kl_sdio_exit); + +MODULE_AUTHOR("Atheros Communications, Inc."); +MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_OTP_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_FW_DIR "/" AR6003_HW_2_0_PATCH_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_OTP_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_FW_DIR "/" AR6003_HW_2_1_1_PATCH_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_OTP_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_OTP_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_FW_DIR "/" AR6004_HW_1_2_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_FW_DIR "/" AR6004_HW_1_2_OTP_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_OTP_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_2_0_FW_DIR "/" AR6004_HW_2_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_2_0_FW_DIR "/" AR6004_HW_2_0_OTP_FILE); +MODULE_FIRMWARE(AR6004_HW_2_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_2_0_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6006_HW_1_0_FW_DIR "/" AR6006_HW_1_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6006_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6006_HW_1_0_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl-3.5/target.h b/drivers/net/wireless/ath/ath6kl-3.5/target.h new file mode 100644 index 000000000000..798a31aaee9b --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/target.h @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2004-2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 TARGET_H +#define TARGET_H + +#define AR6003_BOARD_DATA_SZ 1024 +#define AR6003_BOARD_EXT_DATA_SZ 768 + +#define AR6004_BOARD_DATA_SZ 6144 +#define AR6004_BOARD_EXT_DATA_SZ 0 + +#define AR6006_BOARD_DATA_SZ 6144 +#define AR6006_BOARD_EXT_DATA_SZ 0 + +#define RESET_CONTROL_ADDRESS 0x00000000 +#define RESET_CONTROL_COLD_RST 0x00000100 +#define RESET_CONTROL_MBOX_RST 0x00000004 + +#define CPU_CLOCK_STANDARD_S 0 +#define CPU_CLOCK_STANDARD 0x00000003 +#define CPU_CLOCK_ADDRESS 0x00000020 + +#define CLOCK_CONTROL_ADDRESS 0x00000028 +#define CLOCK_CONTROL_LF_CLK32_S 2 +#define CLOCK_CONTROL_LF_CLK32 0x00000004 + +#define SYSTEM_SLEEP_ADDRESS 0x000000c4 +#define SYSTEM_SLEEP_DISABLE_S 0 +#define SYSTEM_SLEEP_DISABLE 0x00000001 + +#define LPO_CAL_ADDRESS 0x000000e0 +#define LPO_CAL_ENABLE_S 20 +#define LPO_CAL_ENABLE 0x00100000 + +#define GPIO_PIN10_ADDRESS 0x00000050 +#define GPIO_PIN11_ADDRESS 0x00000054 +#define GPIO_PIN12_ADDRESS 0x00000058 +#define GPIO_PIN13_ADDRESS 0x0000005c + +#define HOST_INT_STATUS_ADDRESS 0x00000400 +#define HOST_INT_STATUS_ERROR_S 7 +#define HOST_INT_STATUS_ERROR 0x00000080 + +#define HOST_INT_STATUS_CPU_S 6 +#define HOST_INT_STATUS_CPU 0x00000040 + +#define HOST_INT_STATUS_COUNTER_S 4 +#define HOST_INT_STATUS_COUNTER 0x00000010 + +#define CPU_INT_STATUS_ADDRESS 0x00000401 + +#define ERROR_INT_STATUS_ADDRESS 0x00000402 +#define ERROR_INT_STATUS_WAKEUP_S 2 +#define ERROR_INT_STATUS_WAKEUP 0x00000004 + +#define ERROR_INT_STATUS_RX_UNDERFLOW_S 1 +#define ERROR_INT_STATUS_RX_UNDERFLOW 0x00000002 + +#define ERROR_INT_STATUS_TX_OVERFLOW_S 0 +#define ERROR_INT_STATUS_TX_OVERFLOW 0x00000001 + +#define COUNTER_INT_STATUS_ADDRESS 0x00000403 +#define COUNTER_INT_STATUS_COUNTER_S 0 +#define COUNTER_INT_STATUS_COUNTER 0x000000ff + +#define RX_LOOKAHEAD_VALID_ADDRESS 0x00000405 + +#define INT_STATUS_ENABLE_ADDRESS 0x00000418 +#define INT_STATUS_ENABLE_ERROR_S 7 +#define INT_STATUS_ENABLE_ERROR 0x00000080 + +#define INT_STATUS_ENABLE_CPU_S 6 +#define INT_STATUS_ENABLE_CPU 0x00000040 + +#define INT_STATUS_ENABLE_INT_S 5 +#define INT_STATUS_ENABLE_INT 0x00000020 +#define INT_STATUS_ENABLE_COUNTER_S 4 +#define INT_STATUS_ENABLE_COUNTER 0x00000010 + +#define INT_STATUS_ENABLE_MBOX_DATA_S 0 +#define INT_STATUS_ENABLE_MBOX_DATA 0x0000000f + +#define CPU_INT_STATUS_ENABLE_ADDRESS 0x00000419 +#define CPU_INT_STATUS_ENABLE_BIT_S 0 +#define CPU_INT_STATUS_ENABLE_BIT 0x000000ff + +#define ERROR_STATUS_ENABLE_ADDRESS 0x0000041a +#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_S 1 +#define ERROR_STATUS_ENABLE_RX_UNDERFLOW 0x00000002 + +#define ERROR_STATUS_ENABLE_TX_OVERFLOW_S 0 +#define ERROR_STATUS_ENABLE_TX_OVERFLOW 0x00000001 + +#define COUNTER_INT_STATUS_ENABLE_ADDRESS 0x0000041b +#define COUNTER_INT_STATUS_ENABLE_BIT_S 0 +#define COUNTER_INT_STATUS_ENABLE_BIT 0x000000ff + +#define COUNT_ADDRESS 0x00000420 + +#define COUNT_DEC_ADDRESS 0x00000440 + +#define WINDOW_DATA_ADDRESS 0x00000474 +#define WINDOW_WRITE_ADDR_ADDRESS 0x00000478 +#define WINDOW_READ_ADDR_ADDRESS 0x0000047c +#define CPU_DBG_SEL_ADDRESS 0x00000483 +#define CPU_DBG_ADDRESS 0x00000484 + +#define LOCAL_SCRATCH_ADDRESS 0x000000c0 +#define ATH6KL_OPTION_SLEEP_DISABLE 0x08 + +#define RTC_BASE_ADDRESS 0x00004000 +#define GPIO_BASE_ADDRESS 0x00014000 +#define MBOX_BASE_ADDRESS 0x00018000 +#define ANALOG_INTF_BASE_ADDRESS 0x0001c000 +#define WLAN_BOOTSTRAP_ADDRESS 0x00014140 + +#define BOOTSTRAP_IS_HSIC(v) ((v) & (1 << 10)) +#define BOOTSTRAP_IS_USB(v) ((v) & (1 << 11)) + +/* real name of the register is unknown */ +#define ATH6KL_ANALOG_PLL_REGISTER (ANALOG_INTF_BASE_ADDRESS + 0x284) + +#define SM(f, v) (((v) << f##_S) & f) +#define MS(f, v) (((v) & f) >> f##_S) + +/* + * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the + * host_interest structure. + * + * Host Interest is shared between Host and Target in order to coordinate + * between the two, and is intended to remain constant (with additions only + * at the end). + */ +#define ATH6KL_AR6003_HI_START_ADDR 0x00540600 +#define ATH6KL_AR6004_HI_START_ADDR 0x00400800 +#define ATH6KL_AR6006_HI_START_ADDR 0x00428800 + +/* + * These are items that the Host may need to access + * via BMI or via the Diagnostic Window. The position + * of items in this structure must remain constant. + * across firmware revisions! + * + * Types for each item must be fixed size across target and host platforms. + * The structure is used only to calculate offset for each register with + * HI_ITEM() macro, no values are stored to it. + * + * More items may be added at the end. + */ +struct host_interest { + /* + * Pointer to application-defined area, if any. + * Set by Target application during startup. + */ + u32 hi_app_host_interest; /* 0x00 */ + + /* Pointer to register dump area, valid after Target crash. */ + u32 hi_failure_state; /* 0x04 */ + + /* Pointer to debug logging header */ + u32 hi_dbglog_hdr; /* 0x08 */ + + u32 hi_unused1; /* 0x0c */ + + /* + * General-purpose flag bits, similar to ATH6KL_OPTION_* flags. + * Can be used by application rather than by OS. + */ + u32 hi_option_flag; /* 0x10 */ + + /* + * Boolean that determines whether or not to + * display messages on the serial port. + */ + u32 hi_serial_enable; /* 0x14 */ + + /* Start address of DataSet index, if any */ + u32 hi_dset_list_head; /* 0x18 */ + + /* Override Target application start address */ + u32 hi_app_start; /* 0x1c */ + + /* Clock and voltage tuning */ + u32 hi_skip_clock_init; /* 0x20 */ + u32 hi_core_clock_setting; /* 0x24 */ + u32 hi_cpu_clock_setting; /* 0x28 */ + u32 hi_system_sleep_setting; /* 0x2c */ + u32 hi_xtal_control_setting; /* 0x30 */ + u32 hi_pll_ctrl_setting_24ghz; /* 0x34 */ + u32 hi_pll_ctrl_setting_5ghz; /* 0x38 */ + u32 hi_ref_voltage_trim_setting; /* 0x3c */ + u32 hi_clock_info; /* 0x40 */ + + /* + * Flash configuration overrides, used only + * when firmware is not executing from flash. + * (When using flash, modify the global variables + * with equivalent names.) + */ + u32 hi_bank0_addr_value; /* 0x44 */ + u32 hi_bank0_read_value; /* 0x48 */ + u32 hi_bank0_write_value; /* 0x4c */ + u32 hi_bank0_config_value; /* 0x50 */ + + /* Pointer to Board Data */ + u32 hi_board_data; /* 0x54 */ + u32 hi_board_data_initialized; /* 0x58 */ + + u32 hi_dset_ram_index_tbl; /* 0x5c */ + + u32 hi_desired_baud_rate; /* 0x60 */ + u32 hi_dbglog_config; /* 0x64 */ + u32 hi_end_ram_reserve_sz; /* 0x68 */ + u32 hi_mbox_io_block_sz; /* 0x6c */ + + u32 hi_num_bpatch_streams; /* 0x70 -- unused */ + u32 hi_mbox_isr_yield_limit; /* 0x74 */ + + u32 hi_refclk_hz; /* 0x78 */ + u32 hi_ext_clk_detected; /* 0x7c */ + u32 hi_dbg_uart_txpin; /* 0x80 */ + u32 hi_dbg_uart_rxpin; /* 0x84 */ + u32 hi_hci_uart_baud; /* 0x88 */ + u32 hi_hci_uart_pin_assignments; /* 0x8C */ + /* + * NOTE: byte [0] = tx pin, [1] = rx pin, [2] = rts pin, [3] = cts + * pin + */ + u32 hi_hci_uart_baud_scale_val; /* 0x90 */ + u32 hi_hci_uart_baud_step_val; /* 0x94 */ + + u32 hi_allocram_start; /* 0x98 */ + u32 hi_allocram_sz; /* 0x9c */ + u32 hi_hci_bridge_flags; /* 0xa0 */ + u32 hi_hci_uart_support_pins; /* 0xa4 */ + /* + * NOTE: byte [0] = RESET pin (bit 7 is polarity), + * bytes[1]..bytes[3] are for future use + */ + u32 hi_hci_uart_pwr_mgmt_params; /* 0xa8 */ + /* + * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high + * [31:16]: wakeup timeout in ms + */ + + /* Pointer to extended board data */ + u32 hi_board_ext_data; /* 0xac */ + u32 hi_board_ext_data_config; /* 0xb0 */ + + /* + * Bit [0] : valid + * Bit[31:16: size + */ + /* + * hi_reset_flag is used to do some stuff when target reset. + * such as restore app_start after warm reset or + * preserve host Interest area, or preserve ROM data, literals etc. + */ + u32 hi_reset_flag; /* 0xb4 */ + /* indicate hi_reset_flag is valid */ + u32 hi_reset_flag_valid; /* 0xb8 */ + u32 hi_hci_uart_pwr_mgmt_params_ext; /* 0xbc */ + /* + * 0xbc - [31:0]: idle timeout in ms + */ + /* ACS flags */ + u32 hi_acs_flags; /* 0xc0 */ + u32 hi_console_flags; /* 0xc4 */ + u32 hi_nvram_state; /* 0xc8 */ + u32 hi_option_flag2; /* 0xcc */ + + /* If non-zero, override values sent to Host in WMI_READY event. */ + u32 hi_sw_version_override; /* 0xd0 */ + u32 hi_abi_version_override; /* 0xd4 */ + + /* + * Percentage of high priority RX traffic to total expected RX traffic - + * applicable only to ar6004 + */ + u32 hi_hp_rx_traffic_ratio; /* 0xd8 */ + + /* test applications flags */ + u32 hi_test_apps_related ; /* 0xdc */ + /* location of test script */ + u32 hi_ota_testscript; /* 0xe0 */ + /* location of CAL data */ + u32 hi_cal_data; /* 0xe4 */ + /* Number of packet log buffers */ + u32 hi_pktlog_num_buffers; /* 0xe8 */ + /* wow extension configuration */ + u32 hi_wow_ext_config; /* 0xec */ + /* power save flags */ + u32 hi_pwr_save_flags; /* 0xf0 */ + /* Spatial Multiplexing Power Save (SMPS) options */ + u32 hi_smps_options; /* 0xf4 */ + +} __packed; + +#define HI_ITEM(item) offsetof(struct host_interest, item) + +#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3 + +#define HI_OPTION_FW_MODE_IBSS 0x0 +#define HI_OPTION_FW_MODE_BSS_STA 0x1 +#define HI_OPTION_FW_MODE_AP 0x2 + +#define HI_OPTION_FW_SUBMODE_NONE 0x0 +#define HI_OPTION_FW_SUBMODE_P2PDEV 0x1 +#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 +#define HI_OPTION_FW_SUBMODE_P2PGO 0x3 + +#define HI_OPTION_DISABLE_DBGLOG 0x40 + +/* hi_option_flag2 options: Disable MAC address overwrite via OTP Feature */ +#define HI_OPTION_DISABLE_MAC_OTP 0x40 + +#define HI_OPTION_NUM_DEV_SHIFT 0x9 + +#define HI_OPTION_FW_BRIDGE_SHIFT 0x04 + +/* Disable RTT Feature */ +#define HI_OPTION_DISABLE_RTT 0x20 + +/* Disable p2p dedicate device */ +#define HI_OPTION_DISABLE_P2P_DEDICATE 0x100 + +/* Ext patch download finished after WMI ready*/ +#define HI_OPTION_EXT_FW_DOWNLOAD_DONE 0x80000 + +/*Enable cold reset when firmware crash*/ +#define HI_OPTION_FW_CRASH_COLD_RESET 0x100000 + +/*Enable watchdog*/ +#define HI_OPTION_FW_WATCHDOG_ENABLE 0x200000 + +/* Enable one shot noa */ +#define HI_OPTION_ONE_SHOT_NOA_ENABLE 0x2000000 + +/* Enable SB specific function */ +#define HI_OPTION_ENABLE_SB_SPECIFIC 0x4000000 + +/*Enable/Disable Wifi Heart Beat Feature*/ +#define HI_OPTION_ENABLE_WLAN_HB 0x10000000 + +/* Enable Multichannel Concurrency (MCC) - last bit to sync with mainline */ +#define HI_OPTION_MCC_ENABLE 0x80000000 + +/* Fw Mode/SubMode Mask +|------------------------------------------------------------------------------| +| SUB | SUB | SUB | SUB | | | | +| MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0| +| (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2) +|------------------------------------------------------------------------------| +*/ +#define HI_OPTION_FW_MODE_BITS 0x2 +#define HI_OPTION_FW_MODE_SHIFT 0xC + +#define HI_OPTION_FW_SUBMODE_BITS 0x2 +#define HI_OPTION_FW_SUBMODE_SHIFT 0x14 + +/*power save flag bit definitions*/ +#define HI_PWR_SAVE_LPL_ENABLED 0x1 + +/* power save mode - reduced power listen */ +#define HI_PWR_SAVE_LPL_MODE_RPL 2 +#define HI_PWR_SAVE_LPL_MODE_LSB 4 + +/* SM power save options */ +#define HI_SMPS_ALLOW_MASK 0x1 + +/* Convert a Target virtual address into a Target physical address */ +#define AR6003_VTOP(vaddr) ((vaddr) & 0x001fffff) +#define AR6004_VTOP(vaddr) (vaddr) +#define AR6006_VTOP(vaddr) (vaddr) + +#define TARG_VTOP(target_type, vaddr) \ + (((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \ + (((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : \ + (((target_type) == TARGET_TYPE_AR6006) ? AR6006_VTOP(vaddr) : 0))) + +#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500 + +struct ath6kl_dbglog_buf { + __le32 next; + __le32 buffer_addr; + __le32 bufsize; + __le32 length; + __le32 count; + __le32 free; +} __packed; + +struct ath6kl_dbglog_hdr { + __le32 dbuf_addr; + __le32 dropped; +} __packed; + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/testmode.c b/drivers/net/wireless/ath/ath6kl-3.5/testmode.c new file mode 100644 index 000000000000..6f15e76300f7 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/testmode.c @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "testmode.h" + +#include + +#include "core.h" + +/* + * netlink.h remove these macros from kernel 3.5. + * TODO : Error handle for nla_put_XXX calls. + */ +#ifndef NLA_PUT +#define NLA_PUT_U32 nla_put_u32 +#define NLA_PUT nla_put +#else +#define _NLA_PUT_ERR_RTN +#endif + +enum ath6kl_tm_attr { + __ATH6KL_TM_ATTR_INVALID = 0, + ATH6KL_TM_ATTR_CMD = 1, + ATH6KL_TM_ATTR_DATA = 2, + ATH6KL_TM_ATTR_TYPE = 3, + + /* keep last */ + __ATH6KL_TM_ATTR_AFTER_LAST, + ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1, +}; + +enum ath6kl_tm_cmd { + ATH6KL_TM_CMD_TCMD = 0, + ATH6KL_TM_CMD_WLAN_HB = 1, + ATH6KL_TM_CMD_WIFI_DISC = 2, + ATH6KL_TM_CMD_WIFI_KTK = 3, +}; + +#define ATH6KL_TM_DATA_MAX_LEN 5000 + +static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = { + [ATH6KL_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH6KL_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH6KL_TM_DATA_MAX_LEN }, +}; + +#ifdef CONFIG_NL80211_TESTMODE + +void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len) +{ + if (down_interruptible(&ar->sem)) + return; + + kfree(ar->tm.rx_report); + + ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL); + ar->tm.rx_report_len = buf_len; + + up(&ar->sem); + + wake_up(&ar->event_wq); +} + +void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len) +{ + struct sk_buff *skb; + + if (!buf || buf_len == 0) { + printk(KERN_ERR "buf buflen is empty\n"); + return; + } + + skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_ATOMIC); + + if (!skb) { + printk(KERN_ERR "failed to allocate testmode rx skb!\n"); + return; + } + + NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD); + NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf); + cfg80211_testmode_event(skb, GFP_ATOMIC); + return; + +#ifdef _NLA_PUT_ERR_RTN +nla_put_failure: + kfree_skb(skb); + printk(KERN_ERR "nla_put failed on testmode rx skb!\n"); +#endif + +} + +#ifdef ATH6KL_SUPPORT_WLAN_HB +void ath6kl_wlan_hb_event(struct ath6kl *ar, u8 value, + void *buf, size_t buf_len) +{ + struct sk_buff *skb; + + if (!buf || buf_len == 0) { + printk(KERN_ERR "buf buflen is empty\n"); + return; + } + + skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_ATOMIC); + + if (!skb) { + printk(KERN_ERR "failed to allocate testmode event skb!\n"); + return; + } + + NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_WLAN_HB); + NLA_PUT_U32(skb, ATH6KL_TM_ATTR_TYPE, value); + NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf); + cfg80211_testmode_event(skb, GFP_ATOMIC); + return; + +#ifdef _NLA_PUT_ERR_RTN +nla_put_failure: + kfree_skb(skb); + printk(KERN_ERR "nla_put failed on testmode event skb!\n"); +#endif +} +#endif + +#ifdef ATH6KL_SUPPORT_WIFI_DISC +void ath6kl_tm_disc_event(struct ath6kl *ar, void *buf, size_t buf_len) +{ + struct sk_buff *skb; + + if (!buf || buf_len == 0) { + printk(KERN_ERR "buf buflen is empty\n"); + return; + } + + skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_ATOMIC); + + if (!skb) { + printk(KERN_ERR "failed to allocate testmode event skb!\n"); + return; + } + + NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_WIFI_DISC); + NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf); + cfg80211_testmode_event(skb, GFP_ATOMIC); + return; + +#ifdef _NLA_PUT_ERR_RTN +nla_put_failure: + kfree_skb(skb); + printk(KERN_ERR "nla_put failed on testmode event skb!\n"); +#endif +} +#endif + +static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len, + struct sk_buff *skb) +{ + int ret = 0; + long left; + + if (!test_bit(WMI_READY, &ar->flag)) { + ret = -EIO; + goto out; + } + + if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { + ret = -EBUSY; + goto out; + } + if (down_interruptible(&ar->sem)) + return -EIO; + + if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) { + up(&ar->sem); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + ar->tm.rx_report != NULL, + WMI_TIMEOUT); + + + + if (left == 0) { + ret = -ETIMEDOUT; + goto out; + } else if (left < 0) { + ret = left; + goto out; + } + + if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) { + ret = -EINVAL; + goto out; + } + + NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len, + ar->tm.rx_report); + + kfree(ar->tm.rx_report); + ar->tm.rx_report = NULL; + +out: + up(&ar->sem); + + return ret; + +#ifdef _NLA_PUT_ERR_RTN +nla_put_failure: + ret = -ENOBUFS; + goto out; +#endif +} +EXPORT_SYMBOL(ath6kl_tm_rx_report); + +#ifdef ATH6KL_SUPPORT_WLAN_HB +enum nl80211_wlan_hb_cmd { + NL80211_WLAN_HB_ENABLE = 0, + NL80211_WLAN_TCP_PARAMS = 1, + NL80211_WLAN_TCP_FILTER = 2, + NL80211_WLAN_UDP_PARAMS = 3, + NL80211_WLAN_UDP_FILTER = 4, +}; + +#define WLAN_HB_UDP 0x1 +#define WLAN_HB_TCP 0x2 + +struct wlan_hb_params { + u16 cmd; + u16 dummy; + + union { + struct { + u8 enable; + u8 item; + u8 ses; + } hb_p; + + struct { + u32 srv_ip; + u32 dev_ip; + u16 src_port; + u16 dst_port; + u16 timeout; + u8 session; + u8 gateway_mac[ETH_ALEN]; + } tcp_params; + + struct { + u16 length; + u8 offset; + u8 session; + u8 filter[64]; + } tcp_filter; + + struct { + u32 srv_ip; + u32 dev_ip; + u16 src_port; + u16 dst_port; + u16 interval; + u16 timeout; + u8 session; + u8 gateway_mac[ETH_ALEN]; + } udp_params; + + struct { + u16 length; + u8 offset; + u8 session; + u8 filter[64]; + } udp_filter; + + } params; +}; +#endif + +#ifdef ATH6KL_SUPPORT_WIFI_DISC +#define WLAN_WIFI_DISC_MAX_IE_SIZE 200 + +enum nl80211_wifi_disc_cmd { + NL80211_WIFI_DISC_IE = 0, + NL80211_WIFI_DISC_IE_FILTER = 1, + NL80211_WIFI_DISC_START = 2, + NL80211_WIFI_DISC_STOP = 3, +}; + +struct wifi_disc_params { + u16 cmd; + + union { + struct { + u16 length; + u8 ie[WLAN_WIFI_DISC_MAX_IE_SIZE]; + } ie_params; + + struct { + u16 enable; + u16 startPos; + u16 length; + u8 filter[WLAN_WIFI_DISC_MAX_IE_SIZE]; + } ie_filter_params; + + struct { + u16 channel; + u16 dwellTime; + u16 sleepTime; + u16 random; + u16 numPeers; + u16 peerTimeout; + u16 txPower; + } start_params; + } params; +}; +#endif + +#ifdef ATH6KL_SUPPORT_WIFI_KTK +#define WLAN_WIFI_KTK_MAX_IE_SIZE 200 + +enum nl80211_wifi_ktk_cmd { + NL80211_WIFI_KTK_IE = 0, + NL80211_WIFI_KTK_IE_FILTER = 1, + NL80211_WIFI_KTK_START = 2, + NL80211_WIFI_KTK_STOP = 3, +}; + +struct wifi_ktk_params { + u16 cmd; + union { + struct { + u16 length; + u8 ie[WLAN_WIFI_KTK_MAX_IE_SIZE]; + } ie_params; + + struct { + u16 enable; + u16 startPos; + u16 length; + u8 filter[WLAN_WIFI_KTK_MAX_IE_SIZE]; + } ie_filter_params; + + struct { + u16 ssid_len; + u8 ssid[32]; + u8 passphrase[16]; + } start_params; + } params; +}; +#endif + +int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; + int err, buf_len; + void *buf; + + err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, + ath6kl_tm_policy); + + if (err) + return err; + + if (!tb[ATH6KL_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) { + case ATH6KL_TM_CMD_TCMD: + if (!tb[ATH6KL_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); + + ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len); + + return 0; + + break; +#ifdef ATH6KL_SUPPORT_WLAN_HB + case ATH6KL_TM_CMD_WLAN_HB: + { + struct wlan_hb_params *hb_params; + struct ath6kl_vif *vif; + + vif = ath6kl_vif_first(ar); + + if (!vif) + return -EINVAL; + + if (!tb[ATH6KL_TM_ATTR_DATA]) { + printk(KERN_ERR "%s: NO DATA\n", __func__); + return -EINVAL; + } + + buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); + + hb_params = (struct wlan_hb_params *)buf; + + if (hb_params->cmd == NL80211_WLAN_HB_ENABLE) { + if (hb_params->params.hb_p.enable != 0) { + + if (ath6kl_enable_wow_hb(ar)) { + printk(KERN_ERR + "%s: enable hb wow fail\n", + __func__); + return -EINVAL; + } + + if (hb_params->params.hb_p.item == + WLAN_HB_TCP) { + if (ath6kl_wmi_set_heart_beat_params( + ar->wmi, + vif->fw_vif_idx, + 1, + WLAN_HB_TCP, + hb_params->params.hb_p.ses)) { + printk(KERN_ERR + "%s: set heart beat enable fail\n", + __func__); + return -EINVAL; + } + } else if (hb_params->params.hb_p.item == + WLAN_HB_UDP) { + if (ath6kl_wmi_set_heart_beat_params( + ar->wmi, + vif->fw_vif_idx, + 1, + WLAN_HB_UDP, + hb_params->params.hb_p.ses)) { + printk(KERN_ERR + "%s: set heart beat enable fail\n", + __func__); + return -EINVAL; + } + } + } else { +#ifdef CONFIG_ANDROID + if (ath6kl_android_enable_wow_default(ar)) { + printk(KERN_ERR + "%s: enable android defualt wow fail\n", + __func__); + } +#endif + if (hb_params->params.hb_p.item == + WLAN_HB_TCP) { + if (ath6kl_wmi_set_heart_beat_params( + ar->wmi, + vif->fw_vif_idx, + 0, + WLAN_HB_TCP, + hb_params->params.hb_p.ses)) { + printk(KERN_ERR + "%s: set heart beat disable fail\n", + __func__); + return -EINVAL; + } + } else if (hb_params->params.hb_p.item == + WLAN_HB_UDP) { + if (ath6kl_wmi_set_heart_beat_params( + ar->wmi, + vif->fw_vif_idx, + 0, + WLAN_HB_UDP, + hb_params->params.hb_p.ses)) { + printk(KERN_ERR + "%s: set heart beat disable fail\n", + __func__); + return -EINVAL; + } + } + } + } else if (hb_params->cmd == NL80211_WLAN_TCP_PARAMS) { + if (ath6kl_wmi_heart_beat_set_tcp_params(ar->wmi, + vif->fw_vif_idx, + hb_params->params.tcp_params.src_port, + hb_params->params.tcp_params.dst_port, + hb_params->params.tcp_params.srv_ip, + hb_params->params.tcp_params.dev_ip, + hb_params->params.tcp_params.timeout, + hb_params->params.tcp_params.session, + hb_params->params.tcp_params.gateway_mac)) { + printk(KERN_ERR + "%s: set heart beat tcp params fail\n", + __func__); + return -EINVAL; + } + + } else if (hb_params->cmd == NL80211_WLAN_TCP_FILTER) { + if (hb_params->params.tcp_filter.length + > WMI_MAX_TCP_FILTER_SIZE) { + printk(KERN_ERR "%s: size of tcp filter is too large: %d\n", + __func__, + hb_params->params.tcp_filter.length); + return -E2BIG; + } + + if (ath6kl_wmi_heart_beat_set_tcp_filter(ar->wmi, + vif->fw_vif_idx, + hb_params->params.tcp_filter.filter, + hb_params->params.tcp_filter.length, + hb_params->params.tcp_filter.offset, + hb_params->params.tcp_filter.session)) { + printk(KERN_ERR + "%s: set heart beat tcp filter fail\n", + __func__); + return -EINVAL; + } + } else if (hb_params->cmd == NL80211_WLAN_UDP_PARAMS) { + if (ath6kl_wmi_heart_beat_set_udp_params(ar->wmi, + vif->fw_vif_idx, + hb_params->params.udp_params.src_port, + hb_params->params.udp_params.dst_port, + hb_params->params.udp_params.srv_ip, + hb_params->params.udp_params.dev_ip, + hb_params->params.udp_params.interval, + hb_params->params.udp_params.timeout, + hb_params->params.udp_params.session, + hb_params->params.udp_params.gateway_mac)) { + printk(KERN_ERR + "%s: set heart beat udp params fail\n", + __func__); + return -EINVAL; + } + } else if (hb_params->cmd == NL80211_WLAN_UDP_FILTER) { + if (hb_params->params.udp_filter.length + > WMI_MAX_UDP_FILTER_SIZE) { + printk(KERN_ERR + "%s: size of udp filter is too large: %d\n", + __func__, + hb_params->params.udp_filter.length); + return -E2BIG; + } + + if (ath6kl_wmi_heart_beat_set_udp_filter(ar->wmi, + vif->fw_vif_idx, + hb_params->params.udp_filter.filter, + hb_params->params.udp_filter.length, + hb_params->params.udp_filter.offset, + hb_params->params.udp_filter.session)) { + printk(KERN_ERR + "%s: set heart beat udp filter fail\n", + __func__); + return -EINVAL; + } + } + } + + return 0; + break; +#endif +#ifdef ATH6KL_SUPPORT_WIFI_DISC + case ATH6KL_TM_CMD_WIFI_DISC: + { + struct wifi_disc_params *disc_params; + struct ath6kl_vif *vif; + + vif = ath6kl_vif_first(ar); + + if (!vif) + return -EINVAL; + + if (!tb[ATH6KL_TM_ATTR_DATA]) { + printk(KERN_ERR "%s: NO DATA\n", __func__); + return -EINVAL; + } + + buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); + + disc_params = (struct wifi_disc_params *)buf; + + if (disc_params->cmd == NL80211_WIFI_DISC_IE) { + u8 ie_hdr[6] = {0xDD, 0x00, 0x00, 0x03, 0x7f, 0x00}; + u8 *ie = NULL; + u16 ie_length = disc_params->params.ie_params.length; + + ie = kmalloc(ie_length+6, GFP_KERNEL); + if (ie == NULL) + return -ENOMEM; + + memcpy(ie, ie_hdr, 6); + ie[1] = ie_length+4; + memcpy(ie+6, + disc_params->params.ie_params.ie, + ie_length); + + if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_PROBE_REQ, ie, ie_length+6)) { + kfree(ie); + printk(KERN_ERR + "%s: wifi discovery set probe request ie fail\n", + __func__); + return -EINVAL; + } + + if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_PROBE_RESP, ie, ie_length+6)) { + kfree(ie); + printk(KERN_ERR + "%s: wifi discovery set probe response ie fail\n", + __func__); + return -EINVAL; + } + + kfree(ie); + } else if (disc_params->cmd == NL80211_WIFI_DISC_IE_FILTER) { + if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx, + disc_params->params.ie_filter_params.enable, + disc_params->params.ie_filter_params.startPos, + disc_params->params.ie_filter_params.filter, + disc_params->params.ie_filter_params.length)) { + printk(KERN_ERR + "%s: wifi discovery set ie filter fail\n", + __func__); + return -EINVAL; + } + } else if (disc_params->cmd == NL80211_WIFI_DISC_START) { + int band, freq, numPeers, random; + + if (disc_params->params.start_params.channel <= 14) + band = IEEE80211_BAND_2GHZ; + else + band = IEEE80211_BAND_5GHZ; + + freq = ieee80211_channel_to_frequency( + disc_params->params.start_params.channel, band); + if (!freq) { + printk(KERN_ERR "%s: wifi discovery start channel %d error\n", + __func__, + disc_params->params.start_params.channel); + return -EINVAL; + } + + if (disc_params->params.start_params.numPeers == 0) + numPeers = 1; + else if (disc_params->params.start_params.numPeers > 4) + numPeers = 4; + else + numPeers = + disc_params->params.start_params.numPeers; + + random = (disc_params->params.start_params.random == 0) + ? 100 : disc_params->params.start_params.random; + + if (disc_params->params.start_params.txPower) + ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, + vif->fw_vif_idx, + disc_params->params.start_params.txPower); + + /* disable scanning */ + ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + 0xFFFF, 0, 0, + 0, 0, 0, 0, 0, 0, 0); + + if (ath6kl_wmi_disc_mode_cmd(ar->wmi, + vif->fw_vif_idx, 1, freq, + disc_params->params.start_params.dwellTime, + disc_params->params.start_params.sleepTime, + random, numPeers, + disc_params->params.start_params.peerTimeout + )) { + printk(KERN_ERR "%s: wifi discovery start fail\n", + __func__); + return -EINVAL; + } + + /* change disc state to active */ + ar->disc_active = true; + } else if (disc_params->cmd == NL80211_WIFI_DISC_STOP) { + /* change disc state to inactive */ + ar->disc_active = false; + if (ath6kl_wmi_disc_mode_cmd(ar->wmi, vif->fw_vif_idx, + 0, 0, 0, 0, 0, 0, 0)) { + printk(KERN_ERR "%s: wifi discovery stop fail\n", + __func__); + return -EINVAL; + } + } + } + + return 0; + break; +#endif + +#ifdef ATH6KL_SUPPORT_WIFI_KTK + case ATH6KL_TM_CMD_WIFI_KTK: + { + struct wifi_ktk_params *ktk_params; + struct ath6kl_vif *vif; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EINVAL; + + if (!tb[ATH6KL_TM_ATTR_DATA]) { + printk(KERN_ERR "%s: NO DATA\n", __func__); + return -EINVAL; + } + + buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); + + ktk_params = (struct wifi_ktk_params *)buf; + + if (ktk_params->cmd == NL80211_WIFI_KTK_IE) { + u8 ie_hdr[6] = {0xDD, 0x00, 0x00, 0x03, 0x7f, 0x00}; + u8 *ie = NULL; + u16 ie_length = ktk_params->params.ie_params.length; + + ie = kmalloc(ie_length+6, GFP_KERNEL); + if (ie == NULL) + return -ENOMEM; + + memcpy(ie, ie_hdr, 6); + ie[1] = ie_length+4; + memcpy(ie+6, ktk_params->params.ie_params.ie, + ie_length); + + if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_PROBE_RESP, + ie, + ie_length+6)) { + kfree(ie); + printk(KERN_ERR + "%s: wifi ktk set probe response ie fail\n", + __func__); + return -EINVAL; + } + + if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, + WMI_FRAME_BEACON, ie, ie_length+6)) { + kfree(ie); + printk(KERN_ERR + "%s: wifi ktk set beacon ie fail\n", + __func__); + return -EINVAL; + } + + kfree(ie); + } else if (ktk_params->cmd == NL80211_WIFI_KTK_IE_FILTER) { + if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx, + ktk_params->params.ie_filter_params.enable, + ktk_params->params.ie_filter_params.startPos, + ktk_params->params.ie_filter_params.filter, + ktk_params->params.ie_filter_params.length)) { + printk(KERN_ERR + "%s: wifi ktk set ie filter fail\n", + __func__); + return -EINVAL; + } + } else if (ktk_params->cmd == NL80211_WIFI_KTK_START) { + ar->ktk_active = true; + + /* Clear the legacy ie pattern and filter */ + if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx, + 0, + 0, + NULL, + 0)) { + printk(KERN_ERR "%s: wifi ktk clear ie filter fail\n", + __func__); + return -EINVAL; + } + + memcpy(ar->ktk_passphrase, + ktk_params->params.start_params.passphrase, + 16); + + if (ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, + 1, SPECIFIC_SSID_FLAG, + ktk_params->params.start_params.ssid_len, + ktk_params->params.start_params.ssid)) { + printk(KERN_ERR "%s: wifi ktk set probedssid fail\n", + __func__); + return -EINVAL; + } + + if (ath6kl_wmi_ibss_pm_caps_cmd(ar->wmi, + vif->fw_vif_idx, + ADHOC_PS_KTK, + 5, + 10, + 10)) { + printk(KERN_ERR "%s: wifi ktk set power save mode on fail\n", + __func__); + return -EINVAL; + } + } else if (ktk_params->cmd == NL80211_WIFI_KTK_STOP) { + ar->ktk_active = false; + + if (ath6kl_wmi_ibss_pm_caps_cmd(ar->wmi, + vif->fw_vif_idx, + ADHOC_PS_DISABLE, + 0, + 0, + 0)) { + printk(KERN_ERR "%s: wifi ktk set power save mode off fail\n", + __func__); + return -EINVAL; + } + } + } + + return 0; + break; +#endif + default: + return -EOPNOTSUPP; + } +} + +#endif /* CONFIG_NL80211_TESTMODE */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/testmode.h b/drivers/net/wireless/ath/ath6kl-3.5/testmode.h new file mode 100644 index 000000000000..e7f900b2ff09 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/testmode.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" + +#ifdef CONFIG_NL80211_TESTMODE + +void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len); +void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len); +int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len); +void ath6kl_wlan_hb_event(struct ath6kl *ar, u8 value, void *buf, + size_t buf_len); +void ath6kl_tm_disc_event(struct ath6kl *ar, void *buf, size_t buf_len); + +#else + +static inline void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, + size_t buf_len) +{ +} + +static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) +{ + return 0; +} + +static inline void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, + size_t buf_len) +{ +} + +static inline void ath6kl_wlan_hb_event(struct ath6kl *ar, u8 value, void *buf, + size_t buf_len) +{ +} + +static inline void ath6kl_tm_disc_event(struct ath6kl *ar, void *buf, + size_t buf_len) +{ +} + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/txrx.c b/drivers/net/wireless/ath/ath6kl-3.5/txrx.c new file mode 100644 index 000000000000..d03fbd1cca83 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/txrx.c @@ -0,0 +1,3379 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "debug.h" +#include "htc-ops.h" +#include "epping.h" +#include +#include + +/* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */ +static const u8 up_to_ac[] = { + WMM_AC_BE, + WMM_AC_BK, + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO, +}; + +static int aggr_tx(struct ath6kl_vif *vif, struct ath6kl_sta *sta, + struct sk_buff **skb); +static int aggr_tx_flush(struct ath6kl_vif *vif, struct ath6kl_sta *conn); + +static u8 ath6kl_ibss_map_epid(struct sk_buff *skb, struct net_device *dev, + u32 *map_no) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ethhdr *eth_hdr; + u32 i, ep_map = -1; + u8 *datap; + + *map_no = 0; + datap = skb->data; + eth_hdr = (struct ethhdr *) (datap + sizeof(struct wmi_data_hdr)); + + if (is_multicast_ether_addr(eth_hdr->h_dest)) + return ENDPOINT_2; + + for (i = 0; i < ar->node_num; i++) { + if (memcmp(eth_hdr->h_dest, ar->node_map[i].mac_addr, + ETH_ALEN) == 0) { + *map_no = i + 1; + ar->node_map[i].tx_pend++; + return ar->node_map[i].ep_id; + } + + if ((ep_map == -1) && !ar->node_map[i].tx_pend) + ep_map = i; + } + + if (ep_map == -1) { + ep_map = ar->node_num; + ar->node_num++; + if (ar->node_num > MAX_NODE_NUM) + return ENDPOINT_UNUSED; + } + + memcpy(ar->node_map[ep_map].mac_addr, eth_hdr->h_dest, ETH_ALEN); + + for (i = ENDPOINT_2; i <= ENDPOINT_5; i++) { + if (!ar->tx_pending[i]) { + ar->node_map[ep_map].ep_id = i; + break; + } + + /* + * No free endpoint is available, start redistribution on + * the inuse endpoints. + */ + if (i == ENDPOINT_5) { + ar->node_map[ep_map].ep_id = ar->next_ep_id; + ar->next_ep_id++; + if (ar->next_ep_id > ENDPOINT_5) + ar->next_ep_id = ENDPOINT_2; + } + } + + *map_no = ep_map + 1; + ar->node_map[ep_map].tx_pend++; + + return ar->node_map[ep_map].ep_id; +} + +static inline bool _powersave_ap_tx_multicast(struct ath6kl_vif *vif, + struct sk_buff *skb, u32 *flags) +{ + u8 ctr = 0; + bool q_mcast = false, ps_queued = false; + int ret; + + for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { + if (vif->sta_list[ctr].sta_flags & STA_PS_SLEEP) { + q_mcast = true; + break; + } + } + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: Multicast %d psq_mcast %d\n", + __func__, + q_mcast, + !ath6kl_ps_queue_empty(&vif->psq_mcast)); + + if (q_mcast) { + /* + * If this transmit is not because of a Dtim Expiry + * q it. + */ + if (!test_bit(DTIM_EXPIRED, &vif->flags)) { + bool is_psq_empty = false; + + spin_lock_bh(&vif->psq_mcast_lock); + is_psq_empty = ath6kl_ps_queue_empty(&vif->psq_mcast); + ret = ath6kl_ps_queue_enqueue_data( + &vif->psq_mcast, skb); + spin_unlock_bh(&vif->psq_mcast_lock); + + if (ret == 0) { + /* + * If this is the first Mcast pkt getting + * queued indicate to the target to set the + * BitmapControl LSB of the TIM IE. + */ + if (is_psq_empty) + ath6kl_wmi_set_pvb_cmd(vif->ar->wmi, + vif->fw_vif_idx, + MCAST_AID, + 1); + } else { + /* drop this packet */ + dev_kfree_skb(skb); + } + ps_queued = true; + } else { + /* + * This transmit is because of Dtim expiry. + * Determine if MoreData bit has to be set. + */ + spin_lock_bh(&vif->psq_mcast_lock); + if (!ath6kl_ps_queue_empty(&vif->psq_mcast)) + *flags |= WMI_DATA_HDR_FLAGS_MORE; + spin_unlock_bh(&vif->psq_mcast_lock); + } + } + + return ps_queued; +} + +static inline void __powersave_ap_tx_unicast_sleep(struct ath6kl_vif *vif, + struct ath6kl_sta *conn, struct sk_buff *skb, u32 *flags) +{ + struct ethhdr *datap = (struct ethhdr *) skb->data; + bool trigger = false, is_psq_empty = false; + int ret; + + if (conn->apsd_info) { + u8 up = 0; + u8 traffic_class; + + if (test_bit(WMM_ENABLED, &vif->flags)) { + struct ath6kl_llc_snap_hdr *llc_hdr; + u16 ether_type; + u16 ip_type = IP_ETHERTYPE; + u8 *ip_hdr; + + ether_type = datap->h_proto; + if (is_ethertype(be16_to_cpu(ether_type))) { + /* packet is in DIX format */ + ip_hdr = (u8 *)(datap + 1); + } else { + /* packet is in 802.3 format */ + llc_hdr = (struct ath6kl_llc_snap_hdr *) + (datap + 1); + ether_type = llc_hdr->eth_type; + ip_hdr = (u8 *)(llc_hdr + 1); + } + + if (ether_type == cpu_to_be16(ip_type)) + up = ath6kl_wmi_determine_user_priority(ip_hdr, + 0); + } + traffic_class = up_to_ac[up & 0x7]; + if (conn->apsd_info & (1 << traffic_class)) + trigger = true; + } + + /* Queue the frames if the STA is sleeping */ + spin_lock_bh(&conn->lock); + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: Unicast aid %d sta_flags %x apsd_info %d" + " psq_data %d psq_mgmt %d traffic %d\n", + __func__, + conn->aid, + conn->sta_flags, + conn->apsd_info, + !ath6kl_ps_queue_empty(&conn->psq_data), + !ath6kl_ps_queue_empty(&conn->psq_mgmt), + trigger); + + is_psq_empty = ath6kl_ps_queue_empty(&conn->psq_data) && + ath6kl_ps_queue_empty(&conn->psq_mgmt); + + ret = ath6kl_ps_queue_enqueue_data(&conn->psq_data, skb); + spin_unlock_bh(&conn->lock); + + if (ret == 0) { + if (is_psq_empty) { + if (trigger) + ath6kl_wmi_set_apsd_buffered_traffic_cmd( + vif->ar->wmi, + vif->fw_vif_idx, + conn->aid, + 1, + 0); + + /* + * If this is the first pkt getting quened for this STA, + * update the PVB for this STA. + */ + ath6kl_wmi_set_pvb_cmd(vif->ar->wmi, + vif->fw_vif_idx, + conn->aid, + 1); + } + } else { + /* drop this packet */ + dev_kfree_skb(skb); + } + + return; +} + +static inline void __powersave_ap_tx_unicast_awake(struct ath6kl_vif *vif, + struct ath6kl_sta *conn, u32 *flags) +{ + /* + * This tx is because of a PsPoll or trigger. + * Determine if MoreData bit has to be set + */ + spin_lock_bh(&conn->lock); + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: Unicast aid %d sta_flags %x apsd_info %d" + " psq_data %d psq_mgmt %d\n", + __func__, + conn->aid, + conn->sta_flags, + conn->apsd_info, + !ath6kl_ps_queue_empty(&conn->psq_data), + !ath6kl_ps_queue_empty(&conn->psq_mgmt)); + + if (!ath6kl_ps_queue_empty(&conn->psq_data) || + !ath6kl_ps_queue_empty(&conn->psq_mgmt)) + *flags |= WMI_DATA_HDR_FLAGS_MORE; + + if (!(conn->sta_flags & STA_PS_POLLED)) { + /* + * This tx is because of a uAPSD trigger, determine more and + * EOSP bit. Set EOSP is queue is empty or sufficient frames is + * delivered for this trigger + */ + *flags |= WMI_DATA_HDR_FLAGS_TRIGGERED; + + if (conn->sta_flags & STA_PS_APSD_EOSP) + *flags |= WMI_DATA_HDR_FLAGS_EOSP; + } else + *flags |= WMI_DATA_HDR_FLAGS_PSPOLLED; + spin_unlock_bh(&conn->lock); + + return; +} + +static inline bool _powersave_ap_tx_unicast(struct ath6kl_vif *vif, + struct sk_buff *skb, u32 *flags, struct ath6kl_sta **sta) +{ + struct ethhdr *datap = (struct ethhdr *) skb->data; + struct ath6kl_sta *conn = NULL; + bool ps_queued = false; + + conn = ath6kl_find_sta(vif, datap->h_dest); + if (!conn) { + dev_kfree_skb(skb); + + /* Inform the caller that the skb is consumed */ + return true; + } + + *sta = conn; + + if (conn->sta_flags & STA_PS_SLEEP) { + if (!((conn->sta_flags & STA_PS_POLLED) || + (conn->sta_flags & STA_PS_APSD_TRIGGER))) { + __powersave_ap_tx_unicast_sleep(vif, conn, skb, flags); + ps_queued = true; + } else + __powersave_ap_tx_unicast_awake(vif, conn, flags); + } + + return ps_queued; +} + +static bool ath6kl_powersave_ap(struct ath6kl_vif *vif, struct sk_buff *skb, + u32 *flags, + struct ath6kl_sta **sta) +{ + struct ethhdr *datap = (struct ethhdr *) skb->data; + bool ps_queued = false; + + if (is_multicast_ether_addr(datap->h_dest)) + ps_queued = _powersave_ap_tx_multicast(vif, skb, flags); + else + ps_queued = _powersave_ap_tx_unicast(vif, skb, flags, sta); + + return ps_queued; +} + +bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, + u32 id, + u32 freq, + u32 wait, + const u8 *buf, + size_t len, + bool no_cck, + bool dont_wait_for_ack, + u32 *flags) +{ + struct ieee80211_mgmt *mgmt; + struct ath6kl_sta *conn = NULL; + bool ps_queued = false, is_psq_empty = false; + int ret; + + mgmt = (struct ieee80211_mgmt *)buf; + if (is_multicast_ether_addr(mgmt->da)) { + return false; + } else { + conn = ath6kl_find_sta(vif, mgmt->da); + if (!conn) + return false; + + if (conn->sta_flags & STA_PS_SLEEP) { + if (!(conn->sta_flags & STA_PS_POLLED)) { + /* Queue the frames if the STA is sleeping */ + spin_lock_bh(&conn->lock); + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: Mgmt aid %d sta_flags %x psq_data %d psq_mgmt %d\n", + __func__, conn->aid, conn->sta_flags, + !ath6kl_ps_queue_empty(&conn->psq_data), + !ath6kl_ps_queue_empty(&conn->psq_mgmt) + ); + + is_psq_empty = ath6kl_ps_queue_empty( + &conn->psq_data) && + ath6kl_ps_queue_empty(&conn->psq_mgmt); + + ret = ath6kl_ps_queue_enqueue_mgmt( + &conn->psq_mgmt, + buf, + len, + id, + freq, + wait, + no_cck, + dont_wait_for_ack); + spin_unlock_bh(&conn->lock); + + if (ret == 0) { + /* + * If this is the first pkt getting + * queued for this STA, update the PVB + * for this STA. + */ + if (is_psq_empty) + ath6kl_wmi_set_pvb_cmd( + vif->ar->wmi, + vif->fw_vif_idx, + conn->aid, 1); + } else { + ; + } + ps_queued = true; + } else { + /* + * This tx is because of a PsPoll. + * Determine if MoreData bit has to be set. + */ + spin_lock_bh(&conn->lock); + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: Mgmt aid %d sta_flags %x psq_data %d psq_mgmt %d\n", + __func__, conn->aid, conn->sta_flags, + !ath6kl_ps_queue_empty(&conn->psq_data), + !ath6kl_ps_queue_empty(&conn->psq_mgmt) + ); + + if (!ath6kl_ps_queue_empty(&conn->psq_data) || + !ath6kl_ps_queue_empty(&conn->psq_mgmt)) + *flags |= WMI_DATA_HDR_FLAGS_MORE; + spin_unlock_bh(&conn->lock); + } + } + } + + return ps_queued; +} + +static inline void __ath6kl_arp_parse_offload(struct ath6kl_vif *vif, + struct sk_buff *skb) +{ + struct ath6kl *ar = vif->ar; + struct ethhdr *eth_hdr = (struct ethhdr *) skb->data; + struct ath6kl_llc_snap_hdr *llc_hdr; + struct arphdr *arp_hdr; + unsigned char *ar_sip; + unsigned char src_ip[4]; + u16 ether_type; + int hdr_size; + + if (is_ethertype(be16_to_cpu(eth_hdr->h_proto))) { + /* packet is in DIX format */ + ether_type = eth_hdr->h_proto; + hdr_size = sizeof(struct ethhdr); + } else { + /* packet is in 802.3 format */ + llc_hdr = (struct ath6kl_llc_snap_hdr *)(skb->data + + sizeof(struct ethhdr)); + ether_type = llc_hdr->eth_type; + hdr_size = sizeof(struct ethhdr) + + sizeof(struct ath6kl_llc_snap_hdr); + } + + if (ether_type == htons(ETH_P_ARP)) { + arp_hdr = (struct arphdr *)(skb->data + hdr_size); + ar_sip = skb->data + hdr_size + + sizeof(struct arphdr) + ETH_ALEN; + + if (arp_hdr->ar_op == htons(ARPOP_REPLY)) { + memcpy(src_ip, ar_sip, 4); + + vif->arp_offload_ip_set = 0; + if (!ath6kl_wmi_set_arp_offload_ip_cmd(ar->wmi, + src_ip)) { + vif->arp_offload_ip_set = 1; + ath6kl_dbg(ATH6KL_DBG_WOWLAN, + "%s: enable arp offload %d.%d.%d.%d\n", + __func__, src_ip[0], src_ip[1] + , src_ip[2], src_ip[3]); + } + } + } +} + +/* Tx functions */ + +int ath6kl_control_tx(void *devt, struct sk_buff *skb, + enum htc_endpoint_id eid) +{ + struct ath6kl *ar = devt; + int status = 0; + struct ath6kl_cookie *cookie = NULL; + + spin_lock_bh(&ar->lock); + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX, + "%s: skb=0x%p, len=0x%x eid =%d\n", __func__, + skb, skb->len, eid); + + if (test_bit(WMI_CTRL_EP_FULL, &ar->flag) && (eid == ar->ctrl_ep)) { + /* + * Control endpoint is full, don't allocate resources, we + * are just going to drop this packet. + */ + cookie = NULL; + ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n", + skb, skb->len); + } else + cookie = ath6kl_alloc_cookie(ar, COOKIE_TYPE_CTRL); + + if (cookie == NULL) { + spin_unlock_bh(&ar->lock); + status = -ENOMEM; + goto fail_ctrl_tx; + } + + ar->tx_pending[eid]++; + + if (eid != ar->ctrl_ep) + ar->total_tx_data_pend++; + + spin_unlock_bh(&ar->lock); + + cookie->skb = skb; + cookie->map_no = 0; + set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, + eid, ATH6KL_CONTROL_PKT_TAG); + cookie->htc_pkt.skb = skb; + + /* P2P Flowctrl */ + if (ar->conf_flags & ATH6KL_CONF_ENABLE_FLOWCTRL) { + cookie->htc_pkt.connid = ATH6KL_P2P_FLOWCTRL_NULL_CONNID; + cookie->htc_pkt.recycle_count = 0; + } + + /*null data's vif since it is not applied */ + cookie->htc_pkt.vif = NULL; + + /* + * This interface is asynchronous, if there is an error, cleanup + * will happen in the TX completion callback. + */ + ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt); + + return 0; + +fail_ctrl_tx: + dev_kfree_skb(skb); + return status; +} + +int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev, + bool bypass_tx_aggr) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_cookie *cookie = NULL; + enum htc_endpoint_id eid = ENDPOINT_UNUSED; + struct ath6kl_vif *vif = netdev_priv(dev); + u32 map_no = 0; + u16 htc_tag = ATH6KL_DATA_PKT_TAG; + u8 ac = 99 ; /* initialize to unmapped ac */ + bool chk_adhoc_ps_mapping = false; + u32 wmi_data_flags = 0; + int ret, aggr_tx_status = AGGR_TX_UNKNOW; + struct ath6kl_sta *conn = NULL; + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX, + "%s: skb=0x%p, data=0x%p, len=0x%x\n", __func__, + skb, skb->data, skb->len); + + /* If target is not associated */ + if (!test_bit(CONNECTED, &vif->flags) && + !test_bit(TESTMODE_EPPING, &ar->flag)) { + dev_kfree_skb(skb); + return 0; + } + + if (!test_bit(WMI_READY, &ar->flag) && + !test_bit(TESTMODE_EPPING, &ar->flag)) { + goto fail_tx; + } + + /* AP mode Power saving processing */ + if (vif->nw_type == AP_NETWORK) { + if (ath6kl_powersave_ap(vif, skb, &wmi_data_flags, &conn)) + return 0; + } + + if (test_bit(WMI_ENABLED, &ar->flag)) { + if (skb_headroom(skb) < vif->needed_headroom) { + struct sk_buff *tmp_skb = ath6kl_buf_alloc(skb->len); + + if (tmp_skb == NULL) { + vif->net_stats.tx_dropped++; + goto fail_tx; + } + + skb_put(tmp_skb, skb->len); + memcpy(tmp_skb->data, skb->data, skb->len); + kfree_skb(skb); + skb = tmp_skb; + } + + /* sniffer ARP reply, enable ARP offload by default */ + if ((vif == ath6kl_vif_first(ar)) + && (vif->nw_type == INFRA_NETWORK)) + __ath6kl_arp_parse_offload(vif, skb); + + if (ath6kl_wmi_dix_2_dot3(ar->wmi, skb)) { + ath6kl_err("ath6kl_wmi_dix_2_dot3 failed\n"); + goto fail_tx; + } + + if (ath6kl_wmi_data_hdr_add(ar->wmi, skb, DATA_MSGTYPE, + wmi_data_flags, 0, 0, NULL, + vif->fw_vif_idx)) { + ath6kl_err("wmi_data_hdr_add failed\n"); + goto fail_tx; + } + + if ((vif->nw_type == ADHOC_NETWORK) && + ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags)) + chk_adhoc_ps_mapping = true; + else { + /* get the stream mapping */ + ret = ath6kl_wmi_implicit_create_pstream(ar->wmi, + vif->fw_vif_idx, skb, + 0, test_bit(WMM_ENABLED, &vif->flags), + &ac, &htc_tag); + if (ret) + goto fail_tx; + } + } else if (test_bit(TESTMODE_EPPING, &ar->flag)) { + struct epping_header *epping_hdr; + + epping_hdr = (struct epping_header *)skb->data; + + if (IS_EPPING_PACKET(epping_hdr)) { + ac = epping_hdr->stream_no_h; + + /* some EPPING packets cannot be dropped no matter + * what access class it was sent on. Change the packet + * tag to guarantee it will not get dropped + */ + if (IS_EPING_PACKET_NO_DROP(epping_hdr)) + htc_tag = ATH6KL_CONTROL_PKT_TAG; + + + if (ac == HCI_TRANSPORT_STREAM_NUM) { + goto fail_tx; + } else { + /* The payload of the frame is 32-bit aligned + * and thus the addition of the HTC header will + * mis-align the start of the HTC frame, + * the padding will be stripped off in the + * target */ + if (EPPING_ALIGNMENT_PAD > 0) + skb_push(skb, EPPING_ALIGNMENT_PAD); + + } + } else { + /* In loopback mode, drop non-loopback packet */ + goto fail_tx; + } + } else + goto fail_tx; + + /* TX A-MSDU */ + if ((test_bit(AMSDU_ENABLED, &vif->flags)) && + (!bypass_tx_aggr) && + (vif->aggr_cntxt->tx_amsdu_enable) && + (!chk_adhoc_ps_mapping) && + (vif->nw_type & (INFRA_NETWORK | AP_NETWORK))) { + aggr_tx_status = aggr_tx(vif, conn, &skb); + + if (aggr_tx_status == AGGR_TX_OK) + return 0; + else if (aggr_tx_status == AGGR_TX_DROP) + goto fail_tx; + + WARN_ON(skb == NULL); + + if ((vif->aggr_cntxt->tx_amsdu_seq_pkt) && + (aggr_tx_status == AGGR_TX_BYPASS)) + aggr_tx_flush(vif, conn); + } + + spin_lock_bh(&ar->lock); + + if (chk_adhoc_ps_mapping) + eid = ath6kl_ibss_map_epid(skb, dev, &map_no); + else + eid = ar->ac2ep_map[ac]; + + if (eid == 0 || eid == ENDPOINT_UNUSED) { + if ((ac == WMM_NUM_AC) && + test_bit(TESTMODE_EPPING, &ar->flag)) { + /* for epping testing, the last AC maps to the control + * endpoint + */ + eid = ar->ctrl_ep; + } else { + ath6kl_err("eid %d is not mapped!\n", eid); + spin_unlock_bh(&ar->lock); + goto fail_tx; + } + } + + /* allocate resource for this packet */ + if (htc_tag == ATH6KL_DATA_PKT_TAG) + cookie = ath6kl_alloc_cookie(ar, COOKIE_TYPE_DATA); + else + cookie = ath6kl_alloc_cookie(ar, COOKIE_TYPE_CTRL); + + if (!cookie) { + spin_unlock_bh(&ar->lock); + goto fail_tx; + } + + /* update counts while the lock is held */ + ar->tx_pending[eid]++; + ar->total_tx_data_pend++; + + spin_unlock_bh(&ar->lock); + + if (!IS_ALIGNED((unsigned long) skb->data - HTC_HDR_LENGTH, 4) && + skb_cloned(skb)) { + /* + * We will touch (move the buffer data to align it. Since the + * skb buffer is cloned and not only the header is changed, we + * have to copy it to allow the changes. Since we are copying + * the data here, we may as well align it by reserving suitable + * headroom to avoid the memmove in ath6kl_htc_tx_buf_align(). + */ + struct sk_buff *nskb; + + nskb = skb_copy_expand(skb, HTC_HDR_LENGTH, 0, GFP_ATOMIC); + if (nskb == NULL) + goto fail_tx; + kfree_skb(skb); + skb = nskb; + } + + cookie->skb = skb; + cookie->map_no = map_no; + set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, + eid, htc_tag); + cookie->htc_pkt.skb = skb; + + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ", + skb->data, skb->len); + + /* P2P Flowctrl */ + if (ar->conf_flags & ATH6KL_CONF_ENABLE_FLOWCTRL) { + cookie->htc_pkt.connid = + ath6kl_p2p_flowctrl_get_conn_id(vif, skb); + cookie->htc_pkt.recycle_count = 0; + ret = ath6kl_p2p_flowctrl_tx_schedule_pkt(ar, (void *)cookie); + if (ret == 0) /* Queue it */ + return 0; + else if (ret < 0) /* Error, drop it. */ + goto fail_tx; + } + + cookie->htc_pkt.vif = vif; + + ar->tx_on_vif |= (1 << vif->fw_vif_idx); + + /* + * HTC interface is asynchronous, if this fails, cleanup will + * happen in the ath6kl_tx_complete callback. + */ + ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt); + + return 0; + +fail_tx: + dev_kfree_skb(skb); + + vif->net_stats.tx_dropped++; + vif->net_stats.tx_aborted_errors++; + + return 0; +} + +int ath6kl_start_tx(struct sk_buff *skb, struct net_device *dev) +{ + /* Current design only aggr. the packets sent from the host. */ + return ath6kl_data_tx(skb, dev, false); +} + +/* indicate tx activity or inactivity on a WMI stream */ +void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active) +{ + struct ath6kl *ar = devt; + enum htc_endpoint_id eid; + int i; + u8 num_stream_active; + + eid = ar->ac2ep_map[traffic_class]; + + if (!test_bit(WMI_ENABLED, &ar->flag)) + goto notify_htc; + + spin_lock_bh(&ar->lock); + + ar->ac_stream_active[traffic_class] = active; + + if (active) { + /* + * Keep track of the active stream with the highest + * priority. + */ + if (ar->ac_stream_pri_map[traffic_class] > + ar->hiac_stream_active_pri) + /* set the new highest active priority */ + ar->hiac_stream_active_pri = + ar->ac_stream_pri_map[traffic_class]; + + if (ath6kl_htc_change_credit_bypass(ar->htc_target, + traffic_class)) { + struct ath6kl_vif *vif; + vif = ath6kl_vif_first(ar); + + spin_unlock_bh(&ar->lock); + + ath6kl_wmi_set_credit_bypass(ar->wmi, + vif->fw_vif_idx, + ar->ac2ep_map[WMM_AC_BE], 0, 6); + + spin_lock_bh(&ar->lock); + } + } else { + /* + * We may have to search for the next active stream + * that is the highest priority. + */ + if (ar->hiac_stream_active_pri == + ar->ac_stream_pri_map[traffic_class]) { + /* + * The highest priority stream just went inactive + * reset and search for the "next" highest "active" + * priority stream. + */ + ar->hiac_stream_active_pri = 0; + + for (i = 0; i < WMM_NUM_AC; i++) { + if (ar->ac_stream_active[i] && + (ar->ac_stream_pri_map[i] > + ar->hiac_stream_active_pri)) + /* + * Set the new highest active + * priority. + */ + ar->hiac_stream_active_pri = + ar->ac_stream_pri_map[i]; + } + } + + if (ath6kl_htc_change_credit_bypass(ar->htc_target, + traffic_class)) { + struct ath6kl_vif *vif; + vif = ath6kl_vif_first(ar); + + spin_unlock_bh(&ar->lock); + + ath6kl_wmi_set_credit_bypass(ar->wmi, + vif->fw_vif_idx, + ar->ac2ep_map[WMM_AC_BE], + 1, 1); + + spin_lock_bh(&ar->lock); + } + } + + /* check the number of active stream */ + num_stream_active = 0; + + for (i = 0; i < WMM_NUM_AC; i++) { + if (ar->ac_stream_active[i] == true) + num_stream_active++; + } + + ar->ac_stream_active_num = num_stream_active; + + spin_unlock_bh(&ar->lock); + +notify_htc: + /* notify HTC, this may cause credit distribution changes */ + ath6kl_htc_indicate_activity_change(ar->htc_target, eid, active); +} + +enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, + struct htc_packet *packet) +{ + struct ath6kl *ar = target->dev->ar; + struct ath6kl_vif *vif; + enum htc_endpoint_id endpoint = packet->endpoint; + enum htc_send_full_action action = HTC_SEND_FULL_KEEP; + + if (test_bit(TESTMODE_EPPING, &ar->flag)) { + int ac; + + if (packet->info.tx.tag == ATH6KL_CONTROL_PKT_TAG) { + /* don't drop special control packets */ + return HTC_SEND_FULL_KEEP; + } + + ac = ar->ep2ac_map[endpoint]; + + /* for endpoint ping testing drop Best Effort and Background + * if any of the higher priority traffic is active */ + if ((ar->ac_stream_active[WMM_AC_VO] || + ar->ac_stream_active[WMM_AC_BE]) && + ((ac == WMM_AC_BE) || (ac == WMM_AC_BK))) { + return HTC_SEND_FULL_DROP; + } else { + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + + /* keep but stop the netqueues */ + set_bit(NETQ_STOPPED, &vif->flags); + netif_stop_queue(vif->ndev); + } + spin_unlock_bh(&ar->list_lock); + return HTC_SEND_FULL_KEEP; + } + } + + if (endpoint == ar->ctrl_ep) { + /* + * Under normal WMI if this is getting full, then something + * is running rampant the host should not be exhausting the + * WMI queue with too many commands the only exception to + * this is during testing using endpointping. + */ + spin_lock_bh(&ar->lock); + set_bit(WMI_CTRL_EP_FULL, &ar->flag); + spin_unlock_bh(&ar->lock); + ath6kl_err("wmi ctrl ep is full\n"); + ath6kl_fw_crash_trap(ar); + return action; + } + + if ((packet->info.tx.tag == ATH6KL_CONTROL_PKT_TAG) || + (packet->info.tx.tag == ATH6KL_PRI_DATA_PKT_TAG)) + return action; + + /* + * The last MAX_HI_COOKIE_NUM "batch" of cookies are reserved for + * the highest active stream. + */ + if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] < + ar->hiac_stream_active_pri && + ar->cookie_data.cookie_count <= + target->endpoint[endpoint].tx_drop_packet_threshold){ + /* + * Give preference to the highest priority stream by + * dropping the packets which overflowed. + */ + action = HTC_SEND_FULL_DROP; + } else if (ar->vif_max > 1) { /* WAR: EV108182 */ + int i, ongoing_tx = 0; + + for (i = 0; i < ar->vif_max; i++) { + if (ar->tx_on_vif & (1 << i)) + ongoing_tx++; + } + + if (ongoing_tx > 1) + return action; + } + + /* FIXME: Locking */ + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (vif->nw_type == ADHOC_NETWORK || + action != HTC_SEND_FULL_DROP) { + spin_unlock_bh(&ar->list_lock); + + if (ath6kl_htc_stop_netif_queue_full(ar->htc_target) || + vif->nw_type == INFRA_NETWORK || + ar->ac_stream_active_num == 1) { + set_bit(NETQ_STOPPED, &vif->flags); + netif_stop_queue(vif->ndev); + } + + spin_lock_bh(&ar->list_lock); + } + } + spin_unlock_bh(&ar->list_lock); + + return action; +} + +/* TODO this needs to be looked at */ +static void ath6kl_tx_clear_node_map(struct ath6kl_vif *vif, + enum htc_endpoint_id eid, u32 map_no) +{ + struct ath6kl *ar = vif->ar; + u32 i; + + if (vif->nw_type != ADHOC_NETWORK) + return; + + if (!ar->ibss_ps_enable) + return; + + if (eid == ar->ctrl_ep) + return; + + if (map_no == 0) + return; + + map_no--; + ar->node_map[map_no].tx_pend--; + + if (ar->node_map[map_no].tx_pend) + return; + + if (map_no != (ar->node_num - 1)) + return; + + for (i = ar->node_num; i > 0; i--) { + if (ar->node_map[i - 1].tx_pend) + break; + + memset(&ar->node_map[i - 1], 0, + sizeof(struct ath6kl_node_mapping)); + ar->node_num--; + } +} + +void ath6kl_tx_complete(struct htc_target *target, + struct list_head *packet_queue) +{ + struct ath6kl *ar = target->dev->ar; + struct sk_buff_head skb_queue; + struct htc_packet *packet = NULL; + struct sk_buff *skb; + struct ath6kl_cookie *ath6kl_cookie; + u32 map_no = 0; + int status; + enum htc_endpoint_id eid = 0; + bool wake_event = false; + bool flushing[ATH6KL_VIF_MAX] = {false}; + u8 if_idx; + struct ath6kl_vif *vif = NULL; + struct htc_endpoint *endpoint = NULL; + int txq_depth; + + skb_queue_head_init(&skb_queue); + + /* lock the driver as we update internal state */ + spin_lock_bh(&ar->lock); + + /* reap completed packets */ + while (!list_empty(packet_queue)) { + + packet = list_first_entry(packet_queue, struct htc_packet, + list); + list_del(&packet->list); + + ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt; + if (!ath6kl_cookie) + goto fatal; + + status = packet->status; + skb = ath6kl_cookie->skb; + eid = packet->endpoint; + map_no = ath6kl_cookie->map_no; + + if (!skb || !skb->data) + goto fatal; + + if (eid == ENDPOINT_UNUSED || eid == ENDPOINT_MAX) + goto fatal; + + __skb_queue_tail(&skb_queue, skb); + + if (!status && (packet->act_len != skb->len)) + goto fatal; + + ar->tx_pending[eid]--; + + if (!test_bit(TESTMODE_EPPING, &ar->flag)) { + if (eid != ar->ctrl_ep) + ar->total_tx_data_pend--; + + if (eid == ar->ctrl_ep) { + if (test_bit(WMI_CTRL_EP_FULL, &ar->flag)) + clear_bit(WMI_CTRL_EP_FULL, &ar->flag); + + if (ar->tx_pending[eid] == 0) + wake_event = true; + } + + if (eid == ar->ctrl_ep) { + if_idx = wmi_cmd_hdr_get_if_idx( + (struct wmi_cmd_hdr *) packet->buf); + } else { + if_idx = wmi_data_hdr_get_if_idx( + (struct wmi_data_hdr *) packet->buf); + } + } else { + /* The epping packet is not coming from wmi, + * skip the index retrival, epping assume using the + * first if_idx anyway + */ + if_idx = 0; + } + + vif = ath6kl_get_vif_by_index(ar, if_idx); + if (!vif) { + ath6kl_free_cookie(ar, ath6kl_cookie); + continue; + } + + ar->tx_on_vif &= ~(1 << if_idx); + + if (status) { + if (status == -ECANCELED) + /* a packet was flushed */ + flushing[if_idx] = true; + + vif->net_stats.tx_errors++; + + if (status != -ENOSPC && status != -ECANCELED) + ath6kl_warn("tx complete error: %d\n", status); + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX, + "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n", + __func__, skb, packet->buf, packet->act_len, + eid, "error!"); + } else { + ath6kl_dbg(ATH6KL_DBG_WLAN_TX, + "%s: skb=0x%p data=0x%p len=0x%x eid=%d %s\n", + __func__, skb, packet->buf, packet->act_len, + eid, "OK"); + + flushing[if_idx] = false; + vif->net_stats.tx_packets++; + vif->net_stats.tx_bytes += skb->len; + } + + ath6kl_tx_clear_node_map(vif, eid, map_no); + + ath6kl_free_cookie(ar, ath6kl_cookie); + + if (test_bit(NETQ_STOPPED, &vif->flags)) + clear_bit(NETQ_STOPPED, &vif->flags); + } + + spin_unlock_bh(&ar->lock); + + __skb_queue_purge(&skb_queue); + + if (test_bit(MCC_ENABLED, &ar->flag)) { + endpoint = &ar->htc_target->endpoint[eid]; + /*if (ar && endpoint && packet && ar->htc_target) {*/ + if (endpoint && packet && ar->htc_target) { + struct list_head *tx_queue; + + tx_queue = &endpoint->txq; + if (tx_queue && vif && !flushing[vif->fw_vif_idx]) { + spin_lock_bh(&ar->htc_target->tx_lock); + txq_depth = get_queue_depth(tx_queue); + spin_unlock_bh(&ar->htc_target->tx_lock); + + if (txq_depth < ATH6KL_P2P_FLOWCTRL_REQ_STEP) + ath6kl_p2p_flowctrl_netif_transition( + ar, + ATH6KL_P2P_FLOWCTRL_NETIF_WAKE); + } + } + } else { + /* FIXME: Locking */ + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if ((test_bit(CONNECTED, &vif->flags) || + test_bit(TESTMODE_EPPING, &ar->flag)) && + !flushing[vif->fw_vif_idx]) { + spin_unlock_bh(&ar->list_lock); + netif_wake_queue(vif->ndev); + spin_lock_bh(&ar->list_lock); + } + } + spin_unlock_bh(&ar->list_lock); + } + + if (wake_event) + wake_up(&ar->event_wq); + + return; + +fatal: + WARN_ON(1); + spin_unlock_bh(&ar->lock); + return; +} + +static void ath6kl_flush_data_in_ep_by_if(struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + struct htc_packet *packet, *tmp_pkt; + struct htc_endpoint *endpoint; + struct list_head *tx_queue, container; + int eid; + + INIT_LIST_HEAD(&container); + + spin_lock_bh(&ar->htc_target->tx_lock); + for (eid = ENDPOINT_2; eid <= ENDPOINT_5; eid++) { + + endpoint = &ar->htc_target->endpoint[eid]; + tx_queue = &endpoint->txq; + + if (list_empty(tx_queue)) + continue; + + list_for_each_entry_safe(packet, + tmp_pkt, + tx_queue, + list) { + if (packet->vif != vif) + continue; + list_del(&packet->list); + packet->status = 0; + list_add_tail( + &packet->list, + &container); + } + } + spin_unlock_bh(&ar->htc_target->tx_lock); + + ath6kl_tx_complete(ar->htc_target, &container); + + return; +} + +void ath6kl_tx_data_cleanup_by_if(struct ath6kl_vif *vif) +{ + ath6kl_flush_data_in_ep_by_if(vif); + ath6kl_p2p_flowctrl_conn_list_cleanup_by_if(vif); +} + +void ath6kl_tx_data_cleanup(struct ath6kl *ar) +{ + int i; + + /* flush all the data (non-control) streams */ + for (i = 0; i < WMM_NUM_AC; i++) + ath6kl_htc_flush_txep(ar->htc_target, ar->ac2ep_map[i], + ATH6KL_DATA_PKT_TAG); + + ath6kl_p2p_flowctrl_conn_list_cleanup(ar); +} + +/* Rx functions */ +#ifdef CONFIG_ANDROID +static void ath6kl_eapol_send(struct work_struct *work) +{ + struct ath6kl_vif *vif = NULL; + + if (!work) + goto FAILED; + + vif = container_of(work, struct ath6kl_vif, + work_eapol_send.work); + + if (!vif) + goto FAILED; + + spin_lock_bh(&vif->pend_skb_lock); + + if (!vif->pend_skb) { + clear_bit(FIRST_EAPOL_PENDSENT, &vif->flags); + spin_unlock_bh(&vif->pend_skb_lock); + goto FAILED; + } + + if (!(vif->pend_skb->dev->flags & IFF_UP)) { + dev_kfree_skb(vif->pend_skb); + vif->pend_skb = NULL; + clear_bit(FIRST_EAPOL_PENDSENT, &vif->flags); + spin_unlock_bh(&vif->pend_skb_lock); + return; + } + + netif_rx_ni(vif->pend_skb); + + vif->pend_skb = NULL; + + clear_bit(FIRST_EAPOL_PENDSENT, &vif->flags); + + spin_unlock_bh(&vif->pend_skb_lock); + + return; +FAILED: + clear_bit(FIRST_EAPOL_PENDSENT, &vif->flags); + ath6kl_err("%s failed\n", __func__); + return; +} + +#endif + +static void ath6kl_deliver_frames_to_nw_stack(struct net_device *dev, + struct sk_buff *skb) +{ +#define ETHERTYPE_IP 0x0800 /* IP protocol */ +#define IP_PROTO_UDP 0x11 /* UDP protocol */ + struct ath6kl_vif *vif = netdev_priv(dev); + + if (!skb) + return; + + skb->dev = dev; + + if (!(skb->dev->flags & IFF_UP)) { + dev_kfree_skb(skb); + return; + } + + /* Handle de-aggregated IntraBss's AMSDU frame here. */ + if (vif->nw_type == AP_NETWORK) { + struct ethhdr *datap = (struct ethhdr *) skb->data; + + if (ath6kl_find_sta(vif, datap->h_dest)) { + if (vif->intra_bss) + ath6kl_data_tx(skb, dev, true); + else + dev_kfree_skb(skb); + + return; + } + } + + skb->protocol = eth_type_trans(skb, skb->dev); + +#ifdef CONFIG_ANDROID + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + + if (test_bit(CONNECT_HANDSHAKE_PROTECT, &vif->flags) && + (vif->ar->wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) { + if (vif->pend_skb != NULL) + flush_delayed_work(&vif->work_eapol_send); + if (test_bit(FIRST_EAPOL_PENDSENT, &vif->flags)) { + vif->pend_skb = skb; + INIT_DELAYED_WORK(&vif->work_eapol_send, + ath6kl_eapol_send); + schedule_delayed_work(&vif->work_eapol_send, + ATH6KL_EAPOL_DELAY_REPORT_IN_HANDSHAKE); + return; + } + } + } +#endif + +/* +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0))) +*/ +#ifdef CONFIG_ATH6KL_UDP_TPUT_WAR + if (skb->protocol == htons(ETHERTYPE_IP)) { + struct ethhdr *eth = eth_hdr(skb); + struct iphdr *ip_hdr = + (struct iphdr *)((u8 *)eth + sizeof(struct ethhdr)); + + if (ip_hdr->protocol == IP_PROTO_UDP) { + struct sk_buff *new_skb; + + new_skb = dev_alloc_skb(skb->len+ETH_HLEN); + + /* If we can't allocate a new skb, + * just indicate the original skb. + */ + if (new_skb == NULL) { + netif_rx_ni(skb); + } else { + skb_put(new_skb, skb->len+ETH_HLEN); + memcpy(new_skb->data, eth, skb->len+ETH_HLEN); + + new_skb->dev = dev; + new_skb->protocol = + eth_type_trans(new_skb, new_skb->dev); + dev_kfree_skb(skb); + netif_rx_ni(new_skb); + } + + return; + } + } +#endif + + netif_rx_ni(skb); +} + +static void ath6kl_alloc_netbufs(struct sk_buff_head *q, u16 num) +{ + struct sk_buff *skb; + + while (num) { + skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE); + if (!skb) { + ath6kl_err("netbuf allocation failed\n"); + return; + } + skb_queue_tail(q, skb); + num--; + } +} + +static struct sk_buff *aggr_get_free_skb(struct aggr_conn_info *aggr_conn) +{ + struct aggr_info *aggr = aggr_conn->aggr_cntxt; + struct sk_buff *skb = NULL; + + WARN_ON(!aggr); + if (skb_queue_len(&aggr->free_q) < (AGGR_NUM_OF_FREE_NETBUFS >> 2)) + ath6kl_alloc_netbufs(&aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS); + + skb = skb_dequeue(&aggr->free_q); + + return skb; +} + +void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) +{ + struct ath6kl *ar = target->dev->ar; + struct sk_buff *skb; + int rx_buf; + int n_buf_refill; + struct htc_packet *packet; + struct list_head queue; + + n_buf_refill = ATH6KL_MAX_RX_BUFFERS - + ath6kl_htc_get_rxbuf_num(ar->htc_target, endpoint); + + if (n_buf_refill <= 0) + return; + + INIT_LIST_HEAD(&queue); + + ath6kl_dbg(ATH6KL_DBG_WLAN_RX, + "%s: providing htc with %d buffers at eid=%d\n", + __func__, n_buf_refill, endpoint); + + for (rx_buf = 0; rx_buf < n_buf_refill; rx_buf++) { + skb = ath6kl_buf_alloc(ATH6KL_BUFFER_SIZE); + if (!skb) + break; + + packet = (struct htc_packet *) skb->head; + if (!IS_ALIGNED((unsigned long) skb->data, 4)) { + size_t len = skb_headlen(skb); + skb->data = PTR_ALIGN(skb->data - 4, 4); + skb_set_tail_pointer(skb, len); + } + set_htc_rxpkt_info(packet, skb, skb->data, + ATH6KL_BUFFER_SIZE, endpoint); + packet->skb = skb; + + list_add_tail(&packet->list, &queue); + } + + if (!list_empty(&queue)) + ath6kl_htc_add_rxbuf_multiple(ar->htc_target, &queue); +} + +void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) +{ + struct htc_packet *packet; + struct sk_buff *skb; + + while (count) { + skb = ath6kl_buf_alloc(ATH6KL_AMSDU_BUFFER_SIZE); + if (!skb) + return; + + packet = (struct htc_packet *) skb->head; + if (!IS_ALIGNED((unsigned long) skb->data, 4)) { + size_t len = skb_headlen(skb); + skb->data = PTR_ALIGN(skb->data - 4, 4); + skb_set_tail_pointer(skb, len); + } + set_htc_rxpkt_info(packet, skb, skb->data, + ATH6KL_AMSDU_BUFFER_SIZE, 0); + packet->skb = skb; + + spin_lock_bh(&ar->lock); + list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue); + spin_unlock_bh(&ar->lock); + count--; + } +} + +/* + * Callback to allocate a receive buffer for a pending packet. We use a + * pre-allocated list of buffers of maximum AMSDU size (4K). + */ +struct htc_packet *ath6kl_alloc_amsdu_rxbuf(struct htc_target *target, + enum htc_endpoint_id endpoint, + int len) +{ + struct ath6kl *ar = target->dev->ar; + struct htc_packet *packet = NULL; + struct list_head *pkt_pos; + int refill_cnt = 0, depth = 0; + + ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: eid=%d, len:%d\n", + __func__, endpoint, len); + + if ((len <= ATH6KL_BUFFER_SIZE) || + (len > ATH6KL_AMSDU_BUFFER_SIZE)) + return NULL; + + spin_lock_bh(&ar->lock); + + if (list_empty(&ar->amsdu_rx_buffer_queue)) { + spin_unlock_bh(&ar->lock); + refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS; + goto refill_buf; + } + + packet = list_first_entry(&ar->amsdu_rx_buffer_queue, + struct htc_packet, list); + list_del(&packet->list); + list_for_each(pkt_pos, &ar->amsdu_rx_buffer_queue) + depth++; + + refill_cnt = ATH6KL_MAX_AMSDU_RX_BUFFERS - depth; + spin_unlock_bh(&ar->lock); + + /* set actual endpoint ID */ + packet->endpoint = endpoint; + +refill_buf: + if (refill_cnt >= ATH6KL_AMSDU_REFILL_THRESHOLD) + ath6kl_refill_amsdu_rxbufs(ar, refill_cnt); + + return packet; +} + +static void aggr_slice_amsdu(struct aggr_conn_info *aggr_conn, + struct rxtid *rxtid, struct sk_buff *skb) +{ + struct sk_buff *new_skb; + struct ethhdr *hdr; + u16 frame_8023_len, payload_8023_len, mac_hdr_len, amsdu_len; + u8 *framep; + + mac_hdr_len = sizeof(struct ethhdr); + framep = skb->data + mac_hdr_len; + amsdu_len = skb->len - mac_hdr_len; + + while (amsdu_len > mac_hdr_len) { + hdr = (struct ethhdr *) framep; + payload_8023_len = ntohs(hdr->h_proto); + + if (payload_8023_len < MIN_MSDU_SUBFRAME_PAYLOAD_LEN || + payload_8023_len > MAX_MSDU_SUBFRAME_PAYLOAD_LEN) { + ath6kl_err("802.3 AMSDU frame bound check failed. len %d\n", + payload_8023_len); + break; + } + + frame_8023_len = payload_8023_len + mac_hdr_len; + new_skb = aggr_get_free_skb(aggr_conn); + if (!new_skb) { + ath6kl_err("no buffer available\n"); + break; + } + + memcpy(new_skb->data, framep, frame_8023_len); + skb_put(new_skb, frame_8023_len); + if (ath6kl_wmi_dot3_2_dix(new_skb)) { + ath6kl_err("dot3_2_dix error\n"); + dev_kfree_skb(new_skb); + break; + } + + skb_queue_tail(&rxtid->q, new_skb); + + /* Is this the last subframe within this aggregate ? */ + if ((amsdu_len - frame_8023_len) == 0) + break; + + /* Add the length of A-MSDU subframe padding bytes - + * Round to nearest word. + */ + frame_8023_len = ALIGN(frame_8023_len, 4); + + framep += frame_8023_len; + amsdu_len -= frame_8023_len; + } + + dev_kfree_skb(skb); +} + +static void aggr_deque_frms(struct aggr_conn_info *aggr_conn, u8 tid, + u16 seq_no, u8 order) +{ + struct sk_buff *skb; + struct rxtid *rxtid; + struct skb_hold_q *node; + u16 idx, idx_end, seq_end; + struct rxtid_stats *stats; + struct net_device *dev; + + if (!aggr_conn) + return; + + rxtid = AGGR_GET_RXTID(aggr_conn, tid); + stats = AGGR_GET_RXTID_STATS(aggr_conn, tid); + + idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz); + + /* + * idx_end is typically the last possible frame in the window, + * but changes to 'the' seq_no, when BAR comes. If seq_no + * is non-zero, we will go up to that and stop. + * Note: last seq no in current window will occupy the same + * index position as index that is just previous to start. + * An imp point : if win_sz is 7, for seq_no space of 4095, + * then, there would be holes when sequence wrap around occurs. + * Target should judiciously choose the win_sz, based on + * this condition. For 4095, (TID_WINDOW_SZ = 2 x win_sz + * 2, 4, 8, 16 win_sz works fine). + * We must deque from "idx" to "idx_end", including both. + */ + seq_end = seq_no ? seq_no : rxtid->seq_next; + idx_end = AGGR_WIN_IDX(seq_end, rxtid->hold_q_sz); + + spin_lock_bh(&rxtid->lock); + + do { + node = &rxtid->hold_q[idx]; + if ((order == 1) && (!node->skb)) + break; + + if (node->skb) { + if (node->is_amsdu) + aggr_slice_amsdu(aggr_conn, rxtid, node->skb); + else + skb_queue_tail(&rxtid->q, node->skb); + node->skb = NULL; + } else + stats->num_hole++; + + rxtid->seq_next = ATH6KL_NEXT_SEQ_NO(rxtid->seq_next); + idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz); + } while (idx != idx_end); + + stats->num_delivered += skb_queue_len(&rxtid->q); + + WARN_ON(!aggr_conn->dev); + dev = aggr_conn->dev; + while ((skb = skb_dequeue(&rxtid->q))) + ath6kl_deliver_frames_to_nw_stack(dev, skb); + spin_unlock_bh(&rxtid->lock); +} + +static bool aggr_process_recv_frm(struct ath6kl *ar, + struct aggr_conn_info *aggr_conn, + u8 tid, u16 seq_no, + bool is_amsdu, struct sk_buff *frame) +{ + struct rxtid *rxtid; + struct rxtid_stats *stats; + struct sk_buff *skb; + struct skb_hold_q *node; + u16 idx, st, cur, end; + bool is_queued = false; + u16 extended_end; + bool drop_it = false; + + rxtid = AGGR_GET_RXTID(aggr_conn, tid); + stats = AGGR_GET_RXTID_STATS(aggr_conn, tid); + + stats->num_into_aggr++; + + if (!rxtid->aggr) { + if (is_amsdu) { + struct net_device *dev; + + aggr_slice_amsdu(aggr_conn, rxtid, frame); + is_queued = true; + stats->num_amsdu++; + + WARN_ON(!aggr_conn->dev); + dev = aggr_conn->dev; + while ((skb = skb_dequeue(&rxtid->q))) + ath6kl_deliver_frames_to_nw_stack(dev, + skb); + } + return is_queued; + } + + spin_lock_bh(&rxtid->lock); + if (rxtid->sync_next_seq == true) { + rxtid->seq_next = seq_no; + rxtid->sync_next_seq = false; + } + + /* Check the incoming sequence no, if it's in the window */ + st = rxtid->seq_next; + cur = seq_no; + end = (st + rxtid->hold_q_sz-1) & ATH6KL_MAX_SEQ_NO; + if (((st < end) && (cur < st || cur > end)) || + ((st > end) && (cur > end) && (cur < st))) { + extended_end = (end + rxtid->hold_q_sz) & + ATH6KL_MAX_SEQ_NO; + + if (((end < extended_end) && + (cur < end || cur > extended_end)) || + ((end > extended_end) && (cur > extended_end) && + (cur < end))) { + u16 range_val = ((cur-st) & ATH6KL_MAX_SEQ_NO); + ath6kl_dbg(ATH6KL_DBG_AGGR, + "%s[%d] range_val=%d(%d),st=%d,cur=%d,tid=%d\n", + __func__, __LINE__, range_val, (rxtid->hold_q_sz << 1), + st, cur, tid); + + if ((range_val >= (rxtid->hold_q_sz << 1)) && + (range_val <= + (ATH6KL_MAX_SEQ_NO-(rxtid->hold_q_sz << 1)+1))) { + + ath6kl_dbg(ATH6KL_DBG_AGGR, "%s[%d] chase seq\n", + __func__, __LINE__); + + spin_unlock_bh(&rxtid->lock); + aggr_deque_frms(aggr_conn, tid, 0, 0); + spin_lock_bh(&rxtid->lock); + + rxtid->seq_next = + (cur - rxtid->hold_q_sz) & + ATH6KL_MAX_SEQ_NO; + + } else { + ath6kl_dbg(ATH6KL_DBG_AGGR, "%s[%d] old seq\n", + __func__, __LINE__); + } + drop_it = true; + } else { + /* + * Dequeue only those frames that are outside the + * new shifted window. + */ + st = (cur - (rxtid->hold_q_sz-1)) & ATH6KL_MAX_SEQ_NO; + spin_unlock_bh(&rxtid->lock); + aggr_deque_frms(aggr_conn, tid, st, 0); + spin_lock_bh(&rxtid->lock); + } + stats->num_oow++; + } + + if ((drop_it == true) && + !(ar->conf_flags & ATH6KL_CONF_DISABLE_RX_AGGR_DROP)) { + dev_kfree_skb(frame); + is_queued = true; + spin_unlock_bh(&rxtid->lock); + return is_queued; + } + + idx = AGGR_WIN_IDX(seq_no, rxtid->hold_q_sz); + + node = &rxtid->hold_q[idx]; + + /* + * Is the cur frame duplicate or something beyond our window(hold_q + * -> which is 2x, already)? + * + * 1. Duplicate is easy - drop incoming frame. + * 2. Not falling in current sliding window. + * 2a. is the frame_seq_no preceding current tid_seq_no? + * -> drop the frame. perhaps sender did not get our ACK. + * this is taken care of above. + * 2b. is the frame_seq_no beyond window(st, TID_WINDOW_SZ); + * -> Taken care of it above, by moving window forward. + */ + dev_kfree_skb(node->skb); + stats->num_dups++; + + node->skb = frame; + is_queued = true; + node->is_amsdu = is_amsdu; + node->seq_no = seq_no; + + if (node->is_amsdu) + stats->num_amsdu++; + else + stats->num_mpdu++; + + spin_unlock_bh(&rxtid->lock); + aggr_deque_frms(aggr_conn, tid, 0, 1); + spin_lock_bh(&rxtid->lock); + + if (rxtid->tid_timer_scheduled && + rxtid->timerwait_seq_num != rxtid->seq_next) { + del_timer(&rxtid->tid_timer); + rxtid->tid_timer_scheduled = false; + rxtid->continuous_count = 0; + } + + if (!rxtid->tid_timer_scheduled) { + for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { + if (rxtid->hold_q[idx].skb) { + rxtid->issue_timer_seq = + rxtid->hold_q[idx].seq_no; + /* + * There is a frame in the queue and no + * timer so start a timer to ensure that + * the frame doesn't remain stuck + * forever. + */ + rxtid->tid_timer_scheduled = true; + rxtid->timerwait_seq_num = rxtid->seq_next; + rxtid->continuous_count++; + mod_timer(&rxtid->tid_timer, + (jiffies + + msecs_to_jiffies( + aggr_conn->tid_timeout_setting[tid]))); + break; + } + } + } + spin_unlock_bh(&rxtid->lock); + + return is_queued; +} + +void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif, + struct ath6kl_sta *conn) +{ + bool is_psq_empty; + bool is_psq_empty_at_start; + u32 num_frames_to_deliver; + struct ath6kl_ps_buf_desc *ps_buf; + + /* If the APSD q for this STA is not empty, dequeue and send a pkt from + * the head of the q. Also update the More data bit in the WMI_DATA_HDR + * if there are more pkts for this STA in the APSD q. If there are + * no more pkts for this STA, update the APSD bitmap for this STA. + */ + + num_frames_to_deliver = (conn->apsd_info >> 4) & 0xF; + + /* Number of frames to send in a service period is indicated by + * the station in the QOS_INFO of the association request + * If it is zero, send all frames + */ + if (!num_frames_to_deliver) + num_frames_to_deliver = 0xFFFF; + + + spin_lock_bh(&conn->lock); + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: TriggerRx aid %d sta_flags %x apsd_info %d psq_data %d psq_mgmt %d\n", + __func__, conn->aid, conn->sta_flags, conn->apsd_info, + !ath6kl_ps_queue_empty(&conn->psq_data), + !ath6kl_ps_queue_empty(&conn->psq_mgmt)); + + is_psq_empty = ath6kl_ps_queue_empty(&conn->psq_data) && + ath6kl_ps_queue_empty(&conn->psq_mgmt); + spin_unlock_bh(&conn->lock); + + is_psq_empty_at_start = is_psq_empty; + while ((!is_psq_empty) && (num_frames_to_deliver)) { + spin_lock_bh(&conn->lock); + if (!ath6kl_ps_queue_empty(&conn->psq_mgmt)) { + struct ieee80211_mgmt *mgmt; + + ps_buf = ath6kl_ps_queue_dequeue(&conn->psq_mgmt); + is_psq_empty = ath6kl_ps_queue_empty(&conn->psq_data) && + ath6kl_ps_queue_empty(&conn->psq_mgmt); + spin_unlock_bh(&conn->lock); + + /* Set the STA flag to Trigger delivery, + * so that the frame will go out + */ + conn->sta_flags |= STA_PS_APSD_TRIGGER; + num_frames_to_deliver--; + + /* Last frame in the service period, + * set EOSP or queue empty + */ + if ((is_psq_empty) || + (!num_frames_to_deliver)) + conn->sta_flags |= STA_PS_APSD_EOSP; + + mgmt = (struct ieee80211_mgmt *) ps_buf->buf; + if (ps_buf->buf + ps_buf->len >= + mgmt->u.probe_resp.variable && + ieee80211_is_probe_resp(mgmt->frame_control)) + ath6kl_wmi_send_go_probe_response_cmd( + vif->ar->wmi, + vif, + ps_buf->buf, + ps_buf->len, + ps_buf->freq); + else + ath6kl_wmi_send_action_cmd(vif->ar->wmi, + vif->fw_vif_idx, + ps_buf->id, + ps_buf->freq, + ps_buf->wait, + ps_buf->buf, + ps_buf->len); + + kfree(ps_buf); + conn->sta_flags &= ~(STA_PS_APSD_TRIGGER); + conn->sta_flags &= ~(STA_PS_APSD_EOSP); + } else { + ps_buf = ath6kl_ps_queue_dequeue(&conn->psq_data); + + is_psq_empty = ath6kl_ps_queue_empty(&conn->psq_data) && + ath6kl_ps_queue_empty(&conn->psq_mgmt); + spin_unlock_bh(&conn->lock); + + if (ps_buf) { + /* Set the STA flag to Trigger delivery, + * so that the frame will go out + */ + conn->sta_flags |= STA_PS_APSD_TRIGGER; + num_frames_to_deliver--; + + /* Last frame in the service period, + * set EOSP or queue empty + */ + if ((is_psq_empty) || + (!num_frames_to_deliver)) + conn->sta_flags |= STA_PS_APSD_EOSP; + + WARN_ON(!ps_buf->skb); + ath6kl_data_tx(ps_buf->skb, vif->ndev, true); + kfree(ps_buf); + conn->sta_flags &= ~(STA_PS_APSD_TRIGGER); + conn->sta_flags &= ~(STA_PS_APSD_EOSP); + } + } + } + + if (is_psq_empty) { + ath6kl_wmi_set_pvb_cmd(vif->ar->wmi, + vif->fw_vif_idx, conn->aid, 0); + + if (is_psq_empty_at_start) + ath6kl_wmi_set_apsd_buffered_traffic_cmd(vif->ar->wmi, + vif->fw_vif_idx, conn->aid, 0, + WMI_AP_APSD_NO_DELIVERY_FRAMES_FOR_THIS_TRIGGER + ); + else + ath6kl_wmi_set_apsd_buffered_traffic_cmd(vif->ar->wmi, + vif->fw_vif_idx, conn->aid, 0, + 0); + } + + return; +} + +static inline struct ath6kl_sta *_powersave_ap_rx(struct ath6kl_vif *vif, + struct sk_buff *skb, int len, + bool ps_state, bool trigger_state) +{ + struct ath6kl *ar = vif->ar; + struct ath6kl_sta *conn; + struct ethhdr *datap = NULL; + bool prev_ps; + int min_hdr_len; + + datap = (struct ethhdr *) (skb->data); + conn = ath6kl_find_sta(vif, datap->h_source); + if (!conn) { + dev_kfree_skb(skb); + return NULL; + } + + /* + * If there is a change in PS state of the STA, take appropriate steps: + * + * 1. If Sleep-->Awake, flush the psq for the STA and clear the PVB. + * 2. If Awake-->Sleep, Starting queueing frames the STA. + */ + prev_ps = !!(conn->sta_flags & STA_PS_SLEEP); + + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: aid %d sta_flags %x prev_ps %d" + " ps_state %d is_trigger %d [%d]\n", + __func__, + conn->aid, + conn->sta_flags, + prev_ps, + ps_state, + trigger_state, + len); + + if (ps_state) { + conn->sta_flags |= STA_PS_SLEEP; + if (!prev_ps) { + aggr_tx_flush(vif , conn); + ath6kl_ps_queue_age_start(conn); + } + } else { + conn->sta_flags &= ~STA_PS_SLEEP; + if (prev_ps) + ath6kl_ps_queue_age_stop(conn); + } + + if (conn->sta_flags & STA_PS_SLEEP) { + /* Accept trigger only when the station is in sleep */ + if (trigger_state) + ath6kl_uapsd_trigger_frame_rx(vif, conn); + } + + if (prev_ps ^ !!(conn->sta_flags & STA_PS_SLEEP)) { + if (!(conn->sta_flags & STA_PS_SLEEP)) { + struct ath6kl_ps_buf_desc *ps_buf; + bool is_psq_empty_at_start; + struct ieee80211_mgmt *mgmt; + + spin_lock_bh(&conn->lock); + ath6kl_dbg(ATH6KL_DBG_POWERSAVE, + "%s: psq_data %d psq_mgmt %d\n", + __func__, + !ath6kl_ps_queue_empty(&conn->psq_data), + !ath6kl_ps_queue_empty(&conn->psq_mgmt)); + + is_psq_empty_at_start = + ath6kl_ps_queue_empty(&conn->psq_data) && + ath6kl_ps_queue_empty(&conn->psq_mgmt); + + while ((ps_buf = ath6kl_ps_queue_dequeue( + &conn->psq_mgmt)) != NULL) { + spin_unlock_bh(&conn->lock); + + mgmt = (struct ieee80211_mgmt *) ps_buf->buf; + + if ((ps_buf->buf + ps_buf->len >= + mgmt->u.probe_resp.variable) && + ieee80211_is_probe_resp( + mgmt->frame_control)) + ath6kl_wmi_send_go_probe_response_cmd( + ar->wmi, + vif, + ps_buf->buf, + ps_buf->len, + ps_buf->freq); + else + ath6kl_wmi_send_action_cmd( + ar->wmi, + vif->fw_vif_idx, + ps_buf->id, + ps_buf->freq, + ps_buf->wait, + ps_buf->buf, + ps_buf->len); + kfree(ps_buf); + spin_lock_bh(&conn->lock); + } + + while ((ps_buf = ath6kl_ps_queue_dequeue( + &conn->psq_data)) != NULL) { + spin_unlock_bh(&conn->lock); + + WARN_ON(!ps_buf->skb); + ath6kl_data_tx(ps_buf->skb, + vif->ndev, true); + kfree(ps_buf); + spin_lock_bh(&conn->lock); + } + + spin_unlock_bh(&conn->lock); + + if (!is_psq_empty_at_start) + ath6kl_wmi_set_apsd_buffered_traffic_cmd( + ar->wmi, + vif->fw_vif_idx, + conn->aid, + 0, + 0); + + /* Clear the PVB for this STA */ + ath6kl_wmi_set_pvb_cmd(ar->wmi, + vif->fw_vif_idx, + conn->aid, + 0); + } + } + + min_hdr_len = sizeof(struct ethhdr) + + sizeof(struct wmi_data_hdr) + + sizeof(struct ath6kl_llc_snap_hdr); + + /* drop NULL data frames here */ + if ((len < min_hdr_len) || + (len > WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH)) { + dev_kfree_skb(skb); + return NULL; + } + + return conn; +} + +void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) +{ + struct ath6kl *ar = target->dev->ar; + struct sk_buff *skb = packet->pkt_cntxt; + struct wmi_rx_meta_v2 *meta; + struct wmi_data_hdr *dhdr; + int min_hdr_len; + u8 meta_type, dot11_hdr = 0; + u8 pad_before_data_start; + int status = packet->status; + enum htc_endpoint_id ept = packet->endpoint; + bool is_amsdu; + struct ath6kl_sta *conn = NULL; + struct sk_buff *skb1 = NULL; + struct ethhdr *datap = NULL; + struct ath6kl_vif *vif; + u16 seq_no; + u8 tid, if_idx; + + ath6kl_dbg(ATH6KL_DBG_WLAN_RX, + "%s: ar=0x%p eid=%d, skb=0x%p, data=0x%p, len=0x%x status:%d", + __func__, ar, ept, skb, packet->buf, + packet->act_len, status); + + if (status || !(skb->data + HTC_HDR_LENGTH)) { + dev_kfree_skb(skb); + return; + } + + skb_put(skb, packet->act_len + HTC_HDR_LENGTH); + skb_pull(skb, HTC_HDR_LENGTH); + + if (!test_bit(TESTMODE_EPPING, &ar->flag)) { + if (ept == ar->ctrl_ep) { + if_idx = + wmi_cmd_hdr_get_if_idx( + (struct wmi_cmd_hdr *) skb->data); + } else { + if_idx = + wmi_data_hdr_get_if_idx( + (struct wmi_data_hdr *) skb->data); + } + } else { + /* The epping packet is not coming from wmi, skip the index + * retrival, epping assume using the first if_idx anyway + */ + if_idx = 0; + } + + vif = ath6kl_get_vif_by_index(ar, if_idx); + if (!vif) { + dev_kfree_skb(skb); + return; + } + + /* + * Take lock to protect buffer counts and adaptive power throughput + * state. + */ + vif->net_stats.rx_packets++; + vif->net_stats.rx_bytes += packet->act_len; + + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ", + skb->data, skb->len); + + skb->dev = vif->ndev; + + if (!test_bit(WMI_ENABLED, &ar->flag)) { + if (EPPING_ALIGNMENT_PAD > 0) + skb_pull(skb, EPPING_ALIGNMENT_PAD); + ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); + return; + } + + ath6kl_check_wow_status(ar); + + if (ept == ar->ctrl_ep) { + ath6kl_wmi_control_rx(ar->wmi, skb); + return; + } + + min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) + + sizeof(struct ath6kl_llc_snap_hdr); + + dhdr = (struct wmi_data_hdr *) skb->data; + + is_amsdu = wmi_data_hdr_is_amsdu(dhdr) ? true : false; + tid = wmi_data_hdr_get_up(dhdr); + seq_no = wmi_data_hdr_get_seqno(dhdr); + meta_type = wmi_data_hdr_get_meta(dhdr); + dot11_hdr = wmi_data_hdr_get_dot11(dhdr); + pad_before_data_start = + (le16_to_cpu(dhdr->info3) >> WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT) + & WMI_DATA_HDR_PAD_BEFORE_DATA_MASK; + packet->act_len -= pad_before_data_start; + + /* + * In the case of AP mode we may receive NULL data frames + * that do not have LLC hdr. They are 16 bytes in size. + * Allow these frames in the AP mode. + */ + if (vif->nw_type != AP_NETWORK && + ((packet->act_len < min_hdr_len) || + (packet->act_len > WMI_MAX_AMSDU_RX_DATA_FRAME_LENGTH))) { + ath6kl_info("frame len is too short or too long\n"); + vif->net_stats.rx_errors++; + vif->net_stats.rx_length_errors++; + dev_kfree_skb(skb); + return; + } + + skb_pull(skb, sizeof(struct wmi_data_hdr)); + + switch (meta_type) { + case WMI_META_VERSION_1: + skb_pull(skb, sizeof(struct wmi_rx_meta_v1)); + break; + case WMI_META_VERSION_2: + meta = (struct wmi_rx_meta_v2 *) skb->data; + meta->csum = le16_to_cpu(meta->csum); + if (meta->csum_flags & 0x1) { + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = (__force __wsum) meta->csum; + } + skb_pull(skb, sizeof(struct wmi_rx_meta_v2)); + break; + default: + break; + } + + skb_pull(skb, pad_before_data_start); + + if (dot11_hdr) + status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb); + else if (!is_amsdu) + status = ath6kl_wmi_dot3_2_dix(skb); + + if (status) { + /* + * Drop frames that could not be processed (lack of + * memory, etc.) + */ + dev_kfree_skb(skb); + return; + } + + /* Get the Power save state of the STA */ + if (vif->nw_type == AP_NETWORK) { + bool ps_state, trigger_state; + + ps_state = !!((dhdr->info >> WMI_DATA_HDR_PS_SHIFT) & + WMI_DATA_HDR_PS_MASK); + trigger_state = WMI_DATA_HDR_IS_TRIGGER(dhdr); + + conn = _powersave_ap_rx(vif, + skb, packet->act_len, + ps_state, trigger_state); + if (conn == NULL) + return; + } + + if (!(vif->ndev->flags & IFF_UP)) { + dev_kfree_skb(skb); + return; + } + + if (vif->nw_type == AP_NETWORK) { + datap = (struct ethhdr *) skb->data; + if (is_multicast_ether_addr(datap->h_dest)) + /* + * Bcast/Mcast frames should be sent to the + * OS stack as well as on the air. + */ + skb1 = skb_copy(skb, GFP_ATOMIC); + else { + /* + * Search for a connected STA with dstMac + * as the Mac address. If found send the + * frame to it on the air else send the + * frame up the stack. + */ + struct ath6kl_sta *to_conn = NULL; + + if (is_amsdu) + goto rx_aggr_process; + + to_conn = ath6kl_find_sta(vif, datap->h_dest); + + if (to_conn && vif->intra_bss) { + skb1 = skb; + skb = NULL; + } else if (to_conn && !vif->intra_bss) { + dev_kfree_skb(skb); + skb = NULL; + } + } + if (skb1) + ath6kl_data_tx(skb1, vif->ndev, true); + + if (skb == NULL) { + /* nothing to deliver up the stack */ + return; + } +#ifdef ATHTST_SUPPORT + /* record each connected sta rssi */ + if (conn->avg_data_rssi == 0) { + if ((dhdr->rssi) >= RSSI_LPF_THRESHOLD) + conn->avg_data_rssi = ATH_RSSI_IN(dhdr->rssi); + } else { + ATH_RSSI_LPF(conn->avg_data_rssi, dhdr->rssi); + } +#endif + } +#ifdef ATHTST_SUPPORT + if (vif->nw_type != AP_NETWORK) { + conn = &vif->sta_list[0]; + /* record each connected sta rssi */ + if (conn->avg_data_rssi == 0) { + if ((dhdr->rssi) >= RSSI_LPF_THRESHOLD) + conn->avg_data_rssi = ATH_RSSI_IN(dhdr->rssi); + } else { + ATH_RSSI_LPF(conn->avg_data_rssi, dhdr->rssi); + } + } +#endif + if (vif->nw_type != AP_NETWORK) + conn = &vif->sta_list[0]; + +rx_aggr_process: + datap = (struct ethhdr *) skb->data; + + if ((is_unicast_ether_addr(datap->h_dest) || + (vif->nw_type == AP_NETWORK)) && + aggr_process_recv_frm(ar, conn->aggr_conn_cntxt, tid, + seq_no, is_amsdu, skb)) + /* aggregation code will handle the skb */ + return; + + ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); +} + +static void aggr_tx_progressive(struct txtid *txtid, bool tx_timeout) +{ + struct ath6kl_vif *vif = txtid->vif; + struct aggr_info *aggr = vif->aggr_cntxt; + unsigned long now = jiffies; + + /* Only support STA mode now */ + if (vif->nw_type != INFRA_NETWORK) + return; + + txtid->last_num_amsdu++; + if (tx_timeout) + txtid->last_num_timeout++; + + /* Check it every AGGR_TX_PROG_CHECK_INTVAL */ + if (aggr->tx_amsdu_progressive && + ((txtid->last_check_time == 0) || + (now - txtid->last_check_time > AGGR_TX_PROG_CHECK_INTVAL))) { + /* + * Change to high speed when mass of AMSDUs & most of it + * are not-timeout case. + * Back to normal speed when bit of AMSDUs & most of it + * are timeout case. + */ + if (!aggr->tx_amsdu_progressive_hispeed) { + if ((txtid->last_num_amsdu > AGGR_TX_PROG_HS_THRESH) && + (txtid->last_num_timeout < + (txtid->last_num_amsdu >> + AGGR_TX_PROG_HS_FACTOR))) { + aggr->tx_amsdu_progressive_hispeed = true; + aggr_tx_config(vif, + aggr->tx_amsdu_seq_pkt, + true, + AGGR_TX_PROG_HS_MAX_NUM, + AGGR_TX_MAX_PDU_SIZE, + AGGR_TX_PROG_HS_TIMEOUT); + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: AMSDU change to high speed %d %d\n", + __func__, + txtid->last_num_amsdu, + txtid->last_num_timeout); + } + } else if (aggr->tx_amsdu_progressive_hispeed) { + if ((txtid->last_num_amsdu < AGGR_TX_PROG_NS_THRESH) && + (txtid->last_num_timeout > + (txtid->last_num_amsdu >> + AGGR_TX_PROG_NS_FACTOR))) { + aggr->tx_amsdu_progressive_hispeed = false; + aggr_tx_config(vif, + aggr->tx_amsdu_seq_pkt, + true, + AGGR_TX_MAX_NUM, + AGGR_TX_MAX_PDU_SIZE, + AGGR_TX_TIMEOUT); + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: AMSDU back to normal speed %d %d\n", + __func__, + txtid->last_num_amsdu, + txtid->last_num_timeout); + } + } + + /* reset the counters */ + txtid->last_num_amsdu = 0; + txtid->last_num_timeout = 0; + + /* Update to current time */ + txtid->last_check_time = now; + } + + return; +} + +static void aggr_tx_reset_aggr(struct txtid *txtid, bool free_buf, + bool timer_stop) +{ + /* Need protected by tid->lock. */ + if (timer_stop) + del_timer(&txtid->timer); + + if ((free_buf) && + (txtid->amsdu_skb)) + dev_kfree_skb(txtid->amsdu_skb); + + txtid->amsdu_skb = NULL; + txtid->amsdu_start = NULL; + txtid->amsdu_cnt = 0; + txtid->amsdu_len = 0; + txtid->amsdu_lastpdu_len = 0; + + return; +} + +static void aggr_tx_delete_tid_state(struct aggr_conn_info *aggr_conn, u8 tid) +{ + struct txtid *txtid; + struct aggr_info *aggr = aggr_conn->aggr_cntxt; + + txtid = AGGR_GET_TXTID(aggr_conn, tid); + + spin_lock_bh(&txtid->lock); + txtid->aid = 0; + txtid->max_aggr_sz = 0; + + aggr_tx_reset_aggr(txtid, true, true); + + txtid->num_pdu = 0; + txtid->num_amsdu = 0; + txtid->num_timeout = 0; + txtid->num_flush = 0; + txtid->num_tx_null = 0; + txtid->num_overflow = 0; + + txtid->last_check_time = 0; + txtid->last_num_amsdu = 0; + txtid->last_num_timeout = 0; + if (aggr->tx_amsdu_progressive_hispeed) { + aggr->tx_amsdu_progressive_hispeed = false; + aggr_tx_config(aggr->vif, + aggr->tx_amsdu_seq_pkt, + true, + AGGR_TX_MAX_NUM, + AGGR_TX_MAX_PDU_SIZE, + AGGR_TX_TIMEOUT); + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: AMSDU reset to normal speed\n", + __func__); + } + + spin_unlock_bh(&txtid->lock); + + return; +} + +static int aggr_tx(struct ath6kl_vif *vif, struct ath6kl_sta *sta, + struct sk_buff **skb) +{ +#define ETHERTYPE_IP 0x0800 /* IP protocol */ +#define IP_PROTO_TCP 0x6 /* TCP protocol */ + struct ethhdr *eth_hdr; + struct ath6kl_llc_snap_hdr *llc_hdr; + struct aggr_info *aggr; + struct txtid *txtid; + int pdu_len, subframe_len; + int hdr_len = /*WMI_MAX_TX_META_SZ + */sizeof(struct wmi_data_hdr); + + pdu_len = (*skb)->len - hdr_len; + aggr = vif->aggr_cntxt; + + /* + * Only aggr IP/TCP frames, focus on small TCP-ACK frams. + * Bypass multicast and non-IP/TCP frames. + * + * Reserved 14 bytes 802.3 header ahead of A-MSDU frame for target + * to transfer to 802.11 header. + */ + if (pdu_len > aggr->tx_amsdu_max_pdu_len) + return AGGR_TX_BYPASS; + + eth_hdr = (struct ethhdr *)((*skb)->data + hdr_len); + if (is_multicast_ether_addr(eth_hdr->h_dest)) + return AGGR_TX_BYPASS; + + llc_hdr = (struct ath6kl_llc_snap_hdr *)((*skb)->data + hdr_len + + sizeof(struct ethhdr)); + if (llc_hdr->eth_type == htons(ETHERTYPE_IP)) { + struct iphdr *ip_hdr = (struct iphdr *)((u8 *)eth_hdr + + sizeof(struct ethhdr) + + sizeof(struct ath6kl_llc_snap_hdr)); + + if (ip_hdr->protocol == IP_PROTO_TCP) { + struct ath6kl_sta *conn; + + if (!sta) + conn = ath6kl_find_sta(vif, eth_hdr->h_dest); + else { + /* Only in AP mode and we already know the + * station. + */ + WARN_ON(vif->nw_type != AP_NETWORK); + + conn = sta; + } + + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, + "aggr tx ", (*skb)->data, (*skb)->len); + + if (conn) { + struct sk_buff *amsdu_skb; + struct wmi_data_hdr *wmi_hdr = + (struct wmi_data_hdr *)((u8 *)eth_hdr - + sizeof(struct wmi_data_hdr)); + u16 info2_tmp; + + /* Not allow TX-AMSDU during STA sleep. */ + if ((vif->nw_type == AP_NETWORK) && + (conn->sta_flags & (STA_PS_SLEEP | + STA_PS_POLLED | + STA_PS_APSD_TRIGGER))) { + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: STA is in sleep state, aid %d sta_flags %x\n", + __func__, + conn->aid, + conn->sta_flags); + return AGGR_TX_BYPASS; + } + + txtid = AGGR_GET_TXTID(conn->aggr_conn_cntxt, + ((wmi_hdr->info >> WMI_DATA_HDR_UP_SHIFT) & + WMI_DATA_HDR_UP_MASK)); + + spin_lock_bh(&txtid->lock); + if (!txtid->max_aggr_sz) { + spin_unlock_bh(&txtid->lock); + return AGGR_TX_BYPASS; + } + + amsdu_skb = txtid->amsdu_skb; + if (amsdu_skb == NULL) { + amsdu_skb = + dev_alloc_skb(AGGR_TX_MAX_AGGR_SIZE); + if (amsdu_skb == NULL) { + spin_unlock_bh(&txtid->lock); + return AGGR_TX_BYPASS; + } + + /* Change to A-MSDU type */ + info2_tmp = le16_to_cpu(wmi_hdr->info2); + info2_tmp |= (WMI_DATA_HDR_AMSDU_MASK << + WMI_DATA_HDR_AMSDU_SHIFT); + wmi_hdr->info2 = cpu_to_le16(info2_tmp); + + /* Clone meta-data & WMI-header. */ + memcpy(amsdu_skb->data - hdr_len, + (*skb)->data, hdr_len); + + aggr_tx_reset_aggr(txtid, false, true); + txtid->amsdu_skb = amsdu_skb; + txtid->amsdu_start = amsdu_skb->data; + + amsdu_skb->data += + sizeof(struct ethhdr); + + /* Start tx timeout timer */ + mod_timer(&txtid->timer, jiffies + + msecs_to_jiffies( + aggr->tx_amsdu_timeout)); + } else { + if ((txtid->amsdu_len + pdu_len) > + aggr->tx_amsdu_max_aggr_len) { + txtid->num_overflow++; + spin_unlock_bh(&txtid->lock); + + ath6kl_dbg( + ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: AMSDU overflow, pdu_len=%d, amsdu_cnt=%d, amsdu_len=%d\n", + __func__, + pdu_len, + txtid->amsdu_cnt, + amsdu_skb->len); + + return AGGR_TX_BYPASS; + } + } + + /* Zero padding */ + subframe_len = roundup(pdu_len, 4); + memset(amsdu_skb->data + + subframe_len - 4, 0, 4); + + /* Append PDU to A-MSDU */ + memcpy(amsdu_skb->data, eth_hdr, pdu_len); + amsdu_skb->len += subframe_len; + amsdu_skb->data += subframe_len; + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: subframe_len=%d, tid=%d, amsdu_cnt=%d, amsdu_len=%d\n", + __func__, subframe_len, + ((wmi_hdr->info>>WMI_DATA_HDR_UP_SHIFT) + & WMI_DATA_HDR_UP_MASK), + txtid->amsdu_cnt, amsdu_skb->len); + + txtid->amsdu_cnt++; + txtid->amsdu_lastpdu_len = pdu_len; + txtid->amsdu_len += subframe_len; + + dev_kfree_skb(*skb); + *skb = NULL; + + if (txtid->amsdu_cnt >= + aggr->tx_amsdu_max_aggr_num) { + /* No padding in last MSDU */ + if (pdu_len & 0x3) + amsdu_skb->len -= + (4 - (pdu_len & 0x3)); + + /* Update A-MSDU frame header */ + eth_hdr = + (struct ethhdr *)txtid->amsdu_start; + + if (vif->nw_type == INFRA_NETWORK) { + memcpy(eth_hdr->h_dest, + vif->bssid, ETH_ALEN); + memcpy(eth_hdr->h_source, + vif->ndev->dev_addr, ETH_ALEN); + } else { + memcpy(eth_hdr->h_dest, + conn->mac, ETH_ALEN); + memcpy(eth_hdr->h_source, + vif->ndev->dev_addr, + ETH_ALEN); + } + + eth_hdr->h_proto = + htons(amsdu_skb->len); + + /* Correct final skb's data and length. + */ + amsdu_skb->len += + (hdr_len + sizeof(struct ethhdr)); + amsdu_skb->data = + txtid->amsdu_start - hdr_len; + + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, + __func__, "aggr-tx ", + amsdu_skb->data, + amsdu_skb->len); + + /* update stat. */ + txtid->num_amsdu++; + txtid->num_pdu += txtid->amsdu_cnt; + + *skb = amsdu_skb; + aggr_tx_reset_aggr(txtid, false, true); + aggr_tx_progressive(txtid, false); + spin_unlock_bh(&txtid->lock); + + return AGGR_TX_DONE; + } else { + spin_unlock_bh(&txtid->lock); + return AGGR_TX_OK; + } + } else + return AGGR_TX_DROP; + } + } + + return AGGR_TX_BYPASS; +} + +static int aggr_tx_tid(struct txtid *txtid, bool timer_stop) +{ + struct ath6kl_vif *vif = txtid->vif; + struct ath6kl *ar = vif->ar; + struct ath6kl_cookie *cookie; + enum htc_endpoint_id eid; + struct wmi_data_hdr *wmi_hdr; + struct sk_buff *amsdu_skb, *skb = NULL; + struct ethhdr *eth_hdr; + int ac; + int hdr_len = /*WMI_MAX_TX_META_SZ + */sizeof(struct wmi_data_hdr); + + spin_lock_bh(&txtid->lock); + amsdu_skb = txtid->amsdu_skb; + if (amsdu_skb == NULL) { + txtid->num_tx_null++; + spin_unlock_bh(&txtid->lock); + return -EINVAL; + } + + if (timer_stop) + txtid->num_flush++; + else + txtid->num_timeout++; + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: amsdu_skb=0x%p, data=0x%p, len=0x%x, amsdu_cnt=%d\n", + __func__, + amsdu_skb, amsdu_skb->data, + amsdu_skb->len, txtid->amsdu_cnt); + + /* No padding in last MSDU */ + if (txtid->amsdu_lastpdu_len & 0x3) + amsdu_skb->len -= (4 - (txtid->amsdu_lastpdu_len & 0x3)); + + /* Update A-MSDU frame header */ + eth_hdr = (struct ethhdr *)txtid->amsdu_start; + if (vif->nw_type == INFRA_NETWORK) { + memcpy(eth_hdr->h_dest, vif->bssid, ETH_ALEN); + memcpy(eth_hdr->h_source, vif->ndev->dev_addr, ETH_ALEN); + } else { + struct ath6kl_sta *conn = ath6kl_find_sta_by_aid(vif, txtid->aid); + + if (conn) { + memcpy(eth_hdr->h_dest, conn->mac, ETH_ALEN); + memcpy(eth_hdr->h_source, vif->ndev->dev_addr, ETH_ALEN); + } else { + aggr_tx_reset_aggr(txtid, true, timer_stop); + spin_unlock_bh(&txtid->lock); + + ath6kl_err("aggr_tx_tid error, no STA found, AID = %d\n", + txtid->aid); + return -EINVAL; + } + } + eth_hdr->h_proto = htons(amsdu_skb->len); + + /* Correct final skb's data and length. */ + amsdu_skb->len += (hdr_len + sizeof(struct ethhdr)); + amsdu_skb->data = txtid->amsdu_start - hdr_len; + + /* update stat. */ + txtid->num_amsdu++; + txtid->num_pdu += txtid->amsdu_cnt; + + skb = amsdu_skb; + aggr_tx_reset_aggr(txtid, false, timer_stop); + if (!timer_stop) + aggr_tx_progressive(txtid, true); + spin_unlock_bh(&txtid->lock); + + spin_lock_bh(&ar->lock); + wmi_hdr = (struct wmi_data_hdr *) + (skb->data + hdr_len - sizeof(struct wmi_data_hdr)); + ac = up_to_ac[(wmi_hdr->info >> WMI_DATA_HDR_UP_SHIFT) & + WMI_DATA_HDR_UP_MASK]; + eid = ar->ac2ep_map[ac]; + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: eid=%d, ac=%d\n", __func__, eid, ac); + + if (eid == 0 || eid == ENDPOINT_UNUSED) { + ath6kl_err("eid %d is not mapped!\n", eid); + spin_unlock_bh(&ar->lock); + goto fail_tx; + } + + /* allocate resource for this packet */ + cookie = ath6kl_alloc_cookie(ar, COOKIE_TYPE_DATA); + + if (!cookie) { + spin_unlock_bh(&ar->lock); + goto fail_tx; + } + + /* update counts while the lock is held */ + ar->tx_pending[eid]++; + ar->total_tx_data_pend++; + + spin_unlock_bh(&ar->lock); + + cookie->skb = skb; + cookie->map_no = 0; + set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, + eid, ATH6KL_DATA_PKT_TAG); + cookie->htc_pkt.skb = skb; + + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "aggr-tx ", + skb->data, skb->len); + + /* P2P Flowctrl */ + if (ar->conf_flags & ATH6KL_CONF_ENABLE_FLOWCTRL) { + int ret; + + cookie->htc_pkt.connid = + ath6kl_p2p_flowctrl_get_conn_id(vif, skb); + cookie->htc_pkt.recycle_count = 0; + ret = ath6kl_p2p_flowctrl_tx_schedule_pkt(ar, (void *)cookie); + if (ret == 0) /* Queue it */ + return 0; + else if (ret < 0) /* Error, drop it. */ + goto fail_tx; + } + + cookie->htc_pkt.vif = vif; + + ar->tx_on_vif |= (1 << vif->fw_vif_idx); + + /* + * HTC interface is asynchronous, if this fails, cleanup will + * happen in the ath6kl_tx_complete callback. + */ + ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt); + + return 0; + +fail_tx: + dev_kfree_skb(skb); + + vif->net_stats.tx_dropped++; + vif->net_stats.tx_aborted_errors++; + + return -EINVAL; +} + +static void aggr_tx_timeout(unsigned long arg) +{ + struct txtid *txtid = (struct txtid *)arg; + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: aid %d, tid %d", __func__, txtid->aid, txtid->tid); + + aggr_tx_tid(txtid, false); + + return; +} + +static int aggr_tx_flush(struct ath6kl_vif *vif, struct ath6kl_sta *conn) +{ + int tid; + + if (conn == NULL) { + if (vif->nw_type == INFRA_NETWORK) + conn = &vif->sta_list[0]; + else if (vif->nw_type == AP_NETWORK) + return 0; + else + BUG_ON(1); + } + + /* In AP mode, these packages will be queued in target side. */ + for (tid = (NUM_OF_TIDS - 1); tid >= 0; tid--) { + struct txtid *txtid = + AGGR_GET_TXTID(conn->aggr_conn_cntxt, tid); + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: flush sta aid %d\n", __func__, conn->aid); + aggr_tx_tid(txtid, true); + } + return 0; +} + +/* + * For the continuous un-received packets, wait timer will be divided by 2 + * I.E. pkt#1, pkt#2, pkt#3 don't receive, + * the pkt#1 will wait tid_timeout_setting + * the pkt#2 will wait tid_timeout_setting / 2 + * the pkt#3 will wait tid_timeout_setting / 4 + * If more than ATH6KL_MAX_WAIT_CONTINUOUS_PKT, move to the first + * un-continuous un-received packets +*/ +static void aggr_timeout(unsigned long arg) +{ + u8 j; + struct rxtid *rxtid = (struct rxtid *) arg; + struct aggr_conn_info *aggr_conn = rxtid->aggr_conn; + struct rxtid_stats *stats; + u32 tid_next_timeout = + aggr_conn->tid_timeout_setting[rxtid->tid]; + + stats = AGGR_GET_RXTID_STATS(aggr_conn, rxtid->tid); + + if (!rxtid->aggr || !rxtid->tid_timer_scheduled) + return; + + spin_lock_bh(&rxtid->lock); + + if (rxtid->timerwait_seq_num == rxtid->seq_next) { + stats->num_timeouts++; + ath6kl_dbg(ATH6KL_DBG_AGGR, + "aggr timeout (st %d end %d)(tid=%d)\n", + rxtid->seq_next, + ((rxtid->seq_next + rxtid->hold_q_sz-1) & + ATH6KL_MAX_SEQ_NO), rxtid->tid); + spin_unlock_bh(&rxtid->lock); + aggr_deque_frms(aggr_conn, rxtid->tid, + ((rxtid->timerwait_seq_num + 1) & + ATH6KL_MAX_SEQ_NO) , 0); + /* inorder packet that after time-out packet!! */ + aggr_deque_frms(aggr_conn, rxtid->tid, 0 , 1); + if (rxtid->seq_next == + ((rxtid->timerwait_seq_num + 1) & + ATH6KL_MAX_SEQ_NO)) { + /* Continus hole */ + if (rxtid->continuous_count >= + ATH6KL_MAX_WAIT_CONTINUOUS_PKT) { + aggr_deque_frms(aggr_conn, rxtid->tid, + ((rxtid->issue_timer_seq + 1) & + ATH6KL_MAX_SEQ_NO) , 0); + /* inorder packet that after time-out packet!! */ + aggr_deque_frms(aggr_conn, rxtid->tid, 0 , 1); + rxtid->continuous_count = 0; + } + } else { + rxtid->continuous_count = 0; + } + spin_lock_bh(&rxtid->lock); + } + rxtid->tid_timer_scheduled = false; + + if (rxtid->hold_q) { + for (j = 0; j < rxtid->hold_q_sz; j++) { + if (rxtid->hold_q[j].skb) { + rxtid->issue_timer_seq = + rxtid->hold_q[j].seq_no; + rxtid->timerwait_seq_num = rxtid->seq_next; + rxtid->tid_timer_scheduled = true; + rxtid->continuous_count++; + break; + } + } + } + + if (rxtid->continuous_count > 1) { + if (rxtid->continuous_count <= + ATH6KL_MAX_WAIT_CONTINUOUS_PKT) { + tid_next_timeout = tid_next_timeout / + ((rxtid->continuous_count - 1) * 2); + ath6kl_dbg(ATH6KL_DBG_AGGR, + "aggr continuous hole timeout count %d\n", + rxtid->continuous_count); + } else { + ath6kl_dbg(ATH6KL_DBG_AGGR, + "aggr continuous hole count %d larger than 3?\n", + rxtid->continuous_count); + rxtid->continuous_count = 0; + } + } + + if (rxtid->tid_timer_scheduled) { + mod_timer(&rxtid->tid_timer, + jiffies + msecs_to_jiffies(tid_next_timeout)); + } + spin_unlock_bh(&rxtid->lock); +} + +static void aggr_delete_tid_state(struct aggr_conn_info *aggr_conn, u8 tid) +{ + struct rxtid *rxtid; + struct rxtid_stats *stats; + + if (!aggr_conn || tid >= NUM_OF_TIDS) + return; + + rxtid = AGGR_GET_RXTID(aggr_conn, tid); + stats = AGGR_GET_RXTID_STATS(aggr_conn, tid); + + if (rxtid->aggr) + aggr_deque_frms(aggr_conn, tid, 0, 0); + spin_lock_bh(&rxtid->lock); + rxtid->aggr = false; + rxtid->win_sz = 0; + rxtid->seq_next = 0; + rxtid->hold_q_sz = 0; + + kfree(rxtid->hold_q); + rxtid->hold_q = NULL; + spin_unlock_bh(&rxtid->lock); + memset(stats, 0, sizeof(struct rxtid_stats)); +} + +void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid, u16 seq_no, + u8 win_sz) +{ + struct aggr_conn_info *aggr_conn; + struct rxtid *rxtid; + struct rxtid_stats *stats; + struct ath6kl_sta *conn; + u16 hold_q_size; + u8 conn_tid, conn_aid; + + conn_tid = AGGR_BA_EVT_GET_TID(tid); + conn_aid = AGGR_BA_EVT_GET_CONNID(tid); + conn = ath6kl_find_sta_by_aid(vif, conn_aid); + + if (conn_tid >= NUM_OF_TIDS) { + WARN_ON(1); + return; + } + + if (conn != NULL) { + WARN_ON(!conn->aggr_conn_cntxt); + + aggr_conn = conn->aggr_conn_cntxt; + rxtid = AGGR_GET_RXTID(aggr_conn, conn_tid); + stats = AGGR_GET_RXTID_STATS(aggr_conn, conn_tid); + + if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) + ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d, aid %d\n", + __func__, win_sz, conn_tid, conn_aid); + + if (rxtid->aggr) + aggr_delete_tid_state(aggr_conn, conn_tid); + spin_lock_bh(&rxtid->lock); + rxtid->seq_next = seq_no; + hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q); + rxtid->hold_q = kzalloc(hold_q_size, GFP_ATOMIC); + + if (!rxtid->hold_q) { + spin_unlock_bh(&rxtid->lock); + return; + } + + rxtid->win_sz = win_sz; + rxtid->hold_q_sz = TID_WINDOW_SZ(win_sz); + if (!skb_queue_empty(&rxtid->q)) { + spin_unlock_bh(&rxtid->lock); + return; + } + + rxtid->aggr = true; + rxtid->sync_next_seq = true; + spin_unlock_bh(&rxtid->lock); + } +} + +void aggr_recv_addba_resp_evt(struct ath6kl_vif *vif, u8 tid, + u16 amsdu_sz, u8 status) +{ + struct aggr_conn_info *aggr_conn; + struct txtid *txtid; + struct ath6kl_sta *conn; + u8 i, conn_tid, conn_aid; + + conn_tid = AGGR_BA_EVT_GET_TID(tid); + conn_aid = AGGR_BA_EVT_GET_CONNID(tid); + conn = ath6kl_find_sta_by_aid(vif, conn_aid); + + if (conn_tid >= NUM_OF_TIDS) { + WARN_ON(1); + return; + } + + if (conn != NULL) { + WARN_ON(!conn->aggr_conn_cntxt); + + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: amsdu_sz %d, tid %d, aid %d, status %d\n", + __func__, amsdu_sz, conn_tid, conn_aid, status); + + aggr_conn = conn->aggr_conn_cntxt; + txtid = AGGR_GET_TXTID(aggr_conn, conn_tid); + + spin_lock_bh(&txtid->lock); + txtid->aid = conn_aid; + if (status == 0) + txtid->max_aggr_sz = amsdu_sz; + else + txtid->max_aggr_sz = 0; + + /* 0 means disable */ + if (!txtid->max_aggr_sz) + aggr_tx_reset_aggr(txtid, true, true); + spin_unlock_bh(&txtid->lock); + + if (vif->nw_type != AP_NETWORK) { + vif->aggr_cntxt->tx_amsdu_enable = false; + for (i = 0; i < NUM_OF_TIDS; i++) { + txtid = AGGR_GET_TXTID(aggr_conn, i); + if (txtid->max_aggr_sz) { + vif->aggr_cntxt->tx_amsdu_enable = true; + break; + } + } + } + } +} + +void aggr_tx_config(struct ath6kl_vif *vif, + bool tx_amsdu_seq_pkt, + bool tx_amsdu_progressive, + u8 tx_amsdu_max_aggr_num, + u16 tx_amsdu_max_pdu_len, + u16 tx_amsdu_timeout) +{ + if ((vif) && + (vif->aggr_cntxt)) { + struct aggr_info *aggr = vif->aggr_cntxt; + + aggr->tx_amsdu_seq_pkt = tx_amsdu_seq_pkt; + + if (!tx_amsdu_progressive && + aggr->tx_amsdu_progressive) + aggr->tx_amsdu_progressive_hispeed = false; + else if (tx_amsdu_progressive && + !aggr->tx_amsdu_progressive) { + ; /* TODO : reset all last_XXXX in txtid */ + } + aggr->tx_amsdu_progressive = tx_amsdu_progressive; + + if (tx_amsdu_timeout == 0) + tx_amsdu_timeout = AGGR_TX_TIMEOUT; + aggr->tx_amsdu_timeout = tx_amsdu_timeout; + + if (tx_amsdu_max_pdu_len == 0) + tx_amsdu_max_pdu_len = AGGR_TX_MAX_PDU_SIZE; + else if (tx_amsdu_max_pdu_len < AGGR_TX_MIN_PDU_SIZE) + tx_amsdu_max_pdu_len = AGGR_TX_MIN_PDU_SIZE; + if (tx_amsdu_max_pdu_len > (aggr->tx_amsdu_max_aggr_len / 2)) + tx_amsdu_max_pdu_len = + (aggr->tx_amsdu_max_aggr_len / 2); + aggr->tx_amsdu_max_pdu_len = tx_amsdu_max_pdu_len; + + if (tx_amsdu_max_aggr_num == 0) + tx_amsdu_max_aggr_num = AGGR_TX_MAX_NUM; + else if (tx_amsdu_max_aggr_num < 2) + tx_amsdu_max_aggr_num = 2; + aggr->tx_amsdu_max_aggr_num = tx_amsdu_max_aggr_num; + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, + "%s: aggr-conf, vif%d, pdu_len=%d, aggr_num=%d timeout=%d, seq_pkt=%d, prog=%d\n", + __func__, + vif->fw_vif_idx, + aggr->tx_amsdu_max_pdu_len, + aggr->tx_amsdu_max_aggr_num, + aggr->tx_amsdu_timeout, + aggr->tx_amsdu_seq_pkt, + aggr->tx_amsdu_progressive); + } + + return; +} + +void aggr_config(struct ath6kl_vif *vif, + u16 rx_aggr_timeout) +{ + if ((vif) && + (vif->aggr_cntxt)) { + struct aggr_info *aggr = vif->aggr_cntxt; + + if (rx_aggr_timeout == 0) + rx_aggr_timeout = AGGR_RX_TIMEOUT; + aggr->rx_aggr_timeout = rx_aggr_timeout; + } + + return; +} + +struct aggr_info *aggr_init(struct ath6kl_vif *vif) +{ + struct aggr_info *aggr = NULL; + + aggr = kzalloc(sizeof(struct aggr_info), GFP_KERNEL); + if (!aggr) { + ath6kl_err("failed to alloc memory for aggr_module\n"); + return NULL; + } + + aggr->vif = vif; + + skb_queue_head_init(&aggr->free_q); + ath6kl_alloc_netbufs(&aggr->free_q, AGGR_NUM_OF_FREE_NETBUFS); + aggr->rx_aggr_timeout = AGGR_RX_TIMEOUT; + + aggr->tx_amsdu_enable = true; + aggr->tx_amsdu_seq_pkt = true; +#ifdef CONFIG_ANDROID + aggr->tx_amsdu_progressive = false; +#else + aggr->tx_amsdu_progressive = true; +#endif + aggr->tx_amsdu_max_aggr_num = AGGR_TX_MAX_NUM; + aggr->tx_amsdu_max_aggr_len = AGGR_TX_MAX_AGGR_SIZE - 100; + aggr->tx_amsdu_max_pdu_len = AGGR_TX_MAX_PDU_SIZE; + aggr->tx_amsdu_timeout = AGGR_TX_TIMEOUT; + + /* Always enable host-based A-MSDU. */ + set_bit(AMSDU_ENABLED, &vif->flags); + + return aggr; +} + +struct aggr_conn_info *aggr_init_conn(struct ath6kl_vif *vif) +{ + struct aggr_conn_info *aggr_conn = NULL; + struct rxtid *rxtid; + struct txtid *txtid; + u8 i; + + aggr_conn = kzalloc(sizeof(struct aggr_conn_info), GFP_KERNEL); + if (!aggr_conn) { + ath6kl_err("failed to alloc memory for aggr_node\n"); + return NULL; + } + + aggr_conn->aggr_sz = AGGR_SZ_DEFAULT; + aggr_conn->aggr_cntxt = vif->aggr_cntxt; + aggr_conn->dev = vif->ndev; + + for (i = 0; i < NUM_OF_TIDS; i++) { + rxtid = AGGR_GET_RXTID(aggr_conn, i); + rxtid->aggr = false; + skb_queue_head_init(&rxtid->q); + spin_lock_init(&rxtid->lock); + rxtid->aggr_conn = aggr_conn; + rxtid->tid = i; + init_timer(&rxtid->tid_timer); + rxtid->tid_timer.function = aggr_timeout; + rxtid->tid_timer.data = (unsigned long) rxtid; + rxtid->tid_timer_scheduled = false; + + switch (up_to_ac[i]) { + case WMM_AC_BK: + aggr_conn->tid_timeout_setting[i] = AGGR_RX_TIMEOUT; + break; + case WMM_AC_BE: + aggr_conn->tid_timeout_setting[i] = AGGR_RX_TIMEOUT; + break; + case WMM_AC_VI: + aggr_conn->tid_timeout_setting[i] = AGGR_RX_TIMEOUT; + break; + case WMM_AC_VO: + aggr_conn->tid_timeout_setting[i] = AGGR_RX_TIMEOUT_VO; + break; + } + + /* TX A-MSDU */ + txtid = AGGR_GET_TXTID(aggr_conn, i); + txtid->tid = i; + txtid->vif = vif; + init_timer(&txtid->timer); + txtid->timer.function = aggr_tx_timeout; + txtid->timer.data = (unsigned long)txtid; + spin_lock_init(&txtid->lock); + + } + + return aggr_conn; +} + +void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid, u8 initiator) +{ + struct aggr_conn_info *aggr_conn; + struct rxtid *rxtid; + struct txtid *txtid; + struct ath6kl_sta *conn; + u8 conn_tid, conn_aid; + + conn_tid = AGGR_BA_EVT_GET_TID(tid); + conn_aid = AGGR_BA_EVT_GET_CONNID(tid); + conn = ath6kl_find_sta_by_aid(vif, conn_aid); + + if (conn != NULL) { + WARN_ON(!conn->aggr_conn_cntxt); + + aggr_conn = conn->aggr_conn_cntxt; + if (initiator == 1) { + /* no aggr tx */ + txtid = AGGR_GET_TXTID(aggr_conn, conn_tid); + if (txtid) + aggr_tx_delete_tid_state(aggr_conn, conn_tid); + } else { + rxtid = AGGR_GET_RXTID(aggr_conn, conn_tid); + if (rxtid->aggr) + aggr_delete_tid_state(aggr_conn, conn_tid); + } + } +} + +void aggr_reset_state(struct aggr_conn_info *aggr_conn) +{ + struct ath6kl_vif *vif = aggr_conn->aggr_cntxt->vif; + u8 tid; + + for (tid = 0; tid < NUM_OF_TIDS; tid++) { + aggr_delete_tid_state(aggr_conn, tid); + aggr_tx_delete_tid_state(aggr_conn, tid); + } + + if (vif->nw_type != AP_NETWORK) + aggr_conn->aggr_cntxt->tx_amsdu_enable = false; + + ath6kl_dbg(ATH6KL_DBG_WLAN_TX_AMSDU, "%s: tx_amsdu_enable %d\n", + __func__, aggr_conn->aggr_cntxt->tx_amsdu_enable); + + return; +} + +/* clean up our amsdu buffer list */ +void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar) +{ + struct htc_packet *packet, *tmp_pkt; + + spin_lock_bh(&ar->lock); + if (list_empty(&ar->amsdu_rx_buffer_queue)) { + spin_unlock_bh(&ar->lock); + return; + } + + list_for_each_entry_safe(packet, tmp_pkt, &ar->amsdu_rx_buffer_queue, + list) { + list_del(&packet->list); + spin_unlock_bh(&ar->lock); + dev_kfree_skb(packet->pkt_cntxt); + spin_lock_bh(&ar->lock); + } + + spin_unlock_bh(&ar->lock); +} + +void aggr_module_destroy(struct aggr_info *aggr) +{ + if (!aggr) + return; + + skb_queue_purge(&aggr->free_q); + kfree(aggr); +} + +void aggr_module_destroy_conn(struct aggr_conn_info *aggr_conn) +{ + struct rxtid *rxtid; + struct txtid *txtid; + u8 i, k; + + if (!aggr_conn) + return; + + for (i = 0; i < NUM_OF_TIDS; i++) { + rxtid = AGGR_GET_RXTID(aggr_conn, i); + + if (rxtid->tid_timer_scheduled) { + del_timer(&rxtid->tid_timer); + rxtid->tid_timer_scheduled = false; + rxtid->continuous_count = 0; + } + + if (rxtid->hold_q) { + for (k = 0; k < rxtid->hold_q_sz; k++) + dev_kfree_skb(rxtid->hold_q[k].skb); + kfree(rxtid->hold_q); + } + skb_queue_purge(&rxtid->q); + + /* TX A-MSDU */ + txtid = AGGR_GET_TXTID(aggr_conn, i); + spin_lock_bh(&txtid->lock); + aggr_tx_reset_aggr(txtid, true, true); + spin_unlock_bh(&txtid->lock); + } + + kfree(aggr_conn); +} + +void ath6kl_indicate_wmm_schedule_change(void *devt, bool change) +{ + struct ath6kl *ar = devt; + int change_for_stream_pri = 0; + + change_for_stream_pri = + ath6kl_htc_wmm_schedule_change(ar->htc_target, change); + + if (change_for_stream_pri != 0) { + if (change == true) { + /* change the priority order for BE and VI */ + ar->ac_stream_pri_map[WMM_AC_BE] = 2; + ar->ac_stream_pri_map[WMM_AC_VI] = 1; + } else { + ar->ac_stream_pri_map[WMM_AC_BE] = 1; + ar->ac_stream_pri_map[WMM_AC_VI] = 2; + } + } +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/usb.c b/drivers/net/wireless/ath/ath6kl-3.5/usb.c new file mode 100644 index 000000000000..e5aabc967eef --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/usb.c @@ -0,0 +1,2631 @@ +/* + * Copyright (c) 2007-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "debug.h" +#include "core.h" +#include "cfg80211.h" + +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 +#include +#endif + +/* constants */ +#define TX_URB_COUNT 10 +#define RX_URB_COUNT 32 + +#define ATH6KL_USB_RX_BUFFER_SIZE 2048 +#define ATH6KL_USB_RX_BUNDLE_BUFFER_SIZE 16896 +#define ATH6KL_USB_TX_BUNDLE_BUFFER_SIZE 16384 +#define WORKER_LOCK_BIT 0 + +#define ATH6KL_MAX_AMSDU_SIZE 7935 + +/* tx/rx pipes for usb */ +enum ATH6KL_USB_PIPE_ID { + ATH6KL_USB_PIPE_TX_CTRL = 0, + ATH6KL_USB_PIPE_TX_DATA_LP, + ATH6KL_USB_PIPE_TX_DATA_MP, + ATH6KL_USB_PIPE_TX_DATA_HP, + ATH6KL_USB_PIPE_TX_DATA_VHP, + ATH6KL_USB_PIPE_RX_CTRL, + ATH6KL_USB_PIPE_RX_DATA, + ATH6KL_USB_PIPE_RX_DATA2, + ATH6KL_USB_PIPE_RX_INT, + ATH6KL_USB_PIPE_MAX +}; + +#define ATH6KL_USB_PIPE_INVALID ATH6KL_USB_PIPE_MAX + +struct ath6kl_usb_pipe_stat { + u32 num_rx_comp; + u32 num_tx_comp; + u32 num_io_comp; + + u32 num_max_tx; + u32 num_max_rx; + + u32 num_tx_resche; + u32 num_rx_resche; + + u32 num_tx_sync; + u32 num_tx; + u32 num_tx_err; + u32 num_tx_err_others; + + u32 num_tx_comp_err; + u32 num_rx_comp_err; + + u32 num_rx_bundle_comp; + u32 num_tx_bundle_comp; + + u32 num_tx_multi; + u32 num_tx_multi_err; + u32 num_tx_multi_err_others; + + u32 num_rx_bundle_comp_err; + u32 num_tx_bundle_comp_err; +}; + +struct ath6kl_usb_pipe { + struct list_head urb_list_head; + struct usb_anchor urb_submitted; + u32 urb_alloc; + u32 urb_cnt; + u32 urb_cnt_thresh; + unsigned int usb_pipe_handle; + u32 flags; + u8 ep_address; + u8 logical_pipe_num; + struct ath6kl_usb *ar_usb; + u16 max_packet_size; + struct work_struct io_complete_work; + struct sk_buff_head io_comp_queue; + struct usb_endpoint_descriptor *ep_desc; + struct ath6kl_usb_pipe_stat usb_pipe_stat; + unsigned long worker_lock; +}; + +#define ATH6KL_USB_PIPE_FLAG_TX (1 << 0) + +/* usb device object */ +struct ath6kl_usb { + spinlock_t cs_lock; + spinlock_t tx_lock; + spinlock_t rx_lock; + struct ath6kl_hif_pipe_callbacks htc_callbacks; + struct usb_device *udev; + struct usb_interface *interface; + struct ath6kl_usb_pipe pipes[ATH6KL_USB_PIPE_MAX]; + u8 *diag_cmd_buffer; + u8 *diag_resp_buffer; + struct ath6kl *ar; + struct notifier_block reboot_notifier; /* default mode before reboot */ + u32 max_sche_tx; + u32 max_sche_rx; +}; + +/* usb urb object */ +struct ath6kl_urb_context { + struct list_head link; + struct ath6kl_usb_pipe *pipe; + struct sk_buff *buf; + struct ath6kl *ar; + struct sk_buff_head comp_queue; +}; + +/* USB endpoint definitions */ +#define ATH6KL_USB_EP_ADDR_APP_CTRL_IN 0x81 +#define ATH6KL_USB_EP_ADDR_APP_DATA_IN 0x82 +#define ATH6KL_USB_EP_ADDR_APP_DATA2_IN 0x83 +#define ATH6KL_USB_EP_ADDR_APP_INT_IN 0x84 + +#define ATH6KL_USB_EP_ADDR_APP_CTRL_OUT 0x01 +#define ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT 0x02 +#define ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT 0x03 +#define ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT 0x04 +#define ATH6KL_USB_EP_ADDR_APP_DATA_VHP_OUT 0x05 + +/* diagnostic command defnitions */ +#define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD 1 +#define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP 2 +#define ATH6KL_USB_CONTROL_REQ_DIAG_CMD 3 +#define ATH6KL_USB_CONTROL_REQ_DIAG_RESP 4 + +#define ATH6KL_USB_CTRL_DIAG_CC_READ 0 +#define ATH6KL_USB_CTRL_DIAG_CC_WRITE 1 +#define ATH6KL_USB_CTRL_DIAG_CC_WARM_RESET 2 + +/* Enable it by default */ +#define HIF_USB_MAX_SCHE_PKT (64) + +struct ath6kl_usb_ctrl_diag_cmd_write { + __le32 cmd; + __le32 address; + __le32 value; + __le32 _pad[1]; +} __packed; + +struct ath6kl_usb_ctrl_diag_cmd_read { + __le32 cmd; + __le32 address; +} __packed; + +struct ath6kl_usb_ctrl_diag_resp_read { + __le32 value; +} __packed; + +#define USB_CTRL_MAX_DIAG_CMD_SIZE \ + (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)) +#define USB_CTRL_MAX_DIAG_RESP_SIZE \ + (sizeof(struct ath6kl_usb_ctrl_diag_resp_read)) + +#ifdef ATHTST_SUPPORT +struct hif_product_info_t { + uint16_t idVendor; + uint16_t idProduct; + uint8_t product[64]; + uint8_t manufacturer[64]; + uint8_t serial[64]; +}; +#endif +/* function declarations */ +#ifdef CONFIG_PM + +static int ath6kl_usb_pm_suspend(struct usb_interface *interface, + pm_message_t message); +static int ath6kl_usb_pm_resume(struct usb_interface *interface); +static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf); + +#else + +#define ath6kl_usb_pm_suspend NULL +#define ath6kl_usb_pm_resume NULL +#define ath6kl_usb_pm_reset_resume NULL + +#endif + +#ifdef CONFIG_ANDROID +/* variables for unload driver module */ +#define ATH6KL_USB_UNLOAD_TIMEOUT (2*HZ) +enum ath6kl_usb_drv_unload_state { + ATH6KL_USB_UNLOAD_STATE_NULL = 0, + ATH6KL_USB_UNLOAD_STATE_DRV_DEREG, + ATH6KL_USB_UNLOAD_STATE_TARGET_RESET, + ATH6KL_USB_UNLOAD_STATE_DEV_DISCONNECTED, +}; + +static int ath6kl_usb_unload_dev_num = -1; +static wait_queue_head_t ath6kl_usb_unload_event_wq; +static atomic_t ath6kl_usb_unload_state; +#endif + +static void ath6kl_usb_recv_complete(struct urb *urb); +static void ath6kl_usb_recv_bundle_complete(struct urb *urb); + +#ifdef USB_AUTO_SUSPEND +static void usb_auto_pm_turnoff(struct ath6kl *ar); +#endif + +#define ATH6KL_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02) +#define ATH6KL_USB_IS_INT_EP(attr) (((attr) & 3) == 0x03) +#define ATH6KL_USB_IS_ISOC_EP(attr) (((attr) & 3) == 0x01) +#define ATH6KL_USB_IS_DIR_IN(addr) ((addr) & 0x80) + +/* pipe/urb operations */ +static struct ath6kl_urb_context *ath6kl_usb_alloc_urb_from_pipe( + struct ath6kl_usb_pipe *pipe) +{ + struct ath6kl_urb_context *urb_context = NULL; + unsigned long flags; + + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); + if (!list_empty(&pipe->urb_list_head)) { + urb_context = + list_first_entry(&pipe->urb_list_head, + struct ath6kl_urb_context, link); + list_del(&urb_context->link); + pipe->urb_cnt--; + } + spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); + + return urb_context; +} + +static void ath6kl_usb_free_urb_to_pipe(struct ath6kl_usb_pipe *pipe, + struct ath6kl_urb_context *urb_context) +{ + unsigned long flags; + + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); + pipe->urb_cnt++; + + list_add(&urb_context->link, &pipe->urb_list_head); + spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); +} + +static void ath6kl_usb_cleanup_recv_urb(struct ath6kl_urb_context *urb_context) +{ + if (urb_context->buf != NULL) { + dev_kfree_skb(urb_context->buf); + urb_context->buf = NULL; + } + + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); +} + +static inline struct ath6kl_usb *ath6kl_usb_priv(struct ath6kl *ar) +{ + return ar->hif_priv; +} + +/* pipe resource allocation/cleanup */ +static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe, + int urb_cnt) +{ + int status = 0; + int i; + struct ath6kl_urb_context *urb_context; + + INIT_LIST_HEAD(&pipe->urb_list_head); + init_usb_anchor(&pipe->urb_submitted); + + for (i = 0; i < urb_cnt; i++) { + urb_context = (struct ath6kl_urb_context *) + kzalloc(sizeof(struct ath6kl_urb_context), GFP_KERNEL); + if (urb_context == NULL) { + status = -ENOMEM; + goto fail_alloc_pipe_resources; + } + + memset(urb_context, 0, sizeof(struct ath6kl_urb_context)); + urb_context->pipe = pipe; + + /* + * we are only allocate the urb contexts here, the actual URB + * is allocated from the kernel as needed to do a transaction + */ + pipe->urb_alloc++; + + if (htc_bundle_send) { + /* In tx bundle mode, only pre-allocate bundle buffers + * for data pipes + */ + if (pipe->logical_pipe_num >= ATH6KL_USB_PIPE_TX_DATA_LP && + pipe->logical_pipe_num <= ATH6KL_USB_PIPE_TX_DATA_VHP) { + urb_context->buf = + dev_alloc_skb(ATH6KL_USB_TX_BUNDLE_BUFFER_SIZE); + if (NULL == urb_context->buf) + ath6kl_dbg(ATH6KL_DBG_USB, + "athusb: alloc send bundle buffer %d-byte " + "failed\n", + ATH6KL_USB_TX_BUNDLE_BUFFER_SIZE); + } + skb_queue_head_init(&urb_context->comp_queue); + } + + ath6kl_usb_free_urb_to_pipe(pipe, urb_context); + } + ath6kl_dbg(ATH6KL_DBG_USB, + "ath6kl usb: alloc resources lpipe:%d" + "hpipe:0x%X urbs:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc); + +fail_alloc_pipe_resources: + return status; +} + +static void ath6kl_usb_free_pipe_resources(struct ath6kl_usb_pipe *pipe) +{ + struct ath6kl_urb_context *urb_context; + struct sk_buff *buf = NULL; + + if (pipe->ar_usb == NULL) { + /* nothing allocated for this pipe */ + return; + } + + ath6kl_dbg(ATH6KL_DBG_USB, + "ath6kl usb: free resources lpipe:%d" + "hpipe:0x%X urbs:%d avail:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc, pipe->urb_cnt); + + if (pipe->urb_alloc != pipe->urb_cnt) { + ath6kl_dbg(ATH6KL_DBG_USB, + "ath6kl usb: urb leak! lpipe:%d" + "hpipe:0x%X urbs:%d avail:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc, pipe->urb_cnt); + } + + while (true) { + urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); + if (urb_context == NULL) + break; + + if (htc_bundle_send) { + while ((buf = skb_dequeue(&urb_context->comp_queue)) + != NULL) + dev_kfree_skb(buf); + } + + kfree(urb_context); + } + +} + +static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *device) +{ + int i; + + for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) + ath6kl_usb_free_pipe_resources(&device->pipes[i]); + +} + +static u8 ath6kl_usb_get_logical_pipe_num(struct ath6kl_usb *device, + u8 ep_address, int *urb_count) +{ + u8 pipe_num = ATH6KL_USB_PIPE_INVALID; + + switch (ep_address) { + case ATH6KL_USB_EP_ADDR_APP_CTRL_IN: + pipe_num = ATH6KL_USB_PIPE_RX_CTRL; + *urb_count = RX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_IN: + pipe_num = ATH6KL_USB_PIPE_RX_DATA; + *urb_count = RX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_INT_IN: + pipe_num = ATH6KL_USB_PIPE_RX_INT; + *urb_count = RX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA2_IN: + pipe_num = ATH6KL_USB_PIPE_RX_DATA2; + *urb_count = RX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_CTRL_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_CTRL; + *urb_count = TX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_DATA_LP; + *urb_count = TX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_DATA_MP; + *urb_count = TX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_DATA_HP; + *urb_count = TX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_VHP_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_DATA_VHP; + *urb_count = TX_URB_COUNT; + break; + default: + /* note: there may be endpoints not currently used */ + break; + } + + return pipe_num; +} + +static int ath6kl_usb_setup_pipe_resources(struct ath6kl_usb *device) +{ + struct usb_interface *interface = device->interface; + struct usb_host_interface *iface_desc = interface->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + int i; + int urbcount; + int status = 0; + struct ath6kl_usb_pipe *pipe; + u8 pipe_num; + ath6kl_dbg(ATH6KL_DBG_USB, "setting up USB Pipes using interface\n"); + /* walk decriptors and setup pipes */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) { + ath6kl_dbg(ATH6KL_DBG_USB, + "%s Bulk Ep:0x%2.2X maxpktsz:%d\n", + ATH6KL_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize)); + } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) { + ath6kl_dbg(ATH6KL_DBG_USB, + "%s Int Ep:0x%2.2X maxpktsz:%d interval:%d\n", + ATH6KL_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize), + endpoint->bInterval); + } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) { + /* TODO for ISO */ + ath6kl_dbg(ATH6KL_DBG_USB, + "%s ISOC Ep:0x%2.2X maxpktsz:%d interval:%d\n", + ATH6KL_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize), + endpoint->bInterval); + } + urbcount = 0; + + pipe_num = + ath6kl_usb_get_logical_pipe_num(device, + endpoint->bEndpointAddress, + &urbcount); + if (pipe_num == ATH6KL_USB_PIPE_INVALID) + continue; + + pipe = &device->pipes[pipe_num]; + if (pipe->ar_usb != NULL) { + /* hmmm..pipe was already setup */ + continue; + } + + pipe->ar_usb = device; + pipe->logical_pipe_num = pipe_num; + pipe->ep_address = endpoint->bEndpointAddress; + pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize); + + if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) { + if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvbulkpipe(device->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndbulkpipe(device->udev, + pipe->ep_address); + } + } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) { + if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvintpipe(device->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndintpipe(device->udev, + pipe->ep_address); + } + } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) { + /* TODO for ISO */ + if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvisocpipe(device->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndisocpipe(device->udev, + pipe->ep_address); + } + } + pipe->ep_desc = endpoint; + + if (!ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) + pipe->flags |= ATH6KL_USB_PIPE_FLAG_TX; + + status = ath6kl_usb_alloc_pipe_resources(pipe, urbcount); + if (status != 0) + break; + + } + + return status; +} + +/* pipe operations */ +static void ath6kl_usb_post_recv_transfers(struct ath6kl_usb_pipe *recv_pipe, + int buffer_length) +{ + struct ath6kl_urb_context *urb_context; + u8 *data; + u32 len; + struct urb *urb; + int usb_status; + + while (1) { + + urb_context = ath6kl_usb_alloc_urb_from_pipe(recv_pipe); + if (urb_context == NULL) + break; + + urb_context->buf = dev_alloc_skb(buffer_length); + if (urb_context->buf == NULL) + goto err_cleanup_urb; + + data = urb_context->buf->data; + len = urb_context->buf->len; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) + goto err_cleanup_urb; + + usb_fill_bulk_urb(urb, + recv_pipe->ar_usb->udev, + recv_pipe->usb_pipe_handle, + data, + buffer_length, + ath6kl_usb_recv_complete, urb_context); + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb: bulk recv submit:%d, 0x%X" + "(ep:0x%2.2X), %d bytes buf:0x%p\n", + recv_pipe->logical_pipe_num, + recv_pipe->usb_pipe_handle, recv_pipe->ep_address, + buffer_length, urb_context->buf); + + usb_anchor_urb(urb, &recv_pipe->urb_submitted); + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + + if (usb_status) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb : usb bulk recv failed %d\n", + usb_status); + usb_unanchor_urb(urb); + usb_free_urb(urb); + goto err_cleanup_urb; + } + usb_free_urb(urb); + } + return; + +err_cleanup_urb: + ath6kl_usb_cleanup_recv_urb(urb_context); + return; +} + +static void hif_usb_post_recv_bundle_transfers( + struct ath6kl_usb_pipe *recv_pipe, + int buffer_length) +{ + struct ath6kl_urb_context *urb_context; + u8 *data; + u32 len; + struct urb *urb; + int usb_status; + + while (1) { + urb_context = ath6kl_usb_alloc_urb_from_pipe(recv_pipe); + if (urb_context == NULL) + break; + if (buffer_length) { + urb_context->buf = dev_alloc_skb(buffer_length); + if (urb_context->buf == NULL) { + ath6kl_usb_cleanup_recv_urb(urb_context); + break; + } + } + + data = urb_context->buf->data; + len = urb_context->buf->len; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) { + ath6kl_usb_cleanup_recv_urb(urb_context); + break; + } + + usb_fill_bulk_urb(urb, + recv_pipe->ar_usb->udev, + recv_pipe->usb_pipe_handle, + data, + ATH6KL_USB_RX_BUNDLE_BUFFER_SIZE, + ath6kl_usb_recv_bundle_complete, urb_context); + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb: bulk recv submit:%d, 0x%X" + "(ep:0x%2.2X), %d bytes buf:0x%p\n", + recv_pipe->logical_pipe_num, + recv_pipe->usb_pipe_handle, recv_pipe->ep_address, + buffer_length, urb_context->buf); + + usb_anchor_urb(urb, &recv_pipe->urb_submitted); + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + + if (usb_status) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb : usb bulk recv failed %d\n", + usb_status); + usb_unanchor_urb(urb); + usb_free_urb(urb); + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + break; + } + usb_free_urb(urb); + } + return; +} + + +static void ath6kl_usb_flush_all(struct ath6kl_usb *device) +{ + int i; + struct ath6kl_usb_pipe *pipe; + + for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { + /* flush only USB scheduled work, instead of flushing all */ + if (device->pipes[i].ar_usb) { + if (&device->pipes[i].urb_submitted) + usb_kill_anchored_urbs( + &device->pipes[i].urb_submitted); + pipe = &device->pipes[i].ar_usb->pipes[i]; + if (pipe) + flush_work(&pipe->io_complete_work); + } + } + + /* flushing any pending I/O may schedule work + * this call will block until all scheduled work runs to completion */ + /* flush_scheduled_work(); */ +} + +static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *device) +{ + /* + * note: control pipe is no longer used + * device->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_cnt_thresh = + * device->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_alloc/2; + * ath6kl_usb_post_recv_transfers(&device-> + * pipes[ATH6KL_USB_PIPE_RX_CTRL], + * ATH6KL_USB_RX_BUFFER_SIZE); + */ + + device->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = + device->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_alloc / 2; + + if (!htc_bundle_recv) + ath6kl_usb_post_recv_transfers( + &device->pipes[ATH6KL_USB_PIPE_RX_DATA], + ATH6KL_USB_RX_BUFFER_SIZE); + else + hif_usb_post_recv_bundle_transfers( + &device->pipes[ATH6KL_USB_PIPE_RX_DATA], + ATH6KL_USB_RX_BUNDLE_BUFFER_SIZE); + /* + * Disable rxdata2 directly, it will be enabled + * if FW enable rxdata2 + */ + if (0) { + device->pipes[ATH6KL_USB_PIPE_RX_DATA2].urb_cnt_thresh = + device->pipes[ATH6KL_USB_PIPE_RX_DATA2].urb_alloc / 2; + ath6kl_usb_post_recv_transfers( + &device->pipes[ATH6KL_USB_PIPE_RX_DATA2], + ATH6KL_USB_RX_BUFFER_SIZE); + } +} + +/* hif usb rx/tx completion functions */ +static void ath6kl_usb_recv_complete(struct urb *urb) +{ + struct ath6kl_urb_context *urb_context = + (struct ath6kl_urb_context *)urb->context; + int status = 0; + struct sk_buff *buf = NULL; + struct ath6kl_usb_pipe *pipe = urb_context->pipe; + struct ath6kl_usb_pipe_stat *pipe_st = &pipe->usb_pipe_stat; +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + struct ath6kl *ar = pipe->ar_usb->ar; +#endif + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s: recv pipe: %d, stat:%d, len:%d urb:0x%p\n", __func__, + pipe->logical_pipe_num, urb->status, urb->actual_length, + urb); + + if (urb->status != 0) { + pipe_st->num_rx_comp_err++; + status = -EIO; + switch (urb->status) { + case -EOVERFLOW: + urb->actual_length = ATH6KL_USB_RX_BUFFER_SIZE; + status = 0; + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* + * no need to spew these errors when device + * removed or urb killed due to driver shutdown + */ + status = -ECANCELED; + break; + default: + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n", + __func__, pipe->logical_pipe_num, + pipe->ep_address, urb->status); + break; + } + goto cleanup_recv_urb; + } + if (urb->actual_length == 0) + goto cleanup_recv_urb; + + buf = urb_context->buf; + /* we are going to pass it up */ + urb_context->buf = NULL; + skb_put(buf, urb->actual_length); + /* note: queue implements a lock */ + skb_queue_tail(&pipe->io_comp_queue, buf); + pipe_st->num_rx_comp++; +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + queue_work(ar->ath6kl_wq, &pipe->io_complete_work); +#else + schedule_work(&pipe->io_complete_work); +#endif + +cleanup_recv_urb: + ath6kl_usb_cleanup_recv_urb(urb_context); + + if (status == 0 || urb->status == -EPROTO) { + if (pipe->urb_cnt >= pipe->urb_cnt_thresh) { + /* our free urbs are piling up, post more transfers */ + ath6kl_usb_post_recv_transfers(pipe, + ATH6KL_USB_RX_BUFFER_SIZE); + } + } + return; +} + +static void ath6kl_usb_recv_bundle_complete(struct urb *urb) +{ + struct ath6kl_urb_context *urb_context = + (struct ath6kl_urb_context *)urb->context; + int status = 0; + struct sk_buff *buf = NULL; + struct ath6kl_usb_pipe *pipe = urb_context->pipe; + struct ath6kl_usb_pipe_stat *pipe_st = &pipe->usb_pipe_stat; + u8 *netdata, *netdata_new; + u32 netlen, netlen_new; + struct htc_frame_hdr *htc_hdr; + u16 payload_len; + struct sk_buff *new_skb = NULL; + struct ath6kl *ar = pipe->ar_usb->ar; + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s: recv pipe: %d, stat:%d, len:%d urb:0x%p\n", __func__, + pipe->logical_pipe_num, urb->status, urb->actual_length, + urb); + + do { + + if (urb->status != 0) { + pipe_st->num_rx_bundle_comp_err++; + status = -EIO; + switch (urb->status) { + case -EOVERFLOW: + urb->actual_length = + ATH6KL_USB_RX_BUNDLE_BUFFER_SIZE; + status = 0; + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* + * no need to spew these errors when device + * removed or urb killed due to driver shutdown + */ + status = -ECANCELED; + break; + default: + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n", + __func__, pipe->logical_pipe_num, + pipe->ep_address, urb->status); + } + break; + } + if (urb->actual_length == 0) + break; + buf = urb_context->buf; + + netdata = buf->data; + netlen = buf->len; + netlen = urb->actual_length; + + do { + u8 extra_pad = 0; + u16 act_frame_len = 0; + u16 frame_len; + + /* Hack into HTC header for bundle processing */ + htc_hdr = (struct htc_frame_hdr *)netdata; + if (htc_hdr->eid >= ENDPOINT_MAX) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb: Rx: invalid eid=%d\n", htc_hdr->eid); + break; + } + + payload_len = + le16_to_cpu(get_unaligned((u16 *)&htc_hdr->payld_len)); + + if (ar->hw.flags & ATH6KL_HW_TGT_ALIGN_PADDING) { + act_frame_len = (HTC_HDR_LENGTH + payload_len); + + if (htc_hdr->eid == 0 || htc_hdr->eid == 1) + /* assumption: target won't pad on + * HTC endpoint 0 & 1. + */ + extra_pad = 0; + else + extra_pad = + get_unaligned((u8 *)&htc_hdr->ctrl[1]); + } + + if (payload_len > ATH6KL_MAX_AMSDU_SIZE) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb: payload_len too long %u\n", + payload_len); + break; + } + + if (ar->hw.flags & ATH6KL_HW_TGT_ALIGN_PADDING) + frame_len = (act_frame_len + extra_pad); + else + frame_len = (HTC_HDR_LENGTH + payload_len); + + if (netlen >= frame_len) { + /* allocate a new skb and copy */ + if (ar->hw.flags & + ATH6KL_HW_TGT_ALIGN_PADDING) { + new_skb = dev_alloc_skb(act_frame_len); + if (new_skb == NULL) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb: allocate skb (len=%u) " + "failed\n", act_frame_len); + break; + } + + netdata_new = new_skb->data; + netlen_new = new_skb->len; + memcpy(netdata_new, + netdata, act_frame_len); + skb_put(new_skb, act_frame_len); + } else { + new_skb = dev_alloc_skb(frame_len); + if (new_skb == NULL) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb: allocate skb (len=%u) " + "failed\n", frame_len); + break; + } + + netdata_new = new_skb->data; + netlen_new = new_skb->len; + memcpy(netdata_new, netdata, frame_len); + skb_put(new_skb, frame_len); + } + + skb_queue_tail(&pipe->io_comp_queue, new_skb); + new_skb = NULL; + + netdata += frame_len; + netlen -= frame_len; + } else { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb: subframe length %d not fitted " + "into bundle packet length %d\n", + netlen, frame_len); + break; + } + } while (netlen); + + pipe_st->num_rx_bundle_comp++; +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + queue_work(ar->ath6kl_wq, &pipe->io_complete_work); +#else + schedule_work(&pipe->io_complete_work); +#endif + + } while (0); + + if (urb_context->buf == NULL) + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb: buffer in urb_context is NULL\n"); + + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); + + if (status == 0 || urb->status == -EPROTO) { + if (pipe->urb_cnt >= pipe->urb_cnt_thresh) + /* our free urbs are piling up, post more transfers */ + hif_usb_post_recv_bundle_transfers(pipe, + 0 /* pass zero for not allocating urb-buffer again */); + } + return; +} + + +static void ath6kl_usb_usb_transmit_complete(struct urb *urb) +{ + struct ath6kl_urb_context *urb_context = + (struct ath6kl_urb_context *)urb->context; + struct sk_buff *buf; + struct ath6kl_usb_pipe *pipe = urb_context->pipe; + struct ath6kl_usb_pipe_stat *pipe_st = &pipe->usb_pipe_stat; +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + struct ath6kl *ar = pipe->ar_usb->ar; +#endif + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s: pipe: %d, stat:%d, len:%d\n", + __func__, pipe->logical_pipe_num, urb->status, + urb->actual_length); + + if (urb->status != 0) { + pipe_st->num_tx_comp_err++; + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s: pipe: %d, failed:%d\n", + __func__, pipe->logical_pipe_num, urb->status); + } + + buf = urb_context->buf; + urb_context->buf = NULL; + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); + + /* note: queue implements a lock */ + skb_queue_tail(&pipe->io_comp_queue, buf); + pipe_st->num_tx_comp++; +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + queue_work(ar->ath6kl_wq, &pipe->io_complete_work); +#else + schedule_work(&pipe->io_complete_work); +#endif +} + +static void ath6kl_usb_io_comp_work(struct work_struct *work) +{ + struct ath6kl_usb_pipe *pipe = + container_of(work, struct ath6kl_usb_pipe, io_complete_work); + struct sk_buff *buf; + struct ath6kl_usb *device; + struct ath6kl_usb_pipe_stat *pipe_st = &pipe->usb_pipe_stat; + u32 tx = 0, rx = 0; +#if defined(CE_OLD_KERNEL_SUPPORT_2_6_23) || defined(USB_AUTO_SUSPEND) + struct ath6kl *ar = pipe->ar_usb->ar; +#endif + + pipe_st->num_io_comp++; + device = pipe->ar_usb; + + if (test_and_set_bit(WORKER_LOCK_BIT, &pipe->worker_lock)) + return; + + while ((buf = skb_dequeue(&pipe->io_comp_queue))) { + +#ifdef USB_AUTO_SUSPEND + usb_mark_last_busy(device->udev); +#endif + if (pipe->flags & ATH6KL_USB_PIPE_FLAG_TX) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb xmit callback buf:0x%p\n", buf); + device->htc_callbacks. + tx_completion(device->ar->htc_target, buf); +#ifdef USB_AUTO_SUSPEND + spin_lock_bh(&ar->usb_pm_lock); + ath6kl_auto_pm_wakeup_resume(ar); + spin_unlock_bh(&ar->usb_pm_lock); +#endif + if (tx++ > device->max_sche_tx) { + clear_bit(WORKER_LOCK_BIT, &pipe->worker_lock); + pipe_st->num_tx_resche++; + goto reschedule; + } + } else { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb recv callback buf:0x%p\n", buf); + + if (!device->htc_callbacks.rx_completion) { + dev_kfree_skb(buf); + continue; + } + + device->htc_callbacks. + rx_completion(device->ar->htc_target, buf, + pipe->logical_pipe_num); + + if (rx++ > device->max_sche_rx) { + clear_bit(WORKER_LOCK_BIT, &pipe->worker_lock); + pipe_st->num_rx_resche++; + goto reschedule; + } + } + } + + clear_bit(WORKER_LOCK_BIT, &pipe->worker_lock); + + if (tx > pipe_st->num_max_tx) + pipe_st->num_max_tx = tx; + + if (rx > pipe_st->num_max_rx) + pipe_st->num_max_rx = rx; + + return; + +reschedule: + /* Re-schedule it to avoid one direction to starve another direction. */ + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb re-schedule work.\n"); +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + queue_work(ar->ath6kl_wq, &pipe->io_complete_work); +#else + schedule_work(&pipe->io_complete_work); +#endif + + return; +} + +#define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)) +#define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read)) + +static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb) +{ + unregister_reboot_notifier(&ar_usb->reboot_notifier); +#ifndef CE_OLD_KERNEL_SUPPORT_2_6_23 + ath6kl_usb_flush_all(ar_usb); +#endif + ath6kl_usb_cleanup_pipe_resources(ar_usb); + + usb_set_intfdata(ar_usb->interface, NULL); + + kfree(ar_usb->diag_cmd_buffer); + kfree(ar_usb->diag_resp_buffer); + + kfree(ar_usb); +} + +static int ath6kl_usb_reboot(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct ath6kl_usb *ar_usb; + struct ath6kl *ar ; + + ar_usb = container_of(nb, struct ath6kl_usb, reboot_notifier); + if (ar_usb == NULL) + return NOTIFY_DONE; + + ar = (struct ath6kl *) ar_usb->ar; + if (ar != NULL) + ath6kl_reset_device(ar, ar->target_type, true, true); + + return NOTIFY_DONE; +} + +static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface) +{ + struct ath6kl_usb *ar_usb = NULL; + struct usb_device *dev = interface_to_usbdev(interface); + struct ath6kl_usb_pipe *pipe; + int status = 0; + int i; + + ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL); + if (ar_usb == NULL) + goto fail_ath6kl_usb_create; + + memset(ar_usb, 0, sizeof(struct ath6kl_usb)); + usb_set_intfdata(interface, ar_usb); + spin_lock_init(&(ar_usb->cs_lock)); + spin_lock_init(&(ar_usb->rx_lock)); + spin_lock_init(&(ar_usb->tx_lock)); + ar_usb->udev = dev; + ar_usb->interface = interface; + +#ifdef CONFIG_ANDROID + ath6kl_usb_unload_dev_num = dev->devnum; +#endif + + for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { + pipe = &ar_usb->pipes[i]; + INIT_WORK(&pipe->io_complete_work, + ath6kl_usb_io_comp_work); + skb_queue_head_init(&pipe->io_comp_queue); + pipe->worker_lock = 0; + } + + ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL); + if (ar_usb->diag_cmd_buffer == NULL) { + status = -ENOMEM; + goto fail_ath6kl_usb_create; + } + + ar_usb->diag_resp_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_RESP, + GFP_KERNEL); + if (ar_usb->diag_resp_buffer == NULL) { + status = -ENOMEM; + goto fail_ath6kl_usb_create; + } + + ar_usb->max_sche_tx = + ar_usb->max_sche_rx = HIF_USB_MAX_SCHE_PKT; + + status = ath6kl_usb_setup_pipe_resources(ar_usb); + + ar_usb->reboot_notifier.notifier_call = ath6kl_usb_reboot; + register_reboot_notifier(&ar_usb->reboot_notifier); + +fail_ath6kl_usb_create: + if (status != 0) { + ath6kl_usb_destroy(ar_usb); + ar_usb = NULL; + } + return ar_usb; +} + +static void ath6kl_usb_device_detached(struct usb_interface *interface) +{ + struct ath6kl_usb *ar_usb; +#ifdef USB_AUTO_SUSPEND + struct usb_pm_skb_queue_t *entry, *p_usb_pm_skb_queue; + struct ath6kl *ar; +#endif + + ar_usb = usb_get_intfdata(interface); + if (ar_usb == NULL) + return; + + ath6kl_stop_txrx(ar_usb->ar); + + /* Delay to wait for the target to reboot */ +#ifdef CONFIG_ANDROID + if (atomic_read(&ath6kl_usb_unload_state) == + ATH6KL_USB_UNLOAD_STATE_DRV_DEREG) + atomic_set(&ath6kl_usb_unload_state, + ATH6KL_USB_UNLOAD_STATE_TARGET_RESET); +#else + mdelay(20); +#endif + +#ifdef USB_AUTO_SUSPEND + /* + * when packets are in pm_skb_queue, + * and usb remove before resume happens, + * we need to clean pm_skb_queue to avoid memory leak. + */ + + ar = ar_usb->ar; + p_usb_pm_skb_queue = &ar->usb_pm_skb_queue; + + while (get_queue_depth(&(p_usb_pm_skb_queue->list)) > 0) { + ath6kl_dbg(ATH6KL_DBG_USB, "%s resume_wk qeue %d\n", __func__, + get_queue_depth(&(p_usb_pm_skb_queue->list))); + entry = list_first_entry(&p_usb_pm_skb_queue->list, + struct usb_pm_skb_queue_t, list); + list_del(&entry->list); + kfree(entry); + } + usb_auto_pm_turnoff(ar); +#endif + +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + ath6kl_usb_flush_all(ar_usb); +#endif + ath6kl_core_cleanup(ar_usb->ar); + ath6kl_usb_destroy(ar_usb); +} + +/* exported hif usb APIs for htc pipe */ +static void hif_start(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + int i; + + ath6kl_usb_start_recv_pipes(device); + + /* set the TX resource avail threshold for each TX pipe */ + for (i = ATH6KL_USB_PIPE_TX_CTRL; + i <= ATH6KL_USB_PIPE_TX_DATA_VHP; i++) { + device->pipes[i].urb_cnt_thresh = + device->pipes[i].urb_alloc / 2; + } +} + +static void ath6kl_usb_transmit_bundle_complete(struct urb *urb) +{ + struct ath6kl_urb_context *urb_context = + (struct ath6kl_urb_context *)urb->context; + struct ath6kl_usb_pipe *pipe = urb_context->pipe; + struct ath6kl_usb_pipe_stat *pipe_st = &pipe->usb_pipe_stat; + struct sk_buff *tmp_buf; +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + struct ath6kl *ar = pipe->ar_usb->ar; +#endif + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, "+%s: pipe: %d, stat:%d, len:%d " "\n", + __func__, pipe->logical_pipe_num, urb->status, + urb->actual_length); + + if (urb->status != 0) { + pipe_st->num_tx_bundle_comp_err++; + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s: pipe: %d, failed:%d\n", + __func__, pipe->logical_pipe_num, urb->status); + } + + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); + + while ((tmp_buf = skb_dequeue(&urb_context->comp_queue))) + skb_queue_tail(&pipe->io_comp_queue, tmp_buf); + pipe_st->num_tx_bundle_comp++; +#ifdef CE_OLD_KERNEL_SUPPORT_2_6_23 + queue_work(ar->ath6kl_wq, &pipe->io_complete_work); +#else + schedule_work(&pipe->io_complete_work); +#endif +} + +static int ath6kl_usb_send_bundle(struct ath6kl *ar, u8 pid, + struct sk_buff **msg_bundle, int num_msgs) +{ + int status = 0; + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + struct ath6kl_usb_pipe *pipe = &device->pipes[pid]; + struct ath6kl_usb_pipe_stat *pipe_st = &pipe->usb_pipe_stat; + struct ath6kl_urb_context *urb_context; + struct urb *urb; + struct sk_buff *stream_buf = NULL, *buf = NULL; + int usb_status; + int i; + + pipe_st->num_tx_multi++; + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "+%s pipe : %d\n", + __func__, pid); + + do { + u8 *stream_netdata, *netdata, *stream_netdata_start; + u32 stream_netlen, netlen; + + urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); + + if (urb_context == NULL) { + pipe_st->num_tx_multi_err_others++; + /* + * TODO: it is possible to run out of urbs if + * 2 endpoints map to the same pipe ID + */ + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s pipe:%d no urbs left. URB Cnt : %d\n", + __func__, pid, pipe->urb_cnt); + status = -ENOMEM; + break; + } + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) { + pipe_st->num_tx_multi_err_others++; + status = -ENOMEM; + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + break; + } + + stream_buf = urb_context->buf; + + stream_netdata = stream_buf->data; + stream_netlen = stream_buf->len; + + stream_netlen = 0; + stream_netdata_start = stream_netdata; + + for (i = 0; i < num_msgs; i++) { + buf = msg_bundle[i]; + netdata = buf->data; + netlen = buf->len; + + memcpy(stream_netdata, netdata, netlen); + + /* add additional dummy padding */ + stream_netdata += 1664; /* target credit size */ + stream_netlen += 1664; + + /* note: queue implements a lock */ + skb_queue_tail(&urb_context->comp_queue, buf); + } + + usb_fill_bulk_urb(urb, + device->udev, + pipe->usb_pipe_handle, + stream_netdata_start, + stream_netlen, + ath6kl_usb_transmit_bundle_complete, + urb_context); + + if ((stream_netlen % pipe->max_packet_size) == 0) + /* hit a max packet boundary on this pipe */ + urb->transfer_flags |= URB_ZERO_PACKET; + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb bulk send submit:%d, " + "0x%X (ep:0x%2.2X), %d bytes\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->ep_address, stream_netlen); + + usb_anchor_urb(urb, &pipe->urb_submitted); + + spin_lock_bh(&ar->state_lock); + if ((ar->state == ATH6KL_STATE_DEEPSLEEP) || + (ar->state == ATH6KL_STATE_WOW)) + usb_status = -EINVAL; + else + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + spin_unlock_bh(&ar->state_lock); + + if (usb_status) { + pipe_st->num_tx_multi_err++; + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb : usb bulk transmit failed %d " + "\n", usb_status); + usb_unanchor_urb(urb); + usb_free_urb(urb); + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + status = -EINVAL; + break; + } + usb_free_urb(urb); + } while (0); + + return status; +} + +#ifdef USB_AUTO_SUSPEND +int usb_debugfs_get_pm_usage_cnt(struct ath6kl *ar) +{ + struct ath6kl_usb *device = (struct ath6kl_usb *)ar->hif_priv; + struct usb_interface *interface = device->interface; + return atomic_read(&interface->pm_usage_cnt); +} + +void usb_auto_pm_disable(struct ath6kl *ar) +{ + struct ath6kl_usb *device = (struct ath6kl_usb *)ar->hif_priv; + struct usb_interface *interface = device->interface; + usb_autopm_get_interface_async(interface); + ar->auto_pm_cnt++; + ath6kl_dbg(ATH6KL_DBG_USB, "autopm +1 refcnt=%d my=%d\n", + usb_debugfs_get_pm_usage_cnt(ar), ar->auto_pm_cnt); + /*usb_debugfs_get_pm_usage_cnt(ar);*/ + +} + + +void usb_auto_pm_enable(struct ath6kl *ar) +{ + struct ath6kl_usb *device = (struct ath6kl_usb *)ar->hif_priv; + struct usb_interface *interface = device->interface; + usb_autopm_put_interface_async(interface); + ar->auto_pm_cnt--; + ath6kl_dbg(ATH6KL_DBG_USB, "autopm -1 refcnt=%d my=%d\n", + usb_debugfs_get_pm_usage_cnt(ar), ar->auto_pm_cnt); + /*usb_debugfs_get_pm_usage_cnt(ar);*/ +} + +void usb_auto_pm_turnoff(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + if (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_USB_AUTO_PM)) + usb_disable_autosuspend(device->udev); +} + +void usb_auto_pm_turnon(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + if (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_USB_AUTO_PM)) + usb_enable_autosuspend(device->udev); +} + + +void ath6kl_auto_pm_wakeup_resume(struct ath6kl *wk) +{ + struct sk_buff *buf; + int status = 0; + struct ath6kl_usb *device; + struct ath6kl_usb_pipe *pipe; + struct ath6kl_usb_pipe_stat *pipe_st; + struct ath6kl_urb_context *urb_context; + u8 *data; + u32 len; + struct urb *urb; + int usb_status; + struct usb_pm_skb_queue_t *entry, *p_usb_pm_skb_queue; + struct ath6kl *pm_ar; + int pm_PipeID; + + pm_ar = wk; + p_usb_pm_skb_queue = &pm_ar->usb_pm_skb_queue; + + + ath6kl_dbg(ATH6KL_DBG_USB, "%s resume_wk qeue %d empty=%d\n", + __func__, + get_queue_depth(&(p_usb_pm_skb_queue->list)), + list_empty(&p_usb_pm_skb_queue->list)); + + while (get_queue_depth(&(p_usb_pm_skb_queue->list)) > 0) { + ath6kl_dbg(ATH6KL_DBG_USB, "%s resume_wk qeue %d\n", __func__, + get_queue_depth(&(p_usb_pm_skb_queue->list))); + entry = list_first_entry(&p_usb_pm_skb_queue->list, + struct usb_pm_skb_queue_t, list); + + pm_PipeID = entry->pipeID; + pm_ar = entry->ar; + buf = entry->skb; + + device = ath6kl_usb_priv(pm_ar); + pipe = &device->pipes[pm_PipeID]; + pipe_st = &pipe->usb_pipe_stat; + + usb_mark_last_busy(device->udev); + + urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); + + if (urb_context == NULL) { + pipe_st->num_tx_err_others++; + /* + * TODO: it is possible to run out of urbs if + * 2 endpoints map to the same pipe ID + */ + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s pipe:%d no urbs left. URB Cnt : %d\n", + __func__, pm_PipeID, pipe->urb_cnt); + status = -ENOMEM; + goto fail_hif_send_usb; + } + urb_context->buf = buf; + + data = buf->data; + len = buf->len; + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) { + pipe_st->num_tx_err_others++; + status = -ENOMEM; + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + goto fail_hif_send_usb; + } + + usb_fill_bulk_urb(urb, + device->udev, + pipe->usb_pipe_handle, + data, + len, + ath6kl_usb_usb_transmit_complete, + urb_context); + + if ((len % pipe->max_packet_size) == 0) { + /* hit a max packet boundary on this pipe */ + urb->transfer_flags |= URB_ZERO_PACKET; + } + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->ep_address, len); + + usb_anchor_urb(urb, &pipe->urb_submitted); + list_del(&entry->list); + + /* spin_lock_bh(&pm_ar->state_lock); */ + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + /* spin_unlock_bh(&pm_ar->state_lock); */ + + if (usb_status) { + pipe_st->num_tx_err++; + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb : usb bulk transmit failed %d\n", + usb_status); + usb_unanchor_urb(urb); + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + status = -EINVAL; + } + usb_free_urb(urb); + pipe_st->num_tx++; + + kfree(entry); + } /* end of while (Dequeu ...) */ + +fail_hif_send_usb: + ath6kl_dbg(ATH6KL_DBG_USB, "wakeup_resume done\n"); +} + + + +#endif /* USB_AUTO_SUSPEND */ + +static int ath6kl_usb_send(struct ath6kl *ar, u8 PipeID, + struct sk_buff *hdr_buf, struct sk_buff *buf) +{ + int status = 0; + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + struct ath6kl_usb_pipe *pipe = &device->pipes[PipeID]; + struct ath6kl_usb_pipe_stat *pipe_st = &pipe->usb_pipe_stat; + struct ath6kl_urb_context *urb_context; + u8 *data; + u32 len; + struct urb *urb; + int usb_status; +#ifdef USB_AUTO_SUSPEND + struct usb_pm_skb_queue_t *p_pmskb; + int qlen, usb_pm_increament; + struct usb_pm_skb_queue_t *p_usb_pm_skb_queue = &ar->usb_pm_skb_queue; +#endif +#ifdef USB_AUTO_SUSPEND + +#elif defined(CONFIG_ANDROID) + struct usb_interface *interface = device->interface; +#endif + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "+%s pipe : %d, buf:0x%p, ar state:%d\n", + __func__, PipeID, buf, ar->state); + +#ifdef USB_AUTO_SUSPEND + if (ar->state == ATH6KL_STATE_PRE_SUSPEND_DEEPSLEEP) { + ath6kl_dbg(ATH6KL_DBG_USB, "%s: deep sleep state=%d\n", + __func__, ar->state); + status = -EIO; + pipe_st->num_tx++; + return status; + } + + usb_pm_increament = 0; + if (ar->state != ATH6KL_STATE_PRE_SUSPEND) { + usb_pm_increament++; + usb_auto_pm_disable(ar); + } + + spin_lock_bh(&ar->usb_pm_lock); + if (!list_empty(&p_usb_pm_skb_queue->list) || + (ar->state == ATH6KL_STATE_WOW) || + (ar->state == ATH6KL_STATE_DEEPSLEEP)) { + + ath6kl_dbg(ATH6KL_DBG_USB, "usb_send sleep Q %d queue len =%d\n", + ar->state, + get_queue_depth(&(p_usb_pm_skb_queue->list))); + p_pmskb = kmalloc(sizeof(struct usb_pm_skb_queue_t), + GFP_ATOMIC); + if (p_pmskb == NULL) + ath6kl_dbg(ATH6KL_DBG_USB, "p_pmskb = kmalloc fila\n"); + + p_pmskb->pipeID = PipeID; + p_pmskb->ar = ar; + p_pmskb->skb = buf; + + list_add(&(p_pmskb->list), &(p_usb_pm_skb_queue->list)); + qlen = get_queue_depth(&(p_usb_pm_skb_queue->list)); + ath6kl_dbg(ATH6KL_DBG_USB, "qlen = %d\n", qlen); + + /* + msleep_interruptible(3000); + ath6kl_auto_pm_wakeup_resume(&auto_pm_wakeup_resume_wk); + */ + spin_unlock_bh(&ar->usb_pm_lock); + + if (usb_pm_increament != 0) + usb_auto_pm_enable(ar); + return 0; + } + + spin_unlock_bh(&ar->usb_pm_lock); + ath6kl_dbg(ATH6KL_DBG_USB, "usb_send 2\n"); + +#elif defined(CONFIG_ANDROID) + if (PipeID != ATH6KL_USB_PIPE_TX_CTRL) + usb_autopm_get_interface_async(interface); +#endif + urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); + + if (urb_context == NULL) { + pipe_st->num_tx_err_others++; + /* + * TODO: it is possible to run out of urbs if + * 2 endpoints map to the same pipe ID + */ + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s pipe:%d no urbs left. URB Cnt : %d\n", + __func__, PipeID, pipe->urb_cnt); + status = -ENOMEM; + goto fail_hif_send; + } + urb_context->buf = buf; + + data = buf->data; + len = buf->len; + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) { + pipe_st->num_tx_err_others++; + status = -ENOMEM; + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + goto fail_hif_send; + } + + usb_fill_bulk_urb(urb, + device->udev, + pipe->usb_pipe_handle, + data, + len, + ath6kl_usb_usb_transmit_complete, urb_context); + + if ((len % pipe->max_packet_size) == 0) { + /* hit a max packet boundary on this pipe */ + urb->transfer_flags |= URB_ZERO_PACKET; + } + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->ep_address, len); + + usb_anchor_urb(urb, &pipe->urb_submitted); + + spin_lock_bh(&ar->state_lock); +#ifndef USB_AUTO_SUSPEND /* QQQQ: double check here */ + if ((ar->state == ATH6KL_STATE_DEEPSLEEP) || + (ar->state == ATH6KL_STATE_WOW)) + usb_status = -EINVAL; + else +#endif + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + spin_unlock_bh(&ar->state_lock); + + if (usb_status) { + pipe_st->num_tx_err++; + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb : usb bulk transmit failed %d\n", + usb_status); + usb_unanchor_urb(urb); + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + status = -EINVAL; + } + usb_free_urb(urb); + pipe_st->num_tx++; + +fail_hif_send: + +#ifdef USB_AUTO_SUSPEND + if (usb_pm_increament != 0) + usb_auto_pm_enable(ar); + +#elif defined(CONFIG_ANDROID) + if (PipeID != ATH6KL_USB_PIPE_TX_CTRL) + usb_autopm_put_interface_async(interface); +#endif + return status; +} + +static void hif_stop(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + ath6kl_usb_flush_all(device); +} + +static void ath6kl_usb_get_default_pipe(struct ath6kl *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL; + *dl_pipe = ATH6KL_USB_PIPE_RX_CTRL; +} + +static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svcId, u8 *ULPipe, + u8 *DLPipe) +{ + int status = 0; + + switch (svcId) { + case HTC_CTRL_RSVD_SVC: + case WMI_CONTROL_SVC: + *ULPipe = ATH6KL_USB_PIPE_TX_CTRL; + *DLPipe = ATH6KL_USB_PIPE_RX_DATA; + break; + case WMI_DATA_BE_SVC: + case WMI_DATA_BK_SVC: + *ULPipe = ATH6KL_USB_PIPE_TX_DATA_LP; + *DLPipe = ATH6KL_USB_PIPE_RX_DATA; + break; + case WMI_DATA_VI_SVC: + *ULPipe = ATH6KL_USB_PIPE_TX_DATA_LP; + *DLPipe = ATH6KL_USB_PIPE_RX_DATA; + break; + case WMI_DATA_VO_SVC: + *ULPipe = ATH6KL_USB_PIPE_TX_DATA_LP; + *DLPipe = ATH6KL_USB_PIPE_RX_DATA; + break; + default: + status = -EPERM; + break; + } + + return status; +} + +static void ath6kl_usb_register_callback(struct ath6kl *ar, + void *unused, + struct ath6kl_hif_pipe_callbacks *callbacks) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + memcpy(&device->htc_callbacks, callbacks, + sizeof(struct ath6kl_hif_pipe_callbacks)); +} + +static u16 ath6kl_usb_get_free_queue_number(struct ath6kl *ar, u8 PipeID) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + return device->pipes[PipeID].urb_cnt; +} + +static u16 ath6kl_usb_get_max_queue_number(struct ath6kl *ar, u8 PipeID) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + return device->pipes[PipeID].urb_alloc; +} + +static void hif_detach_htc(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + ath6kl_usb_flush_all(device); + + memset(&device->htc_callbacks, 0, + sizeof(struct ath6kl_hif_pipe_callbacks)); +} + +static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb, + u8 req, u16 value, u16 index, void *data, + u32 size) +{ + u8 *buf = NULL; + int ret; + + if (size > 0) { + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + memcpy(buf, data, size); + } + + /* note: if successful returns number of bytes transfered */ + ret = usb_control_msg(ar_usb->udev, + usb_sndctrlpipe(ar_usb->udev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, buf, + size, 1000); + + if (ret < 0) { + ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n", + __func__, ret); + } + + kfree(buf); + + return 0; +} + +static int ath6kl_usb_submit_ctrl_in(struct ath6kl_usb *ar_usb, + u8 req, u16 value, u16 index, void *data, + u32 size) +{ + u8 *buf = NULL; + int ret; + + if (size > 0) { + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + } + + /* note: if successful returns number of bytes transfered */ + ret = usb_control_msg(ar_usb->udev, + usb_rcvctrlpipe(ar_usb->udev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, buf, + size, 2 * HZ); + + if (ret < 0) { + ath6kl_dbg(ATH6KL_DBG_USB, "%s failed,result = %d\n", + __func__, ret); + } + + memcpy((u8 *) data, buf, size); + + kfree(buf); + + return 0; +} + +static int ath6kl_usb_ctrl_msg_exchange(struct ath6kl_usb *ar_usb, + u8 req_val, u8 *req_buf, u32 req_len, + u8 resp_val, u8 *resp_buf, u32 *resp_len) +{ + int ret; + + /* send command */ + ret = ath6kl_usb_submit_ctrl_out(ar_usb, req_val, 0, 0, + req_buf, req_len); + + if (ret != 0) + return ret; + + if (resp_buf == NULL) { + /* no expected response */ + return ret; + } + + /* get response */ + ret = ath6kl_usb_submit_ctrl_in(ar_usb, resp_val, 0, 0, + resp_buf, *resp_len); + + return ret; +} + +static int ath6kl_usb_diag_read32(struct ath6kl *ar, u32 address, u32 *data) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + struct ath6kl_usb_ctrl_diag_resp_read *resp; + struct ath6kl_usb_ctrl_diag_cmd_read *cmd; + u32 resp_len; + int ret; + + cmd = (struct ath6kl_usb_ctrl_diag_cmd_read *) ar_usb->diag_cmd_buffer; + + memset(cmd, 0, sizeof(*cmd)); + cmd->cmd = ATH6KL_USB_CTRL_DIAG_CC_READ; + cmd->address = cpu_to_le32(address); + resp_len = sizeof(*resp); + + ret = ath6kl_usb_ctrl_msg_exchange(ar_usb, + ATH6KL_USB_CONTROL_REQ_DIAG_CMD, + (u8 *) cmd, + sizeof(struct ath6kl_usb_ctrl_diag_cmd_write), + ATH6KL_USB_CONTROL_REQ_DIAG_RESP, + ar_usb->diag_resp_buffer, &resp_len); + + if (ret) + return ret; + + resp = (struct ath6kl_usb_ctrl_diag_resp_read *) + ar_usb->diag_resp_buffer; + + *data = le32_to_cpu(resp->value); + + return ret; +} + +static int ath6kl_usb_diag_write32(struct ath6kl *ar, u32 address, __le32 data) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + struct ath6kl_usb_ctrl_diag_cmd_write *cmd; + + cmd = (struct ath6kl_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer; + + memset(cmd, 0, sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)); + cmd->cmd = cpu_to_le32(ATH6KL_USB_CTRL_DIAG_CC_WRITE); + cmd->address = cpu_to_le32(address); + cmd->value = data; + + return ath6kl_usb_ctrl_msg_exchange(ar_usb, + ATH6KL_USB_CONTROL_REQ_DIAG_CMD, + (u8 *) cmd, + sizeof(*cmd), + 0, NULL, NULL); + +} + +static int ath6kl_usb_bmi_read(struct ath6kl *ar, u8 *buf, u32 len) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + int ret; + + /* get response */ + ret = ath6kl_usb_submit_ctrl_in(ar_usb, + ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP, + 0, 0, buf, len); + if (ret != 0) { + ath6kl_err("Unable to read the bmi data from the device: %d\n", + ret); + return ret; + } + + return 0; +} + +static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + int ret; + + /* send command */ + ret = ath6kl_usb_submit_ctrl_out(ar_usb, + ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD, + 0, 0, buf, len); + if (ret != 0) { + ath6kl_err("unable to send the bmi data to the device: %d\n", + ret); + return ret; + } + + return 0; +} + +static int ath6kl_usb_power_on(struct ath6kl *ar) +{ + if (test_bit(USB_REMOTE_WKUP, &ar->flag) || + BOOTSTRAP_IS_HSIC(ar->bootstrap_mode)) { + struct ath6kl_usb *ar_usb = (struct ath6kl_usb *)ar->hif_priv; + usb_reset_device(ar_usb->udev); + } + + hif_start(ar); + return 0; +} + +static int ath6kl_usb_power_off(struct ath6kl *ar) +{ + hif_detach_htc(ar); + return 0; +} + +static void ath6kl_usb_stop(struct ath6kl *ar) +{ + hif_stop(ar); +} + +static int ath6kl_usb_pipe_stat(struct ath6kl *ar, u8 *buf, int buf_len) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + struct ath6kl_usb_pipe_stat *pipe_st; + int i, len = 0; + + if ((!device) || (!buf)) + return 0; + + for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { + if ((i == ATH6KL_USB_PIPE_RX_INT) || + (i == ATH6KL_USB_PIPE_RX_DATA2)) + continue; + + pipe_st = &device->pipes[i].usb_pipe_stat; + + len += snprintf(buf + len, buf_len - len, "\nPIPE-%d\n", i); + len += snprintf(buf + len, buf_len - len, + " num_rx_comp : %d\n", + pipe_st->num_rx_comp); + len += snprintf(buf + len, buf_len - len, + " num_tx_comp : %d\n", + pipe_st->num_tx_comp); + len += snprintf(buf + len, buf_len - len, + " num_io_comp : %d\n", + pipe_st->num_io_comp); + len += snprintf(buf + len, buf_len - len, + " num_max_tx : %d\n", + pipe_st->num_max_tx); + len += snprintf(buf + len, buf_len - len, + " num_max_rx : %d\n", + pipe_st->num_max_rx); + len += snprintf(buf + len, buf_len - len, + " num_tx_resche : %d\n", + pipe_st->num_tx_resche); + len += snprintf(buf + len, buf_len - len, + " num_rx_resche : %d\n", + pipe_st->num_rx_resche); + len += snprintf(buf + len, buf_len - len, + " num_tx_sync : %d\n", + pipe_st->num_tx_sync); + len += snprintf(buf + len, buf_len - len, + " num_tx : %d\n", + pipe_st->num_tx); + len += snprintf(buf + len, buf_len - len, + " num_tx_err : %d\n", + pipe_st->num_tx_err); + len += snprintf(buf + len, buf_len - len, + " num_tx_err_others : %d\n", + pipe_st->num_tx_err_others); + len += snprintf(buf + len, buf_len - len, + " num_tx_comp_err : %d\n", + pipe_st->num_tx_comp_err); + len += snprintf(buf + len, buf_len - len, + " num_rx_comp_err : %d\n", + pipe_st->num_rx_comp_err); + + /* Bundle mode */ + if (htc_bundle_recv || htc_bundle_send) { + len += snprintf(buf + len, buf_len - len, + " num_rx_bundle_comp : %d\n", + pipe_st->num_rx_bundle_comp); + len += snprintf(buf + len, buf_len - len, + " num_tx_bundle_comp : %d\n", + pipe_st->num_tx_bundle_comp); + len += snprintf(buf + len, buf_len - len, + " num_tx_multi : %d\n", + pipe_st->num_tx_multi); + len += snprintf(buf + len, buf_len - len, + " num_tx_multi_err : %d\n", + pipe_st->num_tx_multi_err); + len += snprintf(buf + len, buf_len - len, + " num_tx_multi_err_others : %d\n", + pipe_st->num_tx_multi_err_others); + len += snprintf(buf + len, buf_len - len, + " num_rx_bundle_comp_err : %d\n", + pipe_st->num_rx_bundle_comp_err); + len += snprintf(buf + len, buf_len - len, + " num_tx_bundle_comp_err : %d\n", + pipe_st->num_tx_bundle_comp_err); + } + } + + return len; +} + +static int ath6kl_usb_set_max_sche(struct ath6kl *ar, + u32 max_sche_tx, u32 max_sche_rx) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + device->max_sche_tx = max_sche_tx; + device->max_sche_rx = max_sche_rx; + + ath6kl_dbg(ATH6KL_DBG_USB, "max_sche_tx = %d, max_sche_rx = %d\n", + device->max_sche_tx, device->max_sche_rx); + + return 0; +} + +int ath6kl_usb_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + struct usb_interface *interface = device->interface; + pm_message_t message; + int ret; + +#ifdef CONFIG_ANDROID + if (ath6kl_android_need_wow_suspend(ar)) { +#else + if (wow) { +#endif + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow); + if (ret) + return ret; + } else { + memset(&message, 0, sizeof(message)); + ret = ath6kl_usb_pm_suspend(interface, message); + } + + return ret; +} + +int ath6kl_usb_resume(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + struct usb_interface *interface = device->interface; + + return ath6kl_usb_pm_resume(interface); +} + +static void ath6kl_usb_cleanup_scatter(struct ath6kl *ar) +{ + ath6kl_dbg(ATH6KL_DBG_USB, "Init target fail?\n"); + return; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND + +static void ath6kl_usb_early_suspend(struct ath6kl *ar) +{ +#ifndef USB_AUTO_SUSPEND + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + if (!ath6kl_mod_debug_quirks(ar, + ATH6KL_MODULE_DISABLE_USB_AUTO_SUSPEND)) { + if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode)) { + struct usb_device *udev = device->udev; + pm_runtime_set_autosuspend_delay(&udev->dev, 2000); + } + usb_enable_autosuspend(device->udev); + } +#endif +} + +static void ath6kl_usb_late_resume(struct ath6kl *ar) +{ +#ifndef USB_AUTO_SUSPEND + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + if (!ath6kl_mod_debug_quirks(ar, + ATH6KL_MODULE_DISABLE_USB_AUTO_SUSPEND)) + usb_disable_autosuspend(device->udev); +#endif /* define USB_AUTO_SUSPEND */ +} + +#endif + +/* FIXME: revisit a proper place to issue the bus reset*/ +int ath6kl_usb_reconfig(struct ath6kl *ar) +{ + int ret = 0; + u32 data, addr; + + /* To reenumerate the usb device if host want to enable the + * usb remote wakeup feature, + * ar6004 hw1.1, hw1.2 and hw1.3 would support this, + * hw1.6 would enable by default + */ + if (!ath6kl_mod_debug_quirks(ar, + ATH6KL_MODULE_ENABLE_USB_REMOTE_WKUP)) { + ret = 0; + ath6kl_dbg(ATH6KL_DBG_BOOT, "Disable USB remote wakeup.\n"); + } else { + if (ar->version.target_ver == AR6004_HW_1_3_VERSION) + addr = 0x409754; + else if (ar->version.target_ver == AR6004_HW_1_2_VERSION) + addr = 0x4087d4; + else if (ar->version.target_ver == AR6004_HW_1_1_VERSION) + addr = 0x408304; + else + addr = 0; + + if (addr) { + ath6kl_bmi_read(ar, addr, (u8 *)&data, + sizeof(unsigned long)); + data |= (1<<29); + ath6kl_bmi_write(ar, addr, (u8 *)&data, + sizeof(unsigned long)); + ret = 1; + ath6kl_dbg(ATH6KL_DBG_BOOT, + "Enable USB remote wakeup.\n"); + } else { + ret = 0; + ath6kl_dbg(ATH6KL_DBG_BOOT, + "Hw not supporting USB remote wakeup, disable it.\n"); + } + } + + return ret; +} + +int ath6kl_usb_diag_warm_reset(struct ath6kl *ar) +{ + struct ath6kl_usb *ar_usb = ar->hif_priv; + struct ath6kl_usb_ctrl_diag_cmd_write *cmd; + u32 data, address; + + address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_reset_flag_valid)); + ath6kl_usb_diag_read32(ar, address, &data); + data |= 0x12345678; + ath6kl_usb_diag_write32(ar, address, data); + + address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_reset_flag)); + ath6kl_usb_diag_read32(ar, address, &data); + data |= 0x20; + ath6kl_usb_diag_write32(ar, address, data); + + cmd = (struct ath6kl_usb_ctrl_diag_cmd_write *)ar_usb->diag_cmd_buffer; + memset(cmd, 0, sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)); + cmd->cmd = cpu_to_le32(ATH6KL_USB_CTRL_DIAG_CC_WARM_RESET); + + return ath6kl_usb_ctrl_msg_exchange(ar_usb, + ATH6KL_USB_CONTROL_REQ_DIAG_CMD, + (u8 *) cmd, + sizeof(*cmd), + 0, NULL, NULL); +} + + +static const struct ath6kl_hif_ops ath6kl_usb_ops = { + .diag_read32 = ath6kl_usb_diag_read32, + .diag_write32 = ath6kl_usb_diag_write32, + .bmi_read = ath6kl_usb_bmi_read, + .bmi_write = ath6kl_usb_bmi_write, + .power_on = ath6kl_usb_power_on, + .power_off = ath6kl_usb_power_off, + .stop = ath6kl_usb_stop, + .get_stat = ath6kl_usb_pipe_stat, + .pipe_register_callback = ath6kl_usb_register_callback, + .pipe_send = ath6kl_usb_send, + .pipe_get_default = ath6kl_usb_get_default_pipe, + .pipe_map_service = ath6kl_usb_map_service_pipe, + .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number, + .pipe_send_bundle = ath6kl_usb_send_bundle, + .pipe_get_max_queue_number = ath6kl_usb_get_max_queue_number, + .pipe_set_max_sche = ath6kl_usb_set_max_sche, + .suspend = ath6kl_usb_suspend, + .resume = ath6kl_usb_resume, + .cleanup_scatter = ath6kl_usb_cleanup_scatter, + .diag_warm_reset = ath6kl_usb_diag_warm_reset, +#ifdef CONFIG_HAS_EARLYSUSPEND + .early_suspend = ath6kl_usb_early_suspend, + .late_resume = ath6kl_usb_late_resume, +#endif + .bus_config = ath6kl_usb_reconfig, +#ifdef USB_AUTO_SUSPEND + .auto_pm_disable = usb_auto_pm_disable, + .auto_pm_enable = usb_auto_pm_enable, + .auto_pm_turnon = usb_auto_pm_turnon, + .auto_pm_turnoff = usb_auto_pm_turnoff, + .auto_pm_get_usage_cnt = usb_debugfs_get_pm_usage_cnt, +#endif +}; + +#ifdef ATHTST_SUPPORT +static struct hif_product_info_t g_product_info; +void ath6kl_usb_get_usbinfo(void *product_info) +{ + memcpy(product_info, &g_product_info, sizeof(g_product_info)); + return; +} +#endif + +/* ath6kl usb driver registered functions */ +static int ath6kl_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(interface); + struct ath6kl *ar; + struct ath6kl_usb *ar_usb = NULL; + int vendor_id, product_id; + int ret = 0; + + usb_get_dev(dev); + + vendor_id = le16_to_cpu(dev->descriptor.idVendor); + product_id = le16_to_cpu(dev->descriptor.idProduct); +#ifdef ATHTST_SUPPORT + g_product_info.idVendor = vendor_id = + le16_to_cpu(dev->descriptor.idVendor); + g_product_info.idProduct = product_id = + le16_to_cpu(dev->descriptor.idProduct); + if (dev->product) + memcpy(g_product_info.product, dev->product, + sizeof(g_product_info.product)); + if (dev->manufacturer) + memcpy(g_product_info.manufacturer, dev->manufacturer, + sizeof(g_product_info.manufacturer)); + if (dev->serial) + memcpy(g_product_info.serial, dev->serial, + sizeof(g_product_info.serial)); +#endif + + ath6kl_dbg(ATH6KL_DBG_USB, "vendor_id = %04x\n", vendor_id); + ath6kl_dbg(ATH6KL_DBG_USB, "product_id = %04x\n", product_id); + + if (interface->cur_altsetting) + ath6kl_dbg(ATH6KL_DBG_USB, "USB Interface %d\n", + interface->cur_altsetting->desc.bInterfaceNumber); + + + if (dev->speed == USB_SPEED_HIGH) + ath6kl_dbg(ATH6KL_DBG_USB, "USB 2.0 Host\n"); + else + ath6kl_dbg(ATH6KL_DBG_USB, "USB 1.1 Host\n"); + + ar_usb = ath6kl_usb_create(interface); + + if (ar_usb == NULL) { + ret = -ENOMEM; + goto err_usb_put; + } + + ar = ath6kl_core_alloc(&ar_usb->udev->dev); + if (ar == NULL) { + ath6kl_err("Failed to alloc ath6kl core\n"); + ret = -ENOMEM; + goto err_usb_destroy; + } + + ar->hif_priv = ar_usb; + ar->hif_type = ATH6KL_HIF_TYPE_USB; + ar->hif_ops = &ath6kl_usb_ops; + ar->mbox_info.block_size = 16; + ar->bmi.max_data_size = 252; + + ar_usb->ar = ar; +#ifdef CONFIG_ANDROID + if (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_USB_AUTO_PM)) + usb_disable_autosuspend(ar_usb->udev); +#endif + +#ifdef USB_AUTO_SUSPEND + spin_lock_init(&ar->usb_pm_lock); + INIT_LIST_HEAD(&ar->usb_pm_skb_queue.list); + pm_runtime_set_autosuspend_delay(&dev->dev, 2000); + if (!ath6kl_mod_debug_quirks(ar, ATH6KL_MODULE_DISABLE_USB_AUTO_PM)) + usb_enable_autosuspend(dev); + ar->auto_pm_cnt = 0; +#endif + ath6kl_htc_pipe_attach(ar); + ret = ath6kl_core_init(ar); + if (ret) { + ath6kl_err("Failed to init ath6kl core: %d\n", ret); + goto err_core_free; + } + + return ret; + +err_core_free: + ath6kl_core_free(ar); +err_usb_destroy: + ath6kl_usb_destroy(ar_usb); +err_usb_put: + usb_put_dev(dev); + + return ret; +} + +static void ath6kl_usb_remove(struct usb_interface *interface) +{ + usb_put_dev(interface_to_usbdev(interface)); + ath6kl_usb_device_detached(interface); +} + +#ifdef CONFIG_PM + +#ifdef CONFIG_ANDROID +static int ath6kl_usb_pm_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct ath6kl_usb *device; + struct ath6kl *ar; + struct ath6kl_vif *vif; + int ret; + + device = (struct ath6kl_usb *)usb_get_intfdata(interface); + ar = device->ar; + + vif = ath6kl_vif_first(ar); + + if (ath6kl_android_need_wow_suspend(ar)) + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, NULL); + else + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, + NULL); + if (ret == 0) + ath6kl_usb_flush_all(device); + + return ret; +} +#else +static int ath6kl_usb_pm_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct ath6kl_usb *device; + struct ath6kl *ar; +#ifdef USB_AUTO_SUSPEND + int ret; +#endif + device = (struct ath6kl_usb *)usb_get_intfdata(interface); + ar = device->ar; + +#ifdef USB_AUTO_SUSPEND + + if (ath6kl_android_need_wow_suspend(ar)) + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, NULL); + else + ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, + NULL); + + +#else + if (ar->state != ATH6KL_STATE_WOW) + ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL); + +#endif + ath6kl_usb_flush_all(device); + return 0; +} +#endif + + +static int ath6kl_usb_pm_resume(struct usb_interface *interface) +{ + struct ath6kl_usb *device; + struct ath6kl *ar; + + device = (struct ath6kl_usb *)usb_get_intfdata(interface); + ar = device->ar; + + /* re-post urbs? */ + if (0) { + ath6kl_usb_post_recv_transfers( + &device->pipes[ATH6KL_USB_PIPE_RX_CTRL], + ATH6KL_USB_RX_BUFFER_SIZE); + } + if (!htc_bundle_recv) { + ath6kl_usb_post_recv_transfers( + &device->pipes[ATH6KL_USB_PIPE_RX_DATA], + ATH6KL_USB_RX_BUFFER_SIZE); + ath6kl_usb_post_recv_transfers( + &device->pipes[ATH6KL_USB_PIPE_RX_DATA2], + ATH6KL_USB_RX_BUFFER_SIZE); + } else { + hif_usb_post_recv_bundle_transfers( + &device->pipes[ATH6KL_USB_PIPE_RX_DATA], + 0 /* not allocating urb-buffer again */); + hif_usb_post_recv_bundle_transfers( + &device->pipes[ATH6KL_USB_PIPE_RX_DATA2], + 0 /* not allocating urb-buffer again */); + } + + ath6kl_cfg80211_resume(ar); + + return 0; +} + +static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf) +{ + struct ath6kl_usb *device; + struct ath6kl *ar; + + device = (struct ath6kl_usb *)usb_get_intfdata(intf); + ar = device->ar; + /* + instead of call remove directly, In HSIC mode, + we call pm_resume to make usb continue to work + */ + if (BOOTSTRAP_IS_HSIC(ar->bootstrap_mode)) { + ath6kl_dbg(ATH6KL_DBG_USB, + "ath6kl_usb_pm_reset_resume\n"); + ath6kl_usb_pm_resume(intf); + } else { + if (usb_get_intfdata(intf)) + ath6kl_usb_remove(intf); + } + return 0; +} +#endif + +/* table of devices that work with this driver */ +static struct usb_device_id ath6kl_usb_ids[] = { + {USB_DEVICE(0x0cf3, 0x9375)}, + {USB_DEVICE(0x0cf3, 0x9374)}, + {USB_DEVICE(0x0cf3, 0x9372)}, + { /* Terminating entry */ }, +}; + +MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids); + +static struct usb_driver ath6kl_usb_driver = { + .name = "ath6kl_usb", + .probe = ath6kl_usb_probe, +#ifdef CONFIG_PM + .suspend = ath6kl_usb_pm_suspend, + .resume = ath6kl_usb_pm_resume, + .reset_resume = ath6kl_usb_pm_reset_resume, +#endif + .disconnect = ath6kl_usb_remove, + .id_table = ath6kl_usb_ids, + .supports_autosuspend = true, +}; + +#ifdef CONFIG_ANDROID +static int ath6kl_usb_dev_notify(struct notifier_block *nb, + unsigned long action, void *dev) +{ + struct usb_device *udev; + int ret = NOTIFY_DONE; + + if (action != USB_DEVICE_REMOVE) + goto done; + + udev = (struct usb_device *) dev; + if (ath6kl_usb_unload_dev_num != udev->devnum) + goto done; + + if (atomic_read(&ath6kl_usb_unload_state) == + ATH6KL_USB_UNLOAD_STATE_TARGET_RESET) { + atomic_set(&ath6kl_usb_unload_state, + ATH6KL_USB_UNLOAD_STATE_DEV_DISCONNECTED); + wake_up(&ath6kl_usb_unload_event_wq); + } + +done: + return ret; +} + +static struct notifier_block ath6kl_usb_dev_nb = { + .notifier_call = ath6kl_usb_dev_notify, +}; + +static int ath6kl_usb_init(void) +{ + init_waitqueue_head(&ath6kl_usb_unload_event_wq); + atomic_set(&ath6kl_usb_unload_state, ATH6KL_USB_UNLOAD_STATE_NULL); + usb_register_notify(&ath6kl_usb_dev_nb); + + usb_register(&ath6kl_usb_driver); + +#ifdef ATH6KL_BUS_VOTE + if (ath6kl_hsic_init_msm() != 0) + ath6kl_err("%s ath6kl_hsic_init_msm failed\n", __func__); +#endif + + return 0; +} + +static void ath6kl_usb_exit(void) +{ + long timeleft = 0; + atomic_set(&ath6kl_usb_unload_state, ATH6KL_USB_UNLOAD_STATE_DRV_DEREG); + usb_deregister(&ath6kl_usb_driver); + + if (atomic_read(&ath6kl_usb_unload_state) != + ATH6KL_USB_UNLOAD_STATE_TARGET_RESET) + goto finish; + + timeleft = wait_event_interruptible_timeout(ath6kl_usb_unload_event_wq, + atomic_read(&ath6kl_usb_unload_state) == + ATH6KL_USB_UNLOAD_STATE_DEV_DISCONNECTED, + ATH6KL_USB_UNLOAD_TIMEOUT); + +finish: + usb_unregister_notify(&ath6kl_usb_dev_nb); +#ifdef ATH6KL_BUS_VOTE + ath6kl_hsic_exit_msm(); +#endif +} +#else +static int ath6kl_usb_init(void) +{ + usb_register(&ath6kl_usb_driver); + return 0; +} + +static void ath6kl_usb_exit(void) +{ + usb_deregister(&ath6kl_usb_driver); +} +#endif + +module_init(ath6kl_usb_init); +module_exit(ath6kl_usb_exit); + +MODULE_AUTHOR("Atheros Communications, Inc."); +MODULE_DESCRIPTION("Driver support for Atheros AR600x USB devices"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_FIRMWARE(AR6004_HW_1_0_FW_DIR "/" AR6004_HW_1_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_FW_DIR "/" AR6004_HW_1_2_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_2_0_FW_DIR "/" AR6004_HW_2_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_2_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_2_0_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6006_HW_1_0_FW_DIR "/" AR6006_HW_1_0_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6006_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6006_HW_1_0_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl-3.5/wlan_location_defs.h b/drivers/net/wireless/ath/ath6kl-3.5/wlan_location_defs.h new file mode 100644 index 000000000000..439b10722dc0 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/wlan_location_defs.h @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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_LOCATION_DEFS_H +#define _WLAN_LOCATION_DEFS_H + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +#define NUM_OFDM_TONES_ACK_FRAME 52 +#define RESOLUTION_IN_BITS 10 +#define RTTM_CDUMP_SIZE(rxchain, bw40) \ + ((NUM_OFDM_TONES_ACK_FRAME * 2 * (bw40 + 1) * \ + (rxchain) * RESOLUTION_IN_BITS) / 8) +#define MAX_RTTREQ_MEAS 10 +#define NUM_WLAN_BANDS 2 +#define MAX_CHAINS 2 +#define NUM_CLKOFFSETCAL_SAMPLES 5 +#define CLKOFFSET_GPIO_PIN 26 + +#define NSP_MRQST_MEASTYPEMASK 0x3 + +#define MRQST_MODE_RTT 0x0 +#define MRQST_MODE_CIR 0x1 +#define MRQST_MODE_DBG 0x2 + +#define NSMP_MRQST_MEASTYPE(mode) (mode & 0x3) +#define NSP_MRQST_FRAMETYPE(mode) ((mode>>2) & 0x7) +#define NSP_MRQST_TXCHAIN(mode) ((mode>>5) & 0x3) +#define NSP_MRQST_RXCHAIN(mode) ((mode>>7) & 0x3) +#define NSP_MRQST_REQMETHOD(mode) ((mode>>9) & 0x3) +#define NSP_MRQST_MOREREQ(mode) ((mode>>11) & 0x1) + +struct nsp_header { + u8 version; + u8 frame_type; +}; +#define NSP_HDR_LEN sizeof(struct nsp_header) + +struct nsp_mrqst { + u32 sta_info; + /* A bit field used to provide information about the STA + * The bit fields include the station type (WP Capable/Non Capable) + * and other fields to be defined in the future. + * STAInfo[0]: Station Type: 0=STA is not WP Capable,1=STA is WP Capable + * STAInfo[31:1]: TBD */ + + u32 transmit_rate; + /* Rate requested for transmission. If value is all zeros + * the AP will choose its own rate. If not the AP will honor this + * 31:0: IEEE Physical Layer Transmission Rate of the Probe frame */ + + u32 timeout; + /* Time out for collecting all measurements. This includes the + * time taken to send out all sounding frames and retry attempts. + * Measured in units of milliseconds.For the Immediate Measurement mode, + * the WLAN AP system must return a Measurement Response after the + * lapse of an interval equal to “timeout†milliseconds after + * reception of the Measurement Request */ + u32 reserved; + u16 mode; +#define FRAME_TYPE_HASH 0x001c +#define NULL_FRAME 0 +#define QOS_NULL_FRAME 4 +#define RTS_CTS 8 + /* Bits 1:0: Type of measurement: + 00: RTT, 01: CIR + * Bits 4:2: 802.11 Frame Type to use as Probe + 000: NULL, 001: Qos NULL, 010: RTS/CTS + * Bits 6:5 Transmit chainmask to use for transmission + 01: 1, 10: 2, 11:3 + * Bits 8:7: Receive chainmask to use for reception + 01: 1, 10: 2, 11:3 + * Bits 10:9: The method by which the request should be serviced + 00 = Immediate: The request must be serviced as soon as possible + 01 = Delayed: The WPC can defer the request to when + it deems appropriate + 10 = Cached: The WPC should service the request from cached + results only */ + + u8 request_id; + /* A unique ID which identifies the request. It is the responsibility + * of the application to ensure that this is unique. + * The library will use this Id in its response. */ + + u8 sta_mac_addr[ETH_ALEN]; + /* The MAC Address of the STA for which a measurement is requested.*/ + + u8 spoof_mac_addr[ETH_ALEN]; + /* The MAC Address which the AP SW should use as the source + * address when sending out the sounding frames */ + + u8 channel; + /* The channel on which the STA is currently listening. + * The channel is specified in the notation (1-11 for 2.4 GHz + * and 36 – 169 for 5 GHz. + * If a STA is in HT40 mode, then the channel will indicate the + * control channel. Probe frames will always be sent at HT20 */ + + u8 no_of_measurements; + /* The number of results requested i.e the WLAN AP can stop measuring + * after it successfully completes a number of measurements equal + * to this number. For RTT based measurement this will always = 1 */ + u8 reserved2[3]; +}; + +/* NSP Capability Request */ +struct nsp_crqst { + u8 request_id; + /* A unique ID which identifies the request. It is the responsibility + * of the application to ensure that this is unique. + * The library will use this Id in its response. */ +}; + +/*NSP Status Request*/ +struct nsp_srqst { + u8 request_id; + /* A unique ID which identifies the request. It is the responsibility + * of the application to ensure that this is unique. + * The library will use this Id in its response. */ +}; + +#define MRESP_RTT 0x0 +#define MRESP_CIR 0x1 +#define MRESP_DBG 0x +#define MRESP_CLKOFFSETCAL_START 0x3 +#define MRESP_CLKOFFSETCAL_END 0x4 + +struct nsp_mresphdr { + u8 request_id; + /* A unique ID which identifies the request. It is the responsibility + * of the application to ensure that this is unique. + * The library will use this Id in its response. */ + u8 sta_mac_addr[ETH_ALEN]; + /* The MAC Address of the STA for which a measurement is requested.*/ + + u8 response_type; + /*Type Of Response 0:CIR 1:RTT*/ + + u16 no_of_responses; + /* no of responses */ + + u16 result; + /* Result Of Probe Bit-0 0:Complete 1:More Responses For this + * Request ID*/ + + u32 begints; + /* FW Timestamp when RTT Req Begins */ + + u32 endts; + /* FW Timestamp when RTT Req processing completes*/ + + u32 reserved; +}; + +#define RTTM_CHANNEL_DUMP_LEN RTTM_CDUMP_SIZE(MAX_CHAINS, 1) +struct nsp_cir_resp { + u32 tod; + /* A timestamp indicating when the measurement probe was sent */ + + u32 toa; + /* A timestamp indicating when the probe response was received */ + + u32 sendrate; + /* IEEE Physical Layer Transmission Rate of the Send Probe frame */ + + u32 recvrate; + /*IEEE Physical Layer Transmission Rate of the response to the + * Probe Frame*/ + + u8 channel_dump[RTTM_CHANNEL_DUMP_LEN]; + /* channel dump for max no of chains */ + + u8 no_of_chains; + /* Number of chains used for reception */ + + u8 rssi[MAX_CHAINS]; + /* Received signal strength indicator */ + + u8 isht40; + + s8 fwcorr; + + u8 reserved[3]; +}; + +struct rttresp_meas_debug { + u32 rtt; + int rtt_cal; + s8 corr; + u8 rssi0; + u8 rssi1; + u8 reserved; + u32 range; + int clkoffset; +}; + +struct nsp_rttresp_debuginfo { + struct rttresp_meas_debug rttmeasdbg[MAX_RTTREQ_MEAS]; + u8 nTotalMeas; + u8 wlanband; + u8 isht40; + u8 reserved1; + u32 reserved2; +}; + +#define RTTRESP_DEBUG_SIZE sizeof(struct nsp_rttresp_debuginfo) + +struct nsp_rtt_resp { + u32 range_average; + /*Result Of Probe*/ + + u32 range_stddev; + /*Results Of Range*/ + + u32 range_min; + + u32 range_max; + + u32 rtt_min; + + u32 rtt_max; + + u32 rtt_average; + /*The mean of the samples of RTT calculated by WML(ns)*/ + + u32 rtt_stddev; + /*The standard deviation of the samples of RTT calculated by + * WML(ns)*/ + + u8 rssi_average; + /*The mean of the samples of the RSSI(dB)*/ + + u8 rssi_stddev; + /*The Standard Deviation of the samples of the RSSI(dB)*/ + + u8 rssi_min; + /*Min RSSI*/ + + u8 rssi_max; + /*Max RSSI*/ + + u8 rtt_samples; + /*The Number Of Samples used in the RTT Calculation*/ + + u8 reserved1[3]; + + u32 reserved2; + + u32 clock_delta; + + u8 debug[RTTRESP_DEBUG_SIZE]; + +}; + +struct nsp_rtt_config { + u32 ClkCal[NUM_WLAN_BANDS]; + u8 FFTScale; + u8 RangeScale; + u8 ClkSpeed[NUM_WLAN_BANDS]; + u32 reserved[4]; +}; + +struct stRttHostTS { + u32 sec; + u32 nsec; +}; + +struct nsp_rttd2h2_clkoffset { + struct stRttHostTS tabs_h2[NUM_CLKOFFSETCAL_SAMPLES]; + u8 numsamples; + u8 reserved[3]; +}; + + +struct nsp_rtt_clkoffset { + u32 tdelta_d1d2[NUM_CLKOFFSETCAL_SAMPLES]; + struct stRttHostTS tabs_h1[NUM_CLKOFFSETCAL_SAMPLES]; + struct stRttHostTS tabs_h2[NUM_CLKOFFSETCAL_SAMPLES]; + u32 tabs_d1[NUM_CLKOFFSETCAL_SAMPLES]; + u32 tabs_d2[NUM_CLKOFFSETCAL_SAMPLES]; + u8 numsamples; + u8 reserved[3]; +}; + +#define MREQ_LEN sizeof(struct nsp_mrqst) +#define MRES_LEN sizeof(struct nsp_mresphdr) +#define CIR_RES_LEN sizeof(struct nsp_cir_resp) + +enum NSP_FRAME_TYPE { + NSP_MRQST = 1, + NSP_CIRMRESP, + NSP_RTTMRESP, + NSP_SRQST, + NSP_SRESP, + NSP_CRQST, + NSP_CRESP, + NSP_WRQST, + NSP_WRESP, + NSP_SLRQST, + NSP_SLRESP, + NSP_RTTCONFIG, + NSP_RTTCLKCAL_INFO, +}; + + +#endif diff --git a/drivers/net/wireless/ath/ath6kl-3.5/wmi.c b/drivers/net/wireless/ath/ath6kl-3.5/wmi.c new file mode 100644 index 000000000000..b9a9dce2136c --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/wmi.c @@ -0,0 +1,5854 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "debug.h" +#include "testmode.h" +#include "wlan_location_defs.h" +#include "rttapi.h" +#ifdef ATHTST_SUPPORT +#include "ce_athtst.h" +#endif +#ifdef ATH6KL_DIAGNOSTIC +struct wmi *globalwmi; +#endif + +static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx); + +static const s32 wmi_rate_tbl[][2] = { + /* {W/O SGI, with SGI} */ + {1000, 1000}, + {2000, 2000}, + {5500, 5500}, + {11000, 11000}, + {6000, 6000}, + {9000, 9000}, + {12000, 12000}, + {18000, 18000}, + {24000, 24000}, + {36000, 36000}, + {48000, 48000}, + {54000, 54000}, + {6500, 7200}, + {13000, 14400}, + {19500, 21700}, + {26000, 28900}, + {39000, 43300}, + {52000, 57800}, + {58500, 65000}, + {65000, 72200}, + {13500, 15000}, + {27000, 30000}, + {40500, 45000}, + {54000, 60000}, + {81000, 90000}, + {108000, 120000}, + {121500, 135000}, + {135000, 150000}, + {0, 0} +}; + +static const s32 wmi_rate_tbl_ar6004[][2] = { + {1000, 1000}, + {2000, 2000}, + {5500, 5500}, + {11000, 11000}, + {6000, 6000}, + {9000, 9000}, + {12000, 12000}, + {18000, 18000}, + {24000, 24000}, + {36000, 36000}, + {48000, 48000}, + {54000, 54000}, + {6500, 7200}, /* HT 20, MCS 0 */ + {13000, 14400}, + {19500, 21700}, + {26000, 28900}, + {39000, 43300}, + {52000, 57800}, + {58500, 65000}, + {65000, 72200}, + {13000, 14400}, /* HT 20, MCS 8 */ + {26000, 28900}, + {39000, 43300}, + {52000, 57800}, + {78000, 86700}, + {104000, 115600}, + {117000, 130000}, + {130000, 144400}, /* HT 20, MCS 15 */ + {13500, 15000}, /*HT 40, MCS 0 */ + {27000, 30000}, + {40500, 45000}, + {54000, 60000}, + {81000, 90000}, + {108000, 120000}, + {121500, 135000}, + {135000, 150000}, + {27000, 30000}, /*HT 40, MCS 8 */ + {54000, 60000}, + {81000, 90000}, + {108000, 120000}, + {162000, 180000}, + {216000, 240000}, + {243000, 270000}, + {270000, 300000}, /*HT 40, MCS 15 */ + {0, 0} +}; + +/* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */ +static const u8 up_to_ac[] = { + WMM_AC_BE, + WMM_AC_BK, + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO, +}; + +static bool ath6kl_wmi_report_rx_mgmt(struct net_device *dev, int freq, + int sig_mbm, const u8 *buf, size_t len, gfp_t gfp) +{ +#ifdef NL80211_ATTR_WIPHY_RX_SIGNAL_DBM +#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV + BUG_ON(!dev->ieee80211_ptr); + + return cfg80211_rx_mgmt(dev->ieee80211_ptr, + freq, + sig_mbm, + buf, + len, + gfp); +#else + return cfg80211_rx_mgmt(dev, freq, sig_mbm, buf, len, gfp); +#endif +#else + return cfg80211_rx_mgmt(dev, freq, buf, len, gfp); +#endif +} + +static void ath6kl_wmi_ready_on_channel(struct net_device *ndev, + u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, gfp_t gfp) +{ +#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV + BUG_ON(!ndev->ieee80211_ptr); + +#ifdef CFG80211_REMOVE_ROC_CHAN_TYPE + cfg80211_ready_on_channel(ndev->ieee80211_ptr, + cookie, + chan, + duration, + gfp); +#else + cfg80211_ready_on_channel(ndev->ieee80211_ptr, + cookie, + chan, + channel_type, + duration, + gfp); +#endif +#else + cfg80211_ready_on_channel(ndev, + cookie, + chan, + channel_type, + duration, + gfp); +#endif +} + +static void ath6kl_wmi_remain_on_channel_expired(struct net_device *ndev, + u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + gfp_t gfp) +{ +#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV + BUG_ON(!ndev->ieee80211_ptr); + +#ifdef CFG80211_REMOVE_ROC_CHAN_TYPE + cfg80211_remain_on_channel_expired(ndev->ieee80211_ptr, + cookie, + chan, + gfp); +#else + cfg80211_remain_on_channel_expired(ndev->ieee80211_ptr, + cookie, + chan, + channel_type, + gfp); +#endif +#else + cfg80211_remain_on_channel_expired(ndev, + cookie, + chan, + channel_type, + gfp); +#endif +} + +static void ath6kl_wmi_mgmt_tx_status(struct net_device *ndev, u64 cookie, + const u8 *buf, size_t len, bool ack, gfp_t gfp) + +{ +#ifdef CFG80211_NETDEV_REPLACED_BY_WDEV + BUG_ON(!ndev->ieee80211_ptr); + + cfg80211_mgmt_tx_status(ndev->ieee80211_ptr, + cookie, + buf, + len, + ack, + gfp); +#else + cfg80211_mgmt_tx_status(ndev, + cookie, + buf, + len, + ack, + gfp); +#endif +} + +void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id) +{ + if (WARN_ON(ep_id == ENDPOINT_UNUSED || ep_id >= ENDPOINT_MAX)) + return; + + wmi->ep_id = ep_id; +} + +enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi) +{ + return wmi->ep_id; +} + +struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx) +{ + struct ath6kl_vif *vif, *found = NULL; + + if (WARN_ON(if_idx > (ar->vif_max - 1))) + return NULL; + + /* FIXME: Locking */ + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (vif->fw_vif_idx == if_idx) { + found = vif; + break; + } + } + spin_unlock_bh(&ar->list_lock); + + return found; +} + +/* Performs DIX to 802.3 encapsulation for transmit packets. + * Assumes the entire DIX header is contigous and that there is + * enough room in the buffer for a 802.3 mac header and LLC+SNAP headers. + */ +int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb) +{ + struct ath6kl_llc_snap_hdr *llc_hdr; + struct ethhdr *eth_hdr; + size_t new_len; + __be16 type; + u8 *datap; + u16 size; + + if (WARN_ON(skb == NULL)) + return -EINVAL; + + size = sizeof(struct ath6kl_llc_snap_hdr) + sizeof(struct wmi_data_hdr); + if (skb_headroom(skb) < size) + return -ENOMEM; + + eth_hdr = (struct ethhdr *) skb->data; + type = eth_hdr->h_proto; + + if (!is_ethertype(be16_to_cpu(type))) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "%s: pkt is already in 802.3 format\n", __func__); + return 0; + } + + new_len = skb->len - sizeof(*eth_hdr) + sizeof(*llc_hdr); + + skb_push(skb, sizeof(struct ath6kl_llc_snap_hdr)); + datap = skb->data; + + eth_hdr->h_proto = cpu_to_be16(new_len); + + memcpy(datap, eth_hdr, sizeof(*eth_hdr)); + + llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap + sizeof(*eth_hdr)); + llc_hdr->dsap = 0xAA; + llc_hdr->ssap = 0xAA; + llc_hdr->cntl = 0x03; + llc_hdr->org_code[0] = 0x0; + llc_hdr->org_code[1] = 0x0; + llc_hdr->org_code[2] = 0x0; + llc_hdr->eth_type = type; + + return 0; +} + +static int ath6kl_wmi_meta_add(struct wmi *wmi, struct sk_buff *skb, + u8 *version, void *tx_meta_info) +{ + struct wmi_tx_meta_v1 *v1; + struct wmi_tx_meta_v2 *v2; + + if (WARN_ON(skb == NULL || version == NULL)) + return -EINVAL; + + switch (*version) { + case WMI_META_VERSION_1: + skb_push(skb, WMI_MAX_TX_META_SZ); + v1 = (struct wmi_tx_meta_v1 *) skb->data; + v1->pkt_id = 0; + v1->rate_plcy_id = 0; + *version = WMI_META_VERSION_1; + break; + case WMI_META_VERSION_2: + skb_push(skb, WMI_MAX_TX_META_SZ); + v2 = (struct wmi_tx_meta_v2 *) skb->data; + memcpy(v2, (struct wmi_tx_meta_v2 *) tx_meta_info, + sizeof(struct wmi_tx_meta_v2)); + break; + } + + return 0; +} + +int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, + u8 msg_type, u32 flags, + enum wmi_data_hdr_data_type data_type, + u8 meta_ver, void *tx_meta_info, u8 if_idx) +{ + struct wmi_data_hdr *data_hdr; + int ret; + + if (WARN_ON(skb == NULL || (if_idx > wmi->parent_dev->vif_max - 1))) + return -EINVAL; + + if (tx_meta_info) { + ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info); + if (ret) + return ret; + } + + skb_push(skb, sizeof(struct wmi_data_hdr)); + + data_hdr = (struct wmi_data_hdr *)skb->data; + memset(data_hdr, 0, sizeof(struct wmi_data_hdr)); + + data_hdr->info = msg_type << WMI_DATA_HDR_MSG_TYPE_SHIFT; + data_hdr->info |= data_type << WMI_DATA_HDR_DATA_TYPE_SHIFT; + + data_hdr->info2 = meta_ver << WMI_DATA_HDR_META_SHIFT; + data_hdr->info3 = if_idx & WMI_DATA_HDR_IF_IDX_MASK; + + if (flags & WMI_DATA_HDR_FLAGS_MORE) + WMI_DATA_HDR_SET_MORE_BIT(data_hdr); + + if (flags & WMI_DATA_HDR_FLAGS_EOSP) + WMI_DATA_HDR_SET_EOSP_BIT(data_hdr); + + if (flags & WMI_DATA_HDR_FLAGS_TRIGGERED) + WMI_DATA_HDR_SET_TRIGGERED_BIT(data_hdr); + + if (flags & WMI_DATA_HDR_FLAGS_PSPOLLED) + WMI_DATA_HDR_SET_PSPOLLED_BIT(data_hdr); + + data_hdr->info2 = cpu_to_le16(data_hdr->info2); + data_hdr->info3 = cpu_to_le16(data_hdr->info3); + + return 0; +} + +u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri) +{ + struct iphdr *ip_hdr = (struct iphdr *) pkt; + u8 ip_pri; + + /* + * Determine IPTOS priority + * + * IP-TOS - 8bits + * : DSCP(6-bits) ECN(2-bits) + * : DSCP - P2 P1 P0 X X X + * where (P2 P1 P0) form 802.1D + */ + ip_pri = ip_hdr->tos >> 5; + ip_pri &= 0x7; + + if ((layer2_pri & 0x7) > ip_pri) + return (u8) layer2_pri & 0x7; + else + return ip_pri; +} + +int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx, + struct sk_buff *skb, + u32 layer2_priority, bool wmm_enabled, + u8 *ac, u16 *phtc_tag) +{ + struct wmi_data_hdr *data_hdr; + struct ath6kl_llc_snap_hdr *llc_hdr; + struct wmi_create_pstream_cmd cmd; + u32 meta_size, hdr_size; + u16 ip_type = IP_ETHERTYPE; + u8 stream_exist, usr_pri; + u8 traffic_class = WMM_AC_BE; + u8 *datap; + + if (WARN_ON(skb == NULL)) + return -EINVAL; + + datap = skb->data; + data_hdr = (struct wmi_data_hdr *) datap; + + meta_size = ((le16_to_cpu(data_hdr->info2) >> WMI_DATA_HDR_META_SHIFT) & + WMI_DATA_HDR_META_MASK) ? WMI_MAX_TX_META_SZ : 0; + + if (!wmm_enabled) { + /* If WMM is disabled all traffic goes as BE traffic */ + usr_pri = 0; + } else { + hdr_size = sizeof(struct ethhdr); + + llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap + + sizeof(struct + wmi_data_hdr) + + meta_size + hdr_size); + + if (llc_hdr->eth_type == htons(ip_type)) { + /* + * Extract the endpoint info from the TOS field + * in the IP header. + */ + usr_pri = + ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) + + sizeof(struct ath6kl_llc_snap_hdr), + layer2_priority); + } else + usr_pri = layer2_priority & 0x7; + + /* + * Queue the EAPOL frames in the same WMM_AC_VO queue + * as that of management frames. + */ + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + usr_pri = WMI_VOICE_USER_PRIORITY; + *phtc_tag = ATH6KL_PRI_DATA_PKT_TAG; + } + } + + /* + * workaround for WMM S5 + * + * FIXME: wmi->traffic_class is always 100 so this test doesn't + * make sense + */ + if ((wmi->traffic_class == WMM_AC_VI) && + ((usr_pri == 5) || (usr_pri == 4))) + usr_pri = 1; + + /* Convert user priority to traffic class */ + traffic_class = up_to_ac[usr_pri & 0x7]; + + wmi_data_hdr_set_up(data_hdr, usr_pri); + + spin_lock_bh(&wmi->lock); + stream_exist = wmi->fat_pipe_exist; + + if (!(stream_exist & (1 << traffic_class))) { + wmi->fat_pipe_exist |= (1 << traffic_class); + spin_unlock_bh(&wmi->lock); + + memset(&cmd, 0, sizeof(cmd)); + cmd.traffic_class = traffic_class; + cmd.user_pri = usr_pri; + cmd.inactivity_int = + cpu_to_le32(WMI_IMPLICIT_PSTREAM_INACTIVITY_INT); + /* Implicit streams are created with TSID 0xFF */ + cmd.tsid = WMI_IMPLICIT_PSTREAM; + ath6kl_wmi_create_pstream_cmd(wmi, if_idx, &cmd); + + spin_lock_bh(&wmi->lock); + } + spin_unlock_bh(&wmi->lock); + + *ac = traffic_class; + + return 0; +} + +int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb) +{ + struct ieee80211_hdr_3addr *pwh, wh; + struct ath6kl_llc_snap_hdr *llc_hdr; + struct ethhdr eth_hdr; + u32 hdr_size; + u8 *datap; + __le16 sub_type; + + if (WARN_ON(skb == NULL)) + return -EINVAL; + + datap = skb->data; + pwh = (struct ieee80211_hdr_3addr *) datap; + + sub_type = pwh->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE); + + memcpy((u8 *) &wh, datap, sizeof(struct ieee80211_hdr_3addr)); + + /* Strip off the 802.11 header */ + if (sub_type == cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { + hdr_size = roundup(sizeof(struct ieee80211_qos_hdr), + sizeof(u32)); + skb_pull(skb, hdr_size); + } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA)) + skb_pull(skb, sizeof(struct ieee80211_hdr_3addr)); + + datap = skb->data; + llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap); + + memset(ð_hdr, 0, sizeof(eth_hdr)); + eth_hdr.h_proto = llc_hdr->eth_type; + + switch ((le16_to_cpu(wh.frame_control)) & + (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { + case 0: + memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN); + memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN); + break; + case IEEE80211_FCTL_TODS: + memcpy(eth_hdr.h_dest, wh.addr3, ETH_ALEN); + memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN); + break; + case IEEE80211_FCTL_FROMDS: + memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN); + memcpy(eth_hdr.h_source, wh.addr3, ETH_ALEN); + break; + case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: + break; + } + + skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr)); + skb_push(skb, sizeof(eth_hdr)); + + datap = skb->data; + + memcpy(datap, ð_hdr, sizeof(eth_hdr)); + + return 0; +} + +/* + * Performs 802.3 to DIX encapsulation for received packets. + * Assumes the entire 802.3 header is contigous. + */ +int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb) +{ + struct ath6kl_llc_snap_hdr *llc_hdr; + struct ethhdr eth_hdr; + u8 *datap; + + if (WARN_ON(skb == NULL)) + return -EINVAL; + + datap = skb->data; + + memcpy(ð_hdr, datap, sizeof(eth_hdr)); + + llc_hdr = (struct ath6kl_llc_snap_hdr *) (datap + sizeof(eth_hdr)); + eth_hdr.h_proto = llc_hdr->eth_type; + + skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr)); + datap = skb->data; + + memcpy(datap, ð_hdr, sizeof(eth_hdr)); + + return 0; +} + +static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) +{ + struct tx_complete_msg_v1 *msg_v1; + struct wmi_tx_complete_event *evt; + int index; + u16 size; + + evt = (struct wmi_tx_complete_event *) datap; + + ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n", + evt->num_msg, evt->msg_len, evt->msg_type); + + if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_WMI)) + return 0; + + for (index = 0; index < evt->num_msg; index++) { + size = sizeof(struct wmi_tx_complete_event) + + (index * sizeof(struct tx_complete_msg_v1)); + msg_v1 = (struct tx_complete_msg_v1 *)(datap + size); + + ath6kl_dbg(ATH6KL_DBG_WMI, "msg: %d %d %d %d\n", + msg_v1->status, msg_v1->pkt_id, + msg_v1->rate_idx, msg_v1->ack_failures); + } + + return 0; +} + +static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap, + int len, struct ath6kl_vif *vif) +{ + struct wmi_remain_on_chnl_event *ev; + u32 freq; + u32 dur; + struct ieee80211_channel *chan; + struct ath6kl *ar = wmi->parent_dev; + u32 id; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_remain_on_chnl_event *) datap; + freq = le32_to_cpu(ev->freq); + dur = le32_to_cpu(ev->duration); + ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n", + freq, dur); + chan = ieee80211_get_channel(ar->wiphy, freq); + if (!chan) { + ath6kl_err("RoC : remain_on_chnl: Unknown channel " + "(freq=%u) (dur=%u)\n", freq, dur); + return -EINVAL; + } + + spin_lock_bh(&vif->if_lock); + id = vif->last_roc_id; + spin_unlock_bh(&vif->if_lock); + + clear_bit(ROC_PEND, &vif->flags); + set_bit(ROC_ONGOING, &vif->flags); + + /* RoC already be cancelled by user. */ + if (id == vif->last_cancel_roc_id) { + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC : This RoC already be cancelled by user %x\n", id); + } else { + ath6kl_wmi_ready_on_channel(vif->ndev, + id, chan, NL80211_CHAN_NO_HT, dur, GFP_ATOMIC); + } + + if (test_bit(ROC_WAIT_EVENT, &vif->flags)) { + clear_bit(ROC_WAIT_EVENT, &vif->flags); + wake_up(&ar->event_wq); + } + return 0; +} + +static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi, + u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_cancel_remain_on_chnl_event *ev; + u32 freq; + u32 dur; + struct ieee80211_channel *chan; + struct ath6kl *ar = wmi->parent_dev; + u32 id; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_cancel_remain_on_chnl_event *) datap; + freq = le32_to_cpu(ev->freq); + dur = le32_to_cpu(ev->duration); + ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u " + "status=%u\n", freq, dur, ev->status); + + /* + * One case is target reject RoC request because of some reasons. + * Another case is user cancel a non-exist RoC. + */ + if (ev->status != 0) { + if (test_bit(ROC_PEND, &vif->flags)) { + BUG_ON(!vif->last_roc_channel); + + ath6kl_err("RoC : request %d %d reject by target %d\n", + vif->last_roc_id, + vif->last_roc_channel->center_freq, + ev->status); + + /* To sync user's RoC id only for long listen. */ + if (vif->last_roc_duration == + (ATH6KL_ROC_MAX_PERIOD * 1000)) + ath6kl_wmi_ready_on_channel(vif->ndev, + vif->last_roc_id, + vif->last_roc_channel, + NL80211_CHAN_NO_HT, + vif->last_roc_duration, + GFP_ATOMIC); + + /* + * Still report RoC-End, suppose the user will handle + * this case. + */ + ath6kl_wmi_remain_on_channel_expired(vif->ndev, + vif->last_roc_id, + vif->last_roc_channel, + NL80211_CHAN_NO_HT, + GFP_ATOMIC); + vif->last_cancel_roc_id = 0; + clear_bit(ROC_PEND, &vif->flags); + + return 0; + } else + ath6kl_dbg(ATH6KL_DBG_WMI, "RoC : cancal fail %d %d\n", + test_bit(ROC_ONGOING, &vif->flags), + ev->status); + } + + chan = ieee80211_get_channel(ar->wiphy, freq); + if (!chan) { + spin_lock_bh(&vif->if_lock); + vif->last_cancel_roc_id = 0; + spin_unlock_bh(&vif->if_lock); + + ath6kl_err("RoC : cancel_remain_on_chnl: Unknown " + "channel (freq=%u) (dur=%u)\n", freq, dur); + return -EINVAL; + } + + /* If the channel already be closed and send TX fail to supplicant. */ + if (!list_empty(&wmi->mgmt_tx_frame_list)) { + struct wmi_mgmt_tx_frame *mgmt_tx_frame, *tmp; + + list_for_each_entry_safe(mgmt_tx_frame, tmp, + &wmi->mgmt_tx_frame_list, list) { + + ath6kl_dbg(ATH6KL_DBG_INFO, + "RoC close but not yet get previous " + "tx-status %x %d\n", + mgmt_tx_frame->mgmt_tx_frame_idx, + vif->fw_vif_idx); + + if (mgmt_tx_frame->vif == vif) { + list_del(&mgmt_tx_frame->list); + + ath6kl_wmi_mgmt_tx_status(vif->ndev, + mgmt_tx_frame->mgmt_tx_frame_idx, + mgmt_tx_frame->mgmt_tx_frame, + mgmt_tx_frame->mgmt_tx_frame_len, + false, GFP_ATOMIC); + kfree(mgmt_tx_frame->mgmt_tx_frame); + kfree(mgmt_tx_frame); + } + } + } + + spin_lock_bh(&vif->if_lock); + if (vif->last_cancel_roc_id && + vif->last_cancel_roc_id + 1 == vif->last_roc_id) + id = vif->last_cancel_roc_id; /* event for cancel command */ + else + id = vif->last_roc_id; /* timeout on uncanceled r-o-c */ + vif->last_cancel_roc_id = 0; + spin_unlock_bh(&vif->if_lock); + + clear_bit(ROC_ONGOING, &vif->flags); + if (test_bit(ROC_CANCEL_PEND, &vif->flags)) { + /* Cancel by driver and should use last_roc_id, + not last_cancel_roc_id */ + ath6kl_wmi_remain_on_channel_expired(vif->ndev, id, chan, + NL80211_CHAN_NO_HT, + GFP_ATOMIC); + + clear_bit(ROC_CANCEL_PEND, &vif->flags); + wake_up(&ar->event_wq); + } else { + ath6kl_wmi_remain_on_channel_expired(vif->ndev, id, chan, + NL80211_CHAN_NO_HT, + GFP_ATOMIC); + if (test_bit(ROC_WAIT_EVENT, &vif->flags)) { + clear_bit(ROC_WAIT_EVENT, &vif->flags); + wake_up(&ar->event_wq); + } + + } + + return 0; +} + +static int ath6kl_wmi_resend_action_cmd(struct wmi *wmi, + struct wmi_mgmt_tx_frame *mgmt_tx_frame) +{ + struct sk_buff *skb; + struct wmi_send_action_cmd *p; + u32 wait = 0; + + BUG_ON(mgmt_tx_frame->mgmt_tx_frame_retry == 0); + + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + + mgmt_tx_frame->mgmt_tx_frame_len); + if (!skb) + return -ENOMEM; + + mgmt_tx_frame->mgmt_tx_frame_retry--; + list_add_tail(&mgmt_tx_frame->list, &wmi->mgmt_tx_frame_list); + + ath6kl_dbg(ATH6KL_DBG_WMI, "resend_action_cmd: id=%u freq=%u len=%u\n", + mgmt_tx_frame->mgmt_tx_frame_idx, + mgmt_tx_frame->mgmt_tx_frame_freq, + mgmt_tx_frame->mgmt_tx_frame_len); + + p = (struct wmi_send_action_cmd *) skb->data; + p->id = cpu_to_le32(mgmt_tx_frame->mgmt_tx_frame_idx); + p->freq = cpu_to_le32(mgmt_tx_frame->mgmt_tx_frame_freq); + p->wait = cpu_to_le32(wait); + p->len = cpu_to_le16(mgmt_tx_frame->mgmt_tx_frame_len); + memcpy(p->data, + mgmt_tx_frame->mgmt_tx_frame, + mgmt_tx_frame->mgmt_tx_frame_len); + + return ath6kl_wmi_cmd_send(wmi, + mgmt_tx_frame->vif->fw_vif_idx, + skb, + WMI_SEND_ACTION_CMDID, + NO_SYNC_WMIFLAG); +} + +static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_tx_status_event *ev; + u32 id; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_tx_status_event *) datap; + id = le32_to_cpu(ev->id); + ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n", + id, ev->ack_status); + + if (!list_empty(&wmi->mgmt_tx_frame_list)) { + struct wmi_mgmt_tx_frame *mgmt_tx_frame, *tmp; + int found = 0, seq = 0; + + list_for_each_entry_safe(mgmt_tx_frame, tmp, + &wmi->mgmt_tx_frame_list, list) { + seq++; + if (mgmt_tx_frame->mgmt_tx_frame_idx == id) { + list_del(&mgmt_tx_frame->list); + + if ((ev->ack_status == 0) && + (mgmt_tx_frame->mgmt_tx_frame_retry) && + (ath6kl_wmi_resend_action_cmd(wmi, + mgmt_tx_frame) == 0)) + return 0; + + found = 1; + ath6kl_wmi_mgmt_tx_status(vif->ndev, id, + mgmt_tx_frame->mgmt_tx_frame, + mgmt_tx_frame->mgmt_tx_frame_len, + !!ev->ack_status, GFP_ATOMIC); + kfree(mgmt_tx_frame->mgmt_tx_frame); + kfree(mgmt_tx_frame); + break; + } + } + + if (!found) { + ath6kl_err("tx_status_report: " + "unexpected report? seq = %d\n", seq); + } else if (seq > 1) { + ath6kl_err("tx_status_report: " + "not by order, seq = %d\n", seq); + } + } + + return 0; +} + +static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_p2p_rx_probe_req_event *ev; + u32 freq; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_p2p_rx_probe_req_event *) datap; + freq = le32_to_cpu(ev->freq); + dlen = le16_to_cpu(ev->len); +#ifdef CE_SUPPORT + if (ath6kl_ce_flags == 1) + dlen = dlen+4;/* enlarge packet and put rssi to the end */ +#endif + if (datap + len < ev->data + dlen) { + ath6kl_err("invalid wmi_p2p_rx_probe_req_event: " + "len=%d dlen=%u\n", len, dlen); + return -EINVAL; + } + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u freq=%u " + "probe_req_report=%d\n", + dlen, freq, vif->probe_req_report); + + if (vif->probe_req_report || vif->nw_type == AP_NETWORK) + ath6kl_wmi_report_rx_mgmt(vif->ndev, + freq, + 0, + ev->data, + dlen, + GFP_ATOMIC); + + return 0; +} +#ifdef CE_SUPPORT +static int ath6kl_wmi_rx_probe_resp_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + struct wmi_p2p_rx_probe_resp_event *ev; + u32 freq; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_p2p_rx_probe_resp_event *) datap; + freq = le32_to_cpu(ev->freq); + dlen = le16_to_cpu(ev->len); +#ifdef CE_SUPPORT + if (ath6kl_ce_flags == 1) + dlen = dlen+4;/* enlarge packet and put rssi to the end */ +#endif + if (datap + len < ev->data + dlen) { + ath6kl_err("invalid wmi_p2p_rx_probe_resp_event: " + "len=%d dlen=%u\n", len, dlen); + return -EINVAL; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_resp: len=%u freq=%u " + "probe_resp_report=%d\n", + dlen, freq, vif->probe_resp_report); + + if (vif->probe_resp_report) + ath6kl_wmi_report_rx_mgmt(vif->ndev, + freq, + 0, + ev->data, + dlen, + GFP_ATOMIC); + + return 0; +} + +static int ath6kl_wmi_fake_probe_resp_event_by_bssinfo(struct wmi *wmi, + u8 *datap, int len, struct ath6kl_vif *vif) +{ +#define _DEFAULT_SNR (96) /* -96 dBm */ + struct wmi_p2p_rx_probe_resp_event *ev; + struct wmi_bss_info_hdr2 *bih; + u8 *buf, *fake_datap; + struct ieee80211_mgmt *mgmt; + u32 rssi, fake_len; + struct net_device *dev = vif->ndev; + + if (len <= sizeof(struct wmi_bss_info_hdr2)) + return -EINVAL; + + bih = (struct wmi_bss_info_hdr2 *) datap; + buf = datap + sizeof(struct wmi_bss_info_hdr2); + len -= sizeof(struct wmi_bss_info_hdr2); + + if (bih->frame_type != PROBERESP_FTYPE) + return 0; /* Only update BSS table for now */ + + if (bih->snr == 0x80) + return -EINVAL; + fake_len = sizeof(struct wmi_p2p_rx_probe_resp_event) + 24 + len + 4; + fake_datap = kmalloc(fake_len, GFP_ATOMIC); + if (fake_datap == NULL) + return -EINVAL; + ev = (struct wmi_p2p_rx_probe_resp_event *)fake_datap; + ev->freq = cpu_to_le32(-1); + ev->len = cpu_to_le16(24 + len); + + mgmt = (struct ieee80211_mgmt *)(fake_datap + + sizeof(struct wmi_p2p_rx_probe_resp_event)); + if (mgmt == NULL) + return -EINVAL; + + mgmt->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_RESP); + memcpy(mgmt->da, dev->dev_addr, ETH_ALEN); + + mgmt->duration = cpu_to_le16(0); + memcpy(mgmt->sa, bih->bssid, ETH_ALEN); + memcpy(mgmt->bssid, bih->bssid, ETH_ALEN); + mgmt->seq_ctrl = cpu_to_le16(0); + + memcpy(&mgmt->u.probe_resp, buf, len); + rssi = bih->snr; + memcpy(fake_datap + fake_len - 4, &rssi, 4); + + ath6kl_wmi_rx_probe_resp_event_rx(vif, fake_datap, fake_len); + kfree(fake_datap); + return 0; +#undef _DEFAULT_SNR +} +#endif + +#ifdef ACL_SUPPORT +static int ath6kl_wmi_acl_reject_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + struct wmi_acl_reject_event *ev; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_acl_reject_event *) datap; + + dlen = le16_to_cpu(ev->len); + + if (datap + len < ev->data + dlen) { + ath6kl_err("invalid ath6kl_wmi_acl_reject_event_rx: " + "len=%d dlen=%u\n", len, dlen); + return -EINVAL; + } + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_resp: len=%u " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + dlen, ev->data[0], ev->data[1], ev->data[2], + ev->data[3], ev->data[4], ev->data[5]); + cfg80211_rx_acl_reject_info(vif->ndev, ev->data, dlen, GFP_ATOMIC); + + return 0; +} +#endif + +static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len) +{ + struct wmi_p2p_capabilities_event *ev; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_p2p_capabilities_event *) datap; + dlen = le16_to_cpu(ev->len); + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_capab: len=%u\n", dlen); + + return 0; +} + +static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_rx_action_event *ev; + u32 freq; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_rx_action_event *) datap; + freq = le32_to_cpu(ev->freq); + dlen = le16_to_cpu(ev->len); +#ifdef CE_SUPPORT + if (ath6kl_ce_flags == 1) + dlen = dlen+4;/* enlarge packet and put rssi to the end */ +#endif + if (datap + len < ev->data + dlen) { + ath6kl_err("invalid wmi_rx_action_event: " + "len=%d dlen=%u\n", len, dlen); + return -EINVAL; + } + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); + + if (vif->ar->p2p_frame_not_report && + !test_bit(CONNECTED, &vif->flags) && + test_bit(SCANNING, &vif->flags) && + ath6kl_p2p_is_p2p_frame(vif->ar, (const u8 *) ev->data, dlen)) { + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: not report to user!\n"); + ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, __func__, "action rx ", + ev->data, dlen); + + return 0; + } + + ath6kl_wmi_report_rx_mgmt(vif->ndev, + freq, + 0, + ev->data, + dlen, + GFP_ATOMIC); + + return 0; +} + +static int ath6kl_wmi_p2p_info_event_rx(u8 *datap, int len) +{ + struct wmi_p2p_info_event *ev; + u32 flags; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_p2p_info_event *) datap; + flags = le32_to_cpu(ev->info_req_flags); + dlen = le16_to_cpu(ev->len); + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: flags=%x len=%d\n", flags, dlen); + + if (flags & P2P_FLAG_CAPABILITIES_REQ) { + struct wmi_p2p_capabilities *cap; + if (dlen < sizeof(*cap)) + return -EINVAL; + cap = (struct wmi_p2p_capabilities *) ev->data; + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: GO Power Save = %d\n", + cap->go_power_save); + } + + if (flags & P2P_FLAG_MACADDR_REQ) { + struct wmi_p2p_macaddr *mac; + if (dlen < sizeof(*mac)) + return -EINVAL; + mac = (struct wmi_p2p_macaddr *) ev->data; + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: MAC Address = %pM\n", + mac->mac_addr); + } + + if (flags & P2P_FLAG_HMODEL_REQ) { + struct wmi_p2p_hmodel *mod; + if (dlen < sizeof(*mod)) + return -EINVAL; + mod = (struct wmi_p2p_hmodel *) ev->data; + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: P2P Model = %d (%s)\n", + mod->p2p_model, + mod->p2p_model ? "host" : "firmware"); + } + return 0; +} + +static int ath6kl_wmi_flowctrl_ind_event_rx(u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + struct wmi_flowctrl_ind_event *ev; + + if ((!(ar->conf_flags & ATH6KL_CONF_ENABLE_FLOWCTRL)) || + (test_bit(SKIP_FLOWCTRL_EVENT, &ar->flag)) || + (ar->vif_max == 1)) + return 0; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_flowctrl_ind_event *) datap; + + ath6kl_dbg(ATH6KL_DBG_WMI, "flowctrl_info: num_of_conn=%d " + "ac_map[0]=0x%x ac_map[1]=0x%x ac_map[2]=0x%x\n", + ev->num_of_conn, ev->ac_map[0], + ev->ac_map[1], ev->ac_map[2]); + + ath6kl_p2p_flowctrl_state_update(ar, + ev->num_of_conn, + ev->ac_map, + ev->ac_queue_depth); + ath6kl_p2p_flowctrl_state_change(ar); + ath6kl_p2p_flowctrl_tx_schedule(ar); + + return 0; +} + +struct sk_buff *ath6kl_wmi_get_new_buf(u32 size) +{ + struct sk_buff *skb; + + skb = ath6kl_buf_alloc(size); + if (!skb) + return NULL; + + skb_put(skb, size); + if (size) + memset(skb->data, 0, size); + + return skb; +} + +/* Send a "simple" wmi command -- one with no arguments */ +int ath6kl_wmi_simple_cmd(struct wmi *wmi, u8 if_idx, + enum wmi_cmd_id cmd_id) +{ + struct sk_buff *skb; + int ret; + + skb = ath6kl_wmi_get_new_buf(0); + if (!skb) + return -ENOMEM; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, cmd_id, NO_SYNC_WMIFLAG); + + return ret; +} + +static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len) +{ + struct wmi_ready_event_2 *ev = (struct wmi_ready_event_2 *) datap; + + if (len < sizeof(struct wmi_ready_event_2)) + return -EINVAL; + + ath6kl_ready_event(wmi->parent_dev, ev->mac_addr, + le32_to_cpu(ev->sw_version), + le32_to_cpu(ev->abi_version)); + + return 0; +} + +/* + * Mechanism to modify the roaming behavior in the firmware. The lower rssi + * at which the station has to roam can be passed with + * WMI_SET_LRSSI_SCAN_PARAMS. Subtract 96 from RSSI to get the signal level + * in dBm. + */ +int ath6kl_wmi_set_roam_ctrl_cmd_for_lowerrssi(struct wmi *wmi, + u16 lowrssi_scan_period, + u16 lowrssi_scan_threshold, + u16 lowrssi_roam_threshold, + u8 roam_rssi_floor) +{ + struct sk_buff *skb; + struct roam_ctrl_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct roam_ctrl_cmd *) skb->data; + + cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS; + cmd->info.params.lrssi_scan_period = cpu_to_le16(lowrssi_scan_period); + cmd->info.params.lrssi_scan_threshold = + cpu_to_le16(lowrssi_scan_threshold); + cmd->info.params.lrssi_roam_threshold = + cpu_to_le16(lowrssi_roam_threshold); + cmd->info.params.roam_rssi_floor = roam_rssi_floor; + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID, + NO_SYNC_WMIFLAG); + return ret; + +} + +int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid) +{ + struct sk_buff *skb; + struct roam_ctrl_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct roam_ctrl_cmd *) skb->data; + memset(cmd, 0, sizeof(*cmd)); + + memcpy(cmd->info.bssid, bssid, ETH_ALEN); + cmd->roam_ctrl = WMI_FORCE_ROAM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "force roam to %pM\n", bssid); + return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode) +{ + struct sk_buff *skb; + struct roam_ctrl_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct roam_ctrl_cmd *) skb->data; + memset(cmd, 0, sizeof(*cmd)); + + cmd->info.roam_mode = mode; + cmd->roam_ctrl = WMI_SET_ROAM_MODE; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set roam mode %d\n", mode); + return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID, + NO_SYNC_WMIFLAG); +} + +static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_connect_event *ev; + u8 *pie, *peie; + u8 *assoc_req = NULL; + u16 assoc_req_len = 0; + + if (len < sizeof(struct wmi_connect_event)) + return -EINVAL; + + ev = (struct wmi_connect_event *) datap; + + if (vif->nw_type == AP_NETWORK) { + /* AP mode start/STA connected event */ + struct net_device *dev = vif->ndev; + if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) { + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM " + "(AP started)\n", + __func__, le16_to_cpu(ev->u.ap_bss.ch), + ev->u.ap_bss.bssid); + ath6kl_p2p_flowctrl_set_conn_id(vif, + ev->u.ap_bss.bssid, + ev->u.ap_bss.aid); + ath6kl_connect_ap_mode_bss( + vif, + le16_to_cpu(ev->u.ap_bss.ch), + ev->assoc_info, + ev->beacon_ie_len); + + /* Start keep-alive if need. */ + ath6kl_ap_keepalive_start(vif); + + /* Start ACL if need. */ + ath6kl_ap_acl_start(vif); + + /* Start Admission-Control */ + ath6kl_ap_admc_start(vif); + + /* Report Channel Switch if need. */ + ath6kl_ap_ch_switch(vif); + } else { + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: aid %u mac_addr %pM " + "auth=%u keymgmt=%u cipher=%u apsd_info=%u " + "(STA connected)\n", + __func__, ev->u.ap_sta.aid, + ev->u.ap_sta.mac_addr, + ev->u.ap_sta.auth, + ev->u.ap_sta.keymgmt, + le16_to_cpu(ev->u.ap_sta.cipher), + ev->u.ap_sta.apsd_info); + ath6kl_p2p_flowctrl_set_conn_id(vif, + ev->u.ap_sta.mac_addr, + ev->u.ap_sta.aid); + ath6kl_ap_admc_assoc_req_fetch(vif, + ev, + &assoc_req, + &assoc_req_len); + ath6kl_connect_ap_mode_sta( + vif, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr, + ev->u.ap_sta.keymgmt, + le16_to_cpu(ev->u.ap_sta.cipher), + ev->u.ap_sta.auth, assoc_req_len, + assoc_req, + ev->u.ap_sta.apsd_info, + ev->u.ap_sta.phymode); + ath6kl_ap_admc_assoc_req_release(vif, + assoc_req); + } + + ath6kl_ap_ht_update_ies(vif); + return 0; + } + + /* STA/IBSS mode connection event */ + + ath6kl_dbg(ATH6KL_DBG_WMI, + "wmi event connect freq %d bssid %pM listen_intvl %d " + "beacon_intvl %d type %d\n", + le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid, + le16_to_cpu(ev->u.sta.listen_intvl), + le16_to_cpu(ev->u.sta.beacon_intvl), + le32_to_cpu(ev->u.sta.nw_type)); + + /* Start of assoc rsp IEs */ + pie = ev->assoc_info + ev->beacon_ie_len + + ev->assoc_req_len + (sizeof(u16) * 3); /* capinfo, status, aid */ + + /* End of assoc rsp IEs */ + peie = ev->assoc_info + ev->beacon_ie_len + ev->assoc_req_len + + ev->assoc_resp_len; + + while (pie < peie) { + switch (*pie) { + case WLAN_EID_VENDOR_SPECIFIC: + if (pie[1] > 3 && pie[2] == 0x00 && pie[3] == 0x50 && + pie[4] == 0xf2 && pie[5] == WMM_OUI_TYPE) { + /* WMM OUT (00:50:F2) */ + if (pie[1] > 5 + && pie[6] == WMM_PARAM_OUI_SUBTYPE) + wmi->is_wmm_enabled = true; + } + break; + } + + if (wmi->is_wmm_enabled) + break; + + pie += pie[1] + 2; + } + ath6kl_p2p_flowctrl_set_conn_id(vif, + vif->ndev->dev_addr, + ev->u.sta.aid); + + ath6kl_connect_event(vif, le16_to_cpu(ev->u.sta.ch), + ev->u.sta.bssid, + le16_to_cpu(ev->u.sta.listen_intvl), + le16_to_cpu(ev->u.sta.beacon_intvl), + le32_to_cpu(ev->u.sta.nw_type), + ev->beacon_ie_len, ev->assoc_req_len, + ev->assoc_resp_len, ev->assoc_info); + + return 0; +} + +static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) +{ + struct ath6kl_wmi_regdomain *ev; + struct ath6kl *ar = wmi->parent_dev; + u32 reg_code; + + ev = (struct ath6kl_wmi_regdomain *) datap; + reg_code = le32_to_cpu(ev->reg_code); + +#ifdef CONFIG_QC_INTERNAL + ath6kl_info("%s: 0x%x WWR:%d\n", + reg_code & BIT(31) ? "Country Code" : "Reg Domain", + reg_code & 0xfff, !!(reg_code & BIT(30))); +#endif + + ath6kl_reg_target_notify(ar, reg_code); + + return; +} + +static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_disconnect_event *ev; + wmi->traffic_class = 100; + + if (len < sizeof(struct wmi_disconnect_event)) + return -EINVAL; + + ev = (struct wmi_disconnect_event *) datap; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "wmi event disconnect proto_reason %d bssid %pM " + "wmi_reason %d assoc_resp_len %d\n", + le16_to_cpu(ev->proto_reason_status), ev->bssid, + ev->disconn_reason, ev->assoc_resp_len); + + wmi->is_wmm_enabled = false; + + ath6kl_disconnect_event(vif, ev->disconn_reason, + ev->bssid, ev->assoc_resp_len, ev->assoc_info, + le16_to_cpu(ev->proto_reason_status)); + + return 0; +} + +static int ath6kl_wmi_peer_node_event_rx(struct wmi *wmi, u8 *datap, int len) +{ + struct wmi_peer_node_event *ev; + + if (len < sizeof(struct wmi_peer_node_event)) + return -EINVAL; + + ev = (struct wmi_peer_node_event *) datap; + + if (ev->event_code == PEER_NODE_JOIN_EVENT) + ath6kl_dbg(ATH6KL_DBG_WMI, "joined node with mac addr: %pM\n", + ev->peer_mac_addr); + else if (ev->event_code == PEER_NODE_LEAVE_EVENT) + ath6kl_dbg(ATH6KL_DBG_WMI, "left node with mac addr: %pM\n", + ev->peer_mac_addr); + + return 0; +} + +static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_tkip_micerr_event *ev; + + if (len < sizeof(struct wmi_tkip_micerr_event)) + return -EINVAL; + + ev = (struct wmi_tkip_micerr_event *) datap; + + ath6kl_tkip_micerr_event(vif, ev->key_id, ev->is_mcast); + + return 0; +} + +static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ +#define _DEFAULT_SNR (96) /* -96 dBm */ + struct wmi_bss_info_hdr2 *bih; + u8 *buf; + struct ieee80211_channel *channel; + struct ath6kl *ar = wmi->parent_dev; + struct ieee80211_mgmt *mgmt; + struct cfg80211_bss *bss; + + if (len <= sizeof(struct wmi_bss_info_hdr2)) + return -EINVAL; + + bih = (struct wmi_bss_info_hdr2 *) datap; + buf = datap + sizeof(struct wmi_bss_info_hdr2); + len -= sizeof(struct wmi_bss_info_hdr2); + + if (bih->snr == 0x80) + return -EINVAL; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" " + "frame_type=%d\n", + bih->ch, bih->snr, (s8)bih->snr - _DEFAULT_SNR, bih->bssid, + bih->frame_type); + + if (bih->frame_type != BEACON_FTYPE && + bih->frame_type != PROBERESP_FTYPE) + return 0; /* Only update BSS table for now */ + + if (bih->frame_type == BEACON_FTYPE && + test_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags)) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + NONE_BSS_FILTER, 0); + } + + channel = ieee80211_get_channel(ar->wiphy, le16_to_cpu(bih->ch)); + if (channel == NULL) + return -EINVAL; +#ifdef CE_SUPPORT + if (channel == NULL || + ((vif->scan_band == ATHR_CMD_SCANBAND_CHAN_ONLY) && + (vif->scan_chan != channel->center_freq))) + return -EINVAL; +#endif + if (len < 8 + 2 + 2) + return -EINVAL; + + if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &vif->flags) + && memcmp(bih->bssid, vif->bssid, ETH_ALEN) == 0) { + const u8 *tim; + tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2, + len - 8 - 2 - 2); + if (tim && tim[1] >= 2) { + vif->assoc_bss_dtim_period = tim[3]; + set_bit(DTIM_PERIOD_AVAIL, &vif->flags); + } + } + + /* + * In theory, use of cfg80211_inform_bss() would be more natural here + * since we do not have the full frame. However, at least for now, + * cfg80211 can only distinguish Beacon and Probe Response frames from + * each other when using cfg80211_inform_bss_frame(), so let's build a + * fake IEEE 802.11 header to be able to take benefit of this. + */ + mgmt = kmalloc(24 + len, GFP_ATOMIC); + if (mgmt == NULL) + return -EINVAL; + + if (bih->frame_type == BEACON_FTYPE) { + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_BEACON); + memset(mgmt->da, 0xff, ETH_ALEN); + } else { + struct net_device *dev = vif->ndev; + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + memcpy(mgmt->da, dev->dev_addr, ETH_ALEN); + } + mgmt->duration = cpu_to_le16(0); + memcpy(mgmt->sa, bih->bssid, ETH_ALEN); + memcpy(mgmt->bssid, bih->bssid, ETH_ALEN); + mgmt->seq_ctrl = cpu_to_le16(0); + + memcpy(&mgmt->u.beacon, buf, len); + + + +#ifdef CE_SUPPORT +{/* [WAR] drop the unnecessary bssinfo */ + struct cfg80211_scan_request *request; + s8 n_channels = 0; + int i; + bool drop_it = true; + + request = vif->scan_req; + if (request) { + n_channels = request->n_channels; + if (vif->scan_band == ATHR_CMD_SCANBAND_CHAN_ONLY) { + drop_it = false; + } else { + for (i = 0; i < n_channels; i++) { + if (request->channels[i]->center_freq == + channel->center_freq) { + drop_it = false; + break; + } + } + } + if (drop_it == true) { + printk(KERN_DEBUG "%s[%d]incorrect channel,%d\n\r", + __func__, __LINE__, channel->center_freq); + kfree(mgmt); + return -EINVAL; + } + } +} +#endif + +#ifdef ACS_SUPPORT + ath6kl_acs_bss_info(vif, mgmt, 24 + len, channel, bih->snr); +#endif + ath6kl_p2p_rc_bss_info(vif, bih->snr, channel); + ath6kl_htcoex_bss_info(vif, mgmt, 24 + len, channel); + + bss = cfg80211_inform_bss_frame(ar->wiphy, channel, mgmt, + 24 + len, + ((s8)bih->snr - _DEFAULT_SNR) * 100, + GFP_ATOMIC); + kfree(mgmt); + if (bss == NULL) + return -ENOMEM; + +#ifdef CE_SUPPORT + bss->coming_from_dev = vif->ndev; +#endif + + cfg80211_put_bss(bss); + + return 0; +#undef _DEFAULT_SNR +} + +/* Inactivity timeout of a fatpipe(pstream) at the target */ +static int ath6kl_wmi_pstream_timeout_event_rx(struct wmi *wmi, u8 *datap, + int len) +{ + struct wmi_pstream_timeout_event *ev; + + if (len < sizeof(struct wmi_pstream_timeout_event)) + return -EINVAL; + + ev = (struct wmi_pstream_timeout_event *) datap; + + /* + * When the pstream (fat pipe == AC) timesout, it means there were + * no thinStreams within this pstream & it got implicitly created + * due to data flow on this AC. We start the inactivity timer only + * for implicitly created pstream. Just reset the host state. + */ + spin_lock_bh(&wmi->lock); + wmi->stream_exist_for_ac[ev->traffic_class] = 0; + wmi->fat_pipe_exist &= ~(1 << ev->traffic_class); + spin_unlock_bh(&wmi->lock); + + /* Indicate inactivity to driver layer for this fatpipe (pstream) */ + ath6kl_indicate_tx_activity(wmi->parent_dev, ev->traffic_class, false); + + return 0; +} + +static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len) +{ + struct wmi_bit_rate_reply *reply; + s32 rate; + u32 sgi, index; + + if (len < sizeof(struct wmi_bit_rate_reply)) + return -EINVAL; + + reply = (struct wmi_bit_rate_reply *) datap; + + ath6kl_dbg(ATH6KL_DBG_WMI, "rateindex %d\n", reply->rate_index); + + if (reply->rate_index == (s8) RATE_AUTO) { + rate = RATE_AUTO; + } else { + index = reply->rate_index & 0x7f; + sgi = (reply->rate_index & 0x80) ? 1 : 0; + if (index >= sizeof(wmi_rate_tbl)/sizeof(wmi_rate_tbl[0])) + rate = 0; + else + rate = wmi_rate_tbl[index][sgi]; + } + + ath6kl_wakeup_event(wmi->parent_dev); + + return 0; +} + +static int ath6kl_wmi_tcmd_test_report_rx(struct wmi *wmi, u8 *datap, int len) +{ + ath6kl_tm_rx_event(wmi->parent_dev, datap, len); + + return 0; +} + +static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len) +{ + if (len < sizeof(struct wmi_fix_rates_reply)) + return -EINVAL; + + ath6kl_wakeup_event(wmi->parent_dev); + + return 0; +} + +static int ath6kl_wmi_ch_list_reply_rx(struct wmi *wmi, u8 *datap, int len) +{ + if (len < sizeof(struct wmi_channel_list_reply)) + return -EINVAL; + + ath6kl_wakeup_event(wmi->parent_dev); + + return 0; +} + +static int ath6kl_wmi_tx_pwr_reply_rx(struct wmi *wmi, u8 *datap, int len) +{ + struct wmi_tx_pwr_reply *reply; + + if (len < sizeof(struct wmi_tx_pwr_reply)) + return -EINVAL; + + reply = (struct wmi_tx_pwr_reply *) datap; + ath6kl_txpwr_rx_evt(wmi->parent_dev, reply->dbM); + + return 0; +} + +static int ath6kl_wmi_keepalive_reply_rx(struct wmi *wmi, u8 *datap, int len) +{ + if (len < sizeof(struct wmi_get_keepalive_cmd)) + return -EINVAL; + + ath6kl_wakeup_event(wmi->parent_dev); + + return 0; +} + +static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_scan_complete_event *ev; + + ev = (struct wmi_scan_complete_event *) datap; + + ath6kl_scan_complete_evt(vif, a_sle32_to_cpu(ev->status)); + wmi->is_probe_ssid = false; + + return 0; +} + +static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap, + int len, struct ath6kl_vif *vif) +{ + struct wmi_neighbor_report_event *ev; + u8 i; + + if (len < sizeof(*ev)) + return -EINVAL; + ev = (struct wmi_neighbor_report_event *) datap; + if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info) + > len) { + ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event " + "(num=%d len=%d)\n", ev->num_neighbors, len); + return -EINVAL; + } + for (i = 0; i < ev->num_neighbors; i++) { + ath6kl_dbg(ATH6KL_DBG_WMI, "neighbor %d/%d - %pM 0x%x\n", + i + 1, ev->num_neighbors, ev->neighbor[i].bssid, + ev->neighbor[i].bss_flags); + cfg80211_pmksa_candidate_notify(vif->ndev, i, + ev->neighbor[i].bssid, + !!(ev->neighbor[i].bss_flags & + WMI_PREAUTH_CAPABLE_BSS), + GFP_ATOMIC); + } + + return 0; +} + +/* + * Target is reporting a programming error. This is for + * developer aid only. Target only checks a few common violations + * and it is responsibility of host to do all error checking. + * Behavior of target after wmi error event is undefined. + * A reset is recommended. + */ +static int ath6kl_wmi_error_event_rx(struct wmi *wmi, u8 *datap, int len) +{ + const char *type = "unknown error"; + struct wmi_cmd_error_event *ev; + ev = (struct wmi_cmd_error_event *) datap; + + switch (ev->err_code) { + case INVALID_PARAM: + type = "invalid parameter"; + break; + case ILLEGAL_STATE: + type = "invalid state"; + break; + case INTERNAL_ERROR: + type = "internal error"; + break; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, "programming error, cmd=%d %s\n", + ev->cmd_id, type); + + return 0; +} + +static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + ath6kl_tgt_stats_event(vif, datap, len); + + return 0; +} + +#ifdef ATHTST_SUPPORT +static int ath6kl_wmi_ce_get_reg_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + ath6kl_tgt_ce_get_reg_event(vif, datap, len); + + return 0; +} +static int ath6kl_wmi_ce_get_version_info_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + ath6kl_tgt_ce_get_version_info_event(vif, datap, len); + + return 0; +} +#if defined(CE_CUSTOM_1) +static int ath6kl_wmi_ce_get_widimode_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + ath6kl_tgt_ce_get_widimode_event(vif, datap, len); + + return 0; +} +#endif +static int ath6kl_wmi_ce_get_testmode_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + ath6kl_tgt_ce_get_testmode_event(vif, datap, len); + + return 0; +} +static int ath6kl_wmi_ce_get_txpow_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + ath6kl_tgt_ce_get_txpow_event(vif, datap, len); + + return 0; +} +static int ath6kl_wmi_ce_get_stainfo_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + ath6kl_tgt_ce_get_stainfo_event(vif, datap, len); + + return 0; +} +static int ath6kl_wmi_ce_get_scaninfo_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + ath6kl_tgt_ce_get_scaninfo_event(vif, datap, len); + return 0; +} +static int ath6kl_wmi_ce_set_scan_done_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + ath6kl_tgt_ce_set_scan_done_event(vif, datap, len); + return 0; +} +#endif + +static u8 ath6kl_wmi_get_upper_threshold(s16 rssi, + struct sq_threshold_params *sq_thresh, + u32 size) +{ + u32 index; + u8 threshold = (u8) sq_thresh->upper_threshold[size - 1]; + + /* The list is already in sorted order. Get the next lower value */ + for (index = 0; index < size; index++) { + if (rssi < sq_thresh->upper_threshold[index]) { + threshold = (u8) sq_thresh->upper_threshold[index]; + break; + } + } + + return threshold; +} + +static u8 ath6kl_wmi_get_lower_threshold(s16 rssi, + struct sq_threshold_params *sq_thresh, + u32 size) +{ + u32 index; + u8 threshold = (u8) sq_thresh->lower_threshold[size - 1]; + + /* The list is already in sorted order. Get the next lower value */ + for (index = 0; index < size; index++) { + if (rssi > sq_thresh->lower_threshold[index]) { + threshold = (u8) sq_thresh->lower_threshold[index]; + break; + } + } + + return threshold; +} + +static int ath6kl_wmi_send_rssi_threshold_params(struct wmi *wmi, + struct wmi_rssi_threshold_params_cmd *rssi_cmd) +{ + struct sk_buff *skb; + struct wmi_rssi_threshold_params_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_rssi_threshold_params_cmd *) skb->data; + memcpy(cmd, rssi_cmd, sizeof(struct wmi_rssi_threshold_params_cmd)); + + return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_RSSI_THRESHOLD_PARAMS_CMDID, + NO_SYNC_WMIFLAG); +} + +static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap, + int len) +{ + struct wmi_rssi_threshold_event *reply; + struct wmi_rssi_threshold_params_cmd cmd; + struct sq_threshold_params *sq_thresh; + enum wmi_rssi_threshold_val new_threshold; + u8 upper_rssi_threshold, lower_rssi_threshold; + s16 rssi; + int ret; + + if (len < sizeof(struct wmi_rssi_threshold_event)) + return -EINVAL; + + reply = (struct wmi_rssi_threshold_event *) datap; + new_threshold = (enum wmi_rssi_threshold_val) reply->range; + rssi = a_sle16_to_cpu(reply->rssi); + + sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_RSSI]; + + /* + * Identify the threshold breached and communicate that to the app. + * After that install a new set of thresholds based on the signal + * quality reported by the target + */ + if (new_threshold) { + /* Upper threshold breached */ + if (rssi < sq_thresh->upper_threshold[0]) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "spurious upper rssi threshold event: %d\n", + rssi); + } else if ((rssi < sq_thresh->upper_threshold[1]) && + (rssi >= sq_thresh->upper_threshold[0])) { + new_threshold = WMI_RSSI_THRESHOLD1_ABOVE; + } else if ((rssi < sq_thresh->upper_threshold[2]) && + (rssi >= sq_thresh->upper_threshold[1])) { + new_threshold = WMI_RSSI_THRESHOLD2_ABOVE; + } else if ((rssi < sq_thresh->upper_threshold[3]) && + (rssi >= sq_thresh->upper_threshold[2])) { + new_threshold = WMI_RSSI_THRESHOLD3_ABOVE; + } else if ((rssi < sq_thresh->upper_threshold[4]) && + (rssi >= sq_thresh->upper_threshold[3])) { + new_threshold = WMI_RSSI_THRESHOLD4_ABOVE; + } else if ((rssi < sq_thresh->upper_threshold[5]) && + (rssi >= sq_thresh->upper_threshold[4])) { + new_threshold = WMI_RSSI_THRESHOLD5_ABOVE; + } else if (rssi >= sq_thresh->upper_threshold[5]) { + new_threshold = WMI_RSSI_THRESHOLD6_ABOVE; + } + } else { + /* Lower threshold breached */ + if (rssi > sq_thresh->lower_threshold[0]) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "spurious lower rssi threshold event: %d %d\n", + rssi, sq_thresh->lower_threshold[0]); + } else if ((rssi > sq_thresh->lower_threshold[1]) && + (rssi <= sq_thresh->lower_threshold[0])) { + new_threshold = WMI_RSSI_THRESHOLD6_BELOW; + } else if ((rssi > sq_thresh->lower_threshold[2]) && + (rssi <= sq_thresh->lower_threshold[1])) { + new_threshold = WMI_RSSI_THRESHOLD5_BELOW; + } else if ((rssi > sq_thresh->lower_threshold[3]) && + (rssi <= sq_thresh->lower_threshold[2])) { + new_threshold = WMI_RSSI_THRESHOLD4_BELOW; + } else if ((rssi > sq_thresh->lower_threshold[4]) && + (rssi <= sq_thresh->lower_threshold[3])) { + new_threshold = WMI_RSSI_THRESHOLD3_BELOW; + } else if ((rssi > sq_thresh->lower_threshold[5]) && + (rssi <= sq_thresh->lower_threshold[4])) { + new_threshold = WMI_RSSI_THRESHOLD2_BELOW; + } else if (rssi <= sq_thresh->lower_threshold[5]) { + new_threshold = WMI_RSSI_THRESHOLD1_BELOW; + } + } + + /* Calculate and install the next set of thresholds */ + lower_rssi_threshold = ath6kl_wmi_get_lower_threshold(rssi, sq_thresh, + sq_thresh->lower_threshold_valid_count); + upper_rssi_threshold = ath6kl_wmi_get_upper_threshold(rssi, sq_thresh, + sq_thresh->upper_threshold_valid_count); + + /* Issue a wmi command to install the thresholds */ + cmd.thresh_above1_val = a_cpu_to_sle16(upper_rssi_threshold); + cmd.thresh_below1_val = a_cpu_to_sle16(lower_rssi_threshold); + cmd.weight = sq_thresh->weight; + cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval); + + ret = ath6kl_wmi_send_rssi_threshold_params(wmi, &cmd); + if (ret) { + ath6kl_err("unable to configure rssi thresholds\n"); + return -EIO; + } + + return 0; +} + +static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_cac_event *reply; + struct ieee80211_tspec_ie *ts; + u16 active_tsids, tsinfo; + u8 tsid, index; + u8 ts_id; + + if (len < sizeof(struct wmi_cac_event)) + return -EINVAL; + + reply = (struct wmi_cac_event *) datap; + + if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) && + (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) { + + ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion); + tsinfo = le16_to_cpu(ts->tsinfo); + tsid = (tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) & + IEEE80211_WMM_IE_TSPEC_TID_MASK; + + ath6kl_wmi_delete_pstream_cmd(wmi, vif->fw_vif_idx, + reply->ac, tsid); + } else if (reply->cac_indication == CAC_INDICATION_NO_RESP) { + /* + * Following assumes that there is only one outstanding + * ADDTS request when this event is received + */ + spin_lock_bh(&wmi->lock); + active_tsids = wmi->stream_exist_for_ac[reply->ac]; + spin_unlock_bh(&wmi->lock); + + for (index = 0; index < sizeof(active_tsids) * 8; index++) { + if ((active_tsids >> index) & 1) + break; + } + if (index < (sizeof(active_tsids) * 8)) + ath6kl_wmi_delete_pstream_cmd(wmi, vif->fw_vif_idx, + reply->ac, index); + } + + /* + * Clear active tsids and Add missing handling + * for delete qos stream from AP + */ + else if (reply->cac_indication == CAC_INDICATION_DELETE) { + + ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion); + tsinfo = le16_to_cpu(ts->tsinfo); + ts_id = ((tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) & + IEEE80211_WMM_IE_TSPEC_TID_MASK); + + spin_lock_bh(&wmi->lock); + wmi->stream_exist_for_ac[reply->ac] &= ~(1 << ts_id); + active_tsids = wmi->stream_exist_for_ac[reply->ac]; + spin_unlock_bh(&wmi->lock); + + /* Indicate stream inactivity to driver layer only if all tsids + * within this AC are deleted. + */ + if (!active_tsids) { + ath6kl_indicate_tx_activity(wmi->parent_dev, reply->ac, + false); + spin_lock_bh(&wmi->lock); + wmi->fat_pipe_exist &= ~(1 << reply->ac); + spin_unlock_bh(&wmi->lock); + } + } + + return 0; +} + +static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi, + struct wmi_snr_threshold_params_cmd *snr_cmd) +{ + struct sk_buff *skb; + struct wmi_snr_threshold_params_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_snr_threshold_params_cmd *) skb->data; + memcpy(cmd, snr_cmd, sizeof(struct wmi_snr_threshold_params_cmd)); + + return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SNR_THRESHOLD_PARAMS_CMDID, + NO_SYNC_WMIFLAG); +} + +static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap, + int len) +{ + struct wmi_snr_threshold_event *reply; + struct sq_threshold_params *sq_thresh; + struct wmi_snr_threshold_params_cmd cmd; + enum wmi_snr_threshold_val new_threshold; + u8 upper_snr_threshold, lower_snr_threshold; + s16 snr; + int ret; + + if (len < sizeof(struct wmi_snr_threshold_event)) + return -EINVAL; + + reply = (struct wmi_snr_threshold_event *) datap; + + new_threshold = (enum wmi_snr_threshold_val) reply->range; + snr = reply->snr; + + sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_SNR]; + + /* + * Identify the threshold breached and communicate that to the app. + * After that install a new set of thresholds based on the signal + * quality reported by the target. + */ + if (new_threshold) { + /* Upper threshold breached */ + if (snr < sq_thresh->upper_threshold[0]) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "spurious upper snr threshold event: %d\n", + snr); + } else if ((snr < sq_thresh->upper_threshold[1]) && + (snr >= sq_thresh->upper_threshold[0])) { + new_threshold = WMI_SNR_THRESHOLD1_ABOVE; + } else if ((snr < sq_thresh->upper_threshold[2]) && + (snr >= sq_thresh->upper_threshold[1])) { + new_threshold = WMI_SNR_THRESHOLD2_ABOVE; + } else if ((snr < sq_thresh->upper_threshold[3]) && + (snr >= sq_thresh->upper_threshold[2])) { + new_threshold = WMI_SNR_THRESHOLD3_ABOVE; + } else if (snr >= sq_thresh->upper_threshold[3]) { + new_threshold = WMI_SNR_THRESHOLD4_ABOVE; + } + } else { + /* Lower threshold breached */ + if (snr > sq_thresh->lower_threshold[0]) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "spurious lower snr threshold event: %d\n", + sq_thresh->lower_threshold[0]); + } else if ((snr > sq_thresh->lower_threshold[1]) && + (snr <= sq_thresh->lower_threshold[0])) { + new_threshold = WMI_SNR_THRESHOLD4_BELOW; + } else if ((snr > sq_thresh->lower_threshold[2]) && + (snr <= sq_thresh->lower_threshold[1])) { + new_threshold = WMI_SNR_THRESHOLD3_BELOW; + } else if ((snr > sq_thresh->lower_threshold[3]) && + (snr <= sq_thresh->lower_threshold[2])) { + new_threshold = WMI_SNR_THRESHOLD2_BELOW; + } else if (snr <= sq_thresh->lower_threshold[3]) { + new_threshold = WMI_SNR_THRESHOLD1_BELOW; + } + } + + /* Calculate and install the next set of thresholds */ + lower_snr_threshold = ath6kl_wmi_get_lower_threshold(snr, sq_thresh, + sq_thresh->lower_threshold_valid_count); + upper_snr_threshold = ath6kl_wmi_get_upper_threshold(snr, sq_thresh, + sq_thresh->upper_threshold_valid_count); + + /* Issue a wmi command to install the thresholds */ + cmd.thresh_above1_val = upper_snr_threshold; + cmd.thresh_below1_val = lower_snr_threshold; + cmd.weight = sq_thresh->weight; + cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval); + + ath6kl_dbg(ATH6KL_DBG_WMI, + "snr: %d, threshold: %d, lower: %d, upper: %d\n", + snr, new_threshold, + lower_snr_threshold, upper_snr_threshold); + + ret = ath6kl_wmi_send_snr_threshold_params(wmi, &cmd); + if (ret) { + ath6kl_err("unable to configure snr threshold\n"); + return -EIO; + } + + return 0; +} + +static int ath6kl_wmi_aplist_event_rx(struct wmi *wmi, u8 *datap, int len) +{ + u16 ap_info_entry_size; + struct wmi_aplist_event *ev = (struct wmi_aplist_event *) datap; + struct wmi_ap_info_v1 *ap_info_v1; + u8 index; + + if (len < sizeof(struct wmi_aplist_event) || + ev->ap_list_ver != APLIST_VER1) + return -EINVAL; + + ap_info_entry_size = sizeof(struct wmi_ap_info_v1); + ap_info_v1 = (struct wmi_ap_info_v1 *) ev->ap_list; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "number of APs in aplist event: %d\n", ev->num_ap); + + if (len < (int) (sizeof(struct wmi_aplist_event) + + (ev->num_ap - 1) * ap_info_entry_size)) + return -EINVAL; + + /* AP list version 1 contents */ + for (index = 0; index < ev->num_ap; index++) { + ath6kl_dbg(ATH6KL_DBG_WMI, "AP#%d BSSID %pM Channel %d\n", + index, ap_info_v1->bssid, ap_info_v1->channel); + ap_info_v1++; + } + + return 0; +} + +int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, + enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum htc_endpoint_id ep_id = wmi->ep_id; + int ret; + u16 info1; + + if (WARN_ON(skb == NULL || + (if_idx > (wmi->parent_dev->vif_max - 1)))) { + dev_kfree_skb(skb); + return -EINVAL; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n", + cmd_id, skb->len, sync_flag); + ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi tx ", + skb->data, skb->len); + + if (sync_flag >= END_WMIFLAG) { + dev_kfree_skb(skb); + return -EINVAL; + } + + if ((sync_flag == SYNC_BEFORE_WMIFLAG) || + (sync_flag == SYNC_BOTH_WMIFLAG)) { + /* + * Make sure all data currently queued is transmitted before + * the cmd execution. Establish a new sync point. + */ + ath6kl_wmi_sync_point(wmi, if_idx); + } + + skb_push(skb, sizeof(struct wmi_cmd_hdr)); + + cmd_hdr = (struct wmi_cmd_hdr *) skb->data; + cmd_hdr->cmd_id = cpu_to_le16(cmd_id); + info1 = if_idx & WMI_CMD_HDR_IF_ID_MASK; + cmd_hdr->info1 = cpu_to_le16(info1); + + /* Only for OPT_TX_CMD, use BE endpoint. */ + if (cmd_id == WMI_OPT_TX_FRAME_CMDID) { + ret = ath6kl_wmi_data_hdr_add(wmi, skb, OPT_MSGTYPE, + 0, false, 0, NULL, if_idx); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, WMM_AC_BE); + } + + ret = ath6kl_control_tx(wmi->parent_dev, skb, ep_id); + if (ret) + ath6kl_err("wmi fail, cmd_id 0x%x ep_id %d if_idx %d\n", + cmd_id, ep_id, if_idx); + + if ((sync_flag == SYNC_AFTER_WMIFLAG) || + (sync_flag == SYNC_BOTH_WMIFLAG)) { + /* + * Make sure all new data queued waits for the command to + * execute. Establish a new sync point. + */ + ath6kl_wmi_sync_point(wmi, if_idx); + } + + return 0; +} + +int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, + enum network_type nw_type, + enum dot11_auth_mode dot11_auth_mode, + enum auth_mode auth_mode, + enum crypto_type pairwise_crypto, + u8 pairwise_crypto_len, + enum crypto_type group_crypto, + u8 group_crypto_len, int ssid_len, u8 *ssid, + u8 *bssid, u16 channel, u32 ctrl_flags) +{ + struct sk_buff *skb; + struct wmi_connect_cmd *cc; + int ret; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "wmi connect bssid %pM freq %d flags 0x%x ssid_len %d " + "type %d dot11_auth %d auth %d pairwise %d group %d\n", + bssid, channel, ctrl_flags, ssid_len, nw_type, + dot11_auth_mode, auth_mode, pairwise_crypto, group_crypto); + ath6kl_dbg_dump(ATH6KL_DBG_WMI, NULL, "ssid ", ssid, ssid_len); + + wmi->traffic_class = 100; + + if ((pairwise_crypto == NONE_CRYPT) && (group_crypto != NONE_CRYPT)) + return -EINVAL; + + if ((pairwise_crypto != NONE_CRYPT) && (group_crypto == NONE_CRYPT)) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_connect_cmd)); + if (!skb) + return -ENOMEM; + + cc = (struct wmi_connect_cmd *) skb->data; + + if (ssid_len) + memcpy(cc->ssid, ssid, ssid_len); + + cc->ssid_len = ssid_len; + cc->nw_type = nw_type; + cc->dot11_auth_mode = dot11_auth_mode; + cc->auth_mode = auth_mode; + cc->prwise_crypto_type = pairwise_crypto; + cc->prwise_crypto_len = pairwise_crypto_len; + cc->grp_crypto_type = group_crypto; + cc->grp_crypto_len = group_crypto_len; + cc->ch = cpu_to_le16(channel); + cc->ctrl_flags = cpu_to_le32(ctrl_flags); + + if (bssid != NULL) + memcpy(cc->bssid, bssid, ETH_ALEN); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_CONNECT_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 if_idx, u8 *bssid, + u16 channel) +{ + struct sk_buff *skb; + struct wmi_reconnect_cmd *cc; + int ret; + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi reconnect bssid %pM freq %d\n", + bssid, channel); + + wmi->traffic_class = 100; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_reconnect_cmd)); + if (!skb) + return -ENOMEM; + + cc = (struct wmi_reconnect_cmd *) skb->data; + cc->channel = cpu_to_le16(channel); + + if (bssid != NULL) + memcpy(cc->bssid, bssid, ETH_ALEN); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_RECONNECT_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx) +{ + int ret; + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi disconnect\n"); + + wmi->traffic_class = 100; + + /* Disconnect command does not need to do a SYNC before. */ + ret = ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_DISCONNECT_CMDID); + + return ret; +} + +int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, + enum wmi_scan_type scan_type, + u32 force_fgscan, u32 is_legacy, + u32 home_dwell_time, u32 force_scan_interval, + s8 num_chan, u16 *ch_list) +{ + struct sk_buff *skb; + struct wmi_start_scan_cmd *sc; + s8 size; + int i, ret; + + size = sizeof(struct wmi_start_scan_cmd); + + if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) + return -EINVAL; + + if (num_chan > WMI_MAX_CHANNELS) + return -EINVAL; + + if (num_chan) + size += sizeof(u16) * (num_chan - 1); + + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return -ENOMEM; + + sc = (struct wmi_start_scan_cmd *) skb->data; + sc->scan_type = scan_type; + sc->force_fg_scan = cpu_to_le32(force_fgscan); + sc->is_legacy = cpu_to_le32(is_legacy); + sc->home_dwell_time = cpu_to_le32(home_dwell_time); + sc->force_scan_intvl = cpu_to_le32(force_scan_interval); + sc->num_ch = num_chan; + + for (i = 0; i < num_chan; i++) + sc->ch_list[i] = cpu_to_le16(ch_list[i]); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_START_SCAN_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, + u16 fg_start_sec, + u16 fg_end_sec, u16 bg_sec, + u16 minact_chdw_msec, u16 maxact_chdw_msec, + u16 pas_chdw_msec, u8 short_scan_ratio, + u8 scan_ctrl_flag, u32 max_dfsch_act_time, + u16 maxact_scan_per_ssid) +{ + struct sk_buff *skb; + struct wmi_scan_params_cmd *sc; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*sc)); + if (!skb) + return -ENOMEM; + + sc = (struct wmi_scan_params_cmd *) skb->data; + sc->fg_start_period = cpu_to_le16(fg_start_sec); + sc->fg_end_period = cpu_to_le16(fg_end_sec); + sc->bg_period = cpu_to_le16(bg_sec); + sc->minact_chdwell_time = cpu_to_le16(minact_chdw_msec); + sc->maxact_chdwell_time = cpu_to_le16(maxact_chdw_msec); + sc->pas_chdwell_time = cpu_to_le16(pas_chdw_msec); + sc->short_scan_ratio = short_scan_ratio; + sc->scan_ctrl_flags = scan_ctrl_flag; + sc->max_dfsch_act_time = cpu_to_le32(max_dfsch_act_time); + sc->maxact_scan_per_ssid = cpu_to_le16(maxact_scan_per_ssid); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_SCAN_PARAMS_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 if_idx, u8 filter, u32 ie_mask) +{ + struct sk_buff *skb; + struct wmi_bss_filter_cmd *cmd; + int ret; + + if (filter >= LAST_BSS_FILTER) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_bss_filter_cmd *) skb->data; + cmd->bss_filter = filter; + cmd->ie_mask = cpu_to_le32(ie_mask); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_BSS_FILTER_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, u8 index, u8 flag, + u8 ssid_len, u8 *ssid) +{ + struct sk_buff *skb; + struct wmi_probed_ssid_cmd *cmd; + int ret; + + if (index > MAX_PROBED_SSID_INDEX) + return -EINVAL; + + if (ssid_len > sizeof(cmd->ssid)) + return -EINVAL; + + if ((flag & (DISABLE_SSID_FLAG | ANY_SSID_FLAG)) && (ssid_len > 0)) + return -EINVAL; + + if ((flag & SPECIFIC_SSID_FLAG) && !ssid_len) + return -EINVAL; + + if (flag & SPECIFIC_SSID_FLAG) + wmi->is_probe_ssid = true; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_probed_ssid_cmd *) skb->data; + cmd->entry_index = index; + cmd->flag = flag; + cmd->ssid_len = ssid_len; + memcpy(cmd->ssid, ssid, ssid_len); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_PROBED_SSID_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u8 if_idx, + u16 listen_interval, + u16 listen_beacons) +{ + struct sk_buff *skb; + struct wmi_listen_int_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_listen_int_cmd *) skb->data; + cmd->listen_intvl = cpu_to_le16(listen_interval); + cmd->num_beacons = cpu_to_le16(listen_beacons); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_LISTEN_INT_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode) +{ + struct sk_buff *skb; + struct wmi_power_mode_cmd *cmd; + struct ath6kl_vif *vif; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_power_mode_cmd *) skb->data; + cmd->pwr_mode = pwr_mode; + + vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); + if (vif) + vif->last_pwr_mode = pwr_mode; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_POWER_MODE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u8 if_idx, u16 idle_period, + u16 ps_poll_num, u16 dtim_policy, + u16 tx_wakeup_policy, u16 num_tx_to_wakeup, + u16 ps_fail_event_policy) +{ + struct sk_buff *skb; + struct wmi_power_params_cmd *pm; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*pm)); + if (!skb) + return -ENOMEM; + + pm = (struct wmi_power_params_cmd *)skb->data; + pm->idle_period = cpu_to_le16(idle_period); + pm->pspoll_number = cpu_to_le16(ps_poll_num); + pm->dtim_policy = cpu_to_le16(dtim_policy); + pm->tx_wakeup_policy = cpu_to_le16(tx_wakeup_policy); + pm->num_tx_to_wakeup = cpu_to_le16(num_tx_to_wakeup); + pm->ps_fail_event_policy = cpu_to_le16(ps_fail_event_policy); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_POWER_PARAMS_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +#ifdef ATH6KL_SUPPORT_WIFI_KTK +int ath6kl_wmi_ibss_pm_caps_cmd(struct wmi *wmi, u8 if_idx, u8 adhoc_ps_type, + u8 ttl, + u16 atim_windows, + u16 timeout_value) +{ + struct sk_buff *skb; + struct wmi_ibss_pm_caps_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ibss_pm_caps_cmd *) skb->data; + cmd->power_saving = adhoc_ps_type; + cmd->ttl = ttl; + cmd->atim_windows = atim_windows; + cmd->timeout_value = timeout_value; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IBSS_PM_CAPS_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} +#endif + +int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 if_idx, u8 timeout) +{ + struct sk_buff *skb; + struct wmi_disc_timeout_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_disc_timeout_cmd *) skb->data; + cmd->discon_timeout = timeout; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_DISC_TIMEOUT_CMDID, + NO_SYNC_WMIFLAG); + + if (ret == 0) + ath6kl_debug_set_disconnect_timeout(wmi->parent_dev, timeout); + + return ret; +} + +int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, + enum crypto_type key_type, + u8 key_usage, u8 key_len, + u8 *key_rsc, unsigned int key_rsc_len, + u8 *key_material, + u8 key_op_ctrl, u8 *mac_addr, + enum wmi_sync_flag sync_flag) +{ + struct sk_buff *skb; + struct wmi_add_cipher_key_cmd *cmd; + int ret; + + ath6kl_dbg(ATH6KL_DBG_WMI, "addkey cmd: key_index=%u key_type=%d " + "key_usage=%d key_len=%d key_op_ctrl=%d\n", + key_index, key_type, key_usage, key_len, key_op_ctrl); + + if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) || + (key_material == NULL) || key_rsc_len > 8) + return -EINVAL; + + if ((WEP_CRYPT != key_type) && (NULL == key_rsc)) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_add_cipher_key_cmd *) skb->data; + cmd->key_index = key_index; + cmd->key_type = key_type; + cmd->key_usage = key_usage; + cmd->key_len = key_len; + memcpy(cmd->key, key_material, key_len); + + if (key_rsc != NULL) + memcpy(cmd->key_rsc, key_rsc, key_rsc_len); + + cmd->key_op_ctrl = key_op_ctrl; + + if (mac_addr) + memcpy(cmd->key_mac_addr, mac_addr, ETH_ALEN); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_CIPHER_KEY_CMDID, + sync_flag); + + return ret; +} + +#ifdef PMF_SUPPORT +int ath6kl_wmi_addkey_igtk_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, + u8 key_len, u8 *key_rsc, u8 *key_material, + enum wmi_sync_flag sync_flag) +{ + struct sk_buff *skb; + struct wmi_add_igtk_cmd *cmd; + int ret; + + ath6kl_dbg(ATH6KL_DBG_WMI, "addkey_igtk cmd: key_index=%u " + "key_len=%d \n", key_index, key_len); + + if ((key_index > WMI_MAX_IGTK_INDEX) || + (key_index < WMI_MIN_IGTK_INDEX) || + (key_len > WMI_IGTK_KEY_LEN) || + (key_material == NULL) || (NULL == key_rsc)) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_add_igtk_cmd *)skb->data; + cmd->key_index = key_index; + cmd->key_len = key_len; + memcpy(cmd->key, key_material, key_len); + memcpy(cmd->key_rsc, key_rsc, 6); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IGTK_CMDID, + sync_flag); + + return ret; +} +#endif + +int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk) +{ + struct sk_buff *skb; + struct wmi_add_krk_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_add_krk_cmd *) skb->data; + memcpy(cmd->krk, krk, WMI_KRK_LEN); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_KRK_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index) +{ + struct sk_buff *skb; + struct wmi_delete_cipher_key_cmd *cmd; + int ret; + + if (key_index > WMI_MAX_KEY_INDEX) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_delete_cipher_key_cmd *) skb->data; + cmd->key_index = key_index; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DELETE_CIPHER_KEY_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid, + const u8 *pmkid, bool set) +{ + struct sk_buff *skb; + struct wmi_setpmkid_cmd *cmd; + int ret; + + if (bssid == NULL) + return -EINVAL; + + if (set && pmkid == NULL) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_setpmkid_cmd *) skb->data; + memcpy(cmd->bssid, bssid, ETH_ALEN); + if (set) { + memcpy(cmd->pmkid, pmkid, sizeof(cmd->pmkid)); + cmd->enable = PMKID_ENABLE; + } else { + memset(cmd->pmkid, 0, sizeof(cmd->pmkid)); + cmd->enable = PMKID_DISABLE; + } + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_PMKID_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +static int ath6kl_wmi_data_sync_send(struct wmi *wmi, struct sk_buff *skb, + enum htc_endpoint_id ep_id, u8 if_idx) +{ + struct wmi_data_hdr *data_hdr; + int ret; + + if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) { + dev_kfree_skb(skb); + return -EINVAL; + } + + skb_push(skb, sizeof(struct wmi_data_hdr)); + + data_hdr = (struct wmi_data_hdr *) skb->data; + data_hdr->info = SYNC_MSGTYPE << WMI_DATA_HDR_MSG_TYPE_SHIFT; + data_hdr->info3 = cpu_to_le16(if_idx & WMI_DATA_HDR_IF_IDX_MASK); + + ret = ath6kl_control_tx(wmi->parent_dev, skb, ep_id); + if (ret) + ath6kl_err("wmi sync fail, ep_id %d if_idx %d\n", + ep_id, if_idx); + + return ret; +} + +static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) +{ + struct sk_buff *skb; + struct wmi_sync_cmd *cmd; + struct wmi_data_sync_bufs data_sync_bufs[WMM_NUM_AC]; + enum htc_endpoint_id ep_id; + u8 index, num_pri_streams = 0; + int ret = 0; + + memset(data_sync_bufs, 0, sizeof(data_sync_bufs)); + + spin_lock_bh(&wmi->lock); + + for (index = 0; index < WMM_NUM_AC; index++) { + if (wmi->fat_pipe_exist & (1 << index)) { + num_pri_streams++; + data_sync_bufs[num_pri_streams - 1].traffic_class = + index; + } + } + + spin_unlock_bh(&wmi->lock); + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sync_cmd *) skb->data; + + /* + * In the SYNC cmd sent on the control Ep, send a bitmap + * of the data eps on which the Data Sync will be sent + */ + spin_lock_bh(&wmi->lock); + cmd->data_sync_map = wmi->fat_pipe_exist; + spin_unlock_bh(&wmi->lock); + + for (index = 0; index < num_pri_streams; index++) { + data_sync_bufs[index].skb = ath6kl_buf_alloc(0); + if (data_sync_bufs[index].skb == NULL) { + ret = -ENOMEM; + break; + } + } + + /* + * If buffer allocation for any of the dataSync fails, + * then do not send the Synchronize cmd on the control ep + */ + if (ret) + goto free_cmd_skb; + + /* + * Send sync cmd followed by sync data messages on all + * endpoints being used + */ + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SYNCHRONIZE_CMDID, + NO_SYNC_WMIFLAG); + + if (ret) + goto free_data_skb; + + /* cmd buffer sent, we no longer own it */ + skb = NULL; + + for (index = 0; index < num_pri_streams; index++) { + + if (WARN_ON(!data_sync_bufs[index].skb)) + break; + + ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, + data_sync_bufs[index]. + traffic_class); + ret = + ath6kl_wmi_data_sync_send(wmi, data_sync_bufs[index].skb, + ep_id, if_idx); + + data_sync_bufs[index].skb = NULL; + + if (ret) + goto free_data_skb; + } + + return 0; +free_cmd_skb: + /* free up any resources left over (possibly due to an error) */ + if (skb) + dev_kfree_skb(skb); + +free_data_skb: + for (index = 0; index < num_pri_streams; index++) { + if (data_sync_bufs[index].skb != NULL) { + dev_kfree_skb((struct sk_buff *)data_sync_bufs[index]. + skb); + } + } + return ret; +} + +int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi, u8 if_idx, + struct wmi_create_pstream_cmd *params) +{ + struct sk_buff *skb; + struct wmi_create_pstream_cmd *cmd; + struct ath6kl *ar = wmi->parent_dev; + bool fatpipe_exist_for_ac = false; + s32 min_phy = 0; + s32 nominal_phy = 0; + int ret; + + if (!((params->user_pri < 8) && + (params->user_pri <= 0x7) && + (up_to_ac[params->user_pri & 0x7] == params->traffic_class) && + (params->traffic_direc == UPLINK_TRAFFIC || + params->traffic_direc == DNLINK_TRAFFIC || + params->traffic_direc == BIDIR_TRAFFIC) && + (params->traffic_type == TRAFFIC_TYPE_APERIODIC || + params->traffic_type == TRAFFIC_TYPE_PERIODIC) && + (params->voice_psc_cap == DISABLE_FOR_THIS_AC || + params->voice_psc_cap == ENABLE_FOR_THIS_AC || + params->voice_psc_cap == ENABLE_FOR_ALL_AC) && + (params->tsid == WMI_IMPLICIT_PSTREAM || + params->tsid <= WMI_MAX_THINSTREAM))) { + return -EINVAL; + } + + /* + * Check nominal PHY rate is >= minimalPHY, + * so that DUT can allow TSRS IE + */ + + /* Get the physical rate (units of bps) */ + min_phy = ((le32_to_cpu(params->min_phy_rate) / 1000) / 1000); + + /* Check minimal phy < nominal phy rate */ + if (params->nominal_phy >= min_phy) { + /* unit of 500 kbps */ + nominal_phy = (params->nominal_phy * 1000) / 500; + ath6kl_dbg(ATH6KL_DBG_WMI, + "TSRS IE enabled::MinPhy %x->NominalPhy ===> %x\n", + min_phy, nominal_phy); + + params->nominal_phy = nominal_phy; + } else { + params->nominal_phy = 0; + } + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "sending create_pstream_cmd: ac=%d tsid:%d\n", + params->traffic_class, params->tsid); + + cmd = (struct wmi_create_pstream_cmd *) skb->data; + memcpy(cmd, params, sizeof(*cmd)); + + /* This is an implicitly created Fat pipe */ + if ((u32) params->tsid == (u32) WMI_IMPLICIT_PSTREAM) { + spin_lock_bh(&wmi->lock); + fatpipe_exist_for_ac = + ar->ac_stream_active[params->traffic_class]; + wmi->fat_pipe_exist |= (1 << params->traffic_class); + spin_unlock_bh(&wmi->lock); + } else { + /* explicitly created thin stream within a fat pipe */ + spin_lock_bh(&wmi->lock); + fatpipe_exist_for_ac = + ar->ac_stream_active[params->traffic_class]; + wmi->stream_exist_for_ac[params->traffic_class] |= + (1 << params->tsid); + /* + * If a thinstream becomes active, the fat pipe automatically + * becomes active + */ + wmi->fat_pipe_exist |= (1 << params->traffic_class); + spin_unlock_bh(&wmi->lock); + } + + /* + * Indicate activty change to driver layer only if this is the + * first TSID to get created in this AC explicitly or an implicit + * fat pipe is getting created. + */ + if (!fatpipe_exist_for_ac) + ath6kl_indicate_tx_activity(wmi->parent_dev, + params->traffic_class, true); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_CREATE_PSTREAM_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 if_idx, u8 traffic_class, + u8 tsid) +{ + struct sk_buff *skb; + struct wmi_delete_pstream_cmd *cmd; + u16 active_tsids = 0; + int ret; + + if (traffic_class > 3) { + ath6kl_err("invalid traffic class: %d\n", traffic_class); + return -EINVAL; + } + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_delete_pstream_cmd *) skb->data; + cmd->traffic_class = traffic_class; + cmd->tsid = tsid; + + spin_lock_bh(&wmi->lock); + active_tsids = wmi->stream_exist_for_ac[traffic_class]; + spin_unlock_bh(&wmi->lock); + + if (!(active_tsids & (1 << tsid))) { + dev_kfree_skb(skb); + ath6kl_dbg(ATH6KL_DBG_WMI, + "TSID %d doesn't exist for traffic class: %d\n", + tsid, traffic_class); + return -ENODATA; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, + "sending delete_pstream_cmd: traffic class: %d tsid=%d\n", + traffic_class, tsid); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DELETE_PSTREAM_CMDID, + SYNC_BEFORE_WMIFLAG); + + spin_lock_bh(&wmi->lock); + wmi->stream_exist_for_ac[traffic_class] &= ~(1 << tsid); + active_tsids = wmi->stream_exist_for_ac[traffic_class]; + spin_unlock_bh(&wmi->lock); + + /* + * Indicate stream inactivity to driver layer only if all tsids + * within this AC are deleted. + */ + if (!active_tsids) { + ath6kl_indicate_tx_activity(wmi->parent_dev, + traffic_class, false); + spin_lock_bh(&wmi->lock); + wmi->fat_pipe_exist &= ~(1 << traffic_class); + spin_unlock_bh(&wmi->lock); + } + + return ret; +} + +int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd) +{ + struct sk_buff *skb; + struct wmi_set_ip_cmd *cmd; + int ret; + + /* Multicast address are not valid */ + if ((*((u8 *) &ip_cmd->ips[0]) >= 0xE0) || + (*((u8 *) &ip_cmd->ips[1]) >= 0xE0)) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_ip_cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_ip_cmd *) skb->data; + memcpy(cmd, ip_cmd, sizeof(struct wmi_set_ip_cmd)); + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_IP_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi) +{ + u16 active_tsids; + u8 stream_exist; + int i; + + /* + * Relinquish credits from all implicitly created pstreams + * since when we go to sleep. If user created explicit + * thinstreams exists with in a fatpipe leave them intact + * for the user to delete. + */ + spin_lock_bh(&wmi->lock); + stream_exist = wmi->fat_pipe_exist; + spin_unlock_bh(&wmi->lock); + + for (i = 0; i < WMM_NUM_AC; i++) { + if (stream_exist & (1 << i)) { + + /* + * FIXME: Is this lock & unlock inside + * for loop correct? may need rework. + */ + spin_lock_bh(&wmi->lock); + active_tsids = wmi->stream_exist_for_ac[i]; + spin_unlock_bh(&wmi->lock); + + /* + * If there are no user created thin streams + * delete the fatpipe + */ + if (!active_tsids) { + stream_exist &= ~(1 << i); + /* + * Indicate inactivity to driver layer for + * this fatpipe (pstream) + */ + ath6kl_indicate_tx_activity(wmi->parent_dev, + i, false); + } + } + } + + /* FIXME: Can we do this assignment without locking ? */ + spin_lock_bh(&wmi->lock); + wmi->fat_pipe_exist = stream_exist; + spin_unlock_bh(&wmi->lock); +} + +int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, + enum ath6kl_host_mode host_mode) +{ + struct sk_buff *skb; + struct wmi_set_host_sleep_mode_cmd *cmd; + int ret; + + if ((host_mode != ATH6KL_HOST_MODE_ASLEEP) && + (host_mode != ATH6KL_HOST_MODE_AWAKE)) { + ath6kl_err("invalid host sleep mode: %d\n", host_mode); + return -EINVAL; + } + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_host_sleep_mode_cmd *) skb->data; + + if (host_mode == ATH6KL_HOST_MODE_ASLEEP) { + ath6kl_wmi_relinquish_implicit_pstream_credits(wmi); + cmd->asleep = cpu_to_le32(1); + } else + cmd->awake = cpu_to_le32(1); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_HOST_SLEEP_MODE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +/* This command has zero length payload */ +static int ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(struct wmi *wmi, + struct ath6kl_vif *vif) +{ + struct ath6kl *ar = wmi->parent_dev; + + set_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); + wake_up(&ar->event_wq); + + return 0; +} + +int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, + enum ath6kl_wow_mode wow_mode, + u32 filter, u16 host_req_delay) +{ + struct sk_buff *skb; + struct wmi_set_wow_mode_cmd *cmd; + int ret; + + if ((wow_mode != ATH6KL_WOW_MODE_ENABLE) && + wow_mode != ATH6KL_WOW_MODE_DISABLE) { + ath6kl_err("invalid wow mode: %d\n", wow_mode); + return -EINVAL; + } + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_wow_mode_cmd *) skb->data; + cmd->enable_wow = cpu_to_le32(wow_mode); + cmd->filter = cpu_to_le32(filter); + cmd->host_req_delay = cpu_to_le16(host_req_delay); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_WOW_MODE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, + u8 list_id, u8 filter_size, + u8 filter_offset, u8 *filter, u8 *mask) +{ + struct sk_buff *skb; + struct wmi_add_wow_pattern_cmd *cmd; + u16 size; + u8 *filter_mask; + int ret; + + /* + * Allocate additional memory in the buffer to hold + * filter and mask value, which is twice of filter_size. + */ + size = sizeof(*cmd) + (2 * filter_size); + + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_add_wow_pattern_cmd *) skb->data; + cmd->filter_list_id = list_id; + cmd->filter_size = filter_size; + cmd->filter_offset = filter_offset; + + memcpy(cmd->filter, filter, filter_size); + + filter_mask = (u8 *) (cmd->filter + filter_size); + memcpy(filter_mask, mask, filter_size); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ADD_WOW_PATTERN_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, + u16 list_id, u16 filter_id) +{ + struct sk_buff *skb; + struct wmi_del_wow_pattern_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_del_wow_pattern_cmd *) skb->data; + cmd->filter_list_id = cpu_to_le16(list_id); + cmd->filter_id = cpu_to_le16(filter_id); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DEL_WOW_PATTERN_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +static int ath6kl_wmi_cmd_send_xtnd(struct wmi *wmi, struct sk_buff *skb, + enum wmix_command_id cmd_id, + enum wmi_sync_flag sync_flag) +{ + struct wmix_cmd_hdr *cmd_hdr; + int ret; + + skb_push(skb, sizeof(struct wmix_cmd_hdr)); + + cmd_hdr = (struct wmix_cmd_hdr *) skb->data; + cmd_hdr->cmd_id = cpu_to_le32(cmd_id); + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_EXTENSION_CMDID, sync_flag); + + return ret; +} + +int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source) +{ + struct sk_buff *skb; + struct wmix_hb_challenge_resp_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmix_hb_challenge_resp_cmd *) skb->data; + cmd->cookie = cpu_to_le32(cookie); + cmd->source = cpu_to_le32(source); + + ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_HB_CHALLENGE_RESP_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config) +{ + struct ath6kl_wmix_dbglog_cfg_module_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct ath6kl_wmix_dbglog_cfg_module_cmd *) skb->data; + cmd->valid = cpu_to_le32(valid); + cmd->config = cpu_to_le32(config); + + ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_DBGLOG_CFG_MODULE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_get_stats_cmd(struct wmi *wmi, u8 if_idx) +{ + return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_STATISTICS_CMDID); +} + +int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 if_idx, u8 dbM) +{ + struct sk_buff *skb; + struct wmi_set_tx_pwr_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_tx_pwr_cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_tx_pwr_cmd *) skb->data; + cmd->dbM = dbM; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_TX_PWR_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx) +{ + return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_TX_PWR_CMDID); +} + +int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi) +{ + return ath6kl_wmi_simple_cmd(wmi, 0, WMI_GET_ROAM_TBL_CMDID); +} + +int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 if_idx, u8 status, + u8 preamble_policy) +{ + struct sk_buff *skb; + struct wmi_set_lpreamble_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_lpreamble_cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_lpreamble_cmd *) skb->data; + cmd->status = status; + cmd->preamble_policy = preamble_policy; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_LPREAMBLE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u8 if_idx, u16 threshold) +{ + struct sk_buff *skb; + struct wmi_set_rts_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_rts_cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_rts_cmd *) skb->data; + cmd->threshold = cpu_to_le16(threshold); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RTS_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg) +{ + struct sk_buff *skb; + struct wmi_set_wmm_txop_cmd *cmd; + int ret; + + if (!((cfg == WMI_TXOP_DISABLED) || (cfg == WMI_TXOP_ENABLED))) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_set_wmm_txop_cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_wmm_txop_cmd *) skb->data; + cmd->txop_enable = cfg; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_WMM_TXOP_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, + u8 keep_alive_intvl) +{ + struct sk_buff *skb; + struct wmi_set_keepalive_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_keepalive_cmd *) skb->data; + cmd->keep_alive_intvl = keep_alive_intvl; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_KEEPALIVE_CMDID, + NO_SYNC_WMIFLAG); + + if (ret == 0) + ath6kl_debug_set_keepalive(wmi->parent_dev, keep_alive_intvl); + + return ret; +} + +int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len) +{ + struct sk_buff *skb; + int ret; + + skb = ath6kl_wmi_get_new_buf(len); + if (!skb) + return -ENOMEM; + + memcpy(skb->data, buf, len); + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_TEST_CMDID, NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2) +{ + struct sk_buff *skb; + struct wmi_set_regdomain_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_regdomain_cmd *) skb->data; + memcpy(cmd->iso_name, alpha2, 2); + + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: regdomain = %s\n", __func__, alpha2); + + return ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_SET_REGDOMAIN_CMDID, + NO_SYNC_WMIFLAG); +} + +s32 ath6kl_wmi_get_rate(s8 rate_index) +{ + u32 sgi = 0; + if (rate_index == RATE_AUTO) + return 0; + + sgi = (rate_index & 0x80) ? 1 : 0; + + if ((rate_index & 0x7f) >= + (sizeof(wmi_rate_tbl)/sizeof(wmi_rate_tbl[0]))) + return 0; + + return wmi_rate_tbl[(u32) (rate_index&0x7f)][sgi]; +} + +s32 ath6kl_wmi_get_rate_ar6004(s8 rate_index) +{ + u32 sgi = 0; + if (rate_index == RATE_AUTO) + return 0; + + sgi = (rate_index & 0x80) ? 1 : 0; + + if ((rate_index & 0x7f) >= + (sizeof(wmi_rate_tbl_ar6004)/sizeof(wmi_rate_tbl_ar6004[0]))) + return 0; + + return wmi_rate_tbl_ar6004[(u32) (rate_index&0x7f)][sgi]; +} + +static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap, + u32 len, struct ath6kl_vif *vif) +{ + struct wmi_pmkid_list_reply *reply; + struct ath6kl *ar = vif->ar; + u32 expected_len; + + if (len < sizeof(struct wmi_pmkid_list_reply)) { + if (test_bit(PMKLIST_GET_PEND, &vif->flags)) { + clear_bit(PMKLIST_GET_PEND, &vif->flags); + wake_up(&ar->event_wq); + } + + return -EINVAL; + } + + reply = (struct wmi_pmkid_list_reply *)datap; + expected_len = sizeof(reply->num_pmkid) + + le32_to_cpu(reply->num_pmkid) * (ETH_ALEN + WMI_PMKID_LEN); + + if (len < expected_len) { + if (test_bit(PMKLIST_GET_PEND, &vif->flags)) { + clear_bit(PMKLIST_GET_PEND, &vif->flags); + wake_up(&ar->event_wq); + } + + return -EINVAL; + } + + if (expected_len <= MAX_PMKID_LIST_SIZE) { + memcpy(vif->pmkid_list_buf, datap, expected_len); + + if (test_bit(PMKLIST_GET_PEND, &vif->flags)) { + clear_bit(PMKLIST_GET_PEND, &vif->flags); + wake_up(&ar->event_wq); + } + } else { + if (test_bit(PMKLIST_GET_PEND, &vif->flags)) { + clear_bit(PMKLIST_GET_PEND, &vif->flags); + wake_up(&ar->event_wq); + } + + return -EOVERFLOW; + } + + return 0; +} + +static int ath6kl_wmi_addba_req_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_addba_req_event *cmd = (struct wmi_addba_req_event *) datap; + + aggr_recv_addba_req_evt(vif, cmd->tid, + le16_to_cpu(cmd->st_seq_no), cmd->win_sz); + + return 0; +} + +static int ath6kl_wmi_addba_resp_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + struct wmi_addba_resp_event *cmd = + (struct wmi_addba_resp_event *) datap; + + aggr_recv_addba_resp_evt(vif, cmd->tid, + le16_to_cpu(cmd->amsdu_sz), cmd->status); + + return 0; +} + +static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_delba_event *cmd = (struct wmi_delba_event *) datap; + u8 is_peer_initiator = cmd->is_peer_initiator; + + aggr_recv_delba_req_evt(vif, cmd->tid, is_peer_initiator); + + return 0; +} + +/* AP mode functions */ + +int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx, + struct wmi_connect_cmd *p) +{ + struct sk_buff *skb; + struct wmi_connect_cmd *cm; + int res; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cm)); + if (!skb) + return -ENOMEM; + + cm = (struct wmi_connect_cmd *) skb->data; + memcpy(cm, p, sizeof(*cm)); + + res = ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_CONFIG_COMMIT_CMDID, + NO_SYNC_WMIFLAG); + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u " + "ctrl_flags=0x%x-> res=%d\n", + __func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch), + le32_to_cpu(p->ctrl_flags), res); + return res; +} + +int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac, + u16 reason) +{ + struct sk_buff *skb; + struct wmi_ap_set_mlme_cmd *cm; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cm)); + if (!skb) + return -ENOMEM; + + cm = (struct wmi_ap_set_mlme_cmd *) skb->data; + memcpy(cm->mac, mac, ETH_ALEN); + cm->reason = cpu_to_le16(reason); + cm->cmd = cmd; + + return ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_SET_MLME_CMDID, + NO_SYNC_WMIFLAG); +} + +static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_pspoll_event *ev; + + if (len < sizeof(struct wmi_pspoll_event)) + return -EINVAL; + + ev = (struct wmi_pspoll_event *) datap; + + ath6kl_pspoll_event(vif, le16_to_cpu(ev->aid)); + + return 0; +} + +static int ath6kl_wmi_dtimexpiry_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + ath6kl_dtimexpiry_event(vif); + + return 0; +} + +int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u8 if_idx, u16 aid, + bool flag) +{ + struct sk_buff *skb; + struct wmi_ap_set_pvb_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_ap_set_pvb_cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "ath6kl_wmi_set_pvb_cmd: aid=%d flag=%d\n", + aid, flag); + + cmd = (struct wmi_ap_set_pvb_cmd *) skb->data; + cmd->aid = cpu_to_le16(aid); + cmd->rsvd = cpu_to_le16(0); + cmd->flag = cpu_to_le32(flag); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_PVB_CMDID, + NO_SYNC_WMIFLAG); + + return 0; +} + +int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx, + u8 rx_meta_ver, + bool rx_dot11_hdr, bool defrag_on_host) +{ + struct sk_buff *skb; + struct wmi_rx_frame_format_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_rx_frame_format_cmd *) skb->data; + cmd->dot11_hdr = rx_dot11_hdr ? 1 : 0; + cmd->defrag_on_host = defrag_on_host ? 1 : 0; + cmd->meta_ver = rx_meta_ver; + + /* Delete the local aggr state, on host */ + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_RX_FRAME_FORMAT_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, + const u8 *ie, u8 ie_len) +{ + struct sk_buff *skb; + struct wmi_set_appie_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u " + "ie_len=%u\n", mgmt_frm_type, ie_len); + p = (struct wmi_set_appie_cmd *) skb->data; + p->mgmt_frm_type = mgmt_frm_type; + p->ie_len = ie_len; + memcpy(p->ie_info, ie, ie_len); + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_APPIE_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_rate_ctrl_cmd(struct wmi *wmi, + u8 if_idx, + u32 ratemode) +{ + struct sk_buff *skb; + struct wmi_set_ratectrl_parm_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "ath6kl_wmi_setratectrl_cmd: mode=%d\n", + ratemode); + cmd = (struct wmi_set_ratectrl_parm_cmd *) skb->data; + cmd->mode = ratemode ? 1 : 0; + + return ath6kl_wmi_cmd_send(wmi, + if_idx, + skb, + WMI_SET_RATECTRL_PARM_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, u8 if_idx, bool disable) +{ + struct sk_buff *skb; + struct wmi_disable_11b_rates_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "disable_11b_rates_cmd: disable=%u\n", + disable); + cmd = (struct wmi_disable_11b_rates_cmd *) skb->data; + cmd->disable = disable ? 1 : 0; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_DISABLE_11B_RATES_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur) +{ + struct sk_buff *skb; + struct wmi_remain_on_chnl_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl_cmd: freq=%u dur=%u\n", + freq, dur); + p = (struct wmi_remain_on_chnl_cmd *) skb->data; + p->freq = cpu_to_le32(freq); + p->duration = cpu_to_le32(dur); + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_REMAIN_ON_CHNL_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, + u32 wait, const u8 *data, u16 data_len) +{ + struct sk_buff *skb; + struct wmi_send_action_cmd *p; + struct wmi_mgmt_tx_frame *mgmt_tx_frame = NULL; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) data; + u8 *buf = NULL; + + if (wait) + return -EINVAL; /* Offload for wait not supported */ + + /* Only need to take care of P2P's Action frames in + current application. */ + if ((wmi->parent_dev->p2p) && + ieee80211_is_action(mgmt->frame_control)) { + buf = kmalloc(data_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mgmt_tx_frame = kmalloc( + sizeof(struct wmi_mgmt_tx_frame), GFP_KERNEL); + if (!mgmt_tx_frame) { + kfree(buf); + return -ENOMEM; + } + + mgmt_tx_frame->vif = + ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); + WARN_ON(!mgmt_tx_frame->vif); + + memcpy(buf, data, data_len); + mgmt_tx_frame->mgmt_tx_frame = buf; + mgmt_tx_frame->mgmt_tx_frame_len = data_len; + mgmt_tx_frame->mgmt_tx_frame_idx = id; + mgmt_tx_frame->mgmt_tx_frame_freq = freq; + if (ath6kl_p2p_frame_retry(wmi->parent_dev, + (u8 *)&(mgmt->u.action), + (data_len - 24))) + mgmt_tx_frame->mgmt_tx_frame_retry = + WMI_TX_MGMT_RETRY_MAX; + else + mgmt_tx_frame->mgmt_tx_frame_retry = 0; + + list_add_tail(&mgmt_tx_frame->list, &wmi->mgmt_tx_frame_list); + } + + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len); + if (!skb) { + kfree(buf); + if (mgmt_tx_frame) { + list_del(&mgmt_tx_frame->list); + kfree(mgmt_tx_frame); + } + return -ENOMEM; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u " + "len=%u\n", id, freq, wait, data_len); + p = (struct wmi_send_action_cmd *) skb->data; + p->id = cpu_to_le32(id); + p->freq = cpu_to_le32(freq); + p->wait = cpu_to_le32(wait); + p->len = cpu_to_le16(data_len); + memcpy(p->data, data, data_len); + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SEND_ACTION_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq, + const u8 *dst, const u8 *data, + u16 data_len) +{ + struct sk_buff *skb; + struct wmi_p2p_probe_response_cmd *p; + size_t cmd_len = sizeof(*p) + data_len; + + if (data_len == 0) + cmd_len++; /* work around target minimum length requirement */ + + skb = ath6kl_wmi_get_new_buf(cmd_len); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "send_probe_response_cmd: freq=%u dst=%pM " + "len=%u\n", freq, dst, data_len); + p = (struct wmi_p2p_probe_response_cmd *) skb->data; + p->freq = cpu_to_le32(freq); + memcpy(p->destination_addr, dst, ETH_ALEN); + p->len = cpu_to_le16(data_len); + memcpy(p->data, data, data_len); + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SEND_PROBE_RESPONSE_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_send_go_probe_response_cmd(struct wmi *wmi, + struct ath6kl_vif *vif, const u8 *buf, size_t len, unsigned int freq) +{ + const u8 *pos; + u8 *p2p, *wfd; + int p2p_len, wfd_len; + int ret; + const struct ieee80211_mgmt *mgmt; + + mgmt = (const struct ieee80211_mgmt *) buf; + + p2p = kmalloc(len, GFP_KERNEL); + if (p2p == NULL) + return -ENOMEM; + p2p_len = 0; + + wfd = kmalloc(len, GFP_KERNEL); + if (wfd == NULL) { + kfree(p2p); + return -ENOMEM; + } + wfd_len = 0; + + /* Include P2P IE(s) from the frame generated in user space. */ + pos = mgmt->u.probe_resp.variable; + while (pos + 1 < buf + len) { + if (pos + 2 + pos[1] > buf + len) + break; + if (ath6kl_is_p2p_ie(pos)) { + memcpy(p2p + p2p_len, pos, 2 + pos[1]); + p2p_len += 2 + pos[1]; + } else if (ath6kl_is_wfd_ie(pos)) { + memcpy(wfd + wfd_len, pos, 2 + pos[1]); + wfd_len += 2 + pos[1]; + } + pos += 2 + pos[1]; + } + + ath6kl_p2p_ps_user_app_ie(vif->p2p_ps_info_ctx, + WMI_FRAME_PROBE_RESP, + &p2p, + &p2p_len); + + /* Add WFD IEs after P2P IEs. */ + if (wfd_len) { + u8 *p2p_wfd; + + p2p_wfd = kmalloc(p2p_len + wfd_len, GFP_KERNEL); + if (p2p_wfd == NULL) { + kfree(wfd); + kfree(p2p); + return -ENOMEM; + } + + memcpy(p2p_wfd, p2p, p2p_len); + memcpy(p2p_wfd + p2p_len, wfd, wfd_len); + + kfree(p2p); + p2p = p2p_wfd; + p2p_len += wfd_len; + } + + ret = ath6kl_wmi_send_probe_response_cmd(wmi, vif->fw_vif_idx, freq, + mgmt->da, p2p, p2p_len); + + kfree(p2p); + kfree(wfd); + + return ret; +} + +int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, u8 if_idx, bool enable) +{ + struct sk_buff *skb; + struct wmi_probe_req_report_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "probe_report_req_cmd: enable=%u\n", + enable); + p = (struct wmi_probe_req_report_cmd *) skb->data; + p->enable = enable ? 1 : 0; + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_PROBE_REQ_REPORT_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_probe_resp_report_req_cmd(struct wmi *wmi, u8 if_idx, + bool enable) +{ + struct sk_buff *skb; + struct wmi_probe_resp_report_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "probe_resp_report_req_cmd: enable=%u\n", + enable); + p = (struct wmi_probe_resp_report_cmd *) skb->data; + p->enable = enable ? 1 : 0; + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_CUSTOM_PROBE_RESP_REPORT_CMDID, NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u8 if_idx, u32 info_req_flags) +{ + struct sk_buff *skb; + struct wmi_get_p2p_info *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "info_req_cmd: flags=%x\n", + info_req_flags); + p = (struct wmi_get_p2p_info *) skb->data; + p->info_req_flags = cpu_to_le32(info_req_flags); + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_GET_P2P_INFO_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx) +{ + ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl_cmd\n"); + return ath6kl_wmi_simple_cmd(wmi, if_idx, + WMI_CANCEL_REMAIN_ON_CHNL_CMDID); +} + +static int wmi_rtt_event_rx(struct wmi *wmip, u8 *datap, int len) +{ + rttm_recv(datap, len); + return 0; +} + +static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) +{ + struct wmix_cmd_hdr *cmd; + u32 len; + u16 id; + u8 *datap; + int ret = 0; + + if (skb->len < sizeof(struct wmix_cmd_hdr)) { + ath6kl_err("bad packet 1\n"); + return -EINVAL; + } + + cmd = (struct wmix_cmd_hdr *) skb->data; + id = le32_to_cpu(cmd->cmd_id); + + skb_pull(skb, sizeof(struct wmix_cmd_hdr)); + + datap = skb->data; + len = skb->len; + + switch (id) { + case WMIX_HB_CHALLENGE_RESP_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n"); + break; + case WMIX_DBGLOG_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len); + ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len); + break; + case WMIX_RTT_RESP_EVENTID: + wmi_rtt_event_rx(wmi, datap, len); + break; +#ifdef ATH6KL_DIAGNOSTIC + case WMIX_PKTLOG_EVENTID: + ath6kl_wmi_pktlog_event_rx(wmi, datap, len); + break; +#endif + default: + ath6kl_warn("unknown cmd id 0x%x\n", id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int ath6kl_wmi_roam_tbl_event_rx(struct wmi *wmi, u8 *datap, int len) +{ + return ath6kl_debug_roam_tbl_event(wmi->parent_dev, datap, len); +} + +static int ath6kl_wmi_rsn_cap_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + struct wmi_rsn_cap_cmd *cmd = (struct wmi_rsn_cap_cmd *) datap; + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event rsn_cap %04x\n", cmd->rsn_cap); + + vif->last_rsn_cap = le16_to_cpu(cmd->rsn_cap); + + return 0; +} + +static int ath6kl_wmi_noa_info_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + struct wmi_noa_info *ev; + struct wmi_noa_descriptor *noa_descriptor; + int i, num_noa; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_noa_info *) datap; + + if (ev->count > ATH6KL_P2P_PS_MAX_NOA_DESCRIPTORS) + num_noa = ATH6KL_P2P_PS_MAX_NOA_DESCRIPTORS; + else + num_noa = ev->count; + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event noa_info, num_noa %d " + "enabled %d\n", num_noa, ev->enable); + + if (ath6kl_p2p_ps_reset_noa(vif->p2p_ps_info_ctx)) + return -EIO; + + if ((!ev->enable) || (num_noa == 0)) + goto update; + + noa_descriptor = (struct wmi_noa_descriptor *)(ev->noas); + for (i = 0; i < num_noa; i++) { + ath6kl_p2p_ps_setup_noa(vif->p2p_ps_info_ctx, i, + noa_descriptor->count_or_type, + le32_to_cpu(noa_descriptor->interval), + le32_to_cpu(noa_descriptor->start_or_offset), + le32_to_cpu(noa_descriptor->duration)); + noa_descriptor++; + } + +update: + /* Update to supplicant. */ + if (ath6kl_p2p_ps_update_notif(vif->p2p_ps_info_ctx)) + return -EIO; + + return 0; +} + +static int ath6kl_wmi_oppps_info_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + struct wmi_oppps_info *ev; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_oppps_info *) datap; + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event oppps_info, ctwin %d " + "enabled %d\n", ev->ctwin, ev->enable); + + if (ath6kl_p2p_ps_reset_opps(vif->p2p_ps_info_ctx)) + return -EIO; + + ath6kl_p2p_ps_setup_opps(vif->p2p_ps_info_ctx, + ev->enable, + (ev->ctwin & 0x7f)); + + /* Update to supplicant. */ + if (ath6kl_p2p_ps_update_notif(vif->p2p_ps_info_ctx)) + return -EIO; + + return 0; +} + +static int ath6kl_wmi_port_status_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len) +{ + struct wmi_port_status *ev; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_port_status *) datap; + + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event port_status, port_id %d status %d" + " vif->fw_vif_idx %d\n", + ev->port_id, ev->status, vif->fw_vif_idx); + + if ((ev->status == ADD_PORT_FAIL) || + (ev->status == DEL_PORT_FAIL)) { + ath6kl_err("Add_port/Del_port error.\n"); + } + + ath6kl_p2p_utils_check_port(vif, ev->port_id); + + return 0; +} + + +static int ath6kl_wmi_wow_ext_wake_event(struct wmi *wmi, u8 *datap, + int len) +{ + struct wmi_wow_event_wake_event *ev; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_wow_event_wake_event *) datap; + + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "-- wakeup-event --\n"); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, " flag: %d\n", ev->flags); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, " type: %d\n", ev->type); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, " value: %d\n", ev->value); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, " pkt_len: %d\n", ev->packet_length); + +#ifdef ATH6KL_SUPPORT_WLAN_HB + if (ev->type == WOW_EXT_WAKE_TYPE_WLAN_HB) { + ath6kl_wlan_hb_event(wmi->parent_dev, ev->value, ev->wake_data, + ev->packet_length); + } +#endif + + return 0; +} + +#ifdef ATH6KL_SUPPORT_WIFI_DISC +static int ath6kl_wmi_disc_peer_event_rx(u8 *datap, + int len, struct ath6kl_vif *vif) +{ + struct ath6kl *ar = vif->ar; + struct wmi_disc_peer_event *ev; + u16 peers_size; + u8 peer_num; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_disc_peer_event *) datap; + peer_num = ev->peer_num; + peers_size = sizeof(struct wmi_disc_peer)*peer_num; + + ath6kl_tm_disc_event(ar, ev, sizeof(*ev)+peers_size-1); + + return 0; +} +#endif + +static int ath6kl_wmi_wmm_params_event_rx(struct wmi *wmi, u8 *datap, + int len) +{ + int i; + struct wmi_report_wmm_params *wmm_params = + (struct wmi_report_wmm_params *) datap; + bool change = false; + + for (i = 0; i < WMM_NUM_AC; i++) { + ath6kl_dbg(ATH6KL_DBG_WMI, "(%d): acm: %d, aifsn: %d, " + "cwmin: %d, cwmax: %d, txop: %d\n", + i, wmm_params->wmm_params[i].acm, + wmm_params->wmm_params[i].aifsn, + wmm_params->wmm_params[i].logcwmin, + wmm_params->wmm_params[i].logcwmax, + wmm_params->wmm_params[i].txopLimit); + } + + if (wmm_params->wmm_params[WMM_AC_BE].aifsn < + wmm_params->wmm_params[WMM_AC_VI].aifsn) + change = true; + + ath6kl_indicate_wmm_schedule_change(wmi->parent_dev, change); + + return 0; +} + +static int ath6kl_wmi_assoc_req_event_rx(struct ath6kl_vif *vif, u8 *datap, + int len) +{ + struct wmi_assoc_req_event *assoc_req_event = + (struct wmi_assoc_req_event *) datap; + + /* At least include 802.11 header */ + if (len < sizeof(*assoc_req_event) + 28) + return -EINVAL; + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event assoc_req len %d\n", len); + + ath6kl_ap_admc_assoc_req(vif, + assoc_req_event->assocReq, + len - sizeof(struct wmi_assoc_req_event), + assoc_req_event->rspType, + assoc_req_event->status); + + return 0; +} + +/* Control Path */ +int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd; + struct ath6kl_vif *vif; + u32 len; + u16 id; + u8 if_idx; + u8 *datap; + int ret = 0; + struct ath6kl *ar; + + if (WARN_ON(skb == NULL)) + return -EINVAL; + + if (skb->len < sizeof(struct wmi_cmd_hdr)) { + ath6kl_err("bad packet 1\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + cmd = (struct wmi_cmd_hdr *) skb->data; + id = le16_to_cpu(cmd->cmd_id); + if_idx = le16_to_cpu(cmd->info1) & WMI_CMD_HDR_IF_ID_MASK; + + skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + + datap = skb->data; + len = skb->len; + + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi rx id %d len %d\n", id, len); + ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ", + datap, len); + + vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); + if (!vif) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "Wmi event for unavailable vif, vif_index:%d\n", + if_idx); + dev_kfree_skb(skb); + return -EINVAL; + } + + ar = vif->ar; + + /* Keep WMI event be processed in sequence. */ + if (down_interruptible(&ar->wmi_evt_sem)) { + ath6kl_err("ath6kl_wmi_control_rx busy, couldn't get access\n"); + return -ERESTARTSYS; + } + + switch (id) { + case WMI_GET_BITRATE_CMDID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_BITRATE_CMDID\n"); + ret = ath6kl_wmi_bitrate_reply_rx(wmi, datap, len); + break; + case WMI_GET_CHANNEL_LIST_CMDID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_CHANNEL_LIST_CMDID\n"); + ret = ath6kl_wmi_ch_list_reply_rx(wmi, datap, len); + break; + case WMI_GET_TX_PWR_CMDID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_TX_PWR_CMDID\n"); + ret = ath6kl_wmi_tx_pwr_reply_rx(wmi, datap, len); + break; + case WMI_READY_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_READY_EVENTID\n"); + ret = ath6kl_wmi_ready_event_rx(wmi, datap, len); + ath6kl_send_event_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_CONNECT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n"); + ret = ath6kl_wmi_connect_event_rx(wmi, datap, len, vif); + ath6kl_send_genevent_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_DISCONNECT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n"); + ret = ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif); + ath6kl_send_event_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_PEER_NODE_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PEER_NODE_EVENTID\n"); + ret = ath6kl_wmi_peer_node_event_rx(wmi, datap, len); + ath6kl_send_event_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_TKIP_MICERR_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n"); + ret = ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif); + break; + case WMI_BSSINFO_EVENTID: +#ifdef ATHTST_SUPPORT + if (test_bit(CE_WMI_SCAN, &vif->flags)) { + ret = ath6kl_wmi_ce_get_scaninfo_event_rx(vif, datap, + len); + break; + } + if (test_bit(CE_WMI_TESTMODE_RX, &vif->flags)) { + struct wmi_bss_info_hdr2 *bih; + u8 *buf; + + if (len <= sizeof(struct wmi_bss_info_hdr2)) + break; + + bih = (struct wmi_bss_info_hdr2 *) datap; + buf = datap + sizeof(struct wmi_bss_info_hdr2); + len -= sizeof(struct wmi_bss_info_hdr2); + + if (memcmp(testmode_private.bssid, bih->bssid, + sizeof(testmode_private.bssid)) == 0) { + printk( + KERN_DEBUG "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" " + "frame_type=%d\n", + bih->ch, bih->snr, bih->snr - 95, bih->bssid, + bih->frame_type); + testmode_private.rssi_combined = bih->snr; + } + break; + } +#endif + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n"); + ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif); +#ifdef CE_SUPPORT + ath6kl_wmi_fake_probe_resp_event_by_bssinfo(wmi, datap, + len, vif); +#endif + break; + case WMI_REGDOMAIN_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n"); + ath6kl_wmi_regdomain_event(wmi, datap, len); + break; + case WMI_PSTREAM_TIMEOUT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n"); + ret = ath6kl_wmi_pstream_timeout_event_rx(wmi, datap, len); + ath6kl_send_event_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_NEIGHBOR_REPORT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n"); + ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len, + vif); + break; + case WMI_SCAN_COMPLETE_EVENTID: +#ifdef ATHTST_SUPPORT + if (test_bit(CE_WMI_SCAN, &vif->flags)) { + ret = ath6kl_wmi_ce_set_scan_done_event_rx(vif, datap, + len); + break; + } + if (test_bit(CE_WMI_TESTMODE_RX, &vif->flags)) { + /* issue a scan again */ + + struct athcfg_wcmd_testmode_t testmode; + testmode.operation = ATHCFG_WCMD_TESTMODE_RX; + testmode.rx = 1; + ath6kl_wmi_set_customer_testmode_cmd(vif, &testmode); + break; + } +#endif + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n"); + ret = ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif); + ath6kl_send_event_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_CMDERROR_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CMDERROR_EVENTID\n"); + ret = ath6kl_wmi_error_event_rx(wmi, datap, len); + break; + case WMI_REPORT_STATISTICS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n"); + ret = ath6kl_wmi_stats_event_rx(wmi, datap, len, vif); + break; + case WMI_RSSI_THRESHOLD_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RSSI_THRESHOLD_EVENTID\n"); + ret = ath6kl_wmi_rssi_threshold_event_rx(wmi, datap, len); + break; + case WMI_ERROR_REPORT_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ERROR_REPORT_EVENTID\n"); + ath6kl_send_event_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_OPT_RX_FRAME_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n"); + /* this event has been deprecated */ + break; + case WMI_REPORT_ROAM_TBL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n"); + ret = ath6kl_wmi_roam_tbl_event_rx(wmi, datap, len); + break; + case WMI_EXTENSION_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_EXTENSION_EVENTID\n"); + ret = ath6kl_wmi_control_rx_xtnd(wmi, skb); + break; + case WMI_CAC_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n"); + ret = ath6kl_wmi_cac_event_rx(wmi, datap, len, vif); + break; + case WMI_CHANNEL_CHANGE_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CHANNEL_CHANGE_EVENTID\n"); + break; + case WMI_REPORT_ROAM_DATA_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_DATA_EVENTID\n"); + break; + case WMI_TEST_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TEST_EVENTID\n"); + ret = ath6kl_wmi_tcmd_test_report_rx(wmi, datap, len); + break; + case WMI_GET_FIXRATES_CMDID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n"); + ret = ath6kl_wmi_ratemask_reply_rx(wmi, datap, len); + break; + case WMI_TX_RETRY_ERR_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_RETRY_ERR_EVENTID\n"); + ath6kl_send_event_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_SNR_THRESHOLD_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SNR_THRESHOLD_EVENTID\n"); + ret = ath6kl_wmi_snr_threshold_event_rx(wmi, datap, len); + break; + case WMI_LQ_THRESHOLD_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_LQ_THRESHOLD_EVENTID\n"); + ath6kl_send_event_to_app(skb->dev, id, if_idx, datap, len); + break; + case WMI_APLIST_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_APLIST_EVENTID\n"); + ret = ath6kl_wmi_aplist_event_rx(wmi, datap, len); + break; + case WMI_GET_KEEPALIVE_CMDID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_KEEPALIVE_CMDID\n"); + ret = ath6kl_wmi_keepalive_reply_rx(wmi, datap, len); + break; + case WMI_GET_WOW_LIST_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_WOW_LIST_EVENTID\n"); + break; + case WMI_GET_PMKID_LIST_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_PMKID_LIST_EVENTID\n"); + ret = ath6kl_wmi_get_pmkid_list_event_rx(wmi, datap, len, vif); + break; + case WMI_PSPOLL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n"); + ret = ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif); + break; + case WMI_DTIMEXPIRY_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n"); + ret = ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif); + break; + case WMI_SET_PARAMS_REPLY_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SET_PARAMS_REPLY_EVENTID\n"); + break; + case WMI_ADDBA_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n"); + ret = ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif); + break; + case WMI_ADDBA_RESP_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_RESP_EVENTID\n"); + ret = ath6kl_wmi_addba_resp_event_rx(vif, datap, len); + break; + case WMI_DELBA_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n"); + ret = ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif); + break; + case WMI_REPORT_BTCOEX_CONFIG_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_REPORT_BTCOEX_CONFIG_EVENTID\n"); + break; + case WMI_REPORT_BTCOEX_STATS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_REPORT_BTCOEX_STATS_EVENTID\n"); + break; + case WMI_TX_COMPLETE_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n"); + ret = ath6kl_wmi_tx_complete_event_rx(datap, len); + break; + case WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID"); + ret = ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif); + break; + case WMI_REMAIN_ON_CHNL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n"); + ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif); + break; + case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n"); + ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap, + len, vif); + break; + case WMI_TX_STATUS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); + ret = ath6kl_wmi_tx_status_event_rx(wmi, datap, len, vif); + break; + case WMI_RX_PROBE_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); + ret = ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len, vif); + break; +#ifdef CE_SUPPORT + case WMI_RX_PROBE_RESP_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_RESP_EVENTID\n"); + ret = ath6kl_wmi_rx_probe_resp_event_rx(vif, datap, len); + break; +#endif + case WMI_ACL_REJECT_EVENTID: + #ifdef ACL_SUPPORT + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ACL_REJECT_EVENTID\n"); + ret = ath6kl_wmi_acl_reject_event_rx(vif, datap, len); + #endif + break; + case WMI_P2P_CAPABILITIES_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n"); + ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len); + break; + case WMI_RX_ACTION_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); + ret = ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif); + break; + case WMI_P2P_INFO_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n"); + ret = ath6kl_wmi_p2p_info_event_rx(datap, len); + break; + case WMI_GET_RSN_CAP_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_RSN_CAP_EVENTID\n"); + ret = ath6kl_wmi_rsn_cap_event_rx(vif, datap, len); + break; + case WMI_FLOWCTRL_IND_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_FLOWCTRL_IND_EVENTID\n"); + ret = ath6kl_wmi_flowctrl_ind_event_rx(datap, len, vif); + break; + case WMI_NOA_INFO_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NOA_INFO_EVENTID\n"); + ret = ath6kl_wmi_noa_info_event_rx(vif, datap, len); + break; + case WMI_OPPPS_INFO_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPPPS_INFO_EVENTID\n"); + ret = ath6kl_wmi_oppps_info_event_rx(vif, datap, len); + break; + case WMI_PORT_STATUS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PORT_STATUS_EVENTID\n"); + ret = ath6kl_wmi_port_status_event_rx(vif, datap, len); + break; +#ifdef ATH6KL_DIAGNOSTIC + case WMI_DIAGNOSTIC_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DIAGNOSTIC_EVENTID\n"); + ret = ath6kl_wmi_diag_event(vif, wmi, skb); + break; +#endif + case WMI_WOW_EXT_WAKE_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_WOW_EXT_WAKE_EVENTID\n"); + ret = ath6kl_wmi_wow_ext_wake_event(wmi, datap, len); + break; +#ifdef ATH6KL_SUPPORT_WIFI_DISC + case WMI_DISC_PEER_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISC_PEER_EVENTID\n"); + ret = ath6kl_wmi_disc_peer_event_rx(datap, len, vif); + break; +#endif + case WMI_REPORT_WMM_PARAMS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_WMM_PARAMS_EVENTID\n"); + ret = ath6kl_wmi_wmm_params_event_rx(wmi, datap, len); + break; + case WMI_ASSOC_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ASSOC_REQ_EVENTID\n"); + ret = ath6kl_wmi_assoc_req_event_rx(vif, datap, len); + break; +#ifdef ATHTST_SUPPORT + case WMI_GET_REG_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "%s[%d] WMI_GET_REG_EVENTID\n", + __func__, __LINE__); + ret = ath6kl_wmi_ce_get_reg_event_rx(vif, datap, len); + break; + case WMI_GET_STAINFO_EVENTID: + ret = ath6kl_wmi_ce_get_stainfo_event_rx(vif, datap, len); + break; + case WMI_GET_TXPOW_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "%s[%d] WMI_GET_TXPOW_EVENTID\n", + __func__, __LINE__); + ret = ath6kl_wmi_ce_get_txpow_event_rx(vif, datap, len); + break; + case WMI_GET_VERSION_INFO_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "%s[%d] WMI_GET_VERSION_INFO_EVENTID\n", + __func__, __LINE__); + ret = ath6kl_wmi_ce_get_version_info_event_rx(vif, datap, len); + break; + case WMI_GET_TESTMODE_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "%s[%d] WMI_GET_TESTMODE_EVENTID\n", + __func__, __LINE__); + ret = ath6kl_wmi_ce_get_testmode_event_rx(vif, datap, len); + break; +#if defined(CE_CUSTOM_1) + case WMI_GET_WIDIMODE_EVENTID: + ret = ath6kl_wmi_ce_get_widimode_event_rx(vif, datap, len); + break; +#endif +#endif + case WMI_GET_ANTDIVSTAT_CMDID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_ANTDIVSTAT_CMDID\n"); + ret = ath6kl_wmi_antdivstate_event_rx(vif, datap, len); + break; + default: + ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id); + ret = -EINVAL; + break; + } + up(&ar->wmi_evt_sem); + + dev_kfree_skb(skb); + + return ret; +} + +void ath6kl_wmi_reset(struct wmi *wmi) +{ + spin_lock_bh(&wmi->lock); + + wmi->fat_pipe_exist = 0; + memset(wmi->stream_exist_for_ac, 0, sizeof(wmi->stream_exist_for_ac)); + + spin_unlock_bh(&wmi->lock); +} + +void *ath6kl_wmi_init(struct ath6kl *dev) +{ + struct wmi *wmi; + + wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL); + if (!wmi) + return NULL; + + spin_lock_init(&wmi->lock); + + wmi->parent_dev = dev; + + wmi->pwr_mode = REC_POWER; + + INIT_LIST_HEAD(&wmi->mgmt_tx_frame_list); + + ath6kl_wmi_reset(wmi); + +#ifdef ATH6KL_DIAGNOSTIC + globalwmi = wmi; +#endif + + return wmi; +} + +void ath6kl_wmi_shutdown(struct wmi *wmi) +{ + struct wmi_mgmt_tx_frame *mgmt_tx_frame, *tmp; + + if (!wmi) + return; + + list_for_each_entry_safe(mgmt_tx_frame, tmp, + &wmi->mgmt_tx_frame_list, list) { + list_del(&mgmt_tx_frame->list); + kfree(mgmt_tx_frame->mgmt_tx_frame); + kfree(mgmt_tx_frame); + } + kfree(wmi); +} + + +int wmi_rtt_req(struct wmi *wmip, enum wmi_cmd_id cmd_id, void *data, u32 len) +{ + struct sk_buff *skb; + int status; + void *cmd = NULL; + + skb = ath6kl_wmi_get_new_buf(len); + if (skb == NULL) { + ath6kl_dbg(ATH6KL_DBG_RTT, "RTTREQ Failed To get WMI Buffer"); + return -ENOMEM; + } + + cmd = skb->data; + memset(cmd, 0, len); + memcpy(cmd, data, len); + + status = ath6kl_wmi_cmd_send(wmip, 0, skb, cmd_id, + NO_SYNC_WMIFLAG); + return status; +} + +int ath6kl_wmi_set_green_tx_params(struct wmi *wmi, + struct wmi_green_tx_params *params) +{ + struct sk_buff *skb; + struct wmi_green_tx_params *cmd; + int ret = 0; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_green_tx_params)); + + if (skb == NULL) + return -ENOMEM; + + cmd = (struct wmi_green_tx_params *)skb->data; + memset(cmd, 0, sizeof(struct wmi_green_tx_params)); + + memcpy(cmd, params, sizeof(struct wmi_green_tx_params)); + + /* change the byte order */ + cmd->enable = cpu_to_le32(cmd->enable); + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_GREENTX_PARAMS_CMDID, NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_smps_config(struct wmi *wmi, struct wmi_config_smps_cmd *options) +{ + struct sk_buff *skb; + struct wmi_config_smps_cmd *cmd; + int ret = 0; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_config_smps_cmd)); + + if (skb == NULL) + return -ENOMEM; + + cmd = (struct wmi_config_smps_cmd *)skb->data; + memset(cmd, 0, sizeof(struct wmi_config_smps_cmd)); + + memcpy(cmd, options, sizeof(struct wmi_config_smps_cmd)); + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_SMPS_CONFIG_CMDID, NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_smps_enable(struct wmi *wmi, + struct wmi_config_enable_cmd *options) +{ + struct sk_buff *skb; + struct wmi_config_enable_cmd *cmd; + int ret = 0; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_config_enable_cmd)); + + if (skb == NULL) + return -ENOMEM; + + cmd = (struct wmi_config_enable_cmd *)skb->data; + memset(cmd, 0, sizeof(struct wmi_config_enable_cmd)); + + memcpy(cmd, options, sizeof(struct wmi_config_enable_cmd)); + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_SMPS_ENABLE_CMDID, NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_lpl_enable_cmd(struct wmi *wmi, + struct wmi_lpl_force_enable_cmd *force_enable_cmd) +{ + struct sk_buff *skb; + struct wmi_lpl_force_enable_cmd *cmd; + int ret = 0; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_lpl_force_enable_cmd)); + + if (skb == NULL) + return -ENOMEM; + + cmd = (struct wmi_lpl_force_enable_cmd *)skb->data; + memset(cmd, 0, sizeof(struct wmi_lpl_force_enable_cmd)); + + memcpy(cmd, force_enable_cmd, sizeof(struct wmi_lpl_force_enable_cmd)); + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_LPL_FORCE_ENABLE_CMDID, NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_abort_scan_cmd(struct wmi *wmi, u8 if_idx) +{ + ath6kl_dbg(ATH6KL_DBG_WMI, "abort_scan_cmd\n"); + return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_ABORT_SCAN_CMDID); +} + +int ath6kl_wmi_set_ht_cap_cmd(struct wmi *wmi, u8 if_idx, + u8 band, u8 chan_width_40M_supported, u8 short_GI, u8 intolerance_40MHz) +{ + int ret = 0; + struct sk_buff *skb; + struct wmi_set_ht_cap *cmd; + + if (WARN_ON((band != A_BAND_24GHZ) && (band != A_BAND_5GHZ)) || + WARN_ON(chan_width_40M_supported > 1) || + WARN_ON(short_GI > 1)) + return ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "set_ht_cap_cmd: if_idx=%d band=%d " + "chan_width_40M_supported=%d short_GI=%d " + "intolerance_40MHz=%d\n", + if_idx, + band, chan_width_40M_supported, short_GI, intolerance_40MHz); + + cmd = (struct wmi_set_ht_cap *) skb->data; + cmd->band = band; + cmd->enable = 1; + cmd->chan_width_40M_supported = chan_width_40M_supported; + cmd->short_GI_20MHz = short_GI; + cmd->short_GI_40MHz = short_GI; + cmd->intolerance_40MHz = intolerance_40MHz; + cmd->max_ampdu_len_exp = 2; /* always 32K */ + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HT_CAP_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_set_ht_op_cmd(struct wmi *wmi, u8 if_idx, + u8 sta_chan_width, u8 opmode) +{ + int ret = 0; + struct sk_buff *skb; + struct wmi_set_ht_op *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "set_ht_op_cmd: if_idx=%d sta_chan_width=%d opmode=%d\n", + if_idx, + sta_chan_width, opmode); + + cmd = (struct wmi_set_ht_op *) skb->data; + cmd->sta_chan_width = 0; /* TODO */ + cmd->ap_ht_info = ((opmode & 0x3) << 0); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HT_OP_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_set_hidden_ssid_cmd(struct wmi *wmi, u8 if_idx, u8 hidden_ssid) +{ + struct sk_buff *skb; + struct wmi_set_hidden_ssid *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_hidden_ssid: %d\n", hidden_ssid); + + cmd = (struct wmi_set_hidden_ssid *)skb->data; + cmd->hidden_ssid = hidden_ssid; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_HIDDEN_SSID_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_beacon_interval_cmd(struct wmi *wmi, + u8 if_idx, u32 beacon_interval) +{ + struct sk_buff *skb; + struct wmi_set_beacon_intvl *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_beacon_interval: %d\n", + beacon_interval); + + cmd = (struct wmi_set_beacon_intvl *)skb->data; + cmd->beacon_interval = cpu_to_le16(beacon_interval); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_BEACON_INT_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim) +{ + struct sk_buff *skb; + struct wmi_set_dtim_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_dtim: %d\n", dtim); + + cmd = (struct wmi_set_dtim_cmd *)skb->data; + cmd->dtim = (u8)dtim; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_DTIM_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable) +{ + struct sk_buff *skb; + struct wmi_ap_set_apsd_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_apsd: %d\n", enable); + + cmd = (struct wmi_ap_set_apsd_cmd *)skb->data; + cmd->enable = enable; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_APSD_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_apsd_buffered_traffic_cmd(struct wmi *wmi, u8 if_idx, + u16 aid, u16 bitmap, u32 flags) +{ + struct sk_buff *skb; + struct wmi_ap_apsd_buffered_traffic_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_apsd_buffered_traffic: %d %x %x\n", + aid, bitmap, flags); + + cmd = (struct wmi_ap_apsd_buffered_traffic_cmd *)skb->data; + cmd->aid = aid; + cmd->bitmap = bitmap; + cmd->flags = flags; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_add_wow_ext_pattern_cmd(struct wmi *wmi, u8 if_idx, + u8 list_id, u8 filter_size, + u8 filter_id, u8 *filter, u8 *mask) +{ + struct sk_buff *skb; + struct wmi_add_wow_ext_pattern_cmd *cmd; + int size; + u8 *filter_mask; + int mask_size; + int ret = 0; + + if (0 == filter_size) + return -EINVAL; + + mask_size = ((int)filter_size) / 8; + if (filter_size % 8) + mask_size++; + + size = sizeof(struct wmi_add_wow_ext_pattern_cmd); + size += (int)filter_size; + size += mask_size; + + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_add_wow_ext_pattern_cmd *)skb->data; + + cmd->filter_list_id = list_id; + cmd->filter_id = filter_id; + cmd->filter_offset = 0; + cmd->filter_size = filter_size; + + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "Adding wow pattern\n"); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "\t cmd->filter_list_id: %d", + cmd->filter_list_id); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "\t cmd->filter_id: %d", + cmd->filter_id); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "\t cmd->filter_offset: %d", + cmd->filter_offset); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "\t cmd->filter_size: %d", + cmd->filter_size); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "\t mask-size: %d", mask_size); + + memcpy(cmd->filter, filter, cmd->filter_size); + filter_mask = (u8 *)(cmd->filter + cmd->filter_size); + memcpy(filter_mask, mask, mask_size); + ath6kl_dbg(ATH6KL_DBG_WOWLAN, "\t mask: %x", + cmd->filter[cmd->filter_size]); + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_ADD_WOW_EXT_PATTERN_CMDID, NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_del_all_wow_ext_patterns_cmd(struct wmi *wmi, u8 if_idx, + __le16 filter_list_id) +{ + return ath6kl_wmi_del_wow_pattern_cmd(wmi, if_idx, + filter_list_id, WOW_EXT_FILTER_ID_CLEAR_ALL); +} + +int ath6kl_wm_set_gtk_offload(struct wmi *wmi, u8 if_idx, + u8 *kek, u8 *kck, u8 *replay_ctr) +{ + int ret = 0; + struct sk_buff *skb; + struct wmi_gtk_offload_op *cmd; + u64 replay_counter; + + if (WARN_ON(!kek) || WARN_ON(!kck) || WARN_ON(!replay_ctr)) + return ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_gtk_offload_op)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_gtk_offload_op *)skb->data; + + memset(cmd, 0, sizeof(struct wmi_gtk_offload_op)); + + replay_counter = cpu_to_be64(*replay_ctr); + memcpy(cmd->kek, kek, NL80211_KEK_LEN); + memcpy(cmd->kck, kck, NL80211_KCK_LEN); + memcpy(cmd->replay_counter, + (u8 *)&replay_counter, + NL80211_REPLAY_CTR_LEN); + + cmd->opcode = WMI_GTK_OFFLOAD_OPCODE_SET; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_GTK_OFFLOAD_OP_CMDID, NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_set_tx_select_rates_on_all_mode(struct wmi *wmi, + u8 if_idx, u64 mask) +{ + struct sk_buff *skb; + struct wmi_set_tx_select_rate_cmd *cmd; + int ret; + int i; + u64 txselectmask; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_tx_select_rate_cmd *) skb->data; + txselectmask = cpu_to_le64(mask); + + for (i = 0; i < (WMI_MODE_MAX * WMI_MAX_RATE_MASK); i += 2) + memcpy((char *)(cmd->rateMasks + i), + (char *)&txselectmask, sizeof(txselectmask)); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_TX_SELECT_RATES_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_antdivcfg(struct wmi *wmi, + u8 if_idx, u8 diversity_control) +{ + struct sk_buff *skb; + struct wmi_ant_div_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ant_div_cmd *) skb->data; + cmd->diversity_control = diversity_control; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_ANTDIVCFG_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_rsn_cap(struct wmi *wmi, u8 if_idx, u16 rsn_cap) +{ + struct sk_buff *skb; + struct wmi_rsn_cap_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_rsn_cap: 0x%04x\n", rsn_cap); + + cmd = (struct wmi_rsn_cap_cmd *)skb->data; + cmd->rsn_cap = cpu_to_le16(rsn_cap); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSN_CAP_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_get_rsn_cap(struct wmi *wmi, u8 if_idx) +{ + return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_RSN_CAP_CMDID); +} + +int ath6kl_wmi_get_pmkid_list(struct wmi *wmi, u8 if_idx) +{ + return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_PMKID_LIST_CMDID); +} + +int ath6kl_wmi_set_fix_rates(struct wmi *wmi, u8 if_idx, u64 mask) +{ + struct sk_buff *skb; + struct wmi_set_fix_rates_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_fix_rates_cmd *)skb->data; + cmd->fixRateMask[0] = (u32)(mask & 0xffffffff); + cmd->fixRateMask[1] = (u32)((mask >> 32) & 0xffffffff); + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_fix_rate: 0x%08x 0x%08x\n", + cmd->fixRateMask[0], cmd->fixRateMask[1]); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_FIXRATES_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_add_port_cmd(struct wmi *wmi, struct ath6kl_vif *vif, + u8 opmode, u8 subopmode) +{ + struct sk_buff *skb; + struct wmi_add_port_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_add_port_cmd *)skb->data; + cmd->port_id = 0; + cmd->port_opmode = opmode; + cmd->port_subopmode = subopmode; + /* Use MAC selection to find FW's device index. */ + memcpy(cmd->mac_addr, &vif->ndev->dev_addr[0], ETH_ALEN); + + ath6kl_dbg(ATH6KL_DBG_WMI, "add_port: if_idx %d opmode %d " + "subopmode %d\n", + vif->fw_vif_idx, + opmode, subopmode); + + return ath6kl_wmi_cmd_send(wmi, vif->fw_vif_idx, skb, + WMI_ADD_PORT_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_del_port_cmd(struct wmi *wmi, u8 if_idx, u8 port_id) +{ + struct sk_buff *skb; + struct wmi_del_port_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_del_port_cmd *)skb->data; + cmd->port_id = port_id; + + ath6kl_dbg(ATH6KL_DBG_WMI, "del_port: if_idx %d port_id %d\n", + if_idx, port_id); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DEL_PORT_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_noa_cmd(struct wmi *wmi, u8 if_idx, + u8 count, u32 start, u32 duration, u32 interval) +{ + struct ath6kl_vif *vif; + struct wmi_noa_info *cmd; + struct wmi_noa_descriptor *noa_descriptor; + struct sk_buff *skb; + size_t cmd_size; + + if (!wmi->parent_dev->p2p) + return -EINVAL; + + vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx); + if ((!vif) || + ((vif) && + (vif->nw_type != AP_NETWORK))) { + ath6kl_err("set noa in client mode? if_idx %d, count %d, " + "start %d, duration %d, interval %d\n", + if_idx, + count, + start, + duration, + interval); + return -ENOTSUPP; + } + + /* Only support one noa_descriptor now. */ + cmd_size = sizeof(struct wmi_noa_info) + + sizeof(struct wmi_noa_descriptor); + skb = ath6kl_wmi_get_new_buf(cmd_size); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_noa_info *)skb->data; + cmd->enable = 0; + cmd->count = 0; + if (count) { + cmd->enable = 1; + cmd->count = 1; + + noa_descriptor = (struct wmi_noa_descriptor *)cmd->noas; + noa_descriptor->duration = 0; + noa_descriptor->interval = 0; + + /* + * interval: 0 will use Beacon interval as interval + * start_or_offst: always offset of next TBTT + */ + if (count == 1) { + /* One-shot NoA */ + noa_descriptor->count_or_type = 1; + noa_descriptor->duration = duration; + /* TODO : across TBTT */ + noa_descriptor->interval = duration; + noa_descriptor->start_or_offset = start; + } else if (count != 255) { + /* Non-Periodic NoA */ + noa_descriptor->count_or_type = count; + noa_descriptor->duration = duration; + noa_descriptor->interval = interval; + noa_descriptor->start_or_offset = start; + } else { + /* Periodic NoA */ + noa_descriptor->count_or_type = 255; + noa_descriptor->duration = duration; + noa_descriptor->interval = interval; + noa_descriptor->start_or_offset = start; + } + } + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_noa: if_idx %d count %d start %d " + "duration %d interval %d\n", + if_idx, + count, + start, + duration, + interval); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_NOA_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_oppps_cmd(struct wmi *wmi, u8 if_idx, + u8 enable, u8 ctwin) +{ + struct sk_buff *skb; + struct wmi_oppps_info *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_oppps_info *)skb->data; + cmd->enable = enable; + if (enable) + cmd->ctwin = ctwin; + else + cmd->ctwin = 0; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_oppps: if_idx %d enable %d ctwin %d\n", + if_idx, + enable, + ctwin); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_OPPPS_CMDID, + NO_SYNC_WMIFLAG); +} + +#ifdef ATH6KL_SUPPORT_WLAN_HB +int ath6kl_wmi_set_heart_beat_params(struct wmi *wmi, u8 if_idx, + u8 enable, u8 item, u8 session) +{ + struct sk_buff *skb; + struct wmi_heart_beat_params_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_heart_beat_params_cmd *)skb->data; + cmd->enable = enable; + cmd->item = item; + cmd->session = session; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_HEART_PARAMS_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_heart_beat_set_tcp_params(struct wmi *wmi, u8 if_idx, + u16 src_port, u16 dst_port, u32 srv_ip, u32 dev_ip, u16 timeout, + u8 session, u8 *gateway_mac) +{ + struct sk_buff *skb; + struct wmi_heart_beat_tcp_params_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_heart_beat_tcp_params_cmd *)skb->data; + cmd->src_port = cpu_to_le16(src_port); + cmd->dst_port = cpu_to_le16(dst_port); + cmd->srv_ip = cpu_to_le32(srv_ip); + cmd->dev_ip = cpu_to_le32(dev_ip); + cmd->timeout = cpu_to_le16(timeout); + cmd->session = session; + memcpy(cmd->gateway_mac, gateway_mac, ETH_ALEN); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_HEART_SET_TCP_PARAMS_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_heart_beat_set_tcp_filter(struct wmi *wmi, u8 if_idx, + u8 *filter, u8 length, u8 offset, u8 session) +{ + struct sk_buff *skb; + struct wmi_heart_beat_tcp_filter_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_heart_beat_tcp_filter_cmd *)skb->data; + memcpy(cmd->filter, filter, length); + cmd->length = length; + cmd->offset = offset; + cmd->session = session; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_HEART_SET_TCP_PKT_FILTER_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_heart_beat_set_udp_params(struct wmi *wmi, u8 if_idx, + u16 src_port, u16 dst_port, u32 srv_ip, + u32 dev_ip, u16 interval, u16 timeout, + u8 session, u8 *gateway_mac) +{ + struct sk_buff *skb; + struct wmi_heart_beat_udp_params_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_heart_beat_udp_params_cmd *)skb->data; + cmd->src_port = cpu_to_le16(src_port); + cmd->dst_port = cpu_to_le16(dst_port); + cmd->srv_ip = cpu_to_le32(srv_ip); + cmd->dev_ip = cpu_to_le32(dev_ip); + cmd->interval = cpu_to_le16(interval); + cmd->timeout = cpu_to_le16(timeout); + cmd->session = session; + memcpy(cmd->gateway_mac, gateway_mac, ETH_ALEN); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_HEART_SET_UDP_PARAMS_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_heart_beat_set_udp_filter(struct wmi *wmi, u8 if_idx, + u8 *filter, u8 length, u8 offset, u8 session) +{ + struct sk_buff *skb; + struct wmi_heart_beat_udp_filter_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_heart_beat_udp_filter_cmd *)skb->data; + memcpy(cmd->filter, filter, length); + cmd->length = length; + cmd->offset = offset; + cmd->session = session; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_HEART_SET_UDP_PKT_FILTER_CMDID, + NO_SYNC_WMIFLAG); +} +#endif + +#ifdef ATH6KL_SUPPORT_WIFI_DISC +int ath6kl_wmi_disc_ie_cmd(struct wmi *wmi, u8 if_idx, u8 enable, + u8 startPos, u8 *pattern, u8 length) +{ + struct sk_buff *skb; + struct wmi_disc_ie_filter_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) + length); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_disc_ie_filter_cmd *) skb->data; + + cmd->enable = enable; + cmd->startPos = startPos; + cmd->length = length; + + if (cmd->enable) + memcpy(cmd->pattern, pattern, cmd->length); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_DISC_SET_IE_FILTER_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +int ath6kl_wmi_disc_mode_cmd(struct wmi *wmi, u8 if_idx, u16 enable, + u16 channel, u32 home_dwell_time, u32 sleepTime, + u32 random, u32 numPeers, u32 peerTimeout) +{ + struct sk_buff *skb; + struct wmi_disc_mode_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_disc_mode_cmd *) skb->data; + + cmd->enable = enable; + cmd->channel = cpu_to_le16(channel); + cmd->home_dwell_time = cpu_to_le32(home_dwell_time); + cmd->sleepTime = cpu_to_le32(sleepTime); + cmd->random = cpu_to_le32(random); + cmd->numPeers = cpu_to_le32(numPeers); + cmd->peerTimeout = cpu_to_le32(peerTimeout); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_DISC_SET_MODE_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} +#endif + +int ath6kl_wmi_ap_poll_sta(struct wmi *wmi, u8 if_idx, u8 aid) +{ + struct sk_buff *skb; + struct wmi_ap_poll_sta_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "keep_alive_nulldata: aid %d\n", aid); + + cmd = (struct wmi_ap_poll_sta_cmd *)skb->data; + cmd->aid = aid; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_POLL_STA_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_ap_acl_policy(struct wmi *wmi, u8 if_idx, u8 policy) +{ + struct sk_buff *skb; + struct wmi_ap_acl_policy_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "acl_policy: policy %d\n", policy); + + cmd = (struct wmi_ap_acl_policy_cmd *)skb->data; + cmd->policy = policy; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_ACL_POLICY_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_ap_acl_mac_list(struct wmi *wmi, u8 if_idx, + u8 idx, u8 *mac_addr, u8 action) +{ + struct sk_buff *skb; + struct wmi_ap_acl_mac_list_cmd *cmd; + + if (WARN_ON(idx >= AP_ACL_SIZE)) + return -EINVAL; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "acl_policy: %d %02x:%02x:%02x:%02x:%02x:%02x %d\n", + idx, + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5], + action); + + cmd = (struct wmi_ap_acl_mac_list_cmd *)skb->data; + cmd->action = action; + cmd->index = idx; + memcpy(cmd->mac, mac_addr, ETH_ALEN); + cmd->wildcard = 0; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_ACL_MAC_LIST_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_allow_aggr_cmd(struct wmi *wmi, u8 if_idx, + u16 tx_tid_mask, u16 rx_tid_mask) +{ + struct sk_buff *skb; + struct wmi_allow_aggr_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "allow_aggr: tx_tid %x rx_tid %x\n", + tx_tid_mask, + rx_tid_mask); + + cmd = (struct wmi_allow_aggr_cmd *)skb->data; + cmd->tx_allow_aggr = tx_tid_mask; + cmd->rx_allow_aggr = rx_tid_mask; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_ALLOW_AGGR_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_credit_bypass(struct wmi *wmi, u8 if_idx, u8 eid, + u8 restore, u16 threshold) +{ + struct sk_buff *skb; + struct wmi_set_credit_bypass_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "eid %d restore: %d, threshould: %d\n", + eid, restore, threshold); + + cmd = (struct wmi_set_credit_bypass_cmd *)skb->data; + cmd->eid = eid; + cmd->restore = restore; + cmd->threshold = threshold; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_CREDIT_BYPASS_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_arp_offload_ip_cmd(struct wmi *wmi, u8 *ip_addrs) +{ + struct sk_buff *skb; + struct wmi_set_arp_ns_offload_cmd *cmd; + int ret, i; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_arp_ns_offload_cmd *)skb->data; + memset(cmd, 0, sizeof(*cmd)); + + for (i = 0 ; i < 4; i++) { + /*mapping IP Address*/ + cmd->arp_tuples[0].target_ipaddr[i] = *(ip_addrs+i); + } + cmd->arp_tuples[0].flags = WMI_ARPOFF_FLAGS_VALID; + cmd->flags = 0; + + ret = ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_mcc_profile_cmd(struct wmi *wmi, u32 mcc_profile) +{ + struct sk_buff *skb; + struct wmi_set_mcc_profile_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_mcc_profile_cmd *)skb->data; + cmd->mcc_profile = mcc_profile; + + return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_MCC_PROFILE_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_seamless_mcc_scc_switch_freq_cmd(struct wmi *wmi, u32 freq) +{ + struct sk_buff *skb; + u32 *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (u32 *)skb->data; + *cmd = freq; + + return ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_SET_SEAMLESS_MCC_SCC_SWITCH_FREQ_CMDID, NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_inact_cmd(struct wmi *wmi, u32 inacperiod) +{ + struct sk_buff *skb; + struct wmi_ap_conn_inact_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ap_conn_inact_cmd *)skb->data; + cmd->period = inacperiod; + + return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_AP_CONN_INACT_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_send_assoc_resp_cmd(struct wmi *wmi, u8 if_idx, + bool accept, u8 reason_code, u8 fw_status, u8 *sta_mac, u8 req_type) +{ + struct sk_buff *skb; + struct wmi_assoc_resp_send_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "assoc_resp_send: accept %d code %d fw %d type %d\n", + accept, reason_code, fw_status, req_type); + + cmd = (struct wmi_assoc_resp_send_cmd *)skb->data; + if (accept) { + /* + * Though request is validated successfully by the host, + * association response will be sent with failure status + * if firmware validation fails. + */ + cmd->host_accept = 1; + cmd->host_reasonCode = 0; /* follow firmware's */ + } else { + cmd->host_accept = 0; + cmd->host_reasonCode = reason_code; + } + cmd->target_status = fw_status; + cmd->rspType = req_type; + memcpy(cmd->sta_mac_addr, sta_mac, ETH_ALEN); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SEND_ASSOC_RES_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_assoc_req_relay_cmd(struct wmi *wmi, u8 if_idx, bool enabled) +{ + struct sk_buff *skb; + struct wmi_assoc_req_relay_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "assoc_req_relay: %d\n", + enabled); + + cmd = (struct wmi_assoc_req_relay_cmd *)skb->data; + if (enabled) + cmd->enable = 1; + else + cmd->enable = 0; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_ASSOC_REQ_RELAY_CMDID, + NO_SYNC_WMIFLAG); +} +#ifdef ATHTST_SUPPORT +int ath6kl_wmi_set_ap_num_sta_cmd(struct wmi *wmi, u8 if_idx, u8 sta_nums) +{ + struct sk_buff *skb; + struct WMI_AP_NUM_STA_CMD *cmd; + + if (sta_nums > 10) + return -EIO; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct WMI_AP_NUM_STA_CMD *) skb->data; + cmd->num_sta = (u8)sta_nums; + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_SET_NUM_STA_CMDID, + NO_SYNC_WMIFLAG); +} +#endif + +int ath6kl_wmi_antdivstate_event_rx(struct ath6kl_vif *vif, u8 *datap, int len) +{ + memcpy(&vif->ant_div_stat, datap, sizeof(struct wmi_ant_div_stat)); + return 0; +} + diff --git a/drivers/net/wireless/ath/ath6kl-3.5/wmi.h b/drivers/net/wireless/ath/ath6kl-3.5/wmi.h new file mode 100644 index 000000000000..dc9651d7e857 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/wmi.h @@ -0,0 +1,3486 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 WMI protocol specified in the + * Wireless Module Interface (WMI). It includes definitions of all the + * commands and events. Commands are messages from the host to the WM. + * Events and Replies are messages from the WM to the host. + */ + +#ifndef WMI_H +#define WMI_H + +#include + +#include "htc.h" +#include "wlan_location_defs.h" +#define HTC_PROTOCOL_VERSION 0x0002 +#define WMI_PROTOCOL_VERSION 0x0002 +#define WMI_CONTROL_MSG_MAX_LEN 256 +#define is_ethertype(type_or_len) ((type_or_len) >= 0x0600) + +#define IP_ETHERTYPE 0x0800 + +#define WMI_IMPLICIT_PSTREAM 0xFF +#define WMI_MAX_THINSTREAM 15 + +#define SSID_IE_LEN_INDEX 13 + +/* Host side link management data structures */ +#define SIG_QUALITY_THRESH_LVLS 6 +#define SIG_QUALITY_UPPER_THRESH_LVLS SIG_QUALITY_THRESH_LVLS +#define SIG_QUALITY_LOWER_THRESH_LVLS SIG_QUALITY_THRESH_LVLS + +#define A_BAND_24GHZ 0 +#define A_BAND_5GHZ 1 +#define A_NUM_BANDS 2 + +/* in ms */ +#define WMI_IMPLICIT_PSTREAM_INACTIVITY_INT 5000 + +/* + * There are no signed versions of __le16 and __le32, so for a temporary + * solution come up with our own version. The idea is from fs/ntfs/types.h. + * + * Use a_ prefix so that it doesn't conflict if we get proper support to + * linux/types.h. + */ +typedef __s16 __bitwise a_sle16; +typedef __s32 __bitwise a_sle32; + +static inline a_sle32 a_cpu_to_sle32(s32 val) +{ + return (__force a_sle32) cpu_to_le32(val); +} + +static inline s32 a_sle32_to_cpu(a_sle32 val) +{ + return le32_to_cpu((__force __le32) val); +} + +static inline a_sle16 a_cpu_to_sle16(s16 val) +{ + return (__force a_sle16) cpu_to_le16(val); +} + +static inline s16 a_sle16_to_cpu(a_sle16 val) +{ + return le16_to_cpu((__force __le16) val); +} + +struct sq_threshold_params { + s16 upper_threshold[SIG_QUALITY_UPPER_THRESH_LVLS]; + s16 lower_threshold[SIG_QUALITY_LOWER_THRESH_LVLS]; + u32 upper_threshold_valid_count; + u32 lower_threshold_valid_count; + u32 polling_interval; + u8 weight; + u8 last_rssi; + u8 last_rssi_poll_event; +}; + +struct wmi_data_sync_bufs { + u8 traffic_class; + struct sk_buff *skb; +}; + +struct wmi_mgmt_tx_frame { + struct list_head list; + + struct ath6kl_vif *vif; + u8 *mgmt_tx_frame; + size_t mgmt_tx_frame_len; + int mgmt_tx_frame_idx; + u32 mgmt_tx_frame_freq; +#define WMI_TX_MGMT_RETRY_MAX (3) + int mgmt_tx_frame_retry; +}; + +/* WMM stream classes */ +#define WMM_NUM_AC 4 +#define WMM_AC_BE 0 /* best effort */ +#define WMM_AC_BK 1 /* background */ +#define WMM_AC_VI 2 /* video */ +#define WMM_AC_VO 3 /* voice */ + +#define WMI_VOICE_USER_PRIORITY 0x7 + +struct wmi { + u16 stream_exist_for_ac[WMM_NUM_AC]; + u8 fat_pipe_exist; + struct ath6kl *parent_dev; + u8 pwr_mode; + spinlock_t lock; + enum htc_endpoint_id ep_id; + struct sq_threshold_params + sq_threshld[SIGNAL_QUALITY_METRICS_NUM_MAX]; + bool is_wmm_enabled; + u8 traffic_class; + bool is_probe_ssid; + + struct list_head mgmt_tx_frame_list; +}; + +struct host_app_area { + __le32 wmi_protocol_ver; +} __packed; + +enum wmi_msg_type { + DATA_MSGTYPE = 0x0, + CNTL_MSGTYPE, + SYNC_MSGTYPE, + OPT_MSGTYPE, +}; + +/* + * Macros for operating on WMI_DATA_HDR (info) field + */ + +#define WMI_DATA_HDR_MSG_TYPE_MASK 0x03 +#define WMI_DATA_HDR_MSG_TYPE_SHIFT 0 +#define WMI_DATA_HDR_UP_MASK 0x07 +#define WMI_DATA_HDR_UP_SHIFT 2 + +/* In AP mode, the same bit (b5) is used to indicate Power save state in + * the Rx dir and More data bit state in the tx direction. + */ +#define WMI_DATA_HDR_PS_MASK 0x1 +#define WMI_DATA_HDR_PS_SHIFT 5 + +#define WMI_DATA_HDR_MORE_MASK 0x1 +#define WMI_DATA_HDR_MORE_SHIFT 5 +#define WMI_DATA_HDR_SET_MORE_BIT(h) \ + ((h)->info |= (WMI_DATA_HDR_MORE_MASK << WMI_DATA_HDR_MORE_SHIFT)) +#define WMI_DATA_HDR_HAS_MORE_BIT(h) \ + ((h)->info & (WMI_DATA_HDR_MORE_MASK << WMI_DATA_HDR_MORE_SHIFT)) + +enum wmi_data_hdr_data_type { + WMI_DATA_HDR_DATA_TYPE_802_3 = 0, + WMI_DATA_HDR_DATA_TYPE_802_11, + + /* used to be used for the PAL */ + WMI_DATA_HDR_DATA_TYPE_ACL, +}; + +/* Bitmap of data header flags */ +enum WMI_DATA_HDR_FLAGS { + WMI_DATA_HDR_FLAGS_MORE = 0x1, + WMI_DATA_HDR_FLAGS_EOSP = 0x2, + WMI_DATA_HDR_FLAGS_TRIGGERED = 0x4, + WMI_DATA_HDR_FLAGS_PSPOLLED = 0x8, +}; + +#define WMI_DATA_HDR_DATA_TYPE_MASK 0x3 +#define WMI_DATA_HDR_DATA_TYPE_SHIFT 6 + +/* Macros for operating on WMI_DATA_HDR (info2) field */ +#define WMI_DATA_HDR_SEQNO_MASK 0xFFF +#define WMI_DATA_HDR_SEQNO_SHIFT 0 + +#define WMI_DATA_HDR_AMSDU_MASK 0x1 +#define WMI_DATA_HDR_AMSDU_SHIFT 12 + +#define WMI_DATA_HDR_META_MASK 0x7 +#define WMI_DATA_HDR_META_SHIFT 13 + +#define WMI_DATA_HDR_PAD_BEFORE_DATA_MASK 0xFF +#define WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT 0x8 + +#define WMI_DATA_HDR_IF_IDX_MASK 0xF + +/* FIXME : endian? */ +#define WMI_DATA_HDR_TRIGGER_MASK 0x1 +#define WMI_DATA_HDR_TRIGGER_SHIFT 4 +#define WMI_DATA_HDR_SET_TRIGGER(h, _v) \ + ((h)->info3 = ((h)->info3 & ~(WMI_DATA_HDR_TRIGGER_MASK << \ + WMI_DATA_HDR_TRIGGER_SHIFT)) \ + | ((_v) << WMI_DATA_HDR_TRIGGER_SHIFT)) +#define WMI_DATA_HDR_IS_TRIGGER(h) \ + (((le16_to_cpu((h)->info3) >> WMI_DATA_HDR_TRIGGER_SHIFT) & \ + WMI_DATA_HDR_TRIGGER_MASK) == WMI_DATA_HDR_TRIGGER_MASK) + +#define WMI_DATA_HDR_EOSP_MASK WMI_DATA_HDR_TRIGGER_MASK +#define WMI_DATA_HDR_EOSP_SHIFT WMI_DATA_HDR_TRIGGER_SHIFT +#define WMI_DATA_HDR_SET_EOSP_BIT(h) \ + ((h)->info3 |= (WMI_DATA_HDR_EOSP_MASK << WMI_DATA_HDR_EOSP_SHIFT)) +#define WMI_DATA_HDR_HAS_EOSP_BIT(h) \ + ((h)->info3 & (WMI_DATA_HDR_EOSP_MASK << WMI_DATA_HDR_EOSP_SHIFT)) + +#define WMI_DATA_HDR_TRIGGERED_MASK 0x1 +#define WMI_DATA_HDR_TRIGGERED_SHIFT 6 +#define WMI_DATA_HDR_SET_TRIGGERED_BIT(h) ((h)->info3 |= \ + (WMI_DATA_HDR_TRIGGERED_MASK << WMI_DATA_HDR_TRIGGERED_SHIFT)) +#define WMI_DATA_HDR_HAS_TRIGGERED_BIT(h) ((h)->info3 & \ + (WMI_DATA_HDR_TRIGGERED_MASK << WMI_DATA_HDR_TRIGGERED_SHIFT)) + +#define WMI_DATA_HDR_PSPOLLED_MASK 0x1 +#define WMI_DATA_HDR_PSPOLLED_SHIFT 7 +#define WMI_DATA_HDR_SET_PSPOLLED_BIT(h) ((h)->info3 |= \ + (WMI_DATA_HDR_PSPOLLED_MASK << WMI_DATA_HDR_PSPOLLED_SHIFT)) +#define WMI_DATA_HDR_HAS_PSPOLLED_BIT(h) ((h)->info3 & \ + (WMI_DATA_HDR_PSPOLLED_MASK << WMI_DATA_HDR_PSPOLLED_SHIFT)) + +struct wmi_data_hdr { + s8 rssi; + + /* + * usage of 'info' field(8-bit): + * + * b1:b0 - WMI_MSG_TYPE + * b4:b3:b2 - UP(tid) + * b5 - Used in AP mode. + * More-data in tx dir, PS in rx. + * b7:b6 - Dot3 header(0), + * Dot11 Header(1), + * ACL data(2) + */ + u8 info; + + /* + * usage of 'info2' field(16-bit): + * + * b11:b0 - seq_no + * b12 - A-MSDU? + * b15:b13 - META_DATA_VERSION 0 - 7 + */ + __le16 info2; + + /* + * usage of info3, 16-bit: + * b3:b0 - Interface index + * b15:b4 - Reserved + */ + __le16 info3; +} __packed; + +static inline u8 wmi_data_hdr_get_up(struct wmi_data_hdr *dhdr) +{ + return (dhdr->info >> WMI_DATA_HDR_UP_SHIFT) & WMI_DATA_HDR_UP_MASK; +} + +static inline void wmi_data_hdr_set_up(struct wmi_data_hdr *dhdr, + u8 usr_pri) +{ + dhdr->info &= ~(WMI_DATA_HDR_UP_MASK << WMI_DATA_HDR_UP_SHIFT); + dhdr->info |= usr_pri << WMI_DATA_HDR_UP_SHIFT; +} + +static inline u8 wmi_data_hdr_get_dot11(struct wmi_data_hdr *dhdr) +{ + u8 data_type; + + data_type = (dhdr->info >> WMI_DATA_HDR_DATA_TYPE_SHIFT) & + WMI_DATA_HDR_DATA_TYPE_MASK; + return (data_type == WMI_DATA_HDR_DATA_TYPE_802_11); +} + +static inline u16 wmi_data_hdr_get_seqno(struct wmi_data_hdr *dhdr) +{ + return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_SEQNO_SHIFT) & + WMI_DATA_HDR_SEQNO_MASK; +} + +static inline u8 wmi_data_hdr_is_amsdu(struct wmi_data_hdr *dhdr) +{ + return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_AMSDU_SHIFT) & + WMI_DATA_HDR_AMSDU_MASK; +} + +static inline u8 wmi_data_hdr_get_meta(struct wmi_data_hdr *dhdr) +{ + return (le16_to_cpu(dhdr->info2) >> WMI_DATA_HDR_META_SHIFT) & + WMI_DATA_HDR_META_MASK; +} + +static inline u8 wmi_data_hdr_get_if_idx(struct wmi_data_hdr *dhdr) +{ + return le16_to_cpu(dhdr->info3) & WMI_DATA_HDR_IF_IDX_MASK; +} + +/* Tx meta version definitions */ +#define WMI_MAX_TX_META_SZ 12 +#define WMI_META_VERSION_1 0x01 +#define WMI_META_VERSION_2 0x02 + +struct wmi_tx_meta_v1 { + /* packet ID to identify the tx request */ + u8 pkt_id; + + /* rate policy to be used for the tx of this frame */ + u8 rate_plcy_id; +} __packed; + +struct wmi_tx_meta_v2 { + /* + * Offset from start of the WMI header for csum calculation to + * begin. + */ + u8 csum_start; + + /* offset from start of WMI header where final csum goes */ + u8 csum_dest; + + /* no of bytes over which csum is calculated */ + u8 csum_flags; +} __packed; + +struct wmi_rx_meta_v1 { + u8 status; + + /* rate index mapped to rate at which this packet was received. */ + u8 rix; + + /* rssi of packet */ + u8 rssi; + + /* rf channel during packet reception */ + u8 channel; + + __le16 flags; +} __packed; + +struct wmi_rx_meta_v2 { + __le16 csum; + + /* bit 0 set -partial csum valid bit 1 set -test mode */ + u8 csum_flags; +} __packed; + +#define WMI_CMD_HDR_IF_ID_MASK 0xF + +/* Control Path */ +struct wmi_cmd_hdr { + __le16 cmd_id; + + /* info1 - 16 bits + * b03:b00 - id + * b15:b04 - unused */ + __le16 info1; + + /* for alignment */ + __le16 reserved; +} __packed; + +static inline u8 wmi_cmd_hdr_get_if_idx(struct wmi_cmd_hdr *chdr) +{ + return le16_to_cpu(chdr->info1) & WMI_CMD_HDR_IF_ID_MASK; +} + +/* List of WMI commands */ +enum wmi_cmd_id { + WMI_CONNECT_CMDID = 0x0001, + WMI_RECONNECT_CMDID, + WMI_DISCONNECT_CMDID, + WMI_SYNCHRONIZE_CMDID, + WMI_CREATE_PSTREAM_CMDID, + WMI_DELETE_PSTREAM_CMDID, + WMI_START_SCAN_CMDID, + WMI_SET_SCAN_PARAMS_CMDID, + WMI_SET_BSS_FILTER_CMDID, + WMI_SET_PROBED_SSID_CMDID, /* 10 */ + WMI_SET_LISTEN_INT_CMDID, + WMI_SET_BMISS_TIME_CMDID, + WMI_SET_DISC_TIMEOUT_CMDID, + WMI_GET_CHANNEL_LIST_CMDID, + WMI_SET_BEACON_INT_CMDID, + WMI_GET_STATISTICS_CMDID, + WMI_SET_CHANNEL_PARAMS_CMDID, + WMI_SET_POWER_MODE_CMDID, + WMI_SET_IBSS_PM_CAPS_CMDID, + WMI_SET_POWER_PARAMS_CMDID, /* 20 */ + WMI_SET_POWERSAVE_TIMERS_POLICY_CMDID, + WMI_ADD_CIPHER_KEY_CMDID, + WMI_DELETE_CIPHER_KEY_CMDID, + WMI_ADD_KRK_CMDID, + WMI_DELETE_KRK_CMDID, + WMI_SET_PMKID_CMDID, + WMI_SET_TX_PWR_CMDID, + WMI_GET_TX_PWR_CMDID, + WMI_SET_ASSOC_INFO_CMDID, + WMI_ADD_BAD_AP_CMDID, /* 30 */ + WMI_DELETE_BAD_AP_CMDID, + WMI_SET_TKIP_COUNTERMEASURES_CMDID, + WMI_RSSI_THRESHOLD_PARAMS_CMDID, + WMI_TARGET_ERROR_REPORT_BITMASK_CMDID, + WMI_SET_ACCESS_PARAMS_CMDID, + WMI_SET_RETRY_LIMITS_CMDID, + WMI_SET_OPT_MODE_CMDID, + WMI_OPT_TX_FRAME_CMDID, + WMI_SET_VOICE_PKT_SIZE_CMDID, + WMI_SET_MAX_SP_LEN_CMDID, /* 40 */ + WMI_SET_ROAM_CTRL_CMDID, + WMI_GET_ROAM_TBL_CMDID, + WMI_GET_ROAM_DATA_CMDID, + WMI_ENABLE_RM_CMDID, + WMI_SET_MAX_OFFHOME_DURATION_CMDID, + WMI_EXTENSION_CMDID, /* Non-wireless extensions */ + WMI_SNR_THRESHOLD_PARAMS_CMDID, + WMI_LQ_THRESHOLD_PARAMS_CMDID, + WMI_SET_LPREAMBLE_CMDID, + WMI_SET_RTS_CMDID, /* 50 */ + WMI_CLR_RSSI_SNR_CMDID, + WMI_SET_FIXRATES_CMDID, + WMI_GET_FIXRATES_CMDID, + WMI_SET_AUTH_MODE_CMDID, + WMI_SET_REASSOC_MODE_CMDID, + WMI_SET_WMM_CMDID, + WMI_SET_WMM_TXOP_CMDID, + WMI_TEST_CMDID, + /* COEX AR6002 only */ + WMI_SET_BT_STATUS_CMDID, + WMI_SET_BT_PARAMS_CMDID, /* 60 */ + + WMI_SET_KEEPALIVE_CMDID, + WMI_GET_KEEPALIVE_CMDID, + WMI_SET_APPIE_CMDID, + WMI_GET_APPIE_CMDID, + WMI_SET_WSC_STATUS_CMDID, + + /* Wake on Wireless */ + WMI_SET_HOST_SLEEP_MODE_CMDID, + WMI_SET_WOW_MODE_CMDID, + WMI_GET_WOW_LIST_CMDID, + WMI_ADD_WOW_PATTERN_CMDID, + WMI_DEL_WOW_PATTERN_CMDID, /* 70 */ + + WMI_SET_FRAMERATES_CMDID, + WMI_SET_AP_PS_CMDID, + WMI_SET_QOS_SUPP_CMDID, + /* WMI_THIN_RESERVED_... mark the start and end + * values for WMI_THIN_RESERVED command IDs. These + * command IDs can be found in wmi_thin.h */ + WMI_THIN_RESERVED_START = 0x8000, + WMI_THIN_RESERVED_END = 0x8fff, + /* + * Developer commands starts at 0xF000 + */ + WMI_SET_BITRATE_CMDID = 0xF000, + WMI_GET_BITRATE_CMDID, + WMI_SET_WHALPARAM_CMDID, + + + /*Should add the new command to the tail for compatible with + * etna. + */ + WMI_SET_MAC_ADDRESS_CMDID, + WMI_SET_AKMP_PARAMS_CMDID, + WMI_SET_PMKID_LIST_CMDID, + WMI_GET_PMKID_LIST_CMDID, + WMI_ABORT_SCAN_CMDID, + WMI_SET_TARGET_EVENT_REPORT_CMDID, + + /* Unused */ + WMI_UNUSED1, + WMI_UNUSED2, + + /* + * AP mode commands + */ + WMI_AP_HIDDEN_SSID_CMDID, /* F00B */ + WMI_AP_SET_NUM_STA_CMDID, + WMI_AP_ACL_POLICY_CMDID, + WMI_AP_ACL_MAC_LIST_CMDID, + WMI_AP_CONFIG_COMMIT_CMDID, + WMI_AP_SET_MLME_CMDID, /* F010 */ + WMI_AP_SET_PVB_CMDID, + WMI_AP_CONN_INACT_CMDID, + WMI_AP_PROT_SCAN_TIME_CMDID, + WMI_AP_SET_COUNTRY_CMDID, + WMI_AP_SET_DTIM_CMDID, + WMI_AP_MODE_STAT_CMDID, + + WMI_SET_IP_CMDID, /* F017 */ + WMI_SET_PARAMS_CMDID, + WMI_SET_MCAST_FILTER_CMDID, + WMI_DEL_MCAST_FILTER_CMDID, + + WMI_ALLOW_AGGR_CMDID, /* F01B */ + WMI_ADDBA_REQ_CMDID, + WMI_DELBA_REQ_CMDID, + WMI_SET_HT_CAP_CMDID, + WMI_SET_HT_OP_CMDID, + WMI_SET_TX_SELECT_RATES_CMDID, + WMI_SET_TX_SGI_PARAM_CMDID, + WMI_SET_RATE_POLICY_CMDID, + + WMI_HCI_CMD_CMDID, /* F023 */ + WMI_RX_FRAME_FORMAT_CMDID, + WMI_SET_THIN_MODE_CMDID, + WMI_SET_BT_WLAN_CONN_PRECEDENCE_CMDID, + + WMI_AP_SET_11BG_RATESET_CMDID, /* F027 */ + WMI_SET_PMK_CMDID, + WMI_MCAST_FILTER_CMDID, + /* COEX CMDID AR6003 */ + WMI_SET_BTCOEX_FE_ANT_CMDID, /* F02A */ + WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID, + WMI_SET_BTCOEX_SCO_CONFIG_CMDID, + WMI_SET_BTCOEX_A2DP_CONFIG_CMDID, + WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMDID, + WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMDID, + WMI_SET_BTCOEX_DEBUG_CMDID, + WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID, + WMI_GET_BTCOEX_STATS_CMDID, + WMI_GET_BTCOEX_CONFIG_CMDID, + + WMI_DFS_RESERVED, /* F034 */ + WMI_SET_DFS_MINRSSITHRESH_CMDID, + WMI_SET_DFS_MAXPULSEDUR_CMDID, + WMI_DFS_RADAR_DETECTED_CMDID, + + /* P2P CMDS */ + WMI_P2P_SET_CONFIG_CMDID, /* F037 */ + WMI_WPS_SET_CONFIG_CMDID, + WMI_SET_REQ_DEV_ATTR_CMDID, + WMI_P2P_FIND_CMDID, + WMI_P2P_STOP_FIND_CMDID, + WMI_P2P_GO_NEG_START_CMDID, + WMI_P2P_LISTEN_CMDID, + + + WMI_CONFIG_TX_MAC_RULES_CMDID, /* F044 */ + WMI_SET_PROMISCUOUS_MODE_CMDID, + WMI_RX_FRAME_FILTER_CMDID, + WMI_SET_CHANNEL_CMDID, + + /* WAC commands */ + WMI_ENABLE_WAC_CMDID, + WMI_WAC_SCAN_REPLY_CMDID, + WMI_WAC_CTRL_REQ_CMDID, + + WMI_SET_DIV_PARAMS_CMDID, + WMI_GET_PMK_CMDID, + WMI_SET_PASSPHRASE_CMDID, + WMI_SEND_ASSOC_RES_CMDID, + WMI_SET_ASSOC_REQ_RELAY_CMDID, + + /* ACS command, consists of sub-commands */ + WMI_ACS_CTRL_CMDID, + WMI_SET_EXCESS_TX_RETRY_THRES_CMDID, + WMI_SET_TBD_TIME_CMDID, /*added for wmiconfig command for TBD */ + + /* Pktlog cmds */ + WMI_PKTLOG_ENABLE_CMDID, + WMI_PKTLOG_DISABLE_CMDID, + + /* More P2P Cmds */ + WMI_P2P_GO_NEG_REQ_RSP_CMDID, + WMI_P2P_GRP_INIT_CMDID, + WMI_P2P_GRP_FORMATION_DONE_CMDID, + WMI_P2P_INVITE_CMDID, + WMI_P2P_INVITE_REQ_RSP_CMDID, + WMI_P2P_PROV_DISC_REQ_CMDID, + WMI_P2P_SET_CMDID, + + WMI_GET_RFKILL_MODE_CMDID, + WMI_SET_RFKILL_MODE_CMDID, + WMI_AP_SET_APSD_CMDID, + WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID, + + WMI_P2P_SDPD_TX_CMDID, /* F05C */ + WMI_P2P_STOP_SDPD_CMDID, + WMI_P2P_CANCEL_CMDID, + /* Ultra low power store / recall commands */ + WMI_STORERECALL_CONFIGURE_CMDID, + WMI_STORERECALL_RECALL_CMDID, + WMI_STORERECALL_HOST_READY_CMDID, + WMI_FORCE_TARGET_ASSERT_CMDID, + + WMI_SET_PROBED_SSID_EX_CMDID, + WMI_SET_NETWORK_LIST_OFFLOAD_CMDID, + WMI_SET_ARP_NS_OFFLOAD_CMDID, + WMI_ADD_WOW_EXT_PATTERN_CMDID, + WMI_GTK_OFFLOAD_OP_CMDID, + + WMI_REMAIN_ON_CHNL_CMDID, + WMI_CANCEL_REMAIN_ON_CHNL_CMDID, + WMI_SEND_ACTION_CMDID, + WMI_PROBE_REQ_REPORT_CMDID, + WMI_DISABLE_11B_RATES_CMDID, + WMI_SEND_PROBE_RESPONSE_CMDID, + WMI_GET_P2P_INFO_CMDID, + WMI_AP_JOIN_BSS_CMDID, + + WMI_SMPS_ENABLE_CMDID, + WMI_SMPS_CONFIG_CMDID, + WMI_SET_RATECTRL_PARM_CMDID, + /* LPL specific commands*/ + WMI_LPL_FORCE_ENABLE_CMDID, + WMI_LPL_SET_POLICY_CMDID, + WMI_LPL_GET_POLICY_CMDID, + WMI_LPL_GET_HWSTATE_CMDID, + WMI_LPL_SET_PARAMS_CMDID, + WMI_LPL_GET_PARAMS_CMDID, + + WMI_SET_BUNDLE_PARAM_CMDID, + + /*GreenTx specific commands*/ + WMI_GREENTX_PARAMS_CMDID, + + /* WPS Commands */ + WMI_WPS_START_CMDID, + WMI_GET_WPS_STATUS_CMDID, + + /* More P2P commands */ + WMI_SET_NOA_CMDID, + WMI_GET_NOA_CMDID, + WMI_SET_OPPPS_CMDID, + WMI_GET_OPPPS_CMDID, + WMI_ADD_PORT_CMDID, + WMI_DEL_PORT_CMDID, + + /* 802.11w cmd */ + WMI_SET_RSN_CAP_CMDID, + WMI_GET_RSN_CAP_CMDID, + WMI_SET_IGTK_CMDID, + + WMI_RX_FILTER_COALESCE_FILTER_OP_CMDID, + WMI_RX_FILTER_SET_FRAME_TEST_LIST_CMDID, + + WMI_RTT_MEASREQ_CMDID, + WMI_RTT_CAPREQ_CMDID, + WMI_RTT_STATUSREQ_CMDID, + + /*led cotrol*/ + WMI_ENABLE_LED_CMDID, + WMI_CONFIG_LED_CMDID, + WMI_SET_LED_CMDID, + /*Socket translation commands*/ + WMI_SOCKET_CMDID, + WMI_P2P_PSIE_CONFIG_CMDID, + WMI_LOG_FRAME_CMDID, + WMI_QUERY_PHY_INFO_CMDID, + /* P2P FW Offload support */ + WMI_P2P_CONNECT_CMDID, + WMI_P2P_GET_NODE_LIST_CMDID, + WMI_P2P_AUTH_GO_NEG_CMDID, + WMI_P2P_FW_PROV_DISC_REQ_CMDID, + + WMI_SET_DFS_ENABLE_CMDID, + + WMI_CCX_FRAME_REPORT_CMDID, /* CCXv4 */ + WMI_CCX_HOST_FEATURE_CONFIG_CMDID, + WMI_DIAGNOSTIC_CMDID, /* diagnostic */ + WMI_SET_FILTERED_PROMISCUOUS_MODE_CMDID, + + /*added btcoex command*/ + WMI_SET_BTCOEX_HID_CONFIG_CMDID, + WMI_RTT_CONFIG_CMDID, + WMI_STA_BMISS_ENHANCE_CMDID, + WMI_P2P_PERSISTENT_PROFILE_CMDID, + WMI_P2P_SET_JOIN_PROFILE_CMDID, + + /* wifi heart beat */ + WMI_HEART_PARAMS_CMDID, + WMI_HEART_SET_TCP_PARAMS_CMDID, + WMI_HEART_SET_TCP_PKT_FILTER_CMDID, + WMI_HEART_SET_UDP_PARAMS_CMDID, + WMI_HEART_SET_UDP_PKT_FILTER_CMDID, + WMI_HEART_SET_NETWORK_INFO_CMDID, + + /* wifi discovery */ + WMI_DISC_SET_IE_FILTER_CMDID, + WMI_DISC_SET_MODE_CMDID, + + WMI_RTT_CLKCALINFO_CMDID, + + WMI_P2P_SET_PROFILE_CMDID, + + /* P2P FW GO PS Command */ + WMI_P2P_FW_SET_NOA_CMDID, + WMI_P2P_FW_GET_NOA_CMDID, /* F0AA */ + WMI_P2P_FW_SET_OPPPS_CMDID, + WMI_P2P_FW_GET_OPPPS_CMDID, + + /*led cotrol*/ + WMI_ENABLE_BLINKING_LED_CMDID, + + WMI_AP_POLL_STA_CMDID, + WMI_AP_PSBUF_OFFLOAD_CMDID, + WMI_SET_REGDOMAIN_CMDID, +/* merge from olca mainline for align command id - start */ + WMI_ARGOS_CMDID, + WMI_SEND_MGMT_CMDID, + WMI_BEGIN_SCAN_CMDID, + WMI_SET_IE_CMDID, + WMI_SET_RSSI_FILTER_CMDID, + WMI_SET_CREDIT_REVERSE_CMDID = 0xF0B6, + WMI_SET_RCV_DATA_CLASSIFIER_CMDID, + WMI_AP_SET_IDLE_CLOSE_TIME_CMDID, + WMI_SET_LTE_COEX_STATE_CMDID, + WMI_SET_MCC_PROFILE_CMDID, + WMI_SET_MEDIA_STREAM_CMDID = 0xF0BB, + + /* More SB private commands */ + WMI_SET_CUSTOM_REG, /* F0BC */ + WMI_GET_CUSTOM_REG, + WMI_GET_CUSTOM_PRODUCT_INFO, + WMI_SET_CUSTOM_TESTMODE, + WMI_GET_CUSTOM_TESTMODE, /* F0C0 */ + WMI_GET_CUSTOM_STAINFO, + WMI_GET_CUSTOM_SCANTIME, + WMI_SET_CUSTOM_SCAN, + WMI_GET_CUSTOM_SCAN, + WMI_GET_CUSTOM_VERSION_INFO, + WMI_GET_CUSTOM_WIFI_TXPOW, + WMI_GET_CUSTOM_ATHSTATS, + + WMI_TX99TOOL_CMDID,/* F0C8 */ + WMI_SET_CUSTOM_PROBE_RESP_REPORT_CMDID, + WMI_SET_CUSTOM_WIDI, + WMI_GET_CUSTOM_WIDI, + + /*Diversity control*/ + WMI_SET_ANTDIVCFG_CMDID, /* F0CC */ + WMI_GET_ANTDIVSTAT_CMDID, + + WMI_SET_SEAMLESS_MCC_SCC_SWITCH_FREQ_CMDID, +/* merge from olca mainline for align command id - end */ + + WMI_SET_CREDIT_BYPASS_CMDID, +}; + +enum wmi_mgmt_frame_type { + WMI_FRAME_BEACON = 0, + WMI_FRAME_PROBE_REQ, + WMI_FRAME_PROBE_RESP, + WMI_FRAME_ASSOC_REQ, + WMI_FRAME_ASSOC_RESP, + WMI_NUM_MGMT_FRAME +}; + +/* WMI_CONNECT_CMDID */ +enum network_type { + INFRA_NETWORK = 0x01, + ADHOC_NETWORK = 0x02, + ADHOC_CREATOR = 0x04, + AP_NETWORK = 0x10, +}; + +enum dot11_auth_mode { + OPEN_AUTH = 0x01, + SHARED_AUTH = 0x02, + + /* different from IEEE_AUTH_MODE definitions */ + LEAP_AUTH = 0x04, +}; + +enum auth_mode { + NONE_AUTH = 0x01, + WPA_AUTH = 0x02, + WPA2_AUTH = 0x04, + WPA_PSK_AUTH = 0x08, + WPA2_PSK_AUTH = 0x10, + WPA_AUTH_CCKM = 0x20, + WPA2_AUTH_CCKM = 0x40, +#ifdef PMF_SUPPORT + WPA2_PSK_SHA256_AUTH = 0x80, +#endif +}; + +#define WMI_MIN_KEY_INDEX 0 +#define WMI_MAX_KEY_INDEX 3 + +#define WMI_MAX_KEY_LEN 32 + +#ifdef PMF_SUPPORT +#define WMI_MIN_IGTK_INDEX 4 +#define WMI_MAX_IGTK_INDEX 5 +#define WMI_IGTK_KEY_LEN 16 +#endif + +/* + * NB: these values are ordered carefully; there are lots of + * of implications in any reordering. In particular beware + * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY. + */ +#define ATH6KL_CIPHER_WEP 0 +#define ATH6KL_CIPHER_TKIP 1 +#define ATH6KL_CIPHER_AES_OCB 2 +#define ATH6KL_CIPHER_AES_CCM 3 +#define ATH6KL_CIPHER_CKIP 5 +#define ATH6KL_CIPHER_CCKM_KRK 6 +#define ATH6KL_CIPHER_NONE 7 /* pseudo value */ + +/* + * 802.11 rate set. + */ +#define ATH6KL_RATE_MAXSIZE 15 /* max rates we'll handle */ + +#define ATH_OUI_TYPE 0x01 +#define WPA_OUI_TYPE 0x01 +#define WMM_PARAM_OUI_SUBTYPE 0x01 +#define WMM_OUI_TYPE 0x02 +#define WSC_OUT_TYPE 0x04 + +enum wmi_connect_ctrl_flags_bits { + CONNECT_ASSOC_POLICY_USER = 0x0001, + CONNECT_SEND_REASSOC = 0x0002, + CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, + CONNECT_PROFILE_MATCH_DONE = 0x0008, + CONNECT_IGNORE_AAC_BEACON = 0x0010, + CONNECT_CSA_FOLLOW_BSS = 0x0020, + CONNECT_DO_WPA_OFFLOAD = 0x0040, + CONNECT_DO_NOT_DEAUTH = 0x0080, + CONNECT_WPS_FLAG = 0x0100, + /* AP configuration flags */ + AP_NO_DISASSOC_UPON_DEAUTH = 0x10000, + AP_HOSTPAL_SUPPORT = 0x20000, +}; + +#define WMI_CONNECT_AP_CHAN_SELECT_OFFSET (14) +#define WMI_CONNECT_AP_CHAN_SELECT_MASK (0xc000) + +enum wmi_connect_ap_channel_type { + AP_CHANNEL_TYPE_NONE = 0, + AP_CHANNEL_TYPE_HT40PLUS, + AP_CHANNEL_TYPE_HT40MINUS, + AP_CHANNEL_TYPE_HT20 +}; + +struct wmi_connect_cmd { + u8 nw_type; + u8 dot11_auth_mode; + u8 auth_mode; + u8 prwise_crypto_type; + u8 prwise_crypto_len; + u8 grp_crypto_type; + u8 grp_crypto_len; + u8 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + __le16 ch; + u8 bssid[ETH_ALEN]; + __le32 ctrl_flags; +} __packed; + +/* WMI_RECONNECT_CMDID */ +struct wmi_reconnect_cmd { + /* channel hint */ + __le16 channel; + + /* mandatory if set */ + u8 bssid[ETH_ALEN]; +} __packed; + +/* WMI_ADD_CIPHER_KEY_CMDID */ +enum key_usage { + PAIRWISE_USAGE = 0x00, + GROUP_USAGE = 0x01, + + /* default Tx Key - static WEP only */ + TX_USAGE = 0x02, +}; + +/* + * Bit Flag + * Bit 0 - Initialise TSC - default is Initialize + */ +#define KEY_OP_INIT_TSC 0x01 +#define KEY_OP_INIT_RSC 0x02 + +/* default initialise the TSC & RSC */ +#define KEY_OP_INIT_VAL 0x03 +#define KEY_OP_VALID_MASK 0x03 + +struct wmi_add_cipher_key_cmd { + u8 key_index; + u8 key_type; + + /* enum key_usage */ + u8 key_usage; + + u8 key_len; + + /* key replay sequence counter */ + u8 key_rsc[8]; + + u8 key[WLAN_MAX_KEY_LEN]; + + /* additional key control info */ + u8 key_op_ctrl; + + u8 key_mac_addr[ETH_ALEN]; +} __packed; + +/* WMI_DELETE_CIPHER_KEY_CMDID */ +struct wmi_delete_cipher_key_cmd { + u8 key_index; +} __packed; + +#define WMI_KRK_LEN 16 + +/* WMI_ADD_KRK_CMDID */ +struct wmi_add_krk_cmd { + u8 krk[WMI_KRK_LEN]; +} __packed; + +#ifdef PMF_SUPPORT +struct wmi_add_igtk_cmd{ + u8 key_index; + u8 key_len; + u8 key_rsc[6]; + u8 key[WMI_IGTK_KEY_LEN]; +} __packed; +#endif + +/* WMI_SETPMKID_CMDID */ + +#define WMI_PMKID_LEN 16 + +enum pmkid_enable_flg { + PMKID_DISABLE = 0, + PMKID_ENABLE = 1, +}; + +struct wmi_setpmkid_cmd { + u8 bssid[ETH_ALEN]; + + /* enum pmkid_enable_flg */ + u8 enable; + + u8 pmkid[WMI_PMKID_LEN]; +} __packed; + +/* WMI_START_SCAN_CMD */ +enum wmi_scan_type { + WMI_LONG_SCAN = 0, + WMI_SHORT_SCAN = 1, +}; + +struct wmi_start_scan_cmd { + __le32 force_fg_scan; + + /* for legacy cisco AP compatibility */ + __le32 is_legacy; + + /* max duration in the home channel(msec) */ + __le32 home_dwell_time; + + /* time interval between scans (msec) */ + __le32 force_scan_intvl; + + /* enum wmi_scan_type */ + u8 scan_type; + + /* how many channels follow */ + u8 num_ch; + + /* channels in Mhz */ + __le16 ch_list[1]; +} __packed; + +/* + * Warning: scan control flag value of 0xFF is used to disable + * all flags in WMI_SCAN_PARAMS_CMD. Do not add any more + * flags here + */ +enum wmi_scan_ctrl_flags_bits { + + /* set if can scan in the connect cmd */ + CONNECT_SCAN_CTRL_FLAGS = 0x01, + + /* set if scan for the SSID it is already connected to */ + SCAN_CONNECTED_CTRL_FLAGS = 0x02, + + /* set if enable active scan */ + ACTIVE_SCAN_CTRL_FLAGS = 0x04, + + /* set if enable roam scan when bmiss and lowrssi */ + ROAM_SCAN_CTRL_FLAGS = 0x08, + + /* set if follows customer BSSINFO reporting rule */ + REPORT_BSSINFO_CTRL_FLAGS = 0x10, + + /* if disabled, target doesn't scan after a disconnect event */ + ENABLE_AUTO_CTRL_FLAGS = 0x20, + + /* + * Scan complete event with canceled status will be generated when + * a scan is prempted before it gets completed. + */ + ENABLE_SCAN_ABORT_EVENT = 0x40 +}; + +struct wmi_scan_params_cmd { + /* sec */ + __le16 fg_start_period; + + /* sec */ + __le16 fg_end_period; + + /* sec */ + __le16 bg_period; + + /* msec */ + __le16 maxact_chdwell_time; + + /* msec */ + __le16 pas_chdwell_time; + + /* how many shorts scan for one long */ + u8 short_scan_ratio; + + u8 scan_ctrl_flags; + + /* msec */ + __le16 minact_chdwell_time; + + /* max active scans per ssid */ + __le16 maxact_scan_per_ssid; + + /* msecs */ + __le32 max_dfsch_act_time; +} __packed; + +/* WMI_SET_BSS_FILTER_CMDID */ +enum wmi_bss_filter { + /* no beacons forwarded */ + NONE_BSS_FILTER = 0x0, + + /* all beacons forwarded */ + ALL_BSS_FILTER, + + /* only beacons matching profile */ + PROFILE_FILTER, + + /* all but beacons matching profile */ + ALL_BUT_PROFILE_FILTER, + + /* only beacons matching current BSS */ + CURRENT_BSS_FILTER, + + /* all but beacons matching BSS */ + ALL_BUT_BSS_FILTER, + + /* beacons matching probed ssid */ + PROBED_SSID_FILTER, + + /* marker only */ + LAST_BSS_FILTER, +}; + +struct wmi_bss_filter_cmd { + /* see, enum wmi_bss_filter */ + u8 bss_filter; + + /* for alignment */ + u8 reserved1; + + /* for alignment */ + __le16 reserved2; + + __le32 ie_mask; +} __packed; + +/* WMI_SET_PROBED_SSID_CMDID */ +#define MAX_PROBED_SSID_INDEX 9 + +enum wmi_ssid_flag { + /* disables entry */ + DISABLE_SSID_FLAG = 0, + + /* probes specified ssid */ + SPECIFIC_SSID_FLAG = 0x01, + + /* probes for any ssid */ + ANY_SSID_FLAG = 0x02, +}; + +struct wmi_probed_ssid_cmd { + /* 0 to MAX_PROBED_SSID_INDEX */ + u8 entry_index; + + /* see, enum wmi_ssid_flg */ + u8 flag; + + u8 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_LISTEN_INT_CMDID + * The Listen interval is between 15 and 3000 TUs + */ +struct wmi_listen_int_cmd { + __le16 listen_intvl; + __le16 num_beacons; +} __packed; + +struct wmi_set_regdomain_cmd { + u8 length; + u8 iso_name[2]; +} __packed; + +/* WMI_SET_POWER_MODE_CMDID */ +enum wmi_power_mode { + REC_POWER = 0x01, + MAX_PERF_POWER, +}; + +struct wmi_power_mode_cmd { + /* see, enum wmi_power_mode */ + u8 pwr_mode; +} __packed; + +/* + * Policy to determnine whether power save failure event should be sent to + * host during scanning + */ +enum power_save_fail_event_policy { + SEND_POWER_SAVE_FAIL_EVENT_ALWAYS = 1, + IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN = 2, +}; + +struct wmi_power_params_cmd { + /* msec */ + __le16 idle_period; + + __le16 pspoll_number; + __le16 dtim_policy; + __le16 tx_wakeup_policy; + __le16 num_tx_to_wakeup; + __le16 ps_fail_event_policy; +} __packed; + +/* Adhoc power save types */ +enum wmi_adhoc_ps_type { + ADHOC_PS_DISABLE = 1, + ADHOC_PS_ATH = 2, + ADHOC_PS_IEEE = 3, + ADHOC_PS_OTHER = 4, + ADHOC_PS_KTK = 5, +}; + +struct wmi_ibss_pm_caps_cmd { + /* see, enum wmi_adhoc_ps_type */ + u8 power_saving; + + /* number of beacon periods */ + u8 ttl; + + /* msec */ + __le16 atim_windows; + + /* msec */ + __le16 timeout_value; +} __packed; + +/* WMI_SET_DISC_TIMEOUT_CMDID */ +struct wmi_disc_timeout_cmd { + /* seconds */ + u8 discon_timeout; +} __packed; + +enum dir_type { + UPLINK_TRAFFIC = 0, + DNLINK_TRAFFIC = 1, + BIDIR_TRAFFIC = 2, +}; + +enum voiceps_cap_type { + DISABLE_FOR_THIS_AC = 0, + ENABLE_FOR_THIS_AC = 1, + ENABLE_FOR_ALL_AC = 2, +}; + +enum traffic_type { + TRAFFIC_TYPE_APERIODIC = 0, + TRAFFIC_TYPE_PERIODIC = 1, +}; + +/* WMI_SYNCHRONIZE_CMDID */ +struct wmi_sync_cmd { + u8 data_sync_map; +} __packed; + +/* WMI_CREATE_PSTREAM_CMDID */ +struct wmi_create_pstream_cmd { + /* msec */ + __le32 min_service_int; + + /* msec */ + __le32 max_service_int; + + /* msec */ + __le32 inactivity_int; + + /* msec */ + __le32 suspension_int; + + __le32 service_start_time; + + /* in bps */ + __le32 min_data_rate; + + /* in bps */ + __le32 mean_data_rate; + + /* in bps */ + __le32 peak_data_rate; + + __le32 max_burst_size; + __le32 delay_bound; + + /* in bps */ + __le32 min_phy_rate; + + __le32 sba; + __le32 medium_time; + + /* in octects */ + __le16 nominal_msdu; + + /* in octects */ + __le16 max_msdu; + + u8 traffic_class; + + /* see, enum dir_type */ + u8 traffic_direc; + + u8 rx_queue_num; + + /* see, enum traffic_type */ + u8 traffic_type; + + /* see, enum voiceps_cap_type */ + u8 voice_psc_cap; + u8 tsid; + + /* 802.1D user priority */ + u8 user_pri; + + /* nominal phy rate */ + u8 nominal_phy; +} __packed; + +/* WMI_DELETE_PSTREAM_CMDID */ +struct wmi_delete_pstream_cmd { + u8 tx_queue_num; + u8 rx_queue_num; + u8 traffic_direc; + u8 traffic_class; + u8 tsid; +} __packed; + +/* WMI_SET_CHANNEL_PARAMS_CMDID */ +enum wmi_phy_mode { + WMI_11A_MODE = 0x1, + WMI_11G_MODE = 0x2, + WMI_11AG_MODE = 0x3, + WMI_11B_MODE = 0x4, + WMI_11GONLY_MODE = 0x5, +}; + +#define WMI_MAX_CHANNELS 64 + +/* + * WMI_RSSI_THRESHOLD_PARAMS_CMDID + * Setting the polltime to 0 would disable polling. Threshold values are + * in the ascending order, and should agree to: + * (lowThreshold_lowerVal < lowThreshold_upperVal < highThreshold_lowerVal + * < highThreshold_upperVal) + */ + +struct wmi_rssi_threshold_params_cmd { + /* polling time as a factor of LI */ + __le32 poll_time; + + /* lowest of upper */ + a_sle16 thresh_above1_val; + + a_sle16 thresh_above2_val; + a_sle16 thresh_above3_val; + a_sle16 thresh_above4_val; + a_sle16 thresh_above5_val; + + /* highest of upper */ + a_sle16 thresh_above6_val; + + /* lowest of bellow */ + a_sle16 thresh_below1_val; + + a_sle16 thresh_below2_val; + a_sle16 thresh_below3_val; + a_sle16 thresh_below4_val; + a_sle16 thresh_below5_val; + + /* highest of bellow */ + a_sle16 thresh_below6_val; + + /* "alpha" */ + u8 weight; + + u8 reserved[3]; +} __packed; + +/* + * WMI_SNR_THRESHOLD_PARAMS_CMDID + * Setting the polltime to 0 would disable polling. + */ + +struct wmi_snr_threshold_params_cmd { + /* polling time as a factor of LI */ + __le32 poll_time; + + /* "alpha" */ + u8 weight; + + /* lowest of uppper */ + u8 thresh_above1_val; + + u8 thresh_above2_val; + u8 thresh_above3_val; + + /* highest of upper */ + u8 thresh_above4_val; + + /* lowest of bellow */ + u8 thresh_below1_val; + + u8 thresh_below2_val; + u8 thresh_below3_val; + + /* highest of bellow */ + u8 thresh_below4_val; + + u8 reserved[3]; +} __packed; + +enum wmi_preamble_policy { + WMI_IGNORE_BARKER_IN_ERP = 0, + WMI_DONOT_IGNORE_BARKER_IN_ERP +}; + +struct wmi_set_lpreamble_cmd { + u8 status; + u8 preamble_policy; +} __packed; + +struct wmi_set_rts_cmd { + __le16 threshold; +} __packed; + +/* WMI_SET_TX_PWR_CMDID */ +struct wmi_set_tx_pwr_cmd { + /* in dbM units */ + u8 dbM; +} __packed; + +struct wmi_tx_pwr_reply { + /* in dbM units */ + u8 dbM; +} __packed; + +struct wmi_report_sleep_state_event { + __le32 sleep_state; +}; + +enum wmi_report_sleep_status { + WMI_REPORT_SLEEP_STATUS_IS_DEEP_SLEEP = 0, + WMI_REPORT_SLEEP_STATUS_IS_AWAKE +}; +enum target_event_report_config { + /* default */ + DISCONN_EVT_IN_RECONN = 0, + + NO_DISCONN_EVT_IN_RECONN +}; + +/* + * Used with WMI_AP_SET_NUM_STA_CMDID + */ +struct WMI_AP_NUM_STA_CMD { + u8 num_sta; +}; + +/* Command Replies */ + +/* WMI_GET_CHANNEL_LIST_CMDID reply */ +struct wmi_channel_list_reply { + u8 reserved; + + /* number of channels in reply */ + u8 num_ch; + + /* channel in Mhz */ + __le16 ch_list[1]; +} __packed; + +/* List of Events (target to host) */ +enum wmi_event_id { + WMI_READY_EVENTID = 0x1001, + WMI_CONNECT_EVENTID, + WMI_DISCONNECT_EVENTID, + WMI_BSSINFO_EVENTID, + WMI_CMDERROR_EVENTID, + WMI_REGDOMAIN_EVENTID, + WMI_PSTREAM_TIMEOUT_EVENTID, + WMI_NEIGHBOR_REPORT_EVENTID, + WMI_TKIP_MICERR_EVENTID, + WMI_SCAN_COMPLETE_EVENTID, /* 0x100a */ + WMI_REPORT_STATISTICS_EVENTID, + WMI_RSSI_THRESHOLD_EVENTID, + WMI_ERROR_REPORT_EVENTID, + WMI_OPT_RX_FRAME_EVENTID, + WMI_REPORT_ROAM_TBL_EVENTID, + WMI_EXTENSION_EVENTID, + WMI_CAC_EVENTID, + WMI_SNR_THRESHOLD_EVENTID, + WMI_LQ_THRESHOLD_EVENTID, + WMI_TX_RETRY_ERR_EVENTID, /* 0x1014 */ + WMI_REPORT_ROAM_DATA_EVENTID, + WMI_TEST_EVENTID, + WMI_APLIST_EVENTID, + WMI_GET_WOW_LIST_EVENTID, + WMI_GET_PMKID_LIST_EVENTID, + WMI_CHANNEL_CHANGE_EVENTID, + WMI_PEER_NODE_EVENTID, + WMI_PSPOLL_EVENTID, + WMI_DTIMEXPIRY_EVENTID, + WMI_WLAN_VERSION_EVENTID, + WMI_SET_PARAMS_REPLY_EVENTID, + WMI_ADDBA_REQ_EVENTID, /*0x1020 */ + WMI_ADDBA_RESP_EVENTID, + WMI_DELBA_REQ_EVENTID, + WMI_TX_COMPLETE_EVENTID, + WMI_HCI_EVENT_EVENTID, + WMI_ACL_DATA_EVENTID, + WMI_REPORT_SLEEP_STATE_EVENTID, + WMI_WAPI_REKEY_EVENTID, + WMI_REPORT_BTCOEX_STATS_EVENTID, + WMI_REPORT_BTCOEX_CONFIG_EVENTID, + WMI_GET_PMK_EVENTID, + + /* DFS Events */ + WMI_DFS_HOST_ATTACH_EVENTID, /* 102B */ + WMI_DFS_HOST_INIT_EVENTID, + WMI_DFS_RESET_DELAYLINES_EVENTID, + WMI_DFS_RESET_RADARQ_EVENTID, + WMI_DFS_RESET_AR_EVENTID, + WMI_DFS_RESET_ARQ_EVENTID, + WMI_DFS_SET_DUR_MULTIPLIER_EVENTID, + WMI_DFS_SET_BANGRADAR_EVENTID, + WMI_DFS_SET_DEBUGLEVEL_EVENTID, + WMI_DFS_PHYERR_EVENTID, + /* CCX Evants */ + WMI_CCX_RM_STATUS_EVENTID, /* 1035 */ + + /* P2P Events */ + WMI_P2P_GO_NEG_RESULT_EVENTID, /* 1036 */ + + WMI_WAC_SCAN_DONE_EVENTID, + WMI_WAC_REPORT_BSS_EVENTID, + WMI_WAC_START_WPS_EVENTID, + WMI_WAC_CTRL_REQ_REPLY_EVENTID, + WMI_REPORT_WMM_PARAMS_EVENTID, + WMI_WAC_REJECT_WPS_EVENTID, + + /* More P2P Events */ + WMI_P2P_GO_NEG_REQ_EVENTID, + WMI_P2P_INVITE_REQ_EVENTID, + WMI_P2P_INVITE_RCVD_RESULT_EVENTID, + WMI_P2P_INVITE_SENT_RESULT_EVENTID, + WMI_P2P_PROV_DISC_RESP_EVENTID, + WMI_P2P_PROV_DISC_REQ_EVENTID, + + /* RFKILL Events */ + WMI_RFKILL_STATE_CHANGE_EVENTID, + WMI_RFKILL_GET_MODE_CMD_EVENTID, + + WMI_P2P_START_SDPD_EVENTID, + WMI_P2P_SDPD_RX_EVENTID, + + /* Special event used to notify host that AR6003 + * has processed sleep command (needed to prevent + * a late incoming credit report from crashing + * the system) + */ + WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID, + + WMI_THIN_RESERVED_START_EVENTID = 0x8000, + /* Events in this range are reserved for thinmode + * See wmi_thin.h for actual definitions */ + WMI_THIN_RESERVED_END_EVENTID = 0x8fff, + + WMI_SET_CHANNEL_EVENTID, + WMI_ASSOC_REQ_EVENTID, + + /* generic ACS event */ + WMI_ACS_EVENTID, + WMI_STORERECALL_STORE_EVENTID, + WMI_WOW_EXT_WAKE_EVENTID, + WMI_GTK_OFFLOAD_STATUS_EVENTID, + WMI_NETWORK_LIST_OFFLOAD_EVENTID, + WMI_REMAIN_ON_CHNL_EVENTID, + WMI_CANCEL_REMAIN_ON_CHNL_EVENTID, + WMI_TX_STATUS_EVENTID, + WMI_RX_PROBE_REQ_EVENTID, + WMI_P2P_CAPABILITIES_EVENTID, + WMI_RX_ACTION_EVENTID, + WMI_P2P_INFO_EVENTID, + /* WPS Events */ + WMI_WPS_GET_STATUS_EVENTID, + WMI_WPS_PROFILE_EVENTID, + + /* more P2P events */ + WMI_NOA_INFO_EVENTID, + WMI_OPPPS_INFO_EVENTID, + WMI_PORT_STATUS_EVENTID, + + /* 802.11w */ + WMI_GET_RSN_CAP_EVENTID, + + WMI_FLOWCTRL_IND_EVENTID, + WMI_FLOWCTRL_UAPSD_FRAME_DILIVERED_EVENTID, + + /*Socket Translation Events*/ + WMI_SOCKET_RESPONSE_EVENTID, + + WMI_LOG_FRAME_EVENTID, + WMI_QUERY_PHY_INFO_EVENTID, + WMI_CCX_ROAMING_EVENTID, + + WMI_P2P_NODE_LIST_EVENTID, + WMI_P2P_REQ_TO_AUTH_EVENTID, + WMI_DIAGNOSTIC_EVENTID, /* diagnostic */ + WMI_DISC_PEER_EVENTID, /* wifi discovery */ + WMI_BSS_RSSI_INFO_EVENTID, + WMI_ARGOS_EVENTID, + WMI_AP_IDLE_CLOSE_TIMEOUT_EVENTID = 0x9020, + WMI_SEND_DUMMY_DATA_EVENTID, + WMI_FLUSH_BUFFERED_DATA_EVENTID, + WMI_WLAN_INFO_LTE_EVENTID, + WMI_CLIENT_POWER_SAVE_EVENTID, + + /* SB private events */ + WMI_GET_REG_EVENTID, /* 0x9025 */ + WMI_GET_STAINFO_EVENTID, + WMI_GET_TXPOW_EVENTID, + WMI_GET_VERSION_INFO_EVENTID, + WMI_GET_TESTMODE_EVENTID, + WMI_RX_PROBE_RESP_EVENTID, + WMI_ACL_REJECT_EVENTID, + WMI_GET_WIDIMODE_EVENTID,/* 0x902C */ + +}; + +struct wmi_ready_event_2 { + __le32 sw_version; + __le32 abi_version; + u8 mac_addr[ETH_ALEN]; + u8 phy_cap; +} __packed; + +/* Connect Event */ +struct wmi_connect_event { + union { + struct { + __le16 ch; + u8 bssid[ETH_ALEN]; + __le16 listen_intvl; + __le16 beacon_intvl; + __le16 nw_type; + u8 tx_scheduler_enabled; /* 0x5A means enabled */ + u8 aid; + } sta; + struct { + u8 phymode; + u8 aid; + u8 mac_addr[ETH_ALEN]; + u8 auth; + u8 keymgmt; + __le16 cipher; + u8 apsd_info; + u8 unused[3]; + } ap_sta; + struct { + __le16 ch; + u8 bssid[ETH_ALEN]; + u8 tx_scheduler_enabled; /* 0x5A means enabled */ + u8 aid; + u8 unused[6]; + } ap_bss; + } u; + u8 beacon_ie_len; + u8 assoc_req_len; + u8 assoc_resp_len; + u8 assoc_info[1]; +} __packed; + +/* Disconnect Event */ +enum wmi_disconnect_reason { + NO_NETWORK_AVAIL = 0x01, + + /* bmiss */ + LOST_LINK = 0x02, + + DISCONNECT_CMD = 0x03, + BSS_DISCONNECTED = 0x04, + AUTH_FAILED = 0x05, + ASSOC_FAILED = 0x06, + NO_RESOURCES_AVAIL = 0x07, + CSERV_DISCONNECT = 0x08, + INVALID_PROFILE = 0x0a, + DOT11H_CHANNEL_SWITCH = 0x0b, + PROFILE_MISMATCH = 0x0c, + CONNECTION_EVICTED = 0x0d, + IBSS_MERGE = 0xe, +}; + +#define ATH6KL_COUNTRY_RD_SHIFT 16 + +struct ath6kl_wmi_regdomain { + __le32 reg_code; +}; + +struct wmi_disconnect_event { + /* reason code, see 802.11 spec. */ + __le16 proto_reason_status; + + /* set if known */ + u8 bssid[ETH_ALEN]; + + /* see WMI_DISCONNECT_REASON */ + u8 disconn_reason; + + u8 assoc_resp_len; + u8 assoc_info[1]; +} __packed; + +/* + * BSS Info Event. + * Mechanism used to inform host of the presence and characteristic of + * wireless networks present. Consists of bss info header followed by + * the beacon or probe-response frame body. The 802.11 header is no included. + */ +enum wmi_bi_ftype { + BEACON_FTYPE = 0x1, + PROBERESP_FTYPE, + ACTION_MGMT_FTYPE, + PROBEREQ_FTYPE, +}; + +#define DEF_SCAN_FOR_ROAM_INTVL 7 +#define WMI_ROAM_LRSSI_SCAN_PERIOD (15 * 1000) /* secs */ +#define WMI_ROAM_LRSSI_ROAM_THRESHOLD 30 /* rssi */ +#define WMI_ROAM_LRSSI_SCAN_THRESHOLD (WMI_ROAM_LRSSI_ROAM_THRESHOLD + \ + DEF_SCAN_FOR_ROAM_INTVL) /* rssi */ +#define WMI_ROAM_LRSSI_ROAM_FLOOR 60 /* rssi */ + +enum wmi_roam_ctrl { + WMI_FORCE_ROAM = 1, + WMI_SET_ROAM_MODE, + WMI_SET_HOST_BIAS, + WMI_SET_LRSSI_SCAN_PARAMS, +}; + +enum wmi_roam_mode { + WMI_DEFAULT_ROAM_MODE = 1, /* RSSI based roam */ + WMI_HOST_BIAS_ROAM_MODE = 2, /* Host bias based roam */ + WMI_LOCK_BSS_MODE = 3, /* Lock to the current BSS */ +}; + +struct bss_bias { + u8 bssid[ETH_ALEN]; + s8 bias; +} __packed; + +struct bss_bias_info { + u8 num_bss; + struct bss_bias bss_bias[0]; +} __packed; + +struct low_rssi_scan_params { + __le16 lrssi_scan_period; + a_sle16 lrssi_scan_threshold; + a_sle16 lrssi_roam_threshold; + u8 roam_rssi_floor; + u8 reserved[1]; +} __packed; + +struct roam_ctrl_cmd { + union { + u8 bssid[ETH_ALEN]; /* WMI_FORCE_ROAM */ + u8 roam_mode; /* WMI_SET_ROAM_MODE */ + struct bss_bias_info bss; /* WMI_SET_HOST_BIAS */ + struct low_rssi_scan_params params; /* WMI_SET_LRSSI_SCAN_PARAMS + */ + } __packed info; + u8 roam_ctrl; +} __packed; + +/* BSS INFO HDR version 2.0 */ +struct wmi_bss_info_hdr2 { + __le16 ch; /* frequency in MHz */ + + /* see, enum wmi_bi_ftype */ + u8 frame_type; + + u8 snr; /* note: rssi = snr - 95 dBm */ + u8 bssid[ETH_ALEN]; + __le16 ie_mask; +} __packed; + +/* Command Error Event */ +enum wmi_error_code { + INVALID_PARAM = 0x01, + ILLEGAL_STATE = 0x02, + INTERNAL_ERROR = 0x03, +}; + +struct wmi_cmd_error_event { + __le16 cmd_id; + u8 err_code; +} __packed; + +struct wmi_pstream_timeout_event { + u8 tx_queue_num; + u8 rx_queue_num; + u8 traffic_direc; + u8 traffic_class; +} __packed; + +/* + * The WMI_NEIGHBOR_REPORT Event is generated by the target to inform + * the host of BSS's it has found that matches the current profile. + * It can be used by the host to cache PMKs and/to initiate pre-authentication + * if the BSS supports it. The first bssid is always the current associated + * BSS. + * The bssid and bssFlags information repeats according to the number + * or APs reported. + */ +enum wmi_bss_flags { + WMI_DEFAULT_BSS_FLAGS = 0x00, + WMI_PREAUTH_CAPABLE_BSS = 0x01, + WMI_PMKID_VALID_BSS = 0x02, +}; + +struct wmi_neighbor_info { + u8 bssid[ETH_ALEN]; + u8 bss_flags; /* enum wmi_bss_flags */ +} __packed; + +struct wmi_neighbor_report_event { + u8 num_neighbors; + struct wmi_neighbor_info neighbor[0]; +} __packed; + +/* TKIP MIC Error Event */ +struct wmi_tkip_micerr_event { + u8 key_id; + u8 is_mcast; +} __packed; + +enum wmi_scan_status { + WMI_SCAN_STATUS_SUCCESS = 0, +}; + +/* WMI_SCAN_COMPLETE_EVENTID */ +struct wmi_scan_complete_event { + a_sle32 status; +} __packed; + +#define MAX_OPT_DATA_LEN 1400 + +/* + * Special frame receive Event. + * Mechanism used to inform host of the receiption of the special frames. + * Consists of special frame info header followed by special frame body. + * The 802.11 header is not included. + */ +struct wmi_opt_rx_info_hdr { + __le16 ch; + u8 frame_type; + s8 snr; + u8 src_addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; +} __packed; + +/* Reporting statistic */ +struct tx_stats { + __le32 pkt; + __le32 byte; + __le32 ucast_pkt; + __le32 ucast_byte; + __le32 mcast_pkt; + __le32 mcast_byte; + __le32 bcast_pkt; + __le32 bcast_byte; + __le32 rts_success_cnt; + __le32 pkt_per_ac[4]; + __le32 err_per_ac[4]; + + __le32 err; + __le32 fail_cnt; + __le32 retry_cnt; + __le32 mult_retry_cnt; + __le32 rts_fail_cnt; + a_sle32 ucast_rate; +} __packed; + +struct rx_stats { + __le32 pkt; + __le32 byte; + __le32 ucast_pkt; + __le32 ucast_byte; + __le32 mcast_pkt; + __le32 mcast_byte; + __le32 bcast_pkt; + __le32 bcast_byte; + __le32 frgment_pkt; + + __le32 err; + __le32 crc_err; + __le32 key_cache_miss; + __le32 decrypt_err; + __le32 dupl_frame; + a_sle32 ucast_rate; +} __packed; + +struct tkip_ccmp_stats { + __le32 tkip_local_mic_fail; + __le32 tkip_cnter_measures_invoked; + __le32 tkip_replays; + __le32 tkip_fmt_err; + __le32 ccmp_fmt_err; + __le32 ccmp_replays; +} __packed; + +struct pm_stats { + __le32 pwr_save_failure_cnt; + __le16 stop_tx_failure_cnt; + __le16 atim_tx_failure_cnt; + __le16 atim_rx_failure_cnt; + __le16 bcn_rx_failure_cnt; +} __packed; + +struct cserv_stats { + __le32 cs_bmiss_cnt; + __le32 cs_low_rssi_cnt; + __le16 cs_connect_cnt; + __le16 cs_discon_cnt; + a_sle16 cs_ave_beacon_rssi; + __le16 cs_roam_count; + a_sle16 cs_rssi; + u8 cs_snr; + u8 cs_ave_beacon_snr; + u8 cs_last_roam_msec; +} __packed; + +struct wlan_net_stats { + struct tx_stats tx; + struct rx_stats rx; + struct tkip_ccmp_stats tkip_ccmp_stats; +} __packed; + +struct arp_stats { + __le32 arp_received; + __le32 arp_matched; + __le32 arp_replied; +} __packed; + +struct wlan_wow_stats { + __le32 wow_pkt_dropped; + __le16 wow_evt_discarded; + u8 wow_host_pkt_wakeups; + u8 wow_host_evt_wakeups; +} __packed; + +struct wmi_target_stats { + __le32 lq_val; + a_sle32 noise_floor_calib; + struct pm_stats pm_stats; + struct wlan_net_stats stats; + struct wlan_wow_stats wow_stats; + struct arp_stats arp_stats; + struct cserv_stats cserv_stats; +} __packed; + +/* + * WMI_RSSI_THRESHOLD_EVENTID. + * Indicate the RSSI events to host. Events are indicated when we breach a + * thresold value. + */ +enum wmi_rssi_threshold_val { + WMI_RSSI_THRESHOLD1_ABOVE = 0, + WMI_RSSI_THRESHOLD2_ABOVE, + WMI_RSSI_THRESHOLD3_ABOVE, + WMI_RSSI_THRESHOLD4_ABOVE, + WMI_RSSI_THRESHOLD5_ABOVE, + WMI_RSSI_THRESHOLD6_ABOVE, + WMI_RSSI_THRESHOLD1_BELOW, + WMI_RSSI_THRESHOLD2_BELOW, + WMI_RSSI_THRESHOLD3_BELOW, + WMI_RSSI_THRESHOLD4_BELOW, + WMI_RSSI_THRESHOLD5_BELOW, + WMI_RSSI_THRESHOLD6_BELOW +}; + +struct wmi_rssi_threshold_event { + a_sle16 rssi; + u8 range; +} __packed; + +enum wmi_snr_threshold_val { + WMI_SNR_THRESHOLD1_ABOVE = 1, + WMI_SNR_THRESHOLD1_BELOW, + WMI_SNR_THRESHOLD2_ABOVE, + WMI_SNR_THRESHOLD2_BELOW, + WMI_SNR_THRESHOLD3_ABOVE, + WMI_SNR_THRESHOLD3_BELOW, + WMI_SNR_THRESHOLD4_ABOVE, + WMI_SNR_THRESHOLD4_BELOW +}; + +struct wmi_snr_threshold_event { + /* see, enum wmi_snr_threshold_val */ + u8 range; + + u8 snr; +} __packed; + +/* WMI_REPORT_ROAM_TBL_EVENTID */ +#define MAX_ROAM_TBL_CAND 5 + +struct wmi_bss_roam_info { + a_sle32 roam_util; + u8 bssid[ETH_ALEN]; + s8 rssi; + s8 rssidt; + s8 last_rssi; + s8 util; + s8 bias; + + /* for alignment */ + u8 reserved; +} __packed; + +struct wmi_target_roam_tbl { + __le16 roam_mode; + __le16 num_entries; + struct wmi_bss_roam_info info[]; +} __packed; + +/* WMI_CAC_EVENTID */ +enum cac_indication { + CAC_INDICATION_ADMISSION = 0x00, + CAC_INDICATION_ADMISSION_RESP = 0x01, + CAC_INDICATION_DELETE = 0x02, + CAC_INDICATION_NO_RESP = 0x03, +}; + +#define WMM_TSPEC_IE_LEN 63 + +struct wmi_cac_event { + u8 ac; + u8 cac_indication; + u8 status_code; + u8 tspec_suggestion[WMM_TSPEC_IE_LEN]; +} __packed; + +/* WMI_APLIST_EVENTID */ + +enum aplist_ver { + APLIST_VER1 = 1, +}; + +struct wmi_ap_info_v1 { + u8 bssid[ETH_ALEN]; + __le16 channel; +} __packed; + +union wmi_ap_info { + struct wmi_ap_info_v1 ap_info_v1; +} __packed; + +struct wmi_aplist_event { + u8 ap_list_ver; + u8 num_ap; + union wmi_ap_info ap_list[1]; +} __packed; + +/* Developer Commands */ + +/* + * WMI_SET_BITRATE_CMDID + * + * Get bit rate cmd uses same definition as set bit rate cmd + */ +enum wmi_bit_rate { + RATE_AUTO = -1, + RATE_1Mb = 0, + RATE_2Mb = 1, + RATE_5_5Mb = 2, + RATE_11Mb = 3, + RATE_6Mb = 4, + RATE_9Mb = 5, + RATE_12Mb = 6, + RATE_18Mb = 7, + RATE_24Mb = 8, + RATE_36Mb = 9, + RATE_48Mb = 10, + RATE_54Mb = 11, + RATE_MCS_0_20 = 12, + RATE_MCS_1_20 = 13, + RATE_MCS_2_20 = 14, + RATE_MCS_3_20 = 15, + RATE_MCS_4_20 = 16, + RATE_MCS_5_20 = 17, + RATE_MCS_6_20 = 18, + RATE_MCS_7_20 = 19, + RATE_MCS_0_40 = 20, + RATE_MCS_1_40 = 21, + RATE_MCS_2_40 = 22, + RATE_MCS_3_40 = 23, + RATE_MCS_4_40 = 24, + RATE_MCS_5_40 = 25, + RATE_MCS_6_40 = 26, + RATE_MCS_7_40 = 27, +}; + +struct wmi_bit_rate_reply { + /* see, enum wmi_bit_rate */ + s8 rate_index; +} __packed; + +/* + * WMI_SET_FIXRATES_CMDID + * + * Get fix rates cmd uses same definition as set fix rates cmd + */ +struct wmi_fix_rates_reply { + /* see wmi_bit_rate */ + __le32 fix_rate_mask; +} __packed; + +enum roam_data_type { + /* get the roam time data */ + ROAM_DATA_TIME = 1, +}; + +struct wmi_target_roam_time { + __le32 disassoc_time; + __le32 no_txrx_time; + __le32 assoc_time; + __le32 allow_txrx_time; + u8 disassoc_bssid[ETH_ALEN]; + s8 disassoc_bss_rssi; + u8 assoc_bssid[ETH_ALEN]; + s8 assoc_bss_rssi; +} __packed; + +enum wmi_txop_cfg { + WMI_TXOP_DISABLED = 0, + WMI_TXOP_ENABLED +}; + +struct wmi_set_wmm_txop_cmd { + u8 txop_enable; +} __packed; + +struct wmi_set_keepalive_cmd { + u8 keep_alive_intvl; +} __packed; + +struct wmi_get_keepalive_cmd { + __le32 configured; + u8 keep_alive_intvl; +} __packed; + +#define MAX_APP_IE_LEN (255) + +struct wmi_set_appie_cmd { + u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ + u8 ie_len; + u8 ie_info[0]; +} __packed; + +/* Notify the WSC registration status to the target */ +#define WSC_REG_ACTIVE 1 +#define WSC_REG_INACTIVE 0 + +/* use this ID in WMI_DEL_WOW_PATTERN, to clear all patterns */ +#define WOW_EXT_FILTER_ID_CLEAR_ALL 0xFFFF + +#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 WOW_MASK_SIZE 64 + +#define MAC_MAX_FILTERS_PER_LIST 4 + +struct wow_filter { + u8 wow_valid_filter; + u8 wow_filter_id; + u8 wow_filter_size; + u8 wow_filter_offset; + u8 wow_filter_mask[WOW_MASK_SIZE]; + u8 wow_filter_pattern[WOW_MAX_PATTERN_SIZE]; +} __packed; + +#define MAX_IP_ADDRS 2 + +struct wmi_set_ip_cmd { + /* IP in network byte order */ + __le32 ips[MAX_IP_ADDRS]; +} __packed; + +enum ath6kl_wow_filters { + WOW_FILTER_SSID = BIT(0), + WOW_FILTER_OPTION_MAGIC_PACKET = BIT(2), + WOW_FILTER_OPTION_EAP_REQ = BIT(3), + WOW_FILTER_OPTION_PATTERNS = BIT(4), + WOW_FILTER_OPTION_OFFLOAD_ARP = BIT(5), + WOW_FILTER_OPTION_OFFLOAD_NS = BIT(6), + WOW_FILTER_OPTION_OFFLOAD_GTK = BIT(7), + WOW_FILTER_OPTION_8021X_4WAYHS = BIT(8), + WOW_FILTER_OPTION_NLO_DISCVRY = BIT(9), + WOW_FILTER_OPTION_NWK_DISASSOC = BIT(10), + WOW_FILTER_OPTION_GTK_ERROR = BIT(11), + WOW_FILTER_OPTION_TEST_MODE = BIT(15), +}; + +enum ath6kl_host_mode { + ATH6KL_HOST_MODE_AWAKE, + ATH6KL_HOST_MODE_ASLEEP, +}; + +/* WMI_SET_HOST_SLEEP_MODE_CMDID */ +struct wmi_set_host_sleep_mode_cmd { + __le32 awake; + __le32 asleep; +} __packed; + +enum ath6kl_wow_mode { + ATH6KL_WOW_MODE_DISABLE, + ATH6KL_WOW_MODE_ENABLE, +}; + +/* WMI_SET_WOW_MODE_CMDID */ +struct wmi_set_wow_mode_cmd { + __le32 enable_wow; + __le32 filter; + __le16 host_req_delay; +} __packed; + +/* WMI_ADD_WOW_PATTERN_CMDID */ +struct wmi_add_wow_pattern_cmd { + u8 filter_list_id; + u8 filter_size; + u8 filter_offset; + u8 filter[1]; +} __packed; + +/* WMI_ADD_WOW_EXT_PATTERN_CMDID */ +struct wmi_add_wow_ext_pattern_cmd { + u8 filter_list_id; + u8 filter_id; + __le16 filter_size; + u8 filter_offset; + u8 filter[1]; +} __packed; + +/* WMI_DEL_WOW_PATTERN_CMDID */ +struct wmi_del_wow_pattern_cmd { + __le16 filter_list_id; + __le16 filter_id; +} __packed; + + +/* WMI_SET_AKMP_PARAMS_CMD */ +struct wmi_pmkid { + u8 pmkid[WMI_PMKID_LEN]; +} __packed; + +/* WMI_GET_PMKID_LIST_CMD Reply */ +struct wmi_pmkid_list_reply { + __le32 num_pmkid; + u8 bssid_list[ETH_ALEN][1]; + struct wmi_pmkid pmkid_list[1]; +} __packed; + +/* WMI_ANT_DIV_CMD */ +struct wmi_ant_div_cmd { + u8 diversity_control; + u8 main_lna_conf; + u8 alt_lna_conf; + u8 fast_div_bias; + u8 main_gaintb; + u8 alt_gaintb; +} __packed; + +/* WMI_ANT_DIV_STAT */ +struct wmi_ant_div_stat { + u32 scan_start_time; + u16 total_pkt_count; + u16 count; + int alt_recv_cnt; + int main_recv_cnt; + int alt_ratio; + int main_rssi_avg; + int alt_rssi_avg; + int curr_main_set; + int curr_alt_set; + int end_st; + int scan; + int scan_not_start; + int curr_bias; + u8 main_lna_conf; + u8 alt_lna_conf; + u8 fast_div_bias; +} __packed; + +#define WMI_MAX_PMKID_CACHE 8 +#define MAX_PMKID_LIST_SIZE (sizeof(__le32) + WMI_MAX_PMKID_CACHE * \ + (sizeof(struct wmi_pmkid) + ETH_ALEN)) + +/* WMI_ADDBA_REQ_EVENTID */ +struct wmi_addba_req_event { + u8 tid; + u8 win_sz; + __le16 st_seq_no; + + /* f/w response for ADDBA Req; OK (0) or failure (!=0) */ + u8 status; +} __packed; + +/* WMI_ADDBA_RESP_EVENTID */ +struct wmi_addba_resp_event { + u8 tid; + + /* OK (0), failure (!=0) */ + u8 status; + + /* three values: not supported(0), 3839, 8k */ + __le16 amsdu_sz; +} __packed; + +/* WMI_DELBA_EVENTID + * f/w received a DELBA for peer and processed it. + * Host is notified of this + */ +struct wmi_delba_event { + u8 tid; + u8 is_peer_initiator; + __le16 reason_code; +} __packed; + +#define PEER_NODE_JOIN_EVENT 0x00 +#define PEER_NODE_LEAVE_EVENT 0x01 +#define PEER_FIRST_NODE_JOIN_EVENT 0x10 +#define PEER_LAST_NODE_LEAVE_EVENT 0x11 + +struct wmi_peer_node_event { + u8 event_code; + u8 peer_mac_addr[ETH_ALEN]; +} __packed; + +/* Transmit complete event data structure(s) */ + +/* version 1 of tx complete msg */ +struct tx_complete_msg_v1 { +#define TX_COMPLETE_STATUS_SUCCESS 0 +#define TX_COMPLETE_STATUS_RETRIES 1 +#define TX_COMPLETE_STATUS_NOLINK 2 +#define TX_COMPLETE_STATUS_TIMEOUT 3 +#define TX_COMPLETE_STATUS_OTHER 4 + + u8 status; + + /* packet ID to identify parent packet */ + u8 pkt_id; + + /* rate index on successful transmission */ + u8 rate_idx; + + /* number of ACK failures in tx attempt */ + u8 ack_failures; +} __packed; + +struct wmi_tx_complete_event { + /* no of tx comp msgs following this struct */ + u8 num_msg; + + /* length in bytes for each individual msg following this struct */ + u8 msg_len; + + /* version of tx complete msg data following this struct */ + u8 msg_type; + + /* individual messages follow this header */ + u8 reserved; +} __packed; + +/* + * ------- AP Mode definitions -------------- + */ + +/* + * !!! Warning !!! + * -Changing the following values needs compilation of both driver and firmware + */ +#define AP_MAX_NUM_STA 10 + +/* Only for chips after McK1.2. */ +#define NUM_DEV 4 + +/* Sync with target's */ +#define NUM_CONN (AP_MAX_NUM_STA + NUM_DEV) + +/* As P2P device port won't enter CONN state, so we omit 1 CONN buffer */ +#define WMI_NUM_CONN (AP_MAX_NUM_STA + NUM_DEV - 1) + +/* Spl. AID used to set DTIM flag in the beacons */ +#define MCAST_AID 0xFF + +#define DEF_AP_COUNTRY_CODE "US " + +/* Used with WMI_AP_SET_NUM_STA_CMDID */ + +/* + * Used with WMI_AP_SET_MLME_CMDID + */ + +/* MLME Commands */ +#define WMI_AP_MLME_ASSOC 1 /* associate station */ +#define WMI_AP_DISASSOC 2 /* disassociate station */ +#define WMI_AP_DEAUTH 3 /* deauthenticate station */ +#define WMI_AP_MLME_AUTHORIZE 4 /* authorize station */ +#define WMI_AP_MLME_UNAUTHORIZE 5 /* unauthorize station */ + +struct wmi_ap_set_mlme_cmd { + u8 mac[ETH_ALEN]; + __le16 reason; /* 802.11 reason code */ + u8 cmd; /* operation to perform (WMI_AP_*) */ +} __packed; + +struct wmi_ap_set_pvb_cmd { + __le32 flag; + __le16 rsvd; + __le16 aid; +} __packed; + +struct wmi_rx_frame_format_cmd { + /* version of meta data for rx packets <0 = default> (0-7 = valid) */ + u8 meta_ver; + + /* + * 1 == leave .11 header intact, + * 0 == replace .11 header with .3 + */ + u8 dot11_hdr; + + /* + * 1 == defragmentation is performed by host, + * 0 == performed by target + */ + u8 defrag_on_host; + + /* for alignment */ + u8 reserved[1]; +} __packed; + +/* AP mode events */ + +/* WMI_PS_POLL_EVENT */ +struct wmi_pspoll_event { + __le16 aid; +} __packed; + +struct wmi_per_sta_stat { + __le32 tx_bytes; + __le32 tx_pkts; + __le32 tx_error; + __le32 tx_discard; + __le32 rx_bytes; + __le32 rx_pkts; + __le32 rx_error; + __le32 rx_discard; + u8 aid; + u8 tx_ucast_rate; + + /* unit is (ms./1024). Target time, not host time. */ + __le16 last_txrx_time; +} __packed; + +struct wmi_ap_mode_stat { + __le32 action; + struct wmi_per_sta_stat sta[AP_MAX_NUM_STA + 1]; +} __packed; + +/* End of AP mode definitions */ + +struct wmi_remain_on_chnl_cmd { + __le32 freq; + __le32 duration; +} __packed; + +struct wmi_send_action_cmd { + __le32 id; + __le32 freq; + __le32 wait; + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_tx_status_event { + __le32 id; + u8 ack_status; +} __packed; + +struct wmi_probe_req_report_cmd { + u8 enable; +} __packed; + +struct wmi_probe_resp_report_cmd { + u8 enable; +} __packed; + +struct wmi_disable_11b_rates_cmd { + u8 disable; +} __packed; + +struct wmi_set_appie_extended_cmd { + u8 role_id; + u8 mgmt_frm_type; + u8 ie_len; + u8 ie_info[0]; +} __packed; + +struct wmi_remain_on_chnl_event { + __le32 freq; + __le32 duration; +} __packed; + +struct wmi_cancel_remain_on_chnl_event { + __le32 freq; + __le32 duration; + u8 status; +} __packed; + +struct wmi_rx_action_event { + __le32 freq; + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_p2p_capabilities_event { + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_p2p_rx_probe_req_event { + __le32 freq; + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_p2p_rx_probe_resp_event { + __le32 freq; + __le16 len; + u8 data[0]; +} __packed; +struct wmi_acl_reject_event { + __le16 len; + u8 data[0]; +} __packed; +#define P2P_FLAG_CAPABILITIES_REQ (0x00000001) +#define P2P_FLAG_MACADDR_REQ (0x00000002) +#define P2P_FLAG_HMODEL_REQ (0x00000004) + +struct wmi_get_p2p_info { + __le32 info_req_flags; +} __packed; + +struct wmi_p2p_info_event { + __le32 info_req_flags; + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_p2p_capabilities { + u8 go_power_save; +} __packed; + +struct wmi_p2p_macaddr { + u8 mac_addr[ETH_ALEN]; +} __packed; + +struct wmi_p2p_hmodel { + u8 p2p_model; +} __packed; + +struct wmi_p2p_probe_response_cmd { + __le32 freq; + u8 destination_addr[ETH_ALEN]; + __le16 len; + u8 data[0]; +} __packed; + +#define RATECTRL_MODE_DEFAULT 0 +#define RATECTRL_MODE_PERONLY 1 + +struct wmi_set_ratectrl_parm_cmd { + u32 mode; +} __packed; + +enum wmi_pkt_cmd { + WMI_PKTLOG_CMD_DISABLE, + WMI_PKTLOG_CMD_SETSIZE, +} __packed; + +#define PKTLOG_MAX_BYTES 4096 + +struct wmi_get_pktlog_cmd { + __le32 nbytes; + u8 buffer[PKTLOG_MAX_BYTES]; +} __packed; + + +enum wmi_pktlog_option { + WMI_PKTLOG_OPTION_LOG_TCP_HEADERS = 0x1, + WMI_PKTLOG_OPTION_TRIGGER_THRUPUT = 0x2, + WMI_PKTLOG_OPTION_TRIGGER_SACK = 0x4, + WMI_PKTLOG_OPTION_TRIGGER_PER = 0x8, + WMI_PKTLOG_OPTION_LOG_DIAGNOSTIC = 0x20 +}; + +enum wmi_pktlog_event { + WMI_PKTLOG_EVENT_RX = 0x1, + WMI_PKTLOG_EVENT_TX = 0x2, + WMI_PKTLOG_EVENT_RCF = 0x4, /* Rate Control Find */ + WMI_PKTLOG_EVENT_RCU = 0x8, /* Rate Control Update */ +}; + +struct wmi_enable_pktlog_cmd { + enum wmi_pktlog_event evlist; + enum wmi_pktlog_option option; + __le32 trigger_thresh; + __le32 trigger_interval; + __le32 trigger_tail_count; + __le32 buffer_size; +} __packed; + + + +/* Extended WMI (WMIX) + * + * Extended WMIX commands are encapsulated in a WMI message with + * cmd=WMI_EXTENSION_CMD. + * + * Extended WMI commands are those that are needed during wireless + * operation, but which are not really wireless commands. This allows, + * for instance, platform-specific commands. Extended WMI commands are + * embedded in a WMI command message with WMI_COMMAND_ID=WMI_EXTENSION_CMDID. + * Extended WMI events are similarly embedded in a WMI event message with + * WMI_EVENT_ID=WMI_EXTENSION_EVENTID. + */ +struct wmix_cmd_hdr { + __le32 cmd_id; +} __packed; + +enum wmix_command_id { + WMIX_DSETOPEN_REPLY_CMDID = 0x2001, + WMIX_DSETDATA_REPLY_CMDID, + WMIX_GPIO_OUTPUT_SET_CMDID, + WMIX_GPIO_INPUT_GET_CMDID, + WMIX_GPIO_REGISTER_SET_CMDID, + WMIX_GPIO_REGISTER_GET_CMDID, + WMIX_GPIO_INTR_ACK_CMDID, + WMIX_HB_CHALLENGE_RESP_CMDID, + WMIX_DBGLOG_CFG_MODULE_CMDID, + WMIX_PROF_CFG_CMDID, /* 0x200a */ + WMIX_PROF_ADDR_SET_CMDID, + WMIX_PROF_START_CMDID, + WMIX_PROF_STOP_CMDID, + WMIX_PROF_COUNT_GET_CMDID, +}; + +enum wmix_event_id { + WMIX_DSETOPENREQ_EVENTID = 0x3001, + WMIX_DSETCLOSE_EVENTID, + WMIX_DSETDATAREQ_EVENTID, + WMIX_GPIO_INTR_EVENTID, + WMIX_GPIO_DATA_EVENTID, + WMIX_GPIO_ACK_EVENTID, + WMIX_HB_CHALLENGE_RESP_EVENTID, + WMIX_DBGLOG_EVENTID, + WMIX_PROF_COUNT_EVENTID, + WMIX_PKTLOG_EVENTID, + WMIX_RTT_RESP_EVENTID, +}; + +/* + * ------Error Detection support------- + */ + +/* + * WMIX_HB_CHALLENGE_RESP_CMDID + * Heartbeat Challenge Response command + */ +struct wmix_hb_challenge_resp_cmd { + __le32 cookie; + __le32 source; +} __packed; + +struct ath6kl_wmix_dbglog_cfg_module_cmd { + __le32 valid; + __le32 config; +} __packed; + +/* End of Extended WMI (WMIX) */ + + +/* Diagnostic WMI (WMID) + * + * Diagnostic WMI commands are only for diagnostic tool. + * Diagnostic WMI commands are encapsulated in a WMI message with + * WMI_COMMAND_ID=WMI_DIAGNOSTIC_CMDID. + * Diagnostic WMI events are similarly embedded in a WMI event message with + * WMI_EVENT_ID=WMI_DIAGNOSTIC_EVENTID. + */ +struct wmid_cmd_hdr { + __le32 cmd_id; + __le32 seq_num; +} __packed; + +struct wmid_event_set_cmd { + bool enable; +} __packed; + +struct wmid_macfilter_cmd { + __le32 type; + __le32 low; + __le32 high; +} __packed; + +enum wmid_command_id { + WMID_INTERFERENCE_CMDID = 0x2001, + WMID_RXTIME_CMDID, + WMID_FSM_EVENT_CMDID, + WMID_PWR_SAVE_EVENT_CMDID, + WMID_MACFILTER_CMDID, + WMID_STAT_RX_RATE_CMDID, + WMID_STAT_TX_RATE_CMDID, +}; + +enum wmid_event_id { + WMID_START_SCAN_EVENTID = 0x3001, + WMID_FSM_AUTH_EVENTID, + WMID_FSM_ASSOC_EVENTID, + WMID_FSM_DEAUTH_EVENTID, + WMID_FSM_DISASSOC_EVENTID, + WMID_STAT_RX_RATE_EVENTID, + WMID_STAT_TX_RATE_EVENTID, + WMID_INTERFERENCE_EVENTID, + WMID_RXTIME_EVENTID, + WMID_PWR_SAVE_EVENTID, + WMID_FSM_CONNECT_EVENTID, + WMID_FSM_DISCONNECT_EVENTID, +}; + +struct wmid_pwr_save_event { + __le16 oldState; + __le16 pmState; +} __packed; +/* End of Diagnostic WMI (WMID) */ + +enum wmi_sync_flag { + NO_SYNC_WMIFLAG = 0, + + /* transmit all queued data before cmd */ + SYNC_BEFORE_WMIFLAG, + + /* any new data waits until cmd execs */ + SYNC_AFTER_WMIFLAG, + + SYNC_BOTH_WMIFLAG, + + /* end marker */ + END_WMIFLAG +}; + +/* Green TX parameters */ +struct wmi_green_tx_params { + u32 enable; + u8 next_probe_count; + u8 max_back_off; + u8 min_gtx_rssi; + u8 force_back_off; +} __packed; + +/* flow control indication parameters */ +struct wmi_flowctrl_ind_event { + u8 num_of_conn; + u8 ac_map[WMI_NUM_CONN]; + u8 ac_queue_depth[WMI_NUM_CONN * 2]; +} __packed; + +/* SMPS parameters */ +enum WMI_SMPS_OPTION { + WMI_SMPS_OPTION_MODE = 0x1, + WMI_SMPS_OPTION_AUTO = 0x2, + WMI_SMPS_OPTION_DATATHRESH = 0x4, + WMI_SMPS_OPTION_RSSITHRESH = 0x8, +}; + +enum WMI_SMPS_MODE { + WMI_SMPS_MODE_STATIC = 0x1, + WMI_SMPS_MODE_DYNAMIC = 0x2, +}; + +struct wmi_config_smps_cmd { + u8 flags; /* To indicate which options have changed */ + u8 rssiThresh; + u8 dataThresh; + u8 mode; /* static/dynamic */ + u8 automatic; +} __packed; + +struct wmi_config_enable_cmd { + u8 enable; /* Enable/disable */ +} __packed; + +struct wmi_lpl_force_enable_cmd { + u8 lplPolicy; + u8 noBlockerDetect; + u8 noRfbDetect; + u8 rsvd; +} __packed; + +/*command structure for all policy related commands*/ +struct wmi_lpl_policy_cmd { + u64 index; + u32 value; +} __packed; + +struct wmi_lpl_params_cmd { + u32 rfbPeriod; + u32 rfbObsDuration; + u32 blockerBeaconRssi; + u8 chanOff; + u32 rfbDiffThold; + u32 bRssiThold; + u32 maxBlockerRssi; +} __packed; + +/* HT Cap parameters */ +struct wmi_set_ht_cap { + u8 band; + u8 enable; + u8 chan_width_40M_supported; + u8 short_GI_20MHz; + u8 short_GI_40MHz; + u8 intolerance_40MHz; + u8 max_ampdu_len_exp; +} __packed; + +#define HT_INFO_OPMODE_PROT_PURE (0) /* No protection mode */ +#define HT_INFO_OPMODE_PROT_MAY_LEGACY (1) /* Nonmember protection mode */ +#define HT_INFO_OPMODE_PROT_HT20_ASSOC (2) /* HT20 protection mode */ +#define HT_INFO_OPMODE_PROT_MIXED (3) /* NonHT mixed mode */ + +/* HT Info parameters */ +struct wmi_set_ht_op { + u8 sta_chan_width; + u8 ap_ht_info; /* BO-B1 : ht_opmode */ +} __packed; + +/* beacon interval */ +struct wmi_set_beacon_intvl { + u16 beacon_interval; +} __packed; + +/* hidden ssid */ +struct wmi_set_hidden_ssid { + u8 hidden_ssid; +} __packed; + +/* DTIM */ +struct wmi_set_dtim_cmd { + u8 dtim; +} __packed; + +/* uAPSD */ +enum { + WMI_AP_APSD_DISABLED = 0, + WMI_AP_APSD_ENABLED +}; + +struct wmi_ap_set_apsd_cmd { + u8 enable; +} __packed; + +enum { + WMI_AP_APSD_NO_DELIVERY_FRAMES_FOR_THIS_TRIGGER = 0x1, +}; + +struct wmi_ap_apsd_buffered_traffic_cmd { + u16 aid; + u16 bitmap; + u32 flags; +} __packed; + +#define GTK_OFFLOAD_KEK_BYTES 16 +#define GTK_OFFLOAD_KCK_BYTES 16 +#define GTK_REPLAY_COUNTER_BYTES 8 + +/* set offload parameters, KEK,KCK and replay counter values are valid */ +#define WMI_GTK_OFFLOAD_OPCODE_SET 1 + +/* clear offload parameters */ +#define WMI_GTK_OFFLOAD_OPCODE_CLEAR 2 + +/* get status, generates WMI_GTK_OFFLOAD_STATUS_EVENT */ +#define WMI_GTK_OFFLOAD_OPCODE_STATUS 3 + + /* structure to issue GTK offload opcode to set/clear or fetch status + * NOTE: offload is enabled when WOW options are enabled. + */ +/* WOW_FILTER_OPTION_OFFLOAD_GTK */ +struct wmi_gtk_offload_op { + __le32 flags; /* control flags */ + u8 opcode; /* opcode */ + u8 kek[GTK_OFFLOAD_KEK_BYTES]; /* key encryption key */ + u8 kck[GTK_OFFLOAD_KCK_BYTES]; /* key confirmation key */ + + /* replay counter for re-key */ + u8 replay_counter[GTK_REPLAY_COUNTER_BYTES]; +} __packed; + +struct wmi_gtk_offload_status_event { + __le32 flags; /* status flags */ + + /* number of successful GTK refresh exchanges since last SET operation*/ + __le32 refresh_cnt; + + /* current replay counter */ + u8 replay_counter[GTK_REPLAY_COUNTER_BYTES]; +} __packed; + +/* TX rate series */ +#define WMI_MODE_MAX 8 +#define WMI_MAX_RATE_MASK 2 + +struct wmi_set_tx_select_rate_cmd { + __le32 rateMasks[WMI_MODE_MAX * WMI_MAX_RATE_MASK]; +} __packed; + +struct wmi_rsn_cap_cmd { + __le16 rsn_cap; +} __packed; + +#define WMI_MAX_RATE_MASK 2 + +struct wmi_set_fix_rates_cmd { + __le32 fixRateMask[WMI_MAX_RATE_MASK]; +} __packed; + +/* NOA Info. */ +struct wmi_noa_descriptor { + __le32 duration; + __le32 interval; + __le32 start_or_offset; + u8 count_or_type; +} __packed; + +struct wmi_noa_info { + u8 enable; + u8 count; + u8 noas[0]; /* struct wmi_noa_descriptor */ +} __packed; + +/* OppPS Info. */ +struct wmi_oppps_info { + u8 enable; + u8 ctwin; +} __packed; + +/* Port operation */ +#define ADD_PORT_FAIL 0x0 +#define ADD_PORT_SUCCESS 0x1 +#define DEL_PORT_FAIL 0x2 +#define DEL_PORT_SUCCESS 0x3 + +struct wmi_add_port_cmd { + u8 port_id; + u8 port_opmode; + u8 port_subopmode; + u8 mac_addr[6]; +} __packed; + +struct wmi_del_port_cmd { + u8 port_id; +} __packed; + +struct wmi_port_status { + u8 status; + u8 port_id; + u8 mac_addr[6]; +} __packed; + + +/* WMI_WOW_EXT_WAKE_EVENTID */ +enum WOW_EXT_WAKE_TYPE { + WOW_EXT_WAKE_TYPE_UNDEF = 0, + WOW_EXT_WAKE_TYPE_MAGIC, + WOW_EXT_WAKE_TYPE_PATTERN, + WOW_EXT_WAKE_TYPE_EAPREQ, + WOW_EXT_WAKE_TYPE_4WAYHS, + WOW_EXT_WAKE_TYPE_NETWORK_NLO, + WOW_EXT_WAKE_TYPE_NETWORK_DISASSOC, + WOW_EXT_WAKE_TYPE_NETWORK_GTK_OFFL_ERROR, + WOW_EXT_WAKE_TYPE_IPV4_TCP_SYN, + WOW_EXT_WAKE_TYPE_IPV6_TCP_SYN, + WOW_EXT_WAKE_TYPE_WLAN_HB, + WOW_EXT_WAKE_TYPE_MAXs +}; + +struct wmi_wow_event_wake_event { + u16 flags; + u8 type; + u8 value; + u16 packet_length; + u16 wake_data_length; + u8 wake_data[1]; +} __packed; + +#define WMI_MAX_TCP_FILTER_SIZE 32 +#define WMI_MAX_UDP_FILTER_SIZE 32 + +struct wmi_heart_beat_params_cmd { + u8 enable; + u8 item; + u8 session; +} __packed; + +struct wmi_heart_beat_tcp_params_cmd { + __le32 srv_ip; + __le32 dev_ip; + __le16 src_port; + __le16 dst_port; + __le16 timeout; + u8 session; + u8 gateway_mac[ETH_ALEN]; +} __packed; + +struct wmi_heart_beat_tcp_filter_cmd { + u8 length; + u8 offset; + u8 session; + u8 filter[WMI_MAX_TCP_FILTER_SIZE]; +} __packed; + +struct wmi_heart_beat_udp_params_cmd { + __le32 srv_ip; + __le32 dev_ip; + __le16 src_port; + __le16 dst_port; + __le16 interval; + __le16 timeout; + u8 session; + u8 gateway_mac[ETH_ALEN]; +} __packed; + +struct wmi_heart_beat_udp_filter_cmd { + u8 length; + u8 offset; + u8 session; + u8 filter[WMI_MAX_UDP_FILTER_SIZE]; +} __packed; + +struct wmi_heart_beat_network_info_cmd { + u32 device_ip; + u32 server_ip; + u32 gateway_ip; + u8 gateway_mac[ETH_ALEN]; +} __packed; + +/* wifi discovery */ +struct wmi_disc_ie_filter_cmd { + u8 enable; + u8 startPos; + u8 length; + u8 pattern[1]; +} __packed; + +struct wmi_disc_mode_cmd { + __le16 enable; + __le16 channel; /* channels in Mhz */ + __le32 home_dwell_time; /* max duration in the home channel(msec) */ + __le32 sleepTime; + __le32 random; + __le32 numPeers; + __le32 peerTimeout; +} __packed; + +struct wmi_disc_peer { + __le32 rssi; + u8 addr[ETH_ALEN]; +} __packed; + +struct wmi_disc_peer_event { + u8 peer_num; + u8 peer_data[1]; +} __packed; + +struct wmi_ap_poll_sta_cmd { + u8 aid; + u8 reserved[7]; +} __packed; + +struct wmm_params { + u8 acm; + u8 aifsn; + u8 logcwmin; + u8 logcwmax; + u16 txopLimit; +}; + +struct wmi_report_wmm_params { + struct wmm_params wmm_params[WMM_NUM_AC]; +} __packed; + +struct wmi_set_credit_bypass_cmd { + u8 eid; + u8 restore; + u16 threshold; +} __packed; + +/* AP ACL */ +#define AP_ACL_SIZE 10 + +#define AP_ACL_DISABLE 0x00 +#define AP_ACL_ALLOW_MAC 0x01 +#define AP_ACL_DENY_MAC 0x02 +#define AP_ACL_RETAIN_LIST_MASK 0x80 + +struct wmi_ap_acl_policy_cmd { + u8 policy; +} __packed; + +#define ADD_MAC_ADDR 1 +#define DEL_MAC_ADDR 2 + +struct wmi_ap_acl_mac_list_cmd { + u8 action; + u8 index; + u8 mac[ETH_ALEN]; + u8 wildcard; +} __packed; + +struct wmi_allow_aggr_cmd { + u16 tx_allow_aggr; /* bitmask indicate TID */ + u16 rx_allow_aggr; /* bitmask indicate TID */ +} __packed; + +/* AP Admission-Control */ +struct wmi_assoc_req_event { + u8 status; + u8 rspType; + u8 assocReq[0]; +} __packed; + +struct wmi_assoc_resp_send_cmd { + u8 host_accept; + u8 host_reasonCode; + u8 target_status; + u8 sta_mac_addr[ETH_ALEN]; + u8 rspType; +} __packed; + +struct wmi_assoc_req_relay_cmd { + u8 enable; +} __packed; + + +/* ARP OFFLOAD */ +struct wmi_ipv6_addr { + u8 address[16]; /* IPV6 in Network Byte Order */ +} __packed; + +#define WMI_MAX_NS_OFFLOADS 2 +#define WMI_MAX_ARP_OFFLOADS 2 + +/* the tuple entry is valid */ +#define WMI_ARPOFF_FLAGS_VALID (1 << 0) +/* the target mac address is valid */ +#define WMI_ARPOFF_FLAGS_MAC_VALID (1 << 1) +/* remote IP field is valid */ +#define WMI_ARPOFF_FLAGS_REMOTE_IP_VALID (1 << 2) + +/* flags + * IPV4 addresses of the local node + * source address of the remote node requesting the ARP (qualifier) + * mac address for this tuple, if not valid, the local MAC is used + */ +struct wmi_arp_offload_tuple { + u8 flags; + u8 target_ipaddr[4]; + u8 remote_ipaddr[4]; + u8 target_mac[ETH_ALEN]; +} __packed; + +/* the tuple entry is valid */ +#define WMI_NSOFF_FLAGS_VALID (1 << 0) +/* the target mac address is valid */ +#define WMI_NSOFF_FLAGS_MAC_VALID (1 << 1) +/* remote IP field is valid */ +#define WMI_NSOFF_FLAGS_REMOTE_IP_VALID (1 << 2) + +#define WMI_NSOFF_MAX_TARGET_IPS 2 + +/* flags + * IPV6 target addresses of the local node + * multi-cast source IP addresses for receiving solicitations + * address of remote node requesting the solicitation (qualifier) + * mac address for this tuple, if not valid, the local MAC is used + */ +struct wmi_ns_offload_tuple { + u8 flags; + struct wmi_ipv6_addr target_ipaddr[WMI_NSOFF_MAX_TARGET_IPS]; + struct wmi_ipv6_addr solicitation_ipaddr; + struct wmi_ipv6_addr remote_ipaddr; + u8 target_mac[ETH_ALEN]; +} __packed; + +struct wmi_set_arp_ns_offload_cmd { + u32 flags; + struct wmi_ns_offload_tuple ns_tuples[WMI_MAX_NS_OFFLOADS]; + struct wmi_arp_offload_tuple arp_tuples[WMI_MAX_ARP_OFFLOADS]; +} __packed; + +/* MCC profile setting + * + */ +enum WMI_MCC_PROFILE { + WMI_MCC_PROFILE_STA50 = BIT(0), + WMI_MCC_PROFILE_STA20 = BIT(1), + WMI_MCC_PROFILE_STA80 = BIT(2), + WMI_MCC_CTS_ENABLE = BIT(4), + WMI_MCC_PSPOLL_ENABLE = BIT(5), + WMI_MCC_DUAL_TIME_MASK = BIT(8), +}; + +struct wmi_set_mcc_profile_cmd { + u32 mcc_profile; +} __packed; + +#define DISALBE_AP_INACTIVE_TIMEMER 0 +struct wmi_ap_conn_inact_cmd { + u32 period; +} __packed; + +enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi); +void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id); +int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb); +int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, + u8 msg_type, u32 flags, + enum wmi_data_hdr_data_type data_type, + u8 meta_ver, void *tx_meta_info, u8 if_idx); +u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri); + +int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb); +int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb); +int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx, + struct sk_buff *skb, u32 layer2_priority, + bool wmm_enabled, u8 *ac, + u16 *phtc_tag); + +int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb); + +int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, + enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag); + +int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, + enum network_type nw_type, + enum dot11_auth_mode dot11_auth_mode, + enum auth_mode auth_mode, + enum crypto_type pairwise_crypto, + u8 pairwise_crypto_len, + enum crypto_type group_crypto, + u8 group_crypto_len, int ssid_len, u8 *ssid, + u8 *bssid, u16 channel, u32 ctrl_flags); + +int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 if_idx, u8 *bssid, + u16 channel); +int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx); +int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, + enum wmi_scan_type scan_type, + u32 force_fgscan, u32 is_legacy, + u32 home_dwell_time, u32 force_scan_interval, + s8 num_chan, u16 *ch_list); +int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, u16 fg_start_sec, + u16 fg_end_sec, u16 bg_sec, + u16 minact_chdw_msec, u16 maxact_chdw_msec, + u16 pas_chdw_msec, u8 short_scan_ratio, + u8 scan_ctrl_flag, u32 max_dfsch_act_time, + u16 maxact_scan_per_ssid); +int ath6kl_wmi_bssfilter_cmd(struct wmi *wmi, u8 if_idx, u8 filter, + u32 ie_mask); +int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, u8 index, u8 flag, + u8 ssid_len, u8 *ssid); +int ath6kl_wmi_listeninterval_cmd(struct wmi *wmi, u8 if_idx, + u16 listen_interval, + u16 listen_beacons); +int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode); +int ath6kl_wmi_pmparams_cmd(struct wmi *wmi, u8 if_idx, u16 idle_period, + u16 ps_poll_num, u16 dtim_policy, + u16 tx_wakup_policy, u16 num_tx_to_wakeup, + u16 ps_fail_event_policy); +int ath6kl_wmi_ibss_pm_caps_cmd(struct wmi *wmi, u8 if_idx, u8 adhoc_ps_type, + u8 ttl, + u16 atim_windows, + u16 timeout_value); +int ath6kl_wmi_create_pstream_cmd(struct wmi *wmi, u8 if_idx, + struct wmi_create_pstream_cmd *pstream); +int ath6kl_wmi_delete_pstream_cmd(struct wmi *wmi, u8 if_idx, u8 traffic_class, + u8 tsid); +int ath6kl_wmi_disctimeout_cmd(struct wmi *wmi, u8 if_idx, u8 timeout); + +int ath6kl_wmi_set_rts_cmd(struct wmi *wmi, u8 if_idx, u16 threshold); +int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 if_idx, u8 status, + u8 preamble_policy); + +int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source); +int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config); + +int ath6kl_wmi_get_stats_cmd(struct wmi *wmi, u8 if_idx); +int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, + enum crypto_type key_type, + u8 key_usage, u8 key_len, + u8 *key_rsc, unsigned int key_rsc_len, + u8 *key_material, + u8 key_op_ctrl, u8 *mac_addr, + enum wmi_sync_flag sync_flag); +#ifdef PMF_SUPPORT +int ath6kl_wmi_addkey_igtk_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, + u8 key_len, u8 *key_rsc, u8 *key_material, + enum wmi_sync_flag sync_flag); +#endif +int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk); +int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index); +int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid, + const u8 *pmkid, bool set); +int ath6kl_wmi_set_tx_pwr_cmd(struct wmi *wmi, u8 if_idx, u8 dbM); +int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx); +int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi); + +int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg); +int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, + u8 keep_alive_intvl); +int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); + +s32 ath6kl_wmi_get_rate(s8 rate_index); +s32 ath6kl_wmi_get_rate_ar6004(s8 rate_index); + +int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd); + +/*Wake on Wireless WMI commands*/ +int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, + enum ath6kl_host_mode host_mode); +int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, + enum ath6kl_wow_mode wow_mode, + u32 filter, u16 host_req_delay); +int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, + u8 list_id, u8 filter_size, + u8 filter_offset, u8 *filter, u8 *mask); +int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, + u16 list_id, u16 filter_id); +int ath6kl_wmi_add_wow_ext_pattern_cmd(struct wmi *wmi, u8 if_idx, + u8 list_id, u8 filter_size, + u8 filter_id, u8 *filter, u8 *mask); +int ath6kl_wmi_del_all_wow_ext_patterns_cmd(struct wmi *wmi, u8 if_idx, + __le16 filter_list_id); +int ath6kl_wm_set_gtk_offload(struct wmi *wmi, u8 if_idx, + u8 *kek, u8 *kck, u8 *replay_ctr); + +int ath6kl_wmi_set_roam_ctrl_cmd_for_lowerrssi(struct wmi *wmi, + u16 lowrssi_scan_period, u16 lowrssi_scan_threshold, + u16 lowrssi_roam_threshold, + u8 roam_rssi_floor); + +int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid); +int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode); + +/* AP mode */ +int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx, + struct wmi_connect_cmd *p); + +int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, + const u8 *mac, u16 reason); + +int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u8 if_idx, u16 aid, bool flag); + +int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx, + u8 rx_meta_version, + bool rx_dot11_hdr, bool defrag_on_host); + +int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, + const u8 *ie, u8 ie_len); + +int ath6kl_wmi_set_rate_ctrl_cmd(struct wmi *wmi, + u8 if_idx, u32 ratemode); + +/* P2P */ +int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, u8 if_idx, bool disable); + +int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, + u32 dur); + +int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, + u32 wait, const u8 *data, u16 data_len); + +int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq, + const u8 *dst, const u8 *data, + u16 data_len); + +int ath6kl_wmi_send_go_probe_response_cmd(struct wmi *wmi, + struct ath6kl_vif *vif, const u8 *buf, size_t len, unsigned int freq); + +int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, u8 if_idx, bool enable); + +int ath6kl_wmi_probe_resp_report_req_cmd(struct wmi *wmi, u8 if_idx, + bool enable); + +int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u8 if_idx, u32 info_req_flags); + +int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx); + +int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, + const u8 *ie, u8 ie_len); + +struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); +void *ath6kl_wmi_init(struct ath6kl *devt); +void ath6kl_wmi_shutdown(struct wmi *wmi); +void ath6kl_wmi_reset(struct wmi *wmi); + +int wmi_rtt_req_meas(struct wmi *wmip, struct nsp_mrqst *pstmrqst, u32 len); +int wmi_rtt_config(struct wmi *wmip, struct nsp_rtt_config *); +int wmi_rtt_req(struct wmi *wmip, enum wmi_cmd_id cmd_id, void *data, u32 len); + +int ath6kl_wmi_set_green_tx_params(struct wmi *wmi, + struct wmi_green_tx_params *params); + +int ath6kl_wmi_smps_config(struct wmi *wmi, + struct wmi_config_smps_cmd *options); + +int ath6kl_wmi_smps_enable(struct wmi *wmi, + struct wmi_config_enable_cmd *options); + +int ath6kl_wmi_lpl_enable_cmd(struct wmi *wmi, + struct wmi_lpl_force_enable_cmd *force_enable_cmd); + +int ath6kl_wmi_pktlog_enable_cmd(struct wmi *wmi, + struct wmi_enable_pktlog_cmd *options); + +int ath6kl_wmi_pktlog_disable_cmd(struct wmi *wmi); + +int ath6kl_wmi_pktlog_event_rx(struct wmi *wmi, u8 *datap, u32 len); + +int ath6kl_wmi_simple_cmd(struct wmi *wmi, u8 if_idx, enum wmi_cmd_id cmd_id); + +struct sk_buff *ath6kl_wmi_get_new_buf(u32 size); + +int ath6kl_wmi_abort_scan_cmd(struct wmi *wmi, u8 if_idx); + +int ath6kl_wmi_set_ht_cap_cmd(struct wmi *wmi, u8 if_idx, + u8 band, u8 chan_width_40M_supported, + u8 short_GI, u8 intolerance_40MHz); + +int ath6kl_wmi_set_ht_op_cmd(struct wmi *wmi, u8 if_idx, + u8 sta_chan_width, u8 opmode); + +int ath6kl_wmi_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim); + +int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); + +int ath6kl_wmi_set_apsd_buffered_traffic_cmd(struct wmi *wmi, u8 if_idx, + u16 aid, u16 bitmap, u32 flags); + +int ath6kl_wmi_set_tx_select_rates_on_all_mode(struct wmi *wmi, + u8 if_idx, u64 mask); + +int ath6kl_wmi_set_hidden_ssid_cmd(struct wmi *wmi, u8 if_idx, u8 hidden_ssid); + +int ath6kl_wmi_set_beacon_interval_cmd(struct wmi *wmi, u8 if_idx, + u32 beacon_interval); + +int ath6kl_wmi_set_rsn_cap(struct wmi *wmi, u8 if_idx, u16 rsn_cap); +int ath6kl_wmi_get_rsn_cap(struct wmi *wmi, u8 if_idx); +int ath6kl_wmi_get_pmkid_list(struct wmi *wmi, u8 if_idx); + +int ath6kl_wmi_set_fix_rates(struct wmi *wmi, u8 if_idx, u64 mask); + +int ath6kl_wmi_add_port_cmd(struct wmi *wmi, struct ath6kl_vif *vif, + u8 opmode, u8 subopmode); +int ath6kl_wmi_del_port_cmd(struct wmi *wmi, u8 if_idx, u8 port_id); + +int ath6kl_wmi_set_noa_cmd(struct wmi *wmi, u8 if_idx, + u8 count, u32 start, u32 duration, u32 interval); +int ath6kl_wmi_set_oppps_cmd(struct wmi *wmi, u8 if_idx, + u8 enable, u8 ctwin); + +int ath6kl_wmi_set_heart_beat_params(struct wmi *wmi, u8 if_idx, + u8 enable, u8 item, u8 session); +int ath6kl_wmi_heart_beat_set_tcp_params(struct wmi *wmi, u8 if_idx, + u16 src_port, u16 dst_port, u32 srv_ip, u32 dev_ip, u16 timeout, + u8 session, u8 *gateway_mac); +int ath6kl_wmi_heart_beat_set_tcp_filter(struct wmi *wmi, u8 if_idx, + u8 *filter, u8 length, u8 offset, u8 session); +int ath6kl_wmi_heart_beat_set_udp_params(struct wmi *wmi, u8 if_idx, + u16 src_port, u16 dst_port, u32 srv_ip, u32 dev_ip, u16 interval, + u16 timeout, u8 session, u8 *gateway_mac); +int ath6kl_wmi_heart_beat_set_udp_filter(struct wmi *wmi, u8 if_idx, + u8 *filter, u8 length, u8 offset, u8 session); + +int ath6kl_wmi_disc_ie_cmd(struct wmi *wmi, u8 if_idx, u8 enable, + u8 startPos, u8 *pattern, u8 length); +int ath6kl_wmi_disc_mode_cmd(struct wmi *wmi, u8 if_idx, u16 enable, + u16 channel, u32 home_dwell_time, u32 sleepTime, u32 random, + u32 numPeers, u32 peerTimeout); + +int ath6kl_wmi_ap_poll_sta(struct wmi *wmi, u8 if_idx, u8 aid); +int ath6kl_wmi_ap_acl_policy(struct wmi *wmi, u8 if_idx, u8 policy); +int ath6kl_wmi_ap_acl_mac_list(struct wmi *wmi, u8 if_idx, + u8 idx, u8 *mac_addr, u8 action); +int ath6kl_wmi_allow_aggr_cmd(struct wmi *wmi, u8 if_idx, + u16 tx_tid_mask, u16 rx_tid_mask); +int ath6kl_wmi_set_credit_bypass(struct wmi *wmi, u8 if_idx, u8 eid, + u8 restore, u16 threshold); +int ath6kl_wmi_set_arp_offload_ip_cmd(struct wmi *wmi, u8 *ip_addrs); +int ath6kl_wmi_set_mcc_profile_cmd(struct wmi *wmi, u32 mcc_profile); +int ath6kl_wmi_set_seamless_mcc_scc_switch_freq_cmd(struct wmi *wmi, u32 freq); + +int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2); +int ath6kl_wmi_set_inact_cmd(struct wmi *wmi, u32 inacperiod); +int ath6kl_wmi_send_assoc_resp_cmd(struct wmi *wmi, u8 if_idx, + bool accept, u8 reason_code, u8 fw_status, u8 *sta_mac, u8 req_type); +int ath6kl_wmi_set_assoc_req_relay_cmd(struct wmi *wmi, u8 if_idx, + bool enabled); + +int ath6kl_wmi_set_ap_num_sta_cmd(struct wmi *wmi, u8 if_idx, u8 sta_nums); +int ath6kl_wmi_set_antdivcfg(struct wmi *wmi, u8 if_idx, u8 diversity_control); +int ath6kl_wmi_antdivstate_event_rx(struct ath6kl_vif *vif, u8 *datap, int len); + +int ath6kl_wmi_antdivstate_debug_event_rx(struct ath6kl_vif *vif, + u8 *datap, int len); +int ath6kl_antdiv_stat_debug(struct ath6kl *ar, u8 *buf, int buf_len); + +#endif /* WMI_H */ diff --git a/drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.c b/drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.c new file mode 100644 index 000000000000..e53e421f72f9 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 "core.h" +#include "wmi_btcoex.h" +#include "debug.h" + +#define BDATA_ANTCONF_OFFSET 4069 +#define BDATA_BTDEV_OFFSET 4070 + +#define BT_DEVICE_TYPE_QCOM 2 + +#define WMI_A2DP_CONFIG_FLAG_ALLOW_OPTIMIZATION (1 << 0) +#define WMI_A2DP_CONFIG_FLAG_IS_EDR_CAPABLE (1 << 1) +#define WMI_A2DP_CONFIG_FLAG_IS_BT_ROLE_MASTER (1 << 2) +#define WMI_A2DP_CONFIG_FLAG_IS_A2DP_HIGH_PRI (1 << 3) +#define WMI_A2DP_CONFIG_FLAG_FIND_BT_ROLE (1 << 4) +#define WMI_A2DP_CONFIG_FLAG_DIS_SCANCONN_STOMP (1 << 5) + +#define BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB 24 +#define BTCOEX_A2DP_EDR_MAX_BLUETOOTH_TIME \ + (40 << BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB) +#define BTCOEX_A2DP_BDR_MAX_BLUETOOTH_TIME \ + (70 << BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB) +#define BTCOEX_APMODE_A2DP_MAX_BLUETOOTH_TIME \ + (30 << BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB) +#define BTCOEX_APMODE_A2DP_BDR_MAX_BLUETOOTH_TIME \ + (60 << BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB) +#define BTCOEX_A2DP_BDR_MIN_BURST_CNT 5 +#define BTCOEX_A2DP_WLAN_MAX_DUR 25 +#define BTCOEX_A2DP_BDR_WLAN_MAX_DUR 20 +#define BTCOEX_APMODE_A2DP_BDR_WLAN_MAX_DUR 40 +#define BTCOEX_APMODE_A2DP_WLAN_MAX_DUR 70 +#define BTCOEX_APMODE_A2DP_MIN_BURST_CNT 10 + +#define WMI_SCO_CONFIG_FLAG_ALLOW_OPTIMIZATION (1 << 0) +#define WMI_SCO_CONFIG_FLAG_IS_EDR_CAPABLE (1 << 1) +#define WMI_SCO_CONFIG_FLAG_IS_BT_MASTER (1 << 2) +#define WMI_SCO_CONFIG_FLAG_FW_DETECT_OF_PER (1 << 3) +#define WMI_SCO_CONFIG_FLAG_DIS_SCANCONN_STOMP (1 << 4) + +static inline struct sk_buff *ath6kl_wmi_btcoex_get_new_buf(u32 size) +{ + struct sk_buff *skb; + + skb = ath6kl_buf_alloc(size); + if (!skb) + return NULL; + + skb_put(skb, size); + if (size) + memset(skb->data, 0, size); + + return skb; +} + +/* This is a temporary WAR since BTC related NL80211 commands are + * defined in Linux nl80211.h. + */ +#define NL80211_WMI_SET_BT_STATUS 0 +#define NL80211_WMI_SET_BT_PARAMS 1 +#define NL80211_WMI_SET_BT_FT_ANT 2 +#define NL80211_WMI_SET_COLOCATED_BT_DEV 3 +#define NL80211_WMI_SET_BT_INQUIRY_PAGE_CONFIG 4 +#define NL80211_WMI_SET_BT_SCO_CONFIG 5 +#define NL80211_WMI_SET_BT_A2DP_CONFIG 6 +#define NL80211_WMI_SET_BT_ACLCOEX_CONFIG 7 +#define NL80211_WMI_SET_BT_DEBUG 8 +#define NL80211_WMI_SET_BT_OPSTATUS 9 +#define NL80211_WMI_GET_BT_CONFIG 10 +#define NL80211_WMI_GET_BT_STATS 11 +#define NL80211_WMI_SET_BT_HID_CONFIG 12 + +static int ath6kl_get_wmi_cmd(int nl_cmd) +{ + int wmi_cmd = 0; + switch (nl_cmd) { + case NL80211_WMI_SET_BT_STATUS: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT status\n"); + wmi_cmd = WMI_SET_BT_STATUS_CMDID; + break; + + case NL80211_WMI_SET_BT_PARAMS: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT params\n"); + wmi_cmd = WMI_SET_BT_PARAMS_CMDID; + break; + + case NL80211_WMI_SET_BT_FT_ANT: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT FT antenna\n"); + wmi_cmd = WMI_SET_BTCOEX_FE_ANT_CMDID; + break; + + case NL80211_WMI_SET_COLOCATED_BT_DEV: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT collocated dev\n"); + wmi_cmd = WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID; + break; + + case NL80211_WMI_SET_BT_INQUIRY_PAGE_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT inquiry page\n"); + wmi_cmd = WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMDID; + break; + + case NL80211_WMI_SET_BT_SCO_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT sco config\n"); + wmi_cmd = WMI_SET_BTCOEX_SCO_CONFIG_CMDID; + break; + + case NL80211_WMI_SET_BT_A2DP_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT a2dp config\n"); + wmi_cmd = WMI_SET_BTCOEX_A2DP_CONFIG_CMDID; + break; + + case NL80211_WMI_SET_BT_ACLCOEX_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT acl config\n"); + wmi_cmd = WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMDID; + break; + + case NL80211_WMI_SET_BT_DEBUG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT bt debug\n"); + wmi_cmd = WMI_SET_BTCOEX_DEBUG_CMDID; + break; + + case NL80211_WMI_SET_BT_OPSTATUS: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT op status\n"); + wmi_cmd = WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID; + break; + + case NL80211_WMI_GET_BT_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Get BT config\n"); + wmi_cmd = WMI_GET_BTCOEX_CONFIG_CMDID; + break; + + case NL80211_WMI_GET_BT_STATS: + ath6kl_dbg(ATH6KL_DBG_WMI, "Get BT status\n"); + wmi_cmd = WMI_GET_BTCOEX_STATS_CMDID; + break; + + case NL80211_WMI_SET_BT_HID_CONFIG: + ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT hid config\n"); + wmi_cmd = WMI_SET_BTCOEX_HID_CONFIG_CMDID; + } + return wmi_cmd; +} + +void ath6kl_btcoex_adjust_params(struct ath6kl *ar, + int wmi_cmd, u8 *buf) +{ + switch (wmi_cmd) { + case WMI_SET_BTCOEX_FE_ANT_CMDID: + { + struct wmi_set_btcoex_fe_antenna_cmd *cmd = + (struct wmi_set_btcoex_fe_antenna_cmd *)buf; + + if (ar->version.target_ver == AR6004_HW_3_0_VERSION) { + /* use WMI_BTCOEX_FE_ANT_DUAL_SH_BT_HIGH_ISO + by default for McK2.0.4 */ + cmd->fe_antenna_type = + WMI_BTCOEX_FE_ANT_DUAL_SH_BT_HIGH_ISO; + } else { + /* fill in correct antenna configuration from + board data if valid */ + if (ar->fw_board[BDATA_ANTCONF_OFFSET]) + cmd->fe_antenna_type = + ar->fw_board[BDATA_ANTCONF_OFFSET]; + } + + /* disable green tx if it's enabled & BT is on */ + ar->green_tx_params.enable = false; + ath6kl_wmi_set_green_tx_params(ar->wmi, + &ar->green_tx_params); + } + break; + case WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID: + { + struct wmi_set_btcoex_colocated_bt_dev_cmd *cmd = + (struct wmi_set_btcoex_colocated_bt_dev_cmd *)buf; + /* Regardless of setting, force to use + * qcom-colocated BT. It's the current working mode. + */ + ar->btcoex_info.bt_vendor = BT_DEVICE_TYPE_QCOM; + cmd->colocated_bt_dev = BT_DEVICE_TYPE_QCOM; + } + break; + case WMI_SET_BTCOEX_A2DP_CONFIG_CMDID: + { + struct wmi_set_btcoex_a2dp_config_cmd *cmd = + (struct wmi_set_btcoex_a2dp_config_cmd *)buf; + struct btcoex_a2dp_config *a2dp_config = &cmd->a2dp_config; + struct btcoex_pspoll_a2dp_config *pspoll_config = + &cmd->pspoll_config; + struct ath6kl_vif *vif; + + if (ar->btcoex_info.bt_vendor == BT_DEVICE_TYPE_QCOM) { + a2dp_config->a2dp_flags |= cpu_to_le32( + WMI_A2DP_CONFIG_FLAG_ALLOW_OPTIMIZATION); + } else { + a2dp_config->a2dp_flags |= cpu_to_le32( + WMI_A2DP_CONFIG_FLAG_IS_A2DP_HIGH_PRI); + } + + if (a2dp_config->a2dp_flags & + WMI_A2DP_CONFIG_FLAG_IS_EDR_CAPABLE) { + /* A2DP EDR config overwrites */ + + if (a2dp_config->a2dp_flags & + WMI_A2DP_CONFIG_FLAG_IS_BT_ROLE_MASTER) { + + a2dp_config->a2dp_flags |= cpu_to_le32( + BTCOEX_A2DP_EDR_MAX_BLUETOOTH_TIME); + + /* disable stomping BT during WLAN scan + * or connection + */ + a2dp_config->a2dp_flags |= cpu_to_le32( + WMI_A2DP_CONFIG_FLAG_DIS_SCANCONN_STOMP + ); + } else { + /* use BDR parameter for A2DP EDR slave case */ + a2dp_config->a2dp_flags |= cpu_to_le32( + BTCOEX_A2DP_BDR_MAX_BLUETOOTH_TIME); + } + pspoll_config->a2dp_wlan_max_dur = + BTCOEX_A2DP_WLAN_MAX_DUR; + } else { + /* A2DP BDR config overwrites */ + a2dp_config->a2dp_flags |= cpu_to_le32( + BTCOEX_A2DP_BDR_MAX_BLUETOOTH_TIME); + pspoll_config->a2dp_min_bus_cnt = cpu_to_le32( + BTCOEX_A2DP_BDR_MIN_BURST_CNT); + pspoll_config->a2dp_wlan_max_dur = + BTCOEX_A2DP_BDR_WLAN_MAX_DUR; + } + + /* change A2DP parameters for AP mode*/ + vif = ath6kl_vif_first(ar); + if (!vif) + return; + + if (vif->nw_type == AP_NETWORK) { + if (a2dp_config->a2dp_flags & + WMI_A2DP_CONFIG_FLAG_IS_EDR_CAPABLE) { + pspoll_config->a2dp_wlan_max_dur = + BTCOEX_APMODE_A2DP_WLAN_MAX_DUR; + a2dp_config->a2dp_flags |= cpu_to_le32( + BTCOEX_APMODE_A2DP_MAX_BLUETOOTH_TIME); + } else { + pspoll_config->a2dp_wlan_max_dur = + BTCOEX_APMODE_A2DP_BDR_WLAN_MAX_DUR; + a2dp_config->a2dp_flags |= cpu_to_le32( + BTCOEX_APMODE_A2DP_BDR_MAX_BLUETOOTH_TIME); + } + + if (ar->version.target_ver == AR6004_HW_1_3_VERSION) { + pspoll_config->a2dp_min_bus_cnt = cpu_to_le32( + BTCOEX_APMODE_A2DP_MIN_BURST_CNT); + } + } + } + break; + case WMI_SET_BTCOEX_SCO_CONFIG_CMDID: + { + struct wmi_set_btcoex_sco_config_cmd *cmd = + (struct wmi_set_btcoex_sco_config_cmd *)buf; + struct btcoex_sco_config *sco_config = &cmd->sco_config; + if (sco_config->sco_flags & + WMI_SCO_CONFIG_FLAG_IS_EDR_CAPABLE) { + /* disable stomping BT during WLAN scan/connection */ + sco_config->sco_flags |= cpu_to_le32( + WMI_SCO_CONFIG_FLAG_DIS_SCANCONN_STOMP); + } + } + break; + + } +} + +int ath6kl_wmi_send_btcoex_cmd(struct ath6kl *ar, + u8 *buf, int len) +{ + struct sk_buff *skb; + u32 nl_cmd; + int wmi_cmd; + struct wmi *wmi = ar->wmi; + + nl_cmd = *(u32 *)buf; + buf += sizeof(u32); + len -= sizeof(u32); + wmi_cmd = ath6kl_get_wmi_cmd(nl_cmd); + if (wmi_cmd == 0) + return -ENOMEM; + + skb = ath6kl_wmi_btcoex_get_new_buf(len); + if (!skb) + return -ENOMEM; + + ath6kl_btcoex_adjust_params(ar, wmi_cmd, buf); + + memcpy(skb->data, buf, len); + + return ath6kl_wmi_cmd_send(wmi, 0, skb, + (enum wmi_cmd_id)wmi_cmd, + NO_SYNC_WMIFLAG); +} diff --git a/drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.h b/drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.h new file mode 100644 index 000000000000..2440b0eeb1d4 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 WMI protocol specified in the + * Wireless Module Interface (WMI). It includes definitions of all the + * commands and events. Commands are messages from the host to the WM. + * Events and Replies are messages from the WM to the host. + */ + +#ifndef WMI_BTCOEX_H +#define WMI_BTCOEX_H + +#include + +/* + * Indicates front end antenna configuration. + */ +enum wmi_btcoex_fe_ant_type { + WMI_BTCOEX_NOT_ENABLED = 0, + WMI_BTCOEX_FE_ANT_SINGLE = 1, + WMI_BTCOEX_FE_ANT_DUAL = 2, + WMI_BTCOEX_FE_ANT_DUAL_HIGH_ISO = 3, + WMI_BTCOEX_FE_ANT_DUAL_SH_BT_LOW_ISO = 4, + WMI_BTCOEX_FE_ANT_DUAL_SH_BT_HIGH_ISO = 5, + WMI_BTCOEX_FE_ANT_TRIPLE = 6, + WMI_BTCOEX_FE_ANT_TYPE_MAX +}; + +/* + * WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID + * Setting the Bluetooth operation status. + */ +struct wmi_btcoex_bt_op_status_cmd { + __le32 op_type; + __le32 op_status; + __le32 link_id; +} __packed; + +/* + * WMI_SET_BTCOEX_SCO_CONFIG_CMDID + * Setting the Bluetooth operation status. + */ +struct btcoex_sco_config { + __le32 sco_slots; + __le32 sco_idle_slots; + __le32 sco_flags; + __le32 link_id; +} __packed; + +struct btcoex_pspoll_mode_sco_config { + __le32 sco_cycle_force_trigger; + __le32 sco_data_res_to; + __le32 sco_stomp_duty_cycle_val; + __le32 sco_stomp_duty_cycle_max_val; + __le32 sco_pspoll_latency_fraction; +} __packed; + +struct btcoex_optmode_sco_config { + __le32 sco_stomp_cnt_in_100ms; + __le32 sco_cont_stomp_cnt_max; + __le32 sco_min_low_rate_mbps; + __le32 sco_low_rate_cnt; + __le32 sco_hi_pkt_ratio; + __le32 sco_max_aggr_size; + __le32 sco_null_backoff; +} __packed; + +struct btcoex_wlan_sco_config { + __le32 scan_interval; + __le32 max_scan_stomp_cnt; +} __packed; + +struct wmi_set_btcoex_sco_config_cmd { + struct btcoex_sco_config sco_config; + struct btcoex_pspoll_mode_sco_config sco_pspoll_config; + struct btcoex_optmode_sco_config sco_optmode_config; + struct btcoex_wlan_sco_config sco_wlan_config; +} __packed; +/* + * WMI_SET_BTCOEX_A2DP_CONFIG_CMDID + * Setting the Bluetooth A2DP configuration operation. + */ +struct btcoex_a2dp_config { + __le32 a2dp_flags; + __le32 link_id; +} __packed; + +struct btcoex_pspoll_a2dp_config { + __le32 a2dp_wlan_max_dur; + __le32 a2dp_min_bus_cnt; + __le32 a2dp_data_res_to; + +} __packed; + +struct btcoex_a2dp_optmode_config { + __le32 a2dp_min_low_rate_mbps; + __le32 a2dp_low_rate_cnt; + __le32 a2dp_hi_pkt_ratio; + __le32 a2dp_max_aggr_size; + __le32 a2dp_pkt_stomp_cnt; +} __packed; + +struct wmi_set_btcoex_a2dp_config_cmd { + struct btcoex_a2dp_config a2dp_config; + struct btcoex_pspoll_a2dp_config pspoll_config; + struct btcoex_a2dp_optmode_config optmode_config; +} __packed; + +struct wmi_set_btcoex_colocated_bt_dev_cmd { + u8 colocated_bt_dev; +} __packed; + +struct wmi_set_btcoex_fe_antenna_cmd { + u8 fe_antenna_type; +} __packed; + +struct btcoex_acl_config { + __le32 acl_wlan_medium_dur; + __le32 acl_bt_medium_dur; + __le32 acl_detect_timeout; + __le32 acl_pktcnt_lower_limit; + __le32 acl_iter_for_endis; + __le32 acl_pktcnt_upper_limit; + __le32 acl_flags; + __le32 link_id; +} __packed; + +struct btcoex_pspoll_acl_config { + __le32 acl_data_resp_timeout; +} __packed; + +struct btcoex_optmode_acl_config { + __le32 acl_min_low_rate_mbps; + __le32 acl_low_rate_cnt; + __le32 acl_high_pkt_ratio; + __le32 acl_max_aggr_size; + __le32 acl_pkt_stomp_cnt; +} __packed; + +struct wmi_set_btcoex_acl_config_cmd { + struct btcoex_acl_config acl_config; + struct btcoex_pspoll_acl_config acl_pspoll_config; + struct btcoex_optmode_acl_config acl_optmode_config; +} __packed; + +/* BT Coex */ +int ath6kl_wmi_set_btcoex_bt_op_status(struct wmi *wmi, u8 op_id, bool flag); +int ath6kl_wmi_set_btcoex_sco_op(struct wmi *wmi, bool esco, u32 tx_interval, + u32 tx_pkt_len); +int ath6kl_wmi_set_btcoex_a2dp_op(struct wmi *wmi, u32 role, u32 ver, u32 ven); +int ath6kl_wmi_set_btcoex_set_colocated_bt(struct wmi *wmi, u8 dev_type); +int ath6kl_wmi_set_btcoex_set_fe_antenna(struct wmi *wmi, u8 antenna_type); +int ath6kl_wmi_send_btcoex_cmd(struct ath6kl *ar, + u8 *buf, int len); +#endif /* WMI_BTCOEX_H */ diff --git a/include/linux/qca6234_pwrif.h b/include/linux/qca6234_pwrif.h new file mode 100644 index 000000000000..34b654dc6cfd --- /dev/null +++ b/include/linux/qca6234_pwrif.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __QCOM_WLAN_PWRIF_H__ +#define __QCOM_WLAN_PWRIF_H__ + +/* + * Headers for WLAN Power Interface Functions + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHIP_POWER_ON 1 +#define CHIP_POWER_OFF 0 + +extern int vos_chip_power_qca6234(int on); +extern void qca6234_wifi_gpio(bool on); +extern void qca6234_bt_gpio(bool on); +extern int BT_RF_status; +extern int WIFI_RF_status; + +#endif /* __QCOM_WLAN_PWRIF_H__ */ -- GitLab