Upload
Source: https://github.com/cringeops/bes2600 Source: https://github.com/cringeops/bes2600/pull/14 Source: https://github.com/cringeops/bes2600/pull/17 Source: https://github.com/cringeops/bes2600/pull/20
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
config BES2600
|
||||
tristate "BES2600 WLAN support"
|
||||
select MAC80211
|
||||
select CFG80211
|
||||
select NL80211_TESTMODE
|
||||
default m
|
||||
help
|
||||
This is an experimental driver for the bes2600 chip-set.
|
||||
Enabling this option enables the generic driver without
|
||||
any platform support.
|
||||
|
||||
if BES2600
|
||||
|
||||
config BES2600_USE_GPIO_IRQ
|
||||
bool "Use GPIO interrupt"
|
||||
default n
|
||||
help
|
||||
Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ.
|
||||
If unsure, say N.
|
||||
|
||||
config BES2600_5GHZ_SUPPORT
|
||||
bool "5GHz band support"
|
||||
default y
|
||||
help
|
||||
Say Y if your device supports 5GHz band.
|
||||
If unsure, say N.
|
||||
|
||||
config BES2600_WAPI_SUPPORT
|
||||
bool "WAPI support"
|
||||
default n
|
||||
help
|
||||
Say Y if your compat-wireless support WAPI.
|
||||
If unsure, say N.
|
||||
|
||||
config BES2600_USE_STE_EXTENSIONS
|
||||
bool "STE extensions"
|
||||
default n
|
||||
help
|
||||
Say Y if you want to include STE extensions.
|
||||
If unsure, say N.
|
||||
|
||||
config BES2600_DISABLE_BEACON_HINTS
|
||||
bool "Disable 11d beacon hints"
|
||||
default n
|
||||
help
|
||||
Say Y if you want to disable 11d beacon hints.
|
||||
If unsure, say N.
|
||||
|
||||
config BES2600_TESTMODE
|
||||
bool "bes2600 testmode support"
|
||||
select NL80211_TESTMODE
|
||||
default n
|
||||
help
|
||||
Say Y if you want to enable bes2600 testmode.
|
||||
If unsure, say N.
|
||||
|
||||
menu "Driver debug features"
|
||||
|
||||
config BES2600_DEBUGFS
|
||||
bool "Expose driver internals to DebugFS (DEVELOPMENT)"
|
||||
default y
|
||||
|
||||
config BES2600_BH_DEBUG
|
||||
bool "Enable low-level device communication logs (DEVELOPMENT)"
|
||||
|
||||
config BES2600_WSM_DEBUG
|
||||
bool "Enable WSM API debug messages (DEVELOPMENT)"
|
||||
|
||||
config BES2600_WSM_DUMPS
|
||||
bool "Verbose WSM API logging (DEVELOPMENT)"
|
||||
|
||||
config BES2600_WSM_DUMPS_SHORT
|
||||
bool "Dump only first x bytes (default 20) (DEVELOPMENT)"
|
||||
|
||||
config BES2600_TXRX_DEBUG
|
||||
bool "Enable TX/RX debug messages (DEVELOPMENT)"
|
||||
|
||||
config BES2600_TX_POLICY_DEBUG
|
||||
bool "Enable TX policy debug (DEVELOPMENT)"
|
||||
|
||||
config BES2600_STA_DEBUG
|
||||
bool "Enable STA/AP debug (DEVELOPMENT)"
|
||||
|
||||
config BES2600_DUMP_ON_ERROR
|
||||
bool "Dump kernel in case of critical error (DEVELOPMENT)"
|
||||
|
||||
config BES2600_ITP
|
||||
bool "Enable ITP DebugFS"
|
||||
help
|
||||
Say Y if you want to include ITP code.
|
||||
If unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
||||
endif # BES2600
|
||||
@@ -0,0 +1,230 @@
|
||||
KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
|
||||
# feature option
|
||||
BES2600 ?= m
|
||||
|
||||
CONFIG_BES2600_TESTMODE ?= n
|
||||
|
||||
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
|
||||
|
||||
CONFIG_BES2600_WAPI_SUPPORT ?= n
|
||||
CONFIG_BES2600_STA_DEBUG ?= y
|
||||
CONFIG_BES2600_STATIC_SDD ?= y
|
||||
P2P_MULTIVIF ?= y
|
||||
AP_AGGREGATE_FW_FIX ?= y
|
||||
CONFIG_BES2600_BT ?= n
|
||||
WIFI_BT_COEXIST_EPTA_ENABLE ?= y
|
||||
WIFI_BT_COEXIST_EPTA_FDD ?= n
|
||||
CONFIG_BES2600_WOWLAN ?= y
|
||||
CONFIG_BES2600_LISTEN_INTERVAL ?= 3
|
||||
BSS_LOSS_CHECK ?= y
|
||||
|
||||
CONFIG_BES2600_DEBUGFS ?= y
|
||||
CONFIG_BES2600_ITP ?= n
|
||||
|
||||
CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES ?= n
|
||||
CONFIG_BES2600_CALIB_FROM_LINUX ?= y
|
||||
|
||||
CONFIG_BES2600_WIFI_BOOT_ON ?= y
|
||||
CONFIG_BES2600_BT_BOOT_ON ?= n
|
||||
|
||||
BES2600_GPIO_WAKEUP_AP ?= n
|
||||
BES2600_WRITE_DPD_TO_FILE ?= n
|
||||
BES2600_TX_MORE_RETRY ?= n
|
||||
|
||||
# bes evb
|
||||
BES2600_INDEPENDENT_EVB ?= n
|
||||
# wifi module for allwinner made by bes
|
||||
BES2600_INTEGRATED_MODULE_V1 ?= n
|
||||
# xiaomi R329 wifi module
|
||||
BES2600_INTEGRATED_MODULE_V2 ?= n
|
||||
# sicun QM215 wifi module
|
||||
BES2600_INTEGRATED_MODULE_V3 ?= n
|
||||
|
||||
# 0: use dynamic_ps_timeout value
|
||||
# other: override dynamic_ps_timeout value
|
||||
BES2600_FASTPS_IDLE_TIME ?= 8
|
||||
|
||||
#1.rock api(fixed macaddr)
|
||||
#2.read from file(macaddr of customers)
|
||||
#3.read from file(macaddr template)
|
||||
#4.random macaddr
|
||||
GET_MAC_ADDR_METHOD ?= 4
|
||||
|
||||
ifeq ($(CONFIG_BES2600_DEBUGFS),y)
|
||||
BES2600_DUMP_FW_DPD_LOG ?= n
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_BES2600_TESTMODE),y)
|
||||
CONFIG_BES2600_KEEP_ALIVE ?= n
|
||||
ifeq ($(CONFIG_BES2600_KEEP_ALIVE),y)
|
||||
ccflags-y += -DVENDOR_XM_KEEPALIVE
|
||||
endif
|
||||
endif
|
||||
|
||||
BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||
|
||||
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||
FACTORY_CRC_CHECK ?= n
|
||||
STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||
FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
|
||||
endif
|
||||
|
||||
# basic function
|
||||
define boolen_flag
|
||||
$(strip $(if $(findstring $($(1)),$(2)),-D$(1)))
|
||||
endef
|
||||
|
||||
define string_flag
|
||||
$(strip $(if $($(1)),-D$(1)=\"$($(1))\"))
|
||||
endef
|
||||
|
||||
define value_flag
|
||||
$(strip $(if $($(1)),-D$(1)=$($(1))))
|
||||
endef
|
||||
|
||||
ccflags-y += -Werror
|
||||
|
||||
SDIO_HOST_ADMA_SUPPORT ?= y
|
||||
ccflags-y += -DCONFIG_BES2600_WLAN_BES
|
||||
ccflags-y += -DBES_SDIO_RXTX_TOGGLE
|
||||
ccflags-y += -DBES_SDIO_TX_MULTIPLE_ENABLE
|
||||
ccflags-y += -DBES_SDIO_RX_MULTIPLE_ENABLE
|
||||
ccflags-y += -DBES_UNIFIED_PM
|
||||
ccflags-y += -DBES_SDIO_OPTIMIZED_LEN
|
||||
ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG
|
||||
|
||||
ifeq ($(BES2600_WRITE_DPD_TO_FILE),y)
|
||||
BES2600_DPD_PATH ?= /data/cfg/bes2600_dpd.bin
|
||||
BES2600_DEFAULT_DPD_PATH ?= /lib/firmware/bes2600_dpd.bin
|
||||
BES2600_DPD_GOLDEN_PATH ?= /data/cfg/bes2600_dpd_golden.bin
|
||||
endif
|
||||
|
||||
ifeq ($(BES2600_DUMP_FW_DPD_LOG),y)
|
||||
BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log
|
||||
endif
|
||||
|
||||
# compilation macros setting
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_VENDOR_CMD,y)
|
||||
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_STATIC_SDD,y)
|
||||
ccflags-y += $(call boolen_flag,P2P_MULTIVIF,y)
|
||||
ccflags-y += $(call boolen_flag,AP_AGGREGATE_FW_FIX,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_BT,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_WAPI_SUPPORT,y)
|
||||
ccflags-y += $(call boolen_flag,WIFI_BT_COEXIST_EPTA_ENABLE,y)
|
||||
ccflags-y += $(call boolen_flag,WIFI_BT_COEXIST_EPTA_FDD,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_DISABLE_BEACON_HINTS,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES,y)
|
||||
ccflags-y += $(call boolen_flag,BES2600_TX_MORE_RETRY,y)
|
||||
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_ITP,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_DEBUGFS,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_BH_DEBUG,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DEBUG,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DUMPS,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DUMPS_SHORT,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_TXRX_DEBUG,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_TX_POLICY_DEBUG,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_STA_DEBUG,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_DUMP_ON_ERROR,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_WOWLAN,y)
|
||||
ccflags-y += $(call value_flag,CONFIG_BES2600_LISTEN_INTERVAL)
|
||||
ccflags-y += $(call value_flag,BES2600_FASTPS_IDLE_TIME)
|
||||
ccflags-y += $(call boolen_flag,BSS_LOSS_CHECK,y)
|
||||
|
||||
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_PATH)
|
||||
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE)
|
||||
ccflags-y += $(call string_flag,BES2600_DRV_VERSION)
|
||||
ccflags-y += $(call string_flag,BES2600_DPD_PATH)
|
||||
ccflags-y += $(call string_flag,BES2600_DEFAULT_DPD_PATH)
|
||||
ccflags-y += $(call string_flag,BES2600_DPD_GOLDEN_PATH)
|
||||
|
||||
ccflags-y += $(call boolen_flag,BES2600_INDEPENDENT_EVB,y)
|
||||
ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y)
|
||||
ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V2,y)
|
||||
ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V3,y)
|
||||
ccflags-y += $(call boolen_flag,SDIO_HOST_ADMA_SUPPORT,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_TESTMODE,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_KEEP_ALIVE,y)
|
||||
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_WIFI_BOOT_ON,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_BT_BOOT_ON,y)
|
||||
ccflags-y += $(call boolen_flag,CONFIG_BES2600_CALIB_FROM_LINUX,y)
|
||||
|
||||
ccflags-y += $(call value_flag,GET_MAC_ADDR_METHOD)
|
||||
ccflags-y += $(call string_flag,PATH_WIFI_MACADDR)
|
||||
ccflags-y += $(call string_flag,FACTORY_PATH)
|
||||
ccflags-y += $(call string_flag,FACTORY_DEFAULT_PATH)
|
||||
ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y)
|
||||
ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,y)
|
||||
|
||||
ccflags-y += $(call boolen_flag,BES2600_GPIO_WAKEUP_AP,y)
|
||||
ccflags-y += $(call boolen_flag,BES2600_WRITE_DPD_TO_FILE,y)
|
||||
|
||||
ccflags-y += $(call boolen_flag,BES2600_DUMP_FW_DPD_LOG,y)
|
||||
ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH)
|
||||
|
||||
ccflags-y += $(call boolen_flag,STANDARD_FACTORY_EFUSE_FLAG,y)
|
||||
|
||||
# internal feature options
|
||||
ccflags-y += -DAP_HT_CAP_UPDATE
|
||||
ccflags-y += -DBES2600_RX_IN_BH
|
||||
ccflags-y += -DDCW1260_DETECTION_LOGIC
|
||||
|
||||
# sdd options
|
||||
ccflags-y += -DTEST_11B=0
|
||||
ccflags-y += -DDPD_CALI=0
|
||||
ccflags-y += -DDPD_CALI=0
|
||||
ccflags-y += -DALI_CONFG=0
|
||||
ccflags-y += -DCHIP_WIFI_ROM_VER=1
|
||||
ccflags-y += -DWIFI_OUT_FEM=0
|
||||
ccflags-y += -DRF_TX_CONTROL_IO=16
|
||||
|
||||
# stbc rx option
|
||||
ccflags-y += -DSTBC_RX_24G=0
|
||||
ccflags-y += -DSTBC_RX_5G=0
|
||||
|
||||
#ccflags-y += -DP2P_STA_COEX
|
||||
#ccflags-y += -DMCAST_FWDING
|
||||
#ccflags-y += -DAP_AGGREGATE_FW_FIX
|
||||
# Extra IE for probe response from upper layer is needed in P2P GO
|
||||
# For offloading probe response to FW, the extra IE must be included
|
||||
# in the probe response template
|
||||
#ccflags-y += -DPROBE_RESP_EXTRA_IE
|
||||
ccflags-y += -DIPV6_FILTERING
|
||||
|
||||
# basic files for building module
|
||||
bes2600-y := \
|
||||
fwio.o \
|
||||
txrx.o \
|
||||
main.o \
|
||||
queue.o \
|
||||
hwio.o \
|
||||
bh.o \
|
||||
wsm.o \
|
||||
sta.o \
|
||||
ap.o \
|
||||
scan.o \
|
||||
bes_chardev.o \
|
||||
tx_loop.o \
|
||||
bes_fw.o \
|
||||
bes_fw_common.o \
|
||||
bes2600_factory.o \
|
||||
bes2600_sdio.o
|
||||
|
||||
# compilation files select
|
||||
bes2600-$(CONFIG_BES2600_DEBUGFS) += debug.o
|
||||
bes2600-$(CONFIG_BES2600_ITP) += itp.o
|
||||
bes2600-$(CONFIG_PM) += pm.o
|
||||
bes2600-$(CONFIG_BES2600_BT) += bes2600_btusb.o
|
||||
bes2600-$(CONFIG_BES2600_TESTMODE) += wifi_testmode_cmd.o
|
||||
|
||||
bes2600-$(WIFI_BT_COEXIST_EPTA_ENABLE) += epta_coex.o epta_request.o
|
||||
bes2600-$(CONFIG_BES2600_WOWLAN) += bes_pwr.o
|
||||
|
||||
obj-$(BES2600) += bes2600.o
|
||||
|
||||
all: modules
|
||||
|
||||
modules clean:
|
||||
$(MAKE) -C $(KERN_DIR) M=$(shell pwd) $@
|
||||
+1946
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* mac80211 STA and AP API for mac80211 BES2600 drivers
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/version.h>
|
||||
#ifndef AP_H_INCLUDED
|
||||
#define AP_H_INCLUDED
|
||||
|
||||
#define BES2600_NOA_NOTIFICATION_DELAY 10
|
||||
|
||||
int bes2600_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
|
||||
bool set);
|
||||
int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
int bes2600_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
void bes2600_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
|
||||
enum sta_notify_cmd notify_cmd,
|
||||
struct ieee80211_sta *sta);
|
||||
void bes2600_bss_info_changed(struct ieee80211_hw *dev,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *info,
|
||||
u64 changed);
|
||||
|
||||
int bes2600_ampdu_action(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_ampdu_params *params);
|
||||
|
||||
void bes2600_suspend_resume(struct bes2600_vif *priv,
|
||||
struct wsm_suspend_resume *arg);
|
||||
void bes2600_set_tim_work(struct work_struct *work);
|
||||
void bes2600_set_cts_work(struct work_struct *work);
|
||||
void bes2600_multicast_start_work(struct work_struct *work);
|
||||
void bes2600_multicast_stop_work(struct work_struct *work);
|
||||
void bes2600_mcast_timeout(struct timer_list *t);
|
||||
int bes2600_find_link_id(struct bes2600_vif *priv, const u8 *mac);
|
||||
int bes2600_alloc_link_id(struct bes2600_vif *priv, const u8 *mac);
|
||||
void bes2600_link_id_work(struct work_struct *work);
|
||||
void bes2600_link_id_gc_work(struct work_struct *work);
|
||||
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
||||
void bes2600_notify_noa(struct bes2600_vif *priv, int delay);
|
||||
#endif
|
||||
int cw12xx_unmap_link(struct bes2600_vif *priv, int link_id);
|
||||
#ifdef AP_HT_CAP_UPDATE
|
||||
void bes2600_ht_info_update_work(struct work_struct *work);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,859 @@
|
||||
/*
|
||||
* Common private data for BES2600 drivers
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* Based on the mac80211 Prism54 code, which is
|
||||
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
|
||||
*
|
||||
* Based on the islsm (softmac prism54) driver, which is:
|
||||
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_H
|
||||
#define BES2600_H
|
||||
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/usb.h>
|
||||
#include <net/mac80211.h>
|
||||
#ifdef P2P_MULTIVIF
|
||||
#define CW12XX_MAX_VIFS (3)
|
||||
#else
|
||||
#define CW12XX_MAX_VIFS (2)
|
||||
#endif
|
||||
#define CW12XX_GENERIC_IF_ID (2)
|
||||
#define CW12XX_HOST_VIF0_11N_THROTTLE (63)
|
||||
#define CW12XX_HOST_VIF1_11N_THROTTLE (63)
|
||||
#define CW12XX_HOST_VIF0_11BG_THROTTLE (15)
|
||||
#define CW12XX_HOST_VIF1_11BG_THROTTLE (15)
|
||||
#if 0
|
||||
#define CW12XX_FW_VIF0_THROTTLE (15)
|
||||
#define CW12XX_FW_VIF1_THROTTLE (15)
|
||||
#endif
|
||||
#define CW12XX_MAX_QUEUE_SZ (128)
|
||||
|
||||
#define IEEE80211_FCTL_WEP 0x4000
|
||||
#define IEEE80211_QOS_DATAGRP 0x0080
|
||||
#define WSM_KEY_MAX_IDX 20
|
||||
|
||||
#include "queue.h"
|
||||
#include "wsm.h"
|
||||
#include "scan.h"
|
||||
#include "txrx.h"
|
||||
#include "ht.h"
|
||||
#include "pm.h"
|
||||
#include "fwio.h"
|
||||
#include "bes_pwr.h"
|
||||
#include "tx_loop.h"
|
||||
#include "bes_log.h"
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
#include "bes_nl80211_testmode_msg.h"
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
|
||||
|
||||
/* extern */ struct sbus_ops;
|
||||
/* extern */ struct task_struct;
|
||||
/* extern */ struct bes2600_debug_priv;
|
||||
/* extern */ struct bes2600_debug_common;
|
||||
/* extern */ struct firmware;
|
||||
|
||||
/* #define ROC_DEBUG */
|
||||
|
||||
/* hidden ssid is only supported when separate probe resp IE
|
||||
configuration is supported */
|
||||
#ifdef PROBE_RESP_EXTRA_IE
|
||||
#define HIDDEN_SSID 1
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BES2600_TXRX_DEBUG)
|
||||
#define txrx_printk(...) printk(__VA_ARGS__)
|
||||
#else
|
||||
#define txrx_printk(...)
|
||||
#endif
|
||||
|
||||
#define BES2600_MAX_CTRL_FRAME_LEN (0x1000)
|
||||
|
||||
#define CW1250_MAX_STA_IN_AP_MODE (14)
|
||||
#define WLAN_LINK_ID_MAX (CW1250_MAX_STA_IN_AP_MODE + 3)
|
||||
|
||||
#define BES2600_MAX_STA_IN_AP_MODE (5)
|
||||
#define BES2600_MAX_REQUEUE_ATTEMPTS (5)
|
||||
#define BES2600_LINK_ID_UNMAPPED (15)
|
||||
|
||||
#define BES2600_MAX_TID (8)
|
||||
|
||||
#define BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F)
|
||||
#define BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F)
|
||||
#define BES2600_RX_BLOCK_ACK_ENABLED_FOR_BE_TID \
|
||||
(BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID & 0x01)
|
||||
#define BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0)
|
||||
#define BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0)
|
||||
|
||||
#define BES2600_BLOCK_ACK_CNT (30)
|
||||
#define BES2600_BLOCK_ACK_THLD (800)
|
||||
#define BES2600_BLOCK_ACK_HIST (3)
|
||||
#define BES2600_BLOCK_ACK_INTERVAL (1 * HZ / BES2600_BLOCK_ACK_HIST)
|
||||
#define CW12XX_ALL_IFS (-1)
|
||||
#ifdef ROAM_OFFLOAD
|
||||
#define BES2600_SCAN_TYPE_ACTIVE 0x1000
|
||||
#define BES2600_SCAN_BAND_5G 0X2000
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
|
||||
#define IEEE80211_FCTL_WEP 0x4000
|
||||
#define IEEE80211_QOS_DATAGRP 0x0080
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
#define BES2600_SCAN_MEASUREMENT_PASSIVE (0)
|
||||
#define BES2600_SCAN_MEASUREMENT_ACTIVE (1)
|
||||
#endif
|
||||
|
||||
#ifdef MCAST_FWDING
|
||||
#define WSM_MAX_BUF 30
|
||||
#endif
|
||||
|
||||
#ifdef BSS_LOSS_CHECK
|
||||
#define BSS_LOSS_CK_THR 1
|
||||
#define BSS_LOSS_CK_INV 2000
|
||||
#define BSS_LOSS_CFM_THR 1
|
||||
#define BSS_LOSS_CFM_INV 200
|
||||
#else
|
||||
#define BSS_LOSS_CFM_INV 0
|
||||
#endif
|
||||
|
||||
/* Please keep order */
|
||||
enum bes2600_join_status {
|
||||
BES2600_JOIN_STATUS_PASSIVE = 0,
|
||||
BES2600_JOIN_STATUS_MONITOR,
|
||||
BES2600_JOIN_STATUS_STA,
|
||||
BES2600_JOIN_STATUS_AP,
|
||||
};
|
||||
|
||||
enum bes2600_link_status {
|
||||
BES2600_LINK_OFF,
|
||||
BES2600_LINK_RESERVE,
|
||||
BES2600_LINK_SOFT,
|
||||
BES2600_LINK_HARD,
|
||||
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
||||
BES2600_LINK_RESET,
|
||||
BES2600_LINK_RESET_REMAP,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum bes2600_bss_loss_status {
|
||||
BES2600_BSS_LOSS_NONE,
|
||||
BES2600_BSS_LOSS_CHECKING,
|
||||
BES2600_BSS_LOSS_CONFIRMING,
|
||||
BES2600_BSS_LOSS_CONFIRMED,
|
||||
};
|
||||
|
||||
struct bes2600_link_entry {
|
||||
unsigned long timestamp;
|
||||
enum bes2600_link_status status;
|
||||
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
||||
enum bes2600_link_status prev_status;
|
||||
#endif
|
||||
u8 mac[ETH_ALEN];
|
||||
u8 buffered[BES2600_MAX_TID];
|
||||
struct sk_buff_head rx_queue;
|
||||
};
|
||||
|
||||
#if defined(ROAM_OFFLOAD) || defined(CONFIG_BES2600_TESTMODE)
|
||||
struct bes2600_testframe {
|
||||
u8 len;
|
||||
u8 *data;
|
||||
};
|
||||
#endif
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
struct advance_scan_elems {
|
||||
u8 scanMode;
|
||||
u16 duration;
|
||||
};
|
||||
/**
|
||||
* bes2600_tsm_info - Keeps information about ongoing TSM collection
|
||||
* @ac: Access category for which metrics to be collected
|
||||
* @use_rx_roaming: Use received voice packets to compute roam delay
|
||||
* @sta_associated: Set to 1 after association
|
||||
* @sta_roamed: Set to 1 after successful roaming
|
||||
* @roam_delay: Roam delay
|
||||
* @rx_timestamp_vo: Timestamp of received voice packet
|
||||
* @txconf_timestamp_vo: Timestamp of received tx confirmation for
|
||||
* successfully transmitted VO packet
|
||||
* @sum_pkt_q_delay: Sum of packet queue delay
|
||||
* @sum_media_delay: Sum of media delay
|
||||
*
|
||||
*/
|
||||
struct bes2600_tsm_info {
|
||||
u8 ac;
|
||||
u8 use_rx_roaming;
|
||||
u8 sta_associated;
|
||||
u8 sta_roamed;
|
||||
u16 roam_delay;
|
||||
u32 rx_timestamp_vo;
|
||||
u32 txconf_timestamp_vo;
|
||||
u32 sum_pkt_q_delay;
|
||||
u32 sum_media_delay;
|
||||
};
|
||||
|
||||
/**
|
||||
* bes2600_start_stop_tsm - To start or stop collecting TSM metrics in
|
||||
* bes2600 driver
|
||||
* @start: To start or stop collecting TSM metrics
|
||||
* @up: up for which metrics to be collected
|
||||
* @packetization_delay: Packetization delay for this TID
|
||||
*
|
||||
*/
|
||||
struct bes2600_start_stop_tsm {
|
||||
u8 start; /*1: To start, 0: To stop*/
|
||||
u8 up;
|
||||
u16 packetization_delay;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BES2600_TESTMODE */
|
||||
|
||||
/*
|
||||
* tcp & udp alive
|
||||
*/
|
||||
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
||||
#define IP_KEEPALIVE_MAX_LEN (256 + 8)
|
||||
#define AES_KEY_IV_LEN (16 + 16)
|
||||
#define AES_KEY_LEN (16)
|
||||
#define AES_IV_LEN (16)
|
||||
#define NUM_IP_FRAMES 8
|
||||
#define TCP_PROTO 6
|
||||
#define UDP_PROTO 17
|
||||
|
||||
#define KLV_VENDOR_DEFAULT 0
|
||||
#define KLV_VENDOR_XM 1
|
||||
#define WEBSOCKET_HD_LEN 6
|
||||
|
||||
#ifdef P2P_MULTIVIF
|
||||
#define NET_DEVICE_NUM (3)
|
||||
#else
|
||||
#define NET_DEVICE_NUM (2)
|
||||
#endif
|
||||
|
||||
struct ip_header {
|
||||
/* version / header length */
|
||||
uint8_t _v_hl;
|
||||
/* type of service */
|
||||
uint8_t _tos;
|
||||
/* total length */
|
||||
uint16_t _len;
|
||||
/* identification */
|
||||
uint16_t _id;
|
||||
/* fragment offset field */
|
||||
uint16_t _offset;
|
||||
/* time to live */
|
||||
uint8_t _ttl;
|
||||
/* protocol*/
|
||||
uint8_t _proto;
|
||||
/* checksum */
|
||||
uint16_t _chksum;
|
||||
/* source and destination IP addresses */
|
||||
uint32_t src;
|
||||
uint32_t dest;
|
||||
} ;
|
||||
|
||||
struct tcp_header {
|
||||
uint16_t src;
|
||||
uint16_t dest;
|
||||
uint32_t seqno;
|
||||
uint32_t ackno;
|
||||
uint16_t _hdrlen_rsvd_flags;
|
||||
uint16_t wnd;
|
||||
uint16_t chksum;
|
||||
uint16_t urgp;
|
||||
};
|
||||
|
||||
struct udp_header {
|
||||
uint16_t src;
|
||||
uint16_t dest;
|
||||
uint16_t len;
|
||||
uint16_t chksum;
|
||||
};
|
||||
|
||||
struct ip_alive_info {
|
||||
uint8_t idx_used;
|
||||
uint8_t proto; /* 0 for udp and 1 for tcp; */
|
||||
uint16_t src_port;
|
||||
uint16_t dest_port;
|
||||
uint32_t src_ip;
|
||||
uint32_t dest_ip;
|
||||
uint16_t len;
|
||||
uint32_t next_seqno;
|
||||
uint8_t payload[IP_KEEPALIVE_MAX_LEN];
|
||||
uint8_t dest_mac[6];
|
||||
};
|
||||
|
||||
struct ip_alive_cfg {
|
||||
struct ip_header iphd;
|
||||
struct tcp_header tcphd;
|
||||
struct udp_header udphd;
|
||||
struct ip_alive_info bd;
|
||||
uint8_t aes_key[AES_KEY_LEN];
|
||||
uint8_t aes_iv[AES_IV_LEN];
|
||||
uint8_t klv_vendor; /* stands for different vendor's keep-alive resolution; */
|
||||
};
|
||||
#endif /* CONFIG_BES2600_KEEP_ALIVE */
|
||||
|
||||
struct bes2600_common {
|
||||
struct bes2600_debug_common *debug;
|
||||
struct bes2600_queue tx_queue[4];
|
||||
struct bes2600_queue_stats tx_queue_stats;
|
||||
|
||||
struct ieee80211_hw *hw;
|
||||
struct mac_address addresses[CW12XX_MAX_VIFS];
|
||||
|
||||
/*Will be a pointer to a list of VIFs - Dynamically allocated */
|
||||
struct ieee80211_vif *vif_list[CW12XX_MAX_VIFS];
|
||||
atomic_t num_vifs;
|
||||
atomic_t netdevice_start;
|
||||
spinlock_t vif_list_lock;
|
||||
u32 if_id_slot;
|
||||
struct device *pdev;
|
||||
struct workqueue_struct *workqueue;
|
||||
|
||||
struct semaphore conf_lock;
|
||||
|
||||
const struct sbus_ops *sbus_ops;
|
||||
struct sbus_priv *sbus_priv;
|
||||
|
||||
/* HW/FW type (HIF_...) */
|
||||
int hw_type;
|
||||
int hw_revision;
|
||||
int fw_revision;
|
||||
|
||||
/* firmware/hardware info */
|
||||
unsigned int tx_hdr_len;
|
||||
|
||||
/* Radio data */
|
||||
int output_power;
|
||||
int noise;
|
||||
|
||||
/* calibration, output power limit and rssi<->dBm conversation data */
|
||||
|
||||
/* BBP/MAC state */
|
||||
const struct firmware *sdd;
|
||||
struct ieee80211_rate *rates;
|
||||
struct ieee80211_rate *mcs_rates;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
/*TODO:COMBO: To be made per VIFF after mac80211 support */
|
||||
struct ieee80211_channel *channel;
|
||||
int channel_switch_in_progress;
|
||||
wait_queue_head_t channel_switch_done;
|
||||
u8 long_frame_max_tx_count;
|
||||
u8 short_frame_max_tx_count;
|
||||
/* TODO:COMBO: According to Hong aggregation will happen per VIFF.
|
||||
* Keeping in common structure for the time being. Will be moved to VIFF
|
||||
* after the mechanism is clear */
|
||||
u8 ba_tid_mask;
|
||||
int ba_acc; /*TODO: Same as above */
|
||||
int ba_cnt; /*TODO: Same as above */
|
||||
int ba_cnt_rx; /*TODO: Same as above */
|
||||
int ba_acc_rx; /*TODO: Same as above */
|
||||
int ba_hist; /*TODO: Same as above */
|
||||
struct timer_list ba_timer;/*TODO: Same as above */
|
||||
spinlock_t ba_lock; /*TODO: Same as above */
|
||||
bool ba_ena; /*TODO: Same as above */
|
||||
struct work_struct ba_work; /*TODO: Same as above */
|
||||
bool is_BT_Present;
|
||||
bool is_go_thru_go_neg;
|
||||
u8 conf_listen_interval;
|
||||
|
||||
/* BH */
|
||||
atomic_t bh_rx;
|
||||
atomic_t bh_tx;
|
||||
atomic_t bh_term;
|
||||
atomic_t bh_suspend;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*Suspend*/
|
||||
u8 unjoin_if_id_slots;
|
||||
bool suspend_in_progress;
|
||||
struct notifier_block pm_notify;
|
||||
#endif
|
||||
|
||||
struct workqueue_struct *bh_workqueue;
|
||||
struct work_struct bh_work;
|
||||
|
||||
int bh_error;
|
||||
wait_queue_head_t bh_wq;
|
||||
wait_queue_head_t bh_evt_wq;
|
||||
int buf_id_tx; /* byte */
|
||||
int buf_id_rx; /* byte */
|
||||
int wsm_rx_seq[2]; /* idx 0: Normal tx/rx, idx 1: special cmd */
|
||||
int wsm_tx_seq[2]; /* idx 0: Normal tx/rx, idx 1: special cmd */
|
||||
int wsm_tx_pending[2]; /* idx 0: Normal tx/rx, idx 1: special cmd */
|
||||
struct timer_list mcu_mon_timer;
|
||||
struct timer_list lmac_mon_timer;
|
||||
int hw_bufs_used;
|
||||
int hw_bufs_used_vif[CW12XX_MAX_VIFS];
|
||||
struct sk_buff *skb_cache;
|
||||
/* Keep bes2600 awake (WUP = 1) 1 second after each scan to avoid
|
||||
* FW issue with sleeping/waking up. */
|
||||
atomic_t recent_scan;
|
||||
|
||||
/* WSM */
|
||||
struct wsm_caps wsm_caps;
|
||||
struct semaphore wsm_cmd_sema;
|
||||
struct wsm_buf wsm_cmd_buf;
|
||||
struct wsm_cmd wsm_cmd;
|
||||
wait_queue_head_t wsm_cmd_wq;
|
||||
wait_queue_head_t wsm_startup_done;
|
||||
struct wsm_cbc wsm_cbc;
|
||||
atomic_t tx_lock;
|
||||
u32 pending_frame_id;
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
/* Device Power Range */
|
||||
struct wsm_tx_power_range txPowerRange[2];
|
||||
/* Advance Scan */
|
||||
struct advance_scan_elems advanceScanElems;
|
||||
bool enable_advance_scan;
|
||||
struct delayed_work advance_scan_timeout;
|
||||
#endif /* CONFIG_BES2600_TESTMODE */
|
||||
|
||||
/* WSM debug */
|
||||
int wsm_enable_wsm_dumps;
|
||||
u32 wsm_dump_max_size;
|
||||
|
||||
/* Scan status */
|
||||
struct bes2600_scan scan;
|
||||
|
||||
/* TX/RX */
|
||||
unsigned long rx_timestamp;
|
||||
|
||||
/* Scan Timestamp */
|
||||
unsigned long scan_timestamp;
|
||||
|
||||
/* WSM events */
|
||||
spinlock_t event_queue_lock;
|
||||
struct list_head event_queue;
|
||||
struct work_struct event_handler;
|
||||
|
||||
/* TX rate policy cache */
|
||||
struct tx_policy_cache tx_policy_cache;
|
||||
struct work_struct tx_policy_upload_work;
|
||||
|
||||
/* cryptographic engine information */
|
||||
|
||||
/* statistics */
|
||||
struct ieee80211_low_level_stats stats;
|
||||
|
||||
struct bes2600_ht_info ht_info;
|
||||
int tx_burst_idx;
|
||||
|
||||
struct ieee80211_iface_limit if_limits1[2];
|
||||
struct ieee80211_iface_limit if_limits2[2];
|
||||
struct ieee80211_iface_limit if_limits3[2];
|
||||
struct ieee80211_iface_combination if_combs[3];
|
||||
|
||||
struct semaphore wsm_oper_lock;
|
||||
struct delayed_work rem_chan_timeout;
|
||||
MIB_TXRX_OPT_PARAM txrx_opt_param;
|
||||
u32 rtsvalue;
|
||||
spinlock_t rtsvalue_lock;
|
||||
struct timer_list txrx_opt_timer;
|
||||
struct work_struct dynamic_opt_txrx_work;
|
||||
atomic_t remain_on_channel;
|
||||
int roc_if_id;
|
||||
u64 roc_cookie;
|
||||
wait_queue_head_t offchannel_wq;
|
||||
u16 offchannel_done;
|
||||
u16 prev_channel;
|
||||
int if_id_selected;
|
||||
u32 key_map;
|
||||
struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1];
|
||||
#ifdef MCAST_FWDING
|
||||
struct wsm_buf wsm_release_buf[WSM_MAX_BUF];
|
||||
u8 buf_released;
|
||||
#endif
|
||||
#ifdef ROAM_OFFLOAD
|
||||
u8 auto_scanning;
|
||||
u8 frame_rcvd;
|
||||
u8 num_scanchannels;
|
||||
u8 num_2g_channels;
|
||||
u8 num_5g_channels;
|
||||
struct wsm_scan_ch scan_channels[48];
|
||||
struct sk_buff *beacon;
|
||||
struct sk_buff *beacon_bkp;
|
||||
struct bes2600_testframe testframe;
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
struct bes2600_testframe test_frame;
|
||||
struct bes_tsm_stats tsm_stats;
|
||||
struct bes2600_tsm_info tsm_info;
|
||||
spinlock_t tsm_lock;
|
||||
struct bes2600_start_stop_tsm start_stop_tsm;
|
||||
#endif /* CONFIG_BES2600_TESTMODE */
|
||||
u8 connected_sta_cnt;
|
||||
u16 vif0_throttle;
|
||||
u16 vif1_throttle;
|
||||
int scan_switch_if_id;
|
||||
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
||||
int last_ins_wapi_usk_id;
|
||||
int last_del_wapi_usk_id;
|
||||
#endif
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
struct semaphore vendor_rf_cmd_replay_sema;
|
||||
#endif
|
||||
|
||||
/* member for coexistence */
|
||||
struct work_struct coex_work;
|
||||
struct list_head coex_event_list;
|
||||
spinlock_t coex_event_lock;
|
||||
|
||||
/* member for low power */
|
||||
struct bes2600_pwr_t bes_power;
|
||||
|
||||
/* member for tx loop */
|
||||
struct bes2600_tx_loop tx_loop;
|
||||
|
||||
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
||||
struct ip_alive_cfg iac[NUM_IP_FRAMES];
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Virtual Interface State. One copy per VIF */
|
||||
struct bes2600_vif {
|
||||
atomic_t enabled;
|
||||
spinlock_t vif_lock;
|
||||
int if_id;
|
||||
/*TODO: Split into Common and VIF parts */
|
||||
struct bes2600_debug_priv *debug;
|
||||
/* BBP/MAC state */
|
||||
u8 bssid[ETH_ALEN];
|
||||
struct wsm_edca_params edca;
|
||||
struct wsm_tx_queue_params tx_queue_params;
|
||||
struct wsm_association_mode association_mode;
|
||||
struct wsm_set_bss_params bss_params;
|
||||
struct wsm_set_pm powersave_mode;
|
||||
struct wsm_set_pm firmware_ps_mode;
|
||||
int power_set_true;
|
||||
int user_power_set_true;
|
||||
u8 user_pm_mode;
|
||||
int cqm_rssi_thold;
|
||||
unsigned cqm_rssi_hyst;
|
||||
unsigned cqm_tx_failure_thold;
|
||||
unsigned cqm_tx_failure_count;
|
||||
unsigned cmq_tx_success_count;
|
||||
bool cqm_use_rssi;
|
||||
int cqm_link_loss_count;
|
||||
int cqm_beacon_loss_count;
|
||||
int mode;
|
||||
bool enable_beacon;
|
||||
int beacon_int;
|
||||
size_t ssid_length;
|
||||
u8 ssid[IEEE80211_MAX_SSID_LEN];
|
||||
#ifdef HIDDEN_SSID
|
||||
bool hidden_ssid;
|
||||
#endif
|
||||
bool listening;
|
||||
struct wsm_rx_filter rx_filter;
|
||||
struct wsm_beacon_filter_table bf_table;
|
||||
struct wsm_beacon_filter_control bf_control;
|
||||
struct wsm_multicast_filter multicast_filter;
|
||||
bool has_multicast_subscription;
|
||||
struct wsm_broadcast_addr_filter broadcast_filter;
|
||||
bool disable_beacon_filter;
|
||||
struct wsm_arp_ipv4_filter filter4;
|
||||
#ifdef IPV6_FILTERING
|
||||
struct wsm_ndp_ipv6_filter filter6;
|
||||
#endif /*IPV6_FILTERING*/
|
||||
struct work_struct update_filtering_work;
|
||||
struct work_struct set_beacon_wakeup_period_work;
|
||||
struct bes2600_pm_state_vif pm_state_vif;
|
||||
/*TODO: Add support in mac80211 for psmode info per VIF */
|
||||
struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo;
|
||||
struct wsm_uapsd_info uapsd_info;
|
||||
bool setbssparams_done;
|
||||
u32 listen_interval;
|
||||
u32 erp_info;
|
||||
bool powersave_enabled;
|
||||
|
||||
/* WSM Join */
|
||||
enum bes2600_join_status join_status;
|
||||
u8 join_bssid[ETH_ALEN];
|
||||
struct work_struct join_work;
|
||||
struct delayed_work join_timeout;
|
||||
struct work_struct unjoin_work;
|
||||
struct work_struct offchannel_work;
|
||||
int join_dtim_period;
|
||||
bool delayed_unjoin;
|
||||
atomic_t connect_in_process;
|
||||
|
||||
/* Security */
|
||||
s8 wep_default_key_id;
|
||||
struct work_struct wep_key_work;
|
||||
unsigned long rx_timestamp;
|
||||
u32 cipherType;
|
||||
|
||||
|
||||
/* AP powersave */
|
||||
u32 link_id_map;
|
||||
u32 max_sta_ap_mode;
|
||||
u32 link_id_after_dtim;
|
||||
u32 link_id_uapsd;
|
||||
u32 link_id_max;
|
||||
u32 wsm_key_max_idx;
|
||||
struct bes2600_link_entry link_id_db[CW1250_MAX_STA_IN_AP_MODE];
|
||||
struct work_struct link_id_work;
|
||||
struct delayed_work link_id_gc_work;
|
||||
u32 sta_asleep_mask;
|
||||
u32 pspoll_mask;
|
||||
bool aid0_bit_set;
|
||||
spinlock_t ps_state_lock;
|
||||
bool buffered_multicasts;
|
||||
bool tx_multicast;
|
||||
struct work_struct set_tim_work;
|
||||
struct delayed_work set_cts_work;
|
||||
struct work_struct multicast_start_work;
|
||||
struct work_struct multicast_stop_work;
|
||||
struct timer_list mcast_timeout;
|
||||
|
||||
/* CQM Implementation */
|
||||
struct delayed_work bss_loss_work;
|
||||
struct delayed_work connection_loss_work;
|
||||
struct work_struct tx_failure_work;
|
||||
int delayed_link_loss;
|
||||
spinlock_t bss_loss_lock;
|
||||
int bss_loss_status;
|
||||
int bss_loss_confirm_id;
|
||||
|
||||
struct ieee80211_vif *vif;
|
||||
struct bes2600_common *hw_priv;
|
||||
struct ieee80211_hw *hw;
|
||||
|
||||
/* ROC implementation */
|
||||
struct delayed_work pending_offchanneltx_work;
|
||||
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
||||
/* Workaround for WFD testcase 6.1.10*/
|
||||
struct work_struct linkid_reset_work;
|
||||
u8 action_frame_sa[ETH_ALEN];
|
||||
u8 action_linkid;
|
||||
#endif
|
||||
bool htcap;
|
||||
#ifdef AP_HT_CAP_UPDATE
|
||||
u16 ht_info;
|
||||
struct work_struct ht_info_update_work;
|
||||
#endif
|
||||
bool pmf;
|
||||
|
||||
u32 hw_value;
|
||||
/* dot11CountersTable */
|
||||
u32 dot11TransmittedFragmentCount;
|
||||
u32 dot11MulticastTransmittedFrameCount;
|
||||
u32 dot11FailedCount;
|
||||
u32 dot11RetryCount;
|
||||
u32 dot11MultipleRetryCount;
|
||||
u32 dot11FrameDuplicateCount;
|
||||
u32 dot11ReceivedFragmentCount;
|
||||
u32 dot11RxReorderLeakCount;
|
||||
u32 dot11ReceivedBytes;
|
||||
u32 dot11ReceivedDataBytes;
|
||||
u32 dot11MulticastReceivedFrameCount;
|
||||
u32 dot11TransmittedFrameCount;
|
||||
u32 dot11TransmittedBytes;
|
||||
u32 dot11TransmittedDataBytes;
|
||||
u32 dot11Txbps;
|
||||
u32 dot11Rxbps;
|
||||
|
||||
/* used to calculate signal strength */
|
||||
s32 signal;
|
||||
s32 signal_mul;
|
||||
};
|
||||
struct bes2600_sta_priv {
|
||||
int link_id;
|
||||
struct bes2600_vif *priv;
|
||||
};
|
||||
enum bes2600_data_filterid {
|
||||
IPV4ADDR_FILTER_ID = 0,
|
||||
#ifdef IPV6_FILTERING
|
||||
IPV6ADDR_FILTER_ID,
|
||||
#endif /*IPV6_FILTERING*/
|
||||
};
|
||||
|
||||
static inline
|
||||
struct bes2600_common *cw12xx_vifpriv_to_hwpriv(struct bes2600_vif *priv)
|
||||
{
|
||||
return priv->hw_priv;
|
||||
}
|
||||
|
||||
|
||||
static inline
|
||||
struct bes2600_vif *cw12xx_get_vif_from_ieee80211(struct ieee80211_vif *vif)
|
||||
{
|
||||
return (struct bes2600_vif *)vif->drv_priv;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct bes2600_vif *cw12xx_hwpriv_to_vifpriv(struct bes2600_common *hw_priv,
|
||||
int if_id)
|
||||
{
|
||||
struct bes2600_vif *vif;
|
||||
|
||||
if (WARN_ON((-1 == if_id) || (if_id > CW12XX_MAX_VIFS)))
|
||||
return NULL;
|
||||
/* TODO:COMBO: During scanning frames can be received
|
||||
* on interface ID 3 */
|
||||
spin_lock(&hw_priv->vif_list_lock);
|
||||
if (!hw_priv->vif_list[if_id]) {
|
||||
spin_unlock(&hw_priv->vif_list_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vif = cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[if_id]);
|
||||
WARN_ON(!vif);
|
||||
if (vif)
|
||||
spin_lock(&vif->vif_lock);
|
||||
spin_unlock(&hw_priv->vif_list_lock);
|
||||
return vif;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct bes2600_vif *__cw12xx_hwpriv_to_vifpriv(struct bes2600_common *hw_priv,
|
||||
int if_id)
|
||||
{
|
||||
WARN_ON((-1 == if_id) || (if_id > CW12XX_MAX_VIFS));
|
||||
/* TODO:COMBO: During scanning frames can be received
|
||||
* on interface ID 3 */
|
||||
if (!hw_priv->vif_list[if_id]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[if_id]);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct bes2600_vif *cw12xx_get_activevif(struct bes2600_common *hw_priv)
|
||||
{
|
||||
return cw12xx_hwpriv_to_vifpriv(hw_priv, ffs(hw_priv->if_id_slot)-1);
|
||||
}
|
||||
|
||||
static inline bool is_hardware_cw1250(struct bes2600_common *hw_priv)
|
||||
{
|
||||
return (hw_priv->hw_revision == BES2600_HW_REV_CUT20);
|
||||
}
|
||||
|
||||
static inline bool is_hardware_cw1260(struct bes2600_common *hw_priv)
|
||||
{
|
||||
return (hw_priv->hw_revision == BES2600_HW_REV_CUT10);
|
||||
}
|
||||
|
||||
static inline int cw12xx_get_nr_hw_ifaces(struct bes2600_common *hw_priv)
|
||||
{
|
||||
switch(hw_priv->hw_revision) {
|
||||
case BES2600_HW_REV_CUT10:
|
||||
case BES2600_HW_REV_CUT11:
|
||||
case BES2600_HW_REV_CUT20:
|
||||
case BES2600_HW_REV_CUT22:
|
||||
return 1;
|
||||
case CW1250_HW_REV_CUT10:
|
||||
return 3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
||||
/* IPV4 host addr info */
|
||||
struct ipv4_addr_info {
|
||||
u8 filter_mode;
|
||||
u8 address_mode;
|
||||
u8 ipv4[4];
|
||||
};
|
||||
|
||||
/* tcp keep alive test period */
|
||||
struct MIB_TCP_KEEP_ALIVE_PERIOD {
|
||||
u16 TcpKeepAlivePeriod; /* in seconds */
|
||||
u8 EncrType; /* (ex. WSM_KEY_TYPE_WEP_DEFAULT) */
|
||||
u8 Reserved;
|
||||
};
|
||||
|
||||
#ifdef VENDOR_XM_KEEPALIVE
|
||||
struct ip_alive_satus {
|
||||
bool udp;
|
||||
bool tcp;
|
||||
};
|
||||
|
||||
void bes2600_get_keepalive_info(struct bes2600_common *hw_priv, struct ip_alive_satus *status);
|
||||
#endif
|
||||
|
||||
int bes2600_set_ip_offload(struct bes2600_common *hw_priv,
|
||||
struct bes2600_vif *priv,
|
||||
struct ip_alive_cfg *tac,
|
||||
u16 idx);
|
||||
|
||||
int bes2600_del_ip_offload(struct bes2600_common *hw_priv,
|
||||
struct bes2600_vif *priv,
|
||||
u8 stream_idx);
|
||||
|
||||
int bes2600_en_ip_offload(struct bes2600_common *hw_priv,
|
||||
struct bes2600_vif *priv,
|
||||
u16 period_in_s);
|
||||
int bes2600_set_ipv4addrfilter(struct bes2600_common *hw_priv, u8 *data, int if_id);
|
||||
#endif /* CONFIG_BES2600_KEEP_ALIVE */
|
||||
|
||||
#ifdef IPV6_FILTERING
|
||||
/* IPV6 host addr info */
|
||||
struct ipv6_addr_info {
|
||||
u8 filter_mode;
|
||||
u8 address_mode;
|
||||
u16 ipv6[8];
|
||||
};
|
||||
#endif /*IPV6_FILTERING*/
|
||||
|
||||
/* interfaces for the drivers */
|
||||
int bes2600_core_probe(const struct sbus_ops *sbus_ops,
|
||||
struct sbus_priv *sbus,
|
||||
struct device *pdev,
|
||||
struct bes2600_common **pself);
|
||||
void bes2600_core_release(struct bes2600_common *self);
|
||||
|
||||
static inline void bes2600_tx_queues_lock(struct bes2600_common *hw_priv)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i)
|
||||
bes2600_queue_lock(&hw_priv->tx_queue[i]);
|
||||
}
|
||||
|
||||
static inline void bes2600_tx_queues_unlock(struct bes2600_common *hw_priv)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i)
|
||||
bes2600_queue_unlock(&hw_priv->tx_queue[i]);
|
||||
}
|
||||
|
||||
/* Datastructure for LLC-SNAP HDR */
|
||||
#define P80211_OUI_LEN 3
|
||||
|
||||
struct ieee80211_snap_hdr {
|
||||
u8 dsap; /* always 0xAA */
|
||||
u8 ssap; /* always 0xAA */
|
||||
u8 ctrl; /* always 0x03 */
|
||||
u8 oui[P80211_OUI_LEN]; /* organizational universal id */
|
||||
} __packed;
|
||||
|
||||
#define bes2600_for_each_vif(_hw_priv, _priv, _i) \
|
||||
for ( \
|
||||
_i = 0; \
|
||||
(_i < CW12XX_MAX_VIFS) && \
|
||||
(_priv = hw_priv->vif_list[_i] ? \
|
||||
cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[_i]) : NULL); \
|
||||
_i++ \
|
||||
)
|
||||
|
||||
#ifdef CONFIG_BES2600_BT
|
||||
int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv);
|
||||
void bes2600_btusb_uninit(struct usb_interface *interface);
|
||||
#endif
|
||||
|
||||
#endif /* BES2600_H */
|
||||
@@ -0,0 +1,999 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/version.h>
|
||||
#include "bes2600_factory.h"
|
||||
#include "bes_chardev.h"
|
||||
#include "bes_log.h"
|
||||
|
||||
#define LE_CPU_TRANS(val, cvt) (val = cvt(val))
|
||||
|
||||
#define TRANS_LE16(val) LE_CPU_TRANS(val, __cpu_to_le16)
|
||||
#define TRANS_LE32(val) LE_CPU_TRANS(val, __cpu_to_le32)
|
||||
|
||||
#define TRANS_CPU16(val) LE_CPU_TRANS(val, __le16_to_cpu)
|
||||
#define TRANS_CPU32(val) LE_CPU_TRANS(val, __le32_to_cpu)
|
||||
|
||||
static DEFINE_MUTEX(factory_lock);
|
||||
|
||||
/*
|
||||
* It is only used for temporary storage.
|
||||
* Every time get the factory, it will read from the
|
||||
* file and overwrite the original value.
|
||||
*/
|
||||
static struct factory_t factory_cali_data;
|
||||
static struct factory_t *factory_p = NULL;
|
||||
|
||||
void bes2600_factory_lock(void)
|
||||
{
|
||||
mutex_lock(&factory_lock);
|
||||
}
|
||||
|
||||
void bes2600_factory_unlock(void)
|
||||
{
|
||||
mutex_unlock(&factory_lock);
|
||||
}
|
||||
|
||||
u8* bes2600_factory_get_file_buffer(void)
|
||||
{
|
||||
u8 *file_buffer = NULL;
|
||||
|
||||
file_buffer = kmalloc(FACTORY_MAX_SIZE, GFP_KERNEL);
|
||||
if (!file_buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return file_buffer;
|
||||
}
|
||||
|
||||
void bes2600_factory_free_file_buffer(u8 *file_buffer)
|
||||
{
|
||||
if (file_buffer)
|
||||
kfree(file_buffer);
|
||||
}
|
||||
|
||||
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p);
|
||||
|
||||
static inline uint32_t factory_crc32(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
u32 crc_le = 0;
|
||||
crc_le = crc32_le(0xffffffffL, (uint8_t *)data, len);
|
||||
crc_le ^= 0xffffffffL;
|
||||
return crc_le;
|
||||
}
|
||||
|
||||
static int bes2600_factory_head_info_check(struct factory_t *factory_data)
|
||||
{
|
||||
if (!factory_data) {
|
||||
bes_err("%s NULL pointer err\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (factory_data->head.magic != NVREC_DEV_MAGIC) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if ((factory_data->head.version < NVREC_MINI_VERSION) ||
|
||||
(factory_data->head.version > NVREC_CURRENT_VERSION)) {
|
||||
bes_err("factory version error:%d", factory_data->head.version);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bes2600_factory_crc_check(struct factory_t *factory_data)
|
||||
{
|
||||
#ifdef FACTORY_CRC_CHECK
|
||||
u32 cal_crc = 0;
|
||||
u32 crc_len = sizeof(factory_data_t);
|
||||
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
||||
crc_len = (crc_len - sizeof(u16) + 3) & (~0x3);
|
||||
#endif
|
||||
|
||||
if (!factory_data) {
|
||||
bes_err("%s NULL pointer err \n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cal_crc = factory_crc32((uint8_t *)(&(factory_data->data)), crc_len);
|
||||
if (factory_data->head.crc != cal_crc) {
|
||||
bes_err(ES2600_DBG_CHARDEV,
|
||||
"bes2600 factory check failed, calc_crc:0x%08x factory_crc: 0x%08x\n",
|
||||
cal_crc, factory_data->head.crc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* factory_section_read_file - Read data of specified length from file
|
||||
* @path: path of the file
|
||||
* @buffer: storage of read data
|
||||
*
|
||||
* The maximum file length allowed is 600 bytes.
|
||||
* This function does not do crc verification to take into
|
||||
* account different storage requirements.
|
||||
*
|
||||
* Return: length on success, negative error code otherwise.
|
||||
*/
|
||||
static int factory_section_read_file(char *path, void *buffer)
|
||||
{
|
||||
int ret = 0;
|
||||
struct file *fp;
|
||||
|
||||
if (!path || !buffer) {
|
||||
bes_err("%s NULL pointer err\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bes_devel("reading %s \n", path);
|
||||
|
||||
fp = filp_open(path, O_RDONLY, 0); //S_IRUSR
|
||||
if (IS_ERR(fp)) {
|
||||
bes_devel("BES2600 : can't open %s\n",path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fp->f_inode->i_size <= 0 || fp->f_inode->i_size > FACTORY_MAX_SIZE) {
|
||||
bes_err( "bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n",
|
||||
fp->f_inode->i_size, FACTORY_MAX_SIZE);
|
||||
filp_close(fp, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos);
|
||||
|
||||
filp_close(fp, NULL);
|
||||
|
||||
if (ret != fp->f_inode->i_size) {
|
||||
bes_err("bes2600_factory.txt read fail\n");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* factory_section_write_file - Write data of specified length to file
|
||||
* @path: path of the file
|
||||
* @buffer: storage of write data
|
||||
* @size: length of data to write
|
||||
*
|
||||
* Return: length on success, negative error code otherwise.
|
||||
*/
|
||||
static int factory_section_write_file(char *path, void *buffer, int size)
|
||||
{
|
||||
int ret = 0;
|
||||
struct file *fp;
|
||||
|
||||
bes_devel("writing %s \n", path);
|
||||
|
||||
fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||
if (IS_ERR(fp)) {
|
||||
bes_devel("BES2600 : can't open %s\n",path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||
|
||||
filp_close(fp,NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!source_buf || !factory) {
|
||||
bes_err("%s NULL pointer err\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sscanf(source_buf, STANDARD_FACTORY,\
|
||||
&factory->head.magic,\
|
||||
&factory->head.version,\
|
||||
&factory->head.crc,\
|
||||
&factory->data.iQ_offset,\
|
||||
&factory->data.freq_cal,\
|
||||
&factory->data.freq_cal_flags,\
|
||||
&factory->data.tx_power_ch[0],\
|
||||
&factory->data.tx_power_ch[1],\
|
||||
&factory->data.tx_power_ch[2],\
|
||||
&factory->data.tx_power_type,\
|
||||
&factory->data.temperature,\
|
||||
&factory->data.tx_power_ch_5G[0],\
|
||||
&factory->data.tx_power_ch_5G[1],\
|
||||
&factory->data.tx_power_ch_5G[2],\
|
||||
&factory->data.tx_power_ch_5G[3],\
|
||||
&factory->data.tx_power_ch_5G[4],\
|
||||
&factory->data.tx_power_ch_5G[5],\
|
||||
&factory->data.tx_power_ch_5G[6],\
|
||||
&factory->data.tx_power_ch_5G[7],\
|
||||
&factory->data.tx_power_ch_5G[8],\
|
||||
&factory->data.tx_power_ch_5G[9],\
|
||||
&factory->data.tx_power_ch_5G[10],\
|
||||
&factory->data.tx_power_ch_5G[11],\
|
||||
&factory->data.tx_power_ch_5G[12],\
|
||||
&factory->data.tx_power_flags_5G,\
|
||||
&factory->data.temperature_5G,\
|
||||
&factory->data.bt_tx_power[0],\
|
||||
&factory->data.bt_tx_power[1],\
|
||||
&factory->data.bt_tx_power[2],\
|
||||
&factory->data.bt_tx_power[3]
|
||||
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
||||
,&factory->data.select_efuse);
|
||||
#else
|
||||
);
|
||||
#endif
|
||||
|
||||
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
||||
factory->data.select_efuse = 0;
|
||||
#endif
|
||||
|
||||
if (ret != FACTORY_MEMBER_NUM)
|
||||
{
|
||||
bes_err("bes2600_factory.txt parse fail\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int factory_section_read_and_check_file(u8 *file_buf, char* path)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!file_buf || !path) {
|
||||
bes_err("%s NULL pointer err\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = factory_section_read_file(path, file_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
memset(&factory_cali_data, 0, sizeof(struct factory_t));
|
||||
ret = factory_parse(file_buf, &factory_cali_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = bes2600_factory_head_info_check(&factory_cali_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = bes2600_factory_crc_check(&factory_cali_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
factory_p = &factory_cali_data;
|
||||
|
||||
bes_devel("open wifi factory section success");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void factory_section_wifi_tx_power_check(struct factory_t *factory_data)
|
||||
{
|
||||
int i;
|
||||
bool inval_v = false;
|
||||
|
||||
if (!factory_data)
|
||||
return ;
|
||||
|
||||
/* only check cali channel, 11n ch1, ch7, ch13 */
|
||||
for (i = 0; i < ARRAY_SIZE(factory_data->data.tx_power_ch); ++i) {
|
||||
if (factory_data->data.tx_power_ch[i] == 0x0 ||
|
||||
factory_data->data.tx_power_ch[i] > 0x3fff) {
|
||||
inval_v = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inval_v)
|
||||
bes_warn("tx_power_ch_2g cali, inval calibration value\n");
|
||||
print_hex_dump(KERN_DEBUG,
|
||||
"tx_power_ch_2g dump", DUMP_PREFIX_NONE, 16, 1,
|
||||
factory_data->data.tx_power_ch, sizeof(factory_data->data.tx_power_ch), false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void factory_section_wifi_tx_power_5G_check(struct factory_t *factory_data)
|
||||
{
|
||||
int i;
|
||||
bool inval_v = false;
|
||||
|
||||
if (!factory_data)
|
||||
return ;
|
||||
|
||||
/* only check cali channel */
|
||||
for (i = 0; i < ARRAY_SIZE(factory_data->data.tx_power_ch_5G); ++i) {
|
||||
if (factory_data->data.tx_power_ch_5G[i] == 0x0 ||
|
||||
factory_data->data.tx_power_ch_5G[i] > 0x3fff) {
|
||||
inval_v = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inval_v)
|
||||
bes_warn("tx_power_ch_5g cali, inval calibration value\n");
|
||||
print_hex_dump(KERN_DEBUG,
|
||||
"tx_power_ch_5g dump", DUMP_PREFIX_NONE, 16, 1,
|
||||
factory_data->data.tx_power_ch, sizeof(factory_data->data.tx_power_ch), false);
|
||||
|
||||
}
|
||||
|
||||
static void factory_section_wifi_freq_cali_check(struct factory_t *factory_data)
|
||||
{
|
||||
if (!factory_data)
|
||||
return ;
|
||||
|
||||
if (factory_data->data.freq_cal == 0x0 ||
|
||||
factory_data->data.freq_cal > 0x1ff) {
|
||||
bes_warn("freq cali, inval calibration value\n");
|
||||
}
|
||||
|
||||
print_hex_dump(KERN_DEBUG,
|
||||
"wifi freq cali dump dump: ", DUMP_PREFIX_NONE, 16, 1,
|
||||
&factory_data->data.freq_cal, sizeof(factory_data->data.freq_cal), false);
|
||||
|
||||
}
|
||||
|
||||
static void factory_section_bt_tx_power_check(struct factory_t *factory_data)
|
||||
{
|
||||
int i;
|
||||
bool inval_v = false;
|
||||
|
||||
if (!factory_data)
|
||||
return ;
|
||||
|
||||
/* bt only check bdr & edr power, (bdr/edr: div, powerlevel) */
|
||||
for (i = 0; i < ARRAY_SIZE(factory_data->data.bt_tx_power) - 1; i += 2) {
|
||||
if (factory_data->data.bt_tx_power[i] != 0x05) {
|
||||
inval_v = true;
|
||||
}
|
||||
|
||||
if (factory_data->data.bt_tx_power[i + 1] == 0x0 ||
|
||||
factory_data->data.bt_tx_power[i + 1] > 0x20) {
|
||||
inval_v = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inval_v)
|
||||
bes_warn("bt tx power cali, inval calibration value\n");
|
||||
print_hex_dump(KERN_DEBUG,
|
||||
"bt tx power cali dump: ", DUMP_PREFIX_NONE, 16, 1,
|
||||
factory_data->data.bt_tx_power, sizeof(factory_data->data.bt_tx_power), false);
|
||||
|
||||
}
|
||||
|
||||
void factory_little_endian_cvrt(u8 *data)
|
||||
{
|
||||
int i;
|
||||
struct factory_t *trans_data = NULL;
|
||||
if (!data)
|
||||
return ;
|
||||
|
||||
trans_data = (struct factory_t *)data;
|
||||
|
||||
TRANS_LE16(trans_data->head.magic);
|
||||
TRANS_LE16(trans_data->head.version);
|
||||
TRANS_LE32(trans_data->head.crc);
|
||||
|
||||
TRANS_LE16(trans_data->data.freq_cal);
|
||||
TRANS_LE32(trans_data->data.iQ_offset);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trans_data->data.tx_power_ch); i++)
|
||||
TRANS_LE16(trans_data->data.tx_power_ch[i]);
|
||||
|
||||
TRANS_LE16(trans_data->data.temperature);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trans_data->data.bt_tx_power); i++)
|
||||
TRANS_LE32(trans_data->data.bt_tx_power[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trans_data->data.tx_power_ch_5G); i++)
|
||||
TRANS_LE16(trans_data->data.tx_power_ch_5G[i]);
|
||||
|
||||
TRANS_LE16(trans_data->data.tx_power_flags_5G);
|
||||
TRANS_LE16(trans_data->data.temperature_5G);
|
||||
TRANS_LE16(trans_data->data.select_efuse);
|
||||
|
||||
}
|
||||
|
||||
void bes2600_factory_data_check(u8* data)
|
||||
{
|
||||
struct factory_t *factory_data = NULL;
|
||||
|
||||
if (!data)
|
||||
return ;
|
||||
|
||||
factory_data = (struct factory_t *)data;
|
||||
|
||||
factory_section_wifi_tx_power_check(factory_data);
|
||||
factory_section_wifi_tx_power_5G_check(factory_data);
|
||||
factory_section_bt_tx_power_check(factory_data);
|
||||
factory_section_wifi_freq_cali_check(factory_data);
|
||||
|
||||
/* In order to support manual value change, recalculate crc before sending */
|
||||
factory_data->head.crc =
|
||||
factory_crc32((uint8_t *)(&(factory_data->data)), sizeof(factory_data_t));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* get factory data from file each time, and update factory_p.
|
||||
*/
|
||||
u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path)
|
||||
{
|
||||
u8 *ret_p = NULL;
|
||||
|
||||
if (!file_buffer || !path || !data_len) {
|
||||
bes_err("%s NULL pointer\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* reset factory_p */
|
||||
factory_p = NULL;
|
||||
|
||||
*data_len = sizeof(struct factory_t);
|
||||
if (factory_section_read_and_check_file(file_buffer, path) < 0) {
|
||||
bes_err("read and check %s error\n", path);
|
||||
*data_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!factory_p) {
|
||||
*data_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret_p = (u8 *)factory_p;
|
||||
|
||||
return ret_p;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the calibration file does not exist, a new file is automatically created when
|
||||
* the calibration value is written. After writing, update factory_p, if the update is successful,
|
||||
* it means the writing is successful, otherwise it fails. At the same time, subsequent calibration
|
||||
* values are saved on this basis to avoid duplicating file creation and flushing out previously saved values.
|
||||
*/
|
||||
static bool bes2600_factory_file_status_read(u8 *file_buffer)
|
||||
{
|
||||
u8 *factory_temp = NULL;
|
||||
uint32_t len;
|
||||
bool ret = true;
|
||||
|
||||
#ifdef FACTORY_SAVE_MULTI_PATH
|
||||
factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_PATH);
|
||||
if (!factory_temp) {
|
||||
bes_warn("get factory cali from first path fali\n");
|
||||
factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_DEFAULT_PATH);
|
||||
/* clear the flag of the file in the default path, and then create a new file */
|
||||
if (factory_temp) {
|
||||
((struct factory_t *)factory_temp)->data.tx_power_type = 0xff;
|
||||
((struct factory_t *)factory_temp)->data.freq_cal_flags = 0;
|
||||
((struct factory_t *)factory_temp)->data.tx_power_flags_5G = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_PATH);
|
||||
#endif
|
||||
if (!factory_temp) {
|
||||
bes_warn("get factory data fali, check whether the file exists\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* create a new factory.txt file, and set default value */
|
||||
static int bes2600_factory_cali_file_hdr_fill(struct factory_t **factory_head)
|
||||
{
|
||||
u16 tx_power_type = 0xff;
|
||||
int i;
|
||||
|
||||
if (!factory_head)
|
||||
return -1;
|
||||
|
||||
*factory_head = &factory_cali_data;
|
||||
memset(*factory_head, 0, sizeof(struct factory_t));
|
||||
(*factory_head)->data.tx_power_type = tx_power_type;
|
||||
(*factory_head)->head.magic = 0xba80;
|
||||
(*factory_head)->head.version = 2;
|
||||
|
||||
(*factory_head)->data.freq_cal = 0xa0;
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
(*factory_head)->data.tx_power_ch[i] = 0x1400;
|
||||
}
|
||||
|
||||
for (i = 0; i < 13; ++i) {
|
||||
(*factory_head)->data.tx_power_ch_5G[i] = 0x1400;
|
||||
}
|
||||
|
||||
(*factory_head)->data.bt_tx_power[0] = 0x05;
|
||||
(*factory_head)->data.bt_tx_power[1] = 0x10;
|
||||
(*factory_head)->data.bt_tx_power[2] = 0x05;
|
||||
(*factory_head)->data.bt_tx_power[3] = 0x15;
|
||||
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
||||
(*factory_head)->data.select_efuse = 0;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int16_t bes2600_wifi_power_cali_table_write(struct wifi_power_cali_save_t *data_cali)
|
||||
{
|
||||
u16 mode, band, ch, power_cali, bandwidth;
|
||||
int power_index = 0;
|
||||
struct factory_t *factory_power_p = NULL;
|
||||
u8 *file_buffer = NULL;
|
||||
int16_t ret = 0;
|
||||
|
||||
if (!data_cali) {
|
||||
bes_warn("%s: power cali save pointer is NULL\n", __func__);
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
}
|
||||
|
||||
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
|
||||
bes2600_factory_lock();
|
||||
|
||||
/**
|
||||
* When it returns true, it means that the factory file has been read.
|
||||
* When it returns false, it means that the factory file does not exist,
|
||||
* or the operation of reading the text file fails. At this time, a new factory file will be created.
|
||||
*/
|
||||
if (bes2600_factory_file_status_read(file_buffer)) {
|
||||
factory_power_p = factory_p;
|
||||
} else {
|
||||
if (bes2600_factory_cali_file_hdr_fill(&factory_power_p)) {
|
||||
bes_warn("%s, create bes2600_factory.txt fail.", __func__);
|
||||
ret = -FACTORY_FACTORY_TXT_CREATE_FAIL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
mode = data_cali->mode;
|
||||
bandwidth = data_cali->bandwidth;
|
||||
band = data_cali->band;
|
||||
ch = data_cali->ch;
|
||||
power_cali = data_cali->power_cali;
|
||||
|
||||
bes_devel("%s: mode = %u, bandwidth = %u,, band = %u, ch = %u, power_cali = 0x%04x\n",
|
||||
__func__, mode, bandwidth, band, ch, power_cali);
|
||||
|
||||
/* only in 802.11n 20M msc7 mode, the power calibration value is saved */
|
||||
if (bandwidth != 0 || mode != WIFI_RF_11N_MODE) {
|
||||
ret = -FACTORY_SAVE_MODE_ERR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* powerlevel value range: 0 ~ 0x3fff */
|
||||
if (power_cali == 0 || power_cali > 0x3fff) {
|
||||
ret = -FACTORY_SAVE_POWER_ERR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
if (band == BAND_2G4) {
|
||||
switch (ch) {
|
||||
case 1:
|
||||
power_index = 0;
|
||||
break;
|
||||
case 7:
|
||||
power_index = 1;
|
||||
break;
|
||||
case 13:
|
||||
power_index = 2;
|
||||
break;
|
||||
default:
|
||||
ret = -FACTORY_SAVE_CH_ERR;
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
factory_power_p->data.tx_power_ch[power_index] = power_cali;
|
||||
} else if (band == BAND_5G) {
|
||||
switch (ch) {
|
||||
case 36:
|
||||
case 38:
|
||||
case 40:
|
||||
power_index = 0;
|
||||
break;
|
||||
case 44:
|
||||
case 46:
|
||||
case 48:
|
||||
power_index = 1;
|
||||
break;
|
||||
case 52:
|
||||
case 54:
|
||||
case 56:
|
||||
power_index = 2;
|
||||
break;
|
||||
case 60:
|
||||
case 62:
|
||||
case 64:
|
||||
power_index = 3;
|
||||
break;
|
||||
case 100:
|
||||
case 102:
|
||||
case 104:
|
||||
power_index = 4;
|
||||
break;
|
||||
case 108:
|
||||
case 110:
|
||||
case 112:
|
||||
power_index = 5;
|
||||
break;
|
||||
case 116:
|
||||
case 114:
|
||||
case 120:
|
||||
power_index = 6;
|
||||
break;
|
||||
case 124:
|
||||
case 126:
|
||||
case 128:
|
||||
power_index = 7;
|
||||
break;
|
||||
case 132:
|
||||
case 134:
|
||||
case 136:
|
||||
power_index = 8;
|
||||
break;
|
||||
case 140:
|
||||
case 142:
|
||||
case 144:
|
||||
power_index = 9;
|
||||
break;
|
||||
case 149:
|
||||
case 151:
|
||||
case 153:
|
||||
power_index = 10;
|
||||
break;
|
||||
case 157:
|
||||
case 159:
|
||||
case 161:
|
||||
power_index = 11;
|
||||
break;
|
||||
case 165:
|
||||
case 169:
|
||||
power_index = 12;
|
||||
break;
|
||||
default:
|
||||
ret = -FACTORY_SAVE_CH_ERR;
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
factory_power_p->data.tx_power_ch_5G[power_index] = power_cali;
|
||||
}
|
||||
|
||||
/* save to file */
|
||||
if (bes2600_wifi_cali_table_save(file_buffer, factory_power_p)) {
|
||||
ret = -FACTORY_SAVE_WRITE_ERR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
bes2600_factory_free_file_buffer(file_buffer);
|
||||
bes2600_factory_unlock();
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int16_t bes2600_wifi_cali_freq_write(struct wifi_freq_cali_t *data_cali)
|
||||
{
|
||||
u16 freq_cali;
|
||||
struct factory_t *factory_freq_p = NULL;
|
||||
u8 *file_buffer = NULL;
|
||||
int16_t ret = 0;
|
||||
|
||||
if (!data_cali) {
|
||||
bes_warn("%s: freq cali save pointer is NULL\n", __func__);
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
}
|
||||
|
||||
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
|
||||
bes2600_factory_lock();
|
||||
|
||||
/**
|
||||
* When it returns true, it means that the factory file has been read.
|
||||
* When it returns false, it means that the factory file does not exist,
|
||||
* or the operation of reading the text file fails. At this time, a new factory file will be created.
|
||||
*/
|
||||
if (bes2600_factory_file_status_read(file_buffer)) {
|
||||
factory_freq_p = factory_p;
|
||||
} else {
|
||||
if (bes2600_factory_cali_file_hdr_fill(&factory_freq_p)) {
|
||||
bes_warn("%s, create bes2600_factory.txt fail.", __func__);
|
||||
ret = -FACTORY_FACTORY_TXT_CREATE_FAIL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
freq_cali = data_cali->freq_cali;
|
||||
data_cali->cali_flag = 1;
|
||||
|
||||
/* freqOffset value range: 0 ~ 0x1ff */
|
||||
if (freq_cali == 0 || freq_cali > 0x1ff) {
|
||||
ret = -FACTORY_SAVE_FREQ_ERR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
factory_freq_p->data.freq_cal = freq_cali;
|
||||
factory_freq_p->data.freq_cal_flags = (u8)(data_cali->cali_flag);
|
||||
bes_devel("%s: freq_cali = 0x%04x\n", __func__, data_cali->freq_cali);
|
||||
|
||||
/* save to file */
|
||||
if (bes2600_wifi_cali_table_save(file_buffer, factory_freq_p)) {
|
||||
ret = -FACTORY_SAVE_WRITE_ERR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
bes2600_factory_free_file_buffer(file_buffer);
|
||||
bes2600_factory_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
||||
int16_t bes2600_select_efuse_flag_write(uint16_t select_efuse_flag)
|
||||
{
|
||||
struct factory_t *factory_flag_p = NULL;
|
||||
u8 *file_buffer = NULL;
|
||||
int16_t ret = 0;
|
||||
|
||||
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
|
||||
bes2600_factory_lock();
|
||||
|
||||
/**
|
||||
* When it returns true, it means that the factory file has been read.
|
||||
* When it returns false, it means that the factory file does not exist,
|
||||
* or the operation of reading the text file fails. At this time, a new factory file will be created.
|
||||
*/
|
||||
if (bes2600_factory_file_status_read(file_buffer)) {
|
||||
factory_flag_p = factory_p;
|
||||
} else {
|
||||
if (bes2600_factory_cali_file_hdr_fill(&factory_flag_p)) {
|
||||
bes_warn("%s, create bes2600_factory.txt fail.", __func__);
|
||||
ret = -FACTORY_FACTORY_TXT_CREATE_FAIL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
factory_flag_p->data.select_efuse = select_efuse_flag;
|
||||
bes_devel("%s: select_efuse = %x\n", __func__, select_efuse_flag);
|
||||
|
||||
/* save to file */
|
||||
if (bes2600_wifi_cali_table_save(file_buffer, factory_flag_p)) {
|
||||
ret = -FACTORY_SAVE_WRITE_ERR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
bes2600_factory_free_file_buffer(file_buffer);
|
||||
bes2600_factory_unlock();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int16_t vendor_set_power_cali_flag(struct wifi_power_cali_flag_t *cali_flag)
|
||||
{
|
||||
struct factory_t *factory_power_flag_p = NULL;
|
||||
u8 *file_buffer = NULL;
|
||||
u16 calied_flag_5g = 1;
|
||||
u16 calied_flag_2g = 0;
|
||||
int16_t ret = 0;
|
||||
|
||||
if (!cali_flag) {
|
||||
bes_warn("%s: power cali flag save pointer is NULL\n", __func__);
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
}
|
||||
|
||||
if (cali_flag->band != BAND_2G4 && cali_flag->band != BAND_5G) {
|
||||
bes_warn("%s: power cali flag save band err\n", __func__);
|
||||
return -FACTORY_SET_POWER_CALI_FLAG_ERR;
|
||||
}
|
||||
|
||||
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
|
||||
bes2600_factory_lock();
|
||||
|
||||
if (bes2600_factory_file_status_read(file_buffer)) {
|
||||
factory_power_flag_p = factory_p;
|
||||
} else {
|
||||
bes_warn("%s: factory cali data is not exist\n", __func__);
|
||||
ret = -FACTORY_SAVE_FILE_NOT_EXIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (cali_flag->band == BAND_2G4) {
|
||||
factory_power_flag_p->data.tx_power_type = calied_flag_2g;
|
||||
} else {
|
||||
factory_power_flag_p->data.tx_power_flags_5G = calied_flag_5g;
|
||||
}
|
||||
|
||||
/* save to file */
|
||||
if (bes2600_wifi_cali_table_save(file_buffer, factory_power_flag_p)) {
|
||||
ret = -FACTORY_SET_POWER_CALI_FLAG_ERR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
bes2600_factory_free_file_buffer(file_buffer);
|
||||
bes2600_factory_unlock();
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
|
||||
{
|
||||
return snprintf(dest_buf, FACTORY_MAX_SIZE, STANDARD_FACTORY,\
|
||||
factory->head.magic,\
|
||||
factory->head.version,\
|
||||
factory->head.crc,\
|
||||
factory->data.iQ_offset,\
|
||||
factory->data.freq_cal,\
|
||||
factory->data.freq_cal_flags,\
|
||||
factory->data.tx_power_ch[0],\
|
||||
factory->data.tx_power_ch[1],\
|
||||
factory->data.tx_power_ch[2],\
|
||||
factory->data.tx_power_type,\
|
||||
factory->data.temperature,\
|
||||
factory->data.tx_power_ch_5G[0],\
|
||||
factory->data.tx_power_ch_5G[1],\
|
||||
factory->data.tx_power_ch_5G[2],\
|
||||
factory->data.tx_power_ch_5G[3],\
|
||||
factory->data.tx_power_ch_5G[4],\
|
||||
factory->data.tx_power_ch_5G[5],\
|
||||
factory->data.tx_power_ch_5G[6],\
|
||||
factory->data.tx_power_ch_5G[7],\
|
||||
factory->data.tx_power_ch_5G[8],\
|
||||
factory->data.tx_power_ch_5G[9],\
|
||||
factory->data.tx_power_ch_5G[10],\
|
||||
factory->data.tx_power_ch_5G[11],\
|
||||
factory->data.tx_power_ch_5G[12],\
|
||||
factory->data.tx_power_flags_5G,\
|
||||
factory->data.temperature_5G,\
|
||||
factory->data.bt_tx_power[0],\
|
||||
factory->data.bt_tx_power[1],\
|
||||
factory->data.bt_tx_power[2],\
|
||||
factory->data.bt_tx_power[3]
|
||||
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
||||
,factory->data.select_efuse);
|
||||
#else
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p)
|
||||
{
|
||||
int ret = 0;
|
||||
int w_size;
|
||||
u32 crc_len = sizeof(factory_data_t);
|
||||
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
||||
crc_len = (crc_len - sizeof(u16) + 3) & (~0x3);
|
||||
#endif
|
||||
|
||||
bes_devel("enter %s\n", __func__);
|
||||
|
||||
if (!file_buffer) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!factory_save_p) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* All initialized to space */
|
||||
memset(file_buffer, 32, FACTORY_MAX_SIZE);
|
||||
file_buffer[FACTORY_MAX_SIZE - 1] = '\n';
|
||||
|
||||
factory_save_p->head.crc =
|
||||
factory_crc32((uint8_t *)(&(factory_save_p->data)), crc_len);
|
||||
|
||||
w_size = factory_build(file_buffer, factory_save_p);
|
||||
|
||||
if (w_size < 0 || w_size > FACTORY_MAX_SIZE) {
|
||||
bes_err("%s: build failed! ret = %d.", __func__, ret);
|
||||
return -ETXTBSY;
|
||||
}
|
||||
|
||||
#ifdef FACTORY_SAVE_MULTI_PATH
|
||||
/* avoid trailing characters '\0' */
|
||||
file_buffer[w_size] = 32;
|
||||
ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE);
|
||||
#else
|
||||
ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size);
|
||||
#endif
|
||||
if(ret < 0) {
|
||||
bes_err("%s: write failed! ret = %d.", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t vendor_get_power_cali(struct wifi_get_power_cali_t *power_cali)
|
||||
{
|
||||
u8 *file_buffer = NULL;
|
||||
int16_t ret = 0;
|
||||
|
||||
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
|
||||
bes2600_factory_lock();
|
||||
|
||||
if (!power_cali) {
|
||||
ret = -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!bes2600_factory_file_status_read(file_buffer)) {
|
||||
ret = -FACTORY_SAVE_FILE_NOT_EXIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(power_cali->tx_power_ch, factory_p->data.tx_power_ch, sizeof(power_cali->tx_power_ch));
|
||||
memcpy(power_cali->tx_power_ch_5G, factory_p->data.tx_power_ch_5G, sizeof(power_cali->tx_power_ch_5G));
|
||||
|
||||
err:
|
||||
bes2600_factory_free_file_buffer(file_buffer);
|
||||
bes2600_factory_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int16_t vendor_get_freq_cali(struct wifi_freq_cali_t *vendor_freq)
|
||||
{
|
||||
u8 *file_buffer = NULL;
|
||||
int16_t ret = 0;
|
||||
|
||||
if (!vendor_freq)
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
|
||||
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
||||
return -FACTORY_GET_INPUT_NULL_POINTER;
|
||||
|
||||
bes2600_factory_lock();
|
||||
|
||||
if (!bes2600_factory_file_status_read(file_buffer)) {
|
||||
ret = -FACTORY_SAVE_FILE_NOT_EXIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
vendor_freq->status = 0;
|
||||
vendor_freq->freq_cali = factory_p->data.freq_cal;
|
||||
vendor_freq->cali_flag = factory_p->data.freq_cal_flags;
|
||||
|
||||
err:
|
||||
bes2600_factory_free_file_buffer(file_buffer);
|
||||
bes2600_factory_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __FACTORY_H__
|
||||
#define __FACTORY_H__
|
||||
|
||||
#include "bes2600.h"
|
||||
#include "wsm.h"
|
||||
|
||||
#define NVREC_MINI_VERSION 1
|
||||
#define NVREC_DEV_MAGIC 0xba80
|
||||
#define NVREC_CURRENT_VERSION 2
|
||||
|
||||
#define FACTORY_MAX_SIZE 600
|
||||
#define __STANDARD_FACTORY "##head\n\
|
||||
magic:0x%hx\n\
|
||||
version:0x%hx\n\
|
||||
crc:0x%x\n\
|
||||
##iq&xtal\n\
|
||||
iQ_offset:0x%x\n\
|
||||
freq_cal:0x%hx\n\
|
||||
freq_cal_flags:0x%hhx\n\
|
||||
##2.4g_power_11n\n\
|
||||
ch1:0x%hx\n\
|
||||
ch7:0x%hx\n\
|
||||
ch13:0x%hx\n\
|
||||
tx_power_type:0x%hhx\n\
|
||||
temperature:0x%hx\n\
|
||||
##5g_power_11n\n\
|
||||
ch36-40:0x%hx\n\
|
||||
ch44-48:0x%hx\n\
|
||||
ch52-56:0x%hx\n\
|
||||
ch60-64:0x%hx\n\
|
||||
ch100-104:0x%hx\n\
|
||||
ch108-112:0x%hx\n\
|
||||
ch116-120:0x%hx\n\
|
||||
ch124-128:0x%hx\n\
|
||||
ch132-136:0x%hx\n\
|
||||
ch140-144:0x%hx\n\
|
||||
ch149-153:0x%hx\n\
|
||||
ch157-161:0x%hx\n\
|
||||
ch165-169:0x%hx\n\
|
||||
tx_power_flags_5G:0x%hx\n\
|
||||
temperature_5G:0x%hx\n\
|
||||
##bt\n\
|
||||
bdr_div:0x%x\n\
|
||||
bdr_power:0x%x\n\
|
||||
edr_div:0x%x\n\
|
||||
edr_power:0x%x\n"
|
||||
|
||||
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
||||
#define STANDARD_FACTORY_EFUSE "##select_efuse_flag\nselect_efuse:%hx\n"
|
||||
#define FACTORY_MEMBER_NUM 31
|
||||
#else
|
||||
#define STANDARD_FACTORY_EFUSE
|
||||
#define FACTORY_MEMBER_NUM 30
|
||||
#endif
|
||||
|
||||
#define STANDARD_FACTORY __STANDARD_FACTORY STANDARD_FACTORY_EFUSE "%%%%\n"
|
||||
|
||||
typedef struct {
|
||||
uint16_t magic;
|
||||
uint16_t version;
|
||||
uint32_t crc;
|
||||
} factory_head_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t iQ_offset;
|
||||
uint16_t freq_cal;
|
||||
/**
|
||||
* index 0-2
|
||||
* 11n ch1, 11n ch7, 11n ch13
|
||||
*/
|
||||
uint16_t tx_power_ch[3];
|
||||
|
||||
/**
|
||||
* freq_cal_flags 0 - chip not calibrated
|
||||
* freq_cal_flags 1 - chip has been calibrated
|
||||
*/
|
||||
uint8_t freq_cal_flags;
|
||||
|
||||
/**
|
||||
* tx_power_type 0 - save bgn 1,7,13 power
|
||||
* tx_power_type 1 - save bgn 1-13 power
|
||||
* tx_power_type 0xff - not calibration
|
||||
*/
|
||||
uint8_t tx_power_type;
|
||||
uint16_t temperature;
|
||||
|
||||
/**
|
||||
* 11n
|
||||
* 0 36~40:1 44~48:2 52~56:3 60~64;
|
||||
* 4 100~104:5 108~112:6 116~120;
|
||||
* 7 124~128:8 132~136:9 140~144
|
||||
* 10 149~153; 11 157~161:12 165~169
|
||||
*/
|
||||
uint16_t tx_power_ch_5G[13];
|
||||
/**
|
||||
* 0- it means that power not calib
|
||||
* 1- it means that power have clibrated
|
||||
*/
|
||||
uint16_t tx_power_flags_5G;
|
||||
|
||||
|
||||
uint32_t bt_tx_power[4];
|
||||
/* The temperature after 5G clibrating. */
|
||||
uint16_t temperature_5G;
|
||||
uint16_t select_efuse;
|
||||
} factory_data_t;
|
||||
|
||||
struct factory_t {
|
||||
factory_head_t head;
|
||||
factory_data_t data;
|
||||
};
|
||||
|
||||
|
||||
enum band_type {
|
||||
BAND_2G4,
|
||||
BAND_5G,
|
||||
};
|
||||
|
||||
struct wifi_get_power_cali_t {
|
||||
uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */
|
||||
uint16_t tx_power_ch[3];
|
||||
uint16_t tx_power_ch_5G[13];
|
||||
int16_t status; /* 0: success, != 0: fial */
|
||||
};
|
||||
|
||||
struct wifi_power_cali_save_t {
|
||||
uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */
|
||||
uint16_t mode;
|
||||
uint16_t bandwidth;
|
||||
uint16_t band;
|
||||
uint16_t ch;
|
||||
uint16_t power_cali;
|
||||
int16_t status; /* 0: success, != 0: fial */
|
||||
};
|
||||
|
||||
struct wifi_freq_cali_t {
|
||||
uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */
|
||||
uint16_t freq_cali;
|
||||
int16_t status; /* 0: success, != 0: fial */
|
||||
uint16_t cali_flag;
|
||||
};
|
||||
|
||||
struct wifi_power_cali_flag_t {
|
||||
uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */
|
||||
uint16_t band;
|
||||
int16_t status; /* 0: success, != 0: fial */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* fatory cali data save type
|
||||
* @RF_CALIB_DATA_IN_LINUX - save to linux file
|
||||
* @RF_CALIB_DATA_IN_EFUSE - save to efuse
|
||||
* @RF_CALIB_DATA_IN_FLASH - save to flash
|
||||
* @RF_CALIB_DATA_TYPE_MAX - save type num
|
||||
*/
|
||||
enum RF_FACTORY_CALI_DATA_SAVE_TYPE {
|
||||
RF_CALIB_DATA_IN_LINUX = 0,
|
||||
RF_CALIB_DATA_IN_EFUSE,
|
||||
RF_CALIB_DATA_IN_FLASH,
|
||||
RF_CALIB_DATA_TYPE_MAX,
|
||||
};
|
||||
|
||||
/* fatory power & freq cali save status code */
|
||||
enum factory_cali_status {
|
||||
FACTORY_SAVE_SUCCESS = 0,
|
||||
FACTORY_SAVE_FILE_NOT_EXIST = 1,
|
||||
FACTORY_SAVE_MODE_ERR = 2,
|
||||
FACTORY_SAVE_CH_ERR = 3,
|
||||
FACTORY_SAVE_POWER_ERR = 4,
|
||||
FACTORY_SAVE_FREQ_ERR = 5,
|
||||
FACTORY_SAVE_EFUSE_CALIED = 6,
|
||||
FACTORY_SAVE_WRITE_ERR = 7,
|
||||
FACTORY_GET_CALIB_FROM_EFUSE_ERR = 8,
|
||||
FACTORY_GET_FREQ_FROM_EFUSE_ERR = 9,
|
||||
FACTORY_GET_POWER_FROM_EFUSE_ERR = 10,
|
||||
FACTORY_GET_POWER_FROM_FLASH_ERR = 11,
|
||||
FACTORY_GET_FREQ_FROM_FLASH_ERR = 12,
|
||||
FACTORY_SET_POWER_CALI_FLAG_ERR = 13,
|
||||
FACTORY_SET_FREQ_CALI_FLAG_ERR = 14,
|
||||
FACTORY_SAVE_READ_ERR = 15,
|
||||
FACTORY_GET_INPUT_NULL_POINTER = 16,
|
||||
FACTORY_FACTORY_TXT_CREATE_FAIL = 17,
|
||||
/* add new here, and numbered sequentially */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* just calibrate 11n, other protocols are automatically mapped */
|
||||
#define WIFI_RF_11N_MODE 0x15
|
||||
|
||||
/* read wifi & bt factory cali value*/
|
||||
u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path);
|
||||
void factory_little_endian_cvrt(u8 *data);
|
||||
void bes2600_factory_data_check(u8* data);
|
||||
void bes2600_factory_lock(void);
|
||||
void bes2600_factory_unlock(void);
|
||||
u8* bes2600_factory_get_file_buffer(void);
|
||||
void bes2600_factory_free_file_buffer(u8 *file_buffer);
|
||||
|
||||
/* read & write wifi cali value */
|
||||
int16_t bes2600_wifi_power_cali_table_write(struct wifi_power_cali_save_t *data_cali);
|
||||
int16_t bes2600_wifi_cali_freq_write(struct wifi_freq_cali_t *data_cali);
|
||||
int16_t vendor_get_freq_cali(struct wifi_freq_cali_t *vendor_freq);
|
||||
int16_t vendor_get_power_cali(struct wifi_get_power_cali_t *power_cali);
|
||||
int16_t vendor_set_power_cali_flag(struct wifi_power_cali_flag_t *cali_flag);
|
||||
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
||||
int16_t bes2600_select_efuse_flag_write(uint16_t select_efuse_flag);
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef BES2600_PLAT_H_INCLUDED
|
||||
#define BES2600_PLAT_H_INCLUDED
|
||||
|
||||
#include <linux/ioport.h>
|
||||
|
||||
struct bes2600_platform_data_sdio {
|
||||
u16 ref_clk; /* REQUIRED (in KHz) */
|
||||
|
||||
/* All others are optional */
|
||||
bool have_5ghz;
|
||||
bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */
|
||||
struct gpio_desc *reset; /* GPIO to RSTn signal (0 disables) */
|
||||
struct gpio_desc *powerup; /* GPIO to POWERUP signal (0 disables) */
|
||||
struct gpio_desc *wakeup; /* GPIO to WAKEUP signal (0 disables) */
|
||||
struct gpio_desc *host_wakeup; /* wifi GPIO to WAKEUP host signal (0 disables) */
|
||||
bool wlan_bt_hostwake_registered;/* wifi request_irq success or not */
|
||||
struct gpio_desc *gpio_irq; /* IRQ line or 0 to use SDIO IRQ */
|
||||
int (*power_ctrl)(const struct bes2600_platform_data_sdio *pdata,
|
||||
bool enable); /* Control 3v3 / 1v8 supply */
|
||||
int (*clk_ctrl)(const struct bes2600_platform_data_sdio *pdata,
|
||||
bool enable); /* Control CLK32K */
|
||||
const u8 *macaddr; /* if NULL, use bes2600_mac_template module parameter */
|
||||
const char *sdd_file; /* if NULL, will use default for detected hw type */
|
||||
bool wakeup_source; /* marks whether bes2600 is the wakeup souce or not */
|
||||
bool inited; /* platform data init flag */
|
||||
};
|
||||
|
||||
#endif /* BES2600_PLAT_H_INCLUDED */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __BES_CHARDEV_H__
|
||||
#define __BES_CHARDEV_H__
|
||||
|
||||
#define BES2600_FW_TYPE_WIFI_SIGNAL 0
|
||||
#define BES2600_FW_TYPE_WIFI_NO_SIGNAL 1
|
||||
#define BES2600_FW_TYPE_BT 2
|
||||
#define BES2600_FW_TYPE_MAX_NUM 3
|
||||
|
||||
#define DPD_VERSION_OFFSET 0x3AF4
|
||||
#define DPD_BIN_SIZE 0x3B14
|
||||
#define DPD_BIN_FILE_SIZE 0x4000
|
||||
#define DPD_CUR_VERSION 7
|
||||
|
||||
enum pend_read_op {
|
||||
BES_CDEV_READ_WAKEUP_STATE = 0,
|
||||
/* add new here */
|
||||
|
||||
BES_CDEV_READ_NUM_MAX,
|
||||
};
|
||||
|
||||
enum wifi_wakeup_reason_code {
|
||||
WAKEUP_REASON_WIFI_DEAUTH_DISASSOC = 0x1000,
|
||||
WAKEUP_REASON_WIFI_BSSLOST,
|
||||
/* add new here */
|
||||
};
|
||||
|
||||
enum bt_wakeup_reason_code {
|
||||
WAKEUP_REASON_BT_PLAY = 0x0100,
|
||||
/* add new here */
|
||||
};
|
||||
|
||||
enum wakeup_event {
|
||||
WAKEUP_EVENT_NONE = 0,
|
||||
WAKEUP_EVENT_SETTING,
|
||||
WAKEUP_EVENT_WSME,
|
||||
WAKEUP_EVENT_PEER_DETACH,
|
||||
/* add new here */
|
||||
};
|
||||
|
||||
/* dpd management */
|
||||
u8* bes2600_chrdev_get_dpd_buffer(u32 size);
|
||||
int bes2600_chrdev_update_dpd_data(void);
|
||||
const u8* bes2600_chrdev_get_dpd_data(u32 *len);
|
||||
void bes2600_chrdev_free_dpd_data(void);
|
||||
|
||||
/* get/set subs_priv instance from/to bes_chrdev module */
|
||||
void bes2600_chrdev_set_sbus_priv_data(struct sbus_priv *priv, bool error);
|
||||
struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
|
||||
|
||||
/* used to control device power down */
|
||||
int bes2600_chrdev_check_system_close(void);
|
||||
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||
void bes2600_chrdev_wakeup_bt(void);
|
||||
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
|
||||
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv);
|
||||
|
||||
/* get and set internal state */
|
||||
bool bes2600_chrdev_is_wifi_opened(void);
|
||||
bool bes2600_chrdev_is_bt_opened(void);
|
||||
int bes2600_chrdev_get_fw_type(void);
|
||||
bool bes2600_chrdev_is_signal_mode(void);
|
||||
void bes2600_chrdev_update_signal_mode(void);
|
||||
bool bes2600_chrdev_is_bus_error(void);
|
||||
|
||||
/* bus probe check */
|
||||
void bes2600_chrdev_start_bus_probe(void);
|
||||
void bes2600_chrdev_bus_probe_notify(void);
|
||||
|
||||
/* set wifi wakeup state */
|
||||
void bes2600_chrdev_wifi_update_wakeup_reason(u16 reason, u16 port);
|
||||
void bes2600_chrdev_wakeup_by_event_set(enum wakeup_event wakeup_event);
|
||||
int bes2600_chrdev_wakeup_by_event_get(void);
|
||||
|
||||
/* init and deinit module */
|
||||
int bes2600_chrdev_init(struct sbus_ops *ops);
|
||||
void bes2600_chrdev_free(void);
|
||||
|
||||
#ifdef BES2600_DUMP_FW_DPD_LOG
|
||||
void bes2600_free_dpd_log_buffer(void);
|
||||
u8* bes2600_alloc_dpd_log_buffer(u16 len);
|
||||
void bes2600_get_dpd_log(char **data, size_t *len);
|
||||
#endif
|
||||
|
||||
int bes2600_switch_bt(bool);
|
||||
|
||||
#endif /* __BES_CHARDEV_H__ */
|
||||
+1117
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include "bes_fw_common.h"
|
||||
#include "bes_log.h"
|
||||
|
||||
//#define BES_CRC32_DOUBLE_CHECK
|
||||
#ifdef BES_CRC32_DOUBLE_CHECK
|
||||
extern u32 bes_crc32_c(u32 crc, const u8 *data, u32 data_len);
|
||||
#endif
|
||||
|
||||
void bes_parse_fw_info(const u8 *data, u32 data_len, u32 *load_addr, u32 *crc32)
|
||||
{
|
||||
u8 buffer[16];
|
||||
struct exec_struct_t exec_struct;
|
||||
u32 exec_addr_last4byte;
|
||||
u32 crc_le = 0;
|
||||
#ifdef BES_CRC32_DOUBLE_CHECK
|
||||
u32 crc_bes = 0;
|
||||
u32 crc_be = 0;
|
||||
#endif
|
||||
crc_le = crc32_le(0xffffffffL, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE);
|
||||
crc_le ^= 0xffffffffL;
|
||||
|
||||
#ifdef BES_CRC32_DOUBLE_CHECK
|
||||
crc_be = crc32_be(0xffffffffL, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE);
|
||||
crc_be ^= 0xffffffffL;
|
||||
crc_bes = bes_crc32_c(crc_bes, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE);
|
||||
#endif
|
||||
|
||||
//read entry,param,sp,exec_addr
|
||||
memcpy((u8 *)buffer, (u8 *)data, sizeof(exec_struct));
|
||||
exec_struct.entry = ((struct exec_struct_t *)buffer)->entry;//PC
|
||||
exec_struct.param = ((struct exec_struct_t *)buffer)->param;
|
||||
exec_struct.sp = ((struct exec_struct_t *)buffer)->sp;
|
||||
exec_struct.exec_addr = ((struct exec_struct_t *)buffer)->exec_addr;//load addr
|
||||
|
||||
|
||||
#ifdef BES_CRC32_DOUBLE_CHECK
|
||||
bes_devel("crc32 %x(le) %x(be) %x(bes)\n", crc_le, crc_be, crc_bes);
|
||||
#else
|
||||
bes_devel("crc32 :0x%08X\n", crc_le);
|
||||
#endif
|
||||
bes_devel("exec_struct.entry :0x%08X\n", exec_struct.entry);
|
||||
bes_devel("exec_struct.param :0x%08X\n", exec_struct.param);
|
||||
bes_devel("exec_struct.sp :0x%08X\n", exec_struct.sp);
|
||||
bes_devel("exec_struct.exec_addr:0x%08X\n", exec_struct.exec_addr);
|
||||
|
||||
exec_addr_last4byte = (*((u32 *)(data + data_len - 4)));
|
||||
bes_devel("exec_addr_last4byte :0x%08X\n", exec_addr_last4byte);
|
||||
if ((!exec_struct.exec_addr) || (exec_struct.exec_addr != exec_addr_last4byte && exec_addr_last4byte)) {
|
||||
exec_struct.exec_addr = exec_addr_last4byte;
|
||||
bes_devel("exec_addr_last4byte covered exec_struct.exec_addr\n");
|
||||
}
|
||||
bes_devel("final exec_struct.exec_addr:0x%08X\n", exec_struct.exec_addr);
|
||||
|
||||
*load_addr = exec_struct.exec_addr;
|
||||
|
||||
#ifndef BES_CRC32_DOUBLE_CHECK
|
||||
*crc32 = crc_le;
|
||||
#else
|
||||
*crc32 = crc_bes;
|
||||
#endif
|
||||
}
|
||||
|
||||
int bes_frame_rsp_check(void *rsp, u8 frame_num)
|
||||
{
|
||||
int ret = 0;
|
||||
struct frame_struct_t *pframe = (struct frame_struct_t *)rsp;
|
||||
if (pframe->type == FRAME_HEADER_REPLY) {
|
||||
if (pframe->frame_num == frame_num) {
|
||||
if (pframe->len == 4) {
|
||||
if (pframe->payload == ERR_NONE) {
|
||||
bes_devel("bes slave download firmware is ready\n");
|
||||
} else {
|
||||
bes_err("frame payload=0x%x\n", pframe->payload);
|
||||
ret = -200;
|
||||
}
|
||||
} else {
|
||||
bes_err("payload len error:%u\n", pframe->len);
|
||||
ret = -201;
|
||||
}
|
||||
} else {
|
||||
bes_err("frame num err. 0x%x != 0x%x. len:%u\n",
|
||||
pframe->frame_num, frame_num, pframe->len);
|
||||
ret = -202;
|
||||
}
|
||||
} else {
|
||||
bes_err("frame type err. type 0x%x num=0x%x(0x%x), len:%u\n",
|
||||
pframe->type, pframe->frame_num, frame_num, pframe->len);
|
||||
ret = -203;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const u8* bes2600_get_firmware_version_info(const u8 *data, u32 count)
|
||||
{
|
||||
int i = 0;
|
||||
const u8 *tmp_ptr = NULL;
|
||||
const char month[12][4] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
if(!data || count < 4)
|
||||
return NULL;
|
||||
|
||||
for(tmp_ptr = data + count - 3; tmp_ptr > data; tmp_ptr -= 1) {
|
||||
for(i = 0; i < 12; i++) {
|
||||
if(memcmp(tmp_ptr, month[i], 3) == 0) {
|
||||
return tmp_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __BES_FW_COMMON_H__
|
||||
#define __BES_FW_COMMON_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
#include "bes2600.h"
|
||||
|
||||
/***** firmware macro *****/
|
||||
#define BUF_SIZE 49152
|
||||
#define RETRY_CNT_MAX 3
|
||||
#define TIMEOUT_TIME 20
|
||||
#define FRAME_HEADER_SIZE 0x04
|
||||
#define CODE_DATA_USELESS_SIZE 0x04
|
||||
/****frame header code****/
|
||||
#define FRAME_HEADER_REPLY 0xB0
|
||||
#define FRAME_HEADER_DOWNLOAD_INFO 0xB1
|
||||
#define FRAME_HEADER_DOWNLOAD_DATA 0xB2
|
||||
#define FRAME_HEADER_DOWNLOAD_END 0xB3
|
||||
#define FRAME_HEADER_RUN_CODE 0xB4
|
||||
|
||||
/****frame length get****/
|
||||
#define BES_FW_MSG_TOTAL_LEN(msg) (sizeof(struct fw_msg_hdr_t) + ((struct fw_msg_hdr_t )(msg)).len)
|
||||
|
||||
/****frame length get****/
|
||||
#define BES2600_DPD_ADDR 0x2008C000
|
||||
#define BES2600_FACTORY_ADDR 0x2008B000
|
||||
|
||||
/***** bes fw error code *****/
|
||||
enum ERR_CODE {
|
||||
ERR_NONE = 0x00,
|
||||
ERR_LEN = 0x01,
|
||||
};
|
||||
|
||||
/***** data struct *****/
|
||||
struct frame_struct_t {
|
||||
u8 type;
|
||||
u8 frame_num;
|
||||
u16 len;
|
||||
u32 payload;
|
||||
};
|
||||
|
||||
struct fw_msg_hdr_t {
|
||||
u8 type;
|
||||
u8 seq;
|
||||
u16 len;
|
||||
};
|
||||
|
||||
struct fw_msg_replay_t {
|
||||
u32 replay;
|
||||
};
|
||||
|
||||
struct fw_info_t {
|
||||
u32 len;
|
||||
u32 addr;
|
||||
};
|
||||
|
||||
struct download_fw_t {
|
||||
u32 addr;
|
||||
u8 data[0];
|
||||
};
|
||||
|
||||
struct fw_crc_t {
|
||||
u32 crc32;
|
||||
};
|
||||
|
||||
struct run_fw_t {
|
||||
u32 addr;
|
||||
};
|
||||
|
||||
struct exec_struct_t {
|
||||
u32 entry;
|
||||
u32 param;
|
||||
u32 sp;
|
||||
u32 exec_addr;
|
||||
};
|
||||
|
||||
void bes_parse_fw_info(const u8 *data, u32 data_len, u32 *load_addr, u32 *crc32);
|
||||
int bes_frame_rsp_check(void *rsp, u8 frame_num);
|
||||
const u8* bes2600_get_firmware_version_info(const u8 *data, u32 count);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
extern struct device *global_dev;
|
||||
|
||||
#ifdef CONFIG_BES2600_ENABLE_DEVEL_LOGS
|
||||
#define bes_devel(fmt, ...) dev_debug(global_dev, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define bes_devel(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
#define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__)
|
||||
#define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__)
|
||||
#define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__)
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES_NL80211_TESTMODE_MSG_H
|
||||
#define BES_NL80211_TESTMODE_MSG_H
|
||||
|
||||
/* example command structure for test purposes */
|
||||
struct bes_msg_test_t {
|
||||
int dummy;
|
||||
};
|
||||
|
||||
/* example reply structure for test purposes */
|
||||
struct bes_reply_test_t {
|
||||
int dummy;
|
||||
};
|
||||
|
||||
/* example event structure for test purposes */
|
||||
struct bes_event_test_t {
|
||||
int dummy;
|
||||
};
|
||||
|
||||
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
||||
/*example command structure for set select efuse*/
|
||||
struct bes_select_calib_t {
|
||||
uint16_t select_efuse_flag;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* vendor to mcu cmd msg reply structure */
|
||||
struct vendor_rf_cmd_msg_reply {
|
||||
u32 id;
|
||||
u32 msg_len;
|
||||
char ret_msg[1028];
|
||||
};
|
||||
|
||||
/* rf cmd msg reply assembly */
|
||||
void bes2600_rf_cmd_msg_assembly(u32 cmd_type, void *data, u32 msg_len);
|
||||
/* do rf cmd msg */
|
||||
int bes2600_vendor_rf_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len);
|
||||
|
||||
enum bes_msg_id {
|
||||
BES_MSG_TEST = 0, /* for test purposes */
|
||||
BES_MSG_EVENT_TEST, /* for test purposes */
|
||||
BES_MSG_SET_SNAP_FRAME,
|
||||
BES_MSG_EVENT_FRAME_DATA,
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
BES_MSG_GET_TX_POWER_LEVEL,
|
||||
BES_MSG_GET_TX_POWER_RANGE,
|
||||
BES_MSG_SET_ADVANCE_SCAN_ELEMS,
|
||||
BES_MSG_SET_TX_QUEUE_PARAMS,
|
||||
BES_MSG_START_STOP_TSM,
|
||||
BES_MSG_GET_TSM_PARAMS,
|
||||
BES_MSG_GET_ROAM_DELAY,
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
BES_MSG_SET_POWER_SAVE,
|
||||
#ifdef ROAM_OFFLOAD
|
||||
BES_MSG_NEW_SCAN_RESULTS,
|
||||
#endif /* ROAM_OFFLOAD */
|
||||
BES_MSG_ADD_IP_OFFLOAD,
|
||||
BES_MSG_DEL_IP_OFFLOAD,
|
||||
BES_MSG_SET_IP_OFFLOAD_PERIOD,
|
||||
BES_MSG_VENDOR_RF_CMD,
|
||||
BES_MSG_SAVE_CALI_TXT_TO_FLASH,
|
||||
BES_MSG_EPTA_PARM_CONFIG,
|
||||
BES_MSG_GET_KEEP_ALIVE_STREAM,
|
||||
BES_MSG_MCU_CPUUSAGE,
|
||||
BES_MSG_SAVE_CALI_TXT_TO_EFUSE,
|
||||
BES_MSG_SET_SELECT_EFUSE_FLAG,
|
||||
/* Add new IDs here */
|
||||
|
||||
BES_MSG_ID_MAX,
|
||||
};
|
||||
|
||||
enum vendor_rf_cmd_type {
|
||||
VENDOR_RF_SIGNALING_CMD = 0,
|
||||
VENDOR_RF_NOSIGNALING_CMD,
|
||||
VENDOR_RF_GET_SAVE_FREQOFFSET_CMD,
|
||||
VENDOR_RF_GET_SAVE_POWERLEVEL_CMD,
|
||||
VENDOR_RF_SAVE_FREQOFFSET_CMD,
|
||||
VENDOR_RF_SAVE_POWERLEVEL_CMD,
|
||||
VENDOR_RF_POWER_CALIB_FINISH,
|
||||
VENDOR_RF_GET_CALI_FROM_EFUSE,
|
||||
/* add new here */
|
||||
|
||||
VENDOR_RF_CMD_MAX,
|
||||
|
||||
VENDOR_RF_SIG_NOSIG_MIX = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
enum bes_nl80211_testmode_data_attributes {
|
||||
BES_TM_MSG_ID = 0x0001, /* u32 type containing the BES message ID */
|
||||
BES_TM_MSG_DATA, /* message payload */
|
||||
|
||||
/* Max indicator so module test may add its own attributes */
|
||||
BES_TM_MSG_ATTR_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* bes_msg_set_snap_frame - set SNAP frame format
|
||||
* @len: length of SNAP frame, if 0 SNAP frame disabled
|
||||
* @frame: SNAP frame format
|
||||
*
|
||||
* In this structure is difference between user space because
|
||||
* format and length have to be hidden
|
||||
*
|
||||
*/
|
||||
struct bes_msg_set_snap_frame {
|
||||
u8 len;
|
||||
u8 frame[0];
|
||||
};
|
||||
|
||||
struct vendor_epta_parm {
|
||||
int wlan_duration;
|
||||
int bt_duration;
|
||||
int hw_epta_enable;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
/**
|
||||
* bes_msg_set_txqueue_params - store Tx queue params
|
||||
* @user_priority: User priority for which TSPEC negotiated
|
||||
* @medium_time: Allowed medium time
|
||||
* @expiry_time: The expiry time of MSDU
|
||||
*
|
||||
*/
|
||||
struct bes_msg_set_txqueue_params {
|
||||
u8 user_priority;
|
||||
u16 medium_time;
|
||||
u16 expiry_time;
|
||||
};
|
||||
|
||||
/**
|
||||
* bes_tsm_stats - To retrieve the Transmit Stream Measurement stats
|
||||
* @actual_msrmt_start_time: The TSF at the time at which the measurement
|
||||
* started
|
||||
* @msrmt_duration: Duration for measurement
|
||||
* @peer_sta_addr: Peer STA address
|
||||
* @tid: TID for which measurements were made
|
||||
* @reporting_reason: Reason for report sent
|
||||
* @txed_msdu_count: The number of MSDUs transmitted for the specified TID
|
||||
* @msdu_discarded_count: The number of discarded MSDUs for the specified TID
|
||||
* @msdu_failed_count: The number of failed MSDUs for the specified TID
|
||||
* @multi_retry_count: The number of MSDUs which were retried
|
||||
* @qos_cfpolls_lost_count: The number of QOS CF polls frames lost
|
||||
* @avg_q_delay: Average queue delay
|
||||
* @avg_transmit_delay: Average transmit delay
|
||||
* @bin0_range: Delay range of the first bin (Bin 0)
|
||||
* @bin0: bin0 transmit delay histogram
|
||||
* @bin1: bin1 transmit delay histogram
|
||||
* @bin2: bin2 transmit delay histogram
|
||||
* @bin3: bin3 transmit delay histogram
|
||||
* @bin4: bin4 transmit delay histogram
|
||||
* @bin5: bin5 transmit delay histogram
|
||||
*
|
||||
*/
|
||||
struct bes_tsm_stats {
|
||||
u64 actual_msrmt_start_time;
|
||||
u16 msrmt_duration;
|
||||
u8 peer_sta_addr[6];
|
||||
u8 tid;
|
||||
u8 reporting_reason;
|
||||
u32 txed_msdu_count;
|
||||
u32 msdu_discarded_count;
|
||||
u32 msdu_failed_count;
|
||||
u32 multi_retry_count;
|
||||
u32 qos_cfpolls_lost_count;
|
||||
u32 avg_q_delay;
|
||||
u32 avg_transmit_delay;
|
||||
u8 bin0_range;
|
||||
u32 bin0;
|
||||
u32 bin1;
|
||||
u32 bin2;
|
||||
u32 bin3;
|
||||
u32 bin4;
|
||||
u32 bin5;
|
||||
} __packed;
|
||||
|
||||
|
||||
/**
|
||||
* bes_msg_set_start_stop_tsm - To start or stop collecting TSM metrics in
|
||||
* bes2600 driver
|
||||
* @start: To start or stop collecting TSM metrics
|
||||
* @up: up for which metrics to be collected
|
||||
* @packetization_delay: Packetization period for this TID
|
||||
*
|
||||
*/
|
||||
struct bes_msg_start_stop_tsm {
|
||||
u8 start; /*1: To start, 0: To stop*/
|
||||
u8 up;
|
||||
u16 packetization_delay;
|
||||
};
|
||||
|
||||
/**
|
||||
* power_save_elems - To enable/disable legacy power Save
|
||||
*/
|
||||
struct power_save_elems {
|
||||
int powerSave;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
||||
/*
|
||||
* ip keep alive feature's parameters
|
||||
* @idx add(idx=15) a tcp/udp keep alive stream, or the idx number(idx= 0-7) when reconfig one;
|
||||
* set payload_len to 0 and payload to NULL when deleting one stream.
|
||||
* @klv_vendor different number stands for different vendor's keepalive configuration
|
||||
* @period: alive period
|
||||
* @proto 0 for udp and 1 for tcp
|
||||
* @src_port local port
|
||||
* @dst_port tcp server's listen port
|
||||
* @src_ip local ip address
|
||||
* @dst_ip tcp server's ip address
|
||||
* @key: key
|
||||
* @iv: iv
|
||||
* @payload payload of the keep alive packet
|
||||
* @payload_len length of the payload
|
||||
*/
|
||||
struct ip_alive_paras {
|
||||
uint16_t idx;
|
||||
uint8_t klv_vendor;
|
||||
uint8_t period;
|
||||
uint8_t proto;
|
||||
uint16_t src_port;
|
||||
uint16_t dst_port;
|
||||
uint32_t src_ip;
|
||||
uint32_t dst_ip;
|
||||
uint8_t key[16];
|
||||
uint8_t iv[16];
|
||||
uint16_t payload_len;
|
||||
uint8_t payload[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* ip_alive_tac_idx - idx of tcp & udp alive stream
|
||||
*/
|
||||
struct ip_alive_iac_idx {
|
||||
int idx;
|
||||
};
|
||||
|
||||
/**
|
||||
* ip_alive_period - tcp & udp alive period
|
||||
*/
|
||||
struct ip_alive_period {
|
||||
int period;
|
||||
};
|
||||
|
||||
/*
|
||||
* err_code, 0: success, -1: cmd with idx err,
|
||||
* -2: get stream fail, -3: playload err
|
||||
*/
|
||||
struct ip_stream_cfg {
|
||||
int err_code;
|
||||
int idx;
|
||||
};
|
||||
#endif /* CONFIG_BES2600_KEEP_ALIVE */
|
||||
|
||||
#endif /* CONFIG_BES2600_TESTMODE */
|
||||
|
||||
#define BES_TM_MAX_ATTRIB_SIZE 1024
|
||||
|
||||
#endif /* BES_NL80211_TESTMODE_MSG_H*/
|
||||
+1447
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __BES_PWR_H__
|
||||
#define __BES_PWR_H__
|
||||
|
||||
#include "bes2600.h"
|
||||
|
||||
enum bes2600_pwr_event_type {
|
||||
BES_PWR_LOCK_ON_SCAN = 1,
|
||||
BES_PWR_LOCK_ON_JOIN,
|
||||
BES_PWR_LOCK_ON_TX,
|
||||
BES_PWR_LOCK_ON_RX,
|
||||
BES_PWR_LOCK_ON_FLUSH,
|
||||
BES_PWR_LOCK_ON_ROC,
|
||||
BES_PWR_LOCK_ON_WSM_TX,
|
||||
BES_PWR_LOCK_ON_WSM_OPER,
|
||||
BES_PWR_LOCK_ON_BSS_LOST,
|
||||
BES_PWR_LOCK_ON_GET_IP,
|
||||
BES_PWR_LOCK_ON_PS_ACTIVE,
|
||||
BES_PWR_LOCK_ON_LMAC_RSP,
|
||||
BES_PWR_LOCK_ON_AP,
|
||||
BES_PWR_LOCK_ON_TEST_CMD,
|
||||
BES_PWR_LOCK_ON_MUL_REQ,
|
||||
BES_PWR_LOCK_ON_ADV_SCAN,
|
||||
BES_PWR_LOCK_ON_DISCON,
|
||||
BES_PWR_LOCK_ON_QUEUE_GC,
|
||||
BES_PWR_LOCK_ON_AP_LP_BAD,
|
||||
/* add new lock event here */
|
||||
BES_PWR_LOCK_ON_EVENT_MAX,
|
||||
};
|
||||
|
||||
#define BES_PWR_IS_CONSTANT_EVENT(x) ((x) & 0x80000000)
|
||||
#define BES_PWR_EVENT_SET_CONSTANT(x) (x = (x | 0x80000000))
|
||||
#define BES_PWR_EVENT_NUMBER(x) ((x) & 0x7fffffff)
|
||||
|
||||
#define BES2600_POWER_DOWN_DELAY 50 // unit in millisecond
|
||||
#define BES2600_DELAY_EVENT_NUM (BES_PWR_LOCK_ON_EVENT_MAX << 1)
|
||||
|
||||
#define BES_PWR_EVENT_TX_TIMEOUT 1500 // unit in millisecond
|
||||
#define BES_PWR_EVENT_RX_TIMEOUT 100 // unit in millisecond
|
||||
|
||||
struct bes2600_pwr_event_t
|
||||
{
|
||||
struct list_head link;
|
||||
unsigned long timeout;
|
||||
unsigned long delay;
|
||||
u8 idx;
|
||||
enum bes2600_pwr_event_type event;
|
||||
};
|
||||
|
||||
enum power_down_state
|
||||
{
|
||||
POWER_DOWN_STATE_LOCKED = 0,
|
||||
POWER_DOWN_STATE_LOCKING,
|
||||
POWER_DOWN_STATE_UNLOCKING,
|
||||
POWER_DOWN_STATE_UNLOCKED,
|
||||
};
|
||||
|
||||
typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv);
|
||||
typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv);
|
||||
|
||||
struct bes2600_pwr_enter_cb_item
|
||||
{
|
||||
struct list_head link;
|
||||
bes_pwr_enter_lp_cb cb;
|
||||
};
|
||||
|
||||
struct bes2600_pwr_exit_cb_item
|
||||
{
|
||||
struct list_head link;
|
||||
bes_pwr_exit_lp_cb cb;
|
||||
};
|
||||
|
||||
struct bes2600_pwr_t
|
||||
{
|
||||
spinlock_t pwr_lock;
|
||||
struct delayed_work power_down_work;
|
||||
struct work_struct power_async_work;
|
||||
struct work_struct power_mcu_down_work;
|
||||
struct list_head async_timeout_list;
|
||||
struct list_head pending_event_list;
|
||||
struct list_head free_event_list;
|
||||
enum power_down_state power_state;
|
||||
struct task_struct *power_down_task;
|
||||
struct task_struct *power_up_task;
|
||||
struct task_struct *sys_suspend_task;
|
||||
struct task_struct *sys_resume_task;
|
||||
struct semaphore sync_lock;
|
||||
bool pending_lock;
|
||||
atomic_t dev_state;
|
||||
struct mutex pwr_mutex;
|
||||
struct bes2600_common *hw_priv;
|
||||
struct completion pm_enter_cmpl;
|
||||
struct mutex pwr_cb_mutex;
|
||||
struct list_head enter_cb_list;
|
||||
struct list_head exit_cb_list;
|
||||
wait_queue_head_t dev_lp_wq;
|
||||
bool ap_lp_bad;
|
||||
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||
atomic_t pm_set_in_process;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BES2600_WOWLAN
|
||||
void bes2600_pwr_init(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_exit(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_prepare(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_complete(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_start(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_stop(struct bes2600_common *hw_priv);
|
||||
bool bes2600_pwr_constant_event_is_pending(struct bes2600_common *hw_priv, u32 event);
|
||||
int bes2600_pwr_set_busy_event(struct bes2600_common *hw_priv, u32 event);
|
||||
int bes2600_pwr_set_busy_event_async(struct bes2600_common *hw_priv, u32 event);
|
||||
int bes2600_pwr_set_busy_event_with_timeout(struct bes2600_common *hw_priv, u32 event, u32 timeout);
|
||||
int bes2600_pwr_set_busy_event_with_timeout_async(struct bes2600_common *hw_priv, u32 event, u32 timeout);
|
||||
int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event);
|
||||
void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode);
|
||||
bool bes2600_pwr_device_is_idle(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_register_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb);
|
||||
void bes2600_pwr_unregister_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb);
|
||||
void bes2600_pwr_register_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb);
|
||||
void bes2600_pwr_unregister_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb);
|
||||
void bes2600_pwr_suspend_start(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_suspend_end(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_resume_start(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_resume_end(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_mcu_sleep_directly(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_mark_ap_lp_bad(struct bes2600_common *hw_priv);
|
||||
void bes2600_pwr_clear_ap_lp_bad_mark(struct bes2600_common *hw_priv);
|
||||
int bes2600_pwr_busy_event_dump(struct bes2600_common *hw_priv, char *buffer, u32 buf_len);
|
||||
int bes2600_pwr_busy_event_record(struct bes2600_common *hw_priv, char *buffer, u32 buf_len);
|
||||
#else
|
||||
static inline void bes2600_pwr_init(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_exit(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_prepare(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_complete(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_start(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_stop(struct bes2600_common *hw_priv) { }
|
||||
static inline bool bes2600_pwr_constant_event_is_pending(struct bes2600_common *hw_priv, u32 event) { return false; }
|
||||
static inline int bes2600_pwr_set_busy_event(struct bes2600_common *hw_priv, u32 event) { return 0; }
|
||||
static inline int bes2600_pwr_set_busy_event_async(struct bes2600_common *hw_priv, u32 event) {return 0; }
|
||||
static inline int bes2600_pwr_set_busy_event_with_timeout(struct bes2600_common *hw_priv, u32 event, u32 timeout) { return 0; }
|
||||
static inline int bes2600_pwr_set_busy_event_with_timeout_async(struct bes2600_common *hw_priv, u32 event, u32 timeout) { return 0; }
|
||||
static inline int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event) { return 0; }
|
||||
static inline void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode) { }
|
||||
static inline bool bes2600_pwr_device_is_idle(struct bes2600_common *hw_priv) { return false; }
|
||||
static inline void bes2600_pwr_register_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { }
|
||||
static inline void bes2600_pwr_unregister_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { }
|
||||
static inline void bes2600_pwr_register_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { }
|
||||
static inline void bes2600_pwr_unregister_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { }
|
||||
static inline void bes2600_pwr_suspend_start(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_suspend_end(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_resume_start(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_resume_end(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_mcu_sleep_directly(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_mark_ap_lp_bad(struct bes2600_common *hw_priv) { }
|
||||
static inline void bes2600_pwr_clear_ap_lp_bad_mark(struct bes2600_common *hw_priv) { }
|
||||
static inline int bes2600_pwr_busy_event_dump(struct bes2600_common *hw_priv, char *buffer, u32 buf_len) { return 0; }
|
||||
static inline int bes2600_pwr_busy_event_record(struct bes2600_common *hw_priv, char *buffer, u32 buf_len) { return 0; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+1538
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Device handling thread interface for mac80211 BES2600 drivers
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_BH_H
|
||||
#define BES2600_BH_H
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
/* extern */ struct bes2600_common;
|
||||
|
||||
#define SDIO_BLOCK_SIZE (528)
|
||||
|
||||
#define KEY_FRAME_SW_RETRY
|
||||
#ifdef KEY_FRAME_SW_RETRY
|
||||
#define CW1200_MAX_SW_RETRY_CNT (2)
|
||||
#endif
|
||||
|
||||
|
||||
int bes2600_register_bh(struct bes2600_common *hw_priv);
|
||||
void bes2600_unregister_bh(struct bes2600_common *hw_priv);
|
||||
void bes2600_irq_handler(struct bes2600_common *hw_priv);
|
||||
void bes2600_bh_wakeup(struct bes2600_common *hw_priv);
|
||||
int bes2600_bh_suspend(struct bes2600_common *hw_priv);
|
||||
int bes2600_bh_resume(struct bes2600_common *hw_priv);
|
||||
/* Must be called from BH thread. */
|
||||
void bes2600_enable_powersave(struct bes2600_vif *priv,
|
||||
bool enable);
|
||||
int wsm_release_tx_buffer(struct bes2600_common *hw_priv, int count);
|
||||
int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id,
|
||||
int count);
|
||||
int bes2600_bh_sw_process(struct bes2600_common *hw_priv,
|
||||
struct wsm_tx_confirm *tx_confirm);
|
||||
|
||||
void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx);
|
||||
void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx);
|
||||
|
||||
void bes2600_bh_mcu_active_monitor(struct timer_list* t);
|
||||
void bes2600_bh_lmac_active_monitor(struct timer_list* t);
|
||||
|
||||
#endif /* BES2600_BH_H */
|
||||
+766
@@ -0,0 +1,766 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "bes2600.h"
|
||||
#include "debug.h"
|
||||
#ifdef CONFIG_BES2600_DEBUGFS
|
||||
/* join_status */
|
||||
static const char * const bes2600_debug_join_status[] = {
|
||||
"passive",
|
||||
"monitor",
|
||||
"station",
|
||||
"access point",
|
||||
};
|
||||
|
||||
/* WSM_JOIN_PREAMBLE_... */
|
||||
static const char * const bes2600_debug_preamble[] = {
|
||||
"long",
|
||||
"short",
|
||||
"long on 1 and 2 Mbps",
|
||||
};
|
||||
|
||||
static const char * const bes2600_debug_fw_types[] = {
|
||||
"ETF",
|
||||
"WFM",
|
||||
"WSM",
|
||||
"HI test",
|
||||
"Platform test",
|
||||
};
|
||||
|
||||
static const char * const bes2600_debug_link_id[] = {
|
||||
"OFF",
|
||||
"REQ",
|
||||
"SOFT",
|
||||
"HARD",
|
||||
};
|
||||
|
||||
static const char *bes2600_debug_mode(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
return "unspecified";
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
return "monitor";
|
||||
case NL80211_IFTYPE_STATION:
|
||||
return "station";
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
return "ad-hok";
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
return "mesh point";
|
||||
case NL80211_IFTYPE_AP:
|
||||
return "access point";
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
return "p2p client";
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
return "p2p go";
|
||||
default:
|
||||
return "unsupported";
|
||||
}
|
||||
}
|
||||
|
||||
static void bes2600_queue_status_show(struct seq_file *seq,
|
||||
struct bes2600_queue *q)
|
||||
{
|
||||
int i, if_id;
|
||||
seq_printf(seq, "Queue %d:\n", (int)q->queue_id);
|
||||
seq_printf(seq, " capacity: %ld\n", (long)q->capacity);
|
||||
seq_printf(seq, " queued: %ld\n", (long)q->num_queued);
|
||||
seq_printf(seq, " pending: %ld\n", (long)q->num_pending);
|
||||
seq_printf(seq, " sent: %ld\n", (long)q->num_sent);
|
||||
seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no");
|
||||
seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no");
|
||||
seq_puts(seq, " link map: 0-> ");
|
||||
for (if_id = 0; if_id < CW12XX_MAX_VIFS; if_id++) {
|
||||
for (i = 0; i < q->stats->map_capacity; ++i)
|
||||
seq_printf(seq, "%.2d ", q->link_map_cache[if_id][i]);
|
||||
seq_printf(seq, "<-%ld\n", (long)q->stats->map_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
static void bes2600_debug_print_map(struct seq_file *seq,
|
||||
struct bes2600_vif *priv,
|
||||
const char *label,
|
||||
u32 map)
|
||||
{
|
||||
int i;
|
||||
seq_printf(seq, "%s0-> ", label);
|
||||
for (i = 0; i < priv->hw_priv->tx_queue_stats.map_capacity; ++i)
|
||||
seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
|
||||
seq_printf(seq, "<-%ld\n",
|
||||
(long)priv->hw_priv->tx_queue_stats.map_capacity - 1);
|
||||
}
|
||||
|
||||
static int bes2600_status_show_common(struct seq_file *seq, void *v)
|
||||
{
|
||||
int i;
|
||||
struct list_head *item;
|
||||
struct bes2600_common *hw_priv = seq->private;
|
||||
struct bes2600_debug_common *d = hw_priv->debug;
|
||||
int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0;
|
||||
bool ba_ena;
|
||||
|
||||
spin_lock_bh(&hw_priv->ba_lock);
|
||||
ba_cnt = hw_priv->debug->ba_cnt;
|
||||
ba_acc = hw_priv->debug->ba_acc;
|
||||
ba_cnt_rx = hw_priv->debug->ba_cnt_rx;
|
||||
ba_acc_rx = hw_priv->debug->ba_acc_rx;
|
||||
ba_ena = hw_priv->ba_ena;
|
||||
if (ba_cnt)
|
||||
ba_avg = ba_acc / ba_cnt;
|
||||
if (ba_cnt_rx)
|
||||
ba_avg_rx = ba_acc_rx / ba_cnt_rx;
|
||||
spin_unlock_bh(&hw_priv->ba_lock);
|
||||
|
||||
seq_puts(seq, "BES2600 Wireless LAN driver status\n");
|
||||
seq_printf(seq, "Hardware: %d.%d\n",
|
||||
hw_priv->wsm_caps.hardwareId,
|
||||
hw_priv->wsm_caps.hardwareSubId);
|
||||
seq_printf(seq, "Firmware: %s %d.%d\n",
|
||||
bes2600_debug_fw_types[hw_priv->wsm_caps.firmwareType],
|
||||
hw_priv->wsm_caps.firmwareVersion,
|
||||
hw_priv->wsm_caps.firmwareBuildNumber);
|
||||
seq_printf(seq, "FW API: %d\n",
|
||||
hw_priv->wsm_caps.firmwareApiVer);
|
||||
seq_printf(seq, "FW caps: 0x%.4X\n",
|
||||
hw_priv->wsm_caps.firmwareCap);
|
||||
if (hw_priv->channel)
|
||||
seq_printf(seq, "Channel: %d%s\n",
|
||||
hw_priv->channel->hw_value,
|
||||
hw_priv->channel_switch_in_progress ?
|
||||
" (switching)" : "");
|
||||
seq_printf(seq, "HT: %s\n",
|
||||
bes2600_is_ht(&hw_priv->ht_info) ? "on" : "off");
|
||||
if (bes2600_is_ht(&hw_priv->ht_info)) {
|
||||
seq_printf(seq, "Greenfield: %s\n",
|
||||
bes2600_ht_greenfield(&hw_priv->ht_info) ? "yes" : "no");
|
||||
seq_printf(seq, "AMPDU dens: %d\n",
|
||||
bes2600_ht_ampdu_density(&hw_priv->ht_info));
|
||||
}
|
||||
spin_lock_bh(&hw_priv->tx_policy_cache.lock);
|
||||
i = 0;
|
||||
list_for_each(item, &hw_priv->tx_policy_cache.used)
|
||||
++i;
|
||||
spin_unlock_bh(&hw_priv->tx_policy_cache.lock);
|
||||
seq_printf(seq, "RC in use: %d\n", i);
|
||||
seq_printf(seq, "BA stat: %d, %d (%d)\n",
|
||||
ba_cnt, ba_acc, ba_avg);
|
||||
seq_printf(seq, "BA RX stat: %d, %d (%d)\n",
|
||||
ba_cnt_rx, ba_acc_rx, ba_avg_rx);
|
||||
seq_printf(seq, "Block ACK: %s\n", ba_ena ? "on" : "off");
|
||||
|
||||
seq_puts(seq, "\n");
|
||||
for (i = 0; i < 4; ++i) {
|
||||
bes2600_queue_status_show(seq, &hw_priv->tx_queue[i]);
|
||||
seq_puts(seq, "\n");
|
||||
}
|
||||
seq_printf(seq, "TX burst: %d\n",
|
||||
d->tx_burst);
|
||||
seq_printf(seq, "RX burst: %d\n",
|
||||
d->rx_burst);
|
||||
seq_printf(seq, "TX miss: %d\n",
|
||||
d->tx_cache_miss);
|
||||
seq_printf(seq, "Long retr: %d\n",
|
||||
hw_priv->long_frame_max_tx_count);
|
||||
seq_printf(seq, "Short retr: %d\n",
|
||||
hw_priv->short_frame_max_tx_count);
|
||||
|
||||
seq_printf(seq, "BH status: %s\n",
|
||||
atomic_read(&hw_priv->bh_term) ? "terminated" : "alive");
|
||||
seq_printf(seq, "Pending RX: %d\n",
|
||||
atomic_read(&hw_priv->bh_rx));
|
||||
seq_printf(seq, "Pending TX: %d\n",
|
||||
atomic_read(&hw_priv->bh_tx));
|
||||
if (hw_priv->bh_error)
|
||||
seq_printf(seq, "BH errcode: %d\n",
|
||||
hw_priv->bh_error);
|
||||
seq_printf(seq, "TX bufs: %d x %d bytes\n",
|
||||
hw_priv->wsm_caps.numInpChBufs,
|
||||
hw_priv->wsm_caps.sizeInpChBuf);
|
||||
seq_printf(seq, "Used bufs: %d\n",
|
||||
hw_priv->hw_bufs_used);
|
||||
seq_printf(seq, "Device: %s\n",
|
||||
bes2600_pwr_device_is_idle(hw_priv) ? "alseep" : "awake");
|
||||
|
||||
spin_lock(&hw_priv->wsm_cmd.lock);
|
||||
seq_printf(seq, "WSM status: %s\n",
|
||||
hw_priv->wsm_cmd.done ? "idle" : "active");
|
||||
seq_printf(seq, "WSM cmd: 0x%.4X (%ld bytes)\n",
|
||||
hw_priv->wsm_cmd.cmd, (long)hw_priv->wsm_cmd.len);
|
||||
seq_printf(seq, "WSM retval: %d\n",
|
||||
hw_priv->wsm_cmd.ret);
|
||||
spin_unlock(&hw_priv->wsm_cmd.lock);
|
||||
|
||||
seq_printf(seq, "Datapath: %s\n",
|
||||
atomic_read(&hw_priv->tx_lock) ? "locked" : "unlocked");
|
||||
if (atomic_read(&hw_priv->tx_lock))
|
||||
seq_printf(seq, "TXlock cnt: %d\n",
|
||||
atomic_read(&hw_priv->tx_lock));
|
||||
|
||||
seq_printf(seq, "Scan: %s\n",
|
||||
atomic_read(&hw_priv->scan.in_progress) ? "active" : "idle");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bes2600_status_open_common(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, &bes2600_status_show_common,
|
||||
inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fops_status_common = {
|
||||
.open = bes2600_status_open_common,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int bes2600_counters_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
int ret;
|
||||
struct bes2600_common *hw_priv = seq->private;
|
||||
struct wsm_counters_table counters;
|
||||
|
||||
ret = wsm_get_counters_table(hw_priv, &counters);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#define CAT_STR(x, y) x ## y
|
||||
#define PUT_COUNTER(tab, name) \
|
||||
seq_printf(seq, "%s:" tab "%d\n", #name, \
|
||||
__le32_to_cpu(counters.CAT_STR(count, name)))
|
||||
|
||||
PUT_COUNTER("\t\t", PlcpErrors);
|
||||
PUT_COUNTER("\t\t", FcsErrors);
|
||||
PUT_COUNTER("\t\t", TxPackets);
|
||||
PUT_COUNTER("\t\t", RxPackets);
|
||||
PUT_COUNTER("\t\t", RxPacketErrors);
|
||||
PUT_COUNTER("\t", RxDecryptionFailures);
|
||||
PUT_COUNTER("\t\t", RxMicFailures);
|
||||
PUT_COUNTER("\t", RxNoKeyFailures);
|
||||
PUT_COUNTER("\t", TxMulticastFrames);
|
||||
PUT_COUNTER("\t", TxFramesSuccess);
|
||||
PUT_COUNTER("\t", TxFrameFailures);
|
||||
PUT_COUNTER("\t", TxFramesRetried);
|
||||
PUT_COUNTER("\t", TxFramesMultiRetried);
|
||||
PUT_COUNTER("\t", RxFrameDuplicates);
|
||||
PUT_COUNTER("\t\t", RtsSuccess);
|
||||
PUT_COUNTER("\t\t", RtsFailures);
|
||||
PUT_COUNTER("\t\t", AckFailures);
|
||||
PUT_COUNTER("\t", RxMulticastFrames);
|
||||
PUT_COUNTER("\t", RxFramesSuccess);
|
||||
PUT_COUNTER("\t", RxCMACICVErrors);
|
||||
PUT_COUNTER("\t\t", RxCMACReplays);
|
||||
PUT_COUNTER("\t", RxMgmtCCMPReplays);
|
||||
|
||||
#undef PUT_COUNTER
|
||||
#undef CAT_STR
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bes2600_counters_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, &bes2600_counters_show,
|
||||
inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fops_counters = {
|
||||
.open = bes2600_counters_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int bes2600_power_busy_event_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct bes2600_common *hw_priv = seq->private;
|
||||
char *buffer = NULL;
|
||||
|
||||
buffer = kmalloc(8192, GFP_KERNEL);
|
||||
if(!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if(bes2600_pwr_busy_event_dump(hw_priv, buffer, 8192) == 0) {
|
||||
seq_printf(seq, "%s", buffer);
|
||||
} else {
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bes2600_power_busy_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, &bes2600_power_busy_event_show,
|
||||
inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fops_power_busy_events = {
|
||||
.open = bes2600_power_busy_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int bes2600_generic_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t bes2600_11n_read(struct file *file,
|
||||
char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct bes2600_common *hw_priv = file->private_data;
|
||||
struct ieee80211_supported_band *band =
|
||||
hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ];
|
||||
return simple_read_from_buffer(user_buf, count, ppos,
|
||||
band->ht_cap.ht_supported ? "1\n" : "0\n", 2);
|
||||
}
|
||||
|
||||
static ssize_t bes2600_11n_write(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct bes2600_common *hw_priv = file->private_data;
|
||||
struct ieee80211_supported_band *band[2] = {
|
||||
hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ],
|
||||
hw_priv->hw->wiphy->bands[NL80211_BAND_5GHZ],
|
||||
};
|
||||
char buf[1];
|
||||
int ena = 0;
|
||||
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, user_buf, 1))
|
||||
return -EFAULT;
|
||||
if (buf[0] == 1)
|
||||
ena = 1;
|
||||
|
||||
band[0]->ht_cap.ht_supported = ena;
|
||||
band[1]->ht_cap.ht_supported = ena;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_11n = {
|
||||
.open = bes2600_generic_open,
|
||||
.read = bes2600_11n_read,
|
||||
.write = bes2600_11n_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t bes2600_wsm_dumps(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct bes2600_common *hw_priv = file->private_data;
|
||||
char buf[1];
|
||||
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, user_buf, 1))
|
||||
return -EFAULT;
|
||||
|
||||
if (buf[0] == '1')
|
||||
hw_priv->wsm_enable_wsm_dumps = 1;
|
||||
else
|
||||
hw_priv->wsm_enable_wsm_dumps = 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_wsm_dumps = {
|
||||
.open = bes2600_generic_open,
|
||||
.write = bes2600_wsm_dumps,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
|
||||
static ssize_t bes2600_short_dump_read(struct file *file,
|
||||
char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct bes2600_common *hw_priv = file->private_data;
|
||||
char buf[20];
|
||||
size_t size = 0;
|
||||
|
||||
sprintf(buf, "Size: %u\n", hw_priv->wsm_dump_max_size);
|
||||
size = strlen(buf);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos,
|
||||
buf, size);
|
||||
}
|
||||
|
||||
static ssize_t bes2600_short_dump_write(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct bes2600_common *priv = file->private_data;
|
||||
char buf[20];
|
||||
unsigned long dump_size = 0;
|
||||
|
||||
if (!count || count > 20)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, user_buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
if (kstrtoul(buf, 10, &dump_size))
|
||||
return -EINVAL;
|
||||
|
||||
priv->wsm_dump_max_size = dump_size;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_short_dump = {
|
||||
.open = bes2600_generic_open,
|
||||
.write = bes2600_short_dump_write,
|
||||
.read = bes2600_short_dump_read,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
|
||||
|
||||
#ifdef BES2600_DUMP_FW_DPD_LOG
|
||||
extern void bes2600_get_dpd_log(char **data, size_t *len);
|
||||
static ssize_t bes2600_dpd_log_read(struct file *file,
|
||||
char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
char empty[] = {'\n', '\0'};
|
||||
char *data;
|
||||
size_t size;
|
||||
|
||||
bes2600_get_dpd_log(&data, &size);
|
||||
if (!data) {
|
||||
data = empty;
|
||||
size = sizeof(empty);
|
||||
}
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos,
|
||||
data, size);
|
||||
}
|
||||
|
||||
static const struct file_operations dpd_log_dump = {
|
||||
.open = bes2600_generic_open,
|
||||
.read = bes2600_dpd_log_read,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
#endif /* BES2600_DUMP_FW_DPD_LOG */
|
||||
|
||||
int bes2600_debug_init_common(struct bes2600_common *hw_priv)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
struct bes2600_debug_common *d =
|
||||
kzalloc(sizeof(struct bes2600_debug_common), GFP_KERNEL);
|
||||
hw_priv->debug = d;
|
||||
if (!d)
|
||||
return ret;
|
||||
|
||||
d->debugfs_phy = debugfs_create_dir("bes2600",
|
||||
hw_priv->hw->wiphy->debugfsdir);
|
||||
if (!d->debugfs_phy)
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
|
||||
hw_priv, &fops_status_common))
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
|
||||
hw_priv, &fops_counters))
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_file("11n", S_IRUSR | S_IWUSR,
|
||||
d->debugfs_phy, hw_priv, &fops_11n))
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
|
||||
hw_priv, &fops_wsm_dumps))
|
||||
goto err;
|
||||
|
||||
#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
|
||||
if (!debugfs_create_file("wsm_dump_size", S_IRUSR | S_IWUSR,
|
||||
d->debugfs_phy, hw_priv, &fops_short_dump))
|
||||
goto err;
|
||||
#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
|
||||
|
||||
#ifdef CONFIG_BES2600_WOWLAN
|
||||
if (!debugfs_create_file("power_events", S_IRUSR, d->debugfs_phy,
|
||||
hw_priv, &fops_power_busy_events))
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
#ifdef BES2600_DUMP_FW_DPD_LOG
|
||||
if (!debugfs_create_file("dpd_log", S_IRUSR, d->debugfs_phy,
|
||||
hw_priv, &dpd_log_dump))
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
ret = bes2600_itp_init(hw_priv);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
hw_priv->debug = NULL;
|
||||
debugfs_remove_recursive(d->debugfs_phy);
|
||||
kfree(d);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bes2600_debug_release_common(struct bes2600_common *hw_priv)
|
||||
{
|
||||
struct bes2600_debug_common *d = hw_priv->debug;
|
||||
if (d) {
|
||||
bes2600_itp_release(hw_priv);
|
||||
hw_priv->debug = NULL;
|
||||
kfree(d);
|
||||
}
|
||||
}
|
||||
|
||||
static int bes2600_status_show_priv(struct seq_file *seq, void *v)
|
||||
{
|
||||
int i;
|
||||
struct bes2600_vif *priv = seq->private;
|
||||
struct bes2600_debug_priv *d = priv->debug;
|
||||
|
||||
seq_printf(seq, "Mode: %s%s\n",
|
||||
bes2600_debug_mode(priv->mode),
|
||||
priv->listening ? " (listening)" : "");
|
||||
seq_printf(seq, "Assoc: %s\n",
|
||||
bes2600_debug_join_status[priv->join_status]);
|
||||
if (priv->rx_filter.promiscuous)
|
||||
seq_puts(seq, "Filter: promisc\n");
|
||||
else if (priv->rx_filter.fcs)
|
||||
seq_puts(seq, "Filter: fcs\n");
|
||||
if (priv->rx_filter.bssid)
|
||||
seq_puts(seq, "Filter: bssid\n");
|
||||
if (priv->bf_control.bcn_count)
|
||||
seq_puts(seq, "Filter: beacons\n");
|
||||
|
||||
if (priv->enable_beacon ||
|
||||
priv->mode == NL80211_IFTYPE_AP ||
|
||||
priv->mode == NL80211_IFTYPE_ADHOC ||
|
||||
priv->mode == NL80211_IFTYPE_MESH_POINT ||
|
||||
priv->mode == NL80211_IFTYPE_P2P_GO)
|
||||
seq_printf(seq, "Beaconing: %s\n",
|
||||
priv->enable_beacon ?
|
||||
"enabled" : "disabled");
|
||||
if (priv->ssid_length ||
|
||||
priv->mode == NL80211_IFTYPE_AP ||
|
||||
priv->mode == NL80211_IFTYPE_ADHOC ||
|
||||
priv->mode == NL80211_IFTYPE_MESH_POINT ||
|
||||
priv->mode == NL80211_IFTYPE_P2P_GO)
|
||||
seq_printf(seq, "SSID: %.*s\n",
|
||||
(int)priv->ssid_length, priv->ssid);
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i,
|
||||
priv->edca.params[i].cwMin,
|
||||
priv->edca.params[i].cwMax,
|
||||
priv->edca.params[i].aifns,
|
||||
priv->edca.params[i].txOpLimit,
|
||||
priv->edca.params[i].maxReceiveLifetime);
|
||||
}
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_STA) {
|
||||
static const char *pmMode = "unknown";
|
||||
switch (priv->powersave_mode.pmMode) {
|
||||
case WSM_PSM_ACTIVE:
|
||||
pmMode = "off";
|
||||
break;
|
||||
case WSM_PSM_PS:
|
||||
pmMode = "on";
|
||||
break;
|
||||
case WSM_PSM_FAST_PS:
|
||||
pmMode = "dynamic";
|
||||
break;
|
||||
}
|
||||
seq_printf(seq, "Preamble: %s\n",
|
||||
bes2600_debug_preamble[
|
||||
priv->association_mode.preambleType]);
|
||||
seq_printf(seq, "AMPDU spcn: %d\n",
|
||||
priv->association_mode.mpduStartSpacing);
|
||||
seq_printf(seq, "Basic rate: 0x%.8X\n",
|
||||
le32_to_cpu(priv->association_mode.basicRateSet));
|
||||
seq_printf(seq, "Bss lost: %d beacons\n",
|
||||
priv->bss_params.beaconLostCount);
|
||||
seq_printf(seq, "AID: %d\n",
|
||||
priv->bss_params.aid);
|
||||
seq_printf(seq, "Rates: 0x%.8X\n",
|
||||
priv->bss_params.operationalRateSet);
|
||||
seq_printf(seq, "Powersave: %s\n", pmMode);
|
||||
}
|
||||
seq_printf(seq, "RSSI thold: %d\n",
|
||||
priv->cqm_rssi_thold);
|
||||
seq_printf(seq, "RSSI hyst: %d\n",
|
||||
priv->cqm_rssi_hyst);
|
||||
seq_printf(seq, "TXFL thold: %d\n",
|
||||
priv->cqm_tx_failure_thold);
|
||||
seq_printf(seq, "Linkloss: %d\n",
|
||||
priv->cqm_link_loss_count);
|
||||
seq_printf(seq, "Bcnloss: %d\n",
|
||||
priv->cqm_beacon_loss_count);
|
||||
|
||||
bes2600_debug_print_map(seq, priv, "Link map: ",
|
||||
priv->link_id_map);
|
||||
bes2600_debug_print_map(seq, priv, "Asleep map: ",
|
||||
priv->sta_asleep_mask);
|
||||
bes2600_debug_print_map(seq, priv, "PSPOLL map: ",
|
||||
priv->pspoll_mask);
|
||||
|
||||
seq_puts(seq, "\n");
|
||||
|
||||
for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) {
|
||||
if (priv->link_id_db[i].status) {
|
||||
seq_printf(seq, "Link %d: %s, %pM\n",
|
||||
i + 1, bes2600_debug_link_id[
|
||||
priv->link_id_db[i].status],
|
||||
priv->link_id_db[i].mac);
|
||||
}
|
||||
}
|
||||
|
||||
seq_puts(seq, "\n");
|
||||
|
||||
seq_printf(seq, "Powermgmt: %s\n",
|
||||
priv->powersave_enabled ? "on" : "off");
|
||||
|
||||
seq_printf(seq, "TXed: %d\n",
|
||||
d->tx);
|
||||
seq_printf(seq, "AGG TXed: %d\n",
|
||||
d->tx_agg);
|
||||
seq_printf(seq, "MULTI TXed: %d (%d)\n",
|
||||
d->tx_multi, d->tx_multi_frames);
|
||||
seq_printf(seq, "RXed: %d\n",
|
||||
d->rx);
|
||||
seq_printf(seq, "AGG RXed: %d\n",
|
||||
d->rx_agg);
|
||||
seq_printf(seq, "TX align: %d\n",
|
||||
d->tx_align);
|
||||
seq_printf(seq, "TX TTL: %d\n",
|
||||
d->tx_ttl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bes2600_status_open_priv(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, &bes2600_status_show_priv,
|
||||
inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fops_status_priv = {
|
||||
.open = bes2600_status_open_priv,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
||||
|
||||
static ssize_t bes2600_hang_write(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct bes2600_vif *priv = file->private_data;
|
||||
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
||||
char buf[1];
|
||||
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, user_buf, 1))
|
||||
return -EFAULT;
|
||||
|
||||
if (priv->vif) {
|
||||
bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
|
||||
ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_hang = {
|
||||
.open = bes2600_generic_open,
|
||||
.write = bes2600_hang_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
#endif
|
||||
|
||||
#define VIF_DEBUGFS_NAME_S 10
|
||||
int bes2600_debug_init_priv(struct bes2600_common *hw_priv,
|
||||
struct bes2600_vif *priv)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
struct bes2600_debug_priv *d;
|
||||
char name[VIF_DEBUGFS_NAME_S];
|
||||
|
||||
if (WARN_ON(!hw_priv))
|
||||
return ret;
|
||||
|
||||
if (WARN_ON(!hw_priv->debug))
|
||||
return ret;
|
||||
|
||||
d = kzalloc(sizeof(struct bes2600_debug_priv), GFP_KERNEL);
|
||||
priv->debug = d;
|
||||
if (WARN_ON(!d))
|
||||
return ret;
|
||||
|
||||
memset(name, 0, VIF_DEBUGFS_NAME_S);
|
||||
ret = snprintf(name, VIF_DEBUGFS_NAME_S, "vif_%d", priv->if_id);
|
||||
if (WARN_ON(ret < 0))
|
||||
goto err;
|
||||
|
||||
d->debugfs_phy = debugfs_create_dir(name,
|
||||
hw_priv->debug->debugfs_phy);
|
||||
if (WARN_ON(!d->debugfs_phy))
|
||||
goto err;
|
||||
|
||||
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
||||
if (WARN_ON(!debugfs_create_file("hang", S_IWUSR, d->debugfs_phy,
|
||||
priv, &fops_hang)))
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
|
||||
priv, &fops_status_priv))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
priv->debug = NULL;
|
||||
debugfs_remove_recursive(d->debugfs_phy);
|
||||
kfree(d);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void bes2600_debug_release_priv(struct bes2600_vif *priv)
|
||||
{
|
||||
struct bes2600_debug_priv *d = priv->debug;
|
||||
if (d) {
|
||||
debugfs_remove_recursive(priv->debug->debugfs_phy);
|
||||
priv->debug = NULL;
|
||||
kfree(d);
|
||||
}
|
||||
}
|
||||
|
||||
int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len)
|
||||
{
|
||||
return snprintf(buf, len, "%s %d.%d",
|
||||
bes2600_debug_fw_types[hw_priv->wsm_caps.firmwareType],
|
||||
hw_priv->wsm_caps.firmwareVersion,
|
||||
hw_priv->wsm_caps.firmwareBuildNumber);
|
||||
}
|
||||
#endif
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* DebugFS code for BES2600 mac80211 driver
|
||||
*
|
||||
* Copyright (c) 2011, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_DEBUG_H_INCLUDED
|
||||
#define BES2600_DEBUG_H_INCLUDED
|
||||
|
||||
#include "itp.h"
|
||||
|
||||
struct cw200_common;
|
||||
|
||||
struct bes2600_debug_common {
|
||||
struct dentry *debugfs_phy;
|
||||
int tx_cache_miss;
|
||||
int tx_burst;
|
||||
int rx_burst;
|
||||
int ba_cnt;
|
||||
int ba_acc;
|
||||
int ba_cnt_rx;
|
||||
int ba_acc_rx;
|
||||
#ifdef CONFIG_BES2600_ITP
|
||||
struct bes2600_itp itp;
|
||||
#endif /* CONFIG_BES2600_ITP */
|
||||
};
|
||||
|
||||
struct bes2600_debug_priv {
|
||||
struct dentry *debugfs_phy;
|
||||
int tx;
|
||||
int tx_agg;
|
||||
int rx;
|
||||
int rx_agg;
|
||||
int tx_multi;
|
||||
int tx_multi_frames;
|
||||
int tx_align;
|
||||
int tx_ttl;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BES2600_DEBUGFS
|
||||
int bes2600_debug_init_common(struct bes2600_common *hw_priv);
|
||||
int bes2600_debug_init_priv(struct bes2600_common *hw_priv,
|
||||
struct bes2600_vif *priv);
|
||||
void bes2600_debug_release_common(struct bes2600_common *hw_priv);
|
||||
void bes2600_debug_release_priv(struct bes2600_vif *priv);
|
||||
|
||||
static inline void bes2600_debug_txed(struct bes2600_vif *priv)
|
||||
{
|
||||
++priv->debug->tx;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_txed_agg(struct bes2600_vif *priv)
|
||||
{
|
||||
++priv->debug->tx_agg;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_txed_multi(struct bes2600_vif *priv,
|
||||
int count)
|
||||
{
|
||||
++priv->debug->tx_multi;
|
||||
priv->debug->tx_multi_frames += count;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_rxed(struct bes2600_vif *priv)
|
||||
{
|
||||
++priv->debug->rx;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_rxed_agg(struct bes2600_vif *priv)
|
||||
{
|
||||
++priv->debug->rx_agg;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_tx_cache_miss(struct bes2600_common *common)
|
||||
{
|
||||
++common->debug->tx_cache_miss;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_tx_align(struct bes2600_vif *priv)
|
||||
{
|
||||
++priv->debug->tx_align;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_tx_ttl(struct bes2600_vif *priv)
|
||||
{
|
||||
++priv->debug->tx_ttl;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_tx_burst(struct bes2600_common *hw_priv)
|
||||
{
|
||||
++hw_priv->debug->tx_burst;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_rx_burst(struct bes2600_common *hw_priv)
|
||||
{
|
||||
++hw_priv->debug->rx_burst;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_ba(struct bes2600_common *hw_priv,
|
||||
int ba_cnt, int ba_acc, int ba_cnt_rx,
|
||||
int ba_acc_rx)
|
||||
{
|
||||
hw_priv->debug->ba_cnt = ba_cnt;
|
||||
hw_priv->debug->ba_acc = ba_acc;
|
||||
hw_priv->debug->ba_cnt_rx = ba_cnt_rx;
|
||||
hw_priv->debug->ba_acc_rx = ba_acc_rx;
|
||||
}
|
||||
|
||||
int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len);
|
||||
|
||||
#else /* CONFIG_BES2600_DEBUGFS */
|
||||
|
||||
static inline int bes2600_debug_init_common(struct bes2600_common *hw_priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int bes2600_debug_init_priv(struct bes2600_common *hw_priv,
|
||||
struct bes2600_vif *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_release_common(struct bes2600_common *hw_priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_release_priv(struct bes2600_vif *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_txed(struct bes2600_vif *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_txed_agg(struct bes2600_vif *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_txed_multi(struct bes2600_vif *priv,
|
||||
int count)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_rxed(struct bes2600_vif *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_rxed_agg(struct bes2600_vif *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_tx_cache_miss(struct bes2600_vif *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_tx_align(struct bes2600_vif *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_tx_ttl(struct bes2600_vif *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_tx_burst(struct bes2600_common *hw_priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_rx_burst(struct bes2600_common *hw_priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_debug_ba(struct bes2600_common *hw_priv,
|
||||
int ba_cnt, int ba_acc, int ba_cnt_rx,
|
||||
int ba_acc_rx)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BES2600_DEBUGFS */
|
||||
|
||||
#endif /* BES2600_DEBUG_H_INCLUDED */
|
||||
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "bes2600.h"
|
||||
#include "epta_coex.h"
|
||||
#include "epta_request.h"
|
||||
#include "bes_log.h"
|
||||
|
||||
static bool coex_ps_en;
|
||||
static bool coex_fdd_mode; /* fdd or fdd hybrid */
|
||||
static uint8_t epta_conn_state; /* dafault invalid stat */
|
||||
static uint32_t epta_freeze_bitmap; /*bit0: conn, bit1: tp, bit2: tts */
|
||||
static int epta_freezed_wlan_duration[EPTA_FREEZE_MAX] = {0};
|
||||
static int epta_freezed_wlan_duration_cfg = 0;
|
||||
static int wlan_duration_cfg;
|
||||
static int bt_duration_cfg;
|
||||
static int hw_epta_enable_cfg; /* bit0~bit1: epta mode, bit7: prot flag, other bits: reserved */
|
||||
static int epta_ps_mode; /* 0 - normal mode, 1 - power save mode */
|
||||
static int epta_adjust_cnt;
|
||||
static int coex_bt_state;
|
||||
static uint32_t g_coex_mode;
|
||||
|
||||
#define EPTA_MODE_CFG (hw_epta_enable_cfg & 0x03)
|
||||
|
||||
/*
|
||||
* This function will send wsm command to set epta module with inputing parameters.
|
||||
* The inputing parameters may not be used really if epta_freeze_bitmap is not zero.
|
||||
* @hw_epta_enable: when hw_epta_enable is 1, use hardware pta, epta does not work,
|
||||
wlan_duration and bt_duration mean nothing.
|
||||
* @wlan_duration and bt_duration: need set hw_epta_enable to 0, use software epta.
|
||||
Allow 10~100000 us.
|
||||
* e.g.
|
||||
* coex_set_epta_params(20000, 20000, 0); means use software epta, wlan get 20ms, bt get 20ms.
|
||||
* coex_set_epta_params(20000, 20000, 1); means use hardware pta, hardware arbitrate.
|
||||
*/
|
||||
int coex_set_epta_params(struct bes2600_common *hw_priv, int wlan_duration, int bt_duration, int hw_epta_enable)
|
||||
{
|
||||
struct wsm_epta_msg msg;
|
||||
|
||||
if (hw_priv == NULL) {
|
||||
bes_devel("hw_priv is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bes_devel("set epta w:%d hw:%d wc:%d fbit:%x fw:%d\n", wlan_duration, hw_epta_enable,
|
||||
wlan_duration_cfg, epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
|
||||
|
||||
if (wlan_duration_cfg == wlan_duration && bt_duration_cfg == bt_duration && hw_epta_enable_cfg == hw_epta_enable) {
|
||||
bes_devel("same epta params\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wlan_duration_cfg = wlan_duration;
|
||||
bt_duration_cfg = bt_duration;
|
||||
hw_epta_enable_cfg = hw_epta_enable;
|
||||
|
||||
/* If in freeze mode, use freezed wlan duration; */
|
||||
if (epta_freeze_bitmap) {
|
||||
if (epta_freeze_bitmap & EPTA_FREEZE_SCANNING) {
|
||||
bes_devel("skip bt state in wifi scanning");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
if new wlan duration is more than freezed one, update fw settings with new duration
|
||||
if new wlan duration is less than freezed one, update fw settings with freezed duration
|
||||
*/
|
||||
if (wlan_duration < epta_freezed_wlan_duration_cfg) {
|
||||
bes_devel("epta_freeze_bitmap:0x%x, wlan:%u < %u",
|
||||
epta_freeze_bitmap, wlan_duration, epta_freezed_wlan_duration_cfg);
|
||||
wlan_duration = epta_freezed_wlan_duration_cfg;
|
||||
bt_duration = EPTA_FREEZE_TDD_PERIOD - epta_freezed_wlan_duration_cfg;
|
||||
}
|
||||
}
|
||||
|
||||
/* epta disconnect status,force use HW epta */
|
||||
if (epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED && hw_epta_enable == 0)
|
||||
hw_epta_enable = 1;
|
||||
|
||||
msg.wlan_duration = wlan_duration;
|
||||
msg.bt_duration = bt_duration;
|
||||
msg.hw_epta_enable = hw_epta_enable;
|
||||
|
||||
return wsm_epta_cmd(hw_priv, &msg);
|
||||
}
|
||||
|
||||
static int coex_epta_freeze_update(struct bes2600_common *hw_priv)
|
||||
{
|
||||
struct wsm_epta_msg msg;
|
||||
int i = 0;
|
||||
int max_freeze = 0;
|
||||
|
||||
if (hw_priv == NULL) {
|
||||
bes_devel("hw_priv is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bes_devel("epta update. bitmap:0x%x. fw:%d",
|
||||
epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
|
||||
|
||||
bes_devel("%s, %d", __FUNCTION__, hw_epta_enable_cfg);
|
||||
|
||||
/* Exit freeze mode and recover the configuration of epta module; */
|
||||
if (epta_freeze_bitmap == 0) {
|
||||
epta_freezed_wlan_duration_cfg = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Enter freeze mode and set the configuration of the epta module; */
|
||||
/* select max wifi freeze value */
|
||||
for (i = 0; i < EPTA_FREEZE_MAX; i++) {
|
||||
if (max_freeze < epta_freezed_wlan_duration[i])
|
||||
max_freeze = epta_freezed_wlan_duration[i];
|
||||
}
|
||||
|
||||
/* same cfg don't change it */
|
||||
if (max_freeze == epta_freezed_wlan_duration_cfg)
|
||||
return 0;
|
||||
|
||||
epta_freezed_wlan_duration_cfg = max_freeze;
|
||||
|
||||
/* only support epta_freezed_wlan_duration_cfg > wlan_duration_cfg */
|
||||
// wlan_duration_cfg may be override by wsm_epta_cmd
|
||||
// if (max_freeze <= wlan_duration_cfg) {
|
||||
// return 0;
|
||||
// }
|
||||
msg.wlan_duration = max_freeze;
|
||||
msg.bt_duration = EPTA_FREEZE_TDD_PERIOD - max_freeze;
|
||||
msg.hw_epta_enable = 0x00;
|
||||
|
||||
return wsm_epta_cmd(hw_priv, &msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* coex_epta_freeze
|
||||
* freeze coex duration for specific senario
|
||||
* @wlan_duration: 20~100ms
|
||||
* @type:
|
||||
* EPTA_FREEZE_CONENCTING,
|
||||
* EPTA_FREEZE_TTS,
|
||||
* EPTA_FREEZE_THP,
|
||||
* note: the value of wlan_duration should be less than 100000.
|
||||
*/
|
||||
static int coex_epta_freeze(struct bes2600_common *hw_priv, int wlan_duration, uint32_t type)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
epta_freeze_bitmap |= type;
|
||||
|
||||
for (i = 0; i < EPTA_FREEZE_MAX; ++i) {
|
||||
if (type & (1 << i))
|
||||
epta_freezed_wlan_duration[i] = wlan_duration;
|
||||
}
|
||||
/* reset epta value */
|
||||
epta_ps_mode = 0;
|
||||
epta_adjust_cnt = 0;
|
||||
|
||||
return coex_epta_freeze_update(hw_priv);
|
||||
}
|
||||
|
||||
static int coex_epta_recover_update(struct bes2600_common *hw_priv)
|
||||
{
|
||||
struct wsm_epta_msg msg;
|
||||
int i = 0;
|
||||
int max_freeze = 0;
|
||||
|
||||
if (hw_priv == NULL) {
|
||||
bes_devel("hw_priv is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bes_devel("epta update. bitmap:0x%x. fw:%d",
|
||||
epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
|
||||
|
||||
bes_devel("%s, %d", __FUNCTION__, hw_epta_enable_cfg);
|
||||
|
||||
/* Exit freeze mode and recover the configuration of epta module; */
|
||||
if (epta_freeze_bitmap == 0) {
|
||||
if (epta_freezed_wlan_duration_cfg == 0)
|
||||
return -1;
|
||||
epta_freezed_wlan_duration_cfg = 0;
|
||||
// coex_set_epta_params will skip epta:0 after got_ip(use connected epta3)
|
||||
msg.wlan_duration = wlan_duration_cfg;
|
||||
msg.bt_duration = bt_duration_cfg;
|
||||
msg.hw_epta_enable = hw_epta_enable_cfg;
|
||||
return wsm_epta_cmd(hw_priv, &msg);
|
||||
}
|
||||
|
||||
/* Enter freeze mode and set the configuration of the epta module; */
|
||||
/* select max wifi freeze value */
|
||||
for (i = 0; i < EPTA_FREEZE_MAX; i++) {
|
||||
if (max_freeze < epta_freezed_wlan_duration[i])
|
||||
max_freeze = epta_freezed_wlan_duration[i];
|
||||
}
|
||||
|
||||
epta_freezed_wlan_duration_cfg = max_freeze;
|
||||
msg.wlan_duration = max_freeze;
|
||||
msg.bt_duration = EPTA_FREEZE_TDD_PERIOD - max_freeze;
|
||||
msg.hw_epta_enable = hw_epta_enable_cfg;
|
||||
|
||||
return wsm_epta_cmd(hw_priv, &msg);
|
||||
}
|
||||
|
||||
static int coex_epta_recover(struct bes2600_common *hw_priv, uint32_t type)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (epta_freeze_bitmap == 0)
|
||||
return 0;
|
||||
|
||||
if (type == EPTA_FREEZE_ALL) {
|
||||
for (i = 0; i < EPTA_FREEZE_MAX; i++) {
|
||||
epta_freezed_wlan_duration[i] = 0;
|
||||
}
|
||||
epta_freeze_bitmap = 0;
|
||||
} else {
|
||||
epta_freeze_bitmap &= (~type);
|
||||
for (i = 0; i < EPTA_FREEZE_MAX; ++i) {
|
||||
if (type & (1 << i))
|
||||
epta_freezed_wlan_duration[i] = 0;
|
||||
}
|
||||
}
|
||||
return coex_epta_recover_update(hw_priv);
|
||||
}
|
||||
|
||||
static int coex_epta_ps(struct bes2600_common *hw_priv, uint8_t enable)
|
||||
{
|
||||
struct wsm_epta_msg msg;
|
||||
if (hw_priv == NULL) {
|
||||
bes_devel("hw_priv is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (enable && !epta_ps_mode) {
|
||||
epta_ps_mode = 1;
|
||||
msg.wlan_duration = EPTA_PS_WLAN_DURATION;
|
||||
msg.bt_duration = EPTA_PS_BT_DURATION;
|
||||
msg.hw_epta_enable = 0;
|
||||
return wsm_epta_cmd(hw_priv, &msg);
|
||||
} else if (!enable && epta_ps_mode) {
|
||||
epta_ps_mode = 0;
|
||||
msg.wlan_duration = wlan_duration_cfg;
|
||||
msg.bt_duration = bt_duration_cfg;
|
||||
msg.hw_epta_enable = 0;
|
||||
return wsm_epta_cmd(hw_priv, &msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coex_epta_set_connect(struct bes2600_common *hw_priv, int wlan_duration, int bt_duration, int epta)
|
||||
{
|
||||
struct wsm_epta_msg msg;
|
||||
if (!hw_priv) {
|
||||
bes_devel("hw_priv is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
if (epta != 4) {
|
||||
msg.wlan_duration = (wlan_duration_cfg >= wlan_duration) ? wlan_duration_cfg : wlan_duration;
|
||||
msg.bt_duration = (wlan_duration_cfg >= wlan_duration) ? bt_duration_cfg : bt_duration;
|
||||
} else {
|
||||
msg.wlan_duration = wlan_duration;
|
||||
msg.bt_duration = bt_duration;
|
||||
}
|
||||
msg.hw_epta_enable = epta;
|
||||
return wsm_epta_cmd(hw_priv, &msg);
|
||||
}
|
||||
|
||||
static void coex_set_wifi_state_to_fw(struct bes2600_common *hw_priv, uint8_t epta_conn_state)
|
||||
{
|
||||
static uint8_t last_state;
|
||||
|
||||
switch (epta_conn_state) {
|
||||
case EPTA_STATE_WIFI_DISCONNECTED:
|
||||
case EPTA_STATE_WIFI_CONNECTED:
|
||||
if (last_state != epta_conn_state) {
|
||||
last_state = epta_conn_state;
|
||||
break;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s state%u\r", __func__, last_state);
|
||||
|
||||
switch (last_state) {
|
||||
case EPTA_STATE_WIFI_DISCONNECTED:
|
||||
wsm_wifi_status_cmd(hw_priv, (uint32_t)BWIFI_STATUS_IDLE);
|
||||
break;
|
||||
case EPTA_STATE_WIFI_CONNECTED:
|
||||
wsm_wifi_status_cmd(hw_priv, (uint32_t)BWIFI_STATUS_CONNECTED);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* TDD mode conn status change */
|
||||
void coex_set_wifi_conn(struct bes2600_common *hw_priv, uint8_t connect_status)
|
||||
{
|
||||
if (connect_status != EPTA_STATE_WIFI_SCAN_COMP) {
|
||||
if (epta_conn_state == connect_status) {
|
||||
bes_devel("same connect_status:%d\r", connect_status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect_status == EPTA_STATE_WIFI_GOT_IP) {
|
||||
if (epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED || epta_conn_state == EPTA_STATE_WIFI_CONNECTING) {
|
||||
bes_devel("ignore got ip in disconnected\r");
|
||||
return;
|
||||
}
|
||||
}
|
||||
epta_conn_state = connect_status;
|
||||
|
||||
bes_devel("%s connect_status=%d coex_mode=%d\r", __func__, connect_status, g_coex_mode);
|
||||
|
||||
coex_set_wifi_state_to_fw(hw_priv, epta_conn_state);
|
||||
|
||||
if (g_coex_mode & WIFI_COEX_MODE_FDD_BIT) {
|
||||
if (g_coex_mode & WIFI_COEX_MODE_FDD_HYBRID_BIT) {
|
||||
if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
|
||||
coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 3);
|
||||
} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
|
||||
coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
|
||||
/* HYBRID MODE: if wlan is disconnect, default use hw epta */
|
||||
coex_epta_set_connect(hw_priv, 100000, 0, 1);
|
||||
}
|
||||
} else {
|
||||
if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
|
||||
// set bt bad channel according to wifi channel
|
||||
if (hw_priv->channel->band == NL80211_BAND_2GHZ)
|
||||
wsm_epta_wifi_chan_cmd(hw_priv, hw_priv->channel->hw_value, hw_priv->ht_info.channel_type);
|
||||
} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
|
||||
coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
|
||||
coex_epta_set_connect(hw_priv, 100000, 0, 1);
|
||||
wsm_epta_wifi_chan_cmd(hw_priv, 0, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int wlan_tdd_duration = EPTA_TDD_CONNECT_WIFI;
|
||||
int bt_tdd_duration = EPTA_TDD_CONNECT_BT;
|
||||
|
||||
/* if wlan_duration_cfg> EPTA_TDD_CONNECT_WIFI; chose wlan_duration_cfg, in TDD mode */
|
||||
if (wlan_duration_cfg > EPTA_TDD_CONNECT_WIFI) {
|
||||
wlan_tdd_duration = wlan_duration_cfg;
|
||||
bt_tdd_duration = bt_duration_cfg;
|
||||
}
|
||||
if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
|
||||
coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 3);
|
||||
} else if (connect_status == EPTA_STATE_WIFI_SCANNING) {
|
||||
/* in scan status, only valid param 0, others wlan_tdd_duration,bt_tdd_duratio is invalid */
|
||||
coex_epta_freeze(hw_priv, wlan_duration_cfg, EPTA_FREEZE_SCANNING);
|
||||
} else if (connect_status == EPTA_STATE_WIFI_SCAN_COMP) {
|
||||
coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 0);
|
||||
} else if (connect_status == EPTA_STATE_WIFI_GOT_IP) {
|
||||
coex_epta_set_connect(hw_priv, 20000, 80000, 3);
|
||||
coex_epta_recover(hw_priv, EPTA_FREEZE_SCANNING | EPTA_FREEZE_CONNECTING);
|
||||
} else if (connect_status == EPTA_STATE_WIFI_CONNECTING) {
|
||||
coex_epta_freeze(hw_priv, wlan_tdd_duration, EPTA_FREEZE_CONNECTING);
|
||||
if (coex_fdd_mode == 0 && bt_duration_cfg > 50000) //bt audio
|
||||
coex_epta_set_connect(hw_priv, 30000, 15000, 4);
|
||||
} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
|
||||
coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
|
||||
/* TDD MODE: if wlan is disconnect, default use hw epta */
|
||||
// coex_epta_set_connect(hw_priv, 100000, 0, 1);
|
||||
} else {
|
||||
/* do nothing */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool coex_is_wifi_inactive()
|
||||
{
|
||||
bes_devel("%s, epta_conn_state:%d", __FUNCTION__, epta_conn_state);
|
||||
return epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED;
|
||||
}
|
||||
/*
|
||||
* coex_set_epta_tts
|
||||
* @tts_state: 0 - tts start, 1 - tts end
|
||||
*/
|
||||
void coex_set_epta_tts(struct bes2600_common *hw_priv, uint32_t tts_state)
|
||||
{
|
||||
if (EPTA_MODE_CFG == 0) {
|
||||
if (tts_state) {
|
||||
coex_epta_recover(hw_priv, EPTA_FREEZE_TTS);
|
||||
} else {
|
||||
coex_epta_freeze(hw_priv, 50000, EPTA_FREEZE_TTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_set_epta_thp(struct bes2600_common *hw_priv, uint32_t total_bps)
|
||||
{
|
||||
int epta_adjust_inv = 3;
|
||||
int wlan_tp_low;
|
||||
|
||||
if (coex_fdd_mode && epta_conn_state >= EPTA_STATE_WIFI_GOT_IP)
|
||||
return;
|
||||
|
||||
//bes_devel("coex_set_epta_thp %d bt_state %d epta_freeze_bitmap=%d epta_adjust_cnt=%d coex_ps_en=%d fw=%d\n",
|
||||
// total_bps, coex_bt_state, epta_freeze_bitmap, epta_adjust_cnt, coex_ps_en, epta_freezed_wlan_duration_cfg);
|
||||
|
||||
/* sniffer mode always work with ble and tts, need adjust more quickly */
|
||||
// wlan_tp_low = netdev_sniffer_get_stat() ? EPTA_WLAN_TP_LOW : 500;
|
||||
wlan_tp_low = 500;
|
||||
|
||||
/* only need adjust in sw epta mode */
|
||||
if (EPTA_MODE_CFG != 0)
|
||||
return;
|
||||
|
||||
if ((coex_bt_state == 0) && (total_bps > EPTA_WLAN_TP_HIGH)) {
|
||||
coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_HIGH, EPTA_FREEZE_THP);
|
||||
} else if (total_bps > EPTA_WLAN_TP_MEDIUM) {
|
||||
coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_MEDIUM, EPTA_FREEZE_THP);
|
||||
} else if (total_bps > wlan_tp_low) {
|
||||
coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_LOW, EPTA_FREEZE_THP);
|
||||
} else {
|
||||
if ((epta_freeze_bitmap & (1 << EPTA_FREEZE_THP)) &&
|
||||
(((++epta_adjust_cnt) % epta_adjust_inv) == 0)) {
|
||||
// recover if wifi idle 3 secs
|
||||
epta_adjust_cnt = 0;
|
||||
coex_epta_recover(hw_priv, EPTA_FREEZE_THP);
|
||||
} else if (coex_ps_en && !epta_freeze_bitmap) {
|
||||
if ((total_bps > EPTA_WLAN_TP_PS)) {
|
||||
coex_epta_ps(hw_priv, 0);
|
||||
epta_adjust_cnt = 0;
|
||||
} else if ((total_bps < EPTA_WLAN_TP_PS) &&
|
||||
(((++epta_adjust_cnt) % epta_adjust_inv) == 0)) {
|
||||
coex_epta_ps(hw_priv, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// void coex_band_update(struct bes2600_common *hw_priv, enum nl80211_band band)
|
||||
// {
|
||||
// bes_devel("coex_band_update band:%u\n", (uint32_t)band);
|
||||
// }
|
||||
|
||||
void coex_rssi_update(struct bes2600_common *hw_priv, int rssi, int channel, int connected)
|
||||
{
|
||||
bool fdd_en = 0;
|
||||
|
||||
if (g_coex_mode == 0 || // tdd
|
||||
g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) {
|
||||
|
||||
bes_devel("coex_rssi_update rssi:%d, ch:%d, con:%d\n",
|
||||
rssi, channel, connected);
|
||||
if (channel > 14) {
|
||||
fdd_en = 1;
|
||||
///TODO: HYBRID mode
|
||||
// } else if (rssi >= COEX_FDD_RSSI_THR) {
|
||||
// fdd_en = 1;
|
||||
// } else if (rssi < COEX_TDD_RSSI_THR) {
|
||||
// fdd_en = 0;
|
||||
}
|
||||
|
||||
if (fdd_en != coex_fdd_mode) {
|
||||
coex_fdd_mode = fdd_en;
|
||||
coex_set_epta_params(hw_priv, wlan_duration_cfg, bt_duration_cfg, hw_epta_enable_cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void coex_set_bt_state(struct bes2600_common *hw_priv, int state)
|
||||
{
|
||||
bes_devel("coex_set_bt_state %d\n", state);
|
||||
coex_bt_state = state;
|
||||
}
|
||||
|
||||
void coex_peroid_handle(struct bes2600_common *hw_priv, int connected, int rssi, int channel, uint32_t tp)
|
||||
{
|
||||
if (connected) {
|
||||
/*
|
||||
* Adjust wifi/bt duration dynamically according to throughput for a better performance;
|
||||
*/
|
||||
coex_set_epta_thp(hw_priv, tp);
|
||||
}
|
||||
|
||||
if (g_coex_mode == 0 || // tdd
|
||||
g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) {
|
||||
coex_rssi_update(hw_priv, rssi, channel, connected);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set fdd or fdd hybrid
|
||||
* 1: fdd
|
||||
* 0: fddhybrid
|
||||
*/
|
||||
void coex_set_fdd_mode(bool fdd_mode)
|
||||
{
|
||||
if (g_coex_mode == 0 || // tdd
|
||||
g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) { //hybrid
|
||||
bes_devel("%s, %d", __FUNCTION__, fdd_mode);
|
||||
coex_fdd_mode = fdd_mode;
|
||||
}
|
||||
}
|
||||
|
||||
bool coex_is_fdd_mode()
|
||||
{
|
||||
bes_devel("%s, %d", __FUNCTION__, coex_fdd_mode);
|
||||
return coex_fdd_mode;
|
||||
}
|
||||
|
||||
bool coex_is_bt_a2dp()
|
||||
{
|
||||
bes_devel("%s, coex_bt_state:%d", __FUNCTION__, coex_bt_state);
|
||||
return coex_bt_state == 3;
|
||||
}
|
||||
|
||||
bool coex_is_bt_inactive()
|
||||
{
|
||||
bes_devel("%s, coex_bt_state:%d", __FUNCTION__, coex_bt_state);
|
||||
return coex_bt_state == 0;
|
||||
}
|
||||
|
||||
int coex_init_mode(struct bes2600_common *hw_priv, int coex_mode)
|
||||
{
|
||||
bes_devel("coex_init_mode coex_mode %d\n", coex_mode);
|
||||
|
||||
g_coex_mode = coex_mode;
|
||||
|
||||
if (coex_mode & WIFI_COEX_MODE_FDD_BIT)
|
||||
coex_fdd_mode = true;
|
||||
else
|
||||
coex_fdd_mode = false;
|
||||
|
||||
coex_wifi_bt_ts_thread_init(hw_priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coex_deinit_mode(struct bes2600_common *hw_priv)
|
||||
{
|
||||
bes_devel("coex_deinit_mode\n");
|
||||
|
||||
coex_wifi_bt_ts_thread_deinit(hw_priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coex_start(struct bes2600_common *hw_priv)
|
||||
{
|
||||
bes_devel("%s\n", __FUNCTION__);
|
||||
|
||||
coex_ps_en = false;
|
||||
if (g_coex_mode & WIFI_COEX_MODE_FDD_BIT)
|
||||
coex_fdd_mode = true;
|
||||
else
|
||||
coex_fdd_mode = false;
|
||||
epta_conn_state = 0xff;
|
||||
epta_freeze_bitmap = 0;
|
||||
epta_freezed_wlan_duration_cfg = 0;
|
||||
wlan_duration_cfg = 20000;
|
||||
bt_duration_cfg = 80000;
|
||||
hw_epta_enable_cfg = 0;
|
||||
epta_ps_mode = 0;
|
||||
epta_adjust_cnt = 0;
|
||||
coex_bt_state = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coex_stop(struct bes2600_common *hw_priv)
|
||||
{
|
||||
bes_devel("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __EPTA_COEX_H__
|
||||
#define __EPTA_COEX_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
#include "bes2600.h"
|
||||
|
||||
#define WIFI_COEX_MODE_FDD_BIT (1<<0)
|
||||
#define WIFI_COEX_MODE_FDD_HYBRID_BIT (1<<1)
|
||||
|
||||
enum bwifi_epta_state {
|
||||
EPTA_STATE_WIFI_DISCONNECTED = 0,
|
||||
EPTA_STATE_WIFI_SCANNING = 1,
|
||||
EPTA_STATE_WIFI_SCAN_COMP = 2,
|
||||
EPTA_STATE_WIFI_CONNECTING = 3,
|
||||
EPTA_STATE_WIFI_CONNECTED = 4,
|
||||
EPTA_STATE_WIFI_GOT_IP = 5,
|
||||
EPTA_STATE_WIFI_TTS_START = 6,
|
||||
EPTA_STATE_WIFI_TTS_END = 7,
|
||||
|
||||
EPTA_STATE_NUM
|
||||
};
|
||||
|
||||
/* max FREEZE BIT */
|
||||
#define EPTA_FREEZE_MAX 32
|
||||
typedef enum {
|
||||
EPTA_FREEZE_SCANNING = 1 << 0,
|
||||
EPTA_FREEZE_CONNECTING = 1 << 1,
|
||||
EPTA_FREEZE_TTS = 1 << 2,
|
||||
EPTA_FREEZE_THP = 1 << 3,
|
||||
/* clear all FREEZE BIT */
|
||||
EPTA_FREEZE_ALL
|
||||
} EPTA_FREEZE_TYPE_T;
|
||||
|
||||
#ifndef COEX_TDD_RSSI_THR
|
||||
#define COEX_TDD_RSSI_THR (-15)
|
||||
#endif
|
||||
#ifndef COEX_FDD_RSSI_THR
|
||||
#define COEX_FDD_RSSI_THR (-10)
|
||||
#endif
|
||||
|
||||
#define EPTA_PS_WLAN_DURATION (20000)
|
||||
#define EPTA_PS_BT_DURATION (80000)
|
||||
#define EPTA_ADJUST_WLAN_DURATION_HIGH (80000)
|
||||
#define EPTA_ADJUST_WLAN_DURATION_MEDIUM (55000)
|
||||
#define EPTA_ADJUST_WLAN_DURATION_LOW (40000)
|
||||
#define EPTA_WLAN_TP_HIGH (4000)
|
||||
#define EPTA_WLAN_TP_MEDIUM (2000)
|
||||
#define EPTA_WLAN_TP_LOW (100)
|
||||
#define EPTA_WLAN_TP_PS (50)
|
||||
#define EPTA_FREEZE_TDD_PERIOD (102400)
|
||||
#define EPTA_TDD_CONNECT_WIFI (50000)
|
||||
#define EPTA_TDD_CONNECT_BT (50000)
|
||||
|
||||
int coex_set_epta_params(struct bes2600_common *hw_priv, int wlan_duraiton, int bt_duration, int hw_epta_enable);
|
||||
void coex_peroid_handle(struct bes2600_common *hw_priv, int connected, int rssi, int channel, uint32_t tp);
|
||||
void coex_set_bt_state(struct bes2600_common *hw_priv, int state);
|
||||
void coex_set_wifi_conn(struct bes2600_common *hw_priv, uint8_t connect_status);
|
||||
void coex_set_epta_tts(struct bes2600_common *hw_priv, uint32_t tts_state);
|
||||
int coex_init_mode(struct bes2600_common *hw_priv, int coex_mode);
|
||||
int coex_deinit_mode(struct bes2600_common *hw_priv);
|
||||
int coex_start(struct bes2600_common *hw_priv);
|
||||
int coex_stop(struct bes2600_common *hw_priv);
|
||||
|
||||
void coex_rssi_update(struct bes2600_common *hw_priv, int rssi, int channel, int connected);
|
||||
// void coex_band_update(struct bes2600_common *hw_priv, enum nl80211_band band);
|
||||
bool coex_is_fdd_mode(void);
|
||||
void coex_set_fdd_mode(bool fdd_mode);
|
||||
bool coex_is_bt_a2dp(void);
|
||||
bool coex_is_bt_inactive(void);
|
||||
bool coex_is_wifi_inactive(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,651 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "epta_coex.h"
|
||||
#include "epta_request.h"
|
||||
#include "bes_log.h"
|
||||
|
||||
#define EPTA_PERIOD_TIME 102400
|
||||
|
||||
#define BT_TIME_MAX 80000
|
||||
|
||||
#define BT_SHUTDOWN_TIME 0
|
||||
#define BT_SHUTDOWN_MIN_TIME 0
|
||||
#define BT_DISCONNECTED_TIME 0
|
||||
#define BT_DISCONNECTED_MIN_TIME 0
|
||||
#define BT_CONNECTING_TIME 60000
|
||||
#define BT_CONNECTING_MIN_TIME 0
|
||||
#define BT_CONNECTED_TIME 0
|
||||
#define BT_CONNECTED_MIN_TIME 25000
|
||||
#define BT_CONNECTED_SNIFF_TIME 0
|
||||
#define BT_CONNECTED_SNIFF_MIN_TIME 20000
|
||||
|
||||
#define BT_BOTHSCAN_DISABLE_TIME 0
|
||||
#define BT_BOTHSCAN_DISABLE_MIN_TIME 0
|
||||
#define BT_BOTHSCAN_ENABLE_TIME 0
|
||||
#define BT_BOTHSCAN_ENABLE_MIN_TIME 20000
|
||||
#define BT_PSCAN_ENABLE_TIME 0
|
||||
#define BT_PSCAN_ENABLE_MIN_TIME 20000
|
||||
#define BT_ISCAN_ENABLE_TIME 0
|
||||
#define BT_ISCAN_ENABLE_MIN_TIME 20000
|
||||
|
||||
#define BT_AUDIO_NONE_TIME 0
|
||||
#define BT_AUDIO_NONE_MIN_TIME 0
|
||||
#define BT_AUDIO_A2DP_TIME 60000
|
||||
#define BT_AUDIO_A2DP_MIN_TIME 0
|
||||
#define BT_AUDIO_SCO_TIME 80000
|
||||
#define BT_AUDIO_SCO_MIN_TIME 0
|
||||
|
||||
#define BT_INQ_START_TIME 20000
|
||||
#define BT_INQ_START_MIN_TIME 30000
|
||||
#define BT_INQ_STOP_TIME 0
|
||||
#define BT_INQ_STOP_MIN_TIME 0
|
||||
|
||||
#define BT_LE_SCAN_START_TIME 20000
|
||||
#define BT_LE_SCAN_START_MIN_TIME 30000
|
||||
#define BT_LE_SCAN_STOP_TIME 0
|
||||
#define BT_LE_SCAN_STOP_MIN_TIME 0
|
||||
|
||||
#define BT_LE_ADV_START_TIME 20000
|
||||
#define BT_LE_ADV_START_MIN_TIME 40000
|
||||
#define BT_LE_ADV_STOP_TIME 0
|
||||
#define BT_LE_ADV_STOP_MIN_TIME 0
|
||||
|
||||
#define BT_LE_CONNECTED_TIME 0
|
||||
#define BT_LE_CONNECTED_MIN_TIME 50000
|
||||
#define BT_LE_DISCONNECTED_TIME 0
|
||||
#define BT_LE_DISCONNECTED_MIN_TIME 0
|
||||
|
||||
// BIT[2:0]
|
||||
#define BT_REQUEST_STATUS_SHIFT 0
|
||||
#define BT_REQUEST_STATUS_MASK (0x7 << BT_REQUEST_STATUS_SHIFT)
|
||||
#define BT_REQUEST_STATUS_VALUE(n) (((n) & BT_REQUEST_STATUS_MASK) >> BT_REQUEST_STATUS_SHIFT)
|
||||
|
||||
// BIT[5:3]
|
||||
#define BT_REQUEST_SCAN_SHIFT 3
|
||||
#define BT_REQUEST_SCAN_MASK (0x7 << BT_REQUEST_SCAN_SHIFT)
|
||||
#define BT_REQUEST_SCAN_VALUE(n) (((n) & BT_REQUEST_SCAN_MASK) >> BT_REQUEST_SCAN_SHIFT)
|
||||
|
||||
// BIT[8:6]
|
||||
#define BT_REQUEST_AUDIO_SHIFT 6
|
||||
#define BT_REQUEST_AUDIO_MASK (0x7 << BT_REQUEST_AUDIO_SHIFT)
|
||||
#define BT_REQUEST_AUDIO_VALUE(n) (((n) & BT_REQUEST_AUDIO_MASK) >> BT_REQUEST_AUDIO_SHIFT)
|
||||
|
||||
// BIT[10:9]
|
||||
#define BT_REQUEST_INQ_SHIFT 9
|
||||
#define BT_REQUEST_INQ_MASK (0x3 << BT_REQUEST_INQ_SHIFT)
|
||||
#define BT_REQUEST_INQ_VALUE(n) (((n) & BT_REQUEST_INQ_MASK) >> BT_REQUEST_INQ_SHIFT)
|
||||
|
||||
// BIT[12:11]
|
||||
#define BT_REQUEST_LE_SCAN_SHIFT 11
|
||||
#define BT_REQUEST_LE_SCAN_MASK (0x3 << BT_REQUEST_LE_SCAN_SHIFT)
|
||||
#define BT_REQUEST_LE_SCAN_VALUE(n) (((n) & BT_REQUEST_LE_SCAN_MASK) >> BT_REQUEST_LE_SCAN_SHIFT)
|
||||
|
||||
// BIT[15:13]
|
||||
#define BT_REQUEST_LE_ADV_SHIFT 13
|
||||
#define BT_REQUEST_LE_ADV_MASK (0x7 << BT_REQUEST_LE_ADV_SHIFT)
|
||||
#define BT_REQUEST_LE_ADV_VALUE(n) (((n) & BT_REQUEST_LE_ADV_MASK) >> BT_REQUEST_LE_ADV_SHIFT)
|
||||
|
||||
// BIT[17:16]
|
||||
#define BT_REQUEST_LE_STATUS_SHIFT 16
|
||||
#define BT_REQUEST_LE_STATUS_MASK (0x3 << BT_REQUEST_LE_STATUS_SHIFT)
|
||||
#define BT_REQUEST_LE_STATUS_VALUE(n) (((n) & BT_REQUEST_LE_STATUS_MASK) >> BT_REQUEST_LE_STATUS_SHIFT)
|
||||
|
||||
typedef enum {
|
||||
BWIFI_BT_STATUS_SHUTDOWN = 0,
|
||||
BWIFI_BT_STATUS_DISCONNECTED = 1,
|
||||
BWIFI_BT_STATUS_CONNECTING = 2,
|
||||
BWIFI_BT_STATUS_CONNECTED_SNIFF = 3,
|
||||
BWIFI_BT_STATUS_CONNECTED = 4,
|
||||
} BWIFI_BT_STATUS_T;
|
||||
|
||||
typedef enum {
|
||||
BWIFI_BT_BOTHSCAN_DISABLE = 0,
|
||||
BWIFI_BT_BOTHSCAN_ENABLE = 1,
|
||||
BWIFI_BT_PSCAN_ENABLE = 2,
|
||||
BWIFI_BT_ISCAN_ENABLE = 3,
|
||||
} BWIFI_BT_SCAN_T;
|
||||
|
||||
typedef enum {
|
||||
BWIFI_BT_AUDIO_NONE = 0,
|
||||
BWIFI_BT_AUDIO_A2DP = 1,
|
||||
BWIFI_BT_AUDIO_SCO = 2,
|
||||
} BWIFI_BT_AUDIO_T;
|
||||
|
||||
typedef enum {
|
||||
BWIFI_BT_INQ_STOP = 0,
|
||||
BWIFI_BT_INQ_START = 1,
|
||||
} BWIFI_BT_INQ_T;
|
||||
|
||||
typedef enum {
|
||||
BWIFI_LE_SCAN_STOP = 0,
|
||||
BWIFI_LE_SCAN_START = 1,
|
||||
} BWIFI_BT_LE_SCAN_T;
|
||||
|
||||
typedef enum {
|
||||
BWIFI_LE_ADV_STOP = 0,
|
||||
BWIFI_LE_ADV_START = 1,
|
||||
} BWIFI_BT_LE_ADV_T;
|
||||
|
||||
typedef enum {
|
||||
BWIFI_LE_DISCONNECTED = 0,
|
||||
BWIFI_LE_CONNECTED = 1,
|
||||
} BWIFI_BT_LE_STATUS_T;
|
||||
|
||||
enum COEX_BT_OPER_T {
|
||||
COEX_BT_OPER_STATUS,
|
||||
COEX_BT_OPER_SCAN,
|
||||
COEX_BT_OPER_AUDIO,
|
||||
COEX_BT_OPER_INQ,
|
||||
COEX_BT_OPER_LE_SCAN,
|
||||
COEX_BT_OPER_LE_ADV,
|
||||
COEX_BT_OPER_LE_STATUS,
|
||||
|
||||
COEX_BT_OPER_NUM,
|
||||
};
|
||||
|
||||
union COEX_BT_OPER_TYPE_T {
|
||||
BWIFI_BT_STATUS_T status;
|
||||
BWIFI_BT_SCAN_T scan;
|
||||
BWIFI_BT_AUDIO_T audio;
|
||||
BWIFI_BT_INQ_T inq;
|
||||
BWIFI_BT_LE_SCAN_T le_scan;
|
||||
BWIFI_BT_LE_ADV_T le_adv;
|
||||
BWIFI_BT_LE_STATUS_T le_status;
|
||||
};
|
||||
|
||||
struct COEX_BT_OPER_TIME_T {
|
||||
enum COEX_BT_OPER_T oper;
|
||||
union COEX_BT_OPER_TYPE_T type;
|
||||
uint32_t time;
|
||||
uint32_t min_time;
|
||||
};
|
||||
|
||||
struct COEX_BT_OPER_TIME_T g_coex_bt_oper[COEX_BT_OPER_NUM];
|
||||
|
||||
static void coex_bt_time_init(void)
|
||||
{
|
||||
memset(g_coex_bt_oper, 0, sizeof(g_coex_bt_oper));
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_SHUTDOWN;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_DISABLE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_STOP;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_STOP;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_STOP;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_DISCONNECTED;
|
||||
}
|
||||
|
||||
static uint32_t coex_calc_bt_time(void)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t time = 0, min_time = 0;
|
||||
|
||||
for (i = 0; i < COEX_BT_OPER_NUM; ++i) {
|
||||
time += g_coex_bt_oper[i].time;
|
||||
if (min_time < g_coex_bt_oper[i].min_time)
|
||||
min_time = g_coex_bt_oper[i].min_time;
|
||||
}
|
||||
|
||||
bes_devel("%s time:%u, min_time:%u", __func__, time, min_time);
|
||||
time = time < min_time ? min_time : time;
|
||||
return time < BT_TIME_MAX ? time : BT_TIME_MAX;
|
||||
}
|
||||
|
||||
void coex_calc_wifi_scan_time(uint32_t *min_chan, uint32_t *max_chan)
|
||||
{
|
||||
uint32_t time = coex_calc_bt_time();
|
||||
|
||||
if (time == 0) {
|
||||
*min_chan = 100;
|
||||
*max_chan = 100;
|
||||
} else if (time < 40000) {
|
||||
*min_chan = 50;
|
||||
*max_chan = 110;
|
||||
} else if (time < 60000) {
|
||||
*min_chan = 40;
|
||||
*max_chan = 110;
|
||||
} else if (time < 80000) {
|
||||
*min_chan = 30;
|
||||
*max_chan = 120;
|
||||
} else {
|
||||
*min_chan = 30;
|
||||
*max_chan = 130;
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_bt_state_notify(struct bes2600_common *hw_priv)
|
||||
{
|
||||
int32_t wifi_dur, bt_dur, mode;
|
||||
|
||||
bt_dur = coex_calc_bt_time();
|
||||
wifi_dur = EPTA_PERIOD_TIME - bt_dur;
|
||||
mode = 0;
|
||||
coex_set_epta_params(hw_priv, wifi_dur, bt_dur, mode);
|
||||
}
|
||||
|
||||
static void coex_bt_oper_status(struct bes2600_common *hw_priv, BWIFI_BT_STATUS_T type)
|
||||
{
|
||||
bes_devel("%s type:%d", __func__, type);
|
||||
|
||||
switch (type) {
|
||||
case BWIFI_BT_STATUS_SHUTDOWN:
|
||||
coex_bt_time_init();
|
||||
break;
|
||||
case BWIFI_BT_STATUS_DISCONNECTED:
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_DISCONNECTED;
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_DISCONNECTED_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_DISCONNECTED_MIN_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_STATUS_CONNECTING:
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTING;
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTING_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTING_MIN_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_STATUS_CONNECTED:
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTED;
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTED_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTED_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_STATUS_CONNECTED_SNIFF:
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTED_SNIFF;
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTED_SNIFF_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTED_SNIFF_MIN_TIME;
|
||||
break;
|
||||
default:
|
||||
bes_err("%s type error:%d", __func__, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_bt_oper_scan(struct bes2600_common *hw_priv, BWIFI_BT_SCAN_T type)
|
||||
{
|
||||
bes_devel("%s type:%d", __func__, type);
|
||||
|
||||
switch (type) {
|
||||
case BWIFI_BT_BOTHSCAN_DISABLE:
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_DISABLE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_BOTHSCAN_DISABLE_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_BOTHSCAN_DISABLE_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_BOTHSCAN_ENABLE:
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_ENABLE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_BOTHSCAN_ENABLE_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_BOTHSCAN_ENABLE_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_PSCAN_ENABLE:
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_PSCAN_ENABLE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_PSCAN_ENABLE_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_PSCAN_ENABLE_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_ISCAN_ENABLE:
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_ISCAN_ENABLE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_ISCAN_ENABLE_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_ISCAN_ENABLE_MIN_TIME;
|
||||
break;
|
||||
default:
|
||||
bes_err("%s type error:%d", __func__, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_bt_oper_audio(struct bes2600_common *hw_priv, BWIFI_BT_AUDIO_T type)
|
||||
{
|
||||
bes_devel("%s type:%d", __func__, type);
|
||||
|
||||
switch (type) {
|
||||
case BWIFI_BT_AUDIO_NONE:
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_AUDIO_A2DP:
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_A2DP;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_A2DP_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_A2DP_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_AUDIO_SCO:
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_SCO;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_SCO_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_SCO_MIN_TIME;
|
||||
break;
|
||||
default:
|
||||
bes_err("%s type error:%d", __func__, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_bt_oper_inq(struct bes2600_common *hw_priv, BWIFI_BT_INQ_T type)
|
||||
{
|
||||
bes_devel("%s type:%d", __func__, type);
|
||||
|
||||
switch (type) {
|
||||
case BWIFI_BT_INQ_START:
|
||||
g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_START;
|
||||
g_coex_bt_oper[COEX_BT_OPER_INQ].time = BT_INQ_START_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_INQ].min_time = BT_INQ_START_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_BT_INQ_STOP:
|
||||
g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_STOP;
|
||||
g_coex_bt_oper[COEX_BT_OPER_INQ].time = BT_INQ_STOP_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_INQ].min_time = BT_INQ_STOP_MIN_TIME;
|
||||
break;
|
||||
default:
|
||||
bes_err("%s type error:%d", __func__, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_bt_oper_le_scan(struct bes2600_common *hw_priv, BWIFI_BT_LE_SCAN_T type)
|
||||
{
|
||||
bes_devel("%s type:%d", __func__, type);
|
||||
|
||||
switch (type) {
|
||||
case BWIFI_LE_SCAN_START:
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_START;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].time = BT_LE_SCAN_START_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].min_time = BT_LE_SCAN_START_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_LE_SCAN_STOP:
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_STOP;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].time = BT_LE_SCAN_STOP_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].min_time = BT_LE_SCAN_STOP_MIN_TIME;
|
||||
break;
|
||||
default:
|
||||
bes_err("%s type error:%d", __func__, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_bt_oper_le_adv(struct bes2600_common *hw_priv, BWIFI_BT_LE_ADV_T type)
|
||||
{
|
||||
bes_devel("%s type:%d", __func__, type);
|
||||
|
||||
switch (type) {
|
||||
case BWIFI_LE_ADV_START:
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_START;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_ADV].time = BT_LE_ADV_START_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_ADV].min_time = BT_LE_ADV_START_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_LE_ADV_STOP:
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_STOP;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_ADV].time = BT_LE_ADV_STOP_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_ADV].min_time = BT_LE_ADV_STOP_MIN_TIME;
|
||||
break;
|
||||
default:
|
||||
bes_err("%s type error:%d", __func__, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_bt_oper_le_status(struct bes2600_common *hw_priv, BWIFI_BT_LE_STATUS_T type)
|
||||
{
|
||||
bes_devel("%s type:%d", __func__, type);
|
||||
|
||||
switch (type) {
|
||||
case BWIFI_LE_CONNECTED:
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_CONNECTED;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].time = BT_LE_CONNECTED_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].min_time = BT_LE_CONNECTED_MIN_TIME;
|
||||
break;
|
||||
case BWIFI_LE_DISCONNECTED:
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_DISCONNECTED;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].time = BT_LE_DISCONNECTED_TIME;
|
||||
g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].min_time = BT_LE_DISCONNECTED_MIN_TIME;
|
||||
break;
|
||||
default:
|
||||
bes_err("%s type error:%d", __func__, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int coex_wifi_state_notify(struct bes2600_common *hw_priv, enum bwifi_epta_state state)
|
||||
{
|
||||
bes_devel("%s state:%d", __func__, state);
|
||||
switch (state) {
|
||||
case EPTA_STATE_WIFI_DISCONNECTED:
|
||||
coex_set_wifi_conn(hw_priv, state);
|
||||
break;
|
||||
case EPTA_STATE_WIFI_SCANNING:
|
||||
coex_set_wifi_conn(hw_priv, state);
|
||||
break;
|
||||
case EPTA_STATE_WIFI_SCAN_COMP:
|
||||
coex_set_wifi_conn(hw_priv, state);
|
||||
break;
|
||||
case EPTA_STATE_WIFI_CONNECTING:
|
||||
coex_set_wifi_conn(hw_priv, state);
|
||||
break;
|
||||
case EPTA_STATE_WIFI_CONNECTED:
|
||||
coex_set_wifi_conn(hw_priv, state);
|
||||
break;
|
||||
case EPTA_STATE_WIFI_GOT_IP:
|
||||
coex_set_wifi_conn(hw_priv, state);
|
||||
break;
|
||||
case EPTA_STATE_WIFI_TTS_START:
|
||||
coex_set_epta_tts(hw_priv, 0);
|
||||
break;
|
||||
case EPTA_STATE_WIFI_TTS_END:
|
||||
coex_set_epta_tts(hw_priv, 1);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void coex_wifi_idle(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_set_fdd_mode(false);
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_DISCONNECTED);
|
||||
}
|
||||
|
||||
static void coex_wifi_scanning(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_set_fdd_mode(false); //scan use tdd
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_SCANNING);
|
||||
}
|
||||
|
||||
static void coex_wifi_scan_comp(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_SCAN_COMP);
|
||||
}
|
||||
|
||||
static void coex_wifi_connecting(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_set_fdd_mode(false);
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTING);
|
||||
}
|
||||
|
||||
static void coex_wifi_connecting_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_set_fdd_mode(true);
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTING);
|
||||
}
|
||||
|
||||
static void coex_wifi_connected(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTED);
|
||||
}
|
||||
|
||||
static void coex_wifi_connected_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_set_fdd_mode(true);
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTED);
|
||||
}
|
||||
|
||||
static void coex_wifi_got_ip(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_GOT_IP);
|
||||
}
|
||||
|
||||
static void coex_wifi_got_ip_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_set_fdd_mode(true); // used for scan -> got ip 5g
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_GOT_IP);
|
||||
}
|
||||
|
||||
static void coex_wifi_disconnecting(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
}
|
||||
|
||||
static void coex_wifi_disconnected(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
|
||||
{
|
||||
bes_devel("%s", __func__);
|
||||
coex_set_fdd_mode(false);
|
||||
coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_DISCONNECTED);
|
||||
}
|
||||
|
||||
static int coex_wifi_bt_ts_request(struct bes2600_common *hw_priv, COEX_TS_TYPE_T type, uint32_t value)
|
||||
{
|
||||
COEX_WIFI_BT_TS_T *wifi_bt_ts_event;
|
||||
|
||||
bes_devel("%s type:%u, value:0x%x", __func__, type, value);
|
||||
|
||||
if (atomic_read(&hw_priv->netdevice_start) == 0) {
|
||||
bes_devel("net down. skip");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called from spin lock vif_lock context */
|
||||
wifi_bt_ts_event = kmalloc(sizeof(COEX_WIFI_BT_TS_T), GFP_ATOMIC);
|
||||
if (wifi_bt_ts_event == NULL) {
|
||||
bes_err("ts_event: malloc fail");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&wifi_bt_ts_event->node);
|
||||
wifi_bt_ts_event->type = type;
|
||||
wifi_bt_ts_event->value = value;
|
||||
|
||||
spin_lock(&hw_priv->coex_event_lock);
|
||||
list_add_tail(&wifi_bt_ts_event->node, &hw_priv->coex_event_list);
|
||||
spin_unlock(&hw_priv->coex_event_lock);
|
||||
|
||||
schedule_work(&hw_priv->coex_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bbt_change_current_status(struct bes2600_common *hw_priv, uint32_t new_status)
|
||||
{
|
||||
coex_wifi_bt_ts_request(hw_priv, COEX_TS_TYPE_BT, new_status);
|
||||
}
|
||||
|
||||
void bwifi_change_current_status(struct bes2600_common *hw_priv, BWIFI_STATUS_T new_status)
|
||||
{
|
||||
coex_wifi_bt_ts_request(hw_priv, COEX_TS_TYPE_WIFI, new_status);
|
||||
}
|
||||
|
||||
static void coex_wifi_bt_ts_cb(struct bes2600_common *hw_priv, COEX_WIFI_BT_TS_T *evt)
|
||||
{
|
||||
if (evt->type == COEX_TS_TYPE_BT) {
|
||||
coex_bt_oper_scan(hw_priv, (BWIFI_BT_SCAN_T)BT_REQUEST_SCAN_VALUE(evt->value));
|
||||
coex_bt_oper_audio(hw_priv, (BWIFI_BT_AUDIO_T)BT_REQUEST_AUDIO_VALUE(evt->value));
|
||||
coex_bt_oper_inq(hw_priv, (BWIFI_BT_INQ_T)BT_REQUEST_INQ_VALUE(evt->value));
|
||||
coex_bt_oper_le_scan(hw_priv, (BWIFI_BT_LE_SCAN_T)BT_REQUEST_LE_SCAN_VALUE(evt->value));
|
||||
coex_bt_oper_le_adv(hw_priv, (BWIFI_BT_LE_ADV_T)BT_REQUEST_LE_ADV_VALUE(evt->value));
|
||||
coex_bt_oper_le_status(hw_priv, (BWIFI_BT_LE_STATUS_T)BT_REQUEST_LE_STATUS_VALUE(evt->value));
|
||||
// process BT STATUS in the end
|
||||
coex_bt_oper_status(hw_priv, (BWIFI_BT_STATUS_T)BT_REQUEST_STATUS_VALUE(evt->value));
|
||||
coex_bt_state_notify(hw_priv);
|
||||
} else if (evt->type == COEX_TS_TYPE_WIFI) {
|
||||
switch (evt->value) {
|
||||
case BWIFI_STATUS_IDLE:
|
||||
coex_wifi_idle(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_SCANNING:
|
||||
coex_wifi_scanning(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_SCANNING_COMP:
|
||||
coex_wifi_scan_comp(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_CONNECTING:
|
||||
coex_wifi_connecting(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_CONNECTING_5G:
|
||||
coex_wifi_connecting_5g(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_CONNECTED:
|
||||
coex_wifi_connected(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_CONNECTED_5G:
|
||||
coex_wifi_connected_5g(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_GOT_IP:
|
||||
coex_wifi_got_ip(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_GOT_IP_5G:
|
||||
coex_wifi_got_ip_5g(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_DISCONNECTING:
|
||||
coex_wifi_disconnecting(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
case BWIFI_STATUS_DISCONNECTED:
|
||||
coex_wifi_disconnected(hw_priv, (BWIFI_STATUS_T)evt->value);
|
||||
break;
|
||||
default:
|
||||
bes_err("UNKNOWN WIFI type %d", evt->value);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
bes_err("UNKNOWN EPTA type %d, %d", evt->type, evt->value);
|
||||
}
|
||||
}
|
||||
|
||||
static void coex_wifi_bt_ts_thread(struct work_struct *work)
|
||||
{
|
||||
COEX_WIFI_BT_TS_T *coex_event;
|
||||
struct bes2600_common *hw_priv = container_of(work, struct bes2600_common, coex_work);
|
||||
|
||||
spin_lock(&hw_priv->coex_event_lock);
|
||||
while (!list_empty(&hw_priv->coex_event_list)) {
|
||||
coex_event = list_first_entry(&hw_priv->coex_event_list, COEX_WIFI_BT_TS_T, node);
|
||||
list_del(&coex_event->node);
|
||||
spin_unlock(&hw_priv->coex_event_lock);
|
||||
coex_wifi_bt_ts_cb(hw_priv, coex_event);
|
||||
kfree(coex_event);
|
||||
|
||||
spin_lock(&hw_priv->coex_event_lock);
|
||||
}
|
||||
spin_unlock(&hw_priv->coex_event_lock);
|
||||
}
|
||||
|
||||
void coex_wifi_bt_ts_thread_init(struct bes2600_common *hw_priv)
|
||||
{
|
||||
coex_bt_time_init();
|
||||
|
||||
INIT_WORK(&hw_priv->coex_work, coex_wifi_bt_ts_thread);
|
||||
INIT_LIST_HEAD(&hw_priv->coex_event_list);
|
||||
spin_lock_init(&hw_priv->coex_event_lock);
|
||||
}
|
||||
|
||||
void coex_wifi_bt_ts_thread_deinit(struct bes2600_common *hw_priv)
|
||||
{
|
||||
cancel_work_sync(&hw_priv->coex_work);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef EPTA_REQUEST_H
|
||||
#define EPTA_REQUEST_H
|
||||
|
||||
#include "linux/list.h"
|
||||
#include "bes2600.h"
|
||||
|
||||
typedef enum {
|
||||
BWIFI_STATUS_IDLE = 0,
|
||||
BWIFI_STATUS_DISCONNECTING = 1,
|
||||
BWIFI_STATUS_SCANNING = 2,
|
||||
BWIFI_STATUS_CONNECTING = 3,
|
||||
BWIFI_STATUS_WPS_CONNECTING = 4,
|
||||
BWIFI_STATUS_CONNECTED = 5,
|
||||
BWIFI_STATUS_DHCPING = 6,
|
||||
BWIFI_STATUS_GOT_IP = 7,
|
||||
/* Warning: don't change enum value above, it's aligned with fw */
|
||||
|
||||
BWIFI_STATUS_CONNECTING_5G = 8,
|
||||
BWIFI_STATUS_CONNECTED_5G = 9,
|
||||
BWIFI_STATUS_DISCONNECTED = 10,
|
||||
BWIFI_STATUS_GOT_IP_5G = 11,
|
||||
BWIFI_STATUS_SCANNING_5G = 12,
|
||||
BWIFI_STATUS_SCANNING_COMP = 13,
|
||||
} BWIFI_STATUS_T;
|
||||
|
||||
typedef enum {
|
||||
COEX_TS_TYPE_BT,
|
||||
COEX_TS_TYPE_WIFI,
|
||||
} COEX_TS_TYPE_T;
|
||||
|
||||
typedef struct {
|
||||
struct list_head node;
|
||||
COEX_TS_TYPE_T type;
|
||||
uint32_t value;
|
||||
} COEX_WIFI_BT_TS_T;
|
||||
|
||||
void bbt_change_current_status(struct bes2600_common *hw_priv, uint32_t new_status);
|
||||
void bwifi_change_current_status(struct bes2600_common *hw_priv, BWIFI_STATUS_T new_status);
|
||||
void coex_wifi_bt_ts_thread_init(struct bes2600_common *hw_priv);
|
||||
void coex_wifi_bt_ts_thread_deinit(struct bes2600_common *hw_priv);
|
||||
void coex_calc_wifi_scan_time(uint32_t *min_chan, uint32_t *max_chan);
|
||||
#endif /*EPTA_REQUEST_H*/
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "bes2600.h"
|
||||
#include "fwio.h"
|
||||
#include "hwio.h"
|
||||
#include "sbus.h"
|
||||
#include "bh.h"
|
||||
|
||||
extern int bes2600_load_firmware_sdio(struct sbus_ops *ops, struct sbus_priv *priv);
|
||||
|
||||
int bes2600_load_firmware(struct sbus_ops *ops, struct sbus_priv *priv)
|
||||
{
|
||||
return bes2600_load_firmware_sdio(ops, priv);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef FWIO_H_INCLUDED
|
||||
#define FWIO_H_INCLUDED
|
||||
|
||||
#define FIRMWARE_1250_CUT11 ("wsm_5011.bin")
|
||||
#define FIRMWARE_CUT22 ("wsm_22.bin")
|
||||
#define FIRMWARE_CUT20 ("wsm_20.bin")
|
||||
#define FIRMWARE_CUT11 ("wsm_11.bin")
|
||||
#define FIRMWARE_CUT10 ("wsm_10.bin")
|
||||
#if defined(BES2600_DETECTION_LOGIC)
|
||||
#define FIRMWARE_1260_CUT10 ("wsm_6010.bin")
|
||||
#endif
|
||||
#define SDD_FILE_1250_11 ("sdd_5011.bin")
|
||||
#define SDD_FILE_22 ("sdd_22.bin")
|
||||
#define SDD_FILE_20 ("sdd_20.bin")
|
||||
#define SDD_FILE_11 ("sdd_11.bin")
|
||||
#define SDD_FILE_10 ("sdd_10.bin")
|
||||
#if defined(BES2600_DETECTION_LOGIC)
|
||||
#define SDD_FILE_1260_10 ("sdd_6010.bin")
|
||||
#endif
|
||||
#if defined(BES2600_DETECTION_LOGIC)
|
||||
#define BOOTLOADER_FILE_1260 ("bootloader_1260.bin")
|
||||
#endif
|
||||
|
||||
#define BES2600_HW_REV_CUT10 (10)
|
||||
#define BES2600_HW_REV_CUT11 (11)
|
||||
#define BES2600_HW_REV_CUT20 (20)
|
||||
#define BES2600_HW_REV_CUT22 (22)
|
||||
#define CW1250_HW_REV_CUT10 (110)
|
||||
#define CW1250_HW_REV_CUT11 (5011)
|
||||
#if defined(BES2600_DETECTION_LOGIC)
|
||||
#define BES2600_HW_REV_CUT10 (6010)
|
||||
#endif
|
||||
struct sbus_ops;
|
||||
struct sbus_priv;
|
||||
|
||||
int bes2600_load_firmware(struct sbus_ops *ops, struct sbus_priv *priv);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* HT-related code for BES2600 driver
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_HT_H_INCLUDED
|
||||
#define BES2600_HT_H_INCLUDED
|
||||
|
||||
#include <net/mac80211.h>
|
||||
|
||||
struct bes2600_ht_info {
|
||||
struct ieee80211_sta_ht_cap ht_cap;
|
||||
enum nl80211_channel_type channel_type;
|
||||
u16 operation_mode;
|
||||
};
|
||||
|
||||
static inline int bes2600_is_ht(const struct bes2600_ht_info *ht_info)
|
||||
{
|
||||
return ht_info->channel_type != NL80211_CHAN_NO_HT;
|
||||
}
|
||||
|
||||
static inline int bes2600_ht_greenfield(const struct bes2600_ht_info *ht_info)
|
||||
{
|
||||
return bes2600_is_ht(ht_info) &&
|
||||
(ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
|
||||
!(ht_info->operation_mode &
|
||||
IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
|
||||
}
|
||||
|
||||
static inline int bes2600_ht_ampdu_density(const struct bes2600_ht_info *ht_info)
|
||||
{
|
||||
if (!bes2600_is_ht(ht_info))
|
||||
return 0;
|
||||
return ht_info->ht_cap.ampdu_density;
|
||||
}
|
||||
|
||||
#endif /* BES2600_HT_H_INCLUDED */
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Low-level device IO routines for BES2600 drivers
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "bes2600.h"
|
||||
#include "hwio.h"
|
||||
#include "sbus.h"
|
||||
#include "bes_log.h"
|
||||
|
||||
/* Sdio addr is 4*spi_addr */
|
||||
#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
|
||||
#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
|
||||
((((buf_id) & 0x1F) << 7) \
|
||||
| (((mpf) & 1) << 6) \
|
||||
| (((rfu) & 1) << 5) \
|
||||
| (((reg_id_ofs) & 0x1F) << 0))
|
||||
#define MAX_RETRY 3
|
||||
|
||||
static struct sbus_ops *bes2600_subs_ops = NULL;
|
||||
static struct sbus_priv *bes2600_sbus_priv = NULL;
|
||||
|
||||
static int __bes2600_reg_read(u16 addr, void *buf, size_t buf_len, int buf_id)
|
||||
{
|
||||
u16 addr_sdio;
|
||||
u32 sdio_reg_addr_17bit ;
|
||||
|
||||
/* Check if buffer is aligned to 4 byte boundary */
|
||||
if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
|
||||
bes_err("%s: buffer is not aligned.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Convert to SDIO Register Address */
|
||||
addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
|
||||
sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
|
||||
|
||||
BUG_ON(!bes2600_subs_ops);
|
||||
return bes2600_subs_ops->sbus_memcpy_fromio(bes2600_sbus_priv,
|
||||
sdio_reg_addr_17bit,
|
||||
buf, buf_len);
|
||||
}
|
||||
|
||||
static int __bes2600_reg_write(u16 addr, const void *buf, size_t buf_len, int buf_id)
|
||||
{
|
||||
u16 addr_sdio;
|
||||
u32 sdio_reg_addr_17bit ;
|
||||
|
||||
#if 0
|
||||
/* Check if buffer is aligned to 4 byte boundary */
|
||||
if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
|
||||
bes_devel("%s: buffer is not aligned.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Convert to SDIO Register Address */
|
||||
addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
|
||||
sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
|
||||
|
||||
BUG_ON(!bes2600_subs_ops);
|
||||
return bes2600_subs_ops->sbus_memcpy_toio(bes2600_sbus_priv,
|
||||
sdio_reg_addr_17bit,
|
||||
buf, buf_len);
|
||||
}
|
||||
|
||||
static inline int __bes2600_reg_read_32(u16 addr, u32 *val)
|
||||
{
|
||||
return __bes2600_reg_read(addr, val, sizeof(val), 0);
|
||||
}
|
||||
|
||||
static inline int __bes2600_reg_write_32(u16 addr, u32 val)
|
||||
{
|
||||
return __bes2600_reg_write(addr, &val, sizeof(val), 0);
|
||||
}
|
||||
|
||||
void bes2600_reg_set_object(struct sbus_ops *ops, struct sbus_priv *priv)
|
||||
{
|
||||
bes2600_subs_ops = ops;
|
||||
bes2600_sbus_priv = priv;
|
||||
}
|
||||
|
||||
int bes2600_reg_read(u32 addr, void *buf, size_t buf_len)
|
||||
{
|
||||
int ret;
|
||||
BUG_ON(!bes2600_subs_ops);
|
||||
bes2600_subs_ops->lock(bes2600_sbus_priv);
|
||||
ret = bes2600_subs_ops->sbus_reg_read(bes2600_sbus_priv, addr, buf, buf_len);
|
||||
bes2600_subs_ops->unlock(bes2600_sbus_priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_reg_write(u32 addr, const void *buf, size_t buf_len)
|
||||
{
|
||||
int ret;
|
||||
BUG_ON(!bes2600_subs_ops);
|
||||
bes2600_subs_ops->lock(bes2600_sbus_priv);
|
||||
ret = bes2600_subs_ops->sbus_reg_write(bes2600_sbus_priv, addr, buf, buf_len);
|
||||
bes2600_subs_ops->unlock(bes2600_sbus_priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_data_read(void *buf, size_t buf_len)
|
||||
{
|
||||
int ret, retry = 1;
|
||||
BUG_ON(!bes2600_subs_ops);
|
||||
bes2600_subs_ops->lock(bes2600_sbus_priv);
|
||||
#ifndef CONFIG_BES2600_WLAN_BES
|
||||
{
|
||||
int buf_id_rx = hw_priv->buf_id_rx;
|
||||
while (retry <= MAX_RETRY) {
|
||||
ret = __bes2600_reg_read(hw_priv,
|
||||
ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
|
||||
buf_len, buf_id_rx + 1);
|
||||
if (!ret) {
|
||||
buf_id_rx = (buf_id_rx + 1) & 3;
|
||||
hw_priv->buf_id_rx = buf_id_rx;
|
||||
break;
|
||||
} else {
|
||||
retry++;
|
||||
mdelay(1);
|
||||
bes_err("%s,error :[%d]\n", __func__, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
while (retry <= MAX_RETRY) {
|
||||
ret = bes2600_subs_ops->sbus_memcpy_fromio(bes2600_sbus_priv,
|
||||
BES_TX_DATA_ADDR, buf, buf_len);
|
||||
if (ret) {
|
||||
retry ++;
|
||||
mdelay(1);
|
||||
bes_err("%s error :[%d]\n", __func__, ret);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bes2600_subs_ops->unlock(bes2600_sbus_priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_data_write(const void *buf, size_t buf_len)
|
||||
{
|
||||
int ret, retry = 1;
|
||||
u32 addr = 0;
|
||||
|
||||
BUG_ON(!bes2600_subs_ops);
|
||||
bes2600_subs_ops->lock(bes2600_sbus_priv);
|
||||
#ifndef CONFIG_BES2600_WLAN_BES
|
||||
{
|
||||
int buf_id_tx = hw_priv->buf_id_tx;
|
||||
while (retry <= MAX_RETRY) {
|
||||
ret = __bes2600_reg_write(hw_priv,
|
||||
ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
|
||||
buf_len, buf_id_tx);
|
||||
if (!ret) {
|
||||
buf_id_tx = (buf_id_tx + 1) & 31;
|
||||
hw_priv->buf_id_tx = buf_id_tx;
|
||||
break;
|
||||
} else {
|
||||
retry++;
|
||||
mdelay(1);
|
||||
bes_err("%s,error :[%d]\n", __func__, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
while (retry <= MAX_RETRY) {
|
||||
ret = bes2600_subs_ops->sbus_memcpy_toio(bes2600_sbus_priv, addr, buf, buf_len);
|
||||
if (ret) {
|
||||
retry++;
|
||||
mdelay(1);
|
||||
bes_err("%s,error :[%d]\n", __func__, ret);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bes2600_subs_ops->unlock(bes2600_sbus_priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_indirect_read(u32 addr, void *buf, size_t buf_len, u32 prefetch, u16 port_addr)
|
||||
{
|
||||
u32 val32 = 0;
|
||||
int i, ret;
|
||||
|
||||
if ((buf_len / 2) >= 0x1000) {
|
||||
bes_err("%s: Can't read more than 0xfff words.\n", __func__);
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bes2600_subs_ops->lock(bes2600_sbus_priv);
|
||||
/* Write address */
|
||||
ret = __bes2600_reg_write_32(ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
|
||||
if (ret < 0) {
|
||||
bes_err("%s: Can't write address register.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read CONFIG Register Value - We will read 32 bits */
|
||||
ret = __bes2600_reg_read_32(ST90TDS_CONFIG_REG_ID, &val32);
|
||||
if (ret < 0) {
|
||||
bes_err("%s: Can't read config register.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set PREFETCH bit */
|
||||
ret = __bes2600_reg_write_32(ST90TDS_CONFIG_REG_ID, val32 | prefetch);
|
||||
if (ret < 0) {
|
||||
bes_err("%s: Can't write prefetch bit.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check for PRE-FETCH bit to be cleared */
|
||||
for (i = 0; i < 20; i++) {
|
||||
ret = __bes2600_reg_read_32(ST90TDS_CONFIG_REG_ID, &val32);
|
||||
if (ret < 0) {
|
||||
bes_err("%s: Can't check prefetch bit.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (!(val32 & prefetch))
|
||||
break;
|
||||
|
||||
mdelay(i);
|
||||
}
|
||||
|
||||
if (val32 & prefetch) {
|
||||
bes_err("%s: Prefetch bit is not cleared.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read data port */
|
||||
ret = __bes2600_reg_read(port_addr, buf, buf_len, 0);
|
||||
if (ret < 0) {
|
||||
bes_err("%s: Can't read data port.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
bes2600_subs_ops->unlock(bes2600_sbus_priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_apb_write(u32 addr, const void *buf, size_t buf_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((buf_len / 2) >= 0x1000) {
|
||||
bes_err("%s: Can't wrire more than 0xfff words.\n", __func__);
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bes2600_subs_ops->lock(bes2600_sbus_priv);
|
||||
|
||||
/* Write address */
|
||||
ret = __bes2600_reg_write_32(ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
|
||||
if (ret < 0) {
|
||||
bes_err("%s: Can't write address register.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write data port */
|
||||
ret = __bes2600_reg_write(ST90TDS_SRAM_DPORT_REG_ID, buf, buf_len, 0);
|
||||
if (ret < 0) {
|
||||
bes_err("%s: Can't write data port.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
bes2600_subs_ops->unlock(bes2600_sbus_priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(BES2600_DETECTION_LOGIC)
|
||||
int bes2600_ahb_write(u32 addr, const void *buf, size_t buf_len)
|
||||
{
|
||||
int ret;
|
||||
bes2600_info(BES2600_DBG_SBUS,"%s: ENTER\n",__func__);
|
||||
if ((buf_len / 2) >= 0x1000) {
|
||||
bes2600_dbg(BES2600_DBG_SBUS,
|
||||
"%s: Can't wrire more than 0xfff words.\n",
|
||||
__func__);
|
||||
WARN_ON(1);
|
||||
bes2600_info(BES2600_DBG_SBUS, "%s:EXIT (1) \n",__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bes2600_subs_ops->lock(bes2600_sbus_priv);
|
||||
|
||||
/* Write address */
|
||||
ret = __bes2600_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
|
||||
if (ret < 0) {
|
||||
bes2600_dbg(BES2600_DBG_SBUS,
|
||||
"%s: Can't write address register.\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write data port */
|
||||
ret = __bes2600_reg_write(priv, ST90TDS_AHB_DPORT_REG_ID,
|
||||
buf, buf_len, 0);
|
||||
if (ret < 0) {
|
||||
bes2600_dbg(BES2600_DBG_SBUS, "%s: Can't write data port.\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
bes2600_subs_ops->unlock(priv->bes2600_sbus_priv);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int __bes2600_irq_enable(int enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Low-level API for mac80211 BES2600 drivers
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* Based on:
|
||||
* UMAC BES2600 driver which is
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_HWIO_H_INCLUDED
|
||||
#define BES2600_HWIO_H_INCLUDED
|
||||
|
||||
/* extern */ struct sbus_ops;
|
||||
/* extern */ struct sbus_priv;
|
||||
|
||||
/* DPLL initial values */
|
||||
#define DPLL_INIT_VAL_9000 (0x00000191)
|
||||
#define DPLL_INIT_VAL_BES2600 (0x0EC4F121)
|
||||
|
||||
/* Hardware Type Definitions */
|
||||
#define HIF_8601_VERSATILE (0)
|
||||
#define HIF_8601_SILICON (1)
|
||||
#define HIF_9000_SILICON_VERSTAILE (2)
|
||||
|
||||
#define BES2600_CUT_11_ID_STR (0x302E3830)
|
||||
#define BES2600_CUT_22_ID_STR1 (0x302e3132)
|
||||
#define BES2600_CUT_22_ID_STR2 (0x32302e30)
|
||||
#define BES2600_CUT_22_ID_STR3 (0x3335)
|
||||
#define CW1250_CUT_11_ID_STR1 (0x302e3033)
|
||||
#define CW1250_CUT_11_ID_STR2 (0x33302e32)
|
||||
#define CW1250_CUT_11_ID_STR3 (0x3535)
|
||||
#define BES2600_CUT_ID_ADDR (0xFFF17F90)
|
||||
#define BES2600_CUT2_ID_ADDR (0xFFF1FF90)
|
||||
|
||||
/* Download control area */
|
||||
/* boot loader start address in SRAM */
|
||||
#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000)
|
||||
/* 32K, 0x4000 to 0xDFFF */
|
||||
#define DOWNLOAD_FIFO_OFFSET (0x00004000)
|
||||
/* 32K */
|
||||
#define DOWNLOAD_FIFO_SIZE (0x00008000)
|
||||
/* 128 bytes, 0xFF80 to 0xFFFF */
|
||||
#define DOWNLOAD_CTRL_OFFSET (0x0000FF80)
|
||||
#define DOWNLOAD_CTRL_DATA_DWORDS (32-6)
|
||||
|
||||
struct download_cntl_t {
|
||||
/* size of whole firmware file (including Cheksum), host init */
|
||||
u32 ImageSize;
|
||||
/* downloading flags */
|
||||
u32 Flags;
|
||||
/* No. of bytes put into the download, init & updated by host */
|
||||
u32 Put;
|
||||
/* last traced program counter, last ARM reg_pc */
|
||||
u32 TracePc;
|
||||
/* No. of bytes read from the download, host init, device updates */
|
||||
u32 Get;
|
||||
/* r0, boot losader status, host init to pending, device updates */
|
||||
u32 Status;
|
||||
/* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
|
||||
u32 DebugData[DOWNLOAD_CTRL_DATA_DWORDS];
|
||||
};
|
||||
|
||||
#define DOWNLOAD_IMAGE_SIZE_REG \
|
||||
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, ImageSize))
|
||||
#define DOWNLOAD_FLAGS_REG \
|
||||
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Flags))
|
||||
#define DOWNLOAD_PUT_REG \
|
||||
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Put))
|
||||
#define DOWNLOAD_TRACE_PC_REG \
|
||||
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, TracePc))
|
||||
#define DOWNLOAD_GET_REG \
|
||||
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Get))
|
||||
#define DOWNLOAD_STATUS_REG \
|
||||
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Status))
|
||||
#define DOWNLOAD_DEBUG_DATA_REG \
|
||||
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, DebugData))
|
||||
#define DOWNLOAD_DEBUG_DATA_LEN (108)
|
||||
|
||||
#define DOWNLOAD_BLOCK_SIZE (1024)
|
||||
|
||||
/* For boot loader detection */
|
||||
#define DOWNLOAD_ARE_YOU_HERE (0x87654321)
|
||||
#define DOWNLOAD_I_AM_HERE (0x12345678)
|
||||
|
||||
/* Download error code */
|
||||
#define DOWNLOAD_PENDING (0xFFFFFFFF)
|
||||
#define DOWNLOAD_SUCCESS (0)
|
||||
#define DOWNLOAD_EXCEPTION (1)
|
||||
#define DOWNLOAD_ERR_MEM_1 (2)
|
||||
#define DOWNLOAD_ERR_MEM_2 (3)
|
||||
#define DOWNLOAD_ERR_SOFTWARE (4)
|
||||
#define DOWNLOAD_ERR_FILE_SIZE (5)
|
||||
#define DOWNLOAD_ERR_CHECKSUM (6)
|
||||
#define DOWNLOAD_ERR_OVERFLOW (7)
|
||||
#define DOWNLOAD_ERR_IMAGE (8)
|
||||
#define DOWNLOAD_ERR_HOST (9)
|
||||
#define DOWNLOAD_ERR_ABORT (10)
|
||||
|
||||
|
||||
#define SYS_BASE_ADDR_SILICON (0)
|
||||
#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000)
|
||||
#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON)
|
||||
|
||||
#define BES26000_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr))
|
||||
|
||||
/* ***************************************************************
|
||||
*Device register definitions
|
||||
*************************************************************** */
|
||||
/* WBF - SPI Register Addresses */
|
||||
#define ST90TDS_ADDR_ID_BASE (0x0000)
|
||||
/* 16/32 bits */
|
||||
#define ST90TDS_CONFIG_REG_ID (0x0000)
|
||||
/* 16/32 bits */
|
||||
#define ST90TDS_CONTROL_REG_ID (0x0001)
|
||||
/* 16 bits, Q mode W/R */
|
||||
#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002)
|
||||
/* 32 bits, AHB bus R/W */
|
||||
#define ST90TDS_AHB_DPORT_REG_ID (0x0003)
|
||||
/* 16/32 bits */
|
||||
#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004)
|
||||
/* 32 bits, APB bus R/W */
|
||||
#define ST90TDS_SRAM_DPORT_REG_ID (0x0005)
|
||||
/* 32 bits, t_settle/general */
|
||||
#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006)
|
||||
/* 16 bits, Q mode read, no length */
|
||||
#define ST90TDS_FRAME_OUT_REG_ID (0x0007)
|
||||
#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID)
|
||||
|
||||
/* WBF - Control register bit set */
|
||||
/* next o/p length, bit 11 to 0 */
|
||||
#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF)
|
||||
#define ST90TDS_CONT_WUP_BIT (BIT(12))
|
||||
#define ST90TDS_CONT_RDY_BIT (BIT(13))
|
||||
#define ST90TDS_CONT_IRQ_ENABLE (BIT(14))
|
||||
#define ST90TDS_CONT_RDY_ENABLE (BIT(15))
|
||||
#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15))
|
||||
|
||||
/* SPI Config register bit set */
|
||||
#define ST90TDS_CONFIG_FRAME_BIT (BIT(2))
|
||||
#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4))
|
||||
#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3))
|
||||
#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4))
|
||||
#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5))
|
||||
#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6))
|
||||
#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7))
|
||||
/* TBD: Sure??? */
|
||||
#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7))
|
||||
#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8))
|
||||
#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9))
|
||||
/* QueueM */
|
||||
#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
|
||||
/* AHB bus */
|
||||
#define ST90TDS_CONFIG_AHB_PFETCH_BIT (BIT(11))
|
||||
#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
|
||||
/* APB bus */
|
||||
#define ST90TDS_CONFIG_PFETCH_BIT (BIT(13))
|
||||
/* cpu reset */
|
||||
#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14))
|
||||
#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15))
|
||||
|
||||
/* For BES2600 the IRQ Enable and Ready Bits are in CONFIG register */
|
||||
#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17))
|
||||
|
||||
void bes2600_reg_set_object(struct sbus_ops *ops, struct sbus_priv *priv);
|
||||
int bes2600_data_read(void *buf, size_t buf_len);
|
||||
int bes2600_data_write(const void *buf, size_t buf_len);
|
||||
|
||||
int bes2600_reg_read(u32 addr, void *buf, size_t buf_len);
|
||||
int bes2600_reg_write(u32 addr, const void *buf, size_t buf_len);
|
||||
|
||||
static inline int bes2600_reg_read_16(u16 addr, u16 *val)
|
||||
{
|
||||
return bes2600_reg_read(addr, val, sizeof(*val));
|
||||
}
|
||||
|
||||
static inline int bes2600_reg_write_16(u16 addr, u16 val)
|
||||
{
|
||||
return bes2600_reg_write(addr, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline int bes2600_reg_read_32(u16 addr, u32 *val)
|
||||
{
|
||||
return bes2600_reg_read(addr, val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline int bes2600_reg_write_32(u16 addr, u32 val)
|
||||
{
|
||||
return bes2600_reg_write(addr, &val, sizeof(val));
|
||||
}
|
||||
|
||||
int bes2600_indirect_read(u32 addr, void *buf, size_t buf_len, u32 prefetch, u16 port_addr);
|
||||
int bes2600_apb_write(u32 addr, const void *buf, size_t buf_len);
|
||||
int bes2600_ahb_write(u32 addr, const void *buf, size_t buf_len);
|
||||
|
||||
static inline int bes2600_apb_read(u32 addr, void *buf, size_t buf_len)
|
||||
{
|
||||
return bes2600_indirect_read(addr, buf, buf_len,
|
||||
ST90TDS_CONFIG_PFETCH_BIT, ST90TDS_SRAM_DPORT_REG_ID);
|
||||
}
|
||||
|
||||
static inline int bes2600_ahb_read(u32 addr, void *buf, size_t buf_len)
|
||||
{
|
||||
return bes2600_indirect_read(addr, buf, buf_len,
|
||||
ST90TDS_CONFIG_AHB_PFETCH_BIT, ST90TDS_AHB_DPORT_REG_ID);
|
||||
}
|
||||
|
||||
static inline int bes2600_apb_read_32(u32 addr, u32 *val)
|
||||
{
|
||||
return bes2600_apb_read(addr, val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline int bes2600_apb_write_32(u32 addr, u32 val)
|
||||
{
|
||||
return bes2600_apb_write(addr, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline int bes2600_ahb_read_32(u32 addr, u32 *val)
|
||||
{
|
||||
return bes2600_ahb_read(addr, val, sizeof(val));
|
||||
}
|
||||
|
||||
#if defined(BES2600_DETECTION_LOGIC)
|
||||
static inline int bes2600_ahb_write_32(u32 addr, u32 val)
|
||||
{
|
||||
return bes2600_ahb_write(addr, &val, sizeof(val));
|
||||
}
|
||||
#endif /*BES2600_DETECTION_LOGIC*/
|
||||
|
||||
#ifdef CONFIG_BES2600_WLAN_USB
|
||||
#define BES_TX_DATA_ADDR (0x0)
|
||||
#endif
|
||||
|
||||
#define SDIO_DEVICE_SEND_INT_LEN_SEPARATE
|
||||
|
||||
#define BES_TX_CTRL_REG_ID (0x0)
|
||||
|
||||
#ifdef SDIO_DEVICE_SEND_INT_LEN_SEPARATE
|
||||
#define BES_TX_NEXT_LEN_REG_ID (0x104)
|
||||
#else
|
||||
#define BES_TX_NEXT_LEN_REG_ID BES_TX_CTRL_REG_ID
|
||||
#endif
|
||||
|
||||
#define BES_TX_NEXT_LEN_MASK (0xffff)
|
||||
#define BES_TX_DATA_ADDR (0x0)
|
||||
|
||||
#define BES_HOST_INT_REG_ID (0x120)
|
||||
#define BES_HOST_INT (1 << 0)
|
||||
#define BES_AP_WAKEUP_CFG (1 << 1)
|
||||
#define BES_SUBSYSTEM_MCU_DEACTIVE (1 << 2)
|
||||
#define BES_SUBSYSTEM_MCU_ACTIVE (1 << 3)
|
||||
#define BES_SUBSYSTEM_WIFI_DEACTIVE (1 << 4)
|
||||
#define BES_SUBSYSTEM_WIFI_ACTIVE (1 << 5)
|
||||
#define BES_SUBSYSTEM_WIFI_DEBUG (1 << 6)
|
||||
#define BES_SUBSYSTEM_BT_DEACTIVE (1 << 7)
|
||||
#define BES_SUBSYSTEM_BT_ACTIVE (1 << 8)
|
||||
#define BES_SUBSYSTEM_SYSTEM_CLOSE (1 << 9)
|
||||
#define BES_SUBSYSTEM_BT_WAKEUP (1 << 10)
|
||||
#define BES_SUBSYSTEM_BT_SLEEP (1 << 11)
|
||||
|
||||
#define BES_AP_WAKEUP_TYPE_MASK 0xC
|
||||
#define BES_AP_WAKEUP_TYPE_SHIFT 2
|
||||
#define BES_AP_WAKEUP_TYPE_GPIO 0
|
||||
#define BES_AP_WAKEUP_TYPE_IF 1
|
||||
|
||||
#define BES_AP_WAKEUP_REG_ID (0x124)
|
||||
#define BES_AP_WAKEUP_CFG_VALID (0x80)
|
||||
|
||||
#define BES_AP_WAKEUP_GPIO_MASK (0x3)
|
||||
#define BES_AP_WAKEUP_GPIO_HIGH (0x0)
|
||||
#define BES_AP_WAKEUP_GPIO_LOW (0x1)
|
||||
#define BES_AP_WAKEUP_GPIO_RISE (0x2)
|
||||
#define BES_AP_WAKEUP_GPIO_FALL (0x3)
|
||||
|
||||
#define BES_SLAVE_STATUS_REG_ID (0x10c)
|
||||
#define BES_SLAVE_STATUS_MCU_READY (1 << 0)
|
||||
#define BES_SLAVE_STATUS_DPD_READY (1 << 1)
|
||||
#define BES_SLAVE_STATUS_WIFI_READY (1 << 2)
|
||||
#define BES_SLAVE_STATUS_BT_READY (1 << 3)
|
||||
#define BES_SLAVE_STATUS_MCU_WAKEUP_READY (1 << 4)
|
||||
#define BES_SLAVE_STATUS_BT_WAKE_READY (1 << 5)
|
||||
#define BES_SLAVE_STATUS_DPD_LOG_READY (1 << 6)
|
||||
|
||||
#define PACKET_TOTAL_LEN(len) ((len) & 0xffff)
|
||||
#define PACKET_COUNT(len) (((len) >> 16) & 0xff)
|
||||
#define PAKCET_CRC8(len) (((len) >> 24) & 0xff)
|
||||
|
||||
#define BES_SDIO_RX_MULTIPLE_NUM (16)
|
||||
#define BES_SDIO_TX_MULTIPLE_NUM (16)
|
||||
#define BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL (1)
|
||||
|
||||
#define MAX_SDIO_TRANSFER_LEN (32768)
|
||||
|
||||
#endif /* BES2600_HWIO_H_INCLUDED */
|
||||
+744
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
* mac80211 glue code for mac80211 BES2600 drivers
|
||||
* ITP code
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <net/mac80211.h>
|
||||
#include "bes2600.h"
|
||||
#include "debug.h"
|
||||
#include "itp.h"
|
||||
#include "sta.h"
|
||||
|
||||
static int __bes2600_itp_open(struct bes2600_common *priv);
|
||||
static int __bes2600_itp_close(struct bes2600_common *priv);
|
||||
static void bes2600_itp_rx_start(struct bes2600_common *priv);
|
||||
static void bes2600_itp_rx_stop(struct bes2600_common *priv);
|
||||
static void bes2600_itp_rx_stats(struct bes2600_common *priv);
|
||||
static void bes2600_itp_rx_reset(struct bes2600_common *priv);
|
||||
static void bes2600_itp_tx_stop(struct bes2600_common *priv);
|
||||
static void bes2600_itp_handle(struct bes2600_common *priv,
|
||||
struct sk_buff *skb);
|
||||
static void bes2600_itp_err(struct bes2600_common *priv,
|
||||
int err,
|
||||
int arg);
|
||||
static void __bes2600_itp_tx_stop(struct bes2600_common *priv);
|
||||
|
||||
static ssize_t bes2600_itp_read(struct file *file,
|
||||
char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct bes2600_common *priv = file->private_data;
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
|
||||
if (skb_queue_empty(&itp->log_queue))
|
||||
return 0;
|
||||
|
||||
skb = skb_dequeue(&itp->log_queue);
|
||||
ret = copy_to_user(user_buf, skb->data, skb->len);
|
||||
*ppos += skb->len;
|
||||
skb->data[skb->len] = 0;
|
||||
bes2600_dbg(BES2600_DBG_ITP, "[ITP] >>> %s", skb->data);
|
||||
consume_skb(skb);
|
||||
|
||||
return skb->len - ret;
|
||||
}
|
||||
|
||||
static ssize_t bes2600_itp_write(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct bes2600_common *priv = file->private_data;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!count || count > 1024)
|
||||
return -EINVAL;
|
||||
skb = dev_alloc_skb(count + 1);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
skb_trim(skb, 0);
|
||||
skb_put(skb, count + 1);
|
||||
if (copy_from_user(skb->data, user_buf, count)) {
|
||||
kfree_skb(skb);
|
||||
return -EFAULT;
|
||||
}
|
||||
skb->data[count] = 0;
|
||||
|
||||
bes2600_itp_handle(priv, skb);
|
||||
consume_skb(skb);
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned int bes2600_itp_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct bes2600_common *priv = file->private_data;
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
unsigned int mask = 0;
|
||||
|
||||
poll_wait(file, &itp->read_wait, wait);
|
||||
|
||||
if (!skb_queue_empty(&itp->log_queue))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
mask |= POLLOUT | POLLWRNORM;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static int bes2600_itp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct bes2600_common *priv = inode->i_private;
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
int ret = 0;
|
||||
|
||||
file->private_data = priv;
|
||||
if (atomic_inc_return(&itp->open_count) == 1) {
|
||||
ret = __bes2600_itp_open(priv);
|
||||
if (ret && !atomic_dec_return(&itp->open_count))
|
||||
__bes2600_itp_close(priv);
|
||||
} else {
|
||||
atomic_dec(&itp->open_count);
|
||||
ret = -EBUSY;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bes2600_itp_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct bes2600_common *priv = file->private_data;
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
if (!atomic_dec_return(&itp->open_count)) {
|
||||
__bes2600_itp_close(priv);
|
||||
wake_up(&itp->close_wait);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_itp = {
|
||||
.open = bes2600_itp_open,
|
||||
.read = bes2600_itp_read,
|
||||
.write = bes2600_itp_write,
|
||||
.poll = bes2600_itp_poll,
|
||||
.release = bes2600_itp_close,
|
||||
.llseek = default_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void bes2600_itp_fill_pattern(u8 *data, int size,
|
||||
enum bes2600_itp_data_modes mode)
|
||||
{
|
||||
u8 *p = data;
|
||||
|
||||
if (size <= 0)
|
||||
return;
|
||||
|
||||
switch (mode) {
|
||||
default:
|
||||
case ITP_DATA_ZEROS:
|
||||
memset(data, 0x0, size);
|
||||
break;
|
||||
case ITP_DATA_ONES:
|
||||
memset(data, 0xff, size);
|
||||
break;
|
||||
case ITP_DATA_ZERONES:
|
||||
memset(data, 0x55, size);
|
||||
break;
|
||||
case ITP_DATA_RANDOM:
|
||||
while (p < data+size-sizeof(u32)) {
|
||||
(*(u32 *)p) = random32();
|
||||
p += sizeof(u32);
|
||||
}
|
||||
while (p < data+size) {
|
||||
(*p) = random32() & 0xFF;
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void bes2600_itp_tx_work(struct work_struct *work)
|
||||
{
|
||||
struct bes2600_itp *itp = container_of(work, struct bes2600_itp,
|
||||
tx_work.work);
|
||||
struct bes2600_common *priv = itp->priv;
|
||||
atomic_set(&priv->bh_tx, 1);
|
||||
wake_up(&priv->bh_wq);
|
||||
}
|
||||
|
||||
static void bes2600_itp_tx_finish(struct work_struct *work)
|
||||
{
|
||||
struct bes2600_itp *itp = container_of(work, struct bes2600_itp,
|
||||
tx_finish.work);
|
||||
__bes2600_itp_tx_stop(itp->priv);
|
||||
}
|
||||
|
||||
int bes2600_itp_init(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
|
||||
itp->priv = priv;
|
||||
atomic_set(&itp->open_count, 0);
|
||||
atomic_set(&itp->stop_tx, 0);
|
||||
atomic_set(&itp->awaiting_confirm, 0);
|
||||
skb_queue_head_init(&itp->log_queue);
|
||||
spin_lock_init(&itp->tx_lock);
|
||||
init_waitqueue_head(&itp->read_wait);
|
||||
init_waitqueue_head(&itp->write_wait);
|
||||
init_waitqueue_head(&itp->close_wait);
|
||||
INIT_DELAYED_WORK(&itp->tx_work, bes2600_itp_tx_work);
|
||||
INIT_DELAYED_WORK(&itp->tx_finish, bes2600_itp_tx_finish);
|
||||
itp->data = NULL;
|
||||
itp->hdr_len = WSM_TX_EXTRA_HEADROOM +
|
||||
sizeof(struct ieee80211_hdr_3addr);
|
||||
itp->id = 0;
|
||||
|
||||
if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR,
|
||||
priv->debug->debugfs_phy, priv, &fops_itp))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bes2600_itp_release(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
|
||||
wait_event_interruptible(itp->close_wait,
|
||||
!atomic_read(&itp->open_count));
|
||||
|
||||
WARN_ON(atomic_read(&itp->open_count));
|
||||
|
||||
skb_queue_purge(&itp->log_queue);
|
||||
bes2600_itp_tx_stop(priv);
|
||||
}
|
||||
|
||||
static int __bes2600_itp_open(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
|
||||
if (!priv->vif)
|
||||
return -EINVAL;
|
||||
if (priv->join_status)
|
||||
return -EINVAL;
|
||||
itp->saved_channel = priv->channel;
|
||||
if (!priv->channel)
|
||||
priv->channel = &priv->hw->
|
||||
wiphy->bands[NL80211_BAND_2GHZ]->channels[0];
|
||||
wsm_set_bssid_filtering(priv, false);
|
||||
bes2600_itp_rx_reset(priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __bes2600_itp_close(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST)
|
||||
bes2600_itp_rx_stop(priv);
|
||||
bes2600_itp_tx_stop(priv);
|
||||
bes2600_disable_listening(priv);
|
||||
bes2600_update_filtering(priv);
|
||||
priv->channel = itp->saved_channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool bes2600_is_itp(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
return atomic_read(&itp->open_count) != 0;
|
||||
}
|
||||
|
||||
static void bes2600_itp_rx_reset(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
itp->rx_cnt = 0;
|
||||
itp->rx_rssi = 0;
|
||||
itp->rx_rssi_max = -1000;
|
||||
itp->rx_rssi_min = 1000;
|
||||
}
|
||||
|
||||
static void bes2600_itp_rx_start(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
|
||||
bes2600_dbg(BES2600_DBG_ITP, "[ITP] RX start, band = %d, ch = %d\n",
|
||||
itp->band, itp->ch);
|
||||
atomic_set(&itp->test_mode, TEST_MODE_RX_TEST);
|
||||
bes2600_disable_listening(priv, false);
|
||||
priv->channel = &priv->hw->
|
||||
wiphy->bands[itp->band]->channels[itp->ch];
|
||||
bes2600_enable_listening(priv, priv->channel);
|
||||
wsm_set_bssid_filtering(priv, false);
|
||||
}
|
||||
|
||||
static void bes2600_itp_rx_stop(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
bes2600_dbg(BES2600_DBG_ITP, "[ITP] RX stop\n");
|
||||
atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
|
||||
bes2600_itp_rx_reset(priv);
|
||||
}
|
||||
|
||||
static void bes2600_itp_rx_stats(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
struct sk_buff *skb;
|
||||
char buf[128];
|
||||
int len, ret;
|
||||
struct wsm_counters_table counters;
|
||||
|
||||
ret = wsm_get_counters_table(priv, &counters);
|
||||
|
||||
if (ret)
|
||||
bes2600_itp_err(priv, -EBUSY, 20);
|
||||
|
||||
if (!itp->rx_cnt)
|
||||
len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n",
|
||||
counters.countRxPacketErrors);
|
||||
else
|
||||
len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n",
|
||||
itp->rx_cnt,
|
||||
itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0,
|
||||
itp->rx_rssi_min, itp->rx_rssi_max,
|
||||
counters.countRxPacketErrors);
|
||||
|
||||
if (len <= 0) {
|
||||
bes2600_itp_err(priv, -EBUSY, 21);
|
||||
return;
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(len);
|
||||
if (!skb) {
|
||||
bes2600_itp_err(priv, -ENOMEM, 22);
|
||||
return;
|
||||
}
|
||||
|
||||
itp->rx_cnt = 0;
|
||||
itp->rx_rssi = 0;
|
||||
itp->rx_rssi_max = -1000;
|
||||
itp->rx_rssi_min = 1000;
|
||||
|
||||
skb_trim(skb, 0);
|
||||
skb_put(skb, len);
|
||||
|
||||
memcpy(skb->data, buf, len);
|
||||
skb_queue_tail(&itp->log_queue, skb);
|
||||
wake_up(&itp->read_wait);
|
||||
}
|
||||
|
||||
static void bes2600_itp_tx_start(struct bes2600_common *priv)
|
||||
{
|
||||
struct wsm_tx *tx;
|
||||
struct ieee80211_hdr_3addr *hdr;
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
struct wsm_association_mode assoc_mode = {
|
||||
.flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE,
|
||||
.preambleType = itp->preamble,
|
||||
};
|
||||
int len;
|
||||
u8 da_addr[6] = ITP_DEFAULT_DA_ADDR;
|
||||
|
||||
/* Rates index 4 and 5 are not supported */
|
||||
if (itp->rate > 3)
|
||||
itp->rate += 2;
|
||||
|
||||
bes2600_dbg(BES2600_DBG_ITP, "[ITP] TX start: band = %d, ch = %d, rate = %d,"
|
||||
" preamble = %d, number = %d, data_mode = %d,"
|
||||
" interval = %d, power = %d, data_len = %d\n",
|
||||
itp->band, itp->ch, itp->rate, itp->preamble,
|
||||
itp->number, itp->data_mode, itp->interval_us,
|
||||
itp->power, itp->data_len);
|
||||
|
||||
len = itp->hdr_len + itp->data_len;
|
||||
|
||||
itp->data = kmalloc(len, GFP_KERNEL);
|
||||
tx = (struct wsm_tx *)itp->data;
|
||||
tx->hdr.len = itp->data_len + itp->hdr_len;
|
||||
tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6);
|
||||
tx->maxTxRate = itp->rate;
|
||||
tx->queueId = 3;
|
||||
tx->more = 0;
|
||||
tx->flags = 0xc;
|
||||
tx->packetID = 0;
|
||||
tx->reserved = 0;
|
||||
tx->expireTime = 0;
|
||||
|
||||
if (itp->preamble == ITP_PREAMBLE_GREENFIELD)
|
||||
tx->htTxParameters = WSM_HT_TX_GREENFIELD;
|
||||
else if (itp->preamble == ITP_PREAMBLE_MIXED)
|
||||
tx->htTxParameters = WSM_HT_TX_MIXED;
|
||||
|
||||
hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)];
|
||||
memset(hdr, 0, sizeof(*hdr));
|
||||
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
|
||||
IEEE80211_FCTL_TODS);
|
||||
memcpy(hdr->addr1, da_addr, ETH_ALEN);
|
||||
memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN);
|
||||
memcpy(hdr->addr3, da_addr, ETH_ALEN);
|
||||
|
||||
bes2600_itp_fill_pattern(&itp->data[itp->hdr_len],
|
||||
itp->data_len, itp->data_mode);
|
||||
|
||||
bes2600_disable_listening(priv);
|
||||
priv->channel = &priv->hw->
|
||||
wiphy->bands[itp->band]->channels[itp->ch];
|
||||
WARN_ON(wsm_set_output_power(priv, itp->power));
|
||||
if (itp->preamble == ITP_PREAMBLE_SHORT ||
|
||||
itp->preamble == ITP_PREAMBLE_LONG)
|
||||
WARN_ON(wsm_set_association_mode(priv,
|
||||
&assoc_mode));
|
||||
wsm_set_bssid_filtering(priv, false);
|
||||
bes2600_enable_listening(priv, priv->channel);
|
||||
|
||||
spin_lock_bh(&itp->tx_lock);
|
||||
atomic_set(&itp->test_mode, TEST_MODE_TX_TEST);
|
||||
atomic_set(&itp->awaiting_confirm, 0);
|
||||
atomic_set(&itp->stop_tx, 0);
|
||||
atomic_set(&priv->bh_tx, 1);
|
||||
ktime_get_ts(&itp->last_sent);
|
||||
wake_up(&priv->bh_wq);
|
||||
spin_unlock_bh(&itp->tx_lock);
|
||||
}
|
||||
|
||||
void __bes2600_itp_tx_stop(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
spin_lock_bh(&itp->tx_lock);
|
||||
kfree(itp->data);
|
||||
itp->data = NULL;
|
||||
atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
|
||||
spin_unlock_bh(&itp->tx_lock);
|
||||
}
|
||||
|
||||
static void bes2600_itp_tx_stop(struct bes2600_common *priv)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
bes2600_dbg(BES2600_DBG_ITP, "[ITP] TX stop\n");
|
||||
atomic_set(&itp->stop_tx, 1);
|
||||
flush_workqueue(priv->workqueue);
|
||||
|
||||
/* time for FW to confirm all tx requests */
|
||||
msleep(500);
|
||||
|
||||
__bes2600_itp_tx_stop(priv);
|
||||
}
|
||||
|
||||
static void bes2600_itp_get_version(struct bes2600_common *priv,
|
||||
enum bes2600_itp_version_type type)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
struct sk_buff *skb;
|
||||
char buf[ITP_BUF_SIZE];
|
||||
size_t size = 0;
|
||||
int len;
|
||||
bes2600_dbg(BES2600_DBG_ITP, "[ITP] print %s version\n", type == ITP_CHIP_ID ?
|
||||
"chip" : "firmware");
|
||||
|
||||
len = snprintf(buf, ITP_BUF_SIZE, "2,");
|
||||
if (len <= 0) {
|
||||
bes2600_itp_err(priv, -EINVAL, 40);
|
||||
return;
|
||||
}
|
||||
size += len;
|
||||
|
||||
switch (type) {
|
||||
case ITP_CHIP_ID:
|
||||
len = bes2600_print_fw_version(priv, buf+size,
|
||||
ITP_BUF_SIZE - size);
|
||||
|
||||
if (len <= 0) {
|
||||
bes2600_itp_err(priv, -EINVAL, 41);
|
||||
return;
|
||||
}
|
||||
size += len;
|
||||
break;
|
||||
case ITP_FW_VER:
|
||||
len = snprintf(buf+size, ITP_BUF_SIZE - size,
|
||||
"%d.%d", priv->wsm_caps.hardwareId,
|
||||
priv->wsm_caps.hardwareSubId);
|
||||
if (len <= 0) {
|
||||
bes2600_itp_err(priv, -EINVAL, 42);
|
||||
return;
|
||||
}
|
||||
size += len;
|
||||
break;
|
||||
default:
|
||||
bes2600_itp_err(priv, -EINVAL, 43);
|
||||
break;
|
||||
}
|
||||
|
||||
len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n");
|
||||
if (len <= 0) {
|
||||
bes2600_itp_err(priv, -EINVAL, 44);
|
||||
return;
|
||||
}
|
||||
size += len;
|
||||
|
||||
skb = dev_alloc_skb(size);
|
||||
if (!skb) {
|
||||
bes2600_itp_err(priv, -ENOMEM, 45);
|
||||
return;
|
||||
}
|
||||
|
||||
skb_trim(skb, 0);
|
||||
skb_put(skb, size);
|
||||
|
||||
memcpy(skb->data, buf, size);
|
||||
skb_queue_tail(&itp->log_queue, skb);
|
||||
wake_up(&itp->read_wait);
|
||||
}
|
||||
|
||||
int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data,
|
||||
size_t *tx_len, int *burst)
|
||||
{
|
||||
struct bes2600_itp *itp;
|
||||
struct wsm_tx *tx;
|
||||
struct timespec now;
|
||||
int time_left_us;
|
||||
|
||||
if (!priv->debug)
|
||||
return 0;
|
||||
|
||||
itp = &priv->debug->itp;
|
||||
|
||||
if (!itp)
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&itp->tx_lock);
|
||||
if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST)
|
||||
goto out;
|
||||
|
||||
if (atomic_read(&itp->stop_tx))
|
||||
goto out;
|
||||
|
||||
if (itp->number == 0) {
|
||||
atomic_set(&itp->stop_tx, 1);
|
||||
queue_delayed_work(priv->workqueue, &itp->tx_finish,
|
||||
HZ/10);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!itp->data)
|
||||
goto out;
|
||||
|
||||
if (priv->hw_bufs_used >= 2) {
|
||||
if (!atomic_read(&priv->bh_rx))
|
||||
atomic_set(&priv->bh_rx, 1);
|
||||
atomic_set(&priv->bh_tx, 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ktime_get_ts(&now);
|
||||
time_left_us = (itp->last_sent.tv_sec -
|
||||
now.tv_sec)*1000000 +
|
||||
(itp->last_sent.tv_nsec - now.tv_nsec)/1000
|
||||
+ itp->interval_us;
|
||||
|
||||
if (time_left_us > ITP_TIME_THRES_US) {
|
||||
queue_delayed_work(priv->workqueue, &itp->tx_work,
|
||||
ITP_US_TO_MS(time_left_us)*HZ/1000);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (time_left_us > 50)
|
||||
udelay(time_left_us);
|
||||
|
||||
if (itp->number > 0)
|
||||
itp->number--;
|
||||
|
||||
*data = itp->data;
|
||||
*tx_len = itp->data_len + itp->hdr_len;
|
||||
|
||||
if (itp->data_mode == ITP_DATA_RANDOM)
|
||||
bes2600_itp_fill_pattern(&itp->data[itp->hdr_len],
|
||||
itp->data_len, itp->data_mode);
|
||||
|
||||
tx = (struct wsm_tx *)itp->data;
|
||||
tx->packetID = __cpu_to_le32(itp->id++);
|
||||
*burst = 2;
|
||||
atomic_set(&priv->bh_tx, 1);
|
||||
ktime_get_ts(&itp->last_sent);
|
||||
atomic_add(1, &itp->awaiting_confirm);
|
||||
spin_unlock_bh(&itp->tx_lock);
|
||||
return 1;
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&itp->tx_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool bes2600_itp_rxed(struct bes2600_common *priv, struct sk_buff *skb)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb);
|
||||
int signal;
|
||||
|
||||
if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST)
|
||||
return bes2600_is_itp(priv);
|
||||
if (rx->freq != priv->channel->center_freq)
|
||||
return true;
|
||||
|
||||
signal = rx->signal;
|
||||
itp->rx_cnt++;
|
||||
itp->rx_rssi += signal;
|
||||
if (itp->rx_rssi_min > rx->signal)
|
||||
itp->rx_rssi_min = rx->signal;
|
||||
if (itp->rx_rssi_max < rx->signal)
|
||||
itp->rx_rssi_max = rx->signal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bes2600_itp_wake_up_tx(struct bes2600_common *priv)
|
||||
{
|
||||
wake_up(&priv->debug->itp.write_wait);
|
||||
}
|
||||
|
||||
bool bes2600_itp_tx_running(struct bes2600_common *priv)
|
||||
{
|
||||
if (atomic_read(&priv->debug->itp.awaiting_confirm) ||
|
||||
atomic_read(&priv->debug->itp.test_mode) ==
|
||||
TEST_MODE_TX_TEST) {
|
||||
atomic_sub(1, &priv->debug->itp.awaiting_confirm);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bes2600_itp_handle(struct bes2600_common *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
const struct wiphy *wiphy = priv->hw->wiphy;
|
||||
int cmd;
|
||||
int ret;
|
||||
|
||||
bes2600_dbg(BES2600_DBG_ITP, "[ITP] <<< %s", skb->data);
|
||||
if (sscanf(skb->data, "%d", &cmd) != 1) {
|
||||
bes2600_itp_err(priv, -EINVAL, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case 1: /* RX test */
|
||||
if (atomic_read(&itp->test_mode)) {
|
||||
bes2600_itp_err(priv, -EBUSY, 0);
|
||||
return;
|
||||
}
|
||||
ret = sscanf(skb->data, "%d,%d,%d",
|
||||
&cmd, &itp->band, &itp->ch);
|
||||
if (ret != 3) {
|
||||
bes2600_itp_err(priv, -EINVAL, ret + 1);
|
||||
return;
|
||||
}
|
||||
if (itp->band >= 2)
|
||||
bes2600_itp_err(priv, -EINVAL, 2);
|
||||
else if (!wiphy->bands[itp->band])
|
||||
bes2600_itp_err(priv, -EINVAL, 2);
|
||||
else if (itp->ch >=
|
||||
wiphy->bands[itp->band]->n_channels)
|
||||
bes2600_itp_err(priv, -EINVAL, 3);
|
||||
else {
|
||||
bes2600_itp_rx_stats(priv);
|
||||
bes2600_itp_rx_start(priv);
|
||||
}
|
||||
break;
|
||||
case 2: /* RX stat */
|
||||
bes2600_itp_rx_stats(priv);
|
||||
break;
|
||||
case 3: /* RX/TX stop */
|
||||
if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) {
|
||||
bes2600_itp_rx_stats(priv);
|
||||
bes2600_itp_rx_stop(priv);
|
||||
} else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) {
|
||||
bes2600_itp_tx_stop(priv);
|
||||
} else
|
||||
bes2600_itp_err(priv, -EBUSY, 0);
|
||||
break;
|
||||
case 4: /* TX start */
|
||||
if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) {
|
||||
bes2600_itp_err(priv, -EBUSY, 0);
|
||||
return;
|
||||
}
|
||||
ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
|
||||
&cmd, &itp->band, &itp->ch, &itp->rate,
|
||||
&itp->preamble, &itp->number, &itp->data_mode,
|
||||
&itp->interval_us, &itp->power, &itp->data_len);
|
||||
if (ret != 10) {
|
||||
bes2600_itp_err(priv, -EINVAL, ret + 1);
|
||||
return;
|
||||
}
|
||||
if (itp->band >= 2)
|
||||
bes2600_itp_err(priv, -EINVAL, 2);
|
||||
else if (!wiphy->bands[itp->band])
|
||||
bes2600_itp_err(priv, -EINVAL, 2);
|
||||
else if (itp->ch >=
|
||||
wiphy->bands[itp->band]->n_channels)
|
||||
bes2600_itp_err(priv, -EINVAL, 3);
|
||||
else if (itp->rate >= 20)
|
||||
bes2600_itp_err(priv, -EINVAL, 4);
|
||||
else if (itp->preamble >= ITP_PREAMBLE_MAX)
|
||||
bes2600_itp_err(priv, -EINVAL, 5);
|
||||
else if (itp->data_mode >= ITP_DATA_MAX_MODE)
|
||||
bes2600_itp_err(priv, -EINVAL, 7);
|
||||
else if (itp->data_len < ITP_MIN_DATA_SIZE ||
|
||||
itp->data_len > priv->wsm_caps.sizeInpChBuf -
|
||||
itp->hdr_len)
|
||||
bes2600_itp_err(priv, -EINVAL, 8);
|
||||
else {
|
||||
bes2600_itp_tx_start(priv);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
bes2600_itp_get_version(priv, ITP_CHIP_ID);
|
||||
break;
|
||||
case 6:
|
||||
bes2600_itp_get_version(priv, ITP_FW_VER);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void bes2600_itp_err(struct bes2600_common *priv,
|
||||
int err, int arg)
|
||||
{
|
||||
struct bes2600_itp *itp = &priv->debug->itp;
|
||||
struct sk_buff *skb;
|
||||
static char buf[255];
|
||||
int len;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%d,%d\n",
|
||||
err, arg);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
skb = dev_alloc_skb(len);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb_trim(skb, 0);
|
||||
skb_put(skb, len);
|
||||
|
||||
memcpy(skb->data, buf, len);
|
||||
skb_queue_tail(&itp->log_queue, skb);
|
||||
wake_up(&itp->read_wait);
|
||||
|
||||
len = sprint_symbol(buf,
|
||||
(unsigned long)__builtin_return_address(0));
|
||||
if (len <= 0)
|
||||
return;
|
||||
bes2600_dbg(BES2600_DBG_ITP, "[ITP] error %d,%d from %s\n",
|
||||
err, arg, buf);
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* ITP code for BES2600 mac80211 driver
|
||||
*
|
||||
* Copyright (c) 2011, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_ITP_H_INCLUDED
|
||||
#define BES2600_ITP_H_INCLUDED
|
||||
|
||||
struct cw200_common;
|
||||
struct wsm_tx_confirm;
|
||||
struct dentry;
|
||||
|
||||
#ifdef CONFIG_BES2600_ITP
|
||||
|
||||
/*extern*/ struct ieee80211_channel;
|
||||
|
||||
#define TEST_MODE_NO_TEST (0)
|
||||
#define TEST_MODE_RX_TEST (1)
|
||||
#define TEST_MODE_TX_TEST (2)
|
||||
|
||||
#define itp_printk(...) printk(__VA_ARGS__)
|
||||
#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
#define ITP_MIN_DATA_SIZE 6
|
||||
#define ITP_MAX_DATA_SIZE 1600
|
||||
#define ITP_TIME_THRES_US 10000
|
||||
#define ITP_US_TO_MS(x) ((x)/1000)
|
||||
#define ITP_MS_TO_US(x) ((x)*1000)
|
||||
#if ((ITP_US_TO_MS(ITP_TIME_THRES_US))*HZ/1000) < 1
|
||||
#warning not able to achieve non-busywaiting ITP_TIME_THRES_US\
|
||||
precision with current HZ value !
|
||||
#endif
|
||||
#define ITP_BUF_SIZE 255
|
||||
|
||||
|
||||
enum bes2600_itp_data_modes {
|
||||
ITP_DATA_ZEROS,
|
||||
ITP_DATA_ONES,
|
||||
ITP_DATA_ZERONES,
|
||||
ITP_DATA_RANDOM,
|
||||
ITP_DATA_MAX_MODE,
|
||||
};
|
||||
|
||||
enum bes2600_itp_version_type {
|
||||
ITP_CHIP_ID,
|
||||
ITP_FW_VER,
|
||||
};
|
||||
|
||||
enum bes2600_itp_preamble_type {
|
||||
ITP_PREAMBLE_LONG,
|
||||
ITP_PREAMBLE_SHORT,
|
||||
ITP_PREAMBLE_OFDM,
|
||||
ITP_PREAMBLE_MIXED,
|
||||
ITP_PREAMBLE_GREENFIELD,
|
||||
ITP_PREAMBLE_MAX,
|
||||
};
|
||||
|
||||
|
||||
struct bes2600_itp {
|
||||
struct bes2600_common *priv;
|
||||
atomic_t open_count;
|
||||
atomic_t awaiting_confirm;
|
||||
struct sk_buff_head log_queue;
|
||||
wait_queue_head_t read_wait;
|
||||
wait_queue_head_t write_wait;
|
||||
wait_queue_head_t close_wait;
|
||||
struct ieee80211_channel *saved_channel;
|
||||
atomic_t stop_tx;
|
||||
struct delayed_work tx_work;
|
||||
struct delayed_work tx_finish;
|
||||
spinlock_t tx_lock;
|
||||
struct timespec last_sent;
|
||||
atomic_t test_mode;
|
||||
int rx_cnt;
|
||||
long rx_rssi;
|
||||
int rx_rssi_max;
|
||||
int rx_rssi_min;
|
||||
unsigned band;
|
||||
unsigned ch;
|
||||
unsigned rate;
|
||||
unsigned preamble;
|
||||
unsigned int number;
|
||||
unsigned data_mode;
|
||||
int interval_us;
|
||||
int power;
|
||||
u8 *data;
|
||||
int hdr_len;
|
||||
int data_len;
|
||||
int id;
|
||||
};
|
||||
|
||||
int bes2600_itp_init(struct bes2600_common *priv);
|
||||
void bes2600_itp_release(struct bes2600_common *priv);
|
||||
|
||||
bool bes2600_is_itp(struct bes2600_common *priv);
|
||||
bool bes2600_itp_rxed(struct bes2600_common *priv, struct sk_buff *skb);
|
||||
void bes2600_itp_wake_up_tx(struct bes2600_common *priv);
|
||||
int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data,
|
||||
size_t *tx_len, int *burst);
|
||||
bool bes2600_itp_tx_running(struct bes2600_common *priv);
|
||||
|
||||
#else /* CONFIG_BES2600_ITP */
|
||||
|
||||
static inline int
|
||||
bes2600_itp_init(struct bes2600_common *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void bes2600_itp_release(struct bes2600_common *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool bes2600_is_itp(struct bes2600_common *priv)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool bes2600_itp_rxed(struct bes2600_common *priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static inline void bes2600_itp_consume_txed(struct bes2600_common *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bes2600_itp_wake_up_tx(struct bes2600_common *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data,
|
||||
size_t *tx_len, int *burst)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool bes2600_itp_tx_running(struct bes2600_common *priv)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BES2600_ITP */
|
||||
|
||||
#endif /* BES2600_ITP_H_INCLUDED */
|
||||
+914
@@ -0,0 +1,914 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/of.h>
|
||||
#include <net/mac80211.h>
|
||||
|
||||
#include "bes2600.h"
|
||||
#include "txrx.h"
|
||||
#include "sbus.h"
|
||||
#include "fwio.h"
|
||||
#include "hwio.h"
|
||||
#include "bh.h"
|
||||
#include "sta.h"
|
||||
#include "ap.h"
|
||||
#include "scan.h"
|
||||
#include "debug.h"
|
||||
#include "pm.h"
|
||||
#include "bes2600_factory.h"
|
||||
#include "bes_chardev.h"
|
||||
|
||||
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>");
|
||||
MODULE_DESCRIPTION("Softmac BES2600 common code");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("bes2600");
|
||||
|
||||
/* TODO: use rates and channels from the device */
|
||||
#define RATETAB_ENT(_rate, _rateid, _flags) \
|
||||
{ \
|
||||
.bitrate = (_rate), \
|
||||
.hw_value = (_rateid), \
|
||||
.flags = (_flags), \
|
||||
}
|
||||
|
||||
static struct ieee80211_rate bes2600_rates[] = {
|
||||
RATETAB_ENT(10, 0, 0),
|
||||
RATETAB_ENT(20, 1, 0),
|
||||
RATETAB_ENT(55, 2, 0),
|
||||
RATETAB_ENT(110, 3, 0),
|
||||
RATETAB_ENT(60, 6, 0),
|
||||
RATETAB_ENT(90, 7, 0),
|
||||
RATETAB_ENT(120, 8, 0),
|
||||
RATETAB_ENT(180, 9, 0),
|
||||
RATETAB_ENT(240, 10, 0),
|
||||
RATETAB_ENT(360, 11, 0),
|
||||
RATETAB_ENT(480, 12, 0),
|
||||
RATETAB_ENT(540, 13, 0),
|
||||
};
|
||||
|
||||
static struct ieee80211_rate bes2600_mcs_rates[] = {
|
||||
RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS),
|
||||
RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
|
||||
RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
|
||||
RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
|
||||
RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
|
||||
RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
|
||||
RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
|
||||
RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
|
||||
};
|
||||
|
||||
#define bes2600_a_rates (bes2600_rates + 4)
|
||||
#define bes2600_a_rates_size (ARRAY_SIZE(bes2600_rates) - 4)
|
||||
#define bes2600_g_rates (bes2600_rates + 0)
|
||||
#define bes2600_g_rates_size (ARRAY_SIZE(bes2600_rates))
|
||||
#define bes2600_n_rates (bes2600_mcs_rates)
|
||||
#define bes2600_n_rates_size (ARRAY_SIZE(bes2600_mcs_rates))
|
||||
|
||||
|
||||
#define CHAN2G(_channel, _freq, _flags) { \
|
||||
.band = NL80211_BAND_2GHZ, \
|
||||
.center_freq = (_freq), \
|
||||
.hw_value = (_channel), \
|
||||
.flags = (_flags), \
|
||||
.max_antenna_gain = 0, \
|
||||
.max_power = 30, \
|
||||
}
|
||||
|
||||
#define CHAN5G(_channel, _flags) { \
|
||||
.band = NL80211_BAND_5GHZ, \
|
||||
.center_freq = 5000 + (5 * (_channel)), \
|
||||
.hw_value = (_channel), \
|
||||
.flags = (_flags), \
|
||||
.max_antenna_gain = 0, \
|
||||
.max_power = 30, \
|
||||
}
|
||||
|
||||
struct device *global_dev = NULL;
|
||||
|
||||
static struct ieee80211_channel bes2600_2ghz_chantable[] = {
|
||||
CHAN2G(1, 2412, 0),
|
||||
CHAN2G(2, 2417, 0),
|
||||
CHAN2G(3, 2422, 0),
|
||||
CHAN2G(4, 2427, 0),
|
||||
CHAN2G(5, 2432, 0),
|
||||
CHAN2G(6, 2437, 0),
|
||||
CHAN2G(7, 2442, 0),
|
||||
CHAN2G(8, 2447, 0),
|
||||
CHAN2G(9, 2452, 0),
|
||||
CHAN2G(10, 2457, 0),
|
||||
CHAN2G(11, 2462, 0),
|
||||
CHAN2G(12, 2467, 0),
|
||||
CHAN2G(13, 2472, 0),
|
||||
CHAN2G(14, 2484, 0),
|
||||
};
|
||||
|
||||
static struct ieee80211_channel bes2600_5ghz_chantable[] = {
|
||||
CHAN5G(34, 0), CHAN5G(36, 0),
|
||||
CHAN5G(38, 0), CHAN5G(40, 0),
|
||||
CHAN5G(42, 0), CHAN5G(44, 0),
|
||||
CHAN5G(46, 0), CHAN5G(48, 0),
|
||||
CHAN5G(52, 0), CHAN5G(56, 0),
|
||||
CHAN5G(60, 0), CHAN5G(64, 0),
|
||||
CHAN5G(100, 0), CHAN5G(104, 0),
|
||||
CHAN5G(108, 0), CHAN5G(112, 0),
|
||||
CHAN5G(116, 0), CHAN5G(120, 0),
|
||||
CHAN5G(124, 0), CHAN5G(128, 0),
|
||||
CHAN5G(132, 0), CHAN5G(136, 0),
|
||||
CHAN5G(140, 0), CHAN5G(149, 0),
|
||||
CHAN5G(153, 0), CHAN5G(157, 0),
|
||||
CHAN5G(161, 0), CHAN5G(165, 0),
|
||||
CHAN5G(184, 0), CHAN5G(188, 0),
|
||||
CHAN5G(192, 0), CHAN5G(196, 0),
|
||||
CHAN5G(200, 0), CHAN5G(204, 0),
|
||||
CHAN5G(208, 0), CHAN5G(212, 0),
|
||||
CHAN5G(216, 0),
|
||||
};
|
||||
|
||||
static struct ieee80211_supported_band bes2600_band_2ghz = {
|
||||
.channels = bes2600_2ghz_chantable,
|
||||
.n_channels = ARRAY_SIZE(bes2600_2ghz_chantable),
|
||||
.bitrates = bes2600_g_rates,
|
||||
.n_bitrates = bes2600_g_rates_size,
|
||||
.ht_cap = {
|
||||
.cap = IEEE80211_HT_CAP_GRN_FLD |
|
||||
(STBC_RX_24G << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||
IEEE80211_HT_CAP_SGI_20 |
|
||||
IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_MAX_AMSDU,
|
||||
.ht_supported = 1,
|
||||
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K,
|
||||
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
|
||||
.mcs = {
|
||||
.rx_mask[0] = 0xFF,
|
||||
.rx_highest = __cpu_to_le16(0),
|
||||
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct ieee80211_supported_band bes2600_band_5ghz = {
|
||||
.channels = bes2600_5ghz_chantable,
|
||||
.n_channels = ARRAY_SIZE(bes2600_5ghz_chantable),
|
||||
.bitrates = bes2600_a_rates,
|
||||
.n_bitrates = bes2600_a_rates_size,
|
||||
.ht_cap = {
|
||||
.cap = IEEE80211_HT_CAP_GRN_FLD |
|
||||
(STBC_RX_5G << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||
IEEE80211_HT_CAP_SGI_20 |
|
||||
IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_MAX_AMSDU,
|
||||
.ht_supported = 1,
|
||||
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K,
|
||||
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
|
||||
.mcs = {
|
||||
.rx_mask[0] = 0xFF,
|
||||
.rx_highest = __cpu_to_le16(0x41),
|
||||
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const unsigned long bes2600_ttl[] = {
|
||||
1 * HZ, /* VO */
|
||||
2 * HZ, /* VI */
|
||||
5 * HZ, /* BE */
|
||||
10 * HZ /* BK */
|
||||
};
|
||||
|
||||
static const struct ieee80211_iface_limit bes2600_if_limits[] = {
|
||||
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) },
|
||||
{ .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
||||
BIT(NL80211_IFTYPE_P2P_GO) },
|
||||
#ifdef P2P_MULTIVIF
|
||||
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const struct ieee80211_iface_combination bes2600_if_comb[] = {
|
||||
{
|
||||
.limits = bes2600_if_limits,
|
||||
.n_limits = ARRAY_SIZE(bes2600_if_limits),
|
||||
.max_interfaces = CW12XX_MAX_VIFS,
|
||||
.num_different_channels = 1,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static const struct ieee80211_ops bes2600_ops = {
|
||||
.add_chanctx = ieee80211_emulate_add_chanctx,
|
||||
.remove_chanctx = ieee80211_emulate_remove_chanctx,
|
||||
.change_chanctx = ieee80211_emulate_change_chanctx,
|
||||
.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
|
||||
.start = bes2600_start,
|
||||
.stop = bes2600_stop,
|
||||
.add_interface = bes2600_add_interface,
|
||||
.remove_interface = bes2600_remove_interface,
|
||||
.change_interface = bes2600_change_interface,
|
||||
.tx = bes2600_tx,
|
||||
.wake_tx_queue = ieee80211_handle_wake_tx_queue,
|
||||
.hw_scan = bes2600_hw_scan,
|
||||
.cancel_hw_scan = bes2600_cancel_hw_scan,
|
||||
#ifdef ROAM_OFFLOAD
|
||||
.sched_scan_start = bes2600_hw_sched_scan_start,
|
||||
.sched_scan_stop = bes2600_hw_sched_scan_stop,
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
.set_tim = bes2600_set_tim,
|
||||
.sta_notify = bes2600_sta_notify,
|
||||
.sta_add = bes2600_sta_add,
|
||||
.sta_remove = bes2600_sta_remove,
|
||||
.set_key = bes2600_set_key,
|
||||
.set_rts_threshold = bes2600_set_rts_threshold,
|
||||
.config = bes2600_config,
|
||||
.bss_info_changed = bes2600_bss_info_changed,
|
||||
.prepare_multicast = bes2600_prepare_multicast,
|
||||
.configure_filter = bes2600_configure_filter,
|
||||
.conf_tx = bes2600_conf_tx,
|
||||
.get_stats = bes2600_get_stats,
|
||||
.ampdu_action = bes2600_ampdu_action,
|
||||
.flush = bes2600_flush,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = bes2600_wow_suspend,
|
||||
.resume = bes2600_wow_resume,
|
||||
#endif
|
||||
/* Intentionally not offloaded: */
|
||||
/*.channel_switch = bes2600_channel_switch, */
|
||||
.remain_on_channel = bes2600_remain_on_channel,
|
||||
.cancel_remain_on_channel = bes2600_cancel_remain_on_channel,
|
||||
#ifdef IPV6_FILTERING
|
||||
//.set_data_filter = bes2600_set_data_filter,
|
||||
#endif /*IPV6_FILTERING*/
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
.testmode_cmd = bes2600_testmode_cmd,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static const struct wiphy_wowlan_support bes2600_wowlan_support = {
|
||||
/* Support only for limited wowlan functionalities */
|
||||
.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
||||
static void bes2600_init_wapi_cipher(struct ieee80211_hw *hw)
|
||||
{
|
||||
static struct ieee80211_cipher_scheme wapi_cs = {
|
||||
.cipher = WLAN_CIPHER_SUITE_SMS4,
|
||||
.iftype = BIT(NL80211_IFTYPE_STATION),
|
||||
.hdr_len = 18,
|
||||
.pn_len = 16,
|
||||
.pn_off = 2,
|
||||
.key_idx_off = 0,
|
||||
.key_idx_mask = 0x01,
|
||||
.key_idx_shift = 0,
|
||||
.mic_len = 16
|
||||
};
|
||||
|
||||
hw->cipher_schemes = &wapi_cs;
|
||||
hw->n_cipher_schemes = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void bes2600_get_base_mac(struct bes2600_common *hw_priv)
|
||||
{
|
||||
struct device_node *np;
|
||||
const u8* addr = NULL;
|
||||
bool ok = false;
|
||||
int len;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "bestechnic,bes2600-sdio");
|
||||
if (np) {
|
||||
addr = of_get_property(np, "local-mac-address", &len);
|
||||
if (addr && len == ETH_ALEN) {
|
||||
memcpy(hw_priv->addresses[0].addr, addr, ETH_ALEN);
|
||||
ok = true;
|
||||
} else {
|
||||
bes_err("bestechnic,bes2600 device node does not have valid local-mac-address property, random mac will be used!\n");
|
||||
}
|
||||
of_node_put(np);
|
||||
} else {
|
||||
bes_err("bestechnic,bes2600 device node NOT found, random mac will be used!\n");
|
||||
}
|
||||
if (!ok)
|
||||
get_random_bytes(hw_priv->addresses[0].addr, ETH_ALEN);
|
||||
|
||||
hw_priv->addresses[0].addr[0] &= ~1u;
|
||||
}
|
||||
|
||||
static void bes2600_derive_mac(struct bes2600_common *hw_priv)
|
||||
{
|
||||
memcpy(hw_priv->addresses[1].addr, hw_priv->addresses[0].addr, ETH_ALEN);
|
||||
hw_priv->addresses[1].addr[5] = hw_priv->addresses[0].addr[5] + 1;
|
||||
|
||||
#ifdef P2P_MULTIVIF
|
||||
memcpy(hw_priv->addresses[2].addr, hw_priv->addresses[1].addr,
|
||||
ETH_ALEN);
|
||||
hw_priv->addresses[2].addr[4] ^= 0x80;
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
|
||||
{
|
||||
int i;
|
||||
struct ieee80211_hw *hw;
|
||||
struct bes2600_common *hw_priv;
|
||||
struct ieee80211_supported_band *sband;
|
||||
int band;
|
||||
|
||||
hw = ieee80211_alloc_hw(hw_priv_data_len, &bes2600_ops);
|
||||
if (!hw)
|
||||
return NULL;
|
||||
|
||||
hw_priv = hw->priv;
|
||||
hw_priv->if_id_slot = 0;
|
||||
hw_priv->roc_if_id = -1;
|
||||
hw_priv->scan_switch_if_id = -1;
|
||||
atomic_set(&hw_priv->num_vifs, 0);
|
||||
atomic_set(&hw_priv->netdevice_start, 0);
|
||||
|
||||
bes2600_get_base_mac(hw_priv);
|
||||
bes2600_derive_mac(hw_priv);
|
||||
|
||||
hw_priv->hw = hw;
|
||||
hw_priv->rates = bes2600_rates; /* TODO: fetch from FW */
|
||||
hw_priv->mcs_rates = bes2600_n_rates;
|
||||
#ifdef ROAM_OFFLOAD
|
||||
hw_priv->auto_scanning = 0;
|
||||
hw_priv->frame_rcvd = 0;
|
||||
hw_priv->num_scanchannels = 0;
|
||||
hw_priv->num_2g_channels = 0;
|
||||
hw_priv->num_5g_channels = 0;
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
#ifdef AP_AGGREGATE_FW_FIX
|
||||
/* Enable block ACK for 4 TID (BE,VI,VI,VO). */
|
||||
/*due to HW limitations*/
|
||||
hw_priv->ba_tid_mask = 0xB1;
|
||||
#else
|
||||
/* Enable block ACK for every TID but voice. */
|
||||
hw_priv->ba_tid_mask = 0xFF;//0x3F;
|
||||
#endif
|
||||
|
||||
/* Init tx retry limit */
|
||||
#ifdef BES2600_TX_RX_OPT
|
||||
hw_priv->long_frame_max_tx_count = 31;
|
||||
hw_priv->short_frame_max_tx_count = 31;
|
||||
#else
|
||||
hw_priv->long_frame_max_tx_count = 7;
|
||||
hw_priv->short_frame_max_tx_count = 15;
|
||||
#endif
|
||||
hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count;
|
||||
|
||||
ieee80211_hw_set(hw, SIGNAL_DBM);
|
||||
ieee80211_hw_set(hw, SUPPORTS_PS);
|
||||
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
|
||||
ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
|
||||
ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
|
||||
ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
|
||||
ieee80211_hw_set(hw, AMPDU_AGGREGATION);
|
||||
ieee80211_hw_set(hw, CONNECTION_MONITOR);
|
||||
ieee80211_hw_set(hw, MFP_CAPABLE);
|
||||
|
||||
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_ADHOC) |
|
||||
BIT(NL80211_IFTYPE_AP) |
|
||||
BIT(NL80211_IFTYPE_MESH_POINT) |
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
||||
BIT(NL80211_IFTYPE_P2P_GO);
|
||||
#ifdef P2P_MULTIVIF
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||
#endif
|
||||
|
||||
/* Support only for limited wowlan functionalities */
|
||||
#ifdef CONFIG_PM
|
||||
hw->wiphy->wowlan = &bes2600_wowlan_support;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
||||
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
|
||||
#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
|
||||
|
||||
#ifdef PROBE_RESP_EXTRA_IE
|
||||
hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BES2600_DISABLE_BEACON_HINTS)
|
||||
hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS;
|
||||
#endif
|
||||
hw->wiphy->n_addresses = CW12XX_MAX_VIFS;
|
||||
hw->wiphy->addresses = hw_priv->addresses;
|
||||
hw->wiphy->max_remain_on_channel_duration = 500;
|
||||
//hw->channel_change_time = 500; /* TODO: find actual value */
|
||||
/* hw_priv->beacon_req_id = cpu_to_le32(0); */
|
||||
hw->queues = 4;
|
||||
hw_priv->noise = -94;
|
||||
|
||||
hw->max_rates = 8;
|
||||
hw->max_rate_tries = 15;
|
||||
hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
|
||||
8 /* TKIP IV */ +
|
||||
12 /* TKIP ICV and MIC */;
|
||||
|
||||
hw->sta_data_size = sizeof(struct bes2600_sta_priv);
|
||||
hw->vif_data_size = sizeof(struct bes2600_vif);
|
||||
|
||||
hw->wiphy->bands[NL80211_BAND_2GHZ] = &bes2600_band_2ghz;
|
||||
hw->wiphy->bands[NL80211_BAND_5GHZ] = &bes2600_band_5ghz;
|
||||
|
||||
/* Channel params have to be cleared before registering wiphy again */
|
||||
for (band = 0; band < NUM_NL80211_BANDS; band++) {
|
||||
sband = hw->wiphy->bands[band];
|
||||
if (!sband)
|
||||
continue;
|
||||
for (i = 0; i < sband->n_channels; i++) {
|
||||
sband->channels[i].flags = 0;
|
||||
sband->channels[i].max_antenna_gain = 0;
|
||||
sband->channels[i].max_power = 30;
|
||||
}
|
||||
}
|
||||
|
||||
hw->wiphy->max_scan_ssids = WSM_SCAN_MAX_NUM_OF_SSIDS;
|
||||
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
|
||||
|
||||
hw->wiphy->iface_combinations = bes2600_if_comb;
|
||||
hw->wiphy->n_iface_combinations = ARRAY_SIZE(bes2600_if_comb);
|
||||
|
||||
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
||||
hw_priv->last_ins_wapi_usk_id = -1;
|
||||
hw_priv->last_del_wapi_usk_id = -1;
|
||||
bes2600_init_wapi_cipher(hw);
|
||||
#endif
|
||||
|
||||
SET_IEEE80211_PERM_ADDR(hw, hw_priv->addresses[0].addr);
|
||||
|
||||
spin_lock_init(&hw_priv->vif_list_lock);
|
||||
sema_init(&hw_priv->wsm_cmd_sema, 1);
|
||||
sema_init(&hw_priv->conf_lock, 1);
|
||||
sema_init(&hw_priv->wsm_oper_lock, 1);
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
spin_lock_init(&hw_priv->tsm_lock);
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
hw_priv->workqueue = create_singlethread_workqueue("bes2600_wq");
|
||||
sema_init(&hw_priv->scan.lock, 1);
|
||||
INIT_WORK(&hw_priv->scan.work, bes2600_scan_work);
|
||||
#ifdef ROAM_OFFLOAD
|
||||
INIT_WORK(&hw_priv->scan.swork, bes2600_sched_scan_work);
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
INIT_DELAYED_WORK(&hw_priv->scan.probe_work, bes2600_probe_work);
|
||||
INIT_DELAYED_WORK(&hw_priv->scan.timeout, bes2600_scan_timeout);
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
INIT_DELAYED_WORK(&hw_priv->advance_scan_timeout,
|
||||
bes2600_advance_scan_timeout);
|
||||
#endif
|
||||
INIT_DELAYED_WORK(&hw_priv->rem_chan_timeout, bes2600_rem_chan_timeout);
|
||||
hw_priv->rtsvalue = 0;
|
||||
spin_lock_init(&hw_priv->rtsvalue_lock);
|
||||
INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work);
|
||||
INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work);
|
||||
spin_lock_init(&hw_priv->event_queue_lock);
|
||||
INIT_LIST_HEAD(&hw_priv->event_queue);
|
||||
INIT_WORK(&hw_priv->event_handler, bes2600_event_handler);
|
||||
INIT_WORK(&hw_priv->ba_work, bes2600_ba_work);
|
||||
spin_lock_init(&hw_priv->ba_lock);
|
||||
timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0);
|
||||
|
||||
if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats,
|
||||
WLAN_LINK_ID_MAX,
|
||||
bes2600_skb_dtor,
|
||||
hw_priv))) {
|
||||
ieee80211_free_hw(hw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (unlikely(bes2600_queue_init(&hw_priv->tx_queue[i],
|
||||
&hw_priv->tx_queue_stats, i, CW12XX_MAX_QUEUE_SZ,
|
||||
bes2600_ttl[i]))) {
|
||||
for (; i > 0; i--)
|
||||
bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]);
|
||||
bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats);
|
||||
ieee80211_free_hw(hw);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
init_waitqueue_head(&hw_priv->channel_switch_done);
|
||||
init_waitqueue_head(&hw_priv->wsm_cmd_wq);
|
||||
init_waitqueue_head(&hw_priv->wsm_startup_done);
|
||||
init_waitqueue_head(&hw_priv->offchannel_wq);
|
||||
hw_priv->offchannel_done = 0;
|
||||
wsm_buf_init(&hw_priv->wsm_cmd_buf);
|
||||
spin_lock_init(&hw_priv->wsm_cmd.lock);
|
||||
timer_setup(&hw_priv->mcu_mon_timer, bes2600_bh_mcu_active_monitor, 0);
|
||||
timer_setup(&hw_priv->lmac_mon_timer, bes2600_bh_lmac_active_monitor, 0);
|
||||
|
||||
bes2600_tx_loop_init(hw_priv);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
bes2600_suspend_status_set(hw_priv, false);
|
||||
bes2600_pending_unjoin_reset(hw_priv);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
hw_priv->test_frame.data = NULL;
|
||||
hw_priv->test_frame.len = 0;
|
||||
#endif /* CONFIG_BES2600_TESTMODE */
|
||||
|
||||
#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
|
||||
hw_priv->wsm_dump_max_size = 20;
|
||||
#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
|
||||
|
||||
for (i = 0; i < CW12XX_MAX_VIFS; i++)
|
||||
hw_priv->hw_bufs_used_vif[i] = 0;
|
||||
|
||||
#ifdef MCAST_FWDING
|
||||
for (i = 0; i < WSM_MAX_BUF; i++)
|
||||
wsm_init_release_buffer_request(hw_priv, i);
|
||||
hw_priv->buf_released = 0;
|
||||
#endif
|
||||
hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
|
||||
hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
|
||||
return hw;
|
||||
}
|
||||
|
||||
static int bes2600_register_common(struct ieee80211_hw *dev)
|
||||
{
|
||||
struct bes2600_common *hw_priv = dev->priv;
|
||||
int err;
|
||||
|
||||
err = ieee80211_register_hw(dev);
|
||||
if (err) {
|
||||
bes_err("Cannot register device (%d).\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
bes2600_debug_init_common(hw_priv);
|
||||
#ifdef CONFIG_PM
|
||||
bes2600_register_pm_notifier(hw_priv);
|
||||
#endif /* CONFIG_PM */
|
||||
bes_info("registered as '%s'\n", wiphy_name(dev->wiphy));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bes2600_free_common(struct ieee80211_hw *dev)
|
||||
{
|
||||
/* struct bes2600_common *hw_priv = dev->priv; */
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
struct bes2600_common *hw_priv = dev->priv;
|
||||
if (hw_priv->test_frame.data) {
|
||||
kfree(hw_priv->test_frame.data);
|
||||
hw_priv->test_frame.data = NULL;
|
||||
hw_priv->test_frame.len = 0;
|
||||
}
|
||||
#endif /* CONFIG_BES2600_TESTMODE */
|
||||
|
||||
/* unsigned int i; */
|
||||
|
||||
ieee80211_free_hw(dev);
|
||||
}
|
||||
|
||||
static void bes2600_unregister_common(struct ieee80211_hw *dev)
|
||||
{
|
||||
struct bes2600_common *hw_priv = dev->priv;
|
||||
int i;
|
||||
|
||||
ieee80211_unregister_hw(dev);
|
||||
|
||||
del_timer_sync(&hw_priv->ba_timer);
|
||||
|
||||
hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
|
||||
bes2600_unregister_bh(hw_priv);
|
||||
|
||||
bes2600_debug_release_common(hw_priv);
|
||||
#ifdef CONFIG_PM
|
||||
bes2600_unregister_pm_notifier(hw_priv);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
wsm_buf_deinit(&hw_priv->wsm_cmd_buf);
|
||||
destroy_workqueue(hw_priv->workqueue);
|
||||
hw_priv->workqueue = NULL;
|
||||
if (hw_priv->skb_cache) {
|
||||
dev_kfree_skb(hw_priv->skb_cache);
|
||||
hw_priv->skb_cache = NULL;
|
||||
}
|
||||
if (hw_priv->sdd) {
|
||||
#ifndef CONFIG_BES2600_STATIC_SDD
|
||||
release_firmware(hw_priv->sdd);
|
||||
#endif
|
||||
hw_priv->sdd = NULL;
|
||||
}
|
||||
for (i = 0; i < 4; ++i)
|
||||
bes2600_queue_deinit(&hw_priv->tx_queue[i]);
|
||||
bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats);
|
||||
for (i = 0; i < CW12XX_MAX_VIFS; i++) {
|
||||
kfree(hw_priv->vif_list[i]);
|
||||
hw_priv->vif_list[i] = NULL;
|
||||
}
|
||||
|
||||
bes2600_pwr_exit(hw_priv);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void cw12xx_set_ifce_comb(struct bes2600_common *hw_priv,
|
||||
struct ieee80211_hw *hw)
|
||||
{
|
||||
#ifdef P2P_MULTIVIF
|
||||
hw_priv->if_limits1[0].max = 2;
|
||||
#else
|
||||
hw_priv->if_limits1[0].max = 1;
|
||||
#endif
|
||||
|
||||
hw_priv->if_limits1[0].types = BIT(NL80211_IFTYPE_STATION);
|
||||
hw_priv->if_limits1[1].max = 1;
|
||||
hw_priv->if_limits1[1].types = BIT(NL80211_IFTYPE_AP);
|
||||
|
||||
#ifdef P2P_MULTIVIF
|
||||
hw_priv->if_limits2[0].max = 3;
|
||||
#else
|
||||
hw_priv->if_limits2[0].max = 2;
|
||||
#endif
|
||||
hw_priv->if_limits2[0].types = BIT(NL80211_IFTYPE_STATION);
|
||||
|
||||
#ifdef P2P_MULTIVIF
|
||||
hw_priv->if_limits3[0].max = 2;
|
||||
#else
|
||||
hw_priv->if_limits3[0].max = 1;
|
||||
#endif
|
||||
|
||||
hw_priv->if_limits3[0].types = BIT(NL80211_IFTYPE_STATION);
|
||||
hw_priv->if_limits3[1].max = 1;
|
||||
hw_priv->if_limits3[1].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
||||
BIT(NL80211_IFTYPE_P2P_GO);
|
||||
|
||||
/* TODO:COMBO: mac80211 doesn't yet support more than 1
|
||||
* different channel */
|
||||
hw_priv->if_combs[0].num_different_channels = 1;
|
||||
#ifdef P2P_MULTIVIF
|
||||
hw_priv->if_combs[0].max_interfaces = 3;
|
||||
#else
|
||||
hw_priv->if_combs[0].max_interfaces = 2;
|
||||
#endif
|
||||
hw_priv->if_combs[0].limits = hw_priv->if_limits1;
|
||||
hw_priv->if_combs[0].n_limits = 2;
|
||||
|
||||
hw_priv->if_combs[1].num_different_channels = 1;
|
||||
|
||||
#ifdef P2P_MULTIVIF
|
||||
hw_priv->if_combs[1].max_interfaces = 3;
|
||||
#else
|
||||
hw_priv->if_combs[1].max_interfaces = 2;
|
||||
#endif
|
||||
hw_priv->if_combs[1].limits = hw_priv->if_limits2;
|
||||
hw_priv->if_combs[1].n_limits = 1;
|
||||
|
||||
hw_priv->if_combs[2].num_different_channels = 1;
|
||||
#ifdef P2P_MULTIVIF
|
||||
hw_priv->if_combs[2].max_interfaces = 3;
|
||||
#else
|
||||
hw_priv->if_combs[2].max_interfaces = 2;
|
||||
#endif
|
||||
hw_priv->if_combs[2].limits = hw_priv->if_limits3;
|
||||
hw_priv->if_combs[2].n_limits = 2;
|
||||
|
||||
hw->wiphy->iface_combinations = &hw_priv->if_combs[0];
|
||||
hw->wiphy->n_iface_combinations = 3;
|
||||
|
||||
}
|
||||
#endif
|
||||
static int bes2600_sbus_comm_init(struct bes2600_common *hw_priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#if defined(FW_DOWNLOAD_BY_USB)
|
||||
if (hw_priv->sbus_ops->reset)
|
||||
hw_priv->sbus_ops->reset(hw_priv->sbus_priv);
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BES2600_WLAN_SPI
|
||||
if (hw_priv->sbus_ops->init)
|
||||
hw_priv->sbus_ops->init(hw_priv->sbus_priv, hw_priv);
|
||||
#endif
|
||||
|
||||
/* Register Interrupt Handler */
|
||||
hw_priv->sbus_ops->irq_subscribe(hw_priv->sbus_priv,
|
||||
(sbus_irq_handler)bes2600_irq_handler, hw_priv);
|
||||
hw_priv->hw_type = HIF_8601_SILICON;
|
||||
hw_priv->hw_revision = BES2600_HW_REV_CUT10;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_core_probe(const struct sbus_ops *sbus_ops,
|
||||
struct sbus_priv *sbus,
|
||||
struct device *pdev,
|
||||
struct bes2600_common **pself)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
//u16 ctrl_reg;
|
||||
struct ieee80211_hw *dev;
|
||||
struct bes2600_common *hw_priv;
|
||||
|
||||
dev = bes2600_init_common(sizeof(struct bes2600_common));
|
||||
if (!dev)
|
||||
goto err;
|
||||
|
||||
global_dev = pdev;
|
||||
|
||||
hw_priv = dev->priv;
|
||||
hw_priv->sbus_ops = sbus_ops;
|
||||
hw_priv->sbus_priv = sbus;
|
||||
hw_priv->pdev = pdev;
|
||||
SET_IEEE80211_DEV(hw_priv->hw, pdev);
|
||||
|
||||
/* WSM callbacks. */
|
||||
hw_priv->wsm_cbc.scan_complete = bes2600_scan_complete_cb;
|
||||
hw_priv->wsm_cbc.tx_confirm = bes2600_tx_confirm_cb;
|
||||
hw_priv->wsm_cbc.rx = bes2600_rx_cb;
|
||||
hw_priv->wsm_cbc.suspend_resume = bes2600_suspend_resume;
|
||||
/* hw_priv->wsm_cbc.set_pm_complete = bes2600_set_pm_complete_cb; */
|
||||
hw_priv->wsm_cbc.channel_switch = bes2600_channel_switch_cb;
|
||||
|
||||
bes2600_pwr_init(hw_priv);
|
||||
|
||||
err = bes2600_register_bh(hw_priv);
|
||||
if (err)
|
||||
goto err1;
|
||||
|
||||
err = bes2600_sbus_comm_init(hw_priv);
|
||||
if (err)
|
||||
goto err2;
|
||||
|
||||
if (bes2600_chrdev_get_fw_type() == BES2600_FW_TYPE_WIFI_NO_SIGNAL) {
|
||||
*pself = dev->priv;
|
||||
if (bes2600_wifi_start(hw_priv))
|
||||
goto err3;
|
||||
}
|
||||
|
||||
err = bes2600_register_common(dev);
|
||||
if (err)
|
||||
goto err3;
|
||||
|
||||
*pself = dev->priv;
|
||||
return err;
|
||||
|
||||
err3:
|
||||
hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
|
||||
if (sbus_ops->reset)
|
||||
sbus_ops->reset(sbus);
|
||||
err2:
|
||||
bes2600_unregister_bh(hw_priv);
|
||||
err1:
|
||||
bes2600_free_common(dev);
|
||||
err:
|
||||
*pself = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
void bes2600_core_release(struct bes2600_common *self)
|
||||
{
|
||||
bes2600_unregister_common(self->hw);
|
||||
bes2600_free_common(self->hw);
|
||||
return;
|
||||
}
|
||||
|
||||
#if (GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */
|
||||
int access_file(char *path, char *buffer, int size, int isRead)
|
||||
{
|
||||
int ret=0;
|
||||
struct file *fp;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
if(isRead)
|
||||
fp = filp_open(path,O_RDONLY,S_IRUSR);
|
||||
else
|
||||
fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR);
|
||||
|
||||
if (IS_ERR(fp)) {
|
||||
bes_err("BES2600 : can't open %s\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isRead) {
|
||||
fp->f_pos = 0;
|
||||
set_fs(KERNEL_DS);
|
||||
ret = vfs_read(fp,buffer,size,&fp->f_pos);
|
||||
set_fs(old_fs);
|
||||
} else {
|
||||
fp->f_pos = 0;
|
||||
set_fs(KERNEL_DS);
|
||||
ret = vfs_write(fp,buffer,size,&fp->f_pos);
|
||||
set_fs(old_fs);
|
||||
}
|
||||
filp_close(fp,NULL);
|
||||
|
||||
bes_info("BES2600 : access_file return code(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int bes2600_wifi_start(struct bes2600_common *hw_priv)
|
||||
{
|
||||
int ret = 0, if_id;
|
||||
|
||||
if (hw_priv->sbus_ops->gpio_wake) {
|
||||
hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_ON);
|
||||
}
|
||||
|
||||
if (hw_priv->sbus_ops->sbus_active &&
|
||||
WARN_ON((ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_WIFI))))
|
||||
goto err;
|
||||
|
||||
if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
|
||||
hw_priv->wsm_caps.firmwareReady, 10 * HZ) <= 0) {
|
||||
|
||||
/* TODO: Needs to find how to reset device */
|
||||
/* in QUEUE mode properly. */
|
||||
bes_info("startup timeout!!!\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bes2600_chrdev_is_signal_mode()) {
|
||||
for (if_id = 0; if_id < 2; if_id++) {
|
||||
/* Enable multi-TX confirmation */
|
||||
if (WARN_ON((ret = wsm_use_multi_tx_conf(hw_priv, true, if_id)))) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
bes2600_pwr_start(hw_priv);
|
||||
|
||||
err:
|
||||
if (hw_priv->sbus_ops->gpio_sleep) {
|
||||
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_ON);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_wifi_stop(struct bes2600_common *hw_priv)
|
||||
{
|
||||
int ret;
|
||||
unsigned long status = 0;
|
||||
|
||||
status = wait_event_timeout(hw_priv->bh_evt_wq, (!hw_priv->hw_bufs_used), 3 * HZ);
|
||||
if (!status)
|
||||
bes_err("communication exception!\n");
|
||||
|
||||
if(hw_priv->sbus_ops->gpio_wake) {
|
||||
hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
|
||||
}
|
||||
|
||||
bes2600_pwr_stop(hw_priv);
|
||||
|
||||
if (hw_priv->sbus_ops->sbus_deactive &&
|
||||
WARN_ON(ret = hw_priv->sbus_ops->sbus_deactive(hw_priv->sbus_priv, SUBSYSTEM_WIFI))) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(hw_priv->sbus_ops->gpio_sleep) {
|
||||
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
|
||||
}
|
||||
|
||||
memset(&hw_priv->wsm_caps, 0, sizeof(hw_priv->wsm_caps));
|
||||
hw_priv->wsm_rx_seq[0] = 0;
|
||||
hw_priv->wsm_rx_seq[1] = 0;
|
||||
hw_priv->wsm_tx_seq[0] = 0;
|
||||
hw_priv->wsm_tx_seq[1] = 0;
|
||||
hw_priv->wsm_tx_pending[0] = 0;
|
||||
hw_priv->wsm_tx_pending[1] = 0;
|
||||
del_timer_sync(&hw_priv->mcu_mon_timer);
|
||||
del_timer_sync(&hw_priv->lmac_mon_timer);
|
||||
#ifdef CONFIG_BES2600_STATIC_SDD
|
||||
hw_priv->sdd = NULL;
|
||||
#else
|
||||
#error "TO BE CONTINUED: release SDD file"
|
||||
#endif
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if(hw_priv->sbus_ops->gpio_sleep) {
|
||||
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
+603
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
* Mac80211 power management API for BES2600 drivers
|
||||
*
|
||||
* Copyright (c) 2011, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/suspend.h>
|
||||
#include "bes2600.h"
|
||||
#include "pm.h"
|
||||
#include "sta.h"
|
||||
#include "bh.h"
|
||||
#include "sbus.h"
|
||||
#include "bes_chardev.h"
|
||||
#include "bes_log.h"
|
||||
|
||||
#define BES2600_BEACON_SKIPPING_MULTIPLIER 3
|
||||
|
||||
struct bes2600_udp_port_filter {
|
||||
struct wsm_udp_port_filter_hdr hdr;
|
||||
struct wsm_udp_port_filter dhcp;
|
||||
struct wsm_udp_port_filter upnp;
|
||||
struct wsm_udp_port_filter mdns;
|
||||
} __packed;
|
||||
|
||||
struct bes2600_ether_type_filter {
|
||||
struct wsm_ether_type_filter_hdr hdr;
|
||||
struct wsm_ether_type_filter pae;
|
||||
struct wsm_ether_type_filter wapi;
|
||||
struct wsm_ether_type_filter append;
|
||||
} __packed;
|
||||
|
||||
static struct bes2600_udp_port_filter bes2600_udp_port_filter_on = {
|
||||
.hdr.nrFilters = 3,
|
||||
.dhcp = {
|
||||
.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
|
||||
.portType = WSM_FILTER_PORT_TYPE_DST,
|
||||
.udpPort = __cpu_to_le16(67),
|
||||
},
|
||||
.upnp = {
|
||||
.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
|
||||
.portType = WSM_FILTER_PORT_TYPE_DST,
|
||||
.udpPort = __cpu_to_le16(1900),
|
||||
},
|
||||
.mdns = {
|
||||
.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
|
||||
.portType = WSM_FILTER_PORT_TYPE_DST,
|
||||
.udpPort = __cpu_to_le16(5353),
|
||||
},
|
||||
/* Please add other known ports to be filtered out here and
|
||||
* update nrFilters field in the header.
|
||||
* Up to 4 filters are allowed. */
|
||||
};
|
||||
|
||||
static struct wsm_udp_port_filter_hdr bes2600_udp_port_filter_off = {
|
||||
.nrFilters = 0,
|
||||
};
|
||||
|
||||
#ifndef ETH_P_WAPI
|
||||
#define ETH_P_WAPI 0x88B4
|
||||
#endif
|
||||
|
||||
#define ETH_P_UNKNOWN 0xFFFF
|
||||
static struct bes2600_ether_type_filter bes2600_ether_type_filter_on = {
|
||||
.hdr.nrFilters = 3,
|
||||
.hdr.extFlags = WSM_ETH_FILTER_EXT_DISABLE_IPV6_MATCH, // patch for disable lmac SUSPEND_MODE_IPV6_FIX
|
||||
.pae = {
|
||||
.filterAction = WSM_FILTER_ACTION_FILTER_IN,
|
||||
.etherType = __cpu_to_le16(ETH_P_PAE),
|
||||
},
|
||||
.wapi = {
|
||||
.filterAction = WSM_FILTER_ACTION_FILTER_IN,
|
||||
.etherType = __cpu_to_le16(ETH_P_WAPI),
|
||||
},
|
||||
// add for lmac ether filter strategy: If every filtermode is FilterIN, discard all the frame which is mismatched
|
||||
.append = {
|
||||
.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
|
||||
.etherType = __cpu_to_le16(ETH_P_UNKNOWN),
|
||||
},
|
||||
/* Please add other known ether types to be filtered out here and
|
||||
* update nrFilters field in the header.
|
||||
* Up to 4 filters are allowed. */
|
||||
};
|
||||
|
||||
static struct wsm_ether_type_filter_hdr bes2600_ether_type_filter_off = {
|
||||
.nrFilters = 0,
|
||||
};
|
||||
|
||||
#ifdef IPV6_FILTERING
|
||||
static struct wsm_ipv6_filter bes2600_ipv6_filter_on = {
|
||||
.hdr.numfilter = 1,
|
||||
.hdr.action_mode = WSM_FILTER_ACTION_FILTER_IN,
|
||||
.ipv6filter[0] = {
|
||||
.filter_mode = WSM_FILTER_ACTION_FILTER_IN,
|
||||
.address_mode = WSM_IP_DATA_FRAME_ADDRMODE_DEST,
|
||||
/* a random ipvd addr, in order to filter all ipv6 packet */
|
||||
.ipv6 = {0x01, 0x28, 0x35, 0xde, 0xbf, 0x34, 0x9d, 0x8a,
|
||||
0x47, 0x62, 0x85, 0x69, 0x7e, 0x8c, 0x29, 0x38},
|
||||
}
|
||||
};
|
||||
|
||||
static struct wsm_ipv6_filter bes2600_ipv6_filter_off = {
|
||||
.hdr.numfilter = 0,
|
||||
.hdr.action_mode = WSM_FILTER_ACTION_IGNORE,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __bes2600_wow_suspend(struct bes2600_vif *priv,
|
||||
struct cfg80211_wowlan *wowlan);
|
||||
static int __bes2600_wow_resume(struct bes2600_vif *priv);
|
||||
|
||||
|
||||
/* private */
|
||||
struct bes2600_suspend_state {
|
||||
unsigned long bss_loss_tmo;
|
||||
unsigned long connection_loss_tmo;
|
||||
unsigned long join_tmo;
|
||||
unsigned long direct_probe;
|
||||
unsigned long link_id_gc;
|
||||
};
|
||||
|
||||
void bes2600_suspend_status_set(struct bes2600_common *hw_priv, bool status)
|
||||
{
|
||||
hw_priv->suspend_in_progress = status;
|
||||
}
|
||||
|
||||
bool bes2600_suspend_status_get(struct bes2600_common *hw_priv)
|
||||
{
|
||||
return hw_priv->suspend_in_progress;
|
||||
}
|
||||
|
||||
void bes2600_pending_unjoin_reset(struct bes2600_common *hw_priv)
|
||||
{
|
||||
hw_priv->unjoin_if_id_slots = 0x00;
|
||||
}
|
||||
|
||||
void bes2600_pending_unjoin_set(struct bes2600_common *hw_priv, int if_id)
|
||||
{
|
||||
if(if_id > 1)
|
||||
bes_err("unexpected if_id: %d\n", if_id);
|
||||
else
|
||||
hw_priv->unjoin_if_id_slots |= (1 << if_id);
|
||||
}
|
||||
|
||||
bool bes2600_pending_unjoin_get(struct bes2600_common *hw_priv, int if_id)
|
||||
{
|
||||
if(if_id > 1) {
|
||||
bes_err("unexpected if_id: %d\n", if_id);
|
||||
return false;
|
||||
} else
|
||||
return hw_priv->unjoin_if_id_slots & (1 << if_id);
|
||||
}
|
||||
|
||||
static int bes2600_pm_notifier(struct notifier_block *notifier,
|
||||
unsigned long pm_event,
|
||||
void *unused)
|
||||
{
|
||||
int if_id;
|
||||
struct bes2600_vif *priv;
|
||||
struct bes2600_common *hw_priv = container_of(notifier,
|
||||
struct bes2600_common,
|
||||
pm_notify);
|
||||
|
||||
switch (pm_event) {
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
case PM_SUSPEND_PREPARE:
|
||||
bes2600_suspend_status_set(hw_priv, true);
|
||||
break;
|
||||
|
||||
case PM_POST_RESTORE:
|
||||
case PM_POST_HIBERNATION:
|
||||
case PM_POST_SUSPEND:
|
||||
bes2600_suspend_status_set(hw_priv, false);
|
||||
if(hw_priv->unjoin_if_id_slots) {
|
||||
for(if_id = 0; if_id < 2; if_id++) {
|
||||
if(bes2600_pending_unjoin_get(hw_priv, if_id)) {
|
||||
priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
|
||||
ieee80211_connection_loss(priv->vif);
|
||||
}
|
||||
}
|
||||
bes2600_pending_unjoin_reset(hw_priv);
|
||||
}
|
||||
break;
|
||||
|
||||
case PM_RESTORE_PREPARE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
void bes2600_register_pm_notifier(struct bes2600_common *hw_priv)
|
||||
{
|
||||
hw_priv->pm_notify.notifier_call = bes2600_pm_notifier;
|
||||
register_pm_notifier(&hw_priv->pm_notify);
|
||||
}
|
||||
|
||||
void bes2600_unregister_pm_notifier(struct bes2600_common *hw_priv)
|
||||
{
|
||||
unregister_pm_notifier(&hw_priv->pm_notify);
|
||||
}
|
||||
|
||||
static long bes2600_suspend_work(struct delayed_work *work)
|
||||
{
|
||||
int ret = cancel_delayed_work(work);
|
||||
long tmo;
|
||||
if (ret > 0) {
|
||||
/* Timer is pending */
|
||||
tmo = work->timer.expires - jiffies;
|
||||
if (tmo < 0)
|
||||
tmo = 0;
|
||||
} else {
|
||||
tmo = -1;
|
||||
}
|
||||
return tmo;
|
||||
}
|
||||
|
||||
static int bes2600_resume_work(struct bes2600_common *hw_priv,
|
||||
struct delayed_work *work,
|
||||
unsigned long tmo)
|
||||
{
|
||||
if ((long)tmo < 0)
|
||||
return 1;
|
||||
|
||||
return queue_delayed_work(hw_priv->workqueue, work, tmo);
|
||||
}
|
||||
|
||||
int bes2600_can_suspend(struct bes2600_common *priv)
|
||||
{
|
||||
if (atomic_read(&priv->bh_rx)) {
|
||||
wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bes2600_can_suspend);
|
||||
|
||||
int bes2600_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
||||
{
|
||||
struct bes2600_common *hw_priv = hw->priv;
|
||||
struct bes2600_vif *priv;
|
||||
int i, ret = 0;
|
||||
unsigned long begin, end, diff;
|
||||
char *busy_event_buffer = NULL;
|
||||
|
||||
bes_devel("bes2600_wow_suspend enter\n");
|
||||
|
||||
WARN_ON(!atomic_read(&hw_priv->num_vifs));
|
||||
|
||||
/* reset wakeup reason to default */
|
||||
bes2600_chrdev_wifi_update_wakeup_reason(0, 0);
|
||||
|
||||
#ifdef ROAM_OFFLOAD
|
||||
bes2600_for_each_vif(hw_priv, priv, i) {
|
||||
#ifdef P2P_MULTIVIF
|
||||
if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
|
||||
#else
|
||||
if (!priv)
|
||||
#endif
|
||||
continue;
|
||||
if((priv->vif->type == NL80211_IFTYPE_STATION)
|
||||
&& (priv->join_status == BES2600_JOIN_STATUS_STA)) {
|
||||
down(&hw_priv->scan.lock);
|
||||
hw_priv->scan.if_id = priv->if_id;
|
||||
bes2600_sched_scan_work(&hw_priv->scan.swork);
|
||||
}
|
||||
}
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
|
||||
/* Do not suspend when datapath is not idle */
|
||||
if (hw_priv->tx_queue_stats.num_queued[0]
|
||||
+ hw_priv->tx_queue_stats.num_queued[1])
|
||||
return -EBUSY;
|
||||
|
||||
|
||||
/* Make sure there is no configuration requests in progress. */
|
||||
if (down_trylock(&hw_priv->conf_lock))
|
||||
return -EBUSY;
|
||||
|
||||
/* Do not suspend when scanning or ROC*/
|
||||
if (down_trylock(&hw_priv->scan.lock))
|
||||
goto revert1;
|
||||
|
||||
/* Do not suspend when probe is doing */
|
||||
if (delayed_work_pending(&hw_priv->scan.probe_work))
|
||||
goto revert2;
|
||||
|
||||
/* record suspend start time */
|
||||
begin = jiffies;
|
||||
|
||||
/* wait uitil bes2600 finish current pending operation */
|
||||
if (!bes2600_pwr_device_is_idle(hw_priv)) {
|
||||
/* clear power busy event */
|
||||
bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_TX, 10);
|
||||
|
||||
/* wait device enter lp mode */
|
||||
if (wait_event_timeout(hw_priv->bes_power.dev_lp_wq,
|
||||
bes2600_pwr_device_is_idle(hw_priv), HZ * 5) <= 0) {
|
||||
bes_err("wait device idle timeout\n");
|
||||
busy_event_buffer = kmalloc(4096, GFP_KERNEL);
|
||||
|
||||
if(!busy_event_buffer)
|
||||
goto revert2;
|
||||
|
||||
if(bes2600_pwr_busy_event_record(hw_priv, busy_event_buffer, 4096) == 0) {
|
||||
bes_devel("%s\n", busy_event_buffer);
|
||||
} else {
|
||||
bes_err("busy event show failed\n");
|
||||
}
|
||||
|
||||
kfree(busy_event_buffer);
|
||||
goto revert2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Lock TX. */
|
||||
wsm_lock_tx_async(hw_priv);
|
||||
|
||||
/* mark suspend start to avoid device to exit ps mode when setting device */
|
||||
bes2600_pwr_suspend_start(hw_priv);
|
||||
|
||||
/* set filters and offload based on interface */
|
||||
bes2600_for_each_vif(hw_priv, priv, i) {
|
||||
#ifdef P2P_MULTIVIF
|
||||
if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
|
||||
#else
|
||||
if (!priv)
|
||||
#endif
|
||||
continue;
|
||||
|
||||
ret = __bes2600_wow_suspend(priv,
|
||||
wowlan);
|
||||
if (ret) {
|
||||
for (; i >= 0; i--) {
|
||||
if (!hw_priv->vif_list[i])
|
||||
continue;
|
||||
priv = (struct bes2600_vif *)
|
||||
hw_priv->vif_list[i]->drv_priv;
|
||||
__bes2600_wow_resume(priv);
|
||||
}
|
||||
goto revert3;
|
||||
}
|
||||
}
|
||||
|
||||
/* mark suspend end */
|
||||
bes2600_pwr_suspend_end(hw_priv);
|
||||
|
||||
/* Stop serving thread */
|
||||
if (bes2600_bh_suspend(hw_priv)) {
|
||||
bes_err("%s: bes2600_bh_suspend failed\n",
|
||||
__func__);
|
||||
bes2600_wow_resume(hw);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Force resume if event is coming from the device. */
|
||||
if (atomic_read(&hw_priv->bh_rx)) {
|
||||
bes_devel("%s: incoming event present - resume\n",
|
||||
__func__);
|
||||
bes2600_wow_resume(hw);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* calculate the time consumed by bes2600 suspend flow */
|
||||
end = jiffies;
|
||||
diff = end - begin;
|
||||
bes_devel("%s consume %d ms\n", __func__, jiffies_to_msecs(diff));
|
||||
|
||||
return 0;
|
||||
|
||||
revert3:
|
||||
bes2600_pwr_suspend_end(hw_priv);
|
||||
wsm_unlock_tx(hw_priv);
|
||||
revert2:
|
||||
up(&hw_priv->scan.lock);
|
||||
revert1:
|
||||
up(&hw_priv->conf_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void bes2600_set_ehter_and_udp_filter(struct bes2600_common *hw_priv,
|
||||
struct wsm_ether_type_filter_hdr *ether_type, struct wsm_udp_port_filter_hdr *udp_type,
|
||||
int if_id)
|
||||
{
|
||||
/* Set UDP filter */
|
||||
wsm_set_udp_port_filter(hw_priv, udp_type, if_id);
|
||||
|
||||
/* Set ethernet frame type filter */
|
||||
wsm_set_ether_type_filter(hw_priv, ether_type, if_id);
|
||||
}
|
||||
|
||||
static int __bes2600_wow_suspend(struct bes2600_vif *priv,
|
||||
struct cfg80211_wowlan *wowlan)
|
||||
{
|
||||
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
||||
struct bes2600_pm_state_vif *pm_state_vif = &priv->pm_state_vif;
|
||||
struct bes2600_suspend_state *state;
|
||||
int ret;
|
||||
|
||||
#ifdef MCAST_FWDING
|
||||
struct wsm_forwarding_offload fwdoffload = {
|
||||
.fwenable = 0x1,
|
||||
.flags = 0x1,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Do not suspend when join work is scheduled */
|
||||
if (work_pending(&priv->join_work))
|
||||
goto revert1;
|
||||
|
||||
bes2600_set_ehter_and_udp_filter(hw_priv, &bes2600_ether_type_filter_on.hdr,
|
||||
&bes2600_udp_port_filter_on.hdr, priv->if_id);
|
||||
|
||||
/* Set ipv6 filer */
|
||||
#ifdef IPV6_FILTERING
|
||||
wsm_set_ipv6_filter(hw_priv, &bes2600_ipv6_filter_on.hdr, priv->if_id);
|
||||
#endif
|
||||
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
||||
WARN_ON(wsm_set_keepalive_filter(priv, true));
|
||||
|
||||
/* Set Multicast Address Filter */
|
||||
if (priv->multicast_filter.numOfAddresses) {
|
||||
priv->multicast_filter.enable = __cpu_to_le32(2);
|
||||
wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
|
||||
}
|
||||
|
||||
#ifdef MCAST_FWDING
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
||||
WARN_ON(wsm_set_forwarding_offlad(hw_priv,
|
||||
&fwdoffload,priv->if_id));
|
||||
#endif
|
||||
|
||||
/* Allocate state */
|
||||
state = kzalloc(sizeof(struct bes2600_suspend_state), GFP_KERNEL);
|
||||
if (!state)
|
||||
goto revert2;
|
||||
|
||||
/* Store delayed work states. */
|
||||
state->bss_loss_tmo =
|
||||
bes2600_suspend_work(&priv->bss_loss_work);
|
||||
state->connection_loss_tmo =
|
||||
bes2600_suspend_work(&priv->connection_loss_work);
|
||||
state->join_tmo =
|
||||
bes2600_suspend_work(&priv->join_timeout);
|
||||
state->link_id_gc =
|
||||
bes2600_suspend_work(&priv->link_id_gc_work);
|
||||
|
||||
ret = timer_pending(&priv->mcast_timeout);
|
||||
if (ret)
|
||||
goto revert3;
|
||||
|
||||
/* Store suspend state */
|
||||
pm_state_vif->suspend_state = state;
|
||||
|
||||
return 0;
|
||||
|
||||
revert3:
|
||||
bes2600_resume_work(hw_priv, &priv->bss_loss_work,
|
||||
state->bss_loss_tmo);
|
||||
bes2600_resume_work(hw_priv, &priv->connection_loss_work,
|
||||
state->connection_loss_tmo);
|
||||
bes2600_resume_work(hw_priv, &priv->join_timeout,
|
||||
state->join_tmo);
|
||||
bes2600_resume_work(hw_priv, &priv->link_id_gc_work,
|
||||
state->link_id_gc);
|
||||
kfree(state);
|
||||
revert2:
|
||||
wsm_set_udp_port_filter(hw_priv, &bes2600_udp_port_filter_off,
|
||||
priv->if_id);
|
||||
wsm_set_ether_type_filter(hw_priv, &bes2600_ether_type_filter_off,
|
||||
priv->if_id);
|
||||
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
||||
WARN_ON(wsm_set_keepalive_filter(priv, false));
|
||||
|
||||
/* Set Multicast Address Filter */
|
||||
if (priv->multicast_filter.numOfAddresses) {
|
||||
priv->multicast_filter.enable = __cpu_to_le32(1);
|
||||
wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
|
||||
}
|
||||
|
||||
|
||||
#ifdef MCAST_FWDING
|
||||
fwdoffload.flags = 0x0;
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
||||
WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
|
||||
#endif
|
||||
revert1:
|
||||
up(&hw_priv->conf_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
int bes2600_wow_resume(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct bes2600_common *hw_priv = hw->priv;
|
||||
struct bes2600_vif *priv;
|
||||
int i, ret = 0;
|
||||
|
||||
bes_devel("bes2600_wow_resume enter\n");
|
||||
WARN_ON(!atomic_read(&hw_priv->num_vifs));
|
||||
|
||||
up(&hw_priv->scan.lock);
|
||||
|
||||
/* Resume BH thread */
|
||||
WARN_ON(bes2600_bh_resume(hw_priv));
|
||||
|
||||
/* mark resume start to avoid device to exit ps mode when setting device */
|
||||
bes2600_pwr_resume_start(hw_priv);
|
||||
|
||||
/* set filters and offload based on interface */
|
||||
bes2600_for_each_vif(hw_priv, priv, i) {
|
||||
#ifdef P2P_MULTIVIF
|
||||
if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
|
||||
#else
|
||||
if (!priv)
|
||||
#endif
|
||||
continue;
|
||||
ret = __bes2600_wow_resume(priv);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
/* mark resume end */
|
||||
bes2600_pwr_resume_end(hw_priv);
|
||||
|
||||
wsm_unlock_tx(hw_priv);
|
||||
/* Unlock configuration mutex */
|
||||
up(&hw_priv->conf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __bes2600_wow_resume(struct bes2600_vif *priv)
|
||||
{
|
||||
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
||||
struct bes2600_pm_state_vif *pm_state_vif = &priv->pm_state_vif;
|
||||
struct bes2600_suspend_state *state;
|
||||
|
||||
#ifdef MCAST_FWDING
|
||||
struct wsm_forwarding_offload fwdoffload = {
|
||||
.fwenable = 0x1,
|
||||
.flags = 0x0,
|
||||
};
|
||||
#endif
|
||||
state = pm_state_vif->suspend_state;
|
||||
pm_state_vif->suspend_state = NULL;
|
||||
|
||||
#ifdef ROAM_OFFLOAD
|
||||
if((priv->vif->type == NL80211_IFTYPE_STATION)
|
||||
&& (priv->join_status == BES2600_JOIN_STATUS_STA))
|
||||
bes2600_hw_sched_scan_stop(hw_priv);
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
||||
WARN_ON(wsm_set_keepalive_filter(priv, false));
|
||||
|
||||
/* Set Multicast Address Filter */
|
||||
if (priv->multicast_filter.numOfAddresses) {
|
||||
priv->multicast_filter.enable = __cpu_to_le32(1);
|
||||
wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
|
||||
}
|
||||
|
||||
#ifdef MCAST_FWDING
|
||||
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
||||
WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
|
||||
#endif
|
||||
|
||||
/* Resume delayed work */
|
||||
bes2600_resume_work(hw_priv, &priv->bss_loss_work,
|
||||
state->bss_loss_tmo);
|
||||
bes2600_resume_work(hw_priv, &priv->connection_loss_work,
|
||||
state->connection_loss_tmo);
|
||||
bes2600_resume_work(hw_priv, &priv->join_timeout,
|
||||
state->join_tmo);
|
||||
bes2600_resume_work(hw_priv, &priv->link_id_gc_work,
|
||||
state->link_id_gc);
|
||||
|
||||
/* Remove UDP port filter */
|
||||
wsm_set_udp_port_filter(hw_priv, &bes2600_udp_port_filter_off,
|
||||
priv->if_id);
|
||||
|
||||
/* Remove ethernet frame type filter */
|
||||
wsm_set_ether_type_filter(hw_priv, &bes2600_ether_type_filter_off,
|
||||
priv->if_id);
|
||||
|
||||
/* Remove ipv6 filer */
|
||||
#ifdef IPV6_FILTERING
|
||||
wsm_set_ipv6_filter(hw_priv, &bes2600_ipv6_filter_off.hdr, priv->if_id);
|
||||
#endif
|
||||
|
||||
/* Free memory */
|
||||
kfree(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Mac80211 power management interface for BES2600 mac80211 drivers
|
||||
*
|
||||
* Copyright (c) 2011, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef PM_H_INCLUDED
|
||||
#define PM_H_INCLUDED
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* extern */ struct bes2600_common;
|
||||
/* private */ struct bes2600_suspend_state;
|
||||
|
||||
struct bes2600_pm_state_vif {
|
||||
struct bes2600_suspend_state *suspend_state;
|
||||
};
|
||||
|
||||
void bes2600_suspend_status_set(struct bes2600_common *hw_priv, bool status);
|
||||
bool bes2600_suspend_status_get(struct bes2600_common *hw_priv);
|
||||
void bes2600_pending_unjoin_reset(struct bes2600_common *hw_priv);
|
||||
void bes2600_pending_unjoin_set(struct bes2600_common *hw_priv, int if_id);
|
||||
bool bes2600_pending_unjoin_get(struct bes2600_common *hw_priv, int if_id);
|
||||
void bes2600_register_pm_notifier(struct bes2600_common *hw_priv);
|
||||
void bes2600_unregister_pm_notifier(struct bes2600_common *hw_priv);
|
||||
int bes2600_can_suspend(struct bes2600_common *priv);
|
||||
int bes2600_wow_suspend(struct ieee80211_hw *hw,
|
||||
struct cfg80211_wowlan *wowlan);
|
||||
int bes2600_wow_resume(struct ieee80211_hw *hw);
|
||||
#else
|
||||
static inline int bes2600_can_suspend(struct bes2600_common *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#endif
|
||||
+960
@@ -0,0 +1,960 @@
|
||||
/*
|
||||
* O(1) TX queue with built-in allocator for BES2600 drivers
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <net/mac80211.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/version.h>
|
||||
#include "bes2600.h"
|
||||
#include "queue.h"
|
||||
#include "debug.h"
|
||||
#include "bes_log.h"
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
#include <linux/time.h>
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
|
||||
/* private */ struct bes2600_queue_item
|
||||
{
|
||||
struct list_head head;
|
||||
struct sk_buff *skb;
|
||||
u32 packetID;
|
||||
unsigned long queue_timestamp;
|
||||
unsigned long xmit_timestamp;
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
unsigned long mdelay_timestamp;
|
||||
unsigned long qdelay_timestamp;
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
struct bes2600_txpriv txpriv;
|
||||
u8 generation;
|
||||
};
|
||||
|
||||
int bes2600_queue_get_skb_and_timestamp(struct bes2600_queue *queue, u32 packetID,
|
||||
struct sk_buff **skb, struct bes2600_txpriv **txpriv,
|
||||
unsigned long *timestamp)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
|
||||
struct bes2600_queue_item *item;
|
||||
bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
|
||||
&item_generation, &item_id, &if_id, &link_id);
|
||||
if (unlikely(item_id >= (u8) queue->capacity))
|
||||
return -EINVAL;
|
||||
item = &queue->pool[item_id];
|
||||
spin_lock_bh(&queue->lock);
|
||||
BUG_ON(queue_id != queue->queue_id);
|
||||
if (unlikely(queue_generation != queue->generation)) {
|
||||
WARN(1, "queue generation mismatch, %u, expect %u, if_id: %u.\n",
|
||||
queue_generation, queue->generation, if_id);
|
||||
ret = -ENOENT;
|
||||
} else if (unlikely(item_generation != item->generation)) {
|
||||
WARN(1, "item generation mismatch, %u, expect %u.\n",
|
||||
item_generation, item->generation);
|
||||
ret = -ENOENT;
|
||||
} else if (unlikely(WARN_ON(!item->skb))) {
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
*skb = item->skb;
|
||||
*txpriv = &item->txpriv;
|
||||
*timestamp = item->xmit_timestamp;
|
||||
}
|
||||
spin_unlock_bh(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static inline void __bes2600_queue_lock(struct bes2600_queue *queue)
|
||||
{
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
if (queue->tx_locked_cnt++ == 0) {
|
||||
bes_devel("[TX] Queue %d is locked.\n", queue->queue_id);
|
||||
ieee80211_stop_queue(stats->hw_priv->hw, queue->queue_id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __bes2600_queue_unlock(struct bes2600_queue *queue)
|
||||
{
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
BUG_ON(!queue->tx_locked_cnt);
|
||||
if (--queue->tx_locked_cnt == 0) {
|
||||
bes_devel("[TX] Queue %d is unlocked.\n", queue->queue_id);
|
||||
ieee80211_wake_queue(stats->hw_priv->hw, queue->queue_id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32 bes2600_queue_make_packet_id(u8 queue_generation, u8 queue_id,
|
||||
u8 item_generation, u8 item_id,
|
||||
u8 if_id, u8 link_id)
|
||||
{
|
||||
/*TODO:COMBO: Add interfaceID to the packetID */
|
||||
return ((u32)item_id << 0) |
|
||||
((u32)item_generation << 8) |
|
||||
((u32)queue_id << 16) |
|
||||
((u32)if_id << 20) |
|
||||
((u32)link_id << 24) |
|
||||
((u32)queue_generation << 28);
|
||||
}
|
||||
|
||||
static void bes2600_queue_post_gc(struct bes2600_queue_stats *stats,
|
||||
struct list_head *gc_list)
|
||||
{
|
||||
struct bes2600_queue_item *item;
|
||||
|
||||
while (!list_empty(gc_list)) {
|
||||
item = list_first_entry(
|
||||
gc_list, struct bes2600_queue_item, head);
|
||||
list_del(&item->head);
|
||||
stats->skb_dtor(stats->hw_priv, item->skb, &item->txpriv);
|
||||
kfree(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void bes2600_queue_register_post_gc(struct list_head *gc_list,
|
||||
struct bes2600_queue_item *item)
|
||||
{
|
||||
struct bes2600_queue_item *gc_item;
|
||||
gc_item = kmalloc(sizeof(struct bes2600_queue_item),
|
||||
GFP_ATOMIC);
|
||||
BUG_ON(!gc_item);
|
||||
memcpy(gc_item, item, sizeof(struct bes2600_queue_item));
|
||||
list_add_tail(&gc_item->head, gc_list);
|
||||
}
|
||||
|
||||
static void bes2600_queue_pending_record(struct list_head *pending_record_list,
|
||||
struct bes2600_queue_item *pending_item)
|
||||
{
|
||||
struct bes2600_queue_item *record_item;
|
||||
|
||||
record_item = kmalloc(sizeof(struct bes2600_queue_item),GFP_ATOMIC);
|
||||
BUG_ON(!record_item);
|
||||
memcpy(record_item, pending_item, sizeof(struct bes2600_queue_item));
|
||||
record_item->skb = skb_clone(pending_item->skb, GFP_ATOMIC);
|
||||
list_add_tail(&record_item->head, pending_record_list);
|
||||
}
|
||||
|
||||
static void __bes2600_queue_gc(struct bes2600_queue *queue,
|
||||
struct list_head *head,
|
||||
bool unlock)
|
||||
{
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
struct bes2600_queue_item *item = NULL;
|
||||
struct bes2600_vif *priv;
|
||||
int if_id;
|
||||
bool wakeup_stats = false;
|
||||
|
||||
while (!list_empty(&queue->queue)) {
|
||||
struct bes2600_txpriv *txpriv;
|
||||
item = list_first_entry(
|
||||
&queue->queue, struct bes2600_queue_item, head);
|
||||
if (jiffies - item->queue_timestamp < queue->ttl)
|
||||
break;
|
||||
|
||||
txpriv = &item->txpriv;
|
||||
if_id = txpriv->if_id;
|
||||
--queue->num_queued;
|
||||
--queue->num_queued_vif[if_id];
|
||||
--queue->link_map_cache[if_id][txpriv->link_id];
|
||||
spin_lock_bh(&stats->lock);
|
||||
--stats->num_queued[if_id];
|
||||
if (!--stats->link_map_cache[if_id][txpriv->link_id])
|
||||
wakeup_stats = true;
|
||||
spin_unlock_bh(&stats->lock);
|
||||
priv = cw12xx_hwpriv_to_vifpriv(stats->hw_priv, if_id);
|
||||
if (priv) {
|
||||
bes2600_debug_tx_ttl(priv);
|
||||
spin_unlock(&priv->vif_lock);
|
||||
}
|
||||
bes2600_queue_register_post_gc(head, item);
|
||||
item->skb = NULL;
|
||||
list_move_tail(&item->head, &queue->free_pool);
|
||||
}
|
||||
|
||||
if (wakeup_stats)
|
||||
wake_up(&stats->wait_link_id_empty);
|
||||
|
||||
if (queue->overfull) {
|
||||
if (queue->num_queued <= ((stats->hw_priv->vif0_throttle +
|
||||
stats->hw_priv->vif1_throttle + 2)/2)) {
|
||||
queue->overfull = false;
|
||||
if (unlock)
|
||||
__bes2600_queue_unlock(queue);
|
||||
} else if (item) {
|
||||
unsigned long tmo = item->queue_timestamp + queue->ttl;
|
||||
mod_timer(&queue->gc, tmo);
|
||||
bes2600_pwr_set_busy_event_with_timeout_async(stats->hw_priv,
|
||||
BES_PWR_LOCK_ON_QUEUE_GC, jiffies_to_msecs(tmo - jiffies));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bes2600_queue_gc(struct timer_list *t)
|
||||
{
|
||||
LIST_HEAD(list);
|
||||
struct bes2600_queue *queue = from_timer(queue, t, gc);
|
||||
|
||||
spin_lock_bh(&queue->lock);
|
||||
__bes2600_queue_gc(queue, &list, true);
|
||||
spin_unlock_bh(&queue->lock);
|
||||
bes2600_queue_post_gc(queue->stats, &list);
|
||||
}
|
||||
|
||||
int bes2600_queue_stats_init(struct bes2600_queue_stats *stats,
|
||||
size_t map_capacity,
|
||||
bes2600_queue_skb_dtor_t skb_dtor,
|
||||
struct bes2600_common *hw_priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
stats->map_capacity = map_capacity;
|
||||
stats->skb_dtor = skb_dtor;
|
||||
stats->hw_priv = hw_priv;
|
||||
spin_lock_init(&stats->lock);
|
||||
init_waitqueue_head(&stats->wait_link_id_empty);
|
||||
for (i = 0; i < CW12XX_MAX_VIFS; i++) {
|
||||
stats->link_map_cache[i] = kzalloc(map_capacity * sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!stats->link_map_cache[i]) {
|
||||
for (; i >= 0; i--)
|
||||
kfree(stats->link_map_cache[i]);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bes2600_queue_init(struct bes2600_queue *queue,
|
||||
struct bes2600_queue_stats *stats,
|
||||
u8 queue_id,
|
||||
size_t capacity,
|
||||
unsigned long ttl)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(queue, 0, sizeof(*queue));
|
||||
queue->stats = stats;
|
||||
queue->capacity = capacity;
|
||||
queue->queue_id = queue_id;
|
||||
queue->ttl = ttl;
|
||||
INIT_LIST_HEAD(&queue->queue);
|
||||
INIT_LIST_HEAD(&queue->pending);
|
||||
INIT_LIST_HEAD(&queue->free_pool);
|
||||
queue->queue_all_lock = false;
|
||||
spin_lock_init(&queue->lock);
|
||||
timer_setup(&queue->gc, bes2600_queue_gc, 0);
|
||||
queue->pool = kzalloc(sizeof(struct bes2600_queue_item) * capacity,
|
||||
GFP_KERNEL);
|
||||
if (!queue->pool)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < CW12XX_MAX_VIFS; i++) {
|
||||
queue->link_map_cache[i] =
|
||||
kzalloc(stats->map_capacity * sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!queue->link_map_cache[i]) {
|
||||
for (; i >= 0; i--)
|
||||
kfree(queue->link_map_cache[i]);
|
||||
kfree(queue->pool);
|
||||
queue->pool = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < capacity; ++i)
|
||||
list_add_tail(&queue->pool[i].head, &queue->free_pool);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO:COMBO: Flush only a particular interface specific parts */
|
||||
int bes2600_queue_clear(struct bes2600_queue *queue, int if_id)
|
||||
{
|
||||
int i, cnt, iter;
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
LIST_HEAD(gc_list);
|
||||
struct bes2600_queue_item *pending_item = NULL, *temp_pending_item = NULL;
|
||||
|
||||
cnt = 0;
|
||||
spin_lock_bh(&queue->lock);
|
||||
queue->generation++;
|
||||
queue->generation &= 0xf;
|
||||
|
||||
spin_lock(&stats->hw_priv->tx_loop.pending_record_lock);
|
||||
if (!list_empty(&queue->pending)) {
|
||||
list_for_each_entry_safe(pending_item, temp_pending_item, &queue->pending, head) {
|
||||
bes2600_queue_pending_record(&stats->hw_priv->tx_loop.pending_record_list, pending_item);
|
||||
}
|
||||
}
|
||||
spin_unlock(&stats->hw_priv->tx_loop.pending_record_lock);
|
||||
|
||||
list_splice_tail_init(&queue->queue, &queue->pending);
|
||||
while (!list_empty(&queue->pending)) {
|
||||
struct bes2600_queue_item *item = list_first_entry(
|
||||
&queue->pending, struct bes2600_queue_item, head);
|
||||
WARN_ON(!item->skb);
|
||||
if (CW12XX_ALL_IFS == if_id || item->txpriv.if_id == if_id) {
|
||||
bes2600_queue_register_post_gc(&gc_list, item);
|
||||
item->skb = NULL;
|
||||
list_move_tail(&item->head, &queue->free_pool);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
queue->num_queued -= cnt;
|
||||
queue->num_pending -= cnt;
|
||||
if (CW12XX_ALL_IFS != if_id) {
|
||||
queue->num_queued_vif[if_id] = 0;
|
||||
queue->num_pending_vif[if_id] = 0;
|
||||
} else {
|
||||
for (iter = 0; iter < CW12XX_MAX_VIFS; iter++) {
|
||||
queue->num_queued_vif[iter] = 0;
|
||||
queue->num_pending_vif[iter] = 0;
|
||||
}
|
||||
}
|
||||
spin_lock_bh(&stats->lock);
|
||||
if (CW12XX_ALL_IFS != if_id) {
|
||||
for (i = 0; i < stats->map_capacity; ++i) {
|
||||
stats->num_queued[if_id] -=
|
||||
queue->link_map_cache[if_id][i];
|
||||
stats->link_map_cache[if_id][i] -=
|
||||
queue->link_map_cache[if_id][i];
|
||||
queue->link_map_cache[if_id][i] = 0;
|
||||
}
|
||||
} else {
|
||||
for (iter = 0; iter < CW12XX_MAX_VIFS; iter++) {
|
||||
for (i = 0; i < stats->map_capacity; ++i) {
|
||||
stats->num_queued[iter] -=
|
||||
queue->link_map_cache[iter][i];
|
||||
stats->link_map_cache[iter][i] -=
|
||||
queue->link_map_cache[iter][i];
|
||||
queue->link_map_cache[iter][i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&stats->lock);
|
||||
if (unlikely(queue->overfull)) {
|
||||
queue->overfull = false;
|
||||
__bes2600_queue_unlock(queue);
|
||||
}
|
||||
spin_unlock_bh(&queue->lock);
|
||||
wake_up(&stats->wait_link_id_empty);
|
||||
bes2600_queue_post_gc(stats, &gc_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bes2600_queue_stats_deinit(struct bes2600_queue_stats *stats)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CW12XX_MAX_VIFS ; i++) {
|
||||
kfree(stats->link_map_cache[i]);
|
||||
stats->link_map_cache[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void bes2600_queue_deinit(struct bes2600_queue *queue)
|
||||
{
|
||||
int i;
|
||||
|
||||
bes2600_queue_clear(queue, CW12XX_ALL_IFS);
|
||||
del_timer_sync(&queue->gc);
|
||||
INIT_LIST_HEAD(&queue->free_pool);
|
||||
kfree(queue->pool);
|
||||
for (i = 0; i < CW12XX_MAX_VIFS; i++) {
|
||||
kfree(queue->link_map_cache[i]);
|
||||
queue->link_map_cache[i] = NULL;
|
||||
}
|
||||
queue->pool = NULL;
|
||||
queue->capacity = 0;
|
||||
}
|
||||
|
||||
size_t bes2600_queue_get_num_queued(struct bes2600_vif *priv,
|
||||
struct bes2600_queue *queue,
|
||||
u32 link_id_map)
|
||||
{
|
||||
size_t ret;
|
||||
int i, bit;
|
||||
size_t map_capacity = queue->stats->map_capacity;
|
||||
|
||||
if (!link_id_map)
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&queue->lock);
|
||||
if (likely(link_id_map == (u32) -1)) {
|
||||
ret = queue->num_queued_vif[priv->if_id] -
|
||||
queue->num_pending_vif[priv->if_id];
|
||||
} else {
|
||||
ret = 0;
|
||||
for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
|
||||
if (link_id_map & bit)
|
||||
ret +=
|
||||
queue->link_map_cache[priv->if_id][i];
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_queue_put(struct bes2600_queue *queue,
|
||||
struct sk_buff *skb,
|
||||
struct bes2600_txpriv *txpriv)
|
||||
{
|
||||
int ret = 0;
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
struct timespec64 tmval;
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
|
||||
LIST_HEAD(gc_list);
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
/* TODO:COMBO: Add interface ID info to queue item */
|
||||
|
||||
if (txpriv->link_id >= queue->stats->map_capacity)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_bh(&queue->lock);
|
||||
if (!WARN_ON(list_empty(&queue->free_pool))) {
|
||||
struct bes2600_queue_item *item = list_first_entry(
|
||||
&queue->free_pool, struct bes2600_queue_item, head);
|
||||
BUG_ON(item->skb);
|
||||
|
||||
list_move_tail(&item->head, &queue->queue);
|
||||
item->skb = skb;
|
||||
item->txpriv = *txpriv;
|
||||
item->generation = 0;
|
||||
item->packetID = bes2600_queue_make_packet_id(
|
||||
queue->generation, queue->queue_id,
|
||||
item->generation, item - queue->pool,
|
||||
txpriv->if_id, txpriv->raw_link_id);
|
||||
item->queue_timestamp = jiffies;
|
||||
#ifdef BES2600_HOST_TIMESTAMP_DEBUG
|
||||
if (skb_tailroom(skb) >= 4) {
|
||||
u32 *extra_data;
|
||||
extra_data = (u32 *)skb_tail_pointer(skb);
|
||||
*extra_data = (u32)jiffies_to_msecs(item->queue_timestamp);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
ktime_get_real_ts64(&tmval);
|
||||
item->qdelay_timestamp = tmval.tv_nsec / 1000;
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
|
||||
++queue->num_queued;
|
||||
++queue->num_queued_vif[txpriv->if_id];
|
||||
++queue->link_map_cache[txpriv->if_id][txpriv->link_id];
|
||||
|
||||
spin_lock_bh(&stats->lock);
|
||||
++stats->num_queued[txpriv->if_id];
|
||||
++stats->link_map_cache[txpriv->if_id][txpriv->link_id];
|
||||
spin_unlock_bh(&stats->lock);
|
||||
|
||||
/*
|
||||
* TX may happen in parallel sometimes.
|
||||
* Leave extra queue slots so we don't overflow.
|
||||
* An extra slot is set aside to lock the mac80211 send skb to the driver.
|
||||
*/
|
||||
if (queue->overfull == false &&
|
||||
queue->num_queued >=
|
||||
((stats->hw_priv->vif0_throttle +
|
||||
stats->hw_priv->vif1_throttle + 2)
|
||||
- (num_present_cpus() - 1))) {
|
||||
queue->overfull = true;
|
||||
__bes2600_queue_lock(queue);
|
||||
mod_timer(&queue->gc, jiffies);
|
||||
}
|
||||
} else {
|
||||
bes_err("queue->free_pool is empty, queue->num_queued_vif[%d]: %ld\n",
|
||||
txpriv->if_id, queue->num_queued_vif[txpriv->if_id]);
|
||||
ret = -ENOENT;
|
||||
}
|
||||
#if 0
|
||||
bes_devel("queue_put queue %d, %d, %d\n",
|
||||
queue->num_queued,
|
||||
queue->link_map_cache[txpriv->if_id][txpriv->link_id],
|
||||
queue->num_pending);
|
||||
#endif
|
||||
spin_unlock_bh(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_queue_get(struct bes2600_queue *queue,
|
||||
int if_id,
|
||||
u32 link_id_map,
|
||||
struct wsm_tx **tx,
|
||||
struct ieee80211_tx_info **tx_info,
|
||||
struct bes2600_txpriv **txpriv)
|
||||
{
|
||||
int ret = -ENOENT;
|
||||
struct bes2600_queue_item *item = NULL;
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
bool wakeup_stats = false;
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
struct timespec64 tmval;
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
|
||||
spin_lock_bh(&queue->lock);
|
||||
list_for_each_entry(item, &queue->queue, head) {
|
||||
if ((item->txpriv.if_id == if_id) &&
|
||||
(link_id_map & BIT(item->txpriv.link_id))) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
*tx = (struct wsm_tx *)item->skb->data;
|
||||
*tx_info = IEEE80211_SKB_CB(item->skb);
|
||||
*txpriv = &item->txpriv;
|
||||
(*tx)->packetID = __cpu_to_le32(item->packetID);
|
||||
list_move_tail(&item->head, &queue->pending);
|
||||
++queue->num_pending;
|
||||
++queue->num_pending_vif[item->txpriv.if_id];
|
||||
--queue->link_map_cache[item->txpriv.if_id]
|
||||
[item->txpriv.link_id];
|
||||
item->xmit_timestamp = jiffies;
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
ktime_get_real_ts64(&tmval);
|
||||
item->mdelay_timestamp = tmval.tv_nsec / 1000;
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
|
||||
spin_lock_bh(&stats->lock);
|
||||
--stats->num_queued[item->txpriv.if_id];
|
||||
if (!--stats->link_map_cache[item->txpriv.if_id]
|
||||
[item->txpriv.link_id])
|
||||
wakeup_stats = true;
|
||||
|
||||
spin_unlock_bh(&stats->lock);
|
||||
#if 0
|
||||
bes_devel("queue_get queue %d, %d, %d\n",
|
||||
queue->num_queued,
|
||||
queue->link_map_cache[item->txpriv.if_id][item->txpriv.link_id],
|
||||
queue->num_pending);
|
||||
#endif
|
||||
} else {
|
||||
bes_warn("%s: queue get failed, if_id = %d, link_id_map = %u\n", __func__, if_id, link_id_map);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&queue->lock);
|
||||
if (wakeup_stats)
|
||||
wake_up(&stats->wait_link_id_empty);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
int bes2600_queue_requeue(struct bes2600_common *hw_priv,
|
||||
struct bes2600_queue *queue, u32 packetID, bool check)
|
||||
#else
|
||||
int bes2600_queue_requeue(struct bes2600_queue *queue, u32 packetID, bool check)
|
||||
#endif
|
||||
{
|
||||
int ret = 0;
|
||||
u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
|
||||
struct bes2600_queue_item *item;
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
|
||||
bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
|
||||
&item_generation, &item_id, &if_id, &link_id);
|
||||
|
||||
item = &queue->pool[item_id];
|
||||
#ifdef P2P_MULTIVIF
|
||||
if (check && item->txpriv.if_id == CW12XX_GENERIC_IF_ID) {
|
||||
#else
|
||||
if (check && item->txpriv.offchannel_if_id == CW12XX_GENERIC_IF_ID) {
|
||||
#endif
|
||||
bes_devel("Requeued frame dropped for generic interface id.\n");
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
bes2600_queue_remove(hw_priv, queue, packetID);
|
||||
#else
|
||||
bes2600_queue_remove(queue, packetID);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef P2P_MULTIVIF
|
||||
if (!check)
|
||||
item->txpriv.offchannel_if_id = CW12XX_GENERIC_IF_ID;
|
||||
#endif
|
||||
|
||||
/*if_id = item->txpriv.if_id;*/
|
||||
|
||||
spin_lock_bh(&queue->lock);
|
||||
BUG_ON(queue_id != queue->queue_id);
|
||||
if (unlikely(queue_generation != queue->generation)) {
|
||||
bes_info("%s, Queue Generation is not equal\n", __func__);
|
||||
ret = 0;
|
||||
} else if (unlikely(item_id >= (unsigned) queue->capacity)) {
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
} else if (unlikely(item->generation != item_generation)) {
|
||||
WARN_ON(1);
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
--queue->num_pending;
|
||||
--queue->num_pending_vif[if_id];
|
||||
++queue->link_map_cache[if_id][item->txpriv.link_id];
|
||||
|
||||
spin_lock_bh(&stats->lock);
|
||||
++stats->num_queued[item->txpriv.if_id];
|
||||
++stats->link_map_cache[if_id][item->txpriv.link_id];
|
||||
spin_unlock_bh(&stats->lock);
|
||||
|
||||
item->generation = ++item_generation;
|
||||
item->packetID = bes2600_queue_make_packet_id(
|
||||
queue_generation, queue_id, item_generation, item_id,
|
||||
if_id, link_id);
|
||||
list_move(&item->head, &queue->queue);
|
||||
#if 0
|
||||
bes_devel("queue_requeue queue %d, %d, %d\n",
|
||||
queue->num_queued,
|
||||
queue->link_map_cache[if_id][item->txpriv.link_id],
|
||||
queue->num_pending);
|
||||
bes_devel("queue_requeue stats %d, %d\n",
|
||||
stats->num_queued,
|
||||
stats->link_map_cache[if_id][item->txpriv.link_id]);
|
||||
#endif
|
||||
}
|
||||
spin_unlock_bh(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_sw_retry_requeue(struct bes2600_common *hw_priv,
|
||||
struct bes2600_queue *queue, u32 packetID, bool check)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
|
||||
struct bes2600_queue_item *item;
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
|
||||
&item_generation, &item_id, &if_id, &link_id);
|
||||
item = &queue->pool[item_id];
|
||||
#ifdef P2P_MULTIVIF
|
||||
if (check && item->txpriv.if_id == CW12XX_GENERIC_IF_ID) {
|
||||
#else
|
||||
if (check && item->txpriv.offchannel_if_id == CW12XX_GENERIC_IF_ID) {
|
||||
#endif
|
||||
bes_devel("Requeued frame dropped for generic interface id.\n");
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
bes2600_queue_remove(hw_priv, queue, packetID);
|
||||
#else
|
||||
bes2600_queue_remove(queue, packetID);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef P2P_MULTIVIF
|
||||
if (!check)
|
||||
item->txpriv.offchannel_if_id = CW12XX_GENERIC_IF_ID;
|
||||
#endif
|
||||
/*if_id = item->txpriv.if_id;*/
|
||||
spin_lock_bh(&queue->lock);
|
||||
BUG_ON(queue_id != queue->queue_id);
|
||||
if (unlikely(queue_generation != queue->generation)) {
|
||||
bes_info("%s, Queue Generation is not equal\n", __func__);
|
||||
ret = 0;
|
||||
} else if (unlikely(item_id >= (unsigned) queue->capacity)) {
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
} else if (unlikely(item->generation != item_generation)) {
|
||||
WARN_ON(1);
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
--queue->num_pending;
|
||||
--queue->num_pending_vif[if_id];
|
||||
++queue->link_map_cache[if_id][item->txpriv.link_id];
|
||||
spin_lock_bh(&stats->lock);
|
||||
++stats->num_queued[item->txpriv.if_id];
|
||||
++stats->link_map_cache[if_id][item->txpriv.link_id];
|
||||
spin_unlock_bh(&stats->lock);
|
||||
item->packetID = bes2600_queue_make_packet_id(
|
||||
queue_generation, queue_id, item_generation, item_id,
|
||||
if_id, link_id);
|
||||
|
||||
list_move(&item->head, &queue->queue);
|
||||
|
||||
}
|
||||
|
||||
spin_unlock_bh(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int bes2600_queue_requeue_all(struct bes2600_queue *queue)
|
||||
{
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
spin_lock_bh(&queue->lock);
|
||||
while (!list_empty(&queue->pending)) {
|
||||
struct bes2600_queue_item *item = list_entry(
|
||||
queue->pending.prev, struct bes2600_queue_item, head);
|
||||
|
||||
--queue->num_pending;
|
||||
--queue->num_pending_vif[item->txpriv.if_id];
|
||||
++queue->link_map_cache[item->txpriv.if_id]
|
||||
[item->txpriv.link_id];
|
||||
|
||||
spin_lock_bh(&stats->lock);
|
||||
++stats->num_queued[item->txpriv.if_id];
|
||||
++stats->link_map_cache[item->txpriv.if_id]
|
||||
[item->txpriv.link_id];
|
||||
spin_unlock_bh(&stats->lock);
|
||||
|
||||
++item->generation;
|
||||
item->packetID = bes2600_queue_make_packet_id(
|
||||
queue->generation, queue->queue_id,
|
||||
item->generation, item - queue->pool,
|
||||
item->txpriv.if_id, item->txpriv.raw_link_id);
|
||||
list_move(&item->head, &queue->queue);
|
||||
}
|
||||
spin_unlock_bh(&queue->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
int bes2600_queue_remove(struct bes2600_common *hw_priv,
|
||||
struct bes2600_queue *queue, u32 packetID)
|
||||
#else
|
||||
int bes2600_queue_remove(struct bes2600_queue *queue, u32 packetID)
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
{
|
||||
int ret = 0;
|
||||
u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
|
||||
struct bes2600_queue_item *item;
|
||||
struct bes2600_queue_stats *stats = queue->stats;
|
||||
struct sk_buff *gc_skb = NULL;
|
||||
struct bes2600_txpriv gc_txpriv;
|
||||
|
||||
bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
|
||||
&item_generation, &item_id, &if_id, &link_id);
|
||||
|
||||
item = &queue->pool[item_id];
|
||||
|
||||
spin_lock_bh(&queue->lock);
|
||||
BUG_ON(queue_id != queue->queue_id);
|
||||
/*TODO:COMBO:Add check for interface ID also */
|
||||
if (unlikely(queue_generation != queue->generation)) {
|
||||
bes_info("%s, Queue Generation is not equal\n", __func__);
|
||||
ret = 0;
|
||||
} else if (unlikely(item_id >= (unsigned) queue->capacity)) {
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
} else if (unlikely(item->generation != item_generation)) {
|
||||
WARN_ON(1);
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
gc_txpriv = item->txpriv;
|
||||
gc_skb = item->skb;
|
||||
item->skb = NULL;
|
||||
--queue->num_pending;
|
||||
--queue->num_pending_vif[if_id];
|
||||
--queue->num_queued;
|
||||
--queue->num_queued_vif[if_id];
|
||||
++queue->num_sent;
|
||||
++item->generation;
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
spin_lock_bh(&hw_priv->tsm_lock);
|
||||
if (hw_priv->start_stop_tsm.start) {
|
||||
if (queue_id == hw_priv->tsm_info.ac) {
|
||||
struct timespec64 tmval;
|
||||
unsigned long queue_delay;
|
||||
unsigned long media_delay;
|
||||
ktime_get_real_ts64(&tmval);
|
||||
|
||||
if (tmval.tv_nsec / 1000 > item->qdelay_timestamp)
|
||||
queue_delay = tmval.tv_nsec / 1000 -
|
||||
item->qdelay_timestamp;
|
||||
else
|
||||
queue_delay = tmval.tv_nsec / 1000 +
|
||||
1000000 - item->qdelay_timestamp;
|
||||
|
||||
if (tmval.tv_nsec / 1000 > item->mdelay_timestamp)
|
||||
media_delay = tmval.tv_nsec / 1000 -
|
||||
item->mdelay_timestamp;
|
||||
else
|
||||
media_delay = tmval.tv_nsec / 1000 +
|
||||
1000000 - item->mdelay_timestamp;
|
||||
hw_priv->tsm_info.sum_media_delay +=
|
||||
media_delay;
|
||||
hw_priv->tsm_info.sum_pkt_q_delay += queue_delay;
|
||||
if (queue_delay <= 10000)
|
||||
hw_priv->tsm_stats.bin0++;
|
||||
else if (queue_delay <= 20000)
|
||||
hw_priv->tsm_stats.bin1++;
|
||||
else if (queue_delay <= 40000)
|
||||
hw_priv->tsm_stats.bin2++;
|
||||
else
|
||||
hw_priv->tsm_stats.bin3++;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&hw_priv->tsm_lock);
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
/* Do not use list_move_tail here, but list_move:
|
||||
* try to utilize cache row.
|
||||
*/
|
||||
list_move(&item->head, &queue->free_pool);
|
||||
|
||||
if (unlikely(queue->overfull) &&
|
||||
(queue->num_queued <= ((stats->hw_priv->vif0_throttle + stats->hw_priv->vif1_throttle + 2) / 2))) {
|
||||
queue->overfull = false;
|
||||
__bes2600_queue_unlock(queue);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&queue->lock);
|
||||
|
||||
#if 0
|
||||
bes_devel("queue_drop queue %d, %d, %d\n",
|
||||
queue->num_queued, queue->link_map_cache[if_id][0],
|
||||
queue->num_pending);
|
||||
bes_devel("queue_drop stats %d, %d\n", stats->num_queued,
|
||||
stats->link_map_cache[if_id][0]);
|
||||
#endif
|
||||
if (gc_skb)
|
||||
stats->skb_dtor(stats->hw_priv, gc_skb, &gc_txpriv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID,
|
||||
struct sk_buff **skb,
|
||||
const struct bes2600_txpriv **txpriv)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
|
||||
struct bes2600_queue_item *item;
|
||||
struct bes2600_queue_item *record_item = NULL, *temp_record_item = NULL;
|
||||
|
||||
bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
|
||||
&item_generation, &item_id, &if_id, &link_id);
|
||||
|
||||
spin_lock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
if (!list_empty(&queue->stats->hw_priv->tx_loop.pending_record_list)) {
|
||||
list_for_each_entry_safe(record_item, temp_record_item, &queue->stats->hw_priv->tx_loop.pending_record_list, head) {
|
||||
if (record_item->packetID == packetID) {
|
||||
list_del(&record_item->head);
|
||||
dev_kfree_skb(record_item->skb);
|
||||
kfree(record_item);
|
||||
spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
|
||||
|
||||
item = &queue->pool[item_id];
|
||||
|
||||
spin_lock_bh(&queue->lock);
|
||||
BUG_ON(queue_id != queue->queue_id);
|
||||
/* TODO:COMBO: Add check for interface ID here */
|
||||
if (unlikely(queue_generation != queue->generation)) {
|
||||
bes_info("%s, Queue Generation is not equal\n", __func__);
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
} else if (unlikely(item_id >= (unsigned) queue->capacity)) {
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
} else if (unlikely(item->generation != item_generation)) {
|
||||
bes_info("%s, item_generation =%u, item_id =%u link_id=%u queue_generation =%u packetID =%u\n",
|
||||
__func__, item_generation, item_id, link_id, queue_generation, packetID);
|
||||
WARN_ON(1);
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
*skb = item->skb;
|
||||
*txpriv = &item->txpriv;
|
||||
}
|
||||
spin_unlock_bh(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bes2600_queue_lock(struct bes2600_queue *queue)
|
||||
{
|
||||
spin_lock_bh(&queue->lock);
|
||||
__bes2600_queue_lock(queue);
|
||||
spin_unlock_bh(&queue->lock);
|
||||
}
|
||||
|
||||
void bes2600_queue_unlock(struct bes2600_queue *queue)
|
||||
{
|
||||
spin_lock_bh(&queue->lock);
|
||||
__bes2600_queue_unlock(queue);
|
||||
spin_unlock_bh(&queue->lock);
|
||||
}
|
||||
|
||||
bool bes2600_queue_get_xmit_timestamp(struct bes2600_queue *queue,
|
||||
unsigned long *timestamp, int if_id,
|
||||
u32 pending_frameID)
|
||||
{
|
||||
struct bes2600_queue_item *item;
|
||||
bool ret;
|
||||
|
||||
spin_lock_bh(&queue->lock);
|
||||
ret = !list_empty(&queue->pending);
|
||||
if (ret) {
|
||||
list_for_each_entry(item, &queue->pending, head) {
|
||||
if (((if_id == CW12XX_GENERIC_IF_ID) ||
|
||||
(if_id == CW12XX_ALL_IFS) ||
|
||||
(item->txpriv.if_id == if_id)) &&
|
||||
(item->packetID != pending_frameID)) {
|
||||
if (time_before(item->xmit_timestamp,
|
||||
*timestamp))
|
||||
*timestamp = item->xmit_timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool bes2600_queue_stats_is_empty(struct bes2600_queue_stats *stats,
|
||||
u32 link_id_map, int if_id)
|
||||
{
|
||||
bool empty = true;
|
||||
|
||||
spin_lock_bh(&stats->lock);
|
||||
if (link_id_map == (u32)-1)
|
||||
empty = stats->num_queued[if_id] == 0;
|
||||
else {
|
||||
int i, if_id;
|
||||
for (if_id = 0; if_id < CW12XX_MAX_VIFS; if_id++) {
|
||||
for (i = 0; i < stats->map_capacity; ++i) {
|
||||
if (link_id_map & BIT(i)) {
|
||||
if (stats->link_map_cache[if_id][i]) {
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&stats->lock);
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
void bes2600_queue_iterate_pending_packet(struct bes2600_queue *queue,
|
||||
void (*iterate_cb)(struct bes2600_common *hw_priv, struct sk_buff *skb))
|
||||
{
|
||||
struct bes2600_queue_item *item = NULL;
|
||||
|
||||
if (list_empty(&queue->pending))
|
||||
return;
|
||||
|
||||
list_for_each_entry(item, &queue->pending, head) {
|
||||
iterate_cb(queue->stats->hw_priv, item->skb);
|
||||
}
|
||||
}
|
||||
|
||||
void bes2600_queue_iterate_record_pending_packet(struct bes2600_common *hw_priv,
|
||||
void (*iterate_cb)(struct bes2600_common *hw_priv, struct sk_buff *skb))
|
||||
{
|
||||
struct bes2600_queue_item *item = NULL;
|
||||
if (list_empty(&hw_priv->tx_loop.pending_record_list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry(item, &hw_priv->tx_loop.pending_record_list, head) {
|
||||
iterate_cb(hw_priv, item->skb);
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* O(1) TX queue with built-in allocator for BES2600 drivers
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_QUEUE_H_INCLUDED
|
||||
#define BES2600_QUEUE_H_INCLUDED
|
||||
|
||||
/* private */ struct bes2600_queue_item;
|
||||
|
||||
/* extern */ struct sk_buff;
|
||||
/* extern */ struct wsm_tx;
|
||||
/* extern */ struct bes2600_common;
|
||||
/* extern */ struct bes2600_vif;
|
||||
/* extern */ struct ieee80211_tx_queue_stats;
|
||||
/* extern */ struct bes2600_txpriv;
|
||||
|
||||
/* forward */ struct bes2600_queue_stats;
|
||||
|
||||
typedef void (*bes2600_queue_skb_dtor_t)(struct bes2600_common *priv,
|
||||
struct sk_buff *skb,
|
||||
const struct bes2600_txpriv *txpriv);
|
||||
|
||||
struct bes2600_queue {
|
||||
struct bes2600_queue_stats *stats;
|
||||
size_t capacity;
|
||||
size_t num_queued;
|
||||
size_t num_queued_vif[CW12XX_MAX_VIFS];
|
||||
size_t num_pending;
|
||||
size_t num_pending_vif[CW12XX_MAX_VIFS];
|
||||
size_t num_sent;
|
||||
struct bes2600_queue_item *pool;
|
||||
struct list_head queue;
|
||||
struct list_head free_pool;
|
||||
struct list_head pending;
|
||||
int tx_locked_cnt;
|
||||
bool queue_all_lock;
|
||||
int *link_map_cache[CW12XX_MAX_VIFS];
|
||||
bool overfull;
|
||||
spinlock_t lock;
|
||||
u8 queue_id;
|
||||
u8 generation;
|
||||
struct timer_list gc;
|
||||
unsigned long ttl;
|
||||
};
|
||||
|
||||
struct bes2600_queue_stats {
|
||||
spinlock_t lock;
|
||||
int *link_map_cache[CW12XX_MAX_VIFS];
|
||||
int num_queued[CW12XX_MAX_VIFS];
|
||||
size_t map_capacity;
|
||||
wait_queue_head_t wait_link_id_empty;
|
||||
bes2600_queue_skb_dtor_t skb_dtor;
|
||||
struct bes2600_common *hw_priv;
|
||||
};
|
||||
|
||||
struct bes2600_txpriv {
|
||||
u8 link_id;
|
||||
u8 raw_link_id;
|
||||
u8 tid;
|
||||
u8 rate_id;
|
||||
u8 offset;
|
||||
u8 if_id;
|
||||
#ifndef P2P_MULTIVIF
|
||||
u8 offchannel_if_id;
|
||||
#else
|
||||
u8 raw_if_id;
|
||||
#endif
|
||||
u8 retry_count;
|
||||
};
|
||||
|
||||
int bes2600_queue_stats_init(struct bes2600_queue_stats *stats,
|
||||
size_t map_capacity,
|
||||
bes2600_queue_skb_dtor_t skb_dtor,
|
||||
struct bes2600_common *priv);
|
||||
int bes2600_queue_init(struct bes2600_queue *queue,
|
||||
struct bes2600_queue_stats *stats,
|
||||
u8 queue_id,
|
||||
size_t capacity,
|
||||
unsigned long ttl);
|
||||
int bes2600_queue_clear(struct bes2600_queue *queue, int if_id);
|
||||
void bes2600_queue_stats_deinit(struct bes2600_queue_stats *stats);
|
||||
void bes2600_queue_deinit(struct bes2600_queue *queue);
|
||||
|
||||
size_t bes2600_queue_get_num_queued(struct bes2600_vif *priv,
|
||||
struct bes2600_queue *queue,
|
||||
u32 link_id_map);
|
||||
int bes2600_queue_put(struct bes2600_queue *queue,
|
||||
struct sk_buff *skb,
|
||||
struct bes2600_txpriv *txpriv);
|
||||
int bes2600_queue_get(struct bes2600_queue *queue,
|
||||
int if_id,
|
||||
u32 link_id_map,
|
||||
struct wsm_tx **tx,
|
||||
struct ieee80211_tx_info **tx_info,
|
||||
struct bes2600_txpriv **txpriv);
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
int bes2600_queue_requeue(struct bes2600_common *hw_priv,
|
||||
struct bes2600_queue *queue,
|
||||
u32 packetID, bool check);
|
||||
#else
|
||||
int bes2600_queue_requeue(struct bes2600_queue *queue, u32 packetID, bool check);
|
||||
#endif
|
||||
int bes2600_queue_requeue_all(struct bes2600_queue *queue);
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
int bes2600_queue_remove(struct bes2600_common *hw_priv,
|
||||
struct bes2600_queue *queue,
|
||||
u32 packetID);
|
||||
#else
|
||||
int bes2600_queue_remove(struct bes2600_queue *queue,
|
||||
u32 packetID);
|
||||
#endif /*CONFIG_BES2600_TESTMODE*/
|
||||
int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID,
|
||||
struct sk_buff **skb,
|
||||
const struct bes2600_txpriv **txpriv);
|
||||
void bes2600_queue_lock(struct bes2600_queue *queue);
|
||||
void bes2600_queue_unlock(struct bes2600_queue *queue);
|
||||
bool bes2600_queue_get_xmit_timestamp(struct bes2600_queue *queue,
|
||||
unsigned long *timestamp, int if_id,
|
||||
u32 pending_frameID);
|
||||
|
||||
|
||||
bool bes2600_queue_stats_is_empty(struct bes2600_queue_stats *stats,
|
||||
u32 link_id_map, int if_id);
|
||||
|
||||
static inline void bes2600_queue_parse_id(u32 packetID, u8 *queue_generation,
|
||||
u8 *queue_id,
|
||||
u8 *item_generation,
|
||||
u8 *item_id,
|
||||
u8 *if_id,
|
||||
u8 *link_id)
|
||||
{
|
||||
*item_id = (packetID >> 0) & 0xFF;
|
||||
*item_generation = (packetID >> 8) & 0xFF;
|
||||
*queue_id = (packetID >> 16) & 0xF;
|
||||
*if_id = (packetID >> 20) & 0xF;
|
||||
*link_id = (packetID >> 24) & 0xF;
|
||||
*queue_generation = (packetID >> 28) & 0xF;
|
||||
}
|
||||
static inline u8 bes2600_queue_get_queue_id(u32 packetID)
|
||||
{
|
||||
return (packetID >> 16) & 0xF;
|
||||
}
|
||||
|
||||
static inline u8 bes2600_queue_get_if_id(u32 packetID)
|
||||
{
|
||||
return (packetID >> 20) & 0xF;
|
||||
}
|
||||
|
||||
static inline u8 bes2600_queue_get_link_id(u32 packetID)
|
||||
{
|
||||
return (packetID >> 24) & 0xF;
|
||||
}
|
||||
|
||||
static inline u8 bes2600_queue_get_generation(u32 packetID)
|
||||
{
|
||||
return (packetID >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
int bes2600_queue_get_skb_and_timestamp(struct bes2600_queue *queue, u32 packetID,
|
||||
struct sk_buff **skb, struct bes2600_txpriv **txpriv,
|
||||
unsigned long *timestamp);
|
||||
int bes2600_sw_retry_requeue(struct bes2600_common *hw_priv,
|
||||
struct bes2600_queue *queue, u32 packetID, bool check);
|
||||
void bes2600_queue_iterate_pending_packet(struct bes2600_queue *queue,
|
||||
void (*iterate_cb)(struct bes2600_common *hw_priv, struct sk_buff *skb));
|
||||
void bes2600_queue_iterate_record_pending_packet(struct bes2600_common *hw_priv,
|
||||
void (*iterate_cb)(struct bes2600_common *hw_priv, struct sk_buff *skb));
|
||||
#endif /* BES2600_QUEUE_H_INCLUDED */
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Common sbus abstraction layer interface for bes2600 wireless driver
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_SBUS_H
|
||||
#define BES2600_SBUS_H
|
||||
|
||||
/*
|
||||
* sbus priv forward definition.
|
||||
* Implemented and instantiated in particular modules.
|
||||
*/
|
||||
struct sbus_priv;
|
||||
struct bes2600_common;
|
||||
|
||||
typedef void (*sbus_irq_handler)(void *priv);
|
||||
|
||||
enum SUBSYSTEM {
|
||||
SUBSYSTEM_MCU = 0,
|
||||
SUBSYSTEM_WIFI,
|
||||
SUBSYSTEM_BT,
|
||||
SUBSYSTEM_BT_LP,
|
||||
};
|
||||
|
||||
enum GPIO_WAKE_FLAG
|
||||
{
|
||||
GPIO_WAKE_FLAG_MCU = 0,
|
||||
GPIO_WAKE_FLAG_WIFI_ON,
|
||||
GPIO_WAKE_FLAG_WIFI_OFF,
|
||||
GPIO_WAKE_FLAG_BT_ON,
|
||||
GPIO_WAKE_FLAG_BT_OFF,
|
||||
GPIO_WAKE_FLAG_BT_LP_ON,
|
||||
GPIO_WAKE_FLAG_BT_LP_OFF,
|
||||
GPIO_WAKE_FLAG_HOST_SUSPEND,
|
||||
GPIO_WAKE_FLAG_HOST_RESUME,
|
||||
GPIO_WAKE_FLAG_SDIO_RX,
|
||||
GPIO_WAKE_FLAG_SDIO_PROBE,
|
||||
};
|
||||
|
||||
struct sbus_ops {
|
||||
int (*init)(struct sbus_priv *self, struct bes2600_common *core);
|
||||
int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr,
|
||||
void *dst, int count);
|
||||
int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr,
|
||||
const void *src, int count);
|
||||
void (*lock)(struct sbus_priv *self);
|
||||
void (*unlock)(struct sbus_priv *self);
|
||||
int (*irq_subscribe)(struct sbus_priv *self, sbus_irq_handler handler,
|
||||
void *priv);
|
||||
int (*irq_unsubscribe)(struct sbus_priv *self);
|
||||
int (*reset)(struct sbus_priv *self);
|
||||
size_t (*align_size)(struct sbus_priv *self, size_t size);
|
||||
int (*set_block_size)(struct sbus_priv *self, size_t size);
|
||||
int (*pipe_send)(struct sbus_priv *self, u8 pipe, u32 len, u8 *buf);
|
||||
void * (*pipe_read)(struct sbus_priv *self);
|
||||
int (*sbus_reg_read)(struct sbus_priv *self, u32 reg,
|
||||
void *buf, int count);
|
||||
int (*sbus_reg_write)(struct sbus_priv *self, u32 reg,
|
||||
const void *buf, int count);
|
||||
|
||||
/* sub_system: 0 for mcu, 1 for wifi, 2 for bt, ... */
|
||||
int (*sbus_active)(struct sbus_priv *self, int sub_system);
|
||||
int (*sbus_deactive)(struct sbus_priv *self, int sub_system);
|
||||
int (*power_switch)(struct sbus_priv *self, int on);
|
||||
/* gpio wake, beacuse bes2600 sdio can't wakeup mcu, so add the two of interfaces */
|
||||
void (*gpio_wake)(struct sbus_priv *self, int falg);
|
||||
void (*gpio_sleep)(struct sbus_priv *self, int falg);
|
||||
/* halt device to get debug information */
|
||||
void (*halt_device)(struct sbus_priv *self);
|
||||
bool (*wakeup_source)(struct sbus_priv *self);
|
||||
int (*reboot)(struct sbus_priv *self);
|
||||
};
|
||||
|
||||
void bes2600_irq_handler(struct bes2600_common *priv);
|
||||
|
||||
/* This MUST be wrapped with hwbus_ops->lock/unlock! */
|
||||
int __bes2600_irq_enable(int enable);
|
||||
|
||||
#endif /* BES2600_SBUS_H */
|
||||
+1152
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Scan interface for BES2600 mac80211 drivers
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef SCAN_H_INCLUDED
|
||||
#define SCAN_H_INCLUDED
|
||||
|
||||
#include <linux/semaphore.h>
|
||||
#include "wsm.h"
|
||||
|
||||
/* external */ struct sk_buff;
|
||||
/* external */ struct cfg80211_scan_request;
|
||||
/* external */ struct ieee80211_channel;
|
||||
/* external */ struct ieee80211_hw;
|
||||
/* external */ struct work_struct;
|
||||
|
||||
struct bes2600_scan {
|
||||
struct semaphore lock;
|
||||
struct work_struct work;
|
||||
#ifdef ROAM_OFFLOAD
|
||||
struct work_struct swork; /* scheduled scan work */
|
||||
struct cfg80211_sched_scan_request *sched_req;
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
struct delayed_work timeout;
|
||||
struct cfg80211_scan_request *req;
|
||||
struct ieee80211_channel **begin;
|
||||
struct ieee80211_channel **curr;
|
||||
struct ieee80211_channel **end;
|
||||
struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
|
||||
int output_power;
|
||||
int n_ssids;
|
||||
int status;
|
||||
atomic_t in_progress;
|
||||
/* Direct probe requests workaround */
|
||||
struct delayed_work probe_work;
|
||||
int direct_probe;
|
||||
u8 if_id;
|
||||
};
|
||||
|
||||
int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_scan_request *hw_req);
|
||||
#ifdef ROAM_OFFLOAD
|
||||
int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies);
|
||||
void bes2600_hw_sched_scan_stop(struct bes2600_common *priv);
|
||||
void bes2600_sched_scan_work(struct work_struct *work);
|
||||
#endif /*ROAM_OFFLOAD*/
|
||||
void bes2600_scan_work(struct work_struct *work);
|
||||
void bes2600_scan_timeout(struct work_struct *work);
|
||||
void bes2600_scan_complete_cb(struct bes2600_common *priv,
|
||||
struct wsm_scan_complete *arg);
|
||||
void bes2600_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* Raw probe requests TX workaround */
|
||||
void bes2600_probe_work(struct work_struct *work);
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
/* Advance Scan Timer */
|
||||
void bes2600_advance_scan_timeout(struct work_struct *work);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+4481
File diff suppressed because it is too large
Load Diff
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Mac80211 STA interface for BES2600 mac80211 drivers
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/version.h>
|
||||
#ifndef STA_H_INCLUDED
|
||||
#define STA_H_INCLUDED
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* mac80211 API */
|
||||
|
||||
int bes2600_start(struct ieee80211_hw *dev);
|
||||
void bes2600_stop(struct ieee80211_hw *dev, bool suspend);
|
||||
int bes2600_add_interface(struct ieee80211_hw *dev,
|
||||
struct ieee80211_vif *vif);
|
||||
void bes2600_remove_interface(struct ieee80211_hw *dev,
|
||||
struct ieee80211_vif *vif);
|
||||
int bes2600_change_interface(struct ieee80211_hw *dev,
|
||||
struct ieee80211_vif *vif,
|
||||
enum nl80211_iftype new_type,
|
||||
bool p2p);
|
||||
|
||||
int bes2600_config(struct ieee80211_hw *dev, u32 changed);
|
||||
int bes2600_change_interface(struct ieee80211_hw *dev,
|
||||
struct ieee80211_vif *vif,
|
||||
enum nl80211_iftype new_type,
|
||||
bool p2p);
|
||||
void bes2600_configure_filter(struct ieee80211_hw *dev,
|
||||
unsigned int changed_flags,
|
||||
unsigned int *total_flags,
|
||||
u64 multicast);
|
||||
int bes2600_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
|
||||
unsigned int link_id,
|
||||
u16 queue, const struct ieee80211_tx_queue_params *params);
|
||||
int bes2600_get_stats(struct ieee80211_hw *dev,
|
||||
struct ieee80211_low_level_stats *stats);
|
||||
/* Not more a part of interface?
|
||||
int bes2600_get_tx_stats(struct ieee80211_hw *dev,
|
||||
struct ieee80211_tx_queue_stats *stats);
|
||||
*/
|
||||
int bes2600_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
|
||||
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key);
|
||||
|
||||
int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
|
||||
|
||||
void bes2600_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
u32 queues, bool drop);
|
||||
|
||||
int bes2600_remain_on_channel(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel *chan,
|
||||
int duration,
|
||||
enum ieee80211_roc_type type);
|
||||
|
||||
int bes2600_cancel_remain_on_channel(struct ieee80211_hw *hw
|
||||
, struct ieee80211_vif *vif
|
||||
);
|
||||
|
||||
int bes2600_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
|
||||
u64 bes2600_prepare_multicast(struct ieee80211_hw *hw,
|
||||
struct netdev_hw_addr_list *mc_list);
|
||||
|
||||
int bes2600_set_pm(struct bes2600_vif *priv, const struct wsm_set_pm *arg);
|
||||
|
||||
void bes2600_set_data_filter(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
void *data,
|
||||
int len);
|
||||
|
||||
u32 bes2600_bh_get_encry_hdr_len(u32 cipherType);
|
||||
/* ******************************************************************** */
|
||||
/* WSM callbacks */
|
||||
|
||||
/* void bes2600_set_pm_complete_cb(struct bes2600_common *hw_priv,
|
||||
struct wsm_set_pm_complete *arg); */
|
||||
void bes2600_channel_switch_cb(struct bes2600_common *hw_priv);
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* WSM events */
|
||||
|
||||
void bes2600_free_event_queue(struct bes2600_common *hw_priv);
|
||||
void bes2600_event_handler(struct work_struct *work);
|
||||
void bes2600_bss_loss_work(struct work_struct *work);
|
||||
void bes2600_connection_loss_work(struct work_struct *work);
|
||||
void bes2600_keep_alive_work(struct work_struct *work);
|
||||
void bes2600_tx_failure_work(struct work_struct *work);
|
||||
void bes2600_dynamic_opt_txrx_work(struct work_struct *work);
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* Internal API */
|
||||
|
||||
int bes2600_setup_mac(struct bes2600_common *hw_priv);
|
||||
void bes2600_join_work(struct work_struct *work);
|
||||
void bes2600_join_timeout(struct work_struct *work);
|
||||
void bes2600_unjoin_work(struct work_struct *work);
|
||||
void bes2600_offchannel_work(struct work_struct *work);
|
||||
void bes2600_wep_key_work(struct work_struct *work);
|
||||
void bes2600_update_filtering(struct bes2600_vif *priv);
|
||||
void bes2600_update_filtering_work(struct work_struct *work);
|
||||
int __bes2600_flush(struct bes2600_common *hw_priv, bool drop, int if_id);
|
||||
void bes2600_set_beacon_wakeup_period_work(struct work_struct *work);
|
||||
int bes2600_enable_listening(struct bes2600_vif *priv,
|
||||
struct ieee80211_channel *chan);
|
||||
int bes2600_disable_listening(struct bes2600_vif *priv);
|
||||
int bes2600_set_uapsd_param(struct bes2600_vif *priv,
|
||||
const struct wsm_edca_params *arg);
|
||||
void bes2600_ba_work(struct work_struct *work);
|
||||
void bes2600_ba_timer(struct timer_list *t);
|
||||
const u8 *bes2600_get_ie(u8 *start, size_t len, u8 ie);
|
||||
int bes2600_vif_setup(struct bes2600_vif *priv);
|
||||
int bes2600_setup_mac_pvif(struct bes2600_vif *priv);
|
||||
void bes2600_iterate_vifs(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif);
|
||||
void bes2600_rem_chan_timeout(struct work_struct *work);
|
||||
int bes2600_set_macaddrfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data);
|
||||
#ifdef IPV6_FILTERING
|
||||
int bes2600_set_na(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
#endif /*IPV6_FILTERING*/
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
void bes2600_device_power_calc(struct bes2600_common *priv,
|
||||
s16 max_output_power, s16 fe_cor, u32 band);
|
||||
int bes2600_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len);
|
||||
int bes2600_testmode_event(struct wiphy *wiphy, const u32 msg_id,
|
||||
const void *data, int len, gfp_t gfp);
|
||||
int bes2600_get_tx_power_range(struct ieee80211_hw *hw);
|
||||
int bes2600_get_tx_power_level(struct ieee80211_hw *hw);
|
||||
#endif /* CONFIG_BES2600_TESTMODE */
|
||||
int bes2600_wifi_start(struct bes2600_common *hw_priv);
|
||||
int bes2600_wifi_stop(struct bes2600_common *hw_priv);
|
||||
#endif /* STA_H_INCLUDED */
|
||||
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include "bes2600.h"
|
||||
#include "wsm.h"
|
||||
#include "queue.h"
|
||||
#include "bes_log.h"
|
||||
|
||||
struct tx_loop_table
|
||||
{
|
||||
u16 cmd;
|
||||
void (*proc)(struct bes2600_common *hw_priv, u8 *buf, u32 len);
|
||||
};
|
||||
|
||||
static void bes2600_tx_loop_build_lmac_tx_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len);
|
||||
static void bes2600_tx_loop_build_scan_compl_ind(struct bes2600_common *hw_priv);
|
||||
static void bes2600_tx_loop_build_pm_ind(struct bes2600_common *hw_priv);
|
||||
|
||||
void bes2600_tx_loop_init(struct bes2600_common *hw_priv)
|
||||
{
|
||||
hw_priv->tx_loop.enabled = false;
|
||||
hw_priv->tx_loop.start_lmac_seq = 0;
|
||||
hw_priv->tx_loop.start_mcu_seq = 0;
|
||||
INIT_LIST_HEAD(&hw_priv->tx_loop.pending_record_list);
|
||||
spin_lock_init(&hw_priv->tx_loop.pending_record_lock);
|
||||
spin_lock_init(&hw_priv->tx_loop.tx_loop_lock);
|
||||
skb_queue_head_init(&hw_priv->tx_loop.rx_queue);
|
||||
}
|
||||
|
||||
struct sk_buff *bes2600_tx_loop_read(struct bes2600_common *hw_priv)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct wsm_hdr *wsm;
|
||||
|
||||
if(hw_priv == NULL)
|
||||
return NULL;
|
||||
|
||||
skb = skb_dequeue(&hw_priv->tx_loop.rx_queue);
|
||||
if(skb != NULL) {
|
||||
wsm = (struct wsm_hdr *)skb->data;
|
||||
bes_devel("tx loop pipe read msg_id:0x%04x seq:%d\n",
|
||||
WSM_MSG_ID_GET(wsm->id), WSM_MSG_SEQ_GET(wsm->id));
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_item_pending_item(struct bes2600_common *hw_priv, struct sk_buff *skb)
|
||||
{
|
||||
bes_devel("tx loop confirm pending skb.\n");
|
||||
bes2600_tx_loop_build_lmac_tx_cfm(hw_priv, skb->data, skb->data_len);
|
||||
}
|
||||
|
||||
void bes2600_tx_loop_record_wsm_cmd(struct bes2600_common *hw_priv, u8 *wsm_cmd)
|
||||
{
|
||||
hw_priv->tx_loop.wsm_cmd_ptr = wsm_cmd;
|
||||
}
|
||||
|
||||
void bes2600_tx_loop_clear_wsm_cmd(struct bes2600_common *hw_priv)
|
||||
{
|
||||
hw_priv->tx_loop.wsm_cmd_ptr = NULL;
|
||||
}
|
||||
|
||||
void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv, bool need_warn)
|
||||
{
|
||||
int i = 0;
|
||||
u16 cmd_id = -1;
|
||||
|
||||
if(hw_priv == NULL)
|
||||
return;
|
||||
|
||||
if(hw_priv->tx_loop.enabled)
|
||||
return;
|
||||
|
||||
WARN_ON(need_warn);
|
||||
|
||||
hw_priv->tx_loop.enabled = true;
|
||||
hw_priv->tx_loop.start_lmac_seq = hw_priv->wsm_rx_seq[0];
|
||||
hw_priv->tx_loop.start_mcu_seq = hw_priv->wsm_rx_seq[1];
|
||||
|
||||
if(hw_priv->tx_loop.wsm_cmd_ptr) {
|
||||
struct wsm_hdr *tx_wsm = (struct wsm_hdr *)hw_priv->tx_loop.wsm_cmd_ptr;
|
||||
cmd_id = tx_wsm->id & WSM_MSG_ID_MASK;
|
||||
|
||||
if(cmd_id == 0x0005 || cmd_id == 0x0006){
|
||||
struct wsm_mib *mib = (struct wsm_mib *)hw_priv->wsm_cmd.arg;
|
||||
u16 mib_id = mib->mibId;
|
||||
bes_devel("pending cmd:0x%04x seq:%d mib_id:0x%04x\n",
|
||||
cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id), mib_id);
|
||||
} else {
|
||||
bes_devel("pending cmd:0x%04x seq:%d\n",
|
||||
cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id));
|
||||
}
|
||||
|
||||
bes2600_tx_loop_pipe_send(hw_priv, hw_priv->tx_loop.wsm_cmd_ptr, 8);
|
||||
}
|
||||
|
||||
if (atomic_read(&hw_priv->scan.in_progress) && cmd_id != 0x0007) {
|
||||
bes2600_tx_loop_build_scan_compl_ind(hw_priv);
|
||||
} else if (atomic_read(&hw_priv->bes_power.pm_set_in_process) && cmd_id != 0x0010) {
|
||||
bes2600_tx_loop_build_pm_ind(hw_priv);
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
bes2600_queue_iterate_pending_packet(&hw_priv->tx_queue[i],
|
||||
bes2600_tx_loop_item_pending_item);
|
||||
}
|
||||
spin_lock(&hw_priv->tx_loop.pending_record_lock);
|
||||
bes2600_queue_iterate_record_pending_packet(hw_priv, bes2600_tx_loop_item_pending_item);
|
||||
spin_unlock(&hw_priv->tx_loop.pending_record_lock);
|
||||
|
||||
if (atomic_read(&hw_priv->bh_rx) > 0)
|
||||
wake_up(&hw_priv->bh_wq);
|
||||
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_lmac_generic_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_hdr *rx_wsm, *tx_wsm;
|
||||
u32 msg_len = sizeof(struct wsm_hdr) + 4;
|
||||
u16 msg_id = 0;
|
||||
|
||||
tx_wsm = (struct wsm_hdr *)buf;
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_err("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s cmd 0x%04x len:%d\n",
|
||||
__func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_hdr *)out_skb->data;
|
||||
msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
msg_id |= 0x400; // set confirm flag
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
|
||||
rx_wsm->id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->len = __cpu_to_le16(msg_len);
|
||||
*((u32 *)&rx_wsm[1]) = __cpu_to_le32(WSM_STATUS_SUCCESS);
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_lmac_tx_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_hdr *rx_wsm;
|
||||
struct wsm_tx *tx_wsm;
|
||||
struct wsm_tx_confirm *cfm;
|
||||
u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_tx_confirm);
|
||||
u16 msg_id = 0;
|
||||
|
||||
tx_wsm = (struct wsm_tx *)buf;
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_err("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s cmd 0x%04x len:%d\n",
|
||||
__func__, (tx_wsm->hdr.id & WSM_MSG_ID_MASK), msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_hdr *)out_skb->data;
|
||||
msg_id = (tx_wsm->hdr.id & WSM_MSG_ID_MASK);
|
||||
msg_id |= 0x400; // set confirm flag
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
|
||||
rx_wsm->id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->len = __cpu_to_le16(msg_len);
|
||||
|
||||
cfm = (struct wsm_tx_confirm *)&rx_wsm[1];
|
||||
cfm->packetID = tx_wsm->packetID;
|
||||
cfm->status = WSM_STATUS_SUCCESS;
|
||||
cfm->txedRate = tx_wsm->maxTxRate;
|
||||
cfm->ackFailures = 0;
|
||||
cfm->flags = WSM_TX_STATUS_NORMAL_ACK;
|
||||
cfm->txQueueDelay = 5;
|
||||
cfm->mediaDelay = 3;
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_config_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_hdr *rx_wsm;
|
||||
struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
|
||||
u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_configuration) + 4;
|
||||
u16 msg_id = 0;
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_err("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s cmd 0x%04x len:%d\n",
|
||||
__func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_hdr *)out_skb->data;
|
||||
msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
msg_id |= 0x400; // set confirm flag
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
|
||||
rx_wsm->id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->len = __cpu_to_le16(msg_len);
|
||||
*(u32 *)&rx_wsm[1] = WSM_STATUS_SUCCESS;
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_read_mib_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_hdr *rx_wsm;
|
||||
struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
|
||||
struct wsm_mib *mib = (struct wsm_mib *)hw_priv->wsm_cmd.arg;
|
||||
u32 *status;
|
||||
u16 *mib_id, *size;
|
||||
u32 msg_len = sizeof(struct wsm_hdr) + 4 /* status */ + 2 /* mib_id */ + mib->buf_size;
|
||||
u16 msg_id = 0;
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_err("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s cmd 0x%04x len:%d\n",
|
||||
__func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_hdr *)out_skb->data;
|
||||
msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
msg_id |= 0x400; // set confirm flag
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
|
||||
rx_wsm->id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->len = __cpu_to_le16(msg_len);
|
||||
|
||||
status = (u32 *)&rx_wsm[1];
|
||||
*status = WSM_STATUS_SUCCESS;
|
||||
|
||||
mib_id = (u16 *)&status[1];
|
||||
*mib_id = mib->mibId;
|
||||
|
||||
size = (u16 *)&mib_id[1];
|
||||
*size = mib->buf_size;
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_join_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_hdr *rx_wsm;
|
||||
struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
|
||||
u16 msg_id = 0;
|
||||
u32 msg_len = sizeof(struct wsm_hdr) + 4 /* status */ + 8 /* power_level */;
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_err("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s cmd 0x%04x len:%d\n",
|
||||
__func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_hdr *)out_skb->data;
|
||||
msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
msg_id |= 0x400; // set confirm flag
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
|
||||
rx_wsm->id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->len = __cpu_to_le16(msg_len);
|
||||
*(u32 *)&rx_wsm[1] = WSM_STATUS_SUCCESS;
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_rfcmd_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_mcu_hdr *rx_wsm;
|
||||
struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
|
||||
u16 msg_id = 0;
|
||||
u32 msg_len = sizeof(struct wsm_mcu_hdr);
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_err("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s cmd 0x%04x len:%d\n",
|
||||
__func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_mcu_hdr *)out_skb->data;
|
||||
msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq);
|
||||
rx_wsm->hdr.id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->hdr.len = __cpu_to_le16(msg_len);
|
||||
rx_wsm->handle_label = WSM_TO_MCU_CMD_CONFIRM_LABEL;
|
||||
rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_driver_cmd_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_mcu_hdr *rx_wsm;
|
||||
struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
|
||||
u16 msg_id = 0;
|
||||
u32 msg_len = sizeof(struct wsm_mcu_hdr) + 4;
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_err("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s cmd 0x%04x len:%d\n",
|
||||
__func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_mcu_hdr *)out_skb->data;
|
||||
msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq);
|
||||
rx_wsm->hdr.id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->hdr.len = __cpu_to_le16(msg_len);
|
||||
rx_wsm->handle_label = WSM_TO_MCU_CMD_CONFIRM_LABEL;
|
||||
rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
*(uint32_t *)(rx_wsm + 1) = 0;
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static struct tx_loop_table tx_loop_tbl[] = {
|
||||
{.cmd = 0x0004, .proc = bes2600_tx_loop_build_lmac_tx_cfm},
|
||||
{.cmd = 0x0009, .proc = bes2600_tx_loop_build_config_cfm},
|
||||
{.cmd = 0x0005, .proc = bes2600_tx_loop_build_read_mib_cfm},
|
||||
{.cmd = 0x0006, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x000B, .proc = bes2600_tx_loop_build_join_cfm},
|
||||
{.cmd = 0x0007, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0008, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x000A, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x000C, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x000D, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0010, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0011, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0012, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0013, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0016, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0017, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0018, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0019, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x001A, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x001B, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x001C, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0029, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
|
||||
{.cmd = 0x0C25, .proc = bes2600_tx_loop_build_rfcmd_cfm},
|
||||
{.cmd = 0x0C27, .proc = bes2600_tx_loop_build_driver_cmd_cfm},
|
||||
};
|
||||
|
||||
static void bes2600_tx_loop_build_scan_compl_ind(struct bes2600_common *hw_priv)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_hdr *rx_wsm;
|
||||
struct wsm_scan_complete *scan_compl;
|
||||
u16 msg_id = 0;
|
||||
u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_scan_complete);
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_devel("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s len:%d\n", __func__, msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_hdr *)out_skb->data;
|
||||
msg_id |= 0x806; // set indication flag
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
|
||||
rx_wsm->id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->len = __cpu_to_le16(msg_len);
|
||||
|
||||
scan_compl = (struct wsm_scan_complete *)&rx_wsm[1];
|
||||
scan_compl->status = WSM_STATUS_SUCCESS;
|
||||
scan_compl->psm = WSM_PSM_PS;
|
||||
scan_compl->numChannels = 2;
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_pm_ind(struct bes2600_common *hw_priv)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_hdr *rx_wsm;
|
||||
struct wsm_set_pm_complete *pm_compl;
|
||||
u16 msg_id = 0;
|
||||
u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_set_pm_complete);
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_devel("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s len:%d\n", __func__, msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_hdr *)out_skb->data;
|
||||
msg_id |= 0x809; // set indication flag
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
|
||||
rx_wsm->id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->len = __cpu_to_le16(msg_len);
|
||||
|
||||
pm_compl = (struct wsm_set_pm_complete *)&rx_wsm[1];
|
||||
pm_compl->status = WSM_STATUS_SUCCESS;
|
||||
pm_compl->psm = WSM_PSM_PS;
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
static void bes2600_tx_loop_build_rfcmd_ind(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
struct sk_buff *out_skb;
|
||||
struct wsm_mcu_hdr *rx_wsm;
|
||||
struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
|
||||
u16 msg_id = 0;
|
||||
u32 msg_len = sizeof(struct wsm_mcu_hdr);
|
||||
|
||||
out_skb = dev_alloc_skb(msg_len);
|
||||
if(IS_ERR_OR_NULL(out_skb)) {
|
||||
bes_err("%s, alloc mem fail.\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
bes_devel("%s len:%d\n",__func__, msg_len);
|
||||
skb_put(out_skb, msg_len);
|
||||
rx_wsm = (struct wsm_mcu_hdr *)out_skb->data;
|
||||
msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq);
|
||||
rx_wsm->hdr.id = __cpu_to_le16(msg_id);
|
||||
rx_wsm->hdr.len = __cpu_to_le16(msg_len);
|
||||
rx_wsm->handle_label = WSM_TO_MCU_CMD_INDICATION_LABEL;
|
||||
rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK);
|
||||
|
||||
spin_lock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7;
|
||||
skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
|
||||
spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
|
||||
|
||||
atomic_add_return(1, &hw_priv->bh_rx);
|
||||
}
|
||||
|
||||
void bes2600_tx_loop_pipe_send(struct bes2600_common *hw_priv, u8 *buf, u32 len)
|
||||
{
|
||||
int i = 0;
|
||||
int tbl_size = ARRAY_SIZE(tx_loop_tbl);
|
||||
struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
|
||||
u16 cmd_id = tx_wsm->id & WSM_MSG_ID_MASK;
|
||||
|
||||
/* don't need to tx loop if wifi is unregistered */
|
||||
if(hw_priv == NULL)
|
||||
return;
|
||||
|
||||
bes_devel("tx loop pipe send cmd:0x%04x seq:%d\n",
|
||||
cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id));
|
||||
|
||||
/* select build confirm function based on command id */
|
||||
for(i = 0; i < tbl_size; i++) {
|
||||
if(cmd_id == tx_loop_tbl[i].cmd) {
|
||||
tx_loop_tbl[i].proc(hw_priv, buf, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* build indication for special command */
|
||||
if(cmd_id == 0x0007) {
|
||||
bes2600_tx_loop_build_scan_compl_ind(hw_priv);
|
||||
} else if(cmd_id == 0x0010) {
|
||||
bes2600_tx_loop_build_pm_ind(hw_priv);
|
||||
} else if(cmd_id == 0x0C25) {
|
||||
bes2600_tx_loop_build_rfcmd_ind(hw_priv, buf, len);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __TX_LOOP_H__
|
||||
#define __TX_LOOP_H__
|
||||
|
||||
#include "bes2600.h"
|
||||
|
||||
struct bes2600_tx_loop {
|
||||
bool enabled;
|
||||
spinlock_t tx_loop_lock;
|
||||
u8 start_lmac_seq;
|
||||
u8 start_mcu_seq;
|
||||
struct sk_buff_head rx_queue;
|
||||
u8 *wsm_cmd_ptr;
|
||||
struct list_head pending_record_list;
|
||||
spinlock_t pending_record_lock;
|
||||
};
|
||||
|
||||
void bes2600_tx_loop_init(struct bes2600_common *hw_priv);
|
||||
void bes2600_tx_loop_record_wsm_cmd(struct bes2600_common *hw_priv, u8 *wsm_cmd);
|
||||
void bes2600_tx_loop_clear_wsm_cmd(struct bes2600_common *hw_priv);
|
||||
struct sk_buff *bes2600_tx_loop_read(struct bes2600_common *hw_priv);
|
||||
void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv, bool need_warn);
|
||||
void bes2600_tx_loop_pipe_send(struct bes2600_common *hw_priv, u8 *buf, u32 len);
|
||||
|
||||
#endif
|
||||
+1971
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Datapath interface for BES2600 mac80211 drivers
|
||||
*
|
||||
* Copyright (c) 2010, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef BES2600_TXRX_H
|
||||
#define BES2600_TXRX_H
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
/* extern */ struct ieee80211_hw;
|
||||
/* extern */ struct sk_buff;
|
||||
/* extern */ struct wsm_tx;
|
||||
/* extern */ struct wsm_rx;
|
||||
/* extern */ struct wsm_tx_confirm;
|
||||
/* extern */ struct bes2600_txpriv;
|
||||
/* extern */ struct bes2600_vif;
|
||||
|
||||
struct tx_policy {
|
||||
union {
|
||||
__le32 tbl[3];
|
||||
u8 raw[12];
|
||||
};
|
||||
u8 defined; /* TODO: u32 or u8, profile and select best */
|
||||
u8 usage_count; /* --// -- */
|
||||
u8 retry_count; /* --// -- */
|
||||
u8 uploaded;
|
||||
};
|
||||
|
||||
struct tx_policy_cache_entry {
|
||||
struct tx_policy policy;
|
||||
struct list_head link;
|
||||
};
|
||||
|
||||
#define TX_POLICY_CACHE_SIZE (8)
|
||||
struct tx_policy_cache {
|
||||
struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
|
||||
struct list_head used;
|
||||
struct list_head free;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* TX policy cache */
|
||||
/* Intention of TX policy cache is an overcomplicated WSM API.
|
||||
* Device does not accept per-PDU tx retry sequence.
|
||||
* It uses "tx retry policy id" instead, so driver code has to sync
|
||||
* linux tx retry sequences with a retry policy table in the device.
|
||||
*/
|
||||
void tx_policy_init(struct bes2600_common *hw_priv);
|
||||
void tx_policy_upload_work(struct work_struct *work);
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* TX implementation */
|
||||
|
||||
u32 bes2600_rate_mask_to_wsm(struct bes2600_common *hw_priv,
|
||||
u32 rates);
|
||||
void bes2600_tx(struct ieee80211_hw *dev,
|
||||
struct ieee80211_tx_control *control,
|
||||
struct sk_buff *skb);
|
||||
void bes2600_skb_dtor(struct bes2600_common *hw_priv,
|
||||
struct sk_buff *skb,
|
||||
const struct bes2600_txpriv *txpriv);
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* WSM callbacks */
|
||||
|
||||
void bes2600_tx_confirm_cb(struct bes2600_common *hw_priv,
|
||||
struct wsm_tx_confirm *arg);
|
||||
void bes2600_rx_cb(struct bes2600_vif *priv,
|
||||
struct wsm_rx *arg,
|
||||
struct sk_buff **skb_p);
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* Timeout */
|
||||
|
||||
void bes2600_tx_timeout(struct work_struct *work);
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* Security */
|
||||
int bes2600_alloc_key(struct bes2600_common *hw_priv);
|
||||
void bes2600_free_key(struct bes2600_common *hw_priv, int idx);
|
||||
void bes2600_free_keys(struct bes2600_common *hw_priv);
|
||||
int bes2600_upload_keys(struct bes2600_vif *priv);
|
||||
|
||||
/* ******************************************************************** */
|
||||
/* Workaround for WFD test case 6.1.10 */
|
||||
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
||||
void bes2600_link_id_reset(struct work_struct *work);
|
||||
#endif
|
||||
|
||||
#endif /* BES2600_TXRX_H */
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Mac80211 driver for BES2600 device
|
||||
*
|
||||
* Copyright (c) 2022, Bestechnic
|
||||
* Author:
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifdef CONFIG_BES2600_TESTMODE
|
||||
#include <net/netlink.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/sched.h>
|
||||
#include <net/mac80211.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "wsm.h"
|
||||
#include "bes2600.h"
|
||||
#include "bes_nl80211_testmode_msg.h"
|
||||
#include "bes_chardev.h"
|
||||
#include "bes2600_factory.h"
|
||||
|
||||
|
||||
/* store the reply message of the rf cmd */
|
||||
static struct vendor_rf_cmd_msg_reply vendor_rf_cmd_reply;
|
||||
|
||||
extern int bes2600_testmode_reply(struct wiphy *wiphy, const void *data, int len);
|
||||
|
||||
void bes2600_rf_cmd_msg_assembly(u32 cmd_type, void *data, u32 msg_len)
|
||||
{
|
||||
vendor_rf_cmd_reply.id = cmd_type;
|
||||
vendor_rf_cmd_reply.msg_len = msg_len + 2 * sizeof(u32);
|
||||
|
||||
if (msg_len)
|
||||
memcpy(vendor_rf_cmd_reply.ret_msg, (u8 *)data, msg_len);
|
||||
else
|
||||
vendor_rf_cmd_reply.ret_msg[0] = '\0';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* bes2600_vendor_rf_cmd, signaling cmd & rf nosignaling cmd
|
||||
* reaches bes2600
|
||||
*
|
||||
* @hw: the hardware
|
||||
* @vif: vif
|
||||
* @data: incoming data
|
||||
* @len: incoming data length
|
||||
*
|
||||
* Returns: 0 on success or non zero value on failure
|
||||
*/
|
||||
int bes2600_vendor_rf_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len)
|
||||
{
|
||||
struct bes2600_common *hw_priv = hw->priv;
|
||||
int ret = 0;
|
||||
int recv_timeout = 1500; /*waiting for reply message*/
|
||||
struct vendor_rf_cmd_t *vendor_rf_cmd = (struct vendor_rf_cmd_t *)data;
|
||||
//struct timeval tstart, tend;
|
||||
int if_id = 0;
|
||||
u32 count, cmd_len, cmd_type;
|
||||
|
||||
count = vendor_rf_cmd->cmd_argc;
|
||||
cmd_len = vendor_rf_cmd->cmd_len;
|
||||
cmd_type = vendor_rf_cmd->cmd_type;
|
||||
|
||||
if (count == 0 || cmd_len == 0 ||
|
||||
strlen(vendor_rf_cmd->cmd) + 1 != cmd_len) {
|
||||
bes2600_err(BES2600_DBG_TEST_MODE, "%s line: %d, "
|
||||
"vendor rf cmd parsing failed\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bes2600_info(BES2600_DBG_TEST_MODE, "vendor cmd = %s\n", vendor_rf_cmd->cmd);
|
||||
|
||||
|
||||
/**
|
||||
* signaling mode does not support the saving of calibration files,
|
||||
* but the saved calibration values can be read.
|
||||
*
|
||||
*/
|
||||
if (bes2600_chrdev_is_signal_mode()) {
|
||||
if (cmd_type == VENDOR_RF_SAVE_FREQOFFSET_CMD ||
|
||||
cmd_type == VENDOR_RF_SAVE_POWERLEVEL_CMD ||
|
||||
cmd_type == VENDOR_RF_POWER_CALIB_FINISH)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (cmd_type == VENDOR_RF_SIG_NOSIG_MIX) {
|
||||
if (bes2600_chrdev_is_signal_mode()) {
|
||||
vendor_rf_cmd->cmd_type = VENDOR_RF_SIGNALING_CMD;
|
||||
cmd_type = VENDOR_RF_SIGNALING_CMD;
|
||||
} else {
|
||||
vendor_rf_cmd->cmd_type = VENDOR_RF_NOSIGNALING_CMD;
|
||||
cmd_type = VENDOR_RF_NOSIGNALING_CMD;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cmd_type) {
|
||||
case VENDOR_RF_POWER_CALIB_FINISH:
|
||||
case VENDOR_RF_GET_SAVE_FREQOFFSET_CMD:
|
||||
case VENDOR_RF_GET_SAVE_POWERLEVEL_CMD:
|
||||
case VENDOR_RF_SIGNALING_CMD:
|
||||
case VENDOR_RF_NOSIGNALING_CMD:
|
||||
case VENDOR_RF_SAVE_FREQOFFSET_CMD:
|
||||
case VENDOR_RF_SAVE_POWERLEVEL_CMD:
|
||||
case VENDOR_RF_GET_CALI_FROM_EFUSE:
|
||||
sema_init(&hw_priv->vendor_rf_cmd_replay_sema, 0);
|
||||
ret = wsm_vendor_rf_cmd(hw_priv, if_id, vendor_rf_cmd);
|
||||
|
||||
if (ret) {
|
||||
bes2600_err(BES2600_DBG_TEST_MODE, "vendor rf cmd send error code = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//do_gettimeofday(&tstart);
|
||||
if (down_timeout(&hw_priv->vendor_rf_cmd_replay_sema, recv_timeout)) {
|
||||
bes2600_err(BES2600_DBG_TEST_MODE, "vendor rf cmd failed to receive reply message\n");
|
||||
return -ENOMSG;
|
||||
}
|
||||
//do_gettimeofday(&tend);
|
||||
//bes2600_dbg(BES2600_DBG_TEST_MODE,"recv time: %ldms\n",
|
||||
//1000 * (tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec) / 1000);
|
||||
|
||||
ret = bes2600_testmode_reply(hw->wiphy, &vendor_rf_cmd_reply, vendor_rf_cmd_reply.msg_len);
|
||||
vendor_rf_cmd_reply.ret_msg[0] = '\0';
|
||||
break;
|
||||
case VENDOR_RF_SIG_NOSIG_MIX:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_BES2600_TESTMODE */
|
||||
+3159
File diff suppressed because it is too large
Load Diff
+2242
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user