18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 28e99ea8dSJohannes Berg /* 31724fc78SEmmanuel Grumbach * Copyright (C) 2012-2014, 2018-2023 Intel Corporation 48e99ea8dSJohannes Berg * Copyright (C) 2013-2015 Intel Mobile Communications GmbH 58e99ea8dSJohannes Berg * Copyright (C) 2017 Intel Deutschland GmbH 68e99ea8dSJohannes Berg */ 7e705c121SKalle Valo #include <linux/jiffies.h> 8e705c121SKalle Valo #include <net/mac80211.h> 9e705c121SKalle Valo 109fca9d5cSJohannes Berg #include "fw/notif-wait.h" 11e705c121SKalle Valo #include "iwl-trans.h" 12e705c121SKalle Valo #include "fw-api.h" 13e705c121SKalle Valo #include "time-event.h" 14e705c121SKalle Valo #include "mvm.h" 15e705c121SKalle Valo #include "iwl-io.h" 16e705c121SKalle Valo #include "iwl-prph.h" 17e705c121SKalle Valo 18e705c121SKalle Valo /* 19e705c121SKalle Valo * For the high priority TE use a time event type that has similar priority to 20e705c121SKalle Valo * the FW's action scan priority. 21e705c121SKalle Valo */ 22e705c121SKalle Valo #define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE 23e705c121SKalle Valo #define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC 24e705c121SKalle Valo 25e705c121SKalle Valo void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, 26e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data) 27e705c121SKalle Valo { 28e705c121SKalle Valo lockdep_assert_held(&mvm->time_event_lock); 29e705c121SKalle Valo 30cee859feSAvraham Stern if (!te_data || !te_data->vif) 31e705c121SKalle Valo return; 32e705c121SKalle Valo 33e705c121SKalle Valo list_del(&te_data->list); 347b3954a1SIlan Peer 357b3954a1SIlan Peer /* 367b3954a1SIlan Peer * the list is only used for AUX ROC events so make sure it is always 377b3954a1SIlan Peer * initialized 387b3954a1SIlan Peer */ 397b3954a1SIlan Peer INIT_LIST_HEAD(&te_data->list); 407b3954a1SIlan Peer 41e705c121SKalle Valo te_data->running = false; 42e705c121SKalle Valo te_data->uid = 0; 43e705c121SKalle Valo te_data->id = TE_MAX; 44e705c121SKalle Valo te_data->vif = NULL; 45*23673041SMiri Korenblit te_data->link_id = -1; 46e705c121SKalle Valo } 47e705c121SKalle Valo 48e705c121SKalle Valo void iwl_mvm_roc_done_wk(struct work_struct *wk) 49e705c121SKalle Valo { 50e705c121SKalle Valo struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); 51e705c121SKalle Valo 52e705c121SKalle Valo /* 53998e1abaSNathan Errera * Clear the ROC_RUNNING status bit. 54e705c121SKalle Valo * This will cause the TX path to drop offchannel transmissions. 55e705c121SKalle Valo * That would also be done by mac80211, but it is racy, in particular 56e705c121SKalle Valo * in the case that the time event actually completed in the firmware 57e705c121SKalle Valo * (which is handled in iwl_mvm_te_handle_notif). 58e705c121SKalle Valo */ 5958d3bef4SEmmanuel Grumbach clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 60e705c121SKalle Valo 61e705c121SKalle Valo synchronize_net(); 62e705c121SKalle Valo 63e705c121SKalle Valo /* 64e705c121SKalle Valo * Flush the offchannel queue -- this is called when the time 65e705c121SKalle Valo * event finishes or is canceled, so that frames queued for it 66e705c121SKalle Valo * won't get stuck on the queue and be transmitted in the next 67e705c121SKalle Valo * time event. 68e705c121SKalle Valo */ 696c2d49fdSJohannes Berg 70f9084775SNathan Errera mutex_lock(&mvm->mutex); 716c2d49fdSJohannes Berg if (test_and_clear_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) { 726c2d49fdSJohannes Berg struct iwl_mvm_vif *mvmvif; 736c2d49fdSJohannes Berg 746c2d49fdSJohannes Berg /* 756c2d49fdSJohannes Berg * NB: access to this pointer would be racy, but the flush bit 766c2d49fdSJohannes Berg * can only be set when we had a P2P-Device VIF, and we have a 776c2d49fdSJohannes Berg * flush of this work in iwl_mvm_prepare_mac_removal() so it's 786c2d49fdSJohannes Berg * not really racy. 796c2d49fdSJohannes Berg */ 806c2d49fdSJohannes Berg 816c2d49fdSJohannes Berg if (!WARN_ON(!mvm->p2p_device_vif)) { 8284ef7cbeSIlan Peer struct ieee80211_vif *vif = mvm->p2p_device_vif; 8384ef7cbeSIlan Peer 8484ef7cbeSIlan Peer mvmvif = iwl_mvm_vif_from_mac80211(vif); 8539176296SJohannes Berg iwl_mvm_flush_sta(mvm, mvmvif->deflink.bcast_sta.sta_id, 8639176296SJohannes Berg mvmvif->deflink.bcast_sta.tfd_queue_msk); 8784ef7cbeSIlan Peer 8884ef7cbeSIlan Peer if (mvm->mld_api_is_used) { 8984ef7cbeSIlan Peer iwl_mvm_mld_rm_bcast_sta(mvm, vif, 9084ef7cbeSIlan Peer &vif->bss_conf); 9184ef7cbeSIlan Peer 9284ef7cbeSIlan Peer iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 9384ef7cbeSIlan Peer LINK_CONTEXT_MODIFY_ACTIVE, 9484ef7cbeSIlan Peer false); 9584ef7cbeSIlan Peer } else { 9684ef7cbeSIlan Peer iwl_mvm_rm_p2p_bcast_sta(mvm, vif); 9784ef7cbeSIlan Peer iwl_mvm_binding_remove_vif(mvm, vif); 9884ef7cbeSIlan Peer } 9984ef7cbeSIlan Peer 10084ef7cbeSIlan Peer /* Do not remove the PHY context as removing and adding 10184ef7cbeSIlan Peer * a PHY context has timing overheads. Leaving it 10284ef7cbeSIlan Peer * configured in FW would be useful in case the next ROC 10384ef7cbeSIlan Peer * is with the same channel. 10484ef7cbeSIlan Peer */ 1056c2d49fdSJohannes Berg } 106998e1abaSNathan Errera } 107998e1abaSNathan Errera 108998e1abaSNathan Errera /* 109998e1abaSNathan Errera * Clear the ROC_AUX_RUNNING status bit. 110998e1abaSNathan Errera * This will cause the TX path to drop offchannel transmissions. 111998e1abaSNathan Errera * That would also be done by mac80211, but it is racy, in particular 112998e1abaSNathan Errera * in the case that the time event actually completed in the firmware 113998e1abaSNathan Errera * (which is handled in iwl_mvm_te_handle_notif). 114998e1abaSNathan Errera */ 115998e1abaSNathan Errera if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { 116f9084775SNathan Errera /* do the same in case of hot spot 2.0 */ 11739176296SJohannes Berg iwl_mvm_flush_sta(mvm, mvm->aux_sta.sta_id, 11839176296SJohannes Berg mvm->aux_sta.tfd_queue_msk); 119998e1abaSNathan Errera 120fe8b2ad3SMiri Korenblit if (mvm->mld_api_is_used) { 121fe8b2ad3SMiri Korenblit iwl_mvm_mld_rm_aux_sta(mvm); 122fe8b2ad3SMiri Korenblit goto out_unlock; 123fe8b2ad3SMiri Korenblit } 124fe8b2ad3SMiri Korenblit 1252c2c3647SNathan Errera /* In newer version of this command an aux station is added only 1262c2c3647SNathan Errera * in cases of dedicated tx queue and need to be removed in end 1272c2c3647SNathan Errera * of use */ 1281724fc78SEmmanuel Grumbach if (iwl_mvm_has_new_station_api(mvm->fw)) 1292c2c3647SNathan Errera iwl_mvm_rm_aux_sta(mvm); 1306c2d49fdSJohannes Berg } 131f9084775SNathan Errera 132fe8b2ad3SMiri Korenblit out_unlock: 133f9084775SNathan Errera mutex_unlock(&mvm->mutex); 134e705c121SKalle Valo } 135e705c121SKalle Valo 136e705c121SKalle Valo static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) 137e705c121SKalle Valo { 138e705c121SKalle Valo /* 139e705c121SKalle Valo * Of course, our status bit is just as racy as mac80211, so in 140e705c121SKalle Valo * addition, fire off the work struct which will drop all frames 141e705c121SKalle Valo * from the hardware queues that made it through the race. First 142e705c121SKalle Valo * it will of course synchronize the TX path to make sure that 143e705c121SKalle Valo * any *new* TX will be rejected. 144e705c121SKalle Valo */ 145e705c121SKalle Valo schedule_work(&mvm->roc_done_wk); 146e705c121SKalle Valo } 147e705c121SKalle Valo 148e705c121SKalle Valo static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm) 149e705c121SKalle Valo { 150e705c121SKalle Valo struct ieee80211_vif *csa_vif; 151e705c121SKalle Valo 152e705c121SKalle Valo rcu_read_lock(); 153e705c121SKalle Valo 154e705c121SKalle Valo csa_vif = rcu_dereference(mvm->csa_vif); 155d0a9123eSJohannes Berg if (!csa_vif || !csa_vif->bss_conf.csa_active) 156e705c121SKalle Valo goto out_unlock; 157e705c121SKalle Valo 158e705c121SKalle Valo IWL_DEBUG_TE(mvm, "CSA NOA started\n"); 159e705c121SKalle Valo 160e705c121SKalle Valo /* 161e705c121SKalle Valo * CSA NoA is started but we still have beacons to 162e705c121SKalle Valo * transmit on the current channel. 163e705c121SKalle Valo * So we just do nothing here and the switch 164e705c121SKalle Valo * will be performed on the last TBTT. 165e705c121SKalle Valo */ 1668552a434SJohn Crispin if (!ieee80211_beacon_cntdwn_is_complete(csa_vif)) { 167e705c121SKalle Valo IWL_WARN(mvm, "CSA NOA started too early\n"); 168e705c121SKalle Valo goto out_unlock; 169e705c121SKalle Valo } 170e705c121SKalle Valo 171e705c121SKalle Valo ieee80211_csa_finish(csa_vif); 172e705c121SKalle Valo 173e705c121SKalle Valo rcu_read_unlock(); 174e705c121SKalle Valo 175e705c121SKalle Valo RCU_INIT_POINTER(mvm->csa_vif, NULL); 176e705c121SKalle Valo 177e705c121SKalle Valo return; 178e705c121SKalle Valo 179e705c121SKalle Valo out_unlock: 180e705c121SKalle Valo rcu_read_unlock(); 181e705c121SKalle Valo } 182e705c121SKalle Valo 183e705c121SKalle Valo static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, 184e705c121SKalle Valo struct ieee80211_vif *vif, 185e705c121SKalle Valo const char *errmsg) 186e705c121SKalle Valo { 18719125cb0SAndrei Otcheretianski struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 18819125cb0SAndrei Otcheretianski 189e705c121SKalle Valo if (vif->type != NL80211_IFTYPE_STATION) 190e705c121SKalle Valo return false; 19119125cb0SAndrei Otcheretianski 192f276e20bSJohannes Berg if (!mvmvif->csa_bcn_pending && vif->cfg.assoc && 19319125cb0SAndrei Otcheretianski vif->bss_conf.dtim_period) 194e705c121SKalle Valo return false; 195e705c121SKalle Valo if (errmsg) 196e705c121SKalle Valo IWL_ERR(mvm, "%s\n", errmsg); 197e705c121SKalle Valo 1987686fd52SSara Sharon if (mvmvif->csa_bcn_pending) { 1997686fd52SSara Sharon struct iwl_mvm_sta *mvmsta; 2007686fd52SSara Sharon 2017686fd52SSara Sharon rcu_read_lock(); 202650cadb7SGregory Greenman mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, 203650cadb7SGregory Greenman mvmvif->deflink.ap_sta_id); 2047686fd52SSara Sharon if (!WARN_ON(!mvmsta)) 2057686fd52SSara Sharon iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false); 2067686fd52SSara Sharon rcu_read_unlock(); 2077686fd52SSara Sharon } 2087686fd52SSara Sharon 209f276e20bSJohannes Berg if (vif->cfg.assoc) { 210b537ffb6SShaul Triebitz /* 211b537ffb6SShaul Triebitz * When not associated, this will be called from 212b537ffb6SShaul Triebitz * iwl_mvm_event_mlme_callback_ini() 213b537ffb6SShaul Triebitz */ 214b537ffb6SShaul Triebitz iwl_dbg_tlv_time_point(&mvm->fwrt, 215b537ffb6SShaul Triebitz IWL_FW_INI_TIME_POINT_ASSOC_FAILED, 216b537ffb6SShaul Triebitz NULL); 217b537ffb6SShaul Triebitz } 218b537ffb6SShaul Triebitz 219e705c121SKalle Valo iwl_mvm_connection_loss(mvm, vif, errmsg); 220e705c121SKalle Valo return true; 221e705c121SKalle Valo } 222e705c121SKalle Valo 223e705c121SKalle Valo static void 224e705c121SKalle Valo iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, 225e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, 226e705c121SKalle Valo struct iwl_time_event_notif *notif) 227e705c121SKalle Valo { 228e705c121SKalle Valo struct ieee80211_vif *vif = te_data->vif; 229e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 230e705c121SKalle Valo 231e705c121SKalle Valo if (!notif->status) 232e705c121SKalle Valo IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); 233e705c121SKalle Valo 234e705c121SKalle Valo switch (te_data->vif->type) { 235e705c121SKalle Valo case NL80211_IFTYPE_AP: 236e705c121SKalle Valo if (!notif->status) 237e705c121SKalle Valo mvmvif->csa_failed = true; 238e705c121SKalle Valo iwl_mvm_csa_noa_start(mvm); 239e705c121SKalle Valo break; 240e705c121SKalle Valo case NL80211_IFTYPE_STATION: 241e705c121SKalle Valo if (!notif->status) { 242e705c121SKalle Valo iwl_mvm_connection_loss(mvm, vif, 243e705c121SKalle Valo "CSA TE failed to start"); 244e705c121SKalle Valo break; 245e705c121SKalle Valo } 246e705c121SKalle Valo iwl_mvm_csa_client_absent(mvm, te_data->vif); 24711af74adSAndrei Otcheretianski cancel_delayed_work(&mvmvif->csa_work); 248a469a593SEmmanuel Grumbach ieee80211_chswitch_done(te_data->vif, true, 0); 249e705c121SKalle Valo break; 250e705c121SKalle Valo default: 251e705c121SKalle Valo /* should never happen */ 252e705c121SKalle Valo WARN_ON_ONCE(1); 253e705c121SKalle Valo break; 254e705c121SKalle Valo } 255e705c121SKalle Valo 256e705c121SKalle Valo /* we don't need it anymore */ 257e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 258e705c121SKalle Valo } 259e705c121SKalle Valo 260e705c121SKalle Valo static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, 261e705c121SKalle Valo struct iwl_time_event_notif *notif, 262e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data) 263e705c121SKalle Valo { 264e705c121SKalle Valo struct iwl_fw_dbg_trigger_tlv *trig; 265e705c121SKalle Valo struct iwl_fw_dbg_trigger_time_event *te_trig; 266e705c121SKalle Valo int i; 267e705c121SKalle Valo 2686c042d75SSara Sharon trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, 2697174beb6SJohannes Berg ieee80211_vif_to_wdev(te_data->vif), 2706c042d75SSara Sharon FW_DBG_TRIGGER_TIME_EVENT); 2716c042d75SSara Sharon if (!trig) 272e705c121SKalle Valo return; 273e705c121SKalle Valo 2746c042d75SSara Sharon te_trig = (void *)trig->data; 2756c042d75SSara Sharon 276e705c121SKalle Valo for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { 277e705c121SKalle Valo u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id); 278e705c121SKalle Valo u32 trig_action_bitmap = 279e705c121SKalle Valo le32_to_cpu(te_trig->time_events[i].action_bitmap); 280e705c121SKalle Valo u32 trig_status_bitmap = 281e705c121SKalle Valo le32_to_cpu(te_trig->time_events[i].status_bitmap); 282e705c121SKalle Valo 283e705c121SKalle Valo if (trig_te_id != te_data->id || 284e705c121SKalle Valo !(trig_action_bitmap & le32_to_cpu(notif->action)) || 285e705c121SKalle Valo !(trig_status_bitmap & BIT(le32_to_cpu(notif->status)))) 286e705c121SKalle Valo continue; 287e705c121SKalle Valo 2887174beb6SJohannes Berg iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, 289e705c121SKalle Valo "Time event %d Action 0x%x received status: %d", 290e705c121SKalle Valo te_data->id, 291e705c121SKalle Valo le32_to_cpu(notif->action), 292e705c121SKalle Valo le32_to_cpu(notif->status)); 293e705c121SKalle Valo break; 294e705c121SKalle Valo } 295e705c121SKalle Valo } 296e705c121SKalle Valo 297a76b5731SAvraham Stern static void iwl_mvm_p2p_roc_finished(struct iwl_mvm *mvm) 298a76b5731SAvraham Stern { 299a76b5731SAvraham Stern /* 300a76b5731SAvraham Stern * If the IWL_MVM_STATUS_NEED_FLUSH_P2P is already set, then the 301a76b5731SAvraham Stern * roc_done_wk is already scheduled or running, so don't schedule it 302a76b5731SAvraham Stern * again to avoid a race where the roc_done_wk clears this bit after 303a76b5731SAvraham Stern * it is set here, affecting the next run of the roc_done_wk. 304a76b5731SAvraham Stern */ 305a76b5731SAvraham Stern if (!test_and_set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) 306a76b5731SAvraham Stern iwl_mvm_roc_finished(mvm); 307a76b5731SAvraham Stern } 308a76b5731SAvraham Stern 309e705c121SKalle Valo /* 310e705c121SKalle Valo * Handles a FW notification for an event that is known to the driver. 311e705c121SKalle Valo * 312e705c121SKalle Valo * @mvm: the mvm component 313e705c121SKalle Valo * @te_data: the time event data 314e705c121SKalle Valo * @notif: the notification data corresponding the time event data. 315e705c121SKalle Valo */ 316e705c121SKalle Valo static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, 317e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, 318e705c121SKalle Valo struct iwl_time_event_notif *notif) 319e705c121SKalle Valo { 320e705c121SKalle Valo lockdep_assert_held(&mvm->time_event_lock); 321e705c121SKalle Valo 322e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n", 323e705c121SKalle Valo le32_to_cpu(notif->unique_id), 324e705c121SKalle Valo le32_to_cpu(notif->action)); 325e705c121SKalle Valo 326e705c121SKalle Valo iwl_mvm_te_check_trigger(mvm, notif, te_data); 327e705c121SKalle Valo 328e705c121SKalle Valo /* 329e705c121SKalle Valo * The FW sends the start/end time event notifications even for events 330e705c121SKalle Valo * that it fails to schedule. This is indicated in the status field of 331e705c121SKalle Valo * the notification. This happens in cases that the scheduler cannot 332e705c121SKalle Valo * find a schedule that can handle the event (for example requesting a 333e705c121SKalle Valo * P2P Device discoveribility, while there are other higher priority 334e705c121SKalle Valo * events in the system). 335e705c121SKalle Valo */ 336e705c121SKalle Valo if (!le32_to_cpu(notif->status)) { 337e705c121SKalle Valo const char *msg; 338e705c121SKalle Valo 339e705c121SKalle Valo if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START)) 340e705c121SKalle Valo msg = "Time Event start notification failure"; 341e705c121SKalle Valo else 342e705c121SKalle Valo msg = "Time Event end notification failure"; 343e705c121SKalle Valo 344e705c121SKalle Valo IWL_DEBUG_TE(mvm, "%s\n", msg); 345e705c121SKalle Valo 346e705c121SKalle Valo if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) { 347e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 348e705c121SKalle Valo return; 349e705c121SKalle Valo } 350e705c121SKalle Valo } 351e705c121SKalle Valo 352e705c121SKalle Valo if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) { 353e705c121SKalle Valo IWL_DEBUG_TE(mvm, 354e705c121SKalle Valo "TE ended - current time %lu, estimated end %lu\n", 355e705c121SKalle Valo jiffies, te_data->end_jiffies); 356e705c121SKalle Valo 357e705c121SKalle Valo switch (te_data->vif->type) { 358e705c121SKalle Valo case NL80211_IFTYPE_P2P_DEVICE: 359e705c121SKalle Valo ieee80211_remain_on_channel_expired(mvm->hw); 360a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 361e705c121SKalle Valo break; 362e705c121SKalle Valo case NL80211_IFTYPE_STATION: 363e705c121SKalle Valo /* 364cc61d3ceSEmmanuel Grumbach * If we are switching channel, don't disconnect 365cc61d3ceSEmmanuel Grumbach * if the time event is already done. Beacons can 366cc61d3ceSEmmanuel Grumbach * be delayed a bit after the switch. 367cc61d3ceSEmmanuel Grumbach */ 368cc61d3ceSEmmanuel Grumbach if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { 369cc61d3ceSEmmanuel Grumbach IWL_DEBUG_TE(mvm, 370cc61d3ceSEmmanuel Grumbach "No beacon heard and the CS time event is over, don't disconnect\n"); 371cc61d3ceSEmmanuel Grumbach break; 372cc61d3ceSEmmanuel Grumbach } 373cc61d3ceSEmmanuel Grumbach 374cc61d3ceSEmmanuel Grumbach /* 375e705c121SKalle Valo * By now, we should have finished association 376e705c121SKalle Valo * and know the dtim period. 377e705c121SKalle Valo */ 378e705c121SKalle Valo iwl_mvm_te_check_disconnect(mvm, te_data->vif, 379f276e20bSJohannes Berg !te_data->vif->cfg.assoc ? 380976ac0afSShaul Triebitz "Not associated and the time event is over already..." : 38119125cb0SAndrei Otcheretianski "No beacon heard and the time event is over already..."); 382e705c121SKalle Valo break; 383e705c121SKalle Valo default: 384e705c121SKalle Valo break; 385e705c121SKalle Valo } 386e705c121SKalle Valo 387e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 388e705c121SKalle Valo } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) { 389e705c121SKalle Valo te_data->running = true; 390e705c121SKalle Valo te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); 391e705c121SKalle Valo 392e705c121SKalle Valo if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { 393e705c121SKalle Valo set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 394e705c121SKalle Valo ieee80211_ready_on_channel(mvm->hw); 395e705c121SKalle Valo } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { 396e705c121SKalle Valo iwl_mvm_te_handle_notify_csa(mvm, te_data, notif); 397e705c121SKalle Valo } 398e705c121SKalle Valo } else { 399e705c121SKalle Valo IWL_WARN(mvm, "Got TE with unknown action\n"); 400e705c121SKalle Valo } 401e705c121SKalle Valo } 402e705c121SKalle Valo 40367ac248eSShaul Triebitz void iwl_mvm_rx_roc_notif(struct iwl_mvm *mvm, 40467ac248eSShaul Triebitz struct iwl_rx_cmd_buffer *rxb) 40567ac248eSShaul Triebitz { 40667ac248eSShaul Triebitz struct iwl_rx_packet *pkt = rxb_addr(rxb); 40767ac248eSShaul Triebitz struct iwl_roc_notif *notif = (void *)pkt->data; 40867ac248eSShaul Triebitz 40967ac248eSShaul Triebitz if (le32_to_cpu(notif->success) && le32_to_cpu(notif->started) && 41067ac248eSShaul Triebitz le32_to_cpu(notif->activity) == ROC_ACTIVITY_HOTSPOT) { 41167ac248eSShaul Triebitz set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); 41267ac248eSShaul Triebitz ieee80211_ready_on_channel(mvm->hw); 41367ac248eSShaul Triebitz } else { 41467ac248eSShaul Triebitz iwl_mvm_roc_finished(mvm); 41567ac248eSShaul Triebitz ieee80211_remain_on_channel_expired(mvm->hw); 41667ac248eSShaul Triebitz } 41767ac248eSShaul Triebitz } 41867ac248eSShaul Triebitz 419e705c121SKalle Valo /* 420e705c121SKalle Valo * Handle A Aux ROC time event 421e705c121SKalle Valo */ 422e705c121SKalle Valo static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, 423e705c121SKalle Valo struct iwl_time_event_notif *notif) 424e705c121SKalle Valo { 4256d7cb4a6SJakob Koschel struct iwl_mvm_time_event_data *aux_roc_te = NULL, *te_data; 426e705c121SKalle Valo 4276d7cb4a6SJakob Koschel list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) { 428e705c121SKalle Valo if (le32_to_cpu(notif->unique_id) == te_data->uid) { 4296d7cb4a6SJakob Koschel aux_roc_te = te_data; 430e705c121SKalle Valo break; 431e705c121SKalle Valo } 432e705c121SKalle Valo } 433e705c121SKalle Valo if (!aux_roc_te) /* Not a Aux ROC time event */ 434e705c121SKalle Valo return -EINVAL; 435e705c121SKalle Valo 436e705c121SKalle Valo iwl_mvm_te_check_trigger(mvm, notif, te_data); 437e705c121SKalle Valo 438e705c121SKalle Valo IWL_DEBUG_TE(mvm, 4395151ad95SMatti Gottlieb "Aux ROC time event notification - UID = 0x%x action %d (error = %d)\n", 440e705c121SKalle Valo le32_to_cpu(notif->unique_id), 4415151ad95SMatti Gottlieb le32_to_cpu(notif->action), le32_to_cpu(notif->status)); 442e705c121SKalle Valo 4435151ad95SMatti Gottlieb if (!le32_to_cpu(notif->status) || 4445151ad95SMatti Gottlieb le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) { 445e705c121SKalle Valo /* End TE, notify mac80211 */ 446e705c121SKalle Valo ieee80211_remain_on_channel_expired(mvm->hw); 447e705c121SKalle Valo iwl_mvm_roc_finished(mvm); /* flush aux queue */ 448e705c121SKalle Valo list_del(&te_data->list); /* remove from list */ 449e705c121SKalle Valo te_data->running = false; 450e705c121SKalle Valo te_data->vif = NULL; 451e705c121SKalle Valo te_data->uid = 0; 452e705c121SKalle Valo te_data->id = TE_MAX; 453e705c121SKalle Valo } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) { 454e705c121SKalle Valo set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); 455e705c121SKalle Valo te_data->running = true; 456e705c121SKalle Valo ieee80211_ready_on_channel(mvm->hw); /* Start TE */ 457e705c121SKalle Valo } else { 458e705c121SKalle Valo IWL_DEBUG_TE(mvm, 459e705c121SKalle Valo "ERROR: Unknown Aux ROC Time Event (action = %d)\n", 460e705c121SKalle Valo le32_to_cpu(notif->action)); 461e705c121SKalle Valo return -EINVAL; 462e705c121SKalle Valo } 463e705c121SKalle Valo 464e705c121SKalle Valo return 0; 465e705c121SKalle Valo } 466e705c121SKalle Valo 467e705c121SKalle Valo /* 468e705c121SKalle Valo * The Rx handler for time event notifications 469e705c121SKalle Valo */ 470e705c121SKalle Valo void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, 471e705c121SKalle Valo struct iwl_rx_cmd_buffer *rxb) 472e705c121SKalle Valo { 473e705c121SKalle Valo struct iwl_rx_packet *pkt = rxb_addr(rxb); 474e705c121SKalle Valo struct iwl_time_event_notif *notif = (void *)pkt->data; 475e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, *tmp; 476e705c121SKalle Valo 477e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n", 478e705c121SKalle Valo le32_to_cpu(notif->unique_id), 479e705c121SKalle Valo le32_to_cpu(notif->action)); 480e705c121SKalle Valo 481e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 482e705c121SKalle Valo /* This time event is triggered for Aux ROC request */ 483e705c121SKalle Valo if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif)) 484e705c121SKalle Valo goto unlock; 485e705c121SKalle Valo 486e705c121SKalle Valo list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) { 487e705c121SKalle Valo if (le32_to_cpu(notif->unique_id) == te_data->uid) 488e705c121SKalle Valo iwl_mvm_te_handle_notif(mvm, te_data, notif); 489e705c121SKalle Valo } 490e705c121SKalle Valo unlock: 491e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 492e705c121SKalle Valo } 493e705c121SKalle Valo 494e705c121SKalle Valo static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait, 495e705c121SKalle Valo struct iwl_rx_packet *pkt, void *data) 496e705c121SKalle Valo { 497e705c121SKalle Valo struct iwl_mvm *mvm = 498e705c121SKalle Valo container_of(notif_wait, struct iwl_mvm, notif_wait); 499e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = data; 500e705c121SKalle Valo struct iwl_time_event_notif *resp; 501e705c121SKalle Valo int resp_len = iwl_rx_packet_payload_len(pkt); 502e705c121SKalle Valo 503e705c121SKalle Valo if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION)) 504e705c121SKalle Valo return true; 505e705c121SKalle Valo 506e705c121SKalle Valo if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 507e705c121SKalle Valo IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n"); 508e705c121SKalle Valo return true; 509e705c121SKalle Valo } 510e705c121SKalle Valo 511e705c121SKalle Valo resp = (void *)pkt->data; 512e705c121SKalle Valo 513e705c121SKalle Valo /* te_data->uid is already set in the TIME_EVENT_CMD response */ 514e705c121SKalle Valo if (le32_to_cpu(resp->unique_id) != te_data->uid) 515e705c121SKalle Valo return false; 516e705c121SKalle Valo 517e705c121SKalle Valo IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n", 518e705c121SKalle Valo te_data->uid); 519e705c121SKalle Valo if (!resp->status) 520e705c121SKalle Valo IWL_ERR(mvm, 521e705c121SKalle Valo "TIME_EVENT_NOTIFICATION received but not executed\n"); 522e705c121SKalle Valo 523e705c121SKalle Valo return true; 524e705c121SKalle Valo } 525e705c121SKalle Valo 526e705c121SKalle Valo static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, 527e705c121SKalle Valo struct iwl_rx_packet *pkt, void *data) 528e705c121SKalle Valo { 529e705c121SKalle Valo struct iwl_mvm *mvm = 530e705c121SKalle Valo container_of(notif_wait, struct iwl_mvm, notif_wait); 531e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = data; 532e705c121SKalle Valo struct iwl_time_event_resp *resp; 533e705c121SKalle Valo int resp_len = iwl_rx_packet_payload_len(pkt); 534e705c121SKalle Valo 535e705c121SKalle Valo if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) 536e705c121SKalle Valo return true; 537e705c121SKalle Valo 538e705c121SKalle Valo if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 539e705c121SKalle Valo IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); 540e705c121SKalle Valo return true; 541e705c121SKalle Valo } 542e705c121SKalle Valo 543e705c121SKalle Valo resp = (void *)pkt->data; 544e705c121SKalle Valo 545e705c121SKalle Valo /* we should never get a response to another TIME_EVENT_CMD here */ 546e705c121SKalle Valo if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id)) 547e705c121SKalle Valo return false; 548e705c121SKalle Valo 549e705c121SKalle Valo te_data->uid = le32_to_cpu(resp->unique_id); 550e705c121SKalle Valo IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", 551e705c121SKalle Valo te_data->uid); 552e705c121SKalle Valo return true; 553e705c121SKalle Valo } 554e705c121SKalle Valo 555e705c121SKalle Valo static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, 556e705c121SKalle Valo struct ieee80211_vif *vif, 557e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, 558e705c121SKalle Valo struct iwl_time_event_cmd *te_cmd) 559e705c121SKalle Valo { 560e705c121SKalle Valo static const u16 time_event_response[] = { TIME_EVENT_CMD }; 561e705c121SKalle Valo struct iwl_notification_wait wait_time_event; 562e705c121SKalle Valo int ret; 563e705c121SKalle Valo 564e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 565e705c121SKalle Valo 566e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n", 567e705c121SKalle Valo le32_to_cpu(te_cmd->duration)); 568e705c121SKalle Valo 569e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 570e705c121SKalle Valo if (WARN_ON(te_data->id != TE_MAX)) { 571e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 572e705c121SKalle Valo return -EIO; 573e705c121SKalle Valo } 574e705c121SKalle Valo te_data->vif = vif; 575e705c121SKalle Valo te_data->duration = le32_to_cpu(te_cmd->duration); 576e705c121SKalle Valo te_data->id = le32_to_cpu(te_cmd->id); 577e705c121SKalle Valo list_add_tail(&te_data->list, &mvm->time_event_list); 578e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 579e705c121SKalle Valo 580e705c121SKalle Valo /* 581e705c121SKalle Valo * Use a notification wait, which really just processes the 582e705c121SKalle Valo * command response and doesn't wait for anything, in order 583e705c121SKalle Valo * to be able to process the response and get the UID inside 584e705c121SKalle Valo * the RX path. Using CMD_WANT_SKB doesn't work because it 585e705c121SKalle Valo * stores the buffer and then wakes up this thread, by which 586e705c121SKalle Valo * time another notification (that the time event started) 587e705c121SKalle Valo * might already be processed unsuccessfully. 588e705c121SKalle Valo */ 589e705c121SKalle Valo iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, 590e705c121SKalle Valo time_event_response, 591e705c121SKalle Valo ARRAY_SIZE(time_event_response), 592e705c121SKalle Valo iwl_mvm_time_event_response, te_data); 593e705c121SKalle Valo 594e705c121SKalle Valo ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, 595e705c121SKalle Valo sizeof(*te_cmd), te_cmd); 596e705c121SKalle Valo if (ret) { 597e705c121SKalle Valo IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); 598e705c121SKalle Valo iwl_remove_notification(&mvm->notif_wait, &wait_time_event); 599e705c121SKalle Valo goto out_clear_te; 600e705c121SKalle Valo } 601e705c121SKalle Valo 602e705c121SKalle Valo /* No need to wait for anything, so just pass 1 (0 isn't valid) */ 603e705c121SKalle Valo ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); 604e705c121SKalle Valo /* should never fail */ 605e705c121SKalle Valo WARN_ON_ONCE(ret); 606e705c121SKalle Valo 607e705c121SKalle Valo if (ret) { 608e705c121SKalle Valo out_clear_te: 609e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 610e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 611e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 612e705c121SKalle Valo } 613e705c121SKalle Valo return ret; 614e705c121SKalle Valo } 615e705c121SKalle Valo 616e705c121SKalle Valo void iwl_mvm_protect_session(struct iwl_mvm *mvm, 617e705c121SKalle Valo struct ieee80211_vif *vif, 618e705c121SKalle Valo u32 duration, u32 min_duration, 619e705c121SKalle Valo u32 max_delay, bool wait_for_notif) 620e705c121SKalle Valo { 621e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 622e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 623e705c121SKalle Valo const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION }; 624e705c121SKalle Valo struct iwl_notification_wait wait_te_notif; 625e705c121SKalle Valo struct iwl_time_event_cmd time_cmd = {}; 626e705c121SKalle Valo 627e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 628e705c121SKalle Valo 629e705c121SKalle Valo if (te_data->running && 630e705c121SKalle Valo time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { 631e705c121SKalle Valo IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", 632e705c121SKalle Valo jiffies_to_msecs(te_data->end_jiffies - jiffies)); 633e705c121SKalle Valo return; 634e705c121SKalle Valo } 635e705c121SKalle Valo 636e705c121SKalle Valo if (te_data->running) { 637e705c121SKalle Valo IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n", 638e705c121SKalle Valo te_data->uid, 639e705c121SKalle Valo jiffies_to_msecs(te_data->end_jiffies - jiffies)); 640e705c121SKalle Valo /* 641e705c121SKalle Valo * we don't have enough time 642e705c121SKalle Valo * cancel the current TE and issue a new one 643e705c121SKalle Valo * Of course it would be better to remove the old one only 644e705c121SKalle Valo * when the new one is added, but we don't care if we are off 645e705c121SKalle Valo * channel for a bit. All we need to do, is not to return 646e705c121SKalle Valo * before we actually begin to be on the channel. 647e705c121SKalle Valo */ 648e705c121SKalle Valo iwl_mvm_stop_session_protection(mvm, vif); 649e705c121SKalle Valo } 650e705c121SKalle Valo 651e705c121SKalle Valo time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 652e705c121SKalle Valo time_cmd.id_and_color = 653e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 654e705c121SKalle Valo time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC); 655e705c121SKalle Valo 656e705c121SKalle Valo time_cmd.apply_time = cpu_to_le32(0); 657e705c121SKalle Valo 658e705c121SKalle Valo time_cmd.max_frags = TE_V2_FRAG_NONE; 659e705c121SKalle Valo time_cmd.max_delay = cpu_to_le32(max_delay); 660e705c121SKalle Valo /* TODO: why do we need to interval = bi if it is not periodic? */ 661e705c121SKalle Valo time_cmd.interval = cpu_to_le32(1); 662e705c121SKalle Valo time_cmd.duration = cpu_to_le32(duration); 663e705c121SKalle Valo time_cmd.repeat = 1; 664e705c121SKalle Valo time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 665e705c121SKalle Valo TE_V2_NOTIF_HOST_EVENT_END | 66640d53f4aSAndrei Otcheretianski TE_V2_START_IMMEDIATELY); 667e705c121SKalle Valo 668e705c121SKalle Valo if (!wait_for_notif) { 669e705c121SKalle Valo iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 670e705c121SKalle Valo return; 671e705c121SKalle Valo } 672e705c121SKalle Valo 673e705c121SKalle Valo /* 674e705c121SKalle Valo * Create notification_wait for the TIME_EVENT_NOTIFICATION to use 675e705c121SKalle Valo * right after we send the time event 676e705c121SKalle Valo */ 677e705c121SKalle Valo iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif, 678e705c121SKalle Valo te_notif_response, 679e705c121SKalle Valo ARRAY_SIZE(te_notif_response), 680e705c121SKalle Valo iwl_mvm_te_notif, te_data); 681e705c121SKalle Valo 682e705c121SKalle Valo /* If TE was sent OK - wait for the notification that started */ 683e705c121SKalle Valo if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) { 684e705c121SKalle Valo IWL_ERR(mvm, "Failed to add TE to protect session\n"); 685e705c121SKalle Valo iwl_remove_notification(&mvm->notif_wait, &wait_te_notif); 686e705c121SKalle Valo } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif, 687e705c121SKalle Valo TU_TO_JIFFIES(max_delay))) { 688e705c121SKalle Valo IWL_ERR(mvm, "Failed to protect session until TE\n"); 689e705c121SKalle Valo } 690e705c121SKalle Valo } 691e705c121SKalle Valo 6921cf260e3SEmmanuel Grumbach static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, 6937b3954a1SIlan Peer struct iwl_mvm_vif *mvmvif, 6947b3954a1SIlan Peer u32 id) 6951cf260e3SEmmanuel Grumbach { 6961cf260e3SEmmanuel Grumbach struct iwl_mvm_session_prot_cmd cmd = { 6971cf260e3SEmmanuel Grumbach .id_and_color = 6981cf260e3SEmmanuel Grumbach cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 6991cf260e3SEmmanuel Grumbach mvmvif->color)), 7001cf260e3SEmmanuel Grumbach .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 7017b3954a1SIlan Peer .conf_id = cpu_to_le32(id), 7021cf260e3SEmmanuel Grumbach }; 7031cf260e3SEmmanuel Grumbach int ret; 7041cf260e3SEmmanuel Grumbach 705f0c86427SJohannes Berg ret = iwl_mvm_send_cmd_pdu(mvm, 706f0c86427SJohannes Berg WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 7071cf260e3SEmmanuel Grumbach 0, sizeof(cmd), &cmd); 7081cf260e3SEmmanuel Grumbach if (ret) 7091cf260e3SEmmanuel Grumbach IWL_ERR(mvm, 7101cf260e3SEmmanuel Grumbach "Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret); 7111cf260e3SEmmanuel Grumbach } 7121cf260e3SEmmanuel Grumbach 713e705c121SKalle Valo static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, 714e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, 715e705c121SKalle Valo u32 *uid) 716e705c121SKalle Valo { 717e705c121SKalle Valo u32 id; 71824d5f16eSIlan Peer struct iwl_mvm_vif *mvmvif; 7197b3954a1SIlan Peer enum nl80211_iftype iftype; 7207b3954a1SIlan Peer 7217b3954a1SIlan Peer if (!te_data->vif) 7227b3954a1SIlan Peer return false; 7237b3954a1SIlan Peer 72424d5f16eSIlan Peer mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); 7257b3954a1SIlan Peer iftype = te_data->vif->type; 726e705c121SKalle Valo 727e705c121SKalle Valo /* 728e705c121SKalle Valo * It is possible that by the time we got to this point the time 729e705c121SKalle Valo * event was already removed. 730e705c121SKalle Valo */ 731e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 732e705c121SKalle Valo 733e705c121SKalle Valo /* Save time event uid before clearing its data */ 734e705c121SKalle Valo *uid = te_data->uid; 735e705c121SKalle Valo id = te_data->id; 736e705c121SKalle Valo 737e705c121SKalle Valo /* 738e705c121SKalle Valo * The clear_data function handles time events that were already removed 739e705c121SKalle Valo */ 740e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 741e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 742e705c121SKalle Valo 743f0337cb4SAvraham Stern /* When session protection is used, the te_data->id field 7441cf260e3SEmmanuel Grumbach * is reused to save session protection's configuration. 745f0337cb4SAvraham Stern * For AUX ROC, HOT_SPOT_CMD is used and the te_data->id field is set 746f0337cb4SAvraham Stern * to HOT_SPOT_CMD. 7471cf260e3SEmmanuel Grumbach */ 7481cf260e3SEmmanuel Grumbach if (fw_has_capa(&mvm->fw->ucode_capa, 749f0337cb4SAvraham Stern IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD) && 750f0337cb4SAvraham Stern id != HOT_SPOT_CMD) { 7511cf260e3SEmmanuel Grumbach if (mvmvif && id < SESSION_PROTECT_CONF_MAX_ID) { 7521cf260e3SEmmanuel Grumbach /* Session protection is still ongoing. Cancel it */ 7537b3954a1SIlan Peer iwl_mvm_cancel_session_protection(mvm, mvmvif, id); 7547b3954a1SIlan Peer if (iftype == NL80211_IFTYPE_P2P_DEVICE) { 755a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 7561cf260e3SEmmanuel Grumbach } 7571cf260e3SEmmanuel Grumbach } 7581cf260e3SEmmanuel Grumbach return false; 7591cf260e3SEmmanuel Grumbach } else { 7601cf260e3SEmmanuel Grumbach /* It is possible that by the time we try to remove it, the 7611cf260e3SEmmanuel Grumbach * time event has already ended and removed. In such a case 7621cf260e3SEmmanuel Grumbach * there is no need to send a removal command. 763e705c121SKalle Valo */ 764e705c121SKalle Valo if (id == TE_MAX) { 765e705c121SKalle Valo IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); 766e705c121SKalle Valo return false; 767e705c121SKalle Valo } 7681cf260e3SEmmanuel Grumbach } 769e705c121SKalle Valo 770e705c121SKalle Valo return true; 771e705c121SKalle Valo } 772e705c121SKalle Valo 773e705c121SKalle Valo /* 774e705c121SKalle Valo * Explicit request to remove a aux roc time event. The removal of a time 775e705c121SKalle Valo * event needs to be synchronized with the flow of a time event's end 776e705c121SKalle Valo * notification, which also removes the time event from the op mode 777e705c121SKalle Valo * data structures. 778e705c121SKalle Valo */ 779e705c121SKalle Valo static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm, 780e705c121SKalle Valo struct iwl_mvm_vif *mvmvif, 781e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data) 782e705c121SKalle Valo { 783e705c121SKalle Valo struct iwl_hs20_roc_req aux_cmd = {}; 78457e861d9SDavid Spinadel u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm); 78557e861d9SDavid Spinadel 786e705c121SKalle Valo u32 uid; 787e705c121SKalle Valo int ret; 788e705c121SKalle Valo 789e705c121SKalle Valo if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) 790e705c121SKalle Valo return; 791e705c121SKalle Valo 792e705c121SKalle Valo aux_cmd.event_unique_id = cpu_to_le32(uid); 793e705c121SKalle Valo aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); 794e705c121SKalle Valo aux_cmd.id_and_color = 795e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 796e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n", 797e705c121SKalle Valo le32_to_cpu(aux_cmd.event_unique_id)); 798e705c121SKalle Valo ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, 79957e861d9SDavid Spinadel len, &aux_cmd); 800e705c121SKalle Valo 801e705c121SKalle Valo if (WARN_ON(ret)) 802e705c121SKalle Valo return; 803e705c121SKalle Valo } 804e705c121SKalle Valo 805e705c121SKalle Valo /* 806e705c121SKalle Valo * Explicit request to remove a time event. The removal of a time event needs to 807e705c121SKalle Valo * be synchronized with the flow of a time event's end notification, which also 808e705c121SKalle Valo * removes the time event from the op mode data structures. 809e705c121SKalle Valo */ 810e705c121SKalle Valo void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, 811e705c121SKalle Valo struct iwl_mvm_vif *mvmvif, 812e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data) 813e705c121SKalle Valo { 814e705c121SKalle Valo struct iwl_time_event_cmd time_cmd = {}; 815e705c121SKalle Valo u32 uid; 816e705c121SKalle Valo int ret; 817e705c121SKalle Valo 818e705c121SKalle Valo if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) 819e705c121SKalle Valo return; 820e705c121SKalle Valo 821e705c121SKalle Valo /* When we remove a TE, the UID is to be set in the id field */ 822e705c121SKalle Valo time_cmd.id = cpu_to_le32(uid); 823e705c121SKalle Valo time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); 824e705c121SKalle Valo time_cmd.id_and_color = 825e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 826e705c121SKalle Valo 827e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); 828e705c121SKalle Valo ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, 829e705c121SKalle Valo sizeof(time_cmd), &time_cmd); 830997254a9SEmmanuel Grumbach if (ret) 831997254a9SEmmanuel Grumbach IWL_ERR(mvm, "Couldn't remove the time event\n"); 832e705c121SKalle Valo } 833e705c121SKalle Valo 834e705c121SKalle Valo void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, 835e705c121SKalle Valo struct ieee80211_vif *vif) 836e705c121SKalle Valo { 837e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 838e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 8393edfb5f4SAvraham Stern u32 id; 840e705c121SKalle Valo 841e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 8423edfb5f4SAvraham Stern 8433edfb5f4SAvraham Stern spin_lock_bh(&mvm->time_event_lock); 8443edfb5f4SAvraham Stern id = te_data->id; 8453edfb5f4SAvraham Stern spin_unlock_bh(&mvm->time_event_lock); 8463edfb5f4SAvraham Stern 8477b3954a1SIlan Peer if (fw_has_capa(&mvm->fw->ucode_capa, 8487b3954a1SIlan Peer IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { 8497b3954a1SIlan Peer if (id != SESSION_PROTECT_CONF_ASSOC) { 8507b3954a1SIlan Peer IWL_DEBUG_TE(mvm, 8517b3954a1SIlan Peer "don't remove session protection id=%u\n", 8527b3954a1SIlan Peer id); 8537b3954a1SIlan Peer return; 8547b3954a1SIlan Peer } 8557b3954a1SIlan Peer } else if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) { 8563edfb5f4SAvraham Stern IWL_DEBUG_TE(mvm, 8573edfb5f4SAvraham Stern "don't remove TE with id=%u (not session protection)\n", 8583edfb5f4SAvraham Stern id); 8593edfb5f4SAvraham Stern return; 8603edfb5f4SAvraham Stern } 8613edfb5f4SAvraham Stern 862e705c121SKalle Valo iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 863e705c121SKalle Valo } 864e705c121SKalle Valo 865fe959c7bSEmmanuel Grumbach void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, 866fe959c7bSEmmanuel Grumbach struct iwl_rx_cmd_buffer *rxb) 867fe959c7bSEmmanuel Grumbach { 868fe959c7bSEmmanuel Grumbach struct iwl_rx_packet *pkt = rxb_addr(rxb); 869fe959c7bSEmmanuel Grumbach struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data; 870fe959c7bSEmmanuel Grumbach struct ieee80211_vif *vif; 8711cf260e3SEmmanuel Grumbach struct iwl_mvm_vif *mvmvif; 872fe959c7bSEmmanuel Grumbach 873fe959c7bSEmmanuel Grumbach rcu_read_lock(); 874fe959c7bSEmmanuel Grumbach vif = iwl_mvm_rcu_dereference_vif_id(mvm, le32_to_cpu(notif->mac_id), 875fe959c7bSEmmanuel Grumbach true); 876fe959c7bSEmmanuel Grumbach 877fe959c7bSEmmanuel Grumbach if (!vif) 878fe959c7bSEmmanuel Grumbach goto out_unlock; 879fe959c7bSEmmanuel Grumbach 8801cf260e3SEmmanuel Grumbach mvmvif = iwl_mvm_vif_from_mac80211(vif); 8811cf260e3SEmmanuel Grumbach 882fe959c7bSEmmanuel Grumbach /* The vif is not a P2P_DEVICE, maintain its time_event_data */ 883fe959c7bSEmmanuel Grumbach if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { 884fe959c7bSEmmanuel Grumbach struct iwl_mvm_time_event_data *te_data = 885fe959c7bSEmmanuel Grumbach &mvmvif->time_event_data; 886fe959c7bSEmmanuel Grumbach 887fe959c7bSEmmanuel Grumbach if (!le32_to_cpu(notif->status)) { 888fe959c7bSEmmanuel Grumbach iwl_mvm_te_check_disconnect(mvm, vif, 889fe959c7bSEmmanuel Grumbach "Session protection failure"); 890089e5016SAvraham Stern spin_lock_bh(&mvm->time_event_lock); 891fe959c7bSEmmanuel Grumbach iwl_mvm_te_clear_data(mvm, te_data); 892089e5016SAvraham Stern spin_unlock_bh(&mvm->time_event_lock); 893fe959c7bSEmmanuel Grumbach } 894fe959c7bSEmmanuel Grumbach 895fe959c7bSEmmanuel Grumbach if (le32_to_cpu(notif->start)) { 896fe959c7bSEmmanuel Grumbach spin_lock_bh(&mvm->time_event_lock); 897fe959c7bSEmmanuel Grumbach te_data->running = le32_to_cpu(notif->start); 898fe959c7bSEmmanuel Grumbach te_data->end_jiffies = 899fe959c7bSEmmanuel Grumbach TU_TO_EXP_TIME(te_data->duration); 900fe959c7bSEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 901fe959c7bSEmmanuel Grumbach } else { 902fe959c7bSEmmanuel Grumbach /* 903fe959c7bSEmmanuel Grumbach * By now, we should have finished association 904fe959c7bSEmmanuel Grumbach * and know the dtim period. 905fe959c7bSEmmanuel Grumbach */ 906fe959c7bSEmmanuel Grumbach iwl_mvm_te_check_disconnect(mvm, vif, 907f276e20bSJohannes Berg !vif->cfg.assoc ? 908976ac0afSShaul Triebitz "Not associated and the session protection is over already..." : 909fe959c7bSEmmanuel Grumbach "No beacon heard and the session protection is over already..."); 910089e5016SAvraham Stern spin_lock_bh(&mvm->time_event_lock); 911fe959c7bSEmmanuel Grumbach iwl_mvm_te_clear_data(mvm, te_data); 912089e5016SAvraham Stern spin_unlock_bh(&mvm->time_event_lock); 913fe959c7bSEmmanuel Grumbach } 914fe959c7bSEmmanuel Grumbach 915fe959c7bSEmmanuel Grumbach goto out_unlock; 916fe959c7bSEmmanuel Grumbach } 917fe959c7bSEmmanuel Grumbach 918fe959c7bSEmmanuel Grumbach if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) { 919fe959c7bSEmmanuel Grumbach /* End TE, notify mac80211 */ 9201cf260e3SEmmanuel Grumbach mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID; 921a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 92284ef7cbeSIlan Peer ieee80211_remain_on_channel_expired(mvm->hw); 923fe959c7bSEmmanuel Grumbach } else if (le32_to_cpu(notif->start)) { 9241cf260e3SEmmanuel Grumbach if (WARN_ON(mvmvif->time_event_data.id != 9251cf260e3SEmmanuel Grumbach le32_to_cpu(notif->conf_id))) 9261cf260e3SEmmanuel Grumbach goto out_unlock; 927fe959c7bSEmmanuel Grumbach set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 928fe959c7bSEmmanuel Grumbach ieee80211_ready_on_channel(mvm->hw); /* Start TE */ 929fe959c7bSEmmanuel Grumbach } 930fe959c7bSEmmanuel Grumbach 931fe959c7bSEmmanuel Grumbach out_unlock: 932fe959c7bSEmmanuel Grumbach rcu_read_unlock(); 933fe959c7bSEmmanuel Grumbach } 934fe959c7bSEmmanuel Grumbach 935fe959c7bSEmmanuel Grumbach static int 936fe959c7bSEmmanuel Grumbach iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, 937fe959c7bSEmmanuel Grumbach struct ieee80211_vif *vif, 938fe959c7bSEmmanuel Grumbach int duration, 939fe959c7bSEmmanuel Grumbach enum ieee80211_roc_type type) 940fe959c7bSEmmanuel Grumbach { 941fe959c7bSEmmanuel Grumbach struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 942fe959c7bSEmmanuel Grumbach struct iwl_mvm_session_prot_cmd cmd = { 943fe959c7bSEmmanuel Grumbach .id_and_color = 944fe959c7bSEmmanuel Grumbach cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 945fe959c7bSEmmanuel Grumbach mvmvif->color)), 946fe959c7bSEmmanuel Grumbach .action = cpu_to_le32(FW_CTXT_ACTION_ADD), 947fe959c7bSEmmanuel Grumbach .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), 948fe959c7bSEmmanuel Grumbach }; 949fe959c7bSEmmanuel Grumbach 950fe959c7bSEmmanuel Grumbach lockdep_assert_held(&mvm->mutex); 951fe959c7bSEmmanuel Grumbach 9521cf260e3SEmmanuel Grumbach /* The time_event_data.id field is reused to save session 9531cf260e3SEmmanuel Grumbach * protection's configuration. 9541cf260e3SEmmanuel Grumbach */ 955*23673041SMiri Korenblit 956*23673041SMiri Korenblit mvmvif->time_event_data.link_id = 0; 957*23673041SMiri Korenblit 958fe959c7bSEmmanuel Grumbach switch (type) { 959fe959c7bSEmmanuel Grumbach case IEEE80211_ROC_TYPE_NORMAL: 9601cf260e3SEmmanuel Grumbach mvmvif->time_event_data.id = 9611cf260e3SEmmanuel Grumbach SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV; 962fe959c7bSEmmanuel Grumbach break; 963fe959c7bSEmmanuel Grumbach case IEEE80211_ROC_TYPE_MGMT_TX: 9641cf260e3SEmmanuel Grumbach mvmvif->time_event_data.id = 9651cf260e3SEmmanuel Grumbach SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION; 966fe959c7bSEmmanuel Grumbach break; 967fe959c7bSEmmanuel Grumbach default: 968fe959c7bSEmmanuel Grumbach WARN_ONCE(1, "Got an invalid ROC type\n"); 969fe959c7bSEmmanuel Grumbach return -EINVAL; 970fe959c7bSEmmanuel Grumbach } 971fe959c7bSEmmanuel Grumbach 9721cf260e3SEmmanuel Grumbach cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id); 973f0c86427SJohannes Berg return iwl_mvm_send_cmd_pdu(mvm, 974f0c86427SJohannes Berg WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 975fe959c7bSEmmanuel Grumbach 0, sizeof(cmd), &cmd); 976fe959c7bSEmmanuel Grumbach } 977fe959c7bSEmmanuel Grumbach 978e705c121SKalle Valo int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 979e705c121SKalle Valo int duration, enum ieee80211_roc_type type) 980e705c121SKalle Valo { 981e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 982e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 983e705c121SKalle Valo struct iwl_time_event_cmd time_cmd = {}; 984e705c121SKalle Valo 985e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 986e705c121SKalle Valo if (te_data->running) { 987e705c121SKalle Valo IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n"); 988e705c121SKalle Valo return -EBUSY; 989e705c121SKalle Valo } 990e705c121SKalle Valo 991fe959c7bSEmmanuel Grumbach if (fw_has_capa(&mvm->fw->ucode_capa, 992fe959c7bSEmmanuel Grumbach IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) 993fe959c7bSEmmanuel Grumbach return iwl_mvm_start_p2p_roc_session_protection(mvm, vif, 994fe959c7bSEmmanuel Grumbach duration, 995fe959c7bSEmmanuel Grumbach type); 996fe959c7bSEmmanuel Grumbach 997e705c121SKalle Valo time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 998e705c121SKalle Valo time_cmd.id_and_color = 999e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 1000e705c121SKalle Valo 1001e705c121SKalle Valo switch (type) { 1002e705c121SKalle Valo case IEEE80211_ROC_TYPE_NORMAL: 1003e705c121SKalle Valo time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL); 1004e705c121SKalle Valo break; 1005e705c121SKalle Valo case IEEE80211_ROC_TYPE_MGMT_TX: 1006e705c121SKalle Valo time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX); 1007e705c121SKalle Valo break; 1008e705c121SKalle Valo default: 1009e705c121SKalle Valo WARN_ONCE(1, "Got an invalid ROC type\n"); 1010e705c121SKalle Valo return -EINVAL; 1011e705c121SKalle Valo } 1012e705c121SKalle Valo 1013e705c121SKalle Valo time_cmd.apply_time = cpu_to_le32(0); 1014e705c121SKalle Valo time_cmd.interval = cpu_to_le32(1); 1015e705c121SKalle Valo 1016e705c121SKalle Valo /* 1017e705c121SKalle Valo * The P2P Device TEs can have lower priority than other events 1018e705c121SKalle Valo * that are being scheduled by the driver/fw, and thus it might not be 1019e705c121SKalle Valo * scheduled. To improve the chances of it being scheduled, allow them 1020e705c121SKalle Valo * to be fragmented, and in addition allow them to be delayed. 1021e705c121SKalle Valo */ 1022e705c121SKalle Valo time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS); 1023e705c121SKalle Valo time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2)); 1024e705c121SKalle Valo time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); 1025e705c121SKalle Valo time_cmd.repeat = 1; 1026e705c121SKalle Valo time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 1027e705c121SKalle Valo TE_V2_NOTIF_HOST_EVENT_END | 102840d53f4aSAndrei Otcheretianski TE_V2_START_IMMEDIATELY); 1029e705c121SKalle Valo 1030e705c121SKalle Valo return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 1031e705c121SKalle Valo } 1032e705c121SKalle Valo 1033305d236eSEliad Peller static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm) 1034e705c121SKalle Valo { 1035e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data; 1036e705c121SKalle Valo 1037e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 1038e705c121SKalle Valo 1039e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 1040e705c121SKalle Valo 1041e705c121SKalle Valo /* 1042e705c121SKalle Valo * Iterate over the list of time events and find the time event that is 1043e705c121SKalle Valo * associated with a P2P_DEVICE interface. 1044e705c121SKalle Valo * This assumes that a P2P_DEVICE interface can have only a single time 1045e705c121SKalle Valo * event at any given time and this time event coresponds to a ROC 1046e705c121SKalle Valo * request 1047e705c121SKalle Valo */ 1048e705c121SKalle Valo list_for_each_entry(te_data, &mvm->time_event_list, list) { 1049305d236eSEliad Peller if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) 1050305d236eSEliad Peller goto out; 1051e705c121SKalle Valo } 1052e705c121SKalle Valo 1053e705c121SKalle Valo /* There can only be at most one AUX ROC time event, we just use the 1054e705c121SKalle Valo * list to simplify/unify code. Remove it if it exists. 1055e705c121SKalle Valo */ 1056e705c121SKalle Valo te_data = list_first_entry_or_null(&mvm->aux_roc_te_list, 1057e705c121SKalle Valo struct iwl_mvm_time_event_data, 1058e705c121SKalle Valo list); 1059305d236eSEliad Peller out: 1060e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 1061305d236eSEliad Peller return te_data; 1062305d236eSEliad Peller } 1063e705c121SKalle Valo 1064305d236eSEliad Peller void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm) 1065305d236eSEliad Peller { 1066305d236eSEliad Peller struct iwl_mvm_time_event_data *te_data; 1067305d236eSEliad Peller u32 uid; 1068305d236eSEliad Peller 1069305d236eSEliad Peller te_data = iwl_mvm_get_roc_te(mvm); 1070305d236eSEliad Peller if (te_data) 1071305d236eSEliad Peller __iwl_mvm_remove_time_event(mvm, te_data, &uid); 1072305d236eSEliad Peller } 1073305d236eSEliad Peller 107467ac248eSShaul Triebitz static void iwl_mvm_roc_rm_cmd(struct iwl_mvm *mvm, u32 activity) 107567ac248eSShaul Triebitz { 107667ac248eSShaul Triebitz int ret; 107767ac248eSShaul Triebitz struct iwl_roc_req roc_cmd = { 107867ac248eSShaul Triebitz .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 107967ac248eSShaul Triebitz .activity = cpu_to_le32(activity), 108067ac248eSShaul Triebitz }; 108167ac248eSShaul Triebitz 108267ac248eSShaul Triebitz lockdep_assert_held(&mvm->mutex); 108367ac248eSShaul Triebitz ret = iwl_mvm_send_cmd_pdu(mvm, 108467ac248eSShaul Triebitz WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 108567ac248eSShaul Triebitz 0, sizeof(roc_cmd), &roc_cmd); 108667ac248eSShaul Triebitz WARN_ON(ret); 108767ac248eSShaul Triebitz } 108867ac248eSShaul Triebitz 108967ac248eSShaul Triebitz static void iwl_mvm_roc_station_remove(struct iwl_mvm *mvm, 109067ac248eSShaul Triebitz struct iwl_mvm_vif *mvmvif) 109167ac248eSShaul Triebitz { 109267ac248eSShaul Triebitz u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, ROC_CMD); 109367ac248eSShaul Triebitz u8 fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 109467ac248eSShaul Triebitz IWL_FW_CMD_VER_UNKNOWN); 109567ac248eSShaul Triebitz 109667ac248eSShaul Triebitz if (fw_ver == IWL_FW_CMD_VER_UNKNOWN) 109767ac248eSShaul Triebitz iwl_mvm_remove_aux_roc_te(mvm, mvmvif, 109867ac248eSShaul Triebitz &mvmvif->hs_time_event_data); 109967ac248eSShaul Triebitz else if (fw_ver == 3) 110067ac248eSShaul Triebitz iwl_mvm_roc_rm_cmd(mvm, ROC_ACTIVITY_HOTSPOT); 110167ac248eSShaul Triebitz else 110267ac248eSShaul Triebitz IWL_ERR(mvm, "ROC command version %d mismatch!\n", fw_ver); 110367ac248eSShaul Triebitz } 110467ac248eSShaul Triebitz 1105fe959c7bSEmmanuel Grumbach void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 1106305d236eSEliad Peller { 1107305d236eSEliad Peller struct iwl_mvm_vif *mvmvif; 1108305d236eSEliad Peller struct iwl_mvm_time_event_data *te_data; 1109305d236eSEliad Peller 1110fe959c7bSEmmanuel Grumbach if (fw_has_capa(&mvm->fw->ucode_capa, 1111fe959c7bSEmmanuel Grumbach IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { 1112fe959c7bSEmmanuel Grumbach mvmvif = iwl_mvm_vif_from_mac80211(vif); 1113fe959c7bSEmmanuel Grumbach 1114fb8d1b6eSEmmanuel Grumbach if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { 11157b3954a1SIlan Peer iwl_mvm_cancel_session_protection(mvm, mvmvif, 11167b3954a1SIlan Peer mvmvif->time_event_data.id); 1117a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 1118fb8d1b6eSEmmanuel Grumbach } else { 111967ac248eSShaul Triebitz iwl_mvm_roc_station_remove(mvm, mvmvif); 1120fe959c7bSEmmanuel Grumbach iwl_mvm_roc_finished(mvm); 1121a76b5731SAvraham Stern } 1122fe959c7bSEmmanuel Grumbach 1123fe959c7bSEmmanuel Grumbach return; 1124fe959c7bSEmmanuel Grumbach } 1125fe959c7bSEmmanuel Grumbach 1126305d236eSEliad Peller te_data = iwl_mvm_get_roc_te(mvm); 1127305d236eSEliad Peller if (!te_data) { 1128e705c121SKalle Valo IWL_WARN(mvm, "No remain on channel event\n"); 1129e705c121SKalle Valo return; 1130e705c121SKalle Valo } 1131e705c121SKalle Valo 1132305d236eSEliad Peller mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); 1133305d236eSEliad Peller 11346c2d49fdSJohannes Berg if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { 1135e705c121SKalle Valo iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 1136a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 11376c2d49fdSJohannes Berg } else { 1138e705c121SKalle Valo iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data); 1139e705c121SKalle Valo iwl_mvm_roc_finished(mvm); 1140e705c121SKalle Valo } 1141a76b5731SAvraham Stern } 1142e705c121SKalle Valo 114358ddd9b6SEmmanuel Grumbach void iwl_mvm_remove_csa_period(struct iwl_mvm *mvm, 114458ddd9b6SEmmanuel Grumbach struct ieee80211_vif *vif) 114558ddd9b6SEmmanuel Grumbach { 114658ddd9b6SEmmanuel Grumbach struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 114758ddd9b6SEmmanuel Grumbach struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 114858ddd9b6SEmmanuel Grumbach u32 id; 114958ddd9b6SEmmanuel Grumbach 115058ddd9b6SEmmanuel Grumbach lockdep_assert_held(&mvm->mutex); 115158ddd9b6SEmmanuel Grumbach 115258ddd9b6SEmmanuel Grumbach spin_lock_bh(&mvm->time_event_lock); 115358ddd9b6SEmmanuel Grumbach id = te_data->id; 115458ddd9b6SEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 115558ddd9b6SEmmanuel Grumbach 115658ddd9b6SEmmanuel Grumbach if (id != TE_CHANNEL_SWITCH_PERIOD) 115758ddd9b6SEmmanuel Grumbach return; 115858ddd9b6SEmmanuel Grumbach 115958ddd9b6SEmmanuel Grumbach iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 116058ddd9b6SEmmanuel Grumbach } 116158ddd9b6SEmmanuel Grumbach 1162e705c121SKalle Valo int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, 1163e705c121SKalle Valo struct ieee80211_vif *vif, 1164e705c121SKalle Valo u32 duration, u32 apply_time) 1165e705c121SKalle Valo { 1166e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1167e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 1168e705c121SKalle Valo struct iwl_time_event_cmd time_cmd = {}; 1169e705c121SKalle Valo 1170e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 1171e705c121SKalle Valo 1172e705c121SKalle Valo if (te_data->running) { 11733edfb5f4SAvraham Stern u32 id; 11743edfb5f4SAvraham Stern 11753edfb5f4SAvraham Stern spin_lock_bh(&mvm->time_event_lock); 11763edfb5f4SAvraham Stern id = te_data->id; 11773edfb5f4SAvraham Stern spin_unlock_bh(&mvm->time_event_lock); 11783edfb5f4SAvraham Stern 11793edfb5f4SAvraham Stern if (id == TE_CHANNEL_SWITCH_PERIOD) { 1180e705c121SKalle Valo IWL_DEBUG_TE(mvm, "CS period is already scheduled\n"); 1181e705c121SKalle Valo return -EBUSY; 1182e705c121SKalle Valo } 1183e705c121SKalle Valo 11843edfb5f4SAvraham Stern /* 11853edfb5f4SAvraham Stern * Remove the session protection time event to allow the 11863edfb5f4SAvraham Stern * channel switch. If we got here, we just heard a beacon so 11873edfb5f4SAvraham Stern * the session protection is not needed anymore anyway. 11883edfb5f4SAvraham Stern */ 11893edfb5f4SAvraham Stern iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 11903edfb5f4SAvraham Stern } 11913edfb5f4SAvraham Stern 1192e705c121SKalle Valo time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 1193e705c121SKalle Valo time_cmd.id_and_color = 1194e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 1195e705c121SKalle Valo time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD); 1196e705c121SKalle Valo time_cmd.apply_time = cpu_to_le32(apply_time); 1197e705c121SKalle Valo time_cmd.max_frags = TE_V2_FRAG_NONE; 1198e705c121SKalle Valo time_cmd.duration = cpu_to_le32(duration); 1199e705c121SKalle Valo time_cmd.repeat = 1; 1200e705c121SKalle Valo time_cmd.interval = cpu_to_le32(1); 1201e705c121SKalle Valo time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 1202e705c121SKalle Valo TE_V2_ABSENCE); 120340d53f4aSAndrei Otcheretianski if (!apply_time) 120440d53f4aSAndrei Otcheretianski time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY); 1205e705c121SKalle Valo 1206e705c121SKalle Valo return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 1207e705c121SKalle Valo } 1208fe959c7bSEmmanuel Grumbach 1209b5b878e3SEmmanuel Grumbach static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait, 1210b5b878e3SEmmanuel Grumbach struct iwl_rx_packet *pkt, void *data) 1211b5b878e3SEmmanuel Grumbach { 1212b5b878e3SEmmanuel Grumbach struct iwl_mvm *mvm = 1213b5b878e3SEmmanuel Grumbach container_of(notif_wait, struct iwl_mvm, notif_wait); 1214b5b878e3SEmmanuel Grumbach struct iwl_mvm_session_prot_notif *resp; 1215b5b878e3SEmmanuel Grumbach int resp_len = iwl_rx_packet_payload_len(pkt); 1216b5b878e3SEmmanuel Grumbach 1217b5b878e3SEmmanuel Grumbach if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF || 1218b5b878e3SEmmanuel Grumbach pkt->hdr.group_id != MAC_CONF_GROUP)) 1219b5b878e3SEmmanuel Grumbach return true; 1220b5b878e3SEmmanuel Grumbach 1221b5b878e3SEmmanuel Grumbach if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 1222b5b878e3SEmmanuel Grumbach IWL_ERR(mvm, "Invalid SESSION_PROTECTION_NOTIF response\n"); 1223b5b878e3SEmmanuel Grumbach return true; 1224b5b878e3SEmmanuel Grumbach } 1225b5b878e3SEmmanuel Grumbach 1226b5b878e3SEmmanuel Grumbach resp = (void *)pkt->data; 1227b5b878e3SEmmanuel Grumbach 1228b5b878e3SEmmanuel Grumbach if (!resp->status) 1229b5b878e3SEmmanuel Grumbach IWL_ERR(mvm, 1230b5b878e3SEmmanuel Grumbach "TIME_EVENT_NOTIFICATION received but not executed\n"); 1231b5b878e3SEmmanuel Grumbach 1232b5b878e3SEmmanuel Grumbach return true; 1233b5b878e3SEmmanuel Grumbach } 1234b5b878e3SEmmanuel Grumbach 1235fe959c7bSEmmanuel Grumbach void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, 1236fe959c7bSEmmanuel Grumbach struct ieee80211_vif *vif, 1237b5b878e3SEmmanuel Grumbach u32 duration, u32 min_duration, 1238*23673041SMiri Korenblit bool wait_for_notif, 1239*23673041SMiri Korenblit unsigned int link_id) 1240fe959c7bSEmmanuel Grumbach { 1241fe959c7bSEmmanuel Grumbach struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1242fe959c7bSEmmanuel Grumbach struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 1243f0c86427SJohannes Berg const u16 notif[] = { WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF) }; 1244b5b878e3SEmmanuel Grumbach struct iwl_notification_wait wait_notif; 1245fe959c7bSEmmanuel Grumbach struct iwl_mvm_session_prot_cmd cmd = { 1246fe959c7bSEmmanuel Grumbach .id_and_color = 1247fe959c7bSEmmanuel Grumbach cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 1248fe959c7bSEmmanuel Grumbach mvmvif->color)), 1249fe959c7bSEmmanuel Grumbach .action = cpu_to_le32(FW_CTXT_ACTION_ADD), 12508e967c13SShaul Triebitz .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), 1251fe959c7bSEmmanuel Grumbach .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), 1252fe959c7bSEmmanuel Grumbach }; 1253fe959c7bSEmmanuel Grumbach 1254fe959c7bSEmmanuel Grumbach lockdep_assert_held(&mvm->mutex); 1255fe959c7bSEmmanuel Grumbach 1256fe959c7bSEmmanuel Grumbach spin_lock_bh(&mvm->time_event_lock); 1257*23673041SMiri Korenblit if (te_data->running && te_data->link_id == link_id && 1258fe959c7bSEmmanuel Grumbach time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { 1259fe959c7bSEmmanuel Grumbach IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", 1260fe959c7bSEmmanuel Grumbach jiffies_to_msecs(te_data->end_jiffies - jiffies)); 1261fe959c7bSEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 1262fe959c7bSEmmanuel Grumbach 1263fe959c7bSEmmanuel Grumbach return; 1264fe959c7bSEmmanuel Grumbach } 1265fe959c7bSEmmanuel Grumbach 1266fe959c7bSEmmanuel Grumbach iwl_mvm_te_clear_data(mvm, te_data); 12678e967c13SShaul Triebitz /* 12688e967c13SShaul Triebitz * The time_event_data.id field is reused to save session 12698e967c13SShaul Triebitz * protection's configuration. 12708e967c13SShaul Triebitz */ 12718e967c13SShaul Triebitz te_data->id = le32_to_cpu(cmd.conf_id); 1272fe959c7bSEmmanuel Grumbach te_data->duration = le32_to_cpu(cmd.duration_tu); 12737b3954a1SIlan Peer te_data->vif = vif; 1274*23673041SMiri Korenblit te_data->link_id = link_id; 1275fe959c7bSEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 1276fe959c7bSEmmanuel Grumbach 1277fe959c7bSEmmanuel Grumbach IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n", 1278fe959c7bSEmmanuel Grumbach le32_to_cpu(cmd.duration_tu)); 1279fe959c7bSEmmanuel Grumbach 1280b5b878e3SEmmanuel Grumbach if (!wait_for_notif) { 1281b5b878e3SEmmanuel Grumbach if (iwl_mvm_send_cmd_pdu(mvm, 1282f0c86427SJohannes Berg WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 1283b5b878e3SEmmanuel Grumbach 0, sizeof(cmd), &cmd)) { 1284fe959c7bSEmmanuel Grumbach IWL_ERR(mvm, 1285b5b878e3SEmmanuel Grumbach "Couldn't send the SESSION_PROTECTION_CMD\n"); 1286fe959c7bSEmmanuel Grumbach spin_lock_bh(&mvm->time_event_lock); 1287fe959c7bSEmmanuel Grumbach iwl_mvm_te_clear_data(mvm, te_data); 1288fe959c7bSEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 1289fe959c7bSEmmanuel Grumbach } 1290b5b878e3SEmmanuel Grumbach 1291b5b878e3SEmmanuel Grumbach return; 1292b5b878e3SEmmanuel Grumbach } 1293b5b878e3SEmmanuel Grumbach 1294b5b878e3SEmmanuel Grumbach iwl_init_notification_wait(&mvm->notif_wait, &wait_notif, 1295b5b878e3SEmmanuel Grumbach notif, ARRAY_SIZE(notif), 1296b5b878e3SEmmanuel Grumbach iwl_mvm_session_prot_notif, NULL); 1297b5b878e3SEmmanuel Grumbach 1298b5b878e3SEmmanuel Grumbach if (iwl_mvm_send_cmd_pdu(mvm, 1299f0c86427SJohannes Berg WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 1300b5b878e3SEmmanuel Grumbach 0, sizeof(cmd), &cmd)) { 1301b5b878e3SEmmanuel Grumbach IWL_ERR(mvm, 1302b5b878e3SEmmanuel Grumbach "Couldn't send the SESSION_PROTECTION_CMD\n"); 1303b5b878e3SEmmanuel Grumbach iwl_remove_notification(&mvm->notif_wait, &wait_notif); 1304b5b878e3SEmmanuel Grumbach } else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif, 1305b5b878e3SEmmanuel Grumbach TU_TO_JIFFIES(100))) { 1306b5b878e3SEmmanuel Grumbach IWL_ERR(mvm, 1307b5b878e3SEmmanuel Grumbach "Failed to protect session until session protection\n"); 1308b5b878e3SEmmanuel Grumbach } 1309fe959c7bSEmmanuel Grumbach } 1310