xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/d3.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
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 
iwl_mld_set_rekey_data(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct cfg80211_gtk_rekey_data * data)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)
iwl_mld_ipv6_addr_change(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct inet6_dev * idev)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 
iwl_mld_check_err_tables(struct iwl_mld * mld,struct ieee80211_vif * vif)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 
iwl_mld_fw_needs_restart(struct iwl_mld * mld,struct ieee80211_vif * vif)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
iwl_mld_netdetect_config(struct iwl_mld * mld,struct ieee80211_vif * vif,const struct cfg80211_wowlan * wowlan)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
iwl_mld_le64_to_tkip_seq(__le64 le_pn,struct ieee80211_key_seq * seq)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
iwl_mld_le64_to_aes_seq(__le64 le_pn,struct ieee80211_key_seq * seq)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
iwl_mld_convert_gtk_resume_seq(struct iwl_mld_mcast_key_data * gtk_data,const struct iwl_wowlan_all_rsc_tsc_v5 * sc,int rsc_idx)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
iwl_mld_convert_gtk_resume_data(struct iwl_mld * mld,struct iwl_mld_wowlan_status * wowlan_status,const struct iwl_wowlan_gtk_status_v3 * gtk_data,const struct iwl_wowlan_all_rsc_tsc_v5 * sc)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
iwl_mld_convert_ptk_resume_seq(struct iwl_mld * mld,struct iwl_mld_wowlan_status * wowlan_status,const struct iwl_wowlan_all_rsc_tsc_v5 * sc)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
iwl_mld_convert_mcast_ipn(struct iwl_mld_mcast_key_data * key_status,const struct iwl_wowlan_igtk_status * key)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
iwl_mld_convert_igtk_resume_data(struct iwl_mld_wowlan_status * wowlan_status,const struct iwl_wowlan_igtk_status * igtk)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
iwl_mld_convert_bigtk_resume_data(struct iwl_mld_wowlan_status * wowlan_status,const struct iwl_wowlan_igtk_status * bigtk)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
iwl_mld_handle_wowlan_info_notif(struct iwl_mld * mld,struct iwl_mld_wowlan_status * wowlan_status,struct iwl_rx_packet * pkt)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 					&notif->gtk[0].sc);
492*d1e879ecSMiri Korenblit 	iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, &notif->gtk[0].sc);
493*d1e879ecSMiri Korenblit 	/* only one igtk is passed by FW */
494*d1e879ecSMiri Korenblit 	iwl_mld_convert_igtk_resume_data(wowlan_status, &notif->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
iwl_mld_handle_wake_pkt_notif(struct iwl_mld * mld,struct iwl_mld_wowlan_status * wowlan_status,struct iwl_rx_packet * pkt)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
iwl_mld_set_wake_packet(struct iwl_mld * mld,struct ieee80211_vif * vif,const struct iwl_mld_wowlan_status * wowlan_status,struct cfg80211_wowlan_wakeup * wakeup,struct sk_buff ** _pkt)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
iwl_mld_report_wowlan_wakeup(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_wowlan_status * wowlan_status)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
iwl_mld_set_key_rx_seq_tids(struct ieee80211_key_conf * key,struct ieee80211_key_seq * seq)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
iwl_mld_set_key_rx_seq(struct ieee80211_key_conf * key,struct iwl_mld_mcast_key_data * key_data)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
iwl_mld_d3_update_mcast_key(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_wowlan_status * wowlan_status,struct ieee80211_key_conf * key,struct iwl_mld_mcast_key_data * key_data)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
iwl_mld_update_ptk_rx_seq(struct iwl_mld * mld,struct iwl_mld_wowlan_status * wowlan_status,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,bool is_tkip)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
iwl_mld_resume_keys_iter(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,void * _data)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
iwl_mld_add_mcast_rekey(struct ieee80211_vif * vif,struct iwl_mld * mld,struct iwl_mld_mcast_key_data * key_data,struct ieee80211_bss_conf * link_conf,u32 cipher)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
iwl_mld_add_all_rekeys(struct ieee80211_vif * vif,struct iwl_mld_wowlan_status * wowlan_status,struct iwl_mld_resume_key_iter_data * key_iter_data,struct ieee80211_bss_conf * link_conf)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
iwl_mld_update_sec_keys(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_wowlan_status * wowlan_status)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
iwl_mld_process_wowlan_status(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_wowlan_status * wowlan_status)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
iwl_mld_netdetect_match_info_handler(struct iwl_mld * mld,struct iwl_mld_resume_data * resume_data,struct iwl_rx_packet * pkt)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
iwl_mld_set_netdetect_info(struct iwl_mld * mld,const struct cfg80211_sched_scan_request * netdetect_cfg,struct cfg80211_wowlan_nd_info * netdetect_info,struct iwl_mld_netdetect_res * netdetect_res,unsigned long matched_profiles)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
iwl_mld_process_netdetect_res(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_resume_data * resume_data)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 
iwl_mld_handle_d3_notif(struct iwl_notif_wait_data * notif_wait,struct iwl_rx_packet * pkt,void * data)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 
iwl_mld_wait_d3_notif(struct iwl_mld * mld,struct iwl_mld_resume_data * resume_data,bool with_wowlan)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 
iwl_mld_no_wowlan_suspend(struct iwl_mld * mld)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 
iwl_mld_no_wowlan_resume(struct iwl_mld * mld)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
iwl_mld_aes_seq_to_le64_pn(struct ieee80211_key_conf * key,__le64 * key_rsc)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
iwl_mld_suspend_set_ucast_pn(struct iwl_mld * mld,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,__le64 * key_rsc)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
iwl_mld_suspend_convert_tkip_ipn(struct ieee80211_key_conf * key,__le64 * rsc)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
iwl_mld_suspend_key_data_iter(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,void * _data)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
iwl_mld_send_kek_kck_cmd(struct iwl_mld * mld,struct iwl_mld_vif * mld_vif,struct iwl_mld_suspend_key_iter_data data,int ap_sta_id)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
iwl_mld_suspend_send_security_cmds(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_vif * mld_vif,int ap_sta_id)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
iwl_mld_set_wowlan_config_cmd(struct iwl_mld * mld,struct cfg80211_wowlan * wowlan,struct iwl_wowlan_config_cmd * wowlan_config_cmd,struct ieee80211_sta * ap_sta)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 
iwl_mld_send_patterns(struct iwl_mld * mld,struct cfg80211_wowlan * wowlan,int ap_sta_id)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
iwl_mld_send_proto_offload(struct iwl_mld * mld,struct ieee80211_vif * vif,u8 ap_sta_id)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
iwl_mld_wowlan_config(struct iwl_mld * mld,struct ieee80211_vif * bss_vif,struct cfg80211_wowlan * wowlan)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 
iwl_mld_wowlan_suspend(struct iwl_mld * mld,struct cfg80211_wowlan * wowlan)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  */
iwl_mld_wowlan_resume(struct iwl_mld * mld)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