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