162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci * Copyright (C) 2018 - 2019, 2022 Intel Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Portions of this file are derived from the ipw3945 project, as well 862306a36Sopenharmony_ci * as portions of the ieee80211 subsystem header files. 962306a36Sopenharmony_ci *****************************************************************************/ 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/sched.h> 1662306a36Sopenharmony_ci#include <linux/skbuff.h> 1762306a36Sopenharmony_ci#include <linux/netdevice.h> 1862306a36Sopenharmony_ci#include <linux/etherdevice.h> 1962306a36Sopenharmony_ci#include <linux/if_arp.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <net/ieee80211_radiotap.h> 2262306a36Sopenharmony_ci#include <net/mac80211.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/div64.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "iwl-io.h" 2762306a36Sopenharmony_ci#include "iwl-trans.h" 2862306a36Sopenharmony_ci#include "iwl-op-mode.h" 2962306a36Sopenharmony_ci#include "iwl-modparams.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "dev.h" 3262306a36Sopenharmony_ci#include "calib.h" 3362306a36Sopenharmony_ci#include "agn.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/***************************************************************************** 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * mac80211 entry point functions 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci *****************************************************************************/ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = { 4262306a36Sopenharmony_ci { 4362306a36Sopenharmony_ci .max = 1, 4462306a36Sopenharmony_ci .types = BIT(NL80211_IFTYPE_STATION), 4562306a36Sopenharmony_ci }, 4662306a36Sopenharmony_ci { 4762306a36Sopenharmony_ci .max = 1, 4862306a36Sopenharmony_ci .types = BIT(NL80211_IFTYPE_AP), 4962306a36Sopenharmony_ci }, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { 5362306a36Sopenharmony_ci { 5462306a36Sopenharmony_ci .max = 2, 5562306a36Sopenharmony_ci .types = BIT(NL80211_IFTYPE_STATION), 5662306a36Sopenharmony_ci }, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic const struct ieee80211_iface_combination 6062306a36Sopenharmony_ciiwlagn_iface_combinations_dualmode[] = { 6162306a36Sopenharmony_ci { .num_different_channels = 1, 6262306a36Sopenharmony_ci .max_interfaces = 2, 6362306a36Sopenharmony_ci .beacon_int_infra_match = true, 6462306a36Sopenharmony_ci .limits = iwlagn_sta_ap_limits, 6562306a36Sopenharmony_ci .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits), 6662306a36Sopenharmony_ci }, 6762306a36Sopenharmony_ci { .num_different_channels = 1, 6862306a36Sopenharmony_ci .max_interfaces = 2, 6962306a36Sopenharmony_ci .limits = iwlagn_2sta_limits, 7062306a36Sopenharmony_ci .n_limits = ARRAY_SIZE(iwlagn_2sta_limits), 7162306a36Sopenharmony_ci }, 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Not a mac80211 entry point function, but it fits in with all the 7662306a36Sopenharmony_ci * other mac80211 functions grouped here. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ciint iwlagn_mac_setup_register(struct iwl_priv *priv, 7962306a36Sopenharmony_ci const struct iwl_ucode_capabilities *capa) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int ret; 8262306a36Sopenharmony_ci struct ieee80211_hw *hw = priv->hw; 8362306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci hw->rate_control_algorithm = "iwl-agn-rs"; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Tell mac80211 our characteristics */ 8862306a36Sopenharmony_ci ieee80211_hw_set(hw, SIGNAL_DBM); 8962306a36Sopenharmony_ci ieee80211_hw_set(hw, AMPDU_AGGREGATION); 9062306a36Sopenharmony_ci ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); 9162306a36Sopenharmony_ci ieee80211_hw_set(hw, SPECTRUM_MGMT); 9262306a36Sopenharmony_ci ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); 9362306a36Sopenharmony_ci ieee80211_hw_set(hw, QUEUE_CONTROL); 9462306a36Sopenharmony_ci ieee80211_hw_set(hw, SUPPORTS_PS); 9562306a36Sopenharmony_ci ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); 9662306a36Sopenharmony_ci ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); 9762306a36Sopenharmony_ci ieee80211_hw_set(hw, WANT_MONITOR_VIF); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (priv->trans->max_skb_frags) 10062306a36Sopenharmony_ci hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; 10362306a36Sopenharmony_ci hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * Including the following line will crash some AP's. This 10762306a36Sopenharmony_ci * workaround removes the stimulus which causes the crash until 10862306a36Sopenharmony_ci * the AP software can be fixed. 10962306a36Sopenharmony_ci hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (priv->nvm_data->sku_cap_11n_enable) 11362306a36Sopenharmony_ci hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS | 11462306a36Sopenharmony_ci NL80211_FEATURE_STATIC_SMPS; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * Enable 11w if advertised by firmware and software crypto 11862306a36Sopenharmony_ci * is not enabled (as the firmware will interpret some mgmt 11962306a36Sopenharmony_ci * packets, so enabling it with software crypto isn't safe) 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP && 12262306a36Sopenharmony_ci !iwlwifi_mod_params.swcrypto) 12362306a36Sopenharmony_ci ieee80211_hw_set(hw, MFP_CAPABLE); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci hw->sta_data_size = sizeof(struct iwl_station_priv); 12662306a36Sopenharmony_ci hw->vif_data_size = sizeof(struct iwl_vif_priv); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci for_each_context(priv, ctx) { 12962306a36Sopenharmony_ci hw->wiphy->interface_modes |= ctx->interface_modes; 13062306a36Sopenharmony_ci hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { 13662306a36Sopenharmony_ci hw->wiphy->iface_combinations = 13762306a36Sopenharmony_ci iwlagn_iface_combinations_dualmode; 13862306a36Sopenharmony_ci hw->wiphy->n_iface_combinations = 13962306a36Sopenharmony_ci ARRAY_SIZE(iwlagn_iface_combinations_dualmode); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; 14362306a36Sopenharmony_ci hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | 14462306a36Sopenharmony_ci REGULATORY_DISABLE_BEACON_HINTS; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 14762306a36Sopenharmony_ci if (priv->fw->img[IWL_UCODE_WOWLAN].num_sec && 14862306a36Sopenharmony_ci priv->trans->ops->d3_suspend && 14962306a36Sopenharmony_ci priv->trans->ops->d3_resume && 15062306a36Sopenharmony_ci device_can_wakeup(priv->trans->dev)) { 15162306a36Sopenharmony_ci priv->wowlan_support.flags = WIPHY_WOWLAN_MAGIC_PKT | 15262306a36Sopenharmony_ci WIPHY_WOWLAN_DISCONNECT | 15362306a36Sopenharmony_ci WIPHY_WOWLAN_EAP_IDENTITY_REQ | 15462306a36Sopenharmony_ci WIPHY_WOWLAN_RFKILL_RELEASE; 15562306a36Sopenharmony_ci if (!iwlwifi_mod_params.swcrypto) 15662306a36Sopenharmony_ci priv->wowlan_support.flags |= 15762306a36Sopenharmony_ci WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | 15862306a36Sopenharmony_ci WIPHY_WOWLAN_GTK_REKEY_FAILURE; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; 16162306a36Sopenharmony_ci priv->wowlan_support.pattern_min_len = 16262306a36Sopenharmony_ci IWLAGN_WOWLAN_MIN_PATTERN_LEN; 16362306a36Sopenharmony_ci priv->wowlan_support.pattern_max_len = 16462306a36Sopenharmony_ci IWLAGN_WOWLAN_MAX_PATTERN_LEN; 16562306a36Sopenharmony_ci hw->wiphy->wowlan = &priv->wowlan_support; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci#endif 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (iwlwifi_mod_params.power_save) 17062306a36Sopenharmony_ci hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; 17562306a36Sopenharmony_ci /* we create the 802.11 header and a max-length SSID element */ 17662306a36Sopenharmony_ci hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 34; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * We don't use all queues: 4 and 9 are unused and any 18062306a36Sopenharmony_ci * aggregation queue gets mapped down to the AC queue. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci hw->queues = IWLAGN_FIRST_AMPDU_QUEUE; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (priv->nvm_data->bands[NL80211_BAND_2GHZ].n_channels) 18762306a36Sopenharmony_ci priv->hw->wiphy->bands[NL80211_BAND_2GHZ] = 18862306a36Sopenharmony_ci &priv->nvm_data->bands[NL80211_BAND_2GHZ]; 18962306a36Sopenharmony_ci if (priv->nvm_data->bands[NL80211_BAND_5GHZ].n_channels) 19062306a36Sopenharmony_ci priv->hw->wiphy->bands[NL80211_BAND_5GHZ] = 19162306a36Sopenharmony_ci &priv->nvm_data->bands[NL80211_BAND_5GHZ]; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci hw->wiphy->hw_version = priv->trans->hw_id; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci iwl_leds_init(priv); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 19862306a36Sopenharmony_ci wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_EXT_KEY_ID); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci ret = ieee80211_register_hw(priv->hw); 20162306a36Sopenharmony_ci if (ret) { 20262306a36Sopenharmony_ci IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); 20362306a36Sopenharmony_ci iwl_leds_exit(priv); 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci priv->mac80211_registered = 1; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_civoid iwlagn_mac_unregister(struct iwl_priv *priv) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci if (!priv->mac80211_registered) 21462306a36Sopenharmony_ci return; 21562306a36Sopenharmony_ci iwl_leds_exit(priv); 21662306a36Sopenharmony_ci ieee80211_unregister_hw(priv->hw); 21762306a36Sopenharmony_ci priv->mac80211_registered = 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int __iwl_up(struct iwl_priv *priv) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 22362306a36Sopenharmony_ci int ret; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { 22862306a36Sopenharmony_ci IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); 22962306a36Sopenharmony_ci return -EIO; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci for_each_context(priv, ctx) { 23362306a36Sopenharmony_ci ret = iwlagn_alloc_bcast_station(priv, ctx); 23462306a36Sopenharmony_ci if (ret) { 23562306a36Sopenharmony_ci iwl_dealloc_bcast_stations(priv); 23662306a36Sopenharmony_ci return ret; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = iwl_trans_start_hw(priv->trans); 24162306a36Sopenharmony_ci if (ret) { 24262306a36Sopenharmony_ci IWL_ERR(priv, "Failed to start HW: %d\n", ret); 24362306a36Sopenharmony_ci goto error; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci ret = iwl_run_init_ucode(priv); 24762306a36Sopenharmony_ci if (ret) { 24862306a36Sopenharmony_ci IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret); 24962306a36Sopenharmony_ci goto error; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = iwl_trans_start_hw(priv->trans); 25362306a36Sopenharmony_ci if (ret) { 25462306a36Sopenharmony_ci IWL_ERR(priv, "Failed to start HW: %d\n", ret); 25562306a36Sopenharmony_ci goto error; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret); 26162306a36Sopenharmony_ci goto error; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ret = iwl_alive_start(priv); 26562306a36Sopenharmony_ci if (ret) 26662306a36Sopenharmony_ci goto error; 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci error: 27062306a36Sopenharmony_ci set_bit(STATUS_EXIT_PENDING, &priv->status); 27162306a36Sopenharmony_ci iwl_down(priv); 27262306a36Sopenharmony_ci clear_bit(STATUS_EXIT_PENDING, &priv->status); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci IWL_ERR(priv, "Unable to initialize device.\n"); 27562306a36Sopenharmony_ci return ret; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int iwlagn_mac_start(struct ieee80211_hw *hw) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 28162306a36Sopenharmony_ci int ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* we should be verifying the device is ready to be opened */ 28662306a36Sopenharmony_ci mutex_lock(&priv->mutex); 28762306a36Sopenharmony_ci ret = __iwl_up(priv); 28862306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 28962306a36Sopenharmony_ci if (ret) 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Start UP work done.\n"); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Now we should be done, and the READY bit should be set. */ 29562306a36Sopenharmony_ci if (WARN_ON(!test_bit(STATUS_READY, &priv->status))) 29662306a36Sopenharmony_ci ret = -EIO; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci iwlagn_led_enable(priv); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci priv->is_open = 1; 30162306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 30262306a36Sopenharmony_ci return ret; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void iwlagn_mac_stop(struct ieee80211_hw *hw) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!priv->is_open) 31262306a36Sopenharmony_ci return; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci priv->is_open = 0; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci mutex_lock(&priv->mutex); 31762306a36Sopenharmony_ci iwl_down(priv); 31862306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci iwl_cancel_deferred_work(priv); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci flush_workqueue(priv->workqueue); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, 32862306a36Sopenharmony_ci struct ieee80211_vif *vif, 32962306a36Sopenharmony_ci struct cfg80211_gtk_rekey_data *data) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (iwlwifi_mod_params.swcrypto) 33462306a36Sopenharmony_ci return; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 33762306a36Sopenharmony_ci mutex_lock(&priv->mutex); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif) 34062306a36Sopenharmony_ci goto out; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci memcpy(priv->kek, data->kek, NL80211_KEK_LEN); 34362306a36Sopenharmony_ci memcpy(priv->kck, data->kck, NL80211_KCK_LEN); 34462306a36Sopenharmony_ci priv->replay_ctr = 34562306a36Sopenharmony_ci cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); 34662306a36Sopenharmony_ci priv->have_rekey_data = true; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci out: 34962306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 35062306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int iwlagn_mac_suspend(struct ieee80211_hw *hw, 35662306a36Sopenharmony_ci struct cfg80211_wowlan *wowlan) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 35962306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 36062306a36Sopenharmony_ci int ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (WARN_ON(!wowlan)) 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 36662306a36Sopenharmony_ci mutex_lock(&priv->mutex); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Don't attempt WoWLAN when not associated, tear down instead. */ 36962306a36Sopenharmony_ci if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION || 37062306a36Sopenharmony_ci !iwl_is_associated_ctx(ctx)) { 37162306a36Sopenharmony_ci ret = 1; 37262306a36Sopenharmony_ci goto out; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = iwlagn_suspend(priv, wowlan); 37662306a36Sopenharmony_ci if (ret) 37762306a36Sopenharmony_ci goto error; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* let the ucode operate on its own */ 38062306a36Sopenharmony_ci iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, 38162306a36Sopenharmony_ci CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci iwl_trans_d3_suspend(priv->trans, false, true); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci goto out; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci error: 38862306a36Sopenharmony_ci priv->wowlan = false; 38962306a36Sopenharmony_ci iwlagn_prepare_restart(priv); 39062306a36Sopenharmony_ci ieee80211_restart_hw(priv->hw); 39162306a36Sopenharmony_ci out: 39262306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 39362306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return ret; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistruct iwl_resume_data { 39962306a36Sopenharmony_ci struct iwl_priv *priv; 40062306a36Sopenharmony_ci struct iwlagn_wowlan_status *cmd; 40162306a36Sopenharmony_ci bool valid; 40262306a36Sopenharmony_ci}; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait, 40562306a36Sopenharmony_ci struct iwl_rx_packet *pkt, void *data) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct iwl_resume_data *resume_data = data; 40862306a36Sopenharmony_ci struct iwl_priv *priv = resume_data->priv; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (iwl_rx_packet_payload_len(pkt) != sizeof(*resume_data->cmd)) { 41162306a36Sopenharmony_ci IWL_ERR(priv, "rx wrong size data\n"); 41262306a36Sopenharmony_ci return true; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci memcpy(resume_data->cmd, pkt->data, sizeof(*resume_data->cmd)); 41562306a36Sopenharmony_ci resume_data->valid = true; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return true; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int iwlagn_mac_resume(struct ieee80211_hw *hw) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 42362306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 42462306a36Sopenharmony_ci struct ieee80211_vif *vif; 42562306a36Sopenharmony_ci u32 base; 42662306a36Sopenharmony_ci int ret; 42762306a36Sopenharmony_ci enum iwl_d3_status d3_status; 42862306a36Sopenharmony_ci struct error_table_start { 42962306a36Sopenharmony_ci /* cf. struct iwl_error_event_table */ 43062306a36Sopenharmony_ci u32 valid; 43162306a36Sopenharmony_ci u32 error_id; 43262306a36Sopenharmony_ci } err_info; 43362306a36Sopenharmony_ci struct iwl_notification_wait status_wait; 43462306a36Sopenharmony_ci static const u16 status_cmd[] = { 43562306a36Sopenharmony_ci REPLY_WOWLAN_GET_STATUS, 43662306a36Sopenharmony_ci }; 43762306a36Sopenharmony_ci struct iwlagn_wowlan_status status_data = {}; 43862306a36Sopenharmony_ci struct iwl_resume_data resume_data = { 43962306a36Sopenharmony_ci .priv = priv, 44062306a36Sopenharmony_ci .cmd = &status_data, 44162306a36Sopenharmony_ci .valid = false, 44262306a36Sopenharmony_ci }; 44362306a36Sopenharmony_ci struct cfg80211_wowlan_wakeup wakeup = { 44462306a36Sopenharmony_ci .pattern_idx = -1, 44562306a36Sopenharmony_ci }; 44662306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 44762306a36Sopenharmony_ci const struct fw_img *img; 44862306a36Sopenharmony_ci#endif 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 45162306a36Sopenharmony_ci mutex_lock(&priv->mutex); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* we'll clear ctx->vif during iwlagn_prepare_restart() */ 45462306a36Sopenharmony_ci vif = ctx->vif; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ret = iwl_trans_d3_resume(priv->trans, &d3_status, false, true); 45762306a36Sopenharmony_ci if (ret) 45862306a36Sopenharmony_ci goto out_unlock; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (d3_status != IWL_D3_STATUS_ALIVE) { 46162306a36Sopenharmony_ci IWL_INFO(priv, "Device was reset during suspend\n"); 46262306a36Sopenharmony_ci goto out_unlock; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* uCode is no longer operating by itself */ 46662306a36Sopenharmony_ci iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, 46762306a36Sopenharmony_ci CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci base = priv->device_pointers.error_event_table; 47062306a36Sopenharmony_ci if (!iwlagn_hw_valid_rtc_data_addr(base)) { 47162306a36Sopenharmony_ci IWL_WARN(priv, "Invalid error table during resume!\n"); 47262306a36Sopenharmony_ci goto out_unlock; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci iwl_trans_read_mem_bytes(priv->trans, base, 47662306a36Sopenharmony_ci &err_info, sizeof(err_info)); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (err_info.valid) { 47962306a36Sopenharmony_ci IWL_INFO(priv, "error table is valid (%d, 0x%x)\n", 48062306a36Sopenharmony_ci err_info.valid, err_info.error_id); 48162306a36Sopenharmony_ci if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { 48262306a36Sopenharmony_ci wakeup.rfkill_release = true; 48362306a36Sopenharmony_ci ieee80211_report_wowlan_wakeup(vif, &wakeup, 48462306a36Sopenharmony_ci GFP_KERNEL); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci goto out_unlock; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 49062306a36Sopenharmony_ci img = &priv->fw->img[IWL_UCODE_WOWLAN]; 49162306a36Sopenharmony_ci if (!priv->wowlan_sram) 49262306a36Sopenharmony_ci priv->wowlan_sram = 49362306a36Sopenharmony_ci kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len, 49462306a36Sopenharmony_ci GFP_KERNEL); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (priv->wowlan_sram) 49762306a36Sopenharmony_ci iwl_trans_read_mem(priv->trans, 0x800000, 49862306a36Sopenharmony_ci priv->wowlan_sram, 49962306a36Sopenharmony_ci img->sec[IWL_UCODE_SECTION_DATA].len / 4); 50062306a36Sopenharmony_ci#endif 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* 50362306a36Sopenharmony_ci * This is very strange. The GET_STATUS command is sent but the device 50462306a36Sopenharmony_ci * doesn't reply properly, it seems it doesn't close the RBD so one is 50562306a36Sopenharmony_ci * always left open ... As a result, we need to send another command 50662306a36Sopenharmony_ci * and have to reset the driver afterwards. As we need to switch to 50762306a36Sopenharmony_ci * runtime firmware again that'll happen. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci iwl_init_notification_wait(&priv->notif_wait, &status_wait, status_cmd, 51162306a36Sopenharmony_ci ARRAY_SIZE(status_cmd), iwl_resume_status_fn, 51262306a36Sopenharmony_ci &resume_data); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_GET_STATUS, CMD_ASYNC, 0, NULL); 51562306a36Sopenharmony_ci iwl_dvm_send_cmd_pdu(priv, REPLY_ECHO, CMD_ASYNC, 0, NULL); 51662306a36Sopenharmony_ci /* an RBD is left open in the firmware now! */ 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci ret = iwl_wait_notification(&priv->notif_wait, &status_wait, HZ/5); 51962306a36Sopenharmony_ci if (ret) 52062306a36Sopenharmony_ci goto out_unlock; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (resume_data.valid && priv->contexts[IWL_RXON_CTX_BSS].vif) { 52362306a36Sopenharmony_ci u32 reasons = le32_to_cpu(status_data.wakeup_reason); 52462306a36Sopenharmony_ci struct cfg80211_wowlan_wakeup *wakeup_report; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci IWL_INFO(priv, "WoWLAN wakeup reason(s): 0x%.8x\n", reasons); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (reasons) { 52962306a36Sopenharmony_ci if (reasons & IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET) 53062306a36Sopenharmony_ci wakeup.magic_pkt = true; 53162306a36Sopenharmony_ci if (reasons & IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH) 53262306a36Sopenharmony_ci wakeup.pattern_idx = status_data.pattern_number; 53362306a36Sopenharmony_ci if (reasons & (IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | 53462306a36Sopenharmony_ci IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE)) 53562306a36Sopenharmony_ci wakeup.disconnect = true; 53662306a36Sopenharmony_ci if (reasons & IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL) 53762306a36Sopenharmony_ci wakeup.gtk_rekey_failure = true; 53862306a36Sopenharmony_ci if (reasons & IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ) 53962306a36Sopenharmony_ci wakeup.eap_identity_req = true; 54062306a36Sopenharmony_ci if (reasons & IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE) 54162306a36Sopenharmony_ci wakeup.four_way_handshake = true; 54262306a36Sopenharmony_ci wakeup_report = &wakeup; 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci wakeup_report = NULL; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci priv->wowlan = false; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci iwlagn_prepare_restart(priv); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci memset((void *)&ctx->active, 0, sizeof(ctx->active)); 55562306a36Sopenharmony_ci iwl_connection_init_rx_config(priv, ctx); 55662306a36Sopenharmony_ci iwlagn_set_rxon_chain(priv, ctx); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci out_unlock: 55962306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 56062306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci ieee80211_resume_disconnect(vif); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return 1; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci device_set_wakeup_enable(priv->trans->dev, enabled); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci#endif 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic void iwlagn_mac_tx(struct ieee80211_hw *hw, 57662306a36Sopenharmony_ci struct ieee80211_tx_control *control, 57762306a36Sopenharmony_ci struct sk_buff *skb) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (iwlagn_tx_skb(priv, control->sta, skb)) 58262306a36Sopenharmony_ci ieee80211_free_txskb(hw, skb); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, 58662306a36Sopenharmony_ci struct ieee80211_vif *vif, 58762306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, 58862306a36Sopenharmony_ci struct ieee80211_sta *sta, 58962306a36Sopenharmony_ci u32 iv32, u16 *phase1key) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 59762306a36Sopenharmony_ci struct ieee80211_vif *vif, 59862306a36Sopenharmony_ci struct ieee80211_sta *sta, 59962306a36Sopenharmony_ci struct ieee80211_key_conf *key) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 60262306a36Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 60362306a36Sopenharmony_ci struct iwl_rxon_context *ctx = vif_priv->ctx; 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci bool is_default_wep_key = false; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (iwlwifi_mod_params.swcrypto) { 61062306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); 61162306a36Sopenharmony_ci return -EOPNOTSUPP; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci switch (key->cipher) { 61562306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 61662306a36Sopenharmony_ci key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; 61762306a36Sopenharmony_ci fallthrough; 61862306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 61962306a36Sopenharmony_ci key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci default: 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * We could program these keys into the hardware as well, but we 62762306a36Sopenharmony_ci * don't expect much multicast traffic in IBSS and having keys 62862306a36Sopenharmony_ci * for more stations is probably more useful. 62962306a36Sopenharmony_ci * 63062306a36Sopenharmony_ci * Mark key TX-only and return 0. 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_ADHOC && 63362306a36Sopenharmony_ci !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { 63462306a36Sopenharmony_ci key->hw_key_idx = WEP_INVALID_OFFSET; 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* If they key was TX-only, accept deletion */ 63962306a36Sopenharmony_ci if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET) 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci mutex_lock(&priv->mutex); 64362306a36Sopenharmony_ci iwl_scan_cancel_timeout(priv, 100); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * If we are getting WEP group key and we didn't receive any key mapping 64962306a36Sopenharmony_ci * so far, we are in legacy wep mode (group key only), otherwise we are 65062306a36Sopenharmony_ci * in 1X mode. 65162306a36Sopenharmony_ci * In legacy wep mode, we use another host command to the uCode. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_ci if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || 65462306a36Sopenharmony_ci key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { 65562306a36Sopenharmony_ci if (cmd == SET_KEY) 65662306a36Sopenharmony_ci is_default_wep_key = !ctx->key_mapping_keys; 65762306a36Sopenharmony_ci else 65862306a36Sopenharmony_ci is_default_wep_key = 65962306a36Sopenharmony_ci key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci switch (cmd) { 66462306a36Sopenharmony_ci case SET_KEY: 66562306a36Sopenharmony_ci if (is_default_wep_key) { 66662306a36Sopenharmony_ci ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key); 66762306a36Sopenharmony_ci break; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta); 67062306a36Sopenharmony_ci if (ret) { 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * can't add key for RX, but we don't need it 67362306a36Sopenharmony_ci * in the device for TX so still return 0 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci ret = 0; 67662306a36Sopenharmony_ci key->hw_key_idx = WEP_INVALID_OFFSET; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci case DISABLE_KEY: 68262306a36Sopenharmony_ci if (is_default_wep_key) 68362306a36Sopenharmony_ci ret = iwl_remove_default_wep_key(priv, ctx, key); 68462306a36Sopenharmony_ci else 68562306a36Sopenharmony_ci ret = iwl_remove_dynamic_key(priv, ctx, key, sta); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci default: 69062306a36Sopenharmony_ci ret = -EINVAL; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 69462306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return ret; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, 70062306a36Sopenharmony_ci struct ieee80211_vif *vif, 70162306a36Sopenharmony_ci struct ieee80211_ampdu_params *params) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 70462306a36Sopenharmony_ci int ret = -EINVAL; 70562306a36Sopenharmony_ci struct ieee80211_sta *sta = params->sta; 70662306a36Sopenharmony_ci enum ieee80211_ampdu_mlme_action action = params->action; 70762306a36Sopenharmony_ci u16 tid = params->tid; 70862306a36Sopenharmony_ci u16 *ssn = ¶ms->ssn; 70962306a36Sopenharmony_ci u8 buf_size = params->buf_size; 71062306a36Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", 71362306a36Sopenharmony_ci sta->addr, tid); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (!(priv->nvm_data->sku_cap_11n_enable)) 71662306a36Sopenharmony_ci return -EACCES; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 71962306a36Sopenharmony_ci mutex_lock(&priv->mutex); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci switch (action) { 72262306a36Sopenharmony_ci case IEEE80211_AMPDU_RX_START: 72362306a36Sopenharmony_ci if (!iwl_enable_rx_ampdu()) 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "start Rx\n"); 72662306a36Sopenharmony_ci ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci case IEEE80211_AMPDU_RX_STOP: 72962306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "stop Rx\n"); 73062306a36Sopenharmony_ci ret = iwl_sta_rx_agg_stop(priv, sta, tid); 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_START: 73362306a36Sopenharmony_ci if (!priv->trans->ops->txq_enable) 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci if (!iwl_enable_tx_ampdu()) 73662306a36Sopenharmony_ci break; 73762306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "start Tx\n"); 73862306a36Sopenharmony_ci ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); 73962306a36Sopenharmony_ci break; 74062306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_FLUSH: 74162306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: 74262306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "Flush Tx\n"); 74362306a36Sopenharmony_ci ret = iwlagn_tx_agg_flush(priv, vif, sta, tid); 74462306a36Sopenharmony_ci break; 74562306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_CONT: 74662306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "stop Tx\n"); 74762306a36Sopenharmony_ci ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); 74862306a36Sopenharmony_ci if ((ret == 0) && (priv->agg_tids_count > 0)) { 74962306a36Sopenharmony_ci priv->agg_tids_count--; 75062306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", 75162306a36Sopenharmony_ci priv->agg_tids_count); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci if (!priv->agg_tids_count && 75462306a36Sopenharmony_ci priv->hw_params.use_rts_for_aggregation) { 75562306a36Sopenharmony_ci /* 75662306a36Sopenharmony_ci * switch off RTS/CTS if it was previously enabled 75762306a36Sopenharmony_ci */ 75862306a36Sopenharmony_ci sta_priv->lq_sta.lq.general_params.flags &= 75962306a36Sopenharmony_ci ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; 76062306a36Sopenharmony_ci iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), 76162306a36Sopenharmony_ci &sta_priv->lq_sta.lq, CMD_ASYNC, false); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_OPERATIONAL: 76562306a36Sopenharmony_ci ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size); 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 76962306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int iwlagn_mac_sta_add(struct ieee80211_hw *hw, 77462306a36Sopenharmony_ci struct ieee80211_vif *vif, 77562306a36Sopenharmony_ci struct ieee80211_sta *sta) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 77862306a36Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; 77962306a36Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 78062306a36Sopenharmony_ci bool is_ap = vif->type == NL80211_IFTYPE_STATION; 78162306a36Sopenharmony_ci int ret; 78262306a36Sopenharmony_ci u8 sta_id; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", 78562306a36Sopenharmony_ci sta->addr); 78662306a36Sopenharmony_ci sta_priv->sta_id = IWL_INVALID_STATION; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci atomic_set(&sta_priv->pending_frames, 0); 78962306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_AP) 79062306a36Sopenharmony_ci sta_priv->client = true; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr, 79362306a36Sopenharmony_ci is_ap, sta, &sta_id); 79462306a36Sopenharmony_ci if (ret) { 79562306a36Sopenharmony_ci IWL_ERR(priv, "Unable to add station %pM (%d)\n", 79662306a36Sopenharmony_ci sta->addr, ret); 79762306a36Sopenharmony_ci /* Should we return success if return code is EEXIST ? */ 79862306a36Sopenharmony_ci return ret; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci sta_priv->sta_id = sta_id; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return 0; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, 80762306a36Sopenharmony_ci struct ieee80211_vif *vif, 80862306a36Sopenharmony_ci struct ieee80211_sta *sta) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 81162306a36Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; 81262306a36Sopenharmony_ci int ret; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION) { 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * Station will be removed from device when the RXON 81962306a36Sopenharmony_ci * is set to unassociated -- just deactivate it here 82062306a36Sopenharmony_ci * to avoid re-programming it. 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci ret = 0; 82362306a36Sopenharmony_ci iwl_deactivate_station(priv, sta_priv->sta_id, sta->addr); 82462306a36Sopenharmony_ci } else { 82562306a36Sopenharmony_ci ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); 82662306a36Sopenharmony_ci if (ret) 82762306a36Sopenharmony_ci IWL_DEBUG_QUIET_RFKILL(priv, 82862306a36Sopenharmony_ci "Error removing station %pM\n", sta->addr); 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci return ret; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int iwlagn_mac_sta_state(struct ieee80211_hw *hw, 83462306a36Sopenharmony_ci struct ieee80211_vif *vif, 83562306a36Sopenharmony_ci struct ieee80211_sta *sta, 83662306a36Sopenharmony_ci enum ieee80211_sta_state old_state, 83762306a36Sopenharmony_ci enum ieee80211_sta_state new_state) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 84062306a36Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 84162306a36Sopenharmony_ci enum { 84262306a36Sopenharmony_ci NONE, ADD, REMOVE, HT_RATE_INIT, ADD_RATE_INIT, 84362306a36Sopenharmony_ci } op = NONE; 84462306a36Sopenharmony_ci int ret; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "station %pM state change %d->%d\n", 84762306a36Sopenharmony_ci sta->addr, old_state, new_state); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci mutex_lock(&priv->mutex); 85062306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION) { 85162306a36Sopenharmony_ci if (old_state == IEEE80211_STA_NOTEXIST && 85262306a36Sopenharmony_ci new_state == IEEE80211_STA_NONE) 85362306a36Sopenharmony_ci op = ADD; 85462306a36Sopenharmony_ci else if (old_state == IEEE80211_STA_NONE && 85562306a36Sopenharmony_ci new_state == IEEE80211_STA_NOTEXIST) 85662306a36Sopenharmony_ci op = REMOVE; 85762306a36Sopenharmony_ci else if (old_state == IEEE80211_STA_AUTH && 85862306a36Sopenharmony_ci new_state == IEEE80211_STA_ASSOC) 85962306a36Sopenharmony_ci op = HT_RATE_INIT; 86062306a36Sopenharmony_ci } else { 86162306a36Sopenharmony_ci if (old_state == IEEE80211_STA_AUTH && 86262306a36Sopenharmony_ci new_state == IEEE80211_STA_ASSOC) 86362306a36Sopenharmony_ci op = ADD_RATE_INIT; 86462306a36Sopenharmony_ci else if (old_state == IEEE80211_STA_ASSOC && 86562306a36Sopenharmony_ci new_state == IEEE80211_STA_AUTH) 86662306a36Sopenharmony_ci op = REMOVE; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci switch (op) { 87062306a36Sopenharmony_ci case ADD: 87162306a36Sopenharmony_ci ret = iwlagn_mac_sta_add(hw, vif, sta); 87262306a36Sopenharmony_ci if (ret) 87362306a36Sopenharmony_ci break; 87462306a36Sopenharmony_ci /* 87562306a36Sopenharmony_ci * Clear the in-progress flag, the AP station entry was added 87662306a36Sopenharmony_ci * but we'll initialize LQ only when we've associated (which 87762306a36Sopenharmony_ci * would also clear the in-progress flag). This is necessary 87862306a36Sopenharmony_ci * in case we never initialize LQ because association fails. 87962306a36Sopenharmony_ci */ 88062306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 88162306a36Sopenharmony_ci priv->stations[iwl_sta_id(sta)].used &= 88262306a36Sopenharmony_ci ~IWL_STA_UCODE_INPROGRESS; 88362306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 88462306a36Sopenharmony_ci break; 88562306a36Sopenharmony_ci case REMOVE: 88662306a36Sopenharmony_ci ret = iwlagn_mac_sta_remove(hw, vif, sta); 88762306a36Sopenharmony_ci break; 88862306a36Sopenharmony_ci case ADD_RATE_INIT: 88962306a36Sopenharmony_ci ret = iwlagn_mac_sta_add(hw, vif, sta); 89062306a36Sopenharmony_ci if (ret) 89162306a36Sopenharmony_ci break; 89262306a36Sopenharmony_ci /* Initialize rate scaling */ 89362306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, 89462306a36Sopenharmony_ci "Initializing rate scaling for station %pM\n", 89562306a36Sopenharmony_ci sta->addr); 89662306a36Sopenharmony_ci iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); 89762306a36Sopenharmony_ci ret = 0; 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci case HT_RATE_INIT: 90062306a36Sopenharmony_ci /* Initialize rate scaling */ 90162306a36Sopenharmony_ci ret = iwl_sta_update_ht(priv, vif_priv->ctx, sta); 90262306a36Sopenharmony_ci if (ret) 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, 90562306a36Sopenharmony_ci "Initializing rate scaling for station %pM\n", 90662306a36Sopenharmony_ci sta->addr); 90762306a36Sopenharmony_ci iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); 90862306a36Sopenharmony_ci ret = 0; 90962306a36Sopenharmony_ci break; 91062306a36Sopenharmony_ci default: 91162306a36Sopenharmony_ci ret = 0; 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* 91662306a36Sopenharmony_ci * mac80211 might WARN if we fail, but due the way we 91762306a36Sopenharmony_ci * (badly) handle hard rfkill, we might fail here 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ci if (iwl_is_rfkill(priv)) 92062306a36Sopenharmony_ci ret = 0; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 92362306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return ret; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, 92962306a36Sopenharmony_ci struct ieee80211_vif *vif, 93062306a36Sopenharmony_ci struct ieee80211_channel_switch *ch_switch) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 93362306a36Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 93462306a36Sopenharmony_ci struct ieee80211_channel *channel = ch_switch->chandef.chan; 93562306a36Sopenharmony_ci struct iwl_ht_config *ht_conf = &priv->current_ht_config; 93662306a36Sopenharmony_ci /* 93762306a36Sopenharmony_ci * MULTI-FIXME 93862306a36Sopenharmony_ci * When we add support for multiple interfaces, we need to 93962306a36Sopenharmony_ci * revisit this. The channel switch command in the device 94062306a36Sopenharmony_ci * only affects the BSS context, but what does that really 94162306a36Sopenharmony_ci * mean? And what if we get a CSA on the second interface? 94262306a36Sopenharmony_ci * This needs a lot of work. 94362306a36Sopenharmony_ci */ 94462306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 94562306a36Sopenharmony_ci u16 ch; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci mutex_lock(&priv->mutex); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (iwl_is_rfkill(priv)) 95262306a36Sopenharmony_ci goto out; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status) || 95562306a36Sopenharmony_ci test_bit(STATUS_SCANNING, &priv->status) || 95662306a36Sopenharmony_ci test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) 95762306a36Sopenharmony_ci goto out; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (!iwl_is_associated_ctx(ctx)) 96062306a36Sopenharmony_ci goto out; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (!priv->lib->set_channel_switch) 96362306a36Sopenharmony_ci goto out; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci ch = channel->hw_value; 96662306a36Sopenharmony_ci if (le16_to_cpu(ctx->active.channel) == ch) 96762306a36Sopenharmony_ci goto out; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci priv->current_ht_config.smps = conf->smps_mode; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* Configure HT40 channels */ 97262306a36Sopenharmony_ci switch (cfg80211_get_chandef_type(&ch_switch->chandef)) { 97362306a36Sopenharmony_ci case NL80211_CHAN_NO_HT: 97462306a36Sopenharmony_ci case NL80211_CHAN_HT20: 97562306a36Sopenharmony_ci ctx->ht.is_40mhz = false; 97662306a36Sopenharmony_ci ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; 97762306a36Sopenharmony_ci break; 97862306a36Sopenharmony_ci case NL80211_CHAN_HT40MINUS: 97962306a36Sopenharmony_ci ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 98062306a36Sopenharmony_ci ctx->ht.is_40mhz = true; 98162306a36Sopenharmony_ci break; 98262306a36Sopenharmony_ci case NL80211_CHAN_HT40PLUS: 98362306a36Sopenharmony_ci ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 98462306a36Sopenharmony_ci ctx->ht.is_40mhz = true; 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if ((le16_to_cpu(ctx->staging.channel) != ch)) 98962306a36Sopenharmony_ci ctx->staging.flags = 0; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci iwl_set_rxon_channel(priv, channel, ctx); 99262306a36Sopenharmony_ci iwl_set_rxon_ht(priv, ht_conf); 99362306a36Sopenharmony_ci iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* 99662306a36Sopenharmony_ci * at this point, staging_rxon has the 99762306a36Sopenharmony_ci * configuration for channel switch 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_ci set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); 100062306a36Sopenharmony_ci priv->switch_channel = cpu_to_le16(ch); 100162306a36Sopenharmony_ci if (priv->lib->set_channel_switch(priv, ch_switch)) { 100262306a36Sopenharmony_ci clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); 100362306a36Sopenharmony_ci priv->switch_channel = 0; 100462306a36Sopenharmony_ci ieee80211_chswitch_done(ctx->vif, false); 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ciout: 100862306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 100962306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_civoid iwl_chswitch_done(struct iwl_priv *priv, bool is_success) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci /* 101562306a36Sopenharmony_ci * MULTI-FIXME 101662306a36Sopenharmony_ci * See iwlagn_mac_channel_switch. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 102162306a36Sopenharmony_ci return; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) 102462306a36Sopenharmony_ci return; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (ctx->vif) 102762306a36Sopenharmony_ci ieee80211_chswitch_done(ctx->vif, is_success); 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic void iwlagn_configure_filter(struct ieee80211_hw *hw, 103162306a36Sopenharmony_ci unsigned int changed_flags, 103262306a36Sopenharmony_ci unsigned int *total_flags, 103362306a36Sopenharmony_ci u64 multicast) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 103662306a36Sopenharmony_ci __le32 filter_or = 0, filter_nand = 0; 103762306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci#define CHK(test, flag) do { \ 104062306a36Sopenharmony_ci if (*total_flags & (test)) \ 104162306a36Sopenharmony_ci filter_or |= (flag); \ 104262306a36Sopenharmony_ci else \ 104362306a36Sopenharmony_ci filter_nand |= (flag); \ 104462306a36Sopenharmony_ci } while (0) 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", 104762306a36Sopenharmony_ci changed_flags, *total_flags); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK); 105062306a36Sopenharmony_ci /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ 105162306a36Sopenharmony_ci CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); 105262306a36Sopenharmony_ci CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci#undef CHK 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci mutex_lock(&priv->mutex); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci for_each_context(priv, ctx) { 105962306a36Sopenharmony_ci ctx->staging.filter_flags &= ~filter_nand; 106062306a36Sopenharmony_ci ctx->staging.filter_flags |= filter_or; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci /* 106362306a36Sopenharmony_ci * Not committing directly because hardware can perform a scan, 106462306a36Sopenharmony_ci * but we'll eventually commit the filter flags change anyway. 106562306a36Sopenharmony_ci */ 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* 107162306a36Sopenharmony_ci * Receiving all multicast frames is always enabled by the 107262306a36Sopenharmony_ci * default flags setup in iwl_connection_init_rx_config() 107362306a36Sopenharmony_ci * since we currently do not support programming multicast 107462306a36Sopenharmony_ci * filters into the device. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | 107762306a36Sopenharmony_ci FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 108162306a36Sopenharmony_ci u32 queues, bool drop) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 108462306a36Sopenharmony_ci u32 scd_queues; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci mutex_lock(&priv->mutex); 108762306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { 109062306a36Sopenharmony_ci IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n"); 109162306a36Sopenharmony_ci goto done; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci if (iwl_is_rfkill(priv)) { 109462306a36Sopenharmony_ci IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n"); 109562306a36Sopenharmony_ci goto done; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci scd_queues = BIT(priv->trans->trans_cfg->base_params->num_of_queues) - 1; 109962306a36Sopenharmony_ci scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) | 110062306a36Sopenharmony_ci BIT(IWL_DEFAULT_CMD_QUEUE_NUM)); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (drop) { 110362306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", 110462306a36Sopenharmony_ci scd_queues); 110562306a36Sopenharmony_ci if (iwlagn_txfifo_flush(priv, scd_queues)) { 110662306a36Sopenharmony_ci IWL_ERR(priv, "flush request fail\n"); 110762306a36Sopenharmony_ci goto done; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n"); 111262306a36Sopenharmony_ci iwl_trans_wait_tx_queues_empty(priv->trans, scd_queues); 111362306a36Sopenharmony_cidone: 111462306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 111562306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic void iwlagn_mac_event_callback(struct ieee80211_hw *hw, 111962306a36Sopenharmony_ci struct ieee80211_vif *vif, 112062306a36Sopenharmony_ci const struct ieee80211_event *event) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci if (event->type != RSSI_EVENT) 112562306a36Sopenharmony_ci return; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (priv->lib->bt_params && 113062306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist) { 113162306a36Sopenharmony_ci if (event->u.rssi.data == RSSI_EVENT_LOW) 113262306a36Sopenharmony_ci priv->bt_enable_pspoll = true; 113362306a36Sopenharmony_ci else if (event->u.rssi.data == RSSI_EVENT_HIGH) 113462306a36Sopenharmony_ci priv->bt_enable_pspoll = false; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci queue_work(priv->workqueue, &priv->bt_runtime_config); 113762306a36Sopenharmony_ci } else { 113862306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled," 113962306a36Sopenharmony_ci "ignoring RSSI callback\n"); 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistatic int iwlagn_mac_set_tim(struct ieee80211_hw *hw, 114662306a36Sopenharmony_ci struct ieee80211_sta *sta, bool set) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci queue_work(priv->workqueue, &priv->beacon_update); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci return 0; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cistatic int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, 115662306a36Sopenharmony_ci struct ieee80211_vif *vif, 115762306a36Sopenharmony_ci unsigned int link_id, u16 queue, 115862306a36Sopenharmony_ci const struct ieee80211_tx_queue_params *params) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 116162306a36Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 116262306a36Sopenharmony_ci struct iwl_rxon_context *ctx = vif_priv->ctx; 116362306a36Sopenharmony_ci int q; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (WARN_ON(!ctx)) 116662306a36Sopenharmony_ci return -EINVAL; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (!iwl_is_ready_rf(priv)) { 117162306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); 117262306a36Sopenharmony_ci return -EIO; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (queue >= AC_NUM) { 117662306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); 117762306a36Sopenharmony_ci return 0; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci q = AC_NUM - 1 - queue; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci mutex_lock(&priv->mutex); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci ctx->qos_data.def_qos_parm.ac[q].cw_min = 118562306a36Sopenharmony_ci cpu_to_le16(params->cw_min); 118662306a36Sopenharmony_ci ctx->qos_data.def_qos_parm.ac[q].cw_max = 118762306a36Sopenharmony_ci cpu_to_le16(params->cw_max); 118862306a36Sopenharmony_ci ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; 118962306a36Sopenharmony_ci ctx->qos_data.def_qos_parm.ac[q].edca_txop = 119062306a36Sopenharmony_ci cpu_to_le16((params->txop * 32)); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 119762306a36Sopenharmony_ci return 0; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci return priv->ibss_manager == IWL_IBSS_MANAGER; 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci iwl_connection_init_rx_config(priv, ctx); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci iwlagn_set_rxon_chain(priv, ctx); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return iwlagn_commit_rxon(priv, ctx); 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic int iwl_setup_interface(struct iwl_priv *priv, 121762306a36Sopenharmony_ci struct iwl_rxon_context *ctx) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci struct ieee80211_vif *vif = ctx->vif; 122062306a36Sopenharmony_ci int err, ac; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* 122562306a36Sopenharmony_ci * This variable will be correct only when there's just 122662306a36Sopenharmony_ci * a single context, but all code using it is for hardware 122762306a36Sopenharmony_ci * that supports only one context. 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ci priv->iw_mode = vif->type; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci ctx->is_active = true; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci err = iwl_set_mode(priv, ctx); 123462306a36Sopenharmony_ci if (err) { 123562306a36Sopenharmony_ci if (!ctx->always_active) 123662306a36Sopenharmony_ci ctx->is_active = false; 123762306a36Sopenharmony_ci return err; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist && 124162306a36Sopenharmony_ci vif->type == NL80211_IFTYPE_ADHOC) { 124262306a36Sopenharmony_ci /* 124362306a36Sopenharmony_ci * pretend to have high BT traffic as long as we 124462306a36Sopenharmony_ci * are operating in IBSS mode, as this will cause 124562306a36Sopenharmony_ci * the rate scaling etc. to behave as intended. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ci priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* set up queue mappings */ 125162306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 125262306a36Sopenharmony_ci vif->hw_queue[ac] = ctx->ac_to_queue[ac]; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_AP) 125562306a36Sopenharmony_ci vif->cab_queue = ctx->mcast_queue; 125662306a36Sopenharmony_ci else 125762306a36Sopenharmony_ci vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci return 0; 126062306a36Sopenharmony_ci} 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic int iwlagn_mac_add_interface(struct ieee80211_hw *hw, 126362306a36Sopenharmony_ci struct ieee80211_vif *vif) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 126662306a36Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 126762306a36Sopenharmony_ci struct iwl_rxon_context *tmp, *ctx = NULL; 126862306a36Sopenharmony_ci int err; 126962306a36Sopenharmony_ci enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); 127062306a36Sopenharmony_ci bool reset = false; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", 127362306a36Sopenharmony_ci viftype, vif->addr); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci mutex_lock(&priv->mutex); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (!iwl_is_ready_rf(priv)) { 127862306a36Sopenharmony_ci IWL_WARN(priv, "Try to add interface when device not ready\n"); 127962306a36Sopenharmony_ci err = -EINVAL; 128062306a36Sopenharmony_ci goto out; 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci for_each_context(priv, tmp) { 128462306a36Sopenharmony_ci u32 possible_modes = 128562306a36Sopenharmony_ci tmp->interface_modes | tmp->exclusive_interface_modes; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci if (tmp->vif) { 128862306a36Sopenharmony_ci /* On reset we need to add the same interface again */ 128962306a36Sopenharmony_ci if (tmp->vif == vif) { 129062306a36Sopenharmony_ci reset = true; 129162306a36Sopenharmony_ci ctx = tmp; 129262306a36Sopenharmony_ci break; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci /* check if this busy context is exclusive */ 129662306a36Sopenharmony_ci if (tmp->exclusive_interface_modes & 129762306a36Sopenharmony_ci BIT(tmp->vif->type)) { 129862306a36Sopenharmony_ci err = -EINVAL; 129962306a36Sopenharmony_ci goto out; 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci continue; 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (!(possible_modes & BIT(viftype))) 130562306a36Sopenharmony_ci continue; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* have maybe usable context w/o interface */ 130862306a36Sopenharmony_ci ctx = tmp; 130962306a36Sopenharmony_ci break; 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (!ctx) { 131362306a36Sopenharmony_ci err = -EOPNOTSUPP; 131462306a36Sopenharmony_ci goto out; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci vif_priv->ctx = ctx; 131862306a36Sopenharmony_ci ctx->vif = vif; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci /* 132162306a36Sopenharmony_ci * In SNIFFER device type, the firmware reports the FCS to 132262306a36Sopenharmony_ci * the host, rather than snipping it off. Unfortunately, 132362306a36Sopenharmony_ci * mac80211 doesn't (yet) provide a per-packet flag for 132462306a36Sopenharmony_ci * this, so that we have to set the hardware flag based 132562306a36Sopenharmony_ci * on the interfaces added. As the monitor interface can 132662306a36Sopenharmony_ci * only be present by itself, and will be removed before 132762306a36Sopenharmony_ci * other interfaces are added, this is safe. 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_MONITOR) 133062306a36Sopenharmony_ci ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); 133162306a36Sopenharmony_ci else 133262306a36Sopenharmony_ci __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, priv->hw->flags); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci err = iwl_setup_interface(priv, ctx); 133562306a36Sopenharmony_ci if (!err || reset) 133662306a36Sopenharmony_ci goto out; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci ctx->vif = NULL; 133962306a36Sopenharmony_ci priv->iw_mode = NL80211_IFTYPE_STATION; 134062306a36Sopenharmony_ci out: 134162306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 134462306a36Sopenharmony_ci return err; 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic void iwl_teardown_interface(struct iwl_priv *priv, 134862306a36Sopenharmony_ci struct ieee80211_vif *vif, 134962306a36Sopenharmony_ci bool mode_change) 135062306a36Sopenharmony_ci{ 135162306a36Sopenharmony_ci struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (priv->scan_vif == vif) { 135662306a36Sopenharmony_ci iwl_scan_cancel_timeout(priv, 200); 135762306a36Sopenharmony_ci iwl_force_scan_end(priv); 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci if (!mode_change) { 136162306a36Sopenharmony_ci iwl_set_mode(priv, ctx); 136262306a36Sopenharmony_ci if (!ctx->always_active) 136362306a36Sopenharmony_ci ctx->is_active = false; 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci /* 136762306a36Sopenharmony_ci * When removing the IBSS interface, overwrite the 136862306a36Sopenharmony_ci * BT traffic load with the stored one from the last 136962306a36Sopenharmony_ci * notification, if any. If this is a device that 137062306a36Sopenharmony_ci * doesn't implement this, this has no effect since 137162306a36Sopenharmony_ci * both values are the same and zero. 137262306a36Sopenharmony_ci */ 137362306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_ADHOC) 137462306a36Sopenharmony_ci priv->bt_traffic_load = priv->last_bt_traffic_load; 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, 137862306a36Sopenharmony_ci struct ieee80211_vif *vif) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 138162306a36Sopenharmony_ci struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci mutex_lock(&priv->mutex); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci WARN_ON(ctx->vif != vif); 138862306a36Sopenharmony_ci ctx->vif = NULL; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci iwl_teardown_interface(priv, vif, false); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci} 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_cistatic int iwlagn_mac_change_interface(struct ieee80211_hw *hw, 139962306a36Sopenharmony_ci struct ieee80211_vif *vif, 140062306a36Sopenharmony_ci enum nl80211_iftype newtype, bool newp2p) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 140362306a36Sopenharmony_ci struct iwl_rxon_context *ctx, *tmp; 140462306a36Sopenharmony_ci enum nl80211_iftype newviftype = newtype; 140562306a36Sopenharmony_ci u32 interface_modes; 140662306a36Sopenharmony_ci int err; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci newtype = ieee80211_iftype_p2p(newtype, newp2p); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci mutex_lock(&priv->mutex); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci ctx = iwl_rxon_ctx_from_vif(vif); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci /* 141762306a36Sopenharmony_ci * To simplify this code, only support changes on the 141862306a36Sopenharmony_ci * BSS context. The PAN context is usually reassigned 141962306a36Sopenharmony_ci * by creating/removing P2P interfaces anyway. 142062306a36Sopenharmony_ci */ 142162306a36Sopenharmony_ci if (ctx->ctxid != IWL_RXON_CTX_BSS) { 142262306a36Sopenharmony_ci err = -EBUSY; 142362306a36Sopenharmony_ci goto out; 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (!ctx->vif || !iwl_is_ready_rf(priv)) { 142762306a36Sopenharmony_ci /* 142862306a36Sopenharmony_ci * Huh? But wait ... this can maybe happen when 142962306a36Sopenharmony_ci * we're in the middle of a firmware restart! 143062306a36Sopenharmony_ci */ 143162306a36Sopenharmony_ci err = -EBUSY; 143262306a36Sopenharmony_ci goto out; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci /* Check if the switch is supported in the same context */ 143662306a36Sopenharmony_ci interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; 143762306a36Sopenharmony_ci if (!(interface_modes & BIT(newtype))) { 143862306a36Sopenharmony_ci err = -EBUSY; 143962306a36Sopenharmony_ci goto out; 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci if (ctx->exclusive_interface_modes & BIT(newtype)) { 144362306a36Sopenharmony_ci for_each_context(priv, tmp) { 144462306a36Sopenharmony_ci if (ctx == tmp) 144562306a36Sopenharmony_ci continue; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (!tmp->is_active) 144862306a36Sopenharmony_ci continue; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci /* 145162306a36Sopenharmony_ci * The current mode switch would be exclusive, but 145262306a36Sopenharmony_ci * another context is active ... refuse the switch. 145362306a36Sopenharmony_ci */ 145462306a36Sopenharmony_ci err = -EBUSY; 145562306a36Sopenharmony_ci goto out; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci /* success */ 146062306a36Sopenharmony_ci iwl_teardown_interface(priv, vif, true); 146162306a36Sopenharmony_ci vif->type = newviftype; 146262306a36Sopenharmony_ci vif->p2p = newp2p; 146362306a36Sopenharmony_ci err = iwl_setup_interface(priv, ctx); 146462306a36Sopenharmony_ci WARN_ON(err); 146562306a36Sopenharmony_ci /* 146662306a36Sopenharmony_ci * We've switched internally, but submitting to the 146762306a36Sopenharmony_ci * device may have failed for some reason. Mask this 146862306a36Sopenharmony_ci * error, because otherwise mac80211 will not switch 146962306a36Sopenharmony_ci * (and set the interface type back) and we'll be 147062306a36Sopenharmony_ci * out of sync with it. 147162306a36Sopenharmony_ci */ 147262306a36Sopenharmony_ci err = 0; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci out: 147562306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 147662306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci return err; 147962306a36Sopenharmony_ci} 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_cistatic int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, 148262306a36Sopenharmony_ci struct ieee80211_vif *vif, 148362306a36Sopenharmony_ci struct ieee80211_scan_request *hw_req) 148462306a36Sopenharmony_ci{ 148562306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 148662306a36Sopenharmony_ci struct cfg80211_scan_request *req = &hw_req->req; 148762306a36Sopenharmony_ci int ret; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci if (req->n_channels == 0) 149262306a36Sopenharmony_ci return -EINVAL; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci mutex_lock(&priv->mutex); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci /* 149762306a36Sopenharmony_ci * If an internal scan is in progress, just set 149862306a36Sopenharmony_ci * up the scan_request as per above. 149962306a36Sopenharmony_ci */ 150062306a36Sopenharmony_ci if (priv->scan_type != IWL_SCAN_NORMAL) { 150162306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, 150262306a36Sopenharmony_ci "SCAN request during internal scan - defer\n"); 150362306a36Sopenharmony_ci priv->scan_request = req; 150462306a36Sopenharmony_ci priv->scan_vif = vif; 150562306a36Sopenharmony_ci ret = 0; 150662306a36Sopenharmony_ci } else { 150762306a36Sopenharmony_ci priv->scan_request = req; 150862306a36Sopenharmony_ci priv->scan_vif = vif; 150962306a36Sopenharmony_ci /* 151062306a36Sopenharmony_ci * mac80211 will only ask for one band at a time 151162306a36Sopenharmony_ci * so using channels[0] here is ok 151262306a36Sopenharmony_ci */ 151362306a36Sopenharmony_ci ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, 151462306a36Sopenharmony_ci req->channels[0]->band); 151562306a36Sopenharmony_ci if (ret) { 151662306a36Sopenharmony_ci priv->scan_request = NULL; 151762306a36Sopenharmony_ci priv->scan_vif = NULL; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci return ret; 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) 152962306a36Sopenharmony_ci{ 153062306a36Sopenharmony_ci struct iwl_addsta_cmd cmd = { 153162306a36Sopenharmony_ci .mode = STA_CONTROL_MODIFY_MSK, 153262306a36Sopenharmony_ci .station_flags_msk = STA_FLG_PWR_SAVE_MSK, 153362306a36Sopenharmony_ci .sta.sta_id = sta_id, 153462306a36Sopenharmony_ci }; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci iwl_send_add_sta(priv, &cmd, CMD_ASYNC); 153762306a36Sopenharmony_ci} 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_cistatic void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, 154062306a36Sopenharmony_ci struct ieee80211_vif *vif, 154162306a36Sopenharmony_ci enum sta_notify_cmd cmd, 154262306a36Sopenharmony_ci struct ieee80211_sta *sta) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 154562306a36Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; 154662306a36Sopenharmony_ci int sta_id; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "enter\n"); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci switch (cmd) { 155162306a36Sopenharmony_ci case STA_NOTIFY_SLEEP: 155262306a36Sopenharmony_ci WARN_ON(!sta_priv->client); 155362306a36Sopenharmony_ci sta_priv->asleep = true; 155462306a36Sopenharmony_ci if (atomic_read(&sta_priv->pending_frames) > 0) 155562306a36Sopenharmony_ci ieee80211_sta_block_awake(hw, sta, true); 155662306a36Sopenharmony_ci break; 155762306a36Sopenharmony_ci case STA_NOTIFY_AWAKE: 155862306a36Sopenharmony_ci WARN_ON(!sta_priv->client); 155962306a36Sopenharmony_ci if (!sta_priv->asleep) 156062306a36Sopenharmony_ci break; 156162306a36Sopenharmony_ci sta_priv->asleep = false; 156262306a36Sopenharmony_ci sta_id = iwl_sta_id(sta); 156362306a36Sopenharmony_ci if (sta_id != IWL_INVALID_STATION) 156462306a36Sopenharmony_ci iwl_sta_modify_ps_wake(priv, sta_id); 156562306a36Sopenharmony_ci break; 156662306a36Sopenharmony_ci default: 156762306a36Sopenharmony_ci break; 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci IWL_DEBUG_MAC80211(priv, "leave\n"); 157062306a36Sopenharmony_ci} 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ciconst struct ieee80211_ops iwlagn_hw_ops = { 157362306a36Sopenharmony_ci .tx = iwlagn_mac_tx, 157462306a36Sopenharmony_ci .wake_tx_queue = ieee80211_handle_wake_tx_queue, 157562306a36Sopenharmony_ci .start = iwlagn_mac_start, 157662306a36Sopenharmony_ci .stop = iwlagn_mac_stop, 157762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 157862306a36Sopenharmony_ci .suspend = iwlagn_mac_suspend, 157962306a36Sopenharmony_ci .resume = iwlagn_mac_resume, 158062306a36Sopenharmony_ci .set_wakeup = iwlagn_mac_set_wakeup, 158162306a36Sopenharmony_ci#endif 158262306a36Sopenharmony_ci .add_interface = iwlagn_mac_add_interface, 158362306a36Sopenharmony_ci .remove_interface = iwlagn_mac_remove_interface, 158462306a36Sopenharmony_ci .change_interface = iwlagn_mac_change_interface, 158562306a36Sopenharmony_ci .config = iwlagn_mac_config, 158662306a36Sopenharmony_ci .configure_filter = iwlagn_configure_filter, 158762306a36Sopenharmony_ci .set_key = iwlagn_mac_set_key, 158862306a36Sopenharmony_ci .update_tkip_key = iwlagn_mac_update_tkip_key, 158962306a36Sopenharmony_ci .set_rekey_data = iwlagn_mac_set_rekey_data, 159062306a36Sopenharmony_ci .conf_tx = iwlagn_mac_conf_tx, 159162306a36Sopenharmony_ci .bss_info_changed = iwlagn_bss_info_changed, 159262306a36Sopenharmony_ci .ampdu_action = iwlagn_mac_ampdu_action, 159362306a36Sopenharmony_ci .hw_scan = iwlagn_mac_hw_scan, 159462306a36Sopenharmony_ci .sta_notify = iwlagn_mac_sta_notify, 159562306a36Sopenharmony_ci .sta_state = iwlagn_mac_sta_state, 159662306a36Sopenharmony_ci .channel_switch = iwlagn_mac_channel_switch, 159762306a36Sopenharmony_ci .flush = iwlagn_mac_flush, 159862306a36Sopenharmony_ci .tx_last_beacon = iwlagn_mac_tx_last_beacon, 159962306a36Sopenharmony_ci .event_callback = iwlagn_mac_event_callback, 160062306a36Sopenharmony_ci .set_tim = iwlagn_mac_set_tim, 160162306a36Sopenharmony_ci}; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci/* This function both allocates and initializes hw and priv. */ 160462306a36Sopenharmony_cistruct ieee80211_hw *iwl_alloc_all(void) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci struct iwl_priv *priv; 160762306a36Sopenharmony_ci struct iwl_op_mode *op_mode; 160862306a36Sopenharmony_ci /* mac80211 allocates memory for this device instance, including 160962306a36Sopenharmony_ci * space for this driver's private structure */ 161062306a36Sopenharmony_ci struct ieee80211_hw *hw; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci hw = ieee80211_alloc_hw(sizeof(struct iwl_priv) + 161362306a36Sopenharmony_ci sizeof(struct iwl_op_mode), &iwlagn_hw_ops); 161462306a36Sopenharmony_ci if (!hw) 161562306a36Sopenharmony_ci goto out; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci op_mode = hw->priv; 161862306a36Sopenharmony_ci priv = IWL_OP_MODE_GET_DVM(op_mode); 161962306a36Sopenharmony_ci priv->hw = hw; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ciout: 162262306a36Sopenharmony_ci return hw; 162362306a36Sopenharmony_ci} 1624