brintos

brintos / linux-shallow public Read only

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