Compare commits

..

8 Commits

Author SHA1 Message Date
test0r 44e085360f bes2600: drop orphan DATA_DUMP_OBSERVE and access_file() file I/O
Two dead-in-default-build file-I/O sites remain in the driver
after the factory and chardev kernel_*() removals in the preceding
patches:

  - bes_fw.c DATA_DUMP_OBSERVE: four #ifdef DATA_DUMP_OBSERVE
    blocks built around the firmware-download path that open
    /lib/firmware/bes2002_fw_write.bin via filp_open(O_CREAT |
    O_RDWR), then log every transmitted firmware chunk via
    vfs_write() inside a get_fs()/set_fs(KERNEL_DS) wrapper. The
    controlling #define at bes_fw.c line 128 is commented out
    ('//#define DATA_DUMP_OBSERVE'), so none of this is ever
    compiled in a stock build.

  - main.c access_file(): a helper gated on
    GET_MAC_ADDR_METHOD == 2 || == 3 (default 4) using the same
    get_fs()/set_fs()/vfs_read()/vfs_write() pattern. No caller
    in the tree references it -- it was orphaned when the methods
    that consumed it were refactored out.

Both sites are unbuildable on modern kernels anyway: get_fs() /
set_fs() were removed from arm64 and the generic uaccess path in
the v5.10 era, and the legacy vfs_read() / vfs_write() variants
that took userspace-typed buffers went with them. The in-kernel
replacements would be kernel_read() / kernel_write(), which this
series is explicitly removing from the driver.

Remove both blocks, the commented-out '//#define DATA_DUMP_OBSERVE'
line, and the access_file() definition and its #if gate. No
behaviour change in any default or non-default build, because
nothing compiled or linked in the first place. After this patch
the driver contains zero filp_open / kernel_read / kernel_write /
vfs_read / vfs_write references -- a precondition for a
drivers/staging/bes2600/ linux-wireless RFC.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-23 20:19:55 +02:00
test0r 10a05d21bf 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 12:35:13 +02:00
test0r 6f13e008d2 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 14:15:46 +02:00
test0r 3304b13a2b bes2600: remove userspace /dev/bes2600 character device interface
bes_chardev.c implemented a custom character device at /dev/bes2600 with
its own parser and command-dispatch table, exposing operations such as
'wifi on|off', 'bt on|off', 'change_fw_type <n>', 'bt_wakeup',
'bt_sleep', and 'wakeup_read_flag'. None of these surfaces are used by
the in-tree driver - every kernel call site consumes the internal state
accessors (bes2600_chrdev_is_signal_mode, bes2600_chrdev_get_fw_type,
etc) directly, not through the cdev.

The cdev interface is a standing upstream blocker for two reasons:

  1. Drivers under drivers/staging/ and drivers/net/wireless/ are
     expected to expose tuning via the firmware/nl80211/debugfs
     infrastructure rather than a private /dev node with an ad-hoc
     parser.

  2. The cdev handlers keep a global bes_cdev singleton alive whose
     ->cdev, ->dev_id, ->class and ->device pointers exist only to be
     torn down; they add no functionality that nl80211 or rfkill do
     not already provide (wifi/bt on-off, module_param for fw_type).

Remove the userspace interface:

  - open / read / write / release file_operations handlers and the
    bes2600_chardev_fops instance
  - bes2600_op_* command handlers and bes2600_op_map_tab dispatcher
  - bes2600_get_cmd_and_ifname / bes2600_recyle_cmd_and_ifname_mem
    string helpers
  - bes2600_load_uevent (its only caller was
    bes2600_chrdev_wifi_force_close_work informing userspace of a
    state it already gates via rfkill; that snprintf +
    kobject_uevent_env block is gone too, the kernel-side
    halt_device + switch_wifi(0) + chrdev_check_system_close
    sequence remains)
  - alloc_chrdev_region / cdev_init / cdev_add / class_create /
    device_create in bes2600_chrdev_init plus the fail1/fail2/fail3
    unwind labels
  - cdev_del / unregister_chrdev_region / device_destroy /
    class_destroy in bes2600_chrdev_free
  - cdev/dev_id/major/minor/class/device fields in struct bes_cdev

What remains (unchanged behaviour):

  - fw_type module parameter - the primary user-facing knob for
    signal/no-signal/BT mode switch
  - All in-kernel bes2600_chrdev_* accessor functions called from
    bes2600_sdio.c, bes_pwr.c, sta.c, bh.c, main.c, wsm.c, and
    wifi_testmode_cmd.c (13 call sites)
  - bes2600_chrdev_init / bes2600_chrdev_free as state-init / teardown
    for the remaining bes_cdev state (waitqueues, workqueues, flags)
  - DPD management (bes2600_chrdev_get_dpd_buffer / update / free)
  - wifi_force_close worker, system-close logic, bus-probe state
    machine

Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
6.19.10-danctnix1-1. Driver continues to associate and pass traffic;
no kernel messages related to the cdev absence. Users that previously
wrote to /dev/bes2600 should switch to the fw_type module parameter
or (future patch c4) nl80211 testmode commands.

Follow-ups:

  - c3.1: thread struct device * through bes2600_chrdev_is_signal_mode
    and friends so the global bes2600_cdev singleton can be dropped
    and the accessors scale to multi-device scenarios.
  - c4:   enable CONFIG_BES2600_TESTMODE and route nl80211 testmode
    commands to the firmware's patch_wifi_testMode entry.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-22 14:15:46 +02:00
test0r 108d3967ea bes2600: gate device LP-mode entry on successful per-VIF firmware handshake
bes2600_pwr_enter_lp_mode() drives the transition to low-power for each
associated STA VIF: it pushes wsm_set_pm(), waits up to 5 seconds on
pm_enter_cmpl for the firmware to acknowledge, then unconditionally
calls bes2600_pwr_device_enter_lp_mode() to drop the device end of the
bus.

Two bugs:

1. A failed wsm_set_pm() only logs an error, then still falls into
   wait_for_completion_timeout() on a completion the firmware will
   never post (the set-mode command never reached it). The loop
   therefore always blocks the full 5 s, logs a second error, and
   proceeds.

2. A genuine wait-timeout (firmware received the set-mode command but
   never posted the indication) also only logs a warning. The code
   then drops to bes2600_pwr_device_enter_lp_mode(), handing the
   device subsystem an inconsistent view of mac-layer state.

On PineTab2 (BES2600WM + RK3566) the second bug is the recurring
root-cause of the 'bes2600_pwr_enter_lp_mode, wait pm ind timeout'
message flooding dmesg every 5-10 s when the interface is associated
and idle. Sending the device to LP in that state cascades into the
SDIO TX path as the 'bes_sdio_memcpy_to_io_helper / sdio_tx_work'
WARN splat.

Fix:
  - Add a 'timeouts' counter; bump it on both failure paths.
  - Skip the wait_for_completion entirely when wsm_set_pm() failed
    (there is no completion to wait for).
  - Only call bes2600_pwr_device_enter_lp_mode() when every per-VIF
    handshake reached firmware-ACKed completion; otherwise return
    -ETIMEDOUT and leave the device in its current power state.

Tested-on: PineTab2 running linux-pinetab2 6.19.10-danctnix1-1.
Post-patch the handshake still fails on this particular firmware
revision (separate root-cause investigation outside this patch), but
the driver now returns -ETIMEDOUT cleanly instead of flooding dmesg
and destabilising the SDIO path.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-22 14:15:46 +02:00
test0r c7ba2044b7 bes2600: thread struct device * through factory request_firmware() call
Follow-up to \"bes2600: use request_firmware() for factory.txt read\".
That patch switched the factory calibration read path from filp_open()
+ kernel_read() to request_firmware(), but passed dev=NULL to
request_firmware() because factory_section_read_file() did not have a
struct device * in scope. The resulting logs carry the
'(NULL device *):' prefix and do not propagate a udev association.

Add a module-local static struct device * used as the firmware-class
load context, plus a small exported setter:

    static struct device *bes2600_factory_dev;
    void bes2600_factory_set_dev(struct device *dev);

Wire bes2600_factory_set_dev(&func->dev) from bes2600_sdio_probe(),
right after bes2600_platform_data_init() so the platform layer has
already had a chance to use the same struct device for its own
initialization.

factory_section_read_file() now passes bes2600_factory_dev (instead
of NULL) to request_firmware(). When the factory read happens before
probe (not currently the case on PineTab2) the pointer is still NULL
and request_firmware() accepts that; no regression.

No API changes to bes2600_get_factory_cali_data() callers. The
char *path parameter remains (it is the firmware-class name fed
straight to request_firmware()).

Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
6.19.10-danctnix1-1. Driver probes, factory data is read, and any
post-c5 factory diagnostics now carry the SDIO device identity
instead of '(NULL device *)'.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-22 14:15:46 +02:00
test0r a826f4db7d bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for PineTab2 factory.txt format
The shipped factory calibration file bes2600_factory.txt on PineTab2
(danctnix linux-firmware 0.3.5_2023.0209) contains 30 calibration
fields: head (3), iq/xtal (3), 2.4G power 11n (5), 5G power 11n (15),
bt (4). The file terminates with '%%\n' directly after edr_power.

When STANDARD_FACTORY_EFUSE_FLAG is defined at compile time the driver
assembles STANDARD_FACTORY with an extra select_efuse_flag section
appended and expects 31 sscanf matches (FACTORY_MEMBER_NUM=31):

    __STANDARD_FACTORY + \"##select_efuse_flag\\nselect_efuse:%hx\\n\"
                      + \"%%%%\\n\"

