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