1521 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2012-2014, 2018-2024 Intel Corporation4 * Copyright (C) 2013-2015 Intel Mobile Communications GmbH5 * Copyright (C) 2017 Intel Deutschland GmbH6 */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 to20 * the FW's action scan priority.21 */22#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE23#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC24 25void 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 always37 * initialized38 */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 48static 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 particular59 * in the case that the time event actually completed in the firmware.60 *61 * Also flush the offchannel queue -- this is called when the time62 * event finishes or is canceled, so that frames queued for it63 * won't get stuck on the queue and be transmitted in the next64 * 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 bit73 * can only be set when we had a P2P-Device VIF, and we have a74 * flush of this work in iwl_mvm_prepare_mac_removal() so it's75 * 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 adding96 * a PHY context has timing overheads. Leaving it97 * configured in FW would be useful in case the next ROC98 * 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 particular108 * in the case that the time event actually completed in the firmware109 * (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 only118 * in cases of dedicated tx queue and need to be removed in end119 * of use. For the even newer mld api, use the appropriate120 * 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 133void 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 142static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)143{144 /*145 * Of course, our status bit is just as racy as mac80211, so in146 * addition, fire off the work struct which will drop all frames147 * from the hardware queues that made it through the race. First148 * it will of course synchronize the TX path to make sure that149 * any *new* TX will be rejected.150 */151 schedule_work(&mvm->roc_done_wk);152}153 154static 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 to168 * transmit on the current channel.169 * So we just do nothing here and the switch170 * 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 185out_unlock:186 rcu_read_unlock();187}188 189static 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 from218 * 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 231static void232iwl_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 268static 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 component309 * @te_data: the time event data310 * @notif: the notification data corresponding the time event data.311 */312static 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 events326 * that it fails to schedule. This is indicated in the status field of327 * the notification. This happens in cases that the scheduler cannot328 * find a schedule that can handle the event (for example requesting a329 * P2P Device discoveribility, while there are other higher priority330 * 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 else338 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 disconnect361 * if the time event is already done. Beacons can362 * 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 association372 * 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 399struct iwl_mvm_rx_roc_iterator_data {400 u32 activity;401 bool end_activity;402 bool found;403};404 405static 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 418void 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 canceled438 * 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 event454 */455static 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 notifications502 */503void 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 }523unlock:524 spin_unlock_bh(&mvm->time_event_lock);525}526 527static 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 559static 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 588static 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 the615 * command response and doesn't wait for anything, in order616 * to be able to process the response and get the UID inside617 * the RX path. Using CMD_WANT_SKB doesn't work because it618 * stores the buffer and then wakes up this thread, by which619 * 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 649void 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 time675 * cancel the current TE and issue a new one676 * Of course it would be better to remove the old one only677 * when the new one is added, but we don't care if we are off678 * channel for a bit. All we need to do, is not to return679 * 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 use708 * right after we send the time event709 */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 */726static 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 749static 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_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),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 772static 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 int ret;779 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);783 if (ret)784 IWL_ERR(mvm, "Couldn't send the ROC_CMD: %d\n", ret);785}786 787static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,788 struct iwl_mvm_time_event_data *te_data,789 u32 *uid)790{791 u32 id;792 struct ieee80211_vif *vif = te_data->vif;793 struct iwl_mvm_vif *mvmvif;794 enum nl80211_iftype iftype;795 s8 link_id;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);799 800 if (!vif)801 return false;802 803 mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);804 iftype = te_data->vif->type;805 806 /*807 * It is possible that by the time we got to this point the time808 * event was already removed.809 */810 spin_lock_bh(&mvm->time_event_lock);811 812 /* Save time event uid before clearing its data */813 *uid = te_data->uid;814 id = te_data->id;815 link_id = te_data->link_id;816 817 /*818 * The clear_data function handles time events that were already removed819 */820 iwl_mvm_te_clear_data(mvm, te_data);821 spin_unlock_bh(&mvm->time_event_lock);822 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);829 }830 return false;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 field835 * is reused to save session protection's configuration.836 * For AUX ROC, HOT_SPOT_CMD is used and the te_data->id837 * field is set to HOT_SPOT_CMD.838 */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,842 link_id);843 if (iftype == NL80211_IFTYPE_P2P_DEVICE) {844 iwl_mvm_roc_finished(mvm);845 }846 }847 return false;848 } else {849 /* It is possible that by the time we try to remove it, the850 * time event has already ended and removed. In such a case851 * there is no need to send a removal command.852 */853 if (id == TE_MAX) {854 IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);855 return false;856 }857 }858 859 return true;860}861 862/*863 * Explicit request to remove a aux roc time event. The removal of a time864 * event needs to be synchronized with the flow of a time event's end865 * notification, which also removes the time event from the op mode866 * data structures.867 */868static 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)871{872 struct iwl_hs20_roc_req aux_cmd = {};873 u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm);874 875 u32 uid;876 int ret;877 878 if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))879 return;880 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,888 len, &aux_cmd);889 890 if (WARN_ON(ret))891 return;892}893 894/*895 * Explicit request to remove a time event. The removal of a time event needs to896 * be synchronized with the flow of a time event's end notification, which also897 * removes the time event from the op mode data structures.898 */899void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,900 struct iwl_mvm_vif *mvmvif,901 struct iwl_mvm_time_event_data *te_data)902{903 struct iwl_time_event_cmd time_cmd = {};904 u32 uid;905 int ret;906 907 if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))908 return;909 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));915 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);919 if (ret)920 IWL_ERR(mvm, "Couldn't remove the time event\n");921}922 923void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,924 struct ieee80211_vif *vif)925{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;928 u32 id;929 930 lockdep_assert_held(&mvm->mutex);931 932 spin_lock_bh(&mvm->time_event_lock);933 id = te_data->id;934 spin_unlock_bh(&mvm->time_event_lock);935 936 if (fw_has_capa(&mvm->fw->ucode_capa,937 IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {938 if (id != SESSION_PROTECT_CONF_ASSOC) {939 IWL_DEBUG_TE(mvm,940 "don't remove session protection id=%u\n",941 id);942 return;943 }944 } else if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) {945 IWL_DEBUG_TE(mvm,946 "don't remove TE with id=%u (not session protection)\n",947 id);948 return;949 }950 951 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);952}953 954void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,955 struct iwl_rx_cmd_buffer *rxb)956{957 struct iwl_rx_packet *pkt = rxb_addr(rxb);958 struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data;959 unsigned int ver =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;966 967 rcu_read_lock();968 969 if (ver <= 2) {970 vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true);971 } else {972 struct ieee80211_bss_conf *link_conf =973 iwl_mvm_rcu_fw_link_id_to_link_conf(mvm, id, true);974 975 if (!link_conf)976 goto out_unlock;977 978 notif_link_id = link_conf->link_id;979 vif = link_conf->vif;980 }981 982 if (!vif)983 goto out_unlock;984 985 mvmvif = iwl_mvm_vif_from_mac80211(vif);986 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))991 goto out_unlock;992 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;997 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);1004 }1005 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);1012 } else {1013 /*1014 * By now, we should have finished association1015 * and know the dtim period.1016 */1017 iwl_mvm_te_check_disconnect(mvm, vif,1018 !vif->cfg.assoc ?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);1024 }1025 1026 goto out_unlock;1027 }1028 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)))1038 goto out_unlock;1039 set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status);1040 ieee80211_ready_on_channel(mvm->hw); /* Start TE */1041 }1042 1043 out_unlock:1044 rcu_read_unlock();1045}1046 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)1052 1053void iwl_mvm_roc_duration_and_delay(struct ieee80211_vif *vif,1054 u32 duration_ms,1055 u32 *duration_tu,1056 u32 *delay)1057{1058 struct ieee80211_bss_conf *link_conf;1059 unsigned int link_id;1060 u32 dtim_interval = 0;1061 1062 *delay = AUX_ROC_MIN_DELAY;1063 *duration_tu = MSEC_TO_TU(duration_ms);1064 1065 rcu_read_lock();1066 for_each_vif_active_link(vif, link_conf, link_id) {1067 dtim_interval =1068 max_t(u32, dtim_interval,1069 link_conf->dtim_period * link_conf->beacon_int);1070 }1071 rcu_read_unlock();1072 1073 /*1074 * If we are associated we want the delay time to be at least one1075 * dtim interval so that the FW can wait until after the DTIM and1076 * then start the time event, this will potentially allow us to1077 * remain off-channel for the max duration.1078 * Since we want to use almost a whole dtim interval we would also1079 * like the delay to be for 2-3 dtim intervals, in case there are1080 * other time events with higher priority.1081 * dtim_interval should never be 0, it can be 1 if we don't know it1082 * (we haven't heard any beacon yet).1083 */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;1092 }1093 }1094}1095 1096int 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)1100{1101 int res;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),1107 };1108 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);1109 1110 lockdep_assert_held(&mvm->mutex);1111 1112 if (WARN_ON(mvmvif->roc_activity != ROC_NUM_ACTIVITIES))1113 return -EBUSY;1114 1115 /* Set the channel info data */1116 iwl_mvm_set_chan_info(mvm, &roc_req.channel_info,1117 channel->hw_value,1118 iwl_mvm_phy_band_from_nl80211(channel->band),1119 IWL_PHY_CHANNEL_MODE20, 0);1120 1121 iwl_mvm_roc_duration_and_delay(vif, duration, &duration_tu,1122 &delay);1123 roc_req.duration = cpu_to_le32(duration_tu);1124 roc_req.max_delay = cpu_to_le32(delay);1125 1126 IWL_DEBUG_TE(mvm,1127 "\t(requested = %ums, max_delay = %ums)\n",1128 duration, delay);1129 IWL_DEBUG_TE(mvm,1130 "Requesting to remain on channel %u for %utu. activity %u\n",1131 channel->hw_value, duration_tu, activity);1132 1133 /* Set the node address */1134 memcpy(roc_req.node_addr, vif->addr, ETH_ALEN);1135 1136 res = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),1137 0, sizeof(roc_req), &roc_req);1138 if (!res)1139 mvmvif->roc_activity = activity;1140 1141 return res;1142}1143 1144static int1145iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm,1146 struct ieee80211_vif *vif,1147 int duration,1148 enum ieee80211_roc_type type)1149{1150 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);1151 struct iwl_mvm_session_prot_cmd cmd = {1152 .id_and_color =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)),1156 };1157 1158 lockdep_assert_held(&mvm->mutex);1159 1160 /* The time_event_data.id field is reused to save session1161 * protection's configuration.1162 */1163 1164 mvmvif->time_event_data.link_id = 0;1165 1166 switch (type) {1167 case IEEE80211_ROC_TYPE_NORMAL:1168 mvmvif->time_event_data.id =1169 SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV;1170 break;1171 case IEEE80211_ROC_TYPE_MGMT_TX:1172 mvmvif->time_event_data.id =1173 SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION;1174 break;1175 default:1176 WARN_ONCE(1, "Got an invalid ROC type\n");1177 return -EINVAL;1178 }1179 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);1184}1185 1186int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,1187 int duration, enum ieee80211_roc_type type)1188{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 = {};1192 1193 lockdep_assert_held(&mvm->mutex);1194 if (te_data->running) {1195 IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");1196 return -EBUSY;1197 }1198 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,1202 duration,1203 type);1204 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));1208 1209 switch (type) {1210 case IEEE80211_ROC_TYPE_NORMAL:1211 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL);1212 break;1213 case IEEE80211_ROC_TYPE_MGMT_TX:1214 time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX);1215 break;1216 default:1217 WARN_ONCE(1, "Got an invalid ROC type\n");1218 return -EINVAL;1219 }1220 1221 time_cmd.apply_time = cpu_to_le32(0);1222 time_cmd.interval = cpu_to_le32(1);1223 1224 /*1225 * The P2P Device TEs can have lower priority than other events1226 * that are being scheduled by the driver/fw, and thus it might not be1227 * scheduled. To improve the chances of it being scheduled, allow them1228 * to be fragmented, and in addition allow them to be delayed.1229 */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);1237 1238 return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);1239}1240 1241static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm)1242{1243 struct iwl_mvm_time_event_data *te_data;1244 1245 lockdep_assert_held(&mvm->mutex);1246 1247 spin_lock_bh(&mvm->time_event_lock);1248 1249 /*1250 * Iterate over the list of time events and find the time event that is1251 * associated with a P2P_DEVICE interface.1252 * This assumes that a P2P_DEVICE interface can have only a single time1253 * event at any given time and this time event coresponds to a ROC1254 * request1255 */1256 list_for_each_entry(te_data, &mvm->time_event_list, list) {1257 if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)1258 goto out;1259 }1260 1261 /* There can only be at most one AUX ROC time event, we just use the1262 * list to simplify/unify code. Remove it if it exists.1263 */1264 te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,1265 struct iwl_mvm_time_event_data,1266 list);1267out:1268 spin_unlock_bh(&mvm->time_event_lock);1269 return te_data;1270}1271 1272void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)1273{1274 struct iwl_mvm_time_event_data *te_data;1275 u32 uid;1276 1277 te_data = iwl_mvm_get_roc_te(mvm);1278 if (te_data)1279 __iwl_mvm_remove_time_event(mvm, te_data, &uid);1280}1281 1282void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)1283{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;1290 1291 mutex_lock(&mvm->mutex);1292 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;1297 }1298 goto cleanup_roc;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;1302 1303 if (iftype == NL80211_IFTYPE_P2P_DEVICE) {1304 if (te_data->id >= SESSION_PROTECT_CONF_MAX_ID) {1305 IWL_DEBUG_TE(mvm,1306 "No remain on channel event\n");1307 mutex_unlock(&mvm->mutex);1308 return;1309 }1310 iwl_mvm_cancel_session_protection(mvm, vif,1311 te_data->id,1312 te_data->link_id);1313 } else {1314 iwl_mvm_remove_aux_roc_te(mvm, mvmvif,1315 &mvmvif->hs_time_event_data);1316 }1317 goto cleanup_roc;1318 }1319 1320 te_data = iwl_mvm_get_roc_te(mvm);1321 if (!te_data) {1322 IWL_WARN(mvm, "No remain on channel event\n");1323 mutex_unlock(&mvm->mutex);1324 return;1325 }1326 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);1331 else1332 iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);1333 1334cleanup_roc:1335 /*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 will1338 * cleanup things properly1339 */1340 if (p2p_aux || iftype != NL80211_IFTYPE_P2P_DEVICE)1341 set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);1342 else1343 set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status);1344 1345 /* Mutex is released inside this function */1346 iwl_mvm_cleanup_roc(mvm);1347}1348 1349void iwl_mvm_remove_csa_period(struct iwl_mvm *mvm,1350 struct ieee80211_vif *vif)1351{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;1354 u32 id;1355 1356 lockdep_assert_held(&mvm->mutex);1357 1358 spin_lock_bh(&mvm->time_event_lock);1359 id = te_data->id;1360 spin_unlock_bh(&mvm->time_event_lock);1361 1362 if (id != TE_CHANNEL_SWITCH_PERIOD)1363 return;1364 1365 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);1366}1367 1368int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,1369 struct ieee80211_vif *vif,1370 u32 duration, u32 apply_time)1371{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 = {};1375 1376 lockdep_assert_held(&mvm->mutex);1377 1378 if (te_data->running) {1379 u32 id;1380 1381 spin_lock_bh(&mvm->time_event_lock);1382 id = te_data->id;1383 spin_unlock_bh(&mvm->time_event_lock);1384 1385 if (id == TE_CHANNEL_SWITCH_PERIOD) {1386 IWL_DEBUG_TE(mvm, "CS period is already scheduled\n");1387 return -EBUSY;1388 }1389 1390 /*1391 * Remove the session protection time event to allow the1392 * channel switch. If we got here, we just heard a beacon so1393 * the session protection is not needed anymore anyway.1394 */1395 iwl_mvm_remove_time_event(mvm, mvmvif, te_data);1396 }1397 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 |1408 TE_V2_ABSENCE);1409 if (!apply_time)1410 time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY);1411 1412 return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);1413}1414 1415static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait,1416 struct iwl_rx_packet *pkt, void *data)1417{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);1422 1423 if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF ||1424 pkt->hdr.group_id != MAC_CONF_GROUP))1425 return true;1426 1427 if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {1428 IWL_ERR(mvm, "Invalid SESSION_PROTECTION_NOTIF response\n");1429 return true;1430 }1431 1432 resp = (void *)pkt->data;1433 1434 if (!resp->status)1435 IWL_ERR(mvm,1436 "TIME_EVENT_NOTIFICATION received but not executed\n");1437 1438 return true;1439}1440 1441void 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)1446{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)),1457 };1458 1459 if (mac_link_id < 0)1460 return;1461 1462 lockdep_assert_held(&mvm->mutex);1463 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);1470 1471 return;1472 }1473 1474 iwl_mvm_te_clear_data(mvm, te_data);1475 /*1476 * The time_event_data.id field is reused to save session1477 * protection's configuration.1478 */1479 te_data->id = le32_to_cpu(cmd.conf_id);1480 te_data->duration = le32_to_cpu(cmd.duration_tu);1481 te_data->vif = vif;1482 te_data->link_id = link_id;1483 spin_unlock_bh(&mvm->time_event_lock);1484 1485 IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n",1486 le32_to_cpu(cmd.duration_tu));1487 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)) {1492 goto send_cmd_err;1493 }1494 1495 return;1496 }1497 1498 iwl_init_notification_wait(&mvm->notif_wait, &wait_notif,1499 notif, ARRAY_SIZE(notif),1500 iwl_mvm_session_prot_notif, NULL);1501 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);1506 goto send_cmd_err;1507 } else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif,1508 TU_TO_JIFFIES(100))) {1509 IWL_ERR(mvm,1510 "Failed to protect session until session protection\n");1511 }1512 return;1513 1514send_cmd_err:1515 IWL_ERR(mvm,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);1520}1521