18c2ecf20Sopenharmony_ci/****************************************************************************** 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 98c2ecf20Sopenharmony_ci * Copyright(c) 2017 Intel Deutschland GmbH 108c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 138c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 148c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 178c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 188c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 198c2ecf20Sopenharmony_ci * General Public License for more details. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution 228c2ecf20Sopenharmony_ci * in the file called COPYING. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Contact Information: 258c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 268c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * BSD LICENSE 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 318c2ecf20Sopenharmony_ci * Copyright(c) 2017 Intel Deutschland GmbH 328c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation 338c2ecf20Sopenharmony_ci * All rights reserved. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 368c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 378c2ecf20Sopenharmony_ci * are met: 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 408c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 418c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 428c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 438c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 448c2ecf20Sopenharmony_ci * distribution. 458c2ecf20Sopenharmony_ci * * Neither the name Intel Corporation nor the names of its 468c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 478c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 508c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 518c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 528c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 538c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 548c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 558c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 568c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 578c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 588c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 598c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci *****************************************************************************/ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 648c2ecf20Sopenharmony_ci#include <net/mac80211.h> 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#include "fw/notif-wait.h" 678c2ecf20Sopenharmony_ci#include "iwl-trans.h" 688c2ecf20Sopenharmony_ci#include "fw-api.h" 698c2ecf20Sopenharmony_ci#include "time-event.h" 708c2ecf20Sopenharmony_ci#include "mvm.h" 718c2ecf20Sopenharmony_ci#include "iwl-io.h" 728c2ecf20Sopenharmony_ci#include "iwl-prph.h" 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * For the high priority TE use a time event type that has similar priority to 768c2ecf20Sopenharmony_ci * the FW's action scan priority. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE 798c2ecf20Sopenharmony_ci#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_civoid iwl_mvm_te_clear_data(struct iwl_mvm *mvm, 828c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->time_event_lock); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!te_data || !te_data->vif) 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci list_del(&te_data->list); 908c2ecf20Sopenharmony_ci te_data->running = false; 918c2ecf20Sopenharmony_ci te_data->uid = 0; 928c2ecf20Sopenharmony_ci te_data->id = TE_MAX; 938c2ecf20Sopenharmony_ci te_data->vif = NULL; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid iwl_mvm_roc_done_wk(struct work_struct *wk) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * Clear the ROC_RUNNING status bit. 1028c2ecf20Sopenharmony_ci * This will cause the TX path to drop offchannel transmissions. 1038c2ecf20Sopenharmony_ci * That would also be done by mac80211, but it is racy, in particular 1048c2ecf20Sopenharmony_ci * in the case that the time event actually completed in the firmware 1058c2ecf20Sopenharmony_ci * (which is handled in iwl_mvm_te_handle_notif). 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci synchronize_net(); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* 1128c2ecf20Sopenharmony_ci * Flush the offchannel queue -- this is called when the time 1138c2ecf20Sopenharmony_ci * event finishes or is canceled, so that frames queued for it 1148c2ecf20Sopenharmony_ci * won't get stuck on the queue and be transmitted in the next 1158c2ecf20Sopenharmony_ci * time event. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci mutex_lock(&mvm->mutex); 1198c2ecf20Sopenharmony_ci if (test_and_clear_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status)) { 1208c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* 1238c2ecf20Sopenharmony_ci * NB: access to this pointer would be racy, but the flush bit 1248c2ecf20Sopenharmony_ci * can only be set when we had a P2P-Device VIF, and we have a 1258c2ecf20Sopenharmony_ci * flush of this work in iwl_mvm_prepare_mac_removal() so it's 1268c2ecf20Sopenharmony_ci * not really racy. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!WARN_ON(!mvm->p2p_device_vif)) { 1308c2ecf20Sopenharmony_ci mvmvif = iwl_mvm_vif_from_mac80211(mvm->p2p_device_vif); 1318c2ecf20Sopenharmony_ci iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * Clear the ROC_AUX_RUNNING status bit. 1378c2ecf20Sopenharmony_ci * This will cause the TX path to drop offchannel transmissions. 1388c2ecf20Sopenharmony_ci * That would also be done by mac80211, but it is racy, in particular 1398c2ecf20Sopenharmony_ci * in the case that the time event actually completed in the firmware 1408c2ecf20Sopenharmony_ci * (which is handled in iwl_mvm_te_handle_notif). 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { 1438c2ecf20Sopenharmony_ci /* do the same in case of hot spot 2.0 */ 1448c2ecf20Sopenharmony_ci iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* In newer version of this command an aux station is added only 1478c2ecf20Sopenharmony_ci * in cases of dedicated tx queue and need to be removed in end 1488c2ecf20Sopenharmony_ci * of use */ 1498c2ecf20Sopenharmony_ci if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, 1508c2ecf20Sopenharmony_ci ADD_STA, 0) >= 12) 1518c2ecf20Sopenharmony_ci iwl_mvm_rm_aux_sta(mvm); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci mutex_unlock(&mvm->mutex); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void iwl_mvm_roc_finished(struct iwl_mvm *mvm) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * Of course, our status bit is just as racy as mac80211, so in 1618c2ecf20Sopenharmony_ci * addition, fire off the work struct which will drop all frames 1628c2ecf20Sopenharmony_ci * from the hardware queues that made it through the race. First 1638c2ecf20Sopenharmony_ci * it will of course synchronize the TX path to make sure that 1648c2ecf20Sopenharmony_ci * any *new* TX will be rejected. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci schedule_work(&mvm->roc_done_wk); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct ieee80211_vif *csa_vif; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci rcu_read_lock(); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci csa_vif = rcu_dereference(mvm->csa_vif); 1768c2ecf20Sopenharmony_ci if (!csa_vif || !csa_vif->csa_active) 1778c2ecf20Sopenharmony_ci goto out_unlock; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "CSA NOA started\n"); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * CSA NoA is started but we still have beacons to 1838c2ecf20Sopenharmony_ci * transmit on the current channel. 1848c2ecf20Sopenharmony_ci * So we just do nothing here and the switch 1858c2ecf20Sopenharmony_ci * will be performed on the last TBTT. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci if (!ieee80211_beacon_cntdwn_is_complete(csa_vif)) { 1888c2ecf20Sopenharmony_ci IWL_WARN(mvm, "CSA NOA started too early\n"); 1898c2ecf20Sopenharmony_ci goto out_unlock; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ieee80211_csa_finish(csa_vif); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rcu_read_unlock(); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci RCU_INIT_POINTER(mvm->csa_vif, NULL); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ciout_unlock: 2018c2ecf20Sopenharmony_ci rcu_read_unlock(); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, 2058c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 2068c2ecf20Sopenharmony_ci const char *errmsg) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (vif->type != NL80211_IFTYPE_STATION) 2118c2ecf20Sopenharmony_ci return false; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!mvmvif->csa_bcn_pending && vif->bss_conf.assoc && 2148c2ecf20Sopenharmony_ci vif->bss_conf.dtim_period) 2158c2ecf20Sopenharmony_ci return false; 2168c2ecf20Sopenharmony_ci if (errmsg) 2178c2ecf20Sopenharmony_ci IWL_ERR(mvm, "%s\n", errmsg); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci iwl_mvm_connection_loss(mvm, vif, errmsg); 2208c2ecf20Sopenharmony_ci return true; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void 2248c2ecf20Sopenharmony_ciiwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, 2258c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data, 2268c2ecf20Sopenharmony_ci struct iwl_time_event_notif *notif) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = te_data->vif; 2298c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!notif->status) 2328c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci switch (te_data->vif->type) { 2358c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 2368c2ecf20Sopenharmony_ci if (!notif->status) 2378c2ecf20Sopenharmony_ci mvmvif->csa_failed = true; 2388c2ecf20Sopenharmony_ci iwl_mvm_csa_noa_start(mvm); 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 2418c2ecf20Sopenharmony_ci if (!notif->status) { 2428c2ecf20Sopenharmony_ci iwl_mvm_connection_loss(mvm, vif, 2438c2ecf20Sopenharmony_ci "CSA TE failed to start"); 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci iwl_mvm_csa_client_absent(mvm, te_data->vif); 2478c2ecf20Sopenharmony_ci cancel_delayed_work(&mvmvif->csa_work); 2488c2ecf20Sopenharmony_ci ieee80211_chswitch_done(te_data->vif, true); 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci /* should never happen */ 2528c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* we don't need it anymore */ 2578c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, 2618c2ecf20Sopenharmony_ci struct iwl_time_event_notif *notif, 2628c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_tlv *trig; 2658c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_time_event *te_trig; 2668c2ecf20Sopenharmony_ci int i; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, 2698c2ecf20Sopenharmony_ci ieee80211_vif_to_wdev(te_data->vif), 2708c2ecf20Sopenharmony_ci FW_DBG_TRIGGER_TIME_EVENT); 2718c2ecf20Sopenharmony_ci if (!trig) 2728c2ecf20Sopenharmony_ci return; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci te_trig = (void *)trig->data; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { 2778c2ecf20Sopenharmony_ci u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id); 2788c2ecf20Sopenharmony_ci u32 trig_action_bitmap = 2798c2ecf20Sopenharmony_ci le32_to_cpu(te_trig->time_events[i].action_bitmap); 2808c2ecf20Sopenharmony_ci u32 trig_status_bitmap = 2818c2ecf20Sopenharmony_ci le32_to_cpu(te_trig->time_events[i].status_bitmap); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (trig_te_id != te_data->id || 2848c2ecf20Sopenharmony_ci !(trig_action_bitmap & le32_to_cpu(notif->action)) || 2858c2ecf20Sopenharmony_ci !(trig_status_bitmap & BIT(le32_to_cpu(notif->status)))) 2868c2ecf20Sopenharmony_ci continue; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, 2898c2ecf20Sopenharmony_ci "Time event %d Action 0x%x received status: %d", 2908c2ecf20Sopenharmony_ci te_data->id, 2918c2ecf20Sopenharmony_ci le32_to_cpu(notif->action), 2928c2ecf20Sopenharmony_ci le32_to_cpu(notif->status)); 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * Handles a FW notification for an event that is known to the driver. 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * @mvm: the mvm component 3018c2ecf20Sopenharmony_ci * @te_data: the time event data 3028c2ecf20Sopenharmony_ci * @notif: the notification data corresponding the time event data. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, 3058c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data, 3068c2ecf20Sopenharmony_ci struct iwl_time_event_notif *notif) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->time_event_lock); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n", 3118c2ecf20Sopenharmony_ci le32_to_cpu(notif->unique_id), 3128c2ecf20Sopenharmony_ci le32_to_cpu(notif->action)); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci iwl_mvm_te_check_trigger(mvm, notif, te_data); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * The FW sends the start/end time event notifications even for events 3188c2ecf20Sopenharmony_ci * that it fails to schedule. This is indicated in the status field of 3198c2ecf20Sopenharmony_ci * the notification. This happens in cases that the scheduler cannot 3208c2ecf20Sopenharmony_ci * find a schedule that can handle the event (for example requesting a 3218c2ecf20Sopenharmony_ci * P2P Device discoveribility, while there are other higher priority 3228c2ecf20Sopenharmony_ci * events in the system). 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci if (!le32_to_cpu(notif->status)) { 3258c2ecf20Sopenharmony_ci const char *msg; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START)) 3288c2ecf20Sopenharmony_ci msg = "Time Event start notification failure"; 3298c2ecf20Sopenharmony_ci else 3308c2ecf20Sopenharmony_ci msg = "Time Event end notification failure"; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "%s\n", msg); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) { 3358c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 3368c2ecf20Sopenharmony_ci return; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) { 3418c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, 3428c2ecf20Sopenharmony_ci "TE ended - current time %lu, estimated end %lu\n", 3438c2ecf20Sopenharmony_ci jiffies, te_data->end_jiffies); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci switch (te_data->vif->type) { 3468c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 3478c2ecf20Sopenharmony_ci ieee80211_remain_on_channel_expired(mvm->hw); 3488c2ecf20Sopenharmony_ci set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); 3498c2ecf20Sopenharmony_ci iwl_mvm_roc_finished(mvm); 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * By now, we should have finished association 3548c2ecf20Sopenharmony_ci * and know the dtim period. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci iwl_mvm_te_check_disconnect(mvm, te_data->vif, 3578c2ecf20Sopenharmony_ci !te_data->vif->bss_conf.assoc ? 3588c2ecf20Sopenharmony_ci "Not associated and the time event is over already..." : 3598c2ecf20Sopenharmony_ci "No beacon heard and the time event is over already..."); 3608c2ecf20Sopenharmony_ci break; 3618c2ecf20Sopenharmony_ci default: 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 3668c2ecf20Sopenharmony_ci } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) { 3678c2ecf20Sopenharmony_ci te_data->running = true; 3688c2ecf20Sopenharmony_ci te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { 3718c2ecf20Sopenharmony_ci set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 3728c2ecf20Sopenharmony_ci ieee80211_ready_on_channel(mvm->hw); 3738c2ecf20Sopenharmony_ci } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { 3748c2ecf20Sopenharmony_ci iwl_mvm_te_handle_notify_csa(mvm, te_data, notif); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci } else { 3778c2ecf20Sopenharmony_ci IWL_WARN(mvm, "Got TE with unknown action\n"); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* 3828c2ecf20Sopenharmony_ci * Handle A Aux ROC time event 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_cistatic int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, 3858c2ecf20Sopenharmony_ci struct iwl_time_event_notif *notif) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data, *tmp; 3888c2ecf20Sopenharmony_ci bool aux_roc_te = false; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) { 3918c2ecf20Sopenharmony_ci if (le32_to_cpu(notif->unique_id) == te_data->uid) { 3928c2ecf20Sopenharmony_ci aux_roc_te = true; 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci if (!aux_roc_te) /* Not a Aux ROC time event */ 3978c2ecf20Sopenharmony_ci return -EINVAL; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci iwl_mvm_te_check_trigger(mvm, notif, te_data); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, 4028c2ecf20Sopenharmony_ci "Aux ROC time event notification - UID = 0x%x action %d (error = %d)\n", 4038c2ecf20Sopenharmony_ci le32_to_cpu(notif->unique_id), 4048c2ecf20Sopenharmony_ci le32_to_cpu(notif->action), le32_to_cpu(notif->status)); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!le32_to_cpu(notif->status) || 4078c2ecf20Sopenharmony_ci le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) { 4088c2ecf20Sopenharmony_ci /* End TE, notify mac80211 */ 4098c2ecf20Sopenharmony_ci ieee80211_remain_on_channel_expired(mvm->hw); 4108c2ecf20Sopenharmony_ci iwl_mvm_roc_finished(mvm); /* flush aux queue */ 4118c2ecf20Sopenharmony_ci list_del(&te_data->list); /* remove from list */ 4128c2ecf20Sopenharmony_ci te_data->running = false; 4138c2ecf20Sopenharmony_ci te_data->vif = NULL; 4148c2ecf20Sopenharmony_ci te_data->uid = 0; 4158c2ecf20Sopenharmony_ci te_data->id = TE_MAX; 4168c2ecf20Sopenharmony_ci } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) { 4178c2ecf20Sopenharmony_ci set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); 4188c2ecf20Sopenharmony_ci te_data->running = true; 4198c2ecf20Sopenharmony_ci ieee80211_ready_on_channel(mvm->hw); /* Start TE */ 4208c2ecf20Sopenharmony_ci } else { 4218c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, 4228c2ecf20Sopenharmony_ci "ERROR: Unknown Aux ROC Time Event (action = %d)\n", 4238c2ecf20Sopenharmony_ci le32_to_cpu(notif->action)); 4248c2ecf20Sopenharmony_ci return -EINVAL; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* 4318c2ecf20Sopenharmony_ci * The Rx handler for time event notifications 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_civoid iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, 4348c2ecf20Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 4378c2ecf20Sopenharmony_ci struct iwl_time_event_notif *notif = (void *)pkt->data; 4388c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data, *tmp; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n", 4418c2ecf20Sopenharmony_ci le32_to_cpu(notif->unique_id), 4428c2ecf20Sopenharmony_ci le32_to_cpu(notif->action)); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 4458c2ecf20Sopenharmony_ci /* This time event is triggered for Aux ROC request */ 4468c2ecf20Sopenharmony_ci if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif)) 4478c2ecf20Sopenharmony_ci goto unlock; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) { 4508c2ecf20Sopenharmony_ci if (le32_to_cpu(notif->unique_id) == te_data->uid) 4518c2ecf20Sopenharmony_ci iwl_mvm_te_handle_notif(mvm, te_data, notif); 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ciunlock: 4548c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait, 4588c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt, void *data) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = 4618c2ecf20Sopenharmony_ci container_of(notif_wait, struct iwl_mvm, notif_wait); 4628c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = data; 4638c2ecf20Sopenharmony_ci struct iwl_time_event_notif *resp; 4648c2ecf20Sopenharmony_ci int resp_len = iwl_rx_packet_payload_len(pkt); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION)) 4678c2ecf20Sopenharmony_ci return true; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 4708c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n"); 4718c2ecf20Sopenharmony_ci return true; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci resp = (void *)pkt->data; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* te_data->uid is already set in the TIME_EVENT_CMD response */ 4778c2ecf20Sopenharmony_ci if (le32_to_cpu(resp->unique_id) != te_data->uid) 4788c2ecf20Sopenharmony_ci return false; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n", 4818c2ecf20Sopenharmony_ci te_data->uid); 4828c2ecf20Sopenharmony_ci if (!resp->status) 4838c2ecf20Sopenharmony_ci IWL_ERR(mvm, 4848c2ecf20Sopenharmony_ci "TIME_EVENT_NOTIFICATION received but not executed\n"); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return true; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, 4908c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt, void *data) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = 4938c2ecf20Sopenharmony_ci container_of(notif_wait, struct iwl_mvm, notif_wait); 4948c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = data; 4958c2ecf20Sopenharmony_ci struct iwl_time_event_resp *resp; 4968c2ecf20Sopenharmony_ci int resp_len = iwl_rx_packet_payload_len(pkt); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) 4998c2ecf20Sopenharmony_ci return true; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 5028c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); 5038c2ecf20Sopenharmony_ci return true; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci resp = (void *)pkt->data; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* we should never get a response to another TIME_EVENT_CMD here */ 5098c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id)) 5108c2ecf20Sopenharmony_ci return false; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci te_data->uid = le32_to_cpu(resp->unique_id); 5138c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", 5148c2ecf20Sopenharmony_ci te_data->uid); 5158c2ecf20Sopenharmony_ci return true; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, 5198c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 5208c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data, 5218c2ecf20Sopenharmony_ci struct iwl_time_event_cmd *te_cmd) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci static const u16 time_event_response[] = { TIME_EVENT_CMD }; 5248c2ecf20Sopenharmony_ci struct iwl_notification_wait wait_time_event; 5258c2ecf20Sopenharmony_ci int ret; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n", 5308c2ecf20Sopenharmony_ci le32_to_cpu(te_cmd->duration)); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 5338c2ecf20Sopenharmony_ci if (WARN_ON(te_data->id != TE_MAX)) { 5348c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 5358c2ecf20Sopenharmony_ci return -EIO; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci te_data->vif = vif; 5388c2ecf20Sopenharmony_ci te_data->duration = le32_to_cpu(te_cmd->duration); 5398c2ecf20Sopenharmony_ci te_data->id = le32_to_cpu(te_cmd->id); 5408c2ecf20Sopenharmony_ci list_add_tail(&te_data->list, &mvm->time_event_list); 5418c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* 5448c2ecf20Sopenharmony_ci * Use a notification wait, which really just processes the 5458c2ecf20Sopenharmony_ci * command response and doesn't wait for anything, in order 5468c2ecf20Sopenharmony_ci * to be able to process the response and get the UID inside 5478c2ecf20Sopenharmony_ci * the RX path. Using CMD_WANT_SKB doesn't work because it 5488c2ecf20Sopenharmony_ci * stores the buffer and then wakes up this thread, by which 5498c2ecf20Sopenharmony_ci * time another notification (that the time event started) 5508c2ecf20Sopenharmony_ci * might already be processed unsuccessfully. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_ci iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, 5538c2ecf20Sopenharmony_ci time_event_response, 5548c2ecf20Sopenharmony_ci ARRAY_SIZE(time_event_response), 5558c2ecf20Sopenharmony_ci iwl_mvm_time_event_response, te_data); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, 5588c2ecf20Sopenharmony_ci sizeof(*te_cmd), te_cmd); 5598c2ecf20Sopenharmony_ci if (ret) { 5608c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); 5618c2ecf20Sopenharmony_ci iwl_remove_notification(&mvm->notif_wait, &wait_time_event); 5628c2ecf20Sopenharmony_ci goto out_clear_te; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* No need to wait for anything, so just pass 1 (0 isn't valid) */ 5668c2ecf20Sopenharmony_ci ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); 5678c2ecf20Sopenharmony_ci /* should never fail */ 5688c2ecf20Sopenharmony_ci WARN_ON_ONCE(ret); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (ret) { 5718c2ecf20Sopenharmony_ci out_clear_te: 5728c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 5738c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 5748c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci return ret; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_civoid iwl_mvm_protect_session(struct iwl_mvm *mvm, 5808c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 5818c2ecf20Sopenharmony_ci u32 duration, u32 min_duration, 5828c2ecf20Sopenharmony_ci u32 max_delay, bool wait_for_notif) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 5858c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 5868c2ecf20Sopenharmony_ci const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION }; 5878c2ecf20Sopenharmony_ci struct iwl_notification_wait wait_te_notif; 5888c2ecf20Sopenharmony_ci struct iwl_time_event_cmd time_cmd = {}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (te_data->running && 5938c2ecf20Sopenharmony_ci time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { 5948c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", 5958c2ecf20Sopenharmony_ci jiffies_to_msecs(te_data->end_jiffies - jiffies)); 5968c2ecf20Sopenharmony_ci return; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (te_data->running) { 6008c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n", 6018c2ecf20Sopenharmony_ci te_data->uid, 6028c2ecf20Sopenharmony_ci jiffies_to_msecs(te_data->end_jiffies - jiffies)); 6038c2ecf20Sopenharmony_ci /* 6048c2ecf20Sopenharmony_ci * we don't have enough time 6058c2ecf20Sopenharmony_ci * cancel the current TE and issue a new one 6068c2ecf20Sopenharmony_ci * Of course it would be better to remove the old one only 6078c2ecf20Sopenharmony_ci * when the new one is added, but we don't care if we are off 6088c2ecf20Sopenharmony_ci * channel for a bit. All we need to do, is not to return 6098c2ecf20Sopenharmony_ci * before we actually begin to be on the channel. 6108c2ecf20Sopenharmony_ci */ 6118c2ecf20Sopenharmony_ci iwl_mvm_stop_session_protection(mvm, vif); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 6158c2ecf20Sopenharmony_ci time_cmd.id_and_color = 6168c2ecf20Sopenharmony_ci cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 6178c2ecf20Sopenharmony_ci time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci time_cmd.apply_time = cpu_to_le32(0); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci time_cmd.max_frags = TE_V2_FRAG_NONE; 6228c2ecf20Sopenharmony_ci time_cmd.max_delay = cpu_to_le32(max_delay); 6238c2ecf20Sopenharmony_ci /* TODO: why do we need to interval = bi if it is not periodic? */ 6248c2ecf20Sopenharmony_ci time_cmd.interval = cpu_to_le32(1); 6258c2ecf20Sopenharmony_ci time_cmd.duration = cpu_to_le32(duration); 6268c2ecf20Sopenharmony_ci time_cmd.repeat = 1; 6278c2ecf20Sopenharmony_ci time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 6288c2ecf20Sopenharmony_ci TE_V2_NOTIF_HOST_EVENT_END | 6298c2ecf20Sopenharmony_ci TE_V2_START_IMMEDIATELY); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (!wait_for_notif) { 6328c2ecf20Sopenharmony_ci iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 6338c2ecf20Sopenharmony_ci return; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * Create notification_wait for the TIME_EVENT_NOTIFICATION to use 6388c2ecf20Sopenharmony_ci * right after we send the time event 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif, 6418c2ecf20Sopenharmony_ci te_notif_response, 6428c2ecf20Sopenharmony_ci ARRAY_SIZE(te_notif_response), 6438c2ecf20Sopenharmony_ci iwl_mvm_te_notif, te_data); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* If TE was sent OK - wait for the notification that started */ 6468c2ecf20Sopenharmony_ci if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) { 6478c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to add TE to protect session\n"); 6488c2ecf20Sopenharmony_ci iwl_remove_notification(&mvm->notif_wait, &wait_te_notif); 6498c2ecf20Sopenharmony_ci } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif, 6508c2ecf20Sopenharmony_ci TU_TO_JIFFIES(max_delay))) { 6518c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to protect session until TE\n"); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, 6568c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct iwl_mvm_session_prot_cmd cmd = { 6598c2ecf20Sopenharmony_ci .id_and_color = 6608c2ecf20Sopenharmony_ci cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 6618c2ecf20Sopenharmony_ci mvmvif->color)), 6628c2ecf20Sopenharmony_ci .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 6638c2ecf20Sopenharmony_ci .conf_id = cpu_to_le32(mvmvif->time_event_data.id), 6648c2ecf20Sopenharmony_ci }; 6658c2ecf20Sopenharmony_ci int ret; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD, 6688c2ecf20Sopenharmony_ci MAC_CONF_GROUP, 0), 6698c2ecf20Sopenharmony_ci 0, sizeof(cmd), &cmd); 6708c2ecf20Sopenharmony_ci if (ret) 6718c2ecf20Sopenharmony_ci IWL_ERR(mvm, 6728c2ecf20Sopenharmony_ci "Couldn't send the SESSION_PROTECTION_CMD: %d\n", ret); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, 6768c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data, 6778c2ecf20Sopenharmony_ci u32 *uid) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci u32 id; 6808c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* 6838c2ecf20Sopenharmony_ci * It is possible that by the time we got to this point the time 6848c2ecf20Sopenharmony_ci * event was already removed. 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Save time event uid before clearing its data */ 6898c2ecf20Sopenharmony_ci *uid = te_data->uid; 6908c2ecf20Sopenharmony_ci id = te_data->id; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* 6938c2ecf20Sopenharmony_ci * The clear_data function handles time events that were already removed 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 6968c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* When session protection is supported, the te_data->id field 6998c2ecf20Sopenharmony_ci * is reused to save session protection's configuration. 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_ci if (fw_has_capa(&mvm->fw->ucode_capa, 7028c2ecf20Sopenharmony_ci IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { 7038c2ecf20Sopenharmony_ci if (mvmvif && id < SESSION_PROTECT_CONF_MAX_ID) { 7048c2ecf20Sopenharmony_ci /* Session protection is still ongoing. Cancel it */ 7058c2ecf20Sopenharmony_ci iwl_mvm_cancel_session_protection(mvm, mvmvif); 7068c2ecf20Sopenharmony_ci if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { 7078c2ecf20Sopenharmony_ci set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); 7088c2ecf20Sopenharmony_ci iwl_mvm_roc_finished(mvm); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci return false; 7128c2ecf20Sopenharmony_ci } else { 7138c2ecf20Sopenharmony_ci /* It is possible that by the time we try to remove it, the 7148c2ecf20Sopenharmony_ci * time event has already ended and removed. In such a case 7158c2ecf20Sopenharmony_ci * there is no need to send a removal command. 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ci if (id == TE_MAX) { 7188c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); 7198c2ecf20Sopenharmony_ci return false; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return true; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/* 7278c2ecf20Sopenharmony_ci * Explicit request to remove a aux roc time event. The removal of a time 7288c2ecf20Sopenharmony_ci * event needs to be synchronized with the flow of a time event's end 7298c2ecf20Sopenharmony_ci * notification, which also removes the time event from the op mode 7308c2ecf20Sopenharmony_ci * data structures. 7318c2ecf20Sopenharmony_ci */ 7328c2ecf20Sopenharmony_cistatic void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm, 7338c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif, 7348c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct iwl_hs20_roc_req aux_cmd = {}; 7378c2ecf20Sopenharmony_ci u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci u32 uid; 7408c2ecf20Sopenharmony_ci int ret; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) 7438c2ecf20Sopenharmony_ci return; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci aux_cmd.event_unique_id = cpu_to_le32(uid); 7468c2ecf20Sopenharmony_ci aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); 7478c2ecf20Sopenharmony_ci aux_cmd.id_and_color = 7488c2ecf20Sopenharmony_ci cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 7498c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n", 7508c2ecf20Sopenharmony_ci le32_to_cpu(aux_cmd.event_unique_id)); 7518c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, 7528c2ecf20Sopenharmony_ci len, &aux_cmd); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 7558c2ecf20Sopenharmony_ci return; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci/* 7598c2ecf20Sopenharmony_ci * Explicit request to remove a time event. The removal of a time event needs to 7608c2ecf20Sopenharmony_ci * be synchronized with the flow of a time event's end notification, which also 7618c2ecf20Sopenharmony_ci * removes the time event from the op mode data structures. 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_civoid iwl_mvm_remove_time_event(struct iwl_mvm *mvm, 7648c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif, 7658c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct iwl_time_event_cmd time_cmd = {}; 7688c2ecf20Sopenharmony_ci u32 uid; 7698c2ecf20Sopenharmony_ci int ret; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) 7728c2ecf20Sopenharmony_ci return; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* When we remove a TE, the UID is to be set in the id field */ 7758c2ecf20Sopenharmony_ci time_cmd.id = cpu_to_le32(uid); 7768c2ecf20Sopenharmony_ci time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); 7778c2ecf20Sopenharmony_ci time_cmd.id_and_color = 7788c2ecf20Sopenharmony_ci cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); 7818c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, 7828c2ecf20Sopenharmony_ci sizeof(time_cmd), &time_cmd); 7838c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 7848c2ecf20Sopenharmony_ci return; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci/* 7888c2ecf20Sopenharmony_ci * When the firmware supports the session protection API, 7898c2ecf20Sopenharmony_ci * this is not needed since it'll automatically remove the 7908c2ecf20Sopenharmony_ci * session protection after association + beacon reception. 7918c2ecf20Sopenharmony_ci */ 7928c2ecf20Sopenharmony_civoid iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, 7938c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 7968c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 7978c2ecf20Sopenharmony_ci u32 id; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 8028c2ecf20Sopenharmony_ci id = te_data->id; 8038c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (id != TE_BSS_STA_AGGRESSIVE_ASSOC) { 8068c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, 8078c2ecf20Sopenharmony_ci "don't remove TE with id=%u (not session protection)\n", 8088c2ecf20Sopenharmony_ci id); 8098c2ecf20Sopenharmony_ci return; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_civoid iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, 8168c2ecf20Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 8198c2ecf20Sopenharmony_ci struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data; 8208c2ecf20Sopenharmony_ci struct ieee80211_vif *vif; 8218c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci rcu_read_lock(); 8248c2ecf20Sopenharmony_ci vif = iwl_mvm_rcu_dereference_vif_id(mvm, le32_to_cpu(notif->mac_id), 8258c2ecf20Sopenharmony_ci true); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (!vif) 8288c2ecf20Sopenharmony_ci goto out_unlock; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci mvmvif = iwl_mvm_vif_from_mac80211(vif); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* The vif is not a P2P_DEVICE, maintain its time_event_data */ 8338c2ecf20Sopenharmony_ci if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { 8348c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = 8358c2ecf20Sopenharmony_ci &mvmvif->time_event_data; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (!le32_to_cpu(notif->status)) { 8388c2ecf20Sopenharmony_ci iwl_mvm_te_check_disconnect(mvm, vif, 8398c2ecf20Sopenharmony_ci "Session protection failure"); 8408c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 8418c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 8428c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (le32_to_cpu(notif->start)) { 8468c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 8478c2ecf20Sopenharmony_ci te_data->running = le32_to_cpu(notif->start); 8488c2ecf20Sopenharmony_ci te_data->end_jiffies = 8498c2ecf20Sopenharmony_ci TU_TO_EXP_TIME(te_data->duration); 8508c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 8518c2ecf20Sopenharmony_ci } else { 8528c2ecf20Sopenharmony_ci /* 8538c2ecf20Sopenharmony_ci * By now, we should have finished association 8548c2ecf20Sopenharmony_ci * and know the dtim period. 8558c2ecf20Sopenharmony_ci */ 8568c2ecf20Sopenharmony_ci iwl_mvm_te_check_disconnect(mvm, vif, 8578c2ecf20Sopenharmony_ci !vif->bss_conf.assoc ? 8588c2ecf20Sopenharmony_ci "Not associated and the session protection is over already..." : 8598c2ecf20Sopenharmony_ci "No beacon heard and the session protection is over already..."); 8608c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 8618c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 8628c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci goto out_unlock; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) { 8698c2ecf20Sopenharmony_ci /* End TE, notify mac80211 */ 8708c2ecf20Sopenharmony_ci mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID; 8718c2ecf20Sopenharmony_ci ieee80211_remain_on_channel_expired(mvm->hw); 8728c2ecf20Sopenharmony_ci set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); 8738c2ecf20Sopenharmony_ci iwl_mvm_roc_finished(mvm); 8748c2ecf20Sopenharmony_ci } else if (le32_to_cpu(notif->start)) { 8758c2ecf20Sopenharmony_ci if (WARN_ON(mvmvif->time_event_data.id != 8768c2ecf20Sopenharmony_ci le32_to_cpu(notif->conf_id))) 8778c2ecf20Sopenharmony_ci goto out_unlock; 8788c2ecf20Sopenharmony_ci set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 8798c2ecf20Sopenharmony_ci ieee80211_ready_on_channel(mvm->hw); /* Start TE */ 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci out_unlock: 8838c2ecf20Sopenharmony_ci rcu_read_unlock(); 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic int 8878c2ecf20Sopenharmony_ciiwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, 8888c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 8898c2ecf20Sopenharmony_ci int duration, 8908c2ecf20Sopenharmony_ci enum ieee80211_roc_type type) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 8938c2ecf20Sopenharmony_ci struct iwl_mvm_session_prot_cmd cmd = { 8948c2ecf20Sopenharmony_ci .id_and_color = 8958c2ecf20Sopenharmony_ci cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 8968c2ecf20Sopenharmony_ci mvmvif->color)), 8978c2ecf20Sopenharmony_ci .action = cpu_to_le32(FW_CTXT_ACTION_ADD), 8988c2ecf20Sopenharmony_ci .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), 8998c2ecf20Sopenharmony_ci }; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* The time_event_data.id field is reused to save session 9048c2ecf20Sopenharmony_ci * protection's configuration. 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_ci switch (type) { 9078c2ecf20Sopenharmony_ci case IEEE80211_ROC_TYPE_NORMAL: 9088c2ecf20Sopenharmony_ci mvmvif->time_event_data.id = 9098c2ecf20Sopenharmony_ci SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV; 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci case IEEE80211_ROC_TYPE_MGMT_TX: 9128c2ecf20Sopenharmony_ci mvmvif->time_event_data.id = 9138c2ecf20Sopenharmony_ci SESSION_PROTECT_CONF_P2P_GO_NEGOTIATION; 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci default: 9168c2ecf20Sopenharmony_ci WARN_ONCE(1, "Got an invalid ROC type\n"); 9178c2ecf20Sopenharmony_ci return -EINVAL; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci cmd.conf_id = cpu_to_le32(mvmvif->time_event_data.id); 9218c2ecf20Sopenharmony_ci return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(SESSION_PROTECTION_CMD, 9228c2ecf20Sopenharmony_ci MAC_CONF_GROUP, 0), 9238c2ecf20Sopenharmony_ci 0, sizeof(cmd), &cmd); 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ciint iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 9278c2ecf20Sopenharmony_ci int duration, enum ieee80211_roc_type type) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 9308c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 9318c2ecf20Sopenharmony_ci struct iwl_time_event_cmd time_cmd = {}; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 9348c2ecf20Sopenharmony_ci if (te_data->running) { 9358c2ecf20Sopenharmony_ci IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n"); 9368c2ecf20Sopenharmony_ci return -EBUSY; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (fw_has_capa(&mvm->fw->ucode_capa, 9408c2ecf20Sopenharmony_ci IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) 9418c2ecf20Sopenharmony_ci return iwl_mvm_start_p2p_roc_session_protection(mvm, vif, 9428c2ecf20Sopenharmony_ci duration, 9438c2ecf20Sopenharmony_ci type); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 9468c2ecf20Sopenharmony_ci time_cmd.id_and_color = 9478c2ecf20Sopenharmony_ci cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci switch (type) { 9508c2ecf20Sopenharmony_ci case IEEE80211_ROC_TYPE_NORMAL: 9518c2ecf20Sopenharmony_ci time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL); 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci case IEEE80211_ROC_TYPE_MGMT_TX: 9548c2ecf20Sopenharmony_ci time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX); 9558c2ecf20Sopenharmony_ci break; 9568c2ecf20Sopenharmony_ci default: 9578c2ecf20Sopenharmony_ci WARN_ONCE(1, "Got an invalid ROC type\n"); 9588c2ecf20Sopenharmony_ci return -EINVAL; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci time_cmd.apply_time = cpu_to_le32(0); 9628c2ecf20Sopenharmony_ci time_cmd.interval = cpu_to_le32(1); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* 9658c2ecf20Sopenharmony_ci * The P2P Device TEs can have lower priority than other events 9668c2ecf20Sopenharmony_ci * that are being scheduled by the driver/fw, and thus it might not be 9678c2ecf20Sopenharmony_ci * scheduled. To improve the chances of it being scheduled, allow them 9688c2ecf20Sopenharmony_ci * to be fragmented, and in addition allow them to be delayed. 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_ci time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS); 9718c2ecf20Sopenharmony_ci time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2)); 9728c2ecf20Sopenharmony_ci time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); 9738c2ecf20Sopenharmony_ci time_cmd.repeat = 1; 9748c2ecf20Sopenharmony_ci time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 9758c2ecf20Sopenharmony_ci TE_V2_NOTIF_HOST_EVENT_END | 9768c2ecf20Sopenharmony_ci TE_V2_START_IMMEDIATELY); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* 9908c2ecf20Sopenharmony_ci * Iterate over the list of time events and find the time event that is 9918c2ecf20Sopenharmony_ci * associated with a P2P_DEVICE interface. 9928c2ecf20Sopenharmony_ci * This assumes that a P2P_DEVICE interface can have only a single time 9938c2ecf20Sopenharmony_ci * event at any given time and this time event coresponds to a ROC 9948c2ecf20Sopenharmony_ci * request 9958c2ecf20Sopenharmony_ci */ 9968c2ecf20Sopenharmony_ci list_for_each_entry(te_data, &mvm->time_event_list, list) { 9978c2ecf20Sopenharmony_ci if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) 9988c2ecf20Sopenharmony_ci goto out; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* There can only be at most one AUX ROC time event, we just use the 10028c2ecf20Sopenharmony_ci * list to simplify/unify code. Remove it if it exists. 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_ci te_data = list_first_entry_or_null(&mvm->aux_roc_te_list, 10058c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data, 10068c2ecf20Sopenharmony_ci list); 10078c2ecf20Sopenharmony_ciout: 10088c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 10098c2ecf20Sopenharmony_ci return te_data; 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_civoid iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data; 10158c2ecf20Sopenharmony_ci u32 uid; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci te_data = iwl_mvm_get_roc_te(mvm); 10188c2ecf20Sopenharmony_ci if (te_data) 10198c2ecf20Sopenharmony_ci __iwl_mvm_remove_time_event(mvm, te_data, &uid); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_civoid iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif; 10258c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (fw_has_capa(&mvm->fw->ucode_capa, 10288c2ecf20Sopenharmony_ci IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) { 10298c2ecf20Sopenharmony_ci mvmvif = iwl_mvm_vif_from_mac80211(vif); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { 10328c2ecf20Sopenharmony_ci iwl_mvm_cancel_session_protection(mvm, mvmvif); 10338c2ecf20Sopenharmony_ci set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); 10348c2ecf20Sopenharmony_ci } else { 10358c2ecf20Sopenharmony_ci iwl_mvm_remove_aux_roc_te(mvm, mvmvif, 10368c2ecf20Sopenharmony_ci &mvmvif->time_event_data); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci iwl_mvm_roc_finished(mvm); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci return; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci te_data = iwl_mvm_get_roc_te(mvm); 10458c2ecf20Sopenharmony_ci if (!te_data) { 10468c2ecf20Sopenharmony_ci IWL_WARN(mvm, "No remain on channel event\n"); 10478c2ecf20Sopenharmony_ci return; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { 10538c2ecf20Sopenharmony_ci iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 10548c2ecf20Sopenharmony_ci set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status); 10558c2ecf20Sopenharmony_ci } else { 10568c2ecf20Sopenharmony_ci iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data); 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci iwl_mvm_roc_finished(mvm); 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_civoid iwl_mvm_remove_csa_period(struct iwl_mvm *mvm, 10638c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 10668c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 10678c2ecf20Sopenharmony_ci u32 id; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 10728c2ecf20Sopenharmony_ci id = te_data->id; 10738c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (id != TE_CHANNEL_SWITCH_PERIOD) 10768c2ecf20Sopenharmony_ci return; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ciint iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, 10828c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 10838c2ecf20Sopenharmony_ci u32 duration, u32 apply_time) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 10868c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 10878c2ecf20Sopenharmony_ci struct iwl_time_event_cmd time_cmd = {}; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (te_data->running) { 10928c2ecf20Sopenharmony_ci u32 id; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 10958c2ecf20Sopenharmony_ci id = te_data->id; 10968c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (id == TE_CHANNEL_SWITCH_PERIOD) { 10998c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "CS period is already scheduled\n"); 11008c2ecf20Sopenharmony_ci return -EBUSY; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* 11048c2ecf20Sopenharmony_ci * Remove the session protection time event to allow the 11058c2ecf20Sopenharmony_ci * channel switch. If we got here, we just heard a beacon so 11068c2ecf20Sopenharmony_ci * the session protection is not needed anymore anyway. 11078c2ecf20Sopenharmony_ci */ 11088c2ecf20Sopenharmony_ci iwl_mvm_remove_time_event(mvm, mvmvif, te_data); 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); 11128c2ecf20Sopenharmony_ci time_cmd.id_and_color = 11138c2ecf20Sopenharmony_ci cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); 11148c2ecf20Sopenharmony_ci time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD); 11158c2ecf20Sopenharmony_ci time_cmd.apply_time = cpu_to_le32(apply_time); 11168c2ecf20Sopenharmony_ci time_cmd.max_frags = TE_V2_FRAG_NONE; 11178c2ecf20Sopenharmony_ci time_cmd.duration = cpu_to_le32(duration); 11188c2ecf20Sopenharmony_ci time_cmd.repeat = 1; 11198c2ecf20Sopenharmony_ci time_cmd.interval = cpu_to_le32(1); 11208c2ecf20Sopenharmony_ci time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | 11218c2ecf20Sopenharmony_ci TE_V2_ABSENCE); 11228c2ecf20Sopenharmony_ci if (!apply_time) 11238c2ecf20Sopenharmony_ci time_cmd.policy |= cpu_to_le16(TE_V2_START_IMMEDIATELY); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_cistatic bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait, 11298c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt, void *data) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = 11328c2ecf20Sopenharmony_ci container_of(notif_wait, struct iwl_mvm, notif_wait); 11338c2ecf20Sopenharmony_ci struct iwl_mvm_session_prot_notif *resp; 11348c2ecf20Sopenharmony_ci int resp_len = iwl_rx_packet_payload_len(pkt); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF || 11378c2ecf20Sopenharmony_ci pkt->hdr.group_id != MAC_CONF_GROUP)) 11388c2ecf20Sopenharmony_ci return true; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { 11418c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid SESSION_PROTECTION_NOTIF response\n"); 11428c2ecf20Sopenharmony_ci return true; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci resp = (void *)pkt->data; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (!resp->status) 11488c2ecf20Sopenharmony_ci IWL_ERR(mvm, 11498c2ecf20Sopenharmony_ci "TIME_EVENT_NOTIFICATION received but not executed\n"); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci return true; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_civoid iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, 11558c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 11568c2ecf20Sopenharmony_ci u32 duration, u32 min_duration, 11578c2ecf20Sopenharmony_ci bool wait_for_notif) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 11608c2ecf20Sopenharmony_ci struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; 11618c2ecf20Sopenharmony_ci const u16 notif[] = { iwl_cmd_id(SESSION_PROTECTION_NOTIF, 11628c2ecf20Sopenharmony_ci MAC_CONF_GROUP, 0) }; 11638c2ecf20Sopenharmony_ci struct iwl_notification_wait wait_notif; 11648c2ecf20Sopenharmony_ci struct iwl_mvm_session_prot_cmd cmd = { 11658c2ecf20Sopenharmony_ci .id_and_color = 11668c2ecf20Sopenharmony_ci cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, 11678c2ecf20Sopenharmony_ci mvmvif->color)), 11688c2ecf20Sopenharmony_ci .action = cpu_to_le32(FW_CTXT_ACTION_ADD), 11698c2ecf20Sopenharmony_ci .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), 11708c2ecf20Sopenharmony_ci .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), 11718c2ecf20Sopenharmony_ci }; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 11768c2ecf20Sopenharmony_ci if (te_data->running && 11778c2ecf20Sopenharmony_ci time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { 11788c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", 11798c2ecf20Sopenharmony_ci jiffies_to_msecs(te_data->end_jiffies - jiffies)); 11808c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci return; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 11868c2ecf20Sopenharmony_ci /* 11878c2ecf20Sopenharmony_ci * The time_event_data.id field is reused to save session 11888c2ecf20Sopenharmony_ci * protection's configuration. 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_ci te_data->id = le32_to_cpu(cmd.conf_id); 11918c2ecf20Sopenharmony_ci te_data->duration = le32_to_cpu(cmd.duration_tu); 11928c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n", 11958c2ecf20Sopenharmony_ci le32_to_cpu(cmd.duration_tu)); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (!wait_for_notif) { 11988c2ecf20Sopenharmony_ci if (iwl_mvm_send_cmd_pdu(mvm, 11998c2ecf20Sopenharmony_ci iwl_cmd_id(SESSION_PROTECTION_CMD, 12008c2ecf20Sopenharmony_ci MAC_CONF_GROUP, 0), 12018c2ecf20Sopenharmony_ci 0, sizeof(cmd), &cmd)) { 12028c2ecf20Sopenharmony_ci IWL_ERR(mvm, 12038c2ecf20Sopenharmony_ci "Couldn't send the SESSION_PROTECTION_CMD\n"); 12048c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->time_event_lock); 12058c2ecf20Sopenharmony_ci iwl_mvm_te_clear_data(mvm, te_data); 12068c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->time_event_lock); 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci return; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci iwl_init_notification_wait(&mvm->notif_wait, &wait_notif, 12138c2ecf20Sopenharmony_ci notif, ARRAY_SIZE(notif), 12148c2ecf20Sopenharmony_ci iwl_mvm_session_prot_notif, NULL); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci if (iwl_mvm_send_cmd_pdu(mvm, 12178c2ecf20Sopenharmony_ci iwl_cmd_id(SESSION_PROTECTION_CMD, 12188c2ecf20Sopenharmony_ci MAC_CONF_GROUP, 0), 12198c2ecf20Sopenharmony_ci 0, sizeof(cmd), &cmd)) { 12208c2ecf20Sopenharmony_ci IWL_ERR(mvm, 12218c2ecf20Sopenharmony_ci "Couldn't send the SESSION_PROTECTION_CMD\n"); 12228c2ecf20Sopenharmony_ci iwl_remove_notification(&mvm->notif_wait, &wait_notif); 12238c2ecf20Sopenharmony_ci } else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif, 12248c2ecf20Sopenharmony_ci TU_TO_JIFFIES(100))) { 12258c2ecf20Sopenharmony_ci IWL_ERR(mvm, 12268c2ecf20Sopenharmony_ci "Failed to protect session until session protection\n"); 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci} 1229