summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/realtek/rtw88/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/realtek/rtw88/main.c')
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.c218
1 files changed, 137 insertions, 81 deletions
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 6d22628129d0..c4de5d114eda 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -19,6 +19,7 @@
#include "bf.h"
#include "sar.h"
#include "sdio.h"
+#include "led.h"
bool rtw_disable_lps_deep_mode;
EXPORT_SYMBOL(rtw_disable_lps_deep_mode);
@@ -135,7 +136,7 @@ u16 rtw_desc_to_bitrate(u8 desc_rate)
return rate.bitrate;
}
-static struct ieee80211_supported_band rtw_band_2ghz = {
+static const struct ieee80211_supported_band rtw_band_2ghz = {
.band = NL80211_BAND_2GHZ,
.channels = rtw_channeltable_2g,
@@ -148,7 +149,7 @@ static struct ieee80211_supported_band rtw_band_2ghz = {
.vht_cap = {0},
};
-static struct ieee80211_supported_band rtw_band_5ghz = {
+static const struct ieee80211_supported_band rtw_band_5ghz = {
.band = NL80211_BAND_5GHZ,
.channels = rtw_channeltable_5g,
@@ -202,6 +203,21 @@ static void rtw_vif_watch_dog_iter(void *data, struct ieee80211_vif *vif)
rtwvif->stats.rx_cnt = 0;
}
+static void rtw_sw_beacon_loss_check(struct rtw_dev *rtwdev,
+ struct rtw_vif *rtwvif, int received_beacons)
+{
+ int watchdog_delay = 2000000 / 1024; /* TU */
+ int beacon_int, expected_beacons;
+
+ if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_BCN_FILTER) || !rtwvif)
+ return;
+
+ beacon_int = rtwvif_to_vif(rtwvif)->bss_conf.beacon_int;
+ expected_beacons = DIV_ROUND_UP(watchdog_delay, beacon_int);
+
+ rtwdev->beacon_loss = received_beacons < expected_beacons / 2;
+}
+
/* process TX/RX statistics periodically for hardware,
* the information helps hardware to enhance performance
*/
@@ -212,6 +228,8 @@ static void rtw_watch_dog_work(struct work_struct *work)
struct rtw_traffic_stats *stats = &rtwdev->stats;
struct rtw_watch_dog_iter_data data = {};
bool busy_traffic = test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags);
+ int received_beacons = rtwdev->dm_info.cur_pkt_count.num_bcn_pkt;
+ u32 tx_unicast_mbps, rx_unicast_mbps;
bool ps_active;
mutex_lock(&rtwdev->mutex);
@@ -227,9 +245,6 @@ static void rtw_watch_dog_work(struct work_struct *work)
else
clear_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags);
- rtw_coex_wl_status_check(rtwdev);
- rtw_coex_query_bt_hid_list(rtwdev);
-
if (busy_traffic != test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags))
rtw_coex_wl_status_change_notify(rtwdev, 0);
@@ -239,10 +254,11 @@ static void rtw_watch_dog_work(struct work_struct *work)
else
ps_active = false;
- ewma_tp_add(&stats->tx_ewma_tp,
- (u32)(stats->tx_unicast >> RTW_TP_SHIFT));
- ewma_tp_add(&stats->rx_ewma_tp,
- (u32)(stats->rx_unicast >> RTW_TP_SHIFT));
+ tx_unicast_mbps = stats->tx_unicast >> RTW_TP_SHIFT;
+ rx_unicast_mbps = stats->rx_unicast >> RTW_TP_SHIFT;
+
+ ewma_tp_add(&stats->tx_ewma_tp, tx_unicast_mbps);
+ ewma_tp_add(&stats->rx_ewma_tp, rx_unicast_mbps);
stats->tx_throughput = ewma_tp_read(&stats->tx_ewma_tp);
stats->rx_throughput = ewma_tp_read(&stats->rx_ewma_tp);
@@ -257,15 +273,23 @@ static void rtw_watch_dog_work(struct work_struct *work)
/* make sure BB/RF is working for dynamic mech */
rtw_leave_lps(rtwdev);
+ rtw_coex_wl_status_check(rtwdev);
+ rtw_coex_query_bt_hid_list(rtwdev);
+ rtw_coex_active_query_bt_info(rtwdev);
rtw_phy_dynamic_mechanism(rtwdev);
+ rtw_hci_dynamic_rx_agg(rtwdev,
+ tx_unicast_mbps >= 1 || rx_unicast_mbps >= 1);
+
data.rtwdev = rtwdev;
/* rtw_iterate_vifs internally uses an atomic iterator which is needed
* to avoid taking local->iflist_mtx mutex
*/
rtw_iterate_vifs(rtwdev, rtw_vif_watch_dog_iter, &data);
+ rtw_sw_beacon_loss_check(rtwdev, data.rtwvif, received_beacons);
+
/* fw supports only one station associated to enter lps, if there are
* more than two stations associated to the AP, then we can not enter
* lps, because fw does not handle the overlapped beacon interval
@@ -307,17 +331,6 @@ static void rtw_ips_work(struct work_struct *work)
mutex_unlock(&rtwdev->mutex);
}
-static u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
-{
- unsigned long mac_id;
-
- mac_id = find_first_zero_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
- if (mac_id < RTW_MAX_MAC_ID_NUM)
- set_bit(mac_id, rtwdev->mac_id_map);
-
- return mac_id;
-}
-
static void rtw_sta_rc_work(struct work_struct *work)
{
struct rtw_sta_info *si = container_of(work, struct rtw_sta_info,
@@ -336,12 +349,14 @@ int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
int i;
- si->mac_id = rtw_acquire_macid(rtwdev);
- if (si->mac_id >= RTW_MAX_MAC_ID_NUM)
- return -ENOSPC;
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ si->mac_id = rtwvif->mac_id;
+ } else {
+ si->mac_id = rtw_acquire_macid(rtwdev);
+ if (si->mac_id >= RTW_MAX_MAC_ID_NUM)
+ return -ENOSPC;
+ }
- if (vif->type == NL80211_IFTYPE_STATION && vif->cfg.assoc == 0)
- rtwvif->mac_id = si->mac_id;
si->rtwdev = rtwdev;
si->sta = sta;
si->vif = vif;
@@ -366,11 +381,13 @@ void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
bool fw_exist)
{
struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+ struct ieee80211_vif *vif = si->vif;
int i;
cancel_work_sync(&si->rc_work);
- rtw_release_macid(rtwdev, si->mac_id);
+ if (vif->type != NL80211_IFTYPE_STATION)
+ rtw_release_macid(rtwdev, si->mac_id);
if (fw_exist)
rtw_fw_media_status_report(rtwdev, si->mac_id, false);
@@ -610,6 +627,8 @@ static void rtw_reset_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
rtw_bf_disassoc(rtwdev, vif, NULL);
rtw_vif_assoc_changed(rtwvif, NULL);
rtw_txq_cleanup(rtwdev, vif->txq);
+
+ rtw_release_macid(rtwdev, rtwvif->mac_id);
}
void rtw_fw_recovery(struct rtw_dev *rtwdev)
@@ -1199,7 +1218,6 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
u8 wireless_set;
u8 bw_mode;
u8 rate_id;
- u8 rf_type = RF_1T1R;
u8 stbc_en = 0;
u8 ldpc_en = 0;
u8 tx_num = 1;
@@ -1216,7 +1234,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)
ldpc_en = VHT_LDPC_EN;
} else if (sta->deflink.ht_cap.ht_supported) {
- ra_mask |= (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) |
+ ra_mask |= ((u64)sta->deflink.ht_cap.mcs.rx_mask[3] << 36) |
+ ((u64)sta->deflink.ht_cap.mcs.rx_mask[2] << 28) |
+ (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) |
(sta->deflink.ht_cap.mcs.rx_mask[0] << 12);
if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
stbc_en = HT_STBC_EN;
@@ -1226,6 +1246,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
if (efuse->hw_cap.nss == 1 || rtwdev->hal.txrx_1ss)
ra_mask &= RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS;
+ else if (efuse->hw_cap.nss == 2)
+ ra_mask &= RA_MASK_VHT_RATES_2SS | RA_MASK_HT_RATES_2SS |
+ RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS;
if (hal->current_band_type == RTW_BAND_5G) {
ra_mask |= (u64)sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 4;
@@ -1284,13 +1307,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
break;
}
- if (sta->deflink.vht_cap.vht_supported && ra_mask & 0xffc00000) {
- tx_num = 2;
- rf_type = RF_2T2R;
- } else if (sta->deflink.ht_cap.ht_supported && ra_mask & 0xfff00000) {
- tx_num = 2;
- rf_type = RF_2T2R;
- }
+ if (sta->deflink.vht_cap.vht_supported ||
+ sta->deflink.ht_cap.ht_supported)
+ tx_num = efuse->hw_cap.nss;
rate_id = get_rate_id(wireless_set, bw_mode, tx_num);
@@ -1301,7 +1320,6 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
si->bw_mode = bw_mode;
si->stbc_en = stbc_en;
si->ldpc_en = ldpc_en;
- si->rf_type = rf_type;
si->sgi_enable = is_support_sgi;
si->vht_enable = is_vht_enable;
si->ra_mask = ra_mask;
@@ -1310,25 +1328,27 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
rtw_fw_send_ra_info(rtwdev, si, reset_ra_mask);
}
-static int rtw_wait_firmware_completion(struct rtw_dev *rtwdev)
+int rtw_wait_firmware_completion(struct rtw_dev *rtwdev)
{
const struct rtw_chip_info *chip = rtwdev->chip;
struct rtw_fw_state *fw;
+ int ret = 0;
fw = &rtwdev->fw;
wait_for_completion(&fw->completion);
if (!fw->firmware)
- return -EINVAL;
+ ret = -EINVAL;
if (chip->wow_fw_name) {
fw = &rtwdev->wow_fw;
wait_for_completion(&fw->completion);
if (!fw->firmware)
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ return ret;
}
+EXPORT_SYMBOL(rtw_wait_firmware_completion);
static enum rtw_lps_deep_mode rtw_update_lps_deep_mode(struct rtw_dev *rtwdev,
struct rtw_fw_state *fw)
@@ -1350,7 +1370,7 @@ static enum rtw_lps_deep_mode rtw_update_lps_deep_mode(struct rtw_dev *rtwdev,
return LPS_DEEP_MODE_NONE;
}
-static int rtw_power_on(struct rtw_dev *rtwdev)
+int rtw_power_on(struct rtw_dev *rtwdev)
{
const struct rtw_chip_info *chip = rtwdev->chip;
struct rtw_fw_state *fw = &rtwdev->fw;
@@ -1413,6 +1433,7 @@ err_off:
err:
return ret;
}
+EXPORT_SYMBOL(rtw_power_on);
void rtw_core_fw_scan_notify(struct rtw_dev *rtwdev, bool start)
{
@@ -1485,7 +1506,7 @@ int rtw_core_start(struct rtw_dev *rtwdev)
{
int ret;
- ret = rtw_power_on(rtwdev);
+ ret = rtwdev->chip->ops->power_on(rtwdev);
if (ret)
return ret;
@@ -1505,12 +1526,13 @@ int rtw_core_start(struct rtw_dev *rtwdev)
return 0;
}
-static void rtw_power_off(struct rtw_dev *rtwdev)
+void rtw_power_off(struct rtw_dev *rtwdev)
{
rtw_hci_stop(rtwdev);
rtw_coex_power_off_setting(rtwdev);
rtw_mac_power_off(rtwdev);
}
+EXPORT_SYMBOL(rtw_power_off);
void rtw_core_stop(struct rtw_dev *rtwdev)
{
@@ -1535,7 +1557,7 @@ void rtw_core_stop(struct rtw_dev *rtwdev)
mutex_lock(&rtwdev->mutex);
- rtw_power_off(rtwdev);
+ rtwdev->chip->ops->power_off(rtwdev);
}
static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
@@ -1543,6 +1565,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
{
const struct rtw_chip_info *chip = rtwdev->chip;
struct rtw_efuse *efuse = &rtwdev->efuse;
+ int i;
ht_cap->ht_supported = true;
ht_cap->cap = 0;
@@ -1562,25 +1585,20 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_cap->ampdu_density = chip->ampdu_density;
ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
- if (efuse->hw_cap.nss > 1) {
- ht_cap->mcs.rx_mask[0] = 0xFF;
- ht_cap->mcs.rx_mask[1] = 0xFF;
- ht_cap->mcs.rx_mask[4] = 0x01;
- ht_cap->mcs.rx_highest = cpu_to_le16(300);
- } else {
- ht_cap->mcs.rx_mask[0] = 0xFF;
- ht_cap->mcs.rx_mask[1] = 0x00;
- ht_cap->mcs.rx_mask[4] = 0x01;
- ht_cap->mcs.rx_highest = cpu_to_le16(150);
- }
+
+ for (i = 0; i < efuse->hw_cap.nss; i++)
+ ht_cap->mcs.rx_mask[i] = 0xFF;
+ ht_cap->mcs.rx_mask[4] = 0x01;
+ ht_cap->mcs.rx_highest = cpu_to_le16(150 * efuse->hw_cap.nss);
}
static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
struct ieee80211_sta_vht_cap *vht_cap)
{
struct rtw_efuse *efuse = &rtwdev->efuse;
- u16 mcs_map;
+ u16 mcs_map = 0;
__le16 highest;
+ int i;
if (efuse->hw_cap.ptcl != EFUSE_HW_CAP_IGNORE &&
efuse->hw_cap.ptcl != EFUSE_HW_CAP_PTCL_VHT)
@@ -1603,21 +1621,15 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
if (rtw_chip_has_rx_ldpc(rtwdev))
vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
- mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
- if (efuse->hw_cap.nss > 1) {
- highest = cpu_to_le16(780);
- mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << 2;
- } else {
- highest = cpu_to_le16(390);
- mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << 2;
+ for (i = 0; i < 8; i++) {
+ if (i < efuse->hw_cap.nss)
+ mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2);
+ else
+ mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
}
+ highest = cpu_to_le16(390 * efuse->hw_cap.nss);
+
vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->vht_mcs.rx_highest = highest;
@@ -1917,6 +1929,9 @@ static int rtw_dump_hw_feature(struct rtw_dev *rtwdev)
u8 bw;
int i;
+ if (!rtwdev->chip->hw_feature_report)
+ return 0;
+
id = rtw_read8(rtwdev, REG_C2HEVT);
if (id != C2H_HW_FEATURE_REPORT) {
rtw_err(rtwdev, "failed to read hw feature report\n");
@@ -2006,7 +2021,7 @@ static int rtw_chip_efuse_info_setup(struct rtw_dev *rtwdev)
efuse->ext_pa_2g = efuse->pa_type_2g & BIT(4) ? 1 : 0;
efuse->ext_lna_2g = efuse->lna_type_2g & BIT(3) ? 1 : 0;
efuse->ext_pa_5g = efuse->pa_type_5g & BIT(0) ? 1 : 0;
- efuse->ext_lna_2g = efuse->lna_type_5g & BIT(3) ? 1 : 0;
+ efuse->ext_lna_5g = efuse->lna_type_5g & BIT(3) ? 1 : 0;
if (!is_valid_ether_addr(efuse->addr)) {
eth_random_addr(efuse->addr);
@@ -2032,8 +2047,6 @@ static int rtw_chip_board_info_setup(struct rtw_dev *rtwdev)
rtw_phy_setup_phy_cond(rtwdev, hal->pkg_type);
rtw_phy_init_tx_power(rtwdev);
- if (rfe_def->agc_btg_tbl)
- rtw_load_table(rtwdev, rfe_def->agc_btg_tbl);
rtw_load_table(rtwdev, rfe_def->phy_pg_tbl);
rtw_load_table(rtwdev, rfe_def->txpwr_lmt_tbl);
rtw_phy_tx_power_by_rate_config(hal);
@@ -2136,7 +2149,6 @@ int rtw_core_init(struct rtw_dev *rtwdev)
rtwdev->sec.total_cam_num = 32;
rtwdev->hal.current_channel = 1;
rtwdev->dm_info.fix_rate = U8_MAX;
- set_bit(RTW_BC_MC_MACID, rtwdev->mac_id_map);
rtw_stats_init(rtwdev);
@@ -2206,6 +2218,7 @@ EXPORT_SYMBOL(rtw_core_deinit);
int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
{
+ bool sta_mode_only = rtwdev->hci.type == RTW_HCI_TYPE_SDIO;
struct rtw_hal *hal = &rtwdev->hal;
int max_tx_headroom = 0;
int ret;
@@ -2229,15 +2242,18 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SUPPORTS_PS);
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
- ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ if (rtwdev->chip->amsdu_in_ampdu)
+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, HAS_RATE_CONTROL);
ieee80211_hw_set(hw, TX_AMSDU);
ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
- hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_MESH_POINT);
+ if (sta_mode_only)
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ else
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_ADHOC);
hw->wiphy->available_antennas_tx = hal->antenna_tx;
hw->wiphy->available_antennas_rx = hal->antenna_rx;
@@ -2248,7 +2264,7 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
hw->wiphy->max_scan_ssids = RTW_SCAN_MAX_SSIDS;
hw->wiphy->max_scan_ie_len = rtw_get_max_scan_ie_len(rtwdev);
- if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) {
+ if (!sta_mode_only && rtwdev->chip->id == RTW_CHIP_TYPE_8822C) {
hw->wiphy->iface_combinations = rtw_iface_combs;
hw->wiphy->n_iface_combinations = ARRAY_SIZE(rtw_iface_combs);
}
@@ -2272,16 +2288,18 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
return ret;
}
+ rtw_led_init(rtwdev);
+
ret = ieee80211_register_hw(hw);
if (ret) {
rtw_err(rtwdev, "failed to register hw\n");
- return ret;
+ goto led_deinit;
}
ret = rtw_regd_hint(rtwdev);
if (ret) {
rtw_err(rtwdev, "failed to hint regd\n");
- return ret;
+ goto led_deinit;
}
rtw_debugfs_init(rtwdev);
@@ -2290,6 +2308,10 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
rtwdev->bf_info.bfer_su_cnt = 0;
return 0;
+
+led_deinit:
+ rtw_led_deinit(rtwdev);
+ return ret;
}
EXPORT_SYMBOL(rtw_register_hw);
@@ -2299,6 +2321,8 @@ void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
ieee80211_unregister_hw(hw);
rtw_unset_supported_band(hw, chip);
+ rtw_debugfs_deinit(rtwdev);
+ rtw_led_deinit(rtwdev);
}
EXPORT_SYMBOL(rtw_unregister_hw);
@@ -2424,6 +2448,38 @@ void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable)
}
}
+void rtw_set_ampdu_factor(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ const struct rtw_chip_ops *ops = rtwdev->chip->ops;
+ struct ieee80211_sta *sta;
+ u8 factor = 0xff;
+
+ if (!ops->set_ampdu_factor)
+ return;
+
+ rcu_read_lock();
+
+ sta = ieee80211_find_sta(vif, bss_conf->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ rtw_warn(rtwdev, "%s: failed to find station %pM\n",
+ __func__, bss_conf->bssid);
+ return;
+ }
+
+ if (sta->deflink.vht_cap.vht_supported)
+ factor = u32_get_bits(sta->deflink.vht_cap.cap,
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+ else if (sta->deflink.ht_cap.ht_supported)
+ factor = sta->deflink.ht_cap.ampdu_factor;
+
+ rcu_read_unlock();
+
+ if (factor != 0xff)
+ ops->set_ampdu_factor(rtwdev, factor);
+}
+
MODULE_AUTHOR("Realtek Corporation");
MODULE_DESCRIPTION("Realtek 802.11ac wireless core module");
MODULE_LICENSE("Dual BSD/GPL");