Compare commits

..

1 Commits

Author SHA1 Message Date
test0r 9398d3028b bes2600: enable CONFIG_BES2600_TESTMODE by default + fix bit-rotted testmode plumbing
The driver implements a mac80211 testmode_cmd operation that dispatches
to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE,
SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the
BES2600 RF-test path (bes2600_vendor_rf_cmd → firmware
patch_wifi_testMode). The testmode handlers and the .testmode_cmd
binding in struct ieee80211_ops are conditionally compiled under
CONFIG_BES2600_TESTMODE, which previously defaulted to n.

Flip the Makefile default from n to y so wifi_testmode_cmd.o is
included in the build and the .testmode_cmd op is populated. On the
PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with
CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface
through the standard nl80211 testmode interface ('iw phy0 ...').

This also makes visible two classes of bit-rot that had accumulated
while nobody was building with CONFIG_BES2600_TESTMODE=y:

1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() /
   bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a
   legacy log-macro family carrying a BES2600_DBG_* subsystem-id
   first argument. Neither the macros nor any of the BES2600_DBG_*
   constants are defined anywhere in the tree. The same call pattern
   appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and
   under CONFIG_BES2600_ITP in itp.c, both normally disabled.

   Add minimal shim macros to bes_log.h that rewire the calls onto
   the existing bes_info() / bes_err() / bes_warn() / bes_devel()
   family (ignoring the subsystem id). Define BES2600_DBG_SBUS,
   BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE
   as 0 constants for documentation / grep.

