diff mbox series

[v2,5/5] wifi: rtl8xxxu: Add rate control code for RTL8188EU

Message ID d2a2e00a-6a71-49da-356d-53bcd7c0d7e1@gmail.com (mailing list archive)
State Changes Requested
Delegated to: Kalle Valo
Headers show
Series [v2,1/5] wifi: rtl8xxxu: Deduplicate the efuse dumping code | expand

Commit Message

Bitterblue Smith Dec. 13, 2022, 5:33 p.m. UTC
Copied from the newer vendor driver, v5.2.2.4.

Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
v2:
 - Implement suggestions from Ping-Ke Shih:
   - Add missing break in two switch statements.
   - Remove unnecessary initialisation of idx in rtl8188e_set_tx_rpt_timing().
---
 .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  39 ++
 .../realtek/rtl8xxxu/rtl8xxxu_8188e.c         | 601 +++++++++++++++++-
 .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 131 +++-
 3 files changed, 741 insertions(+), 30 deletions(-)

Comments

kernel test robot Dec. 14, 2022, 8:07 a.m. UTC | #1
Hi Bitterblue,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on wireless-next/main]
[also build test WARNING on linus/master next-20221214]
[cannot apply to wireless/main v6.1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Bitterblue-Smith/wifi-rtl8xxxu-Deduplicate-the-efuse-dumping-code/20221214-013923
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
patch link:    https://lore.kernel.org/r/d2a2e00a-6a71-49da-356d-53bcd7c0d7e1%40gmail.com
patch subject: [PATCH v2 5/5] wifi: rtl8xxxu: Add rate control code for RTL8188EU
config: x86_64-allyesconfig
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/9ea9bb5b4820418238d20789475046de5b5e23f3
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Bitterblue-Smith/wifi-rtl8xxxu-Deduplicate-the-efuse-dumping-code/20221214-013923
        git checkout 9ea9bb5b4820418238d20789475046de5b5e23f3
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/net/wireless/realtek/rtl8xxxu/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c: In function 'rtl8188eu_config_channel':
   drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c:456:14: warning: variable 'ht' set but not used [-Wunused-but-set-variable]
     456 |         bool ht = true;
         |              ^~
   At top level:
>> drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c:383:17: warning: 'trying_necessary' defined but not used [-Wunused-const-variable=]
     383 | static const u8 trying_necessary[RATESIZE] = {
         |                 ^~~~~~~~~~~~~~~~
>> drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c:314:17: warning: 'retry_penalty_up' defined but not used [-Wunused-const-variable=]
     314 | static const u8 retry_penalty_up[RETRYSIZE + 1] = {49, 44, 16, 16, 0, 48}; /* 12% for rate up */
         |                 ^~~~~~~~~~~~~~~~


vim +/trying_necessary +383 drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c

   313	
 > 314	static const u8 retry_penalty_up[RETRYSIZE + 1] = {49, 44, 16, 16, 0, 48}; /* 12% for rate up */
   315	
   316	static const u8 pt_penalty[RETRYSIZE + 1] = {34, 31, 30, 24, 0, 32};
   317	
   318	static const u8 retry_penalty_idx_normal[2][RATESIZE] = {
   319		{ /* RSSI>TH */
   320			4, 4, 4, 5,
   321			4, 4, 5, 7, 7, 7, 8, 0x0a,
   322			4, 4, 4, 4, 6, 0x0a, 0x0b, 0x0d,
   323			5, 5, 7, 7, 8, 0x0b, 0x0d, 0x0f
   324		},
   325		{ /* RSSI<TH */
   326			0x0a, 0x0a, 0x0b, 0x0c,
   327			0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x10, 0x13, 0x13,
   328			0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x13, 0x13,
   329			9, 9, 9, 9, 0x0c, 0x0e, 0x11, 0x13
   330		}
   331	};
   332	
   333	static const u8 retry_penalty_idx_cut_i[2][RATESIZE] = {
   334		{ /* RSSI>TH */
   335			4, 4, 4, 5,
   336			4, 4, 5, 7, 7, 7, 8, 0x0a,
   337			4, 4, 4, 4, 6, 0x0a, 0x0b, 0x0d,
   338			5, 5, 7, 7, 8, 0x0b, 0x0d, 0x0f
   339		},
   340		{ /* RSSI<TH */
   341			0x0a, 0x0a, 0x0b, 0x0c,
   342			0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x10, 0x13, 0x13,
   343			0x06, 0x07, 0x08, 0x0d, 0x0e, 0x11, 0x11, 0x11,
   344			9, 9, 9, 9, 0x0c, 0x0e, 0x11, 0x13
   345		}
   346	};
   347	
   348	static const u8 retry_penalty_up_idx_normal[RATESIZE] = {
   349		0x0c, 0x0d, 0x0d, 0x0f,
   350		0x0d, 0x0e, 0x0f, 0x0f, 0x10, 0x12, 0x13, 0x14,
   351		0x0f, 0x10, 0x10, 0x12, 0x12, 0x13, 0x14, 0x15,
   352		0x11, 0x11, 0x12, 0x13, 0x13, 0x13, 0x14, 0x15
   353	};
   354	
   355	static const u8 retry_penalty_up_idx_cut_i[RATESIZE] = {
   356		0x0c, 0x0d, 0x0d, 0x0f,
   357		0x0d, 0x0e, 0x0f, 0x0f, 0x10, 0x12, 0x13, 0x14,
   358		0x0b, 0x0b, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12,
   359		0x11, 0x11, 0x12, 0x13, 0x13, 0x13, 0x14, 0x15
   360	};
   361	
   362	static const u8 rssi_threshold[RATESIZE] = {
   363		0, 0, 0, 0,
   364		0, 0, 0, 0, 0, 0x24, 0x26, 0x2a,
   365		0x18, 0x1a, 0x1d, 0x1f, 0x21, 0x27, 0x29, 0x2a,
   366		0, 0, 0, 0x1f, 0x23, 0x28, 0x2a, 0x2c
   367	};
   368	
   369	static const u16 n_threshold_high[RATESIZE] = {
   370		4, 4, 8, 16,
   371		24, 36, 48, 72, 96, 144, 192, 216,
   372		60, 80, 100, 160, 240, 400, 600, 800,
   373		300, 320, 480, 720, 1000, 1200, 1600, 2000
   374	};
   375	
   376	static const u16 n_threshold_low[RATESIZE] = {
   377		2, 2, 4, 8,
   378		12, 18, 24, 36, 48, 72, 96, 108,
   379		30, 40, 50, 80, 120, 200, 300, 400,
   380		150, 160, 240, 360, 500, 600, 800, 1000
   381	};
   382	
 > 383	static const u8 trying_necessary[RATESIZE] = {
   384		2, 2, 2, 2,
   385		2, 2, 3, 3, 4, 4, 5, 7,
   386		4, 4, 7, 10, 10, 12, 12, 18,
   387		5, 7, 7, 8, 11, 18, 36, 60
   388	};
   389	
   390	static const u8 dropping_necessary[RATESIZE] = {
   391		1, 1, 1, 1,
   392		1, 2, 3, 4, 5, 6, 7, 8,
   393		1, 2, 3, 4, 5, 6, 7, 8,
   394		5, 6, 7, 8, 9, 10, 11, 12
   395	};
   396	
   397	static const u8 pending_for_rate_up_fail[5] = {2, 10, 24, 40, 60};
   398	
   399	static const u16 dynamic_tx_rpt_timing[6] = {
   400		0x186a, 0x30d4, 0x493e, 0x61a8, 0x7a12, 0x927c /* 200ms-1200ms */
   401	};
   402	
   403	enum rtl8188e_tx_rpt_timing {
   404		DEFAULT_TIMING = 0,
   405		INCREASE_TIMING,
   406		DECREASE_TIMING
   407	};
   408	
   409	static int rtl8188eu_identify_chip(struct rtl8xxxu_priv *priv)
   410	{
   411		struct device *dev = &priv->udev->dev;
   412		u32 sys_cfg, vendor;
   413		int ret = 0;
   414	
   415		strscpy(priv->chip_name, "8188EU", sizeof(priv->chip_name));
   416		priv->rtl_chip = RTL8188E;
   417		priv->rf_paths = 1;
   418		priv->rx_paths = 1;
   419		priv->tx_paths = 1;
   420		priv->has_wifi = 1;
   421	
   422		sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG);
   423		priv->chip_cut = u32_get_bits(sys_cfg, SYS_CFG_CHIP_VERSION_MASK);
   424		if (sys_cfg & SYS_CFG_TRP_VAUX_EN) {
   425			dev_info(dev, "Unsupported test chip\n");
   426			ret = -EOPNOTSUPP;
   427			goto out;
   428		}
   429	
   430		/*
   431		 * TODO: At a glance, I cut requires a different firmware,
   432		 * different initialisation tables, and no software rate
   433		 * control. The vendor driver is not configured to handle
   434		 * I cut chips by default. Are there any in the wild?
   435		 */
   436		if (priv->chip_cut == 8) {
   437			dev_info(dev, "RTL8188EU cut I is not supported. Please complain about it at linux-wireless@vger.kernel.org.\n");
   438			ret = -EOPNOTSUPP;
   439			goto out;
   440		}
   441	
   442		vendor = sys_cfg & SYS_CFG_VENDOR_ID;
   443		rtl8xxxu_identify_vendor_1bit(priv, vendor);
   444	
   445		ret = rtl8xxxu_config_endpoints_no_sie(priv);
   446	
   447	out:
   448		return ret;
   449	}
   450	
   451	static void rtl8188eu_config_channel(struct ieee80211_hw *hw)
   452	{
   453		struct rtl8xxxu_priv *priv = hw->priv;
   454		u32 val32, rsr;
   455		u8 opmode;
 > 456		bool ht = true;
   457		int sec_ch_above, channel;
   458		int i;
   459	
   460		opmode = rtl8xxxu_read8(priv, REG_BW_OPMODE);
   461		rsr = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET);
   462		channel = hw->conf.chandef.chan->hw_value;
   463	
   464		switch (hw->conf.chandef.width) {
   465		case NL80211_CHAN_WIDTH_20_NOHT:
   466			ht = false;
   467			fallthrough;
   468		case NL80211_CHAN_WIDTH_20:
   469			opmode |= BW_OPMODE_20MHZ;
   470			rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);
   471	
   472			val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
   473			val32 &= ~FPGA_RF_MODE;
   474			rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
   475	
   476			val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
   477			val32 &= ~FPGA_RF_MODE;
   478			rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);
   479			break;
   480		case NL80211_CHAN_WIDTH_40:
   481			if (hw->conf.chandef.center_freq1 >
   482			    hw->conf.chandef.chan->center_freq) {
   483				sec_ch_above = 1;
   484				channel += 2;
   485			} else {
   486				sec_ch_above = 0;
   487				channel -= 2;
   488			}
   489	
   490			opmode &= ~BW_OPMODE_20MHZ;
   491			rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);
   492			rsr &= ~RSR_RSC_BANDWIDTH_40M;
   493			if (sec_ch_above)
   494				rsr |= RSR_RSC_LOWER_SUB_CHANNEL;
   495			else
   496				rsr |= RSR_RSC_UPPER_SUB_CHANNEL;
   497			rtl8xxxu_write32(priv, REG_RESPONSE_RATE_SET, rsr);
   498	
   499			val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
   500			val32 |= FPGA_RF_MODE;
   501			rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
   502	
   503			val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
   504			val32 |= FPGA_RF_MODE;
   505			rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);
   506	
   507			/*
   508			 * Set Control channel to upper or lower. These settings
   509			 * are required only for 40MHz
   510			 */
   511			val32 = rtl8xxxu_read32(priv, REG_CCK0_SYSTEM);
   512			val32 &= ~CCK0_SIDEBAND;
   513			if (!sec_ch_above)
   514				val32 |= CCK0_SIDEBAND;
   515			rtl8xxxu_write32(priv, REG_CCK0_SYSTEM, val32);
   516	
   517			val32 = rtl8xxxu_read32(priv, REG_OFDM1_LSTF);
   518			val32 &= ~OFDM_LSTF_PRIME_CH_MASK; /* 0xc00 */
   519			if (sec_ch_above)
   520				val32 |= OFDM_LSTF_PRIME_CH_LOW;
   521			else
   522				val32 |= OFDM_LSTF_PRIME_CH_HIGH;
   523			rtl8xxxu_write32(priv, REG_OFDM1_LSTF, val32);
   524	
   525			val32 = rtl8xxxu_read32(priv, REG_FPGA0_POWER_SAVE);
   526			val32 &= ~(FPGA0_PS_LOWER_CHANNEL | FPGA0_PS_UPPER_CHANNEL);
   527			if (sec_ch_above)
   528				val32 |= FPGA0_PS_UPPER_CHANNEL;
   529			else
   530				val32 |= FPGA0_PS_LOWER_CHANNEL;
   531			rtl8xxxu_write32(priv, REG_FPGA0_POWER_SAVE, val32);
   532			break;
   533	
   534		default:
   535			break;
   536		}
   537	
   538		for (i = RF_A; i < priv->rf_paths; i++) {
   539			val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
   540			u32p_replace_bits(&val32, channel, MODE_AG_CHANNEL_MASK);
   541			rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
   542		}
   543	
   544		for (i = RF_A; i < priv->rf_paths; i++) {
   545			val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
   546			val32 &= ~MODE_AG_BW_MASK;
   547			if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40)
   548				val32 |= MODE_AG_BW_40MHZ_8723B;
   549			else
   550				val32 |= MODE_AG_BW_20MHZ_8723B;
   551			rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
   552		}
   553	}
   554
