Julian
2025-09-17 16:35:45 +02:00
commit ba20341e70
59 changed files with 33849 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/make -f
PACKAGE_NAME := bes2600
PACKAGE_VERSION := 0.1
install:
dkms add .
dkms install $(PACKAGE_NAME)/$(PACKAGE_VERSION)
uninstall:
dkms uninstall $(PACKAGE_NAME)/$(PACKAGE_VERSION) || true
dkms remove $(PACKAGE_NAME)/$(PACKAGE_VERSION) --all || true
rm -r /usr/src/$(PACKAGE_NAME)-$(PACKAGE_VERSION) || true
load:
@test -n "$$(dkms status bes2600/0.1)" || { echo 'Please run `make -f Makefile.dkms install` first.'; false; }
modprobe bes2600
unload:
rmmod bes2600
.PHONY: install uninstall load unload
+95
View File
@@ -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
+230
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+54
View File
@@ -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
+859
View File
@@ -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 */
+999
View File
@@ -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;
}
+220
View File
@@ -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
+38
View File
@@ -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
+96
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+124
View File
@@ -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;
}
+101
View File
@@ -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
+10
View File
@@ -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__)
+268
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+169
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+48
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+581
View File
@@ -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;
}
+86
View File
@@ -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
+651
View File
@@ -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);
}
+52
View File
@@ -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*/
+28
View File
@@ -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);
}
+48
View File
@@ -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
+43
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+42
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+85
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+72
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+139
View File
@@ -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 */
+537
View File
@@ -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);
}
}
+34
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+98
View File
@@ -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 */
+143
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+2242
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
bes2600 (0.1) unstable; urgency=low
* Initial release.
-- Julian <mail@julianfairfax.ch> Wed, 17 Sep 2024 15:30:00 +0100
+15
View File
@@ -0,0 +1,15 @@
Source: bes2600
Maintainer: Julian <mail@julianfairfax.ch>
Section: kernel
Priority: optional
Build-Depends: debhelper-compat (= 13),
dh-sequence-dkms,
Standards-Version: 4.6.2
Rules-Requires-Root: no
Testsuite: autopkgtest-pkg-dkms
Package: bes2600
Architecture: all
Depends: ${misc:Depends}
Description: Wi-Fi driver for the PineTab2
This package contains the BES2600 driver.
+25
View File
@@ -0,0 +1,25 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Files: bes2600/*
Copyright: 2022, Bestechnic
License: GPL-2.0
All rights reserved.
Files: *
Copyright: 2024, Arnaud Ferraris <arnaud.ferraris@gmail.com>
License: LGPL-2.1
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; version 2.1.
.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
.
On Debian systems, the full text of the GNU Lesser General Public License
version 2.1 can be found in the file "/usr/share/common-licenses/LGPL-2.1".
Vendored
+1
View File
@@ -0,0 +1 @@
dkms.conf
Vendored Executable
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
for dir in bes2600; do
echo ${dir} /usr/src/${PACKAGE_NAME}-${PACKAGE_VERSION}
done
Vendored Executable
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/make -f
PACKAGE_NAME=$(shell grep PACKAGE_NAME= dkms.conf | cut -d= -f2 | cut -d\" -f2)
PACKAGE_VERSION=$(shell grep PACKAGE_VERSION= dkms.conf | cut -d= -f2 | cut -d\" -f2)
export PACKAGE_NAME PACKAGE_VERSION
%:
dh $@
override_dh_auto_build:
dh_auto_build
+1
View File
@@ -0,0 +1 @@
3.0 (native)
+8
View File
@@ -0,0 +1,8 @@
PACKAGE_VERSION="0.1"
PACKAGE_NAME="bes2600"
CLEAN="make clean"
BUILT_MODULE_NAME="bes2600"
BUILT_MODULE_LOCATION="bes2600/"
DEST_MODULE_LOCATION="/extra"
MAKE="make KVER=$kernelver -C ${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build/${PACKAGE_NAME}"
AUTOINSTALL="yes"