struct rtl8xxxu_fileops;
 
+/*mlme related.*/
+enum wireless_mode {
+       WIRELESS_MODE_UNKNOWN = 0,
+       /* Sub-Element */
+       WIRELESS_MODE_B = BIT(0),
+       WIRELESS_MODE_G = BIT(1),
+       WIRELESS_MODE_A = BIT(2),
+       WIRELESS_MODE_N_24G = BIT(3),
+       WIRELESS_MODE_N_5G = BIT(4),
+       WIRELESS_AUTO = BIT(5),
+       WIRELESS_MODE_AC = BIT(6),
+       WIRELESS_MODE_MAX = 0x7F,
+};
+
+/* from rtlwifi/wifi.h */
+enum ratr_table_mode_new {
+       RATEID_IDX_BGN_40M_2SS = 0,
+       RATEID_IDX_BGN_40M_1SS = 1,
+       RATEID_IDX_BGN_20M_2SS_BN = 2,
+       RATEID_IDX_BGN_20M_1SS_BN = 3,
+       RATEID_IDX_GN_N2SS = 4,
+       RATEID_IDX_GN_N1SS = 5,
+       RATEID_IDX_BG = 6,
+       RATEID_IDX_G = 7,
+       RATEID_IDX_B = 8,
+       RATEID_IDX_VHT_2SS = 9,
+       RATEID_IDX_VHT_1SS = 10,
+       RATEID_IDX_MIX1 = 11,
+       RATEID_IDX_MIX2 = 12,
+       RATEID_IDX_VHT_3SS = 13,
+       RATEID_IDX_BGN_3SS = 14,
+};
+
+#define RTL8XXXU_RATR_STA_INIT 0
+#define RTL8XXXU_RATR_STA_HIGH 1
+#define RTL8XXXU_RATR_STA_MID  2
+#define RTL8XXXU_RATR_STA_LOW  3
+
+#define RTL8XXXU_NOISE_FLOOR_MIN       -100
+#define RTL8XXXU_SNR_THRESH_HIGH       50
+#define RTL8XXXU_SNR_THRESH_LOW        20
+
 struct rtl8xxxu_priv {
        struct ieee80211_hw *hw;
        struct usb_device *udev;
        u8 pi_enabled:1;
        u8 no_pape:1;
        u8 int_buf[USB_INTR_CONTENT_LENGTH];
+       u8 rssi_level;
+       /*
+        * Only one virtual interface permitted because only STA mode
+        * is supported and no iface_combinations are provided.
+        */
+       struct ieee80211_vif *vif;
+       struct delayed_work ra_watchdog;
 };
 
 struct rtl8xxxu_rx_urb {
        void (*set_tx_power) (struct rtl8xxxu_priv *priv, int channel,
                              bool ht40);
        void (*update_rate_mask) (struct rtl8xxxu_priv *priv,
-                                 u32 ramask, int sgi);
+                                 u32 ramask, u8 rateid, int sgi);
        void (*report_connect) (struct rtl8xxxu_priv *priv,
                                u8 macid, bool connect);
        void (*fill_txdesc) (struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
 void rtl8xxxu_gen1_usb_quirks(struct rtl8xxxu_priv *priv);
 void rtl8xxxu_gen2_usb_quirks(struct rtl8xxxu_priv *priv);
 void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv,
-                              u32 ramask, int sgi);
+                              u32 ramask, u8 rateid, int sgi);
 void rtl8xxxu_gen2_update_rate_mask(struct rtl8xxxu_priv *priv,
-                                   u32 ramask, int sgi);
+                                   u32 ramask, u8 rateid, int sgi);
 void rtl8xxxu_gen1_report_connect(struct rtl8xxxu_priv *priv,
                                  u8 macid, bool connect);
 void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv,
 
        rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
 }
 
