wifi: rtl8xxxu: Detect the maximum supported channel width

Some devices malfunction when connected to a network with 40 MHz channel
width, because they don't support that.

RTL8188FU, RTL8192FU, and RTL8710BU (RTL8188GU) have a way to signal
this (and some other capabilities) to the driver. Get this information
from the hardware and advertise 40 MHz support only when the hardware
can handle it. We assume the other chips can always handle it.

RTL8710BU needs a different way to retrieve this information, which will
be implemented some other time.

Fixes: dbf9b7bb0e ("wifi: rtl8xxxu: Enable 40 MHz width by default")
Cc: stable@vger.kernel.org
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221394
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
Reviewed-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/c57de68e-5d57-4c26-898f-8a284bb25381@gmail.com
This commit is contained in:
Bitterblue Smith 2026-04-29 15:02:48 +03:00 committed by Ping-Ke Shih
parent 83d38df692
commit ef771eabc7
11 changed files with 76 additions and 5 deletions

View File

@ -1866,6 +1866,7 @@ struct rtl8xxxu_fileops rtl8188eu_fops = {
.has_tx_report = 1,
.init_reg_pkt_life_time = 1,
.gen2_thermal_meter = 1,
.hw_feature_report = 0,
.max_sec_cam_num = 32,
.adda_1t_init = 0x0b1b25a0,
.adda_1t_path_on = 0x0bdb25a0,

View File

@ -1745,6 +1745,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = {
.init_reg_rxfltmap = 1,
.init_reg_pkt_life_time = 1,
.init_reg_hmtfr = 1,
.hw_feature_report = 1,
.ampdu_max_time = 0x70,
.ustime_tsf_edca = 0x28,
.max_aggr_num = 0x0c14,

View File

@ -723,6 +723,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = {
.tx_desc_size = sizeof(struct rtl8xxxu_txdesc32),
.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16),
.supports_ap = 1,
.hw_feature_report = 0,
.max_macid_num = 32,
.max_sec_cam_num = 32,
.adda_1t_init = 0x0b1b25a0,

View File

@ -1751,6 +1751,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = {
.has_s0s1 = 0,
.gen2_thermal_meter = 1,
.needs_full_init = 1,
.hw_feature_report = 0,
.supports_ap = 1,
.max_macid_num = 128,
.max_sec_cam_num = 64,

View File

@ -2074,6 +2074,7 @@ struct rtl8xxxu_fileops rtl8192fu_fops = {
.init_reg_rxfltmap = 1,
.init_reg_pkt_life_time = 1,
.init_reg_hmtfr = 1,
.hw_feature_report = 1,
.ampdu_max_time = 0x5e,
.ustime_tsf_edca = 0x50,
.max_aggr_num = 0x1f1f,

View File

@ -1852,6 +1852,7 @@ struct rtl8xxxu_fileops rtl8710bu_fops = {
.init_reg_rxfltmap = 1,
.init_reg_pkt_life_time = 1,
.init_reg_hmtfr = 1,
.hw_feature_report = 0, /* TODO, it's different */
.ampdu_max_time = 0x5e,
/*
* The RTL8710BU vendor driver uses 0x50 here and it works fine,

View File

@ -632,6 +632,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = {
.rx_agg_buf_size = 16000,
.tx_desc_size = sizeof(struct rtl8xxxu_txdesc32),
.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16),
.hw_feature_report = 0,
.max_sec_cam_num = 32,
.adda_1t_init = 0x0b1b25a0,
.adda_1t_path_on = 0x0bdb25a0,

View File

@ -1746,6 +1746,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = {
.gen2_thermal_meter = 1,
.needs_full_init = 1,
.init_reg_hmtfr = 1,
.hw_feature_report = 0,
.ampdu_max_time = 0x5e,
.ustime_tsf_edca = 0x50,
.max_aggr_num = 0x0c14,

View File

@ -14,6 +14,7 @@
*/
#include <linux/firmware.h>
#include <linux/iopoll.h>
#include "regs.h"
#include "rtl8xxxu.h"
@ -3915,6 +3916,46 @@ static inline u8 rtl8xxxu_get_macid(struct rtl8xxxu_priv *priv,
return sta_info->macid;
}
static void rtl8xxxu_request_hw_feature(struct rtl8xxxu_priv *priv)
{
if (!priv->fops->hw_feature_report)
return;
rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, C2H_HW_FEATURE_DUMP);
}
static int rtl8xxxu_dump_hw_feature(struct rtl8xxxu_priv *priv)
{
static const u8 bw_map[8] = { 0, 0, 160, 5, 10, 20, 40, 80 };
struct rtl8xxxu_hw_feature *hw_feature = &priv->hw_feature;
u8 feature[13];
int i, ret;
u8 id, bw;
if (!priv->fops->hw_feature_report) {
hw_feature->max_bw = 40;
return 0;
}
ret = read_poll_timeout(rtl8xxxu_read8, id,
id == C2H_HW_FEATURE_REPORT,
10000, 800000, false,
priv, REG_C2HEVT_MSG_NORMAL);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(feature); i++)
feature[i] = rtl8xxxu_read8(priv, REG_C2HEVT_MSG_NORMAL + 2 + i);
rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, 0);
bw = u8_get_bits(feature[6], GENMASK(2, 0));
hw_feature->max_bw = bw_map[bw];
return 0;
}
static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
{
struct rtl8xxxu_priv *priv = hw->priv;
@ -3961,6 +4002,8 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
*/
rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary);
rtl8xxxu_request_hw_feature(priv);
for (int retry = 5; retry >= 0 ; retry--) {
ret = rtl8xxxu_download_firmware(priv);
dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret);
@ -3976,6 +4019,12 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
if (ret)
goto exit;
ret = rtl8xxxu_dump_hw_feature(priv);
if (ret) {
dev_err(dev, "failed to dump hw feature\n");
goto exit;
}
if (fops->phy_init_antenna_selection)
fops->phy_init_antenna_selection(priv);
@ -7844,15 +7893,20 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
sband->ht_cap.ht_supported = true;
sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_SUP_WIDTH_20_40;
sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20;
if (priv->hw_feature.max_bw >= 40) {
sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
} else {
dev_info(&udev->dev, "hardware doesn't support HT40\n");
}
memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs));
sband->ht_cap.mcs.rx_mask[0] = 0xff;
sband->ht_cap.mcs.rx_mask[4] = 0x01;
if (priv->rf_paths > 1) {
if (priv->rf_paths > 1)
sband->ht_cap.mcs.rx_mask[1] = 0xff;
sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
}
sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
hw->wiphy->bands[NL80211_BAND_2GHZ] = sband;

