Compare commits

..

1 Commits

Author SHA1 Message Date
claude-noether af4b52affc bes2600: pre-empt AP-deauth-6 with mac80211 reassoc on decrypt-fail storm
When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a burst
of received frames (typically because the host's PTK or GTK has fallen
out of sync with the AP), the AP eventually concludes that the STA is
not authenticated and emits an unprotected deauth-reason-6 ("Class 2
frame received from non-authenticated station"). On the deployed
pinetab2 + bes2600 stack this AP-initiated deauth has been observed to
leave the link blackholed for up to 109 s before userspace finds a
different SSID/channel to recover on. (Receipts at
https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.)

Add a sliding-window counter on each bes2600_vif: when 5 decrypt
failures fire within 5 s, schedule a worker that calls
ieee80211_connection_loss(vif). mac80211 then performs immediate
disassociation; userspace (NetworkManager / wpa_supplicant) reconnects
with fresh keys before the AP gets a chance to fire its unprotected
deauth.

Predicted Phase 7 delta vs the unpatched baseline:
- decrypt-burst rate: unchanged (this does not address root cause)
- AP-deauth-6 rate: <= 0.2 of baseline
- conditional probability of >5s blackhole given a burst:
  100% -> <= 10%
- worst-case recovery time: 109s -> <5s

Contract pin: ieee80211_connection_loss() per
include/net/mac80211.h: "may also be called if the connection needs to
be terminated for some other reason... will cause immediate change to
disassociated state, without connection recovery attempts." Userspace
recovery is the existing NM/wpa_supplicant path. The worker context
satisfies the implicit process-context expectation.

Files touched:
- bes2600/bes2600.h: 4 new fields on struct bes2600_vif + 2 prototypes
- bes2600/txrx.c: new helpers + the call site at the existing
  WSM_STATUS_DECRYPTFAILURE log point (the unconditional "goto drop"
  branch in bes2600_rx_cb)
- bes2600/sta.c: bes2600_decrypt_storm_init() in bes2600_vif_setup;
  cancel_work_sync() in bes2600_remove_interface, alongside the
  existing per-vif cancel_*_work_sync block. Safe under the kernel
  cancel_work_sync contract: the work_struct is INIT_WORK'd in setup,
  so the call is valid; it blocks until any in-flight handler returns,
  ensuring no use-after-free of priv when mac80211 frees the vif; and
  it is idempotent (subsequent calls just return false).
- bes2600/debug.c: DecryptStormRecoveries seq_printf in the per-vif
  status seq_file output

Threshold (5/5s) is set well above the steady-state per-vif decrypt-
fail rate observed in measurement (~1/min even under sustained 1 MB/s
load), so a true storm is required to trip it. The cw1200/cw1260
ancestor has no equivalent storm-recovery; this is a clean addition.

checkpatch.pl --no-tree --strict: clean (0/0/0).

Signed-off-by: Claude (noether) <claude@reauktion.de>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 20:43:42 +02:00
17 changed files with 130 additions and 1410 deletions
+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);
+9
View File
@@ -596,6 +596,11 @@ struct bes2600_vif {
unsigned long rx_timestamp;
u32 cipherType;
/* Decrypt-storm fast-recover (Trigger B). See txrx.c. */
unsigned long decrypt_storm_window_start;
unsigned int decrypt_storm_count;
unsigned int decrypt_storm_recoveries;
struct work_struct decrypt_storm_recover_work;
/* AP powersave */
u32 link_id_map;
@@ -856,4 +861,8 @@ int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv);
void bes2600_btusb_uninit(struct usb_interface *interface);
#endif
/* Decrypt-storm fast-recover helpers — see txrx.c. */
void bes2600_decrypt_storm_init(struct bes2600_vif *priv);
void bes2600_decrypt_storm_account(struct bes2600_vif *priv);
#endif /* BES2600_H */
-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");
+2 -1
View File
@@ -1879,6 +1879,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 +1914,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) {
+3 -46
View File
@@ -196,7 +196,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 +229,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,47 +249,6 @@ 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;
@@ -1126,7 +1085,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 +1106,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)
{
+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__ */
+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]);
+2
View File
@@ -542,6 +542,8 @@ static int bes2600_status_show_priv(struct seq_file *seq, void *v)
priv->listening ? " (listening)" : "");
seq_printf(seq, "Assoc: %s\n",
bes2600_debug_join_status[priv->join_status]);
seq_printf(seq, "DecryptStormRecoveries: %u\n",
priv->decrypt_storm_recoveries);
if (priv->rx_filter.promiscuous)
seq_puts(seq, "Filter: promisc\n");
else if (priv->rx_filter.fcs)
-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 -9
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);
@@ -901,8 +896,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,
+10 -29
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;
@@ -464,8 +448,9 @@ void bes2600_remove_interface(struct ieee80211_hw *dev,
cancel_delayed_work_sync(&priv->join_timeout);
cancel_delayed_work_sync(&priv->set_cts_work);
cancel_delayed_work_sync(&priv->pending_offchanneltx_work);
cancel_work_sync(&priv->decrypt_storm_recover_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 +503,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 +1138,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 +2170,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 +2253,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 +2359,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 +2568,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,
@@ -2639,6 +2620,7 @@ int bes2600_vif_setup(struct bes2600_vif *priv)
/* Setup per vif workitems and locks */
spin_lock_init(&priv->vif_lock);
bes2600_decrypt_storm_init(priv);
INIT_WORK(&priv->join_work, bes2600_join_work);
INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout);
INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work);
@@ -2809,7 +2791,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);
}
+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);
+75 -24
View File
@@ -21,11 +21,82 @@
#include "debug.h"
#include "sta.h"
#include "sbus.h"
#include "txrx_opt.h"
#include "bes_log.h"
#define BES2600_INVALID_RATE_ID (0xFF)
/*
* Decrypt-storm fast-recover (Trigger B).
*
* When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a
* burst of received frames (typically because the host's PTK or GTK
* has fallen out of sync with the AP), the AP eventually concludes that
* the STA is not authenticated and emits an unprotected deauth-reason-6
* ("Class 2 frame received from non-authenticated station"). On the
* deployed pinetab2 + bes2600 stack this AP-initiated deauth has been
* observed to leave the link blackholed for up to 109 s before
* userspace finds a different SSID/channel to recover on. (Receipts at
* https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.)
*
* Recovery here pre-empts the AP: when we see THRESHOLD decrypt
* failures within WINDOW, we ask mac80211 for a clean reassoc via
* ieee80211_connection_loss(), which causes immediate disassociation
* and lets userspace auto-reconnect with fresh keys.
*
* mac80211 contract: ieee80211_connection_loss() may be called
* regardless of IEEE80211_HW_CONNECTION_MONITOR; it causes immediate
* disassociation without driver-side recovery attempts. See
* include/net/mac80211.h for the canonical doc-comment.
*
* The threshold is set well above the steady-state per-vif
* decrypt-fail rate observed in measurement (~1/min even under
* sustained 1 MB/s load), so a true storm is required to trip it.
*/
#define BES2600_DECRYPT_STORM_THRESHOLD 5
#define BES2600_DECRYPT_STORM_WINDOW_MS 5000
static void bes2600_decrypt_storm_recover_work(struct work_struct *work)
{
struct bes2600_vif *priv = container_of(work, struct bes2600_vif,
decrypt_storm_recover_work);
if (!priv->vif)
return;
bes_warn("[bes2600] decrypt-storm fast-recover: forcing reassoc\n");
ieee80211_connection_loss(priv->vif);
priv->decrypt_storm_recoveries++;
}
void bes2600_decrypt_storm_init(struct bes2600_vif *priv)
{
INIT_WORK(&priv->decrypt_storm_recover_work,
bes2600_decrypt_storm_recover_work);
priv->decrypt_storm_window_start = 0;
priv->decrypt_storm_count = 0;
priv->decrypt_storm_recoveries = 0;
}
void bes2600_decrypt_storm_account(struct bes2600_vif *priv)
{
unsigned long now = jiffies;
unsigned long window = msecs_to_jiffies(BES2600_DECRYPT_STORM_WINDOW_MS);
if (priv->decrypt_storm_window_start == 0 ||
time_after(now, priv->decrypt_storm_window_start + window)) {
priv->decrypt_storm_window_start = now;
priv->decrypt_storm_count = 1;
return;
}
if (++priv->decrypt_storm_count >= BES2600_DECRYPT_STORM_THRESHOLD) {
priv->decrypt_storm_count = 0;
/* Skew the window so we don't re-fire on the same storm. */
priv->decrypt_storm_window_start = now + window;
schedule_work(&priv->decrypt_storm_recover_work);
}
}
#ifdef CONFIG_BES2600_TESTMODE
#include "bes_nl80211_testmode_msg.h"
#endif /* CONFIG_BES2600_TESTMODE */
@@ -1474,35 +1545,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
@@ -1694,6 +1744,8 @@ void bes2600_rx_cb(struct bes2600_vif *priv,
goto drop;
} else {
bes_warn("[RX] Receive failure: %d.\n", arg->status);
if (arg->status == WSM_STATUS_DECRYPTFAILURE)
bes2600_decrypt_storm_account(priv);
goto drop;
}
}
@@ -1871,7 +1923,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