rx_status->rate_idx -= 4;
 }
 
+static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev,
+                                      struct sk_buff *skb,
+                                      struct ieee80211_rx_status *rx_status)
+{
+       static const struct ieee80211_radiotap_he known_he = {
+               .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
+                                    IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN),
+               .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN),
+       };
+       struct ieee80211_radiotap_he *he;
+
+       if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR))
+               return;
+
+       if (rx_status->encoding == RX_ENC_HE) {
+               rx_status->flag |= RX_FLAG_RADIOTAP_HE;
+               he = skb_push(skb, sizeof(*he));
+               *he = known_he;
+       }
+}
+
 static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,
                                      struct rtw89_rx_phy_ppdu *phy_ppdu,
                                      struct rtw89_rx_desc_info *desc_info,
 
        rtw89_core_hw_to_sband_rate(rx_status);
        rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu);
+       rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status);
        /* In low power mode, it does RX in thread context. */
        local_bh_disable();
        ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, napi);
 
 #define RSSI_FACTOR 1
 #define RTW89_RSSI_RAW_TO_DBM(rssi) ((s8)((rssi) >> RSSI_FACTOR) - MAX_RSSI)
 #define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR)
+#define RTW89_RADIOTAP_ROOM ALIGN(sizeof(struct ieee80211_radiotap_he), 64)
 
 #define RTW89_HTC_MASK_VARIANT GENMASK(1, 0)
 #define RTW89_HTC_VARIANT_HE 3
        return &fw_info->normal;
 }
 
+static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev,
+                                                    unsigned int length)
+{
+       struct sk_buff *skb;
+
+       if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) {
+               skb = dev_alloc_skb(length + RTW89_RADIOTAP_ROOM);
+               if (!skb)
+                       return NULL;
+
+               skb_reserve(skb, RTW89_RADIOTAP_ROOM);
+               return skb;
+       }
+
+       return dev_alloc_skb(length);
+}
+
 int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
                        struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel);
 int rtw89_h2c_tx(struct rtw89_dev *rtwdev,