-void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv, u32 ramask, int sgi)
+void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv,
+                              u32 ramask, u8 rateid, int sgi)
 {
        struct h2c_cmd h2c;
 
 }
 
 void rtl8xxxu_gen2_update_rate_mask(struct rtl8xxxu_priv *priv,
-                                   u32 ramask, int sgi)
+                                   u32 ramask, u8 rateid, int sgi)
 {
        struct h2c_cmd h2c;
        u8 bw = 0;
        h2c.b_macid_cfg.ramask3 = (ramask >> 24) & 0xff;
 
        h2c.ramask.arg = 0x80;
-       h2c.b_macid_cfg.data1 = 0;
+       h2c.b_macid_cfg.data1 = rateid;
        if (sgi)
                h2c.b_macid_cfg.data1 |= BIT(7);
 
        rtl8xxxu_write8(priv, REG_INIRTS_RATE_SEL, rate_idx);
 }
 
+static u16
+rtl8xxxu_wireless_mode(struct ieee80211_hw *hw, struct ieee80211_sta *sta)
+{
+       u16 network_type = WIRELESS_MODE_UNKNOWN;
+       u32 rate_mask;
+
+       rate_mask = (sta->supp_rates[0] & 0xfff) |
+                   (sta->ht_cap.mcs.rx_mask[0] << 12) |
+                   (sta->ht_cap.mcs.rx_mask[0] << 20);
+
+       if (hw->conf.chandef.chan->band == NL80211_BAND_5GHZ) {
+               if (sta->vht_cap.vht_supported)
+                       network_type = WIRELESS_MODE_AC;
+               else if (sta->ht_cap.ht_supported)
+                       network_type = WIRELESS_MODE_N_5G;
+
+               network_type |= WIRELESS_MODE_A;
+       } else {
+               if (sta->vht_cap.vht_supported)
+                       network_type = WIRELESS_MODE_AC;
+               else if (sta->ht_cap.ht_supported)
+                       network_type = WIRELESS_MODE_N_24G;
+
+               if (sta->supp_rates[0] <= 0xf)
+                       network_type |= WIRELESS_MODE_B;
+               else if (sta->supp_rates[0] & 0xf)
+                       network_type |= (WIRELESS_MODE_B | WIRELESS_MODE_G);
+               else
+                       network_type |= WIRELESS_MODE_G;
+       }
+
+       return network_type;
+}
+
 static void
 rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                          struct ieee80211_bss_conf *bss_conf, u32 changed)
                                sgi = 1;
                        rcu_read_unlock();
 
-                       priv->fops->update_rate_mask(priv, ramask, sgi);
+                       priv->vif = vif;
+                       priv->rssi_level = RTL8XXXU_RATR_STA_INIT;
+
+                       priv->fops->update_rate_mask(priv, ramask, 0, sgi);
 
                        rtl8xxxu_write8(priv, REG_BCN_MAX_ERR, 0xff);
 
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
+               if (!priv->vif)
+                       priv->vif = vif;
+               else
+                       return -EOPNOTSUPP;
                rtl8xxxu_stop_tx_beacon(priv);
 
                val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL);
        struct rtl8xxxu_priv *priv = hw->priv;
 
        dev_dbg(&priv->udev->dev, "%s\n", __func__);
+
+       if (priv->vif)
+               priv->vif = NULL;
 }
 
 static int rtl8xxxu_config(struct ieee80211_hw *hw, u32 changed)
        return 0;
 }
 
