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