]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: rtw89: implement C2H TX report handler
authorFedor Pchelkin <pchelkin@ispras.ru>
Tue, 4 Nov 2025 13:57:12 +0000 (16:57 +0300)
committerPing-Ke Shih <pkshih@realtek.com>
Thu, 6 Nov 2025 06:30:13 +0000 (14:30 +0800)
rtw89 has several ways of handling TX status report events.  The first one
is based on RPP feature which is used by PCIe HCI.  The other one depends
on firmware sending a corresponding C2H message, quite similar to what
rtw88 has.

Toggle a bit in the TX descriptor to indicate to the firmware that TX
report for the frame is expected.   This will allow handling TX wait skbs
and the ones flagged with IEEE80211_TX_CTL_REQ_TX_STATUS correctly.

Do the bulk of the patch according to the vendor driver for RTL8851BU.
However, there are slight differences in C2H message format between
different types of chips.  RTL885xB ones follow format V0.  RTL8852C has
format V1, and RTL8922AU has format V2.

Found by Linux Verification Center (linuxtesting.org).

Suggested-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20251104135720.321110-6-pchelkin@ispras.ru
drivers/net/wireless/realtek/rtw89/core.c
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/fw.h
drivers/net/wireless/realtek/rtw89/mac.c
drivers/net/wireless/realtek/rtw89/mac.h
drivers/net/wireless/realtek/rtw89/txrx.h

index 1f92be300ea7bb9649519ed29378226b38a19de8..84f94c796a4863db47e09faf550a4cda28a929fc 100644 (file)
@@ -1415,7 +1415,10 @@ static __le32 rtw89_build_txwd_info1(struct rtw89_tx_desc_info *desc_info)
        u32 dword = FIELD_PREP(RTW89_TXWD_INFO1_MAX_AGGNUM, desc_info->ampdu_num) |
                    FIELD_PREP(RTW89_TXWD_INFO1_A_CTRL_BSR, desc_info->a_ctrl_bsr) |
                    FIELD_PREP(RTW89_TXWD_INFO1_DATA_RTY_LOWEST_RATE,
-                              desc_info->data_retry_lowest_rate);
+                              desc_info->data_retry_lowest_rate) |
+                   FIELD_PREP(RTW89_TXWD_INFO1_DATA_TXCNT_LMT_SEL,
+                              desc_info->tx_cnt_lmt_en) |
+                   FIELD_PREP(RTW89_TXWD_INFO1_DATA_TXCNT_LMT, desc_info->tx_cnt_lmt);
 
        return cpu_to_le32(dword);
 }
@@ -1439,11 +1442,19 @@ static __le32 rtw89_build_txwd_info2_v1(struct rtw89_tx_desc_info *desc_info)
        return cpu_to_le32(dword);
 }
 
+static __le32 rtw89_build_txwd_info3(struct rtw89_tx_desc_info *desc_info)
+{
+       u32 dword = FIELD_PREP(RTW89_TXWD_INFO3_SPE_RPT, desc_info->report);
+
+       return cpu_to_le32(dword);
+}
+
 static __le32 rtw89_build_txwd_info4(struct rtw89_tx_desc_info *desc_info)
 {
        bool rts_en = !desc_info->is_bmc;
        u32 dword = FIELD_PREP(RTW89_TXWD_INFO4_RTS_EN, rts_en) |
-                   FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, 1);
+                   FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, 1) |
+                   FIELD_PREP(RTW89_TXWD_INFO4_SW_DEFINE, desc_info->sn);
 
        return cpu_to_le32(dword);
 }
@@ -1466,6 +1477,7 @@ void rtw89_core_fill_txdesc(struct rtw89_dev *rtwdev,
        txwd_info->dword0 = rtw89_build_txwd_info0(desc_info);
        txwd_info->dword1 = rtw89_build_txwd_info1(desc_info);
        txwd_info->dword2 = rtw89_build_txwd_info2(desc_info);
+       txwd_info->dword3 = rtw89_build_txwd_info3(desc_info);
        txwd_info->dword4 = rtw89_build_txwd_info4(desc_info);
 
 }
@@ -1495,6 +1507,7 @@ void rtw89_core_fill_txdesc_v1(struct rtw89_dev *rtwdev,
        txwd_info->dword0 = rtw89_build_txwd_info0_v1(desc_info);
        txwd_info->dword1 = rtw89_build_txwd_info1(desc_info);
        txwd_info->dword2 = rtw89_build_txwd_info2_v1(desc_info);
+       txwd_info->dword3 = rtw89_build_txwd_info3(desc_info);
        txwd_info->dword4 = rtw89_build_txwd_info4(desc_info);
 }
 EXPORT_SYMBOL(rtw89_core_fill_txdesc_v1);
@@ -1580,7 +1593,10 @@ static __le32 rtw89_build_txwd_info0_v2(struct rtw89_tx_desc_info *desc_info)
        u32 dword = FIELD_PREP(BE_TXD_INFO0_DATA_STBC, desc_info->stbc) |
                    FIELD_PREP(BE_TXD_INFO0_DATA_LDPC, desc_info->ldpc) |
                    FIELD_PREP(BE_TXD_INFO0_DISDATAFB, desc_info->dis_data_fb) |
-                   FIELD_PREP(BE_TXD_INFO0_MULTIPORT_ID, desc_info->port);
+                   FIELD_PREP(BE_TXD_INFO0_MULTIPORT_ID, desc_info->port) |
+                   FIELD_PREP(BE_TXD_INFO0_DATA_TXCNT_LMT_SEL,
+                              desc_info->tx_cnt_lmt_en) |
+                   FIELD_PREP(BE_TXD_INFO0_DATA_TXCNT_LMT, desc_info->tx_cnt_lmt);
 
        return cpu_to_le32(dword);
 }
@@ -1590,7 +1606,8 @@ static __le32 rtw89_build_txwd_info1_v2(struct rtw89_tx_desc_info *desc_info)
        u32 dword = FIELD_PREP(BE_TXD_INFO1_MAX_AGG_NUM, desc_info->ampdu_num) |
                    FIELD_PREP(BE_TXD_INFO1_A_CTRL_BSR, desc_info->a_ctrl_bsr) |
                    FIELD_PREP(BE_TXD_INFO1_DATA_RTY_LOWEST_RATE,
-                              desc_info->data_retry_lowest_rate);
+                              desc_info->data_retry_lowest_rate) |
+                   FIELD_PREP(BE_TXD_INFO1_SW_DEFINE, desc_info->sn);
 
        return cpu_to_le32(dword);
 }
@@ -1599,7 +1616,8 @@ static __le32 rtw89_build_txwd_info2_v2(struct rtw89_tx_desc_info *desc_info)
 {
        u32 dword = FIELD_PREP(BE_TXD_INFO2_AMPDU_DENSITY, desc_info->ampdu_density) |
                    FIELD_PREP(BE_TXD_INFO2_FORCE_KEY_EN, desc_info->sec_en) |
-                   FIELD_PREP(BE_TXD_INFO2_SEC_CAM_IDX, desc_info->sec_cam_idx);
+                   FIELD_PREP(BE_TXD_INFO2_SEC_CAM_IDX, desc_info->sec_cam_idx) |
+                   FIELD_PREP(BE_TXD_INFO2_SPE_RPT_V1, desc_info->report);
 
        return cpu_to_le32(dword);
 }
