rtw89_core_tx_update_sec_key(struct rtw89_dev *rtwdev,
                             struct rtw89_core_tx_request *tx_req)
 {
+       const struct rtw89_chip_info *chip = rtwdev->chip;
        struct ieee80211_vif *vif = tx_req->vif;
        struct ieee80211_sta *sta = tx_req->sta;
        struct ieee80211_tx_info *info;
        struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
        struct sk_buff *skb = tx_req->skb;
        u8 sec_type = RTW89_SEC_KEY_TYPE_NONE;
+       u64 pn64;
 
        if (!vif) {
                rtw89_warn(rtwdev, "cannot set sec key without vif\n");
        }
 
        desc_info->sec_en = true;
+       desc_info->sec_keyid = key->keyidx;
        desc_info->sec_type = sec_type;
        desc_info->sec_cam_idx = sec_cam->sec_cam_idx;
+
+       if (!chip->hw_sec_hdr)
+               return;
+
+       pn64 = atomic64_inc_return(&key->tx_pn);
+       desc_info->sec_seq[0] = pn64;
+       desc_info->sec_seq[1] = pn64 >> 8;
+       desc_info->sec_seq[2] = pn64 >> 16;
+       desc_info->sec_seq[3] = pn64 >> 24;
+       desc_info->sec_seq[4] = pn64 >> 32;
+       desc_info->sec_seq[5] = pn64 >> 40;
+       desc_info->wp_offset = 1; /* in unit of 8 bytes for security header */
 }
 
 static u16 rtw89_core_get_mgmt_rate(struct rtw89_dev *rtwdev,
        return PACKET_MAX;
 }
 
+static void rtw89_core_tx_update_llc_hdr(struct rtw89_dev *rtwdev,
+                                        struct rtw89_tx_desc_info *desc_info,
+                                        struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       __le16 fc = hdr->frame_control;
+
+       desc_info->hdr_llc_len = ieee80211_hdrlen(fc);
+       desc_info->hdr_llc_len >>= 1; /* in unit of 2 bytes */
+}
+
 static void
 rtw89_core_tx_wake(struct rtw89_dev *rtwdev,
                   struct rtw89_core_tx_request *tx_req)
                rtw89_core_tx_update_data_info(rtwdev, tx_req);
                pkt_type = rtw89_core_tx_btc_spec_pkt_notify(rtwdev, tx_req);
                rtw89_core_tx_update_he_qos_htc(rtwdev, tx_req, pkt_type);
+               rtw89_core_tx_update_llc_hdr(rtwdev, desc_info, skb);
                break;
        case RTW89_CORE_TX_TYPE_FWCMD:
                rtw89_core_tx_update_h2c_info(rtwdev, tx_req);
 static __le32 rtw89_build_txwd_body1_v1(struct rtw89_tx_desc_info *desc_info)
 {
        u32 dword = FIELD_PREP(RTW89_TXWD_BODY1_ADDR_INFO_NUM, desc_info->addr_info_nr) |
+                   FIELD_PREP(RTW89_TXWD_BODY1_SEC_KEYID, desc_info->sec_keyid) |
                    FIELD_PREP(RTW89_TXWD_BODY1_SEC_TYPE, desc_info->sec_type);
 
        return cpu_to_le32(dword);
        return cpu_to_le32(dword);
 }
 
+static __le32 rtw89_build_txwd_body4(struct rtw89_tx_desc_info *desc_info)
+{
+       u32 dword = FIELD_PREP(RTW89_TXWD_BODY4_SEC_IV_L0, desc_info->sec_seq[0]) |
+                   FIELD_PREP(RTW89_TXWD_BODY4_SEC_IV_L1, desc_info->sec_seq[1]);
+
+       return cpu_to_le32(dword);
+}
+
+static __le32 rtw89_build_txwd_body5(struct rtw89_tx_desc_info *desc_info)
+{
+       u32 dword = FIELD_PREP(RTW89_TXWD_BODY5_SEC_IV_H2, desc_info->sec_seq[2]) |
+                   FIELD_PREP(RTW89_TXWD_BODY5_SEC_IV_H3, desc_info->sec_seq[3]) |
+                   FIELD_PREP(RTW89_TXWD_BODY5_SEC_IV_H4, desc_info->sec_seq[4]) |
+                   FIELD_PREP(RTW89_TXWD_BODY5_SEC_IV_H5, desc_info->sec_seq[5]);
+
+       return cpu_to_le32(dword);
+}
+
 static __le32 rtw89_build_txwd_body7_v1(struct rtw89_tx_desc_info *desc_info)
 {
        u32 dword = FIELD_PREP(RTW89_TXWD_BODY7_USE_RATE_V1, desc_info->use_rate) |
        txwd_body->dword1 = rtw89_build_txwd_body1_v1(desc_info);
        txwd_body->dword2 = rtw89_build_txwd_body2(desc_info);
        txwd_body->dword3 = rtw89_build_txwd_body3(desc_info);
+       if (desc_info->sec_en) {
+               txwd_body->dword4 = rtw89_build_txwd_body4(desc_info);
+               txwd_body->dword5 = rtw89_build_txwd_body5(desc_info);
+       }
        txwd_body->dword7 = rtw89_build_txwd_body7_v1(desc_info);
 
        if (!desc_info->en_wd_info)
 
 /* TX WD BODY DWORD 1 */
 #define RTW89_TXWD_BODY1_ADDR_INFO_NUM GENMASK(31, 26)
 #define RTW89_TXWD_BODY1_PAYLOAD_ID GENMASK(31, 16)
+#define RTW89_TXWD_BODY1_SEC_KEYID GENMASK(5, 4)
 #define RTW89_TXWD_BODY1_SEC_TYPE GENMASK(3, 0)
 
 /* TX WD BODY DWORD 2 */
 #define RTW89_TXWD_BODY3_SW_SEQ GENMASK(11, 0)
 
 /* TX WD BODY DWORD 4 */
+#define RTW89_TXWD_BODY4_SEC_IV_L1 GENMASK(31, 24)
+#define RTW89_TXWD_BODY4_SEC_IV_L0 GENMASK(23, 16)
 
 /* TX WD BODY DWORD 5 */
+#define RTW89_TXWD_BODY5_SEC_IV_H5 GENMASK(31, 24)
+#define RTW89_TXWD_BODY5_SEC_IV_H4 GENMASK(23, 16)
+#define RTW89_TXWD_BODY5_SEC_IV_H3 GENMASK(15, 8)
+#define RTW89_TXWD_BODY5_SEC_IV_H2 GENMASK(7, 0)
 
 /* TX WD BODY DWORD 6 (V1) */