18c2ecf20Sopenharmony_ci/*- 28c2ecf20Sopenharmony_ci * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting 38c2ecf20Sopenharmony_ci * Copyright (c) 2004-2005 Atheros Communications, Inc. 48c2ecf20Sopenharmony_ci * Copyright (c) 2006 Devicescape Software, Inc. 58c2ecf20Sopenharmony_ci * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * All rights reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 118c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 128c2ecf20Sopenharmony_ci * are met: 138c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 148c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer, 158c2ecf20Sopenharmony_ci * without modification. 168c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce at minimum a disclaimer 178c2ecf20Sopenharmony_ci * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 188c2ecf20Sopenharmony_ci * redistribution must be conditioned upon including a substantially 198c2ecf20Sopenharmony_ci * similar Disclaimer requirement for further binary redistribution. 208c2ecf20Sopenharmony_ci * 3. Neither the names of the above-listed copyright holders nor the names 218c2ecf20Sopenharmony_ci * of any contributors may be used to endorse or promote products derived 228c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 258c2ecf20Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free 268c2ecf20Sopenharmony_ci * Software Foundation. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * NO WARRANTY 298c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 308c2ecf20Sopenharmony_ci * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 318c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 328c2ecf20Sopenharmony_ci * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 338c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 348c2ecf20Sopenharmony_ci * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 358c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 368c2ecf20Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 378c2ecf20Sopenharmony_ci * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 388c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 398c2ecf20Sopenharmony_ci * THE POSSIBILITY OF SUCH DAMAGES. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include <linux/module.h> 468c2ecf20Sopenharmony_ci#include <linux/delay.h> 478c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 488c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 498c2ecf20Sopenharmony_ci#include <linux/if.h> 508c2ecf20Sopenharmony_ci#include <linux/io.h> 518c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 528c2ecf20Sopenharmony_ci#include <linux/cache.h> 538c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 548c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 558c2ecf20Sopenharmony_ci#include <linux/slab.h> 568c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 578c2ecf20Sopenharmony_ci#include <linux/nl80211.h> 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 608c2ecf20Sopenharmony_ci#include <net/ieee80211_radiotap.h> 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#include <net/mac80211.h> 658c2ecf20Sopenharmony_ci#include "base.h" 668c2ecf20Sopenharmony_ci#include "reg.h" 678c2ecf20Sopenharmony_ci#include "debug.h" 688c2ecf20Sopenharmony_ci#include "ani.h" 698c2ecf20Sopenharmony_ci#include "ath5k.h" 708c2ecf20Sopenharmony_ci#include "../regd.h" 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 738c2ecf20Sopenharmony_ci#include "trace.h" 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cibool ath5k_modparam_nohwcrypt; 768c2ecf20Sopenharmony_cimodule_param_named(nohwcrypt, ath5k_modparam_nohwcrypt, bool, 0444); 778c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic bool modparam_fastchanswitch; 808c2ecf20Sopenharmony_cimodule_param_named(fastchanswitch, modparam_fastchanswitch, bool, 0444); 818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fastchanswitch, "Enable fast channel switching for AR2413/AR5413 radios."); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic bool ath5k_modparam_no_hw_rfkill_switch; 848c2ecf20Sopenharmony_cimodule_param_named(no_hw_rfkill_switch, ath5k_modparam_no_hw_rfkill_switch, 858c2ecf20Sopenharmony_ci bool, 0444); 868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(no_hw_rfkill_switch, "Ignore the GPIO RFKill switch state"); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* Module info */ 908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jiri Slaby"); 918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nick Kossifidis"); 928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support for 5xxx series of Atheros 802.11 wireless LAN cards."); 938c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards"); 948c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int ath5k_init(struct ieee80211_hw *hw); 978c2ecf20Sopenharmony_cistatic int ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, 988c2ecf20Sopenharmony_ci bool skip_pcu); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Known SREVs */ 1018c2ecf20Sopenharmony_cistatic const struct ath5k_srev_name srev_names[] = { 1028c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH5K_AHB 1038c2ecf20Sopenharmony_ci { "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R2 }, 1048c2ecf20Sopenharmony_ci { "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R7 }, 1058c2ecf20Sopenharmony_ci { "2313", AR5K_VERSION_MAC, AR5K_SREV_AR2313_R8 }, 1068c2ecf20Sopenharmony_ci { "2315", AR5K_VERSION_MAC, AR5K_SREV_AR2315_R6 }, 1078c2ecf20Sopenharmony_ci { "2315", AR5K_VERSION_MAC, AR5K_SREV_AR2315_R7 }, 1088c2ecf20Sopenharmony_ci { "2317", AR5K_VERSION_MAC, AR5K_SREV_AR2317_R1 }, 1098c2ecf20Sopenharmony_ci { "2317", AR5K_VERSION_MAC, AR5K_SREV_AR2317_R2 }, 1108c2ecf20Sopenharmony_ci#else 1118c2ecf20Sopenharmony_ci { "5210", AR5K_VERSION_MAC, AR5K_SREV_AR5210 }, 1128c2ecf20Sopenharmony_ci { "5311", AR5K_VERSION_MAC, AR5K_SREV_AR5311 }, 1138c2ecf20Sopenharmony_ci { "5311A", AR5K_VERSION_MAC, AR5K_SREV_AR5311A }, 1148c2ecf20Sopenharmony_ci { "5311B", AR5K_VERSION_MAC, AR5K_SREV_AR5311B }, 1158c2ecf20Sopenharmony_ci { "5211", AR5K_VERSION_MAC, AR5K_SREV_AR5211 }, 1168c2ecf20Sopenharmony_ci { "5212", AR5K_VERSION_MAC, AR5K_SREV_AR5212 }, 1178c2ecf20Sopenharmony_ci { "5213", AR5K_VERSION_MAC, AR5K_SREV_AR5213 }, 1188c2ecf20Sopenharmony_ci { "5213A", AR5K_VERSION_MAC, AR5K_SREV_AR5213A }, 1198c2ecf20Sopenharmony_ci { "2413", AR5K_VERSION_MAC, AR5K_SREV_AR2413 }, 1208c2ecf20Sopenharmony_ci { "2414", AR5K_VERSION_MAC, AR5K_SREV_AR2414 }, 1218c2ecf20Sopenharmony_ci { "5424", AR5K_VERSION_MAC, AR5K_SREV_AR5424 }, 1228c2ecf20Sopenharmony_ci { "5413", AR5K_VERSION_MAC, AR5K_SREV_AR5413 }, 1238c2ecf20Sopenharmony_ci { "5414", AR5K_VERSION_MAC, AR5K_SREV_AR5414 }, 1248c2ecf20Sopenharmony_ci { "2415", AR5K_VERSION_MAC, AR5K_SREV_AR2415 }, 1258c2ecf20Sopenharmony_ci { "5416", AR5K_VERSION_MAC, AR5K_SREV_AR5416 }, 1268c2ecf20Sopenharmony_ci { "5418", AR5K_VERSION_MAC, AR5K_SREV_AR5418 }, 1278c2ecf20Sopenharmony_ci { "2425", AR5K_VERSION_MAC, AR5K_SREV_AR2425 }, 1288c2ecf20Sopenharmony_ci { "2417", AR5K_VERSION_MAC, AR5K_SREV_AR2417 }, 1298c2ecf20Sopenharmony_ci#endif 1308c2ecf20Sopenharmony_ci { "xxxxx", AR5K_VERSION_MAC, AR5K_SREV_UNKNOWN }, 1318c2ecf20Sopenharmony_ci { "5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110 }, 1328c2ecf20Sopenharmony_ci { "5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111 }, 1338c2ecf20Sopenharmony_ci { "5111A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111A }, 1348c2ecf20Sopenharmony_ci { "2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111 }, 1358c2ecf20Sopenharmony_ci { "5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112 }, 1368c2ecf20Sopenharmony_ci { "5112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A }, 1378c2ecf20Sopenharmony_ci { "5112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112B }, 1388c2ecf20Sopenharmony_ci { "2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112 }, 1398c2ecf20Sopenharmony_ci { "2112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A }, 1408c2ecf20Sopenharmony_ci { "2112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112B }, 1418c2ecf20Sopenharmony_ci { "2413", AR5K_VERSION_RAD, AR5K_SREV_RAD_2413 }, 1428c2ecf20Sopenharmony_ci { "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 }, 1438c2ecf20Sopenharmony_ci { "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 }, 1448c2ecf20Sopenharmony_ci { "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 }, 1458c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH5K_AHB 1468c2ecf20Sopenharmony_ci { "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 }, 1478c2ecf20Sopenharmony_ci { "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 }, 1488c2ecf20Sopenharmony_ci#endif 1498c2ecf20Sopenharmony_ci { "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN }, 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const struct ieee80211_rate ath5k_rates[] = { 1538c2ecf20Sopenharmony_ci { .bitrate = 10, 1548c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_1M, }, 1558c2ecf20Sopenharmony_ci { .bitrate = 20, 1568c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_2M, 1578c2ecf20Sopenharmony_ci .hw_value_short = ATH5K_RATE_CODE_2M | AR5K_SET_SHORT_PREAMBLE, 1588c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 1598c2ecf20Sopenharmony_ci { .bitrate = 55, 1608c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_5_5M, 1618c2ecf20Sopenharmony_ci .hw_value_short = ATH5K_RATE_CODE_5_5M | AR5K_SET_SHORT_PREAMBLE, 1628c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 1638c2ecf20Sopenharmony_ci { .bitrate = 110, 1648c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_11M, 1658c2ecf20Sopenharmony_ci .hw_value_short = ATH5K_RATE_CODE_11M | AR5K_SET_SHORT_PREAMBLE, 1668c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 1678c2ecf20Sopenharmony_ci { .bitrate = 60, 1688c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_6M, 1698c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SUPPORTS_5MHZ | 1708c2ecf20Sopenharmony_ci IEEE80211_RATE_SUPPORTS_10MHZ }, 1718c2ecf20Sopenharmony_ci { .bitrate = 90, 1728c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_9M, 1738c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SUPPORTS_5MHZ | 1748c2ecf20Sopenharmony_ci IEEE80211_RATE_SUPPORTS_10MHZ }, 1758c2ecf20Sopenharmony_ci { .bitrate = 120, 1768c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_12M, 1778c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SUPPORTS_5MHZ | 1788c2ecf20Sopenharmony_ci IEEE80211_RATE_SUPPORTS_10MHZ }, 1798c2ecf20Sopenharmony_ci { .bitrate = 180, 1808c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_18M, 1818c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SUPPORTS_5MHZ | 1828c2ecf20Sopenharmony_ci IEEE80211_RATE_SUPPORTS_10MHZ }, 1838c2ecf20Sopenharmony_ci { .bitrate = 240, 1848c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_24M, 1858c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SUPPORTS_5MHZ | 1868c2ecf20Sopenharmony_ci IEEE80211_RATE_SUPPORTS_10MHZ }, 1878c2ecf20Sopenharmony_ci { .bitrate = 360, 1888c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_36M, 1898c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SUPPORTS_5MHZ | 1908c2ecf20Sopenharmony_ci IEEE80211_RATE_SUPPORTS_10MHZ }, 1918c2ecf20Sopenharmony_ci { .bitrate = 480, 1928c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_48M, 1938c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SUPPORTS_5MHZ | 1948c2ecf20Sopenharmony_ci IEEE80211_RATE_SUPPORTS_10MHZ }, 1958c2ecf20Sopenharmony_ci { .bitrate = 540, 1968c2ecf20Sopenharmony_ci .hw_value = ATH5K_RATE_CODE_54M, 1978c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SUPPORTS_5MHZ | 1988c2ecf20Sopenharmony_ci IEEE80211_RATE_SUPPORTS_10MHZ }, 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci u64 tsf = ath5k_hw_get_tsf64(ah); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if ((tsf & 0x7fff) < rstamp) 2068c2ecf20Sopenharmony_ci tsf -= 0x8000; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return (tsf & ~0x7fff) | rstamp; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciconst char * 2128c2ecf20Sopenharmony_ciath5k_chip_name(enum ath5k_srev_type type, u_int16_t val) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci const char *name = "xxxxx"; 2158c2ecf20Sopenharmony_ci unsigned int i; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(srev_names); i++) { 2188c2ecf20Sopenharmony_ci if (srev_names[i].sr_type != type) 2198c2ecf20Sopenharmony_ci continue; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if ((val & 0xf0) == srev_names[i].sr_val) 2228c2ecf20Sopenharmony_ci name = srev_names[i].sr_name; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if ((val & 0xff) == srev_names[i].sr_val) { 2258c2ecf20Sopenharmony_ci name = srev_names[i].sr_name; 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return name; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_cistatic unsigned int ath5k_ioread32(void *hw_priv, u32 reg_offset) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv; 2358c2ecf20Sopenharmony_ci return ath5k_hw_reg_read(ah, reg_offset); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void ath5k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv; 2418c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, val, reg_offset); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic const struct ath_ops ath5k_common_ops = { 2458c2ecf20Sopenharmony_ci .read = ath5k_ioread32, 2468c2ecf20Sopenharmony_ci .write = ath5k_iowrite32, 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/***********************\ 2508c2ecf20Sopenharmony_ci* Driver Initialization * 2518c2ecf20Sopenharmony_ci\***********************/ 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic void ath5k_reg_notifier(struct wiphy *wiphy, 2548c2ecf20Sopenharmony_ci struct regulatory_request *request) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); 2578c2ecf20Sopenharmony_ci struct ath5k_hw *ah = hw->priv; 2588c2ecf20Sopenharmony_ci struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ath_reg_notifier_apply(wiphy, request, regulatory); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/********************\ 2648c2ecf20Sopenharmony_ci* Channel/mode setup * 2658c2ecf20Sopenharmony_ci\********************/ 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* 2688c2ecf20Sopenharmony_ci * Returns true for the channel numbers used. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH5K_TEST_CHANNELS 2718c2ecf20Sopenharmony_cistatic bool ath5k_is_standard_channel(short chan, enum nl80211_band band) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci return true; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci#else 2778c2ecf20Sopenharmony_cistatic bool ath5k_is_standard_channel(short chan, enum nl80211_band band) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci if (band == NL80211_BAND_2GHZ && chan <= 14) 2808c2ecf20Sopenharmony_ci return true; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return /* UNII 1,2 */ 2838c2ecf20Sopenharmony_ci (((chan & 3) == 0 && chan >= 36 && chan <= 64) || 2848c2ecf20Sopenharmony_ci /* midband */ 2858c2ecf20Sopenharmony_ci ((chan & 3) == 0 && chan >= 100 && chan <= 140) || 2868c2ecf20Sopenharmony_ci /* UNII-3 */ 2878c2ecf20Sopenharmony_ci ((chan & 3) == 1 && chan >= 149 && chan <= 165) || 2888c2ecf20Sopenharmony_ci /* 802.11j 5.030-5.080 GHz (20MHz) */ 2898c2ecf20Sopenharmony_ci (chan == 8 || chan == 12 || chan == 16) || 2908c2ecf20Sopenharmony_ci /* 802.11j 4.9GHz (20MHz) */ 2918c2ecf20Sopenharmony_ci (chan == 184 || chan == 188 || chan == 192 || chan == 196)); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci#endif 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic unsigned int 2968c2ecf20Sopenharmony_ciath5k_setup_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels, 2978c2ecf20Sopenharmony_ci unsigned int mode, unsigned int max) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci unsigned int count, size, freq, ch; 3008c2ecf20Sopenharmony_ci enum nl80211_band band; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci switch (mode) { 3038c2ecf20Sopenharmony_ci case AR5K_MODE_11A: 3048c2ecf20Sopenharmony_ci /* 1..220, but 2GHz frequencies are filtered by check_channel */ 3058c2ecf20Sopenharmony_ci size = 220; 3068c2ecf20Sopenharmony_ci band = NL80211_BAND_5GHZ; 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci case AR5K_MODE_11B: 3098c2ecf20Sopenharmony_ci case AR5K_MODE_11G: 3108c2ecf20Sopenharmony_ci size = 26; 3118c2ecf20Sopenharmony_ci band = NL80211_BAND_2GHZ; 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci default: 3148c2ecf20Sopenharmony_ci ATH5K_WARN(ah, "bad mode, not copying channels\n"); 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci count = 0; 3198c2ecf20Sopenharmony_ci for (ch = 1; ch <= size && count < max; ch++) { 3208c2ecf20Sopenharmony_ci freq = ieee80211_channel_to_frequency(ch, band); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (freq == 0) /* mapping failed - not a standard channel */ 3238c2ecf20Sopenharmony_ci continue; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* Write channel info, needed for ath5k_channel_ok() */ 3268c2ecf20Sopenharmony_ci channels[count].center_freq = freq; 3278c2ecf20Sopenharmony_ci channels[count].band = band; 3288c2ecf20Sopenharmony_ci channels[count].hw_value = mode; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Check if channel is supported by the chipset */ 3318c2ecf20Sopenharmony_ci if (!ath5k_channel_ok(ah, &channels[count])) 3328c2ecf20Sopenharmony_ci continue; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!ath5k_is_standard_channel(ch, band)) 3358c2ecf20Sopenharmony_ci continue; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci count++; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return count; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic void 3448c2ecf20Sopenharmony_ciath5k_setup_rate_idx(struct ath5k_hw *ah, struct ieee80211_supported_band *b) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci u8 i; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci for (i = 0; i < AR5K_MAX_RATES; i++) 3498c2ecf20Sopenharmony_ci ah->rate_idx[b->band][i] = -1; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < b->n_bitrates; i++) { 3528c2ecf20Sopenharmony_ci ah->rate_idx[b->band][b->bitrates[i].hw_value] = i; 3538c2ecf20Sopenharmony_ci if (b->bitrates[i].hw_value_short) 3548c2ecf20Sopenharmony_ci ah->rate_idx[b->band][b->bitrates[i].hw_value_short] = i; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int 3598c2ecf20Sopenharmony_ciath5k_setup_bands(struct ieee80211_hw *hw) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct ath5k_hw *ah = hw->priv; 3628c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 3638c2ecf20Sopenharmony_ci int max_c, count_c = 0; 3648c2ecf20Sopenharmony_ci int i; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(ah->sbands) < NUM_NL80211_BANDS); 3678c2ecf20Sopenharmony_ci max_c = ARRAY_SIZE(ah->channels); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* 2GHz band */ 3708c2ecf20Sopenharmony_ci sband = &ah->sbands[NL80211_BAND_2GHZ]; 3718c2ecf20Sopenharmony_ci sband->band = NL80211_BAND_2GHZ; 3728c2ecf20Sopenharmony_ci sband->bitrates = &ah->rates[NL80211_BAND_2GHZ][0]; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (test_bit(AR5K_MODE_11G, ah->ah_capabilities.cap_mode)) { 3758c2ecf20Sopenharmony_ci /* G mode */ 3768c2ecf20Sopenharmony_ci memcpy(sband->bitrates, &ath5k_rates[0], 3778c2ecf20Sopenharmony_ci sizeof(struct ieee80211_rate) * 12); 3788c2ecf20Sopenharmony_ci sband->n_bitrates = 12; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci sband->channels = ah->channels; 3818c2ecf20Sopenharmony_ci sband->n_channels = ath5k_setup_channels(ah, sband->channels, 3828c2ecf20Sopenharmony_ci AR5K_MODE_11G, max_c); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; 3858c2ecf20Sopenharmony_ci count_c = sband->n_channels; 3868c2ecf20Sopenharmony_ci max_c -= count_c; 3878c2ecf20Sopenharmony_ci } else if (test_bit(AR5K_MODE_11B, ah->ah_capabilities.cap_mode)) { 3888c2ecf20Sopenharmony_ci /* B mode */ 3898c2ecf20Sopenharmony_ci memcpy(sband->bitrates, &ath5k_rates[0], 3908c2ecf20Sopenharmony_ci sizeof(struct ieee80211_rate) * 4); 3918c2ecf20Sopenharmony_ci sband->n_bitrates = 4; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* 5211 only supports B rates and uses 4bit rate codes 3948c2ecf20Sopenharmony_ci * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B) 3958c2ecf20Sopenharmony_ci * fix them up here: 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5211) { 3988c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 3998c2ecf20Sopenharmony_ci sband->bitrates[i].hw_value = 4008c2ecf20Sopenharmony_ci sband->bitrates[i].hw_value & 0xF; 4018c2ecf20Sopenharmony_ci sband->bitrates[i].hw_value_short = 4028c2ecf20Sopenharmony_ci sband->bitrates[i].hw_value_short & 0xF; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci sband->channels = ah->channels; 4078c2ecf20Sopenharmony_ci sband->n_channels = ath5k_setup_channels(ah, sband->channels, 4088c2ecf20Sopenharmony_ci AR5K_MODE_11B, max_c); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; 4118c2ecf20Sopenharmony_ci count_c = sband->n_channels; 4128c2ecf20Sopenharmony_ci max_c -= count_c; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci ath5k_setup_rate_idx(ah, sband); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* 5GHz band, A mode */ 4178c2ecf20Sopenharmony_ci if (test_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode)) { 4188c2ecf20Sopenharmony_ci sband = &ah->sbands[NL80211_BAND_5GHZ]; 4198c2ecf20Sopenharmony_ci sband->band = NL80211_BAND_5GHZ; 4208c2ecf20Sopenharmony_ci sband->bitrates = &ah->rates[NL80211_BAND_5GHZ][0]; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci memcpy(sband->bitrates, &ath5k_rates[4], 4238c2ecf20Sopenharmony_ci sizeof(struct ieee80211_rate) * 8); 4248c2ecf20Sopenharmony_ci sband->n_bitrates = 8; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci sband->channels = &ah->channels[count_c]; 4278c2ecf20Sopenharmony_ci sband->n_channels = ath5k_setup_channels(ah, sband->channels, 4288c2ecf20Sopenharmony_ci AR5K_MODE_11A, max_c); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci hw->wiphy->bands[NL80211_BAND_5GHZ] = sband; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci ath5k_setup_rate_idx(ah, sband); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci ath5k_debug_dump_bands(ah); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci/* 4408c2ecf20Sopenharmony_ci * Set/change channels. We always reset the chip. 4418c2ecf20Sopenharmony_ci * To accomplish this we must first cleanup any pending DMA, 4428c2ecf20Sopenharmony_ci * then restart stuff after a la ath5k_init. 4438c2ecf20Sopenharmony_ci * 4448c2ecf20Sopenharmony_ci * Called with ah->lock. 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ciint 4478c2ecf20Sopenharmony_ciath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, 4508c2ecf20Sopenharmony_ci "channel set, resetting (%u -> %u MHz)\n", 4518c2ecf20Sopenharmony_ci ah->curchan->center_freq, chandef->chan->center_freq); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci switch (chandef->width) { 4548c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 4558c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 4568c2ecf20Sopenharmony_ci ah->ah_bwmode = AR5K_BWMODE_DEFAULT; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 4598c2ecf20Sopenharmony_ci ah->ah_bwmode = AR5K_BWMODE_5MHZ; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 4628c2ecf20Sopenharmony_ci ah->ah_bwmode = AR5K_BWMODE_10MHZ; 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci default: 4658c2ecf20Sopenharmony_ci WARN_ON(1); 4668c2ecf20Sopenharmony_ci return -EINVAL; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* 4708c2ecf20Sopenharmony_ci * To switch channels clear any pending DMA operations; 4718c2ecf20Sopenharmony_ci * wait long enough for the RX fifo to drain, reset the 4728c2ecf20Sopenharmony_ci * hardware at the new frequency, and then re-enable 4738c2ecf20Sopenharmony_ci * the relevant bits of the h/w. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci return ath5k_reset(ah, chandef->chan, true); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_civoid ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct ath5k_vif_iter_data *iter_data = data; 4818c2ecf20Sopenharmony_ci int i; 4828c2ecf20Sopenharmony_ci struct ath5k_vif *avf = (void *)vif->drv_priv; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (iter_data->hw_macaddr) 4858c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 4868c2ecf20Sopenharmony_ci iter_data->mask[i] &= 4878c2ecf20Sopenharmony_ci ~(iter_data->hw_macaddr[i] ^ mac[i]); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (!iter_data->found_active) { 4908c2ecf20Sopenharmony_ci iter_data->found_active = true; 4918c2ecf20Sopenharmony_ci memcpy(iter_data->active_mac, mac, ETH_ALEN); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (iter_data->need_set_hw_addr && iter_data->hw_macaddr) 4958c2ecf20Sopenharmony_ci if (ether_addr_equal(iter_data->hw_macaddr, mac)) 4968c2ecf20Sopenharmony_ci iter_data->need_set_hw_addr = false; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!iter_data->any_assoc) { 4998c2ecf20Sopenharmony_ci if (avf->assoc) 5008c2ecf20Sopenharmony_ci iter_data->any_assoc = true; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* Calculate combined mode - when APs are active, operate in AP mode. 5048c2ecf20Sopenharmony_ci * Otherwise use the mode of the new interface. This can currently 5058c2ecf20Sopenharmony_ci * only deal with combinations of APs and STAs. Only one ad-hoc 5068c2ecf20Sopenharmony_ci * interfaces is allowed. 5078c2ecf20Sopenharmony_ci */ 5088c2ecf20Sopenharmony_ci if (avf->opmode == NL80211_IFTYPE_AP) 5098c2ecf20Sopenharmony_ci iter_data->opmode = NL80211_IFTYPE_AP; 5108c2ecf20Sopenharmony_ci else { 5118c2ecf20Sopenharmony_ci if (avf->opmode == NL80211_IFTYPE_STATION) 5128c2ecf20Sopenharmony_ci iter_data->n_stas++; 5138c2ecf20Sopenharmony_ci if (iter_data->opmode == NL80211_IFTYPE_UNSPECIFIED) 5148c2ecf20Sopenharmony_ci iter_data->opmode = avf->opmode; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_civoid 5198c2ecf20Sopenharmony_ciath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah, 5208c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 5238c2ecf20Sopenharmony_ci struct ath5k_vif_iter_data iter_data; 5248c2ecf20Sopenharmony_ci u32 rfilt; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * Use the hardware MAC address as reference, the hardware uses it 5288c2ecf20Sopenharmony_ci * together with the BSSID mask when matching addresses. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ci iter_data.hw_macaddr = common->macaddr; 5318c2ecf20Sopenharmony_ci eth_broadcast_addr(iter_data.mask); 5328c2ecf20Sopenharmony_ci iter_data.found_active = false; 5338c2ecf20Sopenharmony_ci iter_data.need_set_hw_addr = true; 5348c2ecf20Sopenharmony_ci iter_data.opmode = NL80211_IFTYPE_UNSPECIFIED; 5358c2ecf20Sopenharmony_ci iter_data.n_stas = 0; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (vif) 5388c2ecf20Sopenharmony_ci ath5k_vif_iter(&iter_data, vif->addr, vif); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* Get list of all active MAC addresses */ 5418c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic( 5428c2ecf20Sopenharmony_ci ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL, 5438c2ecf20Sopenharmony_ci ath5k_vif_iter, &iter_data); 5448c2ecf20Sopenharmony_ci memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci ah->opmode = iter_data.opmode; 5478c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_UNSPECIFIED) 5488c2ecf20Sopenharmony_ci /* Nothing active, default to station mode */ 5498c2ecf20Sopenharmony_ci ah->opmode = NL80211_IFTYPE_STATION; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci ath5k_hw_set_opmode(ah, ah->opmode); 5528c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n", 5538c2ecf20Sopenharmony_ci ah->opmode, ath_opmode_to_string(ah->opmode)); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (iter_data.need_set_hw_addr && iter_data.found_active) 5568c2ecf20Sopenharmony_ci ath5k_hw_set_lladdr(ah, iter_data.active_mac); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (ath5k_hw_hasbssidmask(ah)) 5598c2ecf20Sopenharmony_ci ath5k_hw_set_bssid_mask(ah, ah->bssidmask); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Set up RX Filter */ 5628c2ecf20Sopenharmony_ci if (iter_data.n_stas > 1) { 5638c2ecf20Sopenharmony_ci /* If you have multiple STA interfaces connected to 5648c2ecf20Sopenharmony_ci * different APs, ARPs are not received (most of the time?) 5658c2ecf20Sopenharmony_ci * Enabling PROMISC appears to fix that problem. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ci ah->filter_flags |= AR5K_RX_FILTER_PROM; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci rfilt = ah->filter_flags; 5718c2ecf20Sopenharmony_ci ath5k_hw_set_rx_filter(ah, rfilt); 5728c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic inline int 5768c2ecf20Sopenharmony_ciath5k_hw_to_driver_rix(struct ath5k_hw *ah, int hw_rix) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci int rix; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* return base rate on errors */ 5818c2ecf20Sopenharmony_ci if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES, 5828c2ecf20Sopenharmony_ci "hw_rix out of bounds: %x\n", hw_rix)) 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci rix = ah->rate_idx[ah->curchan->band][hw_rix]; 5868c2ecf20Sopenharmony_ci if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix)) 5878c2ecf20Sopenharmony_ci rix = 0; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return rix; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci/***************\ 5938c2ecf20Sopenharmony_ci* Buffers setup * 5948c2ecf20Sopenharmony_ci\***************/ 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic 5978c2ecf20Sopenharmony_cistruct sk_buff *ath5k_rx_skb_alloc(struct ath5k_hw *ah, dma_addr_t *skb_addr) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 6008c2ecf20Sopenharmony_ci struct sk_buff *skb; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * Allocate buffer with headroom_needed space for the 6048c2ecf20Sopenharmony_ci * fake physical layer header at the start. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci skb = ath_rxbuf_alloc(common, 6078c2ecf20Sopenharmony_ci common->rx_bufsize, 6088c2ecf20Sopenharmony_ci GFP_ATOMIC); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (!skb) { 6118c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't alloc skbuff of size %u\n", 6128c2ecf20Sopenharmony_ci common->rx_bufsize); 6138c2ecf20Sopenharmony_ci return NULL; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci *skb_addr = dma_map_single(ah->dev, 6178c2ecf20Sopenharmony_ci skb->data, common->rx_bufsize, 6188c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(ah->dev, *skb_addr))) { 6218c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "%s: DMA mapping failed\n", __func__); 6228c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 6238c2ecf20Sopenharmony_ci return NULL; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci return skb; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic int 6298c2ecf20Sopenharmony_ciath5k_rxbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct sk_buff *skb = bf->skb; 6328c2ecf20Sopenharmony_ci struct ath5k_desc *ds; 6338c2ecf20Sopenharmony_ci int ret; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (!skb) { 6368c2ecf20Sopenharmony_ci skb = ath5k_rx_skb_alloc(ah, &bf->skbaddr); 6378c2ecf20Sopenharmony_ci if (!skb) 6388c2ecf20Sopenharmony_ci return -ENOMEM; 6398c2ecf20Sopenharmony_ci bf->skb = skb; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* 6438c2ecf20Sopenharmony_ci * Setup descriptors. For receive we always terminate 6448c2ecf20Sopenharmony_ci * the descriptor list with a self-linked entry so we'll 6458c2ecf20Sopenharmony_ci * not get overrun under high load (as can happen with a 6468c2ecf20Sopenharmony_ci * 5212 when ANI processing enables PHY error frames). 6478c2ecf20Sopenharmony_ci * 6488c2ecf20Sopenharmony_ci * To ensure the last descriptor is self-linked we create 6498c2ecf20Sopenharmony_ci * each descriptor as self-linked and add it to the end. As 6508c2ecf20Sopenharmony_ci * each additional descriptor is added the previous self-linked 6518c2ecf20Sopenharmony_ci * entry is "fixed" naturally. This should be safe even 6528c2ecf20Sopenharmony_ci * if DMA is happening. When processing RX interrupts we 6538c2ecf20Sopenharmony_ci * never remove/process the last, self-linked, entry on the 6548c2ecf20Sopenharmony_ci * descriptor list. This ensures the hardware always has 6558c2ecf20Sopenharmony_ci * someplace to write a new frame. 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci ds = bf->desc; 6588c2ecf20Sopenharmony_ci ds->ds_link = bf->daddr; /* link to self */ 6598c2ecf20Sopenharmony_ci ds->ds_data = bf->skbaddr; 6608c2ecf20Sopenharmony_ci ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0); 6618c2ecf20Sopenharmony_ci if (ret) { 6628c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "%s: could not setup RX desc\n", __func__); 6638c2ecf20Sopenharmony_ci return ret; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (ah->rxlink != NULL) 6678c2ecf20Sopenharmony_ci *ah->rxlink = bf->daddr; 6688c2ecf20Sopenharmony_ci ah->rxlink = &ds->ds_link; 6698c2ecf20Sopenharmony_ci return 0; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr; 6758c2ecf20Sopenharmony_ci enum ath5k_pkt_type htype; 6768c2ecf20Sopenharmony_ci __le16 fc; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 6798c2ecf20Sopenharmony_ci fc = hdr->frame_control; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (ieee80211_is_beacon(fc)) 6828c2ecf20Sopenharmony_ci htype = AR5K_PKT_TYPE_BEACON; 6838c2ecf20Sopenharmony_ci else if (ieee80211_is_probe_resp(fc)) 6848c2ecf20Sopenharmony_ci htype = AR5K_PKT_TYPE_PROBE_RESP; 6858c2ecf20Sopenharmony_ci else if (ieee80211_is_atim(fc)) 6868c2ecf20Sopenharmony_ci htype = AR5K_PKT_TYPE_ATIM; 6878c2ecf20Sopenharmony_ci else if (ieee80211_is_pspoll(fc)) 6888c2ecf20Sopenharmony_ci htype = AR5K_PKT_TYPE_PSPOLL; 6898c2ecf20Sopenharmony_ci else 6908c2ecf20Sopenharmony_ci htype = AR5K_PKT_TYPE_NORMAL; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return htype; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic struct ieee80211_rate * 6968c2ecf20Sopenharmony_ciath5k_get_rate(const struct ieee80211_hw *hw, 6978c2ecf20Sopenharmony_ci const struct ieee80211_tx_info *info, 6988c2ecf20Sopenharmony_ci struct ath5k_buf *bf, int idx) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * convert a ieee80211_tx_rate RC-table entry to 7028c2ecf20Sopenharmony_ci * the respective ieee80211_rate struct 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_ci if (bf->rates[idx].idx < 0) { 7058c2ecf20Sopenharmony_ci return NULL; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return &hw->wiphy->bands[info->band]->bitrates[ bf->rates[idx].idx ]; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic u16 7128c2ecf20Sopenharmony_ciath5k_get_rate_hw_value(const struct ieee80211_hw *hw, 7138c2ecf20Sopenharmony_ci const struct ieee80211_tx_info *info, 7148c2ecf20Sopenharmony_ci struct ath5k_buf *bf, int idx) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct ieee80211_rate *rate; 7178c2ecf20Sopenharmony_ci u16 hw_rate; 7188c2ecf20Sopenharmony_ci u8 rc_flags; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci rate = ath5k_get_rate(hw, info, bf, idx); 7218c2ecf20Sopenharmony_ci if (!rate) 7228c2ecf20Sopenharmony_ci return 0; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci rc_flags = bf->rates[idx].flags; 7258c2ecf20Sopenharmony_ci hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? 7268c2ecf20Sopenharmony_ci rate->hw_value_short : rate->hw_value; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return hw_rate; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int 7328c2ecf20Sopenharmony_ciath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, 7338c2ecf20Sopenharmony_ci struct ath5k_txq *txq, int padsize, 7348c2ecf20Sopenharmony_ci struct ieee80211_tx_control *control) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct ath5k_desc *ds = bf->desc; 7378c2ecf20Sopenharmony_ci struct sk_buff *skb = bf->skb; 7388c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 7398c2ecf20Sopenharmony_ci unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; 7408c2ecf20Sopenharmony_ci struct ieee80211_rate *rate; 7418c2ecf20Sopenharmony_ci unsigned int mrr_rate[3], mrr_tries[3]; 7428c2ecf20Sopenharmony_ci int i, ret; 7438c2ecf20Sopenharmony_ci u16 hw_rate; 7448c2ecf20Sopenharmony_ci u16 cts_rate = 0; 7458c2ecf20Sopenharmony_ci u16 duration = 0; 7468c2ecf20Sopenharmony_ci u8 rc_flags; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* XXX endianness */ 7518c2ecf20Sopenharmony_ci bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len, 7528c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (dma_mapping_error(ah->dev, bf->skbaddr)) 7558c2ecf20Sopenharmony_ci return -ENOSPC; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates, 7588c2ecf20Sopenharmony_ci ARRAY_SIZE(bf->rates)); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci rate = ath5k_get_rate(ah->hw, info, bf, 0); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (!rate) { 7638c2ecf20Sopenharmony_ci ret = -EINVAL; 7648c2ecf20Sopenharmony_ci goto err_unmap; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_NO_ACK) 7688c2ecf20Sopenharmony_ci flags |= AR5K_TXDESC_NOACK; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci rc_flags = bf->rates[0].flags; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci pktlen = skb->len; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* FIXME: If we are in g mode and rate is a CCK rate 7778c2ecf20Sopenharmony_ci * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta 7788c2ecf20Sopenharmony_ci * from tx power (value is in dB units already) */ 7798c2ecf20Sopenharmony_ci if (info->control.hw_key) { 7808c2ecf20Sopenharmony_ci keyidx = info->control.hw_key->hw_key_idx; 7818c2ecf20Sopenharmony_ci pktlen += info->control.hw_key->icv_len; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { 7848c2ecf20Sopenharmony_ci flags |= AR5K_TXDESC_RTSENA; 7858c2ecf20Sopenharmony_ci cts_rate = ieee80211_get_rts_cts_rate(ah->hw, info)->hw_value; 7868c2ecf20Sopenharmony_ci duration = le16_to_cpu(ieee80211_rts_duration(ah->hw, 7878c2ecf20Sopenharmony_ci info->control.vif, pktlen, info)); 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { 7908c2ecf20Sopenharmony_ci flags |= AR5K_TXDESC_CTSENA; 7918c2ecf20Sopenharmony_ci cts_rate = ieee80211_get_rts_cts_rate(ah->hw, info)->hw_value; 7928c2ecf20Sopenharmony_ci duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw, 7938c2ecf20Sopenharmony_ci info->control.vif, pktlen, info)); 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ret = ah->ah_setup_tx_desc(ah, ds, pktlen, 7978c2ecf20Sopenharmony_ci ieee80211_get_hdrlen_from_skb(skb), padsize, 7988c2ecf20Sopenharmony_ci get_hw_packet_type(skb), 7998c2ecf20Sopenharmony_ci (ah->ah_txpower.txp_requested * 2), 8008c2ecf20Sopenharmony_ci hw_rate, 8018c2ecf20Sopenharmony_ci bf->rates[0].count, keyidx, ah->ah_tx_ant, flags, 8028c2ecf20Sopenharmony_ci cts_rate, duration); 8038c2ecf20Sopenharmony_ci if (ret) 8048c2ecf20Sopenharmony_ci goto err_unmap; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* Set up MRR descriptor */ 8078c2ecf20Sopenharmony_ci if (ah->ah_capabilities.cap_has_mrr_support) { 8088c2ecf20Sopenharmony_ci memset(mrr_rate, 0, sizeof(mrr_rate)); 8098c2ecf20Sopenharmony_ci memset(mrr_tries, 0, sizeof(mrr_tries)); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci rate = ath5k_get_rate(ah->hw, info, bf, i); 8148c2ecf20Sopenharmony_ci if (!rate) 8158c2ecf20Sopenharmony_ci break; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci mrr_rate[i] = ath5k_get_rate_hw_value(ah->hw, info, bf, i); 8188c2ecf20Sopenharmony_ci mrr_tries[i] = bf->rates[i].count; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci ath5k_hw_setup_mrr_tx_desc(ah, ds, 8228c2ecf20Sopenharmony_ci mrr_rate[0], mrr_tries[0], 8238c2ecf20Sopenharmony_ci mrr_rate[1], mrr_tries[1], 8248c2ecf20Sopenharmony_ci mrr_rate[2], mrr_tries[2]); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci ds->ds_link = 0; 8288c2ecf20Sopenharmony_ci ds->ds_data = bf->skbaddr; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci spin_lock_bh(&txq->lock); 8318c2ecf20Sopenharmony_ci list_add_tail(&bf->list, &txq->q); 8328c2ecf20Sopenharmony_ci txq->txq_len++; 8338c2ecf20Sopenharmony_ci if (txq->link == NULL) /* is this first packet? */ 8348c2ecf20Sopenharmony_ci ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr); 8358c2ecf20Sopenharmony_ci else /* no, so only link it */ 8368c2ecf20Sopenharmony_ci *txq->link = bf->daddr; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci txq->link = &ds->ds_link; 8398c2ecf20Sopenharmony_ci ath5k_hw_start_tx_dma(ah, txq->qnum); 8408c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->lock); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci return 0; 8438c2ecf20Sopenharmony_cierr_unmap: 8448c2ecf20Sopenharmony_ci dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE); 8458c2ecf20Sopenharmony_ci return ret; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci/*******************\ 8498c2ecf20Sopenharmony_ci* Descriptors setup * 8508c2ecf20Sopenharmony_ci\*******************/ 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic int 8538c2ecf20Sopenharmony_ciath5k_desc_alloc(struct ath5k_hw *ah) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct ath5k_desc *ds; 8568c2ecf20Sopenharmony_ci struct ath5k_buf *bf; 8578c2ecf20Sopenharmony_ci dma_addr_t da; 8588c2ecf20Sopenharmony_ci unsigned int i; 8598c2ecf20Sopenharmony_ci int ret; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* allocate descriptors */ 8628c2ecf20Sopenharmony_ci ah->desc_len = sizeof(struct ath5k_desc) * 8638c2ecf20Sopenharmony_ci (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci ah->desc = dma_alloc_coherent(ah->dev, ah->desc_len, 8668c2ecf20Sopenharmony_ci &ah->desc_daddr, GFP_KERNEL); 8678c2ecf20Sopenharmony_ci if (ah->desc == NULL) { 8688c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't allocate descriptors\n"); 8698c2ecf20Sopenharmony_ci ret = -ENOMEM; 8708c2ecf20Sopenharmony_ci goto err; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci ds = ah->desc; 8738c2ecf20Sopenharmony_ci da = ah->desc_daddr; 8748c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n", 8758c2ecf20Sopenharmony_ci ds, ah->desc_len, (unsigned long long)ah->desc_daddr); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF, 8788c2ecf20Sopenharmony_ci sizeof(struct ath5k_buf), GFP_KERNEL); 8798c2ecf20Sopenharmony_ci if (bf == NULL) { 8808c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't allocate bufptr\n"); 8818c2ecf20Sopenharmony_ci ret = -ENOMEM; 8828c2ecf20Sopenharmony_ci goto err_free; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci ah->bufptr = bf; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ah->rxbuf); 8878c2ecf20Sopenharmony_ci for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) { 8888c2ecf20Sopenharmony_ci bf->desc = ds; 8898c2ecf20Sopenharmony_ci bf->daddr = da; 8908c2ecf20Sopenharmony_ci list_add_tail(&bf->list, &ah->rxbuf); 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ah->txbuf); 8948c2ecf20Sopenharmony_ci ah->txbuf_len = ATH_TXBUF; 8958c2ecf20Sopenharmony_ci for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, da += sizeof(*ds)) { 8968c2ecf20Sopenharmony_ci bf->desc = ds; 8978c2ecf20Sopenharmony_ci bf->daddr = da; 8988c2ecf20Sopenharmony_ci list_add_tail(&bf->list, &ah->txbuf); 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* beacon buffers */ 9028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ah->bcbuf); 9038c2ecf20Sopenharmony_ci for (i = 0; i < ATH_BCBUF; i++, bf++, ds++, da += sizeof(*ds)) { 9048c2ecf20Sopenharmony_ci bf->desc = ds; 9058c2ecf20Sopenharmony_ci bf->daddr = da; 9068c2ecf20Sopenharmony_ci list_add_tail(&bf->list, &ah->bcbuf); 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_cierr_free: 9118c2ecf20Sopenharmony_ci dma_free_coherent(ah->dev, ah->desc_len, ah->desc, ah->desc_daddr); 9128c2ecf20Sopenharmony_cierr: 9138c2ecf20Sopenharmony_ci ah->desc = NULL; 9148c2ecf20Sopenharmony_ci return ret; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_civoid 9188c2ecf20Sopenharmony_ciath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci BUG_ON(!bf); 9218c2ecf20Sopenharmony_ci if (!bf->skb) 9228c2ecf20Sopenharmony_ci return; 9238c2ecf20Sopenharmony_ci dma_unmap_single(ah->dev, bf->skbaddr, bf->skb->len, 9248c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9258c2ecf20Sopenharmony_ci ieee80211_free_txskb(ah->hw, bf->skb); 9268c2ecf20Sopenharmony_ci bf->skb = NULL; 9278c2ecf20Sopenharmony_ci bf->skbaddr = 0; 9288c2ecf20Sopenharmony_ci bf->desc->ds_data = 0; 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_civoid 9328c2ecf20Sopenharmony_ciath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci BUG_ON(!bf); 9378c2ecf20Sopenharmony_ci if (!bf->skb) 9388c2ecf20Sopenharmony_ci return; 9398c2ecf20Sopenharmony_ci dma_unmap_single(ah->dev, bf->skbaddr, common->rx_bufsize, 9408c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 9418c2ecf20Sopenharmony_ci dev_kfree_skb_any(bf->skb); 9428c2ecf20Sopenharmony_ci bf->skb = NULL; 9438c2ecf20Sopenharmony_ci bf->skbaddr = 0; 9448c2ecf20Sopenharmony_ci bf->desc->ds_data = 0; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic void 9488c2ecf20Sopenharmony_ciath5k_desc_free(struct ath5k_hw *ah) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci struct ath5k_buf *bf; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci list_for_each_entry(bf, &ah->txbuf, list) 9538c2ecf20Sopenharmony_ci ath5k_txbuf_free_skb(ah, bf); 9548c2ecf20Sopenharmony_ci list_for_each_entry(bf, &ah->rxbuf, list) 9558c2ecf20Sopenharmony_ci ath5k_rxbuf_free_skb(ah, bf); 9568c2ecf20Sopenharmony_ci list_for_each_entry(bf, &ah->bcbuf, list) 9578c2ecf20Sopenharmony_ci ath5k_txbuf_free_skb(ah, bf); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* Free memory associated with all descriptors */ 9608c2ecf20Sopenharmony_ci dma_free_coherent(ah->dev, ah->desc_len, ah->desc, ah->desc_daddr); 9618c2ecf20Sopenharmony_ci ah->desc = NULL; 9628c2ecf20Sopenharmony_ci ah->desc_daddr = 0; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci kfree(ah->bufptr); 9658c2ecf20Sopenharmony_ci ah->bufptr = NULL; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci/**************\ 9708c2ecf20Sopenharmony_ci* Queues setup * 9718c2ecf20Sopenharmony_ci\**************/ 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic struct ath5k_txq * 9748c2ecf20Sopenharmony_ciath5k_txq_setup(struct ath5k_hw *ah, 9758c2ecf20Sopenharmony_ci int qtype, int subtype) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct ath5k_txq *txq; 9788c2ecf20Sopenharmony_ci struct ath5k_txq_info qi = { 9798c2ecf20Sopenharmony_ci .tqi_subtype = subtype, 9808c2ecf20Sopenharmony_ci /* XXX: default values not correct for B and XR channels, 9818c2ecf20Sopenharmony_ci * but who cares? */ 9828c2ecf20Sopenharmony_ci .tqi_aifs = AR5K_TUNE_AIFS, 9838c2ecf20Sopenharmony_ci .tqi_cw_min = AR5K_TUNE_CWMIN, 9848c2ecf20Sopenharmony_ci .tqi_cw_max = AR5K_TUNE_CWMAX 9858c2ecf20Sopenharmony_ci }; 9868c2ecf20Sopenharmony_ci int qnum; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* 9898c2ecf20Sopenharmony_ci * Enable interrupts only for EOL and DESC conditions. 9908c2ecf20Sopenharmony_ci * We mark tx descriptors to receive a DESC interrupt 9918c2ecf20Sopenharmony_ci * when a tx queue gets deep; otherwise we wait for the 9928c2ecf20Sopenharmony_ci * EOL to reap descriptors. Note that this is done to 9938c2ecf20Sopenharmony_ci * reduce interrupt load and this only defers reaping 9948c2ecf20Sopenharmony_ci * descriptors, never transmitting frames. Aside from 9958c2ecf20Sopenharmony_ci * reducing interrupts this also permits more concurrency. 9968c2ecf20Sopenharmony_ci * The only potential downside is if the tx queue backs 9978c2ecf20Sopenharmony_ci * up in which case the top half of the kernel may backup 9988c2ecf20Sopenharmony_ci * due to a lack of tx descriptors. 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_ci qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE | 10018c2ecf20Sopenharmony_ci AR5K_TXQ_FLAG_TXDESCINT_ENABLE; 10028c2ecf20Sopenharmony_ci qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi); 10038c2ecf20Sopenharmony_ci if (qnum < 0) { 10048c2ecf20Sopenharmony_ci /* 10058c2ecf20Sopenharmony_ci * NB: don't print a message, this happens 10068c2ecf20Sopenharmony_ci * normally on parts with too few tx queues 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_ci return ERR_PTR(qnum); 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci txq = &ah->txqs[qnum]; 10118c2ecf20Sopenharmony_ci if (!txq->setup) { 10128c2ecf20Sopenharmony_ci txq->qnum = qnum; 10138c2ecf20Sopenharmony_ci txq->link = NULL; 10148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&txq->q); 10158c2ecf20Sopenharmony_ci spin_lock_init(&txq->lock); 10168c2ecf20Sopenharmony_ci txq->setup = true; 10178c2ecf20Sopenharmony_ci txq->txq_len = 0; 10188c2ecf20Sopenharmony_ci txq->txq_max = ATH5K_TXQ_LEN_MAX; 10198c2ecf20Sopenharmony_ci txq->txq_poll_mark = false; 10208c2ecf20Sopenharmony_ci txq->txq_stuck = 0; 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci return &ah->txqs[qnum]; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int 10268c2ecf20Sopenharmony_ciath5k_beaconq_setup(struct ath5k_hw *ah) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci struct ath5k_txq_info qi = { 10298c2ecf20Sopenharmony_ci /* XXX: default values not correct for B and XR channels, 10308c2ecf20Sopenharmony_ci * but who cares? */ 10318c2ecf20Sopenharmony_ci .tqi_aifs = AR5K_TUNE_AIFS, 10328c2ecf20Sopenharmony_ci .tqi_cw_min = AR5K_TUNE_CWMIN, 10338c2ecf20Sopenharmony_ci .tqi_cw_max = AR5K_TUNE_CWMAX, 10348c2ecf20Sopenharmony_ci /* NB: for dynamic turbo, don't enable any other interrupts */ 10358c2ecf20Sopenharmony_ci .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE 10368c2ecf20Sopenharmony_ci }; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi); 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic int 10428c2ecf20Sopenharmony_ciath5k_beaconq_config(struct ath5k_hw *ah) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci struct ath5k_txq_info qi; 10458c2ecf20Sopenharmony_ci int ret; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci ret = ath5k_hw_get_tx_queueprops(ah, ah->bhalq, &qi); 10488c2ecf20Sopenharmony_ci if (ret) 10498c2ecf20Sopenharmony_ci goto err; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP || 10528c2ecf20Sopenharmony_ci ah->opmode == NL80211_IFTYPE_MESH_POINT) { 10538c2ecf20Sopenharmony_ci /* 10548c2ecf20Sopenharmony_ci * Always burst out beacon and CAB traffic 10558c2ecf20Sopenharmony_ci * (aifs = cwmin = cwmax = 0) 10568c2ecf20Sopenharmony_ci */ 10578c2ecf20Sopenharmony_ci qi.tqi_aifs = 0; 10588c2ecf20Sopenharmony_ci qi.tqi_cw_min = 0; 10598c2ecf20Sopenharmony_ci qi.tqi_cw_max = 0; 10608c2ecf20Sopenharmony_ci } else if (ah->opmode == NL80211_IFTYPE_ADHOC) { 10618c2ecf20Sopenharmony_ci /* 10628c2ecf20Sopenharmony_ci * Adhoc mode; backoff between 0 and (2 * cw_min). 10638c2ecf20Sopenharmony_ci */ 10648c2ecf20Sopenharmony_ci qi.tqi_aifs = 0; 10658c2ecf20Sopenharmony_ci qi.tqi_cw_min = 0; 10668c2ecf20Sopenharmony_ci qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, 10708c2ecf20Sopenharmony_ci "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n", 10718c2ecf20Sopenharmony_ci qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci ret = ath5k_hw_set_tx_queueprops(ah, ah->bhalq, &qi); 10748c2ecf20Sopenharmony_ci if (ret) { 10758c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "%s: unable to update parameters for beacon " 10768c2ecf20Sopenharmony_ci "hardware queue!\n", __func__); 10778c2ecf20Sopenharmony_ci goto err; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci ret = ath5k_hw_reset_tx_queue(ah, ah->bhalq); /* push to h/w */ 10808c2ecf20Sopenharmony_ci if (ret) 10818c2ecf20Sopenharmony_ci goto err; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* reconfigure cabq with ready time to 80% of beacon_interval */ 10848c2ecf20Sopenharmony_ci ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); 10858c2ecf20Sopenharmony_ci if (ret) 10868c2ecf20Sopenharmony_ci goto err; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci qi.tqi_ready_time = (ah->bintval * 80) / 100; 10898c2ecf20Sopenharmony_ci ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi); 10908c2ecf20Sopenharmony_ci if (ret) 10918c2ecf20Sopenharmony_ci goto err; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB); 10948c2ecf20Sopenharmony_cierr: 10958c2ecf20Sopenharmony_ci return ret; 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci/** 10998c2ecf20Sopenharmony_ci * ath5k_drain_tx_buffs - Empty tx buffers 11008c2ecf20Sopenharmony_ci * 11018c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 11028c2ecf20Sopenharmony_ci * 11038c2ecf20Sopenharmony_ci * Empty tx buffers from all queues in preparation 11048c2ecf20Sopenharmony_ci * of a reset or during shutdown. 11058c2ecf20Sopenharmony_ci * 11068c2ecf20Sopenharmony_ci * NB: this assumes output has been stopped and 11078c2ecf20Sopenharmony_ci * we do not need to block ath5k_tx_tasklet 11088c2ecf20Sopenharmony_ci */ 11098c2ecf20Sopenharmony_cistatic void 11108c2ecf20Sopenharmony_ciath5k_drain_tx_buffs(struct ath5k_hw *ah) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct ath5k_txq *txq; 11138c2ecf20Sopenharmony_ci struct ath5k_buf *bf, *bf0; 11148c2ecf20Sopenharmony_ci int i; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) { 11178c2ecf20Sopenharmony_ci if (ah->txqs[i].setup) { 11188c2ecf20Sopenharmony_ci txq = &ah->txqs[i]; 11198c2ecf20Sopenharmony_ci spin_lock_bh(&txq->lock); 11208c2ecf20Sopenharmony_ci list_for_each_entry_safe(bf, bf0, &txq->q, list) { 11218c2ecf20Sopenharmony_ci ath5k_debug_printtxbuf(ah, bf); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci ath5k_txbuf_free_skb(ah, bf); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci spin_lock(&ah->txbuflock); 11268c2ecf20Sopenharmony_ci list_move_tail(&bf->list, &ah->txbuf); 11278c2ecf20Sopenharmony_ci ah->txbuf_len++; 11288c2ecf20Sopenharmony_ci txq->txq_len--; 11298c2ecf20Sopenharmony_ci spin_unlock(&ah->txbuflock); 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci txq->link = NULL; 11328c2ecf20Sopenharmony_ci txq->txq_poll_mark = false; 11338c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->lock); 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cistatic void 11398c2ecf20Sopenharmony_ciath5k_txq_release(struct ath5k_hw *ah) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct ath5k_txq *txq = ah->txqs; 11428c2ecf20Sopenharmony_ci unsigned int i; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ah->txqs); i++, txq++) 11458c2ecf20Sopenharmony_ci if (txq->setup) { 11468c2ecf20Sopenharmony_ci ath5k_hw_release_tx_queue(ah, txq->qnum); 11478c2ecf20Sopenharmony_ci txq->setup = false; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/*************\ 11538c2ecf20Sopenharmony_ci* RX Handling * 11548c2ecf20Sopenharmony_ci\*************/ 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci/* 11578c2ecf20Sopenharmony_ci * Enable the receive h/w following a reset. 11588c2ecf20Sopenharmony_ci */ 11598c2ecf20Sopenharmony_cistatic int 11608c2ecf20Sopenharmony_ciath5k_rx_start(struct ath5k_hw *ah) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 11638c2ecf20Sopenharmony_ci struct ath5k_buf *bf; 11648c2ecf20Sopenharmony_ci int ret; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n", 11698c2ecf20Sopenharmony_ci common->cachelsz, common->rx_bufsize); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci spin_lock_bh(&ah->rxbuflock); 11728c2ecf20Sopenharmony_ci ah->rxlink = NULL; 11738c2ecf20Sopenharmony_ci list_for_each_entry(bf, &ah->rxbuf, list) { 11748c2ecf20Sopenharmony_ci ret = ath5k_rxbuf_setup(ah, bf); 11758c2ecf20Sopenharmony_ci if (ret != 0) { 11768c2ecf20Sopenharmony_ci spin_unlock_bh(&ah->rxbuflock); 11778c2ecf20Sopenharmony_ci goto err; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci bf = list_first_entry(&ah->rxbuf, struct ath5k_buf, list); 11818c2ecf20Sopenharmony_ci ath5k_hw_set_rxdp(ah, bf->daddr); 11828c2ecf20Sopenharmony_ci spin_unlock_bh(&ah->rxbuflock); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */ 11858c2ecf20Sopenharmony_ci ath5k_update_bssid_mask_and_opmode(ah, NULL); /* set filters, etc. */ 11868c2ecf20Sopenharmony_ci ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci return 0; 11898c2ecf20Sopenharmony_cierr: 11908c2ecf20Sopenharmony_ci return ret; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci/* 11948c2ecf20Sopenharmony_ci * Disable the receive logic on PCU (DRU) 11958c2ecf20Sopenharmony_ci * In preparation for a shutdown. 11968c2ecf20Sopenharmony_ci * 11978c2ecf20Sopenharmony_ci * Note: Doesn't stop rx DMA, ath5k_hw_dma_stop 11988c2ecf20Sopenharmony_ci * does. 11998c2ecf20Sopenharmony_ci */ 12008c2ecf20Sopenharmony_cistatic void 12018c2ecf20Sopenharmony_ciath5k_rx_stop(struct ath5k_hw *ah) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ 12058c2ecf20Sopenharmony_ci ath5k_hw_stop_rx_pcu(ah); /* disable PCU */ 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci ath5k_debug_printrxbuffs(ah); 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_cistatic unsigned int 12118c2ecf20Sopenharmony_ciath5k_rx_decrypted(struct ath5k_hw *ah, struct sk_buff *skb, 12128c2ecf20Sopenharmony_ci struct ath5k_rx_status *rs) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 12158c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)skb->data; 12168c2ecf20Sopenharmony_ci unsigned int keyix, hlen; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (!(rs->rs_status & AR5K_RXERR_DECRYPT) && 12198c2ecf20Sopenharmony_ci rs->rs_keyix != AR5K_RXKEYIX_INVALID) 12208c2ecf20Sopenharmony_ci return RX_FLAG_DECRYPTED; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* Apparently when a default key is used to decrypt the packet 12238c2ecf20Sopenharmony_ci the hw does not set the index used to decrypt. In such cases 12248c2ecf20Sopenharmony_ci get the index from the packet. */ 12258c2ecf20Sopenharmony_ci hlen = ieee80211_hdrlen(hdr->frame_control); 12268c2ecf20Sopenharmony_ci if (ieee80211_has_protected(hdr->frame_control) && 12278c2ecf20Sopenharmony_ci !(rs->rs_status & AR5K_RXERR_DECRYPT) && 12288c2ecf20Sopenharmony_ci skb->len >= hlen + 4) { 12298c2ecf20Sopenharmony_ci keyix = skb->data[hlen + 3] >> 6; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (test_bit(keyix, common->keymap)) 12328c2ecf20Sopenharmony_ci return RX_FLAG_DECRYPTED; 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci return 0; 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cistatic void 12408c2ecf20Sopenharmony_ciath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb, 12418c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rxs) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci u64 tsf, bc_tstamp; 12448c2ecf20Sopenharmony_ci u32 hw_tu; 12458c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS) { 12488c2ecf20Sopenharmony_ci /* 12498c2ecf20Sopenharmony_ci * Received an IBSS beacon with the same BSSID. Hardware *must* 12508c2ecf20Sopenharmony_ci * have updated the local TSF. We have to work around various 12518c2ecf20Sopenharmony_ci * hardware bugs, though... 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_ci tsf = ath5k_hw_get_tsf64(ah); 12548c2ecf20Sopenharmony_ci bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp); 12558c2ecf20Sopenharmony_ci hw_tu = TSF_TO_TU(tsf); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, 12588c2ecf20Sopenharmony_ci "beacon %llx mactime %llx (diff %lld) tsf now %llx\n", 12598c2ecf20Sopenharmony_ci (unsigned long long)bc_tstamp, 12608c2ecf20Sopenharmony_ci (unsigned long long)rxs->mactime, 12618c2ecf20Sopenharmony_ci (unsigned long long)(rxs->mactime - bc_tstamp), 12628c2ecf20Sopenharmony_ci (unsigned long long)tsf); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci /* 12658c2ecf20Sopenharmony_ci * Sometimes the HW will give us a wrong tstamp in the rx 12668c2ecf20Sopenharmony_ci * status, causing the timestamp extension to go wrong. 12678c2ecf20Sopenharmony_ci * (This seems to happen especially with beacon frames bigger 12688c2ecf20Sopenharmony_ci * than 78 byte (incl. FCS)) 12698c2ecf20Sopenharmony_ci * But we know that the receive timestamp must be later than the 12708c2ecf20Sopenharmony_ci * timestamp of the beacon since HW must have synced to that. 12718c2ecf20Sopenharmony_ci * 12728c2ecf20Sopenharmony_ci * NOTE: here we assume mactime to be after the frame was 12738c2ecf20Sopenharmony_ci * received, not like mac80211 which defines it at the start. 12748c2ecf20Sopenharmony_ci */ 12758c2ecf20Sopenharmony_ci if (bc_tstamp > rxs->mactime) { 12768c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, 12778c2ecf20Sopenharmony_ci "fixing mactime from %llx to %llx\n", 12788c2ecf20Sopenharmony_ci (unsigned long long)rxs->mactime, 12798c2ecf20Sopenharmony_ci (unsigned long long)tsf); 12808c2ecf20Sopenharmony_ci rxs->mactime = tsf; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci /* 12848c2ecf20Sopenharmony_ci * Local TSF might have moved higher than our beacon timers, 12858c2ecf20Sopenharmony_ci * in that case we have to update them to continue sending 12868c2ecf20Sopenharmony_ci * beacons. This also takes care of synchronizing beacon sending 12878c2ecf20Sopenharmony_ci * times with other stations. 12888c2ecf20Sopenharmony_ci */ 12898c2ecf20Sopenharmony_ci if (hw_tu >= ah->nexttbtt) 12908c2ecf20Sopenharmony_ci ath5k_beacon_update_timers(ah, bc_tstamp); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci /* Check if the beacon timers are still correct, because a TSF 12938c2ecf20Sopenharmony_ci * update might have created a window between them - for a 12948c2ecf20Sopenharmony_ci * longer description see the comment of this function: */ 12958c2ecf20Sopenharmony_ci if (!ath5k_hw_check_beacon_timers(ah, ah->bintval)) { 12968c2ecf20Sopenharmony_ci ath5k_beacon_update_timers(ah, bc_tstamp); 12978c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, 12988c2ecf20Sopenharmony_ci "fixed beacon timers after beacon receive\n"); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci/* 13048c2ecf20Sopenharmony_ci * Compute padding position. skb must contain an IEEE 802.11 frame 13058c2ecf20Sopenharmony_ci */ 13068c2ecf20Sopenharmony_cistatic int ath5k_common_padpos(struct sk_buff *skb) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 13098c2ecf20Sopenharmony_ci __le16 frame_control = hdr->frame_control; 13108c2ecf20Sopenharmony_ci int padpos = 24; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (ieee80211_has_a4(frame_control)) 13138c2ecf20Sopenharmony_ci padpos += ETH_ALEN; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (ieee80211_is_data_qos(frame_control)) 13168c2ecf20Sopenharmony_ci padpos += IEEE80211_QOS_CTL_LEN; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci return padpos; 13198c2ecf20Sopenharmony_ci} 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci/* 13228c2ecf20Sopenharmony_ci * This function expects an 802.11 frame and returns the number of 13238c2ecf20Sopenharmony_ci * bytes added, or -1 if we don't have enough header room. 13248c2ecf20Sopenharmony_ci */ 13258c2ecf20Sopenharmony_cistatic int ath5k_add_padding(struct sk_buff *skb) 13268c2ecf20Sopenharmony_ci{ 13278c2ecf20Sopenharmony_ci int padpos = ath5k_common_padpos(skb); 13288c2ecf20Sopenharmony_ci int padsize = padpos & 3; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if (padsize && skb->len > padpos) { 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci if (skb_headroom(skb) < padsize) 13338c2ecf20Sopenharmony_ci return -1; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci skb_push(skb, padsize); 13368c2ecf20Sopenharmony_ci memmove(skb->data, skb->data + padsize, padpos); 13378c2ecf20Sopenharmony_ci return padsize; 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci return 0; 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci/* 13448c2ecf20Sopenharmony_ci * The MAC header is padded to have 32-bit boundary if the 13458c2ecf20Sopenharmony_ci * packet payload is non-zero. The general calculation for 13468c2ecf20Sopenharmony_ci * padsize would take into account odd header lengths: 13478c2ecf20Sopenharmony_ci * padsize = 4 - (hdrlen & 3); however, since only 13488c2ecf20Sopenharmony_ci * even-length headers are used, padding can only be 0 or 2 13498c2ecf20Sopenharmony_ci * bytes and we can optimize this a bit. We must not try to 13508c2ecf20Sopenharmony_ci * remove padding from short control frames that do not have a 13518c2ecf20Sopenharmony_ci * payload. 13528c2ecf20Sopenharmony_ci * 13538c2ecf20Sopenharmony_ci * This function expects an 802.11 frame and returns the number of 13548c2ecf20Sopenharmony_ci * bytes removed. 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_cistatic int ath5k_remove_padding(struct sk_buff *skb) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci int padpos = ath5k_common_padpos(skb); 13598c2ecf20Sopenharmony_ci int padsize = padpos & 3; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (padsize && skb->len >= padpos + padsize) { 13628c2ecf20Sopenharmony_ci memmove(skb->data + padsize, skb->data, padpos); 13638c2ecf20Sopenharmony_ci skb_pull(skb, padsize); 13648c2ecf20Sopenharmony_ci return padsize; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci return 0; 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic void 13718c2ecf20Sopenharmony_ciath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, 13728c2ecf20Sopenharmony_ci struct ath5k_rx_status *rs) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rxs; 13758c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci ath5k_remove_padding(skb); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci rxs = IEEE80211_SKB_RXCB(skb); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci rxs->flag = 0; 13828c2ecf20Sopenharmony_ci if (unlikely(rs->rs_status & AR5K_RXERR_MIC)) 13838c2ecf20Sopenharmony_ci rxs->flag |= RX_FLAG_MMIC_ERROR; 13848c2ecf20Sopenharmony_ci if (unlikely(rs->rs_status & AR5K_RXERR_CRC)) 13858c2ecf20Sopenharmony_ci rxs->flag |= RX_FLAG_FAILED_FCS_CRC; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci /* 13898c2ecf20Sopenharmony_ci * always extend the mac timestamp, since this information is 13908c2ecf20Sopenharmony_ci * also needed for proper IBSS merging. 13918c2ecf20Sopenharmony_ci * 13928c2ecf20Sopenharmony_ci * XXX: it might be too late to do it here, since rs_tstamp is 13938c2ecf20Sopenharmony_ci * 15bit only. that means TSF extension has to be done within 13948c2ecf20Sopenharmony_ci * 32768usec (about 32ms). it might be necessary to move this to 13958c2ecf20Sopenharmony_ci * the interrupt handler, like it is done in madwifi. 13968c2ecf20Sopenharmony_ci */ 13978c2ecf20Sopenharmony_ci rxs->mactime = ath5k_extend_tsf(ah, rs->rs_tstamp); 13988c2ecf20Sopenharmony_ci rxs->flag |= RX_FLAG_MACTIME_END; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci rxs->freq = ah->curchan->center_freq; 14018c2ecf20Sopenharmony_ci rxs->band = ah->curchan->band; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci rxs->signal = ah->ah_noise_floor + rs->rs_rssi; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci rxs->antenna = rs->rs_antenna; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci if (rs->rs_antenna > 0 && rs->rs_antenna < 5) 14088c2ecf20Sopenharmony_ci ah->stats.antenna_rx[rs->rs_antenna]++; 14098c2ecf20Sopenharmony_ci else 14108c2ecf20Sopenharmony_ci ah->stats.antenna_rx[0]++; /* invalid */ 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate); 14138c2ecf20Sopenharmony_ci rxs->flag |= ath5k_rx_decrypted(ah, skb, rs); 14148c2ecf20Sopenharmony_ci switch (ah->ah_bwmode) { 14158c2ecf20Sopenharmony_ci case AR5K_BWMODE_5MHZ: 14168c2ecf20Sopenharmony_ci rxs->bw = RATE_INFO_BW_5; 14178c2ecf20Sopenharmony_ci break; 14188c2ecf20Sopenharmony_ci case AR5K_BWMODE_10MHZ: 14198c2ecf20Sopenharmony_ci rxs->bw = RATE_INFO_BW_10; 14208c2ecf20Sopenharmony_ci break; 14218c2ecf20Sopenharmony_ci default: 14228c2ecf20Sopenharmony_ci break; 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (rs->rs_rate == 14268c2ecf20Sopenharmony_ci ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short) 14278c2ecf20Sopenharmony_ci rxs->enc_flags |= RX_ENC_FLAG_SHORTPRE; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci trace_ath5k_rx(ah, skb); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (ath_is_mybeacon(common, (struct ieee80211_hdr *)skb->data)) { 14328c2ecf20Sopenharmony_ci ewma_beacon_rssi_add(&ah->ah_beacon_rssi_avg, rs->rs_rssi); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* check beacons in IBSS mode */ 14358c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_ADHOC) 14368c2ecf20Sopenharmony_ci ath5k_check_ibss_tsf(ah, skb, rxs); 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci ieee80211_rx(ah->hw, skb); 14408c2ecf20Sopenharmony_ci} 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci/** ath5k_frame_receive_ok() - Do we want to receive this frame or not? 14438c2ecf20Sopenharmony_ci * 14448c2ecf20Sopenharmony_ci * Check if we want to further process this frame or not. Also update 14458c2ecf20Sopenharmony_ci * statistics. Return true if we want this frame, false if not. 14468c2ecf20Sopenharmony_ci */ 14478c2ecf20Sopenharmony_cistatic bool 14488c2ecf20Sopenharmony_ciath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs) 14498c2ecf20Sopenharmony_ci{ 14508c2ecf20Sopenharmony_ci ah->stats.rx_all_count++; 14518c2ecf20Sopenharmony_ci ah->stats.rx_bytes_count += rs->rs_datalen; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (unlikely(rs->rs_status)) { 14548c2ecf20Sopenharmony_ci unsigned int filters; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (rs->rs_status & AR5K_RXERR_CRC) 14578c2ecf20Sopenharmony_ci ah->stats.rxerr_crc++; 14588c2ecf20Sopenharmony_ci if (rs->rs_status & AR5K_RXERR_FIFO) 14598c2ecf20Sopenharmony_ci ah->stats.rxerr_fifo++; 14608c2ecf20Sopenharmony_ci if (rs->rs_status & AR5K_RXERR_PHY) { 14618c2ecf20Sopenharmony_ci ah->stats.rxerr_phy++; 14628c2ecf20Sopenharmony_ci if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32) 14638c2ecf20Sopenharmony_ci ah->stats.rxerr_phy_code[rs->rs_phyerr]++; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci /* 14668c2ecf20Sopenharmony_ci * Treat packets that underwent a CCK or OFDM reset as having a bad CRC. 14678c2ecf20Sopenharmony_ci * These restarts happen when the radio resynchronizes to a stronger frame 14688c2ecf20Sopenharmony_ci * while receiving a weaker frame. Here we receive the prefix of the weak 14698c2ecf20Sopenharmony_ci * frame. Since these are incomplete packets, mark their CRC as invalid. 14708c2ecf20Sopenharmony_ci */ 14718c2ecf20Sopenharmony_ci if (rs->rs_phyerr == AR5K_RX_PHY_ERROR_OFDM_RESTART || 14728c2ecf20Sopenharmony_ci rs->rs_phyerr == AR5K_RX_PHY_ERROR_CCK_RESTART) { 14738c2ecf20Sopenharmony_ci rs->rs_status |= AR5K_RXERR_CRC; 14748c2ecf20Sopenharmony_ci rs->rs_status &= ~AR5K_RXERR_PHY; 14758c2ecf20Sopenharmony_ci } else { 14768c2ecf20Sopenharmony_ci return false; 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci if (rs->rs_status & AR5K_RXERR_DECRYPT) { 14808c2ecf20Sopenharmony_ci /* 14818c2ecf20Sopenharmony_ci * Decrypt error. If the error occurred 14828c2ecf20Sopenharmony_ci * because there was no hardware key, then 14838c2ecf20Sopenharmony_ci * let the frame through so the upper layers 14848c2ecf20Sopenharmony_ci * can process it. This is necessary for 5210 14858c2ecf20Sopenharmony_ci * parts which have no way to setup a ``clear'' 14868c2ecf20Sopenharmony_ci * key cache entry. 14878c2ecf20Sopenharmony_ci * 14888c2ecf20Sopenharmony_ci * XXX do key cache faulting 14898c2ecf20Sopenharmony_ci */ 14908c2ecf20Sopenharmony_ci ah->stats.rxerr_decrypt++; 14918c2ecf20Sopenharmony_ci if (rs->rs_keyix == AR5K_RXKEYIX_INVALID && 14928c2ecf20Sopenharmony_ci !(rs->rs_status & AR5K_RXERR_CRC)) 14938c2ecf20Sopenharmony_ci return true; 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci if (rs->rs_status & AR5K_RXERR_MIC) { 14968c2ecf20Sopenharmony_ci ah->stats.rxerr_mic++; 14978c2ecf20Sopenharmony_ci return true; 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci /* 15018c2ecf20Sopenharmony_ci * Reject any frames with non-crypto errors, and take into account the 15028c2ecf20Sopenharmony_ci * current FIF_* filters. 15038c2ecf20Sopenharmony_ci */ 15048c2ecf20Sopenharmony_ci filters = AR5K_RXERR_DECRYPT; 15058c2ecf20Sopenharmony_ci if (ah->fif_filter_flags & FIF_FCSFAIL) 15068c2ecf20Sopenharmony_ci filters |= AR5K_RXERR_CRC; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (rs->rs_status & ~filters) 15098c2ecf20Sopenharmony_ci return false; 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (unlikely(rs->rs_more)) { 15138c2ecf20Sopenharmony_ci ah->stats.rxerr_jumbo++; 15148c2ecf20Sopenharmony_ci return false; 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci return true; 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_cistatic void 15208c2ecf20Sopenharmony_ciath5k_set_current_imask(struct ath5k_hw *ah) 15218c2ecf20Sopenharmony_ci{ 15228c2ecf20Sopenharmony_ci enum ath5k_int imask; 15238c2ecf20Sopenharmony_ci unsigned long flags; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci if (test_bit(ATH_STAT_RESET, ah->status)) 15268c2ecf20Sopenharmony_ci return; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci spin_lock_irqsave(&ah->irqlock, flags); 15298c2ecf20Sopenharmony_ci imask = ah->imask; 15308c2ecf20Sopenharmony_ci if (ah->rx_pending) 15318c2ecf20Sopenharmony_ci imask &= ~AR5K_INT_RX_ALL; 15328c2ecf20Sopenharmony_ci if (ah->tx_pending) 15338c2ecf20Sopenharmony_ci imask &= ~AR5K_INT_TX_ALL; 15348c2ecf20Sopenharmony_ci ath5k_hw_set_imr(ah, imask); 15358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ah->irqlock, flags); 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic void 15398c2ecf20Sopenharmony_ciath5k_tasklet_rx(struct tasklet_struct *t) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci struct ath5k_rx_status rs = {}; 15428c2ecf20Sopenharmony_ci struct sk_buff *skb, *next_skb; 15438c2ecf20Sopenharmony_ci dma_addr_t next_skb_addr; 15448c2ecf20Sopenharmony_ci struct ath5k_hw *ah = from_tasklet(ah, t, rxtq); 15458c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 15468c2ecf20Sopenharmony_ci struct ath5k_buf *bf; 15478c2ecf20Sopenharmony_ci struct ath5k_desc *ds; 15488c2ecf20Sopenharmony_ci int ret; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci spin_lock(&ah->rxbuflock); 15518c2ecf20Sopenharmony_ci if (list_empty(&ah->rxbuf)) { 15528c2ecf20Sopenharmony_ci ATH5K_WARN(ah, "empty rx buf pool\n"); 15538c2ecf20Sopenharmony_ci goto unlock; 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci do { 15568c2ecf20Sopenharmony_ci bf = list_first_entry(&ah->rxbuf, struct ath5k_buf, list); 15578c2ecf20Sopenharmony_ci BUG_ON(bf->skb == NULL); 15588c2ecf20Sopenharmony_ci skb = bf->skb; 15598c2ecf20Sopenharmony_ci ds = bf->desc; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci /* bail if HW is still using self-linked descriptor */ 15628c2ecf20Sopenharmony_ci if (ath5k_hw_get_rxdp(ah) == bf->daddr) 15638c2ecf20Sopenharmony_ci break; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci ret = ah->ah_proc_rx_desc(ah, ds, &rs); 15668c2ecf20Sopenharmony_ci if (unlikely(ret == -EINPROGRESS)) 15678c2ecf20Sopenharmony_ci break; 15688c2ecf20Sopenharmony_ci else if (unlikely(ret)) { 15698c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "error in processing rx descriptor\n"); 15708c2ecf20Sopenharmony_ci ah->stats.rxerr_proc++; 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (ath5k_receive_frame_ok(ah, &rs)) { 15758c2ecf20Sopenharmony_ci next_skb = ath5k_rx_skb_alloc(ah, &next_skb_addr); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci /* 15788c2ecf20Sopenharmony_ci * If we can't replace bf->skb with a new skb under 15798c2ecf20Sopenharmony_ci * memory pressure, just skip this packet 15808c2ecf20Sopenharmony_ci */ 15818c2ecf20Sopenharmony_ci if (!next_skb) 15828c2ecf20Sopenharmony_ci goto next; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci dma_unmap_single(ah->dev, bf->skbaddr, 15858c2ecf20Sopenharmony_ci common->rx_bufsize, 15868c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci skb_put(skb, rs.rs_datalen); 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci ath5k_receive_frame(ah, skb, &rs); 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci bf->skb = next_skb; 15938c2ecf20Sopenharmony_ci bf->skbaddr = next_skb_addr; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_cinext: 15968c2ecf20Sopenharmony_ci list_move_tail(&bf->list, &ah->rxbuf); 15978c2ecf20Sopenharmony_ci } while (ath5k_rxbuf_setup(ah, bf) == 0); 15988c2ecf20Sopenharmony_ciunlock: 15998c2ecf20Sopenharmony_ci spin_unlock(&ah->rxbuflock); 16008c2ecf20Sopenharmony_ci ah->rx_pending = false; 16018c2ecf20Sopenharmony_ci ath5k_set_current_imask(ah); 16028c2ecf20Sopenharmony_ci} 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci/*************\ 16068c2ecf20Sopenharmony_ci* TX Handling * 16078c2ecf20Sopenharmony_ci\*************/ 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_civoid 16108c2ecf20Sopenharmony_ciath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, 16118c2ecf20Sopenharmony_ci struct ath5k_txq *txq, struct ieee80211_tx_control *control) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci struct ath5k_hw *ah = hw->priv; 16148c2ecf20Sopenharmony_ci struct ath5k_buf *bf; 16158c2ecf20Sopenharmony_ci unsigned long flags; 16168c2ecf20Sopenharmony_ci int padsize; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci trace_ath5k_tx(ah, skb, txq); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci /* 16218c2ecf20Sopenharmony_ci * The hardware expects the header padded to 4 byte boundaries. 16228c2ecf20Sopenharmony_ci * If this is not the case, we add the padding after the header. 16238c2ecf20Sopenharmony_ci */ 16248c2ecf20Sopenharmony_ci padsize = ath5k_add_padding(skb); 16258c2ecf20Sopenharmony_ci if (padsize < 0) { 16268c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "tx hdrlen not %%4: not enough" 16278c2ecf20Sopenharmony_ci " headroom to pad"); 16288c2ecf20Sopenharmony_ci goto drop_packet; 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (txq->txq_len >= txq->txq_max && 16328c2ecf20Sopenharmony_ci txq->qnum <= AR5K_TX_QUEUE_ID_DATA_MAX) 16338c2ecf20Sopenharmony_ci ieee80211_stop_queue(hw, txq->qnum); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci spin_lock_irqsave(&ah->txbuflock, flags); 16368c2ecf20Sopenharmony_ci if (list_empty(&ah->txbuf)) { 16378c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "no further txbuf available, dropping packet\n"); 16388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ah->txbuflock, flags); 16398c2ecf20Sopenharmony_ci ieee80211_stop_queues(hw); 16408c2ecf20Sopenharmony_ci goto drop_packet; 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci bf = list_first_entry(&ah->txbuf, struct ath5k_buf, list); 16438c2ecf20Sopenharmony_ci list_del(&bf->list); 16448c2ecf20Sopenharmony_ci ah->txbuf_len--; 16458c2ecf20Sopenharmony_ci if (list_empty(&ah->txbuf)) 16468c2ecf20Sopenharmony_ci ieee80211_stop_queues(hw); 16478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ah->txbuflock, flags); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci bf->skb = skb; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (ath5k_txbuf_setup(ah, bf, txq, padsize, control)) { 16528c2ecf20Sopenharmony_ci bf->skb = NULL; 16538c2ecf20Sopenharmony_ci spin_lock_irqsave(&ah->txbuflock, flags); 16548c2ecf20Sopenharmony_ci list_add_tail(&bf->list, &ah->txbuf); 16558c2ecf20Sopenharmony_ci ah->txbuf_len++; 16568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ah->txbuflock, flags); 16578c2ecf20Sopenharmony_ci goto drop_packet; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci return; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cidrop_packet: 16628c2ecf20Sopenharmony_ci ieee80211_free_txskb(hw, skb); 16638c2ecf20Sopenharmony_ci} 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic void 16668c2ecf20Sopenharmony_ciath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, 16678c2ecf20Sopenharmony_ci struct ath5k_txq *txq, struct ath5k_tx_status *ts, 16688c2ecf20Sopenharmony_ci struct ath5k_buf *bf) 16698c2ecf20Sopenharmony_ci{ 16708c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 16718c2ecf20Sopenharmony_ci u8 tries[3]; 16728c2ecf20Sopenharmony_ci int i; 16738c2ecf20Sopenharmony_ci int size = 0; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci ah->stats.tx_all_count++; 16768c2ecf20Sopenharmony_ci ah->stats.tx_bytes_count += skb->len; 16778c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); 16808c2ecf20Sopenharmony_ci memcpy(info->status.rates, bf->rates, size); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci tries[0] = info->status.rates[0].count; 16838c2ecf20Sopenharmony_ci tries[1] = info->status.rates[1].count; 16848c2ecf20Sopenharmony_ci tries[2] = info->status.rates[2].count; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci ieee80211_tx_info_clear_status(info); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci for (i = 0; i < ts->ts_final_idx; i++) { 16898c2ecf20Sopenharmony_ci struct ieee80211_tx_rate *r = 16908c2ecf20Sopenharmony_ci &info->status.rates[i]; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci r->count = tries[i]; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci info->status.rates[ts->ts_final_idx].count = ts->ts_final_retry; 16968c2ecf20Sopenharmony_ci info->status.rates[ts->ts_final_idx + 1].idx = -1; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (unlikely(ts->ts_status)) { 16998c2ecf20Sopenharmony_ci ah->stats.ack_fail++; 17008c2ecf20Sopenharmony_ci if (ts->ts_status & AR5K_TXERR_FILT) { 17018c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_TX_FILTERED; 17028c2ecf20Sopenharmony_ci ah->stats.txerr_filt++; 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci if (ts->ts_status & AR5K_TXERR_XRETRY) 17058c2ecf20Sopenharmony_ci ah->stats.txerr_retry++; 17068c2ecf20Sopenharmony_ci if (ts->ts_status & AR5K_TXERR_FIFO) 17078c2ecf20Sopenharmony_ci ah->stats.txerr_fifo++; 17088c2ecf20Sopenharmony_ci } else { 17098c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 17108c2ecf20Sopenharmony_ci info->status.ack_signal = ts->ts_rssi; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci /* count the successful attempt as well */ 17138c2ecf20Sopenharmony_ci info->status.rates[ts->ts_final_idx].count++; 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci /* 17178c2ecf20Sopenharmony_ci * Remove MAC header padding before giving the frame 17188c2ecf20Sopenharmony_ci * back to mac80211. 17198c2ecf20Sopenharmony_ci */ 17208c2ecf20Sopenharmony_ci ath5k_remove_padding(skb); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci if (ts->ts_antenna > 0 && ts->ts_antenna < 5) 17238c2ecf20Sopenharmony_ci ah->stats.antenna_tx[ts->ts_antenna]++; 17248c2ecf20Sopenharmony_ci else 17258c2ecf20Sopenharmony_ci ah->stats.antenna_tx[0]++; /* invalid */ 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci trace_ath5k_tx_complete(ah, skb, txq, ts); 17288c2ecf20Sopenharmony_ci ieee80211_tx_status(ah->hw, skb); 17298c2ecf20Sopenharmony_ci} 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_cistatic void 17328c2ecf20Sopenharmony_ciath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq) 17338c2ecf20Sopenharmony_ci{ 17348c2ecf20Sopenharmony_ci struct ath5k_tx_status ts = {}; 17358c2ecf20Sopenharmony_ci struct ath5k_buf *bf, *bf0; 17368c2ecf20Sopenharmony_ci struct ath5k_desc *ds; 17378c2ecf20Sopenharmony_ci struct sk_buff *skb; 17388c2ecf20Sopenharmony_ci int ret; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci spin_lock(&txq->lock); 17418c2ecf20Sopenharmony_ci list_for_each_entry_safe(bf, bf0, &txq->q, list) { 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci txq->txq_poll_mark = false; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci /* skb might already have been processed last time. */ 17468c2ecf20Sopenharmony_ci if (bf->skb != NULL) { 17478c2ecf20Sopenharmony_ci ds = bf->desc; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci ret = ah->ah_proc_tx_desc(ah, ds, &ts); 17508c2ecf20Sopenharmony_ci if (unlikely(ret == -EINPROGRESS)) 17518c2ecf20Sopenharmony_ci break; 17528c2ecf20Sopenharmony_ci else if (unlikely(ret)) { 17538c2ecf20Sopenharmony_ci ATH5K_ERR(ah, 17548c2ecf20Sopenharmony_ci "error %d while processing " 17558c2ecf20Sopenharmony_ci "queue %u\n", ret, txq->qnum); 17568c2ecf20Sopenharmony_ci break; 17578c2ecf20Sopenharmony_ci } 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci skb = bf->skb; 17608c2ecf20Sopenharmony_ci bf->skb = NULL; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci dma_unmap_single(ah->dev, bf->skbaddr, skb->len, 17638c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 17648c2ecf20Sopenharmony_ci ath5k_tx_frame_completed(ah, skb, txq, &ts, bf); 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci /* 17688c2ecf20Sopenharmony_ci * It's possible that the hardware can say the buffer is 17698c2ecf20Sopenharmony_ci * completed when it hasn't yet loaded the ds_link from 17708c2ecf20Sopenharmony_ci * host memory and moved on. 17718c2ecf20Sopenharmony_ci * Always keep the last descriptor to avoid HW races... 17728c2ecf20Sopenharmony_ci */ 17738c2ecf20Sopenharmony_ci if (ath5k_hw_get_txdp(ah, txq->qnum) != bf->daddr) { 17748c2ecf20Sopenharmony_ci spin_lock(&ah->txbuflock); 17758c2ecf20Sopenharmony_ci list_move_tail(&bf->list, &ah->txbuf); 17768c2ecf20Sopenharmony_ci ah->txbuf_len++; 17778c2ecf20Sopenharmony_ci txq->txq_len--; 17788c2ecf20Sopenharmony_ci spin_unlock(&ah->txbuflock); 17798c2ecf20Sopenharmony_ci } 17808c2ecf20Sopenharmony_ci } 17818c2ecf20Sopenharmony_ci spin_unlock(&txq->lock); 17828c2ecf20Sopenharmony_ci if (txq->txq_len < ATH5K_TXQ_LEN_LOW && txq->qnum < 4) 17838c2ecf20Sopenharmony_ci ieee80211_wake_queue(ah->hw, txq->qnum); 17848c2ecf20Sopenharmony_ci} 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_cistatic void 17878c2ecf20Sopenharmony_ciath5k_tasklet_tx(struct tasklet_struct *t) 17888c2ecf20Sopenharmony_ci{ 17898c2ecf20Sopenharmony_ci int i; 17908c2ecf20Sopenharmony_ci struct ath5k_hw *ah = from_tasklet(ah, t, txtq); 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci for (i = 0; i < AR5K_NUM_TX_QUEUES; i++) 17938c2ecf20Sopenharmony_ci if (ah->txqs[i].setup && (ah->ah_txq_isr_txok_all & BIT(i))) 17948c2ecf20Sopenharmony_ci ath5k_tx_processq(ah, &ah->txqs[i]); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci ah->tx_pending = false; 17978c2ecf20Sopenharmony_ci ath5k_set_current_imask(ah); 17988c2ecf20Sopenharmony_ci} 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci/*****************\ 18028c2ecf20Sopenharmony_ci* Beacon handling * 18038c2ecf20Sopenharmony_ci\*****************/ 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci/* 18068c2ecf20Sopenharmony_ci * Setup the beacon frame for transmit. 18078c2ecf20Sopenharmony_ci */ 18088c2ecf20Sopenharmony_cistatic int 18098c2ecf20Sopenharmony_ciath5k_beacon_setup(struct ath5k_hw *ah, struct ath5k_buf *bf) 18108c2ecf20Sopenharmony_ci{ 18118c2ecf20Sopenharmony_ci struct sk_buff *skb = bf->skb; 18128c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 18138c2ecf20Sopenharmony_ci struct ath5k_desc *ds; 18148c2ecf20Sopenharmony_ci int ret = 0; 18158c2ecf20Sopenharmony_ci u8 antenna; 18168c2ecf20Sopenharmony_ci u32 flags; 18178c2ecf20Sopenharmony_ci const int padsize = 0; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len, 18208c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 18218c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] " 18228c2ecf20Sopenharmony_ci "skbaddr %llx\n", skb, skb->data, skb->len, 18238c2ecf20Sopenharmony_ci (unsigned long long)bf->skbaddr); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci if (dma_mapping_error(ah->dev, bf->skbaddr)) { 18268c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "beacon DMA mapping failed\n"); 18278c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 18288c2ecf20Sopenharmony_ci bf->skb = NULL; 18298c2ecf20Sopenharmony_ci return -EIO; 18308c2ecf20Sopenharmony_ci } 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci ds = bf->desc; 18338c2ecf20Sopenharmony_ci antenna = ah->ah_tx_ant; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci flags = AR5K_TXDESC_NOACK; 18368c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) { 18378c2ecf20Sopenharmony_ci ds->ds_link = bf->daddr; /* self-linked */ 18388c2ecf20Sopenharmony_ci flags |= AR5K_TXDESC_VEOL; 18398c2ecf20Sopenharmony_ci } else 18408c2ecf20Sopenharmony_ci ds->ds_link = 0; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci /* 18438c2ecf20Sopenharmony_ci * If we use multiple antennas on AP and use 18448c2ecf20Sopenharmony_ci * the Sectored AP scenario, switch antenna every 18458c2ecf20Sopenharmony_ci * 4 beacons to make sure everybody hears our AP. 18468c2ecf20Sopenharmony_ci * When a client tries to associate, hw will keep 18478c2ecf20Sopenharmony_ci * track of the tx antenna to be used for this client 18488c2ecf20Sopenharmony_ci * automatically, based on ACKed packets. 18498c2ecf20Sopenharmony_ci * 18508c2ecf20Sopenharmony_ci * Note: AP still listens and transmits RTS on the 18518c2ecf20Sopenharmony_ci * default antenna which is supposed to be an omni. 18528c2ecf20Sopenharmony_ci * 18538c2ecf20Sopenharmony_ci * Note2: On sectored scenarios it's possible to have 18548c2ecf20Sopenharmony_ci * multiple antennas (1 omni -- the default -- and 14 18558c2ecf20Sopenharmony_ci * sectors), so if we choose to actually support this 18568c2ecf20Sopenharmony_ci * mode, we need to allow the user to set how many antennas 18578c2ecf20Sopenharmony_ci * we have and tweak the code below to send beacons 18588c2ecf20Sopenharmony_ci * on all of them. 18598c2ecf20Sopenharmony_ci */ 18608c2ecf20Sopenharmony_ci if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP) 18618c2ecf20Sopenharmony_ci antenna = ah->bsent & 4 ? 2 : 1; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci /* FIXME: If we are in g mode and rate is a CCK rate 18658c2ecf20Sopenharmony_ci * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta 18668c2ecf20Sopenharmony_ci * from tx power (value is in dB units already) */ 18678c2ecf20Sopenharmony_ci ds->ds_data = bf->skbaddr; 18688c2ecf20Sopenharmony_ci ret = ah->ah_setup_tx_desc(ah, ds, skb->len, 18698c2ecf20Sopenharmony_ci ieee80211_get_hdrlen_from_skb(skb), padsize, 18708c2ecf20Sopenharmony_ci AR5K_PKT_TYPE_BEACON, 18718c2ecf20Sopenharmony_ci (ah->ah_txpower.txp_requested * 2), 18728c2ecf20Sopenharmony_ci ieee80211_get_tx_rate(ah->hw, info)->hw_value, 18738c2ecf20Sopenharmony_ci 1, AR5K_TXKEYIX_INVALID, 18748c2ecf20Sopenharmony_ci antenna, flags, 0, 0); 18758c2ecf20Sopenharmony_ci if (ret) 18768c2ecf20Sopenharmony_ci goto err_unmap; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci return 0; 18798c2ecf20Sopenharmony_cierr_unmap: 18808c2ecf20Sopenharmony_ci dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE); 18818c2ecf20Sopenharmony_ci return ret; 18828c2ecf20Sopenharmony_ci} 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci/* 18858c2ecf20Sopenharmony_ci * Updates the beacon that is sent by ath5k_beacon_send. For adhoc, 18868c2ecf20Sopenharmony_ci * this is called only once at config_bss time, for AP we do it every 18878c2ecf20Sopenharmony_ci * SWBA interrupt so that the TIM will reflect buffered frames. 18888c2ecf20Sopenharmony_ci * 18898c2ecf20Sopenharmony_ci * Called with the beacon lock. 18908c2ecf20Sopenharmony_ci */ 18918c2ecf20Sopenharmony_ciint 18928c2ecf20Sopenharmony_ciath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 18938c2ecf20Sopenharmony_ci{ 18948c2ecf20Sopenharmony_ci int ret; 18958c2ecf20Sopenharmony_ci struct ath5k_hw *ah = hw->priv; 18968c2ecf20Sopenharmony_ci struct ath5k_vif *avf; 18978c2ecf20Sopenharmony_ci struct sk_buff *skb; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci if (WARN_ON(!vif)) { 19008c2ecf20Sopenharmony_ci ret = -EINVAL; 19018c2ecf20Sopenharmony_ci goto out; 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci skb = ieee80211_beacon_get(hw, vif); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci if (!skb) { 19078c2ecf20Sopenharmony_ci ret = -ENOMEM; 19088c2ecf20Sopenharmony_ci goto out; 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci avf = (void *)vif->drv_priv; 19128c2ecf20Sopenharmony_ci ath5k_txbuf_free_skb(ah, avf->bbuf); 19138c2ecf20Sopenharmony_ci avf->bbuf->skb = skb; 19148c2ecf20Sopenharmony_ci ret = ath5k_beacon_setup(ah, avf->bbuf); 19158c2ecf20Sopenharmony_ciout: 19168c2ecf20Sopenharmony_ci return ret; 19178c2ecf20Sopenharmony_ci} 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci/* 19208c2ecf20Sopenharmony_ci * Transmit a beacon frame at SWBA. Dynamic updates to the 19218c2ecf20Sopenharmony_ci * frame contents are done as needed and the slot time is 19228c2ecf20Sopenharmony_ci * also adjusted based on current state. 19238c2ecf20Sopenharmony_ci * 19248c2ecf20Sopenharmony_ci * This is called from software irq context (beacontq tasklets) 19258c2ecf20Sopenharmony_ci * or user context from ath5k_beacon_config. 19268c2ecf20Sopenharmony_ci */ 19278c2ecf20Sopenharmony_cistatic void 19288c2ecf20Sopenharmony_ciath5k_beacon_send(struct ath5k_hw *ah) 19298c2ecf20Sopenharmony_ci{ 19308c2ecf20Sopenharmony_ci struct ieee80211_vif *vif; 19318c2ecf20Sopenharmony_ci struct ath5k_vif *avf; 19328c2ecf20Sopenharmony_ci struct ath5k_buf *bf; 19338c2ecf20Sopenharmony_ci struct sk_buff *skb; 19348c2ecf20Sopenharmony_ci int err; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "in beacon_send\n"); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci /* 19398c2ecf20Sopenharmony_ci * Check if the previous beacon has gone out. If 19408c2ecf20Sopenharmony_ci * not, don't don't try to post another: skip this 19418c2ecf20Sopenharmony_ci * period and wait for the next. Missed beacons 19428c2ecf20Sopenharmony_ci * indicate a problem and should not occur. If we 19438c2ecf20Sopenharmony_ci * miss too many consecutive beacons reset the device. 19448c2ecf20Sopenharmony_ci */ 19458c2ecf20Sopenharmony_ci if (unlikely(ath5k_hw_num_tx_pending(ah, ah->bhalq) != 0)) { 19468c2ecf20Sopenharmony_ci ah->bmisscount++; 19478c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, 19488c2ecf20Sopenharmony_ci "missed %u consecutive beacons\n", ah->bmisscount); 19498c2ecf20Sopenharmony_ci if (ah->bmisscount > 10) { /* NB: 10 is a guess */ 19508c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, 19518c2ecf20Sopenharmony_ci "stuck beacon time (%u missed)\n", 19528c2ecf20Sopenharmony_ci ah->bmisscount); 19538c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, 19548c2ecf20Sopenharmony_ci "stuck beacon, resetting\n"); 19558c2ecf20Sopenharmony_ci ieee80211_queue_work(ah->hw, &ah->reset_work); 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci return; 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci if (unlikely(ah->bmisscount != 0)) { 19608c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, 19618c2ecf20Sopenharmony_ci "resume beacon xmit after %u misses\n", 19628c2ecf20Sopenharmony_ci ah->bmisscount); 19638c2ecf20Sopenharmony_ci ah->bmisscount = 0; 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs + 19678c2ecf20Sopenharmony_ci ah->num_mesh_vifs > 1) || 19688c2ecf20Sopenharmony_ci ah->opmode == NL80211_IFTYPE_MESH_POINT) { 19698c2ecf20Sopenharmony_ci u64 tsf = ath5k_hw_get_tsf64(ah); 19708c2ecf20Sopenharmony_ci u32 tsftu = TSF_TO_TU(tsf); 19718c2ecf20Sopenharmony_ci int slot = ((tsftu % ah->bintval) * ATH_BCBUF) / ah->bintval; 19728c2ecf20Sopenharmony_ci vif = ah->bslot[(slot + 1) % ATH_BCBUF]; 19738c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, 19748c2ecf20Sopenharmony_ci "tsf %llx tsftu %x intval %u slot %u vif %p\n", 19758c2ecf20Sopenharmony_ci (unsigned long long)tsf, tsftu, ah->bintval, slot, vif); 19768c2ecf20Sopenharmony_ci } else /* only one interface */ 19778c2ecf20Sopenharmony_ci vif = ah->bslot[0]; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci if (!vif) 19808c2ecf20Sopenharmony_ci return; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci avf = (void *)vif->drv_priv; 19838c2ecf20Sopenharmony_ci bf = avf->bbuf; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* 19868c2ecf20Sopenharmony_ci * Stop any current dma and put the new frame on the queue. 19878c2ecf20Sopenharmony_ci * This should never fail since we check above that no frames 19888c2ecf20Sopenharmony_ci * are still pending on the queue. 19898c2ecf20Sopenharmony_ci */ 19908c2ecf20Sopenharmony_ci if (unlikely(ath5k_hw_stop_beacon_queue(ah, ah->bhalq))) { 19918c2ecf20Sopenharmony_ci ATH5K_WARN(ah, "beacon queue %u didn't start/stop ?\n", ah->bhalq); 19928c2ecf20Sopenharmony_ci /* NB: hw still stops DMA, so proceed */ 19938c2ecf20Sopenharmony_ci } 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci /* refresh the beacon for AP or MESH mode */ 19968c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP || 19978c2ecf20Sopenharmony_ci ah->opmode == NL80211_IFTYPE_MESH_POINT) { 19988c2ecf20Sopenharmony_ci err = ath5k_beacon_update(ah->hw, vif); 19998c2ecf20Sopenharmony_ci if (err) 20008c2ecf20Sopenharmony_ci return; 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci if (unlikely(bf->skb == NULL || ah->opmode == NL80211_IFTYPE_STATION || 20048c2ecf20Sopenharmony_ci ah->opmode == NL80211_IFTYPE_MONITOR)) { 20058c2ecf20Sopenharmony_ci ATH5K_WARN(ah, "bf=%p bf_skb=%p\n", bf, bf->skb); 20068c2ecf20Sopenharmony_ci return; 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci trace_ath5k_tx(ah, bf->skb, &ah->txqs[ah->bhalq]); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci ath5k_hw_set_txdp(ah, ah->bhalq, bf->daddr); 20128c2ecf20Sopenharmony_ci ath5k_hw_start_tx_dma(ah, ah->bhalq); 20138c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n", 20148c2ecf20Sopenharmony_ci ah->bhalq, (unsigned long long)bf->daddr, bf->desc); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci skb = ieee80211_get_buffered_bc(ah->hw, vif); 20178c2ecf20Sopenharmony_ci while (skb) { 20188c2ecf20Sopenharmony_ci ath5k_tx_queue(ah->hw, skb, ah->cabq, NULL); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci if (ah->cabq->txq_len >= ah->cabq->txq_max) 20218c2ecf20Sopenharmony_ci break; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci skb = ieee80211_get_buffered_bc(ah->hw, vif); 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci ah->bsent++; 20278c2ecf20Sopenharmony_ci} 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci/** 20308c2ecf20Sopenharmony_ci * ath5k_beacon_update_timers - update beacon timers 20318c2ecf20Sopenharmony_ci * 20328c2ecf20Sopenharmony_ci * @ah: struct ath5k_hw pointer we are operating on 20338c2ecf20Sopenharmony_ci * @bc_tsf: the timestamp of the beacon. 0 to reset the TSF. -1 to perform a 20348c2ecf20Sopenharmony_ci * beacon timer update based on the current HW TSF. 20358c2ecf20Sopenharmony_ci * 20368c2ecf20Sopenharmony_ci * Calculate the next target beacon transmit time (TBTT) based on the timestamp 20378c2ecf20Sopenharmony_ci * of a received beacon or the current local hardware TSF and write it to the 20388c2ecf20Sopenharmony_ci * beacon timer registers. 20398c2ecf20Sopenharmony_ci * 20408c2ecf20Sopenharmony_ci * This is called in a variety of situations, e.g. when a beacon is received, 20418c2ecf20Sopenharmony_ci * when a TSF update has been detected, but also when an new IBSS is created or 20428c2ecf20Sopenharmony_ci * when we otherwise know we have to update the timers, but we keep it in this 20438c2ecf20Sopenharmony_ci * function to have it all together in one place. 20448c2ecf20Sopenharmony_ci */ 20458c2ecf20Sopenharmony_civoid 20468c2ecf20Sopenharmony_ciath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf) 20478c2ecf20Sopenharmony_ci{ 20488c2ecf20Sopenharmony_ci u32 nexttbtt, intval, hw_tu, bc_tu; 20498c2ecf20Sopenharmony_ci u64 hw_tsf; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci intval = ah->bintval & AR5K_BEACON_PERIOD; 20528c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs 20538c2ecf20Sopenharmony_ci + ah->num_mesh_vifs > 1) { 20548c2ecf20Sopenharmony_ci intval /= ATH_BCBUF; /* staggered multi-bss beacons */ 20558c2ecf20Sopenharmony_ci if (intval < 15) 20568c2ecf20Sopenharmony_ci ATH5K_WARN(ah, "intval %u is too low, min 15\n", 20578c2ecf20Sopenharmony_ci intval); 20588c2ecf20Sopenharmony_ci } 20598c2ecf20Sopenharmony_ci if (WARN_ON(!intval)) 20608c2ecf20Sopenharmony_ci return; 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci /* beacon TSF converted to TU */ 20638c2ecf20Sopenharmony_ci bc_tu = TSF_TO_TU(bc_tsf); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci /* current TSF converted to TU */ 20668c2ecf20Sopenharmony_ci hw_tsf = ath5k_hw_get_tsf64(ah); 20678c2ecf20Sopenharmony_ci hw_tu = TSF_TO_TU(hw_tsf); 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci#define FUDGE (AR5K_TUNE_SW_BEACON_RESP + 3) 20708c2ecf20Sopenharmony_ci /* We use FUDGE to make sure the next TBTT is ahead of the current TU. 20718c2ecf20Sopenharmony_ci * Since we later subtract AR5K_TUNE_SW_BEACON_RESP (10) in the timer 20728c2ecf20Sopenharmony_ci * configuration we need to make sure it is bigger than that. */ 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci if (bc_tsf == -1) { 20758c2ecf20Sopenharmony_ci /* 20768c2ecf20Sopenharmony_ci * no beacons received, called internally. 20778c2ecf20Sopenharmony_ci * just need to refresh timers based on HW TSF. 20788c2ecf20Sopenharmony_ci */ 20798c2ecf20Sopenharmony_ci nexttbtt = roundup(hw_tu + FUDGE, intval); 20808c2ecf20Sopenharmony_ci } else if (bc_tsf == 0) { 20818c2ecf20Sopenharmony_ci /* 20828c2ecf20Sopenharmony_ci * no beacon received, probably called by ath5k_reset_tsf(). 20838c2ecf20Sopenharmony_ci * reset TSF to start with 0. 20848c2ecf20Sopenharmony_ci */ 20858c2ecf20Sopenharmony_ci nexttbtt = intval; 20868c2ecf20Sopenharmony_ci intval |= AR5K_BEACON_RESET_TSF; 20878c2ecf20Sopenharmony_ci } else if (bc_tsf > hw_tsf) { 20888c2ecf20Sopenharmony_ci /* 20898c2ecf20Sopenharmony_ci * beacon received, SW merge happened but HW TSF not yet updated. 20908c2ecf20Sopenharmony_ci * not possible to reconfigure timers yet, but next time we 20918c2ecf20Sopenharmony_ci * receive a beacon with the same BSSID, the hardware will 20928c2ecf20Sopenharmony_ci * automatically update the TSF and then we need to reconfigure 20938c2ecf20Sopenharmony_ci * the timers. 20948c2ecf20Sopenharmony_ci */ 20958c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, 20968c2ecf20Sopenharmony_ci "need to wait for HW TSF sync\n"); 20978c2ecf20Sopenharmony_ci return; 20988c2ecf20Sopenharmony_ci } else { 20998c2ecf20Sopenharmony_ci /* 21008c2ecf20Sopenharmony_ci * most important case for beacon synchronization between STA. 21018c2ecf20Sopenharmony_ci * 21028c2ecf20Sopenharmony_ci * beacon received and HW TSF has been already updated by HW. 21038c2ecf20Sopenharmony_ci * update next TBTT based on the TSF of the beacon, but make 21048c2ecf20Sopenharmony_ci * sure it is ahead of our local TSF timer. 21058c2ecf20Sopenharmony_ci */ 21068c2ecf20Sopenharmony_ci nexttbtt = bc_tu + roundup(hw_tu + FUDGE - bc_tu, intval); 21078c2ecf20Sopenharmony_ci } 21088c2ecf20Sopenharmony_ci#undef FUDGE 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci ah->nexttbtt = nexttbtt; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci intval |= AR5K_BEACON_ENA; 21138c2ecf20Sopenharmony_ci ath5k_hw_init_beacon_timers(ah, nexttbtt, intval); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci /* 21168c2ecf20Sopenharmony_ci * debugging output last in order to preserve the time critical aspect 21178c2ecf20Sopenharmony_ci * of this function 21188c2ecf20Sopenharmony_ci */ 21198c2ecf20Sopenharmony_ci if (bc_tsf == -1) 21208c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, 21218c2ecf20Sopenharmony_ci "reconfigured timers based on HW TSF\n"); 21228c2ecf20Sopenharmony_ci else if (bc_tsf == 0) 21238c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, 21248c2ecf20Sopenharmony_ci "reset HW TSF and timers\n"); 21258c2ecf20Sopenharmony_ci else 21268c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, 21278c2ecf20Sopenharmony_ci "updated timers based on beacon TSF\n"); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, 21308c2ecf20Sopenharmony_ci "bc_tsf %llx hw_tsf %llx bc_tu %u hw_tu %u nexttbtt %u\n", 21318c2ecf20Sopenharmony_ci (unsigned long long) bc_tsf, 21328c2ecf20Sopenharmony_ci (unsigned long long) hw_tsf, bc_tu, hw_tu, nexttbtt); 21338c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "intval %u %s %s\n", 21348c2ecf20Sopenharmony_ci intval & AR5K_BEACON_PERIOD, 21358c2ecf20Sopenharmony_ci intval & AR5K_BEACON_ENA ? "AR5K_BEACON_ENA" : "", 21368c2ecf20Sopenharmony_ci intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : ""); 21378c2ecf20Sopenharmony_ci} 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci/** 21408c2ecf20Sopenharmony_ci * ath5k_beacon_config - Configure the beacon queues and interrupts 21418c2ecf20Sopenharmony_ci * 21428c2ecf20Sopenharmony_ci * @ah: struct ath5k_hw pointer we are operating on 21438c2ecf20Sopenharmony_ci * 21448c2ecf20Sopenharmony_ci * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA 21458c2ecf20Sopenharmony_ci * interrupts to detect TSF updates only. 21468c2ecf20Sopenharmony_ci */ 21478c2ecf20Sopenharmony_civoid 21488c2ecf20Sopenharmony_ciath5k_beacon_config(struct ath5k_hw *ah) 21498c2ecf20Sopenharmony_ci{ 21508c2ecf20Sopenharmony_ci spin_lock_bh(&ah->block); 21518c2ecf20Sopenharmony_ci ah->bmisscount = 0; 21528c2ecf20Sopenharmony_ci ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci if (ah->enable_beacon) { 21558c2ecf20Sopenharmony_ci /* 21568c2ecf20Sopenharmony_ci * In IBSS mode we use a self-linked tx descriptor and let the 21578c2ecf20Sopenharmony_ci * hardware send the beacons automatically. We have to load it 21588c2ecf20Sopenharmony_ci * only once here. 21598c2ecf20Sopenharmony_ci * We use the SWBA interrupt only to keep track of the beacon 21608c2ecf20Sopenharmony_ci * timers in order to detect automatic TSF updates. 21618c2ecf20Sopenharmony_ci */ 21628c2ecf20Sopenharmony_ci ath5k_beaconq_config(ah); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci ah->imask |= AR5K_INT_SWBA; 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_ADHOC) { 21678c2ecf20Sopenharmony_ci if (ath5k_hw_hasveol(ah)) 21688c2ecf20Sopenharmony_ci ath5k_beacon_send(ah); 21698c2ecf20Sopenharmony_ci } else 21708c2ecf20Sopenharmony_ci ath5k_beacon_update_timers(ah, -1); 21718c2ecf20Sopenharmony_ci } else { 21728c2ecf20Sopenharmony_ci ath5k_hw_stop_beacon_queue(ah, ah->bhalq); 21738c2ecf20Sopenharmony_ci } 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci ath5k_hw_set_imr(ah, ah->imask); 21768c2ecf20Sopenharmony_ci spin_unlock_bh(&ah->block); 21778c2ecf20Sopenharmony_ci} 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_cistatic void ath5k_tasklet_beacon(struct tasklet_struct *t) 21808c2ecf20Sopenharmony_ci{ 21818c2ecf20Sopenharmony_ci struct ath5k_hw *ah = from_tasklet(ah, t, beacontq); 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci /* 21848c2ecf20Sopenharmony_ci * Software beacon alert--time to send a beacon. 21858c2ecf20Sopenharmony_ci * 21868c2ecf20Sopenharmony_ci * In IBSS mode we use this interrupt just to 21878c2ecf20Sopenharmony_ci * keep track of the next TBTT (target beacon 21888c2ecf20Sopenharmony_ci * transmission time) in order to detect whether 21898c2ecf20Sopenharmony_ci * automatic TSF updates happened. 21908c2ecf20Sopenharmony_ci */ 21918c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_ADHOC) { 21928c2ecf20Sopenharmony_ci /* XXX: only if VEOL supported */ 21938c2ecf20Sopenharmony_ci u64 tsf = ath5k_hw_get_tsf64(ah); 21948c2ecf20Sopenharmony_ci ah->nexttbtt += ah->bintval; 21958c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_BEACON, 21968c2ecf20Sopenharmony_ci "SWBA nexttbtt: %x hw_tu: %x " 21978c2ecf20Sopenharmony_ci "TSF: %llx\n", 21988c2ecf20Sopenharmony_ci ah->nexttbtt, 21998c2ecf20Sopenharmony_ci TSF_TO_TU(tsf), 22008c2ecf20Sopenharmony_ci (unsigned long long) tsf); 22018c2ecf20Sopenharmony_ci } else { 22028c2ecf20Sopenharmony_ci spin_lock(&ah->block); 22038c2ecf20Sopenharmony_ci ath5k_beacon_send(ah); 22048c2ecf20Sopenharmony_ci spin_unlock(&ah->block); 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci} 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci/********************\ 22108c2ecf20Sopenharmony_ci* Interrupt handling * 22118c2ecf20Sopenharmony_ci\********************/ 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_cistatic void 22148c2ecf20Sopenharmony_ciath5k_intr_calibration_poll(struct ath5k_hw *ah) 22158c2ecf20Sopenharmony_ci{ 22168c2ecf20Sopenharmony_ci if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) && 22178c2ecf20Sopenharmony_ci !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && 22188c2ecf20Sopenharmony_ci !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) { 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci /* Run ANI only when calibration is not active */ 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci ah->ah_cal_next_ani = jiffies + 22238c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI); 22248c2ecf20Sopenharmony_ci tasklet_schedule(&ah->ani_tasklet); 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci } else if (time_is_before_eq_jiffies(ah->ah_cal_next_short) && 22278c2ecf20Sopenharmony_ci !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && 22288c2ecf20Sopenharmony_ci !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) { 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci /* Run calibration only when another calibration 22318c2ecf20Sopenharmony_ci * is not running. 22328c2ecf20Sopenharmony_ci * 22338c2ecf20Sopenharmony_ci * Note: This is for both full/short calibration, 22348c2ecf20Sopenharmony_ci * if it's time for a full one, ath5k_calibrate_work will deal 22358c2ecf20Sopenharmony_ci * with it. */ 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci ah->ah_cal_next_short = jiffies + 22388c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); 22398c2ecf20Sopenharmony_ci ieee80211_queue_work(ah->hw, &ah->calib_work); 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci /* we could use SWI to generate enough interrupts to meet our 22428c2ecf20Sopenharmony_ci * calibration interval requirements, if necessary: 22438c2ecf20Sopenharmony_ci * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */ 22448c2ecf20Sopenharmony_ci} 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_cistatic void 22478c2ecf20Sopenharmony_ciath5k_schedule_rx(struct ath5k_hw *ah) 22488c2ecf20Sopenharmony_ci{ 22498c2ecf20Sopenharmony_ci ah->rx_pending = true; 22508c2ecf20Sopenharmony_ci tasklet_schedule(&ah->rxtq); 22518c2ecf20Sopenharmony_ci} 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_cistatic void 22548c2ecf20Sopenharmony_ciath5k_schedule_tx(struct ath5k_hw *ah) 22558c2ecf20Sopenharmony_ci{ 22568c2ecf20Sopenharmony_ci ah->tx_pending = true; 22578c2ecf20Sopenharmony_ci tasklet_schedule(&ah->txtq); 22588c2ecf20Sopenharmony_ci} 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_cistatic irqreturn_t 22618c2ecf20Sopenharmony_ciath5k_intr(int irq, void *dev_id) 22628c2ecf20Sopenharmony_ci{ 22638c2ecf20Sopenharmony_ci struct ath5k_hw *ah = dev_id; 22648c2ecf20Sopenharmony_ci enum ath5k_int status; 22658c2ecf20Sopenharmony_ci unsigned int counter = 1000; 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci /* 22698c2ecf20Sopenharmony_ci * If hw is not ready (or detached) and we get an 22708c2ecf20Sopenharmony_ci * interrupt, or if we have no interrupts pending 22718c2ecf20Sopenharmony_ci * (that means it's not for us) skip it. 22728c2ecf20Sopenharmony_ci * 22738c2ecf20Sopenharmony_ci * NOTE: Group 0/1 PCI interface registers are not 22748c2ecf20Sopenharmony_ci * supported on WiSOCs, so we can't check for pending 22758c2ecf20Sopenharmony_ci * interrupts (ISR belongs to another register group 22768c2ecf20Sopenharmony_ci * so we are ok). 22778c2ecf20Sopenharmony_ci */ 22788c2ecf20Sopenharmony_ci if (unlikely(test_bit(ATH_STAT_INVALID, ah->status) || 22798c2ecf20Sopenharmony_ci ((ath5k_get_bus_type(ah) != ATH_AHB) && 22808c2ecf20Sopenharmony_ci !ath5k_hw_is_intr_pending(ah)))) 22818c2ecf20Sopenharmony_ci return IRQ_NONE; 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci /** Main loop **/ 22848c2ecf20Sopenharmony_ci do { 22858c2ecf20Sopenharmony_ci ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */ 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n", 22888c2ecf20Sopenharmony_ci status, ah->imask); 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci /* 22918c2ecf20Sopenharmony_ci * Fatal hw error -> Log and reset 22928c2ecf20Sopenharmony_ci * 22938c2ecf20Sopenharmony_ci * Fatal errors are unrecoverable so we have to 22948c2ecf20Sopenharmony_ci * reset the card. These errors include bus and 22958c2ecf20Sopenharmony_ci * dma errors. 22968c2ecf20Sopenharmony_ci */ 22978c2ecf20Sopenharmony_ci if (unlikely(status & AR5K_INT_FATAL)) { 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, 23008c2ecf20Sopenharmony_ci "fatal int, resetting\n"); 23018c2ecf20Sopenharmony_ci ieee80211_queue_work(ah->hw, &ah->reset_work); 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci /* 23048c2ecf20Sopenharmony_ci * RX Overrun -> Count and reset if needed 23058c2ecf20Sopenharmony_ci * 23068c2ecf20Sopenharmony_ci * Receive buffers are full. Either the bus is busy or 23078c2ecf20Sopenharmony_ci * the CPU is not fast enough to process all received 23088c2ecf20Sopenharmony_ci * frames. 23098c2ecf20Sopenharmony_ci */ 23108c2ecf20Sopenharmony_ci } else if (unlikely(status & AR5K_INT_RXORN)) { 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci /* 23138c2ecf20Sopenharmony_ci * Older chipsets need a reset to come out of this 23148c2ecf20Sopenharmony_ci * condition, but we treat it as RX for newer chips. 23158c2ecf20Sopenharmony_ci * We don't know exactly which versions need a reset 23168c2ecf20Sopenharmony_ci * this guess is copied from the HAL. 23178c2ecf20Sopenharmony_ci */ 23188c2ecf20Sopenharmony_ci ah->stats.rxorn_intr++; 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci if (ah->ah_mac_srev < AR5K_SREV_AR5212) { 23218c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, 23228c2ecf20Sopenharmony_ci "rx overrun, resetting\n"); 23238c2ecf20Sopenharmony_ci ieee80211_queue_work(ah->hw, &ah->reset_work); 23248c2ecf20Sopenharmony_ci } else 23258c2ecf20Sopenharmony_ci ath5k_schedule_rx(ah); 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci } else { 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci /* Software Beacon Alert -> Schedule beacon tasklet */ 23308c2ecf20Sopenharmony_ci if (status & AR5K_INT_SWBA) 23318c2ecf20Sopenharmony_ci tasklet_hi_schedule(&ah->beacontq); 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci /* 23348c2ecf20Sopenharmony_ci * No more RX descriptors -> Just count 23358c2ecf20Sopenharmony_ci * 23368c2ecf20Sopenharmony_ci * NB: the hardware should re-read the link when 23378c2ecf20Sopenharmony_ci * RXE bit is written, but it doesn't work at 23388c2ecf20Sopenharmony_ci * least on older hardware revs. 23398c2ecf20Sopenharmony_ci */ 23408c2ecf20Sopenharmony_ci if (status & AR5K_INT_RXEOL) 23418c2ecf20Sopenharmony_ci ah->stats.rxeol_intr++; 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci /* TX Underrun -> Bump tx trigger level */ 23458c2ecf20Sopenharmony_ci if (status & AR5K_INT_TXURN) 23468c2ecf20Sopenharmony_ci ath5k_hw_update_tx_triglevel(ah, true); 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci /* RX -> Schedule rx tasklet */ 23498c2ecf20Sopenharmony_ci if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR)) 23508c2ecf20Sopenharmony_ci ath5k_schedule_rx(ah); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci /* TX -> Schedule tx tasklet */ 23538c2ecf20Sopenharmony_ci if (status & (AR5K_INT_TXOK 23548c2ecf20Sopenharmony_ci | AR5K_INT_TXDESC 23558c2ecf20Sopenharmony_ci | AR5K_INT_TXERR 23568c2ecf20Sopenharmony_ci | AR5K_INT_TXEOL)) 23578c2ecf20Sopenharmony_ci ath5k_schedule_tx(ah); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci /* Missed beacon -> TODO 23608c2ecf20Sopenharmony_ci if (status & AR5K_INT_BMISS) 23618c2ecf20Sopenharmony_ci */ 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci /* MIB event -> Update counters and notify ANI */ 23648c2ecf20Sopenharmony_ci if (status & AR5K_INT_MIB) { 23658c2ecf20Sopenharmony_ci ah->stats.mib_intr++; 23668c2ecf20Sopenharmony_ci ath5k_hw_update_mib_counters(ah); 23678c2ecf20Sopenharmony_ci ath5k_ani_mib_intr(ah); 23688c2ecf20Sopenharmony_ci } 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci /* GPIO -> Notify RFKill layer */ 23718c2ecf20Sopenharmony_ci if (status & AR5K_INT_GPIO) 23728c2ecf20Sopenharmony_ci tasklet_schedule(&ah->rf_kill.toggleq); 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci } 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci if (ath5k_get_bus_type(ah) == ATH_AHB) 23778c2ecf20Sopenharmony_ci break; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci } while (ath5k_hw_is_intr_pending(ah) && --counter > 0); 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci /* 23828c2ecf20Sopenharmony_ci * Until we handle rx/tx interrupts mask them on IMR 23838c2ecf20Sopenharmony_ci * 23848c2ecf20Sopenharmony_ci * NOTE: ah->(rx/tx)_pending are set when scheduling the tasklets 23858c2ecf20Sopenharmony_ci * and unset after we 've handled the interrupts. 23868c2ecf20Sopenharmony_ci */ 23878c2ecf20Sopenharmony_ci if (ah->rx_pending || ah->tx_pending) 23888c2ecf20Sopenharmony_ci ath5k_set_current_imask(ah); 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci if (unlikely(!counter)) 23918c2ecf20Sopenharmony_ci ATH5K_WARN(ah, "too many interrupts, giving up for now\n"); 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci /* Fire up calibration poll */ 23948c2ecf20Sopenharmony_ci ath5k_intr_calibration_poll(ah); 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci return IRQ_HANDLED; 23978c2ecf20Sopenharmony_ci} 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci/* 24008c2ecf20Sopenharmony_ci * Periodically recalibrate the PHY to account 24018c2ecf20Sopenharmony_ci * for temperature/environment changes. 24028c2ecf20Sopenharmony_ci */ 24038c2ecf20Sopenharmony_cistatic void 24048c2ecf20Sopenharmony_ciath5k_calibrate_work(struct work_struct *work) 24058c2ecf20Sopenharmony_ci{ 24068c2ecf20Sopenharmony_ci struct ath5k_hw *ah = container_of(work, struct ath5k_hw, 24078c2ecf20Sopenharmony_ci calib_work); 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci /* Should we run a full calibration ? */ 24108c2ecf20Sopenharmony_ci if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) { 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci ah->ah_cal_next_full = jiffies + 24138c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); 24148c2ecf20Sopenharmony_ci ah->ah_cal_mask |= AR5K_CALIBRATION_FULL; 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, 24178c2ecf20Sopenharmony_ci "running full calibration\n"); 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) { 24208c2ecf20Sopenharmony_ci /* 24218c2ecf20Sopenharmony_ci * Rfgain is out of bounds, reset the chip 24228c2ecf20Sopenharmony_ci * to load new gain values. 24238c2ecf20Sopenharmony_ci */ 24248c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, 24258c2ecf20Sopenharmony_ci "got new rfgain, resetting\n"); 24268c2ecf20Sopenharmony_ci ieee80211_queue_work(ah->hw, &ah->reset_work); 24278c2ecf20Sopenharmony_ci } 24288c2ecf20Sopenharmony_ci } else 24298c2ecf20Sopenharmony_ci ah->ah_cal_mask |= AR5K_CALIBRATION_SHORT; 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", 24338c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(ah->curchan->center_freq), 24348c2ecf20Sopenharmony_ci ah->curchan->hw_value); 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci if (ath5k_hw_phy_calibrate(ah, ah->curchan)) 24378c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "calibration of channel %u failed\n", 24388c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel( 24398c2ecf20Sopenharmony_ci ah->curchan->center_freq)); 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci /* Clear calibration flags */ 24428c2ecf20Sopenharmony_ci if (ah->ah_cal_mask & AR5K_CALIBRATION_FULL) 24438c2ecf20Sopenharmony_ci ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; 24448c2ecf20Sopenharmony_ci else if (ah->ah_cal_mask & AR5K_CALIBRATION_SHORT) 24458c2ecf20Sopenharmony_ci ah->ah_cal_mask &= ~AR5K_CALIBRATION_SHORT; 24468c2ecf20Sopenharmony_ci} 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_cistatic void 24508c2ecf20Sopenharmony_ciath5k_tasklet_ani(struct tasklet_struct *t) 24518c2ecf20Sopenharmony_ci{ 24528c2ecf20Sopenharmony_ci struct ath5k_hw *ah = from_tasklet(ah, t, ani_tasklet); 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci ah->ah_cal_mask |= AR5K_CALIBRATION_ANI; 24558c2ecf20Sopenharmony_ci ath5k_ani_calibration(ah); 24568c2ecf20Sopenharmony_ci ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI; 24578c2ecf20Sopenharmony_ci} 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_cistatic void 24618c2ecf20Sopenharmony_ciath5k_tx_complete_poll_work(struct work_struct *work) 24628c2ecf20Sopenharmony_ci{ 24638c2ecf20Sopenharmony_ci struct ath5k_hw *ah = container_of(work, struct ath5k_hw, 24648c2ecf20Sopenharmony_ci tx_complete_work.work); 24658c2ecf20Sopenharmony_ci struct ath5k_txq *txq; 24668c2ecf20Sopenharmony_ci int i; 24678c2ecf20Sopenharmony_ci bool needreset = false; 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci if (!test_bit(ATH_STAT_STARTED, ah->status)) 24708c2ecf20Sopenharmony_ci return; 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci mutex_lock(&ah->lock); 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) { 24758c2ecf20Sopenharmony_ci if (ah->txqs[i].setup) { 24768c2ecf20Sopenharmony_ci txq = &ah->txqs[i]; 24778c2ecf20Sopenharmony_ci spin_lock_bh(&txq->lock); 24788c2ecf20Sopenharmony_ci if (txq->txq_len > 1) { 24798c2ecf20Sopenharmony_ci if (txq->txq_poll_mark) { 24808c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_XMIT, 24818c2ecf20Sopenharmony_ci "TX queue stuck %d\n", 24828c2ecf20Sopenharmony_ci txq->qnum); 24838c2ecf20Sopenharmony_ci needreset = true; 24848c2ecf20Sopenharmony_ci txq->txq_stuck++; 24858c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->lock); 24868c2ecf20Sopenharmony_ci break; 24878c2ecf20Sopenharmony_ci } else { 24888c2ecf20Sopenharmony_ci txq->txq_poll_mark = true; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci } 24918c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->lock); 24928c2ecf20Sopenharmony_ci } 24938c2ecf20Sopenharmony_ci } 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci if (needreset) { 24968c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, 24978c2ecf20Sopenharmony_ci "TX queues stuck, resetting\n"); 24988c2ecf20Sopenharmony_ci ath5k_reset(ah, NULL, true); 24998c2ecf20Sopenharmony_ci } 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci mutex_unlock(&ah->lock); 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work, 25048c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); 25058c2ecf20Sopenharmony_ci} 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci/*************************\ 25098c2ecf20Sopenharmony_ci* Initialization routines * 25108c2ecf20Sopenharmony_ci\*************************/ 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_cistatic const struct ieee80211_iface_limit if_limits[] = { 25138c2ecf20Sopenharmony_ci { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) }, 25148c2ecf20Sopenharmony_ci { .max = 4, .types = 25158c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH 25168c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_MESH_POINT) | 25178c2ecf20Sopenharmony_ci#endif 25188c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_AP) }, 25198c2ecf20Sopenharmony_ci}; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_cistatic const struct ieee80211_iface_combination if_comb = { 25228c2ecf20Sopenharmony_ci .limits = if_limits, 25238c2ecf20Sopenharmony_ci .n_limits = ARRAY_SIZE(if_limits), 25248c2ecf20Sopenharmony_ci .max_interfaces = 2048, 25258c2ecf20Sopenharmony_ci .num_different_channels = 1, 25268c2ecf20Sopenharmony_ci}; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ciint 25298c2ecf20Sopenharmony_ciath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) 25308c2ecf20Sopenharmony_ci{ 25318c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = ah->hw; 25328c2ecf20Sopenharmony_ci struct ath_common *common; 25338c2ecf20Sopenharmony_ci int ret; 25348c2ecf20Sopenharmony_ci int csz; 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci /* Initialize driver private data */ 25378c2ecf20Sopenharmony_ci SET_IEEE80211_DEV(hw, ah->dev); 25388c2ecf20Sopenharmony_ci ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); 25398c2ecf20Sopenharmony_ci ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); 25408c2ecf20Sopenharmony_ci ieee80211_hw_set(hw, MFP_CAPABLE); 25418c2ecf20Sopenharmony_ci ieee80211_hw_set(hw, SIGNAL_DBM); 25428c2ecf20Sopenharmony_ci ieee80211_hw_set(hw, RX_INCLUDES_FCS); 25438c2ecf20Sopenharmony_ci ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci hw->wiphy->interface_modes = 25468c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_AP) | 25478c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_STATION) | 25488c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC) | 25498c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_MESH_POINT); 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci hw->wiphy->iface_combinations = &if_comb; 25528c2ecf20Sopenharmony_ci hw->wiphy->n_iface_combinations = 1; 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci /* SW support for IBSS_RSN is provided by mac80211 */ 25558c2ecf20Sopenharmony_ci hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_ci hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci /* both antennas can be configured as RX or TX */ 25608c2ecf20Sopenharmony_ci hw->wiphy->available_antennas_tx = 0x3; 25618c2ecf20Sopenharmony_ci hw->wiphy->available_antennas_rx = 0x3; 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci hw->extra_tx_headroom = 2; 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci /* 25688c2ecf20Sopenharmony_ci * Mark the device as detached to avoid processing 25698c2ecf20Sopenharmony_ci * interrupts until setup is complete. 25708c2ecf20Sopenharmony_ci */ 25718c2ecf20Sopenharmony_ci __set_bit(ATH_STAT_INVALID, ah->status); 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci ah->opmode = NL80211_IFTYPE_STATION; 25748c2ecf20Sopenharmony_ci ah->bintval = 1000; 25758c2ecf20Sopenharmony_ci mutex_init(&ah->lock); 25768c2ecf20Sopenharmony_ci spin_lock_init(&ah->rxbuflock); 25778c2ecf20Sopenharmony_ci spin_lock_init(&ah->txbuflock); 25788c2ecf20Sopenharmony_ci spin_lock_init(&ah->block); 25798c2ecf20Sopenharmony_ci spin_lock_init(&ah->irqlock); 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci /* Setup interrupt handler */ 25828c2ecf20Sopenharmony_ci ret = request_irq(ah->irq, ath5k_intr, IRQF_SHARED, "ath", ah); 25838c2ecf20Sopenharmony_ci if (ret) { 25848c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "request_irq failed\n"); 25858c2ecf20Sopenharmony_ci goto err; 25868c2ecf20Sopenharmony_ci } 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ci common = ath5k_hw_common(ah); 25898c2ecf20Sopenharmony_ci common->ops = &ath5k_common_ops; 25908c2ecf20Sopenharmony_ci common->bus_ops = bus_ops; 25918c2ecf20Sopenharmony_ci common->ah = ah; 25928c2ecf20Sopenharmony_ci common->hw = hw; 25938c2ecf20Sopenharmony_ci common->priv = ah; 25948c2ecf20Sopenharmony_ci common->clockrate = 40; 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci /* 25978c2ecf20Sopenharmony_ci * Cache line size is used to size and align various 25988c2ecf20Sopenharmony_ci * structures used to communicate with the hardware. 25998c2ecf20Sopenharmony_ci */ 26008c2ecf20Sopenharmony_ci ath5k_read_cachesize(common, &csz); 26018c2ecf20Sopenharmony_ci common->cachelsz = csz << 2; /* convert to bytes */ 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci spin_lock_init(&common->cc_lock); 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci /* Initialize device */ 26068c2ecf20Sopenharmony_ci ret = ath5k_hw_init(ah); 26078c2ecf20Sopenharmony_ci if (ret) 26088c2ecf20Sopenharmony_ci goto err_irq; 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci /* Set up multi-rate retry capabilities */ 26118c2ecf20Sopenharmony_ci if (ah->ah_capabilities.cap_has_mrr_support) { 26128c2ecf20Sopenharmony_ci hw->max_rates = 4; 26138c2ecf20Sopenharmony_ci hw->max_rate_tries = max(AR5K_INIT_RETRY_SHORT, 26148c2ecf20Sopenharmony_ci AR5K_INIT_RETRY_LONG); 26158c2ecf20Sopenharmony_ci } 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci hw->vif_data_size = sizeof(struct ath5k_vif); 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci /* Finish private driver data initialization */ 26208c2ecf20Sopenharmony_ci ret = ath5k_init(hw); 26218c2ecf20Sopenharmony_ci if (ret) 26228c2ecf20Sopenharmony_ci goto err_ah; 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci ATH5K_INFO(ah, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n", 26258c2ecf20Sopenharmony_ci ath5k_chip_name(AR5K_VERSION_MAC, ah->ah_mac_srev), 26268c2ecf20Sopenharmony_ci ah->ah_mac_srev, 26278c2ecf20Sopenharmony_ci ah->ah_phy_revision); 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci if (!ah->ah_single_chip) { 26308c2ecf20Sopenharmony_ci /* Single chip radio (!RF5111) */ 26318c2ecf20Sopenharmony_ci if (ah->ah_radio_5ghz_revision && 26328c2ecf20Sopenharmony_ci !ah->ah_radio_2ghz_revision) { 26338c2ecf20Sopenharmony_ci /* No 5GHz support -> report 2GHz radio */ 26348c2ecf20Sopenharmony_ci if (!test_bit(AR5K_MODE_11A, 26358c2ecf20Sopenharmony_ci ah->ah_capabilities.cap_mode)) { 26368c2ecf20Sopenharmony_ci ATH5K_INFO(ah, "RF%s 2GHz radio found (0x%x)\n", 26378c2ecf20Sopenharmony_ci ath5k_chip_name(AR5K_VERSION_RAD, 26388c2ecf20Sopenharmony_ci ah->ah_radio_5ghz_revision), 26398c2ecf20Sopenharmony_ci ah->ah_radio_5ghz_revision); 26408c2ecf20Sopenharmony_ci /* No 2GHz support (5110 and some 26418c2ecf20Sopenharmony_ci * 5GHz only cards) -> report 5GHz radio */ 26428c2ecf20Sopenharmony_ci } else if (!test_bit(AR5K_MODE_11B, 26438c2ecf20Sopenharmony_ci ah->ah_capabilities.cap_mode)) { 26448c2ecf20Sopenharmony_ci ATH5K_INFO(ah, "RF%s 5GHz radio found (0x%x)\n", 26458c2ecf20Sopenharmony_ci ath5k_chip_name(AR5K_VERSION_RAD, 26468c2ecf20Sopenharmony_ci ah->ah_radio_5ghz_revision), 26478c2ecf20Sopenharmony_ci ah->ah_radio_5ghz_revision); 26488c2ecf20Sopenharmony_ci /* Multiband radio */ 26498c2ecf20Sopenharmony_ci } else { 26508c2ecf20Sopenharmony_ci ATH5K_INFO(ah, "RF%s multiband radio found" 26518c2ecf20Sopenharmony_ci " (0x%x)\n", 26528c2ecf20Sopenharmony_ci ath5k_chip_name(AR5K_VERSION_RAD, 26538c2ecf20Sopenharmony_ci ah->ah_radio_5ghz_revision), 26548c2ecf20Sopenharmony_ci ah->ah_radio_5ghz_revision); 26558c2ecf20Sopenharmony_ci } 26568c2ecf20Sopenharmony_ci } 26578c2ecf20Sopenharmony_ci /* Multi chip radio (RF5111 - RF2111) -> 26588c2ecf20Sopenharmony_ci * report both 2GHz/5GHz radios */ 26598c2ecf20Sopenharmony_ci else if (ah->ah_radio_5ghz_revision && 26608c2ecf20Sopenharmony_ci ah->ah_radio_2ghz_revision) { 26618c2ecf20Sopenharmony_ci ATH5K_INFO(ah, "RF%s 5GHz radio found (0x%x)\n", 26628c2ecf20Sopenharmony_ci ath5k_chip_name(AR5K_VERSION_RAD, 26638c2ecf20Sopenharmony_ci ah->ah_radio_5ghz_revision), 26648c2ecf20Sopenharmony_ci ah->ah_radio_5ghz_revision); 26658c2ecf20Sopenharmony_ci ATH5K_INFO(ah, "RF%s 2GHz radio found (0x%x)\n", 26668c2ecf20Sopenharmony_ci ath5k_chip_name(AR5K_VERSION_RAD, 26678c2ecf20Sopenharmony_ci ah->ah_radio_2ghz_revision), 26688c2ecf20Sopenharmony_ci ah->ah_radio_2ghz_revision); 26698c2ecf20Sopenharmony_ci } 26708c2ecf20Sopenharmony_ci } 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci ath5k_debug_init_device(ah); 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci /* ready to process interrupts */ 26758c2ecf20Sopenharmony_ci __clear_bit(ATH_STAT_INVALID, ah->status); 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci return 0; 26788c2ecf20Sopenharmony_cierr_ah: 26798c2ecf20Sopenharmony_ci ath5k_hw_deinit(ah); 26808c2ecf20Sopenharmony_cierr_irq: 26818c2ecf20Sopenharmony_ci free_irq(ah->irq, ah); 26828c2ecf20Sopenharmony_cierr: 26838c2ecf20Sopenharmony_ci return ret; 26848c2ecf20Sopenharmony_ci} 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_cistatic int 26878c2ecf20Sopenharmony_ciath5k_stop_locked(struct ath5k_hw *ah) 26888c2ecf20Sopenharmony_ci{ 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "invalid %u\n", 26918c2ecf20Sopenharmony_ci test_bit(ATH_STAT_INVALID, ah->status)); 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci /* 26948c2ecf20Sopenharmony_ci * Shutdown the hardware and driver: 26958c2ecf20Sopenharmony_ci * stop output from above 26968c2ecf20Sopenharmony_ci * disable interrupts 26978c2ecf20Sopenharmony_ci * turn off timers 26988c2ecf20Sopenharmony_ci * turn off the radio 26998c2ecf20Sopenharmony_ci * clear transmit machinery 27008c2ecf20Sopenharmony_ci * clear receive machinery 27018c2ecf20Sopenharmony_ci * drain and release tx queues 27028c2ecf20Sopenharmony_ci * reclaim beacon resources 27038c2ecf20Sopenharmony_ci * power down hardware 27048c2ecf20Sopenharmony_ci * 27058c2ecf20Sopenharmony_ci * Note that some of this work is not possible if the 27068c2ecf20Sopenharmony_ci * hardware is gone (invalid). 27078c2ecf20Sopenharmony_ci */ 27088c2ecf20Sopenharmony_ci ieee80211_stop_queues(ah->hw); 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ci if (!test_bit(ATH_STAT_INVALID, ah->status)) { 27118c2ecf20Sopenharmony_ci ath5k_led_off(ah); 27128c2ecf20Sopenharmony_ci ath5k_hw_set_imr(ah, 0); 27138c2ecf20Sopenharmony_ci synchronize_irq(ah->irq); 27148c2ecf20Sopenharmony_ci ath5k_rx_stop(ah); 27158c2ecf20Sopenharmony_ci ath5k_hw_dma_stop(ah); 27168c2ecf20Sopenharmony_ci ath5k_drain_tx_buffs(ah); 27178c2ecf20Sopenharmony_ci ath5k_hw_phy_disable(ah); 27188c2ecf20Sopenharmony_ci } 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci return 0; 27218c2ecf20Sopenharmony_ci} 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ciint ath5k_start(struct ieee80211_hw *hw) 27248c2ecf20Sopenharmony_ci{ 27258c2ecf20Sopenharmony_ci struct ath5k_hw *ah = hw->priv; 27268c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 27278c2ecf20Sopenharmony_ci int ret, i; 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci mutex_lock(&ah->lock); 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "mode %d\n", ah->opmode); 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci /* 27348c2ecf20Sopenharmony_ci * Stop anything previously setup. This is safe 27358c2ecf20Sopenharmony_ci * no matter this is the first time through or not. 27368c2ecf20Sopenharmony_ci */ 27378c2ecf20Sopenharmony_ci ath5k_stop_locked(ah); 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci /* 27408c2ecf20Sopenharmony_ci * The basic interface to setting the hardware in a good 27418c2ecf20Sopenharmony_ci * state is ``reset''. On return the hardware is known to 27428c2ecf20Sopenharmony_ci * be powered up and with interrupts disabled. This must 27438c2ecf20Sopenharmony_ci * be followed by initialization of the appropriate bits 27448c2ecf20Sopenharmony_ci * and then setup of the interrupt mask. 27458c2ecf20Sopenharmony_ci */ 27468c2ecf20Sopenharmony_ci ah->curchan = ah->hw->conf.chandef.chan; 27478c2ecf20Sopenharmony_ci ah->imask = AR5K_INT_RXOK 27488c2ecf20Sopenharmony_ci | AR5K_INT_RXERR 27498c2ecf20Sopenharmony_ci | AR5K_INT_RXEOL 27508c2ecf20Sopenharmony_ci | AR5K_INT_RXORN 27518c2ecf20Sopenharmony_ci | AR5K_INT_TXDESC 27528c2ecf20Sopenharmony_ci | AR5K_INT_TXEOL 27538c2ecf20Sopenharmony_ci | AR5K_INT_FATAL 27548c2ecf20Sopenharmony_ci | AR5K_INT_GLOBAL 27558c2ecf20Sopenharmony_ci | AR5K_INT_MIB; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci ret = ath5k_reset(ah, NULL, false); 27588c2ecf20Sopenharmony_ci if (ret) 27598c2ecf20Sopenharmony_ci goto done; 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci if (!ath5k_modparam_no_hw_rfkill_switch) 27628c2ecf20Sopenharmony_ci ath5k_rfkill_hw_start(ah); 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci /* 27658c2ecf20Sopenharmony_ci * Reset the key cache since some parts do not reset the 27668c2ecf20Sopenharmony_ci * contents on initial power up or resume from suspend. 27678c2ecf20Sopenharmony_ci */ 27688c2ecf20Sopenharmony_ci for (i = 0; i < common->keymax; i++) 27698c2ecf20Sopenharmony_ci ath_hw_keyreset(common, (u16) i); 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci /* Use higher rates for acks instead of base 27728c2ecf20Sopenharmony_ci * rate */ 27738c2ecf20Sopenharmony_ci ah->ah_ack_bitrate_high = true; 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ah->bslot); i++) 27768c2ecf20Sopenharmony_ci ah->bslot[i] = NULL; 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci ret = 0; 27798c2ecf20Sopenharmony_cidone: 27808c2ecf20Sopenharmony_ci mutex_unlock(&ah->lock); 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci set_bit(ATH_STAT_STARTED, ah->status); 27838c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(ah->hw, &ah->tx_complete_work, 27848c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci return ret; 27878c2ecf20Sopenharmony_ci} 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_cistatic void ath5k_stop_tasklets(struct ath5k_hw *ah) 27908c2ecf20Sopenharmony_ci{ 27918c2ecf20Sopenharmony_ci ah->rx_pending = false; 27928c2ecf20Sopenharmony_ci ah->tx_pending = false; 27938c2ecf20Sopenharmony_ci tasklet_kill(&ah->rxtq); 27948c2ecf20Sopenharmony_ci tasklet_kill(&ah->txtq); 27958c2ecf20Sopenharmony_ci tasklet_kill(&ah->beacontq); 27968c2ecf20Sopenharmony_ci tasklet_kill(&ah->ani_tasklet); 27978c2ecf20Sopenharmony_ci} 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci/* 28008c2ecf20Sopenharmony_ci * Stop the device, grabbing the top-level lock to protect 28018c2ecf20Sopenharmony_ci * against concurrent entry through ath5k_init (which can happen 28028c2ecf20Sopenharmony_ci * if another thread does a system call and the thread doing the 28038c2ecf20Sopenharmony_ci * stop is preempted). 28048c2ecf20Sopenharmony_ci */ 28058c2ecf20Sopenharmony_civoid ath5k_stop(struct ieee80211_hw *hw) 28068c2ecf20Sopenharmony_ci{ 28078c2ecf20Sopenharmony_ci struct ath5k_hw *ah = hw->priv; 28088c2ecf20Sopenharmony_ci int ret; 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci mutex_lock(&ah->lock); 28118c2ecf20Sopenharmony_ci ret = ath5k_stop_locked(ah); 28128c2ecf20Sopenharmony_ci if (ret == 0 && !test_bit(ATH_STAT_INVALID, ah->status)) { 28138c2ecf20Sopenharmony_ci /* 28148c2ecf20Sopenharmony_ci * Don't set the card in full sleep mode! 28158c2ecf20Sopenharmony_ci * 28168c2ecf20Sopenharmony_ci * a) When the device is in this state it must be carefully 28178c2ecf20Sopenharmony_ci * woken up or references to registers in the PCI clock 28188c2ecf20Sopenharmony_ci * domain may freeze the bus (and system). This varies 28198c2ecf20Sopenharmony_ci * by chip and is mostly an issue with newer parts 28208c2ecf20Sopenharmony_ci * (madwifi sources mentioned srev >= 0x78) that go to 28218c2ecf20Sopenharmony_ci * sleep more quickly. 28228c2ecf20Sopenharmony_ci * 28238c2ecf20Sopenharmony_ci * b) On older chips full sleep results a weird behaviour 28248c2ecf20Sopenharmony_ci * during wakeup. I tested various cards with srev < 0x78 28258c2ecf20Sopenharmony_ci * and they don't wake up after module reload, a second 28268c2ecf20Sopenharmony_ci * module reload is needed to bring the card up again. 28278c2ecf20Sopenharmony_ci * 28288c2ecf20Sopenharmony_ci * Until we figure out what's going on don't enable 28298c2ecf20Sopenharmony_ci * full chip reset on any chip (this is what Legacy HAL 28308c2ecf20Sopenharmony_ci * and Sam's HAL do anyway). Instead Perform a full reset 28318c2ecf20Sopenharmony_ci * on the device (same as initial state after attach) and 28328c2ecf20Sopenharmony_ci * leave it idle (keep MAC/BB on warm reset) */ 28338c2ecf20Sopenharmony_ci ret = ath5k_hw_on_hold(ah); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, 28368c2ecf20Sopenharmony_ci "putting device to sleep\n"); 28378c2ecf20Sopenharmony_ci } 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci mutex_unlock(&ah->lock); 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci ath5k_stop_tasklets(ah); 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci clear_bit(ATH_STAT_STARTED, ah->status); 28448c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ah->tx_complete_work); 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci if (!ath5k_modparam_no_hw_rfkill_switch) 28478c2ecf20Sopenharmony_ci ath5k_rfkill_hw_stop(ah); 28488c2ecf20Sopenharmony_ci} 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci/* 28518c2ecf20Sopenharmony_ci * Reset the hardware. If chan is not NULL, then also pause rx/tx 28528c2ecf20Sopenharmony_ci * and change to the given channel. 28538c2ecf20Sopenharmony_ci * 28548c2ecf20Sopenharmony_ci * This should be called with ah->lock. 28558c2ecf20Sopenharmony_ci */ 28568c2ecf20Sopenharmony_cistatic int 28578c2ecf20Sopenharmony_ciath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, 28588c2ecf20Sopenharmony_ci bool skip_pcu) 28598c2ecf20Sopenharmony_ci{ 28608c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 28618c2ecf20Sopenharmony_ci int ret, ani_mode; 28628c2ecf20Sopenharmony_ci bool fast = chan && modparam_fastchanswitch ? 1 : 0; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n"); 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci __set_bit(ATH_STAT_RESET, ah->status); 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci ath5k_hw_set_imr(ah, 0); 28698c2ecf20Sopenharmony_ci synchronize_irq(ah->irq); 28708c2ecf20Sopenharmony_ci ath5k_stop_tasklets(ah); 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci /* Save ani mode and disable ANI during 28738c2ecf20Sopenharmony_ci * reset. If we don't we might get false 28748c2ecf20Sopenharmony_ci * PHY error interrupts. */ 28758c2ecf20Sopenharmony_ci ani_mode = ah->ani_state.ani_mode; 28768c2ecf20Sopenharmony_ci ath5k_ani_init(ah, ATH5K_ANI_MODE_OFF); 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci /* We are going to empty hw queues 28798c2ecf20Sopenharmony_ci * so we should also free any remaining 28808c2ecf20Sopenharmony_ci * tx buffers */ 28818c2ecf20Sopenharmony_ci ath5k_drain_tx_buffs(ah); 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci /* Stop PCU */ 28848c2ecf20Sopenharmony_ci ath5k_hw_stop_rx_pcu(ah); 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci /* Stop DMA 28878c2ecf20Sopenharmony_ci * 28888c2ecf20Sopenharmony_ci * Note: If DMA didn't stop continue 28898c2ecf20Sopenharmony_ci * since only a reset will fix it. 28908c2ecf20Sopenharmony_ci */ 28918c2ecf20Sopenharmony_ci ret = ath5k_hw_dma_stop(ah); 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci /* RF Bus grant won't work if we have pending 28948c2ecf20Sopenharmony_ci * frames 28958c2ecf20Sopenharmony_ci */ 28968c2ecf20Sopenharmony_ci if (ret && fast) { 28978c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_RESET, 28988c2ecf20Sopenharmony_ci "DMA didn't stop, falling back to normal reset\n"); 28998c2ecf20Sopenharmony_ci fast = false; 29008c2ecf20Sopenharmony_ci } 29018c2ecf20Sopenharmony_ci 29028c2ecf20Sopenharmony_ci if (chan) 29038c2ecf20Sopenharmony_ci ah->curchan = chan; 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu); 29068c2ecf20Sopenharmony_ci if (ret) { 29078c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret); 29088c2ecf20Sopenharmony_ci goto err; 29098c2ecf20Sopenharmony_ci } 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci ret = ath5k_rx_start(ah); 29128c2ecf20Sopenharmony_ci if (ret) { 29138c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't start recv logic\n"); 29148c2ecf20Sopenharmony_ci goto err; 29158c2ecf20Sopenharmony_ci } 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci ath5k_ani_init(ah, ani_mode); 29188c2ecf20Sopenharmony_ci 29198c2ecf20Sopenharmony_ci /* 29208c2ecf20Sopenharmony_ci * Set calibration intervals 29218c2ecf20Sopenharmony_ci * 29228c2ecf20Sopenharmony_ci * Note: We don't need to run calibration imediately 29238c2ecf20Sopenharmony_ci * since some initial calibration is done on reset 29248c2ecf20Sopenharmony_ci * even for fast channel switching. Also on scanning 29258c2ecf20Sopenharmony_ci * this will get set again and again and it won't get 29268c2ecf20Sopenharmony_ci * executed unless we connect somewhere and spend some 29278c2ecf20Sopenharmony_ci * time on the channel (that's what calibration needs 29288c2ecf20Sopenharmony_ci * anyway to be accurate). 29298c2ecf20Sopenharmony_ci */ 29308c2ecf20Sopenharmony_ci ah->ah_cal_next_full = jiffies + 29318c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); 29328c2ecf20Sopenharmony_ci ah->ah_cal_next_ani = jiffies + 29338c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI); 29348c2ecf20Sopenharmony_ci ah->ah_cal_next_short = jiffies + 29358c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci ewma_beacon_rssi_init(&ah->ah_beacon_rssi_avg); 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci /* clear survey data and cycle counters */ 29408c2ecf20Sopenharmony_ci memset(&ah->survey, 0, sizeof(ah->survey)); 29418c2ecf20Sopenharmony_ci spin_lock_bh(&common->cc_lock); 29428c2ecf20Sopenharmony_ci ath_hw_cycle_counters_update(common); 29438c2ecf20Sopenharmony_ci memset(&common->cc_survey, 0, sizeof(common->cc_survey)); 29448c2ecf20Sopenharmony_ci memset(&common->cc_ani, 0, sizeof(common->cc_ani)); 29458c2ecf20Sopenharmony_ci spin_unlock_bh(&common->cc_lock); 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_ci /* 29488c2ecf20Sopenharmony_ci * Change channels and update the h/w rate map if we're switching; 29498c2ecf20Sopenharmony_ci * e.g. 11a to 11b/g. 29508c2ecf20Sopenharmony_ci * 29518c2ecf20Sopenharmony_ci * We may be doing a reset in response to an ioctl that changes the 29528c2ecf20Sopenharmony_ci * channel so update any state that might change as a result. 29538c2ecf20Sopenharmony_ci * 29548c2ecf20Sopenharmony_ci * XXX needed? 29558c2ecf20Sopenharmony_ci */ 29568c2ecf20Sopenharmony_ci/* ath5k_chan_change(ah, c); */ 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci __clear_bit(ATH_STAT_RESET, ah->status); 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci ath5k_beacon_config(ah); 29618c2ecf20Sopenharmony_ci /* intrs are enabled by ath5k_beacon_config */ 29628c2ecf20Sopenharmony_ci 29638c2ecf20Sopenharmony_ci ieee80211_wake_queues(ah->hw); 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_ci return 0; 29668c2ecf20Sopenharmony_cierr: 29678c2ecf20Sopenharmony_ci return ret; 29688c2ecf20Sopenharmony_ci} 29698c2ecf20Sopenharmony_ci 29708c2ecf20Sopenharmony_cistatic void ath5k_reset_work(struct work_struct *work) 29718c2ecf20Sopenharmony_ci{ 29728c2ecf20Sopenharmony_ci struct ath5k_hw *ah = container_of(work, struct ath5k_hw, 29738c2ecf20Sopenharmony_ci reset_work); 29748c2ecf20Sopenharmony_ci 29758c2ecf20Sopenharmony_ci mutex_lock(&ah->lock); 29768c2ecf20Sopenharmony_ci ath5k_reset(ah, NULL, true); 29778c2ecf20Sopenharmony_ci mutex_unlock(&ah->lock); 29788c2ecf20Sopenharmony_ci} 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_cistatic int 29818c2ecf20Sopenharmony_ciath5k_init(struct ieee80211_hw *hw) 29828c2ecf20Sopenharmony_ci{ 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci struct ath5k_hw *ah = hw->priv; 29858c2ecf20Sopenharmony_ci struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); 29868c2ecf20Sopenharmony_ci struct ath5k_txq *txq; 29878c2ecf20Sopenharmony_ci u8 mac[ETH_ALEN] = {}; 29888c2ecf20Sopenharmony_ci int ret; 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci /* 29928c2ecf20Sopenharmony_ci * Collect the channel list. The 802.11 layer 29938c2ecf20Sopenharmony_ci * is responsible for filtering this list based 29948c2ecf20Sopenharmony_ci * on settings like the phy mode and regulatory 29958c2ecf20Sopenharmony_ci * domain restrictions. 29968c2ecf20Sopenharmony_ci */ 29978c2ecf20Sopenharmony_ci ret = ath5k_setup_bands(hw); 29988c2ecf20Sopenharmony_ci if (ret) { 29998c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't get channels\n"); 30008c2ecf20Sopenharmony_ci goto err; 30018c2ecf20Sopenharmony_ci } 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci /* 30048c2ecf20Sopenharmony_ci * Allocate tx+rx descriptors and populate the lists. 30058c2ecf20Sopenharmony_ci */ 30068c2ecf20Sopenharmony_ci ret = ath5k_desc_alloc(ah); 30078c2ecf20Sopenharmony_ci if (ret) { 30088c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't allocate descriptors\n"); 30098c2ecf20Sopenharmony_ci goto err; 30108c2ecf20Sopenharmony_ci } 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci /* 30138c2ecf20Sopenharmony_ci * Allocate hardware transmit queues: one queue for 30148c2ecf20Sopenharmony_ci * beacon frames and one data queue for each QoS 30158c2ecf20Sopenharmony_ci * priority. Note that hw functions handle resetting 30168c2ecf20Sopenharmony_ci * these queues at the needed time. 30178c2ecf20Sopenharmony_ci */ 30188c2ecf20Sopenharmony_ci ret = ath5k_beaconq_setup(ah); 30198c2ecf20Sopenharmony_ci if (ret < 0) { 30208c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't setup a beacon xmit queue\n"); 30218c2ecf20Sopenharmony_ci goto err_desc; 30228c2ecf20Sopenharmony_ci } 30238c2ecf20Sopenharmony_ci ah->bhalq = ret; 30248c2ecf20Sopenharmony_ci ah->cabq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_CAB, 0); 30258c2ecf20Sopenharmony_ci if (IS_ERR(ah->cabq)) { 30268c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't setup cab queue\n"); 30278c2ecf20Sopenharmony_ci ret = PTR_ERR(ah->cabq); 30288c2ecf20Sopenharmony_ci goto err_bhal; 30298c2ecf20Sopenharmony_ci } 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci /* 5211 and 5212 usually support 10 queues but we better rely on the 30328c2ecf20Sopenharmony_ci * capability information */ 30338c2ecf20Sopenharmony_ci if (ah->ah_capabilities.cap_queues.q_tx_num >= 6) { 30348c2ecf20Sopenharmony_ci /* This order matches mac80211's queue priority, so we can 30358c2ecf20Sopenharmony_ci * directly use the mac80211 queue number without any mapping */ 30368c2ecf20Sopenharmony_ci txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VO); 30378c2ecf20Sopenharmony_ci if (IS_ERR(txq)) { 30388c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't setup xmit queue\n"); 30398c2ecf20Sopenharmony_ci ret = PTR_ERR(txq); 30408c2ecf20Sopenharmony_ci goto err_queues; 30418c2ecf20Sopenharmony_ci } 30428c2ecf20Sopenharmony_ci txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VI); 30438c2ecf20Sopenharmony_ci if (IS_ERR(txq)) { 30448c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't setup xmit queue\n"); 30458c2ecf20Sopenharmony_ci ret = PTR_ERR(txq); 30468c2ecf20Sopenharmony_ci goto err_queues; 30478c2ecf20Sopenharmony_ci } 30488c2ecf20Sopenharmony_ci txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE); 30498c2ecf20Sopenharmony_ci if (IS_ERR(txq)) { 30508c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't setup xmit queue\n"); 30518c2ecf20Sopenharmony_ci ret = PTR_ERR(txq); 30528c2ecf20Sopenharmony_ci goto err_queues; 30538c2ecf20Sopenharmony_ci } 30548c2ecf20Sopenharmony_ci txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); 30558c2ecf20Sopenharmony_ci if (IS_ERR(txq)) { 30568c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't setup xmit queue\n"); 30578c2ecf20Sopenharmony_ci ret = PTR_ERR(txq); 30588c2ecf20Sopenharmony_ci goto err_queues; 30598c2ecf20Sopenharmony_ci } 30608c2ecf20Sopenharmony_ci hw->queues = 4; 30618c2ecf20Sopenharmony_ci } else { 30628c2ecf20Sopenharmony_ci /* older hardware (5210) can only support one data queue */ 30638c2ecf20Sopenharmony_ci txq = ath5k_txq_setup(ah, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE); 30648c2ecf20Sopenharmony_ci if (IS_ERR(txq)) { 30658c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't setup xmit queue\n"); 30668c2ecf20Sopenharmony_ci ret = PTR_ERR(txq); 30678c2ecf20Sopenharmony_ci goto err_queues; 30688c2ecf20Sopenharmony_ci } 30698c2ecf20Sopenharmony_ci hw->queues = 1; 30708c2ecf20Sopenharmony_ci } 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_ci tasklet_setup(&ah->rxtq, ath5k_tasklet_rx); 30738c2ecf20Sopenharmony_ci tasklet_setup(&ah->txtq, ath5k_tasklet_tx); 30748c2ecf20Sopenharmony_ci tasklet_setup(&ah->beacontq, ath5k_tasklet_beacon); 30758c2ecf20Sopenharmony_ci tasklet_setup(&ah->ani_tasklet, ath5k_tasklet_ani); 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci INIT_WORK(&ah->reset_work, ath5k_reset_work); 30788c2ecf20Sopenharmony_ci INIT_WORK(&ah->calib_work, ath5k_calibrate_work); 30798c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ah->tx_complete_work, ath5k_tx_complete_poll_work); 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci ret = ath5k_hw_common(ah)->bus_ops->eeprom_read_mac(ah, mac); 30828c2ecf20Sopenharmony_ci if (ret) { 30838c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "unable to read address from EEPROM\n"); 30848c2ecf20Sopenharmony_ci goto err_queues; 30858c2ecf20Sopenharmony_ci } 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci SET_IEEE80211_PERM_ADDR(hw, mac); 30888c2ecf20Sopenharmony_ci /* All MAC address bits matter for ACKs */ 30898c2ecf20Sopenharmony_ci ath5k_update_bssid_mask_and_opmode(ah, NULL); 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain; 30928c2ecf20Sopenharmony_ci ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier); 30938c2ecf20Sopenharmony_ci if (ret) { 30948c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't initialize regulatory system\n"); 30958c2ecf20Sopenharmony_ci goto err_queues; 30968c2ecf20Sopenharmony_ci } 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci ret = ieee80211_register_hw(hw); 30998c2ecf20Sopenharmony_ci if (ret) { 31008c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "can't register ieee80211 hw\n"); 31018c2ecf20Sopenharmony_ci goto err_queues; 31028c2ecf20Sopenharmony_ci } 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci if (!ath_is_world_regd(regulatory)) 31058c2ecf20Sopenharmony_ci regulatory_hint(hw->wiphy, regulatory->alpha2); 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci ath5k_init_leds(ah); 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci ath5k_sysfs_register(ah); 31108c2ecf20Sopenharmony_ci 31118c2ecf20Sopenharmony_ci return 0; 31128c2ecf20Sopenharmony_cierr_queues: 31138c2ecf20Sopenharmony_ci ath5k_txq_release(ah); 31148c2ecf20Sopenharmony_cierr_bhal: 31158c2ecf20Sopenharmony_ci ath5k_hw_release_tx_queue(ah, ah->bhalq); 31168c2ecf20Sopenharmony_cierr_desc: 31178c2ecf20Sopenharmony_ci ath5k_desc_free(ah); 31188c2ecf20Sopenharmony_cierr: 31198c2ecf20Sopenharmony_ci return ret; 31208c2ecf20Sopenharmony_ci} 31218c2ecf20Sopenharmony_ci 31228c2ecf20Sopenharmony_civoid 31238c2ecf20Sopenharmony_ciath5k_deinit_ah(struct ath5k_hw *ah) 31248c2ecf20Sopenharmony_ci{ 31258c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = ah->hw; 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci /* 31288c2ecf20Sopenharmony_ci * NB: the order of these is important: 31298c2ecf20Sopenharmony_ci * o call the 802.11 layer before detaching ath5k_hw to 31308c2ecf20Sopenharmony_ci * ensure callbacks into the driver to delete global 31318c2ecf20Sopenharmony_ci * key cache entries can be handled 31328c2ecf20Sopenharmony_ci * o reclaim the tx queue data structures after calling 31338c2ecf20Sopenharmony_ci * the 802.11 layer as we'll get called back to reclaim 31348c2ecf20Sopenharmony_ci * node state and potentially want to use them 31358c2ecf20Sopenharmony_ci * o to cleanup the tx queues the hal is called, so detach 31368c2ecf20Sopenharmony_ci * it last 31378c2ecf20Sopenharmony_ci * XXX: ??? detach ath5k_hw ??? 31388c2ecf20Sopenharmony_ci * Other than that, it's straightforward... 31398c2ecf20Sopenharmony_ci */ 31408c2ecf20Sopenharmony_ci ieee80211_unregister_hw(hw); 31418c2ecf20Sopenharmony_ci ath5k_desc_free(ah); 31428c2ecf20Sopenharmony_ci ath5k_txq_release(ah); 31438c2ecf20Sopenharmony_ci ath5k_hw_release_tx_queue(ah, ah->bhalq); 31448c2ecf20Sopenharmony_ci ath5k_unregister_leds(ah); 31458c2ecf20Sopenharmony_ci 31468c2ecf20Sopenharmony_ci ath5k_sysfs_unregister(ah); 31478c2ecf20Sopenharmony_ci /* 31488c2ecf20Sopenharmony_ci * NB: can't reclaim these until after ieee80211_ifdetach 31498c2ecf20Sopenharmony_ci * returns because we'll get called back to reclaim node 31508c2ecf20Sopenharmony_ci * state and potentially want to use them. 31518c2ecf20Sopenharmony_ci */ 31528c2ecf20Sopenharmony_ci ath5k_hw_deinit(ah); 31538c2ecf20Sopenharmony_ci free_irq(ah->irq, ah); 31548c2ecf20Sopenharmony_ci} 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_cibool 31578c2ecf20Sopenharmony_ciath5k_any_vif_assoc(struct ath5k_hw *ah) 31588c2ecf20Sopenharmony_ci{ 31598c2ecf20Sopenharmony_ci struct ath5k_vif_iter_data iter_data; 31608c2ecf20Sopenharmony_ci iter_data.hw_macaddr = NULL; 31618c2ecf20Sopenharmony_ci iter_data.any_assoc = false; 31628c2ecf20Sopenharmony_ci iter_data.need_set_hw_addr = false; 31638c2ecf20Sopenharmony_ci iter_data.found_active = true; 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic( 31668c2ecf20Sopenharmony_ci ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL, 31678c2ecf20Sopenharmony_ci ath5k_vif_iter, &iter_data); 31688c2ecf20Sopenharmony_ci return iter_data.any_assoc; 31698c2ecf20Sopenharmony_ci} 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_civoid 31728c2ecf20Sopenharmony_ciath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable) 31738c2ecf20Sopenharmony_ci{ 31748c2ecf20Sopenharmony_ci struct ath5k_hw *ah = hw->priv; 31758c2ecf20Sopenharmony_ci u32 rfilt; 31768c2ecf20Sopenharmony_ci rfilt = ath5k_hw_get_rx_filter(ah); 31778c2ecf20Sopenharmony_ci if (enable) 31788c2ecf20Sopenharmony_ci rfilt |= AR5K_RX_FILTER_BEACON; 31798c2ecf20Sopenharmony_ci else 31808c2ecf20Sopenharmony_ci rfilt &= ~AR5K_RX_FILTER_BEACON; 31818c2ecf20Sopenharmony_ci ath5k_hw_set_rx_filter(ah, rfilt); 31828c2ecf20Sopenharmony_ci ah->filter_flags = rfilt; 31838c2ecf20Sopenharmony_ci} 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_civoid _ath5k_printk(const struct ath5k_hw *ah, const char *level, 31868c2ecf20Sopenharmony_ci const char *fmt, ...) 31878c2ecf20Sopenharmony_ci{ 31888c2ecf20Sopenharmony_ci struct va_format vaf; 31898c2ecf20Sopenharmony_ci va_list args; 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_ci va_start(args, fmt); 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci vaf.fmt = fmt; 31948c2ecf20Sopenharmony_ci vaf.va = &args; 31958c2ecf20Sopenharmony_ci 31968c2ecf20Sopenharmony_ci if (ah && ah->hw) 31978c2ecf20Sopenharmony_ci printk("%s" pr_fmt("%s: %pV"), 31988c2ecf20Sopenharmony_ci level, wiphy_name(ah->hw->wiphy), &vaf); 31998c2ecf20Sopenharmony_ci else 32008c2ecf20Sopenharmony_ci printk("%s" pr_fmt("%pV"), level, &vaf); 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci va_end(args); 32038c2ecf20Sopenharmony_ci} 3204