index d23ec4d4095a3427d38690ad0caa4573339ee1a4..2914cc4e97d5e675178f974d34096bbc23642609 100644 (file)
@@ -1169,6 +1169,10 @@ struct rtw89_tx_desc_info {
        u8 ampdu_density;
        u8 ampdu_num;
        bool sec_en;
+       bool report;
+       bool tx_cnt_lmt_en;
+       u8 sn: 4;
+       u8 tx_cnt_lmt: 6;
        u8 addr_info_nr;
        u8 sec_keyid;
        u8 sec_type;
index ddebf797206879a73a37d9adcc9bf6b13d47aba3..3105802063683ab4ffeb270759db5d5377599593 100644 (file)
@@ -3747,6 +3747,47 @@ struct rtw89_c2h_scanofld {
 #define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h) \
        le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(15, 8))
 
+struct rtw89_c2h_mac_tx_rpt {
+       struct rtw89_c2h_hdr hdr;
+       __le32 w2;
+       __le32 w3;
+       __le32 w4;
+       __le32 w5;
+       __le32 w6;
+       __le32 w7;
+} __packed;
+
+#define RTW89_C2H_MAC_TX_RPT_W2_TX_STATE GENMASK(7, 6)
+#define RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE GENMASK(11, 8)
+#define RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT GENMASK(13, 8)
+#define RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT_V1 GENMASK(15, 10)
+
+struct rtw89_c2h_mac_tx_rpt_v2 {
+       struct rtw89_c2h_hdr hdr;
+       __le32 w2;
+       __le32 w3;
+       __le32 w4;
+       __le32 w5;
+       __le32 w6;
+       __le32 w7;
+       __le32 w8;
+       __le32 w9;
+       __le32 w10;
+       __le32 w11;
+       __le32 w12;
+       __le32 w13;
+       __le32 w14;
+       __le32 w15;
+       __le32 w16;
+       __le32 w17;
+       __le32 w18;
+       __le32 w19;
+} __packed;
+
+#define RTW89_C2H_MAC_TX_RPT_W12_TX_STATE_V2 GENMASK(9, 8)
+#define RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2 GENMASK(15, 12)
+#define RTW89_C2H_MAC_TX_RPT_W14_DATA_TX_CNT_V2 GENMASK(15, 10)
+
 struct rtw89_mac_mcc_tsf_rpt {
        u32 macid_x;
        u32 macid_y;
index bc39b4f7bc4f48f1d3d48536ac20a97e8a727595..47048d125c01600f9ffafdd31dd0860fba170da3 100644 (file)
@@ -5488,6 +5488,40 @@ rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32
        rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data);
 }
 
+static void
+rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+       u8 sw_define, tx_status, txcnt;
+
+       if (rtwdev->chip->chip_id == RTL8922A) {
+               const struct rtw89_c2h_mac_tx_rpt_v2 *rpt_v2;
+
+               rpt_v2 = (const struct rtw89_c2h_mac_tx_rpt_v2 *)c2h->data;
+               sw_define = le32_get_bits(rpt_v2->w12,
+                                         RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2);
+               tx_status = le32_get_bits(rpt_v2->w12,
+                                         RTW89_C2H_MAC_TX_RPT_W12_TX_STATE_V2);
+               txcnt = le32_get_bits(rpt_v2->w14,
+                                     RTW89_C2H_MAC_TX_RPT_W14_DATA_TX_CNT_V2);
+       } else {
+               const struct rtw89_c2h_mac_tx_rpt *rpt;
+
+               rpt = (const struct rtw89_c2h_mac_tx_rpt *)c2h->data;
+               sw_define = le32_get_bits(rpt->w2, RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE);
+               tx_status = le32_get_bits(rpt->w2, RTW89_C2H_MAC_TX_RPT_W2_TX_STATE);
+               if (rtwdev->chip->chip_id == RTL8852C)
+                       txcnt = le32_get_bits(rpt->w5,
+                                             RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT_V1);
+               else
+                       txcnt = le32_get_bits(rpt->w5,
+                                             RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT);
+       }
+
+       rtw89_debug(rtwdev, RTW89_DBG_TXRX,
+                   "C2H TX RPT: sn %d, tx_status %d, txcnt %d\n",
+                   sw_define, tx_status, txcnt);
+}
+
 static void
 rtw89_mac_c2h_mrc_tsf_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
 {
@@ -5722,6 +5756,12 @@ void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev,
        [RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT] = rtw89_mac_c2h_mcc_status_rpt,
 };
 
+static
+void (* const rtw89_mac_c2h_misc_handler[])(struct rtw89_dev *rtwdev,
+                                           struct sk_buff *c2h, u32 len) = {
+       [RTW89_MAC_C2H_FUNC_TX_REPORT] = rtw89_mac_c2h_tx_rpt,
+};
+
 static
 void (* const rtw89_mac_c2h_mlo_handler[])(struct rtw89_dev *rtwdev,
                                           struct sk_buff *c2h, u32 len) = {
@@ -5808,6 +5848,8 @@ bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, struct sk_buff *c2h,
                }
        case RTW89_MAC_C2H_CLASS_MCC:
                return true;
+       case RTW89_MAC_C2H_CLASS_MISC:
+               return true;
        case RTW89_MAC_C2H_CLASS_MLO:
                return true;
        case RTW89_MAC_C2H_CLASS_MRC:
@@ -5843,6 +5885,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
                if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC)
                        handler = rtw89_mac_c2h_mcc_handler[func];
                break;
+       case RTW89_MAC_C2H_CLASS_MISC:
+               if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MISC)
+                       handler = rtw89_mac_c2h_misc_handler[func];
+               break;
        case RTW89_MAC_C2H_CLASS_MLO:
                if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MLO)
                        handler = rtw89_mac_c2h_mlo_handler[func];
index 46302f4aa3d989b31df5817b0ca9c0088ebe5891..dfa85ade38ce1018db0313cc379086c0a75417ba 100644 (file)
@@ -432,6 +432,12 @@ enum rtw89_mac_c2h_mcc_func {
        NUM_OF_RTW89_MAC_C2H_FUNC_MCC,
 };
 
+enum rtw89_mac_c2h_misc_func {
+       RTW89_MAC_C2H_FUNC_TX_REPORT = 1,
+
+       NUM_OF_RTW89_MAC_C2H_FUNC_MISC,
+};
+
 enum rtw89_mac_c2h_mlo_func {
        RTW89_MAC_C2H_FUNC_MLO_GET_TBL                  = 0x0,
        RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE         = 0x1,
@@ -470,6 +476,7 @@ enum rtw89_mac_c2h_class {
        RTW89_MAC_C2H_CLASS_WOW = 0x3,
        RTW89_MAC_C2H_CLASS_MCC = 0x4,
        RTW89_MAC_C2H_CLASS_FWDBG = 0x5,
+       RTW89_MAC_C2H_CLASS_MISC = 0x9,
        RTW89_MAC_C2H_CLASS_MLO = 0xc,
        RTW89_MAC_C2H_CLASS_MRC = 0xe,
        RTW89_MAC_C2H_CLASS_AP = 0x18,
index 984c9fdbb018b262100b589d3af56fe6a086c9a2..b37dbac7b790866eacb42fb89983762355c88c69 100644 (file)
@@ -127,6 +127,8 @@ static inline u8 rtw89_get_data_nss(struct rtw89_dev *rtwdev, u16 hw_rate)
 #define RTW89_TXWD_INFO0_MULTIPORT_ID GENMASK(6, 4)
 
 /* TX WD INFO DWORD 1 */
+#define RTW89_TXWD_INFO1_DATA_TXCNT_LMT_SEL BIT(31)
+#define RTW89_TXWD_INFO1_DATA_TXCNT_LMT GENMASK(30, 25)
 #define RTW89_TXWD_INFO1_DATA_RTY_LOWEST_RATE GENMASK(24, 16)
 #define RTW89_TXWD_INFO1_A_CTRL_BSR BIT(14)
 #define RTW89_TXWD_INFO1_MAX_AGGNUM GENMASK(7, 0)
@@ -139,10 +141,12 @@ static inline u8 rtw89_get_data_nss(struct rtw89_dev *rtwdev, u16 hw_rate)
 #define RTW89_TXWD_INFO2_SEC_CAM_IDX GENMASK(7, 0)
 
 /* TX WD INFO DWORD 3 */
+#define RTW89_TXWD_INFO3_SPE_RPT BIT(10)
 
 /* TX WD INFO DWORD 4 */
-#define RTW89_TXWD_INFO4_RTS_EN BIT(27)
 #define RTW89_TXWD_INFO4_HW_RTS_EN BIT(31)
+#define RTW89_TXWD_INFO4_RTS_EN BIT(27)
+#define RTW89_TXWD_INFO4_SW_DEFINE GENMASK(3, 0)
 
 /* TX WD INFO DWORD 5 */