u8 rsvd[92];
 } __packed __aligned(2);
 
+struct rtw89_sa_query {
+       struct ieee80211_hdr_3addr hdr;
+       u8 category;
+       u8 action;
+} __packed __aligned(2);
+
 static const u8 mss_signature[] = {0x4D, 0x53, 0x53, 0x4B, 0x50, 0x4F, 0x4F, 0x4C};
 
 union rtw89_fw_element_arg {
        return skb;
 }
 
+static struct sk_buff *rtw89_sa_query_get(struct rtw89_dev *rtwdev,
+                                         struct rtw89_vif *rtwvif)
+{
+       struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+       struct rtw89_sa_query *sa_query;
+       struct sk_buff *skb;
+
+       skb = dev_alloc_skb(sizeof(*sa_query));
+       if (!skb)
+               return NULL;
+
+       sa_query = skb_put_zero(skb, sizeof(*sa_query));
+       sa_query->hdr.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_ACTION |
+                                                 IEEE80211_FCTL_PROTECTED);
+       ether_addr_copy(sa_query->hdr.addr1, bss_conf->bssid);
+       ether_addr_copy(sa_query->hdr.addr2, vif->addr);
+       ether_addr_copy(sa_query->hdr.addr3, bss_conf->bssid);
+       sa_query->category = WLAN_CATEGORY_SA_QUERY;
+       sa_query->action = WLAN_ACTION_SA_QUERY_RESPONSE;
+
+       return skb;
+}
+
 static int rtw89_fw_h2c_add_general_pkt(struct rtw89_dev *rtwdev,
                                        struct rtw89_vif *rtwvif,
                                        enum rtw89_fw_pkt_ofld_type type,
        case RTW89_PKT_OFLD_TYPE_EAPOL_KEY:
                skb = rtw89_eapol_get(rtwdev, rtwvif);
                break;
+       case RTW89_PKT_OFLD_TYPE_SA_QUERY:
+               skb = rtw89_sa_query_get(rtwdev, rtwvif);
+               break;
        default:
                goto err;
        }
        struct rtw89_h2c_wow_gtk_ofld *h2c;
        u8 macid = rtwvif->mac_id;
        u32 len = sizeof(*h2c);
+       u8 pkt_id_sa_query = 0;
        struct sk_buff *skb;
        u8 pkt_id_eapol = 0;
        int ret;
        if (ret)
                goto fail;
 
-       /* not support TKIP and IEEE80211W yet */
+       if (gtk_info->igtk_keyid) {
+               ret = rtw89_fw_h2c_add_general_pkt(rtwdev, rtwvif,
+                                                  RTW89_PKT_OFLD_TYPE_SA_QUERY,
+                                                  &pkt_id_sa_query);
+               if (ret)
+                       goto fail;
+       }
+
+       /* not support TKIP yet */
        h2c->w0 = le32_encode_bits(enable, RTW89_H2C_WOW_GTK_OFLD_W0_EN) |
                  le32_encode_bits(0, RTW89_H2C_WOW_GTK_OFLD_W0_TKIP_EN) |
-                 le32_encode_bits(0, RTW89_H2C_WOW_GTK_OFLD_W0_IEEE80211W_EN) |
+                 le32_encode_bits(gtk_info->igtk_keyid ? 1 : 0,
+                                  RTW89_H2C_WOW_GTK_OFLD_W0_IEEE80211W_EN) |
                  le32_encode_bits(macid, RTW89_H2C_WOW_GTK_OFLD_W0_MAC_ID) |
                  le32_encode_bits(pkt_id_eapol, RTW89_H2C_WOW_GTK_OFLD_W0_GTK_RSP_ID);
-       h2c->w1 = le32_encode_bits(rtw_wow->akm, RTW89_H2C_WOW_GTK_OFLD_W1_ALGO_AKM_SUIT);
+       h2c->w1 = le32_encode_bits(gtk_info->igtk_keyid ? pkt_id_sa_query : 0,
+                                  RTW89_H2C_WOW_GTK_OFLD_W1_PMF_SA_QUERY_ID) |
+                 le32_encode_bits(rtw_wow->akm, RTW89_H2C_WOW_GTK_OFLD_W1_ALGO_AKM_SUIT);
        h2c->gtk_info = rtw_wow->gtk_info;
 
 hdr:
 
        {WLAN_CIPHER_SUITE_GCMP,        .fw_alg = 8,    .len = WLAN_KEY_LEN_GCMP,},
        {WLAN_CIPHER_SUITE_CCMP_256,    .fw_alg = 7,    .len = WLAN_KEY_LEN_CCMP_256,},
        {WLAN_CIPHER_SUITE_GCMP_256,    .fw_alg = 23,   .len = WLAN_KEY_LEN_GCMP_256,},
+       {WLAN_CIPHER_SUITE_AES_CMAC,    .fw_alg = 32,   .len = WLAN_KEY_LEN_AES_CMAC,},
 };
 
 static const
        return 0;
 }
 
+static int rtw89_rx_pn_get_pmf(struct rtw89_dev *rtwdev,
+                              struct ieee80211_key_conf *key,
+                              struct rtw89_wow_gtk_info *gtk_info)
+{
+       struct ieee80211_key_seq seq;
+       u64 pn;
+
+       if (key->keyidx == 4)
+               memcpy(gtk_info->igtk[0], key->key, key->keylen);
+       else if (key->keyidx == 5)
+               memcpy(gtk_info->igtk[1], key->key, key->keylen);
+       else
+               return -EINVAL;
+
+       ieee80211_get_key_rx_seq(key, 0, &seq);
+
+       /* seq.ccmp.pn[] is BE order array */
+       pn = u64_encode_bits(seq.ccmp.pn[0], RTW89_KEY_PN_5) |
+            u64_encode_bits(seq.ccmp.pn[1], RTW89_KEY_PN_4) |
+            u64_encode_bits(seq.ccmp.pn[2], RTW89_KEY_PN_3) |
+            u64_encode_bits(seq.ccmp.pn[3], RTW89_KEY_PN_2) |
+            u64_encode_bits(seq.ccmp.pn[4], RTW89_KEY_PN_1) |
+            u64_encode_bits(seq.ccmp.pn[5], RTW89_KEY_PN_0);
+       gtk_info->ipn = cpu_to_le64(pn);
+       gtk_info->igtk_keyid = cpu_to_le32(key->keyidx);
+       rtw89_debug(rtwdev, RTW89_DBG_WOW, "%s key %d pn-%llx\n",
+                   __func__, key->keyidx, pn);
+
+       return 0;
+}
+
+static int rtw89_rx_pn_set_pmf(struct rtw89_dev *rtwdev,
+                              struct ieee80211_key_conf *key,
+                              u64 pn)
+{
+       struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+       struct rtw89_wow_aoac_report *aoac_rpt = &rtw_wow->aoac_rpt;
+       struct ieee80211_key_seq seq;
+
+       if (key->keyidx != aoac_rpt->igtk_key_id)
+               return 0;
+
+       /* seq.ccmp.pn[] is BE order array */
+       seq.ccmp.pn[0] = u64_get_bits(pn, RTW89_KEY_PN_5);
+       seq.ccmp.pn[1] = u64_get_bits(pn, RTW89_KEY_PN_4);
+       seq.ccmp.pn[2] = u64_get_bits(pn, RTW89_KEY_PN_3);
+       seq.ccmp.pn[3] = u64_get_bits(pn, RTW89_KEY_PN_2);
+       seq.ccmp.pn[4] = u64_get_bits(pn, RTW89_KEY_PN_1);
+       seq.ccmp.pn[5] = u64_get_bits(pn, RTW89_KEY_PN_0);
+
+       ieee80211_set_key_rx_seq(key, 0, &seq);
+       rtw89_debug(rtwdev, RTW89_DBG_WOW, "%s key %d pn-%*ph\n",
+                   __func__, key->keyidx, 6, seq.ccmp.pn);
+
+       return 0;
+}
+
 static void rtw89_wow_get_key_info_iter(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif,
                                        struct ieee80211_sta *sta,
        struct rtw89_dev *rtwdev = hw->priv;
        struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
        struct rtw89_wow_key_info *key_info = &rtw_wow->key_info;
+       struct rtw89_wow_gtk_info *gtk_info = &rtw_wow->gtk_info;
        const struct rtw89_cipher_info *cipher_info;
        bool *err = data;
        int ret;
                        key_info->gtk_keyidx = key->keyidx;
                }
                break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               ret = rtw89_rx_pn_get_pmf(rtwdev, key, gtk_info);