Ping-Ke Shih Dec. 15, 2022, 1:14 p.m. UTC | #2
On Tue, 2022-12-13 at 19:33 +0200, Bitterblue Smith wrote:
> Copied from the newer vendor driver, v5.2.2.4.
> 
> Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
> ---
> v2:
>  - Implement suggestions from Ping-Ke Shih:
>    - Add missing break in two switch statements.
>    - Remove unnecessary initialisation of idx in rtl8188e_set_tx_rpt_timing().
> ---
>  .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  39 ++
>  .../realtek/rtl8xxxu/rtl8xxxu_8188e.c         | 601 +++++++++++++++++-
>  .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 131 +++-
>  3 files changed, 741 insertions(+), 30 deletions(-)
> 
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> index 29f5dbee16b0..be9479f969b7 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> 

[...]

> +
> +static void rtl8188e_power_training_try_state(struct rtl8xxxu_ra_info *ra)
> +{
> +	ra->pt_try_state = 0;
> +	switch (ra->pt_mode_ss) {
> +	case 3:
> +		if (ra->decision_rate >= DESC_RATE_MCS13)
> +			ra->pt_try_state = 1;
> +		break;
> +	case 2:
> +		if (ra->decision_rate >= DESC_RATE_MCS5)
> +			ra->pt_try_state = 1;
> +		break;
> +	case 1:
> +		if (ra->decision_rate >= DESC_RATE_48M)
> +			ra->pt_try_state = 1;
> +		break;
> +	case 0:
> +		if (ra->decision_rate >= DESC_RATE_11M)
> +			ra->pt_try_state = 1;
> +		break;
> +	default:
> +		ra->pt_try_state = 0;

It seems to be 0 already because of first statement of this function. 

> +		break;
> +	}
> +
> +	if (ra->rssi_sta_ra < 48) {
> +		ra->pt_stage = 0;
> +	} else if (ra->pt_try_state == 1) {
> +		if ((ra->pt_stop_count >= 10) ||
> +		    (ra->pt_pre_rssi > ra->rssi_sta_ra + 5) ||
> +		    (ra->pt_pre_rssi < ra->rssi_sta_ra - 5) ||
> +		    (ra->decision_rate != ra->pt_pre_rate)) {
> +			if (ra->pt_stage == 0)
> +				ra->pt_stage = 1;
> +			else if (ra->pt_stage == 1)
> +				ra->pt_stage = 3;
> +			else
> +				ra->pt_stage = 5;
> +
> +			ra->pt_pre_rssi = ra->rssi_sta_ra;
> +			ra->pt_stop_count = 0;
> +		} else {
> +			ra->ra_stage = 0;
> +			ra->pt_stop_count++;
> +		}
> +	} else {
> +		ra->pt_stage = 0;
> +		ra->ra_stage = 0;
> +	}
> +
> +	ra->pt_pre_rate = ra->decision_rate;
> +
> +	/* TODO: implement the "false alarm" statistics for this */
> +	/* Disable power training when noisy environment */
> +	/* if (p_dm_odm->is_disable_power_training) { */
> +	if (1) {
> +		ra->pt_stage = 0;
> +		ra->ra_stage = 0;
> +		ra->pt_stop_count = 0;
> +	}
> +}
> +
> +static void rtl8188e_power_training_decision(struct rtl8xxxu_ra_info *ra)
> +{
> +	u8 temp_stage;
> +	u32 numsc;
> +	u32 num_total;
> +	u8 stage_id;
> +	u8 j;
> +
> +	numsc = 0;
> +	num_total = ra->total * pt_penalty[5];
> +	for (j = 0; j <= 4; j++) {
> +		numsc += ra->retry[j] * pt_penalty[j];
> +
> +		if (numsc > num_total)
> +			break;
> +	}
> +
> +	j = j >> 1;

j >>= 1;

> +	temp_stage = (ra->pt_stage + 1) >> 1;
> +	if (temp_stage > j)
> +		stage_id = temp_stage - j;
> +	else
> +		stage_id = 0;
> +
> +	ra->pt_smooth_factor = (ra->pt_smooth_factor >> 1) +
> +			       (ra->pt_smooth_factor >> 2) +
> +			       stage_id * 16 + 2;
> +	if (ra->pt_smooth_factor > 192)
> +		ra->pt_smooth_factor = 192;
> +	stage_id = ra->pt_smooth_factor >> 6;
> +	temp_stage = stage_id * 2;
> +	if (temp_stage != 0)
> +		temp_stage--;
> +	if (ra->drop > 3)
> +		temp_stage = 0;
> +	ra->pt_stage = temp_stage;
> +}
> +
> +void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
> +{
> +	u32 *_rx_desc = (u32 *)(skb->data - sizeof(struct rtl8xxxu_rxdesc16));
> +	struct rtl8xxxu_rxdesc16 *rx_desc = (struct rtl8xxxu_rxdesc16 *)_rx_desc;
> +	struct device *dev = &priv->udev->dev;
> +	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
> +

no blank line in declaration part.

> +	u32 tx_rpt_len = rx_desc->pktlen & 0x3ff;
> +	u32 items = tx_rpt_len / TX_RPT2_ITEM_SIZE;
> +	u64 macid_valid = ((u64)_rx_desc[5] << 32) | _rx_desc[4];
> +	u32 macid;
> +	u8 *rpt = skb->data;
> +	bool valid;
> +	u16 min_rpt_time = 0x927c;
> +
> +	dev_dbg(dev, "%s: len: %d items: %d\n", __func__, tx_rpt_len, items);
> +
> +	for (macid = 0; macid < items; macid++) {
> +		valid = false;
> +
> +		if (macid < 64)
> +			valid = macid_valid & BIT(macid);
> +
> +		if (valid) {
> +			ra->retry[0] = le16_to_cpu(*(__le16 *)rpt);
> +			ra->retry[1] = rpt[2];
> +			ra->retry[2] = rpt[3];
> +			ra->retry[3] = rpt[4];
> +			ra->retry[4] = rpt[5];
> +			ra->drop = rpt[6];
> +			ra->total = ra->retry[0] + ra->retry[1] + ra->retry[2] +
> +				    ra->retry[3] + ra->retry[4] + ra->drop;
> +
> +			if (ra->total > 0) {
> +				if (ra->ra_stage < 5)
> +					rtl8188e_rate_decision(ra);
> +				else if (ra->ra_stage == 5)
> +					rtl8188e_power_training_try_state(ra);
> +				else /* ra->ra_stage == 6 */
> +					rtl8188e_power_training_decision(ra);
> +
> +				if (ra->ra_stage <= 5)
> +					ra->ra_stage++;
> +				else
> +					ra->ra_stage = 0;
> +			}
> +		} else if (macid == 0) {
> +			dev_warn(dev, "%s: TX report item 0 not valid\n", __func__);
> +		}
> +
> +		dev_dbg(dev, "%s:  valid: %d retry: %d %d %d %d %d drop: %d\n",
> +			__func__, valid,
> +			ra->retry[0], ra->retry[1], ra->retry[2],
> +			ra->retry[3], ra->retry[4], ra->drop);
> +
> +		if (min_rpt_time > ra->rpt_time)
> +			min_rpt_time = ra->rpt_time;
> +
> +		rpt += TX_RPT2_ITEM_SIZE;
> +
> +		/*
> +		 * We only use macid 0, so only the first item is relevant.
> +		 * AP mode will use more of them if it's ever implemented.
> +		 */
> +		break;
> +	}
> +
> +	if (min_rpt_time != ra->pre_min_rpt_time) {
> +		rtl8xxxu_write16(priv, REG_TX_REPORT_TIME, min_rpt_time);
> +		ra->pre_min_rpt_time = min_rpt_time;
> +	}
> +}
> +

