* [PATCH wireless-next 1/8] wifi: rt2x00: start the link tuner when an interface is added
[not found] <20261029134752.1299130-1-hjf3108@gmail.com>
@ 2026-10-29 13:47 ` Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 2/8] wifi: rt2800: calibrate the crystal of RT3593 with the beacons Ho Jie Feng
` (6 subsequent siblings)
7 siblings, 0 replies; 8+ messages in thread
From: Ho Jie Feng @ 2026-10-29 13:47 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, Ho Jie Feng, Kalle Valo, linux-kernel
rt2x00lib_start() enables the radio before mac80211 adds the first
interface, so rt2x00link_start_tuner(), called from
rt2x00lib_enable_radio(), finds no station or AP interface and returns.
Start the link tuner again when an interface is added.
Fixes: d96324703ffa ("rt2x00: merge agc and vco works with link tuner")
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
drivers/net/wireless/ralink/rt2x00/rt2x00mac.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
index 3bc0c1c906c91..d9e323ccc6d62 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
@@ -264,6 +264,12 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
*/
rt2x00dev->packet_filter = 0;
+ /*
+ * Start the link tuner again if the radio is enabled before the
+ * first interface is added.
+ */
+ rt2x00link_start_tuner(rt2x00dev);
+
return 0;
}
EXPORT_SYMBOL_GPL(rt2x00mac_add_interface);
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH wireless-next 2/8] wifi: rt2800: calibrate the crystal of RT3593 with the beacons
[not found] <20261029134752.1299130-1-hjf3108@gmail.com>
2026-10-29 13:47 ` [PATCH wireless-next 1/8] wifi: rt2x00: start the link tuner when an interface is added Ho Jie Feng
@ 2026-10-29 13:47 ` Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 3/8] wifi: rt2800: use the 3SS fallback table of RT3883 on RT3593 Ho Jie Feng
` (5 subsequent siblings)
7 siblings, 0 replies; 8+ messages in thread
From: Ho Jie Feng @ 2026-10-29 13:47 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, Ho Jie Feng, linux-kernel
On a Netgear WNDA4100 (RT3593), as the adapter warms up, the
frequency offset drifts enough to affect the link quality.
In certain cases it can cause the link speed to drop
to 1Mbit/s or below.
Add a frequency calibration step function to RT3593 to use
the frequency offset obtained from the beacon frames of the AP
for tuning.
Test results:
before after
TX 27.7 28.5
RX 31.6 69.7
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
drivers/net/wireless/ralink/rt2x00/rt2800.h | 1 +
.../net/wireless/ralink/rt2x00/rt2800lib.c | 101 ++++++++++++++++++
.../net/wireless/ralink/rt2x00/rt2800lib.h | 4 +
.../net/wireless/ralink/rt2x00/rt2800usb.c | 1 +
drivers/net/wireless/ralink/rt2x00/rt2x00.h | 6 ++
.../net/wireless/ralink/rt2x00/rt2x00lib.h | 2 +
.../net/wireless/ralink/rt2x00/rt2x00link.c | 9 ++
7 files changed, 124 insertions(+)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h
index 8930589b4967d..b9f6de3a462e3 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
@@ -3163,6 +3163,7 @@ enum rt2800_eeprom_word {
*/
#define RXWI_W3_SNR0 FIELD32(0x000000ff)
#define RXWI_W3_SNR1 FIELD32(0x0000ff00)
+#define RXWI_W3_FOFFSET FIELD32(0xff000000)
/*
* Macros for converting txpower from EEPROM to mac80211 value
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 2e2e382b62475..e7b6edf485099 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -897,6 +897,28 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2)
return (int)max(rssi0, rssi2);
}
+static void rt2800_rx_freq_offset(struct queue_entry *entry,
+ struct rxdone_entry_desc *rxdesc)
+{
+ struct rt2800_drv_data *drv_data = entry->queue->rt2x00dev->drv_data;
+ __le32 *rxwi = (__le32 *) entry->skb->data;
+ struct ieee80211_hdr *hdr =
+ (void *)(entry->skb->data + entry->queue->winfo_size);
+ u32 word;
+
+ if (!(rxdesc->dev_flags & RXDONE_MY_BSS) ||
+ (rxdesc->flags & RX_FLAG_FAILED_FCS_CRC) ||
+ !ieee80211_is_beacon(hdr->frame_control))
+ return;
+
+ word = rt2x00_desc_read(rxwi, 3);
+
+ spin_lock_bh(&drv_data->bcn_lock);
+ drv_data->bcn_freq_offset = rt2x00_get_field32(word, RXWI_W3_FOFFSET);
+ drv_data->bcn_rate_mode = rxdesc->rate_mode;
+ spin_unlock_bh(&drv_data->bcn_lock);
+}
+
void rt2800_process_rxwi(struct queue_entry *entry,
struct rxdone_entry_desc *rxdesc)
{
@@ -935,6 +957,14 @@ void rt2800_process_rxwi(struct queue_entry *entry,
* Convert descriptor AGC value to RSSI value.
*/
rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word);
+
+ /*
+ * The frequency offset of the beacons is used for the frequency
+ * calibration.
+ */
+ if (rt2x00_rt(entry->queue->rt2x00dev, RT3593))
+ rt2800_rx_freq_offset(entry, rxdesc);
+
/*
* Remove RXWI descriptor from start of the buffer.
*/
@@ -2443,6 +2473,7 @@ static inline bool rt2800_clk_is_20mhz(struct rt2x00_dev *rt2x00dev)
}
#define FREQ_OFFSET_BOUND 0x5f
+#define FREQ_OFFSET_INVALID -128
static void rt2800_freq_cal_mode1(struct rt2x00_dev *rt2x00dev)
{
@@ -5633,6 +5664,73 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev)
}
EXPORT_SYMBOL_GPL(rt2800_vco_calibration);
+void rt2800_freq_calibration(struct rt2x00_dev *rt2x00dev)
+{
+ struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+ struct link *link = &rt2x00dev->link;
+ u8 activate_threshold, deactivate_threshold;
+ u8 freq_offset, rate_mode;
+ u16 eeprom;
+ s8 offset;
+
+ if (!rt2x00_rt(rt2x00dev, RT3593))
+ return;
+
+ if (!rt2x00dev->intf_associated) {
+ eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ);
+ rt2x00dev->freq_offset = rt2x00_get_field16(eeprom,
+ EEPROM_FREQ_OFFSET);
+ link->freq_cal_adjusting = false;
+ return;
+ }
+
+ spin_lock_bh(&drv_data->bcn_lock);
+ offset = drv_data->bcn_freq_offset;
+ rate_mode = drv_data->bcn_rate_mode;
+ drv_data->bcn_freq_offset = FREQ_OFFSET_INVALID;
+ spin_unlock_bh(&drv_data->bcn_lock);
+
+ /* No beacons received */
+ if (offset == FREQ_OFFSET_INVALID)
+ return;
+
+ if (rate_mode == RATE_MODE_CCK) {
+ activate_threshold = 4;
+ deactivate_threshold = 2;
+ } else {
+ activate_threshold = 20;
+ deactivate_threshold = 10;
+ }
+
+ /* Hysteresis */
+ if (abs(offset) >= activate_threshold)
+ link->freq_cal_adjusting = true;
+ else if (abs(offset) <= deactivate_threshold)
+ link->freq_cal_adjusting = false;
+
+ if (!link->freq_cal_adjusting)
+ return;
+
+ freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE);
+ freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND);
+
+ if (offset > 0) {
+ if (freq_offset > 0)
+ freq_offset--;
+ else
+ link->freq_cal_adjusting = false;
+ } else {
+ if (freq_offset < FREQ_OFFSET_BOUND)
+ freq_offset++;
+ else
+ link->freq_cal_adjusting = false;
+ }
+
+ rt2x00dev->freq_offset = freq_offset;
+ rt2800_freq_cal_mode1(rt2x00dev);
+}
+EXPORT_SYMBOL_GPL(rt2800_freq_calibration);
+
static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf)
{
@@ -12032,6 +12130,7 @@ static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev)
int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
{
+ struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
int retval;
u32 reg;
@@ -12105,6 +12204,8 @@ int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
*/
rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
+ spin_lock_init(&drv_data->bcn_lock);
+
return 0;
}
EXPORT_SYMBOL_GPL(rt2800_probe_hw);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
index a3c3a751f57e3..9a04a0fba113f 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
@@ -45,6 +45,9 @@ struct rt2800_drv_data {
unsigned int ampdu_factor_cnt[4];
DECLARE_BITMAP(sta_ids, STA_IDS_SIZE);
struct ieee80211_sta *wcid_to_sta[STA_IDS_SIZE];
+ spinlock_t bcn_lock; /* protects bcn_freq_offset, bcn_rate_mode */
+ s8 bcn_freq_offset;
+ u8 bcn_rate_mode;
};
struct rt2800_ops {
@@ -241,6 +244,7 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
const u32 count);
void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev);
void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev);
+void rt2800_freq_calibration(struct rt2x00_dev *rt2x00dev);
int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev);
void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index 15de3db124298..387c07e2251b7 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
@@ -693,6 +693,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.link_tuner = rt2800_link_tuner,
.gain_calibration = rt2800_gain_calibration,
.vco_calibration = rt2800_vco_calibration,
+ .freq_calibration = rt2800_freq_calibration,
.watchdog = rt2800_watchdog,
.start_queue = rt2800usb_start_queue,
.kick_queue = rt2x00usb_kick_queue,
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index 7d313e86d3f20..a80508db32d9c 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -319,6 +319,11 @@ struct link {
*/
struct ewma_rssi avg_rssi;
+ /*
+ * Controls interval of frequency calibration
+ */
+ bool freq_cal_adjusting;
+
/*
* Work structure for scheduling periodic link tuning.
*/
@@ -559,6 +564,7 @@ struct rt2x00lib_ops {
struct link_qual *qual, const u32 count);
void (*gain_calibration) (struct rt2x00_dev *rt2x00dev);
void (*vco_calibration) (struct rt2x00_dev *rt2x00dev);
+ void (*freq_calibration) (struct rt2x00_dev *rt2x00dev);
/*
* Data queue handlers.
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
index 2f1385baa7ae9..a6f516ebdb2db 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
@@ -22,6 +22,8 @@
#define LINK_TUNE_INTERVAL round_jiffies_relative(LINK_TUNE_SECONDS * HZ)
#define AGC_SECONDS 4
#define VCO_SECONDS 10
+#define FREQ_SECONDS 10
+#define FREQ_ADJUST_SECONDS 1
/*
* rt2x00_rate: Per rate device information
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00link.c b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
index fb23d409fba87..2dc4edc12834f 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
@@ -343,6 +343,7 @@ static void rt2x00link_tuner(struct work_struct *work)
struct rt2x00_dev *rt2x00dev =
container_of(work, struct rt2x00_dev, link.work.work);
struct link *link = &rt2x00dev->link;
+ unsigned int freq_interval;
/*
* When the radio is shutting down we should
@@ -362,6 +363,14 @@ static void rt2x00link_tuner(struct work_struct *work)
(link->count % (AGC_SECONDS / LINK_TUNE_SECONDS)) == 0)
rt2x00dev->ops->lib->gain_calibration(rt2x00dev);
+ freq_interval = link->freq_cal_adjusting ?
+ FREQ_ADJUST_SECONDS : FREQ_SECONDS;
+
+ /* VCO calibration should be done after frequency calibration */
+ if (rt2x00dev->ops->lib->freq_calibration &&
+ (link->count % (freq_interval / LINK_TUNE_SECONDS)) == 0)
+ rt2x00dev->ops->lib->freq_calibration(rt2x00dev);
+
if (rt2x00dev->ops->lib->vco_calibration &&
rt2x00_has_cap_vco_recalibration(rt2x00dev) &&
(link->count % (VCO_SECONDS / LINK_TUNE_SECONDS)) == 0)
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH wireless-next 3/8] wifi: rt2800: use the 3SS fallback table of RT3883 on RT3593
[not found] <20261029134752.1299130-1-hjf3108@gmail.com>
2026-10-29 13:47 ` [PATCH wireless-next 1/8] wifi: rt2x00: start the link tuner when an interface is added Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 2/8] wifi: rt2800: calibrate the crystal of RT3593 with the beacons Ho Jie Feng
@ 2026-10-29 13:47 ` Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 4/8] wifi: rt2800: report the MCS16 to MCS8 fallback of RT3593 in TX status Ho Jie Feng
` (4 subsequent siblings)
7 siblings, 0 replies; 8+ messages in thread
From: Ho Jie Feng @ 2026-10-29 13:47 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, Ho Jie Feng, linux-kernel
MCS 15 has worse reliability than MCS 16, since 16 and above
are 3 stream rates, and 15 is the top of the 2 stream rates.
Falling back from MCS 16 to MCS 15 does not make sense, so
make the fallback to MCS 8 like in RT3883
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index e7b6edf485099..9d88ee698b854 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -6303,7 +6303,7 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
reg = rt2x00_rt(rt2x00dev, RT5592) ? 0x00000082 : 0x00000002;
rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, reg);
- if (rt2x00_rt(rt2x00dev, RT3883)) {
+ if (rt2x00_rt(rt2x00dev, RT3593) || rt2x00_rt(rt2x00dev, RT3883)) {
rt2800_register_write(rt2x00dev, TX_FBK_CFG_3S_0, 0x12111008);
rt2800_register_write(rt2x00dev, TX_FBK_CFG_3S_1, 0x16151413);
}
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH wireless-next 4/8] wifi: rt2800: report the MCS16 to MCS8 fallback of RT3593 in TX status
[not found] <20261029134752.1299130-1-hjf3108@gmail.com>
` (2 preceding siblings ...)
2026-10-29 13:47 ` [PATCH wireless-next 3/8] wifi: rt2800: use the 3SS fallback table of RT3883 on RT3593 Ho Jie Feng
@ 2026-10-29 13:47 ` Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 5/8] wifi: rt2800: don't report an A-MPDU length to minstrel_ht Ho Jie Feng
` (3 subsequent siblings)
7 siblings, 0 replies; 8+ messages in thread
From: Ho Jie Feng @ 2026-10-29 13:47 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, Ho Jie Feng, linux-kernel
The RT3593 retry chain is now MCS 16 to MCS 8, skipping 7 MCS
rates in between. Reflect that in the retry calculation code.
Test results:
before after
TX 27.0 29.4
RX 70.0 70.0
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 8 ++++++++
drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 13 +++++++++++--
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 9d88ee698b854..de234bcc7a065 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -1116,6 +1116,14 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi,
*/
__set_bit(TXDONE_SUCCESS, &txdesc.flags);
txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
+
+ /*
+ * On RT3593 MCS16 falls back to MCS8, so a frame that was
+ * sent below MCS16 after a three stream rate skipped MCS9
+ * to MCS15.
+ */
+ if (rt2x00_rt(rt2x00dev, RT3593) && mcs >= 16 && real_mcs < 16)
+ txdesc.retry -= 7;
} else {
/*
* Transmission failed. The number of retries is
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 4d94b7062f440..823785088a308 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -329,10 +329,10 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev,
* lowest rate was used.
*/
for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) {
- tx_info->status.rates[i].idx = rate_idx - i;
+ tx_info->status.rates[i].idx = rate_idx;
tx_info->status.rates[i].flags = rate_flags;
- if (rate_idx - i == 0) {
+ if (rate_idx == 0) {
/*
* The lowest rate (index 0) was used until the
* number of max retries was reached.
@@ -340,8 +340,17 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev,
tx_info->status.rates[i].count = retry_rates - i;
i++;
break;
+ } else if (rate_idx == 16) {
+ /*
+ * TX_FBK_CFG_3S_0 makes MCS16 fall back to MCS8 on
+ * RT3593, the decrement below shifts it back to 8.
+ */
+ if (rt2x00_rt(rt2x00dev, RT3593) &&
+ (rate_flags & IEEE80211_TX_RC_MCS))
+ rate_idx = 9;
}
tx_info->status.rates[i].count = 1;
+ rate_idx--;
}
if (i < (IEEE80211_TX_MAX_RATES - 1))
tx_info->status.rates[i].idx = -1; /* terminate */
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH wireless-next 5/8] wifi: rt2800: don't report an A-MPDU length to minstrel_ht
[not found] <20261029134752.1299130-1-hjf3108@gmail.com>
` (3 preceding siblings ...)
2026-10-29 13:47 ` [PATCH wireless-next 4/8] wifi: rt2800: report the MCS16 to MCS8 fallback of RT3593 in TX status Ho Jie Feng
@ 2026-10-29 13:47 ` Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 6/8] wifi: rt2800: set the mac80211 fast-xmit and aggregation flags Ho Jie Feng
` (2 subsequent siblings)
7 siblings, 0 replies; 8+ messages in thread
From: Ho Jie Feng @ 2026-10-29 13:47 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, Ho Jie Feng, linux-kernel
Set IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN, which was added for drivers
that can't report the proper AMPDU length and record this workaround
below the TODO comment.
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 1 +
drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 2 ++
2 files changed, 3 insertions(+)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index de234bcc7a065..032cd6ac345f0 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -11860,6 +11860,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK);
ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM);
ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS);
+ ieee80211_hw_set(rt2x00dev->hw, TX_STATUS_NO_AMPDU_LEN);
/*
* Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 823785088a308..66094da07325f 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -373,6 +373,8 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev,
* by the hw we could provide the real ampdu_len to mac80211
* which would allow the rc algorithm to better decide on
* which rates are suitable.
+ *
+ * Currently, we work around this by setting TX_STATUS_NO_AMPDU_LEN.
*/
if (test_bit(TXDONE_AMPDU, &txdesc->flags) ||
tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH wireless-next 6/8] wifi: rt2800: set the mac80211 fast-xmit and aggregation flags
[not found] <20261029134752.1299130-1-hjf3108@gmail.com>
` (4 preceding siblings ...)
2026-10-29 13:47 ` [PATCH wireless-next 5/8] wifi: rt2800: don't report an A-MPDU length to minstrel_ht Ho Jie Feng
@ 2026-10-29 13:47 ` Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 7/8] wifi: rt2800usb: enable RX bulk aggregation Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 8/8] wifi: rt2x00usb: use a threaded NAPI with GRO for RX Ho Jie Feng
7 siblings, 0 replies; 8+ messages in thread
From: Ho Jie Feng @ 2026-10-29 13:47 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, Ho Jie Feng, linux-kernel
Enable AMSDU TX/RX support for this driver, which can increase
throughput.
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 032cd6ac345f0..8b4458a74d33d 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -11861,6 +11861,9 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM);
ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS);
ieee80211_hw_set(rt2x00dev->hw, TX_STATUS_NO_AMPDU_LEN);
+ ieee80211_hw_set(rt2x00dev->hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(rt2x00dev->hw, TX_AMSDU);
+ ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_AMSDU_IN_AMPDU);
/*
* Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH wireless-next 7/8] wifi: rt2800usb: enable RX bulk aggregation
[not found] <20261029134752.1299130-1-hjf3108@gmail.com>
` (5 preceding siblings ...)
2026-10-29 13:47 ` [PATCH wireless-next 6/8] wifi: rt2800: set the mac80211 fast-xmit and aggregation flags Ho Jie Feng
@ 2026-10-29 13:47 ` Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 8/8] wifi: rt2x00usb: use a threaded NAPI with GRO for RX Ho Jie Feng
7 siblings, 0 replies; 8+ messages in thread
From: Ho Jie Feng @ 2026-10-29 13:47 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, Ho Jie Feng, linux-kernel
Enable RX bulk aggregation on high speed USB, to receive more
frames per URB.
RX buffers are now 24KB, so reduce it to 16 buffers instead of 128,
so that they take 512 KB in total instead of 4 MB.
Test results:
before after
TX 30.5 30.5
RX 67.8 80.9
This code references the mt7601u driver.
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
.../net/wireless/ralink/rt2x00/rt2800usb.c | 32 +++++--
.../net/wireless/ralink/rt2x00/rt2800usb.h | 6 ++
drivers/net/wireless/ralink/rt2x00/rt2x00.h | 3 +-
.../net/wireless/ralink/rt2x00/rt2x00dev.c | 84 +++++++++++++------
.../net/wireless/ralink/rt2x00/rt2x00mmio.c | 2 +-
.../net/wireless/ralink/rt2x00/rt2x00usb.c | 5 +-
6 files changed, 100 insertions(+), 32 deletions(-)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index 387c07e2251b7..2e4277b1e5d1b 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
@@ -301,15 +301,15 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
return -EIO;
rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0);
- rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, 0);
+ rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN,
+ rt2x00dev->rx->usb_maxpacket == 512);
rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128);
/*
* Total room for RX frames in kilobytes, PBF might still exceed
* this limit so reduce the number to prevent errors.
*/
rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_LIMIT,
- ((rt2x00dev->rx->limit * DATA_FRAME_SIZE)
- / 1024) - 3);
+ (rt2x00dev->rx->data_size / 1024) - 4);
rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_EN, 1);
rt2x00_set_field32(®, USB_DMA_CFG_TX_BULK_EN, 1);
rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, reg);
@@ -478,6 +478,27 @@ static void rt2800usb_work_txdone(struct work_struct *work)
/*
* RX control handlers
*/
+static unsigned int rt2800usb_get_rx_seg_len(void *data, unsigned int len)
+{
+ unsigned int min_seg_len = RXINFO_DESC_SIZE + RXWI_DESC_SIZE_4WORDS +
+ RXD_DESC_SIZE;
+ unsigned int rx_pkt_len;
+ u32 word;
+
+ if (len < min_seg_len)
+ return 0;
+
+ word = rt2x00_desc_read(data, 0);
+ rx_pkt_len = rt2x00_get_field32(word, RXINFO_W0_USB_DMA_RX_PKT_LEN);
+
+ if (!rx_pkt_len ||
+ RXINFO_DESC_SIZE + rx_pkt_len + RXD_DESC_SIZE > len ||
+ rx_pkt_len & 0x3)
+ return 0;
+
+ return RXINFO_DESC_SIZE + rx_pkt_len + RXD_DESC_SIZE;
+}
+
static void rt2800usb_fill_rxdone(struct queue_entry *entry,
struct rxdone_entry_desc *rxdesc)
{
@@ -706,6 +727,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.clear_beacon = rt2800_clear_beacon,
.get_tx_data_len = rt2800usb_get_tx_data_len,
.fill_rxdone = rt2800usb_fill_rxdone,
+ .get_rx_seg_len = rt2800usb_get_rx_seg_len,
.config_shared_key = rt2800_config_shared_key,
.config_pairwise_key = rt2800_config_pairwise_key,
.config_filter = rt2800_config_filter,
@@ -725,8 +747,8 @@ static void rt2800usb_queue_init(struct data_queue *queue)
switch (queue->qid) {
case QID_RX:
- queue->limit = 128;
- queue->data_size = AGGREGATION_SIZE;
+ queue->limit = 16;
+ queue->data_size = MAX_RXBULK_SIZE;
queue->desc_size = RXINFO_DESC_SIZE;
queue->winfo_size = rxwi_size;
queue->priv_size = sizeof(struct queue_entry_priv_usb);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.h b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
index 9e180e9e41d92..2fae79072fd58 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
@@ -29,6 +29,12 @@
*/
#define TXINFO_DESC_SIZE (1 * sizeof(__le32))
#define RXINFO_DESC_SIZE (1 * sizeof(__le32))
+#define RXD_DESC_SIZE (1 * sizeof(__le32))
+
+/*
+ * RX buffer size
+ */
+#define MAX_RXBULK_SIZE 24576
/*
* TX Info structure
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index a80508db32d9c..5bc077fce9cbf 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -593,6 +593,7 @@ struct rt2x00lib_ops {
*/
void (*fill_rxdone) (struct queue_entry *entry,
struct rxdone_entry_desc *rxdesc);
+ unsigned int (*get_rx_seg_len) (void *data, unsigned int len);
/*
* Configuration handlers.
@@ -1448,7 +1449,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
void rt2x00lib_txdone_nomatch(struct queue_entry *entry,
struct txdone_entry_desc *txdesc);
void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status);
-void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp);
+void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp);
/*
* mac80211 handlers.
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 66094da07325f..351e7da178279 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -745,35 +745,14 @@ static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
return 0;
}
-void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
+static void rt2x00lib_rxdone_skb(struct queue_entry *entry)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct rxdone_entry_desc rxdesc;
- struct sk_buff *skb;
struct ieee80211_rx_status *rx_status;
unsigned int header_length;
int rate_idx;
- if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) ||
- !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
- goto submit_entry;
-
- if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
- goto submit_entry;
-
- /*
- * Allocate a new sk_buffer. If no new buffer available, drop the
- * received frame and reuse the existing buffer.
- */
- skb = rt2x00queue_alloc_rxskb(entry, gfp);
- if (!skb)
- goto submit_entry;
-
- /*
- * Unmap the skb.
- */
- rt2x00queue_unmap_skb(entry);
-
/*
* Extract the RXD details.
*/
@@ -789,7 +768,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
rt2x00_err(rt2x00dev, "Wrong frame size %d max %d\n",
rxdesc.size, entry->queue->data_size);
dev_kfree_skb(entry->skb);
- goto renew_skb;
+ return;
}
/*
@@ -868,8 +847,65 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
rx_status->antenna = rt2x00dev->link.ant.active.rx;
ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
+}
+
+void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp)
+{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct skb_frame_desc *skbdesc, *frame_skbdesc;
+ struct sk_buff *skb;
+ unsigned int seg_len;
+ u8 *data;
+
+ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) ||
+ !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ goto submit_entry;
+
+ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
+ goto submit_entry;
+
+ if (rt2x00dev->ops->lib->get_rx_seg_len) {
+ skb = entry->skb;
+ skbdesc = get_skb_frame_desc(skb);
+ data = skb->data;
+
+ while ((seg_len = rt2x00dev->ops->lib->get_rx_seg_len(data, len))) {
+ entry->skb = __dev_alloc_skb(seg_len, gfp);
+ if (!entry->skb)
+ break;
+
+ skb_put_data(entry->skb, data, seg_len);
+
+ frame_skbdesc = get_skb_frame_desc(entry->skb);
+ memset(frame_skbdesc, 0, sizeof(*frame_skbdesc));
+ frame_skbdesc->desc = skbdesc->desc;
+ frame_skbdesc->desc_len = skbdesc->desc_len;
+
+ rt2x00lib_rxdone_skb(entry);
+
+ data += seg_len;
+ len -= seg_len;
+ }
+
+ entry->skb = skb;
+ goto submit_entry;
+ }
+
+ /*
+ * Allocate a new sk_buffer. If no new buffer available, drop the
+ * received frame and reuse the existing buffer.
+ */
+ skb = rt2x00queue_alloc_rxskb(entry, gfp);
+ if (!skb)
+ goto submit_entry;
+
+ /*
+ * Unmap the skb.
+ */
+ rt2x00queue_unmap_skb(entry);
+
+ rt2x00lib_rxdone_skb(entry);
-renew_skb:
/*
* Replace the skb with the freshly allocated one.
*/
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
index 93f76acf3dc7d..a1d8b8d13daca 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
@@ -78,7 +78,7 @@ bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev)
/*
* Send the frame to rt2x00lib for further processing.
*/
- rt2x00lib_rxdone(entry, GFP_ATOMIC);
+ rt2x00lib_rxdone(entry, entry->skb->len, GFP_ATOMIC);
}
return !max_rx;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
index c9fbc3710fd8d..4519a20a7f4bc 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
@@ -345,6 +345,7 @@ static void rt2x00usb_work_rxdone(struct work_struct *work)
{
struct rt2x00_dev *rt2x00dev =
container_of(work, struct rt2x00_dev, rxdone_work);
+ struct queue_entry_priv_usb *entry_priv;
struct queue_entry *entry;
struct skb_frame_desc *skbdesc;
u8 rxd[32];
@@ -365,7 +366,9 @@ static void rt2x00usb_work_rxdone(struct work_struct *work)
/*
* Send the frame to rt2x00lib for further processing.
*/
- rt2x00lib_rxdone(entry, GFP_KERNEL);
+ entry_priv = entry->priv_data;
+ rt2x00lib_rxdone(entry, entry_priv->urb->actual_length,
+ GFP_KERNEL);
}
}
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH wireless-next 8/8] wifi: rt2x00usb: use a threaded NAPI with GRO for RX
[not found] <20261029134752.1299130-1-hjf3108@gmail.com>
` (6 preceding siblings ...)
2026-10-29 13:47 ` [PATCH wireless-next 7/8] wifi: rt2800usb: enable RX bulk aggregation Ho Jie Feng
@ 2026-10-29 13:47 ` Ho Jie Feng
7 siblings, 0 replies; 8+ messages in thread
From: Ho Jie Feng @ 2026-10-29 13:47 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linux-wireless, Ho Jie Feng, linux-kernel
GRO makes sense now that RX aggregation is enabled, since multiple
frames arrive at once.
This code is referenced from commit 5323d3e50c20
("wifi: mt76: mt76u: use a threaded NAPI for the RX path")
Test results:
before after
TX 30.5 30.5
RX 80.9 161.7
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
drivers/net/wireless/ralink/rt2x00/rt2x00.h | 7 ++-
.../net/wireless/ralink/rt2x00/rt2x00dev.c | 6 ++-
.../net/wireless/ralink/rt2x00/rt2x00usb.c | 49 ++++++++++++++++---
3 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index 5bc077fce9cbf..a120adb18f4b8 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -955,9 +955,14 @@ struct rt2x00_dev {
/**
* Scheduled work for TX/RX done handling (USB devices)
*/
- struct work_struct rxdone_work;
struct work_struct txdone_work;
+ /*
+ * NAPI context for RX done handling (USB devices).
+ */
+ struct net_device *napi_dev;
+ struct napi_struct napi;
+
/*
* Powersaving work
*/
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 351e7da178279..4527fdf905b15 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -846,7 +846,11 @@ static void rt2x00lib_rxdone_skb(struct queue_entry *entry)
rx_status->bw = rxdesc.bw;
rx_status->antenna = rt2x00dev->link.ant.active.rx;
- ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
+ if (rt2x00dev->napi_dev)
+ ieee80211_rx_napi(rt2x00dev->hw, NULL, entry->skb,
+ &rt2x00dev->napi);
+ else
+ ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
}
void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
index 4519a20a7f4bc..d11244a2c4f2e 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
@@ -341,10 +341,10 @@ static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void *data)
/*
* RX data handlers.
*/
-static void rt2x00usb_work_rxdone(struct work_struct *work)
+static int rt2x00usb_poll_rxdone(struct napi_struct *napi, int budget)
{
struct rt2x00_dev *rt2x00dev =
- container_of(work, struct rt2x00_dev, rxdone_work);
+ container_of(napi, struct rt2x00_dev, napi);
struct queue_entry_priv_usb *entry_priv;
struct queue_entry *entry;
struct skb_frame_desc *skbdesc;
@@ -368,8 +368,12 @@ static void rt2x00usb_work_rxdone(struct work_struct *work)
*/
entry_priv = entry->priv_data;
rt2x00lib_rxdone(entry, entry_priv->urb->actual_length,
- GFP_KERNEL);
+ GFP_ATOMIC);
}
+
+ napi_complete(napi);
+
+ return 0;
}
static void rt2x00usb_interrupt_rxdone(struct urb *urb)
@@ -396,7 +400,7 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
/*
* Schedule the delayed work for processing RX data
*/
- queue_work(rt2x00dev->workqueue, &rt2x00dev->rxdone_work);
+ napi_schedule(&rt2x00dev->napi);
}
static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void *data)
@@ -496,7 +500,7 @@ void rt2x00usb_flush_queue(struct data_queue *queue, bool drop)
completion = &queue->rt2x00dev->txdone_work;
break;
case QID_RX:
- completion = &queue->rt2x00dev->rxdone_work;
+ completion = NULL;
break;
default:
return;
@@ -515,7 +519,13 @@ void rt2x00usb_flush_queue(struct data_queue *queue, bool drop)
* Schedule the completion handler manually, when this
* worker function runs, it should cleanup the queue.
*/
- queue_work(queue->rt2x00dev->workqueue, completion);
+ if (completion) {
+ queue_work(queue->rt2x00dev->workqueue, completion);
+ } else {
+ local_bh_disable();
+ napi_schedule(&queue->rt2x00dev->napi);
+ local_bh_enable();
+ }
/*
* Wait for a little while to give the driver
@@ -750,7 +760,7 @@ void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev)
usb_kill_anchored_urbs(rt2x00dev->anchor);
hrtimer_cancel(&rt2x00dev->txstatus_timer);
- cancel_work_sync(&rt2x00dev->rxdone_work);
+ napi_synchronize(&rt2x00dev->napi);
cancel_work_sync(&rt2x00dev->txdone_work);
queue_for_each(rt2x00dev, queue)
@@ -758,6 +768,14 @@ void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev)
}
EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize);
+static void rt2x00usb_free_napi(struct rt2x00_dev *rt2x00dev)
+{
+ napi_disable(&rt2x00dev->napi);
+ netif_napi_del(&rt2x00dev->napi);
+ free_netdev(rt2x00dev->napi_dev);
+ rt2x00dev->napi_dev = NULL;
+}
+
int rt2x00usb_probe(struct usb_interface *usb_intf,
const struct rt2x00_ops *ops)
{
@@ -801,9 +819,21 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB);
- INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone);
INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone);
+ rt2x00dev->napi_dev = alloc_netdev_dummy(0);
+ if (!rt2x00dev->napi_dev) {
+ retval = -ENOMEM;
+ goto exit_free_hw;
+ }
+
+ strscpy(rt2x00dev->napi_dev->name, wiphy_name(hw->wiphy),
+ sizeof(rt2x00dev->napi_dev->name));
+ netif_napi_add(rt2x00dev->napi_dev, &rt2x00dev->napi,
+ rt2x00usb_poll_rxdone);
+ netif_threaded_enable(rt2x00dev->napi_dev);
+ napi_enable(&rt2x00dev->napi);
+
init_usb_anchor(rt2x00dev->anchor);
retval = rt2x00lib_probe_dev(rt2x00dev);
@@ -814,6 +844,8 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
exit_free_anchor:
usb_kill_anchored_urbs(rt2x00dev->anchor);
+ rt2x00usb_free_napi(rt2x00dev);
+exit_free_hw:
ieee80211_free_hw(hw);
usb_set_intfdata(usb_intf, NULL);
@@ -830,6 +862,7 @@ void rt2x00usb_disconnect(struct usb_interface *usb_intf)
* Free all allocated data.
*/
rt2x00lib_remove_dev(rt2x00dev);
+ rt2x00usb_free_napi(rt2x00dev);
ieee80211_free_hw(hw);
usb_set_intfdata(usb_intf, NULL);
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread