1*d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2*d1e879ecSMiri Korenblit /* 3*d1e879ecSMiri Korenblit * Copyright (C) 2024-2025 Intel Corporation 4*d1e879ecSMiri Korenblit */ 5*d1e879ecSMiri Korenblit #include "mld.h" 6*d1e879ecSMiri Korenblit 7*d1e879ecSMiri Korenblit #include "d3.h" 8*d1e879ecSMiri Korenblit #include "power.h" 9*d1e879ecSMiri Korenblit #include "hcmd.h" 10*d1e879ecSMiri Korenblit #include "iface.h" 11*d1e879ecSMiri Korenblit #include "mcc.h" 12*d1e879ecSMiri Korenblit #include "sta.h" 13*d1e879ecSMiri Korenblit #include "mlo.h" 14*d1e879ecSMiri Korenblit 15*d1e879ecSMiri Korenblit #include "fw/api/d3.h" 16*d1e879ecSMiri Korenblit #include "fw/api/offload.h" 17*d1e879ecSMiri Korenblit #include "fw/api/sta.h" 18*d1e879ecSMiri Korenblit #include "fw/dbg.h" 19*d1e879ecSMiri Korenblit 20*d1e879ecSMiri Korenblit #include <net/ipv6.h> 21*d1e879ecSMiri Korenblit #include <net/addrconf.h> 22*d1e879ecSMiri Korenblit #include <linux/bitops.h> 23*d1e879ecSMiri Korenblit 24*d1e879ecSMiri Korenblit /** 25*d1e879ecSMiri Korenblit * enum iwl_mld_d3_notif - d3 notifications 26*d1e879ecSMiri Korenblit * @IWL_D3_NOTIF_WOWLAN_INFO: WOWLAN_INFO_NOTIF is expected/was received 27*d1e879ecSMiri Korenblit * @IWL_D3_NOTIF_WOWLAN_WAKE_PKT: WOWLAN_WAKE_PKT_NOTIF is expected/was received 28*d1e879ecSMiri Korenblit * @IWL_D3_NOTIF_PROT_OFFLOAD: PROT_OFFLOAD_NOTIF is expected/was received 29*d1e879ecSMiri Korenblit * @IWL_D3_ND_MATCH_INFO: OFFLOAD_MATCH_INFO_NOTIF is expected/was received 30*d1e879ecSMiri Korenblit * @IWL_D3_NOTIF_D3_END_NOTIF: D3_END_NOTIF is expected/was received 31*d1e879ecSMiri Korenblit */ 32*d1e879ecSMiri Korenblit enum iwl_mld_d3_notif { 33*d1e879ecSMiri Korenblit IWL_D3_NOTIF_WOWLAN_INFO = BIT(0), 34*d1e879ecSMiri Korenblit IWL_D3_NOTIF_WOWLAN_WAKE_PKT = BIT(1), 35*d1e879ecSMiri Korenblit IWL_D3_NOTIF_PROT_OFFLOAD = BIT(2), 36*d1e879ecSMiri Korenblit IWL_D3_ND_MATCH_INFO = BIT(3), 37*d1e879ecSMiri Korenblit IWL_D3_NOTIF_D3_END_NOTIF = BIT(4) 38*d1e879ecSMiri Korenblit }; 39*d1e879ecSMiri Korenblit 40*d1e879ecSMiri Korenblit struct iwl_mld_resume_key_iter_data { 41*d1e879ecSMiri Korenblit struct iwl_mld *mld; 42*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status; 43*d1e879ecSMiri Korenblit u32 num_keys, gtk_cipher, igtk_cipher, bigtk_cipher; 44*d1e879ecSMiri Korenblit bool unhandled_cipher; 45*d1e879ecSMiri Korenblit }; 46*d1e879ecSMiri Korenblit 47*d1e879ecSMiri Korenblit struct iwl_mld_suspend_key_iter_data { 48*d1e879ecSMiri Korenblit struct iwl_wowlan_rsc_tsc_params_cmd *rsc; 49*d1e879ecSMiri Korenblit bool have_rsc; 50*d1e879ecSMiri Korenblit int gtks; 51*d1e879ecSMiri Korenblit int found_gtk_idx[4]; 52*d1e879ecSMiri Korenblit __le32 gtk_cipher; 53*d1e879ecSMiri Korenblit __le32 igtk_cipher; 54*d1e879ecSMiri Korenblit __le32 bigtk_cipher; 55*d1e879ecSMiri Korenblit }; 56*d1e879ecSMiri Korenblit 57*d1e879ecSMiri Korenblit struct iwl_mld_mcast_key_data { 58*d1e879ecSMiri Korenblit u8 key[WOWLAN_KEY_MAX_SIZE]; 59*d1e879ecSMiri Korenblit u8 len; 60*d1e879ecSMiri Korenblit u8 flags; 61*d1e879ecSMiri Korenblit u8 id; 62*d1e879ecSMiri Korenblit union { 63*d1e879ecSMiri Korenblit struct { 64*d1e879ecSMiri Korenblit struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT]; 65*d1e879ecSMiri Korenblit struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT]; 66*d1e879ecSMiri Korenblit } gtk; 67*d1e879ecSMiri Korenblit struct { 68*d1e879ecSMiri Korenblit struct ieee80211_key_seq cmac_gmac_seq; 69*d1e879ecSMiri Korenblit } igtk_bigtk; 70*d1e879ecSMiri Korenblit }; 71*d1e879ecSMiri Korenblit 72*d1e879ecSMiri Korenblit }; 73*d1e879ecSMiri Korenblit 74*d1e879ecSMiri Korenblit /** 75*d1e879ecSMiri Korenblit * struct iwl_mld_wowlan_status - contains wowlan status data from 76*d1e879ecSMiri Korenblit * all wowlan notifications 77*d1e879ecSMiri Korenblit * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason 78*d1e879ecSMiri Korenblit * @replay_ctr: GTK rekey replay counter 79*d1e879ecSMiri Korenblit * @pattern_number: number of the matched patterns on packets 80*d1e879ecSMiri Korenblit * @last_qos_seq: QoS sequence counter of offloaded tid 81*d1e879ecSMiri Korenblit * @num_of_gtk_rekeys: number of GTK rekeys during D3 82*d1e879ecSMiri Korenblit * @tid_offloaded_tx: tid used by the firmware to transmit data packets 83*d1e879ecSMiri Korenblit * while in wowlan 84*d1e879ecSMiri Korenblit * @wake_packet: wakeup packet received 85*d1e879ecSMiri Korenblit * @wake_packet_length: wake packet length 86*d1e879ecSMiri Korenblit * @wake_packet_bufsize: wake packet bufsize 87*d1e879ecSMiri Korenblit * @gtk: data of the last two used gtk's by the FW upon resume 88*d1e879ecSMiri Korenblit * @igtk: data of the last used igtk by the FW upon resume 89*d1e879ecSMiri Korenblit * @bigtk: data of the last two used gtk's by the FW upon resume 90*d1e879ecSMiri Korenblit * @ptk: last seq numbers per tid passed by the FW, 91*d1e879ecSMiri Korenblit * holds both in tkip and aes formats 92*d1e879ecSMiri Korenblit */ 93*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status { 94*d1e879ecSMiri Korenblit u32 wakeup_reasons; 95*d1e879ecSMiri Korenblit u64 replay_ctr; 96*d1e879ecSMiri Korenblit u16 pattern_number; 97*d1e879ecSMiri Korenblit u16 last_qos_seq; 98*d1e879ecSMiri Korenblit u32 num_of_gtk_rekeys; 99*d1e879ecSMiri Korenblit u8 tid_offloaded_tx; 100*d1e879ecSMiri Korenblit u8 *wake_packet; 101*d1e879ecSMiri Korenblit u32 wake_packet_length; 102*d1e879ecSMiri Korenblit u32 wake_packet_bufsize; 103*d1e879ecSMiri Korenblit struct iwl_mld_mcast_key_data gtk[WOWLAN_GTK_KEYS_NUM]; 104*d1e879ecSMiri Korenblit struct iwl_mld_mcast_key_data igtk; 105*d1e879ecSMiri Korenblit struct iwl_mld_mcast_key_data bigtk[WOWLAN_BIGTK_KEYS_NUM]; 106*d1e879ecSMiri Korenblit struct { 107*d1e879ecSMiri Korenblit struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT]; 108*d1e879ecSMiri Korenblit struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT]; 109*d1e879ecSMiri Korenblit 110*d1e879ecSMiri Korenblit } ptk; 111*d1e879ecSMiri Korenblit }; 112*d1e879ecSMiri Korenblit 113*d1e879ecSMiri Korenblit #define NETDETECT_QUERY_BUF_LEN \ 114*d1e879ecSMiri Korenblit (sizeof(struct iwl_scan_offload_profile_match) * \ 115*d1e879ecSMiri Korenblit IWL_SCAN_MAX_PROFILES_V2) 116*d1e879ecSMiri Korenblit 117*d1e879ecSMiri Korenblit /** 118*d1e879ecSMiri Korenblit * struct iwl_mld_netdetect_res - contains netdetect results from 119*d1e879ecSMiri Korenblit * match_info_notif 120*d1e879ecSMiri Korenblit * @matched_profiles: bitmap of matched profiles, referencing the 121*d1e879ecSMiri Korenblit * matches passed in the scan offload request 122*d1e879ecSMiri Korenblit * @matches: array of match information, one for each match 123*d1e879ecSMiri Korenblit */ 124*d1e879ecSMiri Korenblit struct iwl_mld_netdetect_res { 125*d1e879ecSMiri Korenblit u32 matched_profiles; 126*d1e879ecSMiri Korenblit u8 matches[NETDETECT_QUERY_BUF_LEN]; 127*d1e879ecSMiri Korenblit }; 128*d1e879ecSMiri Korenblit 129*d1e879ecSMiri Korenblit /** 130*d1e879ecSMiri Korenblit * struct iwl_mld_resume_data - d3 resume flow data 131*d1e879ecSMiri Korenblit * @notifs_expected: bitmap of expected notifications from fw, 132*d1e879ecSMiri Korenblit * see &enum iwl_mld_d3_notif 133*d1e879ecSMiri Korenblit * @notifs_received: bitmap of received notifications from fw, 134*d1e879ecSMiri Korenblit * see &enum iwl_mld_d3_notif 135*d1e879ecSMiri Korenblit * @d3_end_flags: bitmap of flags from d3_end_notif 136*d1e879ecSMiri Korenblit * @notif_handling_err: error handling one of the resume notifications 137*d1e879ecSMiri Korenblit * @wowlan_status: wowlan status data from all wowlan notifications 138*d1e879ecSMiri Korenblit * @netdetect_res: contains netdetect results from match_info_notif 139*d1e879ecSMiri Korenblit */ 140*d1e879ecSMiri Korenblit struct iwl_mld_resume_data { 141*d1e879ecSMiri Korenblit u32 notifs_expected; 142*d1e879ecSMiri Korenblit u32 notifs_received; 143*d1e879ecSMiri Korenblit u32 d3_end_flags; 144*d1e879ecSMiri Korenblit bool notif_handling_err; 145*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status; 146*d1e879ecSMiri Korenblit struct iwl_mld_netdetect_res *netdetect_res; 147*d1e879ecSMiri Korenblit }; 148*d1e879ecSMiri Korenblit 149*d1e879ecSMiri Korenblit #define IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT \ 150*d1e879ecSMiri Korenblit (IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET | \ 151*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_BY_PATTERN | \ 152*d1e879ecSMiri Korenblit IWL_WAKEUP_BY_PATTERN_IPV4_TCP_SYN |\ 153*d1e879ecSMiri Korenblit IWL_WAKEUP_BY_PATTERN_IPV4_TCP_SYN_WILDCARD |\ 154*d1e879ecSMiri Korenblit IWL_WAKEUP_BY_PATTERN_IPV6_TCP_SYN |\ 155*d1e879ecSMiri Korenblit IWL_WAKEUP_BY_PATTERN_IPV6_TCP_SYN_WILDCARD) 156*d1e879ecSMiri Korenblit 157*d1e879ecSMiri Korenblit #define IWL_WOWLAN_OFFLOAD_TID 0 158*d1e879ecSMiri Korenblit 159*d1e879ecSMiri Korenblit void iwl_mld_set_rekey_data(struct ieee80211_hw *hw, 160*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 161*d1e879ecSMiri Korenblit struct cfg80211_gtk_rekey_data *data) 162*d1e879ecSMiri Korenblit { 163*d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 164*d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 165*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; 166*d1e879ecSMiri Korenblit 167*d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 168*d1e879ecSMiri Korenblit 169*d1e879ecSMiri Korenblit wowlan_data->rekey_data.kek_len = data->kek_len; 170*d1e879ecSMiri Korenblit wowlan_data->rekey_data.kck_len = data->kck_len; 171*d1e879ecSMiri Korenblit memcpy(wowlan_data->rekey_data.kek, data->kek, data->kek_len); 172*d1e879ecSMiri Korenblit memcpy(wowlan_data->rekey_data.kck, data->kck, data->kck_len); 173*d1e879ecSMiri Korenblit wowlan_data->rekey_data.akm = data->akm & 0xFF; 174*d1e879ecSMiri Korenblit wowlan_data->rekey_data.replay_ctr = 175*d1e879ecSMiri Korenblit cpu_to_le64(be64_to_cpup((const __be64 *)data->replay_ctr)); 176*d1e879ecSMiri Korenblit wowlan_data->rekey_data.valid = true; 177*d1e879ecSMiri Korenblit } 178*d1e879ecSMiri Korenblit 179*d1e879ecSMiri Korenblit #if IS_ENABLED(CONFIG_IPV6) 180*d1e879ecSMiri Korenblit void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw, 181*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 182*d1e879ecSMiri Korenblit struct inet6_dev *idev) 183*d1e879ecSMiri Korenblit { 184*d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 185*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; 186*d1e879ecSMiri Korenblit struct inet6_ifaddr *ifa; 187*d1e879ecSMiri Korenblit int idx = 0; 188*d1e879ecSMiri Korenblit 189*d1e879ecSMiri Korenblit memset(wowlan_data->tentative_addrs, 0, 190*d1e879ecSMiri Korenblit sizeof(wowlan_data->tentative_addrs)); 191*d1e879ecSMiri Korenblit 192*d1e879ecSMiri Korenblit read_lock_bh(&idev->lock); 193*d1e879ecSMiri Korenblit list_for_each_entry(ifa, &idev->addr_list, if_list) { 194*d1e879ecSMiri Korenblit wowlan_data->target_ipv6_addrs[idx] = ifa->addr; 195*d1e879ecSMiri Korenblit if (ifa->flags & IFA_F_TENTATIVE) 196*d1e879ecSMiri Korenblit __set_bit(idx, wowlan_data->tentative_addrs); 197*d1e879ecSMiri Korenblit idx++; 198*d1e879ecSMiri Korenblit if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) 199*d1e879ecSMiri Korenblit break; 200*d1e879ecSMiri Korenblit } 201*d1e879ecSMiri Korenblit read_unlock_bh(&idev->lock); 202*d1e879ecSMiri Korenblit 203*d1e879ecSMiri Korenblit wowlan_data->num_target_ipv6_addrs = idx; 204*d1e879ecSMiri Korenblit } 205*d1e879ecSMiri Korenblit #endif 206*d1e879ecSMiri Korenblit 207*d1e879ecSMiri Korenblit enum rt_status { 208*d1e879ecSMiri Korenblit FW_ALIVE, 209*d1e879ecSMiri Korenblit FW_NEEDS_RESET, 210*d1e879ecSMiri Korenblit FW_ERROR, 211*d1e879ecSMiri Korenblit }; 212*d1e879ecSMiri Korenblit 213*d1e879ecSMiri Korenblit static enum rt_status iwl_mld_check_err_tables(struct iwl_mld *mld, 214*d1e879ecSMiri Korenblit struct ieee80211_vif *vif) 215*d1e879ecSMiri Korenblit { 216*d1e879ecSMiri Korenblit u32 err_id; 217*d1e879ecSMiri Korenblit 218*d1e879ecSMiri Korenblit /* check for lmac1 error */ 219*d1e879ecSMiri Korenblit if (iwl_fwrt_read_err_table(mld->trans, 220*d1e879ecSMiri Korenblit mld->trans->dbg.lmac_error_event_table[0], 221*d1e879ecSMiri Korenblit &err_id)) { 222*d1e879ecSMiri Korenblit if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) { 223*d1e879ecSMiri Korenblit struct cfg80211_wowlan_wakeup wakeup = { 224*d1e879ecSMiri Korenblit .rfkill_release = true, 225*d1e879ecSMiri Korenblit }; 226*d1e879ecSMiri Korenblit ieee80211_report_wowlan_wakeup(vif, &wakeup, 227*d1e879ecSMiri Korenblit GFP_KERNEL); 228*d1e879ecSMiri Korenblit 229*d1e879ecSMiri Korenblit return FW_NEEDS_RESET; 230*d1e879ecSMiri Korenblit } 231*d1e879ecSMiri Korenblit return FW_ERROR; 232*d1e879ecSMiri Korenblit } 233*d1e879ecSMiri Korenblit 234*d1e879ecSMiri Korenblit /* check if we have lmac2 set and check for error */ 235*d1e879ecSMiri Korenblit if (iwl_fwrt_read_err_table(mld->trans, 236*d1e879ecSMiri Korenblit mld->trans->dbg.lmac_error_event_table[1], 237*d1e879ecSMiri Korenblit NULL)) 238*d1e879ecSMiri Korenblit return FW_ERROR; 239*d1e879ecSMiri Korenblit 240*d1e879ecSMiri Korenblit /* check for umac error */ 241*d1e879ecSMiri Korenblit if (iwl_fwrt_read_err_table(mld->trans, 242*d1e879ecSMiri Korenblit mld->trans->dbg.umac_error_event_table, 243*d1e879ecSMiri Korenblit NULL)) 244*d1e879ecSMiri Korenblit return FW_ERROR; 245*d1e879ecSMiri Korenblit 246*d1e879ecSMiri Korenblit return FW_ALIVE; 247*d1e879ecSMiri Korenblit } 248*d1e879ecSMiri Korenblit 249*d1e879ecSMiri Korenblit static bool iwl_mld_fw_needs_restart(struct iwl_mld *mld, 250*d1e879ecSMiri Korenblit struct ieee80211_vif *vif) 251*d1e879ecSMiri Korenblit { 252*d1e879ecSMiri Korenblit enum rt_status rt_status = iwl_mld_check_err_tables(mld, vif); 253*d1e879ecSMiri Korenblit 254*d1e879ecSMiri Korenblit if (rt_status == FW_ALIVE) 255*d1e879ecSMiri Korenblit return false; 256*d1e879ecSMiri Korenblit 257*d1e879ecSMiri Korenblit if (rt_status == FW_ERROR) { 258*d1e879ecSMiri Korenblit IWL_ERR(mld, "FW Error occurred during suspend\n"); 259*d1e879ecSMiri Korenblit iwl_fwrt_dump_error_logs(&mld->fwrt); 260*d1e879ecSMiri Korenblit iwl_dbg_tlv_time_point(&mld->fwrt, 261*d1e879ecSMiri Korenblit IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); 262*d1e879ecSMiri Korenblit } 263*d1e879ecSMiri Korenblit 264*d1e879ecSMiri Korenblit return true; 265*d1e879ecSMiri Korenblit } 266*d1e879ecSMiri Korenblit 267*d1e879ecSMiri Korenblit static int 268*d1e879ecSMiri Korenblit iwl_mld_netdetect_config(struct iwl_mld *mld, 269*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 270*d1e879ecSMiri Korenblit const struct cfg80211_wowlan *wowlan) 271*d1e879ecSMiri Korenblit { 272*d1e879ecSMiri Korenblit int ret; 273*d1e879ecSMiri Korenblit struct cfg80211_sched_scan_request *netdetect_cfg = 274*d1e879ecSMiri Korenblit wowlan->nd_config; 275*d1e879ecSMiri Korenblit struct ieee80211_scan_ies ies = {}; 276*d1e879ecSMiri Korenblit 277*d1e879ecSMiri Korenblit ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_SCHED, true); 278*d1e879ecSMiri Korenblit if (ret) 279*d1e879ecSMiri Korenblit return ret; 280*d1e879ecSMiri Korenblit 281*d1e879ecSMiri Korenblit ret = iwl_mld_sched_scan_start(mld, vif, netdetect_cfg, &ies, 282*d1e879ecSMiri Korenblit IWL_MLD_SCAN_NETDETECT); 283*d1e879ecSMiri Korenblit return ret; 284*d1e879ecSMiri Korenblit } 285*d1e879ecSMiri Korenblit 286*d1e879ecSMiri Korenblit static void 287*d1e879ecSMiri Korenblit iwl_mld_le64_to_tkip_seq(__le64 le_pn, struct ieee80211_key_seq *seq) 288*d1e879ecSMiri Korenblit { 289*d1e879ecSMiri Korenblit u64 pn = le64_to_cpu(le_pn); 290*d1e879ecSMiri Korenblit 291*d1e879ecSMiri Korenblit seq->tkip.iv16 = (u16)pn; 292*d1e879ecSMiri Korenblit seq->tkip.iv32 = (u32)(pn >> 16); 293*d1e879ecSMiri Korenblit } 294*d1e879ecSMiri Korenblit 295*d1e879ecSMiri Korenblit static void 296*d1e879ecSMiri Korenblit iwl_mld_le64_to_aes_seq(__le64 le_pn, struct ieee80211_key_seq *seq) 297*d1e879ecSMiri Korenblit { 298*d1e879ecSMiri Korenblit u64 pn = le64_to_cpu(le_pn); 299*d1e879ecSMiri Korenblit 300*d1e879ecSMiri Korenblit seq->ccmp.pn[0] = pn >> 40; 301*d1e879ecSMiri Korenblit seq->ccmp.pn[1] = pn >> 32; 302*d1e879ecSMiri Korenblit seq->ccmp.pn[2] = pn >> 24; 303*d1e879ecSMiri Korenblit seq->ccmp.pn[3] = pn >> 16; 304*d1e879ecSMiri Korenblit seq->ccmp.pn[4] = pn >> 8; 305*d1e879ecSMiri Korenblit seq->ccmp.pn[5] = pn; 306*d1e879ecSMiri Korenblit } 307*d1e879ecSMiri Korenblit 308*d1e879ecSMiri Korenblit static void 309*d1e879ecSMiri Korenblit iwl_mld_convert_gtk_resume_seq(struct iwl_mld_mcast_key_data *gtk_data, 310*d1e879ecSMiri Korenblit const struct iwl_wowlan_all_rsc_tsc_v5 *sc, 311*d1e879ecSMiri Korenblit int rsc_idx) 312*d1e879ecSMiri Korenblit { 313*d1e879ecSMiri Korenblit struct ieee80211_key_seq *aes_seq = gtk_data->gtk.aes_seq; 314*d1e879ecSMiri Korenblit struct ieee80211_key_seq *tkip_seq = gtk_data->gtk.tkip_seq; 315*d1e879ecSMiri Korenblit 316*d1e879ecSMiri Korenblit if (rsc_idx >= ARRAY_SIZE(sc->mcast_rsc)) 317*d1e879ecSMiri Korenblit return; 318*d1e879ecSMiri Korenblit 319*d1e879ecSMiri Korenblit /* We store both the TKIP and AES representations coming from the 320*d1e879ecSMiri Korenblit * FW because we decode the data from there before we iterate 321*d1e879ecSMiri Korenblit * the keys and know which type is used. 322*d1e879ecSMiri Korenblit */ 323*d1e879ecSMiri Korenblit for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { 324*d1e879ecSMiri Korenblit iwl_mld_le64_to_tkip_seq(sc->mcast_rsc[rsc_idx][tid], 325*d1e879ecSMiri Korenblit &tkip_seq[tid]); 326*d1e879ecSMiri Korenblit iwl_mld_le64_to_aes_seq(sc->mcast_rsc[rsc_idx][tid], 327*d1e879ecSMiri Korenblit &aes_seq[tid]); 328*d1e879ecSMiri Korenblit } 329*d1e879ecSMiri Korenblit } 330*d1e879ecSMiri Korenblit 331*d1e879ecSMiri Korenblit static void 332*d1e879ecSMiri Korenblit iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld, 333*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status, 334*d1e879ecSMiri Korenblit const struct iwl_wowlan_gtk_status_v3 *gtk_data, 335*d1e879ecSMiri Korenblit const struct iwl_wowlan_all_rsc_tsc_v5 *sc) 336*d1e879ecSMiri Korenblit { 337*d1e879ecSMiri Korenblit int status_idx = 0; 338*d1e879ecSMiri Korenblit 339*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(wowlan_status->gtk[0].key) < 340*d1e879ecSMiri Korenblit sizeof(gtk_data[0].key)); 341*d1e879ecSMiri Korenblit BUILD_BUG_ON(ARRAY_SIZE(wowlan_status->gtk) < WOWLAN_GTK_KEYS_NUM); 342*d1e879ecSMiri Korenblit 343*d1e879ecSMiri Korenblit for (int notif_idx = 0; notif_idx < ARRAY_SIZE(wowlan_status->gtk); 344*d1e879ecSMiri Korenblit notif_idx++) { 345*d1e879ecSMiri Korenblit int rsc_idx; 346*d1e879ecSMiri Korenblit 347*d1e879ecSMiri Korenblit if (!(gtk_data[notif_idx].key_len)) 348*d1e879ecSMiri Korenblit continue; 349*d1e879ecSMiri Korenblit 350*d1e879ecSMiri Korenblit wowlan_status->gtk[status_idx].len = 351*d1e879ecSMiri Korenblit gtk_data[notif_idx].key_len; 352*d1e879ecSMiri Korenblit wowlan_status->gtk[status_idx].flags = 353*d1e879ecSMiri Korenblit gtk_data[notif_idx].key_flags; 354*d1e879ecSMiri Korenblit wowlan_status->gtk[status_idx].id = 355*d1e879ecSMiri Korenblit wowlan_status->gtk[status_idx].flags & 356*d1e879ecSMiri Korenblit IWL_WOWLAN_GTK_IDX_MASK; 357*d1e879ecSMiri Korenblit memcpy(wowlan_status->gtk[status_idx].key, 358*d1e879ecSMiri Korenblit gtk_data[notif_idx].key, 359*d1e879ecSMiri Korenblit sizeof(gtk_data[notif_idx].key)); 360*d1e879ecSMiri Korenblit 361*d1e879ecSMiri Korenblit /* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc 362*d1e879ecSMiri Korenblit * The gtk ids can be any two numbers between 0 and 3, 363*d1e879ecSMiri Korenblit * the id_map maps between the key id and the index in sc->mcast 364*d1e879ecSMiri Korenblit */ 365*d1e879ecSMiri Korenblit rsc_idx = 366*d1e879ecSMiri Korenblit sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id]; 367*d1e879ecSMiri Korenblit iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx], 368*d1e879ecSMiri Korenblit sc, rsc_idx); 369*d1e879ecSMiri Korenblit 370*d1e879ecSMiri Korenblit /* if it's as long as the TKIP encryption key, copy MIC key */ 371*d1e879ecSMiri Korenblit if (wowlan_status->gtk[status_idx].len == 372*d1e879ecSMiri Korenblit NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY) 373*d1e879ecSMiri Korenblit memcpy(wowlan_status->gtk[status_idx].key + 374*d1e879ecSMiri Korenblit NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, 375*d1e879ecSMiri Korenblit gtk_data[notif_idx].tkip_mic_key, 376*d1e879ecSMiri Korenblit sizeof(gtk_data[notif_idx].tkip_mic_key)); 377*d1e879ecSMiri Korenblit status_idx++; 378*d1e879ecSMiri Korenblit } 379*d1e879ecSMiri Korenblit } 380*d1e879ecSMiri Korenblit 381*d1e879ecSMiri Korenblit static void 382*d1e879ecSMiri Korenblit iwl_mld_convert_ptk_resume_seq(struct iwl_mld *mld, 383*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status, 384*d1e879ecSMiri Korenblit const struct iwl_wowlan_all_rsc_tsc_v5 *sc) 385*d1e879ecSMiri Korenblit { 386*d1e879ecSMiri Korenblit struct ieee80211_key_seq *aes_seq = wowlan_status->ptk.aes_seq; 387*d1e879ecSMiri Korenblit struct ieee80211_key_seq *tkip_seq = wowlan_status->ptk.tkip_seq; 388*d1e879ecSMiri Korenblit 389*d1e879ecSMiri Korenblit BUILD_BUG_ON(ARRAY_SIZE(sc->ucast_rsc) != IWL_MAX_TID_COUNT); 390*d1e879ecSMiri Korenblit 391*d1e879ecSMiri Korenblit for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { 392*d1e879ecSMiri Korenblit iwl_mld_le64_to_aes_seq(sc->ucast_rsc[tid], &aes_seq[tid]); 393*d1e879ecSMiri Korenblit iwl_mld_le64_to_tkip_seq(sc->ucast_rsc[tid], &tkip_seq[tid]); 394*d1e879ecSMiri Korenblit } 395*d1e879ecSMiri Korenblit } 396*d1e879ecSMiri Korenblit 397*d1e879ecSMiri Korenblit static void 398*d1e879ecSMiri Korenblit iwl_mld_convert_mcast_ipn(struct iwl_mld_mcast_key_data *key_status, 399*d1e879ecSMiri Korenblit const struct iwl_wowlan_igtk_status *key) 400*d1e879ecSMiri Korenblit { 401*d1e879ecSMiri Korenblit struct ieee80211_key_seq *seq = 402*d1e879ecSMiri Korenblit &key_status->igtk_bigtk.cmac_gmac_seq; 403*d1e879ecSMiri Korenblit u8 ipn_len = ARRAY_SIZE(key->ipn); 404*d1e879ecSMiri Korenblit 405*d1e879ecSMiri Korenblit BUILD_BUG_ON(ipn_len != ARRAY_SIZE(seq->aes_gmac.pn)); 406*d1e879ecSMiri Korenblit BUILD_BUG_ON(ipn_len != ARRAY_SIZE(seq->aes_cmac.pn)); 407*d1e879ecSMiri Korenblit BUILD_BUG_ON(offsetof(struct ieee80211_key_seq, aes_gmac) != 408*d1e879ecSMiri Korenblit offsetof(struct ieee80211_key_seq, aes_cmac)); 409*d1e879ecSMiri Korenblit 410*d1e879ecSMiri Korenblit /* mac80211 expects big endian for memcmp() to work, convert. 411*d1e879ecSMiri Korenblit * We don't have the key cipher yet so copy to both to cmac and gmac 412*d1e879ecSMiri Korenblit */ 413*d1e879ecSMiri Korenblit for (int i = 0; i < ipn_len; i++) { 414*d1e879ecSMiri Korenblit seq->aes_gmac.pn[i] = key->ipn[ipn_len - i - 1]; 415*d1e879ecSMiri Korenblit seq->aes_cmac.pn[i] = key->ipn[ipn_len - i - 1]; 416*d1e879ecSMiri Korenblit } 417*d1e879ecSMiri Korenblit } 418*d1e879ecSMiri Korenblit 419*d1e879ecSMiri Korenblit static void 420*d1e879ecSMiri Korenblit iwl_mld_convert_igtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status, 421*d1e879ecSMiri Korenblit const struct iwl_wowlan_igtk_status *igtk) 422*d1e879ecSMiri Korenblit { 423*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(wowlan_status->igtk.key) < sizeof(igtk->key)); 424*d1e879ecSMiri Korenblit 425*d1e879ecSMiri Korenblit if (!igtk->key_len) 426*d1e879ecSMiri Korenblit return; 427*d1e879ecSMiri Korenblit 428*d1e879ecSMiri Korenblit wowlan_status->igtk.len = igtk->key_len; 429*d1e879ecSMiri Korenblit wowlan_status->igtk.flags = igtk->key_flags; 430*d1e879ecSMiri Korenblit wowlan_status->igtk.id = 431*d1e879ecSMiri Korenblit u32_get_bits(igtk->key_flags, 432*d1e879ecSMiri Korenblit IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) + 433*d1e879ecSMiri Korenblit WOWLAN_IGTK_MIN_INDEX; 434*d1e879ecSMiri Korenblit 435*d1e879ecSMiri Korenblit memcpy(wowlan_status->igtk.key, igtk->key, sizeof(igtk->key)); 436*d1e879ecSMiri Korenblit iwl_mld_convert_mcast_ipn(&wowlan_status->igtk, igtk); 437*d1e879ecSMiri Korenblit } 438*d1e879ecSMiri Korenblit 439*d1e879ecSMiri Korenblit static void 440*d1e879ecSMiri Korenblit iwl_mld_convert_bigtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status, 441*d1e879ecSMiri Korenblit const struct iwl_wowlan_igtk_status *bigtk) 442*d1e879ecSMiri Korenblit { 443*d1e879ecSMiri Korenblit int status_idx = 0; 444*d1e879ecSMiri Korenblit 445*d1e879ecSMiri Korenblit BUILD_BUG_ON(ARRAY_SIZE(wowlan_status->bigtk) < WOWLAN_BIGTK_KEYS_NUM); 446*d1e879ecSMiri Korenblit 447*d1e879ecSMiri Korenblit for (int notif_idx = 0; notif_idx < WOWLAN_BIGTK_KEYS_NUM; 448*d1e879ecSMiri Korenblit notif_idx++) { 449*d1e879ecSMiri Korenblit if (!bigtk[notif_idx].key_len) 450*d1e879ecSMiri Korenblit continue; 451*d1e879ecSMiri Korenblit 452*d1e879ecSMiri Korenblit wowlan_status->bigtk[status_idx].len = bigtk[notif_idx].key_len; 453*d1e879ecSMiri Korenblit wowlan_status->bigtk[status_idx].flags = 454*d1e879ecSMiri Korenblit bigtk[notif_idx].key_flags; 455*d1e879ecSMiri Korenblit wowlan_status->bigtk[status_idx].id = 456*d1e879ecSMiri Korenblit u32_get_bits(bigtk[notif_idx].key_flags, 457*d1e879ecSMiri Korenblit IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) 458*d1e879ecSMiri Korenblit + WOWLAN_BIGTK_MIN_INDEX; 459*d1e879ecSMiri Korenblit 460*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(wowlan_status->bigtk[status_idx].key) < 461*d1e879ecSMiri Korenblit sizeof(bigtk[notif_idx].key)); 462*d1e879ecSMiri Korenblit memcpy(wowlan_status->bigtk[status_idx].key, 463*d1e879ecSMiri Korenblit bigtk[notif_idx].key, sizeof(bigtk[notif_idx].key)); 464*d1e879ecSMiri Korenblit iwl_mld_convert_mcast_ipn(&wowlan_status->bigtk[status_idx], 465*d1e879ecSMiri Korenblit &bigtk[notif_idx]); 466*d1e879ecSMiri Korenblit status_idx++; 467*d1e879ecSMiri Korenblit } 468*d1e879ecSMiri Korenblit } 469*d1e879ecSMiri Korenblit 470*d1e879ecSMiri Korenblit static bool 471*d1e879ecSMiri Korenblit iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld, 472*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status, 473*d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt) 474*d1e879ecSMiri Korenblit { 475*d1e879ecSMiri Korenblit const struct iwl_wowlan_info_notif *notif = (void *)pkt->data; 476*d1e879ecSMiri Korenblit u32 expected_len, len = iwl_rx_packet_payload_len(pkt); 477*d1e879ecSMiri Korenblit 478*d1e879ecSMiri Korenblit expected_len = sizeof(*notif); 479*d1e879ecSMiri Korenblit 480*d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, len < expected_len, 481*d1e879ecSMiri Korenblit "Invalid wowlan_info_notif (expected=%ud got=%ud)\n", 482*d1e879ecSMiri Korenblit expected_len, len)) 483*d1e879ecSMiri Korenblit return true; 484*d1e879ecSMiri Korenblit 485*d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, notif->tid_offloaded_tx != IWL_WOWLAN_OFFLOAD_TID, 486*d1e879ecSMiri Korenblit "Invalid tid_offloaded_tx %d\n", 487*d1e879ecSMiri Korenblit wowlan_status->tid_offloaded_tx)) 488*d1e879ecSMiri Korenblit return true; 489*d1e879ecSMiri Korenblit 490*d1e879ecSMiri Korenblit iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk, 491*d1e879ecSMiri Korenblit ¬if->gtk[0].sc); 492*d1e879ecSMiri Korenblit iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, ¬if->gtk[0].sc); 493*d1e879ecSMiri Korenblit /* only one igtk is passed by FW */ 494*d1e879ecSMiri Korenblit iwl_mld_convert_igtk_resume_data(wowlan_status, ¬if->igtk[0]); 495*d1e879ecSMiri Korenblit iwl_mld_convert_bigtk_resume_data(wowlan_status, notif->bigtk); 496*d1e879ecSMiri Korenblit 497*d1e879ecSMiri Korenblit wowlan_status->replay_ctr = le64_to_cpu(notif->replay_ctr); 498*d1e879ecSMiri Korenblit wowlan_status->pattern_number = le16_to_cpu(notif->pattern_number); 499*d1e879ecSMiri Korenblit 500*d1e879ecSMiri Korenblit wowlan_status->tid_offloaded_tx = notif->tid_offloaded_tx; 501*d1e879ecSMiri Korenblit wowlan_status->last_qos_seq = le16_to_cpu(notif->qos_seq_ctr); 502*d1e879ecSMiri Korenblit wowlan_status->num_of_gtk_rekeys = 503*d1e879ecSMiri Korenblit le32_to_cpu(notif->num_of_gtk_rekeys); 504*d1e879ecSMiri Korenblit wowlan_status->wakeup_reasons = le32_to_cpu(notif->wakeup_reasons); 505*d1e879ecSMiri Korenblit return false; 506*d1e879ecSMiri Korenblit /* TODO: mlo_links (task=MLO)*/ 507*d1e879ecSMiri Korenblit } 508*d1e879ecSMiri Korenblit 509*d1e879ecSMiri Korenblit static bool 510*d1e879ecSMiri Korenblit iwl_mld_handle_wake_pkt_notif(struct iwl_mld *mld, 511*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status, 512*d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt) 513*d1e879ecSMiri Korenblit { 514*d1e879ecSMiri Korenblit const struct iwl_wowlan_wake_pkt_notif *notif = (void *)pkt->data; 515*d1e879ecSMiri Korenblit u32 actual_size, len = iwl_rx_packet_payload_len(pkt); 516*d1e879ecSMiri Korenblit u32 expected_size = le32_to_cpu(notif->wake_packet_length); 517*d1e879ecSMiri Korenblit 518*d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, len < sizeof(*notif), 519*d1e879ecSMiri Korenblit "Invalid WoWLAN wake packet notification (expected size=%zu got=%u)\n", 520*d1e879ecSMiri Korenblit sizeof(*notif), len)) 521*d1e879ecSMiri Korenblit return true; 522*d1e879ecSMiri Korenblit 523*d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, !(wowlan_status->wakeup_reasons & 524*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT), 525*d1e879ecSMiri Korenblit "Got wake packet but wakeup reason is %x\n", 526*d1e879ecSMiri Korenblit wowlan_status->wakeup_reasons)) 527*d1e879ecSMiri Korenblit return true; 528*d1e879ecSMiri Korenblit 529*d1e879ecSMiri Korenblit actual_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, 530*d1e879ecSMiri Korenblit wake_packet); 531*d1e879ecSMiri Korenblit 532*d1e879ecSMiri Korenblit /* actual_size got the padding from the notification, remove it. */ 533*d1e879ecSMiri Korenblit if (expected_size < actual_size) 534*d1e879ecSMiri Korenblit actual_size = expected_size; 535*d1e879ecSMiri Korenblit wowlan_status->wake_packet = kmemdup(notif->wake_packet, actual_size, 536*d1e879ecSMiri Korenblit GFP_ATOMIC); 537*d1e879ecSMiri Korenblit if (!wowlan_status->wake_packet) 538*d1e879ecSMiri Korenblit return true; 539*d1e879ecSMiri Korenblit 540*d1e879ecSMiri Korenblit wowlan_status->wake_packet_length = expected_size; 541*d1e879ecSMiri Korenblit wowlan_status->wake_packet_bufsize = actual_size; 542*d1e879ecSMiri Korenblit 543*d1e879ecSMiri Korenblit return false; 544*d1e879ecSMiri Korenblit } 545*d1e879ecSMiri Korenblit 546*d1e879ecSMiri Korenblit static void 547*d1e879ecSMiri Korenblit iwl_mld_set_wake_packet(struct iwl_mld *mld, 548*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 549*d1e879ecSMiri Korenblit const struct iwl_mld_wowlan_status *wowlan_status, 550*d1e879ecSMiri Korenblit struct cfg80211_wowlan_wakeup *wakeup, 551*d1e879ecSMiri Korenblit struct sk_buff **_pkt) 552*d1e879ecSMiri Korenblit { 553*d1e879ecSMiri Korenblit int pkt_bufsize = wowlan_status->wake_packet_bufsize; 554*d1e879ecSMiri Korenblit int expected_pktlen = wowlan_status->wake_packet_length; 555*d1e879ecSMiri Korenblit const u8 *pktdata = wowlan_status->wake_packet; 556*d1e879ecSMiri Korenblit const struct ieee80211_hdr *hdr = (const void *)pktdata; 557*d1e879ecSMiri Korenblit int truncated = expected_pktlen - pkt_bufsize; 558*d1e879ecSMiri Korenblit 559*d1e879ecSMiri Korenblit if (ieee80211_is_data(hdr->frame_control)) { 560*d1e879ecSMiri Korenblit int hdrlen = ieee80211_hdrlen(hdr->frame_control); 561*d1e879ecSMiri Korenblit int ivlen = 0, icvlen = 4; /* also FCS */ 562*d1e879ecSMiri Korenblit 563*d1e879ecSMiri Korenblit struct sk_buff *pkt = alloc_skb(pkt_bufsize, GFP_KERNEL); 564*d1e879ecSMiri Korenblit *_pkt = pkt; 565*d1e879ecSMiri Korenblit if (!pkt) 566*d1e879ecSMiri Korenblit return; 567*d1e879ecSMiri Korenblit 568*d1e879ecSMiri Korenblit skb_put_data(pkt, pktdata, hdrlen); 569*d1e879ecSMiri Korenblit pktdata += hdrlen; 570*d1e879ecSMiri Korenblit pkt_bufsize -= hdrlen; 571*d1e879ecSMiri Korenblit 572*d1e879ecSMiri Korenblit /* if truncated, FCS/ICV is (partially) gone */ 573*d1e879ecSMiri Korenblit if (truncated >= icvlen) { 574*d1e879ecSMiri Korenblit truncated -= icvlen; 575*d1e879ecSMiri Korenblit icvlen = 0; 576*d1e879ecSMiri Korenblit } else { 577*d1e879ecSMiri Korenblit icvlen -= truncated; 578*d1e879ecSMiri Korenblit truncated = 0; 579*d1e879ecSMiri Korenblit } 580*d1e879ecSMiri Korenblit 581*d1e879ecSMiri Korenblit pkt_bufsize -= ivlen + icvlen; 582*d1e879ecSMiri Korenblit pktdata += ivlen; 583*d1e879ecSMiri Korenblit 584*d1e879ecSMiri Korenblit skb_put_data(pkt, pktdata, pkt_bufsize); 585*d1e879ecSMiri Korenblit 586*d1e879ecSMiri Korenblit if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) 587*d1e879ecSMiri Korenblit return; 588*d1e879ecSMiri Korenblit wakeup->packet = pkt->data; 589*d1e879ecSMiri Korenblit wakeup->packet_present_len = pkt->len; 590*d1e879ecSMiri Korenblit wakeup->packet_len = pkt->len - truncated; 591*d1e879ecSMiri Korenblit wakeup->packet_80211 = false; 592*d1e879ecSMiri Korenblit } else { 593*d1e879ecSMiri Korenblit int fcslen = 4; 594*d1e879ecSMiri Korenblit 595*d1e879ecSMiri Korenblit if (truncated >= 4) { 596*d1e879ecSMiri Korenblit truncated -= 4; 597*d1e879ecSMiri Korenblit fcslen = 0; 598*d1e879ecSMiri Korenblit } else { 599*d1e879ecSMiri Korenblit fcslen -= truncated; 600*d1e879ecSMiri Korenblit truncated = 0; 601*d1e879ecSMiri Korenblit } 602*d1e879ecSMiri Korenblit pkt_bufsize -= fcslen; 603*d1e879ecSMiri Korenblit wakeup->packet = wowlan_status->wake_packet; 604*d1e879ecSMiri Korenblit wakeup->packet_present_len = pkt_bufsize; 605*d1e879ecSMiri Korenblit wakeup->packet_len = expected_pktlen - truncated; 606*d1e879ecSMiri Korenblit wakeup->packet_80211 = true; 607*d1e879ecSMiri Korenblit } 608*d1e879ecSMiri Korenblit } 609*d1e879ecSMiri Korenblit 610*d1e879ecSMiri Korenblit static void 611*d1e879ecSMiri Korenblit iwl_mld_report_wowlan_wakeup(struct iwl_mld *mld, 612*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 613*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status) 614*d1e879ecSMiri Korenblit { 615*d1e879ecSMiri Korenblit struct sk_buff *pkt = NULL; 616*d1e879ecSMiri Korenblit struct cfg80211_wowlan_wakeup wakeup = { 617*d1e879ecSMiri Korenblit .pattern_idx = -1, 618*d1e879ecSMiri Korenblit }; 619*d1e879ecSMiri Korenblit u32 reasons = wowlan_status->wakeup_reasons; 620*d1e879ecSMiri Korenblit 621*d1e879ecSMiri Korenblit if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { 622*d1e879ecSMiri Korenblit ieee80211_report_wowlan_wakeup(vif, NULL, GFP_KERNEL); 623*d1e879ecSMiri Korenblit return; 624*d1e879ecSMiri Korenblit } 625*d1e879ecSMiri Korenblit 626*d1e879ecSMiri Korenblit pm_wakeup_event(mld->dev, 0); 627*d1e879ecSMiri Korenblit 628*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) 629*d1e879ecSMiri Korenblit wakeup.magic_pkt = true; 630*d1e879ecSMiri Korenblit 631*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) 632*d1e879ecSMiri Korenblit wakeup.pattern_idx = 633*d1e879ecSMiri Korenblit wowlan_status->pattern_number; 634*d1e879ecSMiri Korenblit 635*d1e879ecSMiri Korenblit if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | 636*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH | 637*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)) 638*d1e879ecSMiri Korenblit wakeup.disconnect = true; 639*d1e879ecSMiri Korenblit 640*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) 641*d1e879ecSMiri Korenblit wakeup.gtk_rekey_failure = true; 642*d1e879ecSMiri Korenblit 643*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) 644*d1e879ecSMiri Korenblit wakeup.rfkill_release = true; 645*d1e879ecSMiri Korenblit 646*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) 647*d1e879ecSMiri Korenblit wakeup.eap_identity_req = true; 648*d1e879ecSMiri Korenblit 649*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) 650*d1e879ecSMiri Korenblit wakeup.four_way_handshake = true; 651*d1e879ecSMiri Korenblit 652*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS) 653*d1e879ecSMiri Korenblit wakeup.tcp_connlost = true; 654*d1e879ecSMiri Korenblit 655*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE) 656*d1e879ecSMiri Korenblit wakeup.tcp_nomoretokens = true; 657*d1e879ecSMiri Korenblit 658*d1e879ecSMiri Korenblit if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET) 659*d1e879ecSMiri Korenblit wakeup.tcp_match = true; 660*d1e879ecSMiri Korenblit 661*d1e879ecSMiri Korenblit if (reasons & IWL_WAKEUP_BY_11W_UNPROTECTED_DEAUTH_OR_DISASSOC) 662*d1e879ecSMiri Korenblit wakeup.unprot_deauth_disassoc = true; 663*d1e879ecSMiri Korenblit 664*d1e879ecSMiri Korenblit if (wowlan_status->wake_packet) 665*d1e879ecSMiri Korenblit iwl_mld_set_wake_packet(mld, vif, wowlan_status, &wakeup, &pkt); 666*d1e879ecSMiri Korenblit 667*d1e879ecSMiri Korenblit ieee80211_report_wowlan_wakeup(vif, &wakeup, GFP_KERNEL); 668*d1e879ecSMiri Korenblit kfree_skb(pkt); 669*d1e879ecSMiri Korenblit } 670*d1e879ecSMiri Korenblit 671*d1e879ecSMiri Korenblit static void 672*d1e879ecSMiri Korenblit iwl_mld_set_key_rx_seq_tids(struct ieee80211_key_conf *key, 673*d1e879ecSMiri Korenblit struct ieee80211_key_seq *seq) 674*d1e879ecSMiri Korenblit { 675*d1e879ecSMiri Korenblit int tid; 676*d1e879ecSMiri Korenblit 677*d1e879ecSMiri Korenblit for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) 678*d1e879ecSMiri Korenblit ieee80211_set_key_rx_seq(key, tid, &seq[tid]); 679*d1e879ecSMiri Korenblit } 680*d1e879ecSMiri Korenblit 681*d1e879ecSMiri Korenblit static void 682*d1e879ecSMiri Korenblit iwl_mld_set_key_rx_seq(struct ieee80211_key_conf *key, 683*d1e879ecSMiri Korenblit struct iwl_mld_mcast_key_data *key_data) 684*d1e879ecSMiri Korenblit { 685*d1e879ecSMiri Korenblit switch (key->cipher) { 686*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_CCMP: 687*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_GCMP: 688*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_GCMP_256: 689*d1e879ecSMiri Korenblit iwl_mld_set_key_rx_seq_tids(key, 690*d1e879ecSMiri Korenblit key_data->gtk.aes_seq); 691*d1e879ecSMiri Korenblit break; 692*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_TKIP: 693*d1e879ecSMiri Korenblit iwl_mld_set_key_rx_seq_tids(key, 694*d1e879ecSMiri Korenblit key_data->gtk.tkip_seq); 695*d1e879ecSMiri Korenblit break; 696*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_GMAC_128: 697*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_GMAC_256: 698*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_CMAC_256: 699*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_AES_CMAC: 700*d1e879ecSMiri Korenblit /* igtk/bigtk ciphers*/ 701*d1e879ecSMiri Korenblit ieee80211_set_key_rx_seq(key, 0, 702*d1e879ecSMiri Korenblit &key_data->igtk_bigtk.cmac_gmac_seq); 703*d1e879ecSMiri Korenblit break; 704*d1e879ecSMiri Korenblit default: 705*d1e879ecSMiri Korenblit WARN_ON(1); 706*d1e879ecSMiri Korenblit } 707*d1e879ecSMiri Korenblit } 708*d1e879ecSMiri Korenblit 709*d1e879ecSMiri Korenblit static void 710*d1e879ecSMiri Korenblit iwl_mld_d3_update_mcast_key(struct iwl_mld *mld, 711*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 712*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status, 713*d1e879ecSMiri Korenblit struct ieee80211_key_conf *key, 714*d1e879ecSMiri Korenblit struct iwl_mld_mcast_key_data *key_data) 715*d1e879ecSMiri Korenblit { 716*d1e879ecSMiri Korenblit if (key->keyidx != key_data->id && 717*d1e879ecSMiri Korenblit (key->keyidx < 4 || key->keyidx > 5)) { 718*d1e879ecSMiri Korenblit IWL_ERR(mld, 719*d1e879ecSMiri Korenblit "Unexpected keyId mismatch. Old keyId:%d, New keyId:%d\n", 720*d1e879ecSMiri Korenblit key->keyidx, key_data->id); 721*d1e879ecSMiri Korenblit return; 722*d1e879ecSMiri Korenblit } 723*d1e879ecSMiri Korenblit 724*d1e879ecSMiri Korenblit /* All installed keys are sent by the FW, even weren't 725*d1e879ecSMiri Korenblit * rekeyed during D3. 726*d1e879ecSMiri Korenblit * We remove an existing key if it has the same index as 727*d1e879ecSMiri Korenblit * a new key and a rekey has occurred during d3 728*d1e879ecSMiri Korenblit */ 729*d1e879ecSMiri Korenblit if (wowlan_status->num_of_gtk_rekeys && key_data->len) { 730*d1e879ecSMiri Korenblit if (key->keyidx == 4 || key->keyidx == 5) { 731*d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = 732*d1e879ecSMiri Korenblit iwl_mld_vif_from_mac80211(vif); 733*d1e879ecSMiri Korenblit struct iwl_mld_link *mld_link; 734*d1e879ecSMiri Korenblit int link_id = vif->active_links ? 735*d1e879ecSMiri Korenblit __ffs(vif->active_links) : 0; 736*d1e879ecSMiri Korenblit 737*d1e879ecSMiri Korenblit mld_link = iwl_mld_link_dereference_check(mld_vif, 738*d1e879ecSMiri Korenblit link_id); 739*d1e879ecSMiri Korenblit if (WARN_ON(!mld_link)) 740*d1e879ecSMiri Korenblit return; 741*d1e879ecSMiri Korenblit 742*d1e879ecSMiri Korenblit if (mld_link->igtk == key) 743*d1e879ecSMiri Korenblit mld_link->igtk = NULL; 744*d1e879ecSMiri Korenblit mld->num_igtks--; 745*d1e879ecSMiri Korenblit } 746*d1e879ecSMiri Korenblit 747*d1e879ecSMiri Korenblit ieee80211_remove_key(key); 748*d1e879ecSMiri Korenblit return; 749*d1e879ecSMiri Korenblit } 750*d1e879ecSMiri Korenblit 751*d1e879ecSMiri Korenblit iwl_mld_set_key_rx_seq(key, key_data); 752*d1e879ecSMiri Korenblit } 753*d1e879ecSMiri Korenblit 754*d1e879ecSMiri Korenblit static void 755*d1e879ecSMiri Korenblit iwl_mld_update_ptk_rx_seq(struct iwl_mld *mld, 756*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status, 757*d1e879ecSMiri Korenblit struct ieee80211_sta *sta, 758*d1e879ecSMiri Korenblit struct ieee80211_key_conf *key, 759*d1e879ecSMiri Korenblit bool is_tkip) 760*d1e879ecSMiri Korenblit { 761*d1e879ecSMiri Korenblit struct iwl_mld_sta *mld_sta = 762*d1e879ecSMiri Korenblit iwl_mld_sta_from_mac80211(sta); 763*d1e879ecSMiri Korenblit struct iwl_mld_ptk_pn *mld_ptk_pn = 764*d1e879ecSMiri Korenblit wiphy_dereference(mld->wiphy, 765*d1e879ecSMiri Korenblit mld_sta->ptk_pn[key->keyidx]); 766*d1e879ecSMiri Korenblit 767*d1e879ecSMiri Korenblit iwl_mld_set_key_rx_seq_tids(key, is_tkip ? 768*d1e879ecSMiri Korenblit wowlan_status->ptk.tkip_seq : 769*d1e879ecSMiri Korenblit wowlan_status->ptk.aes_seq); 770*d1e879ecSMiri Korenblit if (is_tkip) 771*d1e879ecSMiri Korenblit return; 772*d1e879ecSMiri Korenblit 773*d1e879ecSMiri Korenblit if (WARN_ON(!mld_ptk_pn)) 774*d1e879ecSMiri Korenblit return; 775*d1e879ecSMiri Korenblit 776*d1e879ecSMiri Korenblit for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { 777*d1e879ecSMiri Korenblit for (int i = 1; i < mld->trans->num_rx_queues; i++) 778*d1e879ecSMiri Korenblit memcpy(mld_ptk_pn->q[i].pn[tid], 779*d1e879ecSMiri Korenblit wowlan_status->ptk.aes_seq[tid].ccmp.pn, 780*d1e879ecSMiri Korenblit IEEE80211_CCMP_PN_LEN); 781*d1e879ecSMiri Korenblit } 782*d1e879ecSMiri Korenblit } 783*d1e879ecSMiri Korenblit 784*d1e879ecSMiri Korenblit static void 785*d1e879ecSMiri Korenblit iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, 786*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 787*d1e879ecSMiri Korenblit struct ieee80211_sta *sta, 788*d1e879ecSMiri Korenblit struct ieee80211_key_conf *key, 789*d1e879ecSMiri Korenblit void *_data) 790*d1e879ecSMiri Korenblit { 791*d1e879ecSMiri Korenblit struct iwl_mld_resume_key_iter_data *data = _data; 792*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status; 793*d1e879ecSMiri Korenblit u8 status_idx; 794*d1e879ecSMiri Korenblit 795*d1e879ecSMiri Korenblit /* TODO: check key link id (task=MLO) */ 796*d1e879ecSMiri Korenblit if (data->unhandled_cipher) 797*d1e879ecSMiri Korenblit return; 798*d1e879ecSMiri Korenblit 799*d1e879ecSMiri Korenblit switch (key->cipher) { 800*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_WEP40: 801*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_WEP104: 802*d1e879ecSMiri Korenblit /* ignore WEP completely, nothing to do */ 803*d1e879ecSMiri Korenblit return; 804*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_CCMP: 805*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_GCMP: 806*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_GCMP_256: 807*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_TKIP: 808*d1e879ecSMiri Korenblit if (sta) { 809*d1e879ecSMiri Korenblit iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status, 810*d1e879ecSMiri Korenblit sta, key, 811*d1e879ecSMiri Korenblit key->cipher == 812*d1e879ecSMiri Korenblit WLAN_CIPHER_SUITE_TKIP); 813*d1e879ecSMiri Korenblit return; 814*d1e879ecSMiri Korenblit } 815*d1e879ecSMiri Korenblit 816*d1e879ecSMiri Korenblit if (WARN_ON(data->gtk_cipher && 817*d1e879ecSMiri Korenblit data->gtk_cipher != key->cipher)) 818*d1e879ecSMiri Korenblit return; 819*d1e879ecSMiri Korenblit 820*d1e879ecSMiri Korenblit data->gtk_cipher = key->cipher; 821*d1e879ecSMiri Korenblit status_idx = key->keyidx == wowlan_status->gtk[1].id; 822*d1e879ecSMiri Korenblit iwl_mld_d3_update_mcast_key(data->mld, vif, wowlan_status, key, 823*d1e879ecSMiri Korenblit &wowlan_status->gtk[status_idx]); 824*d1e879ecSMiri Korenblit break; 825*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_GMAC_128: 826*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_GMAC_256: 827*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_CMAC_256: 828*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_AES_CMAC: 829*d1e879ecSMiri Korenblit if (key->keyidx == 4 || key->keyidx == 5) { 830*d1e879ecSMiri Korenblit if (WARN_ON(data->igtk_cipher && 831*d1e879ecSMiri Korenblit data->igtk_cipher != key->cipher)) 832*d1e879ecSMiri Korenblit return; 833*d1e879ecSMiri Korenblit 834*d1e879ecSMiri Korenblit data->igtk_cipher = key->cipher; 835*d1e879ecSMiri Korenblit iwl_mld_d3_update_mcast_key(data->mld, vif, 836*d1e879ecSMiri Korenblit wowlan_status, 837*d1e879ecSMiri Korenblit key, &wowlan_status->igtk); 838*d1e879ecSMiri Korenblit } 839*d1e879ecSMiri Korenblit if (key->keyidx == 6 || key->keyidx == 7) { 840*d1e879ecSMiri Korenblit if (WARN_ON(data->bigtk_cipher && 841*d1e879ecSMiri Korenblit data->bigtk_cipher != key->cipher)) 842*d1e879ecSMiri Korenblit return; 843*d1e879ecSMiri Korenblit 844*d1e879ecSMiri Korenblit data->bigtk_cipher = key->cipher; 845*d1e879ecSMiri Korenblit status_idx = key->keyidx == wowlan_status->bigtk[1].id; 846*d1e879ecSMiri Korenblit iwl_mld_d3_update_mcast_key(data->mld, vif, 847*d1e879ecSMiri Korenblit wowlan_status, key, 848*d1e879ecSMiri Korenblit &wowlan_status->bigtk[status_idx]); 849*d1e879ecSMiri Korenblit } 850*d1e879ecSMiri Korenblit break; 851*d1e879ecSMiri Korenblit default: 852*d1e879ecSMiri Korenblit data->unhandled_cipher = true; 853*d1e879ecSMiri Korenblit return; 854*d1e879ecSMiri Korenblit } 855*d1e879ecSMiri Korenblit data->num_keys++; 856*d1e879ecSMiri Korenblit } 857*d1e879ecSMiri Korenblit 858*d1e879ecSMiri Korenblit static bool 859*d1e879ecSMiri Korenblit iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, 860*d1e879ecSMiri Korenblit struct iwl_mld *mld, 861*d1e879ecSMiri Korenblit struct iwl_mld_mcast_key_data *key_data, 862*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf, 863*d1e879ecSMiri Korenblit u32 cipher) 864*d1e879ecSMiri Korenblit { 865*d1e879ecSMiri Korenblit struct ieee80211_key_conf *key_config; 866*d1e879ecSMiri Korenblit struct { 867*d1e879ecSMiri Korenblit struct ieee80211_key_conf conf; 868*d1e879ecSMiri Korenblit u8 key[WOWLAN_KEY_MAX_SIZE]; 869*d1e879ecSMiri Korenblit } conf = { 870*d1e879ecSMiri Korenblit .conf.cipher = cipher, 871*d1e879ecSMiri Korenblit .conf.keyidx = key_data->id, 872*d1e879ecSMiri Korenblit }; 873*d1e879ecSMiri Korenblit int link_id = vif->active_links ? __ffs(vif->active_links) : -1; 874*d1e879ecSMiri Korenblit 875*d1e879ecSMiri Korenblit BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP); 876*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP); 877*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256); 878*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP); 879*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_128); 880*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_256); 881*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_AES_CMAC); 882*d1e879ecSMiri Korenblit BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key)); 883*d1e879ecSMiri Korenblit 884*d1e879ecSMiri Korenblit if (!key_data->len) 885*d1e879ecSMiri Korenblit return true; 886*d1e879ecSMiri Korenblit 887*d1e879ecSMiri Korenblit switch (cipher) { 888*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_CCMP: 889*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_GCMP: 890*d1e879ecSMiri Korenblit conf.conf.keylen = WLAN_KEY_LEN_CCMP; 891*d1e879ecSMiri Korenblit break; 892*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_GCMP_256: 893*d1e879ecSMiri Korenblit conf.conf.keylen = WLAN_KEY_LEN_GCMP_256; 894*d1e879ecSMiri Korenblit break; 895*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_TKIP: 896*d1e879ecSMiri Korenblit conf.conf.keylen = WLAN_KEY_LEN_TKIP; 897*d1e879ecSMiri Korenblit break; 898*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_GMAC_128: 899*d1e879ecSMiri Korenblit conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128; 900*d1e879ecSMiri Korenblit break; 901*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_GMAC_256: 902*d1e879ecSMiri Korenblit conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_256; 903*d1e879ecSMiri Korenblit break; 904*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_AES_CMAC: 905*d1e879ecSMiri Korenblit conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC; 906*d1e879ecSMiri Korenblit break; 907*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_CMAC_256: 908*d1e879ecSMiri Korenblit conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256; 909*d1e879ecSMiri Korenblit break; 910*d1e879ecSMiri Korenblit default: 911*d1e879ecSMiri Korenblit WARN_ON(1); 912*d1e879ecSMiri Korenblit } 913*d1e879ecSMiri Korenblit 914*d1e879ecSMiri Korenblit memcpy(conf.conf.key, key_data->key, conf.conf.keylen); 915*d1e879ecSMiri Korenblit key_config = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id); 916*d1e879ecSMiri Korenblit if (IS_ERR(key_config)) 917*d1e879ecSMiri Korenblit return false; 918*d1e879ecSMiri Korenblit 919*d1e879ecSMiri Korenblit iwl_mld_set_key_rx_seq(key_config, key_data); 920*d1e879ecSMiri Korenblit 921*d1e879ecSMiri Korenblit /* The FW holds only one igtk so we keep track of the valid one */ 922*d1e879ecSMiri Korenblit if (key_config->keyidx == 4 || key_config->keyidx == 5) { 923*d1e879ecSMiri Korenblit struct iwl_mld_link *mld_link = 924*d1e879ecSMiri Korenblit iwl_mld_link_from_mac80211(link_conf); 925*d1e879ecSMiri Korenblit mld_link->igtk = key_config; 926*d1e879ecSMiri Korenblit mld->num_igtks++; 927*d1e879ecSMiri Korenblit } 928*d1e879ecSMiri Korenblit return true; 929*d1e879ecSMiri Korenblit } 930*d1e879ecSMiri Korenblit 931*d1e879ecSMiri Korenblit static bool 932*d1e879ecSMiri Korenblit iwl_mld_add_all_rekeys(struct ieee80211_vif *vif, 933*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status, 934*d1e879ecSMiri Korenblit struct iwl_mld_resume_key_iter_data *key_iter_data, 935*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf) 936*d1e879ecSMiri Korenblit { 937*d1e879ecSMiri Korenblit int i; 938*d1e879ecSMiri Korenblit 939*d1e879ecSMiri Korenblit for (i = 0; i < ARRAY_SIZE(wowlan_status->gtk); i++) 940*d1e879ecSMiri Korenblit if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, 941*d1e879ecSMiri Korenblit &wowlan_status->gtk[i], 942*d1e879ecSMiri Korenblit link_conf, 943*d1e879ecSMiri Korenblit key_iter_data->gtk_cipher)) 944*d1e879ecSMiri Korenblit return false; 945*d1e879ecSMiri Korenblit 946*d1e879ecSMiri Korenblit if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, 947*d1e879ecSMiri Korenblit &wowlan_status->igtk, 948*d1e879ecSMiri Korenblit link_conf, key_iter_data->igtk_cipher)) 949*d1e879ecSMiri Korenblit return false; 950*d1e879ecSMiri Korenblit 951*d1e879ecSMiri Korenblit for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++) 952*d1e879ecSMiri Korenblit if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, 953*d1e879ecSMiri Korenblit &wowlan_status->bigtk[i], 954*d1e879ecSMiri Korenblit link_conf, 955*d1e879ecSMiri Korenblit key_iter_data->bigtk_cipher)) 956*d1e879ecSMiri Korenblit return false; 957*d1e879ecSMiri Korenblit 958*d1e879ecSMiri Korenblit return true; 959*d1e879ecSMiri Korenblit } 960*d1e879ecSMiri Korenblit 961*d1e879ecSMiri Korenblit static bool 962*d1e879ecSMiri Korenblit iwl_mld_update_sec_keys(struct iwl_mld *mld, 963*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 964*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status) 965*d1e879ecSMiri Korenblit { 966*d1e879ecSMiri Korenblit int link_id = vif->active_links ? __ffs(vif->active_links) : 0; 967*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf = 968*d1e879ecSMiri Korenblit link_conf_dereference_protected(vif, link_id); 969*d1e879ecSMiri Korenblit __be64 replay_ctr = cpu_to_be64(wowlan_status->replay_ctr); 970*d1e879ecSMiri Korenblit struct iwl_mld_resume_key_iter_data key_iter_data = { 971*d1e879ecSMiri Korenblit .mld = mld, 972*d1e879ecSMiri Korenblit .wowlan_status = wowlan_status, 973*d1e879ecSMiri Korenblit }; 974*d1e879ecSMiri Korenblit 975*d1e879ecSMiri Korenblit if (WARN_ON(!link_conf)) 976*d1e879ecSMiri Korenblit return false; 977*d1e879ecSMiri Korenblit 978*d1e879ecSMiri Korenblit ieee80211_iter_keys(mld->hw, vif, iwl_mld_resume_keys_iter, 979*d1e879ecSMiri Korenblit &key_iter_data); 980*d1e879ecSMiri Korenblit 981*d1e879ecSMiri Korenblit if (key_iter_data.unhandled_cipher) 982*d1e879ecSMiri Korenblit return false; 983*d1e879ecSMiri Korenblit 984*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, 985*d1e879ecSMiri Korenblit "Number of installed keys: %d, Number of rekeys: %d\n", 986*d1e879ecSMiri Korenblit key_iter_data.num_keys, 987*d1e879ecSMiri Korenblit wowlan_status->num_of_gtk_rekeys); 988*d1e879ecSMiri Korenblit 989*d1e879ecSMiri Korenblit if (!key_iter_data.num_keys || !wowlan_status->num_of_gtk_rekeys) 990*d1e879ecSMiri Korenblit return true; 991*d1e879ecSMiri Korenblit 992*d1e879ecSMiri Korenblit iwl_mld_add_all_rekeys(vif, wowlan_status, &key_iter_data, 993*d1e879ecSMiri Korenblit link_conf); 994*d1e879ecSMiri Korenblit 995*d1e879ecSMiri Korenblit ieee80211_gtk_rekey_notify(vif, link_conf->bssid, 996*d1e879ecSMiri Korenblit (void *)&replay_ctr, GFP_KERNEL); 997*d1e879ecSMiri Korenblit /* TODO: MLO rekey (task=MLO) */ 998*d1e879ecSMiri Korenblit return true; 999*d1e879ecSMiri Korenblit } 1000*d1e879ecSMiri Korenblit 1001*d1e879ecSMiri Korenblit static bool 1002*d1e879ecSMiri Korenblit iwl_mld_process_wowlan_status(struct iwl_mld *mld, 1003*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1004*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_status *wowlan_status) 1005*d1e879ecSMiri Korenblit { 1006*d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 1007*d1e879ecSMiri Korenblit struct ieee80211_sta *ap_sta = mld_vif->ap_sta; 1008*d1e879ecSMiri Korenblit struct iwl_mld_txq *mld_txq; 1009*d1e879ecSMiri Korenblit 1010*d1e879ecSMiri Korenblit iwl_mld_report_wowlan_wakeup(mld, vif, wowlan_status); 1011*d1e879ecSMiri Korenblit 1012*d1e879ecSMiri Korenblit if (WARN_ON(!ap_sta)) 1013*d1e879ecSMiri Korenblit return false; 1014*d1e879ecSMiri Korenblit 1015*d1e879ecSMiri Korenblit mld_txq = 1016*d1e879ecSMiri Korenblit iwl_mld_txq_from_mac80211(ap_sta->txq[wowlan_status->tid_offloaded_tx]); 1017*d1e879ecSMiri Korenblit 1018*d1e879ecSMiri Korenblit /* Update the pointers of the Tx queue that may have moved during 1019*d1e879ecSMiri Korenblit * suspend if the firmware sent frames. 1020*d1e879ecSMiri Korenblit * The firmware stores last-used value, we store next value. 1021*d1e879ecSMiri Korenblit */ 1022*d1e879ecSMiri Korenblit WARN_ON(!mld_txq->status.allocated); 1023*d1e879ecSMiri Korenblit iwl_trans_set_q_ptrs(mld->trans, mld_txq->fw_id, 1024*d1e879ecSMiri Korenblit (wowlan_status->last_qos_seq + 1025*d1e879ecSMiri Korenblit 0x10) >> 4); 1026*d1e879ecSMiri Korenblit 1027*d1e879ecSMiri Korenblit if (!iwl_mld_update_sec_keys(mld, vif, wowlan_status)) 1028*d1e879ecSMiri Korenblit return false; 1029*d1e879ecSMiri Korenblit 1030*d1e879ecSMiri Korenblit if (wowlan_status->wakeup_reasons & 1031*d1e879ecSMiri Korenblit (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | 1032*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH | 1033*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)) 1034*d1e879ecSMiri Korenblit return false; 1035*d1e879ecSMiri Korenblit 1036*d1e879ecSMiri Korenblit return true; 1037*d1e879ecSMiri Korenblit } 1038*d1e879ecSMiri Korenblit 1039*d1e879ecSMiri Korenblit static bool 1040*d1e879ecSMiri Korenblit iwl_mld_netdetect_match_info_handler(struct iwl_mld *mld, 1041*d1e879ecSMiri Korenblit struct iwl_mld_resume_data *resume_data, 1042*d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt) 1043*d1e879ecSMiri Korenblit { 1044*d1e879ecSMiri Korenblit struct iwl_mld_netdetect_res *results = resume_data->netdetect_res; 1045*d1e879ecSMiri Korenblit const struct iwl_scan_offload_match_info *notif = (void *)pkt->data; 1046*d1e879ecSMiri Korenblit u32 len = iwl_rx_packet_payload_len(pkt); 1047*d1e879ecSMiri Korenblit 1048*d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, !mld->netdetect, 1049*d1e879ecSMiri Korenblit "Got scan match info notif when mld->netdetect==%d\n", 1050*d1e879ecSMiri Korenblit mld->netdetect)) 1051*d1e879ecSMiri Korenblit return true; 1052*d1e879ecSMiri Korenblit 1053*d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, len < sizeof(*notif), 1054*d1e879ecSMiri Korenblit "Invalid scan offload match notif of length: %d\n", 1055*d1e879ecSMiri Korenblit len)) 1056*d1e879ecSMiri Korenblit return true; 1057*d1e879ecSMiri Korenblit 1058*d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, resume_data->wowlan_status->wakeup_reasons != 1059*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS, 1060*d1e879ecSMiri Korenblit "Ignore scan match info: unexpected wakeup reason (expected=0x%x got=0x%x)\n", 1061*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS, 1062*d1e879ecSMiri Korenblit resume_data->wowlan_status->wakeup_reasons)) 1063*d1e879ecSMiri Korenblit return true; 1064*d1e879ecSMiri Korenblit 1065*d1e879ecSMiri Korenblit results->matched_profiles = le32_to_cpu(notif->matched_profiles); 1066*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, "number of matched profiles=%u\n", 1067*d1e879ecSMiri Korenblit results->matched_profiles); 1068*d1e879ecSMiri Korenblit 1069*d1e879ecSMiri Korenblit if (results->matched_profiles) 1070*d1e879ecSMiri Korenblit memcpy(results->matches, notif->matches, 1071*d1e879ecSMiri Korenblit NETDETECT_QUERY_BUF_LEN); 1072*d1e879ecSMiri Korenblit 1073*d1e879ecSMiri Korenblit /* No scan should be active at this point */ 1074*d1e879ecSMiri Korenblit mld->scan.status = 0; 1075*d1e879ecSMiri Korenblit memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status)); 1076*d1e879ecSMiri Korenblit return false; 1077*d1e879ecSMiri Korenblit } 1078*d1e879ecSMiri Korenblit 1079*d1e879ecSMiri Korenblit static void 1080*d1e879ecSMiri Korenblit iwl_mld_set_netdetect_info(struct iwl_mld *mld, 1081*d1e879ecSMiri Korenblit const struct cfg80211_sched_scan_request *netdetect_cfg, 1082*d1e879ecSMiri Korenblit struct cfg80211_wowlan_nd_info *netdetect_info, 1083*d1e879ecSMiri Korenblit struct iwl_mld_netdetect_res *netdetect_res, 1084*d1e879ecSMiri Korenblit unsigned long matched_profiles) 1085*d1e879ecSMiri Korenblit { 1086*d1e879ecSMiri Korenblit int i; 1087*d1e879ecSMiri Korenblit 1088*d1e879ecSMiri Korenblit for_each_set_bit(i, &matched_profiles, netdetect_cfg->n_match_sets) { 1089*d1e879ecSMiri Korenblit struct cfg80211_wowlan_nd_match *match; 1090*d1e879ecSMiri Korenblit int idx, j, n_channels = 0; 1091*d1e879ecSMiri Korenblit struct iwl_scan_offload_profile_match *matches = 1092*d1e879ecSMiri Korenblit (void *)netdetect_res->matches; 1093*d1e879ecSMiri Korenblit 1094*d1e879ecSMiri Korenblit for (int k = 0; k < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; k++) 1095*d1e879ecSMiri Korenblit n_channels += 1096*d1e879ecSMiri Korenblit hweight8(matches[i].matching_channels[k]); 1097*d1e879ecSMiri Korenblit match = kzalloc(struct_size(match, channels, n_channels), 1098*d1e879ecSMiri Korenblit GFP_KERNEL); 1099*d1e879ecSMiri Korenblit if (!match) 1100*d1e879ecSMiri Korenblit return; 1101*d1e879ecSMiri Korenblit 1102*d1e879ecSMiri Korenblit netdetect_info->matches[netdetect_info->n_matches++] = match; 1103*d1e879ecSMiri Korenblit 1104*d1e879ecSMiri Korenblit /* We inverted the order of the SSIDs in the scan 1105*d1e879ecSMiri Korenblit * request, so invert the index here. 1106*d1e879ecSMiri Korenblit */ 1107*d1e879ecSMiri Korenblit idx = netdetect_cfg->n_match_sets - i - 1; 1108*d1e879ecSMiri Korenblit match->ssid.ssid_len = 1109*d1e879ecSMiri Korenblit netdetect_cfg->match_sets[idx].ssid.ssid_len; 1110*d1e879ecSMiri Korenblit memcpy(match->ssid.ssid, 1111*d1e879ecSMiri Korenblit netdetect_cfg->match_sets[idx].ssid.ssid, 1112*d1e879ecSMiri Korenblit match->ssid.ssid_len); 1113*d1e879ecSMiri Korenblit 1114*d1e879ecSMiri Korenblit if (netdetect_cfg->n_channels < n_channels) 1115*d1e879ecSMiri Korenblit continue; 1116*d1e879ecSMiri Korenblit 1117*d1e879ecSMiri Korenblit for_each_set_bit(j, 1118*d1e879ecSMiri Korenblit (unsigned long *)&matches[i].matching_channels[0], 1119*d1e879ecSMiri Korenblit sizeof(matches[i].matching_channels)) 1120*d1e879ecSMiri Korenblit match->channels[match->n_channels++] = 1121*d1e879ecSMiri Korenblit netdetect_cfg->channels[j]->center_freq; 1122*d1e879ecSMiri Korenblit } 1123*d1e879ecSMiri Korenblit } 1124*d1e879ecSMiri Korenblit 1125*d1e879ecSMiri Korenblit static void 1126*d1e879ecSMiri Korenblit iwl_mld_process_netdetect_res(struct iwl_mld *mld, 1127*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1128*d1e879ecSMiri Korenblit struct iwl_mld_resume_data *resume_data) 1129*d1e879ecSMiri Korenblit { 1130*d1e879ecSMiri Korenblit struct cfg80211_wowlan_nd_info *netdetect_info = NULL; 1131*d1e879ecSMiri Korenblit const struct cfg80211_sched_scan_request *netdetect_cfg; 1132*d1e879ecSMiri Korenblit struct cfg80211_wowlan_wakeup wakeup = { 1133*d1e879ecSMiri Korenblit .pattern_idx = -1, 1134*d1e879ecSMiri Korenblit }; 1135*d1e879ecSMiri Korenblit struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; 1136*d1e879ecSMiri Korenblit unsigned long matched_profiles; 1137*d1e879ecSMiri Korenblit u32 wakeup_reasons; 1138*d1e879ecSMiri Korenblit int n_matches; 1139*d1e879ecSMiri Korenblit 1140*d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 1141*d1e879ecSMiri Korenblit 1142*d1e879ecSMiri Korenblit if (WARN_ON(!mld->wiphy->wowlan_config || 1143*d1e879ecSMiri Korenblit !mld->wiphy->wowlan_config->nd_config)) { 1144*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, 1145*d1e879ecSMiri Korenblit "Netdetect isn't configured on resume flow\n"); 1146*d1e879ecSMiri Korenblit goto out; 1147*d1e879ecSMiri Korenblit } 1148*d1e879ecSMiri Korenblit 1149*d1e879ecSMiri Korenblit netdetect_cfg = mld->wiphy->wowlan_config->nd_config; 1150*d1e879ecSMiri Korenblit wakeup_reasons = resume_data->wowlan_status->wakeup_reasons; 1151*d1e879ecSMiri Korenblit 1152*d1e879ecSMiri Korenblit if (wakeup_reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) 1153*d1e879ecSMiri Korenblit wakeup.rfkill_release = true; 1154*d1e879ecSMiri Korenblit 1155*d1e879ecSMiri Korenblit if (wakeup_reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) 1156*d1e879ecSMiri Korenblit goto out; 1157*d1e879ecSMiri Korenblit 1158*d1e879ecSMiri Korenblit if (!resume_data->netdetect_res->matched_profiles) { 1159*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, 1160*d1e879ecSMiri Korenblit "Netdetect results aren't valid\n"); 1161*d1e879ecSMiri Korenblit wakeup_report = NULL; 1162*d1e879ecSMiri Korenblit goto out; 1163*d1e879ecSMiri Korenblit } 1164*d1e879ecSMiri Korenblit 1165*d1e879ecSMiri Korenblit matched_profiles = resume_data->netdetect_res->matched_profiles; 1166*d1e879ecSMiri Korenblit if (!netdetect_cfg->n_match_sets) { 1167*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, 1168*d1e879ecSMiri Korenblit "No netdetect match sets are configured\n"); 1169*d1e879ecSMiri Korenblit goto out; 1170*d1e879ecSMiri Korenblit } 1171*d1e879ecSMiri Korenblit n_matches = hweight_long(matched_profiles); 1172*d1e879ecSMiri Korenblit netdetect_info = kzalloc(struct_size(netdetect_info, matches, 1173*d1e879ecSMiri Korenblit n_matches), GFP_KERNEL); 1174*d1e879ecSMiri Korenblit if (netdetect_info) 1175*d1e879ecSMiri Korenblit iwl_mld_set_netdetect_info(mld, netdetect_cfg, netdetect_info, 1176*d1e879ecSMiri Korenblit resume_data->netdetect_res, 1177*d1e879ecSMiri Korenblit matched_profiles); 1178*d1e879ecSMiri Korenblit 1179*d1e879ecSMiri Korenblit wakeup.net_detect = netdetect_info; 1180*d1e879ecSMiri Korenblit out: 1181*d1e879ecSMiri Korenblit ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); 1182*d1e879ecSMiri Korenblit if (netdetect_info) { 1183*d1e879ecSMiri Korenblit for (int i = 0; i < netdetect_info->n_matches; i++) 1184*d1e879ecSMiri Korenblit kfree(netdetect_info->matches[i]); 1185*d1e879ecSMiri Korenblit kfree(netdetect_info); 1186*d1e879ecSMiri Korenblit } 1187*d1e879ecSMiri Korenblit } 1188*d1e879ecSMiri Korenblit 1189*d1e879ecSMiri Korenblit static bool iwl_mld_handle_d3_notif(struct iwl_notif_wait_data *notif_wait, 1190*d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt, void *data) 1191*d1e879ecSMiri Korenblit { 1192*d1e879ecSMiri Korenblit struct iwl_mld_resume_data *resume_data = data; 1193*d1e879ecSMiri Korenblit struct iwl_mld *mld = 1194*d1e879ecSMiri Korenblit container_of(notif_wait, struct iwl_mld, notif_wait); 1195*d1e879ecSMiri Korenblit 1196*d1e879ecSMiri Korenblit switch (WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) { 1197*d1e879ecSMiri Korenblit case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION): { 1198*d1e879ecSMiri Korenblit if (resume_data->notifs_received & IWL_D3_NOTIF_WOWLAN_INFO) { 1199*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, 1200*d1e879ecSMiri Korenblit "got additional wowlan_info notif\n"); 1201*d1e879ecSMiri Korenblit break; 1202*d1e879ecSMiri Korenblit } 1203*d1e879ecSMiri Korenblit resume_data->notif_handling_err = 1204*d1e879ecSMiri Korenblit iwl_mld_handle_wowlan_info_notif(mld, 1205*d1e879ecSMiri Korenblit resume_data->wowlan_status, 1206*d1e879ecSMiri Korenblit pkt); 1207*d1e879ecSMiri Korenblit resume_data->notifs_received |= IWL_D3_NOTIF_WOWLAN_INFO; 1208*d1e879ecSMiri Korenblit 1209*d1e879ecSMiri Korenblit if (resume_data->wowlan_status->wakeup_reasons & 1210*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT) 1211*d1e879ecSMiri Korenblit resume_data->notifs_expected |= 1212*d1e879ecSMiri Korenblit IWL_D3_NOTIF_WOWLAN_WAKE_PKT; 1213*d1e879ecSMiri Korenblit break; 1214*d1e879ecSMiri Korenblit } 1215*d1e879ecSMiri Korenblit case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_WAKE_PKT_NOTIFICATION): { 1216*d1e879ecSMiri Korenblit if (resume_data->notifs_received & 1217*d1e879ecSMiri Korenblit IWL_D3_NOTIF_WOWLAN_WAKE_PKT) { 1218*d1e879ecSMiri Korenblit /* We shouldn't get two wake packet notifications */ 1219*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, 1220*d1e879ecSMiri Korenblit "Got additional wowlan wake packet notification\n"); 1221*d1e879ecSMiri Korenblit break; 1222*d1e879ecSMiri Korenblit } 1223*d1e879ecSMiri Korenblit resume_data->notif_handling_err = 1224*d1e879ecSMiri Korenblit iwl_mld_handle_wake_pkt_notif(mld, 1225*d1e879ecSMiri Korenblit resume_data->wowlan_status, 1226*d1e879ecSMiri Korenblit pkt); 1227*d1e879ecSMiri Korenblit resume_data->notifs_received |= IWL_D3_NOTIF_WOWLAN_WAKE_PKT; 1228*d1e879ecSMiri Korenblit break; 1229*d1e879ecSMiri Korenblit } 1230*d1e879ecSMiri Korenblit case WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF): { 1231*d1e879ecSMiri Korenblit if (resume_data->notifs_received & IWL_D3_ND_MATCH_INFO) { 1232*d1e879ecSMiri Korenblit IWL_ERR(mld, 1233*d1e879ecSMiri Korenblit "Got additional netdetect match info\n"); 1234*d1e879ecSMiri Korenblit break; 1235*d1e879ecSMiri Korenblit } 1236*d1e879ecSMiri Korenblit 1237*d1e879ecSMiri Korenblit resume_data->notif_handling_err = 1238*d1e879ecSMiri Korenblit iwl_mld_netdetect_match_info_handler(mld, resume_data, 1239*d1e879ecSMiri Korenblit pkt); 1240*d1e879ecSMiri Korenblit resume_data->notifs_received |= IWL_D3_ND_MATCH_INFO; 1241*d1e879ecSMiri Korenblit break; 1242*d1e879ecSMiri Korenblit } 1243*d1e879ecSMiri Korenblit case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): { 1244*d1e879ecSMiri Korenblit struct iwl_d3_end_notif *notif = (void *)pkt->data; 1245*d1e879ecSMiri Korenblit 1246*d1e879ecSMiri Korenblit resume_data->d3_end_flags = le32_to_cpu(notif->flags); 1247*d1e879ecSMiri Korenblit resume_data->notifs_received |= IWL_D3_NOTIF_D3_END_NOTIF; 1248*d1e879ecSMiri Korenblit break; 1249*d1e879ecSMiri Korenblit } 1250*d1e879ecSMiri Korenblit default: 1251*d1e879ecSMiri Korenblit WARN_ON(1); 1252*d1e879ecSMiri Korenblit } 1253*d1e879ecSMiri Korenblit 1254*d1e879ecSMiri Korenblit return resume_data->notifs_received == resume_data->notifs_expected; 1255*d1e879ecSMiri Korenblit } 1256*d1e879ecSMiri Korenblit 1257*d1e879ecSMiri Korenblit #define IWL_MLD_D3_NOTIF_TIMEOUT (HZ / 3) 1258*d1e879ecSMiri Korenblit 1259*d1e879ecSMiri Korenblit static int iwl_mld_wait_d3_notif(struct iwl_mld *mld, 1260*d1e879ecSMiri Korenblit struct iwl_mld_resume_data *resume_data, 1261*d1e879ecSMiri Korenblit bool with_wowlan) 1262*d1e879ecSMiri Korenblit { 1263*d1e879ecSMiri Korenblit static const u16 wowlan_resume_notif[] = { 1264*d1e879ecSMiri Korenblit WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION), 1265*d1e879ecSMiri Korenblit WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_WAKE_PKT_NOTIFICATION), 1266*d1e879ecSMiri Korenblit WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF), 1267*d1e879ecSMiri Korenblit WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION) 1268*d1e879ecSMiri Korenblit }; 1269*d1e879ecSMiri Korenblit static const u16 d3_resume_notif[] = { 1270*d1e879ecSMiri Korenblit WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION) 1271*d1e879ecSMiri Korenblit }; 1272*d1e879ecSMiri Korenblit struct iwl_notification_wait wait_d3_notif; 1273*d1e879ecSMiri Korenblit enum iwl_d3_status d3_status; 1274*d1e879ecSMiri Korenblit int ret; 1275*d1e879ecSMiri Korenblit 1276*d1e879ecSMiri Korenblit if (with_wowlan) 1277*d1e879ecSMiri Korenblit iwl_init_notification_wait(&mld->notif_wait, &wait_d3_notif, 1278*d1e879ecSMiri Korenblit wowlan_resume_notif, 1279*d1e879ecSMiri Korenblit ARRAY_SIZE(wowlan_resume_notif), 1280*d1e879ecSMiri Korenblit iwl_mld_handle_d3_notif, 1281*d1e879ecSMiri Korenblit resume_data); 1282*d1e879ecSMiri Korenblit else 1283*d1e879ecSMiri Korenblit iwl_init_notification_wait(&mld->notif_wait, &wait_d3_notif, 1284*d1e879ecSMiri Korenblit d3_resume_notif, 1285*d1e879ecSMiri Korenblit ARRAY_SIZE(d3_resume_notif), 1286*d1e879ecSMiri Korenblit iwl_mld_handle_d3_notif, 1287*d1e879ecSMiri Korenblit resume_data); 1288*d1e879ecSMiri Korenblit 1289*d1e879ecSMiri Korenblit ret = iwl_trans_d3_resume(mld->trans, &d3_status, false, false); 1290*d1e879ecSMiri Korenblit if (ret || d3_status != IWL_D3_STATUS_ALIVE) { 1291*d1e879ecSMiri Korenblit if (d3_status != IWL_D3_STATUS_ALIVE) { 1292*d1e879ecSMiri Korenblit IWL_INFO(mld, "Device was reset during suspend\n"); 1293*d1e879ecSMiri Korenblit ret = -ENOENT; 1294*d1e879ecSMiri Korenblit } else { 1295*d1e879ecSMiri Korenblit IWL_ERR(mld, "Transport resume failed\n"); 1296*d1e879ecSMiri Korenblit } 1297*d1e879ecSMiri Korenblit iwl_remove_notification(&mld->notif_wait, &wait_d3_notif); 1298*d1e879ecSMiri Korenblit return ret; 1299*d1e879ecSMiri Korenblit } 1300*d1e879ecSMiri Korenblit 1301*d1e879ecSMiri Korenblit ret = iwl_wait_notification(&mld->notif_wait, &wait_d3_notif, 1302*d1e879ecSMiri Korenblit IWL_MLD_D3_NOTIF_TIMEOUT); 1303*d1e879ecSMiri Korenblit if (ret) 1304*d1e879ecSMiri Korenblit IWL_ERR(mld, "Couldn't get the d3 notif %d\n", ret); 1305*d1e879ecSMiri Korenblit 1306*d1e879ecSMiri Korenblit if (resume_data->notif_handling_err) 1307*d1e879ecSMiri Korenblit ret = -EIO; 1308*d1e879ecSMiri Korenblit 1309*d1e879ecSMiri Korenblit return ret; 1310*d1e879ecSMiri Korenblit } 1311*d1e879ecSMiri Korenblit 1312*d1e879ecSMiri Korenblit int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld) 1313*d1e879ecSMiri Korenblit { 1314*d1e879ecSMiri Korenblit struct iwl_d3_manager_config d3_cfg_cmd_data = {}; 1315*d1e879ecSMiri Korenblit int ret; 1316*d1e879ecSMiri Korenblit 1317*d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 1318*d1e879ecSMiri Korenblit 1319*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan suspend flow\n"); 1320*d1e879ecSMiri Korenblit 1321*d1e879ecSMiri Korenblit iwl_mld_low_latency_stop(mld); 1322*d1e879ecSMiri Korenblit 1323*d1e879ecSMiri Korenblit /* This will happen if iwl_mld_supsend failed with FW error */ 1324*d1e879ecSMiri Korenblit if (mld->trans->state == IWL_TRANS_NO_FW && 1325*d1e879ecSMiri Korenblit test_bit(STATUS_FW_ERROR, &mld->trans->status)) 1326*d1e879ecSMiri Korenblit return -ENODEV; 1327*d1e879ecSMiri Korenblit 1328*d1e879ecSMiri Korenblit ret = iwl_mld_update_device_power(mld, true); 1329*d1e879ecSMiri Korenblit if (ret) { 1330*d1e879ecSMiri Korenblit IWL_ERR(mld, 1331*d1e879ecSMiri Korenblit "d3 suspend: couldn't send power_device %d\n", ret); 1332*d1e879ecSMiri Korenblit goto out; 1333*d1e879ecSMiri Korenblit } 1334*d1e879ecSMiri Korenblit 1335*d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_pdu(mld, D3_CONFIG_CMD, 1336*d1e879ecSMiri Korenblit &d3_cfg_cmd_data); 1337*d1e879ecSMiri Korenblit if (ret) { 1338*d1e879ecSMiri Korenblit IWL_ERR(mld, 1339*d1e879ecSMiri Korenblit "d3 suspend: couldn't send D3_CONFIG_CMD %d\n", ret); 1340*d1e879ecSMiri Korenblit goto out; 1341*d1e879ecSMiri Korenblit } 1342*d1e879ecSMiri Korenblit 1343*d1e879ecSMiri Korenblit ret = iwl_trans_d3_suspend(mld->trans, false, false); 1344*d1e879ecSMiri Korenblit if (ret) { 1345*d1e879ecSMiri Korenblit IWL_ERR(mld, "d3 suspend: trans_d3_suspend failed %d\n", ret); 1346*d1e879ecSMiri Korenblit } else { 1347*d1e879ecSMiri Korenblit mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; 1348*d1e879ecSMiri Korenblit mld->fw_status.in_d3 = true; 1349*d1e879ecSMiri Korenblit } 1350*d1e879ecSMiri Korenblit 1351*d1e879ecSMiri Korenblit out: 1352*d1e879ecSMiri Korenblit if (ret) { 1353*d1e879ecSMiri Korenblit mld->trans->state = IWL_TRANS_NO_FW; 1354*d1e879ecSMiri Korenblit set_bit(STATUS_FW_ERROR, &mld->trans->status); 1355*d1e879ecSMiri Korenblit } 1356*d1e879ecSMiri Korenblit 1357*d1e879ecSMiri Korenblit return ret; 1358*d1e879ecSMiri Korenblit } 1359*d1e879ecSMiri Korenblit 1360*d1e879ecSMiri Korenblit int iwl_mld_no_wowlan_resume(struct iwl_mld *mld) 1361*d1e879ecSMiri Korenblit { 1362*d1e879ecSMiri Korenblit struct iwl_mld_resume_data resume_data = { 1363*d1e879ecSMiri Korenblit .notifs_expected = 1364*d1e879ecSMiri Korenblit IWL_D3_NOTIF_D3_END_NOTIF, 1365*d1e879ecSMiri Korenblit }; 1366*d1e879ecSMiri Korenblit int ret; 1367*d1e879ecSMiri Korenblit 1368*d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 1369*d1e879ecSMiri Korenblit 1370*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan resume flow\n"); 1371*d1e879ecSMiri Korenblit 1372*d1e879ecSMiri Korenblit mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; 1373*d1e879ecSMiri Korenblit mld->fw_status.in_d3 = false; 1374*d1e879ecSMiri Korenblit iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); 1375*d1e879ecSMiri Korenblit 1376*d1e879ecSMiri Korenblit if (iwl_mld_fw_needs_restart(mld, NULL)) 1377*d1e879ecSMiri Korenblit ret = -ENODEV; 1378*d1e879ecSMiri Korenblit else 1379*d1e879ecSMiri Korenblit ret = iwl_mld_wait_d3_notif(mld, &resume_data, false); 1380*d1e879ecSMiri Korenblit 1381*d1e879ecSMiri Korenblit if (!ret && (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE)) 1382*d1e879ecSMiri Korenblit return -ENODEV; 1383*d1e879ecSMiri Korenblit 1384*d1e879ecSMiri Korenblit if (ret) { 1385*d1e879ecSMiri Korenblit mld->trans->state = IWL_TRANS_NO_FW; 1386*d1e879ecSMiri Korenblit set_bit(STATUS_FW_ERROR, &mld->trans->status); 1387*d1e879ecSMiri Korenblit return ret; 1388*d1e879ecSMiri Korenblit } 1389*d1e879ecSMiri Korenblit iwl_mld_low_latency_restart(mld); 1390*d1e879ecSMiri Korenblit 1391*d1e879ecSMiri Korenblit return iwl_mld_update_device_power(mld, false); 1392*d1e879ecSMiri Korenblit } 1393*d1e879ecSMiri Korenblit 1394*d1e879ecSMiri Korenblit static void 1395*d1e879ecSMiri Korenblit iwl_mld_aes_seq_to_le64_pn(struct ieee80211_key_conf *key, 1396*d1e879ecSMiri Korenblit __le64 *key_rsc) 1397*d1e879ecSMiri Korenblit { 1398*d1e879ecSMiri Korenblit for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { 1399*d1e879ecSMiri Korenblit struct ieee80211_key_seq seq; 1400*d1e879ecSMiri Korenblit u8 *pn = key->cipher == WLAN_CIPHER_SUITE_CCMP ? seq.ccmp.pn : 1401*d1e879ecSMiri Korenblit seq.gcmp.pn; 1402*d1e879ecSMiri Korenblit 1403*d1e879ecSMiri Korenblit ieee80211_get_key_rx_seq(key, i, &seq); 1404*d1e879ecSMiri Korenblit key_rsc[i] = cpu_to_le64((u64)pn[5] | 1405*d1e879ecSMiri Korenblit ((u64)pn[4] << 8) | 1406*d1e879ecSMiri Korenblit ((u64)pn[3] << 16) | 1407*d1e879ecSMiri Korenblit ((u64)pn[2] << 24) | 1408*d1e879ecSMiri Korenblit ((u64)pn[1] << 32) | 1409*d1e879ecSMiri Korenblit ((u64)pn[0] << 40)); 1410*d1e879ecSMiri Korenblit } 1411*d1e879ecSMiri Korenblit } 1412*d1e879ecSMiri Korenblit 1413*d1e879ecSMiri Korenblit static void 1414*d1e879ecSMiri Korenblit iwl_mld_suspend_set_ucast_pn(struct iwl_mld *mld, struct ieee80211_sta *sta, 1415*d1e879ecSMiri Korenblit struct ieee80211_key_conf *key, __le64 *key_rsc) 1416*d1e879ecSMiri Korenblit { 1417*d1e879ecSMiri Korenblit struct iwl_mld_sta *mld_sta = 1418*d1e879ecSMiri Korenblit iwl_mld_sta_from_mac80211(sta); 1419*d1e879ecSMiri Korenblit struct iwl_mld_ptk_pn *mld_ptk_pn; 1420*d1e879ecSMiri Korenblit 1421*d1e879ecSMiri Korenblit if (WARN_ON(key->keyidx >= ARRAY_SIZE(mld_sta->ptk_pn))) 1422*d1e879ecSMiri Korenblit return; 1423*d1e879ecSMiri Korenblit 1424*d1e879ecSMiri Korenblit mld_ptk_pn = wiphy_dereference(mld->wiphy, 1425*d1e879ecSMiri Korenblit mld_sta->ptk_pn[key->keyidx]); 1426*d1e879ecSMiri Korenblit if (WARN_ON(!mld_ptk_pn)) 1427*d1e879ecSMiri Korenblit return; 1428*d1e879ecSMiri Korenblit 1429*d1e879ecSMiri Korenblit for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { 1430*d1e879ecSMiri Korenblit struct ieee80211_key_seq seq; 1431*d1e879ecSMiri Korenblit u8 *max_pn = seq.ccmp.pn; 1432*d1e879ecSMiri Korenblit 1433*d1e879ecSMiri Korenblit /* get the PN from mac80211, used on the default queue */ 1434*d1e879ecSMiri Korenblit ieee80211_get_key_rx_seq(key, tid, &seq); 1435*d1e879ecSMiri Korenblit 1436*d1e879ecSMiri Korenblit /* and use the internal data for all queues */ 1437*d1e879ecSMiri Korenblit for (int que = 1; que < mld->trans->num_rx_queues; que++) { 1438*d1e879ecSMiri Korenblit u8 *cur_pn = mld_ptk_pn->q[que].pn[tid]; 1439*d1e879ecSMiri Korenblit 1440*d1e879ecSMiri Korenblit if (memcmp(max_pn, cur_pn, IEEE80211_CCMP_PN_LEN) < 0) 1441*d1e879ecSMiri Korenblit max_pn = cur_pn; 1442*d1e879ecSMiri Korenblit } 1443*d1e879ecSMiri Korenblit key_rsc[tid] = cpu_to_le64((u64)max_pn[5] | 1444*d1e879ecSMiri Korenblit ((u64)max_pn[4] << 8) | 1445*d1e879ecSMiri Korenblit ((u64)max_pn[3] << 16) | 1446*d1e879ecSMiri Korenblit ((u64)max_pn[2] << 24) | 1447*d1e879ecSMiri Korenblit ((u64)max_pn[1] << 32) | 1448*d1e879ecSMiri Korenblit ((u64)max_pn[0] << 40)); 1449*d1e879ecSMiri Korenblit } 1450*d1e879ecSMiri Korenblit } 1451*d1e879ecSMiri Korenblit 1452*d1e879ecSMiri Korenblit static void 1453*d1e879ecSMiri Korenblit iwl_mld_suspend_convert_tkip_ipn(struct ieee80211_key_conf *key, 1454*d1e879ecSMiri Korenblit __le64 *rsc) 1455*d1e879ecSMiri Korenblit { 1456*d1e879ecSMiri Korenblit struct ieee80211_key_seq seq; 1457*d1e879ecSMiri Korenblit 1458*d1e879ecSMiri Korenblit for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { 1459*d1e879ecSMiri Korenblit ieee80211_get_key_rx_seq(key, i, &seq); 1460*d1e879ecSMiri Korenblit rsc[i] = 1461*d1e879ecSMiri Korenblit cpu_to_le64(((u64)seq.tkip.iv32 << 16) | 1462*d1e879ecSMiri Korenblit seq.tkip.iv16); 1463*d1e879ecSMiri Korenblit } 1464*d1e879ecSMiri Korenblit } 1465*d1e879ecSMiri Korenblit 1466*d1e879ecSMiri Korenblit static void 1467*d1e879ecSMiri Korenblit iwl_mld_suspend_key_data_iter(struct ieee80211_hw *hw, 1468*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1469*d1e879ecSMiri Korenblit struct ieee80211_sta *sta, 1470*d1e879ecSMiri Korenblit struct ieee80211_key_conf *key, 1471*d1e879ecSMiri Korenblit void *_data) 1472*d1e879ecSMiri Korenblit { 1473*d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 1474*d1e879ecSMiri Korenblit struct iwl_mld_suspend_key_iter_data *data = _data; 1475*d1e879ecSMiri Korenblit __le64 *key_rsc; 1476*d1e879ecSMiri Korenblit __le32 cipher = 0; 1477*d1e879ecSMiri Korenblit 1478*d1e879ecSMiri Korenblit switch (key->cipher) { 1479*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_CCMP: 1480*d1e879ecSMiri Korenblit cipher = cpu_to_le32(STA_KEY_FLG_CCM); 1481*d1e879ecSMiri Korenblit fallthrough; 1482*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_GCMP: 1483*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_GCMP_256: 1484*d1e879ecSMiri Korenblit if (!cipher) 1485*d1e879ecSMiri Korenblit cipher = cpu_to_le32(STA_KEY_FLG_GCMP); 1486*d1e879ecSMiri Korenblit fallthrough; 1487*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_TKIP: 1488*d1e879ecSMiri Korenblit if (!cipher) 1489*d1e879ecSMiri Korenblit cipher = cpu_to_le32(STA_KEY_FLG_TKIP); 1490*d1e879ecSMiri Korenblit if (sta) { 1491*d1e879ecSMiri Korenblit key_rsc = data->rsc->ucast_rsc; 1492*d1e879ecSMiri Korenblit if (key->cipher == WLAN_CIPHER_SUITE_TKIP) 1493*d1e879ecSMiri Korenblit iwl_mld_suspend_convert_tkip_ipn(key, key_rsc); 1494*d1e879ecSMiri Korenblit else 1495*d1e879ecSMiri Korenblit iwl_mld_suspend_set_ucast_pn(mld, sta, key, 1496*d1e879ecSMiri Korenblit key_rsc); 1497*d1e879ecSMiri Korenblit 1498*d1e879ecSMiri Korenblit data->have_rsc = true; 1499*d1e879ecSMiri Korenblit return; 1500*d1e879ecSMiri Korenblit } 1501*d1e879ecSMiri Korenblit /* We're iterating from old to new, there're 4 possible 1502*d1e879ecSMiri Korenblit * gtk ids, and only the last two keys matter 1503*d1e879ecSMiri Korenblit */ 1504*d1e879ecSMiri Korenblit if (WARN_ON(data->gtks >= 1505*d1e879ecSMiri Korenblit ARRAY_SIZE(data->found_gtk_idx))) 1506*d1e879ecSMiri Korenblit return; 1507*d1e879ecSMiri Korenblit 1508*d1e879ecSMiri Korenblit if (WARN_ON(key->keyidx >= 1509*d1e879ecSMiri Korenblit ARRAY_SIZE(data->rsc->mcast_key_id_map))) 1510*d1e879ecSMiri Korenblit return; 1511*d1e879ecSMiri Korenblit data->gtk_cipher = cipher; 1512*d1e879ecSMiri Korenblit data->found_gtk_idx[data->gtks] = key->keyidx; 1513*d1e879ecSMiri Korenblit key_rsc = data->rsc->mcast_rsc[data->gtks % 2]; 1514*d1e879ecSMiri Korenblit data->rsc->mcast_key_id_map[key->keyidx] = 1515*d1e879ecSMiri Korenblit data->gtks % 2; 1516*d1e879ecSMiri Korenblit 1517*d1e879ecSMiri Korenblit if (data->gtks >= 2) { 1518*d1e879ecSMiri Korenblit int prev = data->gtks % 2; 1519*d1e879ecSMiri Korenblit int prev_idx = data->found_gtk_idx[prev]; 1520*d1e879ecSMiri Korenblit 1521*d1e879ecSMiri Korenblit data->rsc->mcast_key_id_map[prev_idx] = 1522*d1e879ecSMiri Korenblit IWL_MCAST_KEY_MAP_INVALID; 1523*d1e879ecSMiri Korenblit } 1524*d1e879ecSMiri Korenblit 1525*d1e879ecSMiri Korenblit if (key->cipher == WLAN_CIPHER_SUITE_TKIP) 1526*d1e879ecSMiri Korenblit iwl_mld_suspend_convert_tkip_ipn(key, key_rsc); 1527*d1e879ecSMiri Korenblit else 1528*d1e879ecSMiri Korenblit iwl_mld_aes_seq_to_le64_pn(key, key_rsc); 1529*d1e879ecSMiri Korenblit 1530*d1e879ecSMiri Korenblit data->gtks++; 1531*d1e879ecSMiri Korenblit data->have_rsc = true; 1532*d1e879ecSMiri Korenblit break; 1533*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_GMAC_128: 1534*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_GMAC_256: 1535*d1e879ecSMiri Korenblit cipher = cpu_to_le32(STA_KEY_FLG_GCMP); 1536*d1e879ecSMiri Korenblit fallthrough; 1537*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_BIP_CMAC_256: 1538*d1e879ecSMiri Korenblit case WLAN_CIPHER_SUITE_AES_CMAC: 1539*d1e879ecSMiri Korenblit if (!cipher) 1540*d1e879ecSMiri Korenblit cipher = cpu_to_le32(STA_KEY_FLG_CCM); 1541*d1e879ecSMiri Korenblit if (key->keyidx == 4 || key->keyidx == 5) 1542*d1e879ecSMiri Korenblit data->igtk_cipher = cipher; 1543*d1e879ecSMiri Korenblit 1544*d1e879ecSMiri Korenblit if (key->keyidx == 6 || key->keyidx == 7) 1545*d1e879ecSMiri Korenblit data->bigtk_cipher = cipher; 1546*d1e879ecSMiri Korenblit 1547*d1e879ecSMiri Korenblit break; 1548*d1e879ecSMiri Korenblit } 1549*d1e879ecSMiri Korenblit } 1550*d1e879ecSMiri Korenblit 1551*d1e879ecSMiri Korenblit static int 1552*d1e879ecSMiri Korenblit iwl_mld_send_kek_kck_cmd(struct iwl_mld *mld, 1553*d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif, 1554*d1e879ecSMiri Korenblit struct iwl_mld_suspend_key_iter_data data, 1555*d1e879ecSMiri Korenblit int ap_sta_id) 1556*d1e879ecSMiri Korenblit { 1557*d1e879ecSMiri Korenblit struct iwl_wowlan_kek_kck_material_cmd_v4 kek_kck_cmd = {}; 1558*d1e879ecSMiri Korenblit struct iwl_mld_rekey_data *rekey_data = 1559*d1e879ecSMiri Korenblit &mld_vif->wowlan_data.rekey_data; 1560*d1e879ecSMiri Korenblit 1561*d1e879ecSMiri Korenblit memcpy(kek_kck_cmd.kck, rekey_data->kck, 1562*d1e879ecSMiri Korenblit rekey_data->kck_len); 1563*d1e879ecSMiri Korenblit kek_kck_cmd.kck_len = cpu_to_le16(rekey_data->kck_len); 1564*d1e879ecSMiri Korenblit memcpy(kek_kck_cmd.kek, rekey_data->kek, 1565*d1e879ecSMiri Korenblit rekey_data->kek_len); 1566*d1e879ecSMiri Korenblit kek_kck_cmd.kek_len = cpu_to_le16(rekey_data->kek_len); 1567*d1e879ecSMiri Korenblit kek_kck_cmd.replay_ctr = rekey_data->replay_ctr; 1568*d1e879ecSMiri Korenblit kek_kck_cmd.akm = cpu_to_le32(rekey_data->akm); 1569*d1e879ecSMiri Korenblit kek_kck_cmd.sta_id = cpu_to_le32(ap_sta_id); 1570*d1e879ecSMiri Korenblit kek_kck_cmd.gtk_cipher = data.gtk_cipher; 1571*d1e879ecSMiri Korenblit kek_kck_cmd.igtk_cipher = data.igtk_cipher; 1572*d1e879ecSMiri Korenblit kek_kck_cmd.bigtk_cipher = data.bigtk_cipher; 1573*d1e879ecSMiri Korenblit 1574*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, "setting akm %d\n", 1575*d1e879ecSMiri Korenblit rekey_data->akm); 1576*d1e879ecSMiri Korenblit 1577*d1e879ecSMiri Korenblit return iwl_mld_send_cmd_pdu(mld, WOWLAN_KEK_KCK_MATERIAL, 1578*d1e879ecSMiri Korenblit &kek_kck_cmd); 1579*d1e879ecSMiri Korenblit } 1580*d1e879ecSMiri Korenblit 1581*d1e879ecSMiri Korenblit static int 1582*d1e879ecSMiri Korenblit iwl_mld_suspend_send_security_cmds(struct iwl_mld *mld, 1583*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1584*d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif, 1585*d1e879ecSMiri Korenblit int ap_sta_id) 1586*d1e879ecSMiri Korenblit { 1587*d1e879ecSMiri Korenblit struct iwl_mld_suspend_key_iter_data data = {}; 1588*d1e879ecSMiri Korenblit int ret; 1589*d1e879ecSMiri Korenblit 1590*d1e879ecSMiri Korenblit data.rsc = kzalloc(sizeof(*data.rsc), GFP_KERNEL); 1591*d1e879ecSMiri Korenblit if (!data.rsc) 1592*d1e879ecSMiri Korenblit return -ENOMEM; 1593*d1e879ecSMiri Korenblit 1594*d1e879ecSMiri Korenblit memset(data.rsc->mcast_key_id_map, IWL_MCAST_KEY_MAP_INVALID, 1595*d1e879ecSMiri Korenblit ARRAY_SIZE(data.rsc->mcast_key_id_map)); 1596*d1e879ecSMiri Korenblit 1597*d1e879ecSMiri Korenblit data.rsc->sta_id = cpu_to_le32(ap_sta_id); 1598*d1e879ecSMiri Korenblit ieee80211_iter_keys(mld->hw, vif, 1599*d1e879ecSMiri Korenblit iwl_mld_suspend_key_data_iter, 1600*d1e879ecSMiri Korenblit &data); 1601*d1e879ecSMiri Korenblit 1602*d1e879ecSMiri Korenblit if (data.have_rsc) 1603*d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_TSC_RSC_PARAM, 1604*d1e879ecSMiri Korenblit data.rsc); 1605*d1e879ecSMiri Korenblit else 1606*d1e879ecSMiri Korenblit ret = 0; 1607*d1e879ecSMiri Korenblit 1608*d1e879ecSMiri Korenblit if (!ret && mld_vif->wowlan_data.rekey_data.valid) 1609*d1e879ecSMiri Korenblit ret = iwl_mld_send_kek_kck_cmd(mld, mld_vif, data, ap_sta_id); 1610*d1e879ecSMiri Korenblit 1611*d1e879ecSMiri Korenblit kfree(data.rsc); 1612*d1e879ecSMiri Korenblit 1613*d1e879ecSMiri Korenblit return ret; 1614*d1e879ecSMiri Korenblit } 1615*d1e879ecSMiri Korenblit 1616*d1e879ecSMiri Korenblit static void 1617*d1e879ecSMiri Korenblit iwl_mld_set_wowlan_config_cmd(struct iwl_mld *mld, 1618*d1e879ecSMiri Korenblit struct cfg80211_wowlan *wowlan, 1619*d1e879ecSMiri Korenblit struct iwl_wowlan_config_cmd *wowlan_config_cmd, 1620*d1e879ecSMiri Korenblit struct ieee80211_sta *ap_sta) 1621*d1e879ecSMiri Korenblit { 1622*d1e879ecSMiri Korenblit wowlan_config_cmd->is_11n_connection = 1623*d1e879ecSMiri Korenblit ap_sta->deflink.ht_cap.ht_supported; 1624*d1e879ecSMiri Korenblit wowlan_config_cmd->flags = ENABLE_L3_FILTERING | 1625*d1e879ecSMiri Korenblit ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING; 1626*d1e879ecSMiri Korenblit 1627*d1e879ecSMiri Korenblit if (ap_sta->mfp) 1628*d1e879ecSMiri Korenblit wowlan_config_cmd->flags |= IS_11W_ASSOC; 1629*d1e879ecSMiri Korenblit 1630*d1e879ecSMiri Korenblit if (wowlan->disconnect) 1631*d1e879ecSMiri Korenblit wowlan_config_cmd->wakeup_filter |= 1632*d1e879ecSMiri Korenblit cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | 1633*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_LINK_CHANGE); 1634*d1e879ecSMiri Korenblit if (wowlan->magic_pkt) 1635*d1e879ecSMiri Korenblit wowlan_config_cmd->wakeup_filter |= 1636*d1e879ecSMiri Korenblit cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); 1637*d1e879ecSMiri Korenblit if (wowlan->gtk_rekey_failure) 1638*d1e879ecSMiri Korenblit wowlan_config_cmd->wakeup_filter |= 1639*d1e879ecSMiri Korenblit cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); 1640*d1e879ecSMiri Korenblit if (wowlan->eap_identity_req) 1641*d1e879ecSMiri Korenblit wowlan_config_cmd->wakeup_filter |= 1642*d1e879ecSMiri Korenblit cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); 1643*d1e879ecSMiri Korenblit if (wowlan->four_way_handshake) 1644*d1e879ecSMiri Korenblit wowlan_config_cmd->wakeup_filter |= 1645*d1e879ecSMiri Korenblit cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); 1646*d1e879ecSMiri Korenblit if (wowlan->n_patterns) 1647*d1e879ecSMiri Korenblit wowlan_config_cmd->wakeup_filter |= 1648*d1e879ecSMiri Korenblit cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); 1649*d1e879ecSMiri Korenblit 1650*d1e879ecSMiri Korenblit if (wowlan->rfkill_release) 1651*d1e879ecSMiri Korenblit wowlan_config_cmd->wakeup_filter |= 1652*d1e879ecSMiri Korenblit cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); 1653*d1e879ecSMiri Korenblit 1654*d1e879ecSMiri Korenblit if (wowlan->any) { 1655*d1e879ecSMiri Korenblit wowlan_config_cmd->wakeup_filter |= 1656*d1e879ecSMiri Korenblit cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | 1657*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_LINK_CHANGE | 1658*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_RX_FRAME | 1659*d1e879ecSMiri Korenblit IWL_WOWLAN_WAKEUP_BCN_FILTERING); 1660*d1e879ecSMiri Korenblit } 1661*d1e879ecSMiri Korenblit } 1662*d1e879ecSMiri Korenblit 1663*d1e879ecSMiri Korenblit static int iwl_mld_send_patterns(struct iwl_mld *mld, 1664*d1e879ecSMiri Korenblit struct cfg80211_wowlan *wowlan, 1665*d1e879ecSMiri Korenblit int ap_sta_id) 1666*d1e879ecSMiri Korenblit { 1667*d1e879ecSMiri Korenblit struct iwl_wowlan_patterns_cmd *pattern_cmd; 1668*d1e879ecSMiri Korenblit struct iwl_host_cmd cmd = { 1669*d1e879ecSMiri Korenblit .id = WOWLAN_PATTERNS, 1670*d1e879ecSMiri Korenblit .dataflags[0] = IWL_HCMD_DFL_NOCOPY, 1671*d1e879ecSMiri Korenblit }; 1672*d1e879ecSMiri Korenblit int ret; 1673*d1e879ecSMiri Korenblit 1674*d1e879ecSMiri Korenblit if (!wowlan->n_patterns) 1675*d1e879ecSMiri Korenblit return 0; 1676*d1e879ecSMiri Korenblit 1677*d1e879ecSMiri Korenblit cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns); 1678*d1e879ecSMiri Korenblit 1679*d1e879ecSMiri Korenblit pattern_cmd = kzalloc(cmd.len[0], GFP_KERNEL); 1680*d1e879ecSMiri Korenblit if (!pattern_cmd) 1681*d1e879ecSMiri Korenblit return -ENOMEM; 1682*d1e879ecSMiri Korenblit 1683*d1e879ecSMiri Korenblit pattern_cmd->n_patterns = wowlan->n_patterns; 1684*d1e879ecSMiri Korenblit pattern_cmd->sta_id = ap_sta_id; 1685*d1e879ecSMiri Korenblit 1686*d1e879ecSMiri Korenblit for (int i = 0; i < wowlan->n_patterns; i++) { 1687*d1e879ecSMiri Korenblit int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); 1688*d1e879ecSMiri Korenblit 1689*d1e879ecSMiri Korenblit pattern_cmd->patterns[i].pattern_type = 1690*d1e879ecSMiri Korenblit WOWLAN_PATTERN_TYPE_BITMASK; 1691*d1e879ecSMiri Korenblit 1692*d1e879ecSMiri Korenblit memcpy(&pattern_cmd->patterns[i].u.bitmask.mask, 1693*d1e879ecSMiri Korenblit wowlan->patterns[i].mask, mask_len); 1694*d1e879ecSMiri Korenblit memcpy(&pattern_cmd->patterns[i].u.bitmask.pattern, 1695*d1e879ecSMiri Korenblit wowlan->patterns[i].pattern, 1696*d1e879ecSMiri Korenblit wowlan->patterns[i].pattern_len); 1697*d1e879ecSMiri Korenblit pattern_cmd->patterns[i].u.bitmask.mask_size = mask_len; 1698*d1e879ecSMiri Korenblit pattern_cmd->patterns[i].u.bitmask.pattern_size = 1699*d1e879ecSMiri Korenblit wowlan->patterns[i].pattern_len; 1700*d1e879ecSMiri Korenblit } 1701*d1e879ecSMiri Korenblit 1702*d1e879ecSMiri Korenblit cmd.data[0] = pattern_cmd; 1703*d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &cmd); 1704*d1e879ecSMiri Korenblit kfree(pattern_cmd); 1705*d1e879ecSMiri Korenblit return ret; 1706*d1e879ecSMiri Korenblit } 1707*d1e879ecSMiri Korenblit 1708*d1e879ecSMiri Korenblit static int 1709*d1e879ecSMiri Korenblit iwl_mld_send_proto_offload(struct iwl_mld *mld, 1710*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1711*d1e879ecSMiri Korenblit u8 ap_sta_id) 1712*d1e879ecSMiri Korenblit { 1713*d1e879ecSMiri Korenblit struct iwl_proto_offload_cmd_v4 *cmd __free(kfree); 1714*d1e879ecSMiri Korenblit struct iwl_host_cmd hcmd = { 1715*d1e879ecSMiri Korenblit .id = PROT_OFFLOAD_CONFIG_CMD, 1716*d1e879ecSMiri Korenblit .dataflags[0] = IWL_HCMD_DFL_NOCOPY, 1717*d1e879ecSMiri Korenblit .len[0] = sizeof(*cmd), 1718*d1e879ecSMiri Korenblit }; 1719*d1e879ecSMiri Korenblit u32 enabled = 0; 1720*d1e879ecSMiri Korenblit 1721*d1e879ecSMiri Korenblit cmd = kzalloc(hcmd.len[0], GFP_KERNEL); 1722*d1e879ecSMiri Korenblit 1723*d1e879ecSMiri Korenblit #if IS_ENABLED(CONFIG_IPV6) 1724*d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 1725*d1e879ecSMiri Korenblit struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; 1726*d1e879ecSMiri Korenblit struct iwl_ns_config *nsc; 1727*d1e879ecSMiri Korenblit struct iwl_targ_addr *addrs; 1728*d1e879ecSMiri Korenblit int n_nsc, n_addrs; 1729*d1e879ecSMiri Korenblit int i, c; 1730*d1e879ecSMiri Korenblit int num_skipped = 0; 1731*d1e879ecSMiri Korenblit 1732*d1e879ecSMiri Korenblit nsc = cmd->ns_config; 1733*d1e879ecSMiri Korenblit n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; 1734*d1e879ecSMiri Korenblit addrs = cmd->targ_addrs; 1735*d1e879ecSMiri Korenblit n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; 1736*d1e879ecSMiri Korenblit 1737*d1e879ecSMiri Korenblit /* For each address we have (and that will fit) fill a target 1738*d1e879ecSMiri Korenblit * address struct and combine for NS offload structs with the 1739*d1e879ecSMiri Korenblit * solicited node addresses. 1740*d1e879ecSMiri Korenblit */ 1741*d1e879ecSMiri Korenblit for (i = 0, c = 0; 1742*d1e879ecSMiri Korenblit i < wowlan_data->num_target_ipv6_addrs && 1743*d1e879ecSMiri Korenblit i < n_addrs && c < n_nsc; i++) { 1744*d1e879ecSMiri Korenblit int j; 1745*d1e879ecSMiri Korenblit struct in6_addr solicited_addr; 1746*d1e879ecSMiri Korenblit 1747*d1e879ecSMiri Korenblit /* Because ns is offloaded skip tentative address to avoid 1748*d1e879ecSMiri Korenblit * violating RFC4862. 1749*d1e879ecSMiri Korenblit */ 1750*d1e879ecSMiri Korenblit if (test_bit(i, wowlan_data->tentative_addrs)) { 1751*d1e879ecSMiri Korenblit num_skipped++; 1752*d1e879ecSMiri Korenblit continue; 1753*d1e879ecSMiri Korenblit } 1754*d1e879ecSMiri Korenblit 1755*d1e879ecSMiri Korenblit addrconf_addr_solict_mult(&wowlan_data->target_ipv6_addrs[i], 1756*d1e879ecSMiri Korenblit &solicited_addr); 1757*d1e879ecSMiri Korenblit for (j = 0; j < c; j++) 1758*d1e879ecSMiri Korenblit if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, 1759*d1e879ecSMiri Korenblit &solicited_addr) == 0) 1760*d1e879ecSMiri Korenblit break; 1761*d1e879ecSMiri Korenblit if (j == c) 1762*d1e879ecSMiri Korenblit c++; 1763*d1e879ecSMiri Korenblit addrs[i].addr = wowlan_data->target_ipv6_addrs[i]; 1764*d1e879ecSMiri Korenblit addrs[i].config_num = cpu_to_le32(j); 1765*d1e879ecSMiri Korenblit nsc[j].dest_ipv6_addr = solicited_addr; 1766*d1e879ecSMiri Korenblit memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); 1767*d1e879ecSMiri Korenblit } 1768*d1e879ecSMiri Korenblit 1769*d1e879ecSMiri Korenblit if (wowlan_data->num_target_ipv6_addrs - num_skipped) 1770*d1e879ecSMiri Korenblit enabled |= IWL_D3_PROTO_IPV6_VALID; 1771*d1e879ecSMiri Korenblit 1772*d1e879ecSMiri Korenblit cmd->num_valid_ipv6_addrs = cpu_to_le32(i - num_skipped); 1773*d1e879ecSMiri Korenblit if (enabled & IWL_D3_PROTO_IPV6_VALID) 1774*d1e879ecSMiri Korenblit enabled |= IWL_D3_PROTO_OFFLOAD_NS; 1775*d1e879ecSMiri Korenblit #endif 1776*d1e879ecSMiri Korenblit 1777*d1e879ecSMiri Korenblit if (vif->cfg.arp_addr_cnt) { 1778*d1e879ecSMiri Korenblit enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID; 1779*d1e879ecSMiri Korenblit cmd->common.host_ipv4_addr = vif->cfg.arp_addr_list[0]; 1780*d1e879ecSMiri Korenblit ether_addr_copy(cmd->common.arp_mac_addr, vif->addr); 1781*d1e879ecSMiri Korenblit } 1782*d1e879ecSMiri Korenblit 1783*d1e879ecSMiri Korenblit enabled |= IWL_D3_PROTO_OFFLOAD_BTM; 1784*d1e879ecSMiri Korenblit cmd->common.enabled = cpu_to_le32(enabled); 1785*d1e879ecSMiri Korenblit cmd->sta_id = cpu_to_le32(ap_sta_id); 1786*d1e879ecSMiri Korenblit hcmd.data[0] = cmd; 1787*d1e879ecSMiri Korenblit return iwl_mld_send_cmd(mld, &hcmd); 1788*d1e879ecSMiri Korenblit } 1789*d1e879ecSMiri Korenblit 1790*d1e879ecSMiri Korenblit static int 1791*d1e879ecSMiri Korenblit iwl_mld_wowlan_config(struct iwl_mld *mld, struct ieee80211_vif *bss_vif, 1792*d1e879ecSMiri Korenblit struct cfg80211_wowlan *wowlan) 1793*d1e879ecSMiri Korenblit { 1794*d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_vif); 1795*d1e879ecSMiri Korenblit struct ieee80211_sta *ap_sta = mld_vif->ap_sta; 1796*d1e879ecSMiri Korenblit struct iwl_wowlan_config_cmd wowlan_config_cmd = { 1797*d1e879ecSMiri Korenblit .offloading_tid = IWL_WOWLAN_OFFLOAD_TID, 1798*d1e879ecSMiri Korenblit }; 1799*d1e879ecSMiri Korenblit u32 sta_id_mask; 1800*d1e879ecSMiri Korenblit int ap_sta_id, ret; 1801*d1e879ecSMiri Korenblit int link_id = iwl_mld_get_primary_link(bss_vif); 1802*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf; 1803*d1e879ecSMiri Korenblit 1804*d1e879ecSMiri Korenblit ret = iwl_mld_block_emlsr_sync(mld, bss_vif, 1805*d1e879ecSMiri Korenblit IWL_MLD_EMLSR_BLOCKED_WOWLAN, link_id); 1806*d1e879ecSMiri Korenblit if (ret) 1807*d1e879ecSMiri Korenblit return ret; 1808*d1e879ecSMiri Korenblit 1809*d1e879ecSMiri Korenblit link_conf = link_conf_dereference_protected(bss_vif, link_id); 1810*d1e879ecSMiri Korenblit 1811*d1e879ecSMiri Korenblit if (WARN_ON(!ap_sta || !link_conf)) 1812*d1e879ecSMiri Korenblit return -EINVAL; 1813*d1e879ecSMiri Korenblit 1814*d1e879ecSMiri Korenblit sta_id_mask = iwl_mld_fw_sta_id_mask(mld, ap_sta); 1815*d1e879ecSMiri Korenblit if (WARN_ON(hweight32(sta_id_mask) != 1)) 1816*d1e879ecSMiri Korenblit return -EINVAL; 1817*d1e879ecSMiri Korenblit 1818*d1e879ecSMiri Korenblit ap_sta_id = __ffs(sta_id_mask); 1819*d1e879ecSMiri Korenblit wowlan_config_cmd.sta_id = ap_sta_id; 1820*d1e879ecSMiri Korenblit 1821*d1e879ecSMiri Korenblit ret = iwl_mld_ensure_queue(mld, 1822*d1e879ecSMiri Korenblit ap_sta->txq[wowlan_config_cmd.offloading_tid]); 1823*d1e879ecSMiri Korenblit if (ret) 1824*d1e879ecSMiri Korenblit return ret; 1825*d1e879ecSMiri Korenblit 1826*d1e879ecSMiri Korenblit iwl_mld_set_wowlan_config_cmd(mld, wowlan, 1827*d1e879ecSMiri Korenblit &wowlan_config_cmd, ap_sta); 1828*d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_CONFIGURATION, 1829*d1e879ecSMiri Korenblit &wowlan_config_cmd); 1830*d1e879ecSMiri Korenblit if (ret) 1831*d1e879ecSMiri Korenblit return ret; 1832*d1e879ecSMiri Korenblit 1833*d1e879ecSMiri Korenblit ret = iwl_mld_suspend_send_security_cmds(mld, bss_vif, mld_vif, 1834*d1e879ecSMiri Korenblit ap_sta_id); 1835*d1e879ecSMiri Korenblit if (ret) 1836*d1e879ecSMiri Korenblit return ret; 1837*d1e879ecSMiri Korenblit 1838*d1e879ecSMiri Korenblit ret = iwl_mld_send_patterns(mld, wowlan, ap_sta_id); 1839*d1e879ecSMiri Korenblit if (ret) 1840*d1e879ecSMiri Korenblit return ret; 1841*d1e879ecSMiri Korenblit 1842*d1e879ecSMiri Korenblit ret = iwl_mld_send_proto_offload(mld, bss_vif, ap_sta_id); 1843*d1e879ecSMiri Korenblit if (ret) 1844*d1e879ecSMiri Korenblit return ret; 1845*d1e879ecSMiri Korenblit 1846*d1e879ecSMiri Korenblit iwl_mld_enable_beacon_filter(mld, link_conf, true); 1847*d1e879ecSMiri Korenblit return iwl_mld_update_mac_power(mld, bss_vif, true); 1848*d1e879ecSMiri Korenblit } 1849*d1e879ecSMiri Korenblit 1850*d1e879ecSMiri Korenblit int iwl_mld_wowlan_suspend(struct iwl_mld *mld, struct cfg80211_wowlan *wowlan) 1851*d1e879ecSMiri Korenblit { 1852*d1e879ecSMiri Korenblit struct ieee80211_vif *bss_vif; 1853*d1e879ecSMiri Korenblit 1854*d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 1855*d1e879ecSMiri Korenblit 1856*d1e879ecSMiri Korenblit if (WARN_ON(!wowlan)) 1857*d1e879ecSMiri Korenblit return 1; 1858*d1e879ecSMiri Korenblit 1859*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, "Starting the wowlan suspend flow\n"); 1860*d1e879ecSMiri Korenblit 1861*d1e879ecSMiri Korenblit bss_vif = iwl_mld_get_bss_vif(mld); 1862*d1e879ecSMiri Korenblit if (WARN_ON(!bss_vif)) 1863*d1e879ecSMiri Korenblit return 1; 1864*d1e879ecSMiri Korenblit 1865*d1e879ecSMiri Korenblit if (!bss_vif->cfg.assoc) { 1866*d1e879ecSMiri Korenblit int ret; 1867*d1e879ecSMiri Korenblit /* If we're not associated, this must be netdetect */ 1868*d1e879ecSMiri Korenblit if (WARN_ON(!wowlan->nd_config)) 1869*d1e879ecSMiri Korenblit return 1; 1870*d1e879ecSMiri Korenblit 1871*d1e879ecSMiri Korenblit ret = iwl_mld_netdetect_config(mld, bss_vif, wowlan); 1872*d1e879ecSMiri Korenblit if (!ret) 1873*d1e879ecSMiri Korenblit mld->netdetect = true; 1874*d1e879ecSMiri Korenblit 1875*d1e879ecSMiri Korenblit return ret; 1876*d1e879ecSMiri Korenblit } 1877*d1e879ecSMiri Korenblit 1878*d1e879ecSMiri Korenblit return iwl_mld_wowlan_config(mld, bss_vif, wowlan); 1879*d1e879ecSMiri Korenblit } 1880*d1e879ecSMiri Korenblit 1881*d1e879ecSMiri Korenblit /* Returns 0 on success, 1 if an error occurred in firmware during d3, 1882*d1e879ecSMiri Korenblit * A negative value is expected only in unrecovreable cases. 1883*d1e879ecSMiri Korenblit */ 1884*d1e879ecSMiri Korenblit int iwl_mld_wowlan_resume(struct iwl_mld *mld) 1885*d1e879ecSMiri Korenblit { 1886*d1e879ecSMiri Korenblit struct ieee80211_vif *bss_vif; 1887*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf; 1888*d1e879ecSMiri Korenblit struct iwl_mld_netdetect_res netdetect_res; 1889*d1e879ecSMiri Korenblit struct iwl_mld_resume_data resume_data = { 1890*d1e879ecSMiri Korenblit .notifs_expected = 1891*d1e879ecSMiri Korenblit IWL_D3_NOTIF_WOWLAN_INFO | 1892*d1e879ecSMiri Korenblit IWL_D3_NOTIF_D3_END_NOTIF, 1893*d1e879ecSMiri Korenblit .netdetect_res = &netdetect_res, 1894*d1e879ecSMiri Korenblit }; 1895*d1e879ecSMiri Korenblit int link_id; 1896*d1e879ecSMiri Korenblit int ret; 1897*d1e879ecSMiri Korenblit bool fw_err = false; 1898*d1e879ecSMiri Korenblit bool keep_connection; 1899*d1e879ecSMiri Korenblit 1900*d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 1901*d1e879ecSMiri Korenblit 1902*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, "Starting the wowlan resume flow\n"); 1903*d1e879ecSMiri Korenblit 1904*d1e879ecSMiri Korenblit mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; 1905*d1e879ecSMiri Korenblit if (!mld->fw_status.in_d3) { 1906*d1e879ecSMiri Korenblit IWL_DEBUG_WOWLAN(mld, 1907*d1e879ecSMiri Korenblit "Device_powered_off() was called during wowlan\n"); 1908*d1e879ecSMiri Korenblit goto err; 1909*d1e879ecSMiri Korenblit } 1910*d1e879ecSMiri Korenblit 1911*d1e879ecSMiri Korenblit mld->fw_status.in_d3 = false; 1912*d1e879ecSMiri Korenblit mld->scan.last_start_time_jiffies = jiffies; 1913*d1e879ecSMiri Korenblit 1914*d1e879ecSMiri Korenblit bss_vif = iwl_mld_get_bss_vif(mld); 1915*d1e879ecSMiri Korenblit if (WARN_ON(!bss_vif)) 1916*d1e879ecSMiri Korenblit goto err; 1917*d1e879ecSMiri Korenblit 1918*d1e879ecSMiri Korenblit /* We can't have several links upon wowlan entry, 1919*d1e879ecSMiri Korenblit * this is enforced in the suspend flow. 1920*d1e879ecSMiri Korenblit */ 1921*d1e879ecSMiri Korenblit WARN_ON(hweight16(bss_vif->active_links) > 1); 1922*d1e879ecSMiri Korenblit link_id = bss_vif->active_links ? __ffs(bss_vif->active_links) : 0; 1923*d1e879ecSMiri Korenblit link_conf = link_conf_dereference_protected(bss_vif, link_id); 1924*d1e879ecSMiri Korenblit 1925*d1e879ecSMiri Korenblit if (WARN_ON(!link_conf)) 1926*d1e879ecSMiri Korenblit goto err; 1927*d1e879ecSMiri Korenblit 1928*d1e879ecSMiri Korenblit iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); 1929*d1e879ecSMiri Korenblit 1930*d1e879ecSMiri Korenblit if (iwl_mld_fw_needs_restart(mld, bss_vif)) { 1931*d1e879ecSMiri Korenblit fw_err = true; 1932*d1e879ecSMiri Korenblit goto err; 1933*d1e879ecSMiri Korenblit } 1934*d1e879ecSMiri Korenblit 1935*d1e879ecSMiri Korenblit resume_data.wowlan_status = kzalloc(sizeof(*resume_data.wowlan_status), 1936*d1e879ecSMiri Korenblit GFP_KERNEL); 1937*d1e879ecSMiri Korenblit if (!resume_data.wowlan_status) 1938*d1e879ecSMiri Korenblit return -1; 1939*d1e879ecSMiri Korenblit 1940*d1e879ecSMiri Korenblit if (mld->netdetect) 1941*d1e879ecSMiri Korenblit resume_data.notifs_expected |= IWL_D3_ND_MATCH_INFO; 1942*d1e879ecSMiri Korenblit 1943*d1e879ecSMiri Korenblit ret = iwl_mld_wait_d3_notif(mld, &resume_data, true); 1944*d1e879ecSMiri Korenblit if (ret) { 1945*d1e879ecSMiri Korenblit IWL_ERR(mld, "Couldn't get the d3 notifs %d\n", ret); 1946*d1e879ecSMiri Korenblit fw_err = true; 1947*d1e879ecSMiri Korenblit goto err; 1948*d1e879ecSMiri Korenblit } 1949*d1e879ecSMiri Korenblit 1950*d1e879ecSMiri Korenblit if (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE) { 1951*d1e879ecSMiri Korenblit mld->fw_status.in_hw_restart = true; 1952*d1e879ecSMiri Korenblit goto process_wakeup_results; 1953*d1e879ecSMiri Korenblit } 1954*d1e879ecSMiri Korenblit 1955*d1e879ecSMiri Korenblit iwl_mld_update_changed_regdomain(mld); 1956*d1e879ecSMiri Korenblit iwl_mld_update_mac_power(mld, bss_vif, false); 1957*d1e879ecSMiri Korenblit iwl_mld_enable_beacon_filter(mld, link_conf, false); 1958*d1e879ecSMiri Korenblit iwl_mld_update_device_power(mld, false); 1959*d1e879ecSMiri Korenblit 1960*d1e879ecSMiri Korenblit if (mld->netdetect) 1961*d1e879ecSMiri Korenblit ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_NETDETECT, false); 1962*d1e879ecSMiri Korenblit 1963*d1e879ecSMiri Korenblit process_wakeup_results: 1964*d1e879ecSMiri Korenblit if (mld->netdetect) { 1965*d1e879ecSMiri Korenblit iwl_mld_process_netdetect_res(mld, bss_vif, &resume_data); 1966*d1e879ecSMiri Korenblit mld->netdetect = false; 1967*d1e879ecSMiri Korenblit } else { 1968*d1e879ecSMiri Korenblit keep_connection = 1969*d1e879ecSMiri Korenblit iwl_mld_process_wowlan_status(mld, bss_vif, 1970*d1e879ecSMiri Korenblit resume_data.wowlan_status); 1971*d1e879ecSMiri Korenblit 1972*d1e879ecSMiri Korenblit /* EMLSR state will be cleared if the connection is not kept */ 1973*d1e879ecSMiri Korenblit if (keep_connection) 1974*d1e879ecSMiri Korenblit iwl_mld_unblock_emlsr(mld, bss_vif, 1975*d1e879ecSMiri Korenblit IWL_MLD_EMLSR_BLOCKED_WOWLAN); 1976*d1e879ecSMiri Korenblit } 1977*d1e879ecSMiri Korenblit 1978*d1e879ecSMiri Korenblit if (!mld->netdetect && !keep_connection) 1979*d1e879ecSMiri Korenblit ieee80211_resume_disconnect(bss_vif); 1980*d1e879ecSMiri Korenblit 1981*d1e879ecSMiri Korenblit goto out; 1982*d1e879ecSMiri Korenblit 1983*d1e879ecSMiri Korenblit err: 1984*d1e879ecSMiri Korenblit if (fw_err) { 1985*d1e879ecSMiri Korenblit mld->trans->state = IWL_TRANS_NO_FW; 1986*d1e879ecSMiri Korenblit set_bit(STATUS_FW_ERROR, &mld->trans->status); 1987*d1e879ecSMiri Korenblit } 1988*d1e879ecSMiri Korenblit 1989*d1e879ecSMiri Korenblit mld->fw_status.in_hw_restart = true; 1990*d1e879ecSMiri Korenblit ret = 1; 1991*d1e879ecSMiri Korenblit out: 1992*d1e879ecSMiri Korenblit if (resume_data.wowlan_status) { 1993*d1e879ecSMiri Korenblit kfree(resume_data.wowlan_status->wake_packet); 1994*d1e879ecSMiri Korenblit kfree(resume_data.wowlan_status); 1995*d1e879ecSMiri Korenblit } 1996*d1e879ecSMiri Korenblit 1997*d1e879ecSMiri Korenblit return ret; 1998*d1e879ecSMiri Korenblit } 1999