#include "reg.h"
 #include "sec.h"
 #include "debug.h"
+#include "util.h"
 
 static void rtw_fw_c2h_cmd_handle_ext(struct rtw_dev *rtwdev,
                                      struct sk_buff *skb)
        }
 }
 
+struct rtw_fw_iter_ra_data {
+       struct rtw_dev *rtwdev;
+       u8 *payload;
+};
+
+static void rtw_fw_ra_report_iter(void *data, struct ieee80211_sta *sta)
+{
+       struct rtw_fw_iter_ra_data *ra_data = data;
+       struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+       u8 mac_id, rate, sgi, bw;
+       u8 mcs, nss;
+       u32 bit_rate;
+
+       mac_id = GET_RA_REPORT_MACID(ra_data->payload);
+       if (si->mac_id != mac_id)
+               return;
+
+       si->ra_report.txrate.flags = 0;
+
+       rate = GET_RA_REPORT_RATE(ra_data->payload);
+       sgi = GET_RA_REPORT_SGI(ra_data->payload);
+       bw = GET_RA_REPORT_BW(ra_data->payload);
+
+       if (rate < DESC_RATEMCS0) {
+               si->ra_report.txrate.legacy = rtw_desc_to_bitrate(rate);
+               goto legacy;
+       }
+
+       rtw_desc_to_mcsrate(rate, &mcs, &nss);
+       if (rate >= DESC_RATEVHT1SS_MCS0)
+               si->ra_report.txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
+       else if (rate >= DESC_RATEMCS0)
+               si->ra_report.txrate.flags |= RATE_INFO_FLAGS_MCS;
+
+       if (rate >= DESC_RATEMCS0) {
+               si->ra_report.txrate.mcs = mcs;
+               si->ra_report.txrate.nss = nss;
+       }
+
+       if (sgi)
+               si->ra_report.txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+       if (bw == RTW_CHANNEL_WIDTH_80)
+               si->ra_report.txrate.bw = RATE_INFO_BW_80;
+       else if (bw == RTW_CHANNEL_WIDTH_40)
+               si->ra_report.txrate.bw = RATE_INFO_BW_40;
+       else
+               si->ra_report.txrate.bw = RATE_INFO_BW_20;
+
+legacy:
+       bit_rate = cfg80211_calculate_bitrate(&si->ra_report.txrate);
+
+       si->ra_report.desc_rate = rate;
+       si->ra_report.bit_rate = bit_rate;
+}
+
+static void rtw_fw_ra_report_handle(struct rtw_dev *rtwdev, u8 *payload,
+                                   u8 length)
+{
+       struct rtw_fw_iter_ra_data ra_data;
+
+       if (WARN(length < 7, "invalid ra report c2h length\n"))
+               return;
+
+       ra_data.rtwdev = rtwdev;
+       ra_data.payload = payload;
+       rtw_iterate_stas_atomic(rtwdev, rtw_fw_ra_report_iter, &ra_data);
+}
+
 void rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb)
 {
        struct rtw_c2h_cmd *c2h;
        case C2H_HALMAC:
                rtw_fw_c2h_cmd_handle_ext(rtwdev, skb);
                break;
+       case C2H_RA_RPT:
+               rtw_fw_ra_report_handle(rtwdev, c2h->payload, len);
+               break;
        default:
                break;
        }
 
 enum rtw_c2h_cmd_id {
        C2H_BT_INFO = 0x09,
        C2H_BT_MP_INFO = 0x0b,
+       C2H_RA_RPT = 0x0c,
        C2H_HW_FEATURE_REPORT = 0x19,
        C2H_WLAN_INFO = 0x27,
        C2H_HW_FEATURE_DUMP = 0xfd,
 #define GET_CCX_REPORT_SEQNUM(c2h_payload)     (c2h_payload[8] & 0xfc)
 #define GET_CCX_REPORT_STATUS(c2h_payload)     (c2h_payload[9] & 0xc0)
 
+#define GET_RA_REPORT_RATE(c2h_payload)                (c2h_payload[0] & 0x7f)
+#define GET_RA_REPORT_SGI(c2h_payload)         ((c2h_payload[0] & 0x80) >> 7)
+#define GET_RA_REPORT_BW(c2h_payload)          (c2h_payload[6])
+#define GET_RA_REPORT_MACID(c2h_payload)       (c2h_payload[1])
+
 /* PKT H2C */
 #define H2C_PKT_CMD_ID 0xFF
 #define H2C_PKT_CATEGORY 0x01
 
        return 0;
 }
 
+static void rtw_ops_sta_statistics(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_sta *sta,
+                                  struct station_info *sinfo)
+{
+       struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+
+       sinfo->txrate = si->ra_report.txrate;
+       sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+}
+
 const struct ieee80211_ops rtw_ops = {
        .tx                     = rtw_ops_tx,
        .wake_tx_queue          = rtw_ops_wake_tx_queue,
        .sw_scan_complete       = rtw_ops_sw_scan_complete,
        .mgd_prepare_tx         = rtw_ops_mgd_prepare_tx,
        .set_rts_threshold      = rtw_ops_set_rts_threshold,
+       .sta_statistics         = rtw_ops_sta_statistics,
 };
 EXPORT_SYMBOL(rtw_ops);
 
        {.bitrate = 540, .hw_value = 0x0b,},
 };
 
