File Index
Files and directories
Sign in
FILE

notif.c

File preview with syntax highlighting.

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