From 065ea569db06206874bbfa18eb25ff6121aec09b Mon Sep 17 00:00:00 2001 From: lin <lin@kickpi.com> Date: Mon, 25 Aug 2025 12:27:08 +0000 Subject: [PATCH] add vs6621s support in kernel --- longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tdls.c | 1088 + longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mlme.c | 1208 + longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/Kconfig | 145 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/trace.h | 684 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_debugfs.c | 123 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iface.h | 678 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mlme.h | 95 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_log_to_file.h | 51 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tdls.h | 48 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_config.h | 100 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_user_com.c | 534 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_rx.c | 2102 ++ longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iface.c | 1395 + longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/trace.c | 28 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_rx.h | 314 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_msg.c | 2145 ++ longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_timer.h | 36 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_core.h | 899 longan/kernel/linux-4.9/drivers/misc/Kconfig | 1 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_core.c | 3530 +++ longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_msg.h | 721 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_io.c | 2825 ++ longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_regd.c | 370 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_regd.h | 60 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_timer.c | 240 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_compat.h | 659 longan/kernel/linux-4.9/arch/arm64/configs/sun50iw10p1smp_a133_android_defconfig | 4 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/sv6621s_mem_map.h | 114 longan/kernel/linux-4.9/drivers/net/wireless/Makefile | 1 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_log.c | 379 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/Kconfig | 9 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_cfg80211.c | 6186 ++++++ longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_log.h | 99 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iw.h | 345 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_cfg80211.h | 902 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iw.c | 4109 ++++ longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/Kconfig | 25 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/usb_boot.h | 94 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_boot.c | 1226 + longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_main.c | 2744 ++ longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb.h | 55 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_config.c | 481 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/Kconfig | 20 longan/kernel/linux-4.9/drivers/net/wireless/Kconfig | 1 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_log_to_file.c | 767 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dentry.h | 41 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_boot.h | 297 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/Kconfig | 10 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_debugfs.h | 24 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dentry.c | 188 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_host.h | 22 longan/kernel/linux-4.9/drivers/misc/Makefile | 1 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_host.c | 120 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_debugfs.h | 24 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/README.md | 2 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/README.md | 1 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_util.h | 338 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tx.h | 150 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_debugfs.c | 124 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_dump_mem.c | 284 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/genver.pl | 25 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/usb_boot.c | 469 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio.h | 325 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/.gitignore | 1 longan/kernel/linux-4.9/include/linux/platform_data/skw_platform_data.h | 227 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/Makefile | 67 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/boot_config.h | 134 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/Makefile | 61 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_recovery.h | 34 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_log.c | 591 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_log.h | 80 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_work.c | 420 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mbssid.c | 396 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_edma.h | 227 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_vendor.c | 495 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mbssid.h | 27 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_vendor.h | 162 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_calib.h | 93 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_mem_map.h | 116 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_log.c | 452 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_edma.c | 1380 + longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tx.c | 2020 ++ longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/README.md | 2 longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_log.h | 86 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_calib.c | 206 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dfs.c | 729 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_recovery.c | 561 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_util.c | 657 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_db.c | 3417 +++ longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_rx.c | 3419 +++ longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dfs.h | 179 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_work.h | 105 longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_db.h | 37 93 files changed, 56,466 insertions(+), 0 deletions(-) diff --git a/longan/kernel/linux-4.9/arch/arm64/configs/sun50iw10p1smp_a133_android_defconfig b/longan/kernel/linux-4.9/arch/arm64/configs/sun50iw10p1smp_a133_android_defconfig index b93af7e..390d13a 100644 --- a/longan/kernel/linux-4.9/arch/arm64/configs/sun50iw10p1smp_a133_android_defconfig +++ b/longan/kernel/linux-4.9/arch/arm64/configs/sun50iw10p1smp_a133_android_defconfig @@ -251,6 +251,9 @@ CONFIG_SUNXI_SST_STORAGE=y CONFIG_SUNXI_RFKILL=y CONFIG_SUNXI_BOOTEVENT=y +CONFIG_SEEKWAVE_BSP_DRIVERS=y +CONFIG_SKW_NO_CONFIG=y +CONFIG_SKW_SDIOHAL=m CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y @@ -293,6 +296,7 @@ CONFIG_AIC_WLAN_SUPPORT=y CONFIG_AIC8800_WLAN_SUPPORT=m CONFIG_AIC8800_BTLPM_SUPPORT=m +CONFIG_WLAN_VENDOR_SWT6621S=m # CONFIG_INPUT_MOUSEDEV is not set CONFIG_INPUT_EVDEV=y CONFIG_INPUT_KEYRESET=y diff --git a/longan/kernel/linux-4.9/drivers/misc/Kconfig b/longan/kernel/linux-4.9/drivers/misc/Kconfig index 6752129..cc11752 100644 --- a/longan/kernel/linux-4.9/drivers/misc/Kconfig +++ b/longan/kernel/linux-4.9/drivers/misc/Kconfig @@ -813,4 +813,5 @@ source "drivers/misc/sunxi-bootevent/Kconfig" source "drivers/misc/it6612/Kconfig" source "drivers/misc/ncs8801s/Kconfig" +source "drivers/misc/seekwaveplatform_lite/Kconfig" endmenu diff --git a/longan/kernel/linux-4.9/drivers/misc/Makefile b/longan/kernel/linux-4.9/drivers/misc/Makefile index ea76a1f..3b15b36 100644 --- a/longan/kernel/linux-4.9/drivers/misc/Makefile +++ b/longan/kernel/linux-4.9/drivers/misc/Makefile @@ -81,3 +81,4 @@ obj-$(CONFIG_SUNXI_BOOTEVENT) += sunxi-bootevent/ obj-$(CONFIG_IT66121_RGB_TO_HDMI) += it6612/ obj-$(CONFIG_NCS8801S_LVDS_EDP) += ncs8801s/ +obj-$(CONFIG_SEEKWAVE_BSP_DRIVERS) += seekwaveplatform_lite/ diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/Kconfig b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/Kconfig new file mode 100755 index 0000000..7d74bc1 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/Kconfig @@ -0,0 +1,25 @@ +menuconfig SEEKWAVE_BSP_DRIVERS + bool "SeekWave Platform Drivers For SeekWave Chip" + default n + help + This is support seekwave chip for incard board + if you want to buildin bsp driver + please say "y" + Thanks. + +config SEEKWAVE_PLD_RELEASE + bool "seekwave Platfrom support chip recoverymode" + depends on SEEKWAVE_BSP_DRIVERS + default n + +config SKW_NO_CONFIG + bool "skw no config dts" + depends on SEEKWAVE_BSP_DRIVERS + default n + +#seekwave`s wifi bluetooth device driver etc +if SEEKWAVE_BSP_DRIVERS +source "drivers/misc/seekwaveplatform_lite/usb/Kconfig" +source "drivers/misc/seekwaveplatform_lite/sdio/Kconfig" +source "drivers/misc/seekwaveplatform_lite/skwutil/Kconfig" +endif diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/Makefile b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/Makefile new file mode 100755 index 0000000..a524699 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/Makefile @@ -0,0 +1,61 @@ +# +#SeekWave Platform Drivers Makefile +# +#src := $(src) +PWD :=$(shell pwd) +CURFOLDER ?=$(pwd) +CURRENT_DIR := $(src) +@echo "current dir:$src" +$(info Source directory: $(src)) +#ccflags-y :=-Idrivers/misc/seekwaveplatform_lite/skwutil +ccflags-y :=-I$(CURRENT_DIR)/skwutil +ccflags-y +=-I$(CURRENT_DIR)/usb +ccflags-y +=-I$(CURRENT_DIR)/sdio + + +#ifdef CONFIG_SEEKWAVE_BSP_DRIVERS +#obj-$(CONFIG_SKW_USB) += usb/ +#obj-$(CONFIG_SKW_PCIE) += pcie/ +#obj-$(CONFIG_SKW_SDIOHAL) += sdio/ +#obj-$(CONFIG_SEEKWAVE_BSP_DRIVERS) += skwutil/ +#endif + +ifeq ($(CONFIG_SKW_BT),m) + ccflags-y += -DCONFIG_BT_SEEKWAVE +endif + +ifeq ($(CONFIG_64BIT),y) + ccflags-y += -DCONFIG_64BIT +endif + +ifneq ($(skw_extra_flags),) + ccflags-y += $(skw_extra_flags) -DSKW_EXT_INC +endif + +ifeq ($(CONFIG_SKW_NO_CONFIG),y) + ccflags-y += -DCONFIG_SKW_NO_CONFIG +endif +#ccflags-y += -DCONFIG_SV6160_LITE_FPGA + +obj-$(CONFIG_SKW_SDIOHAL) += skw_sdio_lite.o +skw_sdio_lite-y := ./sdio/skw_sdio_main.o +skw_sdio_lite-y += ./sdio/skw_sdio_rx.o +skw_sdio_lite-y += ./sdio/skw_sdio_debugfs.o +skw_sdio_lite-y += ./sdio/skw_sdio_log.o +skw_sdio_lite-y += ./sdio/skw_sdio_host.o +skw_sdio_lite-y += ./skwutil/skw_user_com.o +skw_sdio_lite-y += ./skwutil/skw_log_to_file.o +skw_sdio_lite-y += ./skwutil/skw_boot.o + +obj-$(CONFIG_SKW_USB) += skw_usb_lite.o +skw_usb_lite-y := ./usb/skw_usb_io.o +skw_usb_lite-y += ./usb/skw_usb_debugfs.o +skw_usb_lite-y += ./usb/skw_usb_log.o +#skw_usb_lite-y += ./usb/skw_test.o +skw_usb_lite-y += ./skwutil/skw_user_com.o +skw_usb_lite-y += ./skwutil/skw_log_to_file.o +skw_usb_lite-y += ./skwutil/skw_boot.o + +clean: + @rm -rf *.o *.ko *.mod.c *.order *.a *.builtin .*.cmd .*.d + diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/Kconfig b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/Kconfig new file mode 100755 index 0000000..13424a6 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/Kconfig @@ -0,0 +1,9 @@ +config SKW_SDIOHAL + tristate "Seekwave Platform SDIO Driver Support" + depends on SEEKWAVE_BSP_DRIVERS + default n + help + Enable this module for seekwave + chip sdio interface bus Support. + Please insmod this module before any other + seekwave subsystem. Thanks. diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/README.md b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/README.md new file mode 100755 index 0000000..3d41bf5 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/README.md @@ -0,0 +1,2 @@ +#seekwave tech sdio readme +#seekwave platform driver code diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio.h new file mode 100755 index 0000000..e382d01 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio.h @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2022 Seekwave Tech Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + + * 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 __SKW_SDIO_H__ +#define __SKW_SDIO_H__ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> +#include <linux/version.h> + +#include "../skwutil/skw_boot.h" +#include "skw_sdio_log.h" +#define skwsdio_log(fmt, args...) \ + pr_info("[SKWSDIO]:" fmt, ## args) + +#define skwsdio_err(fmt, args...) \ + pr_err("[SKWSDIO_ERR]:" fmt, ## args) + +#define skwsdio_data_pr(level, prefix_str, prefix_type, rowsize,\ + groupsize, buf, len, asscii)\ + do{if(loglevel) \ + print_hex_dump(level, prefix_str, prefix_type, rowsize,\ + groupsize, buf, len, asscii);\ + }while(0) + + +#define SKW_AP2CP_IRQ_REG 0x1B0 + +#define SKW_BUF_SIZE 2048 + +#define SKW_SDIO_SDMA 0 +#define SKW_SDIO_ADMA 1 + +#define SKW_SDIO_INBAND_IRQ 0 +#define SKW_SDIO_EXTERNAL_IRQ 1 + +#define SDIO_RX_TASK_PRIO 90 +#define SDIO_UPDATE_TASK_PRIO 91 + +#define SKW_SDIO_BLK_SIZE skw_sdio_blk_size +#define MAX_PAC_SIZE 0x700 +#define MAX2_PAC_SIZE 0x600 +#define MAX_PAC_COUNT 72 + +#define SKW_SDIO_NSIZE_BUF_SIZE SKW_SDIO_BLK_SIZE + +#define SKW_SDIO_READ 0 +#define SKW_SDIO_WRITE 1 + +#define SKW_SDIO_DATA_FIX 0 +#define SKW_SDIO_DATA_INC 1 + +#define MAX_IO_RW_BLK 511 + +#define FUNC_0 0 +#define FUNC_1 1 +#define MAX_FUNC_NUM 2 + +#define SKW_SDIO_DT_MODE_ADDR 0x0f +#define SKW_SDIO_PK_MODE_ADDR 0x20 + +#define SKW_SDIO_RESET_MODE_ADDR 0x1C +#define SKW_SDIO_CCCR_ABORT 0x06 +#define SDIO_INT_EXT 0x16 +#define SDIO_ABORT_TRANS 0x01 + +#define SKW_SDIO_FBR_REG 0x15C + +#define SKW_CHIP_ID0 0x40000000 //SV6160 chip id0 +#define SKW_CHIP_ID1 0x40000004 //SV6160 chip id1 +#define SKW_CHIP_ID2 0x40000008 //SV6160 chip id2 +#define SKW_CHIP_ID3 0x4000000C //SV6160 chip id3 +#define SKW_SMEM_POWERON1 0x40104000 //SMEM power on [2:3] WIFI [6:7] BT +#define SKW_SMEM_POWERON2 0x40108000 //SMEM power on [2:3] WIFI [6:7] BT +#define SKW_SERVICE_EN 0x40100000 //service enable [8:9] +#define SKW_REG_RW_LENGTH 4 +#define SKW_CHIP_ID_LENGTH 16 //SV6160 chip id lenght + +#define SKW_SDIO_ALIGN_4BYTE(a) (((a)+3)&(~3)) +#define SKW_SDIO_ALIGN_BLK(a) (((a)%SKW_SDIO_BLK_SIZE) ? \ + (((a)/SKW_SDIO_BLK_SIZE + 1)*SKW_SDIO_BLK_SIZE) : (a)) + +#define SDIO_VER_CCCR (0) + + +#define SKW_SDIO_CARD_OFFLINE 0x8000 +#define SKW_CARD_ONLINE(skw_sdio) \ + (atomic_read(&skw_sdio->online) < SKW_SDIO_CARD_OFFLINE) + +#define SKW_SDIO_RESET_CARD_VAL 0x08 +#define SKW_SDIO_RESET_CP 0x20 + +#define WIFI_SERVICE 0 +#define BT_SERVICE 1 + +#define SERVICE_START 0 +#define SERVICE_STOP 1 + +#define SKW_SDIO_V10 0 +#define SKW_SDIO_V20 1 + + +#define BSP_ATC_PORT 0 +#define BSP_LOG_PORT 1 +#define BT_DATA_PORT 2 +#define BT_CMD_PORT 3 +#define BT_AUDIO_PORT 4 +#define WIFI_CMD_PORT 5 +#define WIFI_DATA_PORT 6 +#define LOOPCHECK_PORT 7 +#define MAX_CH_NUM 8 + +#define SDIO2_BSP_ATC_PORT 0 +#define SDIO2_LOOPCHECK_PORT 1 +#define SDIO2_BT_CMD_PORT 2 +#define SDIO2_BT_AUDIO_PORT 3 +#define SDIO2_BT_ISOC_PORT 4 +#define SDIO2_BT_DATA_PORT 5 +#define SDIO2_WIFI_CMD_PORT 6 +#define SDIO2_WIFI_DATA_PORT 7 +#define SDIO2_WIFI_DATA1_PORT 8 +#define SDIO2_BSP_LOG_PORT 9 +#define SDIO2_BT_LOG_PORT 10 +#define SDIO2_BSP_UPDATE_PORT 11 +#define SDIO2_MAX_CH_NUM 12 + +struct skw_sdio_data_t { + struct task_struct *rx_thread; + struct completion rx_completed; + struct task_struct *update_thread; + struct completion update_completed; +#ifdef CONFIG_WAKELOCK + struct wake_lock rx_wl; +#else + struct wakeup_source *rx_ws; +#endif + atomic_t rx_wakelocked; + struct mutex transfer_mutex; + struct mutex except_mutex; + struct mutex service_mutex; + atomic_t resume_flag; + atomic_t online; + bool threads_exit; + bool adma_rx_enable; + bool pwrseq; + bool blk_size; + /* EXTERNAL_IRQ 0, INBAND_IRQ 1. */ + unsigned char irq_type; + atomic_t suspending; + int gpio_out; + int gpio_in; + unsigned int irq_num; + unsigned int irq_trigger_type; + struct sdio_func *sdio_func[MAX_FUNC_NUM]; + struct mmc_host *sdio_dev_host; + unsigned char *eof_buf; + + unsigned int next_size; + unsigned int remain_packet; + unsigned long long rx_packer_cnt; + char *next_size_buf; + + struct completion scan_done; + struct completion remove_done; + struct completion download_done; + int host_active; + int device_active; + struct completion device_wakeup; + char tx_req_map; + int resume_com; + int cp_state; + int chip_en; + unsigned int chip_id[SKW_CHIP_ID_LENGTH]; + struct seekwave_device *boot_data; + struct skw_log_data_t *log_data; + unsigned int service_state_map; + struct delayed_work skw_except_work; + int power_off; + unsigned int sdio_exti_gpio_state; + int suspend_wake_unlock_enable; + int service_index_map; + wait_queue_head_t wq; + u8 cp_fifo_status; + int multi_sdio_drivers; + u8 cp_downloaded_flag; +}; + +struct debug_vars { + u16 cmd_timeout_cnt; + u32 rx_inband_irq_cnt; + u32 rx_gpio_irq_cnt; + u32 rx_irq_statistics_cnt; + u32 rx_read_cnt; + u32 last_sent_wifi_cmd[3]; + u64 last_sent_time; + u64 last_rx_submit_time; + u64 host_assert_cp_time; + u64 cp_assert_time; + u64 last_irq_time; + u64 rx_irq_statistics_time; + u32 chn_irq_cnt[SDIO2_MAX_CH_NUM]; +#define CHN_IRQ_RECORD_NUM 3 + u64 chn_last_irq_time[SDIO2_MAX_CH_NUM][CHN_IRQ_RECORD_NUM]; + u64 last_irq_times[CHN_IRQ_RECORD_NUM]; + u64 last_clear_irq_times[CHN_IRQ_RECORD_NUM]; + u64 last_rx_read_times[CHN_IRQ_RECORD_NUM]; +}; +struct skw_log_data_t { + u16 cp_log_en; + u16 log_level; + u16 log_port; + uint32_t reg_val; + u32 smem_poweron; + u32 service_en; + u32 service_eb_val; +}; + + +struct sdio_port { + struct platform_device *pdev; + struct scatterlist *sg_rx; + int sg_index; + int total; + int sent_packet; + unsigned int type; + unsigned int channel; + rx_submit_fn rx_submit; + void *rx_data; + int state; + char *read_buffer; + int rx_rp; + int rx_wp; + char *write_buffer; + int length; + struct completion rx_done; + struct completion tx_done; + struct mutex rx_mutex; + int rx_packet; + int rx_count; + int tx_flow_ctrl; + int rx_flow_ctrl; + u16 next_seqno; + int timeout; +}; + + +//======================================================= +//debug sdio macro and Variable +//int glb_wifiready_done; +#define SKW_WIFIONLY_DEBUG 1 +//======================================================= +void skw_resume_check(void); +struct skw_sdio_data_t *skw_sdio_get_data(void); + +void skw_sdio_rx_up(struct skw_sdio_data_t *skw_sdio); +int skw_sdio_rx_thread(void *p); + +void skw_sdio_unlock_rx_ws(struct skw_sdio_data_t *skw_sdio); +int skw_recovery_mode(void); +int skw_sdio_sdma_write(unsigned char *src, unsigned int len); +int skw_sdio_sdma_read(unsigned char *src, unsigned int len); +int skw_sdio_adma_write(int portno, struct scatterlist *sgs, int sg_count, int total); +int skw_sdio_adma_read(struct skw_sdio_data_t *skw_sdio, struct scatterlist *sgs, int sg_count, int total); +int skw_sdio_dt_read(unsigned int address, void *buf, unsigned int len); +int skw_sdio_dt_write(unsigned int address, void *buf, unsigned int len); +int skw_sdio_readb(unsigned int address, unsigned char *data); +int skw_sdio_writeb(unsigned int address, unsigned char data); +int skw_sdio_writel(unsigned int address, void *data); +int skw_sdio_readl(unsigned int address, void *data); +int send_modem_service_command(u16 service, u16 command); +int send_modem_assert_command(void); +int skw_sdio_bind_platform_driver(struct sdio_func * func); +int skw_sdio_bind_WIFI_driver(struct sdio_func * func); +#ifndef CONFIG_BT_SEEKWAVE +int skw_sdio_bind_BT_driver(struct sdio_func * func); +#endif +int skw_sdio_bind_btseekwave_driver(struct sdio_func * func); +int skw_sdio_unbind_platform_driver(struct sdio_func *func); +int skw_sdio_unbind_WIFI_driver(struct sdio_func * func); +int skw_sdio_unbind_BT_driver(struct sdio_func * func); +int skw_boot_loader(struct seekwave_device *boot_data); +void send_host_suspend_indication(struct skw_sdio_data_t *skw_sdio); +void send_host_resume_indication(struct skw_sdio_data_t *skw_sdio); +int try_to_wakeup_modem(int portno); +int wakeup_modem(int portno); +void host_gpio_in_routine(int value); +void skw_sdio_inband_irq_handler(struct sdio_func *func); +void modem_notify_event(int event); +int loopcheck_send_data(char *buffer, int size); +void skw_get_port_statistic(char *buffer, int size); +void skw_get_sdio_config(char *buffer, int size); +int skw_sdio_cp_log_disable(int disable); +int skw_sdio_recovery_debug(int disable); +int skw_sdio_recovery_debug_status(void); +int skw_sdio_wifi_serv_debug(int enable); +int skw_sdio_wifi_serv_debug_status(void); +int skw_sdio_bt_serv_debug(int enable); +int skw_sdio_bt_serv_debug_status(void); +void reboot_to_change_bt_antenna_mode(char *mode); +void reboot_to_change_bt_uart1(char *mode); +void get_bt_antenna_mode(char *mode); +int skw_sdio_wifi_power_on(int power_on); +int skw_sdio_wifi_status(void); +int skw_sdio_dloader(int index); +int skw_sdio_poweron_mem(int index); +void skw_get_sdio_debug_info(char *buffer, int size); +void skw_get_assert_print_info(char *buffer, int size); +int skw_sdio_debug_log_open(void); +int skw_sdio_debug_log_close(void); +int skw_sdio_gpio_irq_pre_ops(void); +int skw_sdio_chk_cp_gpio_cfg(void); +void send_cp_wakeup_signal(struct skw_sdio_data_t *skw_sdio); +int skw_sdio_smem_poweron(void); // for smem +#endif /* SKW_SDIO_H */ diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_debugfs.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_debugfs.c new file mode 100755 index 0000000..72424d7 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_debugfs.c @@ -0,0 +1,124 @@ +/***************************************************************************** + * Copyright(c) 2020-2030 Seekwave Corporation. + * SEEKWAVE TECH LTD..CO + *Seekwave Platform the sdio log debug fs + *FILENAME:skw_sdio_debugfs.c + *DATE:2022-04-11 + *MODIFY: + * + **************************************************************************/ + +#include "skw_sdio_debugfs.h" +#include "skw_sdio_log.h" +#include "skw_sdio.h" + +static struct proc_dir_entry *skw_sdio_proc_root = NULL; + +static int skw_sdio_proc_show(struct seq_file *seq, void *v) +{ +#define SKW_BSP_CONFIG_INT(conf) \ + do { \ + seq_printf(seq, "%s=%d\n", #conf, conf); \ + } while (0) + +#define SKW_BSP_CONFIG_BOOL(conf) \ + do { \ + if (IS_ENABLED(conf)) \ + seq_printf(seq, "%s=y\n", #conf); \ + else \ + seq_printf(seq, "# %s is not set\n", #conf); \ + } while (0) + +#define SKW_BSP_CONFIG_STRING(conf) \ + do { \ + seq_printf(seq, "%s=\"%s\"\n", #conf, conf); \ + } while (0) + + seq_puts(seq, "\n"); + seq_printf(seq, "Kernel Version: \t%s\n", + UTS_RELEASE); + seq_puts(seq, "\n"); + + SKW_BSP_CONFIG_BOOL(CONFIG_SEEKWAVE_BSP_DRIVERS); + SKW_BSP_CONFIG_BOOL(CONFIG_SKW_SDIOHAL); + SKW_BSP_CONFIG_BOOL(CONFIG_SEEKWAVE_PLD_RELEASE); + + seq_puts(seq, "\n"); + + return 0; +} + +static int skw_sdio_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_sdio_proc_show, NULL); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_sdio_profile_proc_fops = { + .proc_open = skw_sdio_proc_open, + .proc_read = seq_read, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_sdio_profile_proc_fops = { + .owner = THIS_MODULE, + .open = skw_sdio_proc_open, + .read = seq_read, + .release = single_release, +}; +#endif + +struct proc_dir_entry *skw_sdio_procfs_file(struct proc_dir_entry *parent, + const char *name, umode_t mode, + const void *fops, void *data) +{ + struct proc_dir_entry *dentry = parent ? parent : skw_sdio_proc_root; + + if (!dentry) + return NULL; + + return proc_create_data(name, mode, dentry, fops, data); +} + +int skw_sdio_proc_init_ex(const char *name, umode_t mode, + const void *fops, void *data) +{ + if (!skw_sdio_proc_root) + return -1; + skw_sdio_procfs_file(skw_sdio_proc_root, name, mode, fops, NULL); + return 0; +} + +int skw_sdio_proc_init(void) +{ + skw_sdio_proc_root = proc_mkdir("skwsdio", NULL); + if (!skw_sdio_proc_root){ + pr_err("creat proc skwsdio failed\n"); + return -ENOMEM; + } + + skw_sdio_procfs_file(skw_sdio_proc_root,"profile", 0666, &skw_sdio_profile_proc_fops, NULL); + + return 0; +} + +void skw_sdio_proc_deinit(void) +{ + if (!skw_sdio_proc_root) + return; + proc_remove(skw_sdio_proc_root); +} + +int skw_sdio_debugfs_init(void) +{ + skw_sdio_dbg("%s :traced\n", __func__); + skw_sdio_proc_init(); + skw_sdio_create_debug_files(); + return 0; +} + +void skw_sdio_debugfs_deinit(void) +{ + skw_sdio_dbg("%s :traced\n", __func__); + skw_sdio_proc_deinit(); +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_debugfs.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_debugfs.h new file mode 100755 index 0000000..1d050b5 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_debugfs.h @@ -0,0 +1,24 @@ +/****************************************************************************** + * + * Copyright(c) 2020-2030 Seekwave Corporation. + * + *****************************************************************************/ +#ifndef __SKW_SDIO_DEBUGFS_H__ +#define __SKW_SDIO_DEBUGFS_H__ + +#include <linux/fs.h> +#include <linux/debugfs.h> +#include <linux/proc_fs.h> +#include <linux/scatterlist.h> +#include <generated/utsrelease.h> +#include "../skwutil/boot_config.h" +#include "../skwutil/skw_boot.h" + +int skw_sdio_debugfs_init(void); +void skw_sdio_debugfs_deinit(void); +struct proc_dir_entry *skw_sdio_procfs_file(struct proc_dir_entry *parent, + const char *name, umode_t mode, + const void *proc_fops, void *data); +int skw_sdio_proc_init_ex(const char *name, umode_t mode, + const void *fops, void *data); +#endif diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_host.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_host.c new file mode 100755 index 0000000..18c3c23 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_host.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2021 Seekwave Tech Inc. + * + * Filename : skw_sdio_host.c + * Abstract : This file is a implementation for Seekwave sdio function + * + * Authors :skw BSP team + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + + * 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 <linux/delay.h> +#include <linux/version.h> +#include <linux/mmc/card.h> +#include <linux/mmc/core.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sdio_func.h> +#include <linux/printk.h> +#include "skw_sdio.h" +#include "skw_sdio_log.h" +#include "skw_sdio_host.h" +#include "../skwutil/boot_config.h" +//#define CONFIG_SKW_HOST_PLATFORM_FULLHAN + +extern int sdio_reset_comm(struct mmc_card *card); +int skw_sdio_mmc_scan(int sd_id) +{ + int ret = 0; + pr_info("%s:[%d]\n", __func__, sd_id); +#if defined(CONFIG_SKW_HOST_PLATFORM_AMLOGIC) + //如果定义了CONFIG_SKW_HOST_PLATFORM_AMLOGIC,则调用extern_wifi_set_enable(1)函数 + extern_wifi_set_enable(1); +#elif defined(CONFIG_SKW_HOST_PLATFORM_FULLHAN) + pr_info("%s:[mmc%d:card init]\n", __func__, sd_id); + fh_sdio_card_scan(sd_id); //fullhan sdio card scan +#elif defined(CONFIG_SKW_HOST_PLATFORM_ALLWINER) + sunxi_wlan_set_power(1); + msleep(100); + sunxi_mmc_rescan_card(1); //allwiner sdio card rescan +#elif defined(CONFIG_SKW_HOST_PLATFORM_ROCKCHIP) + rockchip_wifi_power(1); + msleep(150); + rockchip_wifi_set_carddetect(1); +#else + pr_info("%s: no need skw self scan!!\n", __func__); +#endif + pr_info("%s:[-]\n", __func__); + return ret; +} + +int skw_sdio_mmc_rescan(int sd_id) +{ + int ret = 0; +#if defined(CONFIG_SKW_HOST_PLATFORM_AMLOGIC) + //如果定义了CONFIG_SKW_HOST_PLATFORM_AMLOGIC,则调用extern_wifi_set_enable(1)函数 + //extern_wifi_set_enable(1); +#elif defined(CONFIG_SKW_HOST_PLATFORM_FULLHAN) + skw_chip_power_reset(); + fh_sdio_card_scan(sd_id); //fullhan sdio card scan +#elif defined(CONFIG_SKW_HOST_PLATFORM_ALLWINER) + skw_chip_power_reset(); + msleep(100); + sunxi_mmc_rescan_card(1); //allwiner sdio card rescan +#elif defined(CONFIG_SKW_HOST_PLATFORM_ROCKCHIP) + rockchip_wifi_set_carddetect(0); + msleep(150); + skw_chip_power_reset(); + msleep(150); + rockchip_wifi_set_carddetect(1); +#else + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + if (skw_sdio) { +#if (KERNEL_VERSION(4, 18, 0) <= LINUX_VERSION_CODE && \ + LINUX_VERSION_CODE <= KERNEL_VERSION(5, 18, 19)) + if (skw_sdio->sdio_dev_host) { + sdio_claim_host(skw_sdio->sdio_func[FUNC_1]); + skw_chip_power_reset(); + msleep(100); + ret = mmc_sw_reset(skw_sdio->sdio_dev_host); + sdio_release_host(skw_sdio->sdio_func[FUNC_1]); + } else { + return -EINVAL; + } +#elif (KERNEL_VERSION(5, 19, 0) <= LINUX_VERSION_CODE) + if (skw_sdio->sdio_dev_host && skw_sdio->sdio_dev_host->card) { + sdio_claim_host(skw_sdio->sdio_func[FUNC_1]); + skw_chip_power_reset(); + msleep(100); + ret = mmc_sw_reset(skw_sdio->sdio_dev_host->card); + sdio_release_host(skw_sdio->sdio_func[FUNC_1]); + } else { + return -EINVAL; + } +#else + if (skw_sdio->sdio_dev_host && skw_sdio->sdio_dev_host->card) { + sdio_claim_host(skw_sdio->sdio_func[FUNC_1]); + skw_chip_power_reset(); + msleep(100); + ret = mmc_hw_reset((skw_sdio->sdio_dev_host)); + //ret = sdio_reset_comm((skw_sdio->sdio_dev_host->card)); + sdio_release_host(skw_sdio->sdio_func[FUNC_1]); + } else { + return -EINVAL; + } +#endif + } else { + skw_sdio_warn("sdio_dev_host is null\n"); + } +#endif + skw_sdio_info("[-]"); + return ret; +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_host.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_host.h new file mode 100755 index 0000000..c6d78a1 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_host.h @@ -0,0 +1,22 @@ +/***************************************************************** + *Copyright (C) 2021 Seekwave Tech Inc. + *Filename : skw_sdio_host .h + *Authors:seekwave platform + * + * This software is licensed under the terms of the the GNU + * General Public License version 2, as published by the Free + * Software Foundation, and may be copied, distributed, and + * modified under those terms. + * + * This program is distributed in the hope that it will be usefull, + * but without any warranty;without even the implied warranty of + * merchantability or fitness for a partcular purpose. See the + * GUN General Public License for more details. + * **************************************************************/ +#ifndef __SKW_SDIO_HOST_H__ +#define __SKW_SDIO_HOST_H__ +#include "skw_sdio.h" +//int skw_sdio_card_detect_change(int power_on); +int skw_sdio_mmc_rescan(int sd_id); +int skw_sdio_mmc_scan(int sd_id); +#endif /* __SKW_SDIO_HOST_H__ */ \ No newline at end of file diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_log.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_log.c new file mode 100755 index 0000000..0845d67 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_log.c @@ -0,0 +1,591 @@ +/************************************************************************** + * Copyright(c) 2020-2030 Seekwave Corporation. + * SEEKWAVE TECH LTD..CO + * + *Seekwave Platform the sdio log debug fs + *FILENAME:skw_sdio_log.c + *DATE:2022-04-11 + *MODIFY: + *Author:Jones.Jiang + **************************************************************************/ +#include <linux/uaccess.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include "skw_sdio.h" +#include "skw_sdio_log.h" +#include "skw_sdio_debugfs.h" + +extern char firmware_version[]; +static unsigned long skw_sdio_dbg_level; + +unsigned long skw_sdio_log_level(void) +{ + return skw_sdio_dbg_level; +} + +static void skw_sdio_set_log_level(int level) +{ + unsigned long dbg_level; + + dbg_level = skw_sdio_log_level() & 0xffff0000; + dbg_level |= ((level << 1) - 1); + + xchg(&skw_sdio_dbg_level, dbg_level); +} + +static void skw_sdio_enable_func_log(int func, bool enable) +{ + unsigned long dbg_level = skw_sdio_log_level(); + + if (enable) + dbg_level |= func; + else + dbg_level &= (~func); + + xchg(&skw_sdio_dbg_level, dbg_level); +} + +static int skw_sdio_log_show(struct seq_file *seq, void *data) +{ +#define SKW_SDIO_LOG_STATUS(s) (level & (s) ? "enable" : "disable") + + int i; + u32 level = skw_sdio_log_level(); + u8 *log_name[] = {"NONE", "ERROR", "WARNNING", "INFO", "DEBUG"}; + + for (i = 0; i < 5; i++) { + if (!(level & BIT(i))) + break; + } + // if (i >= 5 + if (i < 5) { + seq_printf(seq, "\nlog level: %s\n", log_name[i]); + } else { + seq_printf(seq, "\nlog level: NONE\n"); + } + seq_puts(seq, "\n"); + seq_printf(seq, "port0 log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_PORT0)); + seq_printf(seq, "port1 log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_PORT1)); + seq_printf(seq, "port2 log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_PORT2)); + seq_printf(seq, "port3 log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_PORT3)); + seq_printf(seq, "port4 log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_PORT4)); + seq_printf(seq, "port5 log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_PORT5)); + seq_printf(seq, "port6 log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_PORT6)); + seq_printf(seq, "port7 log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_PORT7)); + seq_printf(seq, "savelog : %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_SAVELOG)); + seq_printf(seq, "dump log: %s\n", SKW_SDIO_LOG_STATUS(SKW_SDIO_DUMP)); + + return 0; +} + +static int skw_sdio_log_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_sdio_log_show, inode->i_private); +} + +static int skw_sdio_log_control(const char *cmd, bool enable) +{ + if (!strcmp("dump", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_DUMP, enable); + else if (!strcmp("port0", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_PORT0, enable); + else if (!strcmp("port1", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_PORT1, enable); + else if (!strcmp("port2", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_PORT2, enable); + else if (!strcmp("port3", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_PORT3, enable); + else if (!strcmp("port4", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_PORT4, enable); + else if (!strcmp("port5", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_PORT5, enable); + else if (!strcmp("port6", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_PORT6, enable); + else if (!strcmp("port7", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_PORT7, enable); + else if (!strcmp("savelog", cmd)) + skw_sdio_enable_func_log(SKW_SDIO_SAVELOG, enable); + else if (!strcmp("debug", cmd)) + skw_sdio_set_log_level(SKW_SDIO_DEBUG); + else if (!strcmp("info", cmd)) + skw_sdio_set_log_level(SKW_SDIO_INFO); + else if (!strcmp("warn", cmd)) + skw_sdio_set_log_level(SKW_SDIO_WARNING); + else if (!strcmp("error", cmd)) + skw_sdio_set_log_level(SKW_SDIO_ERROR); + else + return -EINVAL; + + return 0; +} + +static ssize_t skw_sdio_log_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + int i, idx; + char cmd[32]; + bool enable = false; + + for (idx = 0, i = 0; i < len; i++) { + char c; + + if (get_user(c, buffer)) + return -EFAULT; + + switch (c) { + case ' ': + break; + + case ':': + cmd[idx] = 0; + if (!strcmp("enable", cmd)) + enable = true; + else + enable = false; + + idx = 0; + break; + + case '|': + case '\0': + case '\n': + cmd[idx] = 0; + skw_sdio_log_control(cmd, enable); + idx = 0; + break; + + default: + cmd[idx++] = c; + idx %= 32; + + break; + } + + buffer++; + } + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_sdio_log_proc_fops = { + .proc_open = skw_sdio_log_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_sdio_log_write, +}; +#else +static const struct file_operations skw_sdio_log_proc_fops = { + .owner = THIS_MODULE, + .open = skw_sdio_log_open, + .read = seq_read, + .release = single_release, + .write = skw_sdio_log_write, +}; +#endif + +static int skw_version_show(struct seq_file *seq, void *data) +{ + seq_printf(seq, "firmware info: %s\n", firmware_version ); + return 0; +} +static int skw_version_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_version_show, inode->i_private); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_version_proc_fops = { + .proc_open = skw_version_open, + .proc_read = seq_read, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_version_proc_fops = { + .owner = THIS_MODULE, + .open = skw_version_open, + .read = seq_read, + .release = single_release, +}; +#endif + +static int skw_port_statistic_show(struct seq_file *seq, void *data) +{ + char *statistic = kzalloc(2048, GFP_KERNEL); + if (statistic == NULL) { + printk(KERN_ERR "kzalloc statistic failed\n"); + return -ENOMEM; + } + skw_get_port_statistic(statistic, 2048); + seq_printf(seq, "Statistic:\n %s\n", statistic); + skw_get_assert_print_info(statistic, 2048); + seq_printf(seq, "sdio last irqs information:\n%s", statistic); + skw_get_sdio_debug_info(statistic, 2048); + seq_printf(seq, "\nsdio debug information:\n%s", statistic); + kfree(statistic); + return 0; +} +static int skw_port_statistic_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_port_statistic_show, inode->i_private); +} + + +static int skw_config_show(struct seq_file *seq, void *data) +{ + char *config = kzalloc(2048, GFP_KERNEL); + if (config == NULL) { + printk(KERN_ERR "kzalloc statistic failed\n"); + return -ENOMEM; + } + + skw_get_sdio_config(config, 2048); + seq_printf(seq, "config:\n %s\n", config); + kfree(config); + return 0; +} + +static int skw_config_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_config_show, inode->i_private); +} + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_port_statistic_proc_fops = { + .proc_open = skw_port_statistic_open, + .proc_read = seq_read, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_port_statistic_proc_fops = { + .owner = THIS_MODULE, + .open = skw_port_statistic_open, + .read = seq_read, + .release = single_release, +}; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_config_proc_fops = { + .proc_open = skw_config_open, + .proc_read = seq_read, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_config_proc_fops = { + .owner = THIS_MODULE, + .open = skw_config_open, + .read = seq_read, + .release = single_release, +}; +#endif + +static int skw_bluetooth_UART1_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, inode->i_private); +} + + +static ssize_t skw_bluetooth_UART1_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[32]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("enable", cmd, 6)) { + memset(cmd, 0, sizeof(cmd)); + reboot_to_change_bt_uart1(cmd); + printk("%s UART-HCI\n", cmd); + } + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_bluetooth_UART1_proc_fops = { + .proc_open = skw_bluetooth_UART1_open, + .proc_release = single_release, + .proc_write = skw_bluetooth_UART1_write, +}; +#else +static const struct file_operations skw_bluetooth_UART1_proc_fops = { + .owner = THIS_MODULE, + .open = skw_bluetooth_UART1_open, + .release = single_release, + .write = skw_bluetooth_UART1_write, +}; +#endif + +static int skw_bluetooth_antenna_show(struct seq_file *seq, void *data) +{ + char result[32]; + + memset(result, 0, sizeof(result)); + get_bt_antenna_mode(result); + if(strlen(result)) + seq_printf(seq, result); + return 0; +} +static int skw_bluetooth_antenna_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_bluetooth_antenna_show, inode->i_private); +} + + +static ssize_t skw_bluetooth_antenna_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[32]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("switch", cmd, 6)) { + memset(cmd, 0, sizeof(cmd)); + reboot_to_change_bt_antenna_mode(cmd); + printk("%s\n", cmd); + } + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_bluetooth_antenna_proc_fops = { + .proc_open = skw_bluetooth_antenna_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_bluetooth_antenna_write, +}; +#else +static const struct file_operations skw_bluetooth_antenna_proc_fops = { + .owner = THIS_MODULE, + .open = skw_bluetooth_antenna_open, + .read = seq_read, + .release = single_release, + .write = skw_bluetooth_antenna_write, +}; +#endif + +static int skw_recovery_debug_show(struct seq_file *seq, void *data) +{ + if (skw_sdio_recovery_debug_status()) + seq_printf(seq, "Disabled"); + else + seq_printf(seq, "Enabled"); + return 0; +} +static int skw_recovery_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_recovery_debug_show, inode->i_private); +} + + +static ssize_t skw_recovery_debug_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[16]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("disable", cmd, 7)) + skw_sdio_recovery_debug(1); + else if (!strncmp("enable", cmd, 6)) + skw_sdio_recovery_debug(0); + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_recovery_debug_proc_fops = { + .proc_open = skw_recovery_debug_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_recovery_debug_write, +}; +#else +static const struct file_operations skw_recovery_debug_proc_fops = { + .owner = THIS_MODULE, + .open = skw_recovery_debug_open, + .read = seq_read, + .release = single_release, + .write = skw_recovery_debug_write, +}; +#endif + +static int skw_wifi_serv_debug_show(struct seq_file *seq, void *data) +{ + if (skw_sdio_wifi_serv_debug_status()) + seq_printf(seq, "START"); + else + seq_printf(seq, "STOP"); + return 0; +} +static int skw_wifi_serv_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_wifi_serv_debug_show, inode->i_private); +} + + +static ssize_t skw_wifi_serv_debug_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[16]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("start", cmd, 5)) + skw_sdio_wifi_serv_debug(1); + else if (!strncmp("stop", cmd, 4)) + skw_sdio_wifi_serv_debug(0); + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_sdio_wifi_serv_proc_fops = { + .proc_open = skw_wifi_serv_debug_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_wifi_serv_debug_write, +}; +#else +static const struct file_operations skw_sdio_wifi_serv_proc_fops = { + .owner = THIS_MODULE, + .open = skw_wifi_serv_debug_open, + .read = seq_read, + .release = single_release, + .write = skw_wifi_serv_debug_write, +}; +#endif + +static int skw_bt_serv_debug_show(struct seq_file *seq, void *data) +{ + if (skw_sdio_bt_serv_debug_status()) + seq_printf(seq, "START"); + else + seq_printf(seq, "STOP"); + return 0; +} +static int skw_bt_serv_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_bt_serv_debug_show, inode->i_private); +} + + +static ssize_t skw_bt_serv_debug_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[16]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("start", cmd, 5)) + skw_sdio_bt_serv_debug(1); + else if (!strncmp("stop", cmd, 4)) + skw_sdio_bt_serv_debug(0); + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_sdio_bt_serv_proc_fops = { + .proc_open = skw_bt_serv_debug_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_bt_serv_debug_write, +}; +#else +static const struct file_operations skw_sdio_bt_serv_proc_fops = { + .owner = THIS_MODULE, + .open = skw_bt_serv_debug_open, + .read = seq_read, + .release = single_release, + .write = skw_bt_serv_debug_write, +}; +#endif + +static int skw_sdio_wifi_show(struct seq_file *seq, void *data) +{ + if (skw_sdio_wifi_status()) + seq_printf(seq, "PowerOn"); + else + seq_printf(seq, "PowerOff"); + return 0; +} +static int skw_sdio_wifi_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_sdio_wifi_show, inode->i_private); +} + + +static ssize_t skw_sdio_wifi_poweron(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[16]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("on", cmd, 2)) + skw_sdio_wifi_power_on(1); + else if (!strncmp("off", cmd, 3)) + skw_sdio_wifi_power_on(0); + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_sdio_wifi_proc_fops = { + .proc_open = skw_sdio_wifi_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_sdio_wifi_poweron, +}; +#else +static const struct file_operations skw_sdio_wifi_proc_fops = { + .owner = THIS_MODULE, + .open = skw_sdio_wifi_open, + .read = seq_read, + .release = single_release, + .write = skw_sdio_wifi_poweron, +}; +#endif + +void skw_sdio_log_level_init(void) +{ + skw_sdio_set_log_level(SKW_SDIO_INFO); + + skw_sdio_enable_func_log(SKW_SDIO_DUMP, false); + skw_sdio_enable_func_log(SKW_SDIO_PORT0, false); + skw_sdio_enable_func_log(SKW_SDIO_PORT1, false); + skw_sdio_enable_func_log(SKW_SDIO_PORT2, false); + skw_sdio_enable_func_log(SKW_SDIO_PORT3, false); + skw_sdio_enable_func_log(SKW_SDIO_PORT4, false); + skw_sdio_enable_func_log(SKW_SDIO_PORT5, false); + skw_sdio_enable_func_log(SKW_SDIO_PORT6, false); + skw_sdio_enable_func_log(SKW_SDIO_SAVELOG, false); + skw_sdio_enable_func_log(SKW_SDIO_PORT7, false); +} +void skw_sdio_create_debug_files(void) +{ + skw_sdio_proc_init_ex("log_level", 0666, &skw_sdio_log_proc_fops, NULL); + skw_sdio_proc_init_ex("Version", 0666, &skw_version_proc_fops, NULL); + skw_sdio_proc_init_ex("Statistic", 0666, &skw_port_statistic_proc_fops, NULL); + skw_sdio_proc_init_ex("config", 0666, &skw_config_proc_fops, NULL); + skw_sdio_proc_init_ex("recovery", 0666, &skw_recovery_debug_proc_fops, NULL); + skw_sdio_proc_init_ex("BT_ANT", 0666, &skw_bluetooth_antenna_proc_fops, NULL); + skw_sdio_proc_init_ex("BT_UART1", 0666, &skw_bluetooth_UART1_proc_fops, NULL); + skw_sdio_proc_init_ex("WiFi", 0666, &skw_sdio_wifi_proc_fops, NULL); + skw_sdio_proc_init_ex("wifi_service", 0666, &skw_sdio_wifi_serv_proc_fops, NULL); + skw_sdio_proc_init_ex("bt_service", 0666, &skw_sdio_bt_serv_proc_fops, NULL); +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_log.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_log.h new file mode 100755 index 0000000..c37e444 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_log.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * Copyright(c) 2020-2030 Seekwave Corporation. + * + *****************************************************************************/ +#ifndef __SKW_SDIO_LOG_H__ +#define __SKW_SDIO_LOG_H__ + +#define SKW_SDIO_ERROR BIT(0) +#define SKW_SDIO_WARNING BIT(1) +#define SKW_SDIO_INFO BIT(2) +#define SKW_SDIO_DEBUG BIT(3) + +#define SKW_SDIO_CMD BIT(16) +#define SKW_SDIO_EVENT BIT(17) +#define SKW_SDIO_SCAN BIT(18) +#define SKW_SDIO_TIMER BIT(19) +#define SKW_SDIO_STATE BIT(20) + +#define SKW_SDIO_PORT0 BIT(21) +#define SKW_SDIO_PORT1 BIT(22) +#define SKW_SDIO_PORT2 BIT(23) +#define SKW_SDIO_PORT3 BIT(24) +#define SKW_SDIO_PORT4 BIT(25) +#define SKW_SDIO_PORT5 BIT(26) +#define SKW_SDIO_PORT6 BIT(27) +#define SKW_SDIO_PORT7 BIT(28) +#define SKW_SDIO_SAVELOG BIT(29) +#define SKW_SDIO_DUMP BIT(31) + +unsigned long skw_sdio_log_level(void); + +#define skw_sdio_log(level, fmt, ...) \ + do { \ + if (skw_sdio_log_level() & level) \ + pr_err(fmt, ##__VA_ARGS__); \ + } while (0) + +#define skw_sdio_port_log(port_num, fmt, ...) \ + do { \ + if (skw_sdio_log_level() &(SKW_SDIO_PORT0<<port_num)) \ + pr_err(fmt, ##__VA_ARGS__); \ + } while (0) + +#define skw_port_log(port_num,fmt, ...) \ + skw_sdio_log((SKW_SDIO_PORT0<<port_num), "[PORT_LOG] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_sdio_err(fmt, ...) \ + skw_sdio_log(SKW_SDIO_ERROR, "[SKWSDIO ERROR] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_sdio_warn(fmt, ...) \ + skw_sdio_log(SKW_SDIO_WARNING, "[SKWSDIO WARN] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_sdio_info(fmt, ...) \ + skw_sdio_log(SKW_SDIO_INFO, "[SKWSDIO INFO] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_sdio_dbg(fmt, ...) \ + skw_sdio_log(SKW_SDIO_DEBUG, "[SKWSDIO DBG] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_sdio_hex_dump(prefix, buf, len) \ + do { \ + if (skw_sdio_log_level() & SKW_SDIO_DUMP) { \ + u8 str[32] = {0}; \ + snprintf(str, sizeof(str), "[SKWSDIO DUMP] %s", prefix); \ + print_hex_dump(KERN_ERR, str, \ + DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); \ + } \ + } while (0) +#if 0 +#define skw_sdio_port_log(port_num, fmt, ...) \ + do { \ + if (skw_sdio_log_level() &(SKW_SDIO_PORT0<<port_num)) \ + pr_err("[PORT_LOG] %s:"fmt,__func__, ##__VA_ARGS__); \ + } while (0) + +#endif +void skw_sdio_log_level_init(void); +void skw_sdio_create_debug_files(void); +#endif + diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_main.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_main.c new file mode 100755 index 0000000..31e34fb --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_main.c @@ -0,0 +1,2744 @@ +/* + * Copyright (C) 2021 Seekwave Tech Inc. + * + * Filename : skw_sdio.c + * Abstract : This file is a implementation for Seekwave sdio function + * + * Authors : + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + + * 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 <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/kthread.h> +#include <linux/version.h> +#include <linux/ktime.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/pm_runtime.h> +#include <linux/mmc/card.h> +#include <linux/mmc/core.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sdio_func.h> +#include "skw_sdio_log.h" +#include "skw_sdio_host.h" +#include "skw_sdio_debugfs.h" +#include "skw_sdio.h" +int bind_device=0; + +extern unsigned int test_debug; +extern int skw_use_sdma; +extern int g_chipen_pin; +extern struct sv6160_platform_data ucom_pdata; +extern struct sv6160_platform_data wifi_pdata; +extern struct sdio_port sdio_ports[]; + +static int card_id = SKW_MMC_HOST_SD_INDEX; +module_param(card_id, int, S_IRUGO); +module_param(bind_device, int, S_IRUGO); +module_param(test_debug, uint, S_IRUGO); +module_param(skw_use_sdma, int, S_IRUGO); + +#ifndef MMC_CAP2_SDIO_IRQ_NOTHREAD +#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17) +#endif + +#define skw_sdio_transfer_enter() mutex_lock(&skw_sdio->transfer_mutex) +#define skw_sdio_transfer_exit() mutex_unlock(&skw_sdio->transfer_mutex) + +irqreturn_t skw_gpio_irq_handler(int irq, void *dev_id); //interrupt +//int (*skw_dloader)(unsigned int subsys); +//static int skw_get_chipid(char *chip_id); +static int check_chipid(void); +static int skw_sdio_cp_reset(void); +static int skw_sdio_cp_service_ops(int service_ops); +static int skw_sdio_cpdebug_boot(void); +struct skw_sdio_data_t *g_skw_sdio_data; +static struct sdio_driver skw_sdio_driver; +static struct mutex dloader_mutex; +static int skw_sdio_set_dma_type(unsigned int address, unsigned int dma_type); +static int skw_sdio_slp_feature_en(unsigned int address, unsigned int slp_en); +static int skw_sdio_host_irq_init(unsigned int irq_gpio_num); +static int skw_WIFI_service_start(void); +static int skw_WIFI_service_stop(void); +static int skw_BT_service_start(void); +static int skw_BT_service_stop(void); +static int skw_sdio_host_check(struct skw_sdio_data_t *skw_sdio); +extern int sdio_reset_comm(struct mmc_card *card); +extern void kernel_restart(char *cmd); +extern void skw_sdio_exception_work(struct work_struct *work); +static int skw_sdio_reg_reset_cp(void); +extern int cp_detect_sleep_mode; +extern char skw_cp_ver; +extern int max_ch_num; +extern int max_pac_size; +extern int skw_sdio_blk_size; +extern char assert_context[]; +extern int assert_context_size; +extern struct debug_vars debug_infos; +extern void sunxi_wlan_set_power(int on); +extern void sunxi_mmc_rescan_card(unsigned ids); + +//======================================================= +//debug sdio macro and Variable +//======================================================= + +struct skw_sdio_data_t *skw_sdio_get_data(void) +{ + return g_skw_sdio_data; +} + +void skw_sdio_unlock_rx_ws(struct skw_sdio_data_t *skw_sdio) +{ + + if (!atomic_read(&skw_sdio->rx_wakelocked)) + return; + atomic_set(&skw_sdio->rx_wakelocked, 0); +#ifdef CONFIG_WAKELOCK + __pm_relax(&skw_sdio->rx_wl.ws); +#else + __pm_relax(skw_sdio->rx_ws); +#endif +} +static void skw_sdio_lock_rx_ws(struct skw_sdio_data_t *skw_sdio) +{ +// if (atomic_read(&skw_sdio->rx_wakelocked)) + return; + atomic_set(&skw_sdio->rx_wakelocked, 1); +#ifdef CONFIG_WAKELOCK + __pm_stay_awake(&skw_sdio->rx_wl.ws); +#else + __pm_stay_awake(skw_sdio->rx_ws); +#endif +} +static void skw_sdio_wakeup_source_init(struct skw_sdio_data_t *skw_sdio) +{ + if(skw_sdio) { +#ifdef CONFIG_WAKELOCK + wake_lock_init(&skw_sdio->rx_wl, WAKE_LOCK_SUSPEND,"skw_sdio_r_wakelock"); +#else + skw_sdio->rx_ws = skw_wakeup_source_register(NULL, "skw_sdio_r_wakelock"); +#endif + } +} +static void skw_sdio_wakeup_source_destroy(struct skw_sdio_data_t *skw_sdio) +{ + if(skw_sdio) { +#ifdef CONFIG_WAKELOCK + wake_lock_destroy(&skw_sdio->rx_wl); +#else + wakeup_source_unregister(skw_sdio->rx_ws); +#endif + } +} + +void skw_resume_check(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + unsigned int timeout; + + timeout = 0; + while((!atomic_read(&skw_sdio->resume_flag)) && (timeout++ < 20000)) + usleep_range(1500, 2000); +} + +static void skw_sdio_abort(int err_code) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func0 = skw_sdio->sdio_func[FUNC_0]; + unsigned char value; + int ret; + + if (err_code == -ETIMEDOUT) + return; + if(err_code != 0) { + if(skw_sdio->cp_state){ + skw_sdio_warn("cp_state:%d on recoving!!\n", skw_sdio->cp_state); + return; + } + //send_modem_assert_command(); + skw_sdio->cp_state = 1; + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(2000)); + } + sdio_claim_host(func0); + + value = sdio_readb(func0, SDIO_VER_CCCR, &ret); + + sdio_writeb(func0, SDIO_ABORT_TRANS, SKW_SDIO_CCCR_ABORT, &ret); + + value = sdio_readb(func0, SDIO_VER_CCCR, &ret); + skw_sdio_err("SDIO Abort, SDIO_VER_CCCR:0x%x\n", value); + + sdio_release_host(func0); +} + +int skw_sdio_sdma_write(unsigned char *src, unsigned int len) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_1]; + int blksize = func->cur_blksize; + int ret = 0; + + if (!src || len%4) { + skw_sdio_err("%s invalid para %p, %d\n", __func__, src, len); + return -1; + } + + len = (len + blksize -1)/blksize*blksize; + + skw_resume_check(); + skw_sdio_transfer_enter(); + sdio_claim_host(func); + ret = sdio_writesb(func, SKW_SDIO_PK_MODE_ADDR, src, len); + if (ret < 0) + skw_sdio_err("%s ret = %d\n", __func__, ret); + sdio_release_host(func); + if (ret) + skw_sdio_abort(ret); + skw_sdio_transfer_exit(); + + return ret; +} + +int skw_sdio_sdma_read(unsigned char *src, unsigned int len) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_1]; + int ret = 0; + skw_resume_check(); + skw_sdio_transfer_enter(); + sdio_claim_host(func); + ret = sdio_readsb(func, src, SKW_SDIO_PK_MODE_ADDR, len); + sdio_release_host(func); + if (ret != 0) + skw_sdio_abort(ret); + skw_sdio_transfer_exit(); + return ret; +} + +void *skw_get_bus_dev(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int time_count = 0; + if ((!skw_sdio) || (!skw_sdio->sdio_dev_host)) { + skw_sdio_err("%d try again get sdio bus dev \n", __LINE__); + do { + skw_sdio = skw_sdio_get_data(); + if (skw_sdio && skw_sdio->sdio_dev_host) { + break; + } + msleep(10); + time_count++; + } while (time_count < 50); + } + if ((!skw_sdio) || (!skw_sdio->sdio_dev_host)) { + skw_sdio_err("skw_sdio or dev_host is NULL!\n"); + return NULL; + } + return &skw_sdio->sdio_func[FUNC_1]->dev; +} +irqreturn_t skw_host_wake_irq_handler(int irq, void *dev_id) //interrupt +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int value = gpio_get_value(skw_sdio->gpio_in); + + skw_sdio_dbg("gpio request_irq=%d GPIO value %d!\n", irq, value); + return IRQ_HANDLED; +} + +static int skw_sdio_host_wake_irq_init(unsigned int gpio_num) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret = 0; + + skw_sdio->device_active = gpio_get_value(skw_sdio->gpio_in); + skw_sdio->irq_num = gpio_to_irq(skw_sdio->gpio_in); + skw_sdio->irq_trigger_type = IRQF_TRIGGER_RISING; + skw_sdio_info("gpio_irq %d\n", skw_sdio->irq_num); + if (skw_sdio->irq_num) { + ret = request_irq(skw_sdio->irq_num, skw_host_wake_irq_handler, + skw_sdio->irq_trigger_type | IRQF_ONESHOT, "skw-gpio-irq", NULL); + if (ret != 0) { + free_irq(skw_sdio->irq_num, NULL); + skw_sdio_err("%s request gpio irq fail ret=%d\n", __func__, ret); + return -1; + } else { + skw_sdio_dbg("gpio request_irq=%d GPIO value %d!\n", + skw_sdio->irq_num, skw_sdio->device_active); + } + } + enable_irq_wake(skw_sdio->irq_num); + return ret; +} + +int skw_sdio_gpio_irq_pre_ops(void) +{ + int ret = 0; + struct sdio_func *func; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + if (!skw_sdio->boot_data) + return -1; + + if (skw_sdio->boot_data->gpio_in < 0 || skw_sdio->boot_data->gpio_out < 0) { + skw_sdio_err("gpio_in is %d or gpio_out is %d invalid\n", + skw_sdio->boot_data->gpio_in, skw_sdio->boot_data->gpio_out); + skw_sdio->gpio_out = skw_sdio->boot_data->gpio_out; + skw_sdio->gpio_in = skw_sdio->boot_data->gpio_in; + return -1; + } + + skw_sdio->gpio_in = skw_sdio->boot_data->gpio_in; + skw_sdio->gpio_out = skw_sdio->boot_data->gpio_out; + if (skw_sdio->boot_data->gpio_in < 0 || + skw_sdio->boot_data->gpio_out < 0) { + if (skw_sdio->boot_data->gpio_in >= 0) + ret = skw_sdio_host_wake_irq_init( + skw_sdio->boot_data->gpio_in); + skw_sdio_warn(" no support gpio irq!!!\n"); + return ret; + } + switch (cp_detect_sleep_mode) { + case 0: + break; + case 1: + case 2: + func = skw_sdio->sdio_func[FUNC_1]; + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); + ret = skw_sdio_host_irq_init(skw_sdio->gpio_in); + break; + case 3: + gpio_set_value(skw_sdio->boot_data->gpio_out, 0); + msleep(100); + loopcheck_send_data("APGPIORDY", 9); + msleep(5); + gpio_set_value(skw_sdio->boot_data->gpio_out, 1); + func = skw_sdio->sdio_func[FUNC_1]; + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); + ret = skw_sdio_host_irq_init(skw_sdio->gpio_in); + break; + default: + break; + } + if (ret) + skw_sdio_err("gpio irq init fail\n"); + return ret; +} +static int skw_sdio_start_transfer(struct scatterlist *sgs, int sg_count, + int total, struct sdio_func *sdio_func, uint fix_inc, bool dir, uint addr) +{ + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; + struct mmc_host *host = sdio_func->card->host; + bool fifo = (fix_inc == SKW_SDIO_DATA_FIX); + uint fn_num = sdio_func->num; + uint blk_num, blk_size, max_blk_count, max_req_size; + int err_ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + + blk_size = SKW_SDIO_BLK_SIZE; + max_blk_count = min_t(unsigned int, host->max_blk_count, (uint)MAX_IO_RW_BLK); + max_req_size = min_t(unsigned int, max_blk_count*blk_size, host->max_req_size); + + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + if (total % blk_size != 0) { + skw_sdio_err("total %d not aligned to blk size\n", total); + return -1; + } + + blk_num = total / blk_size; + mmc_dat.sg = sgs; + mmc_dat.sg_len = sg_count; + mmc_dat.blksz = blk_size; + mmc_dat.blocks = blk_num; + mmc_dat.flags = dir ? MMC_DATA_WRITE : MMC_DATA_READ; + mmc_cmd.opcode = 53; /* SD_IO_RW_EXTENDED */ + mmc_cmd.arg = dir ? 1<<31 : 0; + mmc_cmd.arg |= (fn_num & 0x7) << 28; + mmc_cmd.arg |= 1<<27; + mmc_cmd.arg |= fifo ? 0 : 1<<26; + mmc_cmd.arg |= (addr & 0x1FFFF) << 9; + mmc_cmd.arg |= blk_num & 0x1FF; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + if (!fifo) + addr += total; + skw_sdio_dbg("total:%d sg_count:%d cmd_arg 0x%x\n", total, sg_count, mmc_cmd.arg); + sdio_claim_host(sdio_func); + if(skw_sdio->power_off == 1){ + sdio_release_host(sdio_func); + return 0; + } + mmc_set_data_timeout(&mmc_dat, sdio_func->card); + mmc_wait_for_req(host, &mmc_req); + sdio_release_host(sdio_func); + + err_ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; + if (err_ret != 0) { + skw_sdio_err("%s:CMD53 %s failed error=%d\n",__func__, + dir ? "write" : "read", err_ret); + } + return err_ret; +} + +int skw_sdio_adma_write(int portno, struct scatterlist *sgs, int sg_count, int total) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret = 0; + + skw_resume_check(); + skw_sdio_transfer_enter(); + if(skw_sdio->resume_com==0) + skw_sdio->resume_com = 1; + ret = skw_sdio_start_transfer(sgs, sg_count, SKW_SDIO_ALIGN_BLK(total), + skw_sdio->sdio_func[FUNC_1], SKW_SDIO_DATA_FIX, + SKW_SDIO_WRITE, SKW_SDIO_PK_MODE_ADDR); + if (ret) { + skw_sdio_abort(ret); + } else { + if (skw_sdio->device_active==0 && skw_sdio->irq_type && skw_sdio->gpio_in >= 0) + skw_sdio->device_active = gpio_get_value(skw_sdio->gpio_in); + } + skw_sdio_transfer_exit(); + + return ret; +} + +int skw_sdio_adma_read(struct skw_sdio_data_t *skw_sdio, struct scatterlist *sgs, int sg_count, int total) +{ + int ret = 0; + + skw_resume_check(); + skw_sdio_transfer_enter(); + ret = skw_sdio_start_transfer(sgs, sg_count, total, + skw_sdio->sdio_func[FUNC_1], SKW_SDIO_DATA_FIX, + SKW_SDIO_READ, SKW_SDIO_PK_MODE_ADDR); + if (ret) + skw_sdio_abort(ret); + skw_sdio_transfer_exit(); + return ret; +} + +static int skw_sdio_dt_set_address(unsigned int address) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_0]; + unsigned char value ; + int err = 0; + int i; + + sdio_claim_host(func); + for (i = 0; i < 4; i++) { + value = (address >> (8 * i)) & 0xFF; + sdio_writeb(func, value, SKW_SDIO_FBR_REG+i, &err); + if (err != 0) + break; + } + sdio_release_host(func); + + return err; +} + + +int skw_sdio_writel(unsigned int address, void *data) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_1]; + int ret = 0; + + skw_resume_check(); + skw_sdio_transfer_enter(); + + ret = skw_sdio_dt_set_address(address); + if (ret != 0) { + skw_sdio_transfer_exit(); + return ret; + } + + sdio_claim_host(func); + sdio_writel(func, *(unsigned int *)data, SKW_SDIO_DT_MODE_ADDR, &ret); + sdio_release_host(func); + skw_sdio_transfer_exit(); + + if (ret) { + skw_sdio_err("%s fail ret:%d, addr=0x%x\n", __func__, + ret, address); + skw_sdio_abort(ret); + } + skw_sdio_info("debug----the address=0x%x \n",address); + return ret; +} + +int skw_sdio_readl(unsigned int address, void *data) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_1]; + int ret = 0; + + + skw_resume_check(); + skw_sdio_transfer_enter(); + ret = skw_sdio_dt_set_address(address); + if (ret != 0) { + skw_sdio_transfer_exit(); + return ret; + } + + sdio_claim_host(func); + + *(unsigned int *)data = sdio_readl(func, SKW_SDIO_DT_MODE_ADDR, &ret); + + sdio_release_host(func); + skw_sdio_transfer_exit(); + if (ret) { + skw_sdio_err("%s fail ret:%d, addr=0x%x\n", __func__, ret, address); + skw_sdio_abort(ret); + } + + return ret; +} +/* + *command = 0: service_start else service stop + *service = 0: WIFI_service else BT service. + */ +int send_modem_service_command(u16 service, u16 command) +{ + u16 cmd; + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + if(command) + skw_sdio->service_state_map&= ~(1<<service); + //command = 1; + cmd = (service<<1)|command; + cmd = 1 << cmd; + if (cmd>>8) { + skw_sdio_err("service command error 0x%x!", cmd); + return -EINVAL; + } + skw_sdio_info("service = %d cmd %x\n", service, cmd); + if(skw_sdio->cp_state) + return -EINVAL; + + ret = skw_sdio_writeb(SKW_AP2CP_IRQ_REG, cmd & 0xff); + skw_sdio_info("ret = %d command %x\n", ret, command); + return ret; +} + +static unsigned int max_bytes(struct sdio_func *func) +{ + unsigned int mval = func->card->host->max_blk_size; + + if (func->card->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE) + mval = min(mval, func->cur_blksize); + else + mval = min(mval, func->max_blksize); + + if (func->card->quirks & MMC_QUIRK_BROKEN_BYTE_MODE_512) + return min(mval, 511u); + + /* maximum size for byte mode */ + return min(mval, 512u); +} + +int skw_sdio_dt_write(unsigned int address, void *buf, unsigned int len) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_1]; + unsigned int remainder = len; + unsigned int trans_len; + int ret = 0; + char *data= skw_sdio->next_size_buf; + + skw_resume_check(); + skw_sdio_transfer_enter(); + + ret = skw_sdio_dt_set_address(address); + if (ret != 0) { + skw_sdio_err("%s set address error!!!", __func__); + skw_sdio_transfer_exit(); + return ret; + } + + if(skw_sdio->resume_com==0) + skw_sdio->resume_com = 1; + sdio_claim_host(func); + while (remainder > 0) { + if (remainder >= func->cur_blksize) + trans_len = func->cur_blksize; + else + trans_len = min(remainder, max_bytes(func)); + + memcpy(data, buf,trans_len); + ret = sdio_memcpy_toio(func, SKW_SDIO_DT_MODE_ADDR, data, trans_len); + if (ret) { + skw_sdio_err("%s sdio_memcpy_toio failed!!!", __func__); + break; + } + remainder -= trans_len; + buf += trans_len; + } + sdio_release_host(func); + skw_sdio_transfer_exit(); + if (ret) { + skw_sdio_err("dt write fail ret:%d, address=0x%x\n", ret, address); + skw_sdio_abort(ret); + } + return ret; +} + +int skw_sdio_dt_read(unsigned int address, void *buf, unsigned int len) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_1]; + unsigned int remainder = len; + unsigned int trans_len; + int ret = 0; + + ret = skw_sdio_dt_set_address(address); + if (ret != 0) { + skw_sdio_err("set address error ret=%d !!!", ret); + return ret; + } + if(skw_sdio->resume_com==0) + skw_sdio->resume_com = 1; + skw_sdio_transfer_enter(); + sdio_claim_host(func); + while (remainder > 0) { + if (remainder >= func->cur_blksize) + trans_len = func->cur_blksize; + else + trans_len = min(remainder, max_bytes(func)); + ret = sdio_memcpy_fromio(func, buf, SKW_SDIO_DT_MODE_ADDR, trans_len); + if (ret) { + skw_sdio_err("sdio_memcpy_fromio: %p 0x%x ret=%d\n", buf, *(uint32_t *)buf, ret); + break; + } + remainder -= trans_len; + buf += trans_len; + } + sdio_release_host(func); + skw_sdio_transfer_exit(); + if (ret) { + skw_sdio_err("dt read fail ret:%d, address=0x%x\n", ret, address); + skw_sdio_abort(ret); + } + + return ret; +} + +int skw_sdio_readb(unsigned int address, unsigned char *value) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_0]; + unsigned char reg = 0; + int err = 0, i; + + for (i=0; i<2; i++) { + try_to_wakeup_modem(max_ch_num); + sdio_claim_host(func); + reg = sdio_readb(func, address, &err); + if (value) + *value = reg; + sdio_release_host(func); + if (err ==0) + break; + } + return err; +} + +int skw_sdio_writeb(unsigned int address, unsigned char value) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func = skw_sdio->sdio_func[FUNC_0]; + int err = 0, i; + + for (i=0; i<2; i++) { + try_to_wakeup_modem(max_ch_num); + sdio_claim_host(func); + sdio_writeb(func, value, address, &err); + sdio_release_host(func); + if (err == 0) + break; + } + return err; +} + +static int skw_sdio_host_irq_init(unsigned int irq_gpio_num) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret = 0; + + skw_sdio->irq_type = SKW_SDIO_EXTERNAL_IRQ; + skw_sdio->device_active = gpio_get_value(skw_sdio->gpio_in); + skw_sdio->irq_num = gpio_to_irq(skw_sdio->gpio_in); + skw_sdio->irq_trigger_type = IRQF_TRIGGER_RISING; + skw_sdio_info("gpio_In:%d,gpio_out:%d irq %d\n",skw_sdio->gpio_in, + skw_sdio->gpio_out, skw_sdio->irq_num); + if (skw_sdio->irq_num) { + ret = request_irq(skw_sdio->irq_num, skw_gpio_irq_handler, + skw_sdio->irq_trigger_type | IRQF_ONESHOT, "skw-gpio-irq", NULL); + if (ret != 0) { + free_irq(skw_sdio->irq_num, NULL); + skw_sdio_err("%s request gpio irq fail ret=%d\n", __func__, ret); + return -1; + } else { + skw_sdio->device_active = gpio_get_value(skw_sdio->gpio_in); + skw_sdio_info("gpio request_irq=%d GPIO value %d!\n", + skw_sdio->irq_num, skw_sdio->device_active); + } + } + enable_irq_wake(skw_sdio->irq_num); + skw_sdio_rx_up(skw_sdio); + return ret; +} + +static int skw_sdio_get_dev_func(struct sdio_func *func) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if (func->num >= MAX_FUNC_NUM) { + skw_sdio_err("func num err!!! func num is %d!!!", + func->num); + return -1; + } + skw_sdio_dbg("func num is %d.", func->num); + + if (func->num == 1) { + skw_sdio->sdio_func[FUNC_0] = kmemdup(func, sizeof(*func), + GFP_KERNEL); + if (!skw_sdio->sdio_func[FUNC_0]) { + return -ENOMEM; + } + skw_sdio->sdio_func[FUNC_0]->num = 0; + skw_sdio->sdio_func[FUNC_0]->max_blksize = SKW_SDIO_BLK_SIZE; + } + skw_sdio->sdio_func[FUNC_1] = func; + + return 0; +} + +extern u64 skw_local_clock(void); +void skw_sdio_inband_irq_handler(struct sdio_func *func) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func0 = skw_sdio->sdio_func[FUNC_0]; + int ret; + + if(!skw_sdio->cp_downloaded_flag){//fw not download done + return; + } + + if (!debug_infos.cp_assert_time) { + debug_infos.last_irq_time = skw_local_clock(); + debug_infos.last_irq_times[debug_infos.rx_inband_irq_cnt % CHN_IRQ_RECORD_NUM] = debug_infos.last_irq_time; + skw_sdio_dbg("irq coming %d\n", debug_infos.rx_inband_irq_cnt); + } + if (!SKW_CARD_ONLINE(skw_sdio)) { + skw_sdio_err("%s card offline\n", __func__); + return; + } + + skw_resume_check(); + + /* send cmd to clear cp int status */ + sdio_claim_host(func0); + try_to_wakeup_modem(max_ch_num); + sdio_f0_readb(func0, SDIO_CCCR_INTx, &ret); + if (!debug_infos.cp_assert_time) { + debug_infos.last_clear_irq_times[debug_infos.rx_inband_irq_cnt % CHN_IRQ_RECORD_NUM] = debug_infos.last_irq_time; + debug_infos.rx_inband_irq_cnt++; + } + sdio_release_host(func0); + if (ret < 0) + skw_sdio_err("%s error %d\n", __func__, ret); + skw_sdio_lock_rx_ws(skw_sdio); + skw_sdio_rx_up(skw_sdio); +} + +int skw_sdio_enable_async_irq(void) +{ + int ret = 0; + u8 reg; + skw_sdio_info("[+]"); + ret = skw_sdio_readb(SDIO_INT_EXT, ®); + if (ret) + return ret; + + reg |= 1 << 1; /* Enable Asynchronous Interrupt */ + + ret = skw_sdio_writeb(SDIO_INT_EXT, reg & 0xff); + if (ret) + return ret; + ret = skw_sdio_readb(SDIO_INT_EXT, ®); + if (ret) + return ret; + + if (!(reg & (1 << 1))) + skw_sdio_err("enable sdio async irq fail reg = 0x%x\n", reg); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +int skw_sdio_set_suspend_indication(void) +{ + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info("Enter slp_disable=%d\n", skw_sdio->boot_data->slp_disable); + ret = skw_sdio_writeb(SDIOHAL_CPLOG_TO_AP_SWITCH, 0x02); + if (ret < 0) { + skw_sdio_err("cls the log signal fail ret=%d\n", ret); + return ret; + } + ret = skw_sdio_writeb(SKW_AP2CP_IRQ_REG, BIT(5)); + if (ret < 0) { + skw_sdio_err("cls log irq fail ret=%d\n", ret); + return ret; + } + return 0; +} +int skw_sdio_set_resume_indication(void) +{ + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info("Enter slp_disable=%d\n", skw_sdio->boot_data->slp_disable); + ret = skw_sdio_writeb(SDIOHAL_CPLOG_TO_AP_SWITCH, 0x04); + if (ret < 0) { + skw_sdio_err("cls the log signal fail ret=%d\n", ret); + return ret; + } + ret = skw_sdio_writeb(SKW_AP2CP_IRQ_REG, BIT(5)); + if (ret < 0) { + skw_sdio_err("cls log irq fail ret=%d\n", ret); + return ret; + } + return 0; +} + +static int skw_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = container_of(dev, struct sdio_func, dev); + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret = 0; + + skw_sdio_dbg("[%s]enter\n", __func__); + skw_sdio_set_suspend_indication(); + + atomic_set(&skw_sdio->resume_flag, 0); + + if (SKW_CARD_ONLINE(skw_sdio)) + func->card->host->pm_flags |= MMC_PM_KEEP_POWER; + + func = skw_sdio->sdio_func[FUNC_1]; + send_host_suspend_indication(skw_sdio); + if ((skw_sdio->irq_type == SKW_SDIO_INBAND_IRQ) && skw_sdio->resume_com) { + sdio_claim_host(func); + try_to_wakeup_modem(max_ch_num); + msleep(1); + ret = sdio_release_irq(func); + sdio_release_host(func); + skw_sdio_dbg("%s sdio_release_irq ret = %d\n", __func__, ret); + } + atomic_set(&skw_sdio->suspending, 1); +#ifdef CONFIG_NO_SERVICE_PD + if (!skw_sdio->cp_state && skw_sdio->service_state_map==0) + skw_sdio->power_off = 1; +#endif + skw_sdio->resume_com = 0; + return ret; +} + +static int skw_sdio_resume(struct device *dev) +{ + struct sdio_func *func = container_of(dev, struct sdio_func, dev); + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret = 0; + + skw_sdio_dbg("[%s]enter\n", __func__); +#if defined(SKW_BOOT_DEBUG) + skw_dloader(2); +#endif + skw_sdio_set_resume_indication(); + skw_sdio->suspend_wake_unlock_enable =0; + if (SKW_CARD_ONLINE(skw_sdio)) + func->card->host->pm_flags &= ~MMC_PM_KEEP_POWER; + + func = skw_sdio->sdio_func[FUNC_1]; + send_host_resume_indication(skw_sdio); + atomic_set(&skw_sdio->resume_flag, 1); + if (!func->irq_handler && (skw_sdio->irq_type == SKW_SDIO_INBAND_IRQ)) { + sdio_claim_host(func); + try_to_wakeup_modem(max_ch_num); + ret = sdio_claim_irq(func, skw_sdio_inband_irq_handler); + sdio_release_host(func); + if(ret < 0) { + skw_sdio_err("%s sdio_claim_irq ret = %d\n", __func__, ret); + } else { + ret = skw_sdio_enable_async_irq(); + if (ret < 0) + skw_sdio_err("enable sdio async irq fail ret = %d\n", ret); + } + } + return ret; +} +#endif +irqreturn_t skw_gpio_irq_handler(int irq, void *dev_id) //interrupt +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + int value = gpio_get_value(skw_sdio->gpio_in); + //skw_sdio_info("debug ----line[%d]- cp active[%d]--enter\n", __LINE__,value); + if (!debug_infos.cp_assert_time) { + debug_infos.last_irq_time = skw_local_clock(); + debug_infos.last_irq_times[debug_infos.rx_gpio_irq_cnt % CHN_IRQ_RECORD_NUM] = debug_infos.last_irq_time; + debug_infos.rx_gpio_irq_cnt++; + skw_sdio_dbg("irq coming %d\n", debug_infos.rx_gpio_irq_cnt); + } + if (!SKW_CARD_ONLINE(skw_sdio)) { + skw_sdio_err("%s card offline\n", __func__); + return IRQ_HANDLED; + } + if (!skw_sdio->suspend_wake_unlock_enable) { + skw_sdio_dbg("suspend wake lock enable!!!!\n"); + skw_sdio_lock_rx_ws(skw_sdio); + } + + if (value && (skw_sdio->irq_type == SKW_SDIO_EXTERNAL_IRQ)){ + skw_sdio_rx_up(skw_sdio); + } + host_gpio_in_routine(value); + return IRQ_HANDLED; +} + +static int skw_check_cp_ready(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + if (wait_for_completion_timeout(&skw_sdio->download_done, + msecs_to_jiffies(2000)) == 0) { + skw_sdio_err("CP-ready timeout chip_en:%d, kick RXTHREAD\n", + gpio_get_value(skw_sdio->chip_en)); + skw_sdio_rx_up(skw_sdio); + return -ETIME; + } + return 0; +} +static void skw_sdio_launch_thread(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + init_completion(&skw_sdio->rx_completed); + skw_sdio_wakeup_source_init(skw_sdio); + skw_sdio->rx_thread = + kthread_create(skw_sdio_rx_thread, NULL, "skw_sdio_rx_thread"); + if (IS_ERR(skw_sdio->rx_thread)) { + skw_sdio_err("creat skw_sdio_rx_thread fail\n"); + return; + } + if (skw_sdio->rx_thread) { +#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE + sched_set_fifo_low(skw_sdio->rx_thread); +#else + struct sched_param param; + param.sched_priority = 1; + sched_setscheduler(skw_sdio->rx_thread, SCHED_FIFO, ¶m); +#endif + kthread_bind(skw_sdio->rx_thread, cpumask_first(cpu_online_mask)); + set_user_nice(skw_sdio->rx_thread, SKW_MIN_NICE); + wake_up_process(skw_sdio->rx_thread); + } else + skw_sdio_err("creat skw_sdio_rx_thread fail\n"); +} + +static int skw_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct mmc_host *host = func->card->host; + int ret; + + skw_sdio_info(": func->class=%x, vendor=0x%04x, device=0x%04x, " + "func_num=0x%04x, clock=%d blksize=0x%x max_blkcnt %d\n", + func->class, func->vendor, func->device, func->num, + host->ios.clock, func->cur_blksize, + func->card->host->max_blk_count); + + ret = skw_sdio_get_dev_func(func); + if (ret < 0) { + skw_sdio_err("get func err\n"); + return ret; + } + + skw_sdio->sdio_dev_host = skw_sdio->sdio_func[FUNC_1]->card->host; + if (skw_sdio->sdio_dev_host == NULL) { + kfree(skw_sdio->sdio_func[FUNC_1]); + skw_sdio->sdio_func[FUNC_1] = NULL; + skw_sdio_err("get host failed!!!"); + return -1; + } + + skw_sdio_debugfs_init(); + skw_sdio_launch_thread(); + if (!skw_sdio->pwrseq) { + struct sdio_func *func1 = skw_sdio->sdio_func[FUNC_1]; + /* Enable Function 1 */ + sdio_claim_host(func1); + ret = sdio_enable_func(func1); + + skw_sdio_info("sdio_enable_func ret=%d type %d\n", ret, skw_sdio->irq_type); + if(!ret) { + sdio_set_block_size(func1, SKW_SDIO_BLK_SIZE); + func1->max_blksize = SKW_SDIO_BLK_SIZE; + if (skw_sdio->irq_type == SKW_SDIO_INBAND_IRQ) { + if(sdio_claim_irq(func1,skw_sdio_inband_irq_handler)) { + skw_sdio_err("sdio_claim_irq failed\n"); + } else { + ret = skw_sdio_enable_async_irq(); + if (ret < 0) + skw_sdio_err("enable sdio async irq fail ret = %d\n", ret); + } + } + sdio_release_host(func1); + } else { + sdio_disable_func(func1); //disable func for remove + sdio_release_host(func1); + kfree(skw_sdio->sdio_func[FUNC_1]); + skw_sdio->sdio_func[FUNC_1] = NULL; + skw_sdio_err("enable func1 err!!! ret is %d\n", ret); + return ret; + } + skw_sdio->resume_com = 1; + skw_sdio_info("enable func1 done\n"); + } else + pm_runtime_put_noidle(&func->dev); + if (!SKW_CARD_ONLINE(skw_sdio)) + atomic_sub(SKW_SDIO_CARD_OFFLINE, &skw_sdio->online); + + check_chipid(); + if(!strncmp((char *)skw_sdio->chip_id,"SV6160LITE",10)) + { + struct sdio_func *func1 = skw_sdio->sdio_func[FUNC_1]; + skw_sdio_info("SV6160LITE chip\n"); + sdio_claim_host(func1); + skw_sdio->sdio_func[FUNC_0]->max_blksize = SKW_SDIO_BLK_SIZE; + sdio_set_block_size(func1, SKW_SDIO_BLK_SIZE); + func1->max_blksize = SKW_SDIO_BLK_SIZE; + sdio_release_host(func1); + } + /* the card is nonremovable */ + if (skw_sdio->sdio_dev_host->caps & MMC_CAP_NONREMOVABLE) { + //skw_sdio->sdio_dev_host->caps |= MMC_CAP_NONREMOVABLE; //| MMC_CAP_SDIO_IRQ; + skw_sdio_info("nonremovable card detected\n"); + } else { + skw_sdio_info("removable card detected default\n"); +#ifdef SKW_SUPPORT_MMC_NONREMOVABLE + skw_sdio->sdio_dev_host->caps |= MMC_CAP_NONREMOVABLE; +#endif + } + skw_sdio_bind_platform_driver(skw_sdio->sdio_func[FUNC_1]); +#ifdef CONFIG_BT_SEEKWAVE + skw_sdio_bind_btseekwave_driver(skw_sdio->sdio_func[FUNC_1]); +#endif + skw_sdio->service_state_map = 0; + skw_sdio->service_index_map = 0; + skw_sdio->host_active = 1; + skw_sdio->power_off = 0; + complete(&skw_sdio->scan_done); + return 0; +} + +static void skw_sdio_remove(struct sdio_func *func) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + skw_sdio_info("Enter\n"); + + complete(&skw_sdio->remove_done); + + if (skw_sdio->irq_type == SKW_SDIO_INBAND_IRQ) { + sdio_claim_host(skw_sdio->sdio_func[FUNC_1]); + sdio_release_irq(skw_sdio->sdio_func[FUNC_1]); + sdio_release_host(skw_sdio->sdio_func[FUNC_1]); + } + if (skw_sdio->irq_num >= 0) + free_irq(skw_sdio->irq_num, NULL); + + skw_sdio->host_active = 0; + skw_sdio_unbind_platform_driver(skw_sdio->sdio_func[FUNC_1]); + skw_sdio_unbind_WIFI_driver(skw_sdio->sdio_func[FUNC_1]); + skw_sdio_unbind_BT_driver(skw_sdio->sdio_func[FUNC_1]); + kfree(skw_sdio->sdio_func[FUNC_0]); +} + +void skw_sdio_stop_thread(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if (skw_sdio->rx_thread) { + skw_sdio->threads_exit = 1; + skw_sdio_rx_up(skw_sdio); + kthread_stop(skw_sdio->rx_thread); + skw_sdio->rx_thread = NULL; + skw_sdio_wakeup_source_destroy(skw_sdio); + } + skw_sdio_info("done\n"); +} + +static const struct dev_pm_ops skw_sdio_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(skw_sdio_suspend, skw_sdio_resume) +}; + +static const struct sdio_device_id skw_sdio_ids[] = { + //{ .compatible = "seekwave-sdio", }, + //{SDIO_DEVICE(0, 0)}, + {SDIO_DEVICE(0x3607, 0x6160)}, + {SDIO_DEVICE(0x1FFE, 0x6621)}, + {}, +}; + +static struct sdio_driver skw_sdio_driver = { + .probe = skw_sdio_probe, + .remove = skw_sdio_remove, + .name = "skw_sdio", + .id_table = skw_sdio_ids, + .drv = { + .pm = &skw_sdio_pm_ops, + }, +}; + +void skw_sdio_remove_card(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + sdio_unregister_driver(&skw_sdio_driver); + skw_sdio_info(" sdio_unregister_driver: %s\n", skw_sdio_driver.name); + wait_for_completion_timeout(&skw_sdio->remove_done, msecs_to_jiffies(100)); + skw_sdio_info("remove card end\n"); + +} + +int skw_sdio_scan_card(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret = 0; + + skw_sdio_info("sdio_scan_card\n"); + init_completion(&skw_sdio->scan_done); + init_completion(&skw_sdio->remove_done); + init_completion(&skw_sdio->download_done); + init_completion(&skw_sdio->device_wakeup); + init_waitqueue_head(&skw_sdio->wq); + //skw_sdio->irq_type = SKW_SDIO_EXTERNAL_IRQ; + skw_sdio->irq_type = SKW_SDIO_INBAND_IRQ; + ret = sdio_register_driver(&skw_sdio_driver); + if (ret != 0) { + skw_sdio_driver.name = "skw_sdio_lite"; + skw_sdio->multi_sdio_drivers = 1; + ret = sdio_register_driver(&skw_sdio_driver); + skw_sdio_info("sdio_register_driver rename :%s\n", skw_sdio_driver.name); + if (ret) + skw_sdio_err("sdio_register_driver error :%d\n", ret); + } + if (wait_for_completion_timeout(&skw_sdio->scan_done, msecs_to_jiffies(1000)) == 0) { + skw_sdio_err("wait scan card time out\n"); + return -ENODEV; + } + return ret; +} + +/**************************************************************** + *Description:sleep feature support en api + *Author:junwei.jiang + *Date:2023-06-14 + * ************************************************************/ +static int skw_sdio_slp_feature_en(unsigned int address, unsigned int slp_en) +{ + int ret = 0; + ret = skw_sdio_writeb(address,slp_en); + if(ret !=0){ + skw_sdio_err("no-sleep support en write fail, ret=%d\n",ret); + return -1; + } + skw_sdio_info("no-sleep_support_enable:%d\n ",slp_en); + return 0; +} + +/**************************************************************** + *Description:set the dma type SDMA, AMDA + *Author:junwei.jiang + *Date:2021-11-23 + * ************************************************************/ +static int skw_sdio_set_dma_type(unsigned int address, unsigned int dma_type) +{ + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + if(dma_type == SDMA){ + /*support the sdma so adma_rx_enable set 0*/ + skw_sdio->adma_rx_enable = 0; + } + if(!bind_device){ + ret = skw_sdio_writeb(address, ADMA); + if(ret !=0){ + skw_sdio_err("dma type write fail, ret=%d\n",ret); + return -1; + } + } + skw_sdio_info("dma_type=%d,adma_rx_enable:%d\n ",dma_type,skw_sdio->adma_rx_enable); + return 0; +} + +/**************************************************************** +*Description: +*Func:used the ap boot cp interface; +*Output:the dloader the bin to cp +*Return:0:pass; other : fail +*Author:JUNWEI.JIANG +*Date:2021-09-07 +****************************************************************/ +static int skw_sdio_boot_cp(int boot_mode) +{ + int ret =0; + //struct sdio_func *func; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + skw_sdio_set_dma_type(skw_sdio->boot_data->dma_type_addr, + skw_sdio->boot_data->dma_type); + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr, + skw_sdio->boot_data->slp_disable); + + //2:download the boot bin 1CPALL 2, wifi 3,bt + skw_sdio_info("DOWNLOAD BIN TO CP\n"); + skw_sdio_info("line:%d dram_dl_size = %d iram_dl_size = %d iram_dl_addr = 0x%x, dram_dl_addr = 0x%x\n", __LINE__, + skw_sdio->boot_data->dram_dl_size, skw_sdio->boot_data->iram_dl_size, + skw_sdio->boot_data->iram_dl_addr, skw_sdio->boot_data->dram_dl_addr); + if(skw_sdio->boot_data->dram_dl_size) + ret = skw_sdio_dt_write(skw_sdio->boot_data->dram_dl_addr, + skw_sdio->boot_data->dram_img_data,skw_sdio->boot_data->dram_dl_size); + if(skw_sdio->boot_data->iram_dl_size) + ret = skw_sdio_dt_write(skw_sdio->boot_data->iram_dl_addr, + skw_sdio->boot_data->iram_img_data,skw_sdio->boot_data->iram_dl_size); + if(ret !=0) + goto FAIL; + if (skw_sdio->cp_state && skw_sdio->sdio_exti_gpio_state) { + ret = skw_sdio_writeb(SKW_SDIO_AP2CP_EXTI_SETVAL, + (skw_sdio->sdio_exti_gpio_state) & 0xff); + skw_sdio_info("the -exti gpio state is %d\n", + (skw_sdio->sdio_exti_gpio_state) & 0xff); + } + //first boot need the setup cp first_dl_flag=0 is first + skw_sdio_info("line:%d write the download done flag\n", __LINE__); + skw_sdio->cp_downloaded_flag = 1; + ret |= skw_sdio_writeb(skw_sdio->boot_data->save_setup_addr, BIT(0)); + if (!skw_sdio->cp_state) + ret |= skw_check_cp_ready(); + + if (ret != 0) + goto FAIL; + +#if !defined(SKW_BOOT_MEMPOWERON) + if(!ret&&boot_mode==SKW_FIRST_BOOT) { + if(skw_sdio->chip_en < 0){ + skw_sdio_err("chip_en:%d Invalid Pls check HW !!\n", skw_sdio->chip_en); + ret = -1; + goto FAIL; + } + ret = skw_sdio_host_check(skw_sdio); + ret |= skw_sdio_chk_cp_gpio_cfg();//check the cp gpio cfg + if (ret < 0) + return ret; + + skw_sdio_bind_WIFI_driver(skw_sdio->sdio_func[FUNC_1]); +#ifndef CONFIG_BT_SEEKWAVE + skw_sdio_bind_BT_driver(skw_sdio->sdio_func[FUNC_1]); +#endif + } +#endif + skw_sdio_info("boot cp pass!!\n"); + return ret; +FAIL: + skw_sdio_err("fail ret=%d\n", ret); + //modem_notify_event(DEVICE_BLOCKED_EVENT); + return ret; +} +int skw_sdio_service_enable(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + uint32_t value = 0; + int ret = 0; + skw_sdio->log_data->service_en = SKW_SERVICE_EN; //service enable [8:9] + //get the service enable reg value + ret = skw_sdio_dt_read(skw_sdio->log_data->service_en, + (void *)&skw_sdio->log_data->service_eb_val, + SKW_REG_RW_LENGTH); + if (ret < 0) { + skw_sdio_err("read poweron cp reg fail ret=%d\n", ret); + return ret; + } + //set the service enable bit [8:9] = 2'b11 + value = (unsigned int)skw_sdio->log_data->service_eb_val; + value |= 0x300; + ret = skw_sdio_dt_write(skw_sdio->log_data->service_en, (void *)&value, + SKW_REG_RW_LENGTH); + if (ret < 0) { + skw_sdio_err("write poweron cp reg fail ret=%d\n", ret); + return ret; + } + skw_sdio_info(" reg write success\n"); + return ret; +} + +/****************************************************************/ +// add the sdio memdump poweron cp mem +//6160: +//wifi power on +//40104000[3:2]=2'b0 +//6160lite: +//40108000[3:2]=2'b0 +//6316: +//40108000[3:2]=2'b0 +/**********************************************************/ +int skw_sdio_smem_poweron(void) +{ + int ret = 0; + uint32_t value = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); +#if 1 + if(skw_sdio->log_data->smem_poweron) { + return ret; + } else { + skw_sdio_info("memdump poweron OPS !!\n"); + skw_sdio_service_enable(); + msleep(10); + skw_sdio->log_data->smem_poweron = 1; + } +#endif + //get chip id + skw_sdio_info(" the chip id:%s\n", (char *)skw_sdio->chip_id); + if (!strncmp((char *)skw_sdio->chip_id,"SV6160LITE",10)) { + skw_sdio->log_data->smem_poweron = SKW_SMEM_POWERON2; + } else if (!strncmp((char *)skw_sdio->chip_id,"SV6316",6)) { + skw_sdio->log_data->smem_poweron = SKW_SMEM_POWERON2; + } else if(!strncmp((char *)skw_sdio->chip_id,"SV6160",6)) { + skw_sdio->log_data->smem_poweron = SKW_SMEM_POWERON1;//SKW_SMEM_POWERON1 + } else { + skw_sdio_err("chip_id ls unkown not support memdump!\n"); + return -1; + } + //get the memdump addr value + ret = skw_sdio_dt_read(skw_sdio->log_data->smem_poweron, + (void *)&skw_sdio->log_data->reg_val, SKW_REG_RW_LENGTH); + if(ret <0) { + skw_sdio_err("read poweron cp reg fail ret=%d\n", ret); + return ret; + } + //if vaule [3:2] =2'b0 means power on done,else set the value [3:2]=2'b0 + if (!((unsigned int)skw_sdio->log_data->reg_val & (0x3<<2))) { + skw_sdio_info("cp has poweron done reg addr:%x value:%x\n", + skw_sdio->log_data->smem_poweron,(unsigned int)skw_sdio->log_data->reg_val); + return 0; + } + //set the memdump addr value 40108000[3:2]=2'b0; + value = (unsigned int)skw_sdio->log_data->reg_val; + value &= ~(0x3<<2); + skw_sdio_info("poweron cp reg addr:%x value:%x\n", + skw_sdio->log_data->smem_poweron,value); + ret = skw_sdio_dt_write(skw_sdio->log_data->smem_poweron, + (void *)&value, SKW_REG_RW_LENGTH); + if (ret <0) { + skw_sdio_err("write poweron cp reg fail ret=%d\n", ret); + return ret; + } + return 0; +} + +/************************************************************************ + *Decription:release CP close the CP log + *Author:junwei.jiang + *Date:2023-02-16 + *Modfiy: + * + ********************************************************************* */ +int skw_sdio_cp_log_disable(int disable) +{ + int ret = 0; + skw_sdio_info("Enter\n"); + ret = skw_sdio_writeb(SDIOHAL_CPLOG_TO_AP_SWITCH, disable); + if (ret < 0) { + skw_sdio_err("cls the log signal fail ret=%d\n", ret); + return ret; + } + ret = skw_sdio_writeb(SKW_AP2CP_IRQ_REG, BIT(5)); + if (ret < 0) { + skw_sdio_err("cls log irq fail ret=%d\n", ret); + return ret; + } + if (!disable) + skw_sdio_info(" enable the CP log \n"); + else + skw_sdio_info(" disable the CP log !!\n"); + + return 0; +} + + +/************************************************************************ + *Decription:send WIFI start command to modem. + *Author:junwei.jiang + *Date:2022-10-27 + *Modfiy: + * + ********************************************************************* */ +static int skw_WIFI_service_start(void) +{ + int ret; + + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info("Enter STARTWIFI cp_state:%d\n",skw_sdio->cp_state); + if (skw_sdio->service_state_map & (1<<WIFI_SERVICE)) + return 0; + + mutex_lock(&skw_sdio->except_mutex); + if (skw_sdio->service_state_map==0 && skw_sdio->power_off){ + skw_reinit_completion(skw_sdio->download_done); + skw_recovery_mode(); + } + skw_reinit_completion(skw_sdio->download_done); + ret = send_modem_service_command(WIFI_SERVICE, SERVICE_START); + if (ret==0) + ret = skw_check_cp_ready(); + mutex_unlock(&skw_sdio->except_mutex); + return ret; +} +/************************************************************************ + *Decription: send WIFI stop command to modem. + *Author:junwei.jiang + *Date:2022-10-27 + *Modfiy: + * + ********************************************************************* */ +static int skw_WIFI_service_stop(void) +{ + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); +#if CONFIG_NO_SERVICE_PD + struct sdio_func *sdio_func = skw_sdio->sdio_func[FUNC_1]; +#endif + skw_sdio_info("Enter,STOPWIFI cp_state:%d",skw_sdio->cp_state); + mutex_lock(&skw_sdio->except_mutex); + if (skw_sdio->service_state_map & (1<<WIFI_SERVICE)) + ret = send_modem_service_command(WIFI_SERVICE, SERVICE_STOP); + if (!skw_sdio->cp_state && ret==0 && skw_sdio->service_state_map==0) { +#if CONFIG_NO_SERVICE_PD + skw_sdio->service_index_map = 0; + skw_sdio->power_off = 1; + skw_sdio->cp_downloaded_flag = 0; + + sdio_claim_host(sdio_func); + skw_chip_set_power(0); + sdio_release_host(sdio_func); + skw_sdio_info("chip power off %d\n", ret); + +#endif + } + mutex_unlock(&skw_sdio->except_mutex); + return ret; +} +/************************************************************************ + *Decription:send BT start command to modem. + *Author:junwei.jiang + *Date:2022-10-27 + *Modfiy: + * + ********************************************************************* */ +static int skw_BT_service_start(void) +{ + int ret; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info("Enter cpstate=%d\n",skw_sdio->cp_state); + if(assert_context_size) + skw_sdio_info("%s\n", assert_context); + if (skw_sdio->service_state_map & (1<<BT_SERVICE)) + return 0; + + mutex_lock(&skw_sdio->except_mutex); + if (skw_sdio->service_state_map==0 && skw_sdio->power_off){ + skw_reinit_completion(skw_sdio->download_done); + skw_recovery_mode(); + } + skw_reinit_completion(skw_sdio->download_done); + ret =send_modem_service_command(BT_SERVICE, SERVICE_START); + if (!ret) + ret = skw_check_cp_ready(); + mutex_unlock(&skw_sdio->except_mutex); + return ret; +} + +/************************************************************************ + *Decription:send BT stop command to modem. + *Author:junwei.jiang + *Date:2022-10-27 + *Modfiy: + * + ********************************************************************* */ +static int skw_BT_service_stop(void) +{ + int ret = 0; + + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); +#if CONFIG_NO_SERVICE_PD + struct sdio_func *sdio_func = skw_sdio->sdio_func[FUNC_1]; +#endif + skw_sdio_info("Enter cpstate=%d\n",skw_sdio->cp_state); + + mutex_lock(&skw_sdio->except_mutex); + if (skw_sdio->service_state_map & (1<<BT_SERVICE) && !skw_sdio->cp_state) { + skw_reinit_completion(skw_sdio->download_done); + ret = send_modem_service_command(BT_SERVICE, SERVICE_STOP); + } + if (!skw_sdio->cp_state && ret==0 && skw_sdio->service_state_map==0) { +#if CONFIG_NO_SERVICE_PD + skw_sdio->service_index_map = 0; + skw_sdio->power_off = 1; + skw_sdio->cp_downloaded_flag = 0; + sdio_claim_host(sdio_func); + skw_chip_set_power(0); + sdio_release_host(sdio_func); + + skw_sdio_info("chip power off %d\n", ret); + +#endif + } + mutex_unlock(&skw_sdio->except_mutex); + return ret; +} + +/**************************************************************** +*Description: +*Func:used the ap boot cp interface; +*Output:the dloader the bin to cp +*Return:0:pass; other : fail +*Author:JUNWEI.JIANG +*Date:2021-09-07 +****************************************************************/ +static int skw_sdio_cp_service_ops(int service_ops) +{ + int ret =0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + switch(service_ops) + { + case SKW_WIFI_START: + ret = wait_event_interruptible_timeout(skw_sdio->wq, + !skw_sdio->cp_state, msecs_to_jiffies(2000)); + if (ret <= 0) { + skw_sdio_err("cp_state:%d cp not ready!!\n", + skw_sdio->cp_state); + } + ret = skw_WIFI_service_start(); + skw_sdio_info("-----WIFI SERIVCE START\n"); + break; + case SKW_WIFI_STOP: + ret =skw_WIFI_service_stop(); + skw_sdio_dbg("----WIFI SERVICE---STOP\n"); + break; + case SKW_BT_START: + { + ret = wait_event_interruptible_timeout(skw_sdio->wq, + !skw_sdio->cp_state, msecs_to_jiffies(2000)); + if (ret <= 0) { + skw_sdio_err("cp_state:%d cp not ready!!\n", + skw_sdio->cp_state); + } + ret=skw_BT_service_start(); + skw_sdio_dbg("-----BT SERIVCE --START\n"); + } + break; + case SKW_BT_STOP: + ret =skw_BT_service_stop(); + skw_sdio_dbg("-----BT SERVICE --STOP\n"); + break; + default: + break; + } + return ret; +} +int skw_sdio_chk_cp_gpio_cfg(void) +{ + int ret = 0; + unsigned char exti_val = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + if (skw_sdio->gpio_in < 0 || skw_sdio->gpio_out < 0) { + skw_sdio_info("gpio_in and gpio_out no config\n"); + return ret; + } + if (!cp_detect_sleep_mode) { + skw_sdio_err( + "GPIOOUT:%d cannot be operated, pls check gpio num or hw connect!!.\n", + skw_sdio->gpio_out); + return -1; + } + if (skw_sdio->gpio_in == skw_sdio->gpio_out) { + skw_sdio_err( + "gpio_in :%d and gpio_out:%d can't be the same!!\n", + skw_sdio->gpio_in, skw_sdio->gpio_out); + return -1; + } + ret = skw_sdio_readb(SKW_SDIO_CP2AP_EXTI_GETVAL, &exti_val); + if (ret) { + skw_sdio_err("read exti val fail exti:%d, ret=%d \n", exti_val, + ret); + return -1; + } + if (cp_detect_sleep_mode == 1 || cp_detect_sleep_mode == 2) { + if (exti_val != cp_detect_sleep_mode) { + skw_sdio_err("exti_val is %d error \n", exti_val); + return -1; + } + skw_sdio->sdio_exti_gpio_state = exti_val; + } else if (cp_detect_sleep_mode == 3) { + if (exti_val != 1 && exti_val != 2) { + skw_sdio_err( + "exti val:%d error, pls check your gpio num config or hw connect !!\n", + exti_val); + return -1; + } + skw_sdio->sdio_exti_gpio_state = exti_val; + } + skw_sdio_info("chk cp gpio cfg is %d\n", exti_val); + return ret; +} + +/**************************************************************** +*Description:skw_boot_loader +*Func:used the ap boot cp interface; +*Output:the dloader the bin to cp +*Return:0:pass; other : fail +*Author:JUNWEI.JIANG +*Date:2021-09-07 +****************************************************************/ +int skw_boot_loader(struct seekwave_device *boot_data) +{ + int ret =0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); +#if defined(SKW_BOOT_MEMPOWERON) + struct sdio_func *func; + skw_sdio->boot_data= boot_data; + skw_sdio->gpio_in = skw_sdio->boot_data->gpio_in; + skw_sdio->gpio_out = skw_sdio->boot_data->gpio_out; + skw_sdio_info("debug ------ line :%d \n", __LINE__); + if(!skw_sdio->boot_data->first_boot_flag ){ + skw_sdio_info("debug ------ line :%d \n", __LINE__); + if(skw_sdio->boot_data->iram_img_data) { + if(skw_sdio->boot_data->gpio_in >= 0) { + skw_sdio_set_dma_type(skw_sdio->boot_data->dma_type_addr, + skw_sdio->boot_data->dma_type); + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr, + skw_sdio->boot_data->slp_disable); + func = skw_sdio->sdio_func[FUNC_1]; + sdio_claim_host(func); + try_to_wakeup_modem(max_ch_num); + ret = sdio_release_irq(func); + sdio_release_host(func); + skw_sdio->irq_type = SKW_SDIO_EXTERNAL_IRQ; + ret = skw_sdio_host_irq_init(skw_sdio->gpio_in); + if (ret < 0) { + skw_sdio_err("gpio irq init fail\n"); + goto FAIL; + } + } else { + ret = skw_sdio_cpdebug_boot(); + if (ret < 0) { + skw_sdio_err("cpdebug_boot fail\n"); + goto FAIL; + } + } + if(skw_sdio->boot_data->chip_en < 0){ + skw_sdio_err("chip_en:%d Invalid Pls check HW !!\n", skw_sdio->boot_data->chip_en); + ret = -1; + return ret; + } + + ret = skw_sdio_host_check(skw_sdio); + if (ret < 0) + return ret; + skw_sdio_bind_WIFI_driver(skw_sdio->sdio_func[FUNC_1]); +#ifndef CONFIG_BT_SEEKWAVE + skw_sdio_bind_BT_driver(skw_sdio->sdio_func[FUNC_1]); +#endif + if(!skw_sdio->service_index_map&&skw_sdio->boot_data->iram_img_data){ + skw_chip_set_power(0); + skw_sdio->power_off = 1; + skw_sdio_info(" No service Power Down CP--!!!\n"); + }else{ + skw_sdio_err("sevice_index_map:%d or iram_img_data is NULL Pls CONFIG!!\n", skw_sdio->service_index_map); + ret = -1; + return ret; + } + }else{ + if(!skw_sdio->service_index_map&&skw_sdio->boot_data->iram_img_data){ +#ifdef CONFIG_SV6160_LITE_FPGA + skw_sdio_info(" FPGA DEUBG NO Down CP--!!!\n"); + skw_sdio_boot_cp(RECOVERY_BOOT); +#else + skw_sdio_info("first boot recovery dl firmware -!!!\n"); + skw_recovery_mode(); +#endif + }else{ + if(skw_sdio->boot_data->dl_base_img){ + skw_sdio_info("the dloader CP IMG dl_done_signal =%d the dl_base_addr =0x%x,offset=0x%x dl_size=0x%x---!\n", + skw_sdio->boot_data->dl_done_signal,skw_sdio->boot_data->dl_base_addr,skw_sdio->boot_data->dl_offset_addr, + skw_sdio->boot_data->dl_size); +#if 0 + print_hex_dump(KERN_ERR, "dump_data:", 0, 16, 1, + skw_sdio->boot_data->dl_base_img+skw_sdio->boot_data->dl_offset_addr, 32, 1); +#endif + if(skw_sdio->boot_data->dl_size){ + ret = skw_sdio_dt_write(skw_sdio->boot_data->dl_base_addr, + skw_sdio->boot_data->dl_base_img+skw_sdio->boot_data->dl_offset_addr, + skw_sdio->boot_data->dl_size); + if(ret) + goto FAIL; + } + ret = skw_sdio_writeb(skw_sdio->boot_data->save_setup_addr,skw_sdio->boot_data->dl_done_signal); + if(ret) + goto FAIL; + } + } + ret=skw_sdio_cp_service_ops(skw_sdio->boot_data->service_ops); + } +#else + skw_sdio->boot_data= boot_data; + if (skw_sdio->power_off) + boot_data->dl_module = RECOVERY_BOOT; + /*--------CP RESET RESCAN------------*/ + if (boot_data->dl_module== RECOVERY_BOOT) { + skw_sdio_info("rescan and reset CHIP\n"); + skw_recovery_mode(); + } else { + /*-------FIRST AP BOOT--------------*/ + if (!skw_sdio->boot_data->first_dl_flag) { + if (!strncmp((char *)skw_sdio->chip_id,"SV6160",10)){ + boot_data->chip_id = 0x6160; + skw_sdio_info("boot chip id 0x%x\n", boot_data->chip_id); + } + skw_sdio->chip_en = boot_data->chip_en; + if (skw_sdio->boot_data->iram_dl_size&& + skw_sdio->boot_data->dram_dl_size){ + ret=skw_sdio_boot_cp(SKW_FIRST_BOOT); + } else + ret=skw_sdio_cpdebug_boot(); + + if(ret !=0) + goto FAIL; + } + } + ret=skw_sdio_cp_service_ops(skw_sdio->boot_data->service_ops); +#endif + if(ret !=0) + goto FAIL; + + skw_sdio_info("boot loader ops end!!!\n"); + return 0; +FAIL: + skw_sdio_err("line:%d fail ret=%d\n", __LINE__, ret); + return ret; +} + +void get_bt_antenna_mode(char *mode) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct seekwave_device *boot_data = skw_sdio->boot_data; + u32 bt_antenna = boot_data->bt_antenna; + + if(bt_antenna==0) + return; + bt_antenna--; + if(!mode) + return; + if (bt_antenna) + sprintf(mode,"bt_antenna : alone\n"); + else + sprintf(mode,"bt_antenna : share\n"); +} + +void reboot_to_change_bt_antenna_mode(char *mode) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct seekwave_device *boot_data = skw_sdio->boot_data; + u32 *data = (u32 *) &boot_data->iram_img_data[boot_data->head_addr-4]; + u32 bt_antenna; + + if(boot_data->bt_antenna == 0) + return; + + bt_antenna = boot_data->bt_antenna - 1; + bt_antenna = 1 - bt_antenna; + data[0] = (bt_antenna) | 0x80000000; + if(!mode) + return; + if (bt_antenna==1) { + boot_data->bt_antenna = 2; + sprintf(mode,"bt_antenna : alone\n"); + } else { + boot_data->bt_antenna = 1; + sprintf(mode,"bt_antenna : share\n"); + } + //skw_recovery_mode(); + send_modem_assert_command(); +} + +void reboot_to_change_bt_uart1(char *mode) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct seekwave_device *boot_data = skw_sdio->boot_data; + u32 *data = (u32 *) &boot_data->iram_img_data[boot_data->head_addr-4]; + + if(data[0] & 0x80000000) + data[0] |= 0x0000008; + else + data[0] = 0x80000008; + //skw_recovery_mode(); + send_modem_assert_command(); +} + +/**************************************************************** +*Description:check dev ready +*Func:used the ap boot cp interface; +*Calls:sdio or usb +*Call By:host dev ready +*Input:NULL +*Output:pass :0 or fail ENODEV +*Others: +*Author:JUNWEI.JIANG +*Date:2022-06-09 +****************************************************************/ +int skw_reset_bus_dev(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + if (skw_sdio->chip_en) { + skw_chip_power_reset(); + } else { + skw_sdio_err("the chip_en is NULL\n"); + skw_sdio_reg_reset_cp(); + } + return 0; +} +static int skw_sdio_reg_reset_cp(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret; + /*reset CP register set value:0x4010001C 0x20:CP sys hif reset*/ + skw_sdio_info(" [+]\n"); + //cia force chip reset + if(!strncmp((char *)skw_sdio->chip_id,"SV6160",12)){ + skw_sdio_info("cia SV6160 force chip reset\n"); + ret =skw_sdio_writeb(0x125,BIT(0)); + }else{ + skw_sdio_info("cia SV6160 Lite cia reset\n"); + ret =skw_sdio_writeb(0x125,BIT(7)); + } + if(ret < 0){ + skw_sdio_err("cia the chip %s reset fail\n",(char *)skw_sdio->chip_id); + return ret; + } + skw_sdio_info("cia rest the chip %s\n",(char *)skw_sdio->chip_id); + return ret; +} + +/**************************************************************** +*Description:skw_sdio_reset_card +*Func:used the ap boot cp interface; +*Calls:boot data +*Call By:the ap host +*Input:the boot data informations +*Output:the dloader the bin to cp +*Return:0:pass; other : fail +*Others: +*Author:JUNWEI.JIANG +*Date:2021-10-11 +****************************************************************/ + +static int skw_sdio_reset_card(void) +{ + int ret; + int skw_sd_id = SKW_MMC_HOST_SD_INDEX; + skw_sdio_info(" [+]\n"); + ret = skw_sdio_mmc_rescan(skw_sd_id); + if (ret < 0) { + skw_sdio_err("the reset card fail try again\n"); + ret = skw_sdio_mmc_rescan(skw_sd_id); + } + skw_sdio_info(" [-]\n"); + return ret; +} + +static int skw_sdio_cp_reset(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret; + struct sdio_func *func = skw_sdio->sdio_func[FUNC_1]; + skw_sdio_info(" [+]\n"); + /* Disable Function 1 */ + if (skw_sdio->irq_type == SKW_SDIO_INBAND_IRQ){ + sdio_claim_host(skw_sdio->sdio_func[FUNC_1]); + ret=sdio_release_irq(skw_sdio->sdio_func[FUNC_1]); + sdio_release_host(skw_sdio->sdio_func[FUNC_1]); + if(ret < 0) + skw_sdio_err(" sdio_release_irq ret = %d\n", ret); + } + + ret = skw_sdio_reset_card(); + if (ret < 0) { + skw_sdio_err("the reset card fail \n"); + return ret; + } + sdio_claim_host(func); + ret = sdio_enable_func(func); + sdio_set_block_size(func, SKW_SDIO_BLK_SIZE); + func->max_blksize = SKW_SDIO_BLK_SIZE; + /* Enable Function 1 */ + if (skw_sdio->irq_type == SKW_SDIO_INBAND_IRQ){ + ret=sdio_claim_irq(skw_sdio->sdio_func[FUNC_1], skw_sdio_inband_irq_handler); + if(ret < 0) { + skw_sdio_err(" sdio_claim_irq ret = %d\n", ret); + } else { + ret = skw_sdio_enable_async_irq(); + if (ret < 0) + skw_sdio_err("enable sdio async irq fail ret = %d\n", ret); + } + } else { + skw_sdio_info("the CP is external irq\n"); + } + sdio_release_host(skw_sdio->sdio_func[FUNC_1]); + if ( ret < 0) { + skw_sdio_err("sdio_claim_irq fail\n"); + return ret; + } + skw_sdio_info("CP RESET OK!\n"); + return 0; +} +/**************************************************************** +*Description:skw_sdio_cpdebug_boot +*Func:used the ap boot cp interface; +*Others: +*Author:JUNWEI.JIANG +*Date:2022-07-15 +****************************************************************/ +static int skw_sdio_cpdebug_boot(void) +{ + int ret =0; + struct sdio_func *func; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info("not download CP from AP!!!!\n"); + skw_sdio_set_dma_type(skw_sdio->boot_data->dma_type_addr, + skw_sdio->boot_data->dma_type); + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr, + skw_sdio->boot_data->slp_disable); + if(skw_sdio->gpio_in >=0) { + func = skw_sdio->sdio_func[FUNC_1]; + sdio_claim_host(func); + try_to_wakeup_modem(max_ch_num); + ret = sdio_release_irq(func); + sdio_release_host(func); + skw_sdio->irq_type = SKW_SDIO_EXTERNAL_IRQ; + ret = skw_sdio_host_irq_init(skw_sdio->gpio_in); + } + skw_sdio_info(" CP DUEBGBOOT Done!!!\n"); + return ret; +} + +/**************************************************************** +*Description:skw_recovery_mode +*Func:used the ap boot cp interface; +*Calls:boot data +*Call By:the ap host +*Input:the boot data informations +*Output:reset cp +*Return:0:pass; other : fail +*Others: +*Author:JUNWEI.JIANG +*Date:2022-07-15 +****************************************************************/ +int skw_recovery_mode(void) +{ + int ret=0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info("the CHIPID:%s \n", (char *)&skw_sdio->chip_id); + ret = skw_sdio_recovery_debug_status(); + if(!skw_sdio->boot_data->dram_dl_size || + !skw_sdio->boot_data->iram_dl_size || ret) { + skw_sdio_err("CP DEBUG BOOT,AND NO NEED RECOVERY!!! \n"); + return -1; + } + //skw_reinit_completion(skw_sdio->download_done); + ret=skw_sdio_cp_reset(); + if(ret!=0){ + skw_sdio_err("CP RESET fail \n"); + return -1; + } + skw_sdio->power_off = 0; + skw_sdio->service_index_map = 0; + skw_sdio->cp_fifo_status = 0; + + ret = skw_sdio_boot_cp(RECOVERY_BOOT); + if(ret!=0){ + skw_sdio_err("CP RESET fail \n"); + return -1; + } + skw_sdio_info("Recovery ok\n"); + return ret; +} + +/**************************************************************** +*Description:poweron_bt_mem +*Func:used the ap boot cp interface; +*Calls:boot data +*Call By:the ap host +*Input:the boot data informations +*Output:the dloader the bin to cp +*Return:0:pass; other : fail +*Others: +*Author:JUNWEI.JIANG +*Date:2021-09-07 +****************************************************************/ +static int poweron_bt_mem(struct seekwave_device *boot_data) +{ + int ret=0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + ktime_t cur, stop; + +#if !defined(SKW_BOOT_DEBUG) + //ops the power on wifi reg; + unsigned char bt_poweron_reg = 0; + unsigned char tmp_val=0; +#endif + if (skw_sdio->service_state_map & (1<<BT_SERVICE)){ + skw_sdio_info("No need power on BT mem!!!\n"); + return 0; + } + skw_sdio_info("---Enter---\n"); + //set the poweron wifi flag 0x164 + skw_sdio_readb(SKW_SDIO_DL_CP2AP_BSP, &tmp_val); + ret = skw_sdio_writeb(SKW_SDIO_DL_POWERON_MODULE, SKW_BT); + ret = skw_sdio_writeb(SKW_AP2CP_IRQ_REG, BIT(6)); + if(ret !=0){ + skw_sdio_err("%s poweron fail \n", __func__); + return -1; + } + //the flag 164 tell the CP poweron the + stop = ktime_add_ns(ktime_get(), 2000); +#if !defined(SKW_BOOT_DEBUG) + do{ + //SDIO AON 180 + ret = skw_sdio_readb(SKW_SDIO_DL_CP2AP_BSP, &bt_poweron_reg); + cur = ktime_get(); + // get the cp poweron the wifi signal /184 + }while(tmp_val==bt_poweron_reg && !ret && ktime_before(cur, stop)); +#endif + if(ret !=0 ){ + skw_sdio_info("%s power on BT fail \n", __func__); + return -1; + } + //Poweron_Module = boot_data->dl_module; + skw_sdio->boot_data->dl_module = SKW_BT; + //ops the poweron the bt reg + skw_sdio_info("%s power on pass---time=%lld \n", __func__, ktime_sub(stop, cur)); + return 0; +} +/**************************************************************** +*Description:poweron_wifi_mem +*Func:used the ap boot cp interface; +*Calls:boot data +*Call By:the ap host +*Input:the boot data informations +*Output:the dloader the bin to cp +*Return:0:pass; other : fail +*Others: +*Author:JUNWEI.JIANG +*Date:2021-09-07 +****************************************************************/ +static int poweron_wifi_mem(struct seekwave_device *boot_data) +{ + int ret=0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + ktime_t cur, stop; + +#if !defined(SKW_BOOT_DEBUG) + //ops the power on wifi reg; + unsigned char wifi_poweron_reg = 0; + unsigned char tmp_val=0; +#endif + if (skw_sdio->service_state_map & (1<<WIFI_SERVICE)){ + skw_sdio_info(" No need power on WIFI mem!!! \n"); + return ret; + } + + skw_sdio_info("---Enter---\n"); + //set the poweron wifi flag 0x164 + msleep(5);//waiting cp ready... + skw_sdio_readb(SKW_SDIO_DL_CP2AP_BSP, &tmp_val); + ret = skw_sdio_writeb(SKW_SDIO_DL_POWERON_MODULE, SKW_WIFI); + ret = skw_sdio_writeb(SKW_AP2CP_IRQ_REG, BIT(6)); + if(ret !=0){ + skw_sdio_err("%s poweron fail \n", __func__); + return -1; + } + //the flag 164 tell the CP poweron the + stop = ktime_add_ns(ktime_get(), 2000); +#if !defined(SKW_BOOT_DEBUG) + do{ + //SDIO AON 180 + ret = skw_sdio_readb(SKW_SDIO_DL_CP2AP_BSP, &wifi_poweron_reg); + cur = ktime_get(); + // get the cp poweron the wifi signal /184 + }while(tmp_val==wifi_poweron_reg && ktime_before(cur, stop)); +#endif + //ret = ktime_before(cur, stop); + if(ret !=0 ){ + + skw_sdio_info(" power on WIFI fail \n"); + return -1; + } + skw_sdio->boot_data->dl_module = SKW_WIFI; + + //Poweron_Module = boot_data->dl_module; + skw_sdio_info("%s power on wifi pass \n", __func__); + return 0; +} + +/**************************************************************** +*Description:skw_sdio_poweron_mem +*Func:used the ap boot cp interface; +*Others: +*Author:JUNWEI.JIANG +*Date:2022-07-15 +****************************************************************/ +int skw_sdio_poweron_mem(int index) +{ + int ret=0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info(" Enter\n"); + //skw_sdio_poweron_mem(index); + switch(index) + { + case SKW_WIFI: + poweron_wifi_mem(skw_sdio->boot_data); + break; + case SKW_BT: + poweron_bt_mem(skw_sdio->boot_data); + break; + default: + skw_sdio_info("no need poweron service mem\n"); + break; + } + return ret; + +} +/**************************************************************** +*Description:skw_sdio_dloader +*Func:used the ap boot cp interface; +*Others: +*Author:JUNWEI.JIANG +*Date:2022-07-15 +****************************************************************/ +int skw_sdio_dloader(int service_index) +{ + int ret=0; +#if defined(SKW_BOOT_MEMPOWERON) + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info(" Enter\n"); + mutex_lock(&dloader_mutex); + ret=skw_sdio_poweron_mem(service_index); + if(ret){ + skw_sdio_err("power the module=%d fail\n", service_index); + mutex_unlock(&dloader_mutex); + return ret; + } + switch(service_index) + { + case SKW_BOOT: + //skw_sdio->boot_data->skw_dloader_module(SKW_BOOT); + skw_recovery_mode(); + break; + case SKW_WIFI: + if(skw_sdio->gpio_in >= 0&&skw_sdio->gpio_out >= 0){ + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr,1); + send_cp_wakeup_signal(skw_sdio); + skw_sdio->boot_data->skw_dloader_module(SKW_WIFI); + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr,0); + send_cp_wakeup_signal(skw_sdio); + }else{ + skw_sdio_warn("the gpio_in=%d gpio_out=%d is not set\n",skw_sdio->gpio_in,skw_sdio->gpio_out); + } + break; + case SKW_BT: + if(skw_sdio->gpio_in >= 0&&skw_sdio->gpio_out >= 0){ + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr,1); + send_cp_wakeup_signal(skw_sdio); + skw_sdio->boot_data->skw_dloader_module(SKW_BT); + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr,0); + send_cp_wakeup_signal(skw_sdio); + }else{ + skw_sdio_warn("the gpio_in=%d gpio_out=%d is not set\n",skw_sdio->gpio_in,skw_sdio->gpio_out); + } + break; + case SKW_ALL: + if(skw_sdio->gpio_in >= 0&&skw_sdio->gpio_out >= 0){ + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr,1); + send_cp_wakeup_signal(skw_sdio); + skw_sdio->boot_data->skw_dloader_module(SKW_ALL); + skw_sdio_slp_feature_en(skw_sdio->boot_data->slp_disable_addr,0); + send_cp_wakeup_signal(skw_sdio); + }else{ + skw_sdio_warn("the gpio_in=%d gpio_out=%d is not set\n",skw_sdio->gpio_in,skw_sdio->gpio_out); + } + break; + default: + skw_sdio_info("no need the dloader servce mem\n"); + break; + } + mutex_unlock(&dloader_mutex); +#else + skw_sdio_info("no need the dloader servce mem\n"); +#endif + return ret; + +} + +static int check_chipid(void) +{ + int ret; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + ret = skw_sdio_dt_read(SKW_CHIP_ID0, skw_sdio->chip_id, SKW_CHIP_ID_LENGTH); + if(!strncmp((char *)skw_sdio->chip_id,"SV6160LITE",10)){ + skw_cp_ver = SKW_SDIO_V20; + max_ch_num = SDIO2_MAX_CH_NUM; + max_pac_size = MAX2_PAC_SIZE; + skw_sdio_blk_size = 512; + skw_sdio_info("Chip id:%s used SDIO20 ", (char *)skw_sdio->chip_id); + }else if(!strncmp((char *)skw_sdio->chip_id,"SV6160",6)){ + skw_cp_ver = SKW_SDIO_V10; + max_ch_num = MAX_CH_NUM; + max_pac_size = MAX_PAC_SIZE; + skw_sdio_blk_size = 256; + skw_sdio_info("Chip id:%s used SDIO10",(char *)skw_sdio->chip_id); + }else if(!strncmp((char *)skw_sdio->chip_id,"SV6316",6)){ + skw_cp_ver = SKW_SDIO_V20; + max_ch_num = SDIO2_MAX_CH_NUM; + max_pac_size = MAX2_PAC_SIZE; + skw_sdio_blk_size = 512; + skw_sdio_info("Chip id:%s used SDIO20 ", (char *)skw_sdio->chip_id); + }else{ + skw_cp_ver = SKW_SDIO_V20; + max_ch_num = SDIO2_MAX_CH_NUM; + max_pac_size = MAX2_PAC_SIZE; + skw_sdio_blk_size = 512; + skw_sdio_info("Chip id:%s used SDIO20 ", (char *)skw_sdio->chip_id); + } + if(ret<0){ + skw_sdio_err("Get the chip id fail!!\n"); + return ret; + } + return 0; +} +#if 0 +static int skw_get_chipid(char *chip_id) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + chip_id = (char *)&skw_sdio->chip_id; + skw_sdio_info("---the chip id---%s\n", (char *)skw_sdio->chip_id); + return 0; +} +#endif + + +void skw_get_sdio_config(char *buffer, int size) +{ + int ret = 0; + + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_func *func1 = skw_sdio->sdio_func[FUNC_1]; + struct mmc_host *host = func1->card->host; + struct mmc_ios *ios = &host->ios; + const char *str; + + static const char *vdd_str[] = { + [8] = "2.0", + [9] = "2.1", + [10] = "2.2", + [11] = "2.3", + [12] = "2.4", + [13] = "2.5", + [14] = "2.6", + [15] = "2.7", + [16] = "2.8", + [17] = "2.9", + [18] = "3.0", + [19] = "3.1", + [20] = "3.2", + [21] = "3.3", + [22] = "3.4", + [23] = "3.5", + [24] = "3.6", + }; + + if(!buffer) { + skw_sdio_info("buffer is null!\n"); + return; + } + ret += sprintf(&buffer[ret], "sdio infomation:\n"); + ret += sprintf(&buffer[ret], "clock:\t\t%u Hz\n", ios->clock); + if (host->actual_clock) + ret += sprintf(&buffer[ret], "actual clock:\t%u Hz\n", host->actual_clock); + ret += sprintf(&buffer[ret], "vdd:\t\t%u ", ios->vdd); + if ((1 << ios->vdd) & MMC_VDD_165_195) + ret += sprintf(&buffer[ret], "(1.65 - 1.95 V)\n"); + else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1) + && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1]) + ret += sprintf(&buffer[ret], "(%s ~ %s V)\n", vdd_str[ios->vdd], + vdd_str[ios->vdd + 1]); + else + ret += sprintf(&buffer[ret], "(invalid)\n"); + + switch (ios->bus_mode) { + case MMC_BUSMODE_OPENDRAIN: + str = "open drain"; + break; + case MMC_BUSMODE_PUSHPULL: + str = "push-pull"; + break; + default: + str = "invalid"; + break; + } + ret += sprintf(&buffer[ret], "bus mode:\t%u (%s)\n", ios->bus_mode, str); + + switch (ios->chip_select) { + case MMC_CS_DONTCARE: + str = "don't care"; + break; + case MMC_CS_HIGH: + str = "active high"; + break; + case MMC_CS_LOW: + str = "active low"; + break; + default: + str = "invalid"; + break; + } + ret += sprintf(&buffer[ret], "chip select:\t%u (%s)\n", ios->chip_select, str); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + str = "off"; + break; + case MMC_POWER_UP: + str = "up"; + break; + case MMC_POWER_ON: + str = "on"; + break; + default: + str = "invalid"; + break; + } + ret += sprintf(&buffer[ret], "power mode:\t%u (%s)\n", ios->power_mode, str); + ret += sprintf(&buffer[ret], "bus width:\t%u (%u bits)\n", + ios->bus_width, 1 << ios->bus_width); + + switch (ios->timing) { + case MMC_TIMING_LEGACY: + str = "legacy"; + break; + case MMC_TIMING_MMC_HS: + str = "mmc high-speed"; + break; + case MMC_TIMING_SD_HS: + str = "sd high-speed"; + break; + case MMC_TIMING_UHS_SDR12: + str = "sd uhs SDR12"; + break; + case MMC_TIMING_UHS_SDR25: + str = "sd uhs SDR25"; + break; + case MMC_TIMING_UHS_SDR50: + str = "sd uhs SDR50"; + break; + case MMC_TIMING_UHS_SDR104: + str = "sd uhs SDR104"; + break; + case MMC_TIMING_UHS_DDR50: + str = "sd uhs DDR50"; + break; + case MMC_TIMING_MMC_DDR52: + str = "mmc DDR52"; + break; + case MMC_TIMING_MMC_HS200: + str = "mmc HS200"; + break; + case MMC_TIMING_MMC_HS400: + str = host->card->host->ios.enhanced_strobe ? + "mmc HS400 enhanced strobe" : "mmc HS400"; + break; + default: + str = "invalid"; + break; + } + ret += sprintf(&buffer[ret], "timing spec:\t%u (%s)\n", ios->timing, str); + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + str = "3.30 V"; + break; + case MMC_SIGNAL_VOLTAGE_180: + str = "1.80 V"; + break; + case MMC_SIGNAL_VOLTAGE_120: + str = "1.20 V"; + break; + default: + str = "invalid"; + break; + } + ret += sprintf(&buffer[ret], "signal voltage:\t%u (%s)\n", ios->signal_voltage, str); + + switch (ios->drv_type) { + case MMC_SET_DRIVER_TYPE_A: + str = "driver type A"; + break; + case MMC_SET_DRIVER_TYPE_B: + str = "driver type B"; + break; + case MMC_SET_DRIVER_TYPE_C: + str = "driver type C"; + break; + case MMC_SET_DRIVER_TYPE_D: + str = "driver type D"; + break; + default: + str = "invalid"; + break; + } + ret += sprintf(&buffer[ret], "driver type:\t%u (%s)\n", ios->drv_type, str); + + + + switch (skw_sdio->irq_type) { + case SKW_SDIO_INBAND_IRQ: + str = "INBAND_IRQ"; + break; + case SKW_SDIO_EXTERNAL_IRQ: + str = "EXTERNAL_IRQ"; + break; + default: + str = "invalid"; + break; + } + ret += sprintf(&buffer[ret], "irq type:\t%u (%s)\n", skw_sdio->irq_type, str); + + if(!strncmp((char *)skw_sdio->chip_id,"SV6160LITE",10)){ + str = "SV6160LITE"; + }else if(!strncmp((char *)skw_sdio->chip_id,"SV6160",6)){ + str = "SV6160"; + }else if(!strncmp((char *)skw_sdio->chip_id,"SV6316",6)){ + str = "SV6316"; + }else{ + str = "invalid"; + } + + ret += sprintf(&buffer[ret], "chipid:\t%s (%s)\n", (char *)skw_sdio->chip_id, str); + + switch (skw_cp_ver) { + case SKW_SDIO_V10: + str = "sdio v1.0"; + break; + case SKW_SDIO_V20: + str = "sdio v2.0"; + break; + default: + str = "invalid"; + break; + } + ret += sprintf(&buffer[ret], "skw_cp_ver:\t%u (%s)\n", skw_cp_ver,str); + + str = "byte"; + ret += sprintf(&buffer[ret], "max_pkg_size:\t%u (%s)\n", max_pac_size, str); + ret += sprintf(&buffer[ret], "skw_sdio_blk_size:\t%u (%s)\n", skw_sdio_blk_size, str); + ret += sprintf(&buffer[ret], "max_tx_pkg_cnt:\t%u\n", MAX_PAC_COUNT); + ret += sprintf(&buffer[ret], "max_rx_pkg_cnt:\t%u\n", wifi_pdata.max_buffer_size/max_pac_size); + + + ret += sprintf(&buffer[ret], "wifi data_port:\t%u\n", wifi_pdata.data_port); + ret += sprintf(&buffer[ret], "wifi cmd_port:\t%u\n", wifi_pdata.cmd_port); + + + ret += sprintf(&buffer[ret], "ucom data_port:\t%u\n", ucom_pdata.data_port); + ret += sprintf(&buffer[ret], "ucom cmd_port:\t%u\n", ucom_pdata.cmd_port); + ret += sprintf(&buffer[ret], "ucom audio_port:\t%u\n", ucom_pdata.audio_port); + + ret += sprintf(&buffer[ret], "max_ch_num:\t%u\n", max_ch_num); + + if(g_chipen_pin>=0){ + switch (gpio_get_value(g_chipen_pin)) { + case 0: + str = "low"; + break; + case 1: + str = "high"; + break; + default: + str = "invalid"; + break; + } + } + ret += sprintf(&buffer[ret], "chipen gpio_out:\t%u (%s)\n", g_chipen_pin, str); + + if(skw_sdio->gpio_out>=0){ + switch (gpio_get_value(skw_sdio->gpio_out)) { + case 0: + str = "low"; + break; + case 1: + str = "high"; + break; + default: + str = "invalid"; + break; + } + } + ret += sprintf(&buffer[ret], "ap2cp gpio_out:\t%u (%s)\n", skw_sdio->gpio_out, str); + + if(skw_sdio->gpio_in>=0){ + switch (gpio_get_value(skw_sdio->gpio_in)) { + case 0: + str = "low"; + break; + case 1: + str = "high"; + break; + default: + str = "invalid"; + break; + } + } + ret += sprintf(&buffer[ret], "cp2ap gpio_in:\t%u (%s)\n", skw_sdio->gpio_in, str); + +#if defined(CONFIG_NO_SERVICE_PD) && (CONFIG_NO_SERVICE_PD == 1) + str = "Enable"; +#else + str = "Disable"; +#endif + + ret += sprintf(&buffer[ret], "sleep with cp powerdown:\t(%s)\n", str); + + + switch (skw_use_sdma) { + case 1: + str = "sdma"; + break; + case 0: + str = "adma"; + break; + default: + str = "adma"; + break; + } + ret += sprintf(&buffer[ret], "dma type:\t%u (%s)\n", skw_use_sdma, str); + + + switch (skw_sdio->boot_data->slp_disable) { + case 1: + str = "Disable"; + break; + case 0: + str = "Enable"; + break; + default: + str = "Enable"; + break; + } + ret += sprintf(&buffer[ret], "fw deepsleep:\t%u (%s)\n", skw_sdio->boot_data->slp_disable, str); + + ret += sprintf(&buffer[ret], "host_active:\t%u\n", skw_sdio->host_active); + ret += sprintf(&buffer[ret], "device_active:\t%u\n", skw_sdio->device_active); + ret += sprintf(&buffer[ret], "resume_com:\t%u\n", skw_sdio->resume_com); + ret += sprintf(&buffer[ret], "cp_state:\t%u\n", skw_sdio->cp_state); + + + ret += sprintf(&buffer[ret], "VID:\t0x%x\n", skw_sdio->sdio_func[FUNC_0]->vendor); + ret += sprintf(&buffer[ret], "PID:\t0x%x\n", skw_sdio->sdio_func[FUNC_0]->device); + + + ret += sprintf(&buffer[ret], "wifi_device name:\t%s\n", sdio_ports[WIFI_DATA_PORT].pdev->name); + + + if(ret >= size) + skw_sdio_info("ret bigger than size %d %d\n", ret, size); + +} + + +static int skw_sdio_host_check(struct skw_sdio_data_t *skw_sdio) +{ + int ret = 0; + + struct sdio_func *func1 = skw_sdio->sdio_func[FUNC_1]; + struct mmc_host *host = func1->card->host; + + if ((SKW_SDIO_INBAND_IRQ == skw_sdio->irq_type) && (0 == (host->caps & MMC_CAP_SDIO_IRQ))) { + skw_sdio_err("Please add cap-sdio-irq to dts! irq_type=%d caps=0x%x\n", skw_sdio->irq_type, host->caps); + ret = -EPERM; + } else if ((host->ios.clock > 50000000UL) && (0 == (host->caps & MMC_CAP_UHS_SDR104))) { + skw_sdio_err("please add sd-uhs-sdr104 to dts! clock=%d cap=0x%x\n", host->ios.clock, host->caps); + ret = -EPERM; + } else if ((host->ios.clock <= 50000000UL) && (0 != (host->caps & MMC_CAP_UHS_SDR104))) { + skw_sdio_err("please remove sd-uhs-sdr104 from dts! clock=%d cap=0x%x\n", host->ios.clock, host->caps); + ret = -EPERM; + } else if (host->ios.clock != host->f_max) { + skw_sdio_err("actual clock is not equal to max clock! clock=%d f_max=%d\n", host->ios.clock, host->f_max); + ret = -EPERM; + } else if (host->ios.timing != MMC_TIMING_UHS_SDR104) { + skw_sdio_err("actual timing is not equal to max timing! timing=%d t_max=%d\n", host->ios.timing, MMC_TIMING_UHS_SDR104); + ret = -EPERM; + } + + return ret; +} + +static int __init skw_sdio_io_init(void) +{ + struct skw_sdio_data_t *skw_sdio; + int ret = 0; + int skw_sd_id = SKW_MMC_HOST_SD_INDEX; + if (card_id != SKW_MMC_HOST_SD_INDEX) + skw_sd_id = card_id; + skw_sdio_mmc_scan(skw_sd_id); + memset(&debug_infos, 0, sizeof(struct debug_vars)); + sunxi_wlan_set_power(0); + msleep(200); + sunxi_wlan_set_power(1); + msleep(80); + sunxi_mmc_rescan_card(1); + msleep(20); + skw_sdio_log_level_init(); + + skw_sdio = kzalloc(sizeof(struct skw_sdio_data_t), GFP_KERNEL); + if (!skw_sdio) { + WARN_ON(1); + return -ENOMEM; + } + + /* card not ready */ + g_skw_sdio_data = skw_sdio; + mutex_init(&skw_sdio->transfer_mutex); + mutex_init(&skw_sdio->except_mutex); + mutex_init(&dloader_mutex); + mutex_init(&skw_sdio->service_mutex); + atomic_set(&skw_sdio->resume_flag, 1); + skw_sdio->next_size_buf = kzalloc(SKW_BUF_SIZE, GFP_KERNEL); + if(skw_sdio->next_size_buf == NULL){ + kfree(skw_sdio); + skw_sdio = NULL; + return -ENOMEM; + } + skw_sdio->eof_buf = kzalloc(SKW_BUF_SIZE, GFP_KERNEL); + if(skw_sdio->eof_buf == NULL){ + kfree(skw_sdio->next_size_buf); + skw_sdio->next_size_buf = NULL; + kfree(skw_sdio); + skw_sdio = NULL; + return -ENOMEM; + } + atomic_set(&skw_sdio->online, SKW_SDIO_CARD_OFFLINE); + if(!bind_device){ + skw_sdio->adma_rx_enable = 1; + } + //kzmalloc the log_data + skw_sdio->log_data = kzalloc(sizeof(struct skw_log_data_t), GFP_KERNEL); + if (skw_sdio->log_data == NULL) { + kfree(skw_sdio->next_size_buf); + skw_sdio->next_size_buf = NULL; + kfree(skw_sdio->eof_buf); + skw_sdio->eof_buf = NULL; + kfree(skw_sdio); + skw_sdio = NULL; + skw_sdio_err("kzalloc the log_data fail\n"); + return -ENOMEM; + } + skw_sdio->irq_num = -1; + INIT_DELAYED_WORK(&skw_sdio->skw_except_work, skw_sdio_exception_work); + ret = skw_sdio_scan_card(); + if (ret < 0) { + skw_sdio_remove_card(); + skw_sdio_err("scan card fail\n"); + kfree(skw_sdio->log_data); + skw_sdio->log_data = NULL; + kfree(skw_sdio->next_size_buf); + skw_sdio->next_size_buf = NULL; + kfree(skw_sdio->eof_buf); + skw_sdio->eof_buf = NULL; + kfree(skw_sdio); + skw_sdio = NULL; + return ret; + } + if (skw_sdio->sdio_dev_host) { + seekwave_boot_init((char *)skw_sdio->chip_id); + } + return ret; +} + +static void __exit skw_sdio_io_exit(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if (skw_sdio->sdio_dev_host) + seekwave_boot_exit(); + skw_sdio_debugfs_deinit(); + if (skw_sdio) { + skw_sdio_stop_thread(); + if (!skw_sdio->suspend_wake_unlock_enable) { + skw_sdio_unlock_rx_ws(skw_sdio); + } + skw_sdio_remove_card(); + if (skw_sdio->sdio_dev_host) + skw_sdio_reset_card(); + cancel_delayed_work_sync(&skw_sdio->skw_except_work); + mutex_destroy(&skw_sdio->transfer_mutex); + mutex_destroy(&skw_sdio->except_mutex); + mutex_destroy(&dloader_mutex); + mutex_destroy(&skw_sdio->service_mutex); + + kfree(skw_sdio->next_size_buf); + kfree(skw_sdio->eof_buf); + skw_sdio->boot_data = NULL; + skw_sdio->sdio_dev_host = NULL; + kfree(skw_sdio->log_data); + skw_sdio->log_data = NULL; + kfree(skw_sdio); + skw_sdio = NULL; + } + skw_sdio_info(" OK\n"); +} +module_init(skw_sdio_io_init) +module_exit(skw_sdio_io_exit) +MODULE_LICENSE("GPL v2"); diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_rx.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_rx.c new file mode 100755 index 0000000..2bb6f46 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/sdio/skw_sdio_rx.c @@ -0,0 +1,3419 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/mutex.h> +#include <linux/bitops.h> +#include <linux/kthread.h> +#include <linux/notifier.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> +#include <linux/semaphore.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/mmc/sdio_func.h> +#include <linux/skbuff.h> +#include "skw_sdio.h" +#include "skw_sdio_log.h" +#include <linux/workqueue.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include <linux/bits.h> +#else +#include <linux/bitops.h> +#endif +#ifndef GENMASK +#ifdef CONFIG_64BIT +#define BITS_PER_LONG 64 +#else +#define BITS_PER_LONG 32 +#endif /* CONFIG_64BIT */ +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) +#endif +#define MODEM_ASSERT_TIMEOUT_VALUE 2*HZ +#define MAX_SG_COUNT 100 +#define SDIO_BUFFER_SIZE (16*1024) +#define FRAGSZ_SIZE (3*1024) +#define MAX_FIRMWARE_SIZE 256 +#define PORT_STATE_IDLE 0 +#define PORT_STATE_OPEN 1 +#define PORT_STATE_CLSE 2 +#define PORT_STATE_ASST 3 + +#define CRC_16_L_SEED 0x80 +#define CRC_16_L_POLYNOMIAL 0x8000 +#define CRC_16_POLYNOMIAL 0x1021 + +int recovery_debug_status=0; +int wifi_serv_debug_status=0; +int bt_serv_debug_status=0; + +/***********************************************************/ +char firmware_version[128]; +char assert_context[1024]; +int assert_context_size=0; +static int assert_info_print; +static u64 port_dmamask = DMA_BIT_MASK(32); +struct sdio_port sdio_ports[SDIO2_MAX_CH_NUM]; +static u8 fifo_ind; +struct debug_vars debug_infos; +static BLOCKING_NOTIFIER_HEAD(modem_notifier_list); +unsigned int crc_16_l_calc(char *buf_ptr,unsigned int len); +static int skw_sdio_rx_port_follow_ctl(int portno, int rx_fctl); +//add the crc api the same as cp crc_16 api +extern void kernel_restart(char *cmd); +static int skw_sdio_irq_ops(int irq_enable); +static int bt_service_start(void); +static int bt_service_stop(void); +static int wifi_service_start(void); +static int wifi_service_stop(void); +void send_cp_wakeup_signal(struct skw_sdio_data_t *skw_sdio); +static int skw_sdio_dump(unsigned int address, void *buf, unsigned int len); +char skw_cp_ver = SKW_SDIO_V10; +int max_ch_num = MAX_CH_NUM; +int max_pac_size = MAX_PAC_SIZE; +int skw_sdio_blk_size = 256; +int cp_detect_sleep_mode; +static u8 is_timeout_kick; +#ifdef CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER +#include <clocksource/arm_arch_timer.h> +u64 skw_local_clock(void) +{ + u64 ns; + + ns = arch_timer_read_counter() * 1000; + do_div(ns, 24); + + return ns; +} +#else +#if KERNEL_VERSION(4,11,0) <= LINUX_VERSION_CODE +#include <linux/sched/clock.h> +#else +#include <linux/sched.h> +#endif +u64 skw_local_clock(void) +{ + return local_clock(); +} +#endif +#define IS_LOG_PORT(portno) ((skw_cp_ver == SKW_SDIO_V10)?(portno==1):(portno==SDIO2_BSP_LOG_PORT)) + + +void skw_get_assert_print_info(char *buffer, int size) +{ + int ret = 0; + int j = 0; + u64 ts; + u64 rem_nsec; + u64 rem_usec; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if(!buffer) { + skw_sdio_info("buffer is null!\n"); + return; + } + ret += sprintf(&buffer[ret], "last irq times: [%6d] [%6d] ", debug_infos.rx_inband_irq_cnt, debug_infos.rx_gpio_irq_cnt); + for (j = 0; j < CHN_IRQ_RECORD_NUM; j++) { + ts = debug_infos.last_irq_times[j]; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "[%5lu.%06llu] ", (unsigned long)ts, rem_usec); + } + if (SKW_SDIO_INBAND_IRQ == skw_sdio->irq_type) { + ret += sprintf(&buffer[ret], "\nlast clear irq times: [%6d] ", debug_infos.rx_inband_irq_cnt); + for (j = 0; j < CHN_IRQ_RECORD_NUM; j++) { + ts = debug_infos.last_clear_irq_times[j]; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "[%5lu.%06llu] ", (unsigned long)ts, rem_usec); + } + } + ret += sprintf(&buffer[ret], "\nlast rx read times: [%6d] ", debug_infos.rx_read_cnt); + for (j = 0; j < CHN_IRQ_RECORD_NUM; j++) { + ts = debug_infos.last_rx_read_times[j]; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "[%5lu.%06llu] ", (unsigned long)ts, rem_usec); + } + + if(ret >= size) + skw_sdio_info("ret bigger than size %d %d\n", ret, size); +} + +void skw_get_sdio_debug_info(char *buffer, int size) +{ + int ret = 0; + int i = 0; + int j = 0; + u64 ts; + u64 rem_nsec; + u64 rem_usec; + u32 irq_cnt = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if(!buffer) { + skw_sdio_info("buffer is null!\n"); + return; + } + + if (0 == debug_infos.rx_irq_statistics_cnt) { + if (SKW_SDIO_EXTERNAL_IRQ == skw_sdio->irq_type) + debug_infos.rx_irq_statistics_cnt = debug_infos.rx_gpio_irq_cnt; + else + debug_infos.rx_irq_statistics_cnt = debug_infos.rx_inband_irq_cnt; + debug_infos.rx_irq_statistics_time = skw_local_clock(); + skw_sdio_info("===============rx irq statistics start:%d!===============\n", debug_infos.rx_irq_statistics_cnt); + } else { + ret += sprintf(&buffer[ret], "rx irq statistics:\n"); + ts = skw_local_clock() - debug_infos.rx_irq_statistics_time; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + if (SKW_SDIO_EXTERNAL_IRQ == skw_sdio->irq_type) + irq_cnt = debug_infos.rx_gpio_irq_cnt - debug_infos.rx_irq_statistics_cnt; + else + irq_cnt = debug_infos.rx_inband_irq_cnt - debug_infos.rx_irq_statistics_cnt; + skw_sdio_info("===============rx irq statistics end:%d!===============\n", debug_infos.rx_irq_statistics_cnt + irq_cnt); + ret += sprintf(&buffer[ret], "rx irq time: [%5lu.%06llu] count:%d count per second:%lu\n", (unsigned long)ts, rem_usec, irq_cnt, irq_cnt /(unsigned long)ts); + + debug_infos.rx_irq_statistics_cnt = 0; + debug_infos.rx_irq_statistics_time = 0; + } + + ret += sprintf(&buffer[ret], "channel irq times:\n"); + for (i = 0; i < max_ch_num; i++) { + ret += sprintf(&buffer[ret], "channel[%2d]: [%6d] ", i, debug_infos.chn_irq_cnt[i]); + for (j = 0; j < CHN_IRQ_RECORD_NUM; j++) { + ts = debug_infos.chn_last_irq_time[i][j]; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "[%5lu.%06llu] ", (unsigned long)ts, rem_usec); + } + ret += sprintf(&buffer[ret], "\n"); + } + ret += sprintf(&buffer[ret], "cmd_timeout_cnt: %d\n", debug_infos.cmd_timeout_cnt); + ret += sprintf(&buffer[ret], "last_sent_wifi_cmd[0]: 0x%x\n", debug_infos.last_sent_wifi_cmd[0]); + ret += sprintf(&buffer[ret], "last_sent_wifi_cmd[1]: 0x%x\n", debug_infos.last_sent_wifi_cmd[1]); + ret += sprintf(&buffer[ret], "last_sent_wifi_cmd[2]: 0x%x\n", debug_infos.last_sent_wifi_cmd[2]); + ts = debug_infos.last_sent_time; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "last_sent_time: [%5lu.%06llu]\n", (unsigned long)ts, rem_usec); + ts = debug_infos.last_rx_submit_time; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "last_rx_submit_time: [%5lu.%06llu]\n", (unsigned long)ts, rem_usec); + if (debug_infos.host_assert_cp_time) { + ts = debug_infos.host_assert_cp_time; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "host_assert_cp_time: [%5lu.%06llu]\n", (unsigned long)ts, rem_usec); + } + if (debug_infos.cp_assert_time) { + ts = debug_infos.cp_assert_time; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "cp_assert_time: [%5lu.%06llu]\n", (unsigned long)ts, rem_usec); + } + if (debug_infos.host_assert_cp_time > debug_infos.last_sent_time) { + ts = debug_infos.host_assert_cp_time - debug_infos.last_sent_time; + rem_nsec = do_div(ts, 1000000000); + rem_usec = do_div(rem_nsec, 1000); + ret += sprintf(&buffer[ret], "timeout: [%5lu.%06llu]\n", (unsigned long)ts, rem_usec); + } + + if(ret >= size) + skw_sdio_info("ret bigger than size %d %d\n", ret, size); +} +//======================================================= +//debug sdio macro and Variable +int glb_wifiready_done; +//#define SKW_WIFIONLY_DEBUG 1 +//======================================================= + +/******************************************************** + * skw_sdio_update img crc checksum + * For update the CP IMG + *Author: JUNWEI JIANG + *Date:2022-08-11 + * *****************************************************/ + +int skw_log_port(void) +{ + return (skw_cp_ver == SKW_SDIO_V10)?(BSP_LOG_PORT):(SDIO2_BSP_LOG_PORT); +} + +void skw_get_port_statistic(char *buffer, int size) +{ + int ret = 0; + int i; + + if(!buffer) + return; + ret += sprintf(&buffer[ret], "%s", firmware_version); + for(i=0; i<max_ch_num; i++) { + if(ret >= size) + break; + if(sdio_ports[i].state) + ret += sprintf(&buffer[ret], "port%d: rx %d %d, tx %d %d\n", + i, sdio_ports[i].rx_count, sdio_ports[i].rx_packet, + sdio_ports[i].total, sdio_ports[i].sent_packet); + } +} + +unsigned int crc_16_l_calc(char *buf_ptr,unsigned int len) +{ + unsigned int i; + unsigned short crc=0; + + while(len--!=0) + { + for(i= CRC_16_L_SEED;i!=0;i=i>>1) + { + if((crc &CRC_16_L_POLYNOMIAL)!=0) + { + crc= crc<<1; + crc= crc ^ CRC_16_POLYNOMIAL; + }else{ + crc = crc <<1; + } + + if((*buf_ptr &i)!=0) + { + crc = crc ^ CRC_16_POLYNOMIAL; + } + } + buf_ptr++; + } + return (crc); +} + +static int skw_sdio_rx_port_follow_ctl(int portno, int rx_fctl) +{ + char ftl_val = 0; + int ret = 0; + + skw_sdio_info(" portno:%d, rx_fctl:%d \n", portno, rx_fctl); + + if((portno < 0) || (portno > max_ch_num)) + return -1; + + if(portno < 8){ + ret = skw_sdio_readb(SKW_SDIO_RX_CHANNEL_FTL0, &ftl_val); + if(ret) + return -1; + + if(rx_fctl) + ftl_val = ftl_val | (1 << portno); + else + ftl_val = ftl_val & (~(1 << portno)); + ret = skw_sdio_writeb(SKW_SDIO_RX_CHANNEL_FTL0, ftl_val); + } + else{ + portno = portno - 8; + ret = skw_sdio_readb(SKW_SDIO_RX_CHANNEL_FTL1, &ftl_val); + if(ret) + return -1; + + if(rx_fctl) + ftl_val = ftl_val | (1 << portno); + else + ftl_val = ftl_val & (~(1 << portno)); + ret = skw_sdio_writeb(SKW_SDIO_RX_CHANNEL_FTL1, ftl_val); + } + return ret; +} + +void modem_register_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_register(&modem_notifier_list, nb); +} +void modem_unregister_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&modem_notifier_list, nb); +} +void modem_notify_event(int event) +{ + blocking_notifier_call_chain(&modem_notifier_list, event, NULL); +} + +void skw_sdio_exception_work(struct work_struct *work) +{ + int i=0; + int port_num=5; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info(" entern..cp_state = %d.\n", skw_sdio->cp_state); + mutex_lock(&skw_sdio->except_mutex); + if(skw_sdio->cp_state!=1) + { + skw_sdio_warn("cp assert already\n"); + mutex_unlock(&skw_sdio->except_mutex); + return; + } + skw_sdio->cp_state = DEVICE_BLOCKED_EVENT; + mutex_unlock(&skw_sdio->except_mutex); + modem_notify_event(DEVICE_BLOCKED_EVENT); + for (i=0; i<port_num; i++) + { + if(!sdio_ports[i].state || sdio_ports[i].state==PORT_STATE_CLSE) + continue; + + sdio_ports[i].state = PORT_STATE_ASST; + complete(&(sdio_ports[i].rx_done)); + + if(i!=1) + complete(&(sdio_ports[i].tx_done)); + if(i==0 || i==skw_log_port()) + sdio_ports[i].next_seqno= 1; + + } + skw_sdio->service_state_map=0; + skw_recovery_mode(); +} + +static inline void *skw_sdio_alloc_frag(size_t fragsz, gfp_t gfp_mask) +{ + void *addr; + struct page *page; + addr = netdev_alloc_frag(fragsz); + if (!addr) + return NULL; + + page = virt_to_head_page(addr); + + skw_sdio_dbg( + "dbg: alloc addr: 0x%lx, size: %ld, page addr: 0x%lx, ref: %d\n", + (long)addr, (long int)fragsz, (long)page, page_count(page)); + + return addr; +} +static inline void skw_page_frag_free(void *addr) +{ + struct page *page = NULL; + if (!addr) { + skw_sdio_warn("dbg: free addr is NULL\n"); + return; + } + + page = virt_to_head_page(addr); + skw_sdio_dbg("dbg: free addr: 0x%lx, page addr: 0x%lx, ref: %d\n", + (long)addr, (long)page, page_count(page)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + skb_free_frag(addr); +#else + put_page(virt_to_head_page(addr)); +#endif +} + +static void skw_sdio_rx_down(struct skw_sdio_data_t * skw_sdio) +{ + wait_for_completion_interruptible(&skw_sdio->rx_completed); +} +void skw_sdio_rx_up(struct skw_sdio_data_t * skw_sdio) +{ + skw_reinit_completion(skw_sdio->rx_completed); + complete(&skw_sdio->rx_completed); +} +void skw_sdio_dispatch_packets(struct skw_sdio_data_t * skw_sdio) +{ + int i; + struct sdio_port *port; + + for(i=0; i<max_ch_num; i++) { + port = &sdio_ports[i]; + if(!port->state) + continue; + if(port->rx_rp!=port->rx_wp) + skw_sdio_dbg("port[%d] sg_index=%d (%d,%d)\n", i, + port->sg_index, port->rx_rp, port->rx_wp); + if(port->rx_submit && port->sg_index) { + debug_infos.last_rx_submit_time = skw_local_clock(); + port->rx_submit(port->channel, port->sg_rx, port->sg_index, port->rx_data); + skw_sdio_dbg("port[%d] sg_index=%d (%d,%d)\n", i, + port->sg_index, port->rx_rp, port->rx_wp); + port->sg_index = 0; + } + } +} +static void skw_sdio_sdma_set_nsize(unsigned int size) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int count; + if (size == 0) { + skw_sdio->next_size = max_pac_size; + return; + } + + count = (size >> 10) + 9; + size = SKW_SDIO_ALIGN_BLK(size + (count<<3)); + skw_sdio->next_size = (size>SDIO_BUFFER_SIZE) ? SDIO_BUFFER_SIZE:size; +} + +static void skw_sdio_adma_set_packet_num(unsigned int num) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if (num == 0) + num = 1; + + if (num >= MAX_SG_COUNT) + skw_sdio->remain_packet = MAX_SG_COUNT; + else + skw_sdio->remain_packet = num; +} + +/************************************************************************ + *Decription:release debug recovery auto test + *Author:junwei.jiang + *Date:2023-05-30 + *Modfiy: + * + ********************************************************************* */ +int skw_sdio_recovery_debug(int disable) +{ + recovery_debug_status = disable; + skw_sdio_info("the recovery status =%d\n", recovery_debug_status); + return 0; +} + +int skw_sdio_recovery_debug_status(void) +{ + skw_sdio_info("the recovery val =%d\n", recovery_debug_status); + return recovery_debug_status; +} + +int skw_sdio_bt_serv_debug(int enable) +{ + bt_serv_debug_status = enable; + + skw_sdio_info("the bt_service status =%d\n", bt_serv_debug_status); + if(enable){ + bt_service_start(); + }else{ + bt_service_stop(); + } + return 0; +} + +int skw_sdio_bt_serv_debug_status(void) +{ + skw_sdio_info("the bt_service val =%d\n", bt_serv_debug_status); + + return bt_serv_debug_status; +} +int skw_sdio_wifi_serv_debug(int enable) +{ + wifi_serv_debug_status = enable; + + skw_sdio_info("the wifi_service status =%d\n", wifi_serv_debug_status); + if(enable){ + wifi_service_start(); + }else{ + wifi_service_stop(); + } + return 0; +} + +int skw_sdio_wifi_serv_debug_status(void) +{ + skw_sdio_info("the wifi_service val =%d\n", wifi_serv_debug_status); + return wifi_serv_debug_status; +} + +static int force_cp_wakeup(void) +{ + int ret = 0; + u32 val = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + send_cp_wakeup_signal(skw_sdio); + ret = wait_for_completion_interruptible_timeout(&skw_sdio->device_wakeup, HZ/100); + if (ret <= 0) { + skw_sdio_err("wait for device wakeup timeout\n"); + return -ETIMEDOUT; + } + val = gpio_get_value(skw_sdio->gpio_in); + if(val) + return 0; + else { + send_cp_wakeup_signal(skw_sdio); + ret = wait_for_completion_interruptible_timeout(&skw_sdio->device_wakeup, HZ/100); + if (ret == 0) + return -ETIMEDOUT; + val = gpio_get_value(skw_sdio->gpio_in); + if(val) + return 0; + else + return -ETIMEDOUT; + } + + return -ETIMEDOUT; +} + +static int skw_sdio_dump(unsigned int address, void *buf, unsigned int len) +{ + int ret = 0; + ret = skw_sdio_smem_poweron(); + if (ret != 0) { + skw_sdio_err(" pweron fail ret:%d, addr=0x%x\n", ret, address); + return ret; + } + ret = skw_sdio_dt_read(address, buf, len); + if (ret != 0) { + skw_sdio_err(" dt read fail ret:%d, addr=0x%x\n", ret, address); + return ret; + } + return ret; +} +int update_download_flag(bool enable) +{ + int ret; + u32 buffer; + + /* 1. read DL_FLAG reg */ + ret = force_cp_wakeup(); + if (ret) { + skw_sdio_err("force cp wakeup failed %d\n", __LINE__); + return ret; + } + ret = skw_sdio_dt_read(SKW_DL_FLAG_BASE, &buffer, 4); + if (ret) { + ret = force_cp_wakeup(); + if (ret) { + skw_sdio_err("force cp wakeup failed %d\n", __LINE__); + return ret; + } + ret = skw_sdio_dt_read(SKW_DL_FLAG_BASE, &buffer, 4); + if (ret) { + skw_sdio_err("read dl flag failed\n"); + return ret; + } + } + + if (enable == 1) + buffer |= SKW_DL_FLAG_BIT_MASK; + else + buffer &= ~SKW_DL_FLAG_BIT_MASK; + + /* 2. update DL_FLAG bit */ + ret = force_cp_wakeup(); + if (ret) { + skw_sdio_err("force cp wakeup failed %d\n", __LINE__); + return ret; + } + ret = skw_sdio_dt_write(SKW_DL_FLAG_BASE, &buffer, 4); + if (ret) { + ret = force_cp_wakeup(); + if (ret) { + skw_sdio_err("force cp wakeup failed %d\n", __LINE__); + return ret; + } + ret = skw_sdio_dt_write(SKW_DL_FLAG_BASE, &buffer, 4); + if (ret) { + skw_sdio_err("write dl flag failed\n"); + return ret; + } + } + /* 3. Make sure CP update flag successfully */ + ret = force_cp_wakeup(); + if (ret) { + skw_sdio_err("force cp wakeup failed %d\n", __LINE__); + return ret; + } + + return 0; +} + +static int skw_pin_config(void) +{ + int i, ret; + u32 val; + int func_index = 0; + int g_offset = 0; + u32 pin_group_index = 0; + u32 func_group_index = 0; + u32 func_sel_val[2] = {0}; + u32 func_sel_off[] = {PN_FUNC_SEL0_OFFSET, PN_FUNC_SEL1_OFFSET}; + u32 *pin_val; + u8 pin_off[PN_CNT] = {0}; + u32 buffer; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + + ret = update_download_flag(1); + if (ret) { + skw_sdio_err("set download flag failed\n"); + return ret; + } + + pin_val = (u32 *)kzalloc(PN_CNT * sizeof(u32), GFP_KERNEL); + if (!pin_val) { + skw_sdio_err("pin_val alloc failed\n"); + return -ENOMEM; + } + + while (g_offset < skw_sdio->boot_data->nv_mem_pnfg_size) { + //1. pin offset + pin_off[pin_group_index] = skw_sdio->boot_data->nv_mem_pnfg_data[g_offset]; + + //2. func_sel value + val = skw_sdio->boot_data->nv_mem_pnfg_data[g_offset + 1]; + func_sel_val[func_group_index] |= (val << (3 * func_index)) & (7 << (3 * func_index)); + + //3. pin config value + pin_val[pin_group_index] |= + ((u32)skw_sdio->boot_data->nv_mem_pnfg_data[g_offset + 2] << BIT_PN_DSLP_EN_START) & \ + GENMASK(BIT_PN_DSLP_EN_END, BIT_PN_DSLP_EN_START);//dslp_en + pin_val[pin_group_index] |= + ((u32)skw_sdio->boot_data->nv_mem_pnfg_data[g_offset + 3] << BIT_DRV_STREN_START) & \ + GENMASK(BIT_DRV_STREN_END, BIT_DRV_STREN_START);//drv_strength + pin_val[pin_group_index] |= + ((u32)skw_sdio->boot_data->nv_mem_pnfg_data[g_offset + 4] << BIT_NORMAL_WP_START) & \ + GENMASK(BIT_NORMAL_WP_END, BIT_NORMAL_WP_START);//normal:wpu/wpd + pin_val[pin_group_index] |= + ((u32)skw_sdio->boot_data->nv_mem_pnfg_data[g_offset + 5] << BIT_SCHMITT_START) & \ + GENMASK(BIT_SCHMITT_END, BIT_SCHMITT_START);//schmitt trigger enable + pin_val[pin_group_index] |= + ((u32)skw_sdio->boot_data->nv_mem_pnfg_data[g_offset + 6] << BIT_SLP_WP_START) & \ + GENMASK(BIT_SLP_WP_END, BIT_SLP_WP_START);//sleep:wpu/wpd + pin_val[pin_group_index] |= + ((u32)skw_sdio->boot_data->nv_mem_pnfg_data[g_offset + 7]) & \ + GENMASK(BIT_SLEEP_IE_OE_END, BIT_SLEEP_IE_OE_START);//sleep:ie/oe + + g_offset += 8; // 1(offset) + 1(pin sel) + 6(pin config) + func_index++; + pin_group_index++; + if (func_index == PN_FUNCSEL_ONEGRP_CNT){ + func_group_index++; + func_index = 0; + } + } + + //function select + for (i = 0;i < sizeof(func_sel_off) / sizeof(u32);i++) { + buffer = func_sel_val[i]; + skw_sdio_dbg("sel_off,sel_val::0x%08x,0x%08x\n", func_sel_off[i], buffer); + ret = skw_sdio_dt_write(SKW_PINREG_BASE + func_sel_off[i], &buffer, 4); + if (ret) { + kfree(pin_val); + pin_val = NULL; + skw_sdio_err("write pinreg[0x%x] failed\n", func_sel_off[i]); + return ret; + } + } + + //pin config + for (i = 0;i < PN_CNT;i++) { + buffer = pin_val[i]; + skw_sdio_dbg("pin_off,pin_val:0x%08x,0x%08x\n", pin_off[i], buffer); + ret = skw_sdio_dt_write(SKW_PINREG_BASE + pin_off[i], &buffer, 4); + if (ret) { + kfree(pin_val); + pin_val = NULL; + skw_sdio_err("write pinreg[0x%x] failed\n", pin_off[i]); + return ret; + } + } + kfree(pin_val); + pin_val = NULL; + ret = update_download_flag(0); + if (ret) { + skw_sdio_err("clear download flag failed\n"); + return ret; + } + return 0; +} + +static int skw_sdio_handle_packet(struct skw_sdio_data_t *skw_sdio, + struct scatterlist *sg, struct skw_packet_header *header, int portno) +{ + struct sdio_port *port; + int buf_size, i, ret; + int log_port_num = 0; + char *addr; + u32 *data; + if (portno >= max_ch_num) + return -EINVAL; + log_port_num = skw_log_port(); + port = &sdio_ports[portno]; + port->rx_packet++; + port->rx_count += header->len; + if(portno == LOOPCHECK_PORT) { + char *cmd = (char *)(header+4); + cmd[header->len - 12] = 0; + skw_sdio_info("LOOPCHECK channel received: %s\n", (char *)cmd); + if (header->len==19 && !strncmp(cmd, "BTREADY", 7)) { + skw_sdio->service_state_map |= 2; + //kernel_restart(0); + skw_sdio->device_active = 1; + complete(&skw_sdio->download_done); + }else if(header->len==21 && !strncmp(cmd, "WIFIREADY", 9)){ + skw_sdio->service_state_map |= 1; + //kernel_restart(0); + skw_sdio->device_active = 1; + complete(&skw_sdio->download_done); + } else if (!strncmp((char *)cmd, "BSPASSERT", 9)){ + sprintf(firmware_version, "%s:%s\n", firmware_version, cmd); + debug_infos.cp_assert_time = skw_local_clock(); +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + if(!skw_sdio->cp_state && sdio_ports[log_port_num].state != PORT_STATE_OPEN) + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(800)); + else if(!skw_sdio->cp_state && sdio_ports[log_port_num].state == PORT_STATE_OPEN) + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(8000)); +#else + if(!skw_sdio->cp_state) + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(12000)); +#endif + mutex_lock(&skw_sdio->except_mutex); + if(skw_sdio->cp_state==DEVICE_BLOCKED_EVENT){ + if(skw_sdio->adma_rx_enable) + skw_page_frag_free(header); + + mutex_unlock(&skw_sdio->except_mutex); + return 0; + } + skw_sdio->cp_state=1;/*cp except set value*/ + mutex_unlock(&skw_sdio->except_mutex); + skw_sdio->service_state_map = 0; + memset(assert_context, 0, 1024); + assert_context_size = 0; + modem_notify_event(DEVICE_ASSERT_EVENT); + skw_sdio_err(" bsp assert !!!\n"); + }else if (header->len==20 && !strncmp(cmd, "DUMPDONE",8)){ + mutex_lock(&skw_sdio->except_mutex); + if(skw_sdio->cp_state==DEVICE_BLOCKED_EVENT){ + if(skw_sdio->adma_rx_enable) + skw_page_frag_free(header); + + mutex_unlock(&skw_sdio->except_mutex); + return 0; + } + skw_sdio->cp_state=DEVICE_DUMPDONE_EVENT;/*cp except set value 2*/ + mutex_unlock(&skw_sdio->except_mutex); + cancel_delayed_work_sync(&skw_sdio->skw_except_work); + if(sdio_ports[log_port_num].state == PORT_STATE_OPEN) + modem_notify_event(DEVICE_DUMPDONE_EVENT); + skw_sdio_err("The CP DUMPDONE OK : \n %d::%s\n",assert_context_size, assert_context); + for (i=0; i<5; i++) { + if(!sdio_ports[i].state || sdio_ports[i].state==PORT_STATE_CLSE) + continue; + + sdio_ports[i].state = PORT_STATE_ASST; + complete(&(sdio_ports[i].rx_done)); + if(i!=1) + complete(&(sdio_ports[i].tx_done)); + if(i==0 || i==skw_log_port()) + sdio_ports[i].next_seqno= 1; + } +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + skw_recovery_mode();//recoverymode open api +#else + if(!strncmp((char *)skw_sdio->chip_id,"SV6160",6) && !recovery_debug_status + &&skw_sdio->cp_state !=DEVICE_BLOCKED_EVENT){ + skw_recovery_mode();//recoverymode open api + } +#endif + }else if (!strncmp("trunk_W", cmd, 7)) { + memset(firmware_version, 0 , sizeof(firmware_version)); + if (strlen(cmd) > strlen(firmware_version)) { + strncpy(firmware_version, cmd, sizeof(firmware_version)-1); + firmware_version[sizeof(firmware_version)-1] = '\0'; + } else { + strncpy(firmware_version, cmd, strlen(cmd)); + //strcpy(firmware_version, cmd); + } + cmd = strstr(firmware_version, "slp="); + if (cmd) + cp_detect_sleep_mode = cmd[4] - 0x30; + else + cp_detect_sleep_mode = 4; + + if(!skw_sdio->boot_data->first_dl_flag){ + skw_sdio_gpio_irq_pre_ops(); + } + if(!skw_sdio->cp_state) + complete(&skw_sdio->download_done); + if(skw_sdio->cp_state){ + assert_info_print = 0; + if(sdio_ports[0].state == PORT_STATE_ASST) + sdio_ports[0].state = PORT_STATE_OPEN; + modem_notify_event(DEVICE_BSPREADY_EVENT); + skw_sdio_info("send the bsp state to log service or others\n"); + } + skw_sdio->cp_state = 0; + skw_sdio->log_data->smem_poweron = 0; + wake_up(&skw_sdio->wq); + skw_sdio_info("cp_state = %d \n", skw_sdio->cp_state); + //skw_sdio_info("firmware version: %s:%s \n",cmd, firmware_version); + if (skw_sdio->boot_data->nv_mem_pnfg_data != NULL && skw_sdio->boot_data->nv_mem_pnfg_size != 0) { + skw_sdio_info("UPDATE '%s' PINCFG from %s\n", (char *)skw_sdio->chip_id, skw_sdio->boot_data->skw_nv_name); + ret = skw_pin_config(); + if (ret) + skw_sdio_err("Update pin config failed!!!\n"); + } + } else if (!strncmp(cmd, "BSPREADY",8)) { + loopcheck_send_data("RDVERSION", 9); + } + skw_sdio_dbg("Line:%d the port=%d \n", __LINE__, port->channel); + if(skw_sdio->adma_rx_enable) + skw_page_frag_free(header); + return 0; + } + if(!port->state) { + if(skw_sdio->adma_rx_enable){ + if (!IS_LOG_PORT(portno)) + skw_sdio_err("port%d discard data for wrong state\n", portno); + skw_page_frag_free(header); + return 0; + } + } + if (port->sg_rx && port->rx_data){ + if (port->sg_index >= MAX_SG_COUNT){ + skw_sdio_err(" rx sg_buffer is overflow!\n"); + }else{ + sg_set_buf(&port->sg_rx[port->sg_index++], header, header->len+4); + } + }else { + int packet=0, total=0; + mutex_lock(&port->rx_mutex); + buf_size = (port->length + port->rx_wp - port->rx_rp)%port->length; + buf_size = port->length - 1 - buf_size; + addr = (char *)(header+1); + data = (u32 *) addr; + if(((data[2] & 0xffff) != port->next_seqno) && + (header->len > 12) && !IS_LOG_PORT(portno)) { + skw_sdio_err("portno:%d, packet lost recv seqno=%d expected %d\n", port->channel, + data[2] & 0xffff, port->next_seqno); + if(skw_sdio->adma_rx_enable) + skw_page_frag_free(header); + mutex_unlock(&port->rx_mutex); + return 0; + } + if(header->len > 12) { + port->next_seqno++; + addr += 12; + header->len -= 12; + total = data[1] >> 8; + packet = data[2] & 0xFFFF; + } else if (header->len == 12) { + header->len = 0; + port->tx_flow_ctrl--; + complete(&port->tx_done); + skw_port_log(portno,"%s link msg: 0x%x 0x%x port%d: %d \n", __func__, + data[0], data[1], portno, port->tx_flow_ctrl); + } + if(skw_sdio->cp_state){ + if(header->len!=245 || buf_size < 2048) { + if(assert_info_print++ < 28 && strncmp((const char *)addr, "+LOG", 4)) { + if (assert_context_size + header->len < sizeof(assert_context)) { + memcpy(assert_context + assert_context_size, addr, header->len); + assert_context_size += header->len; + } + } + } + if(buf_size <2048) + msleep(10); + } + if (port->rx_submit && !port->sg_rx) { + if (header->len && port->pdev) + port->rx_submit(portno, port->rx_data, header->len, addr); + } else if (buf_size < header->len) { + skw_port_log(portno,"%s port%d overflow:buf_size %d-%d, packet size %d (w,r)=(%d, %d)\n", + __func__, portno, buf_size, port->length, header->len, + port->rx_wp, port->rx_rp); + } else if(port->state && header->len) { + if(port->length - port->rx_wp > header->len){ + memcpy(&port->read_buffer[port->rx_wp], addr, header->len); + port->rx_wp += header->len; + } else { + memcpy(&port->read_buffer[port->rx_wp], addr, port->length - port->rx_wp); + memcpy(&port->read_buffer[0], &addr[port->length - port->rx_wp], + header->len - port->length + port->rx_wp); + port->rx_wp = header->len - port->length + port->rx_wp; + } + + if(!port->rx_flow_ctrl && buf_size-header->len < (port->length/3)) { + port->rx_flow_ctrl = 1; + skw_sdio_rx_port_follow_ctl(portno, port->rx_flow_ctrl); + } + mutex_unlock(&port->rx_mutex); + complete(&port->rx_done); + if(skw_sdio->adma_rx_enable) + skw_page_frag_free(header); + return 0; + } + mutex_unlock(&port->rx_mutex); + if(skw_sdio->adma_rx_enable) + skw_page_frag_free(header); + } + return 0; +} + +static int skw_sdio2_handle_packet(struct skw_sdio_data_t *skw_sdio, + struct scatterlist *sg, struct skw_packet2_header *header, int portno) +{ + struct sdio_port *port; + int buf_size, i, ret; + int log_port_num = 0; + char *addr; + u32 *data; + + if (portno >= max_ch_num) + return -EINVAL; + port = &sdio_ports[portno]; + log_port_num = skw_log_port(); + port->rx_packet++; + port->rx_count += header->len; + if(portno == SDIO2_LOOPCHECK_PORT) { + char *cmd = (char *)(header+4); + cmd[header->len - 12] = 0; + skw_sdio_info("LOOPCHECK channel received: %s\n", (char *)cmd); + if (header->len==19 && !strncmp(cmd, "BTREADY", 7)) { + skw_sdio->service_state_map |= 2; + //kernel_restart(0); + skw_sdio->device_active = 1; + complete(&skw_sdio->download_done); + }else if(header->len==21 && !strncmp(cmd, "WIFIREADY", 9)){ + skw_sdio->service_state_map |= 1; + //kernel_restart(0); + skw_sdio->device_active = 1; + complete(&skw_sdio->download_done); + } else if (!strncmp((char *)cmd, "BSPASSERT", 9)) { + sprintf(firmware_version, "%s:%s\n", firmware_version, cmd); + debug_infos.cp_assert_time = skw_local_clock(); +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + if(!skw_sdio->cp_state && sdio_ports[log_port_num].state != PORT_STATE_OPEN) + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(800)); + else if(!skw_sdio->cp_state && sdio_ports[log_port_num].state == PORT_STATE_OPEN) + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(8000)); +#else + if(!skw_sdio->cp_state) + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(12000)); +#endif + mutex_lock(&skw_sdio->except_mutex); + if(skw_sdio->cp_state==DEVICE_BLOCKED_EVENT){ + if (skw_sdio->adma_rx_enable) { + skw_page_frag_free(header); + } + + mutex_unlock(&skw_sdio->except_mutex); + return 0; + } + skw_sdio->cp_state=1;/*cp except set value*/ + mutex_unlock(&skw_sdio->except_mutex); + skw_sdio->service_state_map = 0; + memset(assert_context, 0, 1024); + assert_context_size = 0; + modem_notify_event(DEVICE_ASSERT_EVENT); + skw_sdio_err(" bsp assert !!!\n"); + }else if (header->len==20 && !strncmp(cmd, "DUMPDONE",8)){ + mutex_lock(&skw_sdio->except_mutex); + if(skw_sdio->cp_state==DEVICE_BLOCKED_EVENT){ + if (skw_sdio->adma_rx_enable) { + skw_page_frag_free(header); + } + + mutex_unlock(&skw_sdio->except_mutex); + return 0; + } + skw_sdio->cp_state=DEVICE_DUMPDONE_EVENT;/*cp except set value 2*/ + mutex_unlock(&skw_sdio->except_mutex); + cancel_delayed_work_sync(&skw_sdio->skw_except_work); + if(sdio_ports[log_port_num].state == PORT_STATE_OPEN) + modem_notify_event(DEVICE_DUMPDONE_EVENT); + skw_sdio_err("The CP DUMPDONE OK : \n %d::%s\n",assert_context_size, assert_context); + for (i=0; i<5; i++) { + if(!sdio_ports[i].state || sdio_ports[i].state==PORT_STATE_CLSE) + continue; + + sdio_ports[i].state = PORT_STATE_ASST; + complete(&(sdio_ports[i].rx_done)); + if(i!=1) + complete(&(sdio_ports[i].tx_done)); + if(i==1) + sdio_ports[i].next_seqno= 1; + } +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + skw_recovery_mode();//recoverymode open api +#else + if(!strncmp((char *)skw_sdio->chip_id,"SV6160LITE",12) && !recovery_debug_status + &&skw_sdio->cp_state !=DEVICE_BLOCKED_EVENT){ + skw_recovery_mode();//recoverymode open api + } +#endif + + }else if (!strncmp("trunk_W", cmd, 7)) { + memset(firmware_version, 0 , sizeof(firmware_version)); + if (strlen(cmd) > strlen(firmware_version)) { + strncpy(firmware_version, cmd, sizeof(firmware_version)-1); + firmware_version[sizeof(firmware_version)-1] = '\0'; + } else { + strncpy(firmware_version, cmd, strlen(cmd)); + //strcpy(firmware_version, cmd); + } + cmd = strstr(firmware_version, "slp="); + if (cmd) + cp_detect_sleep_mode = cmd[4] - 0x30; + else + cp_detect_sleep_mode = 4; + + if(!skw_sdio->boot_data->first_dl_flag){ + skw_sdio_gpio_irq_pre_ops(); + } + if(!skw_sdio->cp_state) + complete(&skw_sdio->download_done); + + if(skw_sdio->cp_state){ + assert_info_print = 0; + if(sdio_ports[0].state == PORT_STATE_ASST) + sdio_ports[0].state = PORT_STATE_OPEN; + modem_notify_event(DEVICE_BSPREADY_EVENT); + } + skw_sdio->cp_state = 0; + skw_sdio->log_data->smem_poweron = 0; + if (skw_sdio->boot_data->nv_mem_pnfg_data != NULL && skw_sdio->boot_data->nv_mem_pnfg_size != 0) { + skw_sdio_info("UPDATE '%s' PINCFG from %s\n", (char *)skw_sdio->chip_id, skw_sdio->boot_data->skw_nv_name); + ret = skw_pin_config(); + if (ret) + skw_sdio_err("Update pin config failed!!!\n"); + } + } else if (!strncmp(cmd, "BSPREADY",8)) { + loopcheck_send_data("RDVERSION", 9); + } + skw_sdio_dbg("Line:%d the port=%d \n", __LINE__, port->channel); + if (skw_sdio->adma_rx_enable) { + skw_page_frag_free(header); + } + return 0; + } + //skw_sdio_info("Line:%d the port=%d \n", __LINE__, port->channel); + if(!port->state) { + if(skw_sdio->adma_rx_enable){ + if (!IS_LOG_PORT(portno)) + skw_sdio_err( + "port%d discard data for wrong state\n", + portno); + skw_page_frag_free(header); + return 0; + } + } + if (port->sg_rx){ + if (port->sg_index >= MAX_SG_COUNT){ + skw_sdio_err(" rx sg_buffer is overflow!\n"); + }else{ + sg_set_buf(&port->sg_rx[port->sg_index++], header, header->len+4); + } + }else { + int packet=0, total=0; + mutex_lock(&port->rx_mutex); + buf_size = (port->length + port->rx_wp - port->rx_rp)%port->length; + buf_size = port->length - 1 - buf_size; + addr = (char *)(header+1); + data = (u32 *) addr; + if(((data[2] & 0xffff) != port->next_seqno) && header->len > 12) { + skw_sdio_err("portno:%d, packet lost recv seqno=%d expected %d\n", port->channel, + data[2] & 0xffff, port->next_seqno); + port->next_seqno = (data[2] & 0xffff); + } + if(header->len > 12) { + port->next_seqno++; + addr += 12; + header->len -= 12; + total = data[1] >> 8; + packet = data[2] & 0xFFFF; + } else if (header->len == 12) { + header->len = 0; + port->tx_flow_ctrl--; + skw_port_log(portno,"%s link msg: 0x%x 0x%x 0x%x: %d\n", __func__, + data[0], data[1], data[2], port->tx_flow_ctrl); + complete(&port->tx_done); + } + if (skw_sdio->cp_state && !IS_LOG_PORT(portno)) { + if (header->len != 245 || buf_size < 2048) + skw_sdio_info("(%d.%d) (%d,%d) len=%d : 0x%x\n", + portno, port->next_seqno, + port->rx_wp, port->rx_rp, + header->len, data[3]); + if (buf_size < 2048) + msleep(10); + } + if (port->rx_submit && !port->sg_rx) { +#ifdef CONFIG_BT_SEEKWAVE + if (header->len) +#else + if (header->len && port->pdev) +#endif + port->rx_submit(portno, port->rx_data, header->len, addr); + } else if (buf_size < header->len) { + skw_port_log(portno,"%s port%d overflow:buf_size %d-%d, packet size %d (w,r)=(%d, %d)\n", + __func__, portno, buf_size, port->length, header->len, + port->rx_wp, port->rx_rp); + } else if(port->state && header->len) { + if (port->read_buffer != NULL) { + if(port->length - port->rx_wp > header->len){ + memcpy(&port->read_buffer[port->rx_wp], addr, header->len); + port->rx_wp += header->len; + } else { + memcpy(&port->read_buffer[port->rx_wp], addr, port->length - port->rx_wp); + memcpy(&port->read_buffer[0], &addr[port->length - port->rx_wp], + header->len - port->length + port->rx_wp); + port->rx_wp = header->len - port->length + port->rx_wp; + } + } + + if(!port->rx_flow_ctrl && buf_size-header->len < (port->length/3)) { + port->rx_flow_ctrl = 1; + skw_sdio_rx_port_follow_ctl(portno, port->rx_flow_ctrl); + } + mutex_unlock(&port->rx_mutex); + complete(&port->rx_done); + if(skw_sdio->adma_rx_enable){ + skw_page_frag_free(header); + } + return 0; + } + mutex_unlock(&port->rx_mutex); + if (skw_sdio->adma_rx_enable) { + skw_page_frag_free(header); + } + } + return 0; +} +int send_modem_assert_command(void) +{ + int ret =0; + u32 *cmd = debug_infos.last_sent_wifi_cmd; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + char *statistic = kzalloc(2048, GFP_KERNEL); + if(!statistic){ + skw_sdio_err("the statistic malloc fail !!\n"); + return -1; + } + if(skw_sdio->cp_state) { + skw_sdio_err("the cp state is %d !!\n", skw_sdio->cp_state); + kfree(statistic); + statistic = NULL; + return ret; + } + skw_sdio->cp_state=1;/*cp except set value*/ + ret =skw_sdio_writeb(SKW_AP2CP_IRQ_REG, BIT(4)); + if (ret != 0) { + skw_sdio_err("the sdio writeb fail ret= %d !!\n",ret); + } + debug_infos.host_assert_cp_time = skw_local_clock(); + skw_sdio_err("%s ret=%d cmd: 0x%x 0x%x 0x%x :%d-%d %ums-%ums\n", __func__, + ret, cmd[0], cmd[1], cmd[2], skw_sdio->cp_fifo_status, fifo_ind, jiffies_to_msecs(debug_infos.last_sent_time), jiffies_to_msecs(debug_infos.host_assert_cp_time)); + skw_get_assert_print_info(statistic, 2048); + skw_sdio_info("sdio last irqs information:\n%s\n", statistic); + kfree(statistic); + statistic = NULL; +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(1000)); +#else + schedule_delayed_work(&skw_sdio->skw_except_work , msecs_to_jiffies(8000)); +#endif + return ret; +} + +/* for adma */ +static int skw_sdio_adma_parser(struct skw_sdio_data_t *skw_sdio, struct scatterlist *sgs, + int packet_count) +{ + struct skw_packet_header *header = NULL; + unsigned int i; + int channel = 0; + unsigned int parse_len = 0; + uint32_t *data; + struct sdio_port *port; + + port = &sdio_ports[0]; + for (i = 0; i < packet_count; i++) { + header = (struct skw_packet_header *)sg_virt(sgs + i); + data = (uint32_t *)header; + if (atomic_read(&skw_sdio->suspending)) + skw_sdio_info("ch:%d len:%d 0x%x 0x%x\n", header->channel, header->len, data[2], data[3]); + skw_port_log(header->channel, "%s[%d]:ch:%d len:0x%0x 0x%08X 0x%08X : 0x%08X 0x%08x 0x%08X\n", __func__, + i, header->channel, header->len, data[2], data[3], data[5], data[6], data[7]); + channel = header->channel; + + if (!header->eof && (channel < max_ch_num) && header->len) { + parse_len += header->len; + data = (uint32_t *)(header+1); + if ((channel >= max_ch_num) || (header->len > + (max_pac_size - sizeof(struct skw_packet_header))) || + (header->len == 0)) { + if (channel!=0xff) + skw_sdio_err("%s invalid header[%d]len[%d]: 0x%x 0x%x\n", + __func__, header->channel, header->len, data[0], data[1]); + skw_page_frag_free(header); + continue; + } + skw_sdio->rx_packer_cnt++; + skw_sdio_handle_packet(skw_sdio, sgs+i, header, channel); + } else { + skw_sdio_err("%s[%d]:ch:%d len:0x%0x 0x%08X 0x%08X : 0x%08X 0x%08x 0x%08X\n", __func__, + i, header->channel, header->len, data[2], data[3], data[5], data[6], data[7]); +#if 0 + print_hex_dump(KERN_ERR, "PACKET ERR:", 0, 16, 1, + header, 1792, 1); + skw_sdio_err("%s PUB HAEAD ERROR: packet[%d/%d] channel=%d,size=%d eof=%d!!!", + __func__, i, packet_count, channel, header->len, header->eof); +#endif + skw_page_frag_free(header); + continue; + } + } + if (channel >= max_ch_num) + skw_sdio_err("line: %d channel number error %d %d\n", __LINE__, channel, max_ch_num); + if (debug_infos.last_irq_time && (channel > 0 && channel < max_ch_num)) { + debug_infos.chn_last_irq_time[channel][debug_infos.chn_irq_cnt[channel] % CHN_IRQ_RECORD_NUM] = debug_infos.last_irq_time; + debug_infos.chn_irq_cnt[channel]++; + } + atomic_set(&skw_sdio->suspending, 0); + return 0; +} + +static int skw_sdio2_adma_parser(struct skw_sdio_data_t *skw_sdio, struct scatterlist *sgs, + int packet_count) +{ + struct skw_packet2_header *header = NULL; + unsigned int i; + int channel = 0; + unsigned int parse_len = 0; + uint32_t *data; + struct sdio_port *port; + + port = &sdio_ports[0]; + for (i = 0; i < packet_count; i++) { + header = (struct skw_packet2_header *)sg_virt(sgs + i); + data = (uint32_t *)header; + if (atomic_read(&skw_sdio->suspending)) + skw_sdio_info("ch:%d len:%d 0x%x 0x%x\n", header->channel, header->len, data[2], data[3]); + + skw_sdio_dbg("[%d]:protno:%d len:0x%0x 0x%08X 0x%08X : 0x%08X 0x%08x 0x%08X\n", + i, header->channel, header->len, data[2], data[3], data[5], data[6], data[7]); + skw_port_log(header->channel, "%s[%d]:ch:%d len:0x%0x 0x%08X 0x%08X : 0x%08X 0x%08x 0x%08X\n", __func__, + i, header->channel, header->len, data[2], data[3], data[5], data[6], data[7]); + + channel = header->channel; + + if (!header->eof && (channel < max_ch_num) && header->len) { + parse_len += header->len; + data = (uint32_t *)(header+1); + if ((channel >= max_ch_num) || (header->len > + (max_pac_size - sizeof(struct skw_packet2_header))) || + (header->len == 0)) { + if (channel!=0xff) + skw_sdio_err("%s invalid header[%d]len[%d]: 0x%x 0x%x\n", + __func__, header->channel, header->len, data[0], data[1]); + skw_page_frag_free(header); + continue; + } + skw_sdio->rx_packer_cnt++; + skw_sdio2_handle_packet(skw_sdio, sgs+i, header, channel); + } else { + if (channel!=0xff) + skw_sdio_err("%s[%d]:ch:%d len:0x%0x 0x%08X 0x%08X : 0x%08X 0x%08x 0x%08X\n", __func__, + i, header->channel, header->len, data[2], data[3], data[5], data[6], data[7]); + skw_page_frag_free(header); + continue; + } + } + if (channel >= max_ch_num && channel!=0xff) + skw_sdio_err("line: %d channel number error %d %d\n", __LINE__, channel, max_ch_num); + if (debug_infos.last_irq_time && (channel > 0 && channel < max_ch_num)) { + debug_infos.chn_last_irq_time[channel][debug_infos.chn_irq_cnt[channel] % CHN_IRQ_RECORD_NUM] = debug_infos.last_irq_time; + debug_infos.chn_irq_cnt[channel]++; + } + atomic_set(&skw_sdio->suspending, 0); + return 0; +} +/* for normal dma */ +static int skw_sdio_sdma_parser(char *data_buf, int total) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct skw_packet_header *header = NULL; + int channel = 0; + uint32_t *data; + unsigned char *p = NULL; + unsigned int parse_len = 0; + int current_len=0; +#if 0 + print_hex_dump(KERN_ERR, "skw_rx_buf:", 0, 16, 1, + data_buf, total, 1); +#endif + header = (struct skw_packet_header *)data_buf; + for (parse_len = 0; parse_len < total;) { + if (header->eof != 0) + break; + p = (unsigned char *)header; + data = (uint32_t *)header; + if (atomic_read(&skw_sdio->suspending)) + skw_sdio_info("ch:%d len:%d 0x%x 0x%x\n", header->channel, header->len, data[2], data[3]); + skw_port_log(header->channel, "%s:ch:%d len:0x%0x 0x%08X 0x%08X : 0x%08X 0x%08x 0x%08X\n", __func__, + header->channel, header->len, data[1], data[2], data[3], data[4], data[5]); + channel = header->channel; + current_len = header->len; + parse_len += MAX_PAC_SIZE; + if ((channel >= max_ch_num) || (current_len == 0) || + (current_len > (max_pac_size - sizeof(struct skw_packet_header)))) { + skw_sdio_err("%s skip [%d]len[%d]\n",__func__, header->channel, current_len); + break; + } + skw_sdio->rx_packer_cnt++; + skw_sdio_handle_packet(skw_sdio, NULL, header, channel); + skw_port_log(header->channel, "the -header->len----%d\n", current_len); + /* pointer to next packet header*/ + p += MAX_PAC_SIZE; + header = (struct skw_packet_header *)p; + } + if (channel >= max_ch_num) + skw_sdio_err("line: %d channel number error %d %d\n", __LINE__, channel, max_ch_num); + if (debug_infos.last_irq_time && (channel > 0 && channel < max_ch_num)) { + debug_infos.chn_last_irq_time[channel][debug_infos.chn_irq_cnt[channel] % CHN_IRQ_RECORD_NUM] = debug_infos.last_irq_time; + debug_infos.chn_irq_cnt[channel]++; + } + atomic_set(&skw_sdio->suspending, 0); + return 0; +} +static int skw_sdio2_sdma_parser(char *data_buf, int total) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct skw_packet2_header *header = NULL; + int channel = 0; + uint32_t *data; + unsigned char *p = NULL; + unsigned int parse_len = 0; + int current_len=0; +#if 0 + print_hex_dump(KERN_ERR, "skw_rx_buf:", 0, 16, 1, + data_buf, total, 1); +#endif + header = (struct skw_packet2_header *)data_buf; + for (parse_len = 0; parse_len < total;) { + if (header->eof != 0) + break; + p = (unsigned char *)header; + data = (uint32_t *)header; + if (atomic_read(&skw_sdio->suspending)) + skw_sdio_info("ch:%d len:%d 0x%x 0x%x\n", header->channel, header->len, data[2], data[3]); + + skw_sdio_dbg("ch:%d len:0x%0x 0x%08X 0x%08X : 0x%08X 0x%08x 0x%08X\n", + header->channel, header->len, data[1], data[2], data[3],data[4], data[5]); + skw_port_log(header->channel, "ch:%d len:0x%0x 0x%08X 0x%08X : 0x%08X 0x%08x 0x%08X\n", + header->channel, header->len, data[1], data[2], data[3], data[4], data[5]); + channel = header->channel; + current_len = header->len; + parse_len += MAX2_PAC_SIZE; + if ((channel >= max_ch_num) || (current_len == 0) || + (current_len > (max_pac_size - sizeof(struct skw_packet2_header)))) { + skw_sdio_err("%s skip [%d]len[%d]\n",__func__, header->channel, current_len); + break; + } + skw_sdio->rx_packer_cnt++; + skw_sdio2_handle_packet(skw_sdio, NULL, header, channel); + skw_port_log(header->channel, "the -header->len----%d\n", current_len); + /* pointer to next packet header*/ + p += MAX2_PAC_SIZE; + header = (struct skw_packet2_header *)p; + } + if (channel >= max_ch_num) + skw_sdio_err("line: %d channel number error %d %d\n", __LINE__, channel, max_ch_num); + if (debug_infos.last_irq_time && (channel > 0 && channel < max_ch_num)) { + debug_infos.chn_last_irq_time[channel][debug_infos.chn_irq_cnt[channel] % CHN_IRQ_RECORD_NUM] = debug_infos.last_irq_time; + debug_infos.chn_irq_cnt[channel]++; + } + atomic_set(&skw_sdio->suspending, 0); + return 0; +} +struct scatterlist *skw_sdio_prepare_adma_buffer(struct skw_sdio_data_t *skw_sdio, int *sg_count, int *nsize_offset) +{ + struct scatterlist *sgs; + void *buffer; + int i, j, data_size; + int alloc_size = FRAGSZ_SIZE; + + sgs = kzalloc((*sg_count) * sizeof(struct scatterlist), GFP_KERNEL); + if(sgs == NULL) + return NULL; + + for(i = 0; i < (*sg_count) - 1; i++) { + skw_sdio_dbg("skw_sdio_alloc_frag ++ %d\n", i); + buffer = skw_sdio_alloc_frag(alloc_size,GFP_ATOMIC); + if(buffer) + sg_set_buf(&sgs[i], buffer, max_pac_size); + else{ + *sg_count = i+1; + break; + } + } + + if(i <= 0) + goto err; + sg_mark_end(&sgs[*sg_count - 1]); + data_size = max_pac_size*((*sg_count)-1); + data_size = data_size%SKW_SDIO_NSIZE_BUF_SIZE; + *nsize_offset = SKW_SDIO_NSIZE_BUF_SIZE - data_size; + if(*nsize_offset < 8) + *nsize_offset = SKW_SDIO_NSIZE_BUF_SIZE + *nsize_offset; + *nsize_offset = *nsize_offset + SKW_SDIO_NSIZE_BUF_SIZE; + sg_set_buf(sgs + i, skw_sdio->next_size_buf, *nsize_offset); + return sgs; +err: + skw_sdio_err("%s failed\n", __func__); + if (i > 0) { + for (j = 0; j < i; j++) { + skw_page_frag_free(sg_virt(sgs + j)); + } + } + kfree(sgs); + sgs = NULL; + return NULL; + +} + +int skw_sdio_rx_thread(void *p) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int read_len, buf_num; + int ret = 0; + unsigned int rx_nsize = 0; + unsigned int valid_len = 0; + char *rx_buf = NULL; + struct scatterlist *sgs = NULL; + char fifo_ind = 0; + unsigned char reg = 0; + + skw_sdio_sdma_set_nsize(0); + skw_sdio_adma_set_packet_num(1); + skw_sdio->cp_fifo_status = 0; + while (1) { + /* Wait the semaphore */ + skw_sdio_rx_down(skw_sdio); + if (!debug_infos.cp_assert_time) { + debug_infos.last_rx_read_times[debug_infos.rx_read_cnt % CHN_IRQ_RECORD_NUM] = skw_local_clock(); + debug_infos.rx_read_cnt++; + } + if (skw_sdio->threads_exit) { + skw_sdio_err("line %d threads exit\n",__LINE__); + break; + } + if (!SKW_CARD_ONLINE(skw_sdio)) { + skw_sdio_unlock_rx_ws(skw_sdio); + skw_sdio_err("line %d not have card\n",__LINE__); + continue; + } + skw_resume_check(); + if (skw_sdio->irq_type == SKW_SDIO_EXTERNAL_IRQ) { + int value = gpio_get_value(skw_sdio->gpio_in); + if(value == 0) { + skw_sdio_err("line %d gpio_in:%d\n",__LINE__, value); + skw_sdio_unlock_rx_ws(skw_sdio); + continue; + } + ret = skw_sdio_readb(SKW_SDIO_CP2AP_FIFO_IND, &fifo_ind); + if(ret) { + skw_sdio_err("line %d sdio cmd52 read fail ret:%d\n",__LINE__, ret); + skw_sdio_unlock_rx_ws(skw_sdio); + continue; + } + skw_sdio_dbg("line:%d cp fifo status(%d,%d) ret=%d\n", + __LINE__,fifo_ind, skw_sdio->cp_fifo_status, ret); + if (!ret && !fifo_ind) + skw_sdio_dbg("cp fifo ret -- %d \n", ret); + if(fifo_ind == skw_sdio->cp_fifo_status && !is_timeout_kick) { + skw_sdio_info("line:%d cp fifo status(%d,%d) ret=%d\n", + __LINE__,fifo_ind, skw_sdio->cp_fifo_status, ret); + skw_sdio_unlock_rx_ws(skw_sdio); + continue; + } +#if defined(SKW_BOOT_MEMPOWERON) + if(fifo_ind == 0xFF){ + skw_sdio_err("line %d cp assert !! the cp2ap signal=%d\n",__LINE__,fifo_ind); + skw_sdio->service_index_map = SKW_WIFI; + //modem_notify_event(DEVICE_ASSERT_EVENT); + skw_sdio->boot_data->skw_dloader_module(SKW_WIFI); + + } +#endif + } + is_timeout_kick = 0; + skw_sdio->cp_fifo_status = fifo_ind; +receive_again: + if (skw_sdio->adma_rx_enable) { + int nsize_offset; + buf_num = skw_sdio->remain_packet; + if (buf_num > MAX_PAC_COUNT) + buf_num = MAX_PAC_COUNT; + + buf_num = buf_num + 1; + sgs = skw_sdio_prepare_adma_buffer(skw_sdio, &buf_num, &nsize_offset); + buf_num = buf_num -1; + if (!sgs) { + skw_sdio_err("prepare adma buffer fail\n"); + goto submit_packets; + } + ret = skw_sdio_adma_read(skw_sdio, sgs, buf_num + 1, + buf_num * max_pac_size+nsize_offset); + if (ret != 0) { + kfree(sgs); + sgs = NULL; + skw_sdio_err("%s adma read fail ret:%d\n", __func__, ret); + goto submit_packets; + } + rx_nsize = *((uint32_t *)(skw_sdio->next_size_buf + (nsize_offset - 4))); + if (SKW_SDIO_INBAND_IRQ == skw_sdio->irq_type && rx_nsize == 0) { + ret = skw_sdio_readb(SDIO_INT_EXT, ®); + if (ret < 0) { + skw_sdio_err("line %d sdio readb error ret=%d\n", __LINE__, ret); + } else { + skw_sdio_dbg("line %d SDIO_INT_EXT=0x%x\n", __LINE__, reg); + } + } + + valid_len = *((uint32_t *)(skw_sdio->next_size_buf + (nsize_offset - 8))); + skw_sdio_dbg("line:%d total:%lld next_pac:%d:, valid len:%d cnt %d\n", + __LINE__,skw_sdio->rx_packer_cnt, rx_nsize, valid_len, buf_num); + + if(skw_cp_ver == SKW_SDIO_V10){ + skw_sdio_adma_parser(skw_sdio, sgs, buf_num); + } + else{ + skw_sdio2_adma_parser(skw_sdio, sgs, buf_num); + } + kfree(sgs); + sgs = NULL; + } else { + unsigned int alloc_size; + + buf_num = skw_sdio->remain_packet; + if (buf_num > MAX_PAC_COUNT) + buf_num = MAX_PAC_COUNT; + if(skw_cp_ver == SKW_SDIO_V10) + read_len = MAX_PAC_SIZE * buf_num + SKW_SDIO_BLK_SIZE; + else + read_len = MAX2_PAC_SIZE * buf_num + SKW_SDIO_BLK_SIZE; + alloc_size = SKW_SDIO_ALIGN_BLK(read_len); + rx_buf = kzalloc(alloc_size, GFP_KERNEL); + if (!rx_buf) { + skw_sdio_err("line %d kzalloc fail\n",__LINE__); + goto submit_packets; + } + + ret = skw_sdio_sdma_read(rx_buf, alloc_size); +#if 0 + print_hex_dump(KERN_ERR, "src_sdma_data:", 0, 16, 1, + rx_buf, alloc_size, 1); +#endif + if (ret != 0) { + if(rx_buf){ + kfree(rx_buf); + rx_buf = NULL; + } + skw_sdio_err("line %d sdma read fail ret:%d\n",__LINE__, ret); + rx_nsize = 0; + goto submit_packets; + } + rx_nsize = *((uint32_t *)(rx_buf + (alloc_size- 4))); + if (SKW_SDIO_INBAND_IRQ == skw_sdio->irq_type && rx_nsize == 0) { + ret = skw_sdio_readb(SDIO_INT_EXT, ®); + if (ret < 0) { + skw_sdio_err("line %d sdio readb error ret=%d\n", __LINE__, ret); + } else { + skw_sdio_dbg("line %d SDIO_INT_EXT=0x%x\n", __LINE__, reg); + } + } + valid_len = *((uint32_t *)(rx_buf + (alloc_size - 8))); + + skw_sdio_dbg("%s the sdma rx thread alloc_size:%d,read_len:%d,rx_nsize:%d,valid_len:%d\n", + __func__,alloc_size, read_len, rx_nsize, valid_len); + if(skw_cp_ver == SKW_SDIO_V10){ + skw_sdio_sdma_parser(rx_buf, buf_num*MAX_PAC_SIZE); + } else { + skw_sdio2_sdma_parser(rx_buf, buf_num*MAX2_PAC_SIZE); + } + } +submit_packets: + skw_sdio_dispatch_packets(skw_sdio); + if(rx_buf) + kfree(rx_buf); + skw_sdio_adma_set_packet_num(rx_nsize); + if (skw_sdio->power_off) + rx_nsize = 0; + if (rx_nsize > 0) + goto receive_again; + + debug_infos.last_irq_time = 0; + skw_sdio_unlock_rx_ws(skw_sdio); + } + skw_sdio_info("%s exit\n", __func__); + return 0; +} + +static int open_sdio_port(int id, void *callback, void *data) +{ + struct sdio_port *port; + if(id >= max_ch_num) + return -EINVAL; + + port = &sdio_ports[id]; + if((port->state==PORT_STATE_OPEN) || port->rx_submit) + return -EBUSY; + port->rx_submit = callback; + port->rx_data = data; + init_completion(&port->rx_done); + init_completion(&port->tx_done); + mutex_init(&port->rx_mutex); + port->state = PORT_STATE_OPEN; + port->tx_flow_ctrl = 0; + port->rx_flow_ctrl = 0; + if(id && id!=skw_log_port()) { + port->next_seqno = 1; //cp start seqno default no 1 + port->rx_wp = port->rx_rp = 0; + } + if(id == skw_log_port()) { + skw_sdio_cp_log_disable(0); + } + skw_sdio_info("%s(%d) %s portno = %d\n", current->comm, current->pid, __func__, id); + return 0; +} +static int close_sdio_port(int id) +{ + struct sdio_port *port; + if(id >= max_ch_num) + return -EINVAL; + port = &sdio_ports[id]; + skw_sdio_info("%s(state=%d) portno = %d\n", current->comm, port->state, id); + if(!port->state) + return -ENODEV; + port->state = PORT_STATE_CLSE; + port->rx_submit = NULL; + if(id == skw_log_port()) { + skw_sdio_cp_log_disable(1); + } + complete(&port->rx_done); + return 0; +} + +void send_host_suspend_indication(struct skw_sdio_data_t *skw_sdio) +{ + uint32_t value = 0; + uint32_t timeout = 2000, timeout1 = 20; + if(skw_sdio->gpio_out>=0 && skw_sdio->gpio_in>=0 && skw_sdio->resume_com) { + skw_sdio_dbg("%s enter gpio=0\n", __func__); + skw_sdio->host_active = 0; + if (gpio_get_value(skw_sdio->gpio_in) == 0) { + udelay(10); + if (gpio_get_value(skw_sdio->gpio_in) == 0) { + disable_irq(skw_sdio->irq_num); + gpio_set_value(skw_sdio->gpio_out, 0); + do { + value = gpio_get_value(skw_sdio->gpio_in); + if (value || timeout1 == 0) { + skw_sdio_info("%s cp sts:%d in %d ms\n", __func__, value, 20 - timeout1); + enable_irq(skw_sdio->irq_num); + goto next; + } + mdelay(1); + } while(timeout1--); + } + } + gpio_set_value(skw_sdio->gpio_out, 0); +next: + skw_sdio->device_active = 0; + do { + value = gpio_get_value(skw_sdio->gpio_in); + if(value == 0) + break; + udelay(10); + }while(timeout--); + } else + skw_sdio_dbg("%s enter\n", __func__); +} + +void send_host_resume_indication(struct skw_sdio_data_t *skw_sdio) +{ + if(skw_sdio->gpio_out >= 0) { + skw_sdio_dbg("%s enter\n", __func__); + skw_sdio->host_active = 1; + gpio_set_value(skw_sdio->gpio_out, 1); + skw_sdio->resume_com = 1; + } +} + +void send_cp_wakeup_signal(struct skw_sdio_data_t *skw_sdio) +{ + if(skw_sdio->gpio_out < 0) + return; + + gpio_set_value(skw_sdio->gpio_out, 0); + udelay(5); + gpio_set_value(skw_sdio->gpio_out, 1); +} + +extern int skw_sdio_enable_async_irq(void); +int try_to_wakeup_modem(int portno) +{ + int ret = 0; + int val; + unsigned long flags; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if(skw_sdio->gpio_out < 0 || skw_sdio->gpio_in < 0 + || skw_sdio->gpio_out == skw_sdio->gpio_in) + return 0; + skw_sdio->device_active = gpio_get_value(skw_sdio->gpio_in); + + if(skw_sdio->device_active) + return 0; + skw_reinit_completion(skw_sdio->device_wakeup); + skw_sdio->tx_req_map |= 1<<portno; + skw_sdio_dbg("%s enter gpio_val=%d : %d\n", __func__, skw_sdio->device_active, skw_sdio->resume_com); + skw_port_log(portno,"%s enter device_active=%d : %d\n", __func__, skw_sdio->device_active, skw_sdio->resume_com); + if(skw_sdio->device_active == 0) { + local_irq_save(flags); + if(skw_sdio->resume_com==0) + gpio_set_value(skw_sdio->gpio_out, 1); + else + send_cp_wakeup_signal(skw_sdio); + local_irq_restore(flags); + ret = wait_for_completion_interruptible_timeout(&skw_sdio->device_wakeup, HZ/100); + if (ret < 0) { + skw_sdio->tx_req_map &= ~(1<<portno); + return -ETIMEDOUT; + } + } + val = gpio_get_value(skw_sdio->gpio_in); + if(!val) { + local_irq_save(flags); + send_cp_wakeup_signal(skw_sdio); + local_irq_restore(flags); + ret = wait_for_completion_interruptible_timeout(&skw_sdio->device_wakeup, HZ/100); + if (ret < 0) { + skw_sdio->tx_req_map &= ~(1<<portno); + return -ETIMEDOUT; + } + val = gpio_get_value(skw_sdio->gpio_in); + } + if ( val && !skw_sdio->sdio_func[FUNC_1]->irq_handler && + !skw_sdio->resume_com && skw_sdio->irq_type == SKW_SDIO_INBAND_IRQ) { + sdio_claim_host(skw_sdio->sdio_func[FUNC_1]); + ret=sdio_claim_irq(skw_sdio->sdio_func[FUNC_1],skw_sdio_inband_irq_handler); + ret = skw_sdio_enable_async_irq(); + if (ret < 0) + skw_sdio_err("enable sdio async irq fail ret = %d\n", ret); + sdio_release_host(skw_sdio->sdio_func[FUNC_1]); + skw_port_log(portno,"%s enable SDIO inband IRQ ret=%d\n", __func__, ret); + } + return ret; +} + +int wakeup_modem(int portno) +{ + int ret = 0; + int val; + unsigned long flags; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if(skw_sdio->gpio_out < 0 || skw_sdio->gpio_in < 0 + || skw_sdio->gpio_out == skw_sdio->gpio_in) + return 0; + skw_sdio->device_active = gpio_get_value(skw_sdio->gpio_in); + + skw_reinit_completion(skw_sdio->device_wakeup); + skw_sdio->tx_req_map |= 1<<portno; + skw_sdio_dbg("%s enter gpio_val=%d : %d\n", __func__, skw_sdio->device_active, skw_sdio->resume_com); + skw_port_log(portno,"%s enter device_active=%d : %d\n", __func__, skw_sdio->device_active, skw_sdio->resume_com); + + local_irq_save(flags); + if(skw_sdio->resume_com==0) + gpio_set_value(skw_sdio->gpio_out, 1); + else + send_cp_wakeup_signal(skw_sdio); + local_irq_restore(flags); + ret = wait_for_completion_interruptible_timeout(&skw_sdio->device_wakeup, HZ/100); + if (ret < 0) { + skw_sdio->tx_req_map &= ~(1<<portno); + return -ETIMEDOUT; + } + + val = gpio_get_value(skw_sdio->gpio_in); + if(!val) { + local_irq_save(flags); + send_cp_wakeup_signal(skw_sdio); + local_irq_restore(flags); + ret = wait_for_completion_interruptible_timeout(&skw_sdio->device_wakeup, HZ/100); + if (ret < 0) { + skw_sdio->tx_req_map &= ~(1<<portno); + return -ETIMEDOUT; + } + val = gpio_get_value(skw_sdio->gpio_in); + } + if ( val && !skw_sdio->sdio_func[FUNC_1]->irq_handler && + !skw_sdio->resume_com && skw_sdio->irq_type == SKW_SDIO_INBAND_IRQ) { + sdio_claim_host(skw_sdio->sdio_func[FUNC_1]); + ret=sdio_claim_irq(skw_sdio->sdio_func[FUNC_1],skw_sdio_inband_irq_handler); + ret = skw_sdio_enable_async_irq(); + if (ret < 0) + skw_sdio_err("enable sdio async irq fail ret = %d\n", ret); + sdio_release_host(skw_sdio->sdio_func[FUNC_1]); + skw_port_log(portno,"%s enable SDIO inband IRQ ret=%d\n", __func__, ret); + } + return ret; +} + +void host_gpio_in_routine(int value) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int device_active = skw_sdio->device_active; + if(skw_sdio->gpio_out < 0) + return; + + skw_sdio->device_active = value; + skw_sdio_dbg("%s enter %d-%d, host tx=0x%x:%d\n", __func__, device_active, + skw_sdio->device_active, skw_sdio->tx_req_map, skw_sdio->host_active); + if(device_active && !skw_sdio->device_active && + skw_sdio->tx_req_map && skw_sdio->host_active) { + send_cp_wakeup_signal(skw_sdio); + } + if(skw_sdio->device_active && atomic_read(&skw_sdio->resume_flag)) + complete(&skw_sdio->device_wakeup); + if(skw_sdio->device_active) { + if(skw_sdio->host_active == 0) + skw_sdio->host_active = 1; + gpio_set_value(skw_sdio->gpio_out, 1); + skw_sdio->resume_com = 1; + } +} + +static int setup_sdio_packet(void *packet, u8 channel, char *msg, int size) +{ + struct skw_packet_header *header = NULL; + u32 *data = packet; + + data[0] = 0; + header = (struct skw_packet_header *)data; + header->channel = channel; + header->len = size; + memcpy(data+1, msg, size); + data++; + data[size>>2] = 0; + header = (struct skw_packet_header *)&data[size>>2]; + header->eof = 1; + size += 8; + return size; +} +static int setup_sdio2_packet(void *packet, u8 channel, char *msg, int size) +{ + struct skw_packet2_header *header = NULL; + u32 *data = packet; + + data[0] = 0; + header = (struct skw_packet2_header *)data; + header->channel = channel; + header->len = size; + memcpy(data+1, msg, size); + data++; + data[size>>2] = 0; + header = (struct skw_packet2_header *)&data[size>>2]; + header->eof = 1; + size += 8; + return size; +} +int loopcheck_send_data(char *buffer, int size) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_port *port; + int ret, count; + int loop_port = 0; + if(skw_cp_ver == SKW_SDIO_V10){ + loop_port = LOOPCHECK_PORT; + } else { + loop_port = SDIO2_LOOPCHECK_PORT; + } + port = &sdio_ports[loop_port]; + count = (size + 3) & 0xFFFFFFFC; + if(count + 8 < port->length) { + if(skw_cp_ver == SKW_SDIO_V10){ + count = setup_sdio_packet(port->write_buffer, port->channel, buffer, count); + } + else{ + count = setup_sdio2_packet(port->write_buffer, port->channel, buffer, count); + } + try_to_wakeup_modem(loop_port); + if(!(ret = skw_sdio_sdma_write(port->write_buffer, count))) { + port->total += count; + port->sent_packet++; + ret = size; + } + skw_sdio->tx_req_map &= ~(1<<loop_port); + return ret; + } + return -ENOMEM; +} + +static int skw_sdio_suspend_send_data(int portno, char *buffer, int size) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_port *port; + int ret, count, i; + u32 *data = (u32 *)buffer; + if(size==0) + return 0; + if(portno >= max_ch_num) + return -EINVAL; + port = &sdio_ports[portno]; + if(!port->state || skw_sdio->cp_state) + return -EIO; + + if(port->state == PORT_STATE_CLSE) { + port->state = PORT_STATE_IDLE; + return -EIO; + } + + count = (size + 3) & 0xFFFFFFFC; + if(count + 8 < port->length) { + if(skw_cp_ver == SKW_SDIO_V10){ + count = setup_sdio_packet(port->write_buffer, port->channel, buffer, count); + } + else{ + count = setup_sdio2_packet(port->write_buffer, port->channel, buffer, count); + } + skw_reinit_completion(port->tx_done); + try_to_wakeup_modem(portno); + + if(skw_sdio->cp_state) + return -EIO; + + if(!(ret = skw_sdio_sdma_write(port->write_buffer, count))) { + port->tx_flow_ctrl++; + if(sdio_ports[portno].state != PORT_STATE_ASST) { + ret = wait_for_completion_interruptible_timeout(&port->tx_done, + msecs_to_jiffies(100)); + if(!ret && port->tx_flow_ctrl) { + try_to_wakeup_modem(portno); + port->tx_flow_ctrl--; + } + } + port->total += count; + port->sent_packet++; + ret = size; + } else { + skw_sdio_info("%s ret=%d\n", __func__, ret); + } + skw_sdio->tx_req_map &= ~(1<<portno); + skw_port_log(portno,"%s port%d size=%d 0x%x 0x%x\n", + __func__, portno, size, data[0], data[1]); + return ret; + } else { + for(i=0; i<2; i++) { + try_to_wakeup_modem(portno); + if(!(ret = skw_sdio_sdma_write(buffer, count))) { + port->total += count; + port->sent_packet++; + ret = size; + break; + } else { + skw_sdio_info("%s ret=%d\n", __func__, ret); + if(ret == -ETIMEDOUT && !skw_sdio->device_active) + continue; + } + } + skw_sdio->tx_req_map &= ~(1<<portno); + skw_port_log(portno,"%s port%d size=%d 0x%x 0x%x\n", + __func__, portno, size, data[0], data[1]); + return ret; + } + return -ENOMEM; +} +static int send_data(int portno, char *buffer, int size) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + struct sdio_port *port; + int ret, count, i; + u32 *data = (u32 *)buffer; + if(size==0) + return 0; + if(portno >= max_ch_num) + return -EINVAL; + port = &sdio_ports[portno]; + if(!port->state || skw_sdio->cp_state) + return -EIO; + + if(port->state == PORT_STATE_CLSE) { + port->state = PORT_STATE_IDLE; + return -EIO; + } + + count = (size + 3) & 0xFFFFFFFC; + if(count + 8 < port->length) { + if(skw_cp_ver == SKW_SDIO_V10){ + count = setup_sdio_packet(port->write_buffer, port->channel, buffer, count); + } + else{ + count = setup_sdio2_packet(port->write_buffer, port->channel, buffer, count); + } + skw_reinit_completion(port->tx_done); + for(i=0; i<2; i++) { + try_to_wakeup_modem(portno); + + if(skw_sdio->cp_state) + return -EIO; + + if(!(ret = skw_sdio_sdma_write(port->write_buffer, count))) { + port->tx_flow_ctrl++; + if(sdio_ports[portno].state != PORT_STATE_ASST) { + ret = wait_for_completion_interruptible_timeout(&port->tx_done, + msecs_to_jiffies(100)); + if(!ret && port->tx_flow_ctrl) { + skw_sdio_info("%s ret=%d:%d and retry again\n", __func__, ret, port->tx_flow_ctrl); + port->tx_flow_ctrl--; + continue; + } + } + port->total += count; + port->sent_packet++; + ret = size; + break; + } else { + skw_sdio_info("%s ret=%d\n", __func__, ret); + if(ret == -ETIMEDOUT && !skw_sdio->device_active) + continue; + } + } + skw_sdio->tx_req_map &= ~(1<<portno); + skw_port_log(portno,"%s port%d size=%d 0x%x 0x%x\n", + __func__, portno, size, data[0], data[1]); + return ret; + } else { + for(i=0; i<2; i++) { + try_to_wakeup_modem(portno); + if(!(ret = skw_sdio_sdma_write(buffer, count))) { + port->total += count; + port->sent_packet++; + ret = size; + break; + } else { + skw_sdio_info("%s ret=%d\n", __func__, ret); + if(ret == -ETIMEDOUT && !skw_sdio->device_active) + continue; + } + } + skw_sdio->tx_req_map &= ~(1<<portno); + skw_port_log(portno,"%s port%d size=%d 0x%x 0x%x\n", + __func__, portno, size, data[0], data[1]); + return ret; + } + return -ENOMEM; +} +static int sdio_read(struct sdio_port *port, char *buffer, int size) +{ + int data_size; + int ret = 0; + int buffer_size; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_dbg("%s buffer size = %d , (wp, rp) = (%d, %d), state %d\n", + __func__, size, port->rx_wp, port->rx_rp, port->state); + if(port->state == PORT_STATE_ASST) { + skw_sdio_err("Line:%d The CP assert portno =%d error code =%d cp_state=%d !!\n",__LINE__, + port->channel, ENOTCONN, skw_sdio->cp_state); + if(skw_sdio->cp_state!=0){ + if(port->channel==skw_log_port()) + port->state = PORT_STATE_OPEN; + + return -ENOTCONN; + } + } +try_again0: + skw_reinit_completion(port->rx_done); + if(port->rx_wp == port->rx_rp) { + + if ((port->state == PORT_STATE_CLSE) ||((port->channel>0&&port->channel !=skw_log_port()) + && !(skw_sdio->service_state_map & (1<<BT_SERVICE)))) { + skw_sdio_err("the log port or at port ---%d --%d\n", port->channel, skw_log_port()); + return -EIO; + } + if (port->timeout==0) { + ret = wait_for_completion_interruptible(&port->rx_done); + if(ret) + return ret; + } else{ + ret = wait_for_completion_interruptible_timeout(&port->rx_done,msecs_to_jiffies(port->timeout)); + if(ret==0) { + skw_sdio_info(" read timeout=%d\n", port->timeout); + return ret; + } + } + if(port->state == PORT_STATE_CLSE) { + port->state = PORT_STATE_IDLE; + return -EAGAIN; + }else if(port->state == PORT_STATE_ASST) { + skw_sdio_err("The CP assert portno =%d error code =%d!!!!\n", port->channel, ENOTCONN); + if(skw_sdio->cp_state!=0){ + if(port->channel==skw_log_port()) + port->state = PORT_STATE_OPEN; + + return -ENOTCONN; + } + } + } + mutex_lock(&port->rx_mutex); + data_size = (port->length + port->rx_wp - port->rx_rp)%port->length; + if(data_size==0) { + skw_sdio_info("%s buffer size = %d , (wp, rp) = (%d, %d)\n", + __func__, size, port->rx_wp, port->rx_rp); + mutex_unlock(&port->rx_mutex); + goto try_again0; + } + if(size > data_size) + size = data_size; + data_size = port->length - port->rx_rp; + if(size > data_size) { + memcpy(buffer, &port->read_buffer[port->rx_rp], data_size); + memcpy(buffer+data_size, &port->read_buffer[0], size - data_size); + port->rx_rp = size - data_size; + } else { + skw_sdio_dbg("size1 = %d , (wp, rp) = (%d, %d) (packet, total)=(%d, %d)\n", + size, port->rx_wp, port->rx_rp, port->rx_packet, port->rx_count); + memcpy(buffer, &port->read_buffer[port->rx_rp], size); + port->rx_rp += size; + } + + if (port->rx_rp == port->length) + port->rx_rp = 0; + + if(port->rx_rp == port->rx_wp){ + port->rx_rp = 0; + port->rx_wp = 0; + } + if(port->rx_flow_ctrl) { + buffer_size = (port->length + port->rx_wp - port->rx_rp)%port->length; + buffer_size = port->length - 1 - buffer_size; + + if (buffer_size > (port->length*2/3)) { + port->rx_flow_ctrl = 0; + skw_sdio_rx_port_follow_ctl(port->channel, port->rx_flow_ctrl); + } + } + mutex_unlock(&port->rx_mutex); + return size; +} + +int recv_data(int portno, char *buffer, int size) +{ + struct sdio_port *port; + int ret; + if(size==0) + return 0; + if(portno >= max_ch_num) + return -EINVAL; + port = &sdio_ports[portno]; + if(!port->state) + return -EIO; + if(port->state == PORT_STATE_CLSE) { + port->state = PORT_STATE_IDLE; + return -EIO; + } + ret = sdio_read(port, buffer, size); + return ret; +} + +int recv_data_timeout(int portno, char *buffer, int size, int *actual, int timeout) +{ + struct sdio_port *port; + int ret; + if(size==0) + return 0; + if(portno >= max_ch_num) + return -EINVAL; + port = &sdio_ports[portno]; + if(!port->state) + return -EIO; + if(port->state == PORT_STATE_CLSE) { + port->state = PORT_STATE_IDLE; + return -EIO; + } + port->timeout = timeout; + ret = sdio_read(port, buffer, size); + port->timeout = 0; + return ret; +} + +int send_data_timeout(int portno, char *buffer, int size, int *actual, int timeout) +{ + struct sdio_port *port; + int ret; + if(size==0) + return 0; + if(portno >= max_ch_num) + return -EINVAL; + port = &sdio_ports[portno]; + if(!port->state) + return -EIO; + if(port->state == PORT_STATE_CLSE) { + port->state = PORT_STATE_IDLE; + return -EIO; + } + ret = send_data(portno, buffer, size); + return ret; +} + +int skw_sdio_suspend_adma_cmd(int portno, struct scatterlist *sg, int sg_num, int total) +{ + struct sdio_port *port; + int ret, i; + int irq_state = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + u32 *data; + + if(total==0) + return 0; + if(portno >= max_ch_num) + return -EINVAL; + port = &sdio_ports[portno]; + if(!port->state) + return -EIO; + data = (u32 *)sg_virt(sg); + irq_state = skw_sdio_irq_ops(0); + for(i=0; i<2; i++) { + try_to_wakeup_modem(portno); + ret = skw_sdio_adma_write(portno, sg, sg_num, total); + if(skw_sdio->gpio_in >=0) + skw_sdio_info("timeout gpioin value=%d \n",gpio_get_value(skw_sdio->gpio_in)); + if(!ret){ + break; + } + } + if(!irq_state){ + skw_sdio_irq_ops(1); + } + skw_sdio->tx_req_map &= ~(1<<portno); + skw_port_log(portno,"%s port%d sg_num=%d total=%d 0x%x 0x%x\n", + __func__, portno, sg_num, total, data[0], data[1]); + if(portno == WIFI_CMD_PORT) { + memcpy(debug_infos.last_sent_wifi_cmd, data, 12); + debug_infos.last_sent_time = jiffies; + if (skw_sdio->gpio_in >=0 && !gpio_get_value(skw_sdio->gpio_in)) { + skw_sdio_info("modem is sleep and wakeup it\n"); + try_to_wakeup_modem(portno); + } + } + port->total += total; + port->sent_packet += sg_num; + return ret; +} + +static int skw_sdio_irq_ops(int irq_enable) +{ + int ret =-1; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); +#if 0//def CONFIG_SKW_DL_TIME_STATS + ktime_t cur_time,last_time; +#endif + //struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + if(skw_sdio->gpio_in < 0 ) { + skw_sdio_warn("gpio_in < 0 no need the cls the irq ops !!\n"); + return ret; + } + skw_sdio_info("gpio_in num %d the value %d !!\n",skw_sdio->gpio_in,gpio_get_value(skw_sdio->gpio_in)); + if(irq_enable){ + skw_sdio_info("enable irq\n"); + skw_sdio->suspend_wake_unlock_enable = 1; + enable_irq(skw_sdio->irq_num); + ret=0; + //enable_irq_wake(skw_sdio->irq_num); + //last_time = ktime_get(); + //skw_sdio_info("line %d start time %llu and the over time %llu ,the usertime=%llu \n",__LINE__, + //cur_time, last_time,(last_time-cur_time)); + }else{ + if (gpio_get_value(skw_sdio->gpio_in) == 0) { + udelay(10); + if (gpio_get_value(skw_sdio->gpio_in) == 0) { + disable_irq(skw_sdio->irq_num); + ret=0; +#if 0//def CONFIG_SKW_DL_TIME_STATS + cur_time = ktime_get(); +#endif + skw_sdio_info("disable irq\n"); + }else{ + skw_sdio_info("NO disable irq cp wake !the value %d !!\n",gpio_get_value(skw_sdio->gpio_in)); + ret = -2; + } + } + //disable_irq_wake(skw_sdio->irq_num); + //disable_irq(skw_sdio->irq_num); + } + + return ret; +}; + +int wifi_send_cmd(int portno, struct scatterlist *sg, int sg_num, int total) +{ + struct sdio_port *port; + int ret, i; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + u32 *data; + + if(total==0) + return 0; + if(portno >= max_ch_num) + return -EINVAL; + port = &sdio_ports[portno]; + if(!port->state) + return -EIO; + data = (u32 *)sg_virt(sg); + for(i=0; i<2; i++) { + try_to_wakeup_modem(portno); + ret = skw_sdio_adma_write(portno, sg, sg_num, total); + if(!ret) + break; + if (skw_sdio->gpio_in >= 0) { + skw_sdio_info("timeout gpioin value=%d \n",gpio_get_value(skw_sdio->gpio_in)); + } + } + skw_sdio->tx_req_map &= ~(1<<portno); + if (portno == WIFI_CMD_PORT || + (skw_cp_ver == SKW_SDIO_V20 && portno == WIFI_DATA_PORT)) { + skw_port_log(portno, "%s port%d sg_num=%d total=%d 0x%x 0x%x 0x%x 0x%x\n", + __func__, portno, sg_num, total, data[0], data[1], data[2], data[3]); + memcpy(debug_infos.last_sent_wifi_cmd, data, 12); + debug_infos.last_sent_time = skw_local_clock(); + } + port->total += total; + port->sent_packet += sg_num; + return ret; +} +static int register_rx_callback(int id, void *func, void *para) +{ + struct sdio_port *port; + + if(id >= max_ch_num) + return -EINVAL; + port = &sdio_ports[id]; + if(port->state && func) + return -EBUSY; + port->rx_submit = func; + port->rx_data = para; + if(func) { + port->sg_rx = kzalloc(MAX_SG_COUNT * sizeof(struct scatterlist), GFP_KERNEL); + if(port->sg_rx == NULL) + return -ENOMEM; + port->state = PORT_STATE_OPEN; + } else { + if (port->sg_rx) { + kfree(port->sg_rx); + port->sg_rx = NULL; + } + port->state = PORT_STATE_IDLE; + } + + return 0; +} +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static int bt_service_start(void) +{ + int ret =0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); +#if defined(SKW_BOOT_MEMPOWERON) + ktime_t cur, start_poll; + skw_sdio_info("the ---debug---line:%d \n",__LINE__); + + cur = ktime_get(); + if(skw_sdio->boot_data==NULL ||(skw_sdio->service_state_map & (1<<BT_SERVICE))) + return ret; + + skw_sdio_info("the ---debug---line:%d \n",__LINE__); + mutex_lock(&skw_sdio->service_mutex); + if(skw_sdio->boot_data->iram_img_data && skw_sdio->service_index_map){ + skw_sdio_info("just download the BT img!!\n"); + skw_sdio->service_index_map = SKW_BT; + skw_sdio_poweron_mem(SKW_BT); + skw_sdio->boot_data->skw_dloader_module(SKW_BT); + } + ret=skw_sdio->boot_data->bt_start(); + skw_sdio->service_index_map = SKW_BT; + start_poll = ktime_get(); + skw_sdio_info("the start service time =%lld", ktime_sub(start_poll, cur)); + mutex_unlock(&skw_sdio->service_mutex); +#else + if(skw_sdio->boot_data==NULL ||(skw_sdio->service_state_map & (1<<BT_SERVICE))) + return ret; + ret = skw_sdio->boot_data->bt_start(); +#endif + return ret; +} + +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static int bt_service_stop(void) +{ + int ret =0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info("the ---debug---line:%d \n",__LINE__); + + if(skw_sdio->boot_data ==NULL) + return ret; + mutex_lock(&skw_sdio->service_mutex); + ret=skw_sdio->boot_data->bt_stop(); + mutex_unlock(&skw_sdio->service_mutex); + return ret; +} + + +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static int wifi_service_start(void) +{ + int ret =0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info("the ---debug---line:%d \n",__LINE__); + + if (skw_sdio->boot_data==NULL ||(skw_sdio->service_state_map & (1<<WIFI_SERVICE))) + return 0; +#if defined(SKW_BOOT_MEMPOWERON) + skw_sdio_info("the ---debug---line:%d \n",__LINE__); + mutex_lock(&skw_sdio->service_mutex); +#if SKW_WIFIONLY_DEBUG//for the debug wifi only setvalue 0 + if(skw_sdio->boot_data->iram_img_data && glb_wifiready_done){ +#else + if(skw_sdio->boot_data->iram_img_data && skw_sdio->service_index_map){ +#endif + //skw_sdio->service_index_map &= SKW_WIFI; + skw_sdio_info("the ---debug---line:%d \n",__LINE__); +#if 0 + if(!skw_sdio->service_index_map){ + skw_sdio_info("the first downkload firmware!!\n"); + //skw_recovery_mode(); + //skw_sdio->service_index_map = SKW_WIFI; + }else{ + skw_sdio_info("just download the WIFI img!!\n"); + skw_sdio_poweron_mem(SKW_WIFI); + skw_sdio->boot_data->skw_dloader_module(SKW_WIFI); + } +#endif + skw_sdio_info("just download the WIFI img!!\n"); + skw_sdio->service_index_map = SKW_WIFI; + skw_sdio_poweron_mem(SKW_WIFI); + skw_sdio->boot_data->skw_dloader_module(SKW_WIFI); + } + skw_sdio_info("the ---debug---line:%d \n",__LINE__); + if (skw_sdio->boot_data->wifi_start) + ret=skw_sdio->boot_data->wifi_start(); + skw_sdio->service_index_map = SKW_WIFI; + //skw_sdio->service_index_map = SKW_WIFI; + glb_wifiready_done=1; + mutex_unlock(&skw_sdio->service_mutex); +#else + ret=skw_sdio->boot_data->wifi_start(); +#endif + return ret; +} + +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static int wifi_service_stop(void) +{ + int ret =0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + skw_sdio_info ("the ---debug---line:%d \n",__LINE__); + //debug code end + if(skw_sdio->boot_data ==NULL){ + skw_sdio_info("no wifi service start before!!"); + return ret; + } + //mutex_lock(&skw_sdio->service_mutex); + if(skw_sdio->boot_data->wifi_stop) + ret=skw_sdio->boot_data->wifi_stop(); + //mutex_unlock(&skw_sdio->service_mutex); + return ret; +} + +static int wifi_get_credit(void) +{ + char val; + int err; + + err = skw_sdio_readb(SDIOHAL_PD_DL_CP2AP_SIG4, &val); + if(err) + return err; + return val; +} +static int wifi_store_credit_to_cp(unsigned char val) +{ + int err; + + err = skw_sdio_writeb(SKW_SDIO_CREDIT_TO_CP, val); + + return err; +} + +void kick_rx_thread(void) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + debug_infos.cmd_timeout_cnt++; + is_timeout_kick = 1; + if(skw_sdio->gpio_out < 0) { + skw_sdio_rx_up(skw_sdio); + } else { + skw_sdio->device_active = gpio_get_value(skw_sdio->gpio_in); + if(skw_sdio->device_active) { + skw_sdio_rx_up(skw_sdio); + } else { + try_to_wakeup_modem(LOOPCHECK_PORT); + } + } +} +/************************************************************************ + *Decription:bluetooth log enable + *Author:junwei.jiang + *Date:2023-02-16 + *Modfiy: + * + ********************************************************************* */ +static int skw_sdio_bluetooth_log(int disable) +{ + int ret = 0; + skw_sdio_info("Enter\n"); + if (disable) { + skw_sdio_info("disable the CP log \n"); + disable = 0x10; + } else { + skw_sdio_info(" enable the CP log !!\n"); + disable = 0x18; + } + ret = skw_sdio_writeb(SDIOHAL_CPLOG_TO_AP_SWITCH, disable); + if (ret < 0) { + skw_sdio_err("cls the log signal fail ret=%d\n", ret); + return ret; + } + ret = skw_sdio_writeb(SKW_AP2CP_IRQ_REG, BIT(5)); + if (ret < 0) { + skw_sdio_err("cls log irq fail ret=%d\n", ret); + return ret; + } + + return 0; +} + +struct sv6160_platform_data wifi_pdata = { + .data_port = WIFI_DATA_PORT, + .cmd_port = WIFI_CMD_PORT, + .bus_type = SDIO_LINK|TX_ADMA|RX_ADMA|CP_DBG, + .max_buffer_size = 84*1536, + .align_value = 256, + .hw_adma_tx = wifi_send_cmd, + .hw_sdma_tx = send_data, + .callback_register = register_rx_callback, + .modem_assert = send_modem_assert_command, + .service_start = wifi_service_start, + .service_stop = wifi_service_stop, + .skw_dloader = skw_sdio_dloader, + .modem_register_notify = modem_register_notify, + .modem_unregister_notify = modem_unregister_notify, + .wifi_power_on = skw_sdio_wifi_power_on, + .at_ops = { + .port = 0, + .open = open_sdio_port, + .close = close_sdio_port, + .read = recv_data, + .write = send_data, + .read_tm = recv_data_timeout, + .write_tm = send_data_timeout, + }, + .wifi_get_credit=wifi_get_credit, + .wifi_store_credit=wifi_store_credit_to_cp, + .debug_info = assert_context, + .rx_thread_wakeup = kick_rx_thread, + .suspend_adma_cmd = skw_sdio_suspend_adma_cmd, + .suspend_sdma_cmd = skw_sdio_suspend_send_data, + +}; +struct sv6160_platform_data ucom_pdata = { + .data_port = 2, + .cmd_port = 3, + .audio_port = 4, + .bus_type = SDIO_LINK, + .max_buffer_size = 0x1000, + .align_value = 4, + .hw_sdma_rx = recv_data, + .hw_sdma_tx = send_data, + .open_port = open_sdio_port, + .close_port = close_sdio_port, + .modem_assert = send_modem_assert_command, + .service_start = bt_service_start, + .service_stop = bt_service_stop, + .modem_register_notify = modem_register_notify, + .modem_unregister_notify = modem_unregister_notify, + .skw_dump_mem = skw_sdio_dump, + .bluetooth_log_disable = skw_sdio_bluetooth_log, +}; + +int skw_sdio_bind_platform_driver(struct sdio_func *func) +{ + struct platform_device *pdev; + char pdev_name[32]; + struct sdio_port *port; + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + memset(sdio_ports, 0, sizeof(struct sdio_port)*max_ch_num); + sprintf(pdev_name, "skw_ucom"); +/* + * creaete AT device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "ATC"; + ucom_pdata.data_port = 0; + memcpy(ucom_pdata.chipid, skw_sdio->chip_id, SKW_CHIP_ID_LENGTH); + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + port->next_seqno = 1; //cp start seqno default no 1 + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE >> 2; //4K + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + port->pdev = NULL; + platform_device_put(pdev); + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + kfree(port->read_buffer); + port->read_buffer = NULL; + platform_device_put(pdev); + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } +/* + * creaete log device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "LOG"; + if(skw_cp_ver == SKW_SDIO_V10) + ucom_pdata.data_port = 1; + else + ucom_pdata.data_port = SDIO2_BSP_LOG_PORT; + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add %s device \n", ucom_pdata.port_name); + platform_device_put(pdev); + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + port->next_seqno = 1; //cp start seqno default no 1 + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE >> 2; //4K + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + platform_device_put(pdev); + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + kfree(port->read_buffer); + port->read_buffer = NULL; + platform_device_put(pdev); + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } +#ifndef CONFIG_BT_SEEKWAVE + /* + * creaete LOOPCHECK device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "LOOPCHECK"; + if(skw_cp_ver == SKW_SDIO_V10) + ucom_pdata.data_port = 7; + else + ucom_pdata.data_port = SDIO2_LOOPCHECK_PORT; + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE >> 2; + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } +#endif +#if 0 + if(skw_cp_ver == SKW_SDIO_V20){ + /* + * create BSPUPDATE device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "BSPUPDATE"; + ucom_pdata.data_port = SDIO2_BSP_UPDATE_PORT; + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE >> 2; + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + } +#endif + return ret; +} +static int skw_sdio_gpio_check(struct skw_sdio_data_t *skw_sdio) +{ + int ret = -1; + if(!skw_sdio) + return ret; + + if ((SKW_SDIO_INBAND_IRQ == skw_sdio->irq_type) && (skw_sdio->gpio_in>=0 && skw_sdio->gpio_out>=0)) { + skw_sdio_err("Please use SKW_SDIO_EXTERNAL_IRQ! irq_type=%d gpio_in=%d gpio_out=%d\n", skw_sdio->irq_type, skw_sdio->gpio_in,skw_sdio->gpio_out); + return ret; + } + return 0; +} + +int skw_sdio_bind_WIFI_driver(struct sdio_func *func) +{ + struct platform_device *pdev; + char pdev_name[32]; + struct sdio_port *port; + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + if (sdio_ports[WIFI_DATA_PORT].pdev) + return 0; + + if(skw_sdio_gpio_check(skw_sdio)<0) + return -EPERM; + + if (!strncmp((char *)skw_sdio->chip_id, "SV6160LITE", 10)) { +#ifdef SV6621S_WIRELESS + sprintf(pdev_name, "%s%d", SV6621S_WIRELESS, func->num); +#else + sprintf(pdev_name, "%s%d", SV6160_WIRELESS, func->num); +#endif + } else if (!strncmp((char *)skw_sdio->chip_id, "SV6160", 6)) { + sprintf(pdev_name, "%s%d", SV6160_WIRELESS, func->num); + } else { + skw_sdio_err( + "unknow chip id!!! pls check you porting code!! and the connect the seekwave!!\n"); + sprintf(pdev_name, "%s%d", SV6160_WIRELESS, func->num); + } + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + wifi_pdata.bus_type |= CP_RLS; +#else + if (!strncmp((char *)skw_sdio->chip_id, "SV6160", 6) || + !strncmp((char *)skw_sdio->chip_id, "SV6160LITE", 10)) { + wifi_pdata.bus_type |= CP_RLS; + } +#endif + /*support the sdma type bus*/ + if (!skw_sdio->adma_rx_enable) { + if(skw_cp_ver == SKW_SDIO_V10) + wifi_pdata.bus_type = SDIO_LINK|TX_SDMA|RX_SDMA; + else + wifi_pdata.bus_type = SDIO2_LINK|TX_SDMA|RX_SDMA; + } else { + if(skw_cp_ver == SKW_SDIO_V10) + wifi_pdata.bus_type = SDIO_LINK|TX_ADMA|RX_ADMA|CP_DBG; + else + wifi_pdata.bus_type = SDIO2_LINK|TX_ADMA|RX_ADMA|CP_DBG; + } + wifi_pdata.align_value = skw_sdio_blk_size; + skw_sdio_info(" wifi_pdata bus_type:0x%x \n", wifi_pdata.bus_type); + if(skw_cp_ver == SKW_SDIO_V20){ + if(!strncmp((char *)skw_sdio->chip_id,"SV6316",6)){ + wifi_pdata.data_port = (SDIO2_WIFI_DATA1_PORT << 4) | SDIO2_WIFI_DATA_PORT; + wifi_pdata.cmd_port = SDIO2_WIFI_CMD_PORT; + }else if(!strncmp((char *)skw_sdio->chip_id,"SV6160LITE",10)){ + wifi_pdata.data_port = SDIO2_WIFI_DATA_PORT; + wifi_pdata.cmd_port = SDIO2_WIFI_CMD_PORT; + } else { + wifi_pdata.data_port = (SDIO2_WIFI_DATA1_PORT << 4) | SDIO2_WIFI_DATA_PORT; + wifi_pdata.cmd_port = SDIO2_WIFI_CMD_PORT; + } + } + memcpy(wifi_pdata.chipid, skw_sdio->chip_id, SKW_CHIP_ID_LENGTH); + ret = platform_device_add_data(pdev, &wifi_pdata, sizeof(wifi_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + //add kfree wifi_pdata + platform_device_put(pdev);; + return ret; + } + if(skw_cp_ver == SKW_SDIO_V20){ + if(!strncmp((char *)skw_sdio->chip_id,"SV6316",12)){ + port = &sdio_ports[(wifi_pdata.data_port >> 4) & 0x0F]; + port->pdev = pdev; + port->channel = (wifi_pdata.data_port >> 4) & 0x0F; + port->rx_wp = 0; + port->rx_rp = 0; + port->sg_index = 0; + port->state = 0; + } + } + + port = &sdio_ports[wifi_pdata.data_port & 0x0F]; + port->pdev = pdev; + port->channel = wifi_pdata.data_port & 0x0F; + port->rx_wp = 0; + port->rx_rp = 0; + port->sg_index = 0; + port->state = 0; + + port = &sdio_ports[wifi_pdata.cmd_port]; + port->pdev = pdev; + port->channel = wifi_pdata.cmd_port; + port->rx_wp = 0; + port->rx_rp = 0; + port->sg_index = 0; + port->state = 0; + + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + } + return ret; +} +int skw_sdio_wifi_status(void) +{ + struct sdio_port *port = &sdio_ports[wifi_pdata.cmd_port]; + if (port->pdev == NULL) + return 0; + return 1; +} +int skw_sdio_wifi_power_on(int power_on) +{ + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + int ret; + if (power_on) { + if (skw_sdio->power_off) + skw_recovery_mode(); + + ret = skw_sdio_bind_WIFI_driver(skw_sdio->sdio_func[FUNC_1]); + } else { + ret = skw_sdio_unbind_WIFI_driver(skw_sdio->sdio_func[FUNC_1]); + } + return ret; +} +#ifdef CONFIG_BT_SEEKWAVE +int skw_sdio_bind_btseekwave_driver(struct sdio_func *func) +{ + struct platform_device *pdev; + char pdev_name[32]; + struct sdio_port *port; + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + sprintf(pdev_name, "btseekwave"); +/* + * creaete BT DATA device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + + if(skw_cp_ver == SKW_SDIO_V20){ + ucom_pdata.data_port = SDIO2_BT_DATA_PORT; + ucom_pdata.cmd_port = SDIO2_BT_CMD_PORT; + ucom_pdata.audio_port = SDIO2_BT_AUDIO_PORT; + } else { + ucom_pdata.data_port = BT_DATA_PORT; + } + + memcpy(ucom_pdata.chipid, skw_sdio->chip_id, SKW_CHIP_ID_LENGTH); + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE; + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + platform_device_put(pdev); + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } else { + skw_sdio_info("alloc %s TX buffer success\n", ucom_pdata.port_name); + } + +/* + * creaete BT COMMAND device + */ + ucom_pdata.data_port = ucom_pdata.cmd_port; + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + port->pdev = NULL; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE; + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } else { + skw_sdio_info("alloc %s TX buffer success\n", ucom_pdata.port_name); + } + +/* + * creaete BT audio device + */ + ucom_pdata.data_port = ucom_pdata.audio_port; + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + port->pdev = NULL; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE; + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } else { + skw_sdio_info("alloc %s TX buffer success\n", ucom_pdata.port_name); + } + return ret; +} +#else +int skw_sdio_bind_BT_driver(struct sdio_func *func) +{ + struct platform_device *pdev; + char pdev_name[32]; + struct sdio_port *port; + int ret = 0; + struct skw_sdio_data_t *skw_sdio = skw_sdio_get_data(); + + + sprintf(pdev_name, "skw_ucom"); +/* + * creaete BT DATA device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "BTDATA"; + if(skw_cp_ver == SKW_SDIO_V20) + ucom_pdata.data_port = SDIO2_BT_DATA_PORT; + else + ucom_pdata.data_port = BT_DATA_PORT; + memcpy(ucom_pdata.chipid, skw_sdio->chip_id, SKW_CHIP_ID_LENGTH); + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE; + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + platform_device_put(pdev); + kfree(port->read_buffer); + port->read_buffer = NULL; + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + +/* + * creaete BT COMMAND device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "BTCMD"; + if(skw_cp_ver == SKW_SDIO_V20) + ucom_pdata.data_port = SDIO2_BT_CMD_PORT; + else + ucom_pdata.data_port = ucom_pdata.cmd_port; + + //memcpy(ucom_pdata.chipid, skw_sdio->chip_id, SKW_CHIP_ID_LENGTH); + skw_sdio_info("The check chipid ucompdata = %s \n",ucom_pdata.chipid); + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add %s device \n", ucom_pdata.port_name); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE >> 2; + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + +/* + * creaete BT audio device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "BTAUDIO"; + if(skw_cp_ver == SKW_SDIO_V20) + ucom_pdata.data_port = SDIO2_BT_AUDIO_PORT; + else + ucom_pdata.data_port = ucom_pdata.audio_port; + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE >> 2; + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + kfree(port->read_buffer); + port->read_buffer = NULL; + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + + if(skw_cp_ver == SKW_SDIO_V20){ + /* + * create BTISOC device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "BTISOC"; + ucom_pdata.data_port = SDIO2_BT_ISOC_PORT; + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE >> 2; + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + kfree(port->read_buffer); + port->read_buffer = NULL; + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + + /* + * create BTLOG device + */ + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = &func->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + ucom_pdata.port_name = "BTLOG"; + ucom_pdata.data_port = SDIO2_BT_LOG_PORT; + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + dev_err(&func->dev, "failed to add platform data \n"); + platform_device_put(pdev);; + return ret; + } + port = &sdio_ports[ucom_pdata.data_port]; + port->state = PORT_STATE_IDLE; + ret = platform_device_add(pdev); + if(ret) { + dev_err(&func->dev, "failt to register platform device\n"); + platform_device_put(pdev); + return ret; + } + + port->pdev = pdev; + port->channel = ucom_pdata.data_port; + port->length = SDIO_BUFFER_SIZE >> 2; + port->read_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->read_buffer == NULL) { + dev_err(&func->dev, "failed to allocate %s RX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + port->write_buffer = kzalloc(port->length , GFP_KERNEL); + if(port->write_buffer == NULL) { + kfree(port->read_buffer); + port->read_buffer = NULL; + dev_err(&func->dev, "failed to allocate %s TX buffer\n", ucom_pdata.port_name); + return -ENOMEM; + } + } + return ret; +} +#endif +static int skw_sdio_unbind_sdio_port_driver(struct sdio_func *func, int portno) +{ + void *pdev; + struct sdio_port *port; + struct sv6160_platform_data *pdata = NULL; + skw_sdio_info("port no %d\n", portno); + port = &sdio_ports[portno]; + pdev = port->pdev; + + if (pdev) { + pdata = ((struct sv6160_platform_data + *)((struct platform_device *)pdev) + ->dev.platform_data); + if (pdata->port_name != NULL) + skw_sdio_info("port name %s %d\n", pdata->port_name, + portno); + } + port->pdev = NULL; + if (port->read_buffer) + kfree(port->read_buffer); + if (port->write_buffer) + kfree(port->write_buffer); + if (port->sg_rx) + kfree(port->sg_rx); + if (pdev) + platform_device_unregister(pdev); + port->sg_rx = NULL; + port->read_buffer = NULL; + port->write_buffer = NULL; + port->rx_wp = 0; + port->rx_rp = 0; + port->sg_index = 0; + port->state = 0; + return 0; +} + +int skw_sdio_unbind_platform_driver(struct sdio_func *func) +{ + int ret; + + ret = skw_sdio_unbind_sdio_port_driver(func, SDIO2_BSP_ATC_PORT); + if(skw_cp_ver == SKW_SDIO_V20) { + ret |= skw_sdio_unbind_sdio_port_driver(func, SDIO2_BSP_LOG_PORT); +#ifndef CONFIG_BT_SEEKWAVE + ret |= skw_sdio_unbind_sdio_port_driver(func, SDIO2_LOOPCHECK_PORT); +#endif + } else { + ret |= skw_sdio_unbind_sdio_port_driver(func, BSP_LOG_PORT); + ret |= skw_sdio_unbind_sdio_port_driver(func, LOOPCHECK_PORT); + } + return ret; +} + +int skw_sdio_unbind_WIFI_driver(struct sdio_func *func) +{ + int ret; + + ret = skw_sdio_unbind_sdio_port_driver(func, SDIO2_WIFI_CMD_PORT); + return ret; +} + +int skw_sdio_unbind_BT_driver(struct sdio_func *func) +{ + int ret = 0; + if (skw_cp_ver == SKW_SDIO_V20) { + ret = skw_sdio_unbind_sdio_port_driver(func, SDIO2_BT_DATA_PORT); + ret |= skw_sdio_unbind_sdio_port_driver(func, SDIO2_BT_CMD_PORT); + ret |= skw_sdio_unbind_sdio_port_driver(func, SDIO2_BT_AUDIO_PORT); +#ifndef CONFIG_BT_SEEKWAVE + ret |= skw_sdio_unbind_sdio_port_driver(func, SDIO2_BT_LOG_PORT); + ret |= skw_sdio_unbind_sdio_port_driver(func, SDIO2_BT_ISOC_PORT); +#endif + } else { + ret = skw_sdio_unbind_sdio_port_driver(func, BT_DATA_PORT); + ret |= skw_sdio_unbind_sdio_port_driver(func, BT_CMD_PORT); + ret |= skw_sdio_unbind_sdio_port_driver(func, BT_AUDIO_PORT); + } + return ret; +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/Kconfig b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/Kconfig new file mode 100755 index 0000000..bda1658 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/Kconfig @@ -0,0 +1,20 @@ +# +# SEEKWAVE Platfrom Device Drivers (NEW )Configuration +# +config SKW_BSP_UCOM + tristate "SeekWave BSP Drivers util For SeekWave Chip" + depends on SEEKWAVE_BSP_DRIVERS + default m + help + This is support seekwave chip for incard board. + if you want to buildin bsp driver. + please say "y". + Thanks. +config SKW_BSP_BOOT + tristate "Seekwave Platform SKW BOOT Driver Support" + depends on SEEKWAVE_BSP_DRIVERS + default m + help + Enable this module for seekwave. + if you want to use this driver,please insmod skw_boot.ko. + Thanks. diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/README.md b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/README.md new file mode 100755 index 0000000..a0a4610 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/README.md @@ -0,0 +1 @@ +#seekwave platform the skwutil demo diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/boot_config.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/boot_config.h new file mode 100755 index 0000000..7cd88e6 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/boot_config.h @@ -0,0 +1,134 @@ +/****************************************************************************** + * + * Copyright(c) 2020-2030 Seekwave Corporation. + * + *****************************************************************************/ +#ifndef __BOOT_CONFIG_H__ +#define __BOOT_CONFIG_H__ +#include <linux/types.h> +#include <linux/gpio.h> +#include <linux/delay.h> + +#ifdef CONFIG_SKW_NO_CONFIG +#define MODEM_ENABLE_GPIO 202 +#define HOST_WAKEUP_GPIO_IN -1 +#define MODEM_WAKEUP_GPIO_OUT -1 +#else +//#define MODEM_ENABLE_GPIO -1 +#error "WIFI CHIP_EN Is Not Configured Pls Check The Config!!!" +#define HOST_WAKEUP_GPIO_IN -1 +#define MODEM_WAKEUP_GPIO_OUT -1 +#endif +#define SEEKWAVE_NV_NAME "SEEKWAVE_NV_SWT6621S.bin" +//#define CONFIG_SEEKWAVE_FIRMWARE_LOAD +//#define SKW_IRAM_FILE_PATH "/vendor/firmware/SWT6621S_IRAM_USB.bin" +//#define SKW_DRAM_FILE_PATH "/vendor/firmware/SWT6621S_DRAM_USB.bin" +#define SKW_POWER_OFF_VALUE 0 + +//#define SKW_SUPPORT_MMC_NONREMOVABLE //support the host mmc card is not removable but sdcard is removable default +#define CONFIG_SEEKWAVE_PLD_RELEASE 1 +#define SKW_DUMP_BUFFER_SIZE (1200*1024) +#define POWERON_DELAY_TIME 200 +#define SKW_DMA_TYPE_CFG ADMA +#define SKW_BT_ANTENNA_CFG 0 + +#define CONFIG_NO_SERVICE_PD 0 // default ps mode cp not poweroff + + +/*********************************************************** +**CONFIG_SKW_HOST_SUPPORT_ADMA 1 : use ADMA 0 : use SDMA +** +***********************************************************/ +//#define CONFIG_SKW_HOST_SUPPORT_ADMA + +#if defined(CONFIG_SKW_HOST_SUPPORT_ADMA) +#define TX_DMA_TYPE TX_ADMA +#else +#define TX_DMA_TYPE TX_SDMA +#endif +//#define CONFIG_SKW_HOST_PLATFORM_AMLOGIC 1 +//#define CONFIG_SKW_HOST_PLATFORM_FULLHAN + +//#define USB_POWEROFF_IN_LOWPOWER 1 +#define SKW_CHIP_POWEROFF(gpiono) \ +{ \ + if(gpiono >= 0) { \ + gpio_set_value(gpiono>>1, (gpiono&0x01)); \ + } \ +} + +#define SKW_CHIP_POWERON(gpiono) \ +{ \ + if(gpiono >= 0) { \ + gpio_set_value(gpiono>>1, 1-(gpiono&0x01)); \ + } \ +} +#define SKW_MMC_HOST_SD_INDEX 1 //default sd index is 1 if not 1 pls set to 0 or 2 + +#if defined(CONFIG_SKW_HOST_PLATFORM_FULLHAN) +#define SKW_MMC_HOST_SD_INDEX 1 //default sd index is 1 if not 1 pls set to 0 or 2 +extern void fh_sdio_card_scan(int sd_id); //fullhan sdio card scan +#endif +#if defined(CONFIG_SKW_HOST_PLATFORM_AMLOGIC) +extern void extern_wifi_set_enable(int is_on); +#elif defined(CONFIG_SKW_HOST_PLATFORM_ALLWINER) +extern void sunxi_wlan_set_power(int on); +extern void sunxi_mmc_rescan_card(unsigned ids); +#elif defined(CONFIG_SKW_HOST_PLATFORM_ROCKCHIP) +extern int rockchip_wifi_power(int on); +extern int rockchip_wifi_set_carddetect(int val); +#else +extern int skw_chipen_gpio_reset(int on); +static inline int skw_chip_power_ops(int on) +{ + return skw_chipen_gpio_reset(on); +} +#endif + +static inline void skw_chip_set_power(int on) +{ +#if defined(CONFIG_SKW_HOST_PLATFORM_AMLOGIC) + extern_wifi_set_enable(on); +#elif defined(CONFIG_SKW_HOST_PLATFORM_ALLWINER) + sunxi_wlan_set_power(on); +#elif defined(CONFIG_SKW_HOST_PLATFORM_ROCKCHIP) + rockchip_wifi_power(on); +#elif defined(CONFIG_SKW_HOST_PLATFORM_HISI_BIGFISH) + hi_drv_gpio_set_dir_bit(MODEM_ENABLE_GPIO, 0); + hi_drv_gpio_write_bit(MODEM_ENABLE_GPIO, on); +#else + skw_chip_power_ops(on); +#endif + +} +static inline void skw_chip_power_reset(void) +{ +#if defined(CONFIG_SKW_HOST_PLATFORM_AMLOGIC) + printk("amlogic skw chip power reset !!\n"); + extern_wifi_set_enable(0); + msleep(POWERON_DELAY_TIME); + extern_wifi_set_enable(1); +#elif defined(CONFIG_SKW_HOST_PLATFORM_ALLWINER) + printk("allwinner skw chip power reset !!\n"); + sunxi_wlan_set_power(0); + msleep(POWERON_DELAY_TIME); + sunxi_wlan_set_power(1); +#elif defined(CONFIG_SKW_HOST_PLATFORM_ROCKCHIP) + printk("rockchip skw chip power reset !!\n"); + rockchip_wifi_power(0); + msleep(POWERON_DELAY_TIME); + rockchip_wifi_power(1); +#elif defined(CONFIG_SKW_HOST_PLATFORM_HISI_BIGFISH) + printk("hisi skw chip power reset !!\n"); + hi_drv_gpio_set_dir_bit(MODEM_ENABLE_GPIO,0); + hi_drv_gpio_write_bit(MODEM_ENABLE_GPIO,0); + msleep(POWERON_DELAY_TIME); + hi_drv_gpio_write_bit(MODEM_ENABLE_GPIO,1); +#else + printk("self skw chip power reset !!\n"); + skw_chip_power_ops(0); + msleep(POWERON_DELAY_TIME); + skw_chip_power_ops(1); +#endif +} +#endif /* __BOOT_CONFIG_H__ */ diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_boot.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_boot.c new file mode 100755 index 0000000..a1d0db1 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_boot.c @@ -0,0 +1,1226 @@ +/***************************************************************** + *Copyright (C) 2021 Seekwave Tech Inc. + *Filename : skw_boot.c + *Authors:seekwave platform + * + * This software is licensed under the terms of the the GNU + * General Public License version 2, as published by the Free + * Software Foundation, and may be copied, distributed, and + * modified under those terms. + * + * This program is distributed in the hope that it will be usefull, + * but without any warranty;without even the implied warranty of + * merchantability or fitness for a partcular purpose. See the + * GUN General Public License for more details. + * **************************************************************/ + +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/of_gpio.h> +#include <linux/completion.h> +#include <linux/moduleparam.h> +#include <linux/workqueue.h> +#include <linux/of.h> +#include <linux/device.h> +#include <linux/version.h> +#include <linux/debugfs.h> +#include <linux/fs.h> +#include <linux/ctype.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/mmc/sdio_func.h> +#include <linux/dma-mapping.h> +#include <linux/cdev.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> +#include <linux/scatterlist.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include "skw_mem_map.h" +#include "skw_boot.h" +#include "boot_config.h" +/**************************sdio boot start******************************/ +extern int cp_exception_sts; +unsigned int test_debug = 0; +unsigned char dl_signal_acount=0; +struct platform_device *btboot_pdev; +static u64 port_dmamask = DMA_BIT_MASK(32); +static struct mutex boot_mutex; +static char *local_chip_id = "SV6160LITE"; +int g_chipen_pin = -1;//default chipen -1,need update by dts or CHIPEN MACRO +int skw_use_sdma = 0;//default use adma, 0:adma 1:sdma + + +//static char iram_image_buffer[360448]; +//static char dram_image_buffer[206848]; +#ifdef CONFIG_OF +#undef CONFIG_OF +#endif +//#define SDIO_BUFFER_SIZE (16*1024) +/* + *add the little endian + * */ +#define _LITTLE_ENDIAN 1 + +#define CP_IMG_HEAD0 "kees" //"6B656573" +#define CP_IMG_HEAD1 "0616" //"30363136" +#define CP_IMG_TAIL0 "evaw" //"65766177" +#define CP_IMG_TAIL1 "0616" //"30363136" //ASCII code 36 31 36 30 +#define CP_NV_HEAD "TSVN" //"5453564E" //ASCII code 36 31 36 30 +#define CP_NV_TAIL "DEVN" //"4445564E" //ASCII code 36 31 36 30 + +#define CHIP_DEV_NAME "sv6160lite" //same with dts device +#define CHIP_DEV_NAME_COM "seekwave," CHIP_DEV_NAME + +#define IMG_HEAD_OPS_LEN 4 +#define RAM_ADDR_OPS_LEN 8 +#define MODULE_INFO_LEN 12 + +#define IMG_HEAD_INFOR_RANGE 0x200 //10K Byte + +static unsigned int EndianConv_32(unsigned int value); +/***********sdio drv extern interface **************/ +/* driect mode,reg access.etc */ +//extern int skw_get_chipid(char *chip_id); +extern int skw_boot_loader(struct seekwave_device *boot_data); +extern void *skw_get_bus_dev(void); +extern int skw_reset_bus_dev(void); +static int skw_first_boot(struct seekwave_device *boot_data); +static int skw_boot_init(struct seekwave_device *boot_data); +static int skw_download_signal_ops(void); +static int get_sleep_status(int portno, char *buffer, int size); +static int set_sleep_status(int portno, char *buffer, int size); +//int skw_cp_exception_reboot(void); +static int skw_start_bt_service(void); +static int skw_stop_bt_service(void); +/**************************sdio boot end********************************/ +struct seekwave_device *boot_data; + +//======================================================= +//debug sdio macro and Variable +//int glb_wifiready_done; +#define SKW_CPBOOT_DEBUG 0 +//======================================================= +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static unsigned int crc_16_l_calc(char *buf_ptr,unsigned int len) +{ + unsigned int i; + unsigned short crc=0; + + while(len--!=0) + { + for(i= CRC_16_L_SEED;i!=0;i=i>>1) + { + if((crc &CRC_16_L_POLYNOMIAL)!=0) + { + crc= crc<<1; + crc= crc ^ CRC_16_POLYNOMIAL; + }else{ + crc = crc <<1; + } + + if((*buf_ptr &i)!=0) + { + crc = crc ^ CRC_16_POLYNOMIAL; + } + } + buf_ptr++; + } + return (crc); +} + + +static int skw_request_firmwares(struct seekwave_device *boot_data, + const char *dram_image_name, const char *iram_image_name, const char *nv_mem_name) +{ + int ret; + const struct firmware *fw = NULL; + + skwboot_log("request_firmware %s\n", dram_image_name); + ret = request_firmware(&fw, dram_image_name, NULL); + if (ret < 0) { + skwboot_err("request_firmware %s fail\n", dram_image_name); + goto ret; + } + if(!boot_data->first_boot_flag && !boot_data->dram_img_data){ + boot_data->dram_img_data = (char *)vmalloc( fw->size); + if (!boot_data->dram_img_data) { + skwboot_err("%s,line:%d the dram img data malloc fail \n", + __func__, __LINE__); + return -ENOMEM; + } + } + skwboot_log("boot data dram_img_data %p\n",boot_data->dram_img_data); + memset(boot_data->dram_img_data, 0, fw->size); + memcpy(boot_data->dram_img_data, fw->data, fw->size); + boot_data->dram_dl_size = fw->size; + release_firmware(fw); + //dram crc16 + boot_data->dram_crc_en = 1; + boot_data->dram_crc_offset=0; + boot_data->dram_crc_val = crc_16_l_calc(boot_data->dram_img_data + boot_data->dram_crc_offset, boot_data->dram_dl_size); + + skwboot_log("request_firmware %s\n", iram_image_name); + ret = request_firmware(&fw, iram_image_name, NULL); + if (ret < 0) { + skwboot_err("request_firmware %s fail\n", iram_image_name); + goto ret; + } + if(!boot_data->first_boot_flag &&!boot_data->iram_img_data){ + boot_data->iram_img_data = (char *)vmalloc(fw->size); + if (!boot_data->iram_img_data) { + vfree(boot_data->dram_img_data); + boot_data->dram_img_data = NULL; + skwboot_err("%s,line:%d the iram img data malloc fail \n", + __func__, __LINE__); + return -ENOMEM; + } + } + memset(boot_data->iram_img_data, 0, fw->size); + memcpy(boot_data->iram_img_data, fw->data, fw->size); + boot_data->iram_dl_size = fw->size; + release_firmware(fw); + //iram crc16 + boot_data->iram_crc_en = 1; + boot_data->iram_crc_offset=0; + boot_data->iram_crc_val = crc_16_l_calc(boot_data->iram_img_data + boot_data->iram_crc_offset, boot_data->iram_dl_size); + + skwboot_log("boot data iram_img_data %p\n",boot_data->iram_img_data); + if(nv_mem_name == NULL) { + ret = 0; + skwboot_warn("nv_mem_name is NULL\n"); + goto ret; + } + skwboot_log("request_firmware %s\n", nv_mem_name); + ret = request_firmware(&fw, nv_mem_name, NULL); + if (ret < 0) { + skwboot_err("request_firmware %s fail\n", nv_mem_name); + ret = ENOENT; + goto ret; + } + + boot_data->nv_mem_data = (char *)kzalloc(fw->size, GFP_KERNEL); + if (boot_data->nv_mem_data == NULL) { + skwboot_err("alloc nv memory failed\n"); + goto relese_fw; + } + memcpy(boot_data->nv_mem_data, fw->data, fw->size); + boot_data->nv_mem_size = fw->size; + if (boot_data->nv_mem_size > 20) {//new nv + boot_data->nv_mem_cmfg_data = boot_data->nv_mem_data + *(u32 *)(boot_data->nv_mem_data + NV_CMFG_OFFSET); + boot_data->nv_mem_cmfg_size = *(u32 *)(boot_data->nv_mem_data + NV_CMFG_SIZE); + + if (*(u32 *)(boot_data->nv_mem_data + NV_PNFG_OFFSET) != 0) + boot_data->nv_mem_pnfg_data = boot_data->nv_mem_data + *(u32 *)(boot_data->nv_mem_data + NV_PNFG_OFFSET); + else + boot_data->nv_mem_pnfg_data = NULL; + boot_data->nv_mem_pnfg_size = *(u32 *)(boot_data->nv_mem_data + NV_PNFG_SIZE); + } + ret=0; + boot_data->nvmem_crc_en = 1; + boot_data->nvmem_crc_offset=0; + boot_data->nvmem_crc_val = crc_16_l_calc(boot_data->nv_mem_data + boot_data->nvmem_crc_offset, boot_data->nv_mem_size); + //print_hex_dump(KERN_ERR, "nvdata:", 0, 16, 1, boot_data->nv_mem_data, boot_data->nv_mem_size, 1); + +relese_fw: + release_firmware(fw); +ret: + return ret; +} +static int skw_of_property_read(const struct device_node *np, + const char *propname, u32 *out_value) +{ +#if KERNEL_VERSION(3, 1, 0) <= LINUX_VERSION_CODE + return of_property_read_u32(np, propname, out_value); +#else +#if 0 + const unsigned int *value=NULL; + value = of_get_property(np, propname,out_value); + if (value == NULL){ + return ENXIO; + }else{ + return 0; + } +#endif + return ENXIO; +#endif +} +static int seekwave_boot_parse_dt(struct platform_device *pdev, struct seekwave_device *boot_data) +{ + int ret = 0; + enum of_gpio_flags flags; + int tmp_gpio; + struct device_node *np = pdev->dev.of_node; + /*add the dma type dts config*/ + if (skw_of_property_read(np, "bt_antenna", &(boot_data->bt_antenna))){ + skwboot_warn("no BT_antenna setting\n"); + boot_data->bt_antenna = SKW_BT_ANTENNA_CFG; + } else + skwboot_log("BT_antenna setting: %d\n", boot_data->bt_antenna); + + if (skw_of_property_read(np, "dma_type", &(boot_data->dma_type))){ + boot_data->dma_type = SKW_DMA_TYPE_CFG; + g_chipen_pin = boot_data->chip_en = MODEM_ENABLE_GPIO; + boot_data->host_gpio = HOST_WAKEUP_GPIO_IN; + boot_data->chip_gpio = MODEM_WAKEUP_GPIO_OUT; + skwboot_warn("no DTS setting\n"); + } else { +#if KERNEL_VERSION(3, 1, 0) <= LINUX_VERSION_CODE + boot_data->host_gpio = of_get_named_gpio_flags(np, "gpio_host_wake", 0, &flags); + boot_data->chip_gpio = of_get_named_gpio_flags(np, "gpio_chip_wake",0, &flags); + g_chipen_pin = boot_data->chip_en = of_get_named_gpio_flags(np, "gpio_chip_en",0, &flags); +#endif + } + boot_data->dma_type = skw_use_sdma?SDMA:ADMA; + skwboot_log("%s,modem boot setting::=>\n", __func__); + skwboot_log("chipen=%d\n", boot_data->chip_en); + skwboot_log("hst_wak_wf=%d\n", boot_data->chip_gpio); + skwboot_log("wf_wak_hst=%d\n", boot_data->host_gpio); + skwboot_log("dma_type=%d\n", boot_data->dma_type); + skwboot_log("bt_antenna=%d\n", boot_data->bt_antenna); + + if (test_debug) { + if ((test_debug & 0xF) == 0x1) { + boot_data->chip_gpio = -1; + boot_data->host_gpio = -1; + } else if (test_debug == 5) + boot_data->host_gpio = -1; +#if KERNEL_VERSION(3, 1, 0) <= LINUX_VERSION_CODE + if (boot_data->host_gpio >= 0) { + if ((test_debug & 0xF) == 0x2) { + skwboot_log("gpio in out swap\n"); + tmp_gpio = boot_data->chip_gpio; + boot_data->chip_gpio = boot_data->host_gpio; + boot_data->host_gpio = tmp_gpio; + ret = devm_gpio_request_one( + &pdev->dev, boot_data->host_gpio, + GPIOF_IN, "WL_WAKE_HOST"); + } else if ((test_debug & 0xF) == 0x3) { + skwboot_log("test_debug 3 = gpio in high\n"); + ret = devm_gpio_request_one( + &pdev->dev, boot_data->host_gpio, + GPIOF_OUT_INIT_HIGH, "WL_WAKE_HOST"); + } else if ((test_debug & 0xF) == 0x4) { + skwboot_log( + "test_debug 4 = gpio in out swap out low\n"); + tmp_gpio = boot_data->chip_gpio; + boot_data->chip_gpio = boot_data->host_gpio; + boot_data->host_gpio = tmp_gpio; + ret = devm_gpio_request_one( + &pdev->dev, boot_data->host_gpio, + GPIOF_OUT_INIT_LOW, "WL_WAKE_HOST"); + } else { + ret = devm_gpio_request_one( + &pdev->dev, boot_data->host_gpio, + GPIOF_IN, "WL_WAKE_HOST"); + } + if (boot_data->chip_gpio >= 0) + ret = devm_gpio_request_one( + &pdev->dev, boot_data->chip_gpio, + GPIOF_OUT_INIT_HIGH, "HOST_WAKE_WL"); + } +#endif + if (boot_data->chip_gpio >= 0 && boot_data->host_gpio >= 0) { + if (test_debug & 0xF0) + boot_data->slp_disable = 1; + else + boot_data->slp_disable = 0; + } else { + boot_data->slp_disable = 1; + } + } else { +#if KERNEL_VERSION(3, 1, 0) <= LINUX_VERSION_CODE + if (boot_data->host_gpio >= 0) + ret = devm_gpio_request_one(&pdev->dev, + boot_data->host_gpio, + GPIOF_IN, "WL_WAKE_HOST"); + if (boot_data->chip_gpio >= 0) + ret = devm_gpio_request_one(&pdev->dev, + boot_data->chip_gpio, + GPIOF_OUT_INIT_HIGH, + "HOST_WAKE_WL"); +#endif + if (boot_data->chip_gpio >= 0 && boot_data->host_gpio >= 0) { + boot_data->slp_disable = 0; + } else { + boot_data->slp_disable = 1; + } + } + skwboot_log("%s,chipen=%d, gpio_out:%d gpio_in:%d ret = %d\n", __func__, + boot_data->chip_en, boot_data->chip_gpio, + boot_data->host_gpio, ret); +#if KERNEL_VERSION(3, 1, 0) <= LINUX_VERSION_CODE + if (boot_data->chip_en >= 0) + ret = devm_gpio_request_one(&pdev->dev, boot_data->chip_en, GPIOF_OUT_INIT_HIGH,"CHIP_EN"); +#endif + return ret; +} + +/************************************************************************/ +//Description: BT start service +//Func: BT start service +//Call: +//Author:junwei.jiang +//Date:2021-110 +//Modify: +/************************************************************************/ +static int bt_start_service(int id, void *callback, void *data) +{ + int ret=0; + if(cp_exception_sts) + return -1; + + skwboot_log("%s pid:%d %s\n", current->comm, current->pid, __func__); + ret = skw_start_bt_service(); + if(ret < 0){ + skwboot_err("%s boot bt fail \n", __func__); + return -1; + } + return 0; +} + +/************************************************************************/ +//Description: BT stop service +//Func: BT stop service +//Call: +//Author:junwei.jiang +//Date:2021-11-1 +//Modify: +/************************************************************************/ +static int bt_stop_service(int id) +{ + int ret=0; + + if(cp_exception_sts) + return 0; + + ret = skw_stop_bt_service(); + if(ret < 0){ + skwboot_err("%s boot bt fail \n", __func__); + return -1; + } + skwboot_log("%s OK\n",__func__); + return 0; +} +static void seekwave_release(struct device *dev) + +{ +} +static struct platform_device seekwave_device ={ + .name = CHIP_DEV_NAME, + .dev = { + .release = seekwave_release, + } +}; + +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static int seekwave_boot_probe(struct platform_device *pdev) +{ + int ret; + int time_count=0; + struct device *io_bus; + + if (pdev != &seekwave_device) + seekwave_device.name = NULL; + boot_data = devm_kzalloc(&pdev->dev, sizeof(struct seekwave_device), GFP_KERNEL); + if (!boot_data) { + skwboot_err("%s :kzalloc error !\n", __func__); + return -ENOMEM; + } + mutex_init(&boot_mutex); + seekwave_boot_parse_dt(pdev, boot_data); + io_bus = skw_get_bus_dev(); + if (!io_bus) { + skwboot_log("%s :CHIP_RESET AGAIN!\n", __func__); + skw_chip_power_reset(); + do { + msleep(10); + io_bus = skw_get_bus_dev(); + } while(!io_bus && time_count++ < 50); + } + if (!io_bus) { + devm_kfree(&pdev->dev, boot_data); + skwboot_err("%s get bus dev fail !\n",__func__); + return -ENODEV; + } + //get chip id + if (!strncmp(local_chip_id,"SV6160LITE",10)) { + if (!strncmp(io_bus->bus->name, "usb", 3)) { + boot_data->iram_file_path = "SWT6621S_IRAM_USB.bin"; + boot_data->dram_file_path = "SWT6621S_DRAM_USB.bin"; + boot_data->skw_nv_name = "SWT6621S_NV_USB.bin"; + } else { + boot_data->iram_file_path = "SWT6621S_IRAM_SDIO.bin"; + boot_data->dram_file_path = "SWT6621S_DRAM_SDIO.bin"; + boot_data->skw_nv_name = "SWT6621S_NV_SDIO.bin"; + } + } else if (!strncmp(local_chip_id,"SV6160",6)) { + if (!strncmp(io_bus->bus->name, "usb", 3)) { + boot_data->iram_file_path = "SWT6621_IRAM_USB.bin"; + boot_data->dram_file_path = "SWT6621_DRAM_USB.bin"; + } else { + boot_data->iram_file_path = "SWT6621_IRAM_SDIO.bin"; + boot_data->dram_file_path = "SWT6621_DRAM_SDIO.bin"; + } + boot_data->skw_nv_name = NULL; + } else if (!strncmp(local_chip_id,"SV6316",6)) { + if (!strncmp(io_bus->bus->name, "usb", 3)) { + boot_data->iram_file_path = "SWT6652_IRAM_USB.bin"; + boot_data->dram_file_path = "SWT6652_DRAM_USB.bin"; + } else if (!strncmp(io_bus->bus->name, "pci", 3)) { + boot_data->pdev = pdev; + if (container_of(io_bus, struct pci_dev, dev)->device == 0x6316) { + boot_data->iram_file_path = "SWT6652_IRAM_PCIE.bin"; + boot_data->dram_file_path = "SWT6652_DRAM_PCIE.bin"; + } else if (container_of(io_bus, struct pci_dev, dev)->device == 0x6315) { + boot_data->iram_file_path = "SWT6652S_IRAM_PCIE.bin"; + boot_data->dram_file_path = "SWT6652S_DRAM_PCIE.bin"; + } + } else { + boot_data->iram_file_path = "SWT6652_IRAM_SDIO.bin"; + boot_data->dram_file_path = "SWT6652_DRAM_SDIO.bin"; + } + boot_data->skw_nv_name = "SEEKWAVE_NV_SWT6652.bin"; + } else { + skwboot_warn("%s:get chip id is NULL!!!\n", __func__); + } + ret = skw_boot_init(boot_data); + if (ret < 0) { + skwboot_err("%s:boot init fail\n", __func__); + return -1; + } + boot_data->pdev = pdev; + ret = skw_first_boot(boot_data); + if (strncmp(io_bus->bus->name, "usb", 3)) + skw_bind_boot_driver(io_bus); + return ret; +} +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static int seekwave_boot_remove(struct platform_device *pdev) +{ + skwboot_log("%s the Enter \n", __func__); + + if (btboot_pdev) { + platform_device_unregister(btboot_pdev); + btboot_pdev = NULL; + } + if (boot_data) { + if (!boot_data->iram_img_data) { + skwboot_log(":iram_img_data is NULL\n"); + } else { + vfree(boot_data->iram_img_data); + boot_data->iram_img_data = NULL; + } + if (!boot_data->dram_img_data) { + skwboot_err(":dram_img_data is NULL\n"); + } else { + vfree(boot_data->dram_img_data); + boot_data->dram_img_data = NULL; + } + if (boot_data->nv_mem_size > 20) { //new nv + skwboot_log(":free nv_mem_data\n"); + if (boot_data->nv_mem_data) { + skwboot_log(":free 2 nv_mem_data\n"); + kfree(boot_data->nv_mem_data); + boot_data->nv_mem_data = NULL; + } + } + boot_data->dl_base_img = NULL; + boot_data->skw_nv_name = NULL; + boot_data->iram_file_path = NULL; + boot_data->dram_file_path = NULL; + boot_data->wifi_start = NULL; + boot_data->wifi_stop = NULL; + boot_data->bt_start = NULL; + boot_data->bt_stop = NULL; + boot_data->skw_dloader_module = NULL; + devm_kfree(&pdev->dev, boot_data); + boot_data = NULL; + } else { + skwboot_err("%s:boot_data is NULL\n", __func__); + } + mutex_destroy(&boot_mutex); + return 0; +} +extern void skw_modem_log_stop_rec(void); +static void seekwave_boot_shutdown(struct platform_device *pdev) +{ + skwboot_log("%s enter ...\n", __func__); + skw_modem_log_stop_rec(); + skw_reset_bus_dev(); +} +static const struct of_device_id seekwave_match_table[] ={ + + { .compatible = CHIP_DEV_NAME_COM}, + { }, +}; + +static struct platform_driver seekwave_driver ={ + + .driver = { + .owner = THIS_MODULE, + .name = CHIP_DEV_NAME, + .of_match_table = seekwave_match_table, + }, + .probe = seekwave_boot_probe, + .remove = seekwave_boot_remove, + .shutdown = seekwave_boot_shutdown, +}; + +/*********************************************************************** + *Description:chipen gpio pin reset + *Seekwave tech LTD + *Author:zongqiang.cheng + *Date:2025-3-18 + *Modify: + ***********************************************************************/ +int skw_chipen_gpio_reset(int on) +{ + int chip_en = g_chipen_pin; + if (chip_en < 0){ + printk("chip_en need set !!\n"); + return -1; + } + + if(on) { + gpio_set_value(chip_en, 1); + printk("seekwave power on !!\n"); + } else { + gpio_set_value(chip_en, 0); + printk("seekwave power down !!\n"); + } + return 0; +} + +/*********************************************************************** + *Description:BT download boot pdata + *Seekwave tech LTD + *Author:junwei.jiang + *Date:2021-11-3 + *Modify: + ***********************************************************************/ +static int get_sleep_status(int portno, char *buffer, int size) +{ + memcpy(buffer, "WAKE", 4); + if (boot_data->host_gpio >=0) { + if (gpio_get_value(boot_data->host_gpio) == 0) + memcpy(buffer, "DOWN", 4); + } + return 4; +} +static int set_sleep_status(int portno, char *buffer, int size) +{ + int i, count; + + for(i=0; i<2; i++) { + if (gpio_get_value(boot_data->host_gpio)) + return 1; + if(buffer && !strncmp(buffer, "WAKE", 4)) { + gpio_set_value(boot_data->chip_gpio, 0); + udelay(10); + gpio_set_value(boot_data->chip_gpio, 1); + } + count = 0; + do { + if (count++ < 100) + udelay(20); + } while(gpio_get_value(boot_data->host_gpio) ==0); + if (gpio_get_value(boot_data->host_gpio)) + return 1; + udelay(100); + } + if (gpio_get_value(boot_data->host_gpio)==0) + skwboot_log("wakeup CHIP timeout!!! \n"); + return 1; +} +struct sv6160_platform_data boot_pdata = { + .data_port = 8, + .bus_type = SDIO_LINK, + .max_buffer_size = 0x800, + .align_value = 4, + .hw_sdma_rx = get_sleep_status, + .hw_sdma_tx = set_sleep_status, + .open_port = bt_start_service, + .close_port = bt_stop_service, +}; + +/*************************************************************** + *Description:BT bind boot driver + *Seekwave tech LTD + *Author:junwei.jiang + *Date:2021-11-3 + *Modify: +***************************************************************/ +int skw_bind_boot_driver(struct device *dev) +{ + struct platform_device *pdev; + char pdev_name[32]; + int ret = 0; + sprintf(pdev_name, "skw_ucom"); + if(!dev){ + skwboot_err("%s the dev fail \n", __func__); + return -1; + } + if (btboot_pdev) + return ret; + + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + pdev->dev.parent = dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + boot_pdata.port_name = "BTBOOT"; + boot_pdata.data_port = 8; + //skw_get_chipid((char *)&boot_data->chip_id); + ret = platform_device_add_data(pdev, &boot_pdata, sizeof(boot_pdata)); + if(ret) { + dev_err(dev, "failed to add boot data \n"); + platform_device_put(pdev); + return ret; + } + ret = platform_device_add(pdev); + if(ret) { + platform_device_put(pdev); + skwboot_err("%s,line:%d the device add fail \n",__func__,__LINE__); + return ret; + } + btboot_pdev = pdev; + return ret; +} +/**************************************************************** + *Description:the data Little Endian process interface + *Func:EndianConv_32 + *Calls:None + *Call By:The img data process + *Input:value + *Output:the Endian data + *Return:value + *Others: + *Author:JUNWEI.JIANG + *Date:2021-08-26 + * **************************************************************/ +static unsigned int EndianConv_32(unsigned int value) +{ +#ifdef _LITTLE_ENDIAN + unsigned int nTmp = (value >>24 | value <<24); + nTmp |= ((value >> 8) & 0x0000FF00); + nTmp |= ((value << 8) & 0x00FF0000); + return nTmp; +#else + return value; +#endif +} + +/**************************************************************** + *Description:dram read the double img file + *Func: + *Calls: + *Call By: + *Input:the file path + *Output:download data and the data size dl_data image_size + *Return:0:pass other fail + *Others: + *Author:JUNWEI.JIANG + *Date:2022-02-07 + * **************************************************************/ +static int skw_download_signal_ops(void) +{ + unsigned int tmp_signal = 0; + //download done flag ++ + boot_data->dl_done_signal ++; + tmp_signal = boot_data->dl_done_signal; + boot_data->dl_done_signal = 0xff&tmp_signal; + boot_data->dl_acount_addr = SKW_SDIO_PD_DL_AP2CP_BSP; + + //gpio need set high or low power interrupt to cp wakeup + boot_data->gpio_out = boot_data->chip_gpio; + if(boot_data->gpio_val) + boot_data->gpio_val =0; + else + boot_data->gpio_val =1; + skwboot_log("%s line:%d download data ops done the dl_count=%d \n", __func__, __LINE__,boot_data->dl_done_signal); + return 0; +} +/**************************************************************** + *Description:analysis the double img dram iram + *Func: + *Calls: + *Call By: + *Input:the file path + *Output:download data and the data size dl_data image_size + *Return:0:pass other fail + *Others: + *Author:JUNWEI.JIANG + *Date:2022-02-07 + * **************************************************************/ +static int skw_boot_init(struct seekwave_device *boot_data) +{ + int i =0; + int k =0; + unsigned int head_offset=0; + unsigned int tail_offset=0; + int ret = 0; + struct img_head_data_t dl_data_info; + unsigned int *data=NULL; + unsigned int *nvdata=NULL; + unsigned int *dl_addr_data=NULL; + + ret = skw_request_firmwares(boot_data, boot_data->dram_file_path, + boot_data->iram_file_path, + boot_data->skw_nv_name); + if (ret == ENOENT) { + ret = skw_request_firmwares(boot_data, + boot_data->dram_file_path, + boot_data->iram_file_path, + SEEKWAVE_NV_NAME); + } + skwboot_log("image_size=%d,%d, ret=%d\n", boot_data->iram_dl_size, + boot_data->dram_dl_size, ret); + if (ret < 0){ + skwboot_err("request image fail\n"); + return ret; + } + boot_data->head_addr = 0; + boot_data->tail_addr = 0; + boot_data->bsp_head_addr = 0; + boot_data->bsp_tail_addr = 0; + boot_data->wifi_head_addr =0; + boot_data->wifi_tail_addr = 0; + boot_data->bt_head_addr = 0; + boot_data->bt_tail_addr = 0; + boot_data->nv_head_addr = 0; + boot_data->nv_tail_addr = 0; + boot_data->nv_data_size = 0; + + if(boot_data->iram_img_data!=NULL){ + /*analysis the img*/ + for(i=0; i*IMG_HEAD_OPS_LEN<IMG_HEAD_INFOR_RANGE; i++) + { + if(!head_offset) + { + if((0==memcmp(CP_IMG_HEAD0, boot_data->iram_img_data+i*IMG_HEAD_OPS_LEN,IMG_HEAD_OPS_LEN))&& + (0==memcmp(CP_IMG_HEAD1,boot_data->iram_img_data+(i+1)*IMG_HEAD_OPS_LEN,IMG_HEAD_OPS_LEN))) + head_offset = (i+1)*IMG_HEAD_OPS_LEN; + }else if(!tail_offset){ + if((0==memcmp(CP_IMG_TAIL0, boot_data->iram_img_data+i*IMG_HEAD_OPS_LEN, IMG_HEAD_OPS_LEN))&& + (0==memcmp(CP_IMG_TAIL1, boot_data->iram_img_data+(i+1)*IMG_HEAD_OPS_LEN, IMG_HEAD_OPS_LEN))){ + tail_offset = (i-1)*IMG_HEAD_OPS_LEN; + break; + } + } + } + + /*analysis the nv*/ + for(k=0; k*IMG_HEAD_OPS_LEN<IMG_HEAD_INFOR_RANGE; k++) + { + if(!boot_data->nv_head_addr) + { + if(0==memcmp(CP_NV_HEAD, boot_data->iram_img_data+k*IMG_HEAD_OPS_LEN,IMG_HEAD_OPS_LEN)) + boot_data->nv_head_addr = k*IMG_HEAD_OPS_LEN; + }else if(!boot_data->nv_tail_addr){ + if((0==memcmp(CP_NV_TAIL, boot_data->iram_img_data+k*IMG_HEAD_OPS_LEN, IMG_HEAD_OPS_LEN))){ + boot_data->nv_tail_addr = k*IMG_HEAD_OPS_LEN; + boot_data->nv_data_size = boot_data->nv_tail_addr - boot_data->nv_head_addr - IMG_HEAD_OPS_LEN; + nvdata = (u32 *) &boot_data->iram_img_data[boot_data->nv_head_addr]; + break; + } + } + } + if(!tail_offset){ + skwboot_err("%s,%d,the iram_img not need analysis!!! or Fail!! \n",__func__,__LINE__); + return -1; + }else{ + //get the iram img addr and dram img addr + dl_addr_data = (unsigned int *)(boot_data->iram_img_data+head_offset+IMG_HEAD_OPS_LEN); + boot_data->iram_dl_addr = dl_addr_data[0]; + boot_data->dram_dl_addr = dl_addr_data[1]; + head_offset = head_offset+RAM_ADDR_OPS_LEN;//jump the ram addr data; + + skwboot_log("%s line:%d,the tail_offset ---0x%x, the head_offset --0x%x ,iram_addr=0x%x,dram_addr=0x%x, \ + nv_head_addr:0x%x,nv_tail_addr:0x%x,nv_size=%d\n",__func__, __LINE__,tail_offset, head_offset, + boot_data->iram_dl_addr,boot_data->dram_dl_addr,boot_data->nv_head_addr,boot_data->nv_tail_addr, + boot_data->nv_data_size); + } + /*need download the img bin for WIFI or BT service dl_module >0*/ + head_offset = head_offset +IMG_HEAD_OPS_LEN; + /*get the img head tail offset*/ + boot_data->head_addr = head_offset; + boot_data->tail_addr = tail_offset; + + skwboot_log("%s line:%d analysis the img module\n", __func__, __LINE__); + for(i=0; i*MODULE_INFO_LEN<=(tail_offset-head_offset); i++) + { + data = (unsigned int *)(boot_data->iram_img_data +head_offset+i*MODULE_INFO_LEN); + dl_data_info.dl_addr=data[0]; + dl_data_info.write_addr =data[2]; + dl_data_info.index = 0x000000FF&EndianConv_32(data[1]); + dl_data_info.data_size = 0x00FFFFFF&data[1]; + if(dl_data_info.index==1){ + boot_data->bsp_index_count +=1; + }else if(dl_data_info.index ==2){ + boot_data->wifi_index_count +=1; + }else if(dl_data_info.index ==3){ + boot_data->bt_index_count +=1; + } + skwboot_log("%s line:%d dl_addr=0x%x, write_addr=0x%x, index=0x%x,data_size=0x%x\n", __func__, + __LINE__, dl_data_info.dl_addr,dl_data_info.write_addr,dl_data_info.index,dl_data_info.data_size); + } + skwboot_log("%s line:%d bsp_index count:%d, bt_index_count=%d, wifi_index_count=%d \n", + __func__, __LINE__,boot_data->bsp_index_count,boot_data->bt_index_count,boot_data->wifi_index_count); + //get the dl count for the service download module img + boot_data->wifi_dl_count = boot_data->bsp_index_count + boot_data->wifi_index_count; + boot_data->all_dl_count = boot_data->bsp_index_count + boot_data->wifi_index_count + boot_data->bt_index_count; + boot_data->bt_dl_count = boot_data->bsp_index_count + boot_data->bt_index_count; + + if (boot_data->nv_mem_size > 20) {//new nv + if(boot_data->nv_mem_cmfg_size && (boot_data->nv_mem_cmfg_size <= boot_data->nv_data_size)){ + memcpy((boot_data->iram_img_data+boot_data->nv_head_addr+4),boot_data->nv_mem_cmfg_data,boot_data->nv_mem_cmfg_size); + //kfree(boot_data->nv_mem_data); + //boot_data->nv_mem_data = NULL; + } + } else {//old nv + if(boot_data->nv_mem_size && (boot_data->nv_mem_size <= boot_data->nv_data_size)){ + memcpy((boot_data->iram_img_data+boot_data->nv_head_addr+4),boot_data->nv_mem_data,boot_data->nv_mem_size); + kfree(boot_data->nv_mem_data); + boot_data->nv_mem_data = NULL; + } + } + //print_hex_dump(KERN_ERR, "nvcom ", 0, 16, 1,boot_data->iram_img_data+boot_data->nv_head_addr, boot_data->nv_data_size+8, 1); + } + return 0; +} +//debug cp boot for setvalue +int gbl_count_flag=0; +/**************************************************************** + *Description:download the wifi bt service img, + *Func: + *Calls: + *Call By: + *Input:the service index + *Output:download data + *Return:0:pass other fail + *Others: + *Author:JUNWEI.JIANG + *Date:2024-02-07 + * **************************************************************/ +static int skw_dloader_module(int service_index) +{ + int i =0; + int ret = 0; + struct img_head_data_t dl_data_info; + unsigned int *data=NULL; + int tmp_count=0; + int total_count=0; +#if SKW_CPBOOT_DEBUG//for wifionly debug cp boot sleep no dl img setvalue 1; + skwboot_log("%s:the debug skw_dloader Enter \n",__func__); + return 0; +#endif + //dl count flag setvalue + if(service_index==SKW_WIFI){ + tmp_count = boot_data->wifi_dl_count; + }else if(service_index == SKW_BT){ + tmp_count = boot_data->bt_dl_count; + }else if(service_index == SKW_ALL){ + tmp_count = boot_data->all_dl_count; + }else{ + skwboot_warn("%s No service index ops!!!\n",__func__); + } +#if 1//debug cp boot for setvalue 1 + if(gbl_count_flag==0){ + boot_data->dl_done_signal=0; + gbl_count_flag=1; + } +#endif + skwboot_log("the dl_count ---dl_bin_counts ===%d\n",tmp_count); + boot_data->service_ops = SKW_NO_SERVICE; + if(boot_data->iram_img_data!=NULL){ + for(i=0; i*MODULE_INFO_LEN<=(boot_data->tail_addr-boot_data->head_addr); i++) + { + data = (unsigned int *)(boot_data->iram_img_data +boot_data->head_addr+i*MODULE_INFO_LEN); + dl_data_info.dl_addr=data[0]; + dl_data_info.write_addr =data[2]; + dl_data_info.index = 0x000000FF&EndianConv_32(data[1]); + dl_data_info.data_size = 0x00FFFFFF&data[1]; + skwboot_log("%s line:%d dl_addr=0x%x, write_addr=0x%x, index=0x%x,data_size=0x%x\n", __func__, + __LINE__, dl_data_info.dl_addr,dl_data_info.write_addr,dl_data_info.index,dl_data_info.data_size); + if(service_index ==dl_data_info.index || SKW_BSP== dl_data_info.index || service_index == SKW_ALL){ + total_count +=1; + boot_data->dl_base_addr = dl_data_info.write_addr; + boot_data->dl_size = dl_data_info.data_size; + if(dl_data_info.dl_addr >= boot_data->dram_dl_addr){ + //iram data dload img + boot_data->dl_offset_addr =dl_data_info.dl_addr- boot_data->dram_dl_addr; + boot_data->dl_base_img = boot_data->dram_img_data; + }else { + //dram data dload img + boot_data->dl_offset_addr =dl_data_info.dl_addr- boot_data->iram_dl_addr; + boot_data->dl_base_img = boot_data->iram_img_data; + } + if(tmp_count == total_count){ + skw_download_signal_ops(); + } + ret=skw_boot_loader(boot_data); + if(ret){ + skwboot_err("%s the load module=%d, fail,ret=%d \n", __func__, dl_data_info.index, ret); + break; + } + }else{ + skwboot_err("%s not need to load module_index=%d,ret=%d \n", __func__, dl_data_info.index, ret); + } + } + + } + return ret; +} + +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +int skw_start_wifi_service(void) +{ + int ret =0; + + skwboot_log("%s Enter cp_state =%d \n",__func__, cp_exception_sts); + mutex_lock(&boot_mutex); + boot_data->service_ops = SKW_WIFI_START; +#if defined(SKW_BOOT_MEMPOWERON) + boot_data->dl_module = NONE_BOOT; + boot_data->first_boot_flag =1; + boot_data->dl_base_img = NULL; +#else + boot_data->first_dl_flag = 1; + boot_data->dl_module = SKW_WIFI_BOOT; + //download done flag ++ + skw_download_signal_ops(); +#endif + ret = skw_boot_loader(boot_data); + mutex_unlock(&boot_mutex); + if(ret !=0) + { + skwboot_err("%s,line:%d boot fail \n", __func__,__LINE__); + return -1; + } + + skwboot_log("%s wifi boot sucessfull\n", __func__); + return 0; +} +EXPORT_SYMBOL_GPL(skw_start_wifi_service); +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +int skw_stop_wifi_service(void) +{ + int ret =0; + skwboot_log("%s Enter cp_state =%d \n",__func__, cp_exception_sts); + mutex_lock(&boot_mutex); + boot_data->service_ops = SKW_WIFI_STOP; +#if defined(SKW_BOOT_MEMPOWERON) + boot_data->dl_module = NONE_BOOT; + boot_data->first_boot_flag =1; + boot_data->dl_base_img = NULL; +#else + boot_data->dl_module = 0; + boot_data->first_dl_flag = 1; +#endif + //download done flag ++ + //gpio need set high or low power interrupt to cp wakeup + boot_data->gpio_out = boot_data->chip_gpio; + if(boot_data->gpio_val) + boot_data->gpio_val =0; + else + boot_data->gpio_val =1; + ret = skw_boot_loader(boot_data); + mutex_unlock(&boot_mutex); + if(ret !=0) + { + skwboot_warn("dload the img fail \n"); + return -1; + } + skwboot_log("seekwave boot stop done:%s\n",__func__); + return 0; +} +EXPORT_SYMBOL_GPL(skw_stop_wifi_service); +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static int skw_start_bt_service(void) +{ + int ret=0; + skwboot_log("%s Enter cp_state =%d \n",__func__, cp_exception_sts); + mutex_lock(&boot_mutex); + boot_data->service_ops = SKW_BT_START; +#if defined(SKW_BOOT_MEMPOWERON) + boot_data->first_boot_flag =1; + boot_data->dl_base_img = NULL; + boot_data->dl_module = NONE_BOOT; +#else + boot_data->first_dl_flag = 1; + boot_data->dl_module = SKW_BT_BOOT; + //download done flag ++ + skw_download_signal_ops(); +#endif + ret = skw_boot_loader(boot_data); + mutex_unlock(&boot_mutex); + if(ret !=0) + { + skwboot_err("%s boot fail \n", __func__); + return -1; + } + + skwboot_log("%s line:%d , boot bt sucessfully!\n", __func__,__LINE__); + return 0; +} + +/*************************************************************************** + *Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + **************************************************************************/ +static int skw_stop_bt_service(void) +{ + int ret =0; + skwboot_log("%s Enter cp_state =%d \n",__func__, cp_exception_sts); + mutex_lock(&boot_mutex); + boot_data->service_ops = SKW_BT_STOP; +#if defined(SKW_BOOT_MEMPOWERON) + boot_data->first_boot_flag = 1; + boot_data->dl_base_img = NULL; +#else + boot_data->dl_module = 0; + boot_data->first_dl_flag = 1; +#endif + //download done flag ++ + boot_data->dl_module = NONE_BOOT; + //gpio need set high or low power interrupt to cp wakeup + boot_data->gpio_out = boot_data->chip_gpio; + if(boot_data->gpio_val) + boot_data->gpio_val =0; + else + boot_data->gpio_val =1; + ret = skw_boot_loader(boot_data); + mutex_unlock(&boot_mutex); + if(ret !=0) + { + skwboot_warn("dload the img fail \n"); + return -1; + } + skwboot_log("seekwave boot stop done:%s\n",__func__); + return 0; +} + +/**************************************************************** + *Description:double iram dram img first boot cp + *Func: + *Calls: + *Call By:skw_first_boot + *Input:the file path + *Output:download data and the data size dl_data image_size + *Return:0:pass other fail + *Others: + *Author:JUNWEI.JIANG + *Date:2022-02-07 + * **************************************************************/ +static int skw_first_boot(struct seekwave_device *boot_data) +{ + int ret =0; + //get the img data +#ifdef DEBUG_SKWBOOT_TIME + ktime_t cur_time,last_time; + cur_time = ktime_get(); +#endif + //set download the value; + boot_data->service_ops = SKW_NO_SERVICE; + boot_data->save_setup_addr = SKW_SDIO_PD_DL_AP2CP_BSP; //160 + boot_data->gpio_out = boot_data->chip_gpio; + boot_data->gpio_val = 0; + boot_data->dl_module = 0; + boot_data->first_dl_flag =0; + boot_data->gpio_in = boot_data->host_gpio; + boot_data->dma_type_addr = SKW_SDIO_PLD_DMA_TYPE; + boot_data->slp_disable_addr = SKW_SDIO_CP_SLP_SWITCH; + boot_data->wifi_start = skw_start_wifi_service; + boot_data->wifi_stop = skw_stop_wifi_service; + boot_data->bt_start = skw_start_bt_service; + boot_data->bt_stop = skw_stop_bt_service; + boot_data->skw_dloader_module = skw_dloader_module; + ret = skw_boot_loader(boot_data); + if(ret < 0){ + skwboot_err("%s firt boot cp fail \n", __func__); + return -1; + } + //download done set the download flag; + boot_data->first_dl_flag =1; + boot_data->first_boot_flag =1; + + //download done tall cp acount; + boot_data->dl_done_signal &= 0xFF; + boot_data->dl_done_signal +=1; + skwboot_log("%s first boot pass\n", __func__); +#ifdef DEBUG_SKWBOOT_TIME + last_time = ktime_get(); + skwboot_log("%s,the download time start time %llu and the over time %llu \n", + __func__, cur_time, last_time); +#endif + return ret; +} +int seekwave_boot_init(char *chip_id) +{ + int ret; + + skwboot_log("%s: enter chip_id=%s\n",__func__,chip_id); + if(chip_id != NULL && strlen(chip_id)!=0){ + local_chip_id = chip_id; + } + btboot_pdev = NULL; + skw_ucom_init(); + ret = platform_driver_register(&seekwave_driver); + if (seekwave_device.name) + platform_device_register(&seekwave_device); + return ret; +} +void seekwave_boot_exit(void) +{ + skw_ucom_exit(); + + if (seekwave_device.name) + platform_device_unregister(&seekwave_device); + platform_driver_unregister(&seekwave_driver); +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_boot.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_boot.h new file mode 100755 index 0000000..92a97ec --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_boot.h @@ -0,0 +1,297 @@ +/***************************************************************** + *Copyright (C) 2021 Seekwave Tech Inc. + *Filename : skw_sdio.h + *Authors:seekwave platform + * + * This software is licensed under the terms of the the GNU + * General Public License version 2, as published by the Free + * Software Foundation, and may be copied, distributed, and + * modified under those terms. + * + * This program is distributed in the hope that it will be usefull, + * but without any warranty;without even the implied warranty of + * merchantability or fitness for a partcular purpose. See the + * GUN General Public License for more details. + * **************************************************************/ +#ifndef __SKW_BOOT_H__ +#define __SKW_BOOT_H__ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/workqueue.h> +#if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE +#include <uapi/linux/sched/types.h> +#else +#include <linux/sched.h> +#endif + +#ifdef SKW_EXT_INC +#include "skw_platform_data.h" +#else +#include <linux/platform_data/skw_platform_data.h> +#endif + +#ifdef CONFIG_WAKELOCK +#include <linux/wakelock.h> +#else +#include <linux/pm_wakeup.h> +#endif +#include "boot_config.h" +#if KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE +#define skw_read_file kernel_read +#define skw_write_file kernel_write +#else +#define skw_read_file vfs_read +#define skw_write_file vfs_write +#endif + +#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE +#define skw_wakeup_source_register(x, y) wakeup_source_register(x,y) +#else +#define skw_wakeup_source_register(x, y) wakeup_source_register(y) +#endif + +#if KERNEL_VERSION(4, 4, 0) <= LINUX_VERSION_CODE +#define skw_reinit_completion(x) reinit_completion(&x) +#define SKW_MIN_NICE MIN_NICE +#else +#define skw_reinit_completion(x) INIT_COMPLETION(x) +#define SKW_MIN_NICE -20 +#endif +#ifndef PLATFORM_DEVID_AUTO +#define PLATFORM_DEVID_AUTO -1 +#endif + +#ifdef CONFIG_NO_GKI +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +#else +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +#endif + +/**************************************************************** + *Description:the skwsdio log define and the skwsdio data debug, + *Func: skwsdio_log, skwsdio_err, skwsdio_data_pr; + *Calls: + *Call By: + *Input: skwsdio log debug informations + *Output: + *Return: + *Others: + *Author:JUNWEI.JIANG + *Date:2021-08-25 + * **************************************************************/ +#define skwboot_log(fmt, args...) \ + pr_info("[SKWBOOT]:" fmt, ## args) + +#define skwboot_err(fmt, args...) \ + pr_err("[SKWBOOT_ERR]:" fmt, ## args) + +#define skwboot_warn(fmt, args...) \ + pr_warn("[SKWBOOT_WARN]:" fmt, ## args) + +#define skwboot_data_pr(level, prefix_str, prefix_type, rowsize,\ + groupsize, buf, len, asscii)\ + do{if(loglevel) \ + print_hex_dump(level, prefix_str, prefix_type, rowsize,\ + groupsize, buf, len, asscii);\ + }while(0) + +/**********************sdio boot interface start******************/ + +#define SKW_BOOT_START_ADDR 0x100000 +#define SKW_CHIP_ID 0x40000000 //SV6160 chip id +/*add the 32bit*4 128bit */ +struct img_head_data_t +{ + unsigned int index; + unsigned int dl_addr; + unsigned int data_size; + unsigned int write_addr; +}; +/*add the 32bit*4 128bit */ +struct img_dl_data +{ + unsigned int dl_addr; + unsigned int dl_info; /*type and the size*/ + unsigned int write_addr; +}; +#define CRC_16_L_SEED 0x80 +#define CRC_16_L_POLYNOMIAL 0x8000 +#define CRC_16_POLYNOMIAL 0x1021 +#define IRAM_CRC_OFFSET 0 +#define DRAM_CRC_OFFSET 0 + +#define SKW_FIRST_BOOT 0 +#define SKW_BSP_BOOT 1 +#define SKW_WIFI_BOOT 2 +#define SKW_BT_BOOT 3 +#define RECOVERY_BOOT 4 +#define NONE_BOOT 5 +/*slp reg add the ap send the irq to cp reg*/ +#define SKW_SDIO_PD_DL_AP2CP_BSP 0x160 //download done or first boot setup addrn +#define SKW_SDIO_AP2CP_EXTI_SETVAL 0x161 //External Interrupt set val 1 or 2 ;3 is fail +#define SDIOHAL_PD_DL_AP2CP_BT 0x162 +#define SDIOHAL_PD_DL_ALL 0x163 +#define SKW_SDIO_DL_POWERON_MODULE 0x164 //Poweron CP Moudle 1 WIFI 2:BT +#define SKW_SDIO_PLD_DMA_TYPE 0x165 +#define SDIOHAL_CPLOG_TO_AP_SWITCH 0x166 +#define SKW_SDIO_CP_SLP_SWITCH 0x167 //Turn on/off the CP slp feature 1:dis slp 0:enb slp +#define SKW_SDIO_CREDIT_TO_CP 0x168 + +// CP signal 3 +#define SKW_SDIO_RX_CHANNEL_FTL0 0x16C +#define SKW_SDIO_RX_CHANNEL_FTL1 0x16D + +/*slp reg get the cp dl state reg*/ +#define SKW_SDIO_DL_CP2AP_BSP 0x180 //poweron OK ? 1: WIFI 2:BT +#define SKW_SDIO_CP2AP_FIFO_IND 0x181 //CP_RX FIFO Empty Indiacation. +#define SKW_SDIO_CP2AP_EXTI_GETVAL 0x182 //sdio External Interrupt get val 1 or 2 ;3 is fail +#define SDIOHAL_PD_DL_CP2AP_ALL 0x183 +#define SDIOHAL_PD_DL_CP2AP_SIG4 0x184 +#define SDIOHAL_PD_DL_CP2AP_SIG5 0x185 +#define SDIOHAL_PD_DL_CP2AP_SIG6 0x186 +#define SDIOHAL_PD_DL_CP2AP_SIG7 0x187 + +#define SKWSDIO_AP2CP_IRQ 0x1b0 //AP to CP interrupt and used BIT4 set 1 :fifth bit + +#define NV_CMFG_OFFSET 0x8 +#define NV_CMFG_SIZE 0xC +#define NV_PNFG_OFFSET 0x10 +#define NV_PNFG_SIZE 0x14 +#define NV_HEADER_SIZE 0x20 + +#define PN_CNT 20 +#define PN_FUNCSEL_ONEGRP_CNT 10 +#define PN_FUNC_SEL0_OFFSET 0x5c +#define PN_FUNC_SEL1_OFFSET 0x60 +#define SKW_PINREG_BASE 0x40102000 +#define SKW_DL_FLAG_BASE 0x40100030 +#define SKW_DL_FLAG_BIT_MASK BIT(8) + +#define BIT_PN_DSLP_EN_START 17 +#define BIT_PN_DSLP_EN_END 21 +#define BIT_DRV_STREN_START 14 +#define BIT_DRV_STREN_END 16 +#define BIT_NORMAL_WP_START 8 +#define BIT_NORMAL_WP_END 10 +#define BIT_SCHMITT_START 11 +#define BIT_SCHMITT_END 11 +#define BIT_SLP_WP_START 2 +#define BIT_SLP_WP_END 4 +#define BIT_SLEEP_IE_OE_START 0 +#define BIT_SLEEP_IE_OE_END 1 + +enum dma_type_en{ + ADMA=1, + SDMA, +}; +enum skw_service_ops { + SKW_NO_SERVICE =0, + SKW_WIFI_START, + SKW_WIFI_STOP, + SKW_BT_START, + SKW_BT_STOP, +}; + +enum skw_subsys{ + SKW_BOOT=0, + SKW_BSP, + SKW_WIFI, + SKW_BT, + SKW_ALL, +}; + +struct seekwave_device { + struct platform_device *pdev; + char *skw_nv_name; + char *iram_file_path; + char *dram_file_path; + char *iram_img_data; + char *dram_img_data; + char *dl_base_img;// + char *nv_mem_data; + char *nv_mem_cmfg_data; + char *nv_mem_pnfg_data; + int (*wifi_start)(void); + int (*bt_start)(void); + int (*wifi_stop)(void); + int (*bt_stop)(void); + int (*skw_dloader_module)(int service_index); + u32 nv_mem_cmfg_size; + u32 nv_mem_pnfg_size; + unsigned short iram_crc_val; + unsigned short dram_crc_val; + unsigned short nvmem_crc_val; + unsigned int iram_dl_addr; + unsigned int iram_dl_size; + unsigned int iram_crc_offset; + unsigned int iram_crc_en; + unsigned int dram_dl_addr; + unsigned int dram_dl_size; + unsigned int dram_crc_offset; + unsigned int dram_crc_en; + unsigned int setup_addr;//setup address + unsigned int save_setup_addr;//send the setup address register + unsigned int first_dl_flag; + unsigned int first_boot_flag; + unsigned int dl_module; + unsigned int dma_type_addr;//1:ADMA,2:SDMA + unsigned int dma_type;//1:ADMA,2:SDMA + unsigned int slp_disable;//0:disable,1:enable + unsigned int slp_disable_addr; + unsigned int head_addr; + unsigned int tail_addr; + unsigned int bsp_head_addr; + unsigned int bsp_tail_addr; + unsigned int wifi_head_addr; + unsigned int wifi_tail_addr; + unsigned int bt_head_addr; + unsigned int bt_tail_addr; + unsigned int nv_mem_addr; + unsigned int nv_mem_size; + unsigned int nv_head_addr; + unsigned int nv_tail_addr; + unsigned int nv_data_size; + unsigned int nvmem_crc_en; + unsigned int nvmem_crc_offset; + unsigned int chip_id; + unsigned int fpga_debug; + unsigned int bt_antenna; + unsigned int dl_offset_addr; + unsigned int dl_base_addr; + unsigned int dl_addr;// + unsigned int dl_acount_addr; + unsigned int dl_size; + int img_size; + int bsp_index_count; + int bt_index_count; + int wifi_index_count; + int all_dl_count; + int bt_dl_count; + int wifi_dl_count; + int host_gpio;/*GPIO0_A3*/ + int chip_gpio;/*GPIO2_D2*/ + int chip_en;/*GPIO0_B1*/ + int bt_service_state; + int wifi_service_state; + int service_ops; + int dl_done_signal; + int gpio_out;//host wakeup gpio0:/*GPIO0_A3*/,chip_wakeup gpio2:/*GPIO2_D2*/ + int gpio_in;//host wakeup gpio 0 + int gpio_val; + int gpio_next_val; + +}; +void seekwave_boot_exit(void); +int seekwave_boot_init(char *chip_id); +int skw_ucom_init(void); +void skw_ucom_exit(void); +int skw_bind_boot_driver(struct device *dev); +#endif diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_dump_mem.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_dump_mem.c new file mode 100755 index 0000000..c3dcf25 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_dump_mem.c @@ -0,0 +1,284 @@ + +#include "sv6621s_mem_map.h" +#include "skw_log_to_file.h" + +struct memory_segment { + const char *name; + uint32_t address; + uint32_t size; +}; + +struct memory_segment cp_mem_seg[] = { + { "CODE", CODE_MEM_BASE_ADDR, CODE_MEM_SIZE}, + { "DATA", DATA_MEM_BASE_ADDR, DATA_MEM_SIZE}, + { "AHBR", AHB_REG_BASE_ADDR, AHB_REG_SIZE}, + { "PPBM", UMEM_MEM_BASE_ADDR, UMEM_MEM_SIZE}, + { "SMEM", SMEM_MEM_BASE_ADDR, SMEM_MEM_SIZE}, + { "WFRF", 0x40140000, 0x4000}, + { "CSCB", 0xE000E000, 0x1000}, + { "WREG", WREG_MEM_BASE_ADDR, WREG_MEM_SIZE}, + { "PHYR", PHYR_MEM_BASE_ADDR, PHYR_MEM_SIZE}, + { "SDIO", SDIO_MEM_BASE_ADDR, SDIO_MEM_SIZE}, + { "BTRG", BTDM_MEM_BASE_ADDR, BTDM_MEM_SIZE}, + { "BTEM", BTEM_MEM_BASE_ADDR, BTEM_MEM_SIZE}, + { "BTGB", BTGB_MEM_BASE_ADDR, BTGB_MEM_SIZE}, + { "BTRF", BTRF_MEM_BASE_ADDR, BTRF_MEM_SIZE}, + { "RFTOP", RFTOP_MEM_BASE_ADDR, RFTOP_MEM_SIZE}, + { "RCLK", RCLK_MEM_BASE_ADDR, RCLK_MEM_SIZE}, + { "BBPLL", BBPLL_MEM_BASE_ADDR, BBPLL_MEM_SIZE} + +}; +static uint32_t skw_checksum(void *data, int data_len) +{ + uint32_t *d32 = data; + uint32_t checksum=0; + int i; + + data_len = data_len >> 2; + for (i=0; i<data_len; i++) + checksum += d32[i]; + return checksum; +} +int skw_dump_memory_into_buffer(struct ucom_dev *ucom, char *buffer, int length) +{ + struct memory_segment *mem_sg = &cp_mem_seg[0]; + int offset=0; + uint16_t seq, packet_len; + uint32_t sg_size; + char *read_buf; + uint8_t sg_count; + int ret = 0; + + if (!ucom || !ucom->pdata || + !ucom->pdata->skw_dump_mem) + return 0; + sg_count = sizeof(cp_mem_seg)/sizeof(cp_mem_seg[0]); + if (sg_count==0) + return 0; + packet_len = 0x800; + read_buf = kmalloc(packet_len, GFP_KERNEL); + if (read_buf==NULL) + return 0; + buffer[offset] = sg_count; //save total segment count + offset++; + + do { + uint32_t source_addr; + sg_size = mem_sg->size; + + memcpy(&buffer[offset], mem_sg->name, 5); //save segment name + offset += 5; + memcpy(&buffer[offset], &mem_sg->address, 4); //save segment base addrss + offset += 4; + memcpy(&buffer[offset], &mem_sg->size, 4); //save segment size + offset += 4; + memcpy(&buffer[offset], &packet_len, 2); //save segment size + offset += 2; + skwlog_log("%s %s:%d 0x%x 0x%x\n", __func__, mem_sg->name, + offset, mem_sg->address, mem_sg->size); + seq = 0; + source_addr = mem_sg->address; + do { + int read_len; + uint32_t sum; + + memcpy(&buffer[offset], &seq, 2); //save segment size + seq++; + offset += 2; + + if (sg_size > packet_len) + read_len = packet_len; + else + read_len = sg_size; + ret = ucom->pdata->skw_dump_mem(source_addr,(void *)read_buf,read_len); + if (ret != 0) { + skwlog_err("%s dump memory fail :%d \n", __func__, ret); + break; + } + source_addr += read_len; + memcpy(buffer+offset, read_buf, read_len); //save packet payload + offset += read_len; + + sum = skw_checksum(read_buf, read_len); + memcpy (buffer+offset, &sum, 4); //save checksum + offset += 4; + + sg_size -= read_len; + } while (sg_size); + mem_sg++; + sg_count--; + } while (sg_count && (!ret)); + kfree(read_buf); + return offset; +} + +static int skw_ucom_dump_from_buffer(char __user *buf, size_t count, loff_t *pos) +{ + int len; + int ret; + + len = 0; + ret = 0; + if (dump_log_size) { + if (*pos + count < dump_log_size) + len = count; + else if (*pos < dump_log_size) + len = dump_log_size - *pos; + if (len) + ret = copy_to_user(buf, &dump_memory_buffer[*pos], len); + if (ret ==0) + ret = len; + } else if (*pos == 0){ + char assert_info[32]={0}; + sprintf(assert_info,"modem_status=%d\n", cp_exception_sts); + ret = copy_to_user(buf, assert_info, strlen(assert_info)); + len = strlen(assert_info); + } + *pos = *pos+len; + if (len==0) + skwboot_log("dump_log_size: %d offset %d count %d %p ret=%d\n", dump_log_size, (int)*pos, (int)count, dump_memory_buffer, ret); + return ret; +} + +static int user_dump_open(struct inode *ip, struct file *fp) +{ + if (dump_buffer_size) + return 0; + return 0; +} + +static ssize_t user_dump_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) +{ + ssize_t ret = skw_ucom_dump_from_buffer(buf, count, pos); + if (ret == 0) + dump_log_size = 0; + return ret; + +} +static int user_dump_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static ssize_t user_dump_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos) +{ + return 0; +} +static long user_dump_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + return 0; +} +static const struct file_operations skw_dump_ops = { + .owner = THIS_MODULE, + .open = user_dump_open, + .read = user_dump_read, + .write = user_dump_write, + .unlocked_ioctl = user_dump_ioctl, + .release= user_dump_release, +}; +static int bt_state_event_notifier(struct notifier_block *nb, unsigned long action, void *data) +{ + struct ucom_dev *ucom = container_of(nb, struct ucom_dev, notifier); + //int status = cp_exception_sts; + switch(action) + { + case DEVICE_ASSERT_EVENT: + { + skwboot_log("BT BSPASSERT EVENT received!!!!\n"); + cp_exception_sts = 1; + ucom = ucoms[log_portno]; + if (dump_log_size){ + skwboot_log(" dump done ,mem size: %d\n", dump_log_size); + return NOTIFY_OK; + } + if (!dump_memory_buffer && !atomic_read(&ucom->open) && SKW_DUMP_BUFFER_SIZE!=0) + dump_memory_buffer = kzalloc(SKW_DUMP_BUFFER_SIZE, GFP_KERNEL); + if(ucom->pdata && ucom->pdata->dump_modem_memory) { + dump_log_size = 0; + if (dump_memory_buffer) { + dump_buffer_size = SKW_DUMP_BUFFER_SIZE; + ucom->pdata->dump_modem_memory(dump_memory_buffer, + dump_buffer_size, &dump_log_size); + } + } else if (dump_memory_buffer) { + dump_buffer_size = SKW_DUMP_BUFFER_SIZE; + if (!atomic_read(&ucom->open)) + dump_log_size = skw_dump_memory_into_buffer(ucom, dump_memory_buffer,dump_buffer_size); + } + + } + break; + case DEVICE_BSPREADY_EVENT: + { + cp_exception_sts = 0; + skwboot_log("BT BSPREADY EVENT Comming in !!!!\n"); + } + break; + case DEVICE_DUMPDONE_EVENT: + { + cp_exception_sts = 2; + skwboot_log("BT DUMPDONE EVENT Comming in !!!!\n"); + } + break; + case DEVICE_BLOCKED_EVENT: + { + cp_exception_sts = 3; + skwboot_log("BT BLOCKED EVENT Comming in !!!!\n"); + if (dump_log_size){ + skwboot_log(" dump done ,mem size: %d\n", dump_log_size); + return NOTIFY_OK; + } + if (!dump_memory_buffer) + dump_memory_buffer = kzalloc(SKW_DUMP_BUFFER_SIZE, GFP_KERNEL); + if (dump_memory_buffer) { + dump_buffer_size = SKW_DUMP_BUFFER_SIZE; + dump_log_size = skw_dump_memory_into_buffer(ucom, dump_memory_buffer,dump_buffer_size); + } + } + break; + case DEVICE_DISCONNECT_EVENT: + { + cp_exception_sts = action; + } + break; + default: + break; + + } + return NOTIFY_OK; +} + +static int skw_bt_state_event_init(struct ucom_dev *ucom) +{ + int ret = 0; + int devno; + if (ucom->pdata->modem_register_notify && ucom->notifier.notifier_call == NULL) { + ucom->notifier.notifier_call = bt_state_event_notifier; + ucom->pdata->modem_register_notify(&ucom->notifier); + } + ret =__register_chrdev(skw_major, UCOM_PORTNO_MAX+1, 1, + "SKWDUMP", &skw_dump_ops); + + devno = MKDEV(skw_major, UCOM_PORTNO_MAX+1); + if (ret==0) + device_create(skw_com_class, NULL, devno, NULL, "%s", "SKWDUMP"); + skwlog_log("%s enter ret=%d\n",__func__, ret); + return ret; +} + +static int skw_bt_state_event_deinit(struct ucom_dev *ucom) +{ + int ret = 0; + int devno; + if(ucom) { + if((ucom->notifier.notifier_call)){ + skwboot_log("%s :%d release the notifier \n", __func__,__LINE__); + ucom->notifier.notifier_call = NULL; + ucom->pdata->modem_unregister_notify(&ucom->notifier); + } + devno = MKDEV(skw_major, UCOM_PORTNO_MAX+1); + __unregister_chrdev(skw_major, MINOR(devno), 1, "SKWDUMP"); + device_destroy(skw_com_class, devno); + } + return ret; +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_log_to_file.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_log_to_file.c new file mode 100755 index 0000000..c8067da --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_log_to_file.c @@ -0,0 +1,767 @@ +/***************************************************************** + *Copyright (C) 2021 Seekwave Tech Inc. + *Filename : skw_log_process.c + *Authors:seekwave platform + * + * This software is licensed under the terms of the the GNU + * General Public License version 2, as published by the Free + * Software Foundation, and may be copied, distributed, and + * modified under those terms. + * + * This program is distributed in the hope that it will be usefull, + * but without any warranty;without even the implied warranty of + * merchantability or fitness for a partcular purpose. See the + * GUN General Public License for more details. + * **************************************************************/ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> +#include <linux/scatterlist.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/err.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/file.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include "skw_boot.h" + +#include "skw_log_to_file.h" +#include "skw_mem_map.h" +extern int cp_exception_sts; +static char *log_path = "/data"; +module_param(log_path, charp, 0644); + +int skw_log_num = 2; +#ifdef MIN_LOG_SIZE +int skw_log_size = (1*1024*1024); +#else +int skw_log_size = (100*1024*1024); +#endif +#define SKW_LOG_READ_BUFFER_SIZE (8*1024) + +#ifndef CONFIG_NO_GKI +#define CONFIG_NO_GKI +#endif + +module_param(skw_log_size, int, 0644); +module_param(skw_log_num, int, 0644); + +struct skw_log_data { + spinlock_t lock; + + int state; + + /* synchronize access to our device file */ + atomic_t open_excl; + /* to enforce only one ioctl at a time */ + atomic_t ioctl_excl; + + + int rx_done; + /* for processing MTP_SEND_FILE, MTP_RECEIVE_FILE and + * MTP_SEND_FILE_WITH_HEADER ioctls on a work queue + */ + struct workqueue_struct *wq; + struct work_struct log_to_file_work; + struct file *xfer_file; + loff_t xfer_file_offset; + int64_t xfer_file_length; + unsigned xfer_send_header; + uint16_t xfer_command; + uint32_t xfer_transaction_id; + int xfer_result; +}; + +struct log_com_dev { + atomic_t open; + spinlock_t lock; + int rx_busy; + int tx_busy; + int devno; + int portno; + struct sv6160_platform_data *pdata; + wait_queue_head_t wq; + struct cdev cdev; + char *rx_buf; + char *tx_buf; + struct notifier_block notifier; +}; +struct skw_log_read_buffer { + int lenth; + char *buffer; +}; + + +void skw_modem_log_to_file_work(struct work_struct *data); +extern int skw_cp_exception_reboot(void); + +int skw_modem_save_dumpmem(void); +static uint32_t record_flag = 0; +static uint32_t cp_assert_status = 0; +struct file *log_fp = NULL; +struct log_com_dev *log_com = NULL; +struct sv6160_platform_data *port_data = NULL; +struct skw_log_data *skw_log_dev = NULL; +struct skw_log_read_buffer log_read_buffer; + +char *log0_file = "log000"; +char *log1_file = "log111"; +char *log_file; + +char* skw_code_mem = "code_mem_100000_7a000"; +char* skw_data_mem = "data_mem_20200000_40000"; +char* skw_cscb_mem = "cscb_mem_e000ed00_300"; +char* skw_wreg_mem = "wreg_mem_40820000_4000"; +char* skw_phyr_mem = "phyr_mem_40830000_4000"; +char* skw_smem_mem = "smem_mem_40a00000_58000"; +char* skw_umem_mem = "umem_mem_40b00000_c000"; +char* skw_modem_mem = "sdio_mem_401e0000_800"; +char* skw_btdm_mem = "btdm_mem_41000000_400"; +char* skw_btbt_mem = "btbt_mem_41000400_400"; +char* skw_btle_mem = "btle_mem_41000800_400"; +char* skw_btem_mem = "btem_mem_41022000_c000"; + +int modem_event_notifier(struct notifier_block *nb, unsigned long action, void *data) +{ + unsigned long flags; + skwboot_log("%s event = %d\n", __func__, (int)action); + switch(action) + { + case DEVICE_ASSERT_EVENT: + { + struct log_com_dev *ucom = log_com; + skwboot_log("the BSPASSERT EVENT Comming in !!!!\n"); + skw_modem_log_start_rec(); + spin_lock_irqsave(&ucom->lock, flags); + if(ucom->tx_busy) { + spin_unlock_irqrestore(&ucom->lock, flags); + skwboot_err("%s error 0\n", __func__); + return NOTIFY_OK; + } + ucom->tx_busy = 1; + spin_unlock_irqrestore(&ucom->lock, flags); + skw_modem_log_set_assert_status(1); + } + break; + case DEVICE_BSPREADY_EVENT: + { + skwboot_log("the BSPREADY EVENT Comming in !!!!\n"); + skw_modem_log_start_rec(); + } + break; + case DEVICE_DUMPDONE_EVENT: + { + skwboot_log("the DUMPDONE EVENT Comming in !!!!\n"); + skw_modem_log_stop_rec(); + skw_modem_log_set_assert_status(0); + } + break; + case DEVICE_BLOCKED_EVENT: + { + skwboot_log("the BLOCKED EVENT Comming in !!!!\n"); + //skw_modem_dumpmodem_start_rec(); + } + break; + case DEVICE_DUMPMEM_EVENT: + { + //skw_modem_dumpmodem_start_rec(); + } + break; + default: + { + + } + break; + + } + return NOTIFY_OK; +} + +void skw_modem_log_to_file_work(struct work_struct *data) +{ +#ifdef CONFIG_NO_GKI + //int log_len = 0; + struct file *fp = log_fp; + struct file *log_store_fp = NULL; + loff_t offset =0; + loff_t log_store_offset =0; + unsigned long flags; + uint32_t *rx_data; + size_t count = 0; + int ret = 0; + int i = 0; + int log_cnt = 0; + int sdma_rx_error_cnt = 0; + char *log_store; + int log_path_len; + char rd_buff[50]; + + if(record_flag) + return; + record_flag = 1; + + skwlog_log("log path = %s \n", log_path); + log_path_len = strlen(log_path); + + log_file = kzalloc(log_path_len + 16, GFP_KERNEL); + + if(log_file == NULL) + return; + log_store = kzalloc(log_path_len + 16, GFP_KERNEL); + if(log_store == NULL) { + kfree(log_file); + return; + } + + sprintf(log_store, "%s/log_store", log_path); + log_store_fp = filp_open(log_store, O_RDWR, 0777); + while(IS_ERR(log_store_fp)) + { + skwlog_err("open log_store %s failed:%d \n", log_store, (int)PTR_ERR(log_store_fp)); + msleep(1000); + i++; + if(i > 8) { + kfree(log_store); + kfree(log_file); + return; + } + if(i > 5){ + skwlog_err("%s open log_store file failed, create file:%s\n",__func__, log_store); + log_store_fp = filp_open(log_store, O_CREAT | O_RDWR | O_TRUNC, 0777); + } + else{ + log_store_fp = filp_open(log_store, O_RDWR, 0777); + } + } + + ret = skw_read_file(log_store_fp, rd_buff, 30, &log_store_offset); + if(ret < 0){ + skwlog_err("%s Read file:%s failed, err:%d \n",__func__, log_store, ret); + } + + rd_buff[0] = rd_buff[0]+1; + if(rd_buff[0] > skw_log_num) { + rd_buff[0] = 0; + } + sprintf(log_file, "%s/%s", log_path, log0_file); + log_path_len = strlen(log_file); + *(log_file + log_path_len - 1) = ((rd_buff[0]%10) | 0x30); + *(log_file + log_path_len - 2) = (((rd_buff[0]/10)%10) | 0x30); + + log_store_offset = 0; + ret = skw_write_file(log_store_fp, rd_buff, 1, &log_store_offset); + if(ret < 0){ + skwlog_err("%s write file:%s failed, err:%d \n",__func__, log_store, ret); + } + ret = skw_write_file(log_store_fp, log_file, strlen(log_file), &log_store_offset); + if(ret < 0){ + skwlog_err("%s write f name to file:%s failed, err:%d \n",__func__, log_store, ret); + } + + log_fp = filp_open(log_file, O_CREAT | O_RDWR | O_TRUNC, 0777); + fp = log_fp; + + while(IS_ERR(fp)) + { + skwlog_err("open rec file %s failed :%d \n",log_file, (int)PTR_ERR(fp)); + msleep(500); + i++; + if(i > 10) { + kfree(log_store); + kfree(log_file); + return; + } + + log_fp = filp_open(log_file, O_CREAT | O_RDWR | O_TRUNC, 0777); + fp = log_fp; + } + atomic_inc(&log_com->open); + if (atomic_read(&log_com->open)==1) { + init_waitqueue_head(&log_com->wq); + spin_lock_init(&log_com->lock); + log_com->pdata->open_port(log_com->portno, NULL, NULL); + } + + log_read_buffer.lenth = 0; + log_read_buffer.buffer = kzalloc(SKW_LOG_READ_BUFFER_SIZE, GFP_KERNEL); + if(!log_read_buffer.buffer){ + kfree(log_store); + kfree(log_file); + return; + } + skwlog_log(" open %s for CP log record \n", log_file); + while(record_flag || cp_assert_status) + { + ret = 0; + // if(log_com){ +check_rx_busy: + spin_lock_irqsave(&log_com->lock, flags); + if(log_com->rx_busy) { + spin_unlock_irqrestore(&log_com->lock, flags); + mdelay(5); + goto check_rx_busy; + } + log_com->rx_busy = 1; + count = log_com->pdata->max_buffer_size; + spin_unlock_irqrestore(&log_com->lock, flags); + ret = log_com->pdata->hw_sdma_rx(log_com->portno, (log_read_buffer.buffer + log_read_buffer.lenth), count); + + if(ret > 0){ + log_cnt++; + sdma_rx_error_cnt = 0; + log_read_buffer.lenth = log_read_buffer.lenth + ret; + //skwlog_log("hw_sdma_rx:%s read len:%d buffer len:%d \n", skw_log, ret, log_read_buffer.lenth); + if(ret >= 0x1000) + skwlog_err("%s get too long data , err:%d \n",__func__, ret); + + if(log_cnt > 1000){ + skwlog_log("%s log_file:%s offset:%lld data:0x%x 0x%x 0x%x 0x%x 0x%x \n",__func__, log_file, offset, *(log_read_buffer.buffer), + *(log_read_buffer.buffer+1), *(log_read_buffer.buffer+2), *(log_read_buffer.buffer+3), *(log_read_buffer.buffer+4)); + log_cnt = 0; + } + } + else{ + skwlog_err("%s read log data err:%d \n",__func__, ret); + sdma_rx_error_cnt++; + if(sdma_rx_error_cnt > 5){ + skwlog_err("%s sdma_rx_error_cnt over:%d, stop log work \n",__func__, sdma_rx_error_cnt); + skw_modem_log_set_assert_status(0); + skw_modem_log_stop_rec(); + } + } + + if (port_data->bus_type == USB_LINK) { + if(ret < 0){ + skwlog_err("%s read log data err:%d, stop log work \n",__func__, ret); + skw_modem_log_set_assert_status(0); + skw_modem_log_stop_rec(); + } + } + else if (port_data->bus_type == SDIO_LINK) { + if(ret == -ENOTCONN){ + skw_modem_log_set_assert_status(0); + skw_modem_log_stop_rec(); + } + } + //skwlog_log("read log from SDIO len:%d ----- \n", log_read_buffer.lenth); + log_com->rx_busy = 0; + rx_data = (uint32_t *)log_read_buffer.buffer; + // } + + if(((log_read_buffer.lenth > 0) && cp_assert_status) + || ((SKW_LOG_READ_BUFFER_SIZE - log_read_buffer.lenth) <= (log_com->pdata->max_buffer_size))){ + //skwlog_log("skw_write_file:%s offset:%lld lenth:%d \n", skw_log, offset, log_read_buffer.lenth); + ret = skw_write_file(fp, log_read_buffer.buffer, log_read_buffer.lenth, &offset); + if(ret < 0){ + skwlog_err("%s write file failed, err:%d \n",__func__, ret); + } + + log_read_buffer.lenth = 0; + if(ret == -ENOSPC){ + skwlog_err("%s no space, stop CP log record \n",__func__); + skw_modem_log_stop_rec(); + } + + if(offset > skw_log_size && (!cp_assert_status)){ + if(!IS_ERR(fp)) + filp_close(fp, NULL); + + rd_buff[0] = rd_buff[0]+1; + if(rd_buff[0] > skw_log_num) { + rd_buff[0] = 0; + } + sprintf(log_file, "%s/%s", log_path, log0_file); + log_path_len = strlen(log_file); + *(log_file + log_path_len - 1) = ((rd_buff[0]%10) | 0x30); + *(log_file + log_path_len - 2) = (((rd_buff[0]/10)%10) | 0x30); + + log_store_offset = 0; + ret = skw_write_file(log_store_fp, rd_buff, 1, &log_store_offset); + if(ret < 0){ + skwlog_err("%s write file:%s failed, err:%d \n",__func__, log_store, ret); + } + ret = skw_write_file(log_store_fp, log_file, strlen(log_file), &log_store_offset); + if(ret < 0){ + skwlog_err("%s write f name to file:%s failed, err:%d \n",__func__, log_store, ret); + } + + log_fp = filp_open(log_file, O_CREAT | O_RDWR | O_TRUNC, 0777); + fp = log_fp; + if(IS_ERR(fp)){ + skwlog_err("%s switch record file to:%s failed: %d \n",__func__, log_file, (int)PTR_ERR(fp)); + kfree(log_file); + kfree(log_store); + kfree(log_read_buffer.buffer); + return; + } + else{ + skwlog_err("%s switch record file to:%s sucess \n",__func__, log_file); + } + + offset = 0; + } + } + } + atomic_dec(&log_com->open); + log_com->pdata->close_port(log_com->portno); + + if(!IS_ERR(fp)){ + filp_close(fp, NULL); + skwlog_log("%s close file %s before stop work.\n",__func__, log_file); + log_fp = NULL; + fp = log_fp; + } + + if(!IS_ERR(log_store_fp)){ + filp_close(log_store_fp, NULL); + skwlog_log("%s close file %s before stop work.\n",__func__, log_store); + log_store_fp = NULL; + } + kfree(log_file); + kfree(log_store); + kfree(log_read_buffer.buffer); + skwlog_log("%s work exit\n",__func__); +#endif + return; +} + +/*************************************************************************** + *Description:dump modem memory + *Seekwave tech LTD + *Author:JunWei Jiang + *Date:2022-11-14 + *Modify: + **************************************************************************/ +int skw_modem_save_mem(char *mem_path,unsigned int mem_len, unsigned int mem_addr) +{ + int ret =0; +#ifdef CONFIG_NO_GKI + char dump_mem_file[128]; + //dump code data + char *data_mem = NULL; + struct file *fp =NULL; + loff_t pos = 0; + int nwrite = 0; + unsigned int read_len=0; + unsigned int mem_size = mem_len; + + memset(dump_mem_file, 0, sizeof(dump_mem_file)); + sprintf(dump_mem_file,"%s/%s", log_path, mem_path); + data_mem = kzalloc(SKW_MAX_BUF_SIZE, GFP_KERNEL); + if(!data_mem){ + skwlog_log("the kzalloc dump buffer fail"); + return -2; + } + /* open file to write */ + fp = filp_open(dump_mem_file, O_CREAT | O_RDWR | O_TRUNC, 0777); + if (IS_ERR(fp)) { + skwlog_log("open file %s fail try again!!!\n", dump_mem_file); + fp = filp_open(mem_path, O_CREAT | O_RDWR | O_TRUNC, 0777); + if(IS_ERR(fp)){ + skwlog_log("open file error\n"); + ret = -1; + goto exit; + } + } + + while(mem_size) + { + if(mem_size<SKW_MAX_BUF_SIZE) + { + read_len = mem_size; + }else{ + read_len = SKW_MAX_BUF_SIZE; + } + //skwlog_log("the read_len =0x%x mem_size= 0x%x\n", read_len, mem_size); + ret = log_com->pdata->skw_dump_mem(mem_addr+(mem_len-mem_size),(void *)data_mem,read_len); + if(ret< 0) + break; + + //print_hex_dump(KERN_ERR, "img data ", 0, 16, 1,data_mem, 32, 1); + //pos=(unsigned long)offset; + /* Write buf to file */ + nwrite=skw_write_file(fp, data_mem,read_len, &pos); + //offset +=nwrite; + + if(mem_size>=SKW_MAX_BUF_SIZE) + { + mem_size=mem_size-SKW_MAX_BUF_SIZE; + }else{ + mem_size =0; + } + } + skwlog_log("Dump %s memory done !!\n", mem_path); + if(fp) + { + filp_close(fp,NULL); + skwlog_log("the file close!!!\n"); + } +exit: + if (fp) + filp_close(fp, NULL); + kfree(data_mem); +#endif + return ret; +} + +/*************************************************************************** + *Description:dump modem memory + *Seekwave tech LTD + *Author:JunWei Jiang + *Date:2022-11-14 + *Modify: + **************************************************************************/ +int skw_modem_save_dumpmem(void) +{ + int ret =0; + if (!log_com->pdata->skw_dump_mem) + return 0; + skwlog_log("%s [Enter]\n",__func__); + //DATA MEM + ret = skw_modem_save_mem(skw_data_mem,DATA_MEM_SIZE, DATA_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_data_mem, ret); + return -1; + } + //CODE MEM + ret = skw_modem_save_mem(skw_code_mem,CODE_MEM_SIZE, CODE_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_code_mem,ret); + return -1; + } + //CSCB MEM + ret = skw_modem_save_mem(skw_cscb_mem,CSCB_MEM_SIZE, CSCB_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_cscb_mem,ret); + return -1; + } + + //UMEM MEM + ret = skw_modem_save_mem(skw_umem_mem,UMEM_MEM_SIZE, UMEM_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_umem_mem,ret); + return -1; + } + //SDIO MEM + ret = skw_modem_save_mem(skw_modem_mem,SDIO_MEM_SIZE, SDIO_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n",skw_modem_mem,ret); + return -1; + } + //BTDM MEM + ret = skw_modem_save_mem(skw_btdm_mem,BTDM_MEM_SIZE, BTDM_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_btdm_mem,ret); + return -1; + } + //BTBT MEM + ret = skw_modem_save_mem(skw_btbt_mem,BTBT_MEM_SIZE, BTBT_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_btbt_mem,ret); + return -1; + } + //BTLE MEM + ret = skw_modem_save_mem(skw_btle_mem,BTLE_MEM_SIZE, BTLE_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_btle_mem,ret); + return -1; + } + //BTEM MEM + ret = skw_modem_save_mem(skw_btem_mem,BTEM_MEM_SIZE, BTEM_MEM_BASE_ADDR); + if(ret !=0){ + skwlog_log("%s dump fail ret: %d\n",skw_btem_mem,ret); + return -1; + } + //WREG MEM + ret = skw_modem_save_mem(skw_wreg_mem,WREG_MEM_SIZE, WREG_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_wreg_mem,ret); + return -1; + } + //PHYR MEM + ret = skw_modem_save_mem(skw_phyr_mem,PHYR_MEM_SIZE, PHYR_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_phyr_mem,ret); + return -1; + } + //SMEM MEM + ret = skw_modem_save_mem(skw_smem_mem,SMEM_MEM_SIZE, SMEM_MEM_BASE_ADDR); + if(ret !=0) + { + skwlog_log("%s dump fail ret: %d\n", skw_smem_mem,ret); + return -1; + } + return ret; +} + +int skw_modem_log_init(struct sv6160_platform_data *p_data, struct file *fp, void *ucom) +{ + + int ret = 0; +#ifdef CONFIG_NO_GKI + if (skw_log_dev) + return ret; + if(skw_log_size > (200*1024*1024)) + skw_log_size = (200*1024*1024); + if(skw_log_size < (512*1024)) + skw_log_size = (512*1024); + if((skw_log_size*skw_log_num) > (1024*1024*1024)) + skw_log_num = (1024*1024*1024)/skw_log_size; + if(skw_log_num > 99) + skw_log_num = 99; + + skwlog_log("%s enter skw_log_num:%d skw_log_size:%d \n",__func__, skw_log_num, skw_log_size); + log_fp = fp; + log_com = ucom; + port_data = p_data; + + skw_log_dev = (struct skw_log_data *)kzalloc(sizeof(*skw_log_dev), GFP_KERNEL); + if (!skw_log_dev){ + ret = -ENOMEM; + skwlog_err("%s line:%d,can't malloc \n", __func__, __LINE__); + goto err1; + } + + spin_lock_init(&skw_log_dev->lock); + atomic_set(&skw_log_dev->open_excl, 0); + atomic_set(&skw_log_dev->ioctl_excl, 0); + //INIT_LIST_HEAD(&skw_log_dev->tx_idle); + + + skw_log_dev->wq = create_singlethread_workqueue("skw_log"); + if (!skw_log_dev->wq) { + ret = -ENOMEM; + goto err2; + } + INIT_WORK(&skw_log_dev->log_to_file_work, skw_modem_log_to_file_work); + + if (log_com->pdata->modem_register_notify) { + if(log_com->notifier.notifier_call == NULL){ + log_com->notifier.notifier_call = modem_event_notifier; + log_com->pdata->modem_register_notify(&log_com->notifier); + } + } + + skw_modem_log_start_rec(); + return 1; + +err2: + if (skw_log_dev->wq) { + destroy_workqueue(skw_log_dev->wq); + skw_log_dev->wq = NULL; + } + kfree(skw_log_dev); + skwlog_err("mtp gadget driver failed to initialize\n"); + +err1: +#else + skwlog_warn("GKI Not support log2file!\n"); +#endif + return ret; +} + +void skw_modem_log_set_assert_status(uint32_t cp_assert) +{ + cp_assert_status = cp_assert; + if(cp_assert_status){ + skwlog_log("%s CP in ASSERT, dump log in %s \n",__func__, log_file); + } +} + +void skw_modem_log_start_rec(void) +{ +#ifdef CONFIG_NO_GKI + skwlog_log("%s enter \n",__func__); + if(atomic_read(&log_com->open) > 1){ + skwlog_warn("%s:log port is busy\n",__func__); + return; + } + if(!skw_log_dev){ + skwlog_warn("%s no mem ready, can't start \n",__func__); + return; + } + if(record_flag){ + skwlog_warn("%s lof2file already \n",__func__); + return; + } + cp_assert_status = 0; + queue_work(skw_log_dev->wq, &skw_log_dev->log_to_file_work); +#else + skwlog_warn("GKI Not support log2file!\n"); +#endif + +} + +/*************************************************************************** + *Description:dump modem memory + *Seekwave tech LTD + *Author:JunWei Jiang + *Date:2022-11-14 + *Modify: + **************************************************************************/ +void skw_modem_dumpmodem_start_rec(void) +{ +#ifdef CONFIG_NO_GKI + skw_modem_save_dumpmem(); +#else + skwlog_warn("GKI Not support dump!\n"); +#endif + +} + +/*************************************************************************** + *Description:dump modem memory + *Seekwave tech LTD + *Author:JunWei Jiang + *Date:2022-11-14 + *Modify: + **************************************************************************/ + +void skw_modem_log_stop_rec(void) +{ + skwlog_log("%s enter \n",__func__); + if(record_flag) + record_flag = 0; + if(log_com && log_com->pdata && log_com->pdata->close_port) + log_com->pdata->close_port(log_com->portno); + return; +} + +void skw_modem_log_exit(void) +{ +#ifdef CONFIG_NO_GKI + skwlog_log("%s enter\n",__func__); + if(!log_com) return; + log_com->pdata->modem_unregister_notify(&log_com->notifier); + skw_modem_log_stop_rec(); + cancel_work_sync(&skw_log_dev->log_to_file_work); + destroy_workqueue(skw_log_dev->wq); + kfree(skw_log_dev); + skw_log_dev = NULL; + log_com = NULL; +#endif +} + +//DECLARE_USB_FUNCTION_INIT(mtp, mtp_alloc_inst, mtp_alloc); +MODULE_LICENSE("GPL"); diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_log_to_file.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_log_to_file.h new file mode 100755 index 0000000..bae2e64 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_log_to_file.h @@ -0,0 +1,51 @@ +/***************************************************************** + *Copyright (C) 2021 Seekwave Tech Inc. + *Filename : skw_sdio.h + *Authors:seekwave platform + * + * This software is licensed under the terms of the the GNU + * General Public License version 2, as published by the Free + * Software Foundation, and may be copied, distributed, and + * modified under those terms. + * + * This program is distributed in the hope that it will be usefull, + * but without any warranty;without even the implied warranty of + * merchantability or fitness for a partcular purpose. See the + * GUN General Public License for more details. + * **************************************************************/ +#ifndef __SKW_LOG_H__ +#define __SKW_LOG_H__ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> + +/**************************************************************** + *Description:the skwsdio log define and the skwsdio data debug, + *Func: skwsdio_log, skwsdio_err, skwsdio_data_pr; + *Calls: + *Call By: + *Input: skwsdio log debug informations + *Output: + *Return: + *Others: + *Author:JUNWEI.JIANG + *Date:2022-07-18 + * **************************************************************/ +#define skwlog_log(fmt, args...) \ + pr_info("[SKWLOG]:" fmt, ## args) + +#define skwlog_err(fmt, args...) \ + pr_err("[SKWLOG_ERR]:" fmt, ## args) + +#define skwlog_warn(fmt, args...) \ + pr_warn("[SKWLOG_WARN]:" fmt, ## args) + +int skw_modem_log_init(struct sv6160_platform_data *p_data, struct file *fp, void *ucom); +void skw_modem_log_set_assert_status(uint32_t cp_assert); +void skw_modem_dumpmodem_start_rec(void); +void skw_modem_log_start_rec(void); +void skw_modem_log_stop_rec(void); +void skw_modem_log_exit(void); +#endif diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_mem_map.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_mem_map.h new file mode 100755 index 0000000..e0e083a --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_mem_map.h @@ -0,0 +1,116 @@ +/* + * skw_mem_map.h + * Copyright (C) 2022 cfig <junwei.jiang@seekwavetech.com> + * + * Distributed under terms of the MIT license. + */ + +#ifndef SKW_MEM_MAP_H +#define SKW_MEM_MAP_H + +//#define SKW_MAX_BUF_SIZE 0x400 //1K +#define SKW_MAX_BUF_SIZE 0x100 //256B + +/*---------------CODE MEM SECTION-------------------------*/ +#define CODE_MEM_BASE_ADDR 0x100000 +#define CODE_MEM_SIZE 0x7A000//488K +/*-------------------------------------------------------*/ + +/*----------------DATA MEM SECTION-----------------------*/ +#define DATA_MEM_BASE_ADDR 0x20200000 +#define DATA_MEM_SIZE 0x40000//256K +/*-------------------------------------------------------*/ + +/*----------------CSCB MEM SECTION-----------------------*/ +#define CSCB_MEM_BASE_ADDR 0xE000ED00 +#define CSCB_MEM_SIZE 0x300//0.75K +/*-------------------------------------------------------*/ + + +/*----------------WREG MEM SECTION-----------------------*/ +#define WREG_MEM_BASE_ADDR 0x40820000 +#define WREG_MEM_SIZE 0x4000//16K +/*-------------------------------------------------------*/ + + +/*----------------PHYR MEM SECTION-----------------------*/ +#define PHYR_MEM_BASE_ADDR 0x40830000 +#define PHYR_MEM_SIZE 0x4000//16K +/*-------------------------------------------------------*/ + + +/*----------------SMEM MEM SECTION-----------------------*/ +#define SMEM_MEM_BASE_ADDR 0x40A00000 +#define SMEM_MEM_SIZE 0x58000//352K +/*-------------------------------------------------------*/ + + +/*----------------UMEM MEM SECTION-----------------------*/ +#define UMEM_MEM_BASE_ADDR 0x40B00000 +#define UMEM_MEM_SIZE 0xC000//48K +/*-------------------------------------------------------*/ + + +/*----------------SDIO MEM SECTION-----------------------*/ +#define SDIO_MEM_BASE_ADDR 0x401E0000 +#define SDIO_MEM_SIZE 0x800//2K +/*-------------------------------------------------------*/ + + +/*----------------BTDM MEM SECTION-----------------------*/ +#define BTDM_MEM_BASE_ADDR 0x41000000 +#define BTDM_MEM_SIZE 0x400//1K +/*-------------------------------------------------------*/ + + +/*----------------BTBT MEM SECTION-----------------------*/ +#define BTBT_MEM_BASE_ADDR 0x41000400 +#define BTBT_MEM_SIZE 0x400//1K +/*-------------------------------------------------------*/ + + +/*----------------BTLE MEM SECTION-----------------------*/ +#define BTLE_MEM_BASE_ADDR 0x41000800 +#define BTLE_MEM_SIZE 0x400//1K +/*-------------------------------------------------------*/ + + +/*----------------BTEM MEM SECTION-----------------------*/ +#define BTEM_MEM_BASE_ADDR 0x41010000 +#define BTEM_MEM_SIZE 0xC000//48K +/*-------------------------------------------------------*/ + + +/*----------------BTGB MEM SECTION-----------------------*/ +#define BTGB_MEM_BASE_ADDR 0x41022000 +#define BTGB_MEM_SIZE 0x40//64B +/*-------------------------------------------------------*/ + + +/*----------------BTRF MEM SECTION-----------------------*/ +#define BTRF_MEM_BASE_ADDR 0x41024000 +#define BTRF_MEM_SIZE 0x510//1K 272B +/*-------------------------------------------------------*/ + +/*-------------------------------------------------------*/ +//SV6316 +/*-------------------------------------------------------*/ +#define HIF_EDMA_BASE_ADDR 0x40188000 +#define HIF_EDMA_SIZE 0x1080 +/*-------------------------------------------------------*/ + +/*-------------------------------------------------------*/ +#define WFRF_BASE_ADDR 0x40144000 +#define WFRF_MEM_SIZE 0x5000 +/*-------------------------------------------------------*/ + +/*-------------------------------------------------------*/ +#define RFGLB_BASE_ADDR 0x40150000 +#define RFGLB_MEM_SIZE 0x600 +/*-------------------------------------------------------*/ + +/*-------------------------------------------------------*/ + + + +#endif /* !SKW_MEM_MAP_H */ diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_user_com.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_user_com.c new file mode 100755 index 0000000..aa2eba5 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/skw_user_com.c @@ -0,0 +1,534 @@ +#include <linux/kernel.h> +#include <linux/cdev.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/compat.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> +#include <linux/scatterlist.h> + #include <linux/notifier.h> +#include <linux/platform_device.h> +#include "skw_boot.h" +#include "skw_log_to_file.h" +#define UCOM_PORTNO_MAX 13 +#define UCOM_DEV_PM_OPS NULL +int cp_exception_sts=0; +static int log_start; +static unsigned int tmp_chipid = 0; +static int log_portno = 0; +static int skw_major = 0; +struct mutex ucom_mutex; +static struct class *skw_com_class = NULL; +struct ucom_dev { + atomic_t open; + spinlock_t lock; + int rx_busy; + int tx_busy; + int devno; + int portno; + struct sv6160_platform_data *pdata; + wait_queue_head_t wq; + struct cdev cdev; + char *rx_buf; + char *tx_buf; + struct notifier_block notifier; +}; +static struct ucom_dev *ucoms[UCOM_PORTNO_MAX]; +static char *dump_memory_buffer; +static int dump_buffer_size, dump_log_size; +#include "skw_dump_mem.c" +static int user_boot_open(struct inode *ip, struct file *fp) +{ + struct cdev *char_dev; + int ret = -EIO, i; + struct ucom_dev *ucom=NULL; + + char_dev = ip->i_cdev; + for(i=0; i< UCOM_PORTNO_MAX; i++) { + if(ucoms[i] && (ucoms[i]->devno == char_dev->dev)) { + ucom = ucoms[i]; + ret = 0; + break; + } + } + + if(cp_exception_sts) + ret = -EIO; + if(ucom && !cp_exception_sts) { + if(atomic_read(&ucom->open)) + return -EBUSY; + if(!cp_exception_sts) + ret=ucom->pdata->open_port(ucom->portno, NULL, NULL); + + if (ret == 0) { + atomic_inc(&ucom->open); + fp->private_data = ucom; + } + } + skwboot_log("Open user_boot device: ret=%d task %d\n", ret, (int)current->pid); + + return ret; +} + +static int user_boot_release(struct inode *ip, struct file *fp) +{ + struct ucom_dev *ucom = fp->private_data; + int count=100; + while(cp_exception_sts &&count--) + msleep(10); + + if(ucom){ + if(!cp_exception_sts) + ucom->pdata->close_port(ucom->portno); + atomic_dec(&ucom->open); + } + + return 0; +} + +static int ucom_open(struct inode *ip, struct file *fp) +{ + struct cdev *char_dev; + int ret = -EIO, i; + struct ucom_dev *ucom=NULL; + + char_dev = ip->i_cdev; + for(i=0; i< UCOM_PORTNO_MAX; i++) { + if(ucoms[i] && (ucoms[i]->devno == char_dev->dev)) { + ucom = ucoms[i]; + ret = 0; + break; + } + } + if(ucom) { + if(cp_exception_sts && strncmp(ucom->pdata->port_name, "LOG", 3)){ + skwboot_log("%s line:%d the modem assert \n", __func__,__LINE__); + return -EIO; + } + if(atomic_read(&ucom->open) > 1){ + skwboot_log("%s ,%d\n", __func__, __LINE__); + return -EBUSY; + } + atomic_inc(&ucom->open); + if (atomic_read(&ucom->open)==1) { + init_waitqueue_head(&ucom->wq); + spin_lock_init(&ucom->lock); + ucom->pdata->open_port(ucom->portno, NULL, NULL); + } + fp->private_data = ucom; + skwboot_log("%s: ucom[%d] %s(0x%x)\n", __func__, i, ucom->pdata->port_name, ucom->portno); + + if(!strncmp((char *)ucom->pdata->chipid,"SV6160",6)) + tmp_chipid = 0x6160; + else if(!strncmp((char *)ucom->pdata->chipid,"SV6316", 6)) + tmp_chipid = 0x6316; + else if(!strncmp((char *)ucom->pdata->chipid,"SV6160LITE", 10)) + tmp_chipid = 0x6161; + + skwboot_log("the portno=%d - chipid = 0x%lx \n",ucom->portno, (unsigned long)tmp_chipid); + } + + return ret; +} +static int ucom_release(struct inode *ip, struct file *fp) +{ + struct ucom_dev *ucom = fp->private_data; + int i; + + for(i=0; i< UCOM_PORTNO_MAX; i++) { + if(ucoms[i] == ucom) + break; + } + fp->private_data = NULL; + if(ucom && (i<UCOM_PORTNO_MAX)) { + skwboot_log("%s: ucom%p %s(0x%x)\n", __func__, ucom, ucom->pdata->port_name, ucom->devno); + if (atomic_read(&ucom->open)) { + atomic_dec(&ucom->open); + // Don't close LOG/AT port once open it, otherwise RX transfer lost. + if (strncmp(ucom->pdata->port_name, "LOG", 3) && + strncmp(ucom->pdata->port_name, "ATC", 3)) + ucom->pdata->close_port(ucom->portno); + else if (!log_start && !strncmp(ucom->pdata->port_name, "LOG", 3)) + ucom->pdata->close_port(ucom->portno); + wake_up(&ucom->wq); + } else + kfree(ucom); + } + return 0; +} + +static ssize_t ucom_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) +{ + struct ucom_dev *ucom = fp->private_data; + ssize_t r = count; + int ret; + unsigned long flags; + uint32_t *data; + + if(atomic_read(&ucom->open)==0) + return -EIO; + if (strncmp(ucom->pdata->port_name, "LOG", 3) && cp_exception_sts) + return -EIO; + + if (!strncmp(ucom->pdata->port_name, "LOG", 3) && dump_log_size) { + return skw_ucom_dump_from_buffer(buf, count, pos); + } + + spin_lock_irqsave(&ucom->lock, flags); + if(ucom->rx_busy) { + spin_unlock_irqrestore(&ucom->lock, flags); + return -EAGAIN; + } + ucom->rx_busy = 1; + if(count > ucom->pdata->max_buffer_size) + count = ucom->pdata->max_buffer_size; + spin_unlock_irqrestore(&ucom->lock, flags); + ret = ucom->pdata->hw_sdma_rx(ucom->portno, ucom->rx_buf, count); + ucom->rx_busy = 0; + data = (uint32_t *)ucom->rx_buf; + + if(ret > 0) { + r = ret; + if(ret > count) + ret = copy_to_user(buf, ucom->rx_buf, count); + else + ret = copy_to_user(buf, ucom->rx_buf, ret); + if(ret > 0) + return -EFAULT; + } else r = ret; + pr_debug("%s %s ret = %d\n", __func__,current->comm, (int)r); + return r; +} + +static ssize_t ucom_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos) +{ + struct ucom_dev *ucom = fp->private_data; + int ret = 0; + ssize_t r = count; + ssize_t size; + unsigned long flags; + + if(cp_exception_sts || atomic_read(&ucom->open)==0) + return -EIO; + spin_lock_irqsave(&ucom->lock, flags); + if(ucom->tx_busy) { + spin_unlock_irqrestore(&ucom->lock, flags); + skwboot_log("%s error 0\n", __func__); + return -EAGAIN; + } + ucom->tx_busy = 1; + spin_unlock_irqrestore(&ucom->lock, flags); + while(count){ + if(count > ucom->pdata->max_buffer_size) + size = ucom->pdata->max_buffer_size; + else + size = count; + if(copy_from_user(ucom->tx_buf, buf, size)) + return -EFAULT; + + if(ucom->pdata->port_name && !strncmp(ucom->pdata->port_name, "LOG", 3)){ + if(!strncmp(ucom->tx_buf, "START", 5)){ + skwboot_log("%s START log to file \n", __func__); + log_start = skw_modem_log_init(ucom->pdata, NULL, (void *)ucom); + + } + else if(!strncmp(ucom->tx_buf, "STOP", 4)){ + skwboot_log("%s STOP log to file \n", __func__); + skw_modem_log_exit(); + log_start = 0; + } + else + skwboot_log("%s LOG write string:%s \n", __func__, ucom->tx_buf); + ucom->tx_busy = 0; + return r; + } + if (ucom->pdata->port_name && !strncmp(ucom->pdata->port_name, "ATC", 3)) { + ucom->tx_buf[size++] = 0x0D; + ucom->tx_buf[size++] = 0x0A; + count += 2; + } + ret = ucom->pdata->hw_sdma_tx(ucom->portno, ucom->tx_buf, size); + + if(ret < 0){ + skwboot_log("the close the ucom tx_busy=0"); + ucom->tx_busy=0; + return ret; + } + count -= ret; + buf += ret; + } + ucom->tx_busy = 0; + pr_debug("%s %s ret = %d\n", __func__,current->comm,(int)r); + return r; +} + +static long ucom_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct ucom_dev *ucom = fp->private_data; + int i; + + for(i=0; i< UCOM_PORTNO_MAX; i++) { + if(ucoms[i] == ucom) + break; + } + if((i<UCOM_PORTNO_MAX) && atomic_read(&ucom->open)) { + skwboot_log("%s ucom_%p rx_busy=%d\n", __func__, ucom, ucom->rx_busy); + if (ucom->pdata && ucom->rx_busy) + ucom->pdata->close_port(ucom->portno); + } + return 0; +} +#ifdef CONFIG_COMPAT +static long ucom_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + return ucom_ioctl(fp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif +static ssize_t boot_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) +{ + u32 status; + struct ucom_dev *ucom = fp->private_data; + int ret = 0; + + ret = ucom->pdata->hw_sdma_rx(ucom->portno, (char *)&status, 4); + if (ret > 0) + ret = copy_to_user(buf, &status, ret); + return ret; +} + + +static ssize_t boot_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos) +{ + struct ucom_dev *ucom = fp->private_data; + int ret = 0; + ret = ucom->pdata->hw_sdma_tx(ucom->portno, "WAKE", 4); + if(ret > 0) + return count; + + return ret; +} +static long boot_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct ucom_dev *ucom = fp->private_data; + unsigned char cp_log_state = 0; + int ret = 0; + switch (cmd) { + case 0: + ret = copy_to_user((char*)arg, (char*)&tmp_chipid, 4); + skwboot_log("the orgchip = %s ,the chipid = 0x%x\n",(char *)ucom->pdata->chipid, tmp_chipid); + break; + case _IOWR('S', 1, uint8_t *): + break; + case _IOWR('S', 2, uint8_t *): +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + cp_log_state =1; //close log + //skw_sdio_cp_log(1); +#else + cp_log_state = 2;//open log +#endif + ret = copy_to_user((char*)arg, (char*)&cp_log_state, 1); + skwboot_log("the cp_log_state = %d \n", cp_log_state); + break; + } + return 0; +} + +#ifdef CONFIG_COMPAT +static long boot_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + return boot_ioctl(fp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations skw_ucom_ops = { + .owner = THIS_MODULE, + .open = ucom_open, + .read = ucom_read, + .write = ucom_write, + .unlocked_ioctl = ucom_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ucom_compat_ioctl, +#endif + .release= ucom_release, +}; +static const struct file_operations skw_user_boot_ops = { + .owner = THIS_MODULE, + .open = user_boot_open, + .read = boot_read, + .write = boot_write, + .unlocked_ioctl = boot_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = boot_compat_ioctl, +#endif + .release= user_boot_release, +}; + +static int skw_ucom_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sv6160_platform_data *pdata = dev->platform_data; + struct ucom_dev *ucom; + int ret = 0; + + mutex_lock(&ucom_mutex); + if(skw_com_class == NULL) { + skw_com_class = class_create(THIS_MODULE, "btcom"); + if(IS_ERR(skw_com_class)) { + skwboot_log("skw_ucom_probe: class prt = %p\n", skw_com_class); + ret = PTR_ERR(skw_com_class); + skw_com_class = NULL; + mutex_unlock(&ucom_mutex); + return ret; + }else{ + skwboot_log( + "skw_ucom_probe:class prt = %p, name = %s\n", + skw_com_class, skw_com_class->name); + } + } + mutex_unlock(&ucom_mutex); + + if (pdata) { + ucom = kzalloc(sizeof(struct ucom_dev), GFP_KERNEL); + if(!ucom) + return -ENOMEM; + if (strncmp(pdata->port_name, "BTBOOT", 6)) { + ucom->rx_buf = kzalloc(pdata->max_buffer_size, GFP_KERNEL); + if(ucom->rx_buf) { + ucom->tx_buf = kzalloc(pdata->max_buffer_size, GFP_KERNEL); + if(!ucom->tx_buf) { + kfree(ucom->rx_buf); + kfree(ucom); + return -ENOMEM; + } + }else{ + kfree(ucom); + return -ENOMEM; + } + ret =__register_chrdev(skw_major, pdata->data_port+1, 1, pdata->port_name, &skw_ucom_ops); + } else { + pdata->data_port = UCOM_PORTNO_MAX - 1; + ret =__register_chrdev(skw_major, UCOM_PORTNO_MAX, 1, + pdata->port_name, &skw_user_boot_ops); + } + if(ret < 0) { + kfree(ucom->rx_buf); + kfree(ucom->tx_buf); + kfree(ucom); + return ret; + } + if(skw_major == 0) + skw_major = ret; + ucom->devno = MKDEV(skw_major, pdata->data_port+1); + ucom->pdata = pdata; + ucom->portno = pdata->data_port; + atomic_set(&ucom->open, 0); + platform_set_drvdata(pdev, ucom); + ucoms[ucom->portno] = ucom; + device_create(skw_com_class, NULL, ucom->devno, NULL, "%s", pdata->port_name); + if(!strncmp(ucom->pdata->port_name, "ATC", 3)) + skw_bt_state_event_init(ucom); + if (!strncmp(ucom->pdata->port_name, "LOG", 3)) { + log_portno = ucom->portno; +#ifndef CONFIG_SEEKWAVE_PLD_RELEASE + log_start = skw_modem_log_init(ucom->pdata, NULL, (void *)ucom); +#endif + } + return 0; + } + return -EINVAL; +} + +static int skw_ucom_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sv6160_platform_data *pdata = dev->platform_data; + struct ucom_dev *ucom; + int ret, i; + int devno; + + ucom = platform_get_drvdata(pdev); + + if(ucom) { + if(!strncmp(ucom->pdata->port_name, "LOG", 3)) { + skw_modem_log_exit(); + log_start = 0; + } + + if(!strncmp(ucom->pdata->port_name, "BTCMD", 5)) { + if (ucom->rx_busy) { + ucom->pdata->close_port(ucom->portno); + } + cp_exception_sts = 0; + } + if (!strncmp(ucom->pdata->port_name, "ATC", 3)) { + skw_bt_state_event_deinit(ucom); + } + ret = wait_event_interruptible_timeout(ucom->wq, + (!atomic_read(&ucom->open)), + msecs_to_jiffies(1000)); + if (ret <= 0) { + skwboot_warn( + "%s: open timeout ucom%p %s(0x%x) -- open_count=%d \n", + __func__, ucom, ucom->pdata->port_name, + ucom->devno, atomic_read(&ucom->open)); + } + skwboot_log("%s: ucom%p %s(0x%x) -- open_count=%d \n", __func__, ucom, + ucom->pdata->port_name, ucom->devno,atomic_read(&ucom->open)); + + devno = ucom->devno; + device_destroy(skw_com_class, devno); + ucoms[ucom->portno] = NULL; + kfree(ucom->rx_buf); + ucom->rx_buf = NULL; + kfree(ucom->tx_buf); + ucom->tx_buf = NULL; + if (!atomic_read(&ucom->open)) { + kfree(ucom); + ucom = NULL; + }else + atomic_set(&ucom->open, 0); + __unregister_chrdev(MAJOR(devno), MINOR(devno), 1, pdata->port_name); + platform_set_drvdata(pdev, NULL); + } + for(i=0; i<UCOM_PORTNO_MAX; i++) { + if(ucoms[i]) + break; + } + if (i >= UCOM_PORTNO_MAX && skw_com_class) { + class_destroy(skw_com_class); + skw_com_class = NULL; + } + return 0; +} + +static struct platform_driver skw_ucom_driver = { + .driver = { + .name = (char*)"skw_ucom", + .bus = &platform_bus_type, + .pm = UCOM_DEV_PM_OPS, + }, + .probe = skw_ucom_probe, + .remove = skw_ucom_remove, +}; + +int skw_ucom_init(void) +{ + dump_log_size = 0; + log_start = 0; + mutex_init(&ucom_mutex); + platform_driver_register(&skw_ucom_driver); + return 0; +} + +void skw_ucom_exit(void) +{ + if (dump_memory_buffer) + kfree(dump_memory_buffer); + dump_memory_buffer = NULL; + dump_log_size = 0; + cp_exception_sts=0; + mutex_destroy(&ucom_mutex); + platform_driver_unregister(&skw_ucom_driver); +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/sv6621s_mem_map.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/sv6621s_mem_map.h new file mode 100755 index 0000000..6c9003e --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/skwutil/sv6621s_mem_map.h @@ -0,0 +1,114 @@ +/* + * sv6160_mem_map.h + * Copyright (C) 2022 cfig <junwei.jiang@seekwavetech.com> + * + * Distributed under terms of the MIT license. + */ + +#ifndef SV6160_MEM_MAP_H +#define SV6160_MEM_MAP_H + +//#define SKW_MAX_BUF_SIZE 0x400 //1K +#define SKW_MAX_BUF_SIZE 0x100 //256B + +/*---------------CODE MEM SECTION-------------------------*/ +#define CODE_MEM_BASE_ADDR 0x100000 +#define CODE_MEM_SIZE 0x7A000//488K +/*-------------------------------------------------------*/ + +/*----------------DATA MEM SECTION-----------------------*/ +#define DATA_MEM_BASE_ADDR 0x20200000 +#define DATA_MEM_SIZE 0x40000//256K +/*-------------------------------------------------------*/ + +/*----------------CSCB MEM SECTION-----------------------*/ +#define AHB_REG_BASE_ADDR 0x40000000 +#define AHB_REG_SIZE 0x400 +/*-------------------------------------------------------*/ + + +/*----------------WREG MEM SECTION-----------------------*/ +#define WREG_MEM_BASE_ADDR 0x40820000 +#define WREG_MEM_SIZE 0xC000//48K +/*-------------------------------------------------------*/ + + +/*----------------PHYR MEM SECTION-----------------------*/ +#define PHYR_MEM_BASE_ADDR 0x40830000 +#define PHYR_MEM_SIZE 0x4000//16K +/*-------------------------------------------------------*/ + + +/*----------------SMEM MEM SECTION-----------------------*/ +#define SMEM_MEM_BASE_ADDR 0x40A00000 +#define SMEM_MEM_SIZE 0x58000//352K +/*-------------------------------------------------------*/ + +/*----------------UMEM MEM SECTION-----------------------*/ +#define UMEM_MEM_BASE_ADDR 0x401E0000 +#define UMEM_MEM_SIZE 0xC000//48K +/*-------------------------------------------------------*/ + + +/*----------------SDIO MEM SECTION-----------------------*/ +#define SDIO_MEM_BASE_ADDR 0x401D0000 +#define SDIO_MEM_SIZE 0x800//2K +/*-------------------------------------------------------*/ + + +/*----------------BTDM MEM SECTION-----------------------*/ +#define BTDM_MEM_BASE_ADDR 0x41000000 +#define BTDM_MEM_SIZE 0xC00//3K +/*-------------------------------------------------------*/ + + +/*----------------BTBT MEM SECTION-----------------------*/ +#define BTBT_MEM_BASE_ADDR 0x41000400 +#define BTBT_MEM_SIZE 0x400//1K +/*-------------------------------------------------------*/ + + +/*----------------BTLE MEM SECTION-----------------------*/ +#define BTLE_MEM_BASE_ADDR 0x41000800 +#define BTLE_MEM_SIZE 0x400//1K +/*-------------------------------------------------------*/ + + +/*----------------BTEM MEM SECTION-----------------------*/ +#define BTEM_MEM_BASE_ADDR 0x41010000 +#define BTEM_MEM_SIZE 0xC000//48K +/*-------------------------------------------------------*/ + + +/*----------------BTGB MEM SECTION-----------------------*/ +#define BTGB_MEM_BASE_ADDR 0x41022000 +#define BTGB_MEM_SIZE 0x40//64B +/*-------------------------------------------------------*/ + + +/*----------------BTRF MEM SECTION-----------------------*/ +#define BTRF_MEM_BASE_ADDR 0x41024000 +#define BTRF_MEM_SIZE 0x510//1K 272B +/*-------------------------------------------------------*/ + +/*----------------BTRF MEM SECTION-----------------------*/ +#define RFTOP_MEM_BASE_ADDR 0x40148000 +#define RFTOP_MEM_SIZE 0xC20// +/*-------------------------------------------------------*/ + + +/*----------------BTRF MEM SECTION-----------------------*/ +#define RCLK_MEM_BASE_ADDR 0x40150000 +#define RCLK_MEM_SIZE 0x200//512B +/*-------------------------------------------------------*/ + +/*----------------BTRF MEM SECTION-----------------------*/ +#define BBPLL_MEM_BASE_ADDR 0x40150400 +#define BBPLL_MEM_SIZE 0x200//512B +/*-------------------------------------------------------*/ +/*-------------------------------------------------------*/ +/*-------------------------------------------------------*/ + + + +#endif /* !SV6160_MEM_MAP_H */ diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/Kconfig b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/Kconfig new file mode 100755 index 0000000..ce99b1c --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/Kconfig @@ -0,0 +1,10 @@ +config SKW_USB + tristate "SeekWave USB Support" + depends on USB ||COMPILE_TEST + default n + help + Enable this module for WCN_USB + chip usb interface bus Support. + Please insmod this module before any other + WCN subsystems. Thanks. + diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/README.md b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/README.md new file mode 100755 index 0000000..8d12f18 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/README.md @@ -0,0 +1,2 @@ +#seekwave tech usb drivers + diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb.h new file mode 100755 index 0000000..c25821d --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb.h @@ -0,0 +1,55 @@ +/***************************************************************** + *Copyright (C) 2021 Seekwave Tech Inc. + *Filename : skw_usb.h + *Authors:seekwave platform + * + * This software is licensed under the terms of the the GNU + * General Public License version 2, as published by the Free + * Software Foundation, and may be copied, distributed, and + * modified under those terms. + * + * This program is distributed in the hope that it will be usefull, + * but without any warranty;without even the implied warranty of + * merchantability or fitness for a partcular purpose. See the + * GUN General Public License for more details. + * **************************************************************/ +#ifndef WCN_USB_H +#define WCN_USB_H + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/mutex.h> +#include <linux/bitops.h> +#include <linux/kthread.h> +#include <linux/notifier.h> +#include "../skwutil/skw_boot.h" + +#define skwusb_log(fmt, args...) \ + pr_info("[SKW_USB]:" fmt, ## args) + +#define skwusb_err(fmt, args...) \ + pr_err("[SKW_USB_ERR]:" fmt, ## args) + +#define skwusb_data_pr(level, prefix_str, prefix_type, rowsize,\ + groupsize, buf, len, asscii)\ + do{if(loglevel) \ + print_hex_dump(level, prefix_str, prefix_type, rowsize,\ + groupsize, buf, len, asscii);\ + }while(0) + + +#define USB_RX_TASK_PRIO 90 +#define SKW_CHIP_ID_LENGTH 16 //SV6160 chip id lenght + +int skw_usb_recovery_debug(int disable); +int skw_usb_recovery_debug_status(void); +void reboot_to_change_bt_uart1(char *mode); +int skw_usb_debug_log_open(void); +int skw_usb_debug_log_close(void); + +#endif /* WCN_USB_H */ diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_debugfs.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_debugfs.c new file mode 100755 index 0000000..b391eee --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_debugfs.c @@ -0,0 +1,123 @@ +/***************************************************************************** + * Copyright(c) 2020-2030 Seekwave Corporation. + * SEEKWAVE TECH LTD..CO + *Seekwave Platform the usb log debug fs + *FILENAME:skw_usb_debugfs.c + *DATE:2022-04-11 + *MODIFY: + * + **************************************************************************/ + +#include "skw_usb_debugfs.h" +#include "skw_usb_log.h" +#include "skw_usb.h" + +static struct proc_dir_entry *skw_usb_proc_root; + +static int skw_usb_proc_show(struct seq_file *seq, void *v) +{ +#define SKW_BSP_CONFIG_INT(conf) \ + do { \ + seq_printf(seq, "%s=%d\n", #conf, conf); \ + } while (0) + +#define SKW_BSP_CONFIG_BOOL(conf) \ + do { \ + if (IS_ENABLED(conf)) \ + seq_printf(seq, "%s=y\n", #conf); \ + else \ + seq_printf(seq, "# %s is not set\n", #conf); \ + } while (0) + +#define SKW_BSP_CONFIG_STRING(conf) \ + do { \ + seq_printf(seq, "%s=\"%s\"\n", #conf, conf); \ + } while (0) + + seq_puts(seq, "\n"); + seq_printf(seq, "Kernel Version: \t%s\n", + UTS_RELEASE); + seq_puts(seq, "\n"); + + SKW_BSP_CONFIG_BOOL(CONFIG_SEEKWAVE_BSP_DRIVERS); + SKW_BSP_CONFIG_BOOL(CONFIG_SKW_USB); + SKW_BSP_CONFIG_BOOL(CONFIG_SEEKWAVE_PLD_RELEASE); + + seq_puts(seq, "\n"); + + return 0; +} + +static int skw_usb_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_usb_proc_show, NULL); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_usb_profile_proc_fops = { + .proc_open = skw_usb_proc_open, + .proc_read = seq_read, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_usb_profile_proc_fops = { + .owner = THIS_MODULE, + .open = skw_usb_proc_open, + .read = seq_read, + .release = single_release, +}; +#endif + +struct proc_dir_entry *skw_usb_procfs_file(struct proc_dir_entry *parent, + const char *name, umode_t mode, + const void *fops, void *data) +{ + struct proc_dir_entry *dentry = parent ? parent : skw_usb_proc_root; + + if (!dentry) + return NULL; + + return proc_create_data(name, mode, dentry, fops, data); +} + +int skw_usb_proc_init_ex(const char *name, umode_t mode, + const void *fops, void *data) +{ + if (!skw_usb_proc_root) + return -1; + skw_usb_procfs_file(skw_usb_proc_root, name, mode, fops, NULL); + return 0; +} + +int skw_usb_proc_init(void) +{ + skw_usb_proc_root = proc_mkdir("skwusb", NULL); + if (!skw_usb_proc_root){ + pr_err("creat proc skwusb failed\n"); + return -1; + } + + skw_usb_procfs_file(skw_usb_proc_root,"profile", 0666, &skw_usb_profile_proc_fops, NULL); + + return 0; +} + +void skw_usb_proc_deinit(void) +{ + if (!skw_usb_proc_root) + return; + proc_remove(skw_usb_proc_root); +} + +int skw_usb_debugfs_init(void) +{ + skw_usb_dbg("%s :traced\n", __func__); + skw_usb_proc_init(); + return 0; +} + +void skw_usb_debugfs_deinit(void) +{ + skw_usb_dbg("%s :traced\n", __func__); + skw_usb_proc_deinit(); +} \ No newline at end of file diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_debugfs.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_debugfs.h new file mode 100755 index 0000000..6c13915 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_debugfs.h @@ -0,0 +1,24 @@ +/****************************************************************************** + * + * Copyright(c) 2020-2030 Seekwave Corporation. + * + *****************************************************************************/ +#ifndef __SKW_USB_DEBUGFS_H__ +#define __SKW_USB_DEBUGFS_H__ + +#include <linux/fs.h> +#include <linux/debugfs.h> +#include <linux/proc_fs.h> +#include <linux/scatterlist.h> +#include <generated/utsrelease.h> +#include "boot_config.h" +#include "skw_boot.h" + +int skw_usb_debugfs_init(void); +void skw_usb_debugfs_deinit(void); +struct proc_dir_entry *skw_usb_procfs_file(struct proc_dir_entry *parent, + const char *name, umode_t mode, + const void *proc_fops, void *data); +int skw_usb_proc_init_ex(const char *name, umode_t mode, + const void *fops, void *data); +#endif \ No newline at end of file diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_io.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_io.c new file mode 100755 index 0000000..3a9dcbd --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_io.c @@ -0,0 +1,2825 @@ +/************************************************************************ + *Copyright(C) 2020-2021: Seekwave tech LTD China + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +#include <linux/platform_device.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> +#include <linux/version.h> +#include <linux/notifier.h> +#include <linux/semaphore.h> +#include <linux/pm_runtime.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/err.h> +#include <linux/wait.h> +#include <linux/gpio.h> +#include "skw_usb.h" +#include "skw_usb_log.h" +#include "skw_usb_debugfs.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include <linux/bits.h> +#else +#include <linux/bitops.h> +#endif +#ifndef GENMASK +#ifdef CONFIG_64BIT +#define BITS_PER_LONG 64 +#else +#define BITS_PER_LONG 32 +#endif /* CONFIG_64BIT */ +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) +#endif + +#define MAX_BUFFER_SIZE 20*1024 +#define MAX_MSG_SIZE MAX_BUFFER_SIZE + +#define VENDOR_MSG_MODEM_ASSERT 0xA5 +#define VENDOR_MSG_SERVICE_CTRL 0xA6 +#define VENDOR_MSG_PACKET_COUNT 0xA7 +#define VENDOR_MSG_LOG_SWITCH 0xA8 +#define VENDOR_MSG_MODEM_RESET 0xA9 +#define VENDOR_MSG_MODEM_SUSP 0xAA + +#define WIFI_SERVICE 0 +#define BT_SERVICE 1 + +#define SERVICE_START 0 +#define SERVICE_STOP 1 + +#define MODEM_OFF 0 +#define MODEM_ON 1 +#define MODEM_HALT 2 +#define MODEM_DOWNLOAD_FAILED 4 + +#define WIFI_PORT_SHARE_FLAG 0x4000 +//#define MODEM_WDT_SUPPORT 0x40 +#define USB_HOST_RESUME_SUPPORT 0x20 + +#define MAX_USB_PORT MAX_PORT_COUNT +#define MAX_PACKET_COUNT 20 +static struct delayed_work skw_except_work; +static struct work_struct add_device_work; +static struct work_struct dump_memory_worker; +static struct work_struct usb_control_worker; +static struct platform_device *wifi_data_pdev; +static u64 port_dmamask = DMA_BIT_MASK(32); +static u32 service_state_map = 0; +static int cp_log_status = 0; +static char *firmware_data; +static int firmware_size; +static int firmware_addr; +static struct seekwave_device *usb_boot_data; +static struct completion download_done; +static struct completion loop_completion; +static BLOCKING_NOTIFIER_HEAD(modem_notifier_list); +static int chip_en_gpio; +static int host_wake_gpio; +static int modem_status; +static int usb_speed_switching; +static int cls_recovery_mode_en; +static char *skw_chipid; +static u32 last_sent_wifi_cmd[3]; +static u32 last_recv_wifi_evt[3]; +static u32 last_recv_wifi_ack[3]; +static u64 last_sent_time, last_ack_time; +static struct scatterlist *sgs; +static int nr_sgs; +static int start_service_flag = 0; +static u16 transfer_index; +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ + +static const struct usb_device_id skw_usb_io_id_table[] = { + { USB_VENDOR_AND_INTERFACE_INFO(0x3607, 0x02, 0x02, 0) }, + { USB_DEVICE(0x0483, 0x5720) }, + { USB_DEVICE(0x0483, 0x5721) }, + { USB_DEVICE(0x3607, 0x6316) }, + { USB_DEVICE(0x3607, 0x6621) }, + { USB_DEVICE(0x3607, 0x6160) }, + {} /* Terminating entry */ +}; +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy:XW + * + ********************************************************************* */ +static struct recovery_data{ + struct mutex except_mutex; + int cp_state; +} g_recovery_data; + +#ifdef CONFIG_SKW_DL_TIME_STATS + ktime_t cur_time,last_time; +#endif + +#define SKW_USB_GET_RECOVERY_DATA() &g_recovery_data + +static struct usb_port_struct { + struct work_struct work; + struct platform_device *pdev; + int portno; + struct usb_interface *interface; + struct usb_device *udev; + struct urb *read_urb; + struct usb_endpoint_descriptor *epin; + struct urb *write_urb; + struct usb_endpoint_descriptor *epout; + char *read_buffer; + char *write_buffer; + int buffer_size; + struct usb_anchor read_submitted; + struct usb_anchor write_submitted; + struct task_struct *thread; + rx_submit_fn rx_submit; + adma_callback adma_tx_callback; + sdma_callback sdma_tx_callback; + void *rx_data; + void *tx_data; + int state; + int ep_mps; + int max_packet_count; + struct semaphore sem; + int is_dloader; + int sent_packet_count; + int req_tx_packet; + wait_queue_head_t rx_wait; + wait_queue_head_t tx_wait; + struct tasklet_struct tasklet; + struct list_head rx_urb_list; + struct list_head tx_urb_list; + struct list_head rx_done_urb_list; + struct list_head suspend_urb_list; + spinlock_t rx_urb_lock; + spinlock_t tx_urb_lock; + int tx_urb_count; + int rx_packet_count; + int suspend; + u64 tx_done_time; + u64 rx_done_time; +} *usb_ports[MAX_USB_PORT]; + +static int modem_assert(void); +static int skw_recovery_mode(void); +static struct usb_port_struct *log_port; +int skw_reset_bus_dev(void); +extern void kernel_restart(char *cmd); +static int bulkin_read_timeout(int portno, char *buffer, int size, int *actual, int timeout); +static int bulkout_write_timeout(int portno, char *buffer, int size, int *actual, int timeout); +static void bulkout_async_complete(struct urb *urb); +static void bulkin_async_complete(struct urb *urb); +static int assert_info_print; +static int usb_bt_rx_entry(void *para); +char firmware_version[256]; +#ifdef CONFIG_BT_SEEKWAVE +static int bt_audio_port; +#endif +static struct platform_device *bluetooth_pdev = NULL; +static int wifi_port_share; +static int bulk_async_read; +static int dump_memory_done; +static char* dump_memory_buffer=NULL; +static int dump_buffer_size=0; +static int* dump_log_size=NULL; +static int usb_bus_num; +static int usb_port_num; + +void skw_get_port_statistic(char *buffer, int size) +{ + int ret = 0; + int i; + + if(!buffer) + return; + + ret += sprintf(&buffer[ret], "%s", firmware_version); + for(i=0; i<MAX_USB_PORT; i++) { + if(ret >= size) + break; + + if (usb_ports[i]) { + ret += sprintf(&buffer[ret], + "port%d: req_tx %d tx_done %d, rx %d: tx_time: 0x%x rx_time: 0x%x\n", + i, usb_ports[i]->req_tx_packet, usb_ports[i]->sent_packet_count, + usb_ports[i]->rx_packet_count, (u32)usb_ports[i]->tx_done_time, + (u32)usb_ports[i]->rx_done_time); + } + } +} + +#include "usb_boot.c" +void modem_register_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_register(&modem_notifier_list, nb); +} +void modem_unregister_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&modem_notifier_list, nb); +} +void modem_notify_event(int event) +{ + blocking_notifier_call_chain(&modem_notifier_list, event, NULL); +} +void skw_usb_exception_work(struct work_struct *work) +{ + struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); + skw_usb_info(" enter cp_state=%d...\n", recovery->cp_state); + mutex_lock(&recovery->except_mutex); + if(recovery->cp_state!=1) + { + mutex_unlock(&recovery->except_mutex); + return; + } + recovery->cp_state = DEVICE_BLOCKED_EVENT; + mutex_unlock(&recovery->except_mutex); + modem_notify_event(DEVICE_BLOCKED_EVENT); + service_state_map=0; + skw_recovery_mode(); +} + +int skw_usb_recovery_debug(int disable) +{ + cls_recovery_mode_en = disable; + skw_usb_info("the recovery status =%d\n", cls_recovery_mode_en); + return 0; +} + +int skw_usb_recovery_debug_status(void) +{ + skw_usb_info("the recovery val =%d\n", cls_recovery_mode_en); + return cls_recovery_mode_en; +} + + +static void usb_setup_service_devices(void) +{ +#ifdef CONFIG_BT_SEEKWAVE + struct usb_port_struct *bt_port; +#endif + int ret; + + skw_bind_boot_driver(&usb_ports[0]->udev->dev); + if(usb_ports[1]->pdev){ + if(wifi_data_pdev==NULL) { + wifi_data_pdev = usb_ports[1]->pdev; + ret = platform_device_add(usb_ports[1]->pdev); + if(ret) { + skw_usb_err("the fail to register WIFI device\n"); + wifi_data_pdev = NULL; + platform_device_put(usb_ports[1]->pdev); + } else { + skw_usb_info("add WIFI devices done\n"); + } + } + } else + skw_usb_err("NOT suppport WIFI service\n"); +#ifdef CONFIG_BT_SEEKWAVE + if (bluetooth_pdev) { + bt_port = usb_ports[bt_audio_port]; + bt_port->pdev = bluetooth_pdev; + bluetooth_pdev = NULL; + ret = platform_device_add(bt_port->pdev); + if(ret) { + skw_usb_err("failt to register Bluetooth device\n"); + platform_device_put(bt_port->pdev); + bt_port->pdev = NULL; + } else + skw_usb_info("add Bluetooth devices done\n"); + } +#endif + +} +void add_devices_work(struct work_struct *work) +{ + if (usb_ports[0]) + usb_setup_service_devices(); +} +void skw_set_bt_suspend_flag(void) +{ +} +static void usb_port_alloc_recv_urbs(struct usb_port_struct *port, struct usb_endpoint_descriptor *epd, int count, int buffer_size) +{ + int i; + struct urb *urb; + + for(i=0; i<count; i++) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if(!urb) + break; + if(!buffer_size) { + urb->transfer_buffer = NULL; + urb->transfer_buffer_length = 0; + } else{ + urb->transfer_buffer = kzalloc(buffer_size, GFP_KERNEL); + if(!urb->transfer_buffer) { + usb_free_urb(urb); + break; + } + urb->transfer_buffer_length = buffer_size; + } + usb_fill_bulk_urb(urb, port->udev,usb_rcvbulkpipe(port->udev, epd->bEndpointAddress), + urb->transfer_buffer, buffer_size, bulkin_async_complete, NULL); + list_add_tail(&urb->urb_list, &port->rx_urb_list); + } + skw_usb_dbg(" urb cout %d\n", i); +} + +static void usb_port_alloc_xmit_urbs(struct usb_port_struct *port, struct usb_endpoint_descriptor *epd, int count, int buffer_size) +{ + int i; + struct urb *urb; + + for(i=0; i<count; i++) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if(!urb) + break; + if(!buffer_size) { + urb->transfer_buffer = NULL; + urb->transfer_buffer_length = 0; + } else{ + urb->transfer_buffer = kzalloc(buffer_size, GFP_KERNEL); + if(!urb->transfer_buffer) { + usb_free_urb(urb); + break; + } + urb->transfer_buffer_length = buffer_size; + } + usb_fill_bulk_urb(urb, port->udev,usb_sndbulkpipe(port->udev, epd->bEndpointAddress), + urb->transfer_buffer, buffer_size, bulkout_async_complete, NULL); + list_add_tail(&urb->urb_list, &port->tx_urb_list); + } + skw_usb_dbg(" urb cout %d\n", i); +} + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +int open_usb_port(int id, void *callback, void *data) +{ + struct usb_port_struct *port; + + if(id >= MAX_USB_PORT) + return -EINVAL; + + port = usb_ports[id]; + if(port->state==0) + return -EIO; + skw_usb_info("port%d\n", id); + if (port->state==1) { + if(port->read_urb && !port->read_urb->context) + init_usb_anchor(&port->read_submitted); + if(port->write_urb && !port->write_urb->context) + init_usb_anchor(&port->write_submitted); + } + port->state = 2; + port->rx_submit = callback; + port->rx_data = data; + if(callback && data && !port->thread) { + sema_init(&port->sem, 0); + port->thread = kthread_create(usb_bt_rx_entry, port, port->interface->cur_altsetting->string); + if(port->thread) + wake_up_process(port->thread); + } + if (port->interface && modem_status==MODEM_ON) { + struct usb_host_interface *iface_desc; + iface_desc = port->interface->cur_altsetting; + if (iface_desc && iface_desc->string && + !strncmp(iface_desc->string, "LOG", 3)) + skw_usb_cp_log(0); + } + return 0; +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int bulkin_read(struct usb_port_struct *port, void *buffer, int size) +{ + int retval = -1; + DECLARE_COMPLETION_ONSTACK(done); + if(port->state==0) + return -EIO; + + if(port == log_port){ + memset(buffer, 0 , size); + } + + if(port->read_urb) { + port->read_urb->transfer_buffer = buffer; + port->read_urb->transfer_buffer_length = size; + port->read_urb->context = &done; + usb_anchor_urb(port->read_urb, &port->read_submitted); + retval = usb_submit_urb(port->read_urb,GFP_KERNEL); + if(retval==0) { + retval = wait_for_completion_interruptible(&done); + if(retval == -ERESTARTSYS) + usb_kill_urb(port->read_urb); + else if(port->read_urb->status) + retval = port->read_urb->status; + else if(retval==0) + retval = port->read_urb->actual_length; + port->read_urb->context = NULL; + } else { + if (retval < 0) + usb_unanchor_urb(port->read_urb); + port->read_urb->context = NULL; + } + } + if(port == log_port) { + if(assert_info_print && assert_info_print<28 && retval<100) { + assert_info_print++; + if(retval > 4) + skw_usb_info("%s", (char *)buffer); + } + if(retval == 4) + assert_info_print = 28; + } + return retval; +} +int skw_bus_version(void) +{ + skw_usb_info("USB bus Version1.0\n"); + return 0; +} +/******************************************************* + * bulkin_read_async - async read from bulk in endpoint + * @port: pointer to struct usb_port_struct + * + * Submit a read urb for bulk in endpoint asynchronously. + * + * Return: 0 on success, negative value on error + ******************************************************/ +int bulkin_read_async(struct usb_port_struct *port) +{ + int retval = -1; + unsigned long flags; + struct urb *urb; + + spin_lock_irqsave(&port->rx_urb_lock, flags); + urb = list_first_entry(&port->rx_urb_list, struct urb, urb_list); + list_del_init(&urb->urb_list); + spin_unlock_irqrestore(&port->rx_urb_lock, flags); + if(urb->context) { + skw_usb_info("port is busy!!!\n"); + return -EBUSY; + } + + urb->complete = bulkin_async_complete; + urb->context = port; + if (port->suspend) + list_add_tail(&urb->urb_list, &port->suspend_urb_list); + else { + usb_anchor_urb(urb, &port->read_submitted); + bulk_async_read++; + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval < 0) { + bulk_async_read--; + usb_unanchor_urb(urb); + urb->context = NULL; + skw_usb_info(" is error!!! %d\n", retval); + list_add_tail(&urb->urb_list, &port->suspend_urb_list); + } + } + return retval; +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int bulkout_write(struct usb_port_struct *port, void *buffer, int size) +{ + int retval = -1; + DECLARE_COMPLETION_ONSTACK(done); + + + if (port && port->write_urb && !port->write_urb->context) { + if (port->suspend) { + skw_usb_info("port%d is suspended\n", port->portno); + return -EOPNOTSUPP; + } + port->write_urb->context = &done; + port->write_urb->transfer_buffer = buffer; + port->write_urb->transfer_buffer_length = size; + if(size%port->ep_mps == 0) + port->write_urb->transfer_flags |= URB_ZERO_PACKET; + usb_anchor_urb(port->write_urb, &port->write_submitted); + retval = usb_submit_urb(port->write_urb,GFP_KERNEL); + if(retval==0) { + retval = wait_for_completion_interruptible(&done); + if(retval==-ERESTARTSYS) + usb_kill_urb(port->write_urb); + else if (port->write_urb->status) + retval = port->write_urb->status; + else + retval = port->write_urb->actual_length; + port->write_urb->context = NULL; + } else { + + if (retval < 0) { + usb_unanchor_urb(port->write_urb); + skw_usb_info("is error!!! %d\n", retval); + } + port->write_urb->context = NULL; + } + } + return retval; +} + +int bulkout_write_async(struct usb_port_struct *port, void *buffer, int size) +{ + int retval = -1; + struct urb *urb; + unsigned long flags; + + if (port->suspend) { + skw_usb_info("port%d is suspended\n", port->portno); + return -EOPNOTSUPP; + } + spin_lock_irqsave(&port->tx_urb_lock, flags); + if(list_empty(&port->tx_urb_list)) { + spin_unlock_irqrestore(&port->tx_urb_lock, flags); + retval = wait_event_interruptible(port->tx_wait, (!list_empty(&port->tx_urb_list))); + spin_lock_irqsave(&port->tx_urb_lock, flags); + } + urb = list_first_entry(&port->tx_urb_list, struct urb, urb_list); + list_del_init(&urb->urb_list); + port->tx_urb_count++; + spin_unlock_irqrestore(&port->tx_urb_lock, flags); + + usb_fill_bulk_urb(urb, port->udev,usb_sndbulkpipe(port->udev, port->epout->bEndpointAddress), + buffer, size, bulkout_async_complete, port); + if(size%port->ep_mps == 0) + urb->transfer_flags |= URB_ZERO_PACKET; + + usb_anchor_urb(urb, &port->write_submitted); + retval = usb_submit_urb(urb,GFP_KERNEL); + if (retval < 0) { + usb_unanchor_urb(urb); + spin_lock_irqsave(&port->tx_urb_lock, flags); + port->tx_urb_count--; + list_add_tail(&urb->urb_list, &port->tx_urb_list); + spin_unlock_irqrestore(&port->tx_urb_lock, flags); + skw_usb_info("is error!!! %d\n", retval); + } + skw_usb_dbg(" portno %d wait done %d %d\n", port->portno, retval, port->tx_urb_count); + return retval; +} +void check_sgs_headers(struct scatterlist *sgs, int sg_num, int total) +{ + int i,size; + struct skw_packet2_header *header; + + size = 0; + for (i=0; i<sg_num; i++) { + header = (struct skw_packet2_header *)sg_virt(sgs + i); + size += header->len; + if (header->len > 2048 || size > total) + skw_usb_err("invalid packet: (%d - %d):( %d-%d-%d)\n", total, sg_num, i, header->len, size); + } +} + +int bulkout_write_sg_async(struct usb_port_struct *port, struct scatterlist *sgs, int sg_num, int total) +{ + struct urb *urb; + unsigned long flags; + int retval = -1; + + check_sgs_headers(sgs, sg_num, total); + spin_lock_irqsave(&port->tx_urb_lock, flags); + if(list_empty(&port->tx_urb_list)) { + spin_unlock_irqrestore(&port->tx_urb_lock, flags); + retval = wait_event_interruptible(port->tx_wait, (!list_empty(&port->tx_urb_list))); + spin_lock_irqsave(&port->tx_urb_lock, flags); + } + if (port->state==0) + return -EIO; + urb = list_first_entry(&port->tx_urb_list, struct urb, urb_list); + port->tx_urb_count++; + list_del_init(&urb->urb_list); + port->req_tx_packet += sg_num; + spin_unlock_irqrestore(&port->tx_urb_lock, flags); + urb->transfer_buffer = NULL; + urb->transfer_buffer_length = 0; + usb_fill_bulk_urb(urb, port->udev,usb_sndbulkpipe(port->udev, port->epout->bEndpointAddress), + NULL, 0, bulkout_async_complete, port); + urb->sg = sgs; + urb->num_sgs = sg_num; + urb->transfer_buffer_length = total; + if(total%port->ep_mps == 0) + urb->transfer_flags |= URB_ZERO_PACKET; + usb_anchor_urb(urb, &port->write_submitted); + //skw_usb_info("portno %d submit %d\n", port->portno, port->tx_urb_count); + retval = usb_submit_urb(urb,GFP_KERNEL); + if (retval < 0) { + usb_unanchor_urb(urb); + spin_lock_irqsave(&port->tx_urb_lock, flags); + port->tx_urb_count--; + list_add_tail(&urb->urb_list, &port->tx_urb_list); + spin_unlock_irqrestore(&port->tx_urb_lock, flags); + } + return retval; + +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int bulkout_write_sg(struct usb_port_struct *port, struct scatterlist *sgs, int sg_num, int total) +{ + int retval = -1; + DECLARE_COMPLETION_ONSTACK(done); + + if(!port->write_urb) + return -ENODEV; + if(port->write_urb->context) { + skw_usb_info("port is busy!!!\n"); + return -EBUSY; + } + if(port->write_urb) { + port->write_urb->sg = sgs; + port->write_urb->num_sgs = sg_num; + port->write_urb->transfer_buffer_length = total; + if(total%port->ep_mps == 0) + port->write_urb->transfer_flags |= URB_ZERO_PACKET; + port->write_urb->context = &done; + port->req_tx_packet += port->write_urb->num_sgs; + usb_anchor_urb(port->write_urb, &port->write_submitted); + retval = usb_submit_urb(port->write_urb,GFP_KERNEL); + if(retval==0) { + retval = wait_for_completion_interruptible(&done); + if(retval==0) + retval = port->write_urb->actual_length; + port->write_urb->context = NULL; + port->sent_packet_count += sg_num; + + } else { + skw_port_log(port->portno, "%s retval = %d\n", __func__, retval); + usb_unanchor_urb(port->write_urb); + port->write_urb->context = NULL; + } + } + if(retval > 0) + return 0; + return retval; +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int send_data(int portno, char *buffer, int total) +{ + struct usb_port_struct *port; + u32 *data; + + if(total==0) + return 0; + if (modem_status != MODEM_ON) + return -EIO; + port = usb_ports[portno]; + if(!port || !port->state) + return -EIO; + if (port->suspend) { + skw_usb_info("port%d is suspended\n", portno); + return -EOPNOTSUPP; + } + if (portno == 0) { + data = (u32 *)buffer; + memcpy(last_sent_wifi_cmd, data, 12); + last_sent_time = jiffies; + last_sent_wifi_cmd[0] = bulk_async_read; + } + return bulkout_write(port, buffer, total); +} +static void check_first_header(char *buffer, int total) +{ + struct skw_packet_header *header; + + header = (struct skw_packet_header *)buffer; + if (header->len > 1536 || header->len > total) + skw_usb_err("invalid packet: (%d - %d)\n", total, header->len); +} +static int send_data_async(int portno, char *buffer, int total) +{ + struct usb_port_struct *port; + int ret; + + if(total==0) + return 0; + if (modem_status != MODEM_ON) + return -EIO; + port = usb_ports[portno]; + if(!port || !port->state) + return -EIO; + ret = bulkout_write_async(port, buffer, total); + check_first_header(buffer, total); + return ret; +} + +int recv_data(int portno, char *buffer, int total) +{ + struct usb_port_struct *port; + + if(total==0) + return 0; + + port = usb_ports[portno]; + if(!port || !port->state) + return -EIO; + return bulkin_read(port, buffer, total); +} + +int close_usb_port(int portno) +{ + struct usb_port_struct *port; + + port = usb_ports[portno]; + + skw_usb_info("port%d\n", portno); + if (port) { + port->state = 1; + if(port->write_urb && port->write_urb->context) + usb_kill_urb(port->write_urb); + if(port->read_urb && port->read_urb->context) + usb_kill_urb(port->read_urb); + if(port->thread && down_interruptible(&port->sem)) + skw_usb_info("port%d rx thread exit\n", portno); + port->thread = NULL; + if (port->interface) { + struct usb_host_interface *iface_desc; + iface_desc = port->interface->cur_altsetting; + if (iface_desc && iface_desc->string && + !strncmp(iface_desc->string, "LOG", 3)) + skw_usb_cp_log(1); + } + } + return 0; +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +int wifi_send_cmd(int portno, struct scatterlist *sg, int sg_num, int total) +{ + struct usb_port_struct *port; + u32 *data; + int ret; + + if(total==0) + return 0; + if (modem_status != MODEM_ON) + return -EIO; + if(portno >= MAX_USB_PORT) + return -EINVAL; + port = usb_ports[portno]; + if(!port || !port->state) + return -EIO; + if (port->suspend) { + skw_usb_info("port%d is suspended\n", portno); + return -EOPNOTSUPP; + } + if (portno == 0) { + data = (u32 *)sg_virt(sg); + memcpy(last_sent_wifi_cmd, data, 12); + last_sent_time = jiffies; + last_sent_wifi_cmd[0] = bulk_async_read; + } + ret = bulkout_write_sg(port, sg, sg_num, total); + return ret; +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +int wifi_send_cmd_async(int portno, struct scatterlist *sg, int sg_num, int total) +{ + struct usb_port_struct *port; + u32 *data; + + if(total==0) + return 0; + if (modem_status != MODEM_ON) + return -EIO; + if(portno >= MAX_USB_PORT) + return -EINVAL; + port = usb_ports[portno]; + if(!port || !port->state) + return -EIO; + + if (port->suspend) { + skw_usb_info("port%d is suspended\n", portno); + return -EOPNOTSUPP; + } + if (portno == 0) { + data = (u32 *)sg_virt(sg); + memcpy(last_sent_wifi_cmd, data, 12); + last_sent_time = jiffies; + last_sent_wifi_cmd[0] = bulk_async_read; + } + return bulkout_write_sg_async(port, sg, sg_num, total); +} + +/************************************************************************ + *Decription: manual assert modem + *Author:jiayong.yang + *Date:2021-08-03 + *Modfiy: + *Notes: this function must not be invoked in IRQ context. + ************************************************************************/ +static int modem_assert_work(void) +{ + struct usb_port_struct *port; + struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); + int ret = -1; + u32 *cmd = last_sent_wifi_cmd; + + if(modem_status == MODEM_HALT){ + skw_usb_info("modem in recovery mode \n"); + return 0; + } + port = usb_ports[0]; + if(port && port->state) { + recovery->cp_state =1; + ret = usb_control_msg(port->udev, usb_sndctrlpipe(port->udev, 0), + VENDOR_MSG_MODEM_ASSERT, USB_DIR_OUT| USB_TYPE_VENDOR|USB_RECIP_DEVICE, + 0,0,NULL,0,1000); + skw_usb_err("SND ASSERT CMD ret = %d cmd: 0x%x 0x%x 0x%x: ACK 0x%x-0x%x-0x%x EVT: 0x%x 0x%x 0x%x \n", + ret, cmd[0], cmd[1], cmd[2], + last_recv_wifi_ack[0],last_recv_wifi_ack[1],last_recv_wifi_ack[2], + last_recv_wifi_evt[0],last_recv_wifi_evt[1],last_recv_wifi_evt[2]); + modem_status = MODEM_HALT; +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + schedule_delayed_work(&skw_except_work , msecs_to_jiffies(2000)); +#else + schedule_delayed_work(&skw_except_work , msecs_to_jiffies(6000)); +#endif + } + return ret; +} +static void usb_control_work(struct work_struct *work) +{ + modem_assert_work(); +} +static int modem_assert(void) +{ + struct usb_port_struct *port; + + port = usb_ports[0]; + if (port) + schedule_work(&usb_control_worker); + return 0; +} +int wifi_service_start(void) +{ + int ret = 0; + + if(!usb_boot_data) + return -ENODEV; + ret=usb_boot_data->wifi_start(); + + return ret; +} + +int wifi_service_stop(void) +{ + int ret = 0; + if(!usb_boot_data) + return -ENODEV; + ret=usb_boot_data->wifi_stop(); + return ret; +} + +int bt_service_start(void) +{ + int ret = 0; + + if(!usb_boot_data) + return -ENODEV; + ret=usb_boot_data->bt_start(); + return ret; +} + +int bt_service_stop(void) +{ + int ret = 0; + + if(!usb_boot_data) + return -ENODEV; + ret=usb_boot_data->bt_stop(); + return ret; +} +static int send_modem_service_command(u16 service, u16 command) +{ + struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); + struct usb_port_struct *port; + int ret = -1; + int timeout = 1000; + port = usb_ports[1]; +#ifndef MODEM_WDT_SUPPORT + if(usb_boot_data->chip_en < 0){ + skw_usb_err("chip_en = %d Invalid Pls check HW !!\n", usb_boot_data->chip_en); + return ret; + } +#endif + if(port) + skw_usb_info("(%d,%d) cp_state= %d\n", + service, command, recovery->cp_state); + if (recovery->cp_state) + return ret; + + if(port && port->state) { + skw_reinit_completion(download_done); + ret = usb_control_msg(port->udev, usb_sndctrlpipe(port->udev, 0), + VENDOR_MSG_SERVICE_CTRL, USB_DIR_OUT| USB_TYPE_VENDOR|USB_RECIP_DEVICE, + service, command, NULL, 0, 1000); + } + if((command & 0x01) == SERVICE_START) { + skw_usb_info("ret = %d\n", ret); + complete(&loop_completion); + start_service_flag = 1; + wait_for_completion_interruptible_timeout(&download_done, msecs_to_jiffies(timeout + 1000*service)); + service_state_map |= (1<<service); + } else { + if(service==BT_SERVICE && modem_status==MODEM_ON) + wait_for_completion_interruptible_timeout(&download_done, msecs_to_jiffies(1000)); + service_state_map &= ~(1<<service); + } + return ret; +} + +static int skw_get_packet_count(u8 portno) +{ + struct usb_port_struct *port; + int ret = -1; + u16 *packet_count, size=2; + + port = usb_ports[portno]; + if(port && port->state) { + ret = usb_control_msg(port->udev, usb_rcvctrlpipe(port->udev, 0), + VENDOR_MSG_PACKET_COUNT, USB_DIR_IN| USB_TYPE_VENDOR|USB_RECIP_DEVICE, + portno, 0, port->read_buffer, size, 1000); + + packet_count = (u16 *)port->read_buffer; + if(ret < 0) + skw_port_log(portno,"%s (%d,%d) ret = %d\n", __func__, portno, *packet_count, ret); + if(ret==size) + port->max_packet_count = *packet_count; + else + port->max_packet_count = MAX_PACKET_COUNT; + } + return ret; +} + +void skw_usb_cp_log(int disable) +{ + struct usb_port_struct *port; + int ret = -1; + port = usb_ports[0]; + if(port && port->state) { + ret = usb_control_msg(port->udev, usb_rcvctrlpipe(port->udev, 0), + VENDOR_MSG_LOG_SWITCH, USB_DIR_IN| USB_TYPE_VENDOR|USB_RECIP_DEVICE, + disable, 0, NULL, 0, 10); + + skw_usb_info("(disable=%d) ret = %d\n", disable, ret); + } + cp_log_status = disable; +} +/************************************************************************ + *Decription:send BT start command to modem. + *Author:jiayong.yang + *Date:2021-08-30 + *Modfiy: + * + ********************************************************************* */ +static int skw_BT_service_start(void) +{ + u16 cmd = SERVICE_START; + + if (!wifi_data_pdev) + return -ENODEV; + + skw_usb_info("Enter modem_status=%d\n", modem_status); + if (service_state_map & (1<<BT_SERVICE)) + return 0; + +#if defined(CONFIG_SEEKWAVE_PLD_RELEASE) && defined(MODEM_WDT_SUPPORT) + if (chip_en_gpio < 0){ + cmd |= MODEM_WDT_SUPPORT; + } +#endif + return send_modem_service_command(BT_SERVICE, cmd); +} + +/************************************************************************ + *Decription:send BT stop command to modem. + *Author:jiayong.yang + *Date:2021-08-30 + *Modfiy: + * + ********************************************************************* */ +static int skw_BT_service_stop(void) +{ + skw_usb_info("Enter modem_status=%d\n", modem_status); + if (!wifi_data_pdev) + return -ENODEV; + + if (service_state_map & (1<<BT_SERVICE)){ + return send_modem_service_command(BT_SERVICE, SERVICE_STOP); + } + return 0; +} +/************************************************************************ + *Decription:send WIFI start command to modem. + *Author:jiayong.yang + *Date:2021-08-30 + *Modfiy: + * + ********************************************************************* */ +static int skw_WIFI_service_start(void) +{ + int count=90; + u16 cmd = SERVICE_START; + skw_usb_info("Enter STARTWIFI---modem_status=%d, 0x%x\n", + modem_status, service_state_map); + if (modem_status == MODEM_HALT) { + while(!usb_ports[1] && count--) + msleep(10); + } + if (service_state_map & (1<<WIFI_SERVICE)) + return 0; + cmd |= WIFI_PORT_SHARE_FLAG; +#if defined(CONFIG_SEEKWAVE_PLD_RELEASE) && defined(MODEM_WDT_SUPPORT) + if (chip_en_gpio < 0){ + cmd |= MODEM_WDT_SUPPORT; + } +#endif + return send_modem_service_command(WIFI_SERVICE, cmd); +} + +/************************************************************************ + *Decription: send WIFI stop command to modem. + *Author:jiayong.yang + *Date:2021-08-30 + *Modfiy: + * + ********************************************************************* */ +static int skw_WIFI_service_stop(void) +{ + int count=10; + int portno; + struct usb_port_struct *port; + + skw_usb_info("Enter,STOPWIFI--- modem status %d, 0x%x\n", + modem_status, service_state_map); + for (portno=0; portno<2; portno++) { + port = usb_ports[portno]; + if (port && port->write_urb && port->write_urb->context) { + usb_kill_anchored_urbs(&port->write_submitted); + } else if (port && port->tx_urb_count) { + usb_kill_anchored_urbs(&port->write_submitted); + } + } + if (modem_status == MODEM_HALT) { + service_state_map &= ~(1<<WIFI_SERVICE); + while(!usb_ports[1] && count--) + msleep(10); + return 0; + } + if (service_state_map & (1<<WIFI_SERVICE)) + return send_modem_service_command(WIFI_SERVICE, SERVICE_STOP); + return 0; +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-08-30 + *Modfiy: + * + ********************************************************************* */ +static void bulkin_complete(struct urb *urb) +{ + struct usb_port_struct *port; + int portno; + + if(!urb) + return; + + portno = usb_pipeendpoint(urb->pipe) - 1; + port = usb_ports[portno]; + port->rx_done_time = jiffies; + port->rx_packet_count++; + if(urb) { + if(urb->status) { + skw_usb_info("endpoint%d actual = %d status %d\n", + usb_pipeendpoint(urb->pipe), urb->actual_length, urb->status); + } + if(urb->status == -ENOENT && port && port!=log_port && port->suspend) + list_add_tail(&urb->urb_list, &port->suspend_urb_list); + else if (urb->context) + complete(urb->context); + } +} +static void bulkin_async_complete(struct urb *urb) +{ + struct usb_port_struct *port; + + if(!urb) + return; + port = urb->context; + if(!port) + return; + port->rx_done_time = jiffies; + bulk_async_read--; + if(urb->status) { + skw_usb_info("endpoint%d actual = %d status %d\n", + usb_pipeendpoint(urb->pipe), urb->actual_length, urb->status); + } + if(urb->status == -ENOENT && port && port->suspend) + list_add_tail(&urb->urb_list, &port->suspend_urb_list); + else if (port) { + urb->context = NULL; + spin_lock(&port->rx_urb_lock); + if (port->state) + list_add_tail(&urb->urb_list, &port->rx_done_urb_list); + else + list_add_tail(&urb->urb_list, &port->rx_urb_list); + spin_unlock(&port->rx_urb_lock); + if (port->state) + tasklet_hi_schedule(&port->tasklet); + } +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static void bulkout_complete(struct urb *urb) +{ + struct usb_port_struct *port; + int portno; + + portno = usb_pipeendpoint(urb->pipe) - 1; + port = usb_ports[portno]; + + if(urb->status) + skw_usb_info("endpoint%d actual = %d status %d\n", + usb_pipeendpoint(urb->pipe), urb->actual_length, urb->status); + + if (port) { + port->tx_done_time = jiffies; + port->sent_packet_count++; + } + if (urb->context) + complete(urb->context); +} + +static void bulkout_async_complete(struct urb *urb) +{ + struct usb_port_struct *port = urb->context; + + if(urb->status) { + port->sent_packet_count += urb->num_sgs; + if(urb->sg && port->adma_tx_callback) + port->adma_tx_callback(port->portno, urb->sg, urb->num_sgs, port->tx_data, urb->status); + else if(urb->transfer_buffer && port->sdma_tx_callback) + port->sdma_tx_callback(port->portno, urb->transfer_buffer, urb->transfer_buffer_length, port->tx_data, urb->status); + skw_usb_info("port%d endpoint%d actual = %d status %d\n", + port->portno, usb_pipeendpoint(urb->pipe), urb->actual_length, urb->status); + } else if(urb->sg && port->adma_tx_callback) { + port->adma_tx_callback(port->portno, urb->sg, urb->num_sgs, port->tx_data, 0); + port->sent_packet_count += urb->num_sgs; + } else if(urb->transfer_buffer && port->sdma_tx_callback) + port->sdma_tx_callback(port->portno, urb->transfer_buffer, urb->transfer_buffer_length, port->tx_data, 0); + urb->context = NULL; + port->tx_done_time = jiffies; + spin_lock(&port->tx_urb_lock); + list_add_tail(&urb->urb_list, &port->tx_urb_list); + port->tx_urb_count--; + if(port->tx_urb_count==0 && port->sent_packet_count!=port->req_tx_packet) + skw_usb_info(" port[%d]= %d %d\n", port->portno, port->sent_packet_count, port->req_tx_packet); + spin_unlock(&port->tx_urb_lock); + wake_up_interruptible(&port->tx_wait); +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +int bulkin_read_timeout(int portno, char *buffer, int size, int *actual, int timeout) +{ + struct usb_port_struct *port; + unsigned int pipe; + int ret; + + if(portno >= MAX_USB_PORT || !buffer || !size) + return -EINVAL; + port = usb_ports[portno]; + if(!port->state) + return -EIO; + if(actual) + *actual = 0; + pipe = usb_rcvbulkpipe(port->udev, port->epin->bEndpointAddress); + ret = usb_bulk_msg(port->udev, pipe, buffer, size, actual,timeout); + + if(port == log_port && actual) { + if(assert_info_print && assert_info_print<28 && *actual<100) { + assert_info_print++; + if(*actual > 4) + skw_usb_info("%s", (char *)buffer); + } + if(*actual == 4) + assert_info_print = 28; + } + if(ret) + return ret; + + if(actual) + return *actual; + return ret; +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +int bulkout_write_timeout(int portno, char *buffer, int size, int *actual, int timeout) +{ + struct usb_port_struct *port; + unsigned int pipe; + int ret; + + if(portno >= MAX_USB_PORT || !buffer || !size) + return -EINVAL; + port = usb_ports[portno]; + + if(!port->state) + return -EIO; + if(actual) + *actual = 0; + pipe = usb_sndbulkpipe(port->udev, port->epout->bEndpointAddress); + ret = usb_bulk_msg(port->udev, pipe, buffer, size, actual,timeout); + if(ret) + return ret; + if(actual) + return *actual; + return ret; +} + +static void kick_rx_thread(void) +{ + struct usb_port_struct *port; + + skw_usb_info("submitted urb %d\n", bulk_async_read); + port = usb_ports[1]; + if ((bulk_async_read == 0) && port && + (!list_empty(&port->rx_urb_list))) + bulkin_read_async(port); + else if (port && list_empty(&port->rx_urb_list)) + skw_usb_info("urb list is empty \n"); +} +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int register_rx_callback(int id, void *func, void *para); +static int register_tx_callback(int id, void *func, void *para); +static struct sv6160_platform_data wifi_pdata = { + .data_port = 0, + .cmd_port = 1, +#ifdef CONFIG_SEEKWAVE_PLD_RELEASE + .bus_type = USB_LINK|TX_SDMA|RX_SDMA|TX_ASYN|CP_RLS, +#else + .bus_type = USB_LINK|TX_SDMA|RX_SDMA|TX_ASYN|CP_DBG, +#endif + .max_buffer_size = MAX_BUFFER_SIZE, + .align_value = 512, + .hw_adma_tx = wifi_send_cmd, + .hw_sdma_tx = send_data, + .hw_adma_tx_async = wifi_send_cmd_async, + .hw_sdma_tx_async = send_data_async, + .callback_register = register_rx_callback, + .modem_assert = modem_assert, + .service_start = wifi_service_start, + .service_stop = wifi_service_stop, + .modem_register_notify = modem_register_notify, + .modem_unregister_notify = modem_unregister_notify, + .at_ops = { + .port = 2, + .open = open_usb_port, + .close = close_usb_port, + .read = recv_data, + .write = send_data, + .read_tm = bulkin_read_timeout, + .write_tm = bulkout_write_timeout, + }, + .tx_callback_register = register_tx_callback, + .rx_thread_wakeup = kick_rx_thread, +}; + +void usb_handle(unsigned long tsk_data) +{ + int size, read, ret; + int transfer_count = 0, sg_count, offset; + u16 data_flag = 0x8000; + unsigned long flags; + char *buffer; + int *data; + struct usb_port_struct *port = (struct usb_port_struct *) tsk_data; + struct scatterlist *sg; + struct urb *urb; + u16 pcount, sequence; + + if (!strncmp(skw_chipid, "SV6316", 6) || !strlen(skw_chipid) + || !strncmp(skw_chipid, "SV6160LITE", 10)) + data_flag = 2; + + while(!list_empty(&port->rx_done_urb_list)) { + + if (!port->state || port!=usb_ports[1]) + break; + spin_lock_irqsave(&port->rx_urb_lock, flags); + urb = list_first_entry(&port->rx_done_urb_list, struct urb, urb_list); + list_del_init(&urb->urb_list); + list_add_tail(&urb->urb_list, &port->rx_urb_list); + spin_unlock_irqrestore(&port->rx_urb_lock, flags); + + sg_init_table(sgs, nr_sgs); + read = urb->actual_length; + buffer = urb->transfer_buffer; + transfer_count++; + if(urb->status < 0 || !port->state) { + skw_usb_err(" bulkin read status=%d state=%d\n", urb->status, port->state); + return ; + } + if(port->rx_submit) { + int is_cmd; + u32 d32; + + data = (int *)buffer; + d32 = data[0]; + sequence = d32 & 0xffff; + pcount = d32 >> 16; + pcount &= 0x7fff; + offset = 0; + sg_count = 0; + sg = sgs; + is_cmd = 0; + if (d32 & 0x80000000) { + if (transfer_index != sequence) + skw_usb_info("data lost: recved %d, expect:%d\n", transfer_index,sequence); + transfer_index = sequence + 1; + } + while (offset+12 < read) { + sg_count++; + if(sg_count > nr_sgs) { + skw_usb_warn("packet count is overflow %d : %d : %d : %d!!!\n", + offset, read, sg_count, nr_sgs); + sg_count--; + break; + } + size = data[2] >> 16; + size += 3; + size = size & 0xfffffffc; + if(data[2] & data_flag) { + if (sg_count > 1 && !is_cmd) + size = -1; + else + is_cmd = 1; + } + if (size + offset > read || size > 2048 || size <= 12) { + skw_usb_warn("Invalid packet size=%d: %d : %d :%d 0x%x:0x%x!!!\n", + size, offset, read, sg_count, d32, data[2]); + if (cls_recovery_mode_en) { + print_hex_dump(KERN_ERR, "PACKET1::", 0, 16, 1, + urb->transfer_buffer, offset+12, 1); + modem_assert(); + } + if (sg_count > 0) + sg_count--; + break; + } + sg_set_buf(sg, &buffer[offset], size); + sg++; + offset += size; + if (is_cmd) { + if (modem_status != MODEM_ON) + skw_usb_info("rx_submit(0x%x): command: 0x%x 0x%x: 0x%x 0x%x readlen=%d\n", (u32)jiffies, + data[2], data[3], last_recv_wifi_ack[1], last_recv_wifi_ack[2], read); + if ((data[3] & 0xff) == 0x10) { + last_ack_time = jiffies; + memcpy(last_recv_wifi_ack, &data[1], 12); + } else + memcpy(last_recv_wifi_evt, &data[1], 12); + } + data = (int *)&buffer[offset]; + } + if (d32 & 0x80000000) { + if (sg_count != pcount) + skw_usb_info("packet lost:%d %d\n", sg_count, pcount); + } + if(sg_count >15) + skw_usb_info("rx_submit: port%d packet count %d\n", + port->portno, sg_count); + if(is_cmd) + port = usb_ports[wifi_pdata.cmd_port]; + else + port = usb_ports[wifi_pdata.data_port]; + if (port->rx_submit) + port->rx_submit(port->portno, sgs, sg_count, port->rx_data); + port->rx_packet_count += sg_count; + if (modem_status != MODEM_ON) + return ; + port = usb_ports[wifi_pdata.data_port]; + } + } + + while(!list_empty(&port->rx_urb_list)) { + if (port->state==0) + break; + ret = bulkin_read_async(port); + } +} + +/********************************************************************** + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + **********************************************************************/ +int usb_port_async_entry(void *para) +{ + struct usb_port_struct *port = para; + struct sched_param param; + unsigned long flags; + //int size, read, ret; + int ret; + u16 mpc; + struct urb *urb; + u16 data_flag = 0x8000; + + if(port->portno == 0) { + param.sched_priority = USB_RX_TASK_PRIO; +#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE + sched_set_fifo_low(current); +#else + sched_setscheduler(current, SCHED_FIFO, ¶m); +#endif + } + if(port->max_packet_count) + mpc = port->max_packet_count; + else + mpc = 2; + + if (!strncmp(skw_chipid, "SV6316", 6) || !strlen(skw_chipid) + || !strncmp(skw_chipid, "SV6160LITE", 10)) + data_flag = 2; + + nr_sgs = mpc+1; + sgs = kzalloc((nr_sgs)*sizeof(struct scatterlist), GFP_KERNEL); + if (!sgs) + return -ENOMEM; + bulk_async_read = 0; + if (mpc<=13) + usb_port_alloc_recv_urbs(port, port->epin, 3, 20*1024); + else + usb_port_alloc_recv_urbs(port, port->epin, 3, 24*1024); + usb_port_alloc_xmit_urbs(port, port->epout,10,0); + msleep(300); + transfer_index = 0; + skw_usb_info(" port %d running packet %d %s 0x%x...\n",port->portno, mpc, skw_chipid, data_flag); + if (!list_empty(&port->rx_urb_list)) { + ret = bulkin_read_async(port); + } + + wait_event_interruptible(port->rx_wait, (!port->state)); + skw_usb_info(" port %d stoped\n", port->portno); + msleep(50); + kfree(sgs); + + if(port->write_urb) { + usb_kill_anchored_urbs(&port->write_submitted); + } + if(port->read_urb) { + usb_kill_anchored_urbs(&port->read_submitted); + } + + if(port->write_urb && port->write_urb->context) + wait_for_completion_interruptible(port->write_urb->context); + + spin_lock_irqsave(&port->rx_urb_lock, flags); + while(!list_empty(&port->rx_urb_list)) { + urb = list_first_entry(&port->rx_urb_list, struct urb, urb_list); + list_del_init(&urb->urb_list); + if(urb->transfer_buffer) + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + spin_unlock_irqrestore(&port->rx_urb_lock, flags); + while(!list_empty(&port->tx_urb_list)) { + urb = list_first_entry(&port->tx_urb_list, struct urb, urb_list); + list_del_init(&urb->urb_list); + usb_free_urb(urb); + } + up(&port->sem); + return 0; +} + +static void skw_usb_kill_wifi_threads(struct usb_port_struct *p) +{ + int i; + struct usb_port_struct *port; + for(i=0; i<3; i++) { + port = usb_ports[i]; + if(port==NULL) + break; + if(port && port->thread) { + port->state = 0; + } + } +} +static void skw_usb_dump_memory(char *buffer, int size, int *log_size) +{ + if (log_port->state==2) + return; + if (size && buffer && log_size) { + dump_memory_buffer = buffer; + dump_buffer_size = size; + dump_log_size = log_size; + skw_usb_info("dump_memory : %p-%d\n", buffer, size); + schedule_work(&dump_memory_worker); + } +} + +static void show_assert_context(void) +{ + int read; + int error_count; + int total_size; + int dump_memory_size = 0; + + if(log_port && log_port->state!=2) { + char *buffer; + buffer = kzalloc(1024, GFP_KERNEL); + if (!buffer) + return; + open_usb_port(log_port->portno, 0, 0); + dump_memory_done = 0; + error_count=0; + total_size = 0; + do { + if (modem_status == MODEM_ON) + break; + if (!dump_memory_done) + read = bulkin_read_timeout(log_port->portno, buffer, 1024, &read, 10); + if (read > 0) { + if (total_size + read < dump_buffer_size) { + memcpy(&dump_memory_buffer[total_size], buffer, read); + dump_memory_size = total_size + read; + } + total_size += read; + memset(buffer, 0, read); + } + if(read == 4 || read < 0) { + break; + } + } while (assert_info_print<100 && !dump_memory_done); + while (!dump_memory_done) { + if (modem_status == MODEM_ON) + break; + read = bulkin_read_timeout(log_port->portno, buffer, 1024, &read, 10); + if (read <= 0) { + error_count++; + skw_usb_info("%s read = %d : total %d done=%d\n", current->comm, read, total_size, dump_memory_done); + if(error_count >1) + break; + } else { + if (total_size + read < dump_buffer_size) { + memcpy(&dump_memory_buffer[total_size], buffer, read); + dump_memory_size = total_size + read; + } + total_size += read; + } + } + close_usb_port(log_port->portno); + skw_usb_info("dump memory size: %d buffer_size: %d\n", dump_memory_size, dump_buffer_size); + if (dump_log_size) + *dump_log_size = dump_memory_size; + kfree(buffer); + } +} +static void dump_memory_work(struct work_struct *work) +{ + if(dump_log_size && *dump_log_size==0) { + skw_usb_info(" running...\n"); + show_assert_context(); + } +} + +int skw_pin_config(struct usb_port_struct *port, char *buffer) +{ + int i; + u32 val; + int func_index = 0; + int g_offset = 0; + u32 pin_group_index = 0; + u32 func_group_index = 0; + u32 func_sel_val[2] = {0}; + u32 func_sel_off[] = {PN_FUNC_SEL0_OFFSET, PN_FUNC_SEL1_OFFSET}; + u32 *pin_val; + u8 pin_off[PN_CNT] = {0}; + char value_str[9]; + char result_str[15]; //e.g. PN:10:12345678 + char off_str[3]; + + pin_val = (u32 *)kzalloc(PN_CNT * sizeof(u32), GFP_KERNEL); + + while (g_offset < usb_boot_data->nv_mem_pnfg_size) { + //1. pin offset + pin_off[pin_group_index] = usb_boot_data->nv_mem_pnfg_data[g_offset]; + + //2. func_sel value + val = usb_boot_data->nv_mem_pnfg_data[g_offset + 1]; + func_sel_val[func_group_index] |= (val << (3 * func_index)) & (7 << (3 * func_index)); + + //3. pin config value + pin_val[pin_group_index] |= + ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 2] << BIT_PN_DSLP_EN_START) & \ + GENMASK(BIT_PN_DSLP_EN_END, BIT_PN_DSLP_EN_START);//dslp_en + pin_val[pin_group_index] |= + ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 3] << BIT_DRV_STREN_START) & \ + GENMASK(BIT_DRV_STREN_END, BIT_DRV_STREN_START);//drv_strength + pin_val[pin_group_index] |= + ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 4] << BIT_NORMAL_WP_START) & \ + GENMASK(BIT_NORMAL_WP_END, BIT_NORMAL_WP_START);//normal:wpu/wpd + pin_val[pin_group_index] |= + ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 5] << BIT_SCHMITT_START) & \ + GENMASK(BIT_SCHMITT_END, BIT_SCHMITT_START);//schmitt trigger enable + pin_val[pin_group_index] |= + ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 6] << BIT_SLP_WP_START) & \ + GENMASK(BIT_SLP_WP_END, BIT_SLP_WP_START);//sleep:wpu/wpd + pin_val[pin_group_index] |= + ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 7]) & \ + GENMASK(BIT_SLEEP_IE_OE_END, BIT_SLEEP_IE_OE_START);//sleep:ie/oe + + g_offset += 8; // 1(offset) + 1(pin sel) + 6(pin config) + func_index++; + pin_group_index++; + if (func_index == PN_FUNCSEL_ONEGRP_CNT){ + func_group_index++; + func_index = 0; + } + } + + //function select + for (i = 0;i < sizeof(func_sel_off) / sizeof(u32);i++) { + sprintf(value_str, "%08x", func_sel_val[i]); + sprintf(off_str, "%02x", func_sel_off[i]); + sprintf(result_str, "PN:%s:%s", off_str, value_str); + skw_usb_dbg("func_sel[%d]:%s\n", i, result_str); + memcpy(buffer+256, result_str, 14); + bulkout_write(port, buffer+256, 14); + } + + //pin config + for (i = 0;i < PN_CNT;i++) { + sprintf(value_str, "%08x", pin_val[i]); + sprintf(off_str, "%02x", pin_off[i]); + sprintf(result_str, "PN:%s:%s", off_str, value_str); + skw_usb_dbg("pin_conf[%d]:%s\n", i, result_str); + memcpy(buffer+256, result_str, 14); + bulkout_write(port, buffer+256, 14); + } + kfree(pin_val); + + return 0; +} + +static int usb_loopcheck_entry(void *para) +{ + struct usb_port_struct *port = para; + char *buffer; + int read, size; + int count= 0, timeout=300; + struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); + size = 512; + buffer = kzalloc(size, GFP_KERNEL); + schedule_delayed_work(&skw_except_work , msecs_to_jiffies(6000)); + while(port->state && buffer){ + read = 0; + memset(buffer,0,512); + do{ + if(port->state==0) + break; + read = bulkin_read(port, buffer, 256); + }while(!read); + + if (port->suspend) { + msleep(500); + continue; + } + if(read < 0 || !port->state) { + skw_usb_err("bulkin read_len=%d\n",read); + break; + } + if(strncmp(buffer, "BSPREADY", read)) + skw_usb_info("recv(%d):%s %s\n", read, skw_chipid, buffer); + memcpy(buffer+256, "LOOPCHECK", 9); + if (read==8 && !strncmp(buffer, "BSPREADY", read)) { + if (start_service_flag) + continue; + bulkout_write(port, buffer+256, 9); + //bulkout_write_timeout(port->portno, buffer+256,9, &size, 300); + } else if (read==9 && !strncmp(buffer, "WIFIREADY", read)) { + start_service_flag = 0; + service_state_map |= (1<<WIFI_SERVICE); + complete(&download_done); + bulkout_write(port, buffer+256, 9); + //bulkout_write_timeout(port->portno, buffer+256,9, &size, 300); + } else if (read==6 && !strncmp(buffer, "BTEXIT", read)) { + complete(&download_done); + //bulkout_write(port, buffer+256, 9); + bulkout_write_timeout(port->portno, buffer+256,9, &size, 300); + } else if (read==7 && !strncmp(buffer, "BTREADY", read)) { + start_service_flag = 0; + service_state_map |= (1<<BT_SERVICE); + complete(&download_done); + bulkout_write(port, buffer+256, 9); + //bulkout_write_timeout(port->portno, buffer+256,9, &size, 300); + } else if (!strncmp(buffer, "BSPASSERT", 9)) { + sprintf(firmware_version, "%s\n%s\n", firmware_version, buffer); + skw_usb_err("cmd:0x%x 0x%x 0x%x ack:%x %x:%x event:0x%x:0x%x:0x%x time:0x%x:0x%x:0x%x\n", + last_sent_wifi_cmd[0],last_sent_wifi_cmd[1],last_sent_wifi_cmd[2], + last_recv_wifi_ack[0],last_recv_wifi_ack[1],last_recv_wifi_ack[2], + last_recv_wifi_evt[0],last_recv_wifi_evt[1],last_recv_wifi_evt[2], + (u32)jiffies,(u32)last_sent_time, (u32)last_ack_time); + if(recovery->cp_state==1) + cancel_delayed_work_sync(&skw_except_work); + + mutex_lock(&recovery->except_mutex); + if(recovery->cp_state==DEVICE_BLOCKED_EVENT){ + mutex_unlock(&recovery->except_mutex); + break; + } + recovery->cp_state = 1; + mutex_unlock(&recovery->except_mutex); + + assert_info_print = 1; + memset(buffer, 0, read); + skw_usb_kill_wifi_threads(port); + modem_status = MODEM_HALT; + modem_notify_event(DEVICE_ASSERT_EVENT); + if (log_port->state!=2) + schedule_work(&dump_memory_worker); + memset(buffer, 0, 256); + read = bulkin_read_timeout(port->portno, buffer, 256, &read, 1000); + if (read > 0) + skw_usb_info("bspassert after recv(%d): %s\n", read, buffer); + dump_memory_done = 1; + modem_notify_event(DEVICE_DUMPDONE_EVENT); + msleep(100); + skw_recovery_mode(); + service_state_map =0; + + break; + } else if (!strncmp("trunk_W", buffer, 7)) { +#ifdef CONFIG_SKW_DL_TIME_STATS + last_time = ktime_get(); + skw_usb_info(",the download time start time %llu and lasttime %llu ,lose_time=%llu\n", + cur_time, last_time,(last_time-cur_time)); +#endif + cancel_delayed_work_sync(&skw_except_work); + recovery->cp_state = 0; + assert_info_print = 0; + modem_status = MODEM_ON; + memset(firmware_version, 0 , sizeof(firmware_version)); + strncpy(firmware_version, buffer, read); + + modem_notify_event(DEVICE_BSPREADY_EVENT); + count = 0; + service_state_map =0; + //usb_setup_service_devices(); + schedule_work(&add_device_work); + bulkout_write_timeout(port->portno, buffer+256,9, &size, 300); + if (usb_boot_data->nv_mem_pnfg_data != NULL && usb_boot_data->nv_mem_pnfg_size != 0) { + skw_usb_info("UPDATE '%s' PINCFG from %s\n", skw_chipid, usb_boot_data->skw_nv_name); + skw_pin_config(port, buffer); + } + } + wait_for_completion_interruptible_timeout(&loop_completion, msecs_to_jiffies(timeout)); + skw_reinit_completion(loop_completion); + } + skw_usb_info(" -port%d is stopped\n", port->portno); + if(port->read_urb && port->read_urb->context) { + usb_kill_anchored_urbs(&port->read_submitted); + } + + if(port->write_urb && port->write_urb->context) { + usb_kill_anchored_urbs(&port->write_submitted); + if(port->write_urb->context) + wait_for_completion_interruptible(port->write_urb->context); + } + kfree(buffer); + up(&port->sem); + return 0; +} + +static int usb_bt_rx_entry(void *para) +{ + struct usb_port_struct *port = para; + char *buffer; + int read, size; + + size = 2048; + buffer = kzalloc(size, GFP_KERNEL); + while(port->state==2 && buffer){ + read = 0; + memset(buffer,0,size); + do{ + if(port->state != 2) + break; + read = bulkin_read(port, buffer, size); + }while(!read); + + if(read < 0) { + skw_usb_err("bulkin read_len=%d\n",read); + break; + } + if(port->rx_submit) + port->rx_submit(port->portno, port->rx_data, read, buffer); + } + skw_usb_info("-port%d is stopped\n", port->portno); + if(port->write_urb && port->write_urb->context) { + usb_kill_anchored_urbs(&port->write_submitted); + } + if(port->read_urb && port->read_urb->context) { + usb_kill_anchored_urbs(&port->read_submitted); + } + + if(buffer) + kfree(buffer); + up(&port->sem); + return 0; +} + + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static struct sv6160_platform_data ucom_pdata = { + .max_buffer_size =0x800, + .bus_type = USB_LINK, + .hw_sdma_tx = send_data, + .hw_sdma_rx = recv_data, + .open_port = open_usb_port, + .close_port = close_usb_port, + .modem_assert = modem_assert, + .service_start = bt_service_start, + .service_stop = bt_service_stop, + .modem_register_notify = modem_register_notify, + .modem_unregister_notify = modem_unregister_notify, + .dump_modem_memory = skw_usb_dump_memory, +}; + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ + +static int register_rx_callback(int id, void *func, void *para) +{ + if(id >= MAX_USB_PORT) + return -EINVAL; + + if(usb_ports[id] == NULL) + return -EIO; + if(func && !usb_ports[id]->rx_submit) { + usb_ports[id]->rx_submit = func; + usb_ports[id]->rx_data = para; + if(id==1) + skw_WIFI_service_start(); + return 0; + } else if(!func && usb_ports[id]->rx_submit) { + if(id==1) + skw_WIFI_service_stop(); + + usb_ports[id]->rx_submit = func; + usb_ports[id]->rx_data = para; + return 0; + } + if(wifi_pdata.bus_type & TX_ASYN) { + if(wifi_pdata.bus_type & TX_SDMA) + usb_ports[id]->sdma_tx_callback = func; + else + usb_ports[id]->adma_tx_callback = func; + } + usb_ports[id]->tx_data = para; + return 0; +} + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int register_tx_callback(int id, void *func, void *para) +{ + if(id >= MAX_USB_PORT) + return -EINVAL; + + if(usb_ports[id] == NULL) + return -EIO; + if(wifi_pdata.bus_type & TX_ASYN) { + if(wifi_pdata.bus_type & TX_SDMA) + usb_ports[id]->sdma_tx_callback = func; + else + usb_ports[id]->adma_tx_callback = func; + } + usb_ports[id]->tx_data = para; + return 0; +} + + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int skw_usb_io_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); + struct usb_port_struct *port; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *epd; + struct platform_device *pdev; + struct usb_device *udev = interface_to_usbdev(interface); + char pdev_name[32], names[32]; + int i, ret, dloader=0; + + memset(names, 0 ,sizeof(names)); + iface_desc = interface->cur_altsetting; + if (iface_desc->string == NULL) + return -EINVAL; + sprintf(names, "%s", iface_desc->string); + + if (!strncmp(names, "Boot", 4)) + dloader = 1; + +#ifndef CONFIG_SV6160_LITE_FPGA + if ((udev->descriptor.idProduct != 0x6316) && !dloader + && chip_en_gpio < 0 && modem_status == MODEM_OFF) { +#ifndef MODEM_WDT_SUPPORT + return -EINVAL; +#endif + } + if (chip_en_gpio < 0) { + skw_usb_warn(" descriptor.idProduct = 0x%x CHIP_ID= %.100s\n", + udev->descriptor.idProduct,udev->product); + } +#endif + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + mutex_lock(&recovery->except_mutex); + if (usb_bus_num == 0xff && usb_port_num == 0xff) { + skw_usb_info("bus[%x].port[%d]\n", udev->bus->busnum, udev->portnum); + usb_bus_num = udev->bus->busnum; + usb_port_num = udev->portnum; + } else if (usb_bus_num != udev->bus->busnum || usb_port_num != udev->portnum) { + mutex_unlock(&recovery->except_mutex); + if (port) + kfree(port); + return -EBUSY; + } + mutex_unlock(&recovery->except_mutex); + + pdev = NULL; + if (!strncmp(names, "WIFITCMD", 8)) + wifi_port_share = 1; + usb_ports[iface_desc->desc.bInterfaceNumber] = port; + INIT_LIST_HEAD(&port->rx_urb_list); + INIT_LIST_HEAD(&port->tx_urb_list); + INIT_LIST_HEAD(&port->rx_done_urb_list); + INIT_LIST_HEAD(&port->suspend_urb_list); + spin_lock_init(&port->rx_urb_lock); + spin_lock_init(&port->tx_urb_lock); + port->tx_urb_count = 0; + init_waitqueue_head(&port->rx_wait); + init_waitqueue_head(&port->tx_wait); + if(dloader) + dloader = 1; + else if (iface_desc->desc.bInterfaceNumber == 1) { + if (!strncmp(skw_chipid, "SV6160LITE", 10)) { +#ifdef SV6621S_WIRELESS + sprintf(pdev_name, "%s%d", SV6621S_WIRELESS, + iface_desc->desc.bInterfaceNumber); +#else + sprintf(pdev_name, "%s%d", SV6160_WIRELESS, + iface_desc->desc.bInterfaceNumber); +#endif + } else if (!strncmp(skw_chipid, "SV6160", 6)) { + sprintf(pdev_name, "%s%d", SV6160_WIRELESS, + iface_desc->desc.bInterfaceNumber); + } else if (!strncmp(skw_chipid, "SV6316", 6)) { + sprintf(pdev_name, "%s%d", SV6316_WIRELESS, + iface_desc->desc.bInterfaceNumber); + } else { + skw_usb_err( + "unknow chip id!!! pls check you porting code!! and the connect the seekwave!!\n"); + if (skw_chipid) + skw_usb_err("unknow chip id: %s\n", skw_chipid); + + sprintf(pdev_name, "%s%d", SV6316_WIRELESS, + iface_desc->desc.bInterfaceNumber); + } + if(!wifi_data_pdev) + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + else + pdev = wifi_data_pdev; + if(!pdev) + return -ENOMEM; + } else { +#ifdef CONFIG_BT_SEEKWAVE + if (!strncmp(names, "DATA", 4)) { + ucom_pdata.data_port = 0; + } else if(!strncmp(names, "BTDATA", 6)) + ucom_pdata.data_port = iface_desc->desc.bInterfaceNumber; + else if(!strncmp(names, "BTCMD", 5)) + ucom_pdata.cmd_port = iface_desc->desc.bInterfaceNumber; + else if(!strncmp(names, "BTISOC", 6)) { + ucom_pdata.audio_port = iface_desc->desc.bInterfaceNumber; + sprintf(pdev_name, "%s", "btseekwave"); + ucom_pdata.port_name = "BTHCI"; + bluetooth_pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if (!bluetooth_pdev) + return -ENOMEM; + bluetooth_pdev->dev.parent = &udev->dev; + bluetooth_pdev->dev.dma_mask = &port_dmamask; + bluetooth_pdev->dev.coherent_dma_mask = port_dmamask; + bt_audio_port = iface_desc->desc.bInterfaceNumber; + memcpy(ucom_pdata.chipid, skw_chipid, SKW_CHIP_ID_LENGTH); + ret = platform_device_add_data(bluetooth_pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + skw_usb_err("failed to add platform data \n"); + platform_device_put(pdev); + kfree(port); + return ret; + } + skw_usb_info("add the bt devices \n"); + }else if(!strncmp(names, "AUDIO", 5)) { + ucom_pdata.audio_port = 0; + sprintf(pdev_name, "%s", "btseekwave"); + ucom_pdata.port_name = "BTHCI"; + bluetooth_pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if (!bluetooth_pdev) + return -ENOMEM; + bluetooth_pdev->dev.parent = &udev->dev; + bluetooth_pdev->dev.dma_mask = &port_dmamask; + bluetooth_pdev->dev.coherent_dma_mask = port_dmamask; + bt_audio_port = iface_desc->desc.bInterfaceNumber; + memcpy(ucom_pdata.chipid, skw_chipid, SKW_CHIP_ID_LENGTH); + ret = platform_device_add_data(bluetooth_pdev, &ucom_pdata, sizeof(ucom_pdata)); + if(ret) { + skw_usb_err("failed to add platform data \n"); + platform_device_put(pdev); + kfree(port); + return ret; + } + } else +#endif + if (iface_desc->desc.bInterfaceNumber && strncmp(names, "LOOP", 4)) { + sprintf(pdev_name, "%s", "skw_ucom"); + ucom_pdata.port_name = iface_desc->string; + ucom_pdata.data_port = iface_desc->desc.bInterfaceNumber; + memcpy(ucom_pdata.chipid, skw_chipid, SKW_CHIP_ID_LENGTH); + pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); + if(!pdev) + return -ENOMEM; + } + } + if(!dloader) { + if (1==iface_desc->desc.bInterfaceNumber && wifi_data_pdev) { + struct sv6160_platform_data *pdata; + pdev = wifi_data_pdev; + //pdev->dev.parent = NULL; + pdata = pdev->dev.platform_data; + if (pdata) { + pdata->align_value = iface_desc->endpoint[0].desc.wMaxPacketSize; + wifi_pdata.align_value = iface_desc->endpoint[0].desc.wMaxPacketSize; + } + port->pdev = pdev; + } else if (iface_desc->desc.bInterfaceNumber && pdev) { + if (1==iface_desc->desc.bInterfaceNumber && + usb_boot_data && usb_boot_data->pdev) { + pdev->dev.parent = &usb_boot_data->pdev->dev; + } else + pdev->dev.parent = &udev->dev; + pdev->dev.dma_mask = &port_dmamask; + pdev->dev.coherent_dma_mask = port_dmamask; + + if(iface_desc->desc.bInterfaceNumber == 1) { + wifi_pdata.align_value = iface_desc->endpoint[0].desc.wMaxPacketSize; + if(usb_boot_data && usb_boot_data->iram_dl_size >0x50000) + wifi_pdata.at_ops.port = 4; + else + wifi_pdata.at_ops.port = 2; + if(udev->config->string && !strncmp(udev->config->string, "ECOM", 4)) { + wifi_pdata.bus_type &= ~TYPE_MASK; + wifi_pdata.bus_type |= USB2_LINK; + } + ret = platform_device_add_data(pdev, &wifi_pdata, sizeof(wifi_pdata)); + modem_status = MODEM_ON; + } else{ + memcpy(ucom_pdata.chipid, skw_chipid, SKW_CHIP_ID_LENGTH); + ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); + } + if(ret) { + skw_usb_err("failed to add platform data \n"); + platform_device_put(pdev); + kfree(port); + return ret; + } + if(iface_desc->desc.bInterfaceNumber>1){ + ret = platform_device_add(pdev); + if(ret) { + skw_usb_err("failt to register platform device\n"); + platform_device_put(pdev); + kfree(port); + return ret; + } + } + port->pdev = pdev; + } + } + usb_set_intfdata(interface, port); + + port->interface = usb_get_intf(interface); + port->udev = usb_get_dev(udev); + /* register struct wcn_usb_intf */ + skw_usb_dbg("intf[%x] is registerred: ep count %d %s\n", + iface_desc->desc.bInterfaceNumber, + iface_desc->desc.bNumEndpoints, + iface_desc->string); + ret = -ENOMEM; + for(i=0; i<iface_desc->desc.bNumEndpoints; i++) { + + epd = &iface_desc->endpoint[i].desc; + port->buffer_size = 5120; + port->ep_mps = epd->wMaxPacketSize; + if(usb_endpoint_is_bulk_in(epd)) { + port->epin = epd; + port->read_urb = usb_alloc_urb(0, GFP_KERNEL); + if(!port->read_urb) + goto err0; + if(iface_desc->desc.bInterfaceNumber > 1) { + port->read_buffer = NULL; + port->buffer_size = 0; + } else { + port->read_buffer = kzalloc(port->buffer_size , GFP_KERNEL); + if(!port->read_buffer) + goto err0; + } + usb_fill_bulk_urb(port->read_urb, udev, + usb_rcvbulkpipe(udev, epd->bEndpointAddress), + port->read_buffer, port->buffer_size, + bulkin_complete, port); + port->read_urb->context = NULL; + init_usb_anchor(&port->read_submitted); + skw_usb_dbg("BulkinEP = 0x%x rp=%p\n", + epd->bEndpointAddress, port->read_buffer); + } else if(usb_endpoint_is_bulk_out(epd)) { + port->epout = epd; + port->write_urb = usb_alloc_urb(0, GFP_KERNEL); + if(!port->write_urb) + goto err0; + if(iface_desc->desc.bInterfaceNumber > 1) { + port->write_buffer = NULL; + port->buffer_size = 0; + } else{ + port->write_buffer = kzalloc(port->buffer_size, GFP_KERNEL); + if(!port->write_buffer) + goto err0; + } + usb_fill_bulk_urb(port->write_urb, udev, + usb_sndbulkpipe(udev, epd->bEndpointAddress), + port->write_buffer, port->buffer_size, bulkout_complete,port); + port->write_urb->context = NULL; + init_usb_anchor(&port->write_submitted); + skw_usb_dbg("BulkoutEP = 0x%x wp =%p context %p\n", + epd->bEndpointAddress, port->write_buffer, port->write_urb->context); + } + } + if(!dloader) { + port->portno = iface_desc->desc.bInterfaceNumber; + port->state = 1; + if (port->portno<=1) { + if (!strncmp(names, "WIFIDATA", 8)) { + skw_get_packet_count(port->portno); + wifi_pdata.cmd_port = 1 - port->portno; + wifi_pdata.data_port = port->portno; + port->thread = kthread_create(usb_port_async_entry, port, iface_desc->string); + + tasklet_init(&port->tasklet, usb_handle, (unsigned long) port); + } else { + wifi_pdata.cmd_port = port->portno; + wifi_pdata.data_port = 1 - port->portno; + } + if(port->thread) { + sema_init(&port->sem, 0); + wake_up_process(port->thread); + } else + sema_init(&port->sem, 1); + } else if(!strncmp(names, "LOOP", 4)) { + sema_init(&port->sem, 0); + port->thread = kthread_create(usb_loopcheck_entry, port, iface_desc->string); + if (port->thread) + wake_up_process(port->thread); + } else sema_init(&port->sem, 1); + } else { + port->state = 1; + assert_info_print = 0; + INIT_WORK(&port->work, dloader_work); + if (usb_boot_data && + usb_boot_data->iram_dl_size && + usb_boot_data->dram_dl_size) { + skw_usb_info("schedule boot-work: 0x%x:0x%x\n", + usb_boot_data->dram_dl_size,usb_boot_data->iram_dl_size); + schedule_work(&port->work); + modem_status = MODEM_ON; + } + port->is_dloader = 1; + } + if (!strncmp(names, "LOG", 3)) + log_port = port; + return 0; +err0: + skw_usb_err("no memory to register device\n"); + if(port->write_buffer) + kfree(port->write_buffer); + if(port->read_buffer) + kfree(port->read_buffer); + if(port->write_urb) + usb_free_urb(port->write_urb); + if(port->read_urb) + usb_free_urb(port->read_urb); + if(port->pdev) + platform_device_unregister(port->pdev); + usb_ports[iface_desc->desc.bInterfaceNumber] = NULL; + kfree(port); + return ret; +} + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int launch_download_work(char *data, int size,int addr) +{ + int chk_ports =0; + firmware_size = size;//link to usb_download size + firmware_data = data;//link to usb_download dl_data + firmware_addr = addr; + do{ + if((usb_ports[0] !=NULL)&&(usb_ports[0]->state)) { + chk_ports = 1; + break; + } + msleep(10); + }while(!chk_ports); + schedule_work(&usb_ports[0]->work); + return 0; +} + + +static int skw_recovery_mode(void) +{ + int ret=0; + skw_usb_info("[+]\n"); + if(!cls_recovery_mode_en){ + ret = skw_reset_bus_dev(); + } else { + skw_usb_info("No Need To Recovery!\n"); + } + skw_usb_info("[-]\n"); + return ret; +} + +void get_bt_antenna_mode(char *mode) +{ +} + +void reboot_to_change_bt_antenna_mode(char *mode) +{ +} + +void get_USB_speed_mode(char *mode) +{ +} + +void reboot_to_change_USB_speed_mode(char *mode) +{ +} + +void reboot_to_change_bt_uart1(char *mode) +{ +} +static irqreturn_t skw_gpio_irq_handler(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} +/************************************************************************ + *Decription: + *Author:JUNWEI.JIANG + *Date:2021-12-20 + *Modfiy: + * + ********************************************************************* */ +int skw_boot_loader(struct seekwave_device *boot_data) +{ + int ret = 1; + + if (usb_ports[0] && usb_ports[0]->suspend) + return -EOPNOTSUPP; + usb_boot_data= boot_data; + skw_usb_info("status:%d , chip_en_gpio=%d, gpio_in=%d", modem_status, + usb_boot_data->chip_en, usb_boot_data->gpio_in); + chip_en_gpio = usb_boot_data->chip_en; +#ifdef CONFIG_SKW_DL_TIME_STATS + cur_time = ktime_get(); +#endif + if (host_wake_gpio < 0 && usb_boot_data->gpio_in>=0) { + int irq_num; + + host_wake_gpio = usb_boot_data->gpio_in; + irq_num = gpio_to_irq(host_wake_gpio); + ret = request_irq(irq_num, skw_gpio_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "skw-gpio-irq", NULL); + skw_usb_info("request_gpio_irq ret=%d\n", ret); + if (ret == 0) + enable_irq_wake(irq_num); + } + if(!boot_data->first_dl_flag ){ + if (usb_ports[0] && !usb_ports[0]->is_dloader) { + //usb_setup_service_devices(); + schedule_work(&add_device_work); + } else if(boot_data->iram_img_data !=NULL && boot_data->dram_img_data!=NULL){ + skw_usb_info("USB FIRST BOOT... \n"); + ret=launch_download_work(boot_data->iram_img_data,boot_data->iram_dl_size,boot_data->iram_dl_addr); + }else{ + skw_usb_info("The CPBOOT not download from AP!!!!\n"); + } + } + if(boot_data->dl_module==RECOVERY_BOOT){ + skw_recovery_mode(); + return 0; + } + if(boot_data->service_ops==SKW_WIFI_START){ + //skw_WIFI_service_start(); + //skw_usb_info("----WIFI-SERVICE-----START!!!\n"); + }else if(boot_data->service_ops== SKW_WIFI_STOP && + (service_state_map & (1<<WIFI_SERVICE))){ + skw_WIFI_service_stop(); + //skw_usb_info("----WIFI-SERVICE-----STOP!!!\n"); + }else if(boot_data->service_ops == SKW_BT_START){ + skw_usb_info("----BT-SERVICE-----START!!!\n"); + ret=skw_BT_service_start(); + }else if(boot_data->service_ops==SKW_BT_STOP && + (service_state_map & (1<<BT_SERVICE))){ + skw_usb_info("----BT-SERVICE-----STOP!!!\n"); + ret=skw_BT_service_stop(); + } + if(ret < 0) + return -1; + else + return 0; +} +void *skw_get_bus_dev(void) +{ + int time_count=0; + if(modem_status == MODEM_OFF && !usb_ports[0]) { + skw_usb_err(" power on USB\n"); + do{ + msleep(10); + time_count++; + }while(!usb_ports[0] && time_count < 50); + } + if(!usb_ports[0] || !usb_ports[0]->state || !usb_ports[0]->udev){ + skw_usb_err(" the port open device fail !!!\n"); + return NULL; + } + return &usb_ports[0]->udev->dev; +} + +/************************************************************************ + *Decription:check dev ready for boot + *Author:junwei.jiang + *Date:2022-06-07 + *Modfiy: + * + ********************************************************************* */ +int skw_reset_bus_dev(void) +{ + struct usb_port_struct *port; + int ret = -1; + + if(chip_en_gpio >= 0) { + skw_chip_power_reset(); + return 0; + } + port = usb_ports[0]; + if (port == NULL) + return 0; + + ret = usb_control_msg(port->udev, usb_sndctrlpipe(port->udev, 0), + VENDOR_MSG_MODEM_RESET, USB_DIR_OUT| USB_TYPE_VENDOR|USB_RECIP_DEVICE, + 0,0,NULL,0,100); + skw_usb_info("ret = %d\n", ret); + if (ret == -ETIMEDOUT) { + usb_reset_device(port->udev); + } + return ret; +} + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int skw_usb_io_free_suspend_urbs(struct usb_interface *interface) +{ + struct usb_port_struct *port; + struct urb *urb; + + port = usb_get_intfdata(interface); + + port->suspend = 0; + while(!list_empty(&port->suspend_urb_list)) { + urb = list_first_entry(&port->suspend_urb_list, struct urb, urb_list); + list_del_init(&urb->urb_list); + if (!list_empty(&port->suspend_urb_list)) + list_add_tail(&urb->urb_list, &port->rx_urb_list); + else { + urb->status = -EIO; + urb->complete(urb); + } + } + return 0; +} + +static void skw_usb_io_disconnect(struct usb_interface *interface) +{ + int infno = interface->cur_altsetting->desc.bInterfaceNumber; + struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); + struct usb_port_struct *port; + unsigned long flags; + struct urb *urb; + + port = usb_get_intfdata(interface); + if(!port) + return; + log_port = NULL; + port->state = 0; + skw_usb_info("interface[%x] disconnected %d\n", infno, modem_status); + if(!port->is_dloader) { + if (infno > 1) + platform_device_unregister(port->pdev); + if (infno == 1) { + wake_up_interruptible(&port->rx_wait); + wake_up_interruptible(&port->tx_wait); + } + if (modem_status==MODEM_ON) { + if(wifi_data_pdev && &port->udev->dev == wifi_data_pdev->dev.parent) { + if(recovery->cp_state == 0) + modem_notify_event(DEVICE_DISCONNECT_EVENT); + platform_device_unregister(wifi_data_pdev); + wifi_data_pdev = NULL; + skw_usb_info("WIFI device disconnected1!!!\n"); + } + } + if (port->pdev == wifi_data_pdev && port->suspend) { + modem_notify_event(DEVICE_DISCONNECT_EVENT); + tasklet_kill(&port->tasklet); + } + skw_usb_io_free_suspend_urbs(interface); + if(port->read_urb && port->read_urb->context) + usb_kill_anchored_urbs(&port->read_submitted); + if(port->write_urb && port->write_urb->context) + usb_kill_anchored_urbs(&port->write_submitted); + if(port->thread && !port->suspend&& down_timeout(&port->sem, 1000)) + skw_usb_info("start to unregister interface[%x]\n", infno); + } else + flush_work(&port->work); + if(port->read_urb && !port->read_urb->context) { + kfree(port->read_urb); + port->read_urb = NULL; + } else skw_usb_err(" memory leak port.r%d!!!!!!!!\n", infno); + if(port->write_urb && !port->write_urb->context) { + kfree(port->write_urb); + port->write_urb = NULL; + } else skw_usb_err(" memory leak port.w%d!!!!!!!!\n", infno); + if(port->read_buffer) + kfree(port->read_buffer); + if(port->write_buffer) + kfree(port->write_buffer); + spin_lock_irqsave(&port->rx_urb_lock, flags); + while(!list_empty(&port->rx_done_urb_list)) { + urb = list_first_entry(&port->rx_done_urb_list, struct urb, urb_list); + list_del_init(&urb->urb_list); + if(urb->transfer_buffer) + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + spin_unlock_irqrestore(&port->rx_urb_lock, flags); + usb_ports[infno] = NULL; + usb_set_intfdata(interface, NULL); + usb_put_dev(port->udev); + usb_put_intf(interface); + kfree(port); + if (chip_en_gpio >= 0 && MODEM_DOWNLOAD_FAILED == modem_status) { + modem_status = MODEM_HALT; + msleep(50); + gpio_set_value(chip_en_gpio, 1); + skw_usb_info("retry to boot device\n"); + } +} + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int skw_usb_io_pre_reset(struct usb_interface *interface) +{ + /* there is a lock to prevent we reset a interface when + * urb submit + */ + struct usb_port_struct *port; + + port = usb_get_intfdata(interface); + + return 0; +} + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static int skw_usb_io_post_reset(struct usb_interface *interface) +{ + struct usb_port_struct *port; + + port = usb_get_intfdata(interface); + return 0; +} +#ifdef CONFIG_PM +static int skw_usb_io_suspend(struct usb_interface *interface, pm_message_t message) +{ + struct usb_port_struct *port; + struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); + + port = usb_get_intfdata(interface); + + if(usb_ports[1] == port) { + int ret; + u16 *count = (u16 *)port->read_buffer; + + modem_notify_event(DEVICE_SUSPEND_EVENT); + ret = usb_control_msg(port->udev, usb_rcvctrlpipe(port->udev, 0), + VENDOR_MSG_MODEM_SUSP, USB_DIR_IN| USB_TYPE_VENDOR|USB_RECIP_DEVICE, + 1, 0, port->read_buffer, 2, 10); + skw_usb_info("RET = %d packet suspended = %d\n", ret, *count); + if (*count) + msleep(10); + } + if (port->tx_urb_count) + usb_kill_anchored_urbs(&port->write_submitted); + port->suspend = 1; + + if(port->portno == 1 || port->read_urb->context) + usb_kill_anchored_urbs(&port->read_submitted); + if(port->write_urb->context) + usb_kill_anchored_urbs(&port->write_submitted); + if (port->portno==0 && recovery->cp_state) { + recovery->cp_state = 0; + cancel_delayed_work_sync(&skw_except_work); + } +#if KERNEL_VERSION(3, 2, 0) <= LINUX_VERSION_CODE + skw_usb_info("port%d %s MSG\n", port->portno, PMSG_IS_AUTO(message)? "Auto":"None-auto"); +#else + skw_usb_info("port%d MSG not supported print!!\n", port->portno); +#endif + return 0; +} +static int skw_usb_io_resume(struct usb_interface *interface) +{ + int retval = -1; + struct usb_port_struct *port; + struct urb *urb; + port = usb_get_intfdata(interface); + + skw_usb_info("port%d enter...\n", port->portno); + while(!list_empty(&port->suspend_urb_list)) { + urb = list_first_entry(&port->suspend_urb_list, struct urb, urb_list); + list_del_init(&urb->urb_list); + if(port->portno == wifi_pdata.data_port) + urb->context = port; + usb_anchor_urb(urb, &port->read_submitted); + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval < 0) { + usb_unanchor_urb(urb); + skw_usb_info("is error!!! %d\n", retval); + return retval; + } + } + if (usb_ports[1]==port && port->suspend) { + port->suspend = 0; + modem_notify_event(DEVICE_RESUME_EVENT); + } + port->suspend = 0; + return 0; +} +static int skw_usb_io_reset_resume(struct usb_interface *interface) +{ + struct usb_port_struct *port; + + skw_usb_info("enter...\n"); + port = usb_get_intfdata(interface); + if (port) + port->suspend++; + skw_usb_io_resume(interface); + return 0; +} +#endif + +/************************************************************************ + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +struct usb_driver skw_usb_io_driver = { + .name = "skw_usb_io", + .probe = skw_usb_io_probe, + .disconnect = skw_usb_io_disconnect, +#ifdef CONFIG_PM + .suspend = skw_usb_io_suspend, + .resume = skw_usb_io_resume, + .reset_resume = skw_usb_io_reset_resume, +#endif + .pre_reset = skw_usb_io_pre_reset, + .post_reset = skw_usb_io_post_reset, + .id_table = skw_usb_io_id_table, + .supports_autosuspend = 1, +}; + +/** + * wcn_usb_io_init() - init wcn_usb_io's memory and register this driver. + * @void: void. + */ +static int __init skw_usb_io_init(void) +{ + usb_bus_num = 0xff; + usb_port_num = 0xff; + wifi_data_pdev = NULL; + bluetooth_pdev = NULL; + log_port = NULL; + usb_boot_data = NULL; +#ifndef CONFIG_SEEKWAVE_PLD_RELEASE + cls_recovery_mode_en = 1; +#else + cls_recovery_mode_en = 0; +#endif + wifi_port_share = 0; + usb_speed_switching = 0; + memset(usb_ports, 0, sizeof(usb_ports)); + init_completion(&download_done); + init_completion(&loop_completion); + skw_usb_debugfs_init(); + skw_usb_log_level_init(); + chip_en_gpio = -1; + modem_status = MODEM_OFF; + skw_chipid = wifi_pdata.chipid; + mutex_init(&g_recovery_data.except_mutex); + INIT_DELAYED_WORK(&skw_except_work, skw_usb_exception_work); + INIT_WORK(&add_device_work, add_devices_work); + INIT_WORK(&dump_memory_worker, dump_memory_work); + INIT_WORK(&usb_control_worker, usb_control_work); + dump_memory_buffer = NULL; + dump_log_size=NULL; + dump_buffer_size = 0; + usb_register(&skw_usb_io_driver); + return seekwave_boot_init(NULL);; +} + +/************************************************************************ + *Copyright(C) 2020-2021: Seekwave tech LTD China + *Decription: + *Author:jiayong.yang + *Date:2021-05-27 + *Modfiy: + * + ********************************************************************* */ +static void __exit skw_usb_io_exit(void) +{ + int ret; + + if (chip_en_gpio >=0) { + skw_chip_set_power(0); + msleep(50); + } + if (usb_ports[0] && usb_ports[0]->udev) { + skw_usb_info("reset SKWUSB device"); + skw_reset_bus_dev(); + } + if (usb_boot_data && usb_boot_data->pdev && wifi_data_pdev && + wifi_data_pdev->dev.parent == &usb_boot_data->pdev->dev) { + skw_usb_info("unregister WIFI device\n"); + platform_device_unregister(wifi_data_pdev); + wifi_data_pdev = NULL; + ret = 0; + } + seekwave_boot_exit(); + skw_usb_debugfs_deinit(); + cancel_delayed_work_sync(&skw_except_work); + cancel_work_sync(&add_device_work); + cancel_work_sync(&dump_memory_worker); + cancel_work_sync(&usb_control_worker); + dump_memory_buffer = NULL; + dump_log_size=NULL; + mutex_destroy(&g_recovery_data.except_mutex); + if(bluetooth_pdev) + platform_device_put(bluetooth_pdev); + usb_deregister(&skw_usb_io_driver); + if(wifi_data_pdev) + platform_device_put(wifi_data_pdev); +} +module_init(skw_usb_io_init) +module_exit(skw_usb_io_exit) +MODULE_LICENSE("GPL v2"); diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_log.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_log.c new file mode 100755 index 0000000..bfe9f5d --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_log.c @@ -0,0 +1,452 @@ +/***************************************************************************** + * Copyright(c) 2020-2030 Seekwave Corporation. + * SEEKWAVE TECH LTD..CO + * + *Seekwave Platform the usb log debug fs + *FILENAME:skw_usb_log.c + *DATE:2022-04-11 + *MODIFY: + *Author:Jones.Jiang + **************************************************************************/ +#include <linux/uaccess.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/seq_file.h> +#include "skw_usb_log.h" +#include "skw_usb.h" +#include "skw_usb_debugfs.h" + +static unsigned long skw_usb_dbg_level; + +extern char firmware_version[]; +extern void reboot_to_change_bt_antenna_mode(char *mode); +extern void get_bt_antenna_mode(char *mode); +unsigned long skw_usb_log_level(void) +{ + return skw_usb_dbg_level; +} + +static void skw_usb_set_log_level(int level) +{ + unsigned long dbg_level; + + dbg_level = skw_usb_log_level() & 0xffff0000; + dbg_level |= ((level << 1) - 1); + + xchg(&skw_usb_dbg_level, dbg_level); +} + +static void skw_usb_enable_func_log(int func, bool enable) +{ + unsigned long dbg_level = skw_usb_log_level(); + + if (enable) + dbg_level |= func; + else + dbg_level &= (~func); + + xchg(&skw_usb_dbg_level, dbg_level); +} + +static int skw_usb_log_show(struct seq_file *seq, void *data) +{ +#define SKW_USB_LOG_STATUS(s) (level & (s) ? "enable" : "disable") + + int i; + u32 level = skw_usb_log_level(); + u8 *log_name[] = {"NONE", "ERROR", "WARNNING", "INFO", "DEBUG"}; + + for (i = 0; i < 5; i++) { + if (!(level & BIT(i))) + break; + } + if (i >= 5) + return 0; + seq_printf(seq, "\nlog level: %s\n", log_name[i]); + + seq_puts(seq, "\n"); + seq_printf(seq, "port0 log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_PORT0)); + seq_printf(seq, "port1 log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_PORT1)); + seq_printf(seq, "port2 log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_PORT2)); + seq_printf(seq, "port3 log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_PORT3)); + seq_printf(seq, "port4 log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_PORT4)); + seq_printf(seq, "port5 log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_PORT5)); + seq_printf(seq, "port6 log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_PORT6)); + seq_printf(seq, "port7 log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_PORT7)); + seq_printf(seq, "savelog : %s\n", SKW_USB_LOG_STATUS(SKW_USB_SAVELOG)); + seq_printf(seq, "dump log: %s\n", SKW_USB_LOG_STATUS(SKW_USB_DUMP)); + + return 0; +} + +static int skw_usb_log_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_usb_log_show, inode->i_private); +} + +static int skw_usb_log_control(const char *cmd, bool enable) +{ + if (!strcmp("dump", cmd)) + skw_usb_enable_func_log(SKW_USB_DUMP, enable); + else if (!strcmp("port0", cmd)) + skw_usb_enable_func_log(SKW_USB_PORT0, enable); + else if (!strcmp("port1", cmd)) + skw_usb_enable_func_log(SKW_USB_PORT1, enable); + else if (!strcmp("port2", cmd)) + skw_usb_enable_func_log(SKW_USB_PORT2, enable); + else if (!strcmp("port3", cmd)) + skw_usb_enable_func_log(SKW_USB_PORT3, enable); + else if (!strcmp("port4", cmd)) + skw_usb_enable_func_log(SKW_USB_PORT4, enable); + else if (!strcmp("port5", cmd)) + skw_usb_enable_func_log(SKW_USB_PORT5, enable); + else if (!strcmp("port6", cmd)) + skw_usb_enable_func_log(SKW_USB_PORT6, enable); + else if (!strcmp("port7", cmd)) + skw_usb_enable_func_log(SKW_USB_PORT7, enable); + else if (!strcmp("savelog", cmd)) + skw_usb_enable_func_log(SKW_USB_SAVELOG, enable); + else if (!strcmp("debug", cmd)) + skw_usb_set_log_level(SKW_USB_DEBUG); + else if (!strcmp("info", cmd)) + skw_usb_set_log_level(SKW_USB_INFO); + else if (!strcmp("warn", cmd)) + skw_usb_set_log_level(SKW_USB_WARNING); + else if (!strcmp("error", cmd)) + skw_usb_set_log_level(SKW_USB_ERROR); + else + return -EINVAL; + + return 0; +} + +static ssize_t skw_usb_log_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + int i, idx; + char cmd[32]; + bool enable = false; + + for (idx = 0, i = 0; i < len; i++) { + char c; + + if (get_user(c, buffer)) + return -EFAULT; + + switch (c) { + case ' ': + break; + + case ':': + cmd[idx] = 0; + if (!strcmp("enable", cmd)) + enable = true; + else + enable = false; + + idx = 0; + break; + + case '|': + case '\0': + case '\n': + cmd[idx] = 0; + skw_usb_log_control(cmd, enable); + idx = 0; + break; + + default: + cmd[idx++] = c; + idx %= 32; + + break; + } + + buffer++; + } + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_usb_log_proc_fops = { + .proc_open = skw_usb_log_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_usb_log_write, +}; +#else +static const struct file_operations skw_usb_log_proc_fops = { + .owner = THIS_MODULE, + .open = skw_usb_log_open, + .read = seq_read, + .release = single_release, + .write = skw_usb_log_write, +}; +#endif + +static int skw_version_show(struct seq_file *seq, void *data) +{ + seq_printf(seq, "firmware info:\n %s\n", firmware_version ); + return 0; +} + +static int skw_version_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_version_show, inode->i_private); +} + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_version_proc_fops = { + .proc_open = skw_version_open, + .proc_read = seq_read, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_version_proc_fops = { + .owner = THIS_MODULE, + .open = skw_version_open, + .read = seq_read, + .release = single_release, +}; +#endif + +static int skw_port_statistic_show(struct seq_file *seq, void *data) +{ + char *statistic = kzalloc(2048, GFP_KERNEL); + + skw_get_port_statistic(statistic, 2048); + seq_printf(seq, "Statistic:\n%s", statistic ); + kfree(statistic); + return 0; +} + +static int skw_port_statistic_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_port_statistic_show, inode->i_private); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_port_statistic_proc_fops = { + .proc_open = skw_port_statistic_open, + .proc_read = seq_read, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_port_statistic_proc_fops = { + .owner = THIS_MODULE, + .open = skw_port_statistic_open, + .read = seq_read, + .release = single_release, +}; +#endif + +static int skw_bluetooth_antenna_show(struct seq_file *seq, void *data) +{ + char result[32]; + + memset(result, 0, sizeof(result)); + get_bt_antenna_mode(result); + if(strlen(result)) + seq_printf(seq, result); + return 0; +} +static int skw_bluetooth_antenna_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_bluetooth_antenna_show, inode->i_private); +} + + +static ssize_t skw_bluetooth_antenna_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[32]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("switch", cmd, 6)) { + memset(cmd, 0, sizeof(cmd)); + reboot_to_change_bt_antenna_mode(cmd); + skw_usb_info("%s\n", cmd); + } + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_bluetooth_antenna_proc_fops = { + .proc_open = skw_bluetooth_antenna_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_bluetooth_antenna_write, +}; +#else +static const struct file_operations skw_bluetooth_antenna_proc_fops = { + .owner = THIS_MODULE, + .open = skw_bluetooth_antenna_open, + .read = seq_read, + .release = single_release, + .write = skw_bluetooth_antenna_write, +}; +#endif + +static int skw_USB_speed_show(struct seq_file *seq, void *data) +{ + char result[32]; + + memset(result, 0, sizeof(result)); + get_USB_speed_mode(result); + if(strlen(result)) + seq_printf(seq, result); + return 0; +} +static int skw_USB_speed_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skw_USB_speed_show, inode->i_private); +} + + +static ssize_t skw_USB_speed_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[32]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("HIGH", cmd, 4)) { + memset(cmd, 0, sizeof(cmd)); + reboot_to_change_USB_speed_mode(cmd); + skw_usb_info("%s\n", cmd); + } + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_USB_speed_proc_fops = { + .proc_open = skw_USB_speed_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_USB_speed_write, +}; +#else +static const struct file_operations skw_USB_speed_proc_fops = { + .owner = THIS_MODULE, + .open = skw_USB_speed_open, + .read = seq_read, + .release = single_release, + .write = skw_USB_speed_write, +}; +#endif + +static int skwusb_recovery_debug_show(struct seq_file *seq, void *data) +{ + if (skw_usb_recovery_debug_status()) + seq_printf(seq, "Disabled"); + else + seq_printf(seq, "Enabled"); + + return 0; +} +static int skwusb_recovery_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, &skwusb_recovery_debug_show, inode->i_private); +} + +static ssize_t skwusb_recovery_debug_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[16]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("disable", cmd, 7)) + skw_usb_recovery_debug(1); + else if (!strncmp("enable", cmd, 6)) + skw_usb_recovery_debug(0); + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skwusb_recovery_debug_proc_fops = { + .proc_open = skwusb_recovery_debug_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skwusb_recovery_debug_write, +}; +#else +static const struct file_operations skwusb_recovery_debug_proc_fops = { + .owner = THIS_MODULE, + .open = skwusb_recovery_debug_open, + .read = seq_read, + .release = single_release, + .write = skwusb_recovery_debug_write, +}; +#endif + +static int skw_bluetooth_UART1_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, inode->i_private); +} + + +static ssize_t skw_bluetooth_UART1_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + char cmd[32]={0}; + + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buffer, len)) + return -EFAULT; + if (!strncmp("enable", cmd, 6)) { + memset(cmd, 0, sizeof(cmd)); + reboot_to_change_bt_uart1(cmd); + skw_usb_info("%s UART-HCI\n", cmd); + } + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_bluetooth_UART1_proc_fops = { + .proc_open = skw_bluetooth_UART1_open, + .proc_release = single_release, + .proc_write = skw_bluetooth_UART1_write, +}; +#else +static const struct file_operations skw_bluetooth_UART1_proc_fops = { + .owner = THIS_MODULE, + .open = skw_bluetooth_UART1_open, + .release = single_release, + .write = skw_bluetooth_UART1_write, +}; +#endif + +void skw_usb_log_level_init(void) +{ + skw_usb_set_log_level(SKW_USB_INFO); + + skw_usb_enable_func_log(SKW_USB_DUMP, false); + skw_usb_enable_func_log(SKW_USB_PORT0, false); + skw_usb_enable_func_log(SKW_USB_PORT1, false); + skw_usb_enable_func_log(SKW_USB_PORT2, false); + skw_usb_enable_func_log(SKW_USB_PORT3, false); + skw_usb_enable_func_log(SKW_USB_PORT4, false); + skw_usb_enable_func_log(SKW_USB_PORT5, false); + skw_usb_enable_func_log(SKW_USB_PORT6, false); + skw_usb_enable_func_log(SKW_USB_SAVELOG, false); + skw_usb_enable_func_log(SKW_USB_PORT7, false); + skw_usb_proc_init_ex("log_level", 0666, &skw_usb_log_proc_fops, NULL); + skw_usb_proc_init_ex("Version", 0666, &skw_version_proc_fops, NULL); + skw_usb_proc_init_ex("Statistic", 0666, &skw_port_statistic_proc_fops, NULL); + skw_usb_proc_init_ex("BT_ANT", 0666, &skw_bluetooth_antenna_proc_fops, NULL); + skw_usb_proc_init_ex("recovery", 0666, &skwusb_recovery_debug_proc_fops, NULL); + skw_usb_proc_init_ex("USB_SPEED", 0666, &skw_USB_speed_proc_fops, NULL); + skw_usb_proc_init_ex("BT_UART1", 0666, &skw_bluetooth_UART1_proc_fops, NULL); +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_log.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_log.h new file mode 100755 index 0000000..606e9f9 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/skw_usb_log.h @@ -0,0 +1,86 @@ +/****************************************************************************** + * + * Copyright(c) 2020-2030 Seekwave Corporation. + * DATE: 2022-07-18 + * MODIFY: + * Author:junwei.jiang + * + *****************************************************************************/ +#ifndef __SKW_USB_LOG_H__ +#define __SKW_USB_LOG_H__ + +#define SKW_USB_ERROR BIT(0) +#define SKW_USB_WARNING BIT(1) +#define SKW_USB_INFO BIT(2) +#define SKW_USB_DEBUG BIT(3) + +#define SKW_USB_CMD BIT(16) +#define SKW_USB_EVENT BIT(17) +#define SKW_USB_SCAN BIT(18) +#define SKW_USB_TIMER BIT(19) +#define SKW_USB_STATE BIT(20) + +#define SKW_USB_PORT0 BIT(21) +#define SKW_USB_PORT1 BIT(22) +#define SKW_USB_PORT2 BIT(23) +#define SKW_USB_PORT3 BIT(24) +#define SKW_USB_PORT4 BIT(25) +#define SKW_USB_PORT5 BIT(26) +#define SKW_USB_PORT6 BIT(27) +#define SKW_USB_PORT7 BIT(28) +#define SKW_USB_SAVELOG BIT(29) +#define SKW_USB_DUMP BIT(31) + +unsigned long skw_usb_log_level(void); +void skw_usb_cp_log(int disable); +int skw_usb_cp_log_status(void); +void skw_get_port_statistic(char *buffer, int size); +void reboot_to_change_USB_speed_mode(char *mode); +void get_USB_speed_mode(char *mode); +void modem_notify_event(int event); +#define skw_usb_log(level, fmt, ...) \ + do { \ + if (skw_usb_log_level() & level) \ + pr_err(fmt, ##__VA_ARGS__); \ + } while (0) + +#define skw_usb_port_log(port_num, fmt, ...) \ + do { \ + if (skw_usb_log_level() &(SKW_USB_PORT0<<port_num)) \ + pr_err(fmt, ##__VA_ARGS__); \ + } while (0) + +#define skw_port_log(port_num,fmt, ...) \ + skw_usb_log((SKW_USB_PORT0<<port_num), "[PORT_LOG] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_usb_err(fmt, ...) \ + skw_usb_log(SKW_USB_ERROR, "[SKWUSB ERROR] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_usb_warn(fmt, ...) \ + skw_usb_log(SKW_USB_WARNING, "[SKWUSB WARN] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_usb_info(fmt, ...) \ + skw_usb_log(SKW_USB_INFO, "[SKWUSB INFO] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_usb_dbg(fmt, ...) \ + skw_usb_log(SKW_USB_DEBUG, "[SKWUSB DBG] %s: "fmt, __func__, ##__VA_ARGS__) + +#define skw_usb_hex_dump(prefix, buf, len) \ + do { \ + if (skw_usb_log_level() & SKW_USB_DUMP) { \ + u8 str[32] = {0}; \ + snprintf(str, sizeof(str), "[SKWUSB DUMP] %s", prefix); \ + print_hex_dump(KERN_ERR, str, \ + DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); \ + } \ + } while (0) +#if 0 +#define skw_usb_port_log(port_num, fmt, ...) \ + do { \ + if (skw_usb_log_level() &(SKW_USB_PORT0<<port_num)) \ + pr_err("[PORT_LOG] %s:"fmt,__func__, ##__VA_ARGS__); \ + } while (0) + +#endif +void skw_usb_log_level_init(void); +#endif diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/usb_boot.c b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/usb_boot.c new file mode 100755 index 0000000..78a31bb --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/usb_boot.c @@ -0,0 +1,469 @@ +/************************************************************************************* + *Description: usb download + *Seekwave tech LTD + *Author: jiayong.yang/junwei.jiang + *Date:20210527 + *Modify: + * ***********************************************************************************/ +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include <linux/errno.h> +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> +#endif +#include <linux/fs.h> +#include <linux/buffer_head.h> +#include <linux/ctype.h> +#include "skw_usb_log.h" +#include "usb_boot.h" +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static int dl_mps; +static int dloader_port = 0; + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static int check_modem_status_from_connect_message(void) +{ + struct connect_ack *ack = (void *)&connect_ack[12]; + memcpy(skw_chipid,ack->chip_id,16); + dl_mps = ack->packet_size; + if(ack->flags.bitmap.boot) + return NORMAL_BOOT; + else + return NORMAL_BOOT; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static int dloader_write(char *msg, int msg_len, int *actual, int timeout) +{ + return bulkout_write_timeout(dloader_port, msg, msg_len, actual, timeout); +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static int dloader_read(char *msg, int msg_len, int *actual, int timeout) +{ + return bulkin_read_timeout(dloader_port, msg, msg_len, actual, timeout); +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static int compare_msg(const char *src, const char *dst, size_t count) +{ + unsigned char c1, c2; + + while (count) { + c1 = *src++; + c2 = *dst++; + if (c1 != c2) + return c1 < c2 ? -1 : 1; + count--; + } + return 0; +} + +static int dloader_send_data(const char *command, int command_len, const char *ack, int ack_len) +{ + int actual_len = 0; + int ret; + void *data; + int data_size = 128; + + data = kzalloc(data_size, GFP_KERNEL); + + if (!data) + return -ENOMEM; + /* send command */ + ret = dloader_write((char *)command, command_len, &actual_len, 3000); + if (ret <0 || actual_len != command_len) { + skw_usb_err(" send cmd error ret %d actual_len %d command_len %d\n", + ret, actual_len, command_len); + } else { + if (ack == NULL) + goto OUT; + + /* read ack and check it */ + ret = dloader_read(data, data_size, &actual_len, 3000); + if (ret <0 || ack_len > actual_len || compare_msg(ack, data, ack_len)) { + skw_usb_err(" ack is NACK:ret == %d\n", ret); + print_hex_dump(KERN_ERR, "ACK ERR:", 0, 16, 1, data, ack_len, 1); + ret = -EIO; + } + } +OUT: + kfree(data); + return ret; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static int dloader_send_command(const char *command, int command_len, const char *ack, int ack_len) +{ + int actual_len = 0; + int ret; + void *data; + int data_size = 128; + + data = kzalloc(data_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + /* send command */ + memcpy(data, (char*)command, command_len); + ret = dloader_write(data, command_len, &actual_len, 3000); + if (ret <0 || actual_len != command_len) { + skw_usb_err(" send cmd error ret %d actual_len %d command_len %d\n", + ret, actual_len, command_len); + } else { + /* read ack */ + ret = dloader_read(data, data_size, &actual_len, 3000); + if (ret <0) { + skw_usb_warn(" ack is NACK: acklen ===%d- actual_len ==%d--ret == %d\n", + ret, ack_len, actual_len); + } + } + if ((command_len > 8) &&(0 == command[8])){ + if(actual_len > sizeof(connect_ack)) + actual_len = sizeof(connect_ack); + memcpy(connect_ack, data, actual_len); + } + kfree(data); + return ret; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static unsigned short crc16_calculate(unsigned char *buf, int len) +{ + unsigned int i; + unsigned short crc = 0; + + while (len-- != 0) { + for (i = 0x80; i != 0; i = i >> 1) { + if ((crc & 0x8000) != 0) { + crc = crc << 1; + crc = crc ^ 0x1021; + } else { + crc = crc << 1; + } + if ((*buf & i) != 0) + crc = crc ^ 0x1021; + } + buf++; + } + return crc; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +int dloader_command_start_download(unsigned int addr, unsigned int len) +{ + + int command_len = 20; + char command[20] = {0x7E, 0x7E, 0x7E, 0x7E,/* head */ + 0x08, 0x00, 0x00, 0x00, /*length */ + 0x01, 0x00, /* message type, 01: start command */ + 0x00, 0x00, /*crc for data body, excludes message header */ + 0x00, 0x00, 0x10, 0x00,/*addr*/ + 0x60, 0xb3, 0x06, 0x00 /*image size*/}; + + *((u32 *)&command[12]) = addr; + *((u32 *)&command[16]) = len; + + *((u16 *)&command[10]) = cpu_to_be16(crc16_calculate(&command[12], command_len - 12)); + return dloader_send_command(command, command_len, common_ack, sizeof(common_ack)); +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +int dloader_command_exec(unsigned int addr) +{ + unsigned short command_len = 16; + + char command[16] = {0x7E,0x7E,0x7E,0x7E, /* head */ + 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, /*command type */ + 0x43, 0x63, /*command len */ + 0x00, 0x00, 0x10, 0x00 /*addr*/ + }; + *((u32 *)&command[12]) = addr; + *((u16 *)&command[10]) = crc16_calculate(&command[12], 4); + return dloader_send_command(command, command_len, exec_ack, sizeof(exec_ack)); +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +int dloader_setup_usb_connection(struct usb_port_struct *port) +{ + int ret; + + dloader_command_client_probe(); + if (ret < 0) { + dev_err(&port->udev->dev, "get version error\n"); + return ret; + } + dloader_command_connect(); + if (ret < 0) { + dev_err(&port->udev->dev, "connection error\n"); + return ret; + } + dev_info(&port->udev->dev,"dloader connect susscess...\n"); + return 0; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +int dloader_execute_image(struct usb_port_struct *port,unsigned int addr) +{ + int ret; + + ret = dloader_command_exec(addr); + if (ret < 0) { + dev_err(&port->udev->dev, "exec command is error\n"); + return ret; + } + return 0; +} +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author:junwei.jiang + *Date: + *Modify: + * ************************************************************************/ +static unsigned int dloader_send_pdata(char* buf, const void *pdata, unsigned int len) +{ + PACKET_T *packet_ptr = (PACKET_T *)buf; + int command_len = len + PACKET_HEADER_SIZE; + + packet_ptr->magic = PACKET_MAGIC; + packet_ptr->type = 0x0002; + packet_ptr->size = len; + packet_ptr->crc = 0x0000; + memset(packet_ptr->content, 0 , len); + memcpy(packet_ptr->content,pdata, len); + + //crc check sum + packet_ptr->crc = cpu_to_be16(crc16_calculate((char*)(&(packet_ptr->content)), command_len)); + + return command_len; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +int usb_download_image(struct usb_port_struct *port, unsigned int addr, unsigned int len) +{ + int ret; + int size; + int offset = 0; + int img_size = 0; + int temp_size = 0; + + /*the first command connect*/ + ret = dloader_command_start_download(addr, len); + if (ret < 0) { + dev_err(&port->udev->dev,"start download command failed\n"); + return ret; + } + /*get the data and the sv6160.bin size*/ + img_size = len; + + while (img_size > 0) { + temp_size = MIN(dl_mps, img_size-offset); + if(!temp_size) + return 0; + size = dloader_send_pdata(port->read_buffer, (void*)(firmware_data)+offset, temp_size); + if (size%512==0 && temp_size < dl_mps) { + temp_size = temp_size>>1; + size = dloader_send_pdata(port->read_buffer, (void*)(firmware_data)+offset, temp_size); + } + ret = dloader_send_data(port->read_buffer, size, common_ack, sizeof(common_ack)); + if (ret < 0) { + dev_err(&port->udev->dev, "donwload img error\n"); + return ret; + } + offset += temp_size; + } + return 0; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +int dloader_get_chip_id(void *buf, unsigned int buf_size) +{ + int len = strlen(usb_ports[0]->udev->product); + memcpy(buf, usb_ports[0]->udev->product, strlen(usb_ports[0]->udev->product)); + return len; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +int dloader_dump_from_romcode_usb(unsigned int addr, void *buf, int len) +{ + int ret; + unsigned short command_len = 16; + char command[20] = {0x7E, 0x7E, 0x7E, 0x7E,/* head */ + 0x08, 0x00, 0x00, 0x00, /*for command data len */ + 0x00, 0x09, /*command type */ + 0x00, 0x00, /*for crc*/ + 0x00, 0x00, 0x00, 0x00,/*addr*/ + 0x00, 0x00, 0x00, 0x00,/*data len*/ + }; + int actual_len = 0; + int size; + + //*((u32 *)&command[12]) = cpu_to_be32(addr); + //*((u32 *)&command[16]) = cpu_to_be32(len); + *((u32 *)&command[12]) = addr; + *((u32 *)&command[16]) = len; + + *((u16 *)&command[10]) = cpu_to_be16(crc16_calculate(&command[1], command_len - 4)); + + + ret = dloader_send_command(command, command_len, NULL, 0); + if (ret < 0) { + skw_usb_err(" send command error\n"); + return -EIO; + } + + size = dl_mps; + while(len > 0) { + if (len < size) + size = len; + ret = dloader_read(buf, size, &actual_len, 3000); + if (ret < 0) + skw_usb_err("dloader_read_ack dump memory error\n"); + else len -= actual_len; + } + return ret; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static int dloader_dump_read_usb(struct usb_port_struct *port) +{ + int ret; + ret = dloader_dump_from_romcode_usb(START_ADDR, port->read_buffer, MAX_IMAGE_SIZE); + return ret; +} + +/*************************************************************************** + * Description: + *Seekwave tech LTD + *Author: + *Date: + *Modify: + * ************************************************************************/ +static void dloader_work(struct work_struct *work) +{ + struct usb_port_struct *port = container_of(work, struct usb_port_struct, work); + int ret; + dloader_port = port->portno; + dloader_setup_usb_connection(port); + + ret = check_modem_status_from_connect_message(); + if (ret == HANG_REBOOT){ + dloader_dump_read_usb(port); + } + if(usb_boot_data->dram_dl_size > 0){ + firmware_data = usb_boot_data->dram_img_data; + ret = usb_download_image(port, usb_boot_data->dram_dl_addr, usb_boot_data->dram_dl_size); + if(ret <0) + skw_usb_warn(" dram download img fail !!!!\n"); + } + + if(!ret && usb_boot_data->iram_dl_size > 0){ + firmware_data = usb_boot_data->iram_img_data; + ret = usb_download_image(port, usb_boot_data->iram_dl_addr, usb_boot_data->iram_dl_size); + } + modem_notify_event(DEVICE_BOOTUP_EVENT); + if (!ret) + ret = dloader_execute_image(port, START_ADDR); + + if (ret < 0 && chip_en_gpio >= 0) { + modem_status = MODEM_DOWNLOAD_FAILED; + skw_usb_info("download failed! power off device\n"); + gpio_set_value(chip_en_gpio, 0); + msleep(10); + } +} diff --git a/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/usb_boot.h b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/usb_boot.h new file mode 100755 index 0000000..e50180e --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/misc/seekwaveplatform_lite/usb/usb_boot.h @@ -0,0 +1,94 @@ +#define MAX_PACKET_SIZE 0x400 //4K +#define PACKET_MAGIC 0x7e7e7e7e +/****************************************************************** + * **Description:download image,addrress and img size + * Seekwave tech LTD + * * StartDownload 0x0001 + * * + * * MAGIC 4B Length 4B MessageType 2B 2B CRC 0x7E7E7E7E + * ** + * ******************************************************************/ + +struct connect_ack { + unsigned int packet_size; + union packet_attr_tag + { + struct connect_attr_map + { + unsigned int check_sum :1; + unsigned int smp :1; + unsigned int boot :1; + unsigned int res0 :1; + unsigned int strapin :2; + unsigned int usb_sdio_dis :2; + unsigned int res1 :24; + }bitmap; + unsigned int dwValue; + }flags; + unsigned int chip_id[4]; +}; + +typedef struct PACKET_BODY_tag{ + unsigned int magic; //magic + unsigned int size; //length,length - 12 + unsigned short type; //type,the type defferent cmd + unsigned short crc; //checksum + unsigned char content[MAX_PACKET_SIZE]; +}PACKET_T; +#define PACKET_HEADER_SIZE (sizeof(struct PACKET_BODY_tag) - MAX_PACKET_SIZE) +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#define DEBUG +#define NORMAL_BOOT 0 +#define HANG_REBOOT 1 +#define START_ADDR 0x100000 +#define MAX_IMAGE_SIZE 0x7a000 + +static const char client_version[] = { 0x7E, 0x7E, 0x7E, 0x7E }; /* magic only to probe client */ +static const char client_version_ack[] = {0x7E, 0x7E, 0x7E, 0x7E,/* magic */ + 0x18, 0x00, 0x00, 0x00,/*size*/ + 0x81, 0x00, /*message type*/ + 0x00, 0x00, /* crc16 = 0 */ + 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x20, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x2e, 0x30, 0x00}; + +static const char connect[] = {0x7E, 0x7E, 0x7E, 0x7E,/* magic */ + 0x18, 0x00, 0x00, 0x00,/*size*/ + 0x00, 0x00, /* message type, 0: connect command */ + 0x00, 0x00, /*crc16*/ + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static char connect_ack[] = {0x7E, 0x7E, 0x7E, 0x7E,/* magic */ + 0x18, 0x00, 0x00, 0x00,/*size*/ + 0x80, 0x00, /*message type*/ + 0x00, 0x00, /* crc16 */ + 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x53, 0x56, 0x36, 0x31, + 0x36, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const char common_ack[] = { 0x7e, 0x7e, 0x7e, 0x7e, /* magic */ + 0x00, 0x00, 0x00, 0x00,/*size*/ + 0x80, 0x00, /*message type*/ + 0x00, 0x00};/* crc16 */ + +static const char exec_ack[] = { 0x7e, 0x7e, 0x7e, 0x7e, + 0x00, 0x00, 0x00, 0x00,/*size*/ + 0x80, 0x00,/*message type*/ + 0x00, 0x00};/* crc16 */ + +static int dloader_send_command(const char *command, int command_len, const char *ack, int ack_len); + +#define dloader_command_client_probe() \ + do { \ + ret = dloader_send_command(client_version, sizeof(client_version), \ + client_version_ack, sizeof(client_version_ack)); \ + }while(0) + +#define dloader_command_connect() \ + do { \ + ret = dloader_send_command(connect, sizeof(connect), connect_ack, sizeof(connect_ack)); \ + }while(0) diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/Kconfig b/longan/kernel/linux-4.9/drivers/net/wireless/Kconfig index eae7ede..e31f1f3 100644 --- a/longan/kernel/linux-4.9/drivers/net/wireless/Kconfig +++ b/longan/kernel/linux-4.9/drivers/net/wireless/Kconfig @@ -126,4 +126,5 @@ This option adds support for ethernet connections to appear as if they are wifi connections through a special rtnetlink device. +source "drivers/net/wireless/swt6621s_wifi/Kconfig" endif # WLAN diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/Makefile b/longan/kernel/linux-4.9/drivers/net/wireless/Makefile index 68da845..a4a6442 100644 --- a/longan/kernel/linux-4.9/drivers/net/wireless/Makefile +++ b/longan/kernel/linux-4.9/drivers/net/wireless/Makefile @@ -36,3 +36,4 @@ obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800/ obj-$(CONFIG_RTL8723DU) += rtl8723du/ obj-$(CONFIG_RTL8822BS) += rtl8822bs/ +obj-$(CONFIG_WLAN_VENDOR_SWT6621S) += swt6621s_wifi/ diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/.gitignore b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/.gitignore new file mode 100755 index 0000000..6702033 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/.gitignore @@ -0,0 +1 @@ +version.h diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/Kconfig b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/Kconfig new file mode 100755 index 0000000..46ccf93 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/Kconfig @@ -0,0 +1,145 @@ +config WLAN_VENDOR_SWT6621S + tristate "SWT6621S chipset support" + depends on CFG80211 + select CFG80211_WEXT_EXPORT + help + This module adds support for SeekWave 802.11ax wireless adapter chipset. + + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + questions about these cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_SWT6621S +config SWT6621S_LEGACY_P2P + bool "SWT6621S Legacy P2P" + default n + help + Say Y here to create interface p2p0 default + +config SWT6621S_REPEATER_MODE + bool "SWT6621S Repeater Mode" + default n + help + Say Y here to support repeater mode + +config SWT6621S_EXTERNAL_REGDB + bool "Use External Regdomain Database" + default n + help + Say Y here disable regdomain database in driver + +config SWT6621S_TDLS + bool "SWT6621S TDLS" + default n + help + Say Y here to enable TDLS + +config SWT6621S_DFS_MASTER + bool "SWT6621S DFS Master" + depends on SWT6621S_EXTERNAL_REGDB + default n + help + Say Y here to support DFS master + +config SWT6621S_OFFCHAN_TX + bool "Seekwave OFFCHAN TX" + default n + help + Say Y here to support off-channel TX + +config SWT6621S_6GHZ + bool "Seekwave 6GHz" + default n + help + Say Y here to support 6GHz + +config SWT6621S_USB3_WORKAROUND + bool "SWT6621S USB 3.0 workaround" + default n + help + Say Y here to enable workaround for USB 3.0 + +config SWT6621S_RX_REORDER_TIMEOUT + int "SWT6621S reorder timeout (50-500)" + range 50 500 + default 100 + +config SWT6621S_SKB_RECYCLE + bool "SWT6621S skb recycle" + default n + help + Say Y here to support skb recycle + +config SWT6621S_DEFAULT_COUNTRY + string "SWT6621S Default Country Code" + default "" + help + Set Default Country Code + +config SWT6621S_CHIP_ID + string "SWT6621S Chip ID" + default "" + help + Set Chip ID + +config SWT6621S_PROJECT_NAME + string "SWT6621S Default Project Name" + default "SEEKWAVE" + help + Set Default Project Name + +config SWT6621S_NOT_WAKEUP_HOST + bool "SWT6621S Wakeup Host Enable" + default n + help + Say Y here to support wakeup host + +config SWT6621S_WDS + bool "SWT6621S Wds Support" + default n + help + Say Y here to support WDS + +config SWT6621S_PRESUSPEND_SUPPORT + bool "SWT6621S Presuspend Support" + default n + help + Say Y here to support presuspend + +choice + prompt "SWT6621S Log Level" + default SWT6621S_LOG_DEBUG + help + SEEKWAVE Log Level + +config SWT6621S_LOG_ERROR + bool "ERROR" + help + This feature set Error log level + +config SWT6621S_LOG_WARN + bool "WARN" + help + This feature set Warnning log level + +config SWT6621S_LOG_INFO + bool "INFO" + help + This feature set INFO log level + +config SWT6621S_LOG_DEBUG + bool "DEBUG" + help + This feature set DEBUG log level + +config SWT6621S_LOG_DETAIL + bool "DETAIL" + help + This feature set DETAIL log level + +endchoice + +endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/Makefile b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/Makefile new file mode 100755 index 0000000..98b10af --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/Makefile @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_WLAN_VENDOR_SWT6621S) += swt6621s_wifi.o + +swt6621s_wifi-objs += skw_core.o +swt6621s_wifi-objs += skw_iface.o +swt6621s_wifi-objs += skw_cfg80211.o +swt6621s_wifi-objs += skw_msg.o +swt6621s_wifi-objs += skw_rx.o +swt6621s_wifi-objs += skw_tx.o +swt6621s_wifi-objs += skw_regd.o +swt6621s_wifi-objs += skw_mlme.o +swt6621s_wifi-objs += skw_timer.o +swt6621s_wifi-objs += skw_log.o +swt6621s_wifi-objs += skw_iw.o +swt6621s_wifi-objs += skw_work.o +swt6621s_wifi-objs += skw_mbssid.o +swt6621s_wifi-objs += skw_dentry.o +swt6621s_wifi-objs += skw_util.o +swt6621s_wifi-objs += skw_config.o +swt6621s_wifi-objs += skw_recovery.o +swt6621s_wifi-objs += skw_vendor.o +swt6621s_wifi-objs += trace.o +swt6621s_wifi-objs += skw_db.o + +# swt6621s_wifi-$(CONFIG_SWT6621S_CALIB_DPD) += skw_calib.o +# swt6621s_wifi-$(CONFIG_SWT6621S_EDMA) += skw_edma.o +swt6621s_wifi-$(CONFIG_SWT6621S_DFS_MASTER) += skw_dfs.o +swt6621s_wifi-$(CONFIG_SWT6621S_TDLS) += skw_tdls.o + +ccflags-y += -DCONFIG_SWT6621S_STA_SME_EXT +ccflags-y += -DCONFIG_SWT6621S_SAP_SME_EXT +ccflags-y += -DCONFIG_SWT6621S_SCAN_RANDOM_MAC +ccflags-y += -DCONFIG_SWT6621S_TX_WORKQUEUE +ccflags-y += -DCONFIG_SWT6621S_HIGH_PRIORITY +ccflags-y += -DCONFIG_SWT6621S_CALIB_APPEND_BUS_ID +ccflags-y += -DCONFIG_SWT6621S_CALIB_APPEND_MODULE_ID + +ifneq ($(CONFIG_SWT6621S_EXTERNAL_REGDB),y) +ccflags-y += -DCONFIG_SWT6621S_REGD_SELF_MANAGED +endif + +ifeq ($(CONFIG_ARCH_ROCKCHIP),y) +ccflags-y += -DCONFIG_PLATFORM_ROCKCHIP +endif + +ifneq ($(filter y,$(CONFIG_ANDROID_BINDER_IPC) $(CONFIG_SKW_ANDROID)),) +ccflags-y += -D__SKW_ANDROID__ +endif + +ifneq ($(skw_extra_flags),) +ccflags-y += $(skw_extra_flags) +endif + +ifneq ($(skw_extra_symbols),) +KBUILD_EXTRA_SYMBOLS += $(skw_extra_symbols) +endif + +ccflags-y += -I$(srctree)/include/linux/platform_data/ + +CFLAGS_trace.o := -I$(src) +$(obj)/skw_core.o : $(obj)/version.h + +skw_abs_path := $(addprefix $(abspath $(srctree))/,$(filter-out /%,$(src)))$(filter /%,$(src)) +$(obj)/version.h: $(skw_abs_path)/genver.pl + @$(PERL) -s $(skw_abs_path)/genver.pl $@ + +clean-files := version.h diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/genver.pl b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/genver.pl new file mode 100755 index 0000000..b116cf6 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/genver.pl @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0 +#!/usr/bin/perl -s + +use POSIX qw(strftime); +use File::Basename; + +$skw_branch = "swt6621s_dev"; +$skw_version = "2.0.250611.022f652"; + +$output = shift; +open (OUTPUT, ">$output") || die "$0 : can't open $output for writing\n"; + +print OUTPUT "#ifndef __SKW_VERSION_H__\n"; +print OUTPUT "#define __SKW_VERSION_H__\n"; + +print OUTPUT "\n"; + +print OUTPUT "#define SKW_BRANCH \"$skw_branch\"\n"; +print OUTPUT "#define SKW_VERSION \"$skw_version\"\n"; + +print OUTPUT "\n"; + +print OUTPUT "#endif"; + +close (OUTPUT); diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_calib.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_calib.c new file mode 100755 index 0000000..9630ffa --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_calib.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/crc32c.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/firmware.h> + +#include "skw_core.h" +#include "skw_msg.h" +#include "skw_log.h" +#include "skw_cfg80211.h" +#include "skw_util.h" +#include "skw_calib.h" + +static int skw_dpd_chn_to_index(u16 chn, u8 bw) +{ + int index = 0; + + switch (chn) { + case 1 ... 5: + /* these ch cp use ch3 instead */ + if (bw == 0) + index = 2; + else + index = 67; + break; + + case 6 ... 9: + /* these ch cp use ch7 instead */ + if (bw == 0) + index = 6; + else + index = 68; + break; + + case 10 ... 14: + /* these ch cp use ch11 instead */ + if (bw == 0) + index = 10; + else + index = 69; + break; + + case 36 ... 48: + index = ((chn - 36) >> 1) + 14; + break; + + case 52 ... 64: + index = ((chn - 52) >> 1) + 21; + break; + + case 100 ... 144: + index = ((chn - 100) >> 1) + 28; + break; + + case 149 ... 177: + index = ((chn - 149) >> 1) + 51; + break; + + default: + index = -1; + break; + } + + return index; +} + +int skw_dpd_set_coeff_params(struct wiphy *wiphy, + struct net_device *ndev, u8 chn, u8 center_chan, + u8 center_chan2, u8 bandwidth) +{ + + int ret = 0; + int index; + struct skw_core *skw = NULL; + struct skw_rf_rxdpd_data *para; + struct skw_rf_rxdpd_param param; + + skw_dbg("chan: %d, center_chan: %d, center_chan2: %d, band width: %d\n", + chn, center_chan, center_chan2, bandwidth); + + skw = wiphy_priv(wiphy); + if (!skw) { + skw_err("skw->dpd skw null"); + return -EINVAL; + } + + para = skw->dpd.resource; + if (!para) { + skw_err("skw->dpd.resource null"); + return -EINVAL; + } + + param.size = 2 * sizeof(struct skw_rf_rxdpd_train); + + index = skw_dpd_chn_to_index(center_chan, bandwidth); + skw_dbg("ch_idx:%d\n", index); + if (index < 0 || index > DPD_CHAN_CNT - 1) { + skw_err("chn %d not found\n", center_chan); + return -ERANGE; + } + + memcpy(¶m.train[0], ¶->data[index][0], + sizeof(struct skw_rf_rxdpd_train) * 2); + + if (center_chan <= 14) { + param.train[0].chan = center_chan; + param.train[1].chan = center_chan; + } + + skw_hex_dump("dpdresultcmd", ¶m, + sizeof(struct skw_rf_rxdpd_param), false); + + ret = skw_send_msg(wiphy, ndev, SKW_CMD_SET_DPD_RESULT, + ¶m, sizeof(param), NULL, 0); + if (ret) + skw_err("Send dpd result failed, ret: %d", ret); + + return ret; +} + +int skw_dpd_result_handler(struct skw_core *skw, void *buf, int len) +{ + int index; + struct skw_rf_rxdpd_train *res = buf; + struct skw_rf_rxdpd_data *para; + + if (res->done) + skw_dbg("ch:%d rf:%d bw:%d done:%d\n", res->chan, + res->rf_idx, res->cbw, res->done); + + if (res->rf_idx > DPD_RF_CNT - 1) { + skw_err("invalid rf_idx: %d\n", res->rf_idx); + + skw_hw_assert(skw, false); + return -ERANGE; + } + + index = skw_dpd_chn_to_index(res->chan, res->cbw); + if (index < 0 || index > DPD_CHAN_CNT - 1) { + skw_err("chn %d not found\n", res->chan); + return -ERANGE; + } + + para = skw->dpd.resource; + if (!para) { + skw_err("skw->dpd.resource null"); + return -EINVAL; + } + + if (para->data[index][res->rf_idx].chan || + para->data[index][res->rf_idx].cbw) { + skw_hex_dump("havedata", ¶->data[index][res->rf_idx], + sizeof(struct skw_rf_rxdpd_train), true); + skw_err("ch:%d rf:%d cbw:%d index:%d had data\n", + res->chan, res->rf_idx, res->cbw, index); + skw_hw_assert(skw, false); + return -EBUSY; + } + + memcpy(¶->data[index][res->rf_idx], res, + sizeof(struct skw_rf_rxdpd_train)); + + skw_hex_dump("data", ¶->data[index][res->rf_idx], + sizeof(struct skw_rf_rxdpd_train), false); + return 0; +} + +int skw_dpd_init(struct skw_dpd *dpd) +{ + dpd->size = sizeof(struct skw_rf_rxdpd_data); + + dpd->resource = SKW_ZALLOC(dpd->size, GFP_KERNEL); + if (!dpd->resource) { + skw_err("malloc dpd resource failed, size: %d\n", + dpd->size); + return -ENOMEM; + } + + return 0; +} + +void skw_dpd_zero(struct skw_dpd *dpd) +{ + memset(dpd->resource, 0, dpd->size); +} + +void skw_dpd_deinit(struct skw_dpd *dpd) +{ + SKW_KFREE(dpd->resource); +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_calib.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_calib.h new file mode 100755 index 0000000..afed862 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_calib.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_CALIB_H__ +#define __SKW_CALIB_H__ + +#include <linux/ieee80211.h> +#include <net/cfg80211.h> + +#define DPD_CHAN_CNT 70 +#define DPD_RF_CNT 2 + +struct skw_dpd { + void *resource; + int size; +}; + +#ifdef CONFIG_SWT6621S_CALIB_DPD +struct skw_rf_rxdpd_coeff { + u32 low_dpd_lut_coeff[4][12]; + u32 high_dpd_lut_coeff[4][16]; +} __packed; + +struct skw_rf_rxdpd_train { + u16 chan; + u8 done:1; + u8 rf_idx:3; + u8 cbw:4; + u8 dpd_rf_gain_max; + u8 delta_gain_q2[8]; + u32 dpd_tpc_info[8]; + struct skw_rf_rxdpd_coeff dpd_coeff; +} __packed; + +struct skw_rf_rxdpd_data { + struct skw_rf_rxdpd_train data[DPD_CHAN_CNT][DPD_RF_CNT]; +} __packed; + +struct skw_rf_rxdpd_param { + u32 size; + struct skw_rf_rxdpd_train train[2]; +} __packed; + +int skw_dpd_set_coeff_params(struct wiphy *wiphy, struct net_device *ndev, + u8 chn, u8 center_chan, + u8 center_chan2, u8 bandwidth); + +int skw_dpd_result_handler(struct skw_core *skw, void *buf, int len); +int skw_dpd_init(struct skw_dpd *dpd); +void skw_dpd_deinit(struct skw_dpd *dpd); +void skw_dpd_zero(struct skw_dpd *dpd); +#else +static inline int skw_dpd_set_coeff_params(struct wiphy *wiphy, + struct net_device *ndev, u8 chn, u8 center_chan, + u8 center_two_chan2, u8 bandwidth) +{ + return 0; +} + +static inline int skw_dpd_result_handler(struct skw_core *skw, void *buf, int len) +{ + return 0; +} + +static inline int skw_dpd_init(struct skw_dpd *dpd) +{ + return 0; +} + +static inline void skw_dpd_deinit(struct skw_dpd *dpd) +{ +} + +static inline void skw_dpd_zero(struct skw_dpd *dpd) +{ +} + +#endif +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_cfg80211.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_cfg80211.c new file mode 100755 index 0000000..2146a27 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_cfg80211.c @@ -0,0 +1,6186 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/ieee80211.h> +#include <net/cfg80211.h> +#include <linux/inetdevice.h> +#include <net/addrconf.h> +#include <linux/if_tunnel.h> + +#include "skw_core.h" +#include "skw_iface.h" +#include "skw_msg.h" +#include "skw_cfg80211.h" +#include "skw_regd.h" +#include "skw_mlme.h" +#include "skw_timer.h" +#include "skw_work.h" +#include "skw_tdls.h" +#include "skw_calib.h" +#include "skw_recovery.h" +#include "skw_dfs.h" + +#define SKW_BIT_ULL(nr) (1ULL << (nr)) + +int to_skw_bw(enum nl80211_chan_width bw) +{ + switch (bw) { + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: + return SKW_CHAN_WIDTH_20; + + case NL80211_CHAN_WIDTH_40: + return SKW_CHAN_WIDTH_40; + + case NL80211_CHAN_WIDTH_80: + return SKW_CHAN_WIDTH_80; + + case NL80211_CHAN_WIDTH_80P80: + return SKW_CHAN_WIDTH_80P80; + + case NL80211_CHAN_WIDTH_160: + return SKW_CHAN_WIDTH_160; + + default: + break; + } + + return SKW_CHAN_WIDTH_MAX; +} + +static int to_skw_gtk(u8 key_index) +{ + switch (key_index) { + case 0 ... 3: + return SKW_KEY_TYPE_GTK; + case 4 ... 5: + return SKW_KEY_TYPE_IGTK; + case 6: + return SKW_KEY_TYPE_BIGTK; + default: + break; + } + + return SKW_KEY_TYPE_GTK; +} + +static int to_skw_cipher_type(u32 cipher) +{ +#define SKW_CASE_CIPHER_TYPE(c) \ + { \ + case SKW_CIPHER_SUITE_##c: \ + return SKW_CIPHER_TYPE_##c; \ + } + + switch (cipher) { + SKW_CASE_CIPHER_TYPE(WEP40); + SKW_CASE_CIPHER_TYPE(WEP104); + SKW_CASE_CIPHER_TYPE(SMS4); + SKW_CASE_CIPHER_TYPE(TKIP); + SKW_CASE_CIPHER_TYPE(CCMP); + SKW_CASE_CIPHER_TYPE(CCMP_256); + SKW_CASE_CIPHER_TYPE(AES_CMAC); + SKW_CASE_CIPHER_TYPE(BIP_CMAC_256); + SKW_CASE_CIPHER_TYPE(BIP_GMAC_128); + SKW_CASE_CIPHER_TYPE(BIP_GMAC_256); + SKW_CASE_CIPHER_TYPE(GCMP); + SKW_CASE_CIPHER_TYPE(GCMP_256); + + default: + break; + } +#undef SKW_CASE_CIPHER_TYPE + + return SKW_CIPHER_TYPE_INVALID; +} + +static const struct ieee80211_iface_limit skw_iface_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 10) + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE), + }, +#endif +}; + +static const struct ieee80211_iface_limit skw_iface_limits_change[] = { + { + .max = 3, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 10) + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE), + }, +#endif +}; + +static const struct ieee80211_iface_limit skw_iface_limits_aps[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, + { + .max = 2, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_limit skw_iface_limits_monitor[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_MONITOR), + }, +}; + +#ifdef CONFIG_SWT6621S_DFS_MASTER +static const struct ieee80211_iface_limit skw_iface_limits_dfs[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_limit skw_iface_limits_dfs_change[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, +}; +#endif + +static const struct ieee80211_iface_combination skw_iface_combos[] = { + { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 10) + .max_interfaces = 4, +#else + .max_interfaces = 3, +#endif + .num_different_channels = 2, + .limits = skw_iface_limits, + .n_limits = ARRAY_SIZE(skw_iface_limits), + }, + { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 10) + .max_interfaces = 4, +#else + .max_interfaces = 3, +#endif + .num_different_channels = 2, + .limits = skw_iface_limits_change, + .n_limits = ARRAY_SIZE(skw_iface_limits_change), + }, + { + .max_interfaces = 3, + .num_different_channels = 1, + .limits = skw_iface_limits_aps, + .n_limits = ARRAY_SIZE(skw_iface_limits_aps), + }, + { + .max_interfaces = 2, + .num_different_channels = 1, + .limits = skw_iface_limits_monitor, + .n_limits = ARRAY_SIZE(skw_iface_limits_monitor), + }, +#ifdef CONFIG_SWT6621S_DFS_MASTER + { + .max_interfaces = 2, + .num_different_channels = 1, + .limits = skw_iface_limits_dfs, + .n_limits = ARRAY_SIZE(skw_iface_limits_dfs), + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), + }, + { + .max_interfaces = 2, + .num_different_channels = 1, + .limits = skw_iface_limits_dfs_change, + .n_limits = ARRAY_SIZE(skw_iface_limits_dfs_change), + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), + }, +#endif +}; + +static const struct +ieee80211_txrx_stypes skw_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_ADHOC] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_STATION] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4), + }, + [NL80211_IFTYPE_AP] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4), + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4), + }, + [NL80211_IFTYPE_P2P_DEVICE] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, +}; + +#define SKW_CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel skw_2ghz_chan[] = { + SKW_CHAN2G(1, 2412, 0), + SKW_CHAN2G(2, 2417, 0), + SKW_CHAN2G(3, 2422, 0), + SKW_CHAN2G(4, 2427, 0), + SKW_CHAN2G(5, 2432, 0), + SKW_CHAN2G(6, 2437, 0), + SKW_CHAN2G(7, 2442, 0), + SKW_CHAN2G(8, 2447, 0), + SKW_CHAN2G(9, 2452, 0), + SKW_CHAN2G(10, 2457, 0), + SKW_CHAN2G(11, 2462, 0), + SKW_CHAN2G(12, 2467, 0), + SKW_CHAN2G(13, 2472, 0), + SKW_CHAN2G(14, 2484, 0), +}; +#undef SKW_CHAN2G + +#define SKW_CHAN5G(_channel, _flags) { \ + .band = NL80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel skw_5ghz_chan[] = { + SKW_CHAN5G(36, 0), + SKW_CHAN5G(40, 0), + SKW_CHAN5G(44, 0), + SKW_CHAN5G(48, 0), + SKW_CHAN5G(52, 0), + SKW_CHAN5G(56, 0), + SKW_CHAN5G(60, 0), + SKW_CHAN5G(64, 0), + SKW_CHAN5G(100, 0), + SKW_CHAN5G(104, 0), + SKW_CHAN5G(108, 0), + SKW_CHAN5G(112, 0), + SKW_CHAN5G(116, 0), + SKW_CHAN5G(120, 0), + SKW_CHAN5G(124, 0), + SKW_CHAN5G(128, 0), + SKW_CHAN5G(132, 0), + SKW_CHAN5G(136, 0), + SKW_CHAN5G(140, 0), + SKW_CHAN5G(144, 0), + SKW_CHAN5G(149, 0), + SKW_CHAN5G(153, 0), + SKW_CHAN5G(157, 0), + SKW_CHAN5G(161, 0), + SKW_CHAN5G(165, 0), + SKW_CHAN5G(169, 0), + SKW_CHAN5G(173, 0), + SKW_CHAN5G(177, 0), + SKW_CHAN5G(181, 0), +}; +#undef SKW_CHAN5G + +#ifdef CONFIG_SWT6621S_6GHZ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +#define SKW_CHAN6G(_channel, _flags) { \ + .band = NL80211_BAND_6GHZ, \ + .center_freq = 5950 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel skw_6ghz_chan[] = { + SKW_CHAN6G(1, 0), + SKW_CHAN6G(2, 0), + SKW_CHAN6G(5, 0), + SKW_CHAN6G(9, 0), + SKW_CHAN6G(13, 0), + SKW_CHAN6G(17, 0), + SKW_CHAN6G(21, 0), + SKW_CHAN6G(25, 0), + SKW_CHAN6G(29, 0), + SKW_CHAN6G(33, 0), + SKW_CHAN6G(37, 0), + SKW_CHAN6G(41, 0), + SKW_CHAN6G(45, 0), + SKW_CHAN6G(49, 0), + SKW_CHAN6G(53, 0), + SKW_CHAN6G(57, 0), + SKW_CHAN6G(61, 0), + SKW_CHAN6G(65, 0), + SKW_CHAN6G(69, 0), + SKW_CHAN6G(73, 0), + SKW_CHAN6G(77, 0), + SKW_CHAN6G(81, 0), + SKW_CHAN6G(85, 0), + SKW_CHAN6G(89, 0), + SKW_CHAN6G(93, 0), + SKW_CHAN6G(97, 0), + SKW_CHAN6G(101, 0), + SKW_CHAN6G(105, 0), + SKW_CHAN6G(109, 0), + SKW_CHAN6G(113, 0), + SKW_CHAN6G(117, 0), + SKW_CHAN6G(121, 0), + SKW_CHAN6G(125, 0), + SKW_CHAN6G(129, 0), + SKW_CHAN6G(133, 0), + SKW_CHAN6G(137, 0), + SKW_CHAN6G(141, 0), + SKW_CHAN6G(145, 0), + SKW_CHAN6G(149, 0), + SKW_CHAN6G(153, 0), + SKW_CHAN6G(157, 0), + SKW_CHAN6G(161, 0), + SKW_CHAN6G(165, 0), + SKW_CHAN6G(169, 0), + SKW_CHAN6G(173, 0), + SKW_CHAN6G(177, 0), + SKW_CHAN6G(181, 0), + SKW_CHAN6G(185, 0), + SKW_CHAN6G(189, 0), + SKW_CHAN6G(193, 0), + SKW_CHAN6G(197, 0), + SKW_CHAN6G(201, 0), + SKW_CHAN6G(205, 0), + SKW_CHAN6G(209, 0), + SKW_CHAN6G(213, 0), + SKW_CHAN6G(217, 0), + SKW_CHAN6G(221, 0), + SKW_CHAN6G(225, 0), + SKW_CHAN6G(229, 0), + SKW_CHAN6G(233, 0), +}; +#undef SKW_CHAN6G +#endif +#endif + +#define SKW_RATETAB_ENT(_rate, _rateid, _flags) \ +{ \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ +} + +static struct ieee80211_rate skw_rates[] = { + SKW_RATETAB_ENT(10, 0x1, 0), + SKW_RATETAB_ENT(20, 0x2, 0), + SKW_RATETAB_ENT(55, 0x5, 0), + SKW_RATETAB_ENT(110, 0xb, 0), + SKW_RATETAB_ENT(60, 0x6, 0), + SKW_RATETAB_ENT(90, 0x9, 0), + SKW_RATETAB_ENT(120, 0xc, 0), + SKW_RATETAB_ENT(180, 0x12, 0), + SKW_RATETAB_ENT(240, 0x18, 0), + SKW_RATETAB_ENT(360, 0x24, 0), + SKW_RATETAB_ENT(480, 0x30, 0), + SKW_RATETAB_ENT(540, 0x36, 0), +}; + +#undef SKW_RATETAB_ENT + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +static const struct ieee80211_sband_iftype_data skw_he_capa_2ghz = { + .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = SKW_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = SKW_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + SKW_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = SKW_HE_MAC_CAP2_BSR | + SKW_HE_MAC_CAP2_MU_CASCADING | + SKW_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = SKW_HE_MAC_CAP3_OMI_CONTROL | + SKW_HE_MAC_CAP3_GRP_ADDR_MULTI_STA_BA_DL_MU | + SKW_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2, + .mac_cap_info[4] = SKW_HE_MAC_CAP4_AMDSU_IN_AMPDU, + .phy_cap_info[0] = SKW_HE_PHY_CAP0_DUAL_BAND | + SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G, + .phy_cap_info[1] = SKW_HE_PHY_CAP1_DEVICE_CLASS_A | + SKW_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + SKW_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + SKW_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = SKW_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + SKW_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + SKW_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + SKW_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + SKW_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xffff), + .tx_mcs_160 = cpu_to_le16(0xffff), + .rx_mcs_80p80 = cpu_to_le16(0xffff), + .tx_mcs_80p80 = cpu_to_le16(0xffff), + }, + }, +}; + +static const struct ieee80211_sband_iftype_data skw_he_capa_5ghz = { + .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = SKW_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = SKW_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + SKW_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = SKW_HE_MAC_CAP2_BSR | + SKW_HE_MAC_CAP2_MU_CASCADING | + SKW_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = SKW_HE_MAC_CAP3_OMI_CONTROL | + SKW_HE_MAC_CAP3_GRP_ADDR_MULTI_STA_BA_DL_MU | + SKW_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2, + .mac_cap_info[4] = SKW_HE_MAC_CAP4_AMDSU_IN_AMPDU, + + .phy_cap_info[0] = SKW_HE_PHY_CAP0_DUAL_BAND | + SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G, + .phy_cap_info[1] = SKW_HE_PHY_CAP1_DEVICE_CLASS_A | + SKW_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + SKW_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + SKW_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = SKW_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + SKW_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + SKW_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + SKW_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + SKW_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xffff), + .tx_mcs_160 = cpu_to_le16(0xffff), + .rx_mcs_80p80 = cpu_to_le16(0xffff), + .tx_mcs_80p80 = cpu_to_le16(0xffff), + }, + }, +}; + +#endif + +#define skw_a_rates (skw_rates + 4) +#define skw_a_rates_size 8 +#define skw_g_rates (skw_rates + 0) +#define skw_g_rates_size 12 + +static struct ieee80211_supported_band skw_band_2ghz = { + .channels = skw_2ghz_chan, + .n_channels = ARRAY_SIZE(skw_2ghz_chan), + .bitrates = skw_g_rates, + .n_bitrates = skw_g_rates_size, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + .n_iftype_data = 1, + .iftype_data = &skw_he_capa_2ghz, +#endif +}; + +static struct ieee80211_supported_band skw_band_5ghz = { + .channels = skw_5ghz_chan, + .n_channels = ARRAY_SIZE(skw_5ghz_chan), + .bitrates = skw_a_rates, + .n_bitrates = skw_a_rates_size, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + .n_iftype_data = 1, + .iftype_data = &skw_he_capa_5ghz, +#endif +}; + +#ifdef CONFIG_SWT6621S_6GHZ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +static struct ieee80211_supported_band skw_band_6ghz = { + .channels = skw_6ghz_chan, + .n_channels = ARRAY_SIZE(skw_6ghz_chan), + .bitrates = skw_a_rates, + .n_bitrates = skw_a_rates_size, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + .n_iftype_data = 1, + .iftype_data = &skw_he_capa_5ghz, //TBD:check it +#endif +}; +#endif +#endif + +static const u32 skw_cipher_suites[] = { + /* keep WEP first, it may be removed below */ + SKW_CIPHER_SUITE_WEP40, + SKW_CIPHER_SUITE_TKIP, + SKW_CIPHER_SUITE_CCMP, + SKW_CIPHER_SUITE_WEP104, + SKW_CIPHER_SUITE_AES_CMAC, + SKW_CIPHER_SUITE_GCMP, + + SKW_CIPHER_SUITE_CCMP_256, + SKW_CIPHER_SUITE_GCMP_256, + SKW_CIPHER_SUITE_BIP_CMAC_256, + SKW_CIPHER_SUITE_BIP_GMAC_128, + SKW_CIPHER_SUITE_BIP_GMAC_256, + + SKW_CIPHER_SUITE_SMS4, +}; + +#ifdef CONFIG_SWT6621S_WDS +static int skw_set_wds_mib(struct wiphy *wiphy, bool enable) +{ + return skw_util_set_mib_enable(wiphy, 0, + SKW_MIB_SET_WDS_ENABLE, enable); +} +#endif + +static int skw_set_once_noa_mib(struct wiphy *wiphy, u8 en, u8 pre_time, u8 abs_time) +{ + int ret; + u16 *plen; + struct skw_tlv_conf conf; + struct skw_once_noa_enable_mib once_noa; + + skw_dbg("once_noa: %d %d %d\n", en, pre_time, abs_time); + + once_noa.en = en; + once_noa.go_pre_time = pre_time; + once_noa.go_abs_time = abs_time; + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_ONCE_NOA_ENABLE, &once_noa, sizeof(once_noa))) { + skw_err("set once noa enable failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + *plen = conf.total_len; + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_warn("failed, ret: %d\n", ret); + + skw_tlv_free(&conf); + + return ret; +} + +static int skw_set_ratio_level_mib(struct wiphy *wiphy, u8 valid, u8 level) +{ + int ret; + u16 *plen; + struct skw_tlv_conf conf; + struct skw_noa_ratio_mib ratio; + + skw_dbg("ratio level: %d %d\n", valid, level); + + ratio.valid = valid; + ratio.ratio_lv = level; + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_NOA_RATIO_TYPE, &ratio, sizeof(ratio))) { + skw_err("set ratio level failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + *plen = conf.total_len; + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_warn("failed, ret: %d\n", ret); + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_dot11k_mib(struct wiphy *wiphy, struct net_device *dev, + bool enable) +{ + struct skw_iface *iface; + + if (!dev) { + skw_err("dev is null\n"); + + return -EINVAL; + } + + iface = netdev_priv(dev); + + return skw_util_set_mib_enable(wiphy, iface->id, + SKW_MIB_SET_11K_TLV_ID, enable); +} + +int skw_set_dot11v_mib(struct wiphy *wiphy, struct net_device *dev, + bool enable) +{ + struct skw_iface *iface; + + if (!dev) { + skw_err("dev is null\n"); + + return -EINVAL; + } + + iface = netdev_priv(dev); + + return skw_util_set_mib_enable(wiphy, iface->id, + SKW_MIB_SET_11V_TLV_ID, enable); +} + +int skw_set_dot11r_mib(struct wiphy *wiphy, struct net_device *dev, + bool enable) +{ + struct skw_iface *iface; + + if (!dev) { + skw_err("dev is null\n"); + + return -EINVAL; + } + + iface = netdev_priv(dev); + + return skw_util_set_mib_enable(wiphy, iface->id, + SKW_MIB_SET_11R_TLV_ID, enable); +} + +int skw_set_5ghz_band_mib(struct wiphy *wiphy, u32 band) +{ + int ret; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("5ghz_band: %d\n", band); + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_BAND_5G, &band, sizeof(band))) { + skw_err("set 5ghz band failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + *plen = conf.total_len; + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_warn("failed, ret: %d\n", ret); + + skw_tlv_free(&conf); + + return ret; +} + +static inline void skw_iftype_dump(int iftype_num[NUM_NL80211_IFTYPES]) +{ + int i; + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + if (iftype_num[i]) + skw_info("%s: %d\n", skw_iftype_name(i), iftype_num[i]); + } +} + +static void skw_count_iftype(struct wiphy *wiphy, int num[NUM_NL80211_IFTYPES]) +{ + int i; + struct skw_iface *iface; + struct skw_core *skw = wiphy_priv(wiphy); + + spin_lock_bh(&skw->vif.lock); + + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + if (!iface || + (iface->flags & SKW_IFACE_FLAG_LEGACY_P2P_DEV) || + (iface->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE)) + continue; + + num[iface->wdev.iftype]++; + } + + spin_unlock_bh(&skw->vif.lock); +} + +static struct wireless_dev *skw_add_virtual_intf(struct wiphy *wiphy, + const char *name, unsigned char name_assign_type, + enum nl80211_iftype type, u32 *flags, struct vif_params *params) +{ + int ret; + struct skw_iface *iface; + u8 vif_id = SKW_INVALID_ID; + int iftype_num[NUM_NL80211_IFTYPES] = {0}; + + skw_dbg("%s(%s), mac: %pM\n", name, skw_iftype_name(type), + params->macaddr); + + skw_count_iftype(wiphy, iftype_num); + ret = skw_compat_check_combs(wiphy, 0, 0, iftype_num); + if (ret) { + skw_err("check combinations failed, %s(%s)\n", + name, skw_iftype_name(type)); + + skw_iftype_dump(iftype_num); + + return ERR_PTR(-EINVAL); + } + + if (type == NL80211_IFTYPE_P2P_DEVICE) + vif_id = SKW_LAST_IFACE_ID; + + iface = skw_add_iface(wiphy, name, type, params->macaddr, vif_id, + type != NL80211_IFTYPE_P2P_DEVICE); + if (IS_ERR(iface)) { + skw_err("failed, %ld\n", PTR_ERR(iface)); + return ERR_CAST(iface); + } + + return &iface->wdev; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +static struct wireless_dev *skw_cfg80211_add_virtual_intf(struct wiphy *wiphy, + const char *name, unsigned char name_assign_type, + enum nl80211_iftype type, struct vif_params *params) +{ + u32 flags = 0; + + return skw_add_virtual_intf(wiphy, name, name_assign_type, + type, &flags, params); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +static struct wireless_dev *skw_cfg80211_add_virtual_intf(struct wiphy *wiphy, + const char *name, unsigned char name_assign_type, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + return skw_add_virtual_intf(wiphy, name, name_assign_type, + type, flags, params); +} +#else +static struct wireless_dev *skw_cfg80211_add_virtual_intf(struct wiphy *wiphy, + const char *name, enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + return skw_add_virtual_intf(wiphy, name, 0, type, flags, params); +} +#endif + +static int skw_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + + skw_dbg("iftype: %d, iface id: %d\n", wdev->iftype, iface->id); + + return skw_del_iface(wiphy, iface); +} + +static int skw_change_intf(struct wiphy *wiphy, struct net_device *dev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + u8 *mac; + int ret; + int iftype_num[NUM_NL80211_IFTYPES] = {0}; + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("%s (inst: %d), %s -> %s, mac: %pM, 4addr: %d, flags: 0x%x\n", + netdev_name(dev), iface->id, + skw_iftype_name(dev->ieee80211_ptr->iftype), + skw_iftype_name(type), params->macaddr, + params->use_4addr, iface->flags); + + if (iface->flags & SKW_IFACE_FLAG_LEGACY_P2P_DEV) + iface->wdev.iftype = type; + + if (iface->wdev.iftype == type) + return 0; + + skw_count_iftype(wiphy, iftype_num); + iftype_num[type]++; + iftype_num[iface->wdev.iftype]--; + ret = skw_compat_check_combs(wiphy, 0, 0, iftype_num); + if (ret) { + skw_err("check combinations failed, %s(inst: %d), %s -> %s\n", + netdev_name(dev), iface->id, + skw_iftype_name(dev->ieee80211_ptr->iftype), + skw_iftype_name(type)); + + skw_iftype_dump(iftype_num); + + return ret; + } + + if (iface->ndev) + netif_tx_stop_all_queues(dev); + + ret = skw_iface_teardown(wiphy, iface); + if (ret) { + skw_err("teardown failed, %s (inst: %d), ret: %d\n", + skw_iftype_name(iface->wdev.iftype), iface->id, ret); + + goto out; + } + +#ifdef CONFIG_SWT6621S_WDS + skw_set_wds_mib(wiphy, params->use_4addr); +#endif + + if (is_valid_ether_addr(params->macaddr)) + mac = params->macaddr; + else + mac = (u8 *)wdev_address(dev->ieee80211_ptr); + + ret = skw_iface_setup(wiphy, dev, iface, mac, type, iface->id); + if (ret) { + skw_err("open dev failed, %s (inst: %d)\n", + skw_iftype_name(type), iface->id); + + ret = skw_iface_setup(wiphy, dev, iface, iface->addr, + iface->wdev.iftype, iface->id); + if (ret) + skw_err("open dev failed, %s (inst: %d)\n", + skw_iftype_name(iface->wdev.iftype), iface->id); + } + +out: + if (iface->ndev) + netif_tx_start_all_queues(dev); + + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +static int skw_cfg80211_change_intf(struct wiphy *wiphy, struct net_device *dev, + enum nl80211_iftype type, struct vif_params *params) +{ + u32 flags = 0; + + return skw_change_intf(wiphy, dev, type, &flags, params); +} +#else +static int skw_cfg80211_change_intf(struct wiphy *wiphy, struct net_device *dev, + enum nl80211_iftype type, u32 *flags, struct vif_params *params) +{ + return skw_change_intf(wiphy, dev, type, flags, params); +} +#endif + +static int skw_get_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, + const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params *params)) +{ + skw_dbg("dev: %s, link: %d, key_idx: %d, pairwise: %d, mac: %pM\n", + netdev_name(dev), link_id, key_idx, pairwise, mac_addr); + + return 0; +} + +static int skw_cmd_add_key(struct wiphy *wiphy, struct net_device *dev, + int cipher, u8 key_idx, int key_type, + const u8 *key, int key_len, const u8 *addr) +{ + struct skw_key_params params; + struct skw_iface *iface = netdev_priv(dev); + u8 wapi_tx_pn[] = {0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c}; + + memset(¶ms, 0x0, sizeof(params)); + + if (addr) + skw_ether_copy(params.mac_addr, addr); + else + memset(params.mac_addr, 0xff, ETH_ALEN); + + memcpy(params.key, key, key_len); + + params.key_type = key_type; + params.key_len = key_len; + params.key_id = key_idx; + params.cipher_type = cipher; + params.pn[0] = 1; + + switch (cipher) { + case SKW_CIPHER_TYPE_SMS4: + memcpy(params.pn, wapi_tx_pn, SKW_PN_LEN); + + if (is_skw_ap_mode(iface)) + params.pn[0] += 1; + + break; + + case SKW_CIPHER_TYPE_TKIP: + if (is_skw_ap_mode(iface)) + memcpy(¶ms.key[0], key, 32); + else { + memcpy(¶ms.key[0], key, 16); + memcpy(¶ms.key[16], key + 24, 8); + memcpy(¶ms.key[24], key + 16, 8); + } + + break; + + default: + break; + } + + if (is_skw_ap_mode(iface) && iface->buf_keys_idx < SKW_MAX_BUF_KEYS && + !SKW_TEST(iface->flags, SKW_IFACE_FLAG_AP_STARTED)) { + memcpy(&iface->buf_keys[iface->buf_keys_idx++], + ¶ms, sizeof(params)); + SKW_SET(iface->flags, SKW_IFACE_FLAG_BUF_KEY); + skw_dbg("lead key buffered, idx: %d\n", iface->buf_keys_idx - 1); + return 0; + } else + return skw_send_msg(wiphy, dev, SKW_CMD_ADD_KEY, ¶ms, + sizeof(params), NULL, 0); +} + +static int skw_set_key(struct wiphy *wiphy, struct net_device *dev, + struct skw_key_conf *conf, u8 key_idx, int key_type, + const u8 *addr, struct key_params *params) +{ + int i, cipher, ret; + struct skw_key *key, *old_key; + + cipher = to_skw_cipher_type(params->cipher); + if (cipher == SKW_CIPHER_TYPE_INVALID) { + skw_warn("cipher 0x%x unsupported\n", params->cipher); + return -ENOTSUPP; + } + + key = SKW_ZALLOC(sizeof(struct skw_key), GFP_KERNEL); + if (!key) + return -ENOMEM; + + key->key_len = params->key_len; + memcpy(key->key_data, params->key, params->key_len); + + if (params->seq) { + skw_hex_dump("seq", params->seq, params->seq_len, false); + + for (i = 1; i < IEEE80211_NUM_TIDS; i++) + memcpy(key->rx_pn[i], params->seq, SKW_PN_LEN); + } + + conf->skw_cipher = cipher; + + old_key = rcu_dereference_protected(conf->key[key_idx], + lockdep_is_held(&conf->lock)); + + rcu_assign_pointer(conf->key[key_idx], key); + + SKW_SET(conf->installed_bitmap, BIT(key_idx)); + + if (old_key) + kfree_rcu(old_key, rcu); + + if (cipher == SKW_CIPHER_TYPE_WEP40 || + cipher == SKW_CIPHER_TYPE_WEP104) { + SKW_SET(conf->flags, SKW_KEY_FLAG_WEP_SHARE); + return 0; + } + + ret = skw_cmd_add_key(wiphy, dev, cipher, key_idx, key_type, + params->key, params->key_len, addr); + if (ret) { + RCU_INIT_POINTER(conf->key[key_idx], NULL); + SKW_CLEAR(conf->installed_bitmap, BIT(key_idx)); + kfree_rcu(key, rcu); + } + + return ret; +} + +static int skw_add_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, + const u8 *addr, struct key_params *params) +{ + const u8 *mac; + int ret, key_type; + struct skw_key_conf *conf; + struct skw_peer_ctx *ctx; + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("%s, key_idx: %d, cipher: 0x%x, pairwise: %d, mac: %pM\n", + netdev_name(dev), key_idx, params->cipher, pairwise, addr); + + key_type = pairwise ? SKW_KEY_TYPE_PTK : to_skw_gtk(key_idx); + + if (addr) { + ctx = skw_peer_ctx(iface, addr); + if (!ctx) { + skw_warn("%pM not linked\n", addr); + return -ENOLINK; + } + + skw_peer_ctx_lock(ctx); + + if (!ctx->peer) { + skw_peer_ctx_unlock(ctx); + return 0; + } + + if (pairwise) + conf = &ctx->peer->ptk_conf; + else + conf = &ctx->peer->gtk_conf; + + mutex_lock(&conf->lock); + + ret = skw_set_key(wiphy, dev, conf, key_idx, + key_type, addr, params); + + mutex_unlock(&conf->lock); + + skw_peer_ctx_unlock(ctx); + + } else { + if (is_skw_ap_mode(iface)) + mac = NULL; + else + mac = iface->sta.core.bss.bssid; + + conf = &iface->key_conf; + + mutex_lock(&conf->lock); + + ret = skw_set_key(wiphy, dev, conf, key_idx, + key_type, mac, params); + + mutex_unlock(&conf->lock); + } + + if (ret) + skw_err("failed, cipher: 0x%x, ptk: %d, idx: %d, ret: %d\n", + params->cipher, pairwise, key_idx, ret); + + return ret; +} + +static int skw_cmd_del_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, int key_type, int cipher, const u8 *addr) +{ + struct skw_key_params params; + + memset(¶ms, 0x0, sizeof(params)); + + if (addr) + skw_ether_copy(params.mac_addr, addr); + else + memset(params.mac_addr, 0xff, ETH_ALEN); + + params.key_type = key_type; + params.cipher_type = cipher; + params.key_id = key_idx; + + return skw_send_msg(wiphy, dev, SKW_CMD_DEL_KEY, ¶ms, + sizeof(params), NULL, 0); +} + +static int skw_remove_key(struct wiphy *wiphy, struct net_device *dev, + struct skw_key_conf *conf, u8 key_idx, + int key_type, const u8 *addr) +{ + int ret; + struct skw_key *key; + + if (SKW_TEST(conf->installed_bitmap, BIT(key_idx))) { + ret = skw_cmd_del_key(wiphy, dev, key_idx, key_type, + conf->skw_cipher, addr); + if (ret) + skw_err("failed, ret: %d\n", ret); + } + + key = rcu_dereference_protected(conf->key[key_idx], + lockdep_is_held(&conf->lock)); + + RCU_INIT_POINTER(conf->key[key_idx], NULL); + + SKW_CLEAR(conf->installed_bitmap, BIT(key_idx)); + + if (SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_SHARE)) { + SKW_CLEAR(conf->flags, SKW_KEY_FLAG_WEP_SHARE); + SKW_CLEAR(conf->flags, SKW_KEY_FLAG_WEP_UNICAST); + SKW_CLEAR(conf->flags, SKW_KEY_FLAG_WEP_MULTICAST); + } + + if (key) + kfree_rcu(key, rcu); + + return 0; +} + +static int skw_del_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, const u8 *addr) +{ + int ret, key_type; + struct skw_key_conf *conf; + const u8 *mac = NULL; + struct skw_peer_ctx *ctx = NULL; + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("link: %d, key_idx: %d, pairwise: %d, mac: %pM\n", + link_id, key_idx, pairwise, addr); + + if (key_idx >= SKW_NUM_MAX_KEY) { + skw_err("key index %d out of bounds\n", key_idx); + return -EINVAL; + } + + key_type = pairwise ? SKW_KEY_TYPE_PTK : to_skw_gtk(key_idx); + + if (addr) { + ctx = skw_peer_ctx(iface, addr); + if (!ctx) + return 0; + + skw_peer_ctx_lock(ctx); + + if (!ctx->peer) { + skw_peer_ctx_unlock(ctx); + return 0; + } + + if (pairwise) + conf = &ctx->peer->ptk_conf; + else + conf = &ctx->peer->gtk_conf; + + mutex_lock(&conf->lock); + + ret = skw_remove_key(wiphy, dev, conf, key_idx, key_type, addr); + + mutex_unlock(&conf->lock); + + skw_peer_ctx_unlock(ctx); + + } else { + + conf = &iface->key_conf; + + if (is_skw_sta_mode(iface)) + mac = iface->sta.core.bss.bssid; + + mutex_lock(&conf->lock); + + ret = skw_remove_key(wiphy, dev, conf, key_idx, key_type, mac); + + mutex_unlock(&conf->lock); + } + + return ret; +} + +/* for WEP keys */ +static int skw_set_default_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool unicast, bool multicast) +{ + int ret = 0, key_len; + struct skw_key *key; + const u8 *mac = NULL; + u8 key_data[WLAN_MAX_KEY_LEN] = {0}; + struct skw_iface *iface = netdev_priv(dev); + struct skw_key_conf *conf = &iface->key_conf; + + skw_dbg("dev: %s, link: %d, key_idx: %d, unicast: %d, multicast: %d\n", + netdev_name(dev), link_id, key_idx, unicast, multicast); + + if (!(conf->installed_bitmap & BIT(key_idx))) + return 0; + + if (is_skw_sta_mode(iface)) + mac = iface->sta.core.bss.bssid; + + rcu_read_lock(); + + key = conf->key[key_idx]; + if (key) { + memcpy(key_data, key->key_data, key->key_len); + key_len = key->key_len; + } + + rcu_read_unlock(); + + if (!key) + return 0; + + conf->wep_idx = key_idx; + + if (unicast) { + ret = skw_cmd_add_key(wiphy, dev, conf->skw_cipher, + key_idx, SKW_KEY_TYPE_PTK, + key_data, key_len, mac); + + if (ret) + SKW_SET(conf->flags, SKW_KEY_FLAG_WEP_UNICAST); + } + + if (multicast) { + ret = skw_cmd_add_key(wiphy, dev, conf->skw_cipher, + key_idx, SKW_KEY_TYPE_GTK, + key_data, key_len, mac); + + if (ret) + SKW_SET(conf->flags, SKW_KEY_FLAG_WEP_MULTICAST); + } + + return 0; +} + +/* for 11w */ +static int skw_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_index) +{ + skw_dbg("%s, key index: %d\n", netdev_name(dev), key_index); + + return 0; +} + +static int skw_set_mac_acl(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_acl_data *acl) +{ + int size; + struct skw_iface *iface = netdev_priv(dev); + + if (!acl) + return 0; + + skw_dbg("dev: %s, nr_entries: %d\n", + netdev_name(dev), acl->n_acl_entries); + + if (!acl->n_acl_entries) { + SKW_KFREE(iface->sap.acl); + return 0; + } + + size = acl->n_acl_entries * sizeof(struct mac_address); + size += sizeof(struct cfg80211_acl_data); + + SKW_KFREE(iface->sap.acl); + + iface->sap.acl = SKW_ZALLOC(size, GFP_KERNEL); + if (!iface->sap.acl) + return -ENOMEM; + + memcpy(iface->sap.acl, acl, size); + + skw_queue_work(wiphy, netdev_priv(dev), SKW_WORK_ACL_CHECK, NULL, 0); + + return 0; +} + +static bool skw_channel_allowed(struct wiphy *wiphy, u16 channel) +{ +#define BITMAP_SIZE ((164 + BITS_PER_LONG) / BITS_PER_LONG) + int i, nr_channel; + struct skw_iface *iface; + bool extra_chn = false; + struct skw_core *skw = wiphy_priv(wiphy); + int iftype_num[NUM_NL80211_IFTYPES] = {0}; + long channel_map[BITMAP_SIZE] = {0}; + + spin_lock_bh(&skw->vif.lock); + + for (nr_channel = 0, i = 0; i < SKW_NR_IFACE; i++) { + struct ieee80211_channel *chan = NULL; + + iface = skw->vif.iface[i]; + if (!iface) + continue; + + switch (iface->wdev.iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + chan = iface->sap.cfg.channel; + break; + + case NL80211_IFTYPE_STATION: + if (atomic_read(&iface->actived_ctx) > 1) + extra_chn = true; + + /* fall through */ + skw_fallthrough; + case NL80211_IFTYPE_P2P_CLIENT: + chan = iface->sta.core.bss.channel; + break; + + default: + break; + } + + if (chan && !test_and_set_bit(chan->hw_value, channel_map)) + nr_channel++; + } + + spin_unlock_bh(&skw->vif.lock); + + for (i = 0; extra_chn && (i < SKW_MAX_PEER_SUPPORT); i++) { + struct skw_peer_ctx *ctx = &skw->hw.lmac[iface->lmac_id].peer_ctx[i]; + + skw_peer_ctx_lock(ctx); + + if (ctx->peer && ctx->peer->channel && + !test_and_set_bit(ctx->peer->channel, channel_map)) + nr_channel++; + + skw_peer_ctx_unlock(ctx); + } + + if (!test_bit(channel, channel_map)) + nr_channel++; + + if (!skw_compat_check_combs(wiphy, nr_channel, 0, iftype_num)) + return true; + + skw_err("channel %d not allowed, total:%d\n", channel, nr_channel); + skw_hex_dump("channels", channel_map, sizeof(channel_map), true); + + return false; +} + +int skw_sap_set_mib(struct wiphy *wiphy, struct net_device *dev, + const u8 *ies, int ies_len) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + struct skw_iface *iface = netdev_priv(dev); + u32 val_zero = 0; + u32 val_one = 1; + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) + return ret; + + plen = skw_tlv_reserve(&conf, 2); + + if (iface->extend.wireless_mode) { + skw_dbg("wireless_mode: %d\n", iface->extend.wireless_mode); + + switch (iface->extend.wireless_mode) { + case SKW_WIRELESS_11G_ONLY: + if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HE, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_VHT, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HT, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_B, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_A, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_G, &val_one, 4)) { + skw_err("set 11G mode failed\n"); + skw_tlv_free(&conf); + } + + break; + + case SKW_WIRELESS_11N_ONLY: + if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HE, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_VHT, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HT, &val_one, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_B, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_A, &val_zero, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_G, &val_zero, 4)) { + skw_err("set 11N mode failed\n"); + skw_tlv_free(&conf); + } + + break; + + default: + break; + } + } else if (ies && ies_len) { + u8 ext_eid; + const u8 *ie; + int ht_enable = 0, vht_enable = 0, he_enable = 0; + + ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len); + if (ie && ie[1]) + ht_enable = 1; + + ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, ies, ies_len); + if (ie && ie[1]) + vht_enable = 1; + + ext_eid = SKW_WLAN_EID_EXT_HE_CAPABILITY; + ie = skw_find_ie_match(SKW_WLAN_EID_EXTENSION, ies, ies_len, &ext_eid, 1, 2); + if (ie && ie[1]) + he_enable = 1; + + skw_dbg("ht_enable: %d, vht_enable: %d, he_enable: %d\n", + ht_enable, vht_enable, he_enable); + + if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HT, &ht_enable, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_VHT, &vht_enable, 4) || + skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HE, &he_enable, 4)) { + skw_err("set beacon capa failed\n"); + skw_tlv_free(&conf); + } + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + } + + skw_tlv_free(&conf); + + return ret; +} + +#ifdef CONFIG_SWT6621S_USB3_WORKAROUND +static int +skw_switch_usb3_to_usb2_using_2G(struct skw_iface *iface, enum nl80211_band band) +{ + struct skw_core *skw = iface->skw; + char mode[20]; + int ret = 0; + + skw_dbg("bus:%d align_value:%d band:%d\n", skw->hw.bus, + skw->hw_pdata->align_value, band); + if (skw->hw.bus == SKW_BUS_USB && + skw->hw_pdata->align_value == 1024 && + band == NL80211_BAND_2GHZ && + skw->hw_pdata->usb_speed_switch) { + if (test_bit(SKW_FLAG_SWITCHING_USB_MODE, &skw->flags)) { + skw_dbg("already in switching\n"); + return -EBUSY; + } + set_bit(SKW_FLAG_SWITCHING_USB_MODE, &skw->flags); + skw->hw_pdata->usb_speed_switch(mode); + skw_dbg("change usb mode to %s\n", mode); + + skw_dbg("waiting for the switch completion"); + if (wait_for_completion_interruptible_timeout(&skw->usb_switch_done, + SKW_RECOVERY_TIMEOUT) == 0) { + skw_err("switch timeout\n"); + ret = -ETIME; + } + } + + return ret; +} +#endif + +static int skw_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *settings) +{ + int ret, bw; + int total, fixed, offset = 0; + struct skw_startap_resp resp = {}; + struct skw_startap_param *param = NULL; + struct skw_iface *iface = netdev_priv(dev); + struct skw_core *skw = wiphy_priv(wiphy); + struct cfg80211_beacon_data *bcn = &settings->beacon; + struct cfg80211_chan_def *chandef = &settings->chandef; + struct skw_key_conf *conf = &iface->key_conf; + + skw_info("ndev: %s\n", netdev_name(dev)); + skw_dbg(" * ssid: %s\n", settings->ssid); + skw_dbg(" * bssid: %pM\n", iface->addr); + skw_dbg(" * channel: %d band:%d (BW: %d)\n", chandef->chan->hw_value, + chandef->chan->band, chandef->width); + skw_dbg(" * auth type: %d\n", settings->auth_type); + skw_dbg(" * akm_suites: %d\n", settings->crypto.n_akm_suites); + + if (!skw_channel_allowed(wiphy, chandef->chan->hw_value)) + return -ENOTSUPP; + + bw = to_skw_bw(settings->chandef.width); + if (bw == SKW_CHAN_WIDTH_MAX) { + skw_err("BW %d not support\n", settings->chandef.width); + return -ENOTSUPP; + } + +#ifdef CONFIG_SWT6621S_USB3_WORKAROUND + ret = skw_switch_usb3_to_usb2_using_2G(iface, chandef->chan->band); + if (ret) + return ret; +#endif + + skw_sap_set_mib(wiphy, dev, bcn->tail, bcn->tail_len); + + fixed = sizeof(struct skw_startap_param); + total = fixed + + bcn->head_len + + bcn->tail_len + + bcn->probe_resp_len; + + param = SKW_ZALLOC(total, GFP_KERNEL); + if (!param) { + skw_err("malloc failed, size: %d\n", total); + return -ENOMEM; + } + + param->chan_width = bw; + param->chan = chandef->chan->hw_value; + param->band = to_skw_band(chandef->chan->band); + param->center_chn1 = skw_freq_to_chn(chandef->center_freq1); + param->center_chn2 = skw_freq_to_chn(chandef->center_freq2); + + param->beacon_int = settings->beacon_interval; + param->dtim_period = settings->dtim_period; + param->ssid_len = settings->ssid_len; + memcpy(param->ssid, settings->ssid, settings->ssid_len); + + if (settings->hidden_ssid) + param->flags |= settings->hidden_ssid; + + if (bcn->head) { + skw_hex_dump("beacon_head", bcn->head, bcn->head_len, false); + + param->beacon_head_len = bcn->head_len; + param->beacon_head_offset = offset + fixed; + + memcpy(param->ies + offset, bcn->head, bcn->head_len); + offset += bcn->head_len; + } + + if (bcn->tail) { + skw_hex_dump("beacon_tail", bcn->tail, bcn->tail_len, false); + + param->beacon_tail_offset = offset + fixed; + param->beacon_tail_len = bcn->tail_len; + + memcpy(param->ies + offset, bcn->tail, bcn->tail_len); + offset += bcn->tail_len; + + skw_iface_set_wmm_capa(iface, bcn->tail, bcn->tail_len); + } + + if (bcn->probe_resp) { + skw_hex_dump("probe_resp", bcn->probe_resp, + bcn->probe_resp_len, false); + + param->probe_rsp_ies_offset = offset + fixed; + param->probe_rsp_ies_len = bcn->probe_resp_len; + + memcpy(param->ies + offset, bcn->probe_resp, + bcn->probe_resp_len); + + offset += bcn->probe_resp_len; + + if (iface->sap.probe_resp) { + memcpy(iface->sap.probe_resp, bcn->probe_resp, + bcn->probe_resp_len); + iface->sap.probe_resp_len = bcn->probe_resp_len; + } + } + + if (skw_recovery_data_update(iface, param, total)) { + skw_err("build recovery failed\n"); + + SKW_KFREE(param); + return -ENOMEM; + } + + skw_edma_mask_irq(skw, iface->lmac_id); + ret = skw_send_msg(wiphy, dev, SKW_CMD_START_AP, param, total, + &resp, sizeof(resp)); + if (ret) { + skw_err("failed, ret: %d\n", ret); + + skw_recovery_data_clear(iface); + SKW_KFREE(param); + + return ret; + } + + skw_startap_resp_handler(skw, iface, &resp); + + if (SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_UNICAST) || + SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_MULTICAST)) + skw_set_default_key(wiphy, dev, 0, conf->wep_idx, + SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_UNICAST), + SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_MULTICAST)); + + if (skw_compat_cfg80211_chandef_dfs_required(wiphy, chandef, iface->wdev.iftype)) { + ret = skw_dfs_chan_init(wiphy, dev, chandef, 0); + if (ret) { + skw_err("dfs channel init failed, ret: %d\n", ret); + SKW_KFREE(param); + + return ret; + } + + ret = skw_dfs_start_monitor(wiphy, dev); + if (ret) { + skw_dbg("start dfs monitor failed, ret: %d\n", ret); + SKW_KFREE(param); + + return ret; + } + } + + skw_dpd_set_coeff_params(wiphy, dev, param->chan, + param->center_chn1, param->center_chn2, param->chan_width); + + skw_set_mac_acl(wiphy, dev, settings->acl); + + skw_set_ratio_level_mib(wiphy, skw->config.fw.noa_ratio_en, + skw->config.fw.noa_ratio_idx); + skw_set_once_noa_mib(wiphy, skw->config.fw.once_noa_en, + skw->config.fw.once_noa_pre, skw->config.fw.once_noa_abs); + + memcpy(iface->sap.cfg.ssid, settings->ssid, settings->ssid_len); + iface->sap.cfg.ssid_len = settings->ssid_len; + + iface->sap.cfg.auth_type = settings->auth_type; + iface->sap.cfg.channel = chandef->chan; + iface->sap.cfg.width = bw; + + skw_ether_copy(iface->sap.cfg.bssid, iface->addr); + memcpy(&iface->sap.cfg.crypto, &settings->crypto, + sizeof(settings->crypto)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + iface->sap.cfg.ht_cap = SKW_KMEMDUP(settings->ht_cap, + sizeof(*settings->ht_cap), GFP_KERNEL); + iface->sap.cfg.vht_cap = SKW_KMEMDUP(settings->vht_cap, + sizeof(*settings->vht_cap), GFP_KERNEL); + + iface->sap.ht_required = settings->ht_required; + iface->sap.vht_required = settings->vht_required; + + iface->sap.cfg.crypto.wep_keys = NULL; + iface->sap.cfg.crypto.psk = NULL; +#else + iface->sap.cfg.ht_cap = NULL; + iface->sap.cfg.vht_cap = NULL; + + iface->sap.ht_required = false; + iface->sap.vht_required = false; +#endif + + if (iface->skw->hw.bus == SKW_BUS_PCIE) { + if (skw_edma_get_refill((void *)iface->skw, iface->lmac_id) == 0) + skw_edma_init_data_chan((void *)iface->skw, iface->lmac_id); + else + skw_edma_inc_refill((void *)iface->skw, iface->lmac_id); + } + + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_DEAUTH); + netif_carrier_on(dev); + + if (SKW_TEST(iface->flags, SKW_IFACE_FLAG_BUF_KEY)) { + int i; + + for (i = 0; i < iface->buf_keys_idx; i++) { + skw_send_msg(wiphy, dev, SKW_CMD_ADD_KEY, + &iface->buf_keys[i], sizeof(struct skw_key_params), NULL, 0); + skw_dbg("send key buffered, idx: %d\n", i); + memset(&iface->buf_keys[i], 0, sizeof(struct skw_key_params)); + } + + iface->buf_keys_idx = 0; + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_BUF_KEY); + } + + SKW_SET(iface->flags, SKW_IFACE_FLAG_AP_STARTED); + + SKW_KFREE(param); + + return 0; +} + +static int skw_sap_del_sta(struct wiphy *wiphy, struct net_device *dev, + struct skw_peer_ctx *ctx, u8 subtype, u16 reason) +{ + int ret; + bool tx = true; + const u8 *mac = NULL; + struct skw_iface *iface = netdev_priv(dev); + + if (!ctx) + return 0; + + skw_peer_ctx_lock(ctx); + + if (ctx->peer) { + mac = ctx->peer->addr; + __skw_peer_ctx_transmit(ctx, false); + skw_set_state(&ctx->peer->sm, SKW_STATE_NONE); + + tx = !(ctx->peer->flags & SKW_PEER_FLAG_DEAUTHED); + SKW_SET(ctx->peer->flags, SKW_PEER_FLAG_DEAUTHED); + } + + skw_peer_ctx_unlock(ctx); + + if (!mac) + return 0; + + skw_mlme_ap_remove_client(iface, mac); + + ret = skw_cmd_del_sta(wiphy, dev, mac, subtype, reason, tx); + if (!ret) + skw_peer_ctx_bind(iface, ctx, NULL); + + return ret; +} + +static void skw_sap_flush_sta(struct wiphy *wiphy, struct skw_iface *iface, + u8 subtype, u16 reason) +{ + int idx; + struct skw_peer_ctx *ctx; + struct skw_core *skw = wiphy_priv(wiphy); + u32 peer_map = atomic_read(&iface->peer_map); + + while (peer_map) { + idx = ffs(peer_map) - 1; + SKW_CLEAR(peer_map, BIT(idx)); + + ctx = &skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; + if (!ctx) + continue; + + skw_sap_del_sta(wiphy, iface->ndev, ctx, subtype, reason); + } +} + +static int skw_stop_ap(struct wiphy *wiphy, struct net_device *dev, + unsigned int link_id) +{ + int ret = 0; + struct skw_iface *iface = netdev_priv(dev); + + skw_info("ndev: %s, link id: %d\n", netdev_name(dev), link_id); + + if (iface->skw->hw.bus == SKW_BUS_PCIE) + skw_edma_dec_refill((void *)iface->skw, iface->lmac_id); + + netif_carrier_off(dev); + + skw_sap_flush_sta(wiphy, iface, 12, SKW_LEAVE); + + // set flag for tx thread to filter out skb in tx cache + // mutex_lock(&skw->txrx.lock); + // SKW_CLEAR(skw->txrx.tx_map, BIT(iface->id)); + // mutex_unlock(&skw->txrx.lock); + + // WARN_ON(iface->sta_list.count); + skw_purge_key_conf(&iface->key_conf); + skw_recovery_data_clear(iface); + + SKW_SET(iface->flags, SKW_IFACE_FLAG_DEAUTH); + + skw_dfs_deinit(wiphy, dev); + + ret = skw_send_msg(wiphy, dev, SKW_CMD_STOP_AP, NULL, 0, NULL, 0); + if (ret) { + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_DEAUTH); + skw_err("failed, ret = %d\n", ret); + return ret; + } + + SKW_KFREE(iface->sap.acl); + SKW_KFREE(iface->sap.cfg.ht_cap); + SKW_KFREE(iface->sap.cfg.vht_cap); + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_AP_STARTED); + + skw_lmac_unbind_iface(wiphy_priv(wiphy), iface->lmac_id, iface->id); + + return 0; +} + +static int skw_change_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *bcn) +{ + int ret = -1; + int total, fixed, offset = 0; + struct skw_iface *iface = netdev_priv(dev); + struct skw_beacon_params *param = NULL; + + skw_dbg("dev: %s\n", netdev_name(dev)); + if (!bcn) + return -EINVAL; + + fixed = sizeof(struct skw_beacon_params); + total = fixed + + bcn->head_len + + bcn->tail_len + + bcn->probe_resp_len; + + param = SKW_ZALLOC(total, GFP_KERNEL); + if (!param) { + skw_err("malloc failed, size: %d\n", total); + return -ENOMEM; + } + + if (bcn->head) { + skw_hex_dump("beacon_head", bcn->head, bcn->head_len, false); + + param->beacon_head_len = bcn->head_len; + param->beacon_head_offset = fixed + offset; + memcpy(param->ies + offset, bcn->head, bcn->head_len); + offset += bcn->head_len; + } + + if (bcn->tail) { + skw_hex_dump("beacon_tail", bcn->tail, bcn->tail_len, false); + + param->beacon_tail_offset = fixed + offset; + param->beacon_tail_len = bcn->tail_len; + memcpy(param->ies + offset, bcn->tail, bcn->tail_len); + offset += bcn->tail_len; + } + + if (bcn->probe_resp) { + skw_hex_dump("probe_resp", bcn->probe_resp, bcn->probe_resp_len, false); + + param->probe_rsp_offset = fixed + offset; + param->probe_rsp_len = bcn->probe_resp_len; + memcpy(param->ies + offset, bcn->probe_resp, + bcn->probe_resp_len); + offset += bcn->probe_resp_len; + + if (iface->sap.probe_resp) { + memcpy(iface->sap.probe_resp, bcn->probe_resp, + bcn->probe_resp_len); + + iface->sap.probe_resp_len = bcn->probe_resp_len; + } + } + + ret = skw_send_msg(wiphy, dev, SKW_CMD_CHANGE_BEACON, + param, total, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + SKW_KFREE(param); + + return ret; +} + +void skw_set_state(struct skw_sm *sm, enum SKW_STATES state) +{ + skw_log(SKW_STATE, "[%s] inst: %d, %s -> %pM, state: %s -> %s\n", + SKW_TAG_STATE, sm->inst, skw_iftype_name(sm->iface_iftype), + sm->addr, skw_state_name(sm->state), skw_state_name(state)); + + sm->state = state; +} + +int skw_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) +{ + struct skw_iface *iface = netdev_priv(dev); + u32 flags_set = params->sta_flags_set; + struct skw_peer_ctx *ctx = NULL; + + skw_dbg("%s(%s), mac: %pM, flags_set: 0x%x\n", + netdev_name(dev), skw_iftype_name(dev->ieee80211_ptr->iftype), + mac, params->sta_flags_set); + + ctx = skw_peer_ctx(iface, mac); + if (!ctx) + return -EINVAL; + + skw_peer_ctx_lock(ctx); + + switch (dev->ieee80211_ptr->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_ADHOC: + + if (flags_set & BIT(NL80211_STA_FLAG_ASSOCIATED)) { + __skw_peer_ctx_transmit(ctx, true); + skw_set_state(&ctx->peer->sm, SKW_STATE_ASSOCED); + + if (iface->sap.cfg.crypto.n_akm_suites == 0) + flags_set |= BIT(NL80211_STA_FLAG_AUTHORIZED); + + } + + if (flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { + skw_set_state(&ctx->peer->sm, SKW_STATE_COMPLETED); + atomic_set(&ctx->peer->rx_filter, SKW_RX_FILTER_NONE); + } + + break; + + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { + skw_set_state(&iface->sta.core.sm, SKW_STATE_COMPLETED); + atomic_set(&ctx->peer->rx_filter, SKW_RX_FILTER_NONE); + skw_set_ip_to_fw(wiphy, dev); + } + + break; + + default: + break; + } + + skw_peer_ctx_unlock(ctx); + + return 0; +} + +static int skw_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params) +{ + return skw_change_station(wiphy, dev, (const u8 *)mac, params); +} + +static int skw_set_sta_wep_key(struct wiphy *wiphy, struct skw_iface *iface, + const u8 *mac, enum SKW_KEY_TYPE key_type) +{ + int idx; + struct skw_key_params key_params; + struct skw_key *key; + struct skw_key_conf *conf = &iface->key_conf; + + skw_dbg("addr: %pM, key type: %d\n", mac, key_type); + + memset(&key_params, 0x0, sizeof(key_params)); + + idx = skw_key_idx(conf->installed_bitmap); + if (idx == SKW_INVALID_ID) + return -EINVAL; + + rcu_read_lock(); + key = rcu_dereference(conf->key[idx]); + rcu_read_unlock(); + + key_params.cipher_type = conf->skw_cipher; + key_params.key_id = idx; + key_params.key_len = key->key_len; + key_params.key_type = key_type; + + memcpy(key_params.key, key->key_data, key->key_len); + skw_ether_copy(key_params.mac_addr, mac); + + if (is_skw_ap_mode(iface) && iface->buf_keys_idx < SKW_MAX_BUF_KEYS && + !SKW_TEST(iface->flags, SKW_IFACE_FLAG_AP_STARTED)) { + memcpy(&iface->buf_keys[iface->buf_keys_idx++], + &key_params, sizeof(key_params)); + SKW_SET(iface->flags, SKW_IFACE_FLAG_BUF_KEY); + skw_dbg("lead key buffered, idx: %d\n", iface->buf_keys_idx - 1); + return 0; + } else + return skw_send_msg(wiphy, iface->ndev, SKW_CMD_ADD_KEY, + &key_params, sizeof(key_params), NULL, 0); +} + +int skw_cmd_del_sta(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, u8 type, u16 reason, bool tx_frame) +{ + struct skw_del_sta_param params; + + skw_dbg("%s: addr: %pM, reason: %d, tx frame: %d\n", + netdev_name(dev), mac, reason, tx_frame); + + params.reason_code = reason; + skw_ether_copy(params.mac, mac); + params.tx_frame = tx_frame; + + return skw_send_msg(wiphy, dev, SKW_CMD_DEL_STA, ¶ms, + sizeof(params), NULL, 0); +} + +int skw_del_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, u8 subtype, u16 reason) +{ + struct skw_peer_ctx *ctx; + struct skw_iface *iface = netdev_priv(dev); + + skw_info("subtype: %d, reason: %d, mac: %pM\n", subtype, reason, mac); + + if (!mac || is_broadcast_ether_addr(mac)) { + skw_sap_flush_sta(wiphy, iface, subtype, reason); + + return 0; + } + + ctx = skw_peer_ctx(iface, mac); + if (!ctx) + return -ENOENT; + + return skw_sap_del_sta(wiphy, dev, ctx, subtype, reason); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +static int skw_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + struct station_del_parameters *params) +{ + return skw_del_station(wiphy, dev, params->mac, + params->subtype, params->reason_code); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static int skw_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, const u8 *mac) +{ + return skw_del_station(wiphy, dev, mac, + 12, /* Deauth */ + WLAN_REASON_DEAUTH_LEAVING); +} +#else +static int skw_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, u8 *mac) +{ + return skw_del_station(wiphy, dev, (const u8 *)mac, + 12, /* Deauth */ + WLAN_REASON_DEAUTH_LEAVING); +} +#endif + +int skw_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) +{ + struct skw_iface *iface = netdev_priv(dev); + struct skw_peer_ctx *ctx; + struct skw_peer *peer; + int ret; + u8 idx; + + skw_dbg("ndev: %s, mac: %pM, flags: 0x%x\n", + netdev_name(dev), mac, params->sta_flags_set); + + ctx = skw_peer_ctx(iface, mac); + if (!ctx) { + peer = skw_peer_alloc(); + if (!peer) { + skw_err("failed, addr: %pM\n", mac); + return -ENOMEM; + } + + ret = skw_send_msg(wiphy, dev, SKW_CMD_ADD_STA, (void *)mac, + ETH_ALEN, &idx, sizeof(idx)); + if (ret) { + skw_err("command failed, addr: %pM, ret: %d\n", + mac, ret); + + SKW_KFREE(peer); + return ret; + } + + skw_peer_init(peer, mac, idx); + ctx = skw_get_ctx(iface->skw, iface->lmac_id, idx); + ret = skw_peer_ctx_bind(iface, ctx, peer); + if (ret) { + skw_cmd_del_sta(wiphy, dev, mac, 12, SKW_LEAVE, false); + SKW_KFREE(peer); + return -EINVAL; + } + } + + skw_peer_ctx_lock(ctx); + + __skw_peer_ctx_transmit(ctx, false); + skw_set_state(&ctx->peer->sm, SKW_STATE_AUTHED); + + skw_peer_ctx_unlock(ctx); + + if (iface->key_conf.flags & SKW_KEY_FLAG_WEP_SHARE) + skw_set_sta_wep_key(wiphy, iface, mac, SKW_KEY_TYPE_PTK); + + return 0; +} + +static int skw_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params) +{ + return skw_add_station(wiphy, dev, (const u8 *)mac, params); +} + +static void skw_set_rate_info(struct skw_rate *rate, struct rate_info *rinfo) +{ +#if 0 + skw_dbg("flags: %d, mcs: %d, bw: %d, gi: %d, nss: %d, he_ru: %d\n", + rate->flags, rate->mcs_idx, rate->bw, + rate->gi, rate->nss, rate->he_ru); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + switch (rate->bw) { + case SKW_RATE_INFO_BW_40: + rinfo->bw = RATE_INFO_BW_40; + break; + + case SKW_RATE_INFO_BW_80: + rinfo->bw = RATE_INFO_BW_80; + break; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + case SKW_RATE_INFO_BW_HE_RU: + rinfo->bw = RATE_INFO_BW_HE_RU; + rinfo->he_ru_alloc = rate->he_ru; + break; +#endif + default: + rinfo->bw = RATE_INFO_BW_20; + break; + } +#endif + + rinfo->flags = 0; + switch (rate->flags) { + case SKW_RATE_INFO_FLAGS_HT: + rinfo->mcs = rate->mcs_idx; + + rinfo->flags |= RATE_INFO_FLAGS_MCS; + if (rate->gi) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; + + break; + + case SKW_RATE_INFO_FLAGS_VHT: + rinfo->mcs = rate->mcs_idx; + rinfo->nss = rate->nss; + + rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; + if (rate->gi) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; + + break; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + case SKW_RATE_INFO_FLAGS_HE: + rate->gi = skw_gi_to_nl80211_info_gi(rate->gi); + rinfo->mcs = rate->mcs_idx; + rinfo->nss = rate->nss; + rinfo->he_gi = rate->gi; + rinfo->he_dcm = rate->he_dcm; + rinfo->flags |= RATE_INFO_FLAGS_HE_MCS; + break; +#endif + default: + rinfo->legacy = rate->legacy_rate; + break; + } +} + +static int skw_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + u64 ts; + int ret = -1; + struct skw_peer_ctx *ctx; + struct skw_station_params params = {0}; + struct skw_get_sta_resp get_sta_resp; + struct skw_iface *iface = netdev_priv(dev); + + // skw_dbg("dev: %s, mac: %pM\n", netdev_name(dev), mac); + + if (!mac) + return 0; + + ctx = skw_peer_ctx(iface, mac); + if (!ctx) + return -ENOENT; + + memset(&get_sta_resp, 0, sizeof(get_sta_resp)); + + ts = local_clock(); + do_div(ts, 1000000); + params.timestamp = ts; + skw_ether_copy(params.mac, mac); + + ret = skw_send_msg(wiphy, dev, SKW_CMD_GET_STA, ¶ms, + sizeof(params), &get_sta_resp, + sizeof(struct skw_get_sta_resp)); + if (ret) { + skw_warn("failed, ret: %d\n", ret); + return ret; + } + + sinfo->tx_failed = get_sta_resp.tx_failed; + sinfo->filled |= SKW_COMPAT_TX_FAILED; + + sinfo->signal = get_sta_resp.signal; + sinfo->filled |= SKW_COMPAT_SIGNAL; + + if (is_skw_sta_mode(iface)) { + sinfo->tx_packets = dev->stats.tx_packets; + sinfo->filled |= SKW_COMPAT_TX_PACKETS; + + sinfo->tx_bytes = dev->stats.tx_bytes; + sinfo->filled |= SKW_COMPAT_TX_BYTES; + + sinfo->rx_packets = dev->stats.rx_packets; + sinfo->filled |= SKW_COMPAT_RX_PACKETS; + + sinfo->rx_bytes = dev->stats.rx_bytes; + sinfo->filled |= SKW_COMPAT_RX_BYTES; + } + + skw_set_rate_info(&get_sta_resp.tx_rate, &sinfo->txrate); + sinfo->filled |= SKW_COMPAT_TX_BITRATE; + + skw_peer_ctx_lock(ctx); + + if (ctx->peer) { + ctx->peer->tx.rssi = sinfo->signal; + + skw_hex_dump("get_sta_rx_rate:", &get_sta_resp.rx_rate, + sizeof(get_sta_resp.rx_rate), false); + + skw_desc_get_rx_rate(&ctx->peer->rx.rate, get_sta_resp.rx_rate.bw, + get_sta_resp.rx_rate.ppdu_mode, + skw_desc_gi_to_skw_gi(get_sta_resp.rx_rate.gi_type, + get_sta_resp.rx_rate.ppdu_mode), + skw_desc_nss_to_nss_num(get_sta_resp.rx_rate.nss), + get_sta_resp.rx_rate.dcm, + get_sta_resp.rx_rate.data_rate); + skw_set_rate_info(&ctx->peer->rx.rate, &sinfo->rxrate); + + sinfo->filled |= SKW_COMPAT_RX_BITRATE; + + memcpy(&ctx->peer->tx.rate, &get_sta_resp.tx_rate, + sizeof(struct skw_rate)); + + ctx->peer->tx.tx_psr = get_sta_resp.tx_psr; + ctx->peer->tx.tx_failed = get_sta_resp.tx_failed; + + memcpy(ctx->peer->rx.filter_cnt, + get_sta_resp.filter_cnt, sizeof(get_sta_resp.filter_cnt)); + memcpy(ctx->peer->rx.filter_drop_offload_cnt, + get_sta_resp.filter_drop_offload_cnt, + sizeof(get_sta_resp.filter_drop_offload_cnt)); + + ctx->peer->tx.percent = get_sta_resp.tx_percent; + ctx->peer->rx.percent = get_sta_resp.rx_percent; + + if (is_skw_ap_mode(iface)) { + sinfo->tx_packets = ctx->peer->tx.pkts; + sinfo->tx_bytes = ctx->peer->tx.bytes; + sinfo->rx_packets = ctx->peer->rx.pkts; + sinfo->rx_bytes = ctx->peer->rx.bytes; + sinfo->filled |= SKW_COMPAT_TX_PACKETS | SKW_COMPAT_TX_BYTES | + SKW_COMPAT_RX_PACKETS | SKW_COMPAT_RX_BYTES; + } + } + + skw_peer_ctx_unlock(ctx); + +// skw_dbg("tx packets:%u tx_bytes:%llu rx_packets:%u rx_bytes:%llu\n", +// sinfo->tx_packets, sinfo->tx_bytes, +// sinfo->rx_packets, sinfo->rx_bytes); + + return ret; +} + +static int skw_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_info *sinfo) +{ + return skw_get_station(wiphy, dev, (const u8 *)mac, sinfo); +} + +static void skw_scan_timeout(void *data) +{ + struct skw_iface *iface = data; + + if (unlikely(!iface)) { + skw_warn("iface is NULL\n"); + return; + } + + skw_queue_work(priv_to_wiphy(iface->skw), iface, + SKW_WORK_SCAN_TIMEOUT, NULL, 0); +} + +static bool skw_cqm_bg_scan(struct skw_iface *iface, + struct cfg80211_scan_request *req, u16 *target_chn) +{ + bool ret; + + if (iface->wdev.iftype != NL80211_IFTYPE_STATION) + return false; + + spin_lock_bh(&iface->sta.roam_data.lock); + if (iface->sta.roam_data.flags & SKW_IFACE_STA_ROAM_FLAG_CQM_LOW && + iface->sta.core.sm.state == SKW_STATE_COMPLETED && + req->n_channels > 10 && req->n_ssids == 1 && + req->ssids != NULL && req->ssids->ssid_len != 0 && + req->ssids->ssid_len == iface->sta.core.bss.ssid_len && + memcmp(req->ssids->ssid, iface->sta.core.bss.ssid, + req->ssids->ssid_len) == 0) { + skw_dbg("only %d", iface->sta.roam_data.target_chn); + *target_chn = iface->sta.roam_data.target_chn; + iface->sta.roam_data.flags &= ~SKW_IFACE_STA_ROAM_FLAG_CQM_LOW; + skw_del_timer_work(iface->skw, skw_cqm_scan_timeout); + ret = true; + } else { + *target_chn = 0; + ret = false; + } + + spin_unlock_bh(&iface->sta.roam_data.lock); + + return ret; +} + +static bool is_skw_6ghz_non_psc_chan(struct ieee80211_channel *channel) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) + if (channel->band != NL80211_BAND_6GHZ) + return false; + + if (channel->hw_value % 16 != 5) + return true; +#endif + + return false; +} + +static int skw_scan(struct wiphy *wiphy, struct cfg80211_scan_request *req) +{ + int i, ret; + struct skw_scan_chan_info *chan; + int size, nssids_size, offset; + u16 roam_chn = 0; + u16 scan_chn_num = 0; + char *buff = NULL; + struct skw_scan_param *param = NULL; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = SKW_WDEV_TO_IFACE(req->wdev); + + skw_dbg("%s: chip: %d, nr_chan: %d, n_ssids: %d, ie_len: %zd\n", + skw_iftype_name(req->wdev->iftype), skw->idx, + req->n_channels, req->n_ssids, req->ie_len); + + size = sizeof(struct skw_scan_param) + + req->n_channels * sizeof(*chan) + + req->n_ssids * sizeof(struct cfg80211_ssid) + + req->ie_len; + + buff = SKW_ZALLOC(size, GFP_KERNEL); + if (!buff) { + skw_err("malloc failed, size: %d\n", size); + return -ENOMEM; + } + + offset = 0; + + param = (struct skw_scan_param *)buff; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR && + iface->wdev.iftype == NL80211_IFTYPE_STATION) { + param->flags |= SKW_SCAN_FLAG_RND_MAC; + + get_random_mask_addr(param->rand_mac, + req->mac_addr, + req->mac_addr_mask); + } +#endif + + if (iface->wdev.iftype == NL80211_IFTYPE_AP) { + param->flags |= SKW_SCAN_FLAG_ACS; + skw_purge_survey_data(iface); + } + + offset += sizeof(struct skw_scan_param); + param->chan_offset = offset; + + chan = (struct skw_scan_chan_info *)(buff + offset); + + skw_cqm_bg_scan(iface, req, &roam_chn); + + for (i = 0; i < req->n_channels; i++) { + if (unlikely(iface->extend.scan_band_filter)) { + if (!(iface->extend.scan_band_filter & BIT(req->channels[i]->band))) + continue; + } + + if (unlikely(roam_chn && roam_chn != req->channels[i]->hw_value)) + continue; + + if (is_skw_6ghz_non_psc_chan(req->channels[i])) + continue; + + chan->band = to_skw_band(req->channels[i]->band); + chan->chan_num = req->channels[i]->hw_value; + + if ((req->channels[i]->flags & SKW_PASSIVE_SCAN) || + (!req->n_ssids && is_skw_sta_mode(iface))) + chan->scan_flags |= SKW_SCAN_FLAG_PASSIVE; + + scan_chn_num++; + chan++; + } + + param->nr_chan = scan_chn_num; + offset += scan_chn_num * sizeof(*chan); + + param->n_ssid = req->n_ssids; + if (req->n_ssids) { + nssids_size = req->n_ssids * sizeof(struct cfg80211_ssid); + memcpy(buff + offset, req->ssids, nssids_size); + param->ssid_offset = offset; + offset += nssids_size; + } + + if (req->ie_len) { + memcpy(buff + offset, req->ie, req->ie_len); + param->ie_offset = offset; + param->ie_len = req->ie_len; + } + + skw->scan_req = req; + skw->nr_scan_results = 0; + + skw_add_timer_work(skw, "scan_timeout", skw_scan_timeout, iface, + SKW_SCAN_TIMEOUT, req, GFP_KERNEL); + + ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_START_SCAN, + buff, size, NULL, 0); + if (ret) { + skw->scan_req = NULL; + skw_del_timer_work(skw, req); + skw_dbg("failed, ret: %d\n", ret); + } + + SKW_KFREE(buff); + + return ret; +} + +void skw_scan_done(struct skw_core *skw, struct skw_iface *iface, bool aborted) +{ + struct cfg80211_scan_request *scan_req; + int ret_val; + + mutex_lock(&skw->lock); + + if (!skw->scan_req) + goto ret; + + if (&iface->wdev != skw->scan_req->wdev) + goto ret; + + skw_dbg("inst: %d, aborted: %d, scan result: %d\n", + iface->id, aborted, skw->nr_scan_results); + + scan_req = skw->scan_req; + skw->scan_req = NULL; + + skw_del_timer_work(skw, scan_req); + + if (aborted) { + ret_val = skw_msg_xmit(priv_to_wiphy(skw), iface->id, + SKW_CMD_STOP_SCAN, NULL, 0, NULL, 0); + if (ret_val) + skw_warn("failed, return: %d\n", ret_val); + } + + skw_compat_scan_done(scan_req, aborted); + +ret: + mutex_unlock(&skw->lock); +} + +static void skw_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + struct skw_core *skw = wiphy_priv(wiphy); + int ret; + + skw_dbg("inst: %d, scaning: %d\n", iface->id, !!skw->scan_req); + + if (!skw->scan_req) + return; + + ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_STOP_SCAN, NULL, 0, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + skw_scan_done(skw, iface, false); +} + +static int skw_mbssid_index(struct skw_core *skw, struct cfg80211_bss *bss) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)) + return skw_bss_priv(bss)->bssid_index; +#else + return bss->bssid_index; +#endif +} + +static int skw_mbssid_max_indicator(struct skw_core *skw, + struct cfg80211_bss *bss) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)) + return skw_bss_priv(bss)->max_bssid_indicator; +#else + return bss->max_bssid_indicator; +#endif +} + +const u8 *skw_bss_get_ext_ie(struct cfg80211_bss *bss, u8 ext_eid) +{ + const struct cfg80211_bss_ies *ies; + + ies = rcu_dereference(bss->ies); + if (!ies) + return NULL; + + return skw_find_ie_match(SKW_WLAN_EID_EXTENSION, ies->data, + ies->len, &ext_eid, 1, 2); +} + +static int skw_set_he_mib(struct wiphy *wiphy, struct net_device *ndev, int he_enable) +{ + int ret; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("he_enable: %d\n", he_enable); + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HE, &he_enable, 4)) { + skw_err("set HE mode [%d] failed\n", he_enable); + skw_tlv_free(&conf); + + return -EINVAL; + } + + *plen = conf.total_len; + ret = skw_send_msg(wiphy, ndev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_warn("failed, ret: %d\n", ret); + + skw_tlv_free(&conf); + + return ret; +} + +static int skw_set_vht_mib(struct wiphy *wiphy, struct net_device *ndev, int vht_enable) +{ + int ret; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("vht_enable: %d\n", vht_enable); + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_VHT, &vht_enable, 4)) { + skw_err("set vht mode [%d] failed\n", vht_enable); + skw_tlv_free(&conf); + + return -EINVAL; + } + + *plen = conf.total_len; + ret = skw_send_msg(wiphy, ndev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_warn("failed, ret: %d\n", ret); + + skw_tlv_free(&conf); + + return ret; +} + +static void skw_parse_center_chn(struct cfg80211_bss *bss, int *he_enable, + struct skw_center_chn *cc) +{ + unsigned int diff; + const u8 *ht_ie, *vht_ie; + u8 vht_seg0_idx, vht_seg1_idx; + struct ieee80211_ht_operation *ht_oper; + struct ieee80211_vht_operation *vht_oper; + const u8 *he_ie; + struct skw_he_cap_elem *he_cap; + + cc->bw = SKW_CHAN_WIDTH_20; + cc->center_chn1 = bss->channel->hw_value; + cc->center_chn2 = 0; + + *he_enable = 1; + + if (WARN_ON(!bss)) + return; + + rcu_read_lock(); + + ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_OPERATION); + if (ht_ie && ht_ie[1]) { + ht_oper = (struct ieee80211_ht_operation *)(ht_ie + 2); + + cc->center_chn2 = 0; + + switch (ht_oper->ht_param & 0x3) { + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + cc->bw = SKW_CHAN_WIDTH_20; + cc->center_chn1 = ht_oper->primary_chan; + + break; + + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + cc->bw = SKW_CHAN_WIDTH_40; + cc->center_chn1 = ht_oper->primary_chan + 2; + break; + + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + cc->bw = SKW_CHAN_WIDTH_40; + cc->center_chn1 = ht_oper->primary_chan - 2; + break; + + default: + break; + } + } + + vht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); + if (vht_ie && vht_ie[1]) { + vht_oper = (struct ieee80211_vht_operation *)(vht_ie + 2); + cc->center_chn2 = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + vht_seg0_idx = vht_oper->center_freq_seg0_idx; + vht_seg1_idx = vht_oper->center_freq_seg1_idx; +#else + vht_seg0_idx = vht_oper->center_freq_seg1_idx; + vht_seg1_idx = vht_oper->center_freq_seg2_idx; +#endif + switch (vht_oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + cc->bw = SKW_CHAN_WIDTH_80; + cc->center_chn1 = vht_seg0_idx; + + if (vht_seg1_idx) { + diff = abs(vht_seg1_idx - vht_seg0_idx); + if (diff == 8) { + cc->bw = SKW_CHAN_WIDTH_160; + cc->center_chn1 = vht_seg1_idx; + } else if (diff > 8) { + cc->bw = SKW_CHAN_WIDTH_80P80; + cc->center_chn2 = vht_seg1_idx; + } + } + + break; + + case IEEE80211_VHT_CHANWIDTH_160MHZ: + cc->bw = SKW_CHAN_WIDTH_160; + cc->center_chn1 = vht_seg0_idx; + break; + + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + cc->bw = SKW_CHAN_WIDTH_80P80; + cc->center_chn1 = vht_seg0_idx; + cc->center_chn2 = vht_seg1_idx; + break; + + default: + break; + } + } + + he_ie = skw_bss_get_ext_ie(bss, SKW_WLAN_EID_EXT_HE_CAPABILITY); + if (he_ie && he_ie[1]) { + skw_hex_dump("he capa", he_ie, he_ie[1] + 2, false); + + /* 802.11ax D3.0 */ + he_cap = (struct skw_he_cap_elem *)(he_ie + 3); // ID: 1 + len: 1 + Num: 1 + + skw_dbg("band: %d, ppe: 0x%x, phy_cap_info[0]: 0x%x\n", + bss->channel->band, he_cap->ppe, he_cap->phy_cap_info[0]); + + if ((he_cap->phy_cap_info[6] & 0x80) == 0x80 && + (he_cap->ppe & 0x78) == 0x60) { // check BIT[3:6] + switch (bss->channel->band) { + case NL80211_BAND_2GHZ: + *he_enable = 0; + break; + + case NL80211_BAND_5GHZ: + if (!(he_cap->phy_cap_info[0] & + SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G)) + *he_enable = 0; + break; + + default: + break; + } + } + } + + cc->band = to_skw_band(bss->channel->band); + skw_dbg("cc->bw:%d cc->band:%d\n", cc->bw, cc->band); + + rcu_read_unlock(); +} + +static int skw_cmd_join(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_bss *bss, u32 bw, u8 band, + u16 center_chn1, u16 center_chn2, + bool roaming, struct skw_join_resp *resp) +{ + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = netdev_priv(ndev); + struct skw_join_param *params; + int ret = 0, size = 0; + + skw_dbg("bssid: %pM(idx: %d, ind: %d), chn: %d(%d, %d), bw: %d band: %d\n", + bss->bssid, skw_mbssid_index(skw, bss), + skw_mbssid_max_indicator(skw, bss), + bss->channel->hw_value, + center_chn1, center_chn2, bw, band); + + size = sizeof(struct skw_join_param) + bss->ies->len; + params = SKW_ZALLOC(size, GFP_KERNEL); + if (!params) + return -ENOMEM; + + params->bandwidth = bw; + params->band = band; + params->center_chn1 = center_chn1; + params->center_chn2 = center_chn2; + params->chan_num = bss->channel->hw_value; + + params->reserved = 0; + params->roaming = !!roaming; + params->capability = bss->capability; + params->beacon_interval = bss->beacon_interval; + params->bssid_index = skw_mbssid_index(skw, bss); + params->max_bssid_indicator = skw_mbssid_max_indicator(skw, bss); + memcpy(params->bssid, bss->bssid, ETH_ALEN); + + if (bss->ies->len) { + memcpy(params->bss_ie, bss->ies->data, bss->ies->len); + params->bss_ie_offset = sizeof(struct skw_join_param); + params->bss_ie_len = bss->ies->len; + } + + skw_edma_mask_irq(skw, iface->lmac_id); + ret = skw_send_msg(wiphy, ndev, SKW_CMD_JOIN, params, + size, resp, sizeof(*resp)); + if (ret) + skw_err("failed, ret: %d\n", ret); + + SKW_KFREE(params); + + return ret; +} + +int skw_cmd_unjoin(struct wiphy *wiphy, struct net_device *ndev, + const u8 *addr, u16 reason, bool tx_frame) +{ + int ret; + struct skw_disconnect_param params; + + skw_dbg("%s, bssid: %pM, reason: %d\n", + netdev_name(ndev), addr, reason); + + memset(¶ms, 0x0, sizeof(params)); + + params.type = SKW_DISCONNECT_ONLY; + params.reason_code = reason; + params.local_state_change = !tx_frame; + + if (tx_frame) + params.type = SKW_DISCONNECT_SEND_DEAUTH; + + ret = skw_send_msg(wiphy, ndev, SKW_CMD_DISCONNECT, ¶ms, + sizeof(params), NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + return ret; +} + +int skw_cmd_monitor(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, u8 mode) +{ + int ret = 0; + struct skw_set_monitor_param param = {0}; + + param.mode = mode; + switch (param.mode) { + case SKW_MONITOR_CLOSE: + break; + case SKW_MONITOR_COMMON: + case SKW_MONITOR_MAC_CAP: + case SKW_MONITOR_PHY_CAP: + if (chandef == NULL || chandef->chan == NULL) + return -EINVAL; + param.chan_num = chandef->chan->hw_value; + param.center_chn1 = skw_freq_to_chn(chandef->center_freq1); + param.center_chn2 = skw_freq_to_chn(chandef->center_freq2); + + param.bandwidth = to_skw_bw(chandef->width); + + break; + + default: + return -EINVAL; + } + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MONITOR_PARAM, + ¶m, sizeof(struct skw_set_monitor_param), NULL, 0); + + return ret; +} + +static int skw_cmd_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_auth_request *req) +{ + int ret = 0; + u16 auth_alg; + int size, offset; + struct skw_auth_param *params = NULL; + struct skw_iface *iface = netdev_priv(dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + const u8 *auth_data = req->auth_data; + size_t auth_data_len = req->auth_data_len; +#else + const u8 *auth_data = req->sae_data; + size_t auth_data_len = req->sae_data_len; +#endif + + switch (req->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + auth_alg = WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + auth_alg = WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + auth_alg = WLAN_AUTH_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + auth_alg = WLAN_AUTH_LEAP; + break; + case NL80211_AUTHTYPE_SAE: + auth_alg = WLAN_AUTH_SAE; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + case NL80211_AUTHTYPE_FILS_SK: + auth_alg = WLAN_AUTH_FILS_SK; + break; + case NL80211_AUTHTYPE_FILS_SK_PFS: + auth_alg = WLAN_AUTH_FILS_SK_PFS; + break; + case NL80211_AUTHTYPE_FILS_PK: + auth_alg = WLAN_AUTH_FILS_PK; + break; +#endif + case NL80211_AUTHTYPE_AUTOMATIC: + /* + * Fixme: try open wep first, then set share key after using + * open wep failed. + */ + auth_alg = WLAN_AUTH_OPEN; + break; + default: + return -EOPNOTSUPP; + } + + size = sizeof(struct skw_auth_param) + + req->ie_len + + auth_data_len; + + params = SKW_ZALLOC(size, GFP_KERNEL); + if (!params) { + skw_err("malloc failed, size: %d\n", size); + return -ENOMEM; + } + + offset = sizeof(struct skw_auth_param); + params->auth_algorithm = auth_alg; + + if (auth_data_len) { + params->auth_data_offset = offset; + params->auth_data_len = auth_data_len; + + memcpy((u8 *)params + offset, auth_data, + auth_data_len); + + offset += auth_data_len; + } + + if (req->ie && req->ie_len) { + params->auth_ie_offset = offset; + params->auth_ie_len = req->ie_len; + memcpy((u8 *)params + offset, req->ie, req->ie_len); + + offset += req->ie_len; + } + + memcpy(iface->sta.core.pending.auth_cmd, params, size); + iface->sta.core.pending.auth_cmd_len = size; + + ret = skw_msg_xmit_timeout(wiphy, SKW_NDEV_ID(dev), SKW_CMD_AUTH, + params, size, NULL, 0, "SKW_CMD_AUTH", + msecs_to_jiffies(SKW_CMD_TIMEOUT), 0); + + SKW_KFREE(params); + + return ret; +} + +static inline void skw_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, + const struct ieee80211_ht_cap *ht_capa_mask) +{ + int i; + u8 *p1, *p2; + + if (!ht_capa_mask) { + memset(ht_capa, 0, sizeof(*ht_capa)); + return; + } + + p1 = (u8 *)(ht_capa); + p2 = (u8 *)(ht_capa_mask); + for (i = 0; i < sizeof(*ht_capa); i++) + p1[i] &= p2[i]; +} + + /* Do a logical ht_capa &= ht_capa_mask. */ +static inline void skw_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, + const struct ieee80211_vht_cap *vht_capa_mask) +{ + int i; + u8 *p1, *p2; + + if (!vht_capa_mask) { + memset(vht_capa, 0, sizeof(*vht_capa)); + return; + } + + p1 = (u8 *)(vht_capa); + p2 = (u8 *)(vht_capa_mask); + for (i = 0; i < sizeof(*vht_capa); i++) + p1[i] &= p2[i]; +} + +static int skw_cmd_assoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + int ret = 0; + int size, offset; + char *buff = NULL; + struct skw_assoc_req_param *param = NULL; + struct skw_iface *iface = netdev_priv(dev); + + size = sizeof(struct skw_assoc_req_param) + req->ie_len; + + buff = SKW_ZALLOC(size, GFP_KERNEL); + if (!buff) { + skw_err("malloc failed, size: %d\n", size); + return -ENOMEM; + } + + offset = 0; + param = (struct skw_assoc_req_param *)buff; + memcpy(¶m->ht_capa, &req->ht_capa, sizeof(req->ht_capa)); + + skw_oper_and_ht_capa(¶m->ht_capa, &req->ht_capa_mask); + memcpy(¶m->vht_capa, &req->vht_capa, sizeof(req->vht_capa)); + + skw_oper_and_vht_capa(¶m->vht_capa, &req->vht_capa_mask); + memcpy(param->bssid, req->bss->bssid, ETH_ALEN); + + if (req->prev_bssid) + memcpy(param->pre_bssid, req->prev_bssid, ETH_ALEN); + + param->req_ie_len = req->ie_len; + + offset += sizeof(struct skw_assoc_req_param); + param->req_ie_offset = offset; + + if (req->ie_len) + memcpy(param->req_ie, req->ie, req->ie_len); + + memcpy(iface->sta.core.pending.assoc_cmd, buff, size); + iface->sta.core.pending.assoc_cmd_len = size; + + ret = skw_msg_xmit_timeout(wiphy, SKW_NDEV_ID(dev), SKW_CMD_ASSOC, + buff, size, NULL, 0, "SKW_CMD_ASSOC", + msecs_to_jiffies(SKW_CMD_TIMEOUT), 0); + + SKW_KFREE(buff); + + return ret; +} + +static void skw_fix_compatibility_issues(struct wiphy *wiphy, + struct skw_iface *iface, struct cfg80211_bss *bss, + int he_enable, struct skw_center_chn *cc) +{ + struct net_device *ndev = iface->ndev; + + if (ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return; + + skw_set_he_mib(wiphy, ndev, he_enable); + +#if 0 // disabled for lite + rcu_read_lock(); + + he_oper_ie = skw_bss_get_ext_ie(bss, SKW_WLAN_EID_EXT_HE_OPERATION); + if (he_oper_ie) { + he_oper = (struct skw_he_oper_elem *)(he_oper_ie + 3); + + if (he_oper->bss_color == 0 && + (int)bss->channel->band == (int)NL80211_BAND_5GHZ && + !(skw->fw.fw_bw_capa & (SKW_BW_5GHZ_80M | SKW_BW_5GHZ_160M | SKW_BW_5GHZ_8080M))) { + if (he_oper->bss_color_disabled == 0) { + skw_set_vht_mib(wiphy, ndev, 0); + skw_info("Disable VHT"); + } else { + if (cc->bw >= SKW_CHAN_WIDTH_80 && + skw_bss_check_vendor_name(bss, oui)) { + skw_set_he_mib(wiphy, ndev, 0); + skw_info("Disable HE"); + + } + } + } + + } + + rcu_read_unlock(); +#endif +} + +static int skw_join(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_bss *bss, bool roaming) +{ + int ret = 0, he_enable; + struct skw_peer *peer; + struct skw_peer_ctx *ctx; + struct skw_center_chn cc = {}; + struct skw_join_resp resp = {}; + struct skw_iface *iface = netdev_priv(ndev); + struct skw_sta_core *core = &iface->sta.core; + + skw_wdev_assert_lock(iface); + + peer = skw_peer_alloc(); + if (!peer) { + skw_err("alloc peer failed\n"); + return -ENOMEM; + } + + skw_parse_center_chn(bss, &he_enable, &cc); + skw_fix_compatibility_issues(wiphy, iface, bss, he_enable, &cc); + + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_DEAUTH); + ret = skw_cmd_join(wiphy, ndev, bss, cc.bw, cc.band, cc.center_chn1, + cc.center_chn2, roaming, &resp); + if (ret < 0) { + skw_err("command join failed, ret: %d\n", ret); + SKW_KFREE(peer); + + return ret; + } + + skw_peer_init(peer, bss->bssid, resp.peer_idx); + ctx = skw_get_ctx(iface->skw, resp.lmac_id, resp.peer_idx); + ret = skw_peer_ctx_bind(iface, ctx, peer); + if (ret) { + skw_cmd_unjoin(wiphy, ndev, bss->bssid, SKW_LEAVE, false); + SKW_KFREE(peer); + return -EFAULT; + } + + skw_join_resp_handler(wiphy_priv(wiphy), iface, &resp); + + skw_ether_copy(core->bss.bssid, bss->bssid); + core->bss.channel = bss->channel; + core->bss.ctx_idx = resp.peer_idx; + core->bss.width = cc.bw; + + skw_dpd_set_coeff_params(wiphy, ndev, bss->channel->hw_value, + cc.center_chn1, cc.center_chn2, cc.bw); + + if (!iface->sta.sme_external) { + if (!is_valid_ether_addr(iface->sta.conn->prev_bssid)) + core->bss.auth_type = iface->sta.conn->auth_type; + } + + return 0; +} + +static int skw_unjoin(struct wiphy *wiphy, struct net_device *ndev, + const u8 *bssid, u16 reason, bool tx_frame) +{ + int ret = 0; + struct skw_peer_ctx *ctx; + struct skw_iface *iface = netdev_priv(ndev); + + skw_dbg("bssid: %pM, reason: %d\n", bssid, reason); + + if (ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION) { + skw_set_he_mib(wiphy, ndev, 1); + skw_set_vht_mib(wiphy, ndev, 1); + } + + ctx = skw_peer_ctx(iface, bssid); + if (!ctx) { + skw_warn("bssid: %pM not exist\n", bssid); + return 0; + } + + skw_peer_ctx_transmit(ctx, false); + + iface->sta.is_wep = false; + SKW_SET(iface->flags, SKW_IFACE_FLAG_DEAUTH); + + ret = skw_cmd_unjoin(wiphy, ndev, bssid, reason, tx_frame); + if (!ret) { + memset(&iface->sta.core.bss, 0x0, sizeof(iface->sta.core.bss)); + iface->sta.core.bss.ctx_idx = SKW_INVALID_ID; + + skw_lmac_unbind_iface(wiphy_priv(wiphy), iface->lmac_id, iface->id); + + skw_peer_ctx_bind(iface, ctx, NULL); + } else { + skw_warn("command unjoin failed, ret: %d\n", ret); + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_DEAUTH); + } + + return ret; +} + +int skw_sta_leave(struct wiphy *wiphy, struct net_device *dev, + const u8 *bssid, u16 reason, bool tx_frame) +{ + int i; + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("bssid: %pM, reason: %d\n", bssid, reason); + + skw_wdev_assert_lock(iface); + + netif_carrier_off(dev); + + if (iface->skw->hw.bus == SKW_BUS_PCIE && + iface->sta.core.sm.state >= SKW_STATE_ASSOCED) + skw_edma_dec_refill((void *)iface->skw, iface->lmac_id); + + memset(&iface->wmm, 0x0, sizeof(iface->wmm)); + + del_timer_sync(&iface->sta.core.timer); + + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + iface->sta.core.sm.flags = 0; + + skw_unjoin(wiphy, dev, bssid, reason, tx_frame); + skw_purge_key_conf(&iface->key_conf); + + memset(iface->sta.core.bss.ssid, 0x0, IEEE80211_MAX_SSID_LEN); + iface->sta.core.bss.ssid_len = 0; + + for (i = 0; i < SKW_MAX_DEFRAG_ENTRY; i++) { + skb_queue_purge(&iface->frag[i].skb_list); + iface->frag[i].tid = SKW_INVALID_ID; + } + + return 0; +} + +void skw_tx_mlme_mgmt(struct net_device *dev, u16 stype, + const u8 *bssid, const u8 *da, u16 reason) +{ + struct ieee80211_mgmt mgmt; + struct skw_iface *iface = netdev_priv(dev); + + mgmt.duration = 0; + mgmt.seq_ctrl = 0; + memcpy(mgmt.da, da, ETH_ALEN); + memcpy(mgmt.sa, iface->addr, ETH_ALEN); + memcpy(mgmt.bssid, bssid, ETH_ALEN); + mgmt.u.deauth.reason_code = cpu_to_le16(reason); + mgmt.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); + + skw_cfg80211_tx_mlme_mgmt(dev, (void *)&mgmt, SKW_DEAUTH_FRAME_LEN); +} + +static int skw_auth(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_auth_request *req) +{ + int ret; + struct key_params key; + bool roaming = false; + struct skw_iface *iface = netdev_priv(ndev); + struct skw_bss_cfg *bss = &iface->sta.core.bss; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + const u8 *auth_data = req->auth_data; +#else + const u8 *auth_data = req->sae_data; +#endif + + skw_info("%s, bssid: %pM, auth type: %d, state: %s\n", + netdev_name(ndev), req->bss->bssid, + req->auth_type, skw_state_name(iface->sta.core.sm.state)); + + skw_wdev_assert_lock(iface); + +#ifdef CONFIG_SWT6621S_USB3_WORKAROUND + ret = skw_switch_usb3_to_usb2_using_2G(iface, req->bss->channel->band); + if (ret) + return ret; +#endif + + skw_abort_scan(wiphy, ndev->ieee80211_ptr); + + // skw_scan_done(iface->skw, iface, true); + // skw_sched_scan_stop(wiphy, ndev, iface->skw->sched_scan_req->reqid); + + switch (iface->sta.core.sm.state) { + case SKW_STATE_AUTHING: + case SKW_STATE_ASSOCING: + return -EBUSY; + + case SKW_STATE_ASSOCED: + case SKW_STATE_COMPLETED: + if (ether_addr_equal(bss->bssid, req->bss->bssid)) + return 0; + + roaming = true; + + if (iface->sta.sme_external) + skw_tx_mlme_mgmt(iface->ndev, IEEE80211_STYPE_DEAUTH, + iface->sta.core.bss.bssid, + iface->sta.core.bss.bssid, SKW_LEAVE); + + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + + ret = skw_unjoin(wiphy, ndev, bss->bssid, SKW_LEAVE, false); + if (ret) + return ret; + + /* fall through */ + skw_fallthrough; + case SKW_STATE_NONE: + if (is_valid_ether_addr(bss->bssid)) { + skw_warn("unexpected bssid: %pM\n", bss->bssid); + ret = skw_unjoin(wiphy, ndev, bss->bssid, 3, false); + if (ret) + return ret; + } + + if (!skw_channel_allowed(wiphy, req->bss->channel->hw_value)) + return -EBUSY; + + ret = skw_join(wiphy, ndev, req->bss, roaming); + if (ret < 0) + return ret; + + break; + + default: + break; + } + + if (req->key && req->key_len) { + key.seq = NULL; + key.seq_len = 0; + key.key = (u8 *)req->key; + key.key_len = req->key_len; + key.cipher = SKW_CIPHER_SUITE_WEP40; + + if (req->key_len != 5) + key.cipher = SKW_CIPHER_SUITE_WEP104; + + ret = skw_add_key(wiphy, ndev, 0, req->key_idx, false, NULL, &key); + if (ret < 0) { + skw_err("add share key failed, ret: %d\n", ret); + goto unjoin; + } + + iface->sta.is_wep = true; + skw_set_default_key(wiphy, ndev, 0, req->key_idx, true, true); + } + + iface->sta.core.auth_start = jiffies; + iface->sta.core.pending.retry = 0; + iface->sta.core.pending.redo = 0; + iface->sta.core.pending.step_start = jiffies; + iface->sta.core.pending.ctx_start = jiffies; + iface->sta.core.pending.auth_type = req->auth_type; + iface->sta.core.sm.rty_state = SKW_RETRY_NONE; + iface->sta.core.pending.ctx_to = SKW_AUTH_TIMEOUT; + skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHING); + + ret = skw_cmd_auth(wiphy, ndev, req); + if (ret) { + skw_dbg("command auth failed, ret: %d\n", ret); + + goto unjoin; + } + + skw_set_sta_timer(&iface->sta.core, SKW_STEP_TIMEOUT); + + /* SAE confirm */ + if (auth_data && le16_to_cpu(*((u16 *)auth_data) == 2) && + iface->sta.core.sm.flags & SKW_SM_FLAG_SAE_RX_CONFIRM) + skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHED); + + iface->sta.report_deauth = true; + return 0; + +unjoin: + skw_unjoin(wiphy, ndev, req->bss->bssid, SKW_LEAVE, false); + + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + + return ret; +} + +static int skw_cfg80211_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_auth_request *req) +{ + struct skw_iface *iface = netdev_priv(dev); + + iface->sta.report_deauth = false; + + return skw_auth(wiphy, dev, req); +} + +static int skw_assoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + int ret; + const u8 *ssid_ie; + struct skw_iface *iface = netdev_priv(dev); + struct skw_sta_core *core = &iface->sta.core; + + skw_dbg("%s, bssid: %pM\n", netdev_name(dev), req->bss->bssid); + + skw_wdev_assert_lock(iface); + + switch (core->sm.state) { + case SKW_STATE_AUTHING: + case SKW_STATE_ASSOCING: + return -EBUSY; + + case SKW_STATE_ASSOCED: + case SKW_STATE_COMPLETED: + if (ether_addr_equal(core->bss.bssid, req->bss->bssid)) + return 0; + + skw_set_state(&core->sm, SKW_STATE_NONE); + + ret = skw_unjoin(wiphy, dev, core->bss.bssid, SKW_LEAVE, false); + if (ret) + return ret; + + ret = skw_join(wiphy, dev, req->bss, true); + if (ret) + return ret; + + skw_set_state(&core->sm, SKW_STATE_AUTHED); + + break; + + /* continue */ + case SKW_STATE_AUTHED: + break; + + default: + return -EINVAL; + } + + rcu_read_lock(); + + ssid_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + if (ssid_ie) { + memcpy(core->bss.ssid, ssid_ie + 2, ssid_ie[1]); + core->bss.ssid_len = ssid_ie[1]; + } + + rcu_read_unlock(); + + core->cbss = req->bss; + core->pending.retry = 0; + core->pending.ctx_start = jiffies; + iface->sta.core.pending.ctx_to = SKW_ASSOC_TIMEOUT; + core->assoc_req_ie_len = 0; + memset(core->assoc_req_ie, 0x0, SKW_2K_SIZE); + + skw_set_state(&core->sm, SKW_STATE_ASSOCING); + + ret = skw_cmd_assoc(wiphy, dev, req); + if (!ret) { + skw_set_sta_timer(core, SKW_STEP_TIMEOUT); + } else { + skw_err("command assoc failed, ret: %d\n", ret); + + core->cbss = NULL; + + del_timer_sync(&core->timer); + + skw_unjoin(wiphy, dev, req->bss->bssid, SKW_LEAVE, false); + skw_set_state(&core->sm, SKW_STATE_NONE); + + memset(core->bss.ssid, 0x0, IEEE80211_MAX_SSID_LEN); + core->bss.ssid_len = 0; + } + + return ret; +} + +static int skw_cfg80211_assoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + return skw_assoc(wiphy, dev, req); +} + +static int skw_cfg80211_deauth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_deauth_request *req) +{ + int ret; + struct skw_iface *iface = netdev_priv(dev); + bool tx_frame = !req->local_state_change && iface->sta.report_deauth; + + skw_info("%s: bssid: %pM, reason: %d, tx frame: %d\n", + netdev_name(dev), req->bssid, req->reason_code, tx_frame); + + ret = skw_sta_leave(wiphy, dev, req->bssid, req->reason_code, tx_frame); + if (!ret && iface->sta.report_deauth) { + skw_tx_mlme_mgmt(dev, IEEE80211_STYPE_DEAUTH, + req->bssid, req->bssid, + req->reason_code); + } else { + skw_err("failed, ret: %d\n", ret); + } + + return ret; +} + +static int skw_disassoc(struct wiphy *wiphy, struct net_device *dev, + const u8 *bssid, u16 reason, bool tx_frame) +{ + int ret = 0; + + skw_info("%s, bssid: %pM, reason: %d, tx frame: %d\n", + netdev_name(dev), bssid, reason, tx_frame); + + ret = skw_sta_leave(wiphy, dev, bssid, reason, tx_frame); + if (ret) { + skw_err("failed, ret: %d\n", ret); + return ret; + } + + skw_tx_mlme_mgmt(dev, IEEE80211_STYPE_DISASSOC, bssid, bssid, reason); + + return 0; +} + +void skw_connected(struct net_device *dev, struct skw_connect_param *conn, + const u8 *req_ie, int req_ie_len, const u8 *resp_ie, + int resp_ie_len, u16 status, gfp_t gfp) +{ + if (conn->flags & SKW_CONN_FLAG_ASSOCED) { + skw_compat_cfg80211_roamed(dev, conn->bssid, req_ie, + req_ie_len, resp_ie, resp_ie_len, gfp); + } else { + cfg80211_connect_result(dev, conn->bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, status, gfp); + } + + SKW_SET(conn->flags, SKW_CONN_FLAG_ASSOCED); +} + +void skw_disconnected(struct net_device *dev, u16 reason, const u8 *resp_ie, + int resp_ie_len, bool local_gen, gfp_t gfp) +{ + struct skw_iface *iface = netdev_priv(dev); + struct skw_connect_param *conn = iface->sta.conn; + + mutex_lock(&conn->lock); + if (conn->flags & SKW_CONN_FLAG_ASSOCED) { + skw_compat_disconnected(dev, reason, NULL, 0, local_gen, gfp); + } else { + cfg80211_connect_result(dev, conn->bssid, NULL, 0, + resp_ie, resp_ie_len, reason, gfp); + } + + SKW_CLEAR(iface->sta.conn->flags, SKW_CONN_FLAG_ASSOCED); + mutex_unlock(&conn->lock); +} + +int skw_connect_sae_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_bss *bss) +{ + int ret = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + bool roaming = false; + struct skw_iface *iface = netdev_priv(dev); + struct skw_connect_param *conn = iface->sta.conn; + struct cfg80211_external_auth_params params; + + if (!bss) { + cfg80211_connect_result(dev, conn->bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + + return -EINVAL; + } + + // TODO: + // unjoin prev bssid for roaming connection + + roaming = is_valid_ether_addr(conn->prev_bssid); + ret = skw_join(wiphy, dev, bss, roaming); + if (ret < 0) { + skw_err("join %pM failed\n", conn->bssid); + return ret; + } + + skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHING); + + params.action = NL80211_EXTERNAL_AUTH_START; + memcpy(params.bssid, conn->bssid, ETH_ALEN); + + params.ssid.ssid_len = conn->ssid_len; + memcpy(params.ssid.ssid, conn->ssid, conn->ssid_len); + + params.key_mgmt_suite = cpu_to_be32(WLAN_AKM_SUITE_SAE); + params.status = WLAN_STATUS_SUCCESS; + + ret = cfg80211_external_auth_request(dev, ¶ms, GFP_KERNEL); + if (ret) { + skw_err("failed, ret: %d\n", ret); + + skw_unjoin(wiphy, dev, conn->bssid, SKW_LEAVE, false); + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + + cfg80211_connect_result(dev, conn->bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } +#endif + + return ret; +} + +int skw_connect_auth(struct wiphy *wiphy, struct net_device *dev, + struct skw_connect_param *conn, struct cfg80211_bss *bss) +{ + struct cfg80211_auth_request req; + + if (!bss) { + skw_warn("Invalid bss\n"); + return -EINVAL; + } + + memset(&req, 0x0, sizeof(req)); + + req.bss = bss; + req.key = conn->key_len ? conn->key : NULL; + req.key_len = conn->key_len; + req.key_idx = conn->key_idx; + req.auth_type = conn->auth_type; + + return skw_auth(wiphy, dev, &req); +} + +int skw_connect_assoc(struct wiphy *wiphy, struct net_device *ndev, + struct skw_connect_param *conn) +{ + int ret = 0; + struct cfg80211_assoc_request req = {}; + + req.bss = cfg80211_get_bss(wiphy, conn->channel, conn->bssid, + conn->ssid, conn->ssid_len, + SKW_BSS_TYPE_ESS, SKW_PRIVACY_ESS_ANY); + if (!req.bss) { + skw_info("cfg80211_get_bss null\n"); + return -ENOENT; + } + + req.ie = conn->assoc_ie; + req.ie_len = conn->assoc_ie_len; + req.prev_bssid = conn->prev_bssid; + req.use_mfp = conn->flags & SKW_CONN_FLAG_USE_MFP; + req.flags = conn->flags; + req.ht_capa = conn->ht_capa; + req.ht_capa_mask = conn->ht_capa_mask; + req.vht_capa = conn->vht_capa; + req.vht_capa_mask = conn->vht_capa_mask; + + ret = skw_assoc(wiphy, ndev, &req); + + cfg80211_put_bss(wiphy, req.bss); + + return ret; +} + +int skw_roam_connect(struct skw_iface *iface, const u8 *bssid, u8 chn, + enum nl80211_band band) +{ + struct ieee80211_channel *req_channel = NULL; + struct wiphy *wiphy = iface->wdev.wiphy; + struct skw_connect_param *conn = iface->sta.conn; + u32 freq = 0; + + if (!is_valid_ether_addr(bssid)) + return -EINVAL; + + skw_dbg("roam from %pM to %pM auth_type: %d, chn: %d, band: %d\n", + conn->bssid, bssid, conn->auth_type, chn, band); + freq = ieee80211_channel_to_frequency(chn, band); + req_channel = ieee80211_get_channel(wiphy, freq); + + if (!req_channel) { + skw_err("invalid channel: %d\n", chn); + return -EINVAL; + } +#if 0 + iface->sta.backup = SKW_KMEMDUP(&iface->sta.core.bss, + sizeof(iface->sta.core.bss), + GFP_KERNEL); + if (!iface->sta.backup) + return -EINVAL; + + // skw_peer_transmit(); + memset(&iface->sta.core.bss, 0x0, sizeof(iface->sta.core.bss)); +#endif + conn->channel = req_channel; + skw_ether_copy(conn->bssid, bssid); + skw_ether_copy(conn->prev_bssid, iface->sta.core.bss.bssid); + + conn->auth_type = iface->sta.conn->auth_type; + + skw_queue_local_event(priv_to_wiphy(iface->skw), iface, + SKW_EVENT_LOCAL_STA_CONNECT, NULL, 0); + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +static int skw_set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct skw_set_cqm_rssi_param cqm_param; + + skw_dbg("dev: %s, thold: %d, hyst: %d\n", + netdev_name(dev), rssi_thold, rssi_hyst); + + //TBD: whether to store the config at host driver + + cqm_param.rssi_thold = rssi_thold; + cqm_param.rssi_hyst = (u8)rssi_hyst; + + return skw_send_msg(wiphy, dev, SKW_CMD_SET_CQM_RSSI, &cqm_param, + sizeof(cqm_param), NULL, 0); +} + +static int skw_set_cqm_rssi_range_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_low, s32 rssi_high) +{ + struct skw_set_cqm_rssi_param cqm_param = {0}; + s32 val = rssi_high - rssi_low; + + skw_dbg("dev: %s, rssi_low: %d, rssi_high: %d\n", + netdev_name(dev), rssi_low, rssi_high); + + cqm_param.rssi_thold = rssi_low; + if (val < 0) { + skw_warn("rssi err\n"); + return -EINVAL; + } else { + cqm_param.rssi_hyst = (u8)val; + } + + return skw_send_msg(wiphy, dev, SKW_CMD_SET_CQM_RSSI, &cqm_param, + sizeof(cqm_param), NULL, 0); +} +#endif + +static int skw_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_connect_params *req) +{ + struct skw_iface *iface = netdev_priv(ndev); + struct skw_connect_param *conn = iface->sta.conn; + const u8 *bssid = skw_compat_bssid(req); + struct ieee80211_channel *channel = skw_compat_channel(req); + + skw_dbg("%s, ssid: %s, bssid: %pM, auth: %d, chn: %d key_len: %d\n", + netdev_name(ndev), req->ssid, bssid, req->auth_type, + channel->hw_value, req->key_len); + + if (!conn) { + skw_dbg("conn is NULL\n"); + return -ENOMEM; + } + + if (unlikely(req->ssid_len > IEEE80211_MAX_SSID_LEN)) { + skw_err("Invalid SSID: %s, len: %zd\n", + req->ssid, req->ssid_len); + + return -EINVAL; + } + + mutex_lock(&conn->lock); + + skw_ether_copy(conn->bssid, bssid); + eth_zero_addr(conn->prev_bssid); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) + if (req->prev_bssid) + skw_ether_copy(conn->prev_bssid, req->prev_bssid); +#endif + + if (req->ie && req->ie_len) + memcpy(conn->assoc_ie, req->ie, req->ie_len); + + conn->assoc_ie_len = req->ie_len; + + if (req->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { + conn->auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + SKW_SET(conn->flags, SKW_CONN_FLAG_AUTH_AUTO); + } + + if (req->key && req->key_len) { + memcpy(conn->key, req->key, req->key_len); + conn->key_len = req->key_len; + conn->key_idx = req->key_idx; + SKW_SET(conn->flags, SKW_CONN_FLAG_KEY_VALID); + } else + conn->key_len = 0; + + conn->ssid_len = req->ssid_len; + memcpy(conn->ssid, req->ssid, req->ssid_len); + + conn->auth_type = req->auth_type; + conn->ht_capa = req->ht_capa; + conn->vht_capa = req->vht_capa; + + conn->ht_capa_mask = req->ht_capa_mask; + conn->vht_capa_mask = req->vht_capa_mask; + + mutex_unlock(&conn->lock); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + if (iface->wdev.iftype == NL80211_IFTYPE_STATION) { + skw_set_cqm_rssi_config(wiphy, ndev, SKW_CQM_DEFAUT_RSSI_THOLD, + SKW_CQM_DEFAUT_RSSI_HYST); + } +#endif + + return skw_queue_local_event(wiphy, iface, + SKW_EVENT_LOCAL_STA_CONNECT, NULL, 0); +} + + +static int skw_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *dev, u16 reason) +{ + int ret; + struct skw_iface *iface = netdev_priv(dev); + struct skw_sta_core *core = &iface->sta.core; + + skw_info("%s, reason: %d\n", netdev_name(dev), reason); + + ret = skw_sta_leave(wiphy, dev, core->bss.bssid, reason, true); + if (!ret) + skw_disconnected(dev, reason, NULL, 0, true, GFP_KERNEL); + + return ret; +} + +static u64 skw_tx_cookie(void) +{ + static u64 skw_cookie; + + if (WARN_ON(++skw_cookie == 0)) + skw_cookie++; + + return skw_cookie; +} + +static int skw_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, u64 *cookie) +{ + int ret; + struct skw_roc_param roc; + u64 tx_cookie = skw_tx_cookie(); + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + + skw_dbg("iface: %u, chan: %u, band: %u duration: %d, cookie: %llu\n", + iface->id, chan->hw_value, chan->band, duration, tx_cookie); + + roc.enable = 1; + roc.channel_num = chan->hw_value; + roc.band = to_skw_band(chan->band); + roc.duration = duration; + roc.cookie = *cookie = tx_cookie; + //TBD: define the referenced value + if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + roc.channel_type = 2; + else if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + roc.channel_type = 1; + else if (chan->flags & SKW_IEEE80211_CHAN_NO_20MHZ) + roc.channel_type = 0; + else + roc.channel_type = 3; + + ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_REMAIN_ON_CHANNEL, + &roc, sizeof(roc), NULL, 0); + + return ret; +} + +static int skw_cancel_roc(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie) +{ + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + struct skw_roc_param param; + + skw_dbg("cookie: %lld\n", cookie); + +#if 0 + // fixme: + if (cookie != skw->remain_on_channel_cookie) + return -ENOENT; +#endif + + memset(¶m, 0x0, sizeof(param)); + + return skw_msg_xmit(wiphy, iface->id, SKW_CMD_REMAIN_ON_CHANNEL, + ¶m, sizeof(param), NULL, 0); +} +static inline void __skw_set_peer_flags(struct skw_peer_ctx *ctx, u32 flags) +{ + if (ctx) { + skw_peer_ctx_lock(ctx); + + if (ctx->peer) + ctx->peer->flags |= flags; + + skw_peer_ctx_unlock(ctx); + } +} + +static void skw_set_peer_flags(struct skw_iface *iface, + const u8 *addr, u32 flags) +{ + int idx; + struct skw_peer_ctx *ctx; + u32 peer_map = atomic_read(&iface->peer_map); + + if (!addr) + return; + + if (is_unicast_ether_addr(addr)) { + ctx = skw_peer_ctx(iface, addr); + __skw_set_peer_flags(ctx, flags); + return; + } + + while (peer_map) { + idx = ffs(peer_map) - 1; + SKW_CLEAR(peer_map, BIT(idx)); + + ctx = &iface->skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; + __skw_set_peer_flags(ctx, flags); + } +} + +int skw_mgmt_tx(struct wiphy *wiphy, struct skw_iface *iface, + struct ieee80211_channel *chan, u32 wait, u64 *cookie, + bool dont_wait_ack, const void *frame, int frame_len, + int total_frame_len, const struct ieee80211_mgmt *mgmt, bool switchover) +{ + int ret, total_len; + struct skw_mgmt_tx_param *param = NULL; + u64 tx_cookie = skw_tx_cookie(); + u16 fc = SKW_MGMT_SFC(mgmt->frame_control); + + if (!chan || !iface) + return -EINVAL; + + if (frame_len > total_frame_len) + return -EFBIG; + + skw_dbg("%s: chan: %d, wait: %d, cookie: 0x%llx, no_ack: %d, len: %d total: %d\n", + skw_mgmt_name(fc), chan->hw_value, wait, tx_cookie, + dont_wait_ack, frame_len, total_frame_len); + + skw_hex_dump("mgmt tx", frame, frame_len, false); + + total_len = sizeof(*param) + frame_len; + param = SKW_ZALLOC(total_len, GFP_KERNEL); + if (!param) { + skw_err("malloc failed, size: %d\n", total_len); + return -ENOMEM; + } + + param->wait = wait; + param->channel = chan->hw_value; + param->band = to_skw_band(chan->band); + param->dont_wait_for_ack = dont_wait_ack; + param->cookie = *cookie = tx_cookie; + + memcpy(param->mgmt, frame, frame_len); + param->mgmt_frame_len = total_frame_len; + + ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_TX_MGMT, + param, total_len, NULL, 0); + if (!ret) { + if (switchover) + if (is_unicast_ether_addr(mgmt->da) && + (fc == IEEE80211_STYPE_DEAUTH || + fc == IEEE80211_STYPE_DISASSOC)) { + skw_set_peer_flags(iface, mgmt->da, + SKW_PEER_FLAG_DEAUTHED); + } + } else { + skw_err("failed, ret: %d\n", ret); + } + + SKW_KFREE(param); + + return ret; +} + +static inline bool is_skw_rrm_report(const void *buf, int buf_len) +{ + const struct ieee80211_mgmt *mgmt = buf; + + if (!ieee80211_is_action(mgmt->frame_control)) + return false; + + if (buf_len < IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.measurement)) + return false; + + if (mgmt->u.action.category != SKW_WLAN_CATEGORY_RADIO_MEASUREMENT) + return false; + + if (mgmt->u.action.u.measurement.action_code != WLAN_ACTION_SPCT_MSR_RPRT) + return false; + + return true; +} + +static int __skw_cfg80211_mgmt_tx(struct wiphy *wiphy, struct skw_iface *iface, + struct ieee80211_channel *chan, u32 wait, + u64 *cookie, bool dont_wait_for_ack, + const void *frame, int frame_len) +{ + struct ieee80211_channel *tx_chan = chan; + struct skw_core *skw = wiphy_priv(wiphy); + int ret = 0; + +#define SKW_MGMT_TX_LEN 1500 + + if (!tx_chan) { + if (is_skw_sta_mode(iface)) + tx_chan = iface->sta.core.bss.channel; + else + tx_chan = iface->sap.cfg.channel; + } + + + skw_hex_dump("frame", frame, frame_len, false); + + down(&skw->cmd.mgmt_cmd_lock); + + if (!skw_cmd_data_len_in_limit(skw, frame_len + sizeof(struct skw_mgmt_tx_param))) { + if (is_skw_rrm_report(frame, frame_len)) { + int head_offset = offsetof(struct ieee80211_mgmt, + u.action.u.measurement.element_id); + + int ret = -E2BIG; + int elem_len = 0, next_len = 0; + int left = frame_len - head_offset; + char *pos = (u8 *)frame + head_offset, *next = pos; + char *data = NULL; + + data = SKW_ZALLOC(SKW_MGMT_TX_LEN, GFP_KERNEL); + if (!data) { + skw_err("alloc %d failed\n", SKW_MGMT_TX_LEN); + ret = -ENOMEM; + goto unlock; + } + + while (left) { + int tx_len; + + next_len = next[1] + 2; + tx_len = elem_len + head_offset + next_len; + if (tx_len < SKW_MGMT_TX_LEN) { + elem_len += next_len; + left -= next_len; + + if (left) { + next += next_len; + continue; + } + } + + memcpy(data, frame, head_offset); + memcpy(data + head_offset, pos, elem_len); + + skw_hex_dump("rrm", data, elem_len + head_offset, false); + + ret = skw_mgmt_tx(wiphy, iface, tx_chan, wait, + cookie, dont_wait_for_ack, data, + elem_len + head_offset, elem_len + head_offset, frame, true); + + pos = next; + elem_len = 0; + } + + SKW_KFREE(data); + goto unlock; + + } else { + int send_len; + int remain = frame_len; + const u8 *data = frame; + int buf_size = SKW_MGMT_TX_LEN; + + while (remain > 0) { + send_len = (remain < buf_size) ? remain : buf_size; + remain -= send_len; + + skw_dbg("mgmt part : remain: %d, send_len: %d \n", remain, send_len); + skw_hex_dump("part", data, send_len, false); + + ret = skw_mgmt_tx(wiphy, iface, tx_chan, wait, + cookie, true, data, send_len, + frame_len, frame, true); + + if (ret) { + skw_err("failed, ret: %d\n", ret); + goto unlock; + } + + data += send_len; + } + + if (dont_wait_for_ack == false) { + struct skw_mgmt_status status; + status.mgmt_status_data = SKW_ZALLOC(frame_len, GFP_KERNEL); + + if (!status.mgmt_status_data) { + skw_err("malloc mgmt_status_data failed, size: %d\n", frame_len); + ret = -ENOMEM; + } else { + status.mgmt_status_data_len = frame_len; + memcpy(status.mgmt_status_data, frame, frame_len); + status.mgmt_status_cookie = *cookie; + + skw_dbg("split part coockie:0x%llx\n", status.mgmt_status_cookie); + skw_queue_work(wiphy, iface, + SKW_WORK_SPLIT_MGMT_TX_STATUS, &status, sizeof(status)); + } + } + + goto unlock; + } + } + +#undef SKW_MGMT_TX_LEN + skw_dump_frame((u8 *)frame, (u16)frame_len); + ret = skw_mgmt_tx(wiphy, iface, tx_chan, wait, cookie, + dont_wait_for_ack, frame, frame_len, frame_len, frame, true); + +unlock: + up(&skw->cmd.mgmt_cmd_lock); + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static int skw_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, + u64 *cookie) +{ + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + + return __skw_cfg80211_mgmt_tx(wiphy, iface, params->chan, + params->wait, cookie, + params->dont_wait_for_ack, + params->buf, params->len); +} +#else +static int skw_cfg80211_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) +{ + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + + return __skw_cfg80211_mgmt_tx(wiphy, iface, chan, wait, cookie, + dont_wait_for_ack, buf, len); +} +#endif + +static int skw_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + int i; + u8 *pos; + struct cfg80211_bss *bss; + struct ieee80211_mgmt *mgmt; + struct ieee80211_supported_band *sband; + struct skw_iface *iface = netdev_priv(dev); + struct cfg80211_chan_def *chandef = ¶ms->chandef; + + skw_dbg("%s, bssid: %pM, ssid: %s, channel: %d, band: %u, chan_fixed: %d\n", + netdev_name(dev), params->bssid, params->ssid, + chandef->chan->hw_value, chandef->chan->band, params->channel_fixed); + + if (params->bssid) + memcpy(iface->ibss.bssid, params->bssid, ETH_ALEN); + else + eth_random_addr(iface->ibss.bssid); + + iface->ibss.bw = to_skw_bw(params->chandef.width); + if (iface->ibss.bw == SKW_CHAN_WIDTH_MAX) + return -EINVAL; + + iface->ibss.beacon_int = params->beacon_interval; + iface->ibss.channel = chandef->chan->hw_value; + iface->ibss.band = to_skw_band(chandef->chan->band); + iface->ibss.center_freq1 = chandef->center_freq1; + iface->ibss.center_freq2 = chandef->center_freq2; + iface->ibss.chandef = params->chandef; + + // start build presp frame + mgmt = SKW_ZALLOC(SKW_2K_SIZE, GFP_KERNEL); + if (!mgmt) { + skw_err("malloc failed, size: %d\n", SKW_2K_SIZE); + return -ENOMEM; + } + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + + eth_broadcast_addr(mgmt->da); + memcpy(mgmt->sa, iface->addr, ETH_ALEN); + memcpy(mgmt->bssid, iface->ibss.bssid, ETH_ALEN); + + mgmt->u.beacon.beacon_int = cpu_to_le16(params->beacon_interval); + // mgmt->u.beacon.timestamp = cpu_to_le64(0); + mgmt->u.beacon.capab_info = cpu_to_le16(WLAN_CAPABILITY_IBSS); + + pos = mgmt->u.beacon.variable; + + *pos++ = WLAN_EID_SSID; + *pos++ = params->ssid_len; + memcpy(pos, params->ssid, params->ssid_len); + pos += params->ssid_len; + + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = 8; + sband = wiphy->bands[chandef->chan->band]; + + for (i = 0; i < sband->n_bitrates; i++) { + int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); + *pos++ = rate | 0x80; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) + if (sband->band == IEEE80211_BAND_2GHZ) { +#else + if (sband->band == NL80211_BAND_2GHZ) { +#endif + *pos++ = WLAN_EID_DS_PARAMS; + *pos++ = 1; + *pos++ = chandef->chan->hw_value; + } + + *pos++ = WLAN_EID_IBSS_PARAMS; + *pos++ = 2; + *pos++ = 0; + *pos++ = 0; +#if 0 + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = 0; +#endif + if (params->ie) { + memcpy(pos, params->ie, params->ie_len); + pos += params->ie_len; + } + // end build frame + +// skw_set_template_frame(); + bss = cfg80211_get_bss(wiphy, chandef->chan, params->bssid, + params->ssid, params->ssid_len, + SKW_BSS_TYPE_IBSS, + SKW_PRIVACY_IBSS_ANY); + if (!bss) { + skw_info("creating new ibss: %pM\n", iface->ibss.bssid); + + bss = cfg80211_inform_bss_frame(wiphy, chandef->chan, + mgmt, pos - (u8 *)mgmt, DBM_TO_MBM(-30), GFP_KERNEL); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) + // fixme: + if (params->wep_keys) { + skw_add_key(wiphy, dev, 0, params->wep_tx_key, true, + iface->ibss.bssid, params->wep_keys); + + skw_set_default_key(wiphy, dev, 0, params->wep_tx_key, true, true); + } +#endif + + cfg80211_put_bss(wiphy, bss); + + skw_queue_local_event(wiphy, iface, SKW_EVENT_LOCAL_IBSS_CONNECT, + NULL, 0); + + SKW_KFREE(mgmt); + return 0; +} + +static int skw_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct skw_disconnect_param params = {0}; + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("%s\n", netdev_name(dev)); + + iface->ibss.joined = false; + iface->ibss.ssid_len = 0; + + params.type = SKW_DISCONNECT_ONLY; + params.reason_code = 0; + + return skw_send_msg(wiphy, dev, SKW_CMD_DISCONNECT, + ¶ms, sizeof(params), NULL, 0); +} + +static int skw_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("changed: 0x%x\n", changed); + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) + return ret; + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (changed & WIPHY_PARAM_RETRY_SHORT) { + if (skw_tlv_add(&conf, SKW_MIB_RETRY_SHORT, + &wiphy->retry_short, + sizeof(wiphy->retry_short))) + skw_err("add SKW_MIB_RETRY_SHORT failed.\n"); + } + + if (changed & WIPHY_PARAM_RETRY_LONG) { + if (skw_tlv_add(&conf, SKW_MIB_RETRY_LONG, + &wiphy->retry_long, + sizeof(wiphy->retry_long))) + skw_err("add SKW_MIB_RETRY_LONG failed.\n"); + } + + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + if (skw_tlv_add(&conf, SKW_MIB_FRAG_THRESHOLD, + &wiphy->frag_threshold, + sizeof(wiphy->frag_threshold))) + skw_err("add SKW_MIB_FRAG_THRESHOLD failed.\n"); + } + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + if (skw_tlv_add(&conf, SKW_MIB_RTS_THRESHOLD, + &wiphy->rts_threshold, + sizeof(wiphy->rts_threshold))) + skw_err("add SKW_MIB_RTS_THRESHOLD failed.\n"); + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + } + + skw_tlv_free(&conf); + + return ret; +} + +static int skw_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, struct cfg80211_sched_scan_request *req) +{ + int i, ret; + struct skw_scan_chan_info *chan = NULL; + + u32 delay = 0; + u64 reqid = 0; + s8 relative_rssi = 0; + bool relative_rssi_set = false; + s32 min_rssi_thold = 0; + int n_scan_plans = 0, n_plans_len = 0; + int n_ssids_len, n_match_len; + int size, fixed, offset = 0; + u16 scan_chn_num = 0; + + + struct skw_sched_match_sets *match_sets; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_sched_scan_param *params; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + reqid = req->reqid; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + relative_rssi_set = req->relative_rssi_set; + relative_rssi = req->relative_rssi; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + n_scan_plans = req->n_scan_plans; + n_plans_len = n_scan_plans * sizeof(*req->scan_plans); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + delay = req->delay; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) + min_rssi_thold = req->rssi_thold; +#else + min_rssi_thold = req->min_rssi_thold; +#endif + + skw_dbg("%s, n_ssids: %d, n_channels: %d, n_match: %d, n_plans: %d, ie_len: %zd\n", + netdev_name(dev), req->n_ssids, req->n_channels, + req->n_match_sets, n_scan_plans, req->ie_len); + + fixed = sizeof(struct skw_sched_scan_param); + n_ssids_len = req->n_ssids * sizeof(struct cfg80211_ssid); + n_match_len = req->n_match_sets * sizeof(struct skw_sched_match_sets); + + size = fixed + req->ie_len + n_ssids_len + n_plans_len + n_match_len + + req->n_channels * sizeof(*chan); + + params = SKW_ZALLOC(size, GFP_KERNEL); + if (!params) { + skw_err("malloc failed, size: %d\n", size); + + return -ENOMEM; + } + + params->req_id = reqid; + params->flags = req->flags; + params->delay = delay; + params->min_rssi_thold = min_rssi_thold; + params->relative_rssi_set = relative_rssi_set; + params->relative_rssi = relative_rssi; + params->scan_width = NL80211_BSS_CHAN_WIDTH_20; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + skw_ether_copy(params->mac_addr, req->mac_addr); + skw_ether_copy(params->mac_addr_mask, req->mac_addr_mask); +#endif + + params->n_ssids = req->n_ssids; + if (req->n_ssids) { + params->n_ssid_offset = fixed + offset; + params->n_ssids_len = n_ssids_len; + memcpy(params->data + offset, req->ssids, n_ssids_len); + + offset += n_ssids_len; + } + + match_sets = (void *)params->data + offset; + for (i = 0; i < req->n_match_sets; i++) { + memcpy(match_sets[i].ssid, req->match_sets[i].ssid.ssid, 32); + match_sets[i].ssid_len = req->match_sets[i].ssid.ssid_len; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + match_sets[i].rssi_thold = req->match_sets[i].rssi_thold; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + skw_ether_copy(match_sets[i].bssid, req->match_sets[i].bssid); +#endif + } + + params->n_match_sets = req->n_match_sets; + params->match_sets_offset = fixed + offset; + params->match_sets_len = n_match_len; + offset += n_match_len; + + params->n_scan_plans = n_scan_plans; + if (n_scan_plans) { + params->scan_plans_offset = fixed + offset; + params->scan_plans_len = n_plans_len; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + memcpy(params->data + offset, req->scan_plans, n_plans_len); +#endif + + offset += n_plans_len; + } + + if (req->ie_len) { + params->ie_offset = fixed + offset; + params->ie_len = req->ie_len; + memcpy(params->data + offset, req->ie, req->ie_len); + offset += req->ie_len; + } + + chan = (struct skw_scan_chan_info *)(params->data + offset); + for (i = 0; i < req->n_channels; i++) { + if (is_skw_6ghz_non_psc_chan(req->channels[i])) + continue; + + chan->band = to_skw_band(req->channels[i]->band); + chan->chan_num = req->channels[i]->hw_value; + /* BIT[15]: set 1 means to run a passive scan on this channel */ + if (req->channels[i]->flags & SKW_PASSIVE_SCAN) + chan->scan_flags |= SKW_SCAN_FLAG_PASSIVE; + + chan++; + scan_chn_num++; + } + + params->n_channels = scan_chn_num; + params->channels_len = scan_chn_num * sizeof(struct skw_scan_chan_info); + params->channels_offset = fixed + offset; + + skw->sched_scan_req = req; + ret = skw_send_msg(wiphy, dev, SKW_CMD_START_SCHED_SCAN, + params, size, NULL, 0); + if (ret) { + skw_err("failed, ret: %d\n", ret); + skw->sched_scan_req = NULL; + } + + SKW_KFREE(params); + + return ret; +} + +static int skw_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev, u64 reqid) +{ + u64 scan_id = 0; + struct skw_core *skw = wiphy_priv(wiphy); + + skw_dbg("dev: %s, id: %lld, actived: %d\n", + netdev_name(dev), scan_id, !!skw->sched_scan_req); + + if (!skw->sched_scan_req) + return 0; + + skw->sched_scan_req = NULL; + return skw_send_msg(wiphy, dev, SKW_CMD_STOP_SCHED_SCAN, + &scan_id, sizeof(scan_id), NULL, 0); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +static int skw_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev, u64 reqid) +{ + return skw_sched_scan_stop(wiphy, dev, reqid); +} +#else +static int skw_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev) +{ + return skw_sched_scan_stop(wiphy, dev, 0); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) +static void skw_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct mgmt_frame_regs *upd) +{ + u64 ts; + int ret = 0, idx; + struct skw_mgmt_register_param param; + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + u16 new_mask = upd->interface_stypes; + u16 temp_mask = new_mask ^ iface->mgmt_frame_bitmap; + u16 frame_mtype; + bool reg; + + if (!temp_mask) + return; + + while (temp_mask) { + idx = ffs(temp_mask) - 1; + SKW_CLEAR(temp_mask, BIT(idx)); + frame_mtype = idx << 4; + reg = new_mask & BIT(idx); + + skw_dbg("%s %s filter %s\n", skw_iftype_name(wdev->iftype), + reg ? "add" : "del", skw_mgmt_name(frame_mtype)); + + param.frame_type = frame_mtype; + param.reg = reg; + ts = local_clock(); + do_div(ts, 1000000); + + param.timestamp = ts; + ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_REGISTER_FRAME, + ¶m, sizeof(param), NULL, 0); + if (ret) { + skw_err("%s %s failed, ret: %d\n", + reg ? "add" : "del", + skw_mgmt_name(frame_mtype), ret); + } + } + + iface->mgmt_frame_bitmap = new_mask; +} +#else +static void skw_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + u16 frame_type, bool reg) +{ + u64 ts = 0; + int ret = 0; + struct skw_mgmt_register_param param = {0}; + int type = (frame_type >> 4) & 0xf; + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + u16 bitmap = iface->mgmt_frame_bitmap; + + if (reg) + iface->mgmt_frame_bitmap |= BIT(type); + else + iface->mgmt_frame_bitmap &= ~BIT(type); + + if (bitmap == iface->mgmt_frame_bitmap) + return; + + skw_dbg("%s %s filter %s\n", skw_iftype_name(wdev->iftype), + reg ? "add" : "del", skw_mgmt_name(frame_type)); + + param.frame_type = frame_type; + param.reg = reg; + ts = local_clock(); + do_div(ts, 1000000); + + param.timestamp = ts; + ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_REGISTER_FRAME, + ¶m, sizeof(param), NULL, 0); + if (ret) { + skw_err("%s %s failed, ret: %d\n", + reg ? "add" : "del", + skw_mgmt_name(frame_type), ret); + } +} +#endif + +static int skw_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, int timeout) +{ + /* firmware trigger legacy ps automatically */ + skw_dbg("%s, enabled: %d, timeout: %d\n", + netdev_name(dev), enabled, timeout); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static int skw_set_qos_map(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_qos_map *qos_map) +{ + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("ndev: %s, %s qos_map\n", netdev_name(dev), + qos_map ? "add" : "del"); + + if (!qos_map) { + SKW_KFREE(iface->qos_map); + return 0; + } + + if (!iface->qos_map) { + iface->qos_map = SKW_ZALLOC(sizeof(struct cfg80211_qos_map), GFP_KERNEL); + if (!iface->qos_map) + return -ENOMEM; + } + + memcpy(iface->qos_map, qos_map, sizeof(*qos_map)); + + return 0; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) +static int skw_add_tx_ts(struct wiphy *wiphy, struct net_device *ndev, + u8 tsid, const u8 *peer, u8 up, u16 admitted_time) +{ + struct skw_ts_info ts; + + skw_dbg("dev: %s, ts id: %d, addr: %pM, up: %d, time: %d\n", + netdev_name(ndev), tsid, peer, up, admitted_time); + /* cfg80211 will make a sanity check */ + ts.up = up; + ts.tsid = tsid; + skw_ether_copy(ts.peer, peer); + ts.admitted_time = admitted_time; + + return skw_send_msg(wiphy, ndev, SKW_CMD_ADD_TX_TS, + &ts, sizeof(ts), NULL, 0); +} + +static int skw_del_tx_ts(struct wiphy *wiphy, struct net_device *ndev, + u8 tsid, const u8 *peer) +{ + struct skw_ts_info ts; + + skw_dbg("dev: %s, ts id: %d, addr: %pM\n", + netdev_name(ndev), tsid, peer); + + ts.tsid = tsid; + skw_ether_copy(ts.peer, peer); + ts.up = 0xFF; + ts.admitted_time = 0; + + return skw_send_msg(wiphy, ndev, SKW_CMD_DEL_TX_TS, + &ts, sizeof(ts), NULL, 0); +} +#endif + +static int skw_tdls_oper(struct wiphy *wiphy, struct net_device *ndev, + const u8 *peer_addr, enum nl80211_tdls_operation oper) +{ + int ret = 0; + struct skw_iface *iface = netdev_priv(ndev); + struct skw_tdls_oper tdls; + struct skw_peer_ctx *ctx; + + skw_dbg("dev: %s, oper: %d, addr: %pM\n", + netdev_name(ndev), oper, peer_addr); + + ctx = skw_peer_ctx(iface, peer_addr); + if (!ctx) + return -ENOENT; + + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + skw_peer_ctx_transmit(ctx, true); + break; + + case NL80211_TDLS_DISABLE_LINK: + skw_peer_ctx_transmit(ctx, false); + skw_peer_ctx_bind(iface, ctx, NULL); + + break; + + default: + ret = -ENOTSUPP; + break; + } + + if (ret) + return ret; + + tdls.oper = oper; + skw_ether_copy(tdls.peer_addr, peer_addr); + + return skw_send_msg(wiphy, ndev, SKW_CMD_TDLS_OPER, &tdls, + sizeof(tdls), NULL, 0); + +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +static int skw_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *ndev, + const u8 *peer_addr, enum nl80211_tdls_operation oper) +{ + return skw_tdls_oper(wiphy, ndev, peer_addr, oper); +} +#else +static int skw_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *ndev, + u8 *peer_addr, enum nl80211_tdls_operation oper) +{ + return skw_tdls_oper(wiphy, ndev, (const u8 *)peer_addr, oper); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +static int skw_tdls_chn_switch(struct wiphy *wiphy, struct net_device *ndev, + const u8 *addr, u8 oper_class, struct cfg80211_chan_def *def) +{ + int ret; + struct skw_tdls_chan_switch tdls; + struct skw_peer_ctx *ctx; + struct skw_iface *iface = netdev_priv(ndev); + + skw_dbg("dev: %s, addr: %pM, def chan: %d\n", + netdev_name(ndev), addr, def->chan->hw_value); + + ctx = skw_peer_ctx(iface, addr); + if (!ctx) { + skw_err("can't find tdls peer: %pM\n", addr); + return -EINVAL; + } + + if (!skw_channel_allowed(wiphy, def->chan->hw_value)) + return -EBUSY; + + switch (def->width) { + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: + tdls.chan_width = SKW_CHAN_WIDTH_20; + break; + case NL80211_CHAN_WIDTH_40: + tdls.chan_width = SKW_CHAN_WIDTH_40; + break; + case NL80211_CHAN_WIDTH_80: + tdls.chan_width = SKW_CHAN_WIDTH_80; + break; + default: + skw_err("channel width: %d not support\n", def->width); + return -ENOTSUPP; + } + + skw_ether_copy(tdls.addr, addr); + tdls.chn_switch_enable = 1; + tdls.oper_class = oper_class; + tdls.chn = def->chan->hw_value; + tdls.band = to_skw_band(def->chan->band); + + ret = skw_send_msg(wiphy, ndev, SKW_CMD_TDLS_CHANNEL_SWITCH, + &tdls, sizeof(tdls), NULL, 0); + if (!ret) { + skw_peer_ctx_lock(ctx); + + if (ctx->peer) + ctx->peer->channel = def->chan->hw_value; + + skw_peer_ctx_unlock(ctx); + } + + return ret; +} + +static void skw_tdls_cancel_chn_switch(struct wiphy *wiphy, + struct net_device *ndev, const u8 *addr) +{ + struct skw_tdls_chan_switch tdls; + struct skw_iface *iface = netdev_priv(ndev); + + skw_dbg("dev: %s, addr: %pM\n", netdev_name(ndev), addr); + + if (!skw_peer_ctx(iface, addr)) { + skw_dbg("can't find tdls peer:%pM\n", addr); + return; + } + + memset(&tdls, 0x0, sizeof(tdls)); + + tdls.chn_switch_enable = 0; + skw_ether_copy(tdls.addr, addr); + + if (skw_send_msg(wiphy, ndev, SKW_CMD_TDLS_CHANNEL_SWITCH, + &tdls, sizeof(tdls), NULL, 0) < 0) + skw_err("set command SKW_CMD_TDLS_CANCEL_CHN_SWITCH failed\n"); +} +#endif + +static int skw_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action, u8 token, u16 status, + u32 peer_capability, bool initiator, + const u8 *ies, size_t ies_len) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + return skw_tdls_build_send_mgmt(skw, dev, peer, action, token, status, + peer_capability, initiator, ies, ies_len); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) +static int skw_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action, u8 token, u16 status, + u32 peer_capability, bool initiator, + const u8 *ies, size_t ies_len) +{ + return skw_tdls_mgmt(wiphy, dev, peer, action, token, status, + peer_capability, initiator, ies, ies_len); +} +#else +static int skw_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *peer, u8 action, u8 token, u16 status, +#else + u8 *peer, u8 action, u8 token, u16 status, +#endif + u32 peer_capability, const u8 *ies, size_t ies_len) +{ + return skw_tdls_mgmt(wiphy, dev, (const u8 *)peer, action, token, + status, peer_capability, false, ies, ies_len); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) +//iw phy5 wowlan enable patterns 28+43:34:-:12 16+33:-:11:ee:12:34:-:88:99 +static int skw_wow_enable(struct wiphy *wiphy) +{ + int ret = 0; +#ifdef CONFIG_PM + struct cfg80211_wowlan *wow = wiphy->wowlan_config; + struct cfg80211_pkt_pattern *patterns = wow->patterns; + u32 i, j; + int total; + struct skw_spd_action_param *spd = NULL; + struct skw_wow_input_param *wow_param = NULL; + struct skw_wow_rule *rule; + struct skw_pkt_pattern *ptn; + struct skw_pkt_pattern ptn_tmp; + int vi = 0; + int y, b, start = 0, gap = 0; + u8 *rdata; + + total = sizeof(struct skw_spd_action_param) + + sizeof(struct skw_wow_input_param); + + if (wow->any) { + spd = SKW_ZALLOC(total, GFP_KERNEL); + if (!spd) { + skw_err("malloc failed, size: %d\n", total); + return -ENOMEM; + } + + wow_param = (struct skw_wow_input_param *)((u8 *)spd + + sizeof(*spd)); + wow_param->wow_flags = SKW_WOW_ANY_PKT; + wow_param->rule_num = 0; + spd->sub_cmd = ACTION_EN_WOW; + spd->len = sizeof(struct skw_wow_input_param); + goto cmd_send; + } + + total += sizeof(struct skw_wow_rule) * wow->n_patterns; + + spd = SKW_ZALLOC(total, GFP_KERNEL); + if (!spd) { + skw_err("malloc failed, size: %d\n", total); + return -ENOMEM; + } + + wow_param = (struct skw_wow_input_param *)((u8 *)spd + + sizeof(*spd)); + wow_param->rule_num = wow->n_patterns; + spd->sub_cmd = ACTION_EN_WOW; + + if (wow->disconnect) + wow_param->wow_flags |= SKW_WOW_DISCONNECT; + + if (wow->magic_pkt) + wow_param->wow_flags |= SKW_WOW_MAGIC_PKT; + + if (wow->gtk_rekey_failure) + wow_param->wow_flags |= SKW_WOW_GTK_REKEY_FAIL; + + if (wow->eap_identity_req) + wow_param->wow_flags |= SKW_WOW_EAP_IDENTITY_REQ; + + if (wow->four_way_handshake) + wow_param->wow_flags |= SKW_WOW_FOUR_WAY_HANDSHAKE; + + if (wow->rfkill_release) + wow_param->wow_flags |= SKW_WOW_RFKILL_RELEASE; + + for (i = 0; i < wow_param->rule_num; i++) { + rule = &wow_param->rules[i]; + rdata = rule->rule; + ptn_tmp.op = PAT_OP_TYPE_SAME; + ptn_tmp.type_offset = PAT_TYPE_ETH; + ptn_tmp.offset = patterns[i].pkt_offset; + ptn_tmp.len = 0; + + vi = 0; + start = 0; + gap = 0; + for (j = 0; j < patterns[i].pattern_len; j++) { + y = round_up(j + 1, 8)/8 - 1; + b = j%8; + if (patterns[i].mask[y] & BIT(b)) { + if (!start) { + if (vi + sizeof(ptn_tmp) + >= sizeof(rule->rule)) { + skw_warn("pat:%d overage\n", i); + break; + } + + ptn = + (struct skw_pkt_pattern *)&rdata[vi]; + memcpy(ptn, &ptn_tmp, sizeof(ptn_tmp)); + ptn->offset += gap; + vi += sizeof(ptn_tmp); + } + + rdata[vi++] = patterns[i].pattern[j]; + ptn->len++; + start = 1; + gap++; + + if (vi >= sizeof(rule->rule)) { + skw_warn("pat:%d overage\n", i); + break; + } + } else { + gap++; + start = 0; + } + } + rule->len = vi; + + skw_hex_dump("rule", rule, sizeof(*rule), false); + } + + spd->len = sizeof(struct skw_wow_input_param) + + sizeof(struct skw_wow_rule) * wow_param->rule_num; + +cmd_send: + skw_dbg("len:%d %d\n", spd->len, total); + skw_hex_dump("wow", spd, total, false); + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_SPD_ACTION, + spd, total, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + SKW_KFREE(spd); +#endif + return ret; +} +#endif + +int skw_wow_disable(struct wiphy *wiphy) +{ + struct skw_spd_action_param spd; + int ret = 0; + + spd.sub_cmd = ACTION_DIS_WOW; + spd.len = 0; + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_SPD_ACTION, + &spd, sizeof(spd), NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + return ret; +} + +int skw_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) +{ + int ret; + unsigned long flags; + struct skw_suspend_t suspend; + struct skw_core *skw = wiphy_priv(wiphy); + + skw_dbg("WoW: %s, skw flags: 0x%lx\n", + wow ? "enabled" : "disabled", skw->flags); + + /* + * If we have transmitted packets, but don't receive the txc on pcie + * platform, just return busy, because hw can't access the host + * memory while host is sleep. + */ + if (skw->hw.bus == SKW_BUS_PCIE && + !skw_edma_is_txc_completed(skw)) { + skw_dbg("txc is not completed"); + return -EBUSY; + } + + set_bit(SKW_FLAG_BLOCK_TX, &skw->flags); + + memset(&suspend, 0x0, sizeof(suspend)); + + /* WoW disabled */ + if (!wow) { + suspend.wow_enable = 0; + goto send; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + if (wow->nd_config) + skw_cfg80211_sched_scan_start(wiphy, wow->nd_config->dev, wow->nd_config); +#endif + + suspend.wow_enable = 1; + + if (wow->disconnect) + suspend.wow_flags |= SKW_WOW_DISCONNECT; + + if (wow->magic_pkt) + suspend.wow_flags |= SKW_WOW_MAGIC_PKT; + + if (wow->gtk_rekey_failure) + suspend.wow_flags |= SKW_WOW_GTK_REKEY_FAIL; + + if (wow->eap_identity_req) + suspend.wow_flags |= SKW_WOW_EAP_IDENTITY_REQ; + + if (wow->four_way_handshake) + suspend.wow_flags |= SKW_WOW_FOUR_WAY_HANDSHAKE; + + if (wow->rfkill_release) + suspend.wow_flags |= SKW_WOW_RFKILL_RELEASE; + +send: + flags = BIT(SKW_CMD_FLAG_IGNORE_BLOCK_TX) | + BIT(SKW_CMD_FLAG_NO_ACK) | + BIT(SKW_CMD_FLAG_NO_WAKELOCK); + + if (skw->hw.bus == SKW_BUS_SDIO) + flags |= BIT(SKW_CMD_FLAG_DISABLE_IRQ); + + ret = skw_msg_xmit_timeout(wiphy, 0, SKW_CMD_SUSPEND, &suspend, + sizeof(suspend), NULL, 0, "SKW_CMD_SUSPEND", + msecs_to_jiffies(SKW_CMD_TIMEOUT), flags); + if (ret) { + clear_bit(SKW_FLAG_BLOCK_TX, &skw->flags); + + skw_err("ret: %d, fw flags: 0x%lx\n", ret, skw->flags); + } + + return ret; +} + +static int skw_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + skw_dbg("bus: %s, hw flags: 0x%lx\n", skw_bus_name(skw->hw.bus), skw->hw.flags); + + if (test_bit(SKW_HW_FLAG_CFG80211_PM, &skw->hw.flags)) + return skw_suspend(wiphy, wow); + + if (!wow) { + skw->hw.wow.flags = 0; + skw->hw.wow.enabled = false; + + return 0; + } + + if (wow->disconnect) + skw->hw.wow.flags |= SKW_WOW_DISCONNECT; + + if (wow->magic_pkt) + skw->hw.wow.flags |= SKW_WOW_MAGIC_PKT; + + if (wow->gtk_rekey_failure) + skw->hw.wow.flags |= SKW_WOW_GTK_REKEY_FAIL; + + if (wow->eap_identity_req) + skw->hw.wow.flags |= SKW_WOW_EAP_IDENTITY_REQ; + + if (wow->four_way_handshake) + skw->hw.wow.flags |= SKW_WOW_FOUR_WAY_HANDSHAKE; + + if (wow->rfkill_release) + skw->hw.wow.flags |= SKW_WOW_RFKILL_RELEASE; + + skw->hw.wow.enabled = true; + + return 0; +} + +int skw_resume(struct wiphy *wiphy) +{ + int ret = 0; + struct skw_core *skw = wiphy_priv(wiphy); + + skw_dbg("skw flags: 0x%lx\n", skw->flags); + + clear_bit(SKW_FLAG_BLOCK_TX, &skw->flags); + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_RESUME, NULL, 0, NULL, 0); + if (ret) + skw_warn("ret: %d\n", ret); + + return 0; +} + +static int skw_cfg80211_resume(struct wiphy *wiphy) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + skw_dbg("bus: %s, hw flags: 0x%lx\n", skw_bus_name(skw->hw.bus), skw->hw.flags); + + if (test_bit(SKW_HW_FLAG_CFG80211_PM, &skw->hw.flags)) + return skw_resume(wiphy); + + return 0; +} + +static void skw_set_wakeup(struct wiphy *wiphy, bool enabled) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (enabled) + skw_wow_enable(wiphy); + else + skw_wow_disable(wiphy); +#endif + + device_set_wakeup_enable(wiphy_dev(wiphy), enabled); +} + +static int skw_start_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + skw_dbg("traced\n"); + + return 0; +} + +static void skw_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + skw_dbg("traced\n"); +} + +static int skw_probe_client(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie) +{ + skw_dbg("traced\n"); + + return 0; +} + +static int skw_change_bss(struct wiphy *wiphy, struct net_device *ndev, + struct bss_parameters *params) +{ + struct skw_iface *iface = netdev_priv(ndev); + + skw_dbg("%s ap_isolate:%d\n", netdev_name(ndev), params->ap_isolate); + + if (params->ap_isolate >= 0) + iface->sap.ap_isolate = params->ap_isolate; + + return 0; +} + +static int skw_set_monitor_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + return skw_cmd_monitor(wiphy, chandef, SKW_MONITOR_COMMON); +} + +static int skw_dump_survey(struct wiphy *wiphy, struct net_device *ndev, + int idx, struct survey_info *info) +{ + struct skw_iface *iface = netdev_priv(ndev); + struct skw_survey_info *sinfo = NULL; + int freq; + + skw_detail("%s, idx: %d\n", netdev_name(ndev), idx); + + sinfo = list_first_entry_or_null(&iface->survey_list, + struct skw_survey_info, list); + if (!sinfo) { + skw_dbg("last idx: %d\n", idx); + return -EINVAL; + } + + list_del(&sinfo->list); + + freq = ieee80211_channel_to_frequency(sinfo->data.chan, + to_nl80211_band(sinfo->data.band)); + info->noise = sinfo->data.noise; + info->channel = ieee80211_get_channel(wiphy, freq); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + info->time = sinfo->data.time; + info->time_busy = sinfo->data.time_busy; + info->time_ext_busy = sinfo->data.time_ext_busy; + info->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_EXT_BUSY | + SURVEY_INFO_NOISE_DBM; +#else + info->channel_time = sinfo->data.time; + info->channel_time_busy = sinfo->data.time_busy; + info->channel_time_ext_busy = sinfo->data.time_ext_busy; + info->filled = SURVEY_INFO_CHANNEL_TIME | + SURVEY_INFO_CHANNEL_TIME_BUSY | + SURVEY_INFO_CHANNEL_TIME_EXT_BUSY | + SURVEY_INFO_NOISE_DBM; +#endif + + SKW_KFREE(sinfo); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) +static int skw_external_auth(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_external_auth_params *params) +{ + struct skw_iface *iface = netdev_priv(ndev); + + skw_dbg("%s bssid: %pM, action: %u, status: %u\n", + netdev_name(ndev), params->bssid, + params->action, params->status); + + if (iface->wdev.iftype == NL80211_IFTYPE_AP || + iface->wdev.iftype == NL80211_IFTYPE_P2P_GO) { + return 0; + } + + /* Non-AP STA */ + if (!iface->sta.conn) { + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + return -EINVAL; + } + + if (params->status != WLAN_STATUS_SUCCESS) { + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + skw_unjoin(wiphy, ndev, params->bssid, SKW_LEAVE, false); + // release peer and report connect result + + cfg80211_connect_result(iface->ndev, params->bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + return 0; + } + + skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHED); + + return skw_connect_assoc(wiphy, ndev, iface->sta.conn); +} +#endif + +static int skw_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie) +{ +#if 0 + struct skw_iface *iface = NULL; + struct cfg80211_assoc_request req; + u8 *ie = NULL; + int ret = 0; + + skw_dbg("md:%u\n", ftie->md); + + if (ftie->ie && ftie->ie_len) { + iface->sta.ft_ie = SKW_ZALLOC(ftie->ie_len, GFP_KERNEL); + if (iface->sta.ft_ie) + memcpy(iface->sta.ft_ie, ftie->ie, ftie->ie_len); + iface->sta.ft_ie_len = ftie->ie_len; + skw_dbg("ft ie len:%u\n", iface->sta.ft_ie_len); + } + + skw_dbg("state:%u\n", iface->sta.core.sm.state); + if (iface->sta.core.sm.state != SKW_STATE_AUTHING) { + skw_dbg("received update ft cmd during EAPOL process\n"); + return 0; + } + + // req.bss = iface->sta.associating_bss; + req.ie_len = iface->sta.assoc_ie_len + ftie->ie_len; + ie = SKW_ZALLOC(req.ie_len, GFP_KERNEL); + if (!ie) { + skw_err("Mem is not enough\n"); + return -ENOMEM; + } + memcpy(ie, ftie->ie, ftie->ie_len); + memcpy(ie + ftie->ie_len, iface->sta.assoc_ie, + iface->sta.assoc_ie_len); + + req.ie = ie; + req.prev_bssid = iface->sta.core.bssid; + req.use_mfp = iface->sta.use_mfp; + req.flags = iface->sta.flags; + req.ht_capa = iface->sta.ht_capa; + req.ht_capa_mask = iface->sta.ht_capa_mask; + req.vht_capa = iface->sta.vht_capa; + req.vht_capa_mask = iface->sta.vht_capa_mask; + + ret = skw_assoc(iface->wdev.wiphy, iface->ndev, &req); + + SKW_KFREE(ie); + return ret; +#endif + return 0; +} + +static int skw_start_radar_detection(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_chan_def *chandef, u32 cac_time_ms) +{ + int ret; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("dev: %s, channel: %d, cac time: %dms, dfs_region: %d, fw enabled: %d\n", + netdev_name(dev), chandef->chan->hw_value, cac_time_ms, + skw->dfs.region, skw->dfs.fw_enabled); + + if (!skw->dfs.fw_enabled) + return -EINVAL; + + ret = skw_dfs_chan_init(wiphy, dev, chandef, cac_time_ms); + if (ret) + return ret; + + ret = skw_dfs_start_cac(wiphy, dev); + if (!ret) { + set_bit(SKW_DFS_FLAG_CAC_MODE, &iface->sap.dfs.flags); + queue_delayed_work(skw->event_wq, &iface->sap.dfs.cac_work, + msecs_to_jiffies(cac_time_ms)); + } + + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +static int skw_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *csa) +{ + int ret; + char *buff; + const u8 *ie; + int ie_len, offset = 0; + + skw_dbg("dev: %s, chan: %d, width: %d\n", + netdev_name(dev), csa->chandef.chan->hw_value, + csa->chandef.width); + + buff = SKW_ZALLOC(csa->beacon_csa.tail_len, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + ie = cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, + csa->beacon_csa.tail, + csa->beacon_csa.tail_len); + if (ie) { + ie_len = ie[1] + 2; + + memcpy(buff, ie, ie_len); + offset = ie_len; + } + + ie = cfg80211_find_ie(WLAN_EID_EXT_CHANSWITCH_ANN, + csa->beacon_csa.tail, + csa->beacon_csa.tail_len); + if (ie) { + ie_len = ie[1] + 2; + + memcpy(buff + offset, ie, ie_len); + offset += ie_len; + } + + ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, + csa->beacon_csa.tail, + csa->beacon_csa.tail_len); + if (ie) { + ie_len = ie[1] + 2; + + memcpy(buff + offset, ie, ie_len); + offset += ie_len; + } + + ie = cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH_WRAPPER, + csa->beacon_csa.tail, + csa->beacon_csa.tail_len); + if (ie) { + ie_len = ie[1] + 2; + + memcpy(buff + offset, ie, ie_len); + offset += ie_len; + } + + ret = skw_send_msg(wiphy, dev, SKW_CMD_REQ_CHAN_SWITCH, + buff, offset, NULL, 0); + + SKW_KFREE(buff); + + return ret; +} +#endif + +#ifdef __SKW_ANDROID__ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 74) +static int skw_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, + const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params *params)) +{ + return skw_get_key(wiphy, dev, link_id, key_idx, pairwise, + mac_addr, cookie, callback); +} + +static int skw_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, + const u8 *addr, struct key_params *params) +{ + return skw_add_key(wiphy, dev, link_id, key_idx, pairwise, addr, params); +} + +static int skw_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, const u8 *addr) +{ + return skw_del_key(wiphy, dev, link_id, key_idx, pairwise, addr); +} + +static int skw_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool unicast, bool multicast) +{ + return skw_set_default_key(wiphy, dev, link_id, key_idx, unicast, multicast); +} + +static int skw_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, int link_id, u8 key_idx) +{ + return skw_set_default_mgmt_key(wiphy, dev, link_id, key_idx); +} + +static int skw_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + bool tx = !req->local_state_change; + const u8 *bssid = req->ap_addr; + + return skw_disassoc(wiphy, dev, bssid, req->reason_code, tx); +} + +#else +static int skw_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params *params)) +{ + return skw_get_key(wiphy, dev, 0, key_idx, pairwise, + mac_addr, cookie, callback); +} + +static int skw_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, + const u8 *addr, struct key_params *params) +{ + return skw_add_key(wiphy, dev, 0, key_idx, pairwise, addr, params); +} + +static int skw_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *addr) +{ + return skw_del_key(wiphy, dev, 0, key_idx, pairwise, addr); +} + +static int skw_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool unicast, bool multicast) +{ + return skw_set_default_key(wiphy, dev, 0, key_idx, unicast, multicast); +} + +static int skw_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, u8 key_idx) +{ + return skw_set_default_mgmt_key(wiphy, dev, 0, key_idx); +} + +static int skw_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + bool tx = !req->local_state_change; + const u8 *bssid = (const u8 *)req->bss->bssid; + + return skw_disassoc(wiphy, dev, bssid, req->reason_code, tx); +} + +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 41) +static int skw_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, + unsigned int link_id) +{ + return skw_stop_ap(wiphy, dev, link_id); +} +#else +static int skw_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) +{ + return skw_stop_ap(wiphy, dev, 0); +} +#endif + +#else /* Linux */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +static int skw_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, + const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params *params)) +{ + return skw_get_key(wiphy, dev, link_id, key_idx, pairwise, + mac_addr, cookie, callback); +} + +static int skw_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, + const u8 *addr, struct key_params *params) +{ + return skw_add_key(wiphy, dev, link_id, key_idx, pairwise, addr, params); +} + +static int skw_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool pairwise, const u8 *addr) +{ + return skw_del_key(wiphy, dev, link_id, key_idx, pairwise, addr); +} + +static int skw_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_idx, bool unicast, bool multicast) +{ + return skw_set_default_key(wiphy, dev, link_id, key_idx, unicast, multicast); +} + +static int skw_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, int link_id, u8 key_idx) +{ + return skw_set_default_mgmt_key(wiphy, dev, link_id, key_idx); +} + +static int skw_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + bool tx = !req->local_state_change; + const u8 *bssid = req->ap_addr; + + return skw_disassoc(wiphy, dev, bssid, req->reason_code, tx); +} + +static int skw_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, + unsigned int link_id) +{ + return skw_stop_ap(wiphy, dev, link_id); +} +#else +static int skw_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params *params)) +{ + return skw_get_key(wiphy, dev, 0, key_idx, pairwise, + mac_addr, cookie, callback); +} + +static int skw_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, + const u8 *addr, struct key_params *params) +{ + return skw_add_key(wiphy, dev, 0, key_idx, pairwise, addr, params); +} + +static int skw_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *addr) +{ + return skw_del_key(wiphy, dev, 0, key_idx, pairwise, addr); +} + +static int skw_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool unicast, bool multicast) +{ + return skw_set_default_key(wiphy, dev, 0, key_idx, unicast, multicast); +} + +static int skw_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, u8 key_idx) +{ + return skw_set_default_mgmt_key(wiphy, dev, 0, key_idx); +} + +static int skw_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + bool tx = !req->local_state_change; + const u8 *bssid = (const u8 *)req->bss->bssid; + + return skw_disassoc(wiphy, dev, bssid, req->reason_code, tx); +} + +static int skw_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) +{ + return skw_stop_ap(wiphy, dev, 0); +} +#endif + +#endif /* Linux */ + +static struct cfg80211_ops skw_cfg80211_ops = { + .add_virtual_intf = skw_cfg80211_add_virtual_intf, + .del_virtual_intf = skw_cfg80211_del_virtual_intf, + .change_virtual_intf = skw_cfg80211_change_intf, + .scan = skw_scan, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + .abort_scan = skw_abort_scan, +#endif + .get_key = skw_cfg80211_get_key, + .add_key = skw_cfg80211_add_key, + .del_key = skw_cfg80211_del_key, + .set_default_key = skw_cfg80211_set_default_key, + .set_default_mgmt_key = skw_cfg80211_set_default_mgmt_key, + .change_beacon = skw_change_beacon, + .start_ap = skw_cfg80211_start_ap, + .stop_ap = skw_cfg80211_stop_ap, + .add_station = skw_cfg80211_add_station, + .change_station = skw_cfg80211_change_station, + .del_station = skw_cfg80211_del_station, + .get_station = skw_cfg80211_get_station, + .auth = skw_cfg80211_auth, + .assoc = skw_cfg80211_assoc, + .deauth = skw_cfg80211_deauth, + .disassoc = skw_cfg80211_disassoc, + .connect = skw_cfg80211_connect, + .disconnect = skw_cfg80211_disconnect, + .join_ibss = skw_join_ibss, + .leave_ibss = skw_leave_ibss, + .set_wiphy_params = skw_set_wiphy_params, + .remain_on_channel = skw_remain_on_channel, + .cancel_remain_on_channel = skw_cancel_roc, + .mgmt_tx = skw_cfg80211_mgmt_tx, + .sched_scan_start = skw_cfg80211_sched_scan_start, + .sched_scan_stop = skw_cfg80211_sched_scan_stop, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) + .update_mgmt_frame_registrations = skw_mgmt_frame_register, +#else + .mgmt_frame_register = skw_mgmt_frame_register, +#endif + .set_power_mgmt = skw_set_power_mgmt, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + .set_cqm_rssi_config = skw_set_cqm_rssi_config, + .set_cqm_rssi_range_config = skw_set_cqm_rssi_range_config, +#endif + .start_p2p_device = skw_start_p2p_device, + .stop_p2p_device = skw_stop_p2p_device, + .set_mac_acl = skw_set_mac_acl, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + .set_qos_map = skw_set_qos_map, +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + .add_tx_ts = skw_add_tx_ts, + .del_tx_ts = skw_del_tx_ts, +#endif + .tdls_mgmt = skw_cfg80211_tdls_mgmt, + .tdls_oper = skw_cfg80211_tdls_oper, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + .tdls_channel_switch = skw_tdls_chn_switch, + .tdls_cancel_channel_switch = skw_tdls_cancel_chn_switch, +#endif + .suspend = skw_cfg80211_suspend, + .resume = skw_cfg80211_resume, + .set_wakeup = skw_set_wakeup, + .probe_client = skw_probe_client, + .dump_survey = skw_dump_survey, + .set_monitor_channel = skw_set_monitor_channel, + .change_bss = skw_change_bss, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + .external_auth = skw_external_auth, +#endif + .update_ft_ies = skw_update_ft_ies, + .start_radar_detection = skw_start_radar_detection, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + .channel_switch = skw_channel_switch, +#endif +}; + +static void skw_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *req) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + skw_info("regd: %s, initiator = %d, dfs_region: %d\n", + req->alpha2, req->initiator, req->dfs_region); + + skw->dfs.region = req->dfs_region; + + if (!skw_set_wiphy_regd(wiphy, req->alpha2)) + skw_cmd_set_regdom(wiphy, req->alpha2); +} + +struct wiphy *skw_alloc_wiphy(int priv_size) +{ +#ifdef CONFIG_SWT6621S_STA_SME_EXT + skw_cfg80211_ops.connect = NULL; + skw_cfg80211_ops.disconnect = NULL; +#else + skw_cfg80211_ops.auth = NULL; + skw_cfg80211_ops.assoc = NULL; + skw_cfg80211_ops.deauth = NULL; + skw_cfg80211_ops.disassoc = NULL; +#endif + + return wiphy_new(&skw_cfg80211_ops, priv_size); +} + +#ifdef CONFIG_PM +/* cfg80211 wowlan definitions */ +#define SKW_WOWLAN_MAX_PATTERNS 15 +#define SKW_WOWLAN_MIN_PATTERN_LEN 1 +#define SKW_WOWLAN_MAX_PATTERN_LEN 255 +#define SKW_WOWLAN_PKT_FILTER_ID_FIRST 201 + +static const struct wiphy_wowlan_support skw_wowlan_support = { + .flags = WIPHY_WOWLAN_ANY | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_MAGIC_PKT, + .n_patterns = SKW_WOWLAN_MAX_PATTERNS, + .pattern_min_len = SKW_WOWLAN_MIN_PATTERN_LEN, + .pattern_max_len = SKW_WOWLAN_MAX_PATTERN_LEN, + .max_pkt_offset = SKW_WOWLAN_MAX_PATTERN_LEN, +}; +#endif /* CONFIG_PM */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) +struct skw_iftype_ext_cap iftype_ext_cap[NUM_NL80211_IFTYPES] = { + {NL80211_IFTYPE_STATION, {0}, 0}, + {NL80211_IFTYPE_AP, {0}, 0}, + {NL80211_IFTYPE_P2P_GO, {0}, 0}, +#ifndef CONFIG_SWT6621S_LEGACY_P2P + {NL80211_IFTYPE_P2P_DEVICE, {0}, 0}, +#endif +}; + +static struct skw_iftype_ext_cap *skw_get_iftype_ext_cap(u8 iftype) +{ + int i; + struct skw_iftype_ext_cap *capab = NULL; + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + if (iftype_ext_cap[i].iftype == iftype) + capab = &iftype_ext_cap[iftype]; + } + + return capab; +} + +static void skw_setup_wiphy_iftype_ext_cap(struct wiphy *wiphy) +{ + struct skw_core *skw = wiphy_priv(wiphy); + struct wiphy_iftype_ext_capab *capab = NULL; + struct skw_iftype_ext_cap *skw_ext_cap = NULL; + + skw->num_iftype_ext_capab = 0; + + if (wiphy->interface_modes & (BIT(NL80211_IFTYPE_STATION))) { + capab = &skw->iftype_ext_cap[NL80211_IFTYPE_STATION]; + capab->iftype = NL80211_IFTYPE_STATION; + skw_ext_cap = skw_get_iftype_ext_cap(capab->iftype); + capab->extended_capabilities = skw_ext_cap->ext_cap; + capab->extended_capabilities_mask = skw_ext_cap->ext_cap; + capab->extended_capabilities_len = skw_ext_cap->ext_cap_len; + skw->num_iftype_ext_capab++; + } + + if (wiphy->interface_modes & (BIT(NL80211_IFTYPE_AP))) { + capab = &skw->iftype_ext_cap[NL80211_IFTYPE_AP]; + capab->iftype = NL80211_IFTYPE_AP; + skw_ext_cap = skw_get_iftype_ext_cap(capab->iftype); + capab->extended_capabilities = skw_ext_cap->ext_cap; + capab->extended_capabilities_mask = skw_ext_cap->ext_cap; + capab->extended_capabilities_len = skw_ext_cap->ext_cap_len; + skw->num_iftype_ext_capab++; + } + + skw->num_iftype_ext_capab = 0; //Remove it after set the actual info + wiphy->num_iftype_ext_capab = skw->num_iftype_ext_capab; + wiphy->iftype_ext_capab = skw->iftype_ext_cap; +} +#endif + +static void skw_sync_band_capa(struct ieee80211_supported_band *band, + struct skw_chip_info *chip) +{ + u32 flags; + u16 bit_rate; + int i, mcs_map; + int tx_chain = 0, rx_chain = 0; + + band->ht_cap.cap = chip->ht_capa; + band->ht_cap.ht_supported = true; + band->ht_cap.ampdu_factor = chip->ht_ampdu_param & 0x3; + band->ht_cap.ampdu_density = (chip->ht_ampdu_param >> 2) & 0x7; + + for (i = 0; i < 4; i++) { + mcs_map = (chip->ht_rx_mcs_maps >> (i * 8)) & 0xff; + if (mcs_map) { + rx_chain++; + band->ht_cap.mcs.rx_mask[i] = mcs_map; + } + + mcs_map = (chip->ht_tx_mcs_maps >> (i * 8)) & 0xff; + if (mcs_map) + tx_chain++; + } + + if (chip->fw_bw_capa & SKW_BW_2GHZ_40M) + bit_rate = rx_chain * 150; /* Mbps */ + else + bit_rate = rx_chain * 72; /* Mbps */ + + band->ht_cap.mcs.rx_highest = cpu_to_le16(bit_rate); + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (tx_chain != rx_chain) { + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_RX_DIFF; + band->ht_cap.mcs.tx_params |= ((tx_chain - 1) << 2); + } + + band->vht_cap.cap = chip->vht_capa; + band->vht_cap.vht_supported = true; + band->vht_cap.vht_mcs.tx_mcs_map = chip->vht_tx_mcs_maps; + band->vht_cap.vht_mcs.rx_mcs_map = chip->vht_rx_mcs_maps; + + if (!chip->fw_bw_capa) + return; + + /* set channel flags */ + for (flags = 0, i = 0; i < 32; i++) { + if (!(chip->fw_bw_capa & BIT(i))) { + switch (BIT(i)) { + case SKW_BW_CAP_2G_20M: + case SKW_BW_CAP_5G_20M: + flags |= SKW_IEEE80211_CHAN_NO_20MHZ; + break; + + case SKW_BW_CAP_2G_40M: + case SKW_BW_CAP_5G_40M: + flags |= IEEE80211_CHAN_NO_HT40; + break; + + case SKW_BW_CAP_5G_80M: + flags |= IEEE80211_CHAN_NO_80MHZ; + break; + + case SKW_BW_CAP_5G_160M: + flags |= IEEE80211_CHAN_NO_160MHZ; + break; + + default: + break; + } + } + } + + skw_dbg("BW capa: 0x%x, flags: 0x%x\n", chip->fw_bw_capa, flags); + +#ifdef SKW_SYNC_CHANNEL_FLAGS + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = flags; +#endif +} + +int skw_setup_wiphy(struct wiphy *wiphy, struct skw_chip_info *chip) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + wiphy->mgmt_stypes = skw_mgmt_stypes; +#if 0 + wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; +#endif + + wiphy->flags = WIPHY_FLAG_NETNS_OK | + WIPHY_FLAG_4ADDR_AP | + WIPHY_FLAG_4ADDR_STATION | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_REPORTS_OBSS; + +#ifdef CONFIG_SWT6621S_TDLS + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; +#endif + +#ifdef CONFIG_SWT6621S_OFFCHAN_TX + wiphy->flags |= WIPHY_FLAG_OFFCHAN_TX; +#else + wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + wiphy->max_num_csa_counters = 2; +#endif + + /* STA SME EXTERNAL */ + if (!test_bit(SKW_FLAG_STA_SME_EXTERNAL, &skw->flags)) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + + /* AP SME INTERNAL */ + if (!test_bit(SKW_FLAG_SAP_SME_EXTERNAL, &skw->flags)) { + wiphy->max_acl_mac_addrs = SKW_MAX_ACL_ENTRIES; + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; + wiphy->ap_sme_capa = 1; + } + + wiphy->features = NL80211_FEATURE_SK_TX_STATUS | + NL80211_FEATURE_SAE | + NL80211_FEATURE_HT_IBSS | + NL80211_FEATURE_VIF_TXPOWER | + NL80211_FEATURE_USERSPACE_MPM | + NL80211_FEATURE_FULL_AP_CLIENT_STATE | + NL80211_FEATURE_INACTIVITY_TIMER; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + //wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; + wiphy->features |= NL80211_FEATURE_MAC_ON_CREATE; +#endif + +#ifdef CONFIG_SWT6621S_SCAN_RANDOM_MAC + wiphy->features |= SKW_WIPHY_FEATURE_SCAN_RANDOM_MAC; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + + //TODO:Add an function to initialize iftype_ext_cap + skw_setup_wiphy_iftype_ext_cap(wiphy); +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) + wiphy->support_mbssid = true; +#else + wiphy->bss_priv_size = sizeof(struct skw_bss_priv); + set_bit(SKW_FLAG_MBSSID_PRIV, &skw->flags); +#endif + + wiphy->interface_modes = BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_MONITOR); + +#ifndef CONFIG_SWT6621S_LEGACY_P2P + wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE); +#endif + + BUILD_BUG_ON_MSG(SKW_EXTENDED_CAPA_LEN > sizeof(skw->ext_capa), + "SKW_EXTENDED_CAPA_LEN larger than buffer"); + wiphy->extended_capabilities = skw->ext_capa; + wiphy->extended_capabilities_mask = skw->ext_capa; + wiphy->extended_capabilities_len = SKW_EXTENDED_CAPA_LEN; + +#if defined(CONFIG_PM) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) + wiphy->wowlan = &skw_wowlan_support; +#else + wiphy->wowlan = skw_wowlan_support; +#endif +#endif + + skw_sync_band_capa(&skw_band_2ghz, chip); + wiphy->bands[NL80211_BAND_2GHZ] = &skw_band_2ghz; + + skw_info("2g_only:%d", chip->priv_2g_only); + if (!chip->priv_2g_only) { + skw_sync_band_capa(&skw_band_5ghz, chip); + wiphy->bands[NL80211_BAND_5GHZ] = &skw_band_5ghz; + +#ifdef CONFIG_SWT6621S_6GHZ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) + wiphy->bands[NL80211_BAND_6GHZ] = &skw_band_6ghz; +#endif +#endif + } + + wiphy->cipher_suites = skw_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(skw_cipher_suites); + + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->max_scan_ssids = chip->max_scan_ssids; + wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; /*2304*/ + wiphy->max_remain_on_channel_duration = 500; + wiphy->max_sched_scan_ie_len = IEEE80211_MAX_DATA_LEN; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + wiphy->max_sched_scan_reqs = 1; +#endif + wiphy->max_sched_scan_ssids = 10; + wiphy->max_match_sets = 16; + + /* MCC support */ + wiphy->iface_combinations = skw_iface_combos; + wiphy->n_iface_combinations = ARRAY_SIZE(skw_iface_combos); + + wiphy->addresses = skw->address; + wiphy->n_addresses = ARRAY_SIZE(skw->address); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + wiphy->max_ap_assoc_sta = skw->fw.max_num_sta; +#endif + + wiphy->reg_notifier = skw_regd_notifier; + +#ifdef CONFIG_SWT6621S_REGD_SELF_MANAGED + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; +#else + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; +#endif + set_bit(SKW_FLAG_PRIV_REGD, &skw->flags); + +#endif + + return wiphy_register(wiphy); +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_cfg80211.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_cfg80211.h new file mode 100755 index 0000000..e64585d --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_cfg80211.h @@ -0,0 +1,902 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_CFG80211_H__ +#define __SKW_CFG80211_H__ + +#include <linux/ieee80211.h> +#include "skw_msg.h" +#include "skw_iface.h" + +#define SKW_AUTH_TIMEOUT msecs_to_jiffies(4800) +#define SKW_ASSOC_TIMEOUT msecs_to_jiffies(4800) +#define SKW_STEP_TIMEOUT msecs_to_jiffies(300) +#define SKW_MAX_AUTH_RETRY_NUM 3 +#define SKW_MAX_ASSOC_RETRY_NUM 5 +#define SKW_MAX_REAUTH_REDO_NUM 3 + +#define SKW_MAX_SCAN_SSID 4 +#define SKW_SCAN_TIMEOUT 8000 +#define SKW_CQM_SCAN_TIMEOUT 4 +#define SKW_MAX_STA_AUTH_ASSOC_RETRY 3 +#define SKW_EXTENDED_CAPA_LEN 11 + +/* hostap mac acl mode */ +#define SKW_MAX_ACL_ENTRIES 16 + +#define SKW_WOW_DISCONNECT BIT(0) +#define SKW_WOW_MAGIC_PKT BIT(1) +#define SKW_WOW_GTK_REKEY_FAIL BIT(2) +#define SKW_WOW_EAP_IDENTITY_REQ BIT(3) +#define SKW_WOW_FOUR_WAY_HANDSHAKE BIT(4) +#define SKW_WOW_RFKILL_RELEASE BIT(5) +#define SKW_WOW_BLACKLIST_FILTER BIT(31) +#define SKW_WOW_ANY_PKT 0xff + +enum SKW_IP_VERSION { + SKW_IP_IPV4 = 0, + SKW_IP_IPV6, +}; + +struct skw_bss_priv { + u8 bssid_index; + u8 max_bssid_indicator; + u16 resv; +}; + +struct skw_suspend_t { + u8 wow_enable; + u8 reserved; + /* reference SKW_WOW_*, + * set wow_flags to 0 if wakeup any + */ + u16 wow_flags; +}; + +#define SKW_SUITE(oui, id) (((oui) << 8) | (id)) +#define SKW_CIPHER_SUITE_WEP40 SKW_SUITE(0x000FAC, 1) +#define SKW_CIPHER_SUITE_TKIP SKW_SUITE(0x000FAC, 2) +#define SKW_CIPHER_SUITE_CCMP SKW_SUITE(0x000FAC, 4) +#define SKW_CIPHER_SUITE_WEP104 SKW_SUITE(0x000FAC, 5) +#define SKW_CIPHER_SUITE_AES_CMAC SKW_SUITE(0x000FAC, 6) +#define SKW_CIPHER_SUITE_GCMP SKW_SUITE(0x000FAC, 8) +#define SKW_CIPHER_SUITE_GCMP_256 SKW_SUITE(0x000FAC, 9) +#define SKW_CIPHER_SUITE_CCMP_256 SKW_SUITE(0x000FAC, 10) +#define SKW_CIPHER_SUITE_BIP_GMAC_128 SKW_SUITE(0x000FAC, 11) +#define SKW_CIPHER_SUITE_BIP_GMAC_256 SKW_SUITE(0x000FAC, 12) +#define SKW_CIPHER_SUITE_BIP_CMAC_256 SKW_SUITE(0x000FAC, 13) + +#define SKW_CIPHER_SUITE_SMS4 SKW_SUITE(0x001472, 1) + +enum SKW_CIPHER_TYPE { + SKW_CIPHER_TYPE_INVALID = 0, + SKW_CIPHER_TYPE_WEP40 = 1, + SKW_CIPHER_TYPE_WEP104 = 2, + SKW_CIPHER_TYPE_TKIP = 3, + SKW_CIPHER_TYPE_SMS4 = 4, + SKW_CIPHER_TYPE_CCMP = 8, + SKW_CIPHER_TYPE_CCMP_256 = 9, + SKW_CIPHER_TYPE_GCMP = 10, + SKW_CIPHER_TYPE_GCMP_256 = 11, + SKW_CIPHER_TYPE_AES_CMAC = 12, /* BIP_CMAC_128 */ + SKW_CIPHER_TYPE_BIP_CMAC_256 = 13, + SKW_CIPHER_TYPE_BIP_GMAC_128 = 14, + SKW_CIPHER_TYPE_BIP_GMAC_256 = 15, +}; + +enum SKW_MIB_ID { + SKW_MIB_RTS_THRESHOLD = 1, + SKW_MIB_FRAG_THRESHOLD = 2, + SKW_MIB_COVERAGE_CLASS = 3, + SKW_MIB_RETRY_SHORT = 4, + SKW_MIB_RETRY_LONG = 5, + SKW_MIB_DYN_ACK = 6, + SKW_MIB_TXQ_LIMIT = 7, + SKW_MIB_TXQ_MEMORY_LIMIT = 8, + SKW_MIB_TXQ_QUANTUM = 9, + SKW_MIB_DOT11_OMI = 10, + SKW_MIB_DOT11_MODE_B = 11, + SKW_MIB_DOT11_MODE_G = 12, + SKW_MIB_DOT11_MODE_A = 13, + SKW_MIB_DOT11_MODE_HT = 14, + SKW_MIB_DOT11_MODE_VHT = 15, + SKW_MIB_DOT11_MODE_HE = 16, + SKW_MIB_DOT11_CBW_20M = 17, + SKW_MIB_DOT11_CBW_40M_ABOVE = 18, + SKW_MIB_DOT11_CBW_40M_BELOW = 19, + SKW_MIB_DOT11_CBW_80M = 20, + SKW_MIB_DOT11_CBW_160M = 21, + SKW_MIB_DOT11_CBW_80P80M = 22, + SKW_MIB_SET_BAND_2G = 23, + SKW_MIB_SET_BAND_5G = 24, + + SKW_MIB_SET_11K_TLV_ID = 30, + SKW_MIB_SET_11V_TLV_ID = 31, + SKW_MIB_SET_11R_TLV_ID = 32, + SKW_MIB_SET_ONCE_NOA_ENABLE = 33, + SKW_MIB_SET_NOA_RATIO_TYPE = 34, + SKW_MIB_SET_LINK_LOSS_THOLD = 35, + + SKW_MIB_SET_AGEOUT_THOLD = 39, + SKW_MIB_SET_TXOP_LIMIT = 40, + SKW_MIB_SET_MAX_PPDU_DUR = 41, + SKW_MIB_SET_REPORT_CQM_RSSI_LOW_INT = 42, + SKW_MIB_SET_EDCA_PARAM = 43, + SKW_MIB_SET_CCA_THRE_NOWIFI = 44, + SKW_MIB_SET_CCA_THRE_11B = 45, + SKW_MIB_SET_CCA_THRE_OFDM = 46, + SKW_MIB_SET_FORCE_RTS_RATE = 47, + SKW_MIB_SET_FORCE_RX_RSP_RATE = 48, + SKW_MIB_SET_SCAN_TIME = 49, + SKW_MIB_SET_TCP_DISCONN_WAKEUP_HOST = 50, + SKW_MIB_SET_TX_LIFETIME = 51, + SKW_MIB_SET_TX_RETRY_CNT = 52, + SKW_MIB_SET_TX_RTS_THRD = 53, + SKW_MIB_SET_AP_NEW_CHAN = 54, + SKW_MIB_SET_TX_RETRY_LIMIT_EN = 55, + SKW_MIB_SET_PARTIAL_TWT_SCHED = 56, + SKW_MIB_SET_THM_THRD = 57, + + SKW_MIB_SET_NORMAL_SCAN_WITH_ACS = 59, + SKW_MIB_SET_FORCE_RX_SPECIAL_80211FRAME = 60, + SKW_MIB_SET_FORCE_RX_UPDATE_NAV = 61, + SKW_MIB_SET_SCAN_SEND_PROBE_REQ_CNT = 62, + + SKW_MIB_SET_APGO_TIMMAP = 70, + SKW_MIB_SET_DBDC_DISABLE = 71, + + SKW_MIB_SET_RATE_CTRL_MIN_RATE = 80, + SKW_MIB_SET_RATE_CTRL_RATE_CHANGE_PARAM = 81, + SKW_MIB_SET_RATE_CTRL_SPECIAL_FRM_RATE = 82, + + SKW_MIB_SET_HDK_TEST = 131, + + SKW_MIB_SET_ASSIGN_ADDRESS_VAL = 150, + SKW_MIB_SET_WAKEUP_HOST_ENABLE = 151, + SKW_MIB_SET_WDS_ENABLE = 152, + SKW_MIB_SET_SUSPEND_MODE = 153, + + SKW_MIB_W_INFO = 200, + SKW_MIB_MAC_NSS_INFO = 201, + SKW_MIB_GET_HW_TX_INFO_E = 202, + SKW_MIB_GET_INST_INFO_E = 203, + SKW_MIB_GET_PEER_INFO_E = 204, + SKW_MIB_GET_HW_RX_INFO_E = 205, + SKW_MIB_GET_INST_TSF_E = 206, + + SKW_MIB_GET_ASSIGN_ADDR_VAL_E = 250, + + SKW_MIB_LAST +}; + +enum SKW_MONITOR_MODE { + SKW_MONITOR_CLOSE, + SKW_MONITOR_COMMON, + SKW_MONITOR_MAC_CAP, + SKW_MONITOR_PHY_CAP, + SKW_MONITOR_MAX, +}; + +#define SKW_BW_2GHZ_20M BIT(0) +#define SKW_BW_2GHZ_40M BIT(1) +#define SKW_BW_5GHZ_20M BIT(2) +#define SKW_BW_5GHZ_40M BIT(3) +#define SKW_BW_5GHZ_80M BIT(4) +#define SKW_BW_5GHZ_160M BIT(5) +#define SKW_BW_5GHZ_8080M BIT(6) + +enum SKW_CMD_DISCONNECT_TYPE_E { + SKW_DISCONNECT_ONLY = 0, + SKW_DISCONNECT_SEND_DISASSOC = 1, + SKW_DISCONNECT_SEND_DEAUTH = 2, +}; + +struct skw_once_noa_enable_mib { + u8 en; + u8 go_pre_time; + u8 go_abs_time; +} __packed; + +struct skw_noa_ratio_mib { + u8 valid; + u8 ratio_lv; +} __packed; + +#define SKW_SCAN_FLAG_RND_MAC BIT(0) +#define SKW_SCAN_FLAG_ACS BIT(1) +#define SKW_SCAN_FLAG_PASSIVE BIT(7) + +struct skw_scan_chan_info { + u8 chan_num; + u8 band; + u8 scan_flags; +} __packed; + +struct skw_scan_param { + u16 flags; /* reference SKW_SCAN_FLAG_ */ + u8 rand_mac[6]; + u32 nr_chan; + u32 chan_offset; + u32 n_ssid; + u32 ssid_offset; + u32 ie_len; + u32 ie_offset; + u8 ie[]; +} __packed; + +struct skw_sched_match_sets { + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u16 ssid_len; + u8 bssid[ETH_ALEN]; + s32 rssi_thold; +} __packed; + +struct skw_sched_scan_param { + u32 req_id; + u32 flags; + s32 min_rssi_thold; + u32 delay; + u8 mac_addr[ETH_ALEN]; + u8 mac_addr_mask[ETH_ALEN]; + u8 relative_rssi_set; + s8 relative_rssi; + u8 scan_width; + + u8 n_ssids; + u32 n_ssids_len; + u32 n_ssid_offset; + + u32 ie_len; + u32 ie_offset; + + u8 n_channels; + u32 channels_len; + u32 channels_offset; + + u8 n_match_sets; + u32 match_sets_len; + u32 match_sets_offset; + + u8 n_scan_plans; + u32 scan_plans_len; + u32 scan_plans_offset; + u8 data[0]; +} __packed; + +struct skw_center_chn { + u8 bw; + u8 band; + u16 center_chn1; + u16 center_chn2; +}; + +struct skw_he_cap_elem { + u8 mac_cap_info[6]; + u8 phy_cap_info[11]; + u16 rx_mcs_map; + u16 tx_mcs_map; + u32 ppe; +} __packed; + +struct skw_he_oper_param { + u16 default_pe_dur:3; + u16 twt_req:1; + u16 txop_dur_rts_thr:10; + u16 vht_opt_info_pre:1; + u16 co_hosted_bss:1; + u8 er_su_disable:1; + u8 opt_info_6g_pre:1; + u8 reserved:6; +} __packed; + +struct skw_he_oper_elem { + struct skw_he_oper_param he_param; + u8 bss_color:6; + u8 partial_bss_color:1; + u8 bss_color_disabled:1; + u8 basic_mcs_nss[2]; + //u8 vht_opt_info[3]; + //u8 max_cohosted_bssid_ind[1]; + //u8 opt_info_6g[5]; +} __packed; + +struct skw_join_param { + u8 chan_num; + u8 center_chn1; + u8 center_chn2; + u8 bandwidth; + u8 band; + u16 beacon_interval; + u16 capability; + u8 bssid_index; + u8 max_bssid_indicator; + u8 bssid[6]; + u16 roaming:1; + u16 reserved:15; + u16 bss_ie_offset; + u32 bss_ie_len; + u8 bss_ie[]; +} __packed; + +struct skw_join_resp { + u8 peer_idx; + u8 lmac_id; + u8 inst; + u8 multicast_idx; +} __packed; + +struct skw_auth_param { + u16 auth_algorithm; + u16 key_data_offset; + u16 key_data_len; + u16 auth_data_offset; + u16 auth_data_len; + u16 auth_ie_offset; + u16 auth_ie_len; + u8 data[]; +} __packed; + +struct skw_assoc_req_param { + struct ieee80211_ht_cap ht_capa; + struct ieee80211_vht_cap vht_capa; + u8 bssid[6]; + u8 pre_bssid[6]; + u16 req_ie_offset; + u16 req_ie_len; + u8 req_ie[]; +} __packed; + +struct skw_disconnect_param { + u8 type; + u8 local_state_change; + u16 reason_code; + u16 ie_offset; + u16 ie_len; + u8 ie[]; +} __packed; + +struct skw_ibss_params { + /* + * 0: join ibss + * 1: create ibss + */ + u8 type; + u8 chan; + u8 bw; + u8 center_chan1; + u8 center_chan2; + u8 band; + + u8 ssid_len; + u8 ssid[32]; + + u8 bssid[ETH_ALEN]; + u16 atim_win; + u16 beacon_int; +} __packed; + +enum SKW_KEY_TYPE { + SKW_KEY_TYPE_PTK = 0, + SKW_KEY_TYPE_GTK = 1, + SKW_KEY_TYPE_IGTK = 2, + SKW_KEY_TYPE_BIGTK = 3, +}; + +struct skw_startap_param { + int beacon_int; + u8 dtim_period; + u8 flags; /* reference SKW_AP_FLAGS_* */ + u8 chan; + u8 chan_width; + u8 center_chn1; + u8 center_chn2; + u8 band; + u8 ssid_len; + u8 ssid[32]; + + u16 beacon_head_offset; + u16 beacon_head_len; + u16 beacon_tail_offset; + u16 beacon_tail_len; + u16 beacon_ies_offset; + u16 beacon_ies_len; + + u16 probe_rsp_ies_offset; + u16 probe_rsp_ies_len; + u16 assoc_rsp_ies_offset; + u16 assoc_rsp_ies_len; + u8 ies[0]; +} __packed; + +struct skw_startap_resp { + u8 lmac_id; + u8 inst_id; + u8 multicast_idx; +}; + +//TBD: put skw_beacon_param into skw_startp_param +struct skw_beacon_params { + u16 beacon_head_offset; + u16 beacon_head_len; + u16 beacon_tail_offset; + u16 beacon_tail_len; + u16 beacon_ies_offset; + u16 beacon_ies_len; + + u16 probe_rsp_ies_offset; + u16 probe_rsp_ies_len; + u16 assoc_rsp_ies_offset; + u16 assoc_rsp_ies_len; + u16 probe_rsp_offset; + u16 probe_rsp_len; + u8 ies[0]; +} __packed; + +struct skw_del_sta_param { + u8 mac[6]; + u16 reason_code; + u8 tx_frame; +} __packed; + +enum skw_rate_info_bw { + SKW_RATE_INFO_BW_20, + SKW_RATE_INFO_BW_40, + SKW_RATE_INFO_BW_80, + SKW_RATE_INFO_BW_HE_RU = 15, +}; + +struct skw_rx_rate_desc { + u8 ppdu_mode; + u8 data_rate; + u8 nss; + u8 bw; + u8 gi_type; + u8 ru; + u8 dcm; + u8 msdu_filter; + u8 retry_frame; + u8 data_snr; + u16 data_rssi; + u16 resv1; +} __packed; + +struct skw_get_sta_resp { + struct skw_rate tx_rate; + s8 signal; + u8 noise; + u8 tx_psr; + u32 tx_failed; + u16 filter_cnt[35]; + u16 filter_drop_offload_cnt[35]; + struct skw_rx_rate_desc rx_rate; + u8 tx_percent; + u8 rx_percent; +} __packed; + +struct skw_roc_param { + u8 enable; + u8 channel_num; + u8 band; + u8 channel_type; + u32 duration; + u64 cookie; +} __packed; + +struct skw_mgmt_tx_param { + u32 wait; + u64 cookie; + u8 channel; + u8 band; + u8 dont_wait_for_ack; + u16 mgmt_frame_len; + struct ieee80211_mgmt mgmt[0]; +} __packed; + +struct skw_mgmt_register_param { + u16 frame_type; + u8 reg; + u8 resv[5]; + u64 timestamp; +} __packed; + +struct skw_station_params { + u8 mac[ETH_ALEN]; + u16 resv; + + u64 timestamp; +}; + +#define SKW_CQM_DEFAUT_RSSI_THOLD (-70) +#define SKW_CQM_DEFAUT_RSSI_HYST (40) + +struct skw_set_cqm_rssi_param { + s32 rssi_thold; + u8 rssi_hyst; +} __packed; + +enum SKW_SCAN_TYPE { + SKW_SCAN_IDLE, + SKW_SCAN_NORMAL, + SKW_SCAN_SCHED, + SKW_SCAN_BG, + SKW_SCAN_AUTO, + SKW_SCAN_ROAM, +#ifdef RRM_SCAN_SUPPORT + SKW_SCAN_RRM, +#endif +}; + +enum SKW_CQM_STATUS { + CQM_STATUS_RSSI_LOW = 1, + CQM_STATUS_RSSI_HIGH = 2, + CQM_STATUS_BEACON_LOSS = 3, + CQM_STATUS_TDLS_LOSS = 4, +}; + +enum SKW_MPDU_DESC_GI { + DESC_GI_04 = 0, + DESC_GI_08 = 1, + DESC_GI_16 = 2, + DESC_GI_32 = 3, +}; + +/* define same as cp get station rate bw */ +#define SKW_DESC_BW_USED_RU 15 + +/* define same as cp get station tx gi*/ +enum SKW_HE_GI { + SKW_HE_GI_3_2 = 0, + SKW_HE_GI_1_6 = 1, + SKW_HE_GI_0_8 = 2, +}; + +/* define same as cp get station tx gi*/ +enum SKW_HTVHT_GI { + SKW_HTVHT_GI_0_8 = 0, + SKW_HTVHT_GI_0_4 = 1, +}; + +struct skw_cqm_info { + u8 cqm_status; + s16 cqm_rssi; + u8 bssid[ETH_ALEN]; + u8 chan; + u8 band; +} __packed; + +struct skw_del_sta { + u8 reason_code; + u8 mac[ETH_ALEN]; +} __packed; + +struct skw_mic_failure { + u8 is_mcbc; + u8 key_id; + u8 lmac_id; + u8 mac[ETH_ALEN]; +} __packed; + +struct skw_tdls_oper { + u16 oper; /* reference enum nl80211_tdls_operation */ + u8 peer_addr[ETH_ALEN]; +}; + +struct skw_ts_info { + u8 tsid; + u8 up; + u8 peer[ETH_ALEN]; + __le16 admitted_time; +} __packed; + +struct skw_tdls_chan_switch { + u8 addr[6]; + u8 chn_switch_enable; /* 0: disable, 1: enable */ + u8 oper_class; + u8 chn; + u8 band; /* enum nl80211_band */ + u8 chan_width; /* enum skw_chan_width */ +}; + +struct skw_setip_param { + u8 ip_type; + union { + __be32 ipv4; + u8 ipv6[16]; + }; +} __packed; + +#define SKW_CONN_FLAG_ASSOCED BIT(0) +#define SKW_CONN_FLAG_KEY_VALID BIT(1) +#define SKW_CONN_FLAG_USE_MFP BIT(2) +#define SKW_CONN_FLAG_AUTH_AUTO BIT(3) +#define SKW_CONN_FLAG_SAE_AUTH BIT(4) + +struct skw_connect_param { + struct mutex lock; + + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u16 ssid_len; + u8 bssid[ETH_ALEN]; + + u8 key[32]; + u8 key_len, key_idx; + + u8 prev_bssid[ETH_ALEN]; + + enum SKW_STATES state; + enum nl80211_auth_type auth_type; + + u32 flags; /* reference SKW_CONN_FLAG_ */ + + u8 *assoc_ie; + size_t assoc_ie_len; + + struct ieee80211_ht_cap ht_capa, ht_capa_mask; + struct ieee80211_vht_cap vht_capa, vht_capa_mask; + + struct ieee80211_channel *channel; + + struct cfg80211_crypto_settings crypto; +}; + +enum SKW_BAND { + SKW_BAND_2GHZ, + SKW_BAND_5GHZ, + SKW_BAND_6GHZ, + SKW_BAND_60GHZ, + + SKW_BAND_INVALD, +}; + +static inline enum SKW_BAND to_skw_band(enum nl80211_band band) +{ + enum SKW_BAND new_band; + + switch (band) { + case NL80211_BAND_2GHZ: + new_band = SKW_BAND_2GHZ; + break; + + case NL80211_BAND_5GHZ: + new_band = SKW_BAND_5GHZ; + break; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) + case NL80211_BAND_6GHZ: + new_band = SKW_BAND_6GHZ; + break; +#endif + + default: + new_band = SKW_BAND_INVALD; + break; + } + + return new_band; +} + +static inline enum nl80211_band to_nl80211_band(enum SKW_BAND skw_band) +{ + enum nl80211_band band; + + switch (skw_band) { + case SKW_BAND_2GHZ: + band = NL80211_BAND_2GHZ; + break; + + case SKW_BAND_5GHZ: + band = NL80211_BAND_5GHZ; + break; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) + case SKW_BAND_6GHZ: + band = NL80211_BAND_6GHZ; + break; +#endif + + default: + band = NUM_NL80211_BANDS; + } + + return band; +} + +static inline struct skw_bss_priv *skw_bss_priv(struct cfg80211_bss *bss) +{ + return (struct skw_bss_priv *)bss->priv; +} + +static inline void skw_join_resp_handler(struct skw_core *skw, + struct skw_iface *iface, + struct skw_join_resp *resp) +{ + SKW_BUG_ON(skw_lmac_bind_iface(iface->skw, iface, resp->lmac_id)); + iface->default_multicast = resp->multicast_idx; +} + +static inline void skw_startap_resp_handler(struct skw_core *skw, + struct skw_iface *iface, + struct skw_startap_resp *resp) +{ + SKW_BUG_ON(skw_lmac_bind_iface(iface->skw, iface, resp->lmac_id)); + iface->default_multicast = resp->multicast_idx; +} + +static inline u8 skw_desc_nss_to_nss_num(u8 cp_nss_idx) +{ + /* convert cp nss index to nss number */ + return cp_nss_idx + 1; +} + +static inline enum SKW_HE_GI skw_desc_he_gi_to_skw_gi(enum SKW_MPDU_DESC_GI desc_gi) +{ + enum SKW_HE_GI gi = 0; + + /* convert cp desc gi to SKW_HE_GI */ + switch (desc_gi) { + case DESC_GI_04: + case DESC_GI_08: + gi = SKW_HE_GI_0_8; + break; + case DESC_GI_16: + gi = SKW_HE_GI_1_6; + break; + case DESC_GI_32: + gi = SKW_HE_GI_3_2; + break; + default: + SKW_BUG_ON(1); + } + + return gi; +} + +static inline enum SKW_HTVHT_GI skw_desc_htvht_gi_to_skw_gi(enum SKW_MPDU_DESC_GI desc_gi) +{ + enum SKW_HTVHT_GI gi = 0; + + /* convert cp desc gi to SKW_HTVHT_GI */ + switch (desc_gi) { + case DESC_GI_04: + gi = SKW_HTVHT_GI_0_4; + break; + case DESC_GI_08: + gi = SKW_HTVHT_GI_0_8; + break; + default: + SKW_BUG_ON(1); + } + + return gi; +} + +static inline u8 skw_desc_gi_to_skw_gi(enum SKW_MPDU_DESC_GI desc_gi, + enum SKW_RX_MPDU_DESC_PPDUMODE ppdu_mode) +{ + u8 gi = 0; + + switch (ppdu_mode) { + case SKW_PPDUMODE_HT_MIXED: + case SKW_PPDUMODE_VHT_SU: + case SKW_PPDUMODE_VHT_MU: + gi = skw_desc_htvht_gi_to_skw_gi(gi); + break; + + case SKW_PPDUMODE_HE_SU: + case SKW_PPDUMODE_HE_TB: + case SKW_PPDUMODE_HE_ER_SU: + case SKW_PPDUMODE_HE_MU: + gi = skw_desc_he_gi_to_skw_gi(gi); + break; + + default: + gi = desc_gi; + break; + }; + + return gi; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +static inline enum nl80211_he_gi skw_gi_to_nl80211_info_gi(enum SKW_HE_GI skw_gi) +{ + enum nl80211_he_gi gi = 0; + + /* cp tx get station gi to nl80211_he_gi */ + switch (skw_gi) { + case SKW_HE_GI_3_2: + gi = NL80211_RATE_INFO_HE_GI_3_2; + break; + case SKW_HE_GI_1_6: + gi = NL80211_RATE_INFO_HE_GI_1_6; + break; + case SKW_HE_GI_0_8: + gi = NL80211_RATE_INFO_HE_GI_0_8; + break; + default: + SKW_BUG_ON(1); + } + + return gi; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0) + #define __STA_INFO_FIELD(field) SKW_BIT_ULL(NL80211_STA_INFO_##field) +#else + #define __STA_INFO_FIELD(field) STATION_INFO_##field +#endif + +#define SKW_COMPAT_SIGNAL __STA_INFO_FIELD(SIGNAL) +#define SKW_COMPAT_TX_PACKETS __STA_INFO_FIELD(TX_PACKETS) +#define SKW_COMPAT_TX_BYTES __STA_INFO_FIELD(TX_BYTES) +#define SKW_COMPAT_TX_BITRATE __STA_INFO_FIELD(TX_BITRATE) +#define SKW_COMPAT_TX_FAILED __STA_INFO_FIELD(TX_FAILED) +#define SKW_COMPAT_RX_PACKETS __STA_INFO_FIELD(RX_PACKETS) +#define SKW_COMPAT_RX_BYTES __STA_INFO_FIELD(RX_BYTES) +#define SKW_COMPAT_RX_BITRATE __STA_INFO_FIELD(RX_BITRATE) + +int to_skw_bw(enum nl80211_chan_width bw); +struct wiphy *skw_alloc_wiphy(int priv_size); +int skw_setup_wiphy(struct wiphy *wiphy, struct skw_chip_info *chip); + +int skw_mgmt_tx(struct wiphy *wiphy, struct skw_iface *iface, + struct ieee80211_channel *chan, u32 wait, u64 *cookie, + bool dont_wait_ack, const void *frame, int frame_len, + int total_frame_len, const struct ieee80211_mgmt *mgmt, bool switchover); + +int skw_cmd_del_sta(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, u8 type, u16 reason, bool tx_frame); + +int skw_del_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, u8 subtype, u16 reason); +int skw_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params); +int skw_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params); +void skw_scan_done(struct skw_core *skw, struct skw_iface *iface, bool abort); + +void skw_set_state(struct skw_sm *sm, enum SKW_STATES state); +int skw_roam_connect(struct skw_iface *iface, const u8 *bssid, u8 chn, + enum nl80211_band band); + +int skw_sta_leave(struct wiphy *wiphy, struct net_device *dev, + const u8 *bssid, u16 reason, bool tx_frame); + +void skw_tx_mlme_mgmt(struct net_device *dev, u16 stype, + const u8 *bssid, const u8 *da, u16 reason); + +int skw_connect_sae_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_bss *bss); +int skw_connect_auth(struct wiphy *wiphy, struct net_device *dev, + struct skw_connect_param *conn, struct cfg80211_bss *bss); +int skw_connect_assoc(struct wiphy *wiphy, struct net_device *ndev, + struct skw_connect_param *conn); +void skw_connected(struct net_device *dev, struct skw_connect_param *conn, + const u8 *req_ie, int req_ie_len, const u8 *resp_ie, + int resp_ie_len, u16 status, gfp_t gfp); +void skw_disconnected(struct net_device *dev, u16 reason, const u8 *resp_ie, + int resp_ie_len, bool local_gen, gfp_t gfp); +int skw_cmd_unjoin(struct wiphy *wiphy, struct net_device *ndev, + const u8 *addr, u16 reason, bool tx_frame); +int skw_sap_set_mib(struct wiphy *wiphy, struct net_device *dev, + const u8 *ies, int ies_len); +int skw_wow_disable(struct wiphy *wiphy); +int skw_cmd_monitor(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, u8 mode); +int skw_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); +int skw_resume(struct wiphy *wiphy); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_compat.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_compat.h new file mode 100755 index 0000000..dbd43dd --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_compat.h @@ -0,0 +1,659 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_COMPAT_H__ +#define __SKW_COMPAT_H__ + +#include <linux/version.h> +#include <linux/skbuff.h> +#include <net/cfg80211.h> +#include <linux/proc_fs.h> +#include <linux/rtc.h> +#include <linux/etherdevice.h> + +/* EID block */ +#define SKW_WLAN_EID_EXT_HE_CAPABILITY 35 +#define SKW_WLAN_EID_EXT_HE_OPERATION 36 +#define SKW_WLAN_EID_MULTI_BSSID_IDX 85 +#define SKW_WLAN_EID_EXTENSION 255 +#define SKW_WLAN_EID_FRAGMENT 242 + +/* compat for ieee80211 */ +#define SKW_HE_MAC_CAP0_HTC_HE 0x01 +#define SKW_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US 0x08 +#define SKW_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8 0x70 + +#define SKW_HE_MAC_CAP2_BSR 0x08 +#define SKW_HE_MAC_CAP2_MU_CASCADING 0x40 +#define SKW_HE_MAC_CAP2_ACK_EN 0x80 + +#define SKW_HE_MAC_CAP3_GRP_ADDR_MULTI_STA_BA_DL_MU 0x01 +#define SKW_HE_MAC_CAP3_OMI_CONTROL 0x02 +#define SKW_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2 0x10 + +#define SKW_HE_MAC_CAP4_AMDSU_IN_AMPDU 0x40 + +#define SKW_HE_PHY_CAP0_DUAL_BAND 0x01 +#define SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G 0x02 +#define SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G 0x04 +#define SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G 0x08 +#define SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G 0x10 + +#define SKW_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK 0x0f +#define SKW_HE_PHY_CAP1_DEVICE_CLASS_A 0x10 +#define SKW_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD 0x20 +#define SKW_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS 0X80 + +#define SKW_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US 0x02 +#define SKW_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ 0x04 +#define SKW_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ 0x08 +#define SKW_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO 0x40 +#define SKW_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO 0x80 + +#define SKW_WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25 +#define SKW_WLAN_CATEGORY_RADIO_MEASUREMENT 5 + +#define SKW_IEEE80211_CHAN_NO_20MHZ BIT(11) +/* end of compat for ieee80211 */ + +#define SKW_WIPHY_FEATURE_SCAN_RANDOM_MAC BIT(29) +#define SKW_EXT_CAPA_BSS_TRANSITION 19 +#define SKW_EXT_CAPA_MBSSID 22 +#define SKW_EXT_CAPA_TDLS_SUPPORT 37 +#define SKW_EXT_CAPA_TWT_REQ_SUPPORT 77 + +#define SKW_BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 +#define SKW_BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126 + +#ifndef MIN_NICE +#define MIN_NICE -20 +#endif + +#ifndef TASK_IDLE +#define TASK_IDLE TASK_INTERRUPTIBLE +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#define SKW_BSS_TYPE_ESS IEEE80211_BSS_TYPE_ESS +#define SKW_BSS_TYPE_IBSS IEEE80211_BSS_TYPE_IBSS +#define SKW_PRIVACY_ESS_ANY IEEE80211_PRIVACY_ANY +#define SKW_PRIVACY_IBSS_ANY IEEE80211_PRIVACY_ANY +#else +#define SKW_BSS_TYPE_ESS WLAN_CAPABILITY_ESS +#define SKW_BSS_TYPE_IBSS WLAN_CAPABILITY_IBSS +#define SKW_PRIVACY_ESS_ANY WLAN_CAPABILITY_ESS +#define SKW_PRIVACY_IBSS_ANY WLAN_CAPABILITY_ESS +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#define SKW_PASSIVE_SCAN (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR) +#else +#define SKW_PASSIVE_SCAN IEEE80211_CHAN_PASSIVE_SCAN +#endif + +#define skw_from_timer(var, callback_timer, timer_fieldname) \ + container_of(callback_timer, typeof(*var), timer_fieldname) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +#define skw_compat_setup_timer(timer, fn) \ + timer_setup(timer, fn, 0) +#else +typedef void (*tfunc)(unsigned long); +#define skw_compat_setup_timer(timer, fn) \ + setup_timer(timer, (tfunc)fn, (unsigned long)timer) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) +enum nl80211_bss_scan_width { + NL80211_BSS_CHAN_WIDTH_20, + NL80211_BSS_CHAN_WIDTH_10, + NL80211_BSS_CHAN_WIDTH_5, +}; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) +#define NUM_NL80211_BANDS 3 +#endif + +#if (KERNEL_VERSION(4, 6, 0) <= LINUX_VERSION_CODE) +#define SKW_IS_COMPAT_TASK in_compat_syscall +#else +#define SKW_IS_COMPAT_TASK is_compat_task +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static inline struct sk_buff * +skw_compat_vendor_event_alloc(struct wiphy *wiphy, struct wireless_dev *wdev, + int roxlen, int event_idx, gfp_t gfp) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + return cfg80211_vendor_event_alloc(wiphy, wdev, roxlen, event_idx, gfp); +#else + return cfg80211_vendor_event_alloc(wiphy, roxlen, event_idx, gfp); +#endif +} +#endif + +static inline void skw_compat_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + s32 rssi_level, gfp_t gfp) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level, gfp); +#else + cfg80211_cqm_rssi_notify(dev, rssi_event, gfp); +#endif +} + +static inline void skw_compat_page_frag_free(void *addr) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + page_frag_free(addr); +#else + put_page(virt_to_head_page(addr)); +#endif +} + +static inline void +skw_compat_scan_done(struct cfg80211_scan_request *req, bool aborted) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + + cfg80211_scan_done(req, &info); +#else + cfg80211_scan_done(req, aborted); +#endif +} + +static inline void skw_compat_disconnected(struct net_device *ndev, u16 reason, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) + u8 *ie, +#else + const u8 *ie, +#endif + size_t ie_len, + bool locally_generated, gfp_t gfp) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + cfg80211_disconnected(ndev, reason, ie, ie_len, locally_generated, gfp); +#else + cfg80211_disconnected(ndev, reason, ie, ie_len, gfp); +#endif +} + +static inline bool skw_compat_reg_can_beacon(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + return cfg80211_reg_can_beacon_relax(wiphy, chandef, iftype); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + return cfg80211_reg_can_beacon(wiphy, chandef, iftype); +#else + return cfg80211_reg_can_beacon(wiphy, chandef); +#endif +} + +static inline unsigned int +skw_compat_classify8021d(struct sk_buff *skb, void *qos_map) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) + return cfg80211_classify8021d(skb); +#else + return cfg80211_classify8021d(skb, qos_map); +#endif +} + +static inline void +skw_compat_rx_mlme_mgmt(struct net_device *dev, void *buf, size_t len) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_rx_mlme_mgmt(dev, buf, len); +#else + //FIXME: Dead lock wdev_lock->sta_core + struct ieee80211_mgmt *mgmt = buf; + + mutex_unlock(&dev->ieee80211_ptr->mtx); + + if (ieee80211_is_auth(mgmt->frame_control)) + cfg80211_send_rx_auth(dev, buf, len); + else if (ieee80211_is_deauth(mgmt->frame_control)) + cfg80211_send_deauth(dev, buf, len); + else if (ieee80211_is_disassoc(mgmt->frame_control)) + cfg80211_send_disassoc(dev, buf, len); + + mutex_lock(&dev->ieee80211_ptr->mtx); +#endif +} + +static inline const u8 *skw_compat_bssid(struct cfg80211_connect_params *req) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + return req->bssid ? req->bssid : req->bssid_hint; +#else + return req->bssid; +#endif +} + +static inline struct ieee80211_channel * +skw_compat_channel(struct cfg80211_connect_params *req) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + return req->channel ? req->channel : req->channel_hint; +#else + return req->channel; +#endif +} + +// fixme: +// disable interface combination check for debug +#if 0 +static inline int skw_compat_check_combs(struct wiphy *wiphy, int nr_channels, + u8 radar, int type_num[NUM_NL80211_IFTYPES]) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + struct iface_combination_params params = { + .num_different_channels = nr_channels, + .radar_detect = radar, + }; + + memcpy(params.iftype_num, type_num, NUM_NL80211_IFTYPES * sizeof(int)); + + return cfg80211_check_combinations(wiphy, ¶ms); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + return cfg80211_check_combinations(wiphy, nr_channels, radar, type_num); +#else + // TODO: + // implement function to check combinations + return 0; +#endif +} +#else +static inline int skw_compat_check_combs(struct wiphy *wiphy, int nr_channels, + u8 radar, int type_num[NUM_NL80211_IFTYPES]) +{ + return 0; +} +#endif + +static inline void skw_compat_auth_timeout(struct net_device *dev, const u8 *addr) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_auth_timeout(dev, addr); +#else + mutex_unlock(&dev->ieee80211_ptr->mtx); + cfg80211_send_auth_timeout(dev, addr); + mutex_lock(&dev->ieee80211_ptr->mtx); +#endif +} + +static inline void skw_cfg80211_tx_mlme_mgmt(struct net_device *dev, + const u8 *buf, size_t len) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + cfg80211_tx_mlme_mgmt(dev, buf, len, true); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_tx_mlme_mgmt(dev, buf, len); +#else + struct ieee80211_mgmt *mgmt = (void *)buf; + + if (ieee80211_is_deauth(mgmt->frame_control)) + __cfg80211_send_deauth(dev, buf, len); + else + __cfg80211_send_disassoc(dev, buf, len); +#endif +} + +static inline void skw_compat_rtc_time_to_tm(unsigned long time, struct rtc_time *tm) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) + rtc_time_to_tm(time, tm); +#else + rtc_time64_to_tm(time, tm); +#endif +} + +static inline bool skw_compat_cfg80211_rx_mgmt(struct wireless_dev *wdev, + int freq, int sig_dbm, const u8 *buf, + size_t len, u32 flags, gfp_t gfp) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) + return cfg80211_rx_mgmt(wdev, freq, sig_dbm, buf, len, gfp); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + return cfg80211_rx_mgmt(wdev, freq, sig_dbm, buf, len, flags, gfp); +#else + return cfg80211_rx_mgmt(wdev, freq, sig_dbm, buf, len, flags); +#endif +} + +static inline void *skw_pde_data(const struct inode *inode) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + return pde_data(inode); +#else + return PDE_DATA(inode); +#endif +} + +static inline int skw_set_wiphy_regd_sync(struct wiphy *wiphy, + struct ieee80211_regdomain *rd) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + return regulatory_set_wiphy_regd_sync(wiphy, rd); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + return regulatory_set_wiphy_regd_sync_rtnl(wiphy, rd); +#else + return -EINVAL; +#endif +} + +#ifdef __SKW_ANDROID__ +static inline void skw_compat_rx_assoc_resp(struct net_device *dev, + struct cfg80211_bss *bss, const u8 *buf, size_t len, + int uapsd, const u8 *req_ies, size_t req_ies_len) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 74) + struct cfg80211_rx_assoc_resp assoc_resp = { + .buf = buf, + .len = len, + .req_ies = req_ies, + .req_ies_len = req_ies_len, + .ap_mld_addr = NULL, + .links[0] = { + .bss = bss, + }, + }; + + cfg80211_rx_assoc_resp(dev, &assoc_resp); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) + cfg80211_rx_assoc_resp(dev, bss, buf, len, uapsd, req_ies, req_ies_len); +#elif defined SKW_RX_ASSOC_RESP_EXT + cfg80211_rx_assoc_resp_ext(dev, bss, buf, len, uapsd, req_ies, req_ies_len); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + cfg80211_rx_assoc_resp(dev, bss, buf, len, uapsd); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_rx_assoc_resp(dev, bss, buf, len); +#else + mutex_unlock(&dev->ieee80211_ptr->mtx); + cfg80211_send_rx_assoc(dev, bss, buf, len); + mutex_lock(&dev->ieee80211_ptr->mtx); +#endif +} + +static inline void skw_compat_assoc_failure(struct net_device *dev, + struct cfg80211_bss *bss, bool timeout) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 74) + struct cfg80211_assoc_failure info = { + .ap_mld_addr = NULL, + .bss[0] = bss, + .timeout = timeout, + }; + + cfg80211_assoc_failure(dev, &info); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 2) || LINUX_VERSION_CODE == KERNEL_VERSION(4, 8, 17)) + if (timeout) + cfg80211_assoc_timeout(dev, bss); + else + cfg80211_abandon_assoc(dev, bss); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_assoc_timeout(dev, bss); +#else + mutex_unlock(&dev->ieee80211_ptr->mtx); + cfg80211_send_assoc_timeout(dev, bss->bssid); + mutex_lock(&dev->ieee80211_ptr->mtx); +#endif +} + +static inline void skw_compat_cfg80211_roamed(struct net_device *dev, + const u8 *bssid, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, gfp_t gfp) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 41) + struct cfg80211_roam_info roam_info = { + .req_ie = req_ie, + .req_ie_len = req_ie_len, + .resp_ie = resp_ie, + .resp_ie_len = resp_ie_len, + .valid_links = 0, + .links[0] = { + .bss = NULL, + .bssid = bssid, + }, + }; + + cfg80211_roamed(dev, &roam_info, gfp); + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + struct cfg80211_roam_info roam_info = { + .bss = NULL, + .bssid = bssid, + .req_ie = req_ie, + .req_ie_len = req_ie_len, + .resp_ie = resp_ie, + .resp_ie_len = resp_ie_len, + }; + + cfg80211_roamed(dev, &roam_info, gfp); +#else + // fixme: + // fix channel + cfg80211_roamed(dev, NULL, bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, gfp); +#endif +} + +static inline void skw_ch_switch_started_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef, u8 count, bool quiet) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 74) + cfg80211_ch_switch_started_notify(dev, chandef, 0, count, quiet, 0); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 35) + cfg80211_ch_switch_started_notify(dev, chandef, count, quiet); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + cfg80211_ch_switch_started_notify(dev, chandef, count); +#else +#endif +} + +static inline void skw_ch_switch_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 74) + cfg80211_ch_switch_notify(dev, chandef, 0, 0); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 41) + cfg80211_ch_switch_notify(dev, chandef, 0); +#else + cfg80211_ch_switch_notify(dev, chandef); +#endif +} + +#else /* Linux */ +static inline void skw_compat_rx_assoc_resp(struct net_device *dev, + struct cfg80211_bss *bss, const u8 *buf, size_t len, + int uapsd, const u8 *req_ies, size_t req_ies_len) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + struct cfg80211_rx_assoc_resp assoc_resp = { + .buf = buf, + .len = len, + .req_ies = req_ies, + .req_ies_len = req_ies_len, + .ap_mld_addr = NULL, + .links[0] = { + .bss = bss, + }, + }; + + cfg80211_rx_assoc_resp(dev, &assoc_resp); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) + cfg80211_rx_assoc_resp(dev, bss, buf, len, uapsd, req_ies, req_ies_len); +#elif defined SKW_RX_ASSOC_RESP_EXT + cfg80211_rx_assoc_resp_ext(dev, bss, buf, len, uapsd, req_ies, req_ies_len); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + cfg80211_rx_assoc_resp(dev, bss, buf, len, uapsd); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_rx_assoc_resp(dev, bss, buf, len); +#else + mutex_unlock(&dev->ieee80211_ptr->mtx); + cfg80211_send_rx_assoc(dev, bss, buf, len); + mutex_lock(&dev->ieee80211_ptr->mtx); +#endif +} + +static inline void skw_compat_assoc_failure(struct net_device *dev, + struct cfg80211_bss *bss, bool timeout) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + struct cfg80211_assoc_failure info = { + .ap_mld_addr = NULL, + .bss[0] = bss, + .timeout = timeout, + }; + + cfg80211_assoc_failure(dev, &info); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 2) || LINUX_VERSION_CODE == KERNEL_VERSION(4, 8, 17)) + if (timeout) + cfg80211_assoc_timeout(dev, bss); + else + cfg80211_abandon_assoc(dev, bss); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_assoc_timeout(dev, bss); +#else + mutex_unlock(&dev->ieee80211_ptr->mtx); + cfg80211_send_assoc_timeout(dev, bss->bssid); + mutex_lock(&dev->ieee80211_ptr->mtx); +#endif +} + +static inline void skw_compat_cfg80211_roamed(struct net_device *dev, + const u8 *bssid, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, gfp_t gfp) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + struct cfg80211_roam_info roam_info = { + .req_ie = req_ie, + .req_ie_len = req_ie_len, + .resp_ie = resp_ie, + .resp_ie_len = resp_ie_len, + .valid_links = 0, + .links[0] = { + .bss = NULL, + .bssid = bssid, + }, + }; + + cfg80211_roamed(dev, &roam_info, gfp); + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + struct cfg80211_roam_info roam_info = { + .bss = NULL, + .bssid = bssid, + .req_ie = req_ie, + .req_ie_len = req_ie_len, + .resp_ie = resp_ie, + .resp_ie_len = resp_ie_len, + }; + + cfg80211_roamed(dev, &roam_info, gfp); +#else + // fixme: + // fix channel + cfg80211_roamed(dev, NULL, bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, gfp); +#endif +} + +static inline void skw_ch_switch_started_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef, u8 count, bool quiet) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + cfg80211_ch_switch_started_notify(dev, chandef, 0, count, quiet); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + cfg80211_ch_switch_started_notify(dev, chandef, count, quiet); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + cfg80211_ch_switch_started_notify(dev, chandef, count); +#else +#endif +} + +static inline void skw_ch_switch_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 2) + cfg80211_ch_switch_notify(dev, chandef, 0); +#else + cfg80211_ch_switch_notify(dev, chandef); +#endif +} + +#endif /* Linux */ + +static inline unsigned long skw_get_seconds(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + return ktime_get_real_seconds(); +#else + return get_seconds(); +#endif +} + +static inline int skw_register_netdevice(struct net_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + return cfg80211_register_netdevice(dev); +#else + return register_netdevice(dev); +#endif +} + +static inline void skw_unregister_netdevice(struct net_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + return cfg80211_unregister_netdevice(dev); +#else + return unregister_netdevice(dev); +#endif +} + +static inline int skw_compat_cfg80211_chandef_dfs_required(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + return cfg80211_chandef_dfs_required(wiphy, chandef, iftype); +#else + return cfg80211_chandef_dfs_required(wiphy, chandef); +#endif +} + +static inline void skw_compat_netif_napi_add_weight(struct net_device *dev, + struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) + netif_napi_add_weight(dev, napi, poll, weight); +#else + netif_napi_add(dev, napi, poll, weight); +#endif +} + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_config.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_config.c new file mode 100755 index 0000000..87cd184 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_config.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/ctype.h> +#include <linux/firmware.h> + +#include "skw_util.h" +#include "skw_config.h" +#include "skw_log.h" +#include "skw_regd.h" + +#define SKW_LINE_BUFF_LEN 128 + +struct skwifi_cfg { + char *name; + int (*parser)(struct skwifi_cfg *cfg, char *key, char *data); + void *priv; +}; + +static struct skwifi_cfg *skw_cfg_match(struct skwifi_cfg *table, char *name) +{ + int i; + struct skwifi_cfg *cfg = NULL; + + for (i = 0; table[i].name != NULL; i++) { + if (!strcmp(name, table[i].name)) { + cfg = &table[i]; + break; + } + } + + return cfg; +} + +static void skw_parser(struct skwifi_cfg *table, const u8 *data, int size) +{ + char chr, token; + char word[64] = {0}; + char skw_key[64] = {0}; + char skw_dat[256] = {0}; + int flags = 0; + char comma[] = ","; + int i, nr = 0, params = 0; + struct skwifi_cfg *cfg = NULL; + bool do_save, do_append, do_parse; + + for (token = 0, i = 0; i < size; i++) { + chr = data[i]; + do_save = do_append = do_parse = false; + + switch (token) { + case '%': + if (chr == '%') { + token = 0; + word[nr] = '\0'; + + cfg = skw_cfg_match(table, word); + } else { + do_save = true; + } + + break; + + case '[': + if (chr == ']') { + token = 0; + word[nr] = '\0'; + + flags |= BIT(0); + strlcpy(skw_key, word, sizeof(skw_key)); + } else { + do_save = true; + } + + break; + + case '"': + if (chr == '"') { + token = 0; + do_append = true; + } else { + do_save = true; + } + + break; + + case '<': + if (chr == '<') { + do_append = true; + + } else if (chr == '>') { + token = 0; + + if (!nr) + word[nr++] = '*'; + + do_append = true; + } else { + if (isxdigit(chr) || chr == ',' || + (tolower(chr) == 'x' && (nr && word[nr - 1] == '0'))) + do_save = true; + } + + break; + + default: + switch (chr) { + case '%': + case '[': + nr = 0; + memset(word, 0x0, sizeof(word)); + + token = chr; + do_parse = true; + + break; + + case '"': + case '<': + if (flags & BIT(1)) { + nr = 0; + memset(word, 0x0, sizeof(word)); + + token = chr; + } + + break; + + case '\n': + case '\0': + token = 0; + do_parse = true; + + break; + + case '=': + if (flags & BIT(0)) + flags |= BIT(1); + + break; + + default: + // drop + break; + } + + break; + } + + if (do_save) { + if (nr < sizeof(word) - 1) + word[nr++] = chr; + } else if (do_append) { + + if (params++) + strlcat(skw_dat, comma, sizeof(skw_dat)); + + word[nr] = '\0'; + strlcat(skw_dat, word, sizeof(skw_dat)); + + nr = 0; + memset(word, 0x0, sizeof(word)); + + } else if (do_parse) { + if (cfg && flags & BIT(1) && params) { + skw_detail("key: %s, data: %s\n", skw_key, skw_dat); + cfg->parser(cfg, skw_key, skw_dat); + } + + flags = 0; + params = 0; + do_parse = false; + + memset(skw_key, 0x0, sizeof(skw_key)); + memset(skw_dat, 0x0, sizeof(skw_dat)); + } + } +} + +static int skw_global_parser(struct skwifi_cfg *config, char *key, char *data) +{ + int ret; + char *endp; + struct skw_cfg_global *cfg = config->priv; + + if (cfg == NULL) + return -EINVAL; + + if (!strcmp(key, "mac")) { + u8 addr[ETH_ALEN] = {0}; + + ret = sscanf(data, "0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", + (int *)&addr[0], (int *)&addr[1], (int *)&addr[2], + (int *)&addr[3], (int *)&addr[4], (int *)&addr[5]); + + skw_dbg("ret: %d, addr: %pM\n", ret, addr); + if (ret == ETH_ALEN && is_valid_ether_addr(addr)) + skw_ether_copy(cfg->mac, addr); + + } else if (!strcmp(key, "dma_addr_align")) { + cfg->dma_addr_align = simple_strtol(data, &endp, 0); + + } else if (!strcmp(key, "reorder_timeout")) { + cfg->reorder_timeout = simple_strtol(data, &endp, 0); + + } else if (!strcmp(key, "offchan_tx")) { + switch (simple_strtol(data, &endp, 0)) { + case 0: + clear_bit(SKW_CFG_FLAG_OFFCHAN_TX, &cfg->flags); + break; + case 1: + set_bit(SKW_CFG_FLAG_OFFCHAN_TX, &cfg->flags); + break; + default: + break; + } + + } else if (!strcmp(key, "overlay_mode")) { + switch (simple_strtol(data, &endp, 0)) { + case 0: + clear_bit(SKW_CFG_FLAG_OVERLAY_MODE, &cfg->flags); + break; + case 1: + set_bit(SKW_CFG_FLAG_OVERLAY_MODE, &cfg->flags); + break; + default: + break; + } + + } else { + skw_dbg("unsupport key: %s\n", key); + } + + return 0; +} + +static int skw_firmware_parser(struct skwifi_cfg *config, char *key, char *data) +{ + int ret; + char *endp; + struct skw_cfg_firmware *cfg = config->priv; + + if (cfg == NULL) + return -EINVAL; + + if (!strcmp(key, "link_loss_thrd")) { + cfg->link_loss_thrd = simple_strtol(data, &endp, 0); + + skw_dbg("link_loss_thrd: %d\n", cfg->link_loss_thrd); + } else if (!strcmp(key, "go_noa_ratio_idx")) { + cfg->noa_ratio_en = simple_strtol(data, &endp, 0); + + if (cfg->noa_ratio_en) + ret = sscanf(data, "%d,%d", + &cfg->noa_ratio_en, &cfg->noa_ratio_idx); + + skw_dbg("ret:%d noa_ratio_en: %d, idx:%d\n", ret, + cfg->noa_ratio_en, cfg->noa_ratio_idx); + } else if (!strcmp(key, "force_go_once_noa")) { + cfg->once_noa_en = simple_strtol(data, &endp, 0); + + if (cfg->once_noa_en) + ret = sscanf(data, "%d,%d,%d", + &cfg->once_noa_en, &cfg->once_noa_pre, &cfg->once_noa_abs); + + skw_dbg("ret:%d once_noa_en: %d, pre:%d, abs:%d\n", ret, cfg->once_noa_en, + cfg->once_noa_pre, cfg->once_noa_abs); + } else if (!strcmp(key, "offload_roaming_disable")) { + cfg->offload_roaming_disable = simple_strtol(data, &endp, 0); + + skw_dbg("offload_roaming_disable: %d\n", cfg->offload_roaming_disable); + } else if (!strcmp(key, "24ghz_bandwidth")) { + cfg->band_24ghz = simple_strtol(data, &endp, 0); + + skw_dbg("24ghz_bandwidth: %d\n", cfg->band_24ghz); + } else if (!strcmp(key, "5ghz_bandwidth")) { + cfg->band_5ghz = simple_strtol(data, &endp, 0); + + skw_dbg("5ghz_bandwidth: %d\n", cfg->band_5ghz); + } else if (!strcmp(key, "hdk_test")) { + cfg->hdk_tst = simple_strtol(data, &endp, 0); + + skw_dbg("hdk_test: %d\n", cfg->hdk_tst); + } else { + skw_dbg("unsupport key: %s\n", key); + } + + return 0; +} + +static int skw_intf_parser(struct skwifi_cfg *config, char *key, char *data) +{ + int ret; + char *endp; + int i, nr_params; + char mode[64] = {0}; + char inst[64] = {0}; + char flags[64] = {0}; + char mac[64] = {0}; + char ifname[32] = {0}; + u8 addr[ETH_ALEN] = {0}; + // struct skw_cfg_intf *intf = config->priv; + + /* "ifname",<mode>,<inst>,<flags>,<mac> */ + nr_params = sscanf(data, "%16[^,],%32[^,],%32[^,],%32[^,],%s", + ifname, mode, inst, flags, mac); + + skw_detail("key: %s, data: %s, nr_params: %d\n", key, data, nr_params); + + for (i = 0; i < nr_params; i++) + switch (i) { + /* interface name */ + case 0: + skw_dbg("ifname: %s\n", ifname); + break; + + /* interface mode */ + case 1: + skw_dbg("mode: %ld\n", simple_strtol(mode, &endp, 0)); + break; + + /* interface instance */ + case 2: + skw_dbg("inst: %ld\n", simple_strtol(inst, &endp, 0)); + break; + + /* interface flags */ + case 3: + skw_dbg("flags: 0x%lx\n", simple_strtol(flags, &endp, 0)); + break; + + /* interface mac */ + case 4: + ret = sscanf(mac, "0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", + (int *)&addr[0], (int *)&addr[1], + (int *)&addr[2], (int *)&addr[3], + (int *)&addr[4], (int *)&addr[5]); + + skw_dbg("ret: %d, addr: %pM\n", ret, addr); + break; + } + + return 0; +} + +static int skw_calib_parser(struct skwifi_cfg *config, char *key, char *data) +{ + char *endp; + struct skw_cfg_calib *calib = config->priv; + + if (calib == NULL) + return -EINVAL; + + if (!strcmp(key, "strict_mode")) { + switch (simple_strtol(data, &endp, 0)) { + case 0: + clear_bit(SKW_CFG_CALIB_STRICT_MODE, &calib->flags); + break; + case 1: + set_bit(SKW_CFG_CALIB_STRICT_MODE, &calib->flags); + break; + default: + break; + } + + } else if (!strcmp(key, "chip")) { + strlcpy(calib->chip, data, sizeof(calib->chip) - 1); + + } else if (!strcmp(key, "project")) { + strlcpy(calib->project, data, sizeof(calib->project) - 1); + + } else { + skw_dbg("unsupport key: %s\n", key); + } + + return 0; +} + +static int skw_regdom_parser(struct skwifi_cfg *config, char *key, char *data) +{ + struct skw_cfg_regd *regd = config->priv; + + if (regd == NULL) + return -EINVAL; + + if (!strcmp(key, "country")) { + if (strlen(data) == 2) + memcpy(regd->country, data, 2); + + } else { + skw_dbg("unsupport key: %s\n", key); + } + + return 0; +} + +static int skw_roam_parser(struct skwifi_cfg *config, char *key, char *data) +{ + return 0; +} + +static struct skwifi_cfg g_cfg_table[] = { + { + .name = "global", + .parser = skw_global_parser, + }, + { + .name = "interface", + .parser = skw_intf_parser, + }, + { + .name = "calib", + .parser = skw_calib_parser, + }, + { + .name = "regdom", + .parser = skw_regdom_parser, + }, + { + .name = "roaming", + .parser = skw_roam_parser, + }, + { + .name = "firmware", + .parser = skw_firmware_parser, + }, + { + .name = NULL, + .parser = NULL, + .priv = NULL, + } +}; + +void skw_update_config(struct device *dev, const char *name, struct skw_config *config) +{ + int i; + const struct firmware *fw; + + if (request_firmware_direct(&fw, name, dev)) + return; + + skw_dbg("load %s successful\n", name); + + for (i = 0; g_cfg_table[i].name != NULL; i++) { + if (!strcmp(g_cfg_table[i].name, "global")) + g_cfg_table[i].priv = &config->global; + else if (!strcmp(g_cfg_table[i].name, "interface")) + g_cfg_table[i].priv = &config->intf; + else if (!strcmp(g_cfg_table[i].name, "calib")) + g_cfg_table[i].priv = &config->calib; + else if (!strcmp(g_cfg_table[i].name, "regdom")) + g_cfg_table[i].priv = &config->regd; + else if (!strcmp(g_cfg_table[i].name, "roaming")) + g_cfg_table[i].priv = NULL; + else if (!strcmp(g_cfg_table[i].name, "firmware")) + g_cfg_table[i].priv = &config->fw; + else { + g_cfg_table[i].priv = NULL; + + skw_warn("section: %s not support\n", g_cfg_table[i].name); + } + } + + skw_parser(g_cfg_table, fw->data, fw->size); + + release_firmware(fw); +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_config.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_config.h new file mode 100755 index 0000000..badaac5 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_config.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_CONFIG_H__ +#define __SKW_CONFIG_H__ + +#include <linux/kernel.h> +#include <linux/etherdevice.h> + +#define SKW_CFG_FLAG_OVERLAY_MODE 0 +#define SKW_CFG_FLAG_STA_EXT 1 +#define SKW_CFG_FLAG_SAP_EXT 2 +#define SKW_CFG_FLAG_OFFCHAN_TX 4 +#define SKW_CFG_FLAG_REPEATER 5 +#define SKW_CFG_FLAG_P2P_DEV 6 + +struct skw_cfg_global { + unsigned long flags; + + u8 mac[ETH_ALEN]; + u8 dma_addr_align; + u8 reorder_timeout; +}; + +#define SKW_CFG_INTF_FLAG_INVALID 0 +#define SKW_CFG_INTF_FLAG_LEGACY 1 +struct skw_cfg_interface { + char name[IFNAMSIZ]; + u8 mac[ETH_ALEN]; + u8 iftype; + u8 inst; + unsigned long flags; +}; + +struct skw_cfg_intf { + struct skw_cfg_interface interface[4]; +}; + +#define SKW_CFG_REGD_COUNTRY_CODE 0 +#define SKW_CFG_REGD_SELF_MANAGED 1 +#define SKW_CFG_REGD_IGNORE_USER 2 +#define SKW_CFG_REGD_IGNORE_COUNTRY_IE 3 + +struct skw_cfg_regd { + unsigned long flags; + char country[2]; +}; + +#define SKW_CFG_CALIB_STRICT_MODE 0 +#define SKW_CFG_CALIB_CHIP_NAME 1 +#define SKW_CFG_CALIB_PROJECT_NAME 2 +#define SKW_CFG_CALIB_EXTRA_ID 3 + +struct skw_cfg_calib { + unsigned long flags; + + char chip[16]; + char project[16]; +}; + +struct skw_cfg_firmware { + u32 link_loss_thrd; + u32 noa_ratio_idx; + u32 noa_ratio_en; + u32 once_noa_en; + u32 once_noa_pre; + u32 once_noa_abs; + u32 dot11k_disable; + u32 dot11v_disable; + u32 dot11r_disable; + u32 offload_roaming_disable; + u32 band_24ghz; + u32 band_5ghz; + u32 hdk_tst; +}; + +struct skw_config { + struct skw_cfg_global global; + struct skw_cfg_intf intf; + struct skw_cfg_calib calib; + struct skw_cfg_regd regd; + struct skw_cfg_firmware fw; +}; + +void skw_update_config(struct device *dev, const char *name, struct skw_config *config); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_core.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_core.c new file mode 100755 index 0000000..5a319cf --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_core.c @@ -0,0 +1,3530 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/ip.h> +#include <linux/if_ether.h> +#include <linux/skbuff.h> +#include <linux/inetdevice.h> +#include <net/addrconf.h> +#include <linux/ctype.h> +#include <net/tcp.h> +#include <net/arp.h> +#include <linux/platform_device.h> +#include <linux/if_tunnel.h> +#include <linux/firmware.h> +#include <generated/utsrelease.h> +#include <linux/suspend.h> + +#ifdef CONFIG_PLATFORM_ROCKCHIP +#include <linux/rfkill-wlan.h> +#endif + +#include "skw_core.h" +#include "skw_cfg80211.h" +#include "skw_tx.h" +#include "skw_rx.h" +#include "skw_iw.h" +#include "skw_timer.h" +#include "skw_work.h" +#include "version.h" +#include "skw_calib.h" +#include "skw_vendor.h" +#include "skw_regd.h" +#include "skw_recovery.h" +#include "skw_config.h" +#include "trace.h" + +struct skw_global_config { + atomic_t index; + struct skw_config conf; +}; + +static u8 skw_mac[ETH_ALEN]; +static struct skw_global_config g_skw_config; + +static atomic_t skw_chip_idx = ATOMIC_INIT(0); + +static const int g_skw_up_to_ac[8] = { + SKW_WMM_AC_BE, + SKW_WMM_AC_BK, + SKW_WMM_AC_BK, + SKW_WMM_AC_BE, + SKW_WMM_AC_VI, + SKW_WMM_AC_VI, + SKW_WMM_AC_VO, + SKW_WMM_AC_VO +}; + +static int skw_repeater_show(struct seq_file *seq, void *data) +{ + struct skw_core *skw = seq->private; + + if (test_bit(SKW_FLAG_REPEATER, &skw->flags)) + seq_puts(seq, "enable\n"); + else + seq_puts(seq, "disable\n"); + + return 0; +} + +static int skw_repeater_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_repeater_show, inode->i_private); +} + +static ssize_t skw_repeater_write(struct file *fp, const char __user *buf, + size_t len, loff_t *offset) +{ + int i; + char cmd[32] = {0}; + struct skw_core *skw = fp->f_inode->i_private; + + for (i = 0; i < len; i++) { + char c; + + if (get_user(c, buf)) + return -EFAULT; + + if (c == '\n' || c == '\0') + break; + + cmd[i] = tolower(c); + buf++; + } + + if (strcmp(cmd, "enable") == 0) + set_bit(SKW_FLAG_REPEATER, &skw->flags); + else if (strcmp(cmd, "disable") == 0) + clear_bit(SKW_FLAG_REPEATER, &skw->flags); + else + skw_warn("rx_reorder support setting values of \"enable\" or \"disable\"\n"); + + return len; +} + +static const struct file_operations skw_repeater_fops = { + .owner = THIS_MODULE, + .open = skw_repeater_open, + .read = seq_read, + .release = single_release, + .write = skw_repeater_write, +}; + +static void skw_dbg_print(struct skw_core *skw, struct seq_file *seq) +{ + int i; + u64 nsecs; + unsigned long rem_nsec; + + for (i = 0; i < skw->dbg.nr_cmd; i++) { + struct skw_dbg_cmd *cmd = &skw->dbg.cmd[i]; + + seq_printf(seq, "cmd[%d].id: %d, seq: %d, flags: 0x%lx, loop: %d\n", + i, cmd->id, cmd->seq, cmd->flags, cmd->loop); + + nsecs = cmd->trigger; + rem_nsec = do_div(nsecs, 1000000000); + seq_printf(seq, " cmd.%d.%d.trigger: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->build; + rem_nsec = do_div(nsecs, 1000000000); + seq_printf(seq, " cmd.%d.%d.build: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->xmit; + rem_nsec = do_div(nsecs, 1000000000); + seq_printf(seq, " cmd.%d.%d.xmit: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->done; + rem_nsec = do_div(nsecs, 1000000000); + seq_printf(seq, " cmd.%d.%d.done: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->ack; + rem_nsec = do_div(nsecs, 1000000000); + seq_printf(seq, " cmd.%d.%d.ack: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->assert; + rem_nsec = do_div(nsecs, 1000000000); + seq_printf(seq, " cmd.%d.%d.assert: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + seq_puts(seq, "\n"); + } + + for (i = 0; i < skw->dbg.nr_dat; i++) { + struct skw_dbg_dat *dat = &skw->dbg.dat[i]; + + seq_printf(seq, "dat[%d], tx_qlen: %d\n", i, dat->qlen); + + nsecs = dat->trigger; + rem_nsec = do_div(nsecs, 1000000000); + seq_printf(seq, " dat.%d.%d.trigger: %5lu.%06lu\n", + i, dat->qlen, (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = dat->done; + rem_nsec = do_div(nsecs, 1000000000); + seq_printf(seq, " dat.%d.%d.done: %5lu.%06lu\n", + i, dat->qlen, (unsigned long)nsecs, rem_nsec / 1000); + + seq_puts(seq, "\n"); + } +} + +static void skw_timestap_print(struct skw_core *skw, struct seq_file *seq) +{ + u64 ts; + struct rtc_time tm; + unsigned long rem_nsec; + + ts = skw->fw.host_timestamp; + rem_nsec = do_div(ts, 1000000000); + skw_compat_rtc_time_to_tm(skw->fw.host_seconds, &tm); + + seq_printf(seq, + "Timestamp: %u - %lu.%06lu (%d-%02d-%02d %02d:%02d:%02d UTC)\n", + skw->fw.timestamp, (unsigned long)ts, + rem_nsec / 1000, tm.tm_year + 1900, + tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec); +} + +static void skw_drv_info_print(struct skw_core *skw, struct seq_file *seq) +{ + int i; + +#define FLAG_TEST(n) test_bit(SKW_FLAG_##n, &skw->flags) + seq_printf(seq, + "SKW Flags:\t 0x%lx %s\n" + "TX Packets:\t %ld\n" + "RX Packets:\t %ld\n" + "txqlen_pending:\t %d\n" + "nr lmac:\t %d\n" + "skb_recycle_qlist:%d\n", + skw->flags, FLAG_TEST(FW_ASSERT) ? "(Assert)" : "", + skw->tx_packets, + skw->rx_packets, + atomic_read(&skw->txqlen_pending), + skw->hw.nr_lmac, + READ_ONCE(skw->skb_recycle_qlist.qlen)); + + for (i = 0; i < skw->hw.nr_lmac; i++) + seq_printf(seq, " credit[%d]:\t %d (%s)\n", + i, skw_get_hw_credit(skw, i), + skw_lmac_is_actived(skw, i) ? "active" : "inactive"); +#undef FLAG_TEST +} + +static void skw_fw_info_print(struct skw_core *skw, struct seq_file *seq) +{ + if (!skw->hw_pdata) + return; + + /* Firmware & BSP Info */ + seq_printf(seq, "Calib File:\t %s\n" + "FW Build:\t %s\n" + "FW Version:\t %s-%s (BSP-MAC)\n" + "BUS Type:\t 0x%x (%s)\n" + "Align Size:\t %d\n" + "TX Limit:\t %d\n", + skw->fw.calib_file, + skw->fw.build_time, + skw->fw.plat_ver, + skw->fw.wifi_ver, + skw->hw_pdata->bus_type, + skw_bus_name(skw->hw.bus), + skw->hw_pdata->align_value, + skw->hw.pkt_limit); +} + +static int skw_core_show(struct seq_file *seq, void *data) +{ + struct skw_core *skw = seq->private; + + seq_puts(seq, "\n"); + skw_timestap_print(skw, seq); + + seq_puts(seq, "\n"); + skw_drv_info_print(skw, seq); + + seq_puts(seq, "\n"); + skw_fw_info_print(skw, seq); + + seq_puts(seq, "\n"); + skw_dbg_print(skw, seq); + + seq_puts(seq, "\n"); + + return 0; +} + +static int skw_core_open(struct inode *inode, struct file *file) +{ + // return single_open(file, skw_core_show, inode->i_private); + return single_open(file, skw_core_show, skw_pde_data(inode)); +} + +void skw_debug_print_hw_tx_info(struct seq_file *seq, struct skw_get_hw_tx_info_s *info) +{ + int i = 0; + + if (!info) { + seq_puts(seq, "HW Tx Info structure is NULL\n"); + return; + } + + seq_puts(seq, "Hardware Tx info:\n\n"); + seq_printf(seq, "\tHIF TX All Count: %u\n", info->hif_tx_all_cnt); + seq_printf(seq, "\tHIF Report All Credit Count: %u\n", info->hif_rpt_all_credit_cnt); + seq_printf(seq, "\tCurrent Free Buffer Count: %u\n", info->cur_free_buf_cnt); + seq_printf(seq, "\tPending TX Packet Count: %u\n", info->pendding_tx_pkt_cnt); + seq_printf(seq, "\tLast Command Sequence: %u\n", info->lst_cmd_seq); + seq_printf(seq, "\tLast Event Sequence: %u\n", info->lst_evt_seq); + seq_printf(seq, "\tLMAC TX Broadcast Count: %u\n", info->lmac_tx_bc_cnt); + seq_printf(seq, "\tLMAC TX Unicast Count: %u\n", info->lmac_tx_uc_cnt[0]); + + // Print array lmac_tx_uc_cnt + seq_puts(seq, "\tLMAC TX Unicast Count Array: "); + + for (i = 0; i < 5; i++) + seq_printf(seq, "%u ", info->lmac_tx_uc_cnt[i]); + + seq_puts(seq, "\n"); + + // Print hmac_tx_stat + seq_puts(seq, "\tHMAC TX Stat: "); + + for (i = 0; i < 7; i++) + seq_printf(seq, "%u ", info->hmac_tx_stat.hmac_tx_stat[i]); + + seq_puts(seq, "\n"); + + // Print tx_mib_acc_info + seq_printf(seq, "\tAccumulated TX PSDU Count: %u\n", info->tx_mib_acc_info.acc_tx_psdu_cnt); + seq_printf(seq, "\tAccumulated TX With ACK Timeout Count: %u\n", info->tx_mib_acc_info.acc_tx_wi_ack_to_cnt); + seq_printf(seq, "\tAccumulated TX With ACK Fail Count: %u\n", info->tx_mib_acc_info.acc_tx_wi_ack_fail_cnt); + seq_printf(seq, "\tAccumulated RTS Without CTS Count: %u\n", info->tx_mib_acc_info.acc_rts_wo_cts_cnt); + seq_printf(seq, "\tAccumulated TX Post Busy Count: %u\n", info->tx_mib_acc_info.acc_tx_post_busy_cnt); + seq_printf(seq, "\tTX Enable Percentage: %u%%\n", info->tx_mib_acc_info.tx_en_perct); + + // Print tx_mib_tb_acc_info + seq_printf(seq, "\tTB Basic Trigger Count: %u\n", info->tx_mib_tb_acc_info.rx_basic_trig_cnt); + seq_printf(seq, "\tTB Basic Trigger Success Count: %u\n", info->tx_mib_tb_acc_info.rx_basic_trig_succ_cnt); + seq_printf(seq, "\tTB Generate Invoke Count: %u\n", info->tx_mib_tb_acc_info.tb_gen_invoke_cnt); + seq_printf(seq, "\tTB Generate Abort Count: %u\n", info->tx_mib_tb_acc_info.tb_gen_abort_cnt); + seq_printf(seq, "\tTB CS Fail Accumulated Count: %u\n", info->tx_mib_tb_acc_info.tb_cs_fail_acc_cnt); + seq_printf(seq, "\tAll TB Pre TX Count: %u\n", info->tx_mib_tb_acc_info.all_tb_pre_tx_cnt); + seq_printf(seq, "\tTB TX Accumulated Count: %u\n", info->tx_mib_tb_acc_info.tb_tx_acc_cnt); + seq_printf(seq, "\tTB TX Success Accumulated Count: %u\n", info->tx_mib_tb_acc_info.tb_tx_acc_scucc_cnt); + seq_printf(seq, "\tTB TX Success Ratio: %u\n", info->tx_mib_tb_acc_info.tb_tx_suc_ratio); + seq_printf(seq, "\tRX Trigger Register Count: %u\n", info->tx_mib_tb_acc_info.rx_trig_reg_cnt); + seq_printf(seq, "\tRX Trigger Start TX Count: %u\n", info->tx_mib_tb_acc_info.rx_trig_start_tx_cnt); + seq_printf(seq, "\tRX Trigger Success Ratio: %u\n", info->tx_mib_tb_acc_info.rx_trig_suc_ratio); +} + +void skw_debug_print_inst_info(struct seq_file *seq, struct skw_get_inst_info_s *info) +{ + int i = 0; + + if (!info) { + seq_puts(seq, "\tInst Info structure is NULL\n"); + return; + } + + seq_puts(seq, "Inst info:\n\n"); + seq_printf(seq, "\tInst Mode: %u\n", info->inst_mode); + seq_printf(seq, "\tHW ID: %u\n", info->hw_id); + seq_printf(seq, "\tEnable: %u\n", info->enable); + seq_printf(seq, "\tMAC ID: %u\n", info->mac_id); + seq_printf(seq, "\tASOC Peer Map: %u\n", info->asoc_peer_map); + seq_printf(seq, "\tPeer PS State: %u\n", info->peer_ps_state); + + seq_puts(seq, "\tCS Info:\n"); + seq_printf(seq, "\t\tP20 Last Busy Percentage: %u\n", info->cs_info.p20_lst_busy_perctage); + seq_printf(seq, "\t\tP40 Last Busy Percentage: %u\n", info->cs_info.p40_lst_busy_perctage); + seq_printf(seq, "\t\tP80 Last Busy Percentage: %u\n", info->cs_info.p80_lst_busy_perctage); + seq_printf(seq, "\t\tP20 Last NAV Percentage: %u\n", info->cs_info.p20_lst_nav_perctage); + seq_printf(seq, "\t\tP40 Last NAV Percentage: %u\n", info->cs_info.p40_lst_nav_perctage); + seq_printf(seq, "\t\tP80 Last NAV Percentage: %u\n", info->cs_info.p80_lst_nav_perctage); + + seq_puts(seq, "\tRPI Info:\n"); + seq_printf(seq, "\t\tNo UC Direct RPI Level: %d\n", info->rpi_info.no_uc_direct_rpi_level); + seq_printf(seq, "\t\tUC Direct RPI Level: %d\n", info->rpi_info.uc_direct_rpi_level); + seq_printf(seq, "\t\tRX BA RSSI: %d\n", info->rpi_info.rx_ba_rssi); + seq_printf(seq, "\t\tRX Idle Percent in RPI: %u\n", info->rpi_info.rx_idle_percent_in_rpi); + seq_printf(seq, "\t\tRX Percent in RPI: %u\n", info->rpi_info.rx_percent_in_rpi); + + seq_puts(seq, "\t\tRPI Class Duration: "); + + for (i = 0; i < RPI_MAX_LEVEL; i++) + seq_printf(seq, "%u ", info->rpi_info.rpi_class_dur[i]); + + seq_puts(seq, "\n"); + + seq_puts(seq, "\tNoise Info:\n"); + seq_puts(seq, "\t\tNoise Class Duration: "); + + for (i = 0; i < NOISE_MAX_LEVEL; i++) + seq_printf(seq, "%u ", info->noise_info.noise_class_dur[i]); + + seq_puts(seq, "\n"); +} + +void skw_debug_print_peer_info(struct seq_file *seq, struct skw_get_peer_info *peer_info) +{ + int i = 0; + + if (!peer_info) { + seq_puts(seq, "\tPeer Info structure is NULL\n"); + return; + } + + seq_puts(seq, "Peer info:\n\n"); + seq_printf(seq, "\tIs Valid: %u\n", peer_info->is_valid); + seq_printf(seq, "\tInstance ID: %u\n", peer_info->inst_id); + seq_printf(seq, "\tHW ID: %u\n", peer_info->hw_id); + seq_printf(seq, "\tRX RSSI: %d\n", peer_info->rx_rsp_rssi); + seq_printf(seq, "\tAverage ADD Delay (ms): %u\n", peer_info->avg_add_delay_in_ms); + seq_printf(seq, "\tTX Max Delay Time: %u\n", peer_info->tx_max_delay_time); + + // Print TX Hmac Per Peer Report + seq_puts(seq, "\tHMAC TX Stat:\n"); + seq_puts(seq, "\t\t"); + + for (i = 0; i < 5; i++) + seq_printf(seq, "%u ", peer_info->hmac_tx_stat.hmac_tx_stat[i]); + + seq_puts(seq, "\n"); + + seq_printf(seq, "\tTXC ISR Read DSCR FIFO Count: %u\n", + peer_info->hmac_tx_stat.txc_isr_read_dscr_fifo_cnt); + + // Print TX Rate Info + seq_puts(seq, "\tTX Rate Info:\n"); + seq_printf(seq, "\t\tFlags: %u\n", peer_info->tx_rate_info.flags); + seq_printf(seq, "\t\tMCS Index: %u\n", peer_info->tx_rate_info.mcs_idx); + seq_printf(seq, "\t\tLegacy Rate: %u\n", peer_info->tx_rate_info.legacy_rate); + seq_printf(seq, "\t\tNSS: %u\n", peer_info->tx_rate_info.nss); + seq_printf(seq, "\t\tBW: %u\n", peer_info->tx_rate_info.bw); + seq_printf(seq, "\t\tGI: %u\n", peer_info->tx_rate_info.gi); + seq_printf(seq, "\t\tRU: %u\n", peer_info->tx_rate_info.ru); + seq_printf(seq, "\t\tDCM: %u\n", peer_info->tx_rate_info.dcm); + + // Print RX MSDU Info Report + seq_puts(seq, "\tRX MSDU Info:\n"); + seq_printf(seq, "\t\tPPDU Mode: %u\n", peer_info->rx_msdu_info_rpt.ppdu_mode); + seq_printf(seq, "\t\tData Rate: %u\n", peer_info->rx_msdu_info_rpt.data_rate); + seq_printf(seq, "\t\tGI Type: %u\n", peer_info->rx_msdu_info_rpt.gi_type); + seq_printf(seq, "\t\tSBW: %u\n", peer_info->rx_msdu_info_rpt.sbw); + seq_printf(seq, "\t\tDCM: %u\n", peer_info->rx_msdu_info_rpt.dcm); + seq_printf(seq, "\t\tNSS: %u\n", peer_info->rx_msdu_info_rpt.nss); + + // Print TX Debug Stats Per Peer + seq_puts(seq, "\tTX Stats Info:\n"); + seq_printf(seq, "\t\tRTS Fail Count: %u\n", peer_info->tx_stats_info.rts_fail_cnt); + seq_printf(seq, "\t\tPSDU Ack Timeout Count: %u\n", peer_info->tx_stats_info.psdu_ack_timeout_cnt); + seq_printf(seq, "\t\tTX MPDU Count: %u\n", peer_info->tx_stats_info.tx_mpdu_cnt); + seq_printf(seq, "\t\tTX Wait Time: %u\n", peer_info->tx_stats_info.tx_wait_time); + + // Print TX Pending Packet Count + seq_puts(seq, "\tTX Pending Packet Count: "); + + for (i = 0; i < 4; i++) + seq_printf(seq, "%u ", peer_info->tx_pending_pkt_cnt[i]); + + seq_puts(seq, "\n"); +} + +void skw_debug_print_hw_rx_info(struct seq_file *seq, struct skw_get_hw_rx_info *rx_info) +{ + if (!rx_info) { + seq_puts(seq, "\tInfo structure is NULL\n"); + return; + } + + seq_puts(seq, "rx info:\n\n"); + seq_printf(seq, "\tPHY RX All Count: %u\n", rx_info->phy_rx_all_cnt); + seq_printf(seq, "\tPHY RX 11B Count: %u\n", rx_info->phy_rx_11b_cnt); + seq_printf(seq, "\tPHY RX 11G Count: %u\n", rx_info->phy_rx_11g_cnt); + seq_printf(seq, "\tPHY RX 11N Count: %u\n", rx_info->phy_rx_11n_cnt); + seq_printf(seq, "\tPHY RX 11AC Count: %u\n", rx_info->phy_rx_11ac_cnt); + seq_printf(seq, "\tPHY RX 11AX Count: %u\n", rx_info->phy_rx_11ax_cnt); + seq_printf(seq, "\tMAC RX MPDU Count: %u\n", rx_info->mac_rx_mpdu_cnt); + seq_printf(seq, "\tMAC RX MPDU FCS Pass Count: %u\n", rx_info->mac_rx_mpdu_fcs_pass_cnt); + seq_printf(seq, "\tMAC RX MPDU FCS Pass Direct Count: %u\n", rx_info->mac_rx_mpdu_fcs_pass_dir_cnt); +} + +void skw_debug_print_inst_tsf(struct seq_file *seq, struct skw_inst_tsf *inst_tsf) +{ + if (!inst_tsf) { + seq_puts(seq, "\tInst tsf structure is NULL\n"); + return; + } + + seq_puts(seq, "inst tsf:\n\n"); + seq_printf(seq, "\tInst tsf low: %u\n", inst_tsf->tsf_l); + seq_printf(seq, "\tInst tsf high: %u\n", inst_tsf->tsf_h); +} + +void skw_debug_print_winfo(struct seq_file *seq, struct skw_debug_info_w *w_info) +{ + int i, j; + int percent; + + seq_puts(seq, "debug_w info:\n"); + + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + seq_printf(seq, " hw[%d]:\t total[%d]\t other_count:%d\n", + i, w_info->hw_w[i].w_total_cnt, + w_info->hw_w[i].w_cnt[SKW_MAX_W_LEVEL - 1]); + seq_puts(seq, "\n"); + for (j = 0; j < SKW_MAX_W_LEVEL - 1; j++) { + percent = w_info->hw_w[i].w_cnt[j] * 1000 + / w_info->hw_w[i].w_total_cnt; + seq_printf(seq, "\t %d:thresh[%d]\t" + "count:%d\t percent:%d.%d%%\n", j, + w_info->hw_w[i].w_ctrl_thresh[j], + w_info->hw_w[i].w_cnt[j], + percent/10, percent%10); + } + seq_puts(seq, "\n"); + } + + seq_puts(seq, "\n"); + +} + +void skw_debug_print_nss_info(struct seq_file *seq, struct skw_mac_nss_info *nss_info) +{ + int i; + + seq_puts(seq, "nss info:\n"); + seq_printf(seq, + "\t mac0 nss: %d,\tmac1 nss: %d.\t" + " dbdc mode:%d\n\n" + "\t instence num:%d\n", + nss_info->mac_nss[0], + nss_info->mac_nss[1], + nss_info->is_dbdc_mode, + nss_info->max_inst_num); + + for (i = 0; i < nss_info->max_inst_num; i++) + if (nss_info->inst_nss[i].valid_id) + seq_printf(seq, "\t inst[%d]:\t valid:%d\t" + "rx_nss:%d\t tx_nss:%d\n", + i, nss_info->inst_nss[i].valid_id, + nss_info->inst_nss[i].inst_rx_nss, + nss_info->inst_nss[i].inst_tx_nss); + + seq_puts(seq, "\n"); + +} + +int skw_debug_get_hw_tx_info(struct skw_core *skw, u8 *buf, int len) +{ + int ret = -1; + struct skw_tlv *tlv; + struct skw_debug_info_s debug_info_param = {0}; + + debug_info_param.tlv = SKW_MIB_GET_HW_TX_INFO_E; + ret = skw_msg_xmit(priv_to_wiphy(skw), 0, SKW_CMD_GET_DEBUG_INFO, + &debug_info_param, sizeof(struct skw_debug_info_s), + buf, len); + + if (ret) { + skw_warn("failed, ret: %d\n", ret); + return ret; + } + + tlv = (struct skw_tlv *)buf; + if (tlv->type != SKW_MIB_GET_HW_TX_INFO_E || + tlv->len != sizeof(struct skw_get_hw_tx_info_s)) + return -1; + + return 0; +} + +int skw_debug_get_inst_info(struct skw_core *skw, u8 inst_id, u8 *buf, int len) +{ + int ret = -1; + struct skw_tlv *tlv; + struct skw_debug_info_s debug_info_param = {0}; + + debug_info_param.tlv = SKW_MIB_GET_INST_INFO_E; + ret = skw_msg_xmit(priv_to_wiphy(skw), inst_id, SKW_CMD_GET_DEBUG_INFO, + &debug_info_param, sizeof(struct skw_debug_info_s), + buf, len); + + if (ret) { + skw_warn("failed, ret: %d\n", ret); + return ret; + } + + tlv = (struct skw_tlv *)buf; + if (tlv->type != SKW_MIB_GET_INST_INFO_E || + tlv->len != sizeof(struct skw_get_inst_info_s)) + return -1; + + return 0; +} + +int skw_debug_get_peer_info(struct skw_core *skw, u8 peer_idx, u8 *buf, int len) +{ + int ret = -1; + struct skw_tlv *tlv; + struct skw_debug_info_s debug_info_param = {0}; + + debug_info_param.tlv = SKW_MIB_GET_PEER_INFO_E; + debug_info_param.val[0] = peer_idx; + ret = skw_msg_xmit(priv_to_wiphy(skw), 0, SKW_CMD_GET_DEBUG_INFO, + &debug_info_param, sizeof(struct skw_debug_info_s), + buf, len); + + if (ret) { + skw_warn("failed, ret: %d\n", ret); + return ret; + } + + tlv = (struct skw_tlv *)buf; + if (tlv->type != SKW_MIB_GET_PEER_INFO_E || + tlv->len != sizeof(struct skw_get_peer_info)) + return -1; + + return 0; +} + + +int skw_debug_get_hw_rx_info(struct skw_core *skw, u8 *buf, int len) +{ + int ret = -1; + struct skw_tlv *tlv; + struct skw_debug_info_s debug_info_param = {0}; + + debug_info_param.tlv = SKW_MIB_GET_HW_RX_INFO_E; + ret = skw_msg_xmit(priv_to_wiphy(skw), 0, SKW_CMD_GET_DEBUG_INFO, + &debug_info_param, sizeof(struct skw_debug_info_s), + buf, len); + + if (ret) { + skw_warn("failed, ret: %d\n", ret); + return ret; + } + + tlv = (struct skw_tlv *)buf; + if (tlv->type != SKW_MIB_GET_HW_RX_INFO_E || + tlv->len != sizeof(struct skw_get_hw_rx_info)) + return -1; + + return 0; +} + +int skw_debug_get_inst_tsf(struct skw_core *skw, u8 *buf, int len) +{ + int ret = -1; + struct skw_tlv *tlv; + struct skw_debug_info_s debug_info_param = {0}; + + debug_info_param.tlv = SKW_MIB_GET_INST_TSF_E; + ret = skw_msg_xmit(priv_to_wiphy(skw), 0, SKW_CMD_GET_DEBUG_INFO, + &debug_info_param, sizeof(struct skw_debug_info_s), + buf, len); + + if (ret) { + skw_warn("failed, ret: %d\n", ret); + return ret; + } + + tlv = (struct skw_tlv *)buf; + if (tlv->type != SKW_MIB_GET_INST_TSF_E || + tlv->len != sizeof(struct skw_inst_tsf)) + return -1; + + return 0; +} + +int skw_debug_get_winfo(struct skw_core *skw, u8 *buf, int len) +{ + int ret = -1; + struct skw_tlv *tlv; + + struct skw_debug_info_s debug_info_param = {0}; + debug_info_param.tlv = SKW_MIB_W_INFO; + ret = skw_msg_xmit(priv_to_wiphy(skw), 0, SKW_CMD_GET_DEBUG_INFO, + &debug_info_param, sizeof(struct skw_debug_info_s), + buf, len); + + if (ret) { + skw_warn("failed, ret: %d\n", ret); + return ret; + } + + tlv = (struct skw_tlv *)buf; + if (tlv->type != SKW_MIB_W_INFO || + tlv->len != sizeof(struct skw_debug_info_w)) + return -1; + + return 0; +} + +int skw_debug_get_nss_info(struct skw_core *skw, u8 *buf, int len) +{ + int ret = -1; + struct skw_tlv *tlv; + + struct skw_debug_info_s debug_info_param = {0}; + debug_info_param.tlv = SKW_MIB_MAC_NSS_INFO; + ret = skw_msg_xmit(priv_to_wiphy(skw), 0, SKW_CMD_GET_DEBUG_INFO, + &debug_info_param, sizeof(struct skw_debug_info_s), + buf, len); + + if (ret) { + skw_warn("failed, ret: %d\n", ret); + return ret; + } + + tlv = (struct skw_tlv *)buf; + if (tlv->type != SKW_MIB_MAC_NSS_INFO || + tlv->len != sizeof(struct skw_mac_nss_info)) + return -1; + + return 0; +} + +static int skw_debug_info_show(struct seq_file *seq, void *data) +{ + struct skw_core *skw = seq->private; + u8 *tx_info = NULL; + u8 *inst_info = NULL; + u8 *peer_info = NULL; + u8 *rx_info = NULL; + u8 *inst_tsf = NULL; + u8 *nss_buf = NULL; + u8 *w_info = NULL; + int len; + + len = sizeof(struct skw_get_hw_tx_info_s) + sizeof(struct skw_tlv); + tx_info = SKW_ZALLOC(len, GFP_KERNEL); + if (!tx_info) { + skw_err("tx_info failed\n"); + } else { + if (skw_debug_get_hw_tx_info(skw, tx_info, len) == 0) { + seq_puts(seq, "\n"); + skw_debug_print_hw_tx_info(seq, (void *)(tx_info + sizeof(struct skw_tlv))); + seq_puts(seq, "\n"); + } + + SKW_KFREE(tx_info); + } + + len = sizeof(struct skw_get_inst_info_s) + sizeof(struct skw_tlv); + inst_info = SKW_ZALLOC(len, GFP_KERNEL); + if (!inst_info) { + skw_err("inst_info failed\n"); + } else { + if (skw_debug_get_inst_info(skw, 0, inst_info, len) == 0) { + seq_puts(seq, "\n"); + skw_debug_print_inst_info(seq, (void *)(inst_info + sizeof(struct skw_tlv))); + seq_puts(seq, "\n"); + } + + SKW_KFREE(inst_info); + } + + len = sizeof(struct skw_get_peer_info) + sizeof(struct skw_tlv); + peer_info = SKW_ZALLOC(len, GFP_KERNEL); + if (!peer_info) { + skw_err("peer_info failed\n"); + } else { + if (skw_debug_get_peer_info(skw, 0, peer_info, len) == 0) { + seq_puts(seq, "\n"); + skw_debug_print_peer_info(seq, (void *)(peer_info + sizeof(struct skw_tlv))); + seq_puts(seq, "\n"); + } + + SKW_KFREE(peer_info); + } + + len = sizeof(struct skw_get_hw_rx_info) + sizeof(struct skw_tlv); + rx_info = SKW_ZALLOC(len, GFP_KERNEL); + if (!rx_info) { + skw_err("rx_info failed\n"); + } else { + if (skw_debug_get_hw_rx_info(skw, rx_info, len) == 0) { + seq_puts(seq, "\n"); + skw_debug_print_hw_rx_info(seq, (void *)(rx_info + sizeof(struct skw_tlv))); + seq_puts(seq, "\n"); + } + + SKW_KFREE(rx_info); + } + + len = sizeof(struct skw_inst_tsf) + sizeof(struct skw_tlv); + inst_tsf = SKW_ZALLOC(len, GFP_KERNEL); + if (!inst_tsf) { + skw_err("inst_tsf failed\n"); + } else { + if (skw_debug_get_inst_tsf(skw, inst_tsf, len) == 0) { + seq_puts(seq, "\n"); + skw_debug_print_inst_tsf(seq, (void *)(inst_tsf + sizeof(struct skw_tlv))); + seq_puts(seq, "\n"); + } + + SKW_KFREE(inst_tsf); + } + + len = sizeof(struct skw_debug_info_w) + sizeof(struct skw_tlv); + w_info = SKW_ZALLOC(len, GFP_KERNEL); + if (!w_info) { + skw_err("w_info failed\n"); + } else { + if (skw_debug_get_winfo(skw, w_info, len) == 0) { + seq_puts(seq, "\n"); + skw_debug_print_winfo(seq, (void *)(w_info + sizeof(struct skw_tlv))); + seq_puts(seq, "\n"); + } + + SKW_KFREE(w_info); + } + + len = sizeof(struct skw_mac_nss_info) + sizeof(struct skw_tlv); + nss_buf = SKW_ZALLOC(len, GFP_KERNEL); + if (!nss_buf) { + skw_err("nss_buf failed\n"); + } else { + if (skw_debug_get_nss_info(skw, nss_buf, len) == 0) { + seq_puts(seq, "\n"); + skw_debug_print_nss_info(seq, (void *)(nss_buf + sizeof(struct skw_tlv))); + seq_puts(seq, "\n"); + } + + SKW_KFREE(nss_buf); + } + + seq_puts(seq, "\n"); + + return 0; +} + +static int skw_debug_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_debug_info_show, skw_pde_data(inode)); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_core_fops = { + .proc_open = skw_core_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +static const struct proc_ops skw_debug_info_fops = { + .proc_open = skw_debug_info_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_core_fops = { + .owner = THIS_MODULE, + .open = skw_core_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +static const struct file_operations skw_debug_info_fops = { + .owner = THIS_MODULE, + .open = skw_debug_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +static int skw_assert_show(struct seq_file *seq, void *data) +{ + return 0; +} + +static int skw_assert_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_assert_show, inode->i_private); +} + +static ssize_t skw_assert_write(struct file *fp, const char __user *buf, + size_t len, loff_t *offset) +{ + struct skw_core *skw = fp->f_inode->i_private; + + skw_hw_assert(skw, false); + + return len; +} + +static const struct file_operations skw_assert_fops = { + .owner = THIS_MODULE, + .open = skw_assert_open, + .read = seq_read, + .write = skw_assert_write, + .release = single_release, +}; + +static int skw_ndo_open(struct net_device *dev) +{ + struct skw_iface *iface = netdev_priv(dev); + struct skw_core *skw = iface->skw; + + skw_dbg("dev: %s, type: %s\n", netdev_name(dev), + skw_iftype_name(dev->ieee80211_ptr->iftype)); + + if (test_bit(SKW_FLAG_FW_THERMAL, &skw->flags)) { + skw_warn("disable TX for thermal warnning"); + netif_tx_stop_all_queues(dev); + } + + netif_carrier_off(dev); + + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_MONITOR) { + dev->type = ARPHRD_IEEE80211_RADIOTAP; + netif_tx_stop_all_queues(dev); + } else + dev->type = ARPHRD_ETHER; + + return 0; +} + +static int skw_ndo_stop(struct net_device *dev) +{ + struct skw_iface *iface = netdev_priv(dev); + struct skw_core *skw = iface->skw; + + skw_dbg("dev: %s, type: %s\n", netdev_name(dev), + skw_iftype_name(dev->ieee80211_ptr->iftype)); + + //netif_tx_stop_all_queues(dev); + + // fixme: + // check if sched scan is going on current netdev + skw_scan_done(skw, iface, true); + + switch (dev->ieee80211_ptr->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + // if (iface->sta.sm.state != SKW_STATE_NONE) + //skw_disconnect(iface->wdev.wiphy, iface->ndev, + // WLAN_REASON_DEAUTH_LEAVING); + break; + + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + // skw_flush_sta_info(iface); + break; + + case NL80211_IFTYPE_MONITOR: + skw_cmd_monitor(priv_to_wiphy(skw), NULL, SKW_MONITOR_CLOSE); + break; + + default: + break; + } + + return 0; +} + +static bool skw_udp_filter(struct net_device *ndev, struct sk_buff *skb) +{ + bool ret = false; + struct udphdr *udp = udp_hdr(skb); + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 +#define DHCPV6_SERVER_PORT 546 +#define DHCPV6_CLIENT_PORT 547 + + switch (ntohs(udp->dest)) { + case DHCP_CLIENT_PORT: + case DHCP_SERVER_PORT: + if (ndev->priv_flags & IFF_BRIDGE_PORT) { + /* set BOOTP flag to broadcast */ + *((u8 *)udp + 18) = 0x80; + udp->check = 0; + } + + skw_fallthrough; + + case DHCPV6_CLIENT_PORT: + case DHCPV6_SERVER_PORT: + ret = true; + skw_dbg("DHCP, port: %d\n", ntohs(udp->dest)); + break; + + default: + ret = false; + break; + } + + return ret; +} + +static void skw_setup_txba(struct skw_core *skw, struct skw_iface *iface, + struct skw_peer *peer, int tid) +{ + int ret = 0; + struct skw_ba_action tx_ba = {0}; + + if (tid >= SKW_NR_TID) { + skw_warn("tid: %d invalid\n", tid); + return; + } + + if ((peer->txba.bitmap | peer->txba.blacklist) & BIT(tid)) + return; + + tx_ba.tid = tid; + tx_ba.win_size = 64; + tx_ba.lmac_id = iface->lmac_id; + tx_ba.peer_idx = peer->idx; + tx_ba.action = SKW_ADD_TX_BA; + + ret = skw_queue_work(priv_to_wiphy(skw), iface, SKW_WORK_SETUP_TXBA, + &tx_ba, sizeof(tx_ba)); + if (!ret) + peer->txba.bitmap |= BIT(tid); +} + +struct skw_ctx_entry *skw_get_ctx_entry(struct skw_core *skw, const u8 *addr) +{ + int i, j; + struct skw_ctx_entry *entry; + + for (i = 0; i < skw->hw.nr_lmac; i++) { + for (j = 0; j < SKW_MAX_PEER_SUPPORT; j++) { + entry = rcu_dereference(skw->hw.lmac[i].peer_ctx[j].entry); + if (entry && ether_addr_equal(entry->addr, addr)) + return entry; + } + } + + return NULL; +} + +struct skw_peer_ctx *skw_get_ctx(struct skw_core *skw, u8 lmac_id, u8 idx) +{ + if (idx >= SKW_MAX_PEER_SUPPORT) + return NULL; + + return &skw->hw.lmac[lmac_id].peer_ctx[idx]; +} + +static int skw_downgrade_ac(struct skw_iface *iface, int aci) +{ +#if 1 + while (iface->wmm.acm & BIT(aci)) { + if (aci == SKW_WMM_AC_BK) + break; + aci++; + } +#else + if (iface->wmm.acm & BIT(aci)) { + int i; + int acm = SKW_WMM_AC_BK; + + for (i = SKW_WMM_AC_BK; i >= 0; i--) { + if (!(iface->wmm.acm & BIT(i))) { + acm = i; + break; + } + } + + aci = acm; + } +#endif + + return aci; +} + +static inline bool is_skw_tcp_pure_ack(struct sk_buff *skb) +{ + return tcp_hdr(skb)->ack && + ntohs(ip_hdr(skb)->tot_len) == ip_hdrlen(skb) + tcp_hdrlen(skb); +} + +static netdev_tx_t skw_ndo_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + int ret = -1; + u8 tid; + u8 fixed_rate = 0; + u8 fixed_tid = SKW_INVALID_ID; + u8 peer_index = SKW_INVALID_ID; + u8 ac_idx = 0, padding = 0; + u8 prot = 0, tcp_pkt = 0, do_csum = 0; + u32 filter_map = 0, data_prot_map = 0; + bool is_prot_filter = false; + bool is_udp_filter = false; + bool is_802_3_frame = false; + bool is_tx_filter = false; + bool pure_tcp_ack = false; + bool need_desc_hdr = false; + const u8 tid_map[] = {6, 4, 0, 1}; + int l4_hdr_offset = 0, reset_l4_offset = 0; + int msdu_len, txq_len; + int nhead, ntail, hdr_size; + bool is_completed = true; + + struct netdev_queue *txq; + struct skw_peer_ctx *ctx; + struct skw_iface *iface = netdev_priv(ndev); + struct ethhdr *eth = eth_hdr(skb); + struct skw_ctx_entry *entry = NULL; + struct sk_buff_head *qlist; + struct skw_tx_desc_hdr *desc_hdr; + struct skw_tx_desc_conf *desc_conf; + struct skw_core *skw = iface->skw; +#ifdef CONFIG_SWT6621S_SKB_RECYCLE + struct sk_buff *skb_recycle; +#endif + s16 pkt_limit; + + __net_timestamp(skb); + + /* Mini frame size that HW support */ + if (unlikely(skb->len <= 16)) { + skw_dbg("current: %s\n", current->comm); + skw_hex_dump("short skb", skb->data, skb->len, true); + + if (skb->len != ETH_HLEN || eth->h_proto != htons(ETH_P_IP)) + SKW_BUG_ON(1); + + goto free; + } + + if (skb_linearize(skb)) + goto free; + + msdu_len = skb->len; + SKW_SKB_TXCB(skb)->skb_native_len = skb->len; + SKW_SKB_TXCB(skb)->ret = 0; + SKW_SKB_TXCB(skb)->recycle = 0; + if (skb->ip_summed == CHECKSUM_PARTIAL) { + l4_hdr_offset = skb_checksum_start_offset(skb); + do_csum = 1; + } + + switch (eth->h_proto) { + case htons(ETH_P_IP): + prot = ip_hdr(skb)->protocol; + reset_l4_offset = ETH_HLEN + ip_hdrlen(skb); + + if (prot == IPPROTO_TCP) + pure_tcp_ack = is_skw_tcp_pure_ack(skb); + + break; + + case htons(ETH_P_IPV6): + prot = ipv6_hdr(skb)->nexthdr; + // fixme: + // get tcp/udp head offset + reset_l4_offset = ETH_HLEN + sizeof(struct ipv6hdr); + break; + + case htons(ETH_P_ARP): + if (test_bit(SKW_FLAG_FW_FILTER_ARP, &skw->flags)) + is_prot_filter = true; + + if (unlikely(test_bit(SKW_FLAG_REPEATER, &skw->flags)) && + ndev->priv_flags & IFF_BRIDGE_PORT) { + if (iface->wdev.iftype == NL80211_IFTYPE_STATION) { + struct sk_buff *arp_skb; + struct skw_ctx_entry *e; + struct skw_arphdr *arp = skw_arp_hdr(skb); + + rcu_read_lock(); + e = skw_get_ctx_entry(iface->skw, arp->ar_sha); + if (e) + e->peer->ip_addr = arp->ar_sip; + + rcu_read_unlock(); + + arp_skb = arp_create(ntohs(arp->ar_op), + ETH_P_ARP, arp->ar_tip, + iface->ndev, + arp->ar_sip, eth->h_dest, + iface->addr, arp->ar_tha); + + kfree_skb(skb); + + skb = arp_skb; + if (!skb) + return NETDEV_TX_OK; + + eth = skw_eth_hdr(skb); + } + } + + if (is_skw_sta_mode(iface)) + fixed_rate = 1; + //fixed_tid = 4; + break; + + case htons(ETH_P_PAE): + data_prot_map |= (1 << SKW_MSDU_FILTER_EAP); + is_prot_filter = true; + break; + + case htons(SKW_ETH_P_WAPI): + is_prot_filter = true; + data_prot_map |= (1 << SKW_MSDU_FILTER_WAPI); + break; + + case htons(ETH_P_TDLS): + is_prot_filter = true; + break; + + default: + if (eth->h_proto < ETH_P_802_3_MIN) + is_802_3_frame = true; + + break; + } + + if (!do_csum && (prot == IPPROTO_UDP || prot == IPPROTO_TCP)) + skb_set_transport_header(skb, reset_l4_offset); + + // fixme: + /* enable checksum for TCP & UDP frame, except framgment frame */ + switch (prot) { + case IPPROTO_UDP: + is_udp_filter = skw_udp_filter(ndev, skb); + if (udp_hdr(skb)->check == 0) + do_csum = 0; + + break; + + case IPPROTO_TCP: + tcp_pkt = 1; + + break; + + default: + break; + } + + ac_idx = skw_downgrade_ac(iface, g_skw_up_to_ac[skb->priority]); + tid = (fixed_tid != SKW_INVALID_ID) ? fixed_tid : tid_map[ac_idx]; + + rcu_read_lock(); + + switch (iface->wdev.iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (is_unicast_ether_addr(eth->h_dest)) { + entry = skw_get_ctx_entry(skw, eth->h_dest); + peer_index = entry ? entry->idx : SKW_INVALID_ID; + + if (entry && entry->peer->sm.state != SKW_STATE_COMPLETED) + is_completed = false; + } else { + fixed_rate = 1; + peer_index = iface->default_multicast; + } + + break; + + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_ADHOC: + + if (atomic_read(&iface->actived_ctx) > 1) + entry = skw_get_ctx_entry(skw, eth->h_dest); + + if (!entry) { + ctx = skw_get_ctx(skw, iface->lmac_id, iface->sta.core.bss.ctx_idx); + if (ctx) + entry = rcu_dereference(ctx->entry); + } + + peer_index = entry ? entry->idx : SKW_INVALID_ID; + + if (peer_index != SKW_INVALID_ID && + is_multicast_ether_addr(eth->h_dest)) { + peer_index = iface->default_multicast; + + if (iface->sta.core.sm.state != SKW_STATE_COMPLETED) { + + rcu_read_unlock(); + + skw_dbg("%s drop dst: %pm, proto: 0x%x, state: %s\n", + netdev_name(ndev), eth->h_dest, htons(eth->h_proto), + skw_state_name(iface->sta.core.sm.state)); + + goto free; + } + } + + if (iface->sta.core.sm.state != SKW_STATE_COMPLETED) + is_completed = false; + + break; + + default: + peer_index = SKW_INVALID_ID; + break; + } + + if (entry) { + if(is_completed) + skw_setup_txba(skw, iface, entry->peer, tid); + + if (entry->peer) { + filter_map = atomic_read(&entry->peer->rx_filter); + + if (filter_map && !(filter_map & data_prot_map)) { + + rcu_read_unlock(); + + skw_dbg("%s drop dst: %pm, proto: 0x%x, filter: 0x%x\n", + netdev_name(ndev), eth->h_dest, + htons(eth->h_proto), filter_map); + + goto free; + } + } + } + + rcu_read_unlock(); + + if (peer_index == SKW_INVALID_ID) { + skw_dbg("%s drop dst: %pM, proto: 0x%x\n", + netdev_name(ndev), eth->h_dest, htons(eth->h_proto)); + + goto free; + } + + is_tx_filter = (is_prot_filter || is_udp_filter || is_802_3_frame); + +#ifdef CONFIG_SWT6621S_SKB_RECYCLE + if (!is_prot_filter && !is_udp_filter && !is_802_3_frame && !is_multicast_ether_addr(eth->h_dest)) { + skb_recycle = skw_recycle_skb_get(skw); + if (skb_recycle) { + if (!skw_recycle_skb_copy(skw, skb_recycle, skb)) { + dev_kfree_skb_any(skb); + skb = skb_recycle; + eth = skw_eth_hdr(skb); + SKW_SKB_TXCB(skb)->recycle = 1; + } else { + skw_err("skw_recycle_skb_copy failed\n"); + skw_recycle_skb_free(skw, skb_recycle); + } + } else { + skb_recycle = skb_copy(skb, GFP_ATOMIC); + if (skb_recycle) { + dev_kfree_skb_any(skb); + skb = skb_recycle; + eth = skw_eth_hdr(skb); + } + } + } +#endif + +#define SKW_EXPAND_SIZE(x, y) ((x) > (y) ? (x) - (y) : 0) + hdr_size = sizeof(struct skw_tx_desc_conf); + + if (skw->hw.bus == SKW_BUS_PCIE) { + SKW_SKB_TXCB(skb)->lmac_id = iface->lmac_id; + SKW_SKB_TXCB(skb)->e.eth_type = eth->h_proto; + SKW_SKB_TXCB(skb)->e.mac_id = iface->id; + SKW_SKB_TXCB(skb)->e.tid = tid; + SKW_SKB_TXCB(skb)->e.peer_idx = peer_index; + SKW_SKB_TXCB(skb)->e.prot = SKW_ETHER_FRAME; + SKW_SKB_TXCB(skb)->e.encry_dis = 0; + SKW_SKB_TXCB(skb)->e.rate = fixed_rate; + SKW_SKB_TXCB(skb)->e.msdu_len = msdu_len; + + padding = 0; + ntail = 0; + + if (is_tx_filter) { + need_desc_hdr = true; + hdr_size += sizeof(struct skw_tx_desc_hdr); + } + + nhead = SKW_EXPAND_SIZE(hdr_size, skb_headroom(skb)); + } else { + int total_len; + + need_desc_hdr = true; + + hdr_size += sizeof(struct skw_tx_desc_hdr) + SKW_EXTER_HDR_SIZE; + + if (SKW_DATA_ALIGN_SIZE > 4) { + unsigned char *ptr = NULL; + struct sk_buff *pskb = NULL; + + total_len = skb->len + hdr_size + SKW_DATA_ALIGN_SIZE; + pskb = netdev_alloc_skb(skb->dev, ALIGN(total_len, skw->hw.align)); + if (!pskb) + goto free; + + ptr = PTR_ALIGN(pskb->data, SKW_DATA_ALIGN_SIZE); + skb_reserve(pskb, ptr - pskb->data + hdr_size); + skw_put_skb_data(pskb, skb->data, skb->len); + eth = skw_eth_hdr(pskb); + + dev_kfree_skb_any(skb); + skb = pskb; + + nhead = ntail = 0; + } else { + padding = ((long)(skb->data + hdr_size)) & SKW_DATA_ALIGN_MASK; + nhead = SKW_EXPAND_SIZE(hdr_size + padding, skb_headroom(skb)); + + total_len = hdr_size + skb->len + padding; + ntail = ALIGN(total_len, skw->hw.align) - total_len; + ntail = SKW_EXPAND_SIZE(ntail, skb_tailroom(skb)); + } + } +#undef SKW_EXPAND_SIZE + + if (nhead || ntail || skb_cloned(skb)) { + if (unlikely(pskb_expand_head(skb, nhead, ntail, GFP_ATOMIC))) { + skw_dbg("failed, nhead: %d, ntail: %d\n", nhead, ntail); + goto free; + } + + eth = skw_eth_hdr(skb); + } + + desc_conf = (void *)skb_push(skb, sizeof(*desc_conf)); + desc_conf->csum = do_csum; + desc_conf->ip_prot = tcp_pkt; + desc_conf->l4_hdr_offset = l4_hdr_offset; + + if (padding) + skb_push(skb, padding); + + if (need_desc_hdr) { + desc_hdr = (void *)skb_push(skb, sizeof(*desc_hdr)); + + desc_hdr->padding_gap = padding; + desc_hdr->inst = iface->id & 0x3; + desc_hdr->lmac_id = iface->lmac_id; + desc_hdr->tid = tid; + desc_hdr->peer_lut = peer_index; + desc_hdr->frame_type = SKW_ETHER_FRAME; + desc_hdr->encry_dis = 0; + desc_hdr->msdu_len = msdu_len; + desc_hdr->rate = fixed_rate; + + skw_set_tx_desc_eth_type(desc_hdr, eth->h_proto); + } + + if (unlikely(is_tx_filter)) { + skw_dbg("proto: 0x%x, udp filter: %d, 802.3 frame: %d\n", + htons(eth->h_proto), is_udp_filter, is_802_3_frame); + + ret = skw_msg_try_send(skw, iface->id, SKW_CMD_TX_DATA_FRAME, + skb->data, skb->len, NULL, 0, + "SKW_CMD_TX_DATA_FRAME"); + if (ret < 0) { + if (SKW_SKB_TXCB(skb)->tx_retry++ > 3) { + skw_queue_work(priv_to_wiphy(skw), iface, + SKW_WORK_TX_ETHER_DATA, + skb->data, skb->len); + } else { + skb_pull(skb, skb->len - msdu_len); + return NETDEV_TX_BUSY; + } + } + + goto free; + } + + SKW_SKB_TXCB(skb)->ret = 0; + SKW_SKB_TXCB(skb)->peer_idx = peer_index; + + if (pure_tcp_ack) + ac_idx = SKW_ACK_TXQ; + + qlist = &iface->txq[ac_idx]; + + skb_queue_tail(qlist, skb); + + if (skw->hw.bus == SKW_BUS_PCIE) + pkt_limit = TX_BUF_ADDR_CNT / 2; + else + pkt_limit = 15; + + if (skw_get_hw_credit(skw, iface->lmac_id) == 0) + goto _ok; + + if (prot == IPPROTO_UDP || prot == IPPROTO_TCP) { + if (skb_queue_len(qlist) < pkt_limit) { + if (!timer_pending(&skw->tx_worker.timer)) + skw_wakeup_tx(skw, msecs_to_jiffies(1)); + } else + skw_wakeup_tx(skw, 0); + } else + skw_wakeup_tx(skw, 0); + +_ok: + trace_skw_tx_xmit(eth->h_dest, peer_index, prot, fixed_rate, + do_csum, ac_idx, skb_queue_len(qlist)); + + txq_len = skb_queue_len(qlist) + skb_queue_len(&iface->tx_cache[ac_idx]); + if (txq_len >= SKW_TXQ_HIGH_THRESHOLD) { + txq = netdev_get_tx_queue(ndev, ac_idx); + if (!netif_tx_queue_stopped(txq)) + netif_tx_stop_queue(txq); + } + + return NETDEV_TX_OK; + +free: + if (ret != 0) + ndev->stats.tx_dropped++; + + dev_kfree_skb_any(skb); + + return NETDEV_TX_OK; +} + +static u16 skw_ndo_select(struct net_device *dev, struct sk_buff *skb +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) + , struct net_device *sb_dev +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + , struct net_device *sb_dev, + select_queue_fallback_t fallback +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + , void *accel_priv, + select_queue_fallback_t fallback +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + , void *accel_priv +#endif + SKW_NULL) +{ + struct skw_iface *iface = netdev_priv(dev); + + if (!iface->wmm.qos_enabled) { + skb->priority = 0; + return SKW_WMM_AC_BE; + } + + if(!skb->protocol && skb->len > ETH_HLEN) { + skb->protocol = eth_type_trans(skb, iface->ndev); + skb_push(skb, ETH_HLEN); + } + + skb->priority = skw_compat_classify8021d(skb, iface->qos_map); + + return g_skw_up_to_ac[skb->priority]; +} + +#ifdef CONFIG_SWT6621S_PRESUSPEND_SUPPORT +static int skw_set_suspendmode(struct wiphy *wiphy, struct net_device *dev, char *presuspend) +{ + int ret; + u8 suspendmode; + + if (presuspend[0] == '1') { + suspendmode = true; + } else if(presuspend[0] == '0') { + suspendmode = false; + } else { + skw_err("failed, invalid param %c\n", presuspend[0]); + return -1; + } + + skw_dbg("set_suspendmode: %d\n", suspendmode); + + return skw_util_set_mib_enable(wiphy, 0, + SKW_MIB_SET_SUSPEND_MODE, suspendmode); + + return ret; +} +#endif + +#define SKW_ANDROID_CMD_LEN 128 +static int skw_android_cmd(struct net_device *dev, void __user *priv_data) +{ + char command[SKW_ANDROID_CMD_LEN], *data = NULL; + struct android_wifi_priv_cmd priv_cmd; + bool compat_task = false; + int ret = 0; + + if (!priv_data) + return -EINVAL; + +#ifdef CONFIG_COMPAT + if (SKW_IS_COMPAT_TASK()) { + struct compat_android_wifi_priv_cmd compat; + + if (copy_from_user(&compat, priv_data, sizeof(compat))) + return -EFAULT; + + priv_cmd.buf = compat_ptr(compat.buf); + priv_cmd.used_len = compat.used_len; + priv_cmd.total_len = compat.total_len; + + compat_task = true; + } +#endif + + if (!compat_task && + copy_from_user(&priv_cmd, priv_data, sizeof(priv_cmd))) + return -EFAULT; + + if (copy_from_user(command, priv_cmd.buf, sizeof(command))) + return -EFAULT; + + command[SKW_ANDROID_CMD_LEN - 1] = 0; + + skw_dbg("%s: %s\n", netdev_name(dev), command); + +#define IS_SKW_CMD(c, k) \ + !strncasecmp(c, SKW_ANDROID_PRIV_##k, strlen(SKW_ANDROID_PRIV_##k)) + +#define SKW_CMD_DATA(c, k) \ + (c + strlen(SKW_ANDROID_PRIV_##k) + 1) + +#define SKW_CMD_DATA_LEN(c, k) \ + (sizeof(command) - strlen(SKW_ANDROID_PRIV_##k) - 1) + + if (IS_SKW_CMD(command, COUNTRY)) { + data = SKW_CMD_DATA(command, COUNTRY); + ret = skw_set_regdom(dev->ieee80211_ptr->wiphy, data); + if (ret) + skw_err("skw_set_regdom failed, ret: %d\n", ret); + } else if (IS_SKW_CMD(command, BTCOEXSCAN_STOP)) { + } else if (IS_SKW_CMD(command, RXFILTER_START)) { + } else if (IS_SKW_CMD(command, RXFILTER_STOP)) { + } else if (IS_SKW_CMD(command, RXFILTER_ADD)) { + } else if (IS_SKW_CMD(command, RXFILTER_REMOVE)) { + } else if (IS_SKW_CMD(command, SETSUSPENDMODE)) { +#ifdef CONFIG_SWT6621S_PRESUSPEND_SUPPORT + data = SKW_CMD_DATA(command, SETSUSPENDMODE); + if (skw_set_suspendmode(dev->ieee80211_ptr->wiphy, dev, data)) + skw_err("skw_set_suspend mode failed\n"); +#endif + } else if (IS_SKW_CMD(command, BTCOEXMODE)) { + } else if (IS_SKW_CMD(command, SET_AP_WPS_P2P_IE)) { + } else { + skw_info("Unsupport cmd: %s - ignored\n", command); + } + +#undef IS_SKW_CMD + + return 0; +} + +static int skw_ioctl(struct net_device *dev, void __user *user_data) +{ + int ret = -ENOTSUPP; + char country[4] = {0}; + struct skw_ioctl_cmd cmd; + + if (copy_from_user(&cmd, user_data, sizeof(cmd))) + return -ENOMEM; + + switch (cmd.id) { + case SKW_IOCTL_SUBCMD_COUNTRY: + + if (cmd.len > sizeof(country)) + return -EINVAL; + + if (copy_from_user(country, user_data + sizeof(cmd), cmd.len)) + return -EINVAL; + + if (strlen(country) != 2) + return -EINVAL; + + ret = skw_set_regdom(dev->ieee80211_ptr->wiphy, country); + if (ret) + skw_err("set regdom failed, ret: %d\n", ret); + + break; + + default: + skw_warn("unsupport cmd: 0x%x, len: %d\n", cmd.id, cmd.len); + break; + } + + return ret; +} + +static int skw_ndo_ioctl(struct net_device *dev, struct ifreq *ifr, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + void __user *data, +#endif + int cmd) +{ + int ret; + void __user *priv = ifr->ifr_data; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + priv = data; +#endif + + switch (cmd) { + case SKW_IOCTL_ANDROID_CMD: + ret = skw_android_cmd(dev, priv); + break; + + case SKW_IOCTL_CMD: + ret = skw_ioctl(dev, priv); + break; + + default: + ret = -ENOTSUPP; + skw_warn("%s, unsupport cmd: 0x%x\n", netdev_name(dev), cmd); + + break; + } + + return ret; +} + +static void skw_ndo_tx_timeout(struct net_device *dev +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + , unsigned int txqueue +#endif + SKW_NULL) +{ + struct skw_iface *iface = (struct skw_iface *)netdev_priv(dev); + struct skw_core *skw; + + if (!iface) { + skw_err("iface NULL\n"); + return; + } + + skw = iface->skw; + + skw_warn("ndev:%s,flag:0x%lx\n", netdev_name(dev), skw->flags); +} + +static void skw_ndo_set_rx_mode(struct net_device *dev) +{ + int count, total_len; + struct skw_mc_list *mc; + struct netdev_hw_addr *ha; + + skw_dbg("%s, mc: %d, uc: %d\n", netdev_name(dev), + netdev_mc_count(dev), netdev_uc_count(dev)); + + count = netdev_mc_count(dev); + if (!count) + return; + + total_len = sizeof(*mc) + sizeof(struct mac_address) * count; + mc = SKW_ZALLOC(total_len, GFP_ATOMIC); + if (!mc) { + skw_err("alloc failed, mc count: %d, total_len: %d\n", + count, total_len); + return; + } + + mc->count = count; + + count = 0; + netdev_for_each_mc_addr(ha, dev) + skw_ether_copy(mc->mac[count++].addr, ha->addr); + + skw_queue_work(dev->ieee80211_ptr->wiphy, netdev_priv(dev), + SKW_WORK_SET_MC_ADDR, mc, total_len); + + SKW_KFREE(mc); +} +#if 0 +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) +static struct rtnl_link_stats64 * +#else +static void +#endif +skw_ndo_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + int i; + + for_each_possible_cpu(i) { + const struct pcpu_sw_netstats *tstats; + u64 rx_packets, rx_bytes, tx_packets, tx_bytes; + unsigned int start; + + tstats = per_cpu_ptr(dev->tstats, i); + + do { + start = u64_stats_fetch_begin_irq(&tstats->syncp); + rx_packets = tstats->rx_packets; + tx_packets = tstats->tx_packets; + rx_bytes = tstats->rx_bytes; + tx_bytes = tstats->tx_bytes; + } while (u64_stats_fetch_retry_irq(&tstats->syncp, start)); + + stats->rx_packets += rx_packets; + stats->tx_packets += tx_packets; + stats->rx_bytes += rx_bytes; + stats->tx_bytes += tx_bytes; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) + return stats; +#endif +} +#endif + +static int skw_ndo_set_mac_address(struct net_device *dev, void *addr) +{ + struct skw_iface *iface = (struct skw_iface *)netdev_priv(dev); + struct sockaddr *sa = addr; + int ret = 0, err = 0; + + skw_dbg("mac: %pM\n", sa->sa_data); + + ret = eth_mac_addr(dev, sa); + if (ret) { + skw_err("failed, addr: %pM, ret: %d\n", sa->sa_data, ret); + return ret; + } + + ret = skw_send_msg(iface->wdev.wiphy, dev, SKW_CMD_RANDOM_MAC, + sa->sa_data, ETH_ALEN, NULL, 0); + if (ret) { + skw_err("set mac: %pM failed, ret: %d\n", + sa->sa_data, ret); + + err = eth_mac_addr(dev, iface->addr); + if (err) + skw_warn("set eth macaddr failed\n"); + + return ret; + } + + skw_ether_copy(iface->addr, sa->sa_data); + + return 0; +} + +#if 0 +static void skw_ndo_uninit(struct net_device *ndev) +{ + struct skw_iface *iface = netdev_priv(ndev); + struct wiphy *wiphy = ndev->ieee80211_ptr->wiphy; + + skw_dbg("%s\n", netdev_name(ndev)); + + free_percpu(ndev->tstats); + + skw_del_vif(wiphy, iface); + skw_iface_teardown(wiphy, iface); + skw_release_inst(wiphy, iface->id); +} +#endif + +static const struct net_device_ops skw_netdev_ops = { + // .ndo_uninit = skw_ndo_uninit, + .ndo_open = skw_ndo_open, + .ndo_stop = skw_ndo_stop, + .ndo_start_xmit = skw_ndo_xmit, + .ndo_select_queue = skw_ndo_select, + .ndo_tx_timeout = skw_ndo_tx_timeout, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + .ndo_siocdevprivate = skw_ndo_ioctl, +#else + .ndo_do_ioctl = skw_ndo_ioctl, +#endif + //.ndo_get_stats64 = skw_ndo_get_stats64, + .ndo_set_rx_mode = skw_ndo_set_rx_mode, + .ndo_set_mac_address = skw_ndo_set_mac_address, +}; + +int skw_netdev_init(struct wiphy *wiphy, struct net_device *ndev, u8 *addr) +{ + struct skw_core *skw; + struct skw_iface *iface; + + if (!ndev) + return -EINVAL; + + skw = wiphy_priv(wiphy); + iface = netdev_priv(ndev); + SET_NETDEV_DEV(ndev, wiphy_dev(wiphy)); + + ndev->features = NETIF_F_GRO | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM; + + ndev->ieee80211_ptr = &iface->wdev; + ndev->netdev_ops = &skw_netdev_ops; + ndev->watchdog_timeo = 3 * HZ; + ndev->needed_headroom = skw->skb_headroom; + ndev->needed_tailroom = skw->hw_pdata->align_value; + // ndev->priv_destructor = skw_netdev_deinit; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + ndev->destructor = free_netdev; +#else + ndev->needs_free_netdev = true; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + eth_hw_addr_set(ndev, addr); +#else + skw_ether_copy(ndev->dev_addr, addr); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) + ndev->tstats = netdev_alloc_pcpu_stats(struct pcpu_tstats); +#else + ndev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); +#endif + if (!ndev->tstats) + return -ENOMEM; + +#ifdef CONFIG_WIRELESS_EXT + ndev->wireless_handlers = skw_iw_handlers(); +#endif + +#ifdef CONFIG_RPS + iface->cpu_id = skw->isr_cpu_id; + skw_init_rps_map(iface); +#endif + + return 0; +} + +void skw_netdev_deinit(struct net_device *ndev) +{ + free_percpu(ndev->tstats); +} + +#if 0 +void skw_netdev_deinit(struct net_device *dev) +{ + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("%s\n", netdev_name(dev)); + +#if 0 //move these actions to ndo_uninit + free_percpu(dev->tstats); + skw_cmd_close_dev(iface->wdev.wiphy, iface->id); + skw_del_vif(iface->skw, iface); + skw_iface_teardown(iface); +#endif +} +#endif + +int skw_sync_cmd_event_version(struct wiphy *wiphy) +{ + int i, ret = 0; + struct skw_version_info *skw_fw_ver; + +#define SKW_CMD_VER(id, ver) .cmd[id] = ver +#define SKW_EVENT_VER(id, ver) .event[id] = ver + const struct skw_version_info skw_drv_ver = { + SKW_CMD_VER(SKW_CMD_DOWNLOAD_INI, V1), + SKW_CMD_VER(SKW_CMD_GET_INFO, V1), + SKW_CMD_VER(SKW_CMD_SYN_VERSION, V1), + SKW_CMD_VER(SKW_CMD_OPEN_DEV, V1), + SKW_CMD_VER(SKW_CMD_CLOSE_DEV, V1), + SKW_CMD_VER(SKW_CMD_START_SCAN, V1), + SKW_CMD_VER(SKW_CMD_STOP_SCAN, V1), + SKW_CMD_VER(SKW_CMD_START_SCHED_SCAN, V1), + SKW_CMD_VER(SKW_CMD_STOP_SCHED_SCAN, V1), + SKW_CMD_VER(SKW_CMD_JOIN, V1), + SKW_CMD_VER(SKW_CMD_AUTH, V1), + SKW_CMD_VER(SKW_CMD_ASSOC, V1), + SKW_CMD_VER(SKW_CMD_ADD_KEY, V1), + SKW_CMD_VER(SKW_CMD_DEL_KEY, V1), + SKW_CMD_VER(SKW_CMD_TX_MGMT, V1), + SKW_CMD_VER(SKW_CMD_TX_DATA_FRAME, V1), + SKW_CMD_VER(SKW_CMD_SET_IP, V1), + SKW_CMD_VER(SKW_CMD_DISCONNECT, V1), + SKW_CMD_VER(SKW_CMD_RPM_REQ, V1), + SKW_CMD_VER(SKW_CMD_START_AP, V1), + SKW_CMD_VER(SKW_CMD_STOP_AP, V1), + SKW_CMD_VER(SKW_CMD_ADD_STA, V1), + SKW_CMD_VER(SKW_CMD_DEL_STA, V1), + SKW_CMD_VER(SKW_CMD_GET_STA, V1), + SKW_CMD_VER(SKW_CMD_RANDOM_MAC, V1), + SKW_CMD_VER(SKW_CMD_GET_LLSTAT, V1), + SKW_CMD_VER(SKW_CMD_SET_MC_ADDR, V1), + SKW_CMD_VER(SKW_CMD_RESUME, V1), + SKW_CMD_VER(SKW_CMD_SUSPEND, V1), + SKW_CMD_VER(SKW_CMD_REMAIN_ON_CHANNEL, V1), + SKW_CMD_VER(SKW_CMD_BA_ACTION, V1), + SKW_CMD_VER(SKW_CMD_TDLS_MGMT, V1), + SKW_CMD_VER(SKW_CMD_TDLS_OPER, V1), + SKW_CMD_VER(SKW_CMD_TDLS_CHANNEL_SWITCH, V1), + SKW_CMD_VER(SKW_CMD_SET_CQM_RSSI, V1), + SKW_CMD_VER(SKW_CMD_NPI_MSG, V1), + SKW_CMD_VER(SKW_CMD_IBSS_JOIN, V1), + SKW_CMD_VER(SKW_CMD_SET_IBSS_ATTR, V1), + SKW_CMD_VER(SKW_CMD_RSSI_MONITOR, V1), + SKW_CMD_VER(SKW_CMD_SET_IE, V1), + SKW_CMD_VER(SKW_CMD_SET_MIB, V1), + SKW_CMD_VER(SKW_CMD_REGISTER_FRAME, V1), + SKW_CMD_VER(SKW_CMD_ADD_TX_TS, V1), + SKW_CMD_VER(SKW_CMD_DEL_TX_TS, V1), + SKW_CMD_VER(SKW_CMD_REQ_CHAN_SWITCH, V1), + SKW_CMD_VER(SKW_CMD_CHANGE_BEACON, V1), + SKW_CMD_VER(SKW_CMD_DPD_ILC_GEAR_PARAM, V1), + SKW_CMD_VER(SKW_CMD_DPD_ILC_MARTIX_PARAM, V1), + SKW_CMD_VER(SKW_CMD_DPD_ILC_COEFF_PARAM, V1), + SKW_CMD_VER(SKW_CMD_WIFI_RECOVER, V1), + SKW_CMD_VER(SKW_CMD_PHY_BB_CFG, V1), + SKW_CMD_VER(SKW_CMD_SET_REGD, V1), + SKW_CMD_VER(SKW_CMD_SET_EFUSE, V1), + SKW_CMD_VER(SKW_CMD_SET_PROBEREQ_FILTER, V1), + SKW_CMD_VER(SKW_CMD_CFG_ANT, V1), + SKW_CMD_VER(SKW_CMD_RTT, V1), + SKW_CMD_VER(SKW_CMD_GSCAN, V1), + SKW_CMD_VER(SKW_CMD_DFS, V1), + SKW_CMD_VER(SKW_CMD_SET_SPD_ACTION, V1), + SKW_CMD_VER(SKW_CMD_SET_DPD_RESULT, V1), + SKW_CMD_VER(SKW_CMD_SET_MONITOR_PARAM, V1), + + /* event */ + }; + + skw_fw_ver = SKW_ZALLOC(sizeof(struct skw_version_info), GFP_KERNEL); + if (!skw_fw_ver) + return -ENOMEM; + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SYN_VERSION, + NULL, 0, skw_fw_ver, + sizeof(struct skw_version_info)); + if (ret) { + skw_err("ret: %d\n", ret); + SKW_KFREE(skw_fw_ver); + + return ret; + } + + for (i = 0; i < SKW_MAX_MSG_ID; i++) { + if (skw_fw_ver->cmd[i] == 0 || skw_drv_ver.cmd[i] == 0) + continue; + + if (skw_drv_ver.cmd[i] != skw_fw_ver->cmd[i]) { + skw_warn("cmd: %d, drv ver: %d, fw ver: %d\n", + i, skw_drv_ver.cmd[i], skw_fw_ver->cmd[i]); + + ret = -EINVAL; + } + } + + SKW_KFREE(skw_fw_ver); + + return ret; + +} + +static int skw_set_capa(struct skw_core *skw, int capa) +{ + int idx, bit; + int size = sizeof(skw->ext_capa); + + idx = capa / BITS_PER_BYTE; + bit = capa % BITS_PER_BYTE; + + BUG_ON(idx >= size); + + skw->ext_capa[idx] |= BIT(bit); + + return 0; +} + +static int skw_set_ext_capa(struct skw_core *skw, struct skw_chip_info *chip) +{ + skw_set_capa(skw, SKW_EXT_CAPA_BSS_TRANSITION); + skw_set_capa(skw, SKW_EXT_CAPA_MBSSID); + skw_set_capa(skw, SKW_EXT_CAPA_TDLS_SUPPORT); + skw_set_capa(skw, SKW_EXT_CAPA_TWT_REQ_SUPPORT); + + return 0; +} + +static const char *skw_get_chip_id(u32 chip_type) +{ + switch (chip_type) { + case 0x100: + return "EA6621Q"; + + case 0x101: + return "EA6521QF"; + + case 0x102: + return "EA6621QT"; + + case 0x103: + return "EA6521QT"; + + case 0x200: + return "EA6316"; + + case 0x300: + return "SWT6621S"; + + default: + skw_err("Unsupport chip type: 0x%x\n", chip_type); + break; + } + + return NULL; +} + +static int skw_set_link_loss_mib(struct wiphy *wiphy, u8 to) +{ + int ret; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("beacon to: %d\n", to); + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_LINK_LOSS_THOLD, &to, 1)) { + skw_err("set link loss failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + *plen = conf.total_len; + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_warn("failed, ret: %d\n", ret); + + skw_tlv_free(&conf); + + return ret; +} + +static int skw_set_24ghz_band_mib(struct wiphy *wiphy, u32 band) +{ + int ret; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("24ghz_band: %d\n", band); + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_BAND_2G, &band, sizeof(band))) { + skw_err("set 24ghz band failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + *plen = conf.total_len; + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_warn("failed, ret: %d\n", ret); + + skw_tlv_free(&conf); + + return ret; +} + +static int skw_set_hdk_test_mib(struct wiphy *wiphy, bool enable) +{ + return skw_util_set_mib_enable(wiphy, 0, + SKW_MIB_SET_HDK_TEST, enable); +} + +#ifdef CONFIG_SWT6621S_NOT_WAKEUP_HOST +static int skw_set_wakeup_host_mib(struct wiphy *wiphy, bool enable) +{ + return skw_util_set_mib_enable(wiphy, 0, + SKW_MIB_SET_WAKEUP_HOST_ENABLE, enable); +} +#endif + +static void skw_common_mib_set(struct wiphy *wiphy, struct skw_core *skw) +{ + skw_set_link_loss_mib(wiphy, skw->config.fw.link_loss_thrd); + skw_set_24ghz_band_mib(wiphy, skw->config.fw.band_24ghz); + skw_set_hdk_test_mib(wiphy, skw->config.fw.hdk_tst); + +#ifdef CONFIG_SWT6621S_NOT_WAKEUP_HOST + skw_set_wakeup_host_mib(wiphy, false); +#endif +} + +int skw_sync_chip_info(struct wiphy *wiphy, struct skw_chip_info *chip) +{ + int ret; + const char *chipid; + int vendor, revision; + u64 ts = local_clock(); + struct skw_core *skw = wiphy_priv(wiphy); + + skw->fw.host_timestamp = ts; + skw->fw.host_seconds = skw_get_seconds(); + + do_div(ts, 1000000); + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_GET_INFO, &ts, sizeof(ts), + chip, sizeof(*chip)); + if (ret) { + skw_err("ret: %d\n", ret); + return ret; + } + + if (chip->priv_filter_arp) + set_bit(SKW_FLAG_FW_FILTER_ARP, &skw->flags); + + if (chip->priv_ignore_cred) + set_bit(SKW_FLAG_FW_IGNORE_CRED, &skw->flags); + + if (chip->priv_p2p_common_port) + set_bit(SKW_FLAG_LEGACY_P2P_COMMON_PORT, &skw->flags); + + if (chip->priv_dfs_master_enabled) + skw->dfs.fw_enabled = true; + + if (chip->nr_hw_mac) + skw->hw.nr_lmac = chip->nr_hw_mac; + else + skw->hw.nr_lmac = 1; + + BUG_ON(skw->hw.nr_lmac > SKW_MAX_LMAC_SUPPORT); + + memcpy(skw->fw.build_time, chip->fw_build_time, + sizeof(skw->fw.build_time)); + memcpy(skw->fw.plat_ver, chip->fw_plat_ver, sizeof(skw->fw.plat_ver)); + memcpy(skw->fw.wifi_ver, chip->fw_wifi_ver, sizeof(skw->fw.wifi_ver)); + + skw->fw.timestamp = chip->fw_timestamp; + skw->fw.max_num_sta = chip->max_sta_allowed; + skw->fw.fw_bw_capa = chip->fw_bw_capa; + + chipid = skw_get_chip_id(chip->fw_chip_type); + if (!chipid) { + skw_err("chip id 0x%x not support\n", chip->fw_chip_type); + return -ENOTSUPP; + } + + if (test_bit(SKW_CFG_FLAG_OVERLAY_MODE, &g_skw_config.conf.global.flags)) { + int j; + char *pos, cfg[64] = {0}; + + if (strlen(g_skw_config.conf.calib.chip)) + pos = g_skw_config.conf.calib.chip; + else + pos = (char *)chipid; + + snprintf(cfg, sizeof(cfg) - 1, "%s.cfg", pos); + + for (j = 0; j < strlen(cfg); j++) + cfg[j] = tolower(cfg[j]); + + skw_update_config(NULL, cfg, &skw->config); + } + + /* BIT[0:1] Reserved + * BIT[2:3] BUS Type + * BIT[4:7] Vendor ID + */ + vendor = 0; + + if (test_bit(SKW_CFG_CALIB_STRICT_MODE, &skw->config.calib.flags)) + vendor |= ((skw->hw.bus & 0x3) << 2); + +#ifdef CONFIG_SWT6621S_CALIB_APPEND_MODULE_ID + if (chip->calib_module_id) { + int i; + int vdata = chip->calib_module_id; + + for (i = 0; i < 4; i++) { + if ((vdata & 0xf) == 0xf) + vendor |= BIT(4 + i); + + vdata >>= 4; + } + } +#endif + + revision = skw->hw_pdata->chipid[15]; + + if (strlen(skw->config.calib.chip)) + chipid = skw->config.calib.chip; + + snprintf(skw->fw.calib_file, sizeof(skw->fw.calib_file), + "%s_%s_R%02X%03X.bin", chipid, skw->config.calib.project, + vendor & 0xff, revision); + + if (chip->priv_pn_reuse) + set_bit(SKW_FLAG_FW_PN_REUSE, &skw->flags); + + skw_set_ext_capa(skw, chip); + /* HT capa */ + + skw_common_mib_set(wiphy, skw); + + skw_dbg("efuse mac: %pM, %s\n", chip->mac, + is_valid_ether_addr(chip->mac) ? "valid" : "invalid"); + + return 0; +} + +static void skw_setup_mac_address(struct wiphy *wiphy, u8 *user_mac, u8 *hw_mac) +{ + int i; + u8 addr[ETH_ALEN] = {0}; + struct skw_core *skw = wiphy_priv(wiphy); + + if (user_mac && is_valid_ether_addr(user_mac)) { + skw_ether_copy(addr, user_mac); + } else if (hw_mac && is_valid_ether_addr(hw_mac)) { + skw_ether_copy(addr, hw_mac); + } else { + eth_random_addr(addr); + addr[0] = 0xFE; + addr[1] = 0xFD; + addr[2] = 0xFC; + } + + for (i = 0; i < SKW_NR_IFACE; i++) { + skw_ether_copy(skw->address[i].addr, addr); + + if (i != 0) { + skw->address[i].addr[0] |= BIT(1); + skw->address[i].addr[3] ^= BIT(i); + } + + skw_dbg("addr[%d]: %pM\n", i, skw->address[i].addr); + } +} + +static int skw_buffer_init(struct skw_core *skw) +{ + int ret, size; + + if (skw->hw.bus == SKW_BUS_PCIE) { + ret = skw_edma_init(priv_to_wiphy(skw)); + if (ret < 0) { + skw_err("edma init failed, ret: %d\n", ret); + return ret; + } + + skw->cmd.data = skw->edma.cmd_chn.current_node->buffer; + } else { + size = sizeof(struct scatterlist); + + skw->sgl_dat = kcalloc(SKW_NR_SGL_DAT, size, GFP_KERNEL); + if (!skw->sgl_dat) { + skw_err("sg list malloc failed, sg length: %d\n", + SKW_NR_SGL_DAT); + + return -ENOMEM; + } + + skw->sgl_cmd = kcalloc(SKW_NR_SGL_CMD, size, GFP_KERNEL); + if (!skw->sgl_cmd) { + skw_err("sg list malloc failed, sg length: %d\n", + SKW_NR_SGL_CMD); + + SKW_KFREE(skw->sgl_dat); + return -ENOMEM; + } + + skw->cmd.data = SKW_ZALLOC(SKW_MSG_BUFFER_LEN, GFP_KERNEL); + if (!skw->cmd.data) { + skw_err("mallc data buffer failed\n"); + SKW_KFREE(skw->sgl_dat); + SKW_KFREE(skw->sgl_cmd); + return -ENOMEM; + } + } + + return 0; +} + +static void skw_buffer_deinit(struct skw_core *skw) +{ + if (skw->hw.bus == SKW_BUS_PCIE) { + skw_edma_deinit(priv_to_wiphy(skw)); + } else { + SKW_KFREE(skw->sgl_dat); + SKW_KFREE(skw->sgl_cmd); + SKW_KFREE(skw->cmd.data); + + //TODO : recycle txqlen_pending if BSP support API + if (unlikely(atomic_read(&skw->txqlen_pending))) + skw_err("txqlen_pending:%d remind\n", atomic_read(&skw->txqlen_pending)); + } +} + +static struct wakeup_source *skw_wakeup_source_init(const char *name) +{ + struct wakeup_source *ws; + + ws = wakeup_source_create(name); + if (ws) + wakeup_source_add(ws); + + return ws; +} + +static void skw_wakeup_source_deinit(struct wakeup_source *ws) +{ + if (ws) { + wakeup_source_remove(ws); + wakeup_source_destroy(ws); + } +} + +static void skw_hw_hal_init(struct skw_core *skw, struct sv6160_platform_data *pdata) +{ + int i, j; + struct { + u8 dat_port; + u8 logic_port; + u8 flags; + u8 resv; + } skw_port[SKW_MAX_LMAC_SUPPORT] = {0}; + + atomic_set(&skw->hw.credit, 0); + skw->hw.align = pdata->align_value; + skw->hw.cmd_port = pdata->cmd_port; + skw->hw.pkt_limit = pdata->max_buffer_size / SKW_TX_PACK_SIZE; + skw->hw.dma = (pdata->bus_type >> 3) & 0x3; + + if (skw->hw.pkt_limit >= SKW_NR_SGL_DAT) { + WARN_ONCE(1, "pkt limit(%d) larger than buffer", skw->hw.pkt_limit); + skw->hw.pkt_limit = SKW_NR_SGL_DAT - 1; + } + + skw->hw.wow.enabled = false; + set_bit(SKW_HW_FLAG_CFG80211_PM, &skw->hw.flags); + + switch (pdata->bus_type & SKW_BUS_TYPE_MASK) { + case SKW_BUS_SDIO2: + set_bit(SKW_HW_FLAG_SDIO_V2, &skw->hw.flags); + + /* fall through */ + skw_fallthrough; + + case SKW_BUS_SDIO: + skw->hw.bus = SKW_BUS_SDIO; + + skw->hw.bus_dat_xmit = skw_sdio_xmit; + skw->hw.bus_cmd_xmit = skw_sdio_cmd_xmit; + + set_bit(SKW_HW_FLAG_EXTRA_HDR, &skw->hw.flags); + skw->hw.extra.hdr_len = SKW_EXTER_HDR_SIZE; + skw->hw.extra.eof_offset = 23; + skw->hw.extra.chn_offset = 24; + skw->hw.extra.len_offset = 7; + + if(test_bit(SKW_HW_FLAG_SDIO_V2, &skw->hw.flags)) + skw->hw.extra.len_offset = 0; + + skw->hw.rx_desc.hdr_offset = SKW_SDIO_RX_DESC_HDR_OFFSET; + skw->hw.rx_desc.msdu_offset = SKW_SDIO_RX_DESC_MSDU_OFFSET; + + skw_port[0].dat_port = pdata->data_port & 0xf; + skw_port[0].logic_port = skw_port[0].dat_port; + skw_port[0].flags = SKW_LMAC_FLAG_INIT | + SKW_LMAC_FLAG_RXCB; + + skw_port[1].dat_port = (pdata->data_port >> 4) & 0xf; + skw_port[1].logic_port = skw_port[1].dat_port; + if (skw_port[1].logic_port) { + skw_port[1].flags = SKW_LMAC_FLAG_INIT | + SKW_LMAC_FLAG_RXCB; + + } + + break; + + case SKW_BUS_USB2: + set_bit(SKW_HW_FLAG_USB_V2, &skw->hw.flags); + + /* fall through */ + skw_fallthrough; + + case SKW_BUS_USB: + skw->hw.bus = SKW_BUS_USB; + + /* ignore PM callback from cfg80211 */ + clear_bit(SKW_HW_FLAG_CFG80211_PM, &skw->hw.flags); + + skw->hw.bus_dat_xmit = skw_usb_xmit; + skw->hw.bus_cmd_xmit = skw_usb_cmd_xmit; + + set_bit(SKW_HW_FLAG_EXTRA_HDR, &skw->hw.flags); + skw->hw.extra.hdr_len = SKW_EXTER_HDR_SIZE; + skw->hw.extra.eof_offset = 23; + skw->hw.extra.chn_offset = 24; + skw->hw.extra.len_offset = 7; + + if(test_bit(SKW_HW_FLAG_USB_V2, &skw->hw.flags)) + skw->hw.extra.len_offset = 0; + + skw->hw.rx_desc.hdr_offset = SKW_USB_RX_DESC_HDR_OFFSET; + skw->hw.rx_desc.msdu_offset = SKW_USB_RX_DESC_MSDU_OFFSET; + + skw_port[0].dat_port = pdata->data_port; + skw_port[0].logic_port = 0; + skw_port[0].flags = SKW_LMAC_FLAG_INIT | + SKW_LMAC_FLAG_RXCB | + SKW_LMAC_FLAG_TXCB; + + skw_port[1].dat_port = pdata->data_port; + skw_port[1].logic_port = 1; + skw_port[1].flags = SKW_LMAC_FLAG_INIT; + + break; + + case SKW_BUS_PCIE: + skw->hw.bus = SKW_BUS_PCIE; + + /* ignore PM callback from cfg80211 */ + clear_bit(SKW_HW_FLAG_CFG80211_PM, &skw->hw.flags); + + skw->hw.bus_dat_xmit = skw_pcie_xmit; + skw->hw.bus_cmd_xmit = skw_pcie_cmd_xmit; + skw->hw.dma = SKW_ASYNC_EDMA_TX; + + skw->hw.rx_desc.hdr_offset = SKW_PCIE_RX_DESC_HDR_OFFSET; + skw->hw.rx_desc.msdu_offset = SKW_PCIE_RX_DESC_MSDU_OFFSET; + + skw->hw.cmd_port = -1; + + skw->hw.pkt_limit = SKW_EDMA_TX_CHN_NODE_NUM * TX_BUF_ADDR_CNT; + + skw_port[0].dat_port = 0; + skw_port[0].logic_port = 0; + skw_port[0].flags = SKW_LMAC_FLAG_INIT; + + skw_port[1].logic_port = 0; + skw_port[1].dat_port = 0; + skw_port[1].flags = SKW_LMAC_FLAG_INIT; + + break; + + default: + break; + } + + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + skw->hw.lmac[i].id = i; + skw->hw.lmac[i].lport = skw_port[i].logic_port; + skw->hw.lmac[i].dport = skw_port[i].dat_port; + skw->hw.lmac[i].flags = skw_port[i].flags; + skw->hw.lmac[i].iface_bitmap = 0; + + skw->hw.lmac[i].skw = skw; + skb_queue_head_init(&skw->hw.lmac[i].rx_dat_q); + skb_queue_head_init(&skw->hw.lmac[i].avail_skb); + skb_queue_head_init(&skw->hw.lmac[i].edma_free_list); + + atomic_set(&skw->hw.lmac[i].fw_credit, 0); + atomic_set(&skw->hw.lmac[i].avail_skb_num, 0); + + for (j = 0; j < SKW_MAX_PEER_SUPPORT; j++) { + skw->hw.lmac[i].peer_ctx[j].idx = j; + mutex_init(&skw->hw.lmac[i].peer_ctx[j].lock); + } + } +} + +static int skw_core_init(struct skw_core *skw, struct platform_device *pdev, int idx) +{ + int ret; + char name[32] = {0}; + + skw->hw_pdata = dev_get_platdata(&pdev->dev); + if (!skw->hw_pdata) { + skw_err("get drv data failed\n"); + return -ENODEV; + } + + memcpy(&skw->config, &g_skw_config.conf, sizeof(struct skw_config)); + + skw->idx = idx; + skw_hw_hal_init(skw, skw->hw_pdata); + + snprintf(name, sizeof(name), "chip%d.%s", idx, skw_bus_name(skw->hw.bus)); + skw->dentry = skw_debugfs_subdir(name, NULL); + skw->pentry = skw_procfs_subdir(name, NULL); + + mutex_init(&skw->lock); + +#ifdef CONFIG_SWT6621S_SAP_SME_EXT + set_bit(SKW_FLAG_SAP_SME_EXTERNAL, &skw->flags); +#endif + +#ifdef CONFIG_SWT6621S_STA_SME_EXT + set_bit(SKW_FLAG_STA_SME_EXTERNAL, &skw->flags); +#endif + +#ifdef CONFIG_SWT6621S_REPEATER_MODE + set_bit(SKW_FLAG_REPEATER, &skw->flags); +#endif + skw->regd = NULL; + skw->country[0] = '0'; + skw->country[1] = '0'; + + atomic_set(&skw->exit, 0); + atomic_set(&skw->tx_wake, 0); + atomic_set(&skw->rx_wake, 0); + + init_waitqueue_head(&skw->tx_wait_q); + init_waitqueue_head(&skw->rx_wait_q); + + skb_queue_head_init(&skw->rx_dat_q); + atomic_set(&skw->txqlen_pending, 0); + + spin_lock_init(&skw->dfs.skw_pool_lock); + INIT_LIST_HEAD(&skw->dfs.skw_pulse_pool); + INIT_LIST_HEAD(&skw->dfs.skw_pseq_pool); + + sema_init(&skw->cmd.lock, 1); + init_waitqueue_head(&skw->cmd.wq); + skw->cmd.ws = skw_wakeup_source_init("skwifi_cmd"); + sema_init(&skw->cmd.mgmt_cmd_lock, 1); + + spin_lock_init(&skw->vif.lock); + + skw_event_work_init(&skw->event_work, skw_default_event_work); + skw->event_wq = alloc_workqueue("skw_evt_wq.%d", WQ_UNBOUND | WQ_MEM_RECLAIM, 1, idx); + if (!skw->event_wq) { + ret = -ENOMEM; + skw_err("alloc event wq failed, ret: %d\n", ret); + + goto deinit_ws; + } + + ret = skw_dpd_init(&skw->dpd); + if (ret < 0) + goto deinit_wq; + + ret = skw_buffer_init(skw); + if (ret < 0) + goto deinit_dpd; + + ret = skw_rx_init(skw); + if (ret < 0) { + skw_err("rx init failed, ret: %d\n", ret); + goto deinit_buff; + } + + ret = skw_tx_init(skw); + if (ret < 0) { + skw_err("tx init failed, ret: %d\n", ret); + goto deinit_rx; + } + + skw_debugfs_file(skw->dentry, "repeater", 0666, &skw_repeater_fops, skw); + + skw->dbg.nr_cmd = SKW_DBG_NR_CMD; + skw->dbg.nr_dat = SKW_DBG_NR_DAT; + atomic_set(&skw->dbg.loop, 0); + skw->isr_cpu_id = -1; + + return 0; + +deinit_rx: + skw_rx_deinit(skw); + +deinit_buff: + skw_buffer_deinit(skw); + +deinit_dpd: + skw_dpd_deinit(&skw->dpd); + +deinit_wq: + destroy_workqueue(skw->event_wq); + skw_event_work_deinit(&skw->event_work); + +deinit_ws: + skw_wakeup_source_deinit(skw->cmd.ws); + + debugfs_remove_recursive(skw->dentry); + proc_remove(skw->pentry); + + return ret; +} + +static void skw_core_deinit(struct skw_core *skw) +{ + skw_tx_deinit(skw); + + skw_rx_deinit(skw); + + skw_buffer_deinit(skw); + + skw_dpd_deinit(&skw->dpd); + + destroy_workqueue(skw->event_wq); + + skw_event_work_deinit(&skw->event_work); + + skw_wakeup_source_deinit(skw->cmd.ws); + + debugfs_remove_recursive(skw->dentry); + proc_remove(skw->pentry); +} + +int skw_set_ip(struct wiphy *wiphy, struct net_device *ndev, + struct skw_setip_param *setip_param, int size) +{ + int ret = 0; + //int size = 0; + + //size = sizeof(struct skw_setip_param); + ret = skw_queue_work(wiphy, netdev_priv(ndev), + SKW_WORK_SET_IP, setip_param, size); + if (ret) + skw_err("Set IP failed\n"); + + return ret; +} + +/* + * Get the IP address for both IPV4 and IPV6, set it + * to firmware while they are valid. + */ +void skw_set_ip_to_fw(struct wiphy *wiphy, struct net_device *ndev) +{ + struct in_device *in_dev; + struct in_ifaddr *ifa; + + struct inet6_dev *idev; + struct inet6_ifaddr *ifp; + + struct skw_setip_param setip_param[SKW_FW_IPV6_COUNT_LIMIT+1]; + int ip_count = 0, ipv6_count = 0; + + in_dev = __in_dev_get_rtnl(ndev); + if (!in_dev) + goto ipv6; + + ifa = in_dev->ifa_list; + if (!ifa || !ifa->ifa_local) + goto ipv6; + + skw_info("ip addr: %pI4\n", &ifa->ifa_local); + setip_param[ip_count].ip_type = SKW_IP_IPV4; + setip_param[ip_count].ipv4 = ifa->ifa_local; + ip_count++; + +ipv6: + idev = __in6_dev_get(ndev); + if (!idev) + goto ip_apply; + + read_lock_bh(&idev->lock); + list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) { + skw_info("ip addr: %pI6\n", &ifp->addr); + if (++ipv6_count > SKW_FW_IPV6_COUNT_LIMIT) { + skw_warn("%s set first %d of %d ipv6 address\n", + ndev->name, SKW_FW_IPV6_COUNT_LIMIT, ipv6_count); + + read_unlock_bh(&idev->lock); + goto ip_apply; + } + + setip_param[ip_count].ip_type = SKW_IP_IPV6; + memcpy(&setip_param[ip_count].ipv6, &ifp->addr, 16); + ip_count++; + } + read_unlock_bh(&idev->lock); + +ip_apply: + if (ip_count) + skw_set_ip(wiphy, ndev, setip_param, ip_count*sizeof(struct skw_setip_param)); + +} + +#ifdef CONFIG_INET +static int skw_ifa4_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct in_ifaddr *ifa = data; + struct net_device *ndev; + struct wireless_dev *wdev; + struct skw_core *skw = container_of(nb, struct skw_core, ifa4_nf); + + skw_dbg("action: %ld\n", action); + + if (!ifa->ifa_dev) + return NOTIFY_DONE; + + ndev = ifa->ifa_dev->dev; + wdev = ndev->ieee80211_ptr; + + if (!wdev || wdev->wiphy != priv_to_wiphy(skw)) + return NOTIFY_DONE; + + if (ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) + return NOTIFY_DONE; + + switch (action) { + case NETDEV_UP: +// case NETDEV_DOWN: + skw_set_ip_to_fw(wdev->wiphy, ndev); + + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +#endif + +#ifdef CONFIG_IPV6 +static int skw_ifa6_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct inet6_ifaddr *ifa6 = data; + struct skw_core *skw = container_of(nb, struct skw_core, ifa6_nf); + + struct net_device *ndev; + struct wireless_dev *wdev; + + skw_dbg("action: %ld\n", action); + + if (!ifa6->idev || !ifa6->idev->dev) + return NOTIFY_DONE; + + ndev = ifa6->idev->dev; + wdev = ndev->ieee80211_ptr; + + if (!wdev || wdev->wiphy != priv_to_wiphy(skw)) + return NOTIFY_DONE; + + switch (action) { + case NETDEV_UP: + // case NETDEV_DOWN: + + skw_set_ip_to_fw(wdev->wiphy, ndev); + + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +#endif + +static int skw_bsp_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct skw_core *skw = container_of(nb, struct skw_core, bsp_nf); + struct wiphy *wiphy = priv_to_wiphy(skw); + struct cfg80211_wowlan wow, *pwow = NULL; + + skw_info("action: %ld, skw flags: 0x%lx, nf_flags: 0x%lx\n", + action, skw->flags, skw->nf_flags); + + switch (action) { + case SKW_BSP_NF_ASSERT: + case SKW_BSP_NF_BLOCKED: + case SKW_BSP_NF_FW_REBOOT: + set_bit(SKW_FLAG_FW_ASSERT, &skw->flags); + + WRITE_ONCE(skw->nf_flags, 0); + set_bit(SKW_BSP_NF_ASSERT, &skw->nf_flags); + + skw_abort_cmd(skw); + cancel_work_sync(&skw->recovery_work); + skw_wifi_disable(skw->hw_pdata); + + break; + + case SKW_BSP_NF_READY: + if (test_and_clear_bit(SKW_BSP_NF_ASSERT, &skw->nf_flags)) + schedule_work(&skw->recovery_work); + + break; + + case SKW_BSP_NF_SUSPEND: + if (!test_bit(SKW_HW_FLAG_CFG80211_PM, &skw->hw.flags)) { + if (skw->hw.wow.enabled) { + memset(&wow, 0x0, sizeof(struct cfg80211_wowlan)); + + if (skw->hw.wow.flags & SKW_WOW_DISCONNECT) + wow.disconnect = true; + + if (skw->hw.wow.flags & SKW_WOW_MAGIC_PKT) + wow.magic_pkt = true; + + if (skw->hw.wow.flags & SKW_WOW_GTK_REKEY_FAIL) + wow.gtk_rekey_failure = true; + + if (skw->hw.wow.flags & SKW_WOW_EAP_IDENTITY_REQ) + wow.eap_identity_req = true; + + if (skw->hw.wow.flags & SKW_WOW_FOUR_WAY_HANDSHAKE) + wow.four_way_handshake = true; + + if (skw->hw.wow.flags & SKW_WOW_RFKILL_RELEASE) + wow.rfkill_release = true; + + pwow = &wow; + } + + set_bit(SKW_BSP_NF_SUSPEND, &skw->nf_flags); + + skw_suspend(wiphy, pwow); + } + + break; + + case SKW_BSP_NF_RESUME: + if (test_and_clear_bit(SKW_BSP_NF_SUSPEND, &skw->nf_flags)) + skw_work_resume(wiphy); + + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static int skw_pm_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct skw_core *skw = container_of(nb, struct skw_core, pm_nf); + + skw_dbg("action: %ld\n", action); + + switch (action) { + case PM_SUSPEND_PREPARE: + set_bit(SKW_FLAG_SUSPEND_PREPARE, &skw->flags); + break; + + case PM_POST_SUSPEND: + clear_bit(SKW_FLAG_SUSPEND_PREPARE, &skw->flags); + break; + } + + return 0; +} + +static void skw_ifa_notifier_register(struct skw_core *skw) +{ +#ifdef CONFIG_INET + skw->ifa4_nf.notifier_call = skw_ifa4_notifier; + register_inetaddr_notifier(&skw->ifa4_nf); +#endif + +#ifdef CONFIG_IPV6 + skw->ifa6_nf.notifier_call = skw_ifa6_notifier; + register_inet6addr_notifier(&skw->ifa6_nf); +#endif + + skw->bsp_nf.notifier_call = skw_bsp_notifier; + skw_register_bsp_notifier(skw, &skw->bsp_nf); + + skw->pm_nf.notifier_call = skw_pm_notifier; + register_pm_notifier(&skw->pm_nf); +} + +static void skw_ifa_notifier_unregister(struct skw_core *skw) +{ +#ifdef CONFIG_INET + unregister_inetaddr_notifier(&skw->ifa4_nf); +#endif + +#ifdef CONFIG_IPV6 + unregister_inet6addr_notifier(&skw->ifa6_nf); +#endif + + skw_unregister_bsp_notifier(skw, &skw->bsp_nf); + + unregister_pm_notifier(&skw->pm_nf); +} + +int skw_lmac_bind_iface(struct skw_core *skw, struct skw_iface *iface, int lmac_id) +{ + struct skw_lmac *lmac; + + if (lmac_id >= skw->hw.nr_lmac) { + skw_err("invalid lmac id: %d\n", skw->hw.nr_lmac); + return -ENOTSUPP; + } + + iface->lmac_id = lmac_id; + lmac = &skw->hw.lmac[lmac_id]; + BUG_ON(!(lmac->flags & SKW_LMAC_FLAG_INIT)); + + if (skw->hw.bus == SKW_BUS_PCIE) { + // TODO: + // register edma channel + // skw_edma_enable_channel(&lmac->edma_tx_chn, isr); + } + + SKW_SET(lmac->iface_bitmap, BIT(iface->id)); + SKW_SET(lmac->flags, SKW_LMAC_FLAG_ACTIVED); + + return 0; +} + +int skw_lmac_unbind_iface(struct skw_core *skw, int lmac_id, int iface_id) +{ + struct skw_lmac *lmac; + + if (lmac_id >= skw->hw.nr_lmac) { + skw_err("invalid lmac id: %d\n", skw->hw.nr_lmac); + return 0; + } + + lmac = &skw->hw.lmac[lmac_id]; + + SKW_CLEAR(lmac->iface_bitmap, BIT(iface_id)); + + if (lmac->iface_bitmap) + return 0; + + // TODO: + // unregister edma channel + + SKW_CLEAR(lmac->flags, SKW_LMAC_FLAG_ACTIVED); + + skw_dbg("reset fw credit for mac:%d", lmac_id); + atomic_set(&skw->hw.lmac[lmac_id].fw_credit, 0); + + return 0; +} + +void skw_add_credit(struct skw_core *skw, int lmac_id, int cred) +{ + trace_skw_tx_add_credit(lmac_id, cred); + + atomic_add(cred, &skw->hw.lmac[lmac_id].fw_credit); + smp_wmb(); + + skw_wakeup_tx(skw, 0); + skw_detail("lmac_id:%d cred:%d", lmac_id, cred); + + if (skw->hw.bus == SKW_BUS_SDIO || skw->hw.bus == SKW_BUS_SDIO2) + schedule_work(&skw->hw.lmac[lmac_id].dy_work); + +} + +static int skw_add_default_iface(struct wiphy *wiphy) +{ + int ret = 0; + struct skw_iface *iface = NULL; +#ifdef CONFIG_SWT6621S_LEGACY_P2P + struct skw_iface *p2p = NULL; + struct skw_core *skw = wiphy_priv(wiphy); +#endif + + rtnl_lock(); + + iface = skw_add_iface(wiphy, "wlan%d", NL80211_IFTYPE_STATION, + skw_mac, SKW_INVALID_ID, true); + if (IS_ERR(iface)) { + ret = PTR_ERR(iface); + skw_err("add iface failed, ret: %d\n", ret); + + goto unlock; + } + +#ifdef CONFIG_SWT6621S_LEGACY_P2P + if (test_bit(SKW_FLAG_LEGACY_P2P_COMMON_PORT, &skw->flags)) + p2p = skw_add_iface(wiphy, "p2p%d", NL80211_IFTYPE_STATION, + NULL, SKW_LAST_IFACE_ID, true); + else + p2p = skw_add_iface(wiphy, "p2p%d", NL80211_IFTYPE_P2P_DEVICE, + NULL, SKW_LAST_IFACE_ID, true); + + if (IS_ERR(p2p)) { + ret = PTR_ERR(p2p); + skw_err("add p2p legacy interface failed, ret: %d\n", ret); + + skw_del_iface(wiphy, iface); + + goto unlock; + } + + if (!test_bit(SKW_FLAG_LEGACY_P2P_COMMON_PORT, &skw->flags)) + p2p->flags |= SKW_IFACE_FLAG_LEGACY_P2P_DEV; + +#endif + +unlock: + rtnl_unlock(); + + return ret; +} + +static int skw_calib_bin_download(struct wiphy *wiphy, const u8 *data, u32 size) +{ + int i = 0, ret = 0; + int buf_size, remain = size; + struct skw_calib_param calib; + + skw_dbg("file size: %d\n", size); + + buf_size = sizeof(calib.data); + + while (remain > 0) { + calib.len = (remain < buf_size) ? remain : buf_size; + calib.seq = i; + remain -= calib.len; + + memcpy(calib.data, data, calib.len); + + if (!remain) + calib.end = 1; + else + calib.end = 0; + + skw_dbg("bb_file remain: %d, seq: %d len: %d end: %d\n", + remain, calib.seq, calib.len, calib.end); + + ret = skw_msg_xmit_timeout(wiphy, 0, SKW_CMD_PHY_BB_CFG, + &calib, sizeof(calib), NULL, 0, + "SKW_CMD_PHY_BB_CFG", msecs_to_jiffies(5000), 0); + if (ret) { + skw_err("failed, ret: %d, seq: %d\n", ret, i); + break; + } + + i++; + data += calib.len; + } + + return ret; +} + +int skw_calib_download(struct wiphy *wiphy, const char *fname) +{ + int ret = 0; + const struct firmware *fw; + + skw_dbg("fw file: %s\n", fname); + + ret = request_firmware(&fw, fname, &wiphy->dev); + if (ret) { + skw_err("load %s failed, ret: %d\n", fname, ret); + return ret; + } + + ret = skw_calib_bin_download(wiphy, fw->data, fw->size); + if (ret != 0) + skw_err("bb_file cali msg fail\n"); + + release_firmware(fw); + + return ret; +} + +void skw_dbg_dump(struct skw_core *skw) +{ + int i; + u64 nsecs; + unsigned long rem_nsec; + + for (i = 0; i < skw->dbg.nr_cmd; i++) { + struct skw_dbg_cmd *cmd = &skw->dbg.cmd[i]; + + skw_info("cmd[%d].id: %d, seq: %d, flags: 0x%lx, loop: %d\n", + i, cmd->id, cmd->seq, cmd->flags, cmd->loop); + + nsecs = cmd->trigger; + rem_nsec = do_div(nsecs, 1000000000); + skw_info("cmd.%d.%d.trigger: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->build; + rem_nsec = do_div(nsecs, 1000000000); + skw_info("cmd.%d.%d.build: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->xmit; + rem_nsec = do_div(nsecs, 1000000000); + skw_info("cmd.%d.%d.xmit: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->done; + rem_nsec = do_div(nsecs, 1000000000); + skw_info("cmd.%d.%d.done: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->ack; + rem_nsec = do_div(nsecs, 1000000000); + skw_info("cmd.%d.%d.ack: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = cmd->assert; + rem_nsec = do_div(nsecs, 1000000000); + skw_info("cmd.%d.%d.assert: %5lu.%06lu\n", cmd->id, cmd->seq, + (unsigned long)nsecs, rem_nsec / 1000); + } + + for (i = 0; i < skw->dbg.nr_dat; i++) { + struct skw_dbg_dat *dat = &skw->dbg.dat[i]; + + nsecs = dat->trigger; + rem_nsec = do_div(nsecs, 1000000000); + skw_info("dat.%d.%d.trigger: %5lu.%06lu\n", + i, dat->qlen, (unsigned long)nsecs, rem_nsec / 1000); + + nsecs = dat->done; + rem_nsec = do_div(nsecs, 1000000000); + skw_info("dat.%d.%d.done: %5lu.%06lu\n", + i, dat->qlen, (unsigned long)nsecs, rem_nsec / 1000); + } +} + +static int skw_drv_probe(struct platform_device *pdev) +{ + int ret = 0; + struct skw_chip_info chip; + struct wiphy *wiphy = NULL; + struct skw_core *skw = NULL; + int idx = atomic_inc_return(&skw_chip_idx); + + skw_info("chip: %d, MAC: %pM\n", idx, skw_mac); + + wiphy = skw_alloc_wiphy(sizeof(struct skw_core)); + if (!wiphy) { + ret = -ENOMEM; + skw_err("malloc wiphy failed\n"); + + goto failed; + } + + set_wiphy_dev(wiphy, &pdev->dev); + + skw = wiphy_priv(wiphy); + + ret = skw_core_init(skw, pdev, idx); + if (ret) { + skw_err("core init failed, ret: %d\n", ret); + goto free_wiphy; + } + + skw_regd_init(wiphy); + skw_vendor_init(wiphy); + skw_work_init(wiphy); + skw_timer_init(skw); + skw_recovery_init(skw); + + skw_wifi_enable(dev_get_platdata(&pdev->dev)); + + if (skw_sync_cmd_event_version(wiphy) || + skw_sync_chip_info(wiphy, &chip)) + goto core_deinit; + +#ifdef CONFIG_PLATFORM_ROCKCHIP + if (!is_valid_ether_addr(skw_mac)) + rockchip_wifi_mac_addr(skw_mac); +#endif + + skw_setup_mac_address(wiphy, skw_mac, chip.mac); + + if (skw_calib_download(wiphy, skw->fw.calib_file) < 0) + goto core_deinit; + + ret = skw_setup_wiphy(wiphy, &chip); + if (ret) { + skw_err("setup wiphy failed, ret: %d\n", ret); + goto core_deinit; + } + + rtnl_lock(); + + skw_set_wiphy_regd(wiphy, skw->country); + + if (skw->config.regd.country[0] != '0' && + skw->config.regd.country[1] != '0'){ + + ret = skw_set_regdom(wiphy, skw->config.regd.country); + if (ret) + skw_err("skw_set_regdom failed, ret: %d\n", ret); + } + + rtnl_unlock(); + + ret = skw_add_default_iface(wiphy); + if (ret) + goto unregister_wiphy; + + skw_ifa_notifier_register(skw); + + platform_set_drvdata(pdev, skw); + + skw_procfs_file(skw->pentry, "core", 0444, &skw_core_fops, skw); + + skw_debugfs_file(SKW_WIPHY_DENTRY(wiphy), "assert", 0444, + &skw_assert_fops, skw); + + skw_procfs_file(skw->pentry, "debug_info", 0444, &skw_debug_info_fops, skw); + + return 0; + +unregister_wiphy: + wiphy_unregister(wiphy); + +core_deinit: + skw_wifi_disable(dev_get_platdata(&pdev->dev)); + skw_recovery_deinit(skw); + skw_timer_deinit(skw); + skw_work_deinit(wiphy); + skw_vendor_deinit(wiphy); + skw_core_deinit(skw); + +free_wiphy: + wiphy_free(wiphy); + +failed: + atomic_dec(&skw_chip_idx); + + return ret; +} + +static int skw_drv_remove(struct platform_device *pdev) +{ + int i; + struct wiphy *wiphy; + struct skw_core *skw = platform_get_drvdata(pdev); + + skw_info("%s\n", pdev->name); + + if (!skw) + return 0; + + wiphy = priv_to_wiphy(skw); + + skw_ifa_notifier_unregister(skw); + + rtnl_lock(); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + cfg80211_shutdown_all_interfaces(wiphy); +#endif + + for (i = 0; i < SKW_NR_IFACE; i++) + skw_del_iface(wiphy, skw->vif.iface[i]); + + rtnl_unlock(); + + wiphy_unregister(wiphy); + + skw_wifi_disable(dev_get_platdata(&pdev->dev)); + + skw_recovery_deinit(skw); + + skw_timer_deinit(skw); + + skw_vendor_deinit(wiphy); + + skw_core_deinit(skw); + + skw_work_deinit(wiphy); + + wiphy_free(wiphy); + + atomic_dec(&skw_chip_idx); + + return 0; +} + +static struct platform_driver skw_drv = { + .probe = skw_drv_probe, + .remove = skw_drv_remove, + .driver = { + .owner = THIS_MODULE, + .name = "sv6621s_wireless1", + }, +}; + +static void skw_global_config_init(struct skw_global_config *gconf) +{ + memset(gconf, 0x0, sizeof(struct skw_global_config)); + + atomic_set(&gconf->index, 0); + + /* global */ + gconf->conf.global.dma_addr_align = 4; + + gconf->conf.global.reorder_timeout = 100; + + gconf->conf.fw.link_loss_thrd = 6; + + gconf->conf.fw.band_24ghz = SKW_CHAN_WIDTH_40; + + gconf->conf.fw.hdk_tst = 0; + +#ifdef CONFIG_SWT6621S_RX_REORDER_TIMEOUT + gconf->conf.global.reorder_timeout = CONFIG_SWT6621S_RX_REORDER_TIMEOUT; +#endif + +#ifdef CONFIG_SWT6621S_STA_SME_EXT + set_bit(SKW_CFG_FLAG_STA_EXT, &gconf->conf.global.flags); +#endif + +#ifdef CONFIG_SWT6621S_SAP_SME_EXT + set_bit(SKW_CFG_FLAG_SAP_EXT, &gconf->conf.global.flags); +#endif + +#ifdef CONFIG_SWT6621S_REPEATER_MODE + set_bit(SKW_CFG_FLAG_REPEATER, &gconf->conf.global.flags); +#endif + +#ifdef CONFIG_SWT6621S_OFFCHAN_TX + set_bit(SKW_CFG_FLAG_OFFCHAN_TX, &gconf->conf.global.flags); +#endif + + /* interface */ + strcpy(gconf->conf.intf.interface[0].name, "wlan%d"); + gconf->conf.intf.interface[0].inst = SKW_INVALID_ID; + gconf->conf.intf.interface[0].iftype = NL80211_IFTYPE_STATION; + +#ifdef CONFIG_SWT6621S_LEGACY_P2P + set_bit(SKW_CFG_FLAG_P2P_DEV, &gconf->conf.global.flags); + strcpy(gconf->conf.intf.interface[3].name, "p2p%d"); + gconf->conf.intf.interface[3].inst = 3; + gconf->conf.intf.interface[3].iftype = NL80211_IFTYPE_P2P_DEVICE; +#endif + + /* regdom */ + gconf->conf.regd.country[0] = '0'; + gconf->conf.regd.country[1] = '0'; + +#ifdef CONFIG_SWT6621S_REGD_SELF_MANAGED + set_bit(SKW_CFG_REGD_SELF_MANAGED, &gconf->conf.regd.flags); +#endif + +#ifdef CONFIG_SWT6621S_DEFAULT_COUNTRY + if (strlen(CONFIG_SWT6621S_DEFAULT_COUNTRY) == 2 && + is_skw_valid_reg_code(CONFIG_SWT6621S_DEFAULT_COUNTRY)) + memcpy(gconf->conf.regd.country, + CONFIG_SWT6621S_DEFAULT_COUNTRY, 2); + +#endif + + /* calib */ +#ifdef CONFIG_SWT6621S_CALIB_APPEND_BUS_ID + set_bit(SKW_CFG_CALIB_STRICT_MODE, &gconf->conf.calib.flags); +#endif + +#ifdef CONFIG_SWT6621S_CHIP_ID + if (strlen(CONFIG_SWT6621S_CHIP_ID)) + strncpy(gconf->conf.calib.chip, CONFIG_SWT6621S_CHIP_ID, + sizeof(gconf->conf.calib.chip) - 1); +#endif + + strcpy(gconf->conf.calib.project, "SEEKWAVE"); + +#ifdef CONFIG_SWT6621S_PROJECT_NAME + if (strlen(CONFIG_SWT6621S_PROJECT_NAME)) + strncpy(gconf->conf.calib.project, CONFIG_SWT6621S_PROJECT_NAME, + sizeof(gconf->conf.calib.project) - 1); +#endif + + skw_update_config(NULL, "skwifid.cfg", &g_skw_config.conf); +} + +static int __init skw_module_init(void) +{ + int ret = 0; + + pr_info("[%s] VERSION: %s (%s)\n", + SKW_TAG_INFO, SKW_VERSION, UTS_RELEASE); + + skw_dentry_init(); + skw_log_level_init(); + + skw_power_on_chip(); + + skw_global_config_init(&g_skw_config); + + ret = platform_driver_register(&skw_drv); + if (ret) { + skw_err("register %s failed, ret: %d\n", + skw_drv.driver.name, ret); + + skw_power_off_chip(); + + skw_log_level_deinit(); + skw_dentry_deinit(); + } + + return ret; +} + +static void __exit skw_module_exit(void) +{ + skw_info("unload\n"); + + skw_del_dbg_iface(); + platform_driver_unregister(&skw_drv); + + skw_power_off_chip(); + + skw_log_level_deinit(); + skw_dentry_deinit(); +} + +module_init(skw_module_init); +module_exit(skw_module_exit); + +module_param_array_named(mac, skw_mac, byte, NULL, 0444); +MODULE_PARM_DESC(mac, "config mac address"); + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0.0"); +MODULE_AUTHOR("seekwavetech"); diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_core.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_core.h new file mode 100755 index 0000000..6f51dec --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_core.h @@ -0,0 +1,899 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_CORE_H__ +#define __SKW_CORE_H__ + +#include <net/ipv6.h> +#include <linux/version.h> +#include <linux/pm_wakeup.h> + +#ifdef CONFIG_HAS_WAKELOCK +#include <linux/wakelock.h> +#endif + +#include "skw_util.h" +#include "skw_compat.h" +#include "skw_dentry.h" +#include "skw_log.h" +#include "skw_platform_data.h" +#include "skw_work.h" +#include "skw_edma.h" +#include "skw_iface.h" +#include "skw_calib.h" +#include "skw_recovery.h" +#include "skw_config.h" + +extern unsigned int tx_wait_time; + +#define SKW_BUS_TYPE_MASK TYPE_MASK +#define SKW_BUS_SDIO SDIO_LINK +#define SKW_BUS_USB USB_LINK +#define SKW_BUS_PCIE PCIE_LINK +#define SKW_BUS_SDIO2 SDIO2_LINK +#define SKW_BUS_USB2 USB2_LINK + +#define SKW_BSP_NF_ASSERT DEVICE_ASSERT_EVENT +#define SKW_BSP_NF_BLOCKED DEVICE_BLOCKED_EVENT +#define SKW_BSP_NF_READY DEVICE_BSPREADY_EVENT +#define SKW_BSP_NF_DISCONNECT 4 +#define SKW_BSP_NF_SUSPEND 6 +#define SKW_BSP_NF_RESUME 7 +#define SKW_BSP_NF_FW_REBOOT 8 + +#define SKW_FW_IPV6_COUNT_LIMIT 3 + +/*sap capa flag */ +#define SKW_CAPA_HT BIT(0) +#define SKW_CAPA_VHT BIT(1) +#define SKW_CAPA_HE BIT(2) + +/* capability */ +#define SKW_MAX_LMAC_SUPPORT 2 +#define SKW_NR_IFACE 4 +#define SKW_LAST_IFACE_ID (SKW_NR_IFACE - 1) +#define SKW_MAX_PEER_SUPPORT 32 +#define SKW_MAX_STA_ALLOWED 10 +#define SKW_NR_SGL_DAT 256 +#define SKW_NR_SGL_CMD 4 +#define SKW_MAX_IE_LEN 1400 +#define SKW_MSG_BUFFER_LEN 2048 +#define SKW_TX_PACK_SIZE 1536 + +#define SKW_DATA_ALIGN_SIZE 4 +#define SKW_DATA_ALIGN_MASK 3 + +#define SKW_EXTER_HDR_SIZE 4 +#define SKW_TX_HDR_SIZE 6 + +#define SKW_DBG_NR_CMD 2 +#define SKW_DBG_NR_DAT 2 + +/* protocal */ +#define SKW_ETH_P_WAPI 0x88B4 + +/* ioctl */ +#define PRIVATE_COMMAND_MAX_LEN 8192 +#define PRIVATE_COMMAND_DEF_LEN 4096 + +#define SKW_ANDROID_PRIV_START "START" +#define SKW_ANDROID_PRIV_STOP "STOP" +#define SKW_ANDROID_PRIV_SETFWPATH "SETFWPATH" +#define SKW_ANDROID_PRIV_COUNTRY "COUNTRY" +#define SKW_ANDROID_PRIV_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" +#define SKW_ANDROID_PRIV_RXFILTER_START "RXFILTER-START" +#define SKW_ANDROID_PRIV_RXFILTER_STOP "RXFILTER-STOP" +#define SKW_ANDROID_PRIV_RXFILTER_ADD "RXFILTER-ADD" +#define SKW_ANDROID_PRIV_RXFILTER_REMOVE "RXFILTER-REMOVE" +#define SKW_ANDROID_PRIV_SETSUSPENDMODE "SETSUSPENDMODE" +#define SKW_ANDROID_PRIV_BTCOEXMODE "BTCOEXMODE" +#define SKW_ANDROID_PRIV_MAX_NUM_STA "MAX_NUM_STA" +#define SKW_ANDROID_PRIV_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" + +/* SKW_FLAG_* */ +#define SKW_FLAG_FW_ASSERT (0) +#define SKW_FLAG_BLOCK_TX (1) +#define SKW_FLAG_FW_MAC_RECOVERY (2) +#define SKW_FLAG_FW_THERMAL (3) + +#define SKW_FLAG_FW_UART_OPEND (4) +#define SKW_FLAG_FW_FILTER_ARP (5) +#define SKW_FLAG_FW_IGNORE_CRED (6) +/* data not permit */ +#define SKW_FLAG_FW_CHIP_RECOVERY (7) +#define SKW_FLAG_SAP_SME_EXTERNAL (8) +#define SKW_FLAG_STA_SME_EXTERNAL (9) +#define SKW_FLAG_MBSSID_PRIV (10) +#define SKW_FLAG_MP_MODE (11) +#define SKW_FLAG_LEGACY_P2P_COMMON_PORT (12) +#define SKW_FLAG_SWITCHING_USB_MODE (13) +#define SKW_FLAG_PRIV_REGD (15) +#define SKW_FLAG_REPEATER (16) +#define SKW_FLAG_FW_PN_REUSE (17) +#define SKW_FLAG_SUSPEND_PREPARE (19) + +/* SKW_LMAC_FLAG_* */ +#define SKW_LMAC_FLAG_INIT BIT(0) +#define SKW_LMAC_FLAG_ACTIVED BIT(1) +#define SKW_LMAC_FLAG_RXCB BIT(2) +#define SKW_LMAC_FLAG_TXCB BIT(3) + +#define SKW_SYNC_ADMA_TX 0 +#define SKW_SYNC_SDMA_TX 1 +#define SKW_ASYNC_ADMA_TX 2 +#define SKW_ASYNC_SDMA_TX 3 +#define SKW_ASYNC_EDMA_TX 4 + +#define SKW_TXQ_STOPED(n, q) \ + netif_tx_queue_stopped(netdev_get_tx_queue(n, q)) + +#define SKW_IOCTL_ANDROID_CMD (SIOCDEVPRIVATE + 1) +#define SKW_IOCTL_CMD (SIOCDEVPRIVATE + 15) +#define SKW_IOCTL_SUBCMD_COUNTRY 1 + +struct skw_ioctl_cmd { + int len; + int id; +}; + +struct skw_lmac { + u8 id; + u8 flags; /* reference SKW_LMAC_FLAG_ */ + s8 lport; /* logic port */ + s8 dport; /* data port */ + + //u8 tx_done_chn; + //u8 rx_chn; + //u8 rx_buff_chn; + int iface_bitmap; + struct skw_peer_ctx peer_ctx[SKW_MAX_PEER_SUPPORT]; + atomic_t fw_credit; + + // struct skw_wmm_tx cached; + + struct net_device dummy_dev; + struct napi_struct napi_tx; + struct napi_struct napi_rx; + + struct work_struct dy_work; + struct skw_tx_vring *tx_vring; + + atomic_t avail_skb_num; + + struct sk_buff_head rx_dat_q; + struct sk_buff_head avail_skb; + struct sk_buff_head edma_free_list; + + struct skw_list rx_todo_list; + struct skw_core *skw; +}; + +struct skw_firmware_info { + u8 build_time[32]; + u8 plat_ver[16]; + u8 wifi_ver[16]; + u8 calib_file[64]; + u16 max_num_sta; + u16 resv; + u32 timestamp; + u64 host_timestamp; + unsigned long host_seconds; + u32 fw_bw_capa; +}; + +#define SKW_BW_CAP_2G_20M BIT(0) +#define SKW_BW_CAP_2G_40M BIT(1) +#define SKW_BW_CAP_5G_20M BIT(2) +#define SKW_BW_CAP_5G_40M BIT(3) +#define SKW_BW_CAP_5G_80M BIT(4) +#define SKW_BW_CAP_5G_160M BIT(5) +#define SKW_BW_CAP_5G_80P80M BIT(6) + +struct skw_chip_info { + u16 enc_capa; + + u32 chip_model; + u32 chip_version; + u32 fw_version; + u32 fw_capa; + + u8 max_sta_allowed; + u8 max_mc_addr_allowed; + + /* HT */ + u16 ht_capa; + u16 ht_ext_capa; + u16 ht_ampdu_param; + u32 ht_tx_mcs_maps; + u32 ht_rx_mcs_maps; + + /* VHT */ + u32 vht_capa; + u16 vht_tx_mcs_maps; + u16 vht_rx_mcs_maps; + + /* HE */ + u8 max_scan_ssids; + u8 he_capa[6]; + u8 he_phy_capa[11]; + u16 he_tx_mcs_maps; + u16 he_rx_mcs_maps; + u8 mac[ETH_ALEN]; + + u8 abg_rate_num; + u8 abg_rate[15]; + + u32 fw_bw_capa; /* reference SKW_BW_CAP_* */ + + u32 priv_filter_arp:1; + u32 priv_ignore_cred:1; + u32 priv_pn_reuse:1; + u32 priv_p2p_common_port:1; + u32 priv_dfs_master_enabled:1; + u32 priv_2g_only:1; + u32 priv_resv:18; + u32 nr_hw_mac:8; + + u8 fw_build_time[32]; + u8 fw_plat_ver[16]; + u8 fw_wifi_ver[16]; + u8 fw_bt_ver[16]; + + u32 fw_timestamp; + u32 fw_chip_type; + u16 calib_module_id; + u16 resv; + u32 fw_ext_capa[12]; +} __packed; + +enum SKW_MSG_VERSION {V0, V1, V2, V3}; + +#define SKW_MAX_MSG_ID 256 + +struct skw_version_info { + u8 cmd[SKW_MAX_MSG_ID]; + u8 event[SKW_MAX_MSG_ID]; +} __packed; + +struct skw_hw_extra { + u8 hdr_len; + u8 chn_offset; + u8 len_offset; + u8 eof_offset; +}; + +struct skw_fixed_offset { + s16 hdr_offset; + s16 msdu_offset; +}; + +#define SKW_HW_FLAG_EXTRA_HDR (0) +#define SKW_HW_FLAG_SDIO_V2 (1) +#define SKW_HW_FLAG_USB_V2 (2) +#define SKW_HW_FLAG_CFG80211_PM (3) + +typedef int (*hw_xmit_func)(struct skw_core *skw, struct sk_buff_head *list, + int lmac_id, int port, struct scatterlist *sgl, + int nents, int tx_bytes); + +typedef int (*bus_dat_xmit_func)(struct skw_core *skw, int lmac_id, + struct sk_buff_head *txq_list); + +typedef int (*bus_cmd_xmit_func)(struct skw_core *skw, void *cmd, int cmd_len); + +struct skw_hw_info { + u8 bus; + u8 dma; + u8 nr_lmac; + u8 cmd_port; + + u16 align; + s16 pkt_limit; + unsigned long flags; + atomic_t credit; /* total credit of all LMAC */ + + hw_xmit_func cmd_xmit; + hw_xmit_func cmd_disable_irq_xmit; + hw_xmit_func dat_xmit; + + bus_dat_xmit_func bus_dat_xmit; + bus_cmd_xmit_func bus_cmd_xmit; + + struct skw_hw_extra extra; + struct skw_fixed_offset rx_desc; + struct skw_lmac lmac[SKW_MAX_LMAC_SUPPORT]; + + struct { + bool enabled; + u16 flags; + } wow; +}; + +struct skw_vif { + u16 bitmap; + u16 opened_dev; + spinlock_t lock; + struct skw_iface *iface[SKW_NR_IFACE]; +}; + +struct skw_work_data { + spinlock_t rcu_lock; + struct rcu_head *rcu_hdr; + struct rcu_head **rcu_tail; + + unsigned long flags; + struct sk_buff_head work_list; +}; + +struct skw_timer_data { + int count; + spinlock_t lock; + struct list_head list; + struct timer_list timer; +}; + +struct skw_recovery_data { + struct mutex lock; + struct skw_recovery_ifdata iface[SKW_NR_IFACE]; + struct skw_peer *peer[SKW_MAX_LMAC_SUPPORT][SKW_MAX_PEER_SUPPORT]; +}; + +struct skw_dbg_cmd { + u16 id, seq; + u32 loop; + unsigned long flags; + u64 trigger, build, xmit, done, ack, assert; +}; + +struct skw_dbg_dat { + u32 qlen; + u32 resv; + u64 trigger, done; +}; + +struct skw_core { + struct sv6160_platform_data *hw_pdata; + + struct sk_buff_head rx_dat_q; + atomic_t txqlen_pending; + + atomic_t tx_wake, rx_wake, exit; + wait_queue_head_t tx_wait_q, rx_wait_q; + + struct net_device dummy_dev; + struct napi_struct napi_rx; + + struct task_struct *rx_thread; + +#ifdef CONFIG_SWT6621S_TX_WORKQUEUE + struct workqueue_struct *tx_wq; + struct delayed_work tx_worker; + + //struct workqueue_struct *rx_wq; + //struct work_struct rx_worker; +#else + struct task_struct *tx_thread, *rx_thread; +#endif + + /* workqueu for mlme worker and etc. */ + struct workqueue_struct *event_wq; + struct skw_event_work event_work; + + struct work_struct work; + struct skw_work_data work_data; + struct work_struct work_unlock; + + struct sk_buff_head kfree_skb_qlist; + struct work_struct kfree_skb_task; + + struct sk_buff_head skb_recycle_qlist; + + struct work_struct recovery_work; + struct skw_recovery_data recovery_data; + + struct mutex lock; + struct skw_firmware_info fw; + struct skw_hw_info hw; + struct skw_vif vif; + struct mac_address address[SKW_NR_IFACE]; + + unsigned long flags; /* reference SKW_FLAG_FW_ */ + unsigned long nf_flags; + + u8 country[2]; + u16 idx; + u16 skw_event_sn; + int isr_cpu_id; + + u16 nr_scan_results; + struct cfg80211_scan_request *scan_req; + struct cfg80211_sched_scan_request *sched_scan_req; + + struct notifier_block ifa4_nf; + struct notifier_block ifa6_nf; + struct notifier_block bsp_nf; + struct notifier_block pm_nf; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) + unsigned int num_iftype_ext_capab; + struct wiphy_iftype_ext_capab iftype_ext_cap[NUM_NL80211_IFTYPES]; +#endif + + struct skw_dpd dpd; +#ifdef CONFIG_SWT6621S_USB3_WORKAROUND + struct completion usb_switch_done; +#endif + + void *sdma_buff, *eof_blk; + struct scatterlist *sgl_cmd, *sgl_dat; + + u16 skb_headroom; + u16 skb_share_len; + + u8 ext_capa[12]; + unsigned long trans_start; + unsigned long rx_packets; + unsigned long tx_packets; + +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock rx_wlock; +#endif + spinlock_t rx_lock; + struct skw_list rx_todo_list; + + const struct ieee80211_regdomain *regd; + struct dentry *dentry; + struct proc_dir_entry *pentry; + + struct skw_timer_data timer_data; + struct skw_config config; + + struct { + struct semaphore lock; + struct semaphore mgmt_cmd_lock; + wait_queue_head_t wq; + struct wakeup_source *ws; + + unsigned long start_time; + void (*callback)(struct skw_core *skw); + + unsigned long flags; /* reference SKW_CMD_FLAG_ */ + + const char *name; + void *data; + void *arg; + + u16 data_len; + u16 arg_size; + + u16 seq; + u16 status; + + int id; + } cmd; + + struct { + struct skw_edma_chn cmd_chn; + struct skw_edma_chn short_event_chn; + struct skw_edma_chn long_event_chn; + + struct skw_edma_chn tx_chn[SKW_MAX_LMAC_SUPPORT]; + struct skw_edma_chn tx_resp_chn[SKW_MAX_LMAC_SUPPORT]; + struct skw_edma_chn rx_chn[SKW_MAX_LMAC_SUPPORT]; + struct skw_edma_chn rx_req_chn[SKW_MAX_LMAC_SUPPORT]; + struct skw_edma_chn filter_chn[SKW_MAX_LMAC_SUPPORT]; + } edma; + + struct { + bool fw_enabled; + u64 last_pulse_ts; + unsigned long flags; + + struct list_head skw_pulse_pool; + struct list_head skw_pseq_pool; + spinlock_t skw_pool_lock; + + enum nl80211_dfs_regions region; + struct cfg80211_chan_def chan; + const struct skw_radar_info *info; + } dfs; + + struct { + atomic_t loop; + u8 cmd_idx, dat_idx; + u8 nr_cmd, nr_dat; + struct skw_dbg_cmd cmd[SKW_DBG_NR_CMD]; + struct skw_dbg_dat dat[SKW_DBG_NR_DAT]; + } dbg; +}; + +struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +}; + +#ifdef CONFIG_COMPAT +struct compat_android_wifi_priv_cmd { + compat_caddr_t buf; + int used_len; + int total_len; +}; +#endif + +struct skw_calib_param { + u8 seq; + u8 end; + u16 len; + u8 data[512]; +} __packed; + +#define SKW_WIPHY_DENTRY(w) (((struct skw_core *)wiphy_priv(w))->dentry) +#define SKW_WIPHY_PENTRY(w) (((struct skw_core *)wiphy_priv(w))->pentry) + +static inline int skw_wifi_enable(void *pdata) +{ + struct sv6160_platform_data *pd = pdata; + + if (pd && pd->service_start) + return pd->service_start(); + + return -ENOTSUPP; +} + +static inline int skw_wifi_disable(void *pdata) +{ + struct sv6160_platform_data *pd = pdata; + + if (pd && pd->service_stop) + return pd->service_stop(); + + return -ENOTSUPP; +} + +static inline int skw_power_on_chip(void) +{ + return 0; +} + +static inline int skw_power_off_chip(void) +{ + return 0; +} + +static inline int skw_hw_reset_chip(struct skw_core *skw) +{ + return 0; +} + +static inline int skw_hw_get_chip_id(struct skw_core *skw) +{ + return 0; +} + +static inline struct skw_tx_cb *SKW_SKB_TXCB(struct sk_buff *skb) +{ + return (struct skw_tx_cb *)skb->cb; +} + +void skw_dbg_dump(struct skw_core *skw); +static inline int skw_hw_assert(struct skw_core *skw, bool dump) +{ + if (test_and_set_bit(SKW_FLAG_FW_ASSERT, &skw->flags)) + return 0; + + if (dump) + skw_dbg_dump(skw); + + if (skw->hw_pdata->modem_assert) + skw->hw_pdata->modem_assert(); + + return 0; +} + +static inline int skw_hw_request_ack(struct skw_core *skw) +{ + if (!skw->hw_pdata || !skw->hw_pdata->rx_thread_wakeup) + return -ENOTSUPP; + + skw->hw_pdata->rx_thread_wakeup(); + + return 0; +} + +static inline int skw_register_rx_cb(struct skw_core *skw, int port, + rx_submit_fn rx_cb, void *data) +{ + if (!skw->hw_pdata || !skw->hw_pdata->callback_register) + return -ENOTSUPP; + + return skw->hw_pdata->callback_register(port, (void *)rx_cb, data); +} + +static inline int skw_register_tx_cb(struct skw_core *skw, int port, + rx_submit_fn tx_cb, void *data) +{ + if (!skw->hw_pdata || !skw->hw_pdata->tx_callback_register) + return -ENOTSUPP; + + return skw->hw_pdata->tx_callback_register(port, (void *)tx_cb, data); +} + +static inline bool skw_need_extra_hdr(struct skw_core *skw) +{ + return test_bit(SKW_HW_FLAG_EXTRA_HDR, &skw->hw.flags); +} + +static inline void skw_set_extra_hdr(struct skw_core *skw, void *extra_hdr, + u8 chn, u16 len, u16 pad, u8 eof) +{ + u32 *hdr = extra_hdr; + struct skw_hw_extra *ext = &skw->hw.extra; + + *hdr = chn << ext->chn_offset | + (len - ext->hdr_len) << ext->len_offset | + (!!eof) << ext->eof_offset; +} + +static inline int skw_uart_open(struct skw_core *skw) +{ + u8 port; + + if (!skw->hw_pdata || !skw->hw_pdata->at_ops.open) + return -ENOTSUPP; + + if (test_bit(SKW_FLAG_FW_UART_OPEND, &skw->flags)) + return 0; + + port = skw->hw_pdata->at_ops.port; + + set_bit(SKW_FLAG_FW_UART_OPEND, &skw->flags); + + return skw->hw_pdata->at_ops.open(port, NULL, NULL); +} + +static inline int skw_uart_write(struct skw_core *skw, char *cmd, int len) +{ + u8 port; + + if (!skw->hw_pdata || !skw->hw_pdata->at_ops.write) + return -ENOTSUPP; + + port = skw->hw_pdata->at_ops.port; + + return skw->hw_pdata->at_ops.write(port, cmd, len); +} + +static inline int skw_uart_read(struct skw_core *skw, char *buf, int buf_len) +{ + u8 port; + + if (!skw->hw_pdata || !skw->hw_pdata->at_ops.read) + return -ENOTSUPP; + + port = skw->hw_pdata->at_ops.port; + + return skw->hw_pdata->at_ops.read(port, buf, buf_len); +} + +static inline int skw_uart_close(struct skw_core *skw) +{ + u8 port; + + if (!skw->hw_pdata || !skw->hw_pdata->at_ops.close) + return -ENOTSUPP; + + port = skw->hw_pdata->at_ops.port; + + return skw->hw_pdata->at_ops.close(port); +} + +static inline int skw_register_bsp_notifier(struct skw_core *skw, + struct notifier_block *nb) +{ + if (!skw->hw_pdata || !skw->hw_pdata->modem_register_notify) + return -ENOTSUPP; + + skw->hw_pdata->modem_register_notify(nb); + + return 0; +} + +static inline int skw_unregister_bsp_notifier(struct skw_core *skw, + struct notifier_block *nb) +{ + if (!skw->hw_pdata || !skw->hw_pdata->modem_unregister_notify) + return -ENOTSUPP; + + skw->hw_pdata->modem_unregister_notify(nb); + + return 0; +} + +static inline void skw_wakeup_tx(struct skw_core *skw, unsigned long delay) +{ +#ifdef CONFIG_SWT6621S_TX_WORKQUEUE + mod_delayed_work(skw->tx_wq, &skw->tx_worker, delay); +#else + if (atomic_add_return(1, &skw->tx_wake) == 1) + wake_up(&skw->tx_wait_q); +#endif +} + +static inline void skw_wakeup_rx(struct skw_core *skw) +{ + int i; + + //wake_up_process(skw->rx_thread); + if (skw->hw.bus == SKW_BUS_PCIE) { + for (i = 0; i < skw->hw.nr_lmac; i++) + if (skw->hw.lmac->iface_bitmap != 0) + napi_schedule(&skw->hw.lmac[i].napi_rx); + } else + wake_up_process(skw->rx_thread); + //napi_schedule(&skw->napi_rx); +} + +static inline struct skw_iface *to_skw_iface(struct skw_core *skw, int id) +{ + if (!skw || id & 0xfffffffc) + return NULL; + + return skw->vif.iface[id]; +} + +static inline void skw_sync_credit(struct skw_core *skw, int lmac_id) +{ + int new, done; + struct skw_edma_chn *chn; + struct skw_lmac *lmac = &skw->hw.lmac[lmac_id]; + + if (!skw->hw_pdata || !skw->hw_pdata->edma_get_node_tot_cnt) + return; + + chn = &skw->edma.tx_chn[lmac_id]; + done = skw->hw_pdata->edma_get_node_tot_cnt(SKW_EDMA_WIFI_TX0_CHN+lmac_id); + new = chn->max_node_num - atomic_read(&chn->nr_node) - done; + + atomic_add(new, &chn->nr_node); + atomic_set(&lmac->fw_credit, atomic_read(&chn->nr_node) * SKW_EDMA_TX_CHN_CREDIT); +} + +static inline int skw_get_hw_credit(struct skw_core *skw, int lmac_id) +{ + struct skw_lmac *lmac = &skw->hw.lmac[lmac_id]; + +#if 0 + if (!(lmac->flags & SKW_LMAC_FLAG_ACTIVED)) + return 0; +#endif + if (test_bit(SKW_FLAG_FW_IGNORE_CRED, &skw->flags)) + return INT_MAX; + + //for test + if (tx_wait_time == 99) + return INT_MAX; + + if (skw->hw.bus == SKW_BUS_PCIE) + skw_sync_credit(skw, lmac_id); + + return atomic_read(&lmac->fw_credit); +} + +static inline void skw_set_trans_start(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) + netdev_get_tx_queue(dev, i)->trans_start = jiffies; +} + +static inline void skw_start_dev_queue(struct skw_core *skw) +{ + int i; + struct skw_iface *iface; + + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + if (!iface || !iface->ndev) + continue; + if (iface->ndev->flags & IFF_UP) { + netif_tx_start_all_queues(iface->ndev); + skw_set_trans_start(iface->ndev); + netif_tx_schedule_all(iface->ndev); + } + } +} + +static inline void skw_stop_dev_queue(struct skw_core *skw) +{ + int i; + struct skw_iface *iface; + + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + if (!iface || !iface->ndev) + continue; + if (iface->ndev->flags & IFF_UP) { + netif_tx_stop_all_queues(iface->ndev); + smp_mb(); + } + } +} + +static inline void skw_sub_credit(struct skw_core *skw, int lmac_id, int used) +{ + smp_rmb(); + atomic_sub(used, &skw->hw.lmac[lmac_id].fw_credit); +} + +static inline bool is_skw_local_addr6(struct in6_addr *addr) +{ + return ipv6_addr_type(addr) & + (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); +} + +static inline dma_addr_t skw_pci_map_single(struct skw_core *skw, void *ptr, + size_t size, int direction) +{ + struct device *dev = priv_to_wiphy(skw)->dev.parent; + + return dma_map_single(dev, ptr, size, direction); +} + +static inline void skw_pci_unmap_single(struct skw_core *skw, + dma_addr_t dma_addr, size_t size, int direction) +{ + struct device *dev = priv_to_wiphy(skw)->dev.parent; + + return dma_unmap_single(dev, dma_addr, size, direction); +} + +static inline int +skw_pcie_mapping_error(struct skw_core *skw, dma_addr_t dma_addr) +{ + struct device *dev = priv_to_wiphy(skw)->dev.parent; + + return dma_mapping_error(dev, dma_addr); +} + +static inline bool skw_lmac_is_actived(struct skw_core *skw, int lmac_id) +{ + return (skw->hw.lmac[lmac_id].flags & SKW_LMAC_FLAG_ACTIVED); +} + +static inline const char *skw_bus_name(int bus) +{ + static const char name[][8] = {"sdio", "usb", "pcie", "null"}; + + return name[bus & 0x3]; +} + +struct skw_peer_ctx *skw_get_ctx(struct skw_core *skw, u8 lmac_id, u8 idx); +int skw_lmac_bind_iface(struct skw_core *skw, struct skw_iface *iface, int lmac_id); +int skw_lmac_unbind_iface(struct skw_core *skw, int lmac_id, int iface_id); +int skw_netdev_init(struct wiphy *wiphy, struct net_device *ndev, u8 *addr); +void skw_netdev_deinit(struct net_device *ndev); +void skw_add_credit(struct skw_core *skw, int lmac_id, int cred); +int skw_sync_chip_info(struct wiphy *wiphy, struct skw_chip_info *chip); +int skw_sync_cmd_event_version(struct wiphy *wiphy); +void skw_get_dev_ip(struct net_device *ndev); +void skw_set_ip_to_fw(struct wiphy *wiphy, struct net_device *ndev); +int skw_calib_download(struct wiphy *wiphy, const char *fname); +struct skw_ctx_entry *skw_get_ctx_entry(struct skw_core *skw, const u8 *addr); + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_db.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_db.c new file mode 100755 index 0000000..a299346 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_db.c @@ -0,0 +1,3417 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + ******************************************************************************/ + +/* + * DO NOT EDIT -- file generated from data in db.txt + */ + +#include <linux/nl80211.h> +#include <net/cfg80211.h> +#include "skw_db.h" + +// database 2024.10.07 + +static const struct ieee80211_regdomain regdom_XW = { + .alpha2 = "XW", + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 20, 0, 0), + REG_RULE_EXT(2457, 2482, 20, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(2474, 2494, 20, 0, 20, 0, + SKW_RRF_NO_OFDM | 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(57240, 63720, 2160, 0, 0, 0, 0), + }, + .n_reg_rules = 8 +}; + +static const struct ieee80211_regdomain regdom_00 = { + .alpha2 = "00", + .reg_rules = { + REG_RULE_EXT(755, 928, 2, 0, 20, 0, + SKW_RRF_NO_IR | 0), + REG_RULE_EXT(2402, 2472, 40, 0, 20, 0, 0), + REG_RULE_EXT(2457, 2482, 20, 0, 20, 0, + SKW_RRF_NO_IR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(2474, 2494, 20, 0, 20, 0, + SKW_RRF_NO_IR | + SKW_RRF_NO_OFDM | 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_NO_IR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_NO_IR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 20, 0, + SKW_RRF_NO_IR | + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, + SKW_RRF_NO_IR | 0), + REG_RULE_EXT(57240, 63720, 2160, 0, 0, 0, 0), + }, + .n_reg_rules = 9 +}; + +static const struct ieee80211_regdomain regdom_AD = { + .alpha2 = "AD", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_AE = { + .alpha2 = "AE", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_AF = { + .alpha2 = "AF", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_AI = { + .alpha2 = "AI", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_AL = { + .alpha2 = "AL", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_AM = { + .alpha2 = "AM", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 20, 0, 18, 0, 0), + REG_RULE_EXT(5250, 5330, 20, 0, 18, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 3 +}; + +static const struct ieee80211_regdomain regdom_AN = { + .alpha2 = "AN", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_AR = { + .alpha2 = "AR", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 7125, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_AS = { + .alpha2 = "AS", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_AT = { + .alpha2 = "AT", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_AU = { + .alpha2 = "AU", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(915, 920, 4, 0, 30, 0, 0), + REG_RULE_EXT(920, 928, 8, 0, 30, 0, 0), + REG_RULE_EXT(2400, 2483, 40, 0, 36, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | + SKW_RRF_DFS | 0), + REG_RULE_EXT(5470, 5600, 80, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5650, 5730, 80, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5730, 5850, 80, 0, 36, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5850, 5875, 20, 0, 14, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5925, 6425, 160, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 71000, 2160, 0, 43, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 11 +}; + +static const struct ieee80211_regdomain regdom_AW = { + .alpha2 = "AW", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_AZ = { + .alpha2 = "AZ", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 18, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 18, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + }, + .n_reg_rules = 3 +}; + +static const struct ieee80211_regdomain regdom_BA = { + .alpha2 = "BA", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_BB = { + .alpha2 = "BB", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_BD = { + .alpha2 = "BD", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 2 +}; + +static const struct ieee80211_regdomain regdom_BE = { + .alpha2 = "BE", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_BF = { + .alpha2 = "BF", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_BG = { + .alpha2 = "BG", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_BH = { + .alpha2 = "BH", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5350, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5470, 5725, 80, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_BL = { + .alpha2 = "BL", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_BM = { + .alpha2 = "BM", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_BN = { + .alpha2 = "BN", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_BO = { + .alpha2 = "BO", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5250, 5330, 80, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 3 +}; + +static const struct ieee80211_regdomain regdom_BR = { + .alpha2 = "BR", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 30, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 27, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 27, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 30, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5925, 7125, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_NO_IR | 0), + REG_RULE_EXT(57000, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_BS = { + .alpha2 = "BS", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_BT = { + .alpha2 = "BT", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_BY = { + .alpha2 = "BY", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_BZ = { + .alpha2 = "BZ", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 2 +}; + +static const struct ieee80211_regdomain regdom_CA = { + .alpha2 = "CA", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5600, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5650, 5730, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 7125, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_CF = { + .alpha2 = "CF", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 40, 0, 17, 0, 0), + REG_RULE_EXT(5250, 5330, 40, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5490, 5730, 40, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 40, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_CH = { + .alpha2 = "CH", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_CI = { + .alpha2 = "CI", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_CL = { + .alpha2 = "CL", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_CN = { + .alpha2 = "CN", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 33, 0, 0), + REG_RULE_EXT(57240, 59400, 2160, 0, 28, 0, 0), + REG_RULE_EXT(59400, 63720, 2160, 0, 44, 0, 0), + REG_RULE_EXT(63720, 65880, 2160, 0, 28, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_CO = { + .alpha2 = "CO", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 7125, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_CR = { + .alpha2 = "CR", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 36, 0, 0), + REG_RULE_EXT(5170, 5250, 20, 0, 30, 0, 0), + REG_RULE_EXT(5250, 5330, 20, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5490, 5730, 20, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 20, 0, 36, 0, 0), + REG_RULE_EXT(5875, 5925, 20, 0, 30, 0, 0), + REG_RULE_EXT(5925, 7125, 320, 0, 30, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_CU = { + .alpha2 = "CU", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 23, 0, 0), + REG_RULE_EXT(5150, 5350, 80, 0, 23, 0, + SKW_RRF_NO_IR | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5470, 5725, 80, 0, 24, 0, + SKW_RRF_NO_IR | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 23, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_CX = { + .alpha2 = "CX", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_CY = { + .alpha2 = "CY", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_CZ = { + .alpha2 = "CZ", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_DE = { + .alpha2 = "DE", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_DK = { + .alpha2 = "DK", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_DM = { + .alpha2 = "DM", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_DO = { + .alpha2 = "DO", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 7125, 320, 0, 15, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_DZ = { + .alpha2 = "DZ", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5670, 160, 0, 23, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_EC = { + .alpha2 = "EC", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 30, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | + SKW_RRF_DFS | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 21, 0, + SKW_RRF_AUTO_BW | + SKW_RRF_DFS | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 21, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_EE = { + .alpha2 = "EE", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_EG = { + .alpha2 = "EG", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_ES = { + .alpha2 = "ES", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_ET = { + .alpha2 = "ET", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_FI = { + .alpha2 = "FI", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_FM = { + .alpha2 = "FM", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_FR = { + .alpha2 = "FR", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_GB = { + .alpha2 = "GB", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5730, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5925, 6425, 160, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_GD = { + .alpha2 = "GD", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_GE = { + .alpha2 = "GE", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 18, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 18, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_GF = { + .alpha2 = "GF", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_GH = { + .alpha2 = "GH", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_GL = { + .alpha2 = "GL", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_GP = { + .alpha2 = "GP", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_GR = { + .alpha2 = "GR", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_GT = { + .alpha2 = "GT", + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 27, 0, 0), + REG_RULE_EXT(5150, 5350, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5725, 5850, 160, 0, 27, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(6425, 6525, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(6525, 6875, 320, 0, 22, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(6875, 7125, 320, 0, 22, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 13, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 9 +}; + +static const struct ieee80211_regdomain regdom_GU = { + .alpha2 = "GU", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 20, 0, 17, 0, 0), + REG_RULE_EXT(5250, 5330, 20, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5490, 5730, 20, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 20, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_GY = { + .alpha2 = "GY", + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 23, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_HK = { + .alpha2 = "HK", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 36, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5470, 5730, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5730, 5850, 80, 0, 36, 0, 0), + REG_RULE_EXT(5925, 6425, 160, 0, 14, 0, 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_HN = { + .alpha2 = "HN", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_NO_IR | 0), + REG_RULE_EXT(57240, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_HR = { + .alpha2 = "HR", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_HT = { + .alpha2 = "HT", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_HU = { + .alpha2 = "HU", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_ID = { + .alpha2 = "ID", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 27, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5150, 5350, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5725, 5825, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 3 +}; + +static const struct ieee80211_regdomain regdom_IE = { + .alpha2 = "IE", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_IL = { + .alpha2 = "IL", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5945, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_IN = { + .alpha2 = "IN", + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 30, 0, 0), + REG_RULE_EXT(5250, 5350, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_IR = { + .alpha2 = "IR", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 2 +}; + +static const struct ieee80211_regdomain regdom_IS = { + .alpha2 = "IS", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_IT = { + .alpha2 = "IT", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_JM = { + .alpha2 = "JM", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_JO = { + .alpha2 = "JO", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 80, 0, 27, 0, + SKW_RRF_DFS | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_JP = { + .alpha2 = "JP", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(2474, 2494, 20, 0, 20, 0, + SKW_RRF_NO_OFDM | 0), + REG_RULE_EXT(4910, 4990, 40, 0, 23, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 23, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 10, 0, 0), + }, + .n_reg_rules = 8 +}; + +static const struct ieee80211_regdomain regdom_KE = { + .alpha2 = "KE", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 33, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 17, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 40, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_KH = { + .alpha2 = "KH", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_KN = { + .alpha2 = "KN", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_KP = { + .alpha2 = "KP", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 20, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 20, 0, 20, 0, 0), + REG_RULE_EXT(5250, 5330, 20, 0, 20, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5490, 5630, 20, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5815, 20, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_KR = { + .alpha2 = "KR", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 23, 0, 0), + REG_RULE_EXT(5150, 5230, 40, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5230, 5250, 20, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 20, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 23, 0, 0), + REG_RULE_EXT(5925, 7125, 160, 0, 15, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 43, 0, 0), + }, + .n_reg_rules = 8 +}; + +static const struct ieee80211_regdomain regdom_KW = { + .alpha2 = "KW", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 17, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5825, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_KY = { + .alpha2 = "KY", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_KZ = { + .alpha2 = "KZ", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_LB = { + .alpha2 = "LB", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_LC = { + .alpha2 = "LC", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_LI = { + .alpha2 = "LI", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_LK = { + .alpha2 = "LK", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 20, 0, 17, 0, 0), + REG_RULE_EXT(5250, 5330, 20, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5490, 5730, 20, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 20, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_LS = { + .alpha2 = "LS", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_LT = { + .alpha2 = "LT", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_LU = { + .alpha2 = "LU", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_LV = { + .alpha2 = "LV", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_MA = { + .alpha2 = "MA", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_MC = { + .alpha2 = "MC", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_MD = { + .alpha2 = "MD", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_ME = { + .alpha2 = "ME", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_MF = { + .alpha2 = "MF", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_MH = { + .alpha2 = "MH", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_MK = { + .alpha2 = "MK", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_MN = { + .alpha2 = "MN", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 100, 0, 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_MO = { + .alpha2 = "MO", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 23, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5730, 160, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_MP = { + .alpha2 = "MP", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_MQ = { + .alpha2 = "MQ", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_MR = { + .alpha2 = "MR", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_MT = { + .alpha2 = "MT", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_MU = { + .alpha2 = "MU", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5945, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_MV = { + .alpha2 = "MV", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 20, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_MW = { + .alpha2 = "MW", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_MX = { + .alpha2 = "MX", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_MY = { + .alpha2 = "MY", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 27, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 30, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 30, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5650, 160, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_NA = { + .alpha2 = "NA", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 21, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_NG = { + .alpha2 = "NG", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5250, 5330, 80, 0, 30, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 3 +}; + +static const struct ieee80211_regdomain regdom_NI = { + .alpha2 = "NI", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_NL = { + .alpha2 = "NL", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_NO = { + .alpha2 = "NO", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_NP = { + .alpha2 = "NP", + .dfs_region = NL80211_DFS_JP, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_NZ = { + .alpha2 = "NZ", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 36, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 30, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 27, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5730, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 36, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_OM = { + .alpha2 = "OM", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_PA = { + .alpha2 = "PA", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 36, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 36, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 30, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 30, 0, 0), + REG_RULE_EXT(5725, 5850, 80, 0, 36, 0, 0), + REG_RULE_EXT(57000, 64000, 2160, 0, 43, 0, 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_PE = { + .alpha2 = "PE", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 7125, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_PF = { + .alpha2 = "PF", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_PG = { + .alpha2 = "PG", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_PH = { + .alpha2 = "PH", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 24, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 24, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_PK = { + .alpha2 = "PK", + .reg_rules = { + REG_RULE_EXT(2400, 2500, 40, 0, 30, 0, 0), + REG_RULE_EXT(5725, 5875, 80, 0, 30, 0, 0), + REG_RULE_EXT(5945, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 3 +}; + +static const struct ieee80211_regdomain regdom_PL = { + .alpha2 = "PL", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_PM = { + .alpha2 = "PM", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_PR = { + .alpha2 = "PR", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_PT = { + .alpha2 = "PT", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_PW = { + .alpha2 = "PW", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_PY = { + .alpha2 = "PY", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_QA = { + .alpha2 = "QA", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_RE = { + .alpha2 = "RE", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_RO = { + .alpha2 = "RO", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_RS = { + .alpha2 = "RS", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5850, 5875, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 8 +}; + +static const struct ieee80211_regdomain regdom_RU = { + .alpha2 = "RU", + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 100, 0, 0), + REG_RULE_EXT(5150, 5350, 160, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5650, 5850, 160, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5925, 6425, 160, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_RW = { + .alpha2 = "RW", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_SA = { + .alpha2 = "SA", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5925, 7125, 320, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_SE = { + .alpha2 = "SE", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_SG = { + .alpha2 = "SG", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 23, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5730, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 30, 0, 0), + REG_RULE_EXT(5945, 6425, 320, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_SI = { + .alpha2 = "SI", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_SK = { + .alpha2 = "SK", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5875, 80, 0, 14, 0, 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_SN = { + .alpha2 = "SN", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_SR = { + .alpha2 = "SR", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_SV = { + .alpha2 = "SV", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 20, 0, 17, 0, 0), + REG_RULE_EXT(5250, 5330, 20, 0, 23, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 20, 0, 30, 0, 0), + REG_RULE_EXT(5925, 7125, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_SY = { + .alpha2 = "SY", + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + }, + .n_reg_rules = 1 +}; + +static const struct ieee80211_regdomain regdom_TC = { + .alpha2 = "TC", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_TD = { + .alpha2 = "TD", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_TG = { + .alpha2 = "TG", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5150, 5350, 80, 0, 20, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5470, 5850, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5925, 6425, 320, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 0, 0, 0, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_TH = { + .alpha2 = "TH", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 24, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_TN = { + .alpha2 = "TN", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + }, + .n_reg_rules = 3 +}; + +static const struct ieee80211_regdomain regdom_TR = { + .alpha2 = "TR", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5945, 6425, 160, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_TT = { + .alpha2 = "TT", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_TW = { + .alpha2 = "TW", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 30, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5730, 160, 0, 23, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 30, 0, 0), + REG_RULE_EXT(5945, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 7 +}; + +static const struct ieee80211_regdomain regdom_TZ = { + .alpha2 = "TZ", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5945, 6425, 320, 0, 23, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_UA = { + .alpha2 = "UA", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5150, 5250, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5725, 160, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(5725, 5850, 80, 0, 20, 0, + SKW_RRF_NO_OUTDOOR | 0), + REG_RULE_EXT(57000, 66000, 2160, 0, 16, 0, + SKW_RRF_NO_OUTDOOR | 0), + }, + .n_reg_rules = 6 +}; + +static const struct ieee80211_regdomain regdom_UG = { + .alpha2 = "UG", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_US = { + .alpha2 = "US", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(902, 904, 2, 0, 30, 0, 0), + REG_RULE_EXT(904, 920, 16, 0, 30, 0, 0), + REG_RULE_EXT(920, 928, 8, 0, 30, 0, 0), + REG_RULE_EXT(2400, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5150, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5350, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5470, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5730, 5850, 80, 0, 30, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5850, 5895, 40, 0, 27, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_AUTO_BW | + SKW_RRF_NO_IR | 0), + REG_RULE_EXT(5925, 7125, 320, 0, 12, 0, + SKW_RRF_NO_OUTDOOR | + SKW_RRF_NO_IR | 0), + REG_RULE_EXT(57240, 71000, 2160, 0, 40, 0, 0), + }, + .n_reg_rules = 11 +}; + +static const struct ieee80211_regdomain regdom_UY = { + .alpha2 = "UY", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_UZ = { + .alpha2 = "UZ", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + }, + .n_reg_rules = 3 +}; + +static const struct ieee80211_regdomain regdom_VC = { + .alpha2 = "VC", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_VE = { + .alpha2 = "VE", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 23, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_VI = { + .alpha2 = "VI", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 24, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_VN = { + .alpha2 = "VN", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5490, 5730, 80, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_VU = { + .alpha2 = "VU", + .dfs_region = NL80211_DFS_FCC, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 24, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5730, 160, 0, 24, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_WF = { + .alpha2 = "WF", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_WS = { + .alpha2 = "WS", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 40, 0, 20, 0, 0), + REG_RULE_EXT(5250, 5330, 40, 0, 20, 0, + SKW_RRF_DFS | 0), + REG_RULE_EXT(5490, 5710, 40, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_YE = { + .alpha2 = "YE", + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + }, + .n_reg_rules = 1 +}; + +static const struct ieee80211_regdomain regdom_YT = { + .alpha2 = "YT", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +static const struct ieee80211_regdomain regdom_ZA = { + .alpha2 = "ZA", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 30, 0, 0), + REG_RULE_EXT(5925, 6425, 320, 0, 14, 0, 0), + }, + .n_reg_rules = 5 +}; + +static const struct ieee80211_regdomain regdom_ZW = { + .alpha2 = "ZW", + .dfs_region = NL80211_DFS_ETSI, + .reg_rules = { + REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0), + REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5250, 5330, 80, 0, 20, 0, + SKW_RRF_DFS | + SKW_RRF_AUTO_BW | 0), + REG_RULE_EXT(5490, 5710, 160, 0, 27, 0, + SKW_RRF_DFS | 0), + }, + .n_reg_rules = 4 +}; + +const struct ieee80211_regdomain *skw_regdb[] = { + ®dom_XW, + ®dom_00, + ®dom_AD, + ®dom_AE, + ®dom_AF, + ®dom_AI, + ®dom_AL, + ®dom_AM, + ®dom_AN, + ®dom_AR, + ®dom_AS, + ®dom_AT, + ®dom_AU, + ®dom_AW, + ®dom_AZ, + ®dom_BA, + ®dom_BB, + ®dom_BD, + ®dom_BE, + ®dom_BF, + ®dom_BG, + ®dom_BH, + ®dom_BL, + ®dom_BM, + ®dom_BN, + ®dom_BO, + ®dom_BR, + ®dom_BS, + ®dom_BT, + ®dom_BY, + ®dom_BZ, + ®dom_CA, + ®dom_CF, + ®dom_CH, + ®dom_CI, + ®dom_CL, + ®dom_CN, + ®dom_CO, + ®dom_CR, + ®dom_CU, + ®dom_CX, + ®dom_CY, + ®dom_CZ, + ®dom_DE, + ®dom_DK, + ®dom_DM, + ®dom_DO, + ®dom_DZ, + ®dom_EC, + ®dom_EE, + ®dom_EG, + ®dom_ES, + ®dom_ET, + ®dom_FI, + ®dom_FM, + ®dom_FR, + ®dom_GB, + ®dom_GD, + ®dom_GE, + ®dom_GF, + ®dom_GH, + ®dom_GL, + ®dom_GP, + ®dom_GR, + ®dom_GT, + ®dom_GU, + ®dom_GY, + ®dom_HK, + ®dom_HN, + ®dom_HR, + ®dom_HT, + ®dom_HU, + ®dom_ID, + ®dom_IE, + ®dom_IL, + ®dom_IN, + ®dom_IR, + ®dom_IS, + ®dom_IT, + ®dom_JM, + ®dom_JO, + ®dom_JP, + ®dom_KE, + ®dom_KH, + ®dom_KN, + ®dom_KP, + ®dom_KR, + ®dom_KW, + ®dom_KY, + ®dom_KZ, + ®dom_LB, + ®dom_LC, + ®dom_LI, + ®dom_LK, + ®dom_LS, + ®dom_LT, + ®dom_LU, + ®dom_LV, + ®dom_MA, + ®dom_MC, + ®dom_MD, + ®dom_ME, + ®dom_MF, + ®dom_MH, + ®dom_MK, + ®dom_MN, + ®dom_MO, + ®dom_MP, + ®dom_MQ, + ®dom_MR, + ®dom_MT, + ®dom_MU, + ®dom_MV, + ®dom_MW, + ®dom_MX, + ®dom_MY, + ®dom_NA, + ®dom_NG, + ®dom_NI, + ®dom_NL, + ®dom_NO, + ®dom_NP, + ®dom_NZ, + ®dom_OM, + ®dom_PA, + ®dom_PE, + ®dom_PF, + ®dom_PG, + ®dom_PH, + ®dom_PK, + ®dom_PL, + ®dom_PM, + ®dom_PR, + ®dom_PT, + ®dom_PW, + ®dom_PY, + ®dom_QA, + ®dom_RE, + ®dom_RO, + ®dom_RS, + ®dom_RU, + ®dom_RW, + ®dom_SA, + ®dom_SE, + ®dom_SG, + ®dom_SI, + ®dom_SK, + ®dom_SN, + ®dom_SR, + ®dom_SV, + ®dom_SY, + ®dom_TC, + ®dom_TD, + ®dom_TG, + ®dom_TH, + ®dom_TN, + ®dom_TR, + ®dom_TT, + ®dom_TW, + ®dom_TZ, + ®dom_UA, + ®dom_UG, + ®dom_US, + ®dom_UY, + ®dom_UZ, + ®dom_VC, + ®dom_VE, + ®dom_VI, + ®dom_VN, + ®dom_VU, + ®dom_WF, + ®dom_WS, + ®dom_YE, + ®dom_YT, + ®dom_ZA, + ®dom_ZW, +}; + +int skw_regdb_size = ARRAY_SIZE(skw_regdb); diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_db.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_db.h new file mode 100755 index 0000000..9d5d0d8 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_db.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_DB_H__ +#define __SKW_DB_H__ + +#include <linux/version.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) +#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags) \ + REG_RULE(start, end, bw, gain, eirp, reg_flags) +#endif + +#define SKW_RRF_NO_OFDM BIT(0) +#define SKW_RRF_NO_OUTDOOR BIT(3) +#define SKW_RRF_DFS BIT(4) +#define SKW_RRF_NO_IR BIT(7) +#define SKW_RRF_AUTO_BW BIT(11) + +extern int skw_regdb_size; +extern const struct ieee80211_regdomain *skw_regdb[]; + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dentry.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dentry.c new file mode 100755 index 0000000..708329f --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dentry.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <generated/utsrelease.h> +#include "skw_core.h" +#include "skw_dentry.h" +#include "skw_compat.h" +#include "version.h" + +static struct dentry *skw_debugfs_root; +static struct proc_dir_entry *skw_proc_root; + + +static int skw_proc_show(struct seq_file *seq, void *v) +{ +#define SKW_CONFIG_INT(conf) seq_printf(seq, "%s=%d\n", #conf, conf) +#define SKW_CONFIG_STRING(conf) seq_printf(seq, "%s=\"%s\"\n", #conf, conf) + +#define SKW_CONFIG_BOOL(conf) \ + do { \ + if (IS_ENABLED(conf)) \ + seq_printf(seq, "%s=y\n", #conf); \ + else \ + seq_printf(seq, "# %s is not set\n", #conf); \ + } while (0) + + + seq_puts(seq, "\n"); + seq_printf(seq, "Kernel Version: \t%s\n" + "Wi-Fi Driver: \t%s\n" + "Wi-Fi Branch: \t%s\n", + UTS_RELEASE, + SKW_VERSION, + SKW_BRANCH); + + seq_puts(seq, "\n"); + + SKW_CONFIG_BOOL(CONFIG_SWT6621S_STA_SME_EXT); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_SAP_SME_EXT); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_SCAN_RANDOM_MAC); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_LEGACY_P2P); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_TX_WORKQUEUE); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_HIGH_PRIORITY); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_VENDOR); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_REGD_SELF_MANAGED); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_TDLS); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_DFS_MASTER); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_REPEATER_MODE); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_EDMA); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_OFFCHAN_TX); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_CALIB_DPD); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_CALIB_APPEND_BUS_ID); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_6GHZ); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_USB3_WORKAROUND); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_LOG_ERROR); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_LOG_WARN); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_LOG_INFO); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_LOG_DEBUG); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_LOG_DETAIL); + SKW_CONFIG_BOOL(CONFIG_SWT6621S_SKB_RECYCLE); + +#ifdef CONFIG_SWT6621S_RX_REORDER_TIMEOUT + SKW_CONFIG_INT(CONFIG_SWT6621S_RX_REORDER_TIMEOUT); +#endif + +#ifdef CONFIG_SWT6621S_PROJECT_NAME + SKW_CONFIG_STRING(CONFIG_SWT6621S_PROJECT_NAME); +#endif + +#ifdef CONFIG_SWT6621S_DEFAULT_COUNTRY + SKW_CONFIG_STRING(CONFIG_SWT6621S_DEFAULT_COUNTRY); +#endif + +#ifdef CONFIG_SWT6621S_CHIP_ID + SKW_CONFIG_STRING(CONFIG_SWT6621S_CHIP_ID); +#endif + + seq_puts(seq, "\n"); + + return 0; +} + +static int skw_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_proc_show, NULL); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_proc_fops = { + .proc_open = skw_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_proc_fops = { + .open = skw_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +struct dentry *skw_debugfs_subdir(const char *name, struct dentry *parent) +{ + struct dentry *de, *pentry; + + pentry = parent ? parent : skw_debugfs_root; + if (!pentry) + return NULL; + + de = debugfs_create_dir(name, pentry); + + return IS_ERR(de) ? NULL : de; +} + +struct dentry *skw_debugfs_file(struct dentry *parent, + const char *name, umode_t mode, + const struct file_operations *fops, void *data) +{ + struct dentry *de, *pentry; + + pentry = parent ? parent : skw_debugfs_root; + if (!pentry) + return NULL; + + de = debugfs_create_file(name, mode, pentry, data, fops); + + return IS_ERR(de) ? NULL : de; +} + +struct proc_dir_entry *skw_procfs_subdir(const char *name, + struct proc_dir_entry *parent) +{ + struct proc_dir_entry *dentry = parent ? parent : skw_proc_root; + + if (!dentry) + return NULL; + + return proc_mkdir_data(name, 0, dentry, NULL); +} + +struct proc_dir_entry *skw_procfs_file(struct proc_dir_entry *parent, + const char *name, umode_t mode, + const void *fops, void *data) +{ + struct proc_dir_entry *dentry = parent ? parent : skw_proc_root; + + if (!dentry) + return NULL; + + return proc_create_data(name, mode, dentry, fops, data); +} + +int skw_dentry_init(void) +{ + skw_proc_root = proc_mkdir("skwifid", NULL); + if (!skw_proc_root) + pr_err("creat proc skwifid failed\n"); + + skw_procfs_file(skw_proc_root, "profile", 0, &skw_proc_fops, NULL); + + skw_debugfs_root = debugfs_create_dir("skwifid", NULL); + if (IS_ERR(skw_debugfs_root)) + skw_debugfs_root = NULL; + + return 0; +} + +void skw_dentry_deinit(void) +{ + debugfs_remove_recursive(skw_debugfs_root); + proc_remove(skw_proc_root); +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dentry.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dentry.h new file mode 100755 index 0000000..d06dd12 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dentry.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_DENTRY_H__ +#define __SKW_DENTRY_H__ + +#include <linux/fs.h> +#include <linux/debugfs.h> +#include <linux/proc_fs.h> + +static inline void skw_remove_debugfs(struct dentry *dentry) +{ + debugfs_remove(dentry); +} + +struct dentry *skw_debugfs_subdir(const char *name, struct dentry *parent); +struct dentry *skw_debugfs_file(struct dentry *parent, + const char *name, umode_t mode, + const struct file_operations *fops, void *data); +struct proc_dir_entry *skw_procfs_subdir(const char *name, + struct proc_dir_entry *parent); +struct proc_dir_entry *skw_procfs_file(struct proc_dir_entry *parent, + const char *name, umode_t mode, + const void *proc_fops, void *data); +int skw_dentry_init(void); +void skw_dentry_deinit(void); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dfs.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dfs.c new file mode 100755 index 0000000..f7db0bd --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dfs.c @@ -0,0 +1,729 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/kernel.h> +#include <net/cfg80211.h> + +#include "skw_dfs.h" +#include "skw_util.h" +#include "skw_cfg80211.h" + +#define SKW_MIN_PPB_THRESH 50 +#define SKW_PPB_THRESH_RATE(PPB, RATE) ((PPB * RATE + 100 - RATE) / 100) +#define SKW_PPB_THRESH(PPB) SKW_PPB_THRESH_RATE(PPB, SKW_MIN_PPB_THRESH) +#define SKW_PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF) + +/* percentage of pulse width tolerance */ +#define SKW_WIDTH_TOLERANCE 5 +#define SKW_WIDTH_LOWER(W) ((W * (100 - SKW_WIDTH_TOLERANCE) + 50) / 100) +#define SKW_WIDTH_UPPER(W) ((W * (100 + SKW_WIDTH_TOLERANCE) + 50) / 100) + +#define SKW_ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, CHIRP) \ +{ \ + .rule = { \ + .type_id = ID, \ + .width_min = SKW_WIDTH_LOWER(WMIN), \ + .width_max = SKW_WIDTH_UPPER(WMAX), \ + .pri_min = (SKW_PRF2PRI(PMAX) - SKW_PRI_TOLERANCE), \ + .pri_max = (SKW_PRF2PRI(PMIN) * PRF + SKW_PRI_TOLERANCE), \ + .nr_pri = PRF, \ + .ppb = PPB * PRF, \ + .ppb_thresh = SKW_PPB_THRESH(PPB), \ + .max_pri_tolerance = SKW_PRI_TOLERANCE, \ + .chirp = CHIRP \ + } \ +} + +/* radar types as defined by ETSI EN-301-893 v1.5.1 */ +static struct skw_radar_cfg skw_radar_etsi_cfgs[] = { + SKW_ETSI_PATTERN(0, 0, 1, 700, 700, 1, 18, false), + SKW_ETSI_PATTERN(1, 0, 5, 200, 1000, 1, 10, false), + SKW_ETSI_PATTERN(2, 0, 15, 200, 1600, 1, 15, false), + SKW_ETSI_PATTERN(3, 0, 15, 2300, 4000, 1, 25, false), + SKW_ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20, false), + SKW_ETSI_PATTERN(5, 0, 2, 300, 400, 3, 10, false), + SKW_ETSI_PATTERN(6, 0, 2, 400, 1200, 3, 15, false), +}; + +#define SKW_FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, CHIRP) \ +{ \ + .rule = { \ + .type_id = ID, \ + .width_min = SKW_WIDTH_LOWER(WMIN), \ + .width_max = SKW_WIDTH_UPPER(WMAX), \ + .pri_min = PMIN - SKW_PRI_TOLERANCE, \ + .pri_max = PMAX * PRF + SKW_PRI_TOLERANCE, \ + .nr_pri = PRF, \ + .ppb = PPB * PRF, \ + .ppb_thresh = SKW_PPB_THRESH(PPB), \ + .max_pri_tolerance = SKW_PRI_TOLERANCE, \ + .chirp = CHIRP \ + } \ +} + +static struct skw_radar_cfg skw_radar_fcc_cfgs[] = { + SKW_FCC_PATTERN(0, 0, 1, 1428, 1428, 1, 18, false), + SKW_FCC_PATTERN(101, 0, 1, 518, 938, 1, 57, false), + SKW_FCC_PATTERN(102, 0, 1, 938, 2000, 1, 27, false), + SKW_FCC_PATTERN(103, 0, 1, 2000, 3066, 1, 18, false), + SKW_FCC_PATTERN(2, 0, 5, 150, 230, 1, 23, false), + SKW_FCC_PATTERN(3, 6, 10, 200, 500, 1, 16, false), + SKW_FCC_PATTERN(4, 11, 20, 200, 500, 1, 12, false), + SKW_FCC_PATTERN(5, 50, 100, 1000, 2000, 1, 1, true), + SKW_FCC_PATTERN(6, 0, 1, 333, 333, 1, 9, false), +}; + +#define SKW_JP_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, RATE, CHIRP) \ +{ \ + .rule = { \ + .type_id = ID, \ + .width_min = SKW_WIDTH_LOWER(WMIN), \ + .width_max = SKW_WIDTH_UPPER(WMAX), \ + .pri_min = PMIN - SKW_PRI_TOLERANCE, \ + .pri_max = PMAX * PRF + SKW_PRI_TOLERANCE, \ + .nr_pri = PRF, \ + .ppb = PPB * PRF, \ + .ppb_thresh = SKW_PPB_THRESH_RATE(PPB, RATE), \ + .max_pri_tolerance = SKW_PRI_TOLERANCE, \ + .chirp = CHIRP, \ + } \ +} + +static struct skw_radar_cfg skw_radar_jp_cfgs[] = { + SKW_JP_PATTERN(0, 0, 1, 1428, 1428, 1, 18, 29, false), + SKW_JP_PATTERN(1, 2, 3, 3846, 3846, 1, 18, 29, false), + SKW_JP_PATTERN(2, 0, 1, 1388, 1388, 1, 18, 50, false), + SKW_JP_PATTERN(3, 0, 4, 4000, 4000, 1, 18, 50, false), + SKW_JP_PATTERN(4, 0, 5, 150, 230, 1, 23, 50, false), + SKW_JP_PATTERN(5, 6, 10, 200, 500, 1, 16, 50, false), + SKW_JP_PATTERN(6, 11, 20, 200, 500, 1, 12, 50, false), + SKW_JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, true), + SKW_JP_PATTERN(5, 0, 1, 333, 333, 1, 9, 50, false), +}; + +static const struct skw_radar_info skw_radar_infos[] = { + [NL80211_DFS_UNSET] = { + .nr_cfg = 0, + .cfgs = NULL, + }, + [NL80211_DFS_FCC] = { + .nr_cfg = ARRAY_SIZE(skw_radar_fcc_cfgs), + .cfgs = skw_radar_fcc_cfgs, + }, + [NL80211_DFS_ETSI] = { + .nr_cfg = ARRAY_SIZE(skw_radar_etsi_cfgs), + .cfgs = skw_radar_etsi_cfgs, + }, + [NL80211_DFS_JP] = { + .nr_cfg = ARRAY_SIZE(skw_radar_jp_cfgs), + .cfgs = skw_radar_jp_cfgs, + }, +}; + +static void pool_put_pseq_elem(struct skw_core *skw, struct skw_pri_sequence *pse) +{ + spin_lock_bh(&skw->dfs.skw_pool_lock); + + list_add(&pse->head, &skw->dfs.skw_pseq_pool); + + spin_unlock_bh(&skw->dfs.skw_pool_lock); +} + +static void pool_put_pulse_elem(struct skw_core *skw, struct skw_pulse_elem *pe) +{ + spin_lock_bh(&skw->dfs.skw_pool_lock); + + list_add(&pe->head, &skw->dfs.skw_pulse_pool); + + spin_unlock_bh(&skw->dfs.skw_pool_lock); +} + +static struct skw_pulse_elem *pool_get_pulse_elem(struct skw_core *skw) +{ + struct skw_pulse_elem *pe = NULL; + + spin_lock_bh(&skw->dfs.skw_pool_lock); + + if (!list_empty(&skw->dfs.skw_pulse_pool)) { + pe = list_first_entry(&skw->dfs.skw_pulse_pool, struct skw_pulse_elem, head); + list_del(&pe->head); + } + + spin_unlock_bh(&skw->dfs.skw_pool_lock); + + return pe; +} + +static struct skw_pulse_elem *pulse_queue_get_tail(struct skw_pri_detector *pde) +{ + struct list_head *l = &pde->pulses; + + if (list_empty(l)) + return NULL; + + return list_entry(l->prev, struct skw_pulse_elem, head); +} + +static bool pulse_queue_dequeue(struct skw_core *skw, struct skw_pri_detector *pde) +{ + struct skw_pulse_elem *p = pulse_queue_get_tail(pde); + + if (p != NULL) { + list_del_init(&p->head); + pde->count--; + /* give it back to pool */ + pool_put_pulse_elem(skw, p); + } + + return (pde->count > 0); +} + +/* remove pulses older than window */ +static void pulse_queue_check_window(struct skw_core *skw, struct skw_pri_detector *pde) +{ + u64 min_valid_ts; + struct skw_pulse_elem *p; + + /* there is no delta time with less than 2 pulses */ + if (pde->count < 2) + return; + + if (pde->last_ts <= pde->window_size) + return; + + min_valid_ts = pde->last_ts - pde->window_size; + + while ((p = pulse_queue_get_tail(pde)) != NULL) { + if (p->ts >= min_valid_ts) + return; + + pulse_queue_dequeue(skw, pde); + } +} + +static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance) +{ + u32 remainder; + u32 factor; + u32 delta; + + if (fraction == 0) + return 0; + + delta = (val < fraction) ? (fraction - val) : (val - fraction); + + if (delta <= tolerance) + /* val and fraction are within tolerance */ + return 1; + + factor = val / fraction; + remainder = val % fraction; + if (remainder > tolerance) { + /* no exact match */ + if ((fraction - remainder) <= tolerance) + /* remainder is within tolerance */ + factor++; + else + factor = 0; + } + + return factor; +} + +static u32 pseq_handler_add_to_existing_seqs(struct skw_core *skw, struct skw_radar_cfg *cfg, u64 ts) +{ + u32 max_count = 0; + struct skw_pri_sequence *ps, *ps2; + + list_for_each_entry_safe(ps, ps2, &cfg->pri.sequences, head) { + u32 delta_ts; + u32 factor; + + /* first ensure that sequence is within window */ + if (ts > ps->deadline_ts) { + list_del_init(&ps->head); + pool_put_pseq_elem(skw, ps); + continue; + } + + delta_ts = ts - ps->last_ts; + factor = pde_get_multiple(delta_ts, ps->pri, + cfg->rule.max_pri_tolerance); + if (factor > 0) { + ps->last_ts = ts; + ps->count++; + + if (max_count < ps->count) + max_count = ps->count; + } else { + ps->count_falses++; + } + } + + return max_count; +} + +static struct skw_pri_sequence *pool_get_pseq_elem(struct skw_core *skw) +{ + struct skw_pri_sequence *pse = NULL; + + spin_lock_bh(&skw->dfs.skw_pool_lock); + + if (!list_empty(&skw->dfs.skw_pseq_pool)) { + pse = list_first_entry(&skw->dfs.skw_pseq_pool, struct skw_pri_sequence, head); + list_del(&pse->head); + } + + spin_unlock_bh(&skw->dfs.skw_pool_lock); + + return pse; +} + +#define GET_PRI_TO_USE(MIN, MAX, RUNTIME) \ + (MIN + SKW_PRI_TOLERANCE == MAX - SKW_PRI_TOLERANCE ? \ + MIN + SKW_PRI_TOLERANCE : RUNTIME) +static bool pseq_handler_create_sequences(struct skw_core *skw, + struct skw_radar_cfg *cfg, u64 ts, u32 min_count) +{ + struct skw_pulse_elem *p; + + list_for_each_entry(p, &cfg->pri.pulses, head) { + struct skw_pri_sequence ps, *new_ps; + struct skw_pulse_elem *p2; + u32 tmp_false_count; + u64 min_valid_ts; + u32 delta_ts = ts - p->ts; + + if (delta_ts < cfg->rule.pri_min) + /* ignore too small pri */ + continue; + + if (delta_ts > cfg->rule.pri_max) + /* stop on too large pri (sorted list) */ + break; + + /* build a new sequence with new potential pri */ + ps.count = 2; + ps.count_falses = 0; + ps.first_ts = p->ts; + ps.last_ts = ts; + ps.pri = GET_PRI_TO_USE(cfg->rule.pri_min, cfg->rule.pri_max, ts - p->ts); + ps.dur = ps.pri * (cfg->rule.ppb - 1) + 2 * cfg->rule.max_pri_tolerance; + + p2 = p; + tmp_false_count = 0; + min_valid_ts = ts - ps.dur; + /* check which past pulses are candidates for new sequence */ + list_for_each_entry_continue(p2, &cfg->pri.pulses, head) { + u32 factor; + + if (p2->ts < min_valid_ts) + /* stop on crossing window border */ + break; + /* check if pulse match (multi)PRI */ + factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri, + cfg->rule.max_pri_tolerance); + if (factor > 0) { + ps.count++; + ps.first_ts = p2->ts; + /* + * on match, add the intermediate falses + * and reset counter + */ + ps.count_falses += tmp_false_count; + tmp_false_count = 0; + } else { + /* this is a potential false one */ + tmp_false_count++; + } + } + + if (ps.count <= min_count) + /* did not reach minimum count, drop sequence */ + continue; + + /* this is a valid one, add it */ + ps.deadline_ts = ps.first_ts + ps.dur; + new_ps = pool_get_pseq_elem(skw); + if (new_ps == NULL) { + new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC); + if (new_ps == NULL) + return false; + } + + memcpy(new_ps, &ps, sizeof(ps)); + INIT_LIST_HEAD(&new_ps->head); + list_add(&new_ps->head, &cfg->pri.sequences); + } + + return true; +} + +static void pri_detector_reset(struct skw_core *skw, struct skw_pri_detector *pde, u64 ts) +{ + struct skw_pri_sequence *ps, *ps0; + struct skw_pulse_elem *p, *p0; + + list_for_each_entry_safe(ps, ps0, &pde->sequences, head) { + list_del_init(&ps->head); + pool_put_pseq_elem(skw, ps); + } + + list_for_each_entry_safe(p, p0, &pde->pulses, head) { + list_del_init(&p->head); + pool_put_pulse_elem(skw, p); + } + + pde->count = 0; + pde->last_ts = ts; +} + +static struct skw_pri_sequence *pseq_handler_check_detection(struct skw_radar_cfg *cfg) +{ + struct skw_pri_sequence *ps; + + if (list_empty(&cfg->pri.sequences)) + return NULL; + + list_for_each_entry(ps, &cfg->pri.sequences, head) { + /* + * we assume to have enough matching confidence if we + * 1) have enough pulses + * 2) have more matching than false pulses + */ + if ((ps->count >= cfg->rule.ppb_thresh) && + (ps->count * cfg->rule.nr_pri >= ps->count_falses)) + return ps; + } + + return NULL; +} + +static bool pulse_queue_enqueue(struct skw_core *skw, struct skw_pri_detector *pde, u64 ts) +{ + struct skw_pulse_elem *p = pool_get_pulse_elem(skw); + + if (p == NULL) { + p = kmalloc(sizeof(*p), GFP_ATOMIC); + if (p == NULL) + return false; + } + + INIT_LIST_HEAD(&p->head); + + p->ts = ts; + list_add(&p->head, &pde->pulses); + + pde->count++; + pde->last_ts = ts; + pulse_queue_check_window(skw, pde); + + if (pde->count >= pde->max_count) + pulse_queue_dequeue(skw, pde); + + return true; +} + +int skw_dfs_add_pulse(struct wiphy *wiphy, struct net_device *dev, struct skw_pulse_info *pulse) +{ + int i; + bool reset = false; + u32 max_updated_seq; + struct skw_pri_sequence *ps; + struct skw_iface *iface = netdev_priv(dev); + struct skw_core *skw = wiphy_priv(wiphy); + + if (!iface->sap.dfs.flags) + return 0; + + if (skw->dfs.last_pulse_ts > pulse->ts) + reset = true; + + skw->dfs.last_pulse_ts = pulse->ts; + + for (i = 0; i < skw->dfs.info->nr_cfg; i++) { + struct skw_radar_cfg *cfg = &skw->dfs.info->cfgs[i]; + const struct skw_radar_rule *rule = &cfg->rule; + + if (reset) { + pri_detector_reset(skw, &cfg->pri, skw->dfs.last_pulse_ts); + continue; + } + + if ((rule->width_min > pulse->width) || (rule->width_max < pulse->width)) { + skw_detail("invalid pulse width, (%d - %d), pulse width: %d\n", + rule->width_min, rule->width_max, pulse->width); + continue; + } + + if (rule->chirp && rule->chirp != pulse->chirp) { + skw_detail("invalid chirp\n"); + continue; + } + + if ((pulse->ts - cfg->pri.last_ts) < rule->max_pri_tolerance) { + skw_detail("invalid timestap, %lld - %lld = %lld, max: %d\n", + pulse->ts, cfg->pri.last_ts, pulse->ts - cfg->pri.last_ts, + rule->max_pri_tolerance); + continue; + } + + max_updated_seq = pseq_handler_add_to_existing_seqs(skw, cfg, pulse->ts); + if (!pseq_handler_create_sequences(skw, cfg, pulse->ts, max_updated_seq)) { + pri_detector_reset(skw, &cfg->pri, pulse->ts); + continue; + } + + ps = pseq_handler_check_detection(cfg); + if (ps) { + skw_info("radar deteced, iface dfs flags: 0x%lx\n", iface->sap.dfs.flags); + + skw_dfs_deinit(wiphy, dev); + + cfg80211_radar_event(wiphy, &skw->dfs.chan, GFP_KERNEL); + + break; + } + + pulse_queue_enqueue(skw, &cfg->pri, pulse->ts); + } + + return 0; +} + +static void skw_dfs_cac_work(struct work_struct *work) +{ + struct delayed_work *dwk = to_delayed_work(work); + struct skw_iface *iface = container_of(dwk, struct skw_iface, + sap.dfs.cac_work); + + skw_dbg("dev: %s finished\n", netdev_name(iface->ndev)); + + skw_wdev_lock(&iface->wdev); + + if (iface->wdev.cac_started) { + skw_dfs_stop_cac(priv_to_wiphy(iface->skw), iface->ndev); + + cfg80211_cac_event(iface->ndev, &iface->skw->dfs.chan, + NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); + } + + skw_wdev_unlock(&iface->wdev); +} + +int skw_dfs_chan_init(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_chan_def *chandef, u32 cac_time_ms) +{ + int i; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("chan: %d, dfs region: %d, flags: 0x%lx\n", + chandef->chan->hw_value, skw->dfs.region, + skw->dfs.flags); + + if (!skw->dfs.fw_enabled) + return -ENOTSUPP; + + if (skw->dfs.flags) { + if (skw->dfs.chan.width != chandef->width || + skw->dfs.chan.chan != chandef->chan) { + skw_warn("current chan: %d, require chan: %d\n", + skw->dfs.chan.chan->hw_value, + chandef->chan->hw_value); + + return -EBUSY; + } + } + + if (skw->dfs.region >= ARRAY_SIZE(skw_radar_infos)) { + skw_err("invalid dfs region: %d\n", skw->dfs.region); + + return -EINVAL; + } + + skw->dfs.info = &skw_radar_infos[skw->dfs.region]; + if (!skw->dfs.info->nr_cfg || !skw->dfs.info->cfgs) { + skw_err("invalid, region: %d, nr_cfg: %d\n", + skw->dfs.region, skw->dfs.info->nr_cfg); + + return -EINVAL; + } + + iface->sap.dfs.cac_time_ms = cac_time_ms; + skw->dfs.last_pulse_ts = 0; + skw->dfs.chan = *chandef; + + for (i = 0; i < skw->dfs.info->nr_cfg; i++) { + struct skw_radar_cfg *cfg = &skw->dfs.info->cfgs[i]; + const struct skw_radar_rule *rule = &cfg->rule; + + INIT_LIST_HEAD(&cfg->pri.sequences); + INIT_LIST_HEAD(&cfg->pri.pulses); + + cfg->pri.window_size = rule->pri_max * rule->ppb * rule->nr_pri; + cfg->pri.max_count = rule->ppb * 2; + } + + return 0; +} + +int skw_dfs_start_cac(struct wiphy *wiphy, struct net_device *dev) +{ + int ret; + struct skw_dfs_cac cac; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("%s\n", netdev_name(dev)); + + if (!skw->dfs.fw_enabled) + return -ENOTSUPP; + + cac.type = SKW_DFS_START_CAC; + cac.len = sizeof(struct skw_cac_params); + + cac.params.chn = skw->dfs.chan.chan->hw_value; + cac.params.center_chn1 = skw_freq_to_chn(skw->dfs.chan.center_freq1); + cac.params.center_chn2 = skw_freq_to_chn(skw->dfs.chan.center_freq2); + cac.params.band_width = to_skw_bw(skw->dfs.chan.width); + cac.params.region = skw->dfs.region; + cac.params.time_ms = iface->sap.dfs.cac_time_ms; + + ret = skw_send_msg(wiphy, dev, SKW_CMD_DFS, &cac, sizeof(cac), NULL, 0); + if (!ret) { + set_bit(SKW_DFS_FLAG_CAC_MODE, &iface->sap.dfs.flags); + set_bit(SKW_DFS_FLAG_CAC_MODE, &skw->dfs.flags); + } + + return ret; +} + +int skw_dfs_stop_cac(struct wiphy *wiphy, struct net_device *dev) +{ + struct skw_dfs_cac cac; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("%s\n", netdev_name(dev)); + + if (!skw->dfs.fw_enabled) + return -ENOTSUPP; + + cac.type = SKW_DFS_STOP_CAC; + cac.len = 0; + + clear_bit(SKW_DFS_FLAG_CAC_MODE, &iface->sap.dfs.flags); + clear_bit(SKW_DFS_FLAG_CAC_MODE, &skw->dfs.flags); + + return skw_send_msg(wiphy, dev, SKW_CMD_DFS, &cac, sizeof(cac), NULL, 0); +} + +int skw_dfs_start_monitor(struct wiphy *wiphy, struct net_device *dev) +{ + int ret; + struct skw_dfs_cac cac; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("%s\n", netdev_name(dev)); + + if (!skw->dfs.fw_enabled) + return -ENOTSUPP; + + cac.type = SKW_DFS_START_MONITOR; + cac.len = 0; + + ret = skw_send_msg(wiphy, dev, SKW_CMD_DFS, &cac, sizeof(cac), NULL, 0); + if (!ret) { + set_bit(SKW_DFS_FLAG_MONITOR_MODE, &skw->dfs.flags); + set_bit(SKW_DFS_FLAG_MONITOR_MODE, &iface->sap.dfs.flags); + } + + return ret; +} + +int skw_dfs_stop_monitor(struct wiphy *wiphy, struct net_device *dev) +{ + struct skw_dfs_cac cac; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = netdev_priv(dev); + + skw_dbg("%s\n", netdev_name(dev)); + + if (!skw->dfs.fw_enabled) + return -ENOTSUPP; + + cac.type = SKW_DFS_STOP_MONITOR; + cac.len = 0; + + clear_bit(SKW_DFS_FLAG_MONITOR_MODE, &skw->dfs.flags); + clear_bit(SKW_DFS_FLAG_MONITOR_MODE, &iface->sap.dfs.flags); + + return skw_send_msg(wiphy, dev, SKW_CMD_DFS, &cac, sizeof(cac), NULL, 0); +} + +int skw_dfs_init(struct wiphy *wiphy, struct net_device *dev) +{ + int i, j; + struct skw_iface *iface = netdev_priv(dev); + + BUILD_BUG_ON(IS_ENABLED(CONFIG_SWT6621S_REGD_SELF_MANAGED)); + + iface->sap.dfs.flags = 0; + INIT_DELAYED_WORK(&iface->sap.dfs.cac_work, skw_dfs_cac_work); + + for (i = 0; i < ARRAY_SIZE(skw_radar_infos); i++) { + const struct skw_radar_info *info = &skw_radar_infos[i]; + + for (j = 0; j < info->nr_cfg; j++) { + INIT_LIST_HEAD(&info->cfgs[j].pri.sequences); + INIT_LIST_HEAD(&info->cfgs[j].pri.pulses); + } + } + + return 0; +} + +int skw_dfs_deinit(struct wiphy *wiphy, struct net_device *dev) +{ + int i, j; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_iface *iface = netdev_priv(dev); + + if (test_bit(SKW_DFS_FLAG_CAC_MODE, &iface->sap.dfs.flags)) { + + cancel_delayed_work_sync(&iface->sap.dfs.cac_work); + + skw_dfs_stop_cac(wiphy, dev); + + cfg80211_cac_event(dev, &skw->dfs.chan, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); + } + + if (test_bit(SKW_DFS_FLAG_MONITOR_MODE, &iface->sap.dfs.flags)) + skw_dfs_stop_monitor(wiphy, dev); + + for (i = 0; i < ARRAY_SIZE(skw_radar_infos); i++) { + const struct skw_radar_info *info = &skw_radar_infos[i]; + + for (j = 0; j < info->nr_cfg; j++) + pri_detector_reset(skw, &info->cfgs[j].pri, 0); + } + + return 0; +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dfs.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dfs.h new file mode 100755 index 0000000..292182b --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_dfs.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_DFS_H__ +#define __SKW_DFS_H__ + +#include <linux/ieee80211.h> +#include <net/cfg80211.h> +#include <linux/inetdevice.h> + +#define SKW_PRI_TOLERANCE 16 + +#define SKW_DFS_FLAG_CAC_MODE 1 +#define SKW_DFS_FLAG_MONITOR_MODE 2 + +struct skw_pri_detector { + u64 last_ts; + u32 window_size; + u32 count, max_count; + + struct list_head sequences; + struct list_head pulses; +}; + +struct skw_radar_rule { + u8 type_id; + u8 width_min; + u8 width_max; + u16 pri_min; + u16 pri_max; + u8 nr_pri; + u8 ppb; + u8 ppb_thresh; + u8 max_pri_tolerance; + bool chirp; +}; + +struct skw_radar_cfg { + const struct skw_radar_rule rule; + struct skw_pri_detector pri; +}; + +struct skw_radar_info { + int nr_cfg; + struct skw_radar_cfg *cfgs; +}; + +enum SKW_DFS_ACTION { + SKW_DFS_START_CAC = 1, + SKW_DFS_STOP_CAC, + SKW_DFS_START_MONITOR, + SKW_DFS_STOP_MONITOR, +}; + +struct skw_cac_params { + u8 chn; + u8 center_chn1; + u8 center_chn2; + u8 band_width; + + u32 time_ms; + u8 region; +} __packed; + +struct skw_dfs_cac { + u16 type; + u16 len; + struct skw_cac_params params; +}; + +struct skw_pulse_data { + u64 chirp: 1; + u64 rssi: 5; + u64 width: 8; + u64 ts: 24; + u64 resv:26; +}; + +struct skw_radar_pulse { + u8 nr_pulse; + u8 resv; + struct skw_pulse_data data[0]; +} __packed; + +struct skw_pulse_info { + u64 ts; + u16 freq; + u8 width; + s8 rssi; + bool chirp; +}; + +struct skw_pulse_elem { + struct list_head head; + u64 ts; +}; + +struct skw_pri_sequence { + struct list_head head; + u32 pri; + u32 dur; + u32 count; + u32 count_falses; + u64 first_ts; + u64 last_ts; + u64 deadline_ts; +}; + +#ifdef CONFIG_SWT6621S_DFS_MASTER +int skw_dfs_chan_init(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_chan_def *chandef, u32 cac_time_ms); + +int skw_dfs_add_pulse(struct wiphy *wiphy, struct net_device *dev, + struct skw_pulse_info *pulse); + +int skw_dfs_start_cac(struct wiphy *wiphy, struct net_device *dev); +int skw_dfs_stop_cac(struct wiphy *wiphy, struct net_device *ndev); +int skw_dfs_start_monitor(struct wiphy *wiphy, struct net_device *dev); +int skw_dfs_stop_monitor(struct wiphy *wiphy, struct net_device *dev); +int skw_dfs_init(struct wiphy *wiphy, struct net_device *dev); +int skw_dfs_deinit(struct wiphy *wiphy, struct net_device *dev); +#else +static inline int skw_dfs_chan_init(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_chan_def *chandef, u32 cac_time_ms) +{ + return -ENOTSUPP; +} + +static inline int skw_dfs_add_pulse(struct wiphy *wiphy, struct net_device *dev, + struct skw_pulse_info *pulse) +{ + return 0; +} + +static inline int skw_dfs_start_cac(struct wiphy *wiphy, struct net_device *dev) +{ + return -ENOTSUPP; +} + +static inline int skw_dfs_stop_cac(struct wiphy *wiphy, struct net_device *ndev) +{ + return 0; +} + +static inline int skw_dfs_start_monitor(struct wiphy *wiphy, struct net_device *dev) +{ + return -ENOTSUPP; +} + +static inline int skw_dfs_stop_monitor(struct wiphy *wiphy, struct net_device *dev) +{ + return 0; +} +static inline int skw_dfs_init(struct wiphy *wiphy, struct net_device *dev) +{ + return 0; +} + +static inline int skw_dfs_deinit(struct wiphy *wiphy, struct net_device *dev) +{ + return 0; +} +#endif + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_edma.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_edma.c new file mode 100755 index 0000000..9df7785 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_edma.c @@ -0,0 +1,1380 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/kernel.h> +#include <linux/percpu-defs.h> +#include <linux/skbuff.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#include <linux/dma-direct.h> +#else +#include <linux/dma-mapping.h> +#endif + +#include "skw_core.h" +#include "skw_compat.h" +#include "skw_edma.h" +#include "skw_util.h" +#include "skw_log.h" +#include "skw_msg.h" +#include "skw_rx.h" +#include "skw_tx.h" +#include "trace.h" + +static struct kmem_cache *skw_edma_node_cache; +static DEFINE_PER_CPU(struct page_frag_cache, skw_edma_alloc_cache); + +static void *skw_edma_alloc_frag(size_t fragsz, gfp_t gfp_mask) +{ + struct page_frag_cache *nc; + unsigned long flags; + void *data; + + local_irq_save(flags); + nc = this_cpu_ptr(&skw_edma_alloc_cache); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + data = page_frag_alloc(nc, fragsz, gfp_mask); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + data = __alloc_page_frag(nc, fragsz, gfp_mask); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + data = napi_alloc_frag(fragsz); +#else + data = NULL; +#endif + + local_irq_restore(flags); + + return data; +} + +static int skw_lmac_show(struct seq_file *seq, void *data) +{ + struct skw_lmac *lmac = seq->private; + struct skw_edma_node *tmp; + struct list_head *pos, *n; + struct sk_buff *skb, *skb_tmp; + + seq_printf(seq, "edma_free_list len:%d avail_skb len:%d rx_dat_q len:%d\n", + READ_ONCE(lmac->edma_free_list.qlen), + READ_ONCE(lmac->avail_skb.qlen), + READ_ONCE(lmac->rx_dat_q.qlen)); + + skw_detail("each skb address of avail_skb queue\n"); + skb_queue_walk_safe(&lmac->avail_skb, skb, skb_tmp) + skw_detail("skb->data:%p\n", skb->data); + + skw_detail("each skb address of edma_free_list queue\n"); + skb_queue_walk_safe(&lmac->edma_free_list, skb, skb_tmp) + skw_detail("SKW_SKB_TXCB(skb)->e.pa:%llx\n", (u64)SKW_SKB_TXCB(skb)->e.pa); + + seq_printf(seq, "edma_tx_chn Info: current_node:%d nr_node:%d\n", + lmac->skw->edma.tx_chn[lmac->id].current_node->node_id, + atomic_read(&lmac->skw->edma.tx_chn[lmac->id].nr_node)); + list_for_each_safe(pos, n, &lmac->skw->edma.tx_chn[lmac->id].node_list) { + tmp = list_entry(pos, struct skw_edma_node, list); + seq_printf(seq, " node_id:%d used:%d dma_addr:%pad\n", + tmp->node_id, tmp->used, &tmp->dma_addr); + } + + seq_puts(seq, "\n"); + seq_printf(seq, "avail_skb queue len : %d\n", + READ_ONCE(lmac->avail_skb.qlen)); + seq_printf(seq, "edma_rx_req_chn Info: current_node:%d nr_node:%d avail_skb_num:%d\n", + lmac->skw->edma.rx_req_chn[lmac->id].current_node->node_id, + atomic_read(&lmac->skw->edma.rx_req_chn[lmac->id].nr_node), + atomic_read(&lmac->avail_skb_num)); + list_for_each_safe(pos, n, &lmac->skw->edma.rx_req_chn[lmac->id].node_list) { + tmp = list_entry(pos, struct skw_edma_node, list); + seq_printf(seq, " node_id:%d used:%d dma_addr:%pad\n", + tmp->node_id, tmp->used, &tmp->dma_addr); + } + + seq_puts(seq, "\n"); + seq_printf(seq, "edma_filter_ch Info: current_node:%d nr_node:%d rx_node_count:%d\n", + lmac->skw->edma.filter_chn[lmac->id].current_node->node_id, + atomic_read(&lmac->skw->edma.filter_chn[lmac->id].nr_node), + lmac->skw->edma.filter_chn[lmac->id].rx_node_count); + + list_for_each_safe(pos, n, &lmac->skw->edma.filter_chn[lmac->id].node_list) { + tmp = list_entry(pos, struct skw_edma_node, list); + seq_printf(seq, " node_id:%d dma_addr:%pad\n", + tmp->node_id, &tmp->dma_addr); + } + + seq_puts(seq, "\n"); + seq_printf(seq, "edma_rx_chn Info: current_node:%d nr_node:%d rx_node_count:%d\n", + lmac->skw->edma.rx_chn[lmac->id].current_node->node_id, + atomic_read(&lmac->skw->edma.rx_chn[lmac->id].nr_node), + lmac->skw->edma.rx_chn[lmac->id].rx_node_count); + + list_for_each_safe(pos, n, &lmac->skw->edma.rx_chn[lmac->id].node_list) { + tmp = list_entry(pos, struct skw_edma_node, list); + seq_printf(seq, " node_id:%d dma_addr:%pad\n", + tmp->node_id, &tmp->dma_addr); + } + + return 0; +} + +static int skw_lmac_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_lmac_show, inode->i_private); +} + +static const struct file_operations skw_lmac_fops = { + .owner = THIS_MODULE, + .open = skw_lmac_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int skw_edma_alloc_skb(struct skw_core *skw, struct skw_edma_chn *edma, int num, int lmac_id) +{ + int i; + struct wiphy *wiphy = priv_to_wiphy(skw); + struct device *dev = priv_to_wiphy(skw)->dev.parent; + u64 skb_pcie_addr; + dma_addr_t skb_dma_addr; + struct sk_buff *skb; + struct skw_lmac *lmac; + + if (unlikely(!dev)) { + skw_err("dev is null\n"); + return -ENODEV; + } + + edma->tx_node_count = 0; + lmac = &skw->hw.lmac[lmac_id]; + for (i = 0; i < num; i++) { + skb = dev_alloc_skb(SKW_EDMA_SKB_DATA_LEN); + if (!skb) { + skw_err("alloc skb addr fail!\n"); + return -ENOMEM; + } + + skb_put(skb, SKW_EDMA_SKB_DATA_LEN); + + skb_dma_addr = skw_pci_map_single(skw, skb->data, + SKW_EDMA_SKB_DATA_LEN, DMA_FROM_DEVICE); + if (unlikely(skw_pcie_mapping_error(skw, skb_dma_addr))) { + skw_err("dma mapping error :%d\n", __LINE__); + BUG_ON(1); + } + + skb_pcie_addr = skw_dma_to_pcie(skb_dma_addr); + skw_edma_set_data(wiphy, edma, &skb_pcie_addr, sizeof(u64)); + skb_queue_tail(&lmac->avail_skb, skb); + } + + skw_detail("used:%d edma->tx_node_count:%d num:%d\n", + edma->current_node->used, edma->tx_node_count, num); + if (skw->hw_pdata->submit_list_to_edma_channel(edma->channel, + 0, edma->tx_node_count) < 0) + skw_err("submit_list_to_edma_channel failed\n"); + else + atomic_add(num, &lmac->avail_skb_num); + edma->tx_node_count = 0; + + return 0; +} + +static inline void skw_edma_reset_refill(void *priv, u8 lmac_id) +{ + struct skw_core *skw = (struct skw_core *)priv; + struct skw_edma_chn *edma = NULL; + + edma = &skw->edma.rx_req_chn[lmac_id]; + atomic_set(&edma->chn_refill, 0); +} + +bool skw_edma_is_txc_completed(struct skw_core *skw) +{ + u8 lmac_id; + struct skw_lmac *lmac; + + for (lmac_id = 0; lmac_id < skw->hw.nr_lmac; lmac_id++) { + lmac = &skw->hw.lmac[lmac_id]; + + if (lmac != NULL && + skw_lmac_is_actived(skw, lmac_id)) { + spin_lock(&lmac->edma_free_list.lock); + if (!skb_queue_empty(&lmac->edma_free_list)) { + skw_dbg("txc is not completed"); + spin_unlock(&lmac->edma_free_list.lock); + return false; + } + spin_unlock(&lmac->edma_free_list.lock); + } + } + + return true; +} + +void skw_edma_inc_refill(void *priv, u8 lmac_id) +{ + struct skw_core *skw = (struct skw_core *)priv; + struct skw_edma_chn *edma = NULL; + + edma = &skw->edma.rx_req_chn[lmac_id]; + atomic_inc(&edma->chn_refill); +} + +void skw_edma_dec_refill(void *priv, u8 lmac_id) +{ + struct skw_core *skw = (struct skw_core *)priv; + struct skw_edma_chn *edma = NULL; + + edma = &skw->edma.rx_req_chn[lmac_id]; + + if (atomic_read(&edma->chn_refill) > 0) + atomic_dec(&edma->chn_refill); + + if (atomic_read(&edma->chn_refill) == 0) + skw_edma_unmask_irq(skw, lmac_id); +} + +int skw_edma_get_refill(void *priv, u8 lmac_id) +{ + struct skw_core *skw = (struct skw_core *)priv; + struct skw_edma_chn *edma = NULL; + + edma = &skw->edma.rx_req_chn[lmac_id]; + return atomic_read(&edma->chn_refill); +} + +static inline struct skw_edma_chn *skw_edma_get_refill_chan(void *priv, u8 lmac_id) +{ + struct skw_core *skw = (struct skw_core *)priv; + + return &skw->edma.rx_req_chn[lmac_id]; +} + +static void skw_edma_refill_skb(struct skw_core *skw, u8 lmac_id) +{ + u32 total = RX_FREE_BUF_ADDR_CNT * SKW_EDMA_RX_FREE_CHN_NODE_NUM; //TBD: + u16 avail_skb_num = atomic_read(&skw->hw.lmac[lmac_id].avail_skb_num); + struct skw_edma_chn *refill_chn = skw_edma_get_refill_chan((void *)skw, lmac_id); + + if (total - avail_skb_num >= RX_FREE_BUF_ADDR_CNT + && skw_edma_get_refill((void *)skw, lmac_id) > 0) + skw_edma_alloc_skb(skw, refill_chn, + round_down(total - avail_skb_num, RX_FREE_BUF_ADDR_CNT), lmac_id); +} + +static inline void skw_dma_free_coherent(struct skw_core *skw, + dma_addr_t *dma_handle, void *cpu_addr, size_t size) +{ + struct device *dev = priv_to_wiphy(skw)->dev.parent; + + dma_free_coherent(dev, size, cpu_addr, *dma_handle); +} + +static inline void *skw_dma_alloc_coherent(struct skw_core *skw, + dma_addr_t *dma_handle, size_t size, gfp_t flag) +{ + struct device *dev = priv_to_wiphy(skw)->dev.parent; + + return dma_alloc_coherent(dev, size, dma_handle, flag); +} + +struct skw_edma_node *skw_edma_next_node(struct skw_edma_chn *chn) +{ + unsigned long flags; + + chn->current_node->dma_addr = skw_pci_map_single(chn->skw, + chn->current_node->buffer, + chn->current_node->buffer_len, DMA_TO_DEVICE); + spin_lock_irqsave(&chn->edma_chan_lock, flags); + + if (list_is_last(&chn->current_node->list, &chn->node_list)) { + chn->current_node = list_first_entry(&chn->node_list, + struct skw_edma_node, list); + } else { + chn->current_node = list_next_entry(chn->current_node, list); + } + + chn->current_node->used = 0; + chn->tx_node_count++; + atomic_dec(&chn->nr_node); + + spin_unlock_irqrestore(&chn->edma_chan_lock, flags); + + return chn->current_node; +} + +int skw_edma_set_data(struct wiphy *wiphy, struct skw_edma_chn *edma, + void *data, int len) +{ + struct skw_edma_node *node = edma->current_node; + unsigned long flags; + u8 *buff = NULL; + + spin_lock_irqsave(&edma->edma_chan_lock, flags); + buff = (u8 *)node->buffer; + //skw_dbg("chan:%d node_id:%d node->used:%d buff:%px +used:%px\n", + //edma->channel, node->node_id, node->used, buff, + //(buff + node->used)); + //skw_dbg("data:%px\n", data); + memcpy(buff + node->used, data, len); + //skw_dbg("%d channel:%d node:%px\n", __LINE__, edma->channel, node); + node->used += len; + edma->hdr[node->node_id].data_len = node->used; + spin_unlock_irqrestore(&edma->edma_chan_lock, flags); + BUG_ON(len > node->buffer_len); + if (node->used + len > node->buffer_len) + node = skw_edma_next_node(edma); + + return 0; +} + +int skw_edma_tx(struct wiphy *wiphy, struct skw_edma_chn *edma, int tx_len) +{ + int tx_count; + struct skw_core *skw = wiphy_priv(wiphy); + u64 pa = 0; + + if (edma->current_node->used) + skw_edma_next_node(edma); + tx_count = edma->tx_node_count; + pa = edma->hdr->hdr_next; + //skw_dbg("channel:%d tx_node_count:%d pa:0x%llx\n", + //edma->channel, tx_count, pa); + edma->tx_node_count = 0; + + return skw->hw_pdata->hw_adma_tx(edma->channel, NULL, + tx_count, tx_len); +} + +int skw_edma_init_data_chan(void *priv, u8 lmac_id) +{ + int ret = 0; + unsigned long flags; + struct skw_core *skw = priv; + struct skw_edma_chn *edma_chn = NULL; + + edma_chn = &skw->edma.filter_chn[lmac_id]; + + spin_lock_irqsave(&edma_chn->edma_chan_lock, flags); + + ret = skw->hw_pdata->submit_list_to_edma_channel(edma_chn->channel, + 0, edma_chn->rx_node_count); + + edma_chn->rx_node_count = 0; + spin_unlock_irqrestore(&edma_chn->edma_chan_lock, flags); + + edma_chn = &skw->edma.rx_chn[lmac_id]; + spin_lock_irqsave(&edma_chn->edma_chan_lock, flags); + + ret = skw->hw_pdata->submit_list_to_edma_channel(edma_chn->channel, + 0, + //(void *)(skw_dma_to_pcie(edma_chn->edma_hdr_pa) + 8), + edma_chn->rx_node_count); + edma_chn->rx_node_count = 0; + spin_unlock_irqrestore(&edma_chn->edma_chan_lock, flags); + + edma_chn = &skw->edma.tx_resp_chn[lmac_id]; + spin_lock_irqsave(&edma_chn->edma_chan_lock, flags); + ret = skw->hw_pdata->submit_list_to_edma_channel(edma_chn->channel, + 0, + //(void *)(skw_dma_to_pcie(edma_chn->edma_hdr_pa) + 8), + edma_chn->rx_node_count); + edma_chn->rx_node_count = 0; + spin_unlock_irqrestore(&edma_chn->edma_chan_lock, flags); + + edma_chn = &skw->edma.rx_req_chn[lmac_id]; + skw_edma_alloc_skb(skw, edma_chn, + RX_FREE_BUF_ADDR_CNT * SKW_EDMA_RX_FREE_CHN_NODE_NUM, lmac_id); + + skw_edma_inc_refill((void *)skw, lmac_id); + + return ret; +} + +static inline int skw_submit_edma_chn(struct skw_core *skw, int chn, + u64 pcie_addr, int count) +{ + if (!skw->hw_pdata || !skw->hw_pdata->submit_list_to_edma_channel) + return -ENOTSUPP; + + return skw->hw_pdata->submit_list_to_edma_channel(chn, (u64)pcie_addr, count); +} + +static void skw_edma_chn_deinit(struct skw_core *skw, struct skw_edma_chn *edma) +{ + struct skw_edma_node *node = NULL, *tmp = NULL; + unsigned long flags; + u8 direction = edma->direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + // TODO: stop edma channel transmit + + if (!edma) { + skw_err("emda is null\n"); + return; + } + + skw_dbg("chan: %d\n", edma->channel); + + spin_lock_irqsave(&edma->edma_chan_lock, flags); + skw->hw_pdata->hw_channel_deinit(edma->channel); + list_for_each_entry_safe(node, tmp, &edma->node_list, list) { + list_del(&node->list); + skw_detail("channel: %d, node_id: %d, buffer: 0x%p, buffer_pa: 0x%pad\n", + edma->channel, node->node_id, node->buffer, &node->dma_addr); + + if (node->dma_addr) + skw_pci_unmap_single(skw, node->dma_addr, node->buffer_len, + direction); + + if (direction == DMA_FROM_DEVICE) + skw_compat_page_frag_free(node->buffer); + else + SKW_KFREE(node->buffer); + + kmem_cache_free(skw_edma_node_cache, node); + } + + edma->current_node = NULL; + atomic_set(&edma->nr_node, 0); + spin_unlock_irqrestore(&edma->edma_chan_lock, flags); + + skw_dma_free_coherent(skw, &edma->edma_hdr_pa, (void *)edma->hdr, + edma->edma_hdr_size); +} + +static int skw_edma_chn_init(struct skw_core *skw, struct skw_lmac *lmac, + struct skw_edma_chn *edma, int channel, + int max_node, int node_buff_len, + enum SKW_EDMA_DIRECTION direction, + skw_edma_isr isr, int irq_threshold, + enum SKW_EDMA_CHN_PRIORITY priority, + enum SKW_EDMA_CHN_BUFF_ATTR attr, + enum SKW_EDMA_CHN_BUFF_TYPE buff_type, + enum SKW_EDMA_CHN_TRANS_MODE trans_mode, + bool submit_immediately) +{ + int i, ret; + gfp_t flags; + u64 hdr_start; + struct skw_edma_node *node; + struct skw_channel_cfg cfg; + enum dma_data_direction dma_direction; + int hdr_size = sizeof(struct skw_edma_hdr); + struct device *dev = priv_to_wiphy(skw)->dev.parent; + void *(*skw_alloc_func)(size_t len, gfp_t gfp); + + skw_dbg("%d channel:%d edma->edma_hdr_pa:%pad\n", __LINE__, channel, + &edma->edma_hdr_pa); + + if (direction == SKW_FW_TO_HOST) { + flags = GFP_ATOMIC; + dma_direction = DMA_FROM_DEVICE; + skw_alloc_func = skw_edma_alloc_frag; + } else { + flags = GFP_DMA; + dma_direction = DMA_TO_DEVICE; + skw_alloc_func = kzalloc; + } + + INIT_LIST_HEAD(&edma->node_list); + spin_lock_init(&edma->edma_chan_lock); + atomic_set(&edma->nr_node, max_node); + + edma->skw = skw; + edma->lmac = lmac; + edma->direction = direction; + edma->n_pld_size = node_buff_len; + edma->max_node_num = max_node; + edma->channel = channel; + edma->tx_node_count = 0; + edma->rx_node_count = 0; + edma->swtail = 0; + edma->edma_hdr_size = PAGE_ALIGN(max_node * hdr_size); + + edma->hdr = dma_alloc_coherent(dev, edma->edma_hdr_size, &edma->edma_hdr_pa, GFP_DMA); + if (!edma->hdr) + return -ENOMEM; + + memset((void *)edma->hdr, 0x6a, edma->edma_hdr_size); + + hdr_start = SKW_EDMA_HEADR_RESVD + skw_dma_to_pcie(edma->edma_hdr_pa); + + for (i = 0; i < max_node; i++) { + node = kmem_cache_alloc(skw_edma_node_cache, GFP_KERNEL); + if (!node) + goto node_failed; + + node->buffer = skw_alloc_func(node_buff_len, flags); + if (!node->buffer) + goto node_failed; + + memset(node->buffer, 0x5a, node_buff_len); + + node->used = 0; + node->node_id = i; + node->buffer_len = node_buff_len; + node->dma_addr = skw_pci_map_single(skw, node->buffer, + node_buff_len, dma_direction); + + INIT_LIST_HEAD(&node->list); + list_add_tail(&node->list, &edma->node_list); + + edma->hdr[i].hdr_next = hdr_start + ((i + 1) % max_node) * hdr_size; + edma->hdr[i].pcie_addr = skw_dma_to_pcie(node->dma_addr); + + if (channel == SKW_EDMA_WIFI_RX0_FREE_CHN || channel == SKW_EDMA_WIFI_RX1_FREE_CHN) + edma->hdr[i].data_len = node_buff_len; + } + + edma->current_node = list_first_entry(&edma->node_list, + struct skw_edma_node, list); + + memset(&cfg, 0, sizeof(struct skw_channel_cfg)); + cfg.priority = priority; + cfg.split = attr; + cfg.ring = buff_type; + cfg.req_mode = trans_mode; + cfg.irq_threshold = irq_threshold; + cfg.node_count = max_node; + cfg.header = hdr_start; + cfg.complete_callback = isr; + cfg.direction = direction; + cfg.context = edma; + + ret = skw->hw_pdata->hw_channel_init(channel, &cfg, NULL); + + if (submit_immediately) + ret = skw_submit_edma_chn(skw, channel, hdr_start, max_node); + + return ret; + +node_failed: + skw_edma_chn_deinit(skw, edma); + + return -ENOMEM; +} + +static int +skw_edma_tx_node_isr(void *priv, u64 first_pa, u64 last_pa, int count) +{ + struct skw_edma_chn *edma_chn = priv; + struct skw_core *skw = edma_chn->skw; + struct skw_edma_hdr *edma_hdr = NULL; + int i = 0; + u64 pa = 0, hdr_next = 0; + int offset = 0; + unsigned long flags; + + spin_lock_irqsave(&edma_chn->edma_chan_lock, flags); + hdr_next = edma_chn->hdr->hdr_next; + offset = skw_pcie_to_dma(first_pa) - 8 - edma_chn->edma_hdr_pa; + + edma_hdr = (struct skw_edma_hdr *) ((u8 *)edma_chn->hdr + offset); + + //skw_dbg("edma_hdr:%p\n", edma_hdr); + while (i < count) { + pa = edma_hdr->pcie_addr; //pcie address + //skw_dbg("i:%d edma pcie addr:0x%llx, phy addrs:0x%llx\n", + // i, pa, skw_pcie_to_dma(pa)); + skw_pci_unmap_single(skw, + skw_pcie_to_dma(edma_hdr->pcie_addr), + edma_chn->current_node->buffer_len, DMA_TO_DEVICE); + atomic_inc(&edma_chn->nr_node); + edma_hdr++; + i++; + } + spin_unlock_irqrestore(&edma_chn->edma_chan_lock, flags); + + return 0; +} + +static inline void *skw_edma_coherent_rcvheader_to_cpuaddr(u64 rcv_pcie_addr, + struct skw_edma_chn *edma_chn) +{ + u32 offset; + u64 *cpu_addr; + + offset = rcv_pcie_addr - edma_chn->edma_hdr_pa; + cpu_addr = (u64 *)((u64)edma_chn->hdr + offset); + + return cpu_addr; +} + +int skw_edma_txrx_isr(void *priv, u64 first_pa, u64 last_pa, int count) +{ + struct skw_edma_chn *edma_chn = priv; + + if (unlikely(priv == NULL)) { + skw_err("Ignore spurious isr\n"); + return 0; + } + + skw_detail("call channel:%d\n", edma_chn->channel); + //skw->hw_pdata->edma_mask_irq(edma_chn->channel); + + if (edma_chn->channel == SKW_EDMA_WIFI_TX0_FREE_CHN || + edma_chn->channel == SKW_EDMA_WIFI_TX1_FREE_CHN) + napi_schedule(&edma_chn->lmac->napi_tx); + else if (edma_chn->channel == SKW_EDMA_WIFI_RX0_CHN || + edma_chn->channel == SKW_EDMA_WIFI_RX1_CHN || + edma_chn->channel == SKW_EDMA_WIFI_RX0_FITER_CHN || + edma_chn->channel == SKW_EDMA_WIFI_RX1_FITER_CHN) { + napi_schedule(&edma_chn->lmac->napi_rx); + } + + return 0; +} + +static void skw_pci_edma_tx_free(struct skw_core *skw, + struct sk_buff_head *free_list, void *data, u16 data_len) +{ + int count; + unsigned long flags; + struct sk_buff *skb; + struct sk_buff_head qlist; + u64 *p = (u64 *) data; + u64 p_data = 0; + unsigned long *skb_addr, *addr; + struct skw_edma_node *node; + + __skb_queue_head_init(&qlist); + + spin_lock_irqsave(&free_list->lock, flags); + skb_queue_splice_tail_init(free_list, &qlist); + spin_unlock_irqrestore(&free_list->lock, flags); + + trace_skw_tx_pcie_edma_free(data_len/8); + for (count = 0; count < data_len; count = count + 8, p++) { + p_data = *p & 0xFFFFFFFFFF; + skw_detail("p_data:%llx\n", p_data); + + skb_addr = skw_pcie_to_va(p_data) - 2 - sizeof(unsigned long); + skb = (struct sk_buff *)*skb_addr; + if (unlikely(skb < (struct sk_buff *)PAGE_OFFSET)) { + /* Invalid skb pointer */ + skw_dbg("wrong address p_data:0x%llx from FW\n", p_data); + continue; + } + addr = skw_pcie_to_va(p_data) - 2 - sizeof(unsigned long)*2; + node = (struct skw_edma_node *)*addr; + if (node->dma_addr) { + skw_pci_unmap_single(skw, + skw_pcie_to_dma(node->dma_addr), + node->buffer_len, DMA_TO_DEVICE); + node->dma_addr = 0; + skw->hw_pdata->edma_clear_src_node_count(SKW_EDMA_WIFI_TX0_CHN + SKW_SKB_TXCB(skb)->lmac_id); + } + + __skb_unlink(skb, &qlist); + skw_pci_unmap_single(skw, SKW_SKB_TXCB(skb)->skb_data_pa, + skb->len, DMA_TO_DEVICE); + skb->dev->stats.tx_packets++; + skb->dev->stats.tx_bytes += SKW_SKB_TXCB(skb)->skb_native_len; + //dev_kfree_skb_any(skb); + skw_skb_kfree(skw, skb); + } + + if (qlist.qlen) { + spin_lock_irqsave(&free_list->lock, flags); + skb_queue_splice_tail_init(&qlist, free_list); + spin_unlock_irqrestore(&free_list->lock, flags); + } +} + +static struct sk_buff * +skw_check_skb_address_available(struct skw_core *skw, u8 lmac_id, u64 addr) +{ + struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(&skw->hw.lmac[lmac_id].avail_skb, skb, tmp) { + skw_detail("lmac_id: %d, skb->data: 0x%p, addr_pcie: %llx, addr_vir: 0x%p", + lmac_id, skb->data, addr, skw_pcie_to_va(addr)); + if (skb->data == skw_pcie_to_va(addr)) { + skb_unlink(skb, &skw->hw.lmac[lmac_id].avail_skb); + return skb; + } + } + + return NULL; +} + +static void skw_pci_edma_rx_data(struct skw_edma_chn *edma_chn, void *data, int data_len) +{ + struct skw_core *skw = edma_chn->skw; + struct skw_rx_desc *desc = NULL; + struct sk_buff *skb; + struct skw_skb_rxcb *cb = NULL; + int i, total_len; + //u64 p_data = 0; + u64 *p = NULL; + + for (i = 0; i < data_len; i += 8) { + p = (u64 *)((u8 *)data + i); + + skb = skw_check_skb_address_available(skw, edma_chn->lmac->id, *p & 0xFFFFFFFFFF); + if (!skb) { + skw_dbg("wrong rx data from CP %llx\n", *p & 0xFFFFFFFFFF); + skw_warn("rxc node address:%llx\n", virt_to_phys(data)); + skw_hw_assert(skw, false); + continue; + } + + cb = SKW_SKB_RXCB(skb); + cb->lmac_id = edma_chn->lmac->id; + + skw_pci_unmap_single(skw, + skw_pcie_to_dma(*p & 0xFFFFFFFFFF), + skb->len, DMA_FROM_DEVICE); + //p_data = skw_pcie_to_va(*p & 0xFFFFFFFFFF); + + //desc = (struct skw_rx_desc *) ((u8 *) (p_data + 52)); + desc = (struct skw_rx_desc *) ((u8 *) (skb->data + 52)); + + //FW use this way to return unused buff + if (unlikely(!desc->msdu_len)) { + //skw_compat_page_frag_free((void *)p_data); + skw_detail("free skb\n"); + kfree_skb(skb); + atomic_dec(&edma_chn->lmac->avail_skb_num); + continue; + } + atomic_dec(&edma_chn->lmac->avail_skb_num); //TBD: whether to check the value is minus + + if (desc->snap_match) + total_len = desc->msdu_len + 80; + else + total_len = desc->msdu_len + 88; + + if (unlikely(total_len > SKW_ADMA_BUFF_LEN)) { + skw_hw_assert(skw, false); + skw_warn("total len: %d\n", total_len); + skw_warn("rxc node address:%llx skb->data:%llx\n", virt_to_phys(data), virt_to_phys(skb->data)); + skw_hex_dump("invalid rx skb:", skb->data, skb->len, true); + + //skw_compat_page_frag_free((void *)p_data); + //kfree_skb(skb); + continue; + } + + skb_trim(skb, total_len); + skb_pull(skb, 8); + __net_timestamp(skb); + skw_hex_dump("rx skb:", skb->data, skb->len, false); + + skb_queue_tail(&edma_chn->lmac->rx_dat_q, skb); + skw->rx_packets++; + } +} + +static void skw_pci_edma_rx_filter_data(struct skw_core *skw, void *data, int data_len, u8 lmac_id) +{ + struct sk_buff *skb; + struct skw_skb_rxcb *cb = NULL; + int total_len; + + total_len = SKB_DATA_ALIGN(data_len) + skw->skb_share_len; + + if (unlikely(total_len > SKW_ADMA_BUFF_LEN)) { + skw_warn("total_len: %d\n", total_len); + skw_compat_page_frag_free(data); + return; + } + + skb = build_skb((void *)data, total_len); + if (!skb) { + skw_err("build skb failed, len: %d\n", total_len); + skw_compat_page_frag_free(data); + return; + } + + cb = SKW_SKB_RXCB(skb); + cb->lmac_id = lmac_id; + skb_put(skb, data_len); + __net_timestamp(skb); + + skb_queue_tail(&skw->hw.lmac[lmac_id].rx_dat_q, skb); + skw->rx_packets++; +} + +static void skw_pcie_edma_rx_cb(struct skw_edma_chn *edma, void *data, u16 data_len) +{ + u16 channel = 0; + int ret = 0, total_len = 0; + struct skw_core *skw = edma->skw; + struct skw_iface *iface = NULL; + struct skw_event_work *work = NULL; + struct sk_buff *skb = NULL; + struct skw_msg *msg = NULL; + + channel = edma->channel; + + //skw_dbg("phy data:0x%llx len:%u\n", virt_to_phys(data), data_len); + //short & long event channel + //skw_dbg("channel:%d\n", channel); + if (channel == SKW_EDMA_WIFI_SHORT_EVENT_CHN || channel == SKW_EDMA_WIFI_LONG_EVENT_CHN) { + //skw_hex_dump("rx_cb data", data, 16, 1); + + total_len = SKB_DATA_ALIGN(data_len) + skw->skb_share_len; + if (unlikely(total_len > SKW_ADMA_BUFF_LEN)) { + skw_warn("data: %d\n", data_len); + skw_compat_page_frag_free(data); + return; + } + + skb = build_skb(data, total_len); + if (!skb) { + skw_compat_page_frag_free(data); + skw_err("build skb failed, len: %d\n", data_len); + return; + } + skb_put(skb, data_len); + + msg = (struct skw_msg *) skb->data; + switch (msg->type) { + case SKW_MSG_CMD_ACK: + skw_cmd_ack_handler(skw, skb->data, skb->len); + kfree_skb(skb); + break; + + case SKW_MSG_EVENT: + if (++skw->skw_event_sn != msg->seq) { + skw_warn("invalid event seq:%d, expect:%d\n", + msg->seq, skw->skw_event_sn); + // skw_hw_assert(skw, false); + // kfree_skb(skb); + // break; + } + + if (msg->id == SKW_EVENT_CREDIT_UPDATE) { + skw_warn("PCIE doesn't support CREDIT"); + kfree_skb(skb); + break; + } + + iface = to_skw_iface(skw, msg->inst_id); + if (iface) + work = &iface->event_work; + else + work = &skw->event_work; + + ret = skw_queue_event_work(priv_to_wiphy(skw), + work, skb); + if (ret < 0) { + skw_err("inst: %d, drop event %d\n", + msg->inst_id, msg->id); + kfree_skb(skb); + } + break; + + default: + skw_warn("invalid: type: %d, id: %d, seq: %d\n", + msg->type, msg->id, msg->seq); + kfree_skb(skb); + break; + } + } else if (channel == SKW_EDMA_WIFI_TX0_FREE_CHN || + channel == SKW_EDMA_WIFI_TX1_FREE_CHN) { + struct sk_buff_head *edma_free_list = NULL; + + //skw_dbg("channel:%d received tx free data\n", channel); + edma_free_list = &edma->lmac->edma_free_list; + + skw_pci_edma_tx_free(skw, edma_free_list, data, data_len); + } else if (channel == SKW_EDMA_WIFI_RX0_CHN || + channel == SKW_EDMA_WIFI_RX1_CHN) { + //skw_dbg("channel:%d received data\n", channel); + + + skw_pci_edma_rx_data(edma, data, data_len); + skw_edma_refill_skb(skw, edma->lmac->id); + } else if (channel == SKW_EDMA_WIFI_RX0_FITER_CHN || + channel == SKW_EDMA_WIFI_RX1_FITER_CHN) { + //skw_dbg("channel:%d received filter data\n", channel); + //skw_hex_dump("filter data", data, data_len, 1); + skw_pci_edma_rx_filter_data(skw, data, data_len, edma->lmac->id); + } +} + +int skw_edma_napi_txrx_compl_task(void *priv, int *quota) +{ + struct skw_edma_chn *edma_chn = priv; + u16 channel = edma_chn->channel; + struct skw_core *skw = edma_chn->skw; + struct skw_edma_hdr *edma_hdr = NULL; + unsigned long flags; + //int times = 0; + int count = 0; + void *p_addr = NULL; + + spin_lock_irqsave(&edma_chn->edma_chan_lock, flags); + + //while (edma_chn->hdr[edma_chn->swtail].done == 0 && times++ < edma_chn->max_node_num) + // edma_chn->swtail = skw_edma_hdr_tail(edma_chn); + + edma_hdr = (struct skw_edma_hdr *)((u8 *)edma_chn->hdr + + edma_chn->swtail * sizeof(struct skw_edma_hdr)); + + while (edma_hdr->done && (*quota > 0)) { + skw_detail("channel: %d, node_id: %d, node_pa: %pad, edma_pa: %llx, buffer: %p\n", + channel, edma_chn->current_node->node_id, + &edma_chn->current_node->dma_addr, (u64)edma_hdr->pcie_addr, + edma_chn->current_node->buffer); + + count += edma_hdr->data_len / 8; + if (*quota >= count) + *quota -= count; + else + *quota = 0; + edma_chn->swtail = skw_edma_hdr_tail(edma_chn); + + if (edma_chn->current_node->dma_addr) { + skw_pci_unmap_single(skw, + skw_pcie_to_dma(edma_hdr->pcie_addr), + edma_chn->current_node->buffer_len, DMA_FROM_DEVICE); + edma_chn->current_node->dma_addr = 0; + } + + skw_pcie_edma_rx_cb(edma_chn, skw_pcie_to_va(edma_hdr->pcie_addr), + edma_hdr->data_len); + edma_chn->rx_node_count++; + + if (channel == SKW_EDMA_WIFI_RX0_FITER_CHN || + channel == SKW_EDMA_WIFI_RX1_FITER_CHN) { + + p_addr = skw_edma_alloc_frag(edma_chn->n_pld_size, GFP_ATOMIC); + if (!p_addr) { + skw_err("Alloc memory for channel:%d failed\n", channel); + return -ENOMEM; + } + + edma_chn->current_node->dma_addr = skw_pci_map_single(skw, p_addr, + edma_chn->n_pld_size, DMA_FROM_DEVICE); + edma_hdr->pcie_addr = skw_dma_to_pcie(edma_chn->current_node->dma_addr); + edma_chn->current_node->buffer = p_addr; + } + + if (edma_chn->rx_node_count == edma_chn->max_node_num) { + edma_chn->rx_node_count = 0; + skw->hw_pdata->submit_list_to_edma_channel( + edma_chn->channel, 0, edma_chn->max_node_num); + } + + edma_hdr->done = 0; + edma_hdr++; + if (edma_chn->current_node->dma_addr == 0) + edma_chn->current_node->dma_addr = skw_pci_map_single(skw, + edma_chn->current_node->buffer, + edma_chn->current_node->buffer_len, DMA_FROM_DEVICE); + if (list_is_last(&edma_chn->current_node->list, + &edma_chn->node_list)) { + edma_chn->current_node = list_first_entry(&edma_chn->node_list, + struct skw_edma_node, list); + } else { + edma_chn->current_node = list_next_entry(edma_chn->current_node, + list); + } + } + spin_unlock_irqrestore(&edma_chn->edma_chan_lock, flags); + + return count; +} + +static int +skw_edma_rx_node_isr(void *priv, u64 first_pa, u64 last_pa, int count) +{ + struct skw_edma_chn *edma_chn = priv; + u16 channel = edma_chn->channel; + struct skw_core *skw = edma_chn->skw; + struct skw_edma_hdr *edma_hdr = NULL; + struct skw_edma_hdr *last_edma_hdr = NULL; + int i = 0; + u64 hdr_next = 0; + int offset = 0; + unsigned long flags; + void *p_addr = NULL; + + // Wait till tail done bit is set + last_edma_hdr = (struct skw_edma_hdr *)skw_edma_coherent_rcvheader_to_cpuaddr(((u64)last_pa) - 8, edma_chn); + while (!last_edma_hdr->done) { + mdelay(1); + barrier(); + } + + spin_lock_irqsave(&edma_chn->edma_chan_lock, flags); + edma_chn->rx_node_count += count; + hdr_next = edma_chn->hdr->hdr_next; + + offset = skw_pcie_to_dma(first_pa) - 8 - edma_chn->edma_hdr_pa; + edma_hdr = (struct skw_edma_hdr *)((u8 *)edma_chn->hdr + offset); + while (i < count) { + skw_detail("channel:%d node_id:%d current_node->buffer_pa:%pad edma_hdr->buffer_pa:%llx\n", + channel, edma_chn->current_node->node_id, &edma_chn->current_node->dma_addr, (u64)edma_hdr->pcie_addr); + if (edma_chn->current_node->dma_addr) { + skw_pci_unmap_single(skw, + skw_pcie_to_dma(edma_hdr->pcie_addr), + edma_chn->current_node->buffer_len, DMA_FROM_DEVICE); + edma_chn->current_node->dma_addr = 0; + } + + skw_pcie_edma_rx_cb(edma_chn, + (void *)skw_pcie_to_va(edma_hdr->pcie_addr), + edma_hdr->data_len); + + if (channel == SKW_EDMA_WIFI_SHORT_EVENT_CHN || + channel == SKW_EDMA_WIFI_LONG_EVENT_CHN) { + + // This payload of edma channel will be freed in asyn way + p_addr = skw_edma_alloc_frag(edma_chn->n_pld_size, GFP_ATOMIC); + if (!p_addr) { + skw_err("Alloc memory for channel:%d failed\n", channel); + return -ENOMEM; + } + + edma_chn->current_node->dma_addr = skw_pci_map_single(skw, p_addr, + edma_chn->n_pld_size, DMA_FROM_DEVICE); + edma_hdr->pcie_addr = skw_dma_to_pcie(edma_chn->current_node->dma_addr); + edma_chn->current_node->buffer = p_addr; + } else { + // This payload will be reused + // TBD: reset the payload + } + + if (edma_chn->rx_node_count == edma_chn->max_node_num) { //TBD: How to improve rx data channel + //skw_dbg("resubmit for channel:%d\n", edma_chn->channel); + edma_chn->rx_node_count = 0; + skw->hw_pdata->submit_list_to_edma_channel( + edma_chn->channel, 0, edma_chn->max_node_num); + } + + edma_hdr++; + i++; + if (list_is_last(&edma_chn->current_node->list, &edma_chn->node_list)) { + edma_chn->current_node = list_first_entry(&edma_chn->node_list, + struct skw_edma_node, list); + } else { + edma_chn->current_node = list_next_entry(edma_chn->current_node, list); + } + } + + spin_unlock_irqrestore(&edma_chn->edma_chan_lock, flags); + + last_edma_hdr->done = 0; + + return 0; +} + +static int skw_netdev_poll_tx(struct napi_struct *napi, int budget) +{ + int quota = budget; + struct skw_lmac *lmac = container_of(napi, struct skw_lmac, napi_tx); + struct skw_core *skw = lmac->skw; + + skw_edma_napi_txrx_compl_task(&skw->edma.tx_resp_chn[lmac->id], "a); + + if (quota) { + napi_complete(napi); + skw->hw_pdata->edma_unmask_irq(skw->edma.tx_resp_chn[lmac->id].channel); + return 0; + } + + return budget; +} + +static int skw_netdev_poll_rx(struct napi_struct *napi, int budget) +{ + int quota = budget; + struct skw_lmac *lmac = container_of(napi, struct skw_lmac, napi_rx); + struct skw_core *skw = lmac->skw; + + skw_edma_napi_txrx_compl_task(&skw->edma.rx_chn[lmac->id], "a); + skw_edma_napi_txrx_compl_task(&skw->edma.filter_chn[lmac->id], "a); + + skw_rx_process(skw, &lmac->rx_dat_q, &lmac->rx_todo_list); + if (lmac->rx_todo_list.count || !quota) + return budget; + + napi_complete(napi); + skw->hw_pdata->edma_unmask_irq(skw->edma.rx_chn[lmac->id].channel); + skw->hw_pdata->edma_unmask_irq(skw->edma.filter_chn[lmac->id].channel); + + return 0; +} + +static int skw_edma_cache_init(struct skw_core *skw) +{ + skw_edma_node_cache = kmem_cache_create("skw_edma_node_cache", + sizeof(struct skw_edma_node), + 0, 0, NULL); + if (skw_edma_node_cache == NULL) + return -ENOMEM; + + return 0; +} + +static void skw_edma_cache_deinit(struct skw_core *skw) +{ + kmem_cache_destroy(skw_edma_node_cache); +} + +int skw_edma_init(struct wiphy *wiphy) +{ + int ret, i; + struct skw_lmac *lmac = NULL; + struct skw_core *skw = wiphy_priv(wiphy); + char name[32] = {0}; + + if (skw->hw.bus != SKW_BUS_PCIE) + return 0; + + ret = skw_edma_cache_init(skw); + if (ret < 0) { + skw_err("edma cached init failed, ret: %d\n", ret); + return ret; + } + + // cmd channel + ret = skw_edma_chn_init(skw, NULL, &skw->edma.cmd_chn, + SKW_EDMA_WIFI_CMD_CHN, + 1, SKW_MSG_BUFFER_LEN, + SKW_HOST_TO_FW, skw_edma_tx_node_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, false); + + // short event channel + ret = skw_edma_chn_init(skw, NULL, &skw->edma.short_event_chn, + SKW_EDMA_WIFI_SHORT_EVENT_CHN, + SKW_EDMA_EVENT_CHN_NODE_NUM, + SKW_MSG_BUFFER_LEN, SKW_FW_TO_HOST, + skw_edma_rx_node_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_LIST_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, true); + + // long event channel + ret = skw_edma_chn_init(skw, NULL, &skw->edma.long_event_chn, + SKW_EDMA_WIFI_LONG_EVENT_CHN, + SKW_EDMA_EVENT_CHN_NODE_NUM, + SKW_MSG_BUFFER_LEN, SKW_FW_TO_HOST, + skw_edma_rx_node_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_LIST_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, true); + + // RX0 filter channel + ret = skw_edma_chn_init(skw, &skw->hw.lmac[0], + &skw->edma.filter_chn[0], + SKW_EDMA_WIFI_RX0_FITER_CHN, + SKW_EDMA_FILTER_CHN_NODE_NUM, + SKW_MSG_BUFFER_LEN, SKW_FW_TO_HOST, + skw_edma_txrx_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_LIST_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, true); + + // RX1 filter channel + ret = skw_edma_chn_init(skw, &skw->hw.lmac[1], + &skw->edma.filter_chn[1], + SKW_EDMA_WIFI_RX1_FITER_CHN, + SKW_EDMA_FILTER_CHN_NODE_NUM, + SKW_MSG_BUFFER_LEN, SKW_FW_TO_HOST, + skw_edma_txrx_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_LIST_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, true); + + // TX0 chan + ret = skw_edma_chn_init(skw, &skw->hw.lmac[0], + &skw->edma.tx_chn[0], + SKW_EDMA_WIFI_TX0_CHN, + SKW_EDMA_TX_CHN_NODE_NUM, + SKW_EDMA_DATA_LEN, SKW_HOST_TO_FW, + NULL, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, false); + skw->hw_pdata->edma_mask_irq(SKW_EDMA_WIFI_TX0_CHN); + + // TX1 chan + ret = skw_edma_chn_init(skw, &skw->hw.lmac[1], + &skw->edma.tx_chn[1], + SKW_EDMA_WIFI_TX1_CHN, + SKW_EDMA_TX_CHN_NODE_NUM, + SKW_EDMA_DATA_LEN, SKW_HOST_TO_FW, + NULL, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, false); + skw->hw_pdata->edma_mask_irq(SKW_EDMA_WIFI_TX1_CHN); + + // TX0 free chan + ret = skw_edma_chn_init(skw, &skw->hw.lmac[0], + &skw->edma.tx_resp_chn[0], + SKW_EDMA_WIFI_TX0_FREE_CHN, + SKW_EDMA_TX_FREE_CHN_NODE_NUM, + TX_FREE_BUF_ADDR_CNT * sizeof(long long), + SKW_FW_TO_HOST, skw_edma_txrx_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, true); + + // TX1 free chan + ret = skw_edma_chn_init(skw, &skw->hw.lmac[1], + &skw->edma.tx_resp_chn[1], + SKW_EDMA_WIFI_TX1_FREE_CHN, + SKW_EDMA_TX_FREE_CHN_NODE_NUM, + TX_FREE_BUF_ADDR_CNT * sizeof(long long), + SKW_FW_TO_HOST, skw_edma_txrx_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, true); + // RX0 free chan + ret = skw_edma_chn_init(skw, &skw->hw.lmac[0], + &skw->edma.rx_req_chn[0], + SKW_EDMA_WIFI_RX0_FREE_CHN, + SKW_EDMA_RX_FREE_CHN_NODE_NUM, + RX_FREE_BUF_ADDR_CNT * sizeof(long long), + SKW_HOST_TO_FW, skw_edma_tx_node_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, false); + + // RX1 free chan + ret = skw_edma_chn_init(skw, &skw->hw.lmac[1], + &skw->edma.rx_req_chn[1], + SKW_EDMA_WIFI_RX1_FREE_CHN, + SKW_EDMA_RX_FREE_CHN_NODE_NUM, + RX_FREE_BUF_ADDR_CNT * sizeof(long long), + SKW_HOST_TO_FW, skw_edma_tx_node_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, false); + + // RX0 chan + ret = skw_edma_chn_init(skw, &skw->hw.lmac[0], + &skw->edma.rx_chn[0], + SKW_EDMA_WIFI_RX0_CHN, + SKW_EDMA_RX_CHN_NODE_NUM, + RX_PKT_ADDR_BUF_CNT * sizeof(long long), + SKW_FW_TO_HOST, + skw_edma_txrx_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, true); + + // RX1 chan + ret = skw_edma_chn_init(skw, &skw->hw.lmac[1], + &skw->edma.rx_chn[1], + SKW_EDMA_WIFI_RX1_CHN, + SKW_EDMA_RX_CHN_NODE_NUM, + RX_PKT_ADDR_BUF_CNT * sizeof(long long), + SKW_FW_TO_HOST, + skw_edma_txrx_isr, + 1, SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_BUFF_NON_LINNER, + SKW_EDMA_CHN_RING_BUFF, + SKW_EDMA_CHN_LINKLIST_MODE, true); + + + // data tx/rx channel + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + lmac = &skw->hw.lmac[i]; + + sprintf(name, "mac%d", i); + skw_debugfs_file(SKW_WIPHY_DENTRY(wiphy), name, 0444, &skw_lmac_fops, lmac); + + init_dummy_netdev(&lmac->dummy_dev); + skw_compat_netif_napi_add_weight(&lmac->dummy_dev, &lmac->napi_tx, skw_netdev_poll_tx, 64); + skw_compat_netif_napi_add_weight(&lmac->dummy_dev, &lmac->napi_rx, skw_netdev_poll_rx, 64); + + skb_queue_head_init(&lmac->edma_free_list); + skb_queue_head_init(&lmac->avail_skb); + + skb_queue_head_init(&lmac->rx_dat_q); + skw_list_init(&lmac->rx_todo_list); + + skw_edma_reset_refill((void *) skw, i); + lmac->flags = SKW_LMAC_FLAG_INIT; + + napi_enable(&lmac->napi_tx); + napi_enable(&lmac->napi_rx); + } + + return 0; +} + +void skw_edma_deinit(struct wiphy *wiphy) +{ + int i = 0; + struct skw_lmac *lmac = NULL; + struct skw_core *skw = wiphy_priv(wiphy); + + if (skw->hw.bus != SKW_BUS_PCIE) + return; + + skw_edma_chn_deinit(skw, &skw->edma.cmd_chn); + skw_edma_chn_deinit(skw, &skw->edma.short_event_chn); + skw_edma_chn_deinit(skw, &skw->edma.long_event_chn); + + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + lmac = &skw->hw.lmac[i]; + napi_disable(&lmac->napi_tx); + napi_disable(&lmac->napi_rx); + netif_napi_del(&lmac->napi_tx); + netif_napi_del(&lmac->napi_rx); + skw_edma_chn_deinit(skw, &skw->edma.tx_chn[i]); + skw_edma_chn_deinit(skw, &skw->edma.tx_resp_chn[i]); + skw_edma_chn_deinit(skw, &skw->edma.rx_chn[i]); + skw_edma_chn_deinit(skw, &skw->edma.rx_req_chn[i]); + skw_edma_chn_deinit(skw, &skw->edma.filter_chn[i]); + skb_queue_purge(&lmac->edma_free_list); + skb_queue_purge(&lmac->avail_skb); + skb_queue_purge(&lmac->rx_dat_q); + skw_rx_todo(&lmac->rx_todo_list); + } + + skw_edma_cache_deinit(skw); +} + +void skw_edma_mask_irq(struct skw_core *skw, u8 lmac_id) +{ + if (skw->hw.bus != SKW_BUS_PCIE) + return; + + skw->hw_pdata->edma_mask_irq(SKW_EDMA_WIFI_TX0_CHN + lmac_id); +} + +void skw_edma_unmask_irq(struct skw_core *skw, u8 lmac_id) +{ + if (skw->hw.bus != SKW_BUS_PCIE) + return; + + skw->hw_pdata->edma_unmask_irq(SKW_EDMA_WIFI_TX0_CHN + lmac_id); +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_edma.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_edma.h new file mode 100755 index 0000000..4589053 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_edma.h @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_EDMA_H__ +#define __SKW_EDMA_H__ + +#define SKW_NR_EDMA_NODE 32 +#define SKW_NR_EDMA_ELEMENT 64 +#define SKW_EDMA_DATA_LEN 768 +#define SKW_EDMA_SKB_DATA_LEN 2048 + +#define SKW_EDMA_WIFI_CMD_CHN 14 +#define SKW_EDMA_WIFI_SHORT_EVENT_CHN 15 +#define SKW_EDMA_WIFI_LONG_EVENT_CHN 16 +#define SKW_EDMA_WIFI_RX0_FITER_CHN 17 +#define SKW_EDMA_WIFI_RX1_FITER_CHN 18 +#define SKW_EDMA_WIFI_TX0_CHN 19 +#define SKW_EDMA_WIFI_TX1_CHN 20 +#define SKW_EDMA_WIFI_TX0_FREE_CHN 21 +#define SKW_EDMA_WIFI_TX1_FREE_CHN 22 +#define SKW_EDMA_WIFI_RX0_FREE_CHN 23 +#define SKW_EDMA_WIFI_RX1_FREE_CHN 24 +#define SKW_EDMA_WIFI_RX0_CHN 25 +#define SKW_EDMA_WIFI_RX1_CHN 26 + + +#define SKW_EDMA_EVENT_CHN_NODE_NUM 2 +#define SKW_EDMA_FILTER_CHN_NODE_NUM 8 +#define SKW_EDMA_TX_CHN_NODE_NUM 64 +#define SKW_EDMA_TX_FREE_CHN_NODE_NUM 16 +#define SKW_EDMA_RX_CHN_NODE_NUM 32 +#define SKW_EDMA_RX_FREE_CHN_NODE_NUM 24 + +#define SKW_EDMA_TX_CHN_CREDIT 16 + +#define TX_BUF_ADDR_CNT 64 +#define TX_FREE_BUF_ADDR_CNT 64 +#define RX_PKT_ADDR_BUF_CNT 64 +#define RX_FREE_BUF_ADDR_CNT 32 + +#define SKW_EDMA_HEADR_RESVD 8 +#define skw_dma_to_pcie(addr) ((addr) + 0x8000000000) +#define skw_pcie_to_dma(addr) ((addr) - 0x8000000000) +#define skw_pcie_to_va(addr) phys_to_virt(skw_pcie_to_dma(addr)) + +typedef int (*skw_edma_isr)(void *priv, u64 first_pa, u64 last_pa, int cnt); +typedef int (*skw_edma_empty_isr)(void *priv); + +enum SKW_EDMA_DIRECTION { + SKW_HOST_TO_FW = 0, + SKW_FW_TO_HOST, +}; + +enum SKW_EDMA_CHN_PRIORITY { + SKW_EDMA_CHN_PRIORITY_0, + SKW_EDMA_CHN_PRIORITY_1, + SKW_EDMA_CHN_PRIORITY_2, + SKW_EDMA_CHN_PRIORITY_3 +}; + +enum SKW_EDMA_CHN_BUFF_ATTR { + SKW_EDMA_CHN_BUFF_LINNER, + SKW_EDMA_CHN_BUFF_NON_LINNER, +}; + +enum SKW_EDMA_CHN_BUFF_TYPE { + SKW_EDMA_CHN_LIST_BUFF, + SKW_EDMA_CHN_RING_BUFF +}; + +enum SKW_EDMA_CHN_TRANS_MODE { + SKW_EDMA_CHN_STD_MODE, + SKW_EDMA_CHN_LINKLIST_MODE, +}; + +struct skw_edma_elem { + u64 pa:40; + u64 rsv:8; + + u64 eth_type:16; + + u8 id_rsv:2; + u8 mac_id:2; + u8 tid:4; + + u8 peer_idx:5; + u8 prot:1; + u8 encry_dis:1; + u8 rate:1; + + u16 msdu_len:12; + u16 resv:4; +} __packed; + +struct skw_edma_hdr { + u64 pcie_addr:40; + u64 rsv0:16; + u64 tx_int:1; + u64 rsv1:6; + u64 done:1; + + u64 hdr_next:40; + u64 rsv2:8; + u64 data_len:16; +} __packed; + +struct skw_edma_node { + struct list_head list; + void *buffer; + dma_addr_t dma_addr; + int buffer_len; + u16 used; + u16 node_id; +}; + +struct skw_edma_chn { + struct skw_core *skw; + struct skw_lmac *lmac; + struct list_head node_list; + struct skw_edma_hdr *hdr; + struct skw_edma_node *current_node; + atomic_t nr_node; + dma_addr_t edma_hdr_pa; + u16 edma_hdr_size; + u16 n_pld_size; + u16 swtail; + u16 channel; + u16 max_node_num; + u16 tx_node_count; + u16 rx_node_count; + u16 direction; + atomic_t chn_refill; + spinlock_t edma_chan_lock; +}; + +#ifdef CONFIG_SWT6621S_EDMA +int skw_edma_init(struct wiphy *wiphy); +void skw_edma_deinit(struct wiphy *wiphy); +int skw_edma_set_data(struct wiphy *wiphy, struct skw_edma_chn *edma, + void *data, int len); +int skw_edma_tx(struct wiphy *wiphy, struct skw_edma_chn *edma, int tx_len); +int skw_edma_init_data_chan(void *priv, u8 lmac_id); +int skw_edma_get_refill(void *priv, u8 lmac_id); +void skw_edma_inc_refill(void *priv, u8 lmac_id); +void skw_edma_dec_refill(void *priv, u8 lmac_id); +bool skw_edma_is_txc_completed(struct skw_core *skw); +void skw_edma_mask_irq(struct skw_core *skw, u8 lmac_id); +void skw_edma_unmask_irq(struct skw_core *skw, u8 lmac_id); + +static inline u16 skw_edma_hdr_tail(struct skw_edma_chn *edma_chn) +{ + return (edma_chn->swtail + 1) % edma_chn->max_node_num; +} +#else +static inline int skw_edma_init(struct wiphy *wiphy) +{ + return 0; +} + +static inline void skw_edma_deinit(struct wiphy *wiphy) +{ +} + +static inline int skw_edma_set_data(struct wiphy *wiphy, + struct skw_edma_chn *edma, void *data, int len) +{ + return 0; +} + +static inline int skw_edma_tx(struct wiphy *wiphy, + struct skw_edma_chn *edma, int tx_len) +{ + return 0; +} + +static inline int skw_edma_init_data_chan(void *priv, u8 lmac_id) +{ + return 0; +} + +static inline int skw_edma_get_refill(void *priv, u8 lmac_id) +{ + return 0; +} + +static inline void skw_edma_inc_refill(void *priv, u8 lmac_id) +{ +} + +static inline void skw_edma_dec_refill(void *priv, u8 lmac_id) +{ +} + +static inline u16 skw_edma_hdr_tail(struct skw_edma_chn *edma_chn) +{ + return (edma_chn->swtail + 1) % edma_chn->max_node_num; +} + +static inline bool skw_edma_is_txc_completed(struct skw_core *skw) +{ + return true; +} + +static inline void skw_edma_mask_irq(struct skw_core *skw, u8 lmac_id) +{ +} + +static inline void skw_edma_unmask_irq(struct skw_core *skw, u8 lmac_id) +{ +} + +#endif +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iface.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iface.c new file mode 100755 index 0000000..db84efd --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iface.c @@ -0,0 +1,1395 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 "skw_core.h" +#include "skw_iface.h" +#include "skw_rx.h" +#include "skw_mlme.h" +#include "skw_cfg80211.h" +#include "skw_timer.h" +#include "skw_tx.h" +#include "skw_dfs.h" +#include "trace.h" + +static inline const char *skw_width_name(enum SKW_CHAN_BW_INFO state) +{ + static const char * const wd_name[] = {"20M", "40M", "80M", + "80+80M", "160M"}; + + if (state >= ARRAY_SIZE(wd_name)) + return "unknown"; + + return wd_name[state]; +} + +static inline const char *skw_80211_mode_name(enum skw_rate_info_flags mode) +{ + static const char * const md_name[] = {"legacy", "ieee80211n", "ieee80211ac", "ieee80211ax"}; + + if (mode >= ARRAY_SIZE(md_name)) + return "unknown"; + + return md_name[mode]; +} + +static int skw_iface_show(struct seq_file *seq, void *data) +{ + u32 peer_idx_map, idx; + struct skw_peer_ctx *ctx; + struct skw_bss_cfg *bss = NULL; + struct net_device *ndev = seq->private; + struct skw_iface *iface = netdev_priv(ndev); + int i; + + seq_puts(seq, "\n"); + seq_printf(seq, "Iface: \t%s (id: %d)\n" + " addr: \t%pM\n" + " mode: \t%s\n" + " cpu_id: \t%d\n", + netdev_name(iface->ndev), + iface->id, + iface->addr, + skw_iftype_name(iface->wdev.iftype), + iface->cpu_id); + + switch (iface->wdev.iftype) { + case NL80211_IFTYPE_STATION: + if (iface->flags & SKW_IFACE_FLAG_LEGACY_P2P_DEV) + break; + + skw_fallthrough; + + case NL80211_IFTYPE_P2P_CLIENT: + bss = &iface->sta.core.bss; + seq_printf(seq, " state: \t%s\n" + " connect width: %s\n", + skw_state_name(iface->sta.core.sm.state), + bss && bss->channel ? NL80211_BAND_2GHZ != bss->channel->band ? skw_width_name(bss->width) : + skw_width_name(min(bss->width, bss->ht_cap_chwidth)) : ""); + break; + + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + bss = &iface->sap.cfg; + seq_printf(seq, " max sta: \t%d\n", + iface->sap.max_sta_allowed); + break; + + default: + break; + } + + seq_printf(seq, "\nBSS Info: %s\n", bss ? "" : "null"); + if (bss) { + seq_printf(seq, " SSID: \t%s\n" + " BSSID: \t%pM\n" + " channel:\t%d\n" + " width: \t%s\n", + bss->ssid, + bss->bssid, + bss->channel ? bss->channel->hw_value : -1, + skw_width_name(bss->width)); + + } + + peer_idx_map = atomic_read(&iface->peer_map); + + seq_printf(seq, "\nPEER Info: %s\n", peer_idx_map ? "" : "null"); + while (peer_idx_map) { + idx = ffs(peer_idx_map) - 1; + SKW_CLEAR(peer_idx_map, BIT(idx)); + + ctx = &iface->skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; + + mutex_lock(&ctx->lock); + + if (ctx->peer) { + s16 rssi = ctx->peer->rx.rssi >> 3; + + if (ctx->peer->rx.rssi & BIT(10)) + rssi |= 0xff00; + + seq_printf(seq, " %pM (%d) %s\n", + ctx->peer->addr, + ctx->peer->idx, + skw_state_name(ctx->peer->sm.state)); + + seq_printf(seq, " TX: tidmap: 0x%x, %s: %d, nss:%d, %s, psr: %d, tx_failed: %d\n" + " RX: tidmap: 0x%x, %s: %d, nss:%d, %s\n" + " rssi: %d, data: %d\n" + " rx data band width :%s\n" + " filter stats :\n", + ctx->peer->txba.bitmap, + ctx->peer->tx.rate.flags ? + "mcs" : "legacy_rate", + ctx->peer->tx.rate.flags ? + ctx->peer->tx.rate.mcs_idx : + ctx->peer->tx.rate.legacy_rate, + ctx->peer->tx.rate.nss, + skw_80211_mode_name(ctx->peer->tx.rate.flags), + ctx->peer->tx.tx_psr, + ctx->peer->tx.tx_failed, + ctx->peer->rx_tid_map, + ctx->peer->rx.rate.flags ? + "mcs" : "legacy_rate", + ctx->peer->rx.rate.flags ? + ctx->peer->rx.rate.mcs_idx : + ctx->peer->rx.rate.legacy_rate, + ctx->peer->rx.rate.nss, + skw_80211_mode_name(ctx->peer->rx.rate.flags), + ctx->peer->tx.rssi, + rssi, skw_width_name(ctx->peer->rx.rate.bw)); + seq_puts(seq, " fliter:"); + + for (i = 0; i < 35; i++) + seq_printf(seq, "%d ", ctx->peer->rx.filter_cnt[i]); + + seq_puts(seq, "\n filter drop:"); + + for (i = 0; i < 35; i++) + seq_printf(seq, "%d ", ctx->peer->rx.filter_drop_offload_cnt[i]); + + seq_printf(seq, "\n Channnel occupancy: tx: %d, rx_idle: %d\n", + ctx->peer->tx.percent, ctx->peer->rx.percent); + + seq_puts(seq, "\n"); + } + + mutex_unlock(&ctx->lock); + } + + seq_puts(seq, "\nTXQ Info:\n"); + seq_printf(seq, " [VO]: stoped: %d, qlen: %d tx_cache:%d\n" + " [VI]: stoped: %d, qlen: %d tx_cache:%d\n" + " [BE]: stoped: %d, qlen: %d tx_cache:%d\n" + " [BK]: stoped: %d, qlen: %d tx_cache:%d\n", + SKW_TXQ_STOPED(ndev, SKW_WMM_AC_VO), + skb_queue_len(&iface->txq[SKW_WMM_AC_VO]), + skb_queue_len(&iface->tx_cache[SKW_WMM_AC_VO]), + SKW_TXQ_STOPED(ndev, SKW_WMM_AC_VI), + skb_queue_len(&iface->txq[SKW_WMM_AC_VI]), + skb_queue_len(&iface->tx_cache[SKW_WMM_AC_VI]), + SKW_TXQ_STOPED(ndev, SKW_WMM_AC_BE), + skb_queue_len(&iface->txq[SKW_WMM_AC_BE]), + skb_queue_len(&iface->tx_cache[SKW_WMM_AC_BE]), + SKW_TXQ_STOPED(ndev, SKW_WMM_AC_BK), + skb_queue_len(&iface->txq[SKW_WMM_AC_BK]), + skb_queue_len(&iface->tx_cache[SKW_WMM_AC_BK])); + + if (iface->skw->hw.bus != SKW_BUS_PCIE) + seq_printf(seq, "\nskw->rx_dat_q:%d\n", READ_ONCE(iface->skw->rx_dat_q.qlen)); + + return 0; +} + +static int skw_iface_open(struct inode *inode, struct file *file) +{ + // return single_open(file, skw_iface_show, inode->i_private); + return single_open(file, skw_iface_show, skw_pde_data(inode)); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_iface_fops = { + .proc_open = skw_iface_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations skw_iface_fops = { + .owner = THIS_MODULE, + .open = skw_iface_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +/* + * skw_acl_allowed: check if sta is in acl list + * return: true - allowd to access + * false - denied to access + */ +bool skw_acl_allowed(struct skw_iface *iface, u8 *addr) +{ + int i; + bool match = false; + + if (!iface->sap.acl) + return true; + + for (i = 0; i < iface->sap.acl->n_acl_entries; i++) { + u8 *mac = iface->sap.acl->mac_addrs[i].addr; + + if (ether_addr_equal(addr, mac)) { + match = true; + break; + } + } + + /* white list */ + if (iface->sap.acl->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED) + return match; + + return !match; +} + +int skw_cmd_open_dev(struct wiphy *wiphy, int inst, const u8 *mac_addr, + enum nl80211_iftype type, u16 flags) +{ + int mode, ret; + struct skw_open_dev_param open_param; + + skw_dbg("%s, inst: %d, mac: %pM, flags: 0x%x\n", + skw_iftype_name(type), inst, mac_addr, flags); + + BUG_ON(!is_valid_ether_addr(mac_addr)); + + switch (type) { + case NL80211_IFTYPE_ADHOC: + mode = SKW_IBSS_MODE; + break; + case NL80211_IFTYPE_STATION: + mode = SKW_STA_MODE; + break; + case NL80211_IFTYPE_AP: + mode = SKW_AP_MODE; + break; + case NL80211_IFTYPE_P2P_CLIENT: + mode = SKW_GC_MODE; + break; + case NL80211_IFTYPE_P2P_GO: + mode = SKW_GO_MODE; + break; + case NL80211_IFTYPE_P2P_DEVICE: + mode = SKW_P2P_DEV_MODE; + break; + case NL80211_IFTYPE_MONITOR: + mode = SKW_MONITOR_MODE; + break; + default: + skw_err("iftype: %d not support\n", type); + return -EINVAL; + } + + skw_ether_copy(open_param.mac_addr, mac_addr); + open_param.mode = mode; + open_param.flags = flags; + +#ifdef CONFIG_SWT6621S_OFFCHAN_TX + open_param.flags |= SKW_OPEN_FLAG_OFFCHAN_TX; +#endif + + ret = skw_msg_xmit(wiphy, inst, SKW_CMD_OPEN_DEV, &open_param, + sizeof(open_param), NULL, 0); + + return ret; +} + +static int skw_cmd_close_dev(struct wiphy *wiphy, int dev_id) +{ + skw_dbg("dev id: %d\n", dev_id); + + return skw_msg_xmit(wiphy, dev_id, SKW_CMD_CLOSE_DEV, NULL, 0, NULL, 0); +} + +void skw_purge_survey_data(struct skw_iface *iface) +{ + struct skw_survey_info *sinfo = NULL; + LIST_HEAD(flush_list); + + list_replace_init(&iface->survey_list, &flush_list); + + while (!list_empty(&flush_list)) { + sinfo = list_first_entry(&flush_list, + struct skw_survey_info, list); + + list_del(&sinfo->list); + SKW_KFREE(sinfo); + } +} + +void skw_iface_event_work(struct work_struct *work) +{ + struct sk_buff *skb; + struct skw_msg *msg_hdr; + struct skw_iface *iface; + + iface = container_of(work, struct skw_iface, event_work.work); + + while ((skb = skb_dequeue(&iface->event_work.qlist))) { + msg_hdr = (struct skw_msg *)skb->data; + skb_pull(skb, sizeof(*msg_hdr)); + + skw_event_handler(iface->skw, iface, msg_hdr, + skb->data, skb->len, SKW_SKB_RXCB(skb)); + + kfree_skb(skb); + } +} + +static int skw_add_vif(struct wiphy *wiphy, struct skw_iface *iface) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + skw_dbg("iface: 0x%x, bitmap: 0x%x\n", iface->id, skw->vif.bitmap); + + if (iface->id == SKW_INVALID_ID) + return 0; + + BUG_ON(skw->vif.iface[iface->id]); + + spin_lock_bh(&skw->vif.lock); + + skw->vif.iface[iface->id] = iface; + + spin_unlock_bh(&skw->vif.lock); + + return 0; +} + +static void skw_del_vif(struct wiphy *wiphy, struct skw_iface *iface) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + if (!iface) + return; + + skw_dbg("iface id: %d\n", iface->id); + + BUG_ON(iface->id >= SKW_NR_IFACE); + + spin_lock_bh(&skw->vif.lock); + + skw->vif.iface[iface->id] = NULL; + + spin_unlock_bh(&skw->vif.lock); +} + +static int skw_alloc_inst(struct wiphy *wiphy, u8 id) +{ + int inst = SKW_INVALID_ID; + struct skw_core *skw = wiphy_priv(wiphy); + + spin_lock_bh(&skw->vif.lock); + + if (id == SKW_INVALID_ID) { + for (id = 0; id < SKW_NR_IFACE; id++) { + if (!(skw->vif.bitmap & BIT(id))) { + inst = id; + break; + } + } + } else if ((id != (id & 0xf)) || (skw->vif.bitmap & BIT(id))) { + inst = SKW_INVALID_ID; + } else { + inst = id; + } + + if (inst != SKW_INVALID_ID) + SKW_SET(skw->vif.bitmap, BIT(id)); + + spin_unlock_bh(&skw->vif.lock); + + return inst; +} + +static void skw_release_inst(struct wiphy *wiphy, int id) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + if (id != (id & 0xf)) + return; + + spin_lock_bh(&skw->vif.lock); + + SKW_CLEAR(skw->vif.bitmap, BIT(id)); + + spin_unlock_bh(&skw->vif.lock); +} + +static void skw_sta_work(struct work_struct *wk) +{ + bool run_again = false; + bool connect_failed = false; + u64 cookie; + struct skw_iface *iface = container_of(wk, struct skw_iface, sta.work); + struct net_device *ndev = iface->ndev; + struct skw_sta_core *core = &iface->sta.core; + struct wiphy *wiphy = priv_to_wiphy(iface->skw); + + skw_wdev_lock(&iface->wdev); + + skw_dbg("inst: %d, state: %s, retry: %d jiff: %d step_start: %d ctx_start: %d auth_start: %d redo:%d\n", + core->sm.inst, skw_state_name(core->sm.state), + core->pending.retry, jiffies_to_msecs(jiffies), + jiffies_to_msecs(core->pending.step_start), + jiffies_to_msecs(core->pending.ctx_start), + jiffies_to_msecs(core->auth_start), + core->pending.redo); + + if (time_after(jiffies, core->auth_start + SKW_AUTH_TIMEOUT + SKW_ASSOC_TIMEOUT)) + connect_failed = true; + + switch (core->sm.state) { + case SKW_STATE_AUTHED: + if (time_after(jiffies, core->pending.step_start + SKW_STEP_TIMEOUT)) { + iface->sta.report_deauth = false; + + skw_sta_leave(wiphy, ndev, core->bss.bssid, 3, false); + + if (iface->sta.sme_external) + skw_compat_auth_timeout(ndev, core->bss.bssid); + else + skw_disconnected(ndev, 3, NULL, 0, true, GFP_KERNEL); + } else { + run_again = true; + } + + break; + + case SKW_STATE_AUTHING: + if (time_after(jiffies, core->pending.step_start + SKW_STEP_TIMEOUT)) { + if (++core->pending.retry >= SKW_MAX_AUTH_RETRY_NUM && + iface->wdev.iftype == NL80211_IFTYPE_STATION && + core->pending.auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) { + if (time_after(jiffies, core->pending.ctx_start + core->pending.ctx_to) || + core->pending.redo >= SKW_MAX_REAUTH_REDO_NUM) { + connect_failed = true; + } else { + if (core->sm.rty_state == SKW_RETRY_NONE) + core->sm.rty_state = SKW_RETRY_AUTH; + + if (skw_mgmt_frame_with_reason(iface, core->bss.bssid, + &cookie, core->bss.bssid, core->bss.channel, + IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, false)) + connect_failed = true; + else { + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + core->pending.redo++; + + if (skw_msg_xmit_timeout(wiphy, + SKW_NDEV_ID(ndev), + SKW_CMD_AUTH, + core->pending.auth_cmd, + core->pending.auth_cmd_len, + NULL, 0, "SKW_CMD_AUTH", + msecs_to_jiffies(300), 0)) + connect_failed = true; + else { + core->pending.retry = 0; + core->pending.step_start = jiffies; + skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHING); + } + } + } + } else if (core->pending.retry >= SKW_MAX_AUTH_RETRY_NUM) + connect_failed = true; + else { + skw_set_state(&core->sm, SKW_STATE_AUTHING); + + if (skw_msg_xmit_timeout(wiphy, + SKW_NDEV_ID(ndev), + SKW_CMD_AUTH, + core->pending.auth_cmd, + core->pending.auth_cmd_len, + NULL, 0, "SKW_CMD_AUTH", + msecs_to_jiffies(300), 0)) + connect_failed = true; + } + } + + if (connect_failed) { + iface->sta.report_deauth = false; + skw_sta_leave(wiphy, ndev, core->bss.bssid, 3, false); + + if (iface->sta.sme_external) + skw_compat_auth_timeout(ndev, core->bss.bssid); + else + skw_disconnected(ndev, 3, NULL, 0, true, + GFP_KERNEL); + } else { + run_again = true; + } + + break; + + case SKW_STATE_ASSOCING: + if (time_after(jiffies, core->pending.step_start + SKW_STEP_TIMEOUT)) { + if ((++core->pending.retry >= SKW_MAX_ASSOC_RETRY_NUM) && + iface->wdev.iftype == NL80211_IFTYPE_STATION && + core->pending.auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) { + if (time_after(jiffies, core->pending.ctx_start + core->pending.ctx_to) || + core->pending.redo >= SKW_MAX_REAUTH_REDO_NUM) { + connect_failed = true; + } else { + core->sm.rty_state = SKW_RETRY_ASSOC; + + if (skw_mgmt_frame_with_reason(iface, core->bss.bssid, + &cookie, core->bss.bssid, core->bss.channel, + IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, false)) + connect_failed = true; + else { + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + core->pending.redo++; + + if (skw_msg_xmit_timeout(wiphy, + SKW_NDEV_ID(ndev), + SKW_CMD_AUTH, + core->pending.auth_cmd, + core->pending.auth_cmd_len, + NULL, 0, "SKW_CMD_AUTH", + msecs_to_jiffies(300), 0)) + connect_failed = true; + else { + core->pending.retry = 0; + core->pending.step_start = jiffies; + skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHING); + } + } + } + + } else if (core->pending.retry >= SKW_MAX_ASSOC_RETRY_NUM) + connect_failed = true; + else { + skw_set_state(&core->sm, SKW_STATE_ASSOCING); + + if (skw_msg_xmit_timeout(wiphy, + SKW_NDEV_ID(ndev), + SKW_CMD_ASSOC, + core->pending.assoc_cmd, + core->pending.assoc_cmd_len, + NULL, 0, "SKW_CMD_ASSOC", + msecs_to_jiffies(300), 0)) + connect_failed = true; + } + } + + if (connect_failed) { + iface->sta.report_deauth = false; + skw_sta_leave(wiphy, ndev, core->bss.bssid, 3, false); + + if (iface->sta.sme_external) + skw_compat_assoc_failure(ndev, core->cbss, true); + else + skw_disconnected(ndev, 3, NULL, 0, true, GFP_KERNEL); + } else { + run_again = true; + } + + break; + + default: + break; + } + + skw_dbg("state: %s, connect failed: %d, run again: %d\n", + skw_state_name(core->sm.state), connect_failed, run_again); + + if (run_again) + skw_set_sta_timer(core, SKW_STEP_TIMEOUT); + + skw_wdev_unlock(&iface->wdev); +} + +static void skw_sta_timer(struct timer_list *t) +{ + struct skw_iface *iface = skw_from_timer(iface, t, sta.core.timer); + + queue_work(iface->skw->event_wq, &iface->sta.work); +} + +void skw_set_sta_timer(struct skw_sta_core *core, unsigned long timeout) +{ + if (!timer_pending(&core->timer)) + mod_timer(&core->timer, jiffies + timeout); +} + +static int skw_mode_init(struct wiphy *wiphy, struct skw_iface *iface, + enum nl80211_iftype type, int id) +{ + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_sta_core *core = &iface->sta.core; + + switch (type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + memset(&iface->sta, 0x0, sizeof(iface->sta)); + + iface->sta.sme_external = true; + core->bss.ctx_idx = SKW_INVALID_ID; + + mutex_init(&core->lock); + core->pending.auth_cmd = SKW_ZALLOC(SKW_2K_SIZE, GFP_KERNEL); + if (!core->pending.auth_cmd) + return -ENOMEM; + + core->pending.assoc_cmd = SKW_ZALLOC(SKW_2K_SIZE, GFP_KERNEL); + if (!core->pending.assoc_cmd) { + SKW_KFREE(core->pending.auth_cmd); + return -ENOMEM; + } + + core->assoc_req_ie = SKW_ZALLOC(SKW_2K_SIZE, GFP_KERNEL); + if (!core->assoc_req_ie) { + SKW_KFREE(core->pending.assoc_cmd); + SKW_KFREE(core->pending.auth_cmd); + return -ENOMEM; + } + + core->sm.inst = id; + core->sm.iface_iftype = type; + core->sm.state = SKW_STATE_NONE; + core->sm.addr = core->bss.bssid; + + INIT_WORK(&iface->sta.work, skw_sta_work); + skw_compat_setup_timer(&core->timer, skw_sta_timer); + + iface->sta.conn = NULL; + spin_lock_init(&iface->sta.roam_data.lock); + + if (!(test_bit(SKW_FLAG_STA_SME_EXTERNAL, &skw->flags))) { + iface->sta.sme_external = false; + + iface->sta.conn = SKW_ZALLOC(sizeof(*iface->sta.conn), + GFP_KERNEL); + if (!iface->sta.conn) { + iface->sta.conn = NULL; + SKW_KFREE(core->pending.assoc_cmd); + SKW_KFREE(core->pending.auth_cmd); + SKW_KFREE(core->assoc_req_ie); + + return -ENOMEM; + } + + mutex_init(&iface->sta.conn->lock); + iface->sta.conn->channel = NULL; + + iface->sta.conn->assoc_ie = SKW_ZALLOC(SKW_2K_SIZE, + GFP_KERNEL); + if (!iface->sta.conn->assoc_ie) { + SKW_KFREE(core->pending.assoc_cmd); + SKW_KFREE(core->pending.auth_cmd); + SKW_KFREE(core->assoc_req_ie); + SKW_KFREE(iface->sta.conn); + + return -ENOMEM; + } + } + + break; + + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + memset(&iface->sap, 0x0, sizeof(iface->sap)); + + skw_list_init(&iface->sap.mlme_client_list); + iface->sap.max_sta_allowed = skw->fw.max_num_sta; + + skw_dfs_init(wiphy, iface->ndev); + + if (test_bit(SKW_FLAG_SAP_SME_EXTERNAL, &skw->flags)) + iface->sap.sme_external = true; + + iface->sap.probe_resp = SKW_ZALLOC(SKW_2K_SIZE, GFP_KERNEL); + if (!iface->sap.probe_resp) + return -ENOMEM; + + memset(&iface->buf_keys, 0, sizeof(iface->buf_keys)); + iface->buf_keys_idx = 0; + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_BUF_KEY); + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_AP_STARTED); + + break; + + default: + break; + } + + return 0; +} + +static void skw_mode_deinit(struct wiphy *wiphy, struct skw_iface *iface, + enum nl80211_iftype iftype) +{ + struct skw_core *skw = iface->skw; + struct skw_peer_ctx *ctx; + + switch (iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + skw_dfs_deinit(wiphy, iface->ndev); + + SKW_KFREE(iface->sap.acl); + SKW_KFREE(iface->sap.cfg.ht_cap); + SKW_KFREE(iface->sap.cfg.vht_cap); + SKW_KFREE(iface->sap.probe_resp); + + break; + + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (iface->flags & SKW_IFACE_FLAG_LEGACY_P2P_DEV) + break; + + if (iface->sta.conn) { + SKW_KFREE(iface->sta.conn->assoc_ie); + SKW_KFREE(iface->sta.conn); + } + + del_timer_sync(&iface->sta.core.timer); + cancel_work_sync(&iface->sta.work); + + skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); + SKW_KFREE(iface->sta.core.pending.auth_cmd); + SKW_KFREE(iface->sta.core.pending.assoc_cmd); + SKW_KFREE(iface->sta.core.assoc_req_ie); + ctx = skw_get_ctx(skw, iface->lmac_id, iface->sta.core.bss.ctx_idx); + skw_peer_ctx_bind(iface, ctx, NULL); + memset(&iface->sta.core.bss, 0, sizeof(struct skw_bss_cfg)); + iface->sta.core.bss.ctx_idx = SKW_INVALID_ID; + + break; + + default: + break; + } +} + +int skw_iface_setup(struct wiphy *wiphy, struct net_device *dev, + struct skw_iface *iface, const u8 *addr, + enum nl80211_iftype iftype, int id) +{ + int i, ret; + struct skw_core *skw = wiphy_priv(wiphy); + + skw_dbg("%s, addr: %pM\n", skw_iftype_name(iftype), addr); + + BUG_ON(!addr || !is_valid_ether_addr(addr)); + + iface->ndev = dev; + iface->wdev.wiphy = wiphy; + iface->skw = wiphy_priv(wiphy); + iface->default_multicast = -1; + + mutex_init(&iface->lock); + atomic_set(&iface->peer_map, 0); + atomic_set(&iface->actived_ctx, 0); + + INIT_LIST_HEAD(&iface->survey_list); + + mutex_init(&iface->key_conf.lock); + for (i = 0; i < SKW_NUM_MAX_KEY; i++) { + iface->key_conf.installed_bitmap = 0; + RCU_INIT_POINTER(iface->key_conf.key[i], NULL); + } + + skw_event_work_init(&iface->event_work, skw_iface_event_work); + + for (i = 0; i < SKW_MAX_DEFRAG_ENTRY; i++) { + iface->frag[i].id = i; + iface->frag[i].tid = SKW_INVALID_ID; + skb_queue_head_init(&iface->frag[i].skb_list); + } + + for (i = 0; i < SKW_WMM_AC_MAX + 1; i++) { + skb_queue_head_init(&iface->txq[i]); + skb_queue_head_init(&iface->tx_cache[i]); + } + + ret = skw_mode_init(wiphy, iface, iftype, id); + if (ret) { + skw_err("init failed, iface: %d, iftype: %d, ret: %d\n", + id, iftype, ret); + + return ret; + } + + ret = skw_cmd_open_dev(wiphy, id, addr, iftype, 0); + if (ret) { + skw_err("open failed, iface: %d, iftype: %d, ret:%d\n", + id, iftype, ret); + goto iface_deinit; + } + + spin_lock_bh(&skw->vif.lock); + SKW_SET(iface->flags, SKW_IFACE_FLAG_OPENED); + skw->vif.opened_dev++; + spin_unlock_bh(&skw->vif.lock); + + iface->id = id; + iface->wdev.iftype = iftype; + skw_ether_copy(iface->addr, addr); + iface->cpu_id = -1; + + return 0; + +iface_deinit: + skw_mode_deinit(wiphy, iface, iftype); + + return ret; +} + +void skw_purge_key_conf(struct skw_key_conf *conf) +{ + int idx; + struct skw_key *key; + + if (!conf) + return; + + mutex_lock(&conf->lock); + + for (idx = 0; idx < SKW_NUM_MAX_KEY; idx++) { + key = rcu_dereference_protected(conf->key[idx], + lockdep_is_held(&conf->lock)); + + RCU_INIT_POINTER(conf->key[idx], NULL); + if (key) + kfree_rcu(key, rcu); + } + + conf->flags = 0; + conf->installed_bitmap = 0; + conf->skw_cipher = 0; + + mutex_unlock(&conf->lock); +} + +int skw_iface_teardown(struct wiphy *wiphy, struct skw_iface *iface) +{ + int i, ret; + struct skw_core *skw = wiphy_priv(wiphy); + + skw_dbg("iface id: %d\n", iface->id); + + skw_scan_done(skw, iface, true); + skw_purge_survey_data(iface); + + for (i = SKW_WMM_AC_VO; i < SKW_WMM_AC_MAX + 1; i++) { + skb_queue_purge(&iface->txq[i]); + skb_queue_purge(&iface->tx_cache[i]); + } + + for (i = 0; i < SKW_MAX_DEFRAG_ENTRY; i++) { + skb_queue_purge(&iface->frag[i].skb_list); + iface->frag[i].tid = SKW_INVALID_ID; + } + + skw_event_work_deinit(&iface->event_work); + + skw_mode_deinit(wiphy, iface, iface->wdev.iftype); + + skw_purge_key_conf(&iface->key_conf); + + ret = skw_cmd_close_dev(wiphy, iface->id); + if (ret < 0) + return ret; + + spin_lock_bh(&skw->vif.lock); + + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_OPENED); + skw->vif.opened_dev--; + if (!skw->vif.opened_dev) { + for (i = 0; i < skw->hw.nr_lmac; i++) { + atomic_set(&skw->hw.lmac[i].fw_credit, 0); + skw->rx_packets = 0; + skw->tx_packets = 0; + } + } + + spin_unlock_bh(&skw->vif.lock); + + return 0; +} + +struct skw_iface *skw_add_iface(struct wiphy *wiphy, const char *name, + enum nl80211_iftype iftype, u8 *mac, + u8 id, bool need_ndev) +{ + u8 *addr; + int priv_size, ret; + struct skw_iface *iface; + struct net_device *ndev = NULL; + struct skw_core *skw = wiphy_priv(wiphy); + int inst = skw_alloc_inst(wiphy, id); + + skw_info("%s, inst: %d, mac: %pM, bitmap: 0x%x\n", + skw_iftype_name(iftype), inst, mac, skw->vif.bitmap); + + if (inst == SKW_INVALID_ID) { + skw_err("invalid inst: %d, bitmap: 0x%x\n", + inst, skw->vif.bitmap); + + return ERR_PTR(-EINVAL); + } + + priv_size = sizeof(struct skw_iface); + if (need_ndev) { + ndev = alloc_netdev_mqs(priv_size, name, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + NET_NAME_ENUM, +#endif + ether_setup, SKW_WMM_AC_MAX, 1); + + if (!ndev) { + skw_err("alloc netdev failed, iftype: %d\n", iftype); + skw_release_inst(wiphy, inst); + + return ERR_PTR(-ENOMEM); + } + + iface = netdev_priv(ndev); + } else { + iface = SKW_ZALLOC(priv_size, GFP_KERNEL); + if (!iface) { + skw_release_inst(wiphy, inst); + return ERR_PTR(-ENOMEM); + } + } + + if (mac && is_valid_ether_addr(mac)) + addr = mac; + else + addr = wiphy->addresses[inst].addr; + + ret = skw_iface_setup(wiphy, ndev, iface, addr, iftype, inst); + if (ret) { + skw_err("iface setup failed, iftype: %d, ret: %d\n", + iftype, ret); + + goto free_iface; + } + + skw_add_vif(wiphy, iface); + + if (ndev) { + ret = skw_netdev_init(wiphy, ndev, addr); + if (ret) { + skw_err("init netdev failed\n"); + goto iface_teardown; + } + + ret = skw_register_netdevice(ndev); + if (ret) { + skw_err("register netdev failed\n"); + // free_percpu(ndev->tstats); + goto ndev_teardown; + } + + iface->procfs = skw_procfs_file(SKW_WIPHY_PENTRY(wiphy), + netdev_name(ndev), 0444, + &skw_iface_fops, ndev); + } else { + skw_ether_copy(iface->wdev.address, addr); + } + + return iface; + +ndev_teardown: + skw_netdev_deinit(iface->ndev); +iface_teardown: + skw_iface_teardown(wiphy, iface); + skw_del_vif(wiphy, iface); + +free_iface: + if (ndev) + free_netdev(ndev); + else + SKW_KFREE(iface); + + skw_release_inst(wiphy, inst); + + return ERR_PTR(-EBUSY); +} + +int skw_del_iface(struct wiphy *wiphy, struct skw_iface *iface) +{ + if (!iface) + return 0; + + ASSERT_RTNL(); + + skw_dbg("iftype = %d, iface id: %d\n", iface->wdev.iftype, iface->id); + + skw_iface_teardown(wiphy, iface); + skw_del_vif(wiphy, iface); + skw_release_inst(wiphy, iface->id); + + if (iface->ndev) { + proc_remove(iface->procfs); + skw_netdev_deinit(iface->ndev); + skw_unregister_netdevice(iface->ndev); + } else if (iface->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE) { + cfg80211_unregister_wdev(&iface->wdev); + SKW_KFREE(iface); + } + + return 0; +} + +struct skw_peer *skw_peer_alloc(void) +{ + int len; + + len = ALIGN(sizeof(struct skw_peer), SKW_PEER_ALIGN); + len += sizeof(struct skw_ctx_entry); + + return SKW_ZALLOC(len, GFP_KERNEL); +} + +static void skw_peer_release(struct rcu_head *head) +{ + struct skw_ctx_entry *entry; + + entry = container_of(head, struct skw_ctx_entry, rcu); + + // NOTE: DO NOT USE SKW_FREE HERE + kfree(entry->peer); +} + +void skw_peer_free(struct skw_peer *peer) +{ + int i; + struct skw_ctx_entry *entry; + + if (!peer) + return; + + for (i = 0; i < SKW_NR_TID; i++) + skw_del_tid_rx(peer, i); + + skw_purge_key_conf(&peer->ptk_conf); + skw_purge_key_conf(&peer->gtk_conf); + + entry = skw_ctx_entry(peer); + +#ifdef CONFIG_SWT6621S_GKI_DRV + skw_call_rcu(peer->iface->skw, &entry->rcu, skw_peer_release); +#else + call_rcu(&entry->rcu, skw_peer_release); +#endif +} + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +static void skw_reorder_timeout(struct timer_list *timer) +#else +static void skw_reorder_timeout(unsigned long timer) +#endif +{ + struct skw_reorder_rx *reorder; + + reorder = container_of((void *)timer, struct skw_reorder_rx, timer); + + skw_dbg("tid: %d, expired sn: %d\n", reorder->tid, reorder->expired.sn); + + if (atomic_read(&reorder->ref_cnt) != reorder->expired.ref_cnt) + return; + + trace_skw_rx_reorder_timeout(reorder->inst, reorder->peer_idx, + reorder->tid, reorder->expired.sn); + + spin_lock_bh(&reorder->todo.lock); + + if (!reorder->todo.actived) { + reorder->todo.actived = true; + reorder->todo.seq = reorder->expired.sn; + reorder->todo.reason = SKW_RELEASE_EXPIRED; + INIT_LIST_HEAD(&reorder->todo.list); + + skw_list_add(&reorder->skw->rx_todo_list, &reorder->todo.list); + } + + spin_unlock_bh(&reorder->todo.lock); + + skw_wakeup_rx(reorder->skw); +} + +void skw_peer_init(struct skw_peer *peer, const u8 *addr, int idx) +{ + int i; + struct skw_ctx_entry *entry; + + if (WARN_ON(!peer)) + return; + + if (idx >= SKW_MAX_PEER_SUPPORT) + peer->flags |= SKW_PEER_FLAG_BAD_ID; + + if (!addr) + peer->flags |= SKW_PEER_FLAG_BAD_ADDR; + + atomic_set(&peer->rx_filter, 0); + mutex_init(&peer->ptk_conf.lock); + mutex_init(&peer->gtk_conf.lock); + + for (i = 0; i < SKW_NR_TID; i++) { + atomic_set(&peer->reorder[i].ref_cnt, 0); + skw_compat_setup_timer(&peer->reorder[i].timer, skw_reorder_timeout); + INIT_LIST_HEAD(&peer->reorder[i].todo.list); + spin_lock_init(&peer->reorder[i].todo.lock); + spin_lock_init(&peer->reorder[i].lock); + } + + peer->idx = idx; + peer->iface = NULL; + peer->sm.addr = peer->addr; + peer->sm.state = SKW_STATE_NONE; + + entry = skw_ctx_entry(peer); + entry->peer = peer; + entry->idx = idx; + + if (addr) { + skw_ether_copy(entry->addr, addr); + skw_ether_copy(peer->addr, addr); + } +} + +void __skw_peer_ctx_transmit(struct skw_peer_ctx *ctx, bool enable) +{ + struct skw_ctx_entry *entry; + + if (WARN_ON(!ctx)) + return; + + lockdep_assert_held(&ctx->lock); + + if (enable) { + if (WARN_ON(!ctx->peer || ctx->peer->flags)) + return; + + entry = skw_ctx_entry(ctx->peer); + rcu_assign_pointer(ctx->entry, entry); + atomic_inc(&ctx->peer->iface->actived_ctx); + SKW_SET(ctx->peer->flags, SKW_PEER_FLAG_ACTIVE); + + } else { + entry = rcu_dereference_protected(ctx->entry, + lockdep_is_held(&ctx->lock)); + if (entry) { + atomic_dec(&entry->peer->iface->actived_ctx); + SKW_CLEAR(entry->peer->flags, SKW_PEER_FLAG_ACTIVE); + } + + RCU_INIT_POINTER(ctx->entry, NULL); + } +} + +void skw_peer_ctx_transmit(struct skw_peer_ctx *ctx, bool enable) +{ + if (!ctx) + return; + + skw_peer_ctx_lock(ctx); + __skw_peer_ctx_transmit(ctx, enable); + skw_peer_ctx_unlock(ctx); +} + +int __skw_peer_ctx_bind(struct skw_iface *iface, struct skw_peer_ctx *ctx, + struct skw_peer *peer) +{ + if (WARN_ON(!iface || !ctx)) + return -EINVAL; + + lockdep_assert_held(&ctx->lock); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + atomic_and(~BIT(ctx->idx), &iface->peer_map); +#else + atomic_set(&iface->peer_map, atomic_read(&iface->peer_map) & (~BIT(ctx->idx))); +#endif + + skw_peer_free(ctx->peer); + ctx->peer = NULL; + + if (peer) { + peer->iface = iface; + peer->sm.inst = iface->id; + peer->sm.addr = peer->addr; + peer->sm.iface_iftype = iface->wdev.iftype; + ctx->peer = peer; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + atomic_or(BIT(ctx->idx), &iface->peer_map); +#else + atomic_set(&iface->peer_map, atomic_read(&iface->peer_map) | BIT(ctx->idx)); +#endif + } + + return 0; +} + +int skw_peer_ctx_bind(struct skw_iface *iface, struct skw_peer_ctx *ctx, + struct skw_peer *peer) +{ + int ret; + + if (!iface || !ctx) + return -EINVAL; + + skw_dbg("ctx: %d, %s\n", ctx->idx, peer ? "bind" : "unbind"); + + mutex_lock(&ctx->lock); + ret = __skw_peer_ctx_bind(iface, ctx, peer); + mutex_unlock(&ctx->lock); + + return ret; +} + +struct skw_peer_ctx *skw_peer_ctx(struct skw_iface *iface, const u8 *mac) +{ + int idx; + u32 peer_idx_map; + struct skw_peer_ctx *ctx = NULL; + struct skw_core *skw = iface->skw; + + peer_idx_map = atomic_read(&iface->peer_map); + + if (!peer_idx_map || !mac) + return NULL; + + while (peer_idx_map) { + + idx = ffs(peer_idx_map) - 1; + + SKW_CLEAR(peer_idx_map, BIT(idx)); + + ctx = &skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; + if (!ctx) + continue; + + mutex_lock(&ctx->lock); + + if (ctx->peer && ether_addr_equal(mac, ctx->peer->addr)) { + mutex_unlock(&ctx->lock); + return ctx; + } + + mutex_unlock(&ctx->lock); + } + + return NULL; +} + +void skw_iface_set_wmm_capa(struct skw_iface *iface, const u8 *ies, size_t len) +{ + int i, j, tmp; + struct skw_wmm *wmm; + int ac[4] = {-1, -1, -1, -1}; + unsigned int oui = SKW_OUI(0x00, 0x50, 0xF2); + +#define SKW_WMM_SUBTYPE 2 +#define SKW_WMM_ACM BIT(4) + + wmm = (void *)cfg80211_find_vendor_ie(oui, SKW_WMM_SUBTYPE, ies, len); + if (!wmm) + goto default_wmm; + + if (wmm->version != 1) + goto default_wmm; + + iface->wmm.qos_enabled = true; + + for (i = 3; i >= 0; i--) { + for (tmp = i, j = 0; j < 4; j++) { + int id = ac[j]; + + if (id < 0) + break; + + if (wmm->ac[id].aifsn > wmm->ac[tmp].aifsn) { + tmp = id; + ac[j] = i; + } + } + + if (j < 4) + ac[j] = tmp; + } + + for (i = 0; i < 4; i++) { + int aci = ac[i]; + + switch (aci) { + case 0: + iface->wmm.ac[i].aci = SKW_WMM_AC_BE; + if (wmm->ac[aci].acm) + iface->wmm.acm |= BIT(SKW_WMM_AC_BE); + + iface->wmm.factor[SKW_WMM_AC_BE] = SKW_WMM_AC_BE - i; + break; + case 1: + iface->wmm.ac[i].aci = SKW_WMM_AC_BK; + if (wmm->ac[aci].acm) + iface->wmm.acm |= BIT(SKW_WMM_AC_BK); + + iface->wmm.factor[SKW_WMM_AC_BK] = SKW_WMM_AC_BK - i; + break; + case 2: + iface->wmm.ac[i].aci = SKW_WMM_AC_VI; + if (wmm->ac[aci].acm) + iface->wmm.acm |= BIT(SKW_WMM_AC_VI); + + iface->wmm.factor[SKW_WMM_AC_VI] = (SKW_WMM_AC_VI - i) << 2; + break; + case 3: + iface->wmm.ac[i].aci = SKW_WMM_AC_VO; + if (wmm->ac[aci].acm) + iface->wmm.acm |= BIT(SKW_WMM_AC_VI); + + iface->wmm.factor[SKW_WMM_AC_VO] = (SKW_WMM_AC_VO - i) << 1; + break; + default: + break; + } + + iface->wmm.ac[i].aifsn = wmm->ac[aci].aifsn; + iface->wmm.ac[i].txop_limit = le16_to_cpu(wmm->ac[aci].txop_limit); + + skw_dbg("aci: %d, aifsn: %d, txop_limit: %d, factor: %d\n", + iface->wmm.ac[i].aci, iface->wmm.ac[i].aifsn, + iface->wmm.ac[i].txop_limit, iface->wmm.factor[i]); + } + + for (i = 0; i < SKW_WMM_AC_MAX; i++) + if (iface->wmm.factor[i] < 0) + iface->wmm.factor[i] = 0; + + skw_dbg("wmm_acm: 0x%x\n", iface->wmm.acm); + return; + +default_wmm: + iface->wmm.acm = 0; + + iface->wmm.ac[0].aci = 0; + iface->wmm.ac[0].aifsn = 2; + iface->wmm.ac[0].txop_limit = 47; + + iface->wmm.ac[1].aci = 1; + iface->wmm.ac[1].aifsn = 2; + iface->wmm.ac[1].txop_limit = 94; + + iface->wmm.ac[2].aci = 2; + iface->wmm.ac[2].aifsn = 3; + iface->wmm.ac[2].txop_limit = 0; + + iface->wmm.ac[3].aci = 3; + iface->wmm.ac[3].aifsn = 7; + iface->wmm.ac[3].txop_limit = 0; +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iface.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iface.h new file mode 100755 index 0000000..e46c949 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iface.h @@ -0,0 +1,678 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_IFACE_H__ +#define __SKW_IFACE_H__ + +#include <linux/ieee80211.h> +#include <net/cfg80211.h> +#include "skw_work.h" +#include "skw_util.h" +#include "skw_dfs.h" + +#ifndef IEEE80211_CCMP_PN_LEN +#define IEEE80211_CCMP_PN_LEN 6 +#endif + +#define SKW_PN_LEN 6 +#define SKW_NR_TID 8 +#define SKW_MAX_DEFRAG_ENTRY 4 + +/* enable 80211W */ +#define SKW_NUM_DEFAULT_KEY 4 +#define SKW_NUM_DEFAULT_MGMT_KEY 2 + +/* SKW_NUM_DEFAULT_KEY + SKW_NUM_DEFAULT_MGMT_KEY */ +#define SKW_NUM_MAX_KEY 6 + +#define SKW_INVALID_ID 0xff +#define SKW_PEER_ALIGN 32 + +#define SKW_PEER_FLAG_TAINT BIT(0) +#define SKW_PEER_FLAG_BAD_ID BIT(1) +#define SKW_PEER_FLAG_BAD_ADDR BIT(2) +#define SKW_PEER_FLAG_ACTIVE BIT(3) +#define SKW_PEER_FLAG_DEAUTHED BIT(4) + +#define SKW_IFACE_FLAG_LEGACY_P2P_DEV BIT(0) +#define SKW_IFACE_FLAG_DEAUTH BIT(1) +#define SKW_IFACE_FLAG_OPENED BIT(2) +#define SKW_IFACE_FLAG_AP_STARTED BIT(3) +#define SKW_IFACE_FLAG_BUF_KEY BIT(4) + +#define SKW_IFACE_STA_ROAM_FLAG_CQM_LOW BIT(0) +/** + * enum SKW_STATES - STA state + * + * @SKW_STATE_NONE: STA exists without special state + * @SKW_STATE_AUTHING: STA is trying to authentiacate with a BSS + * @SKW_STATE_AUTHED: STA is authenticated + * @SKW_STATE_ASSOCING: STA is trying to assoc with a BSS + * @SKW_STATE_ASSOCED: STA is associated + * @SKW_STATE_COMPLETED, STA connection is compeleted + */ +enum SKW_STATES { + SKW_STATE_NONE, + SKW_STATE_AUTHING, + SKW_STATE_AUTHED, + SKW_STATE_ASSOCING, + SKW_STATE_ASSOCED, + SKW_STATE_COMPLETED, +}; + +enum SKW_STA_WORK_RETRY_STATE { + SKW_RETRY_NONE, + SKW_RETRY_AUTH, + SKW_RETRY_ASSOC, +}; + +enum SKW_WMM_AC { + SKW_WMM_AC_VO = 0, + SKW_WMM_AC_VI, + SKW_WMM_AC_BE, + SKW_WMM_AC_BK, + SKW_WMM_AC_MAX, +}; + +#define SKW_ACK_TXQ SKW_WMM_AC_MAX + +#define SKW_FRAG_STATUS_ACTIVE BIT(0) +#define SKW_FRAG_STATUS_CHK_PN BIT(1) + +enum skw_wireless_mode { + SKW_WIRELESS_11B = 1, + SKW_WIRELESS_11G, + SKW_WIRELESS_11A, + SKW_WIRELESS_11N, + SKW_WIRELESS_11AC, + SKW_WIRELESS_11AX, + SKW_WIRELESS_11G_ONLY, + SKW_WIRELESS_11N_ONLY, +}; + +enum interface_mode { + SKW_NONE_MODE = 0, + SKW_STA_MODE = 1, + SKW_AP_MODE = 2, + SKW_GC_MODE = 3, + SKW_GO_MODE = 4, + SKW_P2P_DEV_MODE = 5, + SKW_IBSS_MODE = 6, + SKW_MONITOR_MODE = 7, + + MAX_MODE_TYPE, +}; + +enum SKW_CHAN_BW_INFO { + SKW_CHAN_WIDTH_20, + SKW_CHAN_WIDTH_40, + SKW_CHAN_WIDTH_80, + SKW_CHAN_WIDTH_80P80, + SKW_CHAN_WIDTH_160, + + SKW_CHAN_WIDTH_MAX, +}; + +enum skw_rate_info_flags { + SKW_RATE_INFO_FLAGS_LEGACY, + SKW_RATE_INFO_FLAGS_HT, + SKW_RATE_INFO_FLAGS_VHT, + SKW_RATE_INFO_FLAGS_HE, +}; + +#define SKW_OPEN_FLAG_OFFCHAN_TX BIT(0) +struct skw_open_dev_param { + u16 mode; + u16 flags; /* reference SKW_OPEN_FLAG_ */ + u8 mac_addr[6]; +} __packed; + +struct skw_frag_entry { + u8 id; + u8 status; /* reference SKW_FRAG_STATUS */ + u16 pending_len; + u8 tid; + u8 frag_num; + u16 sn; + unsigned long start; + struct sk_buff_head skb_list; + + /* PN of the last fragment if CCMP was used */ + u8 last_pn[IEEE80211_CCMP_PN_LEN]; +}; + +struct skw_key { + struct rcu_head rcu; + u32 key_len; + u8 key_data[WLAN_MAX_KEY_LEN]; + u8 rx_pn[IEEE80211_NUM_TIDS][SKW_PN_LEN]; +}; + +#define SKW_KEY_FLAG_WEP_SHARE BIT(0) +#define SKW_KEY_FLAG_WEP_UNICAST BIT(1) +#define SKW_KEY_FLAG_WEP_MULTICAST BIT(2) + +struct skw_key_conf { + u8 skw_cipher; + u8 installed_bitmap; + u8 flags; /* reference to SKW_KEY_FLAG_ */ + u8 wep_idx; + struct mutex lock; + struct skw_key __rcu *key[SKW_NUM_MAX_KEY]; +}; + +struct skw_tid_rx { + u16 win_start; + u16 win_size; + u32 stored_num; + int ref_cnt; + struct rcu_head rcu_head; + struct skw_reorder_rx *reorder; + struct sk_buff_head *reorder_buf; +}; + +struct skw_rx_todo { + spinlock_t lock; + struct list_head list; + u16 seq; + u16 reason; + bool actived; +}; + +struct skw_rx_timer { + u16 sn; + u16 resv; + int ref_cnt; +}; + +struct skw_reorder_rx { + u32 tid: 4; + u32 inst: 2; + u32 peer_idx: 5; + u32 resv: 21; + + atomic_t ref_cnt; + struct skw_core *skw; + struct skw_peer *peer; + struct timer_list timer; + struct skw_rx_timer expired; + + struct skw_rx_todo todo; + + spinlock_t lock; + struct skw_tid_rx __rcu *tid_rx; +}; + +struct skw_ctx_entry { + u8 idx; + u8 padding; + u8 addr[ETH_ALEN]; + struct rcu_head rcu; + struct skw_peer *peer; +}; + +#define SKW_SM_FLAG_SAE_RX_CONFIRM BIT(0) + +struct skw_sm { + u8 *addr; + u8 inst; + u8 iface_iftype; + u16 flags; /* reference SKW_SM_FLAG_ */ + enum SKW_STATES state; + enum SKW_STA_WORK_RETRY_STATE rty_state; +}; + +struct skw_txba_ctrl { + u16 bitmap; + u16 blacklist; + u8 tx_try[SKW_NR_TID]; +}; + +enum skw_msdu_filter { + SKW_MSDU_FILTER_SUCCESS, + SKW_MSDU_FILTER_SNAP_MISMATCH, + SKW_MSDU_FILTER_ARP, + SKW_MSDU_FILTER_VLAN, + SKW_MSDU_FILTER_WAPI, + SKW_MSDU_FILTER_EAP = 5, + SKW_MSDU_FILTER_PPPOE, + SKW_MSDU_FILTER_TDLS, + SKW_MSDU_FILTER_DHCP = 11, + SKW_MSDU_FILTER_DHCPV6 = 12, +}; + +#define SKW_RX_FILTER_NONE 0 +#define SKW_RX_FILTER_SET (BIT(SKW_MSDU_FILTER_EAP) | BIT(SKW_MSDU_FILTER_WAPI)) + +#define SKW_RX_FILTER_EXCL (BIT(SKW_MSDU_FILTER_EAP) | \ + BIT(SKW_MSDU_FILTER_WAPI) | \ + BIT(SKW_MSDU_FILTER_ARP) | \ + BIT(SKW_MSDU_FILTER_DHCP) | \ + BIT(SKW_MSDU_FILTER_DHCPV6)) + +#define SKW_RX_FILTER_DBG (BIT(SKW_MSDU_FILTER_EAP) | \ + BIT(SKW_MSDU_FILTER_WAPI) | \ + BIT(SKW_MSDU_FILTER_ARP) | \ + BIT(SKW_MSDU_FILTER_DHCP) | \ + BIT(SKW_MSDU_FILTER_DHCPV6)) + +enum SKW_RX_MPDU_DESC_PPDUMODE { + SKW_PPDUMODE_11B_SHORT = 0, + SKW_PPDUMODE_11B_LONG, + SKW_PPDUMODE_11G, + SKW_PPDUMODE_HT_MIXED, + SKW_PPDUMODE_VHT_SU, + SKW_PPDUMODE_VHT_MU, + SKW_PPDUMODE_HE_SU, + SKW_PPDUMODE_HE_TB, + SKW_PPDUMODE_HE_ER_SU, + SKW_PPDUMODE_HE_MU, +}; + +struct skw_stats_info { + s16 rssi; + u64 pkts; + u64 bytes; + u64 drops; + u64 cal_time; + u64 cal_bytes; + u8 tx_psr; + u32 tx_failed; + u16 filter_cnt[35]; + u16 filter_drop_offload_cnt[35]; + u8 percent; + struct skw_rate rate; +}; + +struct skw_peer { + u8 idx; + u8 flags; /* reference SKW_PEER_FLAG_ */ + u8 addr[ETH_ALEN]; + u16 channel; + u16 rx_tid_map; + __be32 ip_addr; + + atomic_t rx_filter; + struct skw_sm sm; + struct skw_iface *iface; + struct skw_key_conf ptk_conf, gtk_conf; + + struct skw_txba_ctrl txba; + struct skw_reorder_rx reorder[SKW_NR_TID]; + struct skw_stats_info tx, rx; +}; + +struct skw_bss_cfg { + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; + u8 ctx_idx; + u8 bssid[ETH_ALEN]; + + enum nl80211_auth_type auth_type; + enum SKW_CHAN_BW_INFO width; + enum SKW_CHAN_BW_INFO ht_cap_chwidth; + struct cfg80211_crypto_settings crypto; + + struct ieee80211_channel *channel; + struct ieee80211_ht_cap *ht_cap; + struct ieee80211_vht_cap *vht_cap; +}; + +struct skw_survey_data { + u32 time; + u32 time_busy; + u32 time_ext_busy; + u8 chan; + u8 band; + s8 noise; + u8 resv; +} __packed; + +struct skw_survey_info { + struct list_head list; + struct skw_survey_data data; +}; + +struct skw_ac_param { + u8 aifsn:4; + u8 acm:1; + u8 aci:2; + u8 recv:1; + u8 ecw; + u16 txop_limit; +} __packed; + +struct skw_wmm { + u8 id; + u8 len; + u8 oui[3]; + u8 type; + u8 sub_type; + u8 version; + u8 qos; + u8 resv; + struct skw_ac_param ac[SKW_WMM_AC_MAX]; +} __packed; + +struct skw_list { + int count; + spinlock_t lock; + struct list_head list; +}; + +struct skw_peer_ctx { + int idx; + struct mutex lock; + struct skw_peer *peer; + struct skw_ctx_entry __rcu *entry; +}; + +struct skw_iftype_ext_cap { + u8 iftype; + u8 ext_cap[10]; + u8 ext_cap_len; +}; + +struct skw_ctx_pending { + unsigned long ctx_start; + unsigned long step_start; + unsigned long ctx_to; + u8 *auth_cmd; + int auth_cmd_len; + u8 *assoc_cmd; + int assoc_cmd_len; + int retry; + int redo; + enum nl80211_auth_type auth_type; +}; + +struct skw_sta_core { + struct mutex lock; + struct timer_list timer; + struct skw_ctx_pending pending; + + struct skw_sm sm; + struct skw_bss_cfg bss; + + unsigned long auth_start; + + u8 *assoc_req_ie; + u32 assoc_req_ie_len; + + struct cfg80211_bss *cbss; +}; + +struct skw_wmm_info { + u8 acm; + bool qos_enabled; + s8 factor[SKW_WMM_AC_MAX]; + struct skw_ac_param ac[SKW_WMM_AC_MAX]; +}; + +#define SKW_MAX_BUF_KEYS 4 +struct skw_key_params { + u8 mac_addr[ETH_ALEN]; + u8 key_type; + u8 cipher_type; + u8 pn[6]; + u8 key_id; + u8 key_len; + u8 key[WLAN_MAX_KEY_LEN]; +} __packed; + + +#define SKW_AID_DWORD BITS_TO_LONGS(64) + +struct skw_monitor_dbg_iface { + u8 addr[ETH_ALEN]; + struct net_device *ndev; + u32 frame_cnt; +}; + +struct skw_iface { + u8 id; + u8 lmac_id; + u8 addr[ETH_ALEN]; + + atomic_t peer_map; + atomic_t actived_ctx; + + struct mutex lock; + struct skw_core *skw; + struct net_device *ndev; + struct wireless_dev wdev; + struct list_head survey_list; + struct skw_key_conf key_conf; + struct cfg80211_qos_map *qos_map; + struct skw_event_work event_work; + struct proc_dir_entry *procfs; + struct skw_wmm_info wmm; + + u8 flags; /* reference SKW_IFACE_FLAG_ */ + u8 rand_mac_oui[3]; + u8 buf_keys_idx; + s16 default_multicast; + u16 mgmt_frame_bitmap; + int cpu_id; + + struct sk_buff_head txq[SKW_WMM_AC_MAX + 1]; + struct sk_buff_head tx_cache[SKW_WMM_AC_MAX + 1]; + struct skw_frag_entry frag[SKW_MAX_DEFRAG_ENTRY]; + struct skw_key_params buf_keys[SKW_MAX_BUF_KEYS]; + + struct { + enum skw_wireless_mode wireless_mode; + u16 scan_band_filter; + u16 resv; + } extend; + + union { + struct { + bool sme_external; + struct skw_bss_cfg cfg; + + u8 max_sta_allowed; + struct cfg80211_acl_data *acl; + + bool ht_required, vht_required; + + /* sme external */ + struct skw_list mlme_client_list; + unsigned long aid_map[SKW_AID_DWORD]; + + u8 *probe_resp; + size_t probe_resp_len; + int ap_isolate; + + struct { + u32 cac_time_ms; + unsigned long flags; + struct delayed_work cac_work; + } dfs; + } sap; + + struct { + bool sme_external; + bool is_roam_connect; + bool is_wep; + bool report_deauth; + struct skw_sta_core core; + struct work_struct work; + struct skw_connect_param *conn; + + struct { + spinlock_t lock; + u8 flags; + u8 target_bssid[ETH_ALEN]; + u8 target_chn; + } roam_data; + + u16 last_seq_ctrl; + } sta; + + struct { + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bssid[ETH_ALEN]; + u8 ssid_len; + u8 bw; + u16 flags; + bool joined; + u8 channel; + u8 band; + u16 beacon_int; + u32 center_freq1; + u32 center_freq2; + struct cfg80211_chan_def chandef; + } ibss; + }; +}; + +bool skw_acl_allowed(struct skw_iface *iface, u8 *addr); + +static inline const char *skw_state_name(enum SKW_STATES state) +{ + static const char * const st_name[] = {"NONE", "AUTHING", "AUTHED", + "ASSOCING", "ASSOCED", "COMPLETED"}; + + if (state >= ARRAY_SIZE(st_name)) + return "unknown"; + + return st_name[state]; +} + +static inline void skw_list_init(struct skw_list *list) +{ + spin_lock_init(&list->lock); + INIT_LIST_HEAD(&list->list); + list->count = 0; +} + +static inline void skw_list_add(struct skw_list *list, struct list_head *entry) +{ + spin_lock_bh(&list->lock); + list_add_tail(entry, &list->list); + list->count++; + spin_unlock_bh(&list->lock); +} + +static inline void skw_list_del(struct skw_list *list, struct list_head *entry) +{ + spin_lock_bh(&list->lock); + list_del(entry); + list->count--; + spin_unlock_bh(&list->lock); +} + +static inline void *skw_ctx_entry(const struct skw_peer *peer) +{ + return (char *)peer + ALIGN(sizeof(struct skw_peer), SKW_PEER_ALIGN); +} + +static inline bool is_skw_ap_mode(struct skw_iface *iface) +{ + return iface->wdev.iftype == NL80211_IFTYPE_AP || + iface->wdev.iftype == NL80211_IFTYPE_P2P_GO; +} + +static inline bool is_skw_sta_mode(struct skw_iface *iface) +{ + return iface->wdev.iftype == NL80211_IFTYPE_STATION || + iface->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT; +} + +static inline void skw_peer_ctx_lock(struct skw_peer_ctx *ctx) +{ + if (WARN_ON(!ctx)) + return; + + mutex_lock(&ctx->lock); +} + +static inline void skw_peer_ctx_unlock(struct skw_peer_ctx *ctx) +{ + if (WARN_ON(!ctx)) + return; + + mutex_unlock(&ctx->lock); +} +#if 0 +static inline void skw_sta_lock(struct skw_sta_core *core) +{ + mutex_lock(&core->lock); +} + +static inline void skw_sta_unlock(struct skw_sta_core *core) +{ + mutex_unlock(&core->lock); +} +#endif +static inline void skw_sta_assert_lock(struct skw_sta_core *core) +{ + lockdep_assert_held(&core->lock); +} + +static inline void skw_wdev_lock(struct wireless_dev *wdev) + __acquires(wdev) +{ + mutex_lock(&wdev->mtx); + __acquire(wdev->mtx); +} + +static inline void skw_wdev_unlock(struct wireless_dev *wdev) + __releases(wdev) +{ + __release(wdev->mtx); + mutex_unlock(&wdev->mtx); +} + +static inline void skw_wdev_assert_lock(struct skw_iface *iface) +{ + lockdep_assert_held(&iface->wdev.mtx); +} + +struct skw_iface *skw_add_iface(struct wiphy *wiphy, const char *name, + enum nl80211_iftype iftype, u8 *mac, + u8 id, bool need_ndev); +int skw_del_iface(struct wiphy *wiphy, struct skw_iface *iface); + +void skw_iface_set_wmm_capa(struct skw_iface *iface, const u8 *ies, + size_t ies_len); + +int skw_iface_setup(struct wiphy *wiphy, struct net_device *dev, + struct skw_iface *iface, const u8 *addr, + enum nl80211_iftype iftype, int id); + +int skw_iface_teardown(struct wiphy *wiphy, struct skw_iface *iface); + +int skw_cmd_open_dev(struct wiphy *wiphy, int inst, const u8 *mac_addr, + enum nl80211_iftype type, u16 flags); +void skw_purge_survey_data(struct skw_iface *iface); +void skw_ap_check_sta_throughput(void *data); +void skw_set_sta_timer(struct skw_sta_core *core, unsigned long timeout); + +struct skw_peer *skw_peer_alloc(void); +void skw_peer_init(struct skw_peer *peer, const u8 *addr, int idx); +struct skw_peer_ctx *skw_peer_ctx(struct skw_iface *iface, const u8 *mac); +void skw_peer_ctx_transmit(struct skw_peer_ctx *ctx, bool enable); +void __skw_peer_ctx_transmit(struct skw_peer_ctx *ctx, bool enable); +int skw_peer_ctx_bind(struct skw_iface *iface, struct skw_peer_ctx *ctx, + struct skw_peer *peer); +int __skw_peer_ctx_bind(struct skw_iface *iface, struct skw_peer_ctx *ctx, + struct skw_peer *peer); +void skw_peer_free(struct skw_peer *peer); +void skw_purge_key_conf(struct skw_key_conf *conf); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iw.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iw.c new file mode 100755 index 0000000..fdb2dc2 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iw.c @@ -0,0 +1,4109 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/string.h> +#include <linux/ctype.h> +#include <net/iw_handler.h> +#include <linux/udp.h> +#include <linux/if_ether.h> +#include <linux/ip.h> +#include <net/cfg80211-wext.h> + +#include "skw_core.h" +#include "skw_cfg80211.h" +#include "skw_iface.h" +#include "skw_iw.h" +#include "skw_log.h" + +static int skw_iw_commit(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + skw_dbg("traced\n"); + + return 0; +} + +static int skw_iw_get_name(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + skw_dbg("traced\n"); + + return 0; +} + +static int skw_iw_set_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + skw_dbg("traced\n"); + + return 0; +} + +static int skw_iw_get_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + skw_dbg("traced\n"); + + return 0; +} + +static int skw_iw_set_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + skw_dbg("traced\n"); + + return 0; +} + +static int skw_iw_get_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + skw_dbg("traced\n"); + + return 0; +} + +static struct iw_statistics *skw_get_wireless_stats(struct net_device *dev) +{ + skw_dbg("traced\n"); + + return NULL; +} + +static const iw_handler skw_iw_standard_handlers[] = { + IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)skw_iw_commit), + IW_HANDLER(SIOCGIWNAME, (iw_handler)skw_iw_get_name), + IW_HANDLER(SIOCSIWFREQ, (iw_handler)skw_iw_set_freq), + IW_HANDLER(SIOCGIWFREQ, (iw_handler)skw_iw_get_freq), + IW_HANDLER(SIOCSIWMODE, (iw_handler)skw_iw_set_mode), + IW_HANDLER(SIOCGIWMODE, (iw_handler)skw_iw_get_mode), +#ifdef CONFIG_CFG80211_WEXT_EXPORT + IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), + IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), + IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), +#endif +}; + +#ifdef CONFIG_WEXT_PRIV + +#define SKW_SET_LEN_64 64 +#define SKW_SET_LEN_128 128 +#define SKW_SET_LEN_256 256 +#define SKW_SET_LEN_512 512 +#define SKW_GET_LEN_512 512 +#define SKW_SET_LEN_1024 1024 +#define SKW_GET_LEN_1024 1024 +#define SKW_KEEP_BUF_SIZE 1024 +#define SKW_IW_WOW_BUF_SIZE 1024 + +/* max to 16 commands */ +#define SKW_IW_PRIV_SET (SIOCIWFIRSTPRIV + 1) +#define SKW_IW_PRIV_GET (SIOCIWFIRSTPRIV + 3) +#define SKW_IW_PRIV_AT (SIOCIWFIRSTPRIV + 5) +#define SKW_IW_PRIV_80211MODE (SIOCIWFIRSTPRIV + 6) +#define SKW_IW_PRIV_GET_80211MODE (SIOCIWFIRSTPRIV + 7) +#define SKW_IW_PRIV_KEEP_ALIVE (SIOCIWFIRSTPRIV + 8) +#define SKW_IW_PRIV_WOW_FILTER (SIOCIWFIRSTPRIV + 9) + +#define SKW_IW_PRIV_LAST SIOCIWLASTPRIV + +static struct skw_keep_active_setup kp_set = {0,}; +static u8 skw_wow_flted[256]; + +static int skw_keep_alive_add_checksum(u8 *buff, u32 len) +{ + u8 *ptr = buff; + struct iphdr *ip; + struct udphdr *udp; + __sum16 sum; + __wsum sum1; + u32 udp_len; + + ptr += sizeof(struct ethhdr); + ip = (struct iphdr *)ptr; + ip->check = 0; + ip->check = cpu_to_le16(ip_compute_csum(ip, 20)); + + ptr += sizeof(struct iphdr); + udp = (struct udphdr *)ptr; + udp->check = 0; + + udp_len = len - sizeof(struct ethhdr) + - sizeof(struct iphdr); + sum1 = csum_partial(ptr, + udp_len, 0); + sum = csum_tcpudp_magic(ip->saddr, ip->daddr, + udp_len, IPPROTO_UDP, sum1); + udp->check = cpu_to_le16(sum); + + skw_dbg("chsum %x %x ip:%x %x sum1:%x udp_len:%d\n", ip->check, sum, + ip->saddr, ip->daddr, sum1, udp_len); + return 0; +} + +static int skw_keep_active_rule_save(struct skw_core *skw, + struct skw_keep_active_rule *kp, u8 idx, u8 en, u32 flags) +{ + int ret; + + if (!skw || idx >= SKW_KEEPACTIVE_RULE_MAX) { + ret = -EFAULT; + return ret; + } + + if (kp) { + if (kp_set.rule[idx]) + SKW_KFREE(kp_set.rule[idx]); + + kp_set.rule[idx] = SKW_ZALLOC(kp->payload_len + + sizeof(*kp), GFP_KERNEL); + memcpy(kp_set.rule[idx], kp, kp->payload_len + sizeof(*kp)); + + if (SKW_KEEPALIVE_ALWAYS_FLAG & flags) + kp_set.rule[idx]->always = 1; + else + kp_set.rule[idx]->always = 0; + + if ((SKW_KEEPALIVE_NEEDCHKSUM_FLAG & flags) && + !(SKW_KEEPALIVE_FWCHKSUM_FLAG & flags)) { + skw_keep_alive_add_checksum(kp_set.rule[idx]->data[0].payload, + kp_set.rule[idx]->payload_len + - sizeof(struct skw_keep_active_rule_data)); + kp_set.rule[idx]->data[0].is_chksumed = 0; + } else if ((SKW_KEEPALIVE_NEEDCHKSUM_FLAG & flags) && + (SKW_KEEPALIVE_FWCHKSUM_FLAG & flags)) + kp_set.rule[idx]->data[0].is_chksumed = 1; + else + kp_set.rule[idx]->data[0].is_chksumed = 0; + + kp_set.flags[idx] = flags; + } + + if (en) + kp_set.en_bitmap |= BIT(idx); + else + kp_set.en_bitmap &= ~BIT(idx); + + skw_dbg("enable bitmap 0x%x\n", kp_set.en_bitmap); + skw_hex_dump("kpsave", &kp_set, sizeof(kp_set), false); + + return 0; +} + +static int skw_keep_active_disable_cmd(struct net_device *ndev) +{ + struct skw_spd_action_param spd; + int ret = 0; + + spd.sub_cmd = ACTION_DIS_KEEPALIVE; + spd.len = 0; + + skw_hex_dump("dpdis:", &spd, sizeof(spd), true); + ret = skw_send_msg(ndev->ieee80211_ptr->wiphy, ndev, + SKW_CMD_SET_SPD_ACTION, &spd, sizeof(spd), NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + return ret; +} + +static int skw_keep_active_append_cmd(struct net_device *ndev, + struct skw_core *skw, u32 idx_map, u32 idx) +{ + int ret = 0; + u32 rules = 0; + int total, fixed, len = 0, offset = 0; + struct skw_spd_action_param *spd = NULL; + struct skw_keep_active_param *kp_param = NULL; + + fixed = sizeof(struct skw_spd_action_param) + + sizeof(struct skw_keep_active_param); + total = fixed + SKW_KEEPACTIVE_CMD_BUF_MAX; + + spd = SKW_ZALLOC(total, GFP_KERNEL); + if (!spd) { + skw_err("malloc failed, size: %d\n", total); + return -ENOMEM; + } + + kp_param = (struct skw_keep_active_param *)((u8 *)spd + + sizeof(*spd)); + offset = fixed; + + while (idx_map) { + idx = ffs(idx_map) - 1; + + if (!kp_set.rule[idx]) { + skw_err("rule exception\n"); + break; + } + + if (offset + sizeof(struct skw_keep_active_rule) + + kp_set.rule[idx]->payload_len > total) + break; + + memcpy((u8 *)spd + offset, kp_set.rule[idx], + sizeof(struct skw_keep_active_rule) + + kp_set.rule[idx]->payload_len); + + offset += sizeof(struct skw_keep_active_rule) + + kp_set.rule[idx]->payload_len; + + if (++rules > (SKW_KEEPACTIVE_RULE_MAX >> 1)) + break; + + SKW_CLEAR(idx_map, BIT(idx)); + } + + kp_param->rule_num = rules; + spd->len = offset - sizeof(struct skw_spd_action_param); + len = offset; + + spd->sub_cmd = ACTION_EN_KEEPALIVE_APPEND; + + skw_dbg("len:%d rule num:%d\n", len, rules); + if (rules) { + skw_hex_dump("actvapd:", spd, len, true); + ret = skw_send_msg(ndev->ieee80211_ptr->wiphy, ndev, + SKW_CMD_SET_SPD_ACTION, spd, len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + } + + SKW_KFREE(spd); + return ret; +} + +static int skw_keep_active_cmd(struct net_device *ndev, struct skw_core *skw, + u8 en, u32 flags) +{ + int ret = 0, append = 0; + u32 idx_map, idx, rules = 0; + int total, fixed, len = 0, offset = 0; + struct skw_spd_action_param *spd = NULL; + struct skw_keep_active_param *kp_param = NULL; + + fixed = sizeof(struct skw_spd_action_param) + + sizeof(struct skw_keep_active_param); + total = fixed + SKW_KEEPACTIVE_CMD_BUF_MAX; + + spd = SKW_ZALLOC(total, GFP_KERNEL); + if (!spd) { + skw_err("malloc failed, size: %d\n", total); + return -ENOMEM; + } + + kp_param = (struct skw_keep_active_param *)((u8 *)spd + + sizeof(*spd)); + offset = fixed; + idx_map = kp_set.en_bitmap; + + while (idx_map) { + idx = ffs(idx_map) - 1; + SKW_CLEAR(idx_map, BIT(idx)); + + if (!kp_set.rule[idx]) { + skw_err("rule exception\n"); + break; + } + + if (offset + sizeof(struct skw_keep_active_rule) + + kp_set.rule[idx]->payload_len > total) + break; + + memcpy((u8 *)spd + offset, kp_set.rule[idx], + sizeof(struct skw_keep_active_rule) + + kp_set.rule[idx]->payload_len); + + offset += sizeof(struct skw_keep_active_rule) + + kp_set.rule[idx]->payload_len; + + SKW_CLEAR(idx_map, BIT(idx)); + + if (++rules >= (SKW_KEEPACTIVE_RULE_MAX >> 1)) { + append++; + break; + } + } + + kp_param->rule_num = rules; + spd->len = offset - sizeof(struct skw_spd_action_param); + len = offset; + + ret = skw_keep_active_disable_cmd(ndev); + + skw_dbg("len:%d rule num:%d\n", len, rules); + if (rules) { + spd->sub_cmd = ACTION_EN_KEEPALIVE; + skw_hex_dump("actv:", spd, len, true); + ret = skw_send_msg(ndev->ieee80211_ptr->wiphy, ndev, + SKW_CMD_SET_SPD_ACTION, spd, len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + } + + SKW_KFREE(spd); + + if (append) + ret = skw_keep_active_append_cmd(ndev, skw, idx_map, idx); + + return ret; +} + +//iwpriv wlan0 keep_alive idx=0,en=1,period=1000,flags=0/1, +//pkt=7c:7a:3c:81:e5:72:00:0b +static int skw_keep_active_set(struct net_device *dev, u8 *param, int len) +{ + int result_len = 0; + u8 *ch, *result_val; + char *hex = NULL; + char *tmp_hex = NULL; + u8 idx, en = 0, get_pkt = 0, send_cnt = 1; + u32 flags = 0; + u8 keep_alive[SKW_KEEPACTIVE_LENGTH_MAX]; + struct skw_keep_active_rule *kp = + (struct skw_keep_active_rule *)keep_alive; + int pos = 0, ret = 0; + struct skw_iface *iface = netdev_priv(dev); + struct skw_core *skw = iface->skw; + + memset(kp, 0, sizeof(*kp)); + + kp->send_cnt = SKW_KEEPACTIVE_RULE_SEND_CNT_DEF; + hex = param; + + hex = strstr(hex, "idx="); + if (hex) { + ch = strsep(&hex, "="); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("idx param\n"); + ret = -EFAULT; + goto error; + } + + ch = strsep(&hex, ","); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("idx param\n"); + ret = -ERANGE; + goto error; + } + + ret = kstrtou8(ch, 0, &idx); + if (ret) { + skw_err("idx param\n"); + ret = -EINVAL; + goto error; + } + } else { + skw_err("idx not found\n"); + ret = -EFAULT; + goto error; + } + + if (!hex) { + ret = -EBADF; + goto error; + } + + hex = strstr(hex, "en="); + if (hex) { + ch = strsep(&hex, "="); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("en param\n"); + ret = -EFAULT; + goto error; + } + + ch = strsep(&hex, ","); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("en param\n"); + ret = -ERANGE; + goto error; + } + + ret = kstrtou8(ch, 0, &en); + if (ret) { + skw_err("en param\n"); + ret = -EINVAL; + goto error; + } + } else { + skw_err("en not found\n"); + ret = -EFAULT; + goto error; + } + + if (!hex) + goto done; + + hex = strstr(hex, "period="); + if (hex) { + ch = strsep(&hex, "="); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("period param\n"); + ret = -EFAULT; + goto error; + } + + ch = strsep(&hex, ","); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("period param\n"); + ret = -ERANGE; + goto error; + } + + ret = kstrtou32(ch, 0, &kp->keep_interval); + if (ret) { + skw_err("period param\n"); + ret = -EINVAL; + goto error; + } + } + + if (!hex) + goto done; + + tmp_hex = hex; + hex = strstr(tmp_hex, "send_cnt="); + if (hex) { + ch = strsep(&hex, "="); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("send cnt\n"); + ret = -EFAULT; + goto error; + } + + ch = strsep(&hex, ","); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("send cnt\n"); + ret = -ERANGE; + goto error; + } + + ret = kstrtou8(ch, 0, &send_cnt); + if (ret) { + skw_err("send cnt invalid value\n"); + ret = -EINVAL; + goto error; + } + + kp->send_cnt = send_cnt; + if (kp->send_cnt <= 0) { + kp->send_cnt = SKW_KEEPACTIVE_RULE_SEND_CNT_DEF; + skw_err("send cnt is bigger than %d", + SKW_KEEPACTIVE_RULE_SEND_CNT_MAX); + } + + if (kp->send_cnt > SKW_KEEPACTIVE_RULE_SEND_CNT_MAX) { + kp->send_cnt = SKW_KEEPACTIVE_RULE_SEND_CNT_MAX; + skw_err("send cnt is bigger than %d", + SKW_KEEPACTIVE_RULE_SEND_CNT_MAX); + } + } else + hex = tmp_hex; + + hex = strstr(hex, "flags="); + if (hex) { + ch = strsep(&hex, "="); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("flags param\n"); + ret = -EFAULT; + goto error; + } + + ch = strsep(&hex, ","); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("flags param\n"); + ret = -ERANGE; + goto error; + } + + ret = kstrtou32(ch, 0, &flags); + if (ret) { + skw_err("flags param\n"); + ret = -EINVAL; + goto error; + } + } + + if (!hex) + goto done; + + hex = strstr(hex, "pkt="); + if (hex) { + ch = strsep(&hex, "="); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("pkt param\n"); + ret = -EFAULT; + goto error; + } + + result_val = kp->data[0].payload; + while (1) { + u8 temp = 0; + char *cp = strchr(hex, ':'); + + if (cp) { + *cp = 0; + cp++; + } + + ret = kstrtou8(hex, 16, &temp); + if (ret) { + skw_err("pkt param\n"); + ret = -EINVAL; + goto error; + } + + if (temp > 255) { + skw_err("pkt param\n"); + ret = -ERANGE; + goto error; + } + + result_val[pos] = temp; + result_len++; + pos++; + + if (!cp) + break; + + if (result_len + sizeof(*kp) + + sizeof(struct skw_keep_active_rule_data) >= + SKW_KEEPACTIVE_LENGTH_MAX) { + skw_err("len overload\n"); + ret = -ENOSPC; + goto error; + } + + hex = cp; + } + get_pkt = 1; + } + + kp->payload_len = result_len + sizeof(struct skw_keep_active_rule_data); + +done: + skw_dbg("idx:%d en:%d pr:%d cnt:%d pkt:%d len:%d\n", idx, en, + kp->keep_interval, kp->send_cnt, get_pkt, result_len); + skw_hex_dump("kp", kp, sizeof(*kp) + kp->payload_len, false); + + if (!(kp->keep_interval && get_pkt)) + kp = NULL; + + ret = skw_keep_active_rule_save(skw, kp, idx, en, flags); + if (ret) { + skw_err("save rule\n"); + goto error; + } + + ret = skw_keep_active_cmd(dev, skw, en, flags); + if (ret) { + skw_err("send rule\n"); + goto error; + } + + return 0; + +error: + skw_err("error:%d\n", ret); + return ret; +} + +//iwpriv wlan0 wow_filter idx=0,pattern=6+7c:7a:3c:81:e5:72#20+!ee:66#50+*3b:27:26:5e:2a +//iwpriv wlan0 wow_filter disable +//iwpriv wlan0 wow_filter enable/enable_whitelist +//iwpriv wlan0 wow_filter "list idx=0" +static struct skw_wow_rules_set wow_rules_set = {0,0,0 }; +static struct skw_wow_user_rules wow_user_rules = {0,}; + +static inline int ffs64(u64 x) +{ + int r = 1; + + if (!x) + return 0; + + if (!(x & 0xffffffffffffff)) { + x >>= 56; + r += 56; + } + + if (!(x & 0xffffffffffff)) { + x >>= 48; + r += 48; + } + + if (!(x & 0xffffffffff)) { + x >>= 40; + r += 40; + } + + if (!(x & 0xffffffff)) { + x >>= 32; + r += 32; + } + + if (!(x & 0xffffff)) { + x >>= 24; + r += 24; + } + + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + + if (!(x & 3)) { + x >>= 2; + r += 2; + } + + if (!(x & 1)) { + x >>= 1; + r += 1; + } + + return r; +} + +static int skw_send_wow_filter_append_cmd(struct net_device *ndev, + struct skw_core *skw, u64 idx_map) +{ + int ret = 0, append = 0; + u8 idx, rules = 0; + int total, fixed, offset = 0; + struct skw_spd_action_param *spd = NULL; + struct skw_wow_input_param *wow_param = NULL; + struct skw_wow_rule *send_rule = NULL; + + fixed = sizeof(struct skw_spd_action_param) + + sizeof(struct skw_wow_input_param); + total = fixed + SKW_WOW_RULE_CMD_BUF_MAX; //SKW_MAX_WOW_RULE_NUM_ONE_TIME * struct skw_wow_rule + + spd = SKW_ZALLOC(total, GFP_KERNEL); + if (!spd) { + skw_err("malloc failed, size: %d\n", total); + return -ENOMEM; + } + + wow_param = (struct skw_wow_input_param *)((u8 *)spd + + sizeof(*spd)); + offset = fixed; + + while (idx_map) { + idx = ffs64(idx_map) - 1; + + if (offset + sizeof(struct skw_wow_input_param) + + wow_rules_set.rules[idx].len > total) + break; + + spd->sub_cmd = ACTION_EN_WOW_APPEND; + wow_param->wow_flags = wow_rules_set.wow_flags; + memcpy(&wow_param->rules[rules], &wow_rules_set.rules[idx], + sizeof(struct skw_wow_rule)); + + send_rule = &wow_param->rules[rules]; + skw_hex_dump("wow_param->rules", &wow_param->rules[rules], sizeof(*send_rule), false); + + SKW_CLEAR(idx_map, (1ULL << idx)); + + if (++rules >= SKW_MAX_WOW_RULE_NUM_ONE_TIME) { + append++; + break; + } + } + wow_param->rule_num = rules; + spd->len = sizeof(struct skw_wow_input_param) + + sizeof(struct skw_wow_rule) * wow_param->rule_num; + + skw_dbg("wow_param->rule_num:%d len:%d %d\n", wow_param->rule_num, spd->len, total); + + skw_hex_dump("append spd", spd, total, false); + + ret = skw_msg_xmit(ndev->ieee80211_ptr->wiphy, 0, + SKW_CMD_SET_SPD_ACTION, spd, total, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + else { + memset(skw_wow_flted, 0, sizeof(skw_wow_flted)); + //memcpy(skw_wow_flted, param, len); //TBD + } + + if (append) + ret = skw_send_wow_filter_append_cmd(ndev, skw, idx_map); + + return ret; +} + +static int skw_send_wow_filter_cmd(struct net_device *ndev, struct skw_core *skw) +{ + int ret = 0, append = 0; + u64 idx_map = 0; + u8 idx, rules = 0; + int total, fixed, offset = 0; + struct skw_spd_action_param *spd = NULL; + struct skw_wow_input_param *wow_param = NULL; + struct skw_wow_rule *send_rule = NULL; + + fixed = sizeof(struct skw_spd_action_param) + + sizeof(struct skw_wow_input_param); + total = fixed + SKW_WOW_RULE_CMD_BUF_MAX; + + spd = SKW_ZALLOC(total, GFP_KERNEL); + if (!spd) { + skw_err("malloc failed, size: %d\n", total); + return -ENOMEM; + } + + wow_param = (struct skw_wow_input_param *)((u8 *)spd + + sizeof(*spd)); + offset = fixed; + + idx_map = wow_rules_set.en_bitmap; + + while (idx_map) { + idx = ffs64(idx_map) - 1; + + if (offset + sizeof(struct skw_wow_input_param) + + wow_rules_set.rules[idx].len > total) + break; + + spd->sub_cmd = ACTION_EN_WOW; + wow_param->wow_flags = wow_rules_set.wow_flags; + memcpy(&wow_param->rules[rules], &wow_rules_set.rules[idx], + sizeof(struct skw_wow_rule)); + + send_rule = &wow_param->rules[rules]; + + skw_hex_dump("wow_param->rules", &wow_param->rules[rules], sizeof(*send_rule), false); + SKW_CLEAR(idx_map, (1ULL << idx)); + + if (++rules >= SKW_MAX_WOW_RULE_NUM_ONE_TIME) { + append++; + break; + } + } + wow_param->rule_num = rules; + spd->len = sizeof(struct skw_wow_input_param) + + sizeof(struct skw_wow_rule) * wow_param->rule_num; + + skw_dbg("wow_param->rule_num:%d len:%d total:%d\n", wow_param->rule_num, spd->len, total); + + skw_hex_dump("spd", spd, sizeof(struct skw_spd_action_param) + spd->len, false); + + ret = skw_msg_xmit(ndev->ieee80211_ptr->wiphy, 0, + SKW_CMD_SET_SPD_ACTION, spd, total, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + else { + memset(skw_wow_flted, 0, sizeof(skw_wow_flted)); + //memcpy(skw_wow_flted, param, len); //TBD + } + + if (append) + ret = skw_send_wow_filter_append_cmd(ndev, skw, idx_map); + + return ret; +} + +int skw_wow_filter_set(struct net_device *ndev, u8 *param, int len, char *resp, int *extra_len) +{ + u8 *ch, *result_val, rule_idx = 0; + char *hex,*tmp_ch, *ptr; + struct skw_wow_rule *rule = NULL; + int pos = 0, ret = 0, offset, result_len = 0; + struct skw_pkt_pattern *ptn; + u8 *data; + u32 temp, resp_len = 0; + char help[] = "Usage:['list idx=0']|[disable]|[idx=0,pattern=6+10#23+!11][enable/enable_whitelist]"; + struct skw_iface *iface = netdev_priv(ndev); + struct skw_core *skw = iface->skw; + + if (!param) + return -EINVAL; + + data = SKW_ZALLOC(SKW_IW_WOW_BUF_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(data, param, len); + hex = data; + ptr = hex; + + if (!strncmp(hex, "list idx=", strlen("list idx="))) { + if (len <= strlen("list idx=")) { + resp_len = sprintf(resp, "ERROR: %s\n", + "list cmd"); + ret = -EFAULT; + goto free; + } + + hex += strlen("list idx="); + + ret = kstrtou8(hex, 0, &rule_idx); + if (ret) { + resp_len = sprintf(resp, "ERROR: %s\n", + "list cmd idx param"); + skw_err("idx param\n"); + ret = -EINVAL; + goto free; + } + + if (rule_idx >= SKW_MAX_WOW_RULE_NUM) { + resp_len = sprintf(resp, "ERROR: %s\n", + "list cmd idx param is too big"); + skw_err("rule_idx param:%d is too big\n", rule_idx); + ret = -EINVAL; + goto free; + } + + if (!((1ULL << rule_idx) & wow_user_rules.en_bitmap)) { + resp_len = sprintf(resp, "ERROR: %s\n", + "list cmd idx param is unset"); + skw_err("rule_idx param:%d is unset\n", rule_idx); + ret = -EINVAL; + goto free; + } + + resp_len = sprintf(resp, "List: %s\n", wow_user_rules.rules[rule_idx]); + goto free; + } + + if (!strcmp(hex, "disable")) { + if (len != sizeof("disable")) { + resp_len = sprintf(resp, "ERROR: %s\n", + "dis cmd"); + ret = -EFAULT; + goto free; + } + ret = skw_wow_disable(ndev->ieee80211_ptr->wiphy); + if (!ret) { + memset(skw_wow_flted, 0, sizeof(skw_wow_flted)); + memcpy(skw_wow_flted, param, len); + memset(&wow_rules_set, 0, sizeof(wow_rules_set)); + memset(&wow_user_rules, 0, sizeof(wow_user_rules)); + } + + goto free; + } + + //Add wow filter by idx + if (!strncmp(hex, "idx=", strlen("idx="))) { + ch = strsep(&hex, ","); + if ((ch == NULL) || (strlen(ch) == 0)) { + skw_err("idx param\n"); + resp_len = sprintf(resp, "ERROR: %s\n", + "idx param"); + ret = -ERANGE; + goto free; + } + + tmp_ch = strsep((char **)&ch, "="); + if ((tmp_ch == NULL) || (strlen(tmp_ch) == 0)) { + skw_err("idx param\n"); + resp_len = sprintf(resp, "ERROR: %s\n", + "idx param"); + ret = -EFAULT; + goto free; + } + + ret = kstrtou8(ch, 0, &rule_idx); + if (ret) { + skw_err("idx param\n"); + resp_len = sprintf(resp, "ERROR: %s\n", + "idx param"); + ret = -EINVAL; + goto free; + } + + if (rule_idx >= SKW_MAX_WOW_RULE_NUM) { + resp_len = sprintf(resp, "ERROR: %s\n", + "idx param is too big"); + skw_err("rule_idx param:%d is too big\n", rule_idx); + ret = -EINVAL; + goto free; + } + + //Store the rule + wow_user_rules.en_bitmap |= (1ULL << rule_idx); + + strncpy(wow_user_rules.rules[rule_idx], param, len); + + //Translate the wow filter to skw rules + rule = &wow_rules_set.rules[rule_idx]; + result_len = 0; + + ret = strncmp(hex, "pattern=", strlen("pattern=")); + if (!ret) { + hex += strlen("pattern="); + result_val = rule->rule; + + while (hex < ptr + len - 1) { + ret = sscanf(hex, "%d+%02x", + &offset, &temp); + if (ret != 2) { + ret = sscanf(hex, "%d+!%02x", + &offset, &temp); + if (ret != 2) { + ret = sscanf(hex, "%d+*%02x", + &offset, &temp); + if (ret != 2) { + resp_len = sprintf(resp, + "ERROR: %s\n", + "match char + +*"); + ret = -EINVAL; + goto free; + } + } + } + + if (offset > ETH_DATA_LEN) { + resp_len = sprintf(resp, + "ERROR: offset:%d over limit\n", + offset); + ret = -EINVAL; + goto free; + } + + ptn = (struct skw_pkt_pattern *)result_val; + result_val += sizeof(*ptn); + result_len += sizeof(*ptn); + + if (result_len >= sizeof(rule->rule)) { + resp_len = sprintf(resp, + "ERROR: %s\n", + "ptn over limit\n"); + ret = -ERANGE; + goto free; + } + + ptn->type_offset = PAT_TYPE_ETH; + ptn->offset = offset; + + ch = (u8 *)strsep(&hex, "+"); + if ((ch == NULL) || (strlen(ch) == 0)) { + resp_len = sprintf(resp, + "ERROR: %s\n", + "match char +\n"); + ret = -EINVAL; + goto free; + } + + if (hex[0] == '!') { + ptn->op = PAT_OP_TYPE_DIFF; + ch = strsep(&hex, "!"); + } else if (hex[0] == '*') { + ptn->op = PAT_OP_TYPE_CONTINUE_MATCH; + ch = strsep(&hex, "*"); + } + + pos = 0; + while (hex < ptr + len - 1) { + char *cp; + + if (!hex) { + ret = -EINVAL; + goto free; + } + + if (isxdigit(hex[0]) && + isxdigit(hex[1]) && + (sscanf(hex, "%2x", &temp) + == 1)) { + } else { + resp_len = sprintf(resp, + "ERROR: match char %c%c end\n", + hex[0], hex[1]); + ret = -EINVAL; + goto free; + } + + result_val[pos] = temp; + result_len++; + pos++; + + if (result_len >= sizeof(rule->rule)) { + resp_len = sprintf(resp, + "ERROR: %s\n", + "size over limit\n"); + ret = -ERANGE; + goto free; + } + + if (hex[2] == ',' || hex[2] == '#') + break; + else if (hex[2] == '\0') { + hex += 2; + break; + } else if (hex[2] != ':') { + resp_len = sprintf(resp, + "ERROR: char data %c\n", + hex[2]); + ret = -EINVAL; + goto free; + } + + cp = strchr(hex, ':'); + if (cp) { + *cp = 0; + cp++; + } + + hex = cp; + } + + result_val += pos; + ptn->len = pos; + + if (hex[2] == ',') { + hex += 2; + break; + } else if (hex[2] == '#') + ch = strsep(&hex, "#"); + } + } else { + resp_len = sprintf(resp, "ERROR: %s\n", + "match char pattern=\n"); + ret = -EINVAL; + goto free; + } + + rule->len = result_len; + wow_rules_set.en_bitmap |= (1ULL << rule_idx); + skw_hex_dump("rule", rule, sizeof(*rule), false); + ret = 0; + + goto free; + } + //Make sure enable/enable_whitelist is the final cmd + ret = strncmp(hex, "enable", strlen("enable")); + if (ret) { + resp_len = sprintf(resp, "ERROR\n"); + ret = -EFAULT; + goto free; + } + + if (len != sizeof("enable") && len != sizeof("enable_whitelist")) { + skw_err("Error: enable"); + resp_len = sprintf(resp, "Error: enable\n"); + ret = EINVAL; + goto free; + } + + hex += strlen("enable"); + ret = strncmp(hex, "_whitelist", strlen("_whitelist")); + if (!ret) + hex += strlen("_whitelist"); + else + wow_rules_set.wow_flags |= SKW_WOW_BLACKLIST_FILTER; + + //Send the rules to FW + ret = skw_send_wow_filter_cmd(ndev, skw); + if (ret) { + resp_len = sprintf(resp, "Error: send filter cmd\n"); + skw_err("Error: send filter cmd"); + } + +free: + + if (ret) + resp_len += sprintf(resp + resp_len, " %s\n", help); + else + resp_len += sprintf(resp + resp_len, "%s\n", "OK"); + + *extra_len = resp_len; + + SKW_KFREE(data); + return 0; +} + +static int skw_iwpriv_keep_alive(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + char *param = NULL; + char help[] = "ERROR useage:[idx=0,en=0/1,period=100,flags=0/1,pkt=7c:11]"; + int ret = 0; + + WARN_ON(wrqu->data.length > SKW_KEEP_BUF_SIZE); + + param = SKW_ZALLOC(SKW_KEEP_BUF_SIZE, GFP_KERNEL); + if (!param) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(param, wrqu->data.pointer, SKW_KEEP_BUF_SIZE)) { + skw_err("copy failed, length: %d\n", + wrqu->data.length); + + ret = -EFAULT; + goto free; + } + + skw_dbg("cmd: 0x%x, (len: %d)\n", + info->cmd, wrqu->data.length); + param[SKW_KEEP_BUF_SIZE - 1] = '\0'; + skw_hex_dump("param:", param, SKW_KEEP_BUF_SIZE, false); + + ret = skw_keep_active_set(dev, param, SKW_KEEP_BUF_SIZE); + if (ret) + memcpy(extra, help, sizeof(help)); + else + memcpy(extra, "OK", sizeof("OK")); + + wrqu->data.length = SKW_GET_LEN_512; + + skw_dbg("resp: %s\n", extra); + +free: + SKW_KFREE(param); + +out: + return ret; +} + +static int skw_iwpriv_wow_filter(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + char *param; + int ret; + int extra_len; + + WARN_ON(wrqu->data.length > SKW_IW_WOW_BUF_SIZE); + + param = SKW_ZALLOC(SKW_IW_WOW_BUF_SIZE, GFP_KERNEL); + if (!param) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(param, wrqu->data.pointer, SKW_IW_WOW_BUF_SIZE)) { + skw_err("copy failed, length: %d\n", + wrqu->data.length); + + ret = -EFAULT; + goto free; + } + + skw_hex_dump("flt", param, SKW_IW_WOW_BUF_SIZE, false); + + ret = skw_wow_filter_set(dev, param, + min_t(int, SKW_IW_WOW_BUF_SIZE, (int)wrqu->data.length), + extra, &extra_len); + + wrqu->data.length = extra_len; + +free: + SKW_KFREE(param); + +out: + return ret; +} + +static int skw_send_at_cmd(struct skw_core *skw, char *cmd, int cmd_len, + char *buf, int buf_len) +{ + int ret, len, resp_len, offset; + char *command, *resp; + + len = round_up(cmd_len, 4); + if (len > SKW_SET_LEN_256) + return -E2BIG; + + command = SKW_ZALLOC(SKW_SET_LEN_512, GFP_KERNEL); + if (!command) { + ret = -ENOMEM; + goto out; + } + + offset = (long)command & 0x7; + if (offset) { + offset = 8 - offset; + skw_detail("command: 0x%p, offset: %d\n", command, offset); + } + + resp_len = round_up(buf_len, skw->hw_pdata->align_value); + resp = SKW_ZALLOC(resp_len, GFP_KERNEL); + if (!resp) { + ret = -ENOMEM; + goto fail_alloc_resp; + } + + ret = skw_uart_open(skw); + if (ret < 0) + goto failed; + + memcpy(command + offset, cmd, cmd_len); + ret = skw_uart_write(skw, command + offset, len); + if (ret < 0) + goto failed; + + ret = skw_uart_read(skw, resp, resp_len); + if (ret < 0) + goto failed; + + memcpy(buf, resp, buf_len); + ret = 0; + +failed: + SKW_KFREE(resp); + +fail_alloc_resp: + SKW_KFREE(command); +out: + if (ret < 0) + skw_err("failed: ret: %d\n", ret); + + return ret; +} + +static int skw_iwpriv_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int i; + char param[32] = {0}; + struct skw_iface *iface = (struct skw_iface *)netdev_priv(dev); + + struct skw_iw_wireless_mode { + char *name; + enum skw_wireless_mode mode; + } modes[] = { + {"11B", SKW_WIRELESS_11B}, + {"11G", SKW_WIRELESS_11G}, + {"11A", SKW_WIRELESS_11A}, + {"11N", SKW_WIRELESS_11N}, + {"11AC", SKW_WIRELESS_11AC}, + {"11AX", SKW_WIRELESS_11AX}, + {"11G_ONLY", SKW_WIRELESS_11G_ONLY}, + {"11N_ONLY", SKW_WIRELESS_11N_ONLY}, + + /*keep last*/ + {NULL, 0} + }; + + WARN_ON(sizeof(param) < wrqu->data.length); + + if (copy_from_user(param, wrqu->data.pointer, sizeof(param))) { + skw_err("copy failed, length: %d\n", + wrqu->data.length); + + return -EFAULT; + } + + param[31] = '\0'; + skw_dbg("cmd: 0x%x, %s(len: %d)\n", + info->cmd, param, wrqu->data.length); + + for (i = 0; modes[i].name; i++) { + if (!strcmp(modes[i].name, (char *)param)) { + iface->extend.wireless_mode = modes[i].mode; + return 0; + } + } + + return -EINVAL; +} + +static int skw_iwpriv_get_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + skw_dbg("traced\n"); + return 0; +} + +static int skw_iwpriv_help(struct skw_iface *iface, void *param, char *args, + char *resp, int resp_len) +{ + int len = 0; + struct skw_iwpriv_cmd *cmd = param; + + len = sprintf(resp, " %s:\n", cmd->help_info); + cmd++; + + while (cmd->handler) { + len += sprintf(resp + len, "%-4.4s %s\n", "", cmd->help_info); + cmd++; + } + + return 0; +} + +static int skw_iwpriv_set_bandcfg(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + u16 res; + int ret; + + if (args == NULL) + return -EINVAL; + + ret = kstrtou16(args, 10, &res); + if (!ret && res < 3) { + if (res == 0) + iface->extend.scan_band_filter = 0; + else if (res == 1) + iface->extend.scan_band_filter = BIT(NL80211_BAND_2GHZ); + else if (res == 2) + iface->extend.scan_band_filter = BIT(NL80211_BAND_5GHZ); + + sprintf(resp, "ok"); + } else + sprintf(resp, "failed"); + + return ret; +} + +static int skw_iwpriv_get_bandcfg(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + if (!iface->extend.scan_band_filter) + sprintf(resp, "bandcfg=%s", "Auto"); + else if (iface->extend.scan_band_filter & BIT(NL80211_BAND_2GHZ)) + sprintf(resp, "bandcfg=%s", "2G"); + else if (iface->extend.scan_band_filter & BIT(NL80211_BAND_5GHZ)) + sprintf(resp, "bandcfg=%s", "5G"); + + return 0; +} + +int skw_set_cca_thre_ofdm(struct wiphy *wiphy, struct net_device *dev, + struct skw_cca_thre_ofdm *p_ofdm) + +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_CCA_THRE_OFDM, p_ofdm, sizeof(struct skw_cca_thre_ofdm))) { + skw_err("add cca thre ofdm tlv failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_cca_thre_11b(struct wiphy *wiphy, struct net_device *dev, + struct skw_cca_thre_11b *p_cca_11b) + +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_CCA_THRE_11B, p_cca_11b, sizeof(struct skw_cca_thre_11b))) { + skw_err("add cca thre 11b tlv failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_cca_thre_nowifi(struct wiphy *wiphy, struct net_device *dev, + struct skw_cca_thre_nowifi *p_nowifi) + +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_CCA_THRE_NOWIFI, p_nowifi, sizeof(struct skw_cca_thre_nowifi))) { + skw_err("add cca thre nowifi tlv failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + } + + skw_tlv_free(&conf); + + return ret; +} + + +int skw_set_edca_params(struct wiphy *wiphy, struct net_device *dev, + struct skw_edca_param_s *p_edca_params) + +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_EDCA_PARAM, p_edca_params, sizeof(struct skw_edca_param_s))) { + skw_err("add max ppdu dur tlv failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_max_ppdu_dur(struct wiphy *wiphy, struct net_device *dev, + struct skw_max_ppdu_dur *p_mppdu_dur) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("idx: %d, max ppdu dur %d\n", p_mppdu_dur->idx, p_mppdu_dur->max_ppdu_dur); + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_MAX_PPDU_DUR, p_mppdu_dur, sizeof(struct skw_max_ppdu_dur))) { + skw_err("add max ppdu dur tlv failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_force_rts_rate(struct wiphy *wiphy, struct net_device *dev, + struct skw_force_rts_rate *rate) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("2.4G rate: %d, 5G rate %d\n", rate->rts_rate_24G, rate->rts_rate_5G); + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve force rts rate tlv failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_FORCE_RTS_RATE, rate, sizeof(struct skw_force_rts_rate))) { + skw_err("add force rts rate tlv failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_force_rx_rsp_rate(struct wiphy *wiphy, struct net_device *dev, + struct skw_force_rx_rsp_rate *rate) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("11b long rate: %d, 11b short rate %d, ofdm rate %d\n", rate->rx_rsp_rate_11b_long, + rate->rx_rsp_rate_11b_short, rate->rx_rsp_rate_ofdm); + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve force rx rsp rate tlv failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_FORCE_RX_RSP_RATE, rate, sizeof(struct skw_force_rx_rsp_rate))) { + skw_err("add force rx rsp rate tlv failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_scan_time_cmd(struct wiphy *wiphy, struct net_device *dev, + struct skw_set_scan_time *time) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("active dwell time: %d, bypass active scan auto time %d\n", + time->active_dwell_time, time->bypass_active_acan_auto_time); + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve scan time failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_SCAN_TIME, time, sizeof(struct skw_set_scan_time))) { + skw_err("add scan time failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_tcp_disconn_wakeup_host(struct wiphy *wiphy, struct net_device *dev, + struct skw_set_tcpd_wakeup_host *flag) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("wakeup host:%d\n", flag->enable); + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve wakeup host flag failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_TCP_DISCONN_WAKEUP_HOST, flag, sizeof(struct skw_set_tcpd_wakeup_host))) { + skw_err("add wakeup host flag failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_rc_min_rate(struct wiphy *wiphy, struct net_device *dev, + struct skw_set_rate_control_min_rate *rate) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + skw_dbg("rc min rate:%d\n", rate->rstrict_min_rate); + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve rc min rate failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_RATE_CTRL_MIN_RATE, rate, sizeof(struct skw_set_rate_control_min_rate))) { + skw_err("add rc min rate failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_rate_control_rate_change(struct wiphy *wiphy, struct net_device *dev, + struct skw_set_rate_control_rate_change *rate) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_RATE_CTRL_RATE_CHANGE_PARAM, rate, sizeof(struct skw_set_rate_control_rate_change))) { + skw_err("add failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + } + + skw_tlv_free(&conf); + + return ret; +} + +int skw_set_rc_spe_rate(struct wiphy *wiphy, struct net_device *dev, + struct skw_set_rate_control_special_rate *rate) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc tlv failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve tlv failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, SKW_MIB_SET_RATE_CTRL_SPECIAL_FRM_RATE, rate, sizeof(struct skw_set_rate_control_special_rate))) { + skw_err("add tlv failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_err("failed, ret: %d\n", ret); + } + + skw_tlv_free(&conf); + + return ret; +} + +static int skw_iwpriv_set_max_ppdu_dur(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL; + struct skw_max_ppdu_dur max_ppdu_dur = {0}; + + if (!args) + return -EINVAL; + + skw_dbg("args: %s\n", args); + + p = strchr(args, ','); + if (!p) { + skw_err("idx not found\n"); + return -ENOTSUPP; + } + + skw_dbg("idx found %s\n", p - 1); + + max_ppdu_dur.idx = simple_strtol(p - 1, NULL, 10); + if (max_ppdu_dur.idx < 0 || max_ppdu_dur.idx > 5) { + skw_err("idx out of range\n"); + return -EINVAL; + } + + p = p + strlen(","); + if (!p) { + skw_err("mppdu dur not found\n"); + return -ENOTSUPP; + } + + skw_dbg("mppdu dur found %s\n", p); + max_ppdu_dur.max_ppdu_dur = simple_strtol(p, NULL, 10); + switch (max_ppdu_dur.idx) { + case 0: + if (max_ppdu_dur.max_ppdu_dur > 0xFFFF) { + max_ppdu_dur.max_ppdu_dur = 0xFFFF; + skw_warn("The max ppdu dur of idx 0 is 0xFFFF\n"); + } + + break; + + case 1: + if (max_ppdu_dur.max_ppdu_dur > 0x7FFF) { + max_ppdu_dur.max_ppdu_dur = 0x7FFF; + skw_warn("The max ppdu dur of idx 1 is 0x7FFF\n"); + } + + break; + + case 2: + case 3: + case 4: + case 5: + if (max_ppdu_dur.max_ppdu_dur > 0x1FFF) { + max_ppdu_dur.max_ppdu_dur = 0x1FFF; + skw_warn("The max ppdu dur of idx 1 is 0x1FFF\n"); + } + + break; + + default: + break; + } + + skw_dbg("max ppdu dur idx %d, dur %d\n", max_ppdu_dur.idx, max_ppdu_dur.max_ppdu_dur); + + ret = skw_set_max_ppdu_dur(iface->wdev.wiphy, iface->ndev, &max_ppdu_dur); + if (!ret) + sprintf(resp, "set max ppdu dur ok "); + else + sprintf(resp, "set max ppdu dur failed"); + + return ret; +} + +static u16 skw_iwpriv_convert_string_to_u (char *ptr, u8 length) +{ + char *buffer = NULL; + unsigned long num = 0; + int ret = 0; + + buffer = kmalloc(length + 1, GFP_KERNEL); + if (!buffer) { + skw_err("mem alloc failed\n"); + return -ENOMEM; + } + memcpy(buffer, ptr, length); + buffer[length] = '\0'; + + ret = kstrtoul(buffer, 0, &num); + if (ret == 0) { + if (num <= 0xFFFF) { + kfree(buffer); + return (u16)num; + } else if (num <= 0xFF) { + kfree(buffer); + return (u8)num; + } else { + skw_err("tred beyond u8 and u16\n"); + kfree(buffer); + return -ERANGE; + } + } else { + skw_err("transfer fail\n"); + kfree(buffer); + return -EINVAL; + } +} + +static u32 skw_iwpriv_convert_string_to_u32(char *ptr, unsigned int length) +{ + if (ptr == NULL || length <= 0) { + skw_err("Invalid input parameters"); + return -EINVAL; + } + + if (length >= 2 && ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) { + char *buffer = NULL; + unsigned long num = 0; + int ret = 0; + + buffer = kmalloc(length + 1, GFP_KERNEL); + if (!buffer) { + skw_err("mem alloc failed\n"); + return -ENOMEM; + } + memcpy(buffer, ptr, length); + buffer[length] = '\0'; + + ret = kstrtoul(buffer, 16, &num); + if (ret == 0) { + if (num <= 0xFFFFFFFF) { + kfree(buffer); + return (u32)num; + } else { + skw_err("value exceeds u32 range"); + kfree(buffer); + return -ERANGE; + } + } else { + skw_err("hex conversion failed"); + kfree(buffer); + return -EINVAL; + } + } else { + skw_err("not a valid hex string"); + return -EINVAL; + } +} + +int skw_iwpriv_set_edca_params(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_edca_param_s edca_params = {0}; + u8 parmas_len = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args: %s\n", args); + + parmas_len = strlen(args); + if (parmas_len == 1) { + ret = simple_strtol(args, NULL, 10); + if (!ret) { + skw_dbg(" not set edca params\n"); + edca_params.enable = 0; + goto SETPARA; + } + + skw_err(" need more edca params\n"); + goto SETFAIL; + + } else if (parmas_len > 1) { + p = strchr(args, ','); + if (!p) { + skw_dbg("flag not found\n"); + return -ENOTSUPP; + } + + skw_dbg("params found %s\n", p - 1); + + edca_params.enable = simple_strtol(p - 1, NULL, 10); + if (edca_params.enable != 0 && edca_params.enable != 1) { + skw_err("flag error %d\n", edca_params.enable); + return -EINVAL; + } + + if (edca_params.enable == 0) { + skw_err("not set edca params\n"); + goto SETPARA; + } + + skw_dbg("start set edca params ...\n"); + } + + /* parse AC_BE parametes */ + /** AciAifn **/ + //p = p + strlen(","); + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_BE AciAifn not found\n"); + return -ENOTSUPP; + } + + //edca_params.ac_best_effort.aci_aifn = simple_strtol(p, NULL, 10); + edca_params.ac_best_effort.aci_aifn = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_best_effort.aci_aifn > 0xFF) { + skw_warn("AC_BE AciAifn limit is 0xFF\n"); + edca_params.ac_best_effort.aci_aifn = 0xFF; + } + + skw_dbg("AC_BE Set AciAifn %d\n", edca_params.ac_best_effort.aci_aifn); + + //return 0; + /** EcWminEcWmax **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_BE EcWminEcWmax not found\n"); + return -ENOTSUPP; + } + + //edca_params.ac_best_effort.ec_wmin_wmax = simple_strtol(p, NULL, 10); + edca_params.ac_best_effort.ec_wmin_wmax = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_best_effort.ec_wmin_wmax > 0xFF) { + skw_dbg("AC_BE EcWminEcWmax limit is 0xFF\n"); + edca_params.ac_best_effort.ec_wmin_wmax = 0xFF; + } + + skw_dbg("AC_BE Set EcWminEcWmax %d\n", edca_params.ac_best_effort.ec_wmin_wmax); + + /** TxopLimit **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_BE TxopLimit not found\n"); + return -ENOTSUPP; + } + + //edca_params.ac_best_effort.txop_limit = simple_strtol(p, NULL, 10); + edca_params.ac_best_effort.txop_limit = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_best_effort.txop_limit > 0xFFFF) { + skw_dbg("AC_BE TxopLimit limit is 0xFF\n"); + edca_params.ac_best_effort.txop_limit = 0xFFFF; + } + + skw_dbg("AC_BE Set TxopLimit %d\n", edca_params.ac_best_effort.txop_limit); + + /* parse AC_BG parametes */ + /** AciAifn **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_BG AciAifn not found\n"); + return -ENOTSUPP; + } + + //edca_params.ac_background.aci_aifn = simple_strtol(p, NULL, 10); + edca_params.ac_background.aci_aifn = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_background.aci_aifn > 0xFF) { + skw_warn("AC_BG AciAifn limit is 0xFF\n"); + edca_params.ac_background.aci_aifn = 0xFF; + } + + skw_dbg("AC_BG Set AciAifn %d\n", edca_params.ac_background.aci_aifn); + + /** EcWminEcWmax **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_BG EcWminEcWmax not found\n"); + return -ENOTSUPP; + } + + //edca_params.ac_background.ec_wmin_wmax = simple_strtol(p, NULL, 10); + edca_params.ac_background.ec_wmin_wmax = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_background.ec_wmin_wmax > 0xFF) { + skw_warn("AC_BG EcWminEcWmax limit is 0xFF\n"); + edca_params.ac_background.ec_wmin_wmax = 0xFF; + } + + skw_dbg("AC_BG Set EcWminEcWmax %d\n", edca_params.ac_background.ec_wmin_wmax); + + /** TxopLimit **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_BG TxopLimit not found\n"); + return -ENOTSUPP; + } + + //edca_params.ac_best_effort.txop_limit = simple_strtol(p, NULL, 10); + edca_params.ac_background.txop_limit = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_background.txop_limit > 0xFFFF) { + skw_warn("AC_BG TxopLimit limit is 0xFF\n"); + edca_params.ac_background.txop_limit = 0xFFFF; + } + + skw_dbg("AC_BG Set TxopLimit %d\n", edca_params.ac_best_effort.txop_limit); + + /* parse AC_VIDEO parametes */ + /** AciAifn **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_VIDEO AciAifn not found\n"); + return -ENOTSUPP; + } + + edca_params.ac_video.aci_aifn = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_video.aci_aifn > 0xFF) { + skw_warn("AC_VIDEO AciAifn limit is 0xFF\n"); + edca_params.ac_video.aci_aifn = 0xFF; + } + + skw_dbg("AC_VIDEO Set AciAifn %d\n", edca_params.ac_video.aci_aifn); + + /** EcWminEcWmax **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_VIDEO EcWminEcWmax not found\n"); + return -ENOTSUPP; + } + + edca_params.ac_video.ec_wmin_wmax = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_video.ec_wmin_wmax > 0xFF) { + skw_warn("AC_VIDEO EcWminEcWmax limit is 0xFF\n"); + edca_params.ac_video.ec_wmin_wmax = 0xFF; + } + + skw_dbg("AC_VIDEO Set EcWminEcWmax %d\n", edca_params.ac_video.ec_wmin_wmax); + + /** TxopLimit **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_VIDEO TxopLimit not found\n"); + return -ENOTSUPP; + } + + edca_params.ac_video.txop_limit = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_video.txop_limit > 0xFFFF) { + skw_warn("AC_VIDEO TxopLimit limit is 0xFF\n"); + edca_params.ac_video.txop_limit = 0xFFFF; + } + + skw_dbg("AC_VIDEO Set TxopLimit %d\n", edca_params.ac_video.txop_limit); + + /* parse AC_VOICE parametes */ + /** AciAifn **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_VOICE AciAifn not found\n"); + return -ENOTSUPP; + } + + edca_params.ac_voice.aci_aifn = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_voice.aci_aifn > 0xFF) { + skw_warn("AC_VOICE AciAifn limit is 0xFF\n"); + edca_params.ac_voice.aci_aifn = 0xFF; + } + + skw_dbg("AC_VOICE Set AciAifn %d\n", edca_params.ac_voice.aci_aifn); + + /** EcWminEcWmax **/ + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("AC_VOICE EcWminEcWmax not found\n"); + return -ENOTSUPP; + } + + edca_params.ac_voice.ec_wmin_wmax = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (edca_params.ac_voice.ec_wmin_wmax > 0xFF) { + skw_warn("AC_VOICE EcWminEcWmax limit is 0xFF\n"); + edca_params.ac_voice.ec_wmin_wmax = 0xFF; + } + + skw_dbg("AC_VOICE Set EcWminEcWmax %d\n", edca_params.ac_voice.ec_wmin_wmax); + + /** TxopLimit **/ + p = q; + q = strchr(p + 1, ','); + if (!p) { + skw_err("AC_VOICE TxopLimit not found\n"); + return -ENOTSUPP; + } + + edca_params.ac_voice.txop_limit = simple_strtol(p + 1, NULL, 10); + if (edca_params.ac_voice.txop_limit > 0xFFFF) { + skw_warn("AC_VOICE TxopLimit limit is 0xFF\n"); + edca_params.ac_voice.txop_limit = 0xFFFF; + } + + skw_dbg("AC_VOICE Set TxopLimit %d\n", edca_params.ac_voice.txop_limit); + +SETPARA: + ret = skw_set_edca_params(iface->wdev.wiphy, iface->ndev, &edca_params); + if (!ret) + sprintf(resp, "set edca params ok "); + else + sprintf(resp, "set edca params failed"); + + return ret; + +SETFAIL: + + sprintf(resp, "set edca params failed"); + + return -EINVAL; +} + +static int skw_iwpriv_set_cca_thre_nowifi(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL; + struct skw_cca_thre_nowifi nowifi = {0}; + int temp_val = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args: %s\n", args); + + p = args; + if (!p) { + skw_err("nowifi not found\n"); + return -ENOTSUPP; + } else { + skw_err("nowifi found %s\n", p); + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0 || temp_val < -255) { + skw_warn("The max CCA Thre NOWIFI is 0xFF\n"); + return -EINVAL; + } + nowifi.val = 0 - temp_val; + } + + skw_dbg("nowifi found %s\n", p); + + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0 || temp_val < -255) { + skw_warn("The max CCA Thre NOWIFI is 0xFF\n"); + return -EINVAL; + } + + nowifi.val = 0 - temp_val; + + skw_dbg("Set CCA Thre NOWIFI %d\n", nowifi.val); + + ret = skw_set_cca_thre_nowifi(iface->wdev.wiphy, iface->ndev, &nowifi); + if (!ret) + sprintf(resp, "set CCA Thre NOWIFI ok "); + else + sprintf(resp, "set CCA Thre NOWIFI failed"); + + return ret; +} + +static int skw_iwpriv_set_cca_thre_11b(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL; + struct skw_cca_thre_11b cca_11b = {0}; + int temp_val = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args: %s\n", args); + + p = args; + if (!p) { + skw_err("11b not found\n"); + return -ENOTSUPP; + } else { + skw_err("11b found %s\n", p); + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0 || temp_val < -255) { + skw_warn("The max CCA Thre 11b is 0xFF\n"); + return -EINVAL; + } + cca_11b.val = 0 - temp_val; + } + + skw_dbg("11b found %s\n", p); + + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0 || temp_val < -255) { + skw_warn("The max CCA Thre 11b is 0xFF\n"); + return -EINVAL; + } + + cca_11b.val = 0 - temp_val; + + skw_dbg("Set CCA Thre 11b %d\n", cca_11b.val); + + ret = skw_set_cca_thre_11b(iface->wdev.wiphy, iface->ndev, &cca_11b); + if (!ret) + sprintf(resp, "set CCA Thre 11b ok "); + else + sprintf(resp, "set CCA Thre 11b failed"); + + return ret; +} + +static int skw_iwpriv_set_cca_thre_ofdm(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL; + struct skw_cca_thre_ofdm ofdm = {0}; + int temp_val = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args: %s\n", args); + + p = args; + if (!p) { + skw_err("ofdm not found\n"); + return -ENOTSUPP; + } else { + skw_err("ofdm found %s\n", p); + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0 || temp_val < -255) { + skw_warn("The max CCA Thre ofdm is 0xFF\n"); + return -EINVAL; + } + ofdm.val = 0 - temp_val; + } + + skw_dbg("ofdm found %s\n", p); + + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0 || temp_val < -255) { + skw_warn("The max CCA Thre ofdm is 0xFF\n"); + return -EINVAL; + } + + ofdm.val = 0 - temp_val; + + skw_dbg("Set CCA Thre OFDM %d\n", ofdm.val); + + ret = skw_set_cca_thre_ofdm(iface->wdev.wiphy, iface->ndev, &ofdm); + if (!ret) + sprintf(resp, "set CCA Thre ofdm ok "); + else + sprintf(resp, "set CCA Thre ofdm failed"); + + return ret; +} + +static int skw_is_value_in_enum(u8 value) +{ + switch (value) { + case LEGA_11B_SHORT_2M: + case LEGA_11B_SHORT_55M: + case LEGA_11B_SHORT_11M: + case LEGA_11B_LONG_1M: + case LEGA_11B_LONG_2M: + case LEGA_11B_LONG_55M: + case LEGA_11B_LONG_11M: + case OFDM_6M: + case OFDM_9M: + case OFDM_12M: + case OFDM_18M: + case OFDM_24M: + case OFDM_36M: + case OFDM_48M: + case OFDM_54M: + case HT_MCS_0: + case HT_MCS_1: + case HT_MCS_2: + case HT_MCS_3: + case HT_MCS_4: + case HT_MCS_5: + case HT_MCS_6: + case HT_MCS_7: + case HT_MCS_8: + case HT_MCS_9: + case HT_MCS_10: + case HT_MCS_11: + case HT_MCS_12: + case HT_MCS_13: + case HT_MCS_14: + case HT_MCS_15: + case HT_MCS_16: + case HT_MCS_17: + case HT_MCS_18: + case HT_MCS_19: + case HT_MCS_20: + case HT_MCS_21: + case HT_MCS_22: + case HT_MCS_23: + case HT_MCS_24: + case HT_MCS_25: + case HT_MCS_26: + case HT_MCS_27: + case HT_MCS_28: + case HT_MCS_29: + case HT_MCS_30: + case HT_MCS_31: + case VHT_MCS_0: + case VHT_MCS_1: + case VHT_MCS_2: + case VHT_MCS_3: + case VHT_MCS_4: + case VHT_MCS_5: + case VHT_MCS_6: + case VHT_MCS_7: + case VHT_MCS_8: + case VHT_MCS_9: + case HE_MCS_0: + case HE_MCS_1: + case HE_MCS_2: + case HE_MCS_3: + case HE_MCS_4: + case HE_MCS_5: + case HE_MCS_6: + case HE_MCS_7: + case HE_MCS_8: + case HE_MCS_9: + case HE_MCS_10: + case HE_MCS_11: + case ER_NDCM_1SS_242TONE_MCS0: + case ER_NDCM_1SS_242TONE_MCS1: + case ER_NDCM_1SS_242TONE_MCS2: + case ER_NDCM_1SS_106TONE_MCS0: + case ER_DCM_1SS_242TONE_MCS0: + case ER_DCM_1SS_242TONE_MCS1: + case ER_DCM_1SS_106TONE_MCS0: + case NER_DCM_1SS_MCS0: + case NER_DCM_1SS_MCS1: + case NER_DCM_1SS_MCS3: + case NER_DCM_1SS_MCS4: + case NER_DCM_2SS_MCS0: + case NER_DCM_2SS_MCS1: + case NER_DCM_2SS_MCS3: + case NER_DCM_2SS_MCS4: + return 1; + + default: + return 0; + } +} + +static int skw_iwpriv_set_force_rts_rate(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_force_rts_rate rts_rate = {0}; + u8 parmas_len = 0, val = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args: %s\n", args); + + /* parse rts rate flag */ + parmas_len = strlen(args); + if (parmas_len == 1) { + ret = simple_strtol(args, NULL, 10); + if (!ret) { + skw_warn(" not force set rts rate\n"); + rts_rate.enable = 0; + goto SETPARA; + } + + skw_warn(" need more rate params\n"); + goto SETFAIL; + + } else if (parmas_len > 1) { + p = strchr(args, ','); + if (!p) { + skw_err("flag not found\n"); + return -ENOTSUPP; + } + + skw_dbg("params found %s\n", p - 1); + + rts_rate.enable = simple_strtol(p - 1, NULL, 10); + if (rts_rate.enable != 0 && rts_rate.enable != 1) { + skw_err("flag error %d\n", rts_rate.enable); + return -EINVAL; + } + + if (rts_rate.enable == 0) { + skw_err("not set rts_rate params\n"); + goto SETPARA; + } + + skw_err("start set rts_rate params ...\n"); + } + + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("rts rate 2.4G not found\n"); + return -ENOTSUPP; + } + + val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (!skw_is_value_in_enum(val)) { + skw_warn("invalid 2.4G rate %d\n", val); + return -EINVAL; + } + + rts_rate.rts_rate_24G = val; + + skw_dbg("rts rate 2.4G: %d\n", rts_rate.rts_rate_24G); + + p = q; + q = strchr(p + 1, ','); + if (!p) { + skw_err("rts rate 5G not found\n"); + return -ENOTSUPP; + } + + val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (!skw_is_value_in_enum(val)) { + skw_warn("invalid 5G rate %d\n", val); + return -EINVAL; + } + + rts_rate.rts_rate_5G = val; + skw_dbg("rts_rate_5G: %d\n", rts_rate.rts_rate_5G); + +SETPARA: + ret = skw_set_force_rts_rate(iface->wdev.wiphy, iface->ndev, &rts_rate); + if (!ret) + sprintf(resp, "set force rts rate ok "); + else + sprintf(resp, "set force rts rate failed"); + + return ret; + +SETFAIL: + sprintf(resp, "set force rts rate failed"); + + return -EINVAL; +} + +static int skw_iwpriv_set_force_rx_rsp_rate(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_force_rx_rsp_rate rate = {0}; + u8 parmas_len = 0, val = 0; + + if (!args) + return -EINVAL; + + skw_dbg("skw_iwpriv_set_force_rts_rate args %s\n", args); + + /* parse rts rate flag */ + parmas_len = strlen(args); + if (parmas_len == 1) { + ret = simple_strtol(args, NULL, 10); + if (!ret) { + skw_warn(" not force set rx rsp rate\n"); + goto SETPARA; + } else if (ret == 1) { + skw_warn(" need more rate params\n"); + goto SETFAIL; + } else { + goto SETFAIL; + } + } else if (parmas_len > 1) { + p = strchr(args, ','); + if (!p) { + skw_err("flag not found\n"); + return -ENOTSUPP; + } + + skw_dbg("params found %s\n", p - 1); + + rate.enable = simple_strtol(p - 1, NULL, 10); + if (rate.enable != 0 && rate.enable != 1) { + skw_err("flag error %d\n", rate.enable); + return -EINVAL; + } + + if (rate.enable == 0) { + skw_err("not set rx rsp rate params\n"); + goto SETPARA; + } + + skw_dbg("start set rx rsp rate params ...\n"); + } + + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("11b long not found\n"); + return -ENOTSUPP; + } + + val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (!skw_is_value_in_enum(val)) { + skw_warn("invalid 11b long rate %d\n", val); + return -EINVAL; + } + + rate.rx_rsp_rate_11b_long = val; + skw_dbg("11b long rate: %d\n", rate.rx_rsp_rate_11b_long); + + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("11b short not found\n"); + return -ENOTSUPP; + } + + val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (!skw_is_value_in_enum(val)) { + skw_warn("invalid 11b short rate %d\n", val); + return -EINVAL; + } + + rate.rx_rsp_rate_11b_short = val; + skw_dbg("11b short rate: %d\n", rate.rx_rsp_rate_11b_short); + + p = q; + q = strchr(p + 1, ','); + if (!p) { + skw_err("ofdm not found\n"); + return -ENOTSUPP; + } + + val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + if (!skw_is_value_in_enum(val)) { + skw_warn("invalid ofdm rate %d\n", val); + return -EINVAL; + } + + rate.rx_rsp_rate_ofdm = val; + skw_dbg("ofdm: %d\n", rate.rx_rsp_rate_ofdm); + +SETPARA: + ret = skw_set_force_rx_rsp_rate(iface->wdev.wiphy, iface->ndev, &rate); + if (!ret) + sprintf(resp, "set force rx rsp rate ok "); + else + sprintf(resp, "set force rx rsp rate failed"); + + return ret; + +SETFAIL: + sprintf(resp, "set force rts rate failed"); + + return -EINVAL; +} + +static int skw_iwpriv_set_scan_time(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_set_scan_time time = {0}; + u8 parmas_len = 0; + u16 val = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args: %s\n", args); + + /* parse rts rate flag */ + parmas_len = strlen(args); + + p = args; + q = strchr(args, ','); + if (!q) { + skw_err("parameter error\n"); + return -ENOTSUPP; + } + + skw_dbg("params found %s\n", p - 1); + val = skw_iwpriv_convert_string_to_u(p, q - p); + if (val > 0xFF) { + skw_warn("active dwell limit to 0xFF\n"); + time.active_dwell_time = 0xFF; + } else { + time.active_dwell_time = val; + } + + skw_dbg("active dwell time: %d\n", time.active_dwell_time); + + p = q + strlen(","); + if (!p) { + skw_warn("bypass active scan auto time flag not found\n"); + return -ENOTSUPP; + } + + val = simple_strtol(p, NULL, 10); + if (val != 0 && val != 1) { + skw_err("flag error %d\n", val); + return -EINVAL; + } + + time.bypass_active_acan_auto_time = val; + + skw_dbg("bypass active scan auto time: %d\n", time.bypass_active_acan_auto_time); + + ret = skw_set_scan_time_cmd(iface->wdev.wiphy, iface->ndev, &time); + if (!ret) + sprintf(resp, "set scan time ok "); + else + sprintf(resp, "set scan time failed"); + + return ret; +} + +static int skw_iwpriv_set_tcpd_wakeup_host(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + struct skw_set_tcpd_wakeup_host set_flag = {0}; + u8 parmas_len = 0; + + if (!args) + return -EINVAL; + + skw_dbg("set_tcpd_wakeup_host args %s\n", args); + + parmas_len = strlen(args); + if (parmas_len == 1) { + ret = simple_strtol(args, NULL, 10); + if (!ret) { + skw_dbg("flag: %d\n", ret); + set_flag.enable = 0; + } else if (ret == 1) { + skw_dbg("flag: %d\n", ret); + set_flag.enable = 1; + } else { + goto SETFAIL; + } + } else if (parmas_len > 1) { + skw_err("params error\n"); + goto SETFAIL; + } + + skw_dbg("wakeup host: %d\n", set_flag.enable); + + ret = skw_set_tcp_disconn_wakeup_host(iface->wdev.wiphy, iface->ndev, &set_flag); + if (!ret) + sprintf(resp, "set tcp disc wakeup host ok "); + else + sprintf(resp, "set tcp disc wakeup host failed"); + + return ret; + +SETFAIL: + sprintf(resp, "set tcp disc wakeup host failed(param error)"); + + return ret; +} + +static int skw_iwpriv_set_rc_min_rate(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + struct skw_set_rate_control_min_rate rate = {0}; + u8 parmas_len = 0, val = 0; + char *p = NULL; + + if (!args) + return -EINVAL; + + skw_dbg("set_rc_min_rate args %s\n", args); + + parmas_len = strlen(args); + if (!parmas_len) + return -EINVAL; + + p = args; + val = (u8)skw_iwpriv_convert_string_to_u(p, parmas_len); + if (!skw_is_value_in_enum(val)) { + skw_dbg("val not valid %d\n", val); + goto SETFAIL; + } + + rate.rstrict_min_rate = val; + skw_dbg("rc min rate: %d\n", rate.rstrict_min_rate); + + ret = skw_set_rc_min_rate(iface->wdev.wiphy, iface->ndev, &rate); + if (!ret) + sprintf(resp, "set rc min rate ok"); + else + sprintf(resp, "set rc min rate failed"); + + return ret; + +SETFAIL: + sprintf(resp, "set rc min rate failed (value not support)"); + + return ret; +} + +static int skw_iwpriv_set_rc_rate_change(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_set_rate_control_rate_change rate = {0}; + + if (!args) + return -EINVAL; + + skw_dbg("set_rc_rate_change args %s\n", args); + + //1/5 up rate class num + p = args; + q = strchr(args, ','); + if (!p || !q) { + skw_err("up rate class num not found\n"); + return -ENOTSUPP; + } + + rate.up_rate_class_num = (u8)skw_iwpriv_convert_string_to_u(p, q - p); + + skw_dbg("up_rate_class_num: %d\n", rate.up_rate_class_num); + + //2/5 down rate class num + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("down rate class num not found\n"); + return -ENOTSUPP; + } + + rate.down_rate_class_num = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + + skw_dbg("down_rate_class_num: %d\n", rate.down_rate_class_num); + + //3/5 hw rty limit + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("hw rty limit not found\n"); + return -ENOTSUPP; + } + + rate.hw_rty_limit = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + skw_dbg("hw_rty_limit: %d\n", rate.hw_rty_limit); + + //4/5 per rate hw rty limit + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("per rate hw rty limit not found\n"); + return -ENOTSUPP; + } + + rate.per_rate_hw_rty_limit = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + + skw_dbg("per rate hw_rty_limit: %d\n", rate.per_rate_hw_rty_limit); + + //5/5 per rate probe hw rty limit + p = q; + q = strchr(p + 1, ','); + if (!p) { + skw_err("per rate probe hw rty limit not found\n"); + return -ENOTSUPP; + } + + rate.per_rate_probe_hw_rty_limit = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + + skw_dbg("per rate probe hw_rty_limit: %d\n", rate.per_rate_probe_hw_rty_limit); + + ret = skw_set_rate_control_rate_change(iface->wdev.wiphy, iface->ndev, &rate); + if (!ret) + sprintf(resp, "set force rx rsp rate ok "); + else + sprintf(resp, "set force rx rsp rate failed"); + + return ret; +} + +static int skw_iwpriv_set_rc_spe_rate(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + struct skw_set_rate_control_special_rate rate = {0}; + u8 parmas_len = 0, val = 0; + char *p = NULL; + + if (!args) + return -EINVAL; + + parmas_len = strlen(args); + if (!parmas_len) + return -EINVAL; + + p = args; + val = (u8)skw_iwpriv_convert_string_to_u(p, parmas_len); + if (!skw_is_value_in_enum(val)) { + skw_dbg("val not valid %d\n", val); + goto SETFAIL; + } + + rate.special_frm_rate = val; + skw_dbg("rc special rate: %d\n", rate.special_frm_rate); + + ret = skw_set_rc_spe_rate(iface->wdev.wiphy, iface->ndev, &rate); + if (!ret) + sprintf(resp, "set rc special rate ok "); + else + sprintf(resp, "set rc special rate failed"); + + return ret; + +SETFAIL: + sprintf(resp, "set rc spe rate failed (value not support)"); + + return ret; +} + +static int skw_generic_tlv_operation(struct wiphy *wiphy, struct net_device *dev, + void *param, size_t param_len, enum SKW_MIB_ID tlv_type) +{ + int ret = 0; + u16 *plen; + struct skw_tlv_conf conf; + struct skw_tlv_get_assign_addr_rsp resp = {0}; + + ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); + if (ret) { + skw_err("alloc tlv failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve tlv failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, tlv_type, param, param_len)) { + skw_err("add tlv failed\n"); + skw_tlv_free(&conf); + return -EINVAL; + } + + if (conf.total_len) { + *plen = conf.total_len; + if (tlv_type == SKW_MIB_GET_ASSIGN_ADDR_VAL_E) { + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, &resp, sizeof(resp)); + + if (ret) { + skw_err("failed, ret: %d\n", ret); + goto SETFAIL; + } + skw_dbg("read done val: 0x%08x\n", resp.val); + } else { + ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) { + skw_err("failed, ret: %d\n", ret); + goto SETFAIL; + } + } + } +SETFAIL: + skw_tlv_free(&conf); + return ret; +} + +static int skw_set_tx_lifetime(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_tx_lifetime *lifetime) +{ + return skw_generic_tlv_operation(wiphy, dev, lifetime, + sizeof(struct skw_tlv_set_tx_lifetime), + SKW_MIB_SET_TX_LIFETIME); +} + +static int skw_iwpriv_set_tx_lifetime(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + struct skw_tlv_set_tx_lifetime lftm = {0}; + u8 parmas_len = 0; + char *p = NULL; + + if (!args) + return -EINVAL; + + parmas_len = strlen(args); + if (!parmas_len) + return -EINVAL; + + p = args; + lftm.lifetime = skw_iwpriv_convert_string_to_u(p, parmas_len); + + skw_dbg("set tx lifetime: %d\n", lftm.lifetime); + + ret = skw_set_tx_lifetime(iface->wdev.wiphy, iface->ndev, &lftm); + if (!ret) + sprintf(resp, "set ok "); + else + sprintf(resp, "set failed"); + + return ret; +} + +static int skw_set_tx_retry_cnt(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_retry_cnt *rtycnt) +{ + return skw_generic_tlv_operation(wiphy, dev, rtycnt, + sizeof(struct skw_tlv_set_retry_cnt), + SKW_MIB_SET_TX_RETRY_CNT); +} + +static int skw_iwpriv_set_tx_retry_cnt(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + struct skw_tlv_set_retry_cnt cnt = {0}; + u8 parmas_len = 0; + char *p = NULL; + + if (!args) + return -EINVAL; + + parmas_len = strlen(args); + if (!parmas_len) + return -EINVAL; + + p = args; + cnt.rtycnt = (u8)skw_iwpriv_convert_string_to_u(p, parmas_len); + + skw_dbg("set tx retry cnt: %d\n", cnt.rtycnt); + + ret = skw_set_tx_retry_cnt(iface->wdev.wiphy, iface->ndev, &cnt); + if (!ret) + sprintf(resp, "set ok "); + else + sprintf(resp, "set failed"); + + return ret; +} + +static int skw_set_tx_rts_thrd(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_tx_rts_thrd *thrd) +{ + return skw_generic_tlv_operation(wiphy, dev, thrd, + sizeof(struct skw_tlv_set_tx_rts_thrd), + SKW_MIB_SET_TX_RTS_THRD); +} + +static int skw_iwpriv_set_tx_rts_thrd(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + struct skw_tlv_set_tx_rts_thrd thrd = {0}; + u8 parmas_len = 0; + char *p = NULL; + + if (!args) + return -EINVAL; + + parmas_len = strlen(args); + if (!parmas_len) + return -EINVAL; + + p = args; + thrd.rts_thrd = skw_iwpriv_convert_string_to_u(p, parmas_len); + + skw_dbg("set tx rts thrd: %d\n", thrd.rts_thrd); + + ret = skw_set_tx_rts_thrd(iface->wdev.wiphy, iface->ndev, &thrd); + if (!ret) + sprintf(resp, "set ok "); + else + sprintf(resp, "set failed"); + + return ret; +} + +static int skw_set_rx_sepcial_80211_frame(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_rx_special_80211_frame *frm) +{ + return skw_generic_tlv_operation(wiphy, dev, frm, + sizeof(struct skw_tlv_set_rx_special_80211_frame), + SKW_MIB_SET_FORCE_RX_SPECIAL_80211FRAME); +} + +static int skw_iwpriv_set_rx_sepcial_80211_frame(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_tlv_set_rx_special_80211_frame frm = {0}; + u8 parmas_len = 0; + + if (!args) + return -EINVAL; + + skw_dbg("set_rx_sepcial_80211_frame args %s\n", args); + + parmas_len = strlen(args); + if (parmas_len == 1) { + ret = simple_strtol(args, NULL, 10); + if (!ret) { + skw_warn("disable rx\n"); + frm.en = 0; + goto SETPARA; + } else if (ret == 1) { + skw_warn("need more params\n"); + goto SETFAIL; + } else { + goto SETFAIL; + } + } else if (parmas_len > 1) { + p = args; + q = strchr(args, ','); + if (!p || !q) { + skw_err("flag not found\n"); + return -ENOTSUPP; + } + + frm.en = (u8)skw_iwpriv_convert_string_to_u(p, q - p); + if (frm.en != 0 && frm.en != 1) { + skw_err("flag error %d\n", frm.en); + return -EINVAL; + } + + if (frm.en == 0) { + skw_warn("disable rx\n"); + frm.en = 0; + goto SETPARA; + } + + skw_err("start set type and sub type params ...\n"); + } + + p = q; + q = strchr(p + 1, ','); + if (!p || !q) { + skw_err("param error\n"); + return -ENOTSUPP; + } + + frm.type = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + + skw_dbg("type: %d\n", frm.type); + + p = q; + q = strchr(p + 1, ','); + if (!p) { + skw_err("sub type not found\n"); + return -ENOTSUPP; + } + + frm.sub_type = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + + skw_dbg("sub type: %d\n", frm.sub_type); + +SETPARA: + ret = skw_set_rx_sepcial_80211_frame(iface->wdev.wiphy, iface->ndev, &frm); + if (!ret) + sprintf(resp, "set ok "); + else + sprintf(resp, "set failed"); + + return ret; + +SETFAIL: + + sprintf(resp, "set failed"); + + return -EINVAL; +} + +static int skw_set_rx_update_nav(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_rx_update_nav *nav) +{ + return skw_generic_tlv_operation(wiphy, dev, nav, + sizeof(struct skw_tlv_set_rx_update_nav), + SKW_MIB_SET_FORCE_RX_UPDATE_NAV); +} + +static int skw_iwpriv_set_rx_update_nav(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_tlv_set_rx_update_nav nav = {0}; + int temp_val = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args %s\n", args); + + p = args; + q = strchr(args, ','); + if(!p || !q) + { + skw_err("parameter error\n"); + return -ENOTSUPP; + } else { + skw_dbg("params found %s\n", p); + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0 || temp_val < -255) { + skw_warn("parameter error %d\n", temp_val); + return -EINVAL; + } + nav.intra_rssi = 0x80 | (0 - temp_val); + } + skw_dbg("intra rssi: %d\n", nav.intra_rssi); + + p = q; + q = strchr(p + 1, ','); + if(!p || !p) + { + skw_err("parameter error\n"); + return -ENOTSUPP; + } else { + skw_dbg("params found %s\n", p + 1); + temp_val = simple_strtol(p + 1, NULL, 10); + if (temp_val >= 0 || temp_val < -255) { + skw_warn("parameter error %d\n", temp_val); + return -EINVAL; + } + nav.basic_rssi = 0x80 | (0 - temp_val); + } + skw_dbg("basic rssi: %d\n", nav.basic_rssi); + + p = q; + p = q + strlen(","); + if(!p) + { + skw_warn("nav max time not found\n"); + return -ENOTSUPP; + } else { + nav.nav_max_time = simple_strtol(p, NULL, 10); + } + skw_dbg("nav max time: %d\n", nav.nav_max_time); + + ret = skw_set_rx_update_nav(iface->wdev.wiphy, iface->ndev, &nav); + + if (!ret ) { + sprintf(resp, "set ok "); + } else + sprintf(resp, "set failed"); + + return ret; +} + +//TLV 70 +static int skw_set_apgo_timap(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_apgo_timap *timap) +{ + return skw_generic_tlv_operation(wiphy, dev, timap, + sizeof(struct skw_tlv_set_apgo_timap), + SKW_MIB_SET_APGO_TIMMAP); +} + +static int skw_iwpriv_set_apgo_timap(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_tlv_set_apgo_timap timap = {0}; + + if (!args) + return -EINVAL; + + skw_dbg("set_apgo_timap args %s\n", args); + + p = args; + q = strchr(args, ','); + if (!p || !q) { + skw_err("parameter error\n"); + return -ENOTSUPP; + } + + timap.dtimforce0 = (u8)skw_iwpriv_convert_string_to_u(p, q - p); + skw_dbg("dtim force0: %d\n", timap.dtimforce0); + + p = q; + q = strchr(p + 1, ','); + if (!p || !p) { + skw_err("parameter error\n"); + return -ENOTSUPP; + } + + timap.dtimforce1 = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + + skw_dbg("dtim force1: %d\n", timap.dtimforce1); + + p = q; + q = strchr(p + 1, ','); + if (!p || !p) { + skw_err("parameter error\n"); + return -ENOTSUPP; + } + + timap.timforce0 = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + skw_dbg("tim force0: %d\n", timap.timforce0); + + p = q; + q = strchr(p + 1, ','); + if (!p) { + skw_err("parameter error\n"); + return -ENOTSUPP; + } + + timap.timforce1 = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1); + skw_dbg("tim force1: %d\n", timap.timforce1); + + ret = skw_set_apgo_timap(iface->wdev.wiphy, iface->ndev, &timap); + if (!ret) + sprintf(resp, "set ok "); + else + sprintf(resp, "set failed"); + + return ret; +} + +//TLV 71 +static int skw_set_dbdc_disable(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_dbdc_disable *flag) +{ + return skw_generic_tlv_operation(wiphy, dev, flag, + sizeof(struct skw_tlv_set_dbdc_disable), + SKW_MIB_SET_DBDC_DISABLE); +} + +static int skw_iwpriv_set_dbdc_disable(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + struct skw_tlv_set_dbdc_disable set_flag = {0}; + u8 parmas_len = 0; + + if (!args) + return -EINVAL; + + parmas_len = strlen(args); + if (parmas_len == 1) { + ret = simple_strtol(args, NULL, 10); + if (!ret) { + skw_dbg("flag: %d\n", ret); + set_flag.disable = 0; + } else if (ret == 1) { + skw_dbg("flag: %d\n", ret); + set_flag.disable = 1; + } else { + goto SETFAIL; + } + } else if (parmas_len > 1) { + skw_err("params error\n"); + goto SETFAIL; + } + + skw_dbg("dbdc disable: %d\n", set_flag.disable); + + ret = skw_set_dbdc_disable(iface->wdev.wiphy, iface->ndev, &set_flag); + if (!ret) + sprintf(resp, "set ok "); + else + sprintf(resp, "set failed"); + + return ret; + +SETFAIL: + sprintf(resp, "set failed(param error)"); + + return ret; +} + +//TLV 150 +static int skw_set_assign_address_val(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_assign_addr_val *param) +{ + return skw_generic_tlv_operation(wiphy, dev, param, + sizeof(struct skw_tlv_set_assign_addr_val), + SKW_MIB_SET_ASSIGN_ADDRESS_VAL); +} + +static int skw_iwpriv_set_assign_address_val(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_tlv_set_assign_addr_val params = {0}; + u8 parmas_len = 0, remain_len = 0; + + if (!args) + return -EINVAL; + + parmas_len = strlen(args); + skw_warn("args %s\n", args); + + p = args; + q = strchr(args, ','); + if (!p || !q) { + skw_err("params error\n"); + return -ENOTSUPP; + } + + params.addr = skw_iwpriv_convert_string_to_u32(p, q - p); + skw_dbg("addr: 0x%08x\n", params.addr); + + p = q; + remain_len = parmas_len - (q - p) - 1; + if (!p) { + skw_err("param error\n"); + return -ENOTSUPP; + } + + params.val = skw_iwpriv_convert_string_to_u32(p + 1, remain_len); + skw_dbg("val: 0x%08x\n", params.val); + + ret = skw_set_assign_address_val(iface->wdev.wiphy, iface->ndev, ¶ms); + if (!ret) + sprintf(resp, "set ok "); + else + sprintf(resp, "set failed"); + + return ret; +} + +//TLV 250 +static int skw_read_addr(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_get_assign_addr *param) +{ + return skw_generic_tlv_operation(wiphy, dev, param, + sizeof(struct skw_tlv_get_assign_addr), + SKW_MIB_GET_ASSIGN_ADDR_VAL_E); +} + +static int skw_iwpriv_read_addr(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL; + struct skw_tlv_get_assign_addr params = {0}; + u8 parmas_len = 0; + + if (!args) + return -EINVAL; + + parmas_len = strlen(args); + if (!parmas_len || parmas_len > 10) { + skw_err("params error\n"); + return -ENOTSUPP; + } + + skw_dbg("args %s\n", args); + + p = args; + if (!p) { + skw_err("params error\n"); + return -ENOTSUPP; + } + + params.addr = skw_iwpriv_convert_string_to_u32(p, parmas_len); + skw_dbg("addr: 0x%08x\n", params.addr); + + ret = skw_read_addr(iface->wdev.wiphy, iface->ndev, ¶ms); + if (!ret) + sprintf(resp, "set ok "); + else + sprintf(resp, "set failed"); + + return ret; +} + +//TLV 39 ageout thrd +// +static int skw_set_ageout_thrd(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_ageout_thrd *params) +{ + return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_ageout_thrd), SKW_MIB_SET_AGEOUT_THOLD); +} + +static int skw_iwpriv_set_ageout_thrd(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + struct skw_tlv_set_ageout_thrd thrd = {0}; + u16 temp_val = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args %s\n", args); + + p = args; + q = strchr(args, ','); + if (!p || !q) { + skw_err("param err\n"); + return -ENOTSUPP; + } + + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0xFF) { + skw_warn("parameter out of range (0 ~ 255) %d\n", temp_val); + thrd.ageout_kick_thrd = 0xFF; + } else { + thrd.ageout_kick_thrd = temp_val; + } + + skw_dbg("tbtt cnt, kick out %d\n", thrd.ageout_kick_thrd); + + p = q; + p = q + strlen(","); + if (!p) { + skw_warn("param err\n"); + return -ENOTSUPP; + } + + temp_val = simple_strtol(p, NULL, 10); + if (temp_val >= 0xFF) { + skw_warn("parameter out of range (0 ~ 255) %d\n", temp_val); + thrd.ageout_keep_alive_thrd = 0xFF; + } else { + thrd.ageout_keep_alive_thrd = temp_val; + } + + skw_dbg("keep alive send null thrd: %d\n", thrd.ageout_keep_alive_thrd); + + ret = skw_set_ageout_thrd(iface->wdev.wiphy, iface->ndev, &thrd); + + if (!ret) + sprintf(resp, "set ok"); + else + sprintf(resp, "set failed"); + + return ret; + +} + +//tlv 42 +static int skw_set_reported_cqm_interval(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_report_cqm_rssi_low_itvl *params) +{ + return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_report_cqm_rssi_low_itvl), SKW_MIB_SET_REPORT_CQM_RSSI_LOW_INT); +} + +//tlv 54 ~ 57 +static int skw_set_ap_new_channel(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_ap_new_channel *params) +{ + return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_ap_new_channel), SKW_MIB_SET_AP_NEW_CHAN); +} + +static int skw_set_tx_retry_limit_en(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_tx_retry_limit_en *params) +{ + return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_tx_retry_limit_en), SKW_MIB_SET_TX_RETRY_LIMIT_EN); +} + +static int skw_partial_twt_sched(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_partial_twt_sched *params) +{ + return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_partial_twt_sched), SKW_MIB_SET_PARTIAL_TWT_SCHED); +} + +static int skw_set_thm_thre(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_thm_thrd *params) +{ + return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_thm_thrd), SKW_MIB_SET_THM_THRD); +} + +static int skw_set_normal_scan_with_acs(struct wiphy *wiphy, struct net_device *dev, + struct skw_tlv_set_normal_scan_with_acs *params) +{ + return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_normal_scan_with_acs), + SKW_MIB_SET_NORMAL_SCAN_WITH_ACS); +} + +extern int skw_set_dot11r_mib(struct wiphy *wiphy, struct net_device *dev, bool enable); + +static int skw_set_scan_send_probe_req_cnt(struct wiphy *wiphy, struct net_device *dev, + u8 cnt) +{ + return skw_generic_tlv_operation(wiphy, dev, &cnt, 1, + SKW_MIB_SET_SCAN_SEND_PROBE_REQ_CNT); +} + +static int skw_iwpriv_set_params(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len) +{ + int ret = 0; + char *p = NULL, *q = NULL; + u32 params[16] = {0}; + u32 params_num = 0; + struct skw_tlv_set_ap_new_channel new_chan = {0}; + struct skw_tlv_set_tx_retry_limit_en txr_limit = {0}; + struct skw_tlv_set_report_cqm_rssi_low_itvl itvl = {0}; + struct skw_tlv_set_thm_thrd thrd = {0}; + struct skw_tlv_set_partial_twt_sched twt_params = {0}; + struct skw_tlv_set_normal_scan_with_acs normal_scan_params = {0}; + u8 send_probe_req_cnt = 0; + + if (!args) + return -EINVAL; + + skw_dbg("args %s\n", args); + + p = args; + + while (p) { + params[params_num] = simple_strtol(p, NULL, 10); + params_num++; + + q = strchr(p, ','); + if (!q) { + break; + } + + p = q + 1; /* move past the comma */ + } + + if (params_num < 2) { + skw_warn("params error, at least 2\n"); + return -ENOTSUPP; + } + + switch (params[0]) { + case 1: + if (params_num != 6) { + skw_warn("params error, need 6 totally\n"); + return -EINVAL; + } + + new_chan.chan = params[1]; + new_chan.center_chan = params[2]; + new_chan.center_two_chan = params[3]; + new_chan.bw = params[4]; + new_chan.band = params[5]; + + ret = skw_set_ap_new_channel(iface->wdev.wiphy, iface->ndev, &new_chan); + skw_dbg("set ap new chan done\n"); + + break; + + case 2: + if (params_num != 2) { + skw_warn("params error, need 2 totally\n"); + return -EINVAL; + } + + ret = skw_set_dot11r_mib(iface->wdev.wiphy, iface->ndev, (bool)params[1]); + skw_dbg("set 11r mib done\n"); + break; + + case 3: + if (params_num != 4) { + skw_warn("params error, need 4 totally\n"); + return -EINVAL; + } + + txr_limit.short_retry_check_en = !!params[1]; + txr_limit.long_retry_check_en = !!params[2]; + txr_limit.ampdu_retry_check_en = !!params[3]; + + ret = skw_set_tx_retry_limit_en(iface->wdev.wiphy, iface->ndev,&txr_limit); + skw_dbg("set tx retry limit done\n"); + + break; + + case 4: + if (params_num != 3) { + skw_warn("params error, need 3 totally\n"); + return -EINVAL; + } + + itvl.report_cqm_low_intvl_min_dur = params[1]; + itvl.report_cqm_low_intvl_max_dur = params[2]; + + ret = skw_set_reported_cqm_interval(iface->wdev.wiphy, iface->ndev,&itvl); + skw_dbg("set report cqm interval done\n"); + break; + + case 5: + if (params_num != 3) { + skw_warn("params error, need 3 totally\n"); + return -EINVAL; + } + + thrd.thm_high_thrd_tx_suspend = (s16)params[1]; + thrd.thm_low_thrd_tx_resume = (s16)params[2]; + + ret = skw_set_thm_thre(iface->wdev.wiphy, iface->ndev, &thrd); + skw_dbg("set thm thre done\n"); + + break; + + case 6: + if (params_num != 7) { + skw_warn("params error, need 7 totally\n"); + return -EINVAL; + } + + twt_params.en = params[1]; + twt_params.start_time_l = params[2]; + twt_params.start_time_h = params[3]; + twt_params.interval = params[4]; + twt_params.duration = params[5]; + twt_params.duration_unit = params[6]; + twt_params.sub_type = params[7]; + + ret = skw_partial_twt_sched(iface->wdev.wiphy, iface->ndev, &twt_params); + skw_dbg("set partial twt sched done\n"); + + break; + case 59: + if (params_num != 2) { + skw_warn("params error, need 2 totally\n"); + return -EINVAL; + } + + normal_scan_params.en = params[1]; + ret = skw_set_normal_scan_with_acs(iface->wdev.wiphy, iface->ndev, &normal_scan_params); + skw_dbg("set normal scan with acs %d done\n", normal_scan_params.en); + + break; + case 62: + if (params_num != 2) { + skw_warn("params error, need 2 totally \n"); + return -EINVAL; + } + send_probe_req_cnt = params[1]; + ret = skw_set_scan_send_probe_req_cnt(iface->wdev.wiphy, iface->ndev, send_probe_req_cnt); + skw_dbg("set scan send probe req cnt %d done\n", send_probe_req_cnt); + break; + + default: + return -ENOTSUPP; + } + + if (!ret) + snprintf(resp, resp_len, "set ok"); + else + snprintf(resp, resp_len, "set failed"); + + return ret; +} + +static struct skw_iwpriv_cmd skw_iwpriv_set_cmds[] = { + /* keep first */ + {"help", skw_iwpriv_help, "usage"}, + {"bandcfg", skw_iwpriv_set_bandcfg, "bandcfg=0/1/2"}, + {"mppdudur", skw_iwpriv_set_max_ppdu_dur, "mppdudur=0~5,1~65535"}, + {"edca", skw_iwpriv_set_edca_params, "edca=0,x(13 total)"}, + {"ccanowifi", skw_iwpriv_set_cca_thre_nowifi, "ccanowifi=-u8"}, + {"cca11b", skw_iwpriv_set_cca_thre_11b, "cca11b=-u8"}, + {"ccaofdm", skw_iwpriv_set_cca_thre_ofdm, "ccaofdm=-u8"}, + {"rtsrate", skw_iwpriv_set_force_rts_rate, "rtsrate=u8,u8,u8"}, + {"rxrsprate", skw_iwpriv_set_force_rx_rsp_rate, "rxrsprate=u8,u8,u8,u8"}, + {"scantime", skw_iwpriv_set_scan_time, "scantime=u8,u8"}, + {"tcpdwhost", skw_iwpriv_set_tcpd_wakeup_host, "tcpdwhost=u8"}, + {"rcminrate", skw_iwpriv_set_rc_min_rate, "rcminrate=u8"}, + {"rcratechg", skw_iwpriv_set_rc_rate_change, "rcratechg=u8,u8,u8,u8,u8"}, + {"rcsperate", skw_iwpriv_set_rc_spe_rate, "rcsperate=u8"}, + {"txlftm", skw_iwpriv_set_tx_lifetime, "txlftm=u8"}, + {"txrtycnt", skw_iwpriv_set_tx_retry_cnt, "txrtycnt=u8"}, + {"txrtsthrd", skw_iwpriv_set_tx_rts_thrd, "txrtsthrd=u8"}, + {"rxsped11frm", skw_iwpriv_set_rx_sepcial_80211_frame, "rxsped11frm=u8,u8,u8"}, + {"rxupdnav", skw_iwpriv_set_rx_update_nav, "rxupdnav=-u8,-u8,u8"}, + {"apgotimap", skw_iwpriv_set_apgo_timap, "apgotimap=u8,u8,u8,u8"}, + {"dbdcdis", skw_iwpriv_set_dbdc_disable, "dbdcdis=u8"}, + {"addrval", skw_iwpriv_set_assign_address_val, "addrval=0x12345678,0x12345678"}, + {"rdaddr", skw_iwpriv_read_addr, "rdaddr=0x12345678"}, + {"ageout", skw_iwpriv_set_ageout_thrd, "ageout=u8,u8"}, + {"params", skw_iwpriv_set_params, "params=index,val1,val2,val3,..."}, //for tlv42 and 54~57,59 + + /*keep last*/ + {NULL, NULL, NULL} +}; + +static struct skw_iwpriv_cmd skw_iwpriv_get_cmds[] = { + /* keep first */ + {"help", skw_iwpriv_help, "usage"}, + + {"bandcfg", skw_iwpriv_get_bandcfg, "bandcfg"}, + + /*keep last*/ + {NULL, NULL, NULL} +}; + +static struct skw_iwpriv_cmd *skw_iwpriv_cmd_match(struct skw_iwpriv_cmd *cmds, + const char *key, int key_len) +{ + int i; + + for (i = 0; cmds[i].name; i++) { + if (!memcmp(cmds[i].name, key, key_len)) + return &cmds[i]; + } + + return NULL; +} + +static int skw_iwpriv_set(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + int key_len; + char param[128] = {0}; + char *token = NULL, *args = NULL; + struct skw_iwpriv_cmd *iwpriv_cmd; + struct skw_iface *iface = (struct skw_iface *)netdev_priv(dev); + + WARN_ON(sizeof(param) < wrqu->data.length); + + if (copy_from_user(param, wrqu->data.pointer, sizeof(param))) { + skw_err("copy failed, length: %d\n", + wrqu->data.length); + + return -EFAULT; + } + param[127] = '\0'; + + token = strchr(param, '='); + if (!token) { + key_len = strlen(param); + args = NULL; + } else { + key_len = token - param; + args = token + 1; + } + + iwpriv_cmd = skw_iwpriv_cmd_match(skw_iwpriv_set_cmds, param, key_len); + if (iwpriv_cmd) + ret = iwpriv_cmd->handler(iface, iwpriv_cmd, args, + extra, SKW_GET_LEN_512); + else + ret = skw_iwpriv_help(iface, skw_iwpriv_set_cmds, NULL, + extra, SKW_GET_LEN_512); + + if (ret < 0) + sprintf(extra, " usage: %s\n", iwpriv_cmd->help_info); + + wrqu->data.length = SKW_GET_LEN_1024; + + skw_dbg("resp: %s\n", extra); + + return 0; +} + +static int skw_iwpriv_get(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret; + char cmd[128] = {0}; + struct skw_iwpriv_cmd *priv_cmd = NULL; + struct skw_iface *iface = (struct skw_iface *)netdev_priv(dev); + + if (copy_from_user(cmd, wrqu->data.pointer, sizeof(cmd))) { + skw_err("copy failed, length: %d\n", + wrqu->data.length); + + return -EFAULT; + } + + skw_dbg("cmd: 0x%x, %s(len: %d)\n", info->cmd, cmd, wrqu->data.length); + + cmd[127] = '\0'; + priv_cmd = skw_iwpriv_cmd_match(skw_iwpriv_get_cmds, cmd, strlen(cmd)); + if (priv_cmd) + ret = priv_cmd->handler(iface, priv_cmd, NULL, extra, + SKW_GET_LEN_512); + else + ret = skw_iwpriv_help(iface, skw_iwpriv_get_cmds, NULL, + extra, SKW_GET_LEN_512); + + wrqu->data.length = SKW_GET_LEN_512; + + skw_dbg("resp: %s\n", extra); + + return ret; +} + +static int skw_iwpriv_at(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret; + char cmd[SKW_SET_LEN_256]; + int len = wrqu->data.length; + struct skw_core *skw = ((struct skw_iface *)netdev_priv(dev))->skw; + + BUG_ON(sizeof(cmd) < len); + + if (copy_from_user(cmd, wrqu->data.pointer, sizeof(cmd))) { + skw_err("copy failed, length: %d\n", len); + + return -EFAULT; + } + + skw_dbg("cmd: %s, len: %d\n", cmd, len); + + if (len + 2 > sizeof(cmd)) + return -EINVAL; + + cmd[len - 1] = 0xd; + cmd[len + 0] = 0xa; + cmd[len + 1] = 0x0; + + ret = skw_send_at_cmd(skw, cmd, len + 2, extra, SKW_GET_LEN_512); + + wrqu->data.length = SKW_GET_LEN_512; + + skw_dbg("resp: %s", extra); + + return ret; +} + +static struct iw_priv_args skw_iw_priv_args[] = { + { + SKW_IW_PRIV_SET, + IW_PRIV_TYPE_CHAR | SKW_SET_LEN_128, + IW_PRIV_TYPE_CHAR | SKW_GET_LEN_1024, + "set", + }, + { + SKW_IW_PRIV_GET, + IW_PRIV_TYPE_CHAR | SKW_SET_LEN_128, + IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512, + "get", + }, + { + SKW_IW_PRIV_AT, + IW_PRIV_TYPE_CHAR | SKW_SET_LEN_256, + IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512, + "at", + }, + { + SKW_IW_PRIV_80211MODE, + IW_PRIV_TYPE_CHAR | SKW_SET_LEN_128, + IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512, + "mode", + }, + { + SKW_IW_PRIV_GET_80211MODE, + IW_PRIV_TYPE_CHAR | SKW_SET_LEN_128, + IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512, + "get_mode", + }, + { + SKW_IW_PRIV_KEEP_ALIVE, + IW_PRIV_TYPE_CHAR | SKW_SET_LEN_1024, + IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512, + "keep_alive", + }, + { + SKW_IW_PRIV_WOW_FILTER, + IW_PRIV_TYPE_CHAR | SKW_SET_LEN_1024, + IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512, + "wow_filter", + }, + {0, 0, 0, {0} } +}; + +static const iw_handler skw_iw_priv_handlers[] = { + NULL, + skw_iwpriv_set, + NULL, + skw_iwpriv_get, + NULL, + skw_iwpriv_at, + skw_iwpriv_mode, + skw_iwpriv_get_mode, + skw_iwpriv_keep_alive, + skw_iwpriv_wow_filter, + //skw_iwpriv_get_wow_filter, +}; +#endif + +static const struct iw_handler_def skw_iw_ops = { + .standard = skw_iw_standard_handlers, + .num_standard = ARRAY_SIZE(skw_iw_standard_handlers), +#ifdef CONFIG_WEXT_PRIV + .private = skw_iw_priv_handlers, + .num_private = ARRAY_SIZE(skw_iw_priv_handlers), + .private_args = skw_iw_priv_args, + .num_private_args = ARRAY_SIZE(skw_iw_priv_args), +#endif + .get_wireless_stats = skw_get_wireless_stats, +}; + +const void *skw_iw_handlers(void) +{ +#ifdef CONFIG_WIRELESS_EXT + return &skw_iw_ops; +#else + skw_info("CONFIG_WIRELESS_EXT not enabled\n"); + return NULL; +#endif +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iw.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iw.h new file mode 100755 index 0000000..87e1641 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_iw.h @@ -0,0 +1,345 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_IW_H__ +#define __SKW_IW_H__ + +#define SKW_MAX_TLV_BUFF_LEN 1024 + +#define SKW_KEEPACTIVE_RULE_SEND_CNT_DEF 1 +#define SKW_KEEPACTIVE_RULE_SEND_CNT_MAX 10 +#define SKW_KEEPACTIVE_RULE_MAX 18 +#define SKW_KEEPACTIVE_LENGTH_MAX 135 +#define SKW_KEEPACTIVE_CMD_BUF_MAX 1300 +#define SKW_KEEPALIVE_ALWAYS_FLAG BIT(0) +#define SKW_KEEPALIVE_NEEDCHKSUM_FLAG BIT(1) +#define SKW_KEEPALIVE_FWCHKSUM_FLAG BIT(2) + +struct skw_keep_active_rule_data { + u16 is_chksumed; + u8 payload[0]; +} __packed; +struct skw_keep_active_rule { + u32 keep_interval; + u8 send_cnt:7; + u8 always:1; + u8 payload_len; + struct skw_keep_active_rule_data data[0]; +} __packed; + +struct skw_keep_active_setup { + u32 en_bitmap; + u32 flags[SKW_KEEPACTIVE_RULE_MAX]; + struct skw_keep_active_rule *rule[SKW_KEEPACTIVE_RULE_MAX]; +} __packed; + +struct skw_keep_active_param { + u8 rule_num; + struct skw_keep_active_rule rules[0]; +} __packed; + +typedef int (*skw_at_handler)(struct skw_core *skw, void *param, + char *args, char *resp, int resp_len); + +struct skw_at_cmd { + char *name; + skw_at_handler handler; + char *help_info; +}; + +typedef int (*skw_iwpriv_handler)(struct skw_iface *iface, void *param, + char *args, char *resp, int resp_len); + +struct skw_iwpriv_cmd { + char *name; + skw_iwpriv_handler handler; + char *help_info; +}; + +struct skw_max_ppdu_dur { + u8 idx; + u32 max_ppdu_dur; +} __packed; + +struct skw_wmm_ac_param_s { + /* b0-b3 aifsn + * b4 acm + * b5-b6 aci + * b7 revd + */ + u8 aci_aifn; + + /* b0-b3 ECWmin + * b4-b7 ECWmax + */ + u8 ec_wmin_wmax; + u16 txop_limit; +} __packed; + +struct skw_edca_param_s { + u8 enable; + struct skw_wmm_ac_param_s ac_best_effort; + struct skw_wmm_ac_param_s ac_background; + struct skw_wmm_ac_param_s ac_video; + struct skw_wmm_ac_param_s ac_voice; +} __packed; + +struct skw_cca_thre_nowifi { + u8 val; +} __packed; + +struct skw_cca_thre_11b { + u8 val; +} __packed; + +struct skw_cca_thre_ofdm { + u8 val; +} __packed; + +enum skw_lega_ofdm_rate_map { + LEGA_11B_SHORT_2M = 0x10, + LEGA_11B_SHORT_55M = 0x11, + LEGA_11B_SHORT_11M = 0x12, + LEGA_11B_LONG_1M = 0x20, + LEGA_11B_LONG_2M = 0x21, + LEGA_11B_LONG_55M = 0x22, + LEGA_11B_LONG_11M = 0x23, + + OFDM_6M = 0x30, + OFDM_9M = 0x31, + OFDM_12M = 0x32, + OFDM_18M = 0x33, + OFDM_24M = 0x34, + OFDM_36M = 0x35, + OFDM_48M = 0x36, + OFDM_54M = 0x37, + + HT_MCS_0 = 0x40, + HT_MCS_1 = 0x41, + HT_MCS_2 = 0x42, + HT_MCS_3 = 0x43, + HT_MCS_4 = 0x44, + HT_MCS_5 = 0x45, + HT_MCS_6 = 0x46, + HT_MCS_7 = 0x47, + HT_MCS_8 = 0x48, + HT_MCS_9 = 0x49, + HT_MCS_10 = 0x4a, + HT_MCS_11 = 0x4b, + HT_MCS_12 = 0x4c, + HT_MCS_13 = 0x4d, + HT_MCS_14 = 0x4e, + HT_MCS_15 = 0x4f, + HT_MCS_16 = 0x50, + HT_MCS_17 = 0x51, + HT_MCS_18 = 0x52, + HT_MCS_19 = 0x53, + HT_MCS_20 = 0x54, + HT_MCS_21 = 0x55, + HT_MCS_22 = 0x56, + HT_MCS_23 = 0x57, + HT_MCS_24 = 0x58, + HT_MCS_25 = 0x59, + HT_MCS_26 = 0x5a, + HT_MCS_27 = 0x5b, + HT_MCS_28 = 0x5c, + HT_MCS_29 = 0x5d, + HT_MCS_30 = 0x5e, + HT_MCS_31 = 0x5f, + + VHT_MCS_0 = 0x80, + VHT_MCS_1 = 0x81, + VHT_MCS_2 = 0x82, + VHT_MCS_3 = 0x83, + VHT_MCS_4 = 0x84, + VHT_MCS_5 = 0x85, + VHT_MCS_6 = 0x86, + VHT_MCS_7 = 0x87, + VHT_MCS_8 = 0x88, + VHT_MCS_9 = 0x89, + + HE_MCS_0 = 0xc0, + HE_MCS_1 = 0xc1, + HE_MCS_2 = 0xc2, + HE_MCS_3 = 0xc3, + HE_MCS_4 = 0xc4, + HE_MCS_5 = 0xc5, + HE_MCS_6 = 0xc6, + HE_MCS_7 = 0xc7, + HE_MCS_8 = 0xc8, + HE_MCS_9 = 0xc9, + HE_MCS_10 = 0xca, + HE_MCS_11 = 0xcb, + + ER_NDCM_1SS_242TONE_MCS0 = 0xcc, + ER_NDCM_1SS_242TONE_MCS1 = 0xcd, + ER_NDCM_1SS_242TONE_MCS2 = 0xce, + ER_NDCM_1SS_106TONE_MCS0 = 0xcf, + + ER_DCM_1SS_242TONE_MCS0 = 0xdc, + ER_DCM_1SS_242TONE_MCS1 = 0xdd, + ER_DCM_1SS_106TONE_MCS0 = 0xdf, + + NER_DCM_1SS_MCS0 = 0xec, + NER_DCM_1SS_MCS1 = 0xed, + NER_DCM_1SS_MCS3 = 0xee, + NER_DCM_1SS_MCS4 = 0xef, + + NER_DCM_2SS_MCS0 = 0xfc, + NER_DCM_2SS_MCS1 = 0xfd, + NER_DCM_2SS_MCS3 = 0xfe, + NER_DCM_2SS_MCS4 = 0xff, +}; + +struct skw_force_rts_rate { + u8 enable; + u8 rts_rate_24G; + u8 rts_rate_5G; +} __packed; + +struct skw_force_rx_rsp_rate { + u8 enable; + u8 rx_rsp_rate_11b_long; + u8 rx_rsp_rate_11b_short; + u8 rx_rsp_rate_ofdm; +} __packed; + +struct skw_set_scan_time { + u8 active_dwell_time; + u8 bypass_active_acan_auto_time; + //0 or 1 +} __packed; + +struct skw_set_tcpd_wakeup_host { + u8 enable; +} __packed; + +struct skw_set_rate_control_min_rate { + u8 rstrict_min_rate; +} __packed; + +struct skw_set_rate_control_rate_change { + u8 up_rate_class_num; + u8 down_rate_class_num; + u8 hw_rty_limit; + u8 per_rate_hw_rty_limit; + u8 per_rate_probe_hw_rty_limit; +} __packed; + +struct skw_set_rate_control_special_rate { + u8 special_frm_rate; +} __packed; + +struct skw_tlv_set_tx_lifetime { + u16 lifetime; +} __packed; + +struct skw_tlv_set_retry_cnt { + u8 rtycnt; + +} __packed; +struct skw_tlv_set_tx_rts_thrd { + u16 rts_thrd; +} __packed; + +struct skw_tlv_set_rx_special_80211_frame { + u8 en; + u8 type; + u8 sub_type; +} __packed; + +struct skw_tlv_set_rx_update_nav { + u8 intra_rssi; + u8 basic_rssi; + u8 nav_max_time; +} __packed; + +struct skw_tlv_set_apgo_timap { + u8 dtimforce0; + u8 dtimforce1; + u8 timforce0; + u8 timforce1; +} __packed; + +struct skw_tlv_set_dbdc_disable { + u8 disable; +} __packed; + +struct skw_tlv_set_assign_addr_val { + u32 addr; + u32 val; +} __packed; + +struct skw_tlv_get_assign_addr { + u32 addr; +} __packed; + +struct skw_tlv_get_assign_addr_rsp { + u32 val; +} __packed; + +struct skw_tlv_set_ageout_thrd { + u8 ageout_kick_thrd; + u8 ageout_keep_alive_thrd; +} __packed; + +//TLV 42 +struct skw_tlv_set_report_cqm_rssi_low_itvl { + u16 report_cqm_low_intvl_min_dur; + u16 report_cqm_low_intvl_max_dur; +} __packed; + +//TLV 54 +struct skw_tlv_set_ap_new_channel { + u8 chan; + u8 center_chan; + u8 center_two_chan; + u8 bw; + u8 band; +} __packed; + +//TLV 55 +struct skw_tlv_set_tx_retry_limit_en { + u8 short_retry_check_en; + u8 long_retry_check_en; + u8 ampdu_retry_check_en; +} __packed; + +//TLV 56 +struct skw_tlv_set_partial_twt_sched { + u8 en; + u32 start_time_l; + u32 start_time_h; + u32 interval; + u16 duration; + u8 duration_unit; + u8 sub_type; +} __packed; + +//TLV 57 +struct skw_tlv_set_thm_thrd { + u16 thm_high_thrd_tx_suspend; + u16 thm_low_thrd_tx_resume; +} __packed; + +//TLV 59 +struct skw_tlv_set_normal_scan_with_acs { + u8 en; +} __packed; + +const void *skw_iw_handlers(void); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_log.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_log.c new file mode 100755 index 0000000..c4ae298 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_log.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/version.h> +#include <linux/uaccess.h> +#include <linux/ctype.h> +#include <linux/if_arp.h> + +#include "skw_compat.h" +#include "skw_log.h" +#include "skw_dentry.h" +#include "skw_cfg80211.h" +#include "skw_iface.h" +#include "skw_rx.h" + +#define SKW_LL_MASK 0xffff + +#if defined(CONFIG_SWT6621S_LOG_ERROR) +#define SKW_LOG_LEVEL SKW_ERROR +#elif defined(CONFIG_SWT6621S_LOG_WARN) +#define SKW_LOG_LEVEL SKW_WARN +#elif defined(CONFIG_SWT6621S_LOG_INFO) +#define SKW_LOG_LEVEL SKW_INFO +#elif defined(CONFIG_SWT6621S_LOG_DEBUG) +#define SKW_LOG_LEVEL SKW_DEBUG +#elif defined(CONFIG_SWT6621S_LOG_DETAIL) +#define SKW_LOG_LEVEL SKW_DETAIL +#else +#define SKW_LOG_LEVEL SKW_INFO +#endif + +static unsigned long skw_dbg_level; + +unsigned long skw_log_level(void) +{ + return skw_dbg_level; +} + +static void skw_set_log_level(int level) +{ + unsigned long dbg_level; + + dbg_level = skw_log_level() & (~SKW_LL_MASK); + dbg_level |= ((level << 1) - 1); + + xchg(&skw_dbg_level, dbg_level); +} + +static void skw_enable_func_log(int func, bool enable) +{ + unsigned long dbg_level = skw_log_level(); + + if (enable) + dbg_level |= func; + else + dbg_level &= (~func); + + xchg(&skw_dbg_level, dbg_level); +} + + +static struct skw_monitor_dbg_iface *g_skw_dbg_iface; +static DEFINE_MUTEX(skw_dbg_mutex); + +void skw_dump_frame(u8 *frame, u16 frame_len) +{ + struct skw_radiotap_desc *radio_desc; + struct sk_buff *skb; + + mutex_lock(&skw_dbg_mutex); + if (g_skw_dbg_iface == NULL) { + mutex_unlock(&skw_dbg_mutex); + return; + } + + skb = alloc_skb(frame_len + sizeof(struct skw_radiotap_desc), GFP_KERNEL); + if (skb == NULL) { + mutex_unlock(&skw_dbg_mutex); + return; + } + + radio_desc = (struct skw_radiotap_desc *)skb_put(skb, + sizeof(struct skw_radiotap_desc)); + radio_desc->radiotap_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + radio_desc->radiotap_hdr.it_pad = 0; + radio_desc->radiotap_hdr.it_len = sizeof(struct skw_radiotap_desc); + radio_desc->radiotap_hdr.it_present = BIT(IEEE80211_RADIOTAP_FLAGS); + radio_desc->radiotap_flag = 0; + + skw_put_skb_data(skb, frame, frame_len); + + __skb_trim(skb, frame_len + radio_desc->radiotap_hdr.it_len); + skb_reset_mac_header(skb); + + skb->dev = g_skw_dbg_iface->ndev; + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_80211_RAW); + + #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) + netif_rx_ni(skb); + #else + netif_rx(skb); + #endif + + mutex_unlock(&skw_dbg_mutex); +} + +static netdev_tx_t skw_monitor_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static const struct net_device_ops skw_monitor_netdev_ops = { + .ndo_start_xmit = skw_monitor_xmit, +}; + +static void skw_add_dbg_iface(void) +{ + int priv_size, ret; + struct net_device *ndev = NULL; + struct skw_monitor_dbg_iface *iface; + u8 mac[6] = {0xFe, 0xfd, 0xfc, 0x12, 0x11, 0x88}; + + if (g_skw_dbg_iface) { + skw_err("already add dbg iface\n"); + return; + } + + priv_size = sizeof(struct skw_monitor_dbg_iface); + ndev = alloc_netdev_mqs(priv_size, "skw_dbg", +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + NET_NAME_ENUM, +#endif + ether_setup, SKW_WMM_AC_MAX, 1); + if (!ndev) { + skw_err("alloc ndev fail\n"); + return; + } + + iface = netdev_priv(ndev); + iface->ndev = ndev; + + skw_ether_copy(iface->addr, mac); + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + + ndev->netdev_ops = &skw_monitor_netdev_ops; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + eth_hw_addr_set(ndev, mac); +#else + skw_ether_copy(ndev->dev_addr, mac); +#endif + rtnl_lock(); + ret = skw_register_netdevice(ndev); + rtnl_unlock(); + if (ret) { + skw_err("register netdev failed\n"); + goto free_iface; + } + + mutex_lock(&skw_dbg_mutex); + g_skw_dbg_iface = iface; + mutex_unlock(&skw_dbg_mutex); + + return; + +free_iface: + if (ndev) + free_netdev(ndev); +} + +void skw_del_dbg_iface(void) +{ + struct skw_monitor_dbg_iface *iface; + + if (!g_skw_dbg_iface) + return; + + mutex_lock(&skw_dbg_mutex); + + iface = g_skw_dbg_iface; + g_skw_dbg_iface = NULL; + + mutex_unlock(&skw_dbg_mutex); + + rtnl_lock(); + skw_unregister_netdevice(iface->ndev); + rtnl_unlock(); +} + +static void skw_set_dump_frame(bool enable) +{ + if (enable) + skw_add_dbg_iface(); + else + skw_del_dbg_iface(); +} + +static char *skw_dump_frame_status(void) +{ + if (g_skw_dbg_iface) + return "enable"; + else + return "disable"; +} + +static int skw_log_show(struct seq_file *seq, void *data) +{ + int i; + u32 level = skw_log_level(); + const char *log_level; + static const char *log_name[] = {"NONE", "ERROR", "WARN", "INFO", "DEBUG", "DETAIL"}; + + i = ffs((level & SKW_LL_MASK) + 1) - 1; + if (i >= 0 && i < ARRAY_SIZE(log_name)) + log_level = log_name[i]; + else + log_level = "INVALID"; + + seq_puts(seq, "\n"); + seq_printf(seq, "Log Level: %s [ERROR|WARN|INFO|DEBUG|DETAIL]\n", log_name[i]); + +#define SKW_LOG_STATUS(s) (level & (s) ? "enable" : "disable") + seq_puts(seq, "\n"); + seq_printf(seq, "command log: %s\n", SKW_LOG_STATUS(SKW_CMD)); + seq_printf(seq, "event log: %s\n", SKW_LOG_STATUS(SKW_EVENT)); + seq_printf(seq, "dump log: %s\n", SKW_LOG_STATUS(SKW_DUMP)); + seq_printf(seq, "scan log: %s\n", SKW_LOG_STATUS(SKW_SCAN)); + seq_printf(seq, "timer log: %s\n", SKW_LOG_STATUS(SKW_TIMER)); + seq_printf(seq, "state log: %s\n", SKW_LOG_STATUS(SKW_STATE)); + seq_printf(seq, "work log: %s\n", SKW_LOG_STATUS(SKW_WORK)); + seq_printf(seq, "dump frame: %s\n", skw_dump_frame_status()); + seq_printf(seq, "DFS log: %s\n", SKW_LOG_STATUS(SKW_DFS)); +#undef SKW_LOG_STATUS + + return 0; +} + +static int skw_log_open(struct inode *inode, struct file *file) +{ + // return single_open(file, &skw_log_show, inode->i_private); + return single_open(file, &skw_log_show, skw_pde_data(inode)); +} + +static int skw_log_control(const char *cmd, bool enable) +{ + if (!strcmp("command", cmd)) + skw_enable_func_log(SKW_CMD, enable); + else if (!strcmp("event", cmd)) + skw_enable_func_log(SKW_EVENT, enable); + else if (!strcmp("dump", cmd)) + skw_enable_func_log(SKW_DUMP, enable); + else if (!strcmp("scan", cmd)) + skw_enable_func_log(SKW_SCAN, enable); + else if (!strcmp("timer", cmd)) + skw_enable_func_log(SKW_TIMER, enable); + else if (!strcmp("state", cmd)) + skw_enable_func_log(SKW_STATE, enable); + else if (!strcmp("work", cmd)) + skw_enable_func_log(SKW_WORK, enable); + else if (!strcmp("dfs", cmd)) + skw_enable_func_log(SKW_DFS, enable); + else if (!strcmp("detail", cmd)) + skw_set_log_level(SKW_DETAIL); + else if (!strcmp("debug", cmd)) + skw_set_log_level(SKW_DEBUG); + else if (!strcmp("info", cmd)) + skw_set_log_level(SKW_INFO); + else if (!strcmp("warn", cmd)) + skw_set_log_level(SKW_WARN); + else if (!strcmp("error", cmd)) + skw_set_log_level(SKW_ERROR); + else if (!strcmp("dumpframe", cmd)) + skw_set_dump_frame(enable); + else + return -EINVAL; + + return 0; +} + +static ssize_t skw_log_write(struct file *fp, const char __user *buffer, + size_t len, loff_t *offset) +{ + int i, idx; + char cmd[32]; + bool enable = false; + + for (idx = 0, i = 0; i < len; i++) { + char c; + + if (get_user(c, buffer)) + return -EFAULT; + + switch (c) { + case ' ': + break; + + case ':': + cmd[idx] = 0; + if (!strcmp("enable", cmd)) + enable = true; + else + enable = false; + + idx = 0; + break; + + case '|': + case '\0': + case '\n': + cmd[idx] = 0; + skw_log_control(cmd, enable); + idx = 0; + break; + + default: + cmd[idx++] = tolower(c); + idx %= 32; + + break; + } + + buffer++; + } + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_log_fops = { + .proc_open = skw_log_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_log_write, +}; +#else +static const struct file_operations skw_log_fops = { + .owner = THIS_MODULE, + .open = skw_log_open, + .read = seq_read, + .release = single_release, + .write = skw_log_write, +}; +#endif + +void skw_log_level_init(void) +{ + skw_set_log_level(SKW_LOG_LEVEL); + + skw_enable_func_log(SKW_CMD, false); + skw_enable_func_log(SKW_EVENT, false); + skw_enable_func_log(SKW_DUMP, false); + skw_enable_func_log(SKW_SCAN, false); + skw_enable_func_log(SKW_TIMER, false); + skw_enable_func_log(SKW_STATE, true); + skw_enable_func_log(SKW_WORK, false); + skw_enable_func_log(SKW_DFS, false); + + skw_procfs_file(NULL, "log_level", 0666, &skw_log_fops, NULL); +} + +void skw_log_level_deinit(void) +{ +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_log.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_log.h new file mode 100755 index 0000000..caf6034 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_log.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_LOG_H__ +#define __SKW_LOG_H__ + +#define SKW_ERROR BIT(0) +#define SKW_WARN BIT(1) +#define SKW_INFO BIT(2) +#define SKW_DEBUG BIT(3) +#define SKW_DETAIL BIT(4) + +#define SKW_CMD BIT(16) +#define SKW_EVENT BIT(17) +#define SKW_SCAN BIT(18) +#define SKW_TIMER BIT(19) +#define SKW_STATE BIT(20) +#define SKW_WORK BIT(21) +#define SKW_DFS BIT(22) + +#define SKW_DUMP BIT(31) + +#define SKW_LOG_TAG "SKWIFI6621S" +#define SKW_TAG_NAME(name) SKW_LOG_TAG " " #name + +#define SKW_TAG_ERROR SKW_TAG_NAME(ERROR) +#define SKW_TAG_WARN SKW_TAG_NAME(WARN) +#define SKW_TAG_INFO SKW_TAG_NAME(INFO) +#define SKW_TAG_DEBUG SKW_TAG_NAME(DBG) +#define SKW_TAG_DETAIL SKW_TAG_NAME(DETAIL) + +#define SKW_TAG_CMD SKW_TAG_NAME(CMD) +#define SKW_TAG_DATA SKW_TAG_NAME(DATA) +#define SKW_TAG_EVENT SKW_TAG_NAME(EVENT) +#define SKW_TAG_SCAN SKW_TAG_NAME(SCAN) +#define SKW_TAG_TIMER SKW_TAG_NAME(TIMER) +#define SKW_TAG_STATE SKW_TAG_NAME(STATE) +#define SKW_TAG_WORK SKW_TAG_NAME(WORK) +#define SKW_TAG_DUMP SKW_TAG_NAME(DUMP) +#define SKW_TAG_LOCAL SKW_TAG_NAME(LOCAL) + +unsigned long skw_log_level(void); + +#define skw_data_path_log(fmt, ...) \ + do { \ + if ((skw_log_level() & SKW_DEBUG)) \ + printk_ratelimited("[%s] %s: "fmt, \ + SKW_TAG_DATA, __func__, ##__VA_ARGS__); \ + } while (0) + +#define skw_log(level, fmt, ...) \ + do { \ + if (skw_log_level() & level) \ + pr_err(fmt, ##__VA_ARGS__); \ + } while (0) + +#define skw_err(fmt, ...) \ + skw_log(SKW_ERROR, "[%s] %s: "fmt, SKW_TAG_ERROR, __func__, ##__VA_ARGS__) + +#define skw_warn(fmt, ...) \ + skw_log(SKW_WARN, "[%s] %s: "fmt, SKW_TAG_WARN, __func__, ##__VA_ARGS__) + +#define skw_info(fmt, ...) \ + skw_log(SKW_INFO, "[%s] %s: "fmt, SKW_TAG_INFO, __func__, ##__VA_ARGS__) + +#define skw_dbg(fmt, ...) \ + skw_log(SKW_DEBUG, "[%s] %s: "fmt, SKW_TAG_DEBUG, __func__, ##__VA_ARGS__) + +#define skw_detail(fmt, ...) \ + skw_log(SKW_DETAIL, "[%s] %s: "fmt, SKW_TAG_DETAIL, __func__, ##__VA_ARGS__) + +#define skw_hex_dump(prefix, buf, len, force) \ + do { \ + if ((skw_log_level() & SKW_DUMP) || force) { \ + print_hex_dump(KERN_ERR, "["SKW_TAG_DUMP"] "prefix" - ", \ + DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); \ + } \ + } while (0) + +void skw_dump_frame(u8 *frame, u16 frame_len); +void skw_del_dbg_iface(void); + +void skw_log_level_init(void); +void skw_log_level_deinit(void); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mbssid.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mbssid.c new file mode 100755 index 0000000..5529ea2 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mbssid.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/kernel.h> +#include <linux/ieee80211.h> +#include <net/cfg80211.h> + +#include "skw_core.h" +#include "skw_log.h" +#include "skw_mbssid.h" +#include "skw_cfg80211.h" +#include "skw_compat.h" + +#define SKW_GENMASK_ULL(h, l) (((~0ULL) - (1ULL << (l)) + 1) & \ + (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + +static __always_inline u16 skw_get_unaligned_le16(const void *p) +{ + return le16_to_cpup((__le16 *)p); +} + +static struct skw_element *skw_find_elem(u8 eid, const u8 *ies, + int len, const u8 *match, + unsigned int match_len, + unsigned int match_offset) +{ + struct skw_element *elem; + + skw_foreach_element_id(elem, eid, ies, len) { + if (elem->datalen >= match_offset - 2 + match_len && + !memcmp(elem->data + match_offset - 2, match, match_len)) + return (void *)elem; + } + + return NULL; +} + +static const struct skw_element * +skw_get_profile_continuation(const u8 *ie, size_t ie_len, + const struct skw_element *mbssid_elem, + const struct skw_element *sub_elem) +{ + const u8 *mbssid_end = mbssid_elem->data + mbssid_elem->datalen; + const struct skw_element *next_mbssid; + const struct skw_element *sub; + + next_mbssid = skw_find_elem(WLAN_EID_MULTIPLE_BSSID, + mbssid_end, ie_len - (mbssid_end - ie), + NULL, 0, 0); + + if (!next_mbssid || + (sub_elem->data + sub_elem->datalen < mbssid_end - 1)) + return NULL; + + if (next_mbssid->datalen < 4) + return NULL; + + sub = (void *)&next_mbssid->data[1]; + + if (next_mbssid->data + next_mbssid->datalen < sub->data + sub->datalen) + return NULL; + + if (sub->id != 0 || sub->datalen < 2) + return NULL; + + return sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ? NULL : next_mbssid; +} + +static size_t skw_merge_profile(const u8 *ie, size_t ie_len, + const struct skw_element *mbssid_elem, + const struct skw_element *sub_elem, + u8 *merged_ie, size_t max_copy_len) +{ + size_t copied_len = sub_elem->datalen; + const struct skw_element *next_mbssid; + + if (sub_elem->datalen > max_copy_len) + return 0; + + memcpy(merged_ie, sub_elem->data, sub_elem->datalen); + + while ((next_mbssid = skw_get_profile_continuation(ie, ie_len, + mbssid_elem, sub_elem))) { + const struct skw_element *next = (void *)&next_mbssid->data[1]; + + if (copied_len + next->datalen > max_copy_len) + break; + + memcpy(merged_ie + copied_len, next->data, next->datalen); + + copied_len += next->datalen; + } + + return copied_len; +} + +static inline void skw_gen_new_bssid(const u8 *bssid, u8 max_bssid, + u8 mbssid_index, u8 *new_bssid) +{ + u64 bssid_u64 = skw_mac_to_u64(bssid); + u64 mask = SKW_GENMASK_ULL(max_bssid - 1, 0); + u64 new_bssid_u64; + + new_bssid_u64 = bssid_u64 & ~mask; + + new_bssid_u64 |= ((bssid_u64 & mask) + mbssid_index) & mask; + + skw_u64_to_mac(new_bssid_u64, new_bssid); +} + +static bool is_skw_element_inherited(const struct skw_element *elem, + const struct skw_element *non_inherit_elem) +{ + u8 id_len, ext_id_len, i, loop_len, id; + const u8 *list; + + if (elem->id == WLAN_EID_MULTIPLE_BSSID) + return false; + + if (!non_inherit_elem || non_inherit_elem->datalen < 2) + return true; + + id_len = non_inherit_elem->data[1]; + if (non_inherit_elem->datalen < 3 + id_len) + return true; + + ext_id_len = non_inherit_elem->data[2 + id_len]; + if (non_inherit_elem->datalen < 3 + id_len + ext_id_len) + return true; + + if (elem->id == SKW_WLAN_EID_EXTENSION) { + if (!ext_id_len) + return true; + + loop_len = ext_id_len; + list = &non_inherit_elem->data[3 + id_len]; + id = elem->data[0]; + } else { + if (!id_len) + return true; + + loop_len = id_len; + list = &non_inherit_elem->data[2]; + id = elem->id; + } + + for (i = 0; i < loop_len; i++) { + if (list[i] == id) + return false; + } + + return true; +} + +static size_t skw_gen_new_ie(const u8 *ie, size_t ielen, + const u8 *subelement, size_t subie_len, + u8 *new_ie, gfp_t gfp) +{ + u8 eid; + u8 *pos, *tmp; + const u8 *tmp_old, *tmp_new; + const struct skw_element *non_inherit; + u8 *sub_copy; + + sub_copy = SKW_KMEMDUP(subelement, subie_len, gfp); + if (!sub_copy) + return 0; + + pos = &new_ie[0]; + + /* set new ssid */ + tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len); + if (tmp_new) { + memcpy(pos, tmp_new, tmp_new[1] + 2); + pos += (tmp_new[1] + 2); + } + + /* get non inheritance list if exists */ + eid = SKW_EID_EXT_NON_INHERITANCE; + non_inherit = skw_find_elem(SKW_WLAN_EID_EXTENSION, sub_copy, + subie_len, &eid, 1, 0); + + tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); + tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie; + + while (tmp_old + tmp_old[1] + 2 - ie <= ielen) { + if (tmp_old[0] == 0) { + tmp_old++; + continue; + } + + if (tmp_old[0] == SKW_WLAN_EID_EXTENSION) { + tmp = (u8 *)skw_find_elem(SKW_WLAN_EID_EXTENSION, + sub_copy, subie_len, &tmp_old[2], 1, 2); + } else { + tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy, + subie_len); + } + + if (!tmp) { + const struct skw_element *old_elem = (void *)tmp_old; + + if (is_skw_element_inherited(old_elem, non_inherit)) { + memcpy(pos, tmp_old, tmp_old[1] + 2); + pos += tmp_old[1] + 2; + } + } else { + if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) { + if (!memcmp(tmp_old + 2, tmp + 2, 5)) { + memcpy(pos, tmp, tmp[1] + 2); + pos += tmp[1] + 2; + tmp[0] = WLAN_EID_SSID; + } else { + memcpy(pos, tmp_old, tmp_old[1] + 2); + pos += tmp_old[1] + 2; + } + } else { + memcpy(pos, tmp, tmp[1] + 2); + pos += tmp[1] + 2; + tmp[0] = WLAN_EID_SSID; + } + } + + if (tmp_old + tmp_old[1] + 2 - ie == ielen) + break; + + tmp_old += tmp_old[1] + 2; + } + + tmp_new = sub_copy; + while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) { + if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP || + tmp_new[0] == WLAN_EID_SSID)) { + memcpy(pos, tmp_new, tmp_new[1] + 2); + pos += tmp_new[1] + 2; + } + + if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len) + break; + + tmp_new += tmp_new[1] + 2; + } + + SKW_KFREE(sub_copy); + + return pos - new_ie; +} + +static void skw_parse_mbssid_data(struct wiphy *wiphy, + struct ieee80211_channel *rx_channel, + s32 signal, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + enum cfg80211_bss_frame_type ftype, +#endif + const u8 *bssid, u64 tsf, + u16 beacon_interval, const u8 *ie, + size_t ie_len, gfp_t gfp) +{ + const u8 *idx_ie; + const struct skw_element *elem, *sub; + size_t new_ie_len; + u8 bssid_index; + u8 max_indicator; + u8 new_bssid[ETH_ALEN]; + u8 *new_ie, *profile; + u64 seen_indices = 0; + u16 capability; + struct cfg80211_bss *bss; + + new_ie = SKW_ZALLOC(IEEE80211_MAX_DATA_LEN, gfp); + if (!new_ie) + return; + + profile = SKW_ZALLOC(ie_len, gfp); + if (!profile) + goto out; + + skw_foreach_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ie_len) { + if (elem->datalen < 4) + continue; + + skw_foreach_element(sub, elem->data + 1, elem->datalen - 1) { + u8 profile_len; + + if (sub->id != 0 || sub->datalen < 4) + continue; + + if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || + sub->data[1] != 2) { + continue; + } + + memset(profile, 0, ie_len); + + profile_len = skw_merge_profile(ie, ie_len, + elem, sub, profile, ie_len); + idx_ie = cfg80211_find_ie(SKW_WLAN_EID_MULTI_BSSID_IDX, + profile, profile_len); + + if (!idx_ie || idx_ie[1] < 1 || + idx_ie[2] == 0 || idx_ie[2] > 46) { + /* No valid Multiple BSSID-Index element */ + continue; + } + + if (seen_indices & (1ULL << (idx_ie[2]))) + net_dbg_ratelimited("Partial info for BSSID index %d\n", + idx_ie[2]); + + seen_indices |= (1ULL << (idx_ie[2])); + + bssid_index = idx_ie[2]; + max_indicator = elem->data[0]; + + skw_gen_new_bssid(bssid, max_indicator, + bssid_index, new_bssid); + + memset(new_ie, 0, IEEE80211_MAX_DATA_LEN); + new_ie_len = skw_gen_new_ie(ie, ie_len, profile, + profile_len, new_ie, + GFP_KERNEL); + if (!new_ie_len) + continue; + + capability = skw_get_unaligned_le16(profile + 2); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + bss = cfg80211_inform_bss(wiphy, rx_channel, ftype, + new_bssid, tsf, capability, + beacon_interval, new_ie, + new_ie_len, signal, gfp); +#else + bss = cfg80211_inform_bss(wiphy, rx_channel, + new_bssid, tsf, capability, + beacon_interval, new_ie, + new_ie_len, signal, gfp); +#endif + + if (!bss) + break; + + skw_bss_priv(bss)->bssid_index = bssid_index; + skw_bss_priv(bss)->max_bssid_indicator = max_indicator; + + cfg80211_put_bss(wiphy, bss); + } + } + + SKW_KFREE(profile); +out: + SKW_KFREE(new_ie); +} + +void skw_mbssid_data_parser(struct wiphy *wiphy, bool beacon, + struct ieee80211_channel *chan, s32 signal, + struct ieee80211_mgmt *mgmt, int mgmt_len) +{ + const u8 *ie = mgmt->u.probe_resp.variable; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + enum cfg80211_bss_frame_type ftype = CFG80211_BSS_FTYPE_PRESP; +#endif + size_t len = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + + if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, mgmt_len - len)) + return; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + if (beacon) + ftype = CFG80211_BSS_FTYPE_BEACON; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + skw_parse_mbssid_data(wiphy, chan, signal, ftype, mgmt->bssid, + le64_to_cpu(mgmt->u.probe_resp.timestamp), + le16_to_cpu(mgmt->u.probe_resp.beacon_int), + ie, mgmt_len - len, GFP_KERNEL); +#else + skw_parse_mbssid_data(wiphy, chan, signal, mgmt->bssid, + le64_to_cpu(mgmt->u.probe_resp.timestamp), + le16_to_cpu(mgmt->u.probe_resp.beacon_int), + ie, mgmt_len - len, GFP_KERNEL); +#endif +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mbssid.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mbssid.h new file mode 100755 index 0000000..5c3f130 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mbssid.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_MBSSID_H__ +#define __SKW_MBSSID_H__ + +#define SKW_EID_EXT_NON_INHERITANCE 56 + +void skw_mbssid_data_parser(struct wiphy *wiphy, bool beacon, + struct ieee80211_channel *chan, s32 signal, + struct ieee80211_mgmt *mgmt, int mgmt_len); + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mlme.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mlme.c new file mode 100755 index 0000000..760fcf4 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mlme.c @@ -0,0 +1,1208 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/kernel.h> +#include <linux/workqueue.h> +#include <linux/skbuff.h> +#include <linux/ieee80211.h> +#include <linux/etherdevice.h> +#include <net/cfg80211.h> + +#include "skw_core.h" +#include "skw_cfg80211.h" +#include "skw_iface.h" +#include "skw_timer.h" +#include "skw_msg.h" +#include "skw_mlme.h" +#include "skw_work.h" + +#define SKW_AP_AUTH_TIMEOUT 5000 +#define SKW_IEEE80211_HDR_LEN 24 + +static void skw_mlme_ap_del_client(struct skw_iface *iface, + struct skw_client *client) +{ + if (!client) + return; + + skw_dbg("client: %pM\n", client->addr); + + skw_del_timer_work(iface->skw, client); + + skw_list_del(&iface->sap.mlme_client_list, &client->list); + + if (client->aid) + clear_bit(client->aid, iface->sap.aid_map); + + SKW_KFREE(client->assoc_req_ie); + SKW_KFREE(client->challenge); + SKW_KFREE(client); +} + +static struct skw_client * +skw_mlme_ap_get_client(struct skw_iface *iface, const u8 *addr) +{ + struct skw_client *client = NULL, *tmp; + + if (!iface) + return NULL; + + spin_lock_bh(&iface->sap.mlme_client_list.lock); + + list_for_each_entry(tmp, &iface->sap.mlme_client_list.list, list) { + if (tmp && ether_addr_equal(addr, tmp->addr)) { + client = tmp; + break; + } + } + + spin_unlock_bh(&iface->sap.mlme_client_list.lock); + + return client; +} + +static struct skw_client * +skw_mlme_ap_add_client(struct skw_iface *iface, const u8 *addr) +{ + struct skw_client *client = NULL; + + client = SKW_ZALLOC(sizeof(*client), GFP_KERNEL); + if (client) { + INIT_LIST_HEAD(&client->list); + client->iface = iface; + client->state = SKW_STATE_NONE; + client->last_seq_ctrl = 0xFFFF; + client->idle = jiffies; + client->challenge = NULL; + client->assoc_req_ie = NULL; + client->aid = 0; + skw_ether_copy(client->addr, addr); + skw_dbg("%pM\n", client->addr); + + skw_list_add(&iface->sap.mlme_client_list, &client->list); + } + + return client; +} + +void skw_mlme_ap_remove_client(struct skw_iface *iface, const u8 *addr) +{ + struct skw_client *client; + + if (!iface->sap.sme_external) { + client = skw_mlme_ap_get_client(iface, addr); + skw_mlme_ap_del_client(iface, client); + } +} + +void skw_mlme_ap_del_sta(struct wiphy *wiphy, struct net_device *ndev, + const u8 *addr, u8 force) +{ + int ret = -1; + + skw_dbg("sta: %pM\n", addr); + + ret = skw_del_station(wiphy, ndev, addr, 12, 3); + if (ret) { + skw_err("failed, ret: %d\n", ret); + return; + } + +} + +static void skw_mlme_ap_auth_timeout(void *data) +{ + unsigned long timeout; + struct skw_client *client = data; + + if (!client) + return; + + skw_dbg("client: %pM\n", client->addr); + + if (client->state == SKW_STATE_ASSOCED) + return; + + timeout = client->idle + msecs_to_jiffies(SKW_AP_AUTH_TIMEOUT); + if (time_after(jiffies, timeout)) { + skw_queue_local_event(priv_to_wiphy(client->iface->skw), + client->iface, SKW_EVENT_LOCAL_AP_AUTH_TIMEOUT, + client, sizeof(*client)); + return; + } +} + +#if 0 +void skw_flush_sta_info(struct skw_iface *iface) +{ + LIST_HEAD(flush_list); + struct skw_client *sta; + + spin_lock_bh(&iface->sap.sta_lock); + list_replace_init(&iface->sap.mlme_client_list, &flush_list); + spin_unlock_bh(&iface->sap.sta_lock); + + // fixme: + // deauth all sta + while (!list_empty(&flush_list)) { + sta = list_first_entry(&flush_list, struct skw_client, list); + list_del(&sta->list); + skw_dbg("sta: %pM, state: %d\n", sta->addr, sta->state); + // skw_mlle_ap_state_event(sta, SKW_STATE_NONE); + SKW_KFREE(sta); + } +} +#endif + +int skw_mgmt_frame_with_reason(struct skw_iface *iface, u8 *da, u64 *cookie, u8 *bssid, + struct ieee80211_channel *ch, u16 stype, u16 reason, bool switchover) +{ + struct wiphy *wiphy = priv_to_wiphy(iface->skw); + struct ieee80211_mgmt reply; + + skw_dbg("stype: %d, reason: %d\n", stype, reason); + + reply.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); + reply.duration = 0; + reply.seq_ctrl = 0; + skw_ether_copy(reply.da, da); + skw_ether_copy(reply.sa, iface->addr); + skw_ether_copy(reply.bssid, bssid); + + reply.u.deauth.reason_code = cpu_to_le16(reason); + + return skw_mgmt_tx(wiphy, iface, ch, 0, + cookie, false, &reply, + SKW_DEAUTH_FRAME_LEN, SKW_DEAUTH_FRAME_LEN, &reply, switchover); +} + + +int skw_ap_simple_reply(struct skw_iface *iface, struct skw_client *client, + u16 stype, u16 reason) +{ + skw_dbg("stype: %d, reason: %d\n", stype, reason); + + return skw_mgmt_frame_with_reason(iface, client->addr, &client->cookie, + iface->sap.cfg.bssid, iface->sap.cfg.channel, stype, reason, true); +} + +static void skw_mlme_ap_auth_cb(struct skw_iface *iface, + struct skw_client *client, + struct ieee80211_mgmt *mgmt, + int mgmt_len, bool ack) +{ + u16 status; + + skw_dbg("client: %pM, ack: %d\n", client->addr, ack); + if (!client) + return; + + status = le16_to_cpu(mgmt->u.auth.status_code); + + if (ack && status == WLAN_STATUS_SUCCESS) { + skw_del_timer_work(iface->skw, client); + skw_add_timer_work(iface->skw, "auth_timeout", + skw_mlme_ap_auth_timeout, + client, SKW_AP_AUTH_TIMEOUT, + client, GFP_KERNEL); + } else { + skw_warn("failed\n"); + client->state = SKW_STATE_NONE; + skw_mlme_ap_del_sta(iface->wdev.wiphy, + iface->ndev, client->addr, false); + } +} + +static void skw_mlme_ap_assoc_cb(struct skw_iface *iface, + struct skw_client *client, + struct ieee80211_mgmt *mgmt, + int frame_len, bool ack, int reassoc) +{ + u16 status_code; + struct station_info info; + struct station_parameters params; + + if (!client) + return; + + if (reassoc) + status_code = le16_to_cpu(mgmt->u.reassoc_resp.status_code); + else + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + + skw_dbg("client: %pM, ack: %d, status code: %d\n", + client->addr, ack, status_code); + + if (ack && status_code == WLAN_STATUS_SUCCESS) { + skw_del_timer_work(iface->skw, client); + + params.sta_flags_set = 0; + params.sta_flags_set |= BIT(NL80211_STA_FLAG_ASSOCIATED); + skw_change_station(iface->wdev.wiphy, iface->ndev, + client->addr, ¶ms); + + memset(&info, 0x0, sizeof(info)); + if (client->assoc_req_ie) { + info.assoc_req_ies = client->assoc_req_ie; + info.assoc_req_ies_len = client->assoc_req_ie_len; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) + info.filled |= STATION_INFO_ASSOC_REQ_IES; +#endif + } + + cfg80211_new_sta(iface->ndev, client->addr, + &info, GFP_KERNEL); + SKW_KFREE(client->assoc_req_ie); + client->assoc_req_ie = NULL; + client->assoc_req_ie_len = 0; + + client->state = SKW_STATE_ASSOCED; + } else { + skw_err("failed, ack: %d, status_code: %d\n", ack, status_code); + + client->state = SKW_STATE_NONE; + skw_mlme_ap_del_sta(iface->wdev.wiphy, + iface->ndev, client->addr, false); + } +} + +void skw_mlme_ap_tx_status(struct skw_iface *iface, u64 cookie, + const u8 *frame, int frame_len, u16 ack) +{ + u16 fc; + int reassoc = 0; + struct skw_client *client; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame; + + skw_dbg("iface: %d, da: %pM, ack: %d, cookie: %lld\n", + iface->id, mgmt->da, ack, cookie); + + client = skw_mlme_ap_get_client(iface, mgmt->da); + if (!client || client->cookie != cookie) { + skw_dbg("cfg80211 tx status, cookie: %lld\n", cookie); + goto report; + } + + fc = SKW_MGMT_SFC(mgmt->frame_control); + + switch (fc) { + case IEEE80211_STYPE_AUTH: + skw_mlme_ap_auth_cb(iface, client, mgmt, frame_len, !!ack); + break; + + case IEEE80211_STYPE_REASSOC_RESP: + reassoc = 1; + /* fall through */ + skw_fallthrough; + case IEEE80211_STYPE_ASSOC_RESP: + skw_mlme_ap_assoc_cb(iface, client, mgmt, frame_len, + !!ack, reassoc); + break; + + default: + break; + } + + return; + +report: + cfg80211_mgmt_tx_status(&iface->wdev, cookie, frame, frame_len, + ack, GFP_KERNEL); +} + +static int skw_mlme_ap_auth_reply(struct skw_iface *iface, + struct skw_client *client, const u8 *bssid, + u16 auth_type, u16 transaction, u16 status, + u8 *ie, int ie_len) +{ + int ret; + int frame_len; + struct wiphy *wiphy; + struct ieee80211_mgmt *reply; + + skw_dbg("da: %pM, bssid: %pM, transaction: %d, status: %d, ie: %d\n", + client->addr, bssid, transaction, status, ie_len); + + wiphy = priv_to_wiphy(iface->skw); + frame_len = SKW_IEEE80211_HDR_LEN + + sizeof(reply->u.auth) + + ie_len; + + reply = SKW_ZALLOC(frame_len, GFP_KERNEL); + + if (!reply) + return -ENOMEM; + + reply->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_AUTH); + skw_ether_copy(reply->da, client->addr); + skw_ether_copy(reply->sa, iface->addr); + skw_ether_copy(reply->bssid, bssid); + + reply->u.auth.auth_alg = cpu_to_le16(auth_type); + reply->u.auth.auth_transaction = cpu_to_le16(transaction); + reply->u.auth.status_code = cpu_to_le16(status); + + if (ie && ie_len) + memcpy(reply->u.auth.variable, ie, ie_len); + + // skw_hex_dump("auth_reply", reply, frame_len, false); + ret = skw_mgmt_tx(wiphy, iface, iface->sap.cfg.channel, + 0, &client->cookie, false, reply, frame_len, + frame_len, reply, true); + + SKW_KFREE(reply); + + return ret; +} + +#if 0 +static int skw_ap_auth_shared_key(struct skw_client *sta, u16 trans_action) +{ + u16 status; + u8 *challenge; + + switch (trans_action) { + case 1: + sta->challenge = SKW_ZALLOC(WLAN_AUTH_CHALLENGE_LEN, + GFP_KERNEL); + if (IS_ERR(sta->challenge)) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + + /* Generate challenge text */ + get_random_bytes(sta->challenge, + WLAN_AUTH_CHALLENGE_LEN); + + status = WLAN_STATUS_SUCCESS; + break; + + case 3: + challenge = &mgmt->u.auth.variable[2]; + + if (memcmp(sta->challenge, challenge, + WLAN_AUTH_CHALLENGE_LEN) == 0) { + status = WLAN_STATUS_SUCCESS; + SKW_KFREE(sta->challenge); + } else { + status = WLAN_STATUS_CHALLENGE_FAIL; + } + + break; + + default: + status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + break; + } + + return status; +} +#endif + +static int skw_mlme_ap_auth_handler(struct skw_iface *iface, int freq, + int signal, void *frame, int frame_len) +{ + u8 *ies = NULL, *challenge; + int ies_len = 0, ret = 0; + struct skw_client *client = NULL; + struct station_parameters sta_params; + u8 challenge_ies[WLAN_AUTH_CHALLENGE_LEN + 2]; + struct wiphy *wiphy = priv_to_wiphy(iface->skw); + struct ieee80211_mgmt *mgmt = frame; + u16 auth_alg, status_code, trans_action; + u16 status = WLAN_STATUS_SUCCESS; + u16 seq_ctrl; + + auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); + trans_action = le16_to_cpu(mgmt->u.auth.auth_transaction); + status_code = le16_to_cpu(mgmt->u.auth.status_code); + seq_ctrl = le16_to_cpu(mgmt->seq_ctrl); + + skw_dbg("auth alg: %d, trans action: %d, status: %d, seq: %u\n", + auth_alg, trans_action, status_code, seq_ctrl); + + client = skw_mlme_ap_get_client(iface, mgmt->sa); + if (client) { + skw_dbg("flush peer status\n"); + } else { + client = skw_mlme_ap_add_client(iface, mgmt->sa); + if (!client) { + skw_err("add client: %pM failed\n", mgmt->sa); + return 0; + } + + memset(&sta_params, 0x0, sizeof(sta_params)); + skw_add_station(wiphy, iface->ndev, mgmt->sa, &sta_params); + } + + if (ieee80211_has_retry(mgmt->frame_control) && + client->last_seq_ctrl == seq_ctrl) { + skw_dbg("drop repeated auth(seq: %d)\n", seq_ctrl); + return 0; + } + + client->last_seq_ctrl = seq_ctrl; + client->state = SKW_STATE_AUTHED; + + if (!ether_addr_equal(mgmt->bssid, iface->sap.cfg.bssid)) { + skw_warn("failed, ap bssid: %pM, rx bssid: %pM\n", + iface->sap.cfg.bssid, mgmt->bssid); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + + // TODO: + // transation check + +#if 0 + if (auth_alg != iface->sap.auth_type) { + skw_err("auth type not match (client: %d, ap: %d)\n", + auth_alg, iface->sap.auth_type); + status = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto reply; + } + + if (client->state != SKW_STATE_NONE) { + skw_warn("current state: %s\n", sm_str[client->state]); + return 0; + } + +#endif + + switch (auth_alg) { + case WLAN_AUTH_OPEN: + if (trans_action != 1) { + status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + goto reply; + } + + if (status_code != WLAN_STATUS_SUCCESS) + return 0; + + status = WLAN_STATUS_SUCCESS; + client->last_seq_ctrl = seq_ctrl; + + break; + + case WLAN_AUTH_SAE: + if (!skw_compat_cfg80211_rx_mgmt(&iface->wdev, freq, signal, + frame, frame_len, 0, GFP_ATOMIC)) { + skw_warn("cfg80211_rx_mgmt failed\n"); + } + + return 0; + + case WLAN_AUTH_SHARED_KEY: + switch (trans_action) { + case 1: + client->challenge = SKW_ZALLOC(WLAN_AUTH_CHALLENGE_LEN, GFP_KERNEL); + if (!client->challenge) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + + /* Generate challenge text */ + get_random_bytes(client->challenge, + WLAN_AUTH_CHALLENGE_LEN); + + challenge_ies[0] = WLAN_EID_CHALLENGE; + challenge_ies[1] = WLAN_AUTH_CHALLENGE_LEN; + memcpy(challenge_ies + 2, client->challenge, + WLAN_AUTH_CHALLENGE_LEN); + ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; + + ies = challenge_ies; + status = WLAN_STATUS_SUCCESS; + break; + + case 3: + challenge = &mgmt->u.auth.variable[2]; + + if (client->challenge && + (memcmp(client->challenge, challenge, + WLAN_AUTH_CHALLENGE_LEN) == 0)) + status = WLAN_STATUS_SUCCESS; + else + status = WLAN_STATUS_CHALLENGE_FAIL; + + break; + + default: + status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + break; + } + + + break; + + default: + status = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + skw_warn("unsupport auth alg: %d\n", auth_alg); + break; + } + +reply: + ret = skw_mlme_ap_auth_reply(iface, client, mgmt->bssid, auth_alg, + trans_action + 1, status, ies, ies_len); + if (ret || status != WLAN_STATUS_SUCCESS) { + skw_warn("failed, ret = %d, status: %d\n", ret, status); + client->state = SKW_STATE_NONE; + skw_mlme_ap_del_sta(wiphy, iface->ndev, mgmt->sa, false); + } + + return 0; +} + +#if 0 +static int skw_ap_parse_element(struct skw_80211_element *element, + const u8 *ies, u32 ie_len) +{ + const struct element *elem; + + return 0; + + for_each_element(elem, ies, ie_len) { + switch (elem->id) { + case WLAN_EID_SSID: + // element->ssid = elem->data; + // element->ssid_len = elem->data; + break; + + case WLAN_EID_SUPP_RATES: + break; + case WLAN_EID_EXT_SUPP_RATES: + break; + case WLAN_EID_RSN: + break; + case WLAN_EID_PWR_CAPABILITY: + break; + case WLAN_EID_SUPPORTED_CHANNELS: + break; + case WLAN_EID_HT_CAPABILITY: + break; + case WLAN_EID_HT_OPERATION: + break; + case WLAN_EID_VHT_CAPABILITY: + break; + case WLAN_EID_VHT_OPERATION: + break; + case WLAN_EID_EXT_CAPABILITY: + break; + case WLAN_EID_MIC: + break; + case WLAN_EID_SUPPORTED_REGULATORY_CLASSES: + break; + default: + break; + } + } + + return 0; +} + +static u16 skw_ap_check_ssid(struct skw_iface *iface, + const u8 *ssid, int ssid_len) +{ + if (!ssid || + ssid_len != iface->sap.ssid_len || + memcmp(ssid, iface->sap.ssid, iface->sap.ssid_len) != 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + return WLAN_STATUS_SUCCESS; +} + +static u16 skw_ap_check_wmm(struct skw_client *sta, const u8 *wmm_ie, int len) +{ +#define SKW_WMM_IE_LEN 24 + struct skw_wmm_info { + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 version; + u8 qos_info; + } __packed; + + struct skw_wmm_info *wmm = (struct skw_wmm_info *)wmm_ie; + + if (len != SKW_WMM_IE_LEN || + wmm->oui_subtype != 0 || + wmm->version != 1) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + return WLAN_STATUS_SUCCESS; +} +#endif +static u16 skw_mlme_ap_check_assoc_ie(struct skw_iface *iface, + struct skw_client *client, + const u8 *ie, int ie_len) +{ + // skw_hex_dump("rx assoc ie", ie, ie_len, false); + // struct skw_80211_element e; + + //memset(&ie, 0x0, sizeof(e)); + // skw_ap_parse_element(&e, ie, ie_len); + +#if 0 + /* check ssid */ + if (skw_ap_check_ssid(iface, e.ssid, e.ssid_len)) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* check wmm */ + if (skw_ap_check_wmm(sta, e.wmm, e.wmm_len)) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* check ext capa */ + /* check support rate */ +#endif + return 0; +} + +static u16 skw_mlme_ap_new_aid(struct skw_iface *iface) +{ + u16 aid = 0; + + for (aid = 1; aid < 64; aid++) + if (!test_and_set_bit(aid, iface->sap.aid_map)) + break; + + return aid; +} + +/* add basic rate & ext support rate */ +static u8 *skw_mlme_ap_add_rate(struct wiphy *wiphy, + struct skw_iface *iface, u8 *ies) +{ + int i, nr; + u8 *pos = ies, *ext_rate_count; + struct ieee80211_rate *rate; + struct ieee80211_supported_band *sband; + + /* basic rate */ + sband = wiphy->bands[iface->sap.cfg.channel->band]; + rate = sband->bitrates; +#if 0 + enum ieee80211_rate_flags mandatory; + + if (sband->band == NL80211_BAND_2GHZ) { + if (scan_width == NL80211_BSS_CHAN_WIDTH_5 || + scan_width == NL80211_BSS_CHAN_WIDTH_10) + mandatory = IEEE80211_RATE_MANDATORY_G; + else + mandatory = IEEE80211_RATE_MANDATORY_B; + } else { + mandatory = IEEE80211_RATE_MANDATORY_A; + } +#endif + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = SKW_BASIC_RATE_COUNT; + for (i = 0; i < SKW_BASIC_RATE_COUNT; i++) + *pos++ = rate[i].bitrate / 5; + + /* ext support rate */ + *pos++ = WLAN_EID_EXT_SUPP_RATES; + ext_rate_count = pos++; + + for (i = SKW_BASIC_RATE_COUNT; i < sband->n_bitrates; i++) + *pos++ = rate[i].bitrate / 5; + + nr = sband->n_bitrates - SKW_BASIC_RATE_COUNT; + if (iface->sap.ht_required) { + *pos++ = 0x80 | SKW_BSS_MEMBERSHIP_SELECTOR_HT_PHY; + nr++; + } + + if (iface->sap.vht_required) { + *pos++ = 0x80 | SKW_BSS_MEMBERSHIP_SELECTOR_VHT_PHY; + nr++; + } + + *ext_rate_count = nr; + + return pos; +} + +static u8 *skw_mlme_ap_add_ht_cap(struct skw_iface *iface, u8 *ies) +{ + u8 *pos = ies; + int len = sizeof(struct ieee80211_ht_cap); + + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = len; + memcpy(pos, &iface->sap.cfg.ht_cap, len); + + return pos + len; +} + +static u8 *skw_mlme_ap_add_ht_oper(struct skw_iface *iface, u8 *ies) +{ + u8 *pos = ies; + struct ieee80211_ht_operation *oper; + + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(*oper); + + oper = (struct ieee80211_ht_operation *)pos; + memset(oper, 0x0, sizeof(*oper)); + + oper->primary_chan = iface->sap.cfg.channel->hw_value; + + return pos + sizeof(*oper); +} + +static void skw_mlme_ap_parse_ies(const u8 *beacon, int beacon_len, + struct skw_element_info *e) +{ + const struct skw_element *element; + + if (!beacon || beacon_len == 0) + return; + + skw_foreach_element(element, beacon, beacon_len) { + switch (element->id) { + case WLAN_EID_SSID: + e->ssid.len = element->datalen; + memcpy(e->ssid.data, element->data, element->datalen); + break; + + case WLAN_EID_SUPP_RATES: + e->support_rate = element; + break; + + case WLAN_EID_EXT_SUPP_RATES: + e->ext_rate = element; + break; + + case WLAN_EID_HT_CAPABILITY: + e->ht_capa = element; + break; + + case WLAN_EID_HT_OPERATION: + e->ht_oper = element; + break; + + case WLAN_EID_VHT_CAPABILITY: + e->vht_capa = element; + break; + + case WLAN_EID_VHT_OPERATION: + e->vht_oper = element; + break; + + case WLAN_EID_EXT_CAPABILITY: + e->ext_capa = element; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + e->vendor_vht = element; + break; + + default: + skw_dbg("unused element: %d, len: %d\n", + element->id, element->datalen); + break; + } + } +} + +static int skw_mlme_ap_assoc_reply(struct skw_iface *iface, + struct skw_client *client, u16 status, bool reassoc) +{ + u16 fc, elen; + u8 *ies, *pos; + int ret, frame_len, len; + struct wiphy *wiphy = priv_to_wiphy(iface->skw); + struct ieee80211_mgmt *reply; + struct skw_element_info e; + + skw_dbg("client addr: %pM, reassoc: %d, aid: %d, status code: %d\n", + client->addr, reassoc, client->aid, status); + + len = sizeof(struct ieee80211_mgmt) + 1024; + + reply = SKW_ZALLOC(len, GFP_KERNEL); + if (!reply) + return -ENOMEM; + + memset(&e, 0x0, sizeof(e)); + + fc = reassoc ? IEEE80211_STYPE_REASSOC_RESP : + IEEE80211_STYPE_ASSOC_RESP; + + reply->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | fc); + reply->duration = 0; + skw_ether_copy(reply->da, client->addr); + skw_ether_copy(reply->sa, iface->addr); + skw_ether_copy(reply->bssid, iface->sap.cfg.bssid); + reply->seq_ctrl = 0; + + reply->u.assoc_resp.capab_info = client->capa; + reply->u.assoc_resp.status_code = status; + reply->u.assoc_resp.aid = client->aid; + + frame_len = SKW_IEEE80211_HDR_LEN + + sizeof(reply->u.assoc_resp); + + pos = ies = reply->u.assoc_resp.variable; + + len = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + skw_mlme_ap_parse_ies(iface->sap.probe_resp + len, + iface->sap.probe_resp_len - len, &e); + + /* support rate & ext rate */ + + if (e.support_rate) { + elen = e.support_rate->datalen + 2; + memcpy(pos, e.support_rate, elen); + pos += elen; + + if (e.ext_rate) { + elen = e.ext_rate->datalen + 2; + memcpy(pos, e.ext_rate, elen); + pos += elen; + } + } else { + pos = skw_mlme_ap_add_rate(wiphy, iface, pos); + } + +#if 1 + /* 80211n capa & oper */ + if (e.ht_capa && e.ht_oper) { + elen = e.ht_capa->datalen + 2; + memcpy(pos, e.ht_capa, elen); + pos += elen; + + elen = e.ht_oper->datalen + 2; + memcpy(pos, e.ht_oper, elen); + pos += elen; + } else { + pos = skw_mlme_ap_add_ht_cap(iface, pos); + pos = skw_mlme_ap_add_ht_oper(iface, pos); + } + + /* 11ac capa */ + + /* vendor vht */ + if (e.vendor_vht) { + elen = e.vendor_vht->datalen + 2; + memcpy(pos, e.vendor_vht, elen); + pos += elen; + } +#endif + + frame_len += pos - ies; + + ret = skw_mgmt_tx(wiphy, iface, iface->sap.cfg.channel, + 0, &client->cookie, false, reply, frame_len, + frame_len, reply, true); + + SKW_KFREE(reply); + + return ret; +} + +static int skw_mlme_ap_assoc_handler(struct skw_iface *iface, void *frame, + int frame_len, int reassoc) +{ + u8 *ie; + int ie_len = 0, ret; + u16 capab_info, status; + struct skw_client *client; + struct ieee80211_mgmt *mgmt = frame; + u16 seq_ctrl; + + skw_dbg("iface: %d, sa: %pM, reassoc: %d\n", + iface->id, mgmt->sa, reassoc); + + seq_ctrl = le16_to_cpu(mgmt->seq_ctrl); + + client = skw_mlme_ap_get_client(iface, mgmt->sa); + if (!client) { + skw_warn("client: %pM not exist\n", mgmt->sa); + return 0; + } + + if (client->state == SKW_STATE_NONE) { + status = WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA; + skw_dbg("client->state: %d\n", client->state); + skw_ap_send_disassoc(iface, client, status); + return 0; + } + + if (ieee80211_has_retry(mgmt->frame_control) && + client->last_seq_ctrl == seq_ctrl) { + skw_dbg("drop repeated assoc frame(seq: %d)\n", seq_ctrl); + return 0; + } + + client->last_seq_ctrl = seq_ctrl; + + ie_len = frame_len - sizeof(struct ieee80211_hdr_3addr); + if (reassoc) { + capab_info = le16_to_cpu(mgmt->u.reassoc_req.capab_info); + ie = mgmt->u.reassoc_req.variable; + ie_len -= sizeof(mgmt->u.reassoc_req); + } else { + capab_info = le16_to_cpu(mgmt->u.assoc_req.capab_info); + ie = mgmt->u.assoc_req.variable; + ie_len -= sizeof(mgmt->u.assoc_req); + } + + client->capa = capab_info; + + // check assoc ies + status = skw_mlme_ap_check_assoc_ie(iface, client, ie, ie_len); + if (status != WLAN_STATUS_SUCCESS) { + skw_ap_send_disassoc(iface, client, status); + return 0; + } + + // assign aid + client->aid = skw_mlme_ap_new_aid(iface); + if (!client->aid) { + status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto reply; + } + + // 11W +reply: + ret = skw_mlme_ap_assoc_reply(iface, client, status, reassoc); + if (ret || status != WLAN_STATUS_SUCCESS) { + skw_err("ret: %d, status: %d\n", ret, status); + return ret; + } + + if (!client->assoc_req_ie) { + client->assoc_req_ie = SKW_ZALLOC(ie_len, GFP_KERNEL); + if (client->assoc_req_ie) { + memcpy(client->assoc_req_ie, ie, ie_len); + client->assoc_req_ie_len = ie_len; + } + } + + return ret; +} + +int skw_mlme_ap_rx_mgmt(struct skw_iface *iface, u16 fc, int freq, + int signal, void *frame, int frame_len) +{ + int reassoc = 0; + struct skw_client *client; + struct ieee80211_mgmt *mgmt = frame; + + // address check + switch (fc) { + case IEEE80211_STYPE_AUTH: + skw_mlme_ap_auth_handler(iface, freq, signal, frame, frame_len); + break; + + case IEEE80211_STYPE_DEAUTH: + case IEEE80211_STYPE_DISASSOC: + client = skw_mlme_ap_get_client(iface, mgmt->sa); + if (!client) { + skw_warn("can't find sta:%pM\n", mgmt->sa); + return 0; + } + + if (client->state >= SKW_STATE_ASSOCED) { + //notify hostapd to update state and delete sta + cfg80211_del_sta(iface->ndev, client->addr, GFP_KERNEL); + } else if (client->state >= SKW_STATE_AUTHED) { + //just delete local sta info + skw_mlme_ap_del_sta(iface->wdev.wiphy, + iface->ndev, client->addr, false); + } +#if 0 + skw_add_timer_work("idle_release", skw_mlme_ap_auth_timeout, + client, SKW_AP_IDLE_TIMEOUT, + client, GFP_KERNEL); +#endif + break; + + case IEEE80211_STYPE_REASSOC_REQ: + reassoc = 1; + /* fall through */ + skw_fallthrough; + case IEEE80211_STYPE_ASSOC_REQ: + skw_mlme_ap_assoc_handler(iface, frame, frame_len, reassoc); + break; + + case IEEE80211_STYPE_PROBE_REQ: + skw_fallthrough; + case IEEE80211_STYPE_PROBE_RESP: + skw_fallthrough; + case IEEE80211_STYPE_ACTION: + if (!skw_compat_cfg80211_rx_mgmt(&iface->wdev, freq, signal, + frame, frame_len, 0, GFP_ATOMIC)) { + skw_warn("mlme_ap_rx failed\n"); + } + break; + + default: + skw_warn("unsupport fc type: 0x%x\n", fc); + break; + } + + return 0; +} + +#if 0 +int skw_send_deauth_frame(struct wiphy *wiphy, struct net_device *netdev, + int reason_code) +{ + int ret; + int size; + char *buff = NULL; + struct skw_core *skw; + struct skw_disconnect_param *deauth_param = NULL; + + skw = wiphy_priv(wiphy); + + size = sizeof(struct skw_disconnect_param); + buff = SKW_ZALLOC(size, GFP_KERNEL); + if (IS_ERR_OR_NULL(buff)) { + skw_err("Malloc disconnect param for deauth failed\n"); + return -ENOMEM; + } + + deauth_param = (struct skw_disconnect_param *)buff; + deauth_param->type = SKW_DISCONNECT_SEND_DEAUTH; + deauth_param->local_state_change = true; + deauth_param->reason_code = reason_code; + deauth_param->ie_len = 0; + + ret = skw_send_msg(wiphy, netdev, SKW_CMD_DISCONNECT, buff, + size, NULL, 0); + if (ret) + skw_err("Deauth failed ret:%d\n", ret); + + SKW_KFREE(buff); + + return ret; +} +#endif + +static int skw_mlme_sta_ft_event(struct skw_iface *iface, void *buf, int len) +{ + int ie_len; + struct cfg80211_ft_event_params ft_event; + struct ieee80211_mgmt *mgmt = buf; + + ie_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable); + + ft_event.ies = mgmt->u.auth.variable; + ft_event.ies_len = ie_len; + ft_event.target_ap = mgmt->bssid; + ft_event.ric_ies = NULL; + ft_event.ric_ies_len = 0; + + cfg80211_ft_event(iface->ndev, &ft_event); + + return 0; +} + +int skw_mlme_sta_rx_auth(struct skw_iface *iface, int freq, int signal, + void *buf, int len) +{ + u16 status_code; + struct ieee80211_mgmt *mgmt = buf; + struct wiphy *wiphy = iface->wdev.wiphy; + struct skw_connect_param *conn = iface->sta.conn; + + skw_dbg("auth_type: %d, flags: 0x%x\n", + conn->auth_type, conn->flags); + + conn->state = SKW_STATE_AUTHED; + + if (conn->auth_type == NL80211_AUTHTYPE_SAE) + return skw_compat_cfg80211_rx_mgmt(&iface->wdev, freq, signal, + buf, len, 0, GFP_ATOMIC); + + if (conn->auth_type == NL80211_AUTHTYPE_FT) + return skw_mlme_sta_ft_event(iface, mgmt, len); + + status_code = le16_to_cpu(mgmt->u.auth.status_code); + if (status_code == WLAN_STATUS_SUCCESS) + return skw_connect_assoc(wiphy, iface->ndev, conn); + + if (SKW_TEST(conn->flags, SKW_CONN_FLAG_AUTH_AUTO) && + status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { + + mutex_lock(&iface->sta.conn->lock); + + switch (conn->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + if (conn->key_len) + conn->auth_type = NL80211_AUTHTYPE_SHARED_KEY; + else + conn->auth_type = NL80211_AUTHTYPE_FT; + break; + + case NL80211_AUTHTYPE_SHARED_KEY: + conn->auth_type = NL80211_AUTHTYPE_FT; + break; + + default: + SKW_CLEAR(conn->flags, SKW_CONN_FLAG_AUTH_AUTO); + break; + } + + mutex_unlock(&iface->sta.conn->lock); + + if (conn->flags & SKW_CONN_FLAG_AUTH_AUTO) { + return skw_queue_local_event(wiphy, iface, + SKW_EVENT_LOCAL_STA_CONNECT, + NULL, 0); + } + } + + /* status code != WLAN_STATUS_SUCCESS */ + conn->state = SKW_STATE_NONE; + + return 0; +} + +int skw_mlme_sta_rx_assoc(struct skw_iface *iface, struct cfg80211_bss *bss, + void *frame, int len, void *req_ie, int req_ie_len) +{ + u16 status; + int resp_ie_len; + struct ieee80211_mgmt *mgmt = frame; + struct skw_connect_param *conn = iface->sta.conn; + + skw_dbg("bssid: %pM\n", mgmt->bssid); + + resp_ie_len = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + resp_ie_len = len - resp_ie_len; + + status = le16_to_cpu(mgmt->u.assoc_resp.status_code); + if (status == WLAN_STATUS_SUCCESS) { + mutex_lock(&conn->lock); + conn->state = SKW_STATE_ASSOCED; + skw_connected(iface->ndev, conn, req_ie, req_ie_len, + mgmt->u.assoc_resp.variable, resp_ie_len, + status, GFP_KERNEL); + mutex_unlock(&conn->lock); + } else { + conn->state = SKW_STATE_NONE; + skw_disconnected(iface->ndev, status, + mgmt->u.assoc_resp.variable, resp_ie_len, + false, GFP_KERNEL); + } + + return 0; +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mlme.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mlme.h new file mode 100755 index 0000000..00439bc --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_mlme.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_MLME_H__ +#define __SKW_MLME_H__ + +#include "skw_iface.h" + +struct skw_client { + struct list_head list; + struct skw_iface *iface; + enum SKW_STATES state; + u32 capa; + + u16 aid; + u8 addr[ETH_ALEN]; + + u8 *challenge; + u64 cookie; + unsigned long idle; + u8 *assoc_req_ie; + u16 assoc_req_ie_len; + u16 last_seq_ctrl; +}; + +struct skw_element_info { + struct { + int len; + u8 data[32]; + } ssid; + const struct skw_element *support_rate; + const struct skw_element *ext_rate; + const struct skw_element *ht_capa; + const struct skw_element *ht_oper; + const struct skw_element *vht_capa; + const struct skw_element *vht_oper; + const struct skw_element *ext_capa; + const struct skw_element *vendor_vht; +}; + +int skw_mgmt_frame_with_reason(struct skw_iface *iface, u8 *da, u64 *cookie, u8 *bssid, + struct ieee80211_channel *ch, u16 stype, u16 reason, bool switchover); + +int skw_ap_simple_reply(struct skw_iface *iface, struct skw_client *client, + u16 stype, u16 reason); + +static inline int skw_ap_send_deauth(struct skw_iface *iface, + struct skw_client *client, u16 reason) +{ + return skw_ap_simple_reply(iface, client, + IEEE80211_STYPE_DEAUTH, reason); +} + + +static inline int skw_ap_send_disassoc(struct skw_iface *iface, + struct skw_client *client, u16 code) +{ + return skw_ap_simple_reply(iface, client, + IEEE80211_STYPE_DISASSOC, code); +} + +void skw_mlme_sta_tx_status(struct skw_iface *iface, u64 cookie, + const u8 *frame, int frame_len, u16 ack); +int skw_mlme_sta_rx_mgmt(struct skw_iface *iface, int freq, int signal, + void *frame, int frame_len); +int skw_process_auth_response(struct skw_iface *iface, int freq, + int signal, void *frame, int frame_len); +int skw_ap_mgmt_handler(struct skw_iface *iface, void *frame, int frame_len); +void skw_mlme_ap_del_sta(struct wiphy *wiphy, struct net_device *ndev, + const u8 *addr, u8 force); +int skw_mlme_ap_rx_mgmt(struct skw_iface *iface, u16 fc, int freq, + int signal, void *frame, int frame_len); +void skw_mlme_ap_remove_client(struct skw_iface *iface, const u8 *addr); +void skw_mlme_ap_tx_status(struct skw_iface *iface, u64 cookie, + const u8 *frame, int frame_len, u16 ack); +int skw_mlme_sta_rx_auth(struct skw_iface *iface, int freq, int signal, + void *buf, int len); + +int skw_mlme_sta_rx_assoc(struct skw_iface *iface, struct cfg80211_bss *bss, + void *frame, int len, void *req_ie, int req_ie_len); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_msg.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_msg.c new file mode 100755 index 0000000..060e681 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_msg.c @@ -0,0 +1,2145 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/skbuff.h> +#include <net/netlink.h> + +#include "skw_core.h" +#include "skw_iface.h" +#include "skw_msg.h" +#include "skw_vendor.h" +#include "skw_mlme.h" +#include "skw_mbssid.h" +#include "skw_cfg80211.h" +#include "skw_timer.h" +#include "skw_rx.h" +#include "skw_work.h" +#include "skw_calib.h" +#include "trace.h" +#include "skw_dfs.h" + +static int skw_event_scan_complete(struct skw_core *skw, + struct skw_iface *iface, void *buf, int len, + struct skw_skb_rxcb *cb) +{ + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + skw_scan_done(skw, iface, false); + + return 0; +} + +static int skw_event_sched_scan_done(struct skw_core *skw, + struct skw_iface *iface, void *buf, int len, + struct skw_skb_rxcb *cb) +{ + struct wiphy *wiphy = priv_to_wiphy(skw); + + skw_dbg("actived: %d\n", !!skw->sched_scan_req); + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + if (!skw->sched_scan_req) + return 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + cfg80211_sched_scan_results(wiphy, skw->sched_scan_req->reqid); +#else + cfg80211_sched_scan_results(wiphy); +#endif + + return 0; +} + +static int skw_event_disconnect(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + int ret = 0; + struct wiphy *wiphy = priv_to_wiphy(skw); + struct skw_discon_event_params *param = buf; + + skw_info("bssid: %pM, reason: %u\n", param->bssid, param->reason); + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + skw_sta_leave(wiphy, iface->ndev, param->bssid, + param->reason, false); + + if (iface->sta.sme_external) { + if (iface->sta.core.cbss) { + skw_compat_assoc_failure(iface->ndev, + iface->sta.core.cbss, false); + + iface->sta.core.cbss = NULL; + } else { + skw_tx_mlme_mgmt(iface->ndev, IEEE80211_STYPE_DEAUTH, + param->bssid, param->bssid, param->reason); + } + } else { + skw_disconnected(iface->ndev, param->reason, NULL, 0, true, + GFP_KERNEL); + } + + return ret; +} + +static int skw_sta_rx_deauth(struct wiphy *wiphy, struct skw_iface *iface, + void *buf, int len) +{ + u16 reason; + struct ieee80211_mgmt *mgmt = buf; + struct skw_peer_ctx *ctx; + + skw_wdev_assert_lock(iface); + + if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) { + cfg80211_tdls_oper_request(iface->ndev, mgmt->sa, + NL80211_TDLS_TEARDOWN, + SKW_WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE, + GFP_KERNEL); + return 0; + } + + ctx = skw_peer_ctx(iface, mgmt->bssid); + if (!ctx) { + skw_dbg("recv deauth twice\n"); + return -ENOENT; + } + + reason = le16_to_cpu(mgmt->u.deauth.reason_code); + + skw_sta_leave(wiphy, iface->ndev, mgmt->bssid, reason, false); + + if (iface->sta.sme_external) + skw_compat_rx_mlme_mgmt(iface->ndev, buf, len); + else + skw_disconnected(iface->ndev, reason, NULL, 0, true, + GFP_KERNEL); + + return 0; +} + +static int skw_sta_rx_auth(struct wiphy *wiphy, struct skw_iface *iface, + int freq, int signal, void *buf, int len) +{ + u16 status_code, auth_alg; + struct ieee80211_mgmt *mgmt = buf; + struct skw_bss_cfg *bss = &iface->sta.core.bss; + + skw_wdev_assert_lock(iface); + + if (!ether_addr_equal(bss->bssid, mgmt->bssid)) { + skw_warn("bssid unmatch, current: %pM, mgmt: %pM\n", + bss->bssid, mgmt->bssid); + + return 0; + } + + skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHED); + + iface->sta.core.pending.step_start = jiffies; + iface->sta.core.pending.retry = 0; + + auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); + + /* SAE confirm frame received */ + if (auth_alg == WLAN_AUTH_SAE && + le16_to_cpu(mgmt->u.auth.auth_transaction) == 2) + SKW_SET(iface->sta.core.sm.flags, SKW_SM_FLAG_SAE_RX_CONFIRM); + + status_code = le16_to_cpu(mgmt->u.auth.status_code); + switch (status_code) { + case WLAN_STATUS_SUCCESS: + case SKW_WLAN_STATUS_SAE_HASH_TO_ELEMENT: + break; + + case WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG: + if (iface->sta.is_wep) { + struct skw_auth_param *params; + + params = (struct skw_auth_param *)iface->sta.core.pending.auth_cmd; + if (auth_alg != params->auth_algorithm) + mgmt->u.auth.auth_alg = cpu_to_le16(params->auth_algorithm); + } + + skw_fallthrough; + + default: + skw_info("auth failed, status code: %d\n", status_code); + iface->sta.report_deauth = false; + skw_sta_leave(wiphy, iface->ndev, mgmt->bssid, + WLAN_REASON_UNSPECIFIED, false); + break; + } + + if (iface->sta.core.sm.rty_state == SKW_RETRY_ASSOC) { + skw_dbg("local retry auth, not report\n"); + skw_set_state(&iface->sta.core.sm, SKW_STATE_ASSOCING); + return 0; + } + + if (iface->sta.sme_external) + skw_compat_rx_mlme_mgmt(iface->ndev, buf, len); + else + skw_mlme_sta_rx_auth(iface, freq, signal, buf, len); + + return 0; +} + +static int skw_sta_rx_assoc(struct skw_iface *iface, int freq, + int signal, void *buf, int len) +{ + u16 status_code; + struct skw_peer_ctx *ctx; + u8 *assoc_req_ie = NULL; + struct ieee80211_mgmt *mgmt = buf; + struct skw_sta_core *core = &iface->sta.core; + + skw_dump_frame((u8 *)buf, len); + skw_wdev_assert_lock(iface); + + ctx = skw_get_ctx(iface->skw, iface->lmac_id, core->bss.ctx_idx); + if (!ctx) { + skw_err("invalid pidx: %d\n", core->bss.ctx_idx); + return 0; + } + + skw_peer_ctx_lock(ctx); + + if (!ctx->peer || + !ether_addr_equal(ctx->peer->addr, mgmt->bssid)) { + skw_peer_ctx_unlock(ctx); + return 0; + } + + skw_set_state(&core->sm, SKW_STATE_ASSOCED); + + //TBD: Intial the rx free channel and enable the ability to refill it. + if (iface->skw->hw.bus == SKW_BUS_PCIE) { + if (skw_edma_get_refill((void *)iface->skw, iface->lmac_id) == 0) + skw_edma_init_data_chan((void *)iface->skw, iface->lmac_id); + else + skw_edma_inc_refill((void *)iface->skw, iface->lmac_id); + } + + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + if (status_code == WLAN_STATUS_SUCCESS) { + u8 *ies = mgmt->u.assoc_resp.variable; + int ies_len = len - (ies - (u8 *)mgmt); + + skw_iface_set_wmm_capa(iface, ies, ies_len); + + atomic_set(&ctx->peer->rx_filter, SKW_RX_FILTER_SET); + __skw_peer_ctx_transmit(ctx, true); + + netif_carrier_on(iface->ndev); + + } else { + skw_info("failed, status code: %d\n", status_code); + + iface->sta.report_deauth = false; + skw_set_state(&core->sm, SKW_STATE_NONE); + } + + skw_peer_ctx_unlock(ctx); + + if (core->assoc_req_ie_len) + assoc_req_ie = core->assoc_req_ie; + + if (iface->sta.sme_external) + skw_compat_rx_assoc_resp(iface->ndev, core->cbss, buf, len, 0, + assoc_req_ie, core->assoc_req_ie_len); + else + skw_mlme_sta_rx_assoc(iface, NULL, buf, len, assoc_req_ie, + core->assoc_req_ie_len); + + core->cbss = NULL; + + return 0; +} + +static int skw_sta_rx_mgmt(struct skw_core *skw, struct skw_iface *iface, + u16 fc, int freq, int signal, void *buf, int len) +{ + u16 seq_ctrl; + int ret = 0; + struct ieee80211_mgmt *mgmt = buf; + struct wiphy *wiphy = priv_to_wiphy(skw); + + skw_dump_frame((u8 *)buf, len); + seq_ctrl = le16_to_cpu(mgmt->seq_ctrl); + if (ieee80211_has_retry(mgmt->frame_control) && + iface->sta.last_seq_ctrl == seq_ctrl) { + skw_dbg("drop retry frame (seq: %d)\n", seq_ctrl); + + return 0; + } + + iface->sta.last_seq_ctrl = seq_ctrl; + + skw_wdev_lock(&iface->wdev); + + switch (fc) { + case IEEE80211_STYPE_DISASSOC: + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_DEAUTH); + + skw_fallthrough; + + case IEEE80211_STYPE_DEAUTH: + ret = skw_sta_rx_deauth(wiphy, iface, buf, len); + break; + + case IEEE80211_STYPE_AUTH: + ret = skw_sta_rx_auth(wiphy, iface, freq, signal, buf, len); + break; + + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + ret = skw_sta_rx_assoc(iface, freq, signal, buf, len); + break; + + default: + skw_compat_cfg80211_rx_mgmt(&iface->wdev, freq, signal, buf, + len, 0, GFP_ATOMIC); + break; + } + + skw_wdev_unlock(&iface->wdev); + + return ret; +} + +static void skw_ibss_add_sta(struct skw_iface *iface, void *frame, + int frame_len) +{ + int ret; + struct station_parameters params; + struct ieee80211_mgmt *mgmt = frame; + + if (!ether_addr_equal(mgmt->bssid, iface->ibss.bssid)) + return; + + if (skw_peer_ctx(iface, mgmt->sa)) + return; + + memset(¶ms, 0x0, sizeof(params)); + ret = skw_add_station(iface->wdev.wiphy, iface->ndev, + mgmt->sa, ¶ms); + if (ret < 0) + return; + + params.sta_flags_set |= BIT(NL80211_STA_FLAG_ASSOCIATED); + skw_change_station(iface->wdev.wiphy, iface->ndev, + mgmt->sa, ¶ms); +} + +static void skw_ibss_del_sta(struct skw_iface *iface, void *frame, + int frame_len) +{ + struct skw_peer_ctx *ctx; + struct ieee80211_mgmt *mgmt = frame; + u16 reason = le16_to_cpu(mgmt->u.deauth.reason_code); + + skw_dbg("iface: %d, bssid: %pM, sa: %pM, da: %pM, reason: %d\n", + iface->id, mgmt->bssid, mgmt->sa, mgmt->bssid, reason); + + ctx = skw_peer_ctx(iface, mgmt->sa); + if (!ctx) + return; + + skw_peer_ctx_transmit(ctx, false); + skw_peer_ctx_bind(iface, ctx, NULL); +} + +static void skw_ibss_rx_mgmt(struct skw_iface *iface, void *frame, + int frame_len) +{ + u16 fc; + struct ieee80211_mgmt *mgmt = frame; + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return; + } + + fc = SKW_MGMT_SFC(mgmt->frame_control); + switch (fc) { + case IEEE80211_STYPE_BEACON: + case IEEE80211_STYPE_PROBE_RESP: + skw_ibss_add_sta(iface, frame, frame_len); + break; + + case IEEE80211_STYPE_DEAUTH: + skw_ibss_del_sta(iface, frame, frame_len); + break; + + default: + break; + } +} + +static bool skw_sta_access_allowed(struct skw_iface *iface, u8 *mac) +{ + int idx; + struct skw_peer_ctx *ctx; + int nr_allowed = iface->sap.max_sta_allowed; + int bitmap = atomic_read(&iface->peer_map); + + while (bitmap && nr_allowed) { + idx = ffs(bitmap) - 1; + SKW_CLEAR(bitmap, BIT(idx)); + + ctx = &iface->skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; + + mutex_lock(&ctx->lock); + + if (ctx->peer && ether_addr_equal(ctx->peer->addr, mac)) { + mutex_unlock(&ctx->lock); + break; + } + + mutex_unlock(&ctx->lock); + + nr_allowed--; + } + + return (nr_allowed && skw_acl_allowed(iface, mac)); +} + +static int skw_sap_rx_mgmt(struct skw_core *skw, struct skw_iface *iface, + u16 fc, int freq, int signal, void *buf, int len) +{ + int ret; + struct skw_peer_ctx *ctx; + bool force_deauth = false; + struct ieee80211_mgmt *mgmt = buf; + + if (fc == IEEE80211_STYPE_AUTH) { + if (!skw_sta_access_allowed(iface, mgmt->sa)) { + skw_info("deny: sta: %pM\n", mgmt->sa); + + skw_cmd_del_sta(priv_to_wiphy(skw), iface->ndev, + mgmt->sa, + 12, /* Deauthentication */ + 5, /* WLAN_REASON_DISASSOC_AP_BUSY */ + true); + return 0; + } + + ctx = skw_peer_ctx(iface, mgmt->sa); + if (ctx) { + skw_peer_ctx_lock(ctx); + + if (ctx->peer && ctx->peer->sm.state >= SKW_STATE_ASSOCED) { + ctx->peer->flags |= SKW_PEER_FLAG_DEAUTHED; + force_deauth = true; + } + + skw_peer_ctx_unlock(ctx); + } + + } else if (fc == IEEE80211_STYPE_DISASSOC) { + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_DEAUTH); + } + + if (iface->sap.sme_external) { + if (force_deauth) { + struct ieee80211_mgmt reply; + + skw_info("force deauth with: %pM\n", mgmt->sa); + + reply.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_DEAUTH); + reply.duration = 0; + reply.seq_ctrl = 0; + skw_ether_copy(reply.da, mgmt->da); + skw_ether_copy(reply.sa, mgmt->sa); + skw_ether_copy(reply.bssid, mgmt->bssid); + + reply.u.deauth.reason_code = cpu_to_le16(3); // WLAN_REASON_DEAUTH_LEAVING + + ret = !skw_compat_cfg80211_rx_mgmt(&iface->wdev, freq, + signal, (const u8 *)&reply, + SKW_DEAUTH_FRAME_LEN, 0, GFP_ATOMIC); + if (ret) + skw_warn("deauth with %pM failed\n", mgmt->sa); + } + + ret = !skw_compat_cfg80211_rx_mgmt(&iface->wdev, freq, signal, + buf, len, 0, GFP_ATOMIC); + } else { + ret = skw_mlme_ap_rx_mgmt(iface, fc, freq, signal, buf, len); + } + + if (ret) + skw_warn("frame %s rx failed\n", skw_mgmt_name(fc)); + + return ret; +} + +static int skw_event_rx_mgmt(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + u16 fc; + int freq, signal; + struct skw_peer_ctx *ctx; + struct skw_mgmt_hdr *hdr = buf; + + if (!iface || !hdr) { + skw_err("iface: 0x%p, buf: 0x%p\n", iface, hdr); + return -EINVAL; + } + skw_dump_frame((u8 *)hdr->mgmt, hdr->mgmt_len); + freq = ieee80211_channel_to_frequency(hdr->chan, to_nl80211_band(hdr->band)); + signal = DBM_TO_MBM(hdr->signal); + fc = SKW_MGMT_SFC(hdr->mgmt->frame_control); + + skw_dbg("%s(inst: %d), sa: %pM, chn: %d, band: %d, signal: %d\n", + skw_mgmt_name(fc), iface->id, hdr->mgmt->sa, hdr->chan, + hdr->band, signal); + + skw_hex_dump("mgmt rx", buf, len, false); + + if (fc == IEEE80211_STYPE_DEAUTH || fc == IEEE80211_STYPE_DISASSOC) { + skw_info("iface: %d, sa: %pM, da: %pM, %s(reason: %d)\n", + iface->id, hdr->mgmt->sa, hdr->mgmt->da, + skw_mgmt_name(fc), hdr->mgmt->u.deauth.reason_code); + + if (time_before(cb->rx_time, iface->sta.core.auth_start)) { + skw_dbg("drop deauth frame:%ld before auth:%ld\n", + cb->rx_time, iface->sta.core.auth_start); + return 0; + } + + ctx = skw_peer_ctx(iface, hdr->mgmt->sa); + if (ctx) { + skw_peer_ctx_lock(ctx); + + if (ctx->peer) + SKW_SET(ctx->peer->flags, + SKW_PEER_FLAG_DEAUTHED); + + skw_peer_ctx_unlock(ctx); + } + } + + switch (iface->wdev.iftype) { + case NL80211_IFTYPE_STATION: + if (iface->flags & SKW_IFACE_FLAG_LEGACY_P2P_DEV) { + skw_compat_cfg80211_rx_mgmt(&iface->wdev, freq, signal, + (void *)hdr->mgmt, hdr->mgmt_len, + 0, GFP_ATOMIC); + + break; + } + skw_fallthrough; + case NL80211_IFTYPE_P2P_CLIENT: + skw_sta_rx_mgmt(skw, iface, fc, freq, signal, hdr->mgmt, + hdr->mgmt_len); + break; + + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + skw_sap_rx_mgmt(skw, iface, fc, freq, signal, hdr->mgmt, + hdr->mgmt_len); + break; + + case NL80211_IFTYPE_ADHOC: + skw_ibss_rx_mgmt(iface, hdr->mgmt, hdr->mgmt_len); + break; + + default: + skw_compat_cfg80211_rx_mgmt(&iface->wdev, freq, signal, + (void *)hdr->mgmt, hdr->mgmt_len, + 0, GFP_ATOMIC); + break; + } + + + return 0; +} + +static int skw_event_acs_report(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + struct skw_survey_info *sinfo = NULL; + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + sinfo = SKW_ZALLOC(sizeof(*sinfo), GFP_KERNEL); + if (!sinfo) + return -ENOMEM; + + INIT_LIST_HEAD(&sinfo->list); + memcpy(&sinfo->data, buf, sizeof(struct skw_survey_data)); + + list_add(&sinfo->list, &iface->survey_list); + + return 0; +} + +void skw_del_sta_event(struct skw_iface *iface, const u8 *addr, u16 reason) +{ + struct ieee80211_mgmt mgmt; + + if (iface->wdev.iftype == NL80211_IFTYPE_STATION) { + cfg80211_tdls_oper_request(iface->ndev, addr, + NL80211_TDLS_TEARDOWN, + reason, GFP_KERNEL); + + return; + } + + if (iface->sap.sme_external) { + mgmt.duration = 0; + mgmt.seq_ctrl = 0; + memcpy(mgmt.da, iface->addr, ETH_ALEN); + memcpy(mgmt.sa, addr, ETH_ALEN); + memcpy(mgmt.bssid, iface->sap.cfg.bssid, ETH_ALEN); + mgmt.u.deauth.reason_code = cpu_to_le16(reason); + mgmt.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_DISASSOC); + + skw_compat_cfg80211_rx_mgmt(&iface->wdev, + iface->sap.cfg.channel->center_freq, + -5400, (void *)&mgmt, + SKW_DEAUTH_FRAME_LEN, 0, GFP_ATOMIC); + } else { + cfg80211_del_sta(iface->ndev, addr, GFP_KERNEL); + } +} + +static int skw_event_del_sta(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + struct skw_del_sta *del_sta = buf; + struct skw_peer_ctx *ctx = NULL; + + skw_info("mac: %pM, reason: %d\n", del_sta->mac, del_sta->reason_code); + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + ctx = skw_peer_ctx(iface, del_sta->mac); + if (!ctx) { + skw_err("sta: %pM not exist\n", del_sta->mac); + return -EINVAL; + } + + skw_peer_ctx_lock(ctx); + + skw_del_sta_event(iface, del_sta->mac, del_sta->reason_code); + + skw_peer_ctx_unlock(ctx); + + return 0; +} + +static int skw_event_rrm_report(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + return 0; +} + +static int skw_get_bss_channel(struct ieee80211_mgmt *mgmt, int len) +{ + const u8 *tmp; + int chn = -1; + const u8 *ie = mgmt->u.beacon.variable; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + + tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen); + if (tmp && tmp[1] == 1) { + chn = tmp[2]; + } else { + tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen); + if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) { + struct ieee80211_ht_operation *htop = (void *)(tmp + 2); + + chn = htop->primary_chan; + } + } + + return chn; +} + +static int skw_event_scan_report(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + struct timespec64 ts; +#endif + struct cfg80211_bss *bss = NULL; + struct ieee80211_channel *rx_channel = NULL; + struct skw_mgmt_hdr *hdr = buf; + int freq = ieee80211_channel_to_frequency(hdr->chan, + to_nl80211_band(hdr->band)); + s32 signal = DBM_TO_MBM(hdr->signal); + bool is_beacon = ieee80211_is_beacon(hdr->mgmt->frame_control); + + skw_log(SKW_SCAN, "[%s] bssid: %pM, chn: %d, signal: %d, %s\n", + SKW_TAG_SCAN, hdr->mgmt->sa, hdr->chan, hdr->signal, + is_beacon ? "beacon" : "probe resp"); + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + skw_dump_frame((u8 *)hdr->mgmt, hdr->mgmt_len); + + rx_channel = ieee80211_get_channel(iface->wdev.wiphy, freq); + if (!rx_channel) { + skw_err("invalid, freq: %d, channel: %d\n", freq, hdr->chan); + return -EINVAL; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + ts = ktime_to_timespec64(ktime_get_boottime()); + hdr->mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec*1000000) + + ts.tv_nsec / 1000; +#else + hdr->mgmt->u.probe_resp.timestamp = ktime_get_boottime().tv64; + do_div(hdr->mgmt->u.probe_resp.timestamp, 1000); +#endif + + bss = cfg80211_inform_bss_frame(iface->wdev.wiphy, rx_channel, + hdr->mgmt, hdr->mgmt_len, + signal, GFP_KERNEL); + if (unlikely(!bss)) { + int bss_chn = skw_get_bss_channel(hdr->mgmt, hdr->mgmt_len); + + skw_dbg("failed, bssid: %pM, chn: %d, rx chn: %d, flags: %d\n", + hdr->mgmt->bssid, bss_chn, hdr->chan, rx_channel->flags); + + return 0; + } + + skw->nr_scan_results++; + + if (test_bit(SKW_FLAG_MBSSID_PRIV, &skw->flags) && bss) { + + skw_bss_priv(bss)->bssid_index = 0; + skw_bss_priv(bss)->max_bssid_indicator = 0; + + skw_mbssid_data_parser(iface->wdev.wiphy, is_beacon, + rx_channel, signal, hdr->mgmt, hdr->mgmt_len); + } + + cfg80211_put_bss(iface->wdev.wiphy, bss); + + return 0; +} + +int skw_mgmt_tx_status(struct skw_iface *iface, u64 cookie, + const u8 *frame, int frame_len, u16 ack) +{ + // fixme: + // check this tx status is for driver or for apps + switch (iface->wdev.iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_DEVICE: + cfg80211_mgmt_tx_status(&iface->wdev, cookie, + frame, frame_len, + ack, GFP_KERNEL); + + break; + + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + skw_mlme_ap_tx_status(iface, cookie, frame, + frame_len, ack); + break; + + default: + break; + } + + return 0; +} + +static int skw_event_mgmt_tx_status(struct skw_core *skw, + struct skw_iface *iface, void *buf, + int len, struct skw_skb_rxcb *cb) +{ + struct skw_tx_mgmt_status *status = buf; + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + return skw_mgmt_tx_status(iface, status->cookie, &status->mgmt, + status->payload_len, status->ack); +} + + +static int skw_event_ba_action(struct skw_core *skw, struct skw_iface *iface, + void *data, int len, struct skw_skb_rxcb *cb) +{ + struct skw_peer_ctx *ctx; + int ret; + struct skw_ba_action *ba = (struct skw_ba_action *)data; + const u8 *action_str[] = {"ADD_TX_BA", "DEL_TX_BA", + "ADD_RX_BA", "DEL_RX_BA", + "REQ_RX_BA"}; + + skw_dbg("%s, lmac_id :%d peer: %d, tid: %d, status: %d, win start: %d, win size: %d\n", + action_str[ba->action], ba->lmac_id, ba->peer_idx, + ba->tid, ba->status_code, ba->ssn, ba->win_size); + + if (!iface) { + skw_warn("iface is none\n"); + return 0; + } + + if (unlikely(iface->lmac_id != ba->lmac_id)) { + skw_err("diferent lmac id iface lmac id:%d ba lmac id:%d\n", + iface->lmac_id, ba->lmac_id); + } + + if (ba->tid >= SKW_NR_TID || + ba->peer_idx >= SKW_MAX_PEER_SUPPORT) { + skw_warn("iface: 0x%p, peer idx: %d, tid: %d\n", + iface, ba->peer_idx, ba->tid); + + SKW_BUG_ON(1); + + return 0; + } + + ctx = &skw->hw.lmac[ba->lmac_id].peer_ctx[ba->peer_idx]; + + skw_peer_ctx_lock(ctx); + + if (!ctx->peer) + goto unlock; + + switch (ba->action) { + case SKW_ADD_TX_BA: + if (ba->status_code) { + if (++ctx->peer->txba.tx_try[ba->tid] > 5) + ctx->peer->txba.blacklist |= BIT(ba->tid); + + SKW_CLEAR(ctx->peer->txba.bitmap, BIT(ba->tid)); + } + + break; + + case SKW_DEL_TX_BA: + if (ba->tid != SKW_INVALID_ID) { + SKW_CLEAR(ctx->peer->txba.bitmap, BIT(ba->tid)); + ctx->peer->txba.tx_try[ba->tid] = 0; + } else { + memset(&ctx->peer->txba, 0x0, sizeof(ctx->peer->txba)); + } + + break; + + case SKW_REQ_RX_BA: + skw_update_tid_rx(ctx->peer, ba->tid, ba->ssn, ba->win_size); + break; + + case SKW_ADD_RX_BA: + ret = skw_add_tid_rx(ctx->peer, ba->tid, ba->ssn, ba->win_size); + if (ret < 0) + skw_warn("add tid rx fail, ret: %d\n", ret); + else + SKW_SET(ctx->peer->rx_tid_map, BIT(ba->tid)); + + break; + + case SKW_DEL_RX_BA: + skw_del_tid_rx(ctx->peer, ba->tid); + SKW_CLEAR(ctx->peer->rx_tid_map, BIT(ba->tid)); + break; + + default: + WARN_ON(1); + break; + } + +unlock: + skw_peer_ctx_unlock(ctx); + + return 0; +} + +static int skw_event_enter_roc(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + struct skw_enter_roc *roc = buf; + struct ieee80211_channel *chan = NULL; + u32 freq; + + skw_dbg("cookie: %llu chn: %u duration:%u\n", + roc->cookie, roc->chn, roc->duration); + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + freq = ieee80211_channel_to_frequency(roc->chn, to_nl80211_band(roc->band)); + chan = ieee80211_get_channel(iface->wdev.wiphy, freq); + if (unlikely(!chan)) { + skw_err("can't get channel:%d\n", roc->chn); + return -EINVAL; + } + + cfg80211_ready_on_channel(&iface->wdev, roc->cookie, chan, + roc->duration, GFP_ATOMIC); + + return 0; +} + + +static int skw_event_cancel_roc(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + struct skw_cancel_roc *roc = buf; + struct ieee80211_channel *chan = NULL; + u32 freq; + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + skw_dbg("cookie: %llu chn: %u band: %u\n", + roc->cookie, roc->chn, roc->band); + + freq = ieee80211_channel_to_frequency(roc->chn, to_nl80211_band(roc->band)); + chan = ieee80211_get_channel(iface->wdev.wiphy, freq); + if (unlikely(!chan)) { + skw_err("can't get channel:%d\n", roc->chn); + return -EINVAL; + } + + cfg80211_remain_on_channel_expired(&iface->wdev, roc->cookie, + chan, GFP_KERNEL); + + return 0; +} + +static int skw_event_tdls(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + unsigned int length = 0; + struct sk_buff *skb = NULL; + struct net_device *ndev = NULL; + int ret = 0; + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + length = (unsigned int) len; + skb = dev_alloc_skb(length); + if (!skb) + return -ENOMEM; + + skb_push(skb, length); + memcpy(skb->data, buf, length); + ndev = iface->ndev; + + skb->dev = ndev; + skb->protocol = eth_type_trans(skb, ndev); + + if (!(ndev->flags & IFF_UP)) { + dev_kfree_skb(skb); + return -ENETDOWN; + } + + ret = netif_receive_skb(skb); + if (ret == NET_RX_SUCCESS) + ndev->stats.rx_packets++; + else + ndev->stats.rx_dropped++; + + return 0; +} + +#if 0 +static int skw_event_credit_update(struct skw_core *skw, + struct skw_iface *iface, void *cred, int len) +{ + if (!cred && len != sizeof(u16)) + return -EINVAL; + + skw_add_credit(skw, 0, *(u16 *)cred); + + return 0; +} +#endif + +static int skw_event_mic_failure(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + struct skw_mic_failure *mic_failure = buf; + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + cfg80211_michael_mic_failure(iface->ndev, mic_failure->mac, + (mic_failure->is_mcbc ? NL80211_KEYTYPE_GROUP : + NL80211_KEYTYPE_PAIRWISE), mic_failure->key_id, + NULL, GFP_KERNEL); + + return 0; +} + +static int skw_event_thermal_warn(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ +#define SKW_FW_THERMAL_TRIP 0 + + u8 event = *(u8 *)buf; + struct skw_iface *tmp_iface = NULL; + int i; + + /* + * 0: stop transmit + * 1: resume transmit + */ + + skw_warn("active: %u\n", !event); + + if (event == SKW_FW_THERMAL_TRIP) + set_bit(SKW_FLAG_FW_THERMAL, &skw->flags); + else + clear_bit(SKW_FLAG_FW_THERMAL, &skw->flags); + + for (i = 0; i < SKW_NR_IFACE; i++) { + tmp_iface = skw->vif.iface[i]; + if (!tmp_iface) + continue; + + if (tmp_iface->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE) + continue; + + if (event == SKW_FW_THERMAL_TRIP) + netif_tx_stop_all_queues(tmp_iface->ndev); + else + netif_tx_start_all_queues(tmp_iface->ndev); + } + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +static int skw_event_rssi_monitor(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + struct skw_rssi_mointor *rssi_mointor = buf; + struct sk_buff *skb = NULL; + + if (!iface || !buf || len != sizeof(struct skw_rssi_mointor)) + return -EINVAL; + + skb = skw_compat_vendor_event_alloc(priv_to_wiphy(skw), + NULL, EXT_VENDOR_EVENT_BUF_SIZE + NLMSG_HDRLEN, + SKW_NL80211_VENDOR_SUBCMD_MONITOR_RSSI, GFP_KERNEL); + + if (!skb) { + skw_err("Alloc skb for rssi monitor event failed\n"); + return -ENOMEM; + } + + if (nla_put_u32(skb, SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, + rssi_mointor->req_id) || + nla_put(skb, SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID, + ETH_ALEN, rssi_mointor->curr_bssid) || + nla_put_s8(skb, SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI, + rssi_mointor->curr_rssi)) { + skw_err("nla put for rssi monitor event failed\n"); + goto fail; + } + + cfg80211_vendor_event(skb, GFP_KERNEL); + +fail: + kfree_skb(skb); + return 0; +} +#endif + + +void skw_cqm_scan_timeout(void *data) +{ + struct skw_iface *iface = data; + + skw_dbg(" enter\n"); + if (unlikely(!iface)) { + skw_warn("iface is NULL\n"); + return; + } + + spin_lock_bh(&iface->sta.roam_data.lock); + iface->sta.roam_data.flags &= ~SKW_IFACE_STA_ROAM_FLAG_CQM_LOW; + spin_unlock_bh(&iface->sta.roam_data.lock); +} + +static int skw_event_cqm(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + struct skw_cqm_info *cqm_info = buf; + + skw_dbg("cqm_status:%d cqm_rssi:%d chan:%d band:%d %pM\n", + cqm_info->cqm_status, cqm_info->cqm_rssi, + cqm_info->chan, cqm_info->band, cqm_info->bssid); + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + switch (cqm_info->cqm_status) { + case CQM_STATUS_RSSI_LOW: + if (iface->sta.sme_external) { + if (is_valid_ether_addr(cqm_info->bssid)) { + spin_lock_bh(&iface->sta.roam_data.lock); + if (!(iface->sta.roam_data.flags & SKW_IFACE_STA_ROAM_FLAG_CQM_LOW)) { + skw_dbg("recv cqm low event bssid:%pM\n", cqm_info->bssid); + memcpy(iface->sta.roam_data.target_bssid, cqm_info->bssid, ETH_ALEN); + iface->sta.roam_data.target_chn = cqm_info->chan; + skw_add_timer_work(skw, "cqm_scan_timeout", skw_cqm_scan_timeout, + iface, SKW_CQM_SCAN_TIMEOUT, + skw_cqm_scan_timeout, GFP_KERNEL); + iface->sta.roam_data.flags |= SKW_IFACE_STA_ROAM_FLAG_CQM_LOW; + } + spin_unlock_bh(&iface->sta.roam_data.lock); + } + + skw_compat_cqm_rssi_notify(iface->ndev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + cqm_info->cqm_rssi, GFP_KERNEL); + + } else { + skw_roam_connect(iface, cqm_info->bssid, cqm_info->chan, + to_nl80211_band(cqm_info->band)); + } + + break; + + case CQM_STATUS_RSSI_HIGH: + if (iface->sta.sme_external) { + skw_compat_cqm_rssi_notify(iface->ndev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + cqm_info->cqm_rssi, GFP_KERNEL); + } + + break; + + case CQM_STATUS_BEACON_LOSS: + if (is_valid_ether_addr(cqm_info->bssid)) { + // FW use beacon loss event to trigger roaming + if (iface->sta.sme_external) { + skw_dbg("beacon loss trigger roaming"); + skw_compat_cqm_rssi_notify(iface->ndev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + cqm_info->cqm_rssi, GFP_KERNEL); + } else + skw_roam_connect(iface, cqm_info->bssid, cqm_info->chan, + to_nl80211_band(cqm_info->band)); + } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + cfg80211_cqm_beacon_loss_notify(iface->ndev, GFP_KERNEL); +#endif + } + + break; + + case CQM_STATUS_TDLS_LOSS: + cfg80211_cqm_pktloss_notify(iface->ndev, cqm_info->bssid, + 0, GFP_KERNEL); + break; + + default: + break; + } + + return 0; +} + +static int skw_event_rx_unprotect_frame(struct skw_core *skw, + struct skw_iface *iface, void *buf, + int len, struct skw_skb_rxcb *cb) +{ + struct ieee80211_hdr *hdr = buf; + + skw_dump_frame((u8 *)buf, len); + + skw_dbg("frame control: %02x, len: %d\n", + SKW_MGMT_SFC(hdr->frame_control), len); + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + if (ieee80211_is_data(hdr->frame_control)) { + struct sk_buff *skb; + struct ethhdr *eth_hdr = NULL; + + skb = dev_alloc_skb(len); + if (!skb) { + skw_warn("alloc skb failed, length: %d\n", len); + return 0; + } + + skw_put_skb_data(skb, buf, len); + + if (ieee80211_data_to_8023(skb, iface->addr, iface->wdev.iftype)) { + skw_warn("build 8023 failed\n"); + + dev_kfree_skb_any(skb); + + return 0; + } + + eth_hdr = (struct ethhdr *)skb->data; + + if (htons(SKW_ETH_P_WAPI) == eth_hdr->h_proto) { + skb->dev = iface->ndev; + skb->protocol = eth_type_trans(skb, iface->ndev); + skb->csum = 0; + skb->ip_summed = CHECKSUM_NONE; + + if (netif_receive_skb(skb) == NET_RX_SUCCESS) { + iface->ndev->stats.rx_packets++; + iface->ndev->stats.rx_bytes += skb->len; + } else { + iface->ndev->stats.rx_dropped++; + } + + } else { + skw_warn("data frame, sa: %pM\n", eth_hdr->h_source); + + dev_kfree_skb_any(skb); + } + } else if (ieee80211_is_mgmt(hdr->frame_control)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_rx_unprot_mlme_mgmt(iface->ndev, buf, len); +#else + if (ieee80211_is_deauth(hdr->frame_control)) + cfg80211_send_unprot_deauth(iface->ndev, buf, len); + else + cfg80211_send_unprot_disassoc(iface->ndev, buf, len); +#endif + } else { + skw_err("Unsupported frames\n"); + return -EINVAL; + } + + return 0; +} + +static int skw_chbw_to_cfg80211_chan_def(struct wiphy *wiphy, + struct cfg80211_chan_def *chdef, + struct skw_event_csa_param *csa) +{ + int freq; + struct ieee80211_channel *chan = NULL; + enum nl80211_band band = to_nl80211_band(csa->band); + + skw_dbg("chn: %d, band: %d, bw: %d, center1: %d, center2: %d, bss type: 0x%x\n", + csa->chan, csa->band, csa->band_width, csa->center_chan1, + csa->center_chan2, csa->bss_type); + + memset(chdef, 0, sizeof(struct cfg80211_chan_def)); + + freq = ieee80211_channel_to_frequency(csa->chan, band); + if (!freq) { + skw_err("invalid channel: %d\n", csa->chan); + return -EINVAL; + } + + chan = ieee80211_get_channel(wiphy, freq); + if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { + skw_err("invalid freq: %d\n", freq); + return -EINVAL; + } + + chdef->chan = chan; + chdef->center_freq1 = ieee80211_channel_to_frequency(csa->center_chan1, band); + chdef->center_freq2 = 0; + + switch (csa->band_width) { + case SKW_CHAN_WIDTH_20: + if (csa->bss_type & SKW_CAPA_HT) + chdef->width = NL80211_CHAN_WIDTH_20; + else + chdef->width = NL80211_CHAN_WIDTH_20_NOHT; + break; + + case SKW_CHAN_WIDTH_40: + chdef->width = NL80211_CHAN_WIDTH_40; + break; + + case SKW_CHAN_WIDTH_80: + chdef->width = NL80211_CHAN_WIDTH_80; + break; + + case SKW_CHAN_WIDTH_80P80: + chdef->width = NL80211_CHAN_WIDTH_80P80; + chdef->center_freq2 = ieee80211_channel_to_frequency(csa->center_chan2, band); + break; + + case SKW_CHAN_WIDTH_160: + chdef->width = NL80211_CHAN_WIDTH_160; + break; + + default: + skw_err("invalid band width: %d\n", csa->band_width); + return -EINVAL; + } + + if (!cfg80211_chandef_valid(chdef)) { + skw_err("chandef invalid\n"); + return -EINVAL; + } + + return 0; +} + +static int skw_event_chan_switch(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + struct skw_event_csa_param *csa_param = buf; + struct cfg80211_chan_def chan_def; + + skw_dbg("mode: %d\n", csa_param->mode); + + if (unlikely(!iface)) { + skw_err("iface invalid\n"); + return -EINVAL; + } + + skw_hex_dump("csa_event", csa_param, sizeof(*csa_param), false); + + if (!skw_chbw_to_cfg80211_chan_def(iface->wdev.wiphy, &chan_def, csa_param)) + skw_dbg("mode:%d, switch to chan: %d\n", csa_param->mode, csa_param->chan); + else { + skw_err("failed convert to cfg80211_chan_def\n"); + return -EINVAL; + } + + if (csa_param->mode == SKW_CSA_START) { + netif_carrier_off(iface->ndev); + + skw_ch_switch_started_notify(iface->ndev, &chan_def, 10, true); + } else { + netif_carrier_on(iface->ndev); + + skw_ch_switch_notify(iface->ndev, &chan_def); + + if (is_skw_sta_mode(iface)) { + iface->sta.core.bss.channel = chan_def.chan; + iface->sta.core.bss.width = csa_param->band_width; + } else { + iface->sap.cfg.channel = chan_def.chan; + iface->sap.cfg.width = csa_param->band_width; + } + } + + return 0; +} + +static int skw_event_tx_frame(struct skw_core *skw, struct skw_iface *iface, + void *buf, int len, struct skw_skb_rxcb *cb) +{ + u16 fc; + u8 *ie; + int ie_len; + struct skw_frame_tx_status *tx = buf; + struct skw_sta_core *core; + const u8 *ht_cap_ie; + + skw_dump_frame((u8 *)buf, (u16)len); + + if (unlikely(!iface)) { + skw_warn("iface invalid\n"); + return -EINVAL; + } + + if (iface->wdev.iftype != NL80211_IFTYPE_STATION && + iface->wdev.iftype != NL80211_IFTYPE_P2P_CLIENT) + return 0; + + skw_hex_dump("tx frame", buf, len, false); + + fc = SKW_MGMT_SFC(tx->mgmt->frame_control); + + skw_dbg("iface: %d, fc: 0x%x, len: %d\n", iface->id, fc, len); + + if (fc == IEEE80211_STYPE_ASSOC_REQ || + fc == IEEE80211_STYPE_REASSOC_REQ) { + if (fc == IEEE80211_STYPE_ASSOC_REQ) { + ie = tx->mgmt->u.assoc_req.variable; + ie_len = tx->mgmt_len - offsetof(struct ieee80211_mgmt, + u.assoc_req.variable); + } else { + ie = tx->mgmt->u.reassoc_req.variable; + ie_len = tx->mgmt_len - offsetof(struct ieee80211_mgmt, + u.reassoc_req.variable); + } + + skw_wdev_lock(&iface->wdev); + core = &iface->sta.core; + + if (ie_len <= SKW_2K_SIZE) { + memcpy(core->assoc_req_ie, ie, ie_len); + core->assoc_req_ie_len = ie_len; + } + + skw_wdev_unlock(&iface->wdev); + + if (is_skw_sta_mode(iface)) { + ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, core->assoc_req_ie, core->assoc_req_ie_len); + + if (ht_cap_ie) + skw_dbg("ht_cap: %2x %2x %2x %2x\n", ht_cap_ie[0], ht_cap_ie[1], ht_cap_ie[2], ht_cap_ie[3]); + if (ht_cap_ie && (ht_cap_ie[2] & 0x2)) + iface->sta.core.bss.ht_cap_chwidth = SKW_CHAN_WIDTH_40; + else if (ht_cap_ie && ht_cap_ie[2]) + iface->sta.core.bss.ht_cap_chwidth = SKW_CHAN_WIDTH_20; + } + } + + return 0; +} + +static int skw_event_dpd_coeff_result(struct skw_core *skw, + struct skw_iface *iface, void *buf, int len, struct skw_skb_rxcb *cb) +{ + return 0; + +} + +static int skw_event_dpd_gear_cmpl(struct skw_core *skw, + struct skw_iface *iface, void *buf, int len, struct skw_skb_rxcb *cb) +{ + return 0; +} + +static int skw_event_fw_recovery(struct skw_core *skw, + struct skw_iface *iface, void *buf, int len, struct skw_skb_rxcb *cb) +{ + u8 done = *(u8 *)buf; + + skw_dbg("done: %d\n", done); + + /* Frimware start recovery */ + if (done) + clear_bit(SKW_FLAG_FW_MAC_RECOVERY, &skw->flags); + else + set_bit(SKW_FLAG_FW_MAC_RECOVERY, &skw->flags); + + return 0; +} + +static int skw_event_mp_mode_handler(struct skw_core *skw, + struct skw_iface *iface, void *buf, int len, struct skw_skb_rxcb *cb) +{ + u8 state = *(u8 *)buf; + + skw_dbg("state: %d\n", state); + + if (state) { + set_bit(SKW_FLAG_MP_MODE, &skw->flags); + skw_abort_cmd(skw); + + } else { + clear_bit(SKW_FLAG_MP_MODE, &skw->flags); + } + + return 0; +} + +static int skw_event_dfs_radar_pulse(struct skw_core *skw, + struct skw_iface *iface, void *buff, int len, struct skw_skb_rxcb *cb) +{ + int i; + struct skw_pulse_info info; + struct skw_radar_pulse *radar = buff; + +#define SKW_RADAR_RSSI(r) ((r) < 0x10 ? (r) - 60 : (r) - 92) +#define SKW_RADAR_TS(t) ((jiffies_to_usecs(jiffies) & (~0xffffff)) | t) + + if (!skw->dfs.info || !skw->dfs.fw_enabled || !skw->dfs.flags) + return 0; + + for (i = 0; i < radar->nr_pulse; i++) { + info.chirp = radar->data[i].chirp; + info.width = radar->data[i].width >> 1; + info.rssi = SKW_RADAR_RSSI(radar->data[i].rssi); + info.ts = SKW_RADAR_TS(radar->data[i].ts); + + skw_log(SKW_DFS, "[SKWIFI DFS] %s: [%d] inst: %d, width: %d, rssi: %d, chirp: %d, ts: 0x%llx\n", + __func__, i, iface->id, info.width, info.rssi, info.chirp, info.ts); + + if (!info.width) + continue; + + skw_dfs_add_pulse(priv_to_wiphy(skw), iface->ndev, &info); + } + + return 0; +} + +static int skw_event_dpd_result_handler(struct skw_core *skw, + struct skw_iface *iface, void *buf, int len, struct skw_skb_rxcb *cb) +{ + skw_dpd_result_handler(skw, buf, len); + + return 0; +} + +static int skw_local_ap_auth_timeout(struct skw_core *skw, + struct skw_iface *iface, void *buf, + int len, struct skw_skb_rxcb *cb) +{ + struct skw_client *client = buf; + + skw_warn("client: %pM\n", client->addr); + + skw_mlme_ap_del_sta(priv_to_wiphy(skw), iface->ndev, + client->addr, false); + + return 0; +} + +static int skw_local_ibss_connect(struct skw_core *skw, + struct skw_iface *iface, void *buf, + int len, struct skw_skb_rxcb *cb) +{ + u16 chn; + int ret = 0; + struct skw_ibss_params params; + + memcpy(params.ssid, iface->ibss.ssid, iface->ibss.ssid_len); + params.ssid_len = iface->ibss.ssid_len; + + memcpy(params.bssid, iface->ibss.bssid, ETH_ALEN); + + params.type = 0; + params.chan = iface->ibss.channel; + params.band = iface->ibss.band; + params.bw = iface->ibss.bw; + params.beacon_int = iface->ibss.beacon_int; + + chn = skw_freq_to_chn(iface->ibss.center_freq1); + params.center_chan1 = chn; + + chn = skw_freq_to_chn(iface->ibss.center_freq2); + params.center_chan2 = chn; + + ret = skw_send_msg(iface->wdev.wiphy, iface->ndev, SKW_CMD_IBSS_JOIN, + ¶ms, sizeof(params), NULL, 0); + if (!ret) { + netif_carrier_on(iface->ndev); + + cfg80211_ibss_joined(iface->ndev, iface->ibss.bssid, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + iface->ibss.chandef.chan, +#endif + GFP_KERNEL); + } else { + skw_err("failed, ret: %d, ssid: %s, bssid: %pM\n", + ret, iface->ibss.ssid, iface->ibss.bssid); + } + + return ret; +} + +static int skw_local_sta_connect(struct skw_core *skw, + struct skw_iface *iface, void *buf, + int len, struct skw_skb_rxcb *cb) +{ + int ret = 0; + struct cfg80211_bss *bss; + struct wiphy *wiphy = iface->wdev.wiphy; + struct skw_connect_param *conn = iface->sta.conn; + + if (!iface->sta.conn) + return 0; + + bss = cfg80211_get_bss(wiphy, conn->channel, conn->bssid, + conn->ssid, conn->ssid_len, + SKW_BSS_TYPE_ESS, SKW_PRIVACY_ESS_ANY); + + if (conn->auth_type == NL80211_AUTHTYPE_SAE) + ret = skw_connect_sae_auth(wiphy, iface->ndev, bss); + else + ret = skw_connect_auth(wiphy, iface->ndev, conn, bss); + + cfg80211_put_bss(wiphy, bss); + + return ret; +} + +#define FUNC_INIT(e, f) \ + [e] = { \ + .id = e, \ + .name = #e, \ + .func = f \ + } + +static const struct skw_event_func g_event_fn[] = { + FUNC_INIT(SKW_EVENT_NORMAL_SCAN_CMPL, skw_event_scan_complete), + FUNC_INIT(SKW_EVENT_SCHED_SCAN_CMPL, skw_event_sched_scan_done), + FUNC_INIT(SKW_EVENT_DISCONNECT, skw_event_disconnect), + FUNC_INIT(SKW_EVNET_RX_MGMT, skw_event_rx_mgmt), + FUNC_INIT(SKW_EVENT_ACS_REPORT, skw_event_acs_report), + FUNC_INIT(SKW_EVENT_DEL_STA, skw_event_del_sta), + FUNC_INIT(SKW_EVENT_RRM_REPORT, skw_event_rrm_report), + FUNC_INIT(SKW_EVENT_SCAN_REPORT, skw_event_scan_report), + FUNC_INIT(SKW_EVENT_MGMT_TX_STATUS, skw_event_mgmt_tx_status), + FUNC_INIT(SKW_EVENT_BA_ACTION, skw_event_ba_action), + FUNC_INIT(SKW_EVENT_ENTER_ROC, skw_event_enter_roc), + FUNC_INIT(SKW_EVENT_CANCEL_ROC, skw_event_cancel_roc), + FUNC_INIT(SKW_EVENT_TDLS, skw_event_tdls), + // FUNC_INIT(SKW_EVENT_CREDIT_UPDATE, skw_event_credit_update), + FUNC_INIT(SKW_EVENT_MIC_FAILURE, skw_event_mic_failure), + FUNC_INIT(SKW_EVENT_THERMAL_WARN, skw_event_thermal_warn), +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + FUNC_INIT(SKW_EVENT_RSSI_MONITOR, skw_event_rssi_monitor), +#endif + FUNC_INIT(SKW_EVENT_CQM, skw_event_cqm), + FUNC_INIT(SKW_EVENT_RX_UNPROTECT_FRAME, skw_event_rx_unprotect_frame), + FUNC_INIT(SKW_EVENT_CHAN_SWITCH, skw_event_chan_switch), + FUNC_INIT(SKW_EVENT_TX_FRAME, skw_event_tx_frame), + FUNC_INIT(SKW_EVENT_DPD_ILC_COEFF_REPORT, skw_event_dpd_coeff_result), + FUNC_INIT(SKW_EVENT_DPD_ILC_GEAR_CMPL, skw_event_dpd_gear_cmpl), + FUNC_INIT(SKW_EVENT_FW_RECOVERY, skw_event_fw_recovery), + FUNC_INIT(SKW_EVENT_NPI_MP_MODE, skw_event_mp_mode_handler), + FUNC_INIT(SKW_EVENT_RADAR_PULSE, skw_event_dfs_radar_pulse), + FUNC_INIT(SKW_EVENT_DPD_RESULT, skw_event_dpd_result_handler), + FUNC_INIT(SKW_EVENT_MAX, NULL), +}; + +static const struct skw_event_func g_local_event_fn[] = { + // FUNC_INIT(SKW_EVENT_LOCAL_STA_AUTH_ASSOC_TIMEOUT, skw_local_sta_auth_assoc_timeout), + FUNC_INIT(SKW_EVENT_LOCAL_AP_AUTH_TIMEOUT, skw_local_ap_auth_timeout), + FUNC_INIT(SKW_EVENT_LOCAL_STA_CONNECT, skw_local_sta_connect), + FUNC_INIT(SKW_EVENT_LOCAL_IBSS_CONNECT, skw_local_ibss_connect), + FUNC_INIT(SKW_EVENT_LOCAL_MAX, NULL), +}; + +#undef FUNC_INIT + +static inline void skw_cmd_lock(struct skw_core *skw, unsigned long flags) +{ + down(&skw->cmd.lock); + + if (test_bit(SKW_CMD_FLAG_NO_WAKELOCK, &flags) || + test_bit(SKW_FLAG_SUSPEND_PREPARE, &skw->flags)) + return; + + __pm_stay_awake(skw->cmd.ws); +} + +static inline void skw_cmd_unlock(struct skw_core *skw, unsigned long flags) +{ + if (!(flags & BIT(SKW_CMD_FLAG_NO_WAKELOCK))) + __pm_relax(skw->cmd.ws); + + up(&skw->cmd.lock); +} + +static bool skw_cmd_allowed(struct skw_core *skw, int inst, int cmd, unsigned long extra_flags) +{ + struct skw_iface *iface; + unsigned long flags = READ_ONCE(skw->flags); + bool ret; + + if (inst < 0) { + skw_warn("invalid inst: %d\n", inst); + SKW_BUG_ON(1); + + return false; + } + + if (extra_flags & BIT(SKW_CMD_FLAG_IGNORE_BLOCK_TX)) + clear_bit(SKW_FLAG_BLOCK_TX, &flags); + + if (!skw_cmd_tx_allowed(flags)) { + skw_warn("skw->flags: 0x%lx, extra_flags: 0x%lx\n", + skw->flags, extra_flags); + + return false; + } + + iface = to_skw_iface(skw, inst); + if (iface && iface->ndev && + iface->ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_MONITOR) { + if (cmd == SKW_CMD_SET_MONITOR_PARAM || + cmd == SKW_CMD_CLOSE_DEV || + cmd == SKW_CMD_OPEN_DEV) + return true; + + skw_warn("monitor cmd not allowed: %d\n", cmd); + return false; + } + + if (cmd == SKW_CMD_GET_INFO || + cmd == SKW_CMD_SYN_VERSION || + cmd == SKW_CMD_OPEN_DEV || + cmd == SKW_CMD_PHY_BB_CFG || + cmd == SKW_CMD_SET_REGD || + cmd == SKW_CMD_SET_MIB || + cmd == SKW_CMD_DPD_ILC_GEAR_PARAM || + cmd == SKW_CMD_DPD_ILC_MARTIX_PARAM) + return true; + + ret = (likely(iface && (iface->flags & SKW_IFACE_FLAG_OPENED))); + if (ret != true) { + if (iface) + skw_warn("iface flags:%x, cmd:%d\n", iface->flags, cmd); + else + skw_warn("iface NULL, cmd:%d\n", cmd); + } + + return ret; +} + + +static bool skw_cmd_len_in_limit(struct skw_core *skw, int total_len) +{ + return !((skw->hw.bus == SKW_BUS_USB && total_len > SKW_USB_CMD_MAX_LEN) || + (skw->hw.bus == SKW_BUS_SDIO && total_len > SKW_SDIO_CMD_MAX_LEN) || + (skw->hw.bus == SKW_BUS_PCIE && total_len > SKW_PCIE_CMD_MAX_LEN)); +} + +/* data_len not include struct skw_msg */ +bool skw_cmd_data_len_in_limit(struct skw_core *skw, int data_len) +{ + int total_len; + bool ret; + + total_len = data_len + sizeof(struct skw_msg); + + if (skw_need_extra_hdr(skw)) { + if (skw->hw.bus == SKW_BUS_USB) + total_len = total_len + skw->hw.extra.hdr_len; + else + total_len = round_up(total_len + skw->hw.extra.hdr_len, + skw->hw.align); + } + + ret = skw_cmd_len_in_limit(skw, total_len); + + if (!ret) + skw_warn("data_len: %d, total_len: %d\n", data_len, total_len); + + return ret; +} + +static int skw_set_cmd(struct skw_core *skw, int dev_id, int cmd, + void *data, int data_len, void *arg, + int arg_size, char *name, u64 start, + unsigned long extra_flags) +{ + struct skw_msg *msg_hdr; + int total_len, msg_len; + void *pos; + + // lockdep_assert_held(&skw->cmd.lock.lock); + pos = skw->cmd.data; + total_len = msg_len = data_len + sizeof(*msg_hdr); + + if (skw_need_extra_hdr(skw)) { + if (skw->hw.bus == SKW_BUS_USB) + total_len = total_len + skw->hw.extra.hdr_len; + else + total_len = round_up(total_len + skw->hw.extra.hdr_len, + skw->hw.align); + + skw_set_extra_hdr(skw, pos, skw->hw.cmd_port, total_len, 0, 0); + + pos += skw->hw.extra.hdr_len; + } + + if (!skw_cmd_len_in_limit(skw, total_len)) { + skw_warn("total_len: %d\n", total_len); + SKW_BUG_ON(1); + + return -E2BIG; + } + + skw->cmd.id = cmd; + skw->cmd.name = name; + skw->cmd.seq++; + skw->cmd.start_time = jiffies; + skw->cmd.arg = arg; + skw->cmd.arg_size = arg_size; + skw->cmd.status = 0; + skw->cmd.data_len = total_len; + WRITE_ONCE(skw->cmd.flags, extra_flags); + + msg_hdr = pos; + msg_hdr->inst_id = dev_id; + msg_hdr->type = SKW_MSG_CMD; + msg_hdr->id = cmd; + msg_hdr->total_len = msg_len; + msg_hdr->seq = skw->cmd.seq; + + pos += sizeof(*msg_hdr); + if (data_len) + memcpy(pos, data, data_len); + + skw->dbg.cmd_idx = (skw->dbg.cmd_idx + 1) % skw->dbg.nr_cmd; + skw->dbg.cmd[skw->dbg.cmd_idx].trigger = start; + skw->dbg.cmd[skw->dbg.cmd_idx].build = skw_local_clock(); + skw->dbg.cmd[skw->dbg.cmd_idx].id = cmd; + skw->dbg.cmd[skw->dbg.cmd_idx].seq = skw->cmd.seq; + skw->dbg.cmd[skw->dbg.cmd_idx].flags = skw->cmd.flags; + skw->dbg.cmd[skw->dbg.cmd_idx].xmit = 0; + skw->dbg.cmd[skw->dbg.cmd_idx].ack = 0; + skw->dbg.cmd[skw->dbg.cmd_idx].assert = 0; + skw->dbg.cmd[skw->dbg.cmd_idx].loop = 0; + atomic_set(&skw->dbg.loop, 0); + + skw_log(SKW_CMD, "[%s] TX %s[%d], iface: %d, seq: %d, flags: 0x%lx,len = %d\n", + SKW_TAG_CMD, name, cmd, dev_id, skw->cmd.seq, skw->cmd.flags, data_len); + skw_hex_dump("cmd data", skw->cmd.data, skw->cmd.data_len, false); + + return 0; +} + +static void skw_cmd_timeout_fn(void *data) +{ + struct skw_core *skw = data; + + skw_err("<%s> %s[%d], seq: %d, flags: 0x%lx, timeout:%d(ms)\n", + skw_bus_name(skw->hw.bus), skw->cmd.name, skw->cmd.id, + skw->cmd.seq, skw->flags, jiffies_to_msecs(SKW_CMD_TIMEOUT)); + + set_bit(SKW_FLAG_BLOCK_TX, &skw->flags); + skw->dbg.cmd[skw->dbg.cmd_idx].assert = skw_local_clock(); + + if (!skw->dbg.cmd[skw->dbg.cmd_idx].loop) + skw->dbg.cmd[skw->dbg.cmd_idx].loop = atomic_read(&skw->dbg.loop); + + skw_cmd_unlock(skw, 0); + + skw_dbg_dump(skw); + skw_assert_schedule(priv_to_wiphy(skw)); +} + +static void skw_msg_try_send_cb(struct skw_core *skw) +{ + skw_unlock_schedule(priv_to_wiphy(skw)); + // skw_del_timer_work(skw, skw->cmd.data); + // skw_cmd_unlock(skw); +} + +int skw_msg_try_send(struct skw_core *skw, int inst, int cmd, void *data, + int data_len, void *arg, int arg_size, char *name) +{ + int ret; + + if (down_trylock(&skw->cmd.lock)) + return -EBUSY; + + __pm_stay_awake(skw->cmd.ws); + + if (!skw_cmd_allowed(skw, inst, cmd, 0)) { + skw_cmd_unlock(skw, 0); + return -EIO; + } + + ret = skw_set_cmd(skw, inst, cmd, data, data_len, + arg, arg_size, name, + skw_local_clock(), 0); + if (ret) { + skw_cmd_unlock(skw, 0); + return ret; + } + + skw_add_timer_work(skw, name, skw_cmd_timeout_fn, skw, SKW_CMD_TIMEOUT, + skw->cmd.data, GFP_ATOMIC); + + skw->cmd.callback = skw_msg_try_send_cb; + + set_bit(SKW_CMD_FLAG_XMIT, &skw->cmd.flags); + skw_wakeup_tx(skw, 0); + + return 0; +} + +static void skw_msg_xmit_timeout_cb(struct skw_core *skw) +{ + set_bit(SKW_CMD_FLAG_DONE, &skw->cmd.flags); + + smp_mb(); + + wake_up(&skw->cmd.wq); +} + +/* SDIO BUS + * +--------------------- MSG_HDR->total_len ---------------------+ + * | | + * +-----------+----------+------------+------------+-----------+-------------+ + * | EXTRA_HDR | MSG_HDR | IE_OFFSET | PARAM ... | IE ... | OTHERS ... | + * +-----------+----------+------------+------------+-----------+-------------+ + * | | + * +-------- IE_OFFSET ------+ + */ +static int skw_cmd_xmit_timeout(struct wiphy *wiphy, int dev_id, int cmd, + void *buf, int buf_len, void *arg, int arg_size, + char *name, unsigned long timeout, u64 start, + unsigned long extra_flags) +{ + int ret; + struct skw_core *skw = wiphy_priv(wiphy); + + ret = skw_set_cmd(skw, dev_id, cmd, buf, buf_len, + arg, arg_size, name, start, extra_flags); + if (ret) + return ret; + + skw->cmd.callback = skw_msg_xmit_timeout_cb; + set_bit(SKW_CMD_FLAG_XMIT, &skw->cmd.flags); + + skw_wakeup_tx(skw, 0); + + ret = wait_event_timeout(skw->cmd.wq, + test_bit(SKW_CMD_FLAG_DONE, &skw->cmd.flags), + msecs_to_jiffies(100)); + if (ret) + goto out; + + if (test_bit(SKW_CMD_FLAG_XMIT, &skw->cmd.flags)) + skw_wakeup_tx(skw, 0); + + skw_dbg("skw_hw_request_ack, cmd: %s(%d)\n", name, cmd); + skw_hw_request_ack(skw); + + ret = wait_event_timeout(skw->cmd.wq, + test_bit(SKW_CMD_FLAG_DONE, &skw->cmd.flags), timeout); + if (unlikely(!ret)) { + skw_err("<%s> %s[%d], seq: %d, flags: 0x%lx, timeout:%d(ms)\n", + skw_bus_name(skw->hw.bus), name, cmd, skw->cmd.seq, + skw->flags, jiffies_to_msecs(timeout)); + + skw->dbg.cmd[skw->dbg.cmd_idx].assert = skw_local_clock(); + + if (!skw->dbg.cmd[skw->dbg.cmd_idx].loop) + skw->dbg.cmd[skw->dbg.cmd_idx].loop = atomic_read(&skw->dbg.loop); + + set_bit(SKW_CMD_FLAG_ACKED, &skw->cmd.flags); + skw_hw_assert(skw, true); + + return -ETIMEDOUT; + } + +out: + return ret > 0 ? 0 - skw->cmd.status : ret; +} + +int skw_msg_xmit_timeout(struct wiphy *wiphy, int dev_id, int cmd, + void *buf, int buf_len, void *arg, int arg_size, + char *name, unsigned long timeout, + unsigned long extra_flags) +{ + int ret; + struct skw_core *skw = wiphy_priv(wiphy); + + if (!skw_cmd_allowed(skw, dev_id, cmd, extra_flags)) + return -EIO; + + BUG_ON(in_interrupt()); + + skw_cmd_lock(skw, extra_flags); + + ret = skw_cmd_xmit_timeout(wiphy, dev_id, cmd, buf, buf_len, + arg, arg_size, name, timeout, + skw_local_clock(), extra_flags); + + skw_cmd_unlock(skw, extra_flags); + + return ret; +} + +/* + * +--------------+-----------------+------------------+ + * | msg_hdr | status_code | payload | + * +--------------+-----------------+------------------+ + * octets: 8 2 variable + * + */ +int skw_cmd_ack_handler(struct skw_core *skw, void *data, int data_len) +{ + struct skw_msg *msg_ack = data; + + if (msg_ack->id != skw->cmd.id || msg_ack->seq != skw->cmd.seq || + test_and_set_bit(SKW_CMD_FLAG_ACKED, &skw->cmd.flags)) { + skw_err("ack id: %d, ack seq: %d, cmd id: %d, cmd seq: %d, flags: 0x%lx\n", + msg_ack->id, msg_ack->seq, skw->cmd.id, + skw->cmd.seq, skw->cmd.flags); + + return -EINVAL; + } + + skw->cmd.status = msg_ack->data[0]; + skw->dbg.cmd[skw->dbg.cmd_idx].ack = skw_local_clock(); + + skw_log(SKW_CMD, "[%s] RX %s[%d], status = %d, used %d msec\n", + SKW_TAG_CMD, skw->cmd.name, skw->cmd.id, skw->cmd.status, + jiffies_to_msecs(jiffies - skw->cmd.start_time)); + + if (skw->cmd.arg) { + u16 hdr_len = sizeof(struct skw_msg) + sizeof(u16); + u16 len = msg_ack->total_len - hdr_len; + + // WARN_ON(msg_ack->total_len - hdr_len != skw->cmd.arg_size); + if (len != skw->cmd.arg_size) + skw_warn("%s expect len: %d, recv len: %d\n", + skw->cmd.name, skw->cmd.arg_size, len); + + memcpy(skw->cmd.arg, data + hdr_len, + min(data_len - hdr_len, (int)skw->cmd.arg_size)); + } + + skw->cmd.callback(skw); + + return 0; +} + +void skw_event_handler(struct skw_core *skw, struct skw_iface *iface, + struct skw_msg *msg_hdr, void *data, + size_t data_len, struct skw_skb_rxcb *cb) +{ + int inst, max_event_id; + const struct skw_event_func *handler, *func; + + if (msg_hdr->type == SKW_MSG_EVENT_LOCAL) { + inst = iface->id; + func = g_local_event_fn; + max_event_id = SKW_EVENT_LOCAL_MAX; + } else { + inst = 0; + func = g_event_fn; + max_event_id = SKW_EVENT_MAX; + } + + if (msg_hdr->id >= max_event_id) { + skw_err("invalid event id, type: %d, id: %d(max: %d)\n", + msg_hdr->type, msg_hdr->id, max_event_id); + return; + } + + handler = &func[msg_hdr->id]; + if (!handler->func) { + skw_err("function not implement, type: %d, id: %d\n", + msg_hdr->type, msg_hdr->id); + return; + } + + skw_log(SKW_EVENT, "[%s] iface: %d, %s[%d], seq: %d, len: %ld\n", + msg_hdr->type == SKW_MSG_EVENT_LOCAL ? SKW_TAG_LOCAL : SKW_TAG_EVENT, + inst, handler->name, msg_hdr->id, msg_hdr->seq, (long)data_len); + + handler->func(skw, iface, data, data_len, cb); +} + +void skw_default_event_work(struct work_struct *work) +{ + struct skw_core *skw; + struct sk_buff *skb; + struct skw_msg *msg_hdr; + + skw = container_of(work, struct skw_core, event_work.work); + + while ((skb = skb_dequeue(&skw->event_work.qlist))) { + + msg_hdr = (struct skw_msg *)skb->data; + skb_pull(skb, sizeof(struct skw_msg)); + + if (msg_hdr) + skw_event_handler(skw, NULL, msg_hdr, skb->data, + skb->len, SKW_SKB_RXCB(skb)); + + kfree_skb(skb); + } +} + +int skw_queue_local_event(struct wiphy *wiphy, struct skw_iface *iface, + int event_id, void *data, size_t data_len) +{ + int ret; + struct skw_msg msg = {0}; + struct sk_buff *skb; + struct skw_event_work *work; + static u16 local_event_sn; + + skw_dbg("iface: %d, msg: %s, msg len: %ld\n", iface ? iface->id : 0, + g_local_event_fn[event_id].name, (long)data_len); + + msg.total_len = data_len + sizeof(msg); + + skb = netdev_alloc_skb(NULL, msg.total_len); + if (!skb) { + skw_err("alloc skb failed, len: %d\n", msg.total_len); + return -ENOMEM; + } + + msg.inst_id = iface ? iface->id : 0; + msg.type = SKW_MSG_EVENT_LOCAL; + msg.id = event_id; + msg.seq = ++local_event_sn; + + skw_put_skb_data(skb, &msg, sizeof(msg)); + skw_put_skb_data(skb, data, data_len); + + if (iface) { + work = &iface->event_work; + } else { + struct skw_core *skw = wiphy_priv(wiphy); + + work = &skw->event_work; + } + + ret = skw_queue_event_work(wiphy, work, skb); + if (ret < 0) + kfree_skb(skb); + + return ret; +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_msg.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_msg.h new file mode 100755 index 0000000..015dc96 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_msg.h @@ -0,0 +1,721 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_MSG_H__ +#define __SKW_MSG_H__ + +#include <net/cfg80211.h> +#include "skw_core.h" +#include "skw_rx.h" + +#define SKW_USB_CMD_MAX_LEN 1536 +#define SKW_PCIE_CMD_MAX_LEN 1600 +#define SKW_SDIO_CMD_MAX_LEN 1588 +#define SKW_CMD_TIMEOUT 2000 + +#define SKW_TXBA_COUNT_MASK 0x3 +#define SKW_TXBA_DONE BIT(4) +#define SKW_TXBA_WAIT_EVENT BIT(5) +#define SKW_TXBA_DISABLED BIT(6) + +#define SKW_CMD_FLAG_NO_WAKELOCK 0 +#define SKW_CMD_FLAG_NO_ACK 1 +#define SKW_CMD_FLAG_IGNORE_BLOCK_TX 2 +#define SKW_CMD_FLAG_DONE 3 +#define SKW_CMD_FLAG_XMIT 4 +#define SKW_CMD_FLAG_ACKED 5 +#define SKW_CMD_FLAG_DISABLE_IRQ 6 + +#define SKW_WLAN_STATUS_SAE_HASH_TO_ELEMENT 126 + +typedef int (*skw_event_fn)(struct skw_core *, struct skw_iface *, void *, int, struct skw_skb_rxcb *); + +enum SKW_BA_ACTION_CMD { + SKW_ADD_TX_BA, + SKW_DEL_TX_BA, + SKW_ADD_RX_BA, + SKW_DEL_RX_BA, + SKW_REQ_RX_BA, +}; + +enum SKW_TX_BA_STATUS_CODE { + SKW_TX_BA_SETUP_SUCC, + SKW_TX_BA_REFUSED, + SKW_TX_BA_TIMEOUT, + SKW_TX_BA_DEL_EVENT, + SKW_TX_BA_DEL_SUCC, +}; + +struct skw_ba_action { + u8 action; + u8 lmac_id; + u8 peer_idx; + u8 tid; + u8 status_code; + u8 resv[3]; + u16 ssn; + u16 win_size; +} __packed; + +struct skw_event_func { + int id; + char *name; + skw_event_fn func; +}; + +enum SKW_CSA_COMMAND_MODE { + SKW_CSA_DONE, + SKW_CSA_START, +}; + +struct skw_event_csa_param { + u8 mode; + u8 bss_type; /* reference SKW_CAPA_ */ + u8 chan; + u8 center_chan1; + u8 center_chan2; + u8 band_width; + u8 band; + u8 oper_class; /* Only valid for csa start */ + u8 switch_mode; /* Only valid for csa start */ + u8 switch_count; /* Only valid for csa start */ + u8 hw_id; +}; + +enum SKW_CMD_ID { + SKW_CMD_DOWNLOAD_INI = 0, + SKW_CMD_GET_INFO = 1, + SKW_CMD_SYN_VERSION = 2, + SKW_CMD_OPEN_DEV = 3, + SKW_CMD_CLOSE_DEV = 4, + SKW_CMD_START_SCAN = 5, + SKW_CMD_STOP_SCAN = 6, + SKW_CMD_START_SCHED_SCAN = 7, + SKW_CMD_STOP_SCHED_SCAN = 8, + SKW_CMD_JOIN = 9, + SKW_CMD_AUTH = 10, + SKW_CMD_ASSOC = 11, + SKW_CMD_ADD_KEY = 12, + SKW_CMD_DEL_KEY = 13, + SKW_CMD_TX_MGMT = 14, + SKW_CMD_TX_DATA_FRAME = 15, + SKW_CMD_SET_IP = 16, + SKW_CMD_DISCONNECT = 17, + SKW_CMD_RPM_REQ = 18, + SKW_CMD_START_AP = 19, + SKW_CMD_STOP_AP = 20, + SKW_CMD_ADD_STA = 21, + SKW_CMD_DEL_STA = 22, + SKW_CMD_GET_STA = 23, + SKW_CMD_RANDOM_MAC = 24, + SKW_CMD_GET_LLSTAT = 25, + SKW_CMD_SET_MC_ADDR = 26, + SKW_CMD_RESUME = 27, + SKW_CMD_SUSPEND = 28, + SKW_CMD_REMAIN_ON_CHANNEL = 29, + SKW_CMD_BA_ACTION = 30, + SKW_CMD_TDLS_MGMT = 31, + SKW_CMD_TDLS_OPER = 32, + SKW_CMD_TDLS_CHANNEL_SWITCH = 33, + SKW_CMD_SET_CQM_RSSI = 34, + SKW_CMD_NPI_MSG = 35, + SKW_CMD_IBSS_JOIN = 36, + SKW_CMD_SET_IBSS_ATTR = 37, + SKW_CMD_RSSI_MONITOR = 38, + SKW_CMD_SET_IE = 39, + SKW_CMD_SET_MIB = 40, + SKW_CMD_REGISTER_FRAME = 41, + SKW_CMD_ADD_TX_TS = 42, + SKW_CMD_DEL_TX_TS = 43, + SKW_CMD_REQ_CHAN_SWITCH = 44, + SKW_CMD_CHANGE_BEACON = 45, + SKW_CMD_DPD_ILC_GEAR_PARAM = 46, + SKW_CMD_DPD_ILC_MARTIX_PARAM = 47, + SKW_CMD_DPD_ILC_COEFF_PARAM = 48, + SKW_CMD_WIFI_RECOVER = 49, + SKW_CMD_PHY_BB_CFG = 50, + SKW_CMD_SET_REGD = 51, + SKW_CMD_SET_EFUSE = 52, + SKW_CMD_SET_PROBEREQ_FILTER = 53, + SKW_CMD_CFG_ANT = 54, + SKW_CMD_RTT = 55, + SKW_CMD_GSCAN = 56, + SKW_CMD_DFS = 57, + SKW_CMD_SET_SPD_ACTION = 58, + SKW_CMD_SET_DPD_RESULT = 59, + SKW_CMD_SET_MONITOR_PARAM = 60, + SKW_CMD_GET_DEBUG_INFO = 61, + + SKW_CMD_NUM, +}; + +enum SKW_EVENT_ID { + SKW_EVENT_NORMAL_SCAN_CMPL = 0, + SKW_EVENT_SCHED_SCAN_CMPL = 1, + SKW_EVENT_DISCONNECT = 2, + SKW_EVENT_ASOCC = 3, + SKW_EVNET_RX_MGMT = 4, + SKW_EVENT_DEAUTH = 5, + SKW_EVENT_DISASOC = 6, + SKW_EVENT_JOIN_CMPL = 7, + SKW_EVENT_ACS_REPORT = 8, + SKW_EVENT_DEL_STA = 9, + + SKW_EVENT_RRM_REPORT = 10, + SKW_EVENT_SCAN_REPORT = 11, + SKW_EVENT_MGMT_TX_STATUS = 12, + SKW_EVENT_BA_ACTION = 13, + SKW_EVENT_CANCEL_ROC = 14, + SKW_EVENT_TDLS = 15, + SKW_EVENT_CREDIT_UPDATE = 16, + SKW_EVENT_MIC_FAILURE = 17, + SKW_EVENT_THERMAL_WARN = 18, + SKW_EVENT_RSSI_MONITOR = 19, + + SKW_EVENT_CQM = 20, + SKW_EVENT_RX_UNPROTECT_FRAME = 21, + SKW_EVENT_CHAN_SWITCH = 22, + SKW_EVENT_CHN_SCH_DONE = 23, + SKW_EVENT_DOWNLOAD_FW = 24, + SKW_EVENT_TX_FRAME = 25, + SKW_EVENT_NPI_MP_MODE = 26, + SKW_EVENT_DPD_ILC_COEFF_REPORT = 27, + SKW_EVENT_DPD_ILC_GEAR_CMPL = 28, + SKW_EVENT_FW_RECOVERY = 29, + + SKW_EVENT_TDLS_CHAN_SWITCH_RESULT = 30, + SKW_EVENT_THM_FW_STATE = 31, + SKW_EVENT_ENTER_ROC = 32, + SKW_EVENT_RADAR_PULSE = 33, + SKW_EVENT_RTT = 34, + SKW_EVENT_GSCAN = 35, + SKW_EVENT_GSCAN_FRAME = 36, + SKW_EVENT_DPD_RESULT = 37, + + SKW_EVENT_MAX +}; + +enum SKW_EVENT_LOCAL_ID { + SKW_EVENT_LOCAL_STA_AUTH_ASSOC_TIMEOUT, + SKW_EVENT_LOCAL_AP_AUTH_TIMEOUT, + SKW_EVENT_LOCAL_STA_CONNECT, + SKW_EVENT_LOCAL_IBSS_CONNECT, + + SKW_EVENT_LOCAL_MAX +}; + +enum SKW_MSG_TYPE { + SKW_MSG_CMD, + SKW_MSG_CMD_ACK, + SKW_MSG_EVENT, + SKW_MSG_EVENT_LOCAL +}; + +struct skw_msg { + /* for a global message, inst_id should be 0 */ + u8 inst_id:4; + + /* reference SKW_MSG_TYPE */ + u8 type:4; + u8 id; + u16 seq; + u16 total_len; + u8 resv[2]; + u16 data[0]; +}; + +struct skw_mgmt_hdr { + u8 chan; + u8 band; + s16 signal; + u16 mgmt_len; + u16 resv; + struct ieee80211_mgmt mgmt[0]; +} __packed; + +struct skw_tx_mgmt_status { + u64 cookie; + u16 ack; + u16 payload_len; + const u8 mgmt; +} __packed; + +struct skw_enter_roc { + u8 inst; + u8 chn; + u8 band; + u64 cookie; + u32 duration; +} __packed; + +struct skw_cancel_roc { + u8 inst; + u8 chn; + u8 band; + u64 cookie; +} __packed; + +struct skw_mc_list { + u16 count; + struct mac_address mac[]; +} __packed; + +struct skw_discon_event_params { + u16 reason; + u8 bssid[ETH_ALEN]; +} __packed; + +/* + * IEEE 802.2 Link Level Control headers, for use in conjunction with + * 802.{3,4,5} media access control methods. + * + * Headers here do not use bit fields due to shortcommings in many + * compilers. + */ + +struct llc { + u_int8_t llc_dsap; + u_int8_t llc_ssap; + union { + struct { + u_int8_t control; + u_int8_t format_id; + u_int8_t class; + u_int8_t window_x2; + } __packed type_u; + struct { + u_int8_t num_snd_x2; + u_int8_t num_rcv_x2; + } __packed type_i; + struct { + u_int8_t control; + u_int8_t num_rcv_x2; + } __packed type_s; + struct { + u_int8_t control; + /* + * We cannot put the following fields in a structure because + * the structure rounding might cause padding. + */ + u_int8_t frmr_rej_pdu0; + u_int8_t frmr_rej_pdu1; + u_int8_t frmr_control; + u_int8_t frmr_control_ext; + u_int8_t frmr_cause; + } __packed type_frmr; + struct { + u_int8_t control; + u_int8_t org_code[3]; + u_int16_t ether_type; + } __packed type_snap; + struct { + u_int8_t control; + u_int8_t control_ext; + } __packed type_raw; + } llc_un /* XXX __packed ??? */; +} __packed; + +struct skw_frame_tx_status { + u8 status; + u8 resv; + u16 mgmt_len; + struct ieee80211_mgmt mgmt[0]; +} __packed; + + +enum SKW_OFFLOAD_PAT_OP_TYPE { + PAT_OP_TYPE_SAME = 0, + PAT_OP_TYPE_DIFF = 1, + PAT_OP_TYPE_CONTINUE_MATCH = 2, +}; + +enum SKW_OFFLOAD_PAT_OFFSET { + PAT_TYPE_ETH = 0, + PAT_TYPE_IPV4 = 1, + PAT_TYPE_UDP = 2, + PAT_TYPE_TCP = 3, +}; + +struct skw_pkt_pattern { + u8 op; + u8 type_offset; + u16 offset; + u8 len; + u8 val[0]; +} __packed; + +#define SKW_MAX_WOW_USER_RULE_LEN 64 +#define SKW_MAX_WOW_RULE_NUM 64 +#define SKW_MAX_WOW_RULE_NUM_ONE_TIME 20 +#define SKW_WOW_RULE_CMD_BUF_MAX 1400 +struct skw_wow_rule { + u16 len; + u8 rule[64]; +} __packed; + +struct skw_wow_input_param { + u32 wow_flags; + u8 rule_num; + struct skw_wow_rule rules[0]; +} __packed; + +struct skw_wow_rules_set { + u64 en_bitmap; + u32 wow_flags; + u8 rule_num; + struct skw_wow_rule rules[SKW_MAX_WOW_RULE_NUM]; +}; + +struct skw_wow_user_rules { + u64 en_bitmap; + char rules[SKW_MAX_WOW_RULE_NUM][SKW_MAX_WOW_USER_RULE_LEN]; +}; + +enum SKW_SPD_ACTION_SUBCMD { + ACTION_DIS_ALL = 0, + ACTION_DIS_WOW = 1, + ACTION_DIS_KEEPALIVE = 2, + ACTION_EN_WOW = 3, + ACTION_EN_KEEPALIVE = 4, + ACTION_EN_ALWAYS_KEEPALIVE = 5, + ACTION_DIS_ALWAYS_KEEPALIVE = 6, + ACTION_DIS_ALL_KEEPALIVE = 7, + ACTION_EN_KEEPALIVE_APPEND = 8, + ACTION_WOW_KEEPALIVE = 16, + ACTION_EN_WOW_APPEND = 17, +}; + +struct skw_spd_action_param { + u16 sub_cmd; + u16 len; +} __packed; + +struct skw_set_monitor_param { + u8 mode; + u8 chan_num; + u8 bandwidth; + u8 center_chn1; + u8 center_chn2; +} __packed; + +struct skw_rssi_mointor { + u32 req_id; + s8 curr_rssi; + u8 curr_bssid[ETH_ALEN]; +} __packed; + +static inline int skw_cmd_locked(struct semaphore *sem) +{ + unsigned long flags; + int count; + + raw_spin_lock_irqsave(&sem->lock, flags); + count = sem->count - 1; + raw_spin_unlock_irqrestore(&sem->lock, flags); + + return (count < 0); +} + +#define SKW_MAX_INST_NUM 4 +struct skw_inst_nss_info { + u8 valid_id; + u8 inst_rx_nss:4; + u8 inst_tx_nss:4; +} __packed; + +struct skw_mac_nss_info { + u8 mac_nss[SKW_MAX_LMAC_SUPPORT]; + u8 is_dbdc_mode; + u8 max_inst_num; + struct skw_inst_nss_info inst_nss[SKW_MAX_INST_NUM]; +} __packed; + +#define SKW_MAX_W_LEVEL 5 +struct skw_hw_w_debug { + u16 w_total_cnt; + u16 w_cnt[SKW_MAX_W_LEVEL]; + u16 w_ctrl_thresh[SKW_MAX_W_LEVEL - 1]; +} __packed; + +struct skw_debug_info_w { + struct skw_hw_w_debug hw_w[SKW_MAX_LMAC_SUPPORT]; +} __packed; + +struct skw_debug_info_s { + u16 tlv; + u8 val[1]; +} __packed; + +/* TLV: SKW_MIB_GET_HW_TX_INFO_E */ +/* No val data for debug info cmd */ +/** Rsp of this TLV **/ +struct skw_hw_tx_info_rsp { + u16 status; + u16 tlv; + u16 len; + u8 val[0]; +} __packed; + +/*** val[0] start***/ +struct tx_hmac_per_hw_rpt_info { + u32 hmac_tx_stat[7]; +} __packed; + +struct tx_mib_acc_info { + u32 acc_tx_psdu_cnt; + u16 acc_tx_wi_ack_to_cnt; + u16 acc_tx_wi_ack_fail_cnt; + u32 acc_rts_wo_cts_cnt; + u16 acc_tx_post_busy_cnt; + u8 tx_en_perct; +} __packed; + +struct tx_mib_tb_acc_info { + /*from tx reg*/ + u32 rx_basic_trig_cnt; + u32 rx_basic_trig_succ_cnt; + + u32 tb_gen_invoke_cnt; + u32 tb_gen_abort_cnt; + + u32 tb_cs_fail_acc_cnt; + u32 all_tb_pre_tx_cnt; + + u32 tb_tx_acc_cnt; + u32 tb_tx_acc_scucc_cnt; + u32 tb_tx_suc_ratio; + + /*from rx req*/ + u32 rx_trig_reg_cnt; + u32 rx_trig_start_tx_cnt; + u32 rx_trig_suc_ratio; +} __packed; + +/* val definition */ +struct skw_get_hw_tx_info_s { + u32 hif_tx_all_cnt; + u32 hif_rpt_all_credit_cnt; + u32 cur_free_buf_cnt; + u32 pendding_tx_pkt_cnt; + u32 lst_cmd_seq; + u32 lst_evt_seq; + u32 lmac_tx_bc_cnt; + u32 lmac_tx_uc_cnt[5]; + struct tx_hmac_per_hw_rpt_info hmac_tx_stat; + struct tx_mib_acc_info tx_mib_acc_info; + struct tx_mib_tb_acc_info tx_mib_tb_acc_info; +} __packed; +/*** val[0] end***/ + +/* TLV: SKW_MIB_GET_INST_INFO_E */ +/* No val data for debug info cmd */ +/** Rsp of this TLV **/ +struct skw_inst_info_rsp { + u16 status; + u16 tlv; + u16 len; + u8 val[0]; +} __packed; + +/*** val[0] start***/ +struct skw_cs_monitor_rpt { + u8 p20_lst_busy_perctage; + u8 p40_lst_busy_perctage; + u8 p80_lst_busy_perctage; + u8 p20_lst_nav_perctage; + u8 p40_lst_nav_perctage; + u8 p80_lst_nav_perctage; +} __packed; + +#define RPI_MAX_LEVEL 6 +struct skw_rpi_monitor_rpt { + int8_t no_uc_direct_rpi_level; + int8_t uc_direct_rpi_level; + int8_t rx_ba_rssi; + u8 rx_idle_percent_in_rpi; + u8 rx_percent_in_rpi; + /* measure dur*/ + u32 rpi_class_dur[RPI_MAX_LEVEL]; +} __packed; + +#define NOISE_MAX_LEVEL 6 +struct skw_noise_monitor_rpt { + u32 noise_class_dur[RPI_MAX_LEVEL]; +} __packed; + +/*val[0]*/ +struct skw_get_inst_info_s { + u8 inst_mode; + u8 hw_id; + u8 enable; + u8 mac_id; + u32 asoc_peer_map; + u32 peer_ps_state; + struct skw_cs_monitor_rpt cs_info; + struct skw_rpi_monitor_rpt rpi_info; + struct skw_noise_monitor_rpt noise_info; +} __packed; +/*** val[0] end***/ + +/* TLV: SKW_MIB_GET_PEER_INFO_E */ +/* No val data for debug info cmd */ +/** Rsp of this TLV **/ +struct skw_peer_info_rsp { + u16 status; + u16 tlv; + u16 len; + u8 val[0]; +} __packed; + +/*** val[0] start***/ + +struct skw_tx_hmac_per_peer_rpt_info { + u32 hmac_tx_stat[5]; + u32 txc_isr_read_dscr_fifo_cnt; +} __packed; + +struct skw_rate_struct { + u8 flags; + u8 mcs_idx; + u16 legacy_rate; + u8 nss; + u8 bw; + u8 gi; + u8 ru; + u8 dcm; +} __packed; + +struct skw_rx_msdu_info_rpt { + u8 ppdu_mode; + u8 data_rate; + u8 gi_type; + u8 sbw; + u8 dcm; + u8 nss; +} __packed; + +struct skw_tx_dbg_stats_per_peer { + u32 rts_fail_cnt; + u32 psdu_ack_timeout_cnt; + u32 tx_mpdu_cnt; + u32 tx_wait_time; +} __packed; + +/*val*/ +struct skw_get_peer_info { + u8 is_valid; + u8 inst_id; + u8 hw_id; + int8_t rx_rsp_rssi; + u16 avg_add_delay_in_ms; + u32 tx_max_delay_time; + struct skw_tx_hmac_per_peer_rpt_info hmac_tx_stat; + struct skw_rate_struct tx_rate_info; + struct skw_rx_msdu_info_rpt rx_msdu_info_rpt; + struct skw_tx_dbg_stats_per_peer tx_stats_info; + u16 tx_pending_pkt_cnt[4]; +} __packed; + +/*** val[0] end***/ + + +/* TLV: SKW_MIB_GET_HW_RX_INFO_E */ +/* No val data for debug info cmd */ +/** Rsp of this TLV **/ +struct skw_hw_rx_info_rsp { + u16 status; + u16 tlv; + u16 len; + u8 val[0]; +} __packed; + +/*** val[0] start***/ + +/*val*/ +struct skw_get_hw_rx_info { + u16 phy_rx_all_cnt; + u16 phy_rx_11b_cnt; + u16 phy_rx_11g_cnt; + u16 phy_rx_11n_cnt; + u16 phy_rx_11ac_cnt; + u16 phy_rx_11ax_cnt; + u16 mac_rx_mpdu_cnt; + u16 mac_rx_mpdu_fcs_pass_cnt; + u16 mac_rx_mpdu_fcs_pass_dir_cnt; +} __packed; + +/*** val[0] end***/ + +/* TLV: SKW_MIB_GET_INST_TSF_E */ +/* No val data for debug info cmd */ +/** Rsp of this TLV **/ +struct skw_inst_tsf { + u32 tsf_l; + u32 tsf_h; +} __packed; + +/*** val[0] start***/ + +/*val*/ +struct skw_get_inst_tsf_rsp { + u16 status; + u16 tlv; + u16 len; + u8 val[0]; +} __packed; + +static inline void skw_abort_cmd(struct skw_core *skw) +{ + if (skw_cmd_locked(&skw->cmd.lock) && + !test_and_set_bit(SKW_CMD_FLAG_ACKED, &skw->cmd.flags)) + skw->cmd.callback(skw); +} + +int skw_msg_xmit_timeout(struct wiphy *wiphy, int dev_id, int cmd, + void *buf, int len, void *arg, int size, + char *name, unsigned long timeout, + unsigned long extra_flags); + +#define skw_msg_xmit(wiphy, inst, cmd, buf, len, arg, size) \ + skw_msg_xmit_timeout(wiphy, inst, cmd, buf, len, \ + arg, size, #cmd, \ + msecs_to_jiffies(SKW_CMD_TIMEOUT), 0) + +#define SKW_NDEV_ID(d) (((struct skw_iface *)netdev_priv(d))->id) + +#define skw_send_msg(wiphy, dev, cmd, buf, len, arg, size) \ + skw_msg_xmit_timeout(wiphy, dev ? SKW_NDEV_ID(dev) : -1, \ + cmd, buf, len, arg, size, #cmd, \ + msecs_to_jiffies(SKW_CMD_TIMEOUT), 0) + + +int skw_msg_try_send(struct skw_core *skw, int dev_id, + int cmd, void *data, int data_len, + void *arg, int arg_size, char *name); + +void skw_default_event_work(struct work_struct *work); +int skw_cmd_ack_handler(struct skw_core *skw, void *data, int data_len); +void skw_event_handler(struct skw_core *skw, struct skw_iface *iface, + struct skw_msg *msg_hdr, void *data, size_t data_len, struct skw_skb_rxcb *cb); +int skw_queue_local_event(struct wiphy *wiphy, struct skw_iface *iface, + int event_id, void *data, size_t data_len); +void skw_del_sta_event(struct skw_iface *iface, const u8 *addr, u16 reason); +bool skw_cmd_data_len_in_limit(struct skw_core *skw, int data_len); +void skw_cqm_scan_timeout(void *data); +int skw_mgmt_tx_status(struct skw_iface *iface, u64 cookie, + const u8 *frame, int frame_len, u16 ack); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_recovery.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_recovery.c new file mode 100755 index 0000000..888ace7 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_recovery.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/skbuff.h> + +#include "skw_core.h" +#include "skw_cfg80211.h" +#include "skw_iface.h" +#include "skw_msg.h" +#include "skw_iw.h" +#include "skw_calib.h" +#include "skw_recovery.h" +#include "skw_mlme.h" +#include "skw_rx.h" +#include "skw_tx.h" + +static inline void +skw_recovery_sta_disconnect(struct net_device *ndev, u8 *addr) +{ + struct skw_iface *iface = netdev_priv(ndev); + + if (iface->sta.sme_external) + skw_tx_mlme_mgmt(ndev, IEEE80211_STYPE_DEAUTH, addr, addr, 3); + else + skw_disconnected(ndev, 3, NULL, 0, true, GFP_KERNEL); +} + +static int skw_recovery_sta(struct wiphy *wiphy, struct skw_recovery_data *rd, + struct skw_iface *iface) +{ + int ret; + u32 peer_map = rd->iface[iface->id].peer_map; + struct skw_sta_core *core = &iface->sta.core; + struct net_device *dev = iface->ndev; + +#ifdef SKW_STATE_RECOVERY + // TODO: + // recovery peer state + + struct cfg80211_bss *cbss; + + cbss = cfg80211_get_bss(wiphy, core->bss.channel, core->bss.bssid, + core->bss.ssid, core->bss.ssid_len, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + if (!cbss) { + if (!skw_cmd_unjoin(wiphy, dev, peer->addr, 3, true)) + peer->flags |= SKW_PEER_FLAG_DEAUTHED; + + skw_recovery_sta_disconnect(dev, core->bss.bssid); + return 0; + } + + ret = skw_join(wiphy, dev, cbss, false); + if (ret) { + if (!skw_cmd_unjoin(wiphy, dev, peer->addr, 3, true)) + peer->flags |= SKW_PEER_FLAG_DEAUTHED; + + skw_recovery_sta_disconnect(dev, core->bss.bssid); + return 0; + } + + // set key + // set ip + + cfg80211_put_bss(wiphy, cbss); +#else + while (peer_map) { + u8 idx = ffs(peer_map) - 1; + struct skw_peer *peer = rd->peer[iface->lmac_id][idx]; + + SKW_CLEAR(peer_map, BIT(idx)); + + if (!peer || peer->flags & SKW_PEER_FLAG_DEAUTHED) + continue; + + if (ether_addr_equal(peer->addr, core->bss.bssid)) { + del_timer_sync(&core->timer); + cancel_work_sync(&iface->sta.work); + + ret = skw_cmd_unjoin(wiphy, dev, peer->addr, + SKW_LEAVE, true); + if (ret) + skw_warn("failed, sta: %pM, ret: %d\n", + peer->addr, ret); + + skw_set_state(&core->sm, SKW_STATE_NONE); + memset(&core->bss, 0, sizeof(struct skw_bss_cfg)); + core->bss.ctx_idx = SKW_INVALID_ID; + + skw_recovery_sta_disconnect(dev, peer->addr); + peer->flags |= SKW_PEER_FLAG_DEAUTHED; + + } else { + /* TDLS */ + cfg80211_tdls_oper_request(dev, peer->addr, + NL80211_TDLS_TEARDOWN, + SKW_WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE, + GFP_KERNEL); + + peer->flags |= SKW_PEER_FLAG_DEAUTHED; + } + } + + atomic_set(&iface->actived_ctx, 0); +#endif + + return 0; +} + +static void +skw_recovery_sap_flush_sta(struct wiphy *wiphy, struct skw_recovery_data *rd, + struct skw_iface *iface, u8 subtype, u16 reason) +{ + int idx, ret; + u8 addr[ETH_ALEN]; + struct skw_peer *peer; + struct skw_core *skw = wiphy_priv(wiphy); + u32 peer_map = rd->iface[iface->id].peer_map; + u8 lmac_id = iface->lmac_id; + + while (peer_map) { + + if (test_bit(SKW_FLAG_FW_ASSERT, &skw->flags)) + break; + + idx = ffs(peer_map) - 1; + SKW_CLEAR(peer_map, BIT(idx)); + + peer = rd->peer[lmac_id][idx]; + if (!peer || peer->flags & SKW_PEER_FLAG_DEAUTHED) + continue; + + peer->flags |= SKW_PEER_FLAG_DEAUTHED; + skw_mlme_ap_remove_client(iface, peer->addr); + skw_del_sta_event(iface, peer->addr, SKW_LEAVE); + } + + memset(addr, 0xff, ETH_ALEN); + ret = skw_cmd_del_sta(wiphy, iface->ndev, addr, subtype, reason, true); + if (ret) + skw_warn("failed, sta: %pM, ret: %d\n", addr, ret); +} + +static int skw_recovery_sap(struct wiphy *wiphy, struct skw_recovery_data *rd, + struct skw_iface *iface) +{ + int ret, size; + struct skw_startap_param *param; + struct net_device *ndev = iface->ndev; + struct skw_startap_resp resp = {}; + + param = rd->iface[iface->id].param; + if (!param) { + skw_err("invalid param\n"); + return -EINVAL; + } + + ret = skw_sap_set_mib(wiphy, iface->ndev, + (u8 *)param + param->beacon_tail_offset, + param->beacon_tail_len); + if (ret) { + skw_err("set tlv failed, ret: %d\n", ret); + return ret; + } + + size = rd->iface[iface->id].size; + + ret = skw_send_msg(wiphy, ndev, SKW_CMD_START_AP, param, size, &resp, sizeof(resp)); + if (ret) { + skw_err("failed, ret: %d\n", ret); + return ret; + } + + skw_startap_resp_handler(iface->skw, iface, &resp); + + skw_dpd_set_coeff_params(wiphy, ndev, param->chan, param->center_chn1, + param->center_chn2, param->chan_width); + + skw_recovery_sap_flush_sta(wiphy, rd, iface, 12, SKW_LEAVE); + + return 0; +} + +static int skw_recovery_ibss(struct wiphy *wiphy, struct skw_iface *iface) +{ + return 0; +} + +static int skw_recovery_p2p_dev(struct wiphy *wiphy, struct skw_iface *iface) +{ + skw_dbg("done\n"); + + return 0; +} + +static void +skw_recovery_prepare(struct skw_core *skw, struct skw_recovery_data *rd) +{ + int i, j; + struct skw_peer_ctx *ctx; + struct skw_iface *iface; + + skw->cmd.seq = 0; + skw->skw_event_sn = 0; + + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + atomic_set(&skw->hw.lmac[i].fw_credit, 0); + + WRITE_ONCE(skw->hw.lmac[i].iface_bitmap, 0); + } + + if (test_and_set_bit(SKW_FLAG_FW_CHIP_RECOVERY, &skw->flags)) { + skw_info("received recovery event during recovery"); + + + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + for (j = 0; j < SKW_MAX_PEER_SUPPORT; j++) { + ctx = &skw->hw.lmac[i].peer_ctx[j]; + + skw_peer_ctx_lock(ctx); + + rcu_assign_pointer(ctx->entry, NULL); + ctx->peer = NULL; + + skw_peer_ctx_unlock(ctx); + } + } + + spin_lock_bh(&skw->vif.lock); + + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + if (!iface) + continue; + + for (j = 0; j <= SKW_WMM_AC_MAX; j++) { + skb_queue_purge(&iface->txq[j]); + skb_queue_purge(&iface->tx_cache[j]); + } + + atomic_set(&iface->peer_map, 0); + } + + spin_unlock_bh(&skw->vif.lock); + + return; + } + + mutex_lock(&rd->lock); + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + for (j = 0; j < SKW_MAX_PEER_SUPPORT; j++) { + ctx = &skw->hw.lmac[i].peer_ctx[j]; + + skw_peer_ctx_lock(ctx); + + rcu_assign_pointer(ctx->entry, NULL); + rd->peer[i][j] = ctx->peer; + ctx->peer = NULL; + + skw_peer_ctx_unlock(ctx); + } + } + + spin_lock_bh(&skw->vif.lock); + + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + if (!iface) + continue; + + for (j = 0; j <= SKW_WMM_AC_MAX; j++) { + skb_queue_purge(&iface->txq[j]); + skb_queue_purge(&iface->tx_cache[j]); + } + + rd->iface[i].peer_map = atomic_read(&iface->peer_map); + atomic_set(&iface->peer_map, 0); + } + + spin_unlock_bh(&skw->vif.lock); + + mutex_unlock(&rd->lock); + + skw_dpd_zero(&skw->dpd); +} + +static int skw_recovery_iface(struct work_struct *wk, u8 iface_idx) +{ + int ret = 0; + struct skw_core *skw = container_of(wk, struct skw_core, recovery_work); + struct wiphy *wiphy = priv_to_wiphy(skw); + struct skw_recovery_data *rd = &skw->recovery_data; + struct skw_iface *iface = skw->vif.iface[iface_idx]; + + if (!iface) + goto ret; + + if (test_bit(SKW_FLAG_FW_ASSERT, &skw->flags)) { + ret = -EAGAIN; + goto ret; + } + + skw_info("%s: inst: %d\n", + skw_iftype_name(iface->wdev.iftype), iface_idx); + + ret = skw_cmd_open_dev(wiphy, iface->id, iface->addr, + iface->wdev.iftype, 0); + if (ret) { + skw_err("open %s failed, inst: %d, ret: %d\n", + skw_iftype_name(iface->wdev.iftype), + iface->id, ret); + + skw_hw_assert(skw, false); + + goto ret; + } + + spin_lock_bh(&skw->vif.lock); + SKW_SET(iface->flags, SKW_IFACE_FLAG_OPENED); + skw->vif.opened_dev++; + spin_unlock_bh(&skw->vif.lock); + + mutex_lock(&rd->lock); + + switch (iface->wdev.iftype) { + case NL80211_IFTYPE_STATION: + if (iface->flags & SKW_IFACE_FLAG_LEGACY_P2P_DEV) { + skw_recovery_p2p_dev(wiphy, iface); + break; + } + + skw_fallthrough; + + case NL80211_IFTYPE_P2P_CLIENT: + skw_recovery_sta(wiphy, rd, iface); + break; + + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + skw_recovery_sap(wiphy, rd, iface); + break; + + case NL80211_IFTYPE_ADHOC: + skw_recovery_ibss(wiphy, iface); + break; + + case NL80211_IFTYPE_P2P_DEVICE: + skw_recovery_p2p_dev(wiphy, iface); + break; + + default: + break; + } + + mutex_unlock(&rd->lock); + +ret: + return ret; +} + +static void skw_recovery_work(struct work_struct *wk) +{ + int i, j, ret; + struct skw_chip_info chip; + struct skw_core *skw = container_of(wk, struct skw_core, recovery_work); + struct wiphy *wiphy = priv_to_wiphy(skw); + struct skw_recovery_data *rd = &skw->recovery_data; + struct skw_iface *iface; + + skw_info("start\n"); + + skw_recovery_prepare(skw, rd); + + spin_lock_bh(&skw->vif.lock); + skw->vif.opened_dev = 0; + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + + if (iface) + SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_OPENED); + } + spin_unlock_bh(&skw->vif.lock); + + skw_wifi_enable(skw->hw_pdata); + + ret = skw_register_rx_callback(skw, skw_rx_cb, skw, skw_rx_cb, skw); + if (ret < 0) + skw_err("register rx callback failed, ret: %d\n", ret); + + skw_hw_xmit_init(skw, skw->hw.dma); + + clear_bit(SKW_FLAG_FW_ASSERT, &skw->flags); + clear_bit(SKW_FLAG_BLOCK_TX, &skw->flags); + clear_bit(SKW_FLAG_FW_MAC_RECOVERY, &skw->flags); + clear_bit(SKW_FLAG_FW_THERMAL, &skw->flags); + + skw_sync_cmd_event_version(wiphy); + + ret = skw_sync_chip_info(wiphy, &chip); + if (ret) { + skw_err("sync chip info failed, ret: %d\n", ret); + goto ret; + } + + ret = skw_calib_download(wiphy, skw->fw.calib_file); + if (ret) { + skw_err("calib download failed, ret: %d\n", ret); + goto ret; + } + + for (i = 0; i < SKW_NR_IFACE; i++) { + struct skw_iface *iface = skw->vif.iface[i]; + + if (!iface) + continue; + + if (iface->wdev.iftype == NL80211_IFTYPE_STATION || + iface->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE) { + ret = skw_recovery_iface(wk, i); + if (ret) + goto ret; + } + } + + for (i = 0; i < SKW_NR_IFACE; i++) { + struct skw_iface *iface = skw->vif.iface[i]; + + if (!iface) + continue; + + if (!(iface->wdev.iftype == NL80211_IFTYPE_STATION || + iface->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE)) { + ret = skw_recovery_iface(wk, i); + if (ret) + goto ret; + } + } + + if (!ret) { + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + for (j = 0; j < SKW_MAX_PEER_SUPPORT; j++) { + skw_peer_free(rd->peer[i][j]); + rd->peer[i][j] = NULL; + } + } + + clear_bit(SKW_FLAG_FW_CHIP_RECOVERY, &skw->flags); + +#ifdef CONFIG_SWT6621S_USB3_WORKAROUND + if (test_bit(SKW_FLAG_SWITCHING_USB_MODE, &skw->flags)) { + skw_dbg("usb switch done"); + complete(&skw->usb_switch_done); + } +#endif + } + +ret: + skw_dbg("end %d\n", ret); +} + +void skw_recovery_del_peer(struct skw_iface *iface, u8 peer_idx) +{ + struct skw_recovery_data *rd = &iface->skw->recovery_data; + u8 lmac_id = iface->lmac_id; + + if (!test_bit(SKW_FLAG_FW_CHIP_RECOVERY, &iface->skw->flags)) + return; + + mutex_lock(&rd->lock); + + if (rd->peer[lmac_id]) + rd->peer[lmac_id][peer_idx]->flags |= SKW_PEER_FLAG_DEAUTHED; + + mutex_unlock(&rd->lock); +} + +int skw_recovery_data_update(struct skw_iface *iface, void *param, int len) +{ + void *data; + struct skw_recovery_data *rd = &iface->skw->recovery_data; + + if (!param) + return 0; + + data = SKW_ZALLOC(SKW_2K_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(data, param, len); + + mutex_lock(&rd->lock); + + SKW_KFREE(rd->iface[iface->id].param); + + rd->iface[iface->id].param = data; + rd->iface[iface->id].size = len; + + mutex_unlock(&rd->lock); + + return 0; +} + +void skw_recovery_data_clear(struct skw_iface *iface) +{ + struct skw_recovery_data *rd = &iface->skw->recovery_data; + + mutex_lock(&rd->lock); + + rd->iface[iface->id].size = 0; + rd->iface[iface->id].peer_map = 0; + SKW_KFREE(rd->iface[iface->id].param); + + mutex_unlock(&rd->lock); +} + +int skw_recovery_init(struct skw_core *skw) +{ + mutex_init(&skw->recovery_data.lock); + INIT_WORK(&skw->recovery_work, skw_recovery_work); +#ifdef CONFIG_SWT6621S_USB3_WORKAROUND + init_completion(&skw->usb_switch_done); +#endif + + return 0; +} + +void skw_recovery_deinit(struct skw_core *skw) +{ + int i, j; + struct skw_recovery_data *rd = &skw->recovery_data; + + mutex_lock(&rd->lock); + + cancel_work_sync(&skw->recovery_work); + + for (i = 0; i < SKW_NR_IFACE; i++) + SKW_KFREE(rd->iface[i].param); + + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + for (j = 0; j < SKW_MAX_PEER_SUPPORT; j++) { + skw_peer_free(rd->peer[i][j]); + rd->peer[i][j] = NULL; + } + } + + mutex_unlock(&rd->lock); +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_recovery.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_recovery.h new file mode 100755 index 0000000..a2c0a4d --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_recovery.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_RECOVERY_H__ +#define __SKW_RECOVERY_H__ + +#define SKW_RECOVERY_TIMEOUT (msecs_to_jiffies(5000)) + +struct skw_recovery_ifdata { + void *param; + int size; + u32 peer_map; +}; + +int skw_recovery_data_update(struct skw_iface *iface, void *param, int len); +void skw_recovery_data_clear(struct skw_iface *iface); +int skw_recovery_init(struct skw_core *skw); +void skw_recovery_deinit(struct skw_core *skw); + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_regd.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_regd.c new file mode 100755 index 0000000..a066d65 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_regd.c @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/nl80211.h> +#include <net/cfg80211.h> + +#include "skw_core.h" +#include "skw_regd.h" +#include "skw_msg.h" +#include "skw_log.h" +#include "skw_db.h" + +static int skw_regd_show(struct seq_file *seq, void *data) +{ + struct wiphy *wiphy = seq->private; + struct skw_core *skw = wiphy_priv(wiphy); + + seq_puts(seq, "\n"); + + seq_printf(seq, "country: %c%c\n", skw->country[0], skw->country[1]); + + seq_puts(seq, "\n"); + + return 0; +} + +static int skw_regd_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_regd_show, inode->i_private); +} + +static ssize_t skw_regd_write(struct file *fp, const char __user *buf, + size_t size, loff_t *off) +{ + u8 country[3] = {0}; + struct wiphy *wiphy = fp->f_inode->i_private; + int ret = 0; + + if (size != 3) { + skw_err("invalid len: %zd\n", size); + return size; + } + + if (copy_from_user(&country, buf, size)) { + skw_err("copy failed\n"); + return size; + } + + ret = skw_set_regdom(wiphy, (char *)country); + if (ret) + skw_err("set country failed, ret: %d\n", ret); + + return size; +} + +static const struct file_operations skw_regd_fops = { + .owner = THIS_MODULE, + .open = skw_regd_open, + .read = seq_read, + .write = skw_regd_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static bool skw_alpha2_equal(const char *alpha2_x, const char *alpha2_y) +{ + if (!alpha2_x || !alpha2_y) + return false; + + return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]; +} + +static bool skw_freq_in_rule_band(const struct ieee80211_freq_range *freq_range, + u32 freq_khz) +{ +#define ONE_GHZ_IN_KHZ 1000000 + u32 limit = freq_khz > 45 * ONE_GHZ_IN_KHZ ? + 20 * ONE_GHZ_IN_KHZ : 2 * ONE_GHZ_IN_KHZ; + + if (abs(freq_khz - freq_range->start_freq_khz) <= limit) + return true; + + if (abs(freq_khz - freq_range->end_freq_khz) <= limit) + return true; + + return false; + +#undef ONE_GHZ_IN_KHZ +} + +static bool skw_does_bw_fit_range(const struct ieee80211_freq_range *freq_range, + u32 center_freq_khz, u32 bw_khz) +{ + u32 start_freq_khz, end_freq_khz; + + start_freq_khz = center_freq_khz - (bw_khz / 2); + end_freq_khz = center_freq_khz + (bw_khz / 2); + + if (start_freq_khz >= freq_range->start_freq_khz && + end_freq_khz <= freq_range->end_freq_khz) + return true; + + return false; +} + +static const struct ieee80211_regdomain *skw_get_regd(const char *alpha2) +{ + int i; + const struct ieee80211_regdomain *regdom; + + if (!is_skw_valid_reg_code(alpha2)) { + skw_err("Invalid alpha\n"); + return NULL; + } + + for (i = 0; i < skw_regdb_size; i++) { + regdom = skw_regdb[i]; + + if (skw_alpha2_equal(alpha2, regdom->alpha2)) + return regdom; + } + + skw_warn("country: %c%c not support\n", alpha2[0], alpha2[1]); + + return NULL; +} + +static bool is_skw_valid_reg_rule(const struct ieee80211_reg_rule *rule) +{ + u32 freq_diff; + const struct ieee80211_freq_range *freq_range = &rule->freq_range; + + if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) { + skw_dbg("invalid, start: %d, end: %d\n", + freq_range->start_freq_khz, freq_range->end_freq_khz); + + return false; + } + + if (freq_range->start_freq_khz > freq_range->end_freq_khz) { + skw_dbg("invalid, start: %d > end: %d\n", + freq_range->start_freq_khz, freq_range->end_freq_khz); + return false; + } + + freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; + + if (freq_range->end_freq_khz <= freq_range->start_freq_khz || + freq_range->max_bandwidth_khz > freq_diff) { + skw_dbg("invalid, start: %d, end: %d, max band: %d, diff: %d\n", + freq_range->start_freq_khz, freq_range->end_freq_khz, + freq_range->max_bandwidth_khz, freq_diff); + return false; + } + + return true; +} + +static bool is_skw_valid_rd(const struct ieee80211_regdomain *rd) +{ + int i; + const struct ieee80211_reg_rule *reg_rule = NULL; + + for (i = 0; i < rd->n_reg_rules; i++) { + reg_rule = &rd->reg_rules[i]; + + if (!is_skw_valid_reg_rule(reg_rule)) + return false; + } + + return true; +} + +static const struct ieee80211_reg_rule * +skw_freq_reg_info(const struct ieee80211_regdomain *regd, u32 freq) +{ + int i; + bool band_rule_found = false; + bool bw_fits = false; + + if (!regd) + return ERR_PTR(-EINVAL); + + for (i = 0; i < regd->n_reg_rules; i++) { + const struct ieee80211_reg_rule *rr; + const struct ieee80211_freq_range *fr = NULL; + + rr = ®d->reg_rules[i]; + fr = &rr->freq_range; + + if (!band_rule_found) + band_rule_found = skw_freq_in_rule_band(fr, freq); + + bw_fits = skw_does_bw_fit_range(fr, freq, MHZ_TO_KHZ(20)); + + if (band_rule_found && bw_fits) + return rr; + } + + if (!band_rule_found) + return ERR_PTR(-ERANGE); + + return ERR_PTR(-EINVAL); +} + +static const struct ieee80211_reg_rule *skw_regd_rule(struct wiphy *wiphy, u32 freq) +{ + u32 freq_khz = MHZ_TO_KHZ(freq); + struct skw_core *skw = wiphy_priv(wiphy); + + if (skw->regd || skw_regd_self_mamaged(wiphy)) + return skw_freq_reg_info(skw->regd, freq_khz); + + return freq_reg_info(wiphy, freq_khz); +} + +int skw_cmd_set_regdom(struct wiphy *wiphy, const char *alpha2) +{ + int ret; + int i, idx, band; + struct ieee80211_supported_band *sband; + struct skw_regdom regd = {}; + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_reg_rule *rule = ®d.rules[0]; + const struct ieee80211_reg_rule *rr = NULL, *_rr = NULL; + +#define SKW_MAX_POWER(rr) (MBM_TO_DBM(rr->power_rule.max_eirp)) +#define SKW_MAX_GAIN(rr) (MBI_TO_DBI(rr->power_rule.max_antenna_gain)) + + regd.country[0] = alpha2[0]; + regd.country[1] = alpha2[1]; + regd.country[2] = 0; + + for (idx = 0, band = 0; band < NUM_NL80211_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *chn = &sband->channels[i]; + + rr = skw_regd_rule(wiphy, chn->center_freq); + if (IS_ERR(rr) || rr->flags & SKW_RRF_NO_IR) + continue; + + if (rr != _rr) { + regd.nr_reg_rules++; + + rule = ®d.rules[idx++]; + + rule->nr_channel = 0; + rule->start_channel = chn->hw_value; + rule->max_power = SKW_MAX_POWER(rr); + rule->max_gain = SKW_MAX_GAIN(rr); + rule->flags = rr->flags; + + _rr = rr; + } + + rule->nr_channel = chn->hw_value - rule->start_channel + 1; + } + } + + if (!regd.nr_reg_rules) + return 0; + + for (i = 0; i < regd.nr_reg_rules; i++) { + skw_dbg("%d @ %d, power: %d, gain: %d, flags: 0x%x\n", + regd.rules[i].start_channel, regd.rules[i].nr_channel, + regd.rules[i].max_power, regd.rules[i].max_gain, + regd.rules[i].flags); + } + + ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_REGD, ®d, + sizeof(regd), NULL, 0); + if (!ret) { + skw->country[0] = alpha2[0]; + skw->country[1] = alpha2[1]; + } else { + skw_warn("failed, country: %c%c, rules: %d, ret: %d\n", + alpha2[0], alpha2[1], regd.nr_reg_rules, ret); + } + + return ret; +} + +static int __skw_set_wiphy_regd(struct wiphy *wiphy, struct ieee80211_regdomain *rd) +{ + int ret = 0; + struct skw_core *skw = wiphy_priv(wiphy); + + skw->regd = rd; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + if (rtnl_is_locked()) + ret = skw_set_wiphy_regd_sync(wiphy, rd); + else + ret = regulatory_set_wiphy_regd(wiphy, rd); +#else + wiphy_apply_custom_regulatory(wiphy, rd); +#endif + + return ret; +} + +int skw_set_wiphy_regd(struct wiphy *wiphy, const char *country) +{ + const struct ieee80211_regdomain *regd; + + if (!skw_regd_self_mamaged(wiphy)) + return 0; + + regd = skw_get_regd(country); + if (!regd) + return -EINVAL; + + if (!is_skw_valid_rd(regd)) + return -EINVAL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + if (country[0] == '0' && country[1] == '0') + wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS; + else + wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS; +#endif + + return __skw_set_wiphy_regd(wiphy, (void *)regd); +} + +int skw_set_regdom(struct wiphy *wiphy, char *country) +{ + int ret; + + skw_dbg("country: %c%c\n", country[0], country[1]); + + if (!is_skw_valid_reg_code(country)) { + skw_err("Invalid country code: %c%c\n", + country[0], country[1]); + + return -EINVAL; + } + + if (skw_regd_self_mamaged(wiphy)) { + ret = skw_set_wiphy_regd(wiphy, country); + if (!ret) + ret = skw_cmd_set_regdom(wiphy, country); + + return ret; + } + + return regulatory_hint(wiphy, country); +} + +void skw_regd_init(struct wiphy *wiphy) +{ + skw_debugfs_file(SKW_WIPHY_DENTRY(wiphy), "regdom", 0666, &skw_regd_fops, wiphy); +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_regd.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_regd.h new file mode 100755 index 0000000..a7dacc1 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_regd.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_REGD_H__ +#define __SKW_REGD_H__ + +#include <linux/ctype.h> +#include "skw_core.h" + +struct skw_reg_rule { + u8 start_channel; + u8 nr_channel; + s8 max_power; + s8 max_gain; + u32 flags; +} __packed; + +struct skw_regdom { + u8 country[3]; + u8 nr_reg_rules; + struct skw_reg_rule rules[8]; +} __packed; + +static inline bool skw_regd_self_mamaged(struct wiphy *wiphy) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + return test_bit(SKW_FLAG_PRIV_REGD, &skw->flags); +} + +static inline bool is_skw_valid_reg_code(const char *alpha2) +{ + if (!alpha2) + return false; + + if (alpha2[0] == '0' && alpha2[1] == '0') + return true; + + return isalpha(alpha2[0]) && isalpha(alpha2[1]); +} + +void skw_regd_init(struct wiphy *wiphy); +int skw_set_regdom(struct wiphy *wiphy, char *country); +int skw_set_wiphy_regd(struct wiphy *wiphy, const char *country); +int skw_cmd_set_regdom(struct wiphy *wiphy, const char *alpha2); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_rx.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_rx.c new file mode 100755 index 0000000..ccd9d27 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_rx.c @@ -0,0 +1,2102 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/timer.h> +#include <linux/skbuff.h> +#include <linux/kthread.h> +#include <linux/cpumask.h> +#include <linux/ctype.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <net/ip6_checksum.h> +#include <net/tcp.h> + +#include "skw_core.h" +#include "skw_msg.h" +#include "skw_cfg80211.h" +#include "skw_rx.h" +#include "skw_work.h" +#include "skw_tx.h" +#include "trace.h" + +#define SKW_PN_U48(x) (*(u64 *)(x) & 0xffffffffffff) +#define SKW_PN_U32(x) (*(u64 *)(x) & 0xffffffff) +#define SKW_MSDU_HDR_LEN 6 /* ETH_HLEN - SKW_SNAP_HDR_LEN */ + +static u8 rx_reorder_flag; + +static inline void skw_wake_lock_timeout(struct skw_core *skw, long timeout) +{ +#ifdef CONFIG_HAS_WAKELOCK + if (!wake_lock_active(&skw->rx_wlock)) + wake_lock_timeout(&skw->rx_wlock, msecs_to_jiffies(timeout)); +#endif +} + +static inline void skw_wake_lock_init(struct skw_core *skw, int type, const char *name) +{ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&skw->rx_wlock, type, name); +#endif +} + +static inline void skw_wake_lock_deinit(struct skw_core *skw) +{ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&skw->rx_wlock); +#endif +} + +static int skw_rx_reorder_show(struct seq_file *seq, void *data) +{ + if (rx_reorder_flag) + seq_puts(seq, "enable\n"); + else + seq_puts(seq, "disable\n"); + + return 0; +} + +static int skw_rx_reorder_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_rx_reorder_show, inode->i_private); +} + +static ssize_t skw_rx_reorder_write(struct file *fp, const char __user *buf, + size_t len, loff_t *offset) +{ + int i; + char cmd[32] = {0}; + + for (i = 0; i < len; i++) { + char c; + + if (get_user(c, buf)) + return -EFAULT; + + if (c == '\n' || c == '\0') + break; + + cmd[i] = tolower(c); + buf++; + } + + if (strcmp(cmd, "enable") == 0) + rx_reorder_flag = true; + else if (strcmp(cmd, "disable") == 0) + rx_reorder_flag = false; + else + skw_warn("rx_reorder support setting values of \"enable\" or \"disable\"\n"); + + return len; +} + +static const struct file_operations skw_rx_reorder_fops = { + .owner = THIS_MODULE, + .open = skw_rx_reorder_open, + .read = seq_read, + .release = single_release, + .write = skw_rx_reorder_write, +}; + +static int skw_pn_reuse_show(struct seq_file *seq, void *data) +{ + struct skw_core *skw = seq->private; + + if (test_bit(SKW_FLAG_FW_PN_REUSE, &skw->flags)) + seq_puts(seq, "enable\n"); + else + seq_puts(seq, "disable\n"); + + return 0; +} + +static int skw_pn_reuse_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_pn_reuse_show, inode->i_private); +} + +static ssize_t skw_pn_reuse_write(struct file *fp, const char __user *buf, + size_t len, loff_t *offset) +{ + int i; + struct skw_core *skw = fp->f_inode->i_private; + char cmd[32] = {0}; + + for (i = 0; i < len; i++) { + char c; + + if (get_user(c, buf)) + return -EFAULT; + + if (c == '\n' || c == '\0') + break; + + cmd[i] = tolower(c); + buf++; + } + + if (strcmp(cmd, "enable") == 0) + set_bit(SKW_FLAG_FW_PN_REUSE, &skw->flags); + else if (strcmp(cmd, "disable") == 0) + clear_bit(SKW_FLAG_FW_PN_REUSE, &skw->flags); + else + skw_warn("pn_reuse_flag support setting values of \"enable\" or \"disable\"\n"); + + return len; +} + +static const struct file_operations skw_pn_reuse_fops = { + .owner = THIS_MODULE, + .open = skw_pn_reuse_open, + .read = seq_read, + .release = single_release, + .write = skw_pn_reuse_write, +}; + +static inline struct skw_rx_desc *skw_rx_desc_hdr(struct sk_buff *skb) +{ + return (struct skw_rx_desc *)(skb->data - SKW_SKB_RXCB(skb)->rx_desc_offset); +} + +void skw_tcpopt_window_handle(struct sk_buff *skb) +{ + unsigned int tcphoff, tcp_hdrlen, length; + u8 *ptr; + __sum16 check; + struct tcphdr *tcph; + struct ethhdr *eth = eth_hdr(skb); + struct iphdr *iph = (struct iphdr *)(skb->data); + + if (eth->h_proto == htons(ETH_P_IP) && iph->protocol == IPPROTO_TCP) { + + if (skb->len < ntohs(iph->tot_len)) + return; + + tcphoff = iph->ihl * 4; + tcph = (struct tcphdr *)(skb->data + tcphoff); + + if (!(tcp_flag_word(tcph)&TCP_FLAG_SYN)) + return; + + //skw_dbg("skb->len:%d tot_len:%d\n", skb->len, ntohs(iph->tot_len)); + + tcp_hdrlen = tcph->doff*4; + length = (tcph->doff-5)*4; + ptr = (u8 *)tcph + tcp_hdrlen - length; + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return; + case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ + length--; + continue; + default: + if (length < 2) + return; + opsize = *ptr++; + if (opsize < 2) /* "silly options" */ + return; + if (opsize > length) + return; /* don't parse partial options */ + + if (opcode == TCPOPT_WINDOW + && opsize == TCPOLEN_WINDOW) { + //skw_dbg("val:%d\n", *ptr); + if (*ptr < 6) { + *ptr = 6; + tcph->check = 0; + check = csum_partial(tcph, tcp_hdrlen, 0); + tcph->check = csum_tcpudp_magic(iph->saddr, + iph->daddr, tcp_hdrlen, IPPROTO_TCP, check); + } + } + + ptr += opsize - 2; + length -= opsize; + } + } + } +} + +/* + * To verify HW checksum for ipv6 + */ +static void skw_csum_verify(struct skw_rx_desc *desc, struct sk_buff *skb) +{ + u16 data_len; + __sum16 csum; + unsigned int tcphoff; + struct iphdr *iph; + struct ipv6hdr *ip6h; + struct ethhdr *eth = eth_hdr(skb); + + if (!skb->csum) + return; + + switch (eth->h_proto) { + case htons(ETH_P_IPV6): + ip6h = (struct ipv6hdr *)(skb->data); + tcphoff = sizeof(struct ipv6hdr); + // tcph = (struct tcphdr *)(skb->data + tcphoff); + + // fixme: + // minus the length of any extension headers present between the IPv6 + // header and the upper-layer header + data_len = ntohs(ip6h->payload_len); + + if (skb->len != data_len + tcphoff) { + skw_detail("ipv6 dummy pending: rx len: %d, tot_len: %d", + skb->len, data_len); + + skb->csum = csum_partial(skb->data + tcphoff, + data_len, 0); + + skb_trim(skb, data_len + tcphoff); + } + + csum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, data_len, + ip6h->nexthdr, skb->csum); + if (csum) { + skw_detail("sa: %pI6, da: %pI6, proto: 0x%x, seq: %d, csum: 0x%x, result: 0x%x\n", + &ip6h->saddr, &ip6h->daddr, ip6h->nexthdr, + desc->sn, skb->csum, csum); + + skw_hex_dump("csum failed", skb->data, skb->len, false); + } else { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + + break; + + case htons(ETH_P_IP): + iph = (struct iphdr *)(skb->data); + tcphoff = iph->ihl * 4; + // tcph = (struct tcphdr *)(skb->data + tcphoff); + + data_len = ntohs(iph->tot_len); + + if (skb->len != data_len) { + skw_detail("ipv4 dummy pending: rx len: %d, tot_len: %d", + skb->len, data_len); + + skb->csum = csum_partial(skb->data + tcphoff, + data_len - tcphoff, 0); + + skb_trim(skb, data_len); + } + + csum = csum_tcpudp_magic(iph->saddr, iph->daddr, + data_len - tcphoff, + iph->protocol, skb->csum); + if (csum) { + skw_detail("sa: %pI4, da: %pI4, proto: 0x%x, seq: %d, csum: 0x%x, result: 0x%x\n", + &iph->saddr, &iph->daddr, iph->protocol, + desc->sn, skb->csum, csum); + + skw_hex_dump("csum failed", skb->data, skb->len, false); + } + + break; + + default: + break; + } +} + +static void skw_rptr_hdl(struct skw_iface *iface, struct sk_buff *skb) +{ + int mcast; + struct iphdr *iph; + struct ipv6hdr *ip6h; + struct ethhdr *eth = (struct ethhdr *)skb->data; + + if (unlikely(test_bit(SKW_FLAG_REPEATER, &iface->skw->flags))) { + if (eth->h_proto == ntohs(ETH_P_ARP) && is_skw_sta_mode(iface) + && iface->ndev->priv_flags & IFF_BRIDGE_PORT) { + + struct skw_arphdr *arp = skw_arp_hdr(skb); + + if (arp->ar_op == ntohs(ARPOP_REPLY)) { + int i; + struct skw_ctx_entry *e = NULL; + + rcu_read_lock(); + for (i = 0; i < SKW_MAX_PEER_SUPPORT; i++) { + e = rcu_dereference(iface->skw->hw.lmac[iface->lmac_id].peer_ctx[i].entry); + if (e && e->peer && e->peer->ip_addr == arp->ar_tip) { + skw_ether_copy(eth->h_dest, e->peer->addr); + skw_ether_copy(arp->ar_tha, e->peer->addr); + break; + } + } + rcu_read_unlock(); + } + } + + mcast = is_multicast_ether_addr(eth->h_dest); + if (ntohs(eth->h_proto) == ETH_P_IP) { + iph = (struct iphdr *)(skb->data + sizeof(struct ethhdr)); + + if (ipv4_is_multicast(iph->daddr) && !mcast) { + ip_eth_mc_map(iph->daddr, eth->h_dest); + } + } else if (ntohs(eth->h_proto) == ETH_P_IPV6) { + ip6h = (struct ipv6hdr *)(skb->data + sizeof(struct ethhdr)); + + if (ipv6_addr_is_multicast(&ip6h->daddr) && !mcast) { + ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); + } + } + } +} + +static void skw_deliver_skb(struct skw_iface *iface, struct sk_buff *skb) +{ + struct sk_buff *tx_skb = NULL; + struct ethhdr *eth = (struct ethhdr *)skb->data; + struct skw_rx_desc *desc = skw_rx_desc_hdr(skb); + int mcast; + unsigned int len = skb->len; + int ret = NET_RX_DROP; + + if (ether_addr_equal(eth->h_source, iface->addr)) + goto stats; + + if (unlikely(!desc->snap_match)) { + skw_detail("snap unmatch, sn: %d\n", desc->sn); + + skw_snap_unmatch_handler(skb); + } + + /* forward for ap mode */ + mcast = is_multicast_ether_addr(skb->data); + if (desc->need_forward && is_skw_ap_mode(iface) && !iface->sap.ap_isolate) { + if (mcast) { + tx_skb = skb_copy(skb, GFP_ATOMIC); + } else { + int i; + struct skw_ctx_entry *e = NULL; + + rcu_read_lock(); + for (i = 0; i < SKW_MAX_PEER_SUPPORT; i++) { + e = rcu_dereference(iface->skw->hw.lmac[iface->lmac_id].peer_ctx[i].entry); + if (e && ether_addr_equal(e->addr, skb->data)) { + tx_skb = skb; + skb = NULL; + break; + } + } + rcu_read_unlock(); + } + + if (tx_skb) { + tx_skb->priority += 256; + tx_skb->protocol = htons(ETH_P_802_3); + skb_reset_network_header(tx_skb); + skb_reset_mac_header(tx_skb); + dev_queue_xmit(tx_skb); + } + + if (!skb) { + ret = NET_RX_SUCCESS; + goto stats; + //return; + } + } + + skw_rptr_hdl(iface, skb); + skb->protocol = eth_type_trans(skb, iface->ndev); + // TODO: + // ipv6 csum check + if (desc->csum_valid) { + skb->csum = desc->csum; + skb->ip_summed = CHECKSUM_COMPLETE; + skw_csum_verify(desc, skb); + } else { + skb->csum = 0; + skb->ip_summed = CHECKSUM_NONE; + } + + ret = NET_RX_SUCCESS; + + skw_detail("skb recv delta %lld us\n", ktime_to_us(net_timedelta(skb->tstamp))); + + #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) + netif_rx_ni(skb); + #else + netif_rx(skb); + #endif +stats: + if (unlikely(ret == NET_RX_DROP)) { + iface->ndev->stats.rx_dropped++; + dev_kfree_skb(skb); + } else { + iface->ndev->stats.rx_packets++; + iface->ndev->stats.rx_bytes += len; + if (mcast) + iface->ndev->stats.multicast++; + } +} + +/* + * get fragment entry + * @tid & @sn as fragment entry match id + * @active, if false, check duplicat first, then get an inactive fragment, + * else return the oldest active entry + */ +static struct skw_frag_entry * +skw_frag_get_entry(struct skw_iface *iface, u8 tid, u16 sn, bool active) +{ + int i; + struct skw_frag_entry *entry = NULL, *oldest = NULL; + struct skw_frag_entry *inactive = NULL; + + for (i = 0; i < SKW_MAX_DEFRAG_ENTRY; i++) { + struct skw_frag_entry *e = &iface->frag[i]; + + if (e->sn == sn && e->tid == tid) { + entry = e; + break; + } + + if (!active) { + + // skw_dbg("i: %d,entry tid: %d, sn: %d, status: %d\n", + // i, e->tid, e->sn, e->status); + + if (!(e->status & SKW_FRAG_STATUS_ACTIVE)) { + inactive = e; + continue; + } + + if (!oldest) { + oldest = e; + continue; + } + + if (time_after(oldest->start, e->start)) + oldest = e; + } + } + + if (!active && !entry) + entry = inactive ? inactive : oldest; + + return entry; +} +// Firmware will cover the exception that receiving a fragment +// frame while in a ba session +// Firmware will split A-MSDU frame to MSDU to Wi-Fi driver + +static void skw_frag_init_entry(struct skw_iface *iface, struct sk_buff *skb) +{ + struct skw_frag_entry *entry = NULL; + struct skw_rx_desc *desc = skw_rx_desc_hdr(skb); + + entry = skw_frag_get_entry(iface, desc->tid, desc->sn, false); + if (entry->status & SKW_FRAG_STATUS_ACTIVE) { + skw_warn("overwrite, entry: %d, tid: %d, sn: %d, time: %d ms\n", + entry->id, entry->tid, entry->sn, + jiffies_to_msecs(jiffies - entry->start)); + } + + if (!skb_queue_empty(&entry->skb_list)) + __skb_queue_purge(&entry->skb_list); + + entry->status = SKW_FRAG_STATUS_ACTIVE; + entry->pending_len = 0; + entry->start = jiffies; + entry->tid = desc->tid; + entry->sn = desc->sn; + entry->frag_num = 0; + + if (iface->key_conf.skw_cipher == SKW_CIPHER_TYPE_CCMP || + iface->key_conf.skw_cipher == SKW_CIPHER_TYPE_CCMP_256 || + iface->key_conf.skw_cipher == SKW_CIPHER_TYPE_GCMP || + iface->key_conf.skw_cipher == SKW_CIPHER_TYPE_GCMP_256) { + memcpy(entry->last_pn, desc->pn, IEEE80211_CCMP_PN_LEN); + SKW_SET(entry->status, SKW_FRAG_STATUS_CHK_PN); + } + + __skb_queue_tail(&entry->skb_list, skb); +} + +/* + * if @skb is a fragment frame, start to defragment. + * return skb buffer if all fragment frames have received, else return NULL + */ +static struct sk_buff * +skw_rx_defragment(struct skw_core *skw, struct skw_iface *iface, + struct sk_buff *skb) +{ + struct sk_buff *pskb; + struct skw_frag_entry *entry; + struct skw_rx_desc *desc = skw_rx_desc_hdr(skb); + + if (likely(!desc->more_frag && !desc->frag_num)) + return skb; + + //skw_dbg("peer: %d, tid: %d, sn: %d, more frag: %d, frag num: %d\n", + // desc->peer_idx, desc->tid, desc->sn, + // desc->more_frag, desc->frag_num); + + + if (desc->frag_num == 0) { + desc->csum_valid = 0; + desc->csum = 0; + skw_frag_init_entry(iface, skb); + + return NULL; + } + + entry = skw_frag_get_entry(iface, desc->tid, desc->sn, true); + if (!entry || (entry->frag_num + 1 != desc->frag_num)) { + //TBD: the frag num increased by 2 when it is WAPI + skw_dbg("drop, entry: %d, tid: %d, sn: %d, frag num: %d\n", + entry ? entry->id : -1, desc->tid, + desc->sn, desc->frag_num); + + dev_kfree_skb(skb); + return NULL; + } + + /* check fragment frame PN if cipher is CCMP + * The PN shall be incremented in steps of 1 for constituent + * MPDUs of fragmented MSDUs and MMPDUs + */ + if (entry->status & SKW_FRAG_STATUS_CHK_PN) { + u64 last_pn, pn; + + if (skw->hw.bus == SKW_BUS_SDIO && test_bit(SKW_FLAG_FW_PN_REUSE, &skw->flags)) { + last_pn = SKW_PN_U32(entry->last_pn); + pn = SKW_PN_U32(desc->pn); + } else { + last_pn = SKW_PN_U48(entry->last_pn); + pn = SKW_PN_U48(desc->pn); + } + if (last_pn + 1 != pn) { + skw_dbg("drop frame last pn:%llu desc_pn:%llu\n", + last_pn, pn); + dev_kfree_skb(skb); + return NULL; + } + + memcpy(entry->last_pn, desc->pn, IEEE80211_CCMP_PN_LEN); + } + + entry->frag_num++; + + /* remove mac address header -- SA & DA */ + skb_pull(skb, 12); + + entry->pending_len += skb->len; + + __skb_queue_tail(&entry->skb_list, skb); + + if (desc->more_frag) + return NULL; + + pskb = __skb_dequeue(&entry->skb_list); + if (!pskb) + return NULL; + + if (skb_tailroom(pskb) < entry->pending_len) { + if (unlikely(pskb_expand_head(pskb, 0, entry->pending_len, + GFP_ATOMIC))) { + + skw_warn("drop: tailroom: %d, needed: %d\n", + skb_tailroom(pskb), entry->pending_len); + + __skb_queue_purge(&entry->skb_list); + dev_kfree_skb(pskb); + entry->status = 0; + entry->tid = SKW_INVALID_ID; + + return NULL; + } + } + + while ((skb = __skb_dequeue(&entry->skb_list))) { + /* snap unmatch */ + skw_put_skb_data(pskb, skb->data, skb->len); + dev_kfree_skb(skb); + } + + entry->status = 0; + entry->tid = SKW_INVALID_ID; + + // Remove the mic value in the final fragment when encryption is TKIP + if (iface->key_conf.skw_cipher == SKW_CIPHER_TYPE_TKIP) + skb_trim(pskb, pskb->len - 8); + + return pskb; +} + +static int skw_pn_allowed(struct skw_core *skw, struct skw_key *key, struct skw_rx_desc *desc, int queue) +{ + int ret; + u64 pn, rx_pn; + + if (!key) + return -EINVAL; + + if (skw->hw.bus == SKW_BUS_SDIO && test_bit(SKW_FLAG_FW_PN_REUSE, &skw->flags)) { + pn = SKW_PN_U32(desc->pn); + rx_pn = SKW_PN_U32(key->rx_pn[queue]); + } else { + pn = SKW_PN_U48(desc->pn); + rx_pn = SKW_PN_U48(key->rx_pn[queue]); + } + + if (pn > rx_pn) + ret = 1; + else if (pn == rx_pn) + ret = 0; + else + ret = -1; + + if (ret < 0 || (!ret && !desc->is_amsdu && pn != 0)) { + /* SKW_PN_U48(desc->pn) = 0 allow workaround some devices pn=0*/ + + /* failed that PN less than or equal to rx_pn */ + skw_warn("seq: %d, rx_pn: %llu, pn: %llu\n", + desc->sn, rx_pn, pn); + + return -EINVAL; + } + + return 0; +} + +static int skw_replay_detect(struct skw_core *skw, struct skw_iface *iface, + struct sk_buff *skb) +{ + int64_t ret = 0; + int key_idx, queue = -1; + struct skw_key *key; + struct skw_key_conf *conf; + struct skw_peer_ctx *ctx; + struct skw_rx_desc *desc = skw_rx_desc_hdr(skb); + + if (SKW_SKB_RXCB(skb)->skip_replay_detect) + return 0; + + // fixme: + ctx = &skw->hw.lmac[iface->lmac_id].peer_ctx[desc->peer_idx]; + if (!ctx->peer) + return -EINVAL; + + if (desc->is_mc_addr) { + return 0; +#if 0 + conf = &iface->key_conf; + if (!conf->installed_bitmap) + conf = &ctx->peer->gtk_conf; +#endif + } else { + conf = &ctx->peer->ptk_conf; + } + + key_idx = skw_key_idx(conf->installed_bitmap); + if (key_idx == SKW_INVALID_ID) + return 0; + + switch (conf->skw_cipher) { + case SKW_CIPHER_TYPE_CCMP: + case SKW_CIPHER_TYPE_CCMP_256: + case SKW_CIPHER_TYPE_GCMP: + case SKW_CIPHER_TYPE_GCMP_256: + case SKW_CIPHER_TYPE_TKIP: + queue = desc->tid; + break; + + case SKW_CIPHER_TYPE_AES_CMAC: + case SKW_CIPHER_TYPE_BIP_CMAC_256: + case SKW_CIPHER_TYPE_BIP_GMAC_128: + case SKW_CIPHER_TYPE_BIP_GMAC_256: + queue = -1; + break; + + default: + queue = -1; + break; + } + + if (queue < 0) + return 0; + + rcu_read_lock(); + + key = rcu_dereference(conf->key[key_idx]); + if (key) { +#if 0 + skw_detail("inst: %d, peer: %d, tid: %d, key_idx: %d, queue: %d, pn: 0x%llx, rx pn: 0x%llx\n", + desc->inst_id, desc->peer_idx, desc->tid, key_idx, queue, + SKW_PN_U48(key->rx_pn[queue]), SKW_PN_U48(desc->pn)); +#endif + + ret = skw_pn_allowed(skw, key, desc, queue); + if (!ret) + memcpy(key->rx_pn[queue], desc->pn, SKW_PN_LEN); + } + + rcu_read_unlock(); + + return ret; +} + +static void skw_rx_handler(struct skw_core *skw, struct sk_buff_head *list) +{ + struct sk_buff *skb; + struct skw_iface *iface; + struct skw_rx_desc *desc; + struct sk_buff_head deliver_list; + + __skb_queue_head_init(&deliver_list); + + spin_lock_bh(&skw->rx_lock); + + while ((skb = __skb_dequeue(list))) { + if (SKW_SKB_RXCB(skb)->skw_created) { + dev_kfree_skb(skb); + continue; + } + + desc = skw_rx_desc_hdr(skb); + + trace_skw_rx_handler_seq(desc->sn, desc->msdu_filter); + + iface = to_skw_iface(skw, desc->inst_id); + if (iface == NULL) { + dev_kfree_skb(skb); + continue; + } + + if (skw_replay_detect(skw, iface, skb) < 0) { + dev_kfree_skb(skb); + continue; + } + + skb = skw_rx_defragment(skw, iface, skb); + if (!skb) + continue; + + skb->dev = iface->ndev; + __skb_queue_tail(&deliver_list, skb); + } + + spin_unlock_bh(&skw->rx_lock); + + while ((skb = __skb_dequeue(&deliver_list))) + skw_deliver_skb(netdev_priv(skb->dev), skb); +} + +static void skw_set_reorder_timer(struct skw_tid_rx *tid_rx, u16 sn) +{ + u16 index; + struct sk_buff_head *list; + unsigned long timeout = 0; + struct skw_reorder_rx *reorder = tid_rx->reorder; + + smp_rmb(); + + if (timer_pending(&reorder->timer) || + atomic_read(&reorder->ref_cnt) != tid_rx->ref_cnt) + return; + + index = sn % tid_rx->win_size; + list = &tid_rx->reorder_buf[index]; + if (!list || skb_queue_empty(list)) { + //skw_warn("invalid rx list, sn: %d\n", sn); + return; + } + + timeout = SKW_SKB_RXCB(skb_peek(list))->rx_time + + msecs_to_jiffies(reorder->skw->config.global.reorder_timeout); + + trace_skw_rx_set_reorder_timer(reorder->inst, reorder->peer_idx, + reorder->tid, sn, jiffies, timeout); + + if (time_before(jiffies, timeout)) { + reorder->expired.sn = sn; + reorder->expired.ref_cnt = tid_rx->ref_cnt; + mod_timer(&reorder->timer, timeout); + } else { + + spin_lock_bh(&reorder->todo.lock); + + if (!reorder->todo.actived) { + reorder->todo.seq = sn; + reorder->todo.actived = true; + reorder->todo.reason = SKW_RELEASE_EXPIRED; + if (reorder->skw->hw.bus == SKW_BUS_PCIE) { + u8 lmac_id; + + lmac_id = reorder->peer->iface->lmac_id; + skw_list_add(&reorder->skw->hw.lmac[lmac_id].rx_todo_list, + &reorder->todo.list); + } else + skw_list_add(&reorder->skw->rx_todo_list, &reorder->todo.list); + } + + spin_unlock_bh(&reorder->todo.lock); + + skw_wakeup_rx(reorder->skw); + } +} + +static inline bool is_skw_release_ready(struct sk_buff_head *list) +{ + struct sk_buff *skb = skb_peek(list); + struct skw_skb_rxcb *cb = NULL; + + if (!skb) + return false; + + cb = SKW_SKB_RXCB(skb); + if ((cb->amsdu_flags & SKW_AMSDU_FLAG_VALID) && + (cb->amsdu_bitmap != cb->amsdu_mask)) + return false; + + return true; +} + +static inline bool is_skw_msdu_timeout(struct skw_core *skw, struct sk_buff_head *list) +{ + struct sk_buff *skb; + unsigned long timeout = 0; + + skb = skb_peek(list); + if (skb) { + timeout = SKW_SKB_RXCB(skb)->rx_time + msecs_to_jiffies(skw->config.global.reorder_timeout); + if (time_after(jiffies, timeout)) + return true; + } + + return false; +} + +/* Force release frame in reorder buffer to to_sn*/ +static void skw_reorder_force_release(struct skw_tid_rx *tid_rx, + u16 to_sn, struct sk_buff_head *release_list, int reason) +{ + u16 index, target; + struct sk_buff *skb, *pskb; + + if (!tid_rx) + return; + + target = ieee80211_sn_inc(to_sn); + + smp_rmb(); + + if (timer_pending(&tid_rx->reorder->timer) && + atomic_read(&tid_rx->reorder->ref_cnt) == tid_rx->ref_cnt && + (ieee80211_sn_less(tid_rx->reorder->expired.sn, to_sn) || + ieee80211_sn_less(to_sn, tid_rx->win_start))) + del_timer(&tid_rx->reorder->timer); + + while (ieee80211_sn_less(tid_rx->win_start, target)) { + struct sk_buff_head *list; + + index = tid_rx->win_start % tid_rx->win_size; + list = &tid_rx->reorder_buf[index]; + + if (!tid_rx->stored_num) { + tid_rx->win_start = to_sn; + break; + } + + skb = skb_peek(list); + if (skb) { + if (!is_skw_release_ready(list)) { + skw_dbg("warn, seq: %d, amsdu bitmap: 0x%x\n", + skw_rx_desc_hdr(skb)->sn, + SKW_SKB_RXCB(skb)->amsdu_bitmap); + } + + if (SKW_SKB_RXCB(skb)->amsdu_flags + & SKW_AMSDU_FLAG_TAINT) { + __skb_queue_purge(list); + } else { + while ((pskb = __skb_dequeue(list))) + __skb_queue_tail(release_list, pskb); + } + + tid_rx->stored_num--; + } + + WARN_ON(!skb_queue_empty(list)); + + tid_rx->win_start = ieee80211_sn_inc(tid_rx->win_start); + + trace_skw_rx_force_release(tid_rx->reorder->inst, + tid_rx->reorder->peer_idx, + tid_rx->reorder->tid, + index, tid_rx->win_start, target, + tid_rx->stored_num, reason); + } +} + +/* + * release all ready skb in reorder buffer until a gap + * if first ready skb is timeout, release all skb in reorder buffer, + * else reset timer + */ +static void skw_reorder_release(struct skw_reorder_rx *reorder, + struct sk_buff_head *release_list) +{ + bool release = true; + + u16 i, index; + u16 win_start; + struct sk_buff *skb; + struct sk_buff_head *list; + struct skw_tid_rx *tid_rx; + + tid_rx = rcu_dereference(reorder->tid_rx); + if (!tid_rx) + return; + + win_start = tid_rx->win_start; + + for (i = 0; i < tid_rx->win_size; i++) { + if (tid_rx->stored_num == 0) { + if (timer_pending(&reorder->timer)) + del_timer(&reorder->timer); + + break; + } + + index = (win_start + i) % tid_rx->win_size; + list = &tid_rx->reorder_buf[index]; + + if (!skb_queue_len(list)) { + if (timer_pending(&reorder->timer)) + break; + + release = false; + continue; + } + + /* release timeout skb and reset reorder timer */ + if (!release) { + if (!is_skw_msdu_timeout(reorder->skw, list)) { + skw_set_reorder_timer(tid_rx, win_start + i); + break; + } + + skw_reorder_force_release(tid_rx, win_start + i, + release_list, SKW_RELEASE_EXPIRED); + release = true; + continue; + } + + if (release) { + skb = skb_peek(list); + + if (timer_pending(&reorder->timer) && + reorder->expired.sn == tid_rx->win_start) + del_timer(&reorder->timer); + + if (SKW_SKB_RXCB(skb)->amsdu_flags & SKW_AMSDU_FLAG_TAINT) { + __skb_queue_purge(list); + release = false; + continue; + } + + if (is_skw_release_ready(list) || + is_skw_msdu_timeout(reorder->skw, list)) { + struct sk_buff *pskb; + + while ((pskb = __skb_dequeue(list))) + __skb_queue_tail(release_list, pskb); + + tid_rx->win_start = ieee80211_sn_inc(tid_rx->win_start); + tid_rx->stored_num--; + + trace_skw_rx_reorder_release(reorder->inst, + reorder->peer_idx, reorder->tid, + win_start, win_start + i, + index, tid_rx->win_start, + tid_rx->stored_num); + + } else { + /* AMSDU not ready and expired */ + if (!timer_pending(&reorder->timer)) + skw_set_reorder_timer(tid_rx, win_start + i); + + break; + } + } + } +} + +static void skw_ampdu_reorder(struct skw_core *skw, struct skw_rx_desc *desc, + struct sk_buff *skb, struct sk_buff_head *release_list) +{ + u32 filter; + u16 win_start, win_size; + struct skw_ctx_entry *entry; + struct skw_tid_rx *tid_rx; + struct sk_buff_head *list = NULL; + struct sk_buff *pskb; + struct skw_peer *peer; + struct skw_reorder_rx *reorder; + bool release = false, drop = false; + const u8 snap_hdr[] = {0xAA, 0xAA, 0x03, 0x0, 0x0, 0x0}; + struct skw_skb_rxcb *cb = NULL; + struct skw_lmac *lmac = NULL; + struct ethhdr *eth_hdr = NULL; + +#define SKW_RXCB_AMSDU_LAST BIT(0) + + if (!rx_reorder_flag) { + __skb_queue_tail(release_list, skb); + return; + } + + cb = SKW_SKB_RXCB(skb); + lmac = &skw->hw.lmac[cb->lmac_id]; + entry = rcu_dereference(lmac->peer_ctx[desc->peer_idx].entry); + if (!entry) { + __skb_queue_tail(release_list, skb); + return; + } + + peer = entry->peer; + filter = atomic_read(&peer->rx_filter); + eth_hdr = (struct ethhdr *)skb->data; + if (filter && + !(filter & BIT(desc->msdu_filter & 0x1F)) && + !((htons(ETH_P_PAE) == eth_hdr->h_proto) || (htons(SKW_ETH_P_WAPI) == eth_hdr->h_proto))) { + skw_dbg("warn: rx filter: 0x%x, msdu filter: 0x%x\n", + filter, desc->msdu_filter); + + kfree_skb(skb); + return; + } + + entry->peer->rx.bytes += skb->len; + entry->peer->rx.pkts++; + + if (!desc->is_qos_data || desc->is_mc_addr) { + __skb_queue_tail(release_list, skb); + return; + } + + /* if this mpdu is fragmented, skip reorder */ + if (desc->more_frag || desc->frag_num) { + __skb_queue_tail(release_list, skb); + return; + } + + reorder = &peer->reorder[desc->tid]; + reorder->peer = peer; + tid_rx = rcu_dereference(reorder->tid_rx); + if (!tid_rx) { + __skb_queue_tail(release_list, skb); + return; + } + + win_start = tid_rx->win_start; + win_size = tid_rx->win_size; + /* case: + * frame seqence number less than window start + */ + if (ieee80211_sn_less(desc->sn, win_start)) { + if (SKW_RX_FILTER_EXCL & BIT(desc->msdu_filter & 0x1F)) { + SKW_SKB_RXCB(skb)->skip_replay_detect = 1; + __skb_queue_tail(release_list, skb); + return; + } + + skw_detail("drop: peer: %d, tid: %d, ssn: %d, seq: %d, amsdu idx: %d, filter: %d\n", + desc->peer_idx, desc->tid, win_start, + desc->sn, desc->amsdu_idx, desc->msdu_filter); + + drop = true; + goto out; + } + + /* case: + * frame sequence number exceeds window size + */ + if (!ieee80211_sn_less(desc->sn, win_start + win_size)) { + win_start = ieee80211_sn_sub(desc->sn, win_size); + + skw_reorder_force_release(tid_rx, win_start, release_list, + SKW_RELEASE_OOB); + release = true; + win_start = tid_rx->win_start; + } + + /* dup check + */ + // index = desc->sn % win_size; + list = &tid_rx->reorder_buf[desc->sn % win_size]; + pskb = skb_peek(list); + + if (desc->is_amsdu) { + struct skw_skb_rxcb *cb; + + if (!pskb) { + pskb = skb; + tid_rx->stored_num++; + } + + cb = SKW_SKB_RXCB(pskb); + if (cb->amsdu_bitmap & BIT(desc->amsdu_idx)) { + drop = true; + goto out; + } + + cb->amsdu_bitmap |= BIT(desc->amsdu_idx); + cb->amsdu_flags |= SKW_AMSDU_FLAG_VALID; + __skb_queue_tail(list, skb); + + if (desc->amsdu_first_idx && + ether_addr_equal(skb->data, snap_hdr)) { + cb->amsdu_flags |= SKW_AMSDU_FLAG_TAINT; + skw_hex_dump("attack", skb->data, 14, true); + } + + if (desc->amsdu_last_idx) { + cb->amsdu_mask = BIT(desc->amsdu_idx + 1) - 1; + cb->amsdu_bitmap |= SKW_RXCB_AMSDU_LAST; + } + + if (cb->amsdu_bitmap != cb->amsdu_mask) + goto out; + + /* amsdu ready to release */ + tid_rx->stored_num--; + + if (cb->amsdu_flags & SKW_AMSDU_FLAG_TAINT) { + __skb_queue_purge(list); + tid_rx->win_start = ieee80211_sn_inc(tid_rx->win_start); + drop = true; + skb = NULL; + + goto out; + } + + } else { + if (pskb) { + drop = true; + goto out; + } + + __skb_queue_tail(list, skb); + } + + if (desc->sn == win_start) { + while ((pskb = __skb_dequeue(list))) + __skb_queue_tail(release_list, pskb); + + if (timer_pending(&reorder->timer) && + reorder->expired.sn == tid_rx->win_start) + del_timer(&reorder->timer); + + tid_rx->win_start = ieee80211_sn_inc(tid_rx->win_start); + + release = true; + + } else { + tid_rx->stored_num++; + } + +out: + trace_skw_rx_reorder(desc->inst_id, desc->peer_idx, desc->tid, + desc->sn, desc->is_amsdu, desc->amsdu_idx, + tid_rx->win_size, tid_rx->win_start, + tid_rx->stored_num, release, drop); + + if (drop && skb) { + dev_kfree_skb(skb); + skb = NULL; + } + + if (tid_rx->stored_num) { + if (release) + skw_reorder_release(reorder, release_list); + else if (skb) + skw_set_reorder_timer(tid_rx, desc->sn); + } else { + if (timer_pending(&reorder->timer)) + del_timer(&reorder->timer); + } +} + +void skw_rx_todo(struct skw_list *todo_list) +{ + // u16 target; + LIST_HEAD(list); + struct sk_buff_head release; + struct skw_reorder_rx *reorder; + struct skw_tid_rx *tid_rx; + + if (likely(!todo_list->count)) + return; + + INIT_LIST_HEAD(&list); + __skb_queue_head_init(&release); + + spin_lock_bh(&todo_list->lock); + + list_splice_init(&todo_list->list, &list); + todo_list->count = 0; + + spin_unlock_bh(&todo_list->lock); + + + while (!list_empty(&list)) { + reorder = list_first_entry(&list, struct skw_reorder_rx, + todo.list); + + spin_lock_bh(&reorder->todo.lock); + + list_del(&reorder->todo.list); + INIT_LIST_HEAD(&reorder->todo.list); + + rcu_read_lock(); + tid_rx = rcu_dereference(reorder->tid_rx); + skw_reorder_force_release(tid_rx, reorder->todo.seq, + &release, reorder->todo.reason); + rcu_read_unlock(); + + reorder->todo.actived = false; + + spin_unlock_bh(&reorder->todo.lock); + + skw_reorder_release(reorder, &release); + + skw_rx_handler(reorder->skw, &release); + + trace_skw_rx_expired_release(reorder->inst, reorder->peer_idx, + reorder->tid, reorder->todo.seq); + } + +} + +static void skw_rx_handler_drop_info(struct skw_core *skw, struct sk_buff *pskb, + int offset, struct sk_buff_head *release_list) +{ + int i, buff_len; + int total_drop_sn; + struct sk_buff *skb; + struct skw_rx_desc *new_desc; + struct skw_drop_sn_info *sn_info; + static unsigned long j; + + total_drop_sn = *(int *)(pskb->data + offset); + buff_len = pskb->len - offset; + + if (total_drop_sn > buff_len / sizeof(*sn_info)) { + struct skw_rx_desc *desc; + int msdu_offset; + + desc = skw_rx_desc_hdr(pskb); + msdu_offset = desc->msdu_offset - + skw->hw.rx_desc.msdu_offset - + skw->hw.rx_desc.hdr_offset; + if (printk_timed_ratelimit(&j, 5000)) + skw_hex_dump("dump", pskb->data - msdu_offset, pskb->len+msdu_offset, true); + // skw_hw_assert(skw, false); + return; + } + + sn_info = (struct skw_drop_sn_info *)(pskb->data + offset + 4); + for (i = 0; i < total_drop_sn; i++) { + trace_skw_rx_data(sn_info[i].inst, sn_info[i].peer_idx, + sn_info[i].tid, 0, + sn_info[i].sn, sn_info[i].qos, + 0, sn_info[i].is_amsdu, + sn_info[i].amsdu_idx, sn_info[i].amsdu_first, + sn_info[i].amsdu_last, true); + + if (!sn_info[i].qos) + continue; + + skb = dev_alloc_skb(sizeof(struct skw_rx_desc)); + if (skb) { + SKW_SKB_RXCB(skb)->skw_created = 1; + SKW_SKB_RXCB(skb)->rx_time = jiffies; + + new_desc = skw_put_skb_zero(skb, sizeof(struct skw_rx_desc)); + new_desc->inst_id = sn_info[i].inst; + new_desc->peer_idx = sn_info[i].peer_idx; + new_desc->tid = sn_info[i].tid; + new_desc->is_qos_data = sn_info[i].qos; + new_desc->sn = sn_info[i].sn; + new_desc->is_amsdu = sn_info[i].is_amsdu; + new_desc->amsdu_idx = sn_info[i].amsdu_idx; + new_desc->amsdu_first_idx = sn_info[i].amsdu_first; + new_desc->amsdu_last_idx = sn_info[i].amsdu_last; + + rcu_read_lock(); + skw_ampdu_reorder(skw, new_desc, skb, release_list); + rcu_read_unlock(); + + skw_rx_handler(skw, release_list); + } + } +} + +static void skw_netif_monitor_rx(struct skw_core *skw, struct sk_buff *skb) +{ + struct skw_iface *iface; + struct skw_sniffer_desc *desc; + struct skw_radiotap_desc *skw_radio_desc; + u16 msdu_len; + int i; + + if (skw->hw.bus == SKW_BUS_USB) + skb_pull(skb, 12); //offset word0 ~ word2 + + desc = (struct skw_sniffer_desc *) skb->data; + + msdu_len = desc->mpdu_len; + skw_detail("sniffer mode, len = %d\n", msdu_len); + if (unlikely(!msdu_len)) { + skw_detail("strip invalid pakcet\n"); + kfree_skb(skb); + return; + } + + skw_radio_desc = (struct skw_radiotap_desc *)skb_pull(skb, + sizeof(struct skw_sniffer_desc) - sizeof(struct skw_radiotap_desc)); + + skw_radio_desc->radiotap_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + skw_radio_desc->radiotap_hdr.it_pad = 0; + skw_radio_desc->radiotap_hdr.it_len = sizeof(struct skw_radiotap_desc); + skw_radio_desc->radiotap_hdr.it_present = BIT(IEEE80211_RADIOTAP_FLAGS); + + if (skw_radio_desc->radiotap_hdr.it_present & BIT(IEEE80211_RADIOTAP_FLAGS)) + skw_radio_desc->radiotap_flag = IEEE80211_RADIOTAP_F_FCS; + + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + if (!iface || !iface->ndev) + continue; + if (iface->ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_MONITOR) + break; + } + + if (unlikely(!iface || !iface->ndev || iface->ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_MONITOR)) { + skw_err("iface not valid\n"); + kfree_skb(skb); + return; + } + + __skb_trim(skb, msdu_len + skw_radio_desc->radiotap_hdr.it_len); + + skb->dev = iface->ndev; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_80211_RAW); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) + netif_rx_ni(skb); +#else + netif_rx(skb); +#endif +} + +static void skw_rx_data_handler(struct skw_core *skw, + struct sk_buff_head *rx_list, struct skw_list *rx_todo_list) +{ + struct sk_buff_head release_list; + struct skw_rx_desc *desc; + struct sk_buff *skb; + + __skb_queue_head_init(&release_list); + + while ((skb = __skb_dequeue(rx_list))) { + int msdu_offset = 0, msdu_len = 0; + + if (is_skw_monitor_data(skw, skb->data)) { + skw_netif_monitor_rx(skw, skb); + continue; + } + + desc = (struct skw_rx_desc *)skb_pull(skb, skw->hw.rx_desc.hdr_offset); + + trace_skw_rx_data(desc->inst_id, desc->peer_idx, desc->tid, + desc->msdu_filter, desc->sn, desc->is_qos_data, + desc->retry_frame, desc->is_amsdu, + desc->amsdu_idx, desc->amsdu_first_idx, + desc->amsdu_last_idx, false); + + if (desc->tid >= SKW_NR_TID) { + skw_warn("invlid peer: %d, tid: %d\n", + desc->peer_idx, desc->tid); + + kfree_skb(skb); + continue; + } + + msdu_offset = desc->msdu_offset - + skw->hw.rx_desc.msdu_offset - + skw->hw.rx_desc.hdr_offset; + + skb_pull(skb, msdu_offset); + SKW_SKB_RXCB(skb)->rx_desc_offset = msdu_offset; + SKW_SKB_RXCB(skb)->rx_time = jiffies; + + if (BIT(desc->msdu_filter & 0x1f) & SKW_RX_FILTER_DBG) + skw_dbg("filter: %d, sn: %d, sa: %pM\n", + desc->msdu_filter, desc->sn, + skw_eth_hdr(skb)->h_source); + + if (skw->hw.bus == SKW_BUS_SDIO && test_bit(SKW_FLAG_FW_PN_REUSE, &skw->flags)) + msdu_len = *((u16 *)&desc->pn[4]) + SKW_MSDU_HDR_LEN; + else + msdu_len = desc->msdu_len + SKW_MSDU_HDR_LEN; + + if (desc->mac_drop_frag) { + int offset = round_up(msdu_len + desc->msdu_offset, 4); + + offset -= desc->msdu_offset; + skw_rx_handler_drop_info(skw, skb, offset, &release_list); + } + + skb_trim(skb, msdu_len); + + rcu_read_lock(); + + skw_ampdu_reorder(skw, desc, skb, &release_list); + + rcu_read_unlock(); + + skw_rx_handler(skw, &release_list); + + skw_rx_todo(rx_todo_list); + } +} + +static void skw_free_tid_rx(struct rcu_head *head) +{ + u16 win_end; + struct skw_tid_rx *tid_rx; + struct sk_buff_head release_list; + + tid_rx = container_of(head, struct skw_tid_rx, rcu_head); + + __skb_queue_head_init(&release_list); + win_end = ieee80211_sn_add(tid_rx->win_start, tid_rx->win_size - 1); + + rcu_read_lock(); + + skw_reorder_force_release(tid_rx, win_end, &release_list, + SKW_RELEASE_FREE); + + rcu_read_unlock(); + + skw_rx_handler(tid_rx->reorder->skw, &release_list); + + SKW_KFREE(tid_rx->reorder_buf); + SKW_KFREE(tid_rx); +} + +int skw_update_tid_rx(struct skw_peer *peer, u16 tid, u16 ssn, u16 win_size) +{ + struct skw_tid_rx *tid_rx; + struct skw_reorder_rx *reorder; + + skw_dbg("inst: %d, peer: %d, tid: %d, ssn: %d, win size: %d\n", + peer->iface->id, peer->idx, tid, ssn, win_size); + + trace_skw_rx_update_ba(peer->iface->id, peer->idx, tid, ssn); + + rcu_read_lock(); + + reorder = &peer->reorder[tid]; + tid_rx = rcu_dereference(reorder->tid_rx); + if (!tid_rx) + goto unlock; + + spin_lock_bh(&reorder->todo.lock); + + /* force to update rx todo list */ + reorder->todo.seq = ssn; + reorder->todo.reason = SKW_RELEASE_BAR; + + if (!reorder->todo.actived) { + reorder->todo.actived = true; + INIT_LIST_HEAD(&reorder->todo.list); + if (reorder->skw->hw.bus == SKW_BUS_PCIE) + skw_list_add(&reorder->skw->hw.lmac[peer->iface->lmac_id].rx_todo_list, + &reorder->todo.list); + else + skw_list_add(&reorder->skw->rx_todo_list, &reorder->todo.list); + } + + spin_unlock_bh(&reorder->todo.lock); + + skw_wakeup_rx(reorder->skw); + +unlock: + rcu_read_unlock(); + + return 0; +} + +int skw_add_tid_rx(struct skw_peer *peer, u16 tid, u16 ssn, u16 buf_size) +{ + int i; + u32 win_sz; + struct skw_tid_rx *tid_rx; + struct skw_reorder_rx *reorder; + + skw_dbg("peer: %d, tid: %d, ssn: %d, win size: %d\n", + peer->idx, tid, ssn, buf_size); + + reorder = &peer->reorder[tid]; + + tid_rx = rcu_dereference(reorder->tid_rx); + if (tid_rx) + return skw_update_tid_rx(peer, tid, ssn, buf_size); + + win_sz = buf_size > 64 ? buf_size : 64; + win_sz <<= 1; + + trace_skw_rx_add_ba(peer->iface->id, peer->idx, tid, ssn, win_sz); + + tid_rx = SKW_ZALLOC(sizeof(*tid_rx), GFP_KERNEL); + if (!tid_rx) { + skw_err("alloc failed, len: %ld\n", (long)(sizeof(*tid_rx))); + return -ENOMEM; + } + + tid_rx->reorder_buf = kcalloc(win_sz, sizeof(struct sk_buff_head), + GFP_KERNEL); + if (!tid_rx->reorder_buf) { + SKW_KFREE(tid_rx); + return -ENOMEM; + } + + for (i = 0; i < win_sz; i++) + __skb_queue_head_init(&tid_rx->reorder_buf[i]); + + tid_rx->win_start = ssn; + tid_rx->win_size = win_sz; + tid_rx->stored_num = 0; + tid_rx->reorder = reorder; + tid_rx->ref_cnt = atomic_read(&reorder->ref_cnt); + + reorder->inst = peer->iface->id; + reorder->peer_idx = peer->idx; + reorder->tid = tid; + + reorder->todo.seq = 0; + reorder->todo.actived = false; + reorder->todo.reason = SKW_RELEASE_INVALID; + + reorder->skw = peer->iface->skw; + reorder->peer = peer; + + rcu_assign_pointer(reorder->tid_rx, tid_rx); + + return 0; +} + +int skw_del_tid_rx(struct skw_peer *peer, u16 tid) +{ + struct skw_tid_rx *tid_rx; + struct skw_reorder_rx *reorder; + struct sk_buff_head release_list; + struct skw_list *todo_list; + + reorder = &peer->reorder[tid]; + + __skb_queue_head_init(&release_list); + + trace_skw_rx_del_ba(tid); + + spin_lock_bh(&reorder->lock); + tid_rx = rcu_dereference_protected(reorder->tid_rx, + lockdep_is_held(&reorder->lock)); + + if (tid_rx) { + if (reorder->skw->hw.bus == SKW_BUS_PCIE) + todo_list = &reorder->skw->hw.lmac[peer->iface->lmac_id].rx_todo_list; + else + todo_list = &reorder->skw->rx_todo_list; + + if (!list_empty(&reorder->todo.list)) + skw_list_del(todo_list, &reorder->todo.list); + } + + RCU_INIT_POINTER(reorder->tid_rx, NULL); + + atomic_inc(&reorder->ref_cnt); + + smp_wmb(); + + del_timer_sync(&reorder->timer); + + if (tid_rx) { +#ifdef CONFIG_SWT6621S_GKI_DRV + skw_call_rcu(peer->iface->skw, &tid_rx->rcu_head, skw_free_tid_rx); +#else + call_rcu(&tid_rx->rcu_head, skw_free_tid_rx); +#endif + } + + spin_unlock_bh(&reorder->lock); + + return 0; +} + +#ifdef SKW_RX_WORKQUEUE + +void skw_rx_worker(struct work_struct *work) +{ + unsigned long flags; + struct skw_core *skw; + struct sk_buff_head qlist; + + skw = container_of(work, struct skw_core, rx_worker); + __skb_queue_head_init(&qlist); + + while (skw->rx_todo_list.count || skb_queue_len(&skw->rx_dat_q)) { + + skw_rx_todo(&skw->rx_todo_list); + + if (skb_queue_empty(&skw->rx_dat_q)) + return; + + /* + * data frame format: + * RX_DESC_HEADER + ETHERNET + */ + spin_lock_irqsave(&skw->rx_dat_q.lock, flags); + skb_queue_splice_tail_init(&skw->rx_dat_q, &qlist); + spin_unlock_irqrestore(&skw->rx_dat_q.lock, flags); + + skw_rx_data_handler(skw, &qlist); + } +} + +static int __skw_rx_init(struct skw_core *skw) +{ + int cpu; + struct workqueue_attrs wq_attrs; + + skw->rx_wq = alloc_workqueue("skw_rxwq.%d", WQ_UNBOUND | __WQ_ORDERED, 1, skw->idx); + if (!skw->rx_wq) { + skw_err("alloc skwrx_workqueue failed\n"); + return -EFAULT; + } + + memset(&wq_attrs, 0, sizeof(wq_attrs)); + wq_attrs.nice = MIN_NICE; + + apply_workqueue_attrs(skw->rx_wq, &wq_attrs); + + INIT_WORK(&skw->rx_worker, skw_rx_worker); + + queue_work(skw->rx_wq, &skw->rx_worker); + + return 0; +} + +static void __skw_rx_deinit(struct skw_core *skw) +{ + atomic_set(&skw->exit, 1); + cancel_work_sync(&skw->rx_worker); + destroy_workqueue(skw->rx_wq); +} + +#else + +int skw_rx_process(struct skw_core *skw, struct sk_buff_head *rx_dat_q, struct skw_list *rx_todo_list) +{ + unsigned long flags; + struct sk_buff_head qlist; + + __skb_queue_head_init(&qlist); + while (!skb_queue_empty(rx_dat_q) || rx_todo_list->count) { + skw_rx_todo(rx_todo_list); + + //skw_dbg("enter\n"); + + /* + * data frame format: + * RX_DESC_HEADER + ETHERNET + */ + spin_lock_irqsave(&rx_dat_q->lock, flags); + skb_queue_splice_tail_init(rx_dat_q, &qlist); + spin_unlock_irqrestore(&rx_dat_q->lock, flags); + + skw_rx_data_handler(skw, &qlist, rx_todo_list); + } + + return 0; +} + +int skw_rx_poll_rx(struct napi_struct *napi, int budget) +{ + struct skw_core *skw = container_of(napi, struct skw_core, napi_rx); + + skw_rx_process(skw, &skw->rx_dat_q, &skw->rx_todo_list); + + if (skw->rx_todo_list.count) + return budget; + + napi_complete(napi); + + return 0; +} + +static int skw_rx_thread(void *data) +{ + struct skw_core *skw; + + skw = (struct skw_core *)data; + while (!kthread_should_stop()) { + skw_rx_process(skw, &skw->rx_dat_q, &skw->rx_todo_list); + + if (skb_queue_empty(&skw->rx_dat_q) && !skw->rx_todo_list.count) { + set_current_state(TASK_IDLE); + schedule_timeout(msecs_to_jiffies(1)); + } + } + + skw_info("exit\n"); + + return 0; +} + +static void *skw_thread_thread(int (*threadfn)(void *data), + void *data, const char * namefmt, u8 id) +{ + void *thread = NULL; + + thread = kthread_create(threadfn, data, namefmt, id); + if (IS_ERR(thread)) { + skw_err("create rx thread failed\n"); + + return NULL; + } + + skw_set_thread_priority(thread, SCHED_FIFO, 1); + set_user_nice(thread, MIN_NICE); + wake_up_process(thread); + + return thread; +} + +static int __skw_rx_init(struct skw_core *skw) +{ + skw->rx_thread = skw_thread_thread(skw_rx_thread, skw, "skw_rx.%d", skw->idx); + if (IS_ERR(skw->rx_thread)) { + return PTR_ERR(skw->rx_thread); + } + +#if 0 + init_dummy_netdev(&skw->dummy_dev); + skw_compat_netif_napi_add_weight(&skw->dummy_dev, &skw->napi_rx, skw_rx_poll_rx, 64); + napi_enable(&skw->napi_rx); +#endif + + return 0; +} + +static void __skw_rx_deinit(struct skw_core *skw) +{ +#if 0 + napi_disable(&skw->napi_rx); + netif_napi_del(&skw->napi_rx); +#endif + if (skw->rx_thread) { + atomic_set(&skw->exit, 1); + kthread_stop(skw->rx_thread); + skw->rx_thread = NULL; + } +} + +#endif + +static inline u8 +skw_port_to_lmacid(struct skw_core *skw, int port, bool multi_dport) +{ + int i; + + for (i = 0; i < skw->hw.nr_lmac; i++) { + if (multi_dport) { + if (skw->hw.lmac[i].dport == port) + return i; + } else { + if (skw->hw.lmac[i].lport == port) + return i; + } + } + + // BUG_ON(1); + skw_warn("invalid port: %d\n", port); + + return SKW_INVALID_ID; +} + +/* + * callback function, invoked by bsp + */ +int skw_rx_cb(int port, struct scatterlist *sglist, + int nents, void *priv) +{ + int ret; + bool rx_sdma; + void *sg_addr; + int idx, total_len; + struct sk_buff *skb; + struct scatterlist *sg; + struct skw_msg *msg; + struct skw_iface *iface; + struct skw_event_work *work; + struct skw_core *skw = (struct skw_core *)priv; + u32 *data = NULL; + u8 usb_data_port = 0; + struct skw_skb_rxcb *cb = NULL; + + rx_sdma = skw->hw_pdata->bus_type & RX_SDMA; + + for_each_sg(sglist, sg, nents, idx) { + if (sg == NULL || !sg->length) { + skw_warn("sg: 0x%p, nents: %d, idx: %d, len: %d\n", + sg, nents, idx, sg ? sg->length : 0); + break; + } + + sg_addr = sg_virt(sg); + + //Port info only stored in the first sg for USB platform + if (skw->hw.bus == SKW_BUS_USB && idx == 0) { + data = (u32 *) sg_addr; + usb_data_port = data[2] & 0x1; + //skw_dbg("usb_data_port:%d\n", usb_data_port); + } + + if (rx_sdma) { + skb = dev_alloc_skb(sg->length); + if (!skb) { + skw_err("alloc skb failed, len: %d\n", sg->length); + continue; + } + + skw_put_skb_data(skb, sg_addr, sg->length); + } else { + total_len = SKB_DATA_ALIGN(sg->length) + skw->skb_share_len; + if (unlikely(total_len > SKW_ADMA_BUFF_LEN)) { + skw_warn("sg->length: %d, rx buff: %lu, share info: %d\n", + sg->length, (long)SKW_ADMA_BUFF_LEN, skw->skb_share_len); + + skw_compat_page_frag_free(sg_addr); + continue; + } + + skb = build_skb(sg_addr, total_len); + if (!skb) { + skw_err("build skb failed, len: %d\n", total_len); + + skw_compat_page_frag_free(sg_addr); + continue; + } + + skb_put(skb, sg->length); + } + __net_timestamp(skb); + cb = SKW_SKB_RXCB(skb); + cb->rx_time = jiffies; + + trace_skw_rx_irq(nents, idx, port, sg->length); + + if (port == skw->hw_pdata->cmd_port) { + if (skw->hw.bus == SKW_BUS_SDIO) + skb_pull(skb, 4); + msg = (struct skw_msg *)skb_pull(skb, 12); + if (!msg) { + dev_kfree_skb(skb); + continue; + } + + trace_skw_msg_rx(msg->inst_id, msg->type, msg->id, + msg->seq, msg->total_len); + + switch (msg->type) { + case SKW_MSG_CMD_ACK: + skw->isr_cpu_id = raw_smp_processor_id(); + skw_cmd_ack_handler(skw, skb->data, skb->len); + kfree_skb(skb); + + break; + + case SKW_MSG_EVENT: + if (++skw->skw_event_sn != msg->seq) { + skw_warn("invalid event seq: %d, expect: %d\n", + msg->seq, skw->skw_event_sn); + + skw_hw_assert(skw, false); + kfree_skb(skb); + continue; + } + + if (msg->id == SKW_EVENT_CREDIT_UPDATE) { + skw_event_add_credit(skw, msg + 1); + smp_wmb(); + kfree_skb(skb); + + continue; + } + + iface = to_skw_iface(skw, msg->inst_id); + if (iface) + work = &iface->event_work; + else + work = &skw->event_work; + + ret = skw_queue_event_work(priv_to_wiphy(skw), + work, skb); + if (ret < 0) { + skw_err("inst: %d, drop event %d\n", + msg->inst_id, msg->id); + kfree_skb(skb); + } + + break; + + default: + skw_warn("invalid: type: %d, id: %d, seq: %d\n", + msg->type, msg->id, msg->seq); + + kfree_skb(skb); + break; + } + + } else { + if (skw->hw.bus == SKW_BUS_SDIO && !test_bit(SKW_FLAG_FW_PN_REUSE, &skw->flags)) + skb_pull(skb, 4); + + skw_data_add_credit(skw, skb->data); + + if (skw->hw.bus == SKW_BUS_USB) + cb->lmac_id = skw_port_to_lmacid(skw, usb_data_port, false); + else + cb->lmac_id = skw_port_to_lmacid(skw, port, true); + + if (cb->lmac_id == SKW_INVALID_ID) { + dev_kfree_skb(skb); + continue; + } + + //skw_dbg("lmac_id:%d\n", cb->lmac_id); + skb_queue_tail(&skw->rx_dat_q, skb); + + skw->rx_packets++; + if (skw->hw.bus == SKW_BUS_SDIO || skw->hw.bus == SKW_BUS_SDIO2) + set_cpus_allowed_ptr(skw->rx_thread, cpumask_of(task_cpu(current))); + skw->isr_cpu_id = raw_smp_processor_id(); + skw_wakeup_rx(skw); + + skw_wake_lock_timeout(skw, 400); + } + } + + return 0; +} + +int skw_register_rx_callback(struct skw_core *skw, void *cmd_cb, void *cmd_ctx, + void *dat_cb, void *dat_ctx) +{ + int i, map, ret = 0; + + if (skw->hw.bus == SKW_BUS_PCIE) + return 0; + + ret = skw_register_rx_cb(skw, skw->hw.cmd_port, cmd_cb, cmd_ctx); + if (ret < 0) { + skw_err("failed, command port: %d, ret: %d\n", + skw->hw.cmd_port, ret); + + return ret; + } + + for (map = 0, i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + int port = skw->hw.lmac[i].dport; + + if (!(skw->hw.lmac[i].flags & SKW_LMAC_FLAG_RXCB)) + continue; + + ret = skw_register_rx_cb(skw, port, dat_cb, dat_ctx); + if (ret < 0) { + skw_err("failed, data port: %d, ret: %d\n", port, ret); + + break; + } + + map |= BIT(port); + } + + skw_dbg("%s cmd port: %d, data port bitmap: 0x%x\n", + cmd_cb ? "register" : "unregister", skw->hw.cmd_port, map); + + return ret; +} + +int skw_rx_init(struct skw_core *skw) +{ + int ret; + + skw_list_init(&skw->rx_todo_list); + spin_lock_init(&skw->rx_lock); + skw_wake_lock_init(skw, 0, "skw_rx_wlock"); + + ret = skw_register_rx_callback(skw, skw_rx_cb, skw, skw_rx_cb, skw); + if (ret < 0) { + skw_err("register rx callback failed, ret: %d\n", ret); + return ret; + } + + ret = __skw_rx_init(skw); + if (ret < 0) + skw_register_rx_callback(skw, NULL, NULL, NULL, NULL); + + rx_reorder_flag = true; + skw_debugfs_file(skw->dentry, "rx_reorder", 0666, &skw_rx_reorder_fops, NULL); + + skw_debugfs_file(skw->dentry, "pn_reuse", 0666, &skw_pn_reuse_fops, skw); + + return 0; +} + +int skw_rx_deinit(struct skw_core *skw) +{ + skw_register_rx_callback(skw, NULL, NULL, NULL, NULL); + + __skw_rx_deinit(skw); + skw_rx_todo(&skw->rx_todo_list); + + skw_wake_lock_deinit(skw); + + return 0; +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_rx.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_rx.h new file mode 100755 index 0000000..1fb0721 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_rx.h @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_RX_H__ +#define __SKW_RX_H__ + +#include <net/ieee80211_radiotap.h> + +#include "skw_platform_data.h" +#include "skw_iface.h" +#include "skw_core.h" +#include "skw_tx.h" + +#define SKW_MAX_AMPDU_BUF_SIZE 0x100 /* 256 */ + +#define SKW_AMSDU_FLAG_TAINT BIT(0) +#define SKW_AMSDU_FLAG_VALID BIT(1) + +#define SKW_SDIO_RX_DESC_HDR_OFFSET 0 +#define SKW_SDIO_RX_DESC_MSDU_OFFSET 52 +#define SKW_USB_RX_DESC_HDR_OFFSET 52 +#define SKW_USB_RX_DESC_MSDU_OFFSET 0 +#define SKW_PCIE_RX_DESC_HDR_OFFSET 44 +#define SKW_PCIE_RX_DESC_MSDU_OFFSET 8 + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) +#endif + +enum SKW_RELEASE_REASON { + SKW_RELEASE_INVALID, + SKW_RELEASE_EXPIRED, + SKW_RELEASE_OOB, + SKW_RELEASE_BAR, + SKW_RELEASE_FREE, +}; + +struct skw_skb_rxcb { + unsigned long rx_time; + u16 rx_desc_offset; + u8 amsdu_bitmap; + u8 amsdu_mask; + u16 amsdu_flags; + u8 skw_created; + u8 lmac_id; + u8 skip_replay_detect; +}; + +struct skw_drop_sn_info { + u16 sn; + u8 amsdu_idx; + u8 amsdu_first: 1; + u8 amsdu_last: 1; + u8 is_amsdu: 1; + u8 qos: 1; + u8 tid: 4; + u32 peer_idx: 5; + u32 inst: 2; + u32 resved: 25; +} __packed; + +struct skw_rx_desc { + /* word 13 */ + u16 eosp:1; + u16 more_data:1; + u16 pm:1; + u16 retry_frame:1; + u16 is_eof:1; //mpdu_eof_flag + u16 ba_session:1; + u16 resv1:1; + u16 resv:1; + u16 cipher:4; + u16 snap_type:1; + u16 vlan:1; + u16 eapol:1; + u16 rcv_in_ps_mode:1; + u16 msdu_len; + + /* word 14 */ + u8 csum_valid:1; + u8 is_ampdu:1; + u8 snap_match:1; + u8 is_amsdu:1; + u8 is_qos_data:1; + u8 amsdu_first_idx:1; + u8 amsdu_last_idx:1; + u8 mpdu_sniff:1; + u16 csum; + u8 msdu_filter; + + /* word 15 */ + u16 sn:12; /* seq number */ + u16 frag_num:4; + u16 inst_id:2; //mpdu_ra_index + u16 inst_id_valid:1; + u16 more_frag:1; + u16 peer_idx:5; + u16 peer_idx_valid:1; + u16 is_mc_addr:1; //bc_mc_flag + u16 first_msdu_in_buff:1; + u16 tid:4; + + /* word 16 & word17*/ + u8 pn[6]; //u16 msdu_len; //32:47 + u8 msdu_offset; + u8 amsdu_idx:6; + u16 need_forward:1;//da_ra_diff + u16 mac_drop_frag:1; +} __packed; + +struct skw_phy_rx_desc { + /* word 8 */ + u32 lgacy_len:12; + u32 psdu_len:20; + + /* word 9 */ + u32 flock_rssi0:11; + u32 flock_rssi1:11; + u32 lp_snr0:7; + u32 nss:2; + u32 sigb_dcm:1; + + /* word 10 */ + u32 full_rssi0:11; + u32 full_rssi1:11; + u32 lp_snr1:7; + u32 sbw:3; + + /* word 11 */ + u8 agc_gain0; + u8 agc_gain1; + u8 ppdu_mode:4; + u8 dcm:1; + u8 gi_type:2; + u8 fec_coding:1; + u8 data_rate:6; + u8 ess_n_est_ss:2; + + /* word 12 */ + u16 sta_id:11; + u16 ru_size:3; + u16 he_sigb_comp:1; + u16 doppler:1; + u16 sr4:4; + u16 sr3:4; + u16 sr2:4; + u16 sr1:4; + + /* word 13 */ + u16 grp_id:6; + u16 partial_aid:9; + u16 befmed:1; + u16 top_dura:14; + u16 ltf_type:2; + + /* word 14 */ + u8 ch1_agc_gain0; + u8 ch1_agc_gain1; + u16 serv_field; + + /* word 15 */ + u32 sfo_ppm_init:24; + u32 plcp_delay:8; + + /* word 16 */ + u32 slock_rssi0:11; + u32 slock_rssi2:11; + u32 nsts:2; + u32 bss_color:6; + u32 tgnf_flag0:1; + u32 tgnf_flag1:1; + + /* word 17 */ + u8 ofdma:1; + u8 mimo:1; + u8 sifb_mcs:3; + u8 pe_dur:3; + u8 user_num:7; + u8 stbc:1; + u16 mu3_nsts:3; + u16 mu2_nsts:3; + u16 mu1_nsts:3; + u16 mu0_nsts:3; + u16 ltf_num:2; + u16 mimo_ofdma:1; + u16 resv8:1; + +}; + +struct skw_sniffer_desc { + u32 resv1; + /* word 4 */ + u8 mac_hdr_proc:7; + u8 sniff_flag:1; + u8 mpdu_proc_status; + u8 buf_num_mpdu; + u8 mac_hdr_len:6; + u8 dir_data_sniff:1; + u8 resv2:1; + + /* word 5 */ + u16 mpdu_len:14; + u16 resv3:2; + u16 psdu_cnt; + + /* word 6 */ + u8 sniff_rsv_num; + u8 resv4:1; + u8 is_ampdu:1; + u8 is_amsdu:1; + u8 mpdu2host:1; + u8 mpdu_defrag:1; + u8 mpdu_uc:1; + u8 mpdu_bc:1; + u8 resv5:1; + u8 peer_lut_idx:5; + u8 peer_lut_idx_vaild:1; + u8 resv6:2; + u8 cipher:4; + u8 inst_id:2; //mpdu_ra_index + u8 inst_id_vaild:1; + u8 resv7:1; + + /* word 7 */ + u16 sniff_status:12; + u16 pad_len:4; + u16 sn:12; + u16 frag_num:4; + + /* word 8 - 17 */ + struct skw_phy_rx_desc phy_desc; +} __packed; + +struct skw_radiotap_desc { + struct ieee80211_radiotap_header radiotap_hdr; + u8 radiotap_flag; +} __packed; + +static inline void skw_snap_unmatch_handler(struct sk_buff *skb) +{ + skb_reset_mac_header(skb); + eth_hdr(skb)->h_proto = htons(skb->len & 0xffff); +} + +static inline void skw_event_add_credit(struct skw_core *skw, void *data) +{ + u16 *credit = data; + + skw_add_credit(skw, 0, *credit); + skw_add_credit(skw, 1, *(credit + 1)); +} + +static inline void skw_data_add_credit(struct skw_core *skw, void *data) +{ +} + +static inline bool is_skw_monitor_data(struct skw_core *skw, void *data) +{ + struct skw_sniffer_desc *desc = NULL; + + if (skw->hw.bus == SKW_BUS_USB) + desc = (struct skw_sniffer_desc *)((u8 *)(data + 12)); //offset word0 ~ word2 + else if (skw->hw.bus == SKW_BUS_SDIO) + desc = (struct skw_sniffer_desc *)((u8 *)(data)); + else if (skw->hw.bus == SKW_BUS_PCIE) //TODO + return false; + + if (!desc) + return -EINVAL; + + skw_detail("sniffer flag:%d\n", desc->sniff_flag); + + if (desc->sniff_flag) { + skw_detail("recv sniffer data, desc len:%ld\n", sizeof(struct skw_sniffer_desc)); + return true; + } + + return false; +} + +static inline struct skw_skb_rxcb *SKW_SKB_RXCB(struct sk_buff *skb) +{ + return (struct skw_skb_rxcb *)skb->cb; +} + +int skw_add_tid_rx(struct skw_peer *peer, u16 tid, u16 ssn, u16 buf_size); +int skw_update_tid_rx(struct skw_peer *peer, u16 tid, u16 ssn, u16 win_size); +int skw_del_tid_rx(struct skw_peer *peer, u16 tid); + +int skw_rx_process(struct skw_core *skw, + struct sk_buff_head *rx_dat_q, struct skw_list *rx_todo_list); +void skw_rx_todo(struct skw_list *todo_list); + +int skw_rx_init(struct skw_core *skw); +int skw_rx_deinit(struct skw_core *skw); +int skw_rx_cb(int port, struct scatterlist *sglist, int nents, void *priv); +int skw_register_rx_callback(struct skw_core *skw, void *cmd_cb, void *cmd_ctx, + void *dat_cb, void *dat_ctx); + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tdls.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tdls.c new file mode 100755 index 0000000..28f4d64 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tdls.c @@ -0,0 +1,1088 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/kernel.h> +#include <linux/etherdevice.h> +#include <linux/ieee80211.h> + +#include "skw_core.h" +#include "skw_cfg80211.h" +#include "skw_iface.h" +#include "skw_work.h" +#include "skw_log.h" +#include "skw_tx.h" +#include "skw_compat.h" +#include "skw_msg.h" +#include "skw_tdls.h" + +static size_t skw_skip_ie(const u8 *ies, size_t ielen, size_t pos) +{ + /* we assume a validly formed IEs buffer */ + u8 len = ies[pos + 1]; + + pos += 2 + len; + + /* the IE itself must have 255 bytes for fragments to follow */ + if (len < 255) + return pos; + + while (pos < ielen && ies[pos] == SKW_WLAN_EID_FRAGMENT) { + len = ies[pos + 1]; + pos += 2 + len; + } + + return pos; +} + +static bool skw_id_in_list(const u8 *ids, int n_ids, u8 id, bool id_ext) +{ + int i = 0; + + /* Make sure array values are legal */ + if (WARN_ON(ids[n_ids - 1] == SKW_WLAN_EID_EXTENSION)) + return false; + + while (i < n_ids) { + if (ids[i] == SKW_WLAN_EID_EXTENSION) { + if (id_ext && (ids[i + 1] == id)) + return true; + + i += 2; + continue; + } + + if (ids[i] == id && !id_ext) + return true; + + i++; + } + + return false; +} + +static size_t skw_ie_split_ric(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, + const u8 *after_ric, int n_after_ric, + size_t offset) +{ + size_t pos = offset; + + while (pos < ielen) { + u8 ext = 0; + + if (ies[pos] == SKW_WLAN_EID_EXTENSION) + ext = 2; + if ((pos + ext) >= ielen) + break; + + if (!skw_id_in_list(ids, n_ids, ies[pos + ext], + ies[pos] == SKW_WLAN_EID_EXTENSION)) + break; + + if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { + pos = skw_skip_ie(ies, ielen, pos); + + while (pos < ielen) { + if (ies[pos] == SKW_WLAN_EID_EXTENSION) + ext = 2; + else + ext = 0; + + if ((pos + ext) >= ielen) + break; + + if (!skw_id_in_list(after_ric, + n_after_ric, + ies[pos + ext], + ext == 2)) + pos = skw_skip_ie(ies, ielen, pos); + else + break; + } + } else { + pos = skw_skip_ie(ies, ielen, pos); + } + } + + return pos; +} + +static bool skw_chandef_to_operating_class(struct cfg80211_chan_def *chandef, + u8 *op_class) +{ + u8 vht_opclass; + u32 freq = chandef->center_freq1; + + if (freq >= 2412 && freq <= 2472) { + if (chandef->width > NL80211_CHAN_WIDTH_40) + return false; + + /* 2.407 GHz, channels 1..13 */ + if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 83; /* HT40+ */ + else + *op_class = 84; /* HT40- */ + } else { + *op_class = 81; + } + + return true; + } + + if (freq == 2484) { + if (chandef->width > NL80211_CHAN_WIDTH_40) + return false; + + *op_class = 82; /* channel 14 */ + return true; + } + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_80: + vht_opclass = 128; + break; + case NL80211_CHAN_WIDTH_160: + vht_opclass = 129; + break; + case NL80211_CHAN_WIDTH_80P80: + vht_opclass = 130; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_5: + return false; /* unsupported for now */ +#endif + default: + vht_opclass = 0; + break; + } + + /* 5 GHz, channels 36..48 */ + if (freq >= 5180 && freq <= 5240) { + if (vht_opclass) { + *op_class = vht_opclass; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 116; + else + *op_class = 117; + } else { + *op_class = 115; + } + + return true; + } + + /* 5 GHz, channels 52..64 */ + if (freq >= 5260 && freq <= 5320) { + if (vht_opclass) { + *op_class = vht_opclass; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 119; + else + *op_class = 120; + } else { + *op_class = 118; + } + + return true; + } + + /* 5 GHz, channels 100..144 */ + if (freq >= 5500 && freq <= 5720) { + if (vht_opclass) { + *op_class = vht_opclass; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 122; + else + *op_class = 123; + } else { + *op_class = 121; + } + + return true; + } + + /* 5 GHz, channels 149..169 */ + if (freq >= 5745 && freq <= 5845) { + if (vht_opclass) { + *op_class = vht_opclass; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + if (freq > chandef->chan->center_freq) + *op_class = 126; + else + *op_class = 127; + } else if (freq <= 5805) { + *op_class = 124; + } else { + *op_class = 125; + } + + return true; + } + + /* 56.16 GHz, channel 1..4 */ + if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + if (chandef->width >= NL80211_CHAN_WIDTH_40) + return false; + + *op_class = 180; + return true; + } + + /* not supported yet */ + return false; +} + +static void skw_tdls_add_link_ie(struct net_device *ndev, struct sk_buff *skb, + const u8 *peer, bool initiator) +{ + struct skw_iface *iface = netdev_priv(ndev); + struct ieee80211_tdls_lnkie *lnk; + const u8 *src_addr, *dst_addr; + + if (initiator) { + src_addr = ndev->dev_addr; + dst_addr = peer; + } else { + src_addr = peer; + dst_addr = ndev->dev_addr; + } + + lnk = (struct ieee80211_tdls_lnkie *)skb_put(skb, sizeof(*lnk)); + + lnk->ie_type = WLAN_EID_LINK_ID; + lnk->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; + + memcpy(lnk->bssid, iface->sta.core.bss.bssid, ETH_ALEN); + memcpy(lnk->init_sta, src_addr, ETH_ALEN); + memcpy(lnk->resp_sta, dst_addr, ETH_ALEN); +} + +static int skw_add_srates_ie(struct net_device *ndev, struct sk_buff *skb, + bool need_basic, enum nl80211_band band) +{ + struct ieee80211_supported_band *sband; + struct skw_iface *iface = netdev_priv(ndev); + int rate, shift = 0; + u8 i, rates, *pos; + //u32 basic_rates = sdata->vif.bss_conf.basic_rates; + u32 basic_rates = 0xFFFF; + u32 rate_flags = 0; + + //shift = ieee80211_vif_get_shift(&sdata->vif); + //shift = ieee80211_vif_get_shift(&sdata->vif); + //rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); + sband = iface->wdev.wiphy->bands[band]; + rates = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + rates++; + } + if (rates > 8) + rates = 8; + + if (skb_tailroom(skb) < rates + 2) + return -ENOMEM; + + pos = skb_put(skb, rates + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = rates; + for (i = 0; i < rates; i++) { + u8 basic = 0; + + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + + if (need_basic && basic_rates & BIT(i)) + basic = 0x80; + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, + 5 * (1 << shift)); + *pos++ = basic | (u8) rate; + } + + return 0; +} + +static int skw_add_ext_srates_ie(struct net_device *ndev, + struct sk_buff *skb, bool need_basic, + enum nl80211_band band) +{ + struct ieee80211_supported_band *sband; + int rate, shift = 0; + u8 i, exrates, *pos; + //u32 basic_rates = sdata->vif.bss_conf.basic_rates; + u32 basic_rates = 0xFFFF; + u32 rate_flags = 0; + struct skw_iface *iface = netdev_priv(ndev); + + //rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); + //shift = ieee80211_vif_get_shift(&sdata->vif); + + sband = iface->wdev.wiphy->bands[band]; + exrates = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + exrates++; + } + + if (exrates > 8) + exrates -= 8; + else + exrates = 0; + + if (skb_tailroom(skb) < exrates + 2) + return -ENOMEM; + + if (exrates) { + pos = skb_put(skb, exrates + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = exrates; + for (i = 8; i < sband->n_bitrates; i++) { + u8 basic = 0; + + if ((rate_flags & sband->bitrates[i].flags) + != rate_flags) + continue; + if (need_basic && basic_rates & BIT(i)) + basic = 0x80; + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, + 5 * (1 << shift)); + *pos++ = basic | (u8) rate; + } + } + + return 0; +} + +static u8 +skw_tdls_add_subband(struct net_device *ndev, struct sk_buff *skb, + u16 start, u16 end, u16 spacing) +{ + u8 subband_cnt = 0, ch_cnt = 0; + struct ieee80211_channel *ch; + struct cfg80211_chan_def chandef; + int i, subband_start; + struct skw_iface *iface = netdev_priv(ndev); + struct wiphy *wiphy = iface->wdev.wiphy; + + for (i = start; i <= end; i += spacing) { + if (!ch_cnt) + subband_start = i; + + ch = ieee80211_get_channel(iface->wdev.wiphy, i); + if (ch) { + /* we will be active on the channel */ + cfg80211_chandef_create(&chandef, ch, + NL80211_CHAN_NO_HT); + if (skw_compat_reg_can_beacon(wiphy, &chandef, + iface->wdev.iftype)) { + ch_cnt++; + /* + * check if the next channel is also part of + * this allowed range + */ + continue; + } + } + + /* + * we've reached the end of a range, with allowed channels + * found + */ + if (ch_cnt) { + u8 *pos = skb_put(skb, 2); + *pos++ = skw_freq_to_chn(subband_start); + *pos++ = ch_cnt; + + subband_cnt++; + ch_cnt = 0; + } + } + + /* all channels in the requested range are allowed - add them here */ + if (ch_cnt) { + u8 *pos = skb_put(skb, 2); + *pos++ = skw_freq_to_chn(subband_start); + *pos++ = ch_cnt; + + subband_cnt++; + } + + return subband_cnt; +} + +static void +skw_tdls_add_supp_channels(struct net_device *ndev, struct sk_buff *skb) +{ + /* + * Add possible channels for TDLS. These are channels that are allowed + * to be active. + */ + u8 subband_cnt; + u8 *pos = skb_put(skb, 2); + + *pos++ = WLAN_EID_SUPPORTED_CHANNELS; + + /* + * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as + * this doesn't happen in real world scenarios. + */ + + /* 2GHz, with 5MHz spacing */ + subband_cnt = skw_tdls_add_subband(ndev, skb, 2412, 2472, 5); + + /* 5GHz, with 20MHz spacing */ + subband_cnt += skw_tdls_add_subband(ndev, skb, 5000, 5825, 20); + + /* length */ + *pos = 2 * subband_cnt; +} + +static void skw_tdls_add_ext_capab(struct net_device *ndev, + struct sk_buff *skb) +{ + u8 cap; + //struct ieee80211_supported_band *sband; + //struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + //bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) && + //!ifmgd->tdls_wider_bw_prohibited; + //bool buffer_sta = ieee80211_hw_check(&local->hw, + // SUPPORTS_TDLS_BUFFER_STA); +#ifdef WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED + struct skw_iface *iface = netdev_priv(ndev); + enum nl80211_band band = iface->sta.core.bss.channel->band; + struct ieee80211_supported_band *sband = iface->wdev.wiphy->bands[band]; + bool vht = sband && sband->vht_cap.vht_supported; + bool wider_band = false; +#endif + u8 *pos = skb_put(skb, 10); + + *pos++ = WLAN_EID_EXT_CAPABILITY; + *pos++ = 8; /* len */ + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + + cap = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + cap |= WLAN_EXT_CAPA4_TDLS_BUFFER_STA; + + if (iface->wdev.wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) + cap |= WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH; +#endif + *pos++ = cap; + *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; + *pos++ = 0; + *pos++ = 0; +#ifdef WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED + *pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0; +#else + *pos++ = 0; +#endif +} + +/** + * @brief append wmm ie + * + * @param skb A pointer to sk_buff structure + * @param wmm_type SKW_WMM_TYPE_INFO/SKW_WMM_TYPE_PARAMETER + * @param pQosInfo A pointer to qos info + * + * @return N/A + */ +static void +skw_add_wmm_ie(struct skw_iface *iface, struct sk_buff *skb, + u8 wmm_type, u8 *pQosInfo) +{ + u8 wmmInfoElement[] = { 0x00, 0x50, 0xf2, 0x02, 0x00, 0x01 }; + u8 wmmParamElement[] = { 0x00, 0x50, 0xf2, 0x02, 0x01, 0x01}; + + u8 qosInfo = 0x0; + u8 reserved = 0; + u8 wmmParamIe_len = 24; + u8 wmmInfoIe_len = 7; + u8 len = 0; + u8 *pos; + + if (skb_tailroom(skb) < wmmParamIe_len + 2) + return; + + qosInfo = (pQosInfo == NULL) ? 0xf : (*pQosInfo); + + /*wmm parameter */ + if (wmm_type == SKW_WMM_TYPE_PARAMETER) { + pos = skb_put(skb, wmmParamIe_len + 2); + len = wmmParamIe_len; + } else { + pos = skb_put(skb, wmmInfoIe_len + 2); + len = wmmInfoIe_len; + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = len; + + /*wmm parameter */ + if (wmm_type == SKW_WMM_TYPE_PARAMETER) { + memcpy(pos, wmmParamElement, sizeof(wmmParamElement)); + pos += sizeof(wmmParamElement); + } else { + memcpy(pos, wmmInfoElement, sizeof(wmmInfoElement)); + pos += sizeof(wmmInfoElement); + } + *pos++ = qosInfo; + + /* wmm parameter */ + if (wmm_type == SKW_WMM_TYPE_PARAMETER) { + *pos++ = reserved; + /* Use the same WMM AC parameters as STA for TDLS link */ + memcpy(pos, &iface->wmm.ac[0], sizeof(struct skw_ac_param)); + pos += sizeof(struct skw_ac_param); + memcpy(pos, &iface->wmm.ac[1], sizeof(struct skw_ac_param)); + pos += sizeof(struct skw_ac_param); + memcpy(pos, &iface->wmm.ac[2], sizeof(struct skw_ac_param)); + pos += sizeof(struct skw_ac_param); + memcpy(pos, &iface->wmm.ac[3], sizeof(struct skw_ac_param)); + } +} + +static void +skw_tdls_add_oper_classes(struct net_device *ndev, struct sk_buff *skb) +{ + u8 *pos; + u8 op_class; + int freq; + struct skw_iface *iface = netdev_priv(ndev); + struct cfg80211_chan_def chandef; + struct ieee80211_channel *channel, *bss_chn; + + bss_chn = iface->sta.core.bss.channel; + freq = ieee80211_channel_to_frequency(bss_chn->hw_value, bss_chn->band); + channel = ieee80211_get_channel(ndev->ieee80211_ptr->wiphy, freq); + + cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); + + if (!skw_chandef_to_operating_class(&chandef, &op_class)) + return; + + pos = skb_put(skb, 4); + *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES; + *pos++ = 2; /* len */ + + *pos++ = op_class; + *pos++ = op_class; /* give current operating class as alternate too */ +} + +#if 0 +u8 *skw_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, + u16 cap) +{ + __le16 tmp; + + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + memset(pos, 0, sizeof(struct ieee80211_ht_cap)); + + /* capability flags */ + tmp = cpu_to_le16(cap); + memcpy(pos, &tmp, sizeof(u16)); + pos += sizeof(u16); + + /* AMPDU parameters */ + *pos++ = ht_cap->ampdu_factor | + (ht_cap->ampdu_density << + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); + + /* MCS set */ + memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); + pos += sizeof(ht_cap->mcs); + + /* extended capabilities */ + pos += sizeof(__le16); + + /* BF capabilities */ + pos += sizeof(__le32); + + /* antenna selection */ + pos += sizeof(u8); + + return pos; +} + +u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, + u32 cap) +{ + __le32 tmp; + + *pos++ = WLAN_EID_VHT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_vht_cap); + memset(pos, 0, sizeof(struct ieee80211_vht_cap)); + + /* capability flags */ + tmp = cpu_to_le32(cap); + memcpy(pos, &tmp, sizeof(u32)); + pos += sizeof(u32); + + /* VHT MCS set */ + memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs)); + pos += sizeof(vht_cap->vht_mcs); + + return pos; +} +#endif + +static void +skw_tdls_add_setup_start_ies(struct net_device *ndev, struct sk_buff *skb, + const u8 *peer, u32 peer_cap, u8 action_code, bool initiator, + const u8 *ies, size_t ies_len) +{ + struct ieee80211_supported_band *sband; + //struct ieee80211_sta_ht_cap ht_cap; + //struct ieee80211_sta_vht_cap vht_cap; + size_t offset = 0, noffset; + struct skw_iface *iface = netdev_priv(ndev); + //u8 *pos; + enum nl80211_band band; + + if (iface->sta.core.bss.channel) { + band = iface->sta.core.bss.channel->band; + } else { + skw_err("bss is null\n"); + return; + } + + sband = iface->wdev.wiphy->bands[band]; + if (!sband) + return; + + skw_add_srates_ie(ndev, skb, false, band); + skw_add_ext_srates_ie(ndev, skb, false, band); + skw_tdls_add_supp_channels(ndev, skb); + + /* Add any custom IEs that go before Extended Capabilities */ + if (ies_len) { + static const u8 before_ext_cap[] = { + WLAN_EID_SUPP_RATES, + WLAN_EID_COUNTRY, + WLAN_EID_EXT_SUPP_RATES, + WLAN_EID_SUPPORTED_CHANNELS, + WLAN_EID_RSN, + }; + noffset = skw_ie_split_ric(ies, ies_len, before_ext_cap, + ARRAY_SIZE(before_ext_cap), NULL, 0, offset); + skw_put_skb_data(skb, ies + offset, noffset - offset); + offset = noffset; + } + + skw_tdls_add_ext_capab(ndev, skb); + + /* add the QoS element if we support it */ + if (action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES) + skw_add_wmm_ie(iface, skb, SKW_WMM_TYPE_INFO, NULL); + + /* add any custom IEs that go before HT capabilities */ + if (ies_len) { + static const u8 before_ht_cap[] = { + WLAN_EID_SUPP_RATES, + WLAN_EID_COUNTRY, + WLAN_EID_EXT_SUPP_RATES, + WLAN_EID_SUPPORTED_CHANNELS, + WLAN_EID_RSN, + WLAN_EID_EXT_CAPABILITY, + WLAN_EID_QOS_CAPA, + WLAN_EID_FAST_BSS_TRANSITION, + WLAN_EID_TIMEOUT_INTERVAL, + WLAN_EID_SUPPORTED_REGULATORY_CLASSES, + }; + noffset = skw_ie_split_ric(ies, ies_len, before_ht_cap, + ARRAY_SIZE(before_ht_cap), NULL, 0, offset); + skw_put_skb_data(skb, ies + offset, noffset - offset); + offset = noffset; + } + + skw_tdls_add_oper_classes(ndev, skb); + skw_tdls_add_link_ie(ndev, skb, peer, initiator); + + /* add any remaining IEs */ + if (ies_len) { + noffset = ies_len; + skw_put_skb_data(skb, ies + offset, noffset - offset); + } +} + +static void +skw_tdls_add_setup_cfm_ies(struct net_device *ndev, + struct sk_buff *skb, const u8 *peer, + u32 peer_cap, bool initiator, + const u8 *extra_ies, size_t extra_ies_len) +{ + struct skw_iface *iface = netdev_priv(ndev); + size_t offset = 0, noffset; + struct ieee80211_supported_band *sband = NULL; + enum nl80211_band band; + + band = iface->sta.core.bss.channel->band; + sband = iface->wdev.wiphy->bands[band]; + + if (!sband) + return; + + /* add any custom IEs that go before the QoS IE */ + if (extra_ies_len) { + static const u8 before_qos[] = { + WLAN_EID_RSN, + }; + noffset = skw_ie_split_ric(extra_ies, extra_ies_len, + before_qos, + ARRAY_SIZE(before_qos), + NULL, 0, + offset); + skw_put_skb_data(skb, extra_ies + offset, noffset - offset); + offset = noffset; + } + /* add the QoS param IE if both the peer and we support it */ + if (peer_cap & SKW_TDLS_PEER_WMM) + skw_add_wmm_ie(iface, skb, SKW_WMM_TYPE_PARAMETER, NULL); + + /* add any custom IEs that go before HT operation */ + if (extra_ies_len) { + static const u8 before_ht_op[] = { + WLAN_EID_RSN, + WLAN_EID_QOS_CAPA, + WLAN_EID_FAST_BSS_TRANSITION, + WLAN_EID_TIMEOUT_INTERVAL, + }; + noffset = skw_ie_split_ric(extra_ies, extra_ies_len, + before_ht_op, + ARRAY_SIZE(before_ht_op), + NULL, 0, + offset); + skw_put_skb_data(skb, extra_ies + offset, noffset - offset); + offset = noffset; + } + + skw_tdls_add_link_ie(ndev, skb, peer, initiator); + + /* add any remaining IEs */ + if (extra_ies_len) { + noffset = extra_ies_len; + skw_put_skb_data(skb, extra_ies + offset, noffset - offset); + } +} + +static void skw_tdls_add_chan_switch_req_ies(struct net_device *ndev, + struct sk_buff *skb, const u8 *peer, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len, u8 oper_class, + struct cfg80211_chan_def *chandef) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + struct ieee80211_tdls_data *tf; + size_t offset = 0, noffset; + + if (WARN_ON_ONCE(!chandef)) + return; + + tf = (void *)skb->data; + tf->u.chan_switch_req.target_channel = + skw_freq_to_chn(chandef->chan->center_freq); + tf->u.chan_switch_req.oper_class = oper_class; + + if (extra_ies_len) { + static const u8 before_lnkie[] = { + WLAN_EID_SECONDARY_CHANNEL_OFFSET, + }; + noffset = skw_ie_split_ric(extra_ies, extra_ies_len, + before_lnkie, + ARRAY_SIZE(before_lnkie), + NULL, 0, + offset); + skw_put_skb_data(skb, extra_ies + offset, noffset - offset); + offset = noffset; + } + + skw_tdls_add_link_ie(ndev, skb, peer, initiator); + + /* add any remaining IEs */ + if (extra_ies_len) { + noffset = extra_ies_len; + skw_put_skb_data(skb, extra_ies + offset, noffset - offset); + } +#endif +} + +static void skw_tdls_add_ies(struct net_device *ndev, struct sk_buff *skb, + const u8 *peer, u8 action, u16 status_code, u32 peer_cap, + bool initiator, const u8 *ies, size_t ies_len) +{ + + switch (action) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + if (status_code == 0) + skw_tdls_add_setup_start_ies(ndev, skb, peer, peer_cap, + action, initiator, ies, ies_len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + if (status_code == 0) + skw_tdls_add_setup_cfm_ies(ndev, skb, peer, peer_cap, + initiator, ies, ies_len); + break; + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + if (ies_len) + skw_put_skb_data(skb, ies, ies_len); + + if (status_code == 0 || action == WLAN_TDLS_TEARDOWN) + skw_tdls_add_link_ie(ndev, skb, peer, initiator); + break; + case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: + skw_tdls_add_chan_switch_req_ies(ndev, skb, peer, + initiator, ies, ies_len, 0, NULL); + break; + default: + break; + } +} + +static int +skw_tdls_build_send_encap_data(struct net_device *ndev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_cap, struct sk_buff *skb, + bool initiator, const u8 *ies, size_t ies_len) +{ + int offset; + struct ieee80211_tdls_data *td = NULL; + + offset = offsetof(struct ieee80211_tdls_data, u); + td = (struct ieee80211_tdls_data *)skb_put(skb, offset); + + memcpy(td->da, peer, ETH_ALEN); + memcpy(td->sa, ndev->dev_addr, ETH_ALEN); + td->ether_type = cpu_to_be16(ETH_P_TDLS); + td->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + skb_set_network_header(skb, ETH_HLEN); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + td->category = WLAN_CATEGORY_TDLS; + td->action_code = WLAN_TDLS_SETUP_REQUEST; + + skb_put(skb, sizeof(td->u.setup_req)); + td->u.setup_req.dialog_token = dialog_token; + td->u.setup_req.capability = 0; + break; + + case WLAN_TDLS_SETUP_RESPONSE: + td->category = WLAN_CATEGORY_TDLS; + td->action_code = WLAN_TDLS_SETUP_RESPONSE; + + skb_put(skb, sizeof(td->u.setup_resp)); + td->u.setup_resp.status_code = cpu_to_le16(status_code); + td->u.setup_resp.dialog_token = dialog_token; + + td->u.setup_resp.capability = 0; + break; + + case WLAN_TDLS_SETUP_CONFIRM: + td->category = WLAN_CATEGORY_TDLS; + td->action_code = WLAN_TDLS_SETUP_CONFIRM; + + skb_put(skb, sizeof(td->u.setup_cfm)); + td->u.setup_cfm.status_code = cpu_to_le16(status_code); + td->u.setup_cfm.dialog_token = dialog_token; + break; + + case WLAN_TDLS_TEARDOWN: + td->category = WLAN_CATEGORY_TDLS; + td->action_code = WLAN_TDLS_TEARDOWN; + + skb_put(skb, sizeof(td->u.teardown)); + td->u.teardown.reason_code = cpu_to_le16(status_code); + break; + + case WLAN_TDLS_DISCOVERY_REQUEST: + td->category = WLAN_CATEGORY_TDLS; + td->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + + skb_put(skb, sizeof(td->u.discover_req)); + td->u.discover_req.dialog_token = dialog_token; + break; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: + td->category = WLAN_CATEGORY_TDLS; + td->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; + + skb_put(skb, sizeof(td->u.chan_switch_req)); + break; + + case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: + td->category = WLAN_CATEGORY_TDLS; + td->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; + + skb_put(skb, sizeof(td->u.chan_switch_resp)); + td->u.chan_switch_resp.status_code = cpu_to_le16(status_code); + break; +#endif + + default: + return -EINVAL; + } + + skw_tdls_add_ies(ndev, skb, peer, action_code, status_code, peer_cap, + initiator, ies, ies_len); + + return dev_queue_xmit(skb); +} + +static int +skw_tdls_build_send_direct(struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb, bool initiator, + const u8 *ies, size_t ies_len) +{ + struct skw_iface *iface = netdev_priv(dev); + struct skw_core *skw = iface->skw; + struct wiphy *wiphy = priv_to_wiphy(skw); + struct ieee80211_mgmt *mgmt; + int ret, total_len; + struct skw_mgmt_tx_param *param; + + skw_dbg("Enter\n"); + mgmt = skw_put_skb_zero(skb, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + skw_ether_copy(mgmt->sa, iface->addr); + skw_ether_copy(mgmt->bssid, iface->sta.core.bss.bssid); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + status_code ? 0 : (WLAN_CAPABILITY_SHORT_SLOT_TIME | + WLAN_CAPABILITY_SHORT_PREAMBLE); + break; + + default: + return -EINVAL; + } + + skw_tdls_add_ies(dev, skb, peer, action_code, status_code, 0, + initiator, ies, ies_len); + + skw_dbg("sending tdls discover response\n"); + + total_len = sizeof(*param) + skb->len; + param = SKW_ZALLOC(total_len, GFP_KERNEL); + if (!param) + return -ENOMEM; + + param->channel = 0xFF; + param->wait = 0; + param->dont_wait_for_ack = 0; + param->cookie = 0; + + memcpy(param->mgmt, skb->data, skb->len); + param->mgmt_frame_len = skb->len; + + skw_hex_dump("mgmt tx", skb->data, skb->len, false); + + down(&skw->cmd.mgmt_cmd_lock); + ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_TX_MGMT, + param, total_len, NULL, 0); + up(&skw->cmd.mgmt_cmd_lock); + + SKW_KFREE(param); + return ret; +} + +int skw_tdls_build_send_mgmt(struct skw_core *skw, struct net_device *ndev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_cap, bool initiator, + const u8 *ies, size_t ies_len) +{ + struct sk_buff *skb; + unsigned int skb_len; + int ret; + + skb_len = skw->skb_headroom + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + 50 + /* supported rates */ + 10 + /* ext capab */ + 26 + /* WMM */ + 2 + max(sizeof(struct ieee80211_ht_cap), + sizeof(struct ieee80211_ht_operation)) + + 2 + max(sizeof(struct ieee80211_vht_cap), + sizeof(struct ieee80211_vht_operation)) + + 50 + /* supported channels */ + 3 + /* 40/20 BSS coex */ + 4 + /* AID */ + 4 + /* oper classes */ + ies_len + + sizeof(struct ieee80211_tdls_lnkie); + + skw_dbg("skb_headroom: %u skb_len: %u ies_len: %lu\n", + skw->skb_headroom, skb_len, (long)ies_len); + + skb = netdev_alloc_skb(ndev, skb_len); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, skw->skb_headroom); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: + case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: + ret = skw_tdls_build_send_encap_data(ndev, peer, + action_code, dialog_token, status_code, + peer_cap, skb, initiator, ies, ies_len); + break; + + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + ret = skw_tdls_build_send_direct(ndev, peer, action_code, + dialog_token, status_code, skb, initiator, + ies, ies_len); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tdls.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tdls.h new file mode 100755 index 0000000..8e42bc5 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tdls.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_TDLS_H__ +#define __SKW_TDLS_H__ + +enum SKW_WMM_TYPE { + SKW_WMM_TYPE_INFO, + SKW_WMM_TYPE_PARAMETER, +}; + +enum SKW_TDLS_PEER_CAPA { + SKW_TDLS_PEER_HT = BIT(0), + SKW_TDLS_PEER_VHT = BIT(1), + SKW_TDLS_PEER_WMM = BIT(2), +}; + +#ifdef CONFIG_SWT6621S_TDLS +int skw_tdls_build_send_mgmt(struct skw_core *skw, struct net_device *ndev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_cap, bool initiator, + const u8 *ies, size_t ies_len); +#else +static inline int skw_tdls_build_send_mgmt(struct skw_core *skw, + struct net_device *ndev, const u8 *peer, u8 action, + u8 token, u16 status, u32 peer_capa, bool initiator, + const u8 *ies, size_t ies_len) +{ + return 0; +} + +#endif + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_timer.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_timer.c new file mode 100755 index 0000000..11421ad --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_timer.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/skbuff.h> +#include <net/cfg80211.h> + +#include "skw_core.h" +#include "skw_timer.h" +#include "skw_msg.h" +#include "skw_mlme.h" + +static int skw_timer_show(struct seq_file *seq, void *data) +{ + struct skw_timer *timer; + struct skw_core *skw = seq->private; + + seq_printf(seq, "count: %d\n", skw->timer_data.count); + + if (!skw->timer_data.count) + return 0; + + spin_lock_bh(&skw->timer_data.lock); + list_for_each_entry(timer, &skw->timer_data.list, list) { + seq_puts(seq, "\n"); + + seq_printf(seq, "name: %s\n" + "id: 0x%p\n" + "time left: %u ms\n", + timer->name, + timer->id, + jiffies_to_msecs(timer->timeout - jiffies)); + } + + spin_unlock_bh(&skw->timer_data.lock); + + return 0; +} + +static int skw_timer_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_timer_show, inode->i_private); +} + +static const struct file_operations skw_timer_fops = { + .owner = THIS_MODULE, + .open = skw_timer_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +static void skw_timer_work(struct timer_list *data) +#else +static void skw_timer_work(unsigned long data) +#endif +{ + struct skw_core *skw; + struct skw_timer *timer, *next; + LIST_HEAD(timeout_list); + + skw = container_of((void *)data, struct skw_core, timer_data.timer); + + spin_lock_bh(&skw->timer_data.lock); + list_for_each_entry_safe(timer, next, &skw->timer_data.list, list) { + if (time_before(jiffies, timer->timeout)) + break; + + list_move(&timer->list, &timeout_list); + } + + spin_unlock_bh(&skw->timer_data.lock); + + while (!list_empty(&timeout_list)) { + timer = list_first_entry(&timeout_list, struct skw_timer, list); + skw_log(SKW_TIMER, "[%s] %s: %s(id: 0x%p)\n", + SKW_TAG_TIMER, __func__, timer->name, timer->id); + list_del(&timer->list); + + timer->cb(timer->data); + skw->timer_data.count--; + + SKW_KFREE(timer); + } + + spin_lock_bh(&skw->timer_data.lock); + timer = list_first_entry_or_null(&skw->timer_data.list, struct skw_timer, list); + if (timer) + mod_timer(&skw->timer_data.timer, timer->timeout); + + spin_unlock_bh(&skw->timer_data.lock); +} + +static bool skw_timer_id_exist(struct skw_core *skw, void *id) +{ + bool result = false; + struct skw_timer *timer; + + spin_lock_bh(&skw->timer_data.lock); + + list_for_each_entry(timer, &skw->timer_data.list, list) { + if (id == timer->id) { + result = true; + break; + } + } + + spin_unlock_bh(&skw->timer_data.lock); + + return result; +} + +int skw_add_timer_work(struct skw_core *skw, const char *name, + void (*cb)(void *dat), void *data, + unsigned long timeout, void *timer_id, gfp_t flags) +{ + struct skw_timer *timer, *node; + struct list_head *head; + + if (!timer_id || !cb || !name) + return -EINVAL; + + skw_log(SKW_TIMER, "[%s] %s: %s(id: 0x%p), time out = %ld\n", + SKW_TAG_TIMER, __func__, name, timer_id, timeout); + + if (skw_timer_id_exist(skw, timer_id)) { + skw_warn("id: 0x%p exist\n", timer_id); + SKW_BUG_ON(1); + + return -EINVAL; + } + + timer = SKW_ZALLOC(sizeof(*timer), flags); + if (!timer) + return -ENOMEM; + + INIT_LIST_HEAD(&timer->list); + + timer->name = name; + timer->cb = cb; + timer->data = data; + timer->id = timer_id; + timer->timeout = msecs_to_jiffies(timeout) + jiffies + 1; + + spin_lock_bh(&skw->timer_data.lock); + head = &skw->timer_data.list; + + list_for_each_entry(node, &skw->timer_data.list, list) { + if (time_before_eq(timer->timeout, node->timeout)) { + head = &node->list; + break; + } + } + + list_add(&timer->list, head); + + skw->timer_data.count++; + node = list_first_entry(&skw->timer_data.list, struct skw_timer, list); + + mod_timer(&skw->timer_data.timer, node->timeout); + spin_unlock_bh(&skw->timer_data.lock); + + return 0; +} + +void skw_del_timer_work(struct skw_core *skw, void *timer_id) +{ + struct skw_timer *timer; + + skw_log(SKW_TIMER, "[%s] %s: id: 0x%p\n", + SKW_TAG_TIMER, __func__, timer_id); + + spin_lock_bh(&skw->timer_data.lock); + list_for_each_entry(timer, &skw->timer_data.list, list) { + if (timer->id == timer_id) { + list_del(&timer->list); + skw->timer_data.count--; + SKW_KFREE(timer); + break; + } + } + + timer = list_first_entry_or_null(&skw->timer_data.list, struct skw_timer, list); + if (timer) + mod_timer(&skw->timer_data.timer, timer->timeout); + + spin_unlock_bh(&skw->timer_data.lock); +} + +void skw_timer_init(struct skw_core *skw) +{ + // skw->timer_work.timeout = LONG_MAX; + skw->timer_data.count = 0; + + INIT_LIST_HEAD(&skw->timer_data.list); + spin_lock_init(&skw->timer_data.lock); + + // fixme: + // timer_setup(&skw->timer_data.timer, skw->timer_data.timer_work, 0); + skw_compat_setup_timer(&skw->timer_data.timer, skw_timer_work); + + skw_debugfs_file(skw->dentry, "timer", 04444, &skw_timer_fops, skw); +} + +void skw_timer_deinit(struct skw_core *skw) +{ + LIST_HEAD(flush_list); + + del_timer(&skw->timer_data.timer); + + spin_lock_bh(&skw->timer_data.lock); + list_replace_init(&skw->timer_data.list, &flush_list); + spin_unlock_bh(&skw->timer_data.lock); + + while (!list_empty(&flush_list)) { + struct skw_timer *timer = list_first_entry(&flush_list, + struct skw_timer, list); + + list_del(&timer->list); + + skw_log(SKW_TIMER, "[%s] %s: name: %s\n", + SKW_TAG_TIMER, __func__, timer->name); + + SKW_KFREE(timer); + } +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_timer.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_timer.h new file mode 100755 index 0000000..2f985eb --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_timer.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_WORK_H__ +#define __SKW_WORK_H__ + +struct skw_timer { + struct list_head list; + unsigned long timeout; + void (*cb)(void *data); + void *id; + void *data; + const char *name; +}; + +int skw_add_timer_work(struct skw_core *skw, const char *name, + void (*cb)(void *dat), void *data, + unsigned long timeout, void *timer_id, gfp_t flags); +void skw_del_timer_work(struct skw_core *skw, void *timer_id); +void skw_timer_init(struct skw_core *skw); +void skw_timer_deinit(struct skw_core *skw); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tx.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tx.c new file mode 100755 index 0000000..a47d09d --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tx.c @@ -0,0 +1,2020 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/kthread.h> +#include <linux/ip.h> +#include <linux/ctype.h> + +#include "skw_core.h" +#include "skw_tx.h" +#include "skw_msg.h" +#include "skw_iface.h" +#include "skw_edma.h" +#include "trace.h" + +#define SKW_BASE_VO 16 +#define SKW_BASE_VI 24 +#define SKW_TX_TIMEOUT 200 +#define SKW_TX_RUNING_TIMES 20 + +struct skw_tx_info { + int quota; + bool reset; + struct sk_buff_head *list; +}; + +struct skw_tx_lmac { + bool reset; + int cred; + u16 txq_map; + u16 nr_txq; + int bk_tx_limit; + int current_qlen; + int ac_reset; + int tx_count_limit; + int pending_qlen; + + struct sk_buff_head tx_list; + struct skw_tx_info tx[SKW_NR_IFACE]; +}; + +unsigned int tx_wait_time; + +static int skw_tx_time_show(struct seq_file *seq, void *data) +{ + seq_printf(seq, "current tx_wait_time = %dus\n", tx_wait_time); + return 0; +} + +static int skw_tx_time_open(struct inode *inode, struct file *file) +{ + return single_open(file, skw_tx_time_show, inode->i_private); +} + +static ssize_t skw_tx_time_write(struct file *fp, const char __user *buf, + size_t len, loff_t *offset) +{ + int i; + char cmd[32] = {0}; + unsigned int res = 0; + + for (i = 0; i < 32; i++) { + char c; + + if (get_user(c, buf)) + return -EFAULT; + + if (c == '\n' || c == '\0') + break; + + if (isdigit(c) != 0) + cmd[i] = c; + else { + skw_warn("set fail, not number\n"); + return -EFAULT; + } + buf++; + } + + if (kstrtouint(cmd, 10, &res)) + return -EFAULT; + + skw_info("set tx_wait_time = %dus\n", res); + tx_wait_time = res; + + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops skw_tx_time_fops = { + .proc_open = skw_tx_time_open, + .proc_read = seq_read, + .proc_release = single_release, + .proc_write = skw_tx_time_write, +}; +#else +static const struct file_operations skw_tx_time_fops = { + .owner = THIS_MODULE, + .open = skw_tx_time_open, + .read = seq_read, + .release = single_release, + .write = skw_tx_time_write, +}; +#endif + +#ifdef CONFIG_SWT6621S_SKB_RECYCLE +void skw_recycle_skb_free(struct skw_core *skw, struct sk_buff *skb) +{ + if (!skb) + return; + + skb->data = skb->head; + skb_reset_tail_pointer(skb); + + skb->mac_header = (typeof(skb->mac_header))~0U; + skb->transport_header = (typeof(skb->transport_header))~0U; + skb->network_header = 0; + + skb->len = 0; + + skb_reserve(skb, NET_SKB_PAD); + + skb_queue_tail(&skw->skb_recycle_qlist, skb); +} + +int skw_recycle_skb_copy(struct skw_core *skw, struct sk_buff *skb_recycle, struct sk_buff *skb) +{ + if (!skw || !skb_recycle || !skb) + return -1; + + skb_recycle->dev = skb->dev; + + skb_reserve(skb_recycle, skw->skb_headroom); + skb_put(skb_recycle, skb->len); + memcpy(skb_recycle->data, skb->data, skb->len); + + skb_set_mac_header(skb_recycle, 0); + skb_set_network_header(skb_recycle, ETH_HLEN); + skb_set_transport_header(skb_recycle, skb->transport_header - skb->mac_header); + if (skb->ip_summed == CHECKSUM_PARTIAL) { + skb_recycle->ip_summed = skb->ip_summed; + skb_recycle->csum_start = skb_headroom(skb_recycle) + skb_checksum_start_offset(skb); + } + + return 0; +} + +struct sk_buff *skw_recycle_skb_get(struct skw_core *skw) +{ + unsigned long flags; + struct sk_buff *skb = NULL; + + spin_lock_irqsave(&skw->skb_recycle_qlist.lock, flags); + if (skw->skb_recycle_qlist.qlen > 0) { + skb = skb_peek(&skw->skb_recycle_qlist); + if (skb) + __skb_unlink(skb, &skw->skb_recycle_qlist); + } + spin_unlock_irqrestore(&skw->skb_recycle_qlist.lock, flags); + + return skb; +} +#endif + +void skw_skb_kfree(struct skw_core *skw, struct sk_buff *skb) +{ +#ifdef CONFIG_SWT6621S_SKB_RECYCLE + if (1 == SKW_SKB_TXCB(skb)->recycle) + skw_recycle_skb_free(skw, skb); + else + dev_kfree_skb_any(skb); +#else + dev_kfree_skb_any(skb); +#endif +} + +int skw_pcie_cmd_xmit(struct skw_core *skw, void *data, int data_len) +{ + skw->edma.cmd_chn.hdr[0].data_len = data_len; + skw_edma_set_data(priv_to_wiphy(skw), &skw->edma.cmd_chn, data, data_len); + + return skw_edma_tx(priv_to_wiphy(skw), &skw->edma.cmd_chn, data_len); +} + +int skw_sdio_cmd_xmit(struct skw_core *skw, void *data, int data_len) +{ + int nr = 0, total_len; + + sg_init_table(skw->sgl_cmd, SKW_NR_SGL_CMD); + sg_set_buf(&skw->sgl_cmd[nr++], data, data_len); + total_len = data_len; + + skw_set_extra_hdr(skw, skw->eof_blk, skw->hw.cmd_port, skw->hw.align, 0, 1); + sg_set_buf(&skw->sgl_cmd[nr++], skw->eof_blk, skw->hw.align); + total_len += skw->hw.align; + + if (test_bit(SKW_CMD_FLAG_DISABLE_IRQ, &skw->cmd.flags)) + return skw->hw.cmd_disable_irq_xmit(skw, NULL, -1, + skw->hw.cmd_port, skw->sgl_cmd, nr, total_len); + else + return skw->hw.cmd_xmit(skw, NULL, -1, skw->hw.cmd_port, + skw->sgl_cmd, nr, total_len); +} + +int skw_usb_cmd_xmit(struct skw_core *skw, void *data, int data_len) +{ + int nr = 0, total_len; + + sg_init_table(skw->sgl_cmd, SKW_NR_SGL_CMD); + sg_set_buf(&skw->sgl_cmd[nr++], data, data_len); + total_len = data_len; + + return skw->hw.cmd_xmit(skw, NULL, -1, skw->hw.cmd_port, + skw->sgl_cmd, nr, total_len); +} + +static int skw_sync_sdma_tx(struct skw_core *skw, struct sk_buff_head *list, + int lmac_id, int port, struct scatterlist *sgl, + int nents, int tx_len) +{ + int total_len; + int ret; + struct sk_buff *skb, *tmp; + + if (!skw->hw_pdata || !skw->hw_pdata->hw_sdma_tx) + return -EINVAL; + + if (!skw->sdma_buff) { + skw_err("invalid buff\n"); + return -ENOMEM; + } + + total_len = sg_copy_to_buffer(sgl, nents, skw->sdma_buff, + skw->hw_pdata->max_buffer_size); + + ret = skw->hw_pdata->hw_sdma_tx(port, skw->sdma_buff, total_len); + if (ret < 0) + skw_err("failed, ret: %d nents:%d\n", ret, nents); + + if (list) { + if (likely(0 == ret)) + skw_sub_credit(skw, lmac_id, skb_queue_len(list)); + + skb_queue_walk_safe(list, skb, tmp) { + if (likely(0 == ret)) { + skb->dev->stats.tx_packets++; + skb->dev->stats.tx_bytes += SKW_SKB_TXCB(skb)->skb_native_len; + } else + skb->dev->stats.tx_errors++; + + __skb_unlink(skb, list); + //kfree_skb(skb); + skw_skb_kfree(skw, skb); + } + //skw_sub_credit(skw, lmac_id, skb_queue_len(list)); + //__skb_queue_purge(list); + } + + return ret; +} + +static int skw_sync_sdma_cmd_disable_irq_tx(struct skw_core *skw, + struct sk_buff_head *list, int lmac_id, int port, + struct scatterlist *sgl, int nents, int tx_len) +{ + int total_len; + + if (!skw->hw_pdata || !skw->hw_pdata->suspend_sdma_cmd) + return -EINVAL; + + if (!skw->sdma_buff) { + skw_err("invalid buff\n"); + return -ENOMEM; + } + + total_len = sg_copy_to_buffer(sgl, nents, skw->sdma_buff, + skw->hw_pdata->max_buffer_size); + + return skw->hw_pdata->suspend_sdma_cmd(port, skw->sdma_buff, total_len); +} + + +static int skw_async_sdma_tx(struct skw_core *skw, struct sk_buff_head *list, + int lmac_id, int port, struct scatterlist *sgl, + int nents, int tx_len) +{ + void *buff; + int ret, total_len; + struct sk_buff *skb, *tmp; + struct skw_sg_node *node; + + if (!skw->hw_pdata || !skw->hw_pdata->hw_sdma_tx_async) + return -EINVAL; + + tx_len += sizeof(struct skw_sg_node); + + buff = SKW_ZALLOC(tx_len, GFP_KERNEL); + if (!buff) { + skw_err("invalid buffer\n"); + return -ENOMEM; + } + + node = (struct skw_sg_node *)buff; + buff = (u8 *)buff + sizeof(struct skw_sg_node); + + total_len = sg_copy_to_buffer(sgl, nents, buff, tx_len); + node->lmac_id = lmac_id; + node->data = (void *)skw; + node->nents = nents; + ret = skw->hw_pdata->hw_sdma_tx_async(port, buff, total_len); + if (ret < 0) { + skw_err("failed, ret: %d nents:%d\n", ret, nents); + SKW_KFREE(buff); + } + + if (list) { + if (likely(0 == ret)) + skw_sub_credit(skw, lmac_id, skb_queue_len(list)); + + skb_queue_walk_safe(list, skb, tmp) { + if (likely(0 == ret)) { + skb->dev->stats.tx_packets++; + skb->dev->stats.tx_bytes += SKW_SKB_TXCB(skb)->skb_native_len; + } else + skb->dev->stats.tx_errors++; + + __skb_unlink(skb, list); + //kfree_skb(skb); + skw_skb_kfree(skw, skb); + } + + //skw_sub_credit(skw, lmac_id, skb_queue_len(list)); + //__skb_queue_purge(list); + } + + return ret; +} + +static int skw_sync_adma_tx(struct skw_core *skw, struct sk_buff_head *list, + int lmac_id, int port, struct scatterlist *sgl, + int nents, int tx_len) +{ + struct sk_buff *skb, *tmp; + int ret; + unsigned long flags; + + if (!skw->hw_pdata || !skw->hw_pdata->hw_adma_tx) + return -EINVAL; + + ret = skw->hw_pdata->hw_adma_tx(port, sgl, nents, tx_len); + trace_skw_hw_adma_tx_done(nents); + if (ret < 0) + skw_err("failed, ret: %d nents:%d\n", ret, nents); + + if (list) { + if (likely(0 == ret)) + skw_sub_credit(skw, lmac_id, skb_queue_len(list)); + else + skb_queue_walk_safe(list, skb, tmp) + SKW_SKB_TXCB(skb)->ret = 1; + + spin_lock_irqsave(&skw->kfree_skb_qlist.lock, flags); + skb_queue_splice_tail_init(list, &skw->kfree_skb_qlist); + spin_unlock_irqrestore(&skw->kfree_skb_qlist.lock, flags); + schedule_work(&skw->kfree_skb_task); + } + + return ret; +} + +static int skw_sync_adma_cmd_disable_irq_tx(struct skw_core *skw, + struct sk_buff_head *list, int lmac_id, int port, + struct scatterlist *sgl, int nents, int tx_len) +{ + if (!skw->hw_pdata || !skw->hw_pdata->suspend_adma_cmd) + return -EINVAL; + + return skw->hw_pdata->suspend_adma_cmd(port, sgl, nents, tx_len); +} + +static int skw_async_adma_tx(struct skw_core *skw, struct sk_buff_head *list, + int lmac_id, int port, struct scatterlist *sgl, + int nents, int tx_len) +{ + int ret, idx; + struct sk_buff *skb; + struct scatterlist *sg_list, *sg; + unsigned long *skb_addr, *sg_addr; + + if (!skw->hw_pdata || !skw->hw_pdata->hw_adma_tx_async) + return -EINVAL; + + if (!sgl) { + ret = -ENOMEM; + skw_err("sgl is NULL\n"); + goto out; + } + + sg_list = kcalloc(SKW_NR_SGL_DAT, sizeof(*sg_list), GFP_KERNEL); + + ret = skw->hw_pdata->hw_adma_tx_async(port, sgl, nents, tx_len); + if (unlikely(ret < 0)) { + skw_err("failed, ret: %d nents:%d\n", ret, nents); + + for_each_sg(sgl, sg, nents, idx) { + sg_addr = (unsigned long *)sg_virt(sg); + + skb_addr = sg_addr - 1; + skb = (struct sk_buff *)*skb_addr; + + skb->dev->stats.tx_errors++; + //kfree_skb(skb); + skw_skb_kfree(skw, skb); + } + + SKW_KFREE(sgl); + } else { + atomic_add(nents, &skw->txqlen_pending); + skw_sub_credit(skw, lmac_id, nents); + } + + skw->sgl_dat = sg_list; +out: + return ret; +} + +static int skw_async_adma_tx_free(int id, struct scatterlist *sg, int nents, + void *data, int status) +{ + struct skw_sg_node node = {0}; + struct skw_core *skw = data; + struct wiphy *wiphy = priv_to_wiphy(skw); + + node.sg = sg; + node.nents = nents; + node.status = status; + + skw_queue_work(wiphy, NULL, SKW_WORK_TX_FREE, &node, sizeof(node)); + + return 0; +} + +static int skw_async_sdma_tx_free(int id, void *buffer, int size, + void *data, int status) +{ + struct skw_sg_node *node; + + if (unlikely(!buffer)) { + skw_err("buffer is invalid\n"); + return 0; + } + + node = (struct skw_sg_node *) ((u8 *)buffer - sizeof(struct skw_sg_node)); + if (unlikely(status < 0)) { + skw_err("failed status:%d %p\n", status, node->data); + if (node->data == NULL) + skw_err("buffer content was broke is NULL\n"); + else + skw_add_credit((struct skw_core *)node->data, node->lmac_id, node->nents); + } + + buffer = (void *) node; + SKW_KFREE(buffer); + + return 0; +} + +int skw_sdio_xmit(struct skw_core *skw, int lmac_id, struct sk_buff_head *txq) +{ + struct sk_buff *skb, *tmp; + struct skw_lmac *lmac = &skw->hw.lmac[lmac_id]; + + skb_queue_walk_safe(txq, skb, tmp) { + int aligned; + struct skw_packet_header *extra_hdr; + + extra_hdr = (void *)skb_push(skb, SKW_EXTER_HDR_SIZE); + aligned = round_up(skb->len, skw->hw.align); + skw_set_extra_hdr(skw, extra_hdr, lmac->lport, aligned, 0, 0); + + skw_vring_set(skw, skb->data, skb->len, lmac_id); + __skb_unlink(skb, txq); + skb->dev->stats.tx_packets++; + skb->dev->stats.tx_bytes += SKW_SKB_TXCB(skb)->skb_native_len; + dev_kfree_skb_any(skb); + } + schedule_work(&skw->hw.lmac[lmac_id].dy_work); + + return 0; +} + +#if 0 +int skw_sdio_xmit(struct skw_core *skw, int lmac_id, struct sk_buff_head *txq) +{ + struct sk_buff *skb; + int nents = 0, tx_bytes = 0; + struct skw_lmac *lmac = &skw->hw.lmac[lmac_id]; + + sg_init_table(skw->sgl_dat, SKW_NR_SGL_DAT); + + skb_queue_walk(txq, skb) { + int aligned; + struct skw_packet_header *extra_hdr; + + extra_hdr = (void *)skb_push(skb, SKW_EXTER_HDR_SIZE); + + aligned = round_up(skb->len, skw->hw.align); + skw_set_extra_hdr(skw, extra_hdr, lmac->lport, aligned, 0, 0); + + sg_set_buf(&skw->sgl_dat[nents++], skb->data, aligned); + + tx_bytes += aligned; + } + + skw_set_extra_hdr(skw, skw->eof_blk, lmac->lport, skw->hw.align, 0, 1); + sg_set_buf(&skw->sgl_dat[nents++], skw->eof_blk, skw->hw.align); + tx_bytes += skw->hw.align; + skw_detail("nents:%d", nents); + + return skw->hw.dat_xmit(skw, txq, lmac_id, lmac->dport, + skw->sgl_dat, nents, tx_bytes); +} +#endif + +int skw_usb_xmit(struct skw_core *skw, int lmac_id, struct sk_buff_head *txq) +{ + struct sk_buff *skb, *tmp; + int nents = 0, tx_bytes = 0; + unsigned long *skb_addr; + struct skw_lmac *lmac = &skw->hw.lmac[lmac_id]; + + sg_init_table(skw->sgl_dat, SKW_NR_SGL_DAT); + + skb_queue_walk_safe(txq, skb, tmp) { + int aligned; + struct skw_packet_header *extra_hdr; + + extra_hdr = (void *)skb_push(skb, SKW_EXTER_HDR_SIZE); + + aligned = round_up(skb->len, skw->hw.align); + skw_set_extra_hdr(skw, extra_hdr, lmac->lport, aligned, 0, 0); + + sg_set_buf(&skw->sgl_dat[nents++], skb->data, aligned); + + skb_addr = (unsigned long *)skb_push(skb, sizeof(unsigned long)); + *skb_addr = (unsigned long)skb; + skb_pull(skb, sizeof(unsigned long)); + + tx_bytes += aligned; + + if (skw->hw.dma == SKW_ASYNC_ADMA_TX) + __skb_unlink(skb, txq); + } + + return skw->hw.dat_xmit(skw, txq, lmac_id, lmac->dport, + skw->sgl_dat, nents, tx_bytes); +} + +int skw_pcie_xmit(struct skw_core *skw, int lmac_id, struct sk_buff_head *txq) +{ + int ret, tx_bytes = 0; + unsigned long flags; + struct sk_buff *skb; + struct skw_lmac *lmac = &skw->hw.lmac[lmac_id]; + struct wiphy *wiphy = priv_to_wiphy(skw); + unsigned long *addr; + + skb_queue_walk(txq, skb) { + addr = (unsigned long *)skb_push(skb, sizeof(unsigned long)*2); + *addr = (unsigned long)skw->edma.tx_chn[lmac_id].current_node; + skb_pull(skb, 2 * sizeof(unsigned long)); + + skw_edma_set_data(wiphy, &skw->edma.tx_chn[lmac_id], + &SKW_SKB_TXCB(skb)->e, + sizeof(SKW_SKB_TXCB(skb)->e)); + + tx_bytes += round_up(skb->len, skw->hw.align); + addr = (unsigned long *)skb_push(skb, sizeof(unsigned long)); + *addr = (unsigned long)skb; + } + + skb = skb_peek(txq); + + spin_lock_irqsave(&lmac->edma_free_list.lock, flags); + skb_queue_splice_tail_init(txq, &lmac->edma_free_list); + spin_unlock_irqrestore(&lmac->edma_free_list.lock, flags); + + ret = skw_edma_tx(wiphy, &skw->edma.tx_chn[lmac_id], tx_bytes); + if (ret < 0) { + skw_err("failed, ret: %d\n", ret); + // TODO: + // release free list + } + + return ret; +} + +static inline int skw_bus_data_xmit(struct skw_core *skw, int mac_id, + struct sk_buff_head *txq_list) +{ + int ret; + + if (!skb_queue_len(txq_list)) + return 0; + + skw->tx_packets += skb_queue_len(txq_list); + + skw->dbg.dat_idx = (skw->dbg.dat_idx + 1) % skw->dbg.nr_dat; + skw->dbg.dat[skw->dbg.dat_idx].qlen = skb_queue_len(txq_list); + skw->dbg.dat[skw->dbg.dat_idx].trigger = skw_local_clock(); + + ret = skw->hw.bus_dat_xmit(skw, mac_id, txq_list); + + skw->dbg.dat[skw->dbg.dat_idx].done = skw_local_clock(); + + return ret; +} + +static inline int skw_bus_cmd_xmit(struct skw_core *skw, void *cmd, int cmd_len) +{ + int ret; + unsigned long flags = READ_ONCE(skw->flags); + + if (test_bit(SKW_CMD_FLAG_IGNORE_BLOCK_TX, &skw->cmd.flags)) + clear_bit(SKW_FLAG_BLOCK_TX, &flags); + + if (!skw_cmd_tx_allowed(flags)) { + skw_warn("cmd: %s[%d] not allowed, flags: 0x%lx, cmd flags: 0x%lx\n", + skw->cmd.name, skw->cmd.id, skw->flags, skw->cmd.flags); + + skw_abort_cmd(skw); + + return 0; + } + + skw->dbg.cmd[skw->dbg.cmd_idx].xmit = skw_local_clock(); + skw->dbg.cmd[skw->dbg.cmd_idx].loop = atomic_read(&skw->dbg.loop); + + ret = skw->hw.bus_cmd_xmit(skw, cmd, cmd_len); + + skw->dbg.cmd[skw->dbg.cmd_idx].done = skw_local_clock(); + + return ret; +} + +static inline bool is_skw_same_tcp_stream(struct sk_buff *skb, + struct sk_buff *next) +{ + return ip_hdr(skb)->saddr == ip_hdr(next)->saddr && + ip_hdr(skb)->daddr == ip_hdr(next)->daddr && + tcp_hdr(skb)->source == tcp_hdr(next)->source && + tcp_hdr(skb)->dest == tcp_hdr(next)->dest; +} + +static void skw_merge_pure_ack(struct skw_core *skw, struct sk_buff_head *ackq, + struct sk_buff_head *txq) +{ + int i, drop = 0; + struct sk_buff *skb, *tmp; + + while ((skb = __skb_dequeue_tail(ackq))) { + for (i = 0; i < ackq->qlen; i++) { + tmp = __skb_dequeue(ackq); + if (!tmp) + break; + + if (is_skw_same_tcp_stream(skb, tmp)) { + if (tcp_optlen(tmp) == 0 && + tcp_flag_word(tcp_hdr(tmp)) == TCP_FLAG_ACK) { + skw_skb_kfree(skw, tmp); + drop++; + } else { + __skb_queue_tail(txq, tmp); + } + } else { + __skb_queue_tail(ackq, tmp); + } + } + + __skb_queue_tail(txq, skb); + } +} + +static bool is_skw_peer_data_valid(struct skw_core *skw, struct sk_buff *skb) +{ + struct skw_ctx_entry *entry; + bool valid = true; + int peer_idx = SKW_SKB_TXCB(skb)->peer_idx; + int i; + + rcu_read_lock(); + for (i = 0; i < skw->hw.nr_lmac; i++) { + entry = rcu_dereference(skw->hw.lmac[i].peer_ctx[peer_idx].entry); + if (entry) { + if (entry->peer) { + entry->peer->tx.bytes += skb->len; + entry->peer->tx.pkts++; + + if (entry->peer->flags & SKW_PEER_FLAG_DEAUTHED) + valid = false; + } else { + if (is_unicast_ether_addr(eth_hdr(skb)->h_dest)) + valid = false; + } + } + } + rcu_read_unlock(); + + return valid; +} + +static inline void skw_reset_ac(struct skw_tx_lmac *txlp) +{ + int i; + + for (i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + txlp->ac_reset = 0xF; + txlp++; + } +} + +bool skw_check_txq(struct skw_core *skw) +{ + bool ret; + int ac, i; + unsigned long flags; + struct skw_iface *iface; + + ret = false; + for (ac = 0; ac < SKW_WMM_AC_MAX; ac++) { + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + if (!iface || !iface->ndev) + continue; + + if (skb_queue_len(&iface->tx_cache[ac]) != 0) { + ret = true; + goto _exit; + } + + spin_lock_irqsave(&iface->txq[ac].lock, flags); + if (skb_queue_len(&iface->txq[ac]) != 0) { + ret = true; + spin_unlock_irqrestore(&iface->txq[ac].lock, flags); + goto _exit; + + } + spin_unlock_irqrestore(&iface->txq[ac].lock, flags); + } + } + +_exit: + return ret; +} + +bool skw_tx_exit_check(struct skw_core *skw, struct skw_tx_lmac *txlp) +{ + bool ret; + int mac, credit = 0, pending_qlen = 0; + + ret = true; + + if (skw->hw.bus == SKW_BUS_USB || skw->hw.bus == SKW_BUS_USB2) { + for (mac = 0; mac < skw->hw.nr_lmac; mac++) { + if (skw_lmac_is_actived(skw, mac)) { + credit = skw_get_hw_credit(skw, mac); + if (credit > 0 && txlp[mac].pending_qlen > 0) + ret = false; + } + trace_skw_tx_exit_check(mac, credit, txlp[mac].pending_qlen); + } + } + + if (skw->hw.bus == SKW_BUS_SDIO || skw->hw.bus == SKW_BUS_SDIO2) { + for (mac = 0; mac < skw->hw.nr_lmac; mac++) { + pending_qlen += txlp[mac].pending_qlen; + } + + if (pending_qlen == 0) { + if (skw_check_txq(skw)) + ret = false; + } else + ret = false; + + if (msecs_to_jiffies(100) > (jiffies - skw->trans_start)) + ret = true; + } + + if (test_bit(SKW_CMD_FLAG_XMIT, &skw->cmd.flags)) + ret = false; + + return ret; +} + +int skw_tx_running_check(struct skw_core *skw, int times) +{ + int all_credit, mac, ret; + + ret = SKW_TX_RUNNING_EXIT; + + if (!skw_tx_allowed(READ_ONCE(skw->flags))) + return ret; + + all_credit = 0; + for (mac = 0; mac < skw->hw.nr_lmac; mac++) + if (skw_lmac_is_actived(skw, mac)) + all_credit += skw_get_hw_credit(skw, mac); + + if (all_credit == 0) { + if (skw->hw.bus == SKW_BUS_SDIO || skw->hw.bus == SKW_BUS_SDIO2) { + if (times < tx_wait_time) + ret = SKW_TX_RUNNING_RESTART; + } + } else + ret = SKW_TX_RUNNING_GO; + + return ret; +} + +void skw_cmd_do(struct skw_core *skw) +{ + if (test_and_clear_bit(SKW_CMD_FLAG_XMIT, &skw->cmd.flags)) { + skw_bus_cmd_xmit(skw, skw->cmd.data, skw->cmd.data_len); + + if (test_bit(SKW_CMD_FLAG_NO_ACK, &skw->cmd.flags)) { + set_bit(SKW_CMD_FLAG_DONE, &skw->cmd.flags); + skw->cmd.callback(skw); + } + } +} + +int skw_txq_prepare(struct skw_core *skw, struct sk_buff_head *pure_ack_list, struct skw_tx_lmac *txl, int ac) +{ + int i, qlen, ac_qlen = 0; + unsigned long flags; + struct sk_buff *skb; + struct skw_iface *iface; + struct sk_buff_head *qlist; + struct netdev_queue *txq; + struct skw_tx_lmac *txlp; + + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + // if (!iface || skw_lmac_is_actived(skw, iface->lmac_id)) + if (!iface || !iface->ndev) + continue; + + if (ac == SKW_WMM_AC_BE && iface->txq[SKW_ACK_TXQ].qlen) { + qlist = &iface->txq[SKW_ACK_TXQ]; + + __skb_queue_head_init(pure_ack_list); + + spin_lock_irqsave(&qlist->lock, flags); + skb_queue_splice_tail_init(&iface->txq[SKW_ACK_TXQ], + pure_ack_list); + spin_unlock_irqrestore(&qlist->lock, flags); + + skw_merge_pure_ack(skw, pure_ack_list, + &iface->tx_cache[ac]); + } + + qlist = &iface->txq[ac]; + if (!skb_queue_empty(qlist)) { + spin_lock_irqsave(&qlist->lock, flags); + skb_queue_splice_tail_init(qlist, + &iface->tx_cache[ac]); + spin_unlock_irqrestore(&qlist->lock, flags); + } + + if (READ_ONCE(iface->flags) & SKW_IFACE_FLAG_DEAUTH) { + while ((skb = __skb_dequeue(&iface->tx_cache[ac])) != NULL) + skw_skb_kfree(skw, skb); + } + + qlen = skb_queue_len(&iface->tx_cache[ac]); + if (qlen < SKW_TXQ_LOW_THRESHOLD) { + txq = netdev_get_tx_queue(iface->ndev, ac); + if (netif_tx_queue_stopped(txq)) { + netif_tx_start_queue(txq); + netif_schedule_queue(txq); + } + } + + if (qlen) { + txlp = &txl[iface->lmac_id]; + txlp->current_qlen += qlen; + + trace_skw_txq_prepare(iface->ndev->name, qlen); + + txlp->txq_map |= BIT(txlp->nr_txq); + + txlp->tx[txlp->nr_txq].list = &iface->tx_cache[ac]; + txlp->tx[txlp->nr_txq].reset = true; + txlp->reset = true; + if (txlp->ac_reset & BIT(ac)) { + txlp->tx[txlp->nr_txq].quota = iface->wmm.factor[ac]; + txlp->ac_reset ^= BIT(ac); + } + + txlp->nr_txq++; + ac_qlen += qlen; + } + } + + return ac_qlen; +} + +static inline bool skw_vring_check(struct skw_core *skw, struct sk_buff_head *qlist, struct skw_tx_vring *tx_vring) +{ + if (skw->hw.bus == SKW_BUS_SDIO || skw->hw.bus == SKW_BUS_SDIO2) { + if (skb_queue_len(qlist) + 1 >= skw_vring_available_count(tx_vring)) + return true; + } + + return false; +} + +#ifdef CONFIG_SWT6621S_TX_WORKQUEUE +void skw_tx_worker(struct work_struct *work) +{ + int i, ac, mac; + int base = 0; + //unsigned long flags; + int lmac_tx_capa; + //int qlen, pending_qlen = 0; + int pending_qlen = 0; + int max_tx_count_limit = 0; + struct sk_buff *skb; + //struct skw_iface *iface; + //struct sk_buff_head *qlist; + struct skw_tx_lmac txl[SKW_MAX_LMAC_SUPPORT]; + struct skw_tx_lmac *txlp; + struct sk_buff_head pure_ack_list; + //int xmit_tx_flag; + struct skw_core *skw = container_of(to_delayed_work(work), struct skw_core, tx_worker); + int times = 0, ret; + +start: + + memset(txl, 0, sizeof(txl)); + skw_reset_ac(txl); + + max_tx_count_limit = skw->hw.pkt_limit; + + /* reserve one for eof block */ + if (skw->hw.bus == SKW_BUS_SDIO) + max_tx_count_limit--; + + for (i = 0; i < skw->hw.nr_lmac; i++) { + __skb_queue_head_init(&txl[i].tx_list); + txl[i].tx_count_limit = max_tx_count_limit; + } + + while (!atomic_read(&skw->exit)) { + + // TODO: + /* CPU bind */ + /* check if frame in pending queue is timeout */ + + atomic_inc(&skw->dbg.loop); + skw_cmd_do(skw); + + pending_qlen = 0; + ret = skw_tx_running_check(skw, times++); + if (ret == SKW_TX_RUNNING_RESTART) + goto start; + else if (ret == SKW_TX_RUNNING_EXIT) { + if (skw->hw.bus == SKW_BUS_PCIE) + skw_wakeup_tx(skw, 0); + + return; + } + + for (ac = 0; ac < SKW_WMM_AC_MAX; ac++) { + int ac_qlen = 0; + + ac_qlen = skw_txq_prepare(skw, &pure_ack_list, txl, ac); + + if (!ac_qlen) + continue; + + pending_qlen += ac_qlen; + + lmac_tx_capa = 0; + + for (mac = 0; mac < skw->hw.nr_lmac; mac++) { + int credit; + + txlp = &txl[mac]; + if (!txlp->txq_map) + goto reset; + + credit = txlp->cred = skw_get_hw_credit(skw, mac); + trace_skw_get_credit(credit, mac); + if (!txlp->cred) + goto reset; + + if (txlp->reset) { + switch (ac) { + case SKW_WMM_AC_VO: + base = SKW_BASE_VO; + break; + + case SKW_WMM_AC_VI: + base = SKW_BASE_VI; + break; + + case SKW_WMM_AC_BK: + if (txlp->bk_tx_limit) { + base = min(txlp->cred, txlp->bk_tx_limit); + txlp->bk_tx_limit = 0; + } else { + base = txlp->cred; + } + + base = base / txlp->nr_txq; + break; + + default: + base = min(txlp->cred, txlp->current_qlen); + //base = base / txlp->nr_txq; + txlp->bk_tx_limit = (txlp->cred + 1) >> 1; + break; + } + + base = base ? base : 1; + txlp->reset = false; + } else + base = 0; + + trace_skw_txlp_mac(mac, txlp->cred, txlp->current_qlen, base); + + for (i = 0; txlp->txq_map != 0; i++) { + + i = i % txlp->nr_txq; + if (!(txlp->txq_map & BIT(i))) + continue; + + if (skw_vring_check(skw, &txlp->tx_list, skw->hw.lmac[mac].tx_vring)) + break; + + if (!txlp->cred) + break; + + if (txlp->tx[i].reset) { + txlp->tx[i].quota += base; + + if (txlp->tx[i].quota < 0) + txlp->tx[i].quota = 0; + + txlp->tx[i].reset = false; + } + + skb = skb_peek(txlp->tx[i].list); + if (!skb) { + txlp->txq_map ^= BIT(i); + continue; + } + + if (!is_skw_peer_data_valid(skw, skb)) { + + skw_detail("drop dest: %pM\n", + eth_hdr(skb)->h_dest); + + __skb_unlink(skb, txlp->tx[i].list); + skw_skb_kfree(skw, skb); + + continue; + } + + if (!txlp->tx_count_limit--) + break; + + if (txlp->tx[i].quota) { + txlp->tx[i].quota--; + } else { + txlp->txq_map ^= BIT(i); + continue; + } + + __skb_unlink(skb, txlp->tx[i].list); + __skb_queue_tail(&txlp->tx_list, skb); + + trace_skw_txlp_tx_list(skb->dev->name, ((struct skw_iface *)(netdev_priv(skb->dev)))->id); + + txlp->cred--; + } + + pending_qlen = pending_qlen - credit + txlp->cred; + //txlp->pending_qlen = pending_qlen; + txlp->pending_qlen = txlp->current_qlen - credit + txlp->cred; + + trace_skw_tx_info(mac, ac, credit, credit - txlp->cred, txlp->current_qlen, pending_qlen); + + if (skw_bus_data_xmit(skw, mac, &txlp->tx_list) >= 0) + skw->trans_start = jiffies; + + txlp->tx_count_limit = max_tx_count_limit; + + if (txlp->cred) + lmac_tx_capa |= BIT(mac); + +reset: + txlp->nr_txq = 0; + txlp->txq_map = 0; + txlp->current_qlen = 0; + } + + if (!lmac_tx_capa) + break; + } + + if (ac == SKW_WMM_AC_MAX) + skw_reset_ac(txl); + + if (skw_tx_exit_check(skw, txl)) { + skw_start_dev_queue(skw); + return; + } + } +} + +static void skw_kfree_skb_worker(struct work_struct *work) +{ + unsigned long flags; + struct sk_buff *skb, *tmp; + struct sk_buff_head qlist; + struct skw_core *skw = container_of(work, struct skw_core, kfree_skb_task); + + __skb_queue_head_init(&qlist); + + while (!skb_queue_empty(&skw->kfree_skb_qlist)) { + spin_lock_irqsave(&skw->kfree_skb_qlist.lock, flags); + skb_queue_splice_tail_init(&skw->kfree_skb_qlist, &qlist); + spin_unlock_irqrestore(&skw->kfree_skb_qlist.lock, flags); + + skb_queue_walk_safe(&qlist, skb, tmp) { + if (likely(0 == SKW_SKB_TXCB(skb)->ret)) { + skb->dev->stats.tx_packets++; + skb->dev->stats.tx_bytes += SKW_SKB_TXCB(skb)->skb_native_len; + } else + skb->dev->stats.tx_errors++; + + __skb_unlink(skb, &qlist); + skw_skb_kfree(skw, skb); + } + } +} + +void skw_dump_vring(struct vring *vr) +{ + int i; + u16 avail_idx = vr->avail->idx; + u16 used_idx = vr->used->idx; + u16 pending, available; + + available = vr->num - (avail_idx - used_idx); + pending = (avail_idx - used_idx) & (vr->num - 1); + + skw_dbg("===== VRING DUMP:=====\n"); + skw_dbg(" Queue Size: %u\n", vr->num); + skw_dbg(" avail->idx: %u, used->idx: %u\n", + avail_idx, used_idx); + skw_dbg(" Available descriptors: %u\n", + available); + skw_dbg(" Descriptors pending hardware processing: %u\n", pending); + + // 打印描述符表 + skw_dbg(" Descriptor Table:\n"); + for (i = 0; i < vr->num; i++) { + struct vring_desc *desc = &vr->desc[i]; + skw_dbg(" Desc[%03u]: addr=0x%016llx, len=%u, flags=0x%04x, next=%u\n", + i, (unsigned long long)desc->addr, desc->len, desc->flags, desc->next); + skw_hex_dump("desc addr", phys_to_virt(desc->addr), desc->len, true); + } + + // 打印可用环 + skw_dbg(" Available Ring (avail->idx=%u):\n", avail_idx); + for (i = 0; i < vr->num; i++) { + unsigned int idx = i % vr->num; + skw_dbg(" Avail[%03u]: desc_idx=%u\n", i, vr->avail->ring[idx]); + } + + // 打印已用环 + skw_dbg(" Used Ring (used->idx=%u):\n", used_idx); + for (i = 0; i < vr->num; i++) { + unsigned int idx = i % vr->num; + skw_dbg(" Used[%03u]: id=%u, len=%u\n", + i, vr->used->ring[idx].id, vr->used->ring[idx].len); + } +} + +u16 skw_vring_available_count(struct skw_tx_vring *tx_vring) +{ + struct vring *vr = &tx_vring->vr; + u16 avail_idx; + u16 used_idx; + + spin_lock(&tx_vring->lock); + avail_idx = vr->avail->idx; + used_idx = vr->used->idx; + spin_unlock(&tx_vring->lock); + + // 可用描述符数量 = 总容量 - (avail_idx - used_idx) + // 其中 (avail_idx - used_idx) 是已分配但未被设备处理的描述符数量 + return vr->num - ((avail_idx - used_idx) & (vr->num - 1)); +} + +u16 skw_vring_pending_count(struct skw_tx_vring *tx_vring) +{ + u16 avail_idx, used_idx; + struct vring *vr; + + vr = &tx_vring->vr; + + spin_lock(&tx_vring->lock); + avail_idx = vr->avail->idx; + used_idx = vr->used->idx; + spin_unlock(&tx_vring->lock); + + + // 利用位运算处理回绕(要求vr->num是2的幂) + return (avail_idx - used_idx) & (vr->num - 1); +} + +bool tx_vring_is_full(struct skw_tx_vring *tx_vring) +{ + struct vring *vr = &tx_vring->vr; + u16 avail_idx, used_idx; + u16 pending; + + //spin_lock(&tx_vring->lock); + avail_idx = vr->avail->idx; + used_idx = vr->used->idx; + //spin_unlock(&tx_vring->lock); + + // 计算待处理的描述符数量 + pending = (avail_idx - used_idx) & (vr->num - 1); + + // 当待处理数量达到队列大小时,表示队列已满 + return pending >= (vr->num - 1); +} + +bool vring_has_pending(struct skw_tx_vring *tx_vring) +{ + struct vring *vr; + u16 avail_idx, used_idx; + + vr = &tx_vring->vr; + + spin_lock(&tx_vring->lock); + + avail_idx = vr->avail->idx; + used_idx = vr->used->idx; + + spin_unlock(&tx_vring->lock); + + // 计算待处理描述符数量(考虑回绕) + return ((avail_idx - used_idx) & (vr->num - 1)) != 0; +} + +int skw_vring_set(struct skw_core *skw, void *data, u16 len, int lmac_id) +{ + u16 desc_idx, aligned; + struct vring *vr; + struct skw_tx_vring *tx_vring; + + tx_vring = skw->hw.lmac[lmac_id].tx_vring; + vr = &tx_vring->vr; + + // 1. 先获取锁,再检查队列状态 + spin_lock(&tx_vring->lock); + smp_rmb(); + + if (tx_vring_is_full(tx_vring)) { + spin_unlock(&tx_vring->lock); + //skw_dbg("tx_vring_is_full\n"); + return -EINVAL; + } + + // 2. 获取当前可用描述符索引(正确方式) + desc_idx = vr->avail->idx & (vr->num - 1); + + // 3. 配置描述符指向数据 + //skw_sdio_hdr_set(skw, skb); + aligned = round_up(len, skw->hw.align); + memcpy(phys_to_virt(vr->desc[desc_idx].addr), data, aligned); + vr->desc[desc_idx].len = aligned; + vr->desc[desc_idx].flags = VRING_DESC_F_WRITE; + + // 4. 更新可用环索引 + vr->avail->ring[desc_idx] = desc_idx; + + // 6. 使用内存屏障确保描述符更新先于索引更新 + smp_wmb(); + vr->avail->idx += 1; + + //atomic_inc(&tx_vring->set); + + spin_unlock(&tx_vring->lock); + + return 0; +} + +struct skw_tx_vring *skw_tx_vring_init(void) +{ + int i; + struct skw_tx_vring *tx_vring; + void *queue_mem; + + tx_vring = kcalloc(1, sizeof(struct skw_tx_vring), GFP_KERNEL); + if (!tx_vring) { + skw_dbg("alloc tx_vring fail\n"); + goto __1; + } + + tx_vring->page = alloc_pages(GFP_KERNEL | GFP_DMA, get_order(SKW_TX_PACK_SIZE*SKW_TX_VRING_SIZE)); + if (!tx_vring->page) { + skw_dbg("alloc page fail\n"); + goto __2; + } + + tx_vring->sgl_dat = kcalloc(SKW_TX_VRING_SIZE, sizeof(struct scatterlist), GFP_KERNEL); + if (!tx_vring->sgl_dat) { + skw_dbg("alloc sgl_dat fail\n"); + goto __3; + } + sg_init_table(tx_vring->sgl_dat, SKW_TX_VRING_SIZE); + tx_vring->tx_bytes = 0; + spin_lock_init(&tx_vring->lock); + + skw_dbg("size:%d\n", vring_size(SKW_TX_VRING_SIZE, PAGE_SIZE)); + queue_mem = kzalloc(vring_size(SKW_TX_VRING_SIZE, PAGE_SIZE), GFP_KERNEL); + if (!queue_mem) { + skw_dbg("Failed to allocate vring memory\n"); + goto __4; + } + skw_dbg("queue_mem:%p\n", queue_mem); + + vring_init(&tx_vring->vr, SKW_TX_VRING_SIZE, queue_mem, PAGE_SIZE); + for (i = 0; i < SKW_TX_VRING_SIZE; i++) { + tx_vring->vr.avail->ring[i] = i; // 将描述符索引 0~num-1 依次放入可用环 + tx_vring->vr.desc[i].addr = virt_to_phys(page_address(tx_vring->page)) + i * SKW_TX_PACK_SIZE; + } + tx_vring->queue_mem = queue_mem; // 记录指针以便后续释放 + + atomic_set(&tx_vring->set, 0); + atomic_set(&tx_vring->write, 0); + + return tx_vring; + +__4: + kfree(tx_vring->sgl_dat); +__3: + __free_pages(tx_vring->page, get_order(SKW_TX_PACK_SIZE*SKW_TX_VRING_SIZE)); +__2: + kfree(tx_vring); +__1: + return NULL; +} + +void skw_tx_vring_deinit(struct skw_tx_vring *tx_vring) +{ + if (tx_vring) + return ; + if (tx_vring->page) + __free_pages(tx_vring->page, get_order(SKW_TX_PACK_SIZE*SKW_TX_VRING_SIZE)); + if (tx_vring->sgl_dat) + kfree(tx_vring->sgl_dat); + + if (tx_vring->queue_mem) + kfree(tx_vring->queue_mem); + + kfree(tx_vring); + + return ; +} + +int skw_sdio_write(struct skw_core *skw, struct skw_tx_vring *tx_vring, u16 limit, int lmac_id) +{ + struct vring *vr = &tx_vring->vr; + //u16 avail_idx; + //u16 used_idx; // 使用vr->used->idx而不是last_used_idx + u16 desc_idx, data_len, used_ring_idx, used_idx, avail_idx; + u16 nents; + u32 tx_bytes; + int ret; + + // 检查是否有新的可用描述符 + if (!vring_has_pending(tx_vring)) { + skw_dbg("no avail data vr->avail->idx:%d vr->used->idx:%d\n", vr->avail->idx, vr->used->idx); + return -EINVAL; + } + + spin_lock(&tx_vring->lock); + avail_idx = vr->avail->idx; + used_idx = vr->used->idx; + + // 处理所有待处理的描述符(从used_idx到avail_idx-1) + sg_init_table(tx_vring->sgl_dat, SKW_TX_VRING_SIZE); + nents = 0; + tx_bytes = 0; + while ((used_idx & (vr->num - 1)) != (avail_idx & (vr->num - 1))) { + if (nents == limit) + break; + // 获取当前待处理的描述符索引 + desc_idx = vr->avail->ring[used_idx % vr->num]; + + // 从描述符读取数据地址和长度 + data_len = vr->desc[desc_idx].len; + + // 记录调试信息 + //printk("Processing descriptor %u: len=%u\n", desc_idx, data_len); + + // 将结果添加到已用环 + used_ring_idx = used_idx % vr->num; + vr->used->ring[used_ring_idx].id = desc_idx; + vr->used->ring[used_ring_idx].len = data_len; + + sg_set_buf(&tx_vring->sgl_dat[nents++], phys_to_virt(vr->desc[desc_idx].addr), vr->desc[desc_idx].len); + tx_bytes += vr->desc[desc_idx].len; + + // 清理已处理描述符的状态 + vr->desc[desc_idx].flags &= ~VRING_DESC_F_WRITE; + vr->desc[desc_idx].len = 0; + + // 移动到下一个待处理的描述符 + used_idx++; + + //atomic_inc(&tx_vring->write); + } + spin_unlock(&tx_vring->lock); + + skw_set_extra_hdr(skw, skw->eof_blk, skw->hw.lmac[lmac_id].lport, skw->hw.align, 0, 1); + sg_set_buf(&tx_vring->sgl_dat[nents++], skw->eof_blk, skw->hw.align); + tx_bytes += 512; + + ret = skw->hw_pdata->hw_adma_tx(skw->hw.lmac[lmac_id].dport, tx_vring->sgl_dat, nents, tx_bytes); + if (ret) + skw_err("hw_adma_tx fail ret:%d nents:%d tx_bytes:%d\n", ret, nents, tx_bytes); + + // 更新已用环索引 + spin_lock(&tx_vring->lock); + smp_wmb(); + vr->used->idx = used_idx; + spin_unlock(&tx_vring->lock); + + return ret; +} + +void skw_dy_worker(struct work_struct *work) +{ + int credit; + int pending, limit; + //int nents; + struct skw_tx_vring *tx_vring; + //struct skw_core *skw = container_of(work, struct skw_core, dy_work); + struct skw_lmac *lmac = container_of(work, struct skw_lmac, dy_work); + struct skw_core *skw = lmac->skw; + + //tx_vring = skw->tx_vring; + tx_vring = lmac->tx_vring; + while (!atomic_read(&skw->exit)) { + credit = skw_get_hw_credit(skw, lmac->id); + if (credit == 0) + break; + + //spin_lock(&tx_vring->lock); + pending = skw_vring_pending_count(tx_vring); + //spin_unlock(&tx_vring->lock); + + if (pending == 0) + break; + + limit = min(pending, credit); + + if (skw_sdio_write(skw, tx_vring, limit, lmac->id) == 0) + skw_sub_credit(skw, lmac->id, limit); + } +} + +static int __skw_tx_init(struct skw_core *skw) +{ + //struct workqueue_attrs wq_attrs; + int j; +#ifdef CONFIG_SWT6621S_SKB_RECYCLE + int i; + struct sk_buff *skb; +#endif + + skw->tx_wq = alloc_workqueue("skw_txwq.%d", + WQ_UNBOUND | WQ_CPU_INTENSIVE | WQ_HIGHPRI | WQ_SYSFS, + 0, skw->idx); + if (!skw->tx_wq) { + skw_err("alloc skwtx_workqueue failed\n"); + return -EFAULT; + } + + //memset(&wq_attrs, 0, sizeof(wq_attrs)); + + //wq_attrs.nice = MIN_NICE; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) && LINUX_VERSION_CODE <= KERNEL_VERSION(5, 2, 0) + //apply_workqueue_attrs(skw->tx_wq, &wq_attrs); +#endif + + INIT_DELAYED_WORK(&skw->tx_worker, skw_tx_worker); + queue_delayed_work(skw->tx_wq, &skw->tx_worker, msecs_to_jiffies(0)); + //queue_work_on(cpumask_last(cpu_online_mask), skw->tx_wq, &skw->tx_worker); + skw->trans_start = 0; + + skb_queue_head_init(&skw->kfree_skb_qlist); + INIT_WORK(&skw->kfree_skb_task, skw_kfree_skb_worker); + + skb_queue_head_init(&skw->skb_recycle_qlist); +#ifdef CONFIG_SWT6621S_SKB_RECYCLE + for (i = 0; i < SKW_SKB_RECYCLE_COUNT; i++) { + skb = dev_alloc_skb(SKW_2K_SIZE); + if (skb) + skb_queue_tail(&skw->skb_recycle_qlist, skb); + else { + __skb_queue_purge(&skw->skb_recycle_qlist); + destroy_workqueue(skw->tx_wq); + skw_err("alloc skb recycle failed\n"); + return -ENOMEM; + } + } +#endif + + for (j = 0; j < SKW_MAX_LMAC_SUPPORT; j++) { + INIT_WORK(&skw->hw.lmac[j].dy_work, skw_dy_worker); + skw->hw.lmac[j].tx_vring = skw_tx_vring_init(); + } + + return 0; +} + +static void __skw_tx_deinit(struct skw_core *skw) +{ + int j; + + atomic_set(&skw->exit, 1); + cancel_delayed_work_sync(&skw->tx_worker); + cancel_work_sync(&skw->kfree_skb_task); + skb_queue_purge(&skw->kfree_skb_qlist); + destroy_workqueue(skw->tx_wq); + skb_queue_purge(&skw->skb_recycle_qlist); + + for (j = 0; j < SKW_MAX_LMAC_SUPPORT; j++) { + cancel_work_sync(&skw->hw.lmac[j].dy_work); + skw_tx_vring_deinit(skw->hw.lmac[j].tx_vring); + } +} + +#else + +static int skw_tx_thread(void *data) +{ + struct skw_core *skw = data; + int i, ac, mac; + int base = 0; + unsigned long flags; + int lmac_tx_capa; + int qlen, pending_qlen = 0; + int max_tx_count_limit = 0; + int lmac_tx_map = 0; + struct sk_buff *skb; + struct skw_iface *iface; + struct sk_buff_head *qlist; + struct skw_tx_lmac txl[SKW_MAX_LMAC_SUPPORT]; + struct skw_tx_lmac *txlp; + struct sk_buff_head pure_ack_list; + int xmit_tx_flag; + int all_credit; + + memset(txl, 0, sizeof(txl)); + skw_reset_ac(txl); + + max_tx_count_limit = skw->hw.pkt_limit; + + /* reserve one for eof block */ + if (skw->hw.bus == SKW_BUS_SDIO) + max_tx_count_limit--; + + for (i = 0; i < skw->hw.nr_lmac; i++) { + __skb_queue_head_init(&txl[i].tx_list); + txl[i].tx_count_limit = max_tx_count_limit; + } + + while (!kthread_should_stop()) { + // TODO: + /* CPU bind */ + /* check if frame in pending queue is timeout */ + + atomic_inc(&skw->dbg.loop); + + if (test_and_clear_bit(SKW_CMD_FLAG_XMIT, &skw->cmd.flags)) { + skw_bus_cmd_xmit(skw, skw->cmd.data, skw->cmd.data_len); + + if (test_bit(SKW_CMD_FLAG_NO_ACK, &skw->cmd.flags)) { + set_bit(SKW_CMD_FLAG_DONE, &skw->cmd.flags); + skw->cmd.callback(skw); + } + } + + if (!skw_tx_allowed(skw->flags)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(200)); + continue; + } + + pending_qlen = 0; + lmac_tx_map = 0; + + for (ac = 0; ac < SKW_WMM_AC_MAX; ac++) { + int ac_qlen = 0; + + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + // if (!iface || skw_lmac_is_actived(skw, iface->lmac_id)) + if (!iface || !iface->ndev) + continue; + + if (ac == SKW_WMM_AC_BE && iface->txq[SKW_ACK_TXQ].qlen) { + qlist = &iface->txq[SKW_ACK_TXQ]; + __skb_queue_head_init(&pure_ack_list); + + spin_lock_irqsave(&qlist->lock, flags); + skb_queue_splice_tail_init(&iface->txq[SKW_ACK_TXQ], + &pure_ack_list); + spin_unlock_irqrestore(&qlist->lock, flags); + + skw_merge_pure_ack(skw, &pure_ack_list, &iface->tx_cache[ac]); + } + + qlist = &iface->txq[ac]; + if (!skb_queue_empty(qlist)) { + spin_lock_irqsave(&qlist->lock, flags); + skb_queue_splice_tail_init(qlist, &iface->tx_cache[ac]); + spin_unlock_irqrestore(&qlist->lock, flags); + } + + qlen = skb_queue_len(&iface->tx_cache[ac]); + if (qlen) { + txlp = &txl[iface->lmac_id]; + txlp->current_qlen += qlen; + txlp->txq_map |= BIT(txlp->nr_txq); + txlp->tx[txlp->nr_txq].list = &iface->tx_cache[ac]; + + if (txlp->ac_reset & BIT(ac)) { + txlp->tx[txlp->nr_txq].quota = iface->wmm.factor[ac]; + txlp->tx[txlp->nr_txq].reset = true; + txlp->reset = true; + txlp->ac_reset ^= BIT(ac); + } + + txlp->nr_txq++; + ac_qlen += qlen; + } + } + + if (!ac_qlen) + continue; + + pending_qlen += ac_qlen; + lmac_tx_capa = 0; + + all_credit = 0; + for (mac = 0; mac < skw->hw.nr_lmac; mac++) + all_credit += skw_get_hw_credit(skw, mac); + + if (all_credit == 0) { + skw_stop_dev_queue(skw); + wait_event_interruptible_exclusive(skw->tx_wait_q, + atomic_xchg(&skw->tx_wake, 0) || atomic_xchg(&skw->exit, 0)); + skw_start_dev_queue(skw); + } + + for (mac = 0; mac < skw->hw.nr_lmac; mac++) { + int credit; + + txlp = &txl[mac]; + if (!txlp->txq_map) + goto reset; + + credit = txlp->cred = skw_get_hw_credit(skw, mac); + if (!txlp->cred) + goto reset; + + if (txlp->reset) { + switch (ac) { + case SKW_WMM_AC_VO: + base = SKW_BASE_VO; + break; + + case SKW_WMM_AC_VI: + base = SKW_BASE_VI; + break; + + case SKW_WMM_AC_BK: + if (txlp->bk_tx_limit) { + base = min(txlp->cred, txlp->bk_tx_limit); + txlp->bk_tx_limit = 0; + } else { + base = txlp->cred; + } + + base = base / txlp->nr_txq; + + break; + default: + base = min(txlp->cred, txlp->current_qlen); + base = base / txlp->nr_txq; + txlp->bk_tx_limit = (txlp->cred + 1) >> 1; + + break; + } + + base = base ? base : 1; + txlp->reset = false; + } + + for (i = 0; txlp->txq_map != 0; i++) { + i = i % txlp->nr_txq; + + if (!(txlp->txq_map & BIT(i))) + continue; + + if (!txlp->cred) + break; + + if (txlp->tx[i].reset) { + txlp->tx[i].quota += base; + + if (txlp->tx[i].quota < 0) + txlp->tx[i].quota = 0; + + txlp->tx[i].reset = false; + } + + skb = skb_peek(txlp->tx[i].list); + if (!skb) { + txlp->txq_map ^= BIT(i); + continue; + } + + if (!is_skw_peer_data_valid(skw, skb)) { + skw_detail("drop dest: %pM\n", + eth_hdr(skb)->h_dest); + + __skb_unlink(skb, txlp->tx[i].list); + kfree_skb(skb); + + continue; + } + + if (!txlp->tx_count_limit--) + break; + + if (txlp->tx[i].quota) { + txlp->tx[i].quota--; + } else { + txlp->txq_map ^= BIT(i); + continue; + } + + if ((long)skb->data & SKW_DATA_ALIGN_MASK) + skw_warn("address unaligned\n"); +#if 0 + if (skb->len % skw->hw_pdata->align_value) + skw_warn("len: %d unaligned\n", skb->len); +#endif + __skb_unlink(skb, txlp->tx[i].list); + __skb_queue_tail(&txlp->tx_list, skb); + txlp->cred--; + } + + pending_qlen = pending_qlen - credit + txlp->cred; + + trace_skw_tx_info(mac, ac, credit, credit - txlp->cred, + txlp->current_qlen, pending_qlen); + + // skw_lmac_tx(skw, mac, &txlp->tx_list); + skw_bus_data_xmit(skw, mac, &txlp->tx_list); + txlp->tx_count_limit = max_tx_count_limit; + + lmac_tx_map |= BIT(mac); + + if (txlp->cred) + lmac_tx_capa |= BIT(mac); +reset: + txlp->nr_txq = 0; + txlp->txq_map = 0; + txlp->current_qlen = 0; + } + + if (!lmac_tx_capa) + break; + } + + if (ac == SKW_WMM_AC_MAX) + skw_reset_ac(txl); + + if (pending_qlen == 0) { + xmit_tx_flag = 0; + + for (ac = 0; ac < SKW_WMM_AC_MAX; ac++) { + for (i = 0; i < SKW_NR_IFACE; i++) { + iface = skw->vif.iface[i]; + if (!iface || !iface->ndev) + continue; + + if (skb_queue_len(&iface->tx_cache[ac]) != 0) { + xmit_tx_flag = 1; + goto need_running; + } + + spin_lock_irqsave(&iface->txq[ac].lock, flags); + if (skb_queue_len(&iface->txq[ac]) != 0) { + xmit_tx_flag = 1; + spin_unlock_irqrestore(&iface->txq[ac].lock, flags); + goto need_running; + + } + spin_unlock_irqrestore(&iface->txq[ac].lock, flags); + } + } +need_running: + + if (xmit_tx_flag == 0) { + //skw_start_dev_queue(skw); + wait_event_interruptible_exclusive(skw->tx_wait_q, + atomic_xchg(&skw->tx_wake, 0) || atomic_xchg(&skw->exit, 0)); + } + } + } + + skw_info("exit"); + + return 0; +} + +static int __skw_tx_init(struct skw_core *skw) +{ + skw->tx_thread = kthread_create(skw_tx_thread, skw, "skw_tx.%d", skw->idx); + if (IS_ERR(skw->tx_thread)) { + skw_err("create tx thread failed\n"); + return PTR_ERR(skw->tx_thread); + } + + //kthread_bind(skw->tx_thread, cpumask_last(cpu_online_mask)); + + skw_set_thread_priority(skw->tx_thread, SCHED_RR, 1); + set_user_nice(skw->tx_thread, MIN_NICE); + wake_up_process(skw->tx_thread); + + return 0; +} + +static void __skw_tx_deinit(struct skw_core *skw) +{ + if (skw->tx_thread) { + atomic_set(&skw->exit, 1); + kthread_stop(skw->tx_thread); + skw->tx_thread = NULL; + } +} + +#endif + +static int skw_register_tx_callback(struct skw_core *skw, void *func, void *data) +{ + int i, map, ret = 0; + + for (map = 0, i = 0; i < SKW_MAX_LMAC_SUPPORT; i++) { + if (!(skw->hw.lmac[i].flags & SKW_LMAC_FLAG_TXCB)) + continue; + + ret = skw_register_tx_cb(skw, skw->hw.lmac[i].dport, func, data); + if (ret < 0) { + skw_err("chip: %d, hw mac: %d, port: %d failed, ret: %d\n", + skw->idx, i, skw->hw.lmac[i].dport, ret); + + break; + } + + map |= BIT(skw->hw.lmac[i].dport); + } + + skw_dbg("chip: %d, %s data port: 0x%x\n", + skw->idx, func ? "register" : "unregister", map); + + return ret; +} + +int skw_hw_xmit_init(struct skw_core *skw, int dma) +{ + int ret = 0; + + skw_dbg("dma: %d\n", dma); + + switch (dma) { + case SKW_SYNC_ADMA_TX: + skw->hw.dat_xmit = skw_sync_adma_tx; + skw->hw.cmd_xmit = skw_sync_adma_tx; + skw->hw.cmd_disable_irq_xmit = skw_sync_adma_cmd_disable_irq_tx; + break; + + case SKW_SYNC_SDMA_TX: + skw->hw.dat_xmit = skw_sync_sdma_tx; + skw->hw.cmd_xmit = skw_sync_sdma_tx; + skw->hw.cmd_disable_irq_xmit = skw_sync_sdma_cmd_disable_irq_tx; + + if (skw->sdma_buff) + break; + skw->sdma_buff = SKW_ZALLOC(skw->hw_pdata->max_buffer_size, GFP_KERNEL); + if (!skw->sdma_buff) + ret = -ENOMEM; + + break; + + case SKW_ASYNC_ADMA_TX: + skw->hw.dat_xmit = skw_async_adma_tx; + skw->hw.cmd_xmit = skw_sync_adma_tx; + skw->hw.cmd_disable_irq_xmit = NULL; + + ret = skw_register_tx_callback(skw, skw_async_adma_tx_free, skw); + break; + + case SKW_ASYNC_SDMA_TX: + skw->hw.dat_xmit = skw_async_sdma_tx; + skw->hw.cmd_xmit = skw_sync_sdma_tx; + skw->hw.cmd_disable_irq_xmit = NULL; + + ret = skw_register_tx_callback(skw, skw_async_sdma_tx_free, skw); + + if (skw->sdma_buff) + break; + skw->sdma_buff = SKW_ZALLOC(skw->hw_pdata->max_buffer_size, GFP_KERNEL); + if (!skw->sdma_buff) + ret = -ENOMEM; + break; + + case SKW_ASYNC_EDMA_TX: + skw->hw.dat_xmit = NULL; + skw->hw.cmd_xmit = skw_sync_adma_tx; + skw->hw.cmd_disable_irq_xmit = NULL; + break; + + default: + ret = -EINVAL; + skw->hw.dat_xmit = NULL; + skw->hw.cmd_xmit = NULL; + break; + } + + return ret; +} + +int skw_tx_init(struct skw_core *skw) +{ + int ret; + + skw->skb_share_len = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + skw->skb_headroom = sizeof(struct skw_tx_desc_hdr) + + sizeof(struct skw_tx_desc_conf) + + skw->hw.extra.hdr_len + + SKW_DATA_ALIGN_SIZE + sizeof(unsigned long); + + if (skw->hw.bus == SKW_BUS_SDIO) { + skw->eof_blk = SKW_ZALLOC(skw->hw.align, GFP_KERNEL); + if (!skw->eof_blk) + return -ENOMEM; + } else if (skw->hw.bus == SKW_BUS_PCIE) { + skw->skb_headroom = sizeof(struct skw_tx_desc_conf) + + SKW_DATA_ALIGN_SIZE + 2 * sizeof(unsigned long); + } + + ret = skw_hw_xmit_init(skw, skw->hw.dma); + if (ret < 0) { + SKW_KFREE(skw->eof_blk); + return ret; + } + + ret = __skw_tx_init(skw); + if (ret < 0) { + SKW_KFREE(skw->eof_blk); + SKW_KFREE(skw->sdma_buff); + } + + tx_wait_time = 5000; + skw_procfs_file(skw->pentry, "tx_wait_time", 0666, &skw_tx_time_fops, NULL); + + return ret; +} + +int skw_tx_deinit(struct skw_core *skw) +{ + __skw_tx_deinit(skw); + + skw_register_tx_callback(skw, NULL, NULL); + + SKW_KFREE(skw->eof_blk); + SKW_KFREE(skw->sdma_buff); + + return 0; +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tx.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tx.h new file mode 100755 index 0000000..e53adbd --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_tx.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_TX_H__ +#define __SKW_TX_H__ + +#include <linux/virtio_ring.h> + +#include "skw_platform_data.h" + +/* used for tx descriptor */ +#define SKW_ETHER_FRAME 0 +#define SKW_80211_FRAME 1 + +#define SKW_SNAP_HDR_LEN 8 + +#define SKW_TXQ_HIGH_THRESHOLD 768 +#define SKW_TXQ_LOW_THRESHOLD 512 + +#define SKW_TX_WAIT_TIME 1000000 + +#define SKW_TX_VRING_SIZE 128 + +struct skw_tx_vring { + struct page *page; + int tx_bytes; + struct vring vr; + void *queue_mem; // 新增:记录vring内存块指针 + + struct scatterlist *sgl_dat; + spinlock_t lock; + + atomic_t set, write; +}; + +enum SKW_TX_RUNNING_STATE { + SKW_TX_RUNNING_EXIT, + SKW_TX_RUNNING_RESTART, + SKW_TX_RUNNING_GO, +}; + +struct skw_tx_desc_hdr { + /* pading bytes for gap */ + u16 padding_gap:2; + u16 inst:2; + u16 tid:4; + u16 peer_lut:5; + + /* frame_type: + * 0: ethernet frame + * 1: 80211 frame + */ + u16 frame_type:1; + u16 encry_dis:1; + + /* rate: 0: auto, 1: sw config */ + u16 rate:1; + + u16 msdu_len:12; + u16 lmac_id:2; + u16 rsv:2; + u16 eth_type; + + /* pading for address align */ + u8 gap[0]; +} __packed; + +struct skw_tx_desc_conf { + u16 l4_hdr_offset:10; + u16 csum:1; + + /* ip_prot: 0: UDP, 1: TCP */ + u16 ip_prot:1; + u16 rsv:4; +} __packed; + +struct skw_tx_cb { + u8 ret:1; + u8 recycle:1; + u8 lmac_id; + u8 peer_idx; + u8 tx_retry; + u16 skb_native_len; + dma_addr_t skb_data_pa; + struct skw_edma_elem e; +}; + +static inline void +skw_set_tx_desc_eth_type(struct skw_tx_desc_hdr *desc_hdr, u16 proto) +{ + desc_hdr->eth_type = proto; +} + +static inline int skw_tx_allowed(unsigned long flags) +{ + unsigned long mask = BIT(SKW_FLAG_FW_ASSERT) | + BIT(SKW_FLAG_BLOCK_TX) | + BIT(SKW_FLAG_MP_MODE) | + BIT(SKW_FLAG_FW_THERMAL) | + BIT(SKW_FLAG_FW_MAC_RECOVERY); + + return !(mask & flags); +} + +static inline int skw_cmd_tx_allowed(unsigned long flags) +{ + clear_bit(SKW_FLAG_FW_THERMAL, &flags); + + return skw_tx_allowed(flags); +} + +#ifdef CONFIG_SWT6621S_SKB_RECYCLE +void skw_recycle_skb_free(struct skw_core *skw, struct sk_buff *skb); +int skw_recycle_skb_copy(struct skw_core *skw, struct sk_buff *skb_recycle, struct sk_buff *skb); +struct sk_buff *skw_recycle_skb_get(struct skw_core *skw); +#endif +void skw_skb_kfree(struct skw_core *skw, struct sk_buff *skb); + +int skw_pcie_cmd_xmit(struct skw_core *skw, void *data, int data_len); +int skw_pcie_xmit(struct skw_core *skw, int lmac_id, struct sk_buff_head *txq); + +int skw_sdio_cmd_xmit(struct skw_core *skw, void *data, int data_len); +int skw_sdio_xmit(struct skw_core *skw, int lmac_id, struct sk_buff_head *txq); + +int skw_usb_cmd_xmit(struct skw_core *skw, void *data, int data_len); +int skw_usb_xmit(struct skw_core *skw, int lmac_id, struct sk_buff_head *txq); + +int skw_hw_xmit_init(struct skw_core *skw, int dma); +int skw_tx_init(struct skw_core *skw); +int skw_tx_deinit(struct skw_core *skw); + +void skw_sdio_hdr_set(struct skw_core *skw, struct sk_buff *skb, int lmac_id); +int skw_vring_set(struct skw_core *skw, void *data, u16 len, int lmac_id); +u16 skw_vring_available_count(struct skw_tx_vring *tx_vring); + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_util.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_util.c new file mode 100755 index 0000000..671265e --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_util.c @@ -0,0 +1,657 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/kernel.h> +#include <linux/etherdevice.h> + +#include "skw_core.h" +#include "skw_util.h" +#include "skw_cfg80211.h" + +#ifdef CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER +#include <clocksource/arm_arch_timer.h> +u64 skw_local_clock(void) +{ + u64 ns; + + ns = arch_timer_read_counter() * 1000; + do_div(ns, 24); + + return ns; +} +#else +u64 skw_local_clock(void) +{ + return local_clock(); +} +#endif + + +#ifdef SKW_IMPORT_NS +struct file *skw_file_open(const char *path, int flags, int mode) +{ + struct file *fp = NULL; + + fp = filp_open(path, flags, mode); + if (IS_ERR(fp)) { + skw_err("open fail\n"); + return NULL; + } + + return fp; +} + +int skw_file_read(struct file *fp, unsigned char *data, + size_t size, loff_t offset) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + return kernel_read(fp, data, size, &offset); +#else + return kernel_read(fp, offset, data, size); +#endif +} + +int skw_file_write(struct file *fp, unsigned char *data, + size_t size, loff_t offset) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + return kernel_write(fp, data, size, &offset); +#else + return kernel_write(fp, data, size, offset); +#endif +} + +int skw_file_sync(struct file *fp) +{ + return vfs_fsync(fp, 0); +} + +void skw_file_close(struct file *fp) +{ + filp_close(fp, NULL); +} + +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif + +void *skw_build_presp_frame(struct wiphy *wiphy, struct skw_iface *iface, + u8 *da, u8 *sa, u8 *bssid, u8 *ssid, int ssid_len, + u16 chan, struct ieee80211_supported_band *sband, + u16 capa, u64 tsf, int beacon_int, void *ie, int ie_len) +{ + u8 *pos; + int i, rate; + struct skw_template *temp; + struct ieee80211_mgmt *mgmt; + + skw_dbg("ssid: %s, bssid: %pM\n", ssid, bssid); + + temp = SKW_ZALLOC(1600, GFP_KERNEL); + if (!temp) + return NULL; + + mgmt = temp->mgmt; + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + memcpy(mgmt->sa, sa, ETH_ALEN); + memcpy(mgmt->da, da, ETH_ALEN); + memcpy(mgmt->bssid, bssid, ETH_ALEN); + + mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int); + mgmt->u.beacon.timestamp = cpu_to_le64(tsf); + mgmt->u.beacon.capab_info = cpu_to_le16(capa); + + pos = mgmt->u.beacon.variable; + + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + memcpy(pos, ssid, ssid_len); + pos += ssid_len; + + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = SKW_BASIC_RATE_COUNT; + for (i = 0; i < SKW_BASIC_RATE_COUNT; i++) { + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); + if (sband->bitrates[i].flags & 0x1) + rate |= 0x80; + *pos++ = rate; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) + if (sband->band == IEEE80211_BAND_2GHZ) { +#else + if (sband->band == NL80211_BAND_2GHZ) { +#endif + *pos++ = WLAN_EID_DS_PARAMS; + *pos++ = 1; + *pos++ = chan; + } + + if (iface->wdev.iftype == NL80211_IFTYPE_ADHOC) { + *pos++ = WLAN_EID_IBSS_PARAMS; + *pos++ = 2; + *pos++ = 0; + *pos++ = 0; + } + + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = sband->n_bitrates - SKW_BASIC_RATE_COUNT; + for (i = SKW_BASIC_RATE_COUNT; i < sband->n_bitrates; i++) { + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); + if (sband->bitrates[i].flags & 0x1) + rate |= 0x80; + *pos++ = rate; + } + + if (ie_len) { + memcpy(pos, ie, ie_len); + pos += ie_len; + } + + return temp; +} + +int skw_key_idx(u16 bitmap) +{ + static u8 idx[] = {0xff, 0x00, 0x01, 0xff, + 0x02, 0xff, 0xff, 0xff, + 0x03, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff}; + + return idx[bitmap & 0xf]; +} + +int skw_build_deauth_frame(void *buf, int buf_len, u8 *da, u8 *sa, + u8 *bssid, u16 reason_code) +{ + u16 fc = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH; + struct ieee80211_mgmt *mgmt = buf; + + if (!buf || buf_len < SKW_DEAUTH_FRAME_LEN) + return -EINVAL; + + mgmt->frame_control = cpu_to_le16(fc); + mgmt->duration = 0; + mgmt->seq_ctrl = 0; + skw_ether_copy(mgmt->da, da); + skw_ether_copy(mgmt->sa, sa); + skw_ether_copy(mgmt->bssid, bssid); + + mgmt->u.deauth.reason_code = cpu_to_le16(reason_code); + + return SKW_DEAUTH_FRAME_LEN; +} + +char *skw_mgmt_name(u16 fc) +{ +#define SKW_STYPE_STR(n) {case IEEE80211_STYPE_##n: return #n; } + + switch (fc) { + SKW_STYPE_STR(ASSOC_REQ); + SKW_STYPE_STR(ASSOC_RESP); + SKW_STYPE_STR(REASSOC_REQ); + SKW_STYPE_STR(REASSOC_RESP); + SKW_STYPE_STR(PROBE_REQ); + SKW_STYPE_STR(PROBE_RESP); + SKW_STYPE_STR(BEACON); + SKW_STYPE_STR(ATIM); + SKW_STYPE_STR(DISASSOC); + SKW_STYPE_STR(AUTH); + SKW_STYPE_STR(DEAUTH); + SKW_STYPE_STR(ACTION); + + default: + break; + } + +#undef SKW_STYPE_STR + + return "UNDEFINE"; +} + +int skw_freq_to_chn(int freq) +{ + if (freq == 2484) + return 14; + else if (freq >= 2407 && freq < 2484) + return (freq - 2407) / 5; + else if (freq >= 4910 && freq <= 4980) + return (freq - 4000) / 5; + else if (freq >= 5000 && freq <= 45000) + return (freq - 5000) / 5; + else if (freq >= 58320 && freq <= 64800) + return (freq - 56160) / 2160; + else + return 0; +} + +u32 skw_calc_rate(u64 bytes, u32 delta_ms) +{ + struct skw_tp_rate ret; + u64 cal_bytes = bytes; + u16 bps; + u16 Kbps; + u16 Mbps; + u16 Gbps; + u16 Tbps; + + ret.ret = 0; + cal_bytes *= 8 * 1000; + do_div(cal_bytes, delta_ms); + bps = do_div(cal_bytes, 1 << 10); + Kbps = do_div(cal_bytes, 1 << 10); + Mbps = do_div(cal_bytes, 1 << 10); + Gbps = do_div(cal_bytes, 1 << 10); + Tbps = cal_bytes; + + if (Tbps) { + ret.rate.value = Tbps; + ret.rate.unit = 'T'; + ret.rate.two_dec = Gbps * 100 >> 10; + } else if (Gbps) { + ret.rate.value = Gbps; + ret.rate.unit = 'G'; + ret.rate.two_dec = Mbps * 100 >> 10; + } else if (Mbps) { + ret.rate.value = Mbps; + ret.rate.unit = 'M'; + ret.rate.two_dec = Kbps * 100 >> 10; + } else { + ret.rate.value = Kbps; + ret.rate.unit = 'K'; + ret.rate.two_dec = bps * 100 >> 10; + } + + return ret.ret; +} + +static u32 skw_peer_rate(struct skw_stats_info *stat) +{ + u64 total_bytes, total_jiffies; + + total_bytes = stat->bytes - stat->cal_bytes; + stat->cal_bytes = stat->bytes; + total_jiffies = jiffies - stat->cal_time; + stat->cal_time = jiffies; + + return skw_calc_rate(total_bytes, jiffies_to_msecs(total_jiffies)); +} + +int skw_tx_throughput(struct skw_iface *iface, u8 *mac) +{ + int ret = 0; + struct skw_peer_ctx *ctx; + + + if (!iface) { + ret = -ENOENT; + goto exit; + } + + if (!mac || is_broadcast_ether_addr(mac)) { + ret = -EINVAL; + goto exit; + } + + ctx = skw_peer_ctx(iface, mac); + if (!ctx) { + ret = -ENOENT; + goto exit; + } + + skw_peer_ctx_lock(ctx); + + if (ctx->peer) + ret = skw_peer_rate(&ctx->peer->tx); + + skw_peer_ctx_unlock(ctx); + +exit: + return ret; +} + +int skw_rx_throughput(struct skw_iface *iface, u8 *mac) +{ + int ret = 0; + struct skw_peer_ctx *ctx; + + + if (!iface) { + ret = -ENOENT; + goto exit; + } + + if (!mac || is_broadcast_ether_addr(mac)) { + ret = -EINVAL; + goto exit; + } + + ctx = skw_peer_ctx(iface, mac); + if (!ctx) { + ret = -ENOENT; + goto exit; + } + + skw_peer_ctx_lock(ctx); + + if (ctx->peer) + ret = skw_peer_rate(&ctx->peer->rx); + + skw_peer_ctx_unlock(ctx); + +exit: + return ret; +} + +int skw_tx_throughput_whole(struct skw_iface *iface, u32 *tp) +{ + int ret = 0; + u32 peer_idx_map, idx; + struct skw_peer_ctx *ctx; + + + if (!iface) { + ret = -ENOENT; + goto exit; + } + + peer_idx_map = atomic_read(&iface->peer_map); + + while (peer_idx_map) { + idx = ffs(peer_idx_map) - 1; + SKW_CLEAR(peer_idx_map, BIT(idx)); + + ctx = &iface->skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; + + skw_peer_ctx_lock(ctx); + + if (ctx->peer) + *(&(tp[idx])) = skw_peer_rate(&ctx->peer->tx); + + skw_peer_ctx_unlock(ctx); + } + +exit: + return ret; +} + +int skw_rx_throughput_whole(struct skw_iface *iface, u32 *tp) +{ + int ret = 0; + u32 peer_idx_map, idx; + struct skw_peer_ctx *ctx; + + + if (!iface) { + ret = -ENOENT; + goto exit; + } + + peer_idx_map = atomic_read(&iface->peer_map); + + while (peer_idx_map) { + idx = ffs(peer_idx_map) - 1; + SKW_CLEAR(peer_idx_map, BIT(idx)); + + ctx = &iface->skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; + + skw_peer_ctx_lock(ctx); + + if (ctx->peer) + *(&(tp[idx])) = skw_peer_rate(&ctx->peer->rx); + + skw_peer_ctx_unlock(ctx); + } + +exit: + return ret; +} + +const u8 *skw_find_ie_match(u8 eid, const u8 *ies, int len, const u8 *match, + int match_len, int match_offset) +{ + const struct skw_element *elem; + + /* match_offset can't be smaller than 2, unless match_len is + * zero, in which case match_offset must be zero as well. + */ + if (WARN_ON((match_len && match_offset < 2) || + (!match_len && match_offset))) + return NULL; + + skw_foreach_element_id(elem, eid, ies, len) { + if (elem->datalen >= match_offset - 2 + match_len && + !memcmp(elem->data + match_offset - 2, match, match_len)) + return (void *)elem; + } + + return NULL; +} + +bool skw_bss_check_vendor_name(struct cfg80211_bss *bss, const u8 *oui) +{ + const struct cfg80211_bss_ies *ies; + const struct skw_element *elem; + bool ret = false; + u8 eid = WLAN_EID_VENDOR_SPECIFIC; + + ies = rcu_dereference(bss->beacon_ies); + if (!ies) + return false; + + skw_hex_dump("beacon ies:", ies->data, ies->len, false); + + skw_foreach_element_id(elem, eid, ies->data, ies->len) { + if (!memcmp(elem->data, oui, 3)) { + ret = true; + break; + } + } + + return ret; +} + +int skw_desc_get_rx_rate(struct skw_rate *rate, u8 bw, u8 mode, u8 gi, + u8 nss, u8 dcm, u16 data_rate) +{ + u16 skw_supp_bs_rate[] = { + 20, 55, 110, + /*2M,5.5M,11M*/ + }; + u16 skw_supp_bl_rate[] = { + 10, 20, 55, 110, + /*1M,2M,5.5M,11M*/ + }; + u16 skw_supp_g_rate[] = { + 60, 90, 120, 180, 240, 360, 480, 540, + /*6M, 9M, 12M, 18M, 24M, 36M, 48M, 54M*/ + }; + + memset(rate, 0x0, sizeof(struct skw_rate)); + + rate->bw = bw; + rate->nss = nss; + + switch (mode) { + case SKW_PPDUMODE_HT_MIXED: + rate->flags = SKW_RATE_INFO_FLAGS_HT; + rate->mcs_idx = 0x3F & data_rate; + rate->gi = gi; + break; + + case SKW_PPDUMODE_VHT_SU: + case SKW_PPDUMODE_VHT_MU: + rate->flags = SKW_RATE_INFO_FLAGS_VHT; + rate->mcs_idx = 0xF & data_rate; + rate->gi = gi; + break; + + case SKW_PPDUMODE_HE_SU: + case SKW_PPDUMODE_HE_TB: + case SKW_PPDUMODE_HE_ER_SU: + case SKW_PPDUMODE_HE_MU: + rate->flags = SKW_RATE_INFO_FLAGS_HE; + rate->mcs_idx = 0xF & data_rate; + + if (dcm) { + rate->mcs_idx = 0x3 & data_rate; + rate->he_dcm = dcm; + } else if (mode == SKW_PPDUMODE_HE_ER_SU) { + rate->mcs_idx = 0x3 & data_rate; + } + + rate->gi = gi; + + if (bw != SKW_DESC_BW_USED_RU) + rate->he_ru = bw + 3; + break; + + case SKW_PPDUMODE_11B_SHORT: + rate->flags = SKW_RATE_INFO_FLAGS_LEGACY; + + if (data_rate < ARRAY_SIZE(skw_supp_bs_rate)) + rate->legacy_rate = skw_supp_bs_rate[data_rate]; + else + skw_warn("illegal 11B_SHORT rate:%d\n", data_rate); + break; + + case SKW_PPDUMODE_11B_LONG: + rate->flags = SKW_RATE_INFO_FLAGS_LEGACY; + + if (data_rate < ARRAY_SIZE(skw_supp_bl_rate)) + rate->legacy_rate = skw_supp_bl_rate[data_rate]; + else + skw_warn("illegal 11B_LONG rate:%d\n", data_rate); + break; + + case SKW_PPDUMODE_11G: + rate->flags = SKW_RATE_INFO_FLAGS_LEGACY; + + if (data_rate < ARRAY_SIZE(skw_supp_g_rate)) + rate->legacy_rate = skw_supp_g_rate[data_rate]; + else + skw_warn("illegal 11G rate:%d\n", data_rate); + break; + + default: + skw_warn("unsupport ppdu mode:%d\n", mode); + break; + }; + + return 0; +} + +int skw_tlv_add(struct skw_tlv_conf *conf, int type, void *dat, int dat_len) +{ + struct skw_tlv *tlv; + + if (!conf || !conf->buff) + return -EINVAL; + + if (conf->total_len + dat_len + 4 > conf->buff_len) + return -ENOMEM; + + tlv = (struct skw_tlv *)(conf->buff + conf->total_len); + tlv->type = type; + tlv->len = dat_len; + memcpy(tlv->value, dat, dat_len); + + conf->total_len += dat_len + 4; + + return 0; +} + +int skw_tlv_alloc(struct skw_tlv_conf *conf, int len, gfp_t gfp) +{ + if (!conf) + return -EINVAL; + + conf->buff = SKW_ZALLOC(len, GFP_KERNEL); + if (!conf->buff) + return -ENOMEM; + + conf->total_len = 0; + conf->buff_len = len; + + return 0; +} + +void *skw_tlv_reserve(struct skw_tlv_conf *conf, int len) +{ + void *start = NULL; + + if (!conf || !conf->buff) + return NULL; + + if (conf->total_len + len > conf->buff_len) + return NULL; + + start = conf->buff + conf->total_len; + conf->total_len += len; + + return start; +} + +void skw_tlv_free(struct skw_tlv_conf *conf) +{ + if (conf) { + SKW_KFREE(conf->buff); + conf->total_len = 0; + conf->buff_len = 0; + } +} + +int skw_util_set_mib_enable(struct wiphy *wiphy, int inst, int mib_id, bool enable) +{ + int ret; + u16 *plen; + struct skw_tlv_conf conf; + bool state = !!enable; + + skw_dbg("set %d mib:%d\n", mib_id, enable); + + ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); + if (ret) { + skw_err("alloc failed\n"); + return ret; + } + + plen = skw_tlv_reserve(&conf, 2); + if (!plen) { + skw_err("reserve failed\n"); + skw_tlv_free(&conf); + return -ENOMEM; + } + + if (skw_tlv_add(&conf, mib_id, &state, 1)) { + skw_err("set mib failed\n"); + skw_tlv_free(&conf); + + return -EINVAL; + } + + *plen = conf.total_len; + + ret = skw_msg_xmit(wiphy, inst, SKW_CMD_SET_MIB, conf.buff, + conf.total_len, NULL, 0); + if (ret) + skw_warn("failed, ret: %d\n", ret); + + skw_tlv_free(&conf); + + return ret; +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_util.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_util.h new file mode 100755 index 0000000..795cff5 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_util.h @@ -0,0 +1,338 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_UTIL_H__ +#define __SKW_UTIL_H__ + +#include <linux/version.h> +#include <linux/ieee80211.h> +#include <net/cfg80211.h> +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/sched.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include <uapi/linux/sched/types.h> +#endif + +#define SKW_NULL +#define SKW_LEAVE WLAN_REASON_DEAUTH_LEAVING +#define SKW_2K_SIZE 2048 +#define SKW_SKB_RECYCLE_COUNT 4096 +#define SKW_BASIC_RATE_COUNT 8 +/* hdr(24) + reason(2) */ +#define SKW_DEAUTH_FRAME_LEN 26 + +#define __SKW_STR(x) #x +#define SKW_STR(x) __SKW_STR(x) + +#define SKW_SET(d, v) ((d) |= (v)) +#define SKW_CLEAR(d, v) ((d) &= ~(v)) +#define SKW_TEST(d, v) ((d) & (v)) + +#define SKW_ZALLOC(s, f) kzalloc(s, f) + +#define SKW_KFREE(p) \ + do { \ + kfree(p); \ + p = NULL; \ + } while (0) + +#define SKW_KMEMDUP(s, l, f) (((s) != NULL) ? kmemdup(s, l, f) : NULL) +#define SKW_MGMT_SFC(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) +#define SKW_WDEV_TO_IFACE(w) container_of(w, struct skw_iface, wdev) + +#define SKW_OUI(a, b, c) \ + (((a) & 0xff) << 16 | ((b) & 0xff) << 8 | ((c) & 0xff)) + +#ifndef list_next_entry +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) +#endif + +#ifdef SKWIFI_ASSERT +#define SKW_BUG_ON(c) BUG_ON(c) +#else +#define SKW_BUG_ON(c) WARN_ON(c) +#endif + +#ifndef READ_ONCE +#define READ_ONCE(x) ACCESS_ONCE(x) +#endif + +#ifndef WRITE_ONCE +#define WRITE_ONCE(x, v) (ACCESS_ONCE(x) = v) +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#if __has_attribute(__fallthrough__) +#define skw_fallthrough __attribute__((__fallthrough__)) +#else +#define skw_fallthrough do {} while (0) /* fallthrough */ +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +static inline void u64_stats_init(struct u64_stats_sync *syncp) +{ +#if BITS_PER_LONG == 32 && defined(CONFIG_SMP) + seqcount_init(&syncp->seq); +#endif +} +#endif + +#ifndef NET_NAME_ENUM +#define NET_NAME_ENUM 1 +#endif + +#ifndef netdev_alloc_pcpu_stats +#define netdev_alloc_pcpu_stats(type) \ +({ \ + typeof(type) __percpu *pcpu_stats = alloc_percpu(type); \ + if (pcpu_stats) { \ + int i; \ + for_each_possible_cpu(i) { \ + typeof(type) *stat; \ + stat = per_cpu_ptr(pcpu_stats, i); \ + u64_stats_init(&stat->syncp); \ + } \ + } \ + pcpu_stats; \ +}) +#endif + +#define skw_foreach_element(_elem, _data, _datalen) \ + for (_elem = (struct skw_element *)(_data); \ + (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >= \ + (int)sizeof(*_elem) && \ + (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >= \ + (int)sizeof(*_elem) + _elem->datalen; \ + _elem = (struct skw_element *)(_elem->data + _elem->datalen)) + +#define skw_foreach_element_id(element, _id, data, datalen) \ + skw_foreach_element(element, data, datalen) \ + if (element->id == (_id)) + +struct skw_tp_rate { + union { + struct { + u16 value; + u8 two_dec; + u8 unit; + } rate; + + u32 ret; + }; +}; + +struct skw_template { + u16 head_offset; + u16 head_len; + u16 tail_ofsset; + u16 tail_len; + struct ieee80211_mgmt mgmt[0]; +}; + +struct skw_rate { + u8 flags; + u8 mcs_idx; + u16 legacy_rate; + u8 nss; + u8 bw; + u8 gi; + u8 he_ru; + u8 he_dcm; +} __packed; + +struct skw_arphdr { + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + __be32 ar_sip; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + __be32 ar_tip; /* target IP address */ +} __packed; + +struct skw_element { + u8 id; + u8 datalen; + u8 data[]; +} __packed; + +struct skw_tlv { + u16 type; + u16 len; + char value[0]; +}; + +struct skw_tlv_conf { + void *buff; + int buff_len, total_len; +}; + +static inline struct skw_arphdr *skw_arp_hdr(struct sk_buff *skb) +{ + if (!skb) + return NULL; + + return (struct skw_arphdr *)(skb->data + 14); +} + +static inline u64 skw_mac_to_u64(const u8 *addr) +{ + u64 u = 0; + int i; + + for (i = 0; i < ETH_ALEN; i++) + u = u << 8 | addr[i]; + + return u; +} + +static inline void skw_u64_to_mac(u64 u, u8 *addr) +{ + int i; + + for (i = ETH_ALEN - 1; i >= 0; i--) { + addr[i] = u & 0xff; + u = u >> 8; + } +} + +static inline void *skw_put_skb_data(struct sk_buff *skb, const void *data, + unsigned int len) +{ + void *tmp = skb_put(skb, len); + + memcpy(tmp, data, len); + + return tmp; +} + +static inline void *skw_put_skb_zero(struct sk_buff *skb, unsigned int len) +{ + void *tmp = skb_put(skb, len); + + memset(tmp, 0, len); + + return tmp; +} + +static inline struct ethhdr *skw_eth_hdr(const struct sk_buff *skb) +{ + return (struct ethhdr *)skb->data; +} + +static inline int skw_mac_offset(const struct sk_buff *skb) +{ + return skb_mac_header(skb) - skb->data; +} + +static inline void skw_set_thread_priority(struct task_struct *thread, + int policy, int priority) +{ +#ifdef CONFIG_SWT6621S_HIGH_PRIORITY +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + sched_set_fifo_low(thread); +#else + struct sched_param param = { + .sched_priority = priority, + }; + + sched_setscheduler(thread, policy, ¶m); +#endif +#endif +} + +static inline const char *skw_iftype_name(enum nl80211_iftype iftype) +{ + static const char * const ifname[] = {"IFTYPE_NONE", + "ADHOC", + "STA", + "AP", + "AP_VLAN", + "WDS", + "MONITOR", + "MESH", + "P2P_GC", + "P2P_GO", + "P2P_DEVICE", + "OCB", + "NAN", + "IFTYPE_LAST"}; + + return ifname[iftype]; +} + +/** + * skw_ether_copy - Copy an Ethernet address + * @dst: Pointer to a six-byte array Ethernet address destination + * @src: Pointer to a six-byte array Ethernet address source + * + * Please note: dst & src must both be aligned to u16. + */ +static inline void skw_ether_copy(u8 *dst, const u8 *src) +{ +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + *(u32 *)dst = *(const u32 *)src; + *(u16 *)(dst + 4) = *(const u16 *)(src + 4); +#else + u16 *a = (u16 *)dst; + const u16 *b = (const u16 *)src; + + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; +#endif +} + +#ifdef SKW_IMPORT_NS +struct file *skw_file_open(const char *path, int flags, int mode); +int skw_file_read(struct file *fp, unsigned char *data, + size_t size, loff_t offset); +int skw_file_write(struct file *fp, unsigned char *data, + size_t size, loff_t offset); +int skw_file_sync(struct file *fp); +void skw_file_close(struct file *fp); +#endif + +u64 skw_local_clock(void); +int skw_key_idx(u16 bitmap); +char *skw_mgmt_name(u16 fc); +int skw_freq_to_chn(int freq); +u32 skw_calc_rate(u64 bytes, u32 delta_ms); +int skw_build_deauth_frame(void *buf, int buf_len, u8 *da, u8 *sa, + u8 *bssid, u16 reason_code); +const u8 *skw_find_ie_match(u8 eid, const u8 *ies, int len, const u8 *match, + int match_len, int match_offset); + +int skw_desc_get_rx_rate(struct skw_rate *rate, u8 bw, u8 mode, u8 gi, + u8 nss, u8 dcm, u16 data_rate); +void skw_tlv_free(struct skw_tlv_conf *conf); +void *skw_tlv_reserve(struct skw_tlv_conf *conf, int len); +int skw_tlv_alloc(struct skw_tlv_conf *conf, int len, gfp_t gfp); +int skw_tlv_add(struct skw_tlv_conf *conf, int type, void *dat, int dat_len); +bool skw_bss_check_vendor_name(struct cfg80211_bss *bss, const u8 *oui); +int skw_util_set_mib_enable(struct wiphy *wiphy, int inst, int mib_id, bool enable); +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_vendor.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_vendor.c new file mode 100755 index 0000000..5c43fbb --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_vendor.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <net/cfg80211.h> +#include <net/genetlink.h> + +#include "skw_vendor.h" +#include "skw_cfg80211.h" +#include "skw_core.h" +#include "skw_iface.h" +#include "skw_util.h" +#include "skw_regd.h" +#include "version.h" + +const struct nla_policy +skw_set_country_policy[SKW_SET_COUNTRY_RULES] = { + [SKW_ATTR_SET_COUNTRY] = {.type = NLA_STRING}, +}; + +const struct nla_policy +skw_get_valid_channels_policy[SKW_GET_VALID_CHANNELS_RULES] = { + [SKW_ATTR_GET_VALID_CHANNELS] = {.type = NLA_U32}, +}; + +const struct nla_policy +skw_get_version_policy[SKW_GET_VERSION_RULES] = { + [SKW_ATTR_VERSION_DRIVER] = {.type = NLA_U32}, + [SKW_ATTR_VERSION_FIRMWARE] = {.type = NLA_U32}, +}; + +#if 0 +static int skw_vendor_dbg_reset_logging(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int ret = SKW_OK; + + skw_dbg("Enter\n"); + + return ret; +} + +static int skw_vendor_set_p2p_rand_mac(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int type; + //struct skw_iface *iface = netdev_priv(wdev->netdev); + u8 mac_addr[6] = {0}; + + skw_dbg("set skw mac addr\n"); + type = nla_type(data); + + if (type == SKW_ATTR_DRIVER_RAND_MAC) { + memcpy(mac_addr, nla_data(data), 6); + skw_dbg("mac:%pM\n", mac_addr); + } + + return 0; +} + +static int skw_vendor_set_rand_mac_oui(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + char *oui = nla_data(data); + struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); + + if (!oui || (nla_len(data) != DOT11_OUI_LEN)) + return -EINVAL; + + skw_dbg("%02x:%02x:%02x\n", oui[0], oui[1], oui[2]); + + memcpy(iface->rand_mac_oui, oui, DOT11_OUI_LEN); + + return 0; +} +#endif + +static int skw_vendor_cmd_reply(struct wiphy *wiphy, const void *data, int len) +{ + struct sk_buff *skb; + int err; + + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len); + if (unlikely(!skb)) { + skw_err("skb alloc failed"); + return -ENOMEM; + } + + /* Push the data to the skb */ + err = nla_put_nohdr(skb, len, data); + if (err) { + kfree_skb(skb); + return -EINVAL; + } + + return cfg80211_vendor_cmd_reply(skb); +} + +static int skw_vendor_start_logging(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + skw_dbg("inst: %d\n", SKW_WDEV_TO_IFACE(wdev)->id); + + return 0; +} + +static int skw_vendor_get_wake_reason_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + skw_dbg("inst: %d\n", SKW_WDEV_TO_IFACE(wdev)->id); + + return 0; +} + +static int skw_vendor_get_apf_capabilities(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + struct sk_buff *skb; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, NLMSG_DEFAULT_SIZE); + if (!skb) + return -ENOMEM; + + if (nla_put_u32(skb, SKW_ATTR_APF_VERSION, 4) || + nla_put_u32(skb, SKW_ATTR_APF_MAX_LEN, 1024)) { + kfree_skb(skb); + return -ENOMEM; + } + + return cfg80211_vendor_cmd_reply(skb); +} + +static int skw_vendor_get_ring_buffer_data(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + skw_dbg("inst: %d\n", SKW_WDEV_TO_IFACE(wdev)->id); + + return 0; +} + +static int skw_vendor_get_firmware_dump(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + skw_dbg("inst: %d\n", SKW_WDEV_TO_IFACE(wdev)->id); + + return 0; +} + +static int skw_vendor_select_tx_power_scenario(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + skw_dbg("inst: %d\n", SKW_WDEV_TO_IFACE(wdev)->id); + + return 0; +} + +static int skw_vendor_set_latency_mode(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + skw_dbg("inst: %d\n", SKW_WDEV_TO_IFACE(wdev)->id); + + return 0; +} + +static int skw_vendor_get_feature_set(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + u32 feature_set = 0; + + /* Hardcoding these values for now, need to get + * these values from FW, will change in a later check-in + */ + feature_set |= WIFI_FEATURE_INFRA; + feature_set |= WIFI_FEATURE_INFRA_5G; + feature_set |= WIFI_FEATURE_P2P; + feature_set |= WIFI_FEATURE_SOFT_AP; + feature_set |= WIFI_FEATURE_AP_STA; + //feature_set |= WIFI_FEATURE_TDLS; + //feature_set |= WIFI_FEATURE_TDLS_OFFCHANNEL; + //feature_set |= WIFI_FEATURE_NAN; + //feature_set |= WIFI_FEATURE_HOTSPOT; + //feature_set |= WIFI_FEATURE_LINK_LAYER_STATS; //TBC + //feature_set |= WIFI_FEATURE_RSSI_MONITOR; //TBC with roaming + //feature_set |= WIFI_FEATURE_MKEEP_ALIVE; //TBC compare with QUALCOM + //feature_set |= WIFI_FEATURE_CONFIG_NDO; //TBC + //feature_set |= WIFI_FEATURE_SCAN_RAND; + //feature_set |= WIFI_FEATURE_RAND_MAC; + //feature_set |= WIFI_FEATURE_P2P_RAND_MAC ; + //feature_set |= WIFI_FEATURE_CONTROL_ROAMING; + + skw_dbg("feature: 0x%x\n", feature_set); + + return skw_vendor_cmd_reply(wiphy, &feature_set, sizeof(u32)); +} + +static int skw_vendor_set_country(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len) +{ + char *country = nla_data(data); + + if (nla_type(data) != SKW_ATTR_SET_COUNTRY) + skw_warn("attr mismatch, type: %d\n", nla_type(data)); + + if (!country || strlen(country) != 2) { + skw_err("invalid, country: %s\n", country ? country : "null"); + + return -EINVAL; + } + + skw_dbg("country: %c%c\n", country[0], country[1]); + + return skw_set_regdom(wiphy, country); +} + +static int skw_vendor_get_version(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int len) +{ + char version[64] = {0}; + struct skw_core *skw = wiphy_priv(wiphy); + + switch (nla_type(data)) { + case SKW_ATTR_VERSION_DRIVER: + strncpy(version, SKW_VERSION, sizeof(version)); + break; + + case SKW_ATTR_VERSION_FIRMWARE: + snprintf(version, sizeof(version), "%s-%s", + skw->fw.plat_ver, skw->fw.wifi_ver); + break; + + default: + skw_err("invalid nla type\n"); + strcpy(version, "invalid"); + break; + } + + return skw_vendor_cmd_reply(wiphy, version, sizeof(version)); +} + +static int skw_vendor_get_usable_channels(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int i, nr, max; + struct sk_buff *skb; + enum nl80211_band band; + struct skw_usable_chan *chans; + struct skw_usable_chan_req *req = (struct skw_usable_chan_req *)data; + + skw_dbg("band_mask: 0x%x\n", req->band_mask); + + max = ieee80211_get_num_supported_channels(wiphy); + + chans = SKW_ZALLOC(max * sizeof(*chans), GFP_KERNEL); + if (!chans) + return -ENOMEM; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, NLMSG_DEFAULT_SIZE); + if (!skb) { + SKW_KFREE(chans); + return -ENOMEM; + } + + for (nr = 0, band = 0; band < NUM_NL80211_BANDS; band++) { + if (!(req->band_mask & BIT(to_skw_band(band))) || + !wiphy->bands[band]) + continue; + + 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; + + chans[nr].center_freq = chan->center_freq; + chans[nr].band_width = SKW_CHAN_WIDTH_20; + chans[nr].iface_mode_mask = BIT(SKW_STA_MODE) | + BIT(SKW_AP_MODE) | + BIT(SKW_GC_MODE) | + BIT(SKW_GO_MODE); + nr++; + } + } + + if (nla_put_nohdr(skb, nr * sizeof(*chans), chans)) { + SKW_KFREE(chans); + kfree_skb(skb); + + return -ENOMEM; + } + + SKW_KFREE(chans); + + return cfg80211_vendor_cmd_reply(skb); +} + +static int skw_vendor_get_valid_channels(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int channels[32], size; + int i, band, nr_channels; + struct sk_buff *skb; + + if (nla_type(data) != SKW_ATTR_GET_VALID_CHANNELS) + skw_warn("attr mismatch, type: %d", nla_type(data)); + + band = nla_get_u32(data); + if (band > NL80211_BAND_5GHZ) { + skw_err("invalid band: %d\n", band); + return -EINVAL; + } + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, NLMSG_DEFAULT_SIZE); + if (!skb) + return -ENOMEM; + + nr_channels = wiphy->bands[band]->n_channels; + size = nr_channels * sizeof(int); + + for (i = 0; i < nr_channels; i++) + channels[i] = wiphy->bands[band]->channels[i].hw_value; + + if (nla_put_u32(skb, SKW_ATTR_VALID_CHANNELS_COUNT, nr_channels) || + nla_put(skb, SKW_ATTR_VALID_CHANNELS_LIST, size, channels)) { + kfree_skb(skb); + + return -ENOMEM; + } + + return cfg80211_vendor_cmd_reply(skb); +} + +static int skw_vendor_get_ring_buffers_status(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + struct sk_buff *skb; + struct skw_ring_buff_status status = { + .name = "skw_drv", + .flags = 0, + .ring_id = 0, + .ring_buffer_byte_size = 1024, + .verbose_level = 0, + .written_bytes = 0, + .read_bytes = 0, + .written_records = 0, + }; + + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, NLMSG_DEFAULT_SIZE); + if (!skb) + return -ENOMEM; + + if (nla_put_u32(skb, SKW_ATTR_RING_BUFFERS_COUNT, 1) || + nla_put(skb, SKW_ATTR_RING_BUFFERS_STATUS, sizeof(status), &status)) { + kfree_skb(skb); + + return -ENOMEM; + } + + return cfg80211_vendor_cmd_reply(skb); +} + +static int skw_vendor_get_logger_feature(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + u32 features = 0; + + skw_dbg("features: 0x%x\n", features); + + return skw_vendor_cmd_reply(wiphy, &features, sizeof(features)); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) +#define SKW_VENDOR_CMD(oui, cmd, flag, func, nla_policy, max_attr) \ +{ \ + .info = {.vendor_id = oui, .subcmd = cmd}, \ + .flags = flag, \ + .doit = func, \ + .policy = nla_policy, \ + .maxattr = max_attr, \ +} +#else +#define SKW_VENDOR_CMD(oui, cmd, flag, func, nla_policy, max_attr) \ +{ \ + .info = {.vendor_id = oui, .subcmd = cmd}, \ + .flags = flag, \ + .doit = func, \ +} +#endif + +#define SKW_VENDOR_DEFAULT_FLAGS (WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV) + +static struct wiphy_vendor_command skw_vendor_cmds[] = { + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_VALID_CHANNELS, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_valid_channels, + skw_get_valid_channels_policy, + SKW_GET_VALID_CHANNELS_RULES), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_FEATURE_SET, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_feature_set, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_VERSION, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_version, + skw_get_version_policy, + SKW_GET_VERSION_RULES), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_RING_BUFFERS_STATUS, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_ring_buffers_status, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_LOGGER_FEATURE, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_logger_feature, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_APF_CAPABILITIES, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_apf_capabilities, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_USABLE_CHANS, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_usable_channels, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_SET_COUNTRY, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_set_country, + skw_set_country_policy, SKW_SET_COUNTRY_RULES), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_START_LOGGING, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_start_logging, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_FIRMWARE_DUMP, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_firmware_dump, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_RING_BUFFER_DATA, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_ring_buffer_data, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_GET_WAKE_REASON_STATS, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_get_wake_reason_stats, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_SELECT_TX_POWER_SCENARIO, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_select_tx_power_scenario, + VENDOR_CMD_RAW_DATA, 0), + SKW_VENDOR_CMD(OUI_GOOGLE, SKW_VID_SET_LATENCY_MODE, + SKW_VENDOR_DEFAULT_FLAGS, + skw_vendor_set_latency_mode, + VENDOR_CMD_RAW_DATA, 0), +}; + +static struct nl80211_vendor_cmd_info skw_vendor_events[] = { + { + .vendor_id = 0, + .subcmd = 0, + }, +}; + +void skw_vendor_init(struct wiphy *wiphy) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + wiphy->vendor_commands = skw_vendor_cmds; + wiphy->n_vendor_commands = ARRAY_SIZE(skw_vendor_cmds); + wiphy->vendor_events = skw_vendor_events; + wiphy->n_vendor_events = ARRAY_SIZE(skw_vendor_events); +#else + skw_dbg("cmd: %d, event: %d\n", ARRAY_SIZE(skw_vendor_cmds), + ARRAY_SIZE(skw_vendor_events)); +#endif +} + +void skw_vendor_deinit(struct wiphy *wiphy) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + wiphy->vendor_commands = NULL; + wiphy->n_vendor_commands = 0; +#endif +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_vendor.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_vendor.h new file mode 100755 index 0000000..b8a3de5 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_vendor.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_VENDOR_H__ +#define __SKW_VENDOR_H__ + +#define OUI_GOOGLE 0x001A11 + +/* Vendor Command ID */ +#define SKW_VID_GET_VALID_CHANNELS 0x1009 +#define SKW_VID_GET_FEATURE_SET 0x100A +#define SKW_VID_SET_COUNTRY 0x100E +#define SKW_VID_SET_LATENCY_MODE 0x101B +#define SKW_VID_START_LOGGING 0x1400 +#define SKW_VID_GET_FIRMWARE_DUMP 0x1401 +#define SKW_VID_GET_VERSION 0x1403 +#define SKW_VID_GET_RING_BUFFERS_STATUS 0x1404 +#define SKW_VID_GET_RING_BUFFER_DATA 0x1405 +#define SKW_VID_GET_LOGGER_FEATURE 0x1406 +#define SKW_VID_GET_WAKE_REASON_STATS 0x140D +#define SKW_VID_GET_APF_CAPABILITIES 0x1800 +#define SKW_VID_SELECT_TX_POWER_SCENARIO 0x1900 +#define SKW_VID_GET_USABLE_CHANS 0x2000 + +/* ATTR IE Start */ + +/* ATTR GET_VALID_CHANNELS */ +#define SKW_ATTR_GET_VALID_CHANNELS 20 +#define SKW_ATTR_VALID_CHANNELS_COUNT 36 +#define SKW_ATTR_VALID_CHANNELS_LIST 37 +#define SKW_GET_VALID_CHANNELS_RULES 38 +/* ATTR GET_VALID_CHANNELS */ + +/* ATTR SET_COUNTRY */ +#define SKW_ATTR_SET_COUNTRY 5 +#define SKW_SET_COUNTRY_RULES 6 +/* ATTR SET_COUNTRY */ + +/* ATTR GET_APF_CAPABILITIES */ +#define SKW_ATTR_APF_VERSION 0 +#define SKW_ATTR_APF_MAX_LEN 1 +/* ATTR GET_APF_CAPABILITIES */ + +/* ATTR GET_VERSION */ +#define SKW_ATTR_VERSION_DRIVER 1 +#define SKW_ATTR_VERSION_FIRMWARE 2 +#define SKW_GET_VERSION_RULES 3 +/* ATTR GET_VERSION */ + +/* ATTR RING_BUFFERS_STATUS */ +#define SKW_ATTR_RING_BUFFERS_STATUS 13 +#define SKW_ATTR_RING_BUFFERS_COUNT 14 +/* ATTR RING_BUFFERS_STATUS */ + +/* END OF ATTR IE */ + +/* Feature enums */ +#define WIFI_FEATURE_INFRA 0x1 // Basic infrastructure mode +#define WIFI_FEATURE_INFRA_5G 0x2 // Support for 5 GHz Band +#define WIFI_FEATURE_HOTSPOT 0x4 // Support for GAS/ANQP +#define WIFI_FEATURE_P2P 0x8 // Wifi-Direct +#define WIFI_FEATURE_SOFT_AP 0x10 // Soft AP +#define WIFI_FEATURE_GSCAN 0x20 // Google-Scan APIs +#define WIFI_FEATURE_NAN 0x40 // Neighbor Awareness Networking +#define WIFI_FEATURE_D2D_RTT 0x80 // Device-to-device RTT +#define WIFI_FEATURE_D2AP_RTT 0x100 // Device-to-AP RTT +#define WIFI_FEATURE_BATCH_SCAN 0x200 // Batched Scan (legacy) +#define WIFI_FEATURE_PNO 0x400 // Preferred network offload +#define WIFI_FEATURE_ADDITIONAL_STA 0x800 // Support for two STAs +#define WIFI_FEATURE_TDLS 0x1000 // Tunnel directed link setup +#define WIFI_FEATURE_TDLS_OFFCHANNEL 0x2000 // Support for TDLS off channel +#define WIFI_FEATURE_EPR 0x4000 // Enhanced power reporting +#define WIFI_FEATURE_AP_STA 0x8000 // Support for AP STA Concurrency +#define WIFI_FEATURE_LINK_LAYER_STATS 0x10000 // Link layer stats collection +#define WIFI_FEATURE_LOGGER 0x20000 // WiFi Logger +#define WIFI_FEATURE_HAL_EPNO 0x40000 // WiFi PNO enhanced +#define WIFI_FEATURE_RSSI_MONITOR 0x80000 // RSSI Monitor +#define WIFI_FEATURE_MKEEP_ALIVE 0x100000 // WiFi mkeep_alive +#define WIFI_FEATURE_CONFIG_NDO 0x200000 // ND offload configure +#define WIFI_FEATURE_TX_TRANSMIT_POWER 0x400000 // Capture Tx transmit power levels +#define WIFI_FEATURE_CONTROL_ROAMING 0x800000 // Enable/Disable firmware roaming +#define WIFI_FEATURE_IE_WHITELIST 0x1000000 // Support Probe IE white listing +#define WIFI_FEATURE_SCAN_RAND 0x2000000 // Support MAC & Probe Sequence Number randomization +#define WIFI_FEATURE_SET_TX_POWER_LIMIT 0x4000000 // Support Tx Power Limit setting +#define WIFI_FEATURE_USE_BODY_HEAD_SAR 0x8000000 // Support Using Body/Head Proximity for SAR +#define WIFI_FEATURE_DYNAMIC_SET_MAC 0x10000000 // Support changing MAC address without iface reset(down and up) +#define WIFI_FEATURE_SET_LATENCY_MODE 0x40000000 // Support Latency mode setting +#define WIFI_FEATURE_P2P_RAND_MAC 0x80000000 // Support P2P MAC randomization +#define WIFI_FEATURE_INFRA_60G 0x100000000 // Support for 60GHz Band + +/* Vendor Event ID */ +#define SKW_NL80211_VENDOR_SUBCMD_MONITOR_RSSI 80 +#define EXT_VENDOR_EVENT_BUF_SIZE 4096 +enum skw_wlan_vendor_attr_rssi_monitoring { + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_INVALID = 0, + /* Takes valid value from the enum + * skw_wlan_rssi_monitoring_control + * Unsigned 32-bit value enum skw_wlan_rssi_monitoring_control + */ + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL, + /* Unsigned 32-bit value */ + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, + /* Signed 8-bit value in dBm */ + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI, + /* Signed 8-bit value in dBm */ + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI, + /* attributes to be used/received in callback */ + /* 6-byte MAC address used to represent current BSSID MAC address */ + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID, + /* Signed 8-bit value indicating the current RSSI */ + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI, + /* keep last */ + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST, + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX = + SKW_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1, +}; + +struct skw_ring_buff_status { + u8 name[32]; + u32 flags; + int ring_id; // unique integer representing the ring + u32 ring_buffer_byte_size; // total memory size allocated for the buffer + u32 verbose_level; // verbose level for ring buffer + u32 written_bytes; // number of bytes that was written to the buffer by driver + u32 read_bytes; // number of bytes that was read from the buffer by user land + u32 written_records; // number of records that was written to the buffer by driver +}; + +struct skw_usable_chan { + u16 center_freq; + u16 band_width; + u16 iface_mode_mask; + u16 flags; + u32 resvd; +}; + +struct skw_usable_chan_req { + u32 band_mask; + u32 filter_mask; + u32 iface_mode_mask; + u32 flags; + u32 resvd; +}; + +void skw_vendor_init(struct wiphy *wiphy); +void skw_vendor_deinit(struct wiphy *wiphy); + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_work.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_work.c new file mode 100755 index 0000000..dd75905 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_work.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/skbuff.h> + +#include "skw_core.h" +#include "skw_cfg80211.h" +#include "skw_iface.h" +#include "skw_mlme.h" +#include "skw_msg.h" +#include "skw_work.h" +#include "skw_timer.h" +#include "skw_recovery.h" +#include "skw_tx.h" +#include "skw_dfs.h" + +#define SKW_WORK_FLAG_ASSERT 0 +#define SKW_WORK_FLAG_RCU_FREE 1 +#define SKW_WORK_FLAG_UNLOCK 2 +#define SKW_WORK_FLAG_RESUME 3 + +static void skw_ap_acl_check(struct wiphy *wiphy, struct skw_iface *iface) +{ + int idx; + struct skw_peer_ctx *ctx = NULL; + u32 peer_idx_map = atomic_read(&iface->peer_map); + struct skw_core *skw = wiphy_priv(wiphy); + + if (peer_idx_map == 0) + return; + + while (peer_idx_map) { + idx = ffs(peer_idx_map) - 1; + + SKW_CLEAR(peer_idx_map, BIT(idx)); + + ctx = &skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; + if (!ctx) + continue; + + if (ctx->peer && !skw_acl_allowed(iface, ctx->peer->addr)) + skw_mlme_ap_del_sta(wiphy, iface->ndev, + ctx->peer->addr, false); + } +} + +static void skw_work_async_adma_tx_free(struct skw_core *skw, + struct scatterlist *sglist, int nents) +{ + int idx; + struct scatterlist *sg; + struct sk_buff *skb; + unsigned long *skb_addr, *sg_addr; + + for_each_sg(sglist, sg, nents, idx) { + sg_addr = (unsigned long *)sg_virt(sg); + + skb_addr = sg_addr - 1; + skb = (struct sk_buff *)*skb_addr; + if (!virt_addr_valid(skb)) { + /* Invalid skb pointer */ + skw_dbg("wrong address p_data:0x%lx from FW\n", (unsigned long)sg_addr); + continue; + } + + skb->dev->stats.tx_packets++; + skb->dev->stats.tx_bytes += SKW_SKB_TXCB(skb)->skb_native_len; + //kfree_skb(skb); + skw_skb_kfree(skw, skb); + atomic_dec(&skw->txqlen_pending); + } + + SKW_KFREE(sglist); +} + +#ifdef CONFIG_RPS +int skw_init_rps_map(struct skw_iface *iface) +{ + int i, cpu; + struct rps_map *map, *old_map; + static DEFINE_SPINLOCK(rps_map_lock); + struct netdev_rx_queue *queue = iface->ndev->_rx; + + map = kzalloc(max_t(unsigned int, + RPS_MAP_SIZE(cpumask_weight(cpu_online_mask)), L1_CACHE_BYTES), + GFP_KERNEL); + if (!map) + return -ENOMEM; + + i = 0; + for_each_cpu(cpu, cpu_online_mask) + if (cpu != iface->cpu_id) + map->cpus[i++] = cpu; + + if (i) { + map->len = i; + } else { + kfree(map); + map = NULL; + } + + spin_lock(&rps_map_lock); + old_map = rcu_dereference_protected(queue->rps_map, + lockdep_is_held(&rps_map_lock)); + rcu_assign_pointer(queue->rps_map, map); + spin_unlock(&rps_map_lock); + + if (map) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)) + static_key_slow_inc(&rps_needed.key); +#else + static_key_slow_inc(&rps_needed); +#endif + } + + if (old_map) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)) + static_key_slow_dec(&rps_needed.key); +#else + static_key_slow_dec(&rps_needed); +#endif + kfree_rcu(old_map, rcu); + } + + return 0; +} +#endif + +static int skw_work_process(struct wiphy *wiphy, struct skw_iface *iface, + int work_id, void *data, int data_len, const u8 *name) +{ + int ret = 0; + struct skw_sg_node *node; + struct skw_ba_action *ba; + struct skw_core *skw = wiphy_priv(wiphy); +// int *umask; + struct skw_mgmt_status *status; + + if (!iface) { + ret = -ENOENT; + goto exit; + } + + skw_log(SKW_WORK, "[%s]: iface: %d, %s (id: %d)\n", + SKW_TAG_WORK, iface ? iface->id : -1, name, work_id); + + switch (work_id) { + case SKW_WORK_BA_ACTION: + ret = skw_send_msg(wiphy, iface->ndev, SKW_CMD_BA_ACTION, + data, data_len, NULL, 0); + break; + + case SKW_WORK_SCAN_TIMEOUT: + skw_scan_done(skw, iface, true); + break; + + case SKW_WORK_ACL_CHECK: + skw_ap_acl_check(wiphy, iface); + break; + + case SKW_WORK_SET_MC_ADDR: + ret = skw_send_msg(wiphy, iface->ndev, SKW_CMD_SET_MC_ADDR, + data, data_len, NULL, 0); + break; + + case SKW_WORK_SET_IP: + ret = skw_send_msg(wiphy, iface->ndev, SKW_CMD_SET_IP, + data, data_len, NULL, 0); + break; + + case SKW_WORK_TX_FREE: + node = data; + skw_work_async_adma_tx_free(skw, node->sg, node->nents); + + break; + + case SKW_WORK_SETUP_TXBA: + ba = data; + + skw_dbg("%s, iface: %d, peer: %d, tid: %d\n", + name, iface->id, ba->peer_idx, ba->tid); + + ret = skw_send_msg(wiphy, iface->ndev, SKW_CMD_BA_ACTION, + data, data_len, NULL, 0); + if (ret) { + struct skw_peer_ctx *ctx; + + skw_err("setup TXBA failed, ret: %d\n", ret); + + ctx = skw_get_ctx(skw, iface->lmac_id, ba->peer_idx); + + skw_peer_ctx_lock(ctx); + + if (ctx->peer) + SKW_CLEAR(ctx->peer->txba.bitmap, BIT(ba->tid)); + + skw_peer_ctx_unlock(ctx); + } + + break; + + case SKW_WORK_TX_ETHER_DATA: + ret = skw_send_msg(wiphy, iface->ndev, SKW_CMD_TX_DATA_FRAME, + data, data_len, NULL, 0); + break; +#if 0 + case SKW_WORK_RADAR_PULSE: + skw_dfs_radar_pulse_event(wiphy, iface, data, data_len); + break; +#endif + + case SKW_WORK_IFACE_RPS_INIT: +#if 0 +#ifdef CONFIG_RPS + umask = (int *)data; + skw_init_rps_map(iface, *umask); +#endif +#endif + break; + + case SKW_WORK_SPLIT_MGMT_TX_STATUS: + status = data; + skw_mgmt_tx_status(iface, status->mgmt_status_cookie, + status->mgmt_status_data, status->mgmt_status_data_len, true); + + skw_dbg("split ack cookie:0x%llx\n", status->mgmt_status_cookie); + + SKW_KFREE(status->mgmt_status_data); + break; + + default: + skw_info("invalid work: %d\n", work_id); + break; + } + +exit: + return ret; +} + +//workaround for bug3384 +static void skw_work_unlock(struct work_struct *work) +{ + struct skw_core *skw = container_of(work, struct skw_core, work_unlock); + + skw_del_timer_work(skw, skw->cmd.data); + __pm_relax(skw->cmd.ws); + up(&skw->cmd.lock); +} + +static void skw_work(struct work_struct *work) +{ + struct sk_buff *skb; + struct skw_work_cb *cb; + struct skw_core *skw = container_of(work, struct skw_core, work); + struct wiphy *wiphy = priv_to_wiphy(skw); + + while (READ_ONCE(skw->work_data.flags) || skb_queue_len(&skw->work_data.work_list)) { + + if (test_bit(SKW_WORK_FLAG_RCU_FREE, &skw->work_data.flags)) { + struct rcu_head *head; + + spin_lock_bh(&skw->work_data.rcu_lock); + + head = skw->work_data.rcu_hdr; + if (head) + skw->work_data.rcu_hdr = head->next; + + spin_unlock_bh(&skw->work_data.rcu_lock); + + if (head) { + synchronize_rcu(); + head->func(head); + } else { + skw->work_data.rcu_tail = &skw->work_data.rcu_hdr; + clear_bit(SKW_WORK_FLAG_RCU_FREE, &skw->work_data.flags); + } + } + + if (test_and_clear_bit(SKW_WORK_FLAG_ASSERT, &skw->work_data.flags)) + skw_hw_assert(skw, false); + + if (test_and_clear_bit(SKW_WORK_FLAG_RESUME, &skw->work_data.flags)) + skw_resume(wiphy); + + if (!skb_queue_len(&skw->work_data.work_list)) + continue; + + skb = skb_dequeue(&skw->work_data.work_list); + cb = SKW_WORK_CB(skb); + if (skb && cb) + skw_work_process(wiphy, cb->iface, cb->id, + skb->data, skb->len, cb->name); + + kfree_skb(skb); + } +} + +void skw_assert_schedule(struct wiphy *wiphy) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + set_bit(SKW_WORK_FLAG_ASSERT, &skw->work_data.flags); + schedule_work(&skw->work); +} + +void skw_work_resume(struct wiphy *wiphy) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + set_bit(SKW_WORK_FLAG_RESUME, &skw->work_data.flags); + schedule_work(&skw->work); +} + +void skw_unlock_schedule(struct wiphy *wiphy) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + //set_bit(SKW_WORK_FLAG_UNLOCK, &skw_work_flags); + schedule_work(&skw->work_unlock); + //schedule_work(&skw->work); +} + +#ifdef CONFIG_SWT6621S_GKI_DRV +void skw_call_rcu(void *core, struct rcu_head *head, rcu_callback_t func) +{ + struct skw_core *skw = core; + + spin_lock_bh(&skw->work_data.rcu_lock); + + head->func = func; + head->next = NULL; + + *skw->work_data.rcu_tail = head; + skw->work_data.rcu_tail = &head->next; + + spin_unlock_bh(&skw->work_data.rcu_lock); + + set_bit(SKW_WORK_FLAG_RCU_FREE, &skw->work_data.flags); + + schedule_work(&skw->work); +} +#endif + +int __skw_queue_work(struct wiphy *wiphy, struct skw_iface *iface, + enum SKW_WORK_ID id, void *data, + int dat_len, const u8 *name) +{ + struct skw_core *skw = wiphy_priv(wiphy); + struct skw_work_cb *wcb; + struct sk_buff *skb; + + skb = dev_alloc_skb(dat_len); + if (!skb) + return -ENOMEM; + + if (data) + skw_put_skb_data(skb, data, dat_len); + + wcb = SKW_WORK_CB(skb); + wcb->iface = iface; + wcb->id = id; + wcb->name = name; + + skb_queue_tail(&skw->work_data.work_list, skb); + schedule_work(&skw->work); + + return 0; +} + +int skw_queue_event_work(struct wiphy *wiphy, struct skw_event_work *work, + struct sk_buff *skb) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + if (!atomic_read(&work->enabled)) + return -EINVAL; + + skb_queue_tail(&work->qlist, skb); + + if (!work_pending(&work->work)) + queue_work(skw->event_wq, &work->work); + + return 0; +} + +void skw_work_init(struct wiphy *wiphy) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + skw->work_data.rcu_hdr = NULL; + skw->work_data.rcu_tail = &skw->work_data.rcu_hdr; + + spin_lock_init(&skw->work_data.rcu_lock); + skb_queue_head_init(&skw->work_data.work_list); + INIT_WORK(&skw->work, skw_work); + INIT_WORK(&skw->work_unlock, skw_work_unlock); +} + +void skw_work_deinit(struct wiphy *wiphy) +{ + struct skw_core *skw = wiphy_priv(wiphy); + + flush_work(&skw->work_unlock); + flush_work(&skw->work); + skb_queue_purge(&skw->work_data.work_list); +} diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_work.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_work.h new file mode 100755 index 0000000..dbda393 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/skw_work.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 __SKW_WORK_MSG_H__ +#define __SKW_WORK_MSG_H__ + +struct skw_sg_node { + struct scatterlist *sg; + int nents; + int status; + int lmac_id; + void *data; +}; + +enum SKW_WORK_ID { + SKW_WORK_BA_ACTION, + SKW_WORK_SCAN_TIMEOUT, + SKW_WORK_ACL_CHECK, + SKW_WORK_SET_MC_ADDR, + SKW_WORK_SET_IP, + SKW_WORK_TX_FREE, + SKW_WORK_SETUP_TXBA, + SKW_WORK_TX_ETHER_DATA, + SKW_WORK_RADAR_PULSE, + SKW_WORK_RADAR_CAC, + SKW_WORK_RADAR_CAC_END, + SKW_WORK_IFACE_RPS_INIT, + SKW_WORK_SPLIT_MGMT_TX_STATUS, +}; + +struct skw_work_cb { + struct skw_iface *iface; + const u8 *name; + u32 id; +}; + +struct skw_event_work { + atomic_t enabled; + struct work_struct work; + struct sk_buff_head qlist; +}; + +struct skw_mgmt_status { + void *mgmt_status_data; + int mgmt_status_data_len; + u64 mgmt_status_cookie; +} __packed; + +static inline struct skw_work_cb *SKW_WORK_CB(struct sk_buff *skb) +{ + return (struct skw_work_cb *)skb->cb; +} + +static inline void skw_event_work_init(struct skw_event_work *work, + work_func_t func) +{ + atomic_set(&work->enabled, 0); + skb_queue_head_init(&work->qlist); + INIT_WORK(&work->work, func); + atomic_set(&work->enabled, 1); +} + +static inline void skw_event_work_deinit(struct skw_event_work *work) +{ + atomic_set(&work->enabled, 0); + cancel_work_sync(&work->work); + skb_queue_purge(&work->qlist); +} + +int __skw_queue_work(struct wiphy *wiphy, struct skw_iface *iface, + enum SKW_WORK_ID id, void *data, + int dat_len, const u8 *name); + +#define skw_queue_work(wiphy, iface, id, data, len) \ + __skw_queue_work(wiphy, iface, id, data, len, #id) + +int skw_queue_event_work(struct wiphy *wiphy, struct skw_event_work *work, + struct sk_buff *skb); + +void skw_assert_schedule(struct wiphy *wiphy); +void skw_unlock_schedule(struct wiphy *wiphy); +void skw_work_init(struct wiphy *wiphy); +void skw_work_deinit(struct wiphy *wiphy); +int skw_init_rps_map(struct skw_iface *iface); +void skw_work_resume(struct wiphy *wiphy); + +#ifdef CONFIG_SWT6621S_GKI_DRV +void skw_call_rcu(void *skw, struct rcu_head *head, rcu_callback_t func); +#endif + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/trace.c b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/trace.c new file mode 100755 index 0000000..a460494 --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/trace.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 <linux/module.h> + +#ifndef __CHECKER__ +#include <linux/ieee80211.h> +#include <linux/nl80211.h> +#include <net/cfg80211.h> + +#define CREATE_TRACE_POINTS +#include "trace.h" + +#endif diff --git a/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/trace.h b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/trace.h new file mode 100755 index 0000000..21ecd8a --- /dev/null +++ b/longan/kernel/linux-4.9/drivers/net/wireless/swt6621s_wifi/trace.h @@ -0,0 +1,684 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/****************************************************************************** + * + * Copyright (C) 2020 SeekWave Technology Co.,Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + ******************************************************************************/ + +#if !defined(__SKW_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __SKW_TRACE_H__ + +#include <linux/tracepoint.h> +#include <linux/etherdevice.h> +#include <linux/version.h> + +#include "skw_rx.h" +#include "skw_msg.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM skwifi + +TRACE_EVENT(skw_tx_add_credit, + TP_PROTO(int mac, int cred), + TP_ARGS(mac, cred), + + TP_STRUCT__entry( + __field(int, mac) + __field(int, cred) + ), + + TP_fast_assign( + __entry->mac = mac; + __entry->cred = cred; + ), + + TP_printk("mac: %d, credit: %d", __entry->mac, __entry->cred) +); + +TRACE_EVENT(skw_tx_xmit, + TP_PROTO(u8 *mac, int peer_idx, int ip_prot, int fix_rate, + int do_csum, int tid, int qlen), + TP_ARGS(mac, peer_idx, ip_prot, fix_rate, do_csum, tid, qlen), + TP_STRUCT__entry( + __array(u8, dest, ETH_ALEN) + __field(int, peer_idx) + __field(int, ip_prot) + __field(int, fix_rate) + __field(int, do_csum) + __field(int, tid) + __field(int, qlen) + ), + + TP_fast_assign( + memcpy(__entry->dest, mac, ETH_ALEN); + __entry->peer_idx = peer_idx; + __entry->ip_prot = ip_prot; + __entry->fix_rate = fix_rate; + __entry->do_csum = do_csum; + __entry->tid = tid; + __entry->qlen = qlen; + ), + + TP_printk("dest: %pM, peer: %d, ip_prot: %d, fix rate: %d, csum: %d, queue[%d]: %d", + __entry->dest, __entry->peer_idx, __entry->ip_prot, + __entry->fix_rate, __entry->do_csum, + __entry->tid, __entry->qlen) +); + +TRACE_EVENT(skw_tx_info, + TP_PROTO(int mac, int ac, int cred, int tx_count, int qlen, int pending_qlen), + TP_ARGS(mac, ac, cred, tx_count, qlen, pending_qlen), + + TP_STRUCT__entry( + __field(int, mac) + __field(int, ac) + __field(int, cred) + __field(int, tx_count) + __field(int, qlen) + __field(int, pending_qlen) + ), + + TP_fast_assign( + __entry->mac = mac; + __entry->ac = ac; + __entry->cred = cred; + __entry->tx_count = tx_count; + __entry->qlen = qlen; + __entry->pending_qlen = pending_qlen; + ), + + TP_printk("mac: %d, ac: %d, credit: %d, qlen: %d, tx count: %d pending_qlen:%d", + __entry->mac, __entry->ac, __entry->cred, + __entry->qlen, __entry->tx_count, __entry->pending_qlen) +); + +TRACE_EVENT(skw_tx_exit_check, + TP_PROTO(int mac, int credit, int pending_qlen), + TP_ARGS(mac, credit, pending_qlen), + + TP_STRUCT__entry( + __field(int, mac) + __field(int, credit) + __field(int, pending_qlen) + ), + + TP_fast_assign( + __entry->mac = mac; + __entry->credit = credit; + __entry->pending_qlen = pending_qlen; + ), + + TP_printk("mac: %d, credit: %d pending_qlen:%d", + __entry->mac, __entry->credit, __entry->pending_qlen) +); + +TRACE_EVENT(skw_txlp_tx_list, + TP_PROTO(s8 *name, int id), + TP_ARGS(name, id), + + TP_STRUCT__entry( + __field(s8 *, name) + __field(int, id) + ), + + TP_fast_assign( + __entry->name = name; + __entry->id = id; + ), + + TP_printk("name: %s, iface id:%d", + __entry->name, __entry->id) +); + +TRACE_EVENT(skw_txlp_mac, + TP_PROTO(int mac, int cred, int current_qlen, int base), + TP_ARGS(mac, cred, current_qlen, base), + + TP_STRUCT__entry( + __field(int, mac) + __field(int, cred) + __field(int, current_qlen) + __field(int, base) + ), + + TP_fast_assign( + __entry->mac = mac; + __entry->cred = cred; + __entry->current_qlen = current_qlen; + __entry->base = base; + ), + + TP_printk("mac:%d, cred:%d, current_qlen:%d, base:%d", + __entry->mac, + __entry->cred, + __entry->current_qlen, + __entry->base) +); + +TRACE_EVENT(skw_txq_prepare, + TP_PROTO(s8 *name, int qlen), + TP_ARGS(name, qlen), + + TP_STRUCT__entry( + __field(s8 *, name) + __field(int, qlen) + ), + + TP_fast_assign( + __entry->name = name; + __entry->qlen = qlen; + ), + + TP_printk("name: %s, qlen:%d", + __entry->name, __entry->qlen) +); + +TRACE_EVENT(skw_get_credit, + TP_PROTO(int credit, int mac), + TP_ARGS(credit, mac), + + TP_STRUCT__entry( + __field(int, credit) + __field(int, mac) + ), + + TP_fast_assign( + __entry->credit = credit; + __entry->mac = mac; + ), + + TP_printk("credit: %d, mac:%d", + __entry->credit, __entry->mac) +); + +#if 0 +TRACE_EVENT(skw_tx_thread, + TP_PROTO(u32 cred, u32 nents, u32 tx_data_len, u32 tx_buff_len, u8 *tx_ac, u32 *cached), + TP_ARGS(cred, nents, tx_data_len, tx_buff_len, tx_ac, cached), + + TP_STRUCT__entry( + __field(u32, cred) + __field(u32, nents) + __field(u32, tx_data_len) + __field(u32, tx_buff_len) + __array(u8, tx_ac, SKW_WMM_AC_MAX) + __array(u32, cached, SKW_WMM_AC_MAX) + ), + + TP_fast_assign( + __entry->cred = cred; + __entry->nents = nents; + __entry->tx_data_len = tx_data_len; + __entry->tx_buff_len = tx_buff_len; + memcpy(__entry->tx_ac, tx_ac, sizeof(u32) * SKW_WMM_AC_MAX); + memcpy(__entry->cached, cached, sizeof(u32) * SKW_WMM_AC_MAX); + ), + + TP_printk("creds: %d, tx: %d, len: %d/%d, VO[%d:%d] VI[%d:%d] BE[%d:%d] BK[%d:%d]", + __entry->cred, __entry->nents, + __entry->tx_data_len, + __entry->tx_buff_len, + + __entry->tx_ac[SKW_WMM_AC_VO], + __entry->cached[SKW_WMM_AC_VO], + + __entry->tx_ac[SKW_WMM_AC_VI], + __entry->cached[SKW_WMM_AC_VI], + + __entry->tx_ac[SKW_WMM_AC_BE], + __entry->cached[SKW_WMM_AC_BE], + + __entry->tx_ac[SKW_WMM_AC_BK], + __entry->cached[SKW_WMM_AC_BK]) +); + +TRACE_EVENT(skw_tx_thread_ret, + TP_PROTO(int ret, u32 pending_qlen, u32 ac_reset), + TP_ARGS(ret, pending_qlen, ac_reset), + + TP_STRUCT__entry( + __field(int, ret) + __field(u32, pending_qlen) + __field(u32, ac_reset) + ), + + TP_fast_assign( + __entry->ret = ret; + __entry->pending_qlen = pending_qlen; + __entry->ac_reset = ac_reset; + ), + + TP_printk("ret: %d, pending: %d, ac_reset: 0x%x", + __entry->ret, __entry->pending_qlen, __entry->ac_reset) +); + +#endif + +TRACE_EVENT(skw_tx_runing, + TP_PROTO(int tx, int pending_qlen, long timeout, int keep_running), + TP_ARGS(tx, pending_qlen, timeout, keep_running), + + TP_STRUCT__entry( + __field(int, tx) + __field(int, pending_qlen) + __field(long, timeout) + __field(int, keep_running) + ), + + TP_fast_assign( + __entry->tx = tx; + __entry->pending_qlen = pending_qlen; + __entry->timeout = timeout; + __entry->keep_running = keep_running; + ), + + TP_printk("tx: %d, pending_qlen: %d, timeout: %ld, pending_qlen: %d", + __entry->tx, __entry->pending_qlen, + __entry->timeout, __entry->keep_running) +); + +TRACE_EVENT(skw_tx_pcie_edma_free, + TP_PROTO(u16 count), + TP_ARGS(count), + + TP_STRUCT__entry( + __field(u16, count) + ), + + TP_fast_assign( + __entry->count = count; + ), + + TP_printk("count: %u", + __entry->count) +); + +TRACE_EVENT(skw_rx_set_reorder_timer, + TP_PROTO(u8 inst, u8 pid, u16 tid, u16 seq, unsigned long rx_time, unsigned long timeout), + TP_ARGS(inst, pid, tid, seq, rx_time, timeout), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u16, tid) + __field(u16, seq) + __field(unsigned long, rx_time) + __field(unsigned long, timeout) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->seq = seq; + __entry->rx_time = rx_time; + __entry->timeout = timeout; + ), + + TP_printk("I: %d, P: %d, T: %d, seq: %d, rx_time: %ld, timeout: %ld", + __entry->inst, __entry->pid, + __entry->tid, __entry->seq, + __entry->rx_time, __entry->timeout) +); + +TRACE_EVENT(skw_rx_reorder_timeout, + TP_PROTO(u8 inst, u8 pid, u16 tid, u16 expired_sn), + TP_ARGS(inst, pid, tid, expired_sn), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u16, tid) + __field(u16, expired_sn) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->expired_sn = expired_sn; + ), + + TP_printk("I: %d, P: %d, T: %d, expired_sn: %d", + __entry->inst, __entry->pid, + __entry->tid, __entry->expired_sn) +); + +TRACE_EVENT(skw_rx_expired_release, + TP_PROTO(u8 inst, u8 pid, u16 tid, u16 expired_sn), + TP_ARGS(inst, pid, tid, expired_sn), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u16, tid) + __field(u16, expired_sn) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->expired_sn = expired_sn; + ), + + TP_printk("I: %d, P: %d, T: %d, expired_sn: %d", + __entry->inst, __entry->pid, + __entry->tid, __entry->expired_sn) +); + +TRACE_EVENT(skw_rx_data, + TP_PROTO(u8 inst, u8 pid, u8 tid, u8 filter, u16 seq, u8 qos, + u8 retry, u8 amsdu, u8 idx, u8 first, u8 last, bool fake_ack), + TP_ARGS(inst, pid, tid, filter, seq, qos, retry, amsdu, + idx, first, last, fake_ack), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u8, tid) + __field(u8, filter) + __field(u16, seq) + __field(u8, qos) + __field(u8, retry) + __field(u8, amsdu) + __field(u8, idx) + __field(u8, first) + __field(u8, last) + __field(bool, fake_ack) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->filter = filter; + __entry->seq = seq; + __entry->qos = qos; + __entry->retry = retry; + __entry->amsdu = amsdu; + __entry->idx = idx; + __entry->first = first; + __entry->last = last; + __entry->fake_ack = fake_ack; + ), + + TP_printk("I: %d, P: %d, T: %d, filter: %d, seq: %d, qos: %d, " + "retry: %d, amsdu: %d, idx: %d(F: %d, L: %d), fake ack: %d", + __entry->inst, __entry->pid, + __entry->tid, __entry->filter, + __entry->seq, __entry->qos, + __entry->retry, __entry->amsdu, + __entry->idx, __entry->first, + __entry->last, __entry->fake_ack) +); + +TRACE_EVENT(skw_rx_reorder, + TP_PROTO(u8 inst, u8 pid, u16 tid, u16 sn, u8 is_amsdu, u8 amsdu_idx, + u16 win_size, u16 win_start, u32 stored_num, + bool release, bool drop), + TP_ARGS(inst, pid, tid, sn, is_amsdu, amsdu_idx, win_size, + win_start, stored_num, release, drop), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u16, tid) + __field(u16, sn) + __field(u8, is_amsdu) + __field(u8, amsdu_idx) + __field(u16, win_size) + __field(u16, win_start) + __field(u32, stored_num) + __field(bool, release) + __field(bool, drop) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->sn = sn; + __entry->is_amsdu = is_amsdu; + __entry->amsdu_idx = amsdu_idx; + __entry->win_size = win_size; + __entry->win_start = win_start; + __entry->stored_num = stored_num; + __entry->release = release; + __entry->drop = drop; + ), + + TP_printk("ssn: %d, sn: %d (%d, %d), release: %d, drop: %d, stored: %d, " + "I: %d, P: %d, T: %d, win_sz: %d, amsdu: %d, idx: %d", + __entry->win_start, __entry->sn, + __entry->win_start % __entry->win_size, + __entry->sn % __entry->win_size, + __entry->release, __entry->drop, + __entry->stored_num, + __entry->inst, __entry->pid, + __entry->tid, __entry->win_size, + __entry->is_amsdu, __entry->amsdu_idx) +); + +TRACE_EVENT(skw_rx_reorder_release, + TP_PROTO(u8 inst, u8 pid, u16 tid, u16 win_start, u16 seq, u16 index, u16 ssn, u16 left), + TP_ARGS(inst, pid, tid, win_start, seq, index, ssn, left), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u16, tid) + __field(u16, win_start) + __field(u16, seq) + __field(u16, index) + __field(u16, ssn) + __field(u16, left) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->win_start = win_start; + __entry->seq = seq; + __entry->index = index; + __entry->ssn = ssn; + __entry->left = left; + ), + + TP_printk("I: %d, P: %d, T: %d, win start: %d, seq: %d (index: %d), ssn: %d, left: %d", + __entry->inst, __entry->pid, __entry->tid, __entry->win_start, + __entry->seq, __entry->index, __entry->ssn, __entry->left) +); + +TRACE_EVENT(skw_rx_force_release, + TP_PROTO(u8 inst, u8 pid, u16 tid, u16 index, u16 ssn, u16 target, u16 left, int reason), + TP_ARGS(inst, pid, tid, index, ssn, target, left, reason), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u16, tid) + __field(u16, index) + __field(u16, ssn) + __field(u16, target) + __field(u16, left) + __field(int, reason) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->index = index; + __entry->ssn = ssn; + __entry->target = target; + __entry->left = left; + __entry->reason = reason; + ), + + TP_printk("I: %d, P: %d, T: %d, seq: %d(index: %d), target: %d, left: %d, reason: %d", + __entry->inst, __entry->pid, __entry->tid, + __entry->ssn, __entry->index, __entry->target, + __entry->left, __entry->reason) +); + +TRACE_EVENT(skw_rx_handler_seq, + TP_PROTO(u16 sn, u8 msdu_filter), + TP_ARGS(sn, msdu_filter), + + TP_STRUCT__entry( + __field(u16, sn) + __field(u8, msdu_filter) + ), + + TP_fast_assign( + __entry->sn = sn; + __entry->msdu_filter = msdu_filter; + ), + + TP_printk("seq: %d msdu_filter: %u", + __entry->sn, __entry->msdu_filter) +); + +TRACE_EVENT(skw_rx_add_ba, + TP_PROTO(u8 inst, u8 pid, u16 tid, u16 ssn, u16 buf_size), + TP_ARGS(inst, pid, tid, ssn, buf_size), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u16, tid) + __field(u16, ssn) + __field(u16, buf_size) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->ssn = ssn; + __entry->buf_size = buf_size; + ), + + TP_printk("I: %d, P: %d, T: %d, ssn: %d, buf_size: %d", + __entry->inst, __entry->pid, __entry->tid, + __entry->ssn, __entry->buf_size) +); + +TRACE_EVENT(skw_rx_update_ba, + TP_PROTO(u8 inst, u8 pid, u16 tid, u16 ssn), + TP_ARGS(inst, pid, tid, ssn), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, pid) + __field(u16, tid) + __field(u16, ssn) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->pid = pid; + __entry->tid = tid; + __entry->ssn = ssn; + ), + + TP_printk("I: %d, P: %d, T: %d, ssn: %d", + __entry->inst, __entry->pid, + __entry->tid, __entry->ssn) +); + +TRACE_EVENT(skw_rx_del_ba, + TP_PROTO(u16 tid), + TP_ARGS(tid), + + TP_STRUCT__entry( + __field(u16, tid) + ), + + TP_fast_assign( + __entry->tid = tid; + ), + + TP_printk("del ba, tid: %d", __entry->tid) +); + +TRACE_EVENT(skw_rx_irq, + TP_PROTO(int nents, int idx, int port, int len), + TP_ARGS(nents, idx, port, len), + + TP_STRUCT__entry( + __field(int, nents) + __field(int, idx) + __field(int, port) + __field(int, len) + ), + + TP_fast_assign( + __entry->nents = nents; + __entry->idx = idx; + __entry->port = port; + __entry->len = len; + ), + + TP_printk("nents: %d, idx: %d, port: %d, len: %d", + __entry->nents, __entry->idx, + __entry->port, __entry->len) +); + +TRACE_EVENT(skw_msg_rx, + TP_PROTO(u8 inst, u8 type, u16 id, u16 seq, u16 len), + TP_ARGS(inst, type, id, seq, len), + + TP_STRUCT__entry( + __field(u8, inst) + __field(u8, type) + __field(u16, id) + __field(u16, seq) + __field(u16, len) + ), + + TP_fast_assign( + __entry->inst = inst; + __entry->type = type; + __entry->id = id; + __entry->seq = seq; + __entry->len = len; + ), + + TP_printk("inst: %d, type: %d, id: %d, seq: %d, len: %d", + __entry->inst, __entry->type, __entry->id, + __entry->seq, __entry->len) +); + +TRACE_EVENT(skw_hw_adma_tx_done, + TP_PROTO(u8 num), + TP_ARGS(num), + + TP_STRUCT__entry(__field(u8, num)), + + TP_fast_assign(__entry->num = num;), + + TP_printk("num:%d", __entry->num) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include <trace/define_trace.h> diff --git a/longan/kernel/linux-4.9/include/linux/platform_data/skw_platform_data.h b/longan/kernel/linux-4.9/include/linux/platform_data/skw_platform_data.h new file mode 100755 index 0000000..b7f9857 --- /dev/null +++ b/longan/kernel/linux-4.9/include/linux/platform_data/skw_platform_data.h @@ -0,0 +1,227 @@ +/* + * seekwave - Platform data for sv6160 platform. + * + * This software is distributed under the terms of the GNU General Public + * License ("GPL") version 2, as published by the Free Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __SKW_PLATFORM_DATA_H__ +#define __SKW_PLATFORM_DATA_H__ + +#define MAX_PORT_COUNT 8 + +#define WIFIDATA_PORTNO 0 +#define WIFICMD_PORTNO 1 +#define BRDATA_PORTNO 2 +#define BTCMD_PORTNO 3 +#define SKW_LOG 4 +#define SKW_AT 5 +#define SKW_LOOPCHECK 6 +#define SKW_ASSERT 7 + +#define DEVICE_ASSERT_EVENT 0 +#define DEVICE_BSPREADY_EVENT 1 +#define DEVICE_DUMPDONE_EVENT 2 +#define DEVICE_BLOCKED_EVENT 3 +#define DEVICE_DISCONNECT_EVENT 4 +#define DEVICE_DUMPMEM_EVENT 5 +#define DEVICE_SUSPEND_EVENT 6 +#define DEVICE_RESUME_EVENT 7 +#define DEVICE_BOOTUP_EVENT 8 + +#define SV6160_WIRELESS "sv6160_wireless" +#define SV6621S_WIRELESS "sv6621s_wireless" +#define SV6160_BTDRIVER "sv6160_btdriver" +#define SV6316_WIRELESS "sv6316_wireless" + +#define RX_CALLBACK 0 +#define ADMA_TX_CALLBACK 1 +#define SDMA_TX_CALLBACK 2 +#define SKW_ADMA_BUFF_LEN PAGE_SIZE + +struct skw_packet_header { + u32 pad:7; + u32 len:16; + u32 eof:1; + u32 channel:8; +}; + +struct skw_packet2_header { + u32 len:16; + u32 pad:7; + u32 eof:1; + u32 channel:8; +}; + +struct EDMA_Node{ + u64 data_addr:40; + u64 user_len:16; + u64 tx_int:1; + u64 rsv1:6; + u64 done:1; + + u64 next_hdr:40; + u64 edma_no:8; + u64 length:16;//cur_trans_length,except header +} __attribute__((packed)); + +struct skw_operation { + u8 port; + int (*open) (int id, void *callback, void *data); + int (*close) (int id); + int (*read) (int id, char *buff, int len); + int (*write) (int id, char *buff, int len); + /* + * actual : buffer to save actual read /write data size; + * timeout : timeout unit ms. + * return value + * ret=0: transfer successfully, actual save the data size + * ret=-ETIMEDOUT, timer out + * otherwise error happened. + */ + int (*read_tm) (int id, char *buff, int len, int *actual, int timeout); + int (*write_tm) (int id, char *buff, int len, int *actual, int timeout); +}; + +/***************************************************************** + * add EDMA parameters, usage: + * direction: CP is source: 1; AP is source: 0, + * priority: EDMA channel priority: 4 level: 0(highest)~3(lowest) + * split: 0: not split; + * 1: split. + * AP driver: not successive + * ring: 1:ring node; 0: list mode; AP driver:ring buffer. + * endian: 0; + * irq_threshold: processed node count that raise complete IRQ. + * req_mode: 1:linklist mode + * 0:std mode + * fix_linklist_len(linklist mode): + * 1: current node transfer length is trsc_len + * 0: current node transfer length in head + * trsc_len: ditto + * opposite_node_done: + * 1: report local complete int to opposite end + * 0: no + * node_count: node count in list ready for EDMA to process. + * header: this is the free node EDMA is going to process. + * timeout: timeout value for Complete IRQ, timeout unit is uS. + * maximum timeout value is 4ms. + * list header is set to CHNn_SRC_DSCR_PTR_HIGH(direction=1)/ + * or to CHNn_DST_DSCR_PTR_HIGH(direction=0)/ + * context: save service context to be referred in callbck function. + * header: ring buffer header, it's better to be aligned to 8 bytes. + * header = &edma_node.next_addr_l32 + ******************************************************************/ + +struct skw_channel_cfg { + u8 direction; + u8 priority; + u8 split; + u8 ring; + u8 endian; + u8 irq_threshold; + u8 req_mode; + u8 fix_linklist_len; + u16 trsc_len; + u8 opposite_node_done; + u16 timeout; + dma_addr_t header; //PCIe Address + u16 node_count; + u32 buf_cnt; + u32 buf_level; + void *context; + int (*complete_callback) (void *context, void *header, void *tailed, int node_count); + int (*empty_callback) (void *context); + void (*rx_callback) (void *context, void *data_addr, u16 data_len); +}; +typedef int (*rx_submit_fn) (int id, struct scatterlist *sg, int nets, void *data); +typedef int (*adma_callback) (int id, struct scatterlist *sg, int nets, void *data, int status); +typedef int (*sdma_callback) (int id, void *buffer, int size, void *data, int status); +typedef int (*status_notify) (u8 event); +struct sv6160_platform_data { + u8 data_port; + u8 cmd_port; + u8 audio_port; + u8 bus_type; + +#define SDIO_LINK (0<<0) +#define USB_LINK (1<<0) +#define PCIE_LINK (2<<0) +#define SDIO2_LINK (3<<0) +#define USB2_LINK (4<<0) + +#define TYPE_MASK 0x07 +#define TX_ADMA (0<<3) +#define TX_SDMA (1<<3) +#define TX_ASYN (1<<4) +#define RX_ADMA (0<<5) +#define RX_SDMA (1<<5) +#define CP_DBG (0<<6) +#define CP_RLS (1<<6) +#define REINIT_USB_STR (1<<7) + + + u32 max_buffer_size; + u16 align_value; + char chipid[16]; + char *port_name; + + int (*hw_channel_init) (int id, void *channl_cfg, void *data); + int (*hw_channel_deinit) (int id); + int (*open_port) (int id, void *callback, void *data); + int (*hw_adma_tx)(int id, struct scatterlist *sg, int nets, int size); + int (*hw_sdma_tx)(int id, char *buff, int len); + int (*hw_adma_tx_async)(int id, struct scatterlist *sg, int nets, int size); + int (*hw_sdma_tx_async)(int id, char *buff, int len); + int (*hw_sdma_rx)(int id, char *buff, int len); + int (*read_timeout)(int id, char *buffer, int len, int timeout); + int (*write_timeout)(int id, char *buffer, int len, int timeout); + int (*callback_register)(int id, void *function, void *para); + int (*close_port) (int id); + int (*modem_assert) (void); + dma_addr_t (*phyaddr_to_pcieaddr)(dma_addr_t phy_addr); + dma_addr_t (*pcieaddr_to_phyaddr)(dma_addr_t pcie_addr); + dma_addr_t (*virtaddr_to_pcieaddr)(void *virt_addr); + u64 (*pcieaddr_to_virtaddr)(dma_addr_t phy_addr); + struct skw_operation at_ops; + void (*modem_register_notify)(struct notifier_block *nb); + void (*modem_unregister_notify)(struct notifier_block *nb); + int (*wifi_get_credit)(void); + int (*service_start)(void); + int (*service_stop)(void); + int (*skw_dloader)(int index); + int (*wifi_store_credit)(unsigned char val); + int (*skw_dump_mem)(unsigned int system_addr, void *buf,unsigned int len); + int (*tx_callback_register)(int id, void *function, void *para); + int (*submit_list_to_edma_channel)(int ch_id, void *header, int count); + void (*edma_mask_irq)(int channel); + void (*edma_unmask_irq)(int channel); + int (*wifi_power_on)(int is_on); + void (*usb_speed_switch)(char *mode); + int (*edma_get_node_tot_cnt)(int channel); + u32 (*edma_clear_src_node_count)(int channel); + void (*rx_thread_wakeup)(void); + int (*suspend_adma_cmd)(int id, struct scatterlist *sg, int nets, int size); + int (*suspend_sdma_cmd)(int id, char *buff, int len); + void (*dump_modem_memory)(char *buffer, int size, int *log_size); + int (*bluetooth_log_disable)(int disable); + /* + * add edma channel mask for WIFI platform device. + * value=0x7ff, means first 11 channels owned by WIFI. + */ + u64 wifi_channel_map;//0x7ff; + char *debug_info; +}; + +#endif -- Gitblit v1.6.2