+u16 rtw_desc_to_bitrate(u8 desc_rate)
+{
+       struct ieee80211_rate rate;
+
+       if (WARN(desc_rate >= ARRAY_SIZE(rtw_ratetable), "invalid desc rate\n"))
+               return 0;
+
+       rate = rtw_ratetable[desc_rate];
+
+       return rate.bitrate;
+}
+
 static struct ieee80211_supported_band rtw_band_2ghz = {
        .band = NL80211_BAND_2GHZ,
 
 
        struct timer_list purge_timer;
 };
 
+struct rtw_ra_report {
+       struct rate_info txrate;
+       u32 bit_rate;
+       u8 desc_rate;
+};
+
 struct rtw_txq {
        struct list_head list;
 
        u64 ra_mask;
 
        DECLARE_BITMAP(tid_ba, IEEE80211_NUM_TIDS);
+
+       struct rtw_ra_report ra_report;
 };
 
 struct rtw_vif {
 bool ltecoex_reg_write(struct rtw_dev *rtwdev, u16 offset, u32 value);
 void rtw_restore_reg(struct rtw_dev *rtwdev,
                     struct rtw_backup_info *bckp, u32 num);
+void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss);
 void rtw_set_channel(struct rtw_dev *rtwdev);
 void rtw_vif_port_config(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
                         u32 config);
 void rtw_core_deinit(struct rtw_dev *rtwdev);
 int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw);
 void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw);
+u16 rtw_desc_to_bitrate(u8 desc_rate);
 
 #endif
 
        else if (pkt_stat->rate >= DESC_RATEMCS0)
                rx_status->encoding = RX_ENC_HT;
 
-       if (pkt_stat->rate >= DESC_RATEVHT1SS_MCS0 &&
-           pkt_stat->rate <= DESC_RATEVHT1SS_MCS9) {
-               rx_status->nss = 1;
-               rx_status->rate_idx = pkt_stat->rate - DESC_RATEVHT1SS_MCS0;
-       } else if (pkt_stat->rate >= DESC_RATEVHT2SS_MCS0 &&
-                  pkt_stat->rate <= DESC_RATEVHT2SS_MCS9) {
-               rx_status->nss = 2;
-               rx_status->rate_idx = pkt_stat->rate - DESC_RATEVHT2SS_MCS0;
-       } else if (pkt_stat->rate >= DESC_RATEVHT3SS_MCS0 &&
-                  pkt_stat->rate <= DESC_RATEVHT3SS_MCS9) {
-               rx_status->nss = 3;
-               rx_status->rate_idx = pkt_stat->rate - DESC_RATEVHT3SS_MCS0;
-       } else if (pkt_stat->rate >= DESC_RATEVHT4SS_MCS0 &&
-                  pkt_stat->rate <= DESC_RATEVHT4SS_MCS9) {
-               rx_status->nss = 4;
-               rx_status->rate_idx = pkt_stat->rate - DESC_RATEVHT4SS_MCS0;
-       } else if (pkt_stat->rate >= DESC_RATEMCS0 &&
-                  pkt_stat->rate <= DESC_RATEMCS15) {
-               rx_status->rate_idx = pkt_stat->rate - DESC_RATEMCS0;
+       if (pkt_stat->rate >= DESC_RATEMCS0) {
+               rtw_desc_to_mcsrate(pkt_stat->rate, &rx_status->rate_idx,
+                                   &rx_status->nss);
        } else if (rx_status->band == NL80211_BAND_5GHZ &&
                   pkt_stat->rate >= DESC_RATE6M &&
                   pkt_stat->rate <= DESC_RATE54M) {
 
                }
        }
 }
+
+void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss)
+{
+       if (rate <= DESC_RATE54M)
+               return;
+
+       if (rate >= DESC_RATEVHT1SS_MCS0 &&
+           rate <= DESC_RATEVHT1SS_MCS9) {
+               *nss = 1;
+               *mcs = rate - DESC_RATEVHT1SS_MCS0;
+       } else if (rate >= DESC_RATEVHT2SS_MCS0 &&
+                  rate <= DESC_RATEVHT2SS_MCS9) {
+               *nss = 2;
+               *mcs = rate - DESC_RATEVHT2SS_MCS0;
+       } else if (rate >= DESC_RATEVHT3SS_MCS0 &&
+                  rate <= DESC_RATEVHT3SS_MCS9) {
+               *nss = 3;
+               *mcs = rate - DESC_RATEVHT3SS_MCS0;
+       } else if (rate >= DESC_RATEVHT4SS_MCS0 &&
+                  rate <= DESC_RATEVHT4SS_MCS9) {
+               *nss = 4;
+               *mcs = rate - DESC_RATEVHT4SS_MCS0;
+       } else if (rate >= DESC_RATEMCS0 &&
+                  rate <= DESC_RATEMCS15) {
+               *mcs = rate - DESC_RATEMCS0;
+       }
+}