22689 lines
717 KiB
Diff
22689 lines
717 KiB
Diff
diff -Naur /dev/null b/drivers/net/wireless/hflps170/Kconfig
|
|
--- /dev/null 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/Kconfig 2021-04-17 22:35:12.676948473 +0300
|
|
@@ -0,0 +1,4 @@
|
|
+config HFLPS170
|
|
+ tristate "hi-flying HF-LPS170 SDIO WiFi"
|
|
+ help
|
|
+ Help message of HFLPS170
|
|
diff -Naur /dev/null b/drivers/net/wireless/hflps170/Makefile
|
|
--- /dev/null 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/Makefile 2021-04-17 23:25:30.908143624 +0300
|
|
@@ -0,0 +1,91 @@
|
|
+BL_VERS_NUM=5.5.0.0
|
|
+
|
|
+CONFIG_BL_FULLMAC ?= m
|
|
+
|
|
+KERNELVERSION := $(shell uname -r)
|
|
+
|
|
+subdir-ccflags-$(CONFIG_DEBUG_FS) += -DCONFIG_BL_DEBUGFS
|
|
+subdir-ccflags-y += -I$(src)/fullmac
|
|
+
|
|
+# FW VARS
|
|
+subdir-ccflags-y += -DNX_VIRT_DEV_MAX=4
|
|
+subdir-ccflags-y += -DNX_REMOTE_STA_MAX=10
|
|
+subdir-ccflags-y += -DNX_CHAN_CTXT_CNT=3
|
|
+
|
|
+obj-$(CONFIG_BL_FULLMAC) += fullmac/
|
|
+
|
|
+#KERNELDIR = ~/Workspace/linux-4.10
|
|
+KERNELDIR ?= /lib/modules/$(KERNELVERSION)/build
|
|
+
|
|
+# Enable A-MSDU support (need FW support)
|
|
+## Select this if FW is compiled with AMSDU support
|
|
+CONFIG_BL_SPLIT_TX_BUF ?= n
|
|
+## Select this TO send AMSDU
|
|
+CONFIG_BL_AMSDUS_TX ?= n
|
|
+
|
|
+# Enable HW queue for Broadcast/Multicast traffic (need FW support)
|
|
+CONFIG_BL_BCMC ?= y
|
|
+
|
|
+# extra DEBUG config
|
|
+CONFIG_BL_SW_PROFILING ?= n
|
|
+CONFIG_BL_DBG ?= n
|
|
+CONFIG_AUTO_DNLD ?= y
|
|
+
|
|
+obj-m := bl_fdrv.o
|
|
+bl_fdrv-y := bl_cfgfile.o \
|
|
+ fullmac/bl_main.o \
|
|
+ bl_mod_params.o \
|
|
+ bl_platform.o \
|
|
+ bl_sdio.o \
|
|
+ bl_msg_tx.o \
|
|
+ bl_msg_rx.o \
|
|
+ bl_utils.o \
|
|
+ bl_cmds.o \
|
|
+ bl_irqs.o \
|
|
+ ipc_host.o \
|
|
+ bl_txq.o \
|
|
+ bl_strs.o \
|
|
+ fullmac/bl_tx.o \
|
|
+ fullmac/bl_rx.o \
|
|
+ bl_v7.o \
|
|
+ bl_bootrom.o
|
|
+bl_fdrv-$(CONFIG_DEBUG_FS) += bl_debugfs.o
|
|
+
|
|
+ccflags-y := -DCONFIG_BL_FULLMAC
|
|
+ccflags-y += -I$(src)
|
|
+ccflags-$(CONFIG_BL_SPLIT_TX_BUF) += -DCONFIG_BL_SPLIT_TX_BUF
|
|
+ifeq ($(CONFIG_BL_SPLIT_TX_BUF), y)
|
|
+ccflags-$(CONFIG_BL_AMSDUS_TX) += -DCONFIG_BL_AMSDUS_TX
|
|
+endif
|
|
+ccflags-$(CONFIG_BL_DBG) += -DCONFIG_BL_DBG
|
|
+ccflags-$(CONFIG_BL_MOD_LEV_DBG) += -DCONFIG_BL_MOD_LEV_DBG
|
|
+
|
|
+ccflags-y += -DCONFIG_USER_MAX=1
|
|
+
|
|
+ifeq ($(CONFIG_BL_BCMC), y)
|
|
+ccflags-y += -DNX_TXQ_CNT=5
|
|
+else
|
|
+ccflags-y += -DNX_TXQ_CNT=4
|
|
+endif
|
|
+
|
|
+
|
|
+quiet_cmd_genvers = GENVERSION $@
|
|
+ cmd_genvers = ($(if $(KBUILD_EXTMOD),,$(srctree)/)$(src)/mkvers.sh bl_version_gen.h $@)
|
|
+
|
|
+$(obj)/fullmac/bl_main.o: $(obj)/bl_version_gen.h
|
|
+
|
|
+$(obj)/bl_version_gen.h: FORCE
|
|
+ chmod 755 $(src)/mkvers.sh
|
|
+ chmod 755 $(src)/mklink.sh
|
|
+ $(call cmd,genvers)
|
|
+
|
|
+clean-files := bl_version_gen.h
|
|
+
|
|
+
|
|
+all: modules
|
|
+
|
|
+modules clean:
|
|
+ @$(PWD)/mklink.sh fullmac
|
|
+ $(MAKE) -C $(KERNELDIR) O=$(KBUILDDIR) M=$(PWD) $@
|
|
+ @$(PWD)/mklink.sh clean
|
|
+
|
|
diff -Naur /dev/null.md b/drivers/net/wireless/hflps170/README.md
|
|
--- /dev/null.md 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/README.md 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,23 @@
|
|
+===============================================================================
|
|
+ U S E R M A N U A L
|
|
+
|
|
+ Copyright (C) BouffaloLab 2017-2020
|
|
+
|
|
+1) FOR DRIVER BUILD
|
|
+
|
|
+ Goto source code directory
|
|
+ make [clean]
|
|
+ The driver bl_fdrv.ko can be found in fullmac directory.
|
|
+ The driver code supports Linux kernel from 3.10 to 5.5.19.
|
|
+
|
|
+2) FOR DRIVER INSTALL
|
|
+
|
|
+ a) Copy firmware image to /lib/firmware/, and rename to wholeimg_if.bin, copy bl_settings.ini to /lib/firmware/.
|
|
+ b) Install WLAN driver
|
|
+ insmod bl_fdrv.ko
|
|
+ c) uninstall driver
|
|
+ ifconfig wlan0 down
|
|
+ rmmod bl_fdrv
|
|
+
|
|
+
|
|
+
|
|
diff -Naur /dev/null_bootrom.c b/drivers/net/wireless/hflps170/bl_bootrom.c
|
|
--- /dev/null_bootrom.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_bootrom.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,444 @@
|
|
+#include <linux/printk.h>
|
|
+#include <linux/string.h>
|
|
+
|
|
+#include "bl_bootrom.h"
|
|
+
|
|
+int bl_bootrom_cmd_len(bootrom_host_cmd_t *cmd)
|
|
+{
|
|
+ return cmd->len + BL_BOOTROM_HOST_CMD_LEN_HEADER;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_bootinfo_get(bootrom_host_cmd_t *cmd)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_BOOTINFO_GET;
|
|
+ cmd->len = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_bootinfo_get_res(bootrom_res_bootinfo_t *bootinfo)
|
|
+{
|
|
+ if ('O' == bootinfo->status[0] &&
|
|
+ 'K' == bootinfo->status[1]) {
|
|
+ printk("[RSP] bootinfo versoin: 0x%08X\n", bootinfo->version);
|
|
+ printk("[RSP] bootinfo password_mode: 0x%02X\n", bootinfo->password_mode);
|
|
+ printk("[RSP] bootinfo sboot_enable: 0x%02X\n", bootinfo->sboot_enable);
|
|
+ printk("[RSP] bootinfo jtag_disable: 0x%02X\n", bootinfo->jtag_disable);
|
|
+ printk("[RSP] bootinfo uart_disable: 0x%02X\n", bootinfo->uart_disable);
|
|
+ printk("[RSP] bootinfo sign_type: 0x%02X\n", bootinfo->sign_type);
|
|
+ printk("[RSP] bootinfo aes_type: 0x%02X\n", bootinfo->aes_type);
|
|
+ printk("[RSP] bootinfo reserved: 0x%02X\n", bootinfo->reserved);
|
|
+ printk("[RSP] bootinfo chip_id: 0x%02X%02X%02X%02X%02X\n",
|
|
+ bootinfo->chip_id[0],
|
|
+ bootinfo->chip_id[1],
|
|
+ bootinfo->chip_id[2],
|
|
+ bootinfo->chip_id[3],
|
|
+ bootinfo->chip_id[4]
|
|
+ );
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootinfo->status[0],
|
|
+ bootinfo->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+int bl_bootrom_cmd_password_load(bootrom_host_cmd_t *cmd, uint8_t *password)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_PASSWORD_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER;
|
|
+ memcpy(cmd->data, password, sizeof(cmd->data));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_password_load_get_res(bootrom_res_password_load_t *bootresponse, int size)
|
|
+{
|
|
+ if (size != (sizeof(bootrom_res_password_load_t)) && size != 1) {
|
|
+ printk("[RSP] password_load response len is not correct %d\n", size);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1] && 1 == size) {
|
|
+ printk("[RSP] password_load response OK\n");
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1] && 3 == size) {
|
|
+ printk("[RSP] password_load response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_jtag_open(bootrom_host_cmd_t *cmd, uint8_t *password)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_JTAG_OPEN;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER;
|
|
+ memcpy(cmd->data, password, sizeof(cmd->data));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_jtag_open_get_res(bootrom_res_jtag_open_t *bootresponse, int size)
|
|
+{
|
|
+ if (size != (sizeof(bootrom_res_jtag_open_t)) && size != 1) {
|
|
+ printk("[RSP] jtag_open response len is not correct %d\n", size);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1] && 1 == size) {
|
|
+ printk("[RSP] jtag_open response OK\n");
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1] && 3 == size) {
|
|
+ printk("[RSP] jtag_open response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+#endif
|
|
+
|
|
+int bl_bootrom_cmd_bootheader_load(bootrom_host_cmd_t *cmd, bootheader_t *header)
|
|
+{
|
|
+ //XXX should caller known the size of cmd->data ?
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_BOOTHEADER_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + sizeof(bootheader_t);
|
|
+ memcpy(cmd->data, header, sizeof(bootheader_t));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_bootheader_load_get_res(bootrom_res_bootheader_load_t *bootresponse)
|
|
+{
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1]) {
|
|
+ printk("[RSP] bootheader_load response OK\n");
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1]) {
|
|
+ printk("[RSP] bootheader_load response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_aesiv_load(bootrom_host_cmd_t *cmd, const uint8_t *aesiv)
|
|
+{
|
|
+ //XXX should caller known the size of cmd->data ?
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_AESIV_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + 20;//FIXME use struct instead
|
|
+ memcpy(cmd->data, aesiv, 20);//FIXME use struct instead, but NOT magic number(16 IV + 4 CRC32)
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_aesiv_load_get_res(bootrom_res_aesiv_load_t *bootresponse)
|
|
+{
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1]) {
|
|
+ printk("[RSP] aesiv_load response OK\n");
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1]) {
|
|
+ printk("[RSP] aesiv_load response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_pkey1_load(bootrom_host_cmd_t *cmd, pkey_cfg_t *pk)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_PK1_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + sizeof(pkey_cfg_t);
|
|
+ memcpy(cmd->data, pk, sizeof(pkey_cfg_t));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_pkey1_load_get_res(bootrom_res_pkey_load_t *bootresponse)
|
|
+{
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1]) {
|
|
+ printk("[RSP] pkey1_load response OK\n");
|
|
+ return 0;
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1]) {
|
|
+ printk("[RSP] pkey1_load response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_pkey2_load(bootrom_host_cmd_t *cmd, pkey_cfg_t *pk)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_PK2_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + sizeof(pkey_cfg_t);
|
|
+ memcpy(cmd->data, pk, sizeof(pkey_cfg_t));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_pkey2_load_get_res(bootrom_res_pkey_load_t *bootresponse)
|
|
+{
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1]) {
|
|
+ printk("[RSP] pkey2_load response OK\n");
|
|
+ return 0;
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1]) {
|
|
+ printk("[RSP] pkey2_load response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_signature1_load(bootrom_host_cmd_t *cmd, uint8_t *signature, int len)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_SIGNATURE1_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + len;//use magic number
|
|
+ memcpy(cmd->data, signature, len);//XXX overflow check
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_signature1_get_res(bootrom_res_signature_load_t *bootresponse)
|
|
+{
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1]) {
|
|
+ printk("[RSP] signature1_load response OK\n");
|
|
+ return 0;
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1]) {
|
|
+ printk("[RSP] signature1_load response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_signature2_load(bootrom_host_cmd_t *cmd, uint8_t *signature, int len)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_SIGNATURE2_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + len;//use magic number
|
|
+ memcpy(cmd->data, signature, len);//XXX overflow check
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_signature2_get_res(bootrom_res_signature_load_t *bootresponse)
|
|
+{
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1]) {
|
|
+ printk("[RSP] signature2_load response OK\n");
|
|
+ return 0;
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1]) {
|
|
+ printk("[RSP] signature2_load response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+int bl_bootrom_cmd_tzc_load(bootrom_host_cmd_t *cmd, uint8_t *tzc, int len)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_TZC_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + len;//use magic number
|
|
+ memcpy(cmd->data, tzc, len);//XXX overflow check
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_tzc_get_res(bootrom_res_tzc_load_t *bootresponse, int size)
|
|
+{
|
|
+ if (size != (sizeof(bootrom_res_tzc_load_t)) && size != 1) {
|
|
+ printk("[RSP] tzc_load response len is not correct %d\n", size);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1] && 1 == size) {
|
|
+ printk("[RSP] tzc_load response OK\n");
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1] && 3 == size) {
|
|
+ printk("[RSP] tzc_load response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+#endif
|
|
+
|
|
+int bl_bootrom_cmd_sectionheader_load(bootrom_host_cmd_t *cmd, const segment_header_t *header)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_SECTIONHEADER_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + sizeof(segment_header_t);
|
|
+ memcpy(cmd->data, header, sizeof(segment_header_t));//XXX overflow check
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_sectionheader_get_res(bootrom_res_sectionheader_load_t *bootresponse)
|
|
+{
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1]) {
|
|
+ printk("[RSP] section_header response OK\n");
|
|
+ return 0;
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1]) {
|
|
+ printk("[RSP] section_header response failed, staus 0x%04X\n", bootresponse->len);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_sectiondata_load(bootrom_host_cmd_t *cmd, const uint8_t *data, int len)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_SECTIONDATA_LOAD;
|
|
+ cmd->len = sizeof(bootrom_host_cmd_t) - BL_BOOTROM_HOST_CMD_LEN_HEADER + len;//use magic number
|
|
+ memcpy(cmd->data, data, len);//XXX overflow check
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_sectiondata_get_res(bootrom_res_sectiondata_load_t *bootresponse)
|
|
+{
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1]) {
|
|
+ printk("[RSP] section_data response OK\n");
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1]) {
|
|
+ printk("[RSP] section_data response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_checkimage_get(bootrom_host_cmd_t *cmd)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_CHECK_IMAGE;
|
|
+ cmd->len = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_checkimage_get_res(bootrom_res_checkimage_t *checkimage)
|
|
+{
|
|
+ if ('O' == checkimage->status[0] &&
|
|
+ 'K' == checkimage->status[1]) {
|
|
+ printk("[RSP] checkimage response OK\n");
|
|
+ return 0;
|
|
+ } else if ('F' == checkimage->status[0] &&
|
|
+ 'L' == checkimage->status[1]) {
|
|
+ printk("[RSP] checkimage response failed, staus 0x%04X\n", checkimage->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ checkimage->status[0],
|
|
+ checkimage->status[1]
|
|
+ );
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_runimage_get(bootrom_host_cmd_t *cmd)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_RUN;
|
|
+ cmd->len = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_runimage_get_res(bootrom_res_runimage_t *runimage)
|
|
+{
|
|
+ if ('O' == runimage->status[0] &&
|
|
+ 'K' == runimage->status[1]) {
|
|
+ printk("[RSP] runimage response OK\n");
|
|
+ } else if ('F' == runimage->status[0] &&
|
|
+ 'L' == runimage->status[1]) {
|
|
+ printk("[RSP] runimage response failed, staus 0x%04X\n", runimage->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ runimage->status[0],
|
|
+ runimage->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_run(bootrom_host_cmd_t *cmd)
|
|
+{
|
|
+ cmd->id = BL_BOOTROM_HOST_CMD_RUN;
|
|
+ cmd->len = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_bootrom_cmd_run_get_res(bootrom_res_run_t *bootresponse, int size)
|
|
+{
|
|
+ if (size != (sizeof(bootrom_res_sectiondata_load_t)) && size != 1) {
|
|
+ printk("[RSP] section_data response len is not correct %d\n", size);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ('O' == bootresponse->status[0] &&
|
|
+ 'K' == bootresponse->status[1] && 1 == size) {
|
|
+ printk("[RSP] section_data response OK\n");
|
|
+ } else if ('F' == bootresponse->status[0] &&
|
|
+ 'L' == bootresponse->status[1] && 3 == size) {
|
|
+ printk("[RSP] section_data response failed, staus 0x%04X\n", bootresponse->code);
|
|
+ } else {
|
|
+ printk("[RSP] unkown status:%c%c\n",
|
|
+ bootresponse->status[0],
|
|
+ bootresponse->status[1]
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
diff -Naur /dev/null_bootrom.h b/drivers/net/wireless/hflps170/bl_bootrom.h
|
|
--- /dev/null_bootrom.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_bootrom.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,321 @@
|
|
+#ifndef __BL_BOOTROOM_H__
|
|
+#define __BL_BOOTROOM_H__
|
|
+#include <linux/types.h>
|
|
+#pragma pack(push, 1)
|
|
+
|
|
+#define BFLB_BOOTROM_HASH_SIZE 256/8
|
|
+#define BFLB_BOOTROM_SIGN_MAXSIZE 2048/8
|
|
+#define BFLB_BOOTROM_ECC_KEYXSIZE 256/8
|
|
+#define BFLB_BOOTROM_ECC_KEYYSIZE 256/8
|
|
+
|
|
+typedef struct segment_header
|
|
+{
|
|
+ uint32_t destaddr;
|
|
+ uint32_t len;
|
|
+ uint32_t rsv;
|
|
+ uint32_t crc32;
|
|
+} segment_header_t;
|
|
+
|
|
+typedef struct spi_Flash_Cfg_Tag
|
|
+{
|
|
+ uint8_t ioMode;
|
|
+ uint8_t cReadSupport;
|
|
+ uint8_t clk_delay;
|
|
+ uint8_t rsvd[1];
|
|
+
|
|
+ uint8_t resetEnCmd;
|
|
+ uint8_t resetCmd;
|
|
+ uint8_t resetCreadCmd;
|
|
+ uint8_t rsvd_reset[1];
|
|
+
|
|
+ uint8_t jedecIdCmd; /* jedec id cmd */
|
|
+ uint8_t jedecIdCmdDmyClk;
|
|
+ uint8_t qpiJedecIdCmd;
|
|
+ uint8_t qpiJedecIdCmdDmyClk;
|
|
+
|
|
+ uint8_t sectorSize; /* *1024bytes */
|
|
+ uint8_t capBase; /* 0x17 for 64Mbits,0x18 for 128Mbits, 0x19 for 256Mbits */
|
|
+ uint16_t pageSize; /* page size */
|
|
+
|
|
+ uint8_t chipEraseCmd; /* chip erase cmd */
|
|
+ uint8_t sectorEraseCmd; /* sector erase command */
|
|
+ uint8_t blk32EraseCmd; /* block 32K erase command,some Micron not support */
|
|
+ uint8_t blk64EraseCmd; /* block 64K erase command */
|
|
+
|
|
+ uint8_t writeEnableCmd; /* need before every erase or program */
|
|
+ uint8_t pageProgramCmd; /* page program cmd */
|
|
+ uint8_t qpageProgramCmd; /* qio page program cmd */
|
|
+ uint8_t qppAddrMode; /* gd and winbond and Micron use one line for addr while microchip use four lines */
|
|
+
|
|
+ uint8_t fastReadCmd; /* fast read command */
|
|
+ uint8_t frDmyClk;
|
|
+ uint8_t qpiFastReadCmd; /* qpi fast read command */
|
|
+ uint8_t qpiFrDmyClk;
|
|
+
|
|
+ uint8_t fastReadDoCmd; /* fast read dual output command */
|
|
+ uint8_t frDoDmyClk;
|
|
+ uint8_t fastReadDioCmd; /* fast read dual io comamnd */
|
|
+ uint8_t frDioDmyClk;
|
|
+
|
|
+ uint8_t fastReadQoCmd; /* fast read quad output comamnd */
|
|
+ uint8_t frQoDmyClk;
|
|
+ uint8_t fastReadQioCmd; /* fast read quad io comamnd */
|
|
+ uint8_t frQioDmyClk;
|
|
+
|
|
+ uint8_t qpiFastReadQioCmd; /* qpi fast read quad io comamnd */
|
|
+ uint8_t qpiFrQioDmyClk;
|
|
+ uint8_t qpiPageProgramCmd; /* qpi program command */
|
|
+ uint8_t writeVregEnableCmd; /* enable write reg */
|
|
+
|
|
+ /*reg*/
|
|
+ uint8_t wrEnableIndex; /* write enable bit index */
|
|
+ uint8_t qeIndex; /* quad mode enable bit index */
|
|
+ uint8_t busyIndex; /* busy status bit index */
|
|
+ uint8_t wrEnableBit;
|
|
+
|
|
+ uint8_t qeBit;
|
|
+ uint8_t busyBit; /* gd and winbond need set this bit for SPI_QIO, it seems that Micron need not */
|
|
+ uint8_t wrEnableWriteRegLen;
|
|
+ uint8_t wrEnableReadRegLen;
|
|
+
|
|
+ uint8_t qeWriteRegLen;
|
|
+ uint8_t qeReadRegLen;
|
|
+ uint8_t rsvd1;
|
|
+ uint8_t busyReadRegLen;
|
|
+
|
|
+ uint8_t readRegCmd[4];
|
|
+
|
|
+ uint8_t writeRegCmd[4];
|
|
+
|
|
+ uint8_t enterQpi; /* enter qpi command */
|
|
+ uint8_t exitQpi; /* exit qpi command */
|
|
+ uint8_t cReadMode; /* continuous read modo value */
|
|
+ uint8_t cRExit;
|
|
+
|
|
+ uint8_t burstWrapCmd; /* wrap around operation */
|
|
+ uint8_t burstWrapCmdDmyClk;
|
|
+ uint8_t burstWrapDataMode;
|
|
+ uint8_t burstWrapData;
|
|
+
|
|
+ uint8_t deBurstWrapCmd; /* disable wrap around operation */
|
|
+ uint8_t deBurstWrapCmdDmyClk;
|
|
+ uint8_t deBurstWrapDataMode;
|
|
+ uint8_t deBurstWrapData;
|
|
+
|
|
+ uint16_t timeEsector; /* 4K erase time */
|
|
+ uint16_t timeE32k; /* 32K erase time */
|
|
+
|
|
+ uint16_t timeE64k; /* 64K erase time */
|
|
+ uint16_t timePagePgm; /* page program time */
|
|
+
|
|
+ uint32_t timeCe; /* chip erase time */
|
|
+}spi_Flash_Cfg;
|
|
+
|
|
+typedef struct boot_flash_cfg
|
|
+{
|
|
+ uint32_t magiccode; /*'FCFG'*/
|
|
+ spi_Flash_Cfg cfg;
|
|
+ uint32_t crc32;
|
|
+} boot_flash_cfg_t;
|
|
+
|
|
+typedef struct boot_pll_cfg_t
|
|
+{
|
|
+ uint32_t magiccode; /*'PCFG'*/
|
|
+
|
|
+ uint8_t root_clk;
|
|
+ uint8_t xtal_type;
|
|
+ uint8_t pll_clk;
|
|
+ uint8_t hclk_div;
|
|
+
|
|
+ uint8_t bclk_div;
|
|
+ uint8_t flash_clk_div;
|
|
+ uint8_t uart_clk_div;
|
|
+ uint8_t sdu_clk_div;
|
|
+
|
|
+ uint32_t crc32;
|
|
+} boot_pll_cfg_t;
|
|
+
|
|
+typedef struct pkey_cfg
|
|
+{
|
|
+ uint8_t eckeyx[BFLB_BOOTROM_ECC_KEYXSIZE]; //ec key in boot header
|
|
+ uint8_t eckeyy[BFLB_BOOTROM_ECC_KEYYSIZE]; //ec key in boot header
|
|
+ uint32_t crc32;
|
|
+} pkey_cfg_t;
|
|
+
|
|
+typedef struct sign_cfg
|
|
+{
|
|
+ uint32_t sig_len;
|
|
+ uint8_t signature[0];
|
|
+ uint32_t crc32; //crc32 append tail, NOT use this field
|
|
+} sign_cfg_t;
|
|
+
|
|
+typedef struct bootheader {
|
|
+ uint32_t magiccode; /*'BFXP'*/
|
|
+ uint32_t rivison;
|
|
+ boot_flash_cfg_t flashCfg;
|
|
+ boot_pll_cfg_t pllCfg;
|
|
+ union {
|
|
+ struct {
|
|
+ uint32_t sign : 2; /* [3: 2] for sign */
|
|
+ uint32_t encrypt : 2; /* [1: 0] for encrypt*/
|
|
+ uint32_t key_sel : 2; /* [5: 4] for key sel in boot interface*/
|
|
+ uint32_t rsvd6_31 : 26; /* [31:6] rsvd */
|
|
+ } bval;
|
|
+ uint32_t wval;
|
|
+ } seccfg ;
|
|
+ uint32_t segment_cnt;
|
|
+ uint32_t bootentry; /* entry point of the image*/
|
|
+ uint32_t flashoffset;
|
|
+
|
|
+ uint8_t hash[BFLB_BOOTROM_HASH_SIZE]; /*hash of the image*/
|
|
+
|
|
+ uint32_t rsv1;
|
|
+ uint32_t rsv2;
|
|
+ uint32_t crc32;
|
|
+} bootheader_t;
|
|
+
|
|
+typedef struct bootrom_host_cmd {
|
|
+ uint8_t id;
|
|
+ uint8_t reserved;
|
|
+ uint16_t len;
|
|
+ uint8_t data[0];
|
|
+} bootrom_host_cmd_t;
|
|
+
|
|
+typedef struct bootrom_host_cmd_password_load {
|
|
+ uint8_t id;
|
|
+ uint8_t reserved;
|
|
+ uint16_t len;
|
|
+ uint8_t data[8];
|
|
+} bootrom_host_cmd_password_load_t;
|
|
+
|
|
+typedef struct bootrom_host_cmd_jtag_open {
|
|
+ uint8_t id;
|
|
+ uint8_t reserved;
|
|
+ uint16_t len;
|
|
+ uint8_t data[8];
|
|
+} bootrom_host_cmd_jtag_open_t;
|
|
+
|
|
+typedef struct bootrom_res_bootinfo {
|
|
+ uint8_t status[2];
|
|
+ uint16_t len;
|
|
+ uint32_t version;
|
|
+
|
|
+ uint8_t password_mode;
|
|
+ uint8_t sboot_enable;
|
|
+ uint8_t jtag_disable;
|
|
+ uint8_t uart_disable;
|
|
+
|
|
+ uint8_t sign_type;
|
|
+ uint8_t aes_type;
|
|
+ uint8_t reserved;
|
|
+ uint8_t chip_id[5];
|
|
+
|
|
+} bootrom_res_bootinfo_t;
|
|
+
|
|
+typedef struct bootrom_res_password_load {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_password_load_t;
|
|
+
|
|
+typedef struct bootrom_res_jtag_open {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_jtag_open_t;
|
|
+
|
|
+typedef struct bootrom_res_bootheader_load {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_bootheader_load_t;
|
|
+
|
|
+typedef struct bootrom_res_aesiv_load {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_aesiv_load_t;
|
|
+
|
|
+typedef struct bootrom_res_pkey_load {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_pkey_load_t;
|
|
+
|
|
+typedef struct bootrom_res_signature_load {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_signature_load_t;
|
|
+
|
|
+typedef struct bootrom_res_tzc_load {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_tzc_load_t;
|
|
+
|
|
+typedef struct bootrom_res_sectionheader_load {
|
|
+ uint8_t status[2];
|
|
+ uint16_t len;//reuse also as code. TODO: use union
|
|
+ segment_header_t header;
|
|
+} bootrom_res_sectionheader_load_t;
|
|
+
|
|
+typedef struct bootrom_res_sectiondata_load {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_sectiondata_load_t;
|
|
+
|
|
+typedef struct bootrom_res_run {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_run_t;
|
|
+
|
|
+typedef struct bootrom_res_checkimage {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_checkimage_t;
|
|
+
|
|
+typedef struct bootrom_res_runimage {
|
|
+ uint8_t status[2];
|
|
+ uint16_t code;
|
|
+} bootrom_res_runimage_t;
|
|
+#define BL_BOOTROM_HOST_CMD_LEN_HEADER 4
|
|
+
|
|
+#define BL_BOOTROM_HOST_CMD_BOOTINFO_GET 0x10
|
|
+#define BL_BOOTROM_HOST_CMD_BOOTHEADER_LOAD 0x11
|
|
+#define BL_BOOTROM_HOST_CMD_PK1_LOAD 0x12
|
|
+#define BL_BOOTROM_HOST_CMD_PK2_LOAD 0x13
|
|
+#define BL_BOOTROM_HOST_CMD_SIGNATURE1_LOAD 0x14
|
|
+#define BL_BOOTROM_HOST_CMD_SIGNATURE2_LOAD 0x15
|
|
+#define BL_BOOTROM_HOST_CMD_AESIV_LOAD 0x16
|
|
+#define BL_BOOTROM_HOST_CMD_SECTIONHEADER_LOAD 0x17
|
|
+#define BL_BOOTROM_HOST_CMD_SECTIONDATA_LOAD 0x18
|
|
+#define BL_BOOTROM_HOST_CMD_CHECK_IMAGE 0x19
|
|
+#define BL_BOOTROM_HOST_CMD_RUN 0x1A
|
|
+#define BL_BOOTROM_HOST_CMD_CHANGE_RATE 0x20
|
|
+#define BL_BOOTROM_HOST_CMD_RESET 0x21
|
|
+#define BL_BOOTROM_HOST_CMD_FLASH_ERASE 0x30
|
|
+#define BL_BOOTROM_HOST_CMD_FLASH_WRITE 0x31
|
|
+#define BL_BOOTROM_HOST_CMD_FLASH_READ 0x32
|
|
+#define BL_BOOTROM_HOST_CMD_FLASH_BOOT 0x33
|
|
+#define BL_BOOTROM_HOST_CMD_EFUSE_WRITE 0x40
|
|
+#define BL_BOOTROM_HOST_CMD_EFUSE_READ 0x41
|
|
+int bl_bootrom_cmd_len(bootrom_host_cmd_t *cmd);
|
|
+int bl_bootrom_cmd_bootinfo_get(bootrom_host_cmd_t *cmd);
|
|
+int bl_bootrom_cmd_bootinfo_get_res(bootrom_res_bootinfo_t *bootinfo);
|
|
+int bl_bootrom_cmd_bootheader_load(bootrom_host_cmd_t *cmd, bootheader_t *header);
|
|
+int bl_bootrom_cmd_bootheader_load_get_res(bootrom_res_bootheader_load_t *bootresponse);
|
|
+int bl_bootrom_cmd_aesiv_load(bootrom_host_cmd_t *cmd, const uint8_t *aesiv);
|
|
+int bl_bootrom_cmd_aesiv_load_get_res(bootrom_res_aesiv_load_t *bootresponse);
|
|
+int bl_bootrom_cmd_pkey1_load(bootrom_host_cmd_t *cmd, pkey_cfg_t *pk);
|
|
+int bl_bootrom_cmd_pkey1_load_get_res(bootrom_res_pkey_load_t *bootresponse);
|
|
+int bl_bootrom_cmd_pkey2_load(bootrom_host_cmd_t *cmd, pkey_cfg_t *pk);
|
|
+int bl_bootrom_cmd_pkey2_load_get_res(bootrom_res_pkey_load_t *bootresponse);
|
|
+int bl_bootrom_cmd_signature1_load(bootrom_host_cmd_t *cmd, uint8_t *signature, int len);
|
|
+int bl_bootrom_cmd_signature1_get_res(bootrom_res_signature_load_t *bootresponse);
|
|
+int bl_bootrom_cmd_signature2_load(bootrom_host_cmd_t *cmd, uint8_t *signature, int len);
|
|
+int bl_bootrom_cmd_signature2_get_res(bootrom_res_signature_load_t *bootresponse);
|
|
+int bl_bootrom_cmd_sectionheader_load(bootrom_host_cmd_t *cmd, const segment_header_t *header);
|
|
+int bl_bootrom_cmd_sectionheader_get_res(bootrom_res_sectionheader_load_t *bootresponse);
|
|
+int bl_bootrom_cmd_sectiondata_load(bootrom_host_cmd_t *cmd, const uint8_t *data, int len);
|
|
+int bl_bootrom_cmd_sectiondata_get_res(bootrom_res_sectiondata_load_t *bootresponse);
|
|
+int bl_bootrom_cmd_checkimage_get(bootrom_host_cmd_t *cmd);
|
|
+int bl_bootrom_cmd_checkimage_get_res(bootrom_res_checkimage_t *checkimage);
|
|
+int bl_bootrom_cmd_runimage_get(bootrom_host_cmd_t *cmd);
|
|
+int bl_bootrom_cmd_runimage_get_res(bootrom_res_runimage_t *runimage);
|
|
+
|
|
+#pragma pack(pop)
|
|
+#endif
|
|
diff -Naur /dev/null_cfgfile.c b/drivers/net/wireless/hflps170/bl_cfgfile.c
|
|
--- /dev/null_cfgfile.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_cfgfile.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,237 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_configparse.c
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#include <linux/firmware.h>
|
|
+#include <net/mac80211.h>
|
|
+
|
|
+#include "bl_defs.h"
|
|
+#include "bl_cfgfile.h"
|
|
+
|
|
+/**
|
|
+ *
|
|
+ */
|
|
+static const char *bl_find_tag(const u8 *file_data, unsigned int file_size,
|
|
+ const char *tag_name, unsigned int tag_len)
|
|
+{
|
|
+ unsigned int curr, line_start = 0, line_size;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Walk through all the lines of the configuration file */
|
|
+ while (line_start < file_size) {
|
|
+ /* Search the end of the current line (or the end of the file) */
|
|
+ for (curr = line_start; curr < file_size; curr++)
|
|
+ if (file_data[curr] == '\n')
|
|
+ break;
|
|
+
|
|
+ /* Compute the line size */
|
|
+ line_size = curr - line_start;
|
|
+
|
|
+ /* Check if this line contains the expected tag */
|
|
+ if ((line_size == (strlen(tag_name) + tag_len)) &&
|
|
+ (!strncmp(&file_data[line_start], tag_name, strlen(tag_name))))
|
|
+ return (&file_data[line_start + strlen(tag_name)]);
|
|
+
|
|
+ /* Move to next line */
|
|
+ line_start = curr + 1;
|
|
+ }
|
|
+
|
|
+ /* Tag not found */
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Parse the Config file used at init time
|
|
+ */
|
|
+int bl_parse_configfile(struct bl_hw *bl_hw, const char *filename,
|
|
+ struct bl_conf_file *config)
|
|
+{
|
|
+ const struct firmware *config_fw;
|
|
+ u8 dflt_mac[ETH_ALEN] = { 0, 111, 111, 111, 111, 0 };
|
|
+ int ret;
|
|
+ const u8 *tag_ptr;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if ((ret = request_firmware(&config_fw, filename, bl_hw->dev))) {
|
|
+ printk(KERN_CRIT "%s: Failed to get %s (%d)\n", __func__, filename, ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Get MAC Address */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "MAC_ADDR=", strlen("00:00:00:00:00:00"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ u8 *addr = config->mac_addr;
|
|
+ if (sscanf(tag_ptr,
|
|
+ "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
+ addr + 0, addr + 1, addr + 2,
|
|
+ addr + 3, addr + 4, addr + 5) != ETH_ALEN)
|
|
+ memcpy(config->mac_addr, dflt_mac, ETH_ALEN);
|
|
+ } else
|
|
+ memcpy(config->mac_addr, dflt_mac, ETH_ALEN);
|
|
+
|
|
+ printk("MAC Address is:\n%pM\n", config->mac_addr);
|
|
+
|
|
+ /* Release the configuration file */
|
|
+ release_firmware(config_fw);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Parse the Config file used at init time
|
|
+ */
|
|
+int bl_parse_phy_configfile(struct bl_hw *bl_hw, const char *filename,
|
|
+ struct bl_phy_conf_file *config)
|
|
+{
|
|
+ const struct firmware *config_fw;
|
|
+ int ret;
|
|
+ const u8 *tag_ptr;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if ((ret = request_firmware(&config_fw, filename, bl_hw->dev))) {
|
|
+ printk(KERN_CRIT "%s: Failed to get %s (%d)\n", __func__, filename, ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Get Trident path mapping */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "TRD_PATH_MAPPING=", strlen("00"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ u8 val;
|
|
+ if (sscanf(tag_ptr, "%hhx", &val) == 1)
|
|
+ config->trd.path_mapping = val;
|
|
+ else
|
|
+ config->trd.path_mapping = bl_hw->mod_params->phy_cfg;
|
|
+ } else
|
|
+ config->trd.path_mapping = bl_hw->mod_params->phy_cfg;
|
|
+
|
|
+ BL_DBG("Trident path mapping is: %d\n", config->trd.path_mapping);
|
|
+
|
|
+ /* Get DC offset compensation */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "TX_DC_OFF_COMP=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->trd.tx_dc_off_comp) != 1)
|
|
+ config->trd.tx_dc_off_comp = 0;
|
|
+ } else
|
|
+ config->trd.tx_dc_off_comp = 0;
|
|
+
|
|
+ BL_DBG("TX DC offset compensation is: %08X\n", config->trd.tx_dc_off_comp);
|
|
+
|
|
+ /* Get Karst TX IQ compensation value for path0 on 2.4GHz */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_TX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[0]) != 1)
|
|
+ config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
|
|
+ } else
|
|
+ config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
|
|
+
|
|
+ BL_DBG("Karst TX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[0]);
|
|
+
|
|
+ /* Get Karst TX IQ compensation value for path1 on 2.4GHz */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_TX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[1]) != 1)
|
|
+ config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
|
|
+ } else
|
|
+ config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
|
|
+
|
|
+ BL_DBG("Karst TX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[1]);
|
|
+
|
|
+ /* Get Karst RX IQ compensation value for path0 on 2.4GHz */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_RX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[0]) != 1)
|
|
+ config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
|
|
+ } else
|
|
+ config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
|
|
+
|
|
+ BL_DBG("Karst RX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[0]);
|
|
+
|
|
+ /* Get Karst RX IQ compensation value for path1 on 2.4GHz */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_RX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[1]) != 1)
|
|
+ config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
|
|
+ } else
|
|
+ config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
|
|
+
|
|
+ BL_DBG("Karst RX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[1]);
|
|
+
|
|
+ /* Get Karst TX IQ compensation value for path0 on 5GHz */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_TX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[0]) != 1)
|
|
+ config->karst.tx_iq_comp_5G[0] = 0x01000000;
|
|
+ } else
|
|
+ config->karst.tx_iq_comp_5G[0] = 0x01000000;
|
|
+
|
|
+ BL_DBG("Karst TX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[0]);
|
|
+
|
|
+ /* Get Karst TX IQ compensation value for path1 on 5GHz */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_TX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[1]) != 1)
|
|
+ config->karst.tx_iq_comp_5G[1] = 0x01000000;
|
|
+ } else
|
|
+ config->karst.tx_iq_comp_5G[1] = 0x01000000;
|
|
+
|
|
+ BL_DBG("Karst TX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[1]);
|
|
+
|
|
+ /* Get Karst RX IQ compensation value for path0 on 5GHz */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_RX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[0]) != 1)
|
|
+ config->karst.rx_iq_comp_5G[0] = 0x01000000;
|
|
+ } else
|
|
+ config->karst.rx_iq_comp_5G[0] = 0x01000000;
|
|
+
|
|
+ BL_DBG("Karst RX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[0]);
|
|
+
|
|
+ /* Get Karst RX IQ compensation value for path1 on 5GHz */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_RX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[1]) != 1)
|
|
+ config->karst.rx_iq_comp_5G[1] = 0x01000000;
|
|
+ } else
|
|
+ config->karst.rx_iq_comp_5G[1] = 0x01000000;
|
|
+
|
|
+ BL_DBG("Karst RX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[1]);
|
|
+
|
|
+ /* Get Karst default path */
|
|
+ tag_ptr = bl_find_tag(config_fw->data, config_fw->size,
|
|
+ "KARST_DEFAULT_PATH=", strlen("00"));
|
|
+ if (tag_ptr != NULL) {
|
|
+ u8 val;
|
|
+ if (sscanf(tag_ptr, "%hhx", &val) == 1)
|
|
+ config->karst.path_used = val;
|
|
+ else
|
|
+ config->karst.path_used = bl_hw->mod_params->phy_cfg;
|
|
+ } else
|
|
+ config->karst.path_used = bl_hw->mod_params->phy_cfg;
|
|
+
|
|
+ BL_DBG("Karst default path is: %d\n", config->karst.path_used);
|
|
+
|
|
+ /* Release the configuration file */
|
|
+ release_firmware(config_fw);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
diff -Naur /dev/null_cfgfile.h b/drivers/net/wireless/hflps170/bl_cfgfile.h
|
|
--- /dev/null_cfgfile.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_cfgfile.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,35 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_cfgfile.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_CFGFILE_H_
|
|
+#define _BL_CFGFILE_H_
|
|
+
|
|
+/*
|
|
+ * Structure used to retrieve information from the Config file used at Initialization time
|
|
+ */
|
|
+struct bl_conf_file {
|
|
+ u8 mac_addr[ETH_ALEN];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Structure used to retrieve information from the PHY Config file used at Initialization time
|
|
+ */
|
|
+struct bl_phy_conf_file {
|
|
+ struct phy_trd_cfg_tag trd;
|
|
+ struct phy_karst_cfg_tag karst;
|
|
+};
|
|
+
|
|
+int bl_parse_configfile(struct bl_hw *bl_hw, const char *filename,
|
|
+ struct bl_conf_file *config);
|
|
+
|
|
+int bl_parse_phy_configfile(struct bl_hw *bl_hw, const char *filename,
|
|
+ struct bl_phy_conf_file *config);
|
|
+
|
|
+#endif /* _BL_CFGFILE_H_ */
|
|
diff -Naur /dev/null_cmds.c b/drivers/net/wireless/hflps170/bl_cmds.c
|
|
--- /dev/null_cmds.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_cmds.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,266 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_cmds.c
|
|
+ *
|
|
+ * @brief Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to LMAC FW
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#include <linux/list.h>
|
|
+
|
|
+#include "bl_cmds.h"
|
|
+#include "bl_defs.h"
|
|
+#include "bl_msg_tx.h"
|
|
+#include "bl_strs.h"
|
|
+#define CREATE_TRACE_POINTS
|
|
+#include "bl_events.h"
|
|
+#include "bl_debugfs.h"
|
|
+#include "bl_sdio.h"
|
|
+#include "bl_v7.h"
|
|
+#include "bl_irqs.h"
|
|
+
|
|
+
|
|
+static void cmd_mgr_print(struct bl_cmd_mgr *cmd_mgr);
|
|
+
|
|
+void cmd_dump(const struct bl_cmd *cmd)
|
|
+{
|
|
+ BL_DBG("tkn[%d] flags:%04x result:%3d cmd:%4d-%-24s - reqcfm(%4d-%-s)\n",
|
|
+ cmd->tkn, cmd->flags, cmd->result, cmd->id, BL_ID2STR(cmd->id),
|
|
+ cmd->reqid, cmd->reqid != (lmac_msg_id_t)-1 ? BL_ID2STR(cmd->reqid) : "none");
|
|
+}
|
|
+
|
|
+static void cmd_complete(struct bl_cmd_mgr *cmd_mgr, struct bl_cmd *cmd)
|
|
+{
|
|
+ lockdep_assert_held(&cmd_mgr->lock);
|
|
+
|
|
+ list_del(&cmd->list);
|
|
+ cmd_mgr->queue_sz--;
|
|
+
|
|
+ cmd->flags |= BL_CMD_FLAG_DONE;
|
|
+ if (cmd->flags & BL_CMD_FLAG_NONBLOCK) {
|
|
+ kfree(cmd);
|
|
+ } else {
|
|
+ if (BL_CMD_WAIT_COMPLETE(cmd->flags)) {
|
|
+ cmd->result = 0;
|
|
+ complete(&cmd->complete);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static int cmd_mgr_queue(struct bl_cmd_mgr *cmd_mgr, struct bl_cmd *cmd)
|
|
+{
|
|
+ struct bl_hw *bl_hw = container_of(cmd_mgr, struct bl_hw, cmd_mgr);
|
|
+ struct bl_cmd *last;
|
|
+ unsigned long tout;
|
|
+ bool defer_push = false;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+ trace_msg_send(cmd->id);
|
|
+
|
|
+ spin_lock_bh(&cmd_mgr->lock);
|
|
+
|
|
+ if (cmd_mgr->state == BL_CMD_MGR_STATE_CRASHED) {
|
|
+ printk(KERN_CRIT"cmd queue crashed\n");
|
|
+ cmd->result = -EPIPE;
|
|
+ spin_unlock_bh(&cmd_mgr->lock);
|
|
+ return -EPIPE;
|
|
+ }
|
|
+
|
|
+ if (!list_empty(&cmd_mgr->cmds)) {
|
|
+ if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
|
|
+ printk(KERN_CRIT"Too many cmds (%d) already queued\n",
|
|
+ cmd_mgr->max_queue_sz);
|
|
+ cmd->result = -ENOMEM;
|
|
+ spin_unlock_bh(&cmd_mgr->lock);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ last = list_entry(cmd_mgr->cmds.prev, struct bl_cmd, list);
|
|
+ if (last->flags & (BL_CMD_FLAG_WAIT_ACK | BL_CMD_FLAG_WAIT_PUSH)) {
|
|
+ cmd->flags |= BL_CMD_FLAG_WAIT_PUSH;
|
|
+ defer_push = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cmd->flags |= BL_CMD_FLAG_WAIT_ACK;
|
|
+ if (cmd->flags & BL_CMD_FLAG_REQ_CFM)
|
|
+ cmd->flags |= BL_CMD_FLAG_WAIT_CFM;
|
|
+
|
|
+ cmd->tkn = cmd_mgr->next_tkn++;
|
|
+ cmd->result = -EINTR;
|
|
+
|
|
+ if (!(cmd->flags & BL_CMD_FLAG_NONBLOCK))
|
|
+ init_completion(&cmd->complete);
|
|
+
|
|
+ list_add_tail(&cmd->list, &cmd_mgr->cmds);
|
|
+ cmd_mgr->queue_sz++;
|
|
+ tout = msecs_to_jiffies(BL_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
|
|
+ spin_unlock_bh(&cmd_mgr->lock);
|
|
+
|
|
+ if (!defer_push) {
|
|
+ ASSERT_ERR(!(bl_hw->ipc_env->msga2e_hostid));
|
|
+ bl_hw->ipc_env->msga2e_hostid = (void *)cmd;
|
|
+ spin_lock_bh(&bl_hw->cmd_lock);
|
|
+ bl_hw->cmd_sent = true;
|
|
+ spin_unlock_bh(&bl_hw->cmd_lock);
|
|
+ bl_queue_main_work(bl_hw);
|
|
+ }
|
|
+
|
|
+ BL_DBG("send: cmd:%4d-%-24s\n", cmd->id, BL_ID2STR(cmd->id));
|
|
+
|
|
+ if (!(cmd->flags & BL_CMD_FLAG_NONBLOCK)) {
|
|
+ if (!wait_for_completion_timeout(&cmd->complete, tout)) {
|
|
+ printk("wait for cmd cfm timeout!\n");
|
|
+ cmd_dump(cmd);
|
|
+ spin_lock_bh(&cmd_mgr->lock);
|
|
+ cmd_mgr->state = BL_CMD_MGR_STATE_CRASHED;
|
|
+ if (!(cmd->flags & BL_CMD_FLAG_DONE)) {
|
|
+ cmd->result = -ETIMEDOUT;
|
|
+ cmd_complete(cmd_mgr, cmd);
|
|
+ }
|
|
+ spin_unlock_bh(&cmd_mgr->lock);
|
|
+ }
|
|
+ } else {
|
|
+ cmd->result = 0;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int cmd_mgr_llind(struct bl_cmd_mgr *cmd_mgr, struct bl_cmd *cmd)
|
|
+{
|
|
+ struct bl_cmd *cur, *acked = NULL, *next = NULL;
|
|
+ struct bl_hw *bl_hw = container_of(cmd_mgr, struct bl_hw, cmd_mgr);
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ spin_lock(&cmd_mgr->lock);
|
|
+ list_for_each_entry(cur, &cmd_mgr->cmds, list) {
|
|
+ if (!acked) {
|
|
+ if (cur->tkn == cmd->tkn) {
|
|
+ if (WARN_ON_ONCE(cur != cmd)) {
|
|
+ cmd_mgr_print(cmd_mgr);
|
|
+ //cmd_dump(cmd);
|
|
+ }
|
|
+ acked = cur;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if (cur->flags & BL_CMD_FLAG_WAIT_PUSH) {
|
|
+ next = cur;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!acked) {
|
|
+ printk(KERN_CRIT "Error: acked cmd not found\n");
|
|
+ } else {
|
|
+ cmd->flags &= ~BL_CMD_FLAG_WAIT_ACK;
|
|
+ if (BL_CMD_WAIT_COMPLETE(cmd->flags)) {
|
|
+ cmd_complete(cmd_mgr, cmd);
|
|
+ }
|
|
+ }
|
|
+ if (next) {
|
|
+ ASSERT_ERR(!(bl_hw->ipc_env->msga2e_hostid));
|
|
+ bl_hw->ipc_env->msga2e_hostid = (void *)next;
|
|
+ spin_lock_bh(&bl_hw->cmd_lock);
|
|
+ bl_hw->cmd_sent = true;
|
|
+ spin_unlock_bh(&bl_hw->cmd_lock);
|
|
+ bl_queue_main_work(bl_hw);
|
|
+ }
|
|
+ spin_unlock(&cmd_mgr->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int cmd_mgr_msgind(struct bl_cmd_mgr *cmd_mgr, struct ipc_e2a_msg *msg, msg_cb_fct cb)
|
|
+{
|
|
+ struct bl_hw *bl_hw = container_of(cmd_mgr, struct bl_hw, cmd_mgr);
|
|
+ struct bl_cmd *cmd;
|
|
+ bool found = false;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+ trace_msg_recv(msg->id);
|
|
+
|
|
+ spin_lock(&cmd_mgr->lock);
|
|
+ list_for_each_entry(cmd, &cmd_mgr->cmds, list) {
|
|
+ if (cmd->reqid == msg->id &&
|
|
+ (cmd->flags & BL_CMD_FLAG_WAIT_CFM)) {
|
|
+ if (!cb || (cb && !cb(bl_hw, cmd, msg))) {
|
|
+ found = true;
|
|
+ cmd->flags &= ~BL_CMD_FLAG_WAIT_CFM;
|
|
+
|
|
+ if (cmd->e2a_msg && msg->param_len)
|
|
+ memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
|
|
+
|
|
+ if (BL_CMD_WAIT_COMPLETE(cmd->flags)){
|
|
+ cmd_complete(cmd_mgr, cmd);
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ spin_unlock(&cmd_mgr->lock);
|
|
+
|
|
+ if (!found && cb)
|
|
+ {
|
|
+ cb(bl_hw, NULL, msg);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void cmd_mgr_print(struct bl_cmd_mgr *cmd_mgr)
|
|
+{
|
|
+ struct bl_cmd *cur;
|
|
+
|
|
+ spin_lock_bh(&cmd_mgr->lock);
|
|
+ BL_DBG("q_sz/max: %2d / %2d - next tkn: %d\n",
|
|
+ cmd_mgr->queue_sz, cmd_mgr->max_queue_sz,
|
|
+ cmd_mgr->next_tkn);
|
|
+ list_for_each_entry(cur, &cmd_mgr->cmds, list) {
|
|
+ cmd_dump(cur);
|
|
+ }
|
|
+ spin_unlock_bh(&cmd_mgr->lock);
|
|
+}
|
|
+
|
|
+static void cmd_mgr_drain(struct bl_cmd_mgr *cmd_mgr)
|
|
+{
|
|
+ struct bl_cmd *cur, *nxt;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ spin_lock_bh(&cmd_mgr->lock);
|
|
+ list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
|
|
+ list_del(&cur->list);
|
|
+ cmd_mgr->queue_sz--;
|
|
+ if (!(cur->flags & BL_CMD_FLAG_NONBLOCK))
|
|
+ complete(&cur->complete);
|
|
+ }
|
|
+ spin_unlock_bh(&cmd_mgr->lock);
|
|
+}
|
|
+
|
|
+void bl_cmd_mgr_init(struct bl_cmd_mgr *cmd_mgr)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ INIT_LIST_HEAD(&cmd_mgr->cmds);
|
|
+ spin_lock_init(&cmd_mgr->lock);
|
|
+ cmd_mgr->max_queue_sz = BL_CMD_MAX_QUEUED;
|
|
+ cmd_mgr->queue = &cmd_mgr_queue;
|
|
+ cmd_mgr->print = &cmd_mgr_print;
|
|
+ cmd_mgr->drain = &cmd_mgr_drain;
|
|
+ cmd_mgr->llind = &cmd_mgr_llind;
|
|
+ cmd_mgr->msgind = &cmd_mgr_msgind;
|
|
+}
|
|
+
|
|
+void bl_cmd_mgr_deinit(struct bl_cmd_mgr *cmd_mgr)
|
|
+{
|
|
+ cmd_mgr->print(cmd_mgr);
|
|
+ cmd_mgr->drain(cmd_mgr);
|
|
+ cmd_mgr->print(cmd_mgr);
|
|
+ memset(cmd_mgr, 0, sizeof(*cmd_mgr));
|
|
+}
|
|
diff -Naur /dev/null_cmds.h b/drivers/net/wireless/hflps170/bl_cmds.h
|
|
--- /dev/null_cmds.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_cmds.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,82 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_cmds.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_CMDS_H_
|
|
+#define _BL_CMDS_H_
|
|
+
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/completion.h>
|
|
+#include "lmac_msg.h"
|
|
+#include "ipc_shared.h"
|
|
+
|
|
+#ifdef CONFIG_BL_SDM
|
|
+#define BL_80211_CMD_TIMEOUT_MS (20 * 300)
|
|
+#else
|
|
+#define BL_80211_CMD_TIMEOUT_MS 2000
|
|
+#endif
|
|
+
|
|
+#define BL_CMD_FLAG_NONBLOCK BIT(0)
|
|
+#define BL_CMD_FLAG_REQ_CFM BIT(1)
|
|
+#define BL_CMD_FLAG_WAIT_PUSH BIT(2)
|
|
+#define BL_CMD_FLAG_WAIT_ACK BIT(3)
|
|
+#define BL_CMD_FLAG_WAIT_CFM BIT(4)
|
|
+#define BL_CMD_FLAG_DONE BIT(5)
|
|
+/* ATM IPC design makes it possible to get the CFM before the ACK,
|
|
+ * otherwise this could have simply been a state enum */
|
|
+#define BL_CMD_WAIT_COMPLETE(flags) \
|
|
+ (!(flags & (BL_CMD_FLAG_WAIT_ACK | BL_CMD_FLAG_WAIT_CFM)))
|
|
+
|
|
+#define BL_CMD_MAX_QUEUED 8
|
|
+
|
|
+struct bl_hw;
|
|
+struct bl_cmd;
|
|
+typedef int (*msg_cb_fct)(struct bl_hw *bl_hw, struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg);
|
|
+
|
|
+enum bl_cmd_mgr_state {
|
|
+ BL_CMD_MGR_STATE_DEINIT,
|
|
+ BL_CMD_MGR_STATE_INITED,
|
|
+ BL_CMD_MGR_STATE_CRASHED,
|
|
+};
|
|
+
|
|
+struct bl_cmd {
|
|
+ struct list_head list;
|
|
+ lmac_msg_id_t id;
|
|
+ lmac_msg_id_t reqid;
|
|
+ struct lmac_msg *a2e_msg;
|
|
+ char *e2a_msg;
|
|
+ u32 tkn;
|
|
+ u16 flags;
|
|
+
|
|
+ struct completion complete;
|
|
+ u32 result;
|
|
+};
|
|
+
|
|
+struct bl_cmd_mgr {
|
|
+ enum bl_cmd_mgr_state state;
|
|
+ spinlock_t lock;
|
|
+ u32 next_tkn;
|
|
+ u32 queue_sz;
|
|
+ u32 max_queue_sz;
|
|
+
|
|
+ struct list_head cmds;
|
|
+
|
|
+ int (*queue)(struct bl_cmd_mgr *, struct bl_cmd *);
|
|
+ int (*llind)(struct bl_cmd_mgr *, struct bl_cmd *);
|
|
+ int (*msgind)(struct bl_cmd_mgr *, struct ipc_e2a_msg *, msg_cb_fct);
|
|
+ void (*print)(struct bl_cmd_mgr *);
|
|
+ void (*drain)(struct bl_cmd_mgr *);
|
|
+};
|
|
+
|
|
+void cmd_dump(const struct bl_cmd *cmd);
|
|
+void bl_cmd_mgr_init(struct bl_cmd_mgr *cmd_mgr);
|
|
+void bl_cmd_mgr_deinit(struct bl_cmd_mgr *cmd_mgr);
|
|
+
|
|
+#endif /* _BL_CMDS_H_ */
|
|
diff -Naur /dev/null_compat.h b/drivers/net/wireless/hflps170/bl_compat.h
|
|
--- /dev/null_compat.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_compat.h 2021-01-19 10:07:10.000000000 +0200
|
|
@@ -0,0 +1,508 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_compat.h
|
|
+ *
|
|
+ * Ensure driver compilation for linux 3.16 to 5.5.
|
|
+ *
|
|
+ * To avoid too many #if LINUX_VERSION_CODE in the code, when prototype change
|
|
+ * between different kernel version:
|
|
+ * - For external function, define a macro whose name is the function name with
|
|
+ * _compat suffix and prototype (actually the number of parameter) of the
|
|
+ * latest version. Then latest version this macro simply call the function
|
|
+ * and for older kernel version it call the function adapting the api.
|
|
+ * - For internal function (e.g. cfg80211_ops) do the same but the macro name
|
|
+ * doesn't need to have the _compat suffix when the function is not used
|
|
+ * directly by the driver
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#ifndef _bl_COMPAT_H_
|
|
+#define _bl_COMPAT_H_
|
|
+#include <linux/version.h>
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
|
|
+#error "Minimum kernel version supported is 3.16"
|
|
+#endif
|
|
+
|
|
+/* Generic */
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
|
|
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)
|
|
+#define FIELD_PREP(_mask, _val) \
|
|
+ (((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask))
|
|
+#else
|
|
+#include <linux/bitfield.h>
|
|
+#endif // 4.9
|
|
+
|
|
+#if 0
|
|
+/* CFG80211 */
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)
|
|
+#define WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT 0
|
|
+
|
|
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US 0x00
|
|
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US 0x40
|
|
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US 0x80
|
|
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED 0xc0
|
|
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK 0xc0
|
|
+
|
|
+struct element {
|
|
+ u8 id;
|
|
+ u8 datalen;
|
|
+ u8 data[];
|
|
+} __packed;
|
|
+
|
|
+#define for_each_element(_elem, _data, _datalen) \
|
|
+ for (_elem = (const struct 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 = (const struct element *)(_elem->data + _elem->datalen))
|
|
+
|
|
+static inline const struct element *
|
|
+cfg80211_find_elem(u8 eid, const u8 *ies, int len)
|
|
+{
|
|
+ const struct element *elem;
|
|
+ for_each_element(elem, ies, len) {
|
|
+ if (elem->id == (eid))
|
|
+ return elem;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#endif // 5.1
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)
|
|
+#define cfg80211_notify_new_peer_candidate(dev, addr, ie, ie_len, sig_dbm, gfp) \
|
|
+ cfg80211_notify_new_peer_candidate(dev, addr, ie, ie_len, gfp)
|
|
+
|
|
+#define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT BIT(5)
|
|
+#define WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT BIT(6)
|
|
+
|
|
+#endif // 5.0
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
|
|
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK IEEE80211_HE_MAC_CAP3_MAX_A_AMPDU_LEN_EXP_MASK
|
|
+#endif // 4.20
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
|
|
+#define IEEE80211_RADIOTAP_HE 23
|
|
+#define IEEE80211_RADIOTAP_HE_MU 24
|
|
+
|
|
+struct ieee80211_radiotap_he {
|
|
+ __le16 data1, data2, data3, data4, data5, data6;
|
|
+};
|
|
+
|
|
+enum ieee80211_radiotap_he_bits {
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK = 3,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU = 0,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU = 1,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU = 2,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG = 3,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN = 0x0004,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN = 0x0008,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN = 0x0010,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN = 0x0020,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN = 0x0040,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN = 0x0080,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN = 0x0100,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN = 0x0200,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN = 0x0400,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN = 0x0800,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN = 0x1000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN = 0x2000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN = 0x4000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN = 0x8000,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN = 0x0001,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN = 0x0002,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN = 0x0004,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN = 0x0008,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN = 0x0010,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN = 0x0020,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN = 0x0040,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN = 0x0080,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET = 0x3f00,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN = 0x4000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC = 0x8000,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR = 0x003f,
|
|
+ IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE = 0x0040,
|
|
+ IEEE80211_RADIOTAP_HE_DATA3_UL_DL = 0x0080,
|
|
+ IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS = 0x0f00,
|
|
+ IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM = 0x1000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA3_CODING = 0x2000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG = 0x4000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA3_STBC = 0x8000,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE = 0x000f,
|
|
+ IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID = 0x7ff0,
|
|
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1 = 0x000f,
|
|
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2 = 0x00f0,
|
|
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3 = 0x0f00,
|
|
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4 = 0xf000,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC = 0x000f,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ = 0,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ = 1,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ = 2,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ = 3,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_26T = 4,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_52T = 5,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_106T = 6,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_242T = 7,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_484T = 8,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_996T = 9,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_2x996T = 10,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_GI = 0x0030,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_GI_0_8 = 0,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_GI_1_6 = 1,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_GI_3_2 = 2,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE = 0x00c0,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN = 0,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X = 1,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X = 2,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X = 3,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS = 0x0700,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD = 0x3000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_TXBF = 0x4000,
|
|
+ IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG = 0x8000,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_DATA6_NSTS = 0x000f,
|
|
+ IEEE80211_RADIOTAP_HE_DATA6_DOPPLER = 0x0010,
|
|
+ IEEE80211_RADIOTAP_HE_DATA6_TXOP = 0x7f00,
|
|
+ IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY = 0x8000,
|
|
+};
|
|
+
|
|
+struct ieee80211_radiotap_he_mu {
|
|
+ __le16 flags1, flags2;
|
|
+ u8 ru_ch1[4];
|
|
+ u8 ru_ch2[4];
|
|
+};
|
|
+
|
|
+enum ieee80211_radiotap_he_mu_bits {
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS = 0x000f,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN = 0x0010,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM = 0x0020,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN = 0x0040,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN = 0x0080,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN = 0x0100,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN = 0x0200,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN = 0x1000,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU = 0x2000,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN = 0x4000,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN = 0x8000,
|
|
+
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW = 0x0003,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_20MHZ = 0x0000,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_40MHZ = 0x0001,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_80MHZ = 0x0002,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_160MHZ = 0x0003,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN = 0x0004,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP = 0x0008,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS = 0x00f0,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW = 0x0300,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN= 0x0400,
|
|
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU = 0x0800,
|
|
+};
|
|
+
|
|
+enum {
|
|
+ IEEE80211_HE_MCS_SUPPORT_0_7 = 0,
|
|
+ IEEE80211_HE_MCS_SUPPORT_0_9 = 1,
|
|
+ IEEE80211_HE_MCS_SUPPORT_0_11 = 2,
|
|
+ IEEE80211_HE_MCS_NOT_SUPPORTED = 3,
|
|
+};
|
|
+#endif // 4.19
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
|
|
+#define cfg80211_probe_status(ndev, addr, cookie, ack, ack_pwr, pwr_valid, gfp) \
|
|
+ cfg80211_probe_status(ndev, addr, cookie, ack, gfp)
|
|
+#endif // 4.17
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
|
|
+#define bl_cfg80211_add_iface(wiphy, name, name_assign_type, type, params) \
|
|
+ bl_cfg80211_add_iface(wiphy, name, type, u32 *flags, params)
|
|
+#else
|
|
+#define bl_cfg80211_add_iface(wiphy, name, name_assign_type, type, params) \
|
|
+ bl_cfg80211_add_iface(wiphy, name, name_assign_type, type, u32 *flags, params)
|
|
+#endif
|
|
+
|
|
+#define bl_cfg80211_change_iface(wiphy, dev, type, params) \
|
|
+ bl_cfg80211_change_iface(wiphy, dev, type, u32 *flags, params)
|
|
+
|
|
+#define CCFS0(vht) vht->center_freq_seg1_idx
|
|
+#define CCFS1(vht) vht->center_freq_seg2_idx
|
|
+
|
|
+#define nla_parse(tb, maxtype, head, len, policy, extack) \
|
|
+ nla_parse(tb, maxtype, head, len, policy)
|
|
+
|
|
+struct cfg80211_roam_info {
|
|
+ struct ieee80211_channel *channel;
|
|
+ struct cfg80211_bss *bss;
|
|
+ const u8 *bssid;
|
|
+ const u8 *req_ie;
|
|
+ size_t req_ie_len;
|
|
+ const u8 *resp_ie;
|
|
+ size_t resp_ie_len;
|
|
+};
|
|
+
|
|
+#define cfg80211_roamed(_dev, _info, _gfp) \
|
|
+ cfg80211_roamed(_dev, (_info)->channel, (_info)->bssid, (_info)->req_ie, \
|
|
+ (_info)->req_ie_len, (_info)->resp_ie, (_info)->resp_ie_len, _gfp)
|
|
+
|
|
+#else
|
|
+
|
|
+#define CCFS0(vht) vht->center_freq_seg0_idx
|
|
+#define CCFS1(vht) vht->center_freq_seg1_idx
|
|
+#endif // 4.12
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
|
+#define cfg80211_cqm_rssi_notify(dev, event, level, gfp) \
|
|
+ cfg80211_cqm_rssi_notify(dev, event, gfp)
|
|
+#endif // 4.11
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
|
|
+#define ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, check_da, check_sa) \
|
|
+ ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, false)
|
|
+#endif // 4.9
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
|
|
+#define NUM_NL80211_BANDS IEEE80211_NUM_BANDS
|
|
+#endif // 4.7
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
|
|
+#define cfg80211_disconnected(dev, reason, ie, len, local, gfp) \
|
|
+ cfg80211_disconnected(dev, reason, ie, len, gfp)
|
|
+#endif // 4.2
|
|
+
|
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) && !(defined CONFIG_VENDOR_bl)
|
|
+#define ieee80211_chandef_to_operating_class(chan_def, op_class) 0
|
|
+#endif // 4.1
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
|
|
+#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09
|
|
+#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A
|
|
+
|
|
+#define IEEE80211_CCMP_256_MIC_LEN 16
|
|
+#define IEEE80211_GCMP_MIC_LEN 16
|
|
+
|
|
+#define SURVEY_INFO_TIME SURVEY_INFO_CHANNEL_TIME
|
|
+#define SURVEY_INFO_TIME_BUSY SURVEY_INFO_CHANNEL_TIME_BUSY
|
|
+#define SURVEY_INFO_TIME_EXT_BUSY SURVEY_INFO_CHANNEL_TIME_EXT_BUSY
|
|
+#define SURVEY_INFO_TIME_RX SURVEY_INFO_CHANNEL_TIME_RX
|
|
+#define SURVEY_INFO_TIME_TX SURVEY_INFO_CHANNEL_TIME_TX
|
|
+
|
|
+#define SURVEY_TIME(s) s->channel_time
|
|
+#define SURVEY_TIME_BUSY(s) s->channel_time_busy
|
|
+#else
|
|
+#define SURVEY_TIME(s) s->time
|
|
+#define SURVEY_TIME_BUSY(s) s->time_busy
|
|
+#endif // 4.0
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
|
|
+#define cfg80211_ch_switch_started_notify(dev, chandef, count)
|
|
+
|
|
+#define WLAN_BSS_COEX_INFORMATION_REQUEST BIT(0)
|
|
+#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
|
|
+#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4)
|
|
+#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)
|
|
+#define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH BIT(6)
|
|
+#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
|
|
+#define NL80211_FEATURE_TDLS_CHANNEL_SWITCH 0
|
|
+
|
|
+#define STA_TDLS_INITIATOR(sta) 0
|
|
+
|
|
+#define REGULATORY_IGNORE_STALE_KICKOFF 0
|
|
+#else
|
|
+#define STA_TDLS_INITIATOR(sta) sta->tdls_initiator
|
|
+#endif // 3.19
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
|
|
+#define bl_cfg80211_del_station_compat(wiphy, dev, params) \
|
|
+ bl_cfg80211_del_station(wiphy, dev, const u8 *mac)
|
|
+#else
|
|
+#define bl_cfg80211_del_station_compat bl_cfg80211_del_station
|
|
+#endif // 3.18
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
|
|
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags) \
|
|
+ cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags, GFP_ATOMIC)
|
|
+#endif // 3.18
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
|
+#define bl_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, initiator, buf, len) \
|
|
+ bl_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, buf, len)
|
|
+
|
|
+#define bl_cfg80211_del_station_compat(wiphy, dev, params) \
|
|
+ bl_cfg80211_del_station(wiphy, dev, const u8 *mac)
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+struct ieee80211_wmm_ac_param {
|
|
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
|
|
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
|
|
+ __le16 txop_limit;
|
|
+} __packed;
|
|
+
|
|
+struct ieee80211_wmm_param_ie {
|
|
+ u8 element_id; /* Element ID: 221 (0xdd); */
|
|
+ u8 len; /* Length: 24 */
|
|
+ /* required fields for WMM version 1 */
|
|
+ u8 oui[3]; /* 00:50:f2 */
|
|
+ u8 oui_type; /* 2 */
|
|
+ u8 oui_subtype; /* 1 */
|
|
+ u8 version; /* 1 for WMM version 1.0 */
|
|
+ u8 qos_info; /* AP/STA specific QoS info */
|
|
+ u8 reserved; /* 0 */
|
|
+ /* AC_BE, AC_BK, AC_VI, AC_VO */
|
|
+ struct ieee80211_wmm_ac_param ac[4];
|
|
+} __packed;
|
|
+#endif // 3.17
|
|
+
|
|
+
|
|
+/* MAC80211 */
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
|
|
+#define bl_ops_mgd_prepare_tx(hw, vif, duration) \
|
|
+ bl_ops_mgd_prepare_tx(hw, vif)
|
|
+#endif // 4.18
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
|
|
+
|
|
+#define RX_ENC_HT(s) s->flag |= RX_FLAG_HT
|
|
+#define RX_ENC_HT_GF(s) s->flag |= (RX_FLAG_HT | RX_FLAG_HT_GF)
|
|
+#define RX_ENC_VHT(s) s->flag |= RX_FLAG_HT
|
|
+#define RX_ENC_HE(s) s->flag |= RX_FLAG_HT
|
|
+#define RX_ENC_FLAG_SHORT_GI(s) s->flag |= RX_FLAG_SHORT_GI
|
|
+#define RX_ENC_FLAG_SHORT_PRE(s) s->flag |= RX_FLAG_SHORTPRE
|
|
+#define RX_ENC_FLAG_LDPC(s) s->flag |= RX_FLAG_LDPC
|
|
+#define RX_BW_40MHZ(s) s->flag |= RX_FLAG_40MHZ
|
|
+#define RX_BW_80MHZ(s) s->vht_flag |= RX_VHT_FLAG_80MHZ
|
|
+#define RX_BW_160MHZ(s) s->vht_flag |= RX_VHT_FLAG_160MHZ
|
|
+#define RX_NSS(s) s->vht_nss
|
|
+
|
|
+#else
|
|
+#define RX_ENC_HT(s) s->encoding = RX_ENC_HT
|
|
+#define RX_ENC_HT_GF(s) { s->encoding = RX_ENC_HT; \
|
|
+ s->enc_flags |= RX_ENC_FLAG_HT_GF; }
|
|
+#define RX_ENC_VHT(s) s->encoding = RX_ENC_VHT
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
|
|
+#define RX_ENC_HE(s) s->encoding = RX_ENC_VHT
|
|
+#else
|
|
+#define RX_ENC_HE(s) s->encoding = RX_ENC_HE
|
|
+#endif
|
|
+#define RX_ENC_FLAG_SHORT_GI(s) s->enc_flags |= RX_ENC_FLAG_SHORT_GI
|
|
+#define RX_ENC_FLAG_SHORT_PRE(s) s->enc_flags |= RX_ENC_FLAG_SHORTPRE
|
|
+#define RX_ENC_FLAG_LDPC(s) s->enc_flags |= RX_ENC_FLAG_LDPC
|
|
+#define RX_BW_40MHZ(s) s->bw = RATE_INFO_BW_40
|
|
+#define RX_BW_80MHZ(s) s->bw = RATE_INFO_BW_80
|
|
+#define RX_BW_160MHZ(s) s->bw = RATE_INFO_BW_160
|
|
+#define RX_NSS(s) s->nss
|
|
+
|
|
+#endif // 4.12
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
|
|
+#define ieee80211_cqm_rssi_notify(vif, event, level, gfp) \
|
|
+ ieee80211_cqm_rssi_notify(vif, event, gfp)
|
|
+#endif // 4.11
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
|
|
+#define RX_FLAG_MIC_STRIPPED 0
|
|
+#endif // 4.7
|
|
+
|
|
+#ifndef CONFIG_VENDOR_bl_AMSDUS_TX
|
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0))
|
|
+#define bl_ops_ampdu_action(hw, vif, params) \
|
|
+ bl_ops_ampdu_action(hw, vif, enum ieee80211_ampdu_mlme_action action, \
|
|
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size)
|
|
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))
|
|
+#define bl_ops_ampdu_action(hw, vif, params) \
|
|
+ bl_ops_ampdu_action(hw, vif, enum ieee80211_ampdu_mlme_action action, \
|
|
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, \
|
|
+ bool amsdu)
|
|
+#endif // 4.4
|
|
+#endif /* CONFIG_VENDOR_bl_AMSDUS_TX */
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
|
|
+#define IEEE80211_HW_SUPPORT_FAST_XMIT 0
|
|
+#define ieee80211_hw_check(hw, feat) (hw->flags & IEEE80211_HW_##feat)
|
|
+#define ieee80211_hw_set(hw, feat) {hw->flags |= IEEE80211_HW_##feat;}
|
|
+#endif // 4.2
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
|
|
+#define bl_ops_sw_scan_start(hw, vif, mac_addr) \
|
|
+ bl_ops_sw_scan_start(hw)
|
|
+#define bl_ops_sw_scan_complete(hw, vif) \
|
|
+ bl_ops_sw_scan_complete(hw)
|
|
+#endif // 3.19
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
|
+#define bl_ops_hw_scan(hw, vif, hw_req) \
|
|
+ bl_ops_hw_scan(hw, vif, struct cfg80211_scan_request *req)
|
|
+#endif // 3.17
|
|
+
|
|
+/* NET */
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
|
|
+#define bl_select_queue(dev, skb, sb_dev) \
|
|
+ bl_select_queue(dev, skb)
|
|
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
|
|
+#define bl_select_queue(dev, skb, sb_dev) \
|
|
+ bl_select_queue(dev, skb, void *accel_priv)
|
|
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
|
|
+#define bl_select_queue(dev, skb, sb_dev) \
|
|
+ bl_select_queue(dev, skb, void *accel_priv, select_queue_fallback_t fallback)
|
|
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
|
|
+#define bl_select_queue(dev, skb, sb_dev) \
|
|
+ bl_select_queue(dev, skb, sb_dev, select_queue_fallback_t fallback)
|
|
+#elif LINUX_VERSION_CODE <= KERNEL_VERSION(5, 4, 0)
|
|
+#define bl_select_queue(dev, skb, sb_dev) \
|
|
+ bl_select_queue(dev, skb, sb_dev)
|
|
+#endif //3.13
|
|
+
|
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !(defined CONFIG_VENDOR_bl)
|
|
+#define sk_pacing_shift_update(sk, shift)
|
|
+#endif // 4.16
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
|
+#define alloc_netdev_mqs(size, name, assign, setup, txqs, rxqs) \
|
|
+ alloc_netdev_mqs(size, name, setup, txqs, rxqs)
|
|
+
|
|
+#define NET_NAME_UNKNOWN 0
|
|
+#endif // 3.17
|
|
+
|
|
+/* TRACE */
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
|
|
+#define trace_print_symbols_seq ftrace_print_symbols_seq
|
|
+#endif // 4.2
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
|
+#define trace_seq_buffer_ptr(p) p->buffer + p->len
|
|
+#endif // 3.17
|
|
+
|
|
+/* TIME */
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
|
+#define time64_to_tm(t, o, tm) time_to_tm((time_t)t, o, tm)
|
|
+#endif // 4.8
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
|
|
+#define ktime_get_real_seconds get_seconds
|
|
+#endif // 3.19
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
|
+typedef __s64 time64_t;
|
|
+#endif // 3.17
|
|
+
|
|
+/* timer */
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
+#define from_timer(var, callback_timer, timer_fieldname) \
|
|
+ container_of(callback_timer, typeof(*var), timer_fieldname)
|
|
+
|
|
+#define timer_setup(timer, callback, flags) \
|
|
+ __setup_timer(timer, (void (*)(unsigned long))callback, (unsigned long)timer, flags)
|
|
+#endif // 4.14
|
|
+
|
|
+#endif /* _bl_COMPAT_H_ */
|
|
diff -Naur /dev/null_debugfs.c b/drivers/net/wireless/hflps170/bl_debugfs.c
|
|
--- /dev/null_debugfs.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_debugfs.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,2181 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_utils.c
|
|
+ *
|
|
+ * @brief Miscellaneous utility function definitions
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+
|
|
+#include <linux/version.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/kmod.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/sort.h>
|
|
+
|
|
+#include "bl_debugfs.h"
|
|
+#include "bl_msg_tx.h"
|
|
+#include "bl_tx.h"
|
|
+#include "bl_utils.h"
|
|
+#include "bl_sdio.h"
|
|
+#include "bl_platform.h"
|
|
+#include "bl_v7.h"
|
|
+
|
|
+/* some macros taken from iwlwifi */
|
|
+/* TODO: replace with generic read and fill read buffer in open to avoid double
|
|
+ * reads */
|
|
+#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
|
|
+ if (!debugfs_create_file(#name, mode, parent, bl_hw, \
|
|
+ &bl_dbgfs_##name##_ops)) \
|
|
+ goto err; \
|
|
+} while (0)
|
|
+
|
|
+#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \
|
|
+ struct dentry *__tmp; \
|
|
+ __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \
|
|
+ parent, ptr); \
|
|
+ if (IS_ERR(__tmp) || !__tmp) \
|
|
+ goto err; \
|
|
+} while (0)
|
|
+
|
|
+#define DEBUGFS_ADD_X64(name, parent, ptr) do { \
|
|
+ struct dentry *__tmp; \
|
|
+ __tmp = debugfs_create_x64(#name, S_IWUSR | S_IRUSR, \
|
|
+ parent, ptr); \
|
|
+ if (IS_ERR(__tmp) || !__tmp) \
|
|
+ goto err; \
|
|
+} while (0)
|
|
+
|
|
+#define DEBUGFS_ADD_U64(name, parent, ptr, mode) do { \
|
|
+ struct dentry *__tmp; \
|
|
+ __tmp = debugfs_create_u64(#name, mode, \
|
|
+ parent, ptr); \
|
|
+ if (IS_ERR(__tmp) || !__tmp) \
|
|
+ goto err; \
|
|
+} while (0)
|
|
+
|
|
+#define DEBUGFS_ADD_X32(name, parent, ptr) do { \
|
|
+ struct dentry *__tmp; \
|
|
+ __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \
|
|
+ parent, ptr); \
|
|
+ if (IS_ERR(__tmp) || !__tmp) \
|
|
+ goto err; \
|
|
+} while (0)
|
|
+
|
|
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \
|
|
+ struct dentry *__tmp; \
|
|
+ __tmp = debugfs_create_u32(#name, mode, \
|
|
+ parent, ptr); \
|
|
+ if (IS_ERR(__tmp) || !__tmp) \
|
|
+ goto err; \
|
|
+} while (0)
|
|
+
|
|
+/* file operation */
|
|
+#define DEBUGFS_READ_FUNC(name) \
|
|
+ static ssize_t bl_dbgfs_##name##_read(struct file *file, \
|
|
+ char __user *user_buf, \
|
|
+ size_t count, loff_t *ppos);
|
|
+
|
|
+#define DEBUGFS_WRITE_FUNC(name) \
|
|
+ static ssize_t bl_dbgfs_##name##_write(struct file *file, \
|
|
+ const char __user *user_buf, \
|
|
+ size_t count, loff_t *ppos);
|
|
+
|
|
+
|
|
+#define DEBUGFS_READ_FILE_OPS(name) \
|
|
+ DEBUGFS_READ_FUNC(name); \
|
|
+static const struct file_operations bl_dbgfs_##name##_ops = { \
|
|
+ .read = bl_dbgfs_##name##_read, \
|
|
+ .open = simple_open, \
|
|
+ .llseek = generic_file_llseek, \
|
|
+};
|
|
+
|
|
+#define DEBUGFS_WRITE_FILE_OPS(name) \
|
|
+ DEBUGFS_WRITE_FUNC(name); \
|
|
+static const struct file_operations bl_dbgfs_##name##_ops = { \
|
|
+ .write = bl_dbgfs_##name##_write, \
|
|
+ .open = simple_open, \
|
|
+ .llseek = generic_file_llseek, \
|
|
+};
|
|
+
|
|
+
|
|
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
|
|
+ DEBUGFS_READ_FUNC(name); \
|
|
+DEBUGFS_WRITE_FUNC(name); \
|
|
+static const struct file_operations bl_dbgfs_##name##_ops = { \
|
|
+ .write = bl_dbgfs_##name##_write, \
|
|
+ .read = bl_dbgfs_##name##_read, \
|
|
+ .open = simple_open, \
|
|
+ .llseek = generic_file_llseek, \
|
|
+};
|
|
+
|
|
+#define DBG_TIME_HDR "sdiotxtime"
|
|
+#define DBG_TIME_HDR_FMT "%10lld"
|
|
+#define DBG_TIME_HDR_LEN sizeof(DBG_TIME_HDR)
|
|
+static ssize_t bl_dbgfs_dbg_time_read(struct file *file ,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *bl_hw = file->private_data;
|
|
+ char *buf;
|
|
+ int idx, res;
|
|
+ ssize_t read;
|
|
+ int i;
|
|
+ size_t bufsz = sizeof(bl_hw->dbg_time)+DBG_TIME_HDR_LEN + 3000;
|
|
+
|
|
+ bufsz = min_t(size_t, bufsz, count);
|
|
+ buf = kmalloc(bufsz, GFP_ATOMIC);
|
|
+ if (buf == NULL)
|
|
+ return 0;
|
|
+
|
|
+ bufsz--;
|
|
+ idx = 0;
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, DBG_TIME_HDR);
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, "\n");
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+
|
|
+ for (i = 0; i < 49; i++) {
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, DBG_TIME_HDR_FMT,
|
|
+ bl_hw->dbg_time[i].sdio_tx
|
|
+ );
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+ res = scnprintf(&buf[idx], bufsz, "\n");
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+ }
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, "\n");
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
|
|
+ kfree(buf);
|
|
+
|
|
+ return read;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_dbg_time_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return count;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(dbg_time);
|
|
+
|
|
+#define DBG_CDT_HDR "sdioport|txqcredit|hwqcredit|credit|ready|txqidx"
|
|
+#define DBG_CDT_HDR_FMT "%8d|%9d|%9d|%6d|%5d|%6d"
|
|
+#define DBG_CDT_HDR_LEN sizeof(DBG_CDT_HDR)
|
|
+
|
|
+static ssize_t bl_dbgfs_dbg_cdt_read(struct file *file ,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *bl_hw = file->private_data;
|
|
+ char *buf;
|
|
+ int idx, res;
|
|
+ ssize_t read;
|
|
+ int i;
|
|
+ size_t bufsz = sizeof(bl_hw->dbg_credit)+DBG_CDT_HDR_LEN + 3000;
|
|
+
|
|
+ bufsz = min_t(size_t, bufsz, count);
|
|
+ buf = kmalloc(bufsz, GFP_ATOMIC);
|
|
+ if (buf == NULL)
|
|
+ return 0;
|
|
+
|
|
+ bufsz--;
|
|
+ idx = 0;
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, DBG_CDT_HDR);
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, "\n");
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+
|
|
+ for (i = 0; i < 50; i++) {
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, DBG_CDT_HDR_FMT,
|
|
+ bl_hw->dbg_credit[i].sdio_port,
|
|
+ bl_hw->dbg_credit[i].txq_credit,
|
|
+ bl_hw->dbg_credit[i].hwq_credit,
|
|
+ bl_hw->dbg_credit[i].credit,
|
|
+ bl_hw->dbg_credit[i].nb_ready,
|
|
+ bl_hw->dbg_credit[i].txq_idx);
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+ res = scnprintf(&buf[idx], bufsz, "\n");
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+ }
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, "\n");
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
|
|
+ kfree(buf);
|
|
+
|
|
+ return read;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+static ssize_t bl_dbgfs_dbg_cdt_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return count;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(dbg_cdt);
|
|
+
|
|
+static ssize_t bl_dbgfs_stats_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char *buf;
|
|
+ int ret;
|
|
+ int i, skipped;
|
|
+ ssize_t read;
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ int per;
|
|
+#endif
|
|
+
|
|
+ int bufsz = (NX_TXQ_CNT) * 20 + (ARRAY_SIZE(priv->stats.amsdus_rx) + 1) * 40
|
|
+ + (ARRAY_SIZE(priv->stats.ampdus_tx) * 30);
|
|
+
|
|
+ if (*ppos)
|
|
+ return 0;
|
|
+
|
|
+ buf = kmalloc(bufsz, GFP_ATOMIC);
|
|
+ if (buf == NULL)
|
|
+ return 0;
|
|
+
|
|
+ ret = scnprintf(buf, bufsz, "TXQs CFM balances ");
|
|
+ for (i = 0; i < NX_TXQ_CNT; i++)
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ " [%1d]:%3d", i,
|
|
+ priv->stats.cfm_balance[i]);
|
|
+
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret, "\n");
|
|
+
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ "\nAMSDU[len] done failed received\n");
|
|
+ for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
|
|
+ if (priv->stats.amsdus[i].done) {
|
|
+ per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
|
|
+ 100, priv->stats.amsdus[i].done);
|
|
+ } else if (priv->stats.amsdus_rx[i]) {
|
|
+ per = 0;
|
|
+ } else {
|
|
+ per = 0;
|
|
+ skipped = 1;
|
|
+ continue;
|
|
+ }
|
|
+ if (skipped) {
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret, " ...\n");
|
|
+ skipped = 0;
|
|
+ }
|
|
+
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ " [%2d] %10d %8d(%3d%%) %10d\n", i ? i + 1 : i,
|
|
+ priv->stats.amsdus[i].done,
|
|
+ priv->stats.amsdus[i].failed, per,
|
|
+ priv->stats.amsdus_rx[i]);
|
|
+ }
|
|
+
|
|
+ for (; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
|
|
+ if (!priv->stats.amsdus_rx[i]) {
|
|
+ skipped = 1;
|
|
+ continue;
|
|
+ }
|
|
+ if (skipped) {
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret, " ...\n");
|
|
+ skipped = 0;
|
|
+ }
|
|
+
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ " [%2d] %10d\n",
|
|
+ i + 1, priv->stats.amsdus_rx[i]);
|
|
+ }
|
|
+#else
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ "\nAMSDU[len] received\n");
|
|
+ for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
|
|
+ if (!priv->stats.amsdus_rx[i]) {
|
|
+ skipped = 1;
|
|
+ continue;
|
|
+ }
|
|
+ if (skipped) {
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ " ...\n");
|
|
+ skipped = 0;
|
|
+ }
|
|
+
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ " [%2d] %10d\n",
|
|
+ i + 1, priv->stats.amsdus_rx[i]);
|
|
+ }
|
|
+
|
|
+#endif /* CONFIG_BL_SPLIT_TX_BUF */
|
|
+
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ "\nAMPDU[len] done received\n");
|
|
+ for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.ampdus_tx); i++) {
|
|
+ if (!priv->stats.ampdus_tx[i] && !priv->stats.ampdus_rx[i]) {
|
|
+ skipped = 1;
|
|
+ continue;
|
|
+ }
|
|
+ if (skipped) {
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ " ...\n");
|
|
+ skipped = 0;
|
|
+ }
|
|
+
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ " [%2d] %9d %9d\n", i ? i + 1 : i,
|
|
+ priv->stats.ampdus_tx[i], priv->stats.ampdus_rx[i]);
|
|
+ }
|
|
+
|
|
+ ret += scnprintf(&buf[ret], bufsz - ret,
|
|
+ "#mpdu missed %9d\n",
|
|
+ priv->stats.ampdus_rx_miss);
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
|
|
+
|
|
+ kfree(buf);
|
|
+
|
|
+ return read;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_stats_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+
|
|
+ /* Prevent from interrupt preemption as these statistics are updated under
|
|
+ * interrupt */
|
|
+ spin_lock_bh(&priv->tx_lock);
|
|
+
|
|
+ memset(&priv->stats, 0, sizeof(priv->stats));
|
|
+
|
|
+ spin_unlock_bh(&priv->tx_lock);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(stats);
|
|
+
|
|
+#define TXQ_STA_PREF "tid|"
|
|
+#define TXQ_STA_PREF_FMT "%3d|"
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+#define TXQ_VIF_PREF "type|"
|
|
+#define TXQ_VIF_PREF_FMT "%4s|"
|
|
+#else
|
|
+#define TXQ_VIF_PREF "AC|"
|
|
+#define TXQ_VIF_PREF_FMT "%2s|"
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+#define TXQ_HDR "idx| status|credit|ready|retry"
|
|
+#define TXQ_HDR_FMT "%3d|%s%s%s%s%s%s%s|%6d|%5d|%5d"
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+#define TXQ_HDR_SUFF "|amsdu"
|
|
+#define TXQ_HDR_SUFF_FMT "|%5d"
|
|
+#else
|
|
+#define TXQ_HDR_SUFF "|amsdu-ht|amdsu-vht"
|
|
+#define TXQ_HDR_SUFF_FMT "|%8d|%9d"
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+#else
|
|
+#define TXQ_HDR_SUFF ""
|
|
+#define TXQ_HDR_SUF_FMT ""
|
|
+#endif /* CONFIG_BL_AMSDUS_TX */
|
|
+
|
|
+#define TXQ_HDR_MAX_LEN (sizeof(TXQ_STA_PREF) + sizeof(TXQ_HDR) + sizeof(TXQ_HDR_SUFF) + 1)
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+#define PS_HDR "Legacy PS: ready=%d, sp=%d / UAPSD: ready=%d, sp=%d"
|
|
+#define PS_HDR_LEGACY "Legacy PS: ready=%d, sp=%d"
|
|
+#define PS_HDR_UAPSD "UAPSD: ready=%d, sp=%d"
|
|
+#define PS_HDR_MAX_LEN sizeof("Legacy PS: ready=xxx, sp=xxx / UAPSD: ready=xxx, sp=xxx\n")
|
|
+#else
|
|
+#define PS_HDR ""
|
|
+#define PS_HDR_MAX_LEN 0
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+#define STA_HDR "** STA %d (%pM)\n"
|
|
+#define STA_HDR_MAX_LEN sizeof("- STA xx (xx:xx:xx:xx:xx:xx)\n") + PS_HDR_MAX_LEN
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+#define VIF_HDR "* VIF [%d] %s\n"
|
|
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR) + IFNAMSIZ
|
|
+#else
|
|
+#define VIF_HDR "* VIF [%d]\n"
|
|
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR)
|
|
+#endif
|
|
+
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+#define VIF_SEP "---------------------------------------\n"
|
|
+#else
|
|
+#define VIF_SEP "----------------------------------------------------\n"
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+#else /* ! CONFIG_BL_AMSDUS_TX */
|
|
+#define VIF_SEP "---------------------------------\n"
|
|
+#endif /* CONFIG_BL_AMSDUS_TX*/
|
|
+
|
|
+#define VIF_SEP_LEN sizeof(VIF_SEP)
|
|
+
|
|
+#define CAPTION "status: L=in hwq list, F=stop full, P=stop sta PS, V=stop vif PS, C=stop channel, S=stop CSA, M=stop MU"
|
|
+#define CAPTION_LEN sizeof(CAPTION)
|
|
+
|
|
+#define STA_TXQ 0
|
|
+#define VIF_TXQ 1
|
|
+
|
|
+static int bl_dbgfs_txq(char *buf, size_t size, struct bl_txq *txq, int type, int tid, char *name)
|
|
+{
|
|
+ int res, idx = 0;
|
|
+
|
|
+ if (type == STA_TXQ) {
|
|
+ res = scnprintf(&buf[idx], size, TXQ_STA_PREF_FMT, tid);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ } else {
|
|
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF_FMT, name);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+
|
|
+ res = scnprintf(&buf[idx], size, TXQ_HDR_FMT, txq->idx,
|
|
+ (txq->status & BL_TXQ_IN_HWQ_LIST) ? "L" : " ",
|
|
+ (txq->status & BL_TXQ_STOP_FULL) ? "F" : " ",
|
|
+ (txq->status & BL_TXQ_STOP_STA_PS) ? "P" : " ",
|
|
+ (txq->status & BL_TXQ_STOP_VIF_PS) ? "V" : " ",
|
|
+ (txq->status & BL_TXQ_STOP_CHAN) ? "C" : " ",
|
|
+ (txq->status & BL_TXQ_STOP_CSA) ? "S" : " ",
|
|
+ (txq->status & BL_TXQ_STOP_MU_POS) ? "M" : " ",
|
|
+ txq->credits, skb_queue_len(&txq->sk_list),
|
|
+ txq->nb_retry);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ if (type == STA_TXQ) {
|
|
+ res = scnprintf(&buf[idx], size, TXQ_HDR_SUFF_FMT,
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ txq->amsdu_len
|
|
+#else
|
|
+ txq->amsdu_ht_len_cap, txq->amsdu_vht_len_cap
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+ );
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ res = scnprintf(&buf[idx], size, "\n");
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+
|
|
+ return idx;
|
|
+}
|
|
+
|
|
+static int bl_dbgfs_txq_sta(char *buf, size_t size, struct bl_sta *bl_sta,
|
|
+ struct bl_hw *bl_hw)
|
|
+{
|
|
+ int tid, res, idx = 0;
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ res = scnprintf(&buf[idx], size, "\n" STA_HDR,
|
|
+ bl_sta->sta_idx,
|
|
+ bl_sta->mac_addr
|
|
+ );
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ if (bl_sta->ps.active) {
|
|
+ if (bl_sta->uapsd_tids &&
|
|
+ (bl_sta->uapsd_tids == ((1 << NX_NB_TXQ_PER_STA) - 1)))
|
|
+ res = scnprintf(&buf[idx], size, PS_HDR_UAPSD "\n",
|
|
+ bl_sta->ps.pkt_ready[UAPSD_ID],
|
|
+ bl_sta->ps.sp_cnt[UAPSD_ID]);
|
|
+ else if (bl_sta->uapsd_tids)
|
|
+ res = scnprintf(&buf[idx], size, PS_HDR "\n",
|
|
+ bl_sta->ps.pkt_ready[LEGACY_PS_ID],
|
|
+ bl_sta->ps.sp_cnt[LEGACY_PS_ID],
|
|
+ bl_sta->ps.pkt_ready[UAPSD_ID],
|
|
+ bl_sta->ps.sp_cnt[UAPSD_ID]);
|
|
+ else
|
|
+ res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
|
|
+ bl_sta->ps.pkt_ready[LEGACY_PS_ID],
|
|
+ bl_sta->ps.sp_cnt[LEGACY_PS_ID]);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ } else {
|
|
+ res = scnprintf(&buf[idx], size, "\n");
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+
|
|
+ res = scnprintf(&buf[idx], size, TXQ_STA_PREF TXQ_HDR TXQ_HDR_SUFF "\n");
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ txq = bl_txq_sta_get(bl_sta, 0, NULL, bl_hw);
|
|
+#else
|
|
+ txq = bl_txq_sta_get(bl_sta, 0, NULL);
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+ for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++, txq++) {
|
|
+ res = bl_dbgfs_txq(&buf[idx], size, txq, STA_TXQ, tid, NULL);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+
|
|
+ return idx;
|
|
+}
|
|
+
|
|
+static int bl_dbgfs_txq_vif(char *buf, size_t size, struct bl_vif *bl_vif,
|
|
+ struct bl_hw *bl_hw)
|
|
+{
|
|
+ int res, idx = 0;
|
|
+ struct bl_txq *txq;
|
|
+ struct bl_sta *bl_sta;
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ res = scnprintf(&buf[idx], size, VIF_HDR, bl_vif->vif_index, bl_vif->ndev->name);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ if (!bl_vif->up || bl_vif->ndev == NULL)
|
|
+ return idx;
|
|
+
|
|
+#else
|
|
+ int i;
|
|
+ char ac_name[2] = {'0', '\0'};
|
|
+
|
|
+ res = scnprintf(&buf[idx], size, VIF_HDR, bl_vif->vif_index);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+ #ifdef CONFIG_BL_FULLMAC
|
|
+ if (BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_AP ||
|
|
+ BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_P2P_GO) {
|
|
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_UNK_TXQ_TYPE, NULL);
|
|
+ res = bl_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "UNK");
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_BCMC_TXQ_TYPE, NULL);
|
|
+ res = bl_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "BCMC");
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ bl_sta = &bl_hw->sta_table[bl_vif->ap.bcmc_index];
|
|
+ if (bl_sta->ps.active) {
|
|
+ res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
|
|
+ bl_sta->ps.sp_cnt[LEGACY_PS_ID],
|
|
+ bl_sta->ps.sp_cnt[LEGACY_PS_ID]);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ } else {
|
|
+ res = scnprintf(&buf[idx], size, "\n");
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(bl_sta, &bl_vif->ap.sta_list, list) {
|
|
+ res = bl_dbgfs_txq_sta(&buf[idx], size, bl_sta, bl_hw);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+ } else if (BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_STATION ||
|
|
+ BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_P2P_CLIENT) {
|
|
+ if (bl_vif->sta.ap) {
|
|
+ res = bl_dbgfs_txq_sta(&buf[idx], size, bl_vif->sta.ap, bl_hw);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ #else
|
|
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ txq = bl_txq_vif_get(bl_vif, 0, NULL);
|
|
+ for (i = 0; i < NX_NB_TXQ_PER_VIF; i++, txq++) {
|
|
+ ac_name[0]++;
|
|
+ res = bl_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, ac_name);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(bl_sta, &bl_vif->stations, list) {
|
|
+ res = bl_dbgfs_txq_sta(&buf[idx], size, bl_sta, bl_hw);
|
|
+ idx += res;
|
|
+ size -= res;
|
|
+ }
|
|
+ #endif /* CONFIG_BL_FULLMAC */
|
|
+ return idx;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_txq_read(struct file *file ,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *bl_hw = file->private_data;
|
|
+ struct bl_vif *vif;
|
|
+ char *buf;
|
|
+ int idx, res;
|
|
+ ssize_t read;
|
|
+ size_t bufsz = ((NX_VIRT_DEV_MAX * (VIF_HDR_MAX_LEN + 2 * VIF_SEP_LEN)) +
|
|
+ (NX_REMOTE_STA_MAX * STA_HDR_MAX_LEN) +
|
|
+ ((NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX + NX_NB_TXQ) *
|
|
+ TXQ_HDR_MAX_LEN) + CAPTION_LEN);
|
|
+
|
|
+ /* everything is read in one go */
|
|
+ if (*ppos)
|
|
+ return 0;
|
|
+
|
|
+ bufsz = min_t(size_t, bufsz, count);
|
|
+ buf = kmalloc(bufsz, GFP_ATOMIC);
|
|
+ if (buf == NULL)
|
|
+ return 0;
|
|
+
|
|
+ bufsz--;
|
|
+ idx = 0;
|
|
+
|
|
+ res = scnprintf(&buf[idx], bufsz, CAPTION);
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+
|
|
+ //spin_lock_bh(&bl_hw->tx_lock);
|
|
+ list_for_each_entry(vif, &bl_hw->vifs, list) {
|
|
+ res = scnprintf(&buf[idx], bufsz, "\n"VIF_SEP);
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+ res = bl_dbgfs_txq_vif(&buf[idx], bufsz, vif, bl_hw);
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+ res = scnprintf(&buf[idx], bufsz, VIF_SEP);
|
|
+ idx += res;
|
|
+ bufsz -= res;
|
|
+ }
|
|
+ //spin_unlock_bh(&bl_hw->tx_lock);
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
|
|
+ kfree(buf);
|
|
+
|
|
+ return read;
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(txq);
|
|
+
|
|
+static ssize_t bl_dbgfs_rhd_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ priv->dbginfo.buf->rhd_mem,
|
|
+ priv->dbginfo.buf->dbg_info.rhd_len);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(rhd);
|
|
+
|
|
+static ssize_t bl_dbgfs_rbd_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ priv->dbginfo.buf->rbd_mem,
|
|
+ priv->dbginfo.buf->dbg_info.rbd_len);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(rbd);
|
|
+
|
|
+static ssize_t bl_dbgfs_thdx_read(struct file *file, char __user *user_buf,
|
|
+ size_t count, loff_t *ppos, int idx)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ &priv->dbginfo.buf->thd_mem[idx],
|
|
+ priv->dbginfo.buf->dbg_info.thd_len[idx]);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_thd0_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return bl_dbgfs_thdx_read(file, user_buf, count, ppos, 0);
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(thd0);
|
|
+
|
|
+static ssize_t bl_dbgfs_thd1_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return bl_dbgfs_thdx_read(file, user_buf, count, ppos, 1);
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(thd1);
|
|
+
|
|
+static ssize_t bl_dbgfs_thd2_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return bl_dbgfs_thdx_read(file, user_buf, count, ppos, 2);
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(thd2);
|
|
+
|
|
+static ssize_t bl_dbgfs_thd3_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return bl_dbgfs_thdx_read(file, user_buf, count, ppos, 3);
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(thd3);
|
|
+
|
|
+#if (NX_TXQ_CNT == 5)
|
|
+static ssize_t bl_dbgfs_thd4_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return bl_dbgfs_thdx_read(file, user_buf, count, ppos, 4);
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(thd4);
|
|
+#endif
|
|
+
|
|
+static ssize_t bl_dbgfs_mactrace_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ priv->dbginfo.buf->la_mem,
|
|
+ priv->dbginfo.buf->dbg_info.la_conf.trace_len);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(mactrace);
|
|
+
|
|
+static ssize_t bl_dbgfs_mactctrig_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+
|
|
+ priv->debugfs.trace_prst = false;
|
|
+
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ char msg[64];
|
|
+
|
|
+ printk("sent trace trigger msg\n");
|
|
+
|
|
+ scnprintf(msg, sizeof(msg), "Force trigger\n");
|
|
+ bl_send_dbg_trigger_req(priv, msg);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(mactctrig);
|
|
+
|
|
+
|
|
+static ssize_t bl_dbgfs_macdiags_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ priv->dbginfo.buf->dbg_info.diags_mac,
|
|
+ DBG_DIAGS_MAC_MAX * 2);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(macdiags);
|
|
+
|
|
+static ssize_t bl_dbgfs_phydiags_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ priv->dbginfo.buf->dbg_info.diags_phy,
|
|
+ DBG_DIAGS_PHY_MAX * 2);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(phydiags);
|
|
+
|
|
+static ssize_t bl_dbgfs_hwdiags_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[16];
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
|
|
+ "%08X\n", priv->dbginfo.buf->dbg_info.hw_diag);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(hwdiags);
|
|
+
|
|
+static ssize_t bl_dbgfs_plfdiags_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[16];
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
|
|
+ "%08X\n", priv->dbginfo.buf->dbg_info.la_conf.diag_conf);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(plfdiags);
|
|
+
|
|
+static ssize_t bl_dbgfs_swdiags_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ &priv->dbginfo.buf->dbg_info.sw_diag,
|
|
+ priv->dbginfo.buf->dbg_info.sw_diag_len);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(swdiags);
|
|
+
|
|
+static ssize_t bl_dbgfs_error_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ priv->dbginfo.buf->dbg_info.error,
|
|
+ strlen((char *)priv->dbginfo.buf->dbg_info.error));
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(error);
|
|
+
|
|
+static ssize_t bl_dbgfs_rxdesc_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[32];
|
|
+ int ret;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
|
|
+ "%08X\n%08X\n", priv->dbginfo.buf->dbg_info.rhd,
|
|
+ priv->dbginfo.buf->dbg_info.rbd);
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(rxdesc);
|
|
+
|
|
+static ssize_t bl_dbgfs_txdesc_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[64];
|
|
+ int len = 0;
|
|
+ int i;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < NX_TXQ_CNT; i++) {
|
|
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
|
|
+ "%08X\n", priv->dbginfo.buf->dbg_info.thd[i]);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(txdesc);
|
|
+
|
|
+static ssize_t bl_dbgfs_macrxptr_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ &priv->dbginfo.buf->dbg_info.rhd_hw_ptr,
|
|
+ 2 * sizeof(priv->dbginfo.buf->dbg_info.rhd_hw_ptr));
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(macrxptr);
|
|
+
|
|
+static ssize_t bl_dbgfs_lamacconf_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ ssize_t read;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos,
|
|
+ priv->dbginfo.buf->dbg_info.la_conf.conf,
|
|
+ LA_CONF_LEN * 4);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return read;
|
|
+}
|
|
+DEBUGFS_READ_FILE_OPS(lamacconf);
|
|
+
|
|
+static ssize_t bl_dbgfs_chaninfo_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[4 * 32];
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+ if (!priv->debugfs.trace_prst) {
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
|
|
+ "type: %d\n"
|
|
+ "prim20_freq: %d MHz\n"
|
|
+ "center1_freq: %d MHz\n"
|
|
+ "center2_freq: %d MHz\n",
|
|
+ (priv->dbginfo.buf->dbg_info.chan_info.info1 >> 8) & 0xFF,
|
|
+ (priv->dbginfo.buf->dbg_info.chan_info.info1 >> 16) & 0xFFFF,
|
|
+ (priv->dbginfo.buf->dbg_info.chan_info.info2 >> 0) & 0xFFFF,
|
|
+ (priv->dbginfo.buf->dbg_info.chan_info.info2 >> 16) & 0xFFFF);
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(chaninfo);
|
|
+
|
|
+static ssize_t bl_dbgfs_acsinfo_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ struct wiphy *wiphy = priv->wiphy;
|
|
+ int survey_cnt = 0;
|
|
+ int len = 0;
|
|
+ int band, chan_cnt;
|
|
+ char *buf = NULL;
|
|
+ int buf_len = (SCAN_CHANNEL_MAX + 1) * 43;
|
|
+ size_t ret = 0;
|
|
+
|
|
+ buf = kzalloc(buf_len, GFP_KERNEL);
|
|
+ if(!buf)
|
|
+ return ret;
|
|
+
|
|
+ mutex_lock(&priv->dbginfo.mutex);
|
|
+
|
|
+ len += scnprintf(buf, min_t(size_t, buf_len - 1, count),
|
|
+ "FREQ TIME(ms) BUSY(ms) NOISE(dBm)\n");
|
|
+
|
|
+ for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_5GHZ; band++) {
|
|
+ for (chan_cnt = 0; chan_cnt < wiphy->bands[band]->n_channels; chan_cnt++) {
|
|
+ struct bl_survey_info *p_survey_info = &priv->survey[survey_cnt];
|
|
+ struct ieee80211_channel *p_chan = &wiphy->bands[band]->channels[chan_cnt];
|
|
+
|
|
+ if (p_survey_info->filled) {
|
|
+ len += scnprintf(buf + len, min_t(size_t, buf_len - len - 1, count),
|
|
+ "%d %03d %03d %d\n",
|
|
+ p_chan->center_freq,
|
|
+ p_survey_info->chan_time_ms,
|
|
+ p_survey_info->chan_time_busy_ms,
|
|
+ p_survey_info->noise_dbm);
|
|
+ } else {
|
|
+ len += scnprintf(buf + len, min_t(size_t, buf_len -len -1, count),
|
|
+ "%d NOT AVAILABLE\n",
|
|
+ p_chan->center_freq);
|
|
+ }
|
|
+
|
|
+ survey_cnt++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&priv->dbginfo.mutex);
|
|
+
|
|
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+
|
|
+ kfree(buf);
|
|
+ buf = NULL;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(acsinfo);
|
|
+
|
|
+static ssize_t bl_dbgfs_fw_dbg_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ char help[]="usage: [MOD:<ALL|KE|DBG|IPC|DMA|MM|TX|RX|PHY>]* "
|
|
+ "[DBG:<NONE|CRT|ERR|WRN|INF|VRB>]\n";
|
|
+
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, help, sizeof(help));
|
|
+}
|
|
+
|
|
+
|
|
+static ssize_t bl_dbgfs_fw_dbg_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg);
|
|
+
|
|
+static ssize_t bl_dbgfs_sys_stats_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(sys_stats);
|
|
+
|
|
+static ssize_t bl_dbgfs_um_helper_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[sizeof(priv->debugfs.helper_cmd)];
|
|
+ int ret;
|
|
+
|
|
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
|
|
+ "%s", priv->debugfs.helper_cmd);
|
|
+
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_um_helper_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ int eobuf = min_t(size_t, sizeof(priv->debugfs.helper_cmd) - 1, count);
|
|
+
|
|
+ priv->debugfs.helper_cmd[eobuf] = '\0';
|
|
+ if (copy_from_user(priv->debugfs.helper_cmd, user_buf, eobuf))
|
|
+ return -EFAULT;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(um_helper);
|
|
+
|
|
+
|
|
+static ssize_t bl_dbgfs_sdio_test_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ u8 *buf = NULL;
|
|
+ int pkt_len;
|
|
+ int ret = 0;
|
|
+ u32 port;
|
|
+ ssize_t read;
|
|
+ int i = 0;
|
|
+
|
|
+ bl_get_rd_port(priv, &port);
|
|
+ bl_get_rd_len(priv, 0x8, 0x9, &pkt_len);
|
|
+ buf = kzalloc(pkt_len, GFP_KERNEL);
|
|
+ bl_read_data_sync(priv, buf, pkt_len, priv->plat->io_port + port);
|
|
+
|
|
+ for(i=0; i<pkt_len; i++)
|
|
+ {
|
|
+ if(i%16 == 0)
|
|
+ printk("\n");
|
|
+ printk("0x%x ",buf[i]);
|
|
+ }
|
|
+ printk("\n");
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
|
|
+
|
|
+ kfree(buf);
|
|
+ buf = NULL;
|
|
+ return read;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_sdio_test_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[32];
|
|
+ u8 *send_buf = NULL;
|
|
+ int i;
|
|
+ u32 port;
|
|
+ int val;
|
|
+ int pkt_len;
|
|
+ int act_len;
|
|
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
|
|
+
|
|
+ if (copy_from_user(buf, user_buf, len))
|
|
+ return -EFAULT;
|
|
+
|
|
+ buf[len] = '\0';
|
|
+
|
|
+ if (sscanf(buf, "0x%x-%d", &val, &pkt_len) > 0)
|
|
+ {
|
|
+ printk("val=0x%x, pkt_len=0x%x\n", val, pkt_len);
|
|
+ }
|
|
+
|
|
+ act_len = pkt_len;
|
|
+
|
|
+ pkt_len = ((pkt_len + BL_SDIO_BLOCK_SIZE -1)/BL_SDIO_BLOCK_SIZE) * BL_SDIO_BLOCK_SIZE;
|
|
+
|
|
+ printk("after adjust: alloc %d for pkt_buf\n", pkt_len);
|
|
+
|
|
+ send_buf = kzalloc(pkt_len, GFP_KERNEL);
|
|
+
|
|
+ for(i = 0; i < pkt_len; i++)
|
|
+ {
|
|
+ send_buf[i] = val;
|
|
+ }
|
|
+
|
|
+ bl_get_wr_port(priv, &port);
|
|
+
|
|
+ bl_write_data_sync(priv, send_buf, act_len, priv->plat->io_port + port);
|
|
+
|
|
+ kfree(send_buf);
|
|
+
|
|
+ send_buf = NULL;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(sdio_test);
|
|
+
|
|
+static ssize_t bl_dbgfs_rw_reg_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *bl_hw = file->private_data;
|
|
+ struct bl_device *bl_device;
|
|
+ struct bl_plat *bl_plat;
|
|
+ u8 sdio_ireg;
|
|
+
|
|
+ bl_plat = bl_hw->plat;
|
|
+ bl_device = (struct bl_device *)bl_plat->priv;
|
|
+
|
|
+ if(bl_read_data_sync(bl_hw, bl_plat->mp_regs, bl_device->reg->max_mp_regs, REG_PORT | BL_SDIO_BYTE_MODE_MASK)) {
|
|
+ printk("read mp_regs failed\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ sdio_ireg = bl_plat->mp_regs[bl_device->reg->host_int_status_reg];
|
|
+
|
|
+ printk("sdio_ireg=0x%x\n", sdio_ireg);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_rw_reg_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[32];
|
|
+ int reg;
|
|
+ int wr_val;
|
|
+ u8 data;
|
|
+ int ret = -1;
|
|
+
|
|
+ size_t len = min_t(size_t, count, sizeof(buf)-1);
|
|
+
|
|
+ if (copy_from_user(buf, user_buf, len))
|
|
+ return -EFAULT;
|
|
+
|
|
+ buf[len] = '\0';
|
|
+
|
|
+ if (sscanf(buf, "0x%x-%d", ®, &wr_val) > 0)
|
|
+ {
|
|
+ printk("reg=0x%x, wr_val=0x%x\n", reg, wr_val);
|
|
+ }
|
|
+
|
|
+ ret = bl_read_reg(priv, reg, &data);
|
|
+
|
|
+ if(ret)
|
|
+ printk("11--read reg 0x%x failed\n", reg);
|
|
+ else
|
|
+ printk("11--read reg 0x%x success, val=0x%x\n", reg, data);
|
|
+
|
|
+
|
|
+ ret = bl_write_reg(priv, reg, wr_val);
|
|
+
|
|
+ if(ret)
|
|
+ printk("22--write 0x%x to reg 0x%x failed!\n", wr_val, reg);
|
|
+ else
|
|
+ printk("22--write 0x%x to reg 0x%x success!\n", wr_val, reg);
|
|
+
|
|
+ ret = bl_read_reg(priv, reg, &data);
|
|
+
|
|
+ if(ret)
|
|
+ printk("33--read reg 0x%x failed\n", reg);
|
|
+ else
|
|
+ printk("33--read reg 0x%x success, val=0x%x\n", reg, data);
|
|
+
|
|
+ return count;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(rw_reg);
|
|
+
|
|
+static ssize_t bl_dbgfs_rdbitmap_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ u8 rd_bitmap_l;
|
|
+ u8 rd_bitmap_u;
|
|
+ u32 bitmap;
|
|
+
|
|
+ bl_read_reg(priv, 0x04, &rd_bitmap_l);
|
|
+ bl_read_reg(priv, 0x05, &rd_bitmap_u);
|
|
+ bitmap = rd_bitmap_l;
|
|
+ bitmap |= rd_bitmap_u << 8;
|
|
+
|
|
+ printk("rd_bitmap=0x%08x\n", bitmap);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_rdbitmap_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(rdbitmap);
|
|
+
|
|
+static ssize_t bl_dbgfs_wrbitmap_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ u8 wr_bitmap_l;
|
|
+ u8 wr_bitmap_u;
|
|
+ u32 bitmap;
|
|
+
|
|
+ bl_read_reg(priv, 0x06, &wr_bitmap_l);
|
|
+ bl_read_reg(priv, 0x07, &wr_bitmap_u);
|
|
+ bitmap = wr_bitmap_l;
|
|
+ bitmap |= wr_bitmap_u << 8;
|
|
+
|
|
+ printk("wr_bitmap=0x%08x\n", bitmap);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_wrbitmap_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(wrbitmap);
|
|
+
|
|
+
|
|
+static ssize_t bl_dbgfs_run_fw_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[32];
|
|
+ int ret;
|
|
+ ssize_t read;
|
|
+ u8 data;
|
|
+
|
|
+ bl_read_reg(priv, 0x60, &data);
|
|
+
|
|
+ printk("data=0x%x\n", data);
|
|
+
|
|
+ ret = scnprintf(buf, 4, "0x%x", data);
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
|
|
+
|
|
+ return read;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_run_fw_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char buf[32];
|
|
+ int val;
|
|
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
|
|
+
|
|
+ if (copy_from_user(buf, user_buf, len))
|
|
+ return -EFAULT;
|
|
+
|
|
+ buf[len] = '\0';
|
|
+
|
|
+ if (sscanf(buf, "%d", &val) > 0)
|
|
+ {
|
|
+ printk("val=0x%x\n", val);
|
|
+ bl_write_reg(priv, 0x60, val);
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(run_fw);
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+
|
|
+#define LINE_MAX_SZ 150
|
|
+
|
|
+struct st {
|
|
+ char line[LINE_MAX_SZ + 1];
|
|
+ unsigned int r_idx;
|
|
+};
|
|
+
|
|
+static int compare_idx(const void *st1, const void *st2)
|
|
+{
|
|
+ int index1 = ((struct st *)st1)->r_idx;
|
|
+ int index2 = ((struct st *)st2)->r_idx;
|
|
+
|
|
+ if (index1 > index2) return 1;
|
|
+ if (index1 < index2) return -1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int print_rate(char *buf, int size, int format, int nss, int mcs, int bw,
|
|
+ int sgi, int pre, int *r_idx)
|
|
+{
|
|
+ int res = 0;
|
|
+ int bitrates_cck[4] = { 10, 20, 55, 110 };
|
|
+ int bitrates_ofdm[8] = { 6, 9, 12, 18, 24, 36, 48, 54};
|
|
+
|
|
+ if (format <= FORMATMOD_NON_HT_DUP_OFDM) {
|
|
+ if (mcs < 4) {
|
|
+ if (r_idx) {
|
|
+ *r_idx = (mcs * 2) + pre;
|
|
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
|
|
+ }
|
|
+ res += scnprintf(&buf[res], size - res, "L-CCK/%cP %2u.%1uM ",
|
|
+ pre > 0 ? 'L' : 'S',
|
|
+ bitrates_cck[mcs] / 10,
|
|
+ bitrates_cck[mcs] % 10);
|
|
+ } else {
|
|
+ if (r_idx) {
|
|
+ *r_idx = N_CCK + (mcs - 4);
|
|
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
|
|
+ }
|
|
+ res += scnprintf(&buf[res], size - res, "L-OFDM %2u.0M ",
|
|
+ bitrates_ofdm[mcs]);
|
|
+ }
|
|
+ } else if (format <= FORMATMOD_HT_GF) {
|
|
+ if (r_idx) {
|
|
+ *r_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 + bw * 2 + sgi;
|
|
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
|
|
+ }
|
|
+ res += scnprintf(&buf[res], size - res, "HT%d/%cGI MCS%-2d ",
|
|
+ 20 * (1 << bw), sgi ? 'S' : 'L', nss * 8 + mcs);
|
|
+ } else {
|
|
+ if (r_idx) {
|
|
+ *r_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
|
|
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
|
|
+ }
|
|
+ res += scnprintf(&buf[res], size - res, "VHT%d/%cGI%*cMCS%d/%1d",
|
|
+ 20 * (1 << bw), sgi ? 'S' : 'L', bw > 2 ? 1 : 2, ' ',
|
|
+ mcs, nss + 1);
|
|
+
|
|
+ }
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static int print_rate_from_cfg(char *buf, int size, u32 rate_config, int *r_idx)
|
|
+{
|
|
+ union bl_rate_ctrl_info *r_cfg = (union bl_rate_ctrl_info *)&rate_config;
|
|
+ union bl_mcs_index *mcs_index = (union bl_mcs_index *)&rate_config;
|
|
+ unsigned int ft, pre, gi, bw, nss, mcs, len;
|
|
+
|
|
+ ft = r_cfg->formatModTx;
|
|
+ pre = r_cfg->preTypeTx;
|
|
+ if (ft == FORMATMOD_VHT) {
|
|
+ mcs = mcs_index->vht.mcs;
|
|
+ nss = mcs_index->vht.nss;
|
|
+ } else if (ft >= FORMATMOD_HT_MF) {
|
|
+ mcs = mcs_index->ht.mcs;
|
|
+ nss = mcs_index->ht.nss;
|
|
+ } else {
|
|
+ mcs = mcs_index->legacy;
|
|
+ nss = 0;
|
|
+ }
|
|
+ gi = r_cfg->shortGITx;
|
|
+ bw = r_cfg->bwTx;
|
|
+
|
|
+ len = print_rate(buf, size, ft, nss, mcs, bw, gi, pre, r_idx);
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static void idx_to_rate_cfg(int idx, union bl_rate_ctrl_info *r_cfg)
|
|
+{
|
|
+ r_cfg->value = 0;
|
|
+ if (idx < N_CCK)
|
|
+ {
|
|
+ r_cfg->formatModTx = FORMATMOD_NON_HT;
|
|
+ r_cfg->preTypeTx = idx & 1;
|
|
+ r_cfg->mcsIndexTx = idx / 2;
|
|
+ }
|
|
+ else if (idx < (N_CCK + N_OFDM))
|
|
+ {
|
|
+ r_cfg->formatModTx = FORMATMOD_NON_HT;
|
|
+ r_cfg->mcsIndexTx = idx - N_CCK + 4;
|
|
+ }
|
|
+ else if (idx < (N_CCK + N_OFDM + N_HT))
|
|
+ {
|
|
+ union bl_mcs_index *r = (union bl_mcs_index *)r_cfg;
|
|
+
|
|
+ idx -= (N_CCK + N_OFDM);
|
|
+ r_cfg->formatModTx = FORMATMOD_HT_MF;
|
|
+ r->ht.nss = idx / (8*2*2);
|
|
+ r->ht.mcs = (idx % (8*2*2)) / (2*2);
|
|
+ r_cfg->bwTx = ((idx % (8*2*2)) % (2*2)) / 2;
|
|
+ r_cfg->shortGITx = idx & 1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ union bl_mcs_index *r = (union bl_mcs_index *)r_cfg;
|
|
+
|
|
+ idx -= (N_CCK + N_OFDM + N_HT);
|
|
+ r_cfg->formatModTx = FORMATMOD_VHT;
|
|
+ r->vht.nss = idx / (10*4*2);
|
|
+ r->vht.mcs = (idx % (10*4*2)) / (4*2);
|
|
+ r_cfg->bwTx = ((idx % (10*4*2)) % (4*2)) / 2;
|
|
+ r_cfg->shortGITx = idx & 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_rc_stats_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ char *buf;
|
|
+ int bufsz, len = 0;
|
|
+ ssize_t read;
|
|
+ int i = 0;
|
|
+ int error = 0;
|
|
+ struct me_rc_stats_cfm me_rc_stats_cfm;
|
|
+ unsigned int no_samples;
|
|
+ struct st *st;
|
|
+ u8 mac[6];
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* everything should fit in one call */
|
|
+ if (*ppos)
|
|
+ return 0;
|
|
+
|
|
+ /* Get the station index from MAC address */
|
|
+ sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
|
|
+ if (mac == NULL)
|
|
+ return 0;
|
|
+ sta = bl_get_sta(priv, mac);
|
|
+ if (sta == NULL)
|
|
+ return 0;
|
|
+
|
|
+ /* Forward the information to the LMAC */
|
|
+ if ((error = bl_send_me_rc_stats(priv, sta->sta_idx, &me_rc_stats_cfm)))
|
|
+ return error;
|
|
+
|
|
+ no_samples = me_rc_stats_cfm.no_samples;
|
|
+ if (no_samples == 0)
|
|
+ return 0;
|
|
+
|
|
+ bufsz = no_samples * LINE_MAX_SZ + 500;
|
|
+
|
|
+ buf = kmalloc(bufsz + 1, GFP_ATOMIC);
|
|
+ if (buf == NULL)
|
|
+ return 0;
|
|
+
|
|
+ st = kmalloc(sizeof(struct st) * no_samples, GFP_ATOMIC);
|
|
+ if (st == NULL)
|
|
+ {
|
|
+ kfree(buf);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < no_samples; i++)
|
|
+ {
|
|
+ unsigned int tp, eprob;
|
|
+ len = print_rate_from_cfg(st[i].line, LINE_MAX_SZ,
|
|
+ me_rc_stats_cfm.rate_stats[i].rate_config,
|
|
+ &st[i].r_idx);
|
|
+
|
|
+ if (me_rc_stats_cfm.sw_retry_step != 0)
|
|
+ {
|
|
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
|
|
+ me_rc_stats_cfm.retry[me_rc_stats_cfm.sw_retry_step].idx == i ? '*' : ' ');
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " ");
|
|
+ }
|
|
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
|
|
+ me_rc_stats_cfm.retry[0].idx == i ? 'T' : ' ');
|
|
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
|
|
+ me_rc_stats_cfm.retry[1].idx == i ? 't' : ' ');
|
|
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c ",
|
|
+ me_rc_stats_cfm.retry[2].idx == i ? 'P' : ' ');
|
|
+
|
|
+ tp = me_rc_stats_cfm.tp[i] / 10;
|
|
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " %4u.%1u",
|
|
+ tp / 10, tp % 10);
|
|
+
|
|
+ eprob = ((me_rc_stats_cfm.rate_stats[i].probability * 1000) >> 16) + 1;
|
|
+ len += scnprintf(&st[i].line[len],LINE_MAX_SZ - len,
|
|
+ " %4u.%1u %6u(%6u) %6u %6u",
|
|
+ eprob / 10, eprob % 10,
|
|
+ me_rc_stats_cfm.rate_stats[i].success,
|
|
+ me_rc_stats_cfm.rate_stats[i].attempts,
|
|
+ me_rc_stats_cfm.rate_stats[i].sample_skipped,
|
|
+ me_rc_stats_cfm.rate_stats[i].n_retry & 0x1F);
|
|
+ }
|
|
+ len = scnprintf(buf, bufsz ,
|
|
+ "\nTX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
|
|
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
+
|
|
+ len += scnprintf(&buf[len], bufsz - len,
|
|
+ " # type rate tpt eprob ok( tot) skipped nRetry\n");
|
|
+
|
|
+ // add sorted statistics to the buffer
|
|
+ sort(st, no_samples, sizeof(st[0]), compare_idx, NULL);
|
|
+ for (i = 0; i < no_samples; i++)
|
|
+ {
|
|
+ len += scnprintf(&buf[len], bufsz - len, "%s\n", st[i].line);
|
|
+ }
|
|
+ len += scnprintf(&buf[len], bufsz - len, "\n MPDUs AMPDUs AvLen trialP");
|
|
+ len += scnprintf(&buf[len], bufsz - len, "\n%6u %6u %3d.%1d %6u\n",
|
|
+ me_rc_stats_cfm.ampdu_len,
|
|
+ me_rc_stats_cfm.ampdu_packets,
|
|
+ me_rc_stats_cfm.avg_ampdu_len >> 16,
|
|
+ ((me_rc_stats_cfm.avg_ampdu_len * 10) >> 16) % 10,
|
|
+ me_rc_stats_cfm.sample_wait);
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+
|
|
+ kfree(buf);
|
|
+ kfree(st);
|
|
+
|
|
+ return read;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_FILE_OPS(rc_stats);
|
|
+
|
|
+static ssize_t bl_dbgfs_rc_fixed_rate_idx_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ u8 mac[6];
|
|
+ char buf[10];
|
|
+ int fixed_rate_idx = -1;
|
|
+ union bl_rate_ctrl_info rate_config;
|
|
+ int error = 0;
|
|
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Get the station index from MAC address */
|
|
+ sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
|
|
+ if (mac == NULL)
|
|
+ return 0;
|
|
+ sta = bl_get_sta(priv, mac);
|
|
+ if (sta == NULL)
|
|
+ return 0;
|
|
+
|
|
+ /* Get the content of the file */
|
|
+ if (copy_from_user(buf, user_buf, len))
|
|
+ return -EFAULT;
|
|
+ buf[len] = '\0';
|
|
+ sscanf(buf, "%i\n", &fixed_rate_idx);
|
|
+
|
|
+ /* Convert rate index into rate configuration */
|
|
+ if ((fixed_rate_idx < 0) || (fixed_rate_idx >= (N_CCK + N_OFDM + N_HT + N_VHT)))
|
|
+ {
|
|
+ // disable fixed rate
|
|
+ rate_config.value = 0xFFFF;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ idx_to_rate_cfg(fixed_rate_idx, &rate_config);
|
|
+ }
|
|
+ // Forward the request to the LMAC
|
|
+ if ((error = bl_send_me_rc_set_rate(priv, sta->sta_idx,
|
|
+ (u16)rate_config.value)) != 0)
|
|
+ {
|
|
+ return error;
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+DEBUGFS_WRITE_FILE_OPS(rc_fixed_rate_idx);
|
|
+
|
|
+static ssize_t bl_dbgfs_last_rx_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ struct bl_rx_rate_stats *rate_stats;
|
|
+ char *buf;
|
|
+ int bufsz, i, len = 0;
|
|
+ ssize_t read;
|
|
+ unsigned int fmt, pre, bw, nss, mcs, sgi;
|
|
+ u8 mac[6];
|
|
+ struct hw_vect *last_rx;
|
|
+ char hist[] = "##################################################";
|
|
+ int hist_len = sizeof(hist) - 1;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* everything should fit in one call */
|
|
+ if (*ppos)
|
|
+ return 0;
|
|
+
|
|
+ /* Get the station index from MAC address */
|
|
+ sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
|
|
+ if (mac == NULL)
|
|
+ return 0;
|
|
+ sta = bl_get_sta(priv, mac);
|
|
+ if (sta == NULL)
|
|
+ return 0;
|
|
+
|
|
+ rate_stats = &sta->stats.rx_rate;
|
|
+ bufsz = (rate_stats->size * ( 30 * hist_len) + 200);
|
|
+ buf = kmalloc(bufsz + 1, GFP_ATOMIC);
|
|
+ if (buf == NULL)
|
|
+ return 0;
|
|
+
|
|
+ len += scnprintf(buf, bufsz,
|
|
+ "\nRX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
|
|
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
+
|
|
+ // Display Statistics
|
|
+ for (i = 0 ; i < rate_stats->size ; i++ )
|
|
+ {
|
|
+ if (rate_stats->table[i]) {
|
|
+ union bl_rate_ctrl_info rate_config;
|
|
+ int percent = (rate_stats->table[i] * 1000) / rate_stats->cpt;
|
|
+ int p;
|
|
+
|
|
+ idx_to_rate_cfg(i, &rate_config);
|
|
+ len += print_rate_from_cfg(&buf[len], bufsz - len,
|
|
+ rate_config.value, NULL);
|
|
+ p = (percent * hist_len) / 1000;
|
|
+ len += scnprintf(&buf[len], bufsz - len, ": %6d(%3d.%1d%%)%.*s\n",
|
|
+ rate_stats->table[i],
|
|
+ percent / 10, percent % 10, p, hist);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Display detailled info of the last received rate
|
|
+ last_rx = &sta->stats.last_rx;
|
|
+
|
|
+ len += scnprintf(&buf[len], bufsz - len,"\nLast received rate\n"
|
|
+ " type rate LDPC STBC BEAMFM rssi(dBm)\n");
|
|
+
|
|
+ fmt = last_rx->format_mod;
|
|
+ bw = last_rx->ch_bw;
|
|
+ pre = last_rx->pre_type;
|
|
+ sgi = last_rx->short_gi;
|
|
+ if (fmt == FORMATMOD_VHT) {
|
|
+ mcs = last_rx->mcs;
|
|
+ nss = last_rx->stbc ? last_rx->n_sts/2 : last_rx->n_sts;
|
|
+ } else if (fmt >= FORMATMOD_HT_MF) {
|
|
+ mcs = last_rx->mcs;
|
|
+ nss = last_rx->stbc ? last_rx->stbc : last_rx->n_sts;
|
|
+ } else {
|
|
+ BUG_ON((mcs = legrates_lut[last_rx->leg_rate]) == -1);
|
|
+ nss = 0;
|
|
+ }
|
|
+
|
|
+ len += print_rate(&buf[len], bufsz - len, fmt, nss, mcs, bw, sgi, pre, NULL);
|
|
+
|
|
+ /* flags for HT/VHT */
|
|
+ if (fmt == FORMATMOD_VHT) {
|
|
+ len += scnprintf(&buf[len], bufsz - len, " %c %c %c",
|
|
+ last_rx->fec_coding ? 'L' : ' ',
|
|
+ last_rx->stbc ? 'S' : ' ',
|
|
+ last_rx->smoothing ? ' ' : 'B');
|
|
+ } else if (fmt >= FORMATMOD_HT_MF) {
|
|
+ len += scnprintf(&buf[len], bufsz - len, " %c %c ",
|
|
+ last_rx->fec_coding ? 'L' : ' ',
|
|
+ last_rx->stbc ? 'S' : ' ');
|
|
+ } else {
|
|
+ len += scnprintf(&buf[len], bufsz - len, " ");
|
|
+ }
|
|
+ len += scnprintf(&buf[len], bufsz - len, " %d\n", last_rx->rssi1);
|
|
+
|
|
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+
|
|
+ kfree(buf);
|
|
+ return read;
|
|
+}
|
|
+
|
|
+static ssize_t bl_dbgfs_last_rx_write(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct bl_hw *priv = file->private_data;
|
|
+ u8 mac[6];
|
|
+
|
|
+ /* Get the station index from MAC address */
|
|
+ sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
|
|
+ if (mac == NULL)
|
|
+ return 0;
|
|
+ sta = bl_get_sta(priv, mac);
|
|
+ if (sta == NULL)
|
|
+ return 0;
|
|
+
|
|
+ /* Prevent from interrupt preemption as these statistics are updated under
|
|
+ * interrupt */
|
|
+ spin_lock_bh(&priv->tx_lock);
|
|
+ memset(sta->stats.rx_rate.table, 0,
|
|
+ sta->stats.rx_rate.size * sizeof(sta->stats.rx_rate.table[0]));
|
|
+ sta->stats.rx_rate.cpt = 0;
|
|
+ spin_unlock_bh(&priv->tx_lock);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+DEBUGFS_READ_WRITE_FILE_OPS(last_rx);
|
|
+
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+/*
|
|
+ * Calls a userspace pgm
|
|
+ */
|
|
+int bl_um_helper(struct bl_debugfs *bl_debugfs, const char *cmd)
|
|
+{
|
|
+ char *envp[] = { "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
|
|
+ char **argv;
|
|
+ int argc, ret;
|
|
+
|
|
+ if (!bl_debugfs->dir ||
|
|
+ !strlen((cmd = cmd ? cmd : bl_debugfs->helper_cmd)))
|
|
+ return 0;
|
|
+ argv = argv_split(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL, cmd, &argc);
|
|
+ if (!argc)
|
|
+ return PTR_ERR(argv);
|
|
+
|
|
+ if ((ret = call_usermodehelper(argv[0], argv, envp,
|
|
+ UMH_WAIT_PROC | UMH_KILLABLE)))
|
|
+ printk(KERN_CRIT "Failed to call %s (%s returned %d)\n",
|
|
+ argv[0], cmd, ret);
|
|
+ argv_free(argv);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int bl_trigger_um_helper(struct bl_debugfs *bl_debugfs)
|
|
+{
|
|
+ if (bl_debugfs->helper_scheduled == true) {
|
|
+ printk(KERN_CRIT "%s: Already scheduled\n", __func__);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&bl_debugfs->umh_lock);
|
|
+ if (bl_debugfs->unregistering) {
|
|
+ spin_unlock_bh(&bl_debugfs->umh_lock);
|
|
+ printk(KERN_CRIT "%s: unregistering\n", __func__);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+ bl_debugfs->helper_scheduled = true;
|
|
+ schedule_work(&bl_debugfs->helper_work);
|
|
+ spin_unlock_bh(&bl_debugfs->umh_lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+static void bl_rc_stat_work(struct work_struct *ws)
|
|
+{
|
|
+ struct bl_debugfs *bl_debugfs = container_of(ws, struct bl_debugfs,
|
|
+ rc_stat_work);
|
|
+ struct bl_hw *bl_hw = container_of(bl_debugfs, struct bl_hw,
|
|
+ debugfs);
|
|
+ struct bl_sta *sta;
|
|
+ uint8_t ridx, sta_idx;
|
|
+
|
|
+ ridx = bl_debugfs->rc_read;
|
|
+ sta_idx = bl_debugfs->rc_sta[ridx];
|
|
+ if (sta_idx > (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
|
|
+ WARN(1, "Invalid sta index %d", sta_idx);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ bl_debugfs->rc_sta[ridx] = 0xFF;
|
|
+ ridx = (ridx + 1) % ARRAY_SIZE(bl_debugfs->rc_sta);
|
|
+ bl_debugfs->rc_read = ridx;
|
|
+ sta = &bl_hw->sta_table[sta_idx];
|
|
+ if (!sta) {
|
|
+ WARN(1, "Invalid sta %d", sta_idx);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (bl_debugfs->dir_sta[sta_idx] == NULL) {
|
|
+ /* register the sta */
|
|
+ struct dentry *dir_rc = bl_debugfs->dir_rc;
|
|
+ struct dentry *dir_sta;
|
|
+ struct dentry *file;
|
|
+ char sta_name[18];
|
|
+ struct bl_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
|
|
+ int nb_rx_rate = N_CCK + N_OFDM;
|
|
+
|
|
+
|
|
+ if (sta->sta_idx >= NX_REMOTE_STA_MAX) {
|
|
+ scnprintf(sta_name, sizeof(sta_name), "bc_mc");
|
|
+ } else {
|
|
+ scnprintf(sta_name, sizeof(sta_name), "%pM", sta->mac_addr);
|
|
+ }
|
|
+
|
|
+ if (!(dir_sta = debugfs_create_dir(sta_name, dir_rc)))
|
|
+ goto error;
|
|
+
|
|
+ bl_debugfs->dir_sta[sta->sta_idx] = dir_sta;
|
|
+
|
|
+ file = debugfs_create_file("stats", S_IRUSR, dir_sta, bl_hw,
|
|
+ &bl_dbgfs_rc_stats_ops);
|
|
+ if (IS_ERR_OR_NULL(file))
|
|
+ goto error_after_dir;
|
|
+
|
|
+ file = debugfs_create_file("fixed_rate_idx", S_IWUSR , dir_sta, bl_hw,
|
|
+ &bl_dbgfs_rc_fixed_rate_idx_ops);
|
|
+ if (IS_ERR_OR_NULL(file))
|
|
+ goto error_after_dir;
|
|
+
|
|
+ file = debugfs_create_file("rx_rate", S_IRUSR | S_IWUSR, dir_sta, bl_hw,
|
|
+ &bl_dbgfs_last_rx_ops);
|
|
+ if (IS_ERR_OR_NULL(file))
|
|
+ goto error_after_dir;
|
|
+
|
|
+ if (bl_hw->mod_params->ht_on)
|
|
+ nb_rx_rate += N_HT;
|
|
+
|
|
+ if (bl_hw->mod_params->vht_on)
|
|
+ nb_rx_rate += N_VHT;
|
|
+
|
|
+ rate_stats->table = kzalloc(nb_rx_rate * sizeof(rate_stats->table[0]),
|
|
+ GFP_KERNEL);
|
|
+ if (!rate_stats->table)
|
|
+ goto error_after_dir;
|
|
+
|
|
+ rate_stats->size = nb_rx_rate;
|
|
+ rate_stats->cpt = 0;
|
|
+
|
|
+ } else {
|
|
+ /* unregister the sta */
|
|
+ if (sta->stats.rx_rate.table) {
|
|
+ kfree(sta->stats.rx_rate.table);
|
|
+ sta->stats.rx_rate.table = NULL;
|
|
+ }
|
|
+ sta->stats.rx_rate.size = 0;
|
|
+ sta->stats.rx_rate.cpt = 0;
|
|
+
|
|
+ debugfs_remove_recursive(bl_debugfs->dir_sta[sta_idx]);
|
|
+ bl_debugfs->dir_sta[sta->sta_idx] = NULL;
|
|
+ }
|
|
+
|
|
+ return;
|
|
+
|
|
+ error_after_dir:
|
|
+ debugfs_remove_recursive(bl_debugfs->dir_sta[sta_idx]);
|
|
+ bl_debugfs->dir_sta[sta->sta_idx] = NULL;
|
|
+ error:
|
|
+ dev_err(bl_hw->dev,
|
|
+ "Error while (un)registering debug entry for sta %d\n", sta_idx);
|
|
+}
|
|
+
|
|
+void _bl_dbgfs_rc_stat_write(struct bl_debugfs *bl_debugfs, uint8_t sta_idx)
|
|
+{
|
|
+ uint8_t widx = bl_debugfs->rc_write;
|
|
+ if (bl_debugfs->rc_sta[widx] != 0XFF) {
|
|
+ WARN(1, "Overlap in debugfs rc_sta table\n");
|
|
+ }
|
|
+
|
|
+ bl_debugfs->rc_sta[widx] = sta_idx;
|
|
+ widx = (widx + 1) % ARRAY_SIZE(bl_debugfs->rc_sta);
|
|
+ bl_debugfs->rc_write = widx;
|
|
+
|
|
+ schedule_work(&bl_debugfs->rc_stat_work);
|
|
+}
|
|
+
|
|
+void bl_dbgfs_register_rc_stat(struct bl_hw *bl_hw, struct bl_sta *sta)
|
|
+{
|
|
+ _bl_dbgfs_rc_stat_write(&bl_hw->debugfs, sta->sta_idx);
|
|
+}
|
|
+
|
|
+void bl_dbgfs_unregister_rc_stat(struct bl_hw *bl_hw, struct bl_sta *sta)
|
|
+{
|
|
+ _bl_dbgfs_rc_stat_write(&bl_hw->debugfs, sta->sta_idx);
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+int bl_dbgfs_register(struct bl_hw *bl_hw, const char *name)
|
|
+{
|
|
+ struct dentry *phyd = bl_hw->wiphy->debugfsdir;
|
|
+ struct dentry *dir_rc;
|
|
+ struct bl_debugfs *bl_debugfs = &bl_hw->debugfs;
|
|
+ struct dentry *dir_drv, *dir_diags;
|
|
+
|
|
+ if (!(dir_drv = debugfs_create_dir(name, phyd)))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ bl_debugfs->dir = dir_drv;
|
|
+ bl_debugfs->unregistering = false;
|
|
+
|
|
+ if (!(dir_diags = debugfs_create_dir("diags", dir_drv)))
|
|
+ goto err;
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ if (!(dir_rc = debugfs_create_dir("rc", dir_drv)))
|
|
+ goto err;
|
|
+ bl_debugfs->dir_rc = dir_rc;
|
|
+ INIT_WORK(&bl_debugfs->rc_stat_work, bl_rc_stat_work);
|
|
+ bl_debugfs->rc_write = bl_debugfs->rc_read = 0;
|
|
+ memset(bl_debugfs->rc_sta, 0xFF, sizeof(bl_debugfs->rc_sta));
|
|
+#endif
|
|
+
|
|
+ DEBUGFS_ADD_FILE(stats, dir_drv, S_IWUSR | S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(sys_stats, dir_drv, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(txq, dir_drv, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(acsinfo, dir_drv, S_IRUSR);
|
|
+
|
|
+ DEBUGFS_ADD_FILE(run_fw, dir_drv, S_IWUSR | S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(sdio_test, dir_drv, S_IWUSR | S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(rdbitmap, dir_drv, S_IWUSR | S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(wrbitmap, dir_drv, S_IWUSR | S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(rw_reg, dir_drv, S_IWUSR | S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(dbg_cdt, dir_drv, S_IWUSR | S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(dbg_time, dir_drv, S_IWUSR | S_IRUSR);
|
|
+
|
|
+ bl_debugfs->trace_prst = bl_debugfs->helper_scheduled = false;
|
|
+ spin_lock_init(&bl_debugfs->umh_lock);
|
|
+ DEBUGFS_ADD_FILE(rhd, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(rbd, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(thd0, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(thd1, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(thd2, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(thd3, dir_diags, S_IRUSR);
|
|
+#if (NX_TXQ_CNT == 5)
|
|
+ DEBUGFS_ADD_FILE(thd4, dir_diags, S_IRUSR);
|
|
+#endif
|
|
+ DEBUGFS_ADD_FILE(mactrace, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(mactctrig, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(macdiags, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(phydiags, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(plfdiags, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(hwdiags, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(swdiags, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(error, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(rxdesc, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(txdesc, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(macrxptr, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(lamacconf, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(chaninfo, dir_diags, S_IRUSR);
|
|
+ DEBUGFS_ADD_FILE(fw_dbg, dir_diags, S_IWUSR | S_IRUSR);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err:
|
|
+ bl_dbgfs_unregister(bl_hw);
|
|
+ return -ENOMEM;
|
|
+}
|
|
+
|
|
+void bl_dbgfs_unregister(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct bl_debugfs *bl_debugfs = &bl_hw->debugfs;
|
|
+
|
|
+ if (!bl_hw->debugfs.dir)
|
|
+ return;
|
|
+
|
|
+ bl_debugfs->unregistering = true;
|
|
+ flush_work(&bl_debugfs->helper_work);
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ flush_work(&bl_debugfs->rc_stat_work);
|
|
+#endif
|
|
+ debugfs_remove_recursive(bl_hw->debugfs.dir);
|
|
+ bl_hw->debugfs.dir = NULL;
|
|
+}
|
|
+
|
|
diff -Naur /dev/null_debugfs.h b/drivers/net/wireless/hflps170/bl_debugfs.h
|
|
--- /dev/null_debugfs.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_debugfs.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,78 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_debugfs.h
|
|
+ *
|
|
+ * @brief Miscellaneous utility function definitions
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+
|
|
+#ifndef _BL_DEBUGFS_H_
|
|
+#define _BL_DEBUGFS_H_
|
|
+
|
|
+#include <linux/workqueue.h>
|
|
+
|
|
+struct bl_hw;
|
|
+struct bl_sta;
|
|
+
|
|
+#ifdef CONFIG_BL_DEBUGFS
|
|
+
|
|
+struct bl_debugfs {
|
|
+ unsigned long long rateidx;
|
|
+ struct dentry *dir;
|
|
+ bool trace_prst;
|
|
+
|
|
+ char helper_cmd[64];
|
|
+ struct work_struct helper_work;
|
|
+ bool helper_scheduled;
|
|
+ spinlock_t umh_lock;
|
|
+
|
|
+ bool unregistering;
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ struct work_struct rc_stat_work;
|
|
+ uint8_t rc_sta[NX_REMOTE_STA_MAX];
|
|
+ uint8_t rc_write;
|
|
+ uint8_t rc_read;
|
|
+ struct dentry *dir_rc;
|
|
+ struct dentry *dir_sta[NX_REMOTE_STA_MAX];
|
|
+#endif
|
|
+};
|
|
+
|
|
+int bl_dbgfs_register(struct bl_hw *bl_hw, const char *name);
|
|
+void bl_dbgfs_unregister(struct bl_hw *bl_hw);
|
|
+int bl_um_helper(struct bl_debugfs *bl_debugfs, const char *cmd);
|
|
+int bl_trigger_um_helper(struct bl_debugfs *bl_debugfs);
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+void bl_dbgfs_register_rc_stat(struct bl_hw *bl_hw, struct bl_sta *sta);
|
|
+void bl_dbgfs_unregister_rc_stat(struct bl_hw *bl_hw, struct bl_sta *sta);
|
|
+#endif
|
|
+
|
|
+void bl_dump_trace(struct bl_hw *bl_hw);
|
|
+void bl_reset_trace(struct bl_hw *bl_hw);
|
|
+
|
|
+#else
|
|
+
|
|
+struct bl_debugfs {
|
|
+};
|
|
+
|
|
+static inline int bl_dbgfs_register(struct bl_hw *bl_hw, const char *name) { return 0; }
|
|
+static inline void bl_dbgfs_unregister(struct bl_hw *bl_hw) {}
|
|
+static inline int bl_um_helper(struct bl_debugfs *bl_debugfs, const char *cmd) { return 0; }
|
|
+static inline int bl_trigger_um_helper(struct bl_debugfs *bl_debugfs) {}
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+static inline void bl_dbgfs_register_rc_stat(struct bl_hw *bl_hw, struct bl_sta *sta) {}
|
|
+static inline void bl_dbgfs_unregister_rc_stat(struct bl_hw *bl_hw, struct bl_sta *sta) {}
|
|
+#endif
|
|
+
|
|
+void bl_dump_trace(struct bl_hw *bl_hw) {}
|
|
+void bl_reset_trace(struct bl_hw *bl_hw) {}
|
|
+
|
|
+#endif /* CONFIG_BL_DEBUGFS */
|
|
+
|
|
+
|
|
+#endif /* _BL_DEBUGFS_H_ */
|
|
diff -Naur /dev/null_events.h b/drivers/net/wireless/hflps170/bl_events.h
|
|
--- /dev/null_events.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_events.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,993 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_events.h
|
|
+ *
|
|
+ * @brief Trace events definition
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#undef TRACE_SYSTEM
|
|
+#define TRACE_SYSTEM bl
|
|
+
|
|
+#if !defined(_BL_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
|
|
+#define _BL_EVENTS_H
|
|
+
|
|
+#include <linux/tracepoint.h>
|
|
+
|
|
+#include "bl_compat.h"
|
|
+
|
|
+/*****************************************************************************
|
|
+ * TRACE function for MGMT TX (FULLMAC)
|
|
+ ****************************************************************************/
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+#include "linux/ieee80211.h"
|
|
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
|
|
+#include <linux/trace_seq.h>
|
|
+#include <linux/version.h>
|
|
+
|
|
+/* P2P Public Action Frames Definitions (see WiFi P2P Technical Specification, section 4.2.8) */
|
|
+/* IEEE 802.11 Public Action Usage Category - Define P2P public action frames */
|
|
+#define MGMT_ACTION_PUBLIC_CAT (0x04)
|
|
+/* Offset of OUI Subtype field in P2P Action Frame format */
|
|
+#define MGMT_ACTION_OUI_SUBTYPE_OFFSET (6)
|
|
+/* P2P Public Action Frame Types */
|
|
+enum p2p_action_type {
|
|
+ P2P_ACTION_GO_NEG_REQ = 0, /* GO Negociation Request */
|
|
+ P2P_ACTION_GO_NEG_RSP, /* GO Negociation Response */
|
|
+ P2P_ACTION_GO_NEG_CFM, /* GO Negociation Confirmation */
|
|
+ P2P_ACTION_INVIT_REQ, /* P2P Invitation Request */
|
|
+ P2P_ACTION_INVIT_RSP, /* P2P Invitation Response */
|
|
+ P2P_ACTION_DEV_DISC_REQ, /* Device Discoverability Request */
|
|
+ P2P_ACTION_DEV_DISC_RSP, /* Device Discoverability Response */
|
|
+ P2P_ACTION_PROV_DISC_REQ, /* Provision Discovery Request */
|
|
+ P2P_ACTION_PROV_DISC_RSP, /* Provision Discovery Response */
|
|
+};
|
|
+
|
|
+const char *ftrace_print_mgmt_info(struct trace_seq *p, u16 frame_control, u8 cat, u8 type, u8 p2p) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+ const char *ret = trace_seq_buffer_ptr(p);
|
|
+#else
|
|
+ const char *ret = p->buffer + p->len;
|
|
+#endif
|
|
+
|
|
+ switch (frame_control & IEEE80211_FCTL_STYPE) {
|
|
+ case (IEEE80211_STYPE_ASSOC_REQ): trace_seq_printf(p, "Association Request"); break;
|
|
+ case (IEEE80211_STYPE_ASSOC_RESP): trace_seq_printf(p, "Association Response"); break;
|
|
+ case (IEEE80211_STYPE_REASSOC_REQ): trace_seq_printf(p, "Reassociation Request"); break;
|
|
+ case (IEEE80211_STYPE_REASSOC_RESP): trace_seq_printf(p, "Reassociation Response"); break;
|
|
+ case (IEEE80211_STYPE_PROBE_REQ): trace_seq_printf(p, "Probe Request"); break;
|
|
+ case (IEEE80211_STYPE_PROBE_RESP): trace_seq_printf(p, "Probe Response"); break;
|
|
+ case (IEEE80211_STYPE_BEACON): trace_seq_printf(p, "Beacon"); break;
|
|
+ case (IEEE80211_STYPE_ATIM): trace_seq_printf(p, "ATIM"); break;
|
|
+ case (IEEE80211_STYPE_DISASSOC): trace_seq_printf(p, "Disassociation"); break;
|
|
+ case (IEEE80211_STYPE_AUTH): trace_seq_printf(p, "Authentication"); break;
|
|
+ case (IEEE80211_STYPE_DEAUTH): trace_seq_printf(p, "Deauthentication"); break;
|
|
+ case (IEEE80211_STYPE_ACTION):
|
|
+ trace_seq_printf(p, "Action");
|
|
+ if (cat == MGMT_ACTION_PUBLIC_CAT && type == 0x9)
|
|
+ switch (p2p) {
|
|
+ case (P2P_ACTION_GO_NEG_REQ): trace_seq_printf(p, ": GO Negociation Request"); break;
|
|
+ case (P2P_ACTION_GO_NEG_RSP): trace_seq_printf(p, ": GO Negociation Response"); break;
|
|
+ case (P2P_ACTION_GO_NEG_CFM): trace_seq_printf(p, ": GO Negociation Confirmation"); break;
|
|
+ case (P2P_ACTION_INVIT_REQ): trace_seq_printf(p, ": P2P Invitation Request"); break;
|
|
+ case (P2P_ACTION_INVIT_RSP): trace_seq_printf(p, ": P2P Invitation Response"); break;
|
|
+ case (P2P_ACTION_DEV_DISC_REQ): trace_seq_printf(p, ": Device Discoverability Request"); break;
|
|
+ case (P2P_ACTION_DEV_DISC_RSP): trace_seq_printf(p, ": Device Discoverability Response"); break;
|
|
+ case (P2P_ACTION_PROV_DISC_REQ): trace_seq_printf(p, ": Provision Discovery Request"); break;
|
|
+ case (P2P_ACTION_PROV_DISC_RSP): trace_seq_printf(p, ": Provision Discovery Response"); break;
|
|
+ default: trace_seq_printf(p, "Unknown p2p %d", p2p); break;
|
|
+ }
|
|
+ else {
|
|
+ switch (cat) {
|
|
+ case 0: trace_seq_printf(p, ":Spectrum %d", type); break;
|
|
+ case 1: trace_seq_printf(p, ":QOS %d", type); break;
|
|
+ case 2: trace_seq_printf(p, ":DLS %d", type); break;
|
|
+ case 3: trace_seq_printf(p, ":BA %d", type); break;
|
|
+ case 4: trace_seq_printf(p, ":Public %d", type); break;
|
|
+ case 5: trace_seq_printf(p, ":Radio Measure %d", type); break;
|
|
+ case 6: trace_seq_printf(p, ":Fast BSS %d", type); break;
|
|
+ case 7: trace_seq_printf(p, ":HT Action %d", type); break;
|
|
+ case 8: trace_seq_printf(p, ":SA Query %d", type); break;
|
|
+ case 9: trace_seq_printf(p, ":Protected Public %d", type); break;
|
|
+ case 10: trace_seq_printf(p, ":WNM %d", type); break;
|
|
+ case 11: trace_seq_printf(p, ":Unprotected WNM %d", type); break;
|
|
+ case 12: trace_seq_printf(p, ":TDLS %d", type); break;
|
|
+ case 13: trace_seq_printf(p, ":Mesh %d", type); break;
|
|
+ case 14: trace_seq_printf(p, ":MultiHop %d", type); break;
|
|
+ case 15: trace_seq_printf(p, ":Self Protected %d", type); break;
|
|
+ case 126: trace_seq_printf(p, ":Vendor protected"); break;
|
|
+ case 127: trace_seq_printf(p, ":Vendor"); break;
|
|
+ default: trace_seq_printf(p, ":Unknown category %d", cat); break;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ default: trace_seq_printf(p, "Unknown subtype %d", frame_control & IEEE80211_FCTL_STYPE); break;
|
|
+ }
|
|
+
|
|
+ trace_seq_putc(p, 0);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
|
|
+
|
|
+#undef __print_mgmt_info
|
|
+#define __print_mgmt_info(frame_control, cat, type, p2p) ftrace_print_mgmt_info(p, frame_control, cat, type, p2p)
|
|
+
|
|
+TRACE_EVENT(
|
|
+ roc,
|
|
+ TP_PROTO(u8 vif_idx, u16 freq, unsigned int duration),
|
|
+ TP_ARGS(vif_idx, freq, duration),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u8, vif_idx)
|
|
+ __field(u16, freq)
|
|
+ __field(unsigned int, duration)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->vif_idx = vif_idx;
|
|
+ __entry->freq = freq;
|
|
+ __entry->duration = duration;
|
|
+ ),
|
|
+ TP_printk("f=%d vif=%d dur=%d",
|
|
+ __entry->freq, __entry->vif_idx, __entry->duration)
|
|
+);
|
|
+
|
|
+TRACE_EVENT(
|
|
+ cancel_roc,
|
|
+ TP_PROTO(u8 vif_idx),
|
|
+ TP_ARGS(vif_idx),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u8, vif_idx)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->vif_idx = vif_idx;
|
|
+ ),
|
|
+ TP_printk("vif=%d", __entry->vif_idx)
|
|
+);
|
|
+
|
|
+TRACE_EVENT(
|
|
+ roc_exp,
|
|
+ TP_PROTO(u8 vif_idx),
|
|
+ TP_ARGS(vif_idx),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u8, vif_idx)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->vif_idx = vif_idx;
|
|
+ ),
|
|
+ TP_printk("vif=%d", __entry->vif_idx)
|
|
+);
|
|
+
|
|
+TRACE_EVENT(
|
|
+ switch_roc,
|
|
+ TP_PROTO(u8 vif_idx),
|
|
+ TP_ARGS(vif_idx),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u8, vif_idx)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->vif_idx = vif_idx;
|
|
+ ),
|
|
+ TP_printk("vif=%d", __entry->vif_idx)
|
|
+);
|
|
+
|
|
+DECLARE_EVENT_CLASS(
|
|
+ mgmt_template,
|
|
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
|
|
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, freq)
|
|
+ __field(u8, vif_idx)
|
|
+ __field(u8, sta_idx)
|
|
+ __field(u16, frame_control)
|
|
+ __field(u8, action_cat)
|
|
+ __field(u8, action_type)
|
|
+ __field(u8, action_p2p)
|
|
+ __array(u8, sa, 6)
|
|
+ __array(u8, da, 6)
|
|
+ __array(u8, bssid, 6)
|
|
+ __array(u8, name, 8)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ int i;
|
|
+ __entry->freq = freq;
|
|
+ __entry->vif_idx = vif_idx;
|
|
+ __entry->sta_idx = sta_idx;
|
|
+ __entry->frame_control = mgmt->frame_control;
|
|
+ __entry->action_cat = mgmt->u.action.category;
|
|
+ __entry->action_type = mgmt->u.action.u.wme_action.action_code;
|
|
+ __entry->action_p2p = *((u8 *)&mgmt->u.action.category
|
|
+ + MGMT_ACTION_OUI_SUBTYPE_OFFSET);
|
|
+ for(i=0; i<6; i++) {
|
|
+ __entry->sa[i] = mgmt->sa[i];
|
|
+ __entry->da[i] = mgmt->da[i];
|
|
+ __entry->bssid[i] = mgmt->bssid[i];
|
|
+
|
|
+ }
|
|
+ for(i=0; i<8; i++) {
|
|
+ __entry->name[i] = mgmt->u.beacon.variable[i];
|
|
+ }
|
|
+ ),
|
|
+ TP_printk("f=%d vif=%d sta=%d -> %s, sa:%x:%x:%x:%x:%x:%x, \
|
|
+ da:%x:%x:%x:%x:%x:%x, bssid:%x:%x:%x:%x:%x:%x, \
|
|
+ name: %c%c%c%c%c%c",
|
|
+ __entry->freq, __entry->vif_idx, __entry->sta_idx,
|
|
+ __print_mgmt_info(__entry->frame_control, __entry->action_cat,
|
|
+ __entry->action_type, __entry->action_p2p),
|
|
+ __entry->sa[0], __entry->sa[1], __entry->sa[2], __entry->sa[3], __entry->sa[4], __entry->sa[5],
|
|
+ __entry->da[0], __entry->da[1], __entry->da[2], __entry->da[3], __entry->da[4], __entry->da[5],
|
|
+ __entry->bssid[0], __entry->bssid[1], __entry->bssid[2], __entry->bssid[3], __entry->bssid[4], __entry->bssid[5],
|
|
+ __entry->name[2], __entry->name[3], __entry->name[4], __entry->name[5], __entry->name[6], __entry->name[7])
|
|
+);
|
|
+
|
|
+DEFINE_EVENT(mgmt_template, mgmt_tx,
|
|
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
|
|
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt));
|
|
+
|
|
+DEFINE_EVENT(mgmt_template, mgmt_rx,
|
|
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
|
|
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt));
|
|
+
|
|
+TRACE_EVENT(
|
|
+ mgmt_cfm,
|
|
+ TP_PROTO(u8 vif_idx, u8 sta_idx, bool acked),
|
|
+ TP_ARGS(vif_idx, sta_idx, acked),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u8, vif_idx)
|
|
+ __field(u8, sta_idx)
|
|
+ __field(bool, acked)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->vif_idx = vif_idx;
|
|
+ __entry->sta_idx = sta_idx;
|
|
+ __entry->acked = acked;
|
|
+ ),
|
|
+ TP_printk("vif=%d sta=%d ack=%d",
|
|
+ __entry->vif_idx, __entry->sta_idx, __entry->acked)
|
|
+);
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+/*****************************************************************************
|
|
+ * TRACE function for TXQ
|
|
+ ****************************************************************************/
|
|
+#include "bl_tx.h"
|
|
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
|
|
+
|
|
+#include <linux/trace_seq.h>
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
|
|
+#include <linux/trace_events.h>
|
|
+#else
|
|
+#include <linux/ftrace_event.h>
|
|
+#endif
|
|
+
|
|
+
|
|
+const char *
|
|
+ftrace_print_txq(struct trace_seq *p, int txq_idx) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+ const char *ret = trace_seq_buffer_ptr(p);
|
|
+#else
|
|
+ const char *ret = p->buffer + p->len;
|
|
+#endif
|
|
+
|
|
+ if (txq_idx == TXQ_INACTIVE) {
|
|
+ trace_seq_printf(p, "[INACTIVE]");
|
|
+ } else if (txq_idx < NX_FIRST_VIF_TXQ_IDX) {
|
|
+ trace_seq_printf(p, "[STA %d/%d]",
|
|
+ txq_idx / NX_NB_TXQ_PER_STA,
|
|
+ txq_idx % NX_NB_TXQ_PER_STA);
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ } else if (txq_idx < NX_FIRST_UNK_TXQ_IDX) {
|
|
+ trace_seq_printf(p, "[BC/MC %d]",
|
|
+ txq_idx - NX_FIRST_BCMC_TXQ_IDX);
|
|
+ } else if (txq_idx < NX_OFF_CHAN_TXQ_IDX) {
|
|
+ trace_seq_printf(p, "[UNKNOWN %d]",
|
|
+ txq_idx - NX_FIRST_UNK_TXQ_IDX);
|
|
+ } else if (txq_idx == NX_OFF_CHAN_TXQ_IDX) {
|
|
+ trace_seq_printf(p, "[OFFCHAN]");
|
|
+#else
|
|
+ } else if (txq_idx < NX_NB_TXQ) {
|
|
+ txq_idx -= NX_FIRST_VIF_TXQ_IDX;
|
|
+ trace_seq_printf(p, "[VIF %d/%d]",
|
|
+ txq_idx / NX_NB_TXQ_PER_VIF,
|
|
+ txq_idx % NX_NB_TXQ_PER_VIF);
|
|
+#endif
|
|
+ } else {
|
|
+ trace_seq_printf(p, "[ERROR %d]", txq_idx);
|
|
+ }
|
|
+
|
|
+ trace_seq_putc(p, 0);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+const char *
|
|
+ftrace_print_sta(struct trace_seq *p, int sta_idx) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+ const char *ret = trace_seq_buffer_ptr(p);
|
|
+#else
|
|
+ const char *ret = p->buffer + p->len;
|
|
+#endif
|
|
+
|
|
+ if (sta_idx < NX_REMOTE_STA_MAX) {
|
|
+ trace_seq_printf(p, "[STA %d]", sta_idx);
|
|
+ } else {
|
|
+ trace_seq_printf(p, "[BC/MC %d]", sta_idx - NX_REMOTE_STA_MAX);
|
|
+ }
|
|
+
|
|
+ trace_seq_putc(p, 0);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+const char *
|
|
+ftrace_print_hwq(struct trace_seq *p, int hwq_idx) {
|
|
+
|
|
+ static const struct trace_print_flags symbols[] =
|
|
+ {{BL_HWQ_BK, "BK"},
|
|
+ {BL_HWQ_BE, "BE"},
|
|
+ {BL_HWQ_VI, "VI"},
|
|
+ {BL_HWQ_VO, "VO"},
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ {BL_HWQ_BCMC, "BCMC"},
|
|
+#else
|
|
+ {BL_HWQ_BCN, "BCN"},
|
|
+#endif
|
|
+ { -1, NULL }};
|
|
+ return trace_print_symbols_seq(p, hwq_idx, symbols);
|
|
+}
|
|
+
|
|
+const char *
|
|
+ftrace_print_hwq_cred(struct trace_seq *p, u8 *cred) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+ const char *ret = trace_seq_buffer_ptr(p);
|
|
+#else
|
|
+ const char *ret = p->buffer + p->len;
|
|
+#endif
|
|
+
|
|
+#if CONFIG_USER_MAX == 1
|
|
+ trace_seq_printf(p, "%d", cred[0]);
|
|
+#else
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < CONFIG_USER_MAX - 1; i++)
|
|
+ trace_seq_printf(p, "%d-", cred[i]);
|
|
+ trace_seq_printf(p, "%d", cred[i]);
|
|
+#endif
|
|
+
|
|
+ trace_seq_putc(p, 0);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+const char *
|
|
+ftrace_print_mu_info(struct trace_seq *p, u8 mu_info) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+ const char *ret = trace_seq_buffer_ptr(p);
|
|
+#else
|
|
+ const char *ret = p->buffer + p->len;
|
|
+#endif
|
|
+
|
|
+ if (mu_info)
|
|
+ trace_seq_printf(p, "MU: %d-%d", (mu_info & 0x3f), (mu_info >> 6));
|
|
+
|
|
+ trace_seq_putc(p, 0);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+const char *
|
|
+ftrace_print_mu_group(struct trace_seq *p, int nb_user, u8 *users) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+ const char *ret = trace_seq_buffer_ptr(p);
|
|
+#else
|
|
+ const char *ret = p->buffer + p->len;
|
|
+#endif
|
|
+ int i;
|
|
+
|
|
+ if (users[0] != 0xff)
|
|
+ trace_seq_printf(p, "(%d", users[0]);
|
|
+ else
|
|
+ trace_seq_printf(p, "(-");
|
|
+ for (i = 1; i < CONFIG_USER_MAX ; i++) {
|
|
+ if (users[i] != 0xff)
|
|
+ trace_seq_printf(p, ",%d", users[i]);
|
|
+ else
|
|
+ trace_seq_printf(p, ",-");
|
|
+ }
|
|
+
|
|
+ trace_seq_printf(p, ")");
|
|
+ trace_seq_putc(p, 0);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
|
|
+
|
|
+#undef __print_txq
|
|
+#define __print_txq(txq_idx) ftrace_print_txq(p, txq_idx)
|
|
+
|
|
+#undef __print_sta
|
|
+#define __print_sta(sta_idx) ftrace_print_sta(p, sta_idx)
|
|
+
|
|
+#undef __print_hwq
|
|
+#define __print_hwq(hwq) ftrace_print_hwq(p, hwq)
|
|
+
|
|
+#undef __print_hwq_cred
|
|
+#define __print_hwq_cred(cred) ftrace_print_hwq_cred(p, cred)
|
|
+
|
|
+#undef __print_mu_info
|
|
+#define __print_mu_info(mu_info) ftrace_print_mu_info(p, mu_info)
|
|
+
|
|
+#undef __print_mu_group
|
|
+#define __print_mu_group(nb, users) ftrace_print_mu_group(p, nb, users)
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+
|
|
+TRACE_EVENT(
|
|
+ txq_select,
|
|
+ TP_PROTO(int txq_idx, u16 pkt_ready_up, struct sk_buff *skb),
|
|
+ TP_ARGS(txq_idx, pkt_ready_up, skb),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, txq_idx)
|
|
+ __field(u16, pkt_ready)
|
|
+ __field(struct sk_buff *, skb)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->txq_idx = txq_idx;
|
|
+ __entry->pkt_ready = pkt_ready_up;
|
|
+ __entry->skb = skb;
|
|
+ ),
|
|
+ TP_printk("%s pkt_ready_up=%d skb=%p", __print_txq(__entry->txq_idx),
|
|
+ __entry->pkt_ready, __entry->skb)
|
|
+);
|
|
+
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+DECLARE_EVENT_CLASS(
|
|
+ hwq_template,
|
|
+ TP_PROTO(u8 hwq_idx),
|
|
+ TP_ARGS(hwq_idx),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u8, hwq_idx)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->hwq_idx = hwq_idx;
|
|
+ ),
|
|
+ TP_printk("%s", __print_hwq(__entry->hwq_idx))
|
|
+);
|
|
+
|
|
+DEFINE_EVENT(hwq_template, hwq_flowctrl_stop,
|
|
+ TP_PROTO(u8 hwq_idx),
|
|
+ TP_ARGS(hwq_idx));
|
|
+
|
|
+DEFINE_EVENT(hwq_template, hwq_flowctrl_start,
|
|
+ TP_PROTO(u8 hwq_idx),
|
|
+ TP_ARGS(hwq_idx));
|
|
+
|
|
+
|
|
+DECLARE_EVENT_CLASS(
|
|
+ txq_template,
|
|
+ TP_PROTO(struct bl_txq *txq),
|
|
+ TP_ARGS(txq),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, txq_idx)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->txq_idx = txq->idx;
|
|
+ ),
|
|
+ TP_printk("%s", __print_txq(__entry->txq_idx))
|
|
+);
|
|
+
|
|
+DEFINE_EVENT(txq_template, txq_add_to_hw,
|
|
+ TP_PROTO(struct bl_txq *txq),
|
|
+ TP_ARGS(txq));
|
|
+
|
|
+DEFINE_EVENT(txq_template, txq_del_from_hw,
|
|
+ TP_PROTO(struct bl_txq *txq),
|
|
+ TP_ARGS(txq));
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+
|
|
+DEFINE_EVENT(txq_template, txq_flowctrl_stop,
|
|
+ TP_PROTO(struct bl_txq *txq),
|
|
+ TP_ARGS(txq));
|
|
+
|
|
+DEFINE_EVENT(txq_template, txq_flowctrl_restart,
|
|
+ TP_PROTO(struct bl_txq *txq),
|
|
+ TP_ARGS(txq));
|
|
+
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+TRACE_EVENT(
|
|
+ process_txq,
|
|
+ TP_PROTO(struct bl_txq *txq),
|
|
+ TP_ARGS(txq),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, txq_idx)
|
|
+ __field(u16, len)
|
|
+ __field(u16, len_retry)
|
|
+ __field(s8, credit)
|
|
+ #ifdef CONFIG_BL_FULLMAC
|
|
+ __field(u16, limit)
|
|
+ #endif /* CONFIG_BL_FULLMAC*/
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->txq_idx = txq->idx;
|
|
+ __entry->len = skb_queue_len(&txq->sk_list);
|
|
+ __entry->len_retry = txq->nb_retry;
|
|
+ __entry->credit = txq->credits;
|
|
+ #ifdef CONFIG_BL_FULLMAC
|
|
+ __entry->limit = txq->push_limit;
|
|
+ #endif /* CONFIG_BL_FULLMAC*/
|
|
+ ),
|
|
+
|
|
+ #ifdef CONFIG_BL_FULLMAC
|
|
+ TP_printk("%s txq_credits=%d, len=%d, retry_len=%d, push_limit=%d",
|
|
+ __print_txq(__entry->txq_idx), __entry->credit,
|
|
+ __entry->len, __entry->len_retry, __entry->limit)
|
|
+ #else
|
|
+ TP_printk("%s txq_credits=%d, len=%d, retry_len=%d",
|
|
+ __print_txq(__entry->txq_idx), __entry->credit,
|
|
+ __entry->len, __entry->len_retry)
|
|
+ #endif /* CONFIG_BL_FULLMAC*/
|
|
+);
|
|
+
|
|
+DECLARE_EVENT_CLASS(
|
|
+ txq_reason_template,
|
|
+ TP_PROTO(struct bl_txq *txq, u16 reason),
|
|
+ TP_ARGS(txq, reason),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, txq_idx)
|
|
+ __field(u16, reason)
|
|
+ __field(u16, status)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->txq_idx = txq->idx;
|
|
+ __entry->reason = reason;
|
|
+ __entry->status = txq->status;
|
|
+ ),
|
|
+ TP_printk("%s reason=%s status=%s",
|
|
+ __print_txq(__entry->txq_idx),
|
|
+ __print_symbolic(__entry->reason,
|
|
+ {BL_TXQ_STOP_FULL, "FULL"},
|
|
+ {BL_TXQ_STOP_CSA, "CSA"},
|
|
+ {BL_TXQ_STOP_STA_PS, "PS"},
|
|
+ {BL_TXQ_STOP_VIF_PS, "VPS"},
|
|
+ {BL_TXQ_STOP_CHAN, "CHAN"},
|
|
+ {BL_TXQ_STOP_MU_POS, "MU"}),
|
|
+ __print_flags(__entry->status, "|",
|
|
+ {BL_TXQ_IN_HWQ_LIST, "IN LIST"},
|
|
+ {BL_TXQ_STOP_FULL, "FULL"},
|
|
+ {BL_TXQ_STOP_CSA, "CSA"},
|
|
+ {BL_TXQ_STOP_STA_PS, "PS"},
|
|
+ {BL_TXQ_STOP_VIF_PS, "VPS"},
|
|
+ {BL_TXQ_STOP_CHAN, "CHAN"},
|
|
+ {BL_TXQ_STOP_MU_POS, "MU"},
|
|
+ {BL_TXQ_NDEV_FLOW_CTRL, "FLW_CTRL"}))
|
|
+);
|
|
+
|
|
+DEFINE_EVENT(txq_reason_template, txq_start,
|
|
+ TP_PROTO(struct bl_txq *txq, u16 reason),
|
|
+ TP_ARGS(txq, reason));
|
|
+
|
|
+DEFINE_EVENT(txq_reason_template, txq_stop,
|
|
+ TP_PROTO(struct bl_txq *txq, u16 reason),
|
|
+ TP_ARGS(txq, reason));
|
|
+
|
|
+
|
|
+TRACE_EVENT(
|
|
+ push_desc,
|
|
+ TP_PROTO(struct sk_buff *skb, struct bl_sw_txhdr *sw_txhdr, int push_flags),
|
|
+
|
|
+ TP_ARGS(skb, sw_txhdr, push_flags),
|
|
+
|
|
+ TP_STRUCT__entry(
|
|
+ __field(struct sk_buff *, skb)
|
|
+ __field(unsigned int, len)
|
|
+ __field(u16, tx_queue)
|
|
+ __field(u8, hw_queue)
|
|
+ __field(u8, push_flag)
|
|
+ __field(u32, flag)
|
|
+ __field(s8, txq_cred)
|
|
+ __field(u8, hwq_cred)
|
|
+ __field(u8, mu_info)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->skb = skb;
|
|
+ __entry->tx_queue = sw_txhdr->txq->idx;
|
|
+ __entry->push_flag = push_flags;
|
|
+ __entry->hw_queue = sw_txhdr->txq->hwq->id;
|
|
+ __entry->txq_cred = sw_txhdr->txq->credits;
|
|
+ __entry->hwq_cred = sw_txhdr->txq->hwq->credits[BL_TXQ_POS_ID(sw_txhdr->txq)];
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ __entry->flag = sw_txhdr->desc.host.flags;
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ if (sw_txhdr->amsdu.len)
|
|
+ __entry->len = sw_txhdr->amsdu.len;
|
|
+ else
|
|
+#endif /* CONFIG_BL_AMSDUS_TX */
|
|
+ __entry->len = sw_txhdr->desc.host.packet_len[0];
|
|
+#else
|
|
+ __entry->len = sw_txhdr->desc.host.packet_len;
|
|
+#endif /* CONFIG_BL_SPLIT_TX_BUF */
|
|
+
|
|
+#else /* CONFIG_BL_FULLMAC */
|
|
+ __entry->flag = sw_txhdr->desc.umac.flags;
|
|
+ __entry->len = sw_txhdr->frame_len;
|
|
+ __entry->sn = sw_txhdr->sn;
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+ __entry->mu_info = 0;
|
|
+ ),
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%s %s%s",
|
|
+ __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
|
|
+ __print_hwq(__entry->hw_queue),
|
|
+ __entry->txq_cred, __entry->hwq_cred,
|
|
+ __print_mu_info(__entry->mu_info),
|
|
+ __print_flags(__entry->flag, "|",
|
|
+ {TXU_CNTRL_RETRY, "RETRY"},
|
|
+ {TXU_CNTRL_MORE_DATA, "MOREDATA"},
|
|
+ {TXU_CNTRL_MGMT, "MGMT"},
|
|
+ {TXU_CNTRL_MGMT_NO_CCK, "NO_CCK"},
|
|
+ {TXU_CNTRL_MGMT_ROBUST, "ROBUST"},
|
|
+ {TXU_CNTRL_AMSDU, "AMSDU"},
|
|
+ {TXU_CNTRL_USE_4ADDR, "4ADDR"},
|
|
+ {TXU_CNTRL_EOSP, "EOSP"},
|
|
+ {TXU_CNTRL_MESH_FWD, "MESH_FWD"},
|
|
+ {TXU_CNTRL_TDLS, "TDLS"}),
|
|
+ (__entry->push_flag & BL_PUSH_IMMEDIATE) ? "(IMMEDIATE)" : "",
|
|
+ (!(__entry->flag & TXU_CNTRL_RETRY) &&
|
|
+ (__entry->push_flag & BL_PUSH_RETRY)) ? "(SW_RETRY)" : "")
|
|
+#else
|
|
+ TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%x (%s) sn=%d",
|
|
+ __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
|
|
+ __print_hwq(__entry->hw_queue), __entry->txq_cred, __entry->hwq_cred,
|
|
+ __print_mu_info(__entry->mu_info),
|
|
+ __entry->flag,
|
|
+ __print_flags(__entry->push_flag, "|",
|
|
+ {BL_PUSH_RETRY, "RETRY"},
|
|
+ {BL_PUSH_IMMEDIATE, "IMMEDIATE"}),
|
|
+ __entry->sn)
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+);
|
|
+
|
|
+
|
|
+TRACE_EVENT(
|
|
+ txq_queue_skb,
|
|
+ TP_PROTO(struct sk_buff *skb, struct bl_txq *txq, bool retry),
|
|
+ TP_ARGS(skb, txq, retry),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(struct sk_buff *, skb)
|
|
+ __field(u16, txq_idx)
|
|
+ __field(s8, credit)
|
|
+ __field(u16, q_len)
|
|
+ __field(u16, q_len_retry)
|
|
+ __field(bool, retry)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->skb = skb;
|
|
+ __entry->txq_idx = txq->idx;
|
|
+ __entry->credit = txq->credits;
|
|
+ __entry->q_len = skb_queue_len(&txq->sk_list);
|
|
+ __entry->q_len_retry = txq->nb_retry;
|
|
+ __entry->retry = retry;
|
|
+ ),
|
|
+
|
|
+ TP_printk("%s skb=%p retry=%d txq_credits=%d queue_len=%d (retry = %d)",
|
|
+ __print_txq(__entry->txq_idx), __entry->skb, __entry->retry,
|
|
+ __entry->credit, __entry->q_len, __entry->q_len_retry)
|
|
+);
|
|
+
|
|
+
|
|
+DECLARE_EVENT_CLASS(
|
|
+ idx_template,
|
|
+ TP_PROTO(u16 idx),
|
|
+ TP_ARGS(idx),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, idx)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->idx = idx;
|
|
+ ),
|
|
+ TP_printk("idx=%d", __entry->idx)
|
|
+);
|
|
+
|
|
+
|
|
+DEFINE_EVENT(idx_template, txq_vif_start,
|
|
+ TP_PROTO(u16 idx),
|
|
+ TP_ARGS(idx));
|
|
+
|
|
+DEFINE_EVENT(idx_template, txq_vif_stop,
|
|
+ TP_PROTO(u16 idx),
|
|
+ TP_ARGS(idx));
|
|
+
|
|
+TRACE_EVENT(
|
|
+ process_hw_queue,
|
|
+ TP_PROTO(struct bl_hwq *hwq),
|
|
+ TP_ARGS(hwq),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, hwq)
|
|
+ __array(u8, credits, CONFIG_USER_MAX)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ int i;
|
|
+ __entry->hwq = hwq->id;
|
|
+ for (i=0; i < CONFIG_USER_MAX; i ++)
|
|
+ __entry->credits[i] = hwq->credits[i];
|
|
+ ),
|
|
+ TP_printk("hw_queue=%s hw_credits=%s",
|
|
+ __print_hwq(__entry->hwq), __print_hwq_cred(__entry->credits))
|
|
+);
|
|
+
|
|
+DECLARE_EVENT_CLASS(
|
|
+ sta_idx_template,
|
|
+ TP_PROTO(u16 idx),
|
|
+ TP_ARGS(idx),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, idx)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->idx = idx;
|
|
+ ),
|
|
+ TP_printk("%s", __print_sta(__entry->idx))
|
|
+);
|
|
+
|
|
+DEFINE_EVENT(sta_idx_template, txq_sta_start,
|
|
+ TP_PROTO(u16 idx),
|
|
+ TP_ARGS(idx));
|
|
+
|
|
+DEFINE_EVENT(sta_idx_template, txq_sta_stop,
|
|
+ TP_PROTO(u16 idx),
|
|
+ TP_ARGS(idx));
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+
|
|
+DEFINE_EVENT(sta_idx_template, ps_disable,
|
|
+ TP_PROTO(u16 idx),
|
|
+ TP_ARGS(idx));
|
|
+
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+TRACE_EVENT(
|
|
+ skb_confirm,
|
|
+ TP_PROTO(struct sk_buff *skb, struct bl_txq *txq, struct bl_hwq *hwq,
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ struct tx_cfm_tag *cfm
|
|
+#else
|
|
+ u8 cfm
|
|
+#endif
|
|
+ ),
|
|
+
|
|
+ TP_ARGS(skb, txq, hwq, cfm),
|
|
+
|
|
+ TP_STRUCT__entry(
|
|
+ __field(struct sk_buff *, skb)
|
|
+ __field(u16, txq_idx)
|
|
+ __field(u8, hw_queue)
|
|
+ __array(u8, hw_credit, CONFIG_USER_MAX)
|
|
+ __field(s8, sw_credit)
|
|
+ __field(s8, sw_credit_up)
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ __field(u8, ampdu_size)
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ __field(u16, amsdu)
|
|
+#endif /* CONFIG_BL_SPLIT_TX_BUF */
|
|
+#endif /* CONFIG_BL_FULLMAC*/
|
|
+ ),
|
|
+
|
|
+ TP_fast_assign(
|
|
+ int i;
|
|
+ __entry->skb = skb;
|
|
+ __entry->txq_idx = txq->idx;
|
|
+ __entry->hw_queue = hwq->id;
|
|
+ for (i = 0 ; i < CONFIG_USER_MAX ; i++)
|
|
+ __entry->hw_credit[i] = hwq->credits[i];
|
|
+ __entry->sw_credit = txq->credits;
|
|
+#if defined CONFIG_BL_FULLMAC
|
|
+ __entry->sw_credit_up = cfm->credits;
|
|
+ __entry->ampdu_size = cfm->ampdu_size;
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ __entry->amsdu = cfm->amsdu_size;
|
|
+#endif
|
|
+#else
|
|
+ __entry->sw_credit_up = cfm
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+ ),
|
|
+
|
|
+ TP_printk("%s skb=%p hw_queue=%s, hw_credits=%s, txq_credits=%d (+%d)"
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ " ampdu=%d"
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ " amsdu=%u"
|
|
+#endif
|
|
+#endif
|
|
+ , __print_txq(__entry->txq_idx), __entry->skb,
|
|
+ __print_hwq(__entry->hw_queue),
|
|
+ __print_hwq_cred(__entry->hw_credit),
|
|
+ __entry->sw_credit, __entry->sw_credit_up
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ , __entry->ampdu_size
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ , __entry->amsdu
|
|
+#endif
|
|
+#endif
|
|
+ )
|
|
+);
|
|
+
|
|
+TRACE_EVENT(
|
|
+ credit_update,
|
|
+ TP_PROTO(struct bl_txq *txq, s8_l cred_up),
|
|
+
|
|
+ TP_ARGS(txq, cred_up),
|
|
+
|
|
+ TP_STRUCT__entry(
|
|
+ __field(struct sk_buff *, skb)
|
|
+ __field(u16, txq_idx)
|
|
+ __field(s8, sw_credit)
|
|
+ __field(s8, sw_credit_up)
|
|
+ ),
|
|
+
|
|
+ TP_fast_assign(
|
|
+ __entry->txq_idx = txq->idx;
|
|
+ __entry->sw_credit = txq->credits;
|
|
+ __entry->sw_credit_up = cred_up;
|
|
+ ),
|
|
+
|
|
+ TP_printk("%s txq_credits=%d (%+d)", __print_txq(__entry->txq_idx),
|
|
+ __entry->sw_credit, __entry->sw_credit_up)
|
|
+)
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+
|
|
+DECLARE_EVENT_CLASS(
|
|
+ ps_template,
|
|
+ TP_PROTO(struct bl_sta *sta),
|
|
+ TP_ARGS(sta),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, idx)
|
|
+ __field(u16, ready_ps)
|
|
+ __field(u16, sp_ps)
|
|
+ __field(u16, ready_uapsd)
|
|
+ __field(u16, sp_uapsd)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->idx = sta->sta_idx;
|
|
+ __entry->ready_ps = sta->ps.pkt_ready[LEGACY_PS_ID];
|
|
+ __entry->sp_ps = sta->ps.sp_cnt[LEGACY_PS_ID];
|
|
+ __entry->ready_uapsd = sta->ps.pkt_ready[UAPSD_ID];
|
|
+ __entry->sp_uapsd = sta->ps.sp_cnt[UAPSD_ID];
|
|
+ ),
|
|
+
|
|
+ TP_printk("%s [PS] ready=%d sp=%d [UAPSD] ready=%d sp=%d",
|
|
+ __print_sta(__entry->idx), __entry->ready_ps, __entry->sp_ps,
|
|
+ __entry->ready_uapsd, __entry->sp_uapsd)
|
|
+);
|
|
+
|
|
+DEFINE_EVENT(ps_template, ps_queue,
|
|
+ TP_PROTO(struct bl_sta *sta),
|
|
+ TP_ARGS(sta));
|
|
+
|
|
+DEFINE_EVENT(ps_template, ps_push,
|
|
+ TP_PROTO(struct bl_sta *sta),
|
|
+ TP_ARGS(sta));
|
|
+
|
|
+DEFINE_EVENT(ps_template, ps_enable,
|
|
+ TP_PROTO(struct bl_sta *sta),
|
|
+ TP_ARGS(sta));
|
|
+
|
|
+TRACE_EVENT(
|
|
+ ps_traffic_update,
|
|
+ TP_PROTO(u16 sta_idx, u8 traffic, bool uapsd),
|
|
+
|
|
+ TP_ARGS(sta_idx, traffic, uapsd),
|
|
+
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, sta_idx)
|
|
+ __field(u8, traffic)
|
|
+ __field(bool, uapsd)
|
|
+ ),
|
|
+
|
|
+ TP_fast_assign(
|
|
+ __entry->sta_idx = sta_idx;
|
|
+ __entry->traffic = traffic;
|
|
+ __entry->uapsd = uapsd;
|
|
+ ),
|
|
+
|
|
+ TP_printk("%s %s%s traffic available ", __print_sta(__entry->sta_idx),
|
|
+ __entry->traffic ? "" : "no more ",
|
|
+ __entry->uapsd ? "U-APSD" : "legacy PS")
|
|
+);
|
|
+
|
|
+TRACE_EVENT(
|
|
+ ps_traffic_req,
|
|
+ TP_PROTO(struct bl_sta *sta, u16 pkt_req, u8 ps_id),
|
|
+ TP_ARGS(sta, pkt_req, ps_id),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, idx)
|
|
+ __field(u16, pkt_req)
|
|
+ __field(u8, ps_id)
|
|
+ __field(u16, ready)
|
|
+ __field(u16, sp)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->idx = sta->sta_idx;
|
|
+ __entry->pkt_req = pkt_req;
|
|
+ __entry->ps_id = ps_id;
|
|
+ __entry->ready = sta->ps.pkt_ready[ps_id];
|
|
+ __entry->sp = sta->ps.sp_cnt[ps_id];
|
|
+ ),
|
|
+
|
|
+ TP_printk("%s %s traffic request %d pkt (ready=%d, sp=%d)",
|
|
+ __print_sta(__entry->idx),
|
|
+ __entry->ps_id == UAPSD_ID ? "U-APSD" : "legacy PS" ,
|
|
+ __entry->pkt_req, __entry->ready, __entry->sp)
|
|
+);
|
|
+
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+TRACE_EVENT(
|
|
+ amsdu_subframe,
|
|
+ TP_PROTO(struct bl_sw_txhdr *sw_txhdr),
|
|
+ TP_ARGS(sw_txhdr),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(struct sk_buff *, skb)
|
|
+ __field(u16, txq_idx)
|
|
+ __field(u8, nb)
|
|
+ __field(u32, len)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->skb = sw_txhdr->skb;
|
|
+ __entry->nb = sw_txhdr->amsdu.nb;
|
|
+ __entry->len = sw_txhdr->amsdu.len;
|
|
+ __entry->txq_idx = sw_txhdr->txq->idx;
|
|
+ ),
|
|
+
|
|
+ TP_printk("%s skb=%p %s nb_subframe=%d, len=%u",
|
|
+ __print_txq(__entry->txq_idx), __entry->skb,
|
|
+ (__entry->nb == 2) ? "Start new AMSDU" : "Add subframe",
|
|
+ __entry->nb, __entry->len)
|
|
+);
|
|
+#endif
|
|
+
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+/*****************************************************************************
|
|
+ * TRACE functions for IPC message
|
|
+ ****************************************************************************/
|
|
+#include "bl_strs.h"
|
|
+
|
|
+DECLARE_EVENT_CLASS(
|
|
+ ipc_msg_template,
|
|
+ TP_PROTO(u16 id),
|
|
+ TP_ARGS(id),
|
|
+ TP_STRUCT__entry(
|
|
+ __field(u16, id)
|
|
+ ),
|
|
+ TP_fast_assign(
|
|
+ __entry->id = id;
|
|
+ ),
|
|
+
|
|
+ TP_printk("%s (%d - %d)", BL_ID2STR(__entry->id),
|
|
+ MSG_T(__entry->id), MSG_I(__entry->id))
|
|
+);
|
|
+
|
|
+DEFINE_EVENT(ipc_msg_template, msg_send,
|
|
+ TP_PROTO(u16 id),
|
|
+ TP_ARGS(id));
|
|
+
|
|
+DEFINE_EVENT(ipc_msg_template, msg_recv,
|
|
+ TP_PROTO(u16 id),
|
|
+ TP_ARGS(id));
|
|
+
|
|
+
|
|
+
|
|
+#endif /* !defined(_BL_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) */
|
|
+
|
|
+#undef TRACE_INCLUDE_PATH
|
|
+#undef TRACE_INCLUDE_FILE
|
|
+#define TRACE_INCLUDE_PATH .
|
|
+#define TRACE_INCLUDE_FILE bl_events
|
|
+#include <trace/define_trace.h>
|
|
diff -Naur /dev/null_irqs.c b/drivers/net/wireless/hflps170/bl_irqs.c
|
|
--- /dev/null_irqs.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_irqs.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,886 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_irqs.c
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/timex.h>
|
|
+#include <linux/rtc.h>
|
|
+
|
|
+#include "bl_defs.h"
|
|
+#include "bl_cmds.h"
|
|
+#include "ipc_host.h"
|
|
+#include "bl_sdio.h"
|
|
+#include "bl_v7.h"
|
|
+
|
|
+int bl_main_process(struct bl_hw *bl_hw);
|
|
+
|
|
+
|
|
+void bl_queue_main_work(struct bl_hw *bl_hw)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ spin_lock_irq(&bl_hw->main_proc_lock);
|
|
+ if (bl_hw->bl_processing) {
|
|
+ BL_DBG("bl_queue_main_work: set more_task_flag true\n");
|
|
+ bl_hw->more_task_flag = true;
|
|
+ spin_unlock_irq(&bl_hw->main_proc_lock);
|
|
+ } else {
|
|
+ spin_unlock_irq(&bl_hw->main_proc_lock);
|
|
+ BL_DBG("bl_queue_main_work: schedule work\n");
|
|
+ queue_work(bl_hw->workqueue, &bl_hw->main_work);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * This function decodes a received packet.
|
|
+ *
|
|
+ * Based on the type, the packet is treated as either a data, or
|
|
+ * a command response, or an event, and the correct handler
|
|
+ * function is invoked.
|
|
+ */
|
|
+static int bl_decode_rx_packet(struct bl_hw *bl_hw, struct sk_buff *skb, u32 upld_type, u32 hw_idx, u32 port)
|
|
+{
|
|
+ unsigned long now = jiffies;
|
|
+ void *hostid = bl_hw->ipc_env->msga2e_hostid;
|
|
+ u32 pld_len = le16_to_cpu(*(__le16 *) (skb->data));
|
|
+ struct bl_agg_reodr_msg *agg_reord_msg = NULL;
|
|
+ struct bl_agg_reord_pkt *reord_pkt = NULL;
|
|
+ struct bl_agg_reord_pkt *reord_pkt_tmp = NULL;
|
|
+ bool found = false;
|
|
+ u8 count = 0;
|
|
+ u16 sn_copy = 0;
|
|
+
|
|
+ u16 pad_len = 0;
|
|
+ if(port !=0) {
|
|
+ pad_len = le16_to_cpu(*(__le16 *) (skb->data + 6));
|
|
+ BL_DBG("pad_len=%d\n", pad_len);
|
|
+ }
|
|
+
|
|
+ //skb_pull(skb, sizeof(struct sdio_hdr));
|
|
+ skb_pull(skb, sizeof(struct sdio_hdr)+pad_len);
|
|
+
|
|
+ switch (upld_type) {
|
|
+ case BL_TYPE_DATA:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: Data packet\n");
|
|
+ bl_rxdataind(bl_hw, skb);
|
|
+ bl_hw->stats.last_rx = now;
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_ACK:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: ACK packet\n");
|
|
+ BL_DBG("Receive ACK: msg_cnt=%u, src_id=%u\n", bl_hw->ipc_env->msga2e_cnt, ((*(u8 *)(skb->data)) & 0xFF));
|
|
+ /*just msg_cnt is matched with src_id or not*/
|
|
+ ASSERT_ERR(hostid);
|
|
+ ASSERT_ERR(bl_hw->ipc_env->msga2e_cnt == ((*(u8 *)(skb->data)) & 0xFF));
|
|
+
|
|
+ bl_hw->ipc_env->msga2e_hostid = NULL;
|
|
+ bl_hw->ipc_env->msga2e_cnt++;
|
|
+ bl_hw->ipc_env->cb.recv_msgack_ind(bl_hw, hostid);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_MSG:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: MSG packet\n");
|
|
+ bl_hw->ipc_env->cb.recv_msg_ind(bl_hw, (void *)(skb->data));
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_DBG_DUMP_START:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: DBG_DUMP_START packet\n");
|
|
+ if(!(bl_hw->dbg_dump_start)) {
|
|
+ BL_DBG("dbg_dump start now...\n");
|
|
+ bl_hw->dbg_dump_start = true;
|
|
+ bl_hw->la_buf_idx = 0;
|
|
+ } else {
|
|
+ BL_DBG("dbg_dump has not completed!\n");
|
|
+ }
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_DBG_DUMP_END:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: DBG_DUMP_END packet\n");
|
|
+ bl_hw->dbg_dump_start = false;
|
|
+ BL_DBG("%s\n", (char *)(bl_hw->dbginfo.buf->dbg_info.error));
|
|
+ //TODO: wake app to cat mactrace
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_DBG_LA_TRACE:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: DBG_LA_TRACE packet\n");
|
|
+ memcpy(&(bl_hw->dbginfo.buf->la_mem[bl_hw->la_buf_idx]), skb->data, pld_len);
|
|
+ bl_hw->la_buf_idx = bl_hw->la_buf_idx + pld_len;
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_DBG_RBD_DESC:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: DBG_RBD_DESC packet\n");
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_DBG_RHD_DESC:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: DBG_RHD_DESC packet\n");
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_DBG_TX_DESC:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: DBG_TX_DESC packet\n");
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_DUMP_INFO:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx: DUMP_INFO packet\n");
|
|
+ memcpy(&(bl_hw->dbginfo.buf->dbg_info), skb->data, sizeof(struct dbg_debug_info_tag));
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_TXCFM:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx:TXCFM packet\n");
|
|
+ {
|
|
+ u32 i;
|
|
+ u32 count;
|
|
+ struct bl_hw_txhdr hw_hdr;
|
|
+ struct bl_txq *txq = NULL;
|
|
+
|
|
+ /* 1. get cfm count from skb->data */
|
|
+ memcpy(&hw_hdr, (struct bl_hw_txhdr *)(skb->data), sizeof(struct bl_hw_txhdr));
|
|
+ count = hw_hdr.cfm.count;
|
|
+ BL_DBG("cfm.sn=%u, cfm.timestamp=%u, cfm.count=%u, cfm.credits=%d\n", hw_hdr.cfm.sn, hw_hdr.cfm.timestamp, hw_hdr.cfm.count, hw_hdr.cfm.credits);
|
|
+
|
|
+ /* 2. handle cfm successively */
|
|
+ for(i = 0; i < count; i++) {
|
|
+ ipc_host_tx_cfm_handler(bl_hw->ipc_env, hw_idx, 0, &hw_hdr, &txq);
|
|
+ if(!txq)
|
|
+ goto TXCFM_EXIT;
|
|
+ if((hw_hdr.cfm.status.retry_required || hw_hdr.cfm.status.sw_retry_required) && (count == 1)) {
|
|
+ BL_DBG("Retry packet received, so we just exit\n");
|
|
+ goto RETRY_PACKET;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //BL_DBG("get txq from cfm_handler: txq->idx=%d\n", txq->idx);
|
|
+
|
|
+ /* 3. update txq->credits */
|
|
+ if (txq->idx != TXQ_INACTIVE) {
|
|
+ if (hw_hdr.cfm.credits) {
|
|
+ BL_DBG("orign txq->idx=%d, txq->status=0x%x, txq->credits=%d\n", txq->idx, txq->status, txq->credits);
|
|
+ txq->credits += hw_hdr.cfm.credits;
|
|
+ BL_DBG("after add credits: hw_hdr.cfm.credits=%d, txq->idx=%d, txq->status=0x%x, txq->credits=%d\n",
|
|
+ hw_hdr.cfm.credits, txq->idx, txq->status, txq->credits);
|
|
+
|
|
+ /* spin_lock_bh(&bl_hw->txq_lock);
|
|
+ if (txq->credits <= 0)
|
|
+ bl_txq_stop(txq, BL_TXQ_STOP_FULL);
|
|
+ else if (txq->credits > 0)
|
|
+ bl_txq_start(txq, BL_TXQ_STOP_FULL);
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);*/
|
|
+ }
|
|
+
|
|
+ /* continue service period */
|
|
+ if (unlikely(txq->push_limit && !bl_txq_is_full(txq))) {
|
|
+ bl_txq_add_to_hw_list(txq);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* 4. update ampdu_size */
|
|
+ if (hw_hdr.cfm.ampdu_size &&
|
|
+ hw_hdr.cfm.ampdu_size < IEEE80211_MAX_AMPDU_BUF)
|
|
+ bl_hw->stats.ampdus_tx[hw_hdr.cfm.ampdu_size - 1]++;
|
|
+
|
|
+ /* 5. update amsdu_size */
|
|
+ #ifdef CONFIG_BL_AMSDUS_TX
|
|
+ txq->amsdu_len = hw_hdr.cfm.amsdu_size;
|
|
+ #endif
|
|
+
|
|
+ }
|
|
+
|
|
+ bl_hw->stats.last_tx = now;
|
|
+TXCFM_EXIT:
|
|
+RETRY_PACKET:
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_AGG_REORD_MSG:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx:AGG REORDER MSG packet\n");
|
|
+ agg_reord_msg = (struct bl_agg_reodr_msg *)(skb->data);
|
|
+
|
|
+ sn_copy = agg_reord_msg->sn;
|
|
+
|
|
+ BL_DBG("AGG_REORD_MSG: sn=%u, num=%u, sta_idx=%d, tid=%d, status=%d\n",
|
|
+ agg_reord_msg->sn, agg_reord_msg->num, agg_reord_msg->sta_idx, agg_reord_msg->tid, agg_reord_msg->status);
|
|
+
|
|
+RE_THOUGH_LIST:
|
|
+ /*1. get the skb according sn*/
|
|
+ if (!list_empty(&bl_hw->reorder_list[agg_reord_msg->sta_idx*NX_NB_TID_PER_STA + agg_reord_msg->tid])) {
|
|
+ list_for_each_entry_safe(reord_pkt, reord_pkt_tmp, &bl_hw->reorder_list[agg_reord_msg->sta_idx*NX_NB_TID_PER_STA + agg_reord_msg->tid], list) {
|
|
+ BL_DBG("reord_pkt->sn=%u\n", reord_pkt->sn);
|
|
+ if((reord_pkt->sn == agg_reord_msg->sn) && (count < agg_reord_msg->num)) {
|
|
+ if(!found)
|
|
+ found = true;
|
|
+
|
|
+ BL_DBG("found pkt: sn=%u, next_sn=%u, count(%u) of num(%u)\n",
|
|
+ agg_reord_msg->sn, (agg_reord_msg->sn + 1) % AGG_REORD_MSG_MAX_NUM, count, agg_reord_msg->num);
|
|
+ /*2. modify the status in skb according agg_reodr_msg->status*/
|
|
+ memcpy(reord_pkt->skb->data, &(agg_reord_msg->status), 1);
|
|
+ /*3. call bl_rxdataind to handle the skb*/
|
|
+ bl_rxdataind(bl_hw, reord_pkt->skb);
|
|
+ /*4. delete this agg reodr pkt node*/
|
|
+ list_del(&reord_pkt->list);
|
|
+ kmem_cache_free(bl_hw->agg_reodr_pkt_cache, reord_pkt);
|
|
+ /*5. find next pkt, if sn == 4095, next sn will change to 0*/
|
|
+ agg_reord_msg->sn = (agg_reord_msg->sn + 1) % AGG_REORD_MSG_MAX_NUM;
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ BL_DBG("count=%u, agg_reord_msg->num=%u\n", count, agg_reord_msg->num);
|
|
+
|
|
+ if((agg_reord_msg->sn == 0) && (count < agg_reord_msg->num)) {
|
|
+ BL_DBG("sn=0, re-though the list\n");
|
|
+ goto RE_THOUGH_LIST;
|
|
+ }
|
|
+
|
|
+ if(count == agg_reord_msg->num) {
|
|
+ count = 0;
|
|
+ agg_reord_msg->sn = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if(!found)
|
|
+ BL_DBG("Not found the packet, sn=%u\n", agg_reord_msg->sn);
|
|
+ } else {
|
|
+ BL_DBG("Oh! give me reodr msg, but no packet need to handle?\n");
|
|
+ }
|
|
+
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_DBG:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx:DBG MSG packet\n");
|
|
+ *(skb->data+pld_len) = '\0';
|
|
+ BL_DBG("fw: %s", (char *)(skb->data));
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_TX_STOP:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx:TX_STOP packet\n");
|
|
+ bl_hw->recovery_flag = true;
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ case BL_TYPE_TX_RESUME:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx:TX_RESUME packet\n");
|
|
+ bl_hw->recovery_flag = false;
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ BL_DBG("info: -------------------------------------------------------->>>Rx:UNKNOWN[%#x] packet\n", upld_type);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function receive data from the sdio device.
|
|
+ */
|
|
+static int bl_card_to_host(struct bl_hw *bl_hw, u32 *type, u32 *hw_idx, u8 *buffer, u32 npayload, u32 ioport)
|
|
+{
|
|
+ int ret;
|
|
+ u32 nb;
|
|
+ int reserved;
|
|
+ u8 rd_bitmap_l = 0;
|
|
+ u8 rd_bitmap_u = 0;
|
|
+ u32 bitmap;
|
|
+ u32 bitmap_cur;
|
|
+ u32 pre_rdport = 0;
|
|
+ int count = 0;
|
|
+ struct bl_device *bl_device;
|
|
+ struct bl_plat *bl_plat;
|
|
+
|
|
+ bl_plat = bl_hw->plat;
|
|
+ bl_device = (struct bl_device *)bl_plat->priv;
|
|
+
|
|
+ if (!buffer) {
|
|
+ printk("%s: buffer is NULL\n", __func__);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ ret = bl_read_data_sync(bl_hw, buffer, npayload, ioport);
|
|
+
|
|
+ if (ret) {
|
|
+ printk("%s: read iomem failed: %d\n", __func__, ret);
|
|
+ return -1;
|
|
+ } else {
|
|
+ bl_read_reg(bl_hw, bl_device->reg->rd_bitmap_l, &rd_bitmap_l);
|
|
+ bl_read_reg(bl_hw, bl_device->reg->rd_bitmap_u, &rd_bitmap_u);
|
|
+ bitmap = rd_bitmap_l;
|
|
+ bitmap |= rd_bitmap_u << 8;
|
|
+
|
|
+ bitmap_cur = bitmap;
|
|
+
|
|
+ while(bitmap) {
|
|
+ bitmap = bitmap & (bitmap-1);
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ pre_rdport = ioport - bl_hw->plat->io_port;
|
|
+ BL_DBG("read success: bitmap=0x%x, pre_rdport=%d, 1<<pre_rdport=0x%x, count=%d\n", bitmap_cur, pre_rdport, 1<<pre_rdport, count);
|
|
+
|
|
+ if((bitmap_cur & (1<<pre_rdport)) && (count>1)) {
|
|
+ BL_DBG("read success, but port bitmap was not clear\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *type = le16_to_cpu(*(__le16 *) (buffer + 2));
|
|
+ nb = le16_to_cpu(*(__le16 *) (buffer));
|
|
+ *hw_idx = le16_to_cpu(*(__le16 *) (buffer + 4));
|
|
+ reserved = le16_to_cpu(*(__le16 *) (buffer + 6));
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * This function transfers received packets from card to driver, performing
|
|
+ * aggregation if required.
|
|
+ *
|
|
+ * For data received on control port, or if aggregation is disabled, the
|
|
+ * received buffers are uploaded as separate packets. However, if aggregation
|
|
+ * is enabled and required, the buffers are copied onto an aggregation buffer,
|
|
+ * provided there is space left, processed and finally uploaded.
|
|
+ */
|
|
+static int bl_rx_process(struct bl_hw *bl_hw, struct sk_buff *skb, u32 port)
|
|
+{
|
|
+ u32 pkt_type;
|
|
+ u32 hw_idx;
|
|
+ u16 *buf;
|
|
+
|
|
+ if (bl_card_to_host(bl_hw, &pkt_type, &hw_idx, skb->data, skb->len, bl_hw->plat->io_port + port))
|
|
+ goto error;
|
|
+
|
|
+ buf = (u16 *)(skb->data);
|
|
+ BL_DBG("buf[0]=%u, buf[1]=%u, buf[2]=%u, buf[3]=%u, bl_hw->msg_idx=%u\n", buf[0], buf[1], buf[2], buf[3], bl_hw->msg_idx);
|
|
+ if(port == 0) {
|
|
+ if (bl_hw->msg_idx == buf[3]) {
|
|
+ BL_DBG("receive msg twice, msg_idx=%u\n", bl_hw->msg_idx);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //update msg idx
|
|
+ bl_hw->msg_idx = buf[3];
|
|
+
|
|
+ bl_decode_rx_packet(bl_hw, skb, pkt_type, hw_idx, port);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void bl_get_interrupt_status(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct bl_device *bl_device;
|
|
+ struct bl_plat *bl_plat;
|
|
+ u8 sdio_ireg;
|
|
+
|
|
+ bl_plat = bl_hw->plat;
|
|
+ bl_device = (struct bl_device *)bl_plat->priv;
|
|
+
|
|
+ if(bl_read_data_sync_claim0(bl_hw, bl_plat->mp_regs, bl_device->reg->max_mp_regs, REG_PORT | BL_SDIO_BYTE_MODE_MASK)) {
|
|
+ printk("read mp_regs failed\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ sdio_ireg = bl_plat->mp_regs[bl_device->reg->host_int_status_reg];
|
|
+
|
|
+ if(sdio_ireg) {
|
|
+ BL_DBG(">>>bl_get_interrupt_status, sdio_ireg=0x%x\n", sdio_ireg);
|
|
+ spin_lock(&bl_hw->int_lock);
|
|
+ bl_hw->int_status |= sdio_ireg;
|
|
+ spin_unlock(&bl_hw->int_lock);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_irq_hdlr - IRQ handler
|
|
+ *
|
|
+ * Handler registerd by the platform driver
|
|
+ */
|
|
+void bl_irq_hdlr(struct sdio_func *func)
|
|
+{
|
|
+ struct bl_hw *bl_hw;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_hw = sdio_get_drvdata(func);
|
|
+ if(!bl_hw) {
|
|
+ printk("get bl_hw failed!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ bl_get_interrupt_status(bl_hw);
|
|
+
|
|
+#if 0
|
|
+ /*irq/irq top handler call spin_lock, no need to disable irq
|
|
+ *but, workqueue and user thread need disable irq
|
|
+ */
|
|
+ spin_lock_irq(&bl_hw->main_proc_lock);
|
|
+ if (bl_hw->bl_processing) {
|
|
+ BL_DBG("bl_irq_hdlr: set more_task_flag true\n");
|
|
+ bl_hw->more_task_flag = true;
|
|
+ spin_unlock_irq(&bl_hw->main_proc_lock);
|
|
+ } else {
|
|
+ spin_unlock_irq(&bl_hw->main_proc_lock);
|
|
+ BL_DBG("bl_irq_hdlr: schedule work\n");
|
|
+ queue_work(bl_hw->workqueue, &bl_hw->main_work);
|
|
+ }
|
|
+ //bl_queue_main_work(bl_hw);
|
|
+#endif
|
|
+
|
|
+ bl_main_process(bl_hw);
|
|
+}
|
|
+
|
|
+static int bl_pending_msg_hdl(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct bl_cmd *cur = NULL;
|
|
+ u32 count = 0;
|
|
+ u8 wr_bitmap_l = 0;
|
|
+ struct bl_cmd *hostid = (struct bl_cmd *)(bl_hw->ipc_env->msga2e_hostid);
|
|
+
|
|
+ struct bl_device *device = (struct bl_device *)bl_hw->plat->priv;
|
|
+
|
|
+ list_for_each_entry(cur, &bl_hw->cmd_mgr.cmds, list) {
|
|
+ cmd_dump(cur);
|
|
+ BL_DBG("cur->tkn=%d, hostid->tkn=%d, queue_sz=%d, max_queue_sz=%d\n", cur->tkn, hostid->tkn, bl_hw->cmd_mgr.queue_sz, bl_hw->cmd_mgr.max_queue_sz);
|
|
+ if(cur->tkn == hostid->tkn) {
|
|
+ //bl_read_reg(bl_hw, device->reg->wr_bitmap_l, &wr_bitmap_l);
|
|
+ wr_bitmap_l = bl_hw->plat->mp_regs[device->reg->wr_bitmap_l];
|
|
+ while((!(wr_bitmap_l & CTRL_PORT_MASK)) && count < 200){
|
|
+ msleep(5);
|
|
+ count++;
|
|
+ bl_read_reg(bl_hw, device->reg->wr_bitmap_l, &wr_bitmap_l);
|
|
+ BL_DBG("wait for cmd port ready!\n");
|
|
+ };
|
|
+
|
|
+ if(count >=200){
|
|
+ BL_DBG("wait for cmd port timeout!\n");
|
|
+ kfree(cur->a2e_msg);
|
|
+ return -1;
|
|
+ }
|
|
+ if(cur->flags |BL_CMD_FLAG_WAIT_PUSH)
|
|
+ cur->flags &= ~BL_CMD_FLAG_WAIT_PUSH;
|
|
+ bl_write_data_sync(bl_hw, (u8 *)(cur->a2e_msg), ((sizeof(struct lmac_msg) + cur->a2e_msg->param_len + 3)/4) *4, bl_hw->plat->io_port + CTRL_PORT);
|
|
+ kfree(cur->a2e_msg);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*we are in bootom handler, so we just use spin_lock*/
|
|
+ spin_lock(&bl_hw->cmd_lock);
|
|
+ bl_hw->cmd_sent = false;
|
|
+ spin_unlock(&bl_hw->cmd_lock);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+void main_wq_hdlr(struct work_struct *work)
|
|
+{
|
|
+ struct bl_hw *bl_hw = container_of(work, struct bl_hw, main_work);
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_main_process(bl_hw);
|
|
+}
|
|
+
|
|
+static int bl_upload_hdl(struct bl_hw *bl_hw)
|
|
+{
|
|
+ u8 rd_bitmap_l = 0;
|
|
+ u8 rd_bitmap_u = 0;
|
|
+ u32 len_reg_l = 0;
|
|
+ u32 len_reg_u = 0;
|
|
+ u32 bitmap;
|
|
+ u32 rx_len;
|
|
+ u32 port = CTRL_PORT;
|
|
+ int ret = 0;
|
|
+ struct sk_buff *skb;
|
|
+ u8 cr;
|
|
+ u32 pind;
|
|
+ u8 *curr_ptr;
|
|
+ u32 pkt_len;
|
|
+ u32 pkt_type;
|
|
+ u32 hw_idx;
|
|
+ u32 reserved;
|
|
+ u32 cmd53_port = 0;
|
|
+ int avail_port_num = 0;
|
|
+ u32 rd_bitmap = 0;
|
|
+ bool rd_twice = false;
|
|
+// struct timespec ts;
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,20,0))
|
|
+ struct timespec64 ts;
|
|
+ ktime_get_boottime_ts64(&ts);
|
|
+ return ((u64)ts.tv_sec*1000000) + ts.tv_nsec / 1000;
|
|
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
|
|
+ struct timespec ts;
|
|
+ get_monotonic_boottime(&ts);
|
|
+ return ((u64)ts.tv_sec*1000000) + ts.tv_nsec / 1000;
|
|
+#else
|
|
+ struct timeval tv;
|
|
+ do_gettimeofday(&tv);
|
|
+ return ((u64)tv.tv_sec*1000000) + tv.tv_usec;
|
|
+#endif
|
|
+ struct rtc_time tm;
|
|
+ long start;
|
|
+ long end;
|
|
+
|
|
+ struct bl_plat *bl_plat = bl_hw->plat;
|
|
+ struct bl_device *bl_device = (struct bl_device *)bl_plat->priv;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+#define TEST_REBOOT_TIME
|
|
+
|
|
+#ifdef TEST_REBOOT_TIME
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
|
|
+ ktime_get_real_ts64(&ts);
|
|
+ rtc_time64_to_tm(ts.tv_sec,&tm);
|
|
+#else
|
|
+ getnstimeofday(&ts);
|
|
+ rtc_time_to_tm(ts.tv_sec,&tm);
|
|
+#endif
|
|
+ start = ts.tv_nsec/1000;
|
|
+ //printk("[ST]:%d-%d-%d %d:%d:%d %ld\n", tm.tm_year+1900,tm.tm_mon, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec, start);
|
|
+#endif
|
|
+
|
|
+ bl_hw->mpa_rx_data.pkt_cnt = 0;
|
|
+
|
|
+ rd_bitmap_l = bl_plat->mp_regs[bl_device->reg->rd_bitmap_l];
|
|
+ rd_bitmap_u = bl_plat->mp_regs[bl_device->reg->rd_bitmap_u];
|
|
+
|
|
+ bitmap = rd_bitmap_l;
|
|
+ bitmap |= rd_bitmap_u << 8;
|
|
+
|
|
+ bl_plat->mp_rd_bitmap = bitmap;
|
|
+ rd_bitmap = bitmap;
|
|
+
|
|
+ BL_DBG("int: UPLD: rd_bitmap=0x%x, lost_int_flag=%d\n", bl_plat->mp_rd_bitmap, bl_hw->lost_int_flag);
|
|
+
|
|
+ rd_bitmap &= 0xFFFFFFFE;
|
|
+ while(rd_bitmap) {
|
|
+ rd_bitmap = rd_bitmap & (rd_bitmap-1);
|
|
+ avail_port_num++;
|
|
+ }
|
|
+
|
|
+ //printk("rd_bitmap=0x%x, available rd port nums=%d,------------------------------- ", bl_plat->mp_rd_bitmap, avail_port_num);
|
|
+ if(avail_port_num > 7) {
|
|
+ //printk("will read twice\n");
|
|
+ rd_twice = true;
|
|
+ } else {
|
|
+ //printk("\n");
|
|
+ }
|
|
+
|
|
+TWICE:
|
|
+
|
|
+ while(true) {
|
|
+ ret = bl_get_rd_port(bl_hw, &port);
|
|
+ if (ret) {
|
|
+ //printk("info: no more rd_port available\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+
|
|
+ len_reg_l = bl_plat->mp_regs[bl_device->reg->rd_len_p0_l + (port << 1)];
|
|
+ len_reg_u = bl_plat->mp_regs[bl_device->reg->rd_len_p0_u + (port << 1)];
|
|
+ rx_len = (len_reg_u << 8) + len_reg_l;
|
|
+
|
|
+ //printk("rd_port: port=0x%x, rx_len=%d\n", port, rx_len);
|
|
+
|
|
+ if (rx_len < sizeof(struct sdio_hdr)) {
|
|
+ printk("Invaild rx_len\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ rx_len = ((rx_len + BL_SDIO_BLOCK_SIZE - 1)/BL_SDIO_BLOCK_SIZE) * BL_SDIO_BLOCK_SIZE;
|
|
+
|
|
+
|
|
+ BL_DBG("after round: rx_len=%d, port=%d\n", rx_len, port);
|
|
+
|
|
+ skb = dev_alloc_skb(rx_len);
|
|
+ if (!skb) {
|
|
+ printk("%s: failed to alloc skb", __func__);
|
|
+ return -1;
|
|
+ } else {
|
|
+ //printk("alloc skb: %p\n", skb);
|
|
+ }
|
|
+
|
|
+ if(port == CTRL_PORT) {
|
|
+ /*port0 is not aggr port, ignore */
|
|
+ //cmd53_port = (bl_hw->plat->io_port | BL_SDIO_MPA_ADDR_BASE |
|
|
+ // (1 << 4)) + 0;
|
|
+ //printk("port0: cmd53_port=0x%08x, rx_len=%d\n", cmd53_port, rx_len);
|
|
+
|
|
+ //ret = bl_read_data_sync(bl_hw, skb->data, rx_len, cmd53_port);
|
|
+ ret = bl_read_data_sync(bl_hw, skb->data, rx_len, bl_hw->plat->io_port + port);
|
|
+ if(ret) {
|
|
+ printk("read data failed\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ pkt_len = le16_to_cpu(*(__le16 *) (skb->data + 0));
|
|
+ pkt_type = le16_to_cpu(*(__le16 *) (skb->data + 2));
|
|
+ hw_idx = le16_to_cpu(*(__le16 *) (skb->data + 4));
|
|
+ reserved = le16_to_cpu(*(__le16 *) (skb->data + 6));
|
|
+ skb_put(skb, rx_len);
|
|
+ if(bl_decode_rx_packet(bl_hw, skb, pkt_type, hw_idx, port)) {
|
|
+ printk("bl_rx_process error\n");
|
|
+ goto term_cmd;
|
|
+ }
|
|
+ #ifdef TEST_REBOOT_TIME
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
|
|
+ ktime_get_real_ts64(&ts);
|
|
+ rtc_time64_to_tm(ts.tv_sec,&tm);
|
|
+#else
|
|
+ getnstimeofday(&ts);
|
|
+ rtc_time_to_tm(ts.tv_sec,&tm);
|
|
+#endif
|
|
+ end = ts.tv_nsec/1000;
|
|
+ if(end-start >10000)
|
|
+ printk("Rx Msg over 10ms: start=%ld, end=%ld\n", start, end);
|
|
+ //printk("[E0]:%d-%d-%d %d:%d:%d %0.4d\n",tm.tm_year+1900,tm.tm_mon, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec, txc.time.tv_usec);
|
|
+ #endif
|
|
+
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ //printk("start to record multi port: port=0x%x\n", port);
|
|
+
|
|
+ bl_hw->mpa_rx_data.buf_len += rx_len;
|
|
+ if(!bl_hw->mpa_rx_data.pkt_cnt)
|
|
+ bl_hw->mpa_rx_data.start_port = port;
|
|
+
|
|
+ if(bl_hw->mpa_rx_data.start_port <= port) {
|
|
+ bl_hw->mpa_rx_data.ports |= (1 << bl_hw->mpa_rx_data.pkt_cnt);
|
|
+ } else {
|
|
+ bl_hw->mpa_rx_data.ports |= (1 << (bl_hw->mpa_rx_data.pkt_cnt + 1));
|
|
+ }
|
|
+
|
|
+ bl_hw->mpa_rx_data.buf_arr[bl_hw->mpa_rx_data.pkt_cnt] = skb;
|
|
+ bl_hw->mpa_rx_data.len_arr[bl_hw->mpa_rx_data.pkt_cnt] = rx_len;
|
|
+ bl_hw->mpa_rx_data.mp_rd_port[bl_hw->mpa_rx_data.pkt_cnt] = port;
|
|
+ bl_hw->mpa_rx_data.pkt_cnt++;
|
|
+ if(bl_hw->mpa_rx_data.pkt_cnt==7 && rd_twice == true) {
|
|
+ //printk("pkt_cnts > 7, other port will read next time, ports=0x%x, cnt=%d\n", bl_hw->mpa_rx_data.ports, bl_hw->mpa_rx_data.pkt_cnt);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(bl_hw->mpa_rx_data.pkt_cnt != 0) {
|
|
+
|
|
+ //printk("ports=0x%x, pkt_cnt=%d\n", bl_hw->mpa_rx_data.ports, bl_hw->mpa_rx_data.pkt_cnt);
|
|
+
|
|
+ memset(bl_hw->mpa_rx_data.buf, 0, BL_RX_DATA_BUF_SIZE_16K);
|
|
+
|
|
+ /*recv packet*/
|
|
+ cmd53_port = (bl_hw->plat->io_port | BL_SDIO_MPA_ADDR_BASE |
|
|
+ (bl_hw->mpa_rx_data.ports << 4)) + bl_hw->mpa_rx_data.start_port;
|
|
+
|
|
+ //printk("mpa_buf_len=%d, cmd53_port=0x%08x, mpa_rx_data.buf=%p\n", bl_hw->mpa_rx_data.buf_len, cmd53_port, bl_hw->mpa_rx_data.buf);
|
|
+
|
|
+ ret = bl_read_data_sync(bl_hw, bl_hw->mpa_rx_data.buf, bl_hw->mpa_rx_data.buf_len, cmd53_port);
|
|
+ if(ret)
|
|
+ printk("bl_read_data_sync failed, ret=%d\n", ret);
|
|
+
|
|
+ curr_ptr = bl_hw->mpa_rx_data.buf;
|
|
+
|
|
+ //printk("mpa_rx_data.buf: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x \n",
|
|
+ // curr_ptr[0],
|
|
+ // curr_ptr[1],
|
|
+ // curr_ptr[2],
|
|
+ // curr_ptr[3],
|
|
+ // curr_ptr[4],
|
|
+ // curr_ptr[5],
|
|
+ // curr_ptr[6],
|
|
+ // curr_ptr[7]
|
|
+ // );
|
|
+
|
|
+ for(pind = 0; pind < bl_hw->mpa_rx_data.pkt_cnt; pind++) {
|
|
+
|
|
+ memcpy((u8 *)((struct sk_buff *)(bl_hw->mpa_rx_data.buf_arr[pind])->data), curr_ptr, bl_hw->mpa_rx_data.len_arr[pind]);
|
|
+
|
|
+ pkt_len = le16_to_cpu(*(__le16 *) (((bl_hw->mpa_rx_data.buf_arr[pind])->data + 0)));
|
|
+ pkt_type = le16_to_cpu(*(__le16 *) (((bl_hw->mpa_rx_data.buf_arr[pind])->data + 2)));
|
|
+ hw_idx = le16_to_cpu(*(__le16 *) (((bl_hw->mpa_rx_data.buf_arr[pind])->data + 4)));
|
|
+ reserved = le16_to_cpu(*(__le16 *) (((bl_hw->mpa_rx_data.buf_arr[pind])->data + 6)));
|
|
+
|
|
+ BL_DBG("pkt_len=%d, pkt_type=%d, hw_idx=%d, reserved=%d\n", pkt_len, pkt_type, hw_idx, reserved);
|
|
+
|
|
+ bl_hw->msg_idx = reserved;
|
|
+ skb_put(bl_hw->mpa_rx_data.buf_arr[pind], pkt_len + sizeof(struct sdio_hdr) + reserved);
|
|
+ if(bl_decode_rx_packet(bl_hw, (struct sk_buff *)(bl_hw->mpa_rx_data.buf_arr[pind]), pkt_type, hw_idx, bl_hw->mpa_rx_data.mp_rd_port[pind])) {
|
|
+ printk("bl_rx_process error\n");
|
|
+ goto term_cmd;
|
|
+ }
|
|
+
|
|
+ curr_ptr += bl_hw->mpa_rx_data.len_arr[pind];
|
|
+ }
|
|
+
|
|
+ //printk("clear pkt_cnt and ports, buf_len\n");
|
|
+ bl_hw->mpa_rx_data.pkt_cnt = 0;
|
|
+ bl_hw->mpa_rx_data.ports = 0;
|
|
+ bl_hw->mpa_rx_data.buf_len = 0;
|
|
+ #ifdef TEST_REBOOT_TIME
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
|
|
+ ktime_get_real_ts64(&ts);
|
|
+ rtc_time64_to_tm(ts.tv_sec,&tm);
|
|
+#else
|
|
+ getnstimeofday(&ts);
|
|
+ rtc_time_to_tm(ts.tv_sec,&tm);
|
|
+#endif
|
|
+ end = ts.tv_nsec/1000;
|
|
+ if(end-start >10000)
|
|
+ printk("Rx Data over 10ms: start=%ld, end=%ld\n", start, end);
|
|
+ //printk("[ED]:%d-%d-%d %d:%d:%d %0.4d\n",tm.tm_year+1900,tm.tm_mon, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec, txc.time.tv_usec);
|
|
+ #endif
|
|
+ }
|
|
+
|
|
+ if(rd_twice == true) {
|
|
+ //printk("start read twice\n");
|
|
+ rd_twice = false;
|
|
+ goto TWICE;
|
|
+ }
|
|
+
|
|
+
|
|
+ return 0;
|
|
+
|
|
+ term_cmd:
|
|
+ /* terminate cmd */
|
|
+ if (bl_read_reg(bl_hw, CONFIGURATION_REG, &cr))
|
|
+ printk("read CFG reg failed\n");
|
|
+ else
|
|
+ printk("info: CFG reg val = %d\n", cr);
|
|
+
|
|
+ if (bl_write_reg(bl_hw, CONFIGURATION_REG, (cr | 0x04)))
|
|
+ printk("write CFG reg failed\n");
|
|
+ else
|
|
+ printk("info: write success\n");
|
|
+
|
|
+ if (bl_read_reg(bl_hw, CONFIGURATION_REG, &cr))
|
|
+ printk("read CFG reg failed\n");
|
|
+ else
|
|
+ printk("info: CFG reg val =%x\n", cr);
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static int bl_download_hdl(struct bl_hw *bl_hw)
|
|
+{
|
|
+ u8 wr_bitmap_l = 0;
|
|
+ u8 wr_bitmap_u = 0;
|
|
+ u32 bitmap;
|
|
+
|
|
+ struct bl_plat *bl_plat = bl_hw->plat;
|
|
+ struct bl_device *bl_device = (struct bl_device *)bl_plat->priv;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ //bl_read_reg(bl_hw, bl_device->reg->wr_bitmap_l, &wr_bitmap_l);
|
|
+ //bl_read_reg(bl_hw, bl_device->reg->wr_bitmap_u, &wr_bitmap_u);
|
|
+ //bitmap = wr_bitmap_l;
|
|
+ //bitmap |= wr_bitmap_u << 8;
|
|
+
|
|
+ wr_bitmap_l = bl_plat->mp_regs[bl_device->reg->wr_bitmap_l];
|
|
+ wr_bitmap_u = bl_plat->mp_regs[bl_device->reg->wr_bitmap_u];
|
|
+ bitmap = wr_bitmap_l;
|
|
+ bitmap |= wr_bitmap_u << 8;
|
|
+
|
|
+ bl_plat->mp_wr_bitmap = bitmap;
|
|
+
|
|
+ BL_DBG("int: DNLD: wr_bitmap=0x%x\n", bl_plat->mp_wr_bitmap);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int bl_main_process(struct bl_hw *bl_hw)
|
|
+{
|
|
+ u8 sdio_ireg;
|
|
+ int ret = 0;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /*we know irq is enable, so here use spin_lock_irq*/
|
|
+ spin_lock_irq(&bl_hw->main_proc_lock);
|
|
+ /* Check if already processing */
|
|
+ if (bl_hw->bl_processing) {
|
|
+ BL_DBG("bl_main_process: more_task_flag = true\n");
|
|
+ bl_hw->more_task_flag = true;
|
|
+ spin_unlock_irq(&bl_hw->main_proc_lock);
|
|
+ goto exit_main_proc;
|
|
+ } else {
|
|
+ bl_hw->bl_processing = true;
|
|
+ spin_unlock_irq(&bl_hw->main_proc_lock);
|
|
+ }
|
|
+
|
|
+ bl_hw->lost_int_flag = 0;
|
|
+
|
|
+process_start:
|
|
+
|
|
+ spin_lock_irq(&bl_hw->int_lock);
|
|
+ sdio_ireg = bl_hw->int_status;
|
|
+ bl_hw->int_status = 0;
|
|
+ spin_unlock_irq(&bl_hw->int_lock);
|
|
+
|
|
+ /* 1.handle the msg */
|
|
+ if (!list_empty(&bl_hw->cmd_mgr.cmds)) {
|
|
+ if(bl_hw->cmd_sent) {
|
|
+ ret = bl_pending_msg_hdl(bl_hw);
|
|
+ if(ret) {
|
|
+ printk("send msg failed, ret=%d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* 2.handle recv data */
|
|
+ if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
|
|
+ ret = bl_upload_hdl(bl_hw);
|
|
+ if(ret || (bl_hw->resend))
|
|
+ goto exit_main_proc;
|
|
+ }
|
|
+
|
|
+ /* 3. if wr port ready, see if data need to transmit */
|
|
+ if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
|
|
+ ret = bl_download_hdl(bl_hw);
|
|
+ }
|
|
+
|
|
+ /* 4. handle tx data */
|
|
+ BL_DBG("bl_hw->recovery_flag---> %s\n", bl_hw->recovery_flag ? "true":"false");
|
|
+ if(!bl_hw->recovery_flag) {
|
|
+ BL_DBG("bl_main_process: start call bl_hwq_process_all\n");
|
|
+ bl_hwq_process_all(bl_hw);
|
|
+ BL_DBG("bl_main_process: end call bl_hwq_process_all\n");
|
|
+ }
|
|
+
|
|
+ spin_lock_irq(&bl_hw->main_proc_lock);
|
|
+ if (bl_hw->more_task_flag) {
|
|
+ bl_hw->more_task_flag = false;
|
|
+ spin_unlock_irq(&bl_hw->main_proc_lock);
|
|
+ bl_hw->lost_int_flag = 1;
|
|
+ BL_DBG("go to process start\n");
|
|
+ goto process_start;
|
|
+ }
|
|
+ bl_hw->bl_processing = false;
|
|
+ spin_unlock_irq(&bl_hw->main_proc_lock);
|
|
+
|
|
+exit_main_proc:
|
|
+ return ret;
|
|
+}
|
|
diff -Naur /dev/null_irqs.h b/drivers/net/wireless/hflps170/bl_irqs.h
|
|
--- /dev/null_irqs.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_irqs.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,22 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_irqs.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#ifndef _BL_IRQS_H_
|
|
+#define _BL_IRQS_H_
|
|
+
|
|
+#include <linux/interrupt.h>
|
|
+
|
|
+/* IRQ handler to be registered by platform driver */
|
|
+//irqreturn_t bl_irq_hdlr(int irq, void *dev_id);
|
|
+void bl_irq_hdlr(struct sdio_func *func);
|
|
+void bl_task(unsigned long data);
|
|
+void main_wq_hdlr(struct work_struct *work);
|
|
+void bl_queue_main_work(struct bl_hw *bl_hw);
|
|
+
|
|
+#endif /* _BL_IRQS_H_ */
|
|
diff -Naur /dev/null_mod_params.c b/drivers/net/wireless/hflps170/bl_mod_params.c
|
|
--- /dev/null_mod_params.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_mod_params.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,474 @@
|
|
+/**
|
|
+******************************************************************************
|
|
+*
|
|
+* @file bl_mod_params.c
|
|
+*
|
|
+* @brief Set configuration according to modules parameters
|
|
+*
|
|
+* Copyright (C) BouffaloLab 2017-2018
|
|
+*
|
|
+******************************************************************************
|
|
+*/
|
|
+#include <linux/module.h>
|
|
+
|
|
+#include "bl_defs.h"
|
|
+#include "bl_tx.h"
|
|
+#include "hal_desc.h"
|
|
+#include "bl_cfgfile.h"
|
|
+#include "bl_compat.h"
|
|
+
|
|
+#define COMMON_PARAM(name, default_softmac, default_fullmac) \
|
|
+ .name = default_fullmac,
|
|
+#define SOFTMAC_PARAM(name, default)
|
|
+#define FULLMAC_PARAM(name, default) .name = default,
|
|
+
|
|
+struct bl_mod_params bl_mod_params = {
|
|
+ /* common parameters */
|
|
+ COMMON_PARAM(ht_on, true, true)
|
|
+ COMMON_PARAM(vht_on, true, true)
|
|
+ COMMON_PARAM(mcs_map, IEEE80211_VHT_MCS_SUPPORT_0_9, IEEE80211_VHT_MCS_SUPPORT_0_7)
|
|
+ COMMON_PARAM(ldpc_on, false, false)
|
|
+ COMMON_PARAM(vht_stbc, true, true)
|
|
+ COMMON_PARAM(phy_cfg, 2, 2)
|
|
+ COMMON_PARAM(uapsd_timeout, 300, 300)
|
|
+ COMMON_PARAM(ap_uapsd_on, true, true)
|
|
+ COMMON_PARAM(sgi, true, true)
|
|
+ COMMON_PARAM(sgi80, true, true)
|
|
+ COMMON_PARAM(use_2040, 0, 0)
|
|
+ COMMON_PARAM(nss, 1, 1)
|
|
+ COMMON_PARAM(bfmee, true, true)
|
|
+ COMMON_PARAM(bfmer, false, false)
|
|
+ COMMON_PARAM(mesh, false, false)
|
|
+ COMMON_PARAM(murx, true, true)
|
|
+ COMMON_PARAM(mutx, true, true)
|
|
+ COMMON_PARAM(mutx_on, true, true)
|
|
+ COMMON_PARAM(use_80, true, true)
|
|
+ COMMON_PARAM(custregd, false, false)
|
|
+ COMMON_PARAM(roc_dur_max, 500, 500)
|
|
+ COMMON_PARAM(listen_itv, 0, 0)
|
|
+ COMMON_PARAM(listen_bcmc, true, true)
|
|
+ COMMON_PARAM(lp_clk_ppm, 20, 20)
|
|
+ COMMON_PARAM(ps_on, false, false)
|
|
+ COMMON_PARAM(tx_lft, BL_TX_LIFETIME_MS, BL_TX_LIFETIME_MS)
|
|
+ COMMON_PARAM(amsdu_maxnb, NX_TX_PAYLOAD_MAX, NX_TX_PAYLOAD_MAX)
|
|
+ // By default, only enable UAPSD for Voice queue (see IEEE80211_DEFAULT_UAPSD_QUEUE comment)
|
|
+ COMMON_PARAM(uapsd_queues, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
|
|
+ COMMON_PARAM(tdls, true, true)
|
|
+};
|
|
+
|
|
+module_param_named(ht_on, bl_mod_params.ht_on, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(ht_on, "Enable HT (Default: 1)");
|
|
+
|
|
+module_param_named(vht_on, bl_mod_params.vht_on, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(vht_on, "Enable VHT (Default: 1)");
|
|
+
|
|
+module_param_named(mcs_map, bl_mod_params.mcs_map, int, S_IRUGO);
|
|
+MODULE_PARM_DESC(mcs_map, "VHT MCS map value 0: MCS0_7, 1: MCS0_8, 2: MCS0_9"
|
|
+ " (Default: 0)");
|
|
+
|
|
+module_param_named(amsdu_maxnb, bl_mod_params.amsdu_maxnb, int, S_IRUGO | S_IWUSR);
|
|
+MODULE_PARM_DESC(amsdu_maxnb, "Maximum number of MSDUs inside an A-MSDU in TX: (Default: NX_TX_PAYLOAD_MAX)");
|
|
+
|
|
+module_param_named(ps_on, bl_mod_params.ps_on, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(ps_on, "Enable PowerSaving (Default: 1-Enabled)");
|
|
+
|
|
+module_param_named(tx_lft, bl_mod_params.tx_lft, int, 0644);
|
|
+MODULE_PARM_DESC(tx_lft, "Tx lifetime (ms) - setting it to 0 disables retries "
|
|
+ "(Default: "__stringify(BL_TX_LIFETIME_MS)")");
|
|
+
|
|
+module_param_named(ldpc_on, bl_mod_params.ldpc_on, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(ldpc_on, "Enable LDPC (Default: 1)");
|
|
+
|
|
+module_param_named(vht_stbc, bl_mod_params.vht_stbc, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(vht_stbc, "Enable VHT STBC in RX (Default: 1)");
|
|
+
|
|
+module_param_named(phycfg, bl_mod_params.phy_cfg, int, S_IRUGO);
|
|
+MODULE_PARM_DESC(phycfg,
|
|
+ "0 <= phycfg <= 5 : RF Channel Conf (Default: 2(C0-A1-B2))");
|
|
+
|
|
+module_param_named(uapsd_timeout, bl_mod_params.uapsd_timeout, int, S_IRUGO | S_IWUSR);
|
|
+MODULE_PARM_DESC(uapsd_timeout,
|
|
+ "UAPSD Timer timeout, in ms (Default: 300). If 0, UAPSD is disabled");
|
|
+
|
|
+module_param_named(uapsd_queues, bl_mod_params.uapsd_queues, int, S_IRUGO | S_IWUSR);
|
|
+MODULE_PARM_DESC(uapsd_queues, "UAPSD Queues, integer value, must be seen as a bitfield\n"
|
|
+ " Bit 0 = VO\n"
|
|
+ " Bit 1 = VI\n"
|
|
+ " Bit 2 = BK\n"
|
|
+ " Bit 3 = BE\n"
|
|
+ " -> uapsd_queues=7 will enable uapsd for VO, VI and BK queues");
|
|
+
|
|
+module_param_named(ap_uapsd_on, bl_mod_params.ap_uapsd_on, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(ap_uapsd_on, "Enable UAPSD in AP mode (Default: 1)");
|
|
+
|
|
+module_param_named(sgi, bl_mod_params.sgi, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(sgi, "Advertise Short Guard Interval support (Default: 1)");
|
|
+
|
|
+module_param_named(sgi80, bl_mod_params.sgi80, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(sgi80, "Advertise Short Guard Interval support for 80MHz (Default: 1)");
|
|
+
|
|
+module_param_named(use_2040, bl_mod_params.use_2040, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(use_2040, "Use tweaked 20-40MHz mode (Default: 1)");
|
|
+
|
|
+module_param_named(use_80, bl_mod_params.use_80, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(use_80, "Enable 80MHz (Default: 1)");
|
|
+
|
|
+module_param_named(custregd, bl_mod_params.custregd, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(custregd,
|
|
+ "Use permissive custom regulatory rules (for testing ONLY) (Default: 0)");
|
|
+
|
|
+module_param_named(nss, bl_mod_params.nss, int, S_IRUGO);
|
|
+MODULE_PARM_DESC(nss, "1 <= nss <= 2 : Supported number of Spatial Streams (Default: 1)");
|
|
+
|
|
+module_param_named(bfmee, bl_mod_params.bfmee, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(bfmee, "Enable Beamformee Capability (Default: 1-Enabled)");
|
|
+
|
|
+module_param_named(bfmer, bl_mod_params.bfmer, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(bfmer, "Enable Beamformer Capability (Default: 0-Disabled)");
|
|
+
|
|
+module_param_named(mesh, bl_mod_params.mesh, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(mesh, "Enable Meshing Capability (Default: 0-Disabled)");
|
|
+
|
|
+module_param_named(murx, bl_mod_params.murx, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(murx, "Enable MU-MIMO RX Capability (Default: 1-Enabled)");
|
|
+
|
|
+module_param_named(mutx, bl_mod_params.mutx, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(mutx, "Enable MU-MIMO TX Capability (Default: 1-Enabled)");
|
|
+
|
|
+module_param_named(mutx_on, bl_mod_params.mutx_on, bool, S_IRUGO | S_IWUSR);
|
|
+MODULE_PARM_DESC(mutx_on, "Enable MU-MIMO transmissions (Default: 1-Enabled)");
|
|
+
|
|
+module_param_named(roc_dur_max, bl_mod_params.roc_dur_max, int, S_IRUGO);
|
|
+MODULE_PARM_DESC(roc_dur_max, "Maximum Remain on Channel duration");
|
|
+
|
|
+module_param_named(listen_itv, bl_mod_params.listen_itv, int, S_IRUGO);
|
|
+MODULE_PARM_DESC(listen_itv, "Maximum listen interval");
|
|
+
|
|
+module_param_named(listen_bcmc, bl_mod_params.listen_bcmc, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(listen_bcmc, "Wait for BC/MC traffic following DTIM beacon");
|
|
+
|
|
+module_param_named(lp_clk_ppm, bl_mod_params.lp_clk_ppm, int, S_IRUGO);
|
|
+MODULE_PARM_DESC(lp_clk_ppm, "Low Power Clock accuracy of the local device");
|
|
+
|
|
+module_param_named(tdls, bl_mod_params.tdls, bool, S_IRUGO);
|
|
+MODULE_PARM_DESC(tdls, "Enable TDLS (Default: 1-Enabled)");
|
|
+
|
|
+/* Regulatory rules */
|
|
+static const struct ieee80211_regdomain bl_regdom = {
|
|
+ .n_reg_rules = 3,
|
|
+ .alpha2 = "99",
|
|
+ .reg_rules = {
|
|
+ REG_RULE(2412 - 10, 2472 + 10, 40, 0, 1000, 0),
|
|
+ REG_RULE(2484 - 10, 2484 + 10, 20, 0, 1000, 0),
|
|
+ REG_RULE(5150 - 10, 5850 + 10, 80, 0, 1000, 0),
|
|
+ }
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+/**
|
|
+ * Do some sanity check
|
|
+ *
|
|
+ */
|
|
+static int bl_check_fw_hw_feature(struct bl_hw *bl_hw,
|
|
+ struct wiphy *wiphy)
|
|
+{
|
|
+ u32_l sys_feat = bl_hw->version_cfm.features;
|
|
+ u32_l phy_feat = bl_hw->version_cfm.version_phy_1;
|
|
+
|
|
+ if (!(sys_feat & BIT(MM_FEAT_UMAC_BIT))) {
|
|
+ wiphy_err(wiphy,
|
|
+ "Loading softmac firmware with fullmac driver\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (!(sys_feat & BIT(MM_FEAT_VHT_BIT))) {
|
|
+ bl_hw->mod_params->vht_on = false;
|
|
+ }
|
|
+
|
|
+ if (!(sys_feat & BIT(MM_FEAT_PS_BIT))) {
|
|
+ bl_hw->mod_params->ps_on = false;
|
|
+ }
|
|
+
|
|
+ /* AMSDU (non)support implies different shared structure definition
|
|
+ so insure that fw and drv have consistent compilation option */
|
|
+ if (sys_feat & BIT(MM_FEAT_AMSDU_BIT)) {
|
|
+#ifndef CONFIG_BL_SPLIT_TX_BUF
|
|
+ wiphy_err(wiphy,
|
|
+ "AMSDU enabled in firmware but support not compiled in driver\n");
|
|
+ return -1;
|
|
+#else
|
|
+ if (bl_hw->mod_params->amsdu_maxnb > NX_TX_PAYLOAD_MAX)
|
|
+ bl_hw->mod_params->amsdu_maxnb = NX_TX_PAYLOAD_MAX;
|
|
+#endif /* CONFIG_BL_SPLIT_TX_BUF */
|
|
+ } else {
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ wiphy_err(wiphy,
|
|
+ "AMSDU disabled in firmware but support compiled in driver\n");
|
|
+ return -1;
|
|
+#endif /* CONFIG_BL_SPLIT_TX_BUF */
|
|
+ }
|
|
+
|
|
+ if (!(sys_feat & BIT(MM_FEAT_UAPSD_BIT))) {
|
|
+ bl_hw->mod_params->uapsd_timeout = 0;
|
|
+ }
|
|
+
|
|
+ if (sys_feat & BIT(MM_FEAT_WAPI_BIT)) {
|
|
+ bl_enable_wapi(bl_hw);
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ if (sys_feat & BIT(MM_FEAT_MFP_BIT)) {
|
|
+ bl_enable_mfp(bl_hw);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+#define QUEUE_NAME "Broadcast/Multicast queue "
|
|
+
|
|
+ if (sys_feat & BIT(MM_FEAT_BCN_BIT)) {
|
|
+#if NX_TXQ_CNT == 4
|
|
+ wiphy_err(wiphy, QUEUE_NAME
|
|
+ "enabled in firmware but support not compiled in driver\n");
|
|
+ return -1;
|
|
+#endif /* NX_TXQ_CNT == 4 */
|
|
+ } else {
|
|
+#if NX_TXQ_CNT == 5
|
|
+ wiphy_err(wiphy, QUEUE_NAME
|
|
+ "disabled in firmware but support compiled in driver\n");
|
|
+ return -1;
|
|
+#endif /* NX_TXQ_CNT == 5 */
|
|
+ }
|
|
+#undef QUEUE_NAME
|
|
+
|
|
+#ifdef CONFIG_BL_RADAR
|
|
+ if (sys_feat & BIT(MM_FEAT_RADAR_BIT)) {
|
|
+ /* Enable combination with radar detection */
|
|
+ wiphy->n_iface_combinations++;
|
|
+ }
|
|
+#endif /* CONFIG_BL_RADAR */
|
|
+
|
|
+#ifndef CONFIG_BL_SDM
|
|
+ switch (__MDM_PHYCFG_FROM_VERS(phy_feat)) {
|
|
+ case MDM_PHY_CONFIG_TRIDENT:
|
|
+ case MDM_PHY_CONFIG_ELMA:
|
|
+ bl_hw->mod_params->nss = 1;
|
|
+ break;
|
|
+ case MDM_PHY_CONFIG_KARST:
|
|
+ {
|
|
+ int nss_supp = (phy_feat & MDM_NSS_MASK) >> MDM_NSS_LSB;
|
|
+ if (bl_hw->mod_params->nss > nss_supp)
|
|
+ bl_hw->mod_params->nss = nss_supp;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ WARN_ON(1);
|
|
+ break;
|
|
+ }
|
|
+#endif /* CONFIG_BL_SDM */
|
|
+
|
|
+ if (bl_hw->mod_params->nss < 1 || bl_hw->mod_params->nss > 2)
|
|
+ bl_hw->mod_params->nss = 1;
|
|
+
|
|
+ wiphy_info(wiphy, "PHY features: [NSS=%d][CHBW=%d]%s\n",
|
|
+ bl_hw->mod_params->nss,
|
|
+ 20 * (1 << ((phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB)),
|
|
+ bl_hw->mod_params->ldpc_on ? "[LDPC]" : "");
|
|
+
|
|
+#define PRINT_BL_FEAT(feat) \
|
|
+ (sys_feat & BIT(MM_FEAT_##feat##_BIT) ? "["#feat"]" : "")
|
|
+
|
|
+ wiphy_info(wiphy, "FW features: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
|
+ PRINT_BL_FEAT(BCN),
|
|
+ PRINT_BL_FEAT(AUTOBCN),
|
|
+ PRINT_BL_FEAT(HWSCAN),
|
|
+ PRINT_BL_FEAT(CMON),
|
|
+ PRINT_BL_FEAT(MROLE),
|
|
+ PRINT_BL_FEAT(RADAR),
|
|
+ PRINT_BL_FEAT(PS),
|
|
+ PRINT_BL_FEAT(UAPSD),
|
|
+ PRINT_BL_FEAT(DPSM),
|
|
+ PRINT_BL_FEAT(AMPDU),
|
|
+ PRINT_BL_FEAT(AMSDU),
|
|
+ PRINT_BL_FEAT(CHNL_CTXT),
|
|
+ PRINT_BL_FEAT(REORD),
|
|
+ PRINT_BL_FEAT(UMAC),
|
|
+ PRINT_BL_FEAT(VHT),
|
|
+ PRINT_BL_FEAT(WAPI),
|
|
+ PRINT_BL_FEAT(MFP));
|
|
+#undef PRINT_BL_FEAT
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+int bl_handle_dynparams(struct bl_hw *bl_hw, struct wiphy *wiphy)
|
|
+{
|
|
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
|
|
+#ifndef CONFIG_BL_SDM
|
|
+ u32 mdm_phy_cfg;
|
|
+#endif
|
|
+ int i, ret;
|
|
+ int nss;
|
|
+ int mcs_map;
|
|
+
|
|
+ ret = bl_check_fw_hw_feature(bl_hw, wiphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* FULLMAC specific parameters */
|
|
+ wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS;
|
|
+
|
|
+ if (bl_hw->mod_params->tdls) {
|
|
+ /* TDLS support */
|
|
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ /* TDLS external setup support */
|
|
+ wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ if (bl_hw->mod_params->ap_uapsd_on)
|
|
+ wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
|
|
+
|
|
+ if (bl_hw->mod_params->phy_cfg < 0 || bl_hw->mod_params->phy_cfg > 5)
|
|
+ bl_hw->mod_params->phy_cfg = 2;
|
|
+
|
|
+ if (bl_hw->mod_params->mcs_map < 0 || bl_hw->mod_params->mcs_map > 2)
|
|
+ bl_hw->mod_params->mcs_map = 0;
|
|
+
|
|
+#ifndef CONFIG_BL_SDM
|
|
+ mdm_phy_cfg = __MDM_PHYCFG_FROM_VERS(bl_hw->version_cfm.version_phy_1);
|
|
+ if (mdm_phy_cfg == MDM_PHY_CONFIG_TRIDENT) {
|
|
+ struct bl_phy_conf_file phy_conf;
|
|
+ // Retrieve the Trident configuration
|
|
+ bl_parse_phy_configfile(bl_hw, BL_PHY_CONFIG_TRD_NAME, &phy_conf);
|
|
+ memcpy(&bl_hw->phy_config, &phy_conf.trd, sizeof(phy_conf.trd));
|
|
+ } else if (mdm_phy_cfg == MDM_PHY_CONFIG_ELMA) {
|
|
+ } else if (mdm_phy_cfg == MDM_PHY_CONFIG_KARST) {
|
|
+ struct bl_phy_conf_file phy_conf;
|
|
+ // We use the NSS parameter as is
|
|
+ // Retrieve the Karst configuration
|
|
+ bl_parse_phy_configfile(bl_hw, BL_PHY_CONFIG_KARST_NAME, &phy_conf);
|
|
+
|
|
+ memcpy(&bl_hw->phy_config, &phy_conf.karst, sizeof(phy_conf.karst));
|
|
+ } else {
|
|
+ WARN_ON(1);
|
|
+ }
|
|
+#endif /* CONFIG_BL_SDM */
|
|
+
|
|
+ nss = bl_hw->mod_params->nss;
|
|
+
|
|
+ /* VHT capabilities */
|
|
+ /*
|
|
+ * MCS map:
|
|
+ * This capabilities are filled according to the mcs_map module parameter.
|
|
+ * However currently we have some limitations due to FPGA clock constraints
|
|
+ * that prevent always using the range of MCS that is defined by the
|
|
+ * parameter:
|
|
+ * - in RX, 2SS, we support up to MCS7
|
|
+ * - in TX, 2SS, we support up to MCS8
|
|
+ */
|
|
+ mcs_map = bl_hw->mod_params->mcs_map;
|
|
+ //band_5GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
|
|
+ for (i = 0; i < nss; i++) {
|
|
+ band_2GHz->ht_cap.mcs.rx_mask[i] = 0xFF;
|
|
+ //band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
+ mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
|
|
+ }
|
|
+ for (; i < 8; i++) {
|
|
+ //band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
|
|
+ //IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
+ }
|
|
+ mcs_map = bl_hw->mod_params->mcs_map;
|
|
+ //band_5GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
|
|
+ for (i = 0; i < nss; i++) {
|
|
+ //band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
+ mcs_map = min_t(int, bl_hw->mod_params->mcs_map,
|
|
+ IEEE80211_VHT_MCS_SUPPORT_0_8);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * LDPC capability:
|
|
+ * This capability is filled according to the ldpc_on module parameter.
|
|
+ * However currently we have some limitations due to FPGA clock constraints
|
|
+ * that prevent correctly receiving more than MCS4-2SS when using LDPC.
|
|
+ * We therefore disable the LDPC support if 2SS is supported.
|
|
+ */
|
|
+ bl_hw->mod_params->ldpc_on = nss > 1 ? false: bl_hw->mod_params->ldpc_on;
|
|
+
|
|
+ /* HT capabilities */
|
|
+ band_2GHz->ht_cap.cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
|
|
+ if (bl_hw->mod_params->ldpc_on)
|
|
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
|
|
+ if (bl_hw->mod_params->use_2040) {
|
|
+ band_2GHz->ht_cap.mcs.rx_mask[4] = 0x1; /* MCS32 */
|
|
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(135 * nss);
|
|
+ } else {
|
|
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(65 * nss);
|
|
+ }
|
|
+ if (nss > 1)
|
|
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
|
|
+
|
|
+ if (bl_hw->mod_params->sgi) {
|
|
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
|
|
+ if (bl_hw->mod_params->use_2040) {
|
|
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
|
|
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(150 * nss);
|
|
+ } else
|
|
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(72 * nss);
|
|
+ }
|
|
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
|
|
+ printk("--->ht_on=%d\n", bl_hw->mod_params->ht_on);
|
|
+ if (!bl_hw->mod_params->ht_on)
|
|
+ band_2GHz->ht_cap.ht_supported = false;
|
|
+
|
|
+ if (bl_hw->mod_params->custregd) {
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
|
|
+ // Apply custom regulatory. Note that for recent kernel versions we use instead the
|
|
+ // REGULATORY_WIPHY_SELF_MANAGED flag, along with the regulatory_set_wiphy_regd()
|
|
+ // function, that needs to be called after wiphy registration
|
|
+ printk(KERN_CRIT
|
|
+ "\n\n%s: CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES\n\n",
|
|
+ __func__);
|
|
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
|
+ wiphy_apply_custom_regulatory(wiphy, &bl_regdom);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ wiphy->max_scan_ssids = SCAN_SSID_MAX;
|
|
+ wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
|
+ wiphy->support_mbssid = 1;
|
|
+#endif
|
|
+
|
|
+ /**
|
|
+ * adjust caps with lower layers bl_hw->version_cfm
|
|
+ */
|
|
+#ifndef CONFIG_BL_SDM
|
|
+ switch (mdm_phy_cfg) {
|
|
+ case MDM_PHY_CONFIG_TRIDENT:
|
|
+ {
|
|
+ BL_DBG("%s: found Trident phy .. using phy bw tweaks\n", __func__);
|
|
+ bl_hw->use_phy_bw_tweaks = true;
|
|
+ break;
|
|
+ }
|
|
+ case MDM_PHY_CONFIG_ELMA:
|
|
+ BL_DBG("%s: found ELMA phy .. disabling 2.4GHz and greenfield rx\n", __func__);
|
|
+ wiphy->bands[NL80211_BAND_2GHZ] = NULL;
|
|
+ band_2GHz->ht_cap.cap &= ~IEEE80211_HT_CAP_GRN_FLD;
|
|
+ break;
|
|
+ case MDM_PHY_CONFIG_KARST:
|
|
+ {
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ WARN_ON(1);
|
|
+ break;
|
|
+ }
|
|
+#endif /* CONFIG_BL_SDM */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff -Naur /dev/null_mod_params.h b/drivers/net/wireless/hflps170/bl_mod_params.h
|
|
--- /dev/null_mod_params.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_mod_params.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,55 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_mod_params.h
|
|
+ *
|
|
+ * @brief Declaration of module parameters
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_MOD_PARAM_H_
|
|
+#define _BL_MOD_PARAM_H_
|
|
+
|
|
+struct bl_mod_params {
|
|
+ bool ht_on;
|
|
+ bool vht_on;
|
|
+ int mcs_map;
|
|
+ bool ldpc_on;
|
|
+ bool vht_stbc;
|
|
+ int phy_cfg;
|
|
+ int uapsd_timeout;
|
|
+ bool ap_uapsd_on;
|
|
+ bool sgi;
|
|
+ bool sgi80;
|
|
+ bool use_2040;
|
|
+ bool use_80;
|
|
+ bool custregd;
|
|
+ int nss;
|
|
+ bool bfmee;
|
|
+ bool bfmer;
|
|
+ bool mesh;
|
|
+ bool murx;
|
|
+ bool mutx;
|
|
+ bool mutx_on;
|
|
+ unsigned int roc_dur_max;
|
|
+ int listen_itv;
|
|
+ bool listen_bcmc;
|
|
+ int lp_clk_ppm;
|
|
+ bool ps_on;
|
|
+ int tx_lft;
|
|
+ int amsdu_maxnb;
|
|
+ int uapsd_queues;
|
|
+ bool tdls;
|
|
+};
|
|
+
|
|
+extern struct bl_mod_params bl_mod_params;
|
|
+
|
|
+struct bl_hw;
|
|
+int bl_handle_dynparams(struct bl_hw *bl_hw, struct wiphy *wiphy);
|
|
+void bl_enable_wapi(struct bl_hw *bl_hw);
|
|
+void bl_enable_mfp(struct bl_hw *bl_hw);
|
|
+
|
|
+#endif /* _BL_MOD_PARAM_H_ */
|
|
diff -Naur /dev/null_msg_rx.c b/drivers/net/wireless/hflps170/bl_msg_rx.c
|
|
--- /dev/null_msg_rx.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_msg_rx.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,621 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_msg_rx.c
|
|
+ *
|
|
+ * @brief RX function definitions
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#include <linux/version.h>
|
|
+#include "bl_defs.h"
|
|
+#include "bl_tx.h"
|
|
+#include "bl_events.h"
|
|
+#include "bl_debugfs.h"
|
|
+#include "bl_msg_tx.h"
|
|
+#include "bl_compat.h"
|
|
+
|
|
+static int bl_freq_to_idx(struct bl_hw *bl_hw, int freq)
|
|
+{
|
|
+ struct ieee80211_supported_band *sband;
|
|
+ int band, ch, idx = 0;
|
|
+
|
|
+ for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
|
|
+ sband = bl_hw->wiphy->bands[band];
|
|
+ if (!sband) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (ch = 0; ch < sband->n_channels; ch++, idx++) {
|
|
+ if (sband->channels[ch].center_freq == freq) {
|
|
+ goto exit;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ BUG_ON(1);
|
|
+
|
|
+exit:
|
|
+ // Channel has been found, return the index
|
|
+ return idx;
|
|
+}
|
|
+
|
|
+/***************************************************************************
|
|
+ * Messages from MM task
|
|
+ **************************************************************************/
|
|
+static inline int bl_rx_chan_pre_switch_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct bl_vif *bl_vif;
|
|
+ int chan_idx = ((struct mm_channel_pre_switch_ind *)msg->param)->chan_index;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ list_for_each_entry(bl_vif, &bl_hw->vifs, list) {
|
|
+ if (bl_vif->up && bl_vif->ch_index == chan_idx) {
|
|
+ bl_txq_vif_stop(bl_vif, BL_TXQ_STOP_CHAN, bl_hw);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_chan_switch_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct bl_vif *bl_vif;
|
|
+ int chan_idx = ((struct mm_channel_switch_ind *)msg->param)->chan_index;
|
|
+ bool roc = ((struct mm_channel_switch_ind *)msg->param)->roc;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if (!roc) {
|
|
+ list_for_each_entry(bl_vif, &bl_hw->vifs, list) {
|
|
+ if (bl_vif->up && bl_vif->ch_index == chan_idx) {
|
|
+ bl_txq_vif_start(bl_vif, BL_TXQ_STOP_CHAN, bl_hw);
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ /* Retrieve the allocated RoC element */
|
|
+ struct bl_roc_elem *roc_elem = bl_hw->roc_elem;
|
|
+ /* Get VIF on which RoC has been started */
|
|
+ bl_vif = netdev_priv(roc_elem->wdev->netdev);
|
|
+
|
|
+ /* For debug purpose (use ftrace kernel option) */
|
|
+ trace_switch_roc(bl_vif->vif_index);
|
|
+
|
|
+ /* If mgmt_roc is true, remain on channel has been started by ourself */
|
|
+ if (!roc_elem->mgmt_roc) {
|
|
+ /* Inform the host that we have switch on the indicated off-channel */
|
|
+ cfg80211_ready_on_channel(roc_elem->wdev, (u64)(bl_hw->roc_cookie_cnt),
|
|
+ roc_elem->chan, roc_elem->duration, GFP_KERNEL);
|
|
+ }
|
|
+
|
|
+ /* Keep in mind that we have switched on the channel */
|
|
+ roc_elem->on_chan = true;
|
|
+
|
|
+ // Enable traffic on OFF channel queue
|
|
+ bl_txq_offchan_start(bl_hw);
|
|
+ }
|
|
+
|
|
+ bl_hw->cur_chanctx = chan_idx;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_remain_on_channel_exp_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ /* Retrieve the allocated RoC element */
|
|
+ struct bl_roc_elem *roc_elem = bl_hw->roc_elem;
|
|
+ /* Get VIF on which RoC has been started */
|
|
+ struct bl_vif *bl_vif = netdev_priv(roc_elem->wdev->netdev);
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* For debug purpose (use ftrace kernel option) */
|
|
+ trace_roc_exp(bl_vif->vif_index);
|
|
+
|
|
+ /* If mgmt_roc is true, remain on channel has been started by ourself */
|
|
+ /* If RoC has been cancelled before we switched on channel, do not call cfg80211 */
|
|
+ if (!roc_elem->mgmt_roc && roc_elem->on_chan) {
|
|
+ /* Inform the host that off-channel period has expired */
|
|
+ cfg80211_remain_on_channel_expired(roc_elem->wdev, (u64)(bl_hw->roc_cookie_cnt),
|
|
+ roc_elem->chan, GFP_KERNEL);
|
|
+ }
|
|
+
|
|
+ /* De-init offchannel TX queue */
|
|
+ bl_txq_offchan_deinit(bl_vif);
|
|
+
|
|
+ /* Increase the cookie counter cannot be zero */
|
|
+ bl_hw->roc_cookie_cnt++;
|
|
+
|
|
+ if (bl_hw->roc_cookie_cnt == 0) {
|
|
+ bl_hw->roc_cookie_cnt = 1;
|
|
+ }
|
|
+
|
|
+ /* Free the allocated RoC element */
|
|
+ kfree(roc_elem);
|
|
+ bl_hw->roc_elem = NULL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_p2p_vif_ps_change_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ int vif_idx = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->vif_index;
|
|
+ int ps_state = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->ps_state;
|
|
+ struct bl_vif *vif_entry;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ vif_entry = bl_hw->vif_table[vif_idx];
|
|
+
|
|
+ if (vif_entry) {
|
|
+ goto found_vif;
|
|
+ }
|
|
+
|
|
+ goto exit;
|
|
+
|
|
+found_vif:
|
|
+
|
|
+ if (ps_state == MM_PS_MODE_OFF) {
|
|
+ // Start TX queues for provided VIF
|
|
+ bl_txq_vif_start(vif_entry, BL_TXQ_STOP_VIF_PS, bl_hw);
|
|
+ }
|
|
+ else {
|
|
+ // Stop TX queues for provided VIF
|
|
+ bl_txq_vif_stop(vif_entry, BL_TXQ_STOP_VIF_PS, bl_hw);
|
|
+ }
|
|
+
|
|
+exit:
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_channel_survey_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct mm_channel_survey_ind *ind = (struct mm_channel_survey_ind *)msg->param;
|
|
+ // Get the channel index
|
|
+ int idx = bl_freq_to_idx(bl_hw, ind->freq);
|
|
+ // Get the survey
|
|
+ struct bl_survey_info *bl_survey = &bl_hw->survey[idx];
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ // Store the received parameters
|
|
+ bl_survey->chan_time_ms = ind->chan_time_ms;
|
|
+ bl_survey->chan_time_busy_ms = ind->chan_time_busy_ms;
|
|
+ bl_survey->noise_dbm = ind->noise_dbm;
|
|
+ bl_survey->filled = (SURVEY_INFO_TIME |
|
|
+ SURVEY_INFO_TIME_BUSY);
|
|
+
|
|
+ if (ind->noise_dbm != 0) {
|
|
+ bl_survey->filled |= SURVEY_INFO_NOISE_DBM;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_rssi_status_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct mm_rssi_status_ind *ind = (struct mm_rssi_status_ind *)msg->param;
|
|
+ int vif_idx = ind->vif_index;
|
|
+ bool rssi_status = ind->rssi_status;
|
|
+
|
|
+ struct bl_vif *vif_entry;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ vif_entry = bl_hw->vif_table[vif_idx];
|
|
+ if (vif_entry) {
|
|
+ cfg80211_cqm_rssi_notify(vif_entry->ndev,
|
|
+ rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
|
|
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
|
|
+ ind->rssi, GFP_KERNEL);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_csa_counter_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct mm_csa_counter_ind *ind = (struct mm_csa_counter_ind *)msg->param;
|
|
+ struct bl_vif *vif;
|
|
+ bool found = false;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ // Look for VIF entry
|
|
+ list_for_each_entry(vif, &bl_hw->vifs, list) {
|
|
+ if (vif->vif_index == ind->vif_index) {
|
|
+ found=true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (found) {
|
|
+ if (vif->ap.csa)
|
|
+ vif->ap.csa->count = ind->csa_count;
|
|
+ else
|
|
+ netdev_err(vif->ndev, "CSA counter update but no active CSA");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_csa_finish_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct mm_csa_finish_ind *ind = (struct mm_csa_finish_ind *)msg->param;
|
|
+ struct bl_vif *vif;
|
|
+ bool found = false;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ // Look for VIF entry
|
|
+ list_for_each_entry(vif, &bl_hw->vifs, list) {
|
|
+ if (vif->vif_index == ind->vif_index) {
|
|
+ found=true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (found) {
|
|
+ if (BL_VIF_TYPE(vif) == NL80211_IFTYPE_AP ||
|
|
+ BL_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO) {
|
|
+ if (vif->ap.csa) {
|
|
+ vif->ap.csa->status = ind->status;
|
|
+ vif->ap.csa->ch_idx = ind->chan_idx;
|
|
+ schedule_work(&vif->ap.csa->work);
|
|
+ } else
|
|
+ netdev_err(vif->ndev, "CSA finish indication but no active CSA");
|
|
+ } else {
|
|
+ if (ind->status == 0) {
|
|
+ bl_chanctx_unlink(vif);
|
|
+ bl_chanctx_link(vif, ind->chan_idx, NULL);
|
|
+ if (bl_hw->cur_chanctx == ind->chan_idx) {
|
|
+ bl_txq_vif_start(vif, BL_TXQ_STOP_CHAN, bl_hw);
|
|
+ } else
|
|
+ bl_txq_vif_stop(vif, BL_TXQ_STOP_CHAN, bl_hw);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_csa_traffic_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct mm_csa_traffic_ind *ind = (struct mm_csa_traffic_ind *)msg->param;
|
|
+ struct bl_vif *vif;
|
|
+ bool found = false;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ // Look for VIF entry
|
|
+ list_for_each_entry(vif, &bl_hw->vifs, list) {
|
|
+ if (vif->vif_index == ind->vif_index) {
|
|
+ found=true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (found) {
|
|
+ if (ind->enable)
|
|
+ bl_txq_vif_start(vif, BL_TXQ_STOP_CSA, bl_hw);
|
|
+ else
|
|
+ bl_txq_vif_stop(vif, BL_TXQ_STOP_CSA, bl_hw);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_ps_change_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct mm_ps_change_ind *ind = (struct mm_ps_change_ind *)msg->param;
|
|
+ struct bl_sta *sta = &bl_hw->sta_table[ind->sta_idx];
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ netdev_dbg(bl_hw->vif_table[sta->vif_idx]->ndev,
|
|
+ "Sta %d, change PS mode to %s", sta->sta_idx,
|
|
+ ind->ps_state ? "ON" : "OFF");
|
|
+
|
|
+ if (sta->valid) {
|
|
+ bl_ps_bh_enable(bl_hw, sta, ind->ps_state);
|
|
+ } else if (bl_hw->adding_sta) {
|
|
+ sta->ps.active = ind->ps_state ? true : false;
|
|
+ } else {
|
|
+ netdev_err(bl_hw->vif_table[sta->vif_idx]->ndev,
|
|
+ "Ignore PS mode change on invalid sta\n");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static inline int bl_rx_traffic_req_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct mm_traffic_req_ind *ind = (struct mm_traffic_req_ind *)msg->param;
|
|
+ struct bl_sta *sta = &bl_hw->sta_table[ind->sta_idx];
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ netdev_dbg(bl_hw->vif_table[sta->vif_idx]->ndev,
|
|
+ "Sta %d, asked for %d pkt", sta->sta_idx, ind->pkt_cnt);
|
|
+
|
|
+ bl_ps_bh_traffic_req(bl_hw, sta, ind->pkt_cnt,
|
|
+ ind->uapsd ? UAPSD_ID : LEGACY_PS_ID);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***************************************************************************
|
|
+ * Messages from SCANU task
|
|
+ **************************************************************************/
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+static inline int bl_rx_scanu_start_cfm(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if (bl_hw->scan_request) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
|
|
+ struct cfg80211_scan_info info = {
|
|
+ .aborted = false,
|
|
+ };
|
|
+
|
|
+ cfg80211_scan_done(bl_hw->scan_request, &info);
|
|
+#else
|
|
+ cfg80211_scan_done(bl_hw->scan_request, false);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ bl_hw->scan_request = NULL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_scanu_result_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct cfg80211_bss *bss = NULL;
|
|
+ struct ieee80211_channel *chan;
|
|
+ struct scanu_result_ind *ind = (struct scanu_result_ind *)msg->param;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ chan = ieee80211_get_channel(bl_hw->wiphy, ind->center_freq);
|
|
+
|
|
+ if (chan != NULL)
|
|
+ bss = cfg80211_inform_bss_frame(bl_hw->wiphy, chan,
|
|
+ (struct ieee80211_mgmt *)ind->payload,
|
|
+ ind->length, ind->rssi * 100, GFP_ATOMIC);
|
|
+
|
|
+ if (bss != NULL)
|
|
+ cfg80211_put_bss(bl_hw->wiphy, bss);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+/***************************************************************************
|
|
+ * Messages from ME task
|
|
+ **************************************************************************/
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+static inline int bl_rx_me_tkip_mic_failure_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct me_tkip_mic_failure_ind *ind = (struct me_tkip_mic_failure_ind *)msg->param;
|
|
+ struct bl_vif *bl_vif = bl_hw->vif_table[ind->vif_idx];
|
|
+ struct net_device *dev = bl_vif->ndev;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ cfg80211_michael_mic_failure(dev, (u8 *)&ind->addr, (ind->ga?NL80211_KEYTYPE_GROUP:
|
|
+ NL80211_KEYTYPE_PAIRWISE), ind->keyid,
|
|
+ (u8 *)&ind->tsc, GFP_ATOMIC);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_me_tx_credits_update_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct me_tx_credits_update_ind *ind = (struct me_tx_credits_update_ind *)msg->param;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_txq_credit_update(bl_hw, ind->sta_idx, ind->tid, ind->credits);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+/***************************************************************************
|
|
+ * Messages from SM task
|
|
+ **************************************************************************/
|
|
+static inline int bl_rx_sm_connect_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct sm_connect_ind *ind = (struct sm_connect_ind *)msg->param;
|
|
+ struct bl_vif *bl_vif = bl_hw->vif_table[ind->vif_idx];
|
|
+ struct net_device *dev = bl_vif->ndev;
|
|
+ const u8 *req_ie, *rsp_ie;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Retrieve IE addresses and lengths */
|
|
+ req_ie = (const u8 *)ind->assoc_ie_buf;
|
|
+ rsp_ie = req_ie + ind->assoc_req_ie_len;
|
|
+
|
|
+ // Fill-in the AP information
|
|
+ if (ind->status_code == 0)
|
|
+ {
|
|
+ struct bl_sta *sta = &bl_hw->sta_table[ind->ap_idx];
|
|
+ u8 txq_status;
|
|
+ sta->valid = true;
|
|
+ sta->sta_idx = ind->ap_idx;
|
|
+ sta->ch_idx = ind->ch_idx;
|
|
+ sta->vif_idx = ind->vif_idx;
|
|
+ sta->vlan_idx = sta->vif_idx;
|
|
+ sta->qos = ind->qos;
|
|
+ sta->acm = ind->acm;
|
|
+ sta->ps.active = false;
|
|
+ sta->aid = ind->aid;
|
|
+ sta->band = ind->band;
|
|
+ sta->width = ind->width;
|
|
+ sta->center_freq = ind->center_freq;
|
|
+ sta->center_freq1 = ind->center_freq1;
|
|
+ sta->center_freq2 = ind->center_freq2;
|
|
+ bl_vif->sta.ap = sta;
|
|
+ // TODO: Get chan def in this case (add params in cfm ??)
|
|
+ bl_chanctx_link(bl_vif, ind->ch_idx, NULL);
|
|
+ memcpy(sta->mac_addr, ind->bssid.array, ETH_ALEN);
|
|
+ if (ind->ch_idx == bl_hw->cur_chanctx) {
|
|
+ txq_status = 0;
|
|
+ } else {
|
|
+ txq_status = BL_TXQ_STOP_CHAN;
|
|
+ }
|
|
+ memcpy(sta->ac_param, ind->ac_param, sizeof(sta->ac_param));
|
|
+ bl_txq_sta_init(bl_hw, sta, txq_status);
|
|
+ bl_dbgfs_register_rc_stat(bl_hw, sta);
|
|
+ }
|
|
+
|
|
+ if (!ind->roamed)
|
|
+ cfg80211_connect_result(dev, (const u8 *)ind->bssid.array, req_ie,
|
|
+ ind->assoc_req_ie_len, rsp_ie,
|
|
+ ind->assoc_rsp_ie_len, ind->status_code,
|
|
+ GFP_ATOMIC);
|
|
+
|
|
+ netif_tx_start_all_queues(dev);
|
|
+ netif_carrier_on(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int bl_rx_sm_disconnect_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ struct sm_disconnect_ind *ind = (struct sm_disconnect_ind *)msg->param;
|
|
+ struct bl_vif *bl_vif = bl_hw->vif_table[ind->vif_idx];
|
|
+ struct net_device *dev = bl_vif->ndev;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* if vif is not up, bl_close has already been called */
|
|
+ if (bl_vif->up) {
|
|
+ if (!ind->ft_over_ds) {
|
|
+ cfg80211_disconnected(dev, ind->reason_code, NULL, 0, true, GFP_ATOMIC);
|
|
+ }
|
|
+ netif_tx_stop_all_queues(dev);
|
|
+ netif_carrier_off(dev);
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_BL_BFMER
|
|
+ /* Disable Beamformer if supported */
|
|
+ bl_bfmer_report_del(bl_hw, bl_vif->sta.ap);
|
|
+#endif //(CONFIG_BL_BFMER)
|
|
+
|
|
+ bl_txq_sta_deinit(bl_hw, bl_vif->sta.ap);
|
|
+ bl_dbgfs_unregister_rc_stat(bl_hw, bl_vif->sta.ap);
|
|
+ bl_vif->sta.ap->valid = false;
|
|
+ bl_vif->sta.ap = NULL;
|
|
+ bl_chanctx_unlink(bl_vif);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***************************************************************************
|
|
+ * Messages from DEBUG task
|
|
+ **************************************************************************/
|
|
+static inline int bl_rx_dbg_error_ind(struct bl_hw *bl_hw,
|
|
+ struct bl_cmd *cmd,
|
|
+ struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_error_ind(bl_hw);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = {
|
|
+ [MSG_I(MM_CHANNEL_SWITCH_IND)] = bl_rx_chan_switch_ind,
|
|
+ [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = bl_rx_chan_pre_switch_ind,
|
|
+ [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = bl_rx_remain_on_channel_exp_ind,
|
|
+ [MSG_I(MM_PS_CHANGE_IND)] = bl_rx_ps_change_ind,
|
|
+ [MSG_I(MM_TRAFFIC_REQ_IND)] = bl_rx_traffic_req_ind,
|
|
+ [MSG_I(MM_CSA_COUNTER_IND)] = bl_rx_csa_counter_ind,
|
|
+ [MSG_I(MM_CSA_FINISH_IND)] = bl_rx_csa_finish_ind,
|
|
+ [MSG_I(MM_CSA_TRAFFIC_IND)] = bl_rx_csa_traffic_ind,
|
|
+ [MSG_I(MM_CHANNEL_SURVEY_IND)] = bl_rx_channel_survey_ind,
|
|
+ [MSG_I(MM_RSSI_STATUS_IND)] = bl_rx_rssi_status_ind,
|
|
+};
|
|
+
|
|
+static msg_cb_fct scan_hdlrs[MSG_I(SCANU_MAX)] = {
|
|
+ [MSG_I(SCANU_START_CFM)] = bl_rx_scanu_start_cfm,
|
|
+ [MSG_I(SCANU_RESULT_IND)] = bl_rx_scanu_result_ind,
|
|
+};
|
|
+
|
|
+static msg_cb_fct me_hdlrs[MSG_I(ME_MAX)] = {
|
|
+ [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = bl_rx_me_tkip_mic_failure_ind,
|
|
+ [MSG_I(ME_TX_CREDITS_UPDATE_IND)] = bl_rx_me_tx_credits_update_ind,
|
|
+};
|
|
+
|
|
+static msg_cb_fct sm_hdlrs[MSG_I(SM_MAX)] = {
|
|
+ [MSG_I(SM_CONNECT_IND)] = bl_rx_sm_connect_ind,
|
|
+ [MSG_I(SM_DISCONNECT_IND)] = bl_rx_sm_disconnect_ind,
|
|
+};
|
|
+
|
|
+static msg_cb_fct apm_hdlrs[MSG_I(APM_MAX)] = {
|
|
+};
|
|
+
|
|
+static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = {
|
|
+ [MSG_I(DBG_ERROR_IND)] = bl_rx_dbg_error_ind,
|
|
+};
|
|
+
|
|
+static msg_cb_fct *msg_hdlrs[] = {
|
|
+ [TASK_MM] = mm_hdlrs,
|
|
+ [TASK_DBG] = dbg_hdlrs,
|
|
+ [TASK_SCANU] = scan_hdlrs,
|
|
+ [TASK_ME] = me_hdlrs,
|
|
+ [TASK_SM] = sm_hdlrs,
|
|
+ [TASK_APM] = apm_hdlrs,
|
|
+};
|
|
+
|
|
+/**
|
|
+ *
|
|
+ */
|
|
+void bl_rx_handle_msg(struct bl_hw *bl_hw, struct ipc_e2a_msg *msg)
|
|
+{
|
|
+ printk(KERN_CRIT "recv: msg:%4d-%-24s\n", msg->id, BL_ID2STR(msg->id));
|
|
+ bl_hw->cmd_mgr.msgind(&bl_hw->cmd_mgr, msg,
|
|
+ msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]);
|
|
+}
|
|
diff -Naur /dev/null_msg_rx.h b/drivers/net/wireless/hflps170/bl_msg_rx.h
|
|
--- /dev/null_msg_rx.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_msg_rx.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,18 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_msg_rx.h
|
|
+ *
|
|
+ * @brief RX function declarations
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_MSG_RX_H_
|
|
+#define _BL_MSG_RX_H_
|
|
+
|
|
+void bl_rx_handle_msg(struct bl_hw *bl_hw, struct ipc_e2a_msg *msg);
|
|
+
|
|
+#endif /* _BL_MSG_RX_H_ */
|
|
diff -Naur /dev/null_msg_tx.c b/drivers/net/wireless/hflps170/bl_msg_tx.c
|
|
--- /dev/null_msg_tx.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_msg_tx.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,1315 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_msg_tx.c
|
|
+ *
|
|
+ * @brief TX function definitions
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#include <linux/version.h>
|
|
+#include "bl_msg_tx.h"
|
|
+#include "bl_mod_params.h"
|
|
+#ifdef CONFIG_BL_BFMER
|
|
+#include "bl_bfmer.h"
|
|
+#endif //(CONFIG_BL_BFMER)
|
|
+#include "bl_compat.h"
|
|
+
|
|
+const struct mac_addr mac_addr_bcst = {{0xFFFF, 0xFFFF, 0xFFFF}};
|
|
+
|
|
+/* Default MAC Rx filters that can be changed by mac80211
|
|
+ * (via the configure_filter() callback) */
|
|
+#define BL_MAC80211_CHANGEABLE ( \
|
|
+ NXMAC_ACCEPT_BA_BIT | \
|
|
+ NXMAC_ACCEPT_BAR_BIT | \
|
|
+ NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT | \
|
|
+ NXMAC_ACCEPT_PROBE_REQ_BIT | \
|
|
+ NXMAC_ACCEPT_PS_POLL_BIT \
|
|
+ )
|
|
+
|
|
+/* Default MAC Rx filters that cannot be changed by mac80211 */
|
|
+#define BL_MAC80211_NOT_CHANGEABLE ( \
|
|
+ NXMAC_ACCEPT_QO_S_NULL_BIT | \
|
|
+ NXMAC_ACCEPT_Q_DATA_BIT | \
|
|
+ NXMAC_ACCEPT_DATA_BIT | \
|
|
+ NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT | \
|
|
+ NXMAC_ACCEPT_MY_UNICAST_BIT | \
|
|
+ NXMAC_ACCEPT_BROADCAST_BIT | \
|
|
+ NXMAC_ACCEPT_BEACON_BIT | \
|
|
+ NXMAC_ACCEPT_PROBE_RESP_BIT \
|
|
+ )
|
|
+
|
|
+/* Default MAC Rx filter */
|
|
+#define BL_DEFAULT_RX_FILTER (BL_MAC80211_CHANGEABLE | BL_MAC80211_NOT_CHANGEABLE)
|
|
+
|
|
+static const int bw2chnl[] = {
|
|
+ [NL80211_CHAN_WIDTH_20_NOHT] = PHY_CHNL_BW_20,
|
|
+ [NL80211_CHAN_WIDTH_20] = PHY_CHNL_BW_20,
|
|
+ [NL80211_CHAN_WIDTH_40] = PHY_CHNL_BW_40,
|
|
+ [NL80211_CHAN_WIDTH_80] = PHY_CHNL_BW_80,
|
|
+ [NL80211_CHAN_WIDTH_160] = PHY_CHNL_BW_160,
|
|
+ [NL80211_CHAN_WIDTH_80P80] = PHY_CHNL_BW_80P80,
|
|
+};
|
|
+
|
|
+static const int chnl2bw[] = {
|
|
+ [PHY_CHNL_BW_20] = NL80211_CHAN_WIDTH_20,
|
|
+ [PHY_CHNL_BW_40] = NL80211_CHAN_WIDTH_40,
|
|
+ [PHY_CHNL_BW_80] = NL80211_CHAN_WIDTH_80,
|
|
+ [PHY_CHNL_BW_160] = NL80211_CHAN_WIDTH_160,
|
|
+ [PHY_CHNL_BW_80P80] = NL80211_CHAN_WIDTH_80P80,
|
|
+};
|
|
+
|
|
+/*****************************************************************************/
|
|
+/*
|
|
+ * Parse the ampdu density to retrieve the value in usec, according to the
|
|
+ * values defined in ieee80211.h
|
|
+ */
|
|
+static inline u8 bl_ampdudensity2usec(u8 ampdudensity)
|
|
+{
|
|
+ switch (ampdudensity) {
|
|
+ case IEEE80211_HT_MPDU_DENSITY_NONE:
|
|
+ return 0;
|
|
+ /* 1 microsecond is our granularity */
|
|
+ case IEEE80211_HT_MPDU_DENSITY_0_25:
|
|
+ case IEEE80211_HT_MPDU_DENSITY_0_5:
|
|
+ case IEEE80211_HT_MPDU_DENSITY_1:
|
|
+ return 1;
|
|
+ case IEEE80211_HT_MPDU_DENSITY_2:
|
|
+ return 2;
|
|
+ case IEEE80211_HT_MPDU_DENSITY_4:
|
|
+ return 4;
|
|
+ case IEEE80211_HT_MPDU_DENSITY_8:
|
|
+ return 8;
|
|
+ case IEEE80211_HT_MPDU_DENSITY_16:
|
|
+ return 16;
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline bool use_pairwise_key(struct cfg80211_crypto_settings *crypto)
|
|
+{
|
|
+ if ((crypto->cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
|
|
+ (crypto->cipher_group == WLAN_CIPHER_SUITE_WEP104))
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static inline bool is_non_blocking_msg(int id) {
|
|
+ return ((id == MM_TIM_UPDATE_REQ) || (id == ME_RC_SET_RATE_REQ) ||
|
|
+ (id == ME_TRAFFIC_IND_REQ));
|
|
+}
|
|
+
|
|
+static inline uint8_t passive_scan_flag(uint32_t flags) {
|
|
+ if (flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))
|
|
+ return SCAN_PASSIVE_BIT;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Allocate memory for a message
|
|
+ *
|
|
+ * This primitive allocates memory for a message that has to be sent. The memory
|
|
+ * is allocated dynamically on the heap and the length of the variable parameter
|
|
+ * structure has to be provided in order to allocate the correct size.
|
|
+ *
|
|
+ * Several additional parameters are provided which will be preset in the message
|
|
+ * and which may be used internally to choose the kind of memory to allocate.
|
|
+ *
|
|
+ * The memory allocated will be automatically freed by the kernel, after the
|
|
+ * pointer has been sent to ke_msg_send(). If the message is not sent, it must
|
|
+ * be freed explicitly with ke_msg_free().
|
|
+ *
|
|
+ * Allocation failure is considered critical and should not happen.
|
|
+ *
|
|
+ * @param[in] id Message identifier
|
|
+ * @param[in] dest_id Destination Task Identifier
|
|
+ * @param[in] src_id Source Task Identifier
|
|
+ * @param[in] param_len Size of the message parameters to be allocated
|
|
+ *
|
|
+ * @return Pointer to the parameter member of the ke_msg. If the parameter
|
|
+ * structure is empty, the pointer will point to the end of the message
|
|
+ * and should not be used (except to retrieve the message pointer or to
|
|
+ * send the message)
|
|
+ ******************************************************************************
|
|
+ */
|
|
+static inline void *bl_msg_zalloc(lmac_msg_id_t const id,
|
|
+ lmac_task_id_t const dest_id,
|
|
+ lmac_task_id_t const src_id,
|
|
+ uint16_t const param_len)
|
|
+{
|
|
+ struct lmac_msg *msg;
|
|
+ gfp_t flags;
|
|
+ int len;
|
|
+
|
|
+ len =(sizeof(struct lmac_msg) + param_len + 3)/4*4;
|
|
+
|
|
+
|
|
+ if (is_non_blocking_msg(id) && in_softirq())
|
|
+ flags = GFP_ATOMIC;
|
|
+ else
|
|
+ flags = GFP_KERNEL;
|
|
+
|
|
+
|
|
+ msg = (struct lmac_msg *)kzalloc(len, flags);
|
|
+ if (msg == NULL) {
|
|
+ printk(KERN_CRIT "%s: msg allocation failed\n", __func__);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ msg->sdio_hdr.type = BL_TYPE_MSG;
|
|
+ msg->sdio_hdr.len = len;
|
|
+ msg->sdio_hdr.queue_idx = 0;
|
|
+ msg->id = id;
|
|
+ msg->dest_id = dest_id;
|
|
+ msg->src_id = src_id;
|
|
+ msg->param_len = param_len;
|
|
+
|
|
+ return msg->param;
|
|
+}
|
|
+
|
|
+static int bl_send_msg(struct bl_hw *bl_hw, const void *msg_params,
|
|
+ int reqcfm, lmac_msg_id_t reqid, void *cfm)
|
|
+{
|
|
+ struct lmac_msg *msg;
|
|
+ struct bl_cmd *cmd;
|
|
+ bool nonblock;
|
|
+ int ret;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ msg = container_of((void *)msg_params, struct lmac_msg, param);
|
|
+
|
|
+ if (!test_bit(BL_DEV_STARTED, &bl_hw->drv_flags) &&
|
|
+ reqid != MM_RESET_CFM && reqid != MM_VERSION_CFM &&
|
|
+ reqid != MM_START_CFM && reqid != MM_SET_IDLE_CFM &&
|
|
+ reqid != ME_CONFIG_CFM && reqid != MM_SET_PS_MODE_CFM &&
|
|
+ reqid != ME_CHAN_CONFIG_CFM) {
|
|
+ printk(KERN_CRIT "%s: bypassing (BL_DEV_RESTARTING set) 0x%02x\n",
|
|
+ __func__, reqid);
|
|
+ kfree(msg);
|
|
+ return -EBUSY;
|
|
+ } else if (!bl_hw->ipc_env) {
|
|
+ printk(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
|
|
+ kfree(msg);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ nonblock = is_non_blocking_msg(msg->id);
|
|
+
|
|
+ cmd = kzalloc(sizeof(struct bl_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
|
|
+ cmd->result = -EINTR;
|
|
+ cmd->id = msg->id;
|
|
+ cmd->reqid = reqid;
|
|
+ cmd->a2e_msg = msg;
|
|
+ cmd->e2a_msg = cfm;
|
|
+ if (nonblock)
|
|
+ cmd->flags = BL_CMD_FLAG_NONBLOCK;
|
|
+ if (reqcfm)
|
|
+ cmd->flags |= BL_CMD_FLAG_REQ_CFM;
|
|
+ ret = bl_hw->cmd_mgr.queue(&bl_hw->cmd_mgr, cmd);
|
|
+
|
|
+ if (!nonblock)
|
|
+ kfree(cmd);
|
|
+ else
|
|
+ ret = cmd->result;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * Control messages handling functions (SOFTMAC and FULLMAC)
|
|
+ *****************************************************************************/
|
|
+int bl_send_reset(struct bl_hw *bl_hw)
|
|
+{
|
|
+ void *void_param;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* RESET REQ has no parameter */
|
|
+ void_param = bl_msg_zalloc(MM_RESET_REQ, TASK_MM, DRV_TASK_ID, 0);
|
|
+ if (!void_param)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ return bl_send_msg(bl_hw, void_param, 1, MM_RESET_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_start(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct mm_start_req *start_req_param;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the START REQ message */
|
|
+ start_req_param = bl_msg_zalloc(MM_START_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_start_req));
|
|
+ if (!start_req_param)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the START message */
|
|
+ memcpy(&start_req_param->phy_cfg, &bl_hw->phy_config, sizeof(bl_hw->phy_config));
|
|
+ start_req_param->uapsd_timeout = (u32_l)bl_hw->mod_params->uapsd_timeout;
|
|
+ start_req_param->lp_clk_accuracy = (u16_l)bl_hw->mod_params->lp_clk_ppm;
|
|
+
|
|
+ /* Send the START REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, start_req_param, 1, MM_START_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_version_req(struct bl_hw *bl_hw, struct mm_version_cfm *cfm)
|
|
+{
|
|
+ void *void_param;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* VERSION REQ has no parameter */
|
|
+ void_param = bl_msg_zalloc(MM_VERSION_REQ, TASK_MM, DRV_TASK_ID, 0);
|
|
+ if (!void_param)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ return bl_send_msg(bl_hw, void_param, 1, MM_VERSION_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_add_if(struct bl_hw *bl_hw, const unsigned char *mac,
|
|
+ enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm)
|
|
+{
|
|
+ struct mm_add_if_req *add_if_req_param;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the ADD_IF_REQ message */
|
|
+ add_if_req_param = bl_msg_zalloc(MM_ADD_IF_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_add_if_req));
|
|
+ if (!add_if_req_param)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the ADD_IF_REQ message */
|
|
+ memcpy(&(add_if_req_param->addr.array[0]), mac, ETH_ALEN);
|
|
+ switch (iftype) {
|
|
+ #ifdef CONFIG_BL_FULLMAC
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
+ add_if_req_param->p2p = true;
|
|
+ // no break
|
|
+ #endif /* CONFIG_BL_FULLMAC */
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ add_if_req_param->type = MM_STA;
|
|
+ break;
|
|
+
|
|
+ case NL80211_IFTYPE_ADHOC:
|
|
+ add_if_req_param->type = MM_IBSS;
|
|
+ break;
|
|
+
|
|
+ #ifdef CONFIG_BL_FULLMAC
|
|
+ case NL80211_IFTYPE_P2P_GO:
|
|
+ add_if_req_param->p2p = true;
|
|
+ // no break
|
|
+ #endif /* CONFIG_BL_FULLMAC */
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ add_if_req_param->type = MM_AP;
|
|
+ break;
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ add_if_req_param->type = MM_MESH_POINT;
|
|
+ break;
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ return -1;
|
|
+ default:
|
|
+ add_if_req_param->type = MM_STA;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Send the ADD_IF_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, add_if_req_param, 1, MM_ADD_IF_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_remove_if(struct bl_hw *bl_hw, u8 vif_index)
|
|
+{
|
|
+ struct mm_remove_if_req *remove_if_req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_REMOVE_IF_REQ message */
|
|
+ remove_if_req = bl_msg_zalloc(MM_REMOVE_IF_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_remove_if_req));
|
|
+ if (!remove_if_req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_REMOVE_IF_REQ message */
|
|
+ remove_if_req->inst_nbr = vif_index;
|
|
+
|
|
+ /* Send the MM_REMOVE_IF_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, remove_if_req, 1, MM_REMOVE_IF_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_set_channel(struct bl_hw *bl_hw, int phy_idx,
|
|
+ struct mm_set_channel_cfm *cfm)
|
|
+{
|
|
+ struct mm_set_channel_req *set_chnl_par;
|
|
+ enum nl80211_chan_width width;
|
|
+ u16 center_freq, center_freq1, center_freq2;
|
|
+ s8 tx_power = 0;
|
|
+ enum nl80211_band band;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if (phy_idx >= bl_hw->phy_cnt)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ set_chnl_par = bl_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_set_channel_req));
|
|
+ if (!set_chnl_par)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (phy_idx == 0) {
|
|
+ /* On FULLMAC only setting channel of secondary chain */
|
|
+ wiphy_err(bl_hw->wiphy, "Trying to set channel of primary chain");
|
|
+ return 0;
|
|
+ } else {
|
|
+ struct bl_sec_phy_chan *chan = &bl_hw->sec_phy_chan;
|
|
+
|
|
+ width = chnl2bw[chan->type];
|
|
+ band = chan->band;
|
|
+ center_freq = chan->prim20_freq;
|
|
+ center_freq1 = chan->center_freq1;
|
|
+ center_freq2 = chan->center_freq2;
|
|
+ }
|
|
+
|
|
+ set_chnl_par->band = band;
|
|
+ set_chnl_par->type = bw2chnl[width];
|
|
+ set_chnl_par->prim20_freq = center_freq;
|
|
+ set_chnl_par->center1_freq = center_freq1;
|
|
+ set_chnl_par->center2_freq = center_freq2;
|
|
+ set_chnl_par->index = phy_idx;
|
|
+ set_chnl_par->tx_power = tx_power;
|
|
+
|
|
+ if (bl_hw->use_phy_bw_tweaks) {
|
|
+ /* XXX Tweak for 80MHz VHT */
|
|
+ if (width > NL80211_CHAN_WIDTH_40) {
|
|
+ int _offs = center_freq1 - center_freq;
|
|
+ set_chnl_par->type = PHY_CHNL_BW_40;
|
|
+ set_chnl_par->center1_freq = center_freq + 10 *
|
|
+ (_offs > 0 ? 1 : -1) * (abs(_offs) > 10 ? 1 : -1);
|
|
+ BL_DBG("Tweak for 80MHz VHT: 80MHz chan requested\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ BL_DBG("mac80211: freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
|
|
+ " hw(%d): prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
|
|
+ center_freq, center_freq1,
|
|
+ center_freq2, width, band,
|
|
+ phy_idx, set_chnl_par->prim20_freq, set_chnl_par->center1_freq,
|
|
+ set_chnl_par->center2_freq, set_chnl_par->type, set_chnl_par->band);
|
|
+
|
|
+ /* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, set_chnl_par, 1, MM_SET_CHANNEL_CFM, cfm);
|
|
+}
|
|
+
|
|
+
|
|
+int bl_send_key_add(struct bl_hw *bl_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
|
|
+ u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
|
|
+ struct mm_key_add_cfm *cfm)
|
|
+{
|
|
+ struct mm_key_add_req *key_add_req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_KEY_ADD_REQ message */
|
|
+ key_add_req = bl_msg_zalloc(MM_KEY_ADD_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_key_add_req));
|
|
+ if (!key_add_req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_KEY_ADD_REQ message */
|
|
+ if (sta_idx != 0xFF) {
|
|
+ /* Pairwise key */
|
|
+ key_add_req->sta_idx = sta_idx;
|
|
+ } else {
|
|
+ /* Default key */
|
|
+ key_add_req->sta_idx = sta_idx;
|
|
+ key_add_req->key_idx = (u8_l)key_idx; /* only useful for default keys */
|
|
+ }
|
|
+ key_add_req->pairwise = pairwise;
|
|
+ key_add_req->inst_nbr = vif_idx;
|
|
+ key_add_req->key.length = key_len;
|
|
+ memcpy(&(key_add_req->key.array[0]), key, key_len);
|
|
+
|
|
+ key_add_req->cipher_suite = cipher_suite;
|
|
+
|
|
+ BL_DBG("%s: sta_idx:%d key_idx:%d inst_nbr:%d cipher:%d key_len:%d\n", __func__,
|
|
+ key_add_req->sta_idx, key_add_req->key_idx, key_add_req->inst_nbr,
|
|
+ key_add_req->cipher_suite, key_add_req->key.length);
|
|
+#if defined(CONFIG_BL_DBG) || defined(CONFIG_DYNAMIC_DEBUG)
|
|
+ print_hex_dump_bytes("key: ", DUMP_PREFIX_OFFSET, key_add_req->key.array, key_add_req->key.length);
|
|
+#endif
|
|
+
|
|
+ /* Send the MM_KEY_ADD_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, key_add_req, 1, MM_KEY_ADD_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_key_del(struct bl_hw *bl_hw, uint8_t hw_key_idx)
|
|
+{
|
|
+ struct mm_key_del_req *key_del_req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_KEY_DEL_REQ message */
|
|
+ key_del_req = bl_msg_zalloc(MM_KEY_DEL_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_key_del_req));
|
|
+ if (!key_del_req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_KEY_DEL_REQ message */
|
|
+ key_del_req->hw_key_idx = hw_key_idx;
|
|
+
|
|
+ /* Send the MM_KEY_DEL_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, key_del_req, 1, MM_KEY_DEL_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_bcn_change(struct bl_hw *bl_hw, u8 vif_idx, u8 *buf,
|
|
+ u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft)
|
|
+{
|
|
+ struct mm_bcn_change_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_BCN_CHANGE_REQ message */
|
|
+ req = bl_msg_zalloc(MM_BCN_CHANGE_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_bcn_change_req) + bcn_len);
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memcpy(req->bcn_buf, buf, bcn_len);
|
|
+
|
|
+ /* Set parameters for the MM_BCN_CHANGE_REQ message */
|
|
+ req->bcn_len = bcn_len;
|
|
+ req->tim_oft = tim_oft;
|
|
+ req->tim_len = tim_len;
|
|
+ req->inst_nbr = vif_idx;
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
|
+ if (csa_oft) {
|
|
+ int i;
|
|
+ for (i = 0; i < BCN_MAX_CSA_CPT; i++) {
|
|
+ req->csa_oft[i] = csa_oft[i];
|
|
+ }
|
|
+ }
|
|
+#endif /* VERSION >= 3.16.0 */
|
|
+
|
|
+ /* Send the MM_BCN_CHANGE_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, MM_BCN_CHANGE_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_roc(struct bl_hw *bl_hw, struct bl_vif *vif,
|
|
+ struct ieee80211_channel *chan, unsigned int duration)
|
|
+{
|
|
+ struct mm_remain_on_channel_req *req;
|
|
+ struct cfg80211_chan_def chandef;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Create channel definition structure */
|
|
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
|
|
+
|
|
+ /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
|
|
+ req = bl_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_remain_on_channel_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
|
|
+ req->op_code = MM_ROC_OP_START;
|
|
+ req->vif_index = vif->vif_index;
|
|
+ req->duration_ms = duration;
|
|
+ req->band = chan->band;
|
|
+ req->type = bw2chnl[chandef.width];
|
|
+ req->prim20_freq = chan->center_freq;
|
|
+ req->center1_freq = chandef.center_freq1;
|
|
+ req->center2_freq = chandef.center_freq2;
|
|
+ req->tx_power = chan->max_power;
|
|
+
|
|
+ /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_cancel_roc(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct mm_remain_on_channel_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
|
|
+ req = bl_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_remain_on_channel_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
|
|
+ req->op_code = MM_ROC_OP_CANCEL;
|
|
+
|
|
+ /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 0, 0, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_set_power(struct bl_hw *bl_hw, u8 vif_idx, s8 pwr,
|
|
+ struct mm_set_power_cfm *cfm)
|
|
+{
|
|
+ struct mm_set_power_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_SET_POWER_REQ message */
|
|
+ req = bl_msg_zalloc(MM_SET_POWER_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_set_power_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_SET_POWER_REQ message */
|
|
+ req->inst_nbr = vif_idx;
|
|
+ req->power = pwr;
|
|
+
|
|
+ /* Send the MM_SET_POWER_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, MM_SET_POWER_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_set_edca(struct bl_hw *bl_hw, u8 hw_queue, u32 param,
|
|
+ bool uapsd, u8 inst_nbr)
|
|
+{
|
|
+ struct mm_set_edca_req *set_edca_req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_SET_EDCA_REQ message */
|
|
+ set_edca_req = bl_msg_zalloc(MM_SET_EDCA_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_set_edca_req));
|
|
+ if (!set_edca_req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_SET_EDCA_REQ message */
|
|
+ set_edca_req->ac_param = param;
|
|
+ set_edca_req->uapsd = uapsd;
|
|
+ set_edca_req->hw_queue = hw_queue;
|
|
+ set_edca_req->inst_nbr = inst_nbr;
|
|
+
|
|
+ /* Send the MM_SET_EDCA_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, set_edca_req, 1, MM_SET_EDCA_CFM, NULL);
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * Control messages handling functions (FULLMAC only)
|
|
+ *****************************************************************************/
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+int bl_send_me_config_req(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct me_config_req *req;
|
|
+ struct wiphy *wiphy = bl_hw->wiphy;
|
|
+ struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
|
|
+ uint8_t *ht_mcs = (uint8_t *)&ht_cap->mcs;
|
|
+ int i;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the ME_CONFIG_REQ message */
|
|
+ req = bl_msg_zalloc(ME_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
|
|
+ sizeof(struct me_config_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the ME_CONFIG_REQ message */
|
|
+ printk("HT supp %d\n", ht_cap->ht_supported);
|
|
+
|
|
+ req->ht_supp = ht_cap->ht_supported;
|
|
+ req->ht_cap.ht_capa_info = cpu_to_le16(ht_cap->cap);
|
|
+ req->ht_cap.a_mpdu_param = ht_cap->ampdu_factor |
|
|
+ (ht_cap->ampdu_density <<
|
|
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
|
|
+ for (i = 0; i < sizeof(ht_cap->mcs); i++)
|
|
+ req->ht_cap.mcs_rate[i] = ht_mcs[i];
|
|
+ req->ht_cap.ht_extended_capa = 0;
|
|
+ req->ht_cap.tx_beamforming_capa = 0;
|
|
+ req->ht_cap.asel_capa = 0;
|
|
+
|
|
+ req->ps_on = bl_hw->mod_params->ps_on;
|
|
+ req->tx_lft = bl_hw->mod_params->tx_lft;
|
|
+
|
|
+ /* Send the ME_CONFIG_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, ME_CONFIG_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_me_chan_config_req(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct me_chan_config_req *req;
|
|
+ struct wiphy *wiphy = bl_hw->wiphy;
|
|
+ int i;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the ME_CHAN_CONFIG_REQ message */
|
|
+ req = bl_msg_zalloc(ME_CHAN_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
|
|
+ sizeof(struct me_chan_config_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ req->chan2G4_cnt= 0;
|
|
+ if (wiphy->bands[NL80211_BAND_2GHZ] != NULL) {
|
|
+ struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_2GHZ];
|
|
+ for (i = 0; i < b->n_channels; i++) {
|
|
+ req->chan2G4[req->chan2G4_cnt].flags = 0;
|
|
+ if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
|
|
+ req->chan2G4[req->chan2G4_cnt].flags |= SCAN_DISABLED_BIT;
|
|
+ req->chan2G4[req->chan2G4_cnt].flags |= passive_scan_flag(b->channels[i].flags);
|
|
+ req->chan2G4[req->chan2G4_cnt].band = NL80211_BAND_2GHZ;
|
|
+ req->chan2G4[req->chan2G4_cnt].freq = b->channels[i].center_freq;
|
|
+ req->chan2G4[req->chan2G4_cnt].tx_power = b->channels[i].max_power;
|
|
+ req->chan2G4_cnt++;
|
|
+ if (req->chan2G4_cnt == SCAN_CHANNEL_2G4)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ req->chan5G_cnt = 0;
|
|
+ if (wiphy->bands[NL80211_BAND_5GHZ] != NULL) {
|
|
+ struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_5GHZ];
|
|
+ for (i = 0; i < b->n_channels; i++) {
|
|
+ req->chan5G[req->chan5G_cnt].flags = 0;
|
|
+ if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
|
|
+ req->chan5G[req->chan5G_cnt].flags |= SCAN_DISABLED_BIT;
|
|
+ req->chan5G[req->chan5G_cnt].flags |= passive_scan_flag(b->channels[i].flags);
|
|
+ req->chan5G[req->chan5G_cnt].band = NL80211_BAND_5GHZ;
|
|
+ req->chan5G[req->chan5G_cnt].freq = b->channels[i].center_freq;
|
|
+ req->chan5G[req->chan5G_cnt].tx_power = b->channels[i].max_power;
|
|
+ req->chan5G_cnt++;
|
|
+ if (req->chan5G_cnt == SCAN_CHANNEL_5G)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, ME_CHAN_CONFIG_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_me_set_control_port_req(struct bl_hw *bl_hw, bool opened, u8 sta_idx)
|
|
+{
|
|
+ struct me_set_control_port_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the ME_SET_CONTROL_PORT_REQ message */
|
|
+ req = bl_msg_zalloc(ME_SET_CONTROL_PORT_REQ, TASK_ME, DRV_TASK_ID,
|
|
+ sizeof(struct me_set_control_port_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the ME_SET_CONTROL_PORT_REQ message */
|
|
+ req->sta_idx = sta_idx;
|
|
+ req->control_port_open = opened;
|
|
+
|
|
+ /* Send the ME_SET_CONTROL_PORT_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, ME_SET_CONTROL_PORT_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_me_sta_add(struct bl_hw *bl_hw, struct station_parameters *params,
|
|
+ const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm)
|
|
+{
|
|
+ struct me_sta_add_req *req;
|
|
+ u8 *ht_mcs = (u8 *)¶ms->ht_capa->mcs;
|
|
+ int i;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_STA_ADD_REQ message */
|
|
+ req = bl_msg_zalloc(ME_STA_ADD_REQ, TASK_ME, DRV_TASK_ID,
|
|
+ sizeof(struct me_sta_add_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_STA_ADD_REQ message */
|
|
+ memcpy(&(req->mac_addr.array[0]), mac, ETH_ALEN);
|
|
+
|
|
+ req->rate_set.length = params->supported_rates_len;
|
|
+ for (i = 0; i < params->supported_rates_len; i++)
|
|
+ req->rate_set.array[i] = params->supported_rates[i];
|
|
+
|
|
+ req->flags = 0;
|
|
+ if (params->ht_capa) {
|
|
+ const struct ieee80211_ht_cap *ht_capa = params->ht_capa;
|
|
+
|
|
+ req->flags |= STA_HT_CAPA;
|
|
+ req->ht_cap.ht_capa_info = cpu_to_le16(ht_capa->cap_info);
|
|
+ req->ht_cap.a_mpdu_param = ht_capa->ampdu_params_info;
|
|
+ for (i = 0; i < sizeof(ht_capa->mcs); i++)
|
|
+ req->ht_cap.mcs_rate[i] = ht_mcs[i];
|
|
+ req->ht_cap.ht_extended_capa = cpu_to_le16(ht_capa->extended_ht_cap_info);
|
|
+ req->ht_cap.tx_beamforming_capa = cpu_to_le32(ht_capa->tx_BF_cap_info);
|
|
+ req->ht_cap.asel_capa = ht_capa->antenna_selection_info;
|
|
+ }
|
|
+
|
|
+ if (params->vht_capa) {
|
|
+ const struct ieee80211_vht_cap *vht_capa = params->vht_capa;
|
|
+
|
|
+ req->flags |= STA_VHT_CAPA;
|
|
+ req->vht_cap.vht_capa_info = cpu_to_le32(vht_capa->vht_cap_info);
|
|
+ req->vht_cap.rx_highest = cpu_to_le16(vht_capa->supp_mcs.rx_highest);
|
|
+ req->vht_cap.rx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.rx_mcs_map);
|
|
+ req->vht_cap.tx_highest = cpu_to_le16(vht_capa->supp_mcs.tx_highest);
|
|
+ req->vht_cap.tx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.tx_mcs_map);
|
|
+ }
|
|
+
|
|
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))
|
|
+ req->flags |= STA_QOS_CAPA;
|
|
+
|
|
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP))
|
|
+ req->flags |= STA_MFP_CAPA;
|
|
+
|
|
+ if (params->opmode_notif_used) {
|
|
+ req->flags |= STA_OPMOD_NOTIF;
|
|
+ req->opmode = params->opmode_notif;
|
|
+ }
|
|
+
|
|
+ req->aid = cpu_to_le16(params->aid);
|
|
+ req->uapsd_queues = params->uapsd_queues;
|
|
+ req->max_sp_len = params->max_sp * 2;
|
|
+ req->vif_idx = inst_nbr;
|
|
+
|
|
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
|
+ req->tdls_sta = true;
|
|
+
|
|
+ /* Send the ME_STA_ADD_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, ME_STA_ADD_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_me_sta_del(struct bl_hw *bl_hw, u8 sta_idx, bool tdls_sta)
|
|
+{
|
|
+ struct me_sta_del_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_STA_DEL_REQ message */
|
|
+ req = bl_msg_zalloc(ME_STA_DEL_REQ, TASK_ME, DRV_TASK_ID,
|
|
+ sizeof(struct me_sta_del_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_STA_DEL_REQ message */
|
|
+ req->sta_idx = sta_idx;
|
|
+ req->tdls_sta = tdls_sta;
|
|
+
|
|
+ /* Send the ME_STA_DEL_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, ME_STA_DEL_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_me_traffic_ind(struct bl_hw *bl_hw, u8 sta_idx, bool uapsd, u8 tx_status)
|
|
+{
|
|
+ struct me_traffic_ind_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the ME_UTRAFFIC_IND_REQ message */
|
|
+ req = bl_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID,
|
|
+ sizeof(struct me_traffic_ind_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the ME_TRAFFIC_IND_REQ message */
|
|
+ req->sta_idx = sta_idx;
|
|
+ req->tx_avail = tx_status;
|
|
+ req->uapsd = uapsd;
|
|
+
|
|
+ /* Send the ME_TRAFFIC_IND_REQ to UMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, ME_TRAFFIC_IND_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_me_rc_stats(struct bl_hw *bl_hw,
|
|
+ u8 sta_idx,
|
|
+ struct me_rc_stats_cfm *cfm)
|
|
+{
|
|
+ struct me_rc_stats_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the ME_RC_STATS_REQ message */
|
|
+ req = bl_msg_zalloc(ME_RC_STATS_REQ, TASK_ME, DRV_TASK_ID,
|
|
+ sizeof(struct me_rc_stats_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the ME_RC_STATS_REQ message */
|
|
+ req->sta_idx = sta_idx;
|
|
+
|
|
+ /* Send the ME_RC_STATS_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, ME_RC_STATS_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_me_rc_set_rate(struct bl_hw *bl_hw,
|
|
+ u8 sta_idx,
|
|
+ u16 rate_cfg)
|
|
+{
|
|
+ struct me_rc_set_rate_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the ME_RC_SET_RATE_REQ message */
|
|
+ req = bl_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
|
|
+ sizeof(struct me_rc_set_rate_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the ME_RC_SET_RATE_REQ message */
|
|
+ req->sta_idx = sta_idx;
|
|
+ req->fixed_rate_cfg = rate_cfg;
|
|
+
|
|
+ /* Send the ME_RC_SET_RATE_REQ message to FW */
|
|
+ return bl_send_msg(bl_hw, req, 0, 0, NULL);
|
|
+}
|
|
+
|
|
+
|
|
+int bl_send_sm_connect_req(struct bl_hw *bl_hw,
|
|
+ struct bl_vif *bl_vif,
|
|
+ struct cfg80211_connect_params *sme,
|
|
+ struct sm_connect_cfm *cfm)
|
|
+{
|
|
+ struct sm_connect_req *req;
|
|
+ int i;
|
|
+ u32_l flags = 0;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the SM_CONNECT_REQ message */
|
|
+ req = bl_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID,
|
|
+ sizeof(struct sm_connect_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the SM_CONNECT_REQ message */
|
|
+ if (sme->crypto.n_ciphers_pairwise &&
|
|
+ ((sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
|
|
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_TKIP) ||
|
|
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104)))
|
|
+ flags |= DISABLE_HT;
|
|
+
|
|
+ if (sme->crypto.control_port)
|
|
+ flags |= CONTROL_PORT_HOST;
|
|
+
|
|
+ if (sme->crypto.control_port_no_encrypt)
|
|
+ flags |= CONTROL_PORT_NO_ENC;
|
|
+
|
|
+ if (use_pairwise_key(&sme->crypto))
|
|
+ flags |= WPA_WPA2_IN_USE;
|
|
+
|
|
+ if (sme->mfp == NL80211_MFP_REQUIRED)
|
|
+ flags |= MFP_IN_USE;
|
|
+
|
|
+ if (sme->crypto.control_port_ethertype)
|
|
+ req->ctrl_port_ethertype = sme->crypto.control_port_ethertype;
|
|
+ else
|
|
+ req->ctrl_port_ethertype = ETH_P_PAE;
|
|
+
|
|
+ if (sme->bssid)
|
|
+ memcpy(&req->bssid, sme->bssid, ETH_ALEN);
|
|
+ else
|
|
+ req->bssid = mac_addr_bcst;
|
|
+ req->vif_idx = bl_vif->vif_index;
|
|
+ if (sme->channel) {
|
|
+ req->chan.band = sme->channel->band;
|
|
+ req->chan.freq = sme->channel->center_freq;
|
|
+ req->chan.flags = passive_scan_flag(sme->channel->flags);
|
|
+ } else {
|
|
+ req->chan.freq = (u16_l)-1;
|
|
+ }
|
|
+ for (i = 0; i < sme->ssid_len; i++)
|
|
+ req->ssid.array[i] = sme->ssid[i];
|
|
+ req->ssid.length = sme->ssid_len;
|
|
+ req->flags = flags;
|
|
+ if (WARN_ON(sme->ie_len > sizeof(req->ie_buf)))
|
|
+ return -EINVAL;
|
|
+ if (sme->ie_len)
|
|
+ memcpy(req->ie_buf, sme->ie, sme->ie_len);
|
|
+ req->ie_len = sme->ie_len;
|
|
+ req->listen_interval = bl_mod_params.listen_itv;
|
|
+ req->dont_wait_bcmc = !bl_mod_params.listen_bcmc;
|
|
+
|
|
+ /* Set auth_type */
|
|
+ if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC)
|
|
+ req->auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
|
|
+ else
|
|
+ req->auth_type = sme->auth_type;
|
|
+
|
|
+ /* Set UAPSD queues */
|
|
+ req->uapsd_queues = bl_mod_params.uapsd_queues;
|
|
+
|
|
+ /* Send the SM_CONNECT_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, SM_CONNECT_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_sm_disconnect_req(struct bl_hw *bl_hw,
|
|
+ struct bl_vif *bl_vif,
|
|
+ u16 reason)
|
|
+{
|
|
+ struct sm_disconnect_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the SM_DISCONNECT_REQ message */
|
|
+ req = bl_msg_zalloc(SM_DISCONNECT_REQ, TASK_SM, DRV_TASK_ID,
|
|
+ sizeof(struct sm_disconnect_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the SM_DISCONNECT_REQ message */
|
|
+ req->reason_code = reason;
|
|
+ req->vif_idx = bl_vif->vif_index;
|
|
+
|
|
+ /* Send the SM_DISCONNECT_REQ message to LMAC FW */
|
|
+ //return bl_send_msg(bl_hw, req, 1, SM_DISCONNECT_IND, NULL);
|
|
+ return bl_send_msg(bl_hw, req, 1, SM_DISCONNECT_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_apm_start_req(struct bl_hw *bl_hw, struct bl_vif *vif,
|
|
+ struct cfg80211_ap_settings *settings,
|
|
+ struct apm_start_cfm *cfm, struct bl_dma_elem *elem)
|
|
+{
|
|
+ struct apm_start_req *req;
|
|
+ struct bl_bcn *bcn = &vif->ap.bcn;
|
|
+ u8 *buf;
|
|
+ u32 flags = 0;
|
|
+ const u8 *rate_ie;
|
|
+ u8 rate_len = 0;
|
|
+ int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
|
|
+ const u8 *var_pos;
|
|
+ int len, i;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ // Build the beacon
|
|
+ bcn->dtim = (u8)settings->dtim_period;
|
|
+ buf = bl_build_bcn(bcn, &settings->beacon);
|
|
+ if (!buf) {
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* Build the APM_START_REQ message */
|
|
+ req = bl_msg_zalloc(APM_START_REQ, TASK_APM, DRV_TASK_ID,
|
|
+ sizeof(struct apm_start_req) + bcn->len);
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ // Retrieve the basic rate set from the beacon buffer
|
|
+ len = bcn->len - var_offset;
|
|
+ var_pos = buf + var_offset;
|
|
+
|
|
+ rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
|
|
+ if (rate_ie) {
|
|
+ const u8 *rates = rate_ie + 2;
|
|
+ for (i = 0; i < rate_ie[1]; i++) {
|
|
+ if (rates[i] & 0x80)
|
|
+ req->basic_rates.array[rate_len++] = rates[i];
|
|
+ }
|
|
+ }
|
|
+ rate_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, var_pos, len);
|
|
+ if (rate_ie) {
|
|
+ const u8 *rates = rate_ie + 2;
|
|
+ for (i = 0; i < rate_ie[1]; i++) {
|
|
+ if (rates[i] & 0x80)
|
|
+ req->basic_rates.array[rate_len++] = rates[i];
|
|
+ }
|
|
+ }
|
|
+ req->basic_rates.length = rate_len;
|
|
+
|
|
+ // Fill in the DMA structure
|
|
+ elem->buf = buf;
|
|
+ elem->len = bcn->len;
|
|
+ //elem->dma_addr = dma_map_single(bl_hw->dev, elem->buf, elem->len,
|
|
+ // DMA_TO_DEVICE);
|
|
+ memcpy(req->bcn_buf, elem->buf, elem->len);
|
|
+ /* Set parameters for the APM_START_REQ message */
|
|
+ req->vif_idx = vif->vif_index;
|
|
+ //req->bcn_addr = elem->dma_addr;
|
|
+ req->bcn_len = bcn->len;
|
|
+ req->tim_oft = bcn->head_len;
|
|
+ req->tim_len = bcn->tim_len;
|
|
+ req->chan.band = settings->chandef.chan->band;
|
|
+ req->chan.freq = settings->chandef.chan->center_freq;
|
|
+ req->chan.flags = 0;
|
|
+ req->chan.tx_power = settings->chandef.chan->max_power;
|
|
+ req->center_freq1 = settings->chandef.center_freq1;
|
|
+ req->center_freq2 = settings->chandef.center_freq2;
|
|
+ req->ch_width = bw2chnl[settings->chandef.width];
|
|
+ req->bcn_int = settings->beacon_interval;
|
|
+ if (settings->crypto.control_port)
|
|
+ flags |= CONTROL_PORT_HOST;
|
|
+
|
|
+ if (settings->crypto.control_port_no_encrypt)
|
|
+ flags |= CONTROL_PORT_NO_ENC;
|
|
+
|
|
+ if (use_pairwise_key(&settings->crypto))
|
|
+ flags |= WPA_WPA2_IN_USE;
|
|
+
|
|
+ if (settings->crypto.control_port_ethertype)
|
|
+ req->ctrl_port_ethertype = settings->crypto.control_port_ethertype;
|
|
+ else
|
|
+ req->ctrl_port_ethertype = ETH_P_PAE;
|
|
+ req->flags = flags;
|
|
+
|
|
+ /* Send the APM_START_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, APM_START_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_apm_stop_req(struct bl_hw *bl_hw, struct bl_vif *vif)
|
|
+{
|
|
+ struct apm_stop_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the APM_STOP_REQ message */
|
|
+ req = bl_msg_zalloc(APM_STOP_REQ, TASK_APM, DRV_TASK_ID,
|
|
+ sizeof(struct apm_stop_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the APM_STOP_REQ message */
|
|
+ req->vif_idx = vif->vif_index;
|
|
+
|
|
+ /* Send the APM_STOP_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, APM_STOP_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_scanu_req(struct bl_hw *bl_hw, struct bl_vif *bl_vif,
|
|
+ struct cfg80211_scan_request *param)
|
|
+{
|
|
+ struct scanu_start_req *req;
|
|
+ int i;
|
|
+ uint8_t chan_flags = 0;
|
|
+ u16_l len;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ len = (param->ie == NULL) ? 0: param->ie_len;
|
|
+
|
|
+ /* Build the SCANU_START_REQ message */
|
|
+ req = bl_msg_zalloc(SCANU_START_REQ, TASK_SCANU, DRV_TASK_ID,
|
|
+ sizeof(struct scanu_start_req) + len);
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters */
|
|
+ req->vif_idx = bl_vif->vif_index;
|
|
+ req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
|
|
+ req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
|
|
+ req->bssid = mac_addr_bcst;
|
|
+ req->no_cck = param->no_cck;
|
|
+
|
|
+ if (req->ssid_cnt == 0)
|
|
+ chan_flags |= SCAN_PASSIVE_BIT;
|
|
+ for (i = 0; i < req->ssid_cnt; i++) {
|
|
+ int j;
|
|
+ for (j = 0; j < param->ssids[i].ssid_len; j++)
|
|
+ req->ssid[i].array[j] = param->ssids[i].ssid[j];
|
|
+ req->ssid[i].length = param->ssids[i].ssid_len;
|
|
+ }
|
|
+
|
|
+ if (param->ie){
|
|
+ memcpy(req->add_ies_buf, param->ie, param->ie_len);
|
|
+ req->add_ie_len = param->ie_len;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < req->chan_cnt; i++) {
|
|
+ struct ieee80211_channel *chan = param->channels[i];
|
|
+
|
|
+ req->chan[i].band = chan->band;
|
|
+ req->chan[i].freq = chan->center_freq;
|
|
+ req->chan[i].flags = chan_flags | passive_scan_flag(chan->flags);
|
|
+ req->chan[i].tx_power = chan->max_reg_power;
|
|
+ }
|
|
+
|
|
+ /* Send the SCANU_START_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 0, 0, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_apm_start_cac_req(struct bl_hw *bl_hw, struct bl_vif *vif,
|
|
+ struct cfg80211_chan_def *chandef,
|
|
+ struct apm_start_cac_cfm *cfm)
|
|
+{
|
|
+ struct apm_start_cac_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the APM_START_CAC_REQ message */
|
|
+ req = bl_msg_zalloc(APM_START_CAC_REQ, TASK_APM, DRV_TASK_ID,
|
|
+ sizeof(struct apm_start_cac_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the APM_START_CAC_REQ message */
|
|
+ req->vif_idx = vif->vif_index;
|
|
+ req->chan.band = chandef->chan->band;
|
|
+ req->chan.freq = chandef->chan->center_freq;
|
|
+ req->chan.flags = 0;
|
|
+ req->center_freq1 = chandef->center_freq1;
|
|
+ req->center_freq2 = chandef->center_freq2;
|
|
+ req->ch_width = bw2chnl[chandef->width];
|
|
+
|
|
+ /* Send the APM_START_CAC_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, APM_START_CAC_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_apm_stop_cac_req(struct bl_hw *bl_hw, struct bl_vif *vif)
|
|
+{
|
|
+ struct apm_stop_cac_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the APM_STOP_CAC_REQ message */
|
|
+ req = bl_msg_zalloc(APM_STOP_CAC_REQ, TASK_APM, DRV_TASK_ID,
|
|
+ sizeof(struct apm_stop_cac_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the APM_STOP_CAC_REQ message */
|
|
+ req->vif_idx = vif->vif_index;
|
|
+
|
|
+ /* Send the APM_STOP_CAC_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, APM_STOP_CAC_CFM, NULL);
|
|
+}
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+/**********************************************************************
|
|
+ * Debug Messages
|
|
+ *********************************************************************/
|
|
+int bl_send_dbg_trigger_req(struct bl_hw *bl_hw, char *msg)
|
|
+{
|
|
+ struct mm_dbg_trigger_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_DBG_TRIGGER_REQ message */
|
|
+ req = bl_msg_zalloc(MM_DBG_TRIGGER_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_dbg_trigger_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_DBG_TRIGGER_REQ message */
|
|
+ strncpy(req->error, msg, sizeof(req->error));
|
|
+
|
|
+ /* Send the MM_DBG_TRIGGER_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 0, -1, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_dbg_mem_read_req(struct bl_hw *bl_hw, u32 mem_addr,
|
|
+ struct dbg_mem_read_cfm *cfm)
|
|
+{
|
|
+ struct dbg_mem_read_req *mem_read_req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the DBG_MEM_READ_REQ message */
|
|
+ mem_read_req = bl_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID,
|
|
+ sizeof(struct dbg_mem_read_req));
|
|
+ if (!mem_read_req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the DBG_MEM_READ_REQ message */
|
|
+ mem_read_req->memaddr = mem_addr;
|
|
+
|
|
+ /* Send the DBG_MEM_READ_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, mem_read_req, 1, DBG_MEM_READ_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_dbg_mem_write_req(struct bl_hw *bl_hw, u32 mem_addr,
|
|
+ u32 mem_data)
|
|
+{
|
|
+ struct dbg_mem_write_req *mem_write_req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the DBG_MEM_WRITE_REQ message */
|
|
+ mem_write_req = bl_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
|
|
+ sizeof(struct dbg_mem_write_req));
|
|
+ if (!mem_write_req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the DBG_MEM_WRITE_REQ message */
|
|
+ mem_write_req->memaddr = mem_addr;
|
|
+ mem_write_req->memdata = mem_data;
|
|
+
|
|
+ /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_dbg_set_mod_filter_req(struct bl_hw *bl_hw, u32 filter)
|
|
+{
|
|
+ struct dbg_set_mod_filter_req *set_mod_filter_req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the DBG_SET_MOD_FILTER_REQ message */
|
|
+ set_mod_filter_req =
|
|
+ bl_msg_zalloc(DBG_SET_MOD_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
|
|
+ sizeof(struct dbg_set_mod_filter_req));
|
|
+ if (!set_mod_filter_req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the DBG_SET_MOD_FILTER_REQ message */
|
|
+ set_mod_filter_req->mod_filter = filter;
|
|
+
|
|
+ /* Send the DBG_SET_MOD_FILTER_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, set_mod_filter_req, 1, DBG_SET_MOD_FILTER_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_dbg_set_sev_filter_req(struct bl_hw *bl_hw, u32 filter)
|
|
+{
|
|
+ struct dbg_set_sev_filter_req *set_sev_filter_req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the DBG_SET_SEV_FILTER_REQ message */
|
|
+ set_sev_filter_req =
|
|
+ bl_msg_zalloc(DBG_SET_SEV_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
|
|
+ sizeof(struct dbg_set_sev_filter_req));
|
|
+ if (!set_sev_filter_req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the DBG_SET_SEV_FILTER_REQ message */
|
|
+ set_sev_filter_req->sev_filter = filter;
|
|
+
|
|
+ /* Send the DBG_SET_SEV_FILTER_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, set_sev_filter_req, 1, DBG_SET_SEV_FILTER_CFM, NULL);
|
|
+}
|
|
+
|
|
+int bl_send_dbg_get_sys_stat_req(struct bl_hw *bl_hw,
|
|
+ struct dbg_get_sys_stat_cfm *cfm)
|
|
+{
|
|
+ void *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Allocate the message */
|
|
+ req = bl_msg_zalloc(DBG_GET_SYS_STAT_REQ, TASK_DBG, DRV_TASK_ID, 0);
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Send the DBG_MEM_READ_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 1, DBG_GET_SYS_STAT_CFM, cfm);
|
|
+}
|
|
+
|
|
+int bl_send_cfg_rssi_req(struct bl_hw *bl_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst)
|
|
+{
|
|
+ struct mm_cfg_rssi_req *req;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Build the MM_CFG_RSSI_REQ message */
|
|
+ req = bl_msg_zalloc(MM_CFG_RSSI_REQ, TASK_MM, DRV_TASK_ID,
|
|
+ sizeof(struct mm_cfg_rssi_req));
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set parameters for the MM_CFG_RSSI_REQ message */
|
|
+ req->vif_index = vif_index;
|
|
+ req->rssi_thold = (s8)rssi_thold;
|
|
+ req->rssi_hyst = (u8)rssi_hyst;
|
|
+
|
|
+ /* Send the MM_CFG_RSSI_REQ message to LMAC FW */
|
|
+ return bl_send_msg(bl_hw, req, 0, 0, NULL);
|
|
+}
|
|
+
|
|
diff -Naur /dev/null_msg_tx.h b/drivers/net/wireless/hflps170/bl_msg_tx.h
|
|
--- /dev/null_msg_tx.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_msg_tx.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,103 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_msg_tx.h
|
|
+ *
|
|
+ * @brief TX function declarations
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_MSG_TX_H_
|
|
+#define _BL_MSG_TX_H_
|
|
+
|
|
+#include "bl_defs.h"
|
|
+
|
|
+/*
|
|
+ * c.f LMAC/src/co/mac/mac_frame.h
|
|
+ */
|
|
+#define MAC_RSNIE_CIPHER_WEP40 0x00
|
|
+#define MAC_RSNIE_CIPHER_TKIP 0x01
|
|
+#define MAC_RSNIE_CIPHER_CCMP 0x02
|
|
+#define MAC_RSNIE_CIPHER_WEP104 0x03
|
|
+#define MAC_RSNIE_CIPHER_SMS4 0x04
|
|
+#define MAC_RSNIE_CIPHER_AES_CMAC 0x05
|
|
+
|
|
+enum bl_chan_types {
|
|
+ PHY_CHNL_BW_20,
|
|
+ PHY_CHNL_BW_40,
|
|
+ PHY_CHNL_BW_80,
|
|
+ PHY_CHNL_BW_160,
|
|
+ PHY_CHNL_BW_80P80,
|
|
+ PHY_CHNL_BW_OTHER,
|
|
+};
|
|
+
|
|
+int bl_send_reset(struct bl_hw *bl_hw);
|
|
+int bl_send_start(struct bl_hw *bl_hw);
|
|
+int bl_send_version_req(struct bl_hw *bl_hw, struct mm_version_cfm *cfm);
|
|
+int bl_send_add_if(struct bl_hw *bl_hw, const unsigned char *mac,
|
|
+ enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm);
|
|
+int bl_send_remove_if(struct bl_hw *bl_hw, u8 vif_index);
|
|
+int bl_send_set_channel(struct bl_hw *bl_hw, int phy_idx,
|
|
+ struct mm_set_channel_cfm *cfm);
|
|
+int bl_send_key_add(struct bl_hw *bl_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
|
|
+ u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
|
|
+ struct mm_key_add_cfm *cfm);
|
|
+int bl_send_key_del(struct bl_hw *bl_hw, uint8_t hw_key_idx);
|
|
+int bl_send_bcn_change(struct bl_hw *bl_hw, u8 vif_idx, u8 *buf,
|
|
+ u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft);
|
|
+int bl_send_tim_update(struct bl_hw *bl_hw, u8 vif_idx, u16 aid,
|
|
+ u8 tx_status);
|
|
+int bl_send_roc(struct bl_hw *bl_hw, struct bl_vif *vif,
|
|
+ struct ieee80211_channel *chan, unsigned int duration);
|
|
+int bl_send_cancel_roc(struct bl_hw *bl_hw);
|
|
+int bl_send_set_power(struct bl_hw *bl_hw, u8 vif_idx, s8 pwr,
|
|
+ struct mm_set_power_cfm *cfm);
|
|
+int bl_send_set_edca(struct bl_hw *bl_hw, u8 hw_queue, u32 param,
|
|
+ bool uapsd, u8 inst_nbr);
|
|
+
|
|
+int bl_send_me_config_req(struct bl_hw *bl_hw);
|
|
+int bl_send_me_chan_config_req(struct bl_hw *bl_hw);
|
|
+int bl_send_me_set_control_port_req(struct bl_hw *bl_hw, bool opened,
|
|
+ u8 sta_idx);
|
|
+int bl_send_me_sta_add(struct bl_hw *bl_hw, struct station_parameters *params,
|
|
+ const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm);
|
|
+int bl_send_me_sta_del(struct bl_hw *bl_hw, u8 sta_idx, bool tdls_sta);
|
|
+int bl_send_me_traffic_ind(struct bl_hw *bl_hw, u8 sta_idx, bool uapsd, u8 tx_status);
|
|
+int bl_send_me_rc_stats(struct bl_hw *bl_hw, u8 sta_idx,
|
|
+ struct me_rc_stats_cfm *cfm);
|
|
+int bl_send_me_rc_set_rate(struct bl_hw *bl_hw,
|
|
+ u8 sta_idx,
|
|
+ u16 rate_idx);
|
|
+int bl_send_sm_connect_req(struct bl_hw *bl_hw,
|
|
+ struct bl_vif *bl_vif,
|
|
+ struct cfg80211_connect_params *sme,
|
|
+ struct sm_connect_cfm *cfm);
|
|
+int bl_send_sm_disconnect_req(struct bl_hw *bl_hw,
|
|
+ struct bl_vif *bl_vif,
|
|
+ u16 reason);
|
|
+int bl_send_apm_start_req(struct bl_hw *bl_hw, struct bl_vif *vif,
|
|
+ struct cfg80211_ap_settings *settings,
|
|
+ struct apm_start_cfm *cfm, struct bl_dma_elem *elem);
|
|
+int bl_send_apm_stop_req(struct bl_hw *bl_hw, struct bl_vif *vif);
|
|
+int bl_send_scanu_req(struct bl_hw *bl_hw, struct bl_vif *bl_vif,
|
|
+ struct cfg80211_scan_request *param);
|
|
+int bl_send_apm_start_cac_req(struct bl_hw *bl_hw, struct bl_vif *vif,
|
|
+ struct cfg80211_chan_def *chandef,
|
|
+ struct apm_start_cac_cfm *cfm);
|
|
+int bl_send_apm_stop_cac_req(struct bl_hw *bl_hw, struct bl_vif *vif);
|
|
+/* Debug messages */
|
|
+int bl_send_dbg_trigger_req(struct bl_hw *bl_hw, char *msg);
|
|
+int bl_send_dbg_mem_read_req(struct bl_hw *bl_hw, u32 mem_addr,
|
|
+ struct dbg_mem_read_cfm *cfm);
|
|
+int bl_send_dbg_mem_write_req(struct bl_hw *bl_hw, u32 mem_addr,
|
|
+ u32 mem_data);
|
|
+int bl_send_dbg_set_mod_filter_req(struct bl_hw *bl_hw, u32 filter);
|
|
+int bl_send_dbg_set_sev_filter_req(struct bl_hw *bl_hw, u32 filter);
|
|
+int bl_send_dbg_get_sys_stat_req(struct bl_hw *bl_hw,
|
|
+ struct dbg_get_sys_stat_cfm *cfm);
|
|
+int bl_send_cfg_rssi_req(struct bl_hw *bl_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst);
|
|
+
|
|
+#endif /* _BL_MSG_TX_H_ */
|
|
diff -Naur /dev/null_platform.c b/drivers/net/wireless/hflps170/bl_platform.c
|
|
--- /dev/null_platform.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_platform.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,137 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_platform.c
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/firmware.h>
|
|
+
|
|
+#include "bl_platform.h"
|
|
+#include "bl_irqs.h"
|
|
+#include "hal_desc.h"
|
|
+#include "bl_main.h"
|
|
+#include "bl_sdio.h"
|
|
+
|
|
+/**
|
|
+ * bl_platform_on - Start the platform
|
|
+ *
|
|
+ * @bl_hw Main driver data
|
|
+ *
|
|
+ * It starts the platform :
|
|
+ * - load fw and ucodes
|
|
+ * - initialize IPC
|
|
+ * - boot the fw
|
|
+ * - enable link communication/IRQ
|
|
+ *
|
|
+ * Called by 802.11 part
|
|
+ */
|
|
+int bl_platform_on(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct bl_plat *bl_plat = bl_hw->plat;
|
|
+ int ret;
|
|
+ int count = 0;
|
|
+ u8 fw_ready = 0;
|
|
+
|
|
+ if (bl_plat->enabled)
|
|
+ return 0;
|
|
+
|
|
+ if ((ret = bl_ipc_init(bl_hw)))
|
|
+ return ret;
|
|
+
|
|
+ //BL_DBG("start firmware...\n");
|
|
+ //bl_write_reg(bl_hw, 0x60, 1);
|
|
+ //msleep(500);
|
|
+
|
|
+ printk("Wait firmware...\n");
|
|
+ do {
|
|
+ bl_read_reg(bl_hw, 0x60, &fw_ready);
|
|
+ printk("FW Mark %c:%x\n", fw_ready, fw_ready);
|
|
+ count++;
|
|
+ msleep(100);
|
|
+ } while ('B' != fw_ready && count<50);//TODO put 'B' into header file
|
|
+ msleep(500);
|
|
+
|
|
+ if(count >= 50)
|
|
+ return -1;
|
|
+
|
|
+
|
|
+ bl_plat->enabled = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_platform_off - Stop the platform
|
|
+ *
|
|
+ * @bl_hw Main driver data
|
|
+ *
|
|
+ * Called by 802.11 part
|
|
+ */
|
|
+void bl_platform_off(struct bl_hw *bl_hw)
|
|
+{
|
|
+ if (!bl_hw->plat->enabled)
|
|
+ return;
|
|
+
|
|
+ bl_ipc_deinit(bl_hw);
|
|
+ bl_hw->plat->enabled = false;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_platform_init - Initialize the platform
|
|
+ *
|
|
+ * @bl_plat platform data (already updated by platform driver)
|
|
+ * @platform_data Pointer to store the main driver data pointer (aka bl_hw)
|
|
+ * That will be set as driver data for the platform driver
|
|
+ * @return 0 on success, < 0 otherwise
|
|
+ *
|
|
+ * Called by the platform driver after it has been probed
|
|
+ */
|
|
+int bl_platform_init(struct bl_plat *bl_plat, void **platform_data)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_plat->enabled = false;
|
|
+
|
|
+ return bl_cfg80211_init(bl_plat, platform_data);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_platform_deinit - Deinitialize the platform
|
|
+ *
|
|
+ * @bl_hw ain driver data
|
|
+ *
|
|
+ * Called by the platform driver after it is removed
|
|
+ */
|
|
+void bl_platform_deinit(struct bl_hw *bl_hw)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_cfg80211_deinit(bl_hw);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_platform_register_drv - Register all possible platform drivers
|
|
+ */
|
|
+int bl_platform_register_drv(void)
|
|
+{
|
|
+ return bl_sdio_register_drv();
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_platform_unregister_drv - Unegister all platform drivers
|
|
+ */
|
|
+void bl_platform_unregister_drv(void)
|
|
+{
|
|
+ return bl_sdio_unregister_drv();
|
|
+}
|
|
+
|
|
+MODULE_FIRMWARE(BL_AGC_FW_NAME);
|
|
+MODULE_FIRMWARE(BL_FCU_FW_NAME);
|
|
+MODULE_FIRMWARE(BL_LDPC_RAM_NAME);
|
|
+MODULE_FIRMWARE(BL_MAC_FW_NAME);
|
|
diff -Naur /dev/null_platform.h b/drivers/net/wireless/hflps170/bl_platform.h
|
|
--- /dev/null_platform.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_platform.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,89 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_platorm.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_PLAT_H_
|
|
+#define _BL_PLAT_H_
|
|
+
|
|
+#include <linux/mmc/sdio.h>
|
|
+#include <linux/mmc/sdio_ids.h>
|
|
+#include <linux/mmc/sdio_func.h>
|
|
+#include <linux/mmc/card.h>
|
|
+#include <linux/mmc/host.h>
|
|
+
|
|
+#define BL_CONFIG_FW_NAME "bl_settings.ini"
|
|
+#define BL_PHY_CONFIG_TRD_NAME "bl_trident.ini"
|
|
+#define BL_PHY_CONFIG_KARST_NAME "bl_karst.ini"
|
|
+#define BL_AGC_FW_NAME "agcram.bin"
|
|
+#define BL_LDPC_RAM_NAME "ldpcram.bin"
|
|
+#define BL_MAC_FW_NAME "fmacfw.bin"
|
|
+
|
|
+#define BL_FCU_FW_NAME "fcuram.bin"
|
|
+
|
|
+/**
|
|
+ * Type of memory to access (cf bl_plat.get_address)
|
|
+ *
|
|
+ * @BL_ADDR_CPU To access memory of the embedded CPU
|
|
+ * @BL_ADDR_SYSTEM To access memory/registers of one subsystem of the
|
|
+ * embedded system
|
|
+ *
|
|
+ */
|
|
+enum bl_platform_addr {
|
|
+ BL_ADDR_CPU,
|
|
+ BL_ADDR_SYSTEM,
|
|
+ BL_ADDR_MAX,
|
|
+};
|
|
+
|
|
+struct bl_hw;
|
|
+
|
|
+/**
|
|
+ * @pci_dev pointer to pci dev
|
|
+ * @enabled Set if embedded platform has been enabled (i.e. fw loaded and
|
|
+ * ipc started)
|
|
+ * @enable Configure communication with the fw (i.e. configure the transfers
|
|
+ * enable and register interrupt)
|
|
+ * @disable Stop communication with the fw
|
|
+ * @deinit Free all ressources allocated for the embedded platform
|
|
+ * @get_address Return the virtual address to access the requested address on
|
|
+ * the platform.
|
|
+ * @ack_irq Acknowledge the irq at link level.
|
|
+ *
|
|
+ * @priv Private data for the link driver
|
|
+ */
|
|
+struct bl_plat {
|
|
+ struct sdio_func *func;
|
|
+ bool enabled;
|
|
+
|
|
+ u8 *mp_regs;
|
|
+ u32 mp_rd_bitmap;
|
|
+ u32 mp_wr_bitmap;
|
|
+
|
|
+ u8 curr_rd_port;
|
|
+ u8 curr_wr_port;
|
|
+
|
|
+ u32 io_port;
|
|
+
|
|
+ u8 priv[0] __aligned(sizeof(void *));
|
|
+};
|
|
+
|
|
+int bl_platform_init(struct bl_plat *bl_plat, void **platform_data);
|
|
+void bl_platform_deinit(struct bl_hw *bl_hw);
|
|
+
|
|
+int bl_platform_on(struct bl_hw *bl_hw);
|
|
+void bl_platform_off(struct bl_hw *bl_hw);
|
|
+
|
|
+int bl_platform_register_drv(void);
|
|
+void bl_platform_unregister_drv(void);
|
|
+
|
|
+static inline struct device *bl_platform_get_dev(struct bl_plat *bl_plat)
|
|
+{
|
|
+ return &(bl_plat->func->dev);
|
|
+}
|
|
+
|
|
+#endif /* _BL_PLAT_H_ */
|
|
diff -Naur /dev/null_sdio.c b/drivers/net/wireless/hflps170/bl_sdio.c
|
|
--- /dev/null_sdio.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_sdio.c 2020-12-28 12:36:56.000000000 +0200
|
|
@@ -0,0 +1,1437 @@
|
|
+#include <linux/module.h>
|
|
+#include <linux/mmc/sdio.h>
|
|
+#include <linux/mmc/sdio_ids.h>
|
|
+#include <linux/mmc/sdio_func.h>
|
|
+#include <linux/mmc/card.h>
|
|
+#include <linux/mmc/host.h>
|
|
+
|
|
+#include <linux/firmware.h>
|
|
+
|
|
+#include "bl_defs.h"
|
|
+#include "bl_v7.h"
|
|
+
|
|
+#include "bl_sdio.h"
|
|
+#include "bl_bootrom.h"
|
|
+
|
|
+
|
|
+#define SDIO_VENDOR_ID_BFL 0x424c
|
|
+#define SD_DEVICE_ID_BFL 0x0606
|
|
+
|
|
+
|
|
+#define BOOTROM_SDIO_PORT_USED 0x0001
|
|
+#define BOOTROM_CMD_BUFFER_SIZE (1024 * 2)
|
|
+#define BOOTROM_DATA_BUFFER_SIZE (512 * 2 * 2)
|
|
+
|
|
+#define BOOTROM_SDIO_TEST_READ_BLOCK_COUNT (4 * 8)
|
|
+#define BOOTROM_SDIO_TEST_READ_LOOP_COUNT (1024 * 100)
|
|
+#define BOOTROM_SDIO_TEST_WRITE_BLOCK_COUNT (4 * 8)
|
|
+#define BOOTROM_SDIO_TEST_WRITE_LOOP_COUNT (1024 * 100)
|
|
+static int count = 0;
|
|
+unsigned long time_start, time_end, time_diff, time_ms;
|
|
+extern unsigned long volatile jiffies;
|
|
+
|
|
+
|
|
+static const struct sdio_device_id bl_sdio_ids[] = {
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BFL, SD_DEVICE_ID_BFL)},
|
|
+ {0,}
|
|
+};
|
|
+MODULE_DEVICE_TABLE(sdio, bl_sdio_ids);
|
|
+
|
|
+void bl_get_rd_len(struct bl_hw *bl_hw, u32 reg_l, u32 reg_u, u32 *len)
|
|
+{
|
|
+ u8 rd_len_l = 0;
|
|
+ u8 rd_len_u = 0;
|
|
+
|
|
+ bl_read_reg(bl_hw, reg_l, &rd_len_l);
|
|
+ bl_read_reg(bl_hw, reg_u, &rd_len_u);
|
|
+
|
|
+ *len = (rd_len_u << 8) + rd_len_l;
|
|
+}
|
|
+/*
|
|
+ * This function gets the read port.
|
|
+ *
|
|
+ * If control port bit is set in MP read bitmap, the control port
|
|
+ * is returned, otherwise the current read port is returned and
|
|
+ * the value is increased (provided it does not reach the maximum
|
|
+ * limit, in which case it is reset to 1)
|
|
+ */
|
|
+int bl_get_rd_port(struct bl_hw *bl_hw, u32 *port)
|
|
+{
|
|
+ struct bl_device *bl_device;
|
|
+ u32 rd_bitmap = bl_hw->plat->mp_rd_bitmap;
|
|
+ bl_device = (struct bl_device *)(bl_hw->plat->priv);
|
|
+
|
|
+ if (!(rd_bitmap & (CTRL_PORT_MASK | bl_device->reg->data_port_mask))) {
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ if ((bl_device->has_control_mask) && (bl_hw->plat->mp_rd_bitmap & CTRL_PORT_MASK)) {
|
|
+ bl_hw->plat->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK);
|
|
+ *port = CTRL_PORT;
|
|
+ BL_DBG("ctrl: port=%d rd_bitmap=0x%08x -> 0x%08x\n",
|
|
+ *port, rd_bitmap, bl_hw->plat->mp_rd_bitmap);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!(bl_hw->plat->mp_rd_bitmap & (1 << bl_hw->plat->curr_rd_port)))
|
|
+ return -1;
|
|
+
|
|
+ /* We are now handling the SDIO data ports */
|
|
+ bl_hw->plat->mp_rd_bitmap &= (u32)(~(1 << bl_hw->plat->curr_rd_port));
|
|
+ *port = bl_hw->plat->curr_rd_port;
|
|
+
|
|
+ if (++(bl_hw->plat->curr_rd_port) == MAX_PORT_NUM)
|
|
+ bl_hw->plat->curr_rd_port = bl_device->reg->start_rd_port;
|
|
+
|
|
+ BL_DBG("data: port=%d rd_bitmap=0x%08x -> 0x%08x\n",
|
|
+ *port, rd_bitmap, bl_hw->plat->mp_rd_bitmap);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function gets the write port for data.
|
|
+ *
|
|
+ * The current write port is returned if available and the value is
|
|
+ * increased (provided it does not reach the maximum limit, in which
|
|
+ * case it is reset to 1)
|
|
+ */
|
|
+int bl_get_wr_port(struct bl_hw *bl_hw, u32 *port)
|
|
+{
|
|
+ struct bl_device *bl_device;
|
|
+ u32 wr_bitmap = bl_hw->plat->mp_wr_bitmap;
|
|
+ bl_device = (struct bl_device *)(bl_hw->plat->priv);
|
|
+
|
|
+ if (!(wr_bitmap & bl_device->reg->data_port_mask)) {
|
|
+ printk("no available port\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ if (wr_bitmap & (1 << bl_hw->plat->curr_wr_port)) {
|
|
+ bl_hw->plat->mp_wr_bitmap &= (u32) (~(1 << bl_hw->plat->curr_wr_port));
|
|
+ *port = bl_hw->plat->curr_wr_port;
|
|
+ if (++(bl_hw->plat->curr_wr_port) == MAX_PORT_NUM)
|
|
+ bl_hw->plat->curr_wr_port = bl_device->reg->start_wr_port;
|
|
+ } else {
|
|
+ printk("no available port\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ BL_DBG("data: port=%d wr_bitmap=0x%08x -> 0x%08x\n",
|
|
+ *port, wr_bitmap, bl_hw->plat->mp_wr_bitmap);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function writes multiple data into SDIO card memory.
|
|
+ *
|
|
+ * This does not work in suspended mode.
|
|
+ */
|
|
+int bl_write_data_sync_dnldfw(struct bl_plat *bl_hw, u8 *buffer, u32 pkt_len, u32 port)
|
|
+{
|
|
+ int ret;
|
|
+ u8 blk_mode = (pkt_len < BL_SDIO_BLOCK_SIZE) ? BYTE_MODE : BLOCK_MODE;
|
|
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? BL_SDIO_BLOCK_SIZE : 1;
|
|
+ u32 blk_cnt = (blk_mode == BLOCK_MODE) ? ((pkt_len + BL_SDIO_BLOCK_SIZE -1)/BL_SDIO_BLOCK_SIZE) : pkt_len;
|
|
+
|
|
+ u32 ioport = (port & BL_SDIO_IO_PORT_MASK);
|
|
+
|
|
+ sdio_claim_host(bl_hw->func);
|
|
+ ret = sdio_writesb(bl_hw->func, ioport, buffer, blk_cnt * blk_size);
|
|
+ sdio_release_host(bl_hw->func);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function reads multiple data from SDIO card memory.
|
|
+ */
|
|
+int bl_read_data_sync_dnldfw(struct bl_plat *bl_hw, u8 *buffer, u32 len, u32 port)
|
|
+{
|
|
+ int ret;
|
|
+ u8 blk_mode = (len < BL_SDIO_BLOCK_SIZE) ? BYTE_MODE : BLOCK_MODE;
|
|
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? BL_SDIO_BLOCK_SIZE : 1;
|
|
+ u32 blk_cnt = (blk_mode == BLOCK_MODE) ? ((len + BL_SDIO_BLOCK_SIZE -1)/BL_SDIO_BLOCK_SIZE) : len;
|
|
+ u32 ioport = (port & BL_SDIO_IO_PORT_MASK);
|
|
+
|
|
+ sdio_claim_host(bl_hw->func);
|
|
+ ret = sdio_readsb(bl_hw->func, buffer, ioport, blk_cnt * blk_size);
|
|
+ sdio_release_host(bl_hw->func);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * This function reads data from SDIO card register.
|
|
+ */
|
|
+static int bl_sdio_read_reg(struct bl_plat *card, u32 reg, u8 *data)
|
|
+{
|
|
+ int ret = -1;
|
|
+ u8 val;
|
|
+
|
|
+ val = sdio_readb(card->func, reg, &ret);
|
|
+
|
|
+ *data = val;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function write data to SDIO card register.
|
|
+ */
|
|
+static int bl_sdio_write_reg(struct bl_plat *card, u32 reg, u8 data)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ sdio_writeb(card->func, data, reg, &ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+//TODO TIMEOUT value
|
|
+static int bl_io_write(struct bl_plat *card, u8* data, int len)
|
|
+{
|
|
+ u8 wr_bitmap_l = 0;
|
|
+ u8 wr_bitmap_u = 0;
|
|
+ u32 bitmap = 0;
|
|
+ int ret;
|
|
+
|
|
+ //SDIO_DBG(SDIO_FN_ENTRY_STR);
|
|
+
|
|
+#if 0
|
|
+ data[len - 1] = 0xFF;
|
|
+ data[len - 2] = 0xFE;
|
|
+ data[len - 3] = 0xFD;
|
|
+ data[len - 4] = 0xFC;
|
|
+ data[len - 5] = 0xFB;
|
|
+ data[len - 6] = 0xFA;
|
|
+#endif
|
|
+ count++;
|
|
+
|
|
+ while (0 == (bitmap & (1 << card->curr_wr_port))) {
|
|
+ bl_sdio_read_reg(card, WR_BITMAP_L, &wr_bitmap_l);
|
|
+ bl_sdio_read_reg(card, WR_BITMAP_U, &wr_bitmap_u);
|
|
+ bitmap = wr_bitmap_l;
|
|
+ bitmap |= wr_bitmap_u << 8;
|
|
+#if 0
|
|
+ printk("bl_io_write: wr_bitmap=0x%x, count %d, curr_wr_port %d\n",
|
|
+ bitmap, count, card->curr_wr_port);
|
|
+#endif
|
|
+ //msleep(500);
|
|
+ }
|
|
+ printk("bl_io_write: wr_bitmap=0x%x, count %d\n", bitmap, count);
|
|
+ ret = bl_write_data_sync_dnldfw(card, data, len, card->io_port + card->curr_wr_port);
|
|
+#if 0
|
|
+ if ((++(card->curr_wr_port)) == 4) {
|
|
+ card->curr_wr_port = 0;
|
|
+ }
|
|
+#endif
|
|
+ //printk("%s: ret %d\n", __func__, ret);
|
|
+
|
|
+ //SDIO_DBG(SDIO_FN_LEAVE_STR);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+//caller must free the memory
|
|
+int bl_io_read(struct bl_plat *card, u8 *buf, int rd_len)
|
|
+{
|
|
+ u8 rd_bitmap_l = 0;
|
|
+ u8 rd_bitmap_u = 0;
|
|
+ u32 bitmap = 0;
|
|
+
|
|
+#if 0
|
|
+ SDIO_DBG(SDIO_FN_ENTRY_STR);
|
|
+#endif
|
|
+
|
|
+#if 1
|
|
+ while (0 == (bitmap & (1 << card->curr_rd_port))) {
|
|
+ bl_sdio_read_reg(card, RD_BITMAP_L, &rd_bitmap_l);
|
|
+ bl_sdio_read_reg(card, RD_BITMAP_U, &rd_bitmap_u);
|
|
+ bitmap = rd_bitmap_l;
|
|
+ bitmap |= rd_bitmap_u << 8;
|
|
+ card->mp_rd_bitmap = bitmap;
|
|
+#if 0
|
|
+ printk("int: UPLD: rd_bitmap=0x%x\n", card->mp_rd_bitmap);
|
|
+#endif
|
|
+ //msleep(500);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+#if 1
|
|
+ bl_read_data_sync_dnldfw(card, buf, rd_len, card->io_port + card->curr_rd_port);
|
|
+#endif
|
|
+#if 1
|
|
+ printk("[XXX] len:%d--->receive ok\n", rd_len);
|
|
+ printk("Read 00: %02X %02X %02X %02X %02X %02X %02X %02X\n"
|
|
+ "Read 10: %02X %02X %02X %02X %02X %02X %02X %02X\n"
|
|
+ "Read 20: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
+ buf[0], buf[1],
|
|
+ buf[2], buf[3],
|
|
+ buf[4], buf[5],
|
|
+ buf[6], buf[7],
|
|
+ buf[8 + 0], buf[8 + 1],
|
|
+ buf[8 + 2], buf[8 + 3],
|
|
+ buf[8 + 4], buf[8 + 5],
|
|
+ buf[8 + 6], buf[8 + 7],
|
|
+ buf[16 + 0], buf[16 + 1],
|
|
+ buf[16 + 2], buf[16 + 3],
|
|
+ buf[16 + 4], buf[16 + 5],
|
|
+ buf[16 + 6], buf[16 + 7]
|
|
+ );
|
|
+#endif
|
|
+#if 0
|
|
+ if ((++(card->curr_rd_port)) == 4) {
|
|
+ card->curr_rd_port = 0;
|
|
+ }
|
|
+#endif
|
|
+#if 0
|
|
+ SDIO_DBG(SDIO_FN_LEAVE_STR);
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int bl_sdio_run_fw(struct bl_plat *card)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ printk("Enter bl_sdio_run_fw...\n");
|
|
+ ret = bl_sdio_write_reg(card, CARD_FW_STATUS0_REG, 0x1);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+}
|
|
+
|
|
+static int firmware_dump_head(bootheader_t *header)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ printk("[************Header************]\n");
|
|
+ printk(" magiccode: 0x%X\n", header->magiccode);
|
|
+ printk(" rivison: 0x%X\n", header->rivison);
|
|
+ printk(" flashCfg\n");
|
|
+ printk(" magiccode: 0x%X\n", header->flashCfg.magiccode);
|
|
+ printk(" cfg see below, size: %lu\n", sizeof(header->flashCfg.cfg));
|
|
+ printk(" crc32: 0x%X\n", header->flashCfg.crc32);
|
|
+ printk(" pllCfg\n");
|
|
+ printk(" magiccode: 0x%X\n", header->pllCfg.magiccode);
|
|
+ printk(" root_clk: %u\n", header->pllCfg.root_clk);
|
|
+ printk(" xtal_type: %u\n", header->pllCfg.xtal_type);
|
|
+ printk(" pll_clk: %u\n", header->pllCfg.pll_clk);
|
|
+ printk(" hclk_div: %u\n", header->pllCfg.hclk_div);
|
|
+ printk(" bclk_div: %u\n", header->pllCfg.bclk_div);
|
|
+ printk(" flash_clk_div: %u\n", header->pllCfg.flash_clk_div);
|
|
+ printk(" uart_clk_div: %u\n", header->pllCfg.uart_clk_div);
|
|
+ printk(" sdu_clk_div: %u\n", header->pllCfg.sdu_clk_div);
|
|
+ printk(" crc32: 0x%X\n", header->pllCfg.crc32);
|
|
+ printk(" seccfg\n");
|
|
+ printk(" bval in union\n");
|
|
+ printk(" encrypt: %u\n", header->seccfg.bval.encrypt);
|
|
+ printk(" sign: %u\n", header->seccfg.bval.sign);
|
|
+ printk(" key_sel: %u\n", header->seccfg.bval.key_sel);
|
|
+ printk(" rsvd6_31: %u\n", header->seccfg.bval.rsvd6_31);
|
|
+ printk(" wval in union\n");
|
|
+ printk(" wval: 0x%X\n", header->seccfg.wval);
|
|
+ printk(" segment_cnt: %u\n", header->segment_cnt);
|
|
+ printk(" bootentry: 0x%X\n", header->bootentry);
|
|
+ printk(" flashoffset: 0x%X\n", header->flashoffset);
|
|
+ printk(" bootentry: 0x%X\n", header->bootentry);
|
|
+ printk(" hash:\n");
|
|
+
|
|
+ for (i = 0; i < sizeof(header->hash); i+=4) {
|
|
+ //XXX we may access over-flow
|
|
+ printk("0x%08X ", ((uint32_t)header->hash[i]) << 24 |
|
|
+ (uint32_t)header->hash[i + 1] << 16 |
|
|
+ (uint32_t)header->hash[i + 2] << 8 |
|
|
+ (uint32_t)header->hash[i + 3] << 0
|
|
+ );
|
|
+ }
|
|
+
|
|
+
|
|
+ printk(" rsv1: %u\n", header->rsv1);
|
|
+ printk(" rsv2: %u\n", header->rsv2);
|
|
+ printk(" crc32: 0x%X\n", header->crc32);
|
|
+ printk("--------\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_dump_flashcfg(const boot_flash_cfg_t *cfg)
|
|
+{
|
|
+ printk("[************FLASH CFG(%lu)************]\n", sizeof(boot_flash_cfg_t));
|
|
+ printk(" magiccode: 0x%X\n", cfg->magiccode);
|
|
+ printk(" flash cfg\n");
|
|
+ printk(" ioMode: %u\n", cfg->cfg.ioMode);
|
|
+ printk(" cReadSupport: %u\n", cfg->cfg.cReadSupport);
|
|
+ printk(" clk_delay;: %u\n", cfg->cfg.clk_delay);
|
|
+ printk(" rsvd[1]: %X\n", cfg->cfg.rsvd[0]);
|
|
+ printk(" resetEnCmd: %u\n", cfg->cfg.resetEnCmd);
|
|
+ printk(" resetCmd: %u\n", cfg->cfg.resetCmd);
|
|
+ printk(" resetCreadCmd: %u\n", cfg->cfg.resetCreadCmd);
|
|
+ printk(" rsvd_reset[1]: %X\n", cfg->cfg.rsvd_reset[0]);
|
|
+ printk(" jedecIdCmd: %u(0x%X)\n", cfg->cfg.jedecIdCmd, cfg->cfg.jedecIdCmd);
|
|
+ printk(" jedecIdCmdDmyClk: %u(0x%X)\n", cfg->cfg.jedecIdCmdDmyClk, cfg->cfg.jedecIdCmdDmyClk);
|
|
+ printk(" qpiJedecIdCmd: %u(0x%X)\n", cfg->cfg.qpiJedecIdCmd, cfg->cfg.qpiJedecIdCmd);
|
|
+ printk(" qpiJedecIdCmdDmyClk: %u(0x%X)\n", cfg->cfg.qpiJedecIdCmdDmyClk, cfg->cfg.qpiJedecIdCmdDmyClk);
|
|
+ printk(" sectorSize: %u(0x%X)\n", cfg->cfg.sectorSize, cfg->cfg.sectorSize);
|
|
+ printk(" capBase: %u(0x%X)\n", cfg->cfg.capBase, cfg->cfg.capBase);
|
|
+ printk(" pageSize: %u(0x%X)\n", cfg->cfg.pageSize, cfg->cfg.pageSize);
|
|
+ printk(" chipEraseCmd: %u(0x%X)\n", cfg->cfg.chipEraseCmd, cfg->cfg.chipEraseCmd);
|
|
+ printk(" sectorEraseCmd: %u(0x%X)\n", cfg->cfg.sectorEraseCmd, cfg->cfg.sectorEraseCmd);
|
|
+ printk(" blk32EraseCmd: %u(0x%X)\n", cfg->cfg.blk32EraseCmd, cfg->cfg.blk32EraseCmd);
|
|
+ printk(" blk64EraseCmd: %u(0x%X)\n", cfg->cfg.blk64EraseCmd, cfg->cfg.blk64EraseCmd);
|
|
+ printk(" writeEnableCmd: %u(0x%X)\n", cfg->cfg.writeEnableCmd, cfg->cfg.writeEnableCmd);
|
|
+ printk(" pageProgramCmd: %u(0x%X)\n", cfg->cfg.pageProgramCmd, cfg->cfg.pageProgramCmd);
|
|
+ printk(" qpageProgramCmd: %u(0x%X)\n", cfg->cfg.qpageProgramCmd, cfg->cfg.qpageProgramCmd);
|
|
+ printk(" qppAddrMode: %u(0x%X)\n", cfg->cfg.qppAddrMode, cfg->cfg.qppAddrMode);
|
|
+ printk(" fastReadCmd: %u(0x%X)\n", cfg->cfg.fastReadCmd, cfg->cfg.fastReadCmd);
|
|
+ printk(" frDmyClk: %u(0x%X)\n", cfg->cfg.frDmyClk, cfg->cfg.frDmyClk);
|
|
+ printk(" qpiFastReadCmd: %u(0x%X)\n", cfg->cfg.qpiFastReadCmd, cfg->cfg.qpiFastReadCmd);
|
|
+ printk(" qpiFrDmyClk: %u(0x%X)\n", cfg->cfg.qpiFrDmyClk, cfg->cfg.qpiFrDmyClk);
|
|
+ printk(" fastReadDoCmd: %u(0x%X)\n", cfg->cfg.fastReadDoCmd, cfg->cfg.fastReadDoCmd);
|
|
+ printk(" frDoDmyClk: %u(0x%X)\n", cfg->cfg.frDoDmyClk, cfg->cfg.frDoDmyClk);
|
|
+ printk(" fastReadDioCmd: %u(0x%X)\n", cfg->cfg.fastReadDioCmd, cfg->cfg.fastReadDioCmd);
|
|
+ printk(" frDioDmyClk: %u(0x%X)\n", cfg->cfg.frDioDmyClk, cfg->cfg.frDioDmyClk);
|
|
+ printk(" fastReadQoCmd: %u(0x%X)\n", cfg->cfg.fastReadQoCmd, cfg->cfg.fastReadQoCmd);
|
|
+ printk(" frQoDmyClk: %u(0x%X)\n", cfg->cfg.frQoDmyClk, cfg->cfg.frQoDmyClk);
|
|
+ printk(" fastReadQioCmd: %u(0x%X)\n", cfg->cfg.fastReadQioCmd, cfg->cfg.fastReadQioCmd);
|
|
+ printk(" frQioDmyClk: %u(0x%X)\n", cfg->cfg.frQioDmyClk, cfg->cfg.frQioDmyClk);
|
|
+ printk(" qpiFastReadQioCmd: %u(0x%X)\n", cfg->cfg.qpiFastReadQioCmd, cfg->cfg.qpiFastReadQioCmd);
|
|
+ printk(" qpiFrQioDmyClk: %u(0x%X)\n", cfg->cfg.qpiFrQioDmyClk, cfg->cfg.qpiFrQioDmyClk);
|
|
+ printk(" qpiPageProgramCmd: %u(0x%X)\n", cfg->cfg.qpiPageProgramCmd, cfg->cfg.qpiPageProgramCmd);
|
|
+ printk(" writeVregEnableCmd: %u(0x%X)\n", cfg->cfg.writeVregEnableCmd, cfg->cfg.writeVregEnableCmd);
|
|
+ printk(" wrEnableIndex: %u(0x%X)\n", cfg->cfg.wrEnableIndex, cfg->cfg.wrEnableIndex);
|
|
+ printk(" qeIndex: %u(0x%X)\n", cfg->cfg.qeIndex, cfg->cfg.qeIndex);
|
|
+ printk(" busyIndex: %u(0x%X)\n", cfg->cfg.busyIndex, cfg->cfg.busyIndex);
|
|
+ printk(" wrEnableBit: %u(0x%X)\n", cfg->cfg.wrEnableBit, cfg->cfg.wrEnableBit);
|
|
+ printk(" qeBit: %u(0x%X)\n", cfg->cfg.qeBit, cfg->cfg.qeBit);
|
|
+ printk(" busyBit: %u(0x%X)\n", cfg->cfg.busyBit, cfg->cfg.busyBit);
|
|
+ printk(" wrEnableWriteRegLen: %u(0x%X)\n", cfg->cfg.wrEnableWriteRegLen, cfg->cfg.wrEnableWriteRegLen);
|
|
+ printk(" wrEnableReadRegLen: %u(0x%X)\n", cfg->cfg.wrEnableReadRegLen, cfg->cfg.wrEnableReadRegLen);
|
|
+ printk(" qeWriteRegLen: %u(0x%X)\n", cfg->cfg.qeWriteRegLen, cfg->cfg.qeWriteRegLen);
|
|
+ printk(" qeReadRegLen: %u(0x%X)\n", cfg->cfg.qeReadRegLen, cfg->cfg.qeReadRegLen);
|
|
+ printk(" rsvd1: %u(0x%X)\n", cfg->cfg.rsvd1, cfg->cfg.rsvd1);
|
|
+ printk(" busyReadRegLen: %u(0x%X)\n", cfg->cfg.busyReadRegLen, cfg->cfg.busyReadRegLen);
|
|
+ printk(" readRegCmd[4]: %u(0x%X), %u(0x%X), %u(0x%X), %u(0x%X)\n",
|
|
+ cfg->cfg.readRegCmd[0], cfg->cfg.readRegCmd[0],
|
|
+ cfg->cfg.readRegCmd[1], cfg->cfg.readRegCmd[1],
|
|
+ cfg->cfg.readRegCmd[2], cfg->cfg.readRegCmd[2],
|
|
+ cfg->cfg.readRegCmd[3], cfg->cfg.readRegCmd[3]
|
|
+ );
|
|
+ printk(" writeRegCmd[4]: %u(0x%X), %u(0x%X), %u(0x%X), %u(0x%X)\n",
|
|
+ cfg->cfg.writeRegCmd[0], cfg->cfg.writeRegCmd[0],
|
|
+ cfg->cfg.writeRegCmd[1], cfg->cfg.writeRegCmd[1],
|
|
+ cfg->cfg.writeRegCmd[2], cfg->cfg.writeRegCmd[2],
|
|
+ cfg->cfg.writeRegCmd[3], cfg->cfg.writeRegCmd[3]
|
|
+ );
|
|
+ printk(" enterQpi: %u(0x%X)\n", cfg->cfg.enterQpi, cfg->cfg.enterQpi);
|
|
+ printk(" exitQpi: %u(0x%X)\n", cfg->cfg.exitQpi, cfg->cfg.exitQpi);
|
|
+ printk(" cReadMode: %u(0x%X)\n", cfg->cfg.cReadMode, cfg->cfg.cReadMode);
|
|
+ printk(" cRExit: %u(0x%X)\n", cfg->cfg.cRExit, cfg->cfg.cRExit);
|
|
+ printk(" burstWrapCmd: %u(0x%X)\n", cfg->cfg.burstWrapCmd, cfg->cfg.burstWrapCmd);
|
|
+ printk(" burstWrapCmdDmyClk: %u(0x%X)\n", cfg->cfg.burstWrapCmdDmyClk, cfg->cfg.burstWrapCmdDmyClk);
|
|
+ printk(" burstWrapDataMode: %u(0x%X)\n", cfg->cfg.burstWrapDataMode, cfg->cfg.burstWrapDataMode);
|
|
+ printk(" burstWrapData: %u(0x%X)\n", cfg->cfg.burstWrapData, cfg->cfg.burstWrapData);
|
|
+ printk(" deBurstWrapCmd: %u(0x%X)\n", cfg->cfg.deBurstWrapCmd, cfg->cfg.deBurstWrapCmd);
|
|
+ printk(" deBurstWrapCmdDmyClk: %u(0x%X)\n", cfg->cfg.deBurstWrapCmdDmyClk, cfg->cfg.deBurstWrapCmdDmyClk);
|
|
+ printk(" deBurstWrapDataMode: %u(0x%X)\n", cfg->cfg.deBurstWrapDataMode, cfg->cfg.deBurstWrapDataMode);
|
|
+ printk(" deBurstWrapData: %u(0x%X)\n", cfg->cfg.deBurstWrapData, cfg->cfg.deBurstWrapData);
|
|
+ printk(" timeEsector: %u(0x%X)\n", cfg->cfg.timeEsector, cfg->cfg.timeEsector);
|
|
+ printk(" timeE32k: %u(0x%X)\n", cfg->cfg.timeE32k, cfg->cfg.timeE32k);
|
|
+ printk(" timeE64k: %u(0x%X)\n", cfg->cfg.timeE64k, cfg->cfg.timeE64k);
|
|
+ printk(" timePagePgm: %u(0x%X)\n", cfg->cfg.timePagePgm, cfg->cfg.timePagePgm);
|
|
+ printk(" timeCe: %u(0x%X)\n", cfg->cfg.timeCe, cfg->cfg.timeCe);
|
|
+ printk(" CRC32(%lu): %02X%02X%02X%02X\n",
|
|
+ sizeof(cfg->crc32),
|
|
+ (cfg->crc32 >> 24) & 0xFF,
|
|
+ (cfg->crc32 >> 16) & 0xFF,
|
|
+ (cfg->crc32 >> 8) & 0xFF,
|
|
+ (cfg->crc32 >> 0) & 0xFF
|
|
+ );
|
|
+ printk("--------\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_dump_pllcfg(const boot_pll_cfg_t *cfg)
|
|
+{
|
|
+ printk("[************PLL CFG(%lu)************]\n", sizeof(boot_pll_cfg_t));
|
|
+ printk(" magiccode: 0x%08X\n", cfg->magiccode);
|
|
+ printk(" root_clk: %u(0x%X)\n", cfg->root_clk, cfg->root_clk);
|
|
+ printk(" xtal_type: %u(0x%X)\n", cfg->xtal_type, cfg->xtal_type);
|
|
+ printk(" pll_clk: %u(0x%X)\n", cfg->pll_clk, cfg->pll_clk);
|
|
+ printk(" hclk_div: %u(0x%X)\n", cfg->hclk_div, cfg->hclk_div);
|
|
+ printk(" bclk_div: %u(0x%X)\n", cfg->bclk_div, cfg->bclk_div);
|
|
+ printk(" flash_clk_div: %u(0x%X)\n", cfg->flash_clk_div, cfg->flash_clk_div);
|
|
+ printk(" uart_clk_div: %u(0x%X)\n", cfg->uart_clk_div, cfg->uart_clk_div);
|
|
+ printk(" sdu_clk_div: %u(0x%X)\n", cfg->sdu_clk_div, cfg->sdu_clk_div);
|
|
+ printk(" CRC32(%lu): %02X%02X%02X%02X\n",
|
|
+ sizeof(cfg->crc32),
|
|
+ (cfg->crc32 >> 24) & 0xFF,
|
|
+ (cfg->crc32 >> 16) & 0xFF,
|
|
+ (cfg->crc32 >> 8) & 0xFF,
|
|
+ (cfg->crc32 >> 0) & 0xFF
|
|
+ );
|
|
+ printk("--------\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_dump_encryptiondata(const u8 *encryption)
|
|
+{
|
|
+ //FIXME NOT use magic number
|
|
+ printk("[************ENCRYPTION DATA (20)************]\n");
|
|
+ printk(" IV(16): %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n",
|
|
+ encryption[0],
|
|
+ encryption[1],
|
|
+ encryption[2],
|
|
+ encryption[3],
|
|
+ encryption[4],
|
|
+ encryption[5],
|
|
+ encryption[6],
|
|
+ encryption[7],
|
|
+ encryption[8],
|
|
+ encryption[9],
|
|
+ encryption[10],
|
|
+ encryption[11],
|
|
+ encryption[12],
|
|
+ encryption[13],
|
|
+ encryption[14],
|
|
+ encryption[15]
|
|
+ );
|
|
+ printk(" CRC32(4): %02X%02X%02X%02X\n",
|
|
+ encryption[19],
|
|
+ encryption[18],
|
|
+ encryption[17],
|
|
+ encryption[16]
|
|
+ );
|
|
+ printk("--------\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_dump_publickey(const pkey_cfg_t *key)
|
|
+{
|
|
+ printk("[************PUBLIC KEY DATA(%lu)************]\n", sizeof(pkey_cfg_t));
|
|
+ printk(" public key X(%lu):\n", sizeof(key->eckeyx));
|
|
+ printk(" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n",
|
|
+ key->eckeyx[0],
|
|
+ key->eckeyx[1],
|
|
+ key->eckeyx[2],
|
|
+ key->eckeyx[3],
|
|
+ key->eckeyx[4],
|
|
+ key->eckeyx[5],
|
|
+ key->eckeyx[6],
|
|
+ key->eckeyx[7],
|
|
+ key->eckeyx[8],
|
|
+ key->eckeyx[9],
|
|
+ key->eckeyx[10],
|
|
+ key->eckeyx[11],
|
|
+ key->eckeyx[12],
|
|
+ key->eckeyx[13],
|
|
+ key->eckeyx[14],
|
|
+ key->eckeyx[15],
|
|
+ key->eckeyx[16],
|
|
+ key->eckeyx[17],
|
|
+ key->eckeyx[18],
|
|
+ key->eckeyx[19],
|
|
+ key->eckeyx[20],
|
|
+ key->eckeyx[21],
|
|
+ key->eckeyx[22],
|
|
+ key->eckeyx[23],
|
|
+ key->eckeyx[24],
|
|
+ key->eckeyx[25],
|
|
+ key->eckeyx[26],
|
|
+ key->eckeyx[27],
|
|
+ key->eckeyx[28],
|
|
+ key->eckeyx[29],
|
|
+ key->eckeyx[30],
|
|
+ key->eckeyx[31]
|
|
+ );
|
|
+ printk(" public key Y(%lu):\n", sizeof(key->eckeyy));
|
|
+ printk(" %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n",
|
|
+ key->eckeyy[0],
|
|
+ key->eckeyy[1],
|
|
+ key->eckeyy[2],
|
|
+ key->eckeyy[3],
|
|
+ key->eckeyy[4],
|
|
+ key->eckeyy[5],
|
|
+ key->eckeyy[6],
|
|
+ key->eckeyy[7],
|
|
+ key->eckeyy[8],
|
|
+ key->eckeyy[9],
|
|
+ key->eckeyy[10],
|
|
+ key->eckeyy[11],
|
|
+ key->eckeyy[12],
|
|
+ key->eckeyy[13],
|
|
+ key->eckeyy[14],
|
|
+ key->eckeyy[15],
|
|
+ key->eckeyy[16],
|
|
+ key->eckeyy[17],
|
|
+ key->eckeyy[18],
|
|
+ key->eckeyy[19],
|
|
+ key->eckeyy[20],
|
|
+ key->eckeyy[21],
|
|
+ key->eckeyy[22],
|
|
+ key->eckeyy[23],
|
|
+ key->eckeyy[24],
|
|
+ key->eckeyy[25],
|
|
+ key->eckeyy[26],
|
|
+ key->eckeyy[27],
|
|
+ key->eckeyy[28],
|
|
+ key->eckeyy[29],
|
|
+ key->eckeyy[30],
|
|
+ key->eckeyy[31]
|
|
+ );
|
|
+ printk(" CRC32(%lu): %02X%02X%02X%02X\n",
|
|
+ sizeof(key->crc32),
|
|
+ (key->crc32 >> 24) & 0xFF,
|
|
+ (key->crc32 >> 16) & 0xFF,
|
|
+ (key->crc32 >> 8) & 0xFF,
|
|
+ (key->crc32 >> 0) & 0xFF
|
|
+ );
|
|
+ printk("--------\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_dump_signdata(const sign_cfg_t *sign)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ printk("[************SIGNATURE DATA(%lu)************]\n", sizeof(sign_cfg_t) + sign->sig_len);
|
|
+ printk(" sig_len: %u(0x%X)\n", sign->sig_len, sign->sig_len);
|
|
+ printk(" signature(%u):\n", sign->sig_len);
|
|
+
|
|
+ for (i = 0; i < sign->sig_len; i+= 32) {
|
|
+ printk(" %03u: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n",
|
|
+ i,
|
|
+ sign->signature[i + 0],
|
|
+ sign->signature[i + 1],
|
|
+ sign->signature[i + 2],
|
|
+ sign->signature[i + 3],
|
|
+ sign->signature[i + 4],
|
|
+ sign->signature[i + 5],
|
|
+ sign->signature[i + 6],
|
|
+ sign->signature[i + 7],
|
|
+ sign->signature[i + 8],
|
|
+ sign->signature[i + 9],
|
|
+ sign->signature[i + 10],
|
|
+ sign->signature[i + 11],
|
|
+ sign->signature[i + 12],
|
|
+ sign->signature[i + 13],
|
|
+ sign->signature[i + 14],
|
|
+ sign->signature[i + 15],
|
|
+ sign->signature[i + 16],
|
|
+ sign->signature[i + 17],
|
|
+ sign->signature[i + 18],
|
|
+ sign->signature[i + 19],
|
|
+ sign->signature[i + 20],
|
|
+ sign->signature[i + 21],
|
|
+ sign->signature[i + 22],
|
|
+ sign->signature[i + 23],
|
|
+ sign->signature[i + 24],
|
|
+ sign->signature[i + 25],
|
|
+ sign->signature[i + 26],
|
|
+ sign->signature[i + 27],
|
|
+ sign->signature[i + 28],
|
|
+ sign->signature[i + 29],
|
|
+ sign->signature[i + 30],
|
|
+ sign->signature[i + 31]
|
|
+ );
|
|
+ }
|
|
+ printk(" CRC32(%lu): %02X%02X%02X%02X\n",
|
|
+ sizeof(sign->crc32),
|
|
+ sign->signature[sign->sig_len],
|
|
+ sign->signature[sign->sig_len + 1],
|
|
+ sign->signature[sign->sig_len + 2],
|
|
+ sign->signature[sign->sig_len + 3]
|
|
+ );
|
|
+ printk("--------\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_dump_segment_piece(const segment_header_t *segment)
|
|
+{
|
|
+ printk("[************SEGMENT DUMP(%lu + %u = %lu)************]\n",
|
|
+ sizeof(segment_header_t),
|
|
+ segment->len,
|
|
+ sizeof(segment_header_t) + segment->len
|
|
+ );
|
|
+ printk(" destaddr: %u(0x%X)\n", segment->destaddr, segment->destaddr);
|
|
+ printk(" len: %u(0x%X)\n", segment->len, segment->len);
|
|
+ printk(" rsv: %u(0x%X)\n", segment->rsv, segment->rsv);
|
|
+ printk(" crc32: %u(0x%X)\n", segment->crc32, segment->crc32);
|
|
+ printk("--------\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_head(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8* response,
|
|
+ bootheader_t *header, const u8 **data, int *len)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_ENTRY_STR);
|
|
+ printk("FUNC header: data ptr %p, data left %d\n", *data, *len);
|
|
+ if (*len < sizeof(bootheader_t)) {
|
|
+ printk("%s:len too small %lu:%d\n", __func__, sizeof(bootheader_t), *len);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ firmware_dump_head(header);
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_bootheader_load(cmd, header);
|
|
+ printk("CMD bootheader len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ bl_bootrom_cmd_bootheader_load_get_res((bootrom_res_bootheader_load_t*)response);
|
|
+
|
|
+ *data += sizeof(bootheader_t);
|
|
+ *len -= sizeof(bootheader_t);
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_LEAVE_STR);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_flashcfg(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response, bootheader_t *header)
|
|
+{
|
|
+ firmware_dump_flashcfg(&(header->flashCfg));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_pllcfg(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8* response,
|
|
+ bootheader_t *header)
|
|
+{
|
|
+ firmware_dump_pllcfg(&(header->pllCfg));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_encryptiondata(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response,
|
|
+ bootheader_t *header, const u8 **data, int *len)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_ENTRY_STR);
|
|
+
|
|
+ printk("FUNC encryptiondata: data ptr %p, data left %d\n", *data, *len);
|
|
+ if (0 == header->seccfg.bval.encrypt) {
|
|
+ printk("no encrypt field detected\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ printk("%s, Offset 0x%X\n", __func__, (uint32_t)(((u8*)*data) - ((u8*)header)));
|
|
+ //FIXME NOT use magic number
|
|
+ if (*len < 20) {
|
|
+ printk("%s:len too small %d:%d\n", __func__, 20, *len);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ firmware_dump_encryptiondata(*data);
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_aesiv_load(cmd, *data);
|
|
+ printk("CMD encryptiondata len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ bl_bootrom_cmd_aesiv_load_get_res((bootrom_res_aesiv_load_t*)response);
|
|
+
|
|
+ //FIXME NOT use magic number
|
|
+ *data += 20;
|
|
+ *len -= 20;
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_LEAVE_STR);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_publickey1(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response,
|
|
+ bootheader_t *header, const u8 **data, int *len)
|
|
+{
|
|
+ int ret;
|
|
+ pkey_cfg_t *key;
|
|
+
|
|
+ printk("FUNC publickey1: data ptr %p, data left %d\n", *data, *len);
|
|
+ if (0 == header->seccfg.bval.sign) {
|
|
+ printk("no sign field detected\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ printk("%s, Offset 0x%X\n", __func__, (uint32_t)(((u8*)*data) - ((u8*)header)));
|
|
+ key = (pkey_cfg_t*)(*data);
|
|
+ if (*len < sizeof(pkey_cfg_t)) {
|
|
+ printk("%s:len too small %lu:%d\n", __func__, sizeof(pkey_cfg_t), *len);
|
|
+ return -1;
|
|
+ }
|
|
+ firmware_dump_publickey(key);
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_pkey1_load(cmd, key);
|
|
+ printk("CMD public key1 len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_bootrom_cmd_pkey1_load_get_res((bootrom_res_pkey_load_t*)response);
|
|
+ if (ret) {
|
|
+ printk("Error response get pkey1\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ *data += sizeof(pkey_cfg_t);
|
|
+ *len -= sizeof(pkey_cfg_t);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_publickey2(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response,
|
|
+ bootheader_t *header, const u8 **data, int *len)
|
|
+{
|
|
+ int ret;
|
|
+ pkey_cfg_t *key;
|
|
+
|
|
+ printk("FUNC publickey2: data ptr %p, data left %d\n", *data, *len);
|
|
+ if (0 == header->seccfg.bval.sign) {
|
|
+ printk("no sign field detected\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ printk("%s, Offset 0x%X\n", __func__, (uint32_t)(((u8*)*data) - ((u8*)header)));
|
|
+ key = (pkey_cfg_t*)(*data);
|
|
+ if (*len < sizeof(pkey_cfg_t)) {
|
|
+ printk("%s:len too small %lu:%d\n", __func__, sizeof(pkey_cfg_t), *len);
|
|
+ return -1;
|
|
+ }
|
|
+ firmware_dump_publickey(key);
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_pkey2_load(cmd, key);
|
|
+ printk("CMD public key2 len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_bootrom_cmd_pkey2_load_get_res((bootrom_res_pkey_load_t*)response);
|
|
+ if (ret) {
|
|
+ printk("Error response get pkey2\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ *data += sizeof(pkey_cfg_t);
|
|
+ *len -= sizeof(pkey_cfg_t);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_signdata1(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response,
|
|
+ bootheader_t *header, const u8 **data, int *len)
|
|
+{
|
|
+ int ret;
|
|
+ sign_cfg_t *sign;
|
|
+
|
|
+ printk("FUNC signdata1: data ptr %p, data left %d\n", *data, *len);
|
|
+ if (0 == header->seccfg.bval.sign) {
|
|
+ printk("no sign field detected\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ printk("%s, Offset 0x%X\n", __func__, (uint32_t)(((u8*)*data) - ((u8*)header)));
|
|
+ sign = (sign_cfg_t*)(*data);
|
|
+ if (*len < sizeof(sign_cfg_t) + sign->sig_len) {
|
|
+ printk("%s:len too small %lu:%d\n", __func__, sizeof(sign_cfg_t), *len);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ firmware_dump_signdata(sign);
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_signature1_load(cmd, (uint8_t*)sign, sizeof(sign_cfg_t) + sign->sig_len);
|
|
+ printk("CMD signa data1 len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_bootrom_cmd_signature1_get_res((bootrom_res_signature_load_t*)response);
|
|
+ if (ret) {
|
|
+ printk("Error response get signature1\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ *data += (sizeof(sign_cfg_t) + sign->sig_len);
|
|
+ *len -= (sizeof(sign_cfg_t) + sign->sig_len);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_signdata2(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response,
|
|
+ bootheader_t *header, const u8 **data, int *len)
|
|
+{
|
|
+ int ret;
|
|
+ sign_cfg_t *sign;
|
|
+
|
|
+ printk("FUNC signdata2: data ptr %p, data left %d\n", *data, *len);
|
|
+ if (0 == header->seccfg.bval.sign) {
|
|
+ printk("no sign field detected\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ printk("%s, Offset 0x%X\n", __func__, (uint32_t)(((u8*)*data) - ((u8*)header)));
|
|
+ sign = (sign_cfg_t*)(*data);
|
|
+ if (*len < sizeof(sign_cfg_t) + sign->sig_len) {
|
|
+ printk("%s:len too small %lu:%d\n", __func__, sizeof(sign_cfg_t), *len);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ firmware_dump_signdata(sign);
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_signature2_load(cmd, (uint8_t*)sign, sizeof(sign_cfg_t) + sign->sig_len);
|
|
+ printk("CMD signa data2 len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_bootrom_cmd_signature2_get_res((bootrom_res_signature_load_t*)response);
|
|
+ if (ret) {
|
|
+ printk("Error response get signature2\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ *data += (sizeof(sign_cfg_t) + sign->sig_len);
|
|
+ *len -= (sizeof(sign_cfg_t) + sign->sig_len);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_segment_piece(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response,
|
|
+ bootheader_t *header, const u8 **data, int *len)
|
|
+{
|
|
+ const segment_header_t *segment;
|
|
+ segment_header_t segment_plain;
|
|
+ int ret = 0, i, pos, wr_len;
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_ENTRY_STR);
|
|
+
|
|
+ printk("FUNC segment_piece: data ptr %p, data left %d\n", *data, *len);
|
|
+ segment = (const segment_header_t*)(*data);
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_sectionheader_load(cmd, segment);
|
|
+ printk("CMD segment piece len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_bootrom_cmd_sectionheader_get_res((bootrom_res_sectionheader_load_t*)response);
|
|
+ if (ret) {
|
|
+ printk("response Error\n");
|
|
+ return -1;
|
|
+ }
|
|
+ memcpy(&segment_plain,
|
|
+ &(((bootrom_res_sectionheader_load_t*)response)->header), sizeof(segment_plain));
|
|
+ segment = &segment_plain;
|
|
+ printk("Get SEGMENT From BL606: destaddr: 0x%08x, len %d, rsv %08X, CRC32 %08X\n",
|
|
+ segment->destaddr,
|
|
+ segment->len,
|
|
+ segment->rsv,
|
|
+ segment->crc32
|
|
+ );
|
|
+ if (*len < (sizeof(segment_header_t) + segment->len)) {
|
|
+ printk("SEGMENT: buffer too small %lu(%lX required):%d(%X actual) to 0x%x\n",
|
|
+ sizeof(segment_header_t) + segment->len,
|
|
+ sizeof(segment_header_t) + segment->len,
|
|
+ *len,
|
|
+ *len,
|
|
+ segment->destaddr
|
|
+ );
|
|
+ return -1;
|
|
+ }
|
|
+ firmware_dump_segment_piece(segment);
|
|
+
|
|
+ pos = 0;
|
|
+ i = 0;
|
|
+ while (pos < segment->len) {
|
|
+#define WR_LEN ((BOOTROM_DATA_BUFFER_SIZE - sizeof(bootrom_host_cmd_t)) & 0xFFFFFFF0)
|
|
+ wr_len = segment->len - pos;
|
|
+ wr_len = (wr_len < WR_LEN) ? wr_len : WR_LEN;
|
|
+ printk("------piece %03d[%03d] %d:%u------\n", i++, wr_len, pos, segment->len);
|
|
+#if 1
|
|
+ /*data len is less than one packet*/
|
|
+ printk("data SRC[0-7]: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
+ (*data + sizeof(segment_header_t) + pos)[0],
|
|
+ (*data + sizeof(segment_header_t) + pos)[1],
|
|
+ (*data + sizeof(segment_header_t) + pos)[2],
|
|
+ (*data + sizeof(segment_header_t) + pos)[3],
|
|
+ (*data + sizeof(segment_header_t) + pos)[4],
|
|
+ (*data + sizeof(segment_header_t) + pos)[5],
|
|
+ (*data + sizeof(segment_header_t) + pos)[6],
|
|
+ (*data + sizeof(segment_header_t) + pos)[7]
|
|
+ );
|
|
+#endif
|
|
+ bl_bootrom_cmd_sectiondata_load(cmd, *data + sizeof(segment_header_t) + pos, wr_len);
|
|
+#if 1
|
|
+ printk("data[%u] CMD[0-7]: %02X %02X %02X %02X %02X %02X %02X %02X\n"
|
|
+ " TAL[0-7]: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
+ cmd->len,
|
|
+ cmd->data[0],
|
|
+ cmd->data[1],
|
|
+ cmd->data[2],
|
|
+ cmd->data[3],
|
|
+ cmd->data[4],
|
|
+ cmd->data[5],
|
|
+ cmd->data[6],
|
|
+ cmd->data[7],
|
|
+ cmd->data[cmd->len - 1 - 7],
|
|
+ cmd->data[cmd->len - 1 - 6],
|
|
+ cmd->data[cmd->len - 1 - 5],
|
|
+ cmd->data[cmd->len - 1 - 4],
|
|
+ cmd->data[cmd->len - 1 - 3],
|
|
+ cmd->data[cmd->len - 1 - 2],
|
|
+ cmd->data[cmd->len - 1 - 1],
|
|
+ cmd->data[cmd->len - 1 - 0]
|
|
+ );
|
|
+#endif
|
|
+ printk("CMD segment piece loop len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+#if 0
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ bl_bootrom_cmd_sectiondata_get_res((bootrom_res_sectiondata_load_t*)response);
|
|
+#endif
|
|
+ pos += wr_len;
|
|
+ }
|
|
+
|
|
+ *data += (sizeof(segment_header_t) + segment->len);
|
|
+ *len -= (sizeof(segment_header_t) + segment->len);
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_LEAVE_STR);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_segment(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response,
|
|
+ bootheader_t *header, const u8 **data, int *len)
|
|
+{
|
|
+ int i, ret;
|
|
+
|
|
+ printk("SEGMENT AERA: @Offset 0x%lX\n", (u8*)(*data) - (u8*)(header));
|
|
+ printk("FUNC segment: data ptr %p, data left %d\n", *data, *len);
|
|
+ for (i = 0; i < header->segment_cnt; i++) {
|
|
+ printk("------SEGMENT[%02d]@0x%lX------\n", i, (u8*)(*data) - (u8*)(header));
|
|
+ ret = firmware_download_segment_piece(card, cmd, response, header, data, len);
|
|
+ if (ret) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int firmware_download_image_check(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response, bootheader_t *header)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_checkimage_get(cmd);
|
|
+ printk("CMD checkimage len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_bootrom_cmd_checkimage_get_res((bootrom_res_checkimage_t*)response);
|
|
+ if (ret) {
|
|
+ printk("Error response get checkimage\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_download_image_run(struct bl_plat *card, bootrom_host_cmd_t *cmd, u8 *response, bootheader_t *header)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_runimage_get(cmd);
|
|
+ printk("CMD runimage len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ bl_bootrom_cmd_runimage_get_res((bootrom_res_runimage_t*)response);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int firmware_downloader(struct bl_plat *card, const u8 *data, int len)
|
|
+{
|
|
+ int ret = 0;
|
|
+ bootheader_t *header;
|
|
+ bootrom_host_cmd_t *cmd;
|
|
+ u8 *response;
|
|
+
|
|
+ if (len < sizeof(bootheader_t)) {
|
|
+ printk("Illegal len when check bootheader_t\n");
|
|
+ return -1;
|
|
+ }
|
|
+ header = (bootheader_t*)data;
|
|
+ cmd = kzalloc(BOOTROM_DATA_BUFFER_SIZE, GFP_KERNEL);
|
|
+ if (NULL == cmd) {
|
|
+ printk("NO MEM for alloc host CMD\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ response = kzalloc(BOOTROM_CMD_BUFFER_SIZE, GFP_KERNEL);
|
|
+ if (NULL == response) {
|
|
+ printk("NO MEM for alloc CMD response\n");
|
|
+ kfree(cmd);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /*do NOT change the order bellow*/
|
|
+ if (firmware_download_head(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_flashcfg(card, cmd, response, header)
|
|
+ || firmware_download_pllcfg(card, cmd, response, header)
|
|
+ || firmware_download_publickey1(card, cmd, response, header, &data, &len)
|
|
+ //|| firmware_download_publickey2(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_signdata1(card, cmd, response, header, &data, &len)
|
|
+ //|| firmware_download_signdata2(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_encryptiondata(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_segment(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_image_check(card, cmd, response, header)) {
|
|
+ printk("firmware load faield\n");
|
|
+ return -1;
|
|
+ }
|
|
+ /*check if we need to download the other image*/
|
|
+ if (len > 0) {
|
|
+ printk("Potential Second image found\n");
|
|
+ header = (bootheader_t*)data;//update header to the next image
|
|
+ if (firmware_download_head(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_flashcfg(card, cmd, response, header)
|
|
+ || firmware_download_pllcfg(card, cmd, response, header)
|
|
+ || firmware_download_publickey1(card, cmd, response, header, &data, &len)
|
|
+ //|| firmware_download_publickey2(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_signdata1(card, cmd, response, header, &data, &len)
|
|
+ //|| firmware_download_signdata2(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_encryptiondata(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_segment(card, cmd, response, header, &data, &len)
|
|
+ || firmware_download_image_check(card, cmd, response, header)) {
|
|
+ printk("Sencond firmware load faield\n");
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ if (firmware_download_image_run(card, cmd, response, header)) {
|
|
+ printk("firmware check/Run faield\n");
|
|
+ ret = -1;
|
|
+ }
|
|
+
|
|
+ kfree(cmd);
|
|
+ kfree(response);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int bl_sdio_checkversion(struct bl_plat *card)
|
|
+{
|
|
+ bootrom_host_cmd_t *cmd;
|
|
+ int ret = 0;
|
|
+ u8 *response;
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_ENTRY_STR);
|
|
+
|
|
+ cmd = kzalloc(BOOTROM_CMD_BUFFER_SIZE, GFP_KERNEL);
|
|
+ if (NULL == cmd) {
|
|
+ printk("NO MEM for alloc host CMD\n");
|
|
+ return -1;
|
|
+ }
|
|
+ response = kzalloc(BOOTROM_CMD_BUFFER_SIZE, GFP_KERNEL);
|
|
+ if (NULL == response) {
|
|
+ printk("NO MEM for alloc CMD response\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ memset(cmd, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ memset(response, 0, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ bl_bootrom_cmd_bootinfo_get(cmd);
|
|
+ printk("CMD check version len %d\n", cmd->len);
|
|
+ ret = bl_io_write(card, (u8*)cmd, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("ERR when write\n");
|
|
+ return -1;
|
|
+ }
|
|
+ ret = bl_io_read(card, response, BOOTROM_CMD_BUFFER_SIZE);
|
|
+ if (ret) {
|
|
+ printk("NULL response get");
|
|
+ return -1;
|
|
+ }
|
|
+ bl_bootrom_cmd_bootinfo_get_res((bootrom_res_bootinfo_t*)response);
|
|
+
|
|
+ kfree(response);
|
|
+ kfree(cmd);
|
|
+ SDIO_DBG(SDIO_FN_LEAVE_STR);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int bl_sdio_download_firmware(struct bl_plat *card)
|
|
+{
|
|
+ const struct firmware *fw_helper = NULL;
|
|
+ int ret, imagelen;
|
|
+ const u8 *image = NULL;
|
|
+ ktime_t start;
|
|
+ s64 consume_time;
|
|
+
|
|
+ printk("Enter bl_sdio_download_firmware...\n");
|
|
+ ret = request_firmware(&fw_helper, "img_if_bl602_a0_rel_f157d2013f.bin", &card->func->dev);
|
|
+ if ((ret < 0) || !fw_helper) {
|
|
+ printk("request_firmware failed, error code = %d", ret);
|
|
+ ret = -ENOENT;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ image = fw_helper->data;
|
|
+ imagelen = fw_helper->size;
|
|
+
|
|
+ BL_DBG("Downloading image (%d bytes)\n", imagelen);
|
|
+ //time_start = jiffies;
|
|
+ start = ktime_get();
|
|
+ firmware_downloader(card, image, imagelen);
|
|
+ consume_time = ktime_us_delta(ktime_get(), start);
|
|
+ BL_DBG("Download fw time: %lld\n", consume_time);
|
|
+
|
|
+ //time_end = jiffies;
|
|
+
|
|
+ release_firmware(fw_helper);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+}
|
|
+
|
|
+int bl_sdio_init(struct bl_plat *card)
|
|
+{
|
|
+ u32 ret = 0;
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_ENTRY_STR);
|
|
+
|
|
+ ret = bl_sdio_run_fw(card);
|
|
+
|
|
+ ret = bl_sdio_checkversion(card);
|
|
+
|
|
+ ret = bl_sdio_download_firmware(card);
|
|
+
|
|
+ SDIO_DBG(SDIO_FN_LEAVE_STR);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int bl_read_data_sync_claim0(struct bl_hw *bl_hw, u8 *buffer, u32 len, u32 port)
|
|
+{
|
|
+ int ret;
|
|
+ u8 blk_mode = (len < BL_SDIO_BLOCK_SIZE) ? BYTE_MODE : BLOCK_MODE;
|
|
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? BL_SDIO_BLOCK_SIZE : 1;
|
|
+ u32 blk_cnt = (blk_mode == BLOCK_MODE) ? ((len + BL_SDIO_BLOCK_SIZE -1)/BL_SDIO_BLOCK_SIZE) : len;
|
|
+ u32 ioport = (port & BL_SDIO_IO_PORT_MASK);
|
|
+
|
|
+ ret = sdio_readsb(bl_hw->plat->func, buffer, ioport, blk_cnt * blk_size);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function writes multiple data into SDIO card memory.
|
|
+ *
|
|
+ * This does not work in suspended mode.
|
|
+ */
|
|
+int bl_write_data_sync(struct bl_hw *bl_hw, u8 *buffer, u32 pkt_len, u32 port)
|
|
+{
|
|
+ int ret;
|
|
+ u8 blk_mode = (pkt_len < BL_SDIO_BLOCK_SIZE) ? BYTE_MODE : BLOCK_MODE;
|
|
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? BL_SDIO_BLOCK_SIZE : 1;
|
|
+ u32 blk_cnt = (blk_mode == BLOCK_MODE) ? ((pkt_len + BL_SDIO_BLOCK_SIZE -1)/BL_SDIO_BLOCK_SIZE) : pkt_len;
|
|
+
|
|
+ u32 ioport = (port & BL_SDIO_IO_PORT_MASK);
|
|
+
|
|
+ sdio_claim_host(bl_hw->plat->func);
|
|
+ ret = sdio_writesb(bl_hw->plat->func, ioport, buffer, blk_cnt * blk_size);
|
|
+ sdio_release_host(bl_hw->plat->func);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function reads multiple data from SDIO card memory.
|
|
+ */
|
|
+int bl_read_data_sync(struct bl_hw *bl_hw, u8 *buffer, u32 len, u32 port)
|
|
+{
|
|
+ int ret;
|
|
+ u8 blk_mode = (len < BL_SDIO_BLOCK_SIZE) ? BYTE_MODE : BLOCK_MODE;
|
|
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? BL_SDIO_BLOCK_SIZE : 1;
|
|
+ u32 blk_cnt = (blk_mode == BLOCK_MODE) ? ((len + BL_SDIO_BLOCK_SIZE -1)/BL_SDIO_BLOCK_SIZE) : len;
|
|
+ u32 ioport = (port & BL_SDIO_IO_PORT_MASK);
|
|
+
|
|
+ sdio_claim_host(bl_hw->plat->func);
|
|
+ ret = sdio_readsb(bl_hw->plat->func, buffer, ioport, blk_cnt * blk_size);
|
|
+ sdio_release_host(bl_hw->plat->func);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function reads data from SDIO card register.
|
|
+ */
|
|
+int bl_read_reg(struct bl_hw *bl_hw, u32 reg, u8 *data)
|
|
+{
|
|
+ int ret = -1;
|
|
+ u8 val;
|
|
+
|
|
+ sdio_claim_host(bl_hw->plat->func);
|
|
+ val = sdio_readb(bl_hw->plat->func, reg, &ret);
|
|
+ sdio_release_host(bl_hw->plat->func);
|
|
+
|
|
+ *data = val;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function write data to SDIO card register.
|
|
+ */
|
|
+int bl_write_reg(struct bl_hw *bl_hw, u32 reg, u8 data)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ sdio_claim_host(bl_hw->plat->func);
|
|
+ sdio_writeb(bl_hw->plat->func, data, reg, &ret);
|
|
+ sdio_release_host(bl_hw->plat->func);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int bl_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
|
|
+{
|
|
+ struct bl_plat *bl_plat = NULL;
|
|
+ void *drvdata;
|
|
+ int ret = -ENODEV;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ BL_DBG("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n",
|
|
+ func->vendor, func->device, func->class, func->num);
|
|
+
|
|
+ ret = bl_device_init(func, &bl_plat);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ bl_plat->func = func;
|
|
+
|
|
+ ret = bl_platform_init(bl_plat, &drvdata);
|
|
+ sdio_set_drvdata(func, drvdata);
|
|
+
|
|
+ if (ret)
|
|
+ bl_device_deinit(bl_plat);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void bl_sdio_remove(struct sdio_func *func)
|
|
+{
|
|
+ struct bl_hw *bl_hw;
|
|
+ struct bl_plat *bl_plat;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_hw = sdio_get_drvdata(func);
|
|
+ bl_plat = bl_hw->plat;
|
|
+
|
|
+ bl_platform_deinit(bl_hw);
|
|
+ bl_device_deinit(bl_plat);
|
|
+
|
|
+ sdio_set_drvdata(func, NULL);
|
|
+
|
|
+}
|
|
+
|
|
+static struct sdio_driver bl_sdio_drv = {
|
|
+ .name = KBUILD_MODNAME,
|
|
+ .id_table = bl_sdio_ids,
|
|
+ .probe = bl_sdio_probe,
|
|
+ .remove = bl_sdio_remove
|
|
+};
|
|
+
|
|
+int bl_sdio_register_drv(void)
|
|
+{
|
|
+ return sdio_register_driver(&bl_sdio_drv);
|
|
+}
|
|
+
|
|
+void bl_sdio_unregister_drv(void)
|
|
+{
|
|
+ sdio_unregister_driver(&bl_sdio_drv);
|
|
+}
|
|
diff -Naur /dev/null_sdio.h b/drivers/net/wireless/hflps170/bl_sdio.h
|
|
--- /dev/null_sdio.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_sdio.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,199 @@
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_sdio.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_SDIO_H_
|
|
+#define _BL_SDIO_H_
|
|
+
|
|
+
|
|
+//////////////////////dnld fw//////////////////////////////////
|
|
+#define MBIT(x) (((u32)1) << (x))
|
|
+#define HOST_INT_RSR_REG 0x01
|
|
+#define HOST_INT_RSR_MASK 0x3F
|
|
+#define RD_BITMAP_L 0x04
|
|
+#define RD_BITMAP_U 0x05
|
|
+#define WR_BITMAP_L 0x06
|
|
+#define WR_BITMAP_U 0x07
|
|
+#define CARD_FW_STATUS0_REG 0x60
|
|
+#define CARD_MISC_CFG_REG 0x6C
|
|
+#define CARD_MISC_CFG_REG 0x6C
|
|
+#define IO_PORT_0_REG 0x78
|
|
+#define IO_PORT_1_REG 0x79
|
|
+#define IO_PORT_2_REG 0x7A
|
|
+
|
|
+#define SDIO_DBG printk
|
|
+
|
|
+#define SDIO_FN_ENTRY_STR ">>> %s()\n", __func__
|
|
+#define SDIO_FN_LEAVE_STR "<<< %s()\n", __func__
|
|
+
|
|
+
|
|
+struct bl_fw_image {
|
|
+ u8 *fw_buf;
|
|
+ u32 fw_len;
|
|
+};
|
|
+
|
|
+int bl_write_data_sync_dnldfw(struct bl_plat *card, u8 *buffer, u32 pkt_len, u32 port);
|
|
+int bl_read_data_sync_dnldfw(struct bl_plat *card, u8 *buffer, u32 len, u32 port);
|
|
+//////////////////////dnld fw//////////////////////////////////
|
|
+
|
|
+#define SD606_DEFAULT_FW_NAME "bfl_fullmac.bin"
|
|
+
|
|
+#define USE_15_DATA_PORT
|
|
+
|
|
+#ifdef USE_15_DATA_PORT
|
|
+#define DATA_PORT_MSK 0x0000fffe
|
|
+#define MAX_PORT_NUM 16
|
|
+#else
|
|
+#define DATA_PORT_MSK 0x00007ffe
|
|
+#define MAX_PORT_NUM 15
|
|
+//#define DATA_PORT_MSK 0x00000002
|
|
+//#define MAX_PORT_NUM 2
|
|
+#endif
|
|
+
|
|
+#define BLOCK_MODE 1
|
|
+#define BYTE_MODE 0
|
|
+
|
|
+#define BL_SDIO_BLOCK_SIZE 256
|
|
+
|
|
+#define REG_PORT 0
|
|
+
|
|
+#define BL_SDIO_IO_PORT_MASK 0xfffff
|
|
+
|
|
+#define BL_SDIO_BYTE_MODE_MASK 0x80000000
|
|
+
|
|
+#define SDIO_MPA_ADDR_BASE 0x1000
|
|
+#define CTRL_PORT 0
|
|
+#define CTRL_PORT_MASK 0x0001
|
|
+
|
|
+#define CMD_PORT_UPLD_INT_MASK (0x1U<<6)
|
|
+#define CMD_PORT_DNLD_INT_MASK (0x1U<<7)
|
|
+#define HOST_TERM_CMD53 (0x1U << 2)
|
|
+#define REG_PORT 0
|
|
+#define MEM_PORT 0x10000
|
|
+
|
|
+#define CMD53_NEW_MODE (0x1U << 0)
|
|
+#define CMD_PORT_RD_LEN_EN (0x1U << 2)
|
|
+#define CMD_PORT_AUTO_EN (0x1U << 0)
|
|
+#define CMD_PORT_SLCT 0x8000
|
|
+#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
|
|
+#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
|
|
+
|
|
+#define BL_MP_AGGR_BUF_SIZE_16K (16384)
|
|
+#define BL_MP_AGGR_BUF_SIZE_32K (32768)
|
|
+
|
|
+/* Misc. Config Register : Auto Re-enable interrupts */
|
|
+#define AUTO_RE_ENABLE_INT BIT(4)
|
|
+
|
|
+/* Host Control Registers : Configuration */
|
|
+#define CONFIGURATION_REG 0x00
|
|
+/* Host Control Registers : Host power up */
|
|
+#define HOST_POWER_UP (0x1U << 1)
|
|
+
|
|
+/* Host Control Registers : Upload host interrupt mask */
|
|
+#define UP_LD_HOST_INT_MASK (0x1U)
|
|
+/* Host Control Registers : Download host interrupt mask */
|
|
+#define DN_LD_HOST_INT_MASK (0x2U)
|
|
+
|
|
+/* Host Control Registers : Upload host interrupt status */
|
|
+#define UP_LD_HOST_INT_STATUS (0x1U)
|
|
+/* Host Control Registers : Download host interrupt status */
|
|
+#define DN_LD_HOST_INT_STATUS (0x2U)
|
|
+
|
|
+/* Host Control Registers : Host interrupt status */
|
|
+#define CARD_INT_STATUS_REG 0x28
|
|
+
|
|
+/* Card Control Registers : Card I/O ready */
|
|
+#define CARD_IO_READY (0x1U << 3)
|
|
+/* Card Control Registers : Download card ready */
|
|
+#define DN_LD_CARD_RDY (0x1U << 0)
|
|
+
|
|
+struct bl_sdio_card_reg {
|
|
+ u8 start_rd_port;
|
|
+ u8 start_wr_port;
|
|
+ u8 base_0_reg;
|
|
+ u8 base_1_reg;
|
|
+ u8 poll_reg;
|
|
+ u8 host_int_enable;
|
|
+ u8 host_int_rsr_reg;
|
|
+ u8 host_int_status_reg;
|
|
+ u8 host_int_mask_reg;
|
|
+ u8 status_reg_0;
|
|
+ u8 status_reg_1;
|
|
+ u8 sdio_int_mask;
|
|
+ u32 data_port_mask;
|
|
+ u8 io_port_0_reg;
|
|
+ u8 io_port_1_reg;
|
|
+ u8 io_port_2_reg;
|
|
+ u8 max_mp_regs;
|
|
+ u8 rd_bitmap_l;
|
|
+ u8 rd_bitmap_u;
|
|
+ u8 rd_bitmap_1l;
|
|
+ u8 rd_bitmap_1u;
|
|
+ u8 wr_bitmap_l;
|
|
+ u8 wr_bitmap_u;
|
|
+ u8 wr_bitmap_1l;
|
|
+ u8 wr_bitmap_1u;
|
|
+ u8 rd_len_p0_l;
|
|
+ u8 rd_len_p0_u;
|
|
+ u8 card_misc_cfg_reg;
|
|
+ u8 card_cfg_2_1_reg;
|
|
+ u8 cmd_rd_len_0;
|
|
+ u8 cmd_rd_len_1;
|
|
+ u8 cmd_rd_len_2;
|
|
+ u8 cmd_rd_len_3;
|
|
+ u8 cmd_cfg_0;
|
|
+ u8 cmd_cfg_1;
|
|
+ u8 cmd_cfg_2;
|
|
+ u8 cmd_cfg_3;
|
|
+ u8 fw_dump_ctrl;
|
|
+ u8 fw_dump_start;
|
|
+ u8 fw_dump_end;
|
|
+};
|
|
+
|
|
+static const struct bl_sdio_card_reg bl_reg_sd606 = {
|
|
+ .start_rd_port = 1,
|
|
+ .start_wr_port = 1,
|
|
+ .base_0_reg = 0x0040,
|
|
+ .base_1_reg = 0x0041,
|
|
+ .poll_reg = 0x30,
|
|
+ .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK,
|
|
+ .host_int_rsr_reg = 0x1,
|
|
+ .host_int_mask_reg = 0x02,
|
|
+ .host_int_status_reg = 0x03,
|
|
+ .status_reg_0 = 0x60,
|
|
+ .status_reg_1 = 0x61,
|
|
+ .sdio_int_mask = 0x3f,
|
|
+ .data_port_mask = DATA_PORT_MSK,
|
|
+ .io_port_0_reg = 0x78,
|
|
+ .io_port_1_reg = 0x79,
|
|
+ .io_port_2_reg = 0x7A,
|
|
+ .max_mp_regs = 64,
|
|
+ .rd_bitmap_l = 0x04,
|
|
+ .rd_bitmap_u = 0x05,
|
|
+ .wr_bitmap_l = 0x06,
|
|
+ .wr_bitmap_u = 0x07,
|
|
+ .rd_len_p0_l = 0x08,
|
|
+ .rd_len_p0_u = 0x09,
|
|
+ .card_misc_cfg_reg = 0x6c,
|
|
+};
|
|
+
|
|
+int bl_sdio_register_drv(void);
|
|
+void bl_sdio_unregister_drv(void);
|
|
+int bl_read_reg(struct bl_hw *bl_hw, u32 reg, u8 *data);
|
|
+int bl_write_reg(struct bl_hw *bl_hw, u32 reg, u8 data);
|
|
+void bl_get_rd_len(struct bl_hw *bl_hw, u32 reg_l, u32 reg_u, u32 *len);
|
|
+int bl_get_rd_port(struct bl_hw *bl_hw, u32 *port);
|
|
+int bl_get_wr_port(struct bl_hw *bl_hw, u32 *port);
|
|
+int bl_read_data_sync(struct bl_hw *bl_hw, u8 *buffer, u32 len, u32 port);
|
|
+int bl_read_data_sync_claim0(struct bl_hw *bl_hw, u8 *buffer, u32 len, u32 port);
|
|
+int bl_write_data_sync(struct bl_hw *bl_hw, u8 *buffer, u32 pkt_len, u32 port);
|
|
+int bl_sdio_init(struct bl_plat *card);
|
|
+
|
|
+#endif /* _BL_SDIO_H_ */
|
|
diff -Naur /dev/null_strs.c b/drivers/net/wireless/hflps170/bl_strs.c
|
|
--- /dev/null_strs.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_strs.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,182 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_strs.c
|
|
+ *
|
|
+ * @brief Miscellaneous debug strings
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#include "bl_utils.h"
|
|
+#include "lmac_msg.h"
|
|
+
|
|
+static const char *const bl_mmid2str[MSG_I(MM_MAX)] = {
|
|
+ [MSG_I(MM_RESET_REQ)] = "MM_RESET_REQ",
|
|
+ [MSG_I(MM_RESET_CFM)] = "MM_RESET_CFM",
|
|
+ [MSG_I(MM_START_REQ)] = "MM_START_REQ",
|
|
+ [MSG_I(MM_START_CFM)] = "MM_START_CFM",
|
|
+ [MSG_I(MM_VERSION_REQ)] = "MM_VERSION_REQ",
|
|
+ [MSG_I(MM_VERSION_CFM)] = "MM_VERSION_CFM",
|
|
+ [MSG_I(MM_ADD_IF_REQ)] = "MM_ADD_IF_REQ",
|
|
+ [MSG_I(MM_ADD_IF_CFM)] = "MM_ADD_IF_CFM",
|
|
+ [MSG_I(MM_REMOVE_IF_REQ)] = "MM_REMOVE_IF_REQ",
|
|
+ [MSG_I(MM_REMOVE_IF_CFM)] = "MM_REMOVE_IF_CFM",
|
|
+ [MSG_I(MM_STA_ADD_REQ)] = "MM_STA_ADD_REQ",
|
|
+ [MSG_I(MM_STA_ADD_CFM)] = "MM_STA_ADD_CFM",
|
|
+ [MSG_I(MM_STA_DEL_REQ)] = "MM_STA_DEL_REQ",
|
|
+ [MSG_I(MM_STA_DEL_CFM)] = "MM_STA_DEL_CFM",
|
|
+ [MSG_I(MM_SET_FILTER_REQ)] = "MM_SET_FILTER_REQ",
|
|
+ [MSG_I(MM_SET_FILTER_CFM)] = "MM_SET_FILTER_CFM",
|
|
+ [MSG_I(MM_SET_CHANNEL_REQ)] = "MM_SET_CHANNEL_REQ",
|
|
+ [MSG_I(MM_SET_CHANNEL_CFM)] = "MM_SET_CHANNEL_CFM",
|
|
+ [MSG_I(MM_SET_DTIM_REQ)] = "MM_SET_DTIM_REQ",
|
|
+ [MSG_I(MM_SET_DTIM_CFM)] = "MM_SET_DTIM_CFM",
|
|
+ [MSG_I(MM_SET_BEACON_INT_REQ)] = "MM_SET_BEACON_INT_REQ",
|
|
+ [MSG_I(MM_SET_BEACON_INT_CFM)] = "MM_SET_BEACON_INT_CFM",
|
|
+ [MSG_I(MM_SET_BASIC_RATES_REQ)] = "MM_SET_BASIC_RATES_REQ",
|
|
+ [MSG_I(MM_SET_BASIC_RATES_CFM)] = "MM_SET_BASIC_RATES_CFM",
|
|
+ [MSG_I(MM_SET_BSSID_REQ)] = "MM_SET_BSSID_REQ",
|
|
+ [MSG_I(MM_SET_BSSID_CFM)] = "MM_SET_BSSID_CFM",
|
|
+ [MSG_I(MM_SET_EDCA_REQ)] = "MM_SET_EDCA_REQ",
|
|
+ [MSG_I(MM_SET_EDCA_CFM)] = "MM_SET_EDCA_CFM",
|
|
+ [MSG_I(MM_SET_MODE_REQ)] = "MM_SET_MODE_REQ",
|
|
+ [MSG_I(MM_SET_MODE_CFM)] = "MM_SET_MODE_CFM",
|
|
+ [MSG_I(MM_SET_VIF_STATE_REQ)] = "MM_SET_VIF_STATE_REQ",
|
|
+ [MSG_I(MM_SET_VIF_STATE_CFM)] = "MM_SET_VIF_STATE_CFM",
|
|
+ [MSG_I(MM_SET_SLOTTIME_REQ)] = "MM_SET_SLOTTIME_REQ",
|
|
+ [MSG_I(MM_SET_SLOTTIME_CFM)] = "MM_SET_SLOTTIME_CFM",
|
|
+ [MSG_I(MM_SET_IDLE_REQ)] = "MM_SET_IDLE_REQ",
|
|
+ [MSG_I(MM_SET_IDLE_CFM)] = "MM_SET_IDLE_CFM",
|
|
+ [MSG_I(MM_KEY_ADD_REQ)] = "MM_KEY_ADD_REQ",
|
|
+ [MSG_I(MM_KEY_ADD_CFM)] = "MM_KEY_ADD_CFM",
|
|
+ [MSG_I(MM_KEY_DEL_REQ)] = "MM_KEY_DEL_REQ",
|
|
+ [MSG_I(MM_KEY_DEL_CFM)] = "MM_KEY_DEL_CFM",
|
|
+ [MSG_I(MM_BA_ADD_REQ)] = "MM_BA_ADD_REQ",
|
|
+ [MSG_I(MM_BA_ADD_CFM)] = "MM_BA_ADD_CFM",
|
|
+ [MSG_I(MM_BA_DEL_REQ)] = "MM_BA_DEL_REQ",
|
|
+ [MSG_I(MM_BA_DEL_CFM)] = "MM_BA_DEL_CFM",
|
|
+ [MSG_I(MM_PRIMARY_TBTT_IND)] = "MM_PRIMARY_TBTT_IND",
|
|
+ [MSG_I(MM_SECONDARY_TBTT_IND)] = "MM_SECONDARY_TBTT_IND",
|
|
+ [MSG_I(MM_SET_POWER_REQ)] = "MM_SET_POWER_REQ",
|
|
+ [MSG_I(MM_SET_POWER_CFM)] = "MM_SET_POWER_CFM",
|
|
+ [MSG_I(MM_DBG_TRIGGER_REQ)] = "MM_DBG_TRIGGER_REQ",
|
|
+ [MSG_I(MM_SET_PS_MODE_REQ)] = "MM_SET_PS_MODE_REQ",
|
|
+ [MSG_I(MM_SET_PS_MODE_CFM)] = "MM_SET_PS_MODE_CFM",
|
|
+ [MSG_I(MM_CHAN_CTXT_ADD_REQ)] = "MM_CHAN_CTXT_ADD_REQ",
|
|
+ [MSG_I(MM_CHAN_CTXT_ADD_CFM)] = "MM_CHAN_CTXT_ADD_CFM",
|
|
+ [MSG_I(MM_CHAN_CTXT_DEL_REQ)] = "MM_CHAN_CTXT_DEL_REQ",
|
|
+ [MSG_I(MM_CHAN_CTXT_DEL_CFM)] = "MM_CHAN_CTXT_DEL_CFM",
|
|
+ [MSG_I(MM_CHAN_CTXT_LINK_REQ)] = "MM_CHAN_CTXT_LINK_REQ",
|
|
+ [MSG_I(MM_CHAN_CTXT_LINK_CFM)] = "MM_CHAN_CTXT_LINK_CFM",
|
|
+ [MSG_I(MM_CHAN_CTXT_UNLINK_REQ)] = "MM_CHAN_CTXT_UNLINK_REQ",
|
|
+ [MSG_I(MM_CHAN_CTXT_UNLINK_CFM)] = "MM_CHAN_CTXT_UNLINK_CFM",
|
|
+ [MSG_I(MM_CHAN_CTXT_UPDATE_REQ)] = "MM_CHAN_CTXT_UPDATE_REQ",
|
|
+ [MSG_I(MM_CHAN_CTXT_UPDATE_CFM)] = "MM_CHAN_CTXT_UPDATE_CFM",
|
|
+ [MSG_I(MM_CHAN_CTXT_SCHED_REQ)] = "MM_CHAN_CTXT_SCHED_REQ",
|
|
+ [MSG_I(MM_CHAN_CTXT_SCHED_CFM)] = "MM_CHAN_CTXT_SCHED_CFM",
|
|
+ [MSG_I(MM_BCN_CHANGE_REQ)] = "MM_BCN_CHANGE_REQ",
|
|
+ [MSG_I(MM_BCN_CHANGE_CFM)] = "MM_BCN_CHANGE_CFM",
|
|
+ [MSG_I(MM_TIM_UPDATE_REQ)] = "MM_TIM_UPDATE_REQ",
|
|
+ [MSG_I(MM_TIM_UPDATE_CFM)] = "MM_TIM_UPDATE_CFM",
|
|
+ [MSG_I(MM_CONNECTION_LOSS_IND)] = "MM_CONNECTION_LOSS_IND",
|
|
+ [MSG_I(MM_CHANNEL_SWITCH_IND)] = "MM_CHANNEL_SWITCH_IND",
|
|
+ [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = "MM_CHANNEL_PRE_SWITCH_IND",
|
|
+ [MSG_I(MM_REMAIN_ON_CHANNEL_REQ)] = "MM_REMAIN_ON_CHANNEL_REQ",
|
|
+ [MSG_I(MM_REMAIN_ON_CHANNEL_CFM)] = "MM_REMAIN_ON_CHANNEL_CFM",
|
|
+ [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = "MM_REMAIN_ON_CHANNEL_EXP_IND",
|
|
+ [MSG_I(MM_PS_CHANGE_IND)] = "MM_PS_CHANGE_IND",
|
|
+ [MSG_I(MM_TRAFFIC_REQ_IND)] = "MM_TRAFFIC_REQ_IND",
|
|
+ [MSG_I(MM_SET_PS_OPTIONS_REQ)] = "MM_SET_PS_OPTIONS_REQ",
|
|
+ [MSG_I(MM_SET_PS_OPTIONS_CFM)] = "MM_SET_PS_OPTIONS_CFM",
|
|
+ [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = "MM_P2P_VIF_PS_CHANGE_IND",
|
|
+ [MSG_I(MM_CSA_COUNTER_IND)] = "MM_CSA_COUNTER_IND",
|
|
+ [MSG_I(MM_CHANNEL_SURVEY_IND)] = "MM_CHANNEL_SURVEY_IND",
|
|
+ [MSG_I(MM_CFG_RSSI_REQ)] = "MM_CFG_RSSI_REQ",
|
|
+ [MSG_I(MM_RSSI_STATUS_IND)] = "MM_RSSI_STATUS_IND",
|
|
+ [MSG_I(MM_CSA_FINISH_IND)] = "MM_CSA_FINISH_IND",
|
|
+ [MSG_I(MM_CSA_TRAFFIC_IND)] = "MM_CSA_TRAFFIC_IND",
|
|
+};
|
|
+
|
|
+static const char *const bl_dbgid2str[MSG_I(DBG_MAX)] = {
|
|
+ [MSG_I(DBG_MEM_READ_REQ)] = "DBG_MEM_READ_REQ",
|
|
+ [MSG_I(DBG_MEM_READ_CFM)] = "DBG_MEM_READ_CFM",
|
|
+ [MSG_I(DBG_MEM_WRITE_REQ)] = "DBG_MEM_WRITE_REQ",
|
|
+ [MSG_I(DBG_MEM_WRITE_CFM)] = "DBG_MEM_WRITE_CFM",
|
|
+ [MSG_I(DBG_SET_MOD_FILTER_REQ)] = "DBG_SET_MOD_FILTER_REQ",
|
|
+ [MSG_I(DBG_SET_MOD_FILTER_CFM)] = "DBG_SET_MOD_FILTER_CFM",
|
|
+ [MSG_I(DBG_SET_SEV_FILTER_REQ)] = "DBG_SET_SEV_FILTER_REQ",
|
|
+ [MSG_I(DBG_SET_SEV_FILTER_CFM)] = "DBG_SET_SEV_FILTER_CFM",
|
|
+ [MSG_I(DBG_ERROR_IND)] = "DBG_ERROR_IND",
|
|
+ [MSG_I(DBG_GET_SYS_STAT_REQ)] = "DBG_GET_SYS_STAT_REQ",
|
|
+ [MSG_I(DBG_GET_SYS_STAT_CFM)] = "DBG_GET_SYS_STAT_CFM",
|
|
+};
|
|
+
|
|
+static const char *const bl_scanid2str[MSG_I(SCAN_MAX)] = {
|
|
+ [MSG_I(SCAN_START_REQ)] = "SCAN_START_REQ",
|
|
+ [MSG_I(SCAN_START_CFM)] = "SCAN_START_CFM",
|
|
+ [MSG_I(SCAN_DONE_IND)] = "SCAN_DONE_IND",
|
|
+};
|
|
+
|
|
+static const char *const bl_scanuid2str[MSG_I(SCANU_MAX)] = {
|
|
+ [MSG_I(SCANU_START_REQ)] = "SCANU_START_REQ",
|
|
+ [MSG_I(SCANU_START_CFM)] = "SCANU_START_CFM",
|
|
+ [MSG_I(SCANU_JOIN_REQ)] = "SCANU_JOIN_REQ",
|
|
+ [MSG_I(SCANU_JOIN_CFM)] = "SCANU_JOIN_CFM",
|
|
+ [MSG_I(SCANU_RESULT_IND)] = "SCANU_RESULT_IND",
|
|
+ [MSG_I(SCANU_FAST_REQ)] = "SCANU_FAST_REQ",
|
|
+ [MSG_I(SCANU_FAST_CFM)] = "SCANU_FAST_CFM",
|
|
+};
|
|
+
|
|
+static const char *const bl_meid2str[MSG_I(ME_MAX)] = {
|
|
+ [MSG_I(ME_CONFIG_REQ)] = "ME_CONFIG_REQ",
|
|
+ [MSG_I(ME_CONFIG_CFM)] = "ME_CONFIG_CFM",
|
|
+ [MSG_I(ME_CHAN_CONFIG_REQ)] = "ME_CHAN_CONFIG_REQ",
|
|
+ [MSG_I(ME_CHAN_CONFIG_CFM)] = "ME_CHAN_CONFIG_CFM",
|
|
+ [MSG_I(ME_SET_CONTROL_PORT_REQ)] = "ME_SET_CONTROL_PORT_REQ",
|
|
+ [MSG_I(ME_SET_CONTROL_PORT_CFM)] = "ME_SET_CONTROL_PORT_CFM",
|
|
+ [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = "ME_TKIP_MIC_FAILURE_IND",
|
|
+ [MSG_I(ME_STA_ADD_REQ)] = "ME_STA_ADD_REQ",
|
|
+ [MSG_I(ME_STA_ADD_CFM)] = "ME_STA_ADD_CFM",
|
|
+ [MSG_I(ME_STA_DEL_REQ)] = "ME_STA_DEL_REQ",
|
|
+ [MSG_I(ME_STA_DEL_CFM)] = "ME_STA_DEL_CFM",
|
|
+ [MSG_I(ME_TX_CREDITS_UPDATE_IND)]= "ME_TX_CREDITS_UPDATE_IND",
|
|
+ [MSG_I(ME_RC_STATS_REQ)] = "ME_RC_STATS_REQ",
|
|
+ [MSG_I(ME_RC_STATS_CFM)] = "ME_RC_STATS_CFM",
|
|
+ [MSG_I(ME_RC_SET_RATE_REQ)] = "ME_RC_SET_RATE_REQ",
|
|
+ [MSG_I(ME_TRAFFIC_IND_REQ)] = "ME_TRAFFIC_IND_REQ",
|
|
+ [MSG_I(ME_TRAFFIC_IND_CFM)] = "ME_TRAFFIC_IND_CFM",
|
|
+};
|
|
+
|
|
+static const char *const bl_smid2str[MSG_I(SM_MAX)] = {
|
|
+ [MSG_I(SM_CONNECT_REQ)] = "SM_CONNECT_REQ",
|
|
+ [MSG_I(SM_CONNECT_CFM)] = "SM_CONNECT_CFM",
|
|
+ [MSG_I(SM_CONNECT_IND)] = "SM_CONNECT_IND",
|
|
+ [MSG_I(SM_DISCONNECT_REQ)] = "SM_DISCONNECT_REQ",
|
|
+ [MSG_I(SM_DISCONNECT_CFM)] = "SM_DISCONNECT_CFM",
|
|
+ [MSG_I(SM_DISCONNECT_IND)] = "SM_DISCONNECT_IND",
|
|
+ [MSG_I(SM_RSP_TIMEOUT_IND)] = "SM_RSP_TIMEOUT_IND",
|
|
+};
|
|
+
|
|
+static const char *const bl_apmid2str[MSG_I(APM_MAX)] = {
|
|
+ [MSG_I(APM_START_REQ)] = "APM_START_REQ",
|
|
+ [MSG_I(APM_START_CFM)] = "APM_START_CFM",
|
|
+ [MSG_I(APM_STOP_REQ)] = "APM_STOP_REQ",
|
|
+ [MSG_I(APM_STOP_CFM)] = "APM_STOP_CFM",
|
|
+ [MSG_I(APM_START_CAC_REQ)] = "APM_START_CAC_REQ",
|
|
+ [MSG_I(APM_START_CAC_CFM)] = "APM_START_CAC_CFM",
|
|
+ [MSG_I(APM_STOP_CAC_REQ)] = "APM_STOP_CAC_REQ",
|
|
+ [MSG_I(APM_STOP_CAC_CFM)] = "APM_STOP_CAC_CFM",
|
|
+};
|
|
+
|
|
+const char *const *bl_id2str[TASK_LAST_EMB + 1] = {
|
|
+ [TASK_MM] = bl_mmid2str,
|
|
+ [TASK_DBG] = bl_dbgid2str,
|
|
+ [TASK_SCAN] = bl_scanid2str,
|
|
+ [TASK_SCANU] = bl_scanuid2str,
|
|
+ [TASK_ME] = bl_meid2str,
|
|
+ [TASK_SM] = bl_smid2str,
|
|
+ [TASK_APM] = bl_apmid2str,
|
|
+};
|
|
diff -Naur /dev/null_strs.h b/drivers/net/wireless/hflps170/bl_strs.h
|
|
--- /dev/null_strs.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_strs.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,25 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_strs.h
|
|
+ *
|
|
+ * @brief Miscellaneous debug strings
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_STRS_H_
|
|
+#define _BL_STRS_H_
|
|
+
|
|
+#include "lmac_msg.h"
|
|
+
|
|
+#define BL_ID2STR(tag) (((MSG_T(tag) < ARRAY_SIZE(bl_id2str)) && \
|
|
+ (bl_id2str[MSG_T(tag)]) && \
|
|
+ ((bl_id2str[MSG_T(tag)])[MSG_I(tag)])) ? \
|
|
+ (bl_id2str[MSG_T(tag)])[MSG_I(tag)] : "unknown")
|
|
+
|
|
+extern const char *const *bl_id2str[TASK_LAST_EMB + 1];
|
|
+
|
|
+#endif /* _BL_STRS_H_ */
|
|
diff -Naur /dev/null_txq.c b/drivers/net/wireless/hflps170/bl_txq.c
|
|
--- /dev/null_txq.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_txq.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,983 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_txq.c
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#include "bl_defs.h"
|
|
+#include "bl_events.h"
|
|
+#include "bl_sdio.h"
|
|
+
|
|
+
|
|
+/******************************************************************************
|
|
+ * Utils inline functions
|
|
+ *****************************************************************************/
|
|
+struct bl_txq *bl_txq_sta_get(struct bl_sta *sta, u8 tid, int *idx,
|
|
+ struct bl_hw * bl_hw)
|
|
+{
|
|
+ int id;
|
|
+
|
|
+ if (tid >= NX_NB_TXQ_PER_STA)
|
|
+ tid = 0;
|
|
+
|
|
+ if (is_multicast_sta(sta->sta_idx))
|
|
+ id = (NX_REMOTE_STA_MAX * NX_NB_TXQ_PER_STA) + sta->vif_idx;
|
|
+ else
|
|
+ id = (sta->sta_idx * NX_NB_TXQ_PER_STA) + tid;
|
|
+
|
|
+ if (idx)
|
|
+ *idx = id;
|
|
+
|
|
+ return &bl_hw->txq[id];
|
|
+}
|
|
+
|
|
+struct bl_txq *bl_txq_vif_get(struct bl_vif *vif, u8 type, int *idx)
|
|
+{
|
|
+ int id;
|
|
+
|
|
+ id = NX_FIRST_VIF_TXQ_IDX + master_vif_idx(vif) +
|
|
+ (type * NX_VIRT_DEV_MAX);
|
|
+
|
|
+ if (idx)
|
|
+ *idx = id;
|
|
+
|
|
+ return &vif->bl_hw->txq[id];
|
|
+}
|
|
+
|
|
+static inline
|
|
+struct bl_sta *bl_txq_2_sta(struct bl_txq *txq)
|
|
+{
|
|
+ return txq->sta;
|
|
+}
|
|
+
|
|
+static const u16 bl_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
|
|
+
|
|
+/******************************************************************************
|
|
+ * Init/Deinit functions
|
|
+ *****************************************************************************/
|
|
+/**
|
|
+ * bl_txq_init - Initialize a TX queue
|
|
+ *
|
|
+ * @txq: TX queue to be initialized
|
|
+ * @idx: TX queue index
|
|
+ * @status: TX queue initial status
|
|
+ * @hwq: Associated HW queue
|
|
+ * @ndev: Net device this queue belongs to
|
|
+ * (may be null for non netdev txq)
|
|
+ *
|
|
+ * Each queue is initialized with the credit of @NX_TXQ_INITIAL_CREDITS.
|
|
+ */
|
|
+static void bl_txq_init(struct bl_txq *txq, int idx, u8 status,
|
|
+ struct bl_hwq *hwq,
|
|
+ struct bl_sta *sta, struct net_device *ndev
|
|
+ )
|
|
+{
|
|
+ int i;
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ txq->idx = idx;
|
|
+ txq->status = status;
|
|
+ txq->credits = NX_TXQ_INITIAL_CREDITS;
|
|
+ txq->pkt_sent = 0;
|
|
+ skb_queue_head_init(&txq->sk_list);
|
|
+ txq->last_retry_skb = NULL;
|
|
+ txq->nb_retry = 0;
|
|
+ txq->hwq = hwq;
|
|
+ txq->sta = sta;
|
|
+ for (i = 0; i < CONFIG_USER_MAX ; i++)
|
|
+ txq->pkt_pushed[i] = 0;
|
|
+
|
|
+ txq->ps_id = LEGACY_PS_ID;
|
|
+ txq->push_limit = 0;
|
|
+ if (idx < NX_FIRST_VIF_TXQ_IDX) {
|
|
+ int sta_idx = sta->sta_idx;
|
|
+ int tid = idx - (sta_idx * NX_NB_TXQ_PER_STA);
|
|
+ if (tid < NX_NB_TID_PER_STA)
|
|
+ //txq->ndev_idx = NX_STA_NDEV_IDX(tid, sta_idx);
|
|
+ txq->ndev_idx = bl_1d_to_wmm_queue[tid];
|
|
+ //txq->ndev_idx = tid;
|
|
+ else
|
|
+ txq->ndev_idx = NDEV_NO_TXQ;
|
|
+ } else if (idx < NX_FIRST_UNK_TXQ_IDX) {
|
|
+ txq->ndev_idx = NX_BCMC_TXQ_NDEV_IDX;
|
|
+ } else {
|
|
+ txq->ndev_idx = NDEV_NO_TXQ;
|
|
+ }
|
|
+ txq->ndev = ndev;
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ txq->amsdu = NULL;
|
|
+ txq->amsdu_len = 0;
|
|
+#endif /* CONFIG_BL_AMSDUS_TX */
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_flush - Flush all buffers queued for a TXQ
|
|
+ *
|
|
+ * @bl_hw: main driver data
|
|
+ * @txq: txq to flush
|
|
+ */
|
|
+void bl_txq_flush(struct bl_hw *bl_hw, struct bl_txq *txq)
|
|
+{
|
|
+ struct sk_buff *skb;
|
|
+
|
|
+ while((skb = skb_dequeue(&txq->sk_list)) != NULL) {
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ struct bl_sw_txhdr *hdr = ((struct bl_txhdr *)skb->data)->sw_hdr;
|
|
+ if (hdr->desc.host.packet_cnt > 1) {
|
|
+ struct bl_amsdu_txhdr *amsdu_txhdr;
|
|
+ list_for_each_entry(amsdu_txhdr, &hdr->amsdu.hdrs, list) {
|
|
+ dma_unmap_single(bl_hw->dev, amsdu_txhdr->dma_addr,
|
|
+ amsdu_txhdr->map_len, DMA_TO_DEVICE);
|
|
+ dev_kfree_skb_any(amsdu_txhdr->skb);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ dev_kfree_skb_any(skb);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_deinit - De-initialize a TX queue
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @txq: TX queue to be de-initialized
|
|
+ * Any buffer stuck in a queue will be freed.
|
|
+ */
|
|
+static void bl_txq_deinit(struct bl_hw *bl_hw, struct bl_txq *txq)
|
|
+{
|
|
+ spin_lock_bh(&bl_hw->tx_lock);
|
|
+ bl_txq_del_from_hw_list(txq);
|
|
+ txq->idx = TXQ_INACTIVE;
|
|
+ spin_unlock_bh(&bl_hw->tx_lock);
|
|
+
|
|
+ bl_txq_flush(bl_hw, txq);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_vif_init - Initialize all TXQ linked to a vif
|
|
+ *
|
|
+ * @bl_hw: main driver data
|
|
+ * @bl_vif: Pointer on VIF
|
|
+ * @status: Intial txq status
|
|
+ *
|
|
+ * Softmac : 1 VIF TXQ per HWQ
|
|
+ *
|
|
+ * Fullmac : 1 VIF TXQ for BC/MC
|
|
+ * 1 VIF TXQ for MGMT to unknown STA
|
|
+ */
|
|
+void bl_txq_vif_init(struct bl_hw *bl_hw, struct bl_vif *bl_vif,
|
|
+ u8 status)
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+ int idx;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_BCMC_TXQ_TYPE, &idx);
|
|
+ bl_txq_init(txq, idx, status, &bl_hw->hwq[BL_HWQ_BE],
|
|
+ &bl_hw->sta_table[bl_vif->ap.bcmc_index], bl_vif->ndev);
|
|
+
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_UNK_TXQ_TYPE, &idx);
|
|
+ bl_txq_init(txq, idx, status, &bl_hw->hwq[BL_HWQ_VO],
|
|
+ NULL, bl_vif->ndev);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_vif_deinit - Deinitialize all TXQ linked to a vif
|
|
+ *
|
|
+ * @bl_hw: main driver data
|
|
+ * @bl_vif: Pointer on VIF
|
|
+ */
|
|
+void bl_txq_vif_deinit(struct bl_hw * bl_hw, struct bl_vif *bl_vif)
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_BCMC_TXQ_TYPE, NULL);
|
|
+ bl_txq_deinit(bl_hw, txq);
|
|
+
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_UNK_TXQ_TYPE, NULL);
|
|
+ bl_txq_deinit(bl_hw, txq);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_txq_sta_init - Initialize TX queues for a STA
|
|
+ *
|
|
+ * @bl_hw: Main driver data
|
|
+ * @bl_sta: STA for which tx queues need to be initialized
|
|
+ * @status: Intial txq status
|
|
+ *
|
|
+ * This function initialize all the TXQ associated to a STA.
|
|
+ * Softmac : 1 TXQ per TID
|
|
+ *
|
|
+ * Fullmac : 1 TXQ per TID (limited to 8)
|
|
+ * 1 TXQ for MGMT
|
|
+ */
|
|
+void bl_txq_sta_init(struct bl_hw * bl_hw, struct bl_sta *bl_sta,
|
|
+ u8 status)
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+ int tid, idx;
|
|
+ struct bl_vif *bl_vif = NULL;
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_vif = bl_hw->vif_table[bl_sta->vif_idx];
|
|
+
|
|
+ txq = bl_txq_sta_get(bl_sta, 0, &idx, bl_hw);
|
|
+ for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++, txq++, idx++) {
|
|
+ bl_txq_init(txq, idx, status, &bl_hw->hwq[bl_tid2hwq[tid]],
|
|
+ bl_sta, bl_vif->ndev);
|
|
+ txq->ps_id = bl_sta->uapsd_tids & (1 << tid) ? UAPSD_ID : LEGACY_PS_ID;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_sta_deinit - Deinitialize TX queues for a STA
|
|
+ *
|
|
+ * @bl_hw: Main driver data
|
|
+ * @bl_sta: STA for which tx queues need to be deinitialized
|
|
+ */
|
|
+void bl_txq_sta_deinit(struct bl_hw *bl_hw, struct bl_sta *bl_sta)
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+ int i;
|
|
+
|
|
+ txq = bl_txq_sta_get(bl_sta, 0, NULL, bl_hw);
|
|
+
|
|
+ for (i = 0; i < NX_NB_TXQ_PER_STA; i++, txq++) {
|
|
+ bl_txq_deinit(bl_hw, txq);
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+
|
|
+/**
|
|
+ * bl_init_unk_txq - Initialize TX queue for the transmission on a offchannel
|
|
+ *
|
|
+ * @vif: Interface for which the queue has to be initialized
|
|
+ *
|
|
+ * NOTE: Offchannel txq is only active for the duration of the ROC
|
|
+ */
|
|
+void bl_txq_offchan_init(struct bl_vif *bl_vif)
|
|
+{
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+ struct bl_txq *txq;
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ txq = &bl_hw->txq[NX_OFF_CHAN_TXQ_IDX];
|
|
+ bl_txq_init(txq, NX_OFF_CHAN_TXQ_IDX, BL_TXQ_STOP_CHAN,
|
|
+ &bl_hw->hwq[BL_HWQ_VO], NULL, bl_vif->ndev);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_deinit_offchan_txq - Deinitialize TX queue for offchannel
|
|
+ *
|
|
+ * @vif: Interface that manages the STA
|
|
+ *
|
|
+ * This function deintialize txq for one STA.
|
|
+ * Any buffer stuck in a queue will be freed.
|
|
+ */
|
|
+void bl_txq_offchan_deinit(struct bl_vif *bl_vif)
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ txq = &bl_vif->bl_hw->txq[NX_OFF_CHAN_TXQ_IDX];
|
|
+ bl_txq_deinit(bl_vif->bl_hw, txq);
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+/******************************************************************************
|
|
+ * Start/Stop functions
|
|
+ *****************************************************************************/
|
|
+/**
|
|
+ * bl_txq_add_to_hw_list - Add TX queue to a HW queue schedule list.
|
|
+ *
|
|
+ * @txq: TX queue to add
|
|
+ *
|
|
+ * Add the TX queue if not already present in the HW queue list.
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+void bl_txq_add_to_hw_list(struct bl_txq *txq)
|
|
+{
|
|
+ if (!(txq->status & BL_TXQ_IN_HWQ_LIST)) {
|
|
+ trace_txq_add_to_hw(txq);
|
|
+ txq->status |= BL_TXQ_IN_HWQ_LIST;
|
|
+ list_add_tail(&txq->sched_list, &txq->hwq->list);
|
|
+ } else {
|
|
+ BL_DBG("txq is not scheduled for transmission, txq->status=0x%x\n", txq->status);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_del_from_hw_list - Delete TX queue from a HW queue schedule list.
|
|
+ *
|
|
+ * @txq: TX queue to delete
|
|
+ *
|
|
+ * Remove the TX queue from the HW queue list if present.
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+void bl_txq_del_from_hw_list(struct bl_txq *txq)
|
|
+{
|
|
+ if (txq->status & BL_TXQ_IN_HWQ_LIST) {
|
|
+ trace_txq_del_from_hw(txq);
|
|
+ txq->status &= ~BL_TXQ_IN_HWQ_LIST;
|
|
+ list_del(&txq->sched_list);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_start - Try to Start one TX queue
|
|
+ *
|
|
+ * @txq: TX queue to start
|
|
+ * @reason: reason why the TX queue is started (among BL_TXQ_STOP_xxx)
|
|
+ *
|
|
+ * Re-start the TX queue for one reason.
|
|
+ * If after this the txq is no longer stopped and some buffers are ready,
|
|
+ * the TX queue is also added to HW queue list.
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+void bl_txq_start(struct bl_txq *txq, u16 reason)
|
|
+{
|
|
+ BUG_ON(txq==NULL);
|
|
+ if (txq->idx != TXQ_INACTIVE && (txq->status & reason))
|
|
+ {
|
|
+ trace_txq_start(txq, reason);
|
|
+ txq->status &= ~reason;
|
|
+ if (!bl_txq_is_stopped(txq) &&
|
|
+ !skb_queue_empty(&txq->sk_list)) {
|
|
+ bl_txq_add_to_hw_list(txq);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_stop - Stop one TX queue
|
|
+ *
|
|
+ * @txq: TX queue to stop
|
|
+ * @reason: reason why the TX queue is stopped (among BL_TXQ_STOP_xxx)
|
|
+ *
|
|
+ * Stop the TX queue. It will remove the TX queue from HW queue list
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+void bl_txq_stop(struct bl_txq *txq, u16 reason)
|
|
+{
|
|
+ BUG_ON(txq==NULL);
|
|
+ if (txq->idx != TXQ_INACTIVE)
|
|
+ {
|
|
+ trace_txq_stop(txq, reason);
|
|
+ txq->status |= reason;
|
|
+ bl_txq_del_from_hw_list(txq);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_txq_sta_start - Start all the TX queue linked to a STA
|
|
+ *
|
|
+ * @sta: STA whose TX queues must be re-started
|
|
+ * @reason: Reason why the TX queue are restarted (among BL_TXQ_STOP_xxx)
|
|
+ * @bl_hw: Driver main data
|
|
+ *
|
|
+ * This function will re-start all the TX queues of the STA for the reason
|
|
+ * specified. It can be :
|
|
+ * - BL_TXQ_STOP_STA_PS: the STA is no longer in power save mode
|
|
+ * - BL_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
|
|
+ * - BL_TXQ_STOP_CHAN: the STA's VIF is now on the current active channel
|
|
+ *
|
|
+ * Any TX queue with buffer ready and not Stopped for other reasons, will be
|
|
+ * added to the HW queue list
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+void bl_txq_sta_start(struct bl_sta *bl_sta, u16 reason
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ , struct bl_hw *bl_hw
|
|
+#endif
|
|
+ )
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+ int i;
|
|
+ int nb_txq;
|
|
+
|
|
+ if (!bl_sta)
|
|
+ return;
|
|
+
|
|
+ trace_txq_sta_start(bl_sta->sta_idx);
|
|
+
|
|
+ txq = bl_txq_sta_get(bl_sta, 0, NULL, bl_hw);
|
|
+
|
|
+ if (is_multicast_sta(bl_sta->sta_idx))
|
|
+ nb_txq = 1;
|
|
+ else
|
|
+ nb_txq = NX_NB_TXQ_PER_STA;
|
|
+
|
|
+ for (i = 0; i < nb_txq; i++, txq++)
|
|
+ bl_txq_start(txq, reason);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_stop_sta_txq - Stop all the TX queue linked to a STA
|
|
+ *
|
|
+ * @sta: STA whose TX queues must be stopped
|
|
+ * @reason: Reason why the TX queue are stopped (among BL_TX_STOP_xxx)
|
|
+ * @bl_hw: Driver main data
|
|
+ *
|
|
+ * This function will stop all the TX queues of the STA for the reason
|
|
+ * specified. It can be :
|
|
+ * - BL_TXQ_STOP_STA_PS: the STA is in power save mode
|
|
+ * - BL_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
|
|
+ * - BL_TXQ_STOP_CHAN: the STA's VIF is not on the current active channel
|
|
+ *
|
|
+ * Any TX queue present in a HW queue list will be removed from this list.
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+void bl_txq_sta_stop(struct bl_sta *bl_sta, u16 reason
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ , struct bl_hw *bl_hw
|
|
+#endif
|
|
+ )
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+ int i;
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ int nb_txq;
|
|
+#endif
|
|
+
|
|
+ if (!bl_sta)
|
|
+ return;
|
|
+
|
|
+ trace_txq_sta_stop(bl_sta->sta_idx);
|
|
+ txq = bl_txq_sta_get(bl_sta, 0, NULL, bl_hw);
|
|
+
|
|
+ if (is_multicast_sta(bl_sta->sta_idx))
|
|
+ nb_txq = 1;
|
|
+ else
|
|
+ nb_txq = NX_NB_TXQ_PER_STA;
|
|
+
|
|
+ for (i = 0; i < nb_txq; i++, txq++)
|
|
+ bl_txq_stop(txq, reason);
|
|
+
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+static inline
|
|
+void bl_txq_vif_for_each_sta(struct bl_hw *bl_hw, struct bl_vif *bl_vif,
|
|
+ void (*f)(struct bl_sta *, u16, struct bl_hw *),
|
|
+ u16 reason)
|
|
+{
|
|
+
|
|
+ switch (BL_VIF_TYPE(bl_vif)) {
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ {
|
|
+ f(bl_vif->sta.ap, reason, bl_hw);
|
|
+ break;
|
|
+ }
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ bl_vif = bl_vif->ap_vlan.master;
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ {
|
|
+ struct bl_sta *sta;
|
|
+ list_for_each_entry(sta, &bl_vif->ap.sta_list, list) {
|
|
+ f(sta, reason, bl_hw);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ BUG();
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * bl_txq_vif_start - START TX queues of all STA associated to the vif
|
|
+ * and vif's TXQ
|
|
+ *
|
|
+ * @vif: Interface to start
|
|
+ * @reason: Start reason (BL_TXQ_STOP_CHAN or BL_TXQ_STOP_VIF_PS)
|
|
+ * @bl_hw: Driver main data
|
|
+ *
|
|
+ * Iterate over all the STA associated to the vif and re-start them for the
|
|
+ * reason @reason
|
|
+ * Take tx_lock
|
|
+ */
|
|
+void bl_txq_vif_start(struct bl_vif *bl_vif, u16 reason,
|
|
+ struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ trace_txq_vif_start(bl_vif->vif_index);
|
|
+
|
|
+ spin_lock_bh(&bl_hw->tx_lock);
|
|
+ bl_txq_vif_for_each_sta(bl_hw, bl_vif, bl_txq_sta_start, reason);
|
|
+
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_BCMC_TXQ_TYPE, NULL);
|
|
+ bl_txq_start(txq, reason);
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_UNK_TXQ_TYPE, NULL);
|
|
+ bl_txq_start(txq, reason);
|
|
+
|
|
+ spin_unlock_bh(&bl_hw->tx_lock);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_txq_vif_stop - STOP TX queues of all STA associated to the vif
|
|
+ *
|
|
+ * @vif: Interface to stop
|
|
+ * @arg: Stop reason (BL_TXQ_STOP_CHAN or BL_TXQ_STOP_VIF_PS)
|
|
+ * @bl_hw: Driver main data
|
|
+ *
|
|
+ * Iterate over all the STA associated to the vif and stop them for the
|
|
+ * reason BL_TXQ_STOP_CHAN or BL_TXQ_STOP_VIF_PS
|
|
+ * Take tx_lock
|
|
+ */
|
|
+void bl_txq_vif_stop(struct bl_vif *bl_vif, u16 reason,
|
|
+ struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ trace_txq_vif_stop(bl_vif->vif_index);
|
|
+
|
|
+ spin_lock_bh(&bl_hw->tx_lock);
|
|
+
|
|
+ bl_txq_vif_for_each_sta(bl_hw, bl_vif, bl_txq_sta_stop, reason);
|
|
+
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_BCMC_TXQ_TYPE, NULL);
|
|
+ bl_txq_stop(txq, reason);
|
|
+ txq = bl_txq_vif_get(bl_vif, NX_UNK_TXQ_TYPE, NULL);
|
|
+ bl_txq_stop(txq, reason);
|
|
+
|
|
+ spin_unlock_bh(&bl_hw->tx_lock);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+
|
|
+/**
|
|
+ * bl_start_offchan_txq - START TX queue for offchannel frame
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ */
|
|
+void bl_txq_offchan_start(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ txq = &bl_hw->txq[NX_OFF_CHAN_TXQ_IDX];
|
|
+ bl_txq_start(txq, BL_TXQ_STOP_CHAN);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_switch_vif_sta_txq - Associate TXQ linked to a STA to a new vif
|
|
+ *
|
|
+ * @sta: STA whose txq must be switched
|
|
+ * @old_vif: Vif currently associated to the STA (may no longer be active)
|
|
+ * @new_vif: vif which should be associated to the STA for now on
|
|
+ *
|
|
+ * This function will switch the vif (i.e. the netdev) associated to all STA's
|
|
+ * TXQ. This is used when AP_VLAN interface are created.
|
|
+ * If one STA is associated to an AP_vlan vif, it will be moved from the master
|
|
+ * AP vif to the AP_vlan vif.
|
|
+ * If an AP_vlan vif is removed, then STA will be moved back to mastert AP vif.
|
|
+ *
|
|
+ */
|
|
+void bl_txq_sta_switch_vif(struct bl_sta *sta, struct bl_vif *old_vif,
|
|
+ struct bl_vif *new_vif)
|
|
+{
|
|
+ struct bl_hw *bl_hw = new_vif->bl_hw;
|
|
+ struct bl_txq *txq;
|
|
+ int i;
|
|
+
|
|
+ /* start TXQ on the new interface, and update ndev field in txq */
|
|
+ if (!netif_carrier_ok(new_vif->ndev))
|
|
+ netif_carrier_on(new_vif->ndev);
|
|
+ txq = bl_txq_sta_get(sta, 0, NULL, bl_hw);
|
|
+ for (i = 0; i < NX_NB_TID_PER_STA; i++, txq++) {
|
|
+ txq->ndev = new_vif->ndev;
|
|
+ netif_wake_subqueue(txq->ndev, txq->ndev_idx);
|
|
+ }
|
|
+}
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+
|
|
+/******************************************************************************
|
|
+ * TXQ queue/schedule functions
|
|
+ *****************************************************************************/
|
|
+/**
|
|
+ * bl_txq_queue_skb - Queue a buffer in a TX queue
|
|
+ *
|
|
+ * @skb: Buffer to queue
|
|
+ * @txq: TX Queue in which the buffer must be added
|
|
+ * @bl_hw: Driver main data
|
|
+ * @retry: Should it be queued in the retry list
|
|
+ *
|
|
+ * @return: Retrun 1 if txq has been added to hwq list, 0 otherwise
|
|
+ *
|
|
+ * Add a buffer in the buffer list of the TX queue
|
|
+ * and add this TX queue in the HW queue list if the txq is not stopped.
|
|
+ * If this is a retry packet it is added after the last retry packet or at the
|
|
+ * beginning if there is no retry packet queued.
|
|
+ *
|
|
+ * If the STA is in PS mode and this is the first packet queued for this txq
|
|
+ * update TIM.
|
|
+ *
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+int bl_txq_queue_skb(struct sk_buff *skb, struct bl_txq *txq,
|
|
+ struct bl_hw *bl_hw, bool retry)
|
|
+{
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ if (unlikely(txq->sta && txq->sta->ps.active)) {
|
|
+ txq->sta->ps.pkt_ready[txq->ps_id]++;
|
|
+ trace_ps_queue(txq->sta);
|
|
+
|
|
+ if (txq->sta->ps.pkt_ready[txq->ps_id] == 1) {
|
|
+ BL_DBG("unlikely, sta was in ps mode!\n, txq->ps_id=%d\n", txq->ps_id);
|
|
+ bl_set_traffic_status(bl_hw, txq->sta, true, txq->ps_id);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (!retry) {
|
|
+ /* add buffer in the sk_list */
|
|
+ skb_queue_tail(&txq->sk_list, skb);
|
|
+ } else {
|
|
+ if(skb==NULL)
|
|
+ printk("skb = NULL\n");
|
|
+ if(&txq->sk_list == NULL)
|
|
+ printk("txq->sk_list NULL\n");
|
|
+
|
|
+ if (txq->last_retry_skb) {
|
|
+ BL_DBG("append last retry skb\n");
|
|
+ skb_append(txq->last_retry_skb, skb, &txq->sk_list);
|
|
+ }else{
|
|
+ BL_DBG("append retry skb to head\n");
|
|
+ skb_queue_head(&txq->sk_list, skb);
|
|
+ }
|
|
+
|
|
+ txq->last_retry_skb = skb;
|
|
+ txq->nb_retry++;
|
|
+ }
|
|
+
|
|
+ trace_txq_queue_skb(skb, txq, retry);
|
|
+
|
|
+ /* Flowctrl corresponding netdev queue if needed */
|
|
+ /* If too many buffer are queued for this TXQ stop netdev queue */
|
|
+ if ((txq->ndev_idx != NDEV_NO_TXQ) &&
|
|
+ (skb_queue_len(&txq->sk_list) > BL_NDEV_FLOW_CTRL_STOP)) {
|
|
+ //printk("stop: txq[%d], txq_netdev_idx[%d], skb_len[%d], txq->ndev->num_tx_queues[%d]\n", txq->idx, txq->ndev_idx, skb_queue_len(&txq->sk_list), txq->ndev->num_tx_queues);
|
|
+ txq->status |= BL_TXQ_NDEV_FLOW_CTRL;
|
|
+ netif_stop_subqueue(txq->ndev, txq->ndev_idx);
|
|
+ //netif_tx_stop_all_queues(txq->ndev);
|
|
+ trace_txq_flowctrl_stop(txq);
|
|
+ }
|
|
+
|
|
+ /* add it in the hwq list if not stopped and not yet present */
|
|
+ if (!bl_txq_is_stopped(txq)) {
|
|
+ bl_txq_add_to_hw_list(txq);
|
|
+ return 1;
|
|
+ } else {
|
|
+ BL_DBG("txq stopped, txq->status=0x%x\n", txq->status);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_confirm_any - Process buffer confirmed by fw
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @txq: TX Queue
|
|
+ * @hwq: HW Queue
|
|
+ * @sw_txhdr: software descriptor of the confirmed packet
|
|
+ *
|
|
+ * Process a buffer returned by the fw. It doesn't check buffer status
|
|
+ * and only does systematic counter update:
|
|
+ * - hw credit
|
|
+ * - buffer pushed to fw
|
|
+ *
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+void bl_txq_confirm_any(struct bl_hw *bl_hw, struct bl_txq *txq,
|
|
+ struct bl_hwq *hwq, struct bl_sw_txhdr *sw_txhdr)
|
|
+{
|
|
+ int user = 0;
|
|
+
|
|
+ if (txq->pkt_pushed[user])
|
|
+ txq->pkt_pushed[user]--;
|
|
+
|
|
+ hwq->credits[user]++;
|
|
+ bl_hw->stats.cfm_balance[hwq->id]--;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * HWQ processing
|
|
+ *****************************************************************************/
|
|
+static inline
|
|
+s8 bl_txq_get_credits(struct bl_txq *txq)
|
|
+{
|
|
+ s8 cred = txq->credits;
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ /* if destination is in PS mode, push_limit indicates the maximum
|
|
+ number of packet that can be pushed on this txq. */
|
|
+ if (txq->push_limit && (cred > txq->push_limit)) {
|
|
+ cred = txq->push_limit;
|
|
+ }
|
|
+#endif
|
|
+ return cred;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_get_skb_to_push - Get list of buffer to push for one txq
|
|
+ *
|
|
+ * @bl_hw: main driver data
|
|
+ * @hwq: HWQ on wich buffers will be pushed
|
|
+ * @txq: TXQ to get buffers from
|
|
+ * @user: user postion to use
|
|
+ * @sk_list_push: list to update
|
|
+ *
|
|
+ *
|
|
+ * This function will returned a list of buffer to push for one txq.
|
|
+ * It will take into account the number of credit of the HWQ for this user
|
|
+ * position and TXQ (and push_limit for fullmac).
|
|
+ * This allow to get a list that can be pushed without having to test for
|
|
+ * hwq/txq status after each push
|
|
+ *
|
|
+ * If a MU group has been selected for this txq, it will also update the
|
|
+ * counter for the group
|
|
+ *
|
|
+ * @return true if txq no longer have buffer ready after the ones returned.
|
|
+ * false otherwise
|
|
+ */
|
|
+static
|
|
+bool bl_txq_get_skb_to_push(struct bl_hw *bl_hw, struct bl_hwq *hwq,
|
|
+ struct bl_txq *txq, int user,
|
|
+ struct sk_buff_head *sk_list_push)
|
|
+{
|
|
+ int nb_ready = skb_queue_len(&txq->sk_list);
|
|
+ int credits;
|
|
+ int hwq_credits;
|
|
+ int txq_credits;
|
|
+ bool res = false;
|
|
+ int avail_port_num = 0;
|
|
+ u32 wr_bitmap = bl_hw->plat->mp_wr_bitmap;
|
|
+
|
|
+ wr_bitmap &= 0xFFFFFFFE;
|
|
+ while(wr_bitmap) {
|
|
+ wr_bitmap = wr_bitmap & (wr_bitmap-1);
|
|
+ avail_port_num++;
|
|
+ }
|
|
+
|
|
+ hwq_credits = bl_hw->ipc_env->rb_len[hwq->id];
|
|
+ txq_credits = bl_txq_get_credits(txq);
|
|
+// credits = min3(avail_port_num, txq_credits, hwq_credits);
|
|
+ credits = min(avail_port_num, 8);
|
|
+
|
|
+ BL_DBG("bl_txq_get_skb_to_push: env->rb_len[%d]=%d\n", hwq->id, bl_hw->ipc_env->rb_len[hwq->id]);
|
|
+
|
|
+ BL_DBG("nb_ready=%d, txq->idx=%d, hwq->idx=%d, txq->status=0x%x, txq->credits=%d, avail_port_num=%d, hwq_credits=%d, credits=%d\n",
|
|
+ nb_ready, txq->idx, hwq->id, txq->status, bl_txq_get_credits(txq), avail_port_num, hwq_credits, credits);
|
|
+
|
|
+ bl_hw->dbg_credit[bl_hw->dbg_credit_idx].sdio_port = avail_port_num;
|
|
+ bl_hw->dbg_credit[bl_hw->dbg_credit_idx].txq_credit = txq_credits;
|
|
+ bl_hw->dbg_credit[bl_hw->dbg_credit_idx].hwq_credit = hwq_credits;
|
|
+ bl_hw->dbg_credit[bl_hw->dbg_credit_idx].credit = credits;
|
|
+ bl_hw->dbg_credit[bl_hw->dbg_credit_idx].nb_ready = nb_ready;
|
|
+ bl_hw->dbg_credit[bl_hw->dbg_credit_idx].txq_idx = txq->idx;
|
|
+
|
|
+ bl_hw->dbg_credit_idx++;
|
|
+
|
|
+ if(bl_hw->dbg_credit_idx == 50)
|
|
+ bl_hw->dbg_credit_idx = 0;
|
|
+
|
|
+ __skb_queue_head_init(sk_list_push);
|
|
+
|
|
+ if(credits == 0) {
|
|
+ BL_DBG("credits=0, no availe port\n");
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ if(bl_hw->plat->curr_wr_port + credits > 16)
|
|
+ credits -= 1;
|
|
+
|
|
+#if 0
|
|
+ if (credits >= nb_ready) {
|
|
+ skb_queue_splice_init(&txq->sk_list, sk_list_push);
|
|
+ credits = nb_ready;
|
|
+ res = true;
|
|
+ } else {
|
|
+ skb_queue_extract(&txq->sk_list, sk_list_push, credits);
|
|
+#endif
|
|
+
|
|
+ if(credits >= nb_ready) {
|
|
+ skb_queue_extract(&txq->sk_list, sk_list_push, nb_ready);
|
|
+ credits = nb_ready;
|
|
+ res = true;
|
|
+ } else {
|
|
+ skb_queue_extract(&txq->sk_list, sk_list_push, credits);
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ /* When processing PS service period (i.e. push_limit != 0), no longer
|
|
+ process this txq if this is a legacy PS service period (even if no
|
|
+ packet is pushed) or the SP is complete for this txq */
|
|
+ if (txq->push_limit &&
|
|
+ ((txq->ps_id == LEGACY_PS_ID) ||
|
|
+ (credits >= txq->push_limit)))
|
|
+ res = true;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_hwq_process - Process one HW queue list
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @hw_queue: HW queue index to process
|
|
+ *
|
|
+ * The function will iterate over all the TX queues linked in this HW queue
|
|
+ * list. For each TX queue, push as many buffers as possible in the HW queue.
|
|
+ * (NB: TX queue have at least 1 buffer, otherwise it wouldn't be in the list)
|
|
+ * - If TX queue no longer have buffer, remove it from the list and check next
|
|
+ * TX queue
|
|
+ * - If TX queue no longer have credits or has a push_limit (PS mode) and it
|
|
+ * is reached , remove it from the list and check next TX queue
|
|
+ * - If HW queue is full, update list head to start with the next TX queue on
|
|
+ * next call if current TX queue already pushed "too many" pkt in a row, and
|
|
+ * return
|
|
+ *
|
|
+ * To be called when HW queue list is modified:
|
|
+ * - when a buffer is pushed on a TX queue
|
|
+ * - when new credits are received
|
|
+ * - when a STA returns from Power Save mode or receives traffic request.
|
|
+ * - when Channel context change
|
|
+ *
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+#define ALL_HWQ_MASK ((1 << CONFIG_USER_MAX) - 1)
|
|
+
|
|
+void bl_hwq_process(struct bl_hw *bl_hw, struct bl_hwq *hwq)
|
|
+{
|
|
+ struct bl_txq *txq, *next;
|
|
+ int user = 0;
|
|
+
|
|
+ trace_process_hw_queue(hwq);
|
|
+
|
|
+ list_for_each_entry_safe(txq, next, &hwq->list, sched_list) {
|
|
+ struct sk_buff_head sk_list_push;
|
|
+ bool txq_empty;
|
|
+
|
|
+ trace_process_txq(txq);
|
|
+
|
|
+ spin_lock_bh(&bl_hw->txq_lock);
|
|
+
|
|
+ BL_DBG("---> txq->idx=%d, hwq->idx=%d, txq->status=0x%x, txq->credits=%d\n", txq->idx, hwq->id, txq->status, txq->credits);
|
|
+ /* sanity check for debug */
|
|
+ if(!(txq->status & BL_TXQ_IN_HWQ_LIST)) {
|
|
+ printk("txq was not in hwq, but why we probe this txq!\n");
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ break;
|
|
+ }
|
|
+ BUG_ON(txq->idx == TXQ_INACTIVE);
|
|
+// BUG_ON(txq->credits <= 0);
|
|
+ if(skb_queue_len(&txq->sk_list) == 0) {
|
|
+ printk("there is no skb in txq...\n");
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ txq_empty = bl_txq_get_skb_to_push(bl_hw, hwq, txq, user,
|
|
+ &sk_list_push);
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+
|
|
+ if(skb_queue_len(&sk_list_push) != 0)
|
|
+ bl_tx_multi_pkt_push(bl_hw, &sk_list_push);
|
|
+
|
|
+ if (txq_empty) {
|
|
+ if(skb_queue_len(&txq->sk_list) == 0) {
|
|
+ BL_DBG("txq sk_list empty, delete it\n");
|
|
+ bl_txq_del_from_hw_list(txq);
|
|
+ txq->pkt_sent = 0;
|
|
+ } else {
|
|
+ BL_DBG("txq sk_list is not empty!\n");
|
|
+ }
|
|
+ } else {
|
|
+ BL_DBG("txq_empty=false, txq->credits=%d\n", txq->credits);
|
|
+// if(txq->credits != 0) {
|
|
+// break;
|
|
+// }
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ /* Unable to complete PS traffic request because of hwq credit */
|
|
+ if (txq->push_limit && txq->sta) {
|
|
+ BL_DBG("Unable to complete PS traffic request because of hwq credit\n");
|
|
+ if (txq->ps_id == LEGACY_PS_ID) {
|
|
+ /* for legacy PS abort SP and wait next ps-poll */
|
|
+ BL_DBG("for legacy PS abort SP and wait next ps-poll\n");
|
|
+ txq->sta->ps.sp_cnt[txq->ps_id] -= txq->push_limit;
|
|
+ txq->push_limit = 0;
|
|
+ }
|
|
+ /* for u-apsd need to complete the SP to send EOSP frame */
|
|
+ }
|
|
+
|
|
+ /* restart netdev queue if number of queued buffer is below threshold */
|
|
+ if (unlikely(txq->status & BL_TXQ_NDEV_FLOW_CTRL) &&
|
|
+ skb_queue_len(&txq->sk_list) < BL_NDEV_FLOW_CTRL_RESTART) {
|
|
+ BL_DBG("restart netdev queue if number of queued buffer is below threshold\n");
|
|
+ txq->status &= ~BL_TXQ_NDEV_FLOW_CTRL;
|
|
+ netif_wake_subqueue(txq->ndev, txq->ndev_idx);
|
|
+ //trace_txq_flowctrl_restart(txq);
|
|
+ }
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_hwq_process_all - Process all HW queue list
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ *
|
|
+ * Loop over all HWQ, and process them if needed
|
|
+ * To be called with tx_lock hold
|
|
+ */
|
|
+void bl_hwq_process_all(struct bl_hw *bl_hw)
|
|
+{
|
|
+ int id;
|
|
+
|
|
+ if(!(bl_hw->plat->mp_wr_bitmap & 0xfffe))
|
|
+ return;
|
|
+
|
|
+ for (id = ARRAY_SIZE(bl_hw->hwq) - 1; id >= 0 ; id--) {
|
|
+ bl_hwq_process(bl_hw, &bl_hw->hwq[id]);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_hwq_init - Initialize all hwq structures
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ *
|
|
+ */
|
|
+void bl_hwq_init(struct bl_hw *bl_hw)
|
|
+{
|
|
+ int i, j;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(bl_hw->hwq); i++) {
|
|
+ struct bl_hwq *hwq = &bl_hw->hwq[i];
|
|
+
|
|
+ for (j = 0 ; j < CONFIG_USER_MAX; j++)
|
|
+ hwq->credits[j] = nx_txdesc_cnt[i];
|
|
+ hwq->id = i;
|
|
+ hwq->size = nx_txdesc_cnt[i];
|
|
+ INIT_LIST_HEAD(&hwq->list);
|
|
+ }
|
|
+}
|
|
diff -Naur /dev/null_txq.h b/drivers/net/wireless/hflps170/bl_txq.h
|
|
--- /dev/null_txq.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_txq.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,335 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_txq.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#ifndef _BL_TXQ_H_
|
|
+#define _BL_TXQ_H_
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/bitops.h>
|
|
+#include <linux/ieee80211.h>
|
|
+
|
|
+/**
|
|
+ * Fullmac TXQ configuration:
|
|
+ * - STA: 1 TXQ per TID (limited to 8)
|
|
+ * 1 txq for bufferable MGT frames
|
|
+ * - VIF: 1 tXQ for Multi/Broadcast +
|
|
+ * 1 TXQ for MGT for unknown STAs or non-bufferable MGT frames
|
|
+ * - 1 TXQ for offchannel transmissions
|
|
+ *
|
|
+ *
|
|
+ * Txq mapping looks like
|
|
+ * for NX_REMOTE_STA_MAX=10 and NX_VIRT_DEV_MAX=4
|
|
+ *
|
|
+ * | TXQ | NDEV_ID | VIF | STA | TID | HWQ |
|
|
+ * |-----+---------+-----+-------+------+-----|-
|
|
+ * | 0 | 0 | | 0 | 0 | 1 | 9 TXQ per STA
|
|
+ * | 1 | 1 | | 0 | 1 | 0 | (8 data + 1 mgmt)
|
|
+ * | 2 | 2 | | 0 | 2 | 0 |
|
|
+ * | 3 | 3 | | 0 | 3 | 1 |
|
|
+ * | 4 | 4 | | 0 | 4 | 2 |
|
|
+ * | 5 | 5 | | 0 | 5 | 2 |
|
|
+ * | 6 | 6 | | 0 | 6 | 3 |
|
|
+ * | 7 | 7 | | 0 | 7 | 3 |
|
|
+ * | 8 | N/A | | 0 | MGMT | 3 |
|
|
+ * |-----+---------+-----+-------+------+-----|-
|
|
+ * | ... | | | | | | Same for all STAs
|
|
+ * |-----+---------+-----+-------+------+-----|-
|
|
+ * | 90 | 80 | 0 | BC/MC | 0 | 1/4 | 1 TXQ for BC/MC per VIF
|
|
+ * | ... | | | | | |
|
|
+ * | 93 | 80 | 3 | BC/MC | 0 | 1/4 |
|
|
+ * |-----+---------+-----+-------+------+-----|-
|
|
+ * | 94 | N/A | 0 | N/A | MGMT | 3 | 1 TXQ for unknown STA per VIF
|
|
+ * | ... | | | | | |
|
|
+ * | 97 | N/A | 3 | N/A | MGMT | 3 |
|
|
+ * |-----+---------+-----+-------+------+-----|-
|
|
+ * | 98 | N/A | | N/A | MGMT | 3 | 1 TXQ for offchannel frame
|
|
+ */
|
|
+#define NX_NB_TID_PER_STA 8
|
|
+#define NX_NB_TXQ_PER_STA (NX_NB_TID_PER_STA + 1)
|
|
+#define NX_NB_TXQ_PER_VIF 2
|
|
+#define NX_NB_TXQ ((NX_NB_TXQ_PER_STA * NX_REMOTE_STA_MAX) + \
|
|
+ (NX_NB_TXQ_PER_VIF * NX_VIRT_DEV_MAX) + 1)
|
|
+
|
|
+#define NX_FIRST_VIF_TXQ_IDX (NX_REMOTE_STA_MAX * NX_NB_TXQ_PER_STA)
|
|
+#define NX_FIRST_BCMC_TXQ_IDX NX_FIRST_VIF_TXQ_IDX
|
|
+#define NX_FIRST_UNK_TXQ_IDX (NX_FIRST_BCMC_TXQ_IDX + NX_VIRT_DEV_MAX)
|
|
+
|
|
+#define NX_OFF_CHAN_TXQ_IDX (NX_FIRST_VIF_TXQ_IDX + \
|
|
+ (NX_VIRT_DEV_MAX * NX_NB_TXQ_PER_VIF))
|
|
+#define NX_BCMC_TXQ_TYPE 0
|
|
+#define NX_UNK_TXQ_TYPE 1
|
|
+
|
|
+/**
|
|
+ * Each data TXQ is a netdev queue. TXQ to send MGT are not data TXQ as
|
|
+ * they did not recieved buffer from netdev interface.
|
|
+ * Need to allocate the maximum case.
|
|
+ * AP : all STAs + 1 BC/MC
|
|
+ */
|
|
+#define NX_NB_NDEV_TXQ ((NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX) + 1 )
|
|
+#define NX_BCMC_TXQ_NDEV_IDX (NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX)
|
|
+#define NX_STA_NDEV_IDX(tid, sta_idx) ((tid) + (sta_idx) * NX_NB_TID_PER_STA)
|
|
+#define NDEV_NO_TXQ 0xffff
|
|
+#if (NX_NB_NDEV_TXQ >= NDEV_NO_TXQ)
|
|
+#error("Need to increase struct bl_txq->ndev_idx size")
|
|
+#endif
|
|
+
|
|
+/* stop netdev queue when number of queued buffers if greater than this */
|
|
+#define BL_NDEV_FLOW_CTRL_STOP 200//32
|
|
+/* restart netdev queue when number of queued buffers is lower than this */
|
|
+#define BL_NDEV_FLOW_CTRL_RESTART 32//16
|
|
+
|
|
+#define TXQ_INACTIVE 0xffff
|
|
+#if (NX_NB_TXQ >= TXQ_INACTIVE)
|
|
+#error("Need to increase struct bl_txq->idx size")
|
|
+#endif
|
|
+
|
|
+#define NX_TXQ_INITIAL_CREDITS 32
|
|
+
|
|
+/**
|
|
+ * struct bl_hwq - Structure used to save information relative to
|
|
+ * an AC TX queue (aka HW queue)
|
|
+ * @list: List of TXQ, that have buffers ready for this HWQ
|
|
+ * @credits: available credit for the queue (i.e. nb of buffers that
|
|
+ * can be pushed to FW )
|
|
+ * @id Id of the HWQ among BL_HWQ_....
|
|
+ * @size size of the queue
|
|
+ * @need_processing Indicate if hwq should be processed
|
|
+ * @len number of packet ready to be pushed to fw for this HW queue
|
|
+ * @len_stop threshold to stop mac80211(i.e. netdev) queues. Stop queue when
|
|
+ * driver has more than @len_stop packets ready.
|
|
+ * @len_start threshold to wake mac8011 queues. Wake queue when driver has
|
|
+ * less than @len_start packets ready.
|
|
+ */
|
|
+struct bl_hwq {
|
|
+ struct list_head list;
|
|
+ u8 credits[CONFIG_USER_MAX];
|
|
+ u8 size;
|
|
+ u8 id;
|
|
+ bool need_processing;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * enum bl_push_flags - Flags of pushed buffer
|
|
+ *
|
|
+ * @BL_PUSH_RETRY Pushing a buffer for retry
|
|
+ * @BL_PUSH_IMMEDIATE Pushing a buffer without queuing it first
|
|
+ */
|
|
+enum bl_push_flags {
|
|
+ BL_PUSH_RETRY = BIT(0),
|
|
+ BL_PUSH_IMMEDIATE = BIT(1),
|
|
+};
|
|
+
|
|
+/**
|
|
+ * enum bl_txq_flags - TXQ status flag
|
|
+ *
|
|
+ * @BL_TXQ_IN_HWQ_LIST The queue is scheduled for transmission
|
|
+ * @BL_TXQ_STOP_FULL No more credits for the queue
|
|
+ * @BL_TXQ_STOP_CSA CSA is in progress
|
|
+ * @BL_TXQ_STOP_STA_PS Destiniation sta is currently in power save mode
|
|
+ * @BL_TXQ_STOP_VIF_PS Vif owning this queue is currently in power save mode
|
|
+ * @BL_TXQ_STOP_CHAN Channel of this queue is not the current active channel
|
|
+ * @BL_TXQ_STOP_MU_POS TXQ is stopped waiting for all the buffers pushed to
|
|
+ * fw to be confirmed
|
|
+ * @BL_TXQ_STOP All possible reason to have a txq stopped
|
|
+ * @BL_TXQ_NDEV_FLOW_CTRL associated netdev queue is currently stopped.
|
|
+ * Note: when a TXQ is flowctrl it is NOT stopped
|
|
+ */
|
|
+enum bl_txq_flags {
|
|
+ BL_TXQ_IN_HWQ_LIST = BIT(0),
|
|
+ BL_TXQ_STOP_FULL = BIT(1),
|
|
+ BL_TXQ_STOP_CSA = BIT(2),
|
|
+ BL_TXQ_STOP_STA_PS = BIT(3),
|
|
+ BL_TXQ_STOP_VIF_PS = BIT(4),
|
|
+ BL_TXQ_STOP_CHAN = BIT(5),
|
|
+ BL_TXQ_STOP_MU_POS = BIT(6),
|
|
+ BL_TXQ_STOP = (BL_TXQ_STOP_FULL | BL_TXQ_STOP_CSA |
|
|
+ BL_TXQ_STOP_STA_PS | BL_TXQ_STOP_VIF_PS |
|
|
+ BL_TXQ_STOP_CHAN) ,
|
|
+ BL_TXQ_NDEV_FLOW_CTRL = BIT(7),
|
|
+};
|
|
+
|
|
+
|
|
+/**
|
|
+ * struct bl_txq - Structure used to save information relative to
|
|
+ * a RA/TID TX queue
|
|
+ *
|
|
+ * @idx: Unique txq idx. Set to TXQ_INACTIVE if txq is not used.
|
|
+ * @status: bitfield of @bl_txq_flags.
|
|
+ * @credits: available credit for the queue (i.e. nb of buffers that
|
|
+ * can be pushed to FW).
|
|
+ * @pkt_sent: number of consecutive pkt sent without leaving HW queue list
|
|
+ * @pkt_pushed: number of pkt currently pending for transmission confirmation
|
|
+ * @sched_list: list node for HW queue schedule list (bl_hwq.list)
|
|
+ * @sk_list: list of buffers to push to fw
|
|
+ * @last_retry_skb: pointer on the last skb in @sk_list that is a retry.
|
|
+ * (retry skb are stored at the beginning of the list)
|
|
+ * NULL if no retry skb is queued in @sk_list
|
|
+ * @nb_retry: Number of retry packet queued.
|
|
+ * @hwq: Pointer on the associated HW queue.
|
|
+ *
|
|
+ * SOFTMAC specific:
|
|
+ * @baw: Block Ack window information
|
|
+ * @amsdu_anchor: pointer to bl_sw_txhdr of the first subframe of the A-MSDU.
|
|
+ * NULL if no A-MSDU frame is in construction
|
|
+ * @amsdu_ht_len_cap:
|
|
+ * @amsdu_vht_len_cap:
|
|
+ * @tid:
|
|
+ *
|
|
+ * FULLMAC specific
|
|
+ * @ps_id: Index to use for Power save mode (LEGACY or UAPSD)
|
|
+ * @push_limit: number of packet to push before removing the txq from hwq list.
|
|
+ * (we always have push_limit < skb_queue_len(sk_list))
|
|
+ * @ndev_idx: txq idx from netdev point of view (0xFF for non netdev queue)
|
|
+ * @ndev: pointer to ndev of the corresponding vif
|
|
+ * @amsdu: pointer to bl_sw_txhdr of the first subframe of the A-MSDU.
|
|
+ * NULL if no A-MSDU frame is in construction
|
|
+ * @amsdu_len: Maximum size allowed for an A-MSDU. 0 means A-MSDU not allowed
|
|
+ */
|
|
+struct bl_txq {
|
|
+ u16 idx;
|
|
+ u8 status;
|
|
+ s8 credits;
|
|
+ u8 pkt_sent;
|
|
+ u8 pkt_pushed[CONFIG_USER_MAX];
|
|
+ struct list_head sched_list;
|
|
+ struct sk_buff_head sk_list;
|
|
+ struct sk_buff *last_retry_skb;
|
|
+ struct bl_hwq *hwq;
|
|
+ int nb_retry;
|
|
+ struct bl_sta *sta;
|
|
+ u8 ps_id;
|
|
+ u8 push_limit;
|
|
+ u16 ndev_idx;
|
|
+ struct net_device *ndev;
|
|
+ #ifdef CONFIG_BL_AMSDUS_TX
|
|
+ struct bl_sw_txhdr *amsdu;
|
|
+ u16 amsdu_len;
|
|
+ #endif /* CONFIG_BL_AMSDUS_TX */
|
|
+};
|
|
+
|
|
+struct bl_sta;
|
|
+struct bl_vif;
|
|
+struct bl_hw;
|
|
+struct bl_sw_txhdr;
|
|
+
|
|
+#define BL_TXQ_GROUP_ID(txq) 0
|
|
+#define BL_TXQ_POS_ID(txq) 0
|
|
+
|
|
+static inline bool bl_txq_is_stopped(struct bl_txq *txq)
|
|
+{
|
|
+ return (txq->status & BL_TXQ_STOP);
|
|
+}
|
|
+
|
|
+static inline bool bl_txq_is_full(struct bl_txq *txq)
|
|
+{
|
|
+ return (txq->status & BL_TXQ_STOP_FULL);
|
|
+}
|
|
+
|
|
+static inline bool bl_txq_is_scheduled(struct bl_txq *txq)
|
|
+{
|
|
+ return (txq->status & BL_TXQ_IN_HWQ_LIST);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * if
|
|
+ * - txq is not stopped
|
|
+ * - hwq has credits
|
|
+ * - there is no buffer queued
|
|
+ * then a buffer can be immediately pushed without having to queue it first
|
|
+ */
|
|
+static inline bool bl_txq_is_ready_for_push(struct bl_txq *txq)
|
|
+{
|
|
+ return (!bl_txq_is_stopped(txq) &&
|
|
+ txq->hwq->credits[BL_TXQ_POS_ID(txq)] > 0 &&
|
|
+ skb_queue_empty(&txq->sk_list));
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * extract the first @nb_elt of @list and append them to @head
|
|
+ * It is assume that:
|
|
+ * - @list contains more that @nb_elt
|
|
+ * - There is no need to take @list nor @head lock to modify them
|
|
+ */
|
|
+static inline void skb_queue_extract(struct sk_buff_head *list,
|
|
+ struct sk_buff_head *head, int nb_elt)
|
|
+{
|
|
+ int i;
|
|
+ struct sk_buff *first, *last, *ptr;
|
|
+
|
|
+ first = ptr = list->next;
|
|
+ for (i = 0; i < nb_elt; i++) {
|
|
+ ptr = ptr->next;
|
|
+ }
|
|
+ last = ptr->prev;
|
|
+
|
|
+ /* unlink nb_elt in list */
|
|
+ list->qlen -= nb_elt;
|
|
+ list->next = ptr;
|
|
+ ptr->prev = (struct sk_buff *)list;
|
|
+
|
|
+ /* append nb_elt at end of head */
|
|
+ head->qlen += nb_elt;
|
|
+ last->next = (struct sk_buff *)head;
|
|
+ head->prev->next = first;
|
|
+ first->prev = head->prev;
|
|
+ head->prev = last;
|
|
+}
|
|
+
|
|
+struct bl_txq *bl_txq_sta_get(struct bl_sta *sta, u8 tid, int *idx,
|
|
+ struct bl_hw * bl_hw);
|
|
+struct bl_txq *bl_txq_vif_get(struct bl_vif *vif, u8 type, int *idx);
|
|
+
|
|
+/* return status bits related to the vif */
|
|
+static inline u8 bl_txq_vif_get_status(struct bl_vif *bl_vif)
|
|
+{
|
|
+ struct bl_txq *txq = bl_txq_vif_get(bl_vif, 0, NULL);
|
|
+ return (txq->status & (BL_TXQ_STOP_CHAN | BL_TXQ_STOP_VIF_PS));
|
|
+}
|
|
+
|
|
+void bl_txq_vif_init(struct bl_hw * bl_hw, struct bl_vif *vif,
|
|
+ u8 status);
|
|
+void bl_txq_vif_deinit(struct bl_hw * bl_hw, struct bl_vif *vif);
|
|
+void bl_txq_sta_init(struct bl_hw * bl_hw, struct bl_sta *bl_sta,
|
|
+ u8 status);
|
|
+void bl_txq_sta_deinit(struct bl_hw * bl_hw, struct bl_sta *bl_sta);
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+void bl_txq_offchan_init(struct bl_vif *bl_vif);
|
|
+void bl_txq_offchan_deinit(struct bl_vif *bl_vif);
|
|
+#endif
|
|
+
|
|
+
|
|
+void bl_txq_add_to_hw_list(struct bl_txq *txq);
|
|
+void bl_txq_del_from_hw_list(struct bl_txq *txq);
|
|
+void bl_txq_stop(struct bl_txq *txq, u16 reason);
|
|
+void bl_txq_start(struct bl_txq *txq, u16 reason);
|
|
+void bl_txq_vif_start(struct bl_vif *vif, u16 reason,
|
|
+ struct bl_hw *bl_hw);
|
|
+void bl_txq_vif_stop(struct bl_vif *vif, u16 reason,
|
|
+ struct bl_hw *bl_hw);
|
|
+
|
|
+void bl_txq_sta_start(struct bl_sta *sta, u16 reason,
|
|
+ struct bl_hw *bl_hw);
|
|
+void bl_txq_sta_stop(struct bl_sta *sta, u16 reason,
|
|
+ struct bl_hw *bl_hw);
|
|
+void bl_txq_offchan_start(struct bl_hw *bl_hw);
|
|
+void bl_txq_sta_switch_vif(struct bl_sta *sta, struct bl_vif *old_vif,
|
|
+ struct bl_vif *new_vif);
|
|
+
|
|
+int bl_txq_queue_skb(struct sk_buff *skb, struct bl_txq *txq,
|
|
+ struct bl_hw *bl_hw, bool retry);
|
|
+void bl_txq_confirm_any(struct bl_hw *bl_hw, struct bl_txq *txq,
|
|
+ struct bl_hwq *hwq, struct bl_sw_txhdr *sw_txhdr);
|
|
+
|
|
+
|
|
+void bl_hwq_init(struct bl_hw *bl_hw);
|
|
+void bl_hwq_process(struct bl_hw *bl_hw, struct bl_hwq *hwq);
|
|
+void bl_hwq_process_all(struct bl_hw *bl_hw);
|
|
+
|
|
+#endif /* _BL_TXQ_H_ */
|
|
diff -Naur /dev/null_utils.c b/drivers/net/wireless/hflps170/bl_utils.c
|
|
--- /dev/null_utils.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_utils.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,415 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file bl_utils.c
|
|
+ *
|
|
+ * @brief IPC utility function definitions
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#include <linux/device.h>
|
|
+#include <linux/dmapool.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+#include "bl_utils.h"
|
|
+#include "bl_defs.h"
|
|
+#include "bl_rx.h"
|
|
+#include "bl_tx.h"
|
|
+#include "bl_msg_rx.h"
|
|
+#include "bl_debugfs.h"
|
|
+
|
|
+#ifdef CONFIG_BL_MOD_LEV_DBG
|
|
+unsigned long bl_filter_severity = 0xffffffff;
|
|
+unsigned long bl_filter_module = 0x0;
|
|
+#else
|
|
+unsigned long bl_filter_severity;
|
|
+unsigned long bl_filter_module;
|
|
+#endif
|
|
+
|
|
+struct device_attribute dev_attr_filter_severity;
|
|
+EXPORT_SYMBOL_GPL(dev_attr_filter_severity);
|
|
+struct device_attribute dev_attr_filter_module;
|
|
+EXPORT_SYMBOL_GPL(dev_attr_filter_module);
|
|
+
|
|
+module_param(bl_filter_severity, ulong, S_IRUGO|S_IWUSR);
|
|
+MODULE_PARM_DESC(bl_filter_severity, "used to filter severity");
|
|
+module_param(bl_filter_module, ulong, S_IRUGO|S_IWUSR);
|
|
+MODULE_PARM_DESC(bl_filter_module, "used to filter module");
|
|
+
|
|
+static ssize_t filter_severity_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ return sprintf(buf, "%lx\n", bl_filter_severity);
|
|
+}
|
|
+
|
|
+static ssize_t filter_severity_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ if (sscanf(buf, "%lx", &bl_filter_severity) != 1)
|
|
+ return -EINVAL;
|
|
+ return count;
|
|
+}
|
|
+DEVICE_ATTR_RW(filter_severity);
|
|
+
|
|
+static ssize_t filter_module_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ return sprintf(buf, "%lx\n", bl_filter_module);
|
|
+}
|
|
+
|
|
+static ssize_t filter_module_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ if (sscanf(buf, "%lx", &bl_filter_module) != 1)
|
|
+ return -EINVAL;
|
|
+ return count;
|
|
+}
|
|
+DEVICE_ATTR_RW(filter_module);
|
|
+
|
|
+void log_buf_output(struct bl_hw *bl_hw, const char* buf, size_t size) {
|
|
+ size_t write_size = 0, write_index = 0;
|
|
+ //char tmp_buf[256] = {0};
|
|
+
|
|
+ while (true) {
|
|
+ if (bl_hw->buf_write_size + size > LOG_BUF_OUTPUT_BUF_SIZE) {
|
|
+ write_size = LOG_BUF_OUTPUT_BUF_SIZE - bl_hw->buf_write_size;
|
|
+ memcpy(bl_hw->log_buff + bl_hw->buf_write_size, buf + write_index, write_size);
|
|
+ //snprintf(tmp_buf, write_size, "%s", buf + write_index);
|
|
+ write_index += write_size;
|
|
+ size -= write_size;
|
|
+ /* reset write index */
|
|
+ bl_hw->buf_write_size = 0;
|
|
+ } else {
|
|
+ memcpy(bl_hw->log_buff + bl_hw->buf_write_size, buf + write_index, size);
|
|
+ bl_hw->buf_write_size += size;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+size_t bl_log_strcpy(size_t cur_len, char *dst, const char *src) {
|
|
+ const char *src_old = src;
|
|
+
|
|
+ ASSERT_ERR(dst);
|
|
+ ASSERT_ERR(src);
|
|
+
|
|
+ while (*src != 0) {
|
|
+ /* make sure destination has enough space */
|
|
+ if (cur_len++ < LOG_LINE_BUF_SIZE) {
|
|
+ *dst++ = *src++;
|
|
+ } else {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return src - src_old;
|
|
+}
|
|
+
|
|
+#define LOG_LINE_NUM_MAX_LEN 5
|
|
+static void bl_do_log(struct bl_hw *bl_hw, const char *func, const long line, const char *fmt_usr, va_list args)
|
|
+{
|
|
+ static char buf[LOG_LINE_BUF_SIZE];
|
|
+ char line_num[LOG_LINE_NUM_MAX_LEN + 1] = { 0 };
|
|
+ int log_len = 0;
|
|
+
|
|
+ snprintf(line_num, LOG_LINE_NUM_MAX_LEN, "%ld", line);
|
|
+ log_len += bl_log_strcpy(log_len, buf + log_len, line_num);
|
|
+ log_len += bl_log_strcpy(log_len, buf + log_len, ":");
|
|
+ log_len += bl_log_strcpy(log_len, buf + log_len, func);
|
|
+ log_len += bl_log_strcpy(log_len, buf + log_len, ":");
|
|
+
|
|
+ log_len += vsnprintf(buf + log_len, LOG_LINE_BUF_SIZE - log_len, fmt_usr, args);
|
|
+ log_buf_output(bl_hw, buf, log_len);
|
|
+ //printk("%s", buf);
|
|
+}
|
|
+
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ * @brief Function formatting a string and sending it to the defined output
|
|
+ *
|
|
+ * @param[in] fmt Format string
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+void dbg_test_print(struct bl_hw *bl_hw, const char *func, const long line, const char *fmt, ...)
|
|
+{
|
|
+ const char *fmt_usr = (const char*) fmt;
|
|
+ uint32_t severity = 0;
|
|
+ va_list args;
|
|
+ va_start(args, fmt);
|
|
+
|
|
+ //printk("%d %d %d %d\n", fmt_usr[0], fmt_usr[1], DBG_MOD_MAX, DBG_SEV_MIN);
|
|
+
|
|
+ // permit all the debug message is permited
|
|
+ bl_filter_severity = DBG_SEV_MAX;
|
|
+
|
|
+ if (bl_filter_severity == 0) return;
|
|
+
|
|
+ do
|
|
+ {
|
|
+ // Get the prefix
|
|
+ unsigned char prefix = ((unsigned char)*fmt_usr) & 0xFF;
|
|
+
|
|
+ // ASCII char, start of the user string
|
|
+ if (prefix < DBG_MOD_MIN) break;
|
|
+
|
|
+ if (prefix < DBG_MOD_MAX)
|
|
+ {
|
|
+ // test module, if filtered returns
|
|
+ if (~bl_filter_module & CO_BIT(prefix - DBG_MOD_MIN)) return;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // must be severity code
|
|
+ ASSERT_ERR(DBG_SEV_MIN <= prefix && prefix < DBG_SEV_MAX);
|
|
+ severity = (uint32_t)(prefix - DBG_SEV_MIN);
|
|
+
|
|
+ // test severity, if filtered returns
|
|
+ if (bl_filter_severity <= severity) return;
|
|
+ }
|
|
+
|
|
+ // Check first and second char
|
|
+ fmt_usr++;
|
|
+ }
|
|
+ while (fmt_usr != fmt + 2);
|
|
+
|
|
+ // print
|
|
+ bl_do_log(bl_hw, func, line, fmt_usr, args);
|
|
+
|
|
+ va_end(args);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ *
|
|
+ */
|
|
+static int dbginfo_allocs(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct dbg_debug_dump_tag *buf;
|
|
+ u32 len = sizeof(*buf);
|
|
+
|
|
+ /* Allocate the debug information buffer */
|
|
+ buf = kmalloc(len, GFP_KERNEL);
|
|
+
|
|
+ if (!buf) {
|
|
+ printk(KERN_CRIT "%s:%d: buffer alloc of size %u failed\n\n",
|
|
+ __func__, __LINE__, len);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ bl_hw->dbginfo.buf = buf;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ *
|
|
+ */
|
|
+static void bl_dbginfo_deallocs(struct bl_hw *bl_hw)
|
|
+{
|
|
+ if (bl_hw->dbginfo.buf) {
|
|
+ kfree(bl_hw->dbginfo.buf);
|
|
+ bl_hw->dbginfo.buf = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * @brief Deallocate storage elements.
|
|
+ *
|
|
+ * This function deallocates all the elements required for communications with LMAC,
|
|
+ * such as Rx Data elements, MSGs elements, ...
|
|
+ *
|
|
+ * This function should be called in correspondence with the allocation function.
|
|
+ *
|
|
+ * @param[in] bl_hw Pointer to main structure storing all the relevant information
|
|
+ */
|
|
+static void bl_elems_deallocs(struct bl_hw *bl_hw)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_dbginfo_deallocs(bl_hw);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @brief Allocate storage elements.
|
|
+ *
|
|
+ * This function allocates all the elements required for communications with LMAC,
|
|
+ * such as Rx Data elements, MSGs elements, ...
|
|
+ *
|
|
+ * This function should be called in correspondence with the deallocation function.
|
|
+ *
|
|
+ * @param[in] bl_hw Pointer to main structure storing all the relevant information
|
|
+ */
|
|
+static int bl_elems_allocs(struct bl_hw *bl_hw)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+ /* Initialize the debug information buffer */
|
|
+ if (dbginfo_allocs(bl_hw)) {
|
|
+ printk(KERN_CRIT "%s:%d: ALLOCATIONS FAILED !\n", __func__,
|
|
+ __LINE__);
|
|
+ goto err_alloc;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_alloc:
|
|
+ bl_elems_deallocs(bl_hw);
|
|
+ return -ENOMEM;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * WLAN driver call-back function for message reception indication
|
|
+ */
|
|
+u8 bl_msgind(void *pthis, void *hostid)
|
|
+{
|
|
+ u8 ret = 0;
|
|
+ struct bl_hw *bl_hw = (struct bl_hw *)pthis;
|
|
+ struct ipc_e2a_msg *msg = (struct ipc_e2a_msg *)hostid;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Relay further actions to the msg parser */
|
|
+ bl_rx_handle_msg(bl_hw, msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * FIXME
|
|
+ *
|
|
+ */
|
|
+u8 bl_msgackind(void *pthis, void *hostid)
|
|
+{
|
|
+ struct bl_hw *bl_hw = (struct bl_hw *)pthis;
|
|
+
|
|
+ bl_hw->cmd_mgr.llind(&bl_hw->cmd_mgr, (struct bl_cmd *)hostid);
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * WLAN driver call-back function for primary TBTT indication
|
|
+ */
|
|
+void bl_prim_tbtt_ind(void *pthis)
|
|
+{
|
|
+}
|
|
+
|
|
+/**
|
|
+ * WLAN driver call-back function for secondary TBTT indication
|
|
+ */
|
|
+void bl_sec_tbtt_ind(void *pthis)
|
|
+{
|
|
+ /* TODO */
|
|
+}
|
|
+
|
|
+/**
|
|
+ * WLAN driver call-back function for Debug message reception indication
|
|
+ */
|
|
+u8 bl_dbgind(void *pthis, void *hostid)
|
|
+{
|
|
+ struct bl_dbg_elem *dbg_elem = hostid;
|
|
+ struct ipc_dbg_msg *dbg_msg;
|
|
+ u8 ret = 0;
|
|
+
|
|
+ /* Retrieve the message structure */
|
|
+ dbg_msg = (struct ipc_dbg_msg *)dbg_elem->dbgbuf_ptr;
|
|
+
|
|
+ /* Look for pattern which means that this hostbuf has been used for a MSG */
|
|
+ if (dbg_msg->pattern != IPC_DBG_VALID_PATTERN) {
|
|
+ ret = -1;
|
|
+ goto dbg_no_push;
|
|
+ }
|
|
+
|
|
+ /* Display the LMAC string */
|
|
+ printk("lmac %s", (char *)dbg_msg->string);
|
|
+
|
|
+ /* Reset the msg element and re-use it */
|
|
+ dbg_msg->pattern = 0;
|
|
+ wmb();
|
|
+
|
|
+dbg_no_push:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int bl_ipc_init(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct ipc_host_cb_tag cb;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* initialize the API interface */
|
|
+ cb.recv_data_ind = bl_rxdataind;
|
|
+ cb.recv_msg_ind = bl_msgind;
|
|
+ cb.recv_msgack_ind = bl_msgackind;
|
|
+ cb.recv_dbg_ind = bl_dbgind;
|
|
+ cb.send_data_cfm = bl_txdatacfm;
|
|
+ cb.prim_tbtt_ind = bl_prim_tbtt_ind;
|
|
+ cb.sec_tbtt_ind = bl_sec_tbtt_ind;
|
|
+
|
|
+ /* set the IPC environment */
|
|
+ bl_hw->ipc_env = (struct ipc_host_env_tag *)
|
|
+ kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL);
|
|
+
|
|
+ /* call the initialization of the IPC */
|
|
+ ipc_host_init(bl_hw->ipc_env, &cb, bl_hw);
|
|
+
|
|
+ bl_cmd_mgr_init(&bl_hw->cmd_mgr);
|
|
+
|
|
+ return bl_elems_allocs(bl_hw);
|
|
+}
|
|
+
|
|
+/**
|
|
+ *
|
|
+ */
|
|
+void bl_ipc_deinit(struct bl_hw *bl_hw)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_ipc_tx_drain(bl_hw);
|
|
+ bl_cmd_mgr_deinit(&bl_hw->cmd_mgr);
|
|
+ bl_elems_deallocs(bl_hw);
|
|
+ kfree(bl_hw->ipc_env);
|
|
+ bl_hw->ipc_env = NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * This assumes LMAC is still (tx wise) and there's no TX race until LMAC is up
|
|
+ * tx wise.
|
|
+ * This also lets both IPC sides remain in sync before resetting the LMAC,
|
|
+ * e.g with bl_send_reset.
|
|
+ */
|
|
+void bl_ipc_tx_drain(struct bl_hw *bl_hw)
|
|
+{
|
|
+ int i, j;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if (!bl_hw->ipc_env) {
|
|
+ printk("%s: bypassing (restart must have failed)\n", __func__);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < BL_HWQ_NB; i++) {
|
|
+ for (j = 0; j < nx_txuser_cnt[i]; j++) {
|
|
+ struct sk_buff *skb;
|
|
+ while ((skb = (struct sk_buff *)ipc_host_tx_flush(bl_hw->ipc_env, i, j))) {
|
|
+ struct bl_sw_txhdr *sw_txhdr =
|
|
+ ((struct bl_txhdr *)skb->data)->sw_hdr;
|
|
+ skb_pull(skb, sw_txhdr->headroom);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void bl_error_ind(struct bl_hw *bl_hw)
|
|
+{
|
|
+ printk(KERN_CRIT "%s (type %d): dump received\n", __func__,
|
|
+ bl_hw->dbginfo.buf->dbg_info.error_type);
|
|
+ bl_hw->debugfs.trace_prst = true;
|
|
+}
|
|
+
|
|
diff -Naur /dev/null_utils.h b/drivers/net/wireless/hflps170/bl_utils.h
|
|
--- /dev/null_utils.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_utils.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,275 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_ipc_utils.h
|
|
+ *
|
|
+ * @brief IPC utility function declarations
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_IPC_UTILS_H_
|
|
+#define _BL_IPC_UTILS_H_
|
|
+
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/skbuff.h>
|
|
+
|
|
+#include "lmac_msg.h"
|
|
+#include "ipc_host.h"
|
|
+#include "bl_cmds.h"
|
|
+
|
|
+
|
|
+#ifdef CONFIG_BL_DBG
|
|
+#define BL_DBG printk
|
|
+//#define BL_DBG(f, args...) trace_printk(f, ##args)
|
|
+#else
|
|
+#define BL_DBG(a...) do {} while (0)
|
|
+#endif
|
|
+
|
|
+#define LOG_BUF_SIZE (32*PAGE_SIZE)
|
|
+#define LOG_NEWLINE_SIGN "\n"
|
|
+/* buffer size for every line's log */
|
|
+#define LOG_LINE_BUF_SIZE 256
|
|
+#define LOG_BUF_OUTPUT_BUF_SIZE (LOG_LINE_BUF_SIZE*4*4*32)
|
|
+
|
|
+// Prefix used for module filtering
|
|
+// If you modify any value, modify also DBG_MOD_ macros below
|
|
+#define D_INI "\x80" ///< Prefix for Init
|
|
+#define D_MSG "\x81" ///< Prefix for MSG
|
|
+#define D_TX "\x82" ///< Prefix for TX
|
|
+#define D_RX "\x83" ///< Prefix for RX
|
|
+#define D_CREDIT "\x84" ///< Prefix for CREDIT
|
|
+#define D_AGGR "\x85" ///< Prefix for AGGR
|
|
+#define D_XX0 "\x86" ///< Prefix unused
|
|
+#define D_XX1 "\x87" ///< Prefix unused
|
|
+#define D_XX2 "\x88" ///< Prefix unused
|
|
+#define D_XX3 "\x89" ///< Prefix unused
|
|
+#define D_XX4 "\x8A" ///< Prefix unused
|
|
+#define D_XX5 "\x8B" ///< Prefix unused
|
|
+#define D_XX6 "\x8C" ///< Prefix unused
|
|
+
|
|
+// Prefix used for severity filtering
|
|
+// If you modify any value, modify also DBG_SEV_ macros below
|
|
+#define D_CRT "\x9A" ///< Prefix for critical
|
|
+#define D_ERR "\x9B" ///< Prefix for error
|
|
+#define D_WRN "\x9C" ///< Prefix for warning
|
|
+#define D_INF "\x9D" ///< Prefix for info
|
|
+#define D_VRB "\x9E" ///< Prefix for verbose debug
|
|
+
|
|
+/// Module filtering macros, used only by debug module
|
|
+enum dbg_mod_tag
|
|
+{
|
|
+ DBG_MOD_IDX_INI = 0, ///< Bit index for init
|
|
+ DBG_MOD_IDX_MSG, ///< Bit index for msg
|
|
+ DBG_MOD_IDX_TX, ///< Bit index for tx
|
|
+ DBG_MOD_IDX_RX, ///< Bit index for rx
|
|
+ DBG_MOD_IDX_CREDIT, ///< Bit index for credit
|
|
+ DBG_MOD_IDX_AGGR, ///< Bit index for aggr
|
|
+ DBG_MOD_IDX_MAX, ///< Number of modules
|
|
+};
|
|
+
|
|
+#define DBG_MOD_MIN 0x80
|
|
+#define DBG_MOD_MAX (DBG_MOD_MIN + DBG_MOD_IDX_MAX)
|
|
+
|
|
+#define DBG_MOD_ALL 0xFFFFFFFF
|
|
+
|
|
+/// Severity filtering macros, used only by debug module
|
|
+enum dbg_sev_tag
|
|
+{
|
|
+ DBG_SEV_IDX_NONE = 0, ///< No print allowed
|
|
+ DBG_SEV_IDX_CRT, ///< Critical and unspecified allowed only
|
|
+ DBG_SEV_IDX_ERR, ///< Error allowed and above
|
|
+ DBG_SEV_IDX_WRN, ///< Warning allowed and above
|
|
+ DBG_SEV_IDX_INF, ///< Info allowed and above
|
|
+ DBG_SEV_IDX_VRB, ///< All allowed
|
|
+ DBG_SEV_IDX_MAX, ///< Number of severity levels
|
|
+ DBG_SEV_ALL ///< Convenient macro
|
|
+};
|
|
+
|
|
+#define DBG_SEV_MIN 0x9A
|
|
+#define DBG_SEV_MAX 0xA0
|
|
+
|
|
+#ifdef CONFIG_BL_MOD_LEV_DBG
|
|
+ void dbg_test_print(struct bl_hw *bl_hw, const char *func, const long line, const char *fmt, ...);
|
|
+ #define BL_DBG_MOD_LEVEL(bl_hw, fmt, ...) dbg_test_print(bl_hw, __func__, __LINE__, fmt, ## __VA_ARGS__)
|
|
+#else
|
|
+ #define BL_DBG_MOD_LEVEL(bl_hw, fmt, ...) do {} while (0)
|
|
+#endif
|
|
+
|
|
+#define BL_FN_ENTRY_STR ">>> %s()\n", __func__
|
|
+
|
|
+enum bl_dev_flag {
|
|
+ BL_DEV_RESTARTING,
|
|
+ BL_DEV_STACK_RESTARTING,
|
|
+ BL_DEV_STARTED,
|
|
+};
|
|
+
|
|
+struct bl_preq_ie_elem {
|
|
+ u8 *buf;
|
|
+ dma_addr_t dma_addr;
|
|
+ int bufsz;
|
|
+};
|
|
+
|
|
+struct bl_dma_elem {
|
|
+ u8 *buf;
|
|
+ dma_addr_t dma_addr;
|
|
+ int len;
|
|
+};
|
|
+
|
|
+struct bl_patternbuf {
|
|
+ u32 *buf;
|
|
+ dma_addr_t dma_addr;
|
|
+ int bufsz;
|
|
+};
|
|
+
|
|
+struct bl_labuf {
|
|
+ u8 *buf;
|
|
+ dma_addr_t dma_addr;
|
|
+ int bufsz;
|
|
+};
|
|
+
|
|
+struct bl_dbginfo {
|
|
+ struct mutex mutex;
|
|
+ struct dbg_debug_dump_tag *buf;
|
|
+ dma_addr_t dma_addr;
|
|
+ int bufsz;
|
|
+};
|
|
+
|
|
+#define BL_RXBUFF_PATTERN (0xCAFEFADE)
|
|
+
|
|
+/* Maximum number of rx buffer the fw may use at the same time */
|
|
+#define BL_RXBUFF_MAX (64 * 10)
|
|
+
|
|
+/**
|
|
+ * struct bl_skb_cb - Control Buffer structure for RX buffer
|
|
+ *
|
|
+ * @dma_addr: DMA address of the data buffer
|
|
+ * @pattern: Known pattern (used to check pointer on skb)
|
|
+ * @idx: Index in bl_hw.rxbuff_table to contians address of this buffer
|
|
+ */
|
|
+struct bl_skb_cb {
|
|
+ dma_addr_t dma_addr;
|
|
+ uint32_t pattern;
|
|
+ uint32_t idx;
|
|
+};
|
|
+
|
|
+#define BL_RXBUFF_DMA_ADDR_SET(skbuff, addr) \
|
|
+ ((struct bl_skb_cb *)(skbuff->cb))->dma_addr = addr
|
|
+#define BL_RXBUFF_DMA_ADDR_GET(skbuff) \
|
|
+ ((struct bl_skb_cb *)(skbuff->cb))->dma_addr
|
|
+
|
|
+#define BL_RXBUFF_PATTERN_SET(skbuff, pat) \
|
|
+ ((struct bl_skb_cb *)(skbuff->cb))->pattern = pat
|
|
+#define BL_RXBUFF_PATTERN_GET(skbuff) \
|
|
+ ((struct bl_skb_cb *)(skbuff->cb))->pattern
|
|
+
|
|
+#define BL_RXBUFF_IDX_SET(skbuff, val) \
|
|
+ ((struct bl_skb_cb *)(skbuff->cb))->idx = val
|
|
+#define BL_RXBUFF_IDX_GET(skbuff) \
|
|
+ ((struct bl_skb_cb *)(skbuff->cb))->idx
|
|
+
|
|
+#define BL_RXBUFF_VALID_IDX(idx) ((idx) < BL_RXBUFF_MAX)
|
|
+
|
|
+/* Used to ensure that hostid set to fw is never 0 */
|
|
+#define BL_RXBUFF_IDX_TO_HOSTID(idx) ((idx) + 1)
|
|
+#define BL_RXBUFF_HOSTID_TO_IDX(hostid) ((hostid) - 1)
|
|
+
|
|
+struct bl_e2arxdesc_elem {
|
|
+ struct rxdesc_tag *rxdesc_ptr;
|
|
+ dma_addr_t dma_addr;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Structure used to store information regarding E2A radar events in the driver
|
|
+ */
|
|
+struct bl_e2aradar_elem {
|
|
+ struct radar_pulse_array_desc *radarbuf_ptr;
|
|
+ dma_addr_t dma_addr;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Structure used to store information regarding E2A msg buffers in the driver
|
|
+ */
|
|
+struct bl_e2amsg_elem {
|
|
+ struct ipc_e2a_msg *msgbuf_ptr;
|
|
+ dma_addr_t dma_addr;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Structure used to store information regarding Debug msg buffers in the driver
|
|
+ */
|
|
+struct bl_dbg_elem {
|
|
+ struct ipc_dbg_msg *dbgbuf_ptr;
|
|
+ dma_addr_t dma_addr;
|
|
+};
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Initialize IPC interface.
|
|
+ *
|
|
+ * This function initializes IPC interface by registering callbacks, setting
|
|
+ * shared memory area and calling IPC Init function.
|
|
+ *
|
|
+ * This function should be called only once during driver's lifetime.
|
|
+ *
|
|
+ * @param[in] bl_hw Pointer to main structure storing all the
|
|
+ * relevant information
|
|
+ * @param[in] ipc_shared_mem
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+int bl_ipc_init(struct bl_hw *bl_hw);
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Release IPC interface.
|
|
+ *
|
|
+ * @param[in] bl_hw Pointer to main structure storing all the relevant
|
|
+ * information
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void bl_ipc_deinit(struct bl_hw *bl_hw);
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Flush IPC .
|
|
+ *
|
|
+ * @param[in] bl_hw Pointer to main structure storing all the relevant
|
|
+ * information
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void bl_ipc_tx_drain(struct bl_hw *bl_hw);
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Function called upon DBG_ERROR_IND message reception.
|
|
+ * This function triggers the UMH script call that will indicate to the user
|
|
+ * space the error that occurred and stored the debug dump. Once the UMH script
|
|
+ * is executed, the bl_umh_done function has to be called.
|
|
+ *
|
|
+ * @param[in] bl_hw Pointer to main structure storing all the relevant
|
|
+ * information
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void bl_error_ind(struct bl_hw *bl_hw);
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Function called once UMH script execution is finished.
|
|
+ *
|
|
+ * @param[in] bl_hw Pointer to main structure storing all the relevant
|
|
+ * information
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void bl_umh_done(struct bl_hw *bl_hw);
|
|
+
|
|
+struct bl_sta *bl_get_sta(struct bl_hw *bl_hw, const u8 *mac_addr);
|
|
+
|
|
+
|
|
+#endif /* _BL_IPC_UTILS_H_ */
|
|
diff -Naur /dev/null_v7.c b/drivers/net/wireless/hflps170/bl_v7.c
|
|
--- /dev/null_v7.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_v7.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,139 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_v7.c - Support for v7 platform
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#include "bl_v7.h"
|
|
+#include "bl_defs.h"
|
|
+#include "bl_irqs.h"
|
|
+#include "bl_sdio.h"
|
|
+
|
|
+
|
|
+void bl_device_deinit(struct bl_plat *bl_plat)
|
|
+{
|
|
+
|
|
+ sdio_claim_host(bl_plat->func);
|
|
+ sdio_release_irq(bl_plat->func);
|
|
+ sdio_disable_func(bl_plat->func);
|
|
+ sdio_release_host(bl_plat->func);
|
|
+
|
|
+ kfree(bl_plat->mp_regs);
|
|
+ kfree(bl_plat);
|
|
+}
|
|
+
|
|
+int bl_device_init(struct sdio_func *func, struct bl_plat **bl_plat)
|
|
+{
|
|
+ struct bl_device *device;
|
|
+ u8 sdio_ireg;
|
|
+ int ret = 0;
|
|
+
|
|
+ *bl_plat = kzalloc(sizeof(struct bl_plat) + sizeof(struct bl_device), GFP_KERNEL);
|
|
+ if (!*bl_plat)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ device = (struct bl_device *)(*bl_plat)->priv;
|
|
+
|
|
+ device->firmware = SD606_DEFAULT_FW_NAME;
|
|
+ device->reg = &bl_reg_sd606;
|
|
+ device->max_ports = 16;
|
|
+ device->mp_agg_pkt_limit = 8;
|
|
+ device->supports_sdio_new_mode = false;
|
|
+ device->has_control_mask = true;
|
|
+ device->supports_fw_dump = false;
|
|
+ device->mp_tx_agg_buf_size = BL_MP_AGGR_BUF_SIZE_16K;
|
|
+ device->mp_rx_agg_buf_size = BL_MP_AGGR_BUF_SIZE_16K;
|
|
+ device->auto_tdls = false;
|
|
+
|
|
+ func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
|
|
+
|
|
+ sdio_claim_host(func);
|
|
+
|
|
+ /*1. enable func*/
|
|
+ ret = sdio_enable_func(func);
|
|
+
|
|
+ /*
|
|
+ * 2. Read the host_int_status_reg for ACK the first interrupt got
|
|
+ * from the bootloader. If we don't do this we get a interrupt
|
|
+ * as soon as we register the irq.
|
|
+ */
|
|
+ sdio_readb(func, device->reg->host_int_status_reg, &ret);
|
|
+
|
|
+ /* 3. Get SDIO ioport */
|
|
+ (*bl_plat)->io_port = (sdio_readb(func, device->reg->io_port_0_reg, &ret) & 0xff);
|
|
+ (*bl_plat)->io_port |= ((sdio_readb(func, device->reg->io_port_1_reg, &ret) & 0xff) << 8);
|
|
+ (*bl_plat)->io_port |= ((sdio_readb(func, device->reg->io_port_2_reg, &ret) & 0xff) << 16);
|
|
+ BL_DBG("info: SDIO FUNC1 IO port: %#x\n", (*bl_plat)->io_port);
|
|
+
|
|
+ /* Set Host interrupt reset to read to clear */
|
|
+ sdio_ireg = sdio_readb(func, device->reg->host_int_rsr_reg, &ret);
|
|
+ if(!ret)
|
|
+ sdio_writeb(func, sdio_ireg | device->reg->sdio_int_mask, device->reg->host_int_rsr_reg, &ret);
|
|
+
|
|
+ /* Dnld/Upld ready set to auto reset */
|
|
+ sdio_ireg = sdio_readb(func, device->reg->card_misc_cfg_reg, &ret);
|
|
+ if(!ret)
|
|
+ sdio_writeb(func, sdio_ireg | AUTO_RE_ENABLE_INT, device->reg->card_misc_cfg_reg, &ret);
|
|
+
|
|
+
|
|
+ /* 4. set block size*/
|
|
+ sdio_set_block_size(func, BL_SDIO_BLOCK_SIZE);
|
|
+
|
|
+ /* 4.1 Download Wi-Fi firmware*/
|
|
+ (*bl_plat)->func = func;
|
|
+ ret = bl_sdio_init(*bl_plat);
|
|
+ if (ret) {
|
|
+ printk("bl_sdio_init failed: ret=%d\n", ret);
|
|
+ sdio_release_host(func);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* 5. request irq */
|
|
+ ret = sdio_claim_irq(func, bl_irq_hdlr);
|
|
+ if (ret) {
|
|
+ printk("sdio_claim_irq failed: ret=%d\n", ret);
|
|
+ sdio_release_host(func);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Simply write the mask to the register */
|
|
+ sdio_writeb(func, UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, device->reg->host_int_mask_reg, &ret);
|
|
+ if (ret) {
|
|
+ printk("enable host interrupt failed\n");
|
|
+ sdio_release_irq(func);
|
|
+ sdio_release_host(func);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ sdio_ireg = sdio_readb(func, device->reg->host_int_mask_reg, &ret);
|
|
+ BL_DBG("irq_enable=0x%x\n", sdio_ireg);
|
|
+
|
|
+ sdio_release_host(func);
|
|
+
|
|
+ /* 6. Initialize SDIO variables in card */
|
|
+ (*bl_plat)->mp_rd_bitmap = 0;
|
|
+ (*bl_plat)->mp_wr_bitmap = 0;
|
|
+ (*bl_plat)->curr_rd_port = device->reg->start_rd_port;
|
|
+ (*bl_plat)->curr_wr_port = device->reg->start_wr_port;
|
|
+
|
|
+ (*bl_plat)->mp_regs = kzalloc(device->reg->max_mp_regs, GFP_KERNEL);
|
|
+ if (!(*bl_plat)->mp_regs) {
|
|
+ printk("kzalloc mp_regs failed!\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ if(ret) {
|
|
+ printk("bl_device_init failed!\n");
|
|
+ sdio_claim_host(func);
|
|
+ sdio_release_irq(func);
|
|
+ ret = sdio_disable_func(func);
|
|
+ sdio_release_host(func);
|
|
+ kfree((*bl_plat)->mp_regs);
|
|
+ kfree(*bl_plat);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
diff -Naur /dev/null_v7.h b/drivers/net/wireless/hflps170/bl_v7.h
|
|
--- /dev/null_v7.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_v7.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,34 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_v7.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_V7_H_
|
|
+#define _BL_V7_H_
|
|
+
|
|
+#include "bl_platform.h"
|
|
+
|
|
+struct bl_device {
|
|
+ const char *firmware;
|
|
+ const struct bl_sdio_card_reg *reg;
|
|
+ u8 *mp_regs;
|
|
+ u8 max_ports;
|
|
+ u8 mp_agg_pkt_limit;
|
|
+ bool supports_sdio_new_mode;
|
|
+ bool has_control_mask;
|
|
+ bool supports_fw_dump;
|
|
+ u16 tx_buf_size;
|
|
+ u32 mp_tx_agg_buf_size;
|
|
+ u32 mp_rx_agg_buf_size;
|
|
+ u8 auto_tdls;
|
|
+};
|
|
+
|
|
+int bl_device_init(struct sdio_func *func, struct bl_plat **bl_plat);
|
|
+void bl_device_deinit(struct bl_plat *bl_plat);
|
|
+
|
|
+#endif /* _BL_V7_H_ */
|
|
diff -Naur /dev/null_version.h b/drivers/net/wireless/hflps170/bl_version.h
|
|
--- /dev/null_version.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/bl_version.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,11 @@
|
|
+#ifndef _BL_VERSION_H_
|
|
+#define _BL_VERSION_H_
|
|
+
|
|
+#include "bl_version_gen.h"
|
|
+
|
|
+static inline void bl_print_version(void)
|
|
+{
|
|
+ printk(BL_VERS_BANNER"\n");
|
|
+}
|
|
+
|
|
+#endif /* _BL_VERSION_H_ */
|
|
diff -Naur /dev/null/Makefile b/drivers/net/wireless/hflps170/fullmac/Makefile
|
|
--- /dev/null/Makefile 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/fullmac/Makefile 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,61 @@
|
|
+# Enable A-MSDU support (need FW support)
|
|
+## Select this if FW is compiled with AMSDU support
|
|
+CONFIG_BL_SPLIT_TX_BUF ?= n
|
|
+## Select this TO send AMSDU
|
|
+CONFIG_BL_AMSDUS_TX ?= n
|
|
+
|
|
+# Enable HW queue for Broadcast/Multicast traffic (need FW support)
|
|
+CONFIG_BL_BCMC ?= y
|
|
+
|
|
+# extra DEBUG config
|
|
+CONFIG_BL_SW_PROFILING ?= n
|
|
+CONFIG_BL_DBG ?= n
|
|
+CONFIG_AUTO_DNLD ?= y
|
|
+
|
|
+obj-m := bl_fdrv.o
|
|
+bl_fdrv-y := bl_cfgfile.o \
|
|
+ bl_main.o \
|
|
+ bl_mod_params.o \
|
|
+ bl_platform.o \
|
|
+ bl_sdio.o \
|
|
+ bl_msg_tx.o \
|
|
+ bl_msg_rx.o \
|
|
+ bl_utils.o \
|
|
+ bl_cmds.o \
|
|
+ bl_irqs.o \
|
|
+ ipc_host.o \
|
|
+ bl_txq.o \
|
|
+ bl_strs.o \
|
|
+ bl_tx.o \
|
|
+ bl_rx.o \
|
|
+ bl_v7.o \
|
|
+ bl_bootrom.o
|
|
+bl_fdrv-$(CONFIG_DEBUG_FS) += bl_debugfs.o
|
|
+
|
|
+ccflags-y := -DCONFIG_BL_FULLMAC
|
|
+ccflags-y += -I$(src)/..
|
|
+ccflags-$(CONFIG_BL_SPLIT_TX_BUF) += -DCONFIG_BL_SPLIT_TX_BUF
|
|
+ifeq ($(CONFIG_BL_SPLIT_TX_BUF), y)
|
|
+ccflags-$(CONFIG_BL_AMSDUS_TX) += -DCONFIG_BL_AMSDUS_TX
|
|
+endif
|
|
+ccflags-$(CONFIG_BL_DBG) += -DCONFIG_BL_DBG
|
|
+ccflags-$(CONFIG_BL_MOD_LEV_DBG) += -DCONFIG_BL_MOD_LEV_DBG
|
|
+
|
|
+ccflags-y += -DCONFIG_USER_MAX=1
|
|
+
|
|
+ifeq ($(CONFIG_BL_BCMC), y)
|
|
+ccflags-y += -DNX_TXQ_CNT=5
|
|
+else
|
|
+ccflags-y += -DNX_TXQ_CNT=4
|
|
+endif
|
|
+
|
|
+
|
|
+quiet_cmd_genvers = GENVERSION $@
|
|
+ cmd_genvers = ($(if $(KBUILD_EXTMOD),,$(srctree)/)$(src)/../mkvers.sh $@)
|
|
+
|
|
+$(obj)/bl_main.o: $(obj)/bl_version_gen.h
|
|
+
|
|
+$(obj)/bl_version_gen.h: FORCE
|
|
+ $(call cmd,genvers)
|
|
+
|
|
+clean-files := bl_version_gen.h
|
|
diff -Naur /dev/null/bl_defs.h b/drivers/net/wireless/hflps170/fullmac/bl_defs.h
|
|
--- /dev/null/bl_defs.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/fullmac/bl_defs.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,464 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_defs.h
|
|
+ *
|
|
+ * @brief Main driver structure declarations for fullmac driver
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_DEFS_H_
|
|
+#define _BL_DEFS_H_
|
|
+
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/dmapool.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <net/cfg80211.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#include "bl_mod_params.h"
|
|
+#include "bl_debugfs.h"
|
|
+#include "bl_tx.h"
|
|
+#include "bl_rx.h"
|
|
+#include "bl_utils.h"
|
|
+#include "bl_platform.h"
|
|
+
|
|
+#define WPI_HDR_LEN 18
|
|
+#define WPI_PN_LEN 16
|
|
+#define WPI_PN_OFST 2
|
|
+#define WPI_MIC_LEN 16
|
|
+#define WPI_KEY_LEN 32
|
|
+#define WPI_SUBKEY_LEN 16 // WPI key is actually two 16bytes key
|
|
+
|
|
+#define LEGACY_PS_ID 0
|
|
+#define UAPSD_ID 1
|
|
+
|
|
+
|
|
+/**
|
|
+ * struct bl_bcn - Information of the beacon in used (AP mode)
|
|
+ *
|
|
+ * @head: head portion of beacon (before TIM IE)
|
|
+ * @tail: tail portion of beacon (after TIM IE)
|
|
+ * @ies: extra IEs (not used ?)
|
|
+ * @head_len: length of head data
|
|
+ * @tail_len: length of tail data
|
|
+ * @ies_len: length of extra IEs data
|
|
+ * @tim_len: length of TIM IE
|
|
+ * @len: Total beacon len (head + tim + tail + extra)
|
|
+ * @dtim: dtim period
|
|
+ */
|
|
+struct bl_bcn {
|
|
+ u8 *head;
|
|
+ u8 *tail;
|
|
+ u8 *ies;
|
|
+ size_t head_len;
|
|
+ size_t tail_len;
|
|
+ size_t ies_len;
|
|
+ size_t tim_len;
|
|
+ size_t len;
|
|
+ u8 dtim;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct bl_key - Key information
|
|
+ *
|
|
+ * @hw_idx: Idx of the key from hardware point of view
|
|
+ */
|
|
+struct bl_key {
|
|
+ u8 hw_idx;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct bl_csa - Information for CSA (Channel Switch Announcement)
|
|
+ *
|
|
+ * @vif: Pointer to the vif doing the CSA
|
|
+ * @bcn: Beacon to use after CSA
|
|
+ * @dma: DMA descriptor to send the new beacon to the fw
|
|
+ * @chandef: defines the channel to use after the switch
|
|
+ * @count: Current csa counter
|
|
+ * @status: Status of the CSA at fw level
|
|
+ * @ch_idx: Index of the new channel context
|
|
+ * @work: work scheduled at the end of CSA
|
|
+ */
|
|
+struct bl_csa {
|
|
+ struct bl_vif *vif;
|
|
+ struct bl_bcn bcn;
|
|
+ struct bl_dma_elem dma;
|
|
+ struct cfg80211_chan_def chandef;
|
|
+ int count;
|
|
+ int status;
|
|
+ int ch_idx;
|
|
+ struct work_struct work;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * enum bl_ap_flags - AP flags
|
|
+ *
|
|
+ * @BL_AP_ISOLATE Isolate clients (i.e. Don't brige packets transmitted by
|
|
+ * one client for another one)
|
|
+ */
|
|
+enum bl_ap_flags {
|
|
+ BL_AP_ISOLATE = BIT(0),
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Structure used to save information relative to the managed interfaces.
|
|
+ * This is also linked within the bl_hw vifs list.
|
|
+ *
|
|
+ */
|
|
+struct bl_vif {
|
|
+ struct list_head list;
|
|
+ struct bl_hw *bl_hw;
|
|
+ struct wireless_dev wdev;
|
|
+ struct net_device *ndev;
|
|
+ struct net_device_stats net_stats;
|
|
+ struct bl_key key[6];
|
|
+ u8 drv_vif_index; /* Identifier of the VIF in driver */
|
|
+ u8 vif_index; /* Identifier of the station in FW */
|
|
+ u8 ch_index; /* Channel context identifier */
|
|
+ bool up; /* Indicate if associated netdev is up
|
|
+ (i.e. Interface is created at fw level) */
|
|
+ bool use_4addr; /* Should we use 4addresses mode */
|
|
+ bool is_resending; /* Indicate if a frame is being resent on this interface */
|
|
+ bool user_mpm; /* In case of Mesh Point VIF, indicate if MPM is handled by userspace */
|
|
+ bool roc_tdls; /* Indicate if the ROC has been called by a
|
|
+ TDLS station */
|
|
+ u8 tdls_status; /* Status of the TDLS link */
|
|
+ union
|
|
+ {
|
|
+ struct
|
|
+ {
|
|
+ struct bl_sta *ap; /* Pointer to the peer STA entry allocated for
|
|
+ the AP */
|
|
+ struct bl_sta *tdls_sta; /* Pointer to the TDLS station */
|
|
+ } sta;
|
|
+ struct
|
|
+ {
|
|
+ u16 flags; /* see bl_ap_flags */
|
|
+ struct list_head sta_list; /* List of STA connected to the AP */
|
|
+ struct bl_bcn bcn; /* beacon */
|
|
+ u8 bcmc_index; /* Index of the BCMC sta to use */
|
|
+ struct bl_csa *csa;
|
|
+
|
|
+ struct list_head mpath_list; /* List of Mesh Paths used on this interface */
|
|
+ struct list_head proxy_list; /* List of Proxies Information used on this interface */
|
|
+ bool create_path; /* Indicate if we are waiting for a MESH_CREATE_PATH_CFM
|
|
+ message */
|
|
+ int generation; /* Increased each time the list of Mesh Paths is updated */
|
|
+ } ap;
|
|
+ struct
|
|
+ {
|
|
+ struct bl_vif *master; /* pointer on master interface */
|
|
+ struct bl_sta *sta_4a;
|
|
+ } ap_vlan;
|
|
+ };
|
|
+};
|
|
+
|
|
+#define BL_VIF_TYPE(bl_vif) (bl_vif->wdev.iftype)
|
|
+
|
|
+/**
|
|
+ * Structure used to store information relative to PS mode.
|
|
+ *
|
|
+ * @active: True when the sta is in PS mode.
|
|
+ * If false, other values should be ignored
|
|
+ * @pkt_ready: Number of packets buffered for the sta in drv's txq
|
|
+ * (1 counter for Legacy PS and 1 for U-APSD)
|
|
+ * @sp_cnt: Number of packets that remain to be pushed in the service period.
|
|
+ * 0 means that no service period is in progress
|
|
+ * (1 counter for Legacy PS and 1 for U-APSD)
|
|
+ */
|
|
+struct bl_sta_ps {
|
|
+ bool active;
|
|
+ u16 pkt_ready[2];
|
|
+ u16 sp_cnt[2];
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct bl_rx_rate_stats - Store statistics for RX rates
|
|
+ *
|
|
+ * @table: Table indicating how many frame has been receive which each
|
|
+ * rate index. Rate index is the same as the one used by RC algo for TX
|
|
+ * @size: Size of the table array
|
|
+ * @cpt: number of frames received
|
|
+ */
|
|
+struct bl_rx_rate_stats {
|
|
+ int *table;
|
|
+ int size;
|
|
+ int cpt;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct bl_sta_stats - Structure Used to store statistics specific to a STA
|
|
+ *
|
|
+ * @last_rx: Hardware vector of the last received frame
|
|
+ * @rx_rate: Statistics of the received rates
|
|
+ */
|
|
+struct bl_sta_stats {
|
|
+#ifdef CONFIG_BL_DEBUGFS
|
|
+ struct hw_vect last_rx;
|
|
+ struct bl_rx_rate_stats rx_rate;
|
|
+#endif
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Structure used to save information relative to the managed stations.
|
|
+ */
|
|
+struct bl_sta {
|
|
+ struct list_head list;
|
|
+ u16 aid; /* association ID */
|
|
+ u8 sta_idx; /* Identifier of the station */
|
|
+ u8 vif_idx; /* Identifier of the VIF (fw id) the station
|
|
+ belongs to */
|
|
+ u8 vlan_idx; /* Identifier of the VLAN VIF (fw id) the station
|
|
+ belongs to (= vif_idx if no vlan in used) */
|
|
+ enum nl80211_band band; /* Band */
|
|
+ enum nl80211_chan_width width; /* Channel width */
|
|
+ u16 center_freq; /* Center frequency */
|
|
+ u32 center_freq1; /* Center frequency 1 */
|
|
+ u32 center_freq2; /* Center frequency 2 */
|
|
+ u8 ch_idx; /* Identifier of the channel
|
|
+ context the station belongs to */
|
|
+ bool qos; /* Flag indicating if the station
|
|
+ supports QoS */
|
|
+ u8 acm; /* Bitfield indicating which queues
|
|
+ have AC mandatory */
|
|
+ u16 uapsd_tids; /* Bitfield indicating which tids are subject to
|
|
+ UAPSD */
|
|
+ u8 mac_addr[ETH_ALEN]; /* MAC address of the station */
|
|
+ struct bl_key key;
|
|
+ bool valid; /* Flag indicating if the entry is valid */
|
|
+ struct bl_sta_ps ps; /* Information when STA is in PS (AP only) */
|
|
+ bool ht; /* Flag indicating if the station
|
|
+ supports HT */
|
|
+ bool vht; /* Flag indicating if the station
|
|
+ supports VHT */
|
|
+ u32 ac_param[AC_MAX]; /* EDCA parameters */
|
|
+ struct bl_sta_stats stats;
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+struct bl_amsdu_stats {
|
|
+ int done;
|
|
+ int failed;
|
|
+};
|
|
+#endif
|
|
+
|
|
+struct bl_stats {
|
|
+ int cfm_balance[NX_TXQ_CNT];
|
|
+ unsigned long last_rx, last_tx; /* jiffies */
|
|
+ int ampdus_tx[IEEE80211_MAX_AMPDU_BUF];
|
|
+ int ampdus_rx[IEEE80211_MAX_AMPDU_BUF];
|
|
+ int ampdus_rx_map[4];
|
|
+ int ampdus_rx_miss;
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ struct bl_amsdu_stats amsdus[NX_TX_PAYLOAD_MAX];
|
|
+#endif
|
|
+ int amsdus_rx[64];
|
|
+};
|
|
+
|
|
+struct bl_sec_phy_chan {
|
|
+ u16 prim20_freq;
|
|
+ u16 center_freq1;
|
|
+ u16 center_freq2;
|
|
+ enum nl80211_band band;
|
|
+ u8 type;
|
|
+};
|
|
+
|
|
+/* Structure that will contains all RoC information received from cfg80211 */
|
|
+struct bl_roc_elem {
|
|
+ struct wireless_dev *wdev;
|
|
+ struct ieee80211_channel *chan;
|
|
+ unsigned int duration;
|
|
+ /* Used to avoid call of CFG80211 callback upon expiration of RoC */
|
|
+ bool mgmt_roc;
|
|
+ /* Indicate if we have switch on the RoC channel */
|
|
+ bool on_chan;
|
|
+};
|
|
+
|
|
+/* Structure containing channel survey information received from MAC */
|
|
+struct bl_survey_info {
|
|
+ // Filled
|
|
+ u32 filled;
|
|
+ // Amount of time in ms the radio spent on the channel
|
|
+ u32 chan_time_ms;
|
|
+ // Amount of time the primary channel was sensed busy
|
|
+ u32 chan_time_busy_ms;
|
|
+ // Noise in dbm
|
|
+ s8 noise_dbm;
|
|
+};
|
|
+
|
|
+struct bl_agg_reord_pkt {
|
|
+ struct list_head list;
|
|
+ struct sk_buff *skb;
|
|
+ u16 sn;
|
|
+};
|
|
+
|
|
+#define BL_CH_NOT_SET 0xFF
|
|
+
|
|
+/* Structure containing channel context information */
|
|
+struct bl_chanctx {
|
|
+ struct cfg80211_chan_def chan_def; /* channel description */
|
|
+ u8 count; /* number of vif using this ctxt */
|
|
+};
|
|
+
|
|
+struct bl_dbg_credit {
|
|
+ int sdio_port;
|
|
+ int txq_credit;
|
|
+ int hwq_credit;
|
|
+ int credit;
|
|
+ int nb_ready;
|
|
+ int txq_idx;
|
|
+};
|
|
+
|
|
+struct bl_dbg_time {
|
|
+ s64 sdio_tx;
|
|
+};
|
|
+
|
|
+/** 2K buf size */
|
|
+#define BL_TX_DATA_BUF_SIZE_16K 16*1024
|
|
+#define BL_RX_DATA_BUF_SIZE_16K 16*1024
|
|
+
|
|
+#define BL_SDIO_MP_AGGR_PKT_LIMIT_MAX 8
|
|
+#define BL_SDIO_MPA_ADDR_BASE 0x1000
|
|
+
|
|
+typedef struct _sdio_mpa_tx {
|
|
+ u8 *buf;
|
|
+ u32 buf_len;
|
|
+ u32 pkt_cnt;
|
|
+ u32 ports;
|
|
+ u16 start_port;
|
|
+ u16 mp_wr_info[BL_SDIO_MP_AGGR_PKT_LIMIT_MAX];
|
|
+}sdio_mpa_tx;
|
|
+
|
|
+typedef struct _sdio_mpa_rx {
|
|
+ u8 *buf;
|
|
+ u32 buf_len;
|
|
+ u32 pkt_cnt;
|
|
+ u32 ports;
|
|
+ u16 start_port;
|
|
+ struct sk_buff *buf_arr[BL_SDIO_MP_AGGR_PKT_LIMIT_MAX];
|
|
+ u32 len_arr[BL_SDIO_MP_AGGR_PKT_LIMIT_MAX];
|
|
+ u16 mp_rd_info[BL_SDIO_MP_AGGR_PKT_LIMIT_MAX];
|
|
+ u16 mp_rd_port[BL_SDIO_MP_AGGR_PKT_LIMIT_MAX];
|
|
+}sdio_mpa_rx;
|
|
+
|
|
+struct bl_hw {
|
|
+ struct bl_mod_params *mod_params;
|
|
+ void *log_buff;
|
|
+ int buf_write_size;
|
|
+ bool use_phy_bw_tweaks;
|
|
+ struct device *dev;
|
|
+ struct wiphy *wiphy;
|
|
+ struct list_head vifs;
|
|
+ struct bl_vif *vif_table[NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX]; /* indexed with fw id */
|
|
+ struct bl_sta sta_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX];
|
|
+ struct bl_survey_info survey[SCAN_CHANNEL_MAX];
|
|
+ struct cfg80211_scan_request *scan_request;
|
|
+ struct bl_chanctx chanctx_table[NX_CHAN_CTXT_CNT];
|
|
+ u8 cur_chanctx;
|
|
+
|
|
+
|
|
+ /* RoC Management */
|
|
+ struct bl_roc_elem *roc_elem; /* Information provided by cfg80211 in its remain on channel request */
|
|
+ u32 roc_cookie_cnt; /* Counter used to identify RoC request sent by cfg80211 */
|
|
+
|
|
+ struct bl_cmd_mgr cmd_mgr;
|
|
+
|
|
+ unsigned long drv_flags;
|
|
+ struct bl_plat *plat;
|
|
+ struct ipc_host_env_tag *ipc_env; /* store the IPC environment */
|
|
+
|
|
+ spinlock_t tx_lock;
|
|
+ spinlock_t main_proc_lock;
|
|
+ spinlock_t int_lock;
|
|
+ spinlock_t cmd_lock;
|
|
+ spinlock_t resend_lock;
|
|
+ spinlock_t txq_lock;
|
|
+ spinlock_t rd_int_lock;
|
|
+ struct mutex mutex; /* per-device perimeter lock */
|
|
+ struct semaphore sem;
|
|
+ u8 bl_processing;
|
|
+ u8 more_task_flag;
|
|
+ u8 lost_int_flag;
|
|
+
|
|
+ struct tasklet_struct task;
|
|
+ struct work_struct main_work;
|
|
+ struct workqueue_struct *workqueue;
|
|
+ struct mm_version_cfm version_cfm; /* Lower layers versions - obtained via MM_VERSION_REQ */
|
|
+ u32 int_status;
|
|
+ u32 cmd_sent;
|
|
+ u32 resend;
|
|
+
|
|
+ int rxbuff_idx;
|
|
+ struct sk_buff *rxbuff_table[BL_RXBUFF_MAX];
|
|
+ struct bl_e2amsg_elem *msg_elems; /* pointer to the E2A msg useful addresses */
|
|
+ struct bl_dbg_elem *dbg_elems; /* pointer to the Debug msg useful addresses */
|
|
+ struct bl_e2aradar_elem *radar_elems; /* pointer to the E2A radar useful addresses */
|
|
+ struct bl_e2arxdesc_elem *rxdesc_elems; /* pointer to the E2A RX Desc useful addresses */
|
|
+ struct kmem_cache *sw_txhdr_cache;
|
|
+ struct kmem_cache *agg_reodr_pkt_cache;
|
|
+ sdio_mpa_tx mpa_tx_data;
|
|
+ sdio_mpa_rx mpa_rx_data;
|
|
+
|
|
+ struct bl_dbginfo dbginfo; /* Debug information from FW */
|
|
+
|
|
+ struct bl_debugfs debugfs;
|
|
+ struct bl_stats stats;
|
|
+
|
|
+ struct bl_txq txq[NX_NB_TXQ];
|
|
+ struct bl_hwq hwq[NX_TXQ_CNT];
|
|
+ struct bl_preq_ie_elem preq_ie;
|
|
+ struct bl_sec_phy_chan sec_phy_chan;
|
|
+ u8 phy_cnt;
|
|
+ u8 chan_ctxt_req;
|
|
+ u8 avail_idx_map;
|
|
+ u8 vif_started;
|
|
+ bool adding_sta;
|
|
+ struct phy_cfg_tag phy_config;
|
|
+
|
|
+ /* extended capabilities supported */
|
|
+ u8 ext_capa[8];
|
|
+
|
|
+ u8 dbg_dump_start;
|
|
+ u32 la_buf_idx;
|
|
+
|
|
+ u8 recovery_flag; //temp add for fw recovery flag
|
|
+
|
|
+ u16 msg_idx;
|
|
+
|
|
+ struct bl_dbg_credit dbg_credit[50];
|
|
+ int dbg_credit_idx;
|
|
+
|
|
+ struct bl_dbg_time dbg_time[50];
|
|
+ int dbg_time_idx;
|
|
+
|
|
+ struct list_head reorder_list[NX_REMOTE_STA_MAX*NX_NB_TID_PER_STA];
|
|
+};
|
|
+
|
|
+u8 *bl_build_bcn(struct bl_bcn *bcn, struct cfg80211_beacon_data *new);
|
|
+
|
|
+void bl_chanctx_link(struct bl_vif *vif, u8 idx,
|
|
+ struct cfg80211_chan_def *chandef);
|
|
+void bl_chanctx_unlink(struct bl_vif *vif);
|
|
+int bl_chanctx_valid(struct bl_hw *bl_hw, u8 idx);
|
|
+
|
|
+static inline bool is_multicast_sta(int sta_idx)
|
|
+{
|
|
+ return (sta_idx >= NX_REMOTE_STA_MAX);
|
|
+}
|
|
+
|
|
+static inline uint8_t master_vif_idx(struct bl_vif *vif)
|
|
+{
|
|
+ if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN)) {
|
|
+ return vif->ap_vlan.master->vif_index;
|
|
+ } else {
|
|
+ return vif->vif_index;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#endif /* _BL_DEFS_H_ */
|
|
diff -Naur /dev/null/bl_main.c b/drivers/net/wireless/hflps170/fullmac/bl_main.c
|
|
--- /dev/null/bl_main.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/fullmac/bl_main.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,2706 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_main.c
|
|
+ *
|
|
+ * @brief Entry point of the BL driver
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/inetdevice.h>
|
|
+#include <net/cfg80211.h>
|
|
+#include <net/mac80211.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/miscdevice.h>
|
|
+
|
|
+#include "bl_compat.h"
|
|
+#include "bl_defs.h"
|
|
+#include "bl_msg_tx.h"
|
|
+#include "bl_tx.h"
|
|
+#include "hal_desc.h"
|
|
+#include "bl_debugfs.h"
|
|
+#include "bl_cfgfile.h"
|
|
+#include "bl_irqs.h"
|
|
+#include "bl_version.h"
|
|
+#include "bl_events.h"
|
|
+#include "bl_sdio.h"
|
|
+
|
|
+#define RW_DRV_DESCRIPTION "BouffaloLab 11nac driver for Linux cfg80211"
|
|
+#define RW_DRV_COPYRIGHT "Copyright(c) 2015-2016 BouffaloLab"
|
|
+#define RW_DRV_AUTHOR "BouffaloLab S.A.S"
|
|
+
|
|
+#define BL_PRINT_CFM_ERR(req) \
|
|
+ printk(KERN_CRIT "%s: Status Error(%d)\n", #req, (&req##_cfm)->status)
|
|
+
|
|
+#define BL_HT_CAPABILITIES \
|
|
+{ \
|
|
+ .ht_supported = true, \
|
|
+ .cap = 0, \
|
|
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
|
|
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, \
|
|
+ .mcs = { \
|
|
+ .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
|
|
+ .rx_highest = cpu_to_le16(65), \
|
|
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
|
|
+ }, \
|
|
+}
|
|
+
|
|
+#define BL_VHT_CAPABILITIES \
|
|
+{ \
|
|
+ .vht_supported = false, \
|
|
+ .cap = \
|
|
+ (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT),\
|
|
+ .vht_mcs = { \
|
|
+ .rx_mcs_map = cpu_to_le16( \
|
|
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
|
|
+ .tx_mcs_map = cpu_to_le16( \
|
|
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
|
|
+ } \
|
|
+}
|
|
+
|
|
+#define RATE(_bitrate, _hw_rate, _flags) { \
|
|
+ .bitrate = (_bitrate), \
|
|
+ .flags = (_flags), \
|
|
+ .hw_value = (_hw_rate), \
|
|
+}
|
|
+
|
|
+#define CHAN(_freq) { \
|
|
+ .center_freq = (_freq), \
|
|
+ .max_power = 30, /* FIXME */ \
|
|
+}
|
|
+
|
|
+void *kbuff = NULL;
|
|
+extern struct device_attribute dev_attr_filter_severity;
|
|
+extern struct device_attribute dev_attr_filter_module;
|
|
+
|
|
+static struct ieee80211_rate bl_ratetable[] = {
|
|
+ RATE(10, 0x00, 0),
|
|
+ RATE(20, 0x01, IEEE80211_RATE_SHORT_PREAMBLE),
|
|
+ RATE(55, 0x02, IEEE80211_RATE_SHORT_PREAMBLE),
|
|
+ RATE(110, 0x03, IEEE80211_RATE_SHORT_PREAMBLE),
|
|
+ RATE(60, 0x04, 0),
|
|
+ RATE(90, 0x05, 0),
|
|
+ RATE(120, 0x06, 0),
|
|
+ RATE(180, 0x07, 0),
|
|
+ RATE(240, 0x08, 0),
|
|
+ RATE(360, 0x09, 0),
|
|
+ RATE(480, 0x0A, 0),
|
|
+ RATE(540, 0x0B, 0),
|
|
+};
|
|
+
|
|
+/* The channels indexes here are not used anymore */
|
|
+static struct ieee80211_channel bl_2ghz_channels[] = {
|
|
+ CHAN(2412),
|
|
+ CHAN(2417),
|
|
+ CHAN(2422),
|
|
+ CHAN(2427),
|
|
+ CHAN(2432),
|
|
+ CHAN(2437),
|
|
+ CHAN(2442),
|
|
+ CHAN(2447),
|
|
+ CHAN(2452),
|
|
+ CHAN(2457),
|
|
+ CHAN(2462),
|
|
+ CHAN(2467),
|
|
+ CHAN(2472),
|
|
+ CHAN(2484),
|
|
+};
|
|
+
|
|
+static struct ieee80211_supported_band bl_band_2GHz = {
|
|
+ .channels = bl_2ghz_channels,
|
|
+ .n_channels = ARRAY_SIZE(bl_2ghz_channels),
|
|
+ .bitrates = bl_ratetable,
|
|
+ .n_bitrates = ARRAY_SIZE(bl_ratetable),
|
|
+ .ht_cap = BL_HT_CAPABILITIES,
|
|
+};
|
|
+
|
|
+static struct ieee80211_iface_limit bl_limits[] = {
|
|
+ { .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP) |
|
|
+ BIT(NL80211_IFTYPE_STATION)}
|
|
+};
|
|
+
|
|
+static struct ieee80211_iface_limit bl_limits_dfs[] = {
|
|
+ { .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP)}
|
|
+};
|
|
+
|
|
+static const struct ieee80211_iface_combination bl_combinations[] = {
|
|
+ {
|
|
+ .limits = bl_limits,
|
|
+ .n_limits = ARRAY_SIZE(bl_limits),
|
|
+ .num_different_channels = NX_CHAN_CTXT_CNT,
|
|
+ .max_interfaces = NX_VIRT_DEV_MAX,
|
|
+ },
|
|
+ /* Keep this combination as the last one */
|
|
+ {
|
|
+ .limits = bl_limits_dfs,
|
|
+ .n_limits = ARRAY_SIZE(bl_limits_dfs),
|
|
+ .num_different_channels = 1,
|
|
+ .max_interfaces = NX_VIRT_DEV_MAX,
|
|
+ .radar_detect_widths = (BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
|
+ BIT(NL80211_CHAN_WIDTH_20) |
|
|
+ BIT(NL80211_CHAN_WIDTH_40) |
|
|
+ BIT(NL80211_CHAN_WIDTH_80)),
|
|
+ }
|
|
+};
|
|
+
|
|
+/* There isn't a lot of sense in it, but you can transmit anything you like */
|
|
+static struct ieee80211_txrx_stypes
|
|
+bl_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
|
|
+ [NL80211_IFTYPE_STATION] = {
|
|
+ .tx = 0xffff,
|
|
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
|
|
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 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_AP_VLAN] = {
|
|
+ /* copy 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),
|
|
+ },
|
|
+};
|
|
+
|
|
+
|
|
+static u32 cipher_suites[] = {
|
|
+ WLAN_CIPHER_SUITE_WEP40,
|
|
+ WLAN_CIPHER_SUITE_WEP104,
|
|
+ WLAN_CIPHER_SUITE_TKIP,
|
|
+ WLAN_CIPHER_SUITE_CCMP,
|
|
+ 0, // reserved entries to enable AES-CMAC and/or SMS4
|
|
+ 0,
|
|
+};
|
|
+#define NB_RESERVED_CIPHER 2;
|
|
+
|
|
+static const int bl_ac2hwq[1][NL80211_NUM_ACS] = {
|
|
+ {
|
|
+ [NL80211_TXQ_Q_VO] = BL_HWQ_VO,
|
|
+ [NL80211_TXQ_Q_VI] = BL_HWQ_VI,
|
|
+ [NL80211_TXQ_Q_BE] = BL_HWQ_BE,
|
|
+ [NL80211_TXQ_Q_BK] = BL_HWQ_BK
|
|
+ }
|
|
+};
|
|
+
|
|
+const int bl_tid2hwq[IEEE80211_NUM_TIDS] = {
|
|
+ BL_HWQ_BE,
|
|
+ BL_HWQ_BK,
|
|
+ BL_HWQ_BK,
|
|
+ BL_HWQ_BE,
|
|
+ BL_HWQ_VI,
|
|
+ BL_HWQ_VI,
|
|
+ BL_HWQ_VO,
|
|
+ BL_HWQ_VO,
|
|
+ /* TID_8 is used for management frames */
|
|
+ BL_HWQ_VO,
|
|
+ /* At the moment, all others TID are mapped to BE */
|
|
+ BL_HWQ_BE,
|
|
+ BL_HWQ_BE,
|
|
+ BL_HWQ_BE,
|
|
+ BL_HWQ_BE,
|
|
+ BL_HWQ_BE,
|
|
+ BL_HWQ_BE,
|
|
+ BL_HWQ_BE,
|
|
+};
|
|
+
|
|
+static const int bl_hwq2uapsd[NL80211_NUM_ACS] = {
|
|
+ [BL_HWQ_VO] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VO,
|
|
+ [BL_HWQ_VI] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VI,
|
|
+ [BL_HWQ_BE] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
|
|
+ [BL_HWQ_BK] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BK,
|
|
+};
|
|
+
|
|
+/*********************************************************************
|
|
+ * helper
|
|
+ *********************************************************************/
|
|
+struct bl_sta *bl_get_sta(struct bl_hw *bl_hw, const u8 *mac_addr)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < NX_REMOTE_STA_MAX; i++) {
|
|
+ struct bl_sta *sta = &bl_hw->sta_table[i];
|
|
+ if (sta->valid && (memcmp(mac_addr, &sta->mac_addr, 6) == 0))
|
|
+ return sta;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+void bl_enable_wapi(struct bl_hw *bl_hw)
|
|
+{
|
|
+ cipher_suites[bl_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_SMS4;
|
|
+ bl_hw->wiphy->n_cipher_suites ++;
|
|
+ bl_hw->wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
|
|
+}
|
|
+
|
|
+void bl_enable_mfp(struct bl_hw *bl_hw)
|
|
+{
|
|
+ cipher_suites[bl_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
|
|
+ bl_hw->wiphy->n_cipher_suites ++;
|
|
+}
|
|
+
|
|
+u8 *bl_build_bcn(struct bl_bcn *bcn, struct cfg80211_beacon_data *new)
|
|
+{
|
|
+ u8 *buf, *pos;
|
|
+
|
|
+ if (new->head) {
|
|
+ u8 *head = kmalloc(new->head_len, GFP_KERNEL);
|
|
+
|
|
+ if (!head)
|
|
+ return NULL;
|
|
+
|
|
+ if (bcn->head)
|
|
+ kfree(bcn->head);
|
|
+
|
|
+ bcn->head = head;
|
|
+ bcn->head_len = new->head_len;
|
|
+ memcpy(bcn->head, new->head, new->head_len);
|
|
+ }
|
|
+ if (new->tail) {
|
|
+ u8 *tail = kmalloc(new->tail_len, GFP_KERNEL);
|
|
+
|
|
+ if (!tail)
|
|
+ return NULL;
|
|
+
|
|
+ if (bcn->tail)
|
|
+ kfree(bcn->tail);
|
|
+
|
|
+ bcn->tail = tail;
|
|
+ bcn->tail_len = new->tail_len;
|
|
+ memcpy(bcn->tail, new->tail, new->tail_len);
|
|
+ }
|
|
+
|
|
+ if (!bcn->head)
|
|
+ return NULL;
|
|
+
|
|
+ bcn->tim_len = 6;
|
|
+ bcn->len = bcn->head_len + bcn->tail_len + bcn->ies_len + bcn->tim_len;
|
|
+
|
|
+ buf = kmalloc(bcn->len, GFP_KERNEL);
|
|
+ if (!buf)
|
|
+ return NULL;
|
|
+
|
|
+ // Build the beacon buffer
|
|
+ pos = buf;
|
|
+ memcpy(pos, bcn->head, bcn->head_len);
|
|
+ pos += bcn->head_len;
|
|
+ *pos++ = WLAN_EID_TIM;
|
|
+ *pos++ = 4;
|
|
+ *pos++ = 0;
|
|
+ *pos++ = bcn->dtim;
|
|
+ *pos++ = 0;
|
|
+ *pos++ = 0;
|
|
+ if (bcn->tail) {
|
|
+ memcpy(pos, bcn->tail, bcn->tail_len);
|
|
+ pos += bcn->tail_len;
|
|
+ }
|
|
+ if (bcn->ies) {
|
|
+ memcpy(pos, bcn->ies, bcn->ies_len);
|
|
+ }
|
|
+
|
|
+ return buf;
|
|
+}
|
|
+
|
|
+
|
|
+static void bl_del_bcn(struct bl_bcn *bcn)
|
|
+{
|
|
+ if (bcn->head) {
|
|
+ kfree(bcn->head);
|
|
+ bcn->head = NULL;
|
|
+ }
|
|
+ bcn->head_len = 0;
|
|
+
|
|
+ if (bcn->tail) {
|
|
+ kfree(bcn->tail);
|
|
+ bcn->tail = NULL;
|
|
+ }
|
|
+ bcn->tail_len = 0;
|
|
+
|
|
+ if (bcn->ies) {
|
|
+ kfree(bcn->ies);
|
|
+ bcn->ies = NULL;
|
|
+ }
|
|
+ bcn->ies_len = 0;
|
|
+ bcn->tim_len = 0;
|
|
+ bcn->dtim = 0;
|
|
+ bcn->len = 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Link channel ctxt to a vif and thus increments count for this context.
|
|
+ */
|
|
+void bl_chanctx_link(struct bl_vif *vif, u8 ch_idx,
|
|
+ struct cfg80211_chan_def *chandef)
|
|
+{
|
|
+ struct bl_chanctx *ctxt;
|
|
+
|
|
+ if (ch_idx >= NX_CHAN_CTXT_CNT) {
|
|
+ WARN(1, "Invalid channel ctxt id %d", ch_idx);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ vif->ch_index = ch_idx;
|
|
+ ctxt = &vif->bl_hw->chanctx_table[ch_idx];
|
|
+ ctxt->count++;
|
|
+
|
|
+ // For now chandef is NULL for STATION interface
|
|
+ if (chandef) {
|
|
+ if (!ctxt->chan_def.chan)
|
|
+ ctxt->chan_def = *chandef;
|
|
+ else {
|
|
+ // TODO. check that chandef is the same as the one already
|
|
+ // set for this ctxt
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Unlink channel ctxt from a vif and thus decrements count for this context
|
|
+ */
|
|
+void bl_chanctx_unlink(struct bl_vif *vif)
|
|
+{
|
|
+ struct bl_chanctx *ctxt;
|
|
+
|
|
+ if (vif->ch_index == BL_CH_NOT_SET)
|
|
+ return;
|
|
+
|
|
+ ctxt = &vif->bl_hw->chanctx_table[vif->ch_index];
|
|
+
|
|
+ if (ctxt->count == 0) {
|
|
+ WARN(1, "Chan ctxt ref count is already 0");
|
|
+ } else {
|
|
+ ctxt->count--;
|
|
+ }
|
|
+
|
|
+ if (ctxt->count == 0) {
|
|
+ /* set chan to null, so that if this ctxt is relinked to a vif that
|
|
+ don't have channel information, don't use wrong information */
|
|
+ ctxt->chan_def.chan = NULL;
|
|
+ }
|
|
+ vif->ch_index = BL_CH_NOT_SET;
|
|
+}
|
|
+
|
|
+int bl_chanctx_valid(struct bl_hw *bl_hw, u8 ch_idx)
|
|
+{
|
|
+ if (ch_idx >= NX_CHAN_CTXT_CNT ||
|
|
+ bl_hw->chanctx_table[ch_idx].chan_def.chan == NULL) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static void bl_del_csa(struct bl_vif *vif)
|
|
+{
|
|
+ struct bl_csa *csa = vif->ap.csa;
|
|
+
|
|
+ if (!csa)
|
|
+ return;
|
|
+
|
|
+ if (csa->dma.buf) {
|
|
+ kfree(csa->dma.buf);
|
|
+ }
|
|
+ bl_del_bcn(&csa->bcn);
|
|
+ kfree(csa);
|
|
+ vif->ap.csa = NULL;
|
|
+}
|
|
+
|
|
+static void bl_csa_finish(struct work_struct *ws)
|
|
+{
|
|
+ struct bl_csa *csa = container_of(ws, struct bl_csa, work);
|
|
+ struct bl_vif *vif = csa->vif;
|
|
+ struct bl_hw *bl_hw = vif->bl_hw;
|
|
+ int error = csa->status;
|
|
+
|
|
+ if (!error)
|
|
+ error = bl_send_bcn_change(bl_hw, vif->vif_index, csa->dma.buf,
|
|
+ csa->bcn.len, csa->bcn.head_len,
|
|
+ csa->bcn.tim_len, NULL);
|
|
+
|
|
+ if (error)
|
|
+ cfg80211_stop_iface(bl_hw->wiphy, &vif->wdev, GFP_KERNEL);
|
|
+ else {
|
|
+ bl_chanctx_unlink(vif);
|
|
+ bl_chanctx_link(vif, csa->ch_idx, &csa->chandef);
|
|
+ spin_lock_bh(&bl_hw->cmd_mgr.lock);
|
|
+ if (bl_hw->cur_chanctx == csa->ch_idx) {
|
|
+ bl_txq_vif_start(vif, BL_TXQ_STOP_CHAN, bl_hw);
|
|
+ } else
|
|
+ bl_txq_vif_stop(vif, BL_TXQ_STOP_CHAN, bl_hw);
|
|
+ spin_unlock_bh(&bl_hw->cmd_mgr.lock);
|
|
+ cfg80211_ch_switch_notify(vif->ndev, &csa->chandef);
|
|
+ }
|
|
+ bl_del_csa(vif);
|
|
+}
|
|
+
|
|
+/*********************************************************************
|
|
+ * netdev callbacks
|
|
+ ********************************************************************/
|
|
+/**
|
|
+ * int (*ndo_open)(struct net_device *dev);
|
|
+ * This function is called when network device transistions to the up
|
|
+ * state.
|
|
+ *
|
|
+ * - Start FW if this is the first interface opened
|
|
+ * - Add interface at fw level
|
|
+ */
|
|
+static int bl_open(struct net_device *dev)
|
|
+{
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+ struct mm_add_if_cfm add_if_cfm;
|
|
+ int error = 0;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ // Check if it is the first opened VIF
|
|
+ if (bl_hw->vif_started == 0)
|
|
+ {
|
|
+ // Start the FW
|
|
+ if ((error = bl_send_start(bl_hw)))
|
|
+ return error;
|
|
+
|
|
+ /* Device is now started */
|
|
+ set_bit(BL_DEV_STARTED, &bl_hw->drv_flags);
|
|
+ }
|
|
+
|
|
+ if (BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_AP_VLAN) {
|
|
+ /* For AP_vlan use same fw and drv indexes. We ensure that this index
|
|
+ will not be used by fw for another vif by taking index >= NX_VIRT_DEV_MAX */
|
|
+ add_if_cfm.inst_nbr = bl_vif->drv_vif_index;
|
|
+ netif_tx_stop_all_queues(dev);
|
|
+ } else {
|
|
+ /* Forward the information to the LMAC,
|
|
+ * p2p value not used in FMAC configuration, iftype is sufficient */
|
|
+ if ((error = bl_send_add_if(bl_hw, dev->dev_addr,
|
|
+ BL_VIF_TYPE(bl_vif), false, &add_if_cfm)))
|
|
+ return error;
|
|
+
|
|
+ if (add_if_cfm.status != 0) {
|
|
+ BL_PRINT_CFM_ERR(add_if);
|
|
+ return -EIO;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Save the index retrieved from LMAC */
|
|
+ bl_vif->vif_index = add_if_cfm.inst_nbr;
|
|
+ bl_vif->up = true;
|
|
+ bl_hw->vif_started++;
|
|
+ bl_hw->vif_table[add_if_cfm.inst_nbr] = bl_vif;
|
|
+
|
|
+ netif_carrier_off(dev);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * int (*ndo_stop)(struct net_device *dev);
|
|
+ * This function is called when network device transistions to the down
|
|
+ * state.
|
|
+ *
|
|
+ * - Remove interface at fw level
|
|
+ * - Reset FW if this is the last interface opened
|
|
+ */
|
|
+static int bl_close(struct net_device *dev)
|
|
+{
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ netdev_info(dev, "CLOSE");
|
|
+
|
|
+ /* Abort scan request on the vif */
|
|
+ if (bl_hw->scan_request &&
|
|
+ bl_hw->scan_request->wdev == &bl_vif->wdev) {
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
|
|
+ struct cfg80211_scan_info info = {
|
|
+ .aborted = true,
|
|
+ };
|
|
+
|
|
+ cfg80211_scan_done(bl_hw->scan_request, &info);
|
|
+#else
|
|
+ cfg80211_scan_done(bl_hw->scan_request, true);
|
|
+#endif
|
|
+ bl_hw->scan_request = NULL;
|
|
+ }
|
|
+
|
|
+ bl_send_remove_if(bl_hw, bl_vif->vif_index);
|
|
+
|
|
+ if (bl_hw->roc_elem && (bl_hw->roc_elem->wdev == &bl_vif->wdev)) {
|
|
+ /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
|
|
+ bl_hw->roc_elem = NULL;
|
|
+ }
|
|
+
|
|
+ /* Ensure that we won't process disconnect ind */
|
|
+ spin_lock_bh(&bl_hw->cmd_mgr.lock);
|
|
+
|
|
+ bl_vif->up = false;
|
|
+ if (netif_carrier_ok(dev)) {
|
|
+ if (BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_STATION ||
|
|
+ BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_P2P_CLIENT) {
|
|
+ cfg80211_disconnected(dev, WLAN_REASON_DEAUTH_LEAVING,
|
|
+ NULL, 0, true, GFP_ATOMIC);
|
|
+ netif_tx_stop_all_queues(dev);
|
|
+ netif_carrier_off(dev);
|
|
+ } else if (BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_AP_VLAN) {
|
|
+ netif_carrier_off(dev);
|
|
+ } else {
|
|
+ netdev_warn(dev, "AP not stopped when disabling interface");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_unlock_bh(&bl_hw->cmd_mgr.lock);
|
|
+
|
|
+ bl_hw->vif_table[bl_vif->vif_index] = NULL;
|
|
+ bl_hw->vif_started--;
|
|
+ if (bl_hw->vif_started == 0) {
|
|
+ /* This also lets both ipc sides remain in sync before resetting */
|
|
+ bl_ipc_tx_drain(bl_hw);
|
|
+
|
|
+ bl_send_reset(bl_hw);
|
|
+
|
|
+ // Set parameters to firmware
|
|
+ bl_send_me_config_req(bl_hw);
|
|
+
|
|
+ // Set channel parameters to firmware
|
|
+ bl_send_me_chan_config_req(bl_hw);
|
|
+
|
|
+ clear_bit(BL_DEV_STARTED, &bl_hw->drv_flags);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
|
|
+ * Called when a user wants to get the network device usage
|
|
+ * statistics. Drivers must do one of the following:
|
|
+ * 1. Define @ndo_get_stats64 to fill in a zero-initialised
|
|
+ * rtnl_link_stats64 structure passed by the caller.
|
|
+ * 2. Define @ndo_get_stats to update a net_device_stats structure
|
|
+ * (which should normally be dev->stats) and return a pointer to
|
|
+ * it. The structure may be changed asynchronously only if each
|
|
+ * field is written atomically.
|
|
+ * 3. Update dev->stats asynchronously and atomically, and define
|
|
+ * neither operation.
|
|
+ */
|
|
+static struct net_device_stats *bl_get_stats(struct net_device *dev)
|
|
+{
|
|
+ struct bl_vif *vif = netdev_priv(dev);
|
|
+
|
|
+ return &vif->net_stats;
|
|
+}
|
|
+
|
|
+static const u16 bl_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
|
|
+/**
|
|
+ * u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,
|
|
+ * void *accel_priv, select_queue_fallback_t fallback);
|
|
+ * Called to decide which queue to when device supports multiple
|
|
+ * transmit queues.
|
|
+ */
|
|
+u16 bl_select_queue(struct net_device *dev, struct sk_buff *skb,
|
|
+ struct net_device *sb_dev)
|
|
+{
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+ struct wireless_dev *wdev = &bl_vif->wdev;
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct bl_txq *txq;
|
|
+ u16 netdev_queue;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+#define PRIO_STA_NULL 0xAA
|
|
+
|
|
+ switch (wdev->iftype) {
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
+ {
|
|
+ struct ethhdr *eth;
|
|
+ eth = (struct ethhdr *)skb->data;
|
|
+ sta = bl_vif->sta.ap;
|
|
+ break;
|
|
+ }
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ if (bl_vif->ap_vlan.sta_4a) {
|
|
+ sta = bl_vif->ap_vlan.sta_4a;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* AP_VLAN interface is not used for a 4A STA,
|
|
+ fallback searching sta amongs all AP's clients */
|
|
+ bl_vif = bl_vif->ap_vlan.master;
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_P2P_GO:
|
|
+ {
|
|
+ struct bl_sta *cur;
|
|
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
|
|
+
|
|
+ if (is_multicast_ether_addr(eth->h_dest)) {
|
|
+ sta = &bl_hw->sta_table[bl_vif->ap.bcmc_index];
|
|
+ } else {
|
|
+ list_for_each_entry(cur, &bl_vif->ap.sta_list, list) {
|
|
+ if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
|
|
+ sta = cur;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (sta && sta->qos)
|
|
+ {
|
|
+ /* use the data classifier to determine what 802.1d tag the data frame has */
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
+ skb->priority = cfg80211_classify8021d(skb, NULL) & IEEE80211_QOS_CTL_TAG1D_MASK;
|
|
+#else
|
|
+ skb->priority = cfg80211_classify8021d(skb) & IEEE80211_QOS_CTL_TAG1D_MASK;
|
|
+#endif
|
|
+ if (sta->acm)
|
|
+ bl_downgrade_ac(sta, skb);
|
|
+
|
|
+ txq = bl_txq_sta_get(sta, skb->priority, NULL, bl_hw);
|
|
+ netdev_queue = txq->ndev_idx;
|
|
+ }
|
|
+ else if (sta)
|
|
+ {
|
|
+ skb->priority = 0xFF;
|
|
+ txq = bl_txq_sta_get(sta, 0, NULL, bl_hw);
|
|
+ netdev_queue = txq->ndev_idx;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* This packet will be dropped in xmit function, still need to select
|
|
+ an active queue for xmit to be called. As it most likely to happen
|
|
+ for AP interface, select BCMC queue
|
|
+ (TODO: select another queue if BCMC queue is stopped) */
|
|
+ skb->priority = PRIO_STA_NULL;
|
|
+ netdev_queue = NX_BCMC_TXQ_NDEV_IDX;
|
|
+ }
|
|
+
|
|
+ if(skb->priority < 8)
|
|
+ return bl_1d_to_wmm_queue[skb->priority];
|
|
+ else
|
|
+ return 0;
|
|
+
|
|
+ //BUG_ON(netdev_queue >= NX_NB_NDEV_TXQ);
|
|
+ //return netdev_queue;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
|
|
+ * This function is called when the Media Access Control address
|
|
+ * needs to be changed. If this interface is not defined, the
|
|
+ * mac address can not be changed.
|
|
+ */
|
|
+static int bl_set_mac_address(struct net_device *dev, void *addr)
|
|
+{
|
|
+ struct sockaddr *sa = addr;
|
|
+ int ret;
|
|
+
|
|
+ ret = eth_mac_addr(dev, sa);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct net_device_ops bl_netdev_ops = {
|
|
+ .ndo_open = bl_open,
|
|
+ .ndo_stop = bl_close,
|
|
+ .ndo_start_xmit = bl_start_xmit,
|
|
+ .ndo_get_stats = bl_get_stats,
|
|
+ .ndo_select_queue = bl_select_queue,
|
|
+ .ndo_set_mac_address = bl_set_mac_address
|
|
+};
|
|
+
|
|
+static void bl_netdev_setup(struct net_device *dev)
|
|
+{
|
|
+ ether_setup(dev);
|
|
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
|
|
+ dev->netdev_ops = &bl_netdev_ops;
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
|
|
+ dev->destructor = free_netdev;
|
|
+#else
|
|
+ dev->needs_free_netdev = true;
|
|
+#endif
|
|
+
|
|
+ dev->watchdog_timeo = BL_TX_LIFETIME_MS;
|
|
+
|
|
+ dev->needed_headroom = sizeof(struct bl_txhdr) + BL_SWTXHDR_ALIGN_SZ;
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ dev->needed_headroom = max(dev->needed_headroom,
|
|
+ (unsigned short)(sizeof(struct bl_amsdu_txhdr)
|
|
+ + sizeof(struct ethhdr) + 4
|
|
+ + sizeof(rfc1042_header) + 2));
|
|
+#endif /* CONFIG_BL_AMSDUS_TX */
|
|
+ dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
|
|
+
|
|
+ dev->hw_features = 0;
|
|
+}
|
|
+
|
|
+/*********************************************************************
|
|
+ * Cfg80211 callbacks (and helper)
|
|
+ *********************************************************************/
|
|
+static struct wireless_dev *bl_interface_add(struct bl_hw *bl_hw,
|
|
+ const char *name,
|
|
+ enum nl80211_iftype type,
|
|
+ struct vif_params *params)
|
|
+{
|
|
+ struct net_device *ndev;
|
|
+ struct bl_vif *vif;
|
|
+ int min_idx, max_idx;
|
|
+ int vif_idx = -1;
|
|
+ int i;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ // Look for an available VIF
|
|
+ if (type == NL80211_IFTYPE_AP_VLAN) {
|
|
+ min_idx = NX_VIRT_DEV_MAX;
|
|
+ max_idx = NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX;
|
|
+ } else {
|
|
+ min_idx = 0;
|
|
+ max_idx = NX_VIRT_DEV_MAX;
|
|
+ }
|
|
+
|
|
+ for (i = min_idx; i < max_idx; i++) {
|
|
+ if ((bl_hw->avail_idx_map) & BIT(i)) {
|
|
+ vif_idx = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (vif_idx < 0)
|
|
+ return NULL;
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+ ndev = alloc_netdev_mqs(sizeof(*vif), name, NET_NAME_UNKNOWN,
|
|
+ bl_netdev_setup, IEEE80211_NUM_ACS, 1);
|
|
+#else
|
|
+ ndev = alloc_netdev_mqs(sizeof(*vif), name, bl_netdev_setup,
|
|
+ IEEE80211_NUM_ACS, 1);
|
|
+#endif
|
|
+ if (!ndev)
|
|
+ return NULL;
|
|
+
|
|
+ vif = netdev_priv(ndev);
|
|
+ ndev->ieee80211_ptr = &vif->wdev;
|
|
+ vif->wdev.wiphy = bl_hw->wiphy;
|
|
+ vif->bl_hw = bl_hw;
|
|
+ vif->ndev = ndev;
|
|
+ vif->drv_vif_index = vif_idx;
|
|
+ SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
|
|
+ vif->wdev.netdev = ndev;
|
|
+ vif->wdev.iftype = type;
|
|
+ vif->up = false;
|
|
+ vif->ch_index = BL_CH_NOT_SET;
|
|
+ memset(&vif->net_stats, 0, sizeof(vif->net_stats));
|
|
+
|
|
+ switch (type) {
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
+ vif->sta.ap = NULL;
|
|
+ vif->sta.tdls_sta = NULL;
|
|
+ break;
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ INIT_LIST_HEAD(&vif->ap.mpath_list);
|
|
+ INIT_LIST_HEAD(&vif->ap.proxy_list);
|
|
+ vif->ap.create_path = false;
|
|
+ vif->ap.generation = 0;
|
|
+ // no break
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_P2P_GO:
|
|
+ INIT_LIST_HEAD(&vif->ap.sta_list);
|
|
+ memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
|
|
+ break;
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ {
|
|
+ struct bl_vif *master_vif;
|
|
+ bool found = false;
|
|
+ list_for_each_entry(master_vif, &bl_hw->vifs, list) {
|
|
+ if ((BL_VIF_TYPE(master_vif) == NL80211_IFTYPE_AP) &&
|
|
+ !(!memcmp(master_vif->ndev->dev_addr, params->macaddr,
|
|
+ ETH_ALEN))) {
|
|
+ found=true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!found)
|
|
+ goto err;
|
|
+
|
|
+ vif->ap_vlan.master = master_vif;
|
|
+ vif->ap_vlan.sta_4a = NULL;
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (type == NL80211_IFTYPE_AP_VLAN)
|
|
+ memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
|
|
+ else {
|
|
+ memcpy(ndev->dev_addr, bl_hw->wiphy->perm_addr, ETH_ALEN);
|
|
+ ndev->dev_addr[5] ^= vif_idx;
|
|
+ }
|
|
+
|
|
+ if (params) {
|
|
+ vif->use_4addr = params->use_4addr;
|
|
+ ndev->ieee80211_ptr->use_4addr = params->use_4addr;
|
|
+ } else
|
|
+ vif->use_4addr = false;
|
|
+
|
|
+ if (register_netdevice(ndev))
|
|
+ goto err;
|
|
+
|
|
+ list_add_tail(&vif->list, &bl_hw->vifs);
|
|
+ bl_hw->avail_idx_map &= ~BIT(vif_idx);
|
|
+
|
|
+ return &vif->wdev;
|
|
+
|
|
+err:
|
|
+ free_netdev(ndev);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * @brief Retrieve the bl_sta object allocated for a given MAC address
|
|
+ * and a given role.
|
|
+ */
|
|
+static struct bl_sta *bl_retrieve_sta(struct bl_hw *bl_hw,
|
|
+ struct bl_vif *bl_vif, u8 *addr,
|
|
+ __le16 fc, bool ap)
|
|
+{
|
|
+ if (ap) {
|
|
+ /* only deauth, disassoc and action are bufferable MMPDUs */
|
|
+ bool bufferable = ieee80211_is_deauth(fc) ||
|
|
+ ieee80211_is_disassoc(fc) ||
|
|
+ ieee80211_is_action(fc);
|
|
+
|
|
+ /* Check if the packet is bufferable or not */
|
|
+ if (bufferable)
|
|
+ {
|
|
+ /* Check if address is a broadcast or a multicast address */
|
|
+ if (is_broadcast_ether_addr(addr) || is_multicast_ether_addr(addr)) {
|
|
+ /* Returned STA pointer */
|
|
+ struct bl_sta *bl_sta = &bl_hw->sta_table[bl_vif->ap.bcmc_index];
|
|
+
|
|
+ if (bl_sta->valid)
|
|
+ return bl_sta;
|
|
+ } else {
|
|
+ /* Returned STA pointer */
|
|
+ struct bl_sta *bl_sta;
|
|
+
|
|
+ /* Go through list of STAs linked with the provided VIF */
|
|
+ list_for_each_entry(bl_sta, &bl_vif->ap.sta_list, list) {
|
|
+ if (bl_sta->valid &&
|
|
+ ether_addr_equal(bl_sta->mac_addr, addr)) {
|
|
+ /* Return the found STA */
|
|
+ return bl_sta;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ return bl_vif->sta.ap;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @add_virtual_intf: create a new virtual interface with the given name,
|
|
+ * must set the struct wireless_dev's iftype. Beware: You must create
|
|
+ * the new netdev in the wiphy's network namespace! Returns the struct
|
|
+ * wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must
|
|
+ * also set the address member in the wdev.
|
|
+ */
|
|
+static struct wireless_dev *bl_cfg80211_add_iface(struct wiphy *wiphy,
|
|
+ const char *name,
|
|
+ unsigned char name_assign_type,
|
|
+ enum nl80211_iftype type,
|
|
+ struct vif_params *params)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct wireless_dev *wdev;
|
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
|
|
+ unsigned char name_assign_type = NET_NAME_UNKNOWN;
|
|
+#endif
|
|
+
|
|
+ wdev = bl_interface_add(bl_hw, name, type, params);
|
|
+
|
|
+ return wdev;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @del_virtual_intf: remove the virtual interface
|
|
+ */
|
|
+static int bl_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
|
|
+{
|
|
+ struct net_device *dev = wdev->netdev;
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+
|
|
+ netdev_info(dev, "Remove Interface");
|
|
+
|
|
+ if (dev->reg_state == NETREG_REGISTERED) {
|
|
+ /* Will call bl_close if interface is UP */
|
|
+ unregister_netdevice(dev);
|
|
+ }
|
|
+
|
|
+ list_del(&bl_vif->list);
|
|
+ bl_hw->avail_idx_map |= BIT(bl_vif->drv_vif_index);
|
|
+ bl_vif->ndev = NULL;
|
|
+
|
|
+ /* Clear the priv in adapter */
|
|
+ dev->ieee80211_ptr = NULL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @change_virtual_intf: change type/configuration of virtual interface,
|
|
+ * keep the struct wireless_dev's iftype updated.
|
|
+ */
|
|
+static int bl_cfg80211_change_iface(struct wiphy *wiphy,
|
|
+ struct net_device *dev,
|
|
+ enum nl80211_iftype type,
|
|
+ struct vif_params *params)
|
|
+{
|
|
+ struct bl_vif *vif = netdev_priv(dev);
|
|
+
|
|
+ if (vif->up)
|
|
+ return (-EBUSY);
|
|
+
|
|
+ switch (type) {
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
+ vif->sta.ap = NULL;
|
|
+ vif->sta.tdls_sta = NULL;
|
|
+ break;
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ INIT_LIST_HEAD(&vif->ap.mpath_list);
|
|
+ INIT_LIST_HEAD(&vif->ap.proxy_list);
|
|
+ vif->ap.create_path = false;
|
|
+ vif->ap.generation = 0;
|
|
+ // no break
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_P2P_GO:
|
|
+ INIT_LIST_HEAD(&vif->ap.sta_list);
|
|
+ memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
|
|
+ break;
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ return -EPERM;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ vif->wdev.iftype = type;
|
|
+ if (params->use_4addr != -1)
|
|
+ vif->use_4addr = params->use_4addr;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @scan: Request to do a scan. If returning zero, the scan request is given
|
|
+ * the driver, and will be valid until passed to cfg80211_scan_done().
|
|
+ * For scan results, call cfg80211_inform_bss(); you can call this outside
|
|
+ * the scan/scan_done bracket too.
|
|
+ */
|
|
+static int bl_cfg80211_scan(struct wiphy *wiphy,
|
|
+ struct cfg80211_scan_request *request)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = container_of(request->wdev, struct bl_vif,
|
|
+ wdev);
|
|
+ int error;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if ((error = bl_send_scanu_req(bl_hw, bl_vif, request)))
|
|
+ return error;
|
|
+
|
|
+ bl_hw->scan_request = request;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @add_key: add a key with the given parameters. @mac_addr will be %NULL
|
|
+ * when adding a group key.
|
|
+ */
|
|
+static int bl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
|
|
+ u8 key_index, bool pairwise, const u8 *mac_addr,
|
|
+ struct key_params *params)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *vif = netdev_priv(netdev);
|
|
+ int i, error = 0;
|
|
+ struct mm_key_add_cfm key_add_cfm;
|
|
+ u8_l cipher = 0;
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct bl_key *bl_key;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if (mac_addr) {
|
|
+ sta = bl_get_sta(bl_hw, mac_addr);
|
|
+ if (!sta)
|
|
+ return -EINVAL;
|
|
+ bl_key = &sta->key;
|
|
+ }
|
|
+ else
|
|
+ bl_key = &vif->key[key_index];
|
|
+
|
|
+ /* Retrieve the cipher suite selector */
|
|
+ switch (params->cipher) {
|
|
+ case WLAN_CIPHER_SUITE_WEP40:
|
|
+ cipher = MAC_RSNIE_CIPHER_WEP40;
|
|
+ break;
|
|
+ case WLAN_CIPHER_SUITE_WEP104:
|
|
+ cipher = MAC_RSNIE_CIPHER_WEP104;
|
|
+ break;
|
|
+ case WLAN_CIPHER_SUITE_TKIP:
|
|
+ cipher = MAC_RSNIE_CIPHER_TKIP;
|
|
+ break;
|
|
+ case WLAN_CIPHER_SUITE_CCMP:
|
|
+ cipher = MAC_RSNIE_CIPHER_CCMP;
|
|
+ break;
|
|
+ case WLAN_CIPHER_SUITE_AES_CMAC:
|
|
+ cipher = MAC_RSNIE_CIPHER_AES_CMAC;
|
|
+ break;
|
|
+ case WLAN_CIPHER_SUITE_SMS4:
|
|
+ {
|
|
+ // Need to reverse key order
|
|
+ u8 tmp, *key = (u8 *)params->key;
|
|
+ cipher = MAC_RSNIE_CIPHER_SMS4;
|
|
+ for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
|
|
+ tmp = key[i];
|
|
+ key[i] = key[WPI_SUBKEY_LEN - 1 - i];
|
|
+ key[WPI_SUBKEY_LEN - 1 - i] = tmp;
|
|
+ }
|
|
+ for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
|
|
+ tmp = key[i + WPI_SUBKEY_LEN];
|
|
+ key[i + WPI_SUBKEY_LEN] = key[WPI_KEY_LEN - 1 - i];
|
|
+ key[WPI_KEY_LEN - 1 - i] = tmp;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if ((error = bl_send_key_add(bl_hw, vif->vif_index,
|
|
+ (sta ? sta->sta_idx : 0xFF), pairwise,
|
|
+ (u8 *)params->key, params->key_len,
|
|
+ key_index, cipher, &key_add_cfm)))
|
|
+ return error;
|
|
+
|
|
+ if (key_add_cfm.status != 0) {
|
|
+ BL_PRINT_CFM_ERR(key_add);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* Save the index retrieved from LMAC */
|
|
+ bl_key->hw_idx = key_add_cfm.hw_key_idx;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @get_key: get information about the key with the given parameters.
|
|
+ * @mac_addr will be %NULL when requesting information for a group
|
|
+ * key. All pointers given to the @callback function need not be valid
|
|
+ * after it returns. This function should return an error if it is
|
|
+ * not possible to retrieve the key, -ENOENT if it doesn't exist.
|
|
+ *
|
|
+ */
|
|
+static int bl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *netdev,
|
|
+ u8 key_index, bool pairwise, const u8 *mac_addr,
|
|
+ void *cookie,
|
|
+ void (*callback)(void *cookie, struct key_params*))
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * @del_key: remove a key given the @mac_addr (%NULL for a group key)
|
|
+ * and @key_index, return -ENOENT if the key doesn't exist.
|
|
+ */
|
|
+static int bl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
|
|
+ u8 key_index, bool pairwise, const u8 *mac_addr)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *vif = netdev_priv(netdev);
|
|
+ int error;
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct bl_key *bl_key;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+ if (mac_addr) {
|
|
+ sta = bl_get_sta(bl_hw, mac_addr);
|
|
+ if (!sta)
|
|
+ return -EINVAL;
|
|
+ bl_key = &sta->key;
|
|
+ }
|
|
+ else
|
|
+ bl_key = &vif->key[key_index];
|
|
+
|
|
+ error = bl_send_key_del(bl_hw, bl_key->hw_idx);
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @set_default_key: set the default key on an interface
|
|
+ */
|
|
+static int bl_cfg80211_set_default_key(struct wiphy *wiphy,
|
|
+ struct net_device *netdev,
|
|
+ u8 key_index, bool unicast, bool multicast)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @set_default_mgmt_key: set the default management frame key on an interface
|
|
+ */
|
|
+static int bl_cfg80211_set_default_mgmt_key(struct wiphy *wiphy,
|
|
+ struct net_device *netdev,
|
|
+ u8 key_index)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @connect: Connect to the ESS with the specified parameters. When connected,
|
|
+ * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
|
|
+ * If the connection fails for some reason, call cfg80211_connect_result()
|
|
+ * with the status from the AP.
|
|
+ * (invoked with the wireless_dev mutex held)
|
|
+ */
|
|
+static int bl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
|
|
+ struct cfg80211_connect_params *sme)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct sm_connect_cfm sm_connect_cfm;
|
|
+ int error = 0;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* For SHARED-KEY authentication, must install key first */
|
|
+ if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY && sme->key)
|
|
+ {
|
|
+ struct key_params key_params;
|
|
+ key_params.key = sme->key;
|
|
+ key_params.seq = NULL;
|
|
+ key_params.key_len = sme->key_len;
|
|
+ key_params.seq_len = 0;
|
|
+ key_params.cipher = sme->crypto.cipher_group;
|
|
+ bl_cfg80211_add_key(wiphy, dev, sme->key_idx, false, NULL, &key_params);
|
|
+ }
|
|
+
|
|
+ /* Forward the information to the LMAC */
|
|
+ if ((error = bl_send_sm_connect_req(bl_hw, bl_vif, sme, &sm_connect_cfm)))
|
|
+ return error;
|
|
+
|
|
+ // Check the status
|
|
+ switch (sm_connect_cfm.status)
|
|
+ {
|
|
+ case CO_OK:
|
|
+ error = 0;
|
|
+ break;
|
|
+ case CO_BUSY:
|
|
+ error = -EINPROGRESS;
|
|
+ break;
|
|
+ case CO_OP_IN_PROGRESS:
|
|
+ error = -EALREADY;
|
|
+ break;
|
|
+ default:
|
|
+ error = -EIO;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @disconnect: Disconnect from the BSS/ESS.
|
|
+ * (invoked with the wireless_dev mutex held)
|
|
+ */
|
|
+static int bl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
|
|
+ u16 reason_code)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ return(bl_send_sm_disconnect_req(bl_hw, bl_vif, reason_code));
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @add_station: Add a new station.
|
|
+ */
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
|
+static int bl_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
|
|
+ const u8 *mac, struct station_parameters *params)
|
|
+
|
|
+#else
|
|
+static int bl_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
|
|
+ u8 *mac, struct station_parameters *params)
|
|
+#endif
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct me_sta_add_cfm me_sta_add_cfm;
|
|
+ int error = 0;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ WARN_ON(BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_AP_VLAN);
|
|
+
|
|
+ /* Do not add TDLS station */
|
|
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
|
+ return 0;
|
|
+
|
|
+ /* Indicate we are in a STA addition process - This will allow handling
|
|
+ * potential PS mode change indications correctly
|
|
+ */
|
|
+ bl_hw->adding_sta = true;
|
|
+
|
|
+ /* Forward the information to the LMAC */
|
|
+ if ((error = bl_send_me_sta_add(bl_hw, params, mac, bl_vif->vif_index,
|
|
+ &me_sta_add_cfm)))
|
|
+ return error;
|
|
+
|
|
+ // Check the status
|
|
+ switch (me_sta_add_cfm.status)
|
|
+ {
|
|
+ case CO_OK:
|
|
+ {
|
|
+ struct bl_sta *sta = &bl_hw->sta_table[me_sta_add_cfm.sta_idx];
|
|
+ int tid;
|
|
+ sta->aid = params->aid;
|
|
+
|
|
+ sta->sta_idx = me_sta_add_cfm.sta_idx;
|
|
+ sta->ch_idx = bl_vif->ch_index;
|
|
+ sta->vif_idx = bl_vif->vif_index;
|
|
+ sta->vlan_idx = sta->vif_idx;
|
|
+ sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
|
|
+ sta->ht = params->ht_capa ? 1 : 0;
|
|
+ sta->vht = params->vht_capa ? 1 : 0;
|
|
+ sta->acm = 0;
|
|
+ for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
|
|
+ int uapsd_bit = bl_hwq2uapsd[bl_tid2hwq[tid]];
|
|
+ if (params->uapsd_queues & uapsd_bit)
|
|
+ sta->uapsd_tids |= 1 << tid;
|
|
+ else
|
|
+ sta->uapsd_tids &= ~(1 << tid);
|
|
+ }
|
|
+ memcpy(sta->mac_addr, mac, ETH_ALEN);
|
|
+ bl_dbgfs_register_rc_stat(bl_hw, sta);
|
|
+
|
|
+ /* Ensure that we won't process PS change or channel switch ind*/
|
|
+ spin_lock_bh(&bl_hw->cmd_mgr.lock);
|
|
+ bl_txq_sta_init(bl_hw, sta, bl_txq_vif_get_status(bl_vif));
|
|
+ list_add_tail(&sta->list, &bl_vif->ap.sta_list);
|
|
+ sta->valid = true;
|
|
+ bl_ps_bh_enable(bl_hw, sta, sta->ps.active || me_sta_add_cfm.pm_state);
|
|
+ spin_unlock_bh(&bl_hw->cmd_mgr.lock);
|
|
+
|
|
+ error = 0;
|
|
+
|
|
+#ifdef CONFIG_BL_BFMER
|
|
+ if (bl_hw->mod_params->bfmer)
|
|
+ bl_send_bfmer_enable(bl_hw, sta, params->vht_capa);
|
|
+
|
|
+ bl_mu_group_sta_init(sta, params->vht_capa);
|
|
+#endif /* CONFIG_BL_BFMER */
|
|
+
|
|
+ #define PRINT_STA_FLAG(f) \
|
|
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
|
|
+
|
|
+ netdev_info(dev, "Add sta %d (%pM) flags=%s%s%s%s%s%s%s",
|
|
+ sta->sta_idx, mac,
|
|
+ PRINT_STA_FLAG(AUTHORIZED),
|
|
+ PRINT_STA_FLAG(SHORT_PREAMBLE),
|
|
+ PRINT_STA_FLAG(WME),
|
|
+ PRINT_STA_FLAG(MFP),
|
|
+ PRINT_STA_FLAG(AUTHENTICATED),
|
|
+ PRINT_STA_FLAG(TDLS_PEER),
|
|
+ PRINT_STA_FLAG(ASSOCIATED));
|
|
+ #undef PRINT_STA_FLAG
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ error = -EBUSY;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ bl_hw->adding_sta = false;
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @del_station: Remove a station
|
|
+ */
|
|
+static int bl_cfg80211_del_station_compat(struct wiphy *wiphy, struct net_device *dev,
|
|
+ struct station_del_parameters *params)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct bl_sta *cur, *tmp;
|
|
+ int error = 0, found = 0;
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
|
+ const u8 *mac = NULL;
|
|
+ if(params)
|
|
+ mac = params->mac;
|
|
+#endif
|
|
+
|
|
+ list_for_each_entry_safe(cur, tmp, &bl_vif->ap.sta_list, list) {
|
|
+ if ((!mac) || (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
|
|
+ netdev_info(dev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
|
|
+ /* Ensure that we won't process PS change ind */
|
|
+ spin_lock_bh(&bl_hw->cmd_mgr.lock);
|
|
+ cur->ps.active = false;
|
|
+ cur->valid = false;
|
|
+ spin_unlock_bh(&bl_hw->cmd_mgr.lock);
|
|
+
|
|
+ if (cur->vif_idx != cur->vlan_idx) {
|
|
+ struct bl_vif *vlan_vif;
|
|
+ vlan_vif = bl_hw->vif_table[cur->vlan_idx];
|
|
+ if (vlan_vif->up) {
|
|
+ if ((BL_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
|
|
+ (vlan_vif->use_4addr)) {
|
|
+ vlan_vif->ap_vlan.sta_4a = NULL;
|
|
+ } else {
|
|
+ WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ bl_txq_sta_deinit(bl_hw, cur);
|
|
+ error = bl_send_me_sta_del(bl_hw, cur->sta_idx, false);
|
|
+ if ((error != 0) && (error != -EPIPE))
|
|
+ return error;
|
|
+
|
|
+#ifdef CONFIG_BL_BFMER
|
|
+ // Disable Beamformer if supported
|
|
+ bl_bfmer_report_del(bl_hw, cur);
|
|
+ bl_mu_group_sta_del(bl_hw, cur);
|
|
+#endif /* CONFIG_BL_BFMER */
|
|
+
|
|
+ list_del(&cur->list);
|
|
+ bl_dbgfs_unregister_rc_stat(bl_hw, cur);
|
|
+ found ++;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!found)
|
|
+ return -ENOENT;
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @change_station: Modify a given station. Note that flags changes are not much
|
|
+ * validated in cfg80211, in particular the auth/assoc/authorized flags
|
|
+ * might come to the driver in invalid combinations -- make sure to check
|
|
+ * them, also against the existing state! Drivers must call
|
|
+ * cfg80211_check_station_change() to validate the information.
|
|
+ */
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+static int bl_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
|
|
+ const u8 *mac, struct station_parameters *params)
|
|
+#else
|
|
+static int bl_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
|
|
+ u8 *mac, struct station_parameters *params)
|
|
+#endif
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *vif = netdev_priv(dev);
|
|
+ struct bl_sta *sta;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ sta = bl_get_sta(bl_hw, mac);
|
|
+ if (!sta)
|
|
+ {
|
|
+ /* Add the TDLS station */
|
|
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
|
+ {
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct me_sta_add_cfm me_sta_add_cfm;
|
|
+ int error = 0;
|
|
+
|
|
+ /* Indicate we are in a STA addition process - This will allow handling
|
|
+ * potential PS mode change indications correctly
|
|
+ */
|
|
+ bl_hw->adding_sta = true;
|
|
+
|
|
+ /* Forward the information to the LMAC */
|
|
+ if ((error = bl_send_me_sta_add(bl_hw, params, mac, bl_vif->vif_index,
|
|
+ &me_sta_add_cfm)))
|
|
+ return error;
|
|
+
|
|
+ // Check the status
|
|
+ switch (me_sta_add_cfm.status)
|
|
+ {
|
|
+ case CO_OK:
|
|
+ {
|
|
+ int tid;
|
|
+ sta = &bl_hw->sta_table[me_sta_add_cfm.sta_idx];
|
|
+ sta->aid = params->aid;
|
|
+ sta->sta_idx = me_sta_add_cfm.sta_idx;
|
|
+ sta->ch_idx = bl_vif->ch_index;
|
|
+ sta->vif_idx = bl_vif->vif_index;
|
|
+ sta->vlan_idx = sta->vif_idx;
|
|
+ sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
|
|
+ sta->ht = params->ht_capa ? 1 : 0;
|
|
+ sta->vht = params->vht_capa ? 1 : 0;
|
|
+ sta->acm = 0;
|
|
+ for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
|
|
+ int uapsd_bit = bl_hwq2uapsd[bl_tid2hwq[tid]];
|
|
+ if (params->uapsd_queues & uapsd_bit)
|
|
+ sta->uapsd_tids |= 1 << tid;
|
|
+ else
|
|
+ sta->uapsd_tids &= ~(1 << tid);
|
|
+ }
|
|
+ memcpy(sta->mac_addr, mac, ETH_ALEN);
|
|
+ bl_dbgfs_register_rc_stat(bl_hw, sta);
|
|
+
|
|
+ /* Ensure that we won't process PS change or channel switch ind*/
|
|
+ spin_lock_bh(&bl_hw->cmd_mgr.lock);
|
|
+ bl_txq_sta_init(bl_hw, sta, bl_txq_vif_get_status(bl_vif));
|
|
+ sta->valid = true;
|
|
+ spin_unlock_bh(&bl_hw->cmd_mgr.lock);
|
|
+
|
|
+ #define PRINT_STA_FLAG(f) \
|
|
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
|
|
+
|
|
+ netdev_info(dev, "Add TDLS sta %d (%pM) flags=%s%s%s%s%s%s%s",
|
|
+ sta->sta_idx, mac,
|
|
+ PRINT_STA_FLAG(AUTHORIZED),
|
|
+ PRINT_STA_FLAG(SHORT_PREAMBLE),
|
|
+ PRINT_STA_FLAG(WME),
|
|
+ PRINT_STA_FLAG(MFP),
|
|
+ PRINT_STA_FLAG(AUTHENTICATED),
|
|
+ PRINT_STA_FLAG(TDLS_PEER),
|
|
+ PRINT_STA_FLAG(ASSOCIATED));
|
|
+ #undef PRINT_STA_FLAG
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ error = -EBUSY;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ bl_hw->adding_sta = false;
|
|
+ } else {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))
|
|
+ bl_send_me_set_control_port_req(bl_hw,
|
|
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) != 0,
|
|
+ sta->sta_idx);
|
|
+
|
|
+ if (params->vlan) {
|
|
+ uint8_t vlan_idx;
|
|
+
|
|
+ vif = netdev_priv(params->vlan);
|
|
+ vlan_idx = vif->vif_index;
|
|
+
|
|
+ if (sta->vlan_idx != vlan_idx) {
|
|
+ struct bl_vif *old_vif;
|
|
+ old_vif = bl_hw->vif_table[sta->vlan_idx];
|
|
+ bl_txq_sta_switch_vif(sta, old_vif, vif);
|
|
+ sta->vlan_idx = vlan_idx;
|
|
+
|
|
+ if ((BL_VIF_TYPE(vif) == NL80211_IFTYPE_AP_VLAN) &&
|
|
+ (vif->use_4addr)) {
|
|
+ WARN((vif->ap_vlan.sta_4a),
|
|
+ "4A AP_VLAN interface with more than one sta");
|
|
+ vif->ap_vlan.sta_4a = sta;
|
|
+ }
|
|
+
|
|
+ if ((BL_VIF_TYPE(old_vif) == NL80211_IFTYPE_AP_VLAN) &&
|
|
+ (old_vif->use_4addr)) {
|
|
+ old_vif->ap_vlan.sta_4a = NULL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @start_ap: Start acting in AP mode defined by the parameters.
|
|
+ */
|
|
+static int bl_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|
+ struct cfg80211_ap_settings *settings)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct apm_start_cfm apm_start_cfm;
|
|
+ struct bl_dma_elem elem;
|
|
+ struct bl_sta *sta;
|
|
+ int error = 0;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* Forward the information to the LMAC */
|
|
+ if ((error = bl_send_apm_start_req(bl_hw, bl_vif, settings, &apm_start_cfm, &elem))) {
|
|
+ return error;
|
|
+ }
|
|
+
|
|
+ // Check the status
|
|
+ switch (apm_start_cfm.status)
|
|
+ {
|
|
+ case CO_OK:
|
|
+ {
|
|
+ u8 txq_status = 0;
|
|
+ bl_vif->ap.bcmc_index = apm_start_cfm.bcmc_idx;
|
|
+ bl_vif->ap.flags = 0;
|
|
+ sta = &bl_hw->sta_table[apm_start_cfm.bcmc_idx];
|
|
+ sta->valid = true;
|
|
+ sta->aid = 0;
|
|
+ sta->sta_idx = apm_start_cfm.bcmc_idx;
|
|
+ sta->ch_idx = apm_start_cfm.ch_idx;
|
|
+ sta->vif_idx = bl_vif->vif_index;
|
|
+ sta->qos = false;
|
|
+ sta->acm = 0;
|
|
+ sta->ps.active = false;
|
|
+ spin_lock_bh(&bl_hw->cmd_mgr.lock);
|
|
+ bl_chanctx_link(bl_vif, apm_start_cfm.ch_idx,
|
|
+ &settings->chandef);
|
|
+ if (bl_hw->cur_chanctx != apm_start_cfm.ch_idx) {
|
|
+ txq_status = BL_TXQ_STOP_CHAN;
|
|
+ }
|
|
+ bl_txq_vif_init(bl_hw, bl_vif, txq_status);
|
|
+ spin_unlock_bh(&bl_hw->cmd_mgr.lock);
|
|
+
|
|
+ netif_tx_start_all_queues(dev);
|
|
+ netif_carrier_on(dev);
|
|
+ error = 0;
|
|
+ break;
|
|
+ }
|
|
+ case CO_BUSY:
|
|
+ error = -EINPROGRESS;
|
|
+ break;
|
|
+ case CO_OP_IN_PROGRESS:
|
|
+ error = -EALREADY;
|
|
+ break;
|
|
+ default:
|
|
+ error = -EIO;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (error) {
|
|
+ netdev_info(dev, "Failed to start AP (%d)", error);
|
|
+ } else {
|
|
+ netdev_info(dev, "AP started: ch=%d, bcmc_idx=%d",
|
|
+ bl_vif->ch_index, bl_vif->ap.bcmc_index);
|
|
+ }
|
|
+
|
|
+ // Free the buffer used to build the beacon
|
|
+ kfree(elem.buf);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * @change_beacon: Change the beacon parameters for an access point mode
|
|
+ * interface. This should reject the call when AP mode wasn't started.
|
|
+ */
|
|
+static int bl_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
+ struct cfg80211_beacon_data *info)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *vif = netdev_priv(dev);
|
|
+ struct bl_bcn *bcn = &vif->ap.bcn;
|
|
+ struct bl_dma_elem elem;
|
|
+ u8 *buf;
|
|
+ int error = 0;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ // Build the beacon
|
|
+ buf = bl_build_bcn(bcn, info);
|
|
+ if (!buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ // Fill in the DMA structure
|
|
+ elem.buf = buf;
|
|
+ elem.len = bcn->len;
|
|
+
|
|
+ // Forward the information to the LMAC
|
|
+ error = bl_send_bcn_change(bl_hw, vif->vif_index, elem.buf,
|
|
+ bcn->len, bcn->head_len, bcn->tim_len, NULL);
|
|
+
|
|
+ kfree(elem.buf);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * * @stop_ap: Stop being an AP, including stopping beaconing.
|
|
+ */
|
|
+static int bl_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct bl_sta *sta;
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
|
+ struct station_del_parameters params;
|
|
+ params.mac = NULL;
|
|
+#endif
|
|
+
|
|
+ bl_send_apm_stop_req(bl_hw, bl_vif);
|
|
+ bl_chanctx_unlink(bl_vif);
|
|
+
|
|
+ /* delete any remaining STA*/
|
|
+ while (!list_empty(&bl_vif->ap.sta_list)) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
|
+ bl_cfg80211_del_station(wiphy, dev, ¶ms);
|
|
+#else
|
|
+ bl_cfg80211_del_station(wiphy, dev, NULL);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ /* delete BC/MC STA */
|
|
+ sta = &bl_hw->sta_table[bl_vif->ap.bcmc_index];
|
|
+ bl_txq_vif_deinit(bl_hw, bl_vif);
|
|
+ bl_del_bcn(&bl_vif->ap.bcn);
|
|
+ bl_del_csa(bl_vif);
|
|
+
|
|
+ netif_tx_stop_all_queues(dev);
|
|
+ netif_carrier_off(dev);
|
|
+
|
|
+ netdev_info(dev, "AP Stopped");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @set_monitor_channel: Set the monitor mode channel for the device. If other
|
|
+ * interfaces are active this callback should reject the configuration.
|
|
+ * If no interfaces are active or the device is down, the channel should
|
|
+ * be stored for when a monitor interface becomes active.
|
|
+ */
|
|
+static int bl_cfg80211_set_monitor_channel(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @probe_client: probe an associated client, must return a cookie that it
|
|
+ * later passes to cfg80211_probe_status().
|
|
+ */
|
|
+int bl_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
|
|
+ const u8 *peer, u64 *cookie)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @mgmt_frame_register: Notify driver that a management frame type was
|
|
+ * registered. Note that this callback may not sleep, and cannot run
|
|
+ * concurrently with itself.
|
|
+ */
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0))
|
|
+
|
|
+void bl_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
|
|
+ struct wireless_dev *wdev,
|
|
+ struct mgmt_frame_regs *upd)
|
|
+{
|
|
+}
|
|
+
|
|
+#else
|
|
+
|
|
+void bl_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
|
|
+ struct wireless_dev *wdev,
|
|
+ u16 frame_type, bool reg)
|
|
+{
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+/**
|
|
+ * @set_wiphy_params: Notify that wiphy parameters have changed;
|
|
+ * @changed bitfield (see &enum wiphy_params_flags) describes which values
|
|
+ * have changed. The actual parameter values are available in
|
|
+ * struct wiphy. If returning an error, no value should be changed.
|
|
+ */
|
|
+static int bl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * @set_tx_power: set the transmit power according to the parameters,
|
|
+ * the power passed is in mBm, to get dBm use MBM_TO_DBM(). The
|
|
+ * wdev may be %NULL if power was set for the wiphy, and will
|
|
+ * always be %NULL unless the driver supports per-vif TX power
|
|
+ * (as advertised by the nl80211 feature flag.)
|
|
+ */
|
|
+static int bl_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
+ enum nl80211_tx_power_setting type, int mbm)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *vif;
|
|
+ s8 pwr;
|
|
+ int res = 0;
|
|
+
|
|
+ if (type == NL80211_TX_POWER_AUTOMATIC) {
|
|
+ pwr = 0x7f;
|
|
+ } else {
|
|
+ pwr = MBM_TO_DBM(mbm);
|
|
+ }
|
|
+
|
|
+ if (wdev) {
|
|
+ vif = container_of(wdev, struct bl_vif, wdev);
|
|
+ res = bl_send_set_power(bl_hw, vif->vif_index, pwr, NULL);
|
|
+ } else {
|
|
+ list_for_each_entry(vif, &bl_hw->vifs, list) {
|
|
+ res = bl_send_set_power(bl_hw, vif->vif_index, pwr, NULL);
|
|
+ if (res)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static int bl_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev,
|
|
+ struct ieee80211_txq_params *params)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ u8 hw_queue, aifs, cwmin, cwmax;
|
|
+ u32 param;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ hw_queue = bl_ac2hwq[0][params->ac];
|
|
+
|
|
+ aifs = params->aifs;
|
|
+ cwmin = fls(params->cwmin);
|
|
+ cwmax = fls(params->cwmax);
|
|
+
|
|
+ /* Store queue information in general structure */
|
|
+ param = (u32) (aifs << 0);
|
|
+ param |= (u32) (cwmin << 4);
|
|
+ param |= (u32) (cwmax << 8);
|
|
+ param |= (u32) (params->txop) << 12;
|
|
+
|
|
+ /* Send the MM_SET_EDCA_REQ message to the FW */
|
|
+ return bl_send_set_edca(bl_hw, hw_queue, param, false, bl_vif->vif_index);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * @remain_on_channel: Request the driver to remain awake on the specified
|
|
+ * channel for the specified duration to complete an off-channel
|
|
+ * operation (e.g., public action frame exchange). When the driver is
|
|
+ * ready on the requested channel, it must indicate this with an event
|
|
+ * notification by calling cfg80211_ready_on_channel().
|
|
+ */
|
|
+static int
|
|
+bl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
+ struct ieee80211_channel *chan,
|
|
+ unsigned int duration, u64 *cookie)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(wdev->netdev);
|
|
+ struct bl_roc_elem *roc_elem;
|
|
+ int error;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* For debug purpose (use ftrace kernel option) */
|
|
+ trace_roc(bl_vif->vif_index, chan->center_freq, duration);
|
|
+
|
|
+ /* Check that no other RoC procedure has been launched */
|
|
+ if (bl_hw->roc_elem) {
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ /* Allocate a temporary RoC element */
|
|
+ roc_elem = kmalloc(sizeof(struct bl_roc_elem), GFP_KERNEL);
|
|
+
|
|
+ /* Verify that element has well been allocated */
|
|
+ if (!roc_elem) {
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* Initialize the RoC information element */
|
|
+ roc_elem->wdev = wdev;
|
|
+ roc_elem->chan = chan;
|
|
+ roc_elem->duration = duration;
|
|
+ roc_elem->mgmt_roc = false;
|
|
+ roc_elem->on_chan = false;
|
|
+
|
|
+ /* Forward the information to the FMAC */
|
|
+ error = bl_send_roc(bl_hw, bl_vif, chan, duration);
|
|
+
|
|
+ /* If no error, keep all the information for handling of end of procedure */
|
|
+ if (error == 0) {
|
|
+ bl_hw->roc_elem = roc_elem;
|
|
+
|
|
+ /* Set the cookie value */
|
|
+ *cookie = (u64)(bl_hw->roc_cookie_cnt);
|
|
+
|
|
+ /* Initialize the OFFCHAN TX queue to allow off-channel transmissions */
|
|
+ bl_txq_offchan_init(bl_vif);
|
|
+ } else {
|
|
+ /* Free the allocated element */
|
|
+ kfree(roc_elem);
|
|
+ }
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
|
|
+ * This allows the operation to be terminated prior to timeout based on
|
|
+ * the duration value.
|
|
+ */
|
|
+static int bl_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
|
+ struct wireless_dev *wdev,
|
|
+ u64 cookie)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(wdev->netdev);
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* For debug purpose (use ftrace kernel option) */
|
|
+ trace_cancel_roc(bl_vif->vif_index);
|
|
+
|
|
+ /* Check if a RoC procedure is pending */
|
|
+ if (!bl_hw->roc_elem) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Forward the information to the FMAC */
|
|
+ return bl_send_cancel_roc(bl_hw);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @dump_survey: get site survey information.
|
|
+ */
|
|
+static int bl_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *netdev,
|
|
+ int idx, struct survey_info *info)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct ieee80211_supported_band *sband;
|
|
+ struct bl_survey_info *bl_survey;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ if (idx >= ARRAY_SIZE(bl_hw->survey))
|
|
+ return -ENOENT;
|
|
+
|
|
+ bl_survey = &bl_hw->survey[idx];
|
|
+
|
|
+ // Check if provided index matches with a supported 2.4GHz channel
|
|
+ sband = wiphy->bands[NL80211_BAND_2GHZ];
|
|
+ if (sband && idx >= sband->n_channels) {
|
|
+ idx -= sband->n_channels;
|
|
+ sband = NULL;
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ // Fill the survey
|
|
+ info->channel = &sband->channels[idx];
|
|
+ info->filled = bl_survey->filled;
|
|
+
|
|
+ if (bl_survey->filled != 0) {
|
|
+ info->time = (u64)bl_survey->chan_time_ms;
|
|
+ info->time_busy = (u64)bl_survey->chan_time_busy_ms;
|
|
+ info->noise = bl_survey->noise_dbm;
|
|
+
|
|
+ // Set the survey report as not used
|
|
+ bl_survey->filled = 0;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @get_channel: Get the current operating channel for the virtual interface.
|
|
+ * For monitor interfaces, it should return %NULL unless there's a single
|
|
+ * current monitoring channel.
|
|
+ */
|
|
+static int bl_cfg80211_get_channel(struct wiphy *wiphy,
|
|
+ struct wireless_dev *wdev,
|
|
+ struct cfg80211_chan_def *chandef) {
|
|
+
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = container_of(wdev, struct bl_vif, wdev);
|
|
+ struct bl_chanctx *ctxt;
|
|
+
|
|
+ if (!bl_vif->up ||
|
|
+ !bl_chanctx_valid(bl_hw, bl_vif->ch_index)) {
|
|
+ return -ENODATA;
|
|
+ }
|
|
+
|
|
+ ctxt = &bl_hw->chanctx_table[bl_vif->ch_index];
|
|
+ *chandef = ctxt->chan_def;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @mgmt_tx: Transmit a management frame.
|
|
+ */
|
|
+static int bl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
+ struct cfg80211_mgmt_tx_params *params,
|
|
+ u64 *cookie)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(wdev->netdev);
|
|
+ struct bl_sta *bl_sta;
|
|
+ struct ieee80211_mgmt *mgmt = (void *)params->buf;
|
|
+ int error = 0;
|
|
+ bool ap = false;
|
|
+ bool offchan = false;
|
|
+
|
|
+ do
|
|
+ {
|
|
+ /* Check if provided VIF is an AP or a STA one */
|
|
+ switch (BL_VIF_TYPE(bl_vif)) {
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ bl_vif = bl_vif->ap_vlan.master;
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_P2P_GO:
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
+ ap = true;
|
|
+ break;
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Get STA on which management frame has to be sent */
|
|
+ bl_sta = bl_retrieve_sta(bl_hw, bl_vif, mgmt->da,
|
|
+ mgmt->frame_control, ap);
|
|
+
|
|
+ /* For debug purpose (use ftrace kernel option) */
|
|
+ trace_mgmt_tx((params->chan) ? params->chan->center_freq : 0,
|
|
+ bl_vif->vif_index,
|
|
+ (bl_sta) ? bl_sta->sta_idx : 0xFF,
|
|
+ mgmt);
|
|
+
|
|
+ /* If AP, STA have to exist */
|
|
+ if (!bl_sta) {
|
|
+ if (!ap) {
|
|
+ /* ROC is needed, check that channel parameters have been provided */
|
|
+ if (!params->chan) {
|
|
+ error = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Check that a RoC is already pending */
|
|
+ if (bl_hw->roc_elem) {
|
|
+ /* Get VIF used for current ROC */
|
|
+ struct bl_vif *bl_roc_vif = netdev_priv(bl_hw->roc_elem->wdev->netdev);
|
|
+
|
|
+ /* Check if RoC channel is the same than the required one */
|
|
+ if ((bl_hw->roc_elem->chan->center_freq != params->chan->center_freq)
|
|
+ || (bl_vif->vif_index != bl_roc_vif->vif_index)) {
|
|
+ error = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ u64 cookie;
|
|
+
|
|
+ /* Start a ROC procedure for 30ms */
|
|
+ error = bl_cfg80211_remain_on_channel(wiphy, wdev, params->chan,
|
|
+ 30, &cookie);
|
|
+
|
|
+ if (error) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Need to keep in mind that RoC has been launched internally in order to
|
|
+ * avoid to call the cfg80211 callback once expired
|
|
+ */
|
|
+ bl_hw->roc_elem->mgmt_roc = true;
|
|
+ }
|
|
+
|
|
+ offchan = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Push the management frame on the TX path */
|
|
+ error = bl_start_mgmt_xmit(bl_vif, bl_sta, params, offchan, cookie);
|
|
+ } while (0);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @update_ft_ies: Provide updated Fast BSS Transition information to the
|
|
+ * driver. If the SME is in the driver/firmware, this information can be
|
|
+ * used in building Authentication and Reassociation Request frames.
|
|
+ */
|
|
+static
|
|
+int bl_cfg80211_update_ft_ies(struct wiphy *wiphy,
|
|
+ struct net_device *dev,
|
|
+ struct cfg80211_update_ft_ies_params *ftie)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
|
|
+ */
|
|
+static
|
|
+int bl_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
|
|
+ struct net_device *dev,
|
|
+ int32_t rssi_thold, uint32_t rssi_hyst)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+
|
|
+ return bl_send_cfg_rssi_req(bl_hw, bl_vif->vif_index, rssi_thold, rssi_hyst);
|
|
+}
|
|
+
|
|
+/**
|
|
+ *
|
|
+ * @channel_switch: initiate channel-switch procedure (with CSA). Driver is
|
|
+ * responsible for veryfing if the switch is possible. Since this is
|
|
+ * inherently tricky driver may decide to disconnect an interface later
|
|
+ * with cfg80211_stop_iface(). This doesn't mean driver can accept
|
|
+ * everything. It should do it's best to verify requests and reject them
|
|
+ * as soon as possible.
|
|
+ */
|
|
+int bl_cfg80211_channel_switch(struct wiphy *wiphy,
|
|
+ struct net_device *dev,
|
|
+ struct cfg80211_csa_settings *params)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+ struct bl_vif *vif = netdev_priv(dev);
|
|
+ struct bl_dma_elem elem;
|
|
+ struct bl_bcn *bcn, *bcn_after;
|
|
+ struct bl_csa *csa;
|
|
+ u16 csa_oft[BCN_MAX_CSA_CPT];
|
|
+ u8 *buf;
|
|
+ int i, error = 0;
|
|
+
|
|
+
|
|
+ if (vif->ap.csa)
|
|
+ return -EBUSY;
|
|
+
|
|
+ if (params->n_counter_offsets_beacon > BCN_MAX_CSA_CPT)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Build the new beacon with CSA IE */
|
|
+ bcn = &vif->ap.bcn;
|
|
+ buf = bl_build_bcn(bcn, ¶ms->beacon_csa);
|
|
+ if (!buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memset(csa_oft, 0, sizeof(csa_oft));
|
|
+ for (i = 0; i < params->n_counter_offsets_beacon; i++)
|
|
+ {
|
|
+ csa_oft[i] = params->counter_offsets_beacon[i] + bcn->head_len +
|
|
+ bcn->tim_len;
|
|
+ }
|
|
+
|
|
+ /* If count is set to 0 (i.e anytime after this beacon) force it to 2 */
|
|
+ if (params->count == 0) {
|
|
+ params->count = 2;
|
|
+ for (i = 0; i < params->n_counter_offsets_beacon; i++)
|
|
+ {
|
|
+ buf[csa_oft[i]] = 2;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ elem.buf = buf;
|
|
+ elem.len = bcn->len;
|
|
+
|
|
+ /* Build the beacon to use after CSA. It will only be sent to fw once
|
|
+ CSA is over. */
|
|
+ csa = kzalloc(sizeof(struct bl_csa), GFP_KERNEL);
|
|
+ if (!csa) {
|
|
+ error = -ENOMEM;
|
|
+ goto end;
|
|
+ }
|
|
+
|
|
+
|
|
+ bcn_after = &csa->bcn;
|
|
+ buf = bl_build_bcn(bcn_after, ¶ms->beacon_after);
|
|
+ if (!buf) {
|
|
+ error = -ENOMEM;
|
|
+ bl_del_csa(vif);
|
|
+ goto end;
|
|
+ }
|
|
+
|
|
+ vif->ap.csa = csa;
|
|
+ csa->vif = vif;
|
|
+ csa->chandef = params->chandef;
|
|
+ csa->dma.buf = buf;
|
|
+ csa->dma.len = bcn_after->len;
|
|
+
|
|
+ /* Send new Beacon. FW will extract channel and count from the beacon */
|
|
+ error = bl_send_bcn_change(bl_hw, vif->vif_index, elem.buf,
|
|
+ bcn->len, bcn->head_len, bcn->tim_len, csa_oft);
|
|
+
|
|
+ if (error) {
|
|
+ bl_del_csa(vif);
|
|
+ goto end;
|
|
+ } else {
|
|
+ INIT_WORK(&csa->work, bl_csa_finish);
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0))
|
|
+ cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count, false);
|
|
+#else
|
|
+ cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ end:
|
|
+ kfree(elem.buf);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @change_bss: Modify parameters for a given BSS (mainly for AP mode).
|
|
+ */
|
|
+int bl_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,
|
|
+ struct bss_parameters *params)
|
|
+{
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ int res = -EOPNOTSUPP;
|
|
+
|
|
+ if (((BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_AP) ||
|
|
+ (BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_P2P_GO)) &&
|
|
+ (params->ap_isolate > -1)) {
|
|
+
|
|
+ if (params->ap_isolate)
|
|
+ bl_vif->ap.flags |= BL_AP_ISOLATE;
|
|
+ else
|
|
+ bl_vif->ap.flags &= ~BL_AP_ISOLATE;
|
|
+
|
|
+ res = 0;
|
|
+ }
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
+static struct cfg80211_ops bl_cfg80211_ops = {
|
|
+ .add_virtual_intf = bl_cfg80211_add_iface,
|
|
+ .del_virtual_intf = bl_cfg80211_del_iface,
|
|
+ .change_virtual_intf = bl_cfg80211_change_iface,
|
|
+ .scan = bl_cfg80211_scan,
|
|
+ .connect = bl_cfg80211_connect,
|
|
+ .disconnect = bl_cfg80211_disconnect,
|
|
+ .add_key = bl_cfg80211_add_key,
|
|
+ .get_key = bl_cfg80211_get_key,
|
|
+ .del_key = bl_cfg80211_del_key,
|
|
+ .set_default_key = bl_cfg80211_set_default_key,
|
|
+ .set_default_mgmt_key = bl_cfg80211_set_default_mgmt_key,
|
|
+ .add_station = bl_cfg80211_add_station,
|
|
+ .del_station = bl_cfg80211_del_station,
|
|
+ .change_station = bl_cfg80211_change_station,
|
|
+ .mgmt_tx = bl_cfg80211_mgmt_tx,
|
|
+ .start_ap = bl_cfg80211_start_ap,
|
|
+ .change_beacon = bl_cfg80211_change_beacon,
|
|
+ .stop_ap = bl_cfg80211_stop_ap,
|
|
+ .set_monitor_channel = bl_cfg80211_set_monitor_channel,
|
|
+ .probe_client = bl_cfg80211_probe_client,
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0))
|
|
+// .update_mgmt_frame_registrations = cfg80211_rtw_update_mgmt_frame_registrations,
|
|
+ .update_mgmt_frame_registrations = bl_cfg80211_update_mgmt_frame_registrations,
|
|
+#else
|
|
+// .mgmt_frame_register = cfg80211_rtw_mgmt_frame_register,
|
|
+ .mgmt_frame_register = bl_cfg80211_mgmt_frame_register,
|
|
+#endif
|
|
+ .set_wiphy_params = bl_cfg80211_set_wiphy_params,
|
|
+ .set_txq_params = bl_cfg80211_set_txq_params,
|
|
+ .set_tx_power = bl_cfg80211_set_tx_power,
|
|
+ .remain_on_channel = bl_cfg80211_remain_on_channel,
|
|
+ .cancel_remain_on_channel = bl_cfg80211_cancel_remain_on_channel,
|
|
+ .dump_survey = bl_cfg80211_dump_survey,
|
|
+ .get_channel = bl_cfg80211_get_channel,
|
|
+ .update_ft_ies = bl_cfg80211_update_ft_ies,
|
|
+ .set_cqm_rssi_config = bl_cfg80211_set_cqm_rssi_config,
|
|
+ .channel_switch = bl_cfg80211_channel_switch,
|
|
+ .change_bss = bl_cfg80211_change_bss,
|
|
+};
|
|
+
|
|
+
|
|
+/*********************************************************************
|
|
+ * Init/Exit functions
|
|
+ *********************************************************************/
|
|
+static void bl_wdev_unregister(struct bl_hw *bl_hw)
|
|
+{
|
|
+ struct bl_vif *bl_vif, *tmp;
|
|
+
|
|
+ rtnl_lock();
|
|
+ list_for_each_entry_safe(bl_vif, tmp, &bl_hw->vifs, list) {
|
|
+ bl_cfg80211_del_iface(bl_hw->wiphy, &bl_vif->wdev);
|
|
+ }
|
|
+ rtnl_unlock();
|
|
+}
|
|
+
|
|
+static void bl_set_vers(struct bl_hw *bl_hw)
|
|
+{
|
|
+ u32 vers = bl_hw->version_cfm.version_lmac;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ snprintf(bl_hw->wiphy->fw_version,
|
|
+ sizeof(bl_hw->wiphy->fw_version), "%d.%d.%d.%d",
|
|
+ (vers & (0xff << 24)) >> 24, (vers & (0xff << 16)) >> 16,
|
|
+ (vers & (0xff << 8)) >> 8, (vers & (0xff << 0)) >> 0);
|
|
+}
|
|
+
|
|
+static void bl_reg_notifier(struct wiphy *wiphy,
|
|
+ struct regulatory_request *request)
|
|
+{
|
|
+ struct bl_hw *bl_hw = wiphy_priv(wiphy);
|
|
+
|
|
+ // For now trust all initiator
|
|
+ bl_send_me_chan_config_req(bl_hw);
|
|
+}
|
|
+
|
|
+static int remap_pfn_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct mm_struct *mm = current->mm;
|
|
+
|
|
+ printk("client: %s (%d)\n", current->comm, current->pid);
|
|
+ printk("code section: [0x%lx 0x%lx]\n", mm->start_code, mm->end_code);
|
|
+ printk("data section: [0x%lx 0x%lx]\n", mm->start_data, mm->end_data);
|
|
+ printk("brk section: s: 0x%lx, c: 0x%lx\n", mm->start_brk, mm->brk);
|
|
+ printk("mmap section: s: 0x%lx\n", mm->mmap_base);
|
|
+ printk("stack section: s: 0x%lx\n", mm->start_stack);
|
|
+ printk("arg section: [0x%lx 0x%lx]\n", mm->arg_start, mm->arg_end);
|
|
+ printk("env section: [0x%lx 0x%lx]\n", mm->env_start, mm->env_end);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int remap_pfn_mmap(struct file *file, struct vm_area_struct *vma)
|
|
+{
|
|
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
|
+ unsigned long pfn_start = (virt_to_phys(kbuff) >> PAGE_SHIFT) + vma->vm_pgoff;
|
|
+ unsigned long virt_start = (unsigned long)kbuff + offset;
|
|
+ unsigned long size = vma->vm_end - vma->vm_start;
|
|
+ int ret = 0;
|
|
+
|
|
+ printk("phy: 0x%lx, offset: 0x%lx, size: 0x%lx\n", pfn_start << PAGE_SHIFT, offset, size);
|
|
+
|
|
+ ret = remap_pfn_range(vma, vma->vm_start, pfn_start, size, vma->vm_page_prot);
|
|
+ if (ret)
|
|
+ printk("%s: remap_pfn_range failed at [0x%lx 0x%lx]\n",
|
|
+ __func__, vma->vm_start, vma->vm_end);
|
|
+ else
|
|
+ printk("%s: map 0x%lx to 0x%lx, size: 0x%lx\n", __func__, virt_start,
|
|
+ vma->vm_start, size);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct file_operations remap_pfn_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = remap_pfn_open,
|
|
+ .mmap = remap_pfn_mmap,
|
|
+};
|
|
+
|
|
+static struct miscdevice remap_pfn_misc = {
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .name = "bl_log",
|
|
+ .fops = &remap_pfn_fops,
|
|
+};
|
|
+
|
|
+
|
|
+int bl_logbuf_create(struct bl_hw *bl_hw)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ kbuff = kzalloc(LOG_BUF_SIZE, GFP_KERNEL);
|
|
+ bl_hw->log_buff = kbuff;
|
|
+
|
|
+ device_create_file(bl_hw->dev, &dev_attr_filter_severity);
|
|
+ device_create_file(bl_hw->dev, &dev_attr_filter_module);
|
|
+
|
|
+ ret = misc_register(&remap_pfn_misc);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void bl_logbuf_destroy(struct bl_hw *bl_hw)
|
|
+{
|
|
+ kfree(kbuff);
|
|
+
|
|
+ device_remove_file(bl_hw->dev, &dev_attr_filter_severity);
|
|
+ device_remove_file(bl_hw->dev, &dev_attr_filter_module);
|
|
+
|
|
+ misc_deregister(&remap_pfn_misc);
|
|
+}
|
|
+
|
|
+int bl_cfg80211_init(struct bl_plat *bl_plat, void **platform_data)
|
|
+{
|
|
+ struct bl_hw *bl_hw;
|
|
+ struct bl_conf_file init_conf;
|
|
+ int ret = 0;
|
|
+ struct wiphy *wiphy;
|
|
+ struct wireless_dev *wdev;
|
|
+ int i;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ /* create a new wiphy for use with cfg80211 */
|
|
+ wiphy = wiphy_new(&bl_cfg80211_ops, sizeof(struct bl_hw));
|
|
+
|
|
+ if (!wiphy) {
|
|
+ dev_err(bl_platform_get_dev(bl_plat), "Failed to create new wiphy\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto err_out;
|
|
+ }
|
|
+
|
|
+ bl_hw = wiphy_priv(wiphy);
|
|
+ bl_hw->wiphy = wiphy;
|
|
+ bl_hw->plat = bl_plat;
|
|
+ bl_hw->dev = bl_platform_get_dev(bl_plat);
|
|
+ bl_hw->mod_params = &bl_mod_params;
|
|
+
|
|
+ /* set device pointer for wiphy */
|
|
+ set_wiphy_dev(wiphy, bl_hw->dev);
|
|
+
|
|
+ /* Create cache to allocate sw_txhdr */
|
|
+ bl_hw->sw_txhdr_cache = kmem_cache_create("bl_sw_txhdr_cache",
|
|
+ sizeof(struct bl_sw_txhdr),
|
|
+ 0, 0, NULL);
|
|
+ if (!bl_hw->sw_txhdr_cache) {
|
|
+ wiphy_err(wiphy, "Cannot allocate cache for sw TX header\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto err_cache;
|
|
+ }
|
|
+
|
|
+ bl_hw->agg_reodr_pkt_cache= kmem_cache_create("bl_agg_reodr_pkt_cache",
|
|
+ sizeof(struct bl_agg_reord_pkt),
|
|
+ 0, 0, NULL);
|
|
+ if (!bl_hw->agg_reodr_pkt_cache) {
|
|
+ wiphy_err(wiphy, "Cannot allocate cache for agg reorder packet\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto err_reodr;
|
|
+ }
|
|
+
|
|
+ bl_hw->mpa_rx_data.buf = kzalloc(BL_RX_DATA_BUF_SIZE_16K, GFP_KERNEL);
|
|
+ if(!bl_hw->mpa_rx_data.buf){
|
|
+ BL_DBG("allocate rx buf fail \n");
|
|
+ ret = -ENOMEM;
|
|
+ goto err_config;
|
|
+ }
|
|
+
|
|
+ bl_hw->mpa_tx_data.buf = kzalloc(BL_TX_DATA_BUF_SIZE_16K, GFP_KERNEL);
|
|
+ if(!bl_hw->mpa_tx_data.buf){
|
|
+ BL_DBG("allocate tx buf fail \n");
|
|
+ ret = -ENOMEM;
|
|
+ goto err_alloc;
|
|
+ }
|
|
+
|
|
+ if ((ret = bl_parse_configfile(bl_hw, BL_CONFIG_FW_NAME, &init_conf))) {
|
|
+ wiphy_err(wiphy, "bl_parse_configfile failed\n");
|
|
+ goto err_platon;
|
|
+ }
|
|
+
|
|
+ bl_hw->sec_phy_chan.band = NL80211_BAND_5GHZ;
|
|
+ bl_hw->sec_phy_chan.type = PHY_CHNL_BW_20;
|
|
+ bl_hw->sec_phy_chan.prim20_freq = 5500;
|
|
+ bl_hw->sec_phy_chan.center_freq1 = 5500;
|
|
+ bl_hw->sec_phy_chan.center_freq2 = 0;
|
|
+ bl_hw->vif_started = 0;
|
|
+ bl_hw->adding_sta = false;
|
|
+
|
|
+ bl_hw->preq_ie.buf = NULL;
|
|
+
|
|
+ for (i = 0; i < NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX; i++)
|
|
+ bl_hw->avail_idx_map |= BIT(i);
|
|
+
|
|
+ bl_hwq_init(bl_hw);
|
|
+
|
|
+ for (i = 0; i < NX_NB_TXQ; i++) {
|
|
+ bl_hw->txq[i].idx = TXQ_INACTIVE;
|
|
+ }
|
|
+
|
|
+ /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
|
|
+ bl_hw->roc_elem = NULL;
|
|
+ /* Cookie can not be 0 */
|
|
+ bl_hw->roc_cookie_cnt = 1;
|
|
+
|
|
+ memcpy(wiphy->perm_addr, init_conf.mac_addr, ETH_ALEN);
|
|
+ wiphy->mgmt_stypes = bl_default_mgmt_stypes;
|
|
+ wiphy->bands[NL80211_BAND_2GHZ] = &bl_band_2GHz;
|
|
+ wiphy->interface_modes =
|
|
+ BIT(NL80211_IFTYPE_STATION) |
|
|
+ BIT(NL80211_IFTYPE_AP) |
|
|
+ BIT(NL80211_IFTYPE_AP_VLAN) |
|
|
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
+ BIT(NL80211_IFTYPE_P2P_GO);
|
|
+ wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
|
|
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH |
|
|
+ WIPHY_FLAG_4ADDR_STATION |
|
|
+ WIPHY_FLAG_4ADDR_AP;
|
|
+
|
|
+ wiphy->max_num_csa_counters = BCN_MAX_CSA_CPT;
|
|
+
|
|
+ wiphy->max_remain_on_channel_duration = bl_hw->mod_params->roc_dur_max;
|
|
+
|
|
+ wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN |
|
|
+ NL80211_FEATURE_SK_TX_STATUS |
|
|
+ NL80211_FEATURE_VIF_TXPOWER;
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
|
+ wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
|
|
+#endif
|
|
+
|
|
+ wiphy->iface_combinations = bl_combinations;
|
|
+ /* -1 not to include combination with radar detection, will be re-added in
|
|
+ bl_handle_dynparams if supported */
|
|
+ wiphy->n_iface_combinations = ARRAY_SIZE(bl_combinations) - 1;
|
|
+ wiphy->reg_notifier = bl_reg_notifier;
|
|
+
|
|
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
|
|
+
|
|
+ wiphy->cipher_suites = cipher_suites;
|
|
+ wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - NB_RESERVED_CIPHER;
|
|
+
|
|
+ bl_hw->ext_capa[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
|
|
+ bl_hw->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
|
|
+
|
|
+ wiphy->extended_capabilities = bl_hw->ext_capa;
|
|
+ wiphy->extended_capabilities_mask = bl_hw->ext_capa;
|
|
+ wiphy->extended_capabilities_len = ARRAY_SIZE(bl_hw->ext_capa);
|
|
+ bl_hw->workqueue = alloc_workqueue("BL_WORK_QUEUE", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
|
|
+ if (!bl_hw->workqueue) {
|
|
+ printk("creat workqueue failed\n");
|
|
+ goto err_platon;
|
|
+ }
|
|
+ INIT_WORK(&bl_hw->main_work, main_wq_hdlr);
|
|
+
|
|
+ INIT_LIST_HEAD(&bl_hw->vifs);
|
|
+
|
|
+ mutex_init(&bl_hw->dbginfo.mutex);
|
|
+ spin_lock_init(&bl_hw->tx_lock);
|
|
+ spin_lock_init(&bl_hw->main_proc_lock);
|
|
+ spin_lock_init(&bl_hw->int_lock);
|
|
+ spin_lock_init(&bl_hw->cmd_lock);
|
|
+ spin_lock_init(&bl_hw->resend_lock);
|
|
+ spin_lock_init(&bl_hw->txq_lock);
|
|
+ bl_hw->bl_processing = false;
|
|
+ bl_hw->more_task_flag = false;
|
|
+ bl_hw->cmd_sent = false;
|
|
+ bl_hw->resend = false;
|
|
+ bl_hw->recovery_flag = false;
|
|
+ bl_hw->msg_idx = 1;
|
|
+
|
|
+ for(i = 0; i < NX_REMOTE_STA_MAX*NX_NB_TID_PER_STA; i++)
|
|
+ INIT_LIST_HEAD(&bl_hw->reorder_list[i]);
|
|
+
|
|
+ if ((ret = bl_platform_on(bl_hw)))
|
|
+ goto err_workqueue;
|
|
+
|
|
+ *platform_data = bl_hw;
|
|
+ sdio_set_drvdata(bl_hw->plat->func, bl_hw);
|
|
+
|
|
+ bl_logbuf_create(bl_hw);
|
|
+ //TODO add magic char 'L' to header file
|
|
+ BL_DBG("Notify FW to start...\n");
|
|
+ bl_write_reg(bl_hw, 0x60, 'L');
|
|
+
|
|
+ /* Reset FW */
|
|
+ if ((ret = bl_send_reset(bl_hw)))
|
|
+ goto err_lmac_reqs;
|
|
+
|
|
+ if ((ret = bl_send_version_req(bl_hw, &bl_hw->version_cfm)))
|
|
+ goto err_lmac_reqs;
|
|
+ bl_set_vers(bl_hw);
|
|
+
|
|
+ if ((ret = bl_handle_dynparams(bl_hw, bl_hw->wiphy)))
|
|
+ goto err_lmac_reqs;
|
|
+
|
|
+ /* Set parameters to firmware */
|
|
+ bl_send_me_config_req(bl_hw);
|
|
+
|
|
+ if ((ret = wiphy_register(wiphy))) {
|
|
+ wiphy_err(wiphy, "Could not register wiphy device\n");
|
|
+ goto err_register_wiphy;
|
|
+ }
|
|
+
|
|
+
|
|
+ /* Set channel parameters to firmware (must be done after WiPHY registration) */
|
|
+ bl_send_me_chan_config_req(bl_hw);
|
|
+
|
|
+ *platform_data = bl_hw;
|
|
+
|
|
+ //bl_logbuf_create(bl_hw);
|
|
+
|
|
+ bl_dbgfs_register(bl_hw, "bl");
|
|
+
|
|
+
|
|
+ rtnl_lock();
|
|
+
|
|
+ /* Add an initial station interface */
|
|
+ wdev = bl_interface_add(bl_hw, "wlan%d", NL80211_IFTYPE_STATION, NULL);
|
|
+
|
|
+ rtnl_unlock();
|
|
+
|
|
+ if (!wdev) {
|
|
+ wiphy_err(wiphy, "Failed to instantiate a network device\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto err_add_interface;
|
|
+ }
|
|
+
|
|
+ wiphy_info(wiphy, "New interface create %s", wdev->netdev->name);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_add_interface:
|
|
+ wiphy_unregister(bl_hw->wiphy);
|
|
+err_register_wiphy:
|
|
+err_lmac_reqs:
|
|
+ bl_platform_off(bl_hw);
|
|
+err_workqueue:
|
|
+ if(bl_hw->workqueue)
|
|
+ destroy_workqueue(bl_hw->workqueue);
|
|
+err_platon:
|
|
+ kfree(bl_hw->mpa_tx_data.buf);
|
|
+err_alloc:
|
|
+ kfree(bl_hw->mpa_rx_data.buf);
|
|
+err_config:
|
|
+ kmem_cache_destroy(bl_hw->agg_reodr_pkt_cache);
|
|
+err_reodr:
|
|
+ kmem_cache_destroy(bl_hw->sw_txhdr_cache);
|
|
+err_cache:
|
|
+ wiphy_free(wiphy);
|
|
+err_out:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void bl_cfg80211_deinit(struct bl_hw *bl_hw)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_logbuf_destroy(bl_hw);
|
|
+ kfree(bl_hw->mpa_rx_data.buf);
|
|
+ bl_hw->mpa_rx_data.buf = NULL;
|
|
+ kfree(bl_hw->mpa_tx_data.buf);
|
|
+ bl_hw->mpa_tx_data.buf = NULL;
|
|
+
|
|
+ bl_dbgfs_unregister(bl_hw);
|
|
+
|
|
+ bl_wdev_unregister(bl_hw);
|
|
+ wiphy_unregister(bl_hw->wiphy);
|
|
+
|
|
+ msleep(5000);
|
|
+
|
|
+ destroy_workqueue(bl_hw->workqueue);
|
|
+
|
|
+ bl_platform_off(bl_hw);
|
|
+ kmem_cache_destroy(bl_hw->sw_txhdr_cache);
|
|
+ kmem_cache_destroy(bl_hw->agg_reodr_pkt_cache);
|
|
+ wiphy_free(bl_hw->wiphy);
|
|
+}
|
|
+
|
|
+static int __init bl_mod_init(void)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+ bl_print_version();
|
|
+
|
|
+ return bl_platform_register_drv();
|
|
+}
|
|
+
|
|
+static void __exit bl_mod_exit(void)
|
|
+{
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ bl_platform_unregister_drv();
|
|
+}
|
|
+
|
|
+module_init(bl_mod_init);
|
|
+module_exit(bl_mod_exit);
|
|
+
|
|
+MODULE_FIRMWARE(BL_CONFIG_FW_NAME);
|
|
+
|
|
+MODULE_DESCRIPTION(RW_DRV_DESCRIPTION);
|
|
+MODULE_VERSION(BL_VERS_MOD);
|
|
+MODULE_AUTHOR(RW_DRV_COPYRIGHT " " RW_DRV_AUTHOR);
|
|
+MODULE_LICENSE("GPL");
|
|
diff -Naur /dev/null/bl_main.h b/drivers/net/wireless/hflps170/fullmac/bl_main.h
|
|
--- /dev/null/bl_main.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/fullmac/bl_main.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,19 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_main.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _BL_MAIN_H_
|
|
+#define _BL_MAIN_H_
|
|
+
|
|
+#include "bl_defs.h"
|
|
+
|
|
+int bl_cfg80211_init(struct bl_plat *bl_plat, void **platform_data);
|
|
+void bl_cfg80211_deinit(struct bl_hw *bl_hw);
|
|
+
|
|
+#endif /* _BL_MAIN_H_ */
|
|
diff -Naur /dev/null/bl_rx.c b/drivers/net/wireless/hflps170/fullmac/bl_rx.c
|
|
--- /dev/null/bl_rx.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/fullmac/bl_rx.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,568 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_rx.c
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#include <linux/version.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/ieee80211.h>
|
|
+#include <linux/etherdevice.h>
|
|
+
|
|
+#include "bl_defs.h"
|
|
+#include "bl_rx.h"
|
|
+#include "bl_tx.h"
|
|
+#include "bl_events.h"
|
|
+#include "bl_compat.h"
|
|
+
|
|
+const u8 legrates_lut[] = {
|
|
+ 0, /* 0 */
|
|
+ 1, /* 1 */
|
|
+ 2, /* 2 */
|
|
+ 3, /* 3 */
|
|
+ -1, /* 4 */
|
|
+ -1, /* 5 */
|
|
+ -1, /* 6 */
|
|
+ -1, /* 7 */
|
|
+ 10, /* 8 */
|
|
+ 8, /* 9 */
|
|
+ 6, /* 10 */
|
|
+ 4, /* 11 */
|
|
+ 11, /* 12 */
|
|
+ 9, /* 13 */
|
|
+ 7, /* 14 */
|
|
+ 5 /* 15 */
|
|
+};
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_rx_statistic - save some statistics about received frames
|
|
+ *
|
|
+ * @bl_hw: main driver data.
|
|
+ * @hw_rxhdr: Rx Hardware descriptor of the received frame.
|
|
+ * @sta: STA that sent the frame.
|
|
+ */
|
|
+static void bl_rx_statistic(struct bl_hw *bl_hw, struct hw_rxhdr *hw_rxhdr,
|
|
+ struct bl_sta *sta)
|
|
+{
|
|
+#ifdef CONFIG_BL_DEBUGFS
|
|
+ struct bl_stats *stats = &bl_hw->stats;
|
|
+ struct bl_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
|
|
+ struct hw_vect *hwvect = &hw_rxhdr->hwvect;
|
|
+ int mpdu, ampdu, mpdu_prev, rate_idx;
|
|
+
|
|
+ /* save complete hwvect */
|
|
+ sta->stats.last_rx = *hwvect;
|
|
+
|
|
+ /* update ampdu rx stats */
|
|
+ mpdu = hwvect->mpdu_cnt;
|
|
+ ampdu = hwvect->ampdu_cnt;
|
|
+ mpdu_prev = stats->ampdus_rx_map[ampdu];
|
|
+
|
|
+ if (mpdu_prev < mpdu ) {
|
|
+ stats->ampdus_rx_miss += mpdu - mpdu_prev - 1;
|
|
+ } else {
|
|
+ stats->ampdus_rx[mpdu_prev]++;
|
|
+ }
|
|
+ stats->ampdus_rx_map[ampdu] = mpdu;
|
|
+
|
|
+ /* update rx rate statistic */
|
|
+ if (hwvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
|
|
+ int mcs = hwvect->mcs;
|
|
+ int bw = hwvect->ch_bw;
|
|
+ int sgi = hwvect->short_gi;
|
|
+ int nss;
|
|
+ if (hwvect->format_mod <= FORMATMOD_HT_GF) {
|
|
+ nss = hwvect->stbc ? hwvect->stbc : hwvect->n_sts;
|
|
+ rate_idx = 16 + nss * 32 + mcs * 4 + bw * 2 + sgi;
|
|
+ } else {
|
|
+ nss = hwvect->stbc ? hwvect->stbc/2 : hwvect->n_sts;
|
|
+ rate_idx = 144 + nss * 80 + mcs * 8 + bw * 2 + sgi;
|
|
+ }
|
|
+ } else {
|
|
+ int idx = legrates_lut[hwvect->leg_rate];
|
|
+ if (idx < 4) {
|
|
+ rate_idx = idx * 2 + hwvect->pre_type;
|
|
+ } else {
|
|
+ rate_idx = 8 + idx - 4;
|
|
+ }
|
|
+ }
|
|
+ if (rate_idx < rate_stats->size) {
|
|
+ rate_stats->table[rate_idx]++;
|
|
+ rate_stats->cpt++;
|
|
+ } else {
|
|
+ pr_err("Invalid index conversion => %d / %d\n", rate_idx, rate_stats->size);
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+ static const u16 bl_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
|
|
+
|
|
+u16 bl_recal_priority_netdevidx(struct bl_vif *bl_vif, struct sk_buff *skb)
|
|
+{
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+ struct wireless_dev *wdev = &bl_vif->wdev;
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct bl_txq *txq;
|
|
+ u16 netdev_queue;
|
|
+ bool tdls_mgmgt_frame = false;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ switch (wdev->iftype) {
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ {
|
|
+ struct ethhdr *eth;
|
|
+ eth = (struct ethhdr *)skb->data;
|
|
+ if (eth->h_proto == cpu_to_be16(ETH_P_TDLS)) {
|
|
+ tdls_mgmgt_frame = true;
|
|
+ }
|
|
+ sta = bl_vif->sta.ap;
|
|
+ break;
|
|
+ }
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ if (bl_vif->ap_vlan.sta_4a) {
|
|
+ sta = bl_vif->ap_vlan.sta_4a;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* AP_VLAN interface is not used for a 4A STA,
|
|
+ fallback searching sta amongs all AP's clients */
|
|
+ bl_vif = bl_vif->ap_vlan.master;
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ {
|
|
+ struct bl_sta *cur;
|
|
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
|
|
+
|
|
+ if (is_multicast_ether_addr(eth->h_dest)) {
|
|
+ sta = &bl_hw->sta_table[bl_vif->ap.bcmc_index];
|
|
+ } else {
|
|
+ list_for_each_entry(cur, &bl_vif->ap.sta_list, list) {
|
|
+ if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
|
|
+ sta = cur;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (sta && sta->qos)
|
|
+ {
|
|
+ /* use the data classifier to determine what 802.1d tag the data frame has */
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
+ skb->priority = cfg80211_classify8021d(skb, NULL) & IEEE80211_QOS_CTL_TAG1D_MASK;
|
|
+#else
|
|
+ skb->priority = cfg80211_classify8021d(skb) & IEEE80211_QOS_CTL_TAG1D_MASK;
|
|
+#endif
|
|
+ if (sta->acm)
|
|
+ bl_downgrade_ac(sta, skb);
|
|
+
|
|
+ txq = bl_txq_sta_get(sta, skb->priority, NULL, bl_hw);
|
|
+ netdev_queue = txq->ndev_idx;
|
|
+ }
|
|
+ else if (sta)
|
|
+ {
|
|
+ skb->priority = 0xFF;
|
|
+ txq = bl_txq_sta_get(sta, 0, NULL, bl_hw);
|
|
+ netdev_queue = txq->ndev_idx;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* This packet will be dropped in xmit function, still need to select
|
|
+ an active queue for xmit to be called. As it most likely to happen
|
|
+ for AP interface, select BCMC queue
|
|
+ (TODO: select another queue if BCMC queue is stopped) */
|
|
+ skb->priority = 0xAA;
|
|
+ netdev_queue = NX_BCMC_TXQ_NDEV_IDX;
|
|
+ }
|
|
+
|
|
+ BUG_ON(netdev_queue >= NX_NB_NDEV_TXQ);
|
|
+
|
|
+ if(skb->priority < 8)
|
|
+ return bl_1d_to_wmm_queue[skb->priority];
|
|
+ else
|
|
+ return 0;
|
|
+
|
|
+// return netdev_queue;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_rx_data_skb - Process one data frame
|
|
+ *
|
|
+ * @bl_hw: main driver data
|
|
+ * @bl_vif: vif that received the buffer
|
|
+ * @skb: skb received
|
|
+ * @rxhdr: HW rx descriptor
|
|
+ * @return: true if buffer has been forwarded to upper layer
|
|
+ *
|
|
+ * If buffer is amsdu , it is first split into a list of skb.
|
|
+ * Then each skb may be:
|
|
+ * - forwarded to upper layer
|
|
+ * - resent on wireless interface
|
|
+ *
|
|
+ * When vif is a STA interface, every skb is only forwarded to upper layer.
|
|
+ * When vif is an AP interface, multicast skb are forwarded and resent, whereas
|
|
+ * skb for other BSS's STA are only resent.
|
|
+ */
|
|
+static bool bl_rx_data_skb(struct bl_hw *bl_hw, struct bl_vif *bl_vif,
|
|
+ struct sk_buff *skb, struct hw_rxhdr *rxhdr)
|
|
+{
|
|
+ struct sk_buff_head list;
|
|
+ struct sk_buff *rx_skb;
|
|
+ struct bl_sta *sta = NULL;
|
|
+ u16 netdev_queue;
|
|
+ bool amsdu = rxhdr->flags_is_amsdu;
|
|
+ bool resend = false, forward = true;
|
|
+
|
|
+ skb->dev = bl_vif->ndev;
|
|
+
|
|
+ __skb_queue_head_init(&list);
|
|
+
|
|
+ if (amsdu) {
|
|
+ int count;
|
|
+
|
|
+ ieee80211_amsdu_to_8023s(skb, &list, bl_vif->ndev->dev_addr,
|
|
+ BL_VIF_TYPE(bl_vif), 0, NULL, NULL);
|
|
+
|
|
+ count = skb_queue_len(&list);
|
|
+ if (count > ARRAY_SIZE(bl_hw->stats.amsdus_rx))
|
|
+ count = ARRAY_SIZE(bl_hw->stats.amsdus_rx);
|
|
+ bl_hw->stats.amsdus_rx[count - 1]++;
|
|
+ } else {
|
|
+ bl_hw->stats.amsdus_rx[0]++;
|
|
+ __skb_queue_head(&list, skb);
|
|
+ }
|
|
+
|
|
+ if (((BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_AP) ||
|
|
+ (BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_AP_VLAN)) &&
|
|
+ !(bl_vif->ap.flags & BL_AP_ISOLATE)) {
|
|
+ const struct ethhdr *eth;
|
|
+ rx_skb = skb_peek(&list);
|
|
+ if(rx_skb == NULL)
|
|
+ printk("rx_skb=NULL!!!\n");
|
|
+ /*go through mac_header, after mac_header is eth header*/
|
|
+ skb_reset_mac_header(rx_skb);
|
|
+ eth = eth_hdr(rx_skb);
|
|
+
|
|
+ if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
|
|
+ /* broadcast pkt need to be forwared to upper layer and resent
|
|
+ on wireless interface */
|
|
+ BL_DBG("this is a broadcast packet, forwared true and resend true!\n");
|
|
+ resend = true;
|
|
+ } else {
|
|
+ /* unicast pkt for STA inside the BSS, no need to forward to upper
|
|
+ layer simply resend on wireless interface */
|
|
+ if (rxhdr->flags_dst_idx != 0xFF)
|
|
+ {
|
|
+ BL_DBG("unicast pkt for STA inside the BSS, and forward false and resend true\n");
|
|
+ sta = &bl_hw->sta_table[rxhdr->flags_dst_idx];
|
|
+ if (sta->valid && (sta->vlan_idx == bl_vif->vif_index))
|
|
+ {
|
|
+ forward = false;
|
|
+ resend = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ while (!skb_queue_empty(&list)) {
|
|
+ rx_skb = __skb_dequeue(&list);
|
|
+ BL_DBG("resend rx_skb->priority=%d, ndev_idx=%d\n", rx_skb->priority, skb_get_queue_mapping(rx_skb));
|
|
+
|
|
+ resend = false;
|
|
+ /* resend pkt on wireless interface */
|
|
+ if (resend) {
|
|
+ struct sk_buff *skb_copy;
|
|
+ /* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
|
|
+ skb_copy = skb_copy_expand(rx_skb, sizeof(struct bl_txhdr) + sizeof(struct txdesc_api) + sizeof(struct sdio_hdr) +
|
|
+ BL_SWTXHDR_ALIGN_SZ, 0, GFP_ATOMIC);
|
|
+ if (skb_copy) {
|
|
+ int res;
|
|
+ skb_copy->protocol = htons(ETH_P_802_3);
|
|
+ skb_reset_network_header(skb_copy);
|
|
+ skb_reset_mac_header(skb_copy);
|
|
+
|
|
+ BL_DBG("resend skb_copy=%p, skb len=0x%02x, skb->priority=%d, ndev_idx=%d\n", skb_copy, skb_copy->len, skb_copy->priority, skb_get_queue_mapping(skb_copy));
|
|
+
|
|
+ bl_vif->is_resending = true;
|
|
+
|
|
+ netdev_queue = bl_recal_priority_netdevidx(bl_vif, skb_copy);
|
|
+
|
|
+ skb_set_queue_mapping(skb_copy, netdev_queue);
|
|
+
|
|
+ BL_DBG("after recal: skb->priority=%d, netdev_queue=%d\n", skb_copy->priority, netdev_queue);
|
|
+
|
|
+ bl_hw->resend = true;
|
|
+ //res = dev_queue_xmit(skb_copy);
|
|
+ res = bl_requeue_multicast_skb(skb_copy, bl_vif);
|
|
+ bl_vif->is_resending = false;
|
|
+ /* note: buffer is always consummed by dev_queue_xmit */
|
|
+ if (res == NET_XMIT_DROP) {
|
|
+ bl_vif->net_stats.rx_dropped++;
|
|
+ bl_vif->net_stats.tx_dropped++;
|
|
+ } else if (res != NET_XMIT_SUCCESS) {
|
|
+ netdev_err(bl_vif->ndev,
|
|
+ "Failed to re-send buffer to driver (res=%d)",
|
|
+ res);
|
|
+ bl_vif->net_stats.tx_errors++;
|
|
+ }
|
|
+ } else {
|
|
+ netdev_err(bl_vif->ndev, "Failed to copy skb");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* forward pkt to upper layer */
|
|
+ if (forward) {
|
|
+ rx_skb->protocol = eth_type_trans(rx_skb, bl_vif->ndev);
|
|
+ memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
|
|
+
|
|
+ netif_receive_skb(rx_skb);
|
|
+
|
|
+ /* Update statistics */
|
|
+ bl_vif->net_stats.rx_packets++;
|
|
+ bl_vif->net_stats.rx_bytes += rx_skb->len;
|
|
+ BL_DBG("forward this packet to network layer success\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return forward;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_rx_mgmt - Process one 802.11 management frame
|
|
+ *
|
|
+ * @bl_hw: main driver data
|
|
+ * @bl_vif: vif that received the buffer
|
|
+ * @skb: skb received
|
|
+ * @rxhdr: HW rx descriptor
|
|
+ *
|
|
+ * Process the management frame and free the corresponding skb
|
|
+ */
|
|
+static void bl_rx_mgmt(struct bl_hw *bl_hw, struct bl_vif *bl_vif,
|
|
+ struct sk_buff *skb, struct hw_rxhdr *hw_rxhdr)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
|
|
+
|
|
+ trace_mgmt_rx(hw_rxhdr->phy_prim20_freq, hw_rxhdr->flags_vif_idx,
|
|
+ hw_rxhdr->flags_sta_idx, mgmt);
|
|
+
|
|
+ if (ieee80211_is_beacon(mgmt->frame_control)) {
|
|
+ cfg80211_report_obss_beacon(bl_hw->wiphy, skb->data, skb->len,
|
|
+ hw_rxhdr->phy_prim20_freq,
|
|
+ hw_rxhdr->hwvect.rssi1);
|
|
+ } else if ((ieee80211_is_deauth(mgmt->frame_control) ||
|
|
+ ieee80211_is_disassoc(mgmt->frame_control)) &&
|
|
+ (mgmt->u.deauth.reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
|
|
+ mgmt->u.deauth.reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)) {
|
|
+ cfg80211_rx_unprot_mlme_mgmt(bl_vif->ndev, skb->data, skb->len);
|
|
+ } else if ((BL_VIF_TYPE(bl_vif) == NL80211_IFTYPE_STATION) &&
|
|
+ (ieee80211_is_action(mgmt->frame_control) &&
|
|
+ (mgmt->u.action.category == 6))) {
|
|
+ struct cfg80211_ft_event_params ft_event;
|
|
+ ft_event.target_ap = (uint8_t *)&mgmt->u.action + ETH_ALEN + 2;
|
|
+ ft_event.ies = (uint8_t *)&mgmt->u.action + ETH_ALEN*2 + 2;
|
|
+ ft_event.ies_len = hw_rxhdr->hwvect.len - (ft_event.ies - (uint8_t *)mgmt);
|
|
+ ft_event.ric_ies = NULL;
|
|
+ ft_event.ric_ies_len = 0;
|
|
+ cfg80211_ft_event(bl_vif->ndev, &ft_event);
|
|
+ } else {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
+ cfg80211_rx_mgmt(&bl_vif->wdev, hw_rxhdr->phy_prim20_freq,
|
|
+ hw_rxhdr->hwvect.rssi1, skb->data, skb->len, 0);
|
|
+#else
|
|
+ cfg80211_rx_mgmt(&bl_vif->wdev, hw_rxhdr->phy_prim20_freq,
|
|
+ hw_rxhdr->hwvect.rssi1, skb->data, skb->len, 0, GFP_ATOMIC);
|
|
+#endif
|
|
+ }
|
|
+ dev_kfree_skb(skb);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_rx_get_vif - Return pointer to the destination vif
|
|
+ *
|
|
+ * @bl_hw: main driver data
|
|
+ * @vif_idx: vif index present in rx descriptor
|
|
+ *
|
|
+ * Select the vif that should receive this frame returns NULL if the destination
|
|
+ * vif is not active.
|
|
+ * If no vif is specified, this is probably a mgmt broadcast frame, so select
|
|
+ * the first active interface
|
|
+ */
|
|
+static inline
|
|
+struct bl_vif *bl_rx_get_vif(struct bl_hw * bl_hw, int vif_idx)
|
|
+{
|
|
+ struct bl_vif *bl_vif = NULL;
|
|
+
|
|
+ if (vif_idx == 0xFF) {
|
|
+ list_for_each_entry(bl_vif, &bl_hw->vifs, list) {
|
|
+ if (bl_vif->up)
|
|
+ return bl_vif;
|
|
+ }
|
|
+ return NULL;
|
|
+ } else if (vif_idx < NX_VIRT_DEV_MAX) {
|
|
+ bl_vif = bl_hw->vif_table[vif_idx];
|
|
+ if (!bl_vif || !bl_vif->up)
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return bl_vif;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_rxdataind - Process rx buffer
|
|
+ *
|
|
+ * @pthis: Pointer to the object attached to the IPC structure
|
|
+ * (points to struct bl_hw is this case)
|
|
+ * @hostid: Address of the RX descriptor
|
|
+ *
|
|
+ * This function is called for each buffer received by the fw
|
|
+ *
|
|
+ */
|
|
+u8 bl_rxdataind(void *pthis, void *hostid)
|
|
+{
|
|
+ u32 status;
|
|
+ int sta_idx;
|
|
+ int tid;
|
|
+ u16 sn;
|
|
+ bool found = false;
|
|
+
|
|
+ struct bl_hw *bl_hw = (struct bl_hw *)pthis;
|
|
+ struct hw_rxhdr *hw_rxhdr;
|
|
+ struct bl_vif *bl_vif;
|
|
+ struct sk_buff *skb = (struct sk_buff *)hostid;
|
|
+ struct bl_agg_reord_pkt *reord_pkt = NULL;
|
|
+ struct bl_agg_reord_pkt *reord_pkt_inst = NULL;
|
|
+
|
|
+ int msdu_offset = sizeof(struct hw_rxhdr);
|
|
+
|
|
+ /*status occupy 4 bytes*/
|
|
+ memcpy(&status, skb->data, 4);
|
|
+ skb_pull(skb, 4);
|
|
+
|
|
+ /* check that frame is completely uploaded */
|
|
+ if (!status) {
|
|
+ printk("receive status error, status=0x%x\n", status);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ BL_DBG("status---->:0x%x\n", status);
|
|
+
|
|
+ if(status & RX_STAT_DELETE) {
|
|
+ BL_DBG("staus->delete, just free skb\n");
|
|
+ skb_push(skb, sizeof(struct sdio_hdr) + 4);
|
|
+ dev_kfree_skb(skb);
|
|
+ goto end;
|
|
+ }
|
|
+
|
|
+ /* Check if we need to forward the buffer */
|
|
+ if (status & RX_STAT_FORWARD) {
|
|
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
|
|
+ bl_vif = bl_rx_get_vif(bl_hw, hw_rxhdr->flags_vif_idx);
|
|
+
|
|
+ sn = hw_rxhdr->sn;
|
|
+ tid = hw_rxhdr->tid;
|
|
+ BL_DBG("RX_STATE_FORWARD: sn=%u, tid=%d\n", sn, tid);
|
|
+
|
|
+ if (!bl_vif) {
|
|
+ dev_err(bl_hw->dev, "Frame received but no active vif (%d)",
|
|
+ hw_rxhdr->flags_vif_idx);
|
|
+ dev_kfree_skb(skb);
|
|
+ goto end;
|
|
+ }
|
|
+
|
|
+ skb_pull(skb, msdu_offset);
|
|
+ /*Now, skb->data pointed to the real payload*/
|
|
+
|
|
+ if (hw_rxhdr->flags_is_80211_mpdu) {
|
|
+ BL_DBG("receive mgmt packet: type=0x%x, subtype=0x%x\n",
|
|
+ BL_FC_GET_TYPE(((struct ieee80211_mgmt *)skb->data)->frame_control), BL_FC_GET_STYPE(((struct ieee80211_mgmt *)skb->data)->frame_control));
|
|
+ bl_rx_mgmt(bl_hw, bl_vif, skb, hw_rxhdr);
|
|
+ } else {
|
|
+ BL_DBG("receive data packet: type=0x%x, subtype=0x%x\n",
|
|
+ BL_FC_GET_TYPE(*((u8 *)(skb->data))), BL_FC_GET_STYPE(*((u8 *)(skb->data))));
|
|
+
|
|
+ if (hw_rxhdr->flags_sta_idx != 0xff) {
|
|
+ struct bl_sta *sta= &bl_hw->sta_table[hw_rxhdr->flags_sta_idx];
|
|
+
|
|
+ bl_rx_statistic(bl_hw, hw_rxhdr, sta);
|
|
+
|
|
+ //printk("sta->vlan_idx=%d, bl_vif->vif_index=%d\n", sta->vlan_idx, bl_vif->vif_index);
|
|
+ if (sta->vlan_idx != bl_vif->vif_index) {
|
|
+ bl_vif = bl_hw->vif_table[sta->vlan_idx];
|
|
+ if (!bl_vif) {
|
|
+ printk("cannot find the vif\n");
|
|
+ dev_kfree_skb(skb);
|
|
+ goto end;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (hw_rxhdr->flags_is_4addr && !bl_vif->use_4addr) {
|
|
+ cfg80211_rx_unexpected_4addr_frame(bl_vif->ndev,
|
|
+ sta->mac_addr, GFP_ATOMIC);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ BL_DBG("original skb->priority=%d\n", skb->priority);
|
|
+ skb->priority = 256 + hw_rxhdr->flags_user_prio;
|
|
+ BL_DBG("hw_rxhdr->flags_user_prio=%d, skb->priority=%d, ndev_idx=%d\n", hw_rxhdr->flags_user_prio, skb->priority, skb_get_queue_mapping(skb));
|
|
+ if (!bl_rx_data_skb(bl_hw, bl_vif, skb, hw_rxhdr)) {
|
|
+ dev_kfree_skb(skb);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (status & RX_STAT_ALLOC) {
|
|
+ /*put skb into right list according sta_idx & tid*/
|
|
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
|
|
+ sta_idx = hw_rxhdr->flags_sta_idx;
|
|
+ sn = hw_rxhdr->sn;
|
|
+ tid = hw_rxhdr->tid;
|
|
+
|
|
+ BL_DBG("RX_STATE_ALLOC: sta_idx=%d, sn=%u, tid=%d\n", sta_idx, sn, tid);
|
|
+
|
|
+ //restore the skb and add into the tail of reorder list
|
|
+ skb_push(skb, 4);
|
|
+
|
|
+ /*construct the reorder pkt*/
|
|
+ reord_pkt = kmem_cache_alloc(bl_hw->agg_reodr_pkt_cache, GFP_ATOMIC);
|
|
+ if(reord_pkt == NULL) {
|
|
+ printk("KMEM CACHE ALLOC failed\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ reord_pkt->skb = skb;
|
|
+ reord_pkt->sn = sn;
|
|
+
|
|
+ if (!list_empty(&bl_hw->reorder_list[sta_idx*NX_NB_TID_PER_STA + tid])) {
|
|
+ list_for_each_entry(reord_pkt_inst, &bl_hw->reorder_list[sta_idx*NX_NB_TID_PER_STA + tid], list) {
|
|
+ BL_DBG("reord_pkt_inst->sn=%u, sn=%u\n", reord_pkt_inst->sn, sn);
|
|
+ if(reord_pkt_inst->sn > sn) {
|
|
+ BL_DBG("Add sn %u before sn %u\n", sn, reord_pkt_inst->sn);
|
|
+ found = true;
|
|
+ list_add(&(reord_pkt->list), reord_pkt_inst->list.prev);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(!found) {
|
|
+ BL_DBG("not found, add sn %u to tail\n", sn);
|
|
+ list_add_tail(&(reord_pkt->list), &bl_hw->reorder_list[sta_idx*NX_NB_TID_PER_STA+tid]);
|
|
+ }
|
|
+ } else {
|
|
+ BL_DBG("list is empty, add sn %u in tail\n", sn);
|
|
+ list_add_tail(&(reord_pkt->list), &bl_hw->reorder_list[sta_idx*NX_NB_TID_PER_STA+tid]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ end:
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff -Naur /dev/null/bl_rx.h b/drivers/net/wireless/hflps170/fullmac/bl_rx.h
|
|
--- /dev/null/bl_rx.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/fullmac/bl_rx.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,176 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_rx.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#ifndef _BL_RX_H_
|
|
+#define _BL_RX_H_
|
|
+
|
|
+enum rx_status_bits
|
|
+{
|
|
+ /// The buffer can be forwarded to the networking stack
|
|
+ RX_STAT_FORWARD = 1 << 0,
|
|
+ /// A new buffer has to be allocated
|
|
+ RX_STAT_ALLOC = 1 << 1,
|
|
+ /// The buffer has to be deleted
|
|
+ RX_STAT_DELETE = 1 << 2,
|
|
+ /// The length of the buffer has to be updated
|
|
+ RX_STAT_LEN_UPDATE = 1 << 3,
|
|
+ /// The length in the Ethernet header has to be updated
|
|
+ RX_STAT_ETH_LEN_UPDATE = 1 << 4,
|
|
+ /// Simple copy
|
|
+ RX_STAT_COPY = 1 << 5,
|
|
+};
|
|
+
|
|
+
|
|
+/*
|
|
+ * Decryption status subfields.
|
|
+ * {
|
|
+ */
|
|
+#define BL_RX_HD_DECR_UNENC 0 // Frame unencrypted
|
|
+#define BL_RX_HD_DECR_ICVFAIL 1 // WEP/TKIP ICV failure
|
|
+#define BL_RX_HD_DECR_CCMPFAIL 2 // CCMP failure
|
|
+#define BL_RX_HD_DECR_AMSDUDISCARD 3 // A-MSDU discarded at HW
|
|
+#define BL_RX_HD_DECR_NULLKEY 4 // NULL key found
|
|
+#define BL_RX_HD_DECR_WEPSUCCESS 5 // Security type WEP
|
|
+#define BL_RX_HD_DECR_TKIPSUCCESS 6 // Security type TKIP
|
|
+#define BL_RX_HD_DECR_CCMPSUCCESS 7 // Security type CCMP
|
|
+// @}
|
|
+
|
|
+struct hw_vect {
|
|
+ /** Total length for the MPDU transfer */
|
|
+ u32 len :16;
|
|
+
|
|
+ u32 reserved : 8;
|
|
+
|
|
+ /** AMPDU Status Information */
|
|
+ u32 mpdu_cnt : 6;
|
|
+ u32 ampdu_cnt : 2;
|
|
+
|
|
+
|
|
+ /** TSF Low */
|
|
+ __le32 tsf_lo;
|
|
+ /** TSF High */
|
|
+ __le32 tsf_hi;
|
|
+
|
|
+ /** Receive Vector 1a */
|
|
+ u32 leg_length :12;
|
|
+ u32 leg_rate : 4;
|
|
+ u32 ht_length :16;
|
|
+
|
|
+ /** Receive Vector 1b */
|
|
+ u32 _ht_length : 4; // FIXME
|
|
+ u32 short_gi : 1;
|
|
+ u32 stbc : 2;
|
|
+ u32 smoothing : 1;
|
|
+ u32 mcs : 7;
|
|
+ u32 pre_type : 1;
|
|
+ u32 format_mod : 3;
|
|
+ u32 ch_bw : 2;
|
|
+ u32 n_sts : 3;
|
|
+ u32 lsig_valid : 1;
|
|
+ u32 sounding : 1;
|
|
+ u32 num_extn_ss : 2;
|
|
+ u32 aggregation : 1;
|
|
+ u32 fec_coding : 1;
|
|
+ u32 dyn_bw : 1;
|
|
+ u32 doze_not_allowed : 1;
|
|
+
|
|
+ /** Receive Vector 1c */
|
|
+ u32 antenna_set : 8;
|
|
+ u32 partial_aid : 9;
|
|
+ u32 group_id : 6;
|
|
+ u32 reserved_1c : 1;
|
|
+ s32 rssi1 : 8;
|
|
+
|
|
+ /** Receive Vector 1d */
|
|
+ s32 rssi2 : 8;
|
|
+ s32 rssi3 : 8;
|
|
+ s32 rssi4 : 8;
|
|
+ u32 reserved_1d : 8;
|
|
+
|
|
+ /** Receive Vector 2a */
|
|
+ u32 rcpi : 8;
|
|
+ u32 evm1 : 8;
|
|
+ u32 evm2 : 8;
|
|
+ u32 evm3 : 8;
|
|
+
|
|
+ /** Receive Vector 2b */
|
|
+ u32 evm4 : 8;
|
|
+ u32 reserved2b_1 : 8;
|
|
+ u32 reserved2b_2 : 8;
|
|
+ u32 reserved2b_3 : 8;
|
|
+
|
|
+ /** Status **/
|
|
+ u32 rx_vect2_valid : 1;
|
|
+ u32 resp_frame : 1;
|
|
+ /** Decryption Status */
|
|
+ u32 decr_status : 3;
|
|
+ u32 rx_fifo_oflow : 1;
|
|
+
|
|
+ /** Frame Unsuccessful */
|
|
+ u32 undef_err : 1;
|
|
+ u32 phy_err : 1;
|
|
+ u32 fcs_err : 1;
|
|
+ u32 addr_mismatch : 1;
|
|
+ u32 ga_frame : 1;
|
|
+ u32 current_ac : 2;
|
|
+
|
|
+ u32 frm_successful_rx : 1;
|
|
+ /** Descriptor Done */
|
|
+ u32 desc_done_rx : 1;
|
|
+ /** Key Storage RAM Index */
|
|
+ u32 key_sram_index : 10;
|
|
+ /** Key Storage RAM Index Valid */
|
|
+ u32 key_sram_v : 1;
|
|
+ u32 type : 2;
|
|
+ u32 subtype : 4;
|
|
+};
|
|
+
|
|
+struct hw_rxhdr {
|
|
+ /** RX vector */
|
|
+ struct hw_vect hwvect;
|
|
+
|
|
+ /** PHY channel information 1 */
|
|
+ u32 phy_band : 8;
|
|
+ u32 phy_channel_type : 8;
|
|
+ u32 phy_prim20_freq : 16;
|
|
+ /** PHY channel information 2 */
|
|
+ u32 phy_center1_freq : 16;
|
|
+ u32 phy_center2_freq : 16;
|
|
+ /** RX flags */
|
|
+ u32 flags_is_amsdu : 1;
|
|
+ u32 flags_is_80211_mpdu: 1;
|
|
+ u32 flags_is_4addr : 1;
|
|
+ u32 flags_new_peer : 1;
|
|
+ u32 flags_user_prio : 3;
|
|
+ u32 flags_rsvd0 : 1;
|
|
+ u32 flags_vif_idx : 8; // 0xFF if invalid VIF index
|
|
+ u32 flags_sta_idx : 8; // 0xFF if invalid STA index
|
|
+ u32 flags_dst_idx : 8; // 0xFF if unknown destination STA
|
|
+ u16 sn;
|
|
+ u16 tid;
|
|
+}__packed;
|
|
+
|
|
+struct bl_agg_reodr_msg {
|
|
+ u16 sn;
|
|
+ u8 sta_idx;
|
|
+ u8 tid;
|
|
+ u8 status;
|
|
+ u8 num;
|
|
+}__packed;
|
|
+
|
|
+extern const u8 legrates_lut[];
|
|
+#define BL_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2)
|
|
+#define BL_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4)
|
|
+
|
|
+#define AGG_REORD_MSG_MAX_NUM 4096
|
|
+
|
|
+
|
|
+u8 bl_rxdataind(void *pthis, void *hostid);
|
|
+
|
|
+#endif /* _BL_RX_H_ */
|
|
diff -Naur /dev/null/bl_tx.c b/drivers/net/wireless/hflps170/fullmac/bl_tx.c
|
|
--- /dev/null/bl_tx.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/fullmac/bl_tx.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,1558 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_tx.c
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#include <linux/version.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/if_vlan.h>
|
|
+#include <net/dsfield.h>
|
|
+
|
|
+#include "bl_defs.h"
|
|
+#include "bl_tx.h"
|
|
+#include "bl_msg_tx.h"
|
|
+#include "bl_events.h"
|
|
+#include "bl_sdio.h"
|
|
+#include "bl_irqs.h"
|
|
+#include "bl_compat.h"
|
|
+
|
|
+static u16 g_pkt_sn = 0;
|
|
+
|
|
+/******************************************************************************
|
|
+ * Power Save functions
|
|
+ *****************************************************************************/
|
|
+/**
|
|
+ * bl_set_traffic_status - Inform FW if traffic is available for STA in PS
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @sta: Sta in PS mode
|
|
+ * @available: whether traffic is buffered for the STA
|
|
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
|
|
+ */
|
|
+void bl_set_traffic_status(struct bl_hw *bl_hw,
|
|
+ struct bl_sta *sta,
|
|
+ bool available,
|
|
+ u8 ps_id)
|
|
+{
|
|
+ bool uapsd = (ps_id != LEGACY_PS_ID);
|
|
+ bl_send_me_traffic_ind(bl_hw, sta->sta_idx, uapsd, available);
|
|
+ trace_ps_traffic_update(sta->sta_idx, available, uapsd);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_ps_bh_enable - Enable/disable PS mode for one STA
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @sta: Sta which enters/leaves PS mode
|
|
+ * @enable: PS mode status
|
|
+ *
|
|
+ * This function will enable/disable PS mode for one STA.
|
|
+ * When enabling PS mode:
|
|
+ * - Stop all STA's txq for BL_TXQ_STOP_STA_PS reason
|
|
+ * - Count how many buffers are already ready for this STA
|
|
+ * - For BC/MC sta, update all queued SKB to use hw_queue BCMC
|
|
+ * - Update TIM if some packet are ready
|
|
+ *
|
|
+ * When disabling PS mode:
|
|
+ * - Start all STA's txq for BL_TXQ_STOP_STA_PS reason
|
|
+ * - For BC/MC sta, update all queued SKB to use hw_queue AC_BE
|
|
+ * - Update TIM if some packet are ready (otherwise fw will not update TIM
|
|
+ * in beacon for this STA)
|
|
+ *
|
|
+ * All counter/skb updates are protected from TX path by taking tx_lock
|
|
+ *
|
|
+ * NOTE: _bh_ in function name indicates that this function is called
|
|
+ * from a bottom_half tasklet.
|
|
+ */
|
|
+void bl_ps_bh_enable(struct bl_hw *bl_hw, struct bl_sta *sta,
|
|
+ bool enable)
|
|
+{
|
|
+ struct bl_txq *txq = NULL;
|
|
+ txq = bl_txq_sta_get(sta, 0, NULL, bl_hw);
|
|
+
|
|
+ if (enable) {
|
|
+ trace_ps_enable(sta);
|
|
+
|
|
+ spin_lock(&bl_hw->tx_lock);
|
|
+ sta->ps.active = true;
|
|
+ sta->ps.sp_cnt[LEGACY_PS_ID] = 0;
|
|
+ sta->ps.sp_cnt[UAPSD_ID] = 0;
|
|
+ bl_txq_sta_stop(sta, BL_TXQ_STOP_STA_PS, bl_hw);
|
|
+
|
|
+ if (is_multicast_sta(sta->sta_idx)) {
|
|
+ sta->ps.pkt_ready[LEGACY_PS_ID] = skb_queue_len(&txq->sk_list);
|
|
+ sta->ps.pkt_ready[UAPSD_ID] = 0;
|
|
+ txq->hwq = &bl_hw->hwq[BL_HWQ_BCMC];
|
|
+ } else {
|
|
+ int i;
|
|
+
|
|
+ sta->ps.pkt_ready[LEGACY_PS_ID] = 0;
|
|
+ sta->ps.pkt_ready[UAPSD_ID] = 0;
|
|
+ for (i = 0; i < NX_NB_TXQ_PER_STA; i++, txq++) {
|
|
+ sta->ps.pkt_ready[txq->ps_id] += skb_queue_len(&txq->sk_list);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_unlock(&bl_hw->tx_lock);
|
|
+
|
|
+ if (sta->ps.pkt_ready[LEGACY_PS_ID])
|
|
+ bl_set_traffic_status(bl_hw, sta, true, LEGACY_PS_ID);
|
|
+
|
|
+ if (sta->ps.pkt_ready[UAPSD_ID])
|
|
+ bl_set_traffic_status(bl_hw, sta, true, UAPSD_ID);
|
|
+ } else {
|
|
+ trace_ps_disable(sta->sta_idx);
|
|
+
|
|
+ spin_lock(&bl_hw->tx_lock);
|
|
+ sta->ps.active = false;
|
|
+
|
|
+ if (is_multicast_sta(sta->sta_idx)) {
|
|
+ txq->hwq = &bl_hw->hwq[BL_HWQ_BE];
|
|
+ txq->push_limit = 0;
|
|
+ } else {
|
|
+ int i;
|
|
+ for (i = 0; i < NX_NB_TXQ_PER_STA; i++, txq++) {
|
|
+ txq->push_limit = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ bl_txq_sta_start(sta, BL_TXQ_STOP_STA_PS, bl_hw);
|
|
+ spin_unlock(&bl_hw->tx_lock);
|
|
+
|
|
+ if (sta->ps.pkt_ready[LEGACY_PS_ID])
|
|
+ bl_set_traffic_status(bl_hw, sta, false, LEGACY_PS_ID);
|
|
+
|
|
+ if (sta->ps.pkt_ready[UAPSD_ID])
|
|
+ bl_set_traffic_status(bl_hw, sta, false, UAPSD_ID);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_ps_bh_traffic_req - Handle traffic request for STA in PS mode
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @sta: Sta which enters/leaves PS mode
|
|
+ * @pkt_req: number of pkt to push
|
|
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
|
|
+ *
|
|
+ * This function will make sure that @pkt_req are pushed to fw
|
|
+ * whereas the STA is in PS mode.
|
|
+ * If request is 0, send all traffic
|
|
+ * If request is greater than available pkt, reduce request
|
|
+ * Note: request will also be reduce if txq credits are not available
|
|
+ *
|
|
+ * All counter updates are protected from TX path by taking tx_lock
|
|
+ *
|
|
+ * NOTE: _bh_ in function name indicates that this function is called
|
|
+ * from the bottom_half tasklet.
|
|
+ */
|
|
+void bl_ps_bh_traffic_req(struct bl_hw *bl_hw, struct bl_sta *sta,
|
|
+ u16 pkt_req, u8 ps_id)
|
|
+{
|
|
+ int pkt_ready_all;
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ if (WARN(!sta->ps.active, "sta %pM is not in Power Save mode",
|
|
+ sta->mac_addr))
|
|
+ return;
|
|
+
|
|
+ trace_ps_traffic_req(sta, pkt_req, ps_id);
|
|
+
|
|
+ spin_lock(&bl_hw->tx_lock);
|
|
+
|
|
+ pkt_ready_all = (sta->ps.pkt_ready[ps_id] - sta->ps.sp_cnt[ps_id]);
|
|
+
|
|
+ /* Don't start SP until previous one is finished or we don't have
|
|
+ packet ready (which must not happen for U-APSD) */
|
|
+ if (sta->ps.sp_cnt[ps_id] || pkt_ready_all <= 0) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* Adapt request to what is available. */
|
|
+ if (pkt_req == 0 || pkt_req > pkt_ready_all) {
|
|
+ pkt_req = pkt_ready_all;
|
|
+ }
|
|
+
|
|
+ /* Reset the SP counter */
|
|
+ sta->ps.sp_cnt[ps_id] = 0;
|
|
+
|
|
+ /* "dispatch" the request between txq */
|
|
+ txq = bl_txq_sta_get(sta, NX_NB_TXQ_PER_STA - 1, NULL, bl_hw);
|
|
+
|
|
+ if (is_multicast_sta(sta->sta_idx)) {
|
|
+ if (txq->credits <= 0)
|
|
+ goto done;
|
|
+ if (pkt_req > txq->credits)
|
|
+ pkt_req = txq->credits;
|
|
+ txq->push_limit = pkt_req;
|
|
+ sta->ps.sp_cnt[ps_id] = pkt_req;
|
|
+ bl_txq_add_to_hw_list(txq);
|
|
+ } else {
|
|
+ int i;
|
|
+ /* TODO: dispatch using correct txq priority */
|
|
+ for (i = NX_NB_TXQ_PER_STA - 1; i >= 0; i--, txq--) {
|
|
+ u16 txq_len = skb_queue_len(&txq->sk_list);
|
|
+
|
|
+ if (txq->ps_id != ps_id)
|
|
+ continue;
|
|
+
|
|
+ if (txq_len > txq->credits)
|
|
+ txq_len = txq->credits;
|
|
+
|
|
+ if (txq_len > 0) {
|
|
+ if (txq_len < pkt_req) {
|
|
+ /* Not enough pkt queued in this txq, add this
|
|
+ txq to hwq list and process next txq */
|
|
+ pkt_req -= txq_len;
|
|
+ txq->push_limit = txq_len;
|
|
+ sta->ps.sp_cnt[ps_id] += txq_len;
|
|
+ bl_txq_add_to_hw_list(txq);
|
|
+ } else {
|
|
+ /* Enough pkt in this txq to comlete the request
|
|
+ add this txq to hwq list and stop processing txq */
|
|
+ txq->push_limit = pkt_req;
|
|
+ sta->ps.sp_cnt[ps_id] += pkt_req;
|
|
+ bl_txq_add_to_hw_list(txq);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ done:
|
|
+ spin_unlock(&bl_hw->tx_lock);
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * TX functions
|
|
+ *****************************************************************************/
|
|
+static const int bl_down_hwq2tid[3] = {
|
|
+ [BL_HWQ_BK] = 2,
|
|
+ [BL_HWQ_BE] = 3,
|
|
+ [BL_HWQ_VI] = 5,
|
|
+};
|
|
+
|
|
+void bl_downgrade_ac(struct bl_sta *sta, struct sk_buff *skb)
|
|
+{
|
|
+ int8_t ac = bl_tid2hwq[skb->priority];
|
|
+
|
|
+ if (WARN((ac > BL_HWQ_VO),
|
|
+ "Unexepcted ac %d for skb before downgrade", ac))
|
|
+ ac = BL_HWQ_VO;
|
|
+
|
|
+ while (sta->acm & BIT(ac)) {
|
|
+ if (ac == BL_HWQ_BK) {
|
|
+ skb->priority = 1;
|
|
+ return;
|
|
+ }
|
|
+ ac--;
|
|
+ skb->priority = bl_down_hwq2tid[ac];
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_set_more_data_flag - Update MORE_DATA flag in tx sw desc
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @sw_txhdr: Header for pkt to be pushed
|
|
+ *
|
|
+ * If STA is in PS mode
|
|
+ * - Set EOSP in case the packet is the last of the UAPSD service period
|
|
+ * - Set MORE_DATA flag if more pkt are ready for this sta
|
|
+ * - Update TIM if this is the last pkt buffered for this sta
|
|
+ *
|
|
+ * note: tx_lock already taken.
|
|
+ */
|
|
+static inline void bl_set_more_data_flag(struct bl_hw *bl_hw,
|
|
+ struct bl_sw_txhdr *sw_txhdr)
|
|
+{
|
|
+ struct bl_sta *sta = sw_txhdr->bl_sta;
|
|
+ struct bl_txq *txq = sw_txhdr->txq;
|
|
+
|
|
+ if (unlikely(sta->ps.active)) {
|
|
+ sta->ps.pkt_ready[txq->ps_id]--;
|
|
+ sta->ps.sp_cnt[txq->ps_id]--;
|
|
+
|
|
+ trace_ps_push(sta);
|
|
+
|
|
+ if (((txq->ps_id == UAPSD_ID))
|
|
+ && !sta->ps.sp_cnt[txq->ps_id]) {
|
|
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_EOSP;
|
|
+ }
|
|
+
|
|
+ if (sta->ps.pkt_ready[txq->ps_id]) {
|
|
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_MORE_DATA;
|
|
+ } else {
|
|
+ bl_set_traffic_status(bl_hw, sta, false, txq->ps_id);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#define PRIO_STA_NULL 0xAA
|
|
+/**
|
|
+ * bl_get_tx_info - Get STA and tid for one skb
|
|
+ *
|
|
+ * @bl_vif: vif ptr
|
|
+ * @skb: skb
|
|
+ * @tid: pointer updated with the tid to use for this skb
|
|
+ *
|
|
+ * @return: pointer on the destination STA (may be NULL)
|
|
+ *
|
|
+ * skb has already been parsed in bl_select_queue function
|
|
+ * simply re-read information form skb.
|
|
+ */
|
|
+static struct bl_sta *bl_get_tx_info(struct bl_vif *bl_vif,
|
|
+ struct sk_buff *skb,
|
|
+ u8 *tid)
|
|
+{
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+ struct bl_sta *sta = NULL;
|
|
+ struct wireless_dev *wdev = &bl_vif->wdev;
|
|
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
|
|
+
|
|
+ *tid = skb->priority;
|
|
+
|
|
+ if (unlikely(skb->priority == PRIO_STA_NULL)) {
|
|
+ return NULL;
|
|
+ } else {
|
|
+ switch (wdev->iftype) {
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ case NL80211_IFTYPE_P2P_CLIENT:
|
|
+ {
|
|
+ sta = bl_vif->sta.ap;
|
|
+ break;
|
|
+ }
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ if (bl_vif->ap_vlan.sta_4a) {
|
|
+ sta = bl_vif->ap_vlan.sta_4a;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* AP_VLAN interface is not used for a 4A STA,
|
|
+ fallback searching sta amongs all AP's clients */
|
|
+ bl_vif = bl_vif->ap_vlan.master;
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ case NL80211_IFTYPE_P2P_GO:
|
|
+ {
|
|
+ struct bl_sta *cur;
|
|
+ if (is_broadcast_ether_addr(eth->h_dest) || is_multicast_ether_addr(eth->h_dest)) {
|
|
+ sta = &bl_hw->sta_table[bl_vif->ap.bcmc_index];
|
|
+ } else {
|
|
+ list_for_each_entry(cur, &bl_vif->ap.sta_list, list) {
|
|
+ if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
|
|
+ sta = cur;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ return sta;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+/**
|
|
+ * bl_get_tx_info - Get STA and tid for one skb
|
|
+ *
|
|
+ * @bl_vif: vif ptr
|
|
+ * @skb: skb
|
|
+ * @tid: pointer updated with the tid to use for this skb
|
|
+ *
|
|
+ * @return: pointer on the destination STA (may be NULL)
|
|
+ *
|
|
+ * skb has already been parsed in bl_select_queue function
|
|
+ * simply re-read information form skb.
|
|
+ */
|
|
+static struct bl_sta *bl_get_tx_info(struct bl_vif *bl_vif,
|
|
+ struct sk_buff *skb,
|
|
+ u8 *tid)
|
|
+{
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+ struct bl_sta *sta;
|
|
+ int sta_idx;
|
|
+
|
|
+ *tid = skb->priority;
|
|
+ if (unlikely(skb->priority == PRIO_STA_NULL)) {
|
|
+ return NULL;
|
|
+ } else {
|
|
+ int ndev_idx = skb_get_queue_mapping(skb);
|
|
+ if (ndev_idx == NX_BCMC_TXQ_NDEV_IDX)
|
|
+ sta_idx = NX_REMOTE_STA_MAX + master_vif_idx(bl_vif);
|
|
+ else
|
|
+ sta_idx = ndev_idx / NX_NB_TID_PER_STA;
|
|
+
|
|
+ sta = &bl_hw->sta_table[sta_idx];
|
|
+ }
|
|
+
|
|
+ return sta;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * bl_tx_push - Push one packet to fw
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @txhdr: tx desc of the buffer to push
|
|
+ * @flags: push flags (see @bl_push_flags)
|
|
+ *
|
|
+ * Push one packet to fw. Sw desc of the packet has already been updated.
|
|
+ * Only MORE_DATA flag will be set if needed.
|
|
+ */
|
|
+void bl_tx_push(struct bl_hw *bl_hw, struct bl_txhdr *txhdr, int flags)
|
|
+{
|
|
+ struct bl_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
|
|
+ struct txdesc_api *desc;
|
|
+ struct sk_buff *skb = sw_txhdr->skb;
|
|
+ struct bl_txq *txq = sw_txhdr->txq;
|
|
+ u16 hw_queue = txq->hwq->id;
|
|
+ int user = 0;
|
|
+ u32 wr_port;
|
|
+ u32 ret = 0;
|
|
+ ktime_t start;
|
|
+
|
|
+ lockdep_assert_held(&bl_hw->tx_lock);
|
|
+
|
|
+ /* RETRY flag is not always set so retest here */
|
|
+ if (txq->nb_retry) {
|
|
+ flags |= BL_PUSH_RETRY;
|
|
+ txq->nb_retry--;
|
|
+ if (txq->nb_retry == 0) {
|
|
+ WARN(skb != txq->last_retry_skb,
|
|
+ "last retry buffer is not the expected one");
|
|
+ txq->last_retry_skb = NULL;
|
|
+ }
|
|
+ } else if (!(flags & BL_PUSH_RETRY)) {
|
|
+ txq->pkt_sent++;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ if (txq->amsdu == sw_txhdr) {
|
|
+ WARN((flags & BL_PUSH_RETRY), "End A-MSDU on a retry");
|
|
+ bl_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
|
|
+ txq->amsdu = NULL;
|
|
+ } else if (!(flags & BL_PUSH_RETRY) &&
|
|
+ !(sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)) {
|
|
+ bl_hw->stats.amsdus[0].done++;
|
|
+ }
|
|
+#endif /* CONFIG_BL_AMSDUS_TX */
|
|
+
|
|
+ /* Wait here to update hw_queue, as for multicast STA hwq may change
|
|
+ between queue and push (because of PS) */
|
|
+ sw_txhdr->hw_queue = hw_queue;
|
|
+
|
|
+ if (sw_txhdr->bl_sta) {
|
|
+ /* only for AP mode */
|
|
+ bl_set_more_data_flag(bl_hw, sw_txhdr);
|
|
+ }
|
|
+
|
|
+ trace_push_desc(skb, sw_txhdr, flags);
|
|
+ BL_DBG("bl_tx_push:txq->idx=%d, txq->status=0x%x, txq->credits: %d--->%d\n", txq->idx, txq->status, txq->credits, txq->credits-1);
|
|
+ txq->credits--;
|
|
+ txq->pkt_pushed[user]++;
|
|
+
|
|
+ if (txq->credits <= 0) {
|
|
+ bl_txq_stop(txq, BL_TXQ_STOP_FULL);
|
|
+ BL_DBG("delete txq from hwq, txq->idx=%d, txq->status=0x%x, txq-credits=%d\n", txq->idx, txq->status, txq->credits);
|
|
+ }
|
|
+
|
|
+ if (txq->push_limit)
|
|
+ txq->push_limit--;
|
|
+
|
|
+ /*use sdio interface to send the whole skb packet */
|
|
+ /* fisrt, we should ignore the txhdr, after skb_pull, we got the real data */
|
|
+ skb_pull(skb, sw_txhdr->headroom);
|
|
+ /*use sw_txhdr to override the sdio special header*/
|
|
+ sw_txhdr->hdr.queue_idx = txq->hwq->id; //update queue idx to avoid ps mode modify it.
|
|
+ memcpy((void *)skb->data, &sw_txhdr->hdr, sizeof(struct sdio_hdr));
|
|
+ memcpy((void *)skb->data + sizeof(struct sdio_hdr), &sw_txhdr->desc, sizeof(*desc));
|
|
+
|
|
+ /*when get wr_port failed*/
|
|
+ ret = bl_get_wr_port(bl_hw, &wr_port);
|
|
+ if (ret) {
|
|
+ printk("get wr port failed ret=%d, requeue skb=%p\n", ret, skb);
|
|
+ skb_push(skb, sw_txhdr->headroom);
|
|
+ bl_txq_queue_skb(skb, txq, bl_hw, false);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BL_DBG("bl_tx_push: send skb=%p, skb->len=%d\n", skb, skb->len);
|
|
+
|
|
+ start = ktime_get();
|
|
+ ret = bl_write_data_sync(bl_hw, skb->data, skb->len, bl_hw->plat->io_port + wr_port);
|
|
+ bl_hw->dbg_time[bl_hw->dbg_time_idx].sdio_tx = ktime_us_delta(ktime_get(), start);
|
|
+ bl_hw->dbg_time_idx++;
|
|
+
|
|
+ if(bl_hw->dbg_time_idx == 50)
|
|
+ bl_hw->dbg_time_idx = 0;
|
|
+ if(ret) {
|
|
+ printk("bl_write_data_sync failed, ret=%d\n", ret);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /*restore skb, txcfm will use this skb*/
|
|
+ skb_push(skb, sw_txhdr->headroom);
|
|
+ ipc_host_txdesc_push(bl_hw->ipc_env, hw_queue, user, skb);
|
|
+ txq->hwq->credits[user]--;
|
|
+ bl_hw->stats.cfm_balance[hw_queue]++;
|
|
+}
|
|
+
|
|
+/** 2K buf size */
|
|
+#define BL_TX_DATA_BUF_SIZE_16K 16*1024
|
|
+#define BL_SDIO_MP_AGGR_PKT_LIMIT_MAX 8
|
|
+#define BL_SDIO_MPA_ADDR_BASE 0x1000
|
|
+
|
|
+//typedef struct _sdio_mpa_tx {
|
|
+// u8 *buf;
|
|
+// u32 buf_len;
|
|
+// u32 pkt_cnt;
|
|
+// u32 ports;
|
|
+// u16 start_port;
|
|
+// u16 mp_wr_info[BL_SDIO_MP_AGGR_PKT_LIMIT_MAX];
|
|
+//}sdio_mpa_tx;
|
|
+
|
|
+void bl_tx_multi_pkt_push(struct bl_hw *bl_hw, struct sk_buff_head *sk_list_push)
|
|
+{
|
|
+ struct sk_buff *skb;
|
|
+ struct bl_txhdr *txhdr;
|
|
+ sdio_mpa_tx mpa_tx_data = {0};
|
|
+ int ret;
|
|
+ u32 port;
|
|
+ u32 cmd53_port;
|
|
+ u32 buf_block_len;
|
|
+ int flags = 0;
|
|
+
|
|
+ /*fix alloc buf size, such as 16K(2*8(aggr num))*/
|
|
+ mpa_tx_data.buf = bl_hw->mpa_tx_data.buf;
|
|
+ if(mpa_tx_data.buf == NULL){
|
|
+ printk("mpa_tx_data.buf is null!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /*copy multi skbs into one large buf*/
|
|
+ while ((skb = __skb_dequeue(sk_list_push)) != NULL) {
|
|
+ txhdr = (struct bl_txhdr *)skb->data;
|
|
+ /* RETRY flag is not always set so retest here */
|
|
+ if (txhdr->sw_hdr->txq->nb_retry) {
|
|
+ flags |= BL_PUSH_RETRY;
|
|
+ txhdr->sw_hdr->txq->nb_retry--;
|
|
+ if (txhdr->sw_hdr->txq->nb_retry == 0) {
|
|
+ WARN(skb != txhdr->sw_hdr->txq->last_retry_skb,
|
|
+ "last retry buffer is not the expected one");
|
|
+ txhdr->sw_hdr->txq->last_retry_skb = NULL;
|
|
+ }
|
|
+ } else if (!(flags & BL_PUSH_RETRY)) {
|
|
+ txhdr->sw_hdr->txq->pkt_sent++;
|
|
+ }
|
|
+
|
|
+ txhdr->sw_hdr->hw_queue = txhdr->sw_hdr->txq->hwq->id;
|
|
+
|
|
+ if(txhdr->sw_hdr->bl_sta)
|
|
+ bl_set_more_data_flag(bl_hw, txhdr->sw_hdr);
|
|
+
|
|
+// txhdr->sw_hdr->txq->credits--;
|
|
+ txhdr->sw_hdr->txq->pkt_pushed[0]++;
|
|
+
|
|
+// if(txhdr->sw_hdr->txq->credits <= 0)
|
|
+// bl_txq_stop(txhdr->sw_hdr->txq, BL_TXQ_STOP_FULL);
|
|
+
|
|
+ skb_pull(skb, txhdr->sw_hdr->headroom);
|
|
+ txhdr->sw_hdr->hdr.queue_idx = txhdr->sw_hdr->txq->hwq->id;
|
|
+ memcpy((void *)skb->data, &txhdr->sw_hdr->hdr, sizeof(struct sdio_hdr));
|
|
+ memcpy((void *)skb->data + sizeof(struct sdio_hdr), &txhdr->sw_hdr->desc, sizeof(struct txdesc_api));
|
|
+ buf_block_len = (skb->len + BL_SDIO_BLOCK_SIZE - 1) / BL_SDIO_BLOCK_SIZE;
|
|
+ memcpy((void *)&mpa_tx_data.buf[mpa_tx_data.buf_len], skb->data, buf_block_len * BL_SDIO_BLOCK_SIZE);
|
|
+ mpa_tx_data.buf_len += buf_block_len * BL_SDIO_BLOCK_SIZE;
|
|
+
|
|
+ //printk("###%d: skb->len: %d, pad_len: %d\n", mpa_tx_data.pkt_cnt, skb->len, buf_block_len*BL_SDIO_BLOCK_SIZE);
|
|
+
|
|
+ //mpa_tx_data.mp_wr_info[mpa_tx_data.pkt_cnt] = *(u16 *)skb->data;
|
|
+
|
|
+ bl_get_wr_port(bl_hw, &port);
|
|
+ if(!mpa_tx_data.pkt_cnt) {
|
|
+ mpa_tx_data.start_port = port;
|
|
+ }
|
|
+
|
|
+ if(mpa_tx_data.start_port <= port) {
|
|
+ mpa_tx_data.ports |= (1 << (mpa_tx_data.pkt_cnt));
|
|
+ } else {
|
|
+ mpa_tx_data.ports |= (1 << (mpa_tx_data.pkt_cnt + 1));
|
|
+ }
|
|
+
|
|
+ mpa_tx_data.pkt_cnt++;
|
|
+
|
|
+ skb_push(skb, txhdr->sw_hdr->headroom);
|
|
+ ipc_host_txdesc_push(bl_hw->ipc_env, txhdr->sw_hdr->hw_queue, 0, skb);
|
|
+// txhdr->sw_hdr->txq->hwq->credits[0]--;
|
|
+ bl_hw->stats.cfm_balance[txhdr->sw_hdr->hw_queue]++;
|
|
+
|
|
+ }
|
|
+/*
|
|
+ printk("mpa_tx_data:ports=0x%02x, start_port=%d, buf=%p, buf_len=%d, pkt_cnt=%d\n",
|
|
+ mpa_tx_data.ports,
|
|
+ mpa_tx_data.start_port,
|
|
+ mpa_tx_data.buf,
|
|
+ mpa_tx_data.buf_len,
|
|
+ mpa_tx_data.pkt_cnt);
|
|
+*/
|
|
+
|
|
+ /*send packet*/
|
|
+ cmd53_port = (bl_hw->plat->io_port | BL_SDIO_MPA_ADDR_BASE |
|
|
+ (mpa_tx_data.ports << 4)) + mpa_tx_data.start_port;
|
|
+
|
|
+// printk("cmd53_port=0x%08x\n", cmd53_port);
|
|
+
|
|
+ ret = bl_write_data_sync(bl_hw, mpa_tx_data.buf, mpa_tx_data.buf_len, cmd53_port);
|
|
+ if(ret)
|
|
+ printk("bl_write_data_sync failed, ret=%d\n", ret);
|
|
+
|
|
+ if(bl_hw->mpa_tx_data.buf)
|
|
+ memset(bl_hw->mpa_tx_data.buf, 0, BL_RX_DATA_BUF_SIZE_16K);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_tx_retry - Push an AMPDU pkt that need to be retried
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @skb: pkt to re-push
|
|
+ * @txhdr: tx desc of the pkt to re-push
|
|
+ * @sw_retry: Indicates if fw decide to retry this buffer
|
|
+ * (i.e. it has never been transmitted over the air)
|
|
+ *
|
|
+ * Called when a packet needs to be repushed to the firmware.
|
|
+ * First update sw descriptor and then queue it in the retry list.
|
|
+ */
|
|
+static void bl_tx_retry(struct bl_hw *bl_hw, struct sk_buff *skb,
|
|
+ struct bl_txhdr *txhdr, bool sw_retry)
|
|
+{
|
|
+ struct bl_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
|
|
+ struct tx_cfm_tag *cfm = &txhdr->hw_hdr.cfm;
|
|
+ struct bl_txq *txq = sw_txhdr->txq;
|
|
+
|
|
+ if (!sw_retry) {
|
|
+ /* update sw desc */
|
|
+ sw_txhdr->desc.host.sn = cfm->sn;
|
|
+ sw_txhdr->desc.host.pn[0] = cfm->pn[0];
|
|
+ sw_txhdr->desc.host.pn[1] = cfm->pn[1];
|
|
+ sw_txhdr->desc.host.pn[2] = cfm->pn[2];
|
|
+ sw_txhdr->desc.host.pn[3] = cfm->pn[3];
|
|
+ sw_txhdr->desc.host.timestamp = cfm->timestamp;
|
|
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_RETRY;
|
|
+
|
|
+ #ifdef CONFIG_BL_AMSDUS_TX
|
|
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)
|
|
+ bl_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].failed++;
|
|
+ #endif
|
|
+ }
|
|
+
|
|
+ /* MORE_DATA will be re-set if needed when pkt will be repushed */
|
|
+ sw_txhdr->desc.host.flags &= ~TXU_CNTRL_MORE_DATA;
|
|
+
|
|
+ cfm->status.value = 0;
|
|
+
|
|
+ BL_DBG("bl_tx_retry: skb=%p, sn=%u\n", skb, sw_txhdr->desc.host.sn);
|
|
+ BL_DBG("bl_tx_retry: txq->idx=%d, txq->status=0x%x, txq->credits=%d-->%d\n", txq->idx, txq->status, txq->credits, txq->credits+1);
|
|
+ txq->credits++;
|
|
+/*
|
|
+ spin_lock_bh(&bl_hw->txq_lock);
|
|
+ if (txq->credits > 0)
|
|
+ bl_txq_start(txq, BL_TXQ_STOP_FULL);
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ */
|
|
+ /* Queue the buffer */
|
|
+ bl_txq_queue_skb(skb, txq, bl_hw, true);
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+/* return size of subframe (including header) */
|
|
+static inline int bl_amsdu_subframe_length(struct ethhdr *eth, int eth_len)
|
|
+{
|
|
+ /* ethernet header is replaced with amdsu header that have the same size
|
|
+ Only need to check if LLC/SNAP header will be added */
|
|
+ int len = eth_len;
|
|
+
|
|
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
|
|
+ len += sizeof(rfc1042_header) + 2;
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static inline bool bl_amsdu_is_aggregable(struct sk_buff *skb)
|
|
+{
|
|
+ /* need to add some check on buffer to see if it can be aggregated ? */
|
|
+ return true;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * bl_amsdu_del_subframe_header - remove AMSDU header
|
|
+ *
|
|
+ * amsdu_txhdr: amsdu tx descriptor
|
|
+ *
|
|
+ * Move back the ethernet header at the "beginning" of the data buffer.
|
|
+ * (which has been moved in @bl_amsdu_add_subframe_header)
|
|
+ */
|
|
+static void bl_amsdu_del_subframe_header(struct bl_amsdu_txhdr *amsdu_txhdr)
|
|
+{
|
|
+ struct sk_buff *skb = amsdu_txhdr->skb;
|
|
+ struct ethhdr *eth;
|
|
+ u8 *pos;
|
|
+
|
|
+ pos = skb->data;
|
|
+ pos += sizeof(struct bl_amsdu_txhdr);
|
|
+ eth = (struct ethhdr*)pos;
|
|
+ pos += amsdu_txhdr->pad + sizeof(struct ethhdr);
|
|
+
|
|
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
|
|
+ pos += sizeof(rfc1042_header) + 2;
|
|
+ }
|
|
+
|
|
+ memmove(pos, eth, sizeof(*eth));
|
|
+ skb_pull(skb, (pos - skb->data));
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_amsdu_add_subframe_header - Add AMSDU header and link subframe
|
|
+ *
|
|
+ * @bl_hw Driver main data
|
|
+ * @skb Buffer to aggregate
|
|
+ * @sw_txhdr Tx descriptor for the first A-MSDU subframe
|
|
+ *
|
|
+ * return 0 on sucess, -1 otherwise
|
|
+ *
|
|
+ * This functions Add A-MSDU header and LLC/SNAP header in the buffer
|
|
+ * and update sw_txhdr of the first subframe to link this buffer.
|
|
+ * If an error happens, the buffer will be queued as a normal buffer.
|
|
+ *
|
|
+ *
|
|
+ * Before After
|
|
+ * +-------------+ +-------------+
|
|
+ * | HEADROOM | | HEADROOM |
|
|
+ * | | +-------------+ <- data
|
|
+ * | | | amsdu_txhdr |
|
|
+ * | | | * pad size |
|
|
+ * | | +-------------+
|
|
+ * | | | ETH hdr | keep original eth hdr
|
|
+ * | | | | to restore it once transmitted
|
|
+ * | | +-------------+ <- packet_addr[x]
|
|
+ * | | | Pad |
|
|
+ * | | +-------------+
|
|
+ * data -> +-------------+ | AMSDU HDR |
|
|
+ * | ETH hdr | +-------------+
|
|
+ * | | | LLC/SNAP |
|
|
+ * +-------------+ +-------------+
|
|
+ * | DATA | | DATA |
|
|
+ * | | | |
|
|
+ * +-------------+ +-------------+
|
|
+ *
|
|
+ * Called with tx_lock hold
|
|
+ */
|
|
+static int bl_amsdu_add_subframe_header(struct bl_hw *bl_hw,
|
|
+ struct sk_buff *skb,
|
|
+ struct bl_sw_txhdr *sw_txhdr)
|
|
+{
|
|
+ struct bl_amsdu *amsdu = &sw_txhdr->amsdu;
|
|
+ struct bl_amsdu_txhdr *amsdu_txhdr;
|
|
+ struct ethhdr *amsdu_hdr, *eth = (struct ethhdr *)skb->data;
|
|
+ int headroom_need, map_len, msdu_len;
|
|
+ dma_addr_t dma_addr;
|
|
+ u8 *pos, *map_start;
|
|
+
|
|
+ msdu_len = skb->len - sizeof(*eth);
|
|
+ headroom_need = sizeof(*amsdu_txhdr) + amsdu->pad +
|
|
+ sizeof(*amsdu_hdr);
|
|
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
|
|
+ headroom_need += sizeof(rfc1042_header) + 2;
|
|
+ msdu_len += sizeof(rfc1042_header) + 2;
|
|
+ }
|
|
+
|
|
+ /* we should have enough headroom (checked in xmit) */
|
|
+ if (WARN_ON(skb_headroom(skb) < headroom_need)) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* allocate headroom */
|
|
+ pos = skb_push(skb, headroom_need);
|
|
+ amsdu_txhdr = (struct bl_amsdu_txhdr *)pos;
|
|
+ pos += sizeof(*amsdu_txhdr);
|
|
+
|
|
+ /* move eth header */
|
|
+ memmove(pos, eth, sizeof(*eth));
|
|
+ eth = (struct ethhdr *)pos;
|
|
+ pos += sizeof(*eth);
|
|
+
|
|
+ /* Add padding from previous subframe */
|
|
+ map_start = pos;
|
|
+ memset(pos, 0, amsdu->pad);
|
|
+ pos += amsdu->pad;
|
|
+
|
|
+ /* Add AMSDU hdr */
|
|
+ amsdu_hdr = (struct ethhdr *)pos;
|
|
+ memcpy(amsdu_hdr->h_dest, eth->h_dest, ETH_ALEN);
|
|
+ memcpy(amsdu_hdr->h_source, eth->h_source, ETH_ALEN);
|
|
+ amsdu_hdr->h_proto = htons(msdu_len);
|
|
+ pos += sizeof(*amsdu_hdr);
|
|
+
|
|
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
|
|
+ memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
|
|
+ pos += sizeof(rfc1042_header);
|
|
+ }
|
|
+
|
|
+ /* MAP (and sync) memory for DMA */
|
|
+ map_len = msdu_len + amsdu->pad + sizeof(*amsdu_hdr);
|
|
+ dma_addr = dma_map_single(bl_hw->dev, map_start, map_len,
|
|
+ DMA_BIDIRECTIONAL);
|
|
+ if (WARN_ON(dma_mapping_error(bl_hw->dev, dma_addr))) {
|
|
+ pos -= sizeof(*eth);
|
|
+ memmove(pos, eth, sizeof(*eth));
|
|
+ skb_pull(skb, headroom_need);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* update amdsu_txhdr */
|
|
+ amsdu_txhdr->map_len = map_len;
|
|
+ amsdu_txhdr->dma_addr = dma_addr;
|
|
+ amsdu_txhdr->skb = skb;
|
|
+ amsdu_txhdr->pad = amsdu->pad;
|
|
+
|
|
+ /* update bl_sw_txhdr (of the first subframe) */
|
|
+ BUG_ON(amsdu->nb != sw_txhdr->desc.host.packet_cnt);
|
|
+ sw_txhdr->desc.host.packet_addr[amsdu->nb] = dma_addr;
|
|
+ sw_txhdr->desc.host.packet_len[amsdu->nb] = map_len;
|
|
+ sw_txhdr->desc.host.packet_cnt++;
|
|
+ amsdu->nb++;
|
|
+
|
|
+ amsdu->pad = AMSDU_PADDING(map_len - amsdu->pad);
|
|
+ list_add_tail(&amsdu_txhdr->list, &amsdu->hdrs);
|
|
+ amsdu->len += map_len;
|
|
+
|
|
+ trace_amsdu_subframe(sw_txhdr);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_amsdu_add_subframe - Add this buffer as an A-MSDU subframe if possible
|
|
+ *
|
|
+ * @bl_hw Driver main data
|
|
+ * @skb Buffer to aggregate if possible
|
|
+ * @sta Destination STA
|
|
+ * @txq sta's txq used for this buffer
|
|
+ *
|
|
+ * Tyr to aggregate the buffer in an A-MSDU. If it succeed then the
|
|
+ * buffer is added as a new A-MSDU subframe with AMSDU and LLC/SNAP
|
|
+ * headers added (so FW won't have to modify this subframe).
|
|
+ *
|
|
+ * To be added as subframe :
|
|
+ * - sta must allow amsdu
|
|
+ * - buffer must be aggregable (to be defined)
|
|
+ * - at least one other aggregable buffer is pending in the queue
|
|
+ * or an a-msdu (with enough free space) is currently in progress
|
|
+ *
|
|
+ * returns true if buffer has been added as A-MDSP subframe, false otherwise
|
|
+ *
|
|
+ */
|
|
+static bool bl_amsdu_add_subframe(struct bl_hw *bl_hw, struct sk_buff *skb,
|
|
+ struct bl_sta *sta, struct bl_txq *txq)
|
|
+{
|
|
+ bool res = false;
|
|
+ struct ethhdr *eth;
|
|
+
|
|
+ /* immedialtely return if amsdu are not allowed for this sta */
|
|
+ if (!txq->amsdu_len || bl_hw->mod_params->amsdu_maxnb < 2 ||
|
|
+ !bl_amsdu_is_aggregable(skb))
|
|
+ return false;
|
|
+
|
|
+ spin_lock_bh(&bl_hw->tx_lock);
|
|
+ if (txq->amsdu) {
|
|
+ /* aggreagation already in progress, add this buffer if enough space
|
|
+ available, otherwise end the current amsdu */
|
|
+ struct bl_sw_txhdr *sw_txhdr = txq->amsdu;
|
|
+ eth = (struct ethhdr *)(skb->data);
|
|
+
|
|
+ if (((sw_txhdr->amsdu.len + sw_txhdr->amsdu.pad +
|
|
+ bl_amsdu_subframe_length(eth, skb->len)) > txq->amsdu_len) ||
|
|
+ bl_amsdu_add_subframe_header(bl_hw, skb, sw_txhdr)) {
|
|
+ txq->amsdu = NULL;
|
|
+ goto end;
|
|
+ }
|
|
+
|
|
+ if (sw_txhdr->amsdu.nb >= bl_hw->mod_params->amsdu_maxnb) {
|
|
+ bl_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
|
|
+ /* max number of subframes reached */
|
|
+ txq->amsdu = NULL;
|
|
+ }
|
|
+ } else {
|
|
+ /* Check if a new amsdu can be started with the previous buffer
|
|
+ (if any) and this one */
|
|
+ struct sk_buff *skb_prev = skb_peek_tail(&txq->sk_list);
|
|
+ struct bl_txhdr *txhdr;
|
|
+ struct bl_sw_txhdr *sw_txhdr;
|
|
+ int len1, len2;
|
|
+
|
|
+ if (!skb_prev || !bl_amsdu_is_aggregable(skb_prev))
|
|
+ goto end;
|
|
+
|
|
+ txhdr = (struct bl_txhdr *)skb_prev->data;
|
|
+ sw_txhdr = txhdr->sw_hdr;
|
|
+ if ((sw_txhdr->amsdu.len) ||
|
|
+ (sw_txhdr->desc.host.flags & TXU_CNTRL_RETRY))
|
|
+ /* previous buffer is already a complete amsdu or a retry */
|
|
+ goto end;
|
|
+
|
|
+ eth = (struct ethhdr *)(skb_prev->data + sw_txhdr->headroom);
|
|
+ len1 = bl_amsdu_subframe_length(eth, (sw_txhdr->frame_len +
|
|
+ sizeof(struct ethhdr)));
|
|
+
|
|
+ eth = (struct ethhdr *)(skb->data);
|
|
+ len2 = bl_amsdu_subframe_length(eth, skb->len);
|
|
+
|
|
+ if (len1 + AMSDU_PADDING(len1) + len2 > txq->amsdu_len)
|
|
+ /* not enough space to aggregate those two buffers */
|
|
+ goto end;
|
|
+
|
|
+ /* Add subframe header.
|
|
+ Note: Fw will take care of adding AMDSU header for the first
|
|
+ subframe while generating 802.11 MAC header */
|
|
+ INIT_LIST_HEAD(&sw_txhdr->amsdu.hdrs);
|
|
+ sw_txhdr->amsdu.len = len1;
|
|
+ sw_txhdr->amsdu.nb = 1;
|
|
+ sw_txhdr->amsdu.pad = AMSDU_PADDING(len1);
|
|
+ if (bl_amsdu_add_subframe_header(bl_hw, skb, sw_txhdr))
|
|
+ goto end;
|
|
+
|
|
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_AMSDU;
|
|
+
|
|
+ if (sw_txhdr->amsdu.nb < bl_hw->mod_params->amsdu_maxnb)
|
|
+ txq->amsdu = sw_txhdr;
|
|
+ else
|
|
+ bl_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
|
|
+ }
|
|
+
|
|
+ res = true;
|
|
+
|
|
+ end:
|
|
+ spin_unlock_bh(&bl_hw->tx_lock);
|
|
+ return res;
|
|
+}
|
|
+#endif /* CONFIG_BL_AMSDUS_TX */
|
|
+
|
|
+int bl_requeue_multicast_skb(struct sk_buff *skb, struct bl_vif *bl_vif)
|
|
+{
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+ struct bl_txhdr *txhdr;
|
|
+ struct bl_sw_txhdr *sw_txhdr;
|
|
+ struct ethhdr *eth;
|
|
+ struct ethhdr tmp_eth;
|
|
+ struct txdesc_api *desc;
|
|
+ struct bl_sta *sta;
|
|
+ struct bl_txq *txq;
|
|
+ int headroom;
|
|
+ int hdr_pads;
|
|
+
|
|
+ u16 frame_len;
|
|
+ u16 frame_oft;
|
|
+ u8 tid;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ BL_DBG("1111bl_requeue_multicast_skb: skb=%p, skb->priority=%d\n", skb, skb->priority);
|
|
+
|
|
+ /* Get the STA id and TID information */
|
|
+ sta = bl_get_tx_info(bl_vif, skb, &tid);
|
|
+ if (!sta)
|
|
+ goto free;
|
|
+
|
|
+ txq = bl_txq_sta_get(sta, tid, NULL, bl_hw);
|
|
+
|
|
+ BL_DBG("requeue: txq->idx=%d, txq->status=%d, txq->ndev_idx=%d\n", txq->idx, txq->status, txq->ndev_idx);
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ if (bl_amsdu_add_subframe(bl_hw, skb, sta, txq))
|
|
+ return NETDEV_TX_OK;
|
|
+#endif
|
|
+
|
|
+ /* Retrieve the pointer to the Ethernet data */
|
|
+ eth = (struct ethhdr *)skb->data;
|
|
+ memcpy(tmp_eth.h_dest, eth->h_dest, ETH_ALEN);
|
|
+ memcpy(tmp_eth.h_source, eth->h_source, ETH_ALEN);
|
|
+ tmp_eth.h_proto = eth->h_proto;
|
|
+ hdr_pads = sizeof(struct ethhdr);
|
|
+ headroom = sizeof(struct bl_txhdr);
|
|
+
|
|
+ /* first we increase the area for tx descriptor */
|
|
+ skb_push(skb, sizeof(struct txdesc_api));
|
|
+ /* then we increase the area for sdio header*/
|
|
+ skb_push(skb, sizeof(struct sdio_hdr));
|
|
+ /* then we increase the area for tx header */
|
|
+ skb_push(skb, headroom);
|
|
+
|
|
+ txhdr = (struct bl_txhdr *)skb->data;
|
|
+ sw_txhdr = kmem_cache_alloc(bl_hw->sw_txhdr_cache, GFP_ATOMIC);
|
|
+ if (unlikely(sw_txhdr == NULL))
|
|
+ goto free;
|
|
+ txhdr->sw_hdr = sw_txhdr;
|
|
+ desc = &sw_txhdr->desc;
|
|
+
|
|
+ /* the frame len contains sdio special and actual skb->data */
|
|
+ frame_len = (u16)skb->len - headroom - sizeof(struct sdio_hdr)-sizeof(struct txdesc_api);
|
|
+
|
|
+ sw_txhdr->hdr.type = BL_TYPE_DATA;
|
|
+ sw_txhdr->hdr.len = frame_len + sizeof(struct txdesc_api);
|
|
+ sw_txhdr->hdr.queue_idx = txq->hwq->id;
|
|
+ sw_txhdr->hdr.reserved = 0x0;
|
|
+
|
|
+ sw_txhdr->txq = txq;
|
|
+ sw_txhdr->frame_len = frame_len;
|
|
+ sw_txhdr->bl_sta = sta;
|
|
+ sw_txhdr->bl_vif = bl_vif;
|
|
+ sw_txhdr->skb = skb;
|
|
+ sw_txhdr->headroom = headroom;
|
|
+ sw_txhdr->map_len = skb->len - offsetof(struct bl_txhdr, hw_hdr);
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ sw_txhdr->amsdu.len = 0;
|
|
+ sw_txhdr->amsdu.nb = 0;
|
|
+#endif
|
|
+ // Fill-in the descriptor
|
|
+ memcpy(&desc->host.eth_dest_addr, tmp_eth.h_dest, ETH_ALEN);
|
|
+ memcpy(&desc->host.eth_src_addr, tmp_eth.h_source, ETH_ALEN);
|
|
+ desc->host.ethertype = tmp_eth.h_proto;
|
|
+ BL_DBG("dest_addr:%04x%04x%04x\n", desc->host.eth_dest_addr.array[0], desc->host.eth_dest_addr.array[1], desc->host.eth_dest_addr.array[2]);
|
|
+ BL_DBG("src_addr:%04x%04x%04x\n", desc->host.eth_src_addr.array[0], desc->host.eth_src_addr.array[1], desc->host.eth_src_addr.array[2]);
|
|
+ BL_DBG("desc->host.ethertype=0x%x\n", desc->host.ethertype);
|
|
+ desc->host.staid = sta->sta_idx;
|
|
+ desc->host.tid = tid;
|
|
+ desc->host.host_hdr_pads = hdr_pads;
|
|
+ if (unlikely(bl_vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN)) {
|
|
+ desc->host.vif_idx = bl_vif->ap_vlan.master->vif_index;
|
|
+ } else {
|
|
+ desc->host.vif_idx = bl_vif->vif_index;
|
|
+ }
|
|
+
|
|
+ if (bl_vif->use_4addr && (sta->sta_idx < NX_REMOTE_STA_MAX))
|
|
+ desc->host.flags = TXU_CNTRL_USE_4ADDR;
|
|
+ else
|
|
+ desc->host.flags = 0;
|
|
+
|
|
+ if (bl_vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) {
|
|
+ if (bl_vif->is_resending) {
|
|
+ desc->host.flags |= TXU_CNTRL_MESH_FWD;
|
|
+ }
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ desc->host.packet_len[0] = frame_len;
|
|
+#else
|
|
+ desc->host.packet_len = frame_len;
|
|
+#endif
|
|
+
|
|
+ txhdr->hw_hdr.cfm.status.value = 0;
|
|
+
|
|
+ /* Fill-in TX descriptor */
|
|
+ frame_oft = sizeof(struct bl_txhdr) - offsetof(struct bl_txhdr, hw_hdr)
|
|
+ + sizeof(*eth);
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
|
|
+ desc->host.packet_cnt = 1;
|
|
+#else
|
|
+ desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
|
|
+#endif
|
|
+ //desc->host.status_desc_addr = sw_txhdr->dma_addr;
|
|
+
|
|
+ BL_DBG("requeue total length[%lu], sdio_txdesc[%lu], payload[%u]\n", frame_len+sizeof(struct sdio_hdr)+sizeof(struct txdesc_api), sizeof(struct sdio_hdr)+sizeof(struct txdesc_api), frame_len);
|
|
+ BL_DBG("requeue sdio_hdr: [%04x %04x %04x %04x]\n", sw_txhdr->hdr.type, sw_txhdr->hdr.len, sw_txhdr->hdr.queue_idx, sw_txhdr->hdr.reserved);
|
|
+ BL_DBG("txdesc: %08x%08x%04x%08x%04x%04x%04x%04x%04x%04x%04x%04x%04x%04x%04x%04x%04x%02x%02x%02x%04x\n", \
|
|
+ desc->ready, desc->host.packet_addr, desc->host.packet_len, desc->host.host_hdr_pads, desc->host.eth_dest_addr.array[0], desc->host.eth_dest_addr.array[1], desc->host.eth_dest_addr.array[2], \
|
|
+ desc->host.eth_src_addr.array[0], desc->host.eth_src_addr.array[1], desc->host.eth_src_addr.array[2],desc->host.ethertype, desc->host.pn[0], desc->host.pn[1], desc->host.pn[2], desc->host.pn[3], \
|
|
+ desc->host.sn, desc->host.timestamp, desc->host.tid, desc->host.vif_idx, desc->host.staid, desc->host.flags);
|
|
+ BL_DBG("requeue sw_txhdr: frame_len[%04x], headroom[%04x]\n", sw_txhdr->frame_len,sw_txhdr->headroom);
|
|
+ BL_DBG("requeue hw_txhdr: staid[%02x]vif_idx[%02x]tid[%02x]flags[%04x]\n", desc->host.staid, desc->host.vif_idx, desc->host.tid, desc->host.flags);
|
|
+ BL_DBG("requeue skb=%p\n", skb);
|
|
+ spin_lock_bh(&bl_hw->txq_lock);
|
|
+ if (bl_txq_queue_skb(skb, txq, bl_hw, false)) {
|
|
+ BL_DBG("rwqueue multicast_skb success!\n");
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ //bl_queue_main_work(bl_hw);
|
|
+ } else {
|
|
+ BL_DBG("rwqueue multicast_skb success failed");
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ return NETDEV_TX_OK;
|
|
+ }
|
|
+
|
|
+ return NETDEV_TX_OK;
|
|
+
|
|
+free:
|
|
+ dev_kfree_skb_any(skb);
|
|
+
|
|
+ return NETDEV_TX_OK;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
|
|
+ * struct net_device *dev);
|
|
+ * Called when a packet needs to be transmitted.
|
|
+ * Must return NETDEV_TX_OK , NETDEV_TX_BUSY.
|
|
+ * (can also return NETDEV_TX_LOCKED if NETIF_F_LLTX)
|
|
+ *
|
|
+ * - Initialize the desciptor for this pkt (stored in skb before data)
|
|
+ * - Push the pkt in the corresponding Txq
|
|
+ * - If possible (i.e. credit available and not in PS) the pkt is pushed
|
|
+ * to fw
|
|
+ */
|
|
+netdev_tx_t bl_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
+{
|
|
+ struct bl_vif *bl_vif = netdev_priv(dev);
|
|
+ struct bl_hw *bl_hw = bl_vif->bl_hw;
|
|
+ struct bl_txhdr *txhdr;
|
|
+ struct bl_sw_txhdr *sw_txhdr;
|
|
+ struct ethhdr *eth;
|
|
+ struct ethhdr tmp_eth;
|
|
+ struct txdesc_api *desc;
|
|
+ struct bl_sta *sta;
|
|
+ struct bl_txq *txq;
|
|
+ int headroom;
|
|
+ int max_headroom;
|
|
+ int hdr_pads;
|
|
+
|
|
+ u16 frame_len;
|
|
+ u16 frame_oft;
|
|
+ u8 tid;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ max_headroom = sizeof(struct bl_txhdr)+sizeof(struct txdesc_api) + sizeof(struct sdio_hdr);
|
|
+
|
|
+ /* check whether the current skb can be used */
|
|
+ if (skb_shared(skb) || (skb_headroom(skb) < max_headroom)) {
|
|
+ struct sk_buff *newskb = skb_copy_expand(skb, max_headroom, 0,
|
|
+ GFP_ATOMIC);
|
|
+ if (unlikely(newskb == NULL))
|
|
+ goto free;
|
|
+
|
|
+ dev_kfree_skb_any(skb);
|
|
+
|
|
+ skb = newskb;
|
|
+ }
|
|
+
|
|
+ /* Get the STA id and TID information */
|
|
+ sta = bl_get_tx_info(bl_vif, skb, &tid);
|
|
+ if (!sta)
|
|
+ goto free;
|
|
+
|
|
+ txq = bl_txq_sta_get(sta, tid, NULL, bl_hw);
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ if (bl_amsdu_add_subframe(bl_hw, skb, sta, txq))
|
|
+ return NETDEV_TX_OK;
|
|
+#endif
|
|
+
|
|
+ /* Retrieve the pointer to the Ethernet data */
|
|
+ eth = (struct ethhdr *)skb->data;
|
|
+ memcpy(tmp_eth.h_dest, eth->h_dest, ETH_ALEN);
|
|
+ memcpy(tmp_eth.h_source, eth->h_source, ETH_ALEN);
|
|
+ tmp_eth.h_proto = eth->h_proto;
|
|
+
|
|
+ hdr_pads = sizeof(struct ethhdr);
|
|
+ headroom = sizeof(struct bl_txhdr);
|
|
+
|
|
+ /* first we increase the area for tx descriptor */
|
|
+ skb_push(skb, sizeof(struct txdesc_api));
|
|
+ /* then we increase the area for sdio header*/
|
|
+ skb_push(skb, sizeof(struct sdio_hdr));
|
|
+ /* then we increase the area for tx header */
|
|
+ skb_push(skb, headroom);
|
|
+
|
|
+ txhdr = (struct bl_txhdr *)skb->data;
|
|
+ sw_txhdr = kmem_cache_alloc(bl_hw->sw_txhdr_cache, GFP_ATOMIC);
|
|
+ if (unlikely(sw_txhdr == NULL))
|
|
+ goto free;
|
|
+ txhdr->sw_hdr = sw_txhdr;
|
|
+ desc = &sw_txhdr->desc;
|
|
+
|
|
+ /* the frame len contains sdio special and actual skb->data */
|
|
+ frame_len = (u16)skb->len - headroom - sizeof(struct sdio_hdr)-sizeof(struct txdesc_api) - hdr_pads;
|
|
+
|
|
+ sw_txhdr->hdr.type = BL_TYPE_DATA;
|
|
+ sw_txhdr->hdr.len = skb->len - headroom;
|
|
+ sw_txhdr->hdr.queue_idx = txq->hwq->id;
|
|
+ sw_txhdr->hdr.reserved = g_pkt_sn;
|
|
+
|
|
+ BL_DBG("xmit_pkt_sn: hw_idx=%d, frame_len=%u, sn=%u\n",txq->hwq->id, frame_len, g_pkt_sn);
|
|
+ g_pkt_sn++;
|
|
+
|
|
+ sw_txhdr->txq = txq;
|
|
+ sw_txhdr->frame_len = frame_len;
|
|
+ sw_txhdr->bl_sta = sta;
|
|
+ sw_txhdr->bl_vif = bl_vif;
|
|
+ sw_txhdr->skb = skb;
|
|
+ sw_txhdr->headroom = headroom;
|
|
+ sw_txhdr->map_len = skb->len - offsetof(struct bl_txhdr, hw_hdr);
|
|
+
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ sw_txhdr->amsdu.len = 0;
|
|
+ sw_txhdr->amsdu.nb = 0;
|
|
+#endif
|
|
+ // Fill-in the descriptor
|
|
+ memcpy(&desc->host.eth_dest_addr, tmp_eth.h_dest, ETH_ALEN);
|
|
+ memcpy(&desc->host.eth_src_addr, tmp_eth.h_source, ETH_ALEN);
|
|
+ desc->host.ethertype = tmp_eth.h_proto;
|
|
+ desc->host.staid = sta->sta_idx;
|
|
+ desc->host.tid = tid;
|
|
+ desc->host.host_hdr_pads = hdr_pads;
|
|
+ if (unlikely(bl_vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN)) {
|
|
+ desc->host.vif_idx = bl_vif->ap_vlan.master->vif_index;
|
|
+ } else {
|
|
+ desc->host.vif_idx = bl_vif->vif_index;
|
|
+ }
|
|
+
|
|
+ if (bl_vif->use_4addr && (sta->sta_idx < NX_REMOTE_STA_MAX))
|
|
+ desc->host.flags = TXU_CNTRL_USE_4ADDR;
|
|
+ else
|
|
+ desc->host.flags = 0;
|
|
+
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ desc->host.packet_len[0] = frame_len;
|
|
+#else
|
|
+ desc->host.packet_len = frame_len;
|
|
+#endif
|
|
+
|
|
+ txhdr->hw_hdr.cfm.status.value = 0;
|
|
+
|
|
+ /* Fill-in TX descriptor */
|
|
+ frame_oft = sizeof(struct bl_txhdr) - offsetof(struct bl_txhdr, hw_hdr) + sizeof(*eth);
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
|
|
+ desc->host.packet_cnt = 1;
|
|
+#else
|
|
+ desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
|
|
+#endif
|
|
+
|
|
+ BL_DBG_MOD_LEVEL(bl_hw, D_TX D_INF "bl_start_xmit: queue skb=%p on hwq->idx=%d, txq->idx=%d\n", skb, sw_txhdr->hdr.queue_idx, txq->idx);
|
|
+
|
|
+ spin_lock_bh(&bl_hw->txq_lock);
|
|
+ if (bl_txq_queue_skb(skb, txq, bl_hw, false)) {
|
|
+ BL_DBG("start_xmit: queue_skb success!\n");
|
|
+ bl_queue_main_work(bl_hw);
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ } else {
|
|
+ /*when in ps mode, it just add skb into txq, but donot add txq in hwq*/
|
|
+ BL_DBG("start_xmit: queue_skb success failed");
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ return NETDEV_TX_OK;
|
|
+ }
|
|
+
|
|
+ return NETDEV_TX_OK;
|
|
+
|
|
+free:
|
|
+ dev_kfree_skb_any(skb);
|
|
+
|
|
+ return NETDEV_TX_OK;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_start_mgmt_xmit - Transmit a management frame
|
|
+ *
|
|
+ * @vif: Vif that send the frame
|
|
+ * @sta: Destination of the frame. May be NULL if the destiantion is unknown
|
|
+ * to the AP.
|
|
+ * @params: Mgmt frame parameters
|
|
+ * @offchan: Indicate whether the frame must be send via the offchan TXQ.
|
|
+ * (is is redundant with params->offchan ?)
|
|
+ * @cookie: updated with a unique value to identify the frame with upper layer
|
|
+ *
|
|
+ */
|
|
+int bl_start_mgmt_xmit(struct bl_vif *vif, struct bl_sta *sta,
|
|
+ struct cfg80211_mgmt_tx_params *params, bool offchan,
|
|
+ u64 *cookie)
|
|
+{
|
|
+ struct bl_hw *bl_hw = vif->bl_hw;
|
|
+ struct bl_txhdr *txhdr;
|
|
+ struct bl_sw_txhdr *sw_txhdr;
|
|
+ struct txdesc_api *desc;
|
|
+ struct sk_buff *skb;
|
|
+ u16 frame_len, headroom, frame_oft;
|
|
+ u8 *data;
|
|
+ struct bl_txq *txq;
|
|
+ bool robust;
|
|
+ int i;
|
|
+ int hdr_pads;
|
|
+
|
|
+ BL_DBG(BL_FN_ENTRY_STR);
|
|
+
|
|
+ headroom = sizeof(struct bl_txhdr) + sizeof(struct txdesc_api) + sizeof(struct sdio_hdr);
|
|
+ frame_len = params->len;
|
|
+
|
|
+ /* Set TID and Queues indexes */
|
|
+ if (sta) {
|
|
+ txq = bl_txq_sta_get(sta, 8, NULL, bl_hw);
|
|
+ } else {
|
|
+ if (offchan) {
|
|
+ txq = &bl_hw->txq[NX_OFF_CHAN_TXQ_IDX];
|
|
+ } else {
|
|
+ txq = bl_txq_vif_get(vif, NX_UNK_TXQ_TYPE, NULL);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Ensure that TXQ is active */
|
|
+ if (txq->idx == TXQ_INACTIVE) {
|
|
+ netdev_dbg(vif->ndev, "TXQ inactive\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ /* Create a SK Buff object that will contain the provided data */
|
|
+ skb = dev_alloc_skb(headroom + frame_len);
|
|
+ if (!skb) {
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+
|
|
+ *cookie = (unsigned long)skb;
|
|
+
|
|
+ /*
|
|
+ * Move skb->data pointer in order to reserve room for bl_txhdr
|
|
+ * headroom value will be equal to sizeof(struct bl_txhdr)
|
|
+ */
|
|
+ skb_reserve(skb, headroom);
|
|
+
|
|
+ /*
|
|
+ * Extend the buffer data area in order to contain the provided packet
|
|
+ * len value (for skb) will be equal to param->len
|
|
+ */
|
|
+ data = skb_put(skb, frame_len);
|
|
+ /* Copy the provided data */
|
|
+ memcpy(data, params->buf, frame_len);
|
|
+ robust = ieee80211_is_robust_mgmt_frame(skb);
|
|
+
|
|
+ /* Update CSA counter if present */
|
|
+ if (unlikely(params->n_csa_offsets) &&
|
|
+ vif->wdev.iftype == NL80211_IFTYPE_AP &&
|
|
+ vif->ap.csa) {
|
|
+ data = skb->data;
|
|
+ for (i = 0; i < params->n_csa_offsets ; i++) {
|
|
+ data[params->csa_offsets[i]] = vif->ap.csa->count;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ hdr_pads = BL_SWTXHDR_ALIGN_PADS((long)(skb->data)-sizeof(struct txdesc_api)-sizeof(struct sdio_hdr));
|
|
+
|
|
+ /*
|
|
+ * Go back to the beginning of the allocated data area
|
|
+ * skb->data pointer will move backward
|
|
+ */
|
|
+ skb_push(skb, hdr_pads);
|
|
+ /* first we increase the area for tx descriptor */
|
|
+ skb_push(skb, sizeof(struct txdesc_api));
|
|
+ /* then we increase the area for sdio header*/
|
|
+ skb_push(skb, sizeof(struct sdio_hdr));
|
|
+ /* then we increase the area for tx header */
|
|
+ skb_push(skb, headroom);
|
|
+
|
|
+ /* Fill the TX Header */
|
|
+ txhdr = (struct bl_txhdr *)skb->data;
|
|
+ txhdr->hw_hdr.cfm.status.value = 0;
|
|
+
|
|
+ /* Fill the SW TX Header */
|
|
+ sw_txhdr = kmem_cache_alloc(bl_hw->sw_txhdr_cache, GFP_ATOMIC);
|
|
+ if (unlikely(sw_txhdr == NULL)) {
|
|
+ dev_kfree_skb(skb);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ txhdr->sw_hdr = sw_txhdr;
|
|
+
|
|
+ sw_txhdr->hdr.type = BL_TYPE_DATA;
|
|
+ sw_txhdr->hdr.len = frame_len + sizeof(struct txdesc_api);
|
|
+ sw_txhdr->hdr.queue_idx = txq->hwq->id;
|
|
+ sw_txhdr->hdr.reserved = g_pkt_sn;
|
|
+
|
|
+ BL_DBG("xmit_pkt_sn: hw_idx=%d, frame_len=%u, sn=%u\n",txq->hwq->id, frame_len, g_pkt_sn);
|
|
+ g_pkt_sn++;
|
|
+
|
|
+ sw_txhdr->txq = txq;
|
|
+ sw_txhdr->frame_len = frame_len;
|
|
+ sw_txhdr->bl_sta = sta;
|
|
+ sw_txhdr->bl_vif = vif;
|
|
+ sw_txhdr->skb = skb;
|
|
+ sw_txhdr->headroom = headroom;
|
|
+ sw_txhdr->map_len = skb->len - offsetof(struct bl_txhdr, hw_hdr);
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ sw_txhdr->amsdu.len = 0;
|
|
+ sw_txhdr->amsdu.nb = 0;
|
|
+#endif
|
|
+
|
|
+ /* Fill the Descriptor to be provided to the MAC SW */
|
|
+ desc = &sw_txhdr->desc;
|
|
+
|
|
+ desc->host.staid = (sta) ? sta->sta_idx : 0xFF;
|
|
+ desc->host.vif_idx = vif->vif_index;
|
|
+ desc->host.tid = 0xFF;
|
|
+ desc->host.host_hdr_pads = hdr_pads;
|
|
+ desc->host.flags = TXU_CNTRL_MGMT;
|
|
+ if (robust)
|
|
+ desc->host.flags |= TXU_CNTRL_MGMT_ROBUST;
|
|
+
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ desc->host.packet_len[0] = frame_len;
|
|
+#else
|
|
+ desc->host.packet_len = frame_len;
|
|
+#endif
|
|
+
|
|
+ if (params->no_cck) {
|
|
+ desc->host.flags |= TXU_CNTRL_MGMT_NO_CCK;
|
|
+ }
|
|
+
|
|
+ frame_oft = sizeof(struct bl_txhdr) - offsetof(struct bl_txhdr, hw_hdr);
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
|
|
+ desc->host.packet_cnt = 1;
|
|
+#else
|
|
+ desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
|
|
+#endif
|
|
+
|
|
+ BL_DBG("mgmt_xmit: queue_skb=%p\n", skb);
|
|
+ spin_lock_bh(&bl_hw->txq_lock);
|
|
+ if (bl_txq_queue_skb(skb, txq, bl_hw, false)) {
|
|
+ BL_DBG("mgmt_xmit: queue_skb success!\n");
|
|
+ bl_queue_main_work(bl_hw);
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ } else {
|
|
+ /*when in ps mode, it just add skb into txq, but donot add txq in hwq*/
|
|
+ BL_DBG("mgmt_xmit: queue_skb success failed");
|
|
+ spin_unlock_bh(&bl_hw->txq_lock);
|
|
+ return NETDEV_TX_OK;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txdatacfm - FW callback for TX confirmation
|
|
+ *
|
|
+ * called with tx_lock hold
|
|
+ */
|
|
+int bl_txdatacfm(void *pthis, void *host_id, void *_hw_hdr, void **txq_saved)
|
|
+{
|
|
+ struct bl_hw *bl_hw = (struct bl_hw *)pthis;
|
|
+ struct bl_hw_txhdr *hw_hdr = (struct bl_hw_txhdr *)_hw_hdr;
|
|
+ struct sk_buff *skb = host_id;
|
|
+ struct bl_txhdr *txhdr;
|
|
+ union bl_hw_txstatus bl_txst;
|
|
+ struct bl_sw_txhdr *sw_txhdr;
|
|
+ struct bl_hwq *hwq;
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ txhdr = (struct bl_txhdr *)skb->data;
|
|
+ sw_txhdr = txhdr->sw_hdr;
|
|
+ bl_txst = hw_hdr->cfm.status;
|
|
+
|
|
+ memcpy(&txhdr->hw_hdr, hw_hdr, sizeof(struct bl_hw_txhdr));
|
|
+
|
|
+ txq = sw_txhdr->txq;
|
|
+ *txq_saved = txq;
|
|
+ /* don't use txq->hwq as it may have changed between push and confirm */
|
|
+ hwq = &bl_hw->hwq[sw_txhdr->hw_queue];
|
|
+ if(hwq == NULL) {
|
|
+ printk("bl_txdatacfm: hwq is NULL!\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ bl_txq_confirm_any(bl_hw, txq, hwq, sw_txhdr);
|
|
+
|
|
+ /* Update txq and HW queue credits */
|
|
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT) {
|
|
+ /* For debug purpose (use ftrace kernel option) */
|
|
+ trace_mgmt_cfm(sw_txhdr->bl_vif->vif_index,
|
|
+ (sw_txhdr->bl_sta) ? sw_txhdr->bl_sta->sta_idx : 0xFF,
|
|
+ !(bl_txst.retry_required || bl_txst.sw_retry_required));
|
|
+
|
|
+ /* Confirm transmission to CFG80211 */
|
|
+ cfg80211_mgmt_tx_status(&sw_txhdr->bl_vif->wdev,
|
|
+ (unsigned long)skb,
|
|
+ (skb->data + sw_txhdr->headroom + sizeof(struct txdesc_api) + sizeof(struct sdio_hdr)),
|
|
+ sw_txhdr->frame_len,
|
|
+ !(bl_txst.retry_required || bl_txst.sw_retry_required),
|
|
+ GFP_ATOMIC);
|
|
+ } else if ((txq->idx != TXQ_INACTIVE) &&
|
|
+ (bl_txst.retry_required || bl_txst.sw_retry_required)) {
|
|
+ bool sw_retry = (bl_txst.sw_retry_required) ? true : false;
|
|
+
|
|
+ /* Reset the status */
|
|
+ txhdr->hw_hdr.cfm.status.value = 0;
|
|
+
|
|
+ /* The confirmed packet was part of an AMPDU and not acked
|
|
+ * correctly, so reinject it in the TX path to be retried */
|
|
+ bl_tx_retry(bl_hw, skb, txhdr, sw_retry);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Update statistics */
|
|
+ sw_txhdr->bl_vif->net_stats.tx_packets++;
|
|
+ sw_txhdr->bl_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
|
|
+
|
|
+ /* Release SKBs */
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU) {
|
|
+ struct bl_amsdu_txhdr *amsdu_txhdr;
|
|
+ list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
|
|
+ bl_amsdu_del_subframe_header(amsdu_txhdr);
|
|
+ dma_unmap_single(bl_hw->dev, amsdu_txhdr->dma_addr,
|
|
+ amsdu_txhdr->map_len, DMA_TO_DEVICE);
|
|
+ consume_skb(amsdu_txhdr->skb);
|
|
+ }
|
|
+ }
|
|
+#endif /* CONFIG_BL_AMSDUS_TX */
|
|
+
|
|
+ kmem_cache_free(bl_hw->sw_txhdr_cache, sw_txhdr);
|
|
+ skb_pull(skb, sw_txhdr->headroom);
|
|
+ BL_DBG("txdata_cfm, free skb=%p, sn=%u\n", skb, hw_hdr->cfm.sn);
|
|
+ consume_skb(skb);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * bl_txq_credit_update - Update credit for one txq
|
|
+ *
|
|
+ * @bl_hw: Driver main data
|
|
+ * @sta_idx: STA idx
|
|
+ * @tid: TID
|
|
+ * @update: offset to apply in txq credits
|
|
+ *
|
|
+ * Called when fw send ME_TX_CREDITS_UPDATE_IND message.
|
|
+ * Apply @update to txq credits, and stop/start the txq if needed
|
|
+ */
|
|
+void bl_txq_credit_update(struct bl_hw *bl_hw, int sta_idx, u8 tid,
|
|
+ s8 update)
|
|
+{
|
|
+ struct bl_sta *sta = &bl_hw->sta_table[sta_idx];
|
|
+ struct bl_txq *txq;
|
|
+
|
|
+ txq = bl_txq_sta_get(sta, tid, NULL, bl_hw);
|
|
+
|
|
+ spin_lock(&bl_hw->tx_lock);
|
|
+
|
|
+ if (txq->idx != TXQ_INACTIVE) {
|
|
+ txq->credits += update;
|
|
+ trace_credit_update(txq, update);
|
|
+ /* if (txq->credits <= 0)
|
|
+ bl_txq_stop(txq, BL_TXQ_STOP_FULL);
|
|
+ else
|
|
+ bl_txq_start(txq, BL_TXQ_STOP_FULL);*/
|
|
+ }
|
|
+
|
|
+ spin_unlock(&bl_hw->tx_lock);
|
|
+}
|
|
diff -Naur /dev/null/bl_tx.h b/drivers/net/wireless/hflps170/fullmac/bl_tx.h
|
|
--- /dev/null/bl_tx.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/fullmac/bl_tx.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,177 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file bl_tx.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#ifndef _BL_TX_H_
|
|
+#define _BL_TX_H_
|
|
+
|
|
+#include <linux/version.h>
|
|
+#include <linux/ieee80211.h>
|
|
+#include <net/cfg80211.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include "lmac_types.h"
|
|
+#include "lmac_msg.h"
|
|
+#include "ipc_shared.h"
|
|
+#include "bl_txq.h"
|
|
+#include "hal_desc.h"
|
|
+
|
|
+#define BL_HWQ_BK 0
|
|
+#define BL_HWQ_BE 1
|
|
+#define BL_HWQ_VI 2
|
|
+#define BL_HWQ_VO 3
|
|
+#define BL_HWQ_BCMC 4
|
|
+#define BL_HWQ_NB NX_TXQ_CNT
|
|
+#define BL_HWQ_ALL_ACS (BL_HWQ_BK | BL_HWQ_BE | BL_HWQ_VI | BL_HWQ_VO)
|
|
+#define BL_HWQ_ALL_ACS_BIT ( BIT(BL_HWQ_BK) | BIT(BL_HWQ_BE) | \
|
|
+ BIT(BL_HWQ_VI) | BIT(BL_HWQ_VO) )
|
|
+
|
|
+#define BL_TX_LIFETIME_MS 100
|
|
+
|
|
+#define BL_SWTXHDR_ALIGN_SZ 4
|
|
+#define BL_SWTXHDR_ALIGN_MSK (BL_SWTXHDR_ALIGN_SZ - 1)
|
|
+#define BL_SWTXHDR_ALIGN_PADS(x) \
|
|
+ ((BL_SWTXHDR_ALIGN_SZ - ((x) & BL_SWTXHDR_ALIGN_MSK)) \
|
|
+ & BL_SWTXHDR_ALIGN_MSK)
|
|
+#if BL_SWTXHDR_ALIGN_SZ & BL_SWTXHDR_ALIGN_MSK
|
|
+#error bad BL_SWTXHDR_ALIGN_SZ
|
|
+#endif
|
|
+
|
|
+#define AMSDU_PADDING(x) ((4 - ((x) & 0x3)) & 0x3)
|
|
+
|
|
+#define TXU_CNTRL_RETRY BIT(0)
|
|
+#define TXU_CNTRL_MORE_DATA BIT(2)
|
|
+#define TXU_CNTRL_MGMT BIT(3)
|
|
+#define TXU_CNTRL_MGMT_NO_CCK BIT(4)
|
|
+#define TXU_CNTRL_AMSDU BIT(6)
|
|
+#define TXU_CNTRL_MGMT_ROBUST BIT(7)
|
|
+#define TXU_CNTRL_USE_4ADDR BIT(8)
|
|
+#define TXU_CNTRL_EOSP BIT(9)
|
|
+#define TXU_CNTRL_MESH_FWD BIT(10)
|
|
+#define TXU_CNTRL_TDLS BIT(11)
|
|
+
|
|
+extern const int bl_tid2hwq[IEEE80211_NUM_TIDS];
|
|
+
|
|
+/**
|
|
+ * struct bl_amsdu_txhdr - Structure added in skb headroom (instead of
|
|
+ * bl_txhdr) for amsdu subframe buffer (except for the first subframe
|
|
+ * that has a normal bl_txhdr)
|
|
+ *
|
|
+ * @list List of other amsdu subframe (bl_sw_txhdr.amsdu.hdrs)
|
|
+ * @map_len Length to be downloaded for this subframe
|
|
+ * @dma_addr Buffer address form embedded point of view
|
|
+ * @skb skb
|
|
+ * @pad padding added before this subframe
|
|
+ * (only use when amsdu must be dismantled)
|
|
+ */
|
|
+struct bl_amsdu_txhdr {
|
|
+ struct list_head list;
|
|
+ size_t map_len;
|
|
+ dma_addr_t dma_addr;
|
|
+ struct sk_buff *skb;
|
|
+ u16 pad;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct bl_amsdu - Structure to manage creation of an A-MSDU, updated
|
|
+ * only In the first subframe of an A-MSDU
|
|
+ *
|
|
+ * @hdrs List of subframe of bl_amsdu_txhdr
|
|
+ * @len Current size for this A-MDSU (doesn't take padding into account)
|
|
+ * 0 means that no amsdu is in progress
|
|
+ * @nb Number of subframe in the amsdu
|
|
+ * @pad Padding to add before adding a new subframe
|
|
+ */
|
|
+struct bl_amsdu {
|
|
+ struct list_head hdrs;
|
|
+ u16 len;
|
|
+ u8 nb;
|
|
+ u8 pad;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct bl_sw_txhdr - Software part of tx header
|
|
+ *
|
|
+ * @bl_sta sta to which this buffer is addressed
|
|
+ * @bl_vif vif that send the buffer
|
|
+ * @txq pointer to TXQ used to send the buffer
|
|
+ * @hw_queue Index of the HWQ used to push the buffer.
|
|
+ * May be different than txq->hwq->id on confirmation.
|
|
+ * @frame_len Size of the frame (doesn't not include mac header)
|
|
+ * (Only used to update stat, can't we use skb->len instead ?)
|
|
+ * @headroom Headroom added in skb to add bl_txhdr
|
|
+ * (Only used to remove it before freeing skb, is it needed ?)
|
|
+ * @amsdu Description of amsdu whose first subframe is this buffer
|
|
+ * (amsdu.nb = 0 means this buffer is not part of amsdu)
|
|
+ * @skb skb received from transmission
|
|
+ * @map_len Length mapped for DMA (only bl_hw_txhdr and data are mapped)
|
|
+ * @dma_addr DMA address after mapping
|
|
+ * @desc Buffer description that will be copied in shared mem for FW
|
|
+ */
|
|
+struct bl_sw_txhdr {
|
|
+ struct bl_sta *bl_sta;
|
|
+ struct bl_vif *bl_vif;
|
|
+ struct bl_txq *txq;
|
|
+ u8 hw_queue;
|
|
+ u16 frame_len;
|
|
+ u16 headroom;
|
|
+#ifdef CONFIG_BL_AMSDUS_TX
|
|
+ struct bl_amsdu amsdu;
|
|
+#endif
|
|
+ struct sk_buff *skb;
|
|
+
|
|
+ size_t map_len;
|
|
+ dma_addr_t dma_addr;
|
|
+ struct sdio_hdr hdr;
|
|
+ struct txdesc_api desc;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct bl_txhdr - Stucture to control transimission of packet
|
|
+ * (Added in skb headroom)
|
|
+ *
|
|
+ * @sw_hdr: Information from driver
|
|
+ * @cache_guard:
|
|
+ * @hw_hdr: Information for/from hardware
|
|
+ */
|
|
+struct bl_txhdr {
|
|
+ struct bl_sw_txhdr *sw_hdr;
|
|
+ char cache_guard[L1_CACHE_BYTES];
|
|
+ struct bl_hw_txhdr hw_hdr;
|
|
+};
|
|
+
|
|
+int bl_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
|
+int bl_requeue_multicast_skb(struct sk_buff *skb, struct bl_vif *bl_vif);
|
|
+int bl_start_mgmt_xmit(struct bl_vif *vif, struct bl_sta *sta,
|
|
+ struct cfg80211_mgmt_tx_params *params, bool offchan,
|
|
+ u64 *cookie);
|
|
+int bl_txdatacfm(void *pthis, void *host_id, void *data1, void **data2);
|
|
+
|
|
+struct bl_hw;
|
|
+struct bl_sta;
|
|
+void bl_set_traffic_status(struct bl_hw *bl_hw,
|
|
+ struct bl_sta *sta,
|
|
+ bool available,
|
|
+ u8 ps_id);
|
|
+void bl_ps_bh_enable(struct bl_hw *bl_hw, struct bl_sta *sta,
|
|
+ bool enable);
|
|
+void bl_ps_bh_traffic_req(struct bl_hw *bl_hw, struct bl_sta *sta,
|
|
+ u16 pkt_req, u8 ps_id);
|
|
+
|
|
+void bl_switch_vif_sta_txq(struct bl_sta *sta, struct bl_vif *old_vif,
|
|
+ struct bl_vif *new_vif);
|
|
+
|
|
+int bl_dbgfs_print_sta(char *buf, size_t size, struct bl_sta *sta,
|
|
+ struct bl_hw *bl_hw);
|
|
+void bl_txq_credit_update(struct bl_hw *bl_hw, int sta_idx, u8 tid,
|
|
+ s8 update);
|
|
+void bl_tx_push(struct bl_hw *bl_hw, struct bl_txhdr *txhdr, int flags);
|
|
+void bl_tx_multi_pkt_push(struct bl_hw *bl_hw, struct sk_buff_head *sk_list_push);
|
|
+
|
|
+void bl_downgrade_ac(struct bl_sta *sta, struct sk_buff *skb);
|
|
+
|
|
+#endif /* _BL_TX_H_ */
|
|
diff -Naur /dev/null_desc.h b/drivers/net/wireless/hflps170/hal_desc.h
|
|
--- /dev/null_desc.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/hal_desc.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,265 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file hal_desc.h
|
|
+ *
|
|
+ * @brief File containing the definition of HW descriptors.
|
|
+ *
|
|
+ * Contains the definition and structures used by HW
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _HAL_DESC_H_
|
|
+#define _HAL_DESC_H_
|
|
+
|
|
+#include "lmac_types.h"
|
|
+
|
|
+/* Rate and policy table */
|
|
+
|
|
+#define N_CCK 8
|
|
+#define N_OFDM 8
|
|
+#define N_HT (8 * 2 * 2 * 4)
|
|
+#define N_VHT (10 * 4 * 2 * 8)
|
|
+#define N_RATE (N_CCK + N_OFDM + N_HT + N_VHT)
|
|
+
|
|
+/* Values for bwTx */
|
|
+#define __CHBW_CBW20 0
|
|
+#define __CHBW_CBW40 1
|
|
+#define __CHBW_CBW80 2
|
|
+#define __CHBW_CBW160 3
|
|
+
|
|
+/* Values for formatModTx */
|
|
+#define FORMATMOD_NON_HT 0
|
|
+#define FORMATMOD_NON_HT_DUP_OFDM 1
|
|
+#define FORMATMOD_HT_MF 2
|
|
+#define FORMATMOD_HT_GF 3
|
|
+#define FORMATMOD_VHT 4
|
|
+
|
|
+/* Values for navProtFrmEx */
|
|
+#define NAV_PROT_NO_PROT_BIT 0
|
|
+#define NAV_PROT_SELF_CTS_BIT 1
|
|
+#define NAV_PROT_RTS_CTS_BIT 2
|
|
+#define NAV_PROT_RTS_CTS_WITH_QAP_BIT 3
|
|
+#define NAV_PROT_STBC_BIT 4
|
|
+
|
|
+union bl_mcs_index {
|
|
+ struct {
|
|
+ u32 mcs : 3;
|
|
+ u32 nss : 2;
|
|
+ } ht;
|
|
+ struct {
|
|
+ u32 mcs : 4;
|
|
+ u32 nss : 3;
|
|
+ } vht;
|
|
+ u32 legacy : 7;
|
|
+};
|
|
+
|
|
+/* c.f RW-WLAN-nX-MAC-HW-UM */
|
|
+union bl_rate_ctrl_info {
|
|
+ struct {
|
|
+ u32 mcsIndexTx : 7;
|
|
+ u32 bwTx : 2;
|
|
+ u32 shortGITx : 1;
|
|
+ u32 preTypeTx : 1;
|
|
+ u32 formatModTx : 3;
|
|
+ u32 navProtFrmEx : 3;
|
|
+ u32 mcsIndexProtTx : 7;
|
|
+ u32 bwProtTx : 2;
|
|
+ u32 formatModProtTx : 3;
|
|
+ u32 nRetry : 3;
|
|
+ };
|
|
+ u32 value;
|
|
+};
|
|
+
|
|
+/* c.f RW-WLAN-nX-MAC-HW-UM */
|
|
+struct bl_power_ctrl_info {
|
|
+ u32 txPwrLevelPT : 8;
|
|
+ u32 txPwrLevelProtPT : 8;
|
|
+ u32 reserved :16;
|
|
+};
|
|
+
|
|
+/* c.f RW-WLAN-nX-MAC-HW-UM */
|
|
+union bl_pol_phy_ctrl_info_1 {
|
|
+ struct {
|
|
+ u32 rsvd1 : 3;
|
|
+ u32 bfFrmEx : 1;
|
|
+ u32 numExtnSS : 2;
|
|
+ u32 fecCoding : 1;
|
|
+ u32 stbc : 2;
|
|
+ u32 rsvd2 : 5;
|
|
+ u32 nTx : 3;
|
|
+ u32 nTxProt : 3;
|
|
+ };
|
|
+ u32 value;
|
|
+};
|
|
+
|
|
+/* c.f RW-WLAN-nX-MAC-HW-UM */
|
|
+union bl_pol_phy_ctrl_info_2 {
|
|
+ struct {
|
|
+ u32 antennaSet : 8;
|
|
+ u32 smmIndex : 8;
|
|
+ u32 beamFormed : 1;
|
|
+ };
|
|
+ u32 value;
|
|
+};
|
|
+
|
|
+/* c.f RW-WLAN-nX-MAC-HW-UM */
|
|
+union bl_pol_mac_ctrl_info_1 {
|
|
+ struct {
|
|
+ u32 keySRamIndex : 10;
|
|
+ u32 keySRamIndexRA : 10;
|
|
+ };
|
|
+ u32 value;
|
|
+};
|
|
+
|
|
+/* c.f RW-WLAN-nX-MAC-HW-UM */
|
|
+union bl_pol_mac_ctrl_info_2 {
|
|
+ struct {
|
|
+ u32 longRetryLimit : 8;
|
|
+ u32 shortRetryLimit : 8;
|
|
+ u32 rtsThreshold : 12;
|
|
+ };
|
|
+ u32 value;
|
|
+};
|
|
+
|
|
+#define POLICY_TABLE_PATTERN 0xBADCAB1E
|
|
+
|
|
+/**
|
|
+ * struct bl_hw_txstatus - Bitfield of confirmation status
|
|
+ *
|
|
+ * @tx_done: packet has been sucessfully transmitted
|
|
+ * @retry_required: packet has been transmitted but not acknoledged.
|
|
+ * Driver must repush it.
|
|
+ * @sw_retry_required: packet has not been transmitted (FW wasn't able to push
|
|
+ * it when it received it: not active channel ...). Driver must repush it.
|
|
+ */
|
|
+union bl_hw_txstatus {
|
|
+ struct {
|
|
+ u32 tx_done : 1;
|
|
+ u32 retry_required : 1;
|
|
+ u32 sw_retry_required : 1;
|
|
+ u32 reserved :29;
|
|
+ };
|
|
+ u32 value;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct tx_cfm_tag - Structure indicating the status and other
|
|
+ * information about the transmission
|
|
+ *
|
|
+ * @pn: PN that was used for the transmission
|
|
+ * @sn: Sequence number of the packet
|
|
+ * @timestamp: Timestamp of first transmission of this MPDU
|
|
+ * @credits: Number of credits to be reallocated for the txq that push this
|
|
+ * buffer (can be 0 or 1)
|
|
+ * @ampdu_size: Size of the ampdu in which the frame has been transmitted if
|
|
+ * this was the last frame of the a-mpdu, and 0 if the frame is not the last
|
|
+ * frame on a a-mdpu.
|
|
+ * 1 means that the frame has been transmitted as a singleton.
|
|
+ * @amsdu_size: Size, in bytes, allowed to create a-msdu.
|
|
+ * @status: transmission status
|
|
+ */
|
|
+struct tx_cfm_tag
|
|
+{
|
|
+ u16_l pn[4];
|
|
+ u16_l sn;
|
|
+ u16_l timestamp;
|
|
+ s8_l credits;
|
|
+ u8_l ampdu_size;
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ u16_l amsdu_size;
|
|
+#endif
|
|
+ union bl_hw_txstatus status;
|
|
+ u32_l count;
|
|
+}__packed;
|
|
+
|
|
+/**
|
|
+ * struct bl_hw_txhdr - Hardware part of tx header
|
|
+ *
|
|
+ * @cfm: Information updated by fw/hardware after sending a frame
|
|
+ */
|
|
+struct bl_hw_txhdr {
|
|
+ struct tx_cfm_tag cfm;
|
|
+};
|
|
+
|
|
+/* Modem */
|
|
+
|
|
+#define MDM_PHY_CONFIG_TRIDENT 0
|
|
+#define MDM_PHY_CONFIG_ELMA 1
|
|
+#define MDM_PHY_CONFIG_KARST 2
|
|
+
|
|
+// MODEM features (from reg_mdm_stat.h)
|
|
+/// LDPCDEC field bit
|
|
+#define MDM_LDPCDEC_BIT ((u32)0x08000000)
|
|
+/// LDPCDEC field position
|
|
+#define MDM_LDPCDEC_POS 27
|
|
+/// LDPCENC field bit
|
|
+#define MDM_LDPCENC_BIT ((u32)0x04000000)
|
|
+/// LDPCENC field position
|
|
+#define MDM_LDPCENC_POS 26
|
|
+/// CHBW field mask
|
|
+#define MDM_CHBW_MASK ((u32)0x03000000)
|
|
+/// CHBW field LSB position
|
|
+#define MDM_CHBW_LSB 24
|
|
+/// CHBW field width
|
|
+#define MDM_CHBW_WIDTH ((u32)0x00000002)
|
|
+/// DSSSCCK field bit
|
|
+#define MDM_DSSSCCK_BIT ((u32)0x00800000)
|
|
+/// DSSSCCK field position
|
|
+#define MDM_DSSSCCK_POS 23
|
|
+/// NESS field mask
|
|
+#define MDM_NESS_MASK ((u32)0x00700000)
|
|
+/// NESS field LSB position
|
|
+#define MDM_NESS_LSB 20
|
|
+/// NESS field width
|
|
+#define MDM_NESS_WIDTH ((u32)0x00000003)
|
|
+/// RFMODE field mask
|
|
+#define MDM_RFMODE_MASK ((u32)0x000F0000)
|
|
+/// RFMODE field LSB position
|
|
+#define MDM_RFMODE_LSB 16
|
|
+/// RFMODE field width
|
|
+#define MDM_RFMODE_WIDTH ((u32)0x00000004)
|
|
+/// NSTS field mask
|
|
+#define MDM_NSTS_MASK ((u32)0x0000F000)
|
|
+/// NSTS field LSB position
|
|
+#define MDM_NSTS_LSB 12
|
|
+/// NSTS field width
|
|
+#define MDM_NSTS_WIDTH ((u32)0x00000004)
|
|
+/// NSS field mask
|
|
+#define MDM_NSS_MASK ((u32)0x00000F00)
|
|
+/// NSS field LSB position
|
|
+#define MDM_NSS_LSB 8
|
|
+/// NSS field width
|
|
+#define MDM_NSS_WIDTH ((u32)0x00000004)
|
|
+/// NTX field mask
|
|
+#define MDM_NTX_MASK ((u32)0x000000F0)
|
|
+/// NTX field LSB position
|
|
+#define MDM_NTX_LSB 4
|
|
+/// NTX field width
|
|
+#define MDM_NTX_WIDTH ((u32)0x00000004)
|
|
+/// NRX field mask
|
|
+#define MDM_NRX_MASK ((u32)0x0000000F)
|
|
+/// NRX field LSB position
|
|
+#define MDM_NRX_LSB 0
|
|
+/// NRX field width
|
|
+#define MDM_NRX_WIDTH ((u32)0x00000004)
|
|
+
|
|
+#define __MDM_PHYCFG_FROM_VERS(v) (((v) & MDM_RFMODE_MASK) >> MDM_RFMODE_LSB)
|
|
+
|
|
+#define RIU_FCU_PRESENT_MASK ((u32)0xFF000000)
|
|
+#define RIU_FCU_PRESENT_LSB 24
|
|
+
|
|
+#define __RIU_FCU_PRESENT(v) (((v) & RIU_FCU_PRESENT_MASK) >> RIU_FCU_PRESENT_LSB == 5)
|
|
+
|
|
+/// AGC load version field mask
|
|
+#define RIU_AGC_LOAD_MASK ((u32)0x00C00000)
|
|
+/// AGC load version field LSB position
|
|
+#define RIU_AGC_LOAD_LSB 22
|
|
+
|
|
+#define __RIU_AGCLOAD_FROM_VERS(v) (((v) & RIU_AGC_LOAD_MASK) >> RIU_AGC_LOAD_LSB)
|
|
+
|
|
+
|
|
+#endif // _HAL_DESC_H_
|
|
diff -Naur /dev/null_compat.h b/drivers/net/wireless/hflps170/ipc_compat.h
|
|
--- /dev/null_compat.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/ipc_compat.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,25 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file ipc_compat.h
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _IPC_H_
|
|
+#define _IPC_H_
|
|
+
|
|
+#define __INLINE static __attribute__((__always_inline__)) inline
|
|
+
|
|
+#define __ALIGN4 __aligned(4)
|
|
+
|
|
+#define ASSERT_ERR(condition) \
|
|
+ do { \
|
|
+ if (unlikely(!(condition))) { \
|
|
+ printk(KERN_ERR "%s:%d:ASSERT_ERR(" #condition ")\n", __FILE__, __LINE__); \
|
|
+ } \
|
|
+ } while(0)
|
|
+
|
|
+#endif /* _IPC_H_ */
|
|
diff -Naur /dev/null_host.c b/drivers/net/wireless/hflps170/ipc_host.c
|
|
--- /dev/null_host.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/ipc_host.c 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,204 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file ipc_host.c
|
|
+ *
|
|
+ * @brief IPC module.
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * INCLUDE FILES
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#include <linux/spinlock.h>
|
|
+#include "bl_defs.h"
|
|
+
|
|
+#include "ipc_host.h"
|
|
+
|
|
+/*
|
|
+ * TYPES DEFINITION
|
|
+ ******************************************************************************
|
|
+ */
|
|
+
|
|
+const int nx_txdesc_cnt[] =
|
|
+{
|
|
+ NX_TXDESC_CNT0,
|
|
+ NX_TXDESC_CNT1,
|
|
+ NX_TXDESC_CNT2,
|
|
+ NX_TXDESC_CNT3,
|
|
+ #if NX_TXQ_CNT == 5
|
|
+ NX_TXDESC_CNT4,
|
|
+ #endif
|
|
+};
|
|
+
|
|
+const int nx_txdesc_cnt_msk[] =
|
|
+{
|
|
+ NX_TXDESC_CNT0 - 1,
|
|
+ NX_TXDESC_CNT1 - 1,
|
|
+ NX_TXDESC_CNT2 - 1,
|
|
+ NX_TXDESC_CNT3 - 1,
|
|
+ #if NX_TXQ_CNT == 5
|
|
+ NX_TXDESC_CNT4 - 1,
|
|
+ #endif
|
|
+};
|
|
+
|
|
+const int nx_txuser_cnt[] =
|
|
+{
|
|
+ CONFIG_USER_MAX,
|
|
+ CONFIG_USER_MAX,
|
|
+ CONFIG_USER_MAX,
|
|
+ CONFIG_USER_MAX,
|
|
+ #if NX_TXQ_CNT == 5
|
|
+ 1,
|
|
+ #endif
|
|
+};
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
|
|
+{
|
|
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
|
|
+ void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]];
|
|
+
|
|
+ // call the external function to indicate that a TX packet is freed
|
|
+ if (host_id != 0)
|
|
+ {
|
|
+ // Reset the host id in the array
|
|
+ env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]] = 0;
|
|
+
|
|
+ // Increment the used index
|
|
+ env->txdesc_used_idx[queue_idx][user_pos]++;
|
|
+ }
|
|
+
|
|
+ return (host_id);
|
|
+}
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void ipc_host_tx_cfm_handler(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos, struct bl_hw_txhdr *hw_hdr, struct bl_txq **txq_saved)
|
|
+{
|
|
+ void *host_id = NULL;
|
|
+ struct sk_buff *skb;
|
|
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos] & nx_txdesc_cnt_msk[queue_idx];
|
|
+ uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos] & nx_txdesc_cnt_msk[queue_idx];
|
|
+
|
|
+ host_id = env->tx_host_id[queue_idx][user_pos][used_idx];
|
|
+ //ASSERT_ERR(host_id != NULL);
|
|
+ if(!host_id){
|
|
+ printk("%s: host id is null \n",__func__);
|
|
+ goto exit;
|
|
+ }
|
|
+ env->tx_host_id[queue_idx][user_pos][used_idx] = 0;
|
|
+ skb=host_id;
|
|
+
|
|
+ BL_DBG("cfm skb=%p in %d of buffer, pkt_sn=%u\n", host_id, used_idx & nx_txdesc_cnt_msk[queue_idx], ((struct bl_txhdr *)(skb->data))->sw_hdr->hdr.reserved);
|
|
+
|
|
+ if (env->cb.send_data_cfm(env->pthis, host_id, hw_hdr, (void **)txq_saved) != 0) {
|
|
+ BL_DBG("send_data_cfm!=0, so break, used_idx=%d\n", used_idx);
|
|
+ env->tx_host_id[queue_idx][user_pos][used_idx] = host_id;
|
|
+ } else {
|
|
+ env->txdesc_used_idx[queue_idx][user_pos]++;
|
|
+ }
|
|
+
|
|
+exit:
|
|
+ if ((env->txdesc_used_idx[queue_idx][user_pos]&nx_txdesc_cnt_msk[queue_idx]) == free_idx) {
|
|
+ BL_DBG("ipc_host_tx_cfm_handler: used_idx=free_idx=%d\n", free_idx);
|
|
+ env->rb_len[queue_idx] = nx_txdesc_cnt[queue_idx];
|
|
+ } else {
|
|
+ BL_DBG("ipc_host_tx_cfm_handler: used_idx=%d, free_idx=%d\n", env->txdesc_used_idx[queue_idx][user_pos]&nx_txdesc_cnt_msk[queue_idx], free_idx);
|
|
+ env->rb_len[queue_idx] = ((env->txdesc_used_idx[queue_idx][user_pos]&nx_txdesc_cnt_msk[queue_idx])-free_idx + nx_txdesc_cnt[queue_idx]) % nx_txdesc_cnt[queue_idx];
|
|
+ }
|
|
+
|
|
+ BL_DBG("ipc_host_tx_cfm_handler: env->rb_len[%d]=%d\n", queue_idx, env->rb_len[queue_idx]);
|
|
+ BL_DBG("used_idx=%d--->%d\n", env->txdesc_used_idx[queue_idx][user_pos]-1, env->txdesc_used_idx[queue_idx][user_pos]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void ipc_host_init(struct ipc_host_env_tag *env,
|
|
+ struct ipc_host_cb_tag *cb,
|
|
+ void *pthis)
|
|
+{
|
|
+ unsigned int i;
|
|
+ // Reset the IPC Host environment
|
|
+ memset(env, 0, sizeof(struct ipc_host_env_tag));
|
|
+
|
|
+ // Save the callbacks in our own environment
|
|
+ env->cb = *cb;
|
|
+
|
|
+ // Save the pointer to the register base
|
|
+ env->pthis = pthis;
|
|
+
|
|
+ // Initialize buffers numbers and buffers sizes needed for DMA Receptions
|
|
+ env->rx_bufnb = IPC_RXBUF_CNT;
|
|
+ env->rx_bufsz = IPC_RXBUF_SIZE;
|
|
+ #ifdef CONFIG_BL_FULLMAC
|
|
+ env->rxdesc_nb = IPC_RXDESC_CNT;
|
|
+ #endif //(CONFIG_BL_FULLMAC)
|
|
+ env->ipc_e2amsg_bufnb = IPC_MSGE2A_BUF_CNT;
|
|
+ env->ipc_e2amsg_bufsz = sizeof(struct ipc_e2a_msg);
|
|
+ env->ipc_dbg_bufnb = IPC_DBGBUF_CNT;
|
|
+ env->ipc_dbg_bufsz = sizeof(struct ipc_dbg_msg);
|
|
+
|
|
+ env->rb_len[0] = NX_TXDESC_CNT0;
|
|
+ env->rb_len[1] = NX_TXDESC_CNT1;
|
|
+ env->rb_len[2] = NX_TXDESC_CNT2;
|
|
+ env->rb_len[3] = NX_TXDESC_CNT3;
|
|
+ #if NX_TXQ_CNT == 5
|
|
+ env->rb_len[4] = NX_TXDESC_CNT4;
|
|
+ #endif
|
|
+
|
|
+ for (i = 0; i < CONFIG_USER_MAX; i++)
|
|
+ {
|
|
+ // Initialize the pointers to the hostid arrays
|
|
+ env->tx_host_id[0][i] = env->tx_host_id0[i];
|
|
+ env->tx_host_id[1][i] = env->tx_host_id1[i];
|
|
+ env->tx_host_id[2][i] = env->tx_host_id2[i];
|
|
+ env->tx_host_id[3][i] = env->tx_host_id3[i];
|
|
+ #if NX_TXQ_CNT == 5
|
|
+ env->tx_host_id[4][i] = NULL;
|
|
+ #endif
|
|
+ }
|
|
+
|
|
+ #if NX_TXQ_CNT == 5
|
|
+ env->tx_host_id[4][0] = env->tx_host_id4[0];
|
|
+ #endif
|
|
+}
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
|
|
+ const int user_pos, void *host_id)
|
|
+{
|
|
+#ifdef CONFIG_BL_DBG
|
|
+ struct sk_buff *skb = host_id;
|
|
+#endif
|
|
+ uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos] & nx_txdesc_cnt_msk[queue_idx];
|
|
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos] & nx_txdesc_cnt_msk[queue_idx];
|
|
+
|
|
+ BL_DBG("save skb=%p in %d of buffer, pkt_sn=%u\n", host_id, free_idx, ((struct bl_txhdr *)((struct sk_buff *)skb->data))->sw_hdr->hdr.reserved);
|
|
+
|
|
+ // Save the host id in the environment
|
|
+ env->tx_host_id[queue_idx][user_pos][free_idx] = host_id;
|
|
+
|
|
+ if((free_idx + 1) % nx_txdesc_cnt[queue_idx] == used_idx) {
|
|
+ BL_DBG("queue is full: free_idx=%d, used_idx=%d\n", free_idx, used_idx);
|
|
+ env->txdesc_free_idx[queue_idx][user_pos]++;
|
|
+ env->rb_len[queue_idx] = 0;
|
|
+ } else {
|
|
+ env->txdesc_free_idx[queue_idx][user_pos]++;
|
|
+ BL_DBG("ipc_host_txdesc_push: used_idx=%d, free_idx=%d\n", used_idx, env->txdesc_free_idx[queue_idx][user_pos]&nx_txdesc_cnt_msk[queue_idx]);
|
|
+ env->rb_len[queue_idx] = (used_idx-(env->txdesc_free_idx[queue_idx][user_pos]&nx_txdesc_cnt_msk[queue_idx]) + nx_txdesc_cnt[queue_idx]) % nx_txdesc_cnt[queue_idx];
|
|
+ }
|
|
+
|
|
+ BL_DBG("ipc_host_txdesc_push: env->rb_len[%d]=%d\n", queue_idx, env->rb_len[queue_idx]);
|
|
+ BL_DBG("queue_idx[%d], free_idx: %d--->%d\n", queue_idx, env->txdesc_free_idx[queue_idx][user_pos] - 1, env->txdesc_free_idx[queue_idx][user_pos]);
|
|
+}
|
|
diff -Naur /dev/null_host.h b/drivers/net/wireless/hflps170/ipc_host.h
|
|
--- /dev/null_host.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/ipc_host.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,212 @@
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ *
|
|
+ * @file ipc_host.h
|
|
+ *
|
|
+ * @brief IPC module.
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#ifndef _IPC_HOST_H_
|
|
+#define _IPC_HOST_H_
|
|
+
|
|
+/*
|
|
+ * INCLUDE FILES
|
|
+ ******************************************************************************
|
|
+ */
|
|
+#include "ipc_shared.h"
|
|
+#ifndef __KERNEL__
|
|
+#include "arch.h"
|
|
+#else
|
|
+#include "ipc_compat.h"
|
|
+#endif
|
|
+
|
|
+#include "bl_txq.h"
|
|
+#include "hal_desc.h"
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief This structure is used to initialize the MAC SW
|
|
+ *
|
|
+ * The WLAN device driver provides functions call-back with this structure
|
|
+ ******************************************************************************
|
|
+ */
|
|
+struct ipc_host_cb_tag
|
|
+{
|
|
+ /// WLAN driver call-back function: send_data_cfm
|
|
+ int (*send_data_cfm)(void *pthis, void *host_id, void *data1, void **data2);
|
|
+
|
|
+ /// WLAN driver call-back function: recv_data_ind
|
|
+ uint8_t (*recv_data_ind)(void *pthis, void *host_id);
|
|
+
|
|
+ /// WLAN driver call-back function: recv_radar_ind
|
|
+ uint8_t (*recv_radar_ind)(void *pthis, void *host_id);
|
|
+
|
|
+ /// WLAN driver call-back function: recv_msg_ind
|
|
+ uint8_t (*recv_msg_ind)(void *pthis, void *host_id);
|
|
+
|
|
+ /// WLAN driver call-back function: recv_msgack_ind
|
|
+ uint8_t (*recv_msgack_ind)(void *pthis, void *host_id);
|
|
+
|
|
+ /// WLAN driver call-back function: recv_dbg_ind
|
|
+ uint8_t (*recv_dbg_ind)(void *pthis, void *host_id);
|
|
+
|
|
+ /// WLAN driver call-back function: prim_tbtt_ind
|
|
+ void (*prim_tbtt_ind)(void *pthis);
|
|
+
|
|
+ /// WLAN driver call-back function: sec_tbtt_ind
|
|
+ void (*sec_tbtt_ind)(void *pthis);
|
|
+
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Struct used to store information about host buffers (DMA Address and local pointer)
|
|
+ */
|
|
+struct ipc_hostbuf
|
|
+{
|
|
+ void *hostid; ///< ptr to hostbuf client (ipc_host client) structure
|
|
+ uint32_t dma_addr; ///< ptr to real hostbuf dma address
|
|
+};
|
|
+
|
|
+/// Definition of the IPC Host environment structure.
|
|
+struct ipc_host_env_tag
|
|
+{
|
|
+ /// Structure containing the callback pointers
|
|
+ struct ipc_host_cb_tag cb;
|
|
+
|
|
+ #ifdef CONFIG_BL_FULLMAC
|
|
+ // Array used to store the descriptor addresses
|
|
+ struct ipc_hostbuf ipc_host_rxdesc_array[IPC_RXDESC_CNT];
|
|
+ // Index of the host RX descriptor array (ipc_shared environment)
|
|
+ uint8_t ipc_host_rxdesc_idx;
|
|
+ /// Store the number of RX Descriptors
|
|
+ uint8_t rxdesc_nb;
|
|
+ #endif //(CONFIG_BL_FULLMAC)
|
|
+
|
|
+ /// Fields for Data Rx handling
|
|
+ // Index used for ipc_host_rxbuf_array to point to current buffer
|
|
+ uint8_t ipc_host_rxbuf_idx;
|
|
+ // Store the number of Rx Data buffers
|
|
+ uint32_t rx_bufnb;
|
|
+ // Store the size of the Rx Data buffers
|
|
+ uint32_t rx_bufsz;
|
|
+
|
|
+ uint32_t rb_len[NX_TXQ_CNT];
|
|
+
|
|
+ // Index used that points to the first free TX desc
|
|
+ uint32_t txdesc_free_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
|
|
+ // Index used that points to the first used TX desc
|
|
+ uint32_t txdesc_used_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
|
|
+ // Array storing the currently pushed host ids for the BK queue
|
|
+ void *tx_host_id0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
|
|
+ // Array storing the currently pushed host ids for the BE queue
|
|
+ void *tx_host_id1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
|
|
+ // Array storing the currently pushed host ids for the VI queue
|
|
+ void *tx_host_id2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
|
|
+ // Array storing the currently pushed host ids for the VO queue
|
|
+ void *tx_host_id3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
|
|
+ #if NX_TXQ_CNT == 5
|
|
+ // Array storing the currently pushed host ids for the BCN queue
|
|
+ void *tx_host_id4[1][NX_TXDESC_CNT4];
|
|
+ #endif
|
|
+ // Pointer to the different host ids arrays, per IPC queue
|
|
+ void **tx_host_id[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
|
|
+ // Pointer to the different TX descriptor arrays, per IPC queue
|
|
+ volatile struct txdesc_host *txdesc[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
|
|
+
|
|
+ /// Fields for Emb->App MSGs handling
|
|
+ // Global array used to store the hostid and hostbuf addresses for msg/ind
|
|
+ struct ipc_hostbuf ipc_host_msgbuf_array[IPC_MSGE2A_BUF_CNT];
|
|
+ // Index of the MSG E2A buffers array to point to current buffer
|
|
+ uint8_t ipc_host_msge2a_idx;
|
|
+ // Store the number of E2A MSG buffers
|
|
+ uint32_t ipc_e2amsg_bufnb;
|
|
+ // Store the size of the E2A MSG buffers
|
|
+ uint32_t ipc_e2amsg_bufsz;
|
|
+
|
|
+ /// E2A ACKs of A2E MSGs
|
|
+ uint8_t msga2e_cnt;
|
|
+ void *msga2e_hostid;
|
|
+
|
|
+ /// Fields for Debug MSGs handling
|
|
+ // Global array used to store the hostid and hostbuf addresses for Debug messages
|
|
+ struct ipc_hostbuf ipc_host_dbgbuf_array[IPC_DBGBUF_CNT];
|
|
+ // Index of the Debug messages buffers array to point to current buffer
|
|
+ uint8_t ipc_host_dbg_idx;
|
|
+ // Store the number of Debug messages buffers
|
|
+ uint32_t ipc_dbg_bufnb;
|
|
+ // Store the size of the Debug messages buffers
|
|
+ uint32_t ipc_dbg_bufsz;
|
|
+
|
|
+ /// Pointer to the attached object (used in callbacks and register accesses)
|
|
+ void *pthis;
|
|
+};
|
|
+
|
|
+extern const int nx_txdesc_cnt[];
|
|
+extern const int nx_txdesc_cnt_msk[];
|
|
+extern const int nx_txuser_cnt[];
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Initialize the IPC running on the Application CPU.
|
|
+ *
|
|
+ * This function:
|
|
+ * - initializes the IPC software environments
|
|
+ * - enables the interrupts in the IPC block
|
|
+ *
|
|
+ * @param[in] env Pointer to the IPC host environment
|
|
+ *
|
|
+ * @warning Since this function resets the IPC Shared memory, it must be called
|
|
+ * before the LMAC FW is launched because LMAC sets some init values in IPC
|
|
+ * Shared memory at boot.
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void ipc_host_init(struct ipc_host_env_tag *env,
|
|
+ struct ipc_host_cb_tag *cb,
|
|
+ void *pthis);
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Push a filled Tx descriptor (host side).
|
|
+ *
|
|
+ * This function sets the next Tx descriptor available by the host side:
|
|
+ * - as used for the host side
|
|
+ * - as available for the emb side.
|
|
+ * The Tx descriptor must be correctly filled before calling this function.
|
|
+ *
|
|
+ * This function may trigger an IRQ to the emb CPU depending on the interrupt
|
|
+ * mitigation policy and on the push count.
|
|
+ *
|
|
+ * @param[in] env Pointer to the IPC host environment
|
|
+ * @param[in] queue_idx Queue index. Same value than ipc_host_txdesc_get()
|
|
+ * @param[in] user_pos User position. If MU-MIMO is not used, this value
|
|
+ * shall be 0.
|
|
+ * @param[in] host_id Parameter indicated by the IPC at TX confirmation,
|
|
+ * that allows the driver finding the buffer
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
|
|
+ const int user_pos, void *host_id);
|
|
+
|
|
+/**
|
|
+ ******************************************************************************
|
|
+ * @brief Get and flush a packet from the IPC queue passed as parameter.
|
|
+ *
|
|
+ * @param[in] env Pointer to the IPC host environment
|
|
+ * @param[in] queue_idx Index of the queue to flush
|
|
+ * @param[in] user_pos User position to flush
|
|
+ *
|
|
+ * @return The flushed hostid if there is one, 0 otherwise.
|
|
+ *
|
|
+ ******************************************************************************
|
|
+ */
|
|
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx,
|
|
+ const int user_pos);
|
|
+void ipc_host_tx_cfm_handler(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos, struct bl_hw_txhdr *hw_hdr, struct bl_txq **txq_saved);
|
|
+
|
|
+
|
|
+#endif // _IPC_HOST_H_
|
|
diff -Naur /dev/null_shared.h b/drivers/net/wireless/hflps170/ipc_shared.h
|
|
--- /dev/null_shared.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/ipc_shared.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,341 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file ipc_shared.h
|
|
+ *
|
|
+ * @brief Shared data between both IPC modules.
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _IPC_SHARED_H_
|
|
+#define _IPC_SHARED_H_
|
|
+
|
|
+/*
|
|
+ * INCLUDE FILES
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#include "ipc_compat.h"
|
|
+#include "lmac_types.h"
|
|
+#include "lmac_mac.h"
|
|
+
|
|
+/*
|
|
+ * DEFINES AND MACROS
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#define CO_BIT(pos) (1U<<(pos))
|
|
+
|
|
+#define IPC_TXQUEUE_CNT NX_TXQ_CNT
|
|
+#define NX_TXDESC_CNT0 8
|
|
+#define NX_TXDESC_CNT1 64
|
|
+#define NX_TXDESC_CNT2 64
|
|
+#define NX_TXDESC_CNT3 8
|
|
+#if NX_TXQ_CNT == 5
|
|
+#define NX_TXDESC_CNT4 8
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Number of Host buffers available for Data Rx handling (through DMA)
|
|
+ */
|
|
+#define IPC_RXBUF_CNT 128
|
|
+
|
|
+/*
|
|
+ * Number of shared descriptors available for Data RX handling
|
|
+ */
|
|
+#define IPC_RXDESC_CNT 128
|
|
+
|
|
+/*
|
|
+ * RX Data buffers size (in bytes)
|
|
+ */
|
|
+#define IPC_RXBUF_SIZE 5120
|
|
+
|
|
+/*
|
|
+ * Number of Host buffers available for Emb->App MSGs sending (through DMA)
|
|
+ */
|
|
+#define IPC_MSGE2A_BUF_CNT 64
|
|
+/*
|
|
+ * Number of Host buffers available for Debug Messages sending (through DMA)
|
|
+ */
|
|
+#define IPC_DBGBUF_CNT 32
|
|
+
|
|
+/*
|
|
+ * Length used in MSGs structures
|
|
+ */
|
|
+#define IPC_A2E_MSG_BUF_SIZE 127 // size in 4-byte words
|
|
+#define IPC_E2A_MSG_PARAM_SIZE 256 // size in 4-byte words
|
|
+/*
|
|
+ * Debug messages buffers size (in bytes)
|
|
+ */
|
|
+#define IPC_DBG_PARAM_SIZE 256
|
|
+
|
|
+/*
|
|
+ * Define used for Rx hostbuf validity.
|
|
+ * This value should appear only when hostbuf was used for a Reception.
|
|
+ */
|
|
+#define RX_DMA_OVER_PATTERN 0xAAAAAA00
|
|
+
|
|
+/*
|
|
+ * Define used for MSG buffers validity.
|
|
+ * This value will be written only when a MSG buffer is used for sending from Emb to App.
|
|
+ */
|
|
+#define IPC_MSGE2A_VALID_PATTERN 0xADDEDE2A
|
|
+
|
|
+/*
|
|
+ * Define used for Debug messages buffers validity.
|
|
+ * This value will be written only when a DBG buffer is used for sending from Emb to App.
|
|
+ */
|
|
+#define IPC_DBG_VALID_PATTERN 0x000CACA0
|
|
+
|
|
+/*
|
|
+ * Length of the receive vectors, in bytes
|
|
+ */
|
|
+#define DMA_HDR_PHYVECT_LEN 36
|
|
+
|
|
+/*
|
|
+ * Maximum number of payload addresses and lengths present in the descriptor
|
|
+ */
|
|
+#define NX_TX_PAYLOAD_MAX 6
|
|
+
|
|
+/*
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+// c.f LMAC/src/tx/tx_swdesc.h
|
|
+/// Descriptor filled by the Host
|
|
+struct hostdesc
|
|
+{
|
|
+#ifdef CONFIG_BL_SPLIT_TX_BUF
|
|
+ /// Pointers to packet payloads
|
|
+ u32_l packet_addr[NX_TX_PAYLOAD_MAX];
|
|
+ /// Sizes of the MPDU/MSDU payloads
|
|
+ u16_l packet_len[NX_TX_PAYLOAD_MAX];
|
|
+ /// Number of payloads forming the MPDU
|
|
+ u8_l packet_cnt;
|
|
+#else
|
|
+ /// Pointer to packet payload
|
|
+ u32_l packet_addr;
|
|
+ /// Size of the payload
|
|
+ u16_l packet_len;
|
|
+#endif //(NX_AMSDU_TX)
|
|
+
|
|
+ /// Address of the status descriptor in host memory (used for confirmation upload)
|
|
+ u32_l host_hdr_pads;
|
|
+ /// Destination Address
|
|
+ struct mac_addr eth_dest_addr;
|
|
+ /// Source Address
|
|
+ struct mac_addr eth_src_addr;
|
|
+ /// Ethernet Type
|
|
+ u16_l ethertype;
|
|
+ /// Buffer containing the PN to be used for this packet
|
|
+ u16_l pn[4];
|
|
+ /// Sequence Number used for transmission of this MPDU
|
|
+ u16_l sn;
|
|
+ /// Timestamp of first transmission of this MPDU
|
|
+ u16_l timestamp;
|
|
+ /// Packet TID (0xFF if not a QoS frame)
|
|
+ u8_l tid;
|
|
+ /// Interface Id
|
|
+ u8_l vif_idx;
|
|
+ /// Station Id (0xFF if station is unknown)
|
|
+ u8_l staid;
|
|
+#ifdef CONFIG_BL_FULLMAC
|
|
+ /// TX flags
|
|
+ u16_l flags;
|
|
+#endif /* CONFIG_BL_FULLMAC */
|
|
+};
|
|
+
|
|
+struct txdesc_api
|
|
+{
|
|
+ /// add templately
|
|
+ u32_l ready;
|
|
+ /// Information provided by Host
|
|
+ struct hostdesc host;
|
|
+};
|
|
+
|
|
+
|
|
+struct txdesc_host
|
|
+{
|
|
+ u32_l ready;
|
|
+
|
|
+ /// API of the embedded part
|
|
+ struct txdesc_api api;
|
|
+};
|
|
+
|
|
+/// Comes from ipc_dma.h
|
|
+/// Element in the pool of TX DMA bridge descriptors.
|
|
+struct dma_desc
|
|
+{
|
|
+ /** Application subsystem address which is used as source address for DMA payload
|
|
+ * transfer*/
|
|
+ u32_l src;
|
|
+ /** Points to the start of the embedded data buffer associated with this descriptor.
|
|
+ * This address acts as the destination address for the DMA payload transfer*/
|
|
+ u32_l dest;
|
|
+ /// Complete length of the buffer in memory
|
|
+ u16_l length;
|
|
+ /// Control word for the DMA engine (e.g. for interrupt generation)
|
|
+ u16_l ctrl;
|
|
+ /// Pointer to the next element of the chained list
|
|
+ u32_l next;
|
|
+};
|
|
+
|
|
+// Comes from la.h
|
|
+/// Length of the configuration data of a logic analyzer
|
|
+#define LA_CONF_LEN 10
|
|
+
|
|
+/// Structure containing the configuration data of a logic analyzer
|
|
+struct la_conf_tag
|
|
+{
|
|
+ u32_l conf[LA_CONF_LEN];
|
|
+ u32_l trace_len;
|
|
+ u32_l diag_conf;
|
|
+};
|
|
+
|
|
+/// Size of a logic analyzer memory
|
|
+#define LA_MEM_LEN (1024 * 1024)
|
|
+
|
|
+/// Type of errors
|
|
+enum
|
|
+{
|
|
+ /// Recoverable error, not requiring any action from Upper MAC
|
|
+ DBG_ERROR_RECOVERABLE = 0,
|
|
+ /// Fatal error, requiring Upper MAC to reset Lower MAC and HW and restart operation
|
|
+ DBG_ERROR_FATAL
|
|
+};
|
|
+
|
|
+/// Maximum length of the SW diag trace
|
|
+#define DBG_SW_DIAG_MAX_LEN 1024
|
|
+
|
|
+/// Maximum length of the error trace
|
|
+#define DBG_ERROR_TRACE_SIZE 256
|
|
+
|
|
+/// Number of MAC diagnostic port banks
|
|
+#define DBG_DIAGS_MAC_MAX 48
|
|
+
|
|
+/// Number of PHY diagnostic port banks
|
|
+#define DBG_DIAGS_PHY_MAX 32
|
|
+
|
|
+/// Maximum size of the RX header descriptor information in the debug dump
|
|
+#define DBG_RHD_MEM_LEN (5 * 1024)
|
|
+
|
|
+/// Maximum size of the RX buffer descriptor information in the debug dump
|
|
+#define DBG_RBD_MEM_LEN (5 * 1024)
|
|
+
|
|
+/// Maximum size of the TX header descriptor information in the debug dump
|
|
+#define DBG_THD_MEM_LEN (10 * 1024)
|
|
+
|
|
+/// Structure containing the information about the PHY channel that is used
|
|
+struct phy_channel_info
|
|
+{
|
|
+ /// PHY channel information 1
|
|
+ u32_l info1;
|
|
+ /// PHY channel information 2
|
|
+ u32_l info2;
|
|
+};
|
|
+
|
|
+/// Debug information forwarded to host when an error occurs
|
|
+struct dbg_debug_info_tag
|
|
+{
|
|
+ /// Type of error (0: recoverable, 1: fatal)
|
|
+ u32_l error_type;
|
|
+ /// Pointer to the first RX Header Descriptor chained to the MAC HW
|
|
+ u32_l rhd;
|
|
+ /// Size of the RX header descriptor buffer
|
|
+ u32_l rhd_len;
|
|
+ /// Pointer to the first RX Buffer Descriptor chained to the MAC HW
|
|
+ u32_l rbd;
|
|
+ /// Size of the RX buffer descriptor buffer
|
|
+ u32_l rbd_len;
|
|
+ /// Pointer to the first TX Header Descriptors chained to the MAC HW
|
|
+ u32_l thd[NX_TXQ_CNT];
|
|
+ /// Size of the TX header descriptor buffer
|
|
+ u32_l thd_len[NX_TXQ_CNT];
|
|
+ /// MAC HW diag configuration
|
|
+ u32_l hw_diag;
|
|
+ /// Error message
|
|
+ u32_l error[DBG_ERROR_TRACE_SIZE/4];
|
|
+ /// SW diag configuration length
|
|
+ u32_l sw_diag_len;
|
|
+ /// SW diag configuration
|
|
+ u32_l sw_diag[DBG_SW_DIAG_MAX_LEN/4];
|
|
+ /// PHY channel information
|
|
+ struct phy_channel_info chan_info;
|
|
+ /// Embedded LA configuration
|
|
+ struct la_conf_tag la_conf;
|
|
+ /// MAC diagnostic port state
|
|
+ u16_l diags_mac[DBG_DIAGS_MAC_MAX];
|
|
+ /// PHY diagnostic port state
|
|
+ u16_l diags_phy[DBG_DIAGS_PHY_MAX];
|
|
+ /// MAC HW RX Header descriptor pointer
|
|
+ u32_l rhd_hw_ptr;
|
|
+ /// MAC HW RX Buffer descriptor pointer
|
|
+ u32_l rbd_hw_ptr;
|
|
+};
|
|
+
|
|
+/// Full debug dump that is forwarded to host in case of error
|
|
+struct dbg_debug_dump_tag
|
|
+{
|
|
+ /// Debug information
|
|
+ struct dbg_debug_info_tag dbg_info;
|
|
+
|
|
+ /// RX header descriptor memory
|
|
+ u8_l rhd_mem[DBG_RHD_MEM_LEN];
|
|
+
|
|
+ /// RX buffer descriptor memory
|
|
+ u8_l rbd_mem[DBG_RBD_MEM_LEN];
|
|
+
|
|
+ /// TX header descriptor memory
|
|
+ u8_l thd_mem[NX_TXQ_CNT][DBG_THD_MEM_LEN];
|
|
+
|
|
+ /// Logic analyzer memory
|
|
+ u8_l la_mem[LA_MEM_LEN];
|
|
+};
|
|
+
|
|
+struct rxdesc_tag
|
|
+{
|
|
+ /// Host Buffer Address
|
|
+ u32_l host_id;
|
|
+ /// Length
|
|
+ u32_l frame_len;
|
|
+ /// Status
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ * @defgroup IPC_MISC IPC Misc
|
|
+ * @ingroup IPC
|
|
+ * @brief IPC miscellaneous functions
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+/// Message structure for MSGs from Emb to App
|
|
+struct ipc_e2a_msg
|
|
+{
|
|
+ u16_l id; ///< Message id.
|
|
+ u16_l dummy_dest_id; ///<
|
|
+ u16_l dummy_src_id; ///<
|
|
+ u16_l param_len; ///< Parameter embedded struct length.
|
|
+ //u32_l param[IPC_E2A_MSG_PARAM_SIZE]; ///< Parameter embedded struct. Must be word-aligned.
|
|
+ u32_l param[]; ///< Parameter embedded struct. Must be word-aligned.
|
|
+};
|
|
+
|
|
+/// Message structure for Debug messages from Emb to App
|
|
+struct ipc_dbg_msg
|
|
+{
|
|
+ u32_l string[IPC_DBG_PARAM_SIZE/4]; ///< Debug string
|
|
+ u32_l pattern; ///< Used to stamp a valid buffer
|
|
+};
|
|
+
|
|
+/// Message structure for MSGs from App to Emb.
|
|
+/// Actually a sub-structure will be used when filling the messages.
|
|
+struct ipc_a2e_msg
|
|
+{
|
|
+ u32_l dummy_word; // used to cope with kernel message structure
|
|
+ u32_l msg[IPC_A2E_MSG_BUF_SIZE]; // body of the msg
|
|
+};
|
|
+
|
|
+#endif // _IPC_SHARED_H_
|
|
+
|
|
diff -Naur /dev/null_mac.h b/drivers/net/wireless/hflps170/lmac_mac.h
|
|
--- /dev/null_mac.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/lmac_mac.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,526 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file lmac_mac.h
|
|
+ *
|
|
+ * @brief MAC related definitions.
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _MAC_H_
|
|
+#define _MAC_H_
|
|
+
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ * @defgroup MAC MAC
|
|
+ * @ingroup COMMON
|
|
+ * @brief Common defines,structures
|
|
+ *
|
|
+ * This module contains defines commonaly used for MAC
|
|
+ * @{
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * INCLUDE FILES
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#ifndef __KERNEL__
|
|
+// standard includes
|
|
+#include <stdbool.h>
|
|
+#else
|
|
+#include <linux/types.h>
|
|
+#endif
|
|
+
|
|
+
|
|
+/*
|
|
+ * DEFINES
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+/// duration of a Time Unit in microseconds
|
|
+#define TU_DURATION 1024
|
|
+
|
|
+/// max number of channels in the 2.4 GHZ band
|
|
+#define MAC_DOMAINCHANNEL_24G_MAX 14
|
|
+
|
|
+/// max number of channels in the 5 GHZ band
|
|
+#define MAC_DOMAINCHANNEL_5G_MAX 45
|
|
+
|
|
+/// Mask to test if it's a basic rate - BIT(7)
|
|
+#define MAC_BASIC_RATE 0x80
|
|
+/// Mask for extracting/checking word alignment
|
|
+#define WORD_ALIGN 3
|
|
+
|
|
+#define MAX_AMSDU_LENGTH 7935
|
|
+
|
|
+/*
|
|
+ * MACRO DEFINITIONS
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ * Compare two MAC addresses.
|
|
+ * The MAC addresses MUST be 16 bit aligned.
|
|
+ * @param[in] addr1_ptr Pointer to the first MAC address.
|
|
+ * @param[in] addr2_ptr Pointer to the second MAC address.
|
|
+ * @return True if equal, false if not.
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#define MAC_ADDR_CMP(addr1_ptr, addr2_ptr) \
|
|
+ ((*(((u16*)(addr1_ptr)) + 0) == *(((u16*)(addr2_ptr)) + 0)) && \
|
|
+ (*(((u16*)(addr1_ptr)) + 1) == *(((u16*)(addr2_ptr)) + 1)) && \
|
|
+ (*(((u16*)(addr1_ptr)) + 2) == *(((u16*)(addr2_ptr)) + 2)))
|
|
+
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ * Compare two MAC addresses whose alignment is not known.
|
|
+ * @param[in] __a1 Pointer to the first MAC address.
|
|
+ * @param[in] __a2 Pointer to the second MAC address.
|
|
+ * @return True if equal, false if not.
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#define MAC_ADDR_CMP_PACKED(__a1, __a2) \
|
|
+ (memcmp(__a1, __a2, MAC_ADDR_LEN) == 0)
|
|
+
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ * Copy a MAC address.
|
|
+ * The MAC addresses MUST be 16 bit aligned.
|
|
+ * @param[in] addr1_ptr Pointer to the destination MAC address.
|
|
+ * @param[in] addr2_ptr Pointer to the source MAC address.
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#define MAC_ADDR_CPY(addr1_ptr, addr2_ptr) \
|
|
+ *(((u16*)(addr1_ptr)) + 0) = *(((u16*)(addr2_ptr)) + 0); \
|
|
+ *(((u16*)(addr1_ptr)) + 1) = *(((u16*)(addr2_ptr)) + 1); \
|
|
+ *(((u16*)(addr1_ptr)) + 2) = *(((u16*)(addr2_ptr)) + 2)
|
|
+
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ * Compare two SSID.
|
|
+ * @param ssid1_ptr Pointer to the first SSID structure.
|
|
+ * @param ssid2_ptr Pointer to the second SSID structure.
|
|
+ * @return True if equal, false if not.
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+#define MAC_SSID_CMP(ssid1_ptr,ssid2_ptr) \
|
|
+ (((ssid1_ptr)->length == (ssid2_ptr)->length) && \
|
|
+ (memcmp((&(ssid1_ptr)->array[0]), (&(ssid2_ptr)->array[0]), (ssid1_ptr)->length) == 0))
|
|
+
|
|
+/// Check if MAC address is a group address: test the multicast bit.
|
|
+#define MAC_ADDR_GROUP(mac_addr_ptr) ((*(mac_addr_ptr)) & 1)
|
|
+
|
|
+/// MAC address length in bytes.
|
|
+#define MAC_ADDR_LEN 6
|
|
+
|
|
+/// MAC address structure.
|
|
+struct mac_addr
|
|
+{
|
|
+ /// Array of bytes that make up the MAC address.
|
|
+ u16_l array[MAC_ADDR_LEN/2];
|
|
+}__packed;
|
|
+
|
|
+/// SSID maximum length.
|
|
+#define MAC_SSID_LEN 32
|
|
+
|
|
+/// SSID.
|
|
+struct mac_ssid
|
|
+{
|
|
+ /// Actual length of the SSID.
|
|
+ u8_l length;
|
|
+ /// Array containing the SSID name.
|
|
+ u8_l array[MAC_SSID_LEN];
|
|
+};
|
|
+
|
|
+/// MAC RATE-SET
|
|
+#define MAC_RATESET_LEN 12
|
|
+#define MAC_OFDM_PHY_RATESET_LEN 8
|
|
+#define MAC_EXT_RATES_OFF 8
|
|
+struct mac_rateset
|
|
+{
|
|
+ u8_l length;
|
|
+ u8_l array[MAC_RATESET_LEN];
|
|
+};
|
|
+
|
|
+/// MAC RATES
|
|
+#define MAC_MCS_WORD_CNT 3
|
|
+struct mac_rates
|
|
+{
|
|
+ /// MCS 0 to 76
|
|
+ u32 mcs[MAC_MCS_WORD_CNT];
|
|
+ /// Legacy rates (1Mbps to 54Mbps)
|
|
+ u16 legacy;
|
|
+};
|
|
+
|
|
+/// IV/EIV data
|
|
+#define MAC_IV_LEN 4
|
|
+#define MAC_EIV_LEN 4
|
|
+struct rx_seciv
|
|
+{
|
|
+ u8 iv[MAC_IV_LEN];
|
|
+ u8 ext_iv[MAC_EIV_LEN];
|
|
+};
|
|
+
|
|
+/// MAC MCS SET
|
|
+#define MAX_MCS_LEN 16 // 16 * 8 = 128
|
|
+struct mac_mcsset
|
|
+{
|
|
+ u8 length;
|
|
+ u8 array[MAX_MCS_LEN];
|
|
+};
|
|
+
|
|
+/// MAC Secret Key
|
|
+#define MAC_WEP_KEY_CNT 4 // Number of WEP keys per virtual device
|
|
+#define MAC_WEP_KEY_LEN 13 // Max size of a WEP key (104/8 = 13)
|
|
+struct mac_wep_key
|
|
+{
|
|
+ u8 array[MAC_WEP_KEY_LEN]; // Key material
|
|
+};
|
|
+
|
|
+
|
|
+/// MAC Secret Key
|
|
+#define MAC_SEC_KEY_LEN 32 // TKIP keys 256 bits (max length) with MIC keys
|
|
+struct mac_sec_key
|
|
+{
|
|
+ u8_l length; // Key material length
|
|
+ u32_l array[MAC_SEC_KEY_LEN/4]; // Key material
|
|
+};
|
|
+
|
|
+/// MAC channel list
|
|
+/// @todo: fix that number
|
|
+#define MAC_MAX_CH 40
|
|
+struct mac_ch_list
|
|
+{
|
|
+ /// Number of channels in channel list.
|
|
+ u16 nbr;
|
|
+ /// List of the channels.
|
|
+ u8 list[MAC_MAX_CH];
|
|
+};
|
|
+
|
|
+
|
|
+struct mac_country_subband
|
|
+{
|
|
+ // First channel number of the triplet.
|
|
+ u8 first_chn;
|
|
+ // Max number of channel number for the triplet.
|
|
+ u8 nbr_of_chn;
|
|
+ // Maximum allowed transmit power.
|
|
+ u8 max_tx_power;
|
|
+};
|
|
+
|
|
+#define MAX_COUNTRY_LEN 3
|
|
+#define MAX_COUNTRY_SUBBAND 5
|
|
+struct mac_country
|
|
+{
|
|
+ // Length of the country string
|
|
+ u8 length;
|
|
+ // Country string 2 char.
|
|
+ u8 string[MAX_COUNTRY_LEN];
|
|
+ // channel info triplet
|
|
+ struct mac_country_subband subband[MAX_COUNTRY_SUBBAND];
|
|
+};
|
|
+
|
|
+/// MAC HT CAPABILITY
|
|
+struct mac_htcapability
|
|
+{
|
|
+ u16_l ht_capa_info;
|
|
+ u8_l a_mpdu_param;
|
|
+ u8_l mcs_rate[MAX_MCS_LEN];
|
|
+ u16_l ht_extended_capa;
|
|
+ u32_l tx_beamforming_capa;
|
|
+ u8_l asel_capa;
|
|
+};
|
|
+
|
|
+/// MAC VHT CAPABILITY
|
|
+struct mac_vhtcapability
|
|
+{
|
|
+ u32_l vht_capa_info;
|
|
+ u16_l rx_mcs_map;
|
|
+ u16_l rx_highest;
|
|
+ u16_l tx_mcs_map;
|
|
+ u16_l tx_highest;
|
|
+};
|
|
+
|
|
+
|
|
+/// MAC HT CAPABILITY
|
|
+struct mac_htoprnelmt
|
|
+{
|
|
+ u8 prim_channel;
|
|
+ u8 ht_oper_1;
|
|
+ u16 ht_oper_2;
|
|
+ u16 ht_oper_3;
|
|
+ u8 mcs_rate[MAX_MCS_LEN];
|
|
+
|
|
+};
|
|
+
|
|
+/// MAC QOS CAPABILITY
|
|
+struct mac_qoscapability
|
|
+{
|
|
+ u8 qos_info;
|
|
+};
|
|
+
|
|
+/// RSN information element
|
|
+#define MAC_RAW_RSN_IE_LEN 34
|
|
+struct mac_raw_rsn_ie
|
|
+{
|
|
+ u8 data[2 + MAC_RAW_RSN_IE_LEN];
|
|
+};
|
|
+
|
|
+#define MAC_RAW_ENC_LEN 0x1A
|
|
+struct mac_wpa_frame
|
|
+{
|
|
+ u8 array[MAC_RAW_ENC_LEN];
|
|
+};
|
|
+
|
|
+#define MAC_WME_PARAM_LEN 16
|
|
+struct mac_wmm_frame
|
|
+{
|
|
+ u8 array [MAC_WME_PARAM_LEN];
|
|
+};
|
|
+
|
|
+/// BSS load element
|
|
+struct mac_bss_load
|
|
+{
|
|
+ u16 sta_cnt;
|
|
+ u8 ch_utilization;
|
|
+ u16 avail_adm_capacity;
|
|
+};
|
|
+
|
|
+///EDCA Parameter Set Element
|
|
+struct mac_edca_param_set
|
|
+{
|
|
+ u8 qos_info;
|
|
+ u32 ac_be_param_record;
|
|
+ u32 ac_bk_param_record;
|
|
+ u32 ac_vi_param_record;
|
|
+ u32 ac_vo_param_record;
|
|
+};
|
|
+
|
|
+
|
|
+///MAC Twenty Forty BSS
|
|
+
|
|
+struct mac_twenty_fourty_bss
|
|
+{
|
|
+ u8 bss_coexistence;
|
|
+};
|
|
+
|
|
+/// MAC BA PARAMETERS
|
|
+struct mac_ba_param
|
|
+{
|
|
+ struct mac_addr peer_sta_address; ///< Peer STA MAC Address to which BA is Setup
|
|
+ u16 buffer_size; ///< Number of buffers available for this BA
|
|
+ u16 start_sequence_number;///< Start Sequence Number of BA
|
|
+ u16 ba_timeout; ///< BA Setup timeout value
|
|
+ u8 dev_type; ///< BA Device Type Originator/Responder
|
|
+ u8 block_ack_policy; ///< BLOCK-ACK Policy Setup Immedaite/Delayed
|
|
+ u8 buffer_cnt; ///< Number of buffers required for BA Setup
|
|
+};
|
|
+
|
|
+/// MAC TS INFO field
|
|
+struct mac_ts_info
|
|
+{
|
|
+ u8 traffic_type;
|
|
+ u8 ack_policy;
|
|
+ u8 access_policy;
|
|
+ u8 dir;
|
|
+ u8 tsid;
|
|
+ u8 user_priority;
|
|
+ bool aggregation;
|
|
+ bool apsd;
|
|
+ bool schedule;
|
|
+};
|
|
+
|
|
+/// MAC TSPEC PARAMETERS
|
|
+struct mac_tspec_param
|
|
+{
|
|
+ struct mac_ts_info ts_info;
|
|
+ u16 nominal_msdu_size;
|
|
+ u16 max_msdu_size;
|
|
+ u32 min_service_interval;
|
|
+ u32 max_service_interval;
|
|
+ u32 inactivity_interval;
|
|
+ u32 short_inactivity_interval;
|
|
+ u32 service_start_time;
|
|
+ u32 max_burst_size;
|
|
+ u32 min_data_rate;
|
|
+ u32 mean_data_rate;
|
|
+ u32 min_phy_rate;
|
|
+ u32 peak_data_rate;
|
|
+ u32 delay_bound;
|
|
+ u16 medium_time;
|
|
+ u8 surplusbwallowance;
|
|
+};
|
|
+
|
|
+/// Scan result element, parsed from beacon or probe response frames.
|
|
+struct mac_scan_result
|
|
+{
|
|
+ /// Network BSSID.
|
|
+ struct mac_addr bssid;
|
|
+ /// Network name.
|
|
+ struct mac_ssid ssid;
|
|
+ /// Network type (IBSS or ESS).
|
|
+ u16 bsstype;
|
|
+ /// Network channel number.
|
|
+ u16 ch_nbr;
|
|
+ /// Network beacon period.
|
|
+ u16 beacon_period;
|
|
+ u32 timestamp_high;
|
|
+ u32 timestamp_low;
|
|
+ u16 dtim_period;
|
|
+ u16 ibss_parameter;
|
|
+ u16 cap_info;
|
|
+ struct mac_rateset rate_set;
|
|
+ struct mac_bss_load bss_load;
|
|
+ u8 country_element[3];
|
|
+ struct mac_edca_param_set edca_param;
|
|
+ struct mac_raw_rsn_ie rsn_ie;
|
|
+ struct mac_qoscapability qos_cap;
|
|
+ struct mac_htcapability ht_cap;
|
|
+ u8 sec_ch_oft;
|
|
+ struct mac_twenty_fourty_bss twenty_fourty_bss;
|
|
+ bool valid_flag;
|
|
+ u8 rssi;
|
|
+};
|
|
+
|
|
+/// Structure containing the information required to perform a measurement request
|
|
+struct mac_request_set
|
|
+{
|
|
+
|
|
+ u8 mode; ///<As specified by standard
|
|
+ u8 type; ///< 0: Basic request, 1: CCA request, 2: RPI histogram request
|
|
+ u16 duration; ///< In TU
|
|
+ uint64_t start_time; ///< TSF time
|
|
+ u8 ch_number; ///< channel to be measured
|
|
+};
|
|
+
|
|
+/// Structure containing the information returned from a measurement process
|
|
+struct mac_report_set
|
|
+{
|
|
+ u8 mode; ///<As specified by standard
|
|
+ u8 type; ///< 0: Basic request, 1: CCA request, 2: RPI histogram request
|
|
+ u16 duration; ///< In TU
|
|
+ uint64_t start_time; ///< TSF time
|
|
+ u8 ch_number; ///< channel to be measured
|
|
+ u8 map; ///< As specified by standard
|
|
+ u8 cca_busy_fraction; ///<As specified by standard
|
|
+ u8 rpi_histogram[8]; ///<As specified by standard
|
|
+};
|
|
+
|
|
+/// Structure containing the MAC SW and MAC HW version information
|
|
+struct mac_version
|
|
+{
|
|
+ char mac_sw_version[16];
|
|
+ char mac_sw_version_date[48];
|
|
+ char mac_sw_build_date[48];
|
|
+ u32 mac_hw_version1;
|
|
+ u32 mac_hw_version2;
|
|
+};
|
|
+
|
|
+/// Structure containing some of the properties of a BSS. @todo Add required fields during
|
|
+/// AP/IBSS mode implementation
|
|
+struct mac_bss_conf
|
|
+{
|
|
+ /// Flags (ERP, QoS, etc.).
|
|
+ u32 flags;
|
|
+ /// Beacon period
|
|
+ u16 beacon_period;
|
|
+};
|
|
+
|
|
+/// Traffic ID enumeration
|
|
+enum
|
|
+{
|
|
+ TID_0,
|
|
+ TID_1,
|
|
+ TID_2,
|
|
+ TID_3,
|
|
+ TID_4,
|
|
+ TID_5,
|
|
+ TID_6,
|
|
+ TID_7,
|
|
+ TID_MGT,
|
|
+ TID_MAX
|
|
+};
|
|
+
|
|
+/// Access Category enumeration
|
|
+enum
|
|
+{
|
|
+ AC_BK = 0,
|
|
+ AC_BE,
|
|
+ AC_VI,
|
|
+ AC_VO,
|
|
+ AC_MAX
|
|
+};
|
|
+
|
|
+/// SCAN type
|
|
+enum
|
|
+{
|
|
+ SCAN_PASSIVE,
|
|
+ SCAN_ACTIVE
|
|
+};
|
|
+
|
|
+/// rates
|
|
+enum
|
|
+{
|
|
+ MAC_RATE_1MBPS = 2,
|
|
+ MAC_RATE_2MBPS = 4,
|
|
+ MAC_RATE_5_5MBPS = 11,
|
|
+ MAC_RATE_6MBPS = 12,
|
|
+ MAC_RATE_9MBPS = 18,
|
|
+ MAC_RATE_11MBPS = 22,
|
|
+ MAC_RATE_12MBPS = 24,
|
|
+ MAC_RATE_18MBPS = 36,
|
|
+ MAC_RATE_24MBPS = 48,
|
|
+ MAC_RATE_36MBPS = 72,
|
|
+ MAC_RATE_48MBPS = 96,
|
|
+ MAC_RATE_54MBPS = 108
|
|
+};
|
|
+
|
|
+/// Station flags
|
|
+enum
|
|
+{
|
|
+ /// Bit indicating that a STA has QoS (WMM) capability
|
|
+ STA_QOS_CAPA = 1 << 0,
|
|
+ /// Bit indicating that a STA has HT capability
|
|
+ STA_HT_CAPA = 1 << 1,
|
|
+ /// Bit indicating that a STA has VHT capability
|
|
+ STA_VHT_CAPA = 1 << 2,
|
|
+ /// Bit indicating that a STA has MFP capability
|
|
+ STA_MFP_CAPA = 1 << 3,
|
|
+ /// Bit indicating that the STA included the Operation Notification IE
|
|
+ STA_OPMOD_NOTIF = 1 << 4,
|
|
+};
|
|
+
|
|
+/// Connection flags
|
|
+enum
|
|
+{
|
|
+ /// Flag indicating whether the control port is controlled by host or not
|
|
+ CONTROL_PORT_HOST = 1 << 0,
|
|
+ /// Flag indicating whether the control port frame shall be sent unencrypted
|
|
+ CONTROL_PORT_NO_ENC = 1 << 1,
|
|
+ /// Flag indicating whether HT shall be disabled or not
|
|
+ DISABLE_HT = 1 << 2,
|
|
+ /// Flag indicating whether WPA or WPA2 authentication is in use
|
|
+ WPA_WPA2_IN_USE = 1 << 3,
|
|
+ /// Flag indicating whether MFP is in use
|
|
+ MFP_IN_USE = 1 << 4,
|
|
+};
|
|
+
|
|
+/*
|
|
+* GLOBAL VARIABLES
|
|
+****************************************************************************************
|
|
+*/
|
|
+extern const u8 mac_tid2ac[];
|
|
+
|
|
+extern const u8 mac_id2rate[];
|
|
+
|
|
+extern const u16 mac_mcs_params_20[];
|
|
+
|
|
+extern const u16 mac_mcs_params_40[];
|
|
+
|
|
+/// @}
|
|
+
|
|
+#endif // _MAC_H_
|
|
diff -Naur /dev/null_msg.h b/drivers/net/wireless/hflps170/lmac_msg.h
|
|
--- /dev/null_msg.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/lmac_msg.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,1981 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file lmac_msg.h
|
|
+ *
|
|
+ * @brief Main definitions for message exchanges with LMAC
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef LMAC_MSG_H_
|
|
+#define LMAC_MSG_H_
|
|
+
|
|
+/*
|
|
+ * INCLUDE FILES
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+// for MAC related elements (mac_addr, mac_ssid...)
|
|
+#include "lmac_types.h"
|
|
+#include "lmac_mac.h"
|
|
+
|
|
+/*
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+/////////////////////////////////////////////////////////////////////////////////
|
|
+// COMMUNICATION WITH LMAC LAYER
|
|
+/////////////////////////////////////////////////////////////////////////////////
|
|
+/* Task identifiers for communication between LMAC and DRIVER */
|
|
+enum
|
|
+{
|
|
+ TASK_NONE = (u8_l) -1,
|
|
+
|
|
+ // MAC Management task.
|
|
+ TASK_MM = 0,
|
|
+ // DEBUG task
|
|
+ TASK_DBG,
|
|
+ /// SCAN task
|
|
+ TASK_SCAN,
|
|
+ /// TDLS task
|
|
+ TASK_TDLS,
|
|
+ /// SCANU task
|
|
+ TASK_SCANU,
|
|
+ /// ME task
|
|
+ TASK_ME,
|
|
+ /// SM task
|
|
+ TASK_SM,
|
|
+ /// APM task
|
|
+ TASK_APM,
|
|
+ /// BAM task
|
|
+ TASK_BAM,
|
|
+ /// MESH task
|
|
+ TASK_MESH,
|
|
+ /// RXU task
|
|
+ TASK_RXU,
|
|
+ // This is used to define the last task that is running on the EMB processor
|
|
+ TASK_LAST_EMB = TASK_RXU,
|
|
+ // nX API task
|
|
+ TASK_API,
|
|
+ TASK_MAX,
|
|
+};
|
|
+
|
|
+
|
|
+/// For MAC HW States copied from "hal_machw.h"
|
|
+enum
|
|
+{
|
|
+ /// MAC HW IDLE State.
|
|
+ HW_IDLE = 0,
|
|
+ /// MAC HW RESERVED State.
|
|
+ HW_RESERVED,
|
|
+ /// MAC HW DOZE State.
|
|
+ HW_DOZE,
|
|
+ /// MAC HW ACTIVE State.
|
|
+ HW_ACTIVE
|
|
+};
|
|
+
|
|
+/// Power Save mode setting
|
|
+enum mm_ps_mode_state
|
|
+{
|
|
+ MM_PS_MODE_OFF,
|
|
+ MM_PS_MODE_ON,
|
|
+ MM_PS_MODE_ON_DYN,
|
|
+};
|
|
+
|
|
+/// Status/error codes used in the MAC software.
|
|
+enum
|
|
+{
|
|
+ CO_OK,
|
|
+ CO_FAIL,
|
|
+ CO_EMPTY,
|
|
+ CO_FULL,
|
|
+ CO_BAD_PARAM,
|
|
+ CO_NOT_FOUND,
|
|
+ CO_NO_MORE_ELT_AVAILABLE,
|
|
+ CO_NO_ELT_IN_USE,
|
|
+ CO_BUSY,
|
|
+ CO_OP_IN_PROGRESS,
|
|
+};
|
|
+
|
|
+/// Remain on channel operation codes
|
|
+enum mm_remain_on_channel_op
|
|
+{
|
|
+ MM_ROC_OP_START = 0,
|
|
+ MM_ROC_OP_CANCEL,
|
|
+};
|
|
+
|
|
+#define DRV_TASK_ID 100
|
|
+
|
|
+/// Message Identifier. The number of messages is limited to 0xFFFF.
|
|
+/// The message ID is divided in two parts:
|
|
+/// - bits[15..10] : task index (no more than 64 tasks supported).
|
|
+/// - bits[9..0] : message index (no more that 1024 messages per task).
|
|
+typedef u16 lmac_msg_type_t;
|
|
+typedef u16 lmac_msg_len_t;
|
|
+typedef u16 lmac_msg_id_t;
|
|
+typedef u16 lmac_task_id_t;
|
|
+
|
|
+/// Build the first message ID of a task.
|
|
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10))
|
|
+
|
|
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10))
|
|
+#define MSG_I(msg) ((msg) & ((1<<10)-1))
|
|
+
|
|
+struct sdio_hdr
|
|
+{
|
|
+ u16 len;
|
|
+ u16 type;
|
|
+ u16 queue_idx;
|
|
+ u16 reserved;
|
|
+}__packed;
|
|
+
|
|
+/// Message structure.
|
|
+struct lmac_msg
|
|
+{
|
|
+ struct sdio_hdr sdio_hdr;
|
|
+ lmac_msg_id_t id; ///< Message id.
|
|
+ lmac_task_id_t dest_id; ///< Destination kernel identifier.
|
|
+ lmac_task_id_t src_id; ///< Source kernel identifier.
|
|
+ u16 param_len; ///< Parameter embedded struct length.
|
|
+ u8 param[]; ///< Parameter embedded struct. Must be word-aligned.
|
|
+};
|
|
+
|
|
+enum lmac_msg_type
|
|
+{
|
|
+ BL_TYPE_MSG,
|
|
+ BL_TYPE_ACK,
|
|
+ BL_TYPE_DBG,
|
|
+ BL_TYPE_DATA,
|
|
+ BL_TYPE_TXCFM,
|
|
+ BL_TYPE_AGG_REORD_MSG,
|
|
+ BL_TYPE_DBG_DUMP_START,
|
|
+ BL_TYPE_DBG_DUMP_END,
|
|
+ BL_TYPE_DBG_LA_TRACE,
|
|
+ BL_TYPE_DBG_RHD_DESC,
|
|
+ BL_TYPE_DBG_RBD_DESC,
|
|
+ BL_TYPE_DBG_TX_DESC,
|
|
+ BL_TYPE_DUMP_INFO,
|
|
+ BL_TYPE_TX_STOP,
|
|
+ BL_TYPE_TX_RESUME,
|
|
+ BL_TYPE_MAX,
|
|
+};
|
|
+
|
|
+/// List of messages related to the task.
|
|
+enum mm_msg_tag
|
|
+{
|
|
+ /// RESET Request.
|
|
+ MM_RESET_REQ = LMAC_FIRST_MSG(TASK_MM),
|
|
+ /// RESET Confirmation.
|
|
+ MM_RESET_CFM,
|
|
+ /// START Request.
|
|
+ MM_START_REQ,
|
|
+ /// START Confirmation.
|
|
+ MM_START_CFM,
|
|
+ /// Read Version Request.
|
|
+ MM_VERSION_REQ,
|
|
+ /// Read Version Confirmation.
|
|
+ MM_VERSION_CFM,
|
|
+ /// ADD INTERFACE Request.
|
|
+ MM_ADD_IF_REQ,
|
|
+ /// ADD INTERFACE Confirmation.
|
|
+ MM_ADD_IF_CFM,
|
|
+ /// REMOVE INTERFACE Request.
|
|
+ MM_REMOVE_IF_REQ,
|
|
+ /// REMOVE INTERFACE Confirmation.
|
|
+ MM_REMOVE_IF_CFM,
|
|
+ /// STA ADD Request.
|
|
+ MM_STA_ADD_REQ,
|
|
+ /// STA ADD Confirm.
|
|
+ MM_STA_ADD_CFM,
|
|
+ /// STA DEL Request.
|
|
+ MM_STA_DEL_REQ,
|
|
+ /// STA DEL Confirm.
|
|
+ MM_STA_DEL_CFM,
|
|
+ /// RX FILTER CONFIGURATION Request.
|
|
+ MM_SET_FILTER_REQ,
|
|
+ /// RX FILTER CONFIGURATION Confirmation.
|
|
+ MM_SET_FILTER_CFM,
|
|
+ /// CHANNEL CONFIGURATION Request.
|
|
+ MM_SET_CHANNEL_REQ,
|
|
+ /// CHANNEL CONFIGURATION Confirmation.
|
|
+ MM_SET_CHANNEL_CFM,
|
|
+ /// DTIM PERIOD CONFIGURATION Request.
|
|
+ MM_SET_DTIM_REQ,
|
|
+ /// DTIM PERIOD CONFIGURATION Confirmation.
|
|
+ MM_SET_DTIM_CFM,
|
|
+ /// BEACON INTERVAL CONFIGURATION Request.
|
|
+ MM_SET_BEACON_INT_REQ,
|
|
+ /// BEACON INTERVAL CONFIGURATION Confirmation.
|
|
+ MM_SET_BEACON_INT_CFM,
|
|
+ /// BASIC RATES CONFIGURATION Request.
|
|
+ MM_SET_BASIC_RATES_REQ,
|
|
+ /// BASIC RATES CONFIGURATION Confirmation.
|
|
+ MM_SET_BASIC_RATES_CFM,
|
|
+ /// BSSID CONFIGURATION Request.
|
|
+ MM_SET_BSSID_REQ,
|
|
+ /// BSSID CONFIGURATION Confirmation.
|
|
+ MM_SET_BSSID_CFM,
|
|
+ /// EDCA PARAMETERS CONFIGURATION Request.
|
|
+ MM_SET_EDCA_REQ,
|
|
+ /// EDCA PARAMETERS CONFIGURATION Confirmation.
|
|
+ MM_SET_EDCA_CFM,
|
|
+ /// ABGN MODE CONFIGURATION Request.
|
|
+ MM_SET_MODE_REQ,
|
|
+ /// ABGN MODE CONFIGURATION Confirmation.
|
|
+ MM_SET_MODE_CFM,
|
|
+ /// Request setting the VIF active state (i.e associated or AP started)
|
|
+ MM_SET_VIF_STATE_REQ,
|
|
+ /// Confirmation of the @ref MM_SET_VIF_STATE_REQ message.
|
|
+ MM_SET_VIF_STATE_CFM,
|
|
+ /// SLOT TIME PARAMETERS CONFIGURATION Request.
|
|
+ MM_SET_SLOTTIME_REQ,
|
|
+ /// SLOT TIME PARAMETERS CONFIGURATION Confirmation.
|
|
+ MM_SET_SLOTTIME_CFM,
|
|
+ /// Power Mode Change Request.
|
|
+ MM_SET_IDLE_REQ,
|
|
+ /// Power Mode Change Confirm.
|
|
+ MM_SET_IDLE_CFM,
|
|
+ /// KEY ADD Request.
|
|
+ MM_KEY_ADD_REQ,
|
|
+ /// KEY ADD Confirm.
|
|
+ MM_KEY_ADD_CFM,
|
|
+ /// KEY DEL Request.
|
|
+ MM_KEY_DEL_REQ,
|
|
+ /// KEY DEL Confirm.
|
|
+ MM_KEY_DEL_CFM,
|
|
+ /// Block Ack agreement info addition
|
|
+ MM_BA_ADD_REQ,
|
|
+ /// Block Ack agreement info addition confirmation
|
|
+ MM_BA_ADD_CFM,
|
|
+ /// Block Ack agreement info deletion
|
|
+ MM_BA_DEL_REQ,
|
|
+ /// Block Ack agreement info deletion confirmation
|
|
+ MM_BA_DEL_CFM,
|
|
+ /// Indication of the primary TBTT to the upper MAC. Upon the reception of this
|
|
+ // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
|
|
+ MM_PRIMARY_TBTT_IND,
|
|
+ /// Indication of the secondary TBTT to the upper MAC. Upon the reception of this
|
|
+ // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
|
|
+ MM_SECONDARY_TBTT_IND,
|
|
+ /// Request for changing the TX power
|
|
+ MM_SET_POWER_REQ,
|
|
+ /// Confirmation of the TX power change
|
|
+ MM_SET_POWER_CFM,
|
|
+ /// Request to the LMAC to trigger the embedded logic analyzer and forward the debug
|
|
+ /// dump.
|
|
+ MM_DBG_TRIGGER_REQ,
|
|
+ /// Set Power Save mode
|
|
+ MM_SET_PS_MODE_REQ,
|
|
+ /// Set Power Save mode confirmation
|
|
+ MM_SET_PS_MODE_CFM,
|
|
+ /// Request to add a channel context
|
|
+ MM_CHAN_CTXT_ADD_REQ,
|
|
+ /// Confirmation of the channel context addition
|
|
+ MM_CHAN_CTXT_ADD_CFM,
|
|
+ /// Request to delete a channel context
|
|
+ MM_CHAN_CTXT_DEL_REQ,
|
|
+ /// Confirmation of the channel context deletion
|
|
+ MM_CHAN_CTXT_DEL_CFM,
|
|
+ /// Request to link a channel context to a VIF
|
|
+ MM_CHAN_CTXT_LINK_REQ,
|
|
+ /// Confirmation of the channel context link
|
|
+ MM_CHAN_CTXT_LINK_CFM,
|
|
+ /// Request to unlink a channel context from a VIF
|
|
+ MM_CHAN_CTXT_UNLINK_REQ,
|
|
+ /// Confirmation of the channel context unlink
|
|
+ MM_CHAN_CTXT_UNLINK_CFM,
|
|
+ /// Request to update a channel context
|
|
+ MM_CHAN_CTXT_UPDATE_REQ,
|
|
+ /// Confirmation of the channel context update
|
|
+ MM_CHAN_CTXT_UPDATE_CFM,
|
|
+ /// Request to schedule a channel context
|
|
+ MM_CHAN_CTXT_SCHED_REQ,
|
|
+ /// Confirmation of the channel context scheduling
|
|
+ MM_CHAN_CTXT_SCHED_CFM,
|
|
+ /// Request to change the beacon template in LMAC
|
|
+ MM_BCN_CHANGE_REQ,
|
|
+ /// Confirmation of the beacon change
|
|
+ MM_BCN_CHANGE_CFM,
|
|
+ /// Request to update the TIM in the beacon (i.e to indicate traffic bufferized at AP)
|
|
+ MM_TIM_UPDATE_REQ,
|
|
+ /// Confirmation of the TIM update
|
|
+ MM_TIM_UPDATE_CFM,
|
|
+ /// Connection loss indication
|
|
+ MM_CONNECTION_LOSS_IND,
|
|
+ /// Channel context switch indication to the upper layers
|
|
+ MM_CHANNEL_SWITCH_IND,
|
|
+ /// Channel context pre-switch indication to the upper layers
|
|
+ MM_CHANNEL_PRE_SWITCH_IND,
|
|
+ /// Request to remain on channel or cancel remain on channel
|
|
+ MM_REMAIN_ON_CHANNEL_REQ,
|
|
+ /// Confirmation of the (cancel) remain on channel request
|
|
+ MM_REMAIN_ON_CHANNEL_CFM,
|
|
+ /// Remain on channel expired indication
|
|
+ MM_REMAIN_ON_CHANNEL_EXP_IND,
|
|
+ /// Indication of a PS state change of a peer device
|
|
+ MM_PS_CHANGE_IND,
|
|
+ /// Indication that some buffered traffic should be sent to the peer device
|
|
+ MM_TRAFFIC_REQ_IND,
|
|
+ /// Request to modify the STA Power-save mode options
|
|
+ MM_SET_PS_OPTIONS_REQ,
|
|
+ /// Confirmation of the PS options setting
|
|
+ MM_SET_PS_OPTIONS_CFM,
|
|
+ /// Indication of PS state change for a P2P VIF
|
|
+ MM_P2P_VIF_PS_CHANGE_IND,
|
|
+ /// Indication that CSA counter has been updated
|
|
+ MM_CSA_COUNTER_IND,
|
|
+ /// Channel occupation report indication
|
|
+ MM_CHANNEL_SURVEY_IND,
|
|
+ /// Message containing Beamformer Information
|
|
+ MM_BFMER_ENABLE_REQ,
|
|
+ /// Request to Start/Stop/Update NOA - GO Only
|
|
+ MM_SET_P2P_NOA_REQ,
|
|
+ /// Request to Start/Stop/Update Opportunistic PS - GO Only
|
|
+ MM_SET_P2P_OPPPS_REQ,
|
|
+ /// Start/Stop/Update NOA Confirmation
|
|
+ MM_SET_P2P_NOA_CFM,
|
|
+ /// Start/Stop/Update Opportunistic PS Confirmation
|
|
+ MM_SET_P2P_OPPPS_CFM,
|
|
+ /// P2P NoA Update Indication - GO Only
|
|
+ MM_P2P_NOA_UPD_IND,
|
|
+ /// Request to set RSSI threshold and RSSI hysteresis
|
|
+ MM_CFG_RSSI_REQ,
|
|
+ /// Indication that RSSI level is below or above the threshold
|
|
+ MM_RSSI_STATUS_IND,
|
|
+ /// Indication that CSA is done
|
|
+ MM_CSA_FINISH_IND,
|
|
+ /// Indication that CSA is in prorgess (resp. done) and traffic must be stopped (resp. restarted)
|
|
+ MM_CSA_TRAFFIC_IND,
|
|
+ /// Request to update the group information of a station
|
|
+ MM_MU_GROUP_UPDATE_REQ,
|
|
+ /// Confirmation of the @ref MM_MU_GROUP_UPDATE_REQ message
|
|
+ MM_MU_GROUP_UPDATE_CFM,
|
|
+
|
|
+ /// MAX number of messages
|
|
+ MM_MAX,
|
|
+};
|
|
+
|
|
+/// Interface types
|
|
+enum
|
|
+{
|
|
+ /// ESS STA interface
|
|
+ MM_STA,
|
|
+ /// IBSS STA interface
|
|
+ MM_IBSS,
|
|
+ /// AP interface
|
|
+ MM_AP,
|
|
+ // Mesh Point interface
|
|
+ MM_MESH_POINT,
|
|
+};
|
|
+
|
|
+///BA agreement types
|
|
+enum
|
|
+{
|
|
+ ///BlockAck agreement for TX
|
|
+ BA_AGMT_TX,
|
|
+ ///BlockAck agreement for RX
|
|
+ BA_AGMT_RX,
|
|
+};
|
|
+
|
|
+///BA agreement related status
|
|
+enum
|
|
+{
|
|
+ ///Correct BA agreement establishment
|
|
+ BA_AGMT_ESTABLISHED,
|
|
+ ///BA agreement already exists for STA+TID requested, cannot override it (should have been deleted first)
|
|
+ BA_AGMT_ALREADY_EXISTS,
|
|
+ ///Correct BA agreement deletion
|
|
+ BA_AGMT_DELETED,
|
|
+ ///BA agreement for the (STA, TID) doesn't exist so nothing to delete
|
|
+ BA_AGMT_DOESNT_EXIST,
|
|
+};
|
|
+
|
|
+/// Features supported by LMAC - Positions
|
|
+enum mm_features
|
|
+{
|
|
+ /// Beaconing
|
|
+ MM_FEAT_BCN_BIT = 0,
|
|
+ /// Autonomous Beacon Transmission
|
|
+ MM_FEAT_AUTOBCN_BIT,
|
|
+ /// Scan in LMAC
|
|
+ MM_FEAT_HWSCAN_BIT,
|
|
+ /// Connection Monitoring
|
|
+ MM_FEAT_CMON_BIT,
|
|
+ /// Multi Role
|
|
+ MM_FEAT_MROLE_BIT,
|
|
+ /// Radar Detection
|
|
+ MM_FEAT_RADAR_BIT,
|
|
+ /// Power Save
|
|
+ MM_FEAT_PS_BIT,
|
|
+ /// UAPSD
|
|
+ MM_FEAT_UAPSD_BIT,
|
|
+ /// DPSM
|
|
+ MM_FEAT_DPSM_BIT,
|
|
+ /// A-MPDU
|
|
+ MM_FEAT_AMPDU_BIT,
|
|
+ /// A-MSDU
|
|
+ MM_FEAT_AMSDU_BIT,
|
|
+ /// Channel Context
|
|
+ MM_FEAT_CHNL_CTXT_BIT,
|
|
+ /// Packet reordering
|
|
+ MM_FEAT_REORD_BIT,
|
|
+ /// P2P
|
|
+ MM_FEAT_P2P_BIT,
|
|
+ /// P2P Go
|
|
+ MM_FEAT_P2P_GO_BIT,
|
|
+ /// UMAC Present
|
|
+ MM_FEAT_UMAC_BIT,
|
|
+ /// VHT support
|
|
+ MM_FEAT_VHT_BIT,
|
|
+ /// Beamformee
|
|
+ MM_FEAT_BFMEE_BIT,
|
|
+ /// Beamformer
|
|
+ MM_FEAT_BFMER_BIT,
|
|
+ /// WAPI
|
|
+ MM_FEAT_WAPI_BIT,
|
|
+ /// MFP
|
|
+ MM_FEAT_MFP_BIT,
|
|
+};
|
|
+
|
|
+/// Maximum number of words in the configuration buffer
|
|
+#define PHY_CFG_BUF_SIZE 16
|
|
+
|
|
+/// Structure containing the parameters of the PHY configuration
|
|
+struct phy_cfg_tag
|
|
+{
|
|
+ /// Buffer containing the parameters specific for the PHY used
|
|
+ u32_l parameters[PHY_CFG_BUF_SIZE];
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the Trident PHY configuration
|
|
+struct phy_trd_cfg_tag
|
|
+{
|
|
+ /// MDM type(nxm)(upper nibble) and MDM2RF path mapping(lower nibble)
|
|
+ u8_l path_mapping;
|
|
+ /// TX DC offset compensation
|
|
+ u32_l tx_dc_off_comp;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the Karst PHY configuration
|
|
+struct phy_karst_cfg_tag
|
|
+{
|
|
+ /// TX IQ mismatch compensation in 2.4GHz
|
|
+ u32_l tx_iq_comp_2_4G[2];
|
|
+ /// RX IQ mismatch compensation in 2.4GHz
|
|
+ u32_l rx_iq_comp_2_4G[2];
|
|
+ /// TX IQ mismatch compensation in 5GHz
|
|
+ u32_l tx_iq_comp_5G[2];
|
|
+ /// RX IQ mismatch compensation in 5GHz
|
|
+ u32_l rx_iq_comp_5G[2];
|
|
+ /// RF path used by default (0 or 1)
|
|
+ u8_l path_used;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_START_REQ message
|
|
+struct mm_start_req
|
|
+{
|
|
+ /// PHY configuration
|
|
+ struct phy_cfg_tag phy_cfg;
|
|
+ /// UAPSD timeout
|
|
+ u32_l uapsd_timeout;
|
|
+ /// Local LP clock accuracy (in ppm)
|
|
+ u16_l lp_clk_accuracy;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_REQ message
|
|
+struct mm_set_channel_req
|
|
+{
|
|
+ /// Band (2.4GHz or 5GHz)
|
|
+ u8_l band;
|
|
+ /// Channel type: 20,40,80,160 or 80+80 MHz
|
|
+ u8_l type;
|
|
+ /// Frequency for Primary 20MHz channel (in MHz)
|
|
+ u16_l prim20_freq;
|
|
+ /// Frequency for Center of the contiguous channel or center of Primary 80+80
|
|
+ u16_l center1_freq;
|
|
+ /// Frequency for Center of the non-contiguous secondary 80+80
|
|
+ u16_l center2_freq;
|
|
+ /// Index of the RF for which the channel has to be set (0: operating (primary), 1: secondary
|
|
+ /// RF (used for additional radar detection). This parameter is reserved if no secondary RF
|
|
+ /// is available in the system
|
|
+ u8_l index;
|
|
+ /// Max tx power for this channel
|
|
+ s8_l tx_power;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_CFM message
|
|
+struct mm_set_channel_cfm
|
|
+{
|
|
+ /// Radio index to be used in policy table
|
|
+ u8_l radio_idx;
|
|
+ /// TX power configured (in dBm)
|
|
+ s8_l power;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_DTIM_REQ message
|
|
+struct mm_set_dtim_req
|
|
+{
|
|
+ /// DTIM period
|
|
+ u8_l dtim_period;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_POWER_REQ message
|
|
+struct mm_set_power_req
|
|
+{
|
|
+ /// Index of the interface for which the parameter is configured
|
|
+ u8_l inst_nbr;
|
|
+ /// TX power (in dBm)
|
|
+ s8_l power;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_POWER_CFM message
|
|
+struct mm_set_power_cfm
|
|
+{
|
|
+ /// Radio index to be used in policy table
|
|
+ u8_l radio_idx;
|
|
+ /// TX power configured (in dBm)
|
|
+ s8_l power;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_BEACON_INT_REQ message
|
|
+struct mm_set_beacon_int_req
|
|
+{
|
|
+ /// Beacon interval
|
|
+ u16_l beacon_int;
|
|
+ /// Index of the interface for which the parameter is configured
|
|
+ u8_l inst_nbr;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_BASIC_RATES_REQ message
|
|
+struct mm_set_basic_rates_req
|
|
+{
|
|
+ /// Basic rate set (as expected by bssBasicRateSet field of Rates MAC HW register)
|
|
+ u32_l rates;
|
|
+ /// Index of the interface for which the parameter is configured
|
|
+ u8_l inst_nbr;
|
|
+ /// Band on which the interface will operate
|
|
+ u8_l band;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_BSSID_REQ message
|
|
+struct mm_set_bssid_req
|
|
+{
|
|
+ /// BSSID to be configured in HW
|
|
+ struct mac_addr bssid;
|
|
+ /// Index of the interface for which the parameter is configured
|
|
+ u8_l inst_nbr;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_FILTER_REQ message
|
|
+struct mm_set_filter_req
|
|
+{
|
|
+ /// RX filter to be put into rxCntrlReg HW register
|
|
+ u32_l filter;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_ADD_IF_REQ message.
|
|
+struct mm_add_if_req
|
|
+{
|
|
+ /// Type of the interface (AP, STA, ADHOC, ...)
|
|
+ u8_l type;
|
|
+ /// MAC ADDR of the interface to start
|
|
+ struct mac_addr addr;
|
|
+ /// P2P Interface
|
|
+ bool_l p2p;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_EDCA_REQ message
|
|
+struct mm_set_edca_req
|
|
+{
|
|
+ /// EDCA parameters of the queue (as expected by edcaACxReg HW register)
|
|
+ u32_l ac_param;
|
|
+ /// Flag indicating if UAPSD can be used on this queue
|
|
+ bool_l uapsd;
|
|
+ /// HW queue for which the parameters are configured
|
|
+ u8_l hw_queue;
|
|
+ /// Index of the interface for which the parameters are configured
|
|
+ u8_l inst_nbr;
|
|
+};
|
|
+
|
|
+struct mm_set_idle_req
|
|
+{
|
|
+ u8_l hw_idle;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_SLOTTIME_REQ message
|
|
+struct mm_set_slottime_req
|
|
+{
|
|
+ /// Slot time expressed in us
|
|
+ u8_l slottime;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_MODE_REQ message
|
|
+struct mm_set_mode_req
|
|
+{
|
|
+ /// abgnMode field of macCntrl1Reg register
|
|
+ u8_l abgnmode;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_VIF_STATE_REQ message
|
|
+struct mm_set_vif_state_req
|
|
+{
|
|
+ /// Association Id received from the AP (valid only if the VIF is of STA type)
|
|
+ u16_l aid;
|
|
+ /// Flag indicating if the VIF is active or not
|
|
+ bool_l active;
|
|
+ /// Interface index
|
|
+ u8_l inst_nbr;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_ADD_IF_CFM message.
|
|
+struct mm_add_if_cfm
|
|
+{
|
|
+ /// Status of operation (different from 0 if unsuccessful)
|
|
+ u8_l status;
|
|
+ /// Interface index assigned by the LMAC
|
|
+ u8_l inst_nbr;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_REMOVE_IF_REQ message.
|
|
+struct mm_remove_if_req
|
|
+{
|
|
+ /// Interface index assigned by the LMAC
|
|
+ u8_l inst_nbr;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_VERSION_CFM message.
|
|
+struct mm_version_cfm
|
|
+{
|
|
+ /// Version of the LMAC FW
|
|
+ u32_l version_lmac;
|
|
+ /// Version1 of the MAC HW (as encoded in version1Reg MAC HW register)
|
|
+ u32_l version_machw_1;
|
|
+ /// Version2 of the MAC HW (as encoded in version2Reg MAC HW register)
|
|
+ u32_l version_machw_2;
|
|
+ /// Version1 of the PHY (depends on actual PHY)
|
|
+ u32_l version_phy_1;
|
|
+ /// Version2 of the PHY (depends on actual PHY)
|
|
+ u32_l version_phy_2;
|
|
+ /// Supported Features
|
|
+ u32_l features;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_STA_ADD_REQ message.
|
|
+struct mm_sta_add_req
|
|
+{
|
|
+ /// Maximum A-MPDU size, in bytes, for VHT frames
|
|
+ u32_l ampdu_size_max_vht;
|
|
+ /// PAID/GID
|
|
+ u32_l paid_gid;
|
|
+ /// Maximum A-MPDU size, in bytes, for HT frames
|
|
+ u16_l ampdu_size_max_ht;
|
|
+ /// MAC address of the station to be added
|
|
+ struct mac_addr mac_addr;
|
|
+ /// A-MPDU spacing, in us
|
|
+ u8_l ampdu_spacing_min;
|
|
+ /// Interface index
|
|
+ u8_l inst_nbr;
|
|
+ /// TDLS station
|
|
+ bool_l tdls_sta;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_STA_ADD_CFM message.
|
|
+struct mm_sta_add_cfm
|
|
+{
|
|
+ /// Status of the operation (different from 0 if unsuccessful)
|
|
+ u8_l status;
|
|
+ /// Index assigned by the LMAC to the newly added station
|
|
+ u8_l sta_idx;
|
|
+ /// MAC HW index of the newly added station
|
|
+ u8_l hw_sta_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_STA_DEL_REQ message.
|
|
+struct mm_sta_del_req
|
|
+{
|
|
+ /// Index of the station to be deleted
|
|
+ u8_l sta_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_STA_DEL_CFM message.
|
|
+struct mm_sta_del_cfm
|
|
+{
|
|
+ /// Status of the operation (different from 0 if unsuccessful)
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the SET_POWER_MODE REQ message.
|
|
+struct mm_setpowermode_req
|
|
+{
|
|
+ u8_l mode;
|
|
+ u8_l sta_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the SET_POWER_MODE CFM message.
|
|
+struct mm_setpowermode_cfm
|
|
+{
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_KEY_ADD REQ message.
|
|
+struct mm_key_add_req
|
|
+{
|
|
+ /// Key index (valid only for default keys)
|
|
+ u8_l key_idx;
|
|
+ /// STA index (valid only for pairwise or mesh group keys)
|
|
+ u8_l sta_idx;
|
|
+ /// Key material
|
|
+ struct mac_sec_key key;
|
|
+ /// Cipher suite (WEP64, WEP128, TKIP, CCMP)
|
|
+ u8_l cipher_suite;
|
|
+ /// Index of the interface for which the key is set (valid only for default keys or mesh group keys)
|
|
+ u8_l inst_nbr;
|
|
+ /// A-MSDU SPP parameter
|
|
+ u8_l spp;
|
|
+ /// Indicate if provided key is a pairwise key or not
|
|
+ bool_l pairwise;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_KEY_ADD_CFM message.
|
|
+struct mm_key_add_cfm
|
|
+{
|
|
+ /// Status of the operation (different from 0 if unsuccessful)
|
|
+ u8_l status;
|
|
+ /// HW index of the key just added
|
|
+ u8_l hw_key_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_KEY_DEL_REQ message.
|
|
+struct mm_key_del_req
|
|
+{
|
|
+ /// HW index of the key to be deleted
|
|
+ u8_l hw_key_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_BA_ADD_REQ message.
|
|
+struct mm_ba_add_req
|
|
+{
|
|
+ ///Type of agreement (0: TX, 1: RX)
|
|
+ u8_l type;
|
|
+ ///Index of peer station with which the agreement is made
|
|
+ u8_l sta_idx;
|
|
+ ///TID for which the agreement is made with peer station
|
|
+ u8_l tid;
|
|
+ ///Buffer size - number of MPDUs that can be held in its buffer per TID
|
|
+ u8_l bufsz;
|
|
+ /// Start sequence number negotiated during BA setup - the one in first aggregated MPDU counts more
|
|
+ u16_l ssn;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_BA_ADD_CFM message.
|
|
+struct mm_ba_add_cfm
|
|
+{
|
|
+ ///Index of peer station for which the agreement is being confirmed
|
|
+ u8_l sta_idx;
|
|
+ ///TID for which the agreement is being confirmed
|
|
+ u8_l tid;
|
|
+ /// Status of ba establishment
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_BA_DEL_REQ message.
|
|
+struct mm_ba_del_req
|
|
+{
|
|
+ ///Type of agreement (0: TX, 1: RX)
|
|
+ u8_l type;
|
|
+ ///Index of peer station for which the agreement is being deleted
|
|
+ u8_l sta_idx;
|
|
+ ///TID for which the agreement is being deleted
|
|
+ u8_l tid;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_BA_DEL_CFM message.
|
|
+struct mm_ba_del_cfm
|
|
+{
|
|
+ ///Index of peer station for which the agreement deletion is being confirmed
|
|
+ u8_l sta_idx;
|
|
+ ///TID for which the agreement deletion is being confirmed
|
|
+ u8_l tid;
|
|
+ /// Status of ba deletion
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
|
|
+struct mm_chan_ctxt_add_req
|
|
+{
|
|
+ /// Band (2.4GHz or 5GHz)
|
|
+ u8_l band;
|
|
+ /// Channel type: 20,40,80,160 or 80+80 MHz
|
|
+ u8_l type;
|
|
+ /// Frequency for Primary 20MHz channel (in MHz)
|
|
+ u16_l prim20_freq;
|
|
+ /// Frequency for Center of the contiguous channel or center of Primary 80+80
|
|
+ u16_l center1_freq;
|
|
+ /// Frequency for Center of the non-contiguous secondary 80+80
|
|
+ u16_l center2_freq;
|
|
+ /// Max tx power for this channel
|
|
+ s8_l tx_power;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
|
|
+struct mm_chan_ctxt_add_cfm
|
|
+{
|
|
+ /// Status of the addition
|
|
+ u8_l status;
|
|
+ /// Index of the new channel context
|
|
+ u8_l index;
|
|
+};
|
|
+
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_DEL_REQ message
|
|
+struct mm_chan_ctxt_del_req
|
|
+{
|
|
+ /// Index of the new channel context to be deleted
|
|
+ u8_l index;
|
|
+};
|
|
+
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_LINK_REQ message
|
|
+struct mm_chan_ctxt_link_req
|
|
+{
|
|
+ /// VIF index
|
|
+ u8_l vif_index;
|
|
+ /// Channel context index
|
|
+ u8_l chan_index;
|
|
+ /// Indicate if this is a channel switch (unlink current ctx first if true)
|
|
+ u8_l chan_switch;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UNLINK_REQ message
|
|
+struct mm_chan_ctxt_unlink_req
|
|
+{
|
|
+ /// VIF index
|
|
+ u8_l vif_index;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UPDATE_REQ message
|
|
+struct mm_chan_ctxt_update_req
|
|
+{
|
|
+ /// Channel context index
|
|
+ u8_l chan_index;
|
|
+ /// Band (2.4GHz or 5GHz)
|
|
+ u8_l band;
|
|
+ /// Channel type: 20,40,80,160 or 80+80 MHz
|
|
+ u8_l type;
|
|
+ /// Frequency for Primary 20MHz channel (in MHz)
|
|
+ u16_l prim20_freq;
|
|
+ /// Frequency for Center of the contiguous channel or center of Primary 80+80
|
|
+ u16_l center1_freq;
|
|
+ /// Frequency for Center of the non-contiguous secondary 80+80
|
|
+ u16_l center2_freq;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_SCHED_REQ message
|
|
+struct mm_chan_ctxt_sched_req
|
|
+{
|
|
+ /// VIF index
|
|
+ u8_l vif_index;
|
|
+ /// Channel context index
|
|
+ u8_l chan_index;
|
|
+ /// Type of the scheduling request (0: normal scheduling, 1: derogatory
|
|
+ /// scheduling)
|
|
+ u8_l type;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHANNEL_SWITCH_IND message
|
|
+struct mm_channel_switch_ind
|
|
+{
|
|
+ /// Index of the channel context we will switch to
|
|
+ u8_l chan_index;
|
|
+ /// Indicate if the switch has been triggered by a Remain on channel request
|
|
+ bool_l roc;
|
|
+ /// VIF on which remain on channel operation has been started (if roc == 1)
|
|
+ u8_l vif_index;
|
|
+ /// Indicate if the switch has been triggered by a TDLS Remain on channel request
|
|
+ bool_l roc_tdls;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHANNEL_PRE_SWITCH_IND message
|
|
+struct mm_channel_pre_switch_ind
|
|
+{
|
|
+ /// Index of the channel context we will switch to
|
|
+ u8_l chan_index;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CONNECTION_LOSS_IND message.
|
|
+struct mm_connection_loss_ind
|
|
+{
|
|
+ /// VIF instance number
|
|
+ u8_l inst_nbr;
|
|
+};
|
|
+
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_DBG_TRIGGER_REQ message.
|
|
+struct mm_dbg_trigger_req
|
|
+{
|
|
+ /// Error trace to be reported by the LMAC
|
|
+ char error[64];
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_PS_MODE_REQ message.
|
|
+struct mm_set_ps_mode_req
|
|
+{
|
|
+ /// Power Save is activated or deactivated
|
|
+ u8_l new_state;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_BCN_CHANGE_REQ message.
|
|
+#define BCN_MAX_CSA_CPT 2
|
|
+struct mm_bcn_change_req
|
|
+{
|
|
+ /// Pointer, in host memory, to the new beacon template
|
|
+ u32_l bcn_ptr;
|
|
+ /// Length of the beacon template
|
|
+ u16_l bcn_len;
|
|
+ /// Offset of the TIM IE in the beacon
|
|
+ u16_l tim_oft;
|
|
+ /// Length of the TIM IE
|
|
+ u8_l tim_len;
|
|
+ /// Index of the VIF for which the beacon is updated
|
|
+ u8_l inst_nbr;
|
|
+ /// Offset of CSA (channel switch announcement) counters (0 means no counter)
|
|
+ u8_l csa_oft[BCN_MAX_CSA_CPT];
|
|
+ ///
|
|
+ u8_l bcn_buf[];
|
|
+};
|
|
+
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_TIM_UPDATE_REQ message.
|
|
+struct mm_tim_update_req
|
|
+{
|
|
+ /// Association ID of the STA the bit of which has to be updated (0 for BC/MC traffic)
|
|
+ u16_l aid;
|
|
+ /// Flag indicating the availability of data packets for the given STA
|
|
+ u8_l tx_avail;
|
|
+ /// Index of the VIF for which the TIM is updated
|
|
+ u8_l inst_nbr;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_REQ message.
|
|
+struct mm_remain_on_channel_req
|
|
+{
|
|
+ /// Operation Code
|
|
+ u8_l op_code;
|
|
+ /// VIF Index
|
|
+ u8_l vif_index;
|
|
+ /// Band (2.4GHz or 5GHz)
|
|
+ u8_l band;
|
|
+ /// Channel type: 20,40,80,160 or 80+80 MHz
|
|
+ u8_l type;
|
|
+ /// Frequency for Primary 20MHz channel (in MHz)
|
|
+ u16_l prim20_freq;
|
|
+ /// Frequency for Center of the contiguous channel or center of Primary 80+80
|
|
+ u16_l center1_freq;
|
|
+ /// Frequency for Center of the non-contiguous secondary 80+80
|
|
+ u16_l center2_freq;
|
|
+ /// Duration (in ms)
|
|
+ u32_l duration_ms;
|
|
+ /// TX power (in dBm)
|
|
+ s8_l tx_power;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_CFM message
|
|
+struct mm_remain_on_channel_cfm
|
|
+{
|
|
+ /// Operation Code
|
|
+ u8_l op_code;
|
|
+ /// Status of the operation
|
|
+ u8_l status;
|
|
+ /// Channel Context index
|
|
+ u8_l chan_ctxt_index;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_EXP_IND message
|
|
+struct mm_remain_on_channel_exp_ind
|
|
+{
|
|
+ /// VIF Index
|
|
+ u8_l vif_index;
|
|
+ /// Channel Context index
|
|
+ u8_l chan_ctxt_index;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_REQ message.
|
|
+struct mm_set_uapsd_tmr_req
|
|
+{
|
|
+ /// action: Start or Stop the timer
|
|
+ u8_l action;
|
|
+ /// timeout value, in milliseconds
|
|
+ u32_l timeout;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_CFM message.
|
|
+struct mm_set_uapsd_tmr_cfm
|
|
+{
|
|
+ /// Status of the operation (different from 0 if unsuccessful)
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_PS_CHANGE_IND message
|
|
+struct mm_ps_change_ind
|
|
+{
|
|
+ /// Index of the peer device that is switching its PS state
|
|
+ u8_l sta_idx;
|
|
+ /// New PS state of the peer device (0: active, 1: sleeping)
|
|
+ u8_l ps_state;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_P2P_VIF_PS_CHANGE_IND message
|
|
+struct mm_p2p_vif_ps_change_ind
|
|
+{
|
|
+ /// Index of the P2P VIF that is switching its PS state
|
|
+ u8_l vif_index;
|
|
+ /// New PS state of the P2P VIF interface (0: active, 1: sleeping)
|
|
+ u8_l ps_state;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_TRAFFIC_REQ_IND message
|
|
+struct mm_traffic_req_ind
|
|
+{
|
|
+ /// Index of the peer device that needs traffic
|
|
+ u8_l sta_idx;
|
|
+ /// Number of packets that need to be sent (if 0, all buffered traffic shall be sent)
|
|
+ u8_l pkt_cnt;
|
|
+ /// Flag indicating if the traffic request concerns U-APSD queues or not
|
|
+ bool_l uapsd;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_PS_OPTIONS_REQ message.
|
|
+struct mm_set_ps_options_req
|
|
+{
|
|
+ /// VIF Index
|
|
+ u8_l vif_index;
|
|
+ /// Listen interval (0 if wake up shall be based on DTIM period)
|
|
+ u16_l listen_interval;
|
|
+ /// Flag indicating if we shall listen the BC/MC traffic or not
|
|
+ bool_l dont_listen_bc_mc;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CSA_COUNTER_IND message
|
|
+struct mm_csa_counter_ind
|
|
+{
|
|
+ /// Index of the VIF
|
|
+ u8_l vif_index;
|
|
+ /// Updated CSA counter value
|
|
+ u8_l csa_count;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CHANNEL_SURVEY_IND message
|
|
+struct mm_channel_survey_ind
|
|
+{
|
|
+ /// Frequency of the channel
|
|
+ u16_l freq;
|
|
+ /// Noise in dbm
|
|
+ s8_l noise_dbm;
|
|
+ /// Amount of time spent of the channel (in ms)
|
|
+ u32_l chan_time_ms;
|
|
+ /// Amount of time the primary channel was sensed busy
|
|
+ u32_l chan_time_busy_ms;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_BFMER_ENABLE_REQ message.
|
|
+struct mm_bfmer_enable_req
|
|
+{
|
|
+ /**
|
|
+ * Address of Beamforming Report in host memory
|
|
+ * (Valid only if vht_su_bfmee is true)
|
|
+ */
|
|
+ u32_l host_bfr_addr;
|
|
+ /// AID
|
|
+ u16_l aid;
|
|
+ /// Station Index
|
|
+ u8_l sta_idx;
|
|
+ /// Maximum number of spatial streams the station can receive
|
|
+ u8_l rx_nss;
|
|
+ /**
|
|
+ * Indicate if peer STA is MU Beamformee (VHT) capable
|
|
+ * (Valid only if vht_su_bfmee is true)
|
|
+ */
|
|
+ bool_l vht_mu_bfmee;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_REQ message.
|
|
+struct mm_set_p2p_noa_req
|
|
+{
|
|
+ /// VIF Index
|
|
+ u8_l vif_index;
|
|
+ /// Allocated NOA Instance Number - Valid only if count = 0
|
|
+ u8_l noa_inst_nb;
|
|
+ /// Count
|
|
+ u8_l count;
|
|
+ /// Indicate if NoA can be paused for traffic reason
|
|
+ bool_l dyn_noa;
|
|
+ /// Duration (in us)
|
|
+ u32_l duration_us;
|
|
+ /// Interval (in us)
|
|
+ u32_l interval_us;
|
|
+ /// Start Time offset from next TBTT (in us)
|
|
+ u32_l start_offset;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_REQ message.
|
|
+struct mm_set_p2p_oppps_req
|
|
+{
|
|
+ /// VIF Index
|
|
+ u8_l vif_index;
|
|
+ /// CTWindow
|
|
+ u8_l ctwindow;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_CFM message.
|
|
+struct mm_set_p2p_noa_cfm
|
|
+{
|
|
+ /// Request status
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_CFM message.
|
|
+struct mm_set_p2p_oppps_cfm
|
|
+{
|
|
+ /// Request status
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_P2P_NOA_UPD_IND message.
|
|
+struct mm_p2p_noa_upd_ind
|
|
+{
|
|
+ /// VIF Index
|
|
+ u8_l vif_index;
|
|
+ /// NOA Instance Number
|
|
+ u8_l noa_inst_nb;
|
|
+ /// NoA Type
|
|
+ u8_l noa_type;
|
|
+ /// Count
|
|
+ u8_l count;
|
|
+ /// Duration (in us)
|
|
+ u32_l duration_us;
|
|
+ /// Interval (in us)
|
|
+ u32_l interval_us;
|
|
+ /// Start Time
|
|
+ u32_l start_time;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CFG_RSSI_REQ message
|
|
+struct mm_cfg_rssi_req
|
|
+{
|
|
+ /// Index of the VIF
|
|
+ u8_l vif_index;
|
|
+ /// RSSI threshold
|
|
+ s8_l rssi_thold;
|
|
+ /// RSSI hysteresis
|
|
+ u8_l rssi_hyst;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_RSSI_STATUS_IND message
|
|
+struct mm_rssi_status_ind
|
|
+{
|
|
+ /// Index of the VIF
|
|
+ u8_l vif_index;
|
|
+ /// Status of the RSSI
|
|
+ bool_l rssi_status;
|
|
+ /// Current RSSI
|
|
+ s8_l rssi;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CSA_FINISH_IND message
|
|
+struct mm_csa_finish_ind
|
|
+{
|
|
+ /// Index of the VIF
|
|
+ u8_l vif_index;
|
|
+ /// Status of the operation
|
|
+ u8_l status;
|
|
+ /// New channel ctx index
|
|
+ u8_l chan_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_CSA_TRAFFIC_IND message
|
|
+struct mm_csa_traffic_ind
|
|
+{
|
|
+ /// Index of the VIF
|
|
+ u8_l vif_index;
|
|
+ /// Is tx traffic enable or disable
|
|
+ bool_l enable;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref MM_MU_GROUP_UPDATE_REQ message.
|
|
+/// Size allocated for the structure depends of the number of group
|
|
+struct mm_mu_group_update_req
|
|
+{
|
|
+ /// Station index
|
|
+ u8_l sta_idx;
|
|
+ /// Number of groups the STA belongs to
|
|
+ u8_l group_cnt;
|
|
+ /// Group information
|
|
+ struct
|
|
+ {
|
|
+ /// Group Id
|
|
+ u8_l group_id;
|
|
+ /// User position
|
|
+ u8_l user_pos;
|
|
+ } groups[0];
|
|
+};
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/////////// For Scan messages
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+enum scan_msg_tag
|
|
+{
|
|
+ /// Scanning start Request.
|
|
+ SCAN_START_REQ = LMAC_FIRST_MSG(TASK_SCAN),
|
|
+ /// Scanning start Confirmation.
|
|
+ SCAN_START_CFM,
|
|
+ /// End of scanning indication.
|
|
+ SCAN_DONE_IND,
|
|
+ /// Cancel scan request
|
|
+ SCAN_CANCEL_REQ,
|
|
+ /// Cancel scan confirmation
|
|
+ SCAN_CANCEL_CFM,
|
|
+
|
|
+ /// MAX number of messages
|
|
+ SCAN_MAX,
|
|
+};
|
|
+
|
|
+/// Maximum number of SSIDs in a scan request
|
|
+#define SCAN_SSID_MAX 2
|
|
+
|
|
+/// Maximum number of 2.4GHz channels
|
|
+#define SCAN_CHANNEL_2G4 14
|
|
+
|
|
+/// Maximum number of 5GHz channels
|
|
+#define SCAN_CHANNEL_5G 28
|
|
+
|
|
+/// Maximum number of channels in a scan request
|
|
+#define SCAN_CHANNEL_MAX (SCAN_CHANNEL_2G4 + SCAN_CHANNEL_5G)
|
|
+
|
|
+/// Flag bits
|
|
+#define SCAN_PASSIVE_BIT BIT(0)
|
|
+#define SCAN_DISABLED_BIT BIT(1)
|
|
+
|
|
+/// Maximum number of PHY bands supported
|
|
+#define SCAN_BAND_MAX 2
|
|
+
|
|
+/// Definition of a channel to be scanned
|
|
+struct scan_chan_tag
|
|
+{
|
|
+ /// Frequency of the channel
|
|
+ u16_l freq;
|
|
+ /// RF band (0: 2.4GHz, 1: 5GHz)
|
|
+ u8_l band;
|
|
+ /// Bit field containing additional information about the channel
|
|
+ u8_l flags;
|
|
+ /// Max tx_power for this channel (dBm)
|
|
+ s8_l tx_power;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref SCAN_START_REQ message
|
|
+struct scan_start_req
|
|
+{
|
|
+ /// List of channel to be scanned
|
|
+ struct scan_chan_tag chan[SCAN_CHANNEL_MAX];
|
|
+ /// List of SSIDs to be scanned
|
|
+ struct mac_ssid ssid[SCAN_SSID_MAX];
|
|
+ /// BSSID to be scanned
|
|
+ struct mac_addr bssid;
|
|
+ /// Pointer (in host memory) to the additional IEs that need to be added to the ProbeReq
|
|
+ /// (following the SSID element)
|
|
+ u32_l add_ies;
|
|
+ /// Length of the additional IEs
|
|
+ u16_l add_ie_len;
|
|
+ /// Index of the VIF that is scanning
|
|
+ u8_l vif_idx;
|
|
+ /// Number of channels to scan
|
|
+ u8_l chan_cnt;
|
|
+ /// Number of SSIDs to scan for
|
|
+ u8_l ssid_cnt;
|
|
+ /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
|
|
+ bool no_cck;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
|
|
+struct scan_start_cfm
|
|
+{
|
|
+ /// Status of the request
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref SCAN_CANCEL_REQ message
|
|
+struct scan_cancel_req
|
|
+{
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
|
|
+struct scan_cancel_cfm
|
|
+{
|
|
+ /// Status of the request
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/////////// For Scanu messages
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/// Messages that are logically related to the task.
|
|
+enum
|
|
+{
|
|
+ /// Scan request from host.
|
|
+ SCANU_START_REQ = LMAC_FIRST_MSG(TASK_SCANU),
|
|
+ /// Scanning start Confirmation.
|
|
+ SCANU_START_CFM,
|
|
+ /// Join request
|
|
+ SCANU_JOIN_REQ,
|
|
+ /// Join confirmation.
|
|
+ SCANU_JOIN_CFM,
|
|
+ /// Scan result indication.
|
|
+ SCANU_RESULT_IND,
|
|
+ /// Fast scan request from any other module.
|
|
+ SCANU_FAST_REQ,
|
|
+ /// Confirmation of fast scan request.
|
|
+ SCANU_FAST_CFM,
|
|
+
|
|
+ /// MAX number of messages
|
|
+ SCANU_MAX,
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref SCANU_START_REQ message
|
|
+struct scanu_start_req
|
|
+{
|
|
+ /// List of channel to be scanned
|
|
+ struct scan_chan_tag chan[SCAN_CHANNEL_MAX];
|
|
+ /// List of SSIDs to be scanned
|
|
+ struct mac_ssid ssid[SCAN_SSID_MAX];
|
|
+ /// BSSID to be scanned (or WILDCARD BSSID if no BSSID is searched in particular)
|
|
+ struct mac_addr bssid;
|
|
+ /// Address (in host memory) of the additional IEs that need to be added to the ProbeReq
|
|
+ /// (following the SSID element)
|
|
+ u32_l add_ies;
|
|
+ /// Length of the additional IEs
|
|
+ u16_l add_ie_len;
|
|
+ /// Index of the VIF that is scanning
|
|
+ u8_l vif_idx;
|
|
+ /// Number of channels to scan
|
|
+ u8_l chan_cnt;
|
|
+ /// Number of SSIDs to scan for
|
|
+ u8_l ssid_cnt;
|
|
+ /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
|
|
+ bool no_cck;
|
|
+ ///buf for storing additional IEs
|
|
+ u8_l add_ies_buf[];
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref SCANU_START_CFM message
|
|
+struct scanu_start_cfm
|
|
+{
|
|
+ /// Status of the request
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+/// Parameters of the @SCANU_RESULT_IND message
|
|
+struct scanu_result_ind
|
|
+{
|
|
+ /// Length of the frame
|
|
+ u16_l length;
|
|
+ /// Frame control field of the frame.
|
|
+ u16_l framectrl;
|
|
+ /// Center frequency on which we received the packet
|
|
+ u16_l center_freq;
|
|
+ /// PHY band
|
|
+ u8_l band;
|
|
+ /// Index of the station that sent the frame. 0xFF if unknown.
|
|
+ u8_l sta_idx;
|
|
+ /// Index of the VIF that received the frame. 0xFF if unknown.
|
|
+ u8_l inst_nbr;
|
|
+ /// RSSI of the received frame.
|
|
+ s8_l rssi;
|
|
+ /// Frame payload.
|
|
+ u32_l payload[];
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the message.
|
|
+struct scanu_fast_req
|
|
+{
|
|
+ /// The SSID to scan in the channel.
|
|
+ struct mac_ssid ssid;
|
|
+ /// BSSID.
|
|
+ struct mac_addr bssid;
|
|
+ /// Probe delay.
|
|
+ u16_l probe_delay;
|
|
+ /// Minimum channel time.
|
|
+ u16_l minch_time;
|
|
+ /// Maximum channel time.
|
|
+ u16_l maxch_time;
|
|
+ /// The channel number to scan.
|
|
+ u16_l ch_nbr;
|
|
+};
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/////////// For ME messages
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/// Messages that are logically related to the task.
|
|
+enum
|
|
+{
|
|
+ /// Configuration request from host.
|
|
+ ME_CONFIG_REQ = LMAC_FIRST_MSG(TASK_ME),
|
|
+ /// Configuration confirmation.
|
|
+ ME_CONFIG_CFM,
|
|
+ /// Configuration request from host.
|
|
+ ME_CHAN_CONFIG_REQ,
|
|
+ /// Configuration confirmation.
|
|
+ ME_CHAN_CONFIG_CFM,
|
|
+ /// Set control port state for a station.
|
|
+ ME_SET_CONTROL_PORT_REQ,
|
|
+ /// Control port setting confirmation.
|
|
+ ME_SET_CONTROL_PORT_CFM,
|
|
+ /// TKIP MIC failure indication.
|
|
+ ME_TKIP_MIC_FAILURE_IND,
|
|
+ /// Add a station to the FW (AP mode)
|
|
+ ME_STA_ADD_REQ,
|
|
+ /// Confirmation of the STA addition
|
|
+ ME_STA_ADD_CFM,
|
|
+ /// Delete a station from the FW (AP mode)
|
|
+ ME_STA_DEL_REQ,
|
|
+ /// Confirmation of the STA deletion
|
|
+ ME_STA_DEL_CFM,
|
|
+ /// Indication of a TX RA/TID queue credit update
|
|
+ ME_TX_CREDITS_UPDATE_IND,
|
|
+ /// Request indicating to the FW that there is traffic buffered on host
|
|
+ ME_TRAFFIC_IND_REQ,
|
|
+ /// Confirmation that the @ref ME_TRAFFIC_IND_REQ has been executed
|
|
+ ME_TRAFFIC_IND_CFM,
|
|
+ /// Request of RC statistics to a station
|
|
+ ME_RC_STATS_REQ,
|
|
+ /// RC statistics confirmation
|
|
+ ME_RC_STATS_CFM,
|
|
+ /// RC fixed rate request
|
|
+ ME_RC_SET_RATE_REQ,
|
|
+ /// MAX number of messages
|
|
+ ME_MAX,
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_START_REQ message
|
|
+struct me_config_req
|
|
+{
|
|
+ /// HT Capabilities
|
|
+ struct mac_htcapability ht_cap;
|
|
+ /// VHT Capabilities
|
|
+ struct mac_vhtcapability vht_cap;
|
|
+ /// Lifetime of packets sent under a BlockAck agreement (expressed in TUs)
|
|
+ u16_l tx_lft;
|
|
+ /// Boolean indicating if HT is supported or not
|
|
+ bool_l ht_supp;
|
|
+ /// Boolean indicating if VHT is supported or not
|
|
+ bool_l vht_supp;
|
|
+ /// Boolean indicating if PS mode shall be enabled or not
|
|
+ bool_l ps_on;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_CHAN_CONFIG_REQ message
|
|
+struct me_chan_config_req
|
|
+{
|
|
+ /// List of 2.4GHz supported channels
|
|
+ struct scan_chan_tag chan2G4[SCAN_CHANNEL_2G4];
|
|
+ /// List of 5GHz supported channels
|
|
+ struct scan_chan_tag chan5G[SCAN_CHANNEL_5G];
|
|
+ /// Number of 2.4GHz channels in the list
|
|
+ u8_l chan2G4_cnt;
|
|
+ /// Number of 5GHz channels in the list
|
|
+ u8_l chan5G_cnt;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_SET_CONTROL_PORT_REQ message
|
|
+struct me_set_control_port_req
|
|
+{
|
|
+ /// Index of the station for which the control port is opened
|
|
+ u8_l sta_idx;
|
|
+ /// Control port state
|
|
+ bool_l control_port_open;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_TKIP_MIC_FAILURE_IND message
|
|
+struct me_tkip_mic_failure_ind
|
|
+{
|
|
+ /// Address of the sending STA
|
|
+ struct mac_addr addr;
|
|
+ /// TSC value
|
|
+ u64_l tsc;
|
|
+ /// Boolean indicating if the packet was a group or unicast one (true if group)
|
|
+ bool_l ga;
|
|
+ /// Key Id
|
|
+ u8_l keyid;
|
|
+ /// VIF index
|
|
+ u8_l vif_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_STA_ADD_REQ message
|
|
+struct me_sta_add_req
|
|
+{
|
|
+ /// MAC address of the station to be added
|
|
+ struct mac_addr mac_addr;
|
|
+ /// Supported legacy rates
|
|
+ struct mac_rateset rate_set;
|
|
+ /// HT Capabilities
|
|
+ struct mac_htcapability ht_cap;
|
|
+ /// VHT Capabilities
|
|
+ struct mac_vhtcapability vht_cap;
|
|
+ /// Flags giving additional information about the station
|
|
+ u32_l flags;
|
|
+ /// Association ID of the station
|
|
+ u16_l aid;
|
|
+ /// Bit field indicating which queues have U-APSD enabled
|
|
+ u8_l uapsd_queues;
|
|
+ /// Maximum size, in frames, of a APSD service period
|
|
+ u8_l max_sp_len;
|
|
+ /// Operation mode information (valid if bit @ref STA_OPMOD_NOTIF is
|
|
+ /// set in the flags)
|
|
+ u8_l opmode;
|
|
+ /// Index of the VIF the station is attached to
|
|
+ u8_l vif_idx;
|
|
+ /// Whether the the station is TDLS station
|
|
+ bool_l tdls_sta;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_STA_ADD_CFM message
|
|
+struct me_sta_add_cfm
|
|
+{
|
|
+ /// Station index
|
|
+ u8_l sta_idx;
|
|
+ /// Status of the station addition
|
|
+ u8_l status;
|
|
+ /// PM state of the station
|
|
+ u8_l pm_state;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_STA_DEL_REQ message.
|
|
+struct me_sta_del_req
|
|
+{
|
|
+ /// Index of the station to be deleted
|
|
+ u8_l sta_idx;
|
|
+ /// Whether the the station is TDLS station
|
|
+ bool_l tdls_sta;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_TX_CREDITS_UPDATE_IND message.
|
|
+struct me_tx_credits_update_ind
|
|
+{
|
|
+ /// Index of the station for which the credits are updated
|
|
+ u8_l sta_idx;
|
|
+ /// TID for which the credits are updated
|
|
+ u8_l tid;
|
|
+ /// Offset to be applied on the credit count
|
|
+ s8_l credits;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_TRAFFIC_IND_REQ message.
|
|
+struct me_traffic_ind_req
|
|
+{
|
|
+ /// Index of the station for which UAPSD traffic is available on host
|
|
+ u8_l sta_idx;
|
|
+ /// Flag indicating the availability of UAPSD packets for the given STA
|
|
+ u8_l tx_avail;
|
|
+ /// Indicate if traffic is on uapsd-enabled queues
|
|
+ bool_l uapsd;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_RC_STATS_REQ message.
|
|
+struct me_rc_stats_req
|
|
+{
|
|
+ /// Index of the station for which the RC statistics are requested
|
|
+ u8_l sta_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the structure of a retry chain step
|
|
+struct step
|
|
+{
|
|
+ /// Current calculated throughput
|
|
+ u32_l tp;
|
|
+ /// Index of the sample in the rate_stats table
|
|
+ u16_l idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the rate control statistics
|
|
+struct rc_rate_stats
|
|
+{
|
|
+ /// Number of attempts (per sampling interval)
|
|
+ u16_l attempts;
|
|
+ /// Number of success (per sampling interval)
|
|
+ u16_l success;
|
|
+ /// Estimated probability of success (EWMA)
|
|
+ u16_l probability;
|
|
+ /// Rate configuration of the sample
|
|
+ u16_l rate_config;
|
|
+ /// Number of times the sample has been skipped (per sampling interval)
|
|
+ u8_l sample_skipped;
|
|
+ /// Whether the old probability is available
|
|
+ bool_l old_prob_available;
|
|
+ /// Number of times the AMPDU has been retried with this rate
|
|
+ u8_l n_retry;
|
|
+ // Whether the rate can be used in the retry chain
|
|
+ bool_l rate_allowed;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_RC_STATS_CFM message.
|
|
+struct me_rc_stats_cfm
|
|
+{
|
|
+ /// Index of the station for which the RC statistics are provided
|
|
+ u8_l sta_idx;
|
|
+ /// Number of samples used in the RC algorithm
|
|
+ u16_l no_samples;
|
|
+ /// Number of MPDUs transmitted (per sampling interval)
|
|
+ u16_l ampdu_len;
|
|
+ /// Number of AMPDUs transmitted (per sampling interval)
|
|
+ u16_l ampdu_packets;
|
|
+ /// Average number of MPDUs in each AMPDU frame (EWMA)
|
|
+ u32_l avg_ampdu_len;
|
|
+ // Current step 0 of the retry chain
|
|
+ u8_l sw_retry_step;
|
|
+ /// Trial transmission period
|
|
+ u8_l sample_wait;
|
|
+ /// Retry chain steps
|
|
+ struct step retry[4];
|
|
+ /// RC statistics
|
|
+ struct rc_rate_stats rate_stats[10];
|
|
+ /// Throughput
|
|
+ u32_l tp[10];
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref ME_RC_SET_RATE_REQ message.
|
|
+struct me_rc_set_rate_req
|
|
+{
|
|
+ /// Index of the station for which the fixed rate is set
|
|
+ u8_l sta_idx;
|
|
+ /// Rate configuration to be set
|
|
+ u16_l fixed_rate_cfg;
|
|
+};
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/////////// For SM messages
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/// Message API of the SM task
|
|
+enum sm_msg_tag
|
|
+{
|
|
+ /// Request to connect to an AP
|
|
+ SM_CONNECT_REQ = LMAC_FIRST_MSG(TASK_SM),
|
|
+ /// Confirmation of connection
|
|
+ SM_CONNECT_CFM,
|
|
+ /// Indicates that the SM associated to the AP
|
|
+ SM_CONNECT_IND,
|
|
+ /// Request to disconnect
|
|
+ SM_DISCONNECT_REQ,
|
|
+ /// Confirmation of disconnection
|
|
+ SM_DISCONNECT_CFM,
|
|
+ /// Indicates that the SM disassociated the AP
|
|
+ SM_DISCONNECT_IND,
|
|
+ /// Timeout message for procedures requiring a response from peer
|
|
+ SM_RSP_TIMEOUT_IND,
|
|
+
|
|
+ /// MAX number of messages
|
|
+ SM_MAX,
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of @ref SM_CONNECT_REQ message.
|
|
+struct sm_connect_req
|
|
+{
|
|
+ /// Connection flags (see @ref sm_connect_flags)
|
|
+ u32_l flags;
|
|
+ /// Buffer containing the additional information elements to be put in the
|
|
+ /// association request
|
|
+ u32_l ie_buf[64];
|
|
+ /// BSSID to connect to (if not specified, set this field to WILDCARD BSSID)
|
|
+ struct mac_addr bssid;
|
|
+ /// Control port Ethertype
|
|
+ u16_l ctrl_port_ethertype;
|
|
+ /// Length of the association request IEs
|
|
+ u16_l ie_len;
|
|
+ /// Listen interval to be used for this connection
|
|
+ u16_l listen_interval;
|
|
+ /// SSID to connect to
|
|
+ struct mac_ssid ssid;
|
|
+ /// Channel on which we have to connect (if not specified, set -1 in the chan.freq field)
|
|
+ struct scan_chan_tag chan;
|
|
+ /// Flag indicating if the we have to wait for the BC/MC traffic after beacon or not
|
|
+ bool_l dont_wait_bcmc;
|
|
+ /// Authentication type
|
|
+ u8_l auth_type;
|
|
+ /// UAPSD queues (bit0: VO, bit1: VI, bit2: BE, bit3: BK)
|
|
+ u8_l uapsd_queues;
|
|
+ /// VIF index
|
|
+ u8_l vif_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref SM_CONNECT_CFM message.
|
|
+struct sm_connect_cfm
|
|
+{
|
|
+ /// Status. If 0, it means that the connection procedure will be performed and that
|
|
+ /// a subsequent @ref SM_CONNECT_IND message will be forwarded once the procedure is
|
|
+ /// completed
|
|
+ u8_l status;
|
|
+};
|
|
+
|
|
+#define SM_ASSOC_IE_LEN 800
|
|
+/// Structure containing the parameters of the @ref SM_CONNECT_IND message.
|
|
+struct sm_connect_ind
|
|
+{
|
|
+ /// Status code of the connection procedure
|
|
+ u16_l status_code;
|
|
+ /// BSSID
|
|
+ struct mac_addr bssid;
|
|
+ /// Flag indicating if the indication refers to an internal roaming or from a host request
|
|
+ bool_l roamed;
|
|
+ /// Index of the VIF for which the association process is complete
|
|
+ u8_l vif_idx;
|
|
+ /// Index of the STA entry allocated for the AP
|
|
+ u8_l ap_idx;
|
|
+ /// Index of the LMAC channel context the connection is attached to
|
|
+ u8_l ch_idx;
|
|
+ /// Flag indicating if the AP is supporting QoS
|
|
+ bool_l qos;
|
|
+ /// ACM bits set in the AP WMM parameter element
|
|
+ u8_l acm;
|
|
+ /// Length of the AssocReq IEs
|
|
+ u16_l assoc_req_ie_len;
|
|
+ /// Length of the AssocRsp IEs
|
|
+ u16_l assoc_rsp_ie_len;
|
|
+ /// IE buffer
|
|
+ u32_l assoc_ie_buf[SM_ASSOC_IE_LEN/4];
|
|
+
|
|
+ u16_l aid;
|
|
+ u8_l band;
|
|
+ u16_l center_freq;
|
|
+ u8_l width;
|
|
+ u32_l center_freq1;
|
|
+ u32_l center_freq2;
|
|
+
|
|
+ /// EDCA parameters
|
|
+ u32_l ac_param[AC_MAX];
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref SM_DISCONNECT_REQ message.
|
|
+struct sm_disconnect_req
|
|
+{
|
|
+ /// Reason of the deauthentication.
|
|
+ u16_l reason_code;
|
|
+ /// Index of the VIF.
|
|
+ u8_l vif_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of SM_ASSOCIATION_IND the message
|
|
+struct sm_association_ind
|
|
+{
|
|
+ // MAC ADDR of the STA
|
|
+ struct mac_addr me_mac_addr;
|
|
+};
|
|
+
|
|
+
|
|
+/// Structure containing the parameters of the @ref SM_DISCONNECT_IND message.
|
|
+struct sm_disconnect_ind
|
|
+{
|
|
+ /// Reason of the disconnection.
|
|
+ u16_l reason_code;
|
|
+ /// Index of the VIF.
|
|
+ u8_l vif_idx;
|
|
+ /// FT over DS is ongoing
|
|
+ bool_l ft_over_ds;
|
|
+};
|
|
+
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/////////// For SM messages
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/// Message API of the APM task
|
|
+enum apm_msg_tag
|
|
+{
|
|
+ /// Request to start the AP.
|
|
+ APM_START_REQ = LMAC_FIRST_MSG(TASK_APM),
|
|
+ /// Confirmation of the AP start.
|
|
+ APM_START_CFM,
|
|
+ /// Request to stop the AP.
|
|
+ APM_STOP_REQ,
|
|
+ /// Confirmation of the AP stop.
|
|
+ APM_STOP_CFM,
|
|
+ /// Request to start CAC
|
|
+ APM_START_CAC_REQ,
|
|
+ /// Confirmation of the CAC start
|
|
+ APM_START_CAC_CFM,
|
|
+ /// Request to stop CAC
|
|
+ APM_STOP_CAC_REQ,
|
|
+ /// Confirmation of the CAC stop
|
|
+ APM_STOP_CAC_CFM,
|
|
+
|
|
+ /// MAX number of messages
|
|
+ APM_MAX,
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref APM_START_REQ message.
|
|
+struct apm_start_req
|
|
+{
|
|
+ /// Basic rate set
|
|
+ struct mac_rateset basic_rates;
|
|
+ /// Control channel on which we have to enable the AP
|
|
+ struct scan_chan_tag chan;
|
|
+ /// Center frequency of the first segment
|
|
+ u32_l center_freq1;
|
|
+ /// Center frequency of the second segment (only in 80+80 configuration)
|
|
+ u32_l center_freq2;
|
|
+ /// Width of channel
|
|
+ u8_l ch_width;
|
|
+ /// Address, in host memory, to the beacon template
|
|
+ u32_l bcn_addr;
|
|
+ /// Length of the beacon template
|
|
+ u16_l bcn_len;
|
|
+ /// Offset of the TIM IE in the beacon
|
|
+ u16_l tim_oft;
|
|
+ /// Beacon interval
|
|
+ u16_l bcn_int;
|
|
+ /// Flags
|
|
+ u32_l flags;
|
|
+ /// Control port Ethertype
|
|
+ u16_l ctrl_port_ethertype;
|
|
+ /// Length of the TIM IE
|
|
+ u8_l tim_len;
|
|
+ /// Index of the VIF for which the AP is started
|
|
+ u8_l vif_idx;
|
|
+ ///
|
|
+ u8_l bcn_buf[];
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref APM_START_CFM message.
|
|
+struct apm_start_cfm
|
|
+{
|
|
+ /// Status of the AP starting procedure
|
|
+ u8_l status;
|
|
+ /// Index of the VIF for which the AP is started
|
|
+ u8_l vif_idx;
|
|
+ /// Index of the channel context attached to the VIF
|
|
+ u8_l ch_idx;
|
|
+ /// Index of the STA used for BC/MC traffic
|
|
+ u8_l bcmc_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref APM_STOP_REQ message.
|
|
+struct apm_stop_req
|
|
+{
|
|
+ /// Index of the VIF for which the AP has to be stopped
|
|
+ u8_l vif_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref APM_START_CAC_REQ message.
|
|
+struct apm_start_cac_req
|
|
+{
|
|
+ /// Control channel on which we have to start the CAC
|
|
+ struct scan_chan_tag chan;
|
|
+ /// Center frequency of the first segment
|
|
+ u32_l center_freq1;
|
|
+ /// Center frequency of the second segment (only in 80+80 configuration)
|
|
+ u32_l center_freq2;
|
|
+ /// Width of channel
|
|
+ u8_l ch_width;
|
|
+ /// Index of the VIF for which the CAC is started
|
|
+ u8_l vif_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref APM_START_CAC_CFM message.
|
|
+struct apm_start_cac_cfm
|
|
+{
|
|
+ /// Status of the CAC starting procedure
|
|
+ u8_l status;
|
|
+ /// Index of the channel context attached to the VIF for CAC
|
|
+ u8_l ch_idx;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref APM_STOP_CAC_REQ message.
|
|
+struct apm_stop_cac_req
|
|
+{
|
|
+ /// Index of the VIF for which the CAC has to be stopped
|
|
+ u8_l vif_idx;
|
|
+};
|
|
+
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+/////////// For Debug messages
|
|
+///////////////////////////////////////////////////////////////////////////////
|
|
+
|
|
+/// Messages related to Debug Task
|
|
+enum dbg_msg_tag
|
|
+{
|
|
+ /// Memory read request
|
|
+ DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG),
|
|
+ /// Memory read confirm
|
|
+ DBG_MEM_READ_CFM,
|
|
+ /// Memory write request
|
|
+ DBG_MEM_WRITE_REQ,
|
|
+ /// Memory write confirm
|
|
+ DBG_MEM_WRITE_CFM,
|
|
+ /// Module filter request
|
|
+ DBG_SET_MOD_FILTER_REQ,
|
|
+ /// Module filter confirm
|
|
+ DBG_SET_MOD_FILTER_CFM,
|
|
+ /// Severity filter request
|
|
+ DBG_SET_SEV_FILTER_REQ,
|
|
+ /// Severity filter confirm
|
|
+ DBG_SET_SEV_FILTER_CFM,
|
|
+ /// LMAC/MAC HW fatal error indication
|
|
+ DBG_ERROR_IND,
|
|
+ /// Request to get system statistics
|
|
+ DBG_GET_SYS_STAT_REQ,
|
|
+ /// COnfirmation of system statistics
|
|
+ DBG_GET_SYS_STAT_CFM,
|
|
+ /// Max number of Debug messages
|
|
+ DBG_MAX,
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message.
|
|
+struct dbg_mem_read_req
|
|
+{
|
|
+ u32_l memaddr;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message.
|
|
+struct dbg_mem_read_cfm
|
|
+{
|
|
+ u32_l memaddr;
|
|
+ u32_l memdata;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message.
|
|
+struct dbg_mem_write_req
|
|
+{
|
|
+ u32_l memaddr;
|
|
+ u32_l memdata;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message.
|
|
+struct dbg_mem_write_cfm
|
|
+{
|
|
+ u32_l memaddr;
|
|
+ u32_l memdata;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref DBG_SET_MOD_FILTER_REQ message.
|
|
+struct dbg_set_mod_filter_req
|
|
+{
|
|
+ /// Bit field indicating for each module if the traces are enabled or not
|
|
+ u32_l mod_filter;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref DBG_SEV_MOD_FILTER_REQ message.
|
|
+struct dbg_set_sev_filter_req
|
|
+{
|
|
+ /// Bit field indicating the severity threshold for the traces
|
|
+ u32_l sev_filter;
|
|
+};
|
|
+
|
|
+/// Structure containing the parameters of the @ref DBG_GET_SYS_STAT_CFM message.
|
|
+struct dbg_get_sys_stat_cfm
|
|
+{
|
|
+ /// Time spent in CPU sleep since last reset of the system statistics
|
|
+ u32_l cpu_sleep_time;
|
|
+ /// Time spent in DOZE since last reset of the system statistics
|
|
+ u32_l doze_time;
|
|
+ /// Total time spent since last reset of the system statistics
|
|
+ u32_l stats_time;
|
|
+};
|
|
+
|
|
+#endif // LMAC_MSG_H_
|
|
diff -Naur /dev/null_types.h b/drivers/net/wireless/hflps170/lmac_types.h
|
|
--- /dev/null_types.h 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/lmac_types.h 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,56 @@
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ *
|
|
+ * @file co_types.h
|
|
+ *
|
|
+ * @brief This file replaces the need to include stdint or stdbool typical headers,
|
|
+ * which may not be available in all toolchains, and adds new types
|
|
+ *
|
|
+ * Copyright (C) BouffaloLab 2017-2018
|
|
+ *
|
|
+ * $Rev: $
|
|
+ *
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+#ifndef _LMAC_INT_H_
|
|
+#define _LMAC_INT_H_
|
|
+
|
|
+
|
|
+/**
|
|
+ ****************************************************************************************
|
|
+ * @addtogroup CO_INT
|
|
+ * @ingroup COMMON
|
|
+ * @brief Common integer standard types (removes use of stdint)
|
|
+ *
|
|
+ * @{
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+
|
|
+/*
|
|
+ * DEFINES
|
|
+ ****************************************************************************************
|
|
+ */
|
|
+
|
|
+
|
|
+#include <linux/version.h>
|
|
+#include <linux/types.h>
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
|
+#include <linux/bits.h>
|
|
+#else
|
|
+#include <linux/bitops.h>
|
|
+#endif
|
|
+
|
|
+
|
|
+typedef uint8_t u8_l;
|
|
+typedef int8_t s8_l;
|
|
+typedef bool bool_l;
|
|
+typedef uint16_t u16_l;
|
|
+typedef int16_t s16_l;
|
|
+typedef uint32_t u32_l;
|
|
+typedef int32_t s32_l;
|
|
+typedef uint64_t u64_l;
|
|
+
|
|
+/// @} CO_INT
|
|
+#endif // _LMAC_INT_H_
|
|
diff -Naur /dev/null.sh b/drivers/net/wireless/hflps170/mklink.sh
|
|
--- /dev/null.sh 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/mklink.sh 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,51 @@
|
|
+#!/bin/bash
|
|
+#
|
|
+# Generate link for shared .c files in softmac and fullmac directories
|
|
+# Note:
|
|
+# links are not mandatory and we could easily update softmac (fullmac)
|
|
+# Makefile to compile shared file directly but this would imply to recompile
|
|
+# shared code eveytime when both driver are enabled
|
|
+#
|
|
+
|
|
+set -e
|
|
+
|
|
+dir_list=
|
|
+
|
|
+if [ $1 ] && [ $1 = "clean" ]
|
|
+then
|
|
+ action="clean"
|
|
+else
|
|
+ action="create"
|
|
+fi
|
|
+
|
|
+if [ -d softmac ]
|
|
+then
|
|
+ dir_list="softmac"
|
|
+fi
|
|
+
|
|
+if [ -d fullmac ]
|
|
+then
|
|
+ dir_list="$dir_list fullmac"
|
|
+fi
|
|
+
|
|
+for f in $(find . -maxdepth 1 -name "*.c")
|
|
+do
|
|
+ for d in $dir_list
|
|
+ do
|
|
+ if [ $action = "clean" ]
|
|
+ then
|
|
+ # only delete link
|
|
+ if [ -L $d/${f#./} ]
|
|
+ then
|
|
+ rm -f $d/${f#./}
|
|
+ fi
|
|
+ else
|
|
+ # only create link if file doesn't exist
|
|
+ # (whether it is a link or not)
|
|
+ if [ ! -e $d/${f#./} ]
|
|
+ then
|
|
+ ln -sf .$f $d/${f#./}
|
|
+ fi
|
|
+ fi
|
|
+ done
|
|
+done
|
|
diff -Naur /dev/null.sh b/drivers/net/wireless/hflps170/mkvers.sh
|
|
--- /dev/null.sh 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/net/wireless/hflps170/mkvers.sh 2020-12-23 11:53:40.000000000 +0200
|
|
@@ -0,0 +1,72 @@
|
|
+#!/bin/bash
|
|
+#
|
|
+# Outputs svn revision of directory $VERDIR into $TARGET if it changes $TARGET
|
|
+# example output omitting enclosing quotes:
|
|
+#
|
|
+# '#define BL_VERS_REV "svn1676M"'
|
|
+# '#define BL_VERS_MOD "vX.X.X.X"'
|
|
+# '#define BL_VERS_OTH "build: username date hour"'
|
|
+
|
|
+set -e
|
|
+
|
|
+VERDIR=$(dirname $(readlink -f $0))
|
|
+TARGET=$1
|
|
+
|
|
+DATE_FORMAT="%b %d %Y %T"
|
|
+BL_VERS_MOD=$(grep BL_VERS_NUM $VERDIR/Makefile | cut -f2 -d=)
|
|
+
|
|
+tmpout=$TARGET.tmp
|
|
+cd $VERDIR
|
|
+
|
|
+if [ -e ".svn" ]
|
|
+then
|
|
+ svnrev=$(svnversion -c | sed 's/.*://')
|
|
+elif (git status -uno > /dev/null)
|
|
+then
|
|
+ # maybe git-svn
|
|
+ idx=0
|
|
+ while [ "$svnrev" = "" ]
|
|
+ do
|
|
+ # loop on all commit to find the first one that match a svn revision
|
|
+ # svnrev=$(git svn find-rev HEAD~$idx)
|
|
+ # we not use svn,we use git
|
|
+ svnrev=$(git rev-parse HEAD~$idx)
|
|
+ if [ $? -ne 0 ]
|
|
+ then
|
|
+ svnrev=": Unknown Revision"
|
|
+ elif [ "$svnrev" = "" ]
|
|
+ then
|
|
+ idx=$((idx + 1))
|
|
+ elif [ $idx -gt 0 ] || (git status -uno --porcelain | grep -q '^ M')
|
|
+ then
|
|
+ # If this is not the HEAD, or working copy is not clean then we're
|
|
+ # not at a commited svn revision so add 'M'
|
|
+ svnrev=$svnrev"M"
|
|
+ fi
|
|
+ done
|
|
+
|
|
+ # append git info (sha1 and branch name)
|
|
+ git_sha1=$(git rev-parse --short HEAD)
|
|
+ git_branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo "detached")
|
|
+ svnrev="$svnrev ($git_sha1/$git_branch)"
|
|
+else
|
|
+ svnrev=": Unknown Revision"
|
|
+fi
|
|
+
|
|
+date=$(LC_TIME=C date +"$DATE_FORMAT")
|
|
+
|
|
+BL_VERS_REV="git$svnrev"
|
|
+# "lmac vX.X.X.X - build:"
|
|
+banner="bl v$BL_VERS_MOD - build: $(whoami) $date - $BL_VERS_REV"
|
|
+
|
|
+define() { echo "#define $1 \"$2\""; }
|
|
+{
|
|
+ define "BL_VERS_REV" "$BL_VERS_REV"
|
|
+ define "BL_VERS_MOD" "$BL_VERS_MOD"
|
|
+ define "BL_VERS_BANNER" "$banner"
|
|
+} > $tmpout
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+cmp -s $TARGET $tmpout && rm -f $tmpout || mv $tmpout $TARGET
|