+               if (ret)
+                       goto err;
+               break;
        default:
                rtw89_debug(rtwdev, RTW89_DBG_WOW, "unsupport cipher %x\n",
                            key->cipher);
                if (!sta && update_tx_key_info && aoac_rpt->rekey_ok)
                        iter_data->gtk_cipher = key->cipher;
                break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               if (update_tx_key_info) {
+                       if (aoac_rpt->rekey_ok)
+                               iter_data->igtk_cipher = key->cipher;
+               } else {
+                       ret = rtw89_rx_pn_set_pmf(rtwdev, key,
+                                                 aoac_rpt->igtk_ipn);
+                       if (ret)
+                               goto err;
+               }
+               break;
        default:
                rtw89_debug(rtwdev, RTW89_DBG_WOW, "unsupport cipher %x\n",
                            key->cipher);
        struct rtw89_wow_aoac_report *aoac_rpt = &rtw_wow->aoac_rpt;
        struct rtw89_mac_c2h_info c2h_info = {};
        struct rtw89_mac_h2c_info h2c_info = {};
+       u8 igtk_ipn[8];
        u8 key_idx;
        int ret;
 
                u32_get_bits(c2h_info.u.c2hreg[1], RTW89_C2HREG_AOAC_RPT_2_W1_PTK_IV_6);
        aoac_rpt->ptk_rx_iv[7] =
                u32_get_bits(c2h_info.u.c2hreg[1], RTW89_C2HREG_AOAC_RPT_2_W1_PTK_IV_7);
+       igtk_ipn[0] =
+               u32_get_bits(c2h_info.u.c2hreg[1], RTW89_C2HREG_AOAC_RPT_2_W1_IGTK_IPN_IV_0);
+       igtk_ipn[1] =
+               u32_get_bits(c2h_info.u.c2hreg[1], RTW89_C2HREG_AOAC_RPT_2_W1_IGTK_IPN_IV_1);
+       igtk_ipn[2] =
+               u32_get_bits(c2h_info.u.c2hreg[2], RTW89_C2HREG_AOAC_RPT_2_W2_IGTK_IPN_IV_2);
+       igtk_ipn[3] =
+               u32_get_bits(c2h_info.u.c2hreg[2], RTW89_C2HREG_AOAC_RPT_2_W2_IGTK_IPN_IV_3);
+       igtk_ipn[4] =
+               u32_get_bits(c2h_info.u.c2hreg[2], RTW89_C2HREG_AOAC_RPT_2_W2_IGTK_IPN_IV_4);
+       igtk_ipn[5] =
+               u32_get_bits(c2h_info.u.c2hreg[2], RTW89_C2HREG_AOAC_RPT_2_W2_IGTK_IPN_IV_5);
+       igtk_ipn[6] =
+               u32_get_bits(c2h_info.u.c2hreg[3], RTW89_C2HREG_AOAC_RPT_2_W3_IGTK_IPN_IV_6);
+       igtk_ipn[7] =
+               u32_get_bits(c2h_info.u.c2hreg[3], RTW89_C2HREG_AOAC_RPT_2_W3_IGTK_IPN_IV_7);
+       aoac_rpt->igtk_ipn = u64_encode_bits(igtk_ipn[0], RTW89_IGTK_IPN_0) |
+                            u64_encode_bits(igtk_ipn[1], RTW89_IGTK_IPN_1) |
+                            u64_encode_bits(igtk_ipn[2], RTW89_IGTK_IPN_2) |
+                            u64_encode_bits(igtk_ipn[3], RTW89_IGTK_IPN_3) |
+                            u64_encode_bits(igtk_ipn[4], RTW89_IGTK_IPN_4) |
+                            u64_encode_bits(igtk_ipn[5], RTW89_IGTK_IPN_5) |
+                            u64_encode_bits(igtk_ipn[6], RTW89_IGTK_IPN_6) |
+                            u64_encode_bits(igtk_ipn[7], RTW89_IGTK_IPN_7);
 
        return 0;
 }
 
        rtw89_rx_iv_to_pn(rtwdev, key,
                          aoac_rpt->gtk_rx_iv[key->keyidx]);
+
+       if (!data.igtk_cipher)
+               return;
+
+       key = rtw89_wow_gtk_rekey(rtwdev, data.igtk_cipher, aoac_rpt->igtk_key_id,
+                                 aoac_rpt->igtk);
+       if (!key)
+               return;
+
+       rtw89_rx_pn_set_pmf(rtwdev, key, aoac_rpt->igtk_ipn);
        ieee80211_gtk_rekey_notify(wow_vif, wow_vif->bss_conf.bssid,
                                   aoac_rpt->eapol_key_replay_count,
                                   GFP_KERNEL);