xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/notif.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
1d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2d1e879ecSMiri Korenblit /*
3d1e879ecSMiri Korenblit  * Copyright (C) 2024-2025 Intel Corporation
4d1e879ecSMiri Korenblit  */
5d1e879ecSMiri Korenblit 
6d1e879ecSMiri Korenblit #include "mld.h"
7d1e879ecSMiri Korenblit #include "notif.h"
8d1e879ecSMiri Korenblit #include "scan.h"
9d1e879ecSMiri Korenblit #include "iface.h"
10d1e879ecSMiri Korenblit #include "mlo.h"
11d1e879ecSMiri Korenblit #include "iwl-trans.h"
12d1e879ecSMiri Korenblit #include "fw/file.h"
13d1e879ecSMiri Korenblit #include "fw/dbg.h"
14d1e879ecSMiri Korenblit #include "fw/api/cmdhdr.h"
15d1e879ecSMiri Korenblit #include "fw/api/mac-cfg.h"
16d1e879ecSMiri Korenblit #include "session-protect.h"
17d1e879ecSMiri Korenblit #include "fw/api/time-event.h"
18d1e879ecSMiri Korenblit #include "fw/api/tx.h"
19d1e879ecSMiri Korenblit #include "fw/api/rs.h"
20d1e879ecSMiri Korenblit #include "fw/api/offload.h"
21d1e879ecSMiri Korenblit #include "fw/api/stats.h"
22d1e879ecSMiri Korenblit #include "fw/api/rfi.h"
23d1e879ecSMiri Korenblit #include "fw/api/coex.h"
24d1e879ecSMiri Korenblit 
25d1e879ecSMiri Korenblit #include "mcc.h"
26d1e879ecSMiri Korenblit #include "link.h"
27d1e879ecSMiri Korenblit #include "tx.h"
28d1e879ecSMiri Korenblit #include "rx.h"
29d1e879ecSMiri Korenblit #include "tlc.h"
30d1e879ecSMiri Korenblit #include "agg.h"
31d1e879ecSMiri Korenblit #include "mac80211.h"
32d1e879ecSMiri Korenblit #include "thermal.h"
33d1e879ecSMiri Korenblit #include "roc.h"
34d1e879ecSMiri Korenblit #include "stats.h"
35d1e879ecSMiri Korenblit #include "coex.h"
36d1e879ecSMiri Korenblit #include "time_sync.h"
37d1e879ecSMiri Korenblit #include "ftm-initiator.h"
38d1e879ecSMiri Korenblit 
39d1e879ecSMiri Korenblit /* Please use this in an increasing order of the versions */
40d1e879ecSMiri Korenblit #define CMD_VER_ENTRY(_ver, _struct)			\
41d1e879ecSMiri Korenblit 	{ .size = sizeof(struct _struct), .ver = _ver },
42d1e879ecSMiri Korenblit #define CMD_VERSIONS(name, ...)				\
43d1e879ecSMiri Korenblit 	static const struct iwl_notif_struct_size	\
44d1e879ecSMiri Korenblit 	iwl_notif_struct_sizes_##name[] = { __VA_ARGS__ };
45d1e879ecSMiri Korenblit 
46d1e879ecSMiri Korenblit #define RX_HANDLER_NO_OBJECT(_grp, _cmd, _name, _context)		\
47d1e879ecSMiri Korenblit 	{.cmd_id = WIDE_ID(_grp, _cmd),					\
48d1e879ecSMiri Korenblit 	 .context = _context,						\
49d1e879ecSMiri Korenblit 	 .fn = iwl_mld_handle_##_name,					\
50d1e879ecSMiri Korenblit 	 .sizes = iwl_notif_struct_sizes_##_name,			\
51d1e879ecSMiri Korenblit 	 .n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name),		\
52d1e879ecSMiri Korenblit 	},
53d1e879ecSMiri Korenblit 
54d1e879ecSMiri Korenblit /* Use this for Rx handlers that do not need notification validation */
55d1e879ecSMiri Korenblit #define RX_HANDLER_NO_VAL(_grp, _cmd, _name, _context)			\
56d1e879ecSMiri Korenblit 	{.cmd_id = WIDE_ID(_grp, _cmd),					\
57d1e879ecSMiri Korenblit 	 .context = _context,						\
58d1e879ecSMiri Korenblit 	 .fn = iwl_mld_handle_##_name,					\
59d1e879ecSMiri Korenblit 	},
60d1e879ecSMiri Korenblit 
61d1e879ecSMiri Korenblit #define RX_HANDLER_VAL_FN(_grp, _cmd, _name, _context)			\
62d1e879ecSMiri Korenblit 	{ .cmd_id = WIDE_ID(_grp, _cmd),				\
63d1e879ecSMiri Korenblit 	  .context = _context,						\
64d1e879ecSMiri Korenblit 	  .fn = iwl_mld_handle_##_name,					\
65d1e879ecSMiri Korenblit 	  .val_fn = iwl_mld_validate_##_name,				\
66d1e879ecSMiri Korenblit 	},
67d1e879ecSMiri Korenblit 
68d1e879ecSMiri Korenblit #define DEFINE_SIMPLE_CANCELLATION(name, notif_struct, id_member)		\
69d1e879ecSMiri Korenblit static bool iwl_mld_cancel_##name##_notif(struct iwl_mld *mld,			\
70d1e879ecSMiri Korenblit 					  struct iwl_rx_packet *pkt,		\
71d1e879ecSMiri Korenblit 					  u32 obj_id)				\
72d1e879ecSMiri Korenblit {										\
73d1e879ecSMiri Korenblit 	const struct notif_struct *notif = (const void *)pkt->data;		\
74d1e879ecSMiri Korenblit 										\
75d1e879ecSMiri Korenblit 	return obj_id == _Generic((notif)->id_member,				\
76d1e879ecSMiri Korenblit 				  __le32: le32_to_cpu((notif)->id_member),	\
77d1e879ecSMiri Korenblit 				  __le16: le16_to_cpu((notif)->id_member),	\
78d1e879ecSMiri Korenblit 				  u8: (notif)->id_member);			\
79d1e879ecSMiri Korenblit }
80d1e879ecSMiri Korenblit 
iwl_mld_always_cancel(struct iwl_mld * mld,struct iwl_rx_packet * pkt,u32 obj_id)81d1e879ecSMiri Korenblit static bool iwl_mld_always_cancel(struct iwl_mld *mld,
82d1e879ecSMiri Korenblit 				  struct iwl_rx_packet *pkt,
83d1e879ecSMiri Korenblit 				  u32 obj_id)
84d1e879ecSMiri Korenblit {
85d1e879ecSMiri Korenblit 	return true;
86d1e879ecSMiri Korenblit }
87d1e879ecSMiri Korenblit 
88d1e879ecSMiri Korenblit /* Currently only defined for the RX_HANDLER_SIZES options. Use this for
89d1e879ecSMiri Korenblit  * notifications that belong to a specific object, and that should be
90d1e879ecSMiri Korenblit  * canceled when the object is removed
91d1e879ecSMiri Korenblit  */
92d1e879ecSMiri Korenblit #define RX_HANDLER_OF_OBJ(_grp, _cmd, _name, _obj_type)			\
93d1e879ecSMiri Korenblit 	{.cmd_id = WIDE_ID(_grp, _cmd),					\
94d1e879ecSMiri Korenblit 	/* Only async handlers can be canceled */			\
95d1e879ecSMiri Korenblit 	 .context = RX_HANDLER_ASYNC,					\
96d1e879ecSMiri Korenblit 	 .fn = iwl_mld_handle_##_name,					\
97d1e879ecSMiri Korenblit 	 .sizes = iwl_notif_struct_sizes_##_name,			\
98d1e879ecSMiri Korenblit 	 .n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name),		\
99d1e879ecSMiri Korenblit 	 .obj_type = IWL_MLD_OBJECT_TYPE_##_obj_type,			\
100d1e879ecSMiri Korenblit 	 .cancel = iwl_mld_cancel_##_name,				\
101d1e879ecSMiri Korenblit 	 },
102d1e879ecSMiri Korenblit 
103d1e879ecSMiri Korenblit #define RX_HANDLER_OF_LINK(_grp, _cmd, _name)				\
104d1e879ecSMiri Korenblit 	RX_HANDLER_OF_OBJ(_grp, _cmd, _name, LINK)			\
105d1e879ecSMiri Korenblit 
106d1e879ecSMiri Korenblit #define RX_HANDLER_OF_VIF(_grp, _cmd, _name)				\
107d1e879ecSMiri Korenblit 	RX_HANDLER_OF_OBJ(_grp, _cmd, _name, VIF)			\
108d1e879ecSMiri Korenblit 
109d1e879ecSMiri Korenblit #define RX_HANDLER_OF_STA(_grp, _cmd, _name)				\
110d1e879ecSMiri Korenblit 	RX_HANDLER_OF_OBJ(_grp, _cmd, _name, STA)			\
111d1e879ecSMiri Korenblit 
112d1e879ecSMiri Korenblit #define RX_HANDLER_OF_ROC(_grp, _cmd, _name)				\
113d1e879ecSMiri Korenblit 	RX_HANDLER_OF_OBJ(_grp, _cmd, _name, ROC)
114d1e879ecSMiri Korenblit 
115d1e879ecSMiri Korenblit #define RX_HANDLER_OF_SCAN(_grp, _cmd, _name)				\
116d1e879ecSMiri Korenblit 	RX_HANDLER_OF_OBJ(_grp, _cmd, _name, SCAN)
117d1e879ecSMiri Korenblit 
118d1e879ecSMiri Korenblit #define RX_HANDLER_OF_FTM_REQ(_grp, _cmd, _name)				\
119d1e879ecSMiri Korenblit 	RX_HANDLER_OF_OBJ(_grp, _cmd, _name, FTM_REQ)
120d1e879ecSMiri Korenblit 
iwl_mld_handle_mfuart_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)121d1e879ecSMiri Korenblit static void iwl_mld_handle_mfuart_notif(struct iwl_mld *mld,
122d1e879ecSMiri Korenblit 					struct iwl_rx_packet *pkt)
123d1e879ecSMiri Korenblit {
124d1e879ecSMiri Korenblit 	struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;
125d1e879ecSMiri Korenblit 
126d1e879ecSMiri Korenblit 	IWL_DEBUG_INFO(mld,
127d1e879ecSMiri Korenblit 		       "MFUART: installed ver: 0x%08x, external ver: 0x%08x\n",
128d1e879ecSMiri Korenblit 		       le32_to_cpu(mfuart_notif->installed_ver),
129d1e879ecSMiri Korenblit 		       le32_to_cpu(mfuart_notif->external_ver));
130d1e879ecSMiri Korenblit 	IWL_DEBUG_INFO(mld,
131d1e879ecSMiri Korenblit 		       "MFUART: status: 0x%08x, duration: 0x%08x image size: 0x%08x\n",
132d1e879ecSMiri Korenblit 		       le32_to_cpu(mfuart_notif->status),
133d1e879ecSMiri Korenblit 		       le32_to_cpu(mfuart_notif->duration),
134d1e879ecSMiri Korenblit 		       le32_to_cpu(mfuart_notif->image_size));
135d1e879ecSMiri Korenblit }
136d1e879ecSMiri Korenblit 
iwl_mld_mu_mimo_iface_iterator(void * _data,u8 * mac,struct ieee80211_vif * vif)137d1e879ecSMiri Korenblit static void iwl_mld_mu_mimo_iface_iterator(void *_data, u8 *mac,
138d1e879ecSMiri Korenblit 					   struct ieee80211_vif *vif)
139d1e879ecSMiri Korenblit {
140d1e879ecSMiri Korenblit 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
141d1e879ecSMiri Korenblit 	unsigned int link_id = 0;
142d1e879ecSMiri Korenblit 
143d1e879ecSMiri Korenblit 	if (WARN(hweight16(vif->active_links) > 1,
144d1e879ecSMiri Korenblit 		 "no support for this notif while in EMLSR 0x%x\n",
145d1e879ecSMiri Korenblit 		 vif->active_links))
146d1e879ecSMiri Korenblit 		return;
147d1e879ecSMiri Korenblit 
148d1e879ecSMiri Korenblit 	if (ieee80211_vif_is_mld(vif)) {
149d1e879ecSMiri Korenblit 		link_id = __ffs(vif->active_links);
150d1e879ecSMiri Korenblit 		bss_conf = link_conf_dereference_check(vif, link_id);
151d1e879ecSMiri Korenblit 	}
152d1e879ecSMiri Korenblit 
153d1e879ecSMiri Korenblit 	if (!WARN_ON(!bss_conf) && bss_conf->mu_mimo_owner) {
154d1e879ecSMiri Korenblit 		const struct iwl_mu_group_mgmt_notif *notif = _data;
155d1e879ecSMiri Korenblit 
156d1e879ecSMiri Korenblit 		BUILD_BUG_ON(sizeof(notif->membership_status) !=
157d1e879ecSMiri Korenblit 			     WLAN_MEMBERSHIP_LEN);
158d1e879ecSMiri Korenblit 		BUILD_BUG_ON(sizeof(notif->user_position) !=
159d1e879ecSMiri Korenblit 			     WLAN_USER_POSITION_LEN);
160d1e879ecSMiri Korenblit 
161d1e879ecSMiri Korenblit 		/* MU-MIMO Group Id action frame is little endian. We treat
162d1e879ecSMiri Korenblit 		 * the data received from firmware as if it came from the
163d1e879ecSMiri Korenblit 		 * action frame, so no conversion is needed.
164d1e879ecSMiri Korenblit 		 */
165d1e879ecSMiri Korenblit 		ieee80211_update_mu_groups(vif, link_id,
166d1e879ecSMiri Korenblit 					   (u8 *)&notif->membership_status,
167d1e879ecSMiri Korenblit 					   (u8 *)&notif->user_position);
168d1e879ecSMiri Korenblit 	}
169d1e879ecSMiri Korenblit }
170d1e879ecSMiri Korenblit 
171d1e879ecSMiri Korenblit /* This handler is called in SYNC mode because it needs to be serialized with
172d1e879ecSMiri Korenblit  * Rx as specified in ieee80211_update_mu_groups()'s documentation.
173d1e879ecSMiri Korenblit  */
iwl_mld_handle_mu_mimo_grp_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)174d1e879ecSMiri Korenblit static void iwl_mld_handle_mu_mimo_grp_notif(struct iwl_mld *mld,
175d1e879ecSMiri Korenblit 					     struct iwl_rx_packet *pkt)
176d1e879ecSMiri Korenblit {
177d1e879ecSMiri Korenblit 	struct iwl_mu_group_mgmt_notif *notif = (void *)pkt->data;
178d1e879ecSMiri Korenblit 
179d1e879ecSMiri Korenblit 	ieee80211_iterate_active_interfaces_atomic(mld->hw,
180d1e879ecSMiri Korenblit 						   IEEE80211_IFACE_ITER_NORMAL,
181d1e879ecSMiri Korenblit 						   iwl_mld_mu_mimo_iface_iterator,
182d1e879ecSMiri Korenblit 						   notif);
183d1e879ecSMiri Korenblit }
184d1e879ecSMiri Korenblit 
185d1e879ecSMiri Korenblit static void
iwl_mld_handle_stored_beacon_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)186d1e879ecSMiri Korenblit iwl_mld_handle_stored_beacon_notif(struct iwl_mld *mld,
187d1e879ecSMiri Korenblit 				   struct iwl_rx_packet *pkt)
188d1e879ecSMiri Korenblit {
189d1e879ecSMiri Korenblit 	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
190d1e879ecSMiri Korenblit 	struct iwl_stored_beacon_notif *sb = (void *)pkt->data;
191d1e879ecSMiri Korenblit 	struct ieee80211_rx_status rx_status = {};
192d1e879ecSMiri Korenblit 	struct sk_buff *skb;
193d1e879ecSMiri Korenblit 	u32 size = le32_to_cpu(sb->common.byte_count);
194d1e879ecSMiri Korenblit 
195d1e879ecSMiri Korenblit 	if (size == 0)
196d1e879ecSMiri Korenblit 		return;
197d1e879ecSMiri Korenblit 
198d1e879ecSMiri Korenblit 	if (pkt_len < struct_size(sb, data, size))
199d1e879ecSMiri Korenblit 		return;
200d1e879ecSMiri Korenblit 
201d1e879ecSMiri Korenblit 	skb = alloc_skb(size, GFP_ATOMIC);
202d1e879ecSMiri Korenblit 	if (!skb) {
203d1e879ecSMiri Korenblit 		IWL_ERR(mld, "alloc_skb failed\n");
204d1e879ecSMiri Korenblit 		return;
205d1e879ecSMiri Korenblit 	}
206d1e879ecSMiri Korenblit 
207d1e879ecSMiri Korenblit 	/* update rx_status according to the notification's metadata */
208d1e879ecSMiri Korenblit 	rx_status.mactime = le64_to_cpu(sb->common.tsf);
209d1e879ecSMiri Korenblit 	/* TSF as indicated by the firmware  is at INA time */
210d1e879ecSMiri Korenblit 	rx_status.flag |= RX_FLAG_MACTIME_PLCP_START;
211d1e879ecSMiri Korenblit 	rx_status.device_timestamp = le32_to_cpu(sb->common.system_time);
212d1e879ecSMiri Korenblit 	rx_status.band =
213d1e879ecSMiri Korenblit 		iwl_mld_phy_band_to_nl80211(le16_to_cpu(sb->common.band));
214d1e879ecSMiri Korenblit 	rx_status.freq =
215d1e879ecSMiri Korenblit 		ieee80211_channel_to_frequency(le16_to_cpu(sb->common.channel),
216d1e879ecSMiri Korenblit 					       rx_status.band);
217d1e879ecSMiri Korenblit 
218d1e879ecSMiri Korenblit 	/* copy the data */
219d1e879ecSMiri Korenblit 	skb_put_data(skb, sb->data, size);
220d1e879ecSMiri Korenblit 	memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
221d1e879ecSMiri Korenblit 
222d1e879ecSMiri Korenblit 	/* pass it as regular rx to mac80211 */
223d1e879ecSMiri Korenblit 	ieee80211_rx_napi(mld->hw, NULL, skb, NULL);
224d1e879ecSMiri Korenblit }
225d1e879ecSMiri Korenblit 
226d1e879ecSMiri Korenblit static void
iwl_mld_handle_channel_switch_start_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)227d1e879ecSMiri Korenblit iwl_mld_handle_channel_switch_start_notif(struct iwl_mld *mld,
228d1e879ecSMiri Korenblit 					  struct iwl_rx_packet *pkt)
229d1e879ecSMiri Korenblit {
230d1e879ecSMiri Korenblit 	struct iwl_channel_switch_start_notif *notif = (void *)pkt->data;
231d1e879ecSMiri Korenblit 	u32 link_id = le32_to_cpu(notif->link_id);
232d1e879ecSMiri Korenblit 	struct ieee80211_bss_conf *link_conf =
233d1e879ecSMiri Korenblit 		iwl_mld_fw_id_to_link_conf(mld, link_id);
234d1e879ecSMiri Korenblit 	struct ieee80211_vif *vif;
235d1e879ecSMiri Korenblit 
236d1e879ecSMiri Korenblit 	if (WARN_ON(!link_conf))
237d1e879ecSMiri Korenblit 		return;
238d1e879ecSMiri Korenblit 
239d1e879ecSMiri Korenblit 	vif = link_conf->vif;
240d1e879ecSMiri Korenblit 
241d1e879ecSMiri Korenblit 	IWL_DEBUG_INFO(mld,
242d1e879ecSMiri Korenblit 		       "CSA Start Notification with vif type: %d, link_id: %d\n",
243d1e879ecSMiri Korenblit 		       vif->type,
244d1e879ecSMiri Korenblit 		       link_conf->link_id);
245d1e879ecSMiri Korenblit 
246d1e879ecSMiri Korenblit 	switch (vif->type) {
247d1e879ecSMiri Korenblit 	case NL80211_IFTYPE_AP:
248d1e879ecSMiri Korenblit 		/* We don't support canceling a CSA as it was advertised
249d1e879ecSMiri Korenblit 		 * by the AP itself
250d1e879ecSMiri Korenblit 		 */
251d1e879ecSMiri Korenblit 		if (!link_conf->csa_active)
252d1e879ecSMiri Korenblit 			return;
253d1e879ecSMiri Korenblit 
254d1e879ecSMiri Korenblit 		ieee80211_csa_finish(vif, link_conf->link_id);
255d1e879ecSMiri Korenblit 		break;
256d1e879ecSMiri Korenblit 	case NL80211_IFTYPE_STATION:
257d1e879ecSMiri Korenblit 		if (!link_conf->csa_active) {
258d1e879ecSMiri Korenblit 			/* Either unexpected cs notif or mac80211 chose to
259d1e879ecSMiri Korenblit 			 * ignore, for example in channel switch to same channel
260d1e879ecSMiri Korenblit 			 */
261d1e879ecSMiri Korenblit 			struct iwl_cancel_channel_switch_cmd cmd = {
262d1e879ecSMiri Korenblit 				.id = cpu_to_le32(link_id),
263d1e879ecSMiri Korenblit 			};
264d1e879ecSMiri Korenblit 
265d1e879ecSMiri Korenblit 			if (iwl_mld_send_cmd_pdu(mld,
266d1e879ecSMiri Korenblit 						 WIDE_ID(MAC_CONF_GROUP,
267d1e879ecSMiri Korenblit 							 CANCEL_CHANNEL_SWITCH_CMD),
268d1e879ecSMiri Korenblit 						 &cmd))
269d1e879ecSMiri Korenblit 				IWL_ERR(mld,
270d1e879ecSMiri Korenblit 					"Failed to cancel the channel switch\n");
271d1e879ecSMiri Korenblit 			return;
272d1e879ecSMiri Korenblit 		}
273d1e879ecSMiri Korenblit 
274d1e879ecSMiri Korenblit 		ieee80211_chswitch_done(vif, true, link_conf->link_id);
275d1e879ecSMiri Korenblit 		break;
276d1e879ecSMiri Korenblit 
277d1e879ecSMiri Korenblit 	default:
278d1e879ecSMiri Korenblit 		WARN(1, "CSA on invalid vif type: %d", vif->type);
279d1e879ecSMiri Korenblit 	}
280d1e879ecSMiri Korenblit }
281d1e879ecSMiri Korenblit 
282d1e879ecSMiri Korenblit static void
iwl_mld_handle_channel_switch_error_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)283d1e879ecSMiri Korenblit iwl_mld_handle_channel_switch_error_notif(struct iwl_mld *mld,
284d1e879ecSMiri Korenblit 					  struct iwl_rx_packet *pkt)
285d1e879ecSMiri Korenblit {
286d1e879ecSMiri Korenblit 	struct iwl_channel_switch_error_notif *notif = (void *)pkt->data;
287d1e879ecSMiri Korenblit 	struct ieee80211_bss_conf *link_conf;
288d1e879ecSMiri Korenblit 	struct ieee80211_vif *vif;
289d1e879ecSMiri Korenblit 	u32 link_id = le32_to_cpu(notif->link_id);
290d1e879ecSMiri Korenblit 	u32 csa_err_mask = le32_to_cpu(notif->csa_err_mask);
291d1e879ecSMiri Korenblit 
292d1e879ecSMiri Korenblit 	link_conf = iwl_mld_fw_id_to_link_conf(mld, link_id);
293d1e879ecSMiri Korenblit 	if (WARN_ON(!link_conf))
294d1e879ecSMiri Korenblit 		return;
295d1e879ecSMiri Korenblit 
296d1e879ecSMiri Korenblit 	vif = link_conf->vif;
297d1e879ecSMiri Korenblit 
298d1e879ecSMiri Korenblit 	IWL_DEBUG_INFO(mld, "FW reports CSA error: id=%u, csa_err_mask=%u\n",
299d1e879ecSMiri Korenblit 		       link_id, csa_err_mask);
300d1e879ecSMiri Korenblit 
301d1e879ecSMiri Korenblit 	if (csa_err_mask & (CS_ERR_COUNT_ERROR |
302d1e879ecSMiri Korenblit 			    CS_ERR_LONG_DELAY_AFTER_CS |
303d1e879ecSMiri Korenblit 			    CS_ERR_TX_BLOCK_TIMER_EXPIRED))
304d1e879ecSMiri Korenblit 		ieee80211_channel_switch_disconnect(vif);
305d1e879ecSMiri Korenblit }
306d1e879ecSMiri Korenblit 
iwl_mld_handle_beacon_notification(struct iwl_mld * mld,struct iwl_rx_packet * pkt)307d1e879ecSMiri Korenblit static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld,
308d1e879ecSMiri Korenblit 					       struct iwl_rx_packet *pkt)
309d1e879ecSMiri Korenblit {
310d1e879ecSMiri Korenblit 	struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
311d1e879ecSMiri Korenblit 
312d1e879ecSMiri Korenblit 	mld->ibss_manager = !!beacon->ibss_mgr_status;
313d1e879ecSMiri Korenblit }
314d1e879ecSMiri Korenblit 
315d1e879ecSMiri Korenblit /**
316d1e879ecSMiri Korenblit  * DOC: Notification versioning
317d1e879ecSMiri Korenblit  *
318d1e879ecSMiri Korenblit  * The firmware's notifications change from time to time. In order to
319d1e879ecSMiri Korenblit  * differentiate between different versions of the same notification, the
320d1e879ecSMiri Korenblit  * firmware advertises the version of each notification.
321d1e879ecSMiri Korenblit  * Here are listed all the notifications that are supported. Several versions
322d1e879ecSMiri Korenblit  * of the same notification can be allowed at the same time:
323d1e879ecSMiri Korenblit  *
324d1e879ecSMiri Korenblit  * CMD_VERSION(my_multi_version_notif,
325d1e879ecSMiri Korenblit  *	       CMD_VER_ENTRY(1, iwl_my_multi_version_notif_ver1)
326d1e879ecSMiri Korenblit  *	       CMD_VER_ENTRY(2, iwl_my_multi_version_notif_ver2)
327d1e879ecSMiri Korenblit  *
328d1e879ecSMiri Korenblit  * etc...
329d1e879ecSMiri Korenblit  *
330d1e879ecSMiri Korenblit  * The driver will enforce that the notification coming from the firmware
331d1e879ecSMiri Korenblit  * has its version listed here and it'll also enforce that the firmware sent
332d1e879ecSMiri Korenblit  * at least enough bytes to cover the structure listed in the CMD_VER_ENTRY.
333d1e879ecSMiri Korenblit  */
334d1e879ecSMiri Korenblit 
335d1e879ecSMiri Korenblit CMD_VERSIONS(scan_complete_notif,
336d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_umac_scan_complete))
337d1e879ecSMiri Korenblit CMD_VERSIONS(scan_iter_complete_notif,
338d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(2, iwl_umac_scan_iter_complete_notif))
339d1e879ecSMiri Korenblit CMD_VERSIONS(mfuart_notif,
340d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(2, iwl_mfuart_load_notif))
341d1e879ecSMiri Korenblit CMD_VERSIONS(update_mcc,
342d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_mcc_chub_notif))
343d1e879ecSMiri Korenblit CMD_VERSIONS(session_prot_notif,
344d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(3, iwl_session_prot_notif))
345d1e879ecSMiri Korenblit CMD_VERSIONS(missed_beacon_notif,
346d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(5, iwl_missed_beacons_notif))
347d1e879ecSMiri Korenblit CMD_VERSIONS(tx_resp_notif,
348d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(8, iwl_tx_resp))
349d1e879ecSMiri Korenblit CMD_VERSIONS(compressed_ba_notif,
350*630b6c09SEmmanuel Grumbach 	     CMD_VER_ENTRY(5, iwl_compressed_ba_notif)
351*630b6c09SEmmanuel Grumbach 	     CMD_VER_ENTRY(6, iwl_compressed_ba_notif))
352d1e879ecSMiri Korenblit CMD_VERSIONS(tlc_notif,
353d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(3, iwl_tlc_update_notif))
354d1e879ecSMiri Korenblit CMD_VERSIONS(mu_mimo_grp_notif,
355d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif))
356d1e879ecSMiri Korenblit CMD_VERSIONS(channel_switch_start_notif,
357d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(3, iwl_channel_switch_start_notif))
358d1e879ecSMiri Korenblit CMD_VERSIONS(channel_switch_error_notif,
359d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(2, iwl_channel_switch_error_notif))
360d1e879ecSMiri Korenblit CMD_VERSIONS(ct_kill_notif,
361d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(2, ct_kill_notif))
362d1e879ecSMiri Korenblit CMD_VERSIONS(temp_notif,
363d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(2, iwl_dts_measurement_notif))
364d1e879ecSMiri Korenblit CMD_VERSIONS(stored_beacon_notif,
365d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(4, iwl_stored_beacon_notif))
366d1e879ecSMiri Korenblit CMD_VERSIONS(roc_notif,
367d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_roc_notif))
368d1e879ecSMiri Korenblit CMD_VERSIONS(probe_resp_data_notif,
369d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_probe_resp_data_notif))
370d1e879ecSMiri Korenblit CMD_VERSIONS(datapath_monitor_notif,
371d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_datapath_monitor_notif))
372d1e879ecSMiri Korenblit CMD_VERSIONS(stats_oper_notif,
373d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper))
374d1e879ecSMiri Korenblit CMD_VERSIONS(stats_oper_part1_notif,
375d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(4, iwl_system_statistics_part1_notif_oper))
376d1e879ecSMiri Korenblit CMD_VERSIONS(bt_coex_notif,
377d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_bt_coex_profile_notif))
378d1e879ecSMiri Korenblit CMD_VERSIONS(beacon_notification,
379d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(6, iwl_extended_beacon_notif))
380d1e879ecSMiri Korenblit CMD_VERSIONS(emlsr_mode_notif,
381d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_esr_mode_notif))
382d1e879ecSMiri Korenblit CMD_VERSIONS(emlsr_trans_fail_notif,
383d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif))
384d1e879ecSMiri Korenblit CMD_VERSIONS(uapsd_misbehaving_ap_notif,
385d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_uapsd_misbehaving_ap_notif))
386d1e879ecSMiri Korenblit CMD_VERSIONS(time_msmt_notif,
387d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_time_msmt_notify))
388d1e879ecSMiri Korenblit CMD_VERSIONS(time_sync_confirm_notif,
389d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_time_msmt_cfm_notify))
390d1e879ecSMiri Korenblit CMD_VERSIONS(omi_status_notif,
391d1e879ecSMiri Korenblit 	     CMD_VER_ENTRY(1, iwl_omi_send_status_notif))
392d1e879ecSMiri Korenblit CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(9, iwl_tof_range_rsp_ntfy))
393d1e879ecSMiri Korenblit 
394d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id)
395d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id)
396d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(channel_switch_start,
397d1e879ecSMiri Korenblit 			   iwl_channel_switch_start_notif, link_id)
398d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(channel_switch_error,
399d1e879ecSMiri Korenblit 			   iwl_channel_switch_error_notif, link_id)
400d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif,
401d1e879ecSMiri Korenblit 			   link_id)
402d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity)
403d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
404d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,
405d1e879ecSMiri Korenblit 			   mac_id)
406d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,
407d1e879ecSMiri Korenblit 			   mac_id)
408d1e879ecSMiri Korenblit #define iwl_mld_cancel_omi_status_notif iwl_mld_always_cancel
409d1e879ecSMiri Korenblit DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id)
410d1e879ecSMiri Korenblit 
411d1e879ecSMiri Korenblit /**
412d1e879ecSMiri Korenblit  * DOC: Handlers for fw notifications
413d1e879ecSMiri Korenblit  *
414d1e879ecSMiri Korenblit  * Here are listed the notifications IDs (including the group ID), the handler
415d1e879ecSMiri Korenblit  * of the notification and how it should be called:
416d1e879ecSMiri Korenblit  *
417d1e879ecSMiri Korenblit  *  - RX_HANDLER_SYNC: will be called as part of the Rx path
418d1e879ecSMiri Korenblit  *  - RX_HANDLER_ASYNC: will be handled in a working with the wiphy_lock held
419d1e879ecSMiri Korenblit  *
420d1e879ecSMiri Korenblit  * This means that if the firmware sends two notifications A and B in that
421d1e879ecSMiri Korenblit  * order and notification A is RX_HANDLER_ASYNC and notification is
422d1e879ecSMiri Korenblit  * RX_HANDLER_SYNC, the handler of B will likely be called before the handler
423d1e879ecSMiri Korenblit  * of A.
424d1e879ecSMiri Korenblit  *
425d1e879ecSMiri Korenblit  * This list should be in order of frequency for performance purposes.
426d1e879ecSMiri Korenblit  * The handler can be one from two contexts, see &iwl_rx_handler_context
427d1e879ecSMiri Korenblit  *
428d1e879ecSMiri Korenblit  * A handler can declare that it relies on a specific object in which case it
429d1e879ecSMiri Korenblit  * can be cancelled in case the object is deleted. In order to use this
430d1e879ecSMiri Korenblit  * mechanism, a cancellation function is needed. The cancellation function must
431d1e879ecSMiri Korenblit  * receive an object id (the index of that object in the firmware) and a
432d1e879ecSMiri Korenblit  * notification payload. It'll return true if that specific notification should
433d1e879ecSMiri Korenblit  * be cancelled upon the obliteration of the specific instance of the object.
434d1e879ecSMiri Korenblit  *
435d1e879ecSMiri Korenblit  * DEFINE_SIMPLE_CANCELLATION allows to easily create a cancellation function
436d1e879ecSMiri Korenblit  * that wills simply return true if a given object id matches the object id in
437d1e879ecSMiri Korenblit  * the firmware notification.
438d1e879ecSMiri Korenblit  */
439d1e879ecSMiri Korenblit 
440d1e879ecSMiri Korenblit VISIBLE_IF_IWLWIFI_KUNIT
441d1e879ecSMiri Korenblit const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
442d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(LEGACY_GROUP, TX_CMD, tx_resp_notif,
443d1e879ecSMiri Korenblit 			     RX_HANDLER_SYNC)
444d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif,
445d1e879ecSMiri Korenblit 			     RX_HANDLER_SYNC)
446d1e879ecSMiri Korenblit 	RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC,
447d1e879ecSMiri Korenblit 			   scan_complete_notif)
448d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC,
449d1e879ecSMiri Korenblit 			     scan_iter_complete_notif,
450d1e879ecSMiri Korenblit 			     RX_HANDLER_SYNC)
451d1e879ecSMiri Korenblit 	RX_HANDLER_NO_VAL(LEGACY_GROUP, MATCH_FOUND_NOTIFICATION,
452d1e879ecSMiri Korenblit 			  match_found_notif, RX_HANDLER_SYNC)
453d1e879ecSMiri Korenblit 
454d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_NOTIF,
455d1e879ecSMiri Korenblit 			     stats_oper_notif, RX_HANDLER_ASYNC)
456d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF,
457d1e879ecSMiri Korenblit 			     stats_oper_part1_notif, RX_HANDLER_ASYNC)
458d1e879ecSMiri Korenblit 
459d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MFUART_LOAD_NOTIFICATION,
460d1e879ecSMiri Korenblit 			     mfuart_notif, RX_HANDLER_SYNC)
461d1e879ecSMiri Korenblit 
462d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE,
463d1e879ecSMiri Korenblit 			     temp_notif, RX_HANDLER_ASYNC)
464d1e879ecSMiri Korenblit 	RX_HANDLER_OF_LINK(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF,
465d1e879ecSMiri Korenblit 			   session_prot_notif)
466d1e879ecSMiri Korenblit 	RX_HANDLER_OF_LINK(MAC_CONF_GROUP, MISSED_BEACONS_NOTIF,
467d1e879ecSMiri Korenblit 			   missed_beacon_notif)
468d1e879ecSMiri Korenblit 	RX_HANDLER_OF_STA(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF, tlc_notif)
469d1e879ecSMiri Korenblit 	RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_START_NOTIF,
470d1e879ecSMiri Korenblit 			   channel_switch_start_notif)
471d1e879ecSMiri Korenblit 	RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_ERROR_NOTIF,
472d1e879ecSMiri Korenblit 			   channel_switch_error_notif)
473d1e879ecSMiri Korenblit 	RX_HANDLER_OF_ROC(MAC_CONF_GROUP, ROC_NOTIF, roc_notif)
474d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF,
475d1e879ecSMiri Korenblit 			     mu_mimo_grp_notif, RX_HANDLER_SYNC)
476d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF,
477d1e879ecSMiri Korenblit 			     stored_beacon_notif, RX_HANDLER_SYNC)
478d1e879ecSMiri Korenblit 	RX_HANDLER_OF_VIF(MAC_CONF_GROUP, PROBE_RESPONSE_DATA_NOTIF,
479d1e879ecSMiri Korenblit 			  probe_resp_data_notif)
480d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, CT_KILL_NOTIFICATION,
481d1e879ecSMiri Korenblit 			     ct_kill_notif, RX_HANDLER_ASYNC)
482d1e879ecSMiri Korenblit 	RX_HANDLER_OF_LINK(DATA_PATH_GROUP, MONITOR_NOTIF,
483d1e879ecSMiri Korenblit 			   datapath_monitor_notif)
484d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MCC_CHUB_UPDATE_CMD, update_mcc,
485d1e879ecSMiri Korenblit 			     RX_HANDLER_ASYNC)
486d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(BT_COEX_GROUP, PROFILE_NOTIF,
487d1e879ecSMiri Korenblit 			     bt_coex_notif, RX_HANDLER_ASYNC)
488d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BEACON_NOTIFICATION,
489d1e879ecSMiri Korenblit 			     beacon_notification, RX_HANDLER_ASYNC)
490d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, ESR_MODE_NOTIF,
491d1e879ecSMiri Korenblit 			     emlsr_mode_notif, RX_HANDLER_ASYNC)
492d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF,
493d1e879ecSMiri Korenblit 			     emlsr_trans_fail_notif, RX_HANDLER_ASYNC)
494d1e879ecSMiri Korenblit 	RX_HANDLER_OF_VIF(LEGACY_GROUP, PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
495d1e879ecSMiri Korenblit 			  uapsd_misbehaving_ap_notif)
496d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(LEGACY_GROUP,
497d1e879ecSMiri Korenblit 			     WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION,
498d1e879ecSMiri Korenblit 			     time_msmt_notif, RX_HANDLER_SYNC)
499d1e879ecSMiri Korenblit 	RX_HANDLER_NO_OBJECT(LEGACY_GROUP,
500d1e879ecSMiri Korenblit 			     WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION,
501d1e879ecSMiri Korenblit 			     time_sync_confirm_notif, RX_HANDLER_ASYNC)
502d1e879ecSMiri Korenblit 	RX_HANDLER_OF_LINK(DATA_PATH_GROUP, OMI_SEND_STATUS_NOTIF,
503d1e879ecSMiri Korenblit 			   omi_status_notif)
504d1e879ecSMiri Korenblit 	RX_HANDLER_OF_FTM_REQ(LOCATION_GROUP, TOF_RANGE_RESPONSE_NOTIF,
505d1e879ecSMiri Korenblit 			      ftm_resp_notif)
506d1e879ecSMiri Korenblit };
507d1e879ecSMiri Korenblit EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers);
508d1e879ecSMiri Korenblit 
509d1e879ecSMiri Korenblit #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
510d1e879ecSMiri Korenblit const unsigned int iwl_mld_rx_handlers_num = ARRAY_SIZE(iwl_mld_rx_handlers);
511d1e879ecSMiri Korenblit EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers_num);
512d1e879ecSMiri Korenblit #endif
513d1e879ecSMiri Korenblit 
514d1e879ecSMiri Korenblit static bool
iwl_mld_notif_is_valid(struct iwl_mld * mld,struct iwl_rx_packet * pkt,const struct iwl_rx_handler * handler)515d1e879ecSMiri Korenblit iwl_mld_notif_is_valid(struct iwl_mld *mld, struct iwl_rx_packet *pkt,
516d1e879ecSMiri Korenblit 		       const struct iwl_rx_handler *handler)
517d1e879ecSMiri Korenblit {
518d1e879ecSMiri Korenblit 	unsigned int size = iwl_rx_packet_payload_len(pkt);
519d1e879ecSMiri Korenblit 	size_t notif_ver;
520d1e879ecSMiri Korenblit 
521d1e879ecSMiri Korenblit 	/* If n_sizes == 0, it indicates that a validation function may be used
522d1e879ecSMiri Korenblit 	 * or that no validation is required.
523d1e879ecSMiri Korenblit 	 */
524d1e879ecSMiri Korenblit 	if (!handler->n_sizes) {
525d1e879ecSMiri Korenblit 		if (handler->val_fn)
526d1e879ecSMiri Korenblit 			return handler->val_fn(mld, pkt);
527d1e879ecSMiri Korenblit 		return true;
528d1e879ecSMiri Korenblit 	}
529d1e879ecSMiri Korenblit 
530d1e879ecSMiri Korenblit 	notif_ver = iwl_fw_lookup_notif_ver(mld->fw,
531d1e879ecSMiri Korenblit 					    iwl_cmd_groupid(handler->cmd_id),
532d1e879ecSMiri Korenblit 					    iwl_cmd_opcode(handler->cmd_id),
533d1e879ecSMiri Korenblit 					    IWL_FW_CMD_VER_UNKNOWN);
534d1e879ecSMiri Korenblit 
535d1e879ecSMiri Korenblit 	for (int i = 0; i < handler->n_sizes; i++) {
536d1e879ecSMiri Korenblit 		if (handler->sizes[i].ver != notif_ver)
537d1e879ecSMiri Korenblit 			continue;
538d1e879ecSMiri Korenblit 
539d1e879ecSMiri Korenblit 		if (IWL_FW_CHECK(mld, size < handler->sizes[i].size,
540d1e879ecSMiri Korenblit 				 "unexpected notification 0x%04x size %d, need %d\n",
541d1e879ecSMiri Korenblit 				 handler->cmd_id, size, handler->sizes[i].size))
542d1e879ecSMiri Korenblit 			return false;
543d1e879ecSMiri Korenblit 		return true;
544d1e879ecSMiri Korenblit 	}
545d1e879ecSMiri Korenblit 
546d1e879ecSMiri Korenblit 	IWL_FW_CHECK_FAILED(mld,
547d1e879ecSMiri Korenblit 			    "notif 0x%04x ver %zu missing expected size, use version %u size\n",
548d1e879ecSMiri Korenblit 			    handler->cmd_id, notif_ver,
549d1e879ecSMiri Korenblit 			    handler->sizes[handler->n_sizes - 1].ver);
550d1e879ecSMiri Korenblit 
551d1e879ecSMiri Korenblit 	return size < handler->sizes[handler->n_sizes - 1].size;
552d1e879ecSMiri Korenblit }
553d1e879ecSMiri Korenblit 
554d1e879ecSMiri Korenblit struct iwl_async_handler_entry {
555d1e879ecSMiri Korenblit 	struct list_head list;
556d1e879ecSMiri Korenblit 	struct iwl_rx_cmd_buffer rxb;
557d1e879ecSMiri Korenblit 	const struct iwl_rx_handler *rx_h;
558d1e879ecSMiri Korenblit };
559d1e879ecSMiri Korenblit 
560d1e879ecSMiri Korenblit static void
iwl_mld_log_async_handler_op(struct iwl_mld * mld,const char * op,struct iwl_rx_cmd_buffer * rxb)561d1e879ecSMiri Korenblit iwl_mld_log_async_handler_op(struct iwl_mld *mld, const char *op,
562d1e879ecSMiri Korenblit 			     struct iwl_rx_cmd_buffer *rxb)
563d1e879ecSMiri Korenblit {
564d1e879ecSMiri Korenblit 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
565d1e879ecSMiri Korenblit 
566d1e879ecSMiri Korenblit 	IWL_DEBUG_HC(mld,
567d1e879ecSMiri Korenblit 		     "%s async handler for notif %s (%.2x.%2x, seq 0x%x)\n",
568d1e879ecSMiri Korenblit 		     op, iwl_get_cmd_string(mld->trans,
569d1e879ecSMiri Korenblit 		     WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)),
570d1e879ecSMiri Korenblit 		     pkt->hdr.group_id, pkt->hdr.cmd,
571d1e879ecSMiri Korenblit 		     le16_to_cpu(pkt->hdr.sequence));
572d1e879ecSMiri Korenblit }
573d1e879ecSMiri Korenblit 
iwl_mld_rx_notif(struct iwl_mld * mld,struct iwl_rx_cmd_buffer * rxb,struct iwl_rx_packet * pkt)574d1e879ecSMiri Korenblit static void iwl_mld_rx_notif(struct iwl_mld *mld,
575d1e879ecSMiri Korenblit 			     struct iwl_rx_cmd_buffer *rxb,
576d1e879ecSMiri Korenblit 			     struct iwl_rx_packet *pkt)
577d1e879ecSMiri Korenblit {
578d1e879ecSMiri Korenblit 	for (int i = 0; i < ARRAY_SIZE(iwl_mld_rx_handlers); i++) {
579d1e879ecSMiri Korenblit 		const struct iwl_rx_handler *rx_h = &iwl_mld_rx_handlers[i];
580d1e879ecSMiri Korenblit 		struct iwl_async_handler_entry *entry;
581d1e879ecSMiri Korenblit 
582d1e879ecSMiri Korenblit 		if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd))
583d1e879ecSMiri Korenblit 			continue;
584d1e879ecSMiri Korenblit 
585d1e879ecSMiri Korenblit 		if (!iwl_mld_notif_is_valid(mld, pkt, rx_h))
586d1e879ecSMiri Korenblit 			return;
587d1e879ecSMiri Korenblit 
588d1e879ecSMiri Korenblit 		if (rx_h->context == RX_HANDLER_SYNC) {
589d1e879ecSMiri Korenblit 			rx_h->fn(mld, pkt);
590d1e879ecSMiri Korenblit 			break;
591d1e879ecSMiri Korenblit 		}
592d1e879ecSMiri Korenblit 
593d1e879ecSMiri Korenblit 		entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
594d1e879ecSMiri Korenblit 		/* we can't do much... */
595d1e879ecSMiri Korenblit 		if (!entry)
596d1e879ecSMiri Korenblit 			return;
597d1e879ecSMiri Korenblit 
598d1e879ecSMiri Korenblit 		/* Set the async handler entry */
599d1e879ecSMiri Korenblit 		entry->rxb._page = rxb_steal_page(rxb);
600d1e879ecSMiri Korenblit 		entry->rxb._offset = rxb->_offset;
601d1e879ecSMiri Korenblit 		entry->rxb._rx_page_order = rxb->_rx_page_order;
602d1e879ecSMiri Korenblit 
603d1e879ecSMiri Korenblit 		entry->rx_h = rx_h;
604d1e879ecSMiri Korenblit 
605d1e879ecSMiri Korenblit 		/* Add it to the list and queue the work */
606d1e879ecSMiri Korenblit 		spin_lock(&mld->async_handlers_lock);
607d1e879ecSMiri Korenblit 		list_add_tail(&entry->list, &mld->async_handlers_list);
608d1e879ecSMiri Korenblit 		spin_unlock(&mld->async_handlers_lock);
609d1e879ecSMiri Korenblit 
610d1e879ecSMiri Korenblit 		wiphy_work_queue(mld->hw->wiphy,
611d1e879ecSMiri Korenblit 				 &mld->async_handlers_wk);
612d1e879ecSMiri Korenblit 
613d1e879ecSMiri Korenblit 		iwl_mld_log_async_handler_op(mld, "Queued", rxb);
614d1e879ecSMiri Korenblit 		break;
615d1e879ecSMiri Korenblit 	}
616d1e879ecSMiri Korenblit 
617d1e879ecSMiri Korenblit 	iwl_notification_wait_notify(&mld->notif_wait, pkt);
618d1e879ecSMiri Korenblit }
619d1e879ecSMiri Korenblit 
iwl_mld_rx(struct iwl_op_mode * op_mode,struct napi_struct * napi,struct iwl_rx_cmd_buffer * rxb)620d1e879ecSMiri Korenblit void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,
621d1e879ecSMiri Korenblit 		struct iwl_rx_cmd_buffer *rxb)
622d1e879ecSMiri Korenblit {
623d1e879ecSMiri Korenblit 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
624d1e879ecSMiri Korenblit 	struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
625d1e879ecSMiri Korenblit 	u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);
626d1e879ecSMiri Korenblit 
627d1e879ecSMiri Korenblit 	if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD)))
628d1e879ecSMiri Korenblit 		iwl_mld_rx_mpdu(mld, napi, rxb, 0);
629d1e879ecSMiri Korenblit 	else if (cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))
630d1e879ecSMiri Korenblit 		iwl_mld_handle_frame_release_notif(mld, napi, pkt, 0);
631d1e879ecSMiri Korenblit 	else if (cmd_id == WIDE_ID(LEGACY_GROUP, BAR_FRAME_RELEASE))
632d1e879ecSMiri Korenblit 		iwl_mld_handle_bar_frame_release_notif(mld, napi, pkt, 0);
633d1e879ecSMiri Korenblit 	else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
634d1e879ecSMiri Korenblit 					    RX_QUEUES_NOTIFICATION)))
635d1e879ecSMiri Korenblit 		iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0);
636d1e879ecSMiri Korenblit 	else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF))
637d1e879ecSMiri Korenblit 		iwl_mld_rx_monitor_no_data(mld, napi, pkt, 0);
638d1e879ecSMiri Korenblit 	else
639d1e879ecSMiri Korenblit 		iwl_mld_rx_notif(mld, rxb, pkt);
640d1e879ecSMiri Korenblit }
641d1e879ecSMiri Korenblit 
iwl_mld_rx_rss(struct iwl_op_mode * op_mode,struct napi_struct * napi,struct iwl_rx_cmd_buffer * rxb,unsigned int queue)642d1e879ecSMiri Korenblit void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi,
643d1e879ecSMiri Korenblit 		    struct iwl_rx_cmd_buffer *rxb, unsigned int queue)
644d1e879ecSMiri Korenblit {
645d1e879ecSMiri Korenblit 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
646d1e879ecSMiri Korenblit 	struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
647d1e879ecSMiri Korenblit 	u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);
648d1e879ecSMiri Korenblit 
649d1e879ecSMiri Korenblit 	if (unlikely(queue >= mld->trans->num_rx_queues))
650d1e879ecSMiri Korenblit 		return;
651d1e879ecSMiri Korenblit 
652d1e879ecSMiri Korenblit 	if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD)))
653d1e879ecSMiri Korenblit 		iwl_mld_rx_mpdu(mld, napi, rxb, queue);
654d1e879ecSMiri Korenblit 	else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
655d1e879ecSMiri Korenblit 					    RX_QUEUES_NOTIFICATION)))
656d1e879ecSMiri Korenblit 		iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, queue);
657d1e879ecSMiri Korenblit 	else if (unlikely(cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)))
658d1e879ecSMiri Korenblit 		iwl_mld_handle_frame_release_notif(mld, napi, pkt, queue);
659d1e879ecSMiri Korenblit }
660d1e879ecSMiri Korenblit 
iwl_mld_delete_handlers(struct iwl_mld * mld,const u16 * cmds,int n_cmds)661d1e879ecSMiri Korenblit void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds)
662d1e879ecSMiri Korenblit {
663d1e879ecSMiri Korenblit 	struct iwl_async_handler_entry *entry, *tmp;
664d1e879ecSMiri Korenblit 
665d1e879ecSMiri Korenblit 	spin_lock_bh(&mld->async_handlers_lock);
666d1e879ecSMiri Korenblit 	list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {
667d1e879ecSMiri Korenblit 		bool match = false;
668d1e879ecSMiri Korenblit 
669d1e879ecSMiri Korenblit 		for (int i = 0; i < n_cmds; i++) {
670d1e879ecSMiri Korenblit 			if (entry->rx_h->cmd_id == cmds[i]) {
671d1e879ecSMiri Korenblit 				match = true;
672d1e879ecSMiri Korenblit 				break;
673d1e879ecSMiri Korenblit 			}
674d1e879ecSMiri Korenblit 		}
675d1e879ecSMiri Korenblit 
676d1e879ecSMiri Korenblit 		if (!match)
677d1e879ecSMiri Korenblit 			continue;
678d1e879ecSMiri Korenblit 
679d1e879ecSMiri Korenblit 		iwl_mld_log_async_handler_op(mld, "Delete", &entry->rxb);
680d1e879ecSMiri Korenblit 		iwl_free_rxb(&entry->rxb);
681d1e879ecSMiri Korenblit 		list_del(&entry->list);
682d1e879ecSMiri Korenblit 		kfree(entry);
683d1e879ecSMiri Korenblit 	}
684d1e879ecSMiri Korenblit 	spin_unlock_bh(&mld->async_handlers_lock);
685d1e879ecSMiri Korenblit }
686d1e879ecSMiri Korenblit 
iwl_mld_async_handlers_wk(struct wiphy * wiphy,struct wiphy_work * wk)687d1e879ecSMiri Korenblit void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk)
688d1e879ecSMiri Korenblit {
689d1e879ecSMiri Korenblit 	struct iwl_mld *mld =
690d1e879ecSMiri Korenblit 		container_of(wk, struct iwl_mld, async_handlers_wk);
691d1e879ecSMiri Korenblit 	struct iwl_async_handler_entry *entry, *tmp;
692d1e879ecSMiri Korenblit 	LIST_HEAD(local_list);
693d1e879ecSMiri Korenblit 
694d1e879ecSMiri Korenblit 	/* Sync with Rx path with a lock. Remove all the entries from this
695d1e879ecSMiri Korenblit 	 * list, add them to a local one (lock free), and then handle them.
696d1e879ecSMiri Korenblit 	 */
697d1e879ecSMiri Korenblit 	spin_lock_bh(&mld->async_handlers_lock);
698d1e879ecSMiri Korenblit 	list_splice_init(&mld->async_handlers_list, &local_list);
699d1e879ecSMiri Korenblit 	spin_unlock_bh(&mld->async_handlers_lock);
700d1e879ecSMiri Korenblit 
701d1e879ecSMiri Korenblit 	list_for_each_entry_safe(entry, tmp, &local_list, list) {
702d1e879ecSMiri Korenblit 		iwl_mld_log_async_handler_op(mld, "Handle", &entry->rxb);
703d1e879ecSMiri Korenblit 		entry->rx_h->fn(mld, rxb_addr(&entry->rxb));
704d1e879ecSMiri Korenblit 		iwl_free_rxb(&entry->rxb);
705d1e879ecSMiri Korenblit 		list_del(&entry->list);
706d1e879ecSMiri Korenblit 		kfree(entry);
707d1e879ecSMiri Korenblit 	}
708d1e879ecSMiri Korenblit }
709d1e879ecSMiri Korenblit 
iwl_mld_purge_async_handlers_list(struct iwl_mld * mld)710d1e879ecSMiri Korenblit void iwl_mld_purge_async_handlers_list(struct iwl_mld *mld)
711d1e879ecSMiri Korenblit {
712d1e879ecSMiri Korenblit 	struct iwl_async_handler_entry *entry, *tmp;
713d1e879ecSMiri Korenblit 
714d1e879ecSMiri Korenblit 	spin_lock_bh(&mld->async_handlers_lock);
715d1e879ecSMiri Korenblit 	list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {
716d1e879ecSMiri Korenblit 		iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb);
717d1e879ecSMiri Korenblit 		iwl_free_rxb(&entry->rxb);
718d1e879ecSMiri Korenblit 		list_del(&entry->list);
719d1e879ecSMiri Korenblit 		kfree(entry);
720d1e879ecSMiri Korenblit 	}
721d1e879ecSMiri Korenblit 	spin_unlock_bh(&mld->async_handlers_lock);
722d1e879ecSMiri Korenblit }
723d1e879ecSMiri Korenblit 
iwl_mld_cancel_notifications_of_object(struct iwl_mld * mld,enum iwl_mld_object_type obj_type,u32 obj_id)724d1e879ecSMiri Korenblit void iwl_mld_cancel_notifications_of_object(struct iwl_mld *mld,
725d1e879ecSMiri Korenblit 					    enum iwl_mld_object_type obj_type,
726d1e879ecSMiri Korenblit 					    u32 obj_id)
727d1e879ecSMiri Korenblit {
728d1e879ecSMiri Korenblit 	struct iwl_async_handler_entry *entry, *tmp;
729d1e879ecSMiri Korenblit 	LIST_HEAD(cancel_list);
730d1e879ecSMiri Korenblit 
731d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
732d1e879ecSMiri Korenblit 
733d1e879ecSMiri Korenblit 	if (WARN_ON(obj_type == IWL_MLD_OBJECT_TYPE_NONE))
734d1e879ecSMiri Korenblit 		return;
735d1e879ecSMiri Korenblit 
736d1e879ecSMiri Korenblit 	/* Sync with RX path and remove matching entries from the async list */
737d1e879ecSMiri Korenblit 	spin_lock_bh(&mld->async_handlers_lock);
738d1e879ecSMiri Korenblit 	list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {
739d1e879ecSMiri Korenblit 		const struct iwl_rx_handler *rx_h = entry->rx_h;
740d1e879ecSMiri Korenblit 
741d1e879ecSMiri Korenblit 		if (rx_h->obj_type != obj_type || WARN_ON(!rx_h->cancel))
742d1e879ecSMiri Korenblit 			continue;
743d1e879ecSMiri Korenblit 
744d1e879ecSMiri Korenblit 		if (rx_h->cancel(mld, rxb_addr(&entry->rxb), obj_id)) {
745d1e879ecSMiri Korenblit 			iwl_mld_log_async_handler_op(mld, "Cancel", &entry->rxb);
746d1e879ecSMiri Korenblit 			list_del(&entry->list);
747d1e879ecSMiri Korenblit 			list_add_tail(&entry->list, &cancel_list);
748d1e879ecSMiri Korenblit 		}
749d1e879ecSMiri Korenblit 	}
750d1e879ecSMiri Korenblit 
751d1e879ecSMiri Korenblit 	spin_unlock_bh(&mld->async_handlers_lock);
752d1e879ecSMiri Korenblit 
753d1e879ecSMiri Korenblit 	/* Free the matching entries outside of the spinlock */
754d1e879ecSMiri Korenblit 	list_for_each_entry_safe(entry, tmp, &cancel_list, list) {
755d1e879ecSMiri Korenblit 		iwl_free_rxb(&entry->rxb);
756d1e879ecSMiri Korenblit 		list_del(&entry->list);
757d1e879ecSMiri Korenblit 		kfree(entry);
758d1e879ecSMiri Korenblit 	}
759d1e879ecSMiri Korenblit }
760