diff --git a/bes2600/ap.c b/bes2600/ap.c index 71660d4..7b1e3b4 100644 --- a/bes2600/ap.c +++ b/bes2600/ap.c @@ -17,6 +17,7 @@ #include #include "epta_request.h" #include "epta_coex.h" +#include "txrx_opt.h" #ifdef AP_HT_CAP_UPDATE #define HT_INFO_OFFSET 4 @@ -1154,7 +1155,7 @@ void bes2600_multicast_stop_work(struct work_struct *work) container_of(work, struct bes2600_vif, multicast_stop_work); if (priv->aid0_bit_set) { - del_timer_sync(&priv->mcast_timeout); + timer_delete_sync(&priv->mcast_timeout); wsm_lock_tx(priv->hw_priv); priv->aid0_bit_set = false; bes2600_set_tim_impl(priv, false); @@ -1164,7 +1165,7 @@ void bes2600_multicast_stop_work(struct work_struct *work) void bes2600_mcast_timeout(struct timer_list *t) { - struct bes2600_vif *priv = from_timer(priv, t, mcast_timeout); + struct bes2600_vif *priv = timer_container_of(priv, t, mcast_timeout); wiphy_warn(priv->hw->wiphy, "Multicast delivery timeout.\n"); @@ -1238,7 +1239,7 @@ void bes2600_suspend_resume(struct bes2600_vif *priv, } spin_unlock_bh(&priv->ps_state_lock); if (cancel_tmo) - del_timer_sync(&priv->mcast_timeout); + timer_delete_sync(&priv->mcast_timeout); } else { spin_lock_bh(&priv->ps_state_lock); bes2600_ps_notify(priv, arg->link_id, arg->stop); diff --git a/bes2600/bes2600_btuart.c b/bes2600/bes2600_btuart.c new file mode 100644 index 0000000..f56f357 --- /dev/null +++ b/bes2600/bes2600_btuart.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Bestechnic BES2600 BT UART driver + * + * Copyright (c) 2025 Dang Huynh + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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 "); +MODULE_DESCRIPTION("BES2600 BT UART driver"); +MODULE_LICENSE("GPL"); diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c index b595365..13d4ff1 100644 --- a/bes2600/bes2600_sdio.c +++ b/bes2600/bes2600_sdio.c @@ -1879,7 +1879,6 @@ 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; @@ -1914,8 +1913,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_core_release(bus_priv->core); bes2600_pwr_unregister_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb); + bes2600_core_release(bus_priv->core); bus_priv->core = NULL; if (bus_priv->sdio_wq) { diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c index 9038e48..f89dcb8 100644 --- a/bes2600/bes_chardev.c +++ b/bes2600/bes_chardev.c @@ -196,7 +196,7 @@ static int bes2600_switch_wifi(bool on) return ret; } -int bes2600_switch_bt(bool on) +static int bes2600_switch_bt(bool on) { int ret = 0; long status = 0; @@ -229,11 +229,11 @@ 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_info("enable BT\n"); + bes_devel("bes2600 activate bt.\n"); ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true); } } else { - bes_info("disable BT\n"); + bes_devel("bes2600 deactivate bt.\n"); bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_OFF, SUBSYSTEM_BT, false); } @@ -249,6 +249,47 @@ 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; @@ -1085,6 +1126,7 @@ 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) { @@ -1106,6 +1148,7 @@ bool bes2600_chrdev_is_bus_error(void) return error; } +EXPORT_SYMBOL_GPL(bes2600_chrdev_is_bus_error); void bes2600_chrdev_update_signal_mode(void) { diff --git a/bes2600/bes_chardev.h b/bes2600/bes_chardev.h index 15602ba..c627bb7 100644 --- a/bes2600/bes_chardev.h +++ b/bes2600/bes_chardev.h @@ -63,6 +63,7 @@ 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); @@ -91,6 +92,4 @@ 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__ */ diff --git a/bes2600/bh.c b/bes2600/bh.c index 6385312..175ab5e 100644 --- a/bes2600/bh.c +++ b/bes2600/bh.c @@ -22,6 +22,7 @@ #include "debug.h" #include "epta_coex.h" #include "bes_chardev.h" +#include "txrx_opt.h" #include "sta.h" #include "bes_log.h" @@ -1259,6 +1260,8 @@ 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; @@ -1267,8 +1270,12 @@ 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); @@ -1304,14 +1311,14 @@ void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx) } if (--hw_priv->wsm_tx_pending[idx] == 0) - del_timer_sync(timer); + timer_delete_sync(timer); else mod_timer(timer, jiffies + 3 * HZ); } void bes2600_bh_mcu_active_monitor(struct timer_list* t) { - struct bes2600_common *hw_priv = from_timer(hw_priv, t, mcu_mon_timer); + struct bes2600_common *hw_priv = timer_container_of(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]); @@ -1320,7 +1327,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 = from_timer(hw_priv, t, lmac_mon_timer); + struct bes2600_common *hw_priv = timer_container_of(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]); diff --git a/bes2600/h4_recv.h b/bes2600/h4_recv.h new file mode 100644 index 0000000..5349e00 --- /dev/null +++ b/bes2600/h4_recv.h @@ -0,0 +1,154 @@ +/* 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 + +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; +} diff --git a/bes2600/main.c b/bes2600/main.c index 6ed6b15..3b0b7a3 100644 --- a/bes2600/main.c +++ b/bes2600/main.c @@ -32,6 +32,7 @@ #include "pm.h" #include "bes2600_factory.h" #include "bes_chardev.h" +#include "txrx_opt.h" MODULE_AUTHOR("Dmitry Tarnyagin "); MODULE_DESCRIPTION("Softmac BES2600 common code"); @@ -198,7 +199,11 @@ static const struct ieee80211_iface_limit bes2600_if_limits[] = { BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) }, #ifdef P2P_MULTIVIF - { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, + /* + * HACK: Disable P2P_DEVICE implementation for BES2600 + * as the code is a little buggy. + */ + //{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, #endif }; @@ -593,7 +598,7 @@ static void bes2600_unregister_common(struct ieee80211_hw *dev) ieee80211_unregister_hw(dev); - del_timer_sync(&hw_priv->ba_timer); + timer_delete_sync(&hw_priv->ba_timer); hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv); bes2600_unregister_bh(hw_priv); @@ -896,8 +901,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; - del_timer_sync(&hw_priv->mcu_mon_timer); - del_timer_sync(&hw_priv->lmac_mon_timer); + timer_delete_sync(&hw_priv->mcu_mon_timer); + timer_delete_sync(&hw_priv->lmac_mon_timer); #ifdef CONFIG_BES2600_STATIC_SDD hw_priv->sdd = NULL; #else diff --git a/bes2600/queue.c b/bes2600/queue.c index f050df6..d1b407b 100644 --- a/bes2600/queue.c +++ b/bes2600/queue.c @@ -119,10 +119,9 @@ static void bes2600_queue_register_post_gc(struct list_head *gc_list, struct bes2600_queue_item *item) { struct bes2600_queue_item *gc_item; - gc_item = kmalloc(sizeof(struct bes2600_queue_item), + gc_item = kmemdup(item, 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); } @@ -131,9 +130,9 @@ static void bes2600_queue_pending_record(struct list_head *pending_record_list, { struct bes2600_queue_item *record_item; - record_item = kmalloc(sizeof(struct bes2600_queue_item),GFP_ATOMIC); + record_item = kmemdup(pending_item, 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); } @@ -196,7 +195,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 = from_timer(queue, t, gc); + struct bes2600_queue *queue = timer_container_of(queue, t, gc); spin_lock_bh(&queue->lock); __bes2600_queue_gc(queue, &list, true); @@ -218,7 +217,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] = kzalloc(map_capacity * sizeof(int), + stats->link_map_cache[i] = kcalloc(map_capacity, sizeof(int), GFP_KERNEL); if (!stats->link_map_cache[i]) { for (; i >= 0; i--) @@ -249,14 +248,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 = kzalloc(sizeof(struct bes2600_queue_item) * capacity, + queue->pool = kcalloc(capacity, sizeof(struct bes2600_queue_item), GFP_KERNEL); if (!queue->pool) return -ENOMEM; for (i = 0; i < CW12XX_MAX_VIFS; i++) { queue->link_map_cache[i] = - kzalloc(stats->map_capacity * sizeof(int), + kcalloc(stats->map_capacity, sizeof(int), GFP_KERNEL); if (!queue->link_map_cache[i]) { for (; i >= 0; i--) @@ -363,7 +362,7 @@ void bes2600_queue_deinit(struct bes2600_queue *queue) int i; bes2600_queue_clear(queue, CW12XX_ALL_IFS); - del_timer_sync(&queue->gc); + timer_delete_sync(&queue->gc); INIT_LIST_HEAD(&queue->free_pool); kfree(queue->pool); for (i = 0; i < CW12XX_MAX_VIFS; i++) { @@ -410,7 +409,6 @@ 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 */ diff --git a/bes2600/scan.c b/bes2600/scan.c index b2c22e7..3bfa535 100644 --- a/bes2600/scan.c +++ b/bes2600/scan.c @@ -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_info(priv->hw->wiphy, + wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan completed.\n"); else - wiphy_info(priv->hw->wiphy, + wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan canceled.\n"); #ifdef WIFI_BT_COEXIST_EPTA_ENABLE @@ -621,8 +621,9 @@ void bes2600_scan_work(struct work_struct *work) scan.scanType = WSM_SCAN_TYPE_BACKGROUND; scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; } - scan.ch = kzalloc((it - hw_priv->scan.curr) * - sizeof(struct wsm_scan_ch), GFP_KERNEL); + scan.ch = kcalloc((it - hw_priv->scan.curr), + sizeof(struct wsm_scan_ch), + GFP_KERNEL); if (!scan.ch) { hw_priv->scan.status = -ENOMEM; goto fail; @@ -905,7 +906,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_info(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status); + wiphy_dbg(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status); if(hw_priv->scan.status == -ETIMEDOUT) wiphy_warn(hw_priv->hw->wiphy, diff --git a/bes2600/sta.c b/bes2600/sta.c index aa69eb8..ca1c77c 100644 --- a/bes2600/sta.c +++ b/bes2600/sta.c @@ -42,6 +42,8 @@ #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 @@ -219,7 +221,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); - del_timer_sync(&hw_priv->ba_timer); + timer_delete_sync(&hw_priv->ba_timer); down(&hw_priv->conf_lock); @@ -259,7 +261,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); - del_timer_sync(&priv->mcast_timeout); + timer_delete_sync(&priv->mcast_timeout); } #ifdef WIFI_BT_COEXIST_EPTA_ENABLE @@ -375,9 +377,23 @@ void bes2600_remove_interface(struct ieee80211_hw *dev, atomic_set(&priv->enabled, 0); down(&hw_priv->scan.lock); down(&hw_priv->conf_lock); - if (!__cw12xx_hwpriv_to_vifpriv(hw_priv, priv->if_id)) { + + /* + * 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))) { 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; @@ -449,7 +465,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); - del_timer_sync(&priv->mcast_timeout); + timer_delete_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 */ @@ -502,7 +518,7 @@ int bes2600_change_interface(struct ieee80211_hw *dev, return ret; } -int bes2600_config(struct ieee80211_hw *dev, u32 changed) +int bes2600_config(struct ieee80211_hw *dev, int radio_idx, u32 changed) { int ret = 0; struct bes2600_common *hw_priv = dev->priv; @@ -1137,7 +1153,7 @@ void bes2600_wep_key_work(struct work_struct *work) wsm_unlock_tx(hw_priv); } -int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +int bes2600_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value) { struct bes2600_common *hw_priv = hw->priv; int ret; @@ -2169,6 +2185,8 @@ 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) @@ -2252,6 +2270,8 @@ 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); @@ -2358,7 +2378,7 @@ void bes2600_unjoin_work(struct work_struct *work) int i; struct bes2600_vif *tmp_priv; - del_timer_sync(&hw_priv->ba_timer); + timer_delete_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))) { @@ -2567,7 +2587,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 = from_timer(hw_priv, t, ba_timer); + struct bes2600_common *hw_priv = timer_container_of(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, @@ -2789,6 +2809,7 @@ 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); } diff --git a/bes2600/sta.h b/bes2600/sta.h index e4e266c..39b4b1a 100644 --- a/bes2600/sta.h +++ b/bes2600/sta.h @@ -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, u32 changed); +int bes2600_config(struct ieee80211_hw *dev, int radio_idx, 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, u32 value); +int bes2600_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value); void bes2600_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop); diff --git a/bes2600/txrx.c b/bes2600/txrx.c index dbd1b23..017f0d8 100644 --- a/bes2600/txrx.c +++ b/bes2600/txrx.c @@ -21,6 +21,7 @@ #include "debug.h" #include "sta.h" #include "sbus.h" +#include "txrx_opt.h" #include "bes_log.h" #define BES2600_INVALID_RATE_ID (0xFF) @@ -1473,14 +1474,35 @@ 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 (likely(!bes2600_is_itp(hw_priv))) { + if (priv) { + /* The interface may be already removed */ + bes2600_tx_status(priv, skb); + } ieee80211_tx_status_skb(hw_priv->hw, skb); + } } #ifdef CONFIG_BES2600_TESTMODE @@ -1849,6 +1871,7 @@ 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 diff --git a/bes2600/txrx_opt.c b/bes2600/txrx_opt.c new file mode 100644 index 0000000..14cb31b --- /dev/null +++ b/bes2600/txrx_opt.c @@ -0,0 +1,569 @@ +/*************************************************************************** + * + * 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 +#include +#include +#include + +#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(); + } +} + + diff --git a/bes2600/txrx_opt.h b/bes2600/txrx_opt.h new file mode 100644 index 0000000..93c22f4 --- /dev/null +++ b/bes2600/txrx_opt.h @@ -0,0 +1,38 @@ + +/*************************************************************************** + * + * 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 +/* 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 +