The PineTab2 factory.txt has no select_efuse_flag section, so sscanf
stops after field 30 and factory_parse() returns -1 with:

    bes2600_factory.txt parse fail
    read and check bes2600/bes2600_factory.txt error
    factory cali data get failed.

This was latent until the preceding patch (use request_firmware() for
factory.txt read) fixed the path bug that masked the parse failure.

Default STANDARD_FACTORY_EFUSE_FLAG to n. The flag remains overridable
at build time (make STANDARD_FACTORY_EFUSE_FLAG=y ...) for chips /
firmware packages that do ship the select_efuse_flag section.

Also: the wsm_save_factory_txt_to_mcu() prototype in wsm.h was
inconsistently wrapped in a conditional that keyed on
STANDARD_FACTORY_EFUSE_FLAG, but the function definition in wsm.c and
the call site in sta.c are ungated. With the flag now defaulting to
n, the gcc -Werror=missing-prototypes flag breaks the build. Drop the
conditional wrapper around the prototype — the function exists and is
used regardless of the factory-parse flag.

Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
6.19.10-danctnix1-1. With the flag defaulted off, factory_parse()
succeeds on the shipped factory.txt, factory_cali_data is populated,
and dmesg no longer shows the parse-fail / read-and-check-error /
factory-cali-data-get-failed sequence.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-22 14:15:46 +02:00
test0r d18aa6a9bc bes2600: use request_firmware() for factory.txt read
The BES2600 factory calibration file (bes2600_factory.txt) was being read
via filp_open() + kernel_read() from a hard-coded absolute path baked in
at compile time via the FACTORY_PATH Makefile macro
(default: /lib/firmware/bes2600_factory.txt).

This had several problems:

1. Path mismatch - linux-firmware-style packaging (and danctnix 0.2-5
   device-pine64-pinetab2) ships the file at
   /lib/firmware/bes2600/bes2600_factory.txt, not /lib/firmware/. The
   driver logged '(NULL device *): read and check
   /lib/firmware/bes2600_factory.txt error' on every boot on PineTab2
   running linux-pinetab2 6.19.10-danctnix1-1.

2. Direct filesystem access via filp_open() / kernel_read() from a driver
   is an anti-pattern that upstream rejects: drivers should use
   request_firmware() to get binary data from userspace-managed firmware
   directories. request_firmware() natively searches the firmware_class
   path list (typically /lib/firmware + derivatives), associates the load
   with a uevent, and respects the firmware-loading infrastructure.

3. The (NULL device *) prefix in error messages indicated the absence of
   proper device-context logging. While this patch does not yet thread
   struct device through, the upstream path uses request_firmware() which
   works with dev=NULL and is the building block for a follow-up patch
   that adds per-chip device context.

Repoint the FACTORY_PATH default to the firmware-class name
(bes2600/bes2600_factory.txt) - request_firmware() prepends
/lib/firmware/ from the configured search paths. The macro remains
overridable at build time for non-standard deployments.

Rewrite factory_section_read_file() to:
  * Call request_firmware(&fw, path, NULL).
  * Size-check fw->size against FACTORY_MAX_SIZE.
  * memcpy the data into the caller's buffer.
  * Always call release_firmware() on exit.

The file write path (factory_section_write_file + kernel_write) is left
unchanged in this patch; it is the subject of a follow-up patch that
removes kernel_write and moves any remaining userspace-visible factory
configuration to a standard kernel-userspace boundary (debugfs or
nl80211 testmode).

