18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Portions of this file are derived from the ipw3945 project, as well 78c2ecf20Sopenharmony_ci * as portions of the ieee80211 subsystem header files. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Contact Information: 108c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 118c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci *****************************************************************************/ 148c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 158c2ecf20Sopenharmony_ci#include <net/mac80211.h> 168c2ecf20Sopenharmony_ci#include "iwl-trans.h" 178c2ecf20Sopenharmony_ci#include "dev.h" 188c2ecf20Sopenharmony_ci#include "agn.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciconst u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->sta_lock); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (sta_id >= IWLAGN_STATION_COUNT) { 278c2ecf20Sopenharmony_ci IWL_ERR(priv, "invalid sta_id %u\n", sta_id); 288c2ecf20Sopenharmony_ci return -EINVAL; 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) 318c2ecf20Sopenharmony_ci IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u " 328c2ecf20Sopenharmony_ci "addr %pM\n", 338c2ecf20Sopenharmony_ci sta_id, priv->stations[sta_id].sta.sta.addr); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { 368c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, 378c2ecf20Sopenharmony_ci "STA id %u addr %pM already present in uCode " 388c2ecf20Sopenharmony_ci "(according to driver)\n", 398c2ecf20Sopenharmony_ci sta_id, priv->stations[sta_id].sta.sta.addr); 408c2ecf20Sopenharmony_ci } else { 418c2ecf20Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; 428c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", 438c2ecf20Sopenharmony_ci sta_id, priv->stations[sta_id].sta.sta.addr); 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void iwl_process_add_sta_resp(struct iwl_priv *priv, 498c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Processing response for adding station\n"); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci switch (add_sta_resp->status) { 588c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS_MSK: 598c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case ADD_STA_NO_ROOM_IN_TABLE: 628c2ecf20Sopenharmony_ci IWL_ERR(priv, "Adding station failed, no room in table.\n"); 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci case ADD_STA_NO_BLOCK_ACK_RESOURCE: 658c2ecf20Sopenharmony_ci IWL_ERR(priv, 668c2ecf20Sopenharmony_ci "Adding station failed, no block ack resource.\n"); 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci case ADD_STA_MODIFY_NON_EXIST_STA: 698c2ecf20Sopenharmony_ci IWL_ERR(priv, "Attempting to modify non-existing station\n"); 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci default: 728c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", 738c2ecf20Sopenharmony_ci add_sta_resp->status); 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_civoid iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci iwl_process_add_sta_resp(priv, pkt); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciint iwl_send_add_sta(struct iwl_priv *priv, 888c2ecf20Sopenharmony_ci struct iwl_addsta_cmd *sta, u8 flags) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int ret = 0; 918c2ecf20Sopenharmony_ci struct iwl_host_cmd cmd = { 928c2ecf20Sopenharmony_ci .id = REPLY_ADD_STA, 938c2ecf20Sopenharmony_ci .flags = flags, 948c2ecf20Sopenharmony_ci .data = { sta, }, 958c2ecf20Sopenharmony_ci .len = { sizeof(*sta), }, 968c2ecf20Sopenharmony_ci }; 978c2ecf20Sopenharmony_ci u8 sta_id __maybe_unused = sta->sta.sta_id; 988c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt; 998c2ecf20Sopenharmony_ci struct iwl_add_sta_resp *add_sta_resp; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", 1028c2ecf20Sopenharmony_ci sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (!(flags & CMD_ASYNC)) { 1058c2ecf20Sopenharmony_ci cmd.flags |= CMD_WANT_SKB; 1068c2ecf20Sopenharmony_ci might_sleep(); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &cmd); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (ret || (flags & CMD_ASYNC)) 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci pkt = cmd.resp_pkt; 1158c2ecf20Sopenharmony_ci add_sta_resp = (void *)pkt->data; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* debug messages are printed in the handler */ 1188c2ecf20Sopenharmony_ci if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) { 1198c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 1208c2ecf20Sopenharmony_ci ret = iwl_sta_ucode_activate(priv, sta_id); 1218c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 1228c2ecf20Sopenharmony_ci } else { 1238c2ecf20Sopenharmony_ci ret = -EIO; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci iwl_free_resp(&cmd); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cibool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, 1328c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 1338c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci if (!ctx->ht.enabled || !ctx->ht.is_40mhz) 1368c2ecf20Sopenharmony_ci return false; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 1398c2ecf20Sopenharmony_ci if (priv->disable_ht40) 1408c2ecf20Sopenharmony_ci return false; 1418c2ecf20Sopenharmony_ci#endif 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* special case for RXON */ 1448c2ecf20Sopenharmony_ci if (!sta) 1458c2ecf20Sopenharmony_ci return true; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return sta->bandwidth >= IEEE80211_STA_RX_BW_40; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void iwl_sta_calc_ht_flags(struct iwl_priv *priv, 1518c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 1528c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 1538c2ecf20Sopenharmony_ci __le32 *flags, __le32 *mask) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci *mask = STA_FLG_RTS_MIMO_PROT_MSK | 1588c2ecf20Sopenharmony_ci STA_FLG_MIMO_DIS_MSK | 1598c2ecf20Sopenharmony_ci STA_FLG_HT40_EN_MSK | 1608c2ecf20Sopenharmony_ci STA_FLG_MAX_AGG_SIZE_MSK | 1618c2ecf20Sopenharmony_ci STA_FLG_AGG_MPDU_DENSITY_MSK; 1628c2ecf20Sopenharmony_ci *flags = 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!sta || !sta_ht_inf->ht_supported) 1658c2ecf20Sopenharmony_ci return; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", 1688c2ecf20Sopenharmony_ci sta->addr, 1698c2ecf20Sopenharmony_ci (sta->smps_mode == IEEE80211_SMPS_STATIC) ? 1708c2ecf20Sopenharmony_ci "static" : 1718c2ecf20Sopenharmony_ci (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? 1728c2ecf20Sopenharmony_ci "dynamic" : "disabled"); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci switch (sta->smps_mode) { 1758c2ecf20Sopenharmony_ci case IEEE80211_SMPS_STATIC: 1768c2ecf20Sopenharmony_ci *flags |= STA_FLG_MIMO_DIS_MSK; 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 1798c2ecf20Sopenharmony_ci *flags |= STA_FLG_RTS_MIMO_PROT_MSK; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci case IEEE80211_SMPS_OFF: 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci default: 1848c2ecf20Sopenharmony_ci IWL_WARN(priv, "Invalid MIMO PS mode %d\n", sta->smps_mode); 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci *flags |= cpu_to_le32( 1898c2ecf20Sopenharmony_ci (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci *flags |= cpu_to_le32( 1928c2ecf20Sopenharmony_ci (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) 1958c2ecf20Sopenharmony_ci *flags |= STA_FLG_HT40_EN_MSK; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciint iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 1998c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci u8 sta_id = iwl_sta_id(sta); 2028c2ecf20Sopenharmony_ci __le32 flags, mask; 2038c2ecf20Sopenharmony_ci struct iwl_addsta_cmd cmd; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) 2068c2ecf20Sopenharmony_ci return -EINVAL; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 2118c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.station_flags &= ~mask; 2128c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.station_flags |= flags; 2138c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 2168c2ecf20Sopenharmony_ci cmd.mode = STA_CONTROL_MODIFY_MSK; 2178c2ecf20Sopenharmony_ci cmd.station_flags_msk = mask; 2188c2ecf20Sopenharmony_ci cmd.station_flags = flags; 2198c2ecf20Sopenharmony_ci cmd.sta.sta_id = sta_id; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return iwl_send_add_sta(priv, &cmd, 0); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, 2258c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 2268c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci __le32 flags, mask; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->sta_lock); 2338c2ecf20Sopenharmony_ci priv->stations[index].sta.station_flags &= ~mask; 2348c2ecf20Sopenharmony_ci priv->stations[index].sta.station_flags |= flags; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* 2388c2ecf20Sopenharmony_ci * iwl_prep_station - Prepare station information for addition 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * should be called with sta_lock held 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ciu8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 2438c2ecf20Sopenharmony_ci const u8 *addr, bool is_ap, struct ieee80211_sta *sta) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct iwl_station_entry *station; 2468c2ecf20Sopenharmony_ci int i; 2478c2ecf20Sopenharmony_ci u8 sta_id = IWL_INVALID_STATION; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (is_ap) 2508c2ecf20Sopenharmony_ci sta_id = ctx->ap_sta_id; 2518c2ecf20Sopenharmony_ci else if (is_broadcast_ether_addr(addr)) 2528c2ecf20Sopenharmony_ci sta_id = ctx->bcast_sta_id; 2538c2ecf20Sopenharmony_ci else 2548c2ecf20Sopenharmony_ci for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) { 2558c2ecf20Sopenharmony_ci if (ether_addr_equal(priv->stations[i].sta.sta.addr, 2568c2ecf20Sopenharmony_ci addr)) { 2578c2ecf20Sopenharmony_ci sta_id = i; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (!priv->stations[i].used && 2628c2ecf20Sopenharmony_ci sta_id == IWL_INVALID_STATION) 2638c2ecf20Sopenharmony_ci sta_id = i; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* 2678c2ecf20Sopenharmony_ci * These two conditions have the same outcome, but keep them 2688c2ecf20Sopenharmony_ci * separate 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci if (unlikely(sta_id == IWL_INVALID_STATION)) 2718c2ecf20Sopenharmony_ci return sta_id; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * uCode is not able to deal with multiple requests to add a 2758c2ecf20Sopenharmony_ci * station. Keep track if one is in progress so that we do not send 2768c2ecf20Sopenharmony_ci * another. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { 2798c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "STA %d already in process of being " 2808c2ecf20Sopenharmony_ci "added.\n", sta_id); 2818c2ecf20Sopenharmony_ci return sta_id; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && 2858c2ecf20Sopenharmony_ci (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && 2868c2ecf20Sopenharmony_ci ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) { 2878c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " 2888c2ecf20Sopenharmony_ci "adding again.\n", sta_id, addr); 2898c2ecf20Sopenharmony_ci return sta_id; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci station = &priv->stations[sta_id]; 2938c2ecf20Sopenharmony_ci station->used = IWL_STA_DRIVER_ACTIVE; 2948c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", 2958c2ecf20Sopenharmony_ci sta_id, addr); 2968c2ecf20Sopenharmony_ci priv->num_stations++; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Set up the REPLY_ADD_STA command to send to device */ 2998c2ecf20Sopenharmony_ci memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); 3008c2ecf20Sopenharmony_ci memcpy(station->sta.sta.addr, addr, ETH_ALEN); 3018c2ecf20Sopenharmony_ci station->sta.mode = 0; 3028c2ecf20Sopenharmony_ci station->sta.sta.sta_id = sta_id; 3038c2ecf20Sopenharmony_ci station->sta.station_flags = ctx->station_flags; 3048c2ecf20Sopenharmony_ci station->ctxid = ctx->ctxid; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (sta) { 3078c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci sta_priv = (void *)sta->drv_priv; 3108c2ecf20Sopenharmony_ci sta_priv->ctx = ctx; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * OK to call unconditionally, since local stations (IBSS BSSID 3158c2ecf20Sopenharmony_ci * STA and broadcast STA) pass in a NULL sta, and mac80211 3168c2ecf20Sopenharmony_ci * doesn't allow HT IBSS. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci iwl_set_ht_add_station(priv, sta_id, sta, ctx); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return sta_id; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci#define STA_WAIT_TIMEOUT (HZ/2) 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* 3278c2ecf20Sopenharmony_ci * iwl_add_station_common - 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ciint iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 3308c2ecf20Sopenharmony_ci const u8 *addr, bool is_ap, 3318c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u8 *sta_id_r) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci int ret = 0; 3348c2ecf20Sopenharmony_ci u8 sta_id; 3358c2ecf20Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci *sta_id_r = 0; 3388c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 3398c2ecf20Sopenharmony_ci sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); 3408c2ecf20Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 3418c2ecf20Sopenharmony_ci IWL_ERR(priv, "Unable to prepare station %pM for addition\n", 3428c2ecf20Sopenharmony_ci addr); 3438c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * uCode is not able to deal with multiple requests to add a 3498c2ecf20Sopenharmony_ci * station. Keep track if one is in progress so that we do not send 3508c2ecf20Sopenharmony_ci * another. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { 3538c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "STA %d already in process of being " 3548c2ecf20Sopenharmony_ci "added.\n", sta_id); 3558c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 3568c2ecf20Sopenharmony_ci return -EEXIST; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && 3608c2ecf20Sopenharmony_ci (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { 3618c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " 3628c2ecf20Sopenharmony_ci "adding again.\n", sta_id, addr); 3638c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 3648c2ecf20Sopenharmony_ci return -EEXIST; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; 3688c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, 3698c2ecf20Sopenharmony_ci sizeof(struct iwl_addsta_cmd)); 3708c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Add station to device's station table */ 3738c2ecf20Sopenharmony_ci ret = iwl_send_add_sta(priv, &sta_cmd, 0); 3748c2ecf20Sopenharmony_ci if (ret) { 3758c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 3768c2ecf20Sopenharmony_ci IWL_ERR(priv, "Adding station %pM failed.\n", 3778c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.sta.addr); 3788c2ecf20Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; 3798c2ecf20Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; 3808c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci *sta_id_r = sta_id; 3838c2ecf20Sopenharmony_ci return ret; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * iwl_sta_ucode_deactivate - deactivate ucode status for a station 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_cistatic void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->sta_lock); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* Ucode must be active and driver must be non active */ 3948c2ecf20Sopenharmony_ci if ((priv->stations[sta_id].used & 3958c2ecf20Sopenharmony_ci (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != 3968c2ecf20Sopenharmony_ci IWL_STA_UCODE_ACTIVE) 3978c2ecf20Sopenharmony_ci IWL_ERR(priv, "removed non active STA %u\n", sta_id); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); 4028c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int iwl_send_remove_station(struct iwl_priv *priv, 4068c2ecf20Sopenharmony_ci const u8 *addr, int sta_id, 4078c2ecf20Sopenharmony_ci bool temporary) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt; 4108c2ecf20Sopenharmony_ci int ret; 4118c2ecf20Sopenharmony_ci struct iwl_rem_sta_cmd rm_sta_cmd; 4128c2ecf20Sopenharmony_ci struct iwl_rem_sta_resp *rem_sta_resp; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci struct iwl_host_cmd cmd = { 4158c2ecf20Sopenharmony_ci .id = REPLY_REMOVE_STA, 4168c2ecf20Sopenharmony_ci .len = { sizeof(struct iwl_rem_sta_cmd), }, 4178c2ecf20Sopenharmony_ci .data = { &rm_sta_cmd, }, 4188c2ecf20Sopenharmony_ci }; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); 4218c2ecf20Sopenharmony_ci rm_sta_cmd.num_sta = 1; 4228c2ecf20Sopenharmony_ci memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci cmd.flags |= CMD_WANT_SKB; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &cmd); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (ret) 4298c2ecf20Sopenharmony_ci return ret; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci pkt = cmd.resp_pkt; 4328c2ecf20Sopenharmony_ci rem_sta_resp = (void *)pkt->data; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci switch (rem_sta_resp->status) { 4358c2ecf20Sopenharmony_ci case REM_STA_SUCCESS_MSK: 4368c2ecf20Sopenharmony_ci if (!temporary) { 4378c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 4388c2ecf20Sopenharmony_ci iwl_sta_ucode_deactivate(priv, sta_id); 4398c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci default: 4448c2ecf20Sopenharmony_ci ret = -EIO; 4458c2ecf20Sopenharmony_ci IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci iwl_free_resp(&cmd); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return ret; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/* 4558c2ecf20Sopenharmony_ci * iwl_remove_station - Remove driver's knowledge of station. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ciint iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, 4588c2ecf20Sopenharmony_ci const u8 *addr) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci u8 tid; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (!iwl_is_ready(priv)) { 4638c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, 4648c2ecf20Sopenharmony_ci "Unable to remove station %pM, device not ready.\n", 4658c2ecf20Sopenharmony_ci addr); 4668c2ecf20Sopenharmony_ci /* 4678c2ecf20Sopenharmony_ci * It is typical for stations to be removed when we are 4688c2ecf20Sopenharmony_ci * going down. Return success since device will be down 4698c2ecf20Sopenharmony_ci * soon anyway 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", 4758c2ecf20Sopenharmony_ci sta_id, addr); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (WARN_ON(sta_id == IWL_INVALID_STATION)) 4788c2ecf20Sopenharmony_ci return -EINVAL; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { 4838c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", 4848c2ecf20Sopenharmony_ci addr); 4858c2ecf20Sopenharmony_ci goto out_err; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { 4898c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", 4908c2ecf20Sopenharmony_ci addr); 4918c2ecf20Sopenharmony_ci goto out_err; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (priv->stations[sta_id].used & IWL_STA_LOCAL) { 4958c2ecf20Sopenharmony_ci kfree(priv->stations[sta_id].lq); 4968c2ecf20Sopenharmony_ci priv->stations[sta_id].lq = NULL; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) 5008c2ecf20Sopenharmony_ci memset(&priv->tid_data[sta_id][tid], 0, 5018c2ecf20Sopenharmony_ci sizeof(priv->tid_data[sta_id][tid])); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci priv->num_stations--; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (WARN_ON(priv->num_stations < 0)) 5088c2ecf20Sopenharmony_ci priv->num_stations = 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return iwl_send_remove_station(priv, addr, sta_id, false); 5138c2ecf20Sopenharmony_ciout_err: 5148c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 5158c2ecf20Sopenharmony_ci return -EINVAL; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_civoid iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, 5198c2ecf20Sopenharmony_ci const u8 *addr) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci u8 tid; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (!iwl_is_ready(priv)) { 5248c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, 5258c2ecf20Sopenharmony_ci "Unable to remove station %pM, device not ready.\n", 5268c2ecf20Sopenharmony_ci addr); 5278c2ecf20Sopenharmony_ci return; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) 5338c2ecf20Sopenharmony_ci return; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) 5408c2ecf20Sopenharmony_ci memset(&priv->tid_data[sta_id][tid], 0, 5418c2ecf20Sopenharmony_ci sizeof(priv->tid_data[sta_id][tid])); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; 5448c2ecf20Sopenharmony_ci priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci priv->num_stations--; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(priv->num_stations < 0)) 5498c2ecf20Sopenharmony_ci priv->num_stations = 0; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 5558c2ecf20Sopenharmony_ci u8 sta_id, struct iwl_link_quality_cmd *link_cmd) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int i, r; 5588c2ecf20Sopenharmony_ci u32 rate_flags = 0; 5598c2ecf20Sopenharmony_ci __le32 rate_n_flags; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci memset(link_cmd, 0, sizeof(*link_cmd)); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Set up the rate scaling to start at selected rate, fall back 5668c2ecf20Sopenharmony_ci * all the way down to 1M in IEEE order, and then spin on 1M */ 5678c2ecf20Sopenharmony_ci if (priv->band == NL80211_BAND_5GHZ) 5688c2ecf20Sopenharmony_ci r = IWL_RATE_6M_INDEX; 5698c2ecf20Sopenharmony_ci else if (ctx && ctx->vif && ctx->vif->p2p) 5708c2ecf20Sopenharmony_ci r = IWL_RATE_6M_INDEX; 5718c2ecf20Sopenharmony_ci else 5728c2ecf20Sopenharmony_ci r = IWL_RATE_1M_INDEX; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) 5758c2ecf20Sopenharmony_ci rate_flags |= RATE_MCS_CCK_MSK; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci rate_flags |= first_antenna(priv->nvm_data->valid_tx_ant) << 5788c2ecf20Sopenharmony_ci RATE_MCS_ANT_POS; 5798c2ecf20Sopenharmony_ci rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); 5808c2ecf20Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) 5818c2ecf20Sopenharmony_ci link_cmd->rs_table[i].rate_n_flags = rate_n_flags; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci link_cmd->general_params.single_stream_ant_msk = 5848c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = 5878c2ecf20Sopenharmony_ci priv->nvm_data->valid_tx_ant & 5888c2ecf20Sopenharmony_ci ~first_antenna(priv->nvm_data->valid_tx_ant); 5898c2ecf20Sopenharmony_ci if (!link_cmd->general_params.dual_stream_ant_msk) { 5908c2ecf20Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = ANT_AB; 5918c2ecf20Sopenharmony_ci } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) { 5928c2ecf20Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = 5938c2ecf20Sopenharmony_ci priv->nvm_data->valid_tx_ant; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci link_cmd->agg_params.agg_dis_start_th = 5978c2ecf20Sopenharmony_ci LINK_QUAL_AGG_DISABLE_START_DEF; 5988c2ecf20Sopenharmony_ci link_cmd->agg_params.agg_time_limit = 5998c2ecf20Sopenharmony_ci cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci link_cmd->sta_id = sta_id; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/* 6058c2ecf20Sopenharmony_ci * iwl_clear_ucode_stations - clear ucode station table bits 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * This function clears all the bits in the driver indicating 6088c2ecf20Sopenharmony_ci * which stations are active in the ucode. Call when something 6098c2ecf20Sopenharmony_ci * other than explicit station management would cause this in 6108c2ecf20Sopenharmony_ci * the ucode, e.g. unassociated RXON. 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_civoid iwl_clear_ucode_stations(struct iwl_priv *priv, 6138c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci int i; 6168c2ecf20Sopenharmony_ci bool cleared = false; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 6218c2ecf20Sopenharmony_ci for (i = 0; i < IWLAGN_STATION_COUNT; i++) { 6228c2ecf20Sopenharmony_ci if (ctx && ctx->ctxid != priv->stations[i].ctxid) 6238c2ecf20Sopenharmony_ci continue; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { 6268c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, 6278c2ecf20Sopenharmony_ci "Clearing ucode active for station %d\n", i); 6288c2ecf20Sopenharmony_ci priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; 6298c2ecf20Sopenharmony_ci cleared = true; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (!cleared) 6358c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, 6368c2ecf20Sopenharmony_ci "No active stations found to be cleared\n"); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci/* 6408c2ecf20Sopenharmony_ci * iwl_restore_stations() - Restore driver known stations to device 6418c2ecf20Sopenharmony_ci * 6428c2ecf20Sopenharmony_ci * All stations considered active by driver, but not present in ucode, is 6438c2ecf20Sopenharmony_ci * restored. 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * Function sleeps. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_civoid iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 6508c2ecf20Sopenharmony_ci static const struct iwl_link_quality_cmd zero_lq = {}; 6518c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd lq; 6528c2ecf20Sopenharmony_ci int i; 6538c2ecf20Sopenharmony_ci bool found = false; 6548c2ecf20Sopenharmony_ci int ret; 6558c2ecf20Sopenharmony_ci bool send_lq; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (!iwl_is_ready(priv)) { 6588c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, 6598c2ecf20Sopenharmony_ci "Not ready yet, not restoring any stations.\n"); 6608c2ecf20Sopenharmony_ci return; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); 6648c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 6658c2ecf20Sopenharmony_ci for (i = 0; i < IWLAGN_STATION_COUNT; i++) { 6668c2ecf20Sopenharmony_ci if (ctx->ctxid != priv->stations[i].ctxid) 6678c2ecf20Sopenharmony_ci continue; 6688c2ecf20Sopenharmony_ci if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && 6698c2ecf20Sopenharmony_ci !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { 6708c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", 6718c2ecf20Sopenharmony_ci priv->stations[i].sta.sta.addr); 6728c2ecf20Sopenharmony_ci priv->stations[i].sta.mode = 0; 6738c2ecf20Sopenharmony_ci priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; 6748c2ecf20Sopenharmony_ci found = true; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci for (i = 0; i < IWLAGN_STATION_COUNT; i++) { 6798c2ecf20Sopenharmony_ci if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { 6808c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[i].sta, 6818c2ecf20Sopenharmony_ci sizeof(struct iwl_addsta_cmd)); 6828c2ecf20Sopenharmony_ci send_lq = false; 6838c2ecf20Sopenharmony_ci if (priv->stations[i].lq) { 6848c2ecf20Sopenharmony_ci if (priv->wowlan) 6858c2ecf20Sopenharmony_ci iwl_sta_fill_lq(priv, ctx, i, &lq); 6868c2ecf20Sopenharmony_ci else 6878c2ecf20Sopenharmony_ci memcpy(&lq, priv->stations[i].lq, 6888c2ecf20Sopenharmony_ci sizeof(struct iwl_link_quality_cmd)); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (memcmp(&lq, &zero_lq, sizeof(lq))) 6918c2ecf20Sopenharmony_ci send_lq = true; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 6948c2ecf20Sopenharmony_ci ret = iwl_send_add_sta(priv, &sta_cmd, 0); 6958c2ecf20Sopenharmony_ci if (ret) { 6968c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 6978c2ecf20Sopenharmony_ci IWL_ERR(priv, "Adding station %pM failed.\n", 6988c2ecf20Sopenharmony_ci priv->stations[i].sta.sta.addr); 6998c2ecf20Sopenharmony_ci priv->stations[i].used &= 7008c2ecf20Sopenharmony_ci ~IWL_STA_DRIVER_ACTIVE; 7018c2ecf20Sopenharmony_ci priv->stations[i].used &= 7028c2ecf20Sopenharmony_ci ~IWL_STA_UCODE_INPROGRESS; 7038c2ecf20Sopenharmony_ci continue; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci /* 7068c2ecf20Sopenharmony_ci * Rate scaling has already been initialized, send 7078c2ecf20Sopenharmony_ci * current LQ command 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci if (send_lq) 7108c2ecf20Sopenharmony_ci iwl_send_lq_cmd(priv, ctx, &lq, 0, true); 7118c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 7128c2ecf20Sopenharmony_ci priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 7178c2ecf20Sopenharmony_ci if (!found) 7188c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Restoring all known stations .... " 7198c2ecf20Sopenharmony_ci "no stations to be restored.\n"); 7208c2ecf20Sopenharmony_ci else 7218c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Restoring all known stations .... " 7228c2ecf20Sopenharmony_ci "complete.\n"); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ciint iwl_get_free_ucode_key_offset(struct iwl_priv *priv) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci int i; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci for (i = 0; i < priv->sta_key_max_num; i++) 7308c2ecf20Sopenharmony_ci if (!test_and_set_bit(i, &priv->ucode_key_table)) 7318c2ecf20Sopenharmony_ci return i; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci return WEP_INVALID_OFFSET; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_civoid iwl_dealloc_bcast_stations(struct iwl_priv *priv) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci int i; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 7418c2ecf20Sopenharmony_ci for (i = 0; i < IWLAGN_STATION_COUNT; i++) { 7428c2ecf20Sopenharmony_ci if (!(priv->stations[i].used & IWL_STA_BCAST)) 7438c2ecf20Sopenharmony_ci continue; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; 7468c2ecf20Sopenharmony_ci priv->num_stations--; 7478c2ecf20Sopenharmony_ci if (WARN_ON(priv->num_stations < 0)) 7488c2ecf20Sopenharmony_ci priv->num_stations = 0; 7498c2ecf20Sopenharmony_ci kfree(priv->stations[i].lq); 7508c2ecf20Sopenharmony_ci priv->stations[i].lq = NULL; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 7568c2ecf20Sopenharmony_cistatic void iwl_dump_lq_cmd(struct iwl_priv *priv, 7578c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *lq) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci int i; 7608c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); 7618c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", 7628c2ecf20Sopenharmony_ci lq->general_params.single_stream_ant_msk, 7638c2ecf20Sopenharmony_ci lq->general_params.dual_stream_ant_msk); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) 7668c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", 7678c2ecf20Sopenharmony_ci i, lq->rs_table[i].rate_n_flags); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci#else 7708c2ecf20Sopenharmony_cistatic inline void iwl_dump_lq_cmd(struct iwl_priv *priv, 7718c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *lq) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci#endif 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci/* 7778c2ecf20Sopenharmony_ci * is_lq_table_valid() - Test one aspect of LQ cmd for validity 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * It sometimes happens when a HT rate has been in use and we 7808c2ecf20Sopenharmony_ci * loose connectivity with AP then mac80211 will first tell us that the 7818c2ecf20Sopenharmony_ci * current channel is not HT anymore before removing the station. In such a 7828c2ecf20Sopenharmony_ci * scenario the RXON flags will be updated to indicate we are not 7838c2ecf20Sopenharmony_ci * communicating HT anymore, but the LQ command may still contain HT rates. 7848c2ecf20Sopenharmony_ci * Test for this to prevent driver from sending LQ command between the time 7858c2ecf20Sopenharmony_ci * RXON flags are updated and when LQ command is updated. 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_cistatic bool is_lq_table_valid(struct iwl_priv *priv, 7888c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 7898c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *lq) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci int i; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (ctx->ht.enabled) 7948c2ecf20Sopenharmony_ci return true; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", 7978c2ecf20Sopenharmony_ci ctx->active.channel); 7988c2ecf20Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { 7998c2ecf20Sopenharmony_ci if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & 8008c2ecf20Sopenharmony_ci RATE_MCS_HT_MSK) { 8018c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, 8028c2ecf20Sopenharmony_ci "index %d of LQ expects HT channel\n", 8038c2ecf20Sopenharmony_ci i); 8048c2ecf20Sopenharmony_ci return false; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci return true; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci/* 8118c2ecf20Sopenharmony_ci * iwl_send_lq_cmd() - Send link quality command 8128c2ecf20Sopenharmony_ci * @init: This command is sent as part of station initialization right 8138c2ecf20Sopenharmony_ci * after station has been added. 8148c2ecf20Sopenharmony_ci * 8158c2ecf20Sopenharmony_ci * The link quality command is sent as the last step of station creation. 8168c2ecf20Sopenharmony_ci * This is the special case in which init is set and we call a callback in 8178c2ecf20Sopenharmony_ci * this case to clear the state indicating that station creation is in 8188c2ecf20Sopenharmony_ci * progress. 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_ciint iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 8218c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *lq, u8 flags, bool init) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci int ret = 0; 8248c2ecf20Sopenharmony_ci struct iwl_host_cmd cmd = { 8258c2ecf20Sopenharmony_ci .id = REPLY_TX_LINK_QUALITY_CMD, 8268c2ecf20Sopenharmony_ci .len = { sizeof(struct iwl_link_quality_cmd), }, 8278c2ecf20Sopenharmony_ci .flags = flags, 8288c2ecf20Sopenharmony_ci .data = { lq, }, 8298c2ecf20Sopenharmony_ci }; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) 8328c2ecf20Sopenharmony_ci return -EINVAL; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 8368c2ecf20Sopenharmony_ci if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) { 8378c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 8388c2ecf20Sopenharmony_ci return -EINVAL; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci iwl_dump_lq_cmd(priv, lq); 8438c2ecf20Sopenharmony_ci if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (is_lq_table_valid(priv, ctx, lq)) 8478c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &cmd); 8488c2ecf20Sopenharmony_ci else 8498c2ecf20Sopenharmony_ci ret = -EINVAL; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (cmd.flags & CMD_ASYNC) 8528c2ecf20Sopenharmony_ci return ret; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (init) { 8558c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "init LQ command complete, " 8568c2ecf20Sopenharmony_ci "clearing sta addition status for sta %d\n", 8578c2ecf20Sopenharmony_ci lq->sta_id); 8588c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 8598c2ecf20Sopenharmony_ci priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; 8608c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci return ret; 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic struct iwl_link_quality_cmd * 8678c2ecf20Sopenharmony_ciiwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 8688c2ecf20Sopenharmony_ci u8 sta_id) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *link_cmd; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL); 8738c2ecf20Sopenharmony_ci if (!link_cmd) { 8748c2ecf20Sopenharmony_ci IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n"); 8758c2ecf20Sopenharmony_ci return NULL; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci return link_cmd; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci/* 8848c2ecf20Sopenharmony_ci * iwlagn_add_bssid_station - Add the special IBSS BSSID station 8858c2ecf20Sopenharmony_ci * 8868c2ecf20Sopenharmony_ci * Function sleeps. 8878c2ecf20Sopenharmony_ci */ 8888c2ecf20Sopenharmony_ciint iwlagn_add_bssid_station(struct iwl_priv *priv, 8898c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 8908c2ecf20Sopenharmony_ci const u8 *addr, u8 *sta_id_r) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci int ret; 8938c2ecf20Sopenharmony_ci u8 sta_id; 8948c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *link_cmd; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (sta_id_r) 8978c2ecf20Sopenharmony_ci *sta_id_r = IWL_INVALID_STATION; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); 9008c2ecf20Sopenharmony_ci if (ret) { 9018c2ecf20Sopenharmony_ci IWL_ERR(priv, "Unable to add station %pM\n", addr); 9028c2ecf20Sopenharmony_ci return ret; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci if (sta_id_r) 9068c2ecf20Sopenharmony_ci *sta_id_r = sta_id; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 9098c2ecf20Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_LOCAL; 9108c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* Set up default rate scaling table in device's station table */ 9138c2ecf20Sopenharmony_ci link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); 9148c2ecf20Sopenharmony_ci if (!link_cmd) { 9158c2ecf20Sopenharmony_ci IWL_ERR(priv, 9168c2ecf20Sopenharmony_ci "Unable to initialize rate scaling for station %pM.\n", 9178c2ecf20Sopenharmony_ci addr); 9188c2ecf20Sopenharmony_ci return -ENOMEM; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true); 9228c2ecf20Sopenharmony_ci if (ret) 9238c2ecf20Sopenharmony_ci IWL_ERR(priv, "Link quality command failed (%d)\n", ret); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 9268c2ecf20Sopenharmony_ci priv->stations[sta_id].lq = link_cmd; 9278c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci return 0; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci/* 9338c2ecf20Sopenharmony_ci * static WEP keys 9348c2ecf20Sopenharmony_ci * 9358c2ecf20Sopenharmony_ci * For each context, the device has a table of 4 static WEP keys 9368c2ecf20Sopenharmony_ci * (one for each key index) that is updated with the following 9378c2ecf20Sopenharmony_ci * commands. 9388c2ecf20Sopenharmony_ci */ 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, 9418c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 9428c2ecf20Sopenharmony_ci bool send_if_empty) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci int i, not_empty = 0; 9458c2ecf20Sopenharmony_ci u8 buff[sizeof(struct iwl_wep_cmd) + 9468c2ecf20Sopenharmony_ci sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; 9478c2ecf20Sopenharmony_ci struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; 9488c2ecf20Sopenharmony_ci size_t cmd_size = sizeof(struct iwl_wep_cmd); 9498c2ecf20Sopenharmony_ci struct iwl_host_cmd cmd = { 9508c2ecf20Sopenharmony_ci .id = ctx->wep_key_cmd, 9518c2ecf20Sopenharmony_ci .data = { wep_cmd, }, 9528c2ecf20Sopenharmony_ci }; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci might_sleep(); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci memset(wep_cmd, 0, cmd_size + 9578c2ecf20Sopenharmony_ci (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci for (i = 0; i < WEP_KEYS_MAX ; i++) { 9608c2ecf20Sopenharmony_ci wep_cmd->key[i].key_index = i; 9618c2ecf20Sopenharmony_ci if (ctx->wep_keys[i].key_size) { 9628c2ecf20Sopenharmony_ci wep_cmd->key[i].key_offset = i; 9638c2ecf20Sopenharmony_ci not_empty = 1; 9648c2ecf20Sopenharmony_ci } else { 9658c2ecf20Sopenharmony_ci wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size; 9698c2ecf20Sopenharmony_ci memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key, 9708c2ecf20Sopenharmony_ci ctx->wep_keys[i].key_size); 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; 9748c2ecf20Sopenharmony_ci wep_cmd->num_keys = WEP_KEYS_MAX; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci cmd.len[0] = cmd_size; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (not_empty || send_if_empty) 9818c2ecf20Sopenharmony_ci return iwl_dvm_send_cmd(priv, &cmd); 9828c2ecf20Sopenharmony_ci else 9838c2ecf20Sopenharmony_ci return 0; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ciint iwl_restore_default_wep_keys(struct iwl_priv *priv, 9878c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci return iwl_send_static_wepkey_cmd(priv, ctx, false); 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ciint iwl_remove_default_wep_key(struct iwl_priv *priv, 9958c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 9968c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci int ret; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", 10038c2ecf20Sopenharmony_ci keyconf->keyidx); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0])); 10068c2ecf20Sopenharmony_ci if (iwl_is_rfkill(priv)) { 10078c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(priv, 10088c2ecf20Sopenharmony_ci "Not sending REPLY_WEPKEY command due to RFKILL.\n"); 10098c2ecf20Sopenharmony_ci /* but keys in device are clear anyway so return success */ 10108c2ecf20Sopenharmony_ci return 0; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci ret = iwl_send_static_wepkey_cmd(priv, ctx, 1); 10138c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", 10148c2ecf20Sopenharmony_ci keyconf->keyidx, ret); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return ret; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ciint iwl_set_default_wep_key(struct iwl_priv *priv, 10208c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 10218c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci int ret; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (keyconf->keylen != WEP_KEY_LEN_128 && 10288c2ecf20Sopenharmony_ci keyconf->keylen != WEP_KEY_LEN_64) { 10298c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(priv, 10308c2ecf20Sopenharmony_ci "Bad WEP key length %d\n", keyconf->keylen); 10318c2ecf20Sopenharmony_ci return -EINVAL; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; 10378c2ecf20Sopenharmony_ci memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key, 10388c2ecf20Sopenharmony_ci keyconf->keylen); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci ret = iwl_send_static_wepkey_cmd(priv, ctx, false); 10418c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", 10428c2ecf20Sopenharmony_ci keyconf->keylen, keyconf->keyidx, ret); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci return ret; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci/* 10488c2ecf20Sopenharmony_ci * dynamic (per-station) keys 10498c2ecf20Sopenharmony_ci * 10508c2ecf20Sopenharmony_ci * The dynamic keys are a little more complicated. The device has 10518c2ecf20Sopenharmony_ci * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys. 10528c2ecf20Sopenharmony_ci * These are linked to stations by a table that contains an index 10538c2ecf20Sopenharmony_ci * into the key table for each station/key index/{mcast,unicast}, 10548c2ecf20Sopenharmony_ci * i.e. it's basically an array of pointers like this: 10558c2ecf20Sopenharmony_ci * key_offset_t key_mapping[NUM_STATIONS][4][2]; 10568c2ecf20Sopenharmony_ci * (it really works differently, but you can think of it as such) 10578c2ecf20Sopenharmony_ci * 10588c2ecf20Sopenharmony_ci * The key uploading and linking happens in the same command, the 10598c2ecf20Sopenharmony_ci * add station command with STA_MODIFY_KEY_MASK. 10608c2ecf20Sopenharmony_ci */ 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic u8 iwlagn_key_sta_id(struct iwl_priv *priv, 10638c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 10648c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (sta) 10698c2ecf20Sopenharmony_ci return iwl_sta_id(sta); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* 10728c2ecf20Sopenharmony_ci * The device expects GTKs for station interfaces to be 10738c2ecf20Sopenharmony_ci * installed as GTKs for the AP station. If we have no 10748c2ecf20Sopenharmony_ci * station ID, then use the ap_sta_id in that case. 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx) 10778c2ecf20Sopenharmony_ci return vif_priv->ctx->ap_sta_id; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci return IWL_INVALID_STATION; 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic int iwlagn_send_sta_key(struct iwl_priv *priv, 10838c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 10848c2ecf20Sopenharmony_ci u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, 10858c2ecf20Sopenharmony_ci u32 cmd_flags) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci __le16 key_flags; 10888c2ecf20Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 10898c2ecf20Sopenharmony_ci size_t to_copy; 10908c2ecf20Sopenharmony_ci int i; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 10938c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); 10948c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); 10978c2ecf20Sopenharmony_ci key_flags |= STA_KEY_FLG_MAP_KEY_MSK; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci switch (keyconf->cipher) { 11008c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 11018c2ecf20Sopenharmony_ci key_flags |= STA_KEY_FLG_CCMP; 11028c2ecf20Sopenharmony_ci memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); 11038c2ecf20Sopenharmony_ci break; 11048c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 11058c2ecf20Sopenharmony_ci key_flags |= STA_KEY_FLG_TKIP; 11068c2ecf20Sopenharmony_ci sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; 11078c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 11088c2ecf20Sopenharmony_ci sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); 11098c2ecf20Sopenharmony_ci /* keyconf may contain MIC rx/tx keys which iwl does not use */ 11108c2ecf20Sopenharmony_ci to_copy = min_t(size_t, sizeof(sta_cmd.key.key), keyconf->keylen); 11118c2ecf20Sopenharmony_ci memcpy(sta_cmd.key.key, keyconf->key, to_copy); 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 11148c2ecf20Sopenharmony_ci key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; 11158c2ecf20Sopenharmony_ci /* fall through */ 11168c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 11178c2ecf20Sopenharmony_ci key_flags |= STA_KEY_FLG_WEP; 11188c2ecf20Sopenharmony_ci memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen); 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci default: 11218c2ecf20Sopenharmony_ci WARN_ON(1); 11228c2ecf20Sopenharmony_ci return -EINVAL; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) 11268c2ecf20Sopenharmony_ci key_flags |= STA_KEY_MULTICAST_MSK; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci /* key pointer (offset) */ 11298c2ecf20Sopenharmony_ci sta_cmd.key.key_offset = keyconf->hw_key_idx; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci sta_cmd.key.key_flags = key_flags; 11328c2ecf20Sopenharmony_ci sta_cmd.mode = STA_CONTROL_MODIFY_MSK; 11338c2ecf20Sopenharmony_ci sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, cmd_flags); 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_civoid iwl_update_tkip_key(struct iwl_priv *priv, 11398c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 11408c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 11418c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci u8 sta_id = iwlagn_key_sta_id(priv, vif, sta); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 11468c2ecf20Sopenharmony_ci return; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (iwl_scan_cancel(priv)) { 11498c2ecf20Sopenharmony_ci /* cancel scan failed, just live w/ bad key and rely 11508c2ecf20Sopenharmony_ci briefly on SW decryption */ 11518c2ecf20Sopenharmony_ci return; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci iwlagn_send_sta_key(priv, keyconf, sta_id, 11558c2ecf20Sopenharmony_ci iv32, phase1key, CMD_ASYNC); 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ciint iwl_remove_dynamic_key(struct iwl_priv *priv, 11598c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 11608c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 11618c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 11628c2ecf20Sopenharmony_ci{ 11638c2ecf20Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 11648c2ecf20Sopenharmony_ci u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); 11658c2ecf20Sopenharmony_ci __le16 key_flags; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci /* if station isn't there, neither is the key */ 11688c2ecf20Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 11698c2ecf20Sopenharmony_ci return -ENOENT; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 11728c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); 11738c2ecf20Sopenharmony_ci if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) 11748c2ecf20Sopenharmony_ci sta_id = IWL_INVALID_STATION; 11758c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 11788c2ecf20Sopenharmony_ci return 0; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci ctx->key_mapping_keys--; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", 11858c2ecf20Sopenharmony_ci keyconf->keyidx, sta_id); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table)) 11888c2ecf20Sopenharmony_ci IWL_ERR(priv, "offset %d not used in uCode key table.\n", 11898c2ecf20Sopenharmony_ci keyconf->hw_key_idx); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); 11928c2ecf20Sopenharmony_ci key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC | 11938c2ecf20Sopenharmony_ci STA_KEY_FLG_INVALID; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) 11968c2ecf20Sopenharmony_ci key_flags |= STA_KEY_MULTICAST_MSK; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci sta_cmd.key.key_flags = key_flags; 11998c2ecf20Sopenharmony_ci sta_cmd.key.key_offset = keyconf->hw_key_idx; 12008c2ecf20Sopenharmony_ci sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; 12018c2ecf20Sopenharmony_ci sta_cmd.mode = STA_CONTROL_MODIFY_MSK; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, 0); 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ciint iwl_set_dynamic_key(struct iwl_priv *priv, 12078c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 12088c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 12098c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct ieee80211_key_seq seq; 12128c2ecf20Sopenharmony_ci u16 p1k[5]; 12138c2ecf20Sopenharmony_ci int ret; 12148c2ecf20Sopenharmony_ci u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); 12158c2ecf20Sopenharmony_ci const u8 *addr; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 12188c2ecf20Sopenharmony_ci return -EINVAL; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv); 12238c2ecf20Sopenharmony_ci if (keyconf->hw_key_idx == WEP_INVALID_OFFSET) 12248c2ecf20Sopenharmony_ci return -ENOSPC; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci ctx->key_mapping_keys++; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci switch (keyconf->cipher) { 12298c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 12308c2ecf20Sopenharmony_ci if (sta) 12318c2ecf20Sopenharmony_ci addr = sta->addr; 12328c2ecf20Sopenharmony_ci else /* station mode case only */ 12338c2ecf20Sopenharmony_ci addr = ctx->active.bssid_addr; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* pre-fill phase 1 key into device cache */ 12368c2ecf20Sopenharmony_ci ieee80211_get_key_rx_seq(keyconf, 0, &seq); 12378c2ecf20Sopenharmony_ci ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); 12388c2ecf20Sopenharmony_ci ret = iwlagn_send_sta_key(priv, keyconf, sta_id, 12398c2ecf20Sopenharmony_ci seq.tkip.iv32, p1k, 0); 12408c2ecf20Sopenharmony_ci break; 12418c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 12428c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 12438c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 12448c2ecf20Sopenharmony_ci ret = iwlagn_send_sta_key(priv, keyconf, sta_id, 12458c2ecf20Sopenharmony_ci 0, NULL, 0); 12468c2ecf20Sopenharmony_ci break; 12478c2ecf20Sopenharmony_ci default: 12488c2ecf20Sopenharmony_ci IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); 12498c2ecf20Sopenharmony_ci ret = -EINVAL; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (ret) { 12538c2ecf20Sopenharmony_ci ctx->key_mapping_keys--; 12548c2ecf20Sopenharmony_ci clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table); 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", 12588c2ecf20Sopenharmony_ci keyconf->cipher, keyconf->keylen, keyconf->keyidx, 12598c2ecf20Sopenharmony_ci sta ? sta->addr : NULL, ret); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci return ret; 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci/* 12658c2ecf20Sopenharmony_ci * iwlagn_alloc_bcast_station - add broadcast station into driver's station table. 12668c2ecf20Sopenharmony_ci * 12678c2ecf20Sopenharmony_ci * This adds the broadcast station into the driver's station table 12688c2ecf20Sopenharmony_ci * and marks it driver active, so that it will be restored to the 12698c2ecf20Sopenharmony_ci * device at the next best time. 12708c2ecf20Sopenharmony_ci */ 12718c2ecf20Sopenharmony_ciint iwlagn_alloc_bcast_station(struct iwl_priv *priv, 12728c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *link_cmd; 12758c2ecf20Sopenharmony_ci u8 sta_id; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 12788c2ecf20Sopenharmony_ci sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); 12798c2ecf20Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 12808c2ecf20Sopenharmony_ci IWL_ERR(priv, "Unable to prepare broadcast station\n"); 12818c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci return -EINVAL; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; 12878c2ecf20Sopenharmony_ci priv->stations[sta_id].used |= IWL_STA_BCAST; 12888c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); 12918c2ecf20Sopenharmony_ci if (!link_cmd) { 12928c2ecf20Sopenharmony_ci IWL_ERR(priv, 12938c2ecf20Sopenharmony_ci "Unable to initialize rate scaling for bcast station.\n"); 12948c2ecf20Sopenharmony_ci return -ENOMEM; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 12988c2ecf20Sopenharmony_ci priv->stations[sta_id].lq = link_cmd; 12998c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci return 0; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci/* 13058c2ecf20Sopenharmony_ci * iwl_update_bcast_station - update broadcast station's LQ command 13068c2ecf20Sopenharmony_ci * 13078c2ecf20Sopenharmony_ci * Only used by iwlagn. Placed here to have all bcast station management 13088c2ecf20Sopenharmony_ci * code together. 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_ciint iwl_update_bcast_station(struct iwl_priv *priv, 13118c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *link_cmd; 13148c2ecf20Sopenharmony_ci u8 sta_id = ctx->bcast_sta_id; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); 13178c2ecf20Sopenharmony_ci if (!link_cmd) { 13188c2ecf20Sopenharmony_ci IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); 13198c2ecf20Sopenharmony_ci return -ENOMEM; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 13238c2ecf20Sopenharmony_ci if (priv->stations[sta_id].lq) 13248c2ecf20Sopenharmony_ci kfree(priv->stations[sta_id].lq); 13258c2ecf20Sopenharmony_ci else 13268c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n"); 13278c2ecf20Sopenharmony_ci priv->stations[sta_id].lq = link_cmd; 13288c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci return 0; 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ciint iwl_update_bcast_stations(struct iwl_priv *priv) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx; 13368c2ecf20Sopenharmony_ci int ret = 0; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci for_each_context(priv, ctx) { 13398c2ecf20Sopenharmony_ci ret = iwl_update_bcast_station(priv, ctx); 13408c2ecf20Sopenharmony_ci if (ret) 13418c2ecf20Sopenharmony_ci break; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return ret; 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci/* 13488c2ecf20Sopenharmony_ci * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table 13498c2ecf20Sopenharmony_ci */ 13508c2ecf20Sopenharmony_ciint iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* Remove "disable" flag, to enable Tx for this TID */ 13578c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 13588c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; 13598c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); 13608c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 13618c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); 13628c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, 0); 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ciint iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, 13688c2ecf20Sopenharmony_ci int tid, u16 ssn) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci int sta_id; 13718c2ecf20Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci sta_id = iwl_sta_id(sta); 13768c2ecf20Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) 13778c2ecf20Sopenharmony_ci return -ENXIO; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 13808c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.station_flags_msk = 0; 13818c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; 13828c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; 13838c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); 13848c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 13858c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); 13868c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, 0); 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ciint iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, 13928c2ecf20Sopenharmony_ci int tid) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci int sta_id; 13958c2ecf20Sopenharmony_ci struct iwl_addsta_cmd sta_cmd; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci sta_id = iwl_sta_id(sta); 14008c2ecf20Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 14018c2ecf20Sopenharmony_ci IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); 14028c2ecf20Sopenharmony_ci return -ENXIO; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 14068c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.station_flags_msk = 0; 14078c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; 14088c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; 14098c2ecf20Sopenharmony_ci priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 14108c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); 14118c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci return iwl_send_add_sta(priv, &sta_cmd, 0); 14148c2ecf20Sopenharmony_ci} 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_civoid iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct iwl_addsta_cmd cmd = { 14218c2ecf20Sopenharmony_ci .mode = STA_CONTROL_MODIFY_MSK, 14228c2ecf20Sopenharmony_ci .station_flags = STA_FLG_PWR_SAVE_MSK, 14238c2ecf20Sopenharmony_ci .station_flags_msk = STA_FLG_PWR_SAVE_MSK, 14248c2ecf20Sopenharmony_ci .sta.sta_id = sta_id, 14258c2ecf20Sopenharmony_ci .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK, 14268c2ecf20Sopenharmony_ci .sleep_tx_count = cpu_to_le16(cnt), 14278c2ecf20Sopenharmony_ci }; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci iwl_send_add_sta(priv, &cmd, CMD_ASYNC); 14308c2ecf20Sopenharmony_ci} 1431