1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2/*
3 * Copyright (C) 2012-2014, 2018-2023 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}
46
47void iwl_mvm_roc_done_wk(struct work_struct *wk)
48{
49	struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
50
51	/*
52	 * Clear the ROC_RUNNING status bit.
53	 * This will cause the TX path to drop offchannel transmissions.
54	 * That would also be done by mac80211, but it is racy, in particular
55	 * in the case that the time event actually completed in the firmware
56	 * (which is handled in iwl_mvm_te_handle_notif).
57	 */
58	clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
59
60	synchronize_net();
61
62	/*
63	 * Flush the offchannel queue -- this is called when the time
64	 * event finishes or is canceled, so that frames queued for it
65	 * won't get stuck on the queue and be transmitted in the next
66	 * time event.
67	 */
68
69	mutex_lock(&mvm->mutex);
70	if (test_and_clear_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) {
71		struct iwl_mvm_vif *mvmvif;
72
73		/*
74		 * NB: access to this pointer would be racy, but the flush bit
75		 * can only be set when we had a P2P-Device VIF, and we have a
76		 * flush of this work in iwl_mvm_prepare_mac_removal() so it's
77		 * not really racy.
78		 */
79
80		if (!WARN_ON(!mvm->p2p_device_vif)) {
81			mvmvif = iwl_mvm_vif_from_mac80211(mvm->p2p_device_vif);
82			iwl_mvm_flush_sta(mvm, &mvmvif->deflink.bcast_sta,
83					  true);
84		}
85	}
86
87	/*
88	 * Clear the ROC_AUX_RUNNING status bit.
89	 * This will cause the TX path to drop offchannel transmissions.
90	 * That would also be done by mac80211, but it is racy, in particular
91	 * in the case that the time event actually completed in the firmware
92	 * (which is handled in iwl_mvm_te_handle_notif).
93	 */
94	if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) {
95		/* do the same in case of hot spot 2.0 */
96		iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true);
97
98		if (mvm->mld_api_is_used) {
99			iwl_mvm_mld_rm_aux_sta(mvm);
100			goto out_unlock;
101		}
102
103		/* In newer version of this command an aux station is added only
104		 * in cases of dedicated tx queue and need to be removed in end
105		 * of use */
106		if (iwl_mvm_has_new_station_api(mvm->fw))
107			iwl_mvm_rm_aux_sta(mvm);
108	}
109
110out_unlock:
111	mutex_unlock(&mvm->mutex);
112}
113
114static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
115{
116	/*
117	 * Of course, our status bit is just as racy as mac80211, so in
118	 * addition, fire off the work struct which will drop all frames
119	 * from the hardware queues that made it through the race. First
120	 * it will of course synchronize the TX path to make sure that
121	 * any *new* TX will be rejected.
122	 */
123	schedule_work(&mvm->roc_done_wk);
124}
125
126static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
127{
128	struct ieee80211_vif *csa_vif;
129
130	rcu_read_lock();
131
132	csa_vif = rcu_dereference(mvm->csa_vif);
133	if (!csa_vif || !csa_vif->bss_conf.csa_active)
134		goto out_unlock;
135
136	IWL_DEBUG_TE(mvm, "CSA NOA started\n");
137
138	/*
139	 * CSA NoA is started but we still have beacons to
140	 * transmit on the current channel.
141	 * So we just do nothing here and the switch
142	 * will be performed on the last TBTT.
143	 */
144	if (!ieee80211_beacon_cntdwn_is_complete(csa_vif)) {
145		IWL_WARN(mvm, "CSA NOA started too early\n");
146		goto out_unlock;
147	}
148
149	ieee80211_csa_finish(csa_vif);
150
151	rcu_read_unlock();
152
153	RCU_INIT_POINTER(mvm->csa_vif, NULL);
154
155	return;
156
157out_unlock:
158	rcu_read_unlock();
159}
160
161static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
162					struct ieee80211_vif *vif,
163					const char *errmsg)
164{
165	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
166
167	if (vif->type != NL80211_IFTYPE_STATION)
168		return false;
169
170	if (!mvmvif->csa_bcn_pending && vif->cfg.assoc &&
171	    vif->bss_conf.dtim_period)
172		return false;
173	if (errmsg)
174		IWL_ERR(mvm, "%s\n", errmsg);
175
176	if (mvmvif->csa_bcn_pending) {
177		struct iwl_mvm_sta *mvmsta;
178
179		rcu_read_lock();
180		mvmsta = iwl_mvm_sta_from_staid_rcu(mvm,
181						    mvmvif->deflink.ap_sta_id);
182		if (!WARN_ON(!mvmsta))
183			iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
184		rcu_read_unlock();
185	}
186
187	if (vif->cfg.assoc) {
188		/*
189		 * When not associated, this will be called from
190		 * iwl_mvm_event_mlme_callback_ini()
191		 */
192		iwl_dbg_tlv_time_point(&mvm->fwrt,
193				       IWL_FW_INI_TIME_POINT_ASSOC_FAILED,
194				       NULL);
195	}
196
197	iwl_mvm_connection_loss(mvm, vif, errmsg);
198	return true;
199}
200
201static void
202iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
203			     struct iwl_mvm_time_event_data *te_data,
204			     struct iwl_time_event_notif *notif)
205{
206	struct ieee80211_vif *vif = te_data->vif;
207	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
208
209	if (!notif->status)
210		IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
211
212	switch (te_data->vif->type) {
213	case NL80211_IFTYPE_AP:
214		if (!notif->status)
215			mvmvif->csa_failed = true;
216		iwl_mvm_csa_noa_start(mvm);
217		break;
218	case NL80211_IFTYPE_STATION:
219		if (!notif->status) {
220			iwl_mvm_connection_loss(mvm, vif,
221						"CSA TE failed to start");
222			break;
223		}
224		iwl_mvm_csa_client_absent(mvm, te_data->vif);
225		cancel_delayed_work(&mvmvif->csa_work);
226		ieee80211_chswitch_done(te_data->vif, true);
227		break;
228	default:
229		/* should never happen */
230		WARN_ON_ONCE(1);
231		break;
232	}
233
234	/* we don't need it anymore */
235	iwl_mvm_te_clear_data(mvm, te_data);
236}
237
238static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
239				     struct iwl_time_event_notif *notif,
240				     struct iwl_mvm_time_event_data *te_data)
241{
242	struct iwl_fw_dbg_trigger_tlv *trig;
243	struct iwl_fw_dbg_trigger_time_event *te_trig;
244	int i;
245
246	trig = iwl_fw_dbg_trigger_on(&mvm->fwrt,
247				     ieee80211_vif_to_wdev(te_data->vif),
248				     FW_DBG_TRIGGER_TIME_EVENT);
249	if (!trig)
250		return;
251
252	te_trig = (void *)trig->data;
253
254	for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
255		u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
256		u32 trig_action_bitmap =
257			le32_to_cpu(te_trig->time_events[i].action_bitmap);
258		u32 trig_status_bitmap =
259			le32_to_cpu(te_trig->time_events[i].status_bitmap);
260
261		if (trig_te_id != te_data->id ||
262		    !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
263		    !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
264			continue;
265
266		iwl_fw_dbg_collect_trig(&mvm->fwrt, trig,
267					"Time event %d Action 0x%x received status: %d",
268					te_data->id,
269					le32_to_cpu(notif->action),
270					le32_to_cpu(notif->status));
271		break;
272	}
273}
274
275static void iwl_mvm_p2p_roc_finished(struct iwl_mvm *mvm)
276{
277	/*
278	 * If the IWL_MVM_STATUS_NEED_FLUSH_P2P is already set, then the
279	 * roc_done_wk is already scheduled or running, so don't schedule it
280	 * again to avoid a race where the roc_done_wk clears this bit after
281	 * it is set here, affecting the next run of the roc_done_wk.
282	 */
283	if (!test_and_set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status))
284		iwl_mvm_roc_finished(mvm);
285}
286
287/*
288 * Handles a FW notification for an event that is known to the driver.
289 *
290 * @mvm: the mvm component
291 * @te_data: the time event data
292 * @notif: the notification data corresponding the time event data.
293 */
294static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
295				    struct iwl_mvm_time_event_data *te_data,
296				    struct iwl_time_event_notif *notif)
297{
298	lockdep_assert_held(&mvm->time_event_lock);
299
300	IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
301		     le32_to_cpu(notif->unique_id),
302		     le32_to_cpu(notif->action));
303
304	iwl_mvm_te_check_trigger(mvm, notif, te_data);
305
306	/*
307	 * The FW sends the start/end time event notifications even for events
308	 * that it fails to schedule. This is indicated in the status field of
309	 * the notification. This happens in cases that the scheduler cannot
310	 * find a schedule that can handle the event (for example requesting a
311	 * P2P Device discoveribility, while there are other higher priority
312	 * events in the system).
313	 */
314	if (!le32_to_cpu(notif->status)) {
315		const char *msg;
316
317		if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
318			msg = "Time Event start notification failure";
319		else
320			msg = "Time Event end notification failure";
321
322		IWL_DEBUG_TE(mvm, "%s\n", msg);
323
324		if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
325			iwl_mvm_te_clear_data(mvm, te_data);
326			return;
327		}
328	}
329
330	if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
331		IWL_DEBUG_TE(mvm,
332			     "TE ended - current time %lu, estimated end %lu\n",
333			     jiffies, te_data->end_jiffies);
334
335		switch (te_data->vif->type) {
336		case NL80211_IFTYPE_P2P_DEVICE:
337			ieee80211_remain_on_channel_expired(mvm->hw);
338			iwl_mvm_p2p_roc_finished(mvm);
339			break;
340		case NL80211_IFTYPE_STATION:
341			/*
342			 * If we are switching channel, don't disconnect
343			 * if the time event is already done. Beacons can
344			 * be delayed a bit after the switch.
345			 */
346			if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
347				IWL_DEBUG_TE(mvm,
348					     "No beacon heard and the CS time event is over, don't disconnect\n");
349				break;
350			}
351
352			/*
353			 * By now, we should have finished association
354			 * and know the dtim period.
355			 */
356			iwl_mvm_te_check_disconnect(mvm, te_data->vif,
357				!te_data->vif->cfg.assoc ?
358				"Not associated and the time event is over already..." :
359				"No beacon heard and the time event is over already...");
360			break;
361		default:
362			break;
363		}
364
365		iwl_mvm_te_clear_data(mvm, te_data);
366	} else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
367		te_data->running = true;
368		te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
369
370		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
371			set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
372			ieee80211_ready_on_channel(mvm->hw);
373		} else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
374			iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
375		}
376	} else {
377		IWL_WARN(mvm, "Got TE with unknown action\n");
378	}
379}
380
381/*
382 * Handle A Aux ROC time event
383 */
384static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
385					   struct iwl_time_event_notif *notif)
386{
387	struct iwl_mvm_time_event_data *aux_roc_te = NULL, *te_data;
388
389	list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
390		if (le32_to_cpu(notif->unique_id) == te_data->uid) {
391			aux_roc_te = te_data;
392			break;
393		}
394	}
395	if (!aux_roc_te) /* Not a Aux ROC time event */
396		return -EINVAL;
397
398	iwl_mvm_te_check_trigger(mvm, notif, te_data);
399
400	IWL_DEBUG_TE(mvm,
401		     "Aux ROC time event notification  - UID = 0x%x action %d (error = %d)\n",
402		     le32_to_cpu(notif->unique_id),
403		     le32_to_cpu(notif->action), le32_to_cpu(notif->status));
404
405	if (!le32_to_cpu(notif->status) ||
406	    le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
407		/* End TE, notify mac80211 */
408		ieee80211_remain_on_channel_expired(mvm->hw);
409		iwl_mvm_roc_finished(mvm); /* flush aux queue */
410		list_del(&te_data->list); /* remove from list */
411		te_data->running = false;
412		te_data->vif = NULL;
413		te_data->uid = 0;
414		te_data->id = TE_MAX;
415	} else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
416		set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
417		te_data->running = true;
418		ieee80211_ready_on_channel(mvm->hw); /* Start TE */
419	} else {
420		IWL_DEBUG_TE(mvm,
421			     "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
422			     le32_to_cpu(notif->action));
423		return -EINVAL;
424	}
425
426	return 0;
427}
428
429/*
430 * The Rx handler for time event notifications
431 */
432void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
433				 struct iwl_rx_cmd_buffer *rxb)
434{
435	struct iwl_rx_packet *pkt = rxb_addr(rxb);
436	struct iwl_time_event_notif *notif = (void *)pkt->data;
437	struct iwl_mvm_time_event_data *te_data, *tmp;
438
439	IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
440		     le32_to_cpu(notif->unique_id),
441		     le32_to_cpu(notif->action));
442
443	spin_lock_bh(&mvm->time_event_lock);
444	/* This time event is triggered for Aux ROC request */
445	if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
446		goto unlock;
447
448	list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
449		if (le32_to_cpu(notif->unique_id) == te_data->uid)
450			iwl_mvm_te_handle_notif(mvm, te_data, notif);
451	}
452unlock:
453	spin_unlock_bh(&mvm->time_event_lock);
454}
455
456static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait,
457			     struct iwl_rx_packet *pkt, void *data)
458{
459	struct iwl_mvm *mvm =
460		container_of(notif_wait, struct iwl_mvm, notif_wait);
461	struct iwl_mvm_time_event_data *te_data = data;
462	struct iwl_time_event_notif *resp;
463	int resp_len = iwl_rx_packet_payload_len(pkt);
464
465	if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION))
466		return true;
467
468	if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
469		IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n");
470		return true;
471	}
472
473	resp = (void *)pkt->data;
474
475	/* te_data->uid is already set in the TIME_EVENT_CMD response */
476	if (le32_to_cpu(resp->unique_id) != te_data->uid)
477		return false;
478
479	IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n",
480		     te_data->uid);
481	if (!resp->status)
482		IWL_ERR(mvm,
483			"TIME_EVENT_NOTIFICATION received but not executed\n");
484
485	return true;
486}
487
488static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
489					struct iwl_rx_packet *pkt, void *data)
490{
491	struct iwl_mvm *mvm =
492		container_of(notif_wait, struct iwl_mvm, notif_wait);
493	struct iwl_mvm_time_event_data *te_data = data;
494	struct iwl_time_event_resp *resp;
495	int resp_len = iwl_rx_packet_payload_len(pkt);
496
497	if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD))
498		return true;
499
500	if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
501		IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n");
502		return true;
503	}
504
505	resp = (void *)pkt->data;
506
507	/* we should never get a response to another TIME_EVENT_CMD here */
508	if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id))
509		return false;
510
511	te_data->uid = le32_to_cpu(resp->unique_id);
512	IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
513		     te_data->uid);
514	return true;
515}
516
517static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
518				       struct ieee80211_vif *vif,
519				       struct iwl_mvm_time_event_data *te_data,
520				       struct iwl_time_event_cmd *te_cmd)
521{
522	static const u16 time_event_response[] = { TIME_EVENT_CMD };
523	struct iwl_notification_wait wait_time_event;
524	int ret;
525
526	lockdep_assert_held(&mvm->mutex);
527
528	IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n",
529		     le32_to_cpu(te_cmd->duration));
530
531	spin_lock_bh(&mvm->time_event_lock);
532	if (WARN_ON(te_data->id != TE_MAX)) {
533		spin_unlock_bh(&mvm->time_event_lock);
534		return -EIO;
535	}
536	te_data->vif = vif;
537	te_data->duration = le32_to_cpu(te_cmd->duration);
538	te_data->id = le32_to_cpu(te_cmd->id);
539	list_add_tail(&te_data->list, &mvm->time_event_list);
540	spin_unlock_bh(&mvm->time_event_lock);
541
542	/*
543	 * Use a notification wait, which really just processes the
544	 * command response and doesn't wait for anything, in order
545	 * to be able to process the response and get the UID inside
546	 * the RX path. Using CMD_WANT_SKB doesn't work because it
547	 * stores the buffer and then wakes up this thread, by which
548	 * time another notification (that the time event started)
549	 * might already be processed unsuccessfully.
550	 */
551	iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
552				   time_event_response,
553				   ARRAY_SIZE(time_event_response),
554				   iwl_mvm_time_event_response, te_data);
555
556	ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
557					    sizeof(*te_cmd), te_cmd);
558	if (ret) {
559		IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
560		iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
561		goto out_clear_te;
562	}
563
564	/* No need to wait for anything, so just pass 1 (0 isn't valid) */
565	ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
566	/* should never fail */
567	WARN_ON_ONCE(ret);
568
569	if (ret) {
570 out_clear_te:
571		spin_lock_bh(&mvm->time_event_lock);
572		iwl_mvm_te_clear_data(mvm, te_data);
573		spin_unlock_bh(&mvm->time_event_lock);
574	}
575	return ret;
576}
577
578void iwl_mvm_protect_session(struct iwl_mvm *mvm,
579			     struct ieee80211_vif *vif,
580			     u32 duration, u32 min_duration,
581			     u32 max_delay, bool wait_for_notif)
582{
583	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
584	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
585	const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION };
586	struct iwl_notification_wait wait_te_notif;
587	struct iwl_time_event_cmd time_cmd = {};
588
589	lockdep_assert_held(&mvm->mutex);
590
591	if (te_data->running &&
592	    time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
593		IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
594			     jiffies_to_msecs(te_data->end_jiffies - jiffies));
595		return;
596	}
597
598	if (te_data->running) {
599		IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
600			     te_data->uid,
601			     jiffies_to_msecs(te_data->end_jiffies - jiffies));
602		/*
603		 * we don't have enough time
604		 * cancel the current TE and issue a new one
605		 * Of course it would be better to remove the old one only
606		 * when the new one is added, but we don't care if we are off
607		 * channel for a bit. All we need to do, is not to return
608		 * before we actually begin to be on the channel.
609		 */
610		iwl_mvm_stop_session_protection(mvm, vif);
611	}
612
613	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
614	time_cmd.id_and_color =
615		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
616	time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
617
618	time_cmd.apply_time = cpu_to_le32(0);
619
620	time_cmd.max_frags = TE_V2_FRAG_NONE;
621	time_cmd.max_delay = cpu_to_le32(max_delay);
622	/* TODO: why do we need to interval = bi if it is not periodic? */
623	time_cmd.interval = cpu_to_le32(1);
624	time_cmd.duration = cpu_to_le32(duration);
625	time_cmd.repeat = 1;
626	time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
627				      TE_V2_NOTIF_HOST_EVENT_END |
628				      TE_V2_START_IMMEDIATELY);
629
630	if (!wait_for_notif) {
631		iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
632		return;
633	}
634
635	/*
636	 * Create notification_wait for the TIME_EVENT_NOTIFICATION to use
637	 * right after we send the time event
638	 */
639	iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif,
640				   te_notif_response,
641				   ARRAY_SIZE(te_notif_response),
642				   iwl_mvm_te_notif, te_data);
643
644	/* If TE was sent OK - wait for the notification that started */
645	if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) {
646		IWL_ERR(mvm, "Failed to add TE to protect session\n");
647		iwl_remove_notification(&mvm->notif_wait, &wait_te_notif);
648	} else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif,
649					 TU_TO_JIFFIES(max_delay))) {
650		IWL_ERR(mvm, "Failed to protect session until TE\n");
651	}
652}
653
654static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm,
655					      struct iwl_mvm_vif *mvmvif,
656					      u32 id)
657{
658	struct iwl_mvm_session_prot_cmd cmd = {
659		.id_and_color =
660			cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
661							mvmvif->color)),
662		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
663		.conf_id = cpu_to_le32(id),
664	};
665	int ret;
666
667	ret = iwl_mvm_send_cmd_pdu(mvm,
668				   WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD),
669				   0, sizeof(cmd), &cmd);
670	if (ret)
671		IWL_ERR(mvm,
672			"Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret);
673}
674
675static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
676					struct iwl_mvm_time_event_data *te_data,
677					u32 *uid)
678{
679	u32 id;
680	struct iwl_mvm_vif *mvmvif;
681	enum nl80211_iftype iftype;
682
683	if (!te_data->vif)
684		return false;
685
686	mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
687	iftype = te_data->vif->type;
688
689	/*
690	 * It is possible that by the time we got to this point the time
691	 * event was already removed.
692	 */
693	spin_lock_bh(&mvm->time_event_lock);
694
695	/* Save time event uid before clearing its data */
696	*uid = te_data->uid;
697	id = te_data->id;
698
699	/*
700	 * The clear_data function handles time events that were already removed
701	 */
702	iwl_mvm_te_clear_data(mvm, te_data);
703	spin_unlock_bh(&mvm->time_event_lock);
704
705	/* When session protection is used, the te_data->id field
706	 * is reused to save session protection's configuration.
707	 * For AUX ROC, HOT_SPOT_CMD is used and the te_data->id field is set
708	 * to HOT_SPOT_CMD.
709	 */
710	if (fw_has_capa(&mvm->fw->ucode_capa,
711			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD) &&
712	    id != HOT_SPOT_CMD) {
713		if (mvmvif && id < SESSION_PROTECT_CONF_MAX_ID) {
714			/* Session protection is still ongoing. Cancel it */
715			iwl_mvm_cancel_session_protection(mvm, mvmvif, id);
716			if (iftype == NL80211_IFTYPE_P2P_DEVICE) {
717				iwl_mvm_p2p_roc_finished(mvm);
718			}
719		}
720		return false;
721	} else {
722		/* It is possible that by the time we try to remove it, the
723		 * time event has already ended and removed. In such a case
724		 * there is no need to send a removal command.
725		 */
726		if (id == TE_MAX) {
727			IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
728			return false;
729		}
730	}
731
732	return true;
733}
734
735/*
736 * Explicit request to remove a aux roc time event. The removal of a time
737 * event needs to be synchronized with the flow of a time event's end
738 * notification, which also removes the time event from the op mode
739 * data structures.
740 */
741static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
742				      struct iwl_mvm_vif *mvmvif,
743				      struct iwl_mvm_time_event_data *te_data)
744{
745	struct iwl_hs20_roc_req aux_cmd = {};
746	u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm);
747
748	u32 uid;
749	int ret;
750
751	if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
752		return;
753
754	aux_cmd.event_unique_id = cpu_to_le32(uid);
755	aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
756	aux_cmd.id_and_color =
757		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
758	IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
759		     le32_to_cpu(aux_cmd.event_unique_id));
760	ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
761				   len, &aux_cmd);
762
763	if (WARN_ON(ret))
764		return;
765}
766
767/*
768 * Explicit request to remove a time event. The removal of a time event needs to
769 * be synchronized with the flow of a time event's end notification, which also
770 * removes the time event from the op mode data structures.
771 */
772void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
773			       struct iwl_mvm_vif *mvmvif,
774			       struct iwl_mvm_time_event_data *te_data)
775{
776	struct iwl_time_event_cmd time_cmd = {};
777	u32 uid;
778	int ret;
779
780	if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
781		return;
782
783	/* When we remove a TE, the UID is to be set in the id field */
784	time_cmd.id = cpu_to_le32(uid);
785	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
786	time_cmd.id_and_color =
787		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
788
789	IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
790	ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
791				   sizeof(time_cmd), &time_cmd);
792	if (ret)
793		IWL_ERR(mvm, "Couldn't remove the time event\n");
794}
795
796void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
797				     struct ieee80211_vif *vif)
798{
799	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
800	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
801	u32 id;
802
803	lockdep_assert_held(&mvm->mutex);
804
805	spin_lock_bh(&mvm->time_event_lock);
806	id = te_data->id;
807	spin_unlock_bh(&mvm->time_event_lock);
808
809	if (fw_has_capa(&mvm->fw->ucode_capa,
810			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {
811		if (id != SESSION_PROTECT_CONF_ASSOC) {
812			IWL_DEBUG_TE(mvm,
813				     "don't remove session protection id=%u\n",
814				     id);
815			return;
816		}
817	} else if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) {
818		IWL_DEBUG_TE(mvm,
819			     "don't remove TE with id=%u (not session protection)\n",
820			     id);
821		return;
822	}
823
824	iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
825}
826
827void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,
828				      struct iwl_rx_cmd_buffer *rxb)
829{
830	struct iwl_rx_packet *pkt = rxb_addr(rxb);
831	struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data;
832	struct ieee80211_vif *vif;
833	struct iwl_mvm_vif *mvmvif;
834
835	rcu_read_lock();
836	vif = iwl_mvm_rcu_dereference_vif_id(mvm, le32_to_cpu(notif->mac_id),
837					     true);
838
839	if (!vif)
840		goto out_unlock;
841
842	mvmvif = iwl_mvm_vif_from_mac80211(vif);
843
844	/* The vif is not a P2P_DEVICE, maintain its time_event_data */
845	if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
846		struct iwl_mvm_time_event_data *te_data =
847			&mvmvif->time_event_data;
848
849		if (!le32_to_cpu(notif->status)) {
850			iwl_mvm_te_check_disconnect(mvm, vif,
851						    "Session protection failure");
852			spin_lock_bh(&mvm->time_event_lock);
853			iwl_mvm_te_clear_data(mvm, te_data);
854			spin_unlock_bh(&mvm->time_event_lock);
855		}
856
857		if (le32_to_cpu(notif->start)) {
858			spin_lock_bh(&mvm->time_event_lock);
859			te_data->running = le32_to_cpu(notif->start);
860			te_data->end_jiffies =
861				TU_TO_EXP_TIME(te_data->duration);
862			spin_unlock_bh(&mvm->time_event_lock);
863		} else {
864			/*
865			 * By now, we should have finished association
866			 * and know the dtim period.
867			 */
868			iwl_mvm_te_check_disconnect(mvm, vif,
869						    !vif->cfg.assoc ?
870						    "Not associated and the session protection is over already..." :
871						    "No beacon heard and the session protection is over already...");
872			spin_lock_bh(&mvm->time_event_lock);
873			iwl_mvm_te_clear_data(mvm, te_data);
874			spin_unlock_bh(&mvm->time_event_lock);
875		}
876
877		goto out_unlock;
878	}
879
880	if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) {
881		/* End TE, notify mac80211 */
882		mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID;
883		ieee80211_remain_on_channel_expired(mvm->hw);
884		iwl_mvm_p2p_roc_finished(mvm);
885	} else if (le32_to_cpu(notif->start)) {
886		if (WARN_ON(mvmvif->time_event_data.id !=
887				le32_to_cpu(notif->conf_id)))
888			goto out_unlock;
889		set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
890		ieee80211_ready_on_channel(mvm->hw); /* Start TE */
891	}
892
893 out_unlock:
894	rcu_read_unlock();
895}
896
897static int
898iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm,
899					 struct ieee80211_vif *vif,
900					 int duration,
901					 enum ieee80211_roc_type type)
902{
903	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
904	struct iwl_mvm_session_prot_cmd cmd = {
905		.id_and_color =
906			cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
907							mvmvif->color)),
908		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
909		.duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
910	};
911
912	lockdep_assert_held(&mvm->mutex);
913
914	/* The time_event_data.id field is reused to save session
915	 * protection's configuration.
916	 */
917	switch (type) {
918	case IEEE80211_ROC_TYPE_NORMAL:
919		mvmvif->time_event_data.id =
920			SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV;
921		break;
922	case IEEE80211_ROC_TYPE_MGMT_TX:
923		mvmvif->time_event_data.id =
924			SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION;
925		break;
926	default:
927		WARN_ONCE(1, "Got an invalid ROC type\n");
928		return -EINVAL;
929	}
930
931	cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id);
932	return iwl_mvm_send_cmd_pdu(mvm,
933				    WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD),
934				    0, sizeof(cmd), &cmd);
935}
936
937int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
938			  int duration, enum ieee80211_roc_type type)
939{
940	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
941	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
942	struct iwl_time_event_cmd time_cmd = {};
943
944	lockdep_assert_held(&mvm->mutex);
945	if (te_data->running) {
946		IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
947		return -EBUSY;
948	}
949
950	if (fw_has_capa(&mvm->fw->ucode_capa,
951			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD))
952		return iwl_mvm_start_p2p_roc_session_protection(mvm, vif,
953								duration,
954								type);
955
956	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
957	time_cmd.id_and_color =
958		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
959
960	switch (type) {
961	case IEEE80211_ROC_TYPE_NORMAL:
962		time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
963		break;
964	case IEEE80211_ROC_TYPE_MGMT_TX:
965		time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
966		break;
967	default:
968		WARN_ONCE(1, "Got an invalid ROC type\n");
969		return -EINVAL;
970	}
971
972	time_cmd.apply_time = cpu_to_le32(0);
973	time_cmd.interval = cpu_to_le32(1);
974
975	/*
976	 * The P2P Device TEs can have lower priority than other events
977	 * that are being scheduled by the driver/fw, and thus it might not be
978	 * scheduled. To improve the chances of it being scheduled, allow them
979	 * to be fragmented, and in addition allow them to be delayed.
980	 */
981	time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
982	time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
983	time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
984	time_cmd.repeat = 1;
985	time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
986				      TE_V2_NOTIF_HOST_EVENT_END |
987				      TE_V2_START_IMMEDIATELY);
988
989	return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
990}
991
992static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm)
993{
994	struct iwl_mvm_time_event_data *te_data;
995
996	lockdep_assert_held(&mvm->mutex);
997
998	spin_lock_bh(&mvm->time_event_lock);
999
1000	/*
1001	 * Iterate over the list of time events and find the time event that is
1002	 * associated with a P2P_DEVICE interface.
1003	 * This assumes that a P2P_DEVICE interface can have only a single time
1004	 * event at any given time and this time event coresponds to a ROC
1005	 * request
1006	 */
1007	list_for_each_entry(te_data, &mvm->time_event_list, list) {
1008		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
1009			goto out;
1010	}
1011
1012	/* There can only be at most one AUX ROC time event, we just use the
1013	 * list to simplify/unify code. Remove it if it exists.
1014	 */
1015	te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,
1016					   struct iwl_mvm_time_event_data,
1017					   list);
1018out:
1019	spin_unlock_bh(&mvm->time_event_lock);
1020	return te_data;
1021}
1022
1023void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)
1024{
1025	struct iwl_mvm_time_event_data *te_data;
1026	u32 uid;
1027
1028	te_data = iwl_mvm_get_roc_te(mvm);
1029	if (te_data)
1030		__iwl_mvm_remove_time_event(mvm, te_data, &uid);
1031}
1032
1033void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
1034{
1035	struct iwl_mvm_vif *mvmvif;
1036	struct iwl_mvm_time_event_data *te_data;
1037
1038	if (fw_has_capa(&mvm->fw->ucode_capa,
1039			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {
1040		mvmvif = iwl_mvm_vif_from_mac80211(vif);
1041
1042		if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
1043			iwl_mvm_cancel_session_protection(mvm, mvmvif,
1044							  mvmvif->time_event_data.id);
1045			iwl_mvm_p2p_roc_finished(mvm);
1046		} else {
1047			iwl_mvm_remove_aux_roc_te(mvm, mvmvif,
1048						  &mvmvif->hs_time_event_data);
1049			iwl_mvm_roc_finished(mvm);
1050		}
1051
1052		return;
1053	}
1054
1055	te_data = iwl_mvm_get_roc_te(mvm);
1056	if (!te_data) {
1057		IWL_WARN(mvm, "No remain on channel event\n");
1058		return;
1059	}
1060
1061	mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
1062
1063	if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
1064		iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
1065		iwl_mvm_p2p_roc_finished(mvm);
1066	} else {
1067		iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
1068		iwl_mvm_roc_finished(mvm);
1069	}
1070}
1071
1072void iwl_mvm_remove_csa_period(struct iwl_mvm *mvm,
1073			       struct ieee80211_vif *vif)
1074{
1075	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1076	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1077	u32 id;
1078
1079	lockdep_assert_held(&mvm->mutex);
1080
1081	spin_lock_bh(&mvm->time_event_lock);
1082	id = te_data->id;
1083	spin_unlock_bh(&mvm->time_event_lock);
1084
1085	if (id != TE_CHANNEL_SWITCH_PERIOD)
1086		return;
1087
1088	iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
1089}
1090
1091int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
1092				struct ieee80211_vif *vif,
1093				u32 duration, u32 apply_time)
1094{
1095	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1096	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1097	struct iwl_time_event_cmd time_cmd = {};
1098
1099	lockdep_assert_held(&mvm->mutex);
1100
1101	if (te_data->running) {
1102		u32 id;
1103
1104		spin_lock_bh(&mvm->time_event_lock);
1105		id = te_data->id;
1106		spin_unlock_bh(&mvm->time_event_lock);
1107
1108		if (id == TE_CHANNEL_SWITCH_PERIOD) {
1109			IWL_DEBUG_TE(mvm, "CS period is already scheduled\n");
1110			return -EBUSY;
1111		}
1112
1113		/*
1114		 * Remove the session protection time event to allow the
1115		 * channel switch. If we got here, we just heard a beacon so
1116		 * the session protection is not needed anymore anyway.
1117		 */
1118		iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
1119	}
1120
1121	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
1122	time_cmd.id_and_color =
1123		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
1124	time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD);
1125	time_cmd.apply_time = cpu_to_le32(apply_time);
1126	time_cmd.max_frags = TE_V2_FRAG_NONE;
1127	time_cmd.duration = cpu_to_le32(duration);
1128	time_cmd.repeat = 1;
1129	time_cmd.interval = cpu_to_le32(1);
1130	time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
1131				      TE_V2_ABSENCE);
1132	if (!apply_time)
1133		time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY);
1134
1135	return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
1136}
1137
1138static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait,
1139				       struct iwl_rx_packet *pkt, void *data)
1140{
1141	struct iwl_mvm *mvm =
1142		container_of(notif_wait, struct iwl_mvm, notif_wait);
1143	struct iwl_mvm_session_prot_notif *resp;
1144	int resp_len = iwl_rx_packet_payload_len(pkt);
1145
1146	if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF ||
1147		    pkt->hdr.group_id != MAC_CONF_GROUP))
1148		return true;
1149
1150	if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
1151		IWL_ERR(mvm, "Invalid SESSION_PROTECTION_NOTIF response\n");
1152		return true;
1153	}
1154
1155	resp = (void *)pkt->data;
1156
1157	if (!resp->status)
1158		IWL_ERR(mvm,
1159			"TIME_EVENT_NOTIFICATION received but not executed\n");
1160
1161	return true;
1162}
1163
1164void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm,
1165					 struct ieee80211_vif *vif,
1166					 u32 duration, u32 min_duration,
1167					 bool wait_for_notif)
1168{
1169	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1170	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
1171	const u16 notif[] = { WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF) };
1172	struct iwl_notification_wait wait_notif;
1173	struct iwl_mvm_session_prot_cmd cmd = {
1174		.id_and_color =
1175			cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
1176							mvmvif->color)),
1177		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
1178		.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
1179		.duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
1180	};
1181
1182	lockdep_assert_held(&mvm->mutex);
1183
1184	spin_lock_bh(&mvm->time_event_lock);
1185	if (te_data->running &&
1186	    time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
1187		IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
1188			     jiffies_to_msecs(te_data->end_jiffies - jiffies));
1189		spin_unlock_bh(&mvm->time_event_lock);
1190
1191		return;
1192	}
1193
1194	iwl_mvm_te_clear_data(mvm, te_data);
1195	/*
1196	 * The time_event_data.id field is reused to save session
1197	 * protection's configuration.
1198	 */
1199	te_data->id = le32_to_cpu(cmd.conf_id);
1200	te_data->duration = le32_to_cpu(cmd.duration_tu);
1201	te_data->vif = vif;
1202	spin_unlock_bh(&mvm->time_event_lock);
1203
1204	IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n",
1205		     le32_to_cpu(cmd.duration_tu));
1206
1207	if (!wait_for_notif) {
1208		if (iwl_mvm_send_cmd_pdu(mvm,
1209					 WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD),
1210					 0, sizeof(cmd), &cmd)) {
1211			IWL_ERR(mvm,
1212				"Couldn't send the SESSION_PROTECTION_CMD\n");
1213			spin_lock_bh(&mvm->time_event_lock);
1214			iwl_mvm_te_clear_data(mvm, te_data);
1215			spin_unlock_bh(&mvm->time_event_lock);
1216		}
1217
1218		return;
1219	}
1220
1221	iwl_init_notification_wait(&mvm->notif_wait, &wait_notif,
1222				   notif, ARRAY_SIZE(notif),
1223				   iwl_mvm_session_prot_notif, NULL);
1224
1225	if (iwl_mvm_send_cmd_pdu(mvm,
1226				 WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD),
1227				 0, sizeof(cmd), &cmd)) {
1228		IWL_ERR(mvm,
1229			"Couldn't send the SESSION_PROTECTION_CMD\n");
1230		iwl_remove_notification(&mvm->notif_wait, &wait_notif);
1231	} else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif,
1232					 TU_TO_JIFFIES(100))) {
1233		IWL_ERR(mvm,
1234			"Failed to protect session until session protection\n");
1235	}
1236}
1237