1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65#include <linux/jiffies.h>
66#include <net/mac80211.h>
67
68#include "fw/notif-wait.h"
69#include "iwl-trans.h"
70#include "fw-api.h"
71#include "time-event.h"
72#include "mvm.h"
73#include "iwl-io.h"
74#include "iwl-prph.h"
75
76
77
78
79
80#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE
81#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC
82
83void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
84 struct iwl_mvm_time_event_data *te_data)
85{
86 lockdep_assert_held(&mvm->time_event_lock);
87
88 if (!te_data->vif)
89 return;
90
91 list_del(&te_data->list);
92 te_data->running = false;
93 te_data->uid = 0;
94 te_data->id = TE_MAX;
95 te_data->vif = NULL;
96}
97
98void iwl_mvm_roc_done_wk(struct work_struct *wk)
99{
100 struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
101
102
103
104
105
106
107
108
109 if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
110 iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
111 if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
112 iwl_mvm_unref(mvm, IWL_MVM_REF_ROC_AUX);
113
114 synchronize_net();
115
116
117
118
119
120
121
122
123
124
125
126 iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true, CMD_ASYNC);
127
128
129 if (test_and_clear_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) {
130 struct iwl_mvm_vif *mvmvif;
131
132
133
134
135
136
137
138
139 if (!WARN_ON(!mvm->p2p_device_vif)) {
140 mvmvif = iwl_mvm_vif_from_mac80211(mvm->p2p_device_vif);
141 iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true,
142 CMD_ASYNC);
143 }
144 }
145}
146
147static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
148{
149
150
151
152
153
154
155
156 schedule_work(&mvm->roc_done_wk);
157}
158
159static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
160{
161 struct ieee80211_vif *csa_vif;
162
163 rcu_read_lock();
164
165 csa_vif = rcu_dereference(mvm->csa_vif);
166 if (!csa_vif || !csa_vif->csa_active)
167 goto out_unlock;
168
169 IWL_DEBUG_TE(mvm, "CSA NOA started\n");
170
171
172
173
174
175
176
177 if (!ieee80211_csa_is_complete(csa_vif)) {
178 IWL_WARN(mvm, "CSA NOA started too early\n");
179 goto out_unlock;
180 }
181
182 ieee80211_csa_finish(csa_vif);
183
184 rcu_read_unlock();
185
186 RCU_INIT_POINTER(mvm->csa_vif, NULL);
187
188 return;
189
190out_unlock:
191 rcu_read_unlock();
192}
193
194static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
195 struct ieee80211_vif *vif,
196 const char *errmsg)
197{
198 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
199
200 if (vif->type != NL80211_IFTYPE_STATION)
201 return false;
202
203 if (!mvmvif->csa_bcn_pending && vif->bss_conf.assoc &&
204 vif->bss_conf.dtim_period)
205 return false;
206 if (errmsg)
207 IWL_ERR(mvm, "%s\n", errmsg);
208
209 iwl_mvm_connection_loss(mvm, vif, errmsg);
210 return true;
211}
212
213static void
214iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
215 struct iwl_mvm_time_event_data *te_data,
216 struct iwl_time_event_notif *notif)
217{
218 struct ieee80211_vif *vif = te_data->vif;
219 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
220
221 if (!notif->status)
222 IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
223
224 switch (te_data->vif->type) {
225 case NL80211_IFTYPE_AP:
226 if (!notif->status)
227 mvmvif->csa_failed = true;
228 iwl_mvm_csa_noa_start(mvm);
229 break;
230 case NL80211_IFTYPE_STATION:
231 if (!notif->status) {
232 iwl_mvm_connection_loss(mvm, vif,
233 "CSA TE failed to start");
234 break;
235 }
236 iwl_mvm_csa_client_absent(mvm, te_data->vif);
237 ieee80211_chswitch_done(te_data->vif, true);
238 break;
239 default:
240
241 WARN_ON_ONCE(1);
242 break;
243 }
244
245
246 iwl_mvm_te_clear_data(mvm, te_data);
247}
248
249static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
250 struct iwl_time_event_notif *notif,
251 struct iwl_mvm_time_event_data *te_data)
252{
253 struct iwl_fw_dbg_trigger_tlv *trig;
254 struct iwl_fw_dbg_trigger_time_event *te_trig;
255 int i;
256
257 if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT))
258 return;
259
260 trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT);
261 te_trig = (void *)trig->data;
262
263 if (!iwl_fw_dbg_trigger_check_stop(&mvm->fwrt,
264 ieee80211_vif_to_wdev(te_data->vif),
265 trig))
266 return;
267
268 for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
269 u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
270 u32 trig_action_bitmap =
271 le32_to_cpu(te_trig->time_events[i].action_bitmap);
272 u32 trig_status_bitmap =
273 le32_to_cpu(te_trig->time_events[i].status_bitmap);
274
275 if (trig_te_id != te_data->id ||
276 !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
277 !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
278 continue;
279
280 iwl_fw_dbg_collect_trig(&mvm->fwrt, trig,
281 "Time event %d Action 0x%x received status: %d",
282 te_data->id,
283 le32_to_cpu(notif->action),
284 le32_to_cpu(notif->status));
285 break;
286 }
287}
288
289
290
291
292
293
294
295
296static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
297 struct iwl_mvm_time_event_data *te_data,
298 struct iwl_time_event_notif *notif)
299{
300 lockdep_assert_held(&mvm->time_event_lock);
301
302 IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
303 le32_to_cpu(notif->unique_id),
304 le32_to_cpu(notif->action));
305
306 iwl_mvm_te_check_trigger(mvm, notif, te_data);
307
308
309
310
311
312
313
314
315
316 if (!le32_to_cpu(notif->status)) {
317 const char *msg;
318
319 if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
320 msg = "Time Event start notification failure";
321 else
322 msg = "Time Event end notification failure";
323
324 IWL_DEBUG_TE(mvm, "%s\n", msg);
325
326 if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
327 iwl_mvm_te_clear_data(mvm, te_data);
328 return;
329 }
330 }
331
332 if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
333 IWL_DEBUG_TE(mvm,
334 "TE ended - current time %lu, estimated end %lu\n",
335 jiffies, te_data->end_jiffies);
336
337 switch (te_data->vif->type) {
338 case NL80211_IFTYPE_P2P_DEVICE:
339 ieee80211_remain_on_channel_expired(mvm->hw);
340 iwl_mvm_roc_finished(mvm);
341 break;
342 case NL80211_IFTYPE_STATION:
343
344
345
346
347 iwl_mvm_te_check_disconnect(mvm, te_data->vif,
348 "No beacon heard and the time event is over already...");
349 break;
350 default:
351 break;
352 }
353
354 iwl_mvm_te_clear_data(mvm, te_data);
355 } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
356 te_data->running = true;
357 te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
358
359 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
360 set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
361 iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
362 ieee80211_ready_on_channel(mvm->hw);
363 } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
364 iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
365 }
366 } else {
367 IWL_WARN(mvm, "Got TE with unknown action\n");
368 }
369}
370
371
372
373
374static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
375 struct iwl_time_event_notif *notif)
376{
377 struct iwl_mvm_time_event_data *te_data, *tmp;
378 bool aux_roc_te = false;
379
380 list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) {
381 if (le32_to_cpu(notif->unique_id) == te_data->uid) {
382 aux_roc_te = true;
383 break;
384 }
385 }
386 if (!aux_roc_te)
387 return -EINVAL;
388
389 iwl_mvm_te_check_trigger(mvm, notif, te_data);
390
391 IWL_DEBUG_TE(mvm,
392 "Aux ROC time event notification - UID = 0x%x action %d (error = %d)\n",
393 le32_to_cpu(notif->unique_id),
394 le32_to_cpu(notif->action), le32_to_cpu(notif->status));
395
396 if (!le32_to_cpu(notif->status) ||
397 le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
398
399 ieee80211_remain_on_channel_expired(mvm->hw);
400 iwl_mvm_roc_finished(mvm);
401 list_del(&te_data->list);
402 te_data->running = false;
403 te_data->vif = NULL;
404 te_data->uid = 0;
405 te_data->id = TE_MAX;
406 } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
407 set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
408 te_data->running = true;
409 iwl_mvm_ref(mvm, IWL_MVM_REF_ROC_AUX);
410 ieee80211_ready_on_channel(mvm->hw);
411 } else {
412 IWL_DEBUG_TE(mvm,
413 "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
414 le32_to_cpu(notif->action));
415 return -EINVAL;
416 }
417
418 return 0;
419}
420
421
422
423
424void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
425 struct iwl_rx_cmd_buffer *rxb)
426{
427 struct iwl_rx_packet *pkt = rxb_addr(rxb);
428 struct iwl_time_event_notif *notif = (void *)pkt->data;
429 struct iwl_mvm_time_event_data *te_data, *tmp;
430
431 IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
432 le32_to_cpu(notif->unique_id),
433 le32_to_cpu(notif->action));
434
435 spin_lock_bh(&mvm->time_event_lock);
436
437 if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
438 goto unlock;
439
440 list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
441 if (le32_to_cpu(notif->unique_id) == te_data->uid)
442 iwl_mvm_te_handle_notif(mvm, te_data, notif);
443 }
444unlock:
445 spin_unlock_bh(&mvm->time_event_lock);
446}
447
448static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait,
449 struct iwl_rx_packet *pkt, void *data)
450{
451 struct iwl_mvm *mvm =
452 container_of(notif_wait, struct iwl_mvm, notif_wait);
453 struct iwl_mvm_time_event_data *te_data = data;
454 struct iwl_time_event_notif *resp;
455 int resp_len = iwl_rx_packet_payload_len(pkt);
456
457 if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION))
458 return true;
459
460 if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
461 IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n");
462 return true;
463 }
464
465 resp = (void *)pkt->data;
466
467
468 if (le32_to_cpu(resp->unique_id) != te_data->uid)
469 return false;
470
471 IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n",
472 te_data->uid);
473 if (!resp->status)
474 IWL_ERR(mvm,
475 "TIME_EVENT_NOTIFICATION received but not executed\n");
476
477 return true;
478}
479
480static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
481 struct iwl_rx_packet *pkt, void *data)
482{
483 struct iwl_mvm *mvm =
484 container_of(notif_wait, struct iwl_mvm, notif_wait);
485 struct iwl_mvm_time_event_data *te_data = data;
486 struct iwl_time_event_resp *resp;
487 int resp_len = iwl_rx_packet_payload_len(pkt);
488
489 if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD))
490 return true;
491
492 if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
493 IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n");
494 return true;
495 }
496
497 resp = (void *)pkt->data;
498
499
500 if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id))
501 return false;
502
503 te_data->uid = le32_to_cpu(resp->unique_id);
504 IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
505 te_data->uid);
506 return true;
507}
508
509static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
510 struct ieee80211_vif *vif,
511 struct iwl_mvm_time_event_data *te_data,
512 struct iwl_time_event_cmd *te_cmd)
513{
514 static const u16 time_event_response[] = { TIME_EVENT_CMD };
515 struct iwl_notification_wait wait_time_event;
516 int ret;
517
518 lockdep_assert_held(&mvm->mutex);
519
520 IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n",
521 le32_to_cpu(te_cmd->duration));
522
523 spin_lock_bh(&mvm->time_event_lock);
524 if (WARN_ON(te_data->id != TE_MAX)) {
525 spin_unlock_bh(&mvm->time_event_lock);
526 return -EIO;
527 }
528 te_data->vif = vif;
529 te_data->duration = le32_to_cpu(te_cmd->duration);
530 te_data->id = le32_to_cpu(te_cmd->id);
531 list_add_tail(&te_data->list, &mvm->time_event_list);
532 spin_unlock_bh(&mvm->time_event_lock);
533
534
535
536
537
538
539
540
541
542
543 iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
544 time_event_response,
545 ARRAY_SIZE(time_event_response),
546 iwl_mvm_time_event_response, te_data);
547
548 ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
549 sizeof(*te_cmd), te_cmd);
550 if (ret) {
551 IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
552 iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
553 goto out_clear_te;
554 }
555
556
557 ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
558
559 WARN_ON_ONCE(ret);
560
561 if (ret) {
562 out_clear_te:
563 spin_lock_bh(&mvm->time_event_lock);
564 iwl_mvm_te_clear_data(mvm, te_data);
565 spin_unlock_bh(&mvm->time_event_lock);
566 }
567 return ret;
568}
569
570void iwl_mvm_protect_session(struct iwl_mvm *mvm,
571 struct ieee80211_vif *vif,
572 u32 duration, u32 min_duration,
573 u32 max_delay, bool wait_for_notif)
574{
575 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
576 struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
577 const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION };
578 struct iwl_notification_wait wait_te_notif;
579 struct iwl_time_event_cmd time_cmd = {};
580
581 lockdep_assert_held(&mvm->mutex);
582
583 if (te_data->running &&
584 time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
585 IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
586 jiffies_to_msecs(te_data->end_jiffies - jiffies));
587 return;
588 }
589
590 if (te_data->running) {
591 IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
592 te_data->uid,
593 jiffies_to_msecs(te_data->end_jiffies - jiffies));
594
595
596
597
598
599
600
601
602 iwl_mvm_stop_session_protection(mvm, vif);
603 }
604
605 time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
606 time_cmd.id_and_color =
607 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
608 time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
609
610 time_cmd.apply_time = cpu_to_le32(0);
611
612 time_cmd.max_frags = TE_V2_FRAG_NONE;
613 time_cmd.max_delay = cpu_to_le32(max_delay);
614
615 time_cmd.interval = cpu_to_le32(1);
616 time_cmd.duration = cpu_to_le32(duration);
617 time_cmd.repeat = 1;
618 time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
619 TE_V2_NOTIF_HOST_EVENT_END |
620 TE_V2_START_IMMEDIATELY);
621
622 if (!wait_for_notif) {
623 iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
624 return;
625 }
626
627
628
629
630
631 iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif,
632 te_notif_response,
633 ARRAY_SIZE(te_notif_response),
634 iwl_mvm_te_notif, te_data);
635
636
637 if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) {
638 IWL_ERR(mvm, "Failed to add TE to protect session\n");
639 iwl_remove_notification(&mvm->notif_wait, &wait_te_notif);
640 } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif,
641 TU_TO_JIFFIES(max_delay))) {
642 IWL_ERR(mvm, "Failed to protect session until TE\n");
643 }
644}
645
646static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
647 struct iwl_mvm_time_event_data *te_data,
648 u32 *uid)
649{
650 u32 id;
651
652
653
654
655
656 spin_lock_bh(&mvm->time_event_lock);
657
658
659 *uid = te_data->uid;
660 id = te_data->id;
661
662
663
664
665 iwl_mvm_te_clear_data(mvm, te_data);
666 spin_unlock_bh(&mvm->time_event_lock);
667
668
669
670
671
672
673 if (id == TE_MAX) {
674 IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
675 return false;
676 }
677
678 return true;
679}
680
681
682
683
684
685
686
687static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
688 struct iwl_mvm_vif *mvmvif,
689 struct iwl_mvm_time_event_data *te_data)
690{
691 struct iwl_hs20_roc_req aux_cmd = {};
692 u32 uid;
693 int ret;
694
695 if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
696 return;
697
698 aux_cmd.event_unique_id = cpu_to_le32(uid);
699 aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
700 aux_cmd.id_and_color =
701 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
702 IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
703 le32_to_cpu(aux_cmd.event_unique_id));
704 ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
705 sizeof(aux_cmd), &aux_cmd);
706
707 if (WARN_ON(ret))
708 return;
709}
710
711
712
713
714
715
716void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
717 struct iwl_mvm_vif *mvmvif,
718 struct iwl_mvm_time_event_data *te_data)
719{
720 struct iwl_time_event_cmd time_cmd = {};
721 u32 uid;
722 int ret;
723
724 if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
725 return;
726
727
728 time_cmd.id = cpu_to_le32(uid);
729 time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
730 time_cmd.id_and_color =
731 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
732
733 IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
734 ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
735 sizeof(time_cmd), &time_cmd);
736 if (WARN_ON(ret))
737 return;
738}
739
740void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
741 struct ieee80211_vif *vif)
742{
743 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
744 struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
745 u32 id;
746
747 lockdep_assert_held(&mvm->mutex);
748
749 spin_lock_bh(&mvm->time_event_lock);
750 id = te_data->id;
751 spin_unlock_bh(&mvm->time_event_lock);
752
753 if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) {
754 IWL_DEBUG_TE(mvm,
755 "don't remove TE with id=%u (not session protection)\n",
756 id);
757 return;
758 }
759
760 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
761}
762
763int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
764 int duration, enum ieee80211_roc_type type)
765{
766 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
767 struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
768 struct iwl_time_event_cmd time_cmd = {};
769
770 lockdep_assert_held(&mvm->mutex);
771 if (te_data->running) {
772 IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
773 return -EBUSY;
774 }
775
776 time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
777 time_cmd.id_and_color =
778 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
779
780 switch (type) {
781 case IEEE80211_ROC_TYPE_NORMAL:
782 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);
783 break;
784 case IEEE80211_ROC_TYPE_MGMT_TX:
785 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);
786 break;
787 default:
788 WARN_ONCE(1, "Got an invalid ROC type\n");
789 return -EINVAL;
790 }
791
792 time_cmd.apply_time = cpu_to_le32(0);
793 time_cmd.interval = cpu_to_le32(1);
794
795
796
797
798
799
800
801 time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
802 time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
803 time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
804 time_cmd.repeat = 1;
805 time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
806 TE_V2_NOTIF_HOST_EVENT_END |
807 TE_V2_START_IMMEDIATELY);
808
809 return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
810}
811
812static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm)
813{
814 struct iwl_mvm_time_event_data *te_data;
815
816 lockdep_assert_held(&mvm->mutex);
817
818 spin_lock_bh(&mvm->time_event_lock);
819
820
821
822
823
824
825
826
827 list_for_each_entry(te_data, &mvm->time_event_list, list) {
828 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
829 goto out;
830 }
831
832
833
834
835 te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,
836 struct iwl_mvm_time_event_data,
837 list);
838out:
839 spin_unlock_bh(&mvm->time_event_lock);
840 return te_data;
841}
842
843void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)
844{
845 struct iwl_mvm_time_event_data *te_data;
846 u32 uid;
847
848 te_data = iwl_mvm_get_roc_te(mvm);
849 if (te_data)
850 __iwl_mvm_remove_time_event(mvm, te_data, &uid);
851}
852
853void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
854{
855 struct iwl_mvm_vif *mvmvif;
856 struct iwl_mvm_time_event_data *te_data;
857
858 te_data = iwl_mvm_get_roc_te(mvm);
859 if (!te_data) {
860 IWL_WARN(mvm, "No remain on channel event\n");
861 return;
862 }
863
864 mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
865
866 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
867 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
868 set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status);
869 } else {
870 iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
871 }
872
873 iwl_mvm_roc_finished(mvm);
874}
875
876int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
877 struct ieee80211_vif *vif,
878 u32 duration, u32 apply_time)
879{
880 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
881 struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
882 struct iwl_time_event_cmd time_cmd = {};
883
884 lockdep_assert_held(&mvm->mutex);
885
886 if (te_data->running) {
887 u32 id;
888
889 spin_lock_bh(&mvm->time_event_lock);
890 id = te_data->id;
891 spin_unlock_bh(&mvm->time_event_lock);
892
893 if (id == TE_CHANNEL_SWITCH_PERIOD) {
894 IWL_DEBUG_TE(mvm, "CS period is already scheduled\n");
895 return -EBUSY;
896 }
897
898
899
900
901
902
903 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
904 }
905
906 time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
907 time_cmd.id_and_color =
908 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
909 time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD);
910 time_cmd.apply_time = cpu_to_le32(apply_time);
911 time_cmd.max_frags = TE_V2_FRAG_NONE;
912 time_cmd.duration = cpu_to_le32(duration);
913 time_cmd.repeat = 1;
914 time_cmd.interval = cpu_to_le32(1);
915 time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
916 TE_V2_ABSENCE);
917 if (!apply_time)
918 time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY);
919
920 return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
921}
922