1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
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
7 #include <linux/jiffies.h>
8 #include <net/mac80211.h>
10 #include "fw/notif-wait.h"
11 #include "iwl-trans.h"
13 #include "time-event.h"
19 * For the high priority TE use a time event type that has similar priority to
20 * the FW's action scan priority.
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
25 void iwl_mvm_te_clear_data(struct iwl_mvm
*mvm
,
26 struct iwl_mvm_time_event_data
*te_data
)
28 lockdep_assert_held(&mvm
->time_event_lock
);
30 if (!te_data
|| !te_data
->vif
)
33 list_del(&te_data
->list
);
36 * the list is only used for AUX ROC events so make sure it is always
39 INIT_LIST_HEAD(&te_data
->list
);
41 te_data
->running
= false;
45 te_data
->link_id
= -1;
48 static void iwl_mvm_cleanup_roc(struct iwl_mvm
*mvm
)
50 struct ieee80211_vif
*bss_vif
= iwl_mvm_get_bss_vif(mvm
);
51 struct ieee80211_vif
*vif
= mvm
->p2p_device_vif
;
53 lockdep_assert_held(&mvm
->mutex
);
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.
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
66 if (test_and_clear_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING
, &mvm
->status
)) {
67 struct iwl_mvm_vif
*mvmvif
;
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
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
);
83 if (mvm
->mld_api_is_used
) {
84 iwl_mvm_mld_rm_bcast_sta(mvm
, vif
,
87 iwl_mvm_link_changed(mvm
, vif
, &vif
->bss_conf
,
88 LINK_CONTEXT_MODIFY_ACTIVE
,
91 iwl_mvm_rm_p2p_bcast_sta(mvm
, vif
);
92 iwl_mvm_binding_remove_vif(mvm
, vif
);
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.
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).
111 if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING
, &mvm
->status
)) {
114 iwl_mvm_flush_sta(mvm
, mvm
->aux_sta
.sta_id
,
115 mvm
->aux_sta
.tfd_queue_msk
);
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
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
);
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
);
133 void iwl_mvm_roc_done_wk(struct work_struct
*wk
)
135 struct iwl_mvm
*mvm
= container_of(wk
, struct iwl_mvm
, roc_done_wk
);
137 mutex_lock(&mvm
->mutex
);
138 /* Mutex is released inside */
139 iwl_mvm_cleanup_roc(mvm
);
142 static void iwl_mvm_roc_finished(struct iwl_mvm
*mvm
)
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.
151 schedule_work(&mvm
->roc_done_wk
);
154 static void iwl_mvm_csa_noa_start(struct iwl_mvm
*mvm
)
156 struct ieee80211_vif
*csa_vif
;
160 csa_vif
= rcu_dereference(mvm
->csa_vif
);
161 if (!csa_vif
|| !csa_vif
->bss_conf
.csa_active
)
164 IWL_DEBUG_TE(mvm
, "CSA NOA started\n");
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.
172 if (!ieee80211_beacon_cntdwn_is_complete(csa_vif
, 0)) {
173 IWL_WARN(mvm
, "CSA NOA started too early\n");
177 ieee80211_csa_finish(csa_vif
, 0);
181 RCU_INIT_POINTER(mvm
->csa_vif
, NULL
);
189 static bool iwl_mvm_te_check_disconnect(struct iwl_mvm
*mvm
,
190 struct ieee80211_vif
*vif
,
193 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
195 if (vif
->type
!= NL80211_IFTYPE_STATION
)
198 if (!mvmvif
->csa_bcn_pending
&& vif
->cfg
.assoc
&&
199 vif
->bss_conf
.dtim_period
)
202 IWL_ERR(mvm
, "%s\n", errmsg
);
204 if (mvmvif
->csa_bcn_pending
) {
205 struct iwl_mvm_sta
*mvmsta
;
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);
215 if (vif
->cfg
.assoc
) {
217 * When not associated, this will be called from
218 * iwl_mvm_event_mlme_callback_ini()
220 iwl_dbg_tlv_time_point(&mvm
->fwrt
,
221 IWL_FW_INI_TIME_POINT_ASSOC_FAILED
,
224 mvmvif
->session_prot_connection_loss
= true;
227 iwl_mvm_connection_loss(mvm
, vif
, errmsg
);
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
)
236 struct ieee80211_vif
*vif
= te_data
->vif
;
237 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
240 IWL_DEBUG_TE(mvm
, "CSA time event failed to start\n");
242 switch (te_data
->vif
->type
) {
243 case NL80211_IFTYPE_AP
:
245 mvmvif
->csa_failed
= true;
246 iwl_mvm_csa_noa_start(mvm
);
248 case NL80211_IFTYPE_STATION
:
249 if (!notif
->status
) {
250 iwl_mvm_connection_loss(mvm
, vif
,
251 "CSA TE failed to start");
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);
259 /* should never happen */
264 /* we don't need it anymore */
265 iwl_mvm_te_clear_data(mvm
, te_data
);
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
)
272 struct iwl_fw_dbg_trigger_tlv
*trig
;
273 struct iwl_fw_dbg_trigger_time_event
*te_trig
;
276 trig
= iwl_fw_dbg_trigger_on(&mvm
->fwrt
,
277 ieee80211_vif_to_wdev(te_data
->vif
),
278 FW_DBG_TRIGGER_TIME_EVENT
);
282 te_trig
= (void *)trig
->data
;
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
);
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
))))
296 iwl_fw_dbg_collect_trig(&mvm
->fwrt
, trig
,
297 "Time event %d Action 0x%x received status: %d",
299 le32_to_cpu(notif
->action
),
300 le32_to_cpu(notif
->status
));
306 * Handles a FW notification for an event that is known to the driver.
308 * @mvm: the mvm component
309 * @te_data: the time event data
310 * @notif: the notification data corresponding the time event data.
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
)
316 lockdep_assert_held(&mvm
->time_event_lock
);
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
));
322 iwl_mvm_te_check_trigger(mvm
, notif
, te_data
);
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).
332 if (!le32_to_cpu(notif
->status
)) {
335 if (notif
->action
& cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START
))
336 msg
= "Time Event start notification failure";
338 msg
= "Time Event end notification failure";
340 IWL_DEBUG_TE(mvm
, "%s\n", msg
);
342 if (iwl_mvm_te_check_disconnect(mvm
, te_data
->vif
, msg
)) {
343 iwl_mvm_te_clear_data(mvm
, te_data
);
348 if (le32_to_cpu(notif
->action
) & TE_V2_NOTIF_HOST_EVENT_END
) {
350 "TE ended - current time %lu, estimated end %lu\n",
351 jiffies
, te_data
->end_jiffies
);
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
);
358 case NL80211_IFTYPE_STATION
:
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.
364 if (te_data
->id
== TE_CHANNEL_SWITCH_PERIOD
) {
366 "No beacon heard and the CS time event is over, don't disconnect\n");
371 * By now, we should have finished association
372 * and know the dtim period.
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...");
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
);
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
);
395 IWL_WARN(mvm
, "Got TE with unknown action\n");
399 struct iwl_mvm_rx_roc_iterator_data
{
405 static void iwl_mvm_rx_roc_iterator(void *_data
, u8
*mac
,
406 struct ieee80211_vif
*vif
)
408 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
409 struct iwl_mvm_rx_roc_iterator_data
*data
= _data
;
411 if (mvmvif
->roc_activity
== data
->activity
) {
413 if (data
->end_activity
)
414 mvmvif
->roc_activity
= ROC_NUM_ACTIVITIES
;
418 void iwl_mvm_rx_roc_notif(struct iwl_mvm
*mvm
,
419 struct iwl_rx_cmd_buffer
*rxb
)
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
,
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
,
437 * It is possible that the ROC was canceled
438 * but the notification was already fired.
444 set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING
, &mvm
->status
);
445 ieee80211_ready_on_channel(mvm
->hw
);
447 iwl_mvm_roc_finished(mvm
);
448 ieee80211_remain_on_channel_expired(mvm
->hw
);
453 * Handle A Aux ROC time event
455 static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm
*mvm
,
456 struct iwl_time_event_notif
*notif
)
458 struct iwl_mvm_time_event_data
*aux_roc_te
= NULL
, *te_data
;
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
;
466 if (!aux_roc_te
) /* Not a Aux ROC time event */
469 iwl_mvm_te_check_trigger(mvm
, notif
, te_data
);
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
));
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;
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 */
492 "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
493 le32_to_cpu(notif
->action
));
501 * The Rx handler for time event notifications
503 void iwl_mvm_rx_time_event_notif(struct iwl_mvm
*mvm
,
504 struct iwl_rx_cmd_buffer
*rxb
)
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
;
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
));
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
))
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
);
524 spin_unlock_bh(&mvm
->time_event_lock
);
527 static bool iwl_mvm_te_notif(struct iwl_notif_wait_data
*notif_wait
,
528 struct iwl_rx_packet
*pkt
, void *data
)
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
);
536 if (WARN_ON(pkt
->hdr
.cmd
!= TIME_EVENT_NOTIFICATION
))
539 if (WARN_ON_ONCE(resp_len
!= sizeof(*resp
))) {
540 IWL_ERR(mvm
, "Invalid TIME_EVENT_NOTIFICATION response\n");
544 resp
= (void *)pkt
->data
;
546 /* te_data->uid is already set in the TIME_EVENT_CMD response */
547 if (le32_to_cpu(resp
->unique_id
) != te_data
->uid
)
550 IWL_DEBUG_TE(mvm
, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n",
554 "TIME_EVENT_NOTIFICATION received but not executed\n");
559 static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data
*notif_wait
,
560 struct iwl_rx_packet
*pkt
, void *data
)
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
);
568 if (WARN_ON(pkt
->hdr
.cmd
!= TIME_EVENT_CMD
))
571 if (WARN_ON_ONCE(resp_len
!= sizeof(*resp
))) {
572 IWL_ERR(mvm
, "Invalid TIME_EVENT_CMD response\n");
576 resp
= (void *)pkt
->data
;
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
))
582 te_data
->uid
= le32_to_cpu(resp
->unique_id
);
583 IWL_DEBUG_TE(mvm
, "TIME_EVENT_CMD response - UID = 0x%x\n",
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
)
593 static const u16 time_event_response
[] = { TIME_EVENT_CMD
};
594 struct iwl_notification_wait wait_time_event
;
597 lockdep_assert_held(&mvm
->mutex
);
599 IWL_DEBUG_TE(mvm
, "Add new TE, duration %d TU\n",
600 le32_to_cpu(te_cmd
->duration
));
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
);
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
);
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.
622 iwl_init_notification_wait(&mvm
->notif_wait
, &wait_time_event
,
624 ARRAY_SIZE(time_event_response
),
625 iwl_mvm_time_event_response
, te_data
);
627 ret
= iwl_mvm_send_cmd_pdu(mvm
, TIME_EVENT_CMD
, 0,
628 sizeof(*te_cmd
), te_cmd
);
630 IWL_ERR(mvm
, "Couldn't send TIME_EVENT_CMD: %d\n", ret
);
631 iwl_remove_notification(&mvm
->notif_wait
, &wait_time_event
);
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 */
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
);
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
)
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
= {};
660 lockdep_assert_held(&mvm
->mutex
);
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
));
669 if (te_data
->running
) {
670 IWL_DEBUG_TE(mvm
, "extend 0x%x: only %u ms left\n",
672 jiffies_to_msecs(te_data
->end_jiffies
- jiffies
));
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.
681 iwl_mvm_stop_session_protection(mvm
, vif
);
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
);
689 time_cmd
.apply_time
= cpu_to_le32(0);
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
);
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
);
701 if (!wait_for_notif
) {
702 iwl_mvm_time_event_send_add(mvm
, vif
, te_data
, &time_cmd
);
707 * Create notification_wait for the TIME_EVENT_NOTIFICATION to use
708 * right after we send the time event
710 iwl_init_notification_wait(&mvm
->notif_wait
, &wait_te_notif
,
712 ARRAY_SIZE(te_notif_response
),
713 iwl_mvm_te_notif
, te_data
);
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");
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
,
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);
738 if (WARN(link_id
< 0 || !mvmvif
->link
[link_id
],
739 "Invalid link ID for session protection: %u\n", link_id
))
742 if (WARN(!mvmvif
->link
[link_id
]->active
,
743 "Session Protection on an inactive link: %u\n", link_id
))
746 return mvmvif
->link
[link_id
]->fw_link_id
;
749 static void iwl_mvm_cancel_session_protection(struct iwl_mvm
*mvm
,
750 struct ieee80211_vif
*vif
,
753 int mac_link_id
= iwl_mvm_get_session_prot_id(mvm
, vif
, link_id
);
754 struct iwl_mvm_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
),
764 ret
= iwl_mvm_send_cmd_pdu(mvm
,
765 WIDE_ID(MAC_CONF_GROUP
, SESSION_PROTECTION_CMD
),
766 0, sizeof(cmd
), &cmd
);
769 "Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret
);
772 static void iwl_mvm_roc_rm_cmd(struct iwl_mvm
*mvm
, u32 activity
)
774 struct iwl_roc_req roc_cmd
= {
775 .action
= cpu_to_le32(FW_CTXT_ACTION_REMOVE
),
776 .activity
= cpu_to_le32(activity
),
780 lockdep_assert_held(&mvm
->mutex
);
781 ret
= iwl_mvm_send_cmd_pdu(mvm
, WIDE_ID(MAC_CONF_GROUP
, ROC_CMD
), 0,
782 sizeof(roc_cmd
), &roc_cmd
);
784 IWL_ERR(mvm
, "Couldn't send the ROC_CMD: %d\n", ret
);
787 static bool __iwl_mvm_remove_time_event(struct iwl_mvm
*mvm
,
788 struct iwl_mvm_time_event_data
*te_data
,
792 struct ieee80211_vif
*vif
= te_data
->vif
;
793 struct iwl_mvm_vif
*mvmvif
;
794 enum nl80211_iftype iftype
;
796 bool p2p_aux
= iwl_mvm_has_p2p_over_aux(mvm
);
797 u8 roc_ver
= iwl_fw_lookup_cmd_ver(mvm
->fw
,
798 WIDE_ID(MAC_CONF_GROUP
, ROC_CMD
), 0);
803 mvmvif
= iwl_mvm_vif_from_mac80211(te_data
->vif
);
804 iftype
= te_data
->vif
->type
;
807 * It is possible that by the time we got to this point the time
808 * event was already removed.
810 spin_lock_bh(&mvm
->time_event_lock
);
812 /* Save time event uid before clearing its data */
815 link_id
= te_data
->link_id
;
818 * The clear_data function handles time events that were already removed
820 iwl_mvm_te_clear_data(mvm
, te_data
);
821 spin_unlock_bh(&mvm
->time_event_lock
);
823 if ((p2p_aux
&& iftype
== NL80211_IFTYPE_P2P_DEVICE
) ||
824 (roc_ver
>= 3 && mvmvif
->roc_activity
== ROC_ACTIVITY_HOTSPOT
)) {
825 if (mvmvif
->roc_activity
< ROC_NUM_ACTIVITIES
) {
826 iwl_mvm_roc_rm_cmd(mvm
, mvmvif
->roc_activity
);
827 mvmvif
->roc_activity
= ROC_NUM_ACTIVITIES
;
828 iwl_mvm_roc_finished(mvm
);
831 } else if (fw_has_capa(&mvm
->fw
->ucode_capa
,
832 IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD
) &&
833 id
!= HOT_SPOT_CMD
) {
834 /* When session protection is used, the te_data->id field
835 * is reused to save session protection's configuration.
836 * For AUX ROC, HOT_SPOT_CMD is used and the te_data->id
837 * field is set to HOT_SPOT_CMD.
839 if (mvmvif
&& id
< SESSION_PROTECT_CONF_MAX_ID
) {
840 /* Session protection is still ongoing. Cancel it */
841 iwl_mvm_cancel_session_protection(mvm
, vif
, id
,
843 if (iftype
== NL80211_IFTYPE_P2P_DEVICE
) {
844 iwl_mvm_roc_finished(mvm
);
849 /* It is possible that by the time we try to remove it, the
850 * time event has already ended and removed. In such a case
851 * there is no need to send a removal command.
854 IWL_DEBUG_TE(mvm
, "TE 0x%x has already ended\n", *uid
);
863 * Explicit request to remove a aux roc time event. The removal of a time
864 * event needs to be synchronized with the flow of a time event's end
865 * notification, which also removes the time event from the op mode
868 static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm
*mvm
,
869 struct iwl_mvm_vif
*mvmvif
,
870 struct iwl_mvm_time_event_data
*te_data
)
872 struct iwl_hs20_roc_req aux_cmd
= {};
873 u16 len
= sizeof(aux_cmd
) - iwl_mvm_chan_info_padding(mvm
);
878 if (!__iwl_mvm_remove_time_event(mvm
, te_data
, &uid
))
881 aux_cmd
.event_unique_id
= cpu_to_le32(uid
);
882 aux_cmd
.action
= cpu_to_le32(FW_CTXT_ACTION_REMOVE
);
883 aux_cmd
.id_and_color
=
884 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif
->id
, mvmvif
->color
));
885 IWL_DEBUG_TE(mvm
, "Removing BSS AUX ROC TE 0x%x\n",
886 le32_to_cpu(aux_cmd
.event_unique_id
));
887 ret
= iwl_mvm_send_cmd_pdu(mvm
, HOT_SPOT_CMD
, 0,
895 * Explicit request to remove a time event. The removal of a time event needs to
896 * be synchronized with the flow of a time event's end notification, which also
897 * removes the time event from the op mode data structures.
899 void iwl_mvm_remove_time_event(struct iwl_mvm
*mvm
,
900 struct iwl_mvm_vif
*mvmvif
,
901 struct iwl_mvm_time_event_data
*te_data
)
903 struct iwl_time_event_cmd time_cmd
= {};
907 if (!__iwl_mvm_remove_time_event(mvm
, te_data
, &uid
))
910 /* When we remove a TE, the UID is to be set in the id field */
911 time_cmd
.id
= cpu_to_le32(uid
);
912 time_cmd
.action
= cpu_to_le32(FW_CTXT_ACTION_REMOVE
);
913 time_cmd
.id_and_color
=
914 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif
->id
, mvmvif
->color
));
916 IWL_DEBUG_TE(mvm
, "Removing TE 0x%x\n", le32_to_cpu(time_cmd
.id
));
917 ret
= iwl_mvm_send_cmd_pdu(mvm
, TIME_EVENT_CMD
, 0,
918 sizeof(time_cmd
), &time_cmd
);
920 IWL_ERR(mvm
, "Couldn't remove the time event\n");
923 void iwl_mvm_stop_session_protection(struct iwl_mvm
*mvm
,
924 struct ieee80211_vif
*vif
)
926 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
927 struct iwl_mvm_time_event_data
*te_data
= &mvmvif
->time_event_data
;
930 lockdep_assert_held(&mvm
->mutex
);
932 spin_lock_bh(&mvm
->time_event_lock
);
934 spin_unlock_bh(&mvm
->time_event_lock
);
936 if (fw_has_capa(&mvm
->fw
->ucode_capa
,
937 IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD
)) {
938 if (id
!= SESSION_PROTECT_CONF_ASSOC
) {
940 "don't remove session protection id=%u\n",
944 } else if (id
!= TE_BSS_STA_AGGRESSIVE_ASSOC
) {
946 "don't remove TE with id=%u (not session protection)\n",
951 iwl_mvm_remove_time_event(mvm
, mvmvif
, te_data
);
954 void iwl_mvm_rx_session_protect_notif(struct iwl_mvm
*mvm
,
955 struct iwl_rx_cmd_buffer
*rxb
)
957 struct iwl_rx_packet
*pkt
= rxb_addr(rxb
);
958 struct iwl_mvm_session_prot_notif
*notif
= (void *)pkt
->data
;
960 iwl_fw_lookup_notif_ver(mvm
->fw
, MAC_CONF_GROUP
,
961 SESSION_PROTECTION_NOTIF
, 2);
962 int id
= le32_to_cpu(notif
->mac_link_id
);
963 struct ieee80211_vif
*vif
;
964 struct iwl_mvm_vif
*mvmvif
;
965 unsigned int notif_link_id
;
970 vif
= iwl_mvm_rcu_dereference_vif_id(mvm
, id
, true);
972 struct ieee80211_bss_conf
*link_conf
=
973 iwl_mvm_rcu_fw_link_id_to_link_conf(mvm
, id
, true);
978 notif_link_id
= link_conf
->link_id
;
979 vif
= link_conf
->vif
;
985 mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
987 if (WARN(ver
> 2 && mvmvif
->time_event_data
.link_id
>= 0 &&
988 mvmvif
->time_event_data
.link_id
!= notif_link_id
,
989 "SESSION_PROTECTION_NOTIF was received for link %u, while the current time event is on link %u\n",
990 notif_link_id
, mvmvif
->time_event_data
.link_id
))
993 /* The vif is not a P2P_DEVICE, maintain its time_event_data */
994 if (vif
->type
!= NL80211_IFTYPE_P2P_DEVICE
) {
995 struct iwl_mvm_time_event_data
*te_data
=
996 &mvmvif
->time_event_data
;
998 if (!le32_to_cpu(notif
->status
)) {
999 iwl_mvm_te_check_disconnect(mvm
, vif
,
1000 "Session protection failure");
1001 spin_lock_bh(&mvm
->time_event_lock
);
1002 iwl_mvm_te_clear_data(mvm
, te_data
);
1003 spin_unlock_bh(&mvm
->time_event_lock
);
1006 if (le32_to_cpu(notif
->start
)) {
1007 spin_lock_bh(&mvm
->time_event_lock
);
1008 te_data
->running
= le32_to_cpu(notif
->start
);
1009 te_data
->end_jiffies
=
1010 TU_TO_EXP_TIME(te_data
->duration
);
1011 spin_unlock_bh(&mvm
->time_event_lock
);
1014 * By now, we should have finished association
1015 * and know the dtim period.
1017 iwl_mvm_te_check_disconnect(mvm
, vif
,
1019 "Not associated and the session protection is over already..." :
1020 "No beacon heard and the session protection is over already...");
1021 spin_lock_bh(&mvm
->time_event_lock
);
1022 iwl_mvm_te_clear_data(mvm
, te_data
);
1023 spin_unlock_bh(&mvm
->time_event_lock
);
1029 if (!le32_to_cpu(notif
->status
) || !le32_to_cpu(notif
->start
)) {
1030 /* End TE, notify mac80211 */
1031 mvmvif
->time_event_data
.id
= SESSION_PROTECT_CONF_MAX_ID
;
1032 mvmvif
->time_event_data
.link_id
= -1;
1033 iwl_mvm_roc_finished(mvm
);
1034 ieee80211_remain_on_channel_expired(mvm
->hw
);
1035 } else if (le32_to_cpu(notif
->start
)) {
1036 if (WARN_ON(mvmvif
->time_event_data
.id
!=
1037 le32_to_cpu(notif
->conf_id
)))
1039 set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING
, &mvm
->status
);
1040 ieee80211_ready_on_channel(mvm
->hw
); /* Start TE */
1047 #define AUX_ROC_MIN_DURATION MSEC_TO_TU(100)
1048 #define AUX_ROC_MIN_DELAY MSEC_TO_TU(200)
1049 #define AUX_ROC_MAX_DELAY MSEC_TO_TU(600)
1050 #define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20)
1051 #define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10)
1053 void iwl_mvm_roc_duration_and_delay(struct ieee80211_vif
*vif
,
1058 struct ieee80211_bss_conf
*link_conf
;
1059 unsigned int link_id
;
1060 u32 dtim_interval
= 0;
1062 *delay
= AUX_ROC_MIN_DELAY
;
1063 *duration_tu
= MSEC_TO_TU(duration_ms
);
1066 for_each_vif_active_link(vif
, link_conf
, link_id
) {
1068 max_t(u32
, dtim_interval
,
1069 link_conf
->dtim_period
* link_conf
->beacon_int
);
1074 * If we are associated we want the delay time to be at least one
1075 * dtim interval so that the FW can wait until after the DTIM and
1076 * then start the time event, this will potentially allow us to
1077 * remain off-channel for the max duration.
1078 * Since we want to use almost a whole dtim interval we would also
1079 * like the delay to be for 2-3 dtim intervals, in case there are
1080 * other time events with higher priority.
1081 * dtim_interval should never be 0, it can be 1 if we don't know it
1082 * (we haven't heard any beacon yet).
1084 if (vif
->cfg
.assoc
&& !WARN_ON(!dtim_interval
)) {
1085 *delay
= min_t(u32
, dtim_interval
* 3, AUX_ROC_MAX_DELAY
);
1086 /* We cannot remain off-channel longer than the DTIM interval */
1087 if (dtim_interval
<= *duration_tu
) {
1088 *duration_tu
= dtim_interval
- AUX_ROC_SAFETY_BUFFER
;
1089 if (*duration_tu
<= AUX_ROC_MIN_DURATION
)
1090 *duration_tu
= dtim_interval
-
1091 AUX_ROC_MIN_SAFETY_BUFFER
;
1096 int iwl_mvm_roc_add_cmd(struct iwl_mvm
*mvm
,
1097 struct ieee80211_channel
*channel
,
1098 struct ieee80211_vif
*vif
,
1099 int duration
, enum iwl_roc_activity activity
)
1102 u32 duration_tu
, delay
;
1103 struct iwl_roc_req roc_req
= {
1104 .action
= cpu_to_le32(FW_CTXT_ACTION_ADD
),
1105 .activity
= cpu_to_le32(activity
),
1106 .sta_id
= cpu_to_le32(mvm
->aux_sta
.sta_id
),
1108 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1110 lockdep_assert_held(&mvm
->mutex
);
1112 if (WARN_ON(mvmvif
->roc_activity
!= ROC_NUM_ACTIVITIES
))
1115 /* Set the channel info data */
1116 iwl_mvm_set_chan_info(mvm
, &roc_req
.channel_info
,
1118 iwl_mvm_phy_band_from_nl80211(channel
->band
),
1119 IWL_PHY_CHANNEL_MODE20
, 0);
1121 iwl_mvm_roc_duration_and_delay(vif
, duration
, &duration_tu
,
1123 roc_req
.duration
= cpu_to_le32(duration_tu
);
1124 roc_req
.max_delay
= cpu_to_le32(delay
);
1127 "\t(requested = %ums, max_delay = %ums)\n",
1130 "Requesting to remain on channel %u for %utu. activity %u\n",
1131 channel
->hw_value
, duration_tu
, activity
);
1133 /* Set the node address */
1134 memcpy(roc_req
.node_addr
, vif
->addr
, ETH_ALEN
);
1136 res
= iwl_mvm_send_cmd_pdu(mvm
, WIDE_ID(MAC_CONF_GROUP
, ROC_CMD
),
1137 0, sizeof(roc_req
), &roc_req
);
1139 mvmvif
->roc_activity
= activity
;
1145 iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm
*mvm
,
1146 struct ieee80211_vif
*vif
,
1148 enum ieee80211_roc_type type
)
1150 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1151 struct iwl_mvm_session_prot_cmd cmd
= {
1153 cpu_to_le32(iwl_mvm_get_session_prot_id(mvm
, vif
, 0)),
1154 .action
= cpu_to_le32(FW_CTXT_ACTION_ADD
),
1155 .duration_tu
= cpu_to_le32(MSEC_TO_TU(duration
)),
1158 lockdep_assert_held(&mvm
->mutex
);
1160 /* The time_event_data.id field is reused to save session
1161 * protection's configuration.
1164 mvmvif
->time_event_data
.link_id
= 0;
1167 case IEEE80211_ROC_TYPE_NORMAL
:
1168 mvmvif
->time_event_data
.id
=
1169 SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV
;
1171 case IEEE80211_ROC_TYPE_MGMT_TX
:
1172 mvmvif
->time_event_data
.id
=
1173 SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION
;
1176 WARN_ONCE(1, "Got an invalid ROC type\n");
1180 cmd
.conf_id
= cpu_to_le32(mvmvif
->time_event_data
.id
);
1181 return iwl_mvm_send_cmd_pdu(mvm
,
1182 WIDE_ID(MAC_CONF_GROUP
, SESSION_PROTECTION_CMD
),
1183 0, sizeof(cmd
), &cmd
);
1186 int iwl_mvm_start_p2p_roc(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
1187 int duration
, enum ieee80211_roc_type type
)
1189 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1190 struct iwl_mvm_time_event_data
*te_data
= &mvmvif
->time_event_data
;
1191 struct iwl_time_event_cmd time_cmd
= {};
1193 lockdep_assert_held(&mvm
->mutex
);
1194 if (te_data
->running
) {
1195 IWL_WARN(mvm
, "P2P_DEVICE remain on channel already running\n");
1199 if (fw_has_capa(&mvm
->fw
->ucode_capa
,
1200 IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD
))
1201 return iwl_mvm_start_p2p_roc_session_protection(mvm
, vif
,
1205 time_cmd
.action
= cpu_to_le32(FW_CTXT_ACTION_ADD
);
1206 time_cmd
.id_and_color
=
1207 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif
->id
, mvmvif
->color
));
1210 case IEEE80211_ROC_TYPE_NORMAL
:
1211 time_cmd
.id
= cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL
);
1213 case IEEE80211_ROC_TYPE_MGMT_TX
:
1214 time_cmd
.id
= cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX
);
1217 WARN_ONCE(1, "Got an invalid ROC type\n");
1221 time_cmd
.apply_time
= cpu_to_le32(0);
1222 time_cmd
.interval
= cpu_to_le32(1);
1225 * The P2P Device TEs can have lower priority than other events
1226 * that are being scheduled by the driver/fw, and thus it might not be
1227 * scheduled. To improve the chances of it being scheduled, allow them
1228 * to be fragmented, and in addition allow them to be delayed.
1230 time_cmd
.max_frags
= min(MSEC_TO_TU(duration
)/50, TE_V2_FRAG_ENDLESS
);
1231 time_cmd
.max_delay
= cpu_to_le32(MSEC_TO_TU(duration
/2));
1232 time_cmd
.duration
= cpu_to_le32(MSEC_TO_TU(duration
));
1233 time_cmd
.repeat
= 1;
1234 time_cmd
.policy
= cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START
|
1235 TE_V2_NOTIF_HOST_EVENT_END
|
1236 TE_V2_START_IMMEDIATELY
);
1238 return iwl_mvm_time_event_send_add(mvm
, vif
, te_data
, &time_cmd
);
1241 static struct iwl_mvm_time_event_data
*iwl_mvm_get_roc_te(struct iwl_mvm
*mvm
)
1243 struct iwl_mvm_time_event_data
*te_data
;
1245 lockdep_assert_held(&mvm
->mutex
);
1247 spin_lock_bh(&mvm
->time_event_lock
);
1250 * Iterate over the list of time events and find the time event that is
1251 * associated with a P2P_DEVICE interface.
1252 * This assumes that a P2P_DEVICE interface can have only a single time
1253 * event at any given time and this time event coresponds to a ROC
1256 list_for_each_entry(te_data
, &mvm
->time_event_list
, list
) {
1257 if (te_data
->vif
->type
== NL80211_IFTYPE_P2P_DEVICE
)
1261 /* There can only be at most one AUX ROC time event, we just use the
1262 * list to simplify/unify code. Remove it if it exists.
1264 te_data
= list_first_entry_or_null(&mvm
->aux_roc_te_list
,
1265 struct iwl_mvm_time_event_data
,
1268 spin_unlock_bh(&mvm
->time_event_lock
);
1272 void iwl_mvm_cleanup_roc_te(struct iwl_mvm
*mvm
)
1274 struct iwl_mvm_time_event_data
*te_data
;
1277 te_data
= iwl_mvm_get_roc_te(mvm
);
1279 __iwl_mvm_remove_time_event(mvm
, te_data
, &uid
);
1282 void iwl_mvm_stop_roc(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
)
1284 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1285 struct iwl_mvm_time_event_data
*te_data
;
1286 bool p2p_aux
= iwl_mvm_has_p2p_over_aux(mvm
);
1287 u8 roc_ver
= iwl_fw_lookup_cmd_ver(mvm
->fw
,
1288 WIDE_ID(MAC_CONF_GROUP
, ROC_CMD
), 0);
1289 int iftype
= vif
->type
;
1291 mutex_lock(&mvm
->mutex
);
1293 if (p2p_aux
|| (roc_ver
>= 3 && iftype
!= NL80211_IFTYPE_P2P_DEVICE
)) {
1294 if (mvmvif
->roc_activity
< ROC_NUM_ACTIVITIES
) {
1295 iwl_mvm_roc_rm_cmd(mvm
, mvmvif
->roc_activity
);
1296 mvmvif
->roc_activity
= ROC_NUM_ACTIVITIES
;
1299 } else if (fw_has_capa(&mvm
->fw
->ucode_capa
,
1300 IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD
)) {
1301 te_data
= &mvmvif
->time_event_data
;
1303 if (iftype
== NL80211_IFTYPE_P2P_DEVICE
) {
1304 if (te_data
->id
>= SESSION_PROTECT_CONF_MAX_ID
) {
1306 "No remain on channel event\n");
1307 mutex_unlock(&mvm
->mutex
);
1310 iwl_mvm_cancel_session_protection(mvm
, vif
,
1314 iwl_mvm_remove_aux_roc_te(mvm
, mvmvif
,
1315 &mvmvif
->hs_time_event_data
);
1320 te_data
= iwl_mvm_get_roc_te(mvm
);
1322 IWL_WARN(mvm
, "No remain on channel event\n");
1323 mutex_unlock(&mvm
->mutex
);
1327 mvmvif
= iwl_mvm_vif_from_mac80211(te_data
->vif
);
1328 iftype
= te_data
->vif
->type
;
1329 if (iftype
== NL80211_IFTYPE_P2P_DEVICE
)
1330 iwl_mvm_remove_time_event(mvm
, mvmvif
, te_data
);
1332 iwl_mvm_remove_aux_roc_te(mvm
, mvmvif
, te_data
);
1336 * In case we get here before the ROC event started,
1337 * (so the status bit isn't set) set it here so iwl_mvm_cleanup_roc will
1338 * cleanup things properly
1340 if (p2p_aux
|| iftype
!= NL80211_IFTYPE_P2P_DEVICE
)
1341 set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING
, &mvm
->status
);
1343 set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING
, &mvm
->status
);
1345 /* Mutex is released inside this function */
1346 iwl_mvm_cleanup_roc(mvm
);
1349 void iwl_mvm_remove_csa_period(struct iwl_mvm
*mvm
,
1350 struct ieee80211_vif
*vif
)
1352 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1353 struct iwl_mvm_time_event_data
*te_data
= &mvmvif
->time_event_data
;
1356 lockdep_assert_held(&mvm
->mutex
);
1358 spin_lock_bh(&mvm
->time_event_lock
);
1360 spin_unlock_bh(&mvm
->time_event_lock
);
1362 if (id
!= TE_CHANNEL_SWITCH_PERIOD
)
1365 iwl_mvm_remove_time_event(mvm
, mvmvif
, te_data
);
1368 int iwl_mvm_schedule_csa_period(struct iwl_mvm
*mvm
,
1369 struct ieee80211_vif
*vif
,
1370 u32 duration
, u32 apply_time
)
1372 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1373 struct iwl_mvm_time_event_data
*te_data
= &mvmvif
->time_event_data
;
1374 struct iwl_time_event_cmd time_cmd
= {};
1376 lockdep_assert_held(&mvm
->mutex
);
1378 if (te_data
->running
) {
1381 spin_lock_bh(&mvm
->time_event_lock
);
1383 spin_unlock_bh(&mvm
->time_event_lock
);
1385 if (id
== TE_CHANNEL_SWITCH_PERIOD
) {
1386 IWL_DEBUG_TE(mvm
, "CS period is already scheduled\n");
1391 * Remove the session protection time event to allow the
1392 * channel switch. If we got here, we just heard a beacon so
1393 * the session protection is not needed anymore anyway.
1395 iwl_mvm_remove_time_event(mvm
, mvmvif
, te_data
);
1398 time_cmd
.action
= cpu_to_le32(FW_CTXT_ACTION_ADD
);
1399 time_cmd
.id_and_color
=
1400 cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif
->id
, mvmvif
->color
));
1401 time_cmd
.id
= cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD
);
1402 time_cmd
.apply_time
= cpu_to_le32(apply_time
);
1403 time_cmd
.max_frags
= TE_V2_FRAG_NONE
;
1404 time_cmd
.duration
= cpu_to_le32(duration
);
1405 time_cmd
.repeat
= 1;
1406 time_cmd
.interval
= cpu_to_le32(1);
1407 time_cmd
.policy
= cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START
|
1410 time_cmd
.policy
|= cpu_to_le16(TE_V2_START_IMMEDIATELY
);
1412 return iwl_mvm_time_event_send_add(mvm
, vif
, te_data
, &time_cmd
);
1415 static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data
*notif_wait
,
1416 struct iwl_rx_packet
*pkt
, void *data
)
1418 struct iwl_mvm
*mvm
=
1419 container_of(notif_wait
, struct iwl_mvm
, notif_wait
);
1420 struct iwl_mvm_session_prot_notif
*resp
;
1421 int resp_len
= iwl_rx_packet_payload_len(pkt
);
1423 if (WARN_ON(pkt
->hdr
.cmd
!= SESSION_PROTECTION_NOTIF
||
1424 pkt
->hdr
.group_id
!= MAC_CONF_GROUP
))
1427 if (WARN_ON_ONCE(resp_len
!= sizeof(*resp
))) {
1428 IWL_ERR(mvm
, "Invalid SESSION_PROTECTION_NOTIF response\n");
1432 resp
= (void *)pkt
->data
;
1436 "TIME_EVENT_NOTIFICATION received but not executed\n");
1441 void iwl_mvm_schedule_session_protection(struct iwl_mvm
*mvm
,
1442 struct ieee80211_vif
*vif
,
1443 u32 duration
, u32 min_duration
,
1444 bool wait_for_notif
,
1445 unsigned int link_id
)
1447 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
1448 struct iwl_mvm_time_event_data
*te_data
= &mvmvif
->time_event_data
;
1449 const u16 notif
[] = { WIDE_ID(MAC_CONF_GROUP
, SESSION_PROTECTION_NOTIF
) };
1450 struct iwl_notification_wait wait_notif
;
1451 int mac_link_id
= iwl_mvm_get_session_prot_id(mvm
, vif
, (s8
)link_id
);
1452 struct iwl_mvm_session_prot_cmd cmd
= {
1453 .id_and_color
= cpu_to_le32(mac_link_id
),
1454 .action
= cpu_to_le32(FW_CTXT_ACTION_ADD
),
1455 .conf_id
= cpu_to_le32(SESSION_PROTECT_CONF_ASSOC
),
1456 .duration_tu
= cpu_to_le32(MSEC_TO_TU(duration
)),
1459 if (mac_link_id
< 0)
1462 lockdep_assert_held(&mvm
->mutex
);
1464 spin_lock_bh(&mvm
->time_event_lock
);
1465 if (te_data
->running
&& te_data
->link_id
== link_id
&&
1466 time_after(te_data
->end_jiffies
, TU_TO_EXP_TIME(min_duration
))) {
1467 IWL_DEBUG_TE(mvm
, "We have enough time in the current TE: %u\n",
1468 jiffies_to_msecs(te_data
->end_jiffies
- jiffies
));
1469 spin_unlock_bh(&mvm
->time_event_lock
);
1474 iwl_mvm_te_clear_data(mvm
, te_data
);
1476 * The time_event_data.id field is reused to save session
1477 * protection's configuration.
1479 te_data
->id
= le32_to_cpu(cmd
.conf_id
);
1480 te_data
->duration
= le32_to_cpu(cmd
.duration_tu
);
1482 te_data
->link_id
= link_id
;
1483 spin_unlock_bh(&mvm
->time_event_lock
);
1485 IWL_DEBUG_TE(mvm
, "Add new session protection, duration %d TU\n",
1486 le32_to_cpu(cmd
.duration_tu
));
1488 if (!wait_for_notif
) {
1489 if (iwl_mvm_send_cmd_pdu(mvm
,
1490 WIDE_ID(MAC_CONF_GROUP
, SESSION_PROTECTION_CMD
),
1491 0, sizeof(cmd
), &cmd
)) {
1498 iwl_init_notification_wait(&mvm
->notif_wait
, &wait_notif
,
1499 notif
, ARRAY_SIZE(notif
),
1500 iwl_mvm_session_prot_notif
, NULL
);
1502 if (iwl_mvm_send_cmd_pdu(mvm
,
1503 WIDE_ID(MAC_CONF_GROUP
, SESSION_PROTECTION_CMD
),
1504 0, sizeof(cmd
), &cmd
)) {
1505 iwl_remove_notification(&mvm
->notif_wait
, &wait_notif
);
1507 } else if (iwl_wait_notification(&mvm
->notif_wait
, &wait_notif
,
1508 TU_TO_JIFFIES(100))) {
1510 "Failed to protect session until session protection\n");
1516 "Couldn't send the SESSION_PROTECTION_CMD\n");
1517 spin_lock_bh(&mvm
->time_event_lock
);
1518 iwl_mvm_te_clear_data(mvm
, te_data
);
1519 spin_unlock_bh(&mvm
->time_event_lock
);