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 +#include + +#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 +#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 +#include + +#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 + +#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 +#include +#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 + +#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 +#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 + +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 +#include +#include +#include +#include +#include + +#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:]* " + "[DBG:]\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; iprivate_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 + +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 + +#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 +#include + +/* 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 +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) +#include +#else +#include +#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 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 +#include +#include +#include + +#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<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 + +/* 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 + +#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 +#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 +#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 +#include + +#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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#include + +#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 +#include +#include + +/** + * 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 +#include +#include +#include + +#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 +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#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 +#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 +#else +#include +#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; ///> 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 +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include +#else +#include +#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