2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and
   bes2600_get_roam_delay() are declared in sta.c with external
   linkage but have no prototype in any header. All callers live in
   sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE
   off the compiler never sees them; with it on gcc
   -Werror=missing-prototypes breaks the build.

   Mark the three functions static. (Keeping them file-local also
   matches their actual usage.)

Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y
buildable; no behavioural change when the flag is off.

Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds
cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from
userspace.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-22 13:10:10 +02:00
15 changed files with 823 additions and 287 deletions
+14 -2
View File
@@ -28,6 +28,7 @@ CONFIG_BES2600_WIFI_BOOT_ON ?= y
CONFIG_BES2600_BT_BOOT_ON ?= n CONFIG_BES2600_BT_BOOT_ON ?= n
BES2600_GPIO_WAKEUP_AP ?= n BES2600_GPIO_WAKEUP_AP ?= n
BES2600_WRITE_DPD_TO_FILE ?= n
BES2600_TX_MORE_RETRY ?= n BES2600_TX_MORE_RETRY ?= n
# bes evb # bes evb
@@ -64,8 +65,8 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y) ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
FACTORY_CRC_CHECK ?= n FACTORY_CRC_CHECK ?= n
STANDARD_FACTORY_EFUSE_FLAG ?= n STANDARD_FACTORY_EFUSE_FLAG ?= y
FACTORY_PATH ?= bes2600/bes2600_factory.txt FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
endif endif
# basic function # basic function
@@ -92,6 +93,12 @@ ccflags-y += -DBES_UNIFIED_PM
ccflags-y += -DBES_SDIO_OPTIMIZED_LEN ccflags-y += -DBES_SDIO_OPTIMIZED_LEN
ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG 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) ifeq ($(BES2600_DUMP_FW_DPD_LOG),y)
BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log
endif endif
@@ -128,6 +135,9 @@ 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_PATH)
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE) ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE)
ccflags-y += $(call string_flag,BES2600_DRV_VERSION) 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_INDEPENDENT_EVB,y)
ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y) ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y)
@@ -149,6 +159,8 @@ ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y)
ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,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_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 boolen_flag,BES2600_DUMP_FW_DPD_LOG,y)
ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH) ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH)
+65 -43
View File
@@ -12,7 +12,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/firmware.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/crc32.h> #include <linux/crc32.h>
@@ -31,18 +30,6 @@
static DEFINE_MUTEX(factory_lock); static DEFINE_MUTEX(factory_lock);
/*
* struct device * for request_firmware() context. Set once at SDIO
* probe via bes2600_factory_set_dev(). NULL is tolerated (falls back
* to the udev-less firmware-class path) but loses per-device logging.
*/
static struct device *bes2600_factory_dev;
void bes2600_factory_set_dev(struct device *dev)
{
bes2600_factory_dev = dev;
}
/* /*
* It is only used for temporary storage. * It is only used for temporary storage.
* Every time get the factory, it will read from the * Every time get the factory, it will read from the
@@ -150,32 +137,66 @@ static int bes2600_factory_crc_check(struct factory_t *factory_data)
*/ */
static int factory_section_read_file(char *path, void *buffer) static int factory_section_read_file(char *path, void *buffer)
{ {
const struct firmware *fw; int ret = 0;
int ret; struct file *fp;
if (!path || !buffer) { if (!path || !buffer) {
bes_err("%s NULL pointer err\n", __func__); bes_err("%s NULL pointer err\n", __func__);
return -1; return -1;
} }
bes_devel("requesting firmware-class %s\n", path); bes_devel("reading %s \n", path);
ret = request_firmware(&fw, path, bes2600_factory_dev); fp = filp_open(path, O_RDONLY, 0); //S_IRUSR
if (ret) { if (IS_ERR(fp)) {
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret); bes_devel("BES2600 : can't open %s\n",path);
return -1; return -1;
} }
if (fw->size == 0 || fw->size > FACTORY_MAX_SIZE) { 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: %zu max_size: %d\n", bes_err( "bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n",
fw->size, FACTORY_MAX_SIZE); fp->f_inode->i_size, FACTORY_MAX_SIZE);
release_firmware(fw); filp_close(fp, NULL);
return -1; return -1;
} }
memcpy(buffer, fw->data, fw->size); ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos);
ret = (int)fw->size;
release_firmware(fw); 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; return ret;
} }
@@ -870,22 +891,9 @@ static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
#endif #endif
} }
/*
* Rebuild the serialised calibration blob in file_buffer from the live
* in-memory factory_save_p. Previously this function also persisted the
* blob back to FACTORY_PATH via filp_open(O_CREAT) + kernel_write(); that
* is not acceptable in mainline, so the persistence step has been removed.
*
* The in-memory factory_save_p remains authoritative for the duration of
* the session; on the next probe the firmware-class file is read back
* read-only via request_firmware(). If cross-reboot persistence of runtime
* calibration updates becomes a requirement, the expected route is a
* userspace-facing dump interface (debugfs read-only blob, or nl80211
* vendor command) that lets userspace read the serialised form and store
* it under its own privileges.
*/
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p) static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p)
{ {
int ret = 0;
int w_size; int w_size;
u32 crc_len = sizeof(factory_data_t); u32 crc_len = sizeof(factory_data_t);
#ifndef STANDARD_FACTORY_EFUSE_FLAG #ifndef STANDARD_FACTORY_EFUSE_FLAG
@@ -894,11 +902,13 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
bes_devel("enter %s\n", __func__); bes_devel("enter %s\n", __func__);
if (!file_buffer) if (!file_buffer) {
return -ENOMEM; return -ENOMEM;
}
if (!factory_save_p) if (!factory_save_p) {
return -ENOENT; return -ENOENT;
}
/* All initialized to space */ /* All initialized to space */
memset(file_buffer, 32, FACTORY_MAX_SIZE); memset(file_buffer, 32, FACTORY_MAX_SIZE);
@@ -910,10 +920,22 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
w_size = factory_build(file_buffer, factory_save_p); w_size = factory_build(file_buffer, factory_save_p);
if (w_size < 0 || w_size > FACTORY_MAX_SIZE) { if (w_size < 0 || w_size > FACTORY_MAX_SIZE) {
bes_err("%s: build failed! w_size = %d.", __func__, w_size); bes_err("%s: build failed! ret = %d.", __func__, ret);
return -ETXTBSY; 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; return 0;
} }
-3
View File
@@ -199,9 +199,6 @@ enum factory_cali_status {
/* just calibrate 11n, other protocols are automatically mapped */ /* just calibrate 11n, other protocols are automatically mapped */
#define WIFI_RF_11N_MODE 0x15 #define WIFI_RF_11N_MODE 0x15
/* set the struct device * used for request_firmware() context */
void bes2600_factory_set_dev(struct device *dev);
/* read wifi & bt factory cali value*/ /* read wifi & bt factory cali value*/
u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path); u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path);
void factory_little_endian_cvrt(u8 *data); void factory_little_endian_cvrt(u8 *data);
+1 -71
View File
@@ -16,7 +16,6 @@
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/core.h>
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <net/mac80211.h> #include <net/mac80211.h>
@@ -31,7 +30,6 @@
#include "bes2600.h" #include "bes2600.h"
#include "sbus.h" #include "sbus.h"
#include "bes2600_plat.h" #include "bes2600_plat.h"
#include "bes2600_factory.h"
#include "hwio.h" #include "hwio.h"
#include "bes_chardev.h" #include "bes_chardev.h"
#include "bes_log.h" #include "bes_log.h"
@@ -96,7 +94,6 @@ struct sbus_priv {
struct work_struct tx_work; struct work_struct tx_work;
struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1]; struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1];
struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1]; struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1];
u8 *tx_bounce;
u32 tx_data_cnt; u32 tx_data_cnt;
u32 tx_xfer_cnt; u32 tx_xfer_cnt;
u32 tx_proc_cnt; u32 tx_proc_cnt;
@@ -1138,26 +1135,7 @@ static void sdio_tx_work(struct work_struct *work)
} }
} }
/* sg_set_buf(&sg[scatters], tx_buffer->buf, align);
* The transfer length is rounded up to the SDIO block
* size, but tx_buffer->buf is only tx_buffer->len bytes
* long (it usually aliases into an skb linear head).
* Copy into a driver-owned bounce buffer and zero-pad
* to the aligned size; otherwise DMA reads past the
* skb and leaks adjacent kernel memory on the wire --
* observed as KFENCE OOB reads from
* bes_sdio_memcpy_to_io_helper via dma_map_sg.
*/
if (WARN_ON_ONCE(total_len + align > MAX_SDIO_TRANSFER_LEN))
goto flush_previous;
memcpy(self->tx_bounce + total_len,
tx_buffer->buf, tx_buffer->len);
if (align > tx_buffer->len)
memset(self->tx_bounce + total_len +
tx_buffer->len, 0,
align - tx_buffer->len);
sg_set_buf(&sg[scatters],
self->tx_bounce + total_len, align);
total_len += align; total_len += align;
++scatters; ++scatters;
/*del_node:*/ /*del_node:*/
@@ -1778,33 +1756,6 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self)
sdio_work_debug(self); sdio_work_debug(self);
} }
/*
* Trigger an SDIO bus reset via mmc_hw_reset().
*
* With multiple SDIO functions probed (PineTab2 binds func 1 for WLAN and
* func 2 for the BT-companion path) mmc_sdio_hw_reset() takes the
* remove-and-rescan path: it marks the card removed and schedules
* mmc_rescan, which tears down the bound function drivers and re-detects
* the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
*
* With a single function probed it instead invokes mmc_power_cycle()
* directly, which on PineTab2 toggles the wifi-reset GPIO via sdio_pwrseq.
*
* In both cases the chip ends up in a freshly reset state, which is the
* goal of the recovery path.
*
* mmc_hw_reset() must be called without holding the SDIO host claim --
* the multi-func remove-and-rescan path acquires the host claim via the
* mmc workqueue.
*/
static int bes2600_sdio_bus_reset(struct sbus_priv *self)
{
if (!self || !self->func || !self->func->card)
return -EINVAL;
return mmc_hw_reset(self->func->card);
}
static bool bes2600_sdio_wakeup_source(struct sbus_priv *self) static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)
{ {
struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
@@ -1843,7 +1794,6 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
.gpio_sleep = bes2600_gpio_allow_mcu_sleep, .gpio_sleep = bes2600_gpio_allow_mcu_sleep,
.halt_device = bes2600_sdio_halt_device, .halt_device = bes2600_sdio_halt_device,
.wakeup_source = bes2600_sdio_wakeup_source, .wakeup_source = bes2600_sdio_wakeup_source,
.bus_reset = bes2600_sdio_bus_reset,
}; };
static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv) static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
@@ -1884,9 +1834,6 @@ static int bes2600_sdio_probe(struct sdio_func *func,
if (ret) if (ret)
goto err; goto err;
/* wire struct device into factory.c for request_firmware() context */
bes2600_factory_set_dev(dev);
self->pdata = bes2600_get_platform_data(); self->pdata = bes2600_get_platform_data();
self->func = func; self->func = func;
self->dev = &func->dev; self->dev = &func->dev;
@@ -1906,17 +1853,6 @@ static int bes2600_sdio_probe(struct sdio_func *func,
if (!self->single_gathered_buffer) if (!self->single_gathered_buffer)
return -ENOMEM; return -ENOMEM;
#endif #endif
#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL,
get_order(MAX_SDIO_TRANSFER_LEN));
if (!self->tx_bounce) {
#ifndef SDIO_HOST_ADMA_SUPPORT
free_pages((unsigned long)self->single_gathered_buffer,
get_order(MAX_SDIO_TRANSFER_LEN));
#endif
return -ENOMEM;
}
#endif
#ifdef BES_SDIO_RXTX_TOGGLE #ifdef BES_SDIO_RXTX_TOGGLE
self->fw_started = false; self->fw_started = false;
#endif #endif
@@ -2045,12 +1981,6 @@ static void bes2600_sdio_remove(struct sdio_func *func)
if (self->single_gathered_buffer) { if (self->single_gathered_buffer) {
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN)); free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
} }
#endif
#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
if (self->tx_bounce) {
free_pages((unsigned long)self->tx_bounce,
get_order(MAX_SDIO_TRANSFER_LEN));
}
#endif #endif
kfree(self); kfree(self);
} }
+664 -57
View File
@@ -43,6 +43,12 @@ enum bus_probe_state {
}; };
struct bes_cdev { struct bes_cdev {
struct cdev cdev;
dev_t dev_id;
int major;
int minor;
struct class *class;
struct device *device;
atomic_t num_proc; atomic_t num_proc;
wait_queue_head_t open_wq; wait_queue_head_t open_wq;
spinlock_t status_lock; spinlock_t status_lock;
@@ -63,6 +69,9 @@ struct bes_cdev {
struct delayed_work probe_timeout_work; struct delayed_work probe_timeout_work;
enum bus_probe_state bus_probe; enum bus_probe_state bus_probe;
struct work_struct wifi_force_close_work; struct work_struct wifi_force_close_work;
#ifdef BES2600_WRITE_DPD_TO_FILE
int no_dpd;
#endif
enum pend_read_op read_flag; enum pend_read_op read_flag;
enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */ enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */
u16 wakeup_state; /* for userspace check wakeup reason */ u16 wakeup_state; /* for userspace check wakeup reason */
@@ -82,6 +91,9 @@ struct bes2600_op_map {
static struct bes_cdev bes2600_cdev; static struct bes_cdev bes2600_cdev;
module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644); module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644);
#ifdef BES2600_WRITE_DPD_TO_FILE
module_param_named(no_dpd, bes2600_cdev.no_dpd, int, 0644);
#endif
extern int bes2600_register_net_dev(struct sbus_priv *bus_priv); extern int bes2600_register_net_dev(struct sbus_priv *bus_priv);
extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv); extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv);
@@ -237,18 +249,351 @@ int bes2600_switch_bt(bool on)
return ret; return ret;
} }
static int bes2600_get_cmd_and_ifname(const char *str, char **result)
{
int cmd_len = 0;
int ifname_len = 0;
char *sp = NULL;
char *tmp_ptr = NULL;
char *cmd_ptr = NULL;
/* check if input arguments is valid */
if (!str || strncmp(str, "ifname:", 7) != 0)
return -1;
sp = strchr(str, ' ');
if (strncmp(sp + 1, "cmd:", 4) != 0)
return -1;
/* extract interface name */
ifname_len = sp - str - 7;
tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL);
if (!tmp_ptr) {
return -2;
}
strncpy(tmp_ptr, str+7, ifname_len);
tmp_ptr[ifname_len] = '\0';
result[0] = tmp_ptr;
/* get command length */
cmd_ptr = strstr(str, "cmd:");
cmd_ptr += 4;
sp = strchr(cmd_ptr, ' ');
if (!sp) { /* the command don't have any parameter */
cmd_len = strlen(cmd_ptr);
if (cmd_ptr[cmd_len - 1] == '\n')
--cmd_len;
} else { /* the command have one or more parameter */
cmd_len = sp - cmd_ptr;
}
/* copy command to out buffer */
tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL);
if (!tmp_ptr) {
kfree(result[0]);
result[0] = NULL;
return -3;
}
strncpy(tmp_ptr, cmd_ptr, cmd_len);
tmp_ptr[cmd_len] = '\0';
result[1] = tmp_ptr;
return 0;
}
static void bes2600_recyle_cmd_and_ifname_mem(char **info)
{
if (info[0]) {
kfree(info[0]);
info[0] = NULL;
}
if (info[1]) {
kfree(info[1]);
info[1] = NULL;
}
}
static int bes2600_op_default_handler(const char *str)
{
char *info[2] = {0};
if (bes2600_get_cmd_and_ifname(str, info) == 0) {
bes_devel("cmd(%s) on %s not handled\n", info[1], info[0]);
} else {
bes_err("%s get command fail, the origin string is %s\n", __func__, str);
}
bes2600_recyle_cmd_and_ifname_mem(info);
return 0;
}
static int bes2600_op_wifi_bt_on_off(const char *str)
{
char *info[2] = {0};
int ret = 0;
enum wait_state wait_state;
enum bus_probe_state probe_state;
unsigned long status = 0;
spin_lock(&bes2600_cdev.status_lock);
probe_state = bes2600_cdev.bus_probe;
wait_state = bes2600_cdev.wait_state;
spin_unlock(&bes2600_cdev.status_lock);
/* only work for wifi signal mode */
if (bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL)
return -EFAULT;
/* wait bus probe operation end */
if (probe_state == BES2600_BUS_PROBE_START) {
bes_devel("wait bus probe operation end\n");
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
(bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START),
HZ);
WARN_ON(status <= 0);
}
/* must wait previous operation end in critical section */
if (wait_state != BES2600_BOOT_WAIT_NONE) {
bes_devel("wait previous operation end\n");
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
(bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE),
HZ * 8);
WARN_ON(status <= 0);
}
/* if dpd calibration is doing, modify wifi and bt state directly */
spin_lock(&bes2600_cdev.status_lock);
if (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) {
if (bes2600_get_cmd_and_ifname(str, info) == 0) {
if (strncmp(info[1], "WIFI_ON", 7) == 0) {
bes2600_cdev.wifi_opened = true;
} else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
bes2600_cdev.wifi_opened = false;
} else if (strncmp(info[1], "BT_ON", 5) == 0) {
bes2600_cdev.bt_opened = true;
bes2600_cdev.bton_pending = true;
} else if (strncmp(info[1], "BT_OFF", 6) == 0) {
bes2600_cdev.bt_opened = false;
bes2600_cdev.bton_pending = false;
}
}
bes2600_recyle_cmd_and_ifname_mem(info);
spin_unlock(&bes2600_cdev.status_lock);
/* wait probe done event */
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
bes2600_bootup_end(), HZ * 8);
WARN_ON(status <= 0);
return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0;
}
spin_unlock(&bes2600_cdev.status_lock);
/* process wifi/bt on/off operation */
if (bes2600_get_cmd_and_ifname(str, info) == 0) {
if (strncmp(info[1], "WIFI_ON", 7) == 0) {
ret = bes2600_switch_wifi(1);
} else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
ret = bes2600_switch_wifi(0);
} else if (strncmp(info[1], "BT_ON", 5) == 0) {
ret = bes2600_switch_bt(1);
} else if (strncmp(info[1], "BT_OFF", 6) == 0) {
ret = bes2600_switch_bt(0);
}
}
if (!ret && bes2600_chrdev_check_system_close())
ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
bes2600_cdev.sbus_priv);
bes2600_recyle_cmd_and_ifname_mem(info);
return ret ;
}
static int bes2600_op_change_fw_type(const char *str)
{
int ret = 0;
int temp = 0;
long status = 0;
char *cmd_ptr = NULL;
char fw_type[5] = {0};
bool sys_closed = bes2600_chrdev_check_system_close();
bes_devel("%s is called, arg:%s\n", __func__, str);
if (!bes2600_cdev.sbus_ops->power_switch && !bes2600_cdev.sbus_ops->reboot)
return -EPERM;
/* check if user input is valid */
cmd_ptr = strstr(str, "CHANGE_FW_TYPE ");
if (strlen(str) < 16 || !cmd_ptr) {
bes_err("the format of \"%s\" is error\n", str);
return -EINVAL;
}
/* convert fw_type from string to int */
strncpy(fw_type, cmd_ptr + 14, 4);
fw_type[0] = '+';
ret = kstrtoint(fw_type, 10, &temp);
if (ret < 0) {
bes_err("%s parse error\n", __func__);
return -EINVAL;
}
/* no need to realod firmware if new fw_type is equal to the old */
if (temp == bes2600_cdev.fw_type ) {
bes_devel("fw type is equal\n");
return 0;
}
/* close wifi net device */
if (bes2600_cdev.sbus_priv
&& bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
}
/* update firmware type */
bes2600_cdev.fw_type = temp;
bes2600_chrdev_update_signal_mode();
if (!sys_closed) {
/* close device to call disconnect function */
if (bes2600_cdev.sbus_ops->power_switch)
bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0);
else if (bes2600_cdev.sbus_ops->reboot)
bes2600_cdev.sbus_ops->reboot(bes2600_cdev.sbus_priv);
}
if (bes2600_cdev.sbus_ops->reboot)
bes2600_chrdev_start_bus_probe();
/* wait disconnect event */
status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10);
WARN_ON(status <= 0);
if (bes2600_cdev.dpd_calied
&& bes2600_chrdev_check_system_close()) {
bes_devel("no need to reload firmware\n");
return 0;
}
bes_devel("reload firmware...\n");
/* power on device to call probe function */
if (bes2600_cdev.sbus_ops->power_switch)
bes2600_cdev.sbus_ops->power_switch(NULL, 1);
/* wait probe done event */
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
bes2600_bootup_end(), HZ * 10);
WARN_ON(status <= 0);
ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
return ret;
}
static int bes2600_op_bt_wakeup(const char *str)
{
int ret = 0;
unsigned long status = 0;
spin_lock(&bes2600_cdev.status_lock);
if (!bes2600_cdev.bt_opened) {
spin_unlock(&bes2600_cdev.status_lock);
return -EFAULT;
}
spin_unlock(&bes2600_cdev.status_lock);
/* wait probe done event */
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
bes2600_bootup_end(), HZ * 8);
if (status <= 0 || bes2600_chrdev_is_bus_error())
return -EFAULT;
bes_devel("bes2600 wakeup bt.\n");
ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
return ret;
}
static int bes2600_op_bt_sleep(const char *str)
{
int ret = 0;
unsigned long status = 0;
spin_lock(&bes2600_cdev.status_lock);
if (!bes2600_cdev.bt_opened) {
spin_unlock(&bes2600_cdev.status_lock);
return -EFAULT;
}
spin_unlock(&bes2600_cdev.status_lock);
/* wait probe done event */
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
bes2600_bootup_end(), HZ * 8);
if (status <= 0 || bes2600_chrdev_is_bus_error())
return -EFAULT;
bes_devel("bes2600 allow bt sleep.\n");
ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false);
return ret;
}
static int bes2600_op_set_wakeup_read_flag(const char *str)
{
bes_devel("%s is called, arg:%s\n", __func__, str);
spin_lock(&bes2600_cdev.status_lock);
bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE;
spin_unlock(&bes2600_cdev.status_lock);
return 0;
}
#ifdef FW_DOWNLOAD_UART_DAEMON #ifdef FW_DOWNLOAD_UART_DAEMON
int bes2600_load_uevent(char *env[])
{
return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
}
#endif #endif
static struct bes2600_op_map bes2600_op_map_tab[] ={
/*op op_len handler */
{"P2P_SET_NOA", 11, bes2600_op_default_handler},
{"P2P_SET_PS", 10, bes2600_op_default_handler},
{"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler},
{"LINKSPEED", 9, bes2600_op_default_handler},
{"RSSI", 4, bes2600_op_default_handler},
{"GETBAND", 7, bes2600_op_default_handler},
{"WLS_BATCHING", 12, bes2600_op_default_handler},
{"MACADDR", 7, bes2600_op_default_handler},
{"RXFILTER-START", 14, bes2600_op_default_handler},
{"RXFILTER-STOP", 13, bes2600_op_default_handler},
{"RXFILTER-ADD", 12, bes2600_op_default_handler},
{"RXFILTER-REMOVE", 15, bes2600_op_default_handler},
{"BTCOEXMODE", 10, bes2600_op_default_handler},
{"BTCOEXSCAN-START", 16, bes2600_op_default_handler},
{"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler},
{"SETSUSPENDMODE", 14, bes2600_op_default_handler},
{"COUNTRY", 7, bes2600_op_default_handler},
{"WIFI_ON", 7, bes2600_op_wifi_bt_on_off},
{"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off},
{"BT_ON", 5, bes2600_op_wifi_bt_on_off},
{"BT_OFF", 6, bes2600_op_wifi_bt_on_off},
{"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type},
{"BT_WAKEUP", 9, bes2600_op_bt_wakeup},
{"BT_SLEEP", 8, bes2600_op_bt_sleep},
{"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag},
};
static int bes2600_chrdev_check_system_close_internal(void) static int bes2600_chrdev_check_system_close_internal(void)
{ {
@@ -258,13 +603,255 @@ static int bes2600_chrdev_check_system_close_internal(void)
&& (bes2600_cdev.wifi_opened == false); && (bes2600_cdev.wifi_opened == false);
} }
static int bes2600_chrdev_open(struct inode *inode, struct file *filp)
{
if (atomic_read(&bes2600_cdev.num_proc) > 0) {
wait_event_timeout(bes2600_cdev.open_wq,
(atomic_read(&bes2600_cdev.num_proc) == 0),
MAX_SCHEDULE_TIMEOUT);
}
bes_devel("bes2600 char device is opened\n");
atomic_inc(&bes2600_cdev.num_proc);
return 0;
}
static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[64] = {0};
unsigned int len;
long status = 0;
switch (bes2600_cdev.read_flag) {
case BES_CDEV_READ_WAKEUP_STATE:
if (bes2600_chrdev_wakeup_by_event_get() > WAKEUP_EVENT_NONE) {
status = wait_event_timeout(bes2600_cdev.wakeup_reason_wq,
bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_NONE, HZ * 2);
WARN_ON(status <= 0);
}
len = sprintf(buf, "wakeup_reason: %u, src_port: %u\n",
bes2600_cdev.wakeup_state, bes2600_cdev.src_port);
break;
default:
len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n",
bes2600_cdev.dpd_calied,
bes2600_cdev.wifi_opened,
bes2600_cdev.bt_opened,
bes2600_cdev.fw_type);
break;
}
len = sizeof(buf);
/* reset read flag */
spin_lock(&bes2600_cdev.status_lock);
bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
spin_unlock(&bes2600_cdev.status_lock);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t bes2600_chrdev_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
int i = 0;
int cmd_num = ARRAY_SIZE(bes2600_op_map_tab);
int cmd_len = 0;
int ret = 0;
char *info[2] = {0};
char *buf = NULL;
/* copy content from user space to kernel */
/* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */
buf = kmalloc(count + 1, GFP_KERNEL);
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
/* add terminal character */
buf[count] = '\0';
/* extract comand and interface */
if (bes2600_get_cmd_and_ifname(buf, info) != 0) {
bes_err("%s get command fail, the origin string is %s\n", __func__, buf);
kfree(buf);
return -EINVAL;
}
/* match operation item and execure its handler */
cmd_len = strlen(info[1]);
for (i = 0; i < cmd_num; i++) {
if (cmd_len < bes2600_op_map_tab[i].op_len)
continue;
if (strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) {
ret = bes2600_op_map_tab[i].handler(buf);
break;
}
}
/* operation item mismatch */
if (i == cmd_num) {
bes_err("cmd(%s) mismatch\n", info[1]);
}
bes2600_recyle_cmd_and_ifname_mem(info);
kfree(buf);
return (ret == 0) ? count : ret;
}
static int bes2600_chrdev_release (struct inode *inode, struct file *file)
{
if (atomic_dec_and_test(&bes2600_cdev.num_proc)) {
wake_up(&bes2600_cdev.open_wq);
}
bes_devel("bes2600 char device is closed\n");
return 0;
}
static struct file_operations bes2600_chardev_fops =
{
.owner = THIS_MODULE,
.open = bes2600_chrdev_open,
.read = bes2600_chrdev_read,
.write = bes2600_chrdev_write,
.release = bes2600_chrdev_release,
};
#ifdef BES2600_WRITE_DPD_TO_FILE
static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
{
int ret = 0;
struct file *fp;
if (buffer == NULL || size == 0)
return 0;
fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
if (IS_ERR(fp)) {
bes_err("BES2600 : can't open %s\n",path);
return -1;
}
ret = kernel_write(fp, buffer, size, &fp->f_pos);
if (ret < 0)
bes_err("write dpd to file failed\n");
filp_close(fp,NULL);
bes_devel("write dpd to %s\n", path);
return ret;
}
static bool bes2600_chrdev_dpd_is_vaild(u8 *dpd_data)
{
u32 cal_crc = 0;
u32 dpd_crc = le32_to_cpup((__le32 *)(dpd_data));
u32 dpd_ver = le32_to_cpup((__le32 *)(dpd_data + DPD_VERSION_OFFSET));
/* check version */
if (dpd_ver < DPD_CUR_VERSION)
return false;
cal_crc ^= 0xffffffffL;
cal_crc = crc32_le(cal_crc, dpd_data + 4, DPD_BIN_SIZE - 4);
cal_crc ^= 0xffffffffL;
/* check if the dpd data is valid */
if (cal_crc != dpd_crc) {
bes_err(
"bes2600 dpd data from file check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n",
cal_crc, dpd_crc);
return false;
}
return true;
}
static int bes2600_chrdev_read_and_check_dpd_data(const char *file, u8 **data, u32 *len)
{
int ret = 0;
u8* read_data = NULL;
struct file *fp;
/* open file */
fp = filp_open(file, O_RDONLY, 0);//S_IRUSR
if (IS_ERR(fp)) {
bes_devel("BES2600 : can't open %s\n",file);
return -1;
}
#ifdef BES2600_WRITE_DPD_TO_FILE
if (fp->f_inode->i_size != DPD_BIN_FILE_SIZE) {
bes_err(
"bes2600 dpd data file size check failed, read_size: %lld file_size: %d\n",
fp->f_inode->i_size, DPD_BIN_FILE_SIZE);
filp_close(fp, NULL);
return -1;
}
#endif
/* allocate memory for storing reading data */
read_data = kmalloc(fp->f_inode->i_size, GFP_KERNEL);
if (read_data == NULL) {
bes_devel("%s alloc mem fail\n", __func__);
goto err1;
}
/* read data from file */
ret = kernel_read(fp, read_data, fp->f_inode->i_size, &fp->f_pos);
if (ret < DPD_BIN_SIZE) {
bes_err("%s read fail, ret=%d\n", __func__, ret);
goto err2;
}
/* check dpd version and crc */
if (!bes2600_chrdev_dpd_is_vaild(read_data))
goto err2;
/* close file */
filp_close(fp, NULL);
/* copy data to external */
*data = read_data;
*len = DPD_BIN_SIZE;;
/* output debug information */
bes_devel("read dpd data from %s\n", file);
return 0;
err2:
kfree(read_data);
err1:
filp_close(fp, NULL);
*data = NULL;
*len = 0;
return -1;
}
#endif
const u8* bes2600_chrdev_get_dpd_data(u32 *len) const u8* bes2600_chrdev_get_dpd_data(u32 *len)
{ {
#ifdef BES2600_WRITE_DPD_TO_FILE
if (!bes2600_cdev.dpd_calied && bes2600_cdev.no_dpd) {
/* read dpd data from file that stores factory dpd calibration data */
if ((bes2600_chrdev_read_and_check_dpd_data(BES2600_DPD_GOLDEN_PATH,
&bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0) &&
(bes2600_chrdev_read_and_check_dpd_data(BES2600_DEFAULT_DPD_PATH,
&bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0)) {
bes_err("%s read dpd data fail\n", __func__);
return NULL;
} else {
bes2600_cdev.dpd_calied = true;
}
}
#endif
if (!bes2600_cdev.dpd_calied) if (!bes2600_cdev.dpd_calied)
return NULL; return NULL;
if (len) if (len)
@@ -325,6 +912,14 @@ int bes2600_chrdev_update_dpd_data(void)
} }
spin_unlock(&bes2600_cdev.status_lock); spin_unlock(&bes2600_cdev.status_lock);
#ifdef BES2600_WRITE_DPD_TO_FILE
/* write dpd data to file */
memset(bes2600_cdev.dpd_data + DPD_BIN_SIZE, 0, DPD_BIN_FILE_SIZE - DPD_BIN_SIZE);
bes2600_chrdev_write_dpd_data_to_file(BES2600_DPD_PATH,
bes2600_cdev.dpd_data, DPD_BIN_FILE_SIZE);
#endif
return 0; return 0;
} }
@@ -442,48 +1037,6 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
return ret; return ret;
} }
/*
* Hard-reset the bus and wait for the bus core to remove the chip.
*
* Used by the firmware-wedge recovery path on platforms where the normal
* power_switch(0) sequence has no effective chip-reset signal. The bus
* implementation triggers an asynchronous re-detect; this helper waits for
* the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a
* subsequent bes2600_switch_wifi(true) sees a clean state and can wait on
* the fresh probe.
*/
int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
{
int ret;
long status;
if (!sbus_ops || !priv)
return -EINVAL;
if (!sbus_ops->bus_reset)
return -EOPNOTSUPP;
bes_info("trigger bus reset to recover wedged firmware.\n");
ret = sbus_ops->bus_reset(priv);
if (ret) {
bes_err("bus_reset failed: %d\n", ret);
return ret;
}
/*
* The bus reset is asynchronous: the bus core schedules a rescan
* which removes the bound function drivers and then re-detects the
* chip. Wait for the remove callback to clear sbus_priv. Do not
* dereference 'priv' after this point -- it may already be freed.
*/
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
!bes2600_cdev.sbus_priv, HZ * 3);
WARN_ON(status <= 0);
return 0;
}
bool bes2600_chrdev_is_wifi_opened(void) bool bes2600_chrdev_is_wifi_opened(void)
{ {
bool wifi_opened = false; bool wifi_opened = false;
@@ -571,6 +1124,12 @@ void bes2600_chrdev_update_signal_mode(void)
static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work) static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
{ {
char wifi_state[15];
char bt_state[15];
char fw_type[15];
char *env[] = { wifi_state, bt_state, fw_type, NULL };
int ret;
if (bes2600_chrdev_is_wifi_opened()) { if (bes2600_chrdev_is_wifi_opened()) {
bes_devel("system exeception, force wifi down\n"); bes_devel("system exeception, force wifi down\n");
@@ -582,24 +1141,19 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
/* unregister wifi */ /* unregister wifi */
bes2600_switch_wifi(0); bes2600_switch_wifi(0);
/* /* power down device if wifi is only opened */
* Hard exception with a bus_reset implementation: tear the if (bes2600_chrdev_check_system_close()) {
* bus down via mmc_hw_reset() (or equivalent) so the next
* bringup probes a freshly reset chip. On PineTab2 this is
* the only effective recovery path -- the existing
* power_switch(0)/(1) sequence has no chip-reset signal of
* its own (sdio_pwrseq owns wifi_reset).
*
* Soft close, or hard close on a board without bus_reset:
* fall back to the legacy power_switch(0) sequence.
*/
if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->bus_reset) {
bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
bes2600_cdev.sbus_priv);
} else if (bes2600_chrdev_check_system_close()) {
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops, bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
bes2600_cdev.sbus_priv); bes2600_cdev.sbus_priv);
} }
/* notify userspace */
snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened);
snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened);
snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type);
ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
if (!ret)
bes_err("bes2600 notify userspace failed\n");
} }
} }
@@ -693,6 +1247,46 @@ int bes2600_chrdev_wakeup_by_event_get(void)
int bes2600_chrdev_init(struct sbus_ops *ops) int bes2600_chrdev_init(struct sbus_ops *ops)
{ {
int ret = 0;
/* allocate devide id */
ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev");
if (ret < 0){
bes_err("bes2600 alloc device id fail\n");
ret = -EFAULT;
goto fail;
}
/* extract major and minor device id */
bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id);
bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id);
/* add char device and bind operation function */
bes2600_cdev.cdev.owner = THIS_MODULE;
cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops);
ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1);
if (ret < 0){
bes_err("bes2600 char device add fail\n");
ret = -EFAULT;
goto fail1;
}
/* create class for creating device node */
bes2600_cdev.class = class_create("bes2600_chrdev");
if (IS_ERR(bes2600_cdev.class)){
bes_err("bes2600 char device add fail\n");
ret = -EFAULT;
goto fail2;
}
/* get char device pointer */
bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600");
if (IS_ERR(bes2600_cdev.device)){
bes_err("bes2600 char device create fail\n");
ret = -EFAULT;
goto fail3;
}
/* initialise global variable */ /* initialise global variable */
atomic_set(&bes2600_cdev.num_proc, 0); atomic_set(&bes2600_cdev.num_proc, 0);
init_waitqueue_head(&bes2600_cdev.open_wq); init_waitqueue_head(&bes2600_cdev.open_wq);
@@ -724,6 +1318,15 @@ int bes2600_chrdev_init(struct sbus_ops *ops)
bes_devel("%s done\n", __func__); bes_devel("%s done\n", __func__);
return 0; return 0;
fail3:
class_destroy(bes2600_cdev.class);
fail2:
cdev_del(&bes2600_cdev.cdev);
fail1:
unregister_chrdev_region(bes2600_cdev.dev_id, 1);
fail:
return ret;
} }
void bes2600_chrdev_free(void) void bes2600_chrdev_free(void)
@@ -733,5 +1336,9 @@ void bes2600_chrdev_free(void)
bes2600_free_dpd_log_buffer(); bes2600_free_dpd_log_buffer();
#endif #endif
bes2600_chrdev_free_dpd_data(); bes2600_chrdev_free_dpd_data();
cdev_del(&bes2600_cdev.cdev);
unregister_chrdev_region(bes2600_cdev.dev_id, 1);
device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id);
class_destroy(bes2600_cdev.class);
bes_devel("%s done\n", __func__); bes_devel("%s done\n", __func__);
} }
-1
View File
@@ -60,7 +60,6 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
/* used to control device power down */ /* used to control device power down */
int bes2600_chrdev_check_system_close(void); int bes2600_chrdev_check_system_close(void);
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv); int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
void bes2600_chrdev_wakeup_bt(void); void bes2600_chrdev_wakeup_bt(void);
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev); void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv); void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv);
+34
View File
@@ -125,6 +125,8 @@ int bes_host_slave_sync(struct bes2600_common *hw_priv)
} }
*/ */
//#define DATA_DUMP_OBSERVE
static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val) static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val)
{ {
u8 frame_num = 0; u8 frame_num = 0;
@@ -466,6 +468,14 @@ static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_n
const struct firmware *fw_bin; const struct firmware *fw_bin;
#ifdef DATA_DUMP_OBSERVE
char *observe;
size_t observe_len;
loff_t observe_off = 0;
mm_segment_t old_fs;
struct file *observe_file = NULL;
#endif
struct fw_msg_hdr_t header; struct fw_msg_hdr_t header;
struct fw_info_t fw_info; struct fw_info_t fw_info;
struct download_fw_t download_addr; struct download_fw_t download_addr;
@@ -573,6 +583,14 @@ retry:
} }
download_addr.addr = fw_info.addr; download_addr.addr = fw_info.addr;
#ifdef DATA_DUMP_OBSERVE
observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0);
if (IS_ERR(observe_file)) {
bes_err("create data_dump file err:%ld\n", IS_ERR(observe_file));
observe_file = NULL;
}
#endif
while (code_length) { while (code_length) {
#if 1 #if 1
@@ -622,6 +640,17 @@ retry:
//mdelay(5000); //mdelay(5000);
bes_devel("tx_download_firmware_data:%x %d\n", download_addr.addr, length); bes_devel("tx_download_firmware_data:%x %d\n", download_addr.addr, length);
#ifdef DATA_DUMP_OBSERVE
if (observe_file) {
observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t);
old_fs = get_fs();
set_fs(KERNEL_DS);
vfs_write(observe_file, observe, observe_len, &observe_off);
set_fs(old_fs);
}
#endif
ret = bes2600_data_write(long_buf, length > 512 ? length : 512); ret = bes2600_data_write(long_buf, length > 512 ? length : 512);
if (ret) { if (ret) {
bes_err("tx download fw data err:%d\n", ret); bes_err("tx download fw data err:%d\n", ret);
@@ -803,6 +832,11 @@ retry:
err2: err2:
kfree(long_buf); kfree(long_buf);
#ifdef DATA_DUMP_OBSERVE
if (observe_file) {
filp_close(observe_file, NULL);
}
#endif
err1: err1:
kfree(short_buf); kfree(short_buf);
release_firmware(fw_bin); release_firmware(fw_bin);
+3 -17
View File
@@ -472,7 +472,6 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
int i = 0; int i = 0;
struct bes2600_vif *priv; struct bes2600_vif *priv;
int ret = 0; int ret = 0;
int timeouts = 0;
char ip_str[20]; char ip_str[20];
unsigned long status = 0; unsigned long status = 0;
@@ -529,35 +528,22 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
if (ret) { if (ret) {
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
bes_err("%s, set operation mode fail\n", __func__); bes_err("%s, set operation mode fail\n", __func__);
timeouts++;
continue;
} }
/* wait power save mode changed indication */ /* wait power save mode changed indication */
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ); status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl); reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
if (!status) { if (!status)
bes_devel("%s, wait pm ind timeout\n", __func__); bes_err("%s, wait pm ind timeout\n", __func__);
timeouts++;
}
} else { } else {
bes_devel("skip enter lp mode\n"); bes_devel("skip enter lp mode\n");
} }
} }
} }
/* /* set device low power configuration */
* Enter the device-end of the LP transition only if every per-VIF
* mac80211 handshake reached firmware-ACKed completion. Doing the
* device-LP setup while any VIF is still pending leaves the driver
* in an inconsistent state that cascades into SDIO TX errors on
* the BES2600.
*/
if (timeouts == 0)
bes2600_pwr_device_enter_lp_mode(hw_priv); bes2600_pwr_device_enter_lp_mode(hw_priv);
else
ret = -ETIMEDOUT;
return ret; return ret;
} }
+35
View File
@@ -790,6 +790,41 @@ void bes2600_core_release(struct bes2600_common *self)
return; 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 bes2600_wifi_start(struct bes2600_common *hw_priv)
{ {
int ret = 0, if_id; int ret = 0, if_id;
-8
View File
@@ -75,14 +75,6 @@ struct sbus_ops {
void (*halt_device)(struct sbus_priv *self); void (*halt_device)(struct sbus_priv *self);
bool (*wakeup_source)(struct sbus_priv *self); bool (*wakeup_source)(struct sbus_priv *self);
int (*reboot)(struct sbus_priv *self); int (*reboot)(struct sbus_priv *self);
/*
* Force the host bus to re-detect and re-probe the chip. Called
* from the firmware-wedge recovery path when power_switch() has no
* effective chip-reset signal of its own (e.g. PineTab2, where the
* wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 node).
* Returns 0 on success or a negative errno.
*/
int (*bus_reset)(struct sbus_priv *self);
}; };
void bes2600_irq_handler(struct bes2600_common *priv); void bes2600_irq_handler(struct bes2600_common *priv);
+1 -59
View File
@@ -14,50 +14,11 @@
#include "scan.h" #include "scan.h"
#include "sta.h" #include "sta.h"
#include "pm.h" #include "pm.h"
#include "epta_coex.h"
#include "epta_request.h" #include "epta_request.h"
#include "bes_pwr.h" #include "bes_pwr.h"
/*
* After this many consecutive WSM scan rejections from firmware, stop
* issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
* that's rejecting them (coex window, firmware-internal busy) clear.
*/
#define BES2600_SCAN_REJECT_THRESHOLD 3
#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv); static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
/*
* Decide whether to skip sending the next WSM scan command without
* bothering the firmware. Two triggers:
*
* 1. BT A2DP is streaming in non-FDD coex mode. The firmware is
* known to reject scan requests during that window; short-
* circuiting here saves a WSM round-trip and avoids the
* wsm_generic_confirm / scan_work warning cascade that follows.
*
* 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
* rejections on recent scan attempts and the backoff window has
* not yet elapsed. Whatever was rejecting them is likely still
* rejecting them; give it time.
*
* Returns true if the caller should abandon the scan iteration.
*/
static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
{
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
if (!coex_is_fdd_mode() && coex_is_bt_a2dp())
return true;
#endif
if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
time_before(jiffies, hw_priv->scan.backoff_until))
return true;
return false;
}
#ifdef CONFIG_BES2600_TESTMODE #ifdef CONFIG_BES2600_TESTMODE
static int bes2600_advance_scan_start(struct bes2600_common *hw_priv) static int bes2600_advance_scan_start(struct bes2600_common *hw_priv)
{ {
@@ -741,29 +702,10 @@ void bes2600_scan_work(struct work_struct *work)
wsm_unlock_tx(hw_priv); wsm_unlock_tx(hw_priv);
} else } else
#endif #endif
{
if (bes2600_scan_should_defer(hw_priv)) {
hw_priv->scan.status = -EBUSY;
hw_priv->scan.reject_count++;
hw_priv->scan.backoff_until =
jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
wiphy_dbg(priv->hw->wiphy,
"[SCAN] deferred (coex/backoff, reject_count=%u)\n",
hw_priv->scan.reject_count);
kfree(scan.ch);
goto fail;
}
hw_priv->scan.status = bes2600_scan_start(priv, &scan); hw_priv->scan.status = bes2600_scan_start(priv, &scan);
}
kfree(scan.ch); kfree(scan.ch);
if (hw_priv->scan.status) { if (WARN_ON(hw_priv->scan.status))
hw_priv->scan.reject_count++;
hw_priv->scan.backoff_until =
jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
/* Lower callers already logged the reason at wiphy_warn. */
goto fail; goto fail;
}
hw_priv->scan.reject_count = 0;
hw_priv->scan.curr = it; hw_priv->scan.curr = it;
} }
up(&hw_priv->conf_lock); up(&hw_priv->conf_lock);
-11
View File
@@ -42,17 +42,6 @@ struct bes2600_scan {
struct delayed_work probe_work; struct delayed_work probe_work;
int direct_probe; int direct_probe;
u8 if_id; u8 if_id;
/*
* Track consecutive firmware-side WSM scan rejections so we can
* back off briefly instead of re-issuing the same scan on every
* mac80211 background-scan tick. Firmware returns WSM status != 0
* for a handful of transient conditions (BT A2DP active in non-
* FDD coex, firmware-internal busy windows) and keeps rejecting
* until the state clears; retrying at full cadence just floods
* dmesg.
*/
unsigned int reject_count;
unsigned long backoff_until;
}; };
int bes2600_hw_scan(struct ieee80211_hw *hw, int bes2600_hw_scan(struct ieee80211_hw *hw,
+1 -13
View File
@@ -134,20 +134,8 @@ static int wsm_generic_confirm(struct bes2600_common *hw_priv,
struct wsm_buf *buf) struct wsm_buf *buf)
{ {
u32 status = WSM_GET32(buf); u32 status = WSM_GET32(buf);
if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status))
/*
* A non-SUCCESS status here is a firmware-side policy decision for
* the command whose confirm this is -- commonly WSM status 2 for
* scan (0x0407) rejected because of a coex window or transient
* firmware-busy state. It is not a driver/kernel bug, so avoid the
* WARN()/stack-trace treatment; the caller already emits a
* wiphy_warn identifying the request id and will propagate the
* error to mac80211.
*/
if (status != WSM_STATUS_SUCCESS) {
bes_devel("%s ret %u\n", __func__, status);
return -EINVAL; return -EINVAL;
}
return 0; return 0;
underflow: underflow:
+2
View File
@@ -2236,5 +2236,7 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv);
int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status); int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status);
#if defined(STANDARD_FACTORY_EFUSE_FLAG)
int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type); int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type);
#endif
#endif /* BES2600_HWIO_H_INCLUDED */ #endif /* BES2600_HWIO_H_INCLUDED */
+2 -1
View File
@@ -18,7 +18,8 @@ License: LGPL-2.1
License for more details. License for more details.
. .
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with this library; if not, see <https://www.gnu.org/licenses/>. 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 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". version 2.1 can be found in the file "/usr/share/common-licenses/LGPL-2.1".