+static u8 rtl8xxxu_signal_to_snr(int signal)
+{
+       if (signal < RTL8XXXU_NOISE_FLOOR_MIN)
+               signal = RTL8XXXU_NOISE_FLOOR_MIN;
+       else if (signal > 0)
+               signal = 0;
+       return (u8)(signal - RTL8XXXU_NOISE_FLOOR_MIN);
+}
+
+static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv,
+                                      int signal, struct ieee80211_sta *sta)
+{
+       struct ieee80211_hw *hw = priv->hw;
+       u16 wireless_mode;
+       u8 rssi_level, ratr_idx;
+       u8 txbw_40mhz;
+       u8 snr, snr_thresh_high, snr_thresh_low;
+       u8 go_up_gap = 5;
+
+       rssi_level = priv->rssi_level;
+       snr = rtl8xxxu_signal_to_snr(signal);
+       snr_thresh_high = RTL8XXXU_SNR_THRESH_HIGH;
+       snr_thresh_low = RTL8XXXU_SNR_THRESH_LOW;
+       txbw_40mhz = (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40) ? 1 : 0;
+
+       switch (rssi_level) {
+       case RTL8XXXU_RATR_STA_MID:
+               snr_thresh_high += go_up_gap;
+               break;
+       case RTL8XXXU_RATR_STA_LOW:
+               snr_thresh_high += go_up_gap;
+               snr_thresh_low += go_up_gap;
+               break;
+       default:
+               break;
+       }
+
+       if (snr > snr_thresh_high)
+               rssi_level = RTL8XXXU_RATR_STA_HIGH;
+       else if (snr > snr_thresh_low)
+               rssi_level = RTL8XXXU_RATR_STA_MID;
+       else
+               rssi_level = RTL8XXXU_RATR_STA_LOW;
+
+       if (rssi_level != priv->rssi_level) {
+               int sgi = 0;
+               u32 rate_bitmap = 0;
+
+               rcu_read_lock();
+               rate_bitmap = (sta->supp_rates[0] & 0xfff) |
+                               (sta->ht_cap.mcs.rx_mask[0] << 12) |
+                               (sta->ht_cap.mcs.rx_mask[1] << 20);
+               if (sta->ht_cap.cap &
+                   (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))
+                       sgi = 1;
+               rcu_read_unlock();
+
+               wireless_mode = rtl8xxxu_wireless_mode(hw, sta);
+               switch (wireless_mode) {
+               case WIRELESS_MODE_B:
+                       ratr_idx = RATEID_IDX_B;
+                       if (rate_bitmap & 0x0000000c)
+                               rate_bitmap &= 0x0000000d;
+                       else
+                               rate_bitmap &= 0x0000000f;
+                       break;
+               case WIRELESS_MODE_A:
+               case WIRELESS_MODE_G:
+                       ratr_idx = RATEID_IDX_G;
+                       if (rssi_level == RTL8XXXU_RATR_STA_HIGH)
+                               rate_bitmap &= 0x00000f00;
+                       else
+                               rate_bitmap &= 0x00000ff0;
+                       break;
+               case (WIRELESS_MODE_B | WIRELESS_MODE_G):
+                       ratr_idx = RATEID_IDX_BG;
+                       if (rssi_level == RTL8XXXU_RATR_STA_HIGH)
+                               rate_bitmap &= 0x00000f00;
+                       else if (rssi_level == RTL8XXXU_RATR_STA_MID)
+                               rate_bitmap &= 0x00000ff0;
+                       else
+                               rate_bitmap &= 0x00000ff5;
+                       break;
+               case WIRELESS_MODE_N_24G:
+               case WIRELESS_MODE_N_5G:
+               case (WIRELESS_MODE_G | WIRELESS_MODE_N_24G):
+               case (WIRELESS_MODE_A | WIRELESS_MODE_N_5G):
+                       if (priv->tx_paths == 2 && priv->rx_paths == 2)
+                               ratr_idx = RATEID_IDX_GN_N2SS;
+                       else
+                               ratr_idx = RATEID_IDX_GN_N1SS;
+                       break;
+               case (WIRELESS_MODE_B | WIRELESS_MODE_G | WIRELESS_MODE_N_24G):
+               case (WIRELESS_MODE_B | WIRELESS_MODE_N_24G):
+                       if (txbw_40mhz) {
+                               if (priv->tx_paths == 2 && priv->rx_paths == 2)
+                                       ratr_idx = RATEID_IDX_BGN_40M_2SS;
+                               else
+                                       ratr_idx = RATEID_IDX_BGN_40M_1SS;
+                       } else {
+                               if (priv->tx_paths == 2 && priv->rx_paths == 2)
+                                       ratr_idx = RATEID_IDX_BGN_20M_2SS_BN;
+                               else
+                                       ratr_idx = RATEID_IDX_BGN_20M_1SS_BN;
+                       }
+
+                       if (priv->tx_paths == 2 && priv->rx_paths == 2) {
+                               if (rssi_level == RTL8XXXU_RATR_STA_HIGH) {
+                                       rate_bitmap &= 0x0f8f0000;
+                               } else if (rssi_level == RTL8XXXU_RATR_STA_MID) {
+                                       rate_bitmap &= 0x0f8ff000;
+                               } else {
+                                       if (txbw_40mhz)
+                                               rate_bitmap &= 0x0f8ff015;
+                                       else
+                                               rate_bitmap &= 0x0f8ff005;
+                               }
+                       } else {
+                               if (rssi_level == RTL8XXXU_RATR_STA_HIGH) {
+                                       rate_bitmap &= 0x000f0000;
+                               } else if (rssi_level == RTL8XXXU_RATR_STA_MID) {
+                                       rate_bitmap &= 0x000ff000;
+                               } else {
+                                       if (txbw_40mhz)
+                                               rate_bitmap &= 0x000ff015;
+                                       else
+                                               rate_bitmap &= 0x000ff005;
+                               }
+                       }
+                       break;
+               default:
+                       ratr_idx = RATEID_IDX_BGN_40M_2SS;
+                       rate_bitmap &= 0x0fffffff;
+                       break;
+               }
+
+               priv->rssi_level = rssi_level;
+               priv->fops->update_rate_mask(priv, rate_bitmap, ratr_idx, sgi);
+       }
+}
+
+static void rtl8xxxu_watchdog_callback(struct work_struct *work)
+{
+       struct ieee80211_vif *vif;
+       struct rtl8xxxu_priv *priv;
+
+       priv = container_of(work, struct rtl8xxxu_priv, ra_watchdog.work);
+       vif = priv->vif;
+
+       if (vif && vif->type == NL80211_IFTYPE_STATION) {
+               int signal;
+               struct ieee80211_sta *sta;
+
+               rcu_read_lock();
+               sta = ieee80211_find_sta(vif, vif->bss_conf.bssid);
+               if (!sta) {
+                       struct device *dev = &priv->udev->dev;
+
+                       dev_dbg(dev, "%s: no sta found\n", __func__);
+                       rcu_read_unlock();
+                       goto out;
+               }
+               rcu_read_unlock();
+
+               signal = ieee80211_ave_rssi(vif);
+               rtl8xxxu_refresh_rate_mask(priv, signal, sta);
+       }
+
+out:
+       schedule_delayed_work(&priv->ra_watchdog, 2 * HZ);
+}
+
 static int rtl8xxxu_start(struct ieee80211_hw *hw)
 {
        struct rtl8xxxu_priv *priv = hw->priv;
 
                ret = rtl8xxxu_submit_rx_urb(priv, rx_urb);
        }
+
+       schedule_delayed_work(&priv->ra_watchdog, 2 * HZ);
 exit:
        /*
         * Accept all data and mgmt frames
        if (priv->usb_interrupts)
                rtl8xxxu_write32(priv, REG_USB_HIMR, 0);
 
+       cancel_delayed_work_sync(&priv->ra_watchdog);
+
        rtl8xxxu_free_rx_resources(priv);
        rtl8xxxu_free_tx_resources(priv);
 }
        INIT_LIST_HEAD(&priv->rx_urb_pending_list);
        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);
 
        usb_set_intfdata(interface, hw);