No caller signature changes. No Makefile flag drops. Bisectable.

Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
6.19.10-danctnix1-1, deployed via /lib/modules/<ver>/extra/. Verified
post-reboot: original 'read and check /lib/firmware/bes2600_factory.txt
error' is gone; request_firmware reads the file successfully (a separate
factory_parse() bug, previously masked by the read failure, is now
exposed and tracked separately).

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-22 14:15:46 +02:00
22 changed files with 160 additions and 2029 deletions
+3 -3
View File
@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
# feature option
BES2600 ?= m
CONFIG_BES2600_TESTMODE ?= n
CONFIG_BES2600_TESTMODE ?= y
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
@@ -65,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 ?= y
FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
STANDARD_FACTORY_EFUSE_FLAG ?= n
FACTORY_PATH ?= bes2600/bes2600_factory.txt
endif
# basic function
+3 -4
View File
@@ -17,7 +17,6 @@
#include <linux/umh.h>
#include "epta_request.h"
#include "epta_coex.h"
#include "txrx_opt.h"
#ifdef AP_HT_CAP_UPDATE
#define HT_INFO_OFFSET 4
@@ -1155,7 +1154,7 @@ void bes2600_multicast_stop_work(struct work_struct *work)
container_of(work, struct bes2600_vif, multicast_stop_work);
if (priv->aid0_bit_set) {
timer_delete_sync(&priv->mcast_timeout);
del_timer_sync(&priv->mcast_timeout);
wsm_lock_tx(priv->hw_priv);
priv->aid0_bit_set = false;
bes2600_set_tim_impl(priv, false);
@@ -1165,7 +1164,7 @@ void bes2600_multicast_stop_work(struct work_struct *work)
void bes2600_mcast_timeout(struct timer_list *t)
{
struct bes2600_vif *priv = timer_container_of(priv, t, mcast_timeout);
struct bes2600_vif *priv = from_timer(priv, t, mcast_timeout);
wiphy_warn(priv->hw->wiphy,
"Multicast delivery timeout.\n");
@@ -1239,7 +1238,7 @@ void bes2600_suspend_resume(struct bes2600_vif *priv,
}
spin_unlock_bh(&priv->ps_state_lock);
if (cancel_tmo)
timer_delete_sync(&priv->mcast_timeout);
del_timer_sync(&priv->mcast_timeout);
} else {
spin_lock_bh(&priv->ps_state_lock);
bes2600_ps_notify(priv, arg->link_id, arg->stop);
-509
View File
@@ -1,509 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Bestechnic BES2600 BT UART driver
*
* Copyright (c) 2025 Dang Huynh <dang.huynh@mainlining.org>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/device.h>
#include <linux/pm.h>
#include <linux/pm_wakeirq.h>
#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/serdev.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "bes2600.h"
#include "bes_chardev.h"
#include "h4_recv.h"
struct bes2600_btuart_data {
struct serdev_device *serdev;
struct hci_dev *hdev;
struct pinctrl *pinctrl;
struct pinctrl_state *pinctrl_default;
/* Device tree properties */
struct gpio_desc *power;
struct gpio_desc *wakeup;
int speed;
unsigned long state;
int wake_irq;
bool opened;
int err_count; /* workaround for broken firmware */
struct sk_buff_head txq;
struct work_struct tx_work;
rwlock_t lock;
struct sk_buff *rx_skb;
};
static const struct h4_recv_pkt bes2600_btuart_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
};
static int bes2600_btuart_power(struct bes2600_btuart_data *data, bool enable)
{
int ret, val;
if (enable)
val = 1;
else
val = 0;
ret = gpiod_set_value_cansleep(data->power, val);
if (ret)
return ret;
ret = bes2600_chrdev_switch_subsys_glb(-1, val);
if (ret)
return ret;
return 0;
}
/*
* HACK: Potential DOS issue, not sure if the firmware can filter it but
* there needs to be a better way to detect this. Even better, fix the firmware.
*/
static int bes2600_btuart_bug_recovery(struct bes2600_btuart_data *data)
{
struct hci_dev *hdev = data->hdev;
struct serdev_device *serdev = data->serdev;
bt_dev_info(hdev, "Attempting to recover from Firmware bug (might not work)");
serdev_device_set_flow_control(serdev, false);
bes2600_btuart_power(data, false);
msleep(20);
bes2600_btuart_power(data, true);
serdev_device_set_flow_control(serdev, true);
msleep(100);
hci_reset_dev(hdev);
return 0;
}
static int bes2600_btuart_recv(struct bes2600_btuart_data *data, const void *buffer, int count)
{
struct hci_dev *hdev = data->hdev;
int err = 0;
data->rx_skb = h4_recv_buf(hdev, data->rx_skb, buffer, count,
bes2600_btuart_recv_pkts, ARRAY_SIZE(bes2600_btuart_recv_pkts));
if (IS_ERR(data->rx_skb))
err = PTR_ERR(data->rx_skb);
return err;
}
static size_t bes2600_btuart_receive_buf(struct serdev_device *serdev,
const u8 *buf, size_t count)
{
struct bes2600_btuart_data *data = serdev_device_get_drvdata(serdev);
struct hci_dev *hdev = data->hdev;
int ret;
read_lock(&data->lock);
ret = bes2600_btuart_recv(data, buf, count);
if (ret) {
bt_dev_err(hdev, "Failed to receive packet (%d)", ret);
data->err_count++;
ret = 0;
} else {
hdev->stat.byte_rx += count;
ret = count;
}
read_unlock(&data->lock);
/* Attempting to recovery from a bugged firmware */
if (data->err_count > 10) {
bes2600_btuart_bug_recovery(data);
data->err_count = 0;
}
return ret;
}
static const struct serdev_device_ops bes2600_btuart_serdev_client_ops = {
.receive_buf = bes2600_btuart_receive_buf,
.write_wakeup = serdev_device_write_wakeup,
};
static void bes2600_btuart_wake(struct hci_dev *hdev, bool enable)
{
struct bes2600_btuart_data *data = hci_get_drvdata(hdev);
if (enable)
gpiod_set_value_cansleep(data->wakeup, 1);
else
gpiod_set_value_cansleep(data->wakeup, 0);
}
static int bes2600_btuart_open(struct hci_dev *hdev)
{
struct bes2600_btuart_data *data = hci_get_drvdata(hdev);
struct serdev_device *serdev = data->serdev;
int ret;
ret = serdev_device_open(serdev);
if (ret < 0) {
bt_dev_err(hdev, "Failed to open serial device");
return ret;
}
serdev_device_set_flow_control(serdev, false);
ret = serdev_device_set_baudrate(serdev, data->speed);
if (ret < 0) {
bt_dev_err(hdev, "Failed to set baud rate");
return ret;
}
serdev_device_set_flow_control(serdev, true);
data->opened = true;
bes2600_btuart_wake(hdev, true);
return 0;
}
static int bes2600_btuart_flush(struct hci_dev *hdev)
{
struct bes2600_btuart_data *data = hci_get_drvdata(hdev);
struct serdev_device *serdev = data->serdev;
serdev_device_write_flush(serdev);
read_lock(&data->lock);
skb_queue_purge(&data->txq);
cancel_work_sync(&data->tx_work);
read_unlock(&data->lock);
return 0;
}
static int bes2600_btuart_close(struct hci_dev *hdev)
{
struct bes2600_btuart_data *data = hci_get_drvdata(hdev);
struct serdev_device *serdev = data->serdev;
bes2600_btuart_wake(hdev, false);
serdev_device_set_flow_control(serdev, false);
serdev_device_close(serdev);
data->opened = false;
return 0;
}
static int bes2600_btuart_setup(struct hci_dev *hdev)
{
struct bes2600_btuart_data *data = hci_get_drvdata(hdev);
int ret;
if (bes2600_chrdev_is_bus_error())
return -EFAULT;
/* Set pinctrl state to default */
ret = pinctrl_select_state(data->pinctrl, data->pinctrl_default);
if (ret < 0) {
bt_dev_err(hdev, "Failed to setup pinctrl state");
return ret;
}
/* Set power GPIO to high and tell chardev to switch to BT */
bes2600_btuart_power(data, true);
return 0;
}
static int bes2600_btuart_shutdown(struct hci_dev *hdev)
{
struct bes2600_btuart_data *data = hci_get_drvdata(hdev);
if (bes2600_chrdev_is_bus_error())
return -EFAULT;
bes2600_btuart_power(data, false);
return 0;
}
static int bes2600_btuart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
char *pkt_type = NULL;
struct bes2600_btuart_data *data = hci_get_drvdata(hdev);
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
default:
return -EILSEQ;
}
pkt_type = skb_push(skb, 1);
pkt_type[0] = hci_skb_pkt_type(skb);
read_lock(&data->lock);
skb_queue_tail(&data->txq, skb);
read_unlock(&data->lock);
schedule_work(&data->tx_work);
return 0;
}
static ssize_t bes2600_btuart_send(struct bes2600_btuart_data *data, struct sk_buff *skb)
{
struct hci_dev *hdev = data->hdev;
int len;
len = serdev_device_write_buf(data->serdev, skb->data, skb->len);
if (len < 0) {
bt_dev_err(data->hdev, "Failed to write buffer to TTY");
return -EIO;
}
serdev_device_wait_until_sent(data->serdev, 0);
hdev->stat.byte_tx += len;
skb_pull(skb, len);
return skb->len;
}
static void bes2600_btuart_work(struct work_struct *work)
{
struct bes2600_btuart_data *data = container_of(work, struct bes2600_btuart_data, tx_work);
struct sk_buff *skb;
while ((skb = skb_dequeue(&data->txq))) {
if (bes2600_btuart_send(data, skb) > 0) {
skb_queue_head(&data->txq, skb);
break;
}
kfree_skb(skb);
}
}
static void bes2600_btuart_parse_dt(struct serdev_device *serdev)
{
struct bes2600_btuart_data *data = serdev_device_get_drvdata(serdev);
struct device_node *node = serdev->dev.of_node;
int speed = 1500000;
of_property_read_u32(node, "current-speed", &speed);
data->speed = speed;
}
static irqreturn_t bes2600_btuart_wakeup_handler(int irq, void *priv)
{
struct bes2600_btuart_data *data = (struct bes2600_btuart_data *)priv;
dev_info(&data->serdev->dev, "IRQ wake\n");
return IRQ_HANDLED;
}
#ifdef CONFIG_PM_SLEEP
static int bes2600_btuart_suspend(struct device *dev)
{
struct serdev_device *serdev = to_serdev_device(dev);
struct bes2600_btuart_data *data = serdev_device_get_drvdata(serdev);
if (data->opened) {
bes2600_btuart_wake(data->hdev, false);
if (device_may_wakeup(dev) && data->wake_irq)
enable_irq_wake(data->wake_irq);
}
return 0;
}
static int bes2600_btuart_resume(struct device *dev)
{
struct serdev_device *serdev = to_serdev_device(dev);
struct bes2600_btuart_data *data = serdev_device_get_drvdata(serdev);
if (data->opened) {
bes2600_btuart_wake(data->hdev, true);
if (device_may_wakeup(dev) && data->wake_irq)
disable_irq_wake(data->wake_irq);
}
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(bes2600_btuart_pm_ops,
bes2600_btuart_suspend, bes2600_btuart_resume);
static int bes2600_btuart_probe(struct serdev_device *serdev)
{
struct device *dev = &serdev->dev;
struct bes2600_btuart_data *data;
struct hci_dev *hdev;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
skb_queue_head_init(&data->txq);
data->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(data->pinctrl))
return dev_err_probe(dev, PTR_ERR(data->pinctrl), "Cannot get pinctrl\n");
data->pinctrl_default = pinctrl_lookup_state(data->pinctrl, PINCTRL_STATE_DEFAULT);
if (IS_ERR(data->pinctrl_default))
return dev_err_probe(dev, PTR_ERR(data->pinctrl_default),
"Cannot get default pinctrl state\n");
data->power = devm_gpiod_get_index(dev, "power", 0, GPIOD_OUT_HIGH);
if (IS_ERR(data->power))
return dev_err_probe(dev, PTR_ERR(data->power), "Failed to get power gpio\n");
data->wakeup = devm_gpiod_get_index_optional(dev, "wakeup", 0, GPIOD_OUT_HIGH);
if (!data->wakeup)
dev_err(dev, "Failed to get wakeup gpio\n");
data->wake_irq = of_irq_get_byname(dev->of_node, "wakeup");
if (data->wake_irq <= 0) {
ret = data->wake_irq;
dev_err(dev, "Failed to get IRQ number, host will not wake up (ret=%d)\n", ret);
}
if (data->wake_irq > 0) {
ret = devm_device_init_wakeup(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize device wakeup\n");
ret = devm_request_threaded_irq(dev, data->wake_irq, NULL,
bes2600_btuart_wakeup_handler,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"bes2600_btwakeup_irq", data);
if (ret < 0)
return dev_err_probe(dev, ret, "Cannot request IRQ\n");
ret = devm_pm_set_wake_irq(dev, data->wake_irq);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to set wake irq\n");
}
data->err_count = 0;
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
rwlock_init(&data->lock);
data->serdev = serdev;
data->hdev = hdev;
serdev_device_set_drvdata(serdev, data);
serdev_device_set_client_ops(serdev, &bes2600_btuart_serdev_client_ops);
bes2600_btuart_parse_dt(serdev);
INIT_WORK(&data->tx_work, bes2600_btuart_work);
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, data);
hdev->open = bes2600_btuart_open;
hdev->close = bes2600_btuart_close;
hdev->flush = bes2600_btuart_flush;
hdev->setup = bes2600_btuart_setup;
hdev->shutdown = bes2600_btuart_shutdown;
hdev->send = bes2600_btuart_send_frame;
hdev->manufacturer = 0x02B0;
SET_HCIDEV_DEV(hdev, &serdev->dev);
hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY);
/* Firmware claims to support HCI_OP_LE_READ_BUFFER_SIZE_V2 but broken */
hci_set_quirk(hdev, HCI_QUIRK_BROKEN_BUFFER_SIZE_V2);
/*
* Once shutdown() is ran, it turns off the Bluetooth regulator and
* would not power back on unless setup() is run again.
*/
hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
ret = hci_register_dev(hdev);
if (ret < 0) {
hci_free_dev(hdev);
return dev_err_probe(dev, ret, "Failed to register HCI device\n");
}
return 0;
}
static void bes2600_btuart_remove(struct serdev_device *serdev)
{
struct bes2600_btuart_data *data = serdev_device_get_drvdata(serdev);
struct hci_dev *hdev = data->hdev;
cancel_work_sync(&data->tx_work);
if (hdev) {
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
}
static const struct of_device_id bes2600_btuart_of_match[] = {
{ .compatible = "bestechnic,bes2600-btuart", },
{},
};
MODULE_DEVICE_TABLE(of, bes2600_btuart_of_match);
static struct serdev_device_driver bes2600_btuart_driver = {
.probe = bes2600_btuart_probe,
.remove = bes2600_btuart_remove,
.driver = {
.name = "bes2600_btuart",
.pm = &bes2600_btuart_pm_ops,
.of_match_table = of_match_ptr(bes2600_btuart_of_match),
},
};
module_serdev_device_driver(bes2600_btuart_driver);
MODULE_AUTHOR("Dang Huynh <dang.huynh@mainlining.org>");
MODULE_DESCRIPTION("BES2600 BT UART driver");
MODULE_LICENSE("GPL");
+26 -19
View File
@@ -12,6 +12,7 @@
#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>
@@ -30,6 +31,18 @@
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
@@ -137,38 +150,32 @@ static int bes2600_factory_crc_check(struct factory_t *factory_data)
*/
static int factory_section_read_file(char *path, void *buffer)
{
int ret = 0;
struct file *fp;
const struct firmware *fw;
int ret;
if (!path || !buffer) {
bes_err("%s NULL pointer err\n", __func__);
return -1;
}
bes_devel("reading %s \n", path);
bes_devel("requesting firmware-class %s\n", path);
fp = filp_open(path, O_RDONLY, 0); //S_IRUSR
if (IS_ERR(fp)) {
bes_devel("BES2600 : can't open %s\n",path);
ret = request_firmware(&fw, path, bes2600_factory_dev);
if (ret) {
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
return -1;
}
if (fp->f_inode->i_size <= 0 || fp->f_inode->i_size > FACTORY_MAX_SIZE) {
bes_err( "bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n",
fp->f_inode->i_size, FACTORY_MAX_SIZE);
filp_close(fp, NULL);
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);
return -1;
}
ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos);
filp_close(fp, NULL);
if (ret != fp->f_inode->i_size) {
bes_err("bes2600_factory.txt read fail\n");
ret = -1;
}
memcpy(buffer, fw->data, fw->size);
ret = (int)fw->size;
release_firmware(fw);
return ret;
}
+3
View File
@@ -199,6 +199,9 @@ 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);
+44 -2
View File
@@ -30,6 +30,7 @@
#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"
@@ -94,6 +95,7 @@ struct sbus_priv {
struct work_struct tx_work;
struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1];
struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1];
u8 *tx_bounce;
u32 tx_data_cnt;
u32 tx_xfer_cnt;
u32 tx_proc_cnt;
@@ -1135,7 +1137,26 @@ 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;
++scatters;
/*del_node:*/
@@ -1834,6 +1855,9 @@ 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;
@@ -1853,6 +1877,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
if (!self->single_gathered_buffer)
return -ENOMEM;
#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
self->fw_started = false;
#endif
@@ -1879,6 +1914,7 @@ static int bes2600_sdio_probe(struct sdio_func *func,
out:
bes2600_chrdev_set_sbus_priv_data(self, false);
bes2600_switch_bt(true);
bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_PROBE);
return 0;
@@ -1913,8 +1949,8 @@ int bes2600_unregister_net_dev(struct sbus_priv *bus_priv)
BUG_ON(!bus_priv);
if (bus_priv->core && !bus_priv->unregister_in_process) {
bus_priv->unregister_in_process = true;
bes2600_pwr_unregister_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb);
bes2600_core_release(bus_priv->core);
bes2600_pwr_unregister_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb);
bus_priv->core = NULL;
if (bus_priv->sdio_wq) {
@@ -1980,6 +2016,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
if (self->single_gathered_buffer) {
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
kfree(self);
}
+3 -565
View File
@@ -43,12 +43,6 @@ 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;
@@ -196,7 +190,7 @@ static int bes2600_switch_wifi(bool on)
return ret;
}
static int bes2600_switch_bt(bool on)
int bes2600_switch_bt(bool on)
{
int ret = 0;
long status = 0;
@@ -229,11 +223,11 @@ static int bes2600_switch_bt(bool on)
/* check if there is a error when bootup */
ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
} else {
bes_devel("bes2600 activate bt.\n");
bes_info("enable BT\n");
ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true);
}
} else {
bes_devel("bes2600 deactivate bt.\n");
bes_info("disable BT\n");
bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_OFF, SUBSYSTEM_BT, false);
}
@@ -249,392 +243,18 @@ static int bes2600_switch_bt(bool on)
return ret;
}
/*
* This is a global function so we don't have to make many changes to
* the driver.
*
* @wifi: 1 to turn on, 0 to turn off. Otherwise, leave unchanged
* @bt: 1 to turn on, 0 to turn off. Otherwise, leave unchanged
*/
int bes2600_chrdev_switch_subsys_glb(int wifi, int bt)
{
int ret = 0;
switch (wifi) {
case 0:
ret = bes2600_switch_wifi(false);
break;
case 1:
ret = bes2600_switch_wifi(true);
break;
default:
break;
}
if (ret)
goto result;
switch (bt) {
case 0:
ret = bes2600_switch_bt(false);
break;
case 1:
ret = bes2600_switch_bt(true);
break;
default:
break;
}
result:
return ret;
}
EXPORT_SYMBOL_GPL(bes2600_chrdev_switch_subsys_glb);
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)
{
@@ -644,123 +264,10 @@ 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)
@@ -1126,7 +633,6 @@ void bes2600_chrdev_wakeup_bt(void)
bes_err("Wakeup BT fail in resume\n");
}
}
EXPORT_SYMBOL_GPL(bes2600_chrdev_wakeup_bt);
int bes2600_chrdev_get_fw_type(void)
{
@@ -1148,7 +654,6 @@ bool bes2600_chrdev_is_bus_error(void)
return error;
}
EXPORT_SYMBOL_GPL(bes2600_chrdev_is_bus_error);
void bes2600_chrdev_update_signal_mode(void)
{
@@ -1167,12 +672,6 @@ 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");
@@ -1189,14 +688,6 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
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");
}
}
@@ -1290,46 +781,6 @@ 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);
@@ -1361,15 +812,6 @@ 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)
@@ -1379,9 +821,5 @@ 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__);
}
+2 -1
View File
@@ -63,7 +63,6 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
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);
int bes2600_chrdev_switch_subsys_glb(int wifi, int bt);
/* get and set internal state */
bool bes2600_chrdev_is_wifi_opened(void);
@@ -92,4 +91,6 @@ u8* bes2600_alloc_dpd_log_buffer(u16 len);
void bes2600_get_dpd_log(char **data, size_t *len);
#endif
int bes2600_switch_bt(bool);
#endif /* __BES_CHARDEV_H__ */
-34
View File
@@ -125,8 +125,6 @@ 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;
@@ -468,14 +466,6 @@ 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;
@@ -583,14 +573,6 @@ 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
@@ -640,17 +622,6 @@ 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);
@@ -832,11 +803,6 @@ 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,3 +8,26 @@ 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)
+17 -3
View File
@@ -472,6 +472,7 @@ 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;
@@ -528,22 +529,35 @@ 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)
if (!status) {
bes_err("%s, wait pm ind timeout\n", __func__);
timeouts++;
}
} else {
bes_devel("skip enter lp mode\n");
}
}
}
/* set device low power configuration */
bes2600_pwr_device_enter_lp_mode(hw_priv);
/*
* 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;
return ret;
}
+3 -10
View File
@@ -22,7 +22,6 @@
#include "debug.h"
#include "epta_coex.h"
#include "bes_chardev.h"
#include "txrx_opt.h"
#include "sta.h"
#include "bes_log.h"
@@ -1260,8 +1259,6 @@ int bes2600_bh_sw_process(struct bes2600_common *hw_priv,
delta_time = jiffies + ((unsigned long)0xffffffff - timestamp);
else
delta_time = jiffies - timestamp;
bes2600_add_tx_delta_time(delta_time);
bes2600_add_tx_ac_delta_time(queue_id, delta_time);
if (bes2600_need_retry_type(skb, tx_confirm->status) == 0)
return -1;
@@ -1270,12 +1267,8 @@ int bes2600_bh_sw_process(struct bes2600_common *hw_priv,
return -1;
if (txpriv->retry_count < CW1200_MAX_SW_RETRY_CNT ) {
struct bes2600_vif *priv =
__cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
txpriv->retry_count++;
bes2600_tx_status(priv,skb);
bes2600_pwr_set_busy_event_with_timeout_async(
hw_priv, BES_PWR_LOCK_ON_TX, BES_PWR_EVENT_TX_TIMEOUT);
@@ -1311,14 +1304,14 @@ void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx)
}
if (--hw_priv->wsm_tx_pending[idx] == 0)
timer_delete_sync(timer);
del_timer_sync(timer);
else
mod_timer(timer, jiffies + 3 * HZ);
}
void bes2600_bh_mcu_active_monitor(struct timer_list* t)
{
struct bes2600_common *hw_priv = timer_container_of(hw_priv, t, mcu_mon_timer);
struct bes2600_common *hw_priv = from_timer(hw_priv, t, mcu_mon_timer);
bes_err("link break between mcu and host, hw_buf_used:%d pending:%d\n",
hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[1]);
@@ -1327,7 +1320,7 @@ void bes2600_bh_mcu_active_monitor(struct timer_list* t)
void bes2600_bh_lmac_active_monitor(struct timer_list* t)
{
struct bes2600_common *hw_priv = timer_container_of(hw_priv, t, lmac_mon_timer);
struct bes2600_common *hw_priv = from_timer(hw_priv, t, lmac_mon_timer);
bes_err("link break between lmac and host, hw_buf_used:%d pending:%d\n",
hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[0]);
-154
View File
@@ -1,154 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copy of drivers/bluetooth/h4_recv.h
*
* Generic Bluetooth HCI UART driver
*
* Copyright (C) 2015-2018 Intel Corporation
*/
#include <linux/unaligned.h>
struct h4_recv_pkt {
u8 type; /* Packet type */
u8 hlen; /* Header length */
u8 loff; /* Data length offset in header */
u8 lsize; /* Data length field size */
u16 maxlen; /* Max overall packet length */
int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
};
#define H4_RECV_ACL \
.type = HCI_ACLDATA_PKT, \
.hlen = HCI_ACL_HDR_SIZE, \
.loff = 2, \
.lsize = 2, \
.maxlen = HCI_MAX_FRAME_SIZE \
#define H4_RECV_SCO \
.type = HCI_SCODATA_PKT, \
.hlen = HCI_SCO_HDR_SIZE, \
.loff = 2, \
.lsize = 1, \
.maxlen = HCI_MAX_SCO_SIZE
#define H4_RECV_EVENT \
.type = HCI_EVENT_PKT, \
.hlen = HCI_EVENT_HDR_SIZE, \
.loff = 1, \
.lsize = 1, \
.maxlen = HCI_MAX_EVENT_SIZE
#define H4_RECV_ISO \
.type = HCI_ISODATA_PKT, \
.hlen = HCI_ISO_HDR_SIZE, \
.loff = 2, \
.lsize = 2, \
.maxlen = HCI_MAX_FRAME_SIZE
static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
struct sk_buff *skb,
const unsigned char *buffer,
int count,
const struct h4_recv_pkt *pkts,
int pkts_count)
{
/* Check for error from previous call */
if (IS_ERR(skb))
skb = NULL;
while (count) {
int i, len;
if (!skb) {
for (i = 0; i < pkts_count; i++) {
if (buffer[0] != (&pkts[i])->type)
continue;
skb = bt_skb_alloc((&pkts[i])->maxlen,
GFP_ATOMIC);
if (!skb)
return ERR_PTR(-ENOMEM);
hci_skb_pkt_type(skb) = (&pkts[i])->type;
hci_skb_expect(skb) = (&pkts[i])->hlen;
break;
}
/* Check for invalid packet type */
if (!skb)
return ERR_PTR(-EILSEQ);
count -= 1;
buffer += 1;
}
len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
skb_put_data(skb, buffer, len);
count -= len;
buffer += len;
/* Check for partial packet */
if (skb->len < hci_skb_expect(skb))
continue;
for (i = 0; i < pkts_count; i++) {
if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
break;
}
if (i >= pkts_count) {
kfree_skb(skb);
return ERR_PTR(-EILSEQ);
}
if (skb->len == (&pkts[i])->hlen) {
u16 dlen;
switch ((&pkts[i])->lsize) {
case 0:
/* No variable data length */
dlen = 0;
break;
case 1:
/* Single octet variable length */
dlen = skb->data[(&pkts[i])->loff];
hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
kfree_skb(skb);
return ERR_PTR(-EMSGSIZE);
}
break;
case 2:
/* Double octet variable length */
dlen = get_unaligned_le16(skb->data +
(&pkts[i])->loff);
hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
kfree_skb(skb);
return ERR_PTR(-EMSGSIZE);
}
break;
default:
/* Unsupported variable length */
kfree_skb(skb);
return ERR_PTR(-EILSEQ);
}
if (!dlen) {
/* No more data, complete frame */
(&pkts[i])->recv(hdev, skb);
skb = NULL;
}
} else {
/* Complete frame */
(&pkts[i])->recv(hdev, skb);
skb = NULL;
}
}
return skb;
}
+4 -44
View File
@@ -32,7 +32,6 @@
#include "pm.h"
#include "bes2600_factory.h"
#include "bes_chardev.h"
#include "txrx_opt.h"
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>");
MODULE_DESCRIPTION("Softmac BES2600 common code");
@@ -199,11 +198,7 @@ static const struct ieee80211_iface_limit bes2600_if_limits[] = {
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) },
#ifdef P2P_MULTIVIF
/*
* HACK: Disable P2P_DEVICE implementation for BES2600
* as the code is a little buggy.
*/
//{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
#endif
};
@@ -598,7 +593,7 @@ static void bes2600_unregister_common(struct ieee80211_hw *dev)
ieee80211_unregister_hw(dev);
timer_delete_sync(&hw_priv->ba_timer);
del_timer_sync(&hw_priv->ba_timer);
hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
bes2600_unregister_bh(hw_priv);
@@ -795,41 +790,6 @@ 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;
@@ -901,8 +861,8 @@ int bes2600_wifi_stop(struct bes2600_common *hw_priv)
hw_priv->wsm_tx_seq[1] = 0;
hw_priv->wsm_tx_pending[0] = 0;
hw_priv->wsm_tx_pending[1] = 0;
timer_delete_sync(&hw_priv->mcu_mon_timer);
timer_delete_sync(&hw_priv->lmac_mon_timer);
del_timer_sync(&hw_priv->mcu_mon_timer);
del_timer_sync(&hw_priv->lmac_mon_timer);
#ifdef CONFIG_BES2600_STATIC_SDD
hw_priv->sdd = NULL;
#else
+10 -8
View File
@@ -119,9 +119,10 @@ static void bes2600_queue_register_post_gc(struct list_head *gc_list,
struct bes2600_queue_item *item)
{
struct bes2600_queue_item *gc_item;
gc_item = kmemdup(item, sizeof(struct bes2600_queue_item),
gc_item = kmalloc(sizeof(struct bes2600_queue_item),
GFP_ATOMIC);
BUG_ON(!gc_item);
memcpy(gc_item, item, sizeof(struct bes2600_queue_item));
list_add_tail(&gc_item->head, gc_list);
}
@@ -130,9 +131,9 @@ static void bes2600_queue_pending_record(struct list_head *pending_record_list,
{
struct bes2600_queue_item *record_item;
record_item = kmemdup(pending_item, sizeof(struct bes2600_queue_item),
GFP_ATOMIC);
record_item = kmalloc(sizeof(struct bes2600_queue_item),GFP_ATOMIC);
BUG_ON(!record_item);
memcpy(record_item, pending_item, sizeof(struct bes2600_queue_item));
record_item->skb = skb_clone(pending_item->skb, GFP_ATOMIC);
list_add_tail(&record_item->head, pending_record_list);
}
@@ -195,7 +196,7 @@ static void __bes2600_queue_gc(struct bes2600_queue *queue,
static void bes2600_queue_gc(struct timer_list *t)
{
LIST_HEAD(list);
struct bes2600_queue *queue = timer_container_of(queue, t, gc);
struct bes2600_queue *queue = from_timer(queue, t, gc);
spin_lock_bh(&queue->lock);
__bes2600_queue_gc(queue, &list, true);
@@ -217,7 +218,7 @@ int bes2600_queue_stats_init(struct bes2600_queue_stats *stats,
spin_lock_init(&stats->lock);
init_waitqueue_head(&stats->wait_link_id_empty);
for (i = 0; i < CW12XX_MAX_VIFS; i++) {
stats->link_map_cache[i] = kcalloc(map_capacity, sizeof(int),
stats->link_map_cache[i] = kzalloc(map_capacity * sizeof(int),
GFP_KERNEL);
if (!stats->link_map_cache[i]) {
for (; i >= 0; i--)
@@ -248,14 +249,14 @@ int bes2600_queue_init(struct bes2600_queue *queue,
queue->queue_all_lock = false;
spin_lock_init(&queue->lock);
timer_setup(&queue->gc, bes2600_queue_gc, 0);
queue->pool = kcalloc(capacity, sizeof(struct bes2600_queue_item),
queue->pool = kzalloc(sizeof(struct bes2600_queue_item) * capacity,
GFP_KERNEL);
if (!queue->pool)
return -ENOMEM;
for (i = 0; i < CW12XX_MAX_VIFS; i++) {
queue->link_map_cache[i] =
kcalloc(stats->map_capacity, sizeof(int),
kzalloc(stats->map_capacity * sizeof(int),
GFP_KERNEL);
if (!queue->link_map_cache[i]) {
for (; i >= 0; i--)
@@ -362,7 +363,7 @@ void bes2600_queue_deinit(struct bes2600_queue *queue)
int i;
bes2600_queue_clear(queue, CW12XX_ALL_IFS);
timer_delete_sync(&queue->gc);
del_timer_sync(&queue->gc);
INIT_LIST_HEAD(&queue->free_pool);
kfree(queue->pool);
for (i = 0; i < CW12XX_MAX_VIFS; i++) {
@@ -409,6 +410,7 @@ int bes2600_queue_put(struct bes2600_queue *queue,
struct timespec64 tmval;
#endif /*CONFIG_BES2600_TESTMODE*/
LIST_HEAD(gc_list);
struct bes2600_queue_stats *stats = queue->stats;
/* TODO:COMBO: Add interface ID info to queue item */
+5 -6
View File
@@ -533,10 +533,10 @@ void bes2600_scan_work(struct work_struct *work)
"[SCAN] Scan failed (%d).\n",
hw_priv->scan.status);
else if (hw_priv->scan.req)
wiphy_dbg(priv->hw->wiphy,
wiphy_info(priv->hw->wiphy,
"[SCAN] Scan completed.\n");
else
wiphy_dbg(priv->hw->wiphy,
wiphy_info(priv->hw->wiphy,
"[SCAN] Scan canceled.\n");
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
@@ -621,9 +621,8 @@ void bes2600_scan_work(struct work_struct *work)
scan.scanType = WSM_SCAN_TYPE_BACKGROUND;
scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
}
scan.ch = kcalloc((it - hw_priv->scan.curr),
sizeof(struct wsm_scan_ch),
GFP_KERNEL);
scan.ch = kzalloc((it - hw_priv->scan.curr) *
sizeof(struct wsm_scan_ch), GFP_KERNEL);
if (!scan.ch) {
hw_priv->scan.status = -ENOMEM;
goto fail;
@@ -906,7 +905,7 @@ void bes2600_scan_complete_cb(struct bes2600_common *hw_priv,
// recover EPTA timer after scan wsm msg complete, in case of epta state error
// bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING_COMP);
#endif
wiphy_dbg(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status);
wiphy_info(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status);
if(hw_priv->scan.status == -ETIMEDOUT)
wiphy_warn(hw_priv->hw->wiphy,
+11 -32
View File
@@ -42,8 +42,6 @@
#include "bes2600_factory.h"
#endif
#include "txrx_opt.h"
#define WEP_ENCRYPT_HDR_SIZE 4
#define WEP_ENCRYPT_TAIL_SIZE 4
#define WPA_ENCRYPT_HDR_SIZE 8
@@ -221,7 +219,7 @@ void bes2600_stop(struct ieee80211_hw *dev, bool suspend)
cancel_delayed_work_sync(&hw_priv->advance_scan_timeout);
#endif
flush_workqueue(hw_priv->workqueue);
timer_delete_sync(&hw_priv->ba_timer);
del_timer_sync(&hw_priv->ba_timer);
down(&hw_priv->conf_lock);
@@ -261,7 +259,7 @@ void bes2600_stop(struct ieee80211_hw *dev, bool suspend)
cancel_delayed_work_sync(&priv->bss_loss_work);
cancel_delayed_work_sync(&priv->connection_loss_work);
cancel_delayed_work_sync(&priv->link_id_gc_work);
timer_delete_sync(&priv->mcast_timeout);
del_timer_sync(&priv->mcast_timeout);
}
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
@@ -377,23 +375,9 @@ void bes2600_remove_interface(struct ieee80211_hw *dev,
atomic_set(&priv->enabled, 0);
down(&hw_priv->scan.lock);
down(&hw_priv->conf_lock);
/*
* There's a chance remove_interface will run again on the same
* (already removed) interface.
*
* Currently this only happens when NetworkManager creates a P2P_DEVICE
* alongside a STA.
*
* But there can be other cases where this may run as well. So if that
* happens, let's throw a warning and decrease the vifs count by one.
*/
if (WARN_ON(!__cw12xx_hwpriv_to_vifpriv(hw_priv, priv->if_id))) {
if (!__cw12xx_hwpriv_to_vifpriv(hw_priv, priv->if_id)) {
bes_devel(" !!! %s: interface addr %pM already removed\n",
__func__, vif->addr);
atomic_dec(&hw_priv->num_vifs);
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
return;
@@ -465,7 +449,7 @@ void bes2600_remove_interface(struct ieee80211_hw *dev,
cancel_delayed_work_sync(&priv->set_cts_work);
cancel_delayed_work_sync(&priv->pending_offchanneltx_work);
timer_delete_sync(&priv->mcast_timeout);
del_timer_sync(&priv->mcast_timeout);
/* TODO:COMBO: May be reset of these variables "delayed_link_loss and
* join_status to default can be removed as dev_priv will be freed by
* mac80211 */
@@ -518,7 +502,7 @@ int bes2600_change_interface(struct ieee80211_hw *dev,
return ret;
}
int bes2600_config(struct ieee80211_hw *dev, int radio_idx, u32 changed)
int bes2600_config(struct ieee80211_hw *dev, u32 changed)
{
int ret = 0;
struct bes2600_common *hw_priv = dev->priv;
@@ -1153,7 +1137,7 @@ void bes2600_wep_key_work(struct work_struct *work)
wsm_unlock_tx(hw_priv);
}
int bes2600_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value)
int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
{
struct bes2600_common *hw_priv = hw->priv;
int ret;
@@ -2185,8 +2169,6 @@ void bes2600_join_work(struct work_struct *work)
wsm_unlock_tx(hw_priv);
return;
}
rcu_read_lock();
ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
dtimie = ieee80211_bss_get_ie(bss, WLAN_EID_TIM);
if (dtimie)
@@ -2270,8 +2252,6 @@ void bes2600_join_work(struct work_struct *work)
bes2600_rate_mask_to_wsm(hw_priv, 0xFF0);
}
rcu_read_unlock();
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_JOIN);
wsm_flush_tx(hw_priv);
@@ -2378,7 +2358,7 @@ void bes2600_unjoin_work(struct work_struct *work)
int i;
struct bes2600_vif *tmp_priv;
timer_delete_sync(&hw_priv->ba_timer);
del_timer_sync(&hw_priv->ba_timer);
down(&hw_priv->conf_lock);
if (unlikely(atomic_read(&hw_priv->scan.in_progress)
|| atomic_read(&priv->connect_in_process))) {
@@ -2587,7 +2567,7 @@ void bes2600_ba_work(struct work_struct *work)
void bes2600_ba_timer(struct timer_list *t)
{
bool ba_ena;
struct bes2600_common *hw_priv = timer_container_of(hw_priv, t, ba_timer);
struct bes2600_common *hw_priv = from_timer(hw_priv, t, ba_timer);
spin_lock_bh(&hw_priv->ba_lock);
bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc,
@@ -2809,7 +2789,6 @@ void bes2600_dynamic_opt_txrx_work(struct work_struct *work)
if (priv != NULL && priv->join_status > BES2600_JOIN_STATUS_MONITOR) {
multivif_connected = true;
}
bes2600_txrx_opt_multivif_connected_handler(hw_priv, multivif_connected);
}
@@ -3654,7 +3633,7 @@ static int bes2600_set_power_save(struct ieee80211_hw *hw,
*
* Returns: 0 on success or non zero value on failure
*/
int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
static 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;
@@ -3684,7 +3663,7 @@ int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
*
* Returns: TSM parameters collected
*/
int bes2600_get_tsm_params(struct ieee80211_hw *hw)
static int bes2600_get_tsm_params(struct ieee80211_hw *hw)
{
struct bes2600_common *hw_priv = hw->priv;
struct bes_tsm_stats tsm_stats;
@@ -3724,7 +3703,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw)
*
* Returns: Returns the last measured roam delay
*/
int bes2600_get_roam_delay(struct ieee80211_hw *hw)
static 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;
+2 -2
View File
@@ -26,7 +26,7 @@ int bes2600_change_interface(struct ieee80211_hw *dev,
enum nl80211_iftype new_type,
bool p2p);
int bes2600_config(struct ieee80211_hw *dev, int radio_idx, u32 changed);
int bes2600_config(struct ieee80211_hw *dev, u32 changed);
int bes2600_change_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
enum nl80211_iftype new_type,
@@ -48,7 +48,7 @@ int bes2600_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key);
int bes2600_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value);
int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
void bes2600_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop);
+1 -24
View File
@@ -21,7 +21,6 @@
#include "debug.h"
#include "sta.h"
#include "sbus.h"
#include "txrx_opt.h"
#include "bes_log.h"
#define BES2600_INVALID_RATE_ID (0xFF)
@@ -1474,35 +1473,14 @@ void bes2600_skb_dtor(struct bes2600_common *hw_priv,
struct bes2600_vif *priv =
__cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
if (!skb)
return;
/*
* There should be no reason for skb buffer being larger
* than the offset..
*/
if(WARN_ON(txpriv->offset > skb->len)) {
ieee80211_free_txskb(hw_priv->hw, skb);
return;
}
bes_devel("%s: txpriv->offset: %d - skb->len: %d\n",
__func__, txpriv->offset, skb->len);
skb_pull(skb, txpriv->offset);
if (priv && txpriv->rate_id != BES2600_INVALID_RATE_ID) {
bes2600_notify_buffered_tx(priv, skb,
txpriv->raw_link_id, txpriv->tid);
tx_policy_put(hw_priv, txpriv->rate_id);
}
if (likely(!bes2600_is_itp(hw_priv))) {
if (priv) {
/* The interface may be already removed */
bes2600_tx_status(priv, skb);
}
if (likely(!bes2600_is_itp(hw_priv)))
ieee80211_tx_status_skb(hw_priv->hw, skb);
}
}
#ifdef CONFIG_BES2600_TESTMODE
@@ -1871,7 +1849,6 @@ void bes2600_rx_cb(struct bes2600_vif *priv,
if (ieee80211_is_data(frame->frame_control)) {
bes2600_rx_h_ba_stat(priv, hdrlen, skb->len);
bes2600_rx_status(priv, skb);
}
#ifdef CONFIG_BES2600_TESTMODE
-569
View File
@@ -1,569 +0,0 @@
/***************************************************************************
*
* Copyright 2015-2022 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#include <net/mac80211.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/timer.h>
#include "bes2600.h"
#include "wsm.h"
#include "bh.h"
#include "ap.h"
#include "debug.h"
#include "sta.h"
#include "sbus.h"
#include "bes_pwr.h"
#include "txrx_opt.h"
#define TXRX_OPT_CLOSE_EDCA 0
#define TXRX_OPT_EDCA_MAX_LEVEL 4
#define TX_AVG_TIME_COUNT 10
#define TXRX_OPT_PEROID 500
#define TXRX_OPT_DEBUG 1
#define TXRX_HIGH_TP_THRESHOLD_2G4 30000 // unit is kbps
#define TXRX_HIGH_TP_THRESHOLD_5G 40000 // unit is kbps
#define TXRX_HIGH_TP_DELTA_TIME_2G4 8 // unit ms
#define TXRX_HIGH_TP_DELTA_TIME_5G 6 // unit ms
#define TXRX_RTS_PROT_TRIG_THRESH 80 // percent * 100
#define TXRX_RTS_PROT_DURATION 10 // unit second
#define TXRX_RTS_PROT_OPEN(x) (x = 512)
#define TXRX_RTS_PROT_CLOSE(x) (x = 2437)
#define TXRX_RTS_PROT_OPENED(x) (x < 1536)
static uint32_t tx_delta_time_arr[4][TX_AVG_TIME_COUNT];
static uint32_t tx_queue_arr[4] = {0};
static uint32_t tx_delta_time_total = 0;
static uint32_t tx_delta_time_total_cnt = 0;
static u8 cur_pwr_tbl = 1;
static u16 cur_rts_thres = 2437;
static unsigned long last_rts_set_time = -1;
void bes2600_add_tx_delta_time(uint32_t tx_delta_time)
{
tx_delta_time_total += tx_delta_time;
tx_delta_time_total_cnt++;
}
static uint32_t bes2600_get_tx_delta_time(void)
{
if (tx_delta_time_total_cnt != 0)
return tx_delta_time_total / tx_delta_time_total_cnt;
else
return 0;
}
static void bes2600_clear_tx_delta_time(void)
{
tx_delta_time_total_cnt = 0;
tx_delta_time_total = 0;
return ;
}
static uint32_t bes2600_get_tx_ac_delta_time(int ac)
{
uint32_t avg_time = 0;
int i = 0;
for (i = 0; i < TX_AVG_TIME_COUNT; i++) {
avg_time += tx_delta_time_arr[ac][i];
}
return avg_time / TX_AVG_TIME_COUNT;
}
static void bes2600_clear_tx_ac_delta_time(int ac)
{
int i = 0;
for (i = 0; i < TX_AVG_TIME_COUNT; i++) {
tx_delta_time_arr[ac][i] = 0;
}
return ;
}
void bes2600_add_tx_ac_delta_time(int ac, uint32_t del_time)
{
#if 0
if (tx_queue_arr[ac] >= (TX_AVG_TIME_COUNT - 1)) {
static int num = 0;
if ((num ++ % 10) == 0)
bes_devel( "%s %d %d %d %d %d del=%d\n\r", __func__, tx_delta_time_arr[ac][0],
tx_delta_time_arr[ac][2], tx_delta_time_arr[ac][4], tx_delta_time_arr[ac][6],
tx_delta_time_arr[ac][8], del_time);
}
#endif
tx_delta_time_arr[ac][tx_queue_arr[ac]] = del_time;
tx_queue_arr[ac] = (tx_queue_arr[ac] >= (TX_AVG_TIME_COUNT - 1)) ? 0 : (tx_queue_arr[ac] + 1);
}
static int bes2600_set_txrx_opt_param(struct bes2600_common *hw_priv,
struct bes2600_vif *priv,
MIB_TXRX_OPT_PARAM *para)
{
int ret = 0;
ret = WARN_ON(wsm_write_mib(hw_priv,
WSM_MIB_ID_EXT_TXRX_OPT_PARAM,
(u8 *)para,
sizeof(MIB_TXRX_OPT_PARAM),
priv->if_id));
return ret;
}
static int bes2600_enable_tx_shortgi(struct bes2600_common *hw_priv,
struct bes2600_vif *priv,
u8 onoff)
{
int ret = 0;
static u8 en = 0xff;
bes_devel( "%s onoff=%d\n\r", __func__, onoff);
if (en != onoff) {
en = onoff;
ret = WARN_ON(wsm_write_mib(hw_priv,
WSM_MIB_ID_EXT_TX_SHORT_GI_ENABLED,
(u8 *)&onoff,
sizeof(onoff),
priv->if_id));
}
return ret;
}
void bes2600_rx_status(struct bes2600_vif *priv, struct sk_buff *skb)
{
priv->dot11ReceivedFragmentCount++;
priv->dot11ReceivedDataBytes += skb->len;
}
void bes2600_tx_status(struct bes2600_vif *priv, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int i;
int retry_count = -1;
if(WARN_ON(!priv->hw))
return;
if (!ieee80211_is_data(hdr->frame_control))
return;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
if (info->status.rates[i].idx < 0)
break;
retry_count += info->status.rates[i].count;
}
if (retry_count < 0)
retry_count = 0;
if (info->flags & IEEE80211_TX_STAT_ACK) {
priv->dot11TransmittedFrameCount++;
priv->dot11TransmittedDataBytes += (skb->len + 4);
if (retry_count > 0)
priv->dot11RetryCount += retry_count;
} else {
/* tx fail.*/
priv->dot11FailedCount++;
}
}
void bes2600_set_default_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv);
static int bes2600_set_high_edca_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int level)
{
struct wsm_edca_params arg;
int i = 0;
static int lev = 0;
bes_devel( "set edca level=%d\n\r", level);
if (lev == level)
return 0;
lev = level;
memcpy(&arg, &(priv->edca), sizeof(struct wsm_edca_params));
if (level == 0) {
bes2600_set_default_params(hw_priv, priv);
return 0;
} else if (level == 1) {
for ( i = 0; i < 4; i++) {
arg.params[i].aifns = 2;
arg.params[i].cwMax = 7;
arg.params[i].cwMin = 3;
/*
* tx op must set 0
* set other, some AP may not response BA when rx data.
*/
arg.params[i].txOpLimit = 0;
arg.params[i].maxReceiveLifetime = 0xc8;
}
} else if (level == 2) {
for ( i = 0; i < 4; i++) {
arg.params[i].aifns = 2;
arg.params[i].cwMax = 5;
arg.params[i].cwMin = 1;
arg.params[i].txOpLimit = 0;
arg.params[i].maxReceiveLifetime = 0xc8;
}
} else if (level == 3) {
for ( i = 0; i < 4; i++) {
arg.params[i].aifns = 2;
arg.params[i].cwMax = 3;
arg.params[i].cwMin = 1;
arg.params[i].txOpLimit = 0;
arg.params[i].maxReceiveLifetime = 0xc8;
}
} else if (level == 4) {
for ( i = 0; i < 4; i++) {
arg.params[i].aifns = 1;
arg.params[i].cwMax = 3;
arg.params[i].cwMin = 1;
arg.params[i].txOpLimit = 0;
arg.params[i].maxReceiveLifetime = 0xc8;
}
}
wsm_set_edca_params(hw_priv, &arg, priv->if_id);
return 0;
}
void bes2600_set_default_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv)
{
bes_devel( "set edca default\n\r");
wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id);
}
static void bes2600_set_cca_method(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int value)
{
// todo set cca alg
}
static void bes2600_set_dynamic_agc(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int value)
{
// todo set agc alg
}
static int bes2600_update_pwr_table(struct bes2600_common *hw_priv,
struct bes2600_vif *priv,
u8 pwr_tbl_idx)
{
int ret = 0;
static u8 cur_pwr_tbl_idx = 0xff;
if (cur_pwr_tbl_idx != pwr_tbl_idx) {
cur_pwr_tbl_idx = pwr_tbl_idx;
ret = WARN_ON(wsm_write_mib(hw_priv,
WSM_MIB_ID_EXT_PWR_TBL_UPDATE,
(u8 *)&cur_pwr_tbl_idx,
sizeof(cur_pwr_tbl_idx),
priv->if_id));
bes_devel( "%s pwr_tbl_idx=%d\n\r", __func__, pwr_tbl_idx);
}
return ret;
}
static int bes2600_get_tx_av_max_delta_time(void)
{
int max_avg = 0;
int i = 0;
for (i = 0; i < 4; i++) {
if (max_avg < bes2600_get_tx_ac_delta_time(i)) {
max_avg = bes2600_get_tx_ac_delta_time(i);
}
//bes2600_clear_tx_ac_delta_time(i);
}
return max_avg;
}
static bool bes2600_station_is_ap_ht40(struct bes2600_common *hw_priv)
{
if (hw_priv->hw) {
struct ieee80211_conf *conf = &hw_priv->hw->conf;
if (conf != NULL)
if (conf->chandef.width == NL80211_CHAN_WIDTH_40)
return true;
}
return false;
}
void bes2600_dynamic_opt_rxtx(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int rssi)
{
u32 succPro = 0, tx_cnt, tx_retry, rx_cnt, tx_fail;
static u32 l_tx_cnt = 0, l_tx_fail = 0, l_tx_retry = 0, l_rx_cnt = 0;
static u32 tx_bps = 0, rx_bps = 0;
u32 total_kbps = 0;
static int level;
/* calculate real time throughput */
if (hw_priv == NULL || priv == NULL) {
return;
}
tx_bps = abs (priv->dot11TransmittedDataBytes - tx_bps);
rx_bps = abs (priv->dot11ReceivedDataBytes - rx_bps);
total_kbps = (tx_bps / 128 + rx_bps / 128);
total_kbps *= 1000;
total_kbps /= TXRX_OPT_PEROID;
/* if tx/rx < 100k/s, close*/
if (total_kbps < 100) {
level = 0;
last_rts_set_time = -1;
TXRX_RTS_PROT_CLOSE(cur_rts_thres);
goto txrx_opt_clear;
}
/* calculate tx_cnt, tx_retry, rx_cnt */
tx_cnt = (priv->dot11TransmittedFrameCount - l_tx_cnt);
tx_fail = (priv->dot11FailedCount - l_tx_fail);
tx_retry = (priv->dot11RetryCount - l_tx_retry);
rx_cnt = (priv->dot11ReceivedFragmentCount - l_rx_cnt);
( (tx_cnt + tx_retry) > 0 ) ? (succPro = tx_cnt * 100 / (tx_cnt + tx_retry)) : (succPro = 0);
bes_devel( "%s, tx_cnt:%d prob:%d\n", __func__, tx_cnt, succPro);
/* set rts/cts protection dynamically */
if (tx_cnt > 50 && succPro != 0) {
if (succPro > TXRX_RTS_PROT_TRIG_THRESH &&
TXRX_RTS_PROT_OPENED(cur_rts_thres) &&
time_after(jiffies, last_rts_set_time + TXRX_RTS_PROT_DURATION * HZ)) {
TXRX_RTS_PROT_CLOSE(cur_rts_thres);
} else if (succPro <= TXRX_RTS_PROT_TRIG_THRESH){
TXRX_RTS_PROT_OPEN(cur_rts_thres);
last_rts_set_time = jiffies;
}
}
/* dynamic set edca param */
if (succPro != 0) {
if (bes2600_station_is_ap_ht40(hw_priv)) {
if (bes2600_get_tx_delta_time() > 8 || bes2600_get_tx_av_max_delta_time() > 8) {
if (level < 4)
level++;
} else {
if (level > 0)
level--;
}
/* high throughput force level = 0 */
if (total_kbps > TXRX_HIGH_TP_THRESHOLD_5G && level > 0 && priv->hw_value > 19) {
level = 0;
}
} else {//shiled room 13, office 8
if (bes2600_get_tx_delta_time() > (TXRX_HIGH_TP_DELTA_TIME_5G + total_kbps / 8000)
|| bes2600_get_tx_av_max_delta_time() > (TXRX_HIGH_TP_DELTA_TIME_5G + total_kbps / 8000)) {
if (level < 4)
level++;
} else {
if (level > 0)
level--;
}
/* high throughput force level = 0 */
if (total_kbps > TXRX_HIGH_TP_THRESHOLD_2G4 && level > 0) {
level = 0;
}
}
}
/* dynamic set power table */
if (rssi <= BES2600_TX_RSSI_LOW)
cur_pwr_tbl = 2; // use high power table
else if(rssi >= BES2600_TX_RSSI_HIGH)
cur_pwr_tbl = 1; // use standard power table
#if TXRX_OPT_CLOSE_EDCA
level = 0;
#endif
if (level > TXRX_OPT_EDCA_MAX_LEVEL)
level = TXRX_OPT_EDCA_MAX_LEVEL;
bes_devel( "txrx_opt: tx(cnt=%d retry=%d psr=%d tx_fail=%d (wsm level=%d) tx=%dk/s)\n\r",
tx_cnt, tx_retry, succPro, tx_fail, level, tx_bps / 128);
bes_devel( "txrx_opt: rx(cnt=%d rx=%dk/s) total=%dk/s\n\r", rx_cnt, rx_bps / 128, total_kbps);
bes_devel( "txrx_opt: tx_delta_time=%d [%d %d %d %d] hw_value=%d ht=%d maxtxcnt=%d\n\r",
bes2600_get_tx_delta_time(), bes2600_get_tx_ac_delta_time(0), bes2600_get_tx_ac_delta_time(1),
bes2600_get_tx_ac_delta_time(2), bes2600_get_tx_ac_delta_time(3), priv->hw_value,
bes2600_station_is_ap_ht40(hw_priv), hw_priv->long_frame_max_tx_count);
/* dynamic set cca */
bes2600_set_cca_method(hw_priv, priv, 0);
/* dynamic set agc */
bes2600_set_dynamic_agc(hw_priv, priv, 0);
bes2600_update_pwr_table(hw_priv, priv, cur_pwr_tbl);
txrx_opt_clear:
bes2600_set_high_edca_params(hw_priv, priv, level);
bes2600_set_rts_threshold(hw_priv->hw, -1, cur_rts_thres);
bes2600_clear_tx_delta_time();
bes2600_clear_tx_ac_delta_time(0);
bes2600_clear_tx_ac_delta_time(1);
bes2600_clear_tx_ac_delta_time(2);
bes2600_clear_tx_ac_delta_time(3);
tx_bps = priv->dot11TransmittedDataBytes;
rx_bps = priv->dot11ReceivedDataBytes;
l_tx_cnt = priv->dot11TransmittedFrameCount;
l_tx_fail = priv->dot11FailedCount;
l_tx_retry = priv->dot11RetryCount;
l_rx_cnt = priv->dot11ReceivedFragmentCount;
return ;
}
static struct bes2600_common *txrx_hw_priv = NULL;
static bool bes2600_is_sta_connected(void)
{
if (txrx_hw_priv == NULL)
return false;
else
return true;
}
void bes2600_txrx_opt_timer_restore(void)
{
if (bes2600_is_sta_connected()) {
mod_timer(&txrx_hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID));
}
}
static void txrx_opt_timer_callback(struct timer_list* data)
{
bes_devel( "####Timer callback function Called time = %lu\n", jiffies);
queue_work(txrx_hw_priv->workqueue, &txrx_hw_priv->dynamic_opt_txrx_work);
}
static void txrx_opt_timer_start(struct bes2600_common *hw_priv)
{
mod_timer(&hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID));
}
static void txrx_opt_timer_stop(struct bes2600_common *hw_priv)
{
timer_delete_sync(&hw_priv->txrx_opt_timer);
}
static void bes2600_set_txrx_opt_default_param(struct bes2600_common * hw_priv)
{
MIB_TXRX_OPT_PARAM g_txrx_param = {2, (PROCTECT_MODE_RTS_CTS | PROCTECT_MODE_RTS_CTS_RETRY), 3000};
struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
struct ieee80211_sta *sta = NULL;
int shortgi = 0;
if (priv == NULL)
return;
/* reset states */
cur_pwr_tbl = 1;
TXRX_RTS_PROT_CLOSE(cur_rts_thres);
last_rts_set_time = -1;
memcpy(&hw_priv->txrx_opt_param, &g_txrx_param, sizeof(MIB_TXRX_OPT_PARAM));
/* reset device states */
bes2600_set_txrx_opt_param(hw_priv, priv, &hw_priv->txrx_opt_param);
bes2600_set_rts_threshold(hw_priv->hw, -1, cur_rts_thres); // close rts/cts
bes2600_update_pwr_table(hw_priv, priv, cur_pwr_tbl); // use standard pwr table
if (priv->join_status == BES2600_JOIN_STATUS_STA) {
rcu_read_lock();
sta = ieee80211_find_sta(priv->vif, priv->vif->bss_conf.bssid);
if (sta) {
if (sta->deflink.ht_cap.ht_supported &&
((priv->vif->bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20 &&
sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ||
(priv->vif->bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_40 &&
sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40))) {
shortgi = 1;
}
}
rcu_read_unlock();
bes_devel("short gi tx status: %d\n", shortgi);
bes2600_enable_tx_shortgi(hw_priv, priv, shortgi);
}
}
static int bes2600_set_txrx_opt_unjoin_param(struct bes2600_common * hw_priv)
{
MIB_TXRX_OPT_PARAM g_txrx_param = {1, 0, 2002};
struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
if (priv == NULL)
return 0;
/* reset states */
cur_pwr_tbl = 1;
bes2600_update_pwr_table(hw_priv, priv, cur_pwr_tbl);
memcpy(&hw_priv->txrx_opt_param, &g_txrx_param, sizeof(MIB_TXRX_OPT_PARAM));
bes2600_set_txrx_opt_param(hw_priv, priv, &hw_priv->txrx_opt_param);
return 0;
}
void bes2600_txrx_opt_multivif_connected_handler(struct bes2600_common *hw_priv, bool multivif_connected)
{
struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
if (multivif_connected) {
bes2600_set_txrx_opt_default_param(hw_priv);
} else {
bes_devel("%s, rssi:%d\n", __func__, priv->signal);
bes2600_dynamic_opt_rxtx(hw_priv, priv, priv->signal);
mod_timer(&hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID));
}
}
int txrx_opt_timer_init(struct bes2600_vif *priv)
{
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
bes_devel( "txrx_opt_timer_init:%p", txrx_hw_priv);
if (priv->if_id != 0)
return 0;
if (!txrx_hw_priv) {
txrx_hw_priv = hw_priv;
bes_devel( "####Timer init hw_priv = %p\n", txrx_hw_priv);
timer_setup(&hw_priv->txrx_opt_timer, txrx_opt_timer_callback, 0);
bes2600_set_txrx_opt_default_param(hw_priv);
}
mod_timer(&hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID));
bes2600_pwr_register_en_lp_cb(hw_priv, txrx_opt_timer_stop);
bes2600_pwr_register_exit_lp_cb(hw_priv, txrx_opt_timer_start);
return 0;
}
void txrx_opt_timer_exit(struct bes2600_vif *priv)
{
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
bes_devel( "txrx_opt_timer_exit");
if (priv->if_id == 0) {
timer_delete_sync(&hw_priv->txrx_opt_timer);
cancel_work_sync(&hw_priv->dynamic_opt_txrx_work);
bes2600_pwr_unregister_en_lp_cb(hw_priv, txrx_opt_timer_stop);
bes2600_pwr_unregister_exit_lp_cb(hw_priv, txrx_opt_timer_start);
txrx_hw_priv = NULL;
bes2600_set_txrx_opt_unjoin_param(hw_priv);
} else if (priv->if_id == 1) {
bes2600_txrx_opt_timer_restore();
}
}
-38
View File
@@ -1,38 +0,0 @@
/***************************************************************************
*
* Copyright 2015-2022 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#ifndef bes2600_TXRX_OPT_H
#define bes2600_TXRX_OPT_H
#include <linux/list.h>
/* open it for enhance wifi throughput */
#define BES2600_TX_RX_OPT 1
/* Threshold for powrt table switch */
#define BES2600_TX_RSSI_LOW -65
#define BES2600_TX_RSSI_HIGH -60
void bes2600_add_tx_ac_delta_time(int ac, uint32_t del_time);
void bes2600_add_tx_delta_time(uint32_t tx_time);
void bes2600_rx_status(struct bes2600_vif *priv, struct sk_buff *skb);
void bes2600_tx_status(struct bes2600_vif *priv, struct sk_buff *skb);
void bes2600_dynamic_opt_rxtx(struct bes2600_common *hw_priv,struct bes2600_vif *priv, int rssi);
void bes2600_txrx_opt_multivif_connected_handler(struct bes2600_common *hw_priv, bool multivif_connected);
void bes2600_txrx_opt_timer_restore(void);
int txrx_opt_timer_init(struct bes2600_vif *priv);
void txrx_opt_timer_exit(struct bes2600_vif *priv);
#endif
-2
View File
@@ -2236,7 +2236,5 @@ 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 */