Compare commits

..

2 Commits

Author SHA1 Message Date
test0r c57c77e446 bes2600: gate PM indication completion on pending request and track chip state
When mac80211 toggles PSM on the BES2600, the host sends WSM set_pm
and waits up to 5 s on bes_power.pm_enter_cmpl for a firmware-side
PM-changed indication confirming the transition. Three sequenced
flaws make the wait-and-confirm racy and leave host/chip bookkeeping
desynced when anything misfires:

  1) bes2600_pwr_notify_ps_changed() unconditionally fires
     complete(pm_enter_cmpl) for any non-active psmode. It does not
     check whether a host-initiated set_pm is actually pending. A
     spontaneous indication (firmware-internal coex move,
     idle-driven aging) primes the completion, and the next host-
     driven enter_lp_mode sees a false success on its first
     wait_for_completion_timeout.

  2) The wait/reinit ordering in bes2600_pwr_enter_lp_mode is

         status = wait_for_completion_timeout(...);
         atomic_set(pm_set_in_process, 0);
         reinit_completion(...);

     If an indication arrives between wait_for_completion_timeout
     returning with status==1 and reinit_completion, the next
     enter_lp_mode iteration's wait can also see false success. The
     reinit must happen *before* we start the new request, not
     after handling the previous one.

  3) On wait_pm_ind timeout, the driver returns -ETIMEDOUT and walks
     away. It does not record that the firmware's actual PM state
     is no longer known to the host. Subsequent wake paths
     (gpio_wake / sbus_active) assume the chip is still active and
     hit deterministic SDIO failures when the firmware has
     transitioned anyway.

This patch is the safe-prerequisite half of a wider fix:

  * bes_pwr.h gains enum bes2600_chip_pm_state {ACTIVE, LP, UNKNOWN}
    and bes_power.chip_pm_state. Its job is to track what the host
    has *seen the firmware confirm*, not what the host has
    requested. Initialised to ACTIVE in bes2600_pwr_init().

  * bes2600_pwr_notify_ps_changed() unconditionally updates
    chip_pm_state on every indication, but only fires
    complete(pm_enter_cmpl) when atomic_cmpxchg(pm_set_in_process,
    1, 0) succeeds. A spontaneous indication can no longer prime a
    waiter that will only set up its request afterwards.

  * bes2600_pwr_enter_lp_mode() now reinit_completion()s before
    setting pm_set_in_process and sending wsm_set_pm. After a
    timeout, it cmpxchgs pm_set_in_process back to 0 (so a late
    indication cannot prime the next iteration) and on the win-
    cmpxchg branch records chip_pm_state=UNKNOWN.

A follow-up patch consumes chip_pm_state on the wake side
(bes2600_pwr_device_exit_lp_mode + bes2600_gpio_wakeup_mcu) to fix
the deterministic "active mcu fail" cycle this state-record
enables a fix for. Splitting the work this way keeps the lock-free
race fix small and reviewable on its own.