[...]

> +
>  int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
>  {
>  	struct ieee80211_hw *hw = priv->hw;
> @@ -5823,38 +5884,46 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff
> *skb)
>  
>  		skb_pull(skb, sizeof(struct rtl8xxxu_rxdesc16));
>  
> -		phy_stats = (struct rtl8723au_phy_stats *)skb->data;
> +		if (rx_desc->rpt_sel) {
> +			skb_queue_tail(&priv->c2hcmd_queue, skb);
> +			schedule_work(&priv->c2hcmd_work);
> +		} else {
> +			phy_stats = (struct rtl8723au_phy_stats *)skb->data;
>  
> -		skb_pull(skb, drvinfo_sz + desc_shift);
> +			skb_pull(skb, drvinfo_sz + desc_shift);
>  
> -		skb_trim(skb, pkt_len);
> +			skb_trim(skb, pkt_len);
>  
> -		if (rx_desc->phy_stats)
> -			rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
> -						   rx_desc->rxmcs, (struct ieee80211_hdr *)skb-
> >data,
> -						   rx_desc->crc32 || rx_desc->icverr);
> +			if (rx_desc->phy_stats)
> +				rtl8xxxu_rx_parse_phystats(
> +					priv, rx_status, phy_stats,
> +					rx_desc->rxmcs,
> +					(struct ieee80211_hdr *)skb->data,
> +					rx_desc->crc32 || rx_desc->icverr
> +				);

squash this parenthesis to previous line.

>  
> -		rx_status->mactime = rx_desc->tsfl;
> -		rx_status->flag |= RX_FLAG_MACTIME_START;
> +			rx_status->mactime = rx_desc->tsfl;
> +			rx_status->flag |= RX_FLAG_MACTIME_START;
>  
> -		if (!rx_desc->swdec)
> -			rx_status->flag |= RX_FLAG_DECRYPTED;
> -		if (rx_desc->crc32)
> -			rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
> -		if (rx_desc->bw)
> -			rx_status->bw = RATE_INFO_BW_40;
> +			if (!rx_desc->swdec)
> +				rx_status->flag |= RX_FLAG_DECRYPTED;
> +			if (rx_desc->crc32)
> +				rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
> +			if (rx_desc->bw)
> +				rx_status->bw = RATE_INFO_BW_40;
>  
> -		if (rx_desc->rxht) {
> -			rx_status->encoding = RX_ENC_HT;
> -			rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
> -		} else {
> -			rx_status->rate_idx = rx_desc->rxmcs;
> -		}
> +			if (rx_desc->rxht) {
> +				rx_status->encoding = RX_ENC_HT;
> +				rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
> +			} else {
> +				rx_status->rate_idx = rx_desc->rxmcs;
> +			}
>  
> -		rx_status->freq = hw->conf.chandef.chan->center_freq;
> -		rx_status->band = hw->conf.chandef.chan->band;
> +			rx_status->freq = hw->conf.chandef.chan->center_freq;
> +			rx_status->band = hw->conf.chandef.chan->band;
>  
> -		ieee80211_rx_irqsafe(hw, skb);
> +			ieee80211_rx_irqsafe(hw, skb);
> +		}
>  
>  		skb = next_skb;
>  		if (skb)
> 

[...]

Only some minor comments.