View File

@ -447,6 +447,8 @@
/* 8188EU */
#define REG_32K_CTRL 0x0194
#define REG_C2HEVT_MSG_NORMAL 0x01a0
#define C2H_HW_FEATURE_REPORT 0x19
#define C2H_HW_FEATURE_DUMP 0xfd
/* 8192EU/8723BU/8812 */
#define REG_C2HEVT_CMD_ID_8723B 0x01ae
#define REG_C2HEVT_CLEAR 0x01af

View File

@ -1789,11 +1789,17 @@ struct rtl8xxxu_cfo_tracking {
#define RTL8XXXU_BC_MC_MACID1 1
#define RTL8XXXU_MAX_SEC_CAM_NUM 64
struct rtl8xxxu_hw_feature {
u8 max_bw;
};
struct rtl8xxxu_priv {
struct ieee80211_hw *hw;
struct usb_device *udev;
struct rtl8xxxu_fileops *fops;
struct rtl8xxxu_hw_feature hw_feature;
spinlock_t tx_urb_lock;
struct list_head tx_urb_free_list;
int tx_urb_free_count;
@ -2009,6 +2015,7 @@ struct rtl8xxxu_fileops {
u8 init_reg_pkt_life_time:1;
u8 init_reg_hmtfr:1;
u8 supports_concurrent:1;
u8 hw_feature_report:1;
u8 ampdu_max_time;
u8 ustime_tsf_edca;
u16 max_aggr_num;