Compare commits

..

1 Commits

Author SHA1 Message Date
test0r 4ec7d25817 bes2600: bounce SDIO TX buffers to avoid DMA OOB read
The SDIO TX path rounds the DMA transfer length up to the host's
current block size and hands that length to dma_map_sg() via
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
tx_buffer->buf typically aliases into an skb linear head whose
allocated size matches tx_buffer->len, not the block-aligned
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
to one block past the end of the skb. On a PineTab2 with KFENCE
enabled this fires as:

  BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
  Out-of-bounds read at ... (704B right of kfence-#...):
  __pi_memcpy_generic
  swiotlb_tbl_map_single
  swiotlb_map
  dma_direct_map_sg
  __dma_map_sg_attrs
  dma_map_sg_attrs
  dw_mci_pre_dma_transfer
  __dw_mci_start_request
  ...
  bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
  sdio_tx_work+0x2b4/0x4a0 [bes2600]

allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*

In addition to being undefined behavior, the padding bytes (which
come from whatever memory follows the skb) are transmitted to the
peer, leaking kernel memory on the air.

Allocate a driver-owned DMA-page bounce buffer sized to
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
via single_gathered_buffer; a separate allocation is used for the
TX path because single_gathered_buffer is only serialised via
sdio_claim_host and sdio_tx_work accumulates scatter entries before
claiming the bus.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-23 11:58:31 +02:00
17 changed files with 826 additions and 276 deletions
+15 -3
View File
@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
# feature option
BES2600 ?= m
CONFIG_BES2600_TESTMODE ?= y
CONFIG_BES2600_TESTMODE ?= n
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
@@ -28,6 +28,7 @@ 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
@@ -64,8 +65,8 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
FACTORY_CRC_CHECK ?= n
STANDARD_FACTORY_EFUSE_FLAG ?= n
FACTORY_PATH ?= bes2600/bes2600_factory.txt
STANDARD_FACTORY_EFUSE_FLAG ?= y
FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
endif
# basic function
@@ -92,6 +93,12 @@ 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
@@ -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_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)
@@ -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,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)
+65 -43
View File
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/crc32.h>
@@ -31,18 +30,6 @@
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.
* 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)
{
const struct firmware *fw;
int ret;
int ret = 0;
struct file *fp;
if (!path || !buffer) {
bes_err("%s NULL pointer err\n", __func__);
return -1;
}
bes_devel("requesting firmware-class %s\n", path);
bes_devel("reading %s \n", path);
ret = request_firmware(&fw, path, bes2600_factory_dev);
if (ret) {
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
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 (fw->size == 0 || fw->size > FACTORY_MAX_SIZE) {
bes_err("bes2600_factory.txt size check failed, read_size: %zu max_size: %d\n",
fw->size, FACTORY_MAX_SIZE);
release_firmware(fw);
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;
}
memcpy(buffer, fw->data, fw->size);
ret = (int)fw->size;
release_firmware(fw);
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;
}
@@ -870,22 +891,9 @@ static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
#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)
{
int ret = 0;
int w_size;
u32 crc_len = sizeof(factory_data_t);
#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__);
if (!file_buffer)
if (!file_buffer) {
return -ENOMEM;
}
if (!factory_save_p)
if (!factory_save_p) {
return -ENOENT;
}
/* All initialized to space */
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);
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;
}
#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;
}
-3
View File
@@ -199,9 +199,6 @@ enum factory_cali_status {
/* just calibrate 11n, other protocols are automatically mapped */
#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*/
u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path);
void factory_little_endian_cvrt(u8 *data);
-33
View File
@@ -16,7 +16,6 @@
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
#include <linux/mmc/sdio.h>
#include <linux/spinlock.h>
#include <net/mac80211.h>
@@ -31,7 +30,6 @@
#include "bes2600.h"
#include "sbus.h"
#include "bes2600_plat.h"
#include "bes2600_factory.h"
#include "hwio.h"
#include "bes_chardev.h"
#include "bes_log.h"
@@ -1778,33 +1776,6 @@ static void bes2600_sdio_halt_device(struct sbus_priv *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)
{
struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
@@ -1843,7 +1814,6 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
.gpio_sleep = bes2600_gpio_allow_mcu_sleep,
.halt_device = bes2600_sdio_halt_device,
.wakeup_source = bes2600_sdio_wakeup_source,
.bus_reset = bes2600_sdio_bus_reset,
};
static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
@@ -1884,9 +1854,6 @@ static int bes2600_sdio_probe(struct sdio_func *func,
if (ret)
goto err;
/* wire struct device into factory.c for request_firmware() context */
bes2600_factory_set_dev(dev);
self->pdata = bes2600_get_platform_data();
self->func = func;
self->dev = &func->dev;
+664 -57
View File
@@ -43,6 +43,12 @@ enum bus_probe_state {
};
struct bes_cdev {
struct cdev cdev;
dev_t dev_id;
int major;
int minor;
struct class *class;
struct device *device;
atomic_t num_proc;
wait_queue_head_t open_wq;
spinlock_t status_lock;
@@ -63,6 +69,9 @@ struct bes_cdev {
struct delayed_work probe_timeout_work;
enum bus_probe_state bus_probe;
struct work_struct wifi_force_close_work;
#ifdef BES2600_WRITE_DPD_TO_FILE
int no_dpd;
#endif
enum pend_read_op read_flag;
enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */
u16 wakeup_state; /* for userspace check wakeup reason */
@@ -82,6 +91,9 @@ struct bes2600_op_map {
static struct bes_cdev bes2600_cdev;
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_unregister_net_dev(struct sbus_priv *bus_priv);
@@ -237,18 +249,351 @@ int bes2600_switch_bt(bool on)
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
int bes2600_load_uevent(char *env[])
{
return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
}
#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)
{
@@ -258,13 +603,255 @@ static int bes2600_chrdev_check_system_close_internal(void)
&& (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)
{
#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)
return NULL;
if (len)
@@ -325,6 +912,14 @@ int bes2600_chrdev_update_dpd_data(void)
}
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;
}
@@ -442,48 +1037,6 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
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 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)
{
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()) {
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 */
bes2600_switch_wifi(0);
/*
* Hard exception with a bus_reset implementation: tear the
* 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()) {
/* power down device if wifi is only opened */
if (bes2600_chrdev_check_system_close()) {
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
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 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 */
atomic_set(&bes2600_cdev.num_proc, 0);
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__);
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)
@@ -733,5 +1336,9 @@ void bes2600_chrdev_free(void)
bes2600_free_dpd_log_buffer();
#endif
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__);
}
-1
View File
@@ -60,7 +60,6 @@ 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);
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_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
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)
{
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;
#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_info_t fw_info;
struct download_fw_t download_addr;
@@ -573,6 +583,14 @@ retry:
}
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) {
#if 1
@@ -622,6 +640,17 @@ retry:
//mdelay(5000);
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);
if (ret) {
bes_err("tx download fw data err:%d\n", ret);
@@ -803,6 +832,11 @@ retry:
err2:
kfree(long_buf);
#ifdef DATA_DUMP_OBSERVE
if (observe_file) {
filp_close(observe_file, NULL);
}
#endif
err1:
kfree(short_buf);
release_firmware(fw_bin);
-23
View File
@@ -8,26 +8,3 @@ extern struct device *global_dev;
#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__)
/*
* Legacy debug-subsystem-tagged log macros. The per-subsystem filtering
* was never implemented in-tree; these shims let code paths gated by
* CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC
* build when their conditions are enabled. The first argument is
* currently unused; pick one of the BES2600_DBG_* constants below for
* documentation.
*/
#define BES2600_DBG_SBUS 0
#define BES2600_DBG_DOWNLOAD 0
#define BES2600_DBG_ITP 0
#define BES2600_DBG_TEST_MODE 0
#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__)
#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__)
#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__)
#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__)
#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \
do { \
if (_cond) \
bes_err(fmt, ##__VA_ARGS__); \
} while (0)
+4 -18
View File
@@ -472,7 +472,6 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
int i = 0;
struct bes2600_vif *priv;
int ret = 0;
int timeouts = 0;
char ip_str[20];
unsigned long status = 0;
@@ -529,35 +528,22 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
if (ret) {
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
bes_err("%s, set operation mode fail\n", __func__);
timeouts++;
continue;
}
/* wait power save mode changed indication */
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);
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
if (!status) {
bes_devel("%s, wait pm ind timeout\n", __func__);
timeouts++;
}
if (!status)
bes_err("%s, wait pm ind timeout\n", __func__);
} else {
bes_devel("skip enter lp mode\n");
}
}
}
/*
* 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);
else
ret = -ETIMEDOUT;
/* set device low power configuration */
bes2600_pwr_device_enter_lp_mode(hw_priv);
return ret;
}
+35
View File
@@ -790,6 +790,41 @@ void bes2600_core_release(struct bes2600_common *self)
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;
-8
View File
@@ -75,14 +75,6 @@ struct sbus_ops {
void (*halt_device)(struct sbus_priv *self);
bool (*wakeup_source)(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);
+1 -59
View File
@@ -14,50 +14,11 @@
#include "scan.h"
#include "sta.h"
#include "pm.h"
#include "epta_coex.h"
#include "epta_request.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);
/*
* 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
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);
} else
#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);
}
kfree(scan.ch);
if (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. */
if (WARN_ON(hw_priv->scan.status))
goto fail;
}
hw_priv->scan.reject_count = 0;
hw_priv->scan.curr = it;
}
up(&hw_priv->conf_lock);
-11
View File
@@ -42,17 +42,6 @@ struct bes2600_scan {
struct delayed_work probe_work;
int direct_probe;
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,
+3 -3
View File
@@ -3633,7 +3633,7 @@ static int bes2600_set_power_save(struct ieee80211_hw *hw,
*
* Returns: 0 on success or non zero value on failure
*/
static int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
{
struct bes_msg_start_stop_tsm *start_stop_tsm =
(struct bes_msg_start_stop_tsm *) data;
@@ -3663,7 +3663,7 @@ static int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
*
* Returns: TSM parameters collected
*/
static int bes2600_get_tsm_params(struct ieee80211_hw *hw)
int bes2600_get_tsm_params(struct ieee80211_hw *hw)
{
struct bes2600_common *hw_priv = hw->priv;
struct bes_tsm_stats tsm_stats;
@@ -3703,7 +3703,7 @@ static int bes2600_get_tsm_params(struct ieee80211_hw *hw)
*
* Returns: Returns the last measured roam delay
*/
static int bes2600_get_roam_delay(struct ieee80211_hw *hw)
int bes2600_get_roam_delay(struct ieee80211_hw *hw)
{
struct bes2600_common *hw_priv = hw->priv;
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
+1 -13
View File
@@ -134,20 +134,8 @@ static int wsm_generic_confirm(struct bes2600_common *hw_priv,
struct wsm_buf *buf)
{
u32 status = WSM_GET32(buf);
/*
* 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);
if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status))
return -EINVAL;
}
return 0;
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);
#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);
#endif
#endif /* BES2600_HWIO_H_INCLUDED */
+2 -1
View File
@@ -18,7 +18,8 @@ License: LGPL-2.1
License for more details.
.
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
version 2.1 can be found in the file "/usr/share/common-licenses/LGPL-2.1".