File Index
Files and directories
Sign in
FILE

time-event.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c C
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
   4 * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
   5 * Copyright (C) 2017 Intel Deutschland GmbH
   6 */
   7#include <linux/jiffies.h>
   8#include <net/mac80211.h>
   9
  10#include "fw/notif-wait.h"
  11#include "iwl-trans.h"
  12#include "fw-api.h"
  13#include "time-event.h"
  14#include "mvm.h"
  15#include "iwl-io.h"
  16#include "iwl-prph.h"
  17
  18/*
  19 * For the high priority TE use a time event type that has similar priority to
  20 * the FW's action scan priority.
  21 */
  22#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE
  23#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC
  24
  25void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
  26			   struct iwl_mvm_time_event_data *te_data)
  27{
  28	lockdep_assert_held(&mvm->time_event_lock);
  29
  30	if (!te_data || !te_data->vif)
  31		return;
  32
  33	list_del(&te_data->list);
  34
  35	/*
  36	 * the list is only used for AUX ROC events so make sure it is always
  37	 * initialized
  38	 */
  39	INIT_LIST_HEAD(&te_data->list);
  40
  41	te_data->running = false;
  42	te_data->uid = 0;
  43	te_data->id = TE_MAX;
  44	te_data->vif = NULL;
  45	te_data->link_id = -1;
  46}
  47
  48static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm)
  49{
  50	struct ieee80211_vif *vif = mvm->p2p_device_vif;
  51
  52	lockdep_assert_held(&mvm->mutex);
  53
  54	/*
  55	 * Clear the ROC_P2P_RUNNING status bit.
  56	 * This will cause the TX path to drop offchannel transmissions.
  57	 * That would also be done by mac80211, but it is racy, in particular
  58	 * in the case that the time event actually completed in the firmware.
  59	 *
  60	 * Also flush the offchannel queue -- this is called when the time
  61	 * event finishes or is canceled, so that frames queued for it
  62	 * won't get stuck on the queue and be transmitted in the next
  63	 * time event.
  64	 */
  65	if (test_and_clear_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status)) {
  66		struct iwl_mvm_vif *mvmvif;
  67
  68		synchronize_net();
  69
  70		/*
  71		 * NB: access to this pointer would be racy, but the flush bit
  72		 * can only be set when we had a P2P-Device VIF, and we have a
  73		 * flush of this work in iwl_mvm_prepare_mac_removal() so it's
  74		 * not really racy.
  75		 */
  76
  77		if (!WARN_ON(!vif)) {
  78			mvmvif = iwl_mvm_vif_from_mac80211(vif);
  79			iwl_mvm_flush_sta(mvm, mvmvif->deflink.bcast_sta.sta_id,
  80					  mvmvif->deflink.bcast_sta.tfd_queue_msk);
  81
  82			if (mvm->mld_api_is_used) {
  83				iwl_mvm_mld_rm_bcast_sta(mvm, vif,
  84							 &vif->bss_conf);
  85
  86				iwl_mvm_link_changed(mvm, vif, &vif->bss_conf,
  87						     LINK_CONTEXT_MODIFY_ACTIVE,
  88						     false);
  89			} else {
  90				iwl_mvm_rm_p2p_bcast_sta(mvm, vif);
  91				iwl_mvm_binding_remove_vif(mvm, vif);
  92			}
  93
  94			/* Do not remove the PHY context as removing and adding
  95			 * a PHY context has timing overheads. Leaving it
  96			 * configured in FW would be useful in case the next ROC
  97			 * is with the same channel.
  98			 */
  99		}
 100	}
 101
 102	/*
 103	 * P2P AUX ROC and HS2.0 ROC do not run simultaneously.
 104	 * Clear the ROC_AUX_RUNNING status bit.
 105	 * This will cause the TX path to drop offchannel transmissions.
 106	 * That would also be done by mac80211, but it is racy, in particular
 107	 * in the case that the time event actually completed in the firmware
 108	 * (which is handled in iwl_mvm_te_handle_notif).
 109	 */
 110	if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) {
 111		synchronize_net();
 112
 113		iwl_mvm_flush_sta(mvm, mvm->aux_sta.sta_id,
 114				  mvm->aux_sta.tfd_queue_msk);
 115
 116		/* In newer version of this command an aux station is added only
 117		 * in cases of dedicated tx queue and need to be removed in end
 118		 * of use. For the even newer mld api, use the appropriate
 119		 * function.
 120		 */
 121		if (mvm->mld_api_is_used)
 122			iwl_mvm_mld_rm_aux_sta(mvm);
 123		else if (iwl_mvm_has_new_station_api(mvm->fw))
 124			iwl_mvm_rm_aux_sta(mvm);
 125	}
 126
 127	mutex_unlock(&mvm->mutex);
 128}
 129
 130void iwl_mvm_roc_done_wk(struct work_struct *wk)
 131{
 132	struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
 133
 134	mutex_lock(&mvm->mutex);
 135	/* Mutex is released inside */
 136	iwl_mvm_cleanup_roc(mvm);
 137}
 138
 139static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
 140{
 141	/*
 142	 * Of course, our status bit is just as racy as mac80211, so in
 143	 * addition, fire off the work struct which will drop all frames
 144	 * from the hardware queues that made it through the race. First
 145	 * it will of course synchronize the TX path to make sure that
 146	 * any *new* TX will be rejected.
 147	 */
 148	schedule_work(&mvm->roc_done_wk);
 149}
 150
 151static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
 152{
 153	struct ieee80211_vif *csa_vif;
 154
 155	rcu_read_lock();
 156
 157	csa_vif = rcu_dereference(mvm->csa_vif);
 158	if (!csa_vif || !csa_vif->bss_conf.csa_active)
 159		goto out_unlock;
 160
 161	IWL_DEBUG_TE(mvm, "CSA NOA started\n");
 162
 163	/*
 164	 * CSA NoA is started but we still have beacons to
 165	 * transmit on the current channel.
 166	 * So we just do nothing here and the switch
 167	 * will be performed on the last TBTT.
 168	 */
 169	if (!ieee80211_beacon_cntdwn_is_complete(csa_vif, 0)) {
 170		IWL_WARN(mvm, "CSA NOA started too early\n");
 171		goto out_unlock;
 172	}
 173
 174	ieee80211_csa_finish(csa_vif, 0);
 175
 176	rcu_read_unlock();
 177
 178	RCU_INIT_POINTER(mvm->csa_vif, NULL);
 179
 180	return;
 181
 182out_unlock:
 183	rcu_read_unlock();
 184}
 185
 186static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
 187					struct ieee80211_vif *vif,
 188					const char *errmsg)
 189{
 190	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 191
 192	if (vif->type != NL80211_IFTYPE_STATION)
 193		return false;
 194
 195	if (!mvmvif->csa_bcn_pending && vif->cfg.assoc &&
 196	    vif->bss_conf.dtim_period)
 197		return false;
 198	if (errmsg)
 199		IWL_ERR(mvm, "%s\n", errmsg);
 200
 201	if (mvmvif->csa_bcn_pending) {
 202		struct iwl_mvm_sta *mvmsta;
 203
 204		rcu_read_lock();
 205		mvmsta = iwl_mvm_sta_from_staid_rcu(mvm,
 206						    mvmvif->deflink.ap_sta_id);
 207		if (!WARN_ON(!mvmsta))
 208			iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
 209		rcu_read_unlock();
 210	}
 211
 212	if (vif->cfg.assoc) {
 213		/*
 214		 * When not associated, this will be called from
 215		 * iwl_mvm_event_mlme_callback_ini()
 216		 */
 217		iwl_dbg_tlv_time_point(&mvm->fwrt,
 218				       IWL_FW_INI_TIME_POINT_ASSOC_FAILED,
 219				       NULL);
 220
 221		mvmvif->session_prot_connection_loss = true;
 222	}
 223
 224	iwl_mvm_connection_loss(mvm, vif, errmsg);
 225	return true;
 226}
 227
 228static void
 229iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
 230			     struct iwl_mvm_time_event_data *te_data,
 231			     struct iwl_time_event_notif *notif)
 232{
 233	struct ieee80211_vif *vif = te_data->vif;
 234	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 235
 236	if (!notif->status)
 237		IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
 238
 239	switch (te_data->vif->type) {
 240	case NL80211_IFTYPE_AP:
 241		if (!notif->status)
 242			mvmvif->csa_failed = true;
 243		iwl_mvm_csa_noa_start(mvm);
 244		break;
 245	case NL80211_IFTYPE_STATION:
 246		if (!notif->status) {
 247			iwl_mvm_connection_loss(mvm, vif,
 248						"CSA TE failed to start");
 249			break;
 250		}
 251		iwl_mvm_csa_client_absent(mvm, te_data->vif);
 252		cancel_delayed_work(&mvmvif->csa_work);
 253		ieee80211_chswitch_done(te_data->vif, true, 0);
 254		break;
 255	default:
 256		/* should never happen */
 257		WARN_ON_ONCE(1);
 258		break;
 259	}
 260
 261	/* we don't need it anymore */
 262	iwl_mvm_te_clear_data(mvm, te_data);
 263}
 264
 265static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
 266				     struct iwl_time_event_notif *notif,
 267				     struct iwl_mvm_time_event_data *te_data)
 268{
 269	struct iwl_fw_dbg_trigger_tlv *trig;
 270	struct iwl_fw_dbg_trigger_time_event *te_trig;
 271	int i;
 272
 273	trig = iwl_fw_dbg_trigger_on(&mvm->fwrt,
 274				     ieee80211_vif_to_wdev(te_data->vif),
 275				     FW_DBG_TRIGGER_TIME_EVENT);
 276	if (!trig)
 277		return;
 278
 279	te_trig = (void *)trig->data;
 280
 281	for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
 282		u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
 283		u32 trig_action_bitmap =
 284			le32_to_cpu(te_trig->time_events[i].action_bitmap);
 285		u32 trig_status_bitmap =
 286			le32_to_cpu(te_trig->time_events[i].status_bitmap);
 287
 288		if (trig_te_id != te_data->id ||
 289		    !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
 290		    !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
 291			continue;
 292
 293		iwl_fw_dbg_collect_trig(&mvm->fwrt, trig,
 294					"Time event %d Action 0x%x received status: %d",
 295					te_data->id,
 296					le32_to_cpu(notif->action),
 297					le32_to_cpu(notif->status));
 298		break;
 299	}
 300}
 301
 302/*
 303 * Handles a FW notification for an event that is known to the driver.
 304 *
 305 * @mvm: the mvm component
 306 * @te_data: the time event data
 307 * @notif: the notification data corresponding the time event data.
 308 */
 309static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
 310				    struct iwl_mvm_time_event_data *te_data,
 311				    struct iwl_time_event_notif *notif)
 312{
 313	lockdep_assert_held(&mvm->time_event_lock);
 314
 315	IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
 316		     le32_to_cpu(notif->unique_id),
 317		     le32_to_cpu(notif->action));
 318
 319	iwl_mvm_te_check_trigger(mvm, notif, te_data);
 320
 321	/*
 322	 * The FW sends the start/end time event notifications even for events
 323	 * that it fails to schedule. This is indicated in the status field of
 324	 * the notification. This happens in cases that the scheduler cannot
 325	 * find a schedule that can handle the event (for example requesting a
 326	 * P2P Device discoveribility, while there are other higher priority
 327	 * events in the system).
 328	 */
 329	if (!le32_to_cpu(notif->status)) {
 330		const char *msg;
 331
 332		if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
 333			msg = "Time Event start notification failure";
 334		else
 335			msg = "Time Event end notification failure";
 336
 337		IWL_DEBUG_TE(mvm, "%s\n", msg);
 338
 339		if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
 340			iwl_mvm_te_clear_data(mvm, te_data);
 341			return;
 342		}
 343	}
 344
 345	if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
 346		IWL_DEBUG_TE(mvm,
 347			     "TE ended - current time %lu, estimated end %lu\n",
 348			     jiffies, te_data->end_jiffies);
 349
 350		switch (te_data->vif->type) {
 351		case NL80211_IFTYPE_P2P_DEVICE:
 352			ieee80211_remain_on_channel_expired(mvm->hw);
 353			iwl_mvm_roc_finished(mvm);
 354			break;
 355		case NL80211_IFTYPE_STATION:
 356			/*
 357			 * If we are switching channel, don't disconnect
 358			 * if the time event is already done. Beacons can
 359			 * be delayed a bit after the switch.
 360			 */
 361			if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
 362				IWL_DEBUG_TE(mvm,
 363					     "No beacon heard and the CS time event is over, don't disconnect\n");
 364				break;
 365			}
 366
 367			/*
 368			 * By now, we should have finished association
 369			 * and know the dtim period.
 370			 */
 371			iwl_mvm_te_check_disconnect(mvm, te_data->vif,
 372				!te_data->vif->cfg.assoc ?
 373				"Not associated and the time event is over already..." :
 374				"No beacon heard and the time event is over already...");
 375			break;
 376		default:
 377			break;
 378		}
 379
 380		iwl_mvm_te_clear_data(mvm, te_data);
 381	} else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
 382		te_data->running = true;
 383		te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
 384
 385		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
 386			set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status);
 387			ieee80211_ready_on_channel(mvm->hw);
 388		} else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
 389			iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
 390		}
 391	} else {
 392		IWL_WARN(mvm, "Got TE with unknown action\n");
 393	}
 394}
 395
 396struct iwl_mvm_rx_roc_iterator_data {
 397	u32 activity;
 398	bool end_activity;
 399	bool found;
 400};
 401
 402static void iwl_mvm_rx_roc_iterator(void *_data, u8 *mac,
 403				    struct ieee80211_vif *vif)
 404{
 405	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 406	struct iwl_mvm_rx_roc_iterator_data *data = _data;
 407
 408	if (mvmvif->roc_activity == data->activity) {
 409		data->found = true;
 410		if (data->end_activity)
 411			mvmvif->roc_activity = ROC_NUM_ACTIVITIES;
 412	}
 413}
 414
 415void iwl_mvm_rx_roc_notif(struct iwl_mvm *mvm,
 416			  struct iwl_rx_cmd_buffer *rxb)
 417{
 418	struct iwl_rx_packet *pkt = rxb_addr(rxb);
 419	struct iwl_roc_notif *notif = (void *)pkt->data;
 420	u32 activity = le32_to_cpu(notif->activity);
 421	bool started = le32_to_cpu(notif->success) &&
 422		le32_to_cpu(notif->started);
 423	struct iwl_mvm_rx_roc_iterator_data data = {
 424		.activity = activity,
 425		.end_activity = !started,
 426	};
 427
 428	/* Clear vif roc_activity if done (set to ROC_NUM_ACTIVITIES) */
 429	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
 430						   IEEE80211_IFACE_ITER_NORMAL,
 431						   iwl_mvm_rx_roc_iterator,
 432						   &data);
 433	/*
 434	 * It is possible that the ROC was canceled
 435	 * but the notification was already fired.
 436	 */
 437	if (!data.found)
 438		return;
 439
 440	if (started) {
 441		set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
 442		ieee80211_ready_on_channel(mvm->hw);
 443	} else {
 444		iwl_mvm_roc_finished(mvm);
 445		ieee80211_remain_on_channel_expired(mvm->hw);
 446	}
 447}
 448
 449/*
 450 * Handle A Aux ROC time event
 451 */
 452static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
 453					   struct iwl_time_event_notif *notif)
 454{
 455	struct iwl_mvm_time_event_data *aux_roc_te = NULL, *te_data;
 456
 457	list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
 458		if (le32_to_cpu(notif->unique_id) == te_data->uid) {
 459			aux_roc_te = te_data;
 460			break;
 461		}
 462	}
 463	if (!aux_roc_te) /* Not a Aux ROC time event */
 464		return -EINVAL;
 465
 466	iwl_mvm_te_check_trigger(mvm, notif, aux_roc_te);
 467
 468	IWL_DEBUG_TE(mvm,
 469		     "Aux ROC time event notification  - UID = 0x%x action %d (error = %d)\n",
 470		     le32_to_cpu(notif->unique_id),
 471		     le32_to_cpu(notif->action), le32_to_cpu(notif->status));
 472
 473	if (!le32_to_cpu(notif->status) ||
 474	    le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
 475		/* End TE, notify mac80211 */
 476		ieee80211_remain_on_channel_expired(mvm->hw);
 477		iwl_mvm_roc_finished(mvm); /* flush aux queue */
 478		list_del(&aux_roc_te->list); /* remove from list */
 479		aux_roc_te->running = false;
 480		aux_roc_te->vif = NULL;
 481		aux_roc_te->uid = 0;
 482		aux_roc_te->id = TE_MAX;
 483	} else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
 484		set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
 485		aux_roc_te->running = true;
 486		ieee80211_ready_on_channel(mvm->hw); /* Start TE */
 487	} else {
 488		IWL_DEBUG_TE(mvm,
 489			     "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
 490			     le32_to_cpu(notif->action));
 491		return -EINVAL;
 492	}
 493
 494	return 0;
 495}
 496
 497/*
 498 * The Rx handler for time event notifications
 499 */
 500void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
 501				 struct iwl_rx_cmd_buffer *rxb)
 502{
 503	struct iwl_rx_packet *pkt = rxb_addr(rxb);
 504	struct iwl_time_event_notif *notif = (void *)pkt->data;
 505	struct iwl_mvm_time_event_data *te_data, *tmp;
 506
 507	IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
 508		     le32_to_cpu(notif->unique_id),
 509		     le32_to_cpu(notif->action));
 510
 511	spin_lock_bh(&mvm->time_event_lock);
 512	/* This time event is triggered for Aux ROC request */
 513	if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
 514		goto unlock;
 515
 516	list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
 517		if (le32_to_cpu(notif->unique_id) == te_data->uid)
 518			iwl_mvm_te_handle_notif(mvm, te_data, notif);
 519	}
 520unlock:
 521	spin_unlock_bh(&mvm->time_event_lock);
 522}
 523
 524static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait,
 525			     struct iwl_rx_packet *pkt, void *data)
 526{
 527	struct iwl_mvm *mvm =
 528		container_of(notif_wait, struct iwl_mvm, notif_wait);
 529	struct iwl_mvm_time_event_data *te_data = data;
 530	struct iwl_time_event_notif *resp;
 531	int resp_len = iwl_rx_packet_payload_len(pkt);
 532
 533	if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION))
 534		return true;
 535
 536	if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
 537		IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n");
 538		return true;
 539	}
 540
 541	resp = (void *)pkt->data;
 542
 543	/* te_data->uid is already set in the TIME_EVENT_CMD response */
 544	if (le32_to_cpu(resp->unique_id) != te_data->uid)
 545		return false;
 546
 547	IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n",
 548		     te_data->uid);
 549	if (!resp->status)
 550		IWL_ERR(mvm,
 551			"TIME_EVENT_NOTIFICATION received but not executed\n");
 552
 553	return true;
 554}
 555
 556static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
 557					struct iwl_rx_packet *pkt, void *data)
 558{
 559	struct iwl_mvm *mvm =
 560		container_of(notif_wait, struct iwl_mvm, notif_wait);
 561	struct iwl_mvm_time_event_data *te_data = data;
 562	struct iwl_time_event_resp *resp;
 563	int resp_len = iwl_rx_packet_payload_len(pkt);
 564
 565	if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD))
 566		return true;
 567
 568	if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
 569		IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n");
 570		return true;
 571	}
 572
 573	resp = (void *)pkt->data;
 574
 575	/* we should never get a response to another TIME_EVENT_CMD here */
 576	if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id))
 577		return false;
 578
 579	te_data->uid = le32_to_cpu(resp->unique_id);
 580	IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
 581		     te_data->uid);
 582	return true;
 583}
 584
 585static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
 586				       struct ieee80211_vif *vif,
 587				       struct iwl_mvm_time_event_data *te_data,
 588				       struct iwl_time_event_cmd *te_cmd)
 589{
 590	static const u16 time_event_response[] = { TIME_EVENT_CMD };
 591	struct iwl_notification_wait wait_time_event;
 592	int ret;
 593
 594	lockdep_assert_held(&mvm->mutex);
 595
 596	IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n",
 597		     le32_to_cpu(te_cmd->duration));
 598
 599	spin_lock_bh(&mvm->time_event_lock);
 600	if (WARN_ON(te_data->id != TE_MAX)) {
 601		spin_unlock_bh(&mvm->time_event_lock);
 602		return -EIO;
 603	}
 604	te_data->vif = vif;
 605	te_data->duration = le32_to_cpu(te_cmd->duration);
 606	te_data->id = le32_to_cpu(te_cmd->id);
 607	list_add_tail(&te_data->list, &mvm->time_event_list);
 608	spin_unlock_bh(&mvm->time_event_lock);
 609
 610	/*
 611	 * Use a notification wait, which really just processes the
 612	 * command response and doesn't wait for anything, in order
 613	 * to be able to process the response and get the UID inside
 614	 * the RX path. Using CMD_WANT_SKB doesn't work because it
 615	 * stores the buffer and then wakes up this thread, by which
 616	 * time another notification (that the time event started)
 617	 * might already be processed unsuccessfully.
 618	 */
 619	iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
 620				   time_event_response,
 621				   ARRAY_SIZE(time_event_response),
 622				   iwl_mvm_time_event_response, te_data);
 623
 624	ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
 625					    sizeof(*te_cmd), te_cmd);
 626	if (ret) {
 627		IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
 628		iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
 629		goto out_clear_te;
 630	}
 631
 632	/* No need to wait for anything, so just pass 1 (0 isn't valid) */
 633	ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
 634	/* should never fail */
 635	WARN_ON_ONCE(ret);
 636
 637	if (ret) {
 638 out_clear_te:
 639		spin_lock_bh(&mvm->time_event_lock);
 640		iwl_mvm_te_clear_data(mvm, te_data);
 641		spin_unlock_bh(&mvm->time_event_lock);
 642	}
 643	return ret;
 644}
 645
 646void iwl_mvm_protect_session(struct iwl_mvm *mvm,
 647			     struct ieee80211_vif *vif,
 648			     u32 duration, u32 min_duration,
 649			     u32 max_delay, bool wait_for_notif)
 650{
 651	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 652	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
 653	const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION };
 654	struct iwl_notification_wait wait_te_notif;
 655	struct iwl_time_event_cmd time_cmd = {};
 656
 657	lockdep_assert_held(&mvm->mutex);
 658
 659	if (te_data->running &&
 660	    time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
 661		IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
 662			     jiffies_to_msecs(te_data->end_jiffies - jiffies));
 663		return;
 664	}
 665
 666	if (te_data->running) {
 667		IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
 668			     te_data->uid,
 669			     jiffies_to_msecs(te_data->end_jiffies - jiffies));
 670		/*
 671		 * we don't have enough time
 672		 * cancel the current TE and issue a new one
 673		 * Of course it would be better to remove the old one only
 674		 * when the new one is added, but we don't care if we are off
 675		 * channel for a bit. All we need to do, is not to return
 676		 * before we actually begin to be on the channel.
 677		 */
 678		iwl_mvm_stop_session_protection(mvm, vif);
 679	}
 680
 681	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
 682	time_cmd.id_and_color =
 683		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
 684	time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
 685
 686	time_cmd.apply_time = cpu_to_le32(0);
 687
 688	time_cmd.max_frags = TE_V2_FRAG_NONE;
 689	time_cmd.max_delay = cpu_to_le32(max_delay);
 690	/* TODO: why do we need to interval = bi if it is not periodic? */
 691	time_cmd.interval = cpu_to_le32(1);
 692	time_cmd.duration = cpu_to_le32(duration);
 693	time_cmd.repeat = 1;
 694	time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
 695				      TE_V2_NOTIF_HOST_EVENT_END |
 696				      TE_V2_START_IMMEDIATELY);
 697
 698	if (!wait_for_notif) {
 699		iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
 700		return;
 701	}
 702
 703	/*
 704	 * Create notification_wait for the TIME_EVENT_NOTIFICATION to use
 705	 * right after we send the time event
 706	 */
 707	iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif,
 708				   te_notif_response,
 709				   ARRAY_SIZE(te_notif_response),
 710				   iwl_mvm_te_notif, te_data);
 711
 712	/* If TE was sent OK - wait for the notification that started */
 713	if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) {
 714		IWL_ERR(mvm, "Failed to add TE to protect session\n");
 715		iwl_remove_notification(&mvm->notif_wait, &wait_te_notif);
 716	} else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif,
 717					 TU_TO_JIFFIES(max_delay))) {
 718		IWL_ERR(mvm, "Failed to protect session until TE\n");
 719	}
 720}
 721
 722/* Determine whether mac or link id should be used, and validate the link id */
 723static int iwl_mvm_get_session_prot_id(struct iwl_mvm *mvm,
 724				       struct ieee80211_vif *vif,
 725				       s8 link_id)
 726{
 727	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 728	int ver = iwl_fw_lookup_cmd_ver(mvm->fw,
 729					WIDE_ID(MAC_CONF_GROUP,
 730						SESSION_PROTECTION_CMD), 1);
 731
 732	if (ver < 2)
 733		return mvmvif->id;
 734
 735	if (WARN(link_id < 0 || !mvmvif->link[link_id],
 736		 "Invalid link ID for session protection: %u\n", link_id))
 737		return -EINVAL;
 738
 739	if (WARN(!mvmvif->link[link_id]->active,
 740		 "Session Protection on an inactive link: %u\n", link_id))
 741		return -EINVAL;
 742
 743	return mvmvif->link[link_id]->fw_link_id;
 744}
 745
 746static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm,
 747					      struct ieee80211_vif *vif,
 748					      u32 id, s8 link_id)
 749{
 750	int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, link_id);
 751	struct iwl_session_prot_cmd cmd = {
 752		.id_and_color = cpu_to_le32(mac_link_id),
 753		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
 754		.conf_id = cpu_to_le32(id),
 755	};
 756	int ret;
 757
 758	if (mac_link_id < 0)
 759		return;
 760
 761	ret = iwl_mvm_send_cmd_pdu(mvm,
 762				   WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD),
 763				   0, sizeof(cmd), &cmd);
 764	if (ret)
 765		IWL_ERR(mvm,
 766			"Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret);
 767}
 768
 769static void iwl_mvm_roc_rm_cmd(struct iwl_mvm *mvm, u32 activity)
 770{
 771	struct iwl_roc_req roc_cmd = {
 772		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
 773		.activity = cpu_to_le32(activity),
 774	};
 775	u8 ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
 776	u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(roc_cmd);
 777	int ret;
 778
 779	lockdep_assert_held(&mvm->mutex);
 780	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0,
 781				   cmd_len, &roc_cmd);
 782	if (ret)
 783		IWL_ERR(mvm, "Couldn't send the ROC_CMD: %d\n", ret);
 784}
 785
 786static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
 787					struct iwl_mvm_time_event_data *te_data,
 788					u32 *uid)
 789{
 790	u32 id;
 791	struct ieee80211_vif *vif = te_data->vif;
 792	struct iwl_mvm_vif *mvmvif;
 793	enum nl80211_iftype iftype;
 794	s8 link_id;
 795	bool p2p_aux = iwl_mvm_has_p2p_over_aux(mvm);
 796	u8 roc_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
 797					   WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
 798
 799	if (!vif)
 800		return false;
 801
 802	mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
 803	iftype = te_data->vif->type;
 804
 805	/*
 806	 * It is possible that by the time we got to this point the time
 807	 * event was already removed.
 808	 */
 809	spin_lock_bh(&mvm->time_event_lock);
 810
 811	/* Save time event uid before clearing its data */
 812	*uid = te_data->uid;
 813	id = te_data->id;
 814	link_id = te_data->link_id;
 815
 816	/*
 817	 * The clear_data function handles time events that were already removed
 818	 */
 819	iwl_mvm_te_clear_data(mvm, te_data);
 820	spin_unlock_bh(&mvm->time_event_lock);
 821
 822	if ((p2p_aux && iftype == NL80211_IFTYPE_P2P_DEVICE) ||
 823	    (roc_ver >= 3 && mvmvif->roc_activity == ROC_ACTIVITY_HOTSPOT)) {
 824		if (mvmvif->roc_activity < ROC_NUM_ACTIVITIES) {
 825			iwl_mvm_roc_rm_cmd(mvm, mvmvif->roc_activity);
 826			mvmvif->roc_activity = ROC_NUM_ACTIVITIES;
 827			iwl_mvm_roc_finished(mvm);
 828		}
 829		return false;
 830	} else if (fw_has_capa(&mvm->fw->ucode_capa,
 831			       IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD) &&
 832		   id != HOT_SPOT_CMD) {
 833		/* When session protection is used, the te_data->id field
 834		 * is reused to save session protection's configuration.
 835		 * For AUX ROC, HOT_SPOT_CMD is used and the te_data->id
 836		 * field is set to HOT_SPOT_CMD.
 837		 */
 838		if (mvmvif && id < SESSION_PROTECT_CONF_MAX_ID) {
 839			/* Session protection is still ongoing. Cancel it */
 840			iwl_mvm_cancel_session_protection(mvm, vif, id,
 841							  link_id);
 842			if (iftype == NL80211_IFTYPE_P2P_DEVICE) {
 843				iwl_mvm_roc_finished(mvm);
 844			}
 845		}
 846		return false;
 847	} else {
 848		/* It is possible that by the time we try to remove it, the
 849		 * time event has already ended and removed. In such a case
 850		 * there is no need to send a removal command.
 851		 */
 852		if (id == TE_MAX) {
 853			IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
 854			return false;
 855		}
 856	}
 857
 858	return true;
 859}
 860
 861/*
 862 * Explicit request to remove a aux roc time event. The removal of a time
 863 * event needs to be synchronized with the flow of a time event's end
 864 * notification, which also removes the time event from the op mode
 865 * data structures.
 866 */
 867static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
 868				      struct iwl_mvm_vif *mvmvif,
 869				      struct iwl_mvm_time_event_data *te_data)
 870{
 871	struct iwl_hs20_roc_req aux_cmd = {};
 872	u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm);
 873
 874	u32 uid;
 875	int ret;
 876
 877	if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
 878		return;
 879
 880	aux_cmd.event_unique_id = cpu_to_le32(uid);
 881	aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
 882	aux_cmd.id_and_color =
 883		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
 884	IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
 885		     le32_to_cpu(aux_cmd.event_unique_id));
 886	ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
 887				   len, &aux_cmd);
 888
 889	if (WARN_ON(ret))
 890		return;
 891}
 892
 893/*
 894 * Explicit request to remove a time event. The removal of a time event needs to
 895 * be synchronized with the flow of a time event's end notification, which also
 896 * removes the time event from the op mode data structures.
 897 */
 898void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
 899			       struct iwl_mvm_vif *mvmvif,
 900			       struct iwl_mvm_time_event_data *te_data)
 901{
 902	struct iwl_time_event_cmd time_cmd = {};
 903	u32 uid;
 904	int ret;
 905
 906	if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
 907		return;
 908
 909	/* When we remove a TE, the UID is to be set in the id field */
 910	time_cmd.id = cpu_to_le32(uid);
 911	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
 912	time_cmd.id_and_color =
 913		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
 914
 915	IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
 916	ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
 917				   sizeof(time_cmd), &time_cmd);
 918	if (ret)
 919		IWL_ERR(mvm, "Couldn't remove the time event\n");
 920}
 921
 922void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
 923				     struct ieee80211_vif *vif)
 924{
 925	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 926	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
 927	u32 id;
 928
 929	lockdep_assert_held(&mvm->mutex);
 930
 931	spin_lock_bh(&mvm->time_event_lock);
 932	id = te_data->id;
 933	spin_unlock_bh(&mvm->time_event_lock);
 934
 935	if (fw_has_capa(&mvm->fw->ucode_capa,
 936			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {
 937		if (id != SESSION_PROTECT_CONF_ASSOC) {
 938			IWL_DEBUG_TE(mvm,
 939				     "don't remove session protection id=%u\n",
 940				     id);
 941			return;
 942		}
 943	} else if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) {
 944		IWL_DEBUG_TE(mvm,
 945			     "don't remove TE with id=%u (not session protection)\n",
 946			     id);
 947		return;
 948	}
 949
 950	iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
 951}
 952
 953void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,
 954				      struct iwl_rx_cmd_buffer *rxb)
 955{
 956	struct iwl_rx_packet *pkt = rxb_addr(rxb);
 957	struct iwl_session_prot_notif *notif = (void *)pkt->data;
 958	int id = le32_to_cpu(notif->mac_link_id);
 959	struct ieee80211_vif *vif;
 960	struct iwl_mvm_vif *mvmvif;
 961
 962	rcu_read_lock();
 963
 964	/* note we use link ID == MAC ID */
 965	vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true);
 966	if (!vif)
 967		goto out_unlock;
 968
 969	mvmvif = iwl_mvm_vif_from_mac80211(vif);
 970
 971	/* The vif is not a P2P_DEVICE, maintain its time_event_data */
 972	if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
 973		struct iwl_mvm_time_event_data *te_data =
 974			&mvmvif->time_event_data;
 975
 976		if (!le32_to_cpu(notif->status)) {
 977			iwl_mvm_te_check_disconnect(mvm, vif,
 978						    "Session protection failure");
 979			spin_lock_bh(&mvm->time_event_lock);
 980			iwl_mvm_te_clear_data(mvm, te_data);
 981			spin_unlock_bh(&mvm->time_event_lock);
 982		}
 983
 984		if (le32_to_cpu(notif->start)) {
 985			spin_lock_bh(&mvm->time_event_lock);
 986			te_data->running = le32_to_cpu(notif->start);
 987			te_data->end_jiffies =
 988				TU_TO_EXP_TIME(te_data->duration);
 989			spin_unlock_bh(&mvm->time_event_lock);
 990		} else {
 991			/*
 992			 * By now, we should have finished association
 993			 * and know the dtim period.
 994			 */
 995			iwl_mvm_te_check_disconnect(mvm, vif,
 996						    !vif->cfg.assoc ?
 997						    "Not associated and the session protection is over already..." :
 998						    "No beacon heard and the session protection is over already...");
 999			spin_lock_bh(&mvm->time_event_lock);
1000			iwl_mvm_te_clear_data(mvm, te_data);
1001			spin_unlock_bh(&mvm->time_event_lock);
1002		}
1003
1004		goto out_unlock;
1005	}
1006
1007	if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) {
1008		/* End TE, notify mac80211 */
1009		mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID;
1010		mvmvif->time_event_data.link_id = -1;
1011		/* set the bit so the ROC cleanup will actually clean up */
1012		set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status);
1013		iwl_mvm_roc_finished(mvm);
1014		ieee80211_remain_on_channel_expired(mvm->hw);
1015	} else if (le32_to_cpu(notif->start)) {
1016		if (WARN_ON(mvmvif->time_event_data.id !=
1017				le32_to_cpu(notif->conf_id)))
1018			goto out_unlock;
1019		set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status);
1020		ieee80211_ready_on_channel(mvm->hw); /* Start TE */
1021	}
1022
1023 out_unlock:
1024	rcu_read_unlock();
1025}
1026
1027#define AUX_ROC_MIN_DURATION MSEC_TO_TU(100)
1028#define AUX_ROC_MIN_DELAY MSEC_TO_TU(200)
1029#define AUX_ROC_MAX_DELAY MSEC_TO_TU(600)
1030#define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20)
1031#define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10)
1032
1033void iwl_mvm_roc_duration_and_delay(struct ieee80211_vif *vif,
1034				    u32 duration_ms,
1035				    u32 *duration_tu,
1036				    u32 *delay)
1037{
1038	struct ieee80211_bss_conf *link_conf;
1039	unsigned int link_id;
1040	u32 dtim_interval = 0;
1041
1042	*delay = AUX_ROC_MIN_DELAY;
1043	*duration_tu = MSEC_TO_TU(duration_ms);
1044
1045	rcu_read_lock();
1046	for_each_vif_active_link(vif, link_conf, link_id) {
1047		dtim_interval =
1048			max_t(u32, dtim_interval,
1049			      link_conf->dtim_period * link_conf->beacon_int);
1050	}
1051	rcu_read_unlock();
1052
1053	/*
1054	 * If we are associated we want the delay time to be at least one
1055	 * dtim interval so that the FW can wait until after the DTIM and
1056	 * then start the time event, this will potentially allow us to
1057	 * remain off-channel for the max duration.
1058	 * Since we want to use almost a whole dtim interval we would also
1059	 * like the delay to be for 2-3 dtim intervals, in case there are
1060	 * other time events with higher priority.
1061	 * dtim_interval should never be 0, it can be 1 if we don't know it
1062	 * (we haven't heard any beacon yet).
1063	 */
1064	if (vif->cfg.assoc && !WARN_ON(!dtim_interval)) {
1065		*delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY);
1066		/* We cannot remain off-channel longer than the DTIM interval */
1067		if (dtim_interval <= *duration_tu) {
1068			*duration_tu = dtim_interval - AUX_ROC_SAFETY_BUFFER;
1069			if (*duration_tu <= AUX_ROC_MIN_DURATION)
1070				*duration_tu = dtim_interval -
1071					AUX_ROC_MIN_SAFETY_BUFFER;
1072		}
1073	}
1074}
1075
1076int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm,
1077			struct ieee80211_channel *channel,
1078			struct ieee80211_vif *vif,
1079			int duration, enum iwl_roc_activity activity)
1080{
1081	int res;
1082	u32 duration_tu, delay;
1083	struct iwl_roc_req roc_req = {
1084		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
1085		.activity = cpu_to_le32(activity),
1086		.sta_id = cpu_to_le32(mvm->aux_sta.sta_id),
1087	};
1088	u8 ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
1089	u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(roc_req);
1090	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1091
1092	lockdep_assert_held(&mvm->mutex);
1093
1094	if (WARN_ON(mvmvif->roc_activity != ROC_NUM_ACTIVITIES))
1095		return -EBUSY;
1096
1097	/* Set the channel info data */
1098	iwl_mvm_set_chan_info(mvm, &roc_req.channel_info,
1099			      channel->hw_value,
1100			      iwl_mvm_phy_band_from_nl80211(channel->band),
1101			      IWL_PHY_CHANNEL_MODE20, 0);
1102
1103	iwl_mvm_roc_duration_and_delay(vif, duration, &duration_tu,
1104				       &delay);
1105	roc_req.duration = cpu_to_le32(duration_tu);
1106	roc_req.max_delay = cpu_to_le32(delay);
1107
1108	IWL_DEBUG_TE(mvm,
1109		     "\t(requested = %ums, max_delay = %ums)\n",
1110		     duration, delay);
1111	IWL_DEBUG_TE(mvm,
1112		     "Requesting to remain on channel %u for %utu. activity %u\n",
1113		     channel->hw_value, duration_tu, activity);
1114
1115	/* Set the node address */
1116	memcpy(roc_req.node_addr, vif->addr, ETH_ALEN);
1117
1118	res = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
1119				   0, cmd_len, &roc_req);
1120	if (!res)
1121		mvmvif->roc_activity = activity;
1122
1123	return res;
1124}
1125
1126static int
1127iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm,
1128					 struct ieee80211_vif *vif,
1129					 int duration,
1130					 enum ieee80211_roc_type type)
1131{
1132	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1133	struct iwl_session_prot_cmd cmd = {
1134		.id_and_color =
1135			cpu_to_le32(iwl_mvm_get_session_prot_id(mvm, vif, 0)),
1136		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
1137		.duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
1138	};
1139
1140	lockdep_assert_held(&mvm->mutex);
1141
1142	/* The time_event_data.id field is reused to save session
1143	 * protection's configuration.
1144	 */
1145
1146	mvmvif->time_event_data.link_id = 0;
1147
1148	switch (type) {
1149	case IEEE80211_ROC_TYPE_NORMAL:
1150		mvmvif->time_event_data.id =
1151			SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV;
1152		break;
1153	case IEEE80211_ROC_TYPE_MGMT_TX:
1154		mvmvif->time_event_data.id =
1155			SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION;
1156		break;
1157	default:
1158		WARN_ONCE(1, "Got an invalid ROC type\n");
1159		return -EINVAL;
1160	}
1161
1162	cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id);
1163	return iwl_mvm_send_cmd_pdu(mvm,
1164				    WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD),
1165				    0, sizeof(cmd), &cmd);
1166}
1167
1168int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1169			  int duration, enum ieee80211_roc_type type)
1170{
1171	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1172	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1173	struct iwl_time_event_cmd time_cmd = {};
1174
1175	lockdep_assert_held(&mvm->mutex);
1176	if (te_data->running) {
1177		IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
1178		return -EBUSY;
1179	}
1180
1181	if (fw_has_capa(&mvm->fw->ucode_capa,
1182			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD))
1183		return iwl_mvm_start_p2p_roc_session_protection(mvm, vif,
1184								duration,
1185								type);
1186
1187	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
1188	time_cmd.id_and_color =
1189		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
1190
1191	switch (type) {
1192	case IEEE80211_ROC_TYPE_NORMAL:
1193		time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
1194		break;
1195	case IEEE80211_ROC_TYPE_MGMT_TX:
1196		time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
1197		break;
1198	default:
1199		WARN_ONCE(1, "Got an invalid ROC type\n");
1200		return -EINVAL;
1201	}
1202
1203	time_cmd.apply_time = cpu_to_le32(0);
1204	time_cmd.interval = cpu_to_le32(1);
1205
1206	/*
1207	 * The P2P Device TEs can have lower priority than other events
1208	 * that are being scheduled by the driver/fw, and thus it might not be
1209	 * scheduled. To improve the chances of it being scheduled, allow them
1210	 * to be fragmented, and in addition allow them to be delayed.
1211	 */
1212	time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
1213	time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
1214	time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
1215	time_cmd.repeat = 1;
1216	time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
1217				      TE_V2_NOTIF_HOST_EVENT_END |
1218				      TE_V2_START_IMMEDIATELY);
1219
1220	return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
1221}
1222
1223static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm)
1224{
1225	struct iwl_mvm_time_event_data *te_data;
1226
1227	lockdep_assert_held(&mvm->mutex);
1228
1229	spin_lock_bh(&mvm->time_event_lock);
1230
1231	/*
1232	 * Iterate over the list of time events and find the time event that is
1233	 * associated with a P2P_DEVICE interface.
1234	 * This assumes that a P2P_DEVICE interface can have only a single time
1235	 * event at any given time and this time event coresponds to a ROC
1236	 * request
1237	 */
1238	list_for_each_entry(te_data, &mvm->time_event_list, list) {
1239		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
1240			goto out;
1241	}
1242
1243	/* There can only be at most one AUX ROC time event, we just use the
1244	 * list to simplify/unify code. Remove it if it exists.
1245	 */
1246	te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,
1247					   struct iwl_mvm_time_event_data,
1248					   list);
1249out:
1250	spin_unlock_bh(&mvm->time_event_lock);
1251	return te_data;
1252}
1253
1254void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)
1255{
1256	struct iwl_mvm_time_event_data *te_data;
1257	u32 uid;
1258
1259	te_data = iwl_mvm_get_roc_te(mvm);
1260	if (te_data)
1261		__iwl_mvm_remove_time_event(mvm, te_data, &uid);
1262}
1263
1264void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
1265{
1266	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1267	struct iwl_mvm_time_event_data *te_data;
1268	bool p2p_aux = iwl_mvm_has_p2p_over_aux(mvm);
1269	u8 roc_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
1270					   WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
1271	int iftype = vif->type;
1272
1273	mutex_lock(&mvm->mutex);
1274
1275	if (p2p_aux || (roc_ver >= 3 && iftype != NL80211_IFTYPE_P2P_DEVICE)) {
1276		if (mvmvif->roc_activity < ROC_NUM_ACTIVITIES) {
1277			iwl_mvm_roc_rm_cmd(mvm, mvmvif->roc_activity);
1278			mvmvif->roc_activity = ROC_NUM_ACTIVITIES;
1279		}
1280		goto cleanup_roc;
1281	} else if (fw_has_capa(&mvm->fw->ucode_capa,
1282			       IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {
1283		te_data = &mvmvif->time_event_data;
1284
1285		if (iftype == NL80211_IFTYPE_P2P_DEVICE) {
1286			if (te_data->id >= SESSION_PROTECT_CONF_MAX_ID) {
1287				IWL_DEBUG_TE(mvm,
1288					     "No remain on channel event\n");
1289				mutex_unlock(&mvm->mutex);
1290				return;
1291			}
1292			iwl_mvm_cancel_session_protection(mvm, vif,
1293							  te_data->id,
1294							  te_data->link_id);
1295		} else {
1296			iwl_mvm_remove_aux_roc_te(mvm, mvmvif,
1297						  &mvmvif->hs_time_event_data);
1298		}
1299		goto cleanup_roc;
1300	}
1301
1302	te_data = iwl_mvm_get_roc_te(mvm);
1303	if (!te_data) {
1304		IWL_WARN(mvm, "No remain on channel event\n");
1305		mutex_unlock(&mvm->mutex);
1306		return;
1307	}
1308
1309	mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
1310	iftype = te_data->vif->type;
1311	if (iftype == NL80211_IFTYPE_P2P_DEVICE)
1312		iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
1313	else
1314		iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
1315
1316cleanup_roc:
1317	/*
1318	 * In case we get here before the ROC event started,
1319	 * (so the status bit isn't set) set it here so iwl_mvm_cleanup_roc will
1320	 * cleanup things properly
1321	 */
1322	if (p2p_aux || iftype != NL80211_IFTYPE_P2P_DEVICE)
1323		set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
1324	else
1325		set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status);
1326
1327	/* Mutex is released inside this function */
1328	iwl_mvm_cleanup_roc(mvm);
1329}
1330
1331void iwl_mvm_remove_csa_period(struct iwl_mvm *mvm,
1332			       struct ieee80211_vif *vif)
1333{
1334	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1335	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1336	u32 id;
1337
1338	lockdep_assert_held(&mvm->mutex);
1339
1340	spin_lock_bh(&mvm->time_event_lock);
1341	id = te_data->id;
1342	spin_unlock_bh(&mvm->time_event_lock);
1343
1344	if (id != TE_CHANNEL_SWITCH_PERIOD)
1345		return;
1346
1347	iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
1348}
1349
1350int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
1351				struct ieee80211_vif *vif,
1352				u32 duration, u32 apply_time)
1353{
1354	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1355	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1356	struct iwl_time_event_cmd time_cmd = {};
1357
1358	lockdep_assert_held(&mvm->mutex);
1359
1360	if (te_data->running) {
1361		u32 id;
1362
1363		spin_lock_bh(&mvm->time_event_lock);
1364		id = te_data->id;
1365		spin_unlock_bh(&mvm->time_event_lock);
1366
1367		if (id == TE_CHANNEL_SWITCH_PERIOD) {
1368			IWL_DEBUG_TE(mvm, "CS period is already scheduled\n");
1369			return -EBUSY;
1370		}
1371
1372		/*
1373		 * Remove the session protection time event to allow the
1374		 * channel switch. If we got here, we just heard a beacon so
1375		 * the session protection is not needed anymore anyway.
1376		 */
1377		iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
1378	}
1379
1380	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
1381	time_cmd.id_and_color =
1382		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
1383	time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD);
1384	time_cmd.apply_time = cpu_to_le32(apply_time);
1385	time_cmd.max_frags = TE_V2_FRAG_NONE;
1386	time_cmd.duration = cpu_to_le32(duration);
1387	time_cmd.repeat = 1;
1388	time_cmd.interval = cpu_to_le32(1);
1389	time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
1390				      TE_V2_ABSENCE);
1391	if (!apply_time)
1392		time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY);
1393
1394	return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
1395}
1396
1397static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait,
1398				       struct iwl_rx_packet *pkt, void *data)
1399{
1400	struct iwl_mvm *mvm =
1401		container_of(notif_wait, struct iwl_mvm, notif_wait);
1402	struct iwl_session_prot_notif *resp;
1403	int resp_len = iwl_rx_packet_payload_len(pkt);
1404
1405	if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF ||
1406		    pkt->hdr.group_id != MAC_CONF_GROUP))
1407		return true;
1408
1409	if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
1410		IWL_ERR(mvm, "Invalid SESSION_PROTECTION_NOTIF response\n");
1411		return true;
1412	}
1413
1414	resp = (void *)pkt->data;
1415
1416	if (!resp->status)
1417		IWL_ERR(mvm,
1418			"TIME_EVENT_NOTIFICATION received but not executed\n");
1419
1420	return true;
1421}
1422
1423void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm,
1424					 struct ieee80211_vif *vif,
1425					 u32 duration, u32 min_duration,
1426					 bool wait_for_notif,
1427					 unsigned int link_id)
1428{
1429	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1430	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1431	const u16 notif[] = { WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF) };
1432	struct iwl_notification_wait wait_notif;
1433	int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, (s8)link_id);
1434	struct iwl_session_prot_cmd cmd = {
1435		.id_and_color = cpu_to_le32(mac_link_id),
1436		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
1437		.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
1438		.duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
1439	};
1440
1441	if (mac_link_id < 0)
1442		return;
1443
1444	lockdep_assert_held(&mvm->mutex);
1445
1446	spin_lock_bh(&mvm->time_event_lock);
1447	if (te_data->running && te_data->link_id == link_id &&
1448	    time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
1449		IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
1450			     jiffies_to_msecs(te_data->end_jiffies - jiffies));
1451		spin_unlock_bh(&mvm->time_event_lock);
1452
1453		return;
1454	}
1455
1456	iwl_mvm_te_clear_data(mvm, te_data);
1457	/*
1458	 * The time_event_data.id field is reused to save session
1459	 * protection's configuration.
1460	 */
1461	te_data->id = le32_to_cpu(cmd.conf_id);
1462	te_data->duration = le32_to_cpu(cmd.duration_tu);
1463	te_data->vif = vif;
1464	te_data->link_id = link_id;
1465	spin_unlock_bh(&mvm->time_event_lock);
1466
1467	IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n",
1468		     le32_to_cpu(cmd.duration_tu));
1469
1470	if (!wait_for_notif) {
1471		if (iwl_mvm_send_cmd_pdu(mvm,
1472					 WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD),
1473					 0, sizeof(cmd), &cmd)) {
1474			goto send_cmd_err;
1475		}
1476
1477		return;
1478	}
1479
1480	iwl_init_notification_wait(&mvm->notif_wait, &wait_notif,
1481				   notif, ARRAY_SIZE(notif),
1482				   iwl_mvm_session_prot_notif, NULL);
1483
1484	if (iwl_mvm_send_cmd_pdu(mvm,
1485				 WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD),
1486				 0, sizeof(cmd), &cmd)) {
1487		iwl_remove_notification(&mvm->notif_wait, &wait_notif);
1488		goto send_cmd_err;
1489	} else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif,
1490					 TU_TO_JIFFIES(100))) {
1491		IWL_ERR(mvm,
1492			"Failed to protect session until session protection\n");
1493	}
1494	return;
1495
1496send_cmd_err:
1497	IWL_ERR(mvm,
1498		"Couldn't send the SESSION_PROTECTION_CMD\n");
1499	spin_lock_bh(&mvm->time_event_lock);
1500	iwl_mvm_te_clear_data(mvm, te_data);
1501	spin_unlock_bh(&mvm->time_event_lock);
1502}
~/ Read-only file preview.