Thank you for the patches.
--
Ping-Ke
Bitterblue Smith Dec. 17, 2022, 12:43 p.m. UTC | #3
On 15/12/2022 15:14, Ping-Ke Shih wrote:
> On Tue, 2022-12-13 at 19:33 +0200, Bitterblue Smith wrote:
>> Copied from the newer vendor driver, v5.2.2.4.
>>
>> Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
>> ---
>> v2:
>>  - Implement suggestions from Ping-Ke Shih:
>>    - Add missing break in two switch statements.
>>    - Remove unnecessary initialisation of idx in rtl8188e_set_tx_rpt_timing().
>> ---
>>  .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  39 ++
>>  .../realtek/rtl8xxxu/rtl8xxxu_8188e.c         | 601 +++++++++++++++++-
>>  .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 131 +++-
>>  3 files changed, 741 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
>> b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
>> index 29f5dbee16b0..be9479f969b7 100644
>> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
>> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
>>
> 
> [...]
> 
>> +
>> +static void rtl8188e_power_training_try_state(struct rtl8xxxu_ra_info *ra)
>> +{
>> +	ra->pt_try_state = 0;
>> +	switch (ra->pt_mode_ss) {
>> +	case 3:
>> +		if (ra->decision_rate >= DESC_RATE_MCS13)
>> +			ra->pt_try_state = 1;
>> +		break;
>> +	case 2:
>> +		if (ra->decision_rate >= DESC_RATE_MCS5)
>> +			ra->pt_try_state = 1;
>> +		break;
>> +	case 1:
>> +		if (ra->decision_rate >= DESC_RATE_48M)
>> +			ra->pt_try_state = 1;
>> +		break;
>> +	case 0:
>> +		if (ra->decision_rate >= DESC_RATE_11M)
>> +			ra->pt_try_state = 1;
>> +		break;
>> +	default:
>> +		ra->pt_try_state = 0;
> 
> It seems to be 0 already because of first statement of this function. 
> 
>> +		break;
>> +	}
>> +
>> +	if (ra->rssi_sta_ra < 48) {
>> +		ra->pt_stage = 0;
>> +	} else if (ra->pt_try_state == 1) {
>> +		if ((ra->pt_stop_count >= 10) ||
>> +		    (ra->pt_pre_rssi > ra->rssi_sta_ra + 5) ||
>> +		    (ra->pt_pre_rssi < ra->rssi_sta_ra - 5) ||
>> +		    (ra->decision_rate != ra->pt_pre_rate)) {
>> +			if (ra->pt_stage == 0)
>> +				ra->pt_stage = 1;
>> +			else if (ra->pt_stage == 1)
>> +				ra->pt_stage = 3;
>> +			else
>> +				ra->pt_stage = 5;
>> +
>> +			ra->pt_pre_rssi = ra->rssi_sta_ra;
>> +			ra->pt_stop_count = 0;
>> +		} else {
>> +			ra->ra_stage = 0;
>> +			ra->pt_stop_count++;
>> +		}
>> +	} else {
>> +		ra->pt_stage = 0;
>> +		ra->ra_stage = 0;
>> +	}
>> +
>> +	ra->pt_pre_rate = ra->decision_rate;
>> +
>> +	/* TODO: implement the "false alarm" statistics for this */
>> +	/* Disable power training when noisy environment */
>> +	/* if (p_dm_odm->is_disable_power_training) { */
>> +	if (1) {
>> +		ra->pt_stage = 0;
>> +		ra->ra_stage = 0;
>> +		ra->pt_stop_count = 0;
>> +	}
>> +}
>> +
>> +static void rtl8188e_power_training_decision(struct rtl8xxxu_ra_info *ra)
>> +{
>> +	u8 temp_stage;
>> +	u32 numsc;
>> +	u32 num_total;
>> +	u8 stage_id;
>> +	u8 j;
>> +
>> +	numsc = 0;
>> +	num_total = ra->total * pt_penalty[5];
>> +	for (j = 0; j <= 4; j++) {
>> +		numsc += ra->retry[j] * pt_penalty[j];
>> +
>> +		if (numsc > num_total)
>> +			break;
>> +	}
>> +
>> +	j = j >> 1;
> 
> j >>= 1;
> 
>> +	temp_stage = (ra->pt_stage + 1) >> 1;
>> +	if (temp_stage > j)
>> +		stage_id = temp_stage - j;
>> +	else
>> +		stage_id = 0;
>> +
>> +	ra->pt_smooth_factor = (ra->pt_smooth_factor >> 1) +
>> +			       (ra->pt_smooth_factor >> 2) +
>> +			       stage_id * 16 + 2;
>> +	if (ra->pt_smooth_factor > 192)
>> +		ra->pt_smooth_factor = 192;
>> +	stage_id = ra->pt_smooth_factor >> 6;
>> +	temp_stage = stage_id * 2;
>> +	if (temp_stage != 0)
>> +		temp_stage--;
>> +	if (ra->drop > 3)
>> +		temp_stage = 0;
>> +	ra->pt_stage = temp_stage;
>> +}
>> +
>> +void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
>> +{
>> +	u32 *_rx_desc = (u32 *)(skb->data - sizeof(struct rtl8xxxu_rxdesc16));
>> +	struct rtl8xxxu_rxdesc16 *rx_desc = (struct rtl8xxxu_rxdesc16 *)_rx_desc;
>> +	struct device *dev = &priv->udev->dev;
>> +	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
>> +
> 
> no blank line in declaration part.
> 
>> +	u32 tx_rpt_len = rx_desc->pktlen & 0x3ff;
>> +	u32 items = tx_rpt_len / TX_RPT2_ITEM_SIZE;
>> +	u64 macid_valid = ((u64)_rx_desc[5] << 32) | _rx_desc[4];
>> +	u32 macid;
>> +	u8 *rpt = skb->data;
>> +	bool valid;
>> +	u16 min_rpt_time = 0x927c;
>> +
>> +	dev_dbg(dev, "%s: len: %d items: %d\n", __func__, tx_rpt_len, items);
>> +
>> +	for (macid = 0; macid < items; macid++) {
>> +		valid = false;
>> +
>> +		if (macid < 64)
>> +			valid = macid_valid & BIT(macid);
>> +
>> +		if (valid) {
>> +			ra->retry[0] = le16_to_cpu(*(__le16 *)rpt);
>> +			ra->retry[1] = rpt[2];
>> +			ra->retry[2] = rpt[3];
>> +			ra->retry[3] = rpt[4];
>> +			ra->retry[4] = rpt[5];
>> +			ra->drop = rpt[6];
>> +			ra->total = ra->retry[0] + ra->retry[1] + ra->retry[2] +
>> +				    ra->retry[3] + ra->retry[4] + ra->drop;
>> +
>> +			if (ra->total > 0) {
>> +				if (ra->ra_stage < 5)
>> +					rtl8188e_rate_decision(ra);
>> +				else if (ra->ra_stage == 5)
>> +					rtl8188e_power_training_try_state(ra);
>> +				else /* ra->ra_stage == 6 */
>> +					rtl8188e_power_training_decision(ra);
>> +
>> +				if (ra->ra_stage <= 5)
>> +					ra->ra_stage++;
>> +				else
>> +					ra->ra_stage = 0;
>> +			}
>> +		} else if (macid == 0) {
>> +			dev_warn(dev, "%s: TX report item 0 not valid\n", __func__);
>> +		}
>> +
>> +		dev_dbg(dev, "%s:  valid: %d retry: %d %d %d %d %d drop: %d\n",
>> +			__func__, valid,
>> +			ra->retry[0], ra->retry[1], ra->retry[2],
>> +			ra->retry[3], ra->retry[4], ra->drop);
>> +
>> +		if (min_rpt_time > ra->rpt_time)
>> +			min_rpt_time = ra->rpt_time;
>> +
>> +		rpt += TX_RPT2_ITEM_SIZE;
>> +
>> +		/*
>> +		 * We only use macid 0, so only the first item is relevant.
>> +		 * AP mode will use more of them if it's ever implemented.
>> +		 */
>> +		break;
>> +	}
>> +
>> +	if (min_rpt_time != ra->pre_min_rpt_time) {
>> +		rtl8xxxu_write16(priv, REG_TX_REPORT_TIME, min_rpt_time);
>> +		ra->pre_min_rpt_time = min_rpt_time;
>> +	}
>> +}
>> +
> 
> [...]
> 
>> +
>>  int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
>>  {
>>  	struct ieee80211_hw *hw = priv->hw;
>> @@ -5823,38 +5884,46 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff
>> *skb)
>>  
>>  		skb_pull(skb, sizeof(struct rtl8xxxu_rxdesc16));
>>  
>> -		phy_stats = (struct rtl8723au_phy_stats *)skb->data;
>> +		if (rx_desc->rpt_sel) {
>> +			skb_queue_tail(&priv->c2hcmd_queue, skb);
>> +			schedule_work(&priv->c2hcmd_work);
>> +		} else {
>> +			phy_stats = (struct rtl8723au_phy_stats *)skb->data;
>>  
>> -		skb_pull(skb, drvinfo_sz + desc_shift);
>> +			skb_pull(skb, drvinfo_sz + desc_shift);
>>  
>> -		skb_trim(skb, pkt_len);
>> +			skb_trim(skb, pkt_len);
>>  
>> -		if (rx_desc->phy_stats)
>> -			rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
>> -						   rx_desc->rxmcs, (struct ieee80211_hdr *)skb-
>>> data,
>> -						   rx_desc->crc32 || rx_desc->icverr);
>> +			if (rx_desc->phy_stats)
>> +				rtl8xxxu_rx_parse_phystats(
>> +					priv, rx_status, phy_stats,
>> +					rx_desc->rxmcs,
>> +					(struct ieee80211_hdr *)skb->data,
>> +					rx_desc->crc32 || rx_desc->icverr
>> +				);
> 
> squash this parenthesis to previous line.
> 
>>  
>> -		rx_status->mactime = rx_desc->tsfl;
>> -		rx_status->flag |= RX_FLAG_MACTIME_START;
>> +			rx_status->mactime = rx_desc->tsfl;
>> +			rx_status->flag |= RX_FLAG_MACTIME_START;
>>  
>> -		if (!rx_desc->swdec)
>> -			rx_status->flag |= RX_FLAG_DECRYPTED;
>> -		if (rx_desc->crc32)
>> -			rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
>> -		if (rx_desc->bw)
>> -			rx_status->bw = RATE_INFO_BW_40;
>> +			if (!rx_desc->swdec)
>> +				rx_status->flag |= RX_FLAG_DECRYPTED;
>> +			if (rx_desc->crc32)
>> +				rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
>> +			if (rx_desc->bw)
>> +				rx_status->bw = RATE_INFO_BW_40;
>>  
>> -		if (rx_desc->rxht) {
>> -			rx_status->encoding = RX_ENC_HT;
>> -			rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
>> -		} else {
>> -			rx_status->rate_idx = rx_desc->rxmcs;
>> -		}
>> +			if (rx_desc->rxht) {
>> +				rx_status->encoding = RX_ENC_HT;
>> +				rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
>> +			} else {
>> +				rx_status->rate_idx = rx_desc->rxmcs;
>> +			}
>>  
>> -		rx_status->freq = hw->conf.chandef.chan->center_freq;
>> -		rx_status->band = hw->conf.chandef.chan->band;
>> +			rx_status->freq = hw->conf.chandef.chan->center_freq;
>> +			rx_status->band = hw->conf.chandef.chan->band;
>>  
>> -		ieee80211_rx_irqsafe(hw, skb);
>> +			ieee80211_rx_irqsafe(hw, skb);
>> +		}
>>  
>>  		skb = next_skb;
>>  		if (skb)
>>
> 
> [...]
> 
> Only some minor comments.
> 
> Thank you for the patches.
> --
> Ping-Ke
> 
I'll make the changes. Thank you for reviewing.
diff mbox series

