162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2003 - 2014, 2022 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Portions of this file are derived from the ipw3945 project, as well 762306a36Sopenharmony_ci * as portions of the ieee80211 subsystem header files. 862306a36Sopenharmony_ci *****************************************************************************/ 962306a36Sopenharmony_ci#include <linux/etherdevice.h> 1062306a36Sopenharmony_ci#include <net/mac80211.h> 1162306a36Sopenharmony_ci#include "iwl-trans.h" 1262306a36Sopenharmony_ci#include "dev.h" 1362306a36Sopenharmony_ci#include "agn.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciconst u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci lockdep_assert_held(&priv->sta_lock); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci if (sta_id >= IWLAGN_STATION_COUNT) { 2262306a36Sopenharmony_ci IWL_ERR(priv, "invalid sta_id %u\n", sta_id); 2362306a36Sopenharmony_ci return -EINVAL; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) 2662306a36Sopenharmony_ci IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u " 2762306a36Sopenharmony_ci "addr %pM\n", 2862306a36Sopenharmony_ci sta_id, priv->stations[sta_id].sta.sta.addr); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { 3162306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, 3262306a36Sopenharmony_ci "STA id %u addr %pM already present in uCode " 3362306a36Sopenharmony_ci "(according to driver)\n", 3462306a36Sopenharmony_ci sta_id, priv->stations[sta_id].sta.sta.addr); 3562306a36Sopenharmony_ci } else { 3662306a36Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; 3762306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", 3862306a36Sopenharmony_ci sta_id, priv->stations[sta_id].sta.sta.addr); 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void iwl_process_add_sta_resp(struct iwl_priv *priv, 4462306a36Sopenharmony_ci struct iwl_rx_packet *pkt) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Processing response for adding station\n"); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci switch (add_sta_resp->status) { 5362306a36Sopenharmony_ci case ADD_STA_SUCCESS_MSK: 5462306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case ADD_STA_NO_ROOM_IN_TABLE: 5762306a36Sopenharmony_ci IWL_ERR(priv, "Adding station failed, no room in table.\n"); 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci case ADD_STA_NO_BLOCK_ACK_RESOURCE: 6062306a36Sopenharmony_ci IWL_ERR(priv, 6162306a36Sopenharmony_ci "Adding station failed, no block ack resource.\n"); 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci case ADD_STA_MODIFY_NON_EXIST_STA: 6462306a36Sopenharmony_ci IWL_ERR(priv, "Attempting to modify non-existing station\n"); 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci default: 6762306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", 6862306a36Sopenharmony_ci add_sta_resp->status); 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_civoid iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci iwl_process_add_sta_resp(priv, pkt); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ciint iwl_send_add_sta(struct iwl_priv *priv, 8362306a36Sopenharmony_ci struct iwl_addsta_cmd *sta, u8 flags) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int ret = 0; 8662306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 8762306a36Sopenharmony_ci .id = REPLY_ADD_STA, 8862306a36Sopenharmony_ci .flags = flags, 8962306a36Sopenharmony_ci .data = { sta, }, 9062306a36Sopenharmony_ci .len = { sizeof(*sta), }, 9162306a36Sopenharmony_ci }; 9262306a36Sopenharmony_ci u8 sta_id __maybe_unused = sta->sta.sta_id; 9362306a36Sopenharmony_ci struct iwl_rx_packet *pkt; 9462306a36Sopenharmony_ci struct iwl_add_sta_resp *add_sta_resp; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", 9762306a36Sopenharmony_ci sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!(flags & CMD_ASYNC)) { 10062306a36Sopenharmony_ci cmd.flags |= CMD_WANT_SKB; 10162306a36Sopenharmony_ci might_sleep(); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &cmd); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (ret || (flags & CMD_ASYNC)) 10762306a36Sopenharmony_ci return ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pkt = cmd.resp_pkt; 11062306a36Sopenharmony_ci add_sta_resp = (void *)pkt->data; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* debug messages are printed in the handler */ 11362306a36Sopenharmony_ci if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) { 11462306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 11562306a36Sopenharmony_ci ret = iwl_sta_ucode_activate(priv, sta_id); 11662306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 11762306a36Sopenharmony_ci } else { 11862306a36Sopenharmony_ci ret = -EIO; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci iwl_free_resp(&cmd); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cibool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, 12762306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 12862306a36Sopenharmony_ci struct ieee80211_sta *sta) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci if (!ctx->ht.enabled || !ctx->ht.is_40mhz) 13162306a36Sopenharmony_ci return false; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 13462306a36Sopenharmony_ci if (priv->disable_ht40) 13562306a36Sopenharmony_ci return false; 13662306a36Sopenharmony_ci#endif 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* special case for RXON */ 13962306a36Sopenharmony_ci if (!sta) 14062306a36Sopenharmony_ci return true; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void iwl_sta_calc_ht_flags(struct iwl_priv *priv, 14662306a36Sopenharmony_ci struct ieee80211_sta *sta, 14762306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 14862306a36Sopenharmony_ci __le32 *flags, __le32 *mask) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->deflink.ht_cap; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci *mask = STA_FLG_RTS_MIMO_PROT_MSK | 15362306a36Sopenharmony_ci STA_FLG_MIMO_DIS_MSK | 15462306a36Sopenharmony_ci STA_FLG_HT40_EN_MSK | 15562306a36Sopenharmony_ci STA_FLG_MAX_AGG_SIZE_MSK | 15662306a36Sopenharmony_ci STA_FLG_AGG_MPDU_DENSITY_MSK; 15762306a36Sopenharmony_ci *flags = 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!sta || !sta_ht_inf->ht_supported) 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", 16362306a36Sopenharmony_ci sta->addr, 16462306a36Sopenharmony_ci (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC) ? 16562306a36Sopenharmony_ci "static" : 16662306a36Sopenharmony_ci (sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC) ? 16762306a36Sopenharmony_ci "dynamic" : "disabled"); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci switch (sta->deflink.smps_mode) { 17062306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 17162306a36Sopenharmony_ci *flags |= STA_FLG_MIMO_DIS_MSK; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 17462306a36Sopenharmony_ci *flags |= STA_FLG_RTS_MIMO_PROT_MSK; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci default: 17962306a36Sopenharmony_ci IWL_WARN(priv, "Invalid MIMO PS mode %d\n", sta->deflink.smps_mode); 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci *flags |= cpu_to_le32( 18462306a36Sopenharmony_ci (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci *flags |= cpu_to_le32( 18762306a36Sopenharmony_ci (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) 19062306a36Sopenharmony_ci *flags |= STA_FLG_HT40_EN_MSK; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciint iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 19462306a36Sopenharmony_ci struct ieee80211_sta *sta) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci u8 sta_id = iwl_sta_id(sta); 19762306a36Sopenharmony_ci __le32 flags, mask; 19862306a36Sopenharmony_ci struct iwl_addsta_cmd cmd; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 20662306a36Sopenharmony_ci priv->stations[sta_id].sta.station_flags &= ~mask; 20762306a36Sopenharmony_ci priv->stations[sta_id].sta.station_flags |= flags; 20862306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 21162306a36Sopenharmony_ci cmd.mode = STA_CONTROL_MODIFY_MSK; 21262306a36Sopenharmony_ci cmd.station_flags_msk = mask; 21362306a36Sopenharmony_ci cmd.station_flags = flags; 21462306a36Sopenharmony_ci cmd.sta.sta_id = sta_id; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return iwl_send_add_sta(priv, &cmd, 0); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, 22062306a36Sopenharmony_ci struct ieee80211_sta *sta, 22162306a36Sopenharmony_ci struct iwl_rxon_context *ctx) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci __le32 flags, mask; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci lockdep_assert_held(&priv->sta_lock); 22862306a36Sopenharmony_ci priv->stations[index].sta.station_flags &= ~mask; 22962306a36Sopenharmony_ci priv->stations[index].sta.station_flags |= flags; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* 23362306a36Sopenharmony_ci * iwl_prep_station - Prepare station information for addition 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * should be called with sta_lock held 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ciu8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 23862306a36Sopenharmony_ci const u8 *addr, bool is_ap, struct ieee80211_sta *sta) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct iwl_station_entry *station; 24162306a36Sopenharmony_ci int i; 24262306a36Sopenharmony_ci u8 sta_id = IWL_INVALID_STATION; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (is_ap) 24562306a36Sopenharmony_ci sta_id = ctx->ap_sta_id; 24662306a36Sopenharmony_ci else if (is_broadcast_ether_addr(addr)) 24762306a36Sopenharmony_ci sta_id = ctx->bcast_sta_id; 24862306a36Sopenharmony_ci else 24962306a36Sopenharmony_ci for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) { 25062306a36Sopenharmony_ci if (ether_addr_equal(priv->stations[i].sta.sta.addr, 25162306a36Sopenharmony_ci addr)) { 25262306a36Sopenharmony_ci sta_id = i; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!priv->stations[i].used && 25762306a36Sopenharmony_ci sta_id == IWL_INVALID_STATION) 25862306a36Sopenharmony_ci sta_id = i; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * These two conditions have the same outcome, but keep them 26362306a36Sopenharmony_ci * separate 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci if (unlikely(sta_id == IWL_INVALID_STATION)) 26662306a36Sopenharmony_ci return sta_id; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * uCode is not able to deal with multiple requests to add a 27062306a36Sopenharmony_ci * station. Keep track if one is in progress so that we do not send 27162306a36Sopenharmony_ci * another. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { 27462306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "STA %d already in process of being " 27562306a36Sopenharmony_ci "added.\n", sta_id); 27662306a36Sopenharmony_ci return sta_id; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && 28062306a36Sopenharmony_ci (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && 28162306a36Sopenharmony_ci ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) { 28262306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " 28362306a36Sopenharmony_ci "adding again.\n", sta_id, addr); 28462306a36Sopenharmony_ci return sta_id; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci station = &priv->stations[sta_id]; 28862306a36Sopenharmony_ci station->used = IWL_STA_DRIVER_ACTIVE; 28962306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", 29062306a36Sopenharmony_ci sta_id, addr); 29162306a36Sopenharmony_ci priv->num_stations++; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Set up the REPLY_ADD_STA command to send to device */ 29462306a36Sopenharmony_ci memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); 29562306a36Sopenharmony_ci memcpy(station->sta.sta.addr, addr, ETH_ALEN); 29662306a36Sopenharmony_ci station->sta.mode = 0; 29762306a36Sopenharmony_ci station->sta.sta.sta_id = sta_id; 29862306a36Sopenharmony_ci station->sta.station_flags = ctx->station_flags; 29962306a36Sopenharmony_ci station->ctxid = ctx->ctxid; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (sta) { 30262306a36Sopenharmony_ci struct iwl_station_priv *sta_priv; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci sta_priv = (void *)sta->drv_priv; 30562306a36Sopenharmony_ci sta_priv->ctx = ctx; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * OK to call unconditionally, since local stations (IBSS BSSID 31062306a36Sopenharmony_ci * STA and broadcast STA) pass in a NULL sta, and mac80211 31162306a36Sopenharmony_ci * doesn't allow HT IBSS. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci iwl_set_ht_add_station(priv, sta_id, sta, ctx); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return sta_id; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci#define STA_WAIT_TIMEOUT (HZ/2) 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* 32262306a36Sopenharmony_ci * iwl_add_station_common - 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ciint iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 32562306a36Sopenharmony_ci const u8 *addr, bool is_ap, 32662306a36Sopenharmony_ci struct ieee80211_sta *sta, u8 *sta_id_r) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci int ret = 0; 32962306a36Sopenharmony_ci u8 sta_id; 33062306a36Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci *sta_id_r = 0; 33362306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 33462306a36Sopenharmony_ci sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); 33562306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 33662306a36Sopenharmony_ci IWL_ERR(priv, "Unable to prepare station %pM for addition\n", 33762306a36Sopenharmony_ci addr); 33862306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * uCode is not able to deal with multiple requests to add a 34462306a36Sopenharmony_ci * station. Keep track if one is in progress so that we do not send 34562306a36Sopenharmony_ci * another. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { 34862306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "STA %d already in process of being " 34962306a36Sopenharmony_ci "added.\n", sta_id); 35062306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 35162306a36Sopenharmony_ci return -EEXIST; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && 35562306a36Sopenharmony_ci (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { 35662306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " 35762306a36Sopenharmony_ci "adding again.\n", sta_id, addr); 35862306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 35962306a36Sopenharmony_ci return -EEXIST; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; 36362306a36Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, 36462306a36Sopenharmony_ci sizeof(struct iwl_addsta_cmd)); 36562306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Add station to device's station table */ 36862306a36Sopenharmony_ci ret = iwl_send_add_sta(priv, &sta_cmd, 0); 36962306a36Sopenharmony_ci if (ret) { 37062306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 37162306a36Sopenharmony_ci IWL_ERR(priv, "Adding station %pM failed.\n", 37262306a36Sopenharmony_ci priv->stations[sta_id].sta.sta.addr); 37362306a36Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; 37462306a36Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; 37562306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci *sta_id_r = sta_id; 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* 38262306a36Sopenharmony_ci * iwl_sta_ucode_deactivate - deactivate ucode status for a station 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_cistatic void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci lockdep_assert_held(&priv->sta_lock); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Ucode must be active and driver must be non active */ 38962306a36Sopenharmony_ci if ((priv->stations[sta_id].used & 39062306a36Sopenharmony_ci (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != 39162306a36Sopenharmony_ci IWL_STA_UCODE_ACTIVE) 39262306a36Sopenharmony_ci IWL_ERR(priv, "removed non active STA %u\n", sta_id); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); 39762306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int iwl_send_remove_station(struct iwl_priv *priv, 40162306a36Sopenharmony_ci const u8 *addr, int sta_id, 40262306a36Sopenharmony_ci bool temporary) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct iwl_rx_packet *pkt; 40562306a36Sopenharmony_ci int ret; 40662306a36Sopenharmony_ci struct iwl_rem_sta_cmd rm_sta_cmd; 40762306a36Sopenharmony_ci struct iwl_rem_sta_resp *rem_sta_resp; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 41062306a36Sopenharmony_ci .id = REPLY_REMOVE_STA, 41162306a36Sopenharmony_ci .len = { sizeof(struct iwl_rem_sta_cmd), }, 41262306a36Sopenharmony_ci .data = { &rm_sta_cmd, }, 41362306a36Sopenharmony_ci }; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); 41662306a36Sopenharmony_ci rm_sta_cmd.num_sta = 1; 41762306a36Sopenharmony_ci memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci cmd.flags |= CMD_WANT_SKB; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &cmd); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (ret) 42462306a36Sopenharmony_ci return ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci pkt = cmd.resp_pkt; 42762306a36Sopenharmony_ci rem_sta_resp = (void *)pkt->data; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci switch (rem_sta_resp->status) { 43062306a36Sopenharmony_ci case REM_STA_SUCCESS_MSK: 43162306a36Sopenharmony_ci if (!temporary) { 43262306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 43362306a36Sopenharmony_ci iwl_sta_ucode_deactivate(priv, sta_id); 43462306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci default: 43962306a36Sopenharmony_ci ret = -EIO; 44062306a36Sopenharmony_ci IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci iwl_free_resp(&cmd); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* 45062306a36Sopenharmony_ci * iwl_remove_station - Remove driver's knowledge of station. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ciint iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, 45362306a36Sopenharmony_ci const u8 *addr) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci u8 tid; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (!iwl_is_ready(priv)) { 45862306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, 45962306a36Sopenharmony_ci "Unable to remove station %pM, device not ready.\n", 46062306a36Sopenharmony_ci addr); 46162306a36Sopenharmony_ci /* 46262306a36Sopenharmony_ci * It is typical for stations to be removed when we are 46362306a36Sopenharmony_ci * going down. Return success since device will be down 46462306a36Sopenharmony_ci * soon anyway 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", 47062306a36Sopenharmony_ci sta_id, addr); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (WARN_ON(sta_id == IWL_INVALID_STATION)) 47362306a36Sopenharmony_ci return -EINVAL; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { 47862306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", 47962306a36Sopenharmony_ci addr); 48062306a36Sopenharmony_ci goto out_err; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { 48462306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", 48562306a36Sopenharmony_ci addr); 48662306a36Sopenharmony_ci goto out_err; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (priv->stations[sta_id].used & IWL_STA_LOCAL) { 49062306a36Sopenharmony_ci kfree(priv->stations[sta_id].lq); 49162306a36Sopenharmony_ci priv->stations[sta_id].lq = NULL; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) 49562306a36Sopenharmony_ci memset(&priv->tid_data[sta_id][tid], 0, 49662306a36Sopenharmony_ci sizeof(priv->tid_data[sta_id][tid])); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci priv->num_stations--; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (WARN_ON(priv->num_stations < 0)) 50362306a36Sopenharmony_ci priv->num_stations = 0; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return iwl_send_remove_station(priv, addr, sta_id, false); 50862306a36Sopenharmony_ciout_err: 50962306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 51062306a36Sopenharmony_ci return -EINVAL; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_civoid iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, 51462306a36Sopenharmony_ci const u8 *addr) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci u8 tid; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (!iwl_is_ready(priv)) { 51962306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, 52062306a36Sopenharmony_ci "Unable to remove station %pM, device not ready.\n", 52162306a36Sopenharmony_ci addr); 52262306a36Sopenharmony_ci return; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) 52862306a36Sopenharmony_ci return; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) 53562306a36Sopenharmony_ci memset(&priv->tid_data[sta_id][tid], 0, 53662306a36Sopenharmony_ci sizeof(priv->tid_data[sta_id][tid])); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; 53962306a36Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci priv->num_stations--; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (WARN_ON_ONCE(priv->num_stations < 0)) 54462306a36Sopenharmony_ci priv->num_stations = 0; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 55062306a36Sopenharmony_ci u8 sta_id, struct iwl_link_quality_cmd *link_cmd) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci int i, r; 55362306a36Sopenharmony_ci u32 rate_flags = 0; 55462306a36Sopenharmony_ci __le32 rate_n_flags; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci memset(link_cmd, 0, sizeof(*link_cmd)); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Set up the rate scaling to start at selected rate, fall back 56162306a36Sopenharmony_ci * all the way down to 1M in IEEE order, and then spin on 1M */ 56262306a36Sopenharmony_ci if (priv->band == NL80211_BAND_5GHZ) 56362306a36Sopenharmony_ci r = IWL_RATE_6M_INDEX; 56462306a36Sopenharmony_ci else if (ctx && ctx->vif && ctx->vif->p2p) 56562306a36Sopenharmony_ci r = IWL_RATE_6M_INDEX; 56662306a36Sopenharmony_ci else 56762306a36Sopenharmony_ci r = IWL_RATE_1M_INDEX; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) 57062306a36Sopenharmony_ci rate_flags |= RATE_MCS_CCK_MSK; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci rate_flags |= first_antenna(priv->nvm_data->valid_tx_ant) << 57362306a36Sopenharmony_ci RATE_MCS_ANT_POS; 57462306a36Sopenharmony_ci rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); 57562306a36Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) 57662306a36Sopenharmony_ci link_cmd->rs_table[i].rate_n_flags = rate_n_flags; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci link_cmd->general_params.single_stream_ant_msk = 57962306a36Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = 58262306a36Sopenharmony_ci priv->nvm_data->valid_tx_ant & 58362306a36Sopenharmony_ci ~first_antenna(priv->nvm_data->valid_tx_ant); 58462306a36Sopenharmony_ci if (!link_cmd->general_params.dual_stream_ant_msk) { 58562306a36Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = ANT_AB; 58662306a36Sopenharmony_ci } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) { 58762306a36Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = 58862306a36Sopenharmony_ci priv->nvm_data->valid_tx_ant; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci link_cmd->agg_params.agg_dis_start_th = 59262306a36Sopenharmony_ci LINK_QUAL_AGG_DISABLE_START_DEF; 59362306a36Sopenharmony_ci link_cmd->agg_params.agg_time_limit = 59462306a36Sopenharmony_ci cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci link_cmd->sta_id = sta_id; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* 60062306a36Sopenharmony_ci * iwl_clear_ucode_stations - clear ucode station table bits 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * This function clears all the bits in the driver indicating 60362306a36Sopenharmony_ci * which stations are active in the ucode. Call when something 60462306a36Sopenharmony_ci * other than explicit station management would cause this in 60562306a36Sopenharmony_ci * the ucode, e.g. unassociated RXON. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_civoid iwl_clear_ucode_stations(struct iwl_priv *priv, 60862306a36Sopenharmony_ci struct iwl_rxon_context *ctx) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci int i; 61162306a36Sopenharmony_ci bool cleared = false; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 61662306a36Sopenharmony_ci for (i = 0; i < IWLAGN_STATION_COUNT; i++) { 61762306a36Sopenharmony_ci if (ctx && ctx->ctxid != priv->stations[i].ctxid) 61862306a36Sopenharmony_ci continue; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { 62162306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, 62262306a36Sopenharmony_ci "Clearing ucode active for station %d\n", i); 62362306a36Sopenharmony_ci priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; 62462306a36Sopenharmony_ci cleared = true; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (!cleared) 63062306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, 63162306a36Sopenharmony_ci "No active stations found to be cleared\n"); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/* 63562306a36Sopenharmony_ci * iwl_restore_stations() - Restore driver known stations to device 63662306a36Sopenharmony_ci * 63762306a36Sopenharmony_ci * All stations considered active by driver, but not present in ucode, is 63862306a36Sopenharmony_ci * restored. 63962306a36Sopenharmony_ci * 64062306a36Sopenharmony_ci * Function sleeps. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_civoid iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 64562306a36Sopenharmony_ci static const struct iwl_link_quality_cmd zero_lq = {}; 64662306a36Sopenharmony_ci struct iwl_link_quality_cmd lq; 64762306a36Sopenharmony_ci int i; 64862306a36Sopenharmony_ci bool found = false; 64962306a36Sopenharmony_ci int ret; 65062306a36Sopenharmony_ci bool send_lq; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (!iwl_is_ready(priv)) { 65362306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, 65462306a36Sopenharmony_ci "Not ready yet, not restoring any stations.\n"); 65562306a36Sopenharmony_ci return; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); 65962306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 66062306a36Sopenharmony_ci for (i = 0; i < IWLAGN_STATION_COUNT; i++) { 66162306a36Sopenharmony_ci if (ctx->ctxid != priv->stations[i].ctxid) 66262306a36Sopenharmony_ci continue; 66362306a36Sopenharmony_ci if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && 66462306a36Sopenharmony_ci !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { 66562306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", 66662306a36Sopenharmony_ci priv->stations[i].sta.sta.addr); 66762306a36Sopenharmony_ci priv->stations[i].sta.mode = 0; 66862306a36Sopenharmony_ci priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; 66962306a36Sopenharmony_ci found = true; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci for (i = 0; i < IWLAGN_STATION_COUNT; i++) { 67462306a36Sopenharmony_ci if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { 67562306a36Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[i].sta, 67662306a36Sopenharmony_ci sizeof(struct iwl_addsta_cmd)); 67762306a36Sopenharmony_ci send_lq = false; 67862306a36Sopenharmony_ci if (priv->stations[i].lq) { 67962306a36Sopenharmony_ci if (priv->wowlan) 68062306a36Sopenharmony_ci iwl_sta_fill_lq(priv, ctx, i, &lq); 68162306a36Sopenharmony_ci else 68262306a36Sopenharmony_ci memcpy(&lq, priv->stations[i].lq, 68362306a36Sopenharmony_ci sizeof(struct iwl_link_quality_cmd)); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (memcmp(&lq, &zero_lq, sizeof(lq))) 68662306a36Sopenharmony_ci send_lq = true; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 68962306a36Sopenharmony_ci ret = iwl_send_add_sta(priv, &sta_cmd, 0); 69062306a36Sopenharmony_ci if (ret) { 69162306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 69262306a36Sopenharmony_ci IWL_ERR(priv, "Adding station %pM failed.\n", 69362306a36Sopenharmony_ci priv->stations[i].sta.sta.addr); 69462306a36Sopenharmony_ci priv->stations[i].used &= 69562306a36Sopenharmony_ci ~IWL_STA_DRIVER_ACTIVE; 69662306a36Sopenharmony_ci priv->stations[i].used &= 69762306a36Sopenharmony_ci ~IWL_STA_UCODE_INPROGRESS; 69862306a36Sopenharmony_ci continue; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * Rate scaling has already been initialized, send 70262306a36Sopenharmony_ci * current LQ command 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci if (send_lq) 70562306a36Sopenharmony_ci iwl_send_lq_cmd(priv, ctx, &lq, 0, true); 70662306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 70762306a36Sopenharmony_ci priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 71262306a36Sopenharmony_ci if (!found) 71362306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Restoring all known stations .... " 71462306a36Sopenharmony_ci "no stations to be restored.\n"); 71562306a36Sopenharmony_ci else 71662306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Restoring all known stations .... " 71762306a36Sopenharmony_ci "complete.\n"); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ciint iwl_get_free_ucode_key_offset(struct iwl_priv *priv) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci int i; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci for (i = 0; i < priv->sta_key_max_num; i++) 72562306a36Sopenharmony_ci if (!test_and_set_bit(i, &priv->ucode_key_table)) 72662306a36Sopenharmony_ci return i; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return WEP_INVALID_OFFSET; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_civoid iwl_dealloc_bcast_stations(struct iwl_priv *priv) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci int i; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 73662306a36Sopenharmony_ci for (i = 0; i < IWLAGN_STATION_COUNT; i++) { 73762306a36Sopenharmony_ci if (!(priv->stations[i].used & IWL_STA_BCAST)) 73862306a36Sopenharmony_ci continue; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; 74162306a36Sopenharmony_ci priv->num_stations--; 74262306a36Sopenharmony_ci if (WARN_ON(priv->num_stations < 0)) 74362306a36Sopenharmony_ci priv->num_stations = 0; 74462306a36Sopenharmony_ci kfree(priv->stations[i].lq); 74562306a36Sopenharmony_ci priv->stations[i].lq = NULL; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 75162306a36Sopenharmony_cistatic void iwl_dump_lq_cmd(struct iwl_priv *priv, 75262306a36Sopenharmony_ci struct iwl_link_quality_cmd *lq) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci int i; 75562306a36Sopenharmony_ci IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); 75662306a36Sopenharmony_ci IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", 75762306a36Sopenharmony_ci lq->general_params.single_stream_ant_msk, 75862306a36Sopenharmony_ci lq->general_params.dual_stream_ant_msk); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) 76162306a36Sopenharmony_ci IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", 76262306a36Sopenharmony_ci i, lq->rs_table[i].rate_n_flags); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci#else 76562306a36Sopenharmony_cistatic inline void iwl_dump_lq_cmd(struct iwl_priv *priv, 76662306a36Sopenharmony_ci struct iwl_link_quality_cmd *lq) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci#endif 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci/* 77262306a36Sopenharmony_ci * is_lq_table_valid() - Test one aspect of LQ cmd for validity 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * It sometimes happens when a HT rate has been in use and we 77562306a36Sopenharmony_ci * loose connectivity with AP then mac80211 will first tell us that the 77662306a36Sopenharmony_ci * current channel is not HT anymore before removing the station. In such a 77762306a36Sopenharmony_ci * scenario the RXON flags will be updated to indicate we are not 77862306a36Sopenharmony_ci * communicating HT anymore, but the LQ command may still contain HT rates. 77962306a36Sopenharmony_ci * Test for this to prevent driver from sending LQ command between the time 78062306a36Sopenharmony_ci * RXON flags are updated and when LQ command is updated. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_cistatic bool is_lq_table_valid(struct iwl_priv *priv, 78362306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 78462306a36Sopenharmony_ci struct iwl_link_quality_cmd *lq) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci int i; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (ctx->ht.enabled) 78962306a36Sopenharmony_ci return true; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", 79262306a36Sopenharmony_ci ctx->active.channel); 79362306a36Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { 79462306a36Sopenharmony_ci if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & 79562306a36Sopenharmony_ci RATE_MCS_HT_MSK) { 79662306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, 79762306a36Sopenharmony_ci "index %d of LQ expects HT channel\n", 79862306a36Sopenharmony_ci i); 79962306a36Sopenharmony_ci return false; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci return true; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/* 80662306a36Sopenharmony_ci * iwl_send_lq_cmd() - Send link quality command 80762306a36Sopenharmony_ci * @init: This command is sent as part of station initialization right 80862306a36Sopenharmony_ci * after station has been added. 80962306a36Sopenharmony_ci * 81062306a36Sopenharmony_ci * The link quality command is sent as the last step of station creation. 81162306a36Sopenharmony_ci * This is the special case in which init is set and we call a callback in 81262306a36Sopenharmony_ci * this case to clear the state indicating that station creation is in 81362306a36Sopenharmony_ci * progress. 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ciint iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 81662306a36Sopenharmony_ci struct iwl_link_quality_cmd *lq, u8 flags, bool init) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci int ret = 0; 81962306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 82062306a36Sopenharmony_ci .id = REPLY_TX_LINK_QUALITY_CMD, 82162306a36Sopenharmony_ci .len = { sizeof(struct iwl_link_quality_cmd), }, 82262306a36Sopenharmony_ci .flags = flags, 82362306a36Sopenharmony_ci .data = { lq, }, 82462306a36Sopenharmony_ci }; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) 82762306a36Sopenharmony_ci return -EINVAL; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 83162306a36Sopenharmony_ci if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) { 83262306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 83362306a36Sopenharmony_ci return -EINVAL; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci iwl_dump_lq_cmd(priv, lq); 83862306a36Sopenharmony_ci if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) 83962306a36Sopenharmony_ci return -EINVAL; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (is_lq_table_valid(priv, ctx, lq)) 84262306a36Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &cmd); 84362306a36Sopenharmony_ci else 84462306a36Sopenharmony_ci ret = -EINVAL; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (cmd.flags & CMD_ASYNC) 84762306a36Sopenharmony_ci return ret; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (init) { 85062306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "init LQ command complete, " 85162306a36Sopenharmony_ci "clearing sta addition status for sta %d\n", 85262306a36Sopenharmony_ci lq->sta_id); 85362306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 85462306a36Sopenharmony_ci priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; 85562306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci return ret; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic struct iwl_link_quality_cmd * 86262306a36Sopenharmony_ciiwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 86362306a36Sopenharmony_ci u8 sta_id) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct iwl_link_quality_cmd *link_cmd; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL); 86862306a36Sopenharmony_ci if (!link_cmd) { 86962306a36Sopenharmony_ci IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n"); 87062306a36Sopenharmony_ci return NULL; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return link_cmd; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci/* 87962306a36Sopenharmony_ci * iwlagn_add_bssid_station - Add the special IBSS BSSID station 88062306a36Sopenharmony_ci * 88162306a36Sopenharmony_ci * Function sleeps. 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ciint iwlagn_add_bssid_station(struct iwl_priv *priv, 88462306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 88562306a36Sopenharmony_ci const u8 *addr, u8 *sta_id_r) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci int ret; 88862306a36Sopenharmony_ci u8 sta_id; 88962306a36Sopenharmony_ci struct iwl_link_quality_cmd *link_cmd; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (sta_id_r) 89262306a36Sopenharmony_ci *sta_id_r = IWL_INVALID_STATION; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); 89562306a36Sopenharmony_ci if (ret) { 89662306a36Sopenharmony_ci IWL_ERR(priv, "Unable to add station %pM\n", addr); 89762306a36Sopenharmony_ci return ret; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (sta_id_r) 90162306a36Sopenharmony_ci *sta_id_r = sta_id; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 90462306a36Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_LOCAL; 90562306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci /* Set up default rate scaling table in device's station table */ 90862306a36Sopenharmony_ci link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); 90962306a36Sopenharmony_ci if (!link_cmd) { 91062306a36Sopenharmony_ci IWL_ERR(priv, 91162306a36Sopenharmony_ci "Unable to initialize rate scaling for station %pM.\n", 91262306a36Sopenharmony_ci addr); 91362306a36Sopenharmony_ci return -ENOMEM; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true); 91762306a36Sopenharmony_ci if (ret) 91862306a36Sopenharmony_ci IWL_ERR(priv, "Link quality command failed (%d)\n", ret); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 92162306a36Sopenharmony_ci priv->stations[sta_id].lq = link_cmd; 92262306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci/* 92862306a36Sopenharmony_ci * static WEP keys 92962306a36Sopenharmony_ci * 93062306a36Sopenharmony_ci * For each context, the device has a table of 4 static WEP keys 93162306a36Sopenharmony_ci * (one for each key index) that is updated with the following 93262306a36Sopenharmony_ci * commands. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, 93662306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 93762306a36Sopenharmony_ci bool send_if_empty) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci int i, not_empty = 0; 94062306a36Sopenharmony_ci u8 buff[sizeof(struct iwl_wep_cmd) + 94162306a36Sopenharmony_ci sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; 94262306a36Sopenharmony_ci struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; 94362306a36Sopenharmony_ci size_t cmd_size = sizeof(struct iwl_wep_cmd); 94462306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 94562306a36Sopenharmony_ci .id = ctx->wep_key_cmd, 94662306a36Sopenharmony_ci .data = { wep_cmd, }, 94762306a36Sopenharmony_ci }; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci might_sleep(); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci memset(wep_cmd, 0, cmd_size + 95262306a36Sopenharmony_ci (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci for (i = 0; i < WEP_KEYS_MAX ; i++) { 95562306a36Sopenharmony_ci wep_cmd->key[i].key_index = i; 95662306a36Sopenharmony_ci if (ctx->wep_keys[i].key_size) { 95762306a36Sopenharmony_ci wep_cmd->key[i].key_offset = i; 95862306a36Sopenharmony_ci not_empty = 1; 95962306a36Sopenharmony_ci } else { 96062306a36Sopenharmony_ci wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size; 96462306a36Sopenharmony_ci memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key, 96562306a36Sopenharmony_ci ctx->wep_keys[i].key_size); 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; 96962306a36Sopenharmony_ci wep_cmd->num_keys = WEP_KEYS_MAX; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci cmd.len[0] = cmd_size; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (not_empty || send_if_empty) 97662306a36Sopenharmony_ci return iwl_dvm_send_cmd(priv, &cmd); 97762306a36Sopenharmony_ci else 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ciint iwl_restore_default_wep_keys(struct iwl_priv *priv, 98262306a36Sopenharmony_ci struct iwl_rxon_context *ctx) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci return iwl_send_static_wepkey_cmd(priv, ctx, false); 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ciint iwl_remove_default_wep_key(struct iwl_priv *priv, 99062306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 99162306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci int ret; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", 99862306a36Sopenharmony_ci keyconf->keyidx); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0])); 100162306a36Sopenharmony_ci if (iwl_is_rfkill(priv)) { 100262306a36Sopenharmony_ci IWL_DEBUG_WEP(priv, 100362306a36Sopenharmony_ci "Not sending REPLY_WEPKEY command due to RFKILL.\n"); 100462306a36Sopenharmony_ci /* but keys in device are clear anyway so return success */ 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci ret = iwl_send_static_wepkey_cmd(priv, ctx, 1); 100862306a36Sopenharmony_ci IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", 100962306a36Sopenharmony_ci keyconf->keyidx, ret); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci return ret; 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ciint iwl_set_default_wep_key(struct iwl_priv *priv, 101562306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 101662306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci int ret; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (keyconf->keylen != WEP_KEY_LEN_128 && 102362306a36Sopenharmony_ci keyconf->keylen != WEP_KEY_LEN_64) { 102462306a36Sopenharmony_ci IWL_DEBUG_WEP(priv, 102562306a36Sopenharmony_ci "Bad WEP key length %d\n", keyconf->keylen); 102662306a36Sopenharmony_ci return -EINVAL; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; 103262306a36Sopenharmony_ci memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key, 103362306a36Sopenharmony_ci keyconf->keylen); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci ret = iwl_send_static_wepkey_cmd(priv, ctx, false); 103662306a36Sopenharmony_ci IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", 103762306a36Sopenharmony_ci keyconf->keylen, keyconf->keyidx, ret); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci return ret; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci/* 104362306a36Sopenharmony_ci * dynamic (per-station) keys 104462306a36Sopenharmony_ci * 104562306a36Sopenharmony_ci * The dynamic keys are a little more complicated. The device has 104662306a36Sopenharmony_ci * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys. 104762306a36Sopenharmony_ci * These are linked to stations by a table that contains an index 104862306a36Sopenharmony_ci * into the key table for each station/key index/{mcast,unicast}, 104962306a36Sopenharmony_ci * i.e. it's basically an array of pointers like this: 105062306a36Sopenharmony_ci * key_offset_t key_mapping[NUM_STATIONS][4][2]; 105162306a36Sopenharmony_ci * (it really works differently, but you can think of it as such) 105262306a36Sopenharmony_ci * 105362306a36Sopenharmony_ci * The key uploading and linking happens in the same command, the 105462306a36Sopenharmony_ci * add station command with STA_MODIFY_KEY_MASK. 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic u8 iwlagn_key_sta_id(struct iwl_priv *priv, 105862306a36Sopenharmony_ci struct ieee80211_vif *vif, 105962306a36Sopenharmony_ci struct ieee80211_sta *sta) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (sta) 106462306a36Sopenharmony_ci return iwl_sta_id(sta); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* 106762306a36Sopenharmony_ci * The device expects GTKs for station interfaces to be 106862306a36Sopenharmony_ci * installed as GTKs for the AP station. If we have no 106962306a36Sopenharmony_ci * station ID, then use the ap_sta_id in that case. 107062306a36Sopenharmony_ci */ 107162306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx) 107262306a36Sopenharmony_ci return vif_priv->ctx->ap_sta_id; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci return IWL_INVALID_STATION; 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic int iwlagn_send_sta_key(struct iwl_priv *priv, 107862306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, 107962306a36Sopenharmony_ci u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, 108062306a36Sopenharmony_ci u32 cmd_flags) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci __le16 key_flags; 108362306a36Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 108462306a36Sopenharmony_ci size_t to_copy; 108562306a36Sopenharmony_ci int i; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 108862306a36Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); 108962306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); 109262306a36Sopenharmony_ci key_flags |= STA_KEY_FLG_MAP_KEY_MSK; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci switch (keyconf->cipher) { 109562306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 109662306a36Sopenharmony_ci key_flags |= STA_KEY_FLG_CCMP; 109762306a36Sopenharmony_ci memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); 109862306a36Sopenharmony_ci break; 109962306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 110062306a36Sopenharmony_ci key_flags |= STA_KEY_FLG_TKIP; 110162306a36Sopenharmony_ci sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; 110262306a36Sopenharmony_ci for (i = 0; i < 5; i++) 110362306a36Sopenharmony_ci sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); 110462306a36Sopenharmony_ci /* keyconf may contain MIC rx/tx keys which iwl does not use */ 110562306a36Sopenharmony_ci to_copy = min_t(size_t, sizeof(sta_cmd.key.key), keyconf->keylen); 110662306a36Sopenharmony_ci memcpy(sta_cmd.key.key, keyconf->key, to_copy); 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 110962306a36Sopenharmony_ci key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; 111062306a36Sopenharmony_ci fallthrough; 111162306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 111262306a36Sopenharmony_ci key_flags |= STA_KEY_FLG_WEP; 111362306a36Sopenharmony_ci memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen); 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci default: 111662306a36Sopenharmony_ci WARN_ON(1); 111762306a36Sopenharmony_ci return -EINVAL; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) 112162306a36Sopenharmony_ci key_flags |= STA_KEY_MULTICAST_MSK; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* key pointer (offset) */ 112462306a36Sopenharmony_ci sta_cmd.key.key_offset = keyconf->hw_key_idx; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci sta_cmd.key.key_flags = key_flags; 112762306a36Sopenharmony_ci sta_cmd.mode = STA_CONTROL_MODIFY_MSK; 112862306a36Sopenharmony_ci sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, cmd_flags); 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_civoid iwl_update_tkip_key(struct iwl_priv *priv, 113462306a36Sopenharmony_ci struct ieee80211_vif *vif, 113562306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, 113662306a36Sopenharmony_ci struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci u8 sta_id = iwlagn_key_sta_id(priv, vif, sta); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 114162306a36Sopenharmony_ci return; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (iwl_scan_cancel(priv)) { 114462306a36Sopenharmony_ci /* cancel scan failed, just live w/ bad key and rely 114562306a36Sopenharmony_ci briefly on SW decryption */ 114662306a36Sopenharmony_ci return; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci iwlagn_send_sta_key(priv, keyconf, sta_id, 115062306a36Sopenharmony_ci iv32, phase1key, CMD_ASYNC); 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ciint iwl_remove_dynamic_key(struct iwl_priv *priv, 115462306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 115562306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, 115662306a36Sopenharmony_ci struct ieee80211_sta *sta) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 115962306a36Sopenharmony_ci u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); 116062306a36Sopenharmony_ci __le16 key_flags; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* if station isn't there, neither is the key */ 116362306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 116462306a36Sopenharmony_ci return -ENOENT; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 116762306a36Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); 116862306a36Sopenharmony_ci if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) 116962306a36Sopenharmony_ci sta_id = IWL_INVALID_STATION; 117062306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci ctx->key_mapping_keys--; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", 118062306a36Sopenharmony_ci keyconf->keyidx, sta_id); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table)) 118362306a36Sopenharmony_ci IWL_ERR(priv, "offset %d not used in uCode key table.\n", 118462306a36Sopenharmony_ci keyconf->hw_key_idx); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); 118762306a36Sopenharmony_ci key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC | 118862306a36Sopenharmony_ci STA_KEY_FLG_INVALID; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) 119162306a36Sopenharmony_ci key_flags |= STA_KEY_MULTICAST_MSK; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci sta_cmd.key.key_flags = key_flags; 119462306a36Sopenharmony_ci sta_cmd.key.key_offset = keyconf->hw_key_idx; 119562306a36Sopenharmony_ci sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; 119662306a36Sopenharmony_ci sta_cmd.mode = STA_CONTROL_MODIFY_MSK; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, 0); 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ciint iwl_set_dynamic_key(struct iwl_priv *priv, 120262306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 120362306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, 120462306a36Sopenharmony_ci struct ieee80211_sta *sta) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct ieee80211_key_seq seq; 120762306a36Sopenharmony_ci u16 p1k[5]; 120862306a36Sopenharmony_ci int ret; 120962306a36Sopenharmony_ci u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); 121062306a36Sopenharmony_ci const u8 *addr; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 121362306a36Sopenharmony_ci return -EINVAL; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv); 121862306a36Sopenharmony_ci if (keyconf->hw_key_idx == WEP_INVALID_OFFSET) 121962306a36Sopenharmony_ci return -ENOSPC; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci ctx->key_mapping_keys++; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci switch (keyconf->cipher) { 122462306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 122562306a36Sopenharmony_ci if (sta) 122662306a36Sopenharmony_ci addr = sta->addr; 122762306a36Sopenharmony_ci else /* station mode case only */ 122862306a36Sopenharmony_ci addr = ctx->active.bssid_addr; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* pre-fill phase 1 key into device cache */ 123162306a36Sopenharmony_ci ieee80211_get_key_rx_seq(keyconf, 0, &seq); 123262306a36Sopenharmony_ci ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); 123362306a36Sopenharmony_ci ret = iwlagn_send_sta_key(priv, keyconf, sta_id, 123462306a36Sopenharmony_ci seq.tkip.iv32, p1k, 0); 123562306a36Sopenharmony_ci break; 123662306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 123762306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 123862306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 123962306a36Sopenharmony_ci ret = iwlagn_send_sta_key(priv, keyconf, sta_id, 124062306a36Sopenharmony_ci 0, NULL, 0); 124162306a36Sopenharmony_ci break; 124262306a36Sopenharmony_ci default: 124362306a36Sopenharmony_ci IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); 124462306a36Sopenharmony_ci ret = -EINVAL; 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci if (ret) { 124862306a36Sopenharmony_ci ctx->key_mapping_keys--; 124962306a36Sopenharmony_ci clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table); 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", 125362306a36Sopenharmony_ci keyconf->cipher, keyconf->keylen, keyconf->keyidx, 125462306a36Sopenharmony_ci sta ? sta->addr : NULL, ret); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci return ret; 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci/* 126062306a36Sopenharmony_ci * iwlagn_alloc_bcast_station - add broadcast station into driver's station table. 126162306a36Sopenharmony_ci * 126262306a36Sopenharmony_ci * This adds the broadcast station into the driver's station table 126362306a36Sopenharmony_ci * and marks it driver active, so that it will be restored to the 126462306a36Sopenharmony_ci * device at the next best time. 126562306a36Sopenharmony_ci */ 126662306a36Sopenharmony_ciint iwlagn_alloc_bcast_station(struct iwl_priv *priv, 126762306a36Sopenharmony_ci struct iwl_rxon_context *ctx) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci struct iwl_link_quality_cmd *link_cmd; 127062306a36Sopenharmony_ci u8 sta_id; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 127362306a36Sopenharmony_ci sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); 127462306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 127562306a36Sopenharmony_ci IWL_ERR(priv, "Unable to prepare broadcast station\n"); 127662306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci return -EINVAL; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; 128262306a36Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_BCAST; 128362306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); 128662306a36Sopenharmony_ci if (!link_cmd) { 128762306a36Sopenharmony_ci IWL_ERR(priv, 128862306a36Sopenharmony_ci "Unable to initialize rate scaling for bcast station.\n"); 128962306a36Sopenharmony_ci return -ENOMEM; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 129362306a36Sopenharmony_ci priv->stations[sta_id].lq = link_cmd; 129462306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci return 0; 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci/* 130062306a36Sopenharmony_ci * iwl_update_bcast_station - update broadcast station's LQ command 130162306a36Sopenharmony_ci * 130262306a36Sopenharmony_ci * Only used by iwlagn. Placed here to have all bcast station management 130362306a36Sopenharmony_ci * code together. 130462306a36Sopenharmony_ci */ 130562306a36Sopenharmony_ciint iwl_update_bcast_station(struct iwl_priv *priv, 130662306a36Sopenharmony_ci struct iwl_rxon_context *ctx) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci struct iwl_link_quality_cmd *link_cmd; 130962306a36Sopenharmony_ci u8 sta_id = ctx->bcast_sta_id; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); 131262306a36Sopenharmony_ci if (!link_cmd) { 131362306a36Sopenharmony_ci IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); 131462306a36Sopenharmony_ci return -ENOMEM; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 131862306a36Sopenharmony_ci if (priv->stations[sta_id].lq) 131962306a36Sopenharmony_ci kfree(priv->stations[sta_id].lq); 132062306a36Sopenharmony_ci else 132162306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n"); 132262306a36Sopenharmony_ci priv->stations[sta_id].lq = link_cmd; 132362306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci return 0; 132662306a36Sopenharmony_ci} 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ciint iwl_update_bcast_stations(struct iwl_priv *priv) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 133162306a36Sopenharmony_ci int ret = 0; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci for_each_context(priv, ctx) { 133462306a36Sopenharmony_ci ret = iwl_update_bcast_station(priv, ctx); 133562306a36Sopenharmony_ci if (ret) 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci return ret; 134062306a36Sopenharmony_ci} 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci/* 134362306a36Sopenharmony_ci * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table 134462306a36Sopenharmony_ci */ 134562306a36Sopenharmony_ciint iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) 134662306a36Sopenharmony_ci{ 134762306a36Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci /* Remove "disable" flag, to enable Tx for this TID */ 135262306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 135362306a36Sopenharmony_ci priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; 135462306a36Sopenharmony_ci priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); 135562306a36Sopenharmony_ci priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 135662306a36Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); 135762306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, 0); 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ciint iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, 136362306a36Sopenharmony_ci int tid, u16 ssn) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci int sta_id; 136662306a36Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci sta_id = iwl_sta_id(sta); 137162306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 137262306a36Sopenharmony_ci return -ENXIO; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 137562306a36Sopenharmony_ci priv->stations[sta_id].sta.station_flags_msk = 0; 137662306a36Sopenharmony_ci priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; 137762306a36Sopenharmony_ci priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; 137862306a36Sopenharmony_ci priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); 137962306a36Sopenharmony_ci priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 138062306a36Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); 138162306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, 0); 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ciint iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, 138762306a36Sopenharmony_ci int tid) 138862306a36Sopenharmony_ci{ 138962306a36Sopenharmony_ci int sta_id; 139062306a36Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci sta_id = iwl_sta_id(sta); 139562306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 139662306a36Sopenharmony_ci IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); 139762306a36Sopenharmony_ci return -ENXIO; 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 140162306a36Sopenharmony_ci priv->stations[sta_id].sta.station_flags_msk = 0; 140262306a36Sopenharmony_ci priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; 140362306a36Sopenharmony_ci priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; 140462306a36Sopenharmony_ci priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 140562306a36Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); 140662306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, 0); 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_civoid iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci struct iwl_addsta_cmd cmd = { 141662306a36Sopenharmony_ci .mode = STA_CONTROL_MODIFY_MSK, 141762306a36Sopenharmony_ci .station_flags = STA_FLG_PWR_SAVE_MSK, 141862306a36Sopenharmony_ci .station_flags_msk = STA_FLG_PWR_SAVE_MSK, 141962306a36Sopenharmony_ci .sta.sta_id = sta_id, 142062306a36Sopenharmony_ci .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK, 142162306a36Sopenharmony_ci .sleep_tx_count = cpu_to_le16(cnt), 142262306a36Sopenharmony_ci }; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci iwl_send_add_sta(priv, &cmd, CMD_ASYNC); 142562306a36Sopenharmony_ci} 1426