No new locks, no behaviour change on the success path. Only the
recovery path (timeout + spontaneous indication) gains correctness.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-28 16:11:08 +02:00
test0r db4ea70fb5 bes2600: widen scan-defer backoff to 30s and decay count on quiet
The scan-defer logic added in the previous patch ("bes2600: defer
scan and soften WARN on firmware reject") used a 10-second backoff
window and never cleared reject_count outside of a successful scan.
Field testing on a PineTab2 (linux-pinetab2 6.19.10-danctnix1) shows
two distinct mac80211 scan-retry cadences in practice:

  * Idle background scans every ~5 minutes when associated -- well
    outside any plausible backoff, the defer guard correctly falls
    through to a real WSM scan attempt.

  * Roam-evaluation bursts triggered when mac80211 wants to find a
    candidate AP for handover (signal degradation, beacon loss,
    locally-generated DEAUTH_LEAVING reason=3). Cadence is ~12 s, and
    one boot reproduced 14 such rejected scans in 3 minutes during a
    single burst, none of which engaged the defer guard because every
    retry landed just outside the 10 s window.

Two-line behaviour change to fix that:

  1. BES2600_SCAN_BACKOFF_JIFFIES grows from 10*HZ to 30*HZ, so a
     12 s-cadence burst stays inside the window across consecutive
     rejects and the third reject in the burst trips the threshold
     guard. The 5 min idle case is still naturally past the window
     and is unaffected.

  2. bes2600_scan_should_defer() resets reject_count to 0 when
     time_after(jiffies, backoff_until). Without this, reject_count
     accumulated indefinitely across the slow-cadence rejects, so an
     isolated reject after long quiet would have tripped the
     threshold the moment it arrived. After the change, count is
     latched only inside an active burst and decays cleanly when the
     burst ends.

Net effect on a roam burst:

  * t=0   reject #1 (count 1, backoff_until = t0 + 30s)
  * t=12  reject #2 (count 2, backoff_until = t1 + 30s)
  * t=24  reject #3 (count 3, threshold met, next scan deferred)
  * t=36  defer fires, no WSM round-trip, reject not sent
  * ...   defers continue until the firmware-policy state clears
  * scan succeeds -> reject_count = 0, normal cadence resumes

WSM 0x0007 confirm rejections in a burst drop from ~14 to ~3 (just
the scans needed to reach the threshold). wpa_supplicant's reason=3
locally-generated disconnects driven by exhausted roam candidates
during the same burst window also drop.

No new state, no new symbols, no change to mac80211-facing semantics:
the deferred scan still completes via the existing fail: path with
status=-EBUSY, the same response a real firmware-busy would produce.

Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
2026-04-28 14:33:00 +02:00
48 changed files with 1031 additions and 1163 deletions
+8 -16
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* AP mode for BES2600 mac80211 driver * mac80211 STA and AP API for mac80211 BES2600 drivers
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include "bes2600.h" #include "bes2600.h"
@@ -62,11 +65,8 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct bes2600_link_entry *entry; struct bes2600_link_entry *entry;
struct sk_buff *skb; struct sk_buff *skb;
struct sk_buff_head local_drain;
struct bes2600_common *hw_priv = hw->priv; struct bes2600_common *hw_priv = hw->priv;
__skb_queue_head_init(&local_drain);
#ifdef P2P_MULTIVIF #ifdef P2P_MULTIVIF
WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
#endif #endif
@@ -95,17 +95,9 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
priv->sta_asleep_mask |= BIT(sta_priv->link_id); priv->sta_asleep_mask |= BIT(sta_priv->link_id);
entry->status = BES2600_LINK_HARD; entry->status = BES2600_LINK_HARD;
/* while ((skb = skb_dequeue(&entry->rx_queue)))
* Patch C2: splice the rx_queue out under the lock then deliver ieee80211_rx_irqsafe(priv->hw, skb);
* after unlock. ieee80211_rx_ni() runs the mac80211 RX path
* synchronously (formerly ieee80211_rx_irqsafe deferred to a
* tasklet); calling it from inside spin_lock_bh would hold the
* lock across mac80211's full RX dispatch.
*/
skb_queue_splice_init(&entry->rx_queue, &local_drain);
spin_unlock_bh(&priv->ps_state_lock); spin_unlock_bh(&priv->ps_state_lock);
while ((skb = __skb_dequeue(&local_drain)))
ieee80211_rx_ni(priv->hw, skb);
#ifdef AP_AGGREGATE_FW_FIX #ifdef AP_AGGREGATE_FW_FIX
hw_priv->connected_sta_cnt++; hw_priv->connected_sta_cnt++;
if(hw_priv->connected_sta_cnt>1) { if(hw_priv->connected_sta_cnt>1) {
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* AP mode interface for BES2600 mac80211 driver * mac80211 STA and AP API for mac80211 BES2600 drivers
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2010, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/version.h> #include <linux/version.h>
#ifndef AP_H_INCLUDED #ifndef AP_H_INCLUDED
+16 -42
View File
@@ -1,15 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Common private data for BES2600 mac80211 driver * Common private data for BES2600 drivers
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2010, Bestechnic
* Author:
* *
* Based on the mac80211 Prism54 code, which is * Based on the mac80211 Prism54 code, which is
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* *
* Based on the islsm (softmac prism54) driver, which is * Based on the islsm (softmac prism54) driver, which is:
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_H #ifndef BES2600_H
@@ -353,23 +356,15 @@ struct bes2600_common {
* Keeping in common structure for the time being. Will be moved to VIFF * Keeping in common structure for the time being. Will be moved to VIFF
* after the mechanism is clear */ * after the mechanism is clear */
u8 ba_tid_mask; u8 ba_tid_mask;
/* int ba_acc; /*TODO: Same as above */
* Patch D: ba_lock removed. Per-frame TX/RX hot-path bumped these int ba_cnt; /*TODO: Same as above */
* counters under spin_lock_bh; the lock did not protect any int ba_cnt_rx; /*TODO: Same as above */
* compound invariant that atomic ops can't satisfy. Counters are int ba_acc_rx; /*TODO: Same as above */
* now atomic_t; ba_armed gates the once-per-window mod_timer int ba_hist; /*TODO: Same as above */
* arm via cmpxchg so concurrent TX/RX at a fresh window each struct timer_list ba_timer;/*TODO: Same as above */
* try to claim the arm and exactly one succeeds. spinlock_t ba_lock; /*TODO: Same as above */
*/ bool ba_ena; /*TODO: Same as above */
atomic_t ba_acc; struct work_struct ba_work; /*TODO: Same as above */
atomic_t ba_cnt;
atomic_t ba_cnt_rx;
atomic_t ba_acc_rx;
atomic_t ba_armed;
int ba_hist;
struct timer_list ba_timer;
atomic_t ba_ena;
struct work_struct ba_work;
bool is_BT_Present; bool is_BT_Present;
bool is_go_thru_go_neg; bool is_go_thru_go_neg;
u8 conf_listen_interval; u8 conf_listen_interval;
@@ -516,9 +511,6 @@ struct bes2600_common {
struct list_head coex_event_list; struct list_head coex_event_list;
spinlock_t coex_event_lock; spinlock_t coex_event_lock;
/* Connection-loss-storm fast-recover (Trigger A). See sta.c. */
struct work_struct connection_loss_storm_recover_work;
/* member for low power */ /* member for low power */
struct bes2600_pwr_t bes_power; struct bes2600_pwr_t bes_power;
@@ -604,11 +596,6 @@ struct bes2600_vif {
unsigned long rx_timestamp; unsigned long rx_timestamp;
u32 cipherType; 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 */ /* AP powersave */
u32 link_id_map; u32 link_id_map;
@@ -635,10 +622,6 @@ struct bes2600_vif {
/* CQM Implementation */ /* CQM Implementation */
struct delayed_work bss_loss_work; struct delayed_work bss_loss_work;
struct delayed_work connection_loss_work; struct delayed_work connection_loss_work;
/* Connection-loss-storm fast-recover (Trigger A). See sta.c. */
unsigned long connection_loss_storm_window_start;
unsigned int connection_loss_storm_count;
unsigned int connection_loss_storm_recoveries;
struct work_struct tx_failure_work; struct work_struct tx_failure_work;
int delayed_link_loss; int delayed_link_loss;
spinlock_t bss_loss_lock; spinlock_t bss_loss_lock;
@@ -873,13 +856,4 @@ int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv);
void bes2600_btusb_uninit(struct usb_interface *interface); void bes2600_btusb_uninit(struct usb_interface *interface);
#endif #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);
/* Connection-loss-storm fast-recover helpers — see sta.c. */
void bes2600_connection_loss_storm_init(struct bes2600_vif *priv);
bool bes2600_connection_loss_storm_account(struct bes2600_vif *priv);
void bes2600_connection_loss_storm_recover(struct work_struct *work);
#endif /* BES2600_H */ #endif /* BES2600_H */
+6 -3
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Factory calibration loader for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/module.h> #include <linux/module.h>
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Factory calibration loader interface * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef __FACTORY_H__ #ifndef __FACTORY_H__
#define __FACTORY_H__ #define __FACTORY_H__
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Platform data for BES2600 SDIO bus * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2010, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_PLAT_H_INCLUDED #ifndef BES2600_PLAT_H_INCLUDED
#define BES2600_PLAT_H_INCLUDED #define BES2600_PLAT_H_INCLUDED
+67 -154
View File
@@ -1,13 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* SDIO bus glue for BES2600 mac80211 driver * Mac80211 SDIO driver for BES2600 device
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* Derived from drivers/net/wireless/st/cw1200/cw1200_sdio.c
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
* *
* Copyright (c) 2010, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#define DEBUG 1 #define DEBUG 1
#include <linux/version.h> #include <linux/version.h>
@@ -17,7 +16,6 @@
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/core.h>
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <net/mac80211.h> #include <net/mac80211.h>
@@ -30,7 +28,6 @@
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include "bes2600.h" #include "bes2600.h"
#include "bh.h"
#include "sbus.h" #include "sbus.h"
#include "bes2600_plat.h" #include "bes2600_plat.h"
#include "bes2600_factory.h" #include "bes2600_factory.h"
@@ -74,12 +71,10 @@ struct sbus_priv {
int rx_data_toggle; int rx_data_toggle;
#endif #endif
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE #ifdef BES_SDIO_RX_MULTIPLE_ENABLE
/* spinlock_t rx_queue_lock;
* Patch C v3: rx_queue, rx_queue_lock, rx_work removed (no relay). struct sk_buff_head rx_queue;
* The bh thread now reads RX inline; the rx_buffer scratch area
* stays. Counters/timestamps stay for debugfs visibility.
*/
u8 *rx_buffer; u8 *rx_buffer;
struct work_struct rx_work;
u32 rx_last_ctrl; u32 rx_last_ctrl;
u32 rx_valid_ctrl; u32 rx_valid_ctrl;
u32 rx_total_ctrl_cnt; u32 rx_total_ctrl_cnt;
@@ -416,17 +411,8 @@ static void bes2600_sdio_irq_handler(struct sdio_func *func)
bes_devel("%s called, fw_started:%d \n", bes_devel("%s called, fw_started:%d \n",
__func__, self->fw_started); __func__, self->fw_started);
/* if (likely(self->fw_started && self->core)) {
* Patch C v3: no more sdio_rx_work relay. Wake the bh thread queue_work(self->sdio_wq, &self->rx_work);
* directly via self->irq_handler (bes2600_irq_handler in bh.c
* which bumps bh_rx atomic + wakes bh_wq). The bh thread will
* then call sbus_ops->bus_rx_batch() to do the SDIO read inline.
* Matches cw1200 mainline IRQ → bh-direct architecture.
*/
if (likely(self->fw_started && self->core && self->irq_handler)) {
spin_lock_irqsave(&self->lock, flags);
self->irq_handler(self->irq_priv);
spin_unlock_irqrestore(&self->lock, flags);
self->last_irq_timestamp = jiffies; self->last_irq_timestamp = jiffies;
} else if(self->irq_handler) { } else if(self->irq_handler) {
spin_lock_irqsave(&self->lock, flags); spin_lock_irqsave(&self->lock, flags);
@@ -825,15 +811,10 @@ static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8
skb_put(skb, packet_len); skb_put(skb, packet_len);
memcpy(skb->data, &data[pos], packet_len); memcpy(skb->data, &data[pos], packet_len);
bes_devel("%s, %d,%d\n", __func__, packet_len, pos); bes_devel("%s, %d,%d\n", __func__, packet_len, pos);
spin_lock(&self->rx_queue_lock);
skb_queue_tail(&self->rx_queue, skb);
self->rx_data_cnt++; self->rx_data_cnt++;
/* spin_unlock(&self->rx_queue_lock);
* Patch C v3: deliver the SKB directly into the WSM/mac80211
* stack from the bh thread. No rx_queue, no inter-thread
* handoff, no atomic_t needed on the counters that
* wsm_release_tx_buffer touches — single-writer-from-bh is
* preserved by construction. See bh.c for the contract block.
*/
bes2600_bh_handle_rx_skb(self->core, skb);
packet_len = (packet_len + 3) & (~0x3); packet_len = (packet_len + 3) & (~0x3);
pos += packet_len; pos += packet_len;
#ifdef BES_SDIO_OPTIMIZED_LEN #ifdef BES_SDIO_OPTIMIZED_LEN
@@ -844,31 +825,17 @@ static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8
return 0; return 0;
} }
/* static void sdio_rx_work(struct work_struct *work)
* Patch C v3: bh thread calls this directly via sbus_ops->bus_rx_batch.
* No more sdio_rx_work workqueue. SDIO read sequence (lock →
* read_ctrl → memcpy_fromio → packets_check → extract_packets) runs
* inline in bh-thread context. Each parsed SKB is delivered via
* bes2600_bh_handle_rx_skb() from extract_packets — no rx_queue, no
* second worker, no inter-thread handoff.
*
* Architecture matches cw1200 mainline. Single-writer-from-bh
* invariant on hw_bufs_used preserved by construction.
*
* Returns 0 on success (caller's bh outer loop decides whether to
* continue), negative on bus read error. On error: triggers
* wifi_force_close (same as the old sdio_rx_work).
*/
static int bes2600_sdio_read_rx_batch(struct sbus_priv *self)
{ {
int ret = 0, again = 0, retry = 0, crc_retry = 0; int ret, again = 0, retry = 0, crc_retry = 0;
u32 ctrl_reg = 0; u32 ctrl_reg = 0;
int total_len; int total_len;
struct sbus_priv *self = container_of(work, struct sbus_priv, rx_work);
u8 *buf = self->rx_buffer; u8 *buf = self->rx_buffer;
/* don't read/write sdio when sdio error */ /* don't read/write sdio when sdio error */
if (bes2600_chrdev_is_bus_error()) if (bes2600_chrdev_is_bus_error())
return 0; return;
bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_RX); bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_RX);
@@ -923,10 +890,6 @@ static int bes2600_sdio_read_rx_batch(struct sbus_priv *self)
goto failed; goto failed;
} }
/*
* extract_packets parses the multi-RX buffer and calls
* bes2600_bh_handle_rx_skb() per SKB. No queueing.
*/
if ((ret = bes2600_sdio_extract_packets(self, ctrl_reg, buf))) { if ((ret = bes2600_sdio_extract_packets(self, ctrl_reg, buf))) {
bes_err("%s,%d error=%d\n", __func__, __LINE__, ret); bes_err("%s,%d error=%d\n", __func__, __LINE__, ret);
goto failed; goto failed;
@@ -934,16 +897,22 @@ static int bes2600_sdio_read_rx_batch(struct sbus_priv *self)
ctrl_reg = 0; ctrl_reg = 0;
if (likely(self->irq_handler)) {
self->irq_handler(self->irq_priv);
} else {
bes_err("%s,%d\n", __func__, __LINE__);
goto failed;
}
} while (again); } while (again);
bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX); bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX);
return 0; return;
failed: failed:
bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX); bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX);
bes2600_chrdev_wifi_force_close(self->core, false); bes2600_chrdev_wifi_force_close(self->core, false);
WARN_ON(1); WARN_ON(1);
return -1;
} }
static void sdio_scan_work(struct work_struct *work) static void sdio_scan_work(struct work_struct *work)
@@ -951,11 +920,26 @@ static void sdio_scan_work(struct work_struct *work)
bes_warn("%s: this function does nothing\n", __FUNCTION__); bes_warn("%s: this function does nothing\n", __FUNCTION__);
} }
/* Patch C v3: bes2600_sdio_pipe_read deleted. bh thread reads the static void *bes2600_sdio_pipe_read(struct sbus_priv *self)
* SDIO bus inline via bes2600_sdio_read_rx_batch (sbus_ops->bus_rx_batch). {
* No rx_queue, no skb_dequeue, no relay. bes2600_tx_loop_read remains struct sk_buff *skb;
* for the test bus error-fallback path but is now invoked at higher
* level. */ if (bes2600_chrdev_is_bus_error()) {
return bes2600_tx_loop_read(self->core);
}
spin_lock(&self->rx_queue_lock);
skb = skb_dequeue(&self->rx_queue);
if (skb)
self->rx_proc_cnt++;
spin_unlock(&self->rx_queue_lock);
if (likely(self->fw_started == true &&
!bes2600_pwr_device_is_idle(self->core) &&
self->core->hw_bufs_used > 0))
if (!skb)
queue_work(self->sdio_wq, &self->rx_work);
return skb;
}
#endif #endif
@@ -1211,14 +1195,7 @@ flush_previous:
} }
} while (crc_retry <= 10); } while (crc_retry <= 10);
sdio_release_host(self->func); sdio_release_host(self->func);
/* queue_work(self->sdio_wq, &self->rx_work);
* Patch C v3: wake the bh thread to check for any RX
* that piggybacked on this TX window. Bumps bh_rx
* atomic; bh's wait_event will pick it up and call
* sbus_ops->bus_rx_batch().
*/
if (likely(self->irq_handler))
self->irq_handler(self->irq_priv);
if (ret) { if (ret) {
bes_err("%s,%d err=%d,%d,%d\n", __func__, __LINE__, ret, scatters, cur_blk); bes_err("%s,%d err=%d,%d,%d\n", __func__, __LINE__, ret, scatters, cur_blk);
sdio_work_debug(self); sdio_work_debug(self);
@@ -1269,11 +1246,12 @@ static int bes2600_sdio_misc_init(struct sbus_priv *self, struct bes2600_common
self->next_toggle = 0; self->next_toggle = 0;
#endif #endif
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE #ifdef BES_SDIO_RX_MULTIPLE_ENABLE
/* Patch C v3: rx_queue / rx_queue_lock removed (no relay). */ spin_lock_init(&self->rx_queue_lock);
skb_queue_head_init(&self->rx_queue);
self->rx_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM)); self->rx_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM));
if (!self->rx_buffer) if (!self->rx_buffer)
return -ENOMEM; return -ENOMEM;
/* Patch C v3: sdio_rx_work removed; bh thread does the read. */ INIT_WORK(&self->rx_work, sdio_rx_work);
#endif #endif
#ifdef BES_SDIO_TX_MULTIPLE_ENABLE #ifdef BES_SDIO_TX_MULTIPLE_ENABLE
INIT_LIST_HEAD(&self->tx_bufferlist); INIT_LIST_HEAD(&self->tx_bufferlist);
@@ -1410,14 +1388,7 @@ static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int flag)
/* error check */ /* error check */
if((self->gpio_wakup_flags & BIT(flag)) != 0) { if((self->gpio_wakup_flags & BIT(flag)) != 0) {
/* bes_err( "repeat set gpio_wake_flag, sub_sys:%d", flag);
* Multiple subsystems holding wake is the steady-state case
* (e.g. WIFI + BT both want MCU awake). Demoted from bes_err
* to bes_devel since it isn't an error - the GPIO is already
* asserted high and the subsystem is now also tracked.
*/
bes_devel("repeat set gpio_wake_flag, sub_sys:%d\n", flag);
self->gpio_wakup_flags |= BIT(flag);
mutex_unlock(&self->io_mutex); mutex_unlock(&self->io_mutex);
return; return;
} }
@@ -1449,11 +1420,7 @@ static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag)
/* error check */ /* error check */
if((self->gpio_wakup_flags & BIT(flag)) == 0) { if((self->gpio_wakup_flags & BIT(flag)) == 0) {
/* bes_err( "repeat clear gpio_wake_flag, sub_sys:%d", flag);
* Mirror of the wake path: a clear when the bit is already
* clear is racy bookkeeping, not a hardware error.
*/
bes_devel("repeat clear gpio_wake_flag, sub_sys:%d\n", flag);
mutex_unlock(&self->io_mutex); mutex_unlock(&self->io_mutex);
return; return;
} }
@@ -1602,15 +1569,22 @@ err:
static void bes2600_sdio_empty_work(struct sbus_priv *self) static void bes2600_sdio_empty_work(struct sbus_priv *self)
{ {
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
struct sk_buff *skb;
#endif
#ifdef BES_SDIO_TX_MULTIPLE_ENABLE #ifdef BES_SDIO_TX_MULTIPLE_ENABLE
struct bes_sdio_tx_list_t *tx_buffer, *temp; struct bes_sdio_tx_list_t *tx_buffer, *temp;
#endif #endif
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE #ifdef BES_SDIO_RX_MULTIPLE_ENABLE
/* cancel_work_sync(&self->rx_work);
* Patch C v3: rx_work and rx_queue removed. Counters still while (1) {
* reset for the next attach cycle. skb = skb_dequeue(&self->rx_queue);
*/ if (skb)
dev_kfree_skb(skb);
else
break;
}
self->rx_last_ctrl = 0; self->rx_last_ctrl = 0;
self->rx_total_ctrl_cnt = 0; self->rx_total_ctrl_cnt = 0;
self->rx_continuous_ctrl_cnt = 0; self->rx_continuous_ctrl_cnt = 0;
@@ -1803,55 +1777,6 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self)
sdio_work_debug(self); sdio_work_debug(self);
} }
/*
* Trigger an SDIO bus reset via mmc_hw_reset().
*
* With multiple SDIO functions probed (PineTab2 binds func 1 for WLAN and
* func 2 for the BT-companion path) mmc_sdio_hw_reset() takes the
* remove-and-rescan path: it marks the card removed and schedules
* mmc_rescan, which tears down the bound function drivers and re-detects
* the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
*
* With a single function probed it instead invokes mmc_power_cycle()
* directly, which on PineTab2 toggles the wifi-reset GPIO via sdio_pwrseq.
*
* In both cases the chip ends up in a freshly reset state, which is the
* goal of the recovery path.
*
* mmc_hw_reset() must be called without holding the SDIO host claim --
* the multi-func remove-and-rescan path acquires the host claim via the
* mmc workqueue.
*/
static int bes2600_sdio_bus_reset(struct sbus_priv *self)
{
struct mmc_host *host;
int ret;
if (!self || !self->func || !self->func->card)
return -EINVAL;
host = self->func->card->host;
ret = mmc_hw_reset(self->func->card);
/*
* On multi-function SDIO cards (BES2600 has WLAN func 1 + BT
* companion func 2), mmc_sdio_hw_reset() removes the card and
* returns 1 to signal "remove happened, caller must trigger
* rescan". The kernel does NOT auto-rescan in this case;
* single-function cards take the rescan path inline and return 0.
* Treat any non-negative return as success and force a rescan if
* mmc_hw_reset signalled the multi-function path - otherwise the
* card stays removed indefinitely after a wedge recovery,
* leaving wifi (and the BT companion) silent until reboot.
*/
if (ret > 0) {
bes_info("multi-func mmc_hw_reset removed card; scheduling rescan\n");
mmc_detect_change(host, 0);
ret = 0;
}
return ret;
}
static bool bes2600_sdio_wakeup_source(struct sbus_priv *self) static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)
{ {
struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
@@ -1878,8 +1803,7 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
.sbus_reg_write = bes2600_sdio_reg_write, .sbus_reg_write = bes2600_sdio_reg_write,
.init = bes2600_sdio_misc_init, .init = bes2600_sdio_misc_init,
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE #ifdef BES_SDIO_RX_MULTIPLE_ENABLE
/* Patch C v3: .pipe_read removed; bus_rx_batch replaces it. */ .pipe_read = bes2600_sdio_pipe_read,
.bus_rx_batch = bes2600_sdio_read_rx_batch,
#endif #endif
#ifdef BES_SDIO_TX_MULTIPLE_ENABLE #ifdef BES_SDIO_TX_MULTIPLE_ENABLE
.pipe_send = bes2600_sdio_pipe_send, .pipe_send = bes2600_sdio_pipe_send,
@@ -1891,7 +1815,6 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
.gpio_sleep = bes2600_gpio_allow_mcu_sleep, .gpio_sleep = bes2600_gpio_allow_mcu_sleep,
.halt_device = bes2600_sdio_halt_device, .halt_device = bes2600_sdio_halt_device,
.wakeup_source = bes2600_sdio_wakeup_source, .wakeup_source = bes2600_sdio_wakeup_source,
.bus_reset = bes2600_sdio_bus_reset,
}; };
static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv) static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
@@ -1899,15 +1822,9 @@ static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
long unsigned int old_ts, new_ts; long unsigned int old_ts, new_ts;
struct sbus_priv *self = hw_priv->sbus_priv; struct sbus_priv *self = hw_priv->sbus_priv;
/*
* Patch C v3: rx_work removed. Wait for IRQ-timestamp activity
* to settle by polling self->last_irq_timestamp via msleep
* (best-effort). The caller already knows the bh thread will
* process pending bh_rx during its next wait_event round.
*/
do { do {
old_ts = self->last_irq_timestamp; old_ts = self->last_irq_timestamp;
msleep(2); flush_work(&self->rx_work);
new_ts = self->last_irq_timestamp; new_ts = self->last_irq_timestamp;
} while(old_ts != new_ts); } while(old_ts != new_ts);
} }
@@ -2265,12 +2182,8 @@ static int bes2600_sdio_suspend_noirq(struct device *dev)
if (func->num > 1) if (func->num > 1)
return 0; return 0;
/* if(self->core &&
* Patch C v3: work_pending(&self->rx_work) check dropped (no (work_pending(&self->rx_work) || atomic_read(&self->core->bh_rx))) {
* relay). bh_rx atomic alone tells us whether the bh thread
* has un-processed RX events queued.
*/
if (self->core && atomic_read(&self->core->bh_rx)) {
bes_devel("%s: Suspend interrupted.\n", __func__); bes_devel("%s: Suspend interrupted.\n", __func__);
return -EAGAIN; return -EAGAIN;
} }
+8 -72
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Character device for BES2600 mac80211 driver * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include<linux/module.h> #include<linux/module.h>
#include <linux/init.h> #include <linux/init.h>
@@ -439,60 +442,6 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
return ret; return ret;
} }
/*
* Hard-reset the bus and wait for the bus core to remove the chip.
*
* Used by the firmware-wedge recovery path on platforms where the normal
* power_switch(0) sequence has no effective chip-reset signal. The bus
* implementation triggers an asynchronous re-detect; this helper waits for
* the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a
* subsequent bes2600_switch_wifi(true) sees a clean state and can wait on
* the fresh probe.
*/
int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
{
int ret;
long status;
if (!sbus_ops || !priv)
return -EINVAL;
if (!sbus_ops->bus_reset)
return -EOPNOTSUPP;
bes_info("trigger bus reset to recover wedged firmware.\n");
ret = sbus_ops->bus_reset(priv);
if (ret) {
bes_err("bus_reset failed: %d\n", ret);
return ret;
}
/*
* The bus reset is asynchronous: the bus core schedules a rescan
* which removes the bound function drivers and then re-detects the
* chip. Wait for the remove callback to clear sbus_priv. Do not
* dereference 'priv' after this point -- it may already be freed.
*/
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
!bes2600_cdev.sbus_priv, HZ * 3);
WARN_ON(status <= 0);
return 0;
}
/*
* Trigger bes2600_chrdev_do_bus_reset() against the file-global
* bes2600_cdev. Used by host-side recovery paths outside this
* compilation unit (e.g. sta.c connection-loss-storm fast-recover) so
* those callers do not need to reach the static bes2600_cdev directly.
*/
int bes2600_chrdev_trigger_bus_reset(void)
{
return bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
bes2600_cdev.sbus_priv);
}
bool bes2600_chrdev_is_wifi_opened(void) bool bes2600_chrdev_is_wifi_opened(void)
{ {
bool wifi_opened = false; bool wifi_opened = false;
@@ -591,21 +540,8 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
/* unregister wifi */ /* unregister wifi */
bes2600_switch_wifi(0); bes2600_switch_wifi(0);
/* /* power down device if wifi is only opened */
* Hard exception with a bus_reset implementation: tear the if (bes2600_chrdev_check_system_close()) {
* bus down via mmc_hw_reset() (or equivalent) so the next
* bringup probes a freshly reset chip. On PineTab2 this is
* the only effective recovery path -- the existing
* power_switch(0)/(1) sequence has no chip-reset signal of
* its own (sdio_pwrseq owns wifi_reset).
*
* Soft close, or hard close on a board without bus_reset:
* fall back to the legacy power_switch(0) sequence.
*/
if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->bus_reset) {
bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
bes2600_cdev.sbus_priv);
} else if (bes2600_chrdev_check_system_close()) {
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops, bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
bes2600_cdev.sbus_priv); bes2600_cdev.sbus_priv);
} }
+6 -5
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Character device interface for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef __BES_CHARDEV_H__ #ifndef __BES_CHARDEV_H__
#define __BES_CHARDEV_H__ #define __BES_CHARDEV_H__
@@ -57,8 +60,6 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
/* used to control device power down */ /* used to control device power down */
int bes2600_chrdev_check_system_close(void); int bes2600_chrdev_check_system_close(void);
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv); int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
int bes2600_chrdev_trigger_bus_reset(void);
void bes2600_chrdev_wakeup_bt(void); void bes2600_chrdev_wakeup_bt(void);
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev); void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv); void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv);
+6 -3
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Firmware download for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include "bes_fw_common.h" #include "bes_fw_common.h"
#include "bes2600.h" #include "bes2600.h"
+6 -3
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Firmware download common code for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include "bes_fw_common.h" #include "bes_fw_common.h"
#include "bes_log.h" #include "bes_log.h"
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Firmware download common interface * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef __BES_FW_COMMON_H__ #ifndef __BES_FW_COMMON_H__
#define __BES_FW_COMMON_H__ #define __BES_FW_COMMON_H__
-7
View File
@@ -1,10 +1,3 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* printk wrappers for BES2600
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
*
*/
extern struct device *global_dev; extern struct device *global_dev;
#ifdef CONFIG_BES2600_ENABLE_DEVEL_LOGS #ifdef CONFIG_BES2600_ENABLE_DEVEL_LOGS
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Vendor testmode messages for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2010, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES_NL80211_TESTMODE_MSG_H #ifndef BES_NL80211_TESTMODE_MSG_H
+10 -117
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Chip-side power state machine for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/list.h> #include <linux/list.h>
#include <linux/pm.h> #include <linux/pm.h>
@@ -464,45 +467,6 @@ static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv)
bes_devel("device enter sleep\n"); bes_devel("device enter sleep\n");
} }
/*
* Number of consecutive bes2600_pwr_enter_lp_mode timeouts (with zero
* PM_INDICATIONs received) before we conclude the firmware does not
* honor host-driven PSM and switch to a sticky skip path.
*/
#define BES2600_PM_UNSUPPORTED_THRESHOLD 3
/*
* Latch pm_unsupported = true and force chip_pm_state = ACTIVE so the
* c6.2 wake-side skip branch covers bes2600_pwr_device_exit_lp_mode.
* Called after BES2600_PM_UNSUPPORTED_THRESHOLD consecutive enter_lp_mode
* timeouts with zero PM_INDICATIONs.
*/
static void bes2600_pwr_latch_pm_unsupported(struct bes2600_common *hw_priv)
{
bes_warn("PSM not honored (%u timeouts), switching to skip mode\n",
hw_priv->bes_power.pm_consecutive_timeouts);
hw_priv->bes_power.pm_unsupported = true;
atomic_set(&hw_priv->bes_power.chip_pm_state,
BES2600_CHIP_PM_ACTIVE);
/*
* Hold the MCU wake-flag bit permanently. Without this, every
* sdio_rx_work invocation hits bes2600_gpio_wakeup_mcu(SDIO_RX)
* when gpio_wakup_flags == 0, drives the GPIO high and msleeps
* 10 ms per RX. With ~50 RX/s of beacons + multicast that's
* ~50%% of the bes_sdio workqueue thread blocked in msleep,
* which directly caps RX throughput. Holding the MCU bit makes
* those calls bit-only bookkeeping (gpio_wakeup = (flags == 0)
* stays false, no GPIO toggle, no msleep). The bit is never
* cleared once pm_unsupported is set because
* bes2600_pwr_device_enter_lp_mode is unreachable under the
* early-return.
*/
if (hw_priv->sbus_ops->gpio_wake)
hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
GPIO_WAKE_FLAG_MCU);
}
static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
{ {
int i = 0; int i = 0;
@@ -512,17 +476,6 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
char ip_str[20]; char ip_str[20];
unsigned long status = 0; unsigned long status = 0;
/*
* Sticky early-return when we've previously concluded the firmware
* doesn't honor PSM. Each attempt would otherwise burn 5s on a
* doomed wait_for_completion_timeout and produce a noisy three-line
* cascade in dmesg every time power_down_work retries (every
* ~10s). The chip stays in active mode, which on this firmware is
* the de-facto state anyway.
*/
if (hw_priv->bes_power.pm_unsupported)
return -EOPNOTSUPP;
/* set interface low power configuration */ /* set interface low power configuration */
bes2600_for_each_vif(hw_priv, priv, i) { bes2600_for_each_vif(hw_priv, priv, i) {
#ifdef P2P_MULTIVIF #ifdef P2P_MULTIVIF
@@ -618,9 +571,6 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
atomic_set(&hw_priv->bes_power.chip_pm_state, atomic_set(&hw_priv->bes_power.chip_pm_state,
BES2600_CHIP_PM_UNKNOWN); BES2600_CHIP_PM_UNKNOWN);
timeouts++; timeouts++;
if (++hw_priv->bes_power.pm_consecutive_timeouts
>= BES2600_PM_UNSUPPORTED_THRESHOLD)
bes2600_pwr_latch_pm_unsupported(hw_priv);
} }
} }
} else { } else {
@@ -659,8 +609,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
* GPIO stays high and the bit clear here is purely * GPIO stays high and the bit clear here is purely
* bookkeeping (so the next gpio_wake doesn't no-op). * bookkeeping (so the next gpio_wake doesn't no-op).
*/ */
if (!hw_priv->bes_power.pm_unsupported && if (hw_priv->sbus_ops->gpio_sleep)
hw_priv->sbus_ops->gpio_sleep)
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
GPIO_WAKE_FLAG_MCU); GPIO_WAKE_FLAG_MCU);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
@@ -672,61 +621,19 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv) static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv)
{ {
int ret = 0; int ret = 0;
enum bes2600_chip_pm_state state;
struct wsm_operational_mode mode = { struct wsm_operational_mode mode = {
.power_mode = wsm_power_mode_active, .power_mode = wsm_power_mode_active,
.disableMoreFlagUsage = true, .disableMoreFlagUsage = true,
}; };
/*
* Consult chip_pm_state set by bes2600_pwr_notify_ps_changed().
* If we last saw the firmware confirm ACTIVE, skip ONLY the
* gpio_wake + sbus_active wake handshake - the GPIO is already
* asserted high and the SDIO MCU subsystem is already running,
* so another sbus_active() round-trip just hits its 200x2ms
* timeout because the firmware has nothing to do.
*
* wsm_set_operational_mode() below is NOT part of the wake
* handshake; it is the operational-mode setter the firmware
* tracks per call. Skipping it leaves the chip's SDIO state
* machine without a fresh operational-mode update, which on
* PineTab2 wedges the bus (-EBUSY on next sdio_rx_work read)
* within a few seconds of probe completion. So it must run
* unconditionally.
*/
state = atomic_read(&hw_priv->bes_power.chip_pm_state);
if (state == BES2600_CHIP_PM_ACTIVE) {
bes_devel("device_exit_lp_mode: chip already ACTIVE, skipping wake handshake\n");
} else {
bes_devel("host lock lmac\n"); bes_devel("host lock lmac\n");
if(hw_priv->sbus_ops->gpio_wake) if(hw_priv->sbus_ops->gpio_wake)
hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
GPIO_WAKE_FLAG_MCU);
if(hw_priv->sbus_ops->sbus_active) { if(hw_priv->sbus_ops->sbus_active) {
ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU);
SUBSYSTEM_MCU); if (ret)
if (ret) {
/*
* MCU_WAKEUP_READY did not arrive within
* the SDIO handshake window. Record state
* as UNKNOWN so the next exit_lp_mode call
* also runs the full wake sequence (no
* skip), but still send operational_mode
* below to match pre-c6 behaviour - the
* WSM may succeed even if the SDIO active
* confirm was lost, and if it fails too,
* we just emit a second devel-level error.
* Repeated UNKNOWN is the signal for the
* LMAC active-monitor to eventually
* escalate to bus_reset (c5.2's
* mmc_hw_reset path).
*/
bes_err("%s, active mcu fail\n", __func__); bes_err("%s, active mcu fail\n", __func__);
atomic_set(&hw_priv->bes_power.chip_pm_state,
BES2600_CHIP_PM_UNKNOWN);
}
}
} }
ret = wsm_set_operational_mode(hw_priv, &mode, 0); ret = wsm_set_operational_mode(hw_priv, &mode, 0);
@@ -983,8 +890,6 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
mutex_init(&hw_priv->bes_power.pwr_mutex); mutex_init(&hw_priv->bes_power.pwr_mutex);
atomic_set(&hw_priv->bes_power.dev_state, 0); atomic_set(&hw_priv->bes_power.dev_state, 0);
atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN); atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN);
hw_priv->bes_power.pm_unsupported = false;
hw_priv->bes_power.pm_consecutive_timeouts = 0;
init_completion(&hw_priv->bes_power.pm_enter_cmpl); init_completion(&hw_priv->bes_power.pm_enter_cmpl);
sema_init(&hw_priv->bes_power.sync_lock, 1); sema_init(&hw_priv->bes_power.sync_lock, 1);
device_set_wakeup_capable(hw_priv->pdev, true); device_set_wakeup_capable(hw_priv->pdev, true);
@@ -1374,18 +1279,6 @@ void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
* indication can prime a future wait against a freshly * indication can prime a future wait against a freshly
* reinit_completion()'ed state. * reinit_completion()'ed state.
*/ */
/*
* Any PM indication, whatever its psmode, proves the firmware is
* actually emitting them. Reset the consecutive-timeout counter
* so a transient stall doesn't permanently disable PSM, and clear
* pm_unsupported if a previous run had latched it.
*/
hw_priv->bes_power.pm_consecutive_timeouts = 0;
if (hw_priv->bes_power.pm_unsupported) {
bes_warn("PM indication arrived after pm_unsupported was set; re-enabling PSM transitions\n");
hw_priv->bes_power.pm_unsupported = false;
}
if ((psmode & 0x01) != WSM_PSM_ACTIVE) { if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
atomic_set(&hw_priv->bes_power.chip_pm_state, atomic_set(&hw_priv->bes_power.chip_pm_state,
BES2600_CHIP_PM_LP); BES2600_CHIP_PM_LP);
+6 -12
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Chip-side power state machine interface * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef __BES_PWR_H__ #ifndef __BES_PWR_H__
#define __BES_PWR_H__ #define __BES_PWR_H__
@@ -118,15 +121,6 @@ struct bes2600_pwr_t
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM]; struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
atomic_t pm_set_in_process; atomic_t pm_set_in_process;
atomic_t chip_pm_state; atomic_t chip_pm_state;
/*
* Sticky flag set after BES2600_PM_UNSUPPORTED_THRESHOLD
* consecutive enter_lp_mode timeouts with zero PM_INDICATIONs
* received from firmware. Indicates this chip's firmware does
* not honor host-driven PSM transitions; further attempts are
* skipped to avoid the 5s timeout cascade.
*/
bool pm_unsupported;
unsigned int pm_consecutive_timeouts;
}; };
#ifdef CONFIG_BES2600_WOWLAN #ifdef CONFIG_BES2600_WOWLAN
+570 -153
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Bottom-half thread for BES2600 mac80211 driver * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <net/mac80211.h> #include <net/mac80211.h>
#include <linux/kthread.h> #include <linux/kthread.h>
@@ -101,7 +101,7 @@ void bes2600_unregister_bh(struct bes2600_common *hw_priv)
coex_deinit_mode(hw_priv); coex_deinit_mode(hw_priv);
#endif #endif
atomic_inc(&hw_priv->bh_term); atomic_add(1, &hw_priv->bh_term);
wake_up(&hw_priv->bh_wq); wake_up(&hw_priv->bh_wq);
flush_workqueue(hw_priv->bh_workqueue); flush_workqueue(hw_priv->bh_workqueue);
@@ -316,6 +316,83 @@ int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count)
} }
#endif #endif
#if 0
static struct sk_buff *bes2600_get_skb(struct bes2600_common *hw_priv, size_t len)
{
struct sk_buff *skb;
size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE;
if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) {
skb = dev_alloc_skb(alloc_len
+ WSM_TX_EXTRA_HEADROOM
+ 8 /* TKIP IV */
+ 12 /* TKIP ICV + MIC */
- 2 /* Piggyback */);
/* In AP mode RXed SKB can be looped back as a broadcast.
* Here we reserve enough space for headers. */
skb_reserve(skb, WSM_TX_EXTRA_HEADROOM
+ 8 /* TKIP IV */
- WSM_RX_EXTRA_HEADROOM);
} else {
skb = hw_priv->skb_cache;
hw_priv->skb_cache = NULL;
}
return skb;
}
static void bes2600_put_skb(struct bes2600_common *hw_priv, struct sk_buff *skb)
{
if (hw_priv->skb_cache)
dev_kfree_skb(skb);
else
hw_priv->skb_cache = skb;
}
static int bes2600_bh_read_ctrl_reg(struct bes2600_common *hw_priv,
u16 *ctrl_reg)
{
int ret;
ret = bes2600_reg_read_16(hw_priv,
ST90TDS_CONTROL_REG_ID, ctrl_reg);
if (ret) {
ret = bes2600_reg_read_16(hw_priv,
ST90TDS_CONTROL_REG_ID, ctrl_reg);
if (ret)
bes_err("[BH] Failed to read control register.\n");
}
return ret;
}
static int bes2600_device_wakeup(struct bes2600_common *hw_priv)
{
u16 ctrl_reg;
int ret;
bes_devel("[BH] Device wakeup.\n");
/* To force the device to be always-on, the host sets WLAN_UP to 1 */
ret = bes2600_reg_write_16(hw_priv, ST90TDS_CONTROL_REG_ID,
ST90TDS_CONT_WUP_BIT);
if (WARN_ON(ret))
return ret;
ret = bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
if (WARN_ON(ret))
return ret;
/* If the device returns WLAN_RDY as 1, the device is active and will
* remain active. */
if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
bes_devel("[BH] Device awake.\n");
return 1;
}
return 0;
}
#endif
/* Must be called from BH thraed. */ /* Must be called from BH thraed. */
void bes2600_enable_powersave(struct bes2600_vif *priv, void bes2600_enable_powersave(struct bes2600_vif *priv,
@@ -325,6 +402,475 @@ void bes2600_enable_powersave(struct bes2600_vif *priv,
priv->powersave_enabled = enable; priv->powersave_enabled = enable;
} }
#if 0
#define INTERRUPT_WORKAROUND
static int bes2600_bh(void *arg)
{
struct bes2600_common *hw_priv = arg;
struct bes2600_vif *priv = NULL;
struct sk_buff *skb_rx = NULL;
size_t read_len = 0;
int rx, tx, term, suspend;
struct wsm_hdr *wsm;
size_t wsm_len;
int wsm_id;
u8 wsm_seq;
int rx_resync = 1;
u16 ctrl_reg = 0;
int tx_allowed;
int pending_tx = 0;
int tx_burst;
int rx_burst = 0;
long status;
#if defined(CONFIG_BES2600_WSM_DUMPS)
size_t wsm_dump_max = -1;
#endif
u32 dummy;
bool powersave_enabled;
int i;
int vif_selected;
for (;;) {
powersave_enabled = 1;
spin_lock(&hw_priv->vif_list_lock);
bes2600_for_each_vif(hw_priv, priv, i) {
#ifdef P2P_MULTIVIF
if ((i = (CW12XX_MAX_VIFS - 1)) || !priv)
#else
if (!priv)
#endif
continue;
powersave_enabled &= !!priv->powersave_enabled;
}
spin_unlock(&hw_priv->vif_list_lock);
if (!hw_priv->hw_bufs_used
&& powersave_enabled
&& !hw_priv->device_can_sleep
&& !atomic_read(&hw_priv->recent_scan)) {
status = HZ/8;
bes_devel("[BH] No Device wakedown.\n");
#ifndef FPGA_SETUP
WARN_ON(bes2600_reg_write_16(hw_priv,
ST90TDS_CONTROL_REG_ID, 0));
hw_priv->device_can_sleep = true;
#endif
} else if (hw_priv->hw_bufs_used)
/* Interrupt loss detection */
status = HZ/8;
else
status = HZ/8;
/* Dummy Read for SDIO retry mechanism*/
if (((atomic_read(&hw_priv->bh_rx) == 0) &&
(atomic_read(&hw_priv->bh_tx) == 0)))
bes2600_reg_read(hw_priv, ST90TDS_CONFIG_REG_ID,
&dummy, sizeof(dummy));
#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
wsm_dump_max = hw_priv->wsm_dump_max_size;
#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
#ifdef INTERRUPT_WORKAROUND
/* If a packet has already been txed to the device then read the
control register for a probable interrupt miss before going
further to wait for interrupt; if the read length is non-zero
then it means there is some data to be received */
if (hw_priv->hw_bufs_used) {
bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
{
rx = 1;
goto test;
}
}
#endif
status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({
rx = atomic_xchg(&hw_priv->bh_rx, 0);
tx = atomic_xchg(&hw_priv->bh_tx, 0);
term = atomic_xchg(&hw_priv->bh_term, 0);
suspend = pending_tx ?
0 : atomic_read(&hw_priv->bh_suspend);
(rx || tx || term || suspend || hw_priv->bh_error);
}), status);
if (status < 0 || term || hw_priv->bh_error)
break;
#ifdef INTERRUPT_WORKAROUND
if (!status) {
bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
{
bes_err("MISS 1\n");
rx = 1;
goto test;
}
}
#endif
if (!status && hw_priv->hw_bufs_used) {
unsigned long timestamp = jiffies;
long timeout;
bool pending = false;
int i;
wiphy_warn(hw_priv->hw->wiphy, "Missed interrupt?\n");
rx = 1;
/* Get a timestamp of "oldest" frame */
for (i = 0; i < 4; ++i)
pending |= bes2600_queue_get_xmit_timestamp(
&hw_priv->tx_queue[i],
&timestamp, -1,
hw_priv->pending_frame_id);
/* Check if frame transmission is timed out.
* Add an extra second with respect to possible
* interrupt loss. */
timeout = timestamp +
WSM_CMD_LAST_CHANCE_TIMEOUT +
1 * HZ -
jiffies;
/* And terminate BH tread if the frame is "stuck" */
if (pending && timeout < 0) {
//wiphy_warn(priv->hw->wiphy,
// "Timeout waiting for TX confirm.\n");
bes_devel("bes2600_bh: Timeout waiting for TX confirm.\n");
break;
}
#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
BUG_ON(1);
#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
} else if (!status) {
if (!hw_priv->device_can_sleep
&& !atomic_read(&hw_priv->recent_scan)) {
bes_devel("[BH] Device wakedown. Timeout.\n");
#ifndef FPGA_SETUP
WARN_ON(bes2600_reg_write_16(hw_priv,
ST90TDS_CONTROL_REG_ID, 0));
hw_priv->device_can_sleep = true;
#endif
}
continue;
} else if (suspend) {
bes_devel("[BH] Device suspend.\n");
powersave_enabled = 1;
spin_lock(&hw_priv->vif_list_lock);
bes2600_for_each_vif(hw_priv, priv, i) {
#ifdef P2P_MULTIVIF
if ((i = (CW12XX_MAX_VIFS - 1)) || !priv)
#else
if (!priv)
#endif
continue;
powersave_enabled &= !!priv->powersave_enabled;
}
spin_unlock(&hw_priv->vif_list_lock);
if (powersave_enabled) {
bes_devel("[BH] No Device wakedown. Suspend.\n");
#ifndef FPGA_SETUP
WARN_ON(bes2600_reg_write_16(hw_priv,
ST90TDS_CONTROL_REG_ID, 0));
hw_priv->device_can_sleep = true;
#endif
}
atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED);
wake_up(&hw_priv->bh_evt_wq);
status = wait_event_interruptible(hw_priv->bh_wq,
BES2600_BH_RESUME == atomic_read(
&hw_priv->bh_suspend));
if (status < 0) {
wiphy_err(hw_priv->hw->wiphy,
"%s: Failed to wait for resume: %ld.\n",
__func__, status);
break;
}
bes_devel("[BH] Device resume.\n");
atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
wake_up(&hw_priv->bh_evt_wq);
atomic_add(1, &hw_priv->bh_rx);
continue;
}
test:
tx += pending_tx;
pending_tx = 0;
if (rx) {
size_t alloc_len;
u8 *data;
#ifdef INTERRUPT_WORKAROUND
if(!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK))
#endif
if (WARN_ON(bes2600_bh_read_ctrl_reg(
hw_priv, &ctrl_reg)))
break;
rx:
read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
if (!read_len) {
rx_burst = 0;
goto tx;
}
if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
(read_len > EFFECTIVE_BUF_SIZE))) {
bes_devel("Invalid read len: %d", read_len);
break;
}
/* Add SIZE of PIGGYBACK reg (CONTROL Reg)
* to the NEXT Message length + 2 Bytes for SKB */
read_len = read_len + 2;
#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES)
alloc_len = hw_priv->sbus_ops->align_size(
hw_priv->sbus_priv, read_len);
#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
/* Platform's SDIO workaround */
alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1);
if (read_len & (SDIO_BLOCK_SIZE - 1))
alloc_len += SDIO_BLOCK_SIZE;
#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
/* Check if not exceeding BES2600 capabilities */
if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE))
bes_devel("Read aligned len: %d\n", alloc_len);
skb_rx = bes2600_get_skb(hw_priv, alloc_len);
if (WARN_ON(!skb_rx))
break;
skb_trim(skb_rx, 0);
skb_put(skb_rx, read_len);
data = skb_rx->data;
if (WARN_ON(!data))
break;
if (WARN_ON(bes2600_data_read(hw_priv, data, alloc_len)))
break;
/* Piggyback */
ctrl_reg = __le16_to_cpu(
((__le16 *)data)[alloc_len / 2 - 1]);
wsm = (struct wsm_hdr *)data;
wsm_len = __le32_to_cpu(wsm->len);
if (WARN_ON(wsm_len > read_len))
break;
#if defined(CONFIG_BES2600_WSM_DUMPS)
if (unlikely(hw_priv->wsm_enable_wsm_dumps)) {
u16 msgid, ifid;
u16 *p = (u16 *)data;
msgid = (*(p + 1)) & 0xC3F;
ifid = (*(p + 1)) >> 6;
ifid &= 0xF;
bes_devel("[DUMP] <<< msgid 0x%.4X ifid %d len %d\n", msgid, ifid, *p);
print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, data, min(wsm_len, wsm_dump_max));
}
#endif /* CONFIG_BES2600_WSM_DUMPS */
wsm_id = __le32_to_cpu(wsm->id) & 0xFFF;
wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7;
skb_trim(skb_rx, wsm_len);
if (unlikely(wsm_id == 0x0800)) {
wsm_handle_exception(hw_priv,
&data[sizeof(*wsm)],
wsm_len - sizeof(*wsm));
break;
} else if (unlikely(!rx_resync)) {
if (WARN_ON(wsm_seq != hw_priv->wsm_rx_seq)) {
#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
BUG_ON(1);
#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
break;
}
}
hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7;
rx_resync = 0;
if (wsm_id & 0x0400) {
int rc = wsm_release_tx_buffer(hw_priv, 1);
if (WARN_ON(rc < 0))
break;
else if (rc > 0)
tx = 1;
}
/* bes2600_wsm_rx takes care on SKB livetime */
if (WARN_ON(wsm_handle_rx(hw_priv, wsm_id, wsm,
&skb_rx)))
break;
if (skb_rx) {
bes2600_put_skb(hw_priv, skb_rx);
skb_rx = NULL;
}
read_len = 0;
if (rx_burst) {
bes2600_debug_rx_burst(hw_priv);
--rx_burst;
goto rx;
}
}
tx:
BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
tx_burst = hw_priv->wsm_caps.numInpChBufs -
hw_priv->hw_bufs_used;
tx_allowed = tx_burst > 0;
if (tx && tx_allowed) {
size_t tx_len;
u8 *data;
int ret;
if (hw_priv->device_can_sleep) {
ret = bes2600_device_wakeup(hw_priv);
if (WARN_ON(ret < 0))
break;
else if (ret)
hw_priv->device_can_sleep = false;
else {
/* Wait for "awake" interrupt */
pending_tx = tx;
continue;
}
}
wsm_alloc_tx_buffer(hw_priv);
ret = wsm_get_tx(hw_priv, &data, &tx_len, &tx_burst,
&vif_selected);
if (ret <= 0) {
wsm_release_tx_buffer(hw_priv, 1);
if (WARN_ON(ret < 0))
break;
} else {
wsm = (struct wsm_hdr *)data;
BUG_ON(tx_len < sizeof(*wsm));
BUG_ON(__le32_to_cpu(wsm->len) != tx_len);
#if 0 /* count is not implemented */
if (ret > 1)
atomic_add(1, &hw_priv->bh_tx);
#else
atomic_add(1, &hw_priv->bh_tx);
#endif
#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES)
if (tx_len <= 8)
tx_len = 16;
tx_len = hw_priv->sbus_ops->align_size(
hw_priv->sbus_priv, tx_len);
#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
/* HACK!!! Platform limitation.
* It is also supported by upper layer:
* there is always enough space at the
* end of the buffer. */
if (tx_len & (SDIO_BLOCK_SIZE - 1)) {
tx_len &= ~(SDIO_BLOCK_SIZE - 1);
tx_len += SDIO_BLOCK_SIZE;
}
#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
/* Check if not exceeding BES2600
capabilities */
if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
bes_devel("Write aligned len: %d\n", tx_len);
wsm->id &= __cpu_to_le32(
~WSM_TX_SEQ(WSM_TX_SEQ_MAX));
wsm->id |= cpu_to_le32(WSM_TX_SEQ(
hw_priv->wsm_tx_seq));
if (WARN_ON(bes2600_data_write(hw_priv,
data, tx_len))) {
wsm_release_tx_buffer(hw_priv, 1);
break;
}
if (vif_selected != -1) {
hw_priv->hw_bufs_used_vif[
vif_selected]++;
}
#if defined(CONFIG_BES2600_WSM_DUMPS)
if (unlikely(hw_priv->wsm_enable_wsm_dumps)) {
u16 msgid, ifid;
u16 *p = (u16 *)data;
msgid = (*(p + 1)) & 0x3F;
ifid = (*(p + 1)) >> 6;
ifid &= 0xF;
if (msgid == 0x0006)
bes_devel("[DUMP] >>> msgid 0x%.4X ifid %d len %d MIB 0x%.4X\n", msgid, ifid, *p, *(p + 2));
else
bes_devel("[DUMP] >>> msgid 0x%.4X ifid %d len %d\n", msgid, ifid, *p);
print_hex_dump(KERN_DEBUG, "--> ", DUMP_PREFIX_NONE, data, min(__le32_to_cpu(wsm->len), wsm_dump_max));
}
#endif /* CONFIG_BES2600_WSM_DUMPS */
wsm_txed(hw_priv, data);
hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1)
& WSM_TX_SEQ_MAX;
if (tx_burst > 1) {
bes2600_debug_tx_burst(hw_priv);
++rx_burst;
goto tx;
}
}
}
if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
goto rx;
}
if (skb_rx) {
bes2600_put_skb(hw_priv, skb_rx);
skb_rx = NULL;
}
if (!term) {
bes_devel("[BH] Fatal error, exitting.\n");
#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
BUG_ON(1);
#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
hw_priv->bh_error = 1;
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
spin_lock(&hw_priv->vif_list_lock);
bes2600_for_each_vif(hw_priv, priv, i) {
if (!priv)
continue;
ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
}
spin_unlock(&hw_priv->vif_list_lock);
bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
#endif
/* TODO: schedule_work(recovery) */
#ifndef HAS_PUT_TASK_STRUCT
/* The only reason of having this stupid code here is
* that __put_task_struct is not exported by kernel. */
for (;;) {
int status = wait_event_interruptible(hw_priv->bh_wq, ({
term = atomic_xchg(&hw_priv->bh_term, 0);
(term);
}));
if (status || term)
break;
}
#endif
}
return 0;
}
#else
extern int bes2600_bh_read_ctrl_reg(struct bes2600_common *priv, u32 *ctrl_reg); extern int bes2600_bh_read_ctrl_reg(struct bes2600_common *priv, u32 *ctrl_reg);
@@ -412,119 +958,6 @@ static void bes2600_bh_parse_wakeup_event(struct bes2600_common *hw_priv, struct
} }
} }
/*
* Direct-deliver an RX SKB into the WSM/mac80211 stack.
*
* Patch C v3 (no-relay architecture, matches cw1200): the bh thread
* calls bes2600_sdio_read_rx_batch which calls
* bes2600_sdio_extract_packets which calls THIS function per parsed
* SKB. No rx_queue, no sdio_rx_work, no inter-thread handoff.
*
* Single-writer-from-bh invariant on hw_priv->hw_bufs_used,
* hw_priv->hw_bufs_used_vif[] and hw_priv->wsm_tx_pending[] is
* preserved BY CONSTRUCTION — there is now only one writer (the bh
* thread itself), same as cw1200's design. No atomic_t conversion
* needed.
*
* Contract:
* - process context, sleepable. wsm_handle_rx (wsm.c, EXPORT_SYMBOL)
* acquires wsm_cmd.lock and may sleep on wait_event_timeout.
* - caller holds no bes2600 spinlock. bes2600_sdio_unlock(self) is
* called inside read_rx_batch before extract_packets is invoked.
* - SKB ownership: function frees on every path (success + error).
* - No need to wake the bh thread on TX-confirm — we ARE the bh
* thread; tx_burst is signalled by returning *tx_out = 1 to the
* caller (bh_rx_helper), which propagates it to bh's outer loop.
*/
int bes2600_bh_handle_rx_skb(struct bes2600_common *priv, struct sk_buff *skb)
{
struct wsm_hdr *wsm;
size_t wsm_len;
u16 wsm_id;
u8 wsm_seq;
int tx = 0;
u32 confirm_label = 0x0;
if (!skb)
return 0;
wsm = (struct wsm_hdr *)skb->data;
wsm_len = __le16_to_cpu(wsm->len);
if (WARN_ON(wsm_len > skb->len)) {
bes_err("wsm_len err %d %d\n", (int)wsm_len, (int)skb->len);
dev_kfree_skb(skb);
return -1;
}
if (priv->wsm_enable_wsm_dumps)
print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, 16, 1,
skb->data, wsm_len, false);
wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
bes_devel("bes2600_bh_handle_rx_skb wsm_id:0x%04x seq:%d\n",
wsm_id, wsm_seq);
skb_trim(skb, wsm_len);
if (wsm_id == 0x0800) {
wsm_handle_exception(priv,
&skb->data[sizeof(*wsm)],
wsm_len - sizeof(*wsm));
bes_err("wsm exception\n");
dev_kfree_skb(skb);
return -1;
} else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) {
bes_err("seq error! %u. %u. 0x%x.", wsm_seq,
priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id);
dev_kfree_skb(skb);
return -1;
}
bes2600_bh_parse_wakeup_event(priv, skb);
priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7;
if (IS_DRIVER_TO_MCU_CMD(wsm_id))
confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label);
if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) {
int rc = wsm_release_tx_buffer(priv, 1);
bes2600_bh_dec_pending_count(priv, WSM_TXRX_SEQ_IDX(wsm->id));
if (rc < 0) {
bes_err("wsm_release_tx_buffer failed: %d\n", rc);
dev_kfree_skb(skb);
return rc;
} else if (rc > 0) {
tx = 1;
}
}
/* wsm_handle_rx takes care of SKB lifetime: zeroes *skb_p if consumed. */
if (wsm_handle_rx(priv, wsm_id, wsm, &skb)) {
bes_err("wsm_handle_rx failed (id=0x%04x)\n", wsm_id);
if (skb)
dev_kfree_skb(skb);
return -1;
}
if (skb)
dev_kfree_skb(skb);
/*
* Signal "tx side has new headroom" via atomic so the bh outer
* loop's wait_event predicate notices on its next wait. No
* cross-thread wake needed because we are the bh thread; the
* outer loop will pick this up after read_rx_batch returns.
*/
if (tx)
atomic_inc(&priv->bh_tx);
return 0;
}
EXPORT_SYMBOL(bes2600_bh_handle_rx_skb);
static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx) static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx)
{ {
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
@@ -536,18 +969,10 @@ static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx)
u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */ u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */
#if defined(BES_SDIO_RX_MULTIPLE_ENABLE) #if defined(BES_SDIO_RX_MULTIPLE_ENABLE)
/* skb = (struct sk_buff *)priv->sbus_ops->pipe_read(priv->sbus_priv);
* Patch C v3: the bh thread does the SDIO read inline via if (!skb)
* sbus_ops->bus_rx_batch. bes2600_sdio_read_rx_batch reads the
* multi-RX coalesced frames out of the chip and delivers each
* one inline via bes2600_bh_handle_rx_skb (no rx_queue, no
* pipe_read, no inter-thread handoff). Return value: 0 on
* success (bh outer loop will check whether to continue),
* negative on read error.
*/
if (priv->sbus_ops->bus_rx_batch)
return priv->sbus_ops->bus_rx_batch(priv->sbus_priv);
return 0; return 0;
rx = 1; // always consider rx pipe not empty
#else #else
u32 ctrl_reg = 0; u32 ctrl_reg = 0;
size_t read_len = 0; size_t read_len = 0;
@@ -709,7 +1134,7 @@ static int bes2600_bh_tx_helper(struct bes2600_common *hw_priv,
tx_len += 4; tx_len += 4;
#endif #endif
atomic_inc(&hw_priv->bh_tx); atomic_add(1, &hw_priv->bh_tx);
tx_len = hw_priv->sbus_ops->align_size( tx_len = hw_priv->sbus_ops->align_size(
hw_priv->sbus_priv, tx_len); hw_priv->sbus_priv, tx_len);
@@ -1010,7 +1435,7 @@ static int bes2600_bh(void *arg)
bes_devel("[BH] Device resume.\n"); bes_devel("[BH] Device resume.\n");
atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
wake_up(&hw_priv->bh_evt_wq); wake_up(&hw_priv->bh_evt_wq);
atomic_inc(&hw_priv->bh_rx); atomic_add(1, &hw_priv->bh_rx);
goto done; goto done;
} }
@@ -1046,15 +1471,7 @@ static int bes2600_bh(void *arg)
tx = 0; tx = 0;
/* BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
* Patch H: BUG_ON -> WARN_ON_ONCE in the steady-state
* hot path. The original BUG_ON ran every bh-loop
* iteration; tripping it on a bookkeeping bug locks
* the kernel up during normal operation, which is
* the wrong response. WARN_ON_ONCE surfaces the
* issue without taking the system down.
*/
WARN_ON_ONCE(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used; tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used;
tx_allowed = tx_burst > 0; tx_allowed = tx_burst > 0;
@@ -1098,19 +1515,18 @@ static int bes2600_bh(void *arg)
goto tx; goto tx;
done: done:
/* /* Re-enable device interrupts */
* Patch H: dropped the dead `__bes2600_irq_enable(1)` / //hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
* `asm volatile("nop")` placeholder that used to sit here. //__bes2600_irq_enable(1);
* `__bes2600_irq_enable()` is a stub that returns 0 on //hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
* bes2600 silicon — the IRQ is managed by sdio_claim_irq asm volatile ("nop");
* and chip-side firmware, not by a driver-side enable bit.
* (cw1200 inherited the function from a different chip
* shape; bes2600 kept the stub but the call sites are
* meaningless.)
*/
;
} }
/* Explicitly disable device interrupts */
hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
__bes2600_irq_enable(0);
hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
if (!term) { if (!term) {
bes_err("[BH] Fatal error, exiting.\n"); bes_err("[BH] Fatal error, exiting.\n");
sdio_work_debug(hw_priv->sbus_priv); sdio_work_debug(hw_priv->sbus_priv);
@@ -1119,3 +1535,4 @@ static int bes2600_bh(void *arg)
} }
return 0; return 0;
} }
#endif
+6 -15
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Bottom-half thread interface for BES2600 mac80211 driver * Device handling thread interface for mac80211 BES2600 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2010, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_BH_H #ifndef BES2600_BH_H
@@ -39,15 +39,6 @@ int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id,
int bes2600_bh_sw_process(struct bes2600_common *hw_priv, int bes2600_bh_sw_process(struct bes2600_common *hw_priv,
struct wsm_tx_confirm *tx_confirm); struct wsm_tx_confirm *tx_confirm);
/*
* Direct-deliver an RX SKB into the WSM/mac80211 stack from the bh thread.
* Called by bes2600_sdio_extract_packets per RX frame, no queueing.
* Process context, sleepable, caller holds no bes2600 spinlock.
* Function frees skb on every path. See bh.c for full contract.
*/
int bes2600_bh_handle_rx_skb(struct bes2600_common *hw_priv,
struct sk_buff *skb);
void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx); void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx);
void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx); void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx);
+9 -16
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Debugging interface for BES2600 mac80211 driver * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/module.h> #include <linux/module.h>
@@ -110,20 +110,17 @@ static int bes2600_status_show_common(struct seq_file *seq, void *v)
int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0; int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0;
bool ba_ena; bool ba_ena;
/* spin_lock_bh(&hw_priv->ba_lock);
* Patch D: ba_lock removed. hw_priv->debug->ba_* are written only
* by the timer callback (single writer); reading without a lock is
* fine for stats. ba_ena is atomic_t.
*/
ba_cnt = hw_priv->debug->ba_cnt; ba_cnt = hw_priv->debug->ba_cnt;
ba_acc = hw_priv->debug->ba_acc; ba_acc = hw_priv->debug->ba_acc;
ba_cnt_rx = hw_priv->debug->ba_cnt_rx; ba_cnt_rx = hw_priv->debug->ba_cnt_rx;
ba_acc_rx = hw_priv->debug->ba_acc_rx; ba_acc_rx = hw_priv->debug->ba_acc_rx;
ba_ena = !!atomic_read(&hw_priv->ba_ena); ba_ena = hw_priv->ba_ena;
if (ba_cnt) if (ba_cnt)
ba_avg = ba_acc / ba_cnt; ba_avg = ba_acc / ba_cnt;
if (ba_cnt_rx) if (ba_cnt_rx)
ba_avg_rx = ba_acc_rx / ba_cnt_rx; ba_avg_rx = ba_acc_rx / ba_cnt_rx;
spin_unlock_bh(&hw_priv->ba_lock);
seq_puts(seq, "BES2600 Wireless LAN driver status\n"); seq_puts(seq, "BES2600 Wireless LAN driver status\n");
seq_printf(seq, "Hardware: %d.%d\n", seq_printf(seq, "Hardware: %d.%d\n",
@@ -545,10 +542,6 @@ static int bes2600_status_show_priv(struct seq_file *seq, void *v)
priv->listening ? " (listening)" : ""); priv->listening ? " (listening)" : "");
seq_printf(seq, "Assoc: %s\n", seq_printf(seq, "Assoc: %s\n",
bes2600_debug_join_status[priv->join_status]); bes2600_debug_join_status[priv->join_status]);
seq_printf(seq, "DecryptStormRecoveries: %u\n",
priv->decrypt_storm_recoveries);
seq_printf(seq, "ConnectionLossStormRecoveries: %u\n",
priv->connection_loss_storm_recoveries);
if (priv->rx_filter.promiscuous) if (priv->rx_filter.promiscuous)
seq_puts(seq, "Filter: promisc\n"); seq_puts(seq, "Filter: promisc\n");
else if (priv->rx_filter.fcs) else if (priv->rx_filter.fcs)
+6 -6
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Debugging interface for BES2600 mac80211 driver * DebugFS code for BES2600 mac80211 driver
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2011, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_DEBUG_H_INCLUDED #ifndef BES2600_DEBUG_H_INCLUDED
+6 -3
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* BT/WiFi coexistence (ePTA) for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/version.h> #include <linux/version.h>
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* BT/WiFi coexistence interface for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef __EPTA_COEX_H__ #ifndef __EPTA_COEX_H__
#define __EPTA_COEX_H__ #define __EPTA_COEX_H__
+6 -3
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* BT/WiFi coexistence request handling * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* BT/WiFi coexistence request interface * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef EPTA_REQUEST_H #ifndef EPTA_REQUEST_H
#define EPTA_REQUEST_H #define EPTA_REQUEST_H
+6 -6
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Firmware I/O for BES2600 mac80211 driver * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/init.h> #include <linux/init.h>
+6 -6
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Firmware I/O interface for BES2600 mac80211 driver * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef FWIO_H_INCLUDED #ifndef FWIO_H_INCLUDED
#define FWIO_H_INCLUDED #define FWIO_H_INCLUDED
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* HT capability config for BES2600 * HT-related code for BES2600 driver
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2010, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_HT_H_INCLUDED #ifndef BES2600_HT_H_INCLUDED
+10 -13
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Low-level device I/O for BES2600 mac80211 driver * Low-level device IO routines for BES2600 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/types.h> #include <linux/types.h>
@@ -324,10 +324,7 @@ out:
} }
#endif #endif
/* int __bes2600_irq_enable(int enable)
* Patch H: __bes2600_irq_enable stub removed. It was a no-op {
* (always returned 0) inherited from cw1200 where the analogous return 0;
* function manipulates the chip's IRQ-enable register. bes2600 }
* silicon manages SDIO IRQ via sdio_claim_irq and chip-side
* firmware there is no driver-side enable register to write.
*/
+10 -5
View File
@@ -1,12 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Low-level device I/O interface for BES2600 mac80211 driver * Low-level API for mac80211 BES2600 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2010, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Based on:
* UMAC BES2600 driver which is
* Copyright (c) 2010, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_HWIO_H_INCLUDED #ifndef BES2600_HWIO_H_INCLUDED
+8 -4
View File
@@ -1,9 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* ITP (in-band test mode) for BES2600 * mac80211 glue code for mac80211 BES2600 drivers
* ITP code
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2010, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/module.h> #include <linux/module.h>
@@ -566,7 +570,7 @@ int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data,
*burst = 2; *burst = 2;
atomic_set(&priv->bh_tx, 1); atomic_set(&priv->bh_tx, 1);
ktime_get_ts(&itp->last_sent); ktime_get_ts(&itp->last_sent);
atomic_inc(&itp->awaiting_confirm); atomic_add(1, &itp->awaiting_confirm);
spin_unlock_bh(&itp->tx_lock); spin_unlock_bh(&itp->tx_lock);
return 1; return 1;
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* ITP interface for BES2600 * ITP code for BES2600 mac80211 driver
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2011, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_ITP_H_INCLUDED #ifndef BES2600_ITP_H_INCLUDED
+7 -17
View File
@@ -1,18 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Main entry/init for BES2600 mac80211 driver * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
*
* Based on the mac80211 Prism54 code, which is
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
*
* Based on the islsm (softmac prism54) driver, which is
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/module.h> #include <linux/module.h>
@@ -490,20 +484,17 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
spin_lock_init(&hw_priv->rtsvalue_lock); spin_lock_init(&hw_priv->rtsvalue_lock);
INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work); INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work);
INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work); INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work);
INIT_WORK(&hw_priv->connection_loss_storm_recover_work,
bes2600_connection_loss_storm_recover);
spin_lock_init(&hw_priv->event_queue_lock); spin_lock_init(&hw_priv->event_queue_lock);
INIT_LIST_HEAD(&hw_priv->event_queue); INIT_LIST_HEAD(&hw_priv->event_queue);
INIT_WORK(&hw_priv->event_handler, bes2600_event_handler); INIT_WORK(&hw_priv->event_handler, bes2600_event_handler);
INIT_WORK(&hw_priv->ba_work, bes2600_ba_work); INIT_WORK(&hw_priv->ba_work, bes2600_ba_work);
/* Patch D: ba_lock removed; ba_acc/ba_cnt/etc are atomic_t. */ spin_lock_init(&hw_priv->ba_lock);
timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0); timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0);
if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats, if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats,
WLAN_LINK_ID_MAX, WLAN_LINK_ID_MAX,
bes2600_skb_dtor, bes2600_skb_dtor,
hw_priv))) { hw_priv))) {
destroy_workqueue(hw_priv->workqueue);
ieee80211_free_hw(hw); ieee80211_free_hw(hw);
return NULL; return NULL;
} }
@@ -515,7 +506,6 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
for (; i > 0; i--) for (; i > 0; i--)
bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]); bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]);
bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats); bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats);
destroy_workqueue(hw_priv->workqueue);
ieee80211_free_hw(hw); ieee80211_free_hw(hw);
return NULL; return NULL;
} }
+6 -6
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Power management for BES2600 mac80211 driver * Mac80211 power management API for BES2600 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2011, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/platform_device.h> #include <linux/platform_device.h>
+6 -6
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Power management interface for BES2600 mac80211 driver * Mac80211 power management interface for BES2600 mac80211 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2011, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef PM_H_INCLUDED #ifndef PM_H_INCLUDED
+9 -9
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* O(1) TX queue for BES2600 mac80211 driver * O(1) TX queue with built-in allocator for BES2600 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <net/mac80211.h> #include <net/mac80211.h>
@@ -829,19 +829,19 @@ int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID,
bes2600_queue_parse_id(packetID, &queue_generation, &queue_id, bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
&item_generation, &item_id, &if_id, &link_id); &item_generation, &item_id, &if_id, &link_id);
spin_lock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock); spin_lock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
if (!list_empty(&queue->stats->hw_priv->tx_loop.pending_record_list)) { if (!list_empty(&queue->stats->hw_priv->tx_loop.pending_record_list)) {
list_for_each_entry_safe(record_item, temp_record_item, &queue->stats->hw_priv->tx_loop.pending_record_list, head) { list_for_each_entry_safe(record_item, temp_record_item, &queue->stats->hw_priv->tx_loop.pending_record_list, head) {
if (record_item->packetID == packetID) { if (record_item->packetID == packetID) {
list_del(&record_item->head); list_del(&record_item->head);
dev_kfree_skb(record_item->skb); dev_kfree_skb(record_item->skb);
kfree(record_item); kfree(record_item);
spin_unlock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock); spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
return -EINVAL; return -EINVAL;
} }
} }
} }
spin_unlock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock); spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
item = &queue->pool[item_id]; item = &queue->pool[item_id];
+6 -6
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* O(1) TX queue interface for BES2600 mac80211 driver * O(1) TX queue with built-in allocator for BES2600 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2010, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_QUEUE_H_INCLUDED #ifndef BES2600_QUEUE_H_INCLUDED
+8 -23
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Bus abstraction interface for BES2600 * Common sbus abstraction layer interface for bes2600 wireless driver
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* Replaces hwbus.h from drivers/net/wireless/st/cw1200/
* Copyright (c) 2010, ST-Ericsson
* *
* Copyright (c) 2010, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_SBUS_H #ifndef BES2600_SBUS_H
@@ -75,26 +75,11 @@ struct sbus_ops {
void (*halt_device)(struct sbus_priv *self); void (*halt_device)(struct sbus_priv *self);
bool (*wakeup_source)(struct sbus_priv *self); bool (*wakeup_source)(struct sbus_priv *self);
int (*reboot)(struct sbus_priv *self); int (*reboot)(struct sbus_priv *self);
/*
* Force the host bus to re-detect and re-probe the chip. Called
* from the firmware-wedge recovery path when power_switch() has no
* effective chip-reset signal of its own (e.g. PineTab2, where the
* wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 node).
* Returns 0 on success or a negative errno.
*/
int (*bus_reset)(struct sbus_priv *self);
/*
* Read a batch of RX frames inline from the bus and deliver each
* one via bes2600_bh_handle_rx_skb(). Called from the bh thread
* (process context, sleepable). Replaces the
* sdio_rx_work + rx_queue + pipe_read relay (Patch C v3, 2026).
* Returns 0 on success, negative on read error.
*/
int (*bus_rx_batch)(struct sbus_priv *self);
}; };
void bes2600_irq_handler(struct bes2600_common *priv); void bes2600_irq_handler(struct bes2600_common *priv);
/* Patch H: __bes2600_irq_enable removed (was a stub). */ /* This MUST be wrapped with hwbus_ops->lock/unlock! */
int __bes2600_irq_enable(int enable);
#endif /* BES2600_SBUS_H */ #endif /* BES2600_SBUS_H */
+22 -59
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Scan implementation for BES2600 mac80211 driver * Scan implementation for BES2600 mac80211 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/sched.h> #include <linux/sched.h>
@@ -238,36 +238,6 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
/* Scan when P2P_GO corrupt firmware MiniAP mode */ /* Scan when P2P_GO corrupt firmware MiniAP mode */
if (priv->join_status == BES2600_JOIN_STATUS_AP) if (priv->join_status == BES2600_JOIN_STATUS_AP)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/*
* Firmware refuses WSM start-scan for 5 GHz with status 2 ("rejected
* by policy"); see besser issue #1. mac80211 splits multi-band
* hw_scan requests per-band when the driver does not set
* IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't -- see
* ieee80211_hw_set() calls in bes2600_main.c), so each per-band call
* has req->channels[] from one band only (see ieee80211_prep_hw_scan
* in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
* boundary so userspace gets a clean aborted-scan for that portion
* rather than waiting for the firmware reject to cascade up.
*
* Only the multi-channel case is refused (n_channels > 1): that's
* the per-band-sweep pattern mac80211 issues internally and the
* one that triggers the firmware storm at the per-band loop
* boundary. Single-channel 5 GHz scans (BSS verification, NM's
* per-freq iteration when 802-11-wireless.band=a is set) pass
* through to firmware, which generally accepts them since the
* storm is the back-to-back per-band issue, not a blanket 5 GHz
* reject. This preserves 5 GHz association via the
* "wpa_supplicant iterates freq_list per channel" path.
*
* Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
* documentation, a negative return aborts the scan without requiring
* ieee80211_scan_completed().
*/
if (req->n_channels > 1 &&
req->channels[0]->band == NL80211_BAND_5GHZ)
return -EOPNOTSUPP;
#if 0 #if 0
if (work_pending(&priv->offchannel_work) || if (work_pending(&priv->offchannel_work) ||
(hw_priv->roc_if_id != -1)) { (hw_priv->roc_if_id != -1)) {
@@ -287,21 +257,18 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN); bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN);
/* will be unlocked in bes2600_scan_work() */
down(&hw_priv->scan.lock);
down(&hw_priv->conf_lock);
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
req->ie_len); req->ie_len);
if (!frame.skb) { if (!frame.skb)
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
return -ENOMEM; return -ENOMEM;
}
if (req->ie_len) if (req->ie_len)
skb_put_data(frame.skb, req->ie, req->ie_len); skb_put_data(frame.skb, req->ie, req->ie_len);
/* will be unlocked in bes2600_scan_work() */
down(&hw_priv->scan.lock);
down(&hw_priv->conf_lock);
if (frame.skb) { if (frame.skb) {
int ret; int ret;
//if (priv->if_id == 0) //if (priv->if_id == 0)
@@ -319,9 +286,9 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
} }
#endif #endif
if (ret) { if (ret) {
dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock); up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock); up(&hw_priv->scan.lock);
dev_kfree_skb(frame.skb);
return ret; return ret;
} }
} }
@@ -351,10 +318,10 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
++hw_priv->scan.n_ssids; ++hw_priv->scan.n_ssids;
} }
up(&hw_priv->conf_lock);
if (frame.skb) if (frame.skb)
dev_kfree_skb(frame.skb); dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock);
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE #ifdef WIFI_BT_COEXIST_EPTA_ENABLE
bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING); bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING);
#endif #endif
@@ -395,18 +362,14 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
if (req->n_ssids > hw->wiphy->max_scan_ssids) if (req->n_ssids > hw->wiphy->max_scan_ssids)
return -EINVAL; return -EINVAL;
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
req->ie_len);
if (!frame.skb)
return -ENOMEM;
/* will be unlocked in bes2600_scan_work() */ /* will be unlocked in bes2600_scan_work() */
down(&hw_priv->scan.lock); down(&hw_priv->scan.lock);
down(&hw_priv->conf_lock); down(&hw_priv->conf_lock);
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
req->ie_len);
if (!frame.skb) {
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
return -ENOMEM;
}
if (frame.skb) { if (frame.skb) {
int ret; int ret;
if (priv->if_id == 0) if (priv->if_id == 0)
@@ -417,9 +380,9 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
ret = wsm_set_probe_responder(priv, true); ret = wsm_set_probe_responder(priv, true);
} }
if (ret) { if (ret) {
dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock); up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock); up(&hw_priv->scan.lock);
dev_kfree_skb(frame.skb);
return ret; return ret;
} }
} }
@@ -451,10 +414,10 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
} }
} }
up(&hw_priv->conf_lock);
if (frame.skb) if (frame.skb)
dev_kfree_skb(frame.skb); dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock);
queue_work(hw_priv->workqueue, &hw_priv->scan.swork); queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n"); wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n");
return 0; return 0;
+6 -6
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Scan interface for BES2600 mac80211 driver * Scan interface for BES2600 mac80211 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2010, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef SCAN_H_INCLUDED #ifndef SCAN_H_INCLUDED
+43 -177
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Mac80211 STA API for BES2600 mac80211 driver * Mac80211 STA API for BES2600 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
@@ -266,7 +266,6 @@ void bes2600_stop(struct ieee80211_hw *dev, bool suspend)
cancel_work_sync(&hw_priv->coex_work); cancel_work_sync(&hw_priv->coex_work);
coex_stop(hw_priv); coex_stop(hw_priv);
#endif #endif
cancel_work_sync(&hw_priv->connection_loss_storm_recover_work);
bes2600_wifi_stop(hw_priv); bes2600_wifi_stop(hw_priv);
@@ -449,7 +448,6 @@ void bes2600_remove_interface(struct ieee80211_hw *dev,
cancel_delayed_work_sync(&priv->join_timeout); cancel_delayed_work_sync(&priv->join_timeout);
cancel_delayed_work_sync(&priv->set_cts_work); cancel_delayed_work_sync(&priv->set_cts_work);
cancel_delayed_work_sync(&priv->pending_offchanneltx_work); cancel_delayed_work_sync(&priv->pending_offchanneltx_work);
cancel_work_sync(&priv->decrypt_storm_recover_work);
del_timer_sync(&priv->mcast_timeout); del_timer_sync(&priv->mcast_timeout);
/* TODO:COMBO: May be reset of these variables "delayed_link_loss and /* TODO:COMBO: May be reset of these variables "delayed_link_loss and
@@ -1484,7 +1482,7 @@ void bes2600_event_handler(struct work_struct *work)
IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS); IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS);
deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING;
deauth->seq_ctrl = 0; deauth->seq_ctrl = 0;
ieee80211_rx_ni(priv->hw, skb); ieee80211_rx_irqsafe(priv->hw, skb);
bes_devel(" Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da); bes_devel(" Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da);
queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); queue_work(priv->hw_priv->workqueue, &priv->set_tim_work);
break; break;
@@ -1660,70 +1658,6 @@ report:
spin_unlock(&priv->bss_loss_lock); spin_unlock(&priv->bss_loss_lock);
} }
/*
* Connection-loss-storm fast-recover (Trigger A).
*
* bes2600_connection_loss_work below is the driver's own decision-point
* to give up on a BSS (after bss-loss detection accumulates beyond
* tolerance) and tell mac80211 via ieee80211_connection_loss(). On the
* deployed pinetab2 stack a single ieee80211_connection_loss() event
* sometimes triggers a userspace reauth blackhole (assoc-comeback
* timeouts followed by AP unprotected-deauth-reason-6) that ends only
* via cross-channel/cross-SSID fallback and can take 80+ s. Receipts at
* https://git.reauktion.de/marfrit/besser, notes/phase4-2026-05-07.md.
*
* When N connection-loss decisions land within WINDOW on the same vif,
* skip the ieee80211_connection_loss() path and trigger a chip-level
* bus_reset (the c5.2-introduced bes2600_chrdev_do_bus_reset). The chip
* is removed and re-probed; userspace re-associates from a fresh state,
* dodging the assoc-comeback loop.
*
* Threshold (3 / 60 s) is chosen well above the steady-state per-vif
* connection-loss rate observed in the patch-A Phase-7 rep
* (0.86/h under sustained load), so a true storm is required.
*
* The recover work_struct lives on bes2600_common (hw_priv) so that
* scheduling it does not race with vif teardown after bus_reset frees
* the per-vif state.
*/
#define BES2600_CONNECTION_LOSS_STORM_THRESHOLD 3
#define BES2600_CONNECTION_LOSS_STORM_WINDOW_MS 60000
void bes2600_connection_loss_storm_recover(struct work_struct *work)
{
bes_warn("[bes2600] connection-loss-storm fast-recover: bus_reset\n");
bes2600_chrdev_trigger_bus_reset();
/*
* After bes2600_chrdev_do_bus_reset() returns, the SDIO core has
* scheduled a remove + rescan; per-vif state may already be gone.
* Do not dereference any per-vif pointer here.
*/
}
void bes2600_connection_loss_storm_init(struct bes2600_vif *priv)
{
priv->connection_loss_storm_window_start = 0;
priv->connection_loss_storm_count = 0;
priv->connection_loss_storm_recoveries = 0;
}
bool bes2600_connection_loss_storm_account(struct bes2600_vif *priv)
{
unsigned long now = jiffies;
unsigned long window =
msecs_to_jiffies(BES2600_CONNECTION_LOSS_STORM_WINDOW_MS);
if (priv->connection_loss_storm_window_start == 0 ||
time_after(now, priv->connection_loss_storm_window_start + window)) {
priv->connection_loss_storm_window_start = now;
priv->connection_loss_storm_count = 1;
return false;
}
return ++priv->connection_loss_storm_count >=
BES2600_CONNECTION_LOSS_STORM_THRESHOLD;
}
void bes2600_connection_loss_work(struct work_struct *work) void bes2600_connection_loss_work(struct work_struct *work)
{ {
struct bes2600_vif *priv = struct bes2600_vif *priv =
@@ -1733,21 +1667,9 @@ void bes2600_connection_loss_work(struct work_struct *work)
bes_devel("[CQM] Reporting connection loss.\n"); bes_devel("[CQM] Reporting connection loss.\n");
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_BSS_LOST); bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_BSS_LOST);
if(bes2600_suspend_status_get(hw_priv)) {
if (bes2600_connection_loss_storm_account(priv)) {
bes_warn("[bes2600] connection-loss storm: %u in %u s, scheduling bus reset\n",
priv->connection_loss_storm_count,
BES2600_CONNECTION_LOSS_STORM_WINDOW_MS / 1000);
priv->connection_loss_storm_count = 0;
priv->connection_loss_storm_recoveries++;
schedule_work(&hw_priv->connection_loss_storm_recover_work);
/* bus_reset will tear the chip down; skip the mac80211 path. */
return;
}
if (bes2600_suspend_status_get(hw_priv))
bes2600_pending_unjoin_set(hw_priv, priv->if_id); bes2600_pending_unjoin_set(hw_priv, priv->if_id);
else } else
ieee80211_connection_loss(priv->vif); ieee80211_connection_loss(priv->vif);
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE #ifdef WIFI_BT_COEXIST_EPTA_ENABLE
// set disconnected in BSS_CHANGED_ASSOC // set disconnected in BSS_CHANGED_ASSOC
@@ -2209,10 +2131,9 @@ void bes2600_join_work(struct work_struct *work)
struct wsm_template_frame probe_tmp = { struct wsm_template_frame probe_tmp = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
}; };
struct wsm_reset join_fail_reset = { /*struct wsm_reset reset = {
.reset_statistics = false, .reset_statistics = true,
}; };*/
bool join_failed = false;
BUG_ON(queueId >= 4); BUG_ON(queueId >= 4);
@@ -2343,19 +2264,14 @@ void bes2600_join_work(struct work_struct *work)
//WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id)); //WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id));
WARN_ON(wsm_set_block_ack_policy(hw_priv, WARN_ON(wsm_set_block_ack_policy(hw_priv,
0, hw_priv->ba_tid_mask, priv->if_id)); 0, hw_priv->ba_tid_mask, priv->if_id));
/* spin_lock_bh(&hw_priv->ba_lock);
* Patch D: ba_lock removed. Disconnect-reset clears the hw_priv->ba_ena = false;
* counters and the arm flag; producers racing here cannot hw_priv->ba_cnt = 0;
* cause harm at worst they re-arm the timer and bump hw_priv->ba_acc = 0;
* counters that will be cleared on the next timer tick.
*/
atomic_set(&hw_priv->ba_ena, 0);
atomic_set(&hw_priv->ba_cnt, 0);
atomic_set(&hw_priv->ba_acc, 0);
hw_priv->ba_hist = 0; hw_priv->ba_hist = 0;
atomic_set(&hw_priv->ba_cnt_rx, 0); hw_priv->ba_cnt_rx = 0;
atomic_set(&hw_priv->ba_acc_rx, 0); hw_priv->ba_acc_rx = 0;
atomic_set(&hw_priv->ba_armed, 0); spin_unlock_bh(&hw_priv->ba_lock);
mgmt_policy.protectedMgmtEnable = 0; mgmt_policy.protectedMgmtEnable = 0;
mgmt_policy.unprotectedMgmtFramesAllowed = 1; mgmt_policy.unprotectedMgmtFramesAllowed = 1;
@@ -2391,33 +2307,6 @@ void bes2600_join_work(struct work_struct *work)
#endif /*CONFIG_BES2600_TESTMODE*/ #endif /*CONFIG_BES2600_TESTMODE*/
cancel_delayed_work_sync(&priv->join_timeout); cancel_delayed_work_sync(&priv->join_timeout);
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN); bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN);
/*
* Firmware rejected WSM_JOIN (wsm_join_confirm ret 1).
* Issue wsm_reset so the firmware returns to a clean
* IDLE state before the next association attempt.
*
* Without this reset the firmware sits in an
* intermediate post-reject state. A rapid second
* JOIN (e.g. wpa_supplicant retrying after the
* PREV_AUTH_NOT_VALID deauth that follows) hits an
* inconsistent firmware context, causing
* bes2600_sdio_read_rx_batch to return SDIO error
* which cascades into wifi_force_close.
*
* cw1200 ancestor (drivers/net/wireless/st/cw1200/
* sta.c:1339) queues unjoin_work on join failure for
* the same reason; bes2600_unjoin_work gates its
* wsm_reset on join_status != PASSIVE, so after a
* failed JOIN (join_status stays PASSIVE) that path
* never fires call wsm_reset directly here instead.
*
* Contract: wsm_reset takes only wsm_cmd_lock; safe
* to call while conf_lock is held. wsm_oper_unlock
* was already called in wsm_join_confirm() before
* wsm_join() returned the error.
*/
WARN_ON(wsm_reset(hw_priv, &join_fail_reset, priv->if_id));
join_failed = true;
} else { } else {
/* Upload keys */ /* Upload keys */
#ifdef CONFIG_BES2600_TESTMODE #ifdef CONFIG_BES2600_TESTMODE
@@ -2442,18 +2331,7 @@ void bes2600_join_work(struct work_struct *work)
up(&hw_priv->conf_lock); up(&hw_priv->conf_lock);
if (bss) if (bss)
cfg80211_put_bss(hw_priv->hw->wiphy, bss); cfg80211_put_bss(hw_priv->hw->wiphy, bss);
/*
* On join failure: queue unjoin_work so the next association
* attempt is serialised after any lingering cleanup, matching
* cw1200 sta.c:1344 "Tx lock still held, unjoin will clear it."
* If unjoin_work is already queued, release TX immediately.
*/
if (join_failed) {
if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
wsm_unlock_tx(hw_priv); wsm_unlock_tx(hw_priv);
} else {
wsm_unlock_tx(hw_priv);
}
} }
void bes2600_join_timeout(struct work_struct *work) void bes2600_join_timeout(struct work_struct *work)
@@ -2673,11 +2551,10 @@ void bes2600_ba_work(struct work_struct *work)
return;*/ return;*/
bes_devel("BA work****\n"); bes_devel("BA work****\n");
/* spin_lock_bh(&hw_priv->ba_lock);
* Patch D: ba_lock removed. ba_tid_mask is u8 set once at init // tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0;
* (main.c); reading it without a lock is fine.
*/
tx_ba_tid_mask = hw_priv->ba_tid_mask; tx_ba_tid_mask = hw_priv->ba_tid_mask;
spin_unlock_bh(&hw_priv->ba_lock);
wsm_lock_tx(hw_priv); wsm_lock_tx(hw_priv);
@@ -2690,49 +2567,37 @@ void bes2600_ba_work(struct work_struct *work)
void bes2600_ba_timer(struct timer_list *t) void bes2600_ba_timer(struct timer_list *t)
{ {
bool ba_ena; bool ba_ena;
int cnt, acc, cnt_rx, acc_rx;
struct bes2600_common *hw_priv = from_timer(hw_priv, t, ba_timer); struct bes2600_common *hw_priv = from_timer(hw_priv, t, ba_timer);
/* spin_lock_bh(&hw_priv->ba_lock);
* Patch D: ba_lock removed. Snapshot atomic counters into locals bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc,
* for the predicate evaluation; producers may race incrementing hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx);
* after the snapshot but the resulting decision is approximate
* which the policy already tolerates (next timer tick re-evaluates).
*/
cnt = atomic_read(&hw_priv->ba_cnt);
acc = atomic_read(&hw_priv->ba_acc);
cnt_rx = atomic_read(&hw_priv->ba_cnt_rx);
acc_rx = atomic_read(&hw_priv->ba_acc_rx);
bes2600_debug_ba(hw_priv, cnt, acc, cnt_rx, acc_rx);
if (atomic_read(&hw_priv->scan.in_progress)) { if (atomic_read(&hw_priv->scan.in_progress)) {
atomic_set(&hw_priv->ba_cnt, 0); hw_priv->ba_cnt = 0;
atomic_set(&hw_priv->ba_acc, 0); hw_priv->ba_acc = 0;
atomic_set(&hw_priv->ba_cnt_rx, 0); hw_priv->ba_cnt_rx = 0;
atomic_set(&hw_priv->ba_acc_rx, 0); hw_priv->ba_acc_rx = 0;
atomic_set(&hw_priv->ba_armed, 0); goto skip_statistic_update;
return;
} }
if (cnt >= BES2600_BLOCK_ACK_CNT && if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT &&
(acc / cnt >= BES2600_BLOCK_ACK_THLD || (hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD ||
(cnt_rx >= BES2600_BLOCK_ACK_CNT && (hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT &&
acc_rx / cnt_rx >= hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >=
BES2600_BLOCK_ACK_THLD))) BES2600_BLOCK_ACK_THLD)))
ba_ena = true; ba_ena = true;
else else
ba_ena = false; ba_ena = false;
atomic_set(&hw_priv->ba_cnt, 0); hw_priv->ba_cnt = 0;
atomic_set(&hw_priv->ba_acc, 0); hw_priv->ba_acc = 0;
atomic_set(&hw_priv->ba_cnt_rx, 0); hw_priv->ba_cnt_rx = 0;
atomic_set(&hw_priv->ba_acc_rx, 0); hw_priv->ba_acc_rx = 0;
atomic_set(&hw_priv->ba_armed, 0);
if (ba_ena != !!atomic_read(&hw_priv->ba_ena)) { if (ba_ena != hw_priv->ba_ena) {
if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) { if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) {
atomic_set(&hw_priv->ba_ena, ba_ena ? 1 : 0); hw_priv->ba_ena = ba_ena;
hw_priv->ba_hist = 0; hw_priv->ba_hist = 0;
#if 0 #if 0
bes_devel("[STA] %s block ACK:\n", bes_devel("[STA] %s block ACK:\n",
@@ -2742,6 +2607,9 @@ void bes2600_ba_timer(struct timer_list *t)
} }
} else if (hw_priv->ba_hist) } else if (hw_priv->ba_hist)
--hw_priv->ba_hist; --hw_priv->ba_hist;
skip_statistic_update:
spin_unlock_bh(&hw_priv->ba_lock);
} }
int bes2600_vif_setup(struct bes2600_vif *priv) int bes2600_vif_setup(struct bes2600_vif *priv)
@@ -2751,8 +2619,6 @@ int bes2600_vif_setup(struct bes2600_vif *priv)
/* Setup per vif workitems and locks */ /* Setup per vif workitems and locks */
spin_lock_init(&priv->vif_lock); spin_lock_init(&priv->vif_lock);
bes2600_decrypt_storm_init(priv);
bes2600_connection_loss_storm_init(priv);
INIT_WORK(&priv->join_work, bes2600_join_work); INIT_WORK(&priv->join_work, bes2600_join_work);
INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout); INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout);
INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work); INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work);
+6 -6
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Mac80211 STA API interface for BES2600 mac80211 driver * Mac80211 STA interface for BES2600 mac80211 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2010, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/version.h> #include <linux/version.h>
#ifndef STA_H_INCLUDED #ifndef STA_H_INCLUDED
+8 -5
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Test-mode TX loopback for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include "bes2600.h" #include "bes2600.h"
#include "wsm.h" #include "wsm.h"
@@ -109,9 +112,9 @@ void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv, bool need_warn)
bes2600_queue_iterate_pending_packet(&hw_priv->tx_queue[i], bes2600_queue_iterate_pending_packet(&hw_priv->tx_queue[i],
bes2600_tx_loop_item_pending_item); bes2600_tx_loop_item_pending_item);
} }
spin_lock_bh(&hw_priv->tx_loop.pending_record_lock); spin_lock(&hw_priv->tx_loop.pending_record_lock);
bes2600_queue_iterate_record_pending_packet(hw_priv, bes2600_tx_loop_item_pending_item); bes2600_queue_iterate_record_pending_packet(hw_priv, bes2600_tx_loop_item_pending_item);
spin_unlock_bh(&hw_priv->tx_loop.pending_record_lock); spin_unlock(&hw_priv->tx_loop.pending_record_lock);
if (atomic_read(&hw_priv->bh_rx) > 0) if (atomic_read(&hw_priv->bh_rx) > 0)
wake_up(&hw_priv->bh_wq); wake_up(&hw_priv->bh_wq);
+6 -3
View File
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Test-mode TX loopback interface for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef __TX_LOOP_H__ #ifndef __TX_LOOP_H__
#define __TX_LOOP_H__ #define __TX_LOOP_H__
+18 -113
View File
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Datapath implementation for BES2600 mac80211 driver * Datapath implementation for BES2600 mac80211 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <net/mac80211.h> #include <net/mac80211.h>
@@ -25,78 +25,6 @@
#define BES2600_INVALID_RATE_ID (0xFF) #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 #ifdef CONFIG_BES2600_TESTMODE
#include "bes_nl80211_testmode_msg.h" #include "bes_nl80211_testmode_msg.h"
#endif /* CONFIG_BES2600_TESTMODE */ #endif /* CONFIG_BES2600_TESTMODE */
@@ -995,18 +923,14 @@ bes2600_tx_h_ba_stat(struct bes2600_vif *priv,
if (!ieee80211_is_data(t->hdr->frame_control)) if (!ieee80211_is_data(t->hdr->frame_control))
return; return;
/* spin_lock_bh(&hw_priv->ba_lock);
* Patch D: lock-free hot-path BA accounting. atomic_inc + atomic_add hw_priv->ba_acc += t->skb->len - t->hdrlen;
* each per-frame; the once-per-window timer-arm uses cmpxchg on if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
* ba_armed so concurrent TX/RX can't both try to set the timer and
* we don't need cross-counter coherency on the ba_cnt/ba_cnt_rx pair.
*/
atomic_add(t->skb->len - t->hdrlen, &hw_priv->ba_acc);
atomic_inc(&hw_priv->ba_cnt);
if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) {
mod_timer(&hw_priv->ba_timer, mod_timer(&hw_priv->ba_timer,
jiffies + BES2600_BLOCK_ACK_INTERVAL); jiffies + BES2600_BLOCK_ACK_INTERVAL);
} }
hw_priv->ba_cnt++;
spin_unlock_bh(&hw_priv->ba_lock);
} }
static int static int
@@ -1633,13 +1557,14 @@ bes2600_rx_h_ba_stat(struct bes2600_vif *priv,
if (!priv->setbssparams_done) if (!priv->setbssparams_done)
return; return;
/* Patch D: lock-free hot-path BA accounting; see TX side comment. */ spin_lock_bh(&hw_priv->ba_lock);
atomic_add(skb_len - hdrlen, &hw_priv->ba_acc_rx); hw_priv->ba_acc_rx += skb_len - hdrlen;
atomic_inc(&hw_priv->ba_cnt_rx); if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) {
mod_timer(&hw_priv->ba_timer, mod_timer(&hw_priv->ba_timer,
jiffies + BES2600_BLOCK_ACK_INTERVAL); jiffies + BES2600_BLOCK_ACK_INTERVAL);
} }
hw_priv->ba_cnt_rx++;
spin_unlock_bh(&hw_priv->ba_lock);
} }
void bes2600_rx_cb(struct bes2600_vif *priv, void bes2600_rx_cb(struct bes2600_vif *priv,
@@ -1747,8 +1672,6 @@ void bes2600_rx_cb(struct bes2600_vif *priv,
goto drop; goto drop;
} else { } else {
bes_warn("[RX] Receive failure: %d.\n", arg->status); bes_warn("[RX] Receive failure: %d.\n", arg->status);
if (arg->status == WSM_STATUS_DECRYPTFAILURE)
bes2600_decrypt_storm_account(priv);
goto drop; goto drop;
} }
} }
@@ -1942,33 +1865,15 @@ void bes2600_rx_cb(struct bes2600_vif *priv,
if (unlikely(bes2600_itp_rxed(hw_priv, skb))) if (unlikely(bes2600_itp_rxed(hw_priv, skb)))
consume_skb(skb); consume_skb(skb);
else if (unlikely(early_data)) { else if (unlikely(early_data)) {
/*
* Patch E: when c7 has latched pm_unsupported (firmware
* doesn't honour PSM, see feedback_bes2600_firmware_no_psm),
* AP-side power-save state machine is dead and link entries
* never transition to BES2600_LINK_SOFT. The double-check
* branch under ps_state_lock is unreachable in that case,
* so skip the per-frame lock acquisition entirely and
* deliver to mac80211 directly.
*
* On firmware that does honour PSM (the latch self-clears
* if a real PM_INDICATION ever arrives see c7), this
* predicate flips back to false and the original locked
* path is taken.
*/
if (hw_priv->bes_power.pm_unsupported) {
ieee80211_rx_ni(priv->hw, skb);
} else {
spin_lock_bh(&priv->ps_state_lock); spin_lock_bh(&priv->ps_state_lock);
/* Double-check status with lock held */ /* Double-check status with lock held */
if (entry->status == BES2600_LINK_SOFT) if (entry->status == BES2600_LINK_SOFT)
skb_queue_tail(&entry->rx_queue, skb); skb_queue_tail(&entry->rx_queue, skb);
else else
ieee80211_rx_ni(priv->hw, skb); ieee80211_rx_irqsafe(priv->hw, skb);
spin_unlock_bh(&priv->ps_state_lock); spin_unlock_bh(&priv->ps_state_lock);
}
} else { } else {
ieee80211_rx_ni(priv->hw, skb); ieee80211_rx_irqsafe(priv->hw, skb);
} }
*skb_p = NULL; *skb_p = NULL;
+6 -6
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Datapath interface for BES2600 mac80211 driver * Datapath interface for BES2600 mac80211 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2010, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_TXRX_H #ifndef BES2600_TXRX_H
+6 -3
View File
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* WiFi testmode commands for BES2600 * Mac80211 driver for BES2600 device
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Copyright (c) 2022, Bestechnic
* Author:
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifdef CONFIG_BES2600_TESTMODE #ifdef CONFIG_BES2600_TESTMODE
#include <net/netlink.h> #include <net/netlink.h>
+8 -7
View File
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* WSM host interface for BES2600 mac80211 driver * WSM host interface (HI) implementation for
* BES2600 mac80211 drivers.
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/skbuff.h> #include <linux/skbuff.h>
@@ -2412,7 +2413,7 @@ int wsm_handle_rx(struct bes2600_common *hw_priv, int id,
if (!hw_priv->beacon_bkp) if (!hw_priv->beacon_bkp)
hw_priv->beacon_bkp = \ hw_priv->beacon_bkp = \
skb_copy(hw_priv->beacon, GFP_ATOMIC); skb_copy(hw_priv->beacon, GFP_ATOMIC);
ieee80211_rx_ni(hw_priv->hw, hw_priv->beacon); ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon);
hw_priv->beacon = hw_priv->beacon_bkp; hw_priv->beacon = hw_priv->beacon_bkp;
hw_priv->beacon_bkp = NULL; hw_priv->beacon_bkp = NULL;
+9 -5
View File
@@ -1,12 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* WSM host interface for BES2600 mac80211 driver * WSM host interface (HI) interface for BES2600 mac80211 drivers
* *
* Copyright (c) 2010, ST-Ericsson * Copyright (c) 2022, Bestechnic
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> * Author:
* *
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. * Based on BES2600 UMAC WSM API, which is
* Copyright (C) SA 2010
* Author: Stewart Mathers <stewart.mathers@stericsson.com>
* *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef BES2600_WSM_H_INCLUDED #ifndef BES2600_WSM_H_INCLUDED