Patch

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 29f5dbee16b0..be9479f969b7 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -531,6 +531,7 @@  struct rtl8xxxu_txdesc40 {
 #define TXDESC32_CTS_SELF_ENABLE	BIT(11)
 #define TXDESC32_RTS_CTS_ENABLE		BIT(12)
 #define TXDESC32_HW_RTS_ENABLE		BIT(13)
+#define TXDESC32_PT_STAGE_MASK		GENMASK(17, 15)
 #define TXDESC_PRIME_CH_OFF_LOWER	BIT(20)
 #define TXDESC_PRIME_CH_OFF_UPPER	BIT(21)
 #define TXDESC32_SHORT_PREAMBLE		BIT(24)
@@ -1376,6 +1377,39 @@  struct rtl8xxxu_ra_report {
 	u8 desc_rate;
 };
 
+struct rtl8xxxu_ra_info {
+	u8 rate_id;
+	u32 rate_mask;
+	u32 ra_use_rate;
+	u8 rate_sgi;
+	u8 rssi_sta_ra;		/* Percentage */
+	u8 pre_rssi_sta_ra;
+	u8 sgi_enable;
+	u8 decision_rate;
+	u8 pre_rate;
+	u8 highest_rate;
+	u8 lowest_rate;
+	u32 nsc_up;
+	u32 nsc_down;
+	u32 total;
+	u16 retry[5];
+	u16 drop;
+	u16 rpt_time;
+	u16 pre_min_rpt_time;
+	u8 dynamic_tx_rpt_timing_counter;
+	u8 ra_waiting_counter;
+	u8 ra_pending_counter;
+	u8 ra_drop_after_down;
+	u8 pt_try_state;	/* 0 trying state, 1 for decision state */
+	u8 pt_stage;		/* 0~6 */
+	u8 pt_stop_count;	/* Stop PT counter */
+	u8 pt_pre_rate;		/* if rate change do PT */
+	u8 pt_pre_rssi;		/* if RSSI change 5% do PT */
+	u8 pt_mode_ss;		/* decide which rate should do PT */
+	u8 ra_stage;		/* StageRA, decide how many times RA will be done between PT */
+	u8 pt_smooth_factor;
+};
+
 #define CFO_TH_XTAL_HIGH	20 /* kHz */
 #define CFO_TH_XTAL_LOW	10 /* kHz */
 #define CFO_TH_ATC		80 /* kHz */
@@ -1509,6 +1543,7 @@  struct rtl8xxxu_priv {
 	struct rtl8xxxu_btcoex bt_coex;
 	struct rtl8xxxu_ra_report ra_report;
 	struct rtl8xxxu_cfo_tracking cfo_tracking;
+	struct rtl8xxxu_ra_info ra_info;
 };
 
 struct rtl8xxxu_rx_urb {
@@ -1684,6 +1719,10 @@  void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv);
 void rtl8723a_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap);
 void rtl8188f_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap);
 s8 rtl8723a_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt);
+void rtl8xxxu_update_ra_report(struct rtl8xxxu_ra_report *rarpt,
+			       u8 rate, u8 sgi, u8 bw);
+void rtl8188e_ra_info_init_all(struct rtl8xxxu_ra_info *ra);
+void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *skb);
 
 extern struct rtl8xxxu_fileops rtl8188fu_fops;
 extern struct rtl8xxxu_fileops rtl8188eu_fops;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c
index 587555da9bce..f5693b2c1be6 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c
@@ -280,6 +280,132 @@  static const struct rtl8xxxu_rfregval rtl8188eu_radioa_init_table[] = {
 	{0xff, 0xffffffff}
 };
 
+#define PERENTRY		23
+#define RETRYSIZE		5
+#define RATESIZE		28
+#define TX_RPT2_ITEM_SIZE	8
+
+static const u8 retry_penalty[PERENTRY][RETRYSIZE + 1] = {
+	{5, 4, 3, 2, 0, 3}, /* 92 , idx=0 */
+	{6, 5, 4, 3, 0, 4}, /* 86 , idx=1 */
+	{6, 5, 4, 2, 0, 4}, /* 81 , idx=2 */
+	{8, 7, 6, 4, 0, 6}, /* 75 , idx=3 */
+	{10, 9, 8, 6, 0, 8}, /* 71 , idx=4 */
+	{10, 9, 8, 4, 0, 8}, /* 66 , idx=5 */
+	{10, 9, 8, 2, 0, 8}, /* 62 , idx=6 */
+	{10, 9, 8, 0, 0, 8}, /* 59 , idx=7 */
+	{18, 17, 16, 8, 0, 16}, /* 53 , idx=8 */
+	{26, 25, 24, 16, 0, 24}, /* 50 , idx=9 */
+	{34, 33, 32, 24, 0, 32}, /* 47 , idx=0x0a */
+	{34, 31, 28, 20, 0, 32}, /* 43 , idx=0x0b */
+	{34, 31, 27, 18, 0, 32}, /* 40 , idx=0x0c */
+	{34, 31, 26, 16, 0, 32}, /* 37 , idx=0x0d */
+	{34, 30, 22, 16, 0, 32}, /* 32 , idx=0x0e */
+	{34, 30, 24, 16, 0, 32}, /* 26 , idx=0x0f */
+	{49, 46, 40, 16, 0, 48}, /* 20 , idx=0x10 */
+	{49, 45, 32, 0, 0, 48}, /* 17 , idx=0x11 */
+	{49, 45, 22, 18, 0, 48}, /* 15 , idx=0x12 */
+	{49, 40, 24, 16, 0, 48}, /* 12 , idx=0x13 */
+	{49, 32, 18, 12, 0, 48}, /* 9 , idx=0x14 */
+	{49, 22, 18, 14, 0, 48}, /* 6 , idx=0x15 */
+	{49, 16, 16, 0, 0, 48} /* 3, idx=0x16 */
+};
+
+static const u8 retry_penalty_up[RETRYSIZE + 1] = {49, 44, 16, 16, 0, 48}; /* 12% for rate up */
+
+static const u8 pt_penalty[RETRYSIZE + 1] = {34, 31, 30, 24, 0, 32};
+
+static const u8 retry_penalty_idx_normal[2][RATESIZE] = {
+	{ /* RSSI>TH */
+		4, 4, 4, 5,
+		4, 4, 5, 7, 7, 7, 8, 0x0a,
+		4, 4, 4, 4, 6, 0x0a, 0x0b, 0x0d,
+		5, 5, 7, 7, 8, 0x0b, 0x0d, 0x0f
+	},
+	{ /* RSSI<TH */
+		0x0a, 0x0a, 0x0b, 0x0c,
+		0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x10, 0x13, 0x13,
+		0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x13, 0x13,
+		9, 9, 9, 9, 0x0c, 0x0e, 0x11, 0x13
+	}
+};
+
+static const u8 retry_penalty_idx_cut_i[2][RATESIZE] = {
+	{ /* RSSI>TH */
+		4, 4, 4, 5,
+		4, 4, 5, 7, 7, 7, 8, 0x0a,
+		4, 4, 4, 4, 6, 0x0a, 0x0b, 0x0d,
+		5, 5, 7, 7, 8, 0x0b, 0x0d, 0x0f
+	},
+	{ /* RSSI<TH */
+		0x0a, 0x0a, 0x0b, 0x0c,
+		0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x10, 0x13, 0x13,
+		0x06, 0x07, 0x08, 0x0d, 0x0e, 0x11, 0x11, 0x11,
+		9, 9, 9, 9, 0x0c, 0x0e, 0x11, 0x13
+	}
+};
+
+static const u8 retry_penalty_up_idx_normal[RATESIZE] = {
+	0x0c, 0x0d, 0x0d, 0x0f,
+	0x0d, 0x0e, 0x0f, 0x0f, 0x10, 0x12, 0x13, 0x14,
+	0x0f, 0x10, 0x10, 0x12, 0x12, 0x13, 0x14, 0x15,
+	0x11, 0x11, 0x12, 0x13, 0x13, 0x13, 0x14, 0x15
+};
+
+static const u8 retry_penalty_up_idx_cut_i[RATESIZE] = {
+	0x0c, 0x0d, 0x0d, 0x0f,
+	0x0d, 0x0e, 0x0f, 0x0f, 0x10, 0x12, 0x13, 0x14,
+	0x0b, 0x0b, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12,
+	0x11, 0x11, 0x12, 0x13, 0x13, 0x13, 0x14, 0x15
+};
+
+static const u8 rssi_threshold[RATESIZE] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0x24, 0x26, 0x2a,
+	0x18, 0x1a, 0x1d, 0x1f, 0x21, 0x27, 0x29, 0x2a,
+	0, 0, 0, 0x1f, 0x23, 0x28, 0x2a, 0x2c
+};
+
+static const u16 n_threshold_high[RATESIZE] = {
+	4, 4, 8, 16,
+	24, 36, 48, 72, 96, 144, 192, 216,
+	60, 80, 100, 160, 240, 400, 600, 800,
+	300, 320, 480, 720, 1000, 1200, 1600, 2000
+};
+
+static const u16 n_threshold_low[RATESIZE] = {
+	2, 2, 4, 8,
+	12, 18, 24, 36, 48, 72, 96, 108,
+	30, 40, 50, 80, 120, 200, 300, 400,
+	150, 160, 240, 360, 500, 600, 800, 1000
+};
+
+static const u8 trying_necessary[RATESIZE] = {
+	2, 2, 2, 2,
+	2, 2, 3, 3, 4, 4, 5, 7,
+	4, 4, 7, 10, 10, 12, 12, 18,
+	5, 7, 7, 8, 11, 18, 36, 60
+};
+
+static const u8 dropping_necessary[RATESIZE] = {
+	1, 1, 1, 1,
+	1, 2, 3, 4, 5, 6, 7, 8,
+	1, 2, 3, 4, 5, 6, 7, 8,
+	5, 6, 7, 8, 9, 10, 11, 12
+};
+
+static const u8 pending_for_rate_up_fail[5] = {2, 10, 24, 40, 60};
+
+static const u16 dynamic_tx_rpt_timing[6] = {
+	0x186a, 0x30d4, 0x493e, 0x61a8, 0x7a12, 0x927c /* 200ms-1200ms */
+};
+
+enum rtl8188e_tx_rpt_timing {
+	DEFAULT_TIMING = 0,
+	INCREASE_TIMING,
+	DECREASE_TIMING
+};
+
 static int rtl8188eu_identify_chip(struct rtl8xxxu_priv *priv)
 {
 	struct device *dev = &priv->udev->dev;
@@ -1239,6 +1365,479 @@  static s8 rtl8188e_cck_rssi(struct rtl8xxxu_priv *priv, u8 cck_agc_rpt)
 	return rx_pwr_all;
 }
 
+static void rtl8188e_set_tx_rpt_timing(struct rtl8xxxu_ra_info *ra, u8 timing)
+{
+	u8 idx;
+
+	for (idx = 0; idx < 5; idx++)
+		if (dynamic_tx_rpt_timing[idx] == ra->rpt_time)
+			break;
+
+	if (timing == DEFAULT_TIMING) {
+		idx = 0; /* 200ms */
+	} else if (timing == INCREASE_TIMING) {
+		if (idx < 5)
+			idx++;
+	} else if (timing == DECREASE_TIMING) {
+		if (idx > 0)
+			idx--;
+	}
+
+	ra->rpt_time = dynamic_tx_rpt_timing[idx];
+}
+
+static void rtl8188e_rate_down(struct rtl8xxxu_ra_info *ra)
+{
+	u8 rate_id = ra->pre_rate;
+	u8 lowest_rate = ra->lowest_rate;
+	u8 highest_rate = ra->highest_rate;
+	s8 i;
+
+	if (rate_id > highest_rate) {
+		rate_id = highest_rate;
+	} else if (ra->rate_sgi) {
+		ra->rate_sgi = 0;
+	} else if (rate_id > lowest_rate) {
+		if (rate_id > 0) {
+			for (i = rate_id - 1; i >= lowest_rate; i--) {
+				if (ra->ra_use_rate & BIT(i)) {
+					rate_id = i;
+					goto rate_down_finish;
+				}
+			}
+		}
+	} else if (rate_id <= lowest_rate) {
+		rate_id = lowest_rate;
+	}
+
+rate_down_finish:
+	if (ra->ra_waiting_counter == 1) {
+		ra->ra_waiting_counter++;
+		ra->ra_pending_counter++;
+	} else if (ra->ra_waiting_counter > 1) {
+		ra->ra_waiting_counter = 0;
+		ra->ra_pending_counter = 0;
+	}
+
+	if (ra->ra_pending_counter >= 4)
+		ra->ra_pending_counter = 4;
+
+	ra->ra_drop_after_down = 1;
+
+	ra->decision_rate = rate_id;
+
+	rtl8188e_set_tx_rpt_timing(ra, DECREASE_TIMING);
+}
+
+static void rtl8188e_rate_up(struct rtl8xxxu_ra_info *ra)
+{
+	u8 rate_id = ra->pre_rate;
+	u8 highest_rate = ra->highest_rate;
+	u8 i;
+
+	if (ra->ra_waiting_counter == 1) {
+		ra->ra_waiting_counter = 0;
+		ra->ra_pending_counter = 0;
+	} else if (ra->ra_waiting_counter > 1) {
+		ra->pre_rssi_sta_ra = ra->rssi_sta_ra;
+		goto rate_up_finish;
+	}
+
+	rtl8188e_set_tx_rpt_timing(ra, DEFAULT_TIMING);
+
+	if (rate_id < highest_rate) {
+		for (i = rate_id + 1; i <= highest_rate; i++) {
+			if (ra->ra_use_rate & BIT(i)) {
+				rate_id = i;
+				goto rate_up_finish;
+			}
+		}
+	} else if (rate_id == highest_rate) {
+		if (ra->sgi_enable && !ra->rate_sgi)
+			ra->rate_sgi = 1;
+		else if (!ra->sgi_enable)
+			ra->rate_sgi = 0;
+	} else { /* rate_id > ra->highest_rate */
+		rate_id = highest_rate;
+	}
+
+rate_up_finish:
+	if (ra->ra_waiting_counter == (4 + pending_for_rate_up_fail[ra->ra_pending_counter]))
+		ra->ra_waiting_counter = 0;
+	else
+		ra->ra_waiting_counter++;
+
+	ra->decision_rate = rate_id;
+}
+
+static void rtl8188e_reset_ra_counter(struct rtl8xxxu_ra_info *ra)
+{
+	u8 rate_id = ra->decision_rate;
+
+	ra->nsc_up = (n_threshold_high[rate_id] + n_threshold_low[rate_id]) >> 1;
+	ra->nsc_down = (n_threshold_high[rate_id] + n_threshold_low[rate_id]) >> 1;
+}
+
+static void rtl8188e_rate_decision(struct rtl8xxxu_ra_info *ra)
+{
+	struct rtl8xxxu_priv *priv = container_of(ra, struct rtl8xxxu_priv, ra_info);
+	const u8 *retry_penalty_idx_0;
+	const u8 *retry_penalty_idx_1;
+	const u8 *retry_penalty_up_idx;
+	u8 rate_id, penalty_id1, penalty_id2;
+	int i;
+
+	if (ra->total == 0)
+		return;
+
+	if (ra->ra_drop_after_down) {
+		ra->ra_drop_after_down--;
+
+		rtl8188e_reset_ra_counter(ra);
+
+		return;
+	}
+
+	if (priv->chip_cut == 8) { /* cut I */
+		retry_penalty_idx_0 = retry_penalty_idx_cut_i[0];
+		retry_penalty_idx_1 = retry_penalty_idx_cut_i[1];
+		retry_penalty_up_idx = retry_penalty_up_idx_cut_i;
+	} else {
+		retry_penalty_idx_0 = retry_penalty_idx_normal[0];
+		retry_penalty_idx_1 = retry_penalty_idx_normal[1];
+		retry_penalty_up_idx = retry_penalty_up_idx_normal;
+	}
+
+	if (ra->rssi_sta_ra < (ra->pre_rssi_sta_ra - 3) ||
+	    ra->rssi_sta_ra > (ra->pre_rssi_sta_ra + 3)) {
+		ra->pre_rssi_sta_ra = ra->rssi_sta_ra;
+		ra->ra_waiting_counter = 0;
+		ra->ra_pending_counter = 0;
+	}
+
+	/* Start RA decision */
+	if (ra->pre_rate > ra->highest_rate)
+		rate_id = ra->highest_rate;
+	else
+		rate_id = ra->pre_rate;
+
+	/* rate down */
+	if (ra->rssi_sta_ra > rssi_threshold[rate_id])
+		penalty_id1 = retry_penalty_idx_0[rate_id];
+	else
+		penalty_id1 = retry_penalty_idx_1[rate_id];
+
+	for (i = 0; i < 5; i++)
+		ra->nsc_down += ra->retry[i] * retry_penalty[penalty_id1][i];
+
+	if (ra->nsc_down > (ra->total * retry_penalty[penalty_id1][5]))
+		ra->nsc_down -= ra->total * retry_penalty[penalty_id1][5];
+	else
+		ra->nsc_down = 0;
+
+	/* rate up */
+	penalty_id2 = retry_penalty_up_idx[rate_id];
+
+	for (i = 0; i < 5; i++)
+		ra->nsc_up += ra->retry[i] * retry_penalty[penalty_id2][i];
+
+	if (ra->nsc_up > (ra->total * retry_penalty[penalty_id2][5]))
+		ra->nsc_up -= ra->total * retry_penalty[penalty_id2][5];
+	else
+		ra->nsc_up = 0;
+
+	if (ra->nsc_down < n_threshold_low[rate_id] ||
+	    ra->drop > dropping_necessary[rate_id]) {
+		rtl8188e_rate_down(ra);
+
+		rtl8xxxu_update_ra_report(&priv->ra_report, ra->decision_rate,
+					  ra->rate_sgi, priv->ra_report.txrate.bw);
+	} else if (ra->nsc_up > n_threshold_high[rate_id]) {
+		rtl8188e_rate_up(ra);
+
+		rtl8xxxu_update_ra_report(&priv->ra_report, ra->decision_rate,
+					  ra->rate_sgi, priv->ra_report.txrate.bw);
+	}
+
+	if (ra->decision_rate == ra->pre_rate)
+		ra->dynamic_tx_rpt_timing_counter++;
+	else
+		ra->dynamic_tx_rpt_timing_counter = 0;
+
+	if (ra->dynamic_tx_rpt_timing_counter >= 4) {
+		/* Rate didn't change 4 times, extend RPT timing */
+		rtl8188e_set_tx_rpt_timing(ra, INCREASE_TIMING);
+		ra->dynamic_tx_rpt_timing_counter = 0;
+	}
+
+	ra->pre_rate = ra->decision_rate;
+
+	rtl8188e_reset_ra_counter(ra);
+}
+
+static void rtl8188e_power_training_try_state(struct rtl8xxxu_ra_info *ra)
+{
+	ra->pt_try_state = 0;
+	switch (ra->pt_mode_ss) {
+	case 3:
+		if (ra->decision_rate >= DESC_RATE_MCS13)
+			ra->pt_try_state = 1;
+		break;
+	case 2:
+		if (ra->decision_rate >= DESC_RATE_MCS5)
+			ra->pt_try_state = 1;
+		break;
+	case 1:
+		if (ra->decision_rate >= DESC_RATE_48M)
+			ra->pt_try_state = 1;
+		break;
+	case 0:
+		if (ra->decision_rate >= DESC_RATE_11M)
+			ra->pt_try_state = 1;
+		break;
+	default:
+		ra->pt_try_state = 0;
+		break;
+	}
+
+	if (ra->rssi_sta_ra < 48) {
+		ra->pt_stage = 0;
+	} else if (ra->pt_try_state == 1) {
+		if ((ra->pt_stop_count >= 10) ||
+		    (ra->pt_pre_rssi > ra->rssi_sta_ra + 5) ||
+		    (ra->pt_pre_rssi < ra->rssi_sta_ra - 5) ||
+		    (ra->decision_rate != ra->pt_pre_rate)) {
+			if (ra->pt_stage == 0)
+				ra->pt_stage = 1;
+			else if (ra->pt_stage == 1)
+				ra->pt_stage = 3;
+			else
+				ra->pt_stage = 5;
+
+			ra->pt_pre_rssi = ra->rssi_sta_ra;
+			ra->pt_stop_count = 0;
+		} else {
+			ra->ra_stage = 0;
+			ra->pt_stop_count++;
+		}
+	} else {
+		ra->pt_stage = 0;
+		ra->ra_stage = 0;
+	}
+
+	ra->pt_pre_rate = ra->decision_rate;
+
+	/* TODO: implement the "false alarm" statistics for this */
+	/* Disable power training when noisy environment */
+	/* if (p_dm_odm->is_disable_power_training) { */
+	if (1) {
+		ra->pt_stage = 0;
+		ra->ra_stage = 0;
+		ra->pt_stop_count = 0;
+	}
+}
+
+static void rtl8188e_power_training_decision(struct rtl8xxxu_ra_info *ra)
+{
+	u8 temp_stage;
+	u32 numsc;
+	u32 num_total;
+	u8 stage_id;
+	u8 j;
+
+	numsc = 0;
+	num_total = ra->total * pt_penalty[5];
+	for (j = 0; j <= 4; j++) {
+		numsc += ra->retry[j] * pt_penalty[j];
+
+		if (numsc > num_total)
+			break;
+	}
+
+	j = j >> 1;
+	temp_stage = (ra->pt_stage + 1) >> 1;
+	if (temp_stage > j)
+		stage_id = temp_stage - j;
+	else
+		stage_id = 0;
+
+	ra->pt_smooth_factor = (ra->pt_smooth_factor >> 1) +
+			       (ra->pt_smooth_factor >> 2) +
+			       stage_id * 16 + 2;
+	if (ra->pt_smooth_factor > 192)
+		ra->pt_smooth_factor = 192;
+	stage_id = ra->pt_smooth_factor >> 6;
+	temp_stage = stage_id * 2;
+	if (temp_stage != 0)
+		temp_stage--;
+	if (ra->drop > 3)
+		temp_stage = 0;
+	ra->pt_stage = temp_stage;
+}
+
+void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
+{
+	u32 *_rx_desc = (u32 *)(skb->data - sizeof(struct rtl8xxxu_rxdesc16));
+	struct rtl8xxxu_rxdesc16 *rx_desc = (struct rtl8xxxu_rxdesc16 *)_rx_desc;
+	struct device *dev = &priv->udev->dev;
+	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+
+	u32 tx_rpt_len = rx_desc->pktlen & 0x3ff;
+	u32 items = tx_rpt_len / TX_RPT2_ITEM_SIZE;
+	u64 macid_valid = ((u64)_rx_desc[5] << 32) | _rx_desc[4];
+	u32 macid;
+	u8 *rpt = skb->data;
+	bool valid;
+	u16 min_rpt_time = 0x927c;
+
+	dev_dbg(dev, "%s: len: %d items: %d\n", __func__, tx_rpt_len, items);
+
+	for (macid = 0; macid < items; macid++) {
+		valid = false;
+
+		if (macid < 64)
+			valid = macid_valid & BIT(macid);
+
+		if (valid) {
+			ra->retry[0] = le16_to_cpu(*(__le16 *)rpt);
+			ra->retry[1] = rpt[2];
+			ra->retry[2] = rpt[3];
+			ra->retry[3] = rpt[4];
+			ra->retry[4] = rpt[5];
+			ra->drop = rpt[6];
+			ra->total = ra->retry[0] + ra->retry[1] + ra->retry[2] +
+				    ra->retry[3] + ra->retry[4] + ra->drop;
+
+			if (ra->total > 0) {
+				if (ra->ra_stage < 5)
+					rtl8188e_rate_decision(ra);
+				else if (ra->ra_stage == 5)
+					rtl8188e_power_training_try_state(ra);
+				else /* ra->ra_stage == 6 */
+					rtl8188e_power_training_decision(ra);
+
+				if (ra->ra_stage <= 5)
+					ra->ra_stage++;
+				else
+					ra->ra_stage = 0;
+			}
+		} else if (macid == 0) {
+			dev_warn(dev, "%s: TX report item 0 not valid\n", __func__);
+		}
+
+		dev_dbg(dev, "%s:  valid: %d retry: %d %d %d %d %d drop: %d\n",
+			__func__, valid,
+			ra->retry[0], ra->retry[1], ra->retry[2],
+			ra->retry[3], ra->retry[4], ra->drop);
+
+		if (min_rpt_time > ra->rpt_time)
+			min_rpt_time = ra->rpt_time;
+
+		rpt += TX_RPT2_ITEM_SIZE;
+
+		/*
+		 * We only use macid 0, so only the first item is relevant.
+		 * AP mode will use more of them if it's ever implemented.
+		 */
+		break;
+	}
+
+	if (min_rpt_time != ra->pre_min_rpt_time) {
+		rtl8xxxu_write16(priv, REG_TX_REPORT_TIME, min_rpt_time);
+		ra->pre_min_rpt_time = min_rpt_time;
+	}
+}
+
+static void rtl8188e_arfb_refresh(struct rtl8xxxu_ra_info *ra)
+{
+	s8 i;
+
+	ra->ra_use_rate = ra->rate_mask;
+
+	/* Highest rate */
+	if (ra->ra_use_rate) {
+		for (i = RATESIZE; i >= 0; i--) {
+			if (ra->ra_use_rate & BIT(i)) {
+				ra->highest_rate = i;
+				break;
+			}
+		}
+	} else {
+		ra->highest_rate = 0;
+	}
+
+	/* Lowest rate */
+	if (ra->ra_use_rate) {
+		for (i = 0; i < RATESIZE; i++) {
+			if (ra->ra_use_rate & BIT(i)) {
+				ra->lowest_rate = i;
+				break;
+			}
+		}
+	} else {
+		ra->lowest_rate = 0;
+	}
+
+	if (ra->highest_rate > DESC_RATE_MCS7)
+		ra->pt_mode_ss = 3;
+	else if (ra->highest_rate > DESC_RATE_54M)
+		ra->pt_mode_ss = 2;
+	else if (ra->highest_rate > DESC_RATE_11M)
+		ra->pt_mode_ss = 1;
+	else
+		ra->pt_mode_ss = 0;
+}
+
+static void
+rtl8188e_update_rate_mask(struct rtl8xxxu_priv *priv,
+			  u32 ramask, u8 rateid, int sgi, int txbw_40mhz)
+{
+	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+
+	ra->rate_id = rateid;
+	ra->rate_mask = ramask;
+	ra->sgi_enable = sgi;
+
+	rtl8188e_arfb_refresh(ra);
+}
+
+void rtl8188e_ra_info_init_all(struct rtl8xxxu_ra_info *ra)
+{
+	ra->decision_rate = DESC_RATE_MCS7;
+	ra->pre_rate = DESC_RATE_MCS7;
+	ra->highest_rate = DESC_RATE_MCS7;
+	ra->lowest_rate = 0;
+	ra->rate_id = 0;
+	ra->rate_mask = 0xfffff;
+	ra->rssi_sta_ra = 0;
+	ra->pre_rssi_sta_ra = 0;
+	ra->sgi_enable = 0;
+	ra->ra_use_rate = 0xfffff;
+	ra->nsc_down = (n_threshold_high[DESC_RATE_MCS7] + n_threshold_low[DESC_RATE_MCS7]) / 2;
+	ra->nsc_up = (n_threshold_high[DESC_RATE_MCS7] + n_threshold_low[DESC_RATE_MCS7]) / 2;
+	ra->rate_sgi = 0;
+	ra->rpt_time = 0x927c;
+	ra->drop = 0;
+	ra->retry[0] = 0;
+	ra->retry[1] = 0;
+	ra->retry[2] = 0;
+	ra->retry[3] = 0;
+	ra->retry[4] = 0;
+	ra->total = 0;
+	ra->ra_waiting_counter = 0;
+	ra->ra_pending_counter = 0;
+	ra->ra_drop_after_down = 0;
+
+	ra->pt_try_state = 0;
+	ra->pt_stage = 5;
+	ra->pt_smooth_factor = 192;
+	ra->pt_stop_count = 0;
+	ra->pt_pre_rate = 0;
+	ra->pt_pre_rssi = 0;
+	ra->pt_mode_ss = 0;
+	ra->ra_stage = 0;
+}
+
 struct rtl8xxxu_fileops rtl8188eu_fops = {
 	.identify_chip = rtl8188eu_identify_chip,
 	.parse_efuse = rtl8188eu_parse_efuse,
@@ -1258,7 +1857,7 @@  struct rtl8xxxu_fileops rtl8188eu_fops = {
 	.disable_rf = rtl8188e_disable_rf,
 	.usb_quirks = rtl8188e_usb_quirks,
 	.set_tx_power = rtl8188f_set_tx_power,
-	.update_rate_mask = rtl8xxxu_gen2_update_rate_mask,
+	.update_rate_mask = rtl8188e_update_rate_mask,
 	.report_connect = rtl8xxxu_gen2_report_connect,
 	.fill_txdesc = rtl8xxxu_fill_txdesc_v3,
 	.set_crystal_cap = rtl8188f_set_crystal_cap,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index daf70a6bc380..5b969759859c 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -3982,7 +3982,25 @@  static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 		 * Enable TX report and TX report timer for 8723bu/8188eu/...
 		 */
 		if (fops->has_tx_report) {
+			/*
+			 * The RTL8188EU has two types of TX reports:
+			 * rpt_sel=1:
+			 *   One report for one frame. We can use this for frames
+			 *   with IEEE80211_TX_CTL_REQ_TX_STATUS.
+			 * rpt_sel=2:
+			 *   One report for many frames transmitted over a period
+			 *   of time. (This is what REG_TX_REPORT_TIME is for.) The
+			 *   report includes the number of frames transmitted
+			 *   successfully, and the number of unsuccessful
+			 *   transmissions. We use this for software rate control.
+			 *
+			 * Bit 0 of REG_TX_REPORT_CTRL is required for both types.
+			 * Bit 1 (TX_REPORT_CTRL_TIMER_ENABLE) is required for
+			 * type 2.
+			 */
 			val8 = rtl8xxxu_read8(priv, REG_TX_REPORT_CTRL);
+			if (priv->rtl_chip == RTL8188E)
+				val8 |= BIT(0);
 			val8 |= TX_REPORT_CTRL_TIMER_ENABLE;
 			rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL, val8);
 			/* Set MAX RPT MACID */
@@ -4273,6 +4291,9 @@  static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 		priv->cfo_tracking.crystal_cap = priv->default_crystal_cap;
 	}
 
+	if (priv->rtl_chip == RTL8188E)
+		rtl8188e_ra_info_init_all(&priv->ra_info);
+
 exit:
 	return ret;
 }
@@ -4636,8 +4657,8 @@  static void rtl8xxxu_set_aifs(struct rtl8xxxu_priv *priv, u8 slot_time)
 	}
 }
 
-static void rtl8xxxu_update_ra_report(struct rtl8xxxu_ra_report *rarpt,
-				      u8 rate, u8 sgi, u8 bw)
+void rtl8xxxu_update_ra_report(struct rtl8xxxu_ra_report *rarpt,
+			       u8 rate, u8 sgi, u8 bw)
 {
 	u8 mcs, nss;
 
@@ -5121,6 +5142,7 @@  rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
 	struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info);
 	struct rtl8xxxu_priv *priv = hw->priv;
 	struct device *dev = &priv->udev->dev;
+	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
 	u8 *qc = ieee80211_get_qos_ctl(hdr);
 	u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
 	u32 rate;
@@ -5136,9 +5158,10 @@  rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
 	seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
 
 	if (ieee80211_is_data(hdr->frame_control)) {
-		rate = DESC_RATE_MCS7; /* TODO: software rate control */
+		rate = ra->decision_rate;
 		tx_desc->txdw5 = cpu_to_le32(rate);
 		tx_desc->txdw4 |= cpu_to_le32(TXDESC32_USE_DRIVER_RATE);
+		tx_desc->txdw4 |= le32_encode_bits(ra->pt_stage, TXDESC32_PT_STAGE_MASK);
 		/* Data/RTS rate FB limit */
 		tx_desc->txdw5 |= cpu_to_le32(0x0001ff00);
 	}
@@ -5177,7 +5200,7 @@  rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
 	if (short_preamble)
 		tx_desc->txdw4 |= cpu_to_le32(TXDESC32_SHORT_PREAMBLE);
 
-	if (sgi)
+	if (sgi && ra->rate_sgi)
 		tx_desc->txdw5 |= cpu_to_le32(TXDESC32_SHORT_GI);
 
 	/*
@@ -5768,6 +5791,44 @@  static void rtl8723bu_handle_c2h(struct rtl8xxxu_priv *priv,
 	schedule_work(&priv->c2hcmd_work);
 }
 
+static void rtl8188e_c2hcmd_callback(struct work_struct *work)
+{
+	struct rtl8xxxu_priv *priv = container_of(work, struct rtl8xxxu_priv, c2hcmd_work);
+	struct device *dev = &priv->udev->dev;
+	struct sk_buff *skb = NULL;
+	struct rtl8xxxu_rxdesc16 *rx_desc;
+
+	while (!skb_queue_empty(&priv->c2hcmd_queue)) {
+		skb = skb_dequeue(&priv->c2hcmd_queue);
+
+		rx_desc = (struct rtl8xxxu_rxdesc16 *)(skb->data - sizeof(struct rtl8xxxu_rxdesc16));
+
+		switch (rx_desc->rpt_sel) {
+		case 1:
+			dev_dbg(dev, "C2H TX report type 1\n");
+
+			break;
+		case 2:
+			dev_dbg(dev, "C2H TX report type 2\n");
+
+			rtl8188e_handle_ra_tx_report2(priv, skb);
+
+			break;
+		case 3:
+			dev_dbg(dev, "C2H USB interrupt report\n");
+
+			break;
+		default:
+			dev_warn(dev, "%s: rpt_sel should not be %d\n",
+				 __func__, rx_desc->rpt_sel);
+
+			break;
+		}
+
+		dev_kfree_skb(skb);
+	}
+}
+
 int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
 {
 	struct ieee80211_hw *hw = priv->hw;
@@ -5823,38 +5884,46 @@  int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
 
 		skb_pull(skb, sizeof(struct rtl8xxxu_rxdesc16));
 
-		phy_stats = (struct rtl8723au_phy_stats *)skb->data;
+		if (rx_desc->rpt_sel) {
+			skb_queue_tail(&priv->c2hcmd_queue, skb);
+			schedule_work(&priv->c2hcmd_work);
+		} else {
+			phy_stats = (struct rtl8723au_phy_stats *)skb->data;
 
-		skb_pull(skb, drvinfo_sz + desc_shift);
+			skb_pull(skb, drvinfo_sz + desc_shift);
 
-		skb_trim(skb, pkt_len);
+			skb_trim(skb, pkt_len);
 
-		if (rx_desc->phy_stats)
-			rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
-						   rx_desc->rxmcs, (struct ieee80211_hdr *)skb->data,
-						   rx_desc->crc32 || rx_desc->icverr);
+			if (rx_desc->phy_stats)
+				rtl8xxxu_rx_parse_phystats(
+					priv, rx_status, phy_stats,
+					rx_desc->rxmcs,
+					(struct ieee80211_hdr *)skb->data,
+					rx_desc->crc32 || rx_desc->icverr
+				);
 
-		rx_status->mactime = rx_desc->tsfl;
-		rx_status->flag |= RX_FLAG_MACTIME_START;
+			rx_status->mactime = rx_desc->tsfl;
+			rx_status->flag |= RX_FLAG_MACTIME_START;
 
-		if (!rx_desc->swdec)
-			rx_status->flag |= RX_FLAG_DECRYPTED;
-		if (rx_desc->crc32)
-			rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
-		if (rx_desc->bw)
-			rx_status->bw = RATE_INFO_BW_40;
+			if (!rx_desc->swdec)
+				rx_status->flag |= RX_FLAG_DECRYPTED;
+			if (rx_desc->crc32)
+				rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+			if (rx_desc->bw)
+				rx_status->bw = RATE_INFO_BW_40;
 
-		if (rx_desc->rxht) {
-			rx_status->encoding = RX_ENC_HT;
-			rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
-		} else {
-			rx_status->rate_idx = rx_desc->rxmcs;
-		}
+			if (rx_desc->rxht) {
+				rx_status->encoding = RX_ENC_HT;
+				rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
+			} else {
+				rx_status->rate_idx = rx_desc->rxmcs;
+			}
 
-		rx_status->freq = hw->conf.chandef.chan->center_freq;
-		rx_status->band = hw->conf.chandef.chan->band;
+			rx_status->freq = hw->conf.chandef.chan->center_freq;
+			rx_status->band = hw->conf.chandef.chan->band;
 
-		ieee80211_rx_irqsafe(hw, skb);
+			ieee80211_rx_irqsafe(hw, skb);
+		}
 
 		skb = next_skb;
 		if (skb)
@@ -6941,7 +7010,6 @@  static int rtl8xxxu_probe(struct usb_interface *interface,
 	spin_lock_init(&priv->rx_urb_lock);
 	INIT_WORK(&priv->rx_urb_wq, rtl8xxxu_rx_urb_work);
 	INIT_DELAYED_WORK(&priv->ra_watchdog, rtl8xxxu_watchdog_callback);
-	INIT_WORK(&priv->c2hcmd_work, rtl8xxxu_c2hcmd_callback);
 	skb_queue_head_init(&priv->c2hcmd_queue);
 
 	usb_set_intfdata(interface, hw);
@@ -6959,6 +7027,11 @@  static int rtl8xxxu_probe(struct usb_interface *interface,
 	hw->wiphy->available_antennas_tx = BIT(priv->tx_paths) - 1;
 	hw->wiphy->available_antennas_rx = BIT(priv->rx_paths) - 1;
 
+	if (priv->rtl_chip == RTL8188E)
+		INIT_WORK(&priv->c2hcmd_work, rtl8188e_c2hcmd_callback);
+	else
+		INIT_WORK(&priv->c2hcmd_work, rtl8xxxu_c2hcmd_callback);
+
 	ret = rtl8xxxu_read_efuse(priv);
 	if (ret) {
 		dev_err(&udev->dev, "Fatal - failed to read EFuse\n");