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