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; 45e705c121SKalle Valo } 46e705c121SKalle Valo 47e705c121SKalle Valo void iwl_mvm_roc_done_wk(struct work_struct *wk) 48e705c121SKalle Valo { 49e705c121SKalle Valo struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); 50e705c121SKalle Valo 51e705c121SKalle Valo /* 52998e1abaSNathan Errera * Clear the ROC_RUNNING status bit. 53e705c121SKalle Valo * This will cause the TX path to drop offchannel transmissions. 54e705c121SKalle Valo * That would also be done by mac80211, but it is racy, in particular 55e705c121SKalle Valo * in the case that the time event actually completed in the firmware 56e705c121SKalle Valo * (which is handled in iwl_mvm_te_handle_notif). 57e705c121SKalle Valo */ 5858d3bef4SEmmanuel Grumbach clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 59e705c121SKalle Valo 60e705c121SKalle Valo synchronize_net(); 61e705c121SKalle Valo 62e705c121SKalle Valo /* 63e705c121SKalle Valo * Flush the offchannel queue -- this is called when the time 64e705c121SKalle Valo * event finishes or is canceled, so that frames queued for it 65e705c121SKalle Valo * won't get stuck on the queue and be transmitted in the next 66e705c121SKalle Valo * time event. 67e705c121SKalle Valo */ 686c2d49fdSJohannes Berg 69f9084775SNathan Errera mutex_lock(&mvm->mutex); 706c2d49fdSJohannes Berg if (test_and_clear_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) { 716c2d49fdSJohannes Berg struct iwl_mvm_vif *mvmvif; 726c2d49fdSJohannes Berg 736c2d49fdSJohannes Berg /* 746c2d49fdSJohannes Berg * NB: access to this pointer would be racy, but the flush bit 756c2d49fdSJohannes Berg * can only be set when we had a P2P-Device VIF, and we have a 766c2d49fdSJohannes Berg * flush of this work in iwl_mvm_prepare_mac_removal() so it's 776c2d49fdSJohannes Berg * not really racy. 786c2d49fdSJohannes Berg */ 796c2d49fdSJohannes Berg 806c2d49fdSJohannes Berg if (!WARN_ON(!mvm->p2p_device_vif)) { 8184ef7cbeSIlan Peer struct ieee80211_vif *vif = mvm->p2p_device_vif; 8284ef7cbeSIlan Peer 8384ef7cbeSIlan Peer mvmvif = iwl_mvm_vif_from_mac80211(vif); 84*39176296SJohannes Berg iwl_mvm_flush_sta(mvm, mvmvif->deflink.bcast_sta.sta_id, 85*39176296SJohannes Berg mvmvif->deflink.bcast_sta.tfd_queue_msk); 8684ef7cbeSIlan Peer 8784ef7cbeSIlan Peer if (mvm->mld_api_is_used) { 8884ef7cbeSIlan Peer iwl_mvm_mld_rm_bcast_sta(mvm, vif, 8984ef7cbeSIlan Peer &vif->bss_conf); 9084ef7cbeSIlan Peer 9184ef7cbeSIlan Peer iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 9284ef7cbeSIlan Peer LINK_CONTEXT_MODIFY_ACTIVE, 9384ef7cbeSIlan Peer false); 9484ef7cbeSIlan Peer } else { 9584ef7cbeSIlan Peer iwl_mvm_rm_p2p_bcast_sta(mvm, vif); 9684ef7cbeSIlan Peer iwl_mvm_binding_remove_vif(mvm, vif); 9784ef7cbeSIlan Peer } 9884ef7cbeSIlan Peer 9984ef7cbeSIlan Peer /* Do not remove the PHY context as removing and adding 10084ef7cbeSIlan Peer * a PHY context has timing overheads. Leaving it 10184ef7cbeSIlan Peer * configured in FW would be useful in case the next ROC 10284ef7cbeSIlan Peer * is with the same channel. 10384ef7cbeSIlan Peer */ 1046c2d49fdSJohannes Berg } 105998e1abaSNathan Errera } 106998e1abaSNathan Errera 107998e1abaSNathan Errera /* 108998e1abaSNathan Errera * Clear the ROC_AUX_RUNNING status bit. 109998e1abaSNathan Errera * This will cause the TX path to drop offchannel transmissions. 110998e1abaSNathan Errera * That would also be done by mac80211, but it is racy, in particular 111998e1abaSNathan Errera * in the case that the time event actually completed in the firmware 112998e1abaSNathan Errera * (which is handled in iwl_mvm_te_handle_notif). 113998e1abaSNathan Errera */ 114998e1abaSNathan Errera if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { 115f9084775SNathan Errera /* do the same in case of hot spot 2.0 */ 116*39176296SJohannes Berg iwl_mvm_flush_sta(mvm, mvm->aux_sta.sta_id, 117*39176296SJohannes Berg mvm->aux_sta.tfd_queue_msk); 118998e1abaSNathan Errera 119fe8b2ad3SMiri Korenblit if (mvm->mld_api_is_used) { 120fe8b2ad3SMiri Korenblit iwl_mvm_mld_rm_aux_sta(mvm); 121fe8b2ad3SMiri Korenblit goto out_unlock; 122fe8b2ad3SMiri Korenblit } 123fe8b2ad3SMiri Korenblit 1242c2c3647SNathan Errera /* In newer version of this command an aux station is added only 1252c2c3647SNathan Errera * in cases of dedicated tx queue and need to be removed in end 1262c2c3647SNathan Errera * of use */ 1271724fc78SEmmanuel Grumbach if (iwl_mvm_has_new_station_api(mvm->fw)) 1282c2c3647SNathan Errera iwl_mvm_rm_aux_sta(mvm); 1296c2d49fdSJohannes Berg } 130f9084775SNathan Errera 131fe8b2ad3SMiri Korenblit out_unlock: 132f9084775SNathan Errera mutex_unlock(&mvm->mutex); 133e705c121SKalle Valo } 134e705c121SKalle Valo 135e705c121SKalle Valo static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) 136e705c121SKalle Valo { 137e705c121SKalle Valo /* 138e705c121SKalle Valo * Of course, our status bit is just as racy as mac80211, so in 139e705c121SKalle Valo * addition, fire off the work struct which will drop all frames 140e705c121SKalle Valo * from the hardware queues that made it through the race. First 141e705c121SKalle Valo * it will of course synchronize the TX path to make sure that 142e705c121SKalle Valo * any *new* TX will be rejected. 143e705c121SKalle Valo */ 144e705c121SKalle Valo schedule_work(&mvm->roc_done_wk); 145e705c121SKalle Valo } 146e705c121SKalle Valo 147e705c121SKalle Valo static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm) 148e705c121SKalle Valo { 149e705c121SKalle Valo struct ieee80211_vif *csa_vif; 150e705c121SKalle Valo 151e705c121SKalle Valo rcu_read_lock(); 152e705c121SKalle Valo 153e705c121SKalle Valo csa_vif = rcu_dereference(mvm->csa_vif); 154d0a9123eSJohannes Berg if (!csa_vif || !csa_vif->bss_conf.csa_active) 155e705c121SKalle Valo goto out_unlock; 156e705c121SKalle Valo 157e705c121SKalle Valo IWL_DEBUG_TE(mvm, "CSA NOA started\n"); 158e705c121SKalle Valo 159e705c121SKalle Valo /* 160e705c121SKalle Valo * CSA NoA is started but we still have beacons to 161e705c121SKalle Valo * transmit on the current channel. 162e705c121SKalle Valo * So we just do nothing here and the switch 163e705c121SKalle Valo * will be performed on the last TBTT. 164e705c121SKalle Valo */ 1658552a434SJohn Crispin if (!ieee80211_beacon_cntdwn_is_complete(csa_vif)) { 166e705c121SKalle Valo IWL_WARN(mvm, "CSA NOA started too early\n"); 167e705c121SKalle Valo goto out_unlock; 168e705c121SKalle Valo } 169e705c121SKalle Valo 170e705c121SKalle Valo ieee80211_csa_finish(csa_vif); 171e705c121SKalle Valo 172e705c121SKalle Valo rcu_read_unlock(); 173e705c121SKalle Valo 174e705c121SKalle Valo RCU_INIT_POINTER(mvm->csa_vif, NULL); 175e705c121SKalle Valo 176e705c121SKalle Valo return; 177e705c121SKalle Valo 178e705c121SKalle Valo out_unlock: 179e705c121SKalle Valo rcu_read_unlock(); 180e705c121SKalle Valo } 181e705c121SKalle Valo 182e705c121SKalle Valo static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, 183e705c121SKalle Valo struct ieee80211_vif *vif, 184e705c121SKalle Valo const char *errmsg) 185e705c121SKalle Valo { 18619125cb0SAndrei Otcheretianski struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 18719125cb0SAndrei Otcheretianski 188e705c121SKalle Valo if (vif->type != NL80211_IFTYPE_STATION) 189e705c121SKalle Valo return false; 19019125cb0SAndrei Otcheretianski 191f276e20bSJohannes Berg if (!mvmvif->csa_bcn_pending && vif->cfg.assoc && 19219125cb0SAndrei Otcheretianski vif->bss_conf.dtim_period) 193e705c121SKalle Valo return false; 194e705c121SKalle Valo if (errmsg) 195e705c121SKalle Valo IWL_ERR(mvm, "%s\n", errmsg); 196e705c121SKalle Valo 1977686fd52SSara Sharon if (mvmvif->csa_bcn_pending) { 1987686fd52SSara Sharon struct iwl_mvm_sta *mvmsta; 1997686fd52SSara Sharon 2007686fd52SSara Sharon rcu_read_lock(); 201650cadb7SGregory Greenman mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, 202650cadb7SGregory Greenman mvmvif->deflink.ap_sta_id); 2037686fd52SSara Sharon if (!WARN_ON(!mvmsta)) 2047686fd52SSara Sharon iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false); 2057686fd52SSara Sharon rcu_read_unlock(); 2067686fd52SSara Sharon } 2077686fd52SSara Sharon 208f276e20bSJohannes Berg if (vif->cfg.assoc) { 209b537ffb6SShaul Triebitz /* 210b537ffb6SShaul Triebitz * When not associated, this will be called from 211b537ffb6SShaul Triebitz * iwl_mvm_event_mlme_callback_ini() 212b537ffb6SShaul Triebitz */ 213b537ffb6SShaul Triebitz iwl_dbg_tlv_time_point(&mvm->fwrt, 214b537ffb6SShaul Triebitz IWL_FW_INI_TIME_POINT_ASSOC_FAILED, 215b537ffb6SShaul Triebitz NULL); 216b537ffb6SShaul Triebitz } 217b537ffb6SShaul Triebitz 218e705c121SKalle Valo iwl_mvm_connection_loss(mvm, vif, errmsg); 219e705c121SKalle Valo return true; 220e705c121SKalle Valo } 221e705c121SKalle Valo 222e705c121SKalle Valo static void 223e705c121SKalle Valo iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, 224e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, 225e705c121SKalle Valo struct iwl_time_event_notif *notif) 226e705c121SKalle Valo { 227e705c121SKalle Valo struct ieee80211_vif *vif = te_data->vif; 228e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 229e705c121SKalle Valo 230e705c121SKalle Valo if (!notif->status) 231e705c121SKalle Valo IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); 232e705c121SKalle Valo 233e705c121SKalle Valo switch (te_data->vif->type) { 234e705c121SKalle Valo case NL80211_IFTYPE_AP: 235e705c121SKalle Valo if (!notif->status) 236e705c121SKalle Valo mvmvif->csa_failed = true; 237e705c121SKalle Valo iwl_mvm_csa_noa_start(mvm); 238e705c121SKalle Valo break; 239e705c121SKalle Valo case NL80211_IFTYPE_STATION: 240e705c121SKalle Valo if (!notif->status) { 241e705c121SKalle Valo iwl_mvm_connection_loss(mvm, vif, 242e705c121SKalle Valo "CSA TE failed to start"); 243e705c121SKalle Valo break; 244e705c121SKalle Valo } 245e705c121SKalle Valo iwl_mvm_csa_client_absent(mvm, te_data->vif); 24611af74adSAndrei Otcheretianski cancel_delayed_work(&mvmvif->csa_work); 247a469a593SEmmanuel Grumbach ieee80211_chswitch_done(te_data->vif, true, 0); 248e705c121SKalle Valo break; 249e705c121SKalle Valo default: 250e705c121SKalle Valo /* should never happen */ 251e705c121SKalle Valo WARN_ON_ONCE(1); 252e705c121SKalle Valo break; 253e705c121SKalle Valo } 254e705c121SKalle Valo 255e705c121SKalle Valo /* we don't need it anymore */ 256e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 257e705c121SKalle Valo } 258e705c121SKalle Valo 259e705c121SKalle Valo static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, 260e705c121SKalle Valo struct iwl_time_event_notif *notif, 261e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data) 262e705c121SKalle Valo { 263e705c121SKalle Valo struct iwl_fw_dbg_trigger_tlv *trig; 264e705c121SKalle Valo struct iwl_fw_dbg_trigger_time_event *te_trig; 265e705c121SKalle Valo int i; 266e705c121SKalle Valo 2676c042d75SSara Sharon trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, 2687174beb6SJohannes Berg ieee80211_vif_to_wdev(te_data->vif), 2696c042d75SSara Sharon FW_DBG_TRIGGER_TIME_EVENT); 2706c042d75SSara Sharon if (!trig) 271e705c121SKalle Valo return; 272e705c121SKalle Valo 2736c042d75SSara Sharon te_trig = (void *)trig->data; 2746c042d75SSara Sharon 275e705c121SKalle Valo for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { 276e705c121SKalle Valo u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id); 277e705c121SKalle Valo u32 trig_action_bitmap = 278e705c121SKalle Valo le32_to_cpu(te_trig->time_events[i].action_bitmap); 279e705c121SKalle Valo u32 trig_status_bitmap = 280e705c121SKalle Valo le32_to_cpu(te_trig->time_events[i].status_bitmap); 281e705c121SKalle Valo 282e705c121SKalle Valo if (trig_te_id != te_data->id || 283e705c121SKalle Valo !(trig_action_bitmap & le32_to_cpu(notif->action)) || 284e705c121SKalle Valo !(trig_status_bitmap & BIT(le32_to_cpu(notif->status)))) 285e705c121SKalle Valo continue; 286e705c121SKalle Valo 2877174beb6SJohannes Berg iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, 288e705c121SKalle Valo "Time event %d Action 0x%x received status: %d", 289e705c121SKalle Valo te_data->id, 290e705c121SKalle Valo le32_to_cpu(notif->action), 291e705c121SKalle Valo le32_to_cpu(notif->status)); 292e705c121SKalle Valo break; 293e705c121SKalle Valo } 294e705c121SKalle Valo } 295e705c121SKalle Valo 296a76b5731SAvraham Stern static void iwl_mvm_p2p_roc_finished(struct iwl_mvm *mvm) 297a76b5731SAvraham Stern { 298a76b5731SAvraham Stern /* 299a76b5731SAvraham Stern * If the IWL_MVM_STATUS_NEED_FLUSH_P2P is already set, then the 300a76b5731SAvraham Stern * roc_done_wk is already scheduled or running, so don't schedule it 301a76b5731SAvraham Stern * again to avoid a race where the roc_done_wk clears this bit after 302a76b5731SAvraham Stern * it is set here, affecting the next run of the roc_done_wk. 303a76b5731SAvraham Stern */ 304a76b5731SAvraham Stern if (!test_and_set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) 305a76b5731SAvraham Stern iwl_mvm_roc_finished(mvm); 306a76b5731SAvraham Stern } 307a76b5731SAvraham Stern 308e705c121SKalle Valo /* 309e705c121SKalle Valo * Handles a FW notification for an event that is known to the driver. 310e705c121SKalle Valo * 311e705c121SKalle Valo * @mvm: the mvm component 312e705c121SKalle Valo * @te_data: the time event data 313e705c121SKalle Valo * @notif: the notification data corresponding the time event data. 314e705c121SKalle Valo */ 315e705c121SKalle Valo static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, 316e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, 317e705c121SKalle Valo struct iwl_time_event_notif *notif) 318e705c121SKalle Valo { 319e705c121SKalle Valo lockdep_assert_held(&mvm->time_event_lock); 320e705c121SKalle Valo 321e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n", 322e705c121SKalle Valo le32_to_cpu(notif->unique_id), 323e705c121SKalle Valo le32_to_cpu(notif->action)); 324e705c121SKalle Valo 325e705c121SKalle Valo iwl_mvm_te_check_trigger(mvm, notif, te_data); 326e705c121SKalle Valo 327e705c121SKalle Valo /* 328e705c121SKalle Valo * The FW sends the start/end time event notifications even for events 329e705c121SKalle Valo * that it fails to schedule. This is indicated in the status field of 330e705c121SKalle Valo * the notification. This happens in cases that the scheduler cannot 331e705c121SKalle Valo * find a schedule that can handle the event (for example requesting a 332e705c121SKalle Valo * P2P Device discoveribility, while there are other higher priority 333e705c121SKalle Valo * events in the system). 334e705c121SKalle Valo */ 335e705c121SKalle Valo if (!le32_to_cpu(notif->status)) { 336e705c121SKalle Valo const char *msg; 337e705c121SKalle Valo 338e705c121SKalle Valo if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START)) 339e705c121SKalle Valo msg = "Time Event start notification failure"; 340e705c121SKalle Valo else 341e705c121SKalle Valo msg = "Time Event end notification failure"; 342e705c121SKalle Valo 343e705c121SKalle Valo IWL_DEBUG_TE(mvm, "%s\n", msg); 344e705c121SKalle Valo 345e705c121SKalle Valo if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) { 346e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 347e705c121SKalle Valo return; 348e705c121SKalle Valo } 349e705c121SKalle Valo } 350e705c121SKalle Valo 351e705c121SKalle Valo if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) { 352e705c121SKalle Valo IWL_DEBUG_TE(mvm, 353e705c121SKalle Valo "TE ended - current time %lu, estimated end %lu\n", 354e705c121SKalle Valo jiffies, te_data->end_jiffies); 355e705c121SKalle Valo 356e705c121SKalle Valo switch (te_data->vif->type) { 357e705c121SKalle Valo case NL80211_IFTYPE_P2P_DEVICE: 358e705c121SKalle Valo ieee80211_remain_on_channel_expired(mvm->hw); 359a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 360e705c121SKalle Valo break; 361e705c121SKalle Valo case NL80211_IFTYPE_STATION: 362e705c121SKalle Valo /* 363cc61d3ceSEmmanuel Grumbach * If we are switching channel, don't disconnect 364cc61d3ceSEmmanuel Grumbach * if the time event is already done. Beacons can 365cc61d3ceSEmmanuel Grumbach * be delayed a bit after the switch. 366cc61d3ceSEmmanuel Grumbach */ 367cc61d3ceSEmmanuel Grumbach if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { 368cc61d3ceSEmmanuel Grumbach IWL_DEBUG_TE(mvm, 369cc61d3ceSEmmanuel Grumbach "No beacon heard and the CS time event is over, don't disconnect\n"); 370cc61d3ceSEmmanuel Grumbach break; 371cc61d3ceSEmmanuel Grumbach } 372cc61d3ceSEmmanuel Grumbach 373cc61d3ceSEmmanuel Grumbach /* 374e705c121SKalle Valo * By now, we should have finished association 375e705c121SKalle Valo * and know the dtim period. 376e705c121SKalle Valo */ 377e705c121SKalle Valo iwl_mvm_te_check_disconnect(mvm, te_data->vif, 378f276e20bSJohannes Berg !te_data->vif->cfg.assoc ? 379976ac0afSShaul Triebitz "Not associated and the time event is over already..." : 38019125cb0SAndrei Otcheretianski "No beacon heard and the time event is over already..."); 381e705c121SKalle Valo break; 382e705c121SKalle Valo default: 383e705c121SKalle Valo break; 384e705c121SKalle Valo } 385e705c121SKalle Valo 386e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 387e705c121SKalle Valo } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) { 388e705c121SKalle Valo te_data->running = true; 389e705c121SKalle Valo te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); 390e705c121SKalle Valo 391e705c121SKalle Valo if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { 392e705c121SKalle Valo set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 393e705c121SKalle Valo ieee80211_ready_on_channel(mvm->hw); 394e705c121SKalle Valo } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { 395e705c121SKalle Valo iwl_mvm_te_handle_notify_csa(mvm, te_data, notif); 396e705c121SKalle Valo } 397e705c121SKalle Valo } else { 398e705c121SKalle Valo IWL_WARN(mvm, "Got TE with unknown action\n"); 399e705c121SKalle Valo } 400e705c121SKalle Valo } 401e705c121SKalle Valo 40267ac248eSShaul Triebitz void iwl_mvm_rx_roc_notif(struct iwl_mvm *mvm, 40367ac248eSShaul Triebitz struct iwl_rx_cmd_buffer *rxb) 40467ac248eSShaul Triebitz { 40567ac248eSShaul Triebitz struct iwl_rx_packet *pkt = rxb_addr(rxb); 40667ac248eSShaul Triebitz struct iwl_roc_notif *notif = (void *)pkt->data; 40767ac248eSShaul Triebitz 40867ac248eSShaul Triebitz if (le32_to_cpu(notif->success) && le32_to_cpu(notif->started) && 40967ac248eSShaul Triebitz le32_to_cpu(notif->activity) == ROC_ACTIVITY_HOTSPOT) { 41067ac248eSShaul Triebitz set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); 41167ac248eSShaul Triebitz ieee80211_ready_on_channel(mvm->hw); 41267ac248eSShaul Triebitz } else { 41367ac248eSShaul Triebitz iwl_mvm_roc_finished(mvm); 41467ac248eSShaul Triebitz ieee80211_remain_on_channel_expired(mvm->hw); 41567ac248eSShaul Triebitz } 41667ac248eSShaul Triebitz } 41767ac248eSShaul Triebitz 418e705c121SKalle Valo /* 419e705c121SKalle Valo * Handle A Aux ROC time event 420e705c121SKalle Valo */ 421e705c121SKalle Valo static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, 422e705c121SKalle Valo struct iwl_time_event_notif *notif) 423e705c121SKalle Valo { 4246d7cb4a6SJakob Koschel struct iwl_mvm_time_event_data *aux_roc_te = NULL, *te_data; 425e705c121SKalle Valo 4266d7cb4a6SJakob Koschel list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) { 427e705c121SKalle Valo if (le32_to_cpu(notif->unique_id) == te_data->uid) { 4286d7cb4a6SJakob Koschel aux_roc_te = te_data; 429e705c121SKalle Valo break; 430e705c121SKalle Valo } 431e705c121SKalle Valo } 432e705c121SKalle Valo if (!aux_roc_te) /* Not a Aux ROC time event */ 433e705c121SKalle Valo return -EINVAL; 434e705c121SKalle Valo 435e705c121SKalle Valo iwl_mvm_te_check_trigger(mvm, notif, te_data); 436e705c121SKalle Valo 437e705c121SKalle Valo IWL_DEBUG_TE(mvm, 4385151ad95SMatti Gottlieb "Aux ROC time event notification - UID = 0x%x action %d (error = %d)\n", 439e705c121SKalle Valo le32_to_cpu(notif->unique_id), 4405151ad95SMatti Gottlieb le32_to_cpu(notif->action), le32_to_cpu(notif->status)); 441e705c121SKalle Valo 4425151ad95SMatti Gottlieb if (!le32_to_cpu(notif->status) || 4435151ad95SMatti Gottlieb le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) { 444e705c121SKalle Valo /* End TE, notify mac80211 */ 445e705c121SKalle Valo ieee80211_remain_on_channel_expired(mvm->hw); 446e705c121SKalle Valo iwl_mvm_roc_finished(mvm); /* flush aux queue */ 447e705c121SKalle Valo list_del(&te_data->list); /* remove from list */ 448e705c121SKalle Valo te_data->running = false; 449e705c121SKalle Valo te_data->vif = NULL; 450e705c121SKalle Valo te_data->uid = 0; 451e705c121SKalle Valo te_data->id = TE_MAX; 452e705c121SKalle Valo } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) { 453e705c121SKalle Valo set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); 454e705c121SKalle Valo te_data->running = true; 455e705c121SKalle Valo ieee80211_ready_on_channel(mvm->hw); /* Start TE */ 456e705c121SKalle Valo } else { 457e705c121SKalle Valo IWL_DEBUG_TE(mvm, 458e705c121SKalle Valo "ERROR: Unknown Aux ROC Time Event (action = %d)\n", 459e705c121SKalle Valo le32_to_cpu(notif->action)); 460e705c121SKalle Valo return -EINVAL; 461e705c121SKalle Valo } 462e705c121SKalle Valo 463e705c121SKalle Valo return 0; 464e705c121SKalle Valo } 465e705c121SKalle Valo 466e705c121SKalle Valo /* 467e705c121SKalle Valo * The Rx handler for time event notifications 468e705c121SKalle Valo */ 469e705c121SKalle Valo void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, 470e705c121SKalle Valo struct iwl_rx_cmd_buffer *rxb) 471e705c121SKalle Valo { 472e705c121SKalle Valo struct iwl_rx_packet *pkt = rxb_addr(rxb); 473e705c121SKalle Valo struct iwl_time_event_notif *notif = (void *)pkt->data; 474e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, *tmp; 475e705c121SKalle Valo 476e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n", 477e705c121SKalle Valo le32_to_cpu(notif->unique_id), 478e705c121SKalle Valo le32_to_cpu(notif->action)); 479e705c121SKalle Valo 480e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 481e705c121SKalle Valo /* This time event is triggered for Aux ROC request */ 482e705c121SKalle Valo if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif)) 483e705c121SKalle Valo goto unlock; 484e705c121SKalle Valo 485e705c121SKalle Valo list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) { 486e705c121SKalle Valo if (le32_to_cpu(notif->unique_id) == te_data->uid) 487e705c121SKalle Valo iwl_mvm_te_handle_notif(mvm, te_data, notif); 488e705c121SKalle Valo } 489e705c121SKalle Valo unlock: 490e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 491e705c121SKalle Valo } 492e705c121SKalle Valo 493e705c121SKalle Valo static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait, 494e705c121SKalle Valo struct iwl_rx_packet *pkt, void *data) 495e705c121SKalle Valo { 496e705c121SKalle Valo struct iwl_mvm *mvm = 497e705c121SKalle Valo container_of(notif_wait, struct iwl_mvm, notif_wait); 498e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = data; 499e705c121SKalle Valo struct iwl_time_event_notif *resp; 500e705c121SKalle Valo int resp_len = iwl_rx_packet_payload_len(pkt); 501e705c121SKalle Valo 502e705c121SKalle Valo if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION)) 503e705c121SKalle Valo return true; 504e705c121SKalle Valo 505e705c121SKalle Valo if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 506e705c121SKalle Valo IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n"); 507e705c121SKalle Valo return true; 508e705c121SKalle Valo } 509e705c121SKalle Valo 510e705c121SKalle Valo resp = (void *)pkt->data; 511e705c121SKalle Valo 512e705c121SKalle Valo /* te_data->uid is already set in the TIME_EVENT_CMD response */ 513e705c121SKalle Valo if (le32_to_cpu(resp->unique_id) != te_data->uid) 514e705c121SKalle Valo return false; 515e705c121SKalle Valo 516e705c121SKalle Valo IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n", 517e705c121SKalle Valo te_data->uid); 518e705c121SKalle Valo if (!resp->status) 519e705c121SKalle Valo IWL_ERR(mvm, 520e705c121SKalle Valo "TIME_EVENT_NOTIFICATION received but not executed\n"); 521e705c121SKalle Valo 522e705c121SKalle Valo return true; 523e705c121SKalle Valo } 524e705c121SKalle Valo 525e705c121SKalle Valo static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, 526e705c121SKalle Valo struct iwl_rx_packet *pkt, void *data) 527e705c121SKalle Valo { 528e705c121SKalle Valo struct iwl_mvm *mvm = 529e705c121SKalle Valo container_of(notif_wait, struct iwl_mvm, notif_wait); 530e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = data; 531e705c121SKalle Valo struct iwl_time_event_resp *resp; 532e705c121SKalle Valo int resp_len = iwl_rx_packet_payload_len(pkt); 533e705c121SKalle Valo 534e705c121SKalle Valo if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) 535e705c121SKalle Valo return true; 536e705c121SKalle Valo 537e705c121SKalle Valo if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 538e705c121SKalle Valo IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); 539e705c121SKalle Valo return true; 540e705c121SKalle Valo } 541e705c121SKalle Valo 542e705c121SKalle Valo resp = (void *)pkt->data; 543e705c121SKalle Valo 544e705c121SKalle Valo /* we should never get a response to another TIME_EVENT_CMD here */ 545e705c121SKalle Valo if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id)) 546e705c121SKalle Valo return false; 547e705c121SKalle Valo 548e705c121SKalle Valo te_data->uid = le32_to_cpu(resp->unique_id); 549e705c121SKalle Valo IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", 550e705c121SKalle Valo te_data->uid); 551e705c121SKalle Valo return true; 552e705c121SKalle Valo } 553e705c121SKalle Valo 554e705c121SKalle Valo static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, 555e705c121SKalle Valo struct ieee80211_vif *vif, 556e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, 557e705c121SKalle Valo struct iwl_time_event_cmd *te_cmd) 558e705c121SKalle Valo { 559e705c121SKalle Valo static const u16 time_event_response[] = { TIME_EVENT_CMD }; 560e705c121SKalle Valo struct iwl_notification_wait wait_time_event; 561e705c121SKalle Valo int ret; 562e705c121SKalle Valo 563e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 564e705c121SKalle Valo 565e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n", 566e705c121SKalle Valo le32_to_cpu(te_cmd->duration)); 567e705c121SKalle Valo 568e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 569e705c121SKalle Valo if (WARN_ON(te_data->id != TE_MAX)) { 570e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 571e705c121SKalle Valo return -EIO; 572e705c121SKalle Valo } 573e705c121SKalle Valo te_data->vif = vif; 574e705c121SKalle Valo te_data->duration = le32_to_cpu(te_cmd->duration); 575e705c121SKalle Valo te_data->id = le32_to_cpu(te_cmd->id); 576e705c121SKalle Valo list_add_tail(&te_data->list, &mvm->time_event_list); 577e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 578e705c121SKalle Valo 579e705c121SKalle Valo /* 580e705c121SKalle Valo * Use a notification wait, which really just processes the 581e705c121SKalle Valo * command response and doesn't wait for anything, in order 582e705c121SKalle Valo * to be able to process the response and get the UID inside 583e705c121SKalle Valo * the RX path. Using CMD_WANT_SKB doesn't work because it 584e705c121SKalle Valo * stores the buffer and then wakes up this thread, by which 585e705c121SKalle Valo * time another notification (that the time event started) 586e705c121SKalle Valo * might already be processed unsuccessfully. 587e705c121SKalle Valo */ 588e705c121SKalle Valo iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, 589e705c121SKalle Valo time_event_response, 590e705c121SKalle Valo ARRAY_SIZE(time_event_response), 591e705c121SKalle Valo iwl_mvm_time_event_response, te_data); 592e705c121SKalle Valo 593e705c121SKalle Valo ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, 594e705c121SKalle Valo sizeof(*te_cmd), te_cmd); 595e705c121SKalle Valo if (ret) { 596e705c121SKalle Valo IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); 597e705c121SKalle Valo iwl_remove_notification(&mvm->notif_wait, &wait_time_event); 598e705c121SKalle Valo goto out_clear_te; 599e705c121SKalle Valo } 600e705c121SKalle Valo 601e705c121SKalle Valo /* No need to wait for anything, so just pass 1 (0 isn't valid) */ 602e705c121SKalle Valo ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); 603e705c121SKalle Valo /* should never fail */ 604e705c121SKalle Valo WARN_ON_ONCE(ret); 605e705c121SKalle Valo 606e705c121SKalle Valo if (ret) { 607e705c121SKalle Valo out_clear_te: 608e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 609e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 610e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 611e705c121SKalle Valo } 612e705c121SKalle Valo return ret; 613e705c121SKalle Valo } 614e705c121SKalle Valo 615e705c121SKalle Valo void iwl_mvm_protect_session(struct iwl_mvm *mvm, 616e705c121SKalle Valo struct ieee80211_vif *vif, 617e705c121SKalle Valo u32 duration, u32 min_duration, 618e705c121SKalle Valo u32 max_delay, bool wait_for_notif) 619e705c121SKalle Valo { 620e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 621e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 622e705c121SKalle Valo const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION }; 623e705c121SKalle Valo struct iwl_notification_wait wait_te_notif; 624e705c121SKalle Valo struct iwl_time_event_cmd time_cmd = {}; 625e705c121SKalle Valo 626e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 627e705c121SKalle Valo 628e705c121SKalle Valo if (te_data->running && 629e705c121SKalle Valo time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { 630e705c121SKalle Valo IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", 631e705c121SKalle Valo jiffies_to_msecs(te_data->end_jiffies - jiffies)); 632e705c121SKalle Valo return; 633e705c121SKalle Valo } 634e705c121SKalle Valo 635e705c121SKalle Valo if (te_data->running) { 636e705c121SKalle Valo IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n", 637e705c121SKalle Valo te_data->uid, 638e705c121SKalle Valo jiffies_to_msecs(te_data->end_jiffies - jiffies)); 639e705c121SKalle Valo /* 640e705c121SKalle Valo * we don't have enough time 641e705c121SKalle Valo * cancel the current TE and issue a new one 642e705c121SKalle Valo * Of course it would be better to remove the old one only 643e705c121SKalle Valo * when the new one is added, but we don't care if we are off 644e705c121SKalle Valo * channel for a bit. All we need to do, is not to return 645e705c121SKalle Valo * before we actually begin to be on the channel. 646e705c121SKalle Valo */ 647e705c121SKalle Valo iwl_mvm_stop_session_protection(mvm, vif); 648e705c121SKalle Valo } 649e705c121SKalle Valo 650e705c121SKalle Valo time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 651e705c121SKalle Valo time_cmd.id_and_color = 652e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 653e705c121SKalle Valo time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC); 654e705c121SKalle Valo 655e705c121SKalle Valo time_cmd.apply_time = cpu_to_le32(0); 656e705c121SKalle Valo 657e705c121SKalle Valo time_cmd.max_frags = TE_V2_FRAG_NONE; 658e705c121SKalle Valo time_cmd.max_delay = cpu_to_le32(max_delay); 659e705c121SKalle Valo /* TODO: why do we need to interval = bi if it is not periodic? */ 660e705c121SKalle Valo time_cmd.interval = cpu_to_le32(1); 661e705c121SKalle Valo time_cmd.duration = cpu_to_le32(duration); 662e705c121SKalle Valo time_cmd.repeat = 1; 663e705c121SKalle Valo time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 664e705c121SKalle Valo TE_V2_NOTIF_HOST_EVENT_END | 66540d53f4aSAndrei Otcheretianski TE_V2_START_IMMEDIATELY); 666e705c121SKalle Valo 667e705c121SKalle Valo if (!wait_for_notif) { 668e705c121SKalle Valo iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 669e705c121SKalle Valo return; 670e705c121SKalle Valo } 671e705c121SKalle Valo 672e705c121SKalle Valo /* 673e705c121SKalle Valo * Create notification_wait for the TIME_EVENT_NOTIFICATION to use 674e705c121SKalle Valo * right after we send the time event 675e705c121SKalle Valo */ 676e705c121SKalle Valo iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif, 677e705c121SKalle Valo te_notif_response, 678e705c121SKalle Valo ARRAY_SIZE(te_notif_response), 679e705c121SKalle Valo iwl_mvm_te_notif, te_data); 680e705c121SKalle Valo 681e705c121SKalle Valo /* If TE was sent OK - wait for the notification that started */ 682e705c121SKalle Valo if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) { 683e705c121SKalle Valo IWL_ERR(mvm, "Failed to add TE to protect session\n"); 684e705c121SKalle Valo iwl_remove_notification(&mvm->notif_wait, &wait_te_notif); 685e705c121SKalle Valo } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif, 686e705c121SKalle Valo TU_TO_JIFFIES(max_delay))) { 687e705c121SKalle Valo IWL_ERR(mvm, "Failed to protect session until TE\n"); 688e705c121SKalle Valo } 689e705c121SKalle Valo } 690e705c121SKalle Valo 6911cf260e3SEmmanuel Grumbach static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, 6927b3954a1SIlan Peer struct iwl_mvm_vif *mvmvif, 6937b3954a1SIlan Peer u32 id) 6941cf260e3SEmmanuel Grumbach { 6951cf260e3SEmmanuel Grumbach struct iwl_mvm_session_prot_cmd cmd = { 6961cf260e3SEmmanuel Grumbach .id_and_color = 6971cf260e3SEmmanuel Grumbach cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 6981cf260e3SEmmanuel Grumbach mvmvif->color)), 6991cf260e3SEmmanuel Grumbach .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 7007b3954a1SIlan Peer .conf_id = cpu_to_le32(id), 7011cf260e3SEmmanuel Grumbach }; 7021cf260e3SEmmanuel Grumbach int ret; 7031cf260e3SEmmanuel Grumbach 704f0c86427SJohannes Berg ret = iwl_mvm_send_cmd_pdu(mvm, 705f0c86427SJohannes Berg WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 7061cf260e3SEmmanuel Grumbach 0, sizeof(cmd), &cmd); 7071cf260e3SEmmanuel Grumbach if (ret) 7081cf260e3SEmmanuel Grumbach IWL_ERR(mvm, 7091cf260e3SEmmanuel Grumbach "Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret); 7101cf260e3SEmmanuel Grumbach } 7111cf260e3SEmmanuel Grumbach 712e705c121SKalle Valo static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, 713e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data, 714e705c121SKalle Valo u32 *uid) 715e705c121SKalle Valo { 716e705c121SKalle Valo u32 id; 71724d5f16eSIlan Peer struct iwl_mvm_vif *mvmvif; 7187b3954a1SIlan Peer enum nl80211_iftype iftype; 7197b3954a1SIlan Peer 7207b3954a1SIlan Peer if (!te_data->vif) 7217b3954a1SIlan Peer return false; 7227b3954a1SIlan Peer 72324d5f16eSIlan Peer mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); 7247b3954a1SIlan Peer iftype = te_data->vif->type; 725e705c121SKalle Valo 726e705c121SKalle Valo /* 727e705c121SKalle Valo * It is possible that by the time we got to this point the time 728e705c121SKalle Valo * event was already removed. 729e705c121SKalle Valo */ 730e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 731e705c121SKalle Valo 732e705c121SKalle Valo /* Save time event uid before clearing its data */ 733e705c121SKalle Valo *uid = te_data->uid; 734e705c121SKalle Valo id = te_data->id; 735e705c121SKalle Valo 736e705c121SKalle Valo /* 737e705c121SKalle Valo * The clear_data function handles time events that were already removed 738e705c121SKalle Valo */ 739e705c121SKalle Valo iwl_mvm_te_clear_data(mvm, te_data); 740e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 741e705c121SKalle Valo 742f0337cb4SAvraham Stern /* When session protection is used, the te_data->id field 7431cf260e3SEmmanuel Grumbach * is reused to save session protection's configuration. 744f0337cb4SAvraham Stern * For AUX ROC, HOT_SPOT_CMD is used and the te_data->id field is set 745f0337cb4SAvraham Stern * to HOT_SPOT_CMD. 7461cf260e3SEmmanuel Grumbach */ 7471cf260e3SEmmanuel Grumbach if (fw_has_capa(&mvm->fw->ucode_capa, 748f0337cb4SAvraham Stern IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD) && 749f0337cb4SAvraham Stern id != HOT_SPOT_CMD) { 7501cf260e3SEmmanuel Grumbach if (mvmvif && id < SESSION_PROTECT_CONF_MAX_ID) { 7511cf260e3SEmmanuel Grumbach /* Session protection is still ongoing. Cancel it */ 7527b3954a1SIlan Peer iwl_mvm_cancel_session_protection(mvm, mvmvif, id); 7537b3954a1SIlan Peer if (iftype == NL80211_IFTYPE_P2P_DEVICE) { 754a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 7551cf260e3SEmmanuel Grumbach } 7561cf260e3SEmmanuel Grumbach } 7571cf260e3SEmmanuel Grumbach return false; 7581cf260e3SEmmanuel Grumbach } else { 7591cf260e3SEmmanuel Grumbach /* It is possible that by the time we try to remove it, the 7601cf260e3SEmmanuel Grumbach * time event has already ended and removed. In such a case 7611cf260e3SEmmanuel Grumbach * there is no need to send a removal command. 762e705c121SKalle Valo */ 763e705c121SKalle Valo if (id == TE_MAX) { 764e705c121SKalle Valo IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); 765e705c121SKalle Valo return false; 766e705c121SKalle Valo } 7671cf260e3SEmmanuel Grumbach } 768e705c121SKalle Valo 769e705c121SKalle Valo return true; 770e705c121SKalle Valo } 771e705c121SKalle Valo 772e705c121SKalle Valo /* 773e705c121SKalle Valo * Explicit request to remove a aux roc time event. The removal of a time 774e705c121SKalle Valo * event needs to be synchronized with the flow of a time event's end 775e705c121SKalle Valo * notification, which also removes the time event from the op mode 776e705c121SKalle Valo * data structures. 777e705c121SKalle Valo */ 778e705c121SKalle Valo static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm, 779e705c121SKalle Valo struct iwl_mvm_vif *mvmvif, 780e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data) 781e705c121SKalle Valo { 782e705c121SKalle Valo struct iwl_hs20_roc_req aux_cmd = {}; 78357e861d9SDavid Spinadel u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm); 78457e861d9SDavid Spinadel 785e705c121SKalle Valo u32 uid; 786e705c121SKalle Valo int ret; 787e705c121SKalle Valo 788e705c121SKalle Valo if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) 789e705c121SKalle Valo return; 790e705c121SKalle Valo 791e705c121SKalle Valo aux_cmd.event_unique_id = cpu_to_le32(uid); 792e705c121SKalle Valo aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); 793e705c121SKalle Valo aux_cmd.id_and_color = 794e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 795e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n", 796e705c121SKalle Valo le32_to_cpu(aux_cmd.event_unique_id)); 797e705c121SKalle Valo ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, 79857e861d9SDavid Spinadel len, &aux_cmd); 799e705c121SKalle Valo 800e705c121SKalle Valo if (WARN_ON(ret)) 801e705c121SKalle Valo return; 802e705c121SKalle Valo } 803e705c121SKalle Valo 804e705c121SKalle Valo /* 805e705c121SKalle Valo * Explicit request to remove a time event. The removal of a time event needs to 806e705c121SKalle Valo * be synchronized with the flow of a time event's end notification, which also 807e705c121SKalle Valo * removes the time event from the op mode data structures. 808e705c121SKalle Valo */ 809e705c121SKalle Valo void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, 810e705c121SKalle Valo struct iwl_mvm_vif *mvmvif, 811e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data) 812e705c121SKalle Valo { 813e705c121SKalle Valo struct iwl_time_event_cmd time_cmd = {}; 814e705c121SKalle Valo u32 uid; 815e705c121SKalle Valo int ret; 816e705c121SKalle Valo 817e705c121SKalle Valo if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) 818e705c121SKalle Valo return; 819e705c121SKalle Valo 820e705c121SKalle Valo /* When we remove a TE, the UID is to be set in the id field */ 821e705c121SKalle Valo time_cmd.id = cpu_to_le32(uid); 822e705c121SKalle Valo time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); 823e705c121SKalle Valo time_cmd.id_and_color = 824e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 825e705c121SKalle Valo 826e705c121SKalle Valo IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); 827e705c121SKalle Valo ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, 828e705c121SKalle Valo sizeof(time_cmd), &time_cmd); 829997254a9SEmmanuel Grumbach if (ret) 830997254a9SEmmanuel Grumbach IWL_ERR(mvm, "Couldn't remove the time event\n"); 831e705c121SKalle Valo } 832e705c121SKalle Valo 833e705c121SKalle Valo void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, 834e705c121SKalle Valo struct ieee80211_vif *vif) 835e705c121SKalle Valo { 836e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 837e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 8383edfb5f4SAvraham Stern u32 id; 839e705c121SKalle Valo 840e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 8413edfb5f4SAvraham Stern 8423edfb5f4SAvraham Stern spin_lock_bh(&mvm->time_event_lock); 8433edfb5f4SAvraham Stern id = te_data->id; 8443edfb5f4SAvraham Stern spin_unlock_bh(&mvm->time_event_lock); 8453edfb5f4SAvraham Stern 8467b3954a1SIlan Peer if (fw_has_capa(&mvm->fw->ucode_capa, 8477b3954a1SIlan Peer IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { 8487b3954a1SIlan Peer if (id != SESSION_PROTECT_CONF_ASSOC) { 8497b3954a1SIlan Peer IWL_DEBUG_TE(mvm, 8507b3954a1SIlan Peer "don't remove session protection id=%u\n", 8517b3954a1SIlan Peer id); 8527b3954a1SIlan Peer return; 8537b3954a1SIlan Peer } 8547b3954a1SIlan Peer } else if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) { 8553edfb5f4SAvraham Stern IWL_DEBUG_TE(mvm, 8563edfb5f4SAvraham Stern "don't remove TE with id=%u (not session protection)\n", 8573edfb5f4SAvraham Stern id); 8583edfb5f4SAvraham Stern return; 8593edfb5f4SAvraham Stern } 8603edfb5f4SAvraham Stern 861e705c121SKalle Valo iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 862e705c121SKalle Valo } 863e705c121SKalle Valo 864fe959c7bSEmmanuel Grumbach void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, 865fe959c7bSEmmanuel Grumbach struct iwl_rx_cmd_buffer *rxb) 866fe959c7bSEmmanuel Grumbach { 867fe959c7bSEmmanuel Grumbach struct iwl_rx_packet *pkt = rxb_addr(rxb); 868fe959c7bSEmmanuel Grumbach struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data; 869fe959c7bSEmmanuel Grumbach struct ieee80211_vif *vif; 8701cf260e3SEmmanuel Grumbach struct iwl_mvm_vif *mvmvif; 871fe959c7bSEmmanuel Grumbach 872fe959c7bSEmmanuel Grumbach rcu_read_lock(); 873fe959c7bSEmmanuel Grumbach vif = iwl_mvm_rcu_dereference_vif_id(mvm, le32_to_cpu(notif->mac_id), 874fe959c7bSEmmanuel Grumbach true); 875fe959c7bSEmmanuel Grumbach 876fe959c7bSEmmanuel Grumbach if (!vif) 877fe959c7bSEmmanuel Grumbach goto out_unlock; 878fe959c7bSEmmanuel Grumbach 8791cf260e3SEmmanuel Grumbach mvmvif = iwl_mvm_vif_from_mac80211(vif); 8801cf260e3SEmmanuel Grumbach 881fe959c7bSEmmanuel Grumbach /* The vif is not a P2P_DEVICE, maintain its time_event_data */ 882fe959c7bSEmmanuel Grumbach if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { 883fe959c7bSEmmanuel Grumbach struct iwl_mvm_time_event_data *te_data = 884fe959c7bSEmmanuel Grumbach &mvmvif->time_event_data; 885fe959c7bSEmmanuel Grumbach 886fe959c7bSEmmanuel Grumbach if (!le32_to_cpu(notif->status)) { 887fe959c7bSEmmanuel Grumbach iwl_mvm_te_check_disconnect(mvm, vif, 888fe959c7bSEmmanuel Grumbach "Session protection failure"); 889089e5016SAvraham Stern spin_lock_bh(&mvm->time_event_lock); 890fe959c7bSEmmanuel Grumbach iwl_mvm_te_clear_data(mvm, te_data); 891089e5016SAvraham Stern spin_unlock_bh(&mvm->time_event_lock); 892fe959c7bSEmmanuel Grumbach } 893fe959c7bSEmmanuel Grumbach 894fe959c7bSEmmanuel Grumbach if (le32_to_cpu(notif->start)) { 895fe959c7bSEmmanuel Grumbach spin_lock_bh(&mvm->time_event_lock); 896fe959c7bSEmmanuel Grumbach te_data->running = le32_to_cpu(notif->start); 897fe959c7bSEmmanuel Grumbach te_data->end_jiffies = 898fe959c7bSEmmanuel Grumbach TU_TO_EXP_TIME(te_data->duration); 899fe959c7bSEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 900fe959c7bSEmmanuel Grumbach } else { 901fe959c7bSEmmanuel Grumbach /* 902fe959c7bSEmmanuel Grumbach * By now, we should have finished association 903fe959c7bSEmmanuel Grumbach * and know the dtim period. 904fe959c7bSEmmanuel Grumbach */ 905fe959c7bSEmmanuel Grumbach iwl_mvm_te_check_disconnect(mvm, vif, 906f276e20bSJohannes Berg !vif->cfg.assoc ? 907976ac0afSShaul Triebitz "Not associated and the session protection is over already..." : 908fe959c7bSEmmanuel Grumbach "No beacon heard and the session protection is over already..."); 909089e5016SAvraham Stern spin_lock_bh(&mvm->time_event_lock); 910fe959c7bSEmmanuel Grumbach iwl_mvm_te_clear_data(mvm, te_data); 911089e5016SAvraham Stern spin_unlock_bh(&mvm->time_event_lock); 912fe959c7bSEmmanuel Grumbach } 913fe959c7bSEmmanuel Grumbach 914fe959c7bSEmmanuel Grumbach goto out_unlock; 915fe959c7bSEmmanuel Grumbach } 916fe959c7bSEmmanuel Grumbach 917fe959c7bSEmmanuel Grumbach if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) { 918fe959c7bSEmmanuel Grumbach /* End TE, notify mac80211 */ 9191cf260e3SEmmanuel Grumbach mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID; 920a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 92184ef7cbeSIlan Peer ieee80211_remain_on_channel_expired(mvm->hw); 922fe959c7bSEmmanuel Grumbach } else if (le32_to_cpu(notif->start)) { 9231cf260e3SEmmanuel Grumbach if (WARN_ON(mvmvif->time_event_data.id != 9241cf260e3SEmmanuel Grumbach le32_to_cpu(notif->conf_id))) 9251cf260e3SEmmanuel Grumbach goto out_unlock; 926fe959c7bSEmmanuel Grumbach set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 927fe959c7bSEmmanuel Grumbach ieee80211_ready_on_channel(mvm->hw); /* Start TE */ 928fe959c7bSEmmanuel Grumbach } 929fe959c7bSEmmanuel Grumbach 930fe959c7bSEmmanuel Grumbach out_unlock: 931fe959c7bSEmmanuel Grumbach rcu_read_unlock(); 932fe959c7bSEmmanuel Grumbach } 933fe959c7bSEmmanuel Grumbach 934fe959c7bSEmmanuel Grumbach static int 935fe959c7bSEmmanuel Grumbach iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, 936fe959c7bSEmmanuel Grumbach struct ieee80211_vif *vif, 937fe959c7bSEmmanuel Grumbach int duration, 938fe959c7bSEmmanuel Grumbach enum ieee80211_roc_type type) 939fe959c7bSEmmanuel Grumbach { 940fe959c7bSEmmanuel Grumbach struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 941fe959c7bSEmmanuel Grumbach struct iwl_mvm_session_prot_cmd cmd = { 942fe959c7bSEmmanuel Grumbach .id_and_color = 943fe959c7bSEmmanuel Grumbach cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 944fe959c7bSEmmanuel Grumbach mvmvif->color)), 945fe959c7bSEmmanuel Grumbach .action = cpu_to_le32(FW_CTXT_ACTION_ADD), 946fe959c7bSEmmanuel Grumbach .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), 947fe959c7bSEmmanuel Grumbach }; 948fe959c7bSEmmanuel Grumbach 949fe959c7bSEmmanuel Grumbach lockdep_assert_held(&mvm->mutex); 950fe959c7bSEmmanuel Grumbach 9511cf260e3SEmmanuel Grumbach /* The time_event_data.id field is reused to save session 9521cf260e3SEmmanuel Grumbach * protection's configuration. 9531cf260e3SEmmanuel Grumbach */ 954fe959c7bSEmmanuel Grumbach switch (type) { 955fe959c7bSEmmanuel Grumbach case IEEE80211_ROC_TYPE_NORMAL: 9561cf260e3SEmmanuel Grumbach mvmvif->time_event_data.id = 9571cf260e3SEmmanuel Grumbach SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV; 958fe959c7bSEmmanuel Grumbach break; 959fe959c7bSEmmanuel Grumbach case IEEE80211_ROC_TYPE_MGMT_TX: 9601cf260e3SEmmanuel Grumbach mvmvif->time_event_data.id = 9611cf260e3SEmmanuel Grumbach SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION; 962fe959c7bSEmmanuel Grumbach break; 963fe959c7bSEmmanuel Grumbach default: 964fe959c7bSEmmanuel Grumbach WARN_ONCE(1, "Got an invalid ROC type\n"); 965fe959c7bSEmmanuel Grumbach return -EINVAL; 966fe959c7bSEmmanuel Grumbach } 967fe959c7bSEmmanuel Grumbach 9681cf260e3SEmmanuel Grumbach cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id); 969f0c86427SJohannes Berg return iwl_mvm_send_cmd_pdu(mvm, 970f0c86427SJohannes Berg WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 971fe959c7bSEmmanuel Grumbach 0, sizeof(cmd), &cmd); 972fe959c7bSEmmanuel Grumbach } 973fe959c7bSEmmanuel Grumbach 974e705c121SKalle Valo int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 975e705c121SKalle Valo int duration, enum ieee80211_roc_type type) 976e705c121SKalle Valo { 977e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 978e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 979e705c121SKalle Valo struct iwl_time_event_cmd time_cmd = {}; 980e705c121SKalle Valo 981e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 982e705c121SKalle Valo if (te_data->running) { 983e705c121SKalle Valo IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n"); 984e705c121SKalle Valo return -EBUSY; 985e705c121SKalle Valo } 986e705c121SKalle Valo 987fe959c7bSEmmanuel Grumbach if (fw_has_capa(&mvm->fw->ucode_capa, 988fe959c7bSEmmanuel Grumbach IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) 989fe959c7bSEmmanuel Grumbach return iwl_mvm_start_p2p_roc_session_protection(mvm, vif, 990fe959c7bSEmmanuel Grumbach duration, 991fe959c7bSEmmanuel Grumbach type); 992fe959c7bSEmmanuel Grumbach 993e705c121SKalle Valo time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 994e705c121SKalle Valo time_cmd.id_and_color = 995e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 996e705c121SKalle Valo 997e705c121SKalle Valo switch (type) { 998e705c121SKalle Valo case IEEE80211_ROC_TYPE_NORMAL: 999e705c121SKalle Valo time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL); 1000e705c121SKalle Valo break; 1001e705c121SKalle Valo case IEEE80211_ROC_TYPE_MGMT_TX: 1002e705c121SKalle Valo time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX); 1003e705c121SKalle Valo break; 1004e705c121SKalle Valo default: 1005e705c121SKalle Valo WARN_ONCE(1, "Got an invalid ROC type\n"); 1006e705c121SKalle Valo return -EINVAL; 1007e705c121SKalle Valo } 1008e705c121SKalle Valo 1009e705c121SKalle Valo time_cmd.apply_time = cpu_to_le32(0); 1010e705c121SKalle Valo time_cmd.interval = cpu_to_le32(1); 1011e705c121SKalle Valo 1012e705c121SKalle Valo /* 1013e705c121SKalle Valo * The P2P Device TEs can have lower priority than other events 1014e705c121SKalle Valo * that are being scheduled by the driver/fw, and thus it might not be 1015e705c121SKalle Valo * scheduled. To improve the chances of it being scheduled, allow them 1016e705c121SKalle Valo * to be fragmented, and in addition allow them to be delayed. 1017e705c121SKalle Valo */ 1018e705c121SKalle Valo time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS); 1019e705c121SKalle Valo time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2)); 1020e705c121SKalle Valo time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); 1021e705c121SKalle Valo time_cmd.repeat = 1; 1022e705c121SKalle Valo time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 1023e705c121SKalle Valo TE_V2_NOTIF_HOST_EVENT_END | 102440d53f4aSAndrei Otcheretianski TE_V2_START_IMMEDIATELY); 1025e705c121SKalle Valo 1026e705c121SKalle Valo return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 1027e705c121SKalle Valo } 1028e705c121SKalle Valo 1029305d236eSEliad Peller static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm) 1030e705c121SKalle Valo { 1031e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data; 1032e705c121SKalle Valo 1033e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 1034e705c121SKalle Valo 1035e705c121SKalle Valo spin_lock_bh(&mvm->time_event_lock); 1036e705c121SKalle Valo 1037e705c121SKalle Valo /* 1038e705c121SKalle Valo * Iterate over the list of time events and find the time event that is 1039e705c121SKalle Valo * associated with a P2P_DEVICE interface. 1040e705c121SKalle Valo * This assumes that a P2P_DEVICE interface can have only a single time 1041e705c121SKalle Valo * event at any given time and this time event coresponds to a ROC 1042e705c121SKalle Valo * request 1043e705c121SKalle Valo */ 1044e705c121SKalle Valo list_for_each_entry(te_data, &mvm->time_event_list, list) { 1045305d236eSEliad Peller if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) 1046305d236eSEliad Peller goto out; 1047e705c121SKalle Valo } 1048e705c121SKalle Valo 1049e705c121SKalle Valo /* There can only be at most one AUX ROC time event, we just use the 1050e705c121SKalle Valo * list to simplify/unify code. Remove it if it exists. 1051e705c121SKalle Valo */ 1052e705c121SKalle Valo te_data = list_first_entry_or_null(&mvm->aux_roc_te_list, 1053e705c121SKalle Valo struct iwl_mvm_time_event_data, 1054e705c121SKalle Valo list); 1055305d236eSEliad Peller out: 1056e705c121SKalle Valo spin_unlock_bh(&mvm->time_event_lock); 1057305d236eSEliad Peller return te_data; 1058305d236eSEliad Peller } 1059e705c121SKalle Valo 1060305d236eSEliad Peller void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm) 1061305d236eSEliad Peller { 1062305d236eSEliad Peller struct iwl_mvm_time_event_data *te_data; 1063305d236eSEliad Peller u32 uid; 1064305d236eSEliad Peller 1065305d236eSEliad Peller te_data = iwl_mvm_get_roc_te(mvm); 1066305d236eSEliad Peller if (te_data) 1067305d236eSEliad Peller __iwl_mvm_remove_time_event(mvm, te_data, &uid); 1068305d236eSEliad Peller } 1069305d236eSEliad Peller 107067ac248eSShaul Triebitz static void iwl_mvm_roc_rm_cmd(struct iwl_mvm *mvm, u32 activity) 107167ac248eSShaul Triebitz { 107267ac248eSShaul Triebitz int ret; 107367ac248eSShaul Triebitz struct iwl_roc_req roc_cmd = { 107467ac248eSShaul Triebitz .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 107567ac248eSShaul Triebitz .activity = cpu_to_le32(activity), 107667ac248eSShaul Triebitz }; 107767ac248eSShaul Triebitz 107867ac248eSShaul Triebitz lockdep_assert_held(&mvm->mutex); 107967ac248eSShaul Triebitz ret = iwl_mvm_send_cmd_pdu(mvm, 108067ac248eSShaul Triebitz WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 108167ac248eSShaul Triebitz 0, sizeof(roc_cmd), &roc_cmd); 108267ac248eSShaul Triebitz WARN_ON(ret); 108367ac248eSShaul Triebitz } 108467ac248eSShaul Triebitz 108567ac248eSShaul Triebitz static void iwl_mvm_roc_station_remove(struct iwl_mvm *mvm, 108667ac248eSShaul Triebitz struct iwl_mvm_vif *mvmvif) 108767ac248eSShaul Triebitz { 108867ac248eSShaul Triebitz u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, ROC_CMD); 108967ac248eSShaul Triebitz u8 fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 109067ac248eSShaul Triebitz IWL_FW_CMD_VER_UNKNOWN); 109167ac248eSShaul Triebitz 109267ac248eSShaul Triebitz if (fw_ver == IWL_FW_CMD_VER_UNKNOWN) 109367ac248eSShaul Triebitz iwl_mvm_remove_aux_roc_te(mvm, mvmvif, 109467ac248eSShaul Triebitz &mvmvif->hs_time_event_data); 109567ac248eSShaul Triebitz else if (fw_ver == 3) 109667ac248eSShaul Triebitz iwl_mvm_roc_rm_cmd(mvm, ROC_ACTIVITY_HOTSPOT); 109767ac248eSShaul Triebitz else 109867ac248eSShaul Triebitz IWL_ERR(mvm, "ROC command version %d mismatch!\n", fw_ver); 109967ac248eSShaul Triebitz } 110067ac248eSShaul Triebitz 1101fe959c7bSEmmanuel Grumbach void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 1102305d236eSEliad Peller { 1103305d236eSEliad Peller struct iwl_mvm_vif *mvmvif; 1104305d236eSEliad Peller struct iwl_mvm_time_event_data *te_data; 1105305d236eSEliad Peller 1106fe959c7bSEmmanuel Grumbach if (fw_has_capa(&mvm->fw->ucode_capa, 1107fe959c7bSEmmanuel Grumbach IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { 1108fe959c7bSEmmanuel Grumbach mvmvif = iwl_mvm_vif_from_mac80211(vif); 1109fe959c7bSEmmanuel Grumbach 1110fb8d1b6eSEmmanuel Grumbach if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { 11117b3954a1SIlan Peer iwl_mvm_cancel_session_protection(mvm, mvmvif, 11127b3954a1SIlan Peer mvmvif->time_event_data.id); 1113a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 1114fb8d1b6eSEmmanuel Grumbach } else { 111567ac248eSShaul Triebitz iwl_mvm_roc_station_remove(mvm, mvmvif); 1116fe959c7bSEmmanuel Grumbach iwl_mvm_roc_finished(mvm); 1117a76b5731SAvraham Stern } 1118fe959c7bSEmmanuel Grumbach 1119fe959c7bSEmmanuel Grumbach return; 1120fe959c7bSEmmanuel Grumbach } 1121fe959c7bSEmmanuel Grumbach 1122305d236eSEliad Peller te_data = iwl_mvm_get_roc_te(mvm); 1123305d236eSEliad Peller if (!te_data) { 1124e705c121SKalle Valo IWL_WARN(mvm, "No remain on channel event\n"); 1125e705c121SKalle Valo return; 1126e705c121SKalle Valo } 1127e705c121SKalle Valo 1128305d236eSEliad Peller mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); 1129305d236eSEliad Peller 11306c2d49fdSJohannes Berg if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { 1131e705c121SKalle Valo iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 1132a76b5731SAvraham Stern iwl_mvm_p2p_roc_finished(mvm); 11336c2d49fdSJohannes Berg } else { 1134e705c121SKalle Valo iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data); 1135e705c121SKalle Valo iwl_mvm_roc_finished(mvm); 1136e705c121SKalle Valo } 1137a76b5731SAvraham Stern } 1138e705c121SKalle Valo 113958ddd9b6SEmmanuel Grumbach void iwl_mvm_remove_csa_period(struct iwl_mvm *mvm, 114058ddd9b6SEmmanuel Grumbach struct ieee80211_vif *vif) 114158ddd9b6SEmmanuel Grumbach { 114258ddd9b6SEmmanuel Grumbach struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 114358ddd9b6SEmmanuel Grumbach struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 114458ddd9b6SEmmanuel Grumbach u32 id; 114558ddd9b6SEmmanuel Grumbach 114658ddd9b6SEmmanuel Grumbach lockdep_assert_held(&mvm->mutex); 114758ddd9b6SEmmanuel Grumbach 114858ddd9b6SEmmanuel Grumbach spin_lock_bh(&mvm->time_event_lock); 114958ddd9b6SEmmanuel Grumbach id = te_data->id; 115058ddd9b6SEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 115158ddd9b6SEmmanuel Grumbach 115258ddd9b6SEmmanuel Grumbach if (id != TE_CHANNEL_SWITCH_PERIOD) 115358ddd9b6SEmmanuel Grumbach return; 115458ddd9b6SEmmanuel Grumbach 115558ddd9b6SEmmanuel Grumbach iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 115658ddd9b6SEmmanuel Grumbach } 115758ddd9b6SEmmanuel Grumbach 1158e705c121SKalle Valo int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, 1159e705c121SKalle Valo struct ieee80211_vif *vif, 1160e705c121SKalle Valo u32 duration, u32 apply_time) 1161e705c121SKalle Valo { 1162e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1163e705c121SKalle Valo struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 1164e705c121SKalle Valo struct iwl_time_event_cmd time_cmd = {}; 1165e705c121SKalle Valo 1166e705c121SKalle Valo lockdep_assert_held(&mvm->mutex); 1167e705c121SKalle Valo 1168e705c121SKalle Valo if (te_data->running) { 11693edfb5f4SAvraham Stern u32 id; 11703edfb5f4SAvraham Stern 11713edfb5f4SAvraham Stern spin_lock_bh(&mvm->time_event_lock); 11723edfb5f4SAvraham Stern id = te_data->id; 11733edfb5f4SAvraham Stern spin_unlock_bh(&mvm->time_event_lock); 11743edfb5f4SAvraham Stern 11753edfb5f4SAvraham Stern if (id == TE_CHANNEL_SWITCH_PERIOD) { 1176e705c121SKalle Valo IWL_DEBUG_TE(mvm, "CS period is already scheduled\n"); 1177e705c121SKalle Valo return -EBUSY; 1178e705c121SKalle Valo } 1179e705c121SKalle Valo 11803edfb5f4SAvraham Stern /* 11813edfb5f4SAvraham Stern * Remove the session protection time event to allow the 11823edfb5f4SAvraham Stern * channel switch. If we got here, we just heard a beacon so 11833edfb5f4SAvraham Stern * the session protection is not needed anymore anyway. 11843edfb5f4SAvraham Stern */ 11853edfb5f4SAvraham Stern iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 11863edfb5f4SAvraham Stern } 11873edfb5f4SAvraham Stern 1188e705c121SKalle Valo time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 1189e705c121SKalle Valo time_cmd.id_and_color = 1190e705c121SKalle Valo cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 1191e705c121SKalle Valo time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD); 1192e705c121SKalle Valo time_cmd.apply_time = cpu_to_le32(apply_time); 1193e705c121SKalle Valo time_cmd.max_frags = TE_V2_FRAG_NONE; 1194e705c121SKalle Valo time_cmd.duration = cpu_to_le32(duration); 1195e705c121SKalle Valo time_cmd.repeat = 1; 1196e705c121SKalle Valo time_cmd.interval = cpu_to_le32(1); 1197e705c121SKalle Valo time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 1198e705c121SKalle Valo TE_V2_ABSENCE); 119940d53f4aSAndrei Otcheretianski if (!apply_time) 120040d53f4aSAndrei Otcheretianski time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY); 1201e705c121SKalle Valo 1202e705c121SKalle Valo return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 1203e705c121SKalle Valo } 1204fe959c7bSEmmanuel Grumbach 1205b5b878e3SEmmanuel Grumbach static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait, 1206b5b878e3SEmmanuel Grumbach struct iwl_rx_packet *pkt, void *data) 1207b5b878e3SEmmanuel Grumbach { 1208b5b878e3SEmmanuel Grumbach struct iwl_mvm *mvm = 1209b5b878e3SEmmanuel Grumbach container_of(notif_wait, struct iwl_mvm, notif_wait); 1210b5b878e3SEmmanuel Grumbach struct iwl_mvm_session_prot_notif *resp; 1211b5b878e3SEmmanuel Grumbach int resp_len = iwl_rx_packet_payload_len(pkt); 1212b5b878e3SEmmanuel Grumbach 1213b5b878e3SEmmanuel Grumbach if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF || 1214b5b878e3SEmmanuel Grumbach pkt->hdr.group_id != MAC_CONF_GROUP)) 1215b5b878e3SEmmanuel Grumbach return true; 1216b5b878e3SEmmanuel Grumbach 1217b5b878e3SEmmanuel Grumbach if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 1218b5b878e3SEmmanuel Grumbach IWL_ERR(mvm, "Invalid SESSION_PROTECTION_NOTIF response\n"); 1219b5b878e3SEmmanuel Grumbach return true; 1220b5b878e3SEmmanuel Grumbach } 1221b5b878e3SEmmanuel Grumbach 1222b5b878e3SEmmanuel Grumbach resp = (void *)pkt->data; 1223b5b878e3SEmmanuel Grumbach 1224b5b878e3SEmmanuel Grumbach if (!resp->status) 1225b5b878e3SEmmanuel Grumbach IWL_ERR(mvm, 1226b5b878e3SEmmanuel Grumbach "TIME_EVENT_NOTIFICATION received but not executed\n"); 1227b5b878e3SEmmanuel Grumbach 1228b5b878e3SEmmanuel Grumbach return true; 1229b5b878e3SEmmanuel Grumbach } 1230b5b878e3SEmmanuel Grumbach 1231fe959c7bSEmmanuel Grumbach void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, 1232fe959c7bSEmmanuel Grumbach struct ieee80211_vif *vif, 1233b5b878e3SEmmanuel Grumbach u32 duration, u32 min_duration, 1234b5b878e3SEmmanuel Grumbach bool wait_for_notif) 1235fe959c7bSEmmanuel Grumbach { 1236fe959c7bSEmmanuel Grumbach struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 1237fe959c7bSEmmanuel Grumbach struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 1238f0c86427SJohannes Berg const u16 notif[] = { WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF) }; 1239b5b878e3SEmmanuel Grumbach struct iwl_notification_wait wait_notif; 1240fe959c7bSEmmanuel Grumbach struct iwl_mvm_session_prot_cmd cmd = { 1241fe959c7bSEmmanuel Grumbach .id_and_color = 1242fe959c7bSEmmanuel Grumbach cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 1243fe959c7bSEmmanuel Grumbach mvmvif->color)), 1244fe959c7bSEmmanuel Grumbach .action = cpu_to_le32(FW_CTXT_ACTION_ADD), 12458e967c13SShaul Triebitz .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), 1246fe959c7bSEmmanuel Grumbach .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), 1247fe959c7bSEmmanuel Grumbach }; 1248fe959c7bSEmmanuel Grumbach 1249fe959c7bSEmmanuel Grumbach lockdep_assert_held(&mvm->mutex); 1250fe959c7bSEmmanuel Grumbach 1251fe959c7bSEmmanuel Grumbach spin_lock_bh(&mvm->time_event_lock); 1252fe959c7bSEmmanuel Grumbach if (te_data->running && 1253fe959c7bSEmmanuel Grumbach time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { 1254fe959c7bSEmmanuel Grumbach IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", 1255fe959c7bSEmmanuel Grumbach jiffies_to_msecs(te_data->end_jiffies - jiffies)); 1256fe959c7bSEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 1257fe959c7bSEmmanuel Grumbach 1258fe959c7bSEmmanuel Grumbach return; 1259fe959c7bSEmmanuel Grumbach } 1260fe959c7bSEmmanuel Grumbach 1261fe959c7bSEmmanuel Grumbach iwl_mvm_te_clear_data(mvm, te_data); 12628e967c13SShaul Triebitz /* 12638e967c13SShaul Triebitz * The time_event_data.id field is reused to save session 12648e967c13SShaul Triebitz * protection's configuration. 12658e967c13SShaul Triebitz */ 12668e967c13SShaul Triebitz te_data->id = le32_to_cpu(cmd.conf_id); 1267fe959c7bSEmmanuel Grumbach te_data->duration = le32_to_cpu(cmd.duration_tu); 12687b3954a1SIlan Peer te_data->vif = vif; 1269fe959c7bSEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 1270fe959c7bSEmmanuel Grumbach 1271fe959c7bSEmmanuel Grumbach IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n", 1272fe959c7bSEmmanuel Grumbach le32_to_cpu(cmd.duration_tu)); 1273fe959c7bSEmmanuel Grumbach 1274b5b878e3SEmmanuel Grumbach if (!wait_for_notif) { 1275b5b878e3SEmmanuel Grumbach if (iwl_mvm_send_cmd_pdu(mvm, 1276f0c86427SJohannes Berg WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 1277b5b878e3SEmmanuel Grumbach 0, sizeof(cmd), &cmd)) { 1278fe959c7bSEmmanuel Grumbach IWL_ERR(mvm, 1279b5b878e3SEmmanuel Grumbach "Couldn't send the SESSION_PROTECTION_CMD\n"); 1280fe959c7bSEmmanuel Grumbach spin_lock_bh(&mvm->time_event_lock); 1281fe959c7bSEmmanuel Grumbach iwl_mvm_te_clear_data(mvm, te_data); 1282fe959c7bSEmmanuel Grumbach spin_unlock_bh(&mvm->time_event_lock); 1283fe959c7bSEmmanuel Grumbach } 1284b5b878e3SEmmanuel Grumbach 1285b5b878e3SEmmanuel Grumbach return; 1286b5b878e3SEmmanuel Grumbach } 1287b5b878e3SEmmanuel Grumbach 1288b5b878e3SEmmanuel Grumbach iwl_init_notification_wait(&mvm->notif_wait, &wait_notif, 1289b5b878e3SEmmanuel Grumbach notif, ARRAY_SIZE(notif), 1290b5b878e3SEmmanuel Grumbach iwl_mvm_session_prot_notif, NULL); 1291b5b878e3SEmmanuel Grumbach 1292b5b878e3SEmmanuel Grumbach if (iwl_mvm_send_cmd_pdu(mvm, 1293f0c86427SJohannes Berg WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 1294b5b878e3SEmmanuel Grumbach 0, sizeof(cmd), &cmd)) { 1295b5b878e3SEmmanuel Grumbach IWL_ERR(mvm, 1296b5b878e3SEmmanuel Grumbach "Couldn't send the SESSION_PROTECTION_CMD\n"); 1297b5b878e3SEmmanuel Grumbach iwl_remove_notification(&mvm->notif_wait, &wait_notif); 1298b5b878e3SEmmanuel Grumbach } else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif, 1299b5b878e3SEmmanuel Grumbach TU_TO_JIFFIES(100))) { 1300b5b878e3SEmmanuel Grumbach IWL_ERR(mvm, 1301b5b878e3SEmmanuel Grumbach "Failed to protect session until session protection\n"); 1302b5b878e3SEmmanuel Grumbach } 1303fe959c7bSEmmanuel Grumbach } 1304