18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Contact Information: 78c2ecf20Sopenharmony_ci * Intel Linux Wireless <ilw@linux.intel.com> 88c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 98c2ecf20Sopenharmony_ci *****************************************************************************/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/sched.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/lockdep.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 228c2ecf20Sopenharmony_ci#include <net/mac80211.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "common.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciint 278c2ecf20Sopenharmony_ci_il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci const int interval = 10; /* microseconds */ 308c2ecf20Sopenharmony_ci int t = 0; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci do { 338c2ecf20Sopenharmony_ci if ((_il_rd(il, addr) & mask) == (bits & mask)) 348c2ecf20Sopenharmony_ci return t; 358c2ecf20Sopenharmony_ci udelay(interval); 368c2ecf20Sopenharmony_ci t += interval; 378c2ecf20Sopenharmony_ci } while (t < timeout); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return -ETIMEDOUT; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(_il_poll_bit); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_civoid 448c2ecf20Sopenharmony_ciil_set_bit(struct il_priv *p, u32 r, u32 m) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci unsigned long reg_flags; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->reg_lock, reg_flags); 498c2ecf20Sopenharmony_ci _il_set_bit(p, r, m); 508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->reg_lock, reg_flags); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_bit); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_civoid 558c2ecf20Sopenharmony_ciil_clear_bit(struct il_priv *p, u32 r, u32 m) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci unsigned long reg_flags; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->reg_lock, reg_flags); 608c2ecf20Sopenharmony_ci _il_clear_bit(p, r, m); 618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->reg_lock, reg_flags); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_clear_bit); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cibool 668c2ecf20Sopenharmony_ci_il_grab_nic_access(struct il_priv *il) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci u32 val; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* this bit wakes up the NIC */ 728c2ecf20Sopenharmony_ci _il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * These bits say the device is running, and should keep running for 768c2ecf20Sopenharmony_ci * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), 778c2ecf20Sopenharmony_ci * but they do not indicate that embedded SRAM is restored yet; 788c2ecf20Sopenharmony_ci * 3945 and 4965 have volatile SRAM, and must save/restore contents 798c2ecf20Sopenharmony_ci * to/from host DRAM when sleeping/waking for power-saving. 808c2ecf20Sopenharmony_ci * Each direction takes approximately 1/4 millisecond; with this 818c2ecf20Sopenharmony_ci * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a 828c2ecf20Sopenharmony_ci * series of register accesses are expected (e.g. reading Event Log), 838c2ecf20Sopenharmony_ci * to keep device from sleeping. 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that 868c2ecf20Sopenharmony_ci * SRAM is okay/restored. We don't check that here because this call 878c2ecf20Sopenharmony_ci * is just for hardware register access; but GP1 MAC_SLEEP check is a 888c2ecf20Sopenharmony_ci * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci ret = 928c2ecf20Sopenharmony_ci _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, 938c2ecf20Sopenharmony_ci (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | 948c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); 958c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 968c2ecf20Sopenharmony_ci val = _il_rd(il, CSR_GP_CNTRL); 978c2ecf20Sopenharmony_ci WARN_ONCE(1, "Timeout waiting for ucode processor access " 988c2ecf20Sopenharmony_ci "(CSR_GP_CNTRL 0x%08x)\n", val); 998c2ecf20Sopenharmony_ci _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); 1008c2ecf20Sopenharmony_ci return false; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return true; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(_il_grab_nic_access); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ciint 1088c2ecf20Sopenharmony_ciil_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci const int interval = 10; /* microseconds */ 1118c2ecf20Sopenharmony_ci int t = 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci do { 1148c2ecf20Sopenharmony_ci if ((il_rd(il, addr) & mask) == mask) 1158c2ecf20Sopenharmony_ci return t; 1168c2ecf20Sopenharmony_ci udelay(interval); 1178c2ecf20Sopenharmony_ci t += interval; 1188c2ecf20Sopenharmony_ci } while (t < timeout); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_poll_bit); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ciu32 1258c2ecf20Sopenharmony_ciil_rd_prph(struct il_priv *il, u32 reg) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci unsigned long reg_flags; 1288c2ecf20Sopenharmony_ci u32 val; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, reg_flags); 1318c2ecf20Sopenharmony_ci _il_grab_nic_access(il); 1328c2ecf20Sopenharmony_ci val = _il_rd_prph(il, reg); 1338c2ecf20Sopenharmony_ci _il_release_nic_access(il); 1348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, reg_flags); 1358c2ecf20Sopenharmony_ci return val; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_rd_prph); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_civoid 1408c2ecf20Sopenharmony_ciil_wr_prph(struct il_priv *il, u32 addr, u32 val) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci unsigned long reg_flags; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, reg_flags); 1458c2ecf20Sopenharmony_ci if (likely(_il_grab_nic_access(il))) { 1468c2ecf20Sopenharmony_ci _il_wr_prph(il, addr, val); 1478c2ecf20Sopenharmony_ci _il_release_nic_access(il); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, reg_flags); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_wr_prph); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciu32 1548c2ecf20Sopenharmony_ciil_read_targ_mem(struct il_priv *il, u32 addr) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci unsigned long reg_flags; 1578c2ecf20Sopenharmony_ci u32 value; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, reg_flags); 1608c2ecf20Sopenharmony_ci _il_grab_nic_access(il); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci _il_wr(il, HBUS_TARG_MEM_RADDR, addr); 1638c2ecf20Sopenharmony_ci value = _il_rd(il, HBUS_TARG_MEM_RDAT); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci _il_release_nic_access(il); 1668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, reg_flags); 1678c2ecf20Sopenharmony_ci return value; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_read_targ_mem); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_civoid 1728c2ecf20Sopenharmony_ciil_write_targ_mem(struct il_priv *il, u32 addr, u32 val) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci unsigned long reg_flags; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, reg_flags); 1778c2ecf20Sopenharmony_ci if (likely(_il_grab_nic_access(il))) { 1788c2ecf20Sopenharmony_ci _il_wr(il, HBUS_TARG_MEM_WADDR, addr); 1798c2ecf20Sopenharmony_ci _il_wr(il, HBUS_TARG_MEM_WDAT, val); 1808c2ecf20Sopenharmony_ci _il_release_nic_access(il); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, reg_flags); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_write_targ_mem); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciconst char * 1878c2ecf20Sopenharmony_ciil_get_cmd_string(u8 cmd) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci switch (cmd) { 1908c2ecf20Sopenharmony_ci IL_CMD(N_ALIVE); 1918c2ecf20Sopenharmony_ci IL_CMD(N_ERROR); 1928c2ecf20Sopenharmony_ci IL_CMD(C_RXON); 1938c2ecf20Sopenharmony_ci IL_CMD(C_RXON_ASSOC); 1948c2ecf20Sopenharmony_ci IL_CMD(C_QOS_PARAM); 1958c2ecf20Sopenharmony_ci IL_CMD(C_RXON_TIMING); 1968c2ecf20Sopenharmony_ci IL_CMD(C_ADD_STA); 1978c2ecf20Sopenharmony_ci IL_CMD(C_REM_STA); 1988c2ecf20Sopenharmony_ci IL_CMD(C_WEPKEY); 1998c2ecf20Sopenharmony_ci IL_CMD(N_3945_RX); 2008c2ecf20Sopenharmony_ci IL_CMD(C_TX); 2018c2ecf20Sopenharmony_ci IL_CMD(C_RATE_SCALE); 2028c2ecf20Sopenharmony_ci IL_CMD(C_LEDS); 2038c2ecf20Sopenharmony_ci IL_CMD(C_TX_LINK_QUALITY_CMD); 2048c2ecf20Sopenharmony_ci IL_CMD(C_CHANNEL_SWITCH); 2058c2ecf20Sopenharmony_ci IL_CMD(N_CHANNEL_SWITCH); 2068c2ecf20Sopenharmony_ci IL_CMD(C_SPECTRUM_MEASUREMENT); 2078c2ecf20Sopenharmony_ci IL_CMD(N_SPECTRUM_MEASUREMENT); 2088c2ecf20Sopenharmony_ci IL_CMD(C_POWER_TBL); 2098c2ecf20Sopenharmony_ci IL_CMD(N_PM_SLEEP); 2108c2ecf20Sopenharmony_ci IL_CMD(N_PM_DEBUG_STATS); 2118c2ecf20Sopenharmony_ci IL_CMD(C_SCAN); 2128c2ecf20Sopenharmony_ci IL_CMD(C_SCAN_ABORT); 2138c2ecf20Sopenharmony_ci IL_CMD(N_SCAN_START); 2148c2ecf20Sopenharmony_ci IL_CMD(N_SCAN_RESULTS); 2158c2ecf20Sopenharmony_ci IL_CMD(N_SCAN_COMPLETE); 2168c2ecf20Sopenharmony_ci IL_CMD(N_BEACON); 2178c2ecf20Sopenharmony_ci IL_CMD(C_TX_BEACON); 2188c2ecf20Sopenharmony_ci IL_CMD(C_TX_PWR_TBL); 2198c2ecf20Sopenharmony_ci IL_CMD(C_BT_CONFIG); 2208c2ecf20Sopenharmony_ci IL_CMD(C_STATS); 2218c2ecf20Sopenharmony_ci IL_CMD(N_STATS); 2228c2ecf20Sopenharmony_ci IL_CMD(N_CARD_STATE); 2238c2ecf20Sopenharmony_ci IL_CMD(N_MISSED_BEACONS); 2248c2ecf20Sopenharmony_ci IL_CMD(C_CT_KILL_CONFIG); 2258c2ecf20Sopenharmony_ci IL_CMD(C_SENSITIVITY); 2268c2ecf20Sopenharmony_ci IL_CMD(C_PHY_CALIBRATION); 2278c2ecf20Sopenharmony_ci IL_CMD(N_RX_PHY); 2288c2ecf20Sopenharmony_ci IL_CMD(N_RX_MPDU); 2298c2ecf20Sopenharmony_ci IL_CMD(N_RX); 2308c2ecf20Sopenharmony_ci IL_CMD(N_COMPRESSED_BA); 2318c2ecf20Sopenharmony_ci default: 2328c2ecf20Sopenharmony_ci return "UNKNOWN"; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_cmd_string); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci#define HOST_COMPLETE_TIMEOUT (HZ / 2) 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic void 2418c2ecf20Sopenharmony_ciil_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd, 2428c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { 2458c2ecf20Sopenharmony_ci IL_ERR("Bad return from %s (0x%08X)\n", 2468c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); 2478c2ecf20Sopenharmony_ci return; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 2508c2ecf20Sopenharmony_ci switch (cmd->hdr.cmd) { 2518c2ecf20Sopenharmony_ci case C_TX_LINK_QUALITY_CMD: 2528c2ecf20Sopenharmony_ci case C_SENSITIVITY: 2538c2ecf20Sopenharmony_ci D_HC_DUMP("back from %s (0x%08X)\n", 2548c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci default: 2578c2ecf20Sopenharmony_ci D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd), 2588c2ecf20Sopenharmony_ci pkt->hdr.flags); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci#endif 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int 2648c2ecf20Sopenharmony_ciil_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci int ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci BUG_ON(!(cmd->flags & CMD_ASYNC)); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* An asynchronous command can not expect an SKB to be set. */ 2718c2ecf20Sopenharmony_ci BUG_ON(cmd->flags & CMD_WANT_SKB); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Assign a generic callback if one is not provided */ 2748c2ecf20Sopenharmony_ci if (!cmd->callback) 2758c2ecf20Sopenharmony_ci cmd->callback = il_generic_cmd_callback; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 2788c2ecf20Sopenharmony_ci return -EBUSY; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ret = il_enqueue_hcmd(il, cmd); 2818c2ecf20Sopenharmony_ci if (ret < 0) { 2828c2ecf20Sopenharmony_ci IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", 2838c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id), ret); 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciint 2908c2ecf20Sopenharmony_ciil_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int cmd_idx; 2938c2ecf20Sopenharmony_ci int ret; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci BUG_ON(cmd->flags & CMD_ASYNC); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* A synchronous command can not have a callback set. */ 3008c2ecf20Sopenharmony_ci BUG_ON(cmd->callback); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci D_INFO("Attempting to send sync command %s\n", 3038c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id)); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci set_bit(S_HCMD_ACTIVE, &il->status); 3068c2ecf20Sopenharmony_ci D_INFO("Setting HCMD_ACTIVE for command %s\n", 3078c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id)); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci cmd_idx = il_enqueue_hcmd(il, cmd); 3108c2ecf20Sopenharmony_ci if (cmd_idx < 0) { 3118c2ecf20Sopenharmony_ci ret = cmd_idx; 3128c2ecf20Sopenharmony_ci IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", 3138c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id), ret); 3148c2ecf20Sopenharmony_ci goto out; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = wait_event_timeout(il->wait_command_queue, 3188c2ecf20Sopenharmony_ci !test_bit(S_HCMD_ACTIVE, &il->status), 3198c2ecf20Sopenharmony_ci HOST_COMPLETE_TIMEOUT); 3208c2ecf20Sopenharmony_ci if (!ret) { 3218c2ecf20Sopenharmony_ci if (test_bit(S_HCMD_ACTIVE, &il->status)) { 3228c2ecf20Sopenharmony_ci IL_ERR("Error sending %s: time out after %dms.\n", 3238c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id), 3248c2ecf20Sopenharmony_ci jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci clear_bit(S_HCMD_ACTIVE, &il->status); 3278c2ecf20Sopenharmony_ci D_INFO("Clearing HCMD_ACTIVE for command %s\n", 3288c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id)); 3298c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3308c2ecf20Sopenharmony_ci goto cancel; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (test_bit(S_RFKILL, &il->status)) { 3358c2ecf20Sopenharmony_ci IL_ERR("Command %s aborted: RF KILL Switch\n", 3368c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id)); 3378c2ecf20Sopenharmony_ci ret = -ECANCELED; 3388c2ecf20Sopenharmony_ci goto fail; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci if (test_bit(S_FW_ERROR, &il->status)) { 3418c2ecf20Sopenharmony_ci IL_ERR("Command %s failed: FW Error\n", 3428c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id)); 3438c2ecf20Sopenharmony_ci ret = -EIO; 3448c2ecf20Sopenharmony_ci goto fail; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { 3478c2ecf20Sopenharmony_ci IL_ERR("Error: Response NULL in '%s'\n", 3488c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->id)); 3498c2ecf20Sopenharmony_ci ret = -EIO; 3508c2ecf20Sopenharmony_ci goto cancel; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci ret = 0; 3548c2ecf20Sopenharmony_ci goto out; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cicancel: 3578c2ecf20Sopenharmony_ci if (cmd->flags & CMD_WANT_SKB) { 3588c2ecf20Sopenharmony_ci /* 3598c2ecf20Sopenharmony_ci * Cancel the CMD_WANT_SKB flag for the cmd in the 3608c2ecf20Sopenharmony_ci * TX cmd queue. Otherwise in case the cmd comes 3618c2ecf20Sopenharmony_ci * in later, it will possibly set an invalid 3628c2ecf20Sopenharmony_ci * address (cmd->meta.source). 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ci il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_cifail: 3678c2ecf20Sopenharmony_ci if (cmd->reply_page) { 3688c2ecf20Sopenharmony_ci il_free_pages(il, cmd->reply_page); 3698c2ecf20Sopenharmony_ci cmd->reply_page = 0; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ciout: 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_sync); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ciint 3778c2ecf20Sopenharmony_ciil_send_cmd(struct il_priv *il, struct il_host_cmd *cmd) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci if (cmd->flags & CMD_ASYNC) 3808c2ecf20Sopenharmony_ci return il_send_cmd_async(il, cmd); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return il_send_cmd_sync(il, cmd); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciint 3878c2ecf20Sopenharmony_ciil_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct il_host_cmd cmd = { 3908c2ecf20Sopenharmony_ci .id = id, 3918c2ecf20Sopenharmony_ci .len = len, 3928c2ecf20Sopenharmony_ci .data = data, 3938c2ecf20Sopenharmony_ci }; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return il_send_cmd_sync(il, &cmd); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_pdu); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ciint 4008c2ecf20Sopenharmony_ciil_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data, 4018c2ecf20Sopenharmony_ci void (*callback) (struct il_priv *il, 4028c2ecf20Sopenharmony_ci struct il_device_cmd *cmd, 4038c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt)) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct il_host_cmd cmd = { 4068c2ecf20Sopenharmony_ci .id = id, 4078c2ecf20Sopenharmony_ci .len = len, 4088c2ecf20Sopenharmony_ci .data = data, 4098c2ecf20Sopenharmony_ci }; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci cmd.flags |= CMD_ASYNC; 4128c2ecf20Sopenharmony_ci cmd.callback = callback; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return il_send_cmd_async(il, &cmd); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_pdu_async); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/* default: IL_LED_BLINK(0) using blinking idx table */ 4198c2ecf20Sopenharmony_cistatic int led_mode; 4208c2ecf20Sopenharmony_cimodule_param(led_mode, int, 0444); 4218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(led_mode, 4228c2ecf20Sopenharmony_ci "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking"); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/* Throughput OFF time(ms) ON time (ms) 4258c2ecf20Sopenharmony_ci * >300 25 25 4268c2ecf20Sopenharmony_ci * >200 to 300 40 40 4278c2ecf20Sopenharmony_ci * >100 to 200 55 55 4288c2ecf20Sopenharmony_ci * >70 to 100 65 65 4298c2ecf20Sopenharmony_ci * >50 to 70 75 75 4308c2ecf20Sopenharmony_ci * >20 to 50 85 85 4318c2ecf20Sopenharmony_ci * >10 to 20 95 95 4328c2ecf20Sopenharmony_ci * >5 to 10 110 110 4338c2ecf20Sopenharmony_ci * >1 to 5 130 130 4348c2ecf20Sopenharmony_ci * >0 to 1 167 167 4358c2ecf20Sopenharmony_ci * <=0 SOLID ON 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_cistatic const struct ieee80211_tpt_blink il_blink[] = { 4388c2ecf20Sopenharmony_ci {.throughput = 0, .blink_time = 334}, 4398c2ecf20Sopenharmony_ci {.throughput = 1 * 1024 - 1, .blink_time = 260}, 4408c2ecf20Sopenharmony_ci {.throughput = 5 * 1024 - 1, .blink_time = 220}, 4418c2ecf20Sopenharmony_ci {.throughput = 10 * 1024 - 1, .blink_time = 190}, 4428c2ecf20Sopenharmony_ci {.throughput = 20 * 1024 - 1, .blink_time = 170}, 4438c2ecf20Sopenharmony_ci {.throughput = 50 * 1024 - 1, .blink_time = 150}, 4448c2ecf20Sopenharmony_ci {.throughput = 70 * 1024 - 1, .blink_time = 130}, 4458c2ecf20Sopenharmony_ci {.throughput = 100 * 1024 - 1, .blink_time = 110}, 4468c2ecf20Sopenharmony_ci {.throughput = 200 * 1024 - 1, .blink_time = 80}, 4478c2ecf20Sopenharmony_ci {.throughput = 300 * 1024 - 1, .blink_time = 50}, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/* 4518c2ecf20Sopenharmony_ci * Adjust led blink rate to compensate on a MAC Clock difference on every HW 4528c2ecf20Sopenharmony_ci * Led blink rate analysis showed an average deviation of 0% on 3945, 4538c2ecf20Sopenharmony_ci * 5% on 4965 HW. 4548c2ecf20Sopenharmony_ci * Need to compensate on the led on/off time per HW according to the deviation 4558c2ecf20Sopenharmony_ci * to achieve the desired led frequency 4568c2ecf20Sopenharmony_ci * The calculation is: (100-averageDeviation)/100 * blinkTime 4578c2ecf20Sopenharmony_ci * For code efficiency the calculation will be: 4588c2ecf20Sopenharmony_ci * compensation = (100 - averageDeviation) * 64 / 100 4598c2ecf20Sopenharmony_ci * NewBlinkTime = (compensation * BlinkTime) / 64 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_cistatic inline u8 4628c2ecf20Sopenharmony_ciil_blink_compensation(struct il_priv *il, u8 time, u16 compensation) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci if (!compensation) { 4658c2ecf20Sopenharmony_ci IL_ERR("undefined blink compensation: " 4668c2ecf20Sopenharmony_ci "use pre-defined blinking time\n"); 4678c2ecf20Sopenharmony_ci return time; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return (u8) ((time * compensation) >> 6); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* Set led pattern command */ 4748c2ecf20Sopenharmony_cistatic int 4758c2ecf20Sopenharmony_ciil_led_cmd(struct il_priv *il, unsigned long on, unsigned long off) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct il_led_cmd led_cmd = { 4788c2ecf20Sopenharmony_ci .id = IL_LED_LINK, 4798c2ecf20Sopenharmony_ci .interval = IL_DEF_LED_INTRVL 4808c2ecf20Sopenharmony_ci }; 4818c2ecf20Sopenharmony_ci int ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!test_bit(S_READY, &il->status)) 4848c2ecf20Sopenharmony_ci return -EBUSY; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (il->blink_on == on && il->blink_off == off) 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (off == 0) { 4908c2ecf20Sopenharmony_ci /* led is SOLID_ON */ 4918c2ecf20Sopenharmony_ci on = IL_LED_SOLID; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci D_LED("Led blink time compensation=%u\n", 4958c2ecf20Sopenharmony_ci il->cfg->led_compensation); 4968c2ecf20Sopenharmony_ci led_cmd.on = 4978c2ecf20Sopenharmony_ci il_blink_compensation(il, on, 4988c2ecf20Sopenharmony_ci il->cfg->led_compensation); 4998c2ecf20Sopenharmony_ci led_cmd.off = 5008c2ecf20Sopenharmony_ci il_blink_compensation(il, off, 5018c2ecf20Sopenharmony_ci il->cfg->led_compensation); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ret = il->ops->send_led_cmd(il, &led_cmd); 5048c2ecf20Sopenharmony_ci if (!ret) { 5058c2ecf20Sopenharmony_ci il->blink_on = on; 5068c2ecf20Sopenharmony_ci il->blink_off = off; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci return ret; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic void 5128c2ecf20Sopenharmony_ciil_led_brightness_set(struct led_classdev *led_cdev, 5138c2ecf20Sopenharmony_ci enum led_brightness brightness) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct il_priv *il = container_of(led_cdev, struct il_priv, led); 5168c2ecf20Sopenharmony_ci unsigned long on = 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (brightness > 0) 5198c2ecf20Sopenharmony_ci on = IL_LED_SOLID; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci il_led_cmd(il, on, 0); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic int 5258c2ecf20Sopenharmony_ciil_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, 5268c2ecf20Sopenharmony_ci unsigned long *delay_off) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct il_priv *il = container_of(led_cdev, struct il_priv, led); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return il_led_cmd(il, *delay_on, *delay_off); 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_civoid 5348c2ecf20Sopenharmony_ciil_leds_init(struct il_priv *il) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci int mode = led_mode; 5378c2ecf20Sopenharmony_ci int ret; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (mode == IL_LED_DEFAULT) 5408c2ecf20Sopenharmony_ci mode = il->cfg->led_mode; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci il->led.name = 5438c2ecf20Sopenharmony_ci kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy)); 5448c2ecf20Sopenharmony_ci il->led.brightness_set = il_led_brightness_set; 5458c2ecf20Sopenharmony_ci il->led.blink_set = il_led_blink_set; 5468c2ecf20Sopenharmony_ci il->led.max_brightness = 1; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci switch (mode) { 5498c2ecf20Sopenharmony_ci case IL_LED_DEFAULT: 5508c2ecf20Sopenharmony_ci WARN_ON(1); 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci case IL_LED_BLINK: 5538c2ecf20Sopenharmony_ci il->led.default_trigger = 5548c2ecf20Sopenharmony_ci ieee80211_create_tpt_led_trigger(il->hw, 5558c2ecf20Sopenharmony_ci IEEE80211_TPT_LEDTRIG_FL_CONNECTED, 5568c2ecf20Sopenharmony_ci il_blink, 5578c2ecf20Sopenharmony_ci ARRAY_SIZE(il_blink)); 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci case IL_LED_RF_STATE: 5608c2ecf20Sopenharmony_ci il->led.default_trigger = ieee80211_get_radio_led_name(il->hw); 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci ret = led_classdev_register(&il->pci_dev->dev, &il->led); 5658c2ecf20Sopenharmony_ci if (ret) { 5668c2ecf20Sopenharmony_ci kfree(il->led.name); 5678c2ecf20Sopenharmony_ci return; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci il->led_registered = true; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_leds_init); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_civoid 5758c2ecf20Sopenharmony_ciil_leds_exit(struct il_priv *il) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci if (!il->led_registered) 5788c2ecf20Sopenharmony_ci return; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci led_classdev_unregister(&il->led); 5818c2ecf20Sopenharmony_ci kfree(il->led.name); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_leds_exit); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/************************** EEPROM BANDS **************************** 5868c2ecf20Sopenharmony_ci * 5878c2ecf20Sopenharmony_ci * The il_eeprom_band definitions below provide the mapping from the 5888c2ecf20Sopenharmony_ci * EEPROM contents to the specific channel number supported for each 5898c2ecf20Sopenharmony_ci * band. 5908c2ecf20Sopenharmony_ci * 5918c2ecf20Sopenharmony_ci * For example, il_priv->eeprom.band_3_channels[4] from the band_3 5928c2ecf20Sopenharmony_ci * definition below maps to physical channel 42 in the 5.2GHz spectrum. 5938c2ecf20Sopenharmony_ci * The specific geography and calibration information for that channel 5948c2ecf20Sopenharmony_ci * is contained in the eeprom map itself. 5958c2ecf20Sopenharmony_ci * 5968c2ecf20Sopenharmony_ci * During init, we copy the eeprom information and channel map 5978c2ecf20Sopenharmony_ci * information into il->channel_info_24/52 and il->channel_map_24/52 5988c2ecf20Sopenharmony_ci * 5998c2ecf20Sopenharmony_ci * channel_map_24/52 provides the idx in the channel_info array for a 6008c2ecf20Sopenharmony_ci * given channel. We have to have two separate maps as there is channel 6018c2ecf20Sopenharmony_ci * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and 6028c2ecf20Sopenharmony_ci * band_2 6038c2ecf20Sopenharmony_ci * 6048c2ecf20Sopenharmony_ci * A value of 0xff stored in the channel_map indicates that the channel 6058c2ecf20Sopenharmony_ci * is not supported by the hardware at all. 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * A value of 0xfe in the channel_map indicates that the channel is not 6088c2ecf20Sopenharmony_ci * valid for Tx with the current hardware. This means that 6098c2ecf20Sopenharmony_ci * while the system can tune and receive on a given channel, it may not 6108c2ecf20Sopenharmony_ci * be able to associate or transmit any frames on that 6118c2ecf20Sopenharmony_ci * channel. There is no corresponding channel information for that 6128c2ecf20Sopenharmony_ci * entry. 6138c2ecf20Sopenharmony_ci * 6148c2ecf20Sopenharmony_ci *********************************************************************/ 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci/* 2.4 GHz */ 6178c2ecf20Sopenharmony_ciconst u8 il_eeprom_band_1[14] = { 6188c2ecf20Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 6198c2ecf20Sopenharmony_ci}; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci/* 5.2 GHz bands */ 6228c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_2[] = { /* 4915-5080MHz */ 6238c2ecf20Sopenharmony_ci 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 6248c2ecf20Sopenharmony_ci}; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_3[] = { /* 5170-5320MHz */ 6278c2ecf20Sopenharmony_ci 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_4[] = { /* 5500-5700MHz */ 6318c2ecf20Sopenharmony_ci 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 6328c2ecf20Sopenharmony_ci}; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_5[] = { /* 5725-5825MHz */ 6358c2ecf20Sopenharmony_ci 145, 149, 153, 157, 161, 165 6368c2ecf20Sopenharmony_ci}; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_6[] = { /* 2.4 ht40 channel */ 6398c2ecf20Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_7[] = { /* 5.2 ht40 channel */ 6438c2ecf20Sopenharmony_ci 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 6448c2ecf20Sopenharmony_ci}; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/****************************************************************************** 6478c2ecf20Sopenharmony_ci * 6488c2ecf20Sopenharmony_ci * EEPROM related functions 6498c2ecf20Sopenharmony_ci * 6508c2ecf20Sopenharmony_ci******************************************************************************/ 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic int 6538c2ecf20Sopenharmony_ciil_eeprom_verify_signature(struct il_priv *il) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; 6568c2ecf20Sopenharmony_ci int ret = 0; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci D_EEPROM("EEPROM signature=0x%08x\n", gp); 6598c2ecf20Sopenharmony_ci switch (gp) { 6608c2ecf20Sopenharmony_ci case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: 6618c2ecf20Sopenharmony_ci case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci default: 6648c2ecf20Sopenharmony_ci IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp); 6658c2ecf20Sopenharmony_ci ret = -ENOENT; 6668c2ecf20Sopenharmony_ci break; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci return ret; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ciconst u8 * 6728c2ecf20Sopenharmony_ciil_eeprom_query_addr(const struct il_priv *il, size_t offset) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci BUG_ON(offset >= il->cfg->eeprom_size); 6758c2ecf20Sopenharmony_ci return &il->eeprom[offset]; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_query_addr); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ciu16 6808c2ecf20Sopenharmony_ciil_eeprom_query16(const struct il_priv *il, size_t offset) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci if (!il->eeprom) 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_query16); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/* 6898c2ecf20Sopenharmony_ci * il_eeprom_init - read EEPROM contents 6908c2ecf20Sopenharmony_ci * 6918c2ecf20Sopenharmony_ci * Load the EEPROM contents from adapter into il->eeprom 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * NOTE: This routine uses the non-debug IO access functions. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ciint 6968c2ecf20Sopenharmony_ciil_eeprom_init(struct il_priv *il) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci __le16 *e; 6998c2ecf20Sopenharmony_ci u32 gp = _il_rd(il, CSR_EEPROM_GP); 7008c2ecf20Sopenharmony_ci int sz; 7018c2ecf20Sopenharmony_ci int ret; 7028c2ecf20Sopenharmony_ci int addr; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* allocate eeprom */ 7058c2ecf20Sopenharmony_ci sz = il->cfg->eeprom_size; 7068c2ecf20Sopenharmony_ci D_EEPROM("NVM size = %d\n", sz); 7078c2ecf20Sopenharmony_ci il->eeprom = kzalloc(sz, GFP_KERNEL); 7088c2ecf20Sopenharmony_ci if (!il->eeprom) 7098c2ecf20Sopenharmony_ci return -ENOMEM; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci e = (__le16 *) il->eeprom; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci il->ops->apm_init(il); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci ret = il_eeprom_verify_signature(il); 7168c2ecf20Sopenharmony_ci if (ret < 0) { 7178c2ecf20Sopenharmony_ci IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp); 7188c2ecf20Sopenharmony_ci ret = -ENOENT; 7198c2ecf20Sopenharmony_ci goto err; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Make sure driver (instead of uCode) is allowed to read EEPROM */ 7238c2ecf20Sopenharmony_ci ret = il->ops->eeprom_acquire_semaphore(il); 7248c2ecf20Sopenharmony_ci if (ret < 0) { 7258c2ecf20Sopenharmony_ci IL_ERR("Failed to acquire EEPROM semaphore.\n"); 7268c2ecf20Sopenharmony_ci ret = -ENOENT; 7278c2ecf20Sopenharmony_ci goto err; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* eeprom is an array of 16bit values */ 7318c2ecf20Sopenharmony_ci for (addr = 0; addr < sz; addr += sizeof(u16)) { 7328c2ecf20Sopenharmony_ci u32 r; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci _il_wr(il, CSR_EEPROM_REG, 7358c2ecf20Sopenharmony_ci CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci ret = 7388c2ecf20Sopenharmony_ci _il_poll_bit(il, CSR_EEPROM_REG, 7398c2ecf20Sopenharmony_ci CSR_EEPROM_REG_READ_VALID_MSK, 7408c2ecf20Sopenharmony_ci CSR_EEPROM_REG_READ_VALID_MSK, 7418c2ecf20Sopenharmony_ci IL_EEPROM_ACCESS_TIMEOUT); 7428c2ecf20Sopenharmony_ci if (ret < 0) { 7438c2ecf20Sopenharmony_ci IL_ERR("Time out reading EEPROM[%d]\n", addr); 7448c2ecf20Sopenharmony_ci goto done; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci r = _il_rd(il, CSR_EEPROM_REG); 7478c2ecf20Sopenharmony_ci e[addr / 2] = cpu_to_le16(r >> 16); 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci D_EEPROM("NVM Type: %s, version: 0x%x\n", "EEPROM", 7518c2ecf20Sopenharmony_ci il_eeprom_query16(il, EEPROM_VERSION)); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ret = 0; 7548c2ecf20Sopenharmony_cidone: 7558c2ecf20Sopenharmony_ci il->ops->eeprom_release_semaphore(il); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cierr: 7588c2ecf20Sopenharmony_ci if (ret) 7598c2ecf20Sopenharmony_ci il_eeprom_free(il); 7608c2ecf20Sopenharmony_ci /* Reset chip to save power until we load uCode during "up". */ 7618c2ecf20Sopenharmony_ci il_apm_stop(il); 7628c2ecf20Sopenharmony_ci return ret; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_init); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_civoid 7678c2ecf20Sopenharmony_ciil_eeprom_free(struct il_priv *il) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci kfree(il->eeprom); 7708c2ecf20Sopenharmony_ci il->eeprom = NULL; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_free); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic void 7758c2ecf20Sopenharmony_ciil_init_band_reference(const struct il_priv *il, int eep_band, 7768c2ecf20Sopenharmony_ci int *eeprom_ch_count, 7778c2ecf20Sopenharmony_ci const struct il_eeprom_channel **eeprom_ch_info, 7788c2ecf20Sopenharmony_ci const u8 **eeprom_ch_idx) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci u32 offset = il->cfg->regulatory_bands[eep_band - 1]; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci switch (eep_band) { 7838c2ecf20Sopenharmony_ci case 1: /* 2.4GHz band */ 7848c2ecf20Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1); 7858c2ecf20Sopenharmony_ci *eeprom_ch_info = 7868c2ecf20Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 7878c2ecf20Sopenharmony_ci offset); 7888c2ecf20Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_1; 7898c2ecf20Sopenharmony_ci break; 7908c2ecf20Sopenharmony_ci case 2: /* 4.9GHz band */ 7918c2ecf20Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2); 7928c2ecf20Sopenharmony_ci *eeprom_ch_info = 7938c2ecf20Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 7948c2ecf20Sopenharmony_ci offset); 7958c2ecf20Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_2; 7968c2ecf20Sopenharmony_ci break; 7978c2ecf20Sopenharmony_ci case 3: /* 5.2GHz band */ 7988c2ecf20Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3); 7998c2ecf20Sopenharmony_ci *eeprom_ch_info = 8008c2ecf20Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 8018c2ecf20Sopenharmony_ci offset); 8028c2ecf20Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_3; 8038c2ecf20Sopenharmony_ci break; 8048c2ecf20Sopenharmony_ci case 4: /* 5.5GHz band */ 8058c2ecf20Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4); 8068c2ecf20Sopenharmony_ci *eeprom_ch_info = 8078c2ecf20Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 8088c2ecf20Sopenharmony_ci offset); 8098c2ecf20Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_4; 8108c2ecf20Sopenharmony_ci break; 8118c2ecf20Sopenharmony_ci case 5: /* 5.7GHz band */ 8128c2ecf20Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5); 8138c2ecf20Sopenharmony_ci *eeprom_ch_info = 8148c2ecf20Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 8158c2ecf20Sopenharmony_ci offset); 8168c2ecf20Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_5; 8178c2ecf20Sopenharmony_ci break; 8188c2ecf20Sopenharmony_ci case 6: /* 2.4GHz ht40 channels */ 8198c2ecf20Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6); 8208c2ecf20Sopenharmony_ci *eeprom_ch_info = 8218c2ecf20Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 8228c2ecf20Sopenharmony_ci offset); 8238c2ecf20Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_6; 8248c2ecf20Sopenharmony_ci break; 8258c2ecf20Sopenharmony_ci case 7: /* 5 GHz ht40 channels */ 8268c2ecf20Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7); 8278c2ecf20Sopenharmony_ci *eeprom_ch_info = 8288c2ecf20Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 8298c2ecf20Sopenharmony_ci offset); 8308c2ecf20Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_7; 8318c2ecf20Sopenharmony_ci break; 8328c2ecf20Sopenharmony_ci default: 8338c2ecf20Sopenharmony_ci BUG(); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ 8388c2ecf20Sopenharmony_ci ? # x " " : "") 8398c2ecf20Sopenharmony_ci/* 8408c2ecf20Sopenharmony_ci * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il. 8418c2ecf20Sopenharmony_ci * 8428c2ecf20Sopenharmony_ci * Does not set up a command, or touch hardware. 8438c2ecf20Sopenharmony_ci */ 8448c2ecf20Sopenharmony_cistatic int 8458c2ecf20Sopenharmony_ciil_mod_ht40_chan_info(struct il_priv *il, enum nl80211_band band, u16 channel, 8468c2ecf20Sopenharmony_ci const struct il_eeprom_channel *eeprom_ch, 8478c2ecf20Sopenharmony_ci u8 clear_ht40_extension_channel) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci struct il_channel_info *ch_info; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ch_info = 8528c2ecf20Sopenharmony_ci (struct il_channel_info *)il_get_channel_info(il, band, channel); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (!il_is_channel_valid(ch_info)) 8558c2ecf20Sopenharmony_ci return -1; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" 8588c2ecf20Sopenharmony_ci " Ad-Hoc %ssupported\n", ch_info->channel, 8598c2ecf20Sopenharmony_ci il_is_channel_a_band(ch_info) ? "5.2" : "2.4", 8608c2ecf20Sopenharmony_ci CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE), 8618c2ecf20Sopenharmony_ci CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE), 8628c2ecf20Sopenharmony_ci CHECK_AND_PRINT(DFS), eeprom_ch->flags, 8638c2ecf20Sopenharmony_ci eeprom_ch->max_power_avg, 8648c2ecf20Sopenharmony_ci ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && 8658c2ecf20Sopenharmony_ci !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci ch_info->ht40_eeprom = *eeprom_ch; 8688c2ecf20Sopenharmony_ci ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; 8698c2ecf20Sopenharmony_ci ch_info->ht40_flags = eeprom_ch->flags; 8708c2ecf20Sopenharmony_ci if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) 8718c2ecf20Sopenharmony_ci ch_info->ht40_extension_channel &= 8728c2ecf20Sopenharmony_ci ~clear_ht40_extension_channel; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ 8788c2ecf20Sopenharmony_ci ? # x " " : "") 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci/* 8818c2ecf20Sopenharmony_ci * il_init_channel_map - Set up driver's info for all possible channels 8828c2ecf20Sopenharmony_ci */ 8838c2ecf20Sopenharmony_ciint 8848c2ecf20Sopenharmony_ciil_init_channel_map(struct il_priv *il) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci int eeprom_ch_count = 0; 8878c2ecf20Sopenharmony_ci const u8 *eeprom_ch_idx = NULL; 8888c2ecf20Sopenharmony_ci const struct il_eeprom_channel *eeprom_ch_info = NULL; 8898c2ecf20Sopenharmony_ci int band, ch; 8908c2ecf20Sopenharmony_ci struct il_channel_info *ch_info; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (il->channel_count) { 8938c2ecf20Sopenharmony_ci D_EEPROM("Channel map already initialized.\n"); 8948c2ecf20Sopenharmony_ci return 0; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci D_EEPROM("Initializing regulatory info from EEPROM\n"); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci il->channel_count = 9008c2ecf20Sopenharmony_ci ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) + 9018c2ecf20Sopenharmony_ci ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) + 9028c2ecf20Sopenharmony_ci ARRAY_SIZE(il_eeprom_band_5); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci D_EEPROM("Parsing data for %d channels.\n", il->channel_count); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci il->channel_info = 9078c2ecf20Sopenharmony_ci kcalloc(il->channel_count, sizeof(struct il_channel_info), 9088c2ecf20Sopenharmony_ci GFP_KERNEL); 9098c2ecf20Sopenharmony_ci if (!il->channel_info) { 9108c2ecf20Sopenharmony_ci IL_ERR("Could not allocate channel_info\n"); 9118c2ecf20Sopenharmony_ci il->channel_count = 0; 9128c2ecf20Sopenharmony_ci return -ENOMEM; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci ch_info = il->channel_info; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* Loop through the 5 EEPROM bands adding them in order to the 9188c2ecf20Sopenharmony_ci * channel map we maintain (that contains additional information than 9198c2ecf20Sopenharmony_ci * what just in the EEPROM) */ 9208c2ecf20Sopenharmony_ci for (band = 1; band <= 5; band++) { 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci il_init_band_reference(il, band, &eeprom_ch_count, 9238c2ecf20Sopenharmony_ci &eeprom_ch_info, &eeprom_ch_idx); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* Loop through each band adding each of the channels */ 9268c2ecf20Sopenharmony_ci for (ch = 0; ch < eeprom_ch_count; ch++) { 9278c2ecf20Sopenharmony_ci ch_info->channel = eeprom_ch_idx[ch]; 9288c2ecf20Sopenharmony_ci ch_info->band = 9298c2ecf20Sopenharmony_ci (band == 9308c2ecf20Sopenharmony_ci 1) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* permanently store EEPROM's channel regulatory flags 9338c2ecf20Sopenharmony_ci * and max power in channel info database. */ 9348c2ecf20Sopenharmony_ci ch_info->eeprom = eeprom_ch_info[ch]; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci /* Copy the run-time flags so they are there even on 9378c2ecf20Sopenharmony_ci * invalid channels */ 9388c2ecf20Sopenharmony_ci ch_info->flags = eeprom_ch_info[ch].flags; 9398c2ecf20Sopenharmony_ci /* First write that ht40 is not enabled, and then enable 9408c2ecf20Sopenharmony_ci * one by one */ 9418c2ecf20Sopenharmony_ci ch_info->ht40_extension_channel = 9428c2ecf20Sopenharmony_ci IEEE80211_CHAN_NO_HT40; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (!(il_is_channel_valid(ch_info))) { 9458c2ecf20Sopenharmony_ci D_EEPROM("Ch. %d Flags %x [%sGHz] - " 9468c2ecf20Sopenharmony_ci "No traffic\n", ch_info->channel, 9478c2ecf20Sopenharmony_ci ch_info->flags, 9488c2ecf20Sopenharmony_ci il_is_channel_a_band(ch_info) ? "5.2" : 9498c2ecf20Sopenharmony_ci "2.4"); 9508c2ecf20Sopenharmony_ci ch_info++; 9518c2ecf20Sopenharmony_ci continue; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci /* Initialize regulatory-based run-time data */ 9558c2ecf20Sopenharmony_ci ch_info->max_power_avg = ch_info->curr_txpow = 9568c2ecf20Sopenharmony_ci eeprom_ch_info[ch].max_power_avg; 9578c2ecf20Sopenharmony_ci ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; 9588c2ecf20Sopenharmony_ci ch_info->min_power = 0; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):" 9618c2ecf20Sopenharmony_ci " Ad-Hoc %ssupported\n", ch_info->channel, 9628c2ecf20Sopenharmony_ci il_is_channel_a_band(ch_info) ? "5.2" : "2.4", 9638c2ecf20Sopenharmony_ci CHECK_AND_PRINT_I(VALID), 9648c2ecf20Sopenharmony_ci CHECK_AND_PRINT_I(IBSS), 9658c2ecf20Sopenharmony_ci CHECK_AND_PRINT_I(ACTIVE), 9668c2ecf20Sopenharmony_ci CHECK_AND_PRINT_I(RADAR), 9678c2ecf20Sopenharmony_ci CHECK_AND_PRINT_I(WIDE), 9688c2ecf20Sopenharmony_ci CHECK_AND_PRINT_I(DFS), 9698c2ecf20Sopenharmony_ci eeprom_ch_info[ch].flags, 9708c2ecf20Sopenharmony_ci eeprom_ch_info[ch].max_power_avg, 9718c2ecf20Sopenharmony_ci ((eeprom_ch_info[ch]. 9728c2ecf20Sopenharmony_ci flags & EEPROM_CHANNEL_IBSS) && 9738c2ecf20Sopenharmony_ci !(eeprom_ch_info[ch]. 9748c2ecf20Sopenharmony_ci flags & EEPROM_CHANNEL_RADAR)) ? "" : 9758c2ecf20Sopenharmony_ci "not "); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci ch_info++; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* Check if we do have HT40 channels */ 9828c2ecf20Sopenharmony_ci if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 && 9838c2ecf20Sopenharmony_ci il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40) 9848c2ecf20Sopenharmony_ci return 0; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ 9878c2ecf20Sopenharmony_ci for (band = 6; band <= 7; band++) { 9888c2ecf20Sopenharmony_ci enum nl80211_band ieeeband; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci il_init_band_reference(il, band, &eeprom_ch_count, 9918c2ecf20Sopenharmony_ci &eeprom_ch_info, &eeprom_ch_idx); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ 9948c2ecf20Sopenharmony_ci ieeeband = 9958c2ecf20Sopenharmony_ci (band == 6) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* Loop through each band adding each of the channels */ 9988c2ecf20Sopenharmony_ci for (ch = 0; ch < eeprom_ch_count; ch++) { 9998c2ecf20Sopenharmony_ci /* Set up driver's info for lower half */ 10008c2ecf20Sopenharmony_ci il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch], 10018c2ecf20Sopenharmony_ci &eeprom_ch_info[ch], 10028c2ecf20Sopenharmony_ci IEEE80211_CHAN_NO_HT40PLUS); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* Set up driver's info for upper half */ 10058c2ecf20Sopenharmony_ci il_mod_ht40_chan_info(il, ieeeband, 10068c2ecf20Sopenharmony_ci eeprom_ch_idx[ch] + 4, 10078c2ecf20Sopenharmony_ci &eeprom_ch_info[ch], 10088c2ecf20Sopenharmony_ci IEEE80211_CHAN_NO_HT40MINUS); 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci return 0; 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_init_channel_map); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci/* 10178c2ecf20Sopenharmony_ci * il_free_channel_map - undo allocations in il_init_channel_map 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_civoid 10208c2ecf20Sopenharmony_ciil_free_channel_map(struct il_priv *il) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci kfree(il->channel_info); 10238c2ecf20Sopenharmony_ci il->channel_count = 0; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_free_channel_map); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci/* 10288c2ecf20Sopenharmony_ci * il_get_channel_info - Find driver's ilate channel info 10298c2ecf20Sopenharmony_ci * 10308c2ecf20Sopenharmony_ci * Based on band and channel number. 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_ciconst struct il_channel_info * 10338c2ecf20Sopenharmony_ciil_get_channel_info(const struct il_priv *il, enum nl80211_band band, 10348c2ecf20Sopenharmony_ci u16 channel) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci int i; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci switch (band) { 10398c2ecf20Sopenharmony_ci case NL80211_BAND_5GHZ: 10408c2ecf20Sopenharmony_ci for (i = 14; i < il->channel_count; i++) { 10418c2ecf20Sopenharmony_ci if (il->channel_info[i].channel == channel) 10428c2ecf20Sopenharmony_ci return &il->channel_info[i]; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci break; 10458c2ecf20Sopenharmony_ci case NL80211_BAND_2GHZ: 10468c2ecf20Sopenharmony_ci if (channel >= 1 && channel <= 14) 10478c2ecf20Sopenharmony_ci return &il->channel_info[channel - 1]; 10488c2ecf20Sopenharmony_ci break; 10498c2ecf20Sopenharmony_ci default: 10508c2ecf20Sopenharmony_ci BUG(); 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return NULL; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_channel_info); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci/* 10588c2ecf20Sopenharmony_ci * Setting power level allows the card to go to sleep when not busy. 10598c2ecf20Sopenharmony_ci * 10608c2ecf20Sopenharmony_ci * We calculate a sleep command based on the required latency, which 10618c2ecf20Sopenharmony_ci * we get from mac80211. 10628c2ecf20Sopenharmony_ci */ 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci#define SLP_VEC(X0, X1, X2, X3, X4) { \ 10658c2ecf20Sopenharmony_ci cpu_to_le32(X0), \ 10668c2ecf20Sopenharmony_ci cpu_to_le32(X1), \ 10678c2ecf20Sopenharmony_ci cpu_to_le32(X2), \ 10688c2ecf20Sopenharmony_ci cpu_to_le32(X3), \ 10698c2ecf20Sopenharmony_ci cpu_to_le32(X4) \ 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic void 10738c2ecf20Sopenharmony_ciil_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci static const __le32 interval[3][IL_POWER_VEC_SIZE] = { 10768c2ecf20Sopenharmony_ci SLP_VEC(2, 2, 4, 6, 0xFF), 10778c2ecf20Sopenharmony_ci SLP_VEC(2, 4, 7, 10, 10), 10788c2ecf20Sopenharmony_ci SLP_VEC(4, 7, 10, 10, 0xFF) 10798c2ecf20Sopenharmony_ci }; 10808c2ecf20Sopenharmony_ci int i, dtim_period, no_dtim; 10818c2ecf20Sopenharmony_ci u32 max_sleep; 10828c2ecf20Sopenharmony_ci bool skip; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (il->power_data.pci_pm) 10878c2ecf20Sopenharmony_ci cmd->flags |= IL_POWER_PCI_PM_MSK; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* if no Power Save, we are done */ 10908c2ecf20Sopenharmony_ci if (il->power_data.ps_disabled) 10918c2ecf20Sopenharmony_ci return; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK; 10948c2ecf20Sopenharmony_ci cmd->keep_alive_seconds = 0; 10958c2ecf20Sopenharmony_ci cmd->debug_flags = 0; 10968c2ecf20Sopenharmony_ci cmd->rx_data_timeout = cpu_to_le32(25 * 1024); 10978c2ecf20Sopenharmony_ci cmd->tx_data_timeout = cpu_to_le32(25 * 1024); 10988c2ecf20Sopenharmony_ci cmd->keep_alive_beacons = 0; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (dtim_period <= 2) { 11038c2ecf20Sopenharmony_ci memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0])); 11048c2ecf20Sopenharmony_ci no_dtim = 2; 11058c2ecf20Sopenharmony_ci } else if (dtim_period <= 10) { 11068c2ecf20Sopenharmony_ci memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1])); 11078c2ecf20Sopenharmony_ci no_dtim = 2; 11088c2ecf20Sopenharmony_ci } else { 11098c2ecf20Sopenharmony_ci memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2])); 11108c2ecf20Sopenharmony_ci no_dtim = 0; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci if (dtim_period == 0) { 11148c2ecf20Sopenharmony_ci dtim_period = 1; 11158c2ecf20Sopenharmony_ci skip = false; 11168c2ecf20Sopenharmony_ci } else { 11178c2ecf20Sopenharmony_ci skip = !!no_dtim; 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (skip) { 11218c2ecf20Sopenharmony_ci __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1]; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci max_sleep = le32_to_cpu(tmp); 11248c2ecf20Sopenharmony_ci if (max_sleep == 0xFF) 11258c2ecf20Sopenharmony_ci max_sleep = dtim_period * (skip + 1); 11268c2ecf20Sopenharmony_ci else if (max_sleep > dtim_period) 11278c2ecf20Sopenharmony_ci max_sleep = (max_sleep / dtim_period) * dtim_period; 11288c2ecf20Sopenharmony_ci cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK; 11298c2ecf20Sopenharmony_ci } else { 11308c2ecf20Sopenharmony_ci max_sleep = dtim_period; 11318c2ecf20Sopenharmony_ci cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci for (i = 0; i < IL_POWER_VEC_SIZE; i++) 11358c2ecf20Sopenharmony_ci if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) 11368c2ecf20Sopenharmony_ci cmd->sleep_interval[i] = cpu_to_le32(max_sleep); 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic int 11408c2ecf20Sopenharmony_ciil_set_power(struct il_priv *il, struct il_powertable_cmd *cmd) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci D_POWER("Sending power/sleep command\n"); 11438c2ecf20Sopenharmony_ci D_POWER("Flags value = 0x%08X\n", cmd->flags); 11448c2ecf20Sopenharmony_ci D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); 11458c2ecf20Sopenharmony_ci D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); 11468c2ecf20Sopenharmony_ci D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n", 11478c2ecf20Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[0]), 11488c2ecf20Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[1]), 11498c2ecf20Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[2]), 11508c2ecf20Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[3]), 11518c2ecf20Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[4])); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci return il_send_cmd_pdu(il, C_POWER_TBL, 11548c2ecf20Sopenharmony_ci sizeof(struct il_powertable_cmd), cmd); 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int 11588c2ecf20Sopenharmony_ciil_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci int ret; 11618c2ecf20Sopenharmony_ci bool update_chains; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* Don't update the RX chain when chain noise calibration is running */ 11668c2ecf20Sopenharmony_ci update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE || 11678c2ecf20Sopenharmony_ci il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) 11708c2ecf20Sopenharmony_ci return 0; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (!il_is_ready_rf(il)) 11738c2ecf20Sopenharmony_ci return -EIO; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* scan complete use sleep_power_next, need to be updated */ 11768c2ecf20Sopenharmony_ci memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); 11778c2ecf20Sopenharmony_ci if (test_bit(S_SCANNING, &il->status) && !force) { 11788c2ecf20Sopenharmony_ci D_INFO("Defer power set mode while scanning\n"); 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK) 11838c2ecf20Sopenharmony_ci set_bit(S_POWER_PMI, &il->status); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci ret = il_set_power(il, cmd); 11868c2ecf20Sopenharmony_ci if (!ret) { 11878c2ecf20Sopenharmony_ci if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)) 11888c2ecf20Sopenharmony_ci clear_bit(S_POWER_PMI, &il->status); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (il->ops->update_chain_flags && update_chains) 11918c2ecf20Sopenharmony_ci il->ops->update_chain_flags(il); 11928c2ecf20Sopenharmony_ci else if (il->ops->update_chain_flags) 11938c2ecf20Sopenharmony_ci D_POWER("Cannot update the power, chain noise " 11948c2ecf20Sopenharmony_ci "calibration running: %d\n", 11958c2ecf20Sopenharmony_ci il->chain_noise_data.state); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)); 11988c2ecf20Sopenharmony_ci } else 11998c2ecf20Sopenharmony_ci IL_ERR("set power fail, ret = %d", ret); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return ret; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ciint 12058c2ecf20Sopenharmony_ciil_power_update_mode(struct il_priv *il, bool force) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci struct il_powertable_cmd cmd; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci il_build_powertable_cmd(il, &cmd); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci return il_power_set_mode(il, &cmd, force); 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_power_update_mode); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci/* initialize to default */ 12168c2ecf20Sopenharmony_civoid 12178c2ecf20Sopenharmony_ciil_power_initialize(struct il_priv *il) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci u16 lctl; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); 12228c2ecf20Sopenharmony_ci il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci il->power_data.debug_sleep_level_override = -1; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd)); 12278c2ecf20Sopenharmony_ci} 12288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_power_initialize); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after 12318c2ecf20Sopenharmony_ci * sending probe req. This should be set long enough to hear probe responses 12328c2ecf20Sopenharmony_ci * from more than one AP. */ 12338c2ecf20Sopenharmony_ci#define IL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ 12348c2ecf20Sopenharmony_ci#define IL_ACTIVE_DWELL_TIME_52 (20) 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci#define IL_ACTIVE_DWELL_FACTOR_24GHZ (3) 12378c2ecf20Sopenharmony_ci#define IL_ACTIVE_DWELL_FACTOR_52GHZ (2) 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. 12408c2ecf20Sopenharmony_ci * Must be set longer than active dwell time. 12418c2ecf20Sopenharmony_ci * For the most reliable scan, set > AP beacon interval (typically 100msec). */ 12428c2ecf20Sopenharmony_ci#define IL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ 12438c2ecf20Sopenharmony_ci#define IL_PASSIVE_DWELL_TIME_52 (10) 12448c2ecf20Sopenharmony_ci#define IL_PASSIVE_DWELL_BASE (100) 12458c2ecf20Sopenharmony_ci#define IL_CHANNEL_TUNE_TIME 5 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic int 12488c2ecf20Sopenharmony_ciil_send_scan_abort(struct il_priv *il) 12498c2ecf20Sopenharmony_ci{ 12508c2ecf20Sopenharmony_ci int ret; 12518c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt; 12528c2ecf20Sopenharmony_ci struct il_host_cmd cmd = { 12538c2ecf20Sopenharmony_ci .id = C_SCAN_ABORT, 12548c2ecf20Sopenharmony_ci .flags = CMD_WANT_SKB, 12558c2ecf20Sopenharmony_ci }; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci /* Exit instantly with error when device is not ready 12588c2ecf20Sopenharmony_ci * to receive scan abort command or it does not perform 12598c2ecf20Sopenharmony_ci * hardware scan currently */ 12608c2ecf20Sopenharmony_ci if (!test_bit(S_READY, &il->status) || 12618c2ecf20Sopenharmony_ci !test_bit(S_GEO_CONFIGURED, &il->status) || 12628c2ecf20Sopenharmony_ci !test_bit(S_SCAN_HW, &il->status) || 12638c2ecf20Sopenharmony_ci test_bit(S_FW_ERROR, &il->status) || 12648c2ecf20Sopenharmony_ci test_bit(S_EXIT_PENDING, &il->status)) 12658c2ecf20Sopenharmony_ci return -EIO; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci ret = il_send_cmd_sync(il, &cmd); 12688c2ecf20Sopenharmony_ci if (ret) 12698c2ecf20Sopenharmony_ci return ret; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci pkt = (struct il_rx_pkt *)cmd.reply_page; 12728c2ecf20Sopenharmony_ci if (pkt->u.status != CAN_ABORT_STATUS) { 12738c2ecf20Sopenharmony_ci /* The scan abort will return 1 for success or 12748c2ecf20Sopenharmony_ci * 2 for "failure". A failure condition can be 12758c2ecf20Sopenharmony_ci * due to simply not being in an active scan which 12768c2ecf20Sopenharmony_ci * can occur if we send the scan abort before we 12778c2ecf20Sopenharmony_ci * the microcode has notified us that a scan is 12788c2ecf20Sopenharmony_ci * completed. */ 12798c2ecf20Sopenharmony_ci D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status); 12808c2ecf20Sopenharmony_ci ret = -EIO; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci il_free_pages(il, cmd.reply_page); 12848c2ecf20Sopenharmony_ci return ret; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic void 12888c2ecf20Sopenharmony_ciil_complete_scan(struct il_priv *il, bool aborted) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci struct cfg80211_scan_info info = { 12918c2ecf20Sopenharmony_ci .aborted = aborted, 12928c2ecf20Sopenharmony_ci }; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci /* check if scan was requested from mac80211 */ 12958c2ecf20Sopenharmony_ci if (il->scan_request) { 12968c2ecf20Sopenharmony_ci D_SCAN("Complete scan in mac80211\n"); 12978c2ecf20Sopenharmony_ci ieee80211_scan_completed(il->hw, &info); 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci il->scan_vif = NULL; 13018c2ecf20Sopenharmony_ci il->scan_request = NULL; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_civoid 13058c2ecf20Sopenharmony_ciil_force_scan_end(struct il_priv *il) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci if (!test_bit(S_SCANNING, &il->status)) { 13108c2ecf20Sopenharmony_ci D_SCAN("Forcing scan end while not scanning\n"); 13118c2ecf20Sopenharmony_ci return; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci D_SCAN("Forcing scan end\n"); 13158c2ecf20Sopenharmony_ci clear_bit(S_SCANNING, &il->status); 13168c2ecf20Sopenharmony_ci clear_bit(S_SCAN_HW, &il->status); 13178c2ecf20Sopenharmony_ci clear_bit(S_SCAN_ABORTING, &il->status); 13188c2ecf20Sopenharmony_ci il_complete_scan(il, true); 13198c2ecf20Sopenharmony_ci} 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_cistatic void 13228c2ecf20Sopenharmony_ciil_do_scan_abort(struct il_priv *il) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci int ret; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (!test_bit(S_SCANNING, &il->status)) { 13298c2ecf20Sopenharmony_ci D_SCAN("Not performing scan to abort\n"); 13308c2ecf20Sopenharmony_ci return; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) { 13348c2ecf20Sopenharmony_ci D_SCAN("Scan abort in progress\n"); 13358c2ecf20Sopenharmony_ci return; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci ret = il_send_scan_abort(il); 13398c2ecf20Sopenharmony_ci if (ret) { 13408c2ecf20Sopenharmony_ci D_SCAN("Send scan abort failed %d\n", ret); 13418c2ecf20Sopenharmony_ci il_force_scan_end(il); 13428c2ecf20Sopenharmony_ci } else 13438c2ecf20Sopenharmony_ci D_SCAN("Successfully send scan abort\n"); 13448c2ecf20Sopenharmony_ci} 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci/* 13478c2ecf20Sopenharmony_ci * il_scan_cancel - Cancel any currently executing HW scan 13488c2ecf20Sopenharmony_ci */ 13498c2ecf20Sopenharmony_ciint 13508c2ecf20Sopenharmony_ciil_scan_cancel(struct il_priv *il) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci D_SCAN("Queuing abort scan\n"); 13538c2ecf20Sopenharmony_ci queue_work(il->workqueue, &il->abort_scan); 13548c2ecf20Sopenharmony_ci return 0; 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_scan_cancel); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci/* 13598c2ecf20Sopenharmony_ci * il_scan_cancel_timeout - Cancel any currently executing HW scan 13608c2ecf20Sopenharmony_ci * @ms: amount of time to wait (in milliseconds) for scan to abort 13618c2ecf20Sopenharmony_ci * 13628c2ecf20Sopenharmony_ci */ 13638c2ecf20Sopenharmony_ciint 13648c2ecf20Sopenharmony_ciil_scan_cancel_timeout(struct il_priv *il, unsigned long ms) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(ms); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci D_SCAN("Scan cancel timeout\n"); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci il_do_scan_abort(il); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci while (time_before_eq(jiffies, timeout)) { 13758c2ecf20Sopenharmony_ci if (!test_bit(S_SCAN_HW, &il->status)) 13768c2ecf20Sopenharmony_ci break; 13778c2ecf20Sopenharmony_ci msleep(20); 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci return test_bit(S_SCAN_HW, &il->status); 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_scan_cancel_timeout); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci/* Service response to C_SCAN (0x80) */ 13858c2ecf20Sopenharmony_cistatic void 13868c2ecf20Sopenharmony_ciil_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 13898c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 13908c2ecf20Sopenharmony_ci struct il_scanreq_notification *notif = 13918c2ecf20Sopenharmony_ci (struct il_scanreq_notification *)pkt->u.raw; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci D_SCAN("Scan request status = 0x%x\n", notif->status); 13948c2ecf20Sopenharmony_ci#endif 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci/* Service N_SCAN_START (0x82) */ 13988c2ecf20Sopenharmony_cistatic void 13998c2ecf20Sopenharmony_ciil_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 14028c2ecf20Sopenharmony_ci struct il_scanstart_notification *notif = 14038c2ecf20Sopenharmony_ci (struct il_scanstart_notification *)pkt->u.raw; 14048c2ecf20Sopenharmony_ci il->scan_start_tsf = le32_to_cpu(notif->tsf_low); 14058c2ecf20Sopenharmony_ci D_SCAN("Scan start: " "%d [802.11%s] " 14068c2ecf20Sopenharmony_ci "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel, 14078c2ecf20Sopenharmony_ci notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high), 14088c2ecf20Sopenharmony_ci le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer); 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci/* Service N_SCAN_RESULTS (0x83) */ 14128c2ecf20Sopenharmony_cistatic void 14138c2ecf20Sopenharmony_ciil_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb) 14148c2ecf20Sopenharmony_ci{ 14158c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 14168c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 14178c2ecf20Sopenharmony_ci struct il_scanresults_notification *notif = 14188c2ecf20Sopenharmony_ci (struct il_scanresults_notification *)pkt->u.raw; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d " 14218c2ecf20Sopenharmony_ci "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a", 14228c2ecf20Sopenharmony_ci le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), 14238c2ecf20Sopenharmony_ci le32_to_cpu(notif->stats[0]), 14248c2ecf20Sopenharmony_ci le32_to_cpu(notif->tsf_low) - il->scan_start_tsf); 14258c2ecf20Sopenharmony_ci#endif 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci/* Service N_SCAN_COMPLETE (0x84) */ 14298c2ecf20Sopenharmony_cistatic void 14308c2ecf20Sopenharmony_ciil_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb) 14318c2ecf20Sopenharmony_ci{ 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 14348c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 14358c2ecf20Sopenharmony_ci struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw; 14368c2ecf20Sopenharmony_ci#endif 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", 14398c2ecf20Sopenharmony_ci scan_notif->scanned_channels, scan_notif->tsf_low, 14408c2ecf20Sopenharmony_ci scan_notif->tsf_high, scan_notif->status); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci /* The HW is no longer scanning */ 14438c2ecf20Sopenharmony_ci clear_bit(S_SCAN_HW, &il->status); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci D_SCAN("Scan on %sGHz took %dms\n", 14468c2ecf20Sopenharmony_ci (il->scan_band == NL80211_BAND_2GHZ) ? "2.4" : "5.2", 14478c2ecf20Sopenharmony_ci jiffies_to_msecs(jiffies - il->scan_start)); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci queue_work(il->workqueue, &il->scan_completed); 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_civoid 14538c2ecf20Sopenharmony_ciil_setup_rx_scan_handlers(struct il_priv *il) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci /* scan handlers */ 14568c2ecf20Sopenharmony_ci il->handlers[C_SCAN] = il_hdl_scan; 14578c2ecf20Sopenharmony_ci il->handlers[N_SCAN_START] = il_hdl_scan_start; 14588c2ecf20Sopenharmony_ci il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results; 14598c2ecf20Sopenharmony_ci il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete; 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_setup_rx_scan_handlers); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ciu16 14648c2ecf20Sopenharmony_ciil_get_active_dwell_time(struct il_priv *il, enum nl80211_band band, 14658c2ecf20Sopenharmony_ci u8 n_probes) 14668c2ecf20Sopenharmony_ci{ 14678c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 14688c2ecf20Sopenharmony_ci return IL_ACTIVE_DWELL_TIME_52 + 14698c2ecf20Sopenharmony_ci IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); 14708c2ecf20Sopenharmony_ci else 14718c2ecf20Sopenharmony_ci return IL_ACTIVE_DWELL_TIME_24 + 14728c2ecf20Sopenharmony_ci IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_active_dwell_time); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ciu16 14778c2ecf20Sopenharmony_ciil_get_passive_dwell_time(struct il_priv *il, enum nl80211_band band, 14788c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci u16 value; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci u16 passive = 14838c2ecf20Sopenharmony_ci (band == 14848c2ecf20Sopenharmony_ci NL80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE + 14858c2ecf20Sopenharmony_ci IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE + 14868c2ecf20Sopenharmony_ci IL_PASSIVE_DWELL_TIME_52; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if (il_is_any_associated(il)) { 14898c2ecf20Sopenharmony_ci /* 14908c2ecf20Sopenharmony_ci * If we're associated, we clamp the maximum passive 14918c2ecf20Sopenharmony_ci * dwell time to be 98% of the smallest beacon interval 14928c2ecf20Sopenharmony_ci * (minus 2 * channel tune time) 14938c2ecf20Sopenharmony_ci */ 14948c2ecf20Sopenharmony_ci value = il->vif ? il->vif->bss_conf.beacon_int : 0; 14958c2ecf20Sopenharmony_ci if (value > IL_PASSIVE_DWELL_BASE || !value) 14968c2ecf20Sopenharmony_ci value = IL_PASSIVE_DWELL_BASE; 14978c2ecf20Sopenharmony_ci value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2; 14988c2ecf20Sopenharmony_ci passive = min(value, passive); 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci return passive; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_passive_dwell_time); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_civoid 15068c2ecf20Sopenharmony_ciil_init_scan_params(struct il_priv *il) 15078c2ecf20Sopenharmony_ci{ 15088c2ecf20Sopenharmony_ci u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1; 15098c2ecf20Sopenharmony_ci if (!il->scan_tx_ant[NL80211_BAND_5GHZ]) 15108c2ecf20Sopenharmony_ci il->scan_tx_ant[NL80211_BAND_5GHZ] = ant_idx; 15118c2ecf20Sopenharmony_ci if (!il->scan_tx_ant[NL80211_BAND_2GHZ]) 15128c2ecf20Sopenharmony_ci il->scan_tx_ant[NL80211_BAND_2GHZ] = ant_idx; 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_init_scan_params); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cistatic int 15178c2ecf20Sopenharmony_ciil_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif) 15188c2ecf20Sopenharmony_ci{ 15198c2ecf20Sopenharmony_ci int ret; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci cancel_delayed_work(&il->scan_check); 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci if (!il_is_ready_rf(il)) { 15268c2ecf20Sopenharmony_ci IL_WARN("Request scan called when driver not ready.\n"); 15278c2ecf20Sopenharmony_ci return -EIO; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (test_bit(S_SCAN_HW, &il->status)) { 15318c2ecf20Sopenharmony_ci D_SCAN("Multiple concurrent scan requests in parallel.\n"); 15328c2ecf20Sopenharmony_ci return -EBUSY; 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci if (test_bit(S_SCAN_ABORTING, &il->status)) { 15368c2ecf20Sopenharmony_ci D_SCAN("Scan request while abort pending.\n"); 15378c2ecf20Sopenharmony_ci return -EBUSY; 15388c2ecf20Sopenharmony_ci } 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci D_SCAN("Starting scan...\n"); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci set_bit(S_SCANNING, &il->status); 15438c2ecf20Sopenharmony_ci il->scan_start = jiffies; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci ret = il->ops->request_scan(il, vif); 15468c2ecf20Sopenharmony_ci if (ret) { 15478c2ecf20Sopenharmony_ci clear_bit(S_SCANNING, &il->status); 15488c2ecf20Sopenharmony_ci return ret; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci queue_delayed_work(il->workqueue, &il->scan_check, 15528c2ecf20Sopenharmony_ci IL_SCAN_CHECK_WATCHDOG); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci return 0; 15558c2ecf20Sopenharmony_ci} 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ciint 15588c2ecf20Sopenharmony_ciil_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 15598c2ecf20Sopenharmony_ci struct ieee80211_scan_request *hw_req) 15608c2ecf20Sopenharmony_ci{ 15618c2ecf20Sopenharmony_ci struct cfg80211_scan_request *req = &hw_req->req; 15628c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 15638c2ecf20Sopenharmony_ci int ret; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci if (req->n_channels == 0) { 15668c2ecf20Sopenharmony_ci IL_ERR("Can not scan on no channels.\n"); 15678c2ecf20Sopenharmony_ci return -EINVAL; 15688c2ecf20Sopenharmony_ci } 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 15718c2ecf20Sopenharmony_ci D_MAC80211("enter\n"); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci if (test_bit(S_SCANNING, &il->status)) { 15748c2ecf20Sopenharmony_ci D_SCAN("Scan already in progress.\n"); 15758c2ecf20Sopenharmony_ci ret = -EAGAIN; 15768c2ecf20Sopenharmony_ci goto out_unlock; 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci /* mac80211 will only ask for one band at a time */ 15808c2ecf20Sopenharmony_ci il->scan_request = req; 15818c2ecf20Sopenharmony_ci il->scan_vif = vif; 15828c2ecf20Sopenharmony_ci il->scan_band = req->channels[0]->band; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci ret = il_scan_initiate(il, vif); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ciout_unlock: 15878c2ecf20Sopenharmony_ci D_MAC80211("leave ret %d\n", ret); 15888c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci return ret; 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_hw_scan); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic void 15958c2ecf20Sopenharmony_ciil_bg_scan_check(struct work_struct *data) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci struct il_priv *il = 15988c2ecf20Sopenharmony_ci container_of(data, struct il_priv, scan_check.work); 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci D_SCAN("Scan check work\n"); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci /* Since we are here firmware does not finish scan and 16038c2ecf20Sopenharmony_ci * most likely is in bad shape, so we don't bother to 16048c2ecf20Sopenharmony_ci * send abort command, just force scan complete to mac80211 */ 16058c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 16068c2ecf20Sopenharmony_ci il_force_scan_end(il); 16078c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci/* 16118c2ecf20Sopenharmony_ci * il_fill_probe_req - fill in all required fields and IE for probe request 16128c2ecf20Sopenharmony_ci */ 16138c2ecf20Sopenharmony_ciu16 16148c2ecf20Sopenharmony_ciil_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, 16158c2ecf20Sopenharmony_ci const u8 *ta, const u8 *ies, int ie_len, int left) 16168c2ecf20Sopenharmony_ci{ 16178c2ecf20Sopenharmony_ci int len = 0; 16188c2ecf20Sopenharmony_ci u8 *pos = NULL; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci /* Make sure there is enough space for the probe request, 16218c2ecf20Sopenharmony_ci * two mandatory IEs and the data */ 16228c2ecf20Sopenharmony_ci left -= 24; 16238c2ecf20Sopenharmony_ci if (left < 0) 16248c2ecf20Sopenharmony_ci return 0; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); 16278c2ecf20Sopenharmony_ci eth_broadcast_addr(frame->da); 16288c2ecf20Sopenharmony_ci memcpy(frame->sa, ta, ETH_ALEN); 16298c2ecf20Sopenharmony_ci eth_broadcast_addr(frame->bssid); 16308c2ecf20Sopenharmony_ci frame->seq_ctrl = 0; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci len += 24; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci /* ...next IE... */ 16358c2ecf20Sopenharmony_ci pos = &frame->u.probe_req.variable[0]; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci /* fill in our indirect SSID IE */ 16388c2ecf20Sopenharmony_ci left -= 2; 16398c2ecf20Sopenharmony_ci if (left < 0) 16408c2ecf20Sopenharmony_ci return 0; 16418c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_SSID; 16428c2ecf20Sopenharmony_ci *pos++ = 0; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci len += 2; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci if (WARN_ON(left < ie_len)) 16478c2ecf20Sopenharmony_ci return len; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (ies && ie_len) { 16508c2ecf20Sopenharmony_ci memcpy(pos, ies, ie_len); 16518c2ecf20Sopenharmony_ci len += ie_len; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci return (u16) len; 16558c2ecf20Sopenharmony_ci} 16568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_fill_probe_req); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic void 16598c2ecf20Sopenharmony_ciil_bg_abort_scan(struct work_struct *work) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci struct il_priv *il = container_of(work, struct il_priv, abort_scan); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci D_SCAN("Abort scan work\n"); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci /* We keep scan_check work queued in case when firmware will not 16668c2ecf20Sopenharmony_ci * report back scan completed notification */ 16678c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 16688c2ecf20Sopenharmony_ci il_scan_cancel_timeout(il, 200); 16698c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic void 16738c2ecf20Sopenharmony_ciil_bg_scan_completed(struct work_struct *work) 16748c2ecf20Sopenharmony_ci{ 16758c2ecf20Sopenharmony_ci struct il_priv *il = container_of(work, struct il_priv, scan_completed); 16768c2ecf20Sopenharmony_ci bool aborted; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci D_SCAN("Completed scan.\n"); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci cancel_delayed_work(&il->scan_check); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status); 16858c2ecf20Sopenharmony_ci if (aborted) 16868c2ecf20Sopenharmony_ci D_SCAN("Aborted scan completed.\n"); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci if (!test_and_clear_bit(S_SCANNING, &il->status)) { 16898c2ecf20Sopenharmony_ci D_SCAN("Scan already completed.\n"); 16908c2ecf20Sopenharmony_ci goto out_settings; 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci il_complete_scan(il, aborted); 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ciout_settings: 16968c2ecf20Sopenharmony_ci /* Can we still talk to firmware ? */ 16978c2ecf20Sopenharmony_ci if (!il_is_ready_rf(il)) 16988c2ecf20Sopenharmony_ci goto out; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci /* 17018c2ecf20Sopenharmony_ci * We do not commit power settings while scan is pending, 17028c2ecf20Sopenharmony_ci * do it now if the settings changed. 17038c2ecf20Sopenharmony_ci */ 17048c2ecf20Sopenharmony_ci il_power_set_mode(il, &il->power_data.sleep_cmd_next, false); 17058c2ecf20Sopenharmony_ci il_set_tx_power(il, il->tx_power_next, false); 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci il->ops->post_scan(il); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ciout: 17108c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 17118c2ecf20Sopenharmony_ci} 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_civoid 17148c2ecf20Sopenharmony_ciil_setup_scan_deferred_work(struct il_priv *il) 17158c2ecf20Sopenharmony_ci{ 17168c2ecf20Sopenharmony_ci INIT_WORK(&il->scan_completed, il_bg_scan_completed); 17178c2ecf20Sopenharmony_ci INIT_WORK(&il->abort_scan, il_bg_abort_scan); 17188c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check); 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_setup_scan_deferred_work); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_civoid 17238c2ecf20Sopenharmony_ciil_cancel_scan_deferred_work(struct il_priv *il) 17248c2ecf20Sopenharmony_ci{ 17258c2ecf20Sopenharmony_ci cancel_work_sync(&il->abort_scan); 17268c2ecf20Sopenharmony_ci cancel_work_sync(&il->scan_completed); 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci if (cancel_delayed_work_sync(&il->scan_check)) { 17298c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 17308c2ecf20Sopenharmony_ci il_force_scan_end(il); 17318c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_cancel_scan_deferred_work); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci/* il->sta_lock must be held */ 17378c2ecf20Sopenharmony_cistatic void 17388c2ecf20Sopenharmony_ciil_sta_ucode_activate(struct il_priv *il, u8 sta_id) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) 17428c2ecf20Sopenharmony_ci IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n", 17438c2ecf20Sopenharmony_ci sta_id, il->stations[sta_id].sta.sta.addr); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) { 17468c2ecf20Sopenharmony_ci D_ASSOC("STA id %u addr %pM already present" 17478c2ecf20Sopenharmony_ci " in uCode (according to driver)\n", sta_id, 17488c2ecf20Sopenharmony_ci il->stations[sta_id].sta.sta.addr); 17498c2ecf20Sopenharmony_ci } else { 17508c2ecf20Sopenharmony_ci il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE; 17518c2ecf20Sopenharmony_ci D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id, 17528c2ecf20Sopenharmony_ci il->stations[sta_id].sta.sta.addr); 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci} 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_cistatic int 17578c2ecf20Sopenharmony_ciil_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta, 17588c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt, bool sync) 17598c2ecf20Sopenharmony_ci{ 17608c2ecf20Sopenharmony_ci u8 sta_id = addsta->sta.sta_id; 17618c2ecf20Sopenharmony_ci unsigned long flags; 17628c2ecf20Sopenharmony_ci int ret = -EIO; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { 17658c2ecf20Sopenharmony_ci IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags); 17668c2ecf20Sopenharmony_ci return ret; 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci D_INFO("Processing response for adding station %u\n", sta_id); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci switch (pkt->u.add_sta.status) { 17748c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS_MSK: 17758c2ecf20Sopenharmony_ci D_INFO("C_ADD_STA PASSED\n"); 17768c2ecf20Sopenharmony_ci il_sta_ucode_activate(il, sta_id); 17778c2ecf20Sopenharmony_ci ret = 0; 17788c2ecf20Sopenharmony_ci break; 17798c2ecf20Sopenharmony_ci case ADD_STA_NO_ROOM_IN_TBL: 17808c2ecf20Sopenharmony_ci IL_ERR("Adding station %d failed, no room in table.\n", sta_id); 17818c2ecf20Sopenharmony_ci break; 17828c2ecf20Sopenharmony_ci case ADD_STA_NO_BLOCK_ACK_RESOURCE: 17838c2ecf20Sopenharmony_ci IL_ERR("Adding station %d failed, no block ack resource.\n", 17848c2ecf20Sopenharmony_ci sta_id); 17858c2ecf20Sopenharmony_ci break; 17868c2ecf20Sopenharmony_ci case ADD_STA_MODIFY_NON_EXIST_STA: 17878c2ecf20Sopenharmony_ci IL_ERR("Attempting to modify non-existing station %d\n", 17888c2ecf20Sopenharmony_ci sta_id); 17898c2ecf20Sopenharmony_ci break; 17908c2ecf20Sopenharmony_ci default: 17918c2ecf20Sopenharmony_ci D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status); 17928c2ecf20Sopenharmony_ci break; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci D_INFO("%s station id %u addr %pM\n", 17968c2ecf20Sopenharmony_ci il->stations[sta_id].sta.mode == 17978c2ecf20Sopenharmony_ci STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id, 17988c2ecf20Sopenharmony_ci il->stations[sta_id].sta.sta.addr); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci /* 18018c2ecf20Sopenharmony_ci * XXX: The MAC address in the command buffer is often changed from 18028c2ecf20Sopenharmony_ci * the original sent to the device. That is, the MAC address 18038c2ecf20Sopenharmony_ci * written to the command buffer often is not the same MAC address 18048c2ecf20Sopenharmony_ci * read from the command buffer when the command returns. This 18058c2ecf20Sopenharmony_ci * issue has not yet been resolved and this debugging is left to 18068c2ecf20Sopenharmony_ci * observe the problem. 18078c2ecf20Sopenharmony_ci */ 18088c2ecf20Sopenharmony_ci D_INFO("%s station according to cmd buffer %pM\n", 18098c2ecf20Sopenharmony_ci il->stations[sta_id].sta.mode == 18108c2ecf20Sopenharmony_ci STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr); 18118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci return ret; 18148c2ecf20Sopenharmony_ci} 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_cistatic void 18178c2ecf20Sopenharmony_ciil_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd, 18188c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt) 18198c2ecf20Sopenharmony_ci{ 18208c2ecf20Sopenharmony_ci struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci il_process_add_sta_resp(il, addsta, pkt, false); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci} 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ciint 18278c2ecf20Sopenharmony_ciil_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags) 18288c2ecf20Sopenharmony_ci{ 18298c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = NULL; 18308c2ecf20Sopenharmony_ci int ret = 0; 18318c2ecf20Sopenharmony_ci u8 data[sizeof(*sta)]; 18328c2ecf20Sopenharmony_ci struct il_host_cmd cmd = { 18338c2ecf20Sopenharmony_ci .id = C_ADD_STA, 18348c2ecf20Sopenharmony_ci .flags = flags, 18358c2ecf20Sopenharmony_ci .data = data, 18368c2ecf20Sopenharmony_ci }; 18378c2ecf20Sopenharmony_ci u8 sta_id __maybe_unused = sta->sta.sta_id; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr, 18408c2ecf20Sopenharmony_ci flags & CMD_ASYNC ? "a" : ""); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (flags & CMD_ASYNC) 18438c2ecf20Sopenharmony_ci cmd.callback = il_add_sta_callback; 18448c2ecf20Sopenharmony_ci else { 18458c2ecf20Sopenharmony_ci cmd.flags |= CMD_WANT_SKB; 18468c2ecf20Sopenharmony_ci might_sleep(); 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci cmd.len = il->ops->build_addsta_hcmd(sta, data); 18508c2ecf20Sopenharmony_ci ret = il_send_cmd(il, &cmd); 18518c2ecf20Sopenharmony_ci if (ret) 18528c2ecf20Sopenharmony_ci return ret; 18538c2ecf20Sopenharmony_ci if (flags & CMD_ASYNC) 18548c2ecf20Sopenharmony_ci return 0; 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci pkt = (struct il_rx_pkt *)cmd.reply_page; 18578c2ecf20Sopenharmony_ci ret = il_process_add_sta_resp(il, sta, pkt, true); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci il_free_pages(il, cmd.reply_page); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci return ret; 18628c2ecf20Sopenharmony_ci} 18638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_add_sta); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_cistatic void 18668c2ecf20Sopenharmony_ciil_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta) 18678c2ecf20Sopenharmony_ci{ 18688c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; 18698c2ecf20Sopenharmony_ci __le32 sta_flags; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci if (!sta || !sta_ht_inf->ht_supported) 18728c2ecf20Sopenharmony_ci goto done; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci D_ASSOC("spatial multiplexing power save mode: %s\n", 18758c2ecf20Sopenharmony_ci (sta->smps_mode == IEEE80211_SMPS_STATIC) ? "static" : 18768c2ecf20Sopenharmony_ci (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" : 18778c2ecf20Sopenharmony_ci "disabled"); 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci sta_flags = il->stations[idx].sta.station_flags; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci switch (sta->smps_mode) { 18848c2ecf20Sopenharmony_ci case IEEE80211_SMPS_STATIC: 18858c2ecf20Sopenharmony_ci sta_flags |= STA_FLG_MIMO_DIS_MSK; 18868c2ecf20Sopenharmony_ci break; 18878c2ecf20Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 18888c2ecf20Sopenharmony_ci sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; 18898c2ecf20Sopenharmony_ci break; 18908c2ecf20Sopenharmony_ci case IEEE80211_SMPS_OFF: 18918c2ecf20Sopenharmony_ci break; 18928c2ecf20Sopenharmony_ci default: 18938c2ecf20Sopenharmony_ci IL_WARN("Invalid MIMO PS mode %d\n", sta->smps_mode); 18948c2ecf20Sopenharmony_ci break; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci sta_flags |= 18988c2ecf20Sopenharmony_ci cpu_to_le32((u32) sta_ht_inf-> 18998c2ecf20Sopenharmony_ci ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci sta_flags |= 19028c2ecf20Sopenharmony_ci cpu_to_le32((u32) sta_ht_inf-> 19038c2ecf20Sopenharmony_ci ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) 19068c2ecf20Sopenharmony_ci sta_flags |= STA_FLG_HT40_EN_MSK; 19078c2ecf20Sopenharmony_ci else 19088c2ecf20Sopenharmony_ci sta_flags &= ~STA_FLG_HT40_EN_MSK; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci il->stations[idx].sta.station_flags = sta_flags; 19118c2ecf20Sopenharmony_cidone: 19128c2ecf20Sopenharmony_ci return; 19138c2ecf20Sopenharmony_ci} 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci/* 19168c2ecf20Sopenharmony_ci * il_prep_station - Prepare station information for addition 19178c2ecf20Sopenharmony_ci * 19188c2ecf20Sopenharmony_ci * should be called with sta_lock held 19198c2ecf20Sopenharmony_ci */ 19208c2ecf20Sopenharmony_ciu8 19218c2ecf20Sopenharmony_ciil_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, 19228c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 19238c2ecf20Sopenharmony_ci{ 19248c2ecf20Sopenharmony_ci struct il_station_entry *station; 19258c2ecf20Sopenharmony_ci int i; 19268c2ecf20Sopenharmony_ci u8 sta_id = IL_INVALID_STATION; 19278c2ecf20Sopenharmony_ci u16 rate; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci if (is_ap) 19308c2ecf20Sopenharmony_ci sta_id = IL_AP_ID; 19318c2ecf20Sopenharmony_ci else if (is_broadcast_ether_addr(addr)) 19328c2ecf20Sopenharmony_ci sta_id = il->hw_params.bcast_id; 19338c2ecf20Sopenharmony_ci else 19348c2ecf20Sopenharmony_ci for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) { 19358c2ecf20Sopenharmony_ci if (ether_addr_equal(il->stations[i].sta.sta.addr, 19368c2ecf20Sopenharmony_ci addr)) { 19378c2ecf20Sopenharmony_ci sta_id = i; 19388c2ecf20Sopenharmony_ci break; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci if (!il->stations[i].used && 19428c2ecf20Sopenharmony_ci sta_id == IL_INVALID_STATION) 19438c2ecf20Sopenharmony_ci sta_id = i; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci /* 19478c2ecf20Sopenharmony_ci * These two conditions have the same outcome, but keep them 19488c2ecf20Sopenharmony_ci * separate 19498c2ecf20Sopenharmony_ci */ 19508c2ecf20Sopenharmony_ci if (unlikely(sta_id == IL_INVALID_STATION)) 19518c2ecf20Sopenharmony_ci return sta_id; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci /* 19548c2ecf20Sopenharmony_ci * uCode is not able to deal with multiple requests to add a 19558c2ecf20Sopenharmony_ci * station. Keep track if one is in progress so that we do not send 19568c2ecf20Sopenharmony_ci * another. 19578c2ecf20Sopenharmony_ci */ 19588c2ecf20Sopenharmony_ci if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { 19598c2ecf20Sopenharmony_ci D_INFO("STA %d already in process of being added.\n", sta_id); 19608c2ecf20Sopenharmony_ci return sta_id; 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && 19648c2ecf20Sopenharmony_ci (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) && 19658c2ecf20Sopenharmony_ci ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) { 19668c2ecf20Sopenharmony_ci D_ASSOC("STA %d (%pM) already added, not adding again.\n", 19678c2ecf20Sopenharmony_ci sta_id, addr); 19688c2ecf20Sopenharmony_ci return sta_id; 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci station = &il->stations[sta_id]; 19728c2ecf20Sopenharmony_ci station->used = IL_STA_DRIVER_ACTIVE; 19738c2ecf20Sopenharmony_ci D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr); 19748c2ecf20Sopenharmony_ci il->num_stations++; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci /* Set up the C_ADD_STA command to send to device */ 19778c2ecf20Sopenharmony_ci memset(&station->sta, 0, sizeof(struct il_addsta_cmd)); 19788c2ecf20Sopenharmony_ci memcpy(station->sta.sta.addr, addr, ETH_ALEN); 19798c2ecf20Sopenharmony_ci station->sta.mode = 0; 19808c2ecf20Sopenharmony_ci station->sta.sta.sta_id = sta_id; 19818c2ecf20Sopenharmony_ci station->sta.station_flags = 0; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci /* 19848c2ecf20Sopenharmony_ci * OK to call unconditionally, since local stations (IBSS BSSID 19858c2ecf20Sopenharmony_ci * STA and broadcast STA) pass in a NULL sta, and mac80211 19868c2ecf20Sopenharmony_ci * doesn't allow HT IBSS. 19878c2ecf20Sopenharmony_ci */ 19888c2ecf20Sopenharmony_ci il_set_ht_add_station(il, sta_id, sta); 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci /* 3945 only */ 19918c2ecf20Sopenharmony_ci rate = (il->band == NL80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP; 19928c2ecf20Sopenharmony_ci /* Turn on both antennas for the station... */ 19938c2ecf20Sopenharmony_ci station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci return sta_id; 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci} 19988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(il_prep_station); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci#define STA_WAIT_TIMEOUT (HZ/2) 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci/* 20038c2ecf20Sopenharmony_ci * il_add_station_common - 20048c2ecf20Sopenharmony_ci */ 20058c2ecf20Sopenharmony_ciint 20068c2ecf20Sopenharmony_ciil_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap, 20078c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u8 *sta_id_r) 20088c2ecf20Sopenharmony_ci{ 20098c2ecf20Sopenharmony_ci unsigned long flags_spin; 20108c2ecf20Sopenharmony_ci int ret = 0; 20118c2ecf20Sopenharmony_ci u8 sta_id; 20128c2ecf20Sopenharmony_ci struct il_addsta_cmd sta_cmd; 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci *sta_id_r = 0; 20158c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 20168c2ecf20Sopenharmony_ci sta_id = il_prep_station(il, addr, is_ap, sta); 20178c2ecf20Sopenharmony_ci if (sta_id == IL_INVALID_STATION) { 20188c2ecf20Sopenharmony_ci IL_ERR("Unable to prepare station %pM for addition\n", addr); 20198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 20208c2ecf20Sopenharmony_ci return -EINVAL; 20218c2ecf20Sopenharmony_ci } 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci /* 20248c2ecf20Sopenharmony_ci * uCode is not able to deal with multiple requests to add a 20258c2ecf20Sopenharmony_ci * station. Keep track if one is in progress so that we do not send 20268c2ecf20Sopenharmony_ci * another. 20278c2ecf20Sopenharmony_ci */ 20288c2ecf20Sopenharmony_ci if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { 20298c2ecf20Sopenharmony_ci D_INFO("STA %d already in process of being added.\n", sta_id); 20308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 20318c2ecf20Sopenharmony_ci return -EEXIST; 20328c2ecf20Sopenharmony_ci } 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && 20358c2ecf20Sopenharmony_ci (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { 20368c2ecf20Sopenharmony_ci D_ASSOC("STA %d (%pM) already added, not adding again.\n", 20378c2ecf20Sopenharmony_ci sta_id, addr); 20388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 20398c2ecf20Sopenharmony_ci return -EEXIST; 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS; 20438c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &il->stations[sta_id].sta, 20448c2ecf20Sopenharmony_ci sizeof(struct il_addsta_cmd)); 20458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci /* Add station to device's station table */ 20488c2ecf20Sopenharmony_ci ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); 20498c2ecf20Sopenharmony_ci if (ret) { 20508c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 20518c2ecf20Sopenharmony_ci IL_ERR("Adding station %pM failed.\n", 20528c2ecf20Sopenharmony_ci il->stations[sta_id].sta.sta.addr); 20538c2ecf20Sopenharmony_ci il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; 20548c2ecf20Sopenharmony_ci il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; 20558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci *sta_id_r = sta_id; 20588c2ecf20Sopenharmony_ci return ret; 20598c2ecf20Sopenharmony_ci} 20608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_add_station_common); 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci/* 20638c2ecf20Sopenharmony_ci * il_sta_ucode_deactivate - deactivate ucode status for a station 20648c2ecf20Sopenharmony_ci * 20658c2ecf20Sopenharmony_ci * il->sta_lock must be held 20668c2ecf20Sopenharmony_ci */ 20678c2ecf20Sopenharmony_cistatic void 20688c2ecf20Sopenharmony_ciil_sta_ucode_deactivate(struct il_priv *il, u8 sta_id) 20698c2ecf20Sopenharmony_ci{ 20708c2ecf20Sopenharmony_ci /* Ucode must be active and driver must be non active */ 20718c2ecf20Sopenharmony_ci if ((il->stations[sta_id]. 20728c2ecf20Sopenharmony_ci used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) != 20738c2ecf20Sopenharmony_ci IL_STA_UCODE_ACTIVE) 20748c2ecf20Sopenharmony_ci IL_ERR("removed non active STA %u\n", sta_id); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry)); 20798c2ecf20Sopenharmony_ci D_ASSOC("Removed STA %u\n", sta_id); 20808c2ecf20Sopenharmony_ci} 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_cistatic int 20838c2ecf20Sopenharmony_ciil_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id, 20848c2ecf20Sopenharmony_ci bool temporary) 20858c2ecf20Sopenharmony_ci{ 20868c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt; 20878c2ecf20Sopenharmony_ci int ret; 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci unsigned long flags_spin; 20908c2ecf20Sopenharmony_ci struct il_rem_sta_cmd rm_sta_cmd; 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci struct il_host_cmd cmd = { 20938c2ecf20Sopenharmony_ci .id = C_REM_STA, 20948c2ecf20Sopenharmony_ci .len = sizeof(struct il_rem_sta_cmd), 20958c2ecf20Sopenharmony_ci .flags = CMD_SYNC, 20968c2ecf20Sopenharmony_ci .data = &rm_sta_cmd, 20978c2ecf20Sopenharmony_ci }; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); 21008c2ecf20Sopenharmony_ci rm_sta_cmd.num_sta = 1; 21018c2ecf20Sopenharmony_ci memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci cmd.flags |= CMD_WANT_SKB; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci ret = il_send_cmd(il, &cmd); 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci if (ret) 21088c2ecf20Sopenharmony_ci return ret; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci pkt = (struct il_rx_pkt *)cmd.reply_page; 21118c2ecf20Sopenharmony_ci if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { 21128c2ecf20Sopenharmony_ci IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags); 21138c2ecf20Sopenharmony_ci ret = -EIO; 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci if (!ret) { 21178c2ecf20Sopenharmony_ci switch (pkt->u.rem_sta.status) { 21188c2ecf20Sopenharmony_ci case REM_STA_SUCCESS_MSK: 21198c2ecf20Sopenharmony_ci if (!temporary) { 21208c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 21218c2ecf20Sopenharmony_ci il_sta_ucode_deactivate(il, sta_id); 21228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, 21238c2ecf20Sopenharmony_ci flags_spin); 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci D_ASSOC("C_REM_STA PASSED\n"); 21268c2ecf20Sopenharmony_ci break; 21278c2ecf20Sopenharmony_ci default: 21288c2ecf20Sopenharmony_ci ret = -EIO; 21298c2ecf20Sopenharmony_ci IL_ERR("C_REM_STA failed\n"); 21308c2ecf20Sopenharmony_ci break; 21318c2ecf20Sopenharmony_ci } 21328c2ecf20Sopenharmony_ci } 21338c2ecf20Sopenharmony_ci il_free_pages(il, cmd.reply_page); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci return ret; 21368c2ecf20Sopenharmony_ci} 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci/* 21398c2ecf20Sopenharmony_ci * il_remove_station - Remove driver's knowledge of station. 21408c2ecf20Sopenharmony_ci */ 21418c2ecf20Sopenharmony_ciint 21428c2ecf20Sopenharmony_ciil_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr) 21438c2ecf20Sopenharmony_ci{ 21448c2ecf20Sopenharmony_ci unsigned long flags; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci if (!il_is_ready(il)) { 21478c2ecf20Sopenharmony_ci D_INFO("Unable to remove station %pM, device not ready.\n", 21488c2ecf20Sopenharmony_ci addr); 21498c2ecf20Sopenharmony_ci /* 21508c2ecf20Sopenharmony_ci * It is typical for stations to be removed when we are 21518c2ecf20Sopenharmony_ci * going down. Return success since device will be down 21528c2ecf20Sopenharmony_ci * soon anyway 21538c2ecf20Sopenharmony_ci */ 21548c2ecf20Sopenharmony_ci return 0; 21558c2ecf20Sopenharmony_ci } 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci if (WARN_ON(sta_id == IL_INVALID_STATION)) 21608c2ecf20Sopenharmony_ci return -EINVAL; 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) { 21658c2ecf20Sopenharmony_ci D_INFO("Removing %pM but non DRIVER active\n", addr); 21668c2ecf20Sopenharmony_ci goto out_err; 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { 21708c2ecf20Sopenharmony_ci D_INFO("Removing %pM but non UCODE active\n", addr); 21718c2ecf20Sopenharmony_ci goto out_err; 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci if (il->stations[sta_id].used & IL_STA_LOCAL) { 21758c2ecf20Sopenharmony_ci kfree(il->stations[sta_id].lq); 21768c2ecf20Sopenharmony_ci il->stations[sta_id].lq = NULL; 21778c2ecf20Sopenharmony_ci } 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci il->num_stations--; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci BUG_ON(il->num_stations < 0); 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci return il_send_remove_station(il, addr, sta_id, false); 21888c2ecf20Sopenharmony_ciout_err: 21898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 21908c2ecf20Sopenharmony_ci return -EINVAL; 21918c2ecf20Sopenharmony_ci} 21928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(il_remove_station); 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci/* 21958c2ecf20Sopenharmony_ci * il_clear_ucode_stations - clear ucode station table bits 21968c2ecf20Sopenharmony_ci * 21978c2ecf20Sopenharmony_ci * This function clears all the bits in the driver indicating 21988c2ecf20Sopenharmony_ci * which stations are active in the ucode. Call when something 21998c2ecf20Sopenharmony_ci * other than explicit station management would cause this in 22008c2ecf20Sopenharmony_ci * the ucode, e.g. unassociated RXON. 22018c2ecf20Sopenharmony_ci */ 22028c2ecf20Sopenharmony_civoid 22038c2ecf20Sopenharmony_ciil_clear_ucode_stations(struct il_priv *il) 22048c2ecf20Sopenharmony_ci{ 22058c2ecf20Sopenharmony_ci int i; 22068c2ecf20Sopenharmony_ci unsigned long flags_spin; 22078c2ecf20Sopenharmony_ci bool cleared = false; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci D_INFO("Clearing ucode stations in driver\n"); 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 22128c2ecf20Sopenharmony_ci for (i = 0; i < il->hw_params.max_stations; i++) { 22138c2ecf20Sopenharmony_ci if (il->stations[i].used & IL_STA_UCODE_ACTIVE) { 22148c2ecf20Sopenharmony_ci D_INFO("Clearing ucode active for station %d\n", i); 22158c2ecf20Sopenharmony_ci il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; 22168c2ecf20Sopenharmony_ci cleared = true; 22178c2ecf20Sopenharmony_ci } 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci if (!cleared) 22228c2ecf20Sopenharmony_ci D_INFO("No active stations found to be cleared\n"); 22238c2ecf20Sopenharmony_ci} 22248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_clear_ucode_stations); 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci/* 22278c2ecf20Sopenharmony_ci * il_restore_stations() - Restore driver known stations to device 22288c2ecf20Sopenharmony_ci * 22298c2ecf20Sopenharmony_ci * All stations considered active by driver, but not present in ucode, is 22308c2ecf20Sopenharmony_ci * restored. 22318c2ecf20Sopenharmony_ci * 22328c2ecf20Sopenharmony_ci * Function sleeps. 22338c2ecf20Sopenharmony_ci */ 22348c2ecf20Sopenharmony_civoid 22358c2ecf20Sopenharmony_ciil_restore_stations(struct il_priv *il) 22368c2ecf20Sopenharmony_ci{ 22378c2ecf20Sopenharmony_ci struct il_addsta_cmd sta_cmd; 22388c2ecf20Sopenharmony_ci struct il_link_quality_cmd lq; 22398c2ecf20Sopenharmony_ci unsigned long flags_spin; 22408c2ecf20Sopenharmony_ci int i; 22418c2ecf20Sopenharmony_ci bool found = false; 22428c2ecf20Sopenharmony_ci int ret; 22438c2ecf20Sopenharmony_ci bool send_lq; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci if (!il_is_ready(il)) { 22468c2ecf20Sopenharmony_ci D_INFO("Not ready yet, not restoring any stations.\n"); 22478c2ecf20Sopenharmony_ci return; 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci D_ASSOC("Restoring all known stations ... start.\n"); 22518c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 22528c2ecf20Sopenharmony_ci for (i = 0; i < il->hw_params.max_stations; i++) { 22538c2ecf20Sopenharmony_ci if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) && 22548c2ecf20Sopenharmony_ci !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) { 22558c2ecf20Sopenharmony_ci D_ASSOC("Restoring sta %pM\n", 22568c2ecf20Sopenharmony_ci il->stations[i].sta.sta.addr); 22578c2ecf20Sopenharmony_ci il->stations[i].sta.mode = 0; 22588c2ecf20Sopenharmony_ci il->stations[i].used |= IL_STA_UCODE_INPROGRESS; 22598c2ecf20Sopenharmony_ci found = true; 22608c2ecf20Sopenharmony_ci } 22618c2ecf20Sopenharmony_ci } 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci for (i = 0; i < il->hw_params.max_stations; i++) { 22648c2ecf20Sopenharmony_ci if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) { 22658c2ecf20Sopenharmony_ci memcpy(&sta_cmd, &il->stations[i].sta, 22668c2ecf20Sopenharmony_ci sizeof(struct il_addsta_cmd)); 22678c2ecf20Sopenharmony_ci send_lq = false; 22688c2ecf20Sopenharmony_ci if (il->stations[i].lq) { 22698c2ecf20Sopenharmony_ci memcpy(&lq, il->stations[i].lq, 22708c2ecf20Sopenharmony_ci sizeof(struct il_link_quality_cmd)); 22718c2ecf20Sopenharmony_ci send_lq = true; 22728c2ecf20Sopenharmony_ci } 22738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 22748c2ecf20Sopenharmony_ci ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); 22758c2ecf20Sopenharmony_ci if (ret) { 22768c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 22778c2ecf20Sopenharmony_ci IL_ERR("Adding station %pM failed.\n", 22788c2ecf20Sopenharmony_ci il->stations[i].sta.sta.addr); 22798c2ecf20Sopenharmony_ci il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE; 22808c2ecf20Sopenharmony_ci il->stations[i].used &= 22818c2ecf20Sopenharmony_ci ~IL_STA_UCODE_INPROGRESS; 22828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, 22838c2ecf20Sopenharmony_ci flags_spin); 22848c2ecf20Sopenharmony_ci } 22858c2ecf20Sopenharmony_ci /* 22868c2ecf20Sopenharmony_ci * Rate scaling has already been initialized, send 22878c2ecf20Sopenharmony_ci * current LQ command 22888c2ecf20Sopenharmony_ci */ 22898c2ecf20Sopenharmony_ci if (send_lq) 22908c2ecf20Sopenharmony_ci il_send_lq_cmd(il, &lq, CMD_SYNC, true); 22918c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 22928c2ecf20Sopenharmony_ci il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS; 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci } 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 22978c2ecf20Sopenharmony_ci if (!found) 22988c2ecf20Sopenharmony_ci D_INFO("Restoring all known stations" 22998c2ecf20Sopenharmony_ci " .... no stations to be restored.\n"); 23008c2ecf20Sopenharmony_ci else 23018c2ecf20Sopenharmony_ci D_INFO("Restoring all known stations" " .... complete.\n"); 23028c2ecf20Sopenharmony_ci} 23038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_restore_stations); 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ciint 23068c2ecf20Sopenharmony_ciil_get_free_ucode_key_idx(struct il_priv *il) 23078c2ecf20Sopenharmony_ci{ 23088c2ecf20Sopenharmony_ci int i; 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci for (i = 0; i < il->sta_key_max_num; i++) 23118c2ecf20Sopenharmony_ci if (!test_and_set_bit(i, &il->ucode_key_table)) 23128c2ecf20Sopenharmony_ci return i; 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci return WEP_INVALID_OFFSET; 23158c2ecf20Sopenharmony_ci} 23168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_free_ucode_key_idx); 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_civoid 23198c2ecf20Sopenharmony_ciil_dealloc_bcast_stations(struct il_priv *il) 23208c2ecf20Sopenharmony_ci{ 23218c2ecf20Sopenharmony_ci unsigned long flags; 23228c2ecf20Sopenharmony_ci int i; 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 23258c2ecf20Sopenharmony_ci for (i = 0; i < il->hw_params.max_stations; i++) { 23268c2ecf20Sopenharmony_ci if (!(il->stations[i].used & IL_STA_BCAST)) 23278c2ecf20Sopenharmony_ci continue; 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; 23308c2ecf20Sopenharmony_ci il->num_stations--; 23318c2ecf20Sopenharmony_ci BUG_ON(il->num_stations < 0); 23328c2ecf20Sopenharmony_ci kfree(il->stations[i].lq); 23338c2ecf20Sopenharmony_ci il->stations[i].lq = NULL; 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 23368c2ecf20Sopenharmony_ci} 23378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(il_dealloc_bcast_stations); 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 23408c2ecf20Sopenharmony_cistatic void 23418c2ecf20Sopenharmony_ciil_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) 23428c2ecf20Sopenharmony_ci{ 23438c2ecf20Sopenharmony_ci int i; 23448c2ecf20Sopenharmony_ci D_RATE("lq station id 0x%x\n", lq->sta_id); 23458c2ecf20Sopenharmony_ci D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk, 23468c2ecf20Sopenharmony_ci lq->general_params.dual_stream_ant_msk); 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) 23498c2ecf20Sopenharmony_ci D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags); 23508c2ecf20Sopenharmony_ci} 23518c2ecf20Sopenharmony_ci#else 23528c2ecf20Sopenharmony_cistatic inline void 23538c2ecf20Sopenharmony_ciil_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) 23548c2ecf20Sopenharmony_ci{ 23558c2ecf20Sopenharmony_ci} 23568c2ecf20Sopenharmony_ci#endif 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci/* 23598c2ecf20Sopenharmony_ci * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity 23608c2ecf20Sopenharmony_ci * 23618c2ecf20Sopenharmony_ci * It sometimes happens when a HT rate has been in use and we 23628c2ecf20Sopenharmony_ci * loose connectivity with AP then mac80211 will first tell us that the 23638c2ecf20Sopenharmony_ci * current channel is not HT anymore before removing the station. In such a 23648c2ecf20Sopenharmony_ci * scenario the RXON flags will be updated to indicate we are not 23658c2ecf20Sopenharmony_ci * communicating HT anymore, but the LQ command may still contain HT rates. 23668c2ecf20Sopenharmony_ci * Test for this to prevent driver from sending LQ command between the time 23678c2ecf20Sopenharmony_ci * RXON flags are updated and when LQ command is updated. 23688c2ecf20Sopenharmony_ci */ 23698c2ecf20Sopenharmony_cistatic bool 23708c2ecf20Sopenharmony_ciil_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq) 23718c2ecf20Sopenharmony_ci{ 23728c2ecf20Sopenharmony_ci int i; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci if (il->ht.enabled) 23758c2ecf20Sopenharmony_ci return true; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci D_INFO("Channel %u is not an HT channel\n", il->active.channel); 23788c2ecf20Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { 23798c2ecf20Sopenharmony_ci if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) { 23808c2ecf20Sopenharmony_ci D_INFO("idx %d of LQ expects HT channel\n", i); 23818c2ecf20Sopenharmony_ci return false; 23828c2ecf20Sopenharmony_ci } 23838c2ecf20Sopenharmony_ci } 23848c2ecf20Sopenharmony_ci return true; 23858c2ecf20Sopenharmony_ci} 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci/* 23888c2ecf20Sopenharmony_ci * il_send_lq_cmd() - Send link quality command 23898c2ecf20Sopenharmony_ci * @init: This command is sent as part of station initialization right 23908c2ecf20Sopenharmony_ci * after station has been added. 23918c2ecf20Sopenharmony_ci * 23928c2ecf20Sopenharmony_ci * The link quality command is sent as the last step of station creation. 23938c2ecf20Sopenharmony_ci * This is the special case in which init is set and we call a callback in 23948c2ecf20Sopenharmony_ci * this case to clear the state indicating that station creation is in 23958c2ecf20Sopenharmony_ci * progress. 23968c2ecf20Sopenharmony_ci */ 23978c2ecf20Sopenharmony_ciint 23988c2ecf20Sopenharmony_ciil_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq, 23998c2ecf20Sopenharmony_ci u8 flags, bool init) 24008c2ecf20Sopenharmony_ci{ 24018c2ecf20Sopenharmony_ci int ret = 0; 24028c2ecf20Sopenharmony_ci unsigned long flags_spin; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci struct il_host_cmd cmd = { 24058c2ecf20Sopenharmony_ci .id = C_TX_LINK_QUALITY_CMD, 24068c2ecf20Sopenharmony_ci .len = sizeof(struct il_link_quality_cmd), 24078c2ecf20Sopenharmony_ci .flags = flags, 24088c2ecf20Sopenharmony_ci .data = lq, 24098c2ecf20Sopenharmony_ci }; 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci if (WARN_ON(lq->sta_id == IL_INVALID_STATION)) 24128c2ecf20Sopenharmony_ci return -EINVAL; 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 24158c2ecf20Sopenharmony_ci if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) { 24168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 24178c2ecf20Sopenharmony_ci return -EINVAL; 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci il_dump_lq_cmd(il, lq); 24228c2ecf20Sopenharmony_ci BUG_ON(init && (cmd.flags & CMD_ASYNC)); 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci if (il_is_lq_table_valid(il, lq)) 24258c2ecf20Sopenharmony_ci ret = il_send_cmd(il, &cmd); 24268c2ecf20Sopenharmony_ci else 24278c2ecf20Sopenharmony_ci ret = -EINVAL; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci if (cmd.flags & CMD_ASYNC) 24308c2ecf20Sopenharmony_ci return ret; 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci if (init) { 24338c2ecf20Sopenharmony_ci D_INFO("init LQ command complete," 24348c2ecf20Sopenharmony_ci " clearing sta addition status for sta %d\n", 24358c2ecf20Sopenharmony_ci lq->sta_id); 24368c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 24378c2ecf20Sopenharmony_ci il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS; 24388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 24398c2ecf20Sopenharmony_ci } 24408c2ecf20Sopenharmony_ci return ret; 24418c2ecf20Sopenharmony_ci} 24428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_lq_cmd); 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ciint 24458c2ecf20Sopenharmony_ciil_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 24468c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 24498c2ecf20Sopenharmony_ci struct il_station_priv_common *sta_common = (void *)sta->drv_priv; 24508c2ecf20Sopenharmony_ci int ret; 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 24538c2ecf20Sopenharmony_ci D_MAC80211("enter station %pM\n", sta->addr); 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci ret = il_remove_station(il, sta_common->sta_id, sta->addr); 24568c2ecf20Sopenharmony_ci if (ret) 24578c2ecf20Sopenharmony_ci IL_ERR("Error removing station %pM\n", sta->addr); 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci D_MAC80211("leave ret %d\n", ret); 24608c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci return ret; 24638c2ecf20Sopenharmony_ci} 24648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_sta_remove); 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci/************************** RX-FUNCTIONS ****************************/ 24678c2ecf20Sopenharmony_ci/* 24688c2ecf20Sopenharmony_ci * Rx theory of operation 24698c2ecf20Sopenharmony_ci * 24708c2ecf20Sopenharmony_ci * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), 24718c2ecf20Sopenharmony_ci * each of which point to Receive Buffers to be filled by the NIC. These get 24728c2ecf20Sopenharmony_ci * used not only for Rx frames, but for any command response or notification 24738c2ecf20Sopenharmony_ci * from the NIC. The driver and NIC manage the Rx buffers by means 24748c2ecf20Sopenharmony_ci * of idxes into the circular buffer. 24758c2ecf20Sopenharmony_ci * 24768c2ecf20Sopenharmony_ci * Rx Queue Indexes 24778c2ecf20Sopenharmony_ci * The host/firmware share two idx registers for managing the Rx buffers. 24788c2ecf20Sopenharmony_ci * 24798c2ecf20Sopenharmony_ci * The READ idx maps to the first position that the firmware may be writing 24808c2ecf20Sopenharmony_ci * to -- the driver can read up to (but not including) this position and get 24818c2ecf20Sopenharmony_ci * good data. 24828c2ecf20Sopenharmony_ci * The READ idx is managed by the firmware once the card is enabled. 24838c2ecf20Sopenharmony_ci * 24848c2ecf20Sopenharmony_ci * The WRITE idx maps to the last position the driver has read from -- the 24858c2ecf20Sopenharmony_ci * position preceding WRITE is the last slot the firmware can place a packet. 24868c2ecf20Sopenharmony_ci * 24878c2ecf20Sopenharmony_ci * The queue is empty (no good data) if WRITE = READ - 1, and is full if 24888c2ecf20Sopenharmony_ci * WRITE = READ. 24898c2ecf20Sopenharmony_ci * 24908c2ecf20Sopenharmony_ci * During initialization, the host sets up the READ queue position to the first 24918c2ecf20Sopenharmony_ci * IDX position, and WRITE to the last (READ - 1 wrapped) 24928c2ecf20Sopenharmony_ci * 24938c2ecf20Sopenharmony_ci * When the firmware places a packet in a buffer, it will advance the READ idx 24948c2ecf20Sopenharmony_ci * and fire the RX interrupt. The driver can then query the READ idx and 24958c2ecf20Sopenharmony_ci * process as many packets as possible, moving the WRITE idx forward as it 24968c2ecf20Sopenharmony_ci * resets the Rx queue buffers with new memory. 24978c2ecf20Sopenharmony_ci * 24988c2ecf20Sopenharmony_ci * The management in the driver is as follows: 24998c2ecf20Sopenharmony_ci * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When 25008c2ecf20Sopenharmony_ci * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled 25018c2ecf20Sopenharmony_ci * to replenish the iwl->rxq->rx_free. 25028c2ecf20Sopenharmony_ci * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the 25038c2ecf20Sopenharmony_ci * iwl->rxq is replenished and the READ IDX is updated (updating the 25048c2ecf20Sopenharmony_ci * 'processed' and 'read' driver idxes as well) 25058c2ecf20Sopenharmony_ci * + A received packet is processed and handed to the kernel network stack, 25068c2ecf20Sopenharmony_ci * detached from the iwl->rxq. The driver 'processed' idx is updated. 25078c2ecf20Sopenharmony_ci * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free 25088c2ecf20Sopenharmony_ci * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ 25098c2ecf20Sopenharmony_ci * IDX is not incremented and iwl->status(RX_STALLED) is set. If there 25108c2ecf20Sopenharmony_ci * were enough free buffers and RX_STALLED is set it is cleared. 25118c2ecf20Sopenharmony_ci * 25128c2ecf20Sopenharmony_ci * 25138c2ecf20Sopenharmony_ci * Driver sequence: 25148c2ecf20Sopenharmony_ci * 25158c2ecf20Sopenharmony_ci * il_rx_queue_alloc() Allocates rx_free 25168c2ecf20Sopenharmony_ci * il_rx_replenish() Replenishes rx_free list from rx_used, and calls 25178c2ecf20Sopenharmony_ci * il_rx_queue_restock 25188c2ecf20Sopenharmony_ci * il_rx_queue_restock() Moves available buffers from rx_free into Rx 25198c2ecf20Sopenharmony_ci * queue, updates firmware pointers, and updates 25208c2ecf20Sopenharmony_ci * the WRITE idx. If insufficient rx_free buffers 25218c2ecf20Sopenharmony_ci * are available, schedules il_rx_replenish 25228c2ecf20Sopenharmony_ci * 25238c2ecf20Sopenharmony_ci * -- enable interrupts -- 25248c2ecf20Sopenharmony_ci * ISR - il_rx() Detach il_rx_bufs from pool up to the 25258c2ecf20Sopenharmony_ci * READ IDX, detaching the SKB from the pool. 25268c2ecf20Sopenharmony_ci * Moves the packet buffer from queue to rx_used. 25278c2ecf20Sopenharmony_ci * Calls il_rx_queue_restock to refill any empty 25288c2ecf20Sopenharmony_ci * slots. 25298c2ecf20Sopenharmony_ci * ... 25308c2ecf20Sopenharmony_ci * 25318c2ecf20Sopenharmony_ci */ 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci/* 25348c2ecf20Sopenharmony_ci * il_rx_queue_space - Return number of free slots available in queue. 25358c2ecf20Sopenharmony_ci */ 25368c2ecf20Sopenharmony_ciint 25378c2ecf20Sopenharmony_ciil_rx_queue_space(const struct il_rx_queue *q) 25388c2ecf20Sopenharmony_ci{ 25398c2ecf20Sopenharmony_ci int s = q->read - q->write; 25408c2ecf20Sopenharmony_ci if (s <= 0) 25418c2ecf20Sopenharmony_ci s += RX_QUEUE_SIZE; 25428c2ecf20Sopenharmony_ci /* keep some buffer to not confuse full and empty queue */ 25438c2ecf20Sopenharmony_ci s -= 2; 25448c2ecf20Sopenharmony_ci if (s < 0) 25458c2ecf20Sopenharmony_ci s = 0; 25468c2ecf20Sopenharmony_ci return s; 25478c2ecf20Sopenharmony_ci} 25488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_space); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci/* 25518c2ecf20Sopenharmony_ci * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue 25528c2ecf20Sopenharmony_ci */ 25538c2ecf20Sopenharmony_civoid 25548c2ecf20Sopenharmony_ciil_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q) 25558c2ecf20Sopenharmony_ci{ 25568c2ecf20Sopenharmony_ci unsigned long flags; 25578c2ecf20Sopenharmony_ci u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg; 25588c2ecf20Sopenharmony_ci u32 reg; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci spin_lock_irqsave(&q->lock, flags); 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci if (q->need_update == 0) 25638c2ecf20Sopenharmony_ci goto exit_unlock; 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci /* If power-saving is in use, make sure device is awake */ 25668c2ecf20Sopenharmony_ci if (test_bit(S_POWER_PMI, &il->status)) { 25678c2ecf20Sopenharmony_ci reg = _il_rd(il, CSR_UCODE_DRV_GP1); 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { 25708c2ecf20Sopenharmony_ci D_INFO("Rx queue requesting wakeup," " GP1 = 0x%x\n", 25718c2ecf20Sopenharmony_ci reg); 25728c2ecf20Sopenharmony_ci il_set_bit(il, CSR_GP_CNTRL, 25738c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 25748c2ecf20Sopenharmony_ci goto exit_unlock; 25758c2ecf20Sopenharmony_ci } 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci q->write_actual = (q->write & ~0x7); 25788c2ecf20Sopenharmony_ci il_wr(il, rx_wrt_ptr_reg, q->write_actual); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci /* Else device is assumed to be awake */ 25818c2ecf20Sopenharmony_ci } else { 25828c2ecf20Sopenharmony_ci /* Device expects a multiple of 8 */ 25838c2ecf20Sopenharmony_ci q->write_actual = (q->write & ~0x7); 25848c2ecf20Sopenharmony_ci il_wr(il, rx_wrt_ptr_reg, q->write_actual); 25858c2ecf20Sopenharmony_ci } 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci q->need_update = 0; 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ciexit_unlock: 25908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&q->lock, flags); 25918c2ecf20Sopenharmony_ci} 25928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_update_write_ptr); 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ciint 25958c2ecf20Sopenharmony_ciil_rx_queue_alloc(struct il_priv *il) 25968c2ecf20Sopenharmony_ci{ 25978c2ecf20Sopenharmony_ci struct il_rx_queue *rxq = &il->rxq; 25988c2ecf20Sopenharmony_ci struct device *dev = &il->pci_dev->dev; 25998c2ecf20Sopenharmony_ci int i; 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci spin_lock_init(&rxq->lock); 26028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rxq->rx_free); 26038c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rxq->rx_used); 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ 26068c2ecf20Sopenharmony_ci rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma, 26078c2ecf20Sopenharmony_ci GFP_KERNEL); 26088c2ecf20Sopenharmony_ci if (!rxq->bd) 26098c2ecf20Sopenharmony_ci goto err_bd; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct il_rb_status), 26128c2ecf20Sopenharmony_ci &rxq->rb_stts_dma, GFP_KERNEL); 26138c2ecf20Sopenharmony_ci if (!rxq->rb_stts) 26148c2ecf20Sopenharmony_ci goto err_rb; 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci /* Fill the rx_used queue with _all_ of the Rx buffers */ 26178c2ecf20Sopenharmony_ci for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) 26188c2ecf20Sopenharmony_ci list_add_tail(&rxq->pool[i].list, &rxq->rx_used); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci /* Set us so that we have processed and used all buffers, but have 26218c2ecf20Sopenharmony_ci * not restocked the Rx queue with fresh buffers */ 26228c2ecf20Sopenharmony_ci rxq->read = rxq->write = 0; 26238c2ecf20Sopenharmony_ci rxq->write_actual = 0; 26248c2ecf20Sopenharmony_ci rxq->free_count = 0; 26258c2ecf20Sopenharmony_ci rxq->need_update = 0; 26268c2ecf20Sopenharmony_ci return 0; 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_cierr_rb: 26298c2ecf20Sopenharmony_ci dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, 26308c2ecf20Sopenharmony_ci rxq->bd_dma); 26318c2ecf20Sopenharmony_cierr_bd: 26328c2ecf20Sopenharmony_ci return -ENOMEM; 26338c2ecf20Sopenharmony_ci} 26348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_alloc); 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_civoid 26378c2ecf20Sopenharmony_ciil_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb) 26388c2ecf20Sopenharmony_ci{ 26398c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 26408c2ecf20Sopenharmony_ci struct il_spectrum_notification *report = &(pkt->u.spectrum_notif); 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci if (!report->state) { 26438c2ecf20Sopenharmony_ci D_11H("Spectrum Measure Notification: Start\n"); 26448c2ecf20Sopenharmony_ci return; 26458c2ecf20Sopenharmony_ci } 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci memcpy(&il->measure_report, report, sizeof(*report)); 26488c2ecf20Sopenharmony_ci il->measurement_status |= MEASUREMENT_READY; 26498c2ecf20Sopenharmony_ci} 26508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_spectrum_measurement); 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci/* 26538c2ecf20Sopenharmony_ci * returns non-zero if packet should be dropped 26548c2ecf20Sopenharmony_ci */ 26558c2ecf20Sopenharmony_ciint 26568c2ecf20Sopenharmony_ciil_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr, 26578c2ecf20Sopenharmony_ci u32 decrypt_res, struct ieee80211_rx_status *stats) 26588c2ecf20Sopenharmony_ci{ 26598c2ecf20Sopenharmony_ci u16 fc = le16_to_cpu(hdr->frame_control); 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci /* 26628c2ecf20Sopenharmony_ci * All contexts have the same setting here due to it being 26638c2ecf20Sopenharmony_ci * a module parameter, so OK to check any context. 26648c2ecf20Sopenharmony_ci */ 26658c2ecf20Sopenharmony_ci if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK) 26668c2ecf20Sopenharmony_ci return 0; 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci if (!(fc & IEEE80211_FCTL_PROTECTED)) 26698c2ecf20Sopenharmony_ci return 0; 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci D_RX("decrypt_res:0x%x\n", decrypt_res); 26728c2ecf20Sopenharmony_ci switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { 26738c2ecf20Sopenharmony_ci case RX_RES_STATUS_SEC_TYPE_TKIP: 26748c2ecf20Sopenharmony_ci /* The uCode has got a bad phase 1 Key, pushes the packet. 26758c2ecf20Sopenharmony_ci * Decryption will be done in SW. */ 26768c2ecf20Sopenharmony_ci if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == 26778c2ecf20Sopenharmony_ci RX_RES_STATUS_BAD_KEY_TTAK) 26788c2ecf20Sopenharmony_ci break; 26798c2ecf20Sopenharmony_ci fallthrough; 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci case RX_RES_STATUS_SEC_TYPE_WEP: 26828c2ecf20Sopenharmony_ci if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == 26838c2ecf20Sopenharmony_ci RX_RES_STATUS_BAD_ICV_MIC) { 26848c2ecf20Sopenharmony_ci /* bad ICV, the packet is destroyed since the 26858c2ecf20Sopenharmony_ci * decryption is inplace, drop it */ 26868c2ecf20Sopenharmony_ci D_RX("Packet destroyed\n"); 26878c2ecf20Sopenharmony_ci return -1; 26888c2ecf20Sopenharmony_ci } 26898c2ecf20Sopenharmony_ci fallthrough; 26908c2ecf20Sopenharmony_ci case RX_RES_STATUS_SEC_TYPE_CCMP: 26918c2ecf20Sopenharmony_ci if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == 26928c2ecf20Sopenharmony_ci RX_RES_STATUS_DECRYPT_OK) { 26938c2ecf20Sopenharmony_ci D_RX("hw decrypt successfully!!!\n"); 26948c2ecf20Sopenharmony_ci stats->flag |= RX_FLAG_DECRYPTED; 26958c2ecf20Sopenharmony_ci } 26968c2ecf20Sopenharmony_ci break; 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci default: 26998c2ecf20Sopenharmony_ci break; 27008c2ecf20Sopenharmony_ci } 27018c2ecf20Sopenharmony_ci return 0; 27028c2ecf20Sopenharmony_ci} 27038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_decrypted_flag); 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci/* 27068c2ecf20Sopenharmony_ci * il_txq_update_write_ptr - Send new write idx to hardware 27078c2ecf20Sopenharmony_ci */ 27088c2ecf20Sopenharmony_civoid 27098c2ecf20Sopenharmony_ciil_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq) 27108c2ecf20Sopenharmony_ci{ 27118c2ecf20Sopenharmony_ci u32 reg = 0; 27128c2ecf20Sopenharmony_ci int txq_id = txq->q.id; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci if (txq->need_update == 0) 27158c2ecf20Sopenharmony_ci return; 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci /* if we're trying to save power */ 27188c2ecf20Sopenharmony_ci if (test_bit(S_POWER_PMI, &il->status)) { 27198c2ecf20Sopenharmony_ci /* wake up nic if it's powered down ... 27208c2ecf20Sopenharmony_ci * uCode will wake up, and interrupt us again, so next 27218c2ecf20Sopenharmony_ci * time we'll skip this part. */ 27228c2ecf20Sopenharmony_ci reg = _il_rd(il, CSR_UCODE_DRV_GP1); 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { 27258c2ecf20Sopenharmony_ci D_INFO("Tx queue %d requesting wakeup," " GP1 = 0x%x\n", 27268c2ecf20Sopenharmony_ci txq_id, reg); 27278c2ecf20Sopenharmony_ci il_set_bit(il, CSR_GP_CNTRL, 27288c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 27298c2ecf20Sopenharmony_ci return; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_ci /* 27358c2ecf20Sopenharmony_ci * else not in power-save mode, 27368c2ecf20Sopenharmony_ci * uCode will never sleep when we're 27378c2ecf20Sopenharmony_ci * trying to tx (during RFKILL, we're not trying to tx). 27388c2ecf20Sopenharmony_ci */ 27398c2ecf20Sopenharmony_ci } else 27408c2ecf20Sopenharmony_ci _il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); 27418c2ecf20Sopenharmony_ci txq->need_update = 0; 27428c2ecf20Sopenharmony_ci} 27438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_txq_update_write_ptr); 27448c2ecf20Sopenharmony_ci 27458c2ecf20Sopenharmony_ci/* 27468c2ecf20Sopenharmony_ci * il_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's 27478c2ecf20Sopenharmony_ci */ 27488c2ecf20Sopenharmony_civoid 27498c2ecf20Sopenharmony_ciil_tx_queue_unmap(struct il_priv *il, int txq_id) 27508c2ecf20Sopenharmony_ci{ 27518c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 27528c2ecf20Sopenharmony_ci struct il_queue *q = &txq->q; 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci if (q->n_bd == 0) 27558c2ecf20Sopenharmony_ci return; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci while (q->write_ptr != q->read_ptr) { 27588c2ecf20Sopenharmony_ci il->ops->txq_free_tfd(il, txq); 27598c2ecf20Sopenharmony_ci q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); 27608c2ecf20Sopenharmony_ci } 27618c2ecf20Sopenharmony_ci} 27628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_unmap); 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci/* 27658c2ecf20Sopenharmony_ci * il_tx_queue_free - Deallocate DMA queue. 27668c2ecf20Sopenharmony_ci * @txq: Transmit queue to deallocate. 27678c2ecf20Sopenharmony_ci * 27688c2ecf20Sopenharmony_ci * Empty queue by removing and destroying all BD's. 27698c2ecf20Sopenharmony_ci * Free all buffers. 27708c2ecf20Sopenharmony_ci * 0-fill, but do not free "txq" descriptor structure. 27718c2ecf20Sopenharmony_ci */ 27728c2ecf20Sopenharmony_civoid 27738c2ecf20Sopenharmony_ciil_tx_queue_free(struct il_priv *il, int txq_id) 27748c2ecf20Sopenharmony_ci{ 27758c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 27768c2ecf20Sopenharmony_ci struct device *dev = &il->pci_dev->dev; 27778c2ecf20Sopenharmony_ci int i; 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci il_tx_queue_unmap(il, txq_id); 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci /* De-alloc array of command/tx buffers */ 27828c2ecf20Sopenharmony_ci if (txq->cmd) { 27838c2ecf20Sopenharmony_ci for (i = 0; i < TFD_TX_CMD_SLOTS; i++) 27848c2ecf20Sopenharmony_ci kfree(txq->cmd[i]); 27858c2ecf20Sopenharmony_ci } 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_ci /* De-alloc circular buffer of TFDs */ 27888c2ecf20Sopenharmony_ci if (txq->q.n_bd) 27898c2ecf20Sopenharmony_ci dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, 27908c2ecf20Sopenharmony_ci txq->tfds, txq->q.dma_addr); 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci /* De-alloc array of per-TFD driver data */ 27938c2ecf20Sopenharmony_ci kfree(txq->skbs); 27948c2ecf20Sopenharmony_ci txq->skbs = NULL; 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci /* deallocate arrays */ 27978c2ecf20Sopenharmony_ci kfree(txq->cmd); 27988c2ecf20Sopenharmony_ci kfree(txq->meta); 27998c2ecf20Sopenharmony_ci txq->cmd = NULL; 28008c2ecf20Sopenharmony_ci txq->meta = NULL; 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci /* 0-fill queue descriptor structure */ 28038c2ecf20Sopenharmony_ci memset(txq, 0, sizeof(*txq)); 28048c2ecf20Sopenharmony_ci} 28058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_free); 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci/* 28088c2ecf20Sopenharmony_ci * il_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue 28098c2ecf20Sopenharmony_ci */ 28108c2ecf20Sopenharmony_civoid 28118c2ecf20Sopenharmony_ciil_cmd_queue_unmap(struct il_priv *il) 28128c2ecf20Sopenharmony_ci{ 28138c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[il->cmd_queue]; 28148c2ecf20Sopenharmony_ci struct il_queue *q = &txq->q; 28158c2ecf20Sopenharmony_ci int i; 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci if (q->n_bd == 0) 28188c2ecf20Sopenharmony_ci return; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci while (q->read_ptr != q->write_ptr) { 28218c2ecf20Sopenharmony_ci i = il_get_cmd_idx(q, q->read_ptr, 0); 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_ci if (txq->meta[i].flags & CMD_MAPPED) { 28248c2ecf20Sopenharmony_ci pci_unmap_single(il->pci_dev, 28258c2ecf20Sopenharmony_ci dma_unmap_addr(&txq->meta[i], mapping), 28268c2ecf20Sopenharmony_ci dma_unmap_len(&txq->meta[i], len), 28278c2ecf20Sopenharmony_ci PCI_DMA_BIDIRECTIONAL); 28288c2ecf20Sopenharmony_ci txq->meta[i].flags = 0; 28298c2ecf20Sopenharmony_ci } 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); 28328c2ecf20Sopenharmony_ci } 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci i = q->n_win; 28358c2ecf20Sopenharmony_ci if (txq->meta[i].flags & CMD_MAPPED) { 28368c2ecf20Sopenharmony_ci pci_unmap_single(il->pci_dev, 28378c2ecf20Sopenharmony_ci dma_unmap_addr(&txq->meta[i], mapping), 28388c2ecf20Sopenharmony_ci dma_unmap_len(&txq->meta[i], len), 28398c2ecf20Sopenharmony_ci PCI_DMA_BIDIRECTIONAL); 28408c2ecf20Sopenharmony_ci txq->meta[i].flags = 0; 28418c2ecf20Sopenharmony_ci } 28428c2ecf20Sopenharmony_ci} 28438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_cmd_queue_unmap); 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci/* 28468c2ecf20Sopenharmony_ci * il_cmd_queue_free - Deallocate DMA queue. 28478c2ecf20Sopenharmony_ci * 28488c2ecf20Sopenharmony_ci * Empty queue by removing and destroying all BD's. 28498c2ecf20Sopenharmony_ci * Free all buffers. 28508c2ecf20Sopenharmony_ci * 0-fill, but do not free "txq" descriptor structure. 28518c2ecf20Sopenharmony_ci */ 28528c2ecf20Sopenharmony_civoid 28538c2ecf20Sopenharmony_ciil_cmd_queue_free(struct il_priv *il) 28548c2ecf20Sopenharmony_ci{ 28558c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[il->cmd_queue]; 28568c2ecf20Sopenharmony_ci struct device *dev = &il->pci_dev->dev; 28578c2ecf20Sopenharmony_ci int i; 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci il_cmd_queue_unmap(il); 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci /* De-alloc array of command/tx buffers */ 28628c2ecf20Sopenharmony_ci if (txq->cmd) { 28638c2ecf20Sopenharmony_ci for (i = 0; i <= TFD_CMD_SLOTS; i++) 28648c2ecf20Sopenharmony_ci kfree(txq->cmd[i]); 28658c2ecf20Sopenharmony_ci } 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci /* De-alloc circular buffer of TFDs */ 28688c2ecf20Sopenharmony_ci if (txq->q.n_bd) 28698c2ecf20Sopenharmony_ci dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, 28708c2ecf20Sopenharmony_ci txq->tfds, txq->q.dma_addr); 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci /* deallocate arrays */ 28738c2ecf20Sopenharmony_ci kfree(txq->cmd); 28748c2ecf20Sopenharmony_ci kfree(txq->meta); 28758c2ecf20Sopenharmony_ci txq->cmd = NULL; 28768c2ecf20Sopenharmony_ci txq->meta = NULL; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci /* 0-fill queue descriptor structure */ 28798c2ecf20Sopenharmony_ci memset(txq, 0, sizeof(*txq)); 28808c2ecf20Sopenharmony_ci} 28818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_cmd_queue_free); 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** 28848c2ecf20Sopenharmony_ci * DMA services 28858c2ecf20Sopenharmony_ci * 28868c2ecf20Sopenharmony_ci * Theory of operation 28878c2ecf20Sopenharmony_ci * 28888c2ecf20Sopenharmony_ci * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer 28898c2ecf20Sopenharmony_ci * of buffer descriptors, each of which points to one or more data buffers for 28908c2ecf20Sopenharmony_ci * the device to read from or fill. Driver and device exchange status of each 28918c2ecf20Sopenharmony_ci * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty 28928c2ecf20Sopenharmony_ci * entries in each circular buffer, to protect against confusing empty and full 28938c2ecf20Sopenharmony_ci * queue states. 28948c2ecf20Sopenharmony_ci * 28958c2ecf20Sopenharmony_ci * The device reads or writes the data in the queues via the device's several 28968c2ecf20Sopenharmony_ci * DMA/FIFO channels. Each queue is mapped to a single DMA channel. 28978c2ecf20Sopenharmony_ci * 28988c2ecf20Sopenharmony_ci * For Tx queue, there are low mark and high mark limits. If, after queuing 28998c2ecf20Sopenharmony_ci * the packet for Tx, free space become < low mark, Tx queue stopped. When 29008c2ecf20Sopenharmony_ci * reclaiming packets (on 'tx done IRQ), if free space become > high mark, 29018c2ecf20Sopenharmony_ci * Tx queue resumed. 29028c2ecf20Sopenharmony_ci * 29038c2ecf20Sopenharmony_ci * See more detailed info in 4965.h. 29048c2ecf20Sopenharmony_ci ***************************************************/ 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_ciint 29078c2ecf20Sopenharmony_ciil_queue_space(const struct il_queue *q) 29088c2ecf20Sopenharmony_ci{ 29098c2ecf20Sopenharmony_ci int s = q->read_ptr - q->write_ptr; 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci if (q->read_ptr > q->write_ptr) 29128c2ecf20Sopenharmony_ci s -= q->n_bd; 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci if (s <= 0) 29158c2ecf20Sopenharmony_ci s += q->n_win; 29168c2ecf20Sopenharmony_ci /* keep some reserve to not confuse empty and full situations */ 29178c2ecf20Sopenharmony_ci s -= 2; 29188c2ecf20Sopenharmony_ci if (s < 0) 29198c2ecf20Sopenharmony_ci s = 0; 29208c2ecf20Sopenharmony_ci return s; 29218c2ecf20Sopenharmony_ci} 29228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_queue_space); 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci/* 29268c2ecf20Sopenharmony_ci * il_queue_init - Initialize queue's high/low-water and read/write idxes 29278c2ecf20Sopenharmony_ci */ 29288c2ecf20Sopenharmony_cistatic int 29298c2ecf20Sopenharmony_ciil_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id) 29308c2ecf20Sopenharmony_ci{ 29318c2ecf20Sopenharmony_ci /* 29328c2ecf20Sopenharmony_ci * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise 29338c2ecf20Sopenharmony_ci * il_queue_inc_wrap and il_queue_dec_wrap are broken. 29348c2ecf20Sopenharmony_ci */ 29358c2ecf20Sopenharmony_ci BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); 29368c2ecf20Sopenharmony_ci /* FIXME: remove q->n_bd */ 29378c2ecf20Sopenharmony_ci q->n_bd = TFD_QUEUE_SIZE_MAX; 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci q->n_win = slots; 29408c2ecf20Sopenharmony_ci q->id = id; 29418c2ecf20Sopenharmony_ci 29428c2ecf20Sopenharmony_ci /* slots_must be power-of-two size, otherwise 29438c2ecf20Sopenharmony_ci * il_get_cmd_idx is broken. */ 29448c2ecf20Sopenharmony_ci BUG_ON(!is_power_of_2(slots)); 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci q->low_mark = q->n_win / 4; 29478c2ecf20Sopenharmony_ci if (q->low_mark < 4) 29488c2ecf20Sopenharmony_ci q->low_mark = 4; 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ci q->high_mark = q->n_win / 8; 29518c2ecf20Sopenharmony_ci if (q->high_mark < 2) 29528c2ecf20Sopenharmony_ci q->high_mark = 2; 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci q->write_ptr = q->read_ptr = 0; 29558c2ecf20Sopenharmony_ci 29568c2ecf20Sopenharmony_ci return 0; 29578c2ecf20Sopenharmony_ci} 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci/* 29608c2ecf20Sopenharmony_ci * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue 29618c2ecf20Sopenharmony_ci */ 29628c2ecf20Sopenharmony_cistatic int 29638c2ecf20Sopenharmony_ciil_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id) 29648c2ecf20Sopenharmony_ci{ 29658c2ecf20Sopenharmony_ci struct device *dev = &il->pci_dev->dev; 29668c2ecf20Sopenharmony_ci size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX; 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_ci /* Driver ilate data, only for Tx (not command) queues, 29698c2ecf20Sopenharmony_ci * not shared with device. */ 29708c2ecf20Sopenharmony_ci if (id != il->cmd_queue) { 29718c2ecf20Sopenharmony_ci txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, 29728c2ecf20Sopenharmony_ci sizeof(struct sk_buff *), 29738c2ecf20Sopenharmony_ci GFP_KERNEL); 29748c2ecf20Sopenharmony_ci if (!txq->skbs) { 29758c2ecf20Sopenharmony_ci IL_ERR("Fail to alloc skbs\n"); 29768c2ecf20Sopenharmony_ci goto error; 29778c2ecf20Sopenharmony_ci } 29788c2ecf20Sopenharmony_ci } else 29798c2ecf20Sopenharmony_ci txq->skbs = NULL; 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci /* Circular buffer of transmit frame descriptors (TFDs), 29828c2ecf20Sopenharmony_ci * shared with device */ 29838c2ecf20Sopenharmony_ci txq->tfds = 29848c2ecf20Sopenharmony_ci dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL); 29858c2ecf20Sopenharmony_ci if (!txq->tfds) 29868c2ecf20Sopenharmony_ci goto error; 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci txq->q.id = id; 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci return 0; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_cierror: 29938c2ecf20Sopenharmony_ci kfree(txq->skbs); 29948c2ecf20Sopenharmony_ci txq->skbs = NULL; 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci return -ENOMEM; 29978c2ecf20Sopenharmony_ci} 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci/* 30008c2ecf20Sopenharmony_ci * il_tx_queue_init - Allocate and initialize one tx/cmd queue 30018c2ecf20Sopenharmony_ci */ 30028c2ecf20Sopenharmony_ciint 30038c2ecf20Sopenharmony_ciil_tx_queue_init(struct il_priv *il, u32 txq_id) 30048c2ecf20Sopenharmony_ci{ 30058c2ecf20Sopenharmony_ci int i, len, ret; 30068c2ecf20Sopenharmony_ci int slots, actual_slots; 30078c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci /* 30108c2ecf20Sopenharmony_ci * Alloc buffer array for commands (Tx or other types of commands). 30118c2ecf20Sopenharmony_ci * For the command queue (#4/#9), allocate command space + one big 30128c2ecf20Sopenharmony_ci * command for scan, since scan command is very huge; the system will 30138c2ecf20Sopenharmony_ci * not have two scans at the same time, so only one is needed. 30148c2ecf20Sopenharmony_ci * For normal Tx queues (all other queues), no super-size command 30158c2ecf20Sopenharmony_ci * space is needed. 30168c2ecf20Sopenharmony_ci */ 30178c2ecf20Sopenharmony_ci if (txq_id == il->cmd_queue) { 30188c2ecf20Sopenharmony_ci slots = TFD_CMD_SLOTS; 30198c2ecf20Sopenharmony_ci actual_slots = slots + 1; 30208c2ecf20Sopenharmony_ci } else { 30218c2ecf20Sopenharmony_ci slots = TFD_TX_CMD_SLOTS; 30228c2ecf20Sopenharmony_ci actual_slots = slots; 30238c2ecf20Sopenharmony_ci } 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci txq->meta = 30268c2ecf20Sopenharmony_ci kcalloc(actual_slots, sizeof(struct il_cmd_meta), GFP_KERNEL); 30278c2ecf20Sopenharmony_ci txq->cmd = 30288c2ecf20Sopenharmony_ci kcalloc(actual_slots, sizeof(struct il_device_cmd *), GFP_KERNEL); 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci if (!txq->meta || !txq->cmd) 30318c2ecf20Sopenharmony_ci goto out_free_arrays; 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci len = sizeof(struct il_device_cmd); 30348c2ecf20Sopenharmony_ci for (i = 0; i < actual_slots; i++) { 30358c2ecf20Sopenharmony_ci /* only happens for cmd queue */ 30368c2ecf20Sopenharmony_ci if (i == slots) 30378c2ecf20Sopenharmony_ci len = IL_MAX_CMD_SIZE; 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci txq->cmd[i] = kmalloc(len, GFP_KERNEL); 30408c2ecf20Sopenharmony_ci if (!txq->cmd[i]) 30418c2ecf20Sopenharmony_ci goto err; 30428c2ecf20Sopenharmony_ci } 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci /* Alloc driver data array and TFD circular buffer */ 30458c2ecf20Sopenharmony_ci ret = il_tx_queue_alloc(il, txq, txq_id); 30468c2ecf20Sopenharmony_ci if (ret) 30478c2ecf20Sopenharmony_ci goto err; 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ci txq->need_update = 0; 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci /* 30528c2ecf20Sopenharmony_ci * For the default queues 0-3, set up the swq_id 30538c2ecf20Sopenharmony_ci * already -- all others need to get one later 30548c2ecf20Sopenharmony_ci * (if they need one at all). 30558c2ecf20Sopenharmony_ci */ 30568c2ecf20Sopenharmony_ci if (txq_id < 4) 30578c2ecf20Sopenharmony_ci il_set_swq_id(txq, txq_id, txq_id); 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci /* Initialize queue's high/low-water marks, and head/tail idxes */ 30608c2ecf20Sopenharmony_ci il_queue_init(il, &txq->q, slots, txq_id); 30618c2ecf20Sopenharmony_ci 30628c2ecf20Sopenharmony_ci /* Tell device where to find queue */ 30638c2ecf20Sopenharmony_ci il->ops->txq_init(il, txq); 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci return 0; 30668c2ecf20Sopenharmony_cierr: 30678c2ecf20Sopenharmony_ci for (i = 0; i < actual_slots; i++) 30688c2ecf20Sopenharmony_ci kfree(txq->cmd[i]); 30698c2ecf20Sopenharmony_ciout_free_arrays: 30708c2ecf20Sopenharmony_ci kfree(txq->meta); 30718c2ecf20Sopenharmony_ci txq->meta = NULL; 30728c2ecf20Sopenharmony_ci kfree(txq->cmd); 30738c2ecf20Sopenharmony_ci txq->cmd = NULL; 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci return -ENOMEM; 30768c2ecf20Sopenharmony_ci} 30778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_init); 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_civoid 30808c2ecf20Sopenharmony_ciil_tx_queue_reset(struct il_priv *il, u32 txq_id) 30818c2ecf20Sopenharmony_ci{ 30828c2ecf20Sopenharmony_ci int slots, actual_slots; 30838c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci if (txq_id == il->cmd_queue) { 30868c2ecf20Sopenharmony_ci slots = TFD_CMD_SLOTS; 30878c2ecf20Sopenharmony_ci actual_slots = TFD_CMD_SLOTS + 1; 30888c2ecf20Sopenharmony_ci } else { 30898c2ecf20Sopenharmony_ci slots = TFD_TX_CMD_SLOTS; 30908c2ecf20Sopenharmony_ci actual_slots = TFD_TX_CMD_SLOTS; 30918c2ecf20Sopenharmony_ci } 30928c2ecf20Sopenharmony_ci 30938c2ecf20Sopenharmony_ci memset(txq->meta, 0, sizeof(struct il_cmd_meta) * actual_slots); 30948c2ecf20Sopenharmony_ci txq->need_update = 0; 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci /* Initialize queue's high/low-water marks, and head/tail idxes */ 30978c2ecf20Sopenharmony_ci il_queue_init(il, &txq->q, slots, txq_id); 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci /* Tell device where to find queue */ 31008c2ecf20Sopenharmony_ci il->ops->txq_init(il, txq); 31018c2ecf20Sopenharmony_ci} 31028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_reset); 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci/*************** HOST COMMAND QUEUE FUNCTIONS *****/ 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci/* 31078c2ecf20Sopenharmony_ci * il_enqueue_hcmd - enqueue a uCode command 31088c2ecf20Sopenharmony_ci * @il: device ilate data point 31098c2ecf20Sopenharmony_ci * @cmd: a point to the ucode command structure 31108c2ecf20Sopenharmony_ci * 31118c2ecf20Sopenharmony_ci * The function returns < 0 values to indicate the operation is 31128c2ecf20Sopenharmony_ci * failed. On success, it turns the idx (> 0) of command in the 31138c2ecf20Sopenharmony_ci * command queue. 31148c2ecf20Sopenharmony_ci */ 31158c2ecf20Sopenharmony_ciint 31168c2ecf20Sopenharmony_ciil_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd) 31178c2ecf20Sopenharmony_ci{ 31188c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[il->cmd_queue]; 31198c2ecf20Sopenharmony_ci struct il_queue *q = &txq->q; 31208c2ecf20Sopenharmony_ci struct il_device_cmd *out_cmd; 31218c2ecf20Sopenharmony_ci struct il_cmd_meta *out_meta; 31228c2ecf20Sopenharmony_ci dma_addr_t phys_addr; 31238c2ecf20Sopenharmony_ci unsigned long flags; 31248c2ecf20Sopenharmony_ci u32 idx; 31258c2ecf20Sopenharmony_ci u16 fix_size; 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci cmd->len = il->ops->get_hcmd_size(cmd->id, cmd->len); 31288c2ecf20Sopenharmony_ci fix_size = (u16) (cmd->len + sizeof(out_cmd->hdr)); 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ci /* If any of the command structures end up being larger than 31318c2ecf20Sopenharmony_ci * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then 31328c2ecf20Sopenharmony_ci * we will need to increase the size of the TFD entries 31338c2ecf20Sopenharmony_ci * Also, check to see if command buffer should not exceed the size 31348c2ecf20Sopenharmony_ci * of device_cmd and max_cmd_size. */ 31358c2ecf20Sopenharmony_ci BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && 31368c2ecf20Sopenharmony_ci !(cmd->flags & CMD_SIZE_HUGE)); 31378c2ecf20Sopenharmony_ci BUG_ON(fix_size > IL_MAX_CMD_SIZE); 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci if (il_is_rfkill(il) || il_is_ctkill(il)) { 31408c2ecf20Sopenharmony_ci IL_WARN("Not sending command - %s KILL\n", 31418c2ecf20Sopenharmony_ci il_is_rfkill(il) ? "RF" : "CT"); 31428c2ecf20Sopenharmony_ci return -EIO; 31438c2ecf20Sopenharmony_ci } 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->hcmd_lock, flags); 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ci if (il_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { 31488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->hcmd_lock, flags); 31498c2ecf20Sopenharmony_ci 31508c2ecf20Sopenharmony_ci IL_ERR("Restarting adapter due to command queue full\n"); 31518c2ecf20Sopenharmony_ci queue_work(il->workqueue, &il->restart); 31528c2ecf20Sopenharmony_ci return -ENOSPC; 31538c2ecf20Sopenharmony_ci } 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci idx = il_get_cmd_idx(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); 31568c2ecf20Sopenharmony_ci out_cmd = txq->cmd[idx]; 31578c2ecf20Sopenharmony_ci out_meta = &txq->meta[idx]; 31588c2ecf20Sopenharmony_ci 31598c2ecf20Sopenharmony_ci if (WARN_ON(out_meta->flags & CMD_MAPPED)) { 31608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->hcmd_lock, flags); 31618c2ecf20Sopenharmony_ci return -ENOSPC; 31628c2ecf20Sopenharmony_ci } 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ 31658c2ecf20Sopenharmony_ci out_meta->flags = cmd->flags | CMD_MAPPED; 31668c2ecf20Sopenharmony_ci if (cmd->flags & CMD_WANT_SKB) 31678c2ecf20Sopenharmony_ci out_meta->source = cmd; 31688c2ecf20Sopenharmony_ci if (cmd->flags & CMD_ASYNC) 31698c2ecf20Sopenharmony_ci out_meta->callback = cmd->callback; 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci out_cmd->hdr.cmd = cmd->id; 31728c2ecf20Sopenharmony_ci memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci /* At this point, the out_cmd now has all of the incoming cmd 31758c2ecf20Sopenharmony_ci * information */ 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_ci out_cmd->hdr.flags = 0; 31788c2ecf20Sopenharmony_ci out_cmd->hdr.sequence = 31798c2ecf20Sopenharmony_ci cpu_to_le16(QUEUE_TO_SEQ(il->cmd_queue) | IDX_TO_SEQ(q->write_ptr)); 31808c2ecf20Sopenharmony_ci if (cmd->flags & CMD_SIZE_HUGE) 31818c2ecf20Sopenharmony_ci out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 31848c2ecf20Sopenharmony_ci switch (out_cmd->hdr.cmd) { 31858c2ecf20Sopenharmony_ci case C_TX_LINK_QUALITY_CMD: 31868c2ecf20Sopenharmony_ci case C_SENSITIVITY: 31878c2ecf20Sopenharmony_ci D_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, " 31888c2ecf20Sopenharmony_ci "%d bytes at %d[%d]:%d\n", 31898c2ecf20Sopenharmony_ci il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, 31908c2ecf20Sopenharmony_ci le16_to_cpu(out_cmd->hdr.sequence), fix_size, 31918c2ecf20Sopenharmony_ci q->write_ptr, idx, il->cmd_queue); 31928c2ecf20Sopenharmony_ci break; 31938c2ecf20Sopenharmony_ci default: 31948c2ecf20Sopenharmony_ci D_HC("Sending command %s (#%x), seq: 0x%04X, " 31958c2ecf20Sopenharmony_ci "%d bytes at %d[%d]:%d\n", 31968c2ecf20Sopenharmony_ci il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, 31978c2ecf20Sopenharmony_ci le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr, 31988c2ecf20Sopenharmony_ci idx, il->cmd_queue); 31998c2ecf20Sopenharmony_ci } 32008c2ecf20Sopenharmony_ci#endif 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci phys_addr = 32038c2ecf20Sopenharmony_ci pci_map_single(il->pci_dev, &out_cmd->hdr, fix_size, 32048c2ecf20Sopenharmony_ci PCI_DMA_BIDIRECTIONAL); 32058c2ecf20Sopenharmony_ci if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) { 32068c2ecf20Sopenharmony_ci idx = -ENOMEM; 32078c2ecf20Sopenharmony_ci goto out; 32088c2ecf20Sopenharmony_ci } 32098c2ecf20Sopenharmony_ci dma_unmap_addr_set(out_meta, mapping, phys_addr); 32108c2ecf20Sopenharmony_ci dma_unmap_len_set(out_meta, len, fix_size); 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci txq->need_update = 1; 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_ci if (il->ops->txq_update_byte_cnt_tbl) 32158c2ecf20Sopenharmony_ci /* Set up entry in queue's byte count circular buffer */ 32168c2ecf20Sopenharmony_ci il->ops->txq_update_byte_cnt_tbl(il, txq, 0); 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_ci il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, fix_size, 1, 32198c2ecf20Sopenharmony_ci U32_PAD(cmd->len)); 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci /* Increment and update queue's write idx */ 32228c2ecf20Sopenharmony_ci q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); 32238c2ecf20Sopenharmony_ci il_txq_update_write_ptr(il, txq); 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ciout: 32268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->hcmd_lock, flags); 32278c2ecf20Sopenharmony_ci return idx; 32288c2ecf20Sopenharmony_ci} 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci/* 32318c2ecf20Sopenharmony_ci * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd 32328c2ecf20Sopenharmony_ci * 32338c2ecf20Sopenharmony_ci * When FW advances 'R' idx, all entries between old and new 'R' idx 32348c2ecf20Sopenharmony_ci * need to be reclaimed. As result, some free space forms. If there is 32358c2ecf20Sopenharmony_ci * enough free space (> low mark), wake the stack that feeds us. 32368c2ecf20Sopenharmony_ci */ 32378c2ecf20Sopenharmony_cistatic void 32388c2ecf20Sopenharmony_ciil_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx) 32398c2ecf20Sopenharmony_ci{ 32408c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 32418c2ecf20Sopenharmony_ci struct il_queue *q = &txq->q; 32428c2ecf20Sopenharmony_ci int nfreed = 0; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { 32458c2ecf20Sopenharmony_ci IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " 32468c2ecf20Sopenharmony_ci "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, 32478c2ecf20Sopenharmony_ci q->write_ptr, q->read_ptr); 32488c2ecf20Sopenharmony_ci return; 32498c2ecf20Sopenharmony_ci } 32508c2ecf20Sopenharmony_ci 32518c2ecf20Sopenharmony_ci for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; 32528c2ecf20Sopenharmony_ci q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { 32538c2ecf20Sopenharmony_ci 32548c2ecf20Sopenharmony_ci if (nfreed++ > 0) { 32558c2ecf20Sopenharmony_ci IL_ERR("HCMD skipped: idx (%d) %d %d\n", idx, 32568c2ecf20Sopenharmony_ci q->write_ptr, q->read_ptr); 32578c2ecf20Sopenharmony_ci queue_work(il->workqueue, &il->restart); 32588c2ecf20Sopenharmony_ci } 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_ci } 32618c2ecf20Sopenharmony_ci} 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci/* 32648c2ecf20Sopenharmony_ci * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them 32658c2ecf20Sopenharmony_ci * @rxb: Rx buffer to reclaim 32668c2ecf20Sopenharmony_ci * 32678c2ecf20Sopenharmony_ci * If an Rx buffer has an async callback associated with it the callback 32688c2ecf20Sopenharmony_ci * will be executed. The attached skb (if present) will only be freed 32698c2ecf20Sopenharmony_ci * if the callback returns 1 32708c2ecf20Sopenharmony_ci */ 32718c2ecf20Sopenharmony_civoid 32728c2ecf20Sopenharmony_ciil_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb) 32738c2ecf20Sopenharmony_ci{ 32748c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 32758c2ecf20Sopenharmony_ci u16 sequence = le16_to_cpu(pkt->hdr.sequence); 32768c2ecf20Sopenharmony_ci int txq_id = SEQ_TO_QUEUE(sequence); 32778c2ecf20Sopenharmony_ci int idx = SEQ_TO_IDX(sequence); 32788c2ecf20Sopenharmony_ci int cmd_idx; 32798c2ecf20Sopenharmony_ci bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); 32808c2ecf20Sopenharmony_ci struct il_device_cmd *cmd; 32818c2ecf20Sopenharmony_ci struct il_cmd_meta *meta; 32828c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[il->cmd_queue]; 32838c2ecf20Sopenharmony_ci unsigned long flags; 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci /* If a Tx command is being handled and it isn't in the actual 32868c2ecf20Sopenharmony_ci * command queue then there a command routing bug has been introduced 32878c2ecf20Sopenharmony_ci * in the queue management code. */ 32888c2ecf20Sopenharmony_ci if (WARN 32898c2ecf20Sopenharmony_ci (txq_id != il->cmd_queue, 32908c2ecf20Sopenharmony_ci "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", 32918c2ecf20Sopenharmony_ci txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr, 32928c2ecf20Sopenharmony_ci il->txq[il->cmd_queue].q.write_ptr)) { 32938c2ecf20Sopenharmony_ci il_print_hex_error(il, pkt, 32); 32948c2ecf20Sopenharmony_ci return; 32958c2ecf20Sopenharmony_ci } 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_ci cmd_idx = il_get_cmd_idx(&txq->q, idx, huge); 32988c2ecf20Sopenharmony_ci cmd = txq->cmd[cmd_idx]; 32998c2ecf20Sopenharmony_ci meta = &txq->meta[cmd_idx]; 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_ci txq->time_stamp = jiffies; 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci pci_unmap_single(il->pci_dev, dma_unmap_addr(meta, mapping), 33048c2ecf20Sopenharmony_ci dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL); 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci /* Input error checking is done when commands are added to queue. */ 33078c2ecf20Sopenharmony_ci if (meta->flags & CMD_WANT_SKB) { 33088c2ecf20Sopenharmony_ci meta->source->reply_page = (unsigned long)rxb_addr(rxb); 33098c2ecf20Sopenharmony_ci rxb->page = NULL; 33108c2ecf20Sopenharmony_ci } else if (meta->callback) 33118c2ecf20Sopenharmony_ci meta->callback(il, cmd, pkt); 33128c2ecf20Sopenharmony_ci 33138c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->hcmd_lock, flags); 33148c2ecf20Sopenharmony_ci 33158c2ecf20Sopenharmony_ci il_hcmd_queue_reclaim(il, txq_id, idx, cmd_idx); 33168c2ecf20Sopenharmony_ci 33178c2ecf20Sopenharmony_ci if (!(meta->flags & CMD_ASYNC)) { 33188c2ecf20Sopenharmony_ci clear_bit(S_HCMD_ACTIVE, &il->status); 33198c2ecf20Sopenharmony_ci D_INFO("Clearing HCMD_ACTIVE for command %s\n", 33208c2ecf20Sopenharmony_ci il_get_cmd_string(cmd->hdr.cmd)); 33218c2ecf20Sopenharmony_ci wake_up(&il->wait_command_queue); 33228c2ecf20Sopenharmony_ci } 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_ci /* Mark as unmapped */ 33258c2ecf20Sopenharmony_ci meta->flags = 0; 33268c2ecf20Sopenharmony_ci 33278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->hcmd_lock, flags); 33288c2ecf20Sopenharmony_ci} 33298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_cmd_complete); 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965"); 33328c2ecf20Sopenharmony_ciMODULE_VERSION(IWLWIFI_VERSION); 33338c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); 33348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 33358c2ecf20Sopenharmony_ci 33368c2ecf20Sopenharmony_ci/* 33378c2ecf20Sopenharmony_ci * set bt_coex_active to true, uCode will do kill/defer 33388c2ecf20Sopenharmony_ci * every time the priority line is asserted (BT is sending signals on the 33398c2ecf20Sopenharmony_ci * priority line in the PCIx). 33408c2ecf20Sopenharmony_ci * set bt_coex_active to false, uCode will ignore the BT activity and 33418c2ecf20Sopenharmony_ci * perform the normal operation 33428c2ecf20Sopenharmony_ci * 33438c2ecf20Sopenharmony_ci * User might experience transmit issue on some platform due to WiFi/BT 33448c2ecf20Sopenharmony_ci * co-exist problem. The possible behaviors are: 33458c2ecf20Sopenharmony_ci * Able to scan and finding all the available AP 33468c2ecf20Sopenharmony_ci * Not able to associate with any AP 33478c2ecf20Sopenharmony_ci * On those platforms, WiFi communication can be restored by set 33488c2ecf20Sopenharmony_ci * "bt_coex_active" module parameter to "false" 33498c2ecf20Sopenharmony_ci * 33508c2ecf20Sopenharmony_ci * default: bt_coex_active = true (BT_COEX_ENABLE) 33518c2ecf20Sopenharmony_ci */ 33528c2ecf20Sopenharmony_cistatic bool bt_coex_active = true; 33538c2ecf20Sopenharmony_cimodule_param(bt_coex_active, bool, 0444); 33548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist"); 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ciu32 il_debug_level; 33578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_debug_level); 33588c2ecf20Sopenharmony_ci 33598c2ecf20Sopenharmony_ciconst u8 il_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 33608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_bcast_addr); 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_ci#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ 33638c2ecf20Sopenharmony_ci#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ 33648c2ecf20Sopenharmony_cistatic void 33658c2ecf20Sopenharmony_ciil_init_ht_hw_capab(const struct il_priv *il, 33668c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_info, 33678c2ecf20Sopenharmony_ci enum nl80211_band band) 33688c2ecf20Sopenharmony_ci{ 33698c2ecf20Sopenharmony_ci u16 max_bit_rate = 0; 33708c2ecf20Sopenharmony_ci u8 rx_chains_num = il->hw_params.rx_chains_num; 33718c2ecf20Sopenharmony_ci u8 tx_chains_num = il->hw_params.tx_chains_num; 33728c2ecf20Sopenharmony_ci 33738c2ecf20Sopenharmony_ci ht_info->cap = 0; 33748c2ecf20Sopenharmony_ci memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); 33758c2ecf20Sopenharmony_ci 33768c2ecf20Sopenharmony_ci ht_info->ht_supported = true; 33778c2ecf20Sopenharmony_ci 33788c2ecf20Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SGI_20; 33798c2ecf20Sopenharmony_ci max_bit_rate = MAX_BIT_RATE_20_MHZ; 33808c2ecf20Sopenharmony_ci if (il->hw_params.ht40_channel & BIT(band)) { 33818c2ecf20Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; 33828c2ecf20Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SGI_40; 33838c2ecf20Sopenharmony_ci ht_info->mcs.rx_mask[4] = 0x01; 33848c2ecf20Sopenharmony_ci max_bit_rate = MAX_BIT_RATE_40_MHZ; 33858c2ecf20Sopenharmony_ci } 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci if (il->cfg->mod_params->amsdu_size_8K) 33888c2ecf20Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_ci ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; 33918c2ecf20Sopenharmony_ci ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_ci ht_info->mcs.rx_mask[0] = 0xFF; 33948c2ecf20Sopenharmony_ci if (rx_chains_num >= 2) 33958c2ecf20Sopenharmony_ci ht_info->mcs.rx_mask[1] = 0xFF; 33968c2ecf20Sopenharmony_ci if (rx_chains_num >= 3) 33978c2ecf20Sopenharmony_ci ht_info->mcs.rx_mask[2] = 0xFF; 33988c2ecf20Sopenharmony_ci 33998c2ecf20Sopenharmony_ci /* Highest supported Rx data rate */ 34008c2ecf20Sopenharmony_ci max_bit_rate *= rx_chains_num; 34018c2ecf20Sopenharmony_ci WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); 34028c2ecf20Sopenharmony_ci ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci /* Tx MCS capabilities */ 34058c2ecf20Sopenharmony_ci ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; 34068c2ecf20Sopenharmony_ci if (tx_chains_num != rx_chains_num) { 34078c2ecf20Sopenharmony_ci ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; 34088c2ecf20Sopenharmony_ci ht_info->mcs.tx_params |= 34098c2ecf20Sopenharmony_ci ((tx_chains_num - 34108c2ecf20Sopenharmony_ci 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); 34118c2ecf20Sopenharmony_ci } 34128c2ecf20Sopenharmony_ci} 34138c2ecf20Sopenharmony_ci 34148c2ecf20Sopenharmony_ci/* 34158c2ecf20Sopenharmony_ci * il_init_geos - Initialize mac80211's geo/channel info based from eeprom 34168c2ecf20Sopenharmony_ci */ 34178c2ecf20Sopenharmony_ciint 34188c2ecf20Sopenharmony_ciil_init_geos(struct il_priv *il) 34198c2ecf20Sopenharmony_ci{ 34208c2ecf20Sopenharmony_ci struct il_channel_info *ch; 34218c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 34228c2ecf20Sopenharmony_ci struct ieee80211_channel *channels; 34238c2ecf20Sopenharmony_ci struct ieee80211_channel *geo_ch; 34248c2ecf20Sopenharmony_ci struct ieee80211_rate *rates; 34258c2ecf20Sopenharmony_ci int i = 0; 34268c2ecf20Sopenharmony_ci s8 max_tx_power = 0; 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci if (il->bands[NL80211_BAND_2GHZ].n_bitrates || 34298c2ecf20Sopenharmony_ci il->bands[NL80211_BAND_5GHZ].n_bitrates) { 34308c2ecf20Sopenharmony_ci D_INFO("Geography modes already initialized.\n"); 34318c2ecf20Sopenharmony_ci set_bit(S_GEO_CONFIGURED, &il->status); 34328c2ecf20Sopenharmony_ci return 0; 34338c2ecf20Sopenharmony_ci } 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci channels = 34368c2ecf20Sopenharmony_ci kcalloc(il->channel_count, sizeof(struct ieee80211_channel), 34378c2ecf20Sopenharmony_ci GFP_KERNEL); 34388c2ecf20Sopenharmony_ci if (!channels) 34398c2ecf20Sopenharmony_ci return -ENOMEM; 34408c2ecf20Sopenharmony_ci 34418c2ecf20Sopenharmony_ci rates = 34428c2ecf20Sopenharmony_ci kzalloc((sizeof(struct ieee80211_rate) * RATE_COUNT_LEGACY), 34438c2ecf20Sopenharmony_ci GFP_KERNEL); 34448c2ecf20Sopenharmony_ci if (!rates) { 34458c2ecf20Sopenharmony_ci kfree(channels); 34468c2ecf20Sopenharmony_ci return -ENOMEM; 34478c2ecf20Sopenharmony_ci } 34488c2ecf20Sopenharmony_ci 34498c2ecf20Sopenharmony_ci /* 5.2GHz channels start after the 2.4GHz channels */ 34508c2ecf20Sopenharmony_ci sband = &il->bands[NL80211_BAND_5GHZ]; 34518c2ecf20Sopenharmony_ci sband->channels = &channels[ARRAY_SIZE(il_eeprom_band_1)]; 34528c2ecf20Sopenharmony_ci /* just OFDM */ 34538c2ecf20Sopenharmony_ci sband->bitrates = &rates[IL_FIRST_OFDM_RATE]; 34548c2ecf20Sopenharmony_ci sband->n_bitrates = RATE_COUNT_LEGACY - IL_FIRST_OFDM_RATE; 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_ci if (il->cfg->sku & IL_SKU_N) 34578c2ecf20Sopenharmony_ci il_init_ht_hw_capab(il, &sband->ht_cap, NL80211_BAND_5GHZ); 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci sband = &il->bands[NL80211_BAND_2GHZ]; 34608c2ecf20Sopenharmony_ci sband->channels = channels; 34618c2ecf20Sopenharmony_ci /* OFDM & CCK */ 34628c2ecf20Sopenharmony_ci sband->bitrates = rates; 34638c2ecf20Sopenharmony_ci sband->n_bitrates = RATE_COUNT_LEGACY; 34648c2ecf20Sopenharmony_ci 34658c2ecf20Sopenharmony_ci if (il->cfg->sku & IL_SKU_N) 34668c2ecf20Sopenharmony_ci il_init_ht_hw_capab(il, &sband->ht_cap, NL80211_BAND_2GHZ); 34678c2ecf20Sopenharmony_ci 34688c2ecf20Sopenharmony_ci il->ieee_channels = channels; 34698c2ecf20Sopenharmony_ci il->ieee_rates = rates; 34708c2ecf20Sopenharmony_ci 34718c2ecf20Sopenharmony_ci for (i = 0; i < il->channel_count; i++) { 34728c2ecf20Sopenharmony_ci ch = &il->channel_info[i]; 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci if (!il_is_channel_valid(ch)) 34758c2ecf20Sopenharmony_ci continue; 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_ci sband = &il->bands[ch->band]; 34788c2ecf20Sopenharmony_ci 34798c2ecf20Sopenharmony_ci geo_ch = &sband->channels[sband->n_channels++]; 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_ci geo_ch->center_freq = 34828c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(ch->channel, ch->band); 34838c2ecf20Sopenharmony_ci geo_ch->max_power = ch->max_power_avg; 34848c2ecf20Sopenharmony_ci geo_ch->max_antenna_gain = 0xff; 34858c2ecf20Sopenharmony_ci geo_ch->hw_value = ch->channel; 34868c2ecf20Sopenharmony_ci 34878c2ecf20Sopenharmony_ci if (il_is_channel_valid(ch)) { 34888c2ecf20Sopenharmony_ci if (!(ch->flags & EEPROM_CHANNEL_IBSS)) 34898c2ecf20Sopenharmony_ci geo_ch->flags |= IEEE80211_CHAN_NO_IR; 34908c2ecf20Sopenharmony_ci 34918c2ecf20Sopenharmony_ci if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) 34928c2ecf20Sopenharmony_ci geo_ch->flags |= IEEE80211_CHAN_NO_IR; 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_ci if (ch->flags & EEPROM_CHANNEL_RADAR) 34958c2ecf20Sopenharmony_ci geo_ch->flags |= IEEE80211_CHAN_RADAR; 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ci geo_ch->flags |= ch->ht40_extension_channel; 34988c2ecf20Sopenharmony_ci 34998c2ecf20Sopenharmony_ci if (ch->max_power_avg > max_tx_power) 35008c2ecf20Sopenharmony_ci max_tx_power = ch->max_power_avg; 35018c2ecf20Sopenharmony_ci } else { 35028c2ecf20Sopenharmony_ci geo_ch->flags |= IEEE80211_CHAN_DISABLED; 35038c2ecf20Sopenharmony_ci } 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_ci D_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, 35068c2ecf20Sopenharmony_ci geo_ch->center_freq, 35078c2ecf20Sopenharmony_ci il_is_channel_a_band(ch) ? "5.2" : "2.4", 35088c2ecf20Sopenharmony_ci geo_ch-> 35098c2ecf20Sopenharmony_ci flags & IEEE80211_CHAN_DISABLED ? "restricted" : "valid", 35108c2ecf20Sopenharmony_ci geo_ch->flags); 35118c2ecf20Sopenharmony_ci } 35128c2ecf20Sopenharmony_ci 35138c2ecf20Sopenharmony_ci il->tx_power_device_lmt = max_tx_power; 35148c2ecf20Sopenharmony_ci il->tx_power_user_lmt = max_tx_power; 35158c2ecf20Sopenharmony_ci il->tx_power_next = max_tx_power; 35168c2ecf20Sopenharmony_ci 35178c2ecf20Sopenharmony_ci if (il->bands[NL80211_BAND_5GHZ].n_channels == 0 && 35188c2ecf20Sopenharmony_ci (il->cfg->sku & IL_SKU_A)) { 35198c2ecf20Sopenharmony_ci IL_INFO("Incorrectly detected BG card as ABG. " 35208c2ecf20Sopenharmony_ci "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n", 35218c2ecf20Sopenharmony_ci il->pci_dev->device, il->pci_dev->subsystem_device); 35228c2ecf20Sopenharmony_ci il->cfg->sku &= ~IL_SKU_A; 35238c2ecf20Sopenharmony_ci } 35248c2ecf20Sopenharmony_ci 35258c2ecf20Sopenharmony_ci IL_INFO("Tunable channels: %d 802.11bg, %d 802.11a channels\n", 35268c2ecf20Sopenharmony_ci il->bands[NL80211_BAND_2GHZ].n_channels, 35278c2ecf20Sopenharmony_ci il->bands[NL80211_BAND_5GHZ].n_channels); 35288c2ecf20Sopenharmony_ci 35298c2ecf20Sopenharmony_ci set_bit(S_GEO_CONFIGURED, &il->status); 35308c2ecf20Sopenharmony_ci 35318c2ecf20Sopenharmony_ci return 0; 35328c2ecf20Sopenharmony_ci} 35338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_init_geos); 35348c2ecf20Sopenharmony_ci 35358c2ecf20Sopenharmony_ci/* 35368c2ecf20Sopenharmony_ci * il_free_geos - undo allocations in il_init_geos 35378c2ecf20Sopenharmony_ci */ 35388c2ecf20Sopenharmony_civoid 35398c2ecf20Sopenharmony_ciil_free_geos(struct il_priv *il) 35408c2ecf20Sopenharmony_ci{ 35418c2ecf20Sopenharmony_ci kfree(il->ieee_channels); 35428c2ecf20Sopenharmony_ci kfree(il->ieee_rates); 35438c2ecf20Sopenharmony_ci clear_bit(S_GEO_CONFIGURED, &il->status); 35448c2ecf20Sopenharmony_ci} 35458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_free_geos); 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_cistatic bool 35488c2ecf20Sopenharmony_ciil_is_channel_extension(struct il_priv *il, enum nl80211_band band, 35498c2ecf20Sopenharmony_ci u16 channel, u8 extension_chan_offset) 35508c2ecf20Sopenharmony_ci{ 35518c2ecf20Sopenharmony_ci const struct il_channel_info *ch_info; 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci ch_info = il_get_channel_info(il, band, channel); 35548c2ecf20Sopenharmony_ci if (!il_is_channel_valid(ch_info)) 35558c2ecf20Sopenharmony_ci return false; 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_ci if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) 35588c2ecf20Sopenharmony_ci return !(ch_info-> 35598c2ecf20Sopenharmony_ci ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS); 35608c2ecf20Sopenharmony_ci else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) 35618c2ecf20Sopenharmony_ci return !(ch_info-> 35628c2ecf20Sopenharmony_ci ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS); 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_ci return false; 35658c2ecf20Sopenharmony_ci} 35668c2ecf20Sopenharmony_ci 35678c2ecf20Sopenharmony_cibool 35688c2ecf20Sopenharmony_ciil_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap) 35698c2ecf20Sopenharmony_ci{ 35708c2ecf20Sopenharmony_ci if (!il->ht.enabled || !il->ht.is_40mhz) 35718c2ecf20Sopenharmony_ci return false; 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci /* 35748c2ecf20Sopenharmony_ci * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 35758c2ecf20Sopenharmony_ci * the bit will not set if it is pure 40MHz case 35768c2ecf20Sopenharmony_ci */ 35778c2ecf20Sopenharmony_ci if (ht_cap && !ht_cap->ht_supported) 35788c2ecf20Sopenharmony_ci return false; 35798c2ecf20Sopenharmony_ci 35808c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS 35818c2ecf20Sopenharmony_ci if (il->disable_ht40) 35828c2ecf20Sopenharmony_ci return false; 35838c2ecf20Sopenharmony_ci#endif 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci return il_is_channel_extension(il, il->band, 35868c2ecf20Sopenharmony_ci le16_to_cpu(il->staging.channel), 35878c2ecf20Sopenharmony_ci il->ht.extension_chan_offset); 35888c2ecf20Sopenharmony_ci} 35898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_is_ht40_tx_allowed); 35908c2ecf20Sopenharmony_ci 35918c2ecf20Sopenharmony_cistatic u16 noinline 35928c2ecf20Sopenharmony_ciil_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) 35938c2ecf20Sopenharmony_ci{ 35948c2ecf20Sopenharmony_ci u16 new_val; 35958c2ecf20Sopenharmony_ci u16 beacon_factor; 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci /* 35988c2ecf20Sopenharmony_ci * If mac80211 hasn't given us a beacon interval, program 35998c2ecf20Sopenharmony_ci * the default into the device. 36008c2ecf20Sopenharmony_ci */ 36018c2ecf20Sopenharmony_ci if (!beacon_val) 36028c2ecf20Sopenharmony_ci return DEFAULT_BEACON_INTERVAL; 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci /* 36058c2ecf20Sopenharmony_ci * If the beacon interval we obtained from the peer 36068c2ecf20Sopenharmony_ci * is too large, we'll have to wake up more often 36078c2ecf20Sopenharmony_ci * (and in IBSS case, we'll beacon too much) 36088c2ecf20Sopenharmony_ci * 36098c2ecf20Sopenharmony_ci * For example, if max_beacon_val is 4096, and the 36108c2ecf20Sopenharmony_ci * requested beacon interval is 7000, we'll have to 36118c2ecf20Sopenharmony_ci * use 3500 to be able to wake up on the beacons. 36128c2ecf20Sopenharmony_ci * 36138c2ecf20Sopenharmony_ci * This could badly influence beacon detection stats. 36148c2ecf20Sopenharmony_ci */ 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; 36178c2ecf20Sopenharmony_ci new_val = beacon_val / beacon_factor; 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci if (!new_val) 36208c2ecf20Sopenharmony_ci new_val = max_beacon_val; 36218c2ecf20Sopenharmony_ci 36228c2ecf20Sopenharmony_ci return new_val; 36238c2ecf20Sopenharmony_ci} 36248c2ecf20Sopenharmony_ci 36258c2ecf20Sopenharmony_ciint 36268c2ecf20Sopenharmony_ciil_send_rxon_timing(struct il_priv *il) 36278c2ecf20Sopenharmony_ci{ 36288c2ecf20Sopenharmony_ci u64 tsf; 36298c2ecf20Sopenharmony_ci s32 interval_tm, rem; 36308c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = NULL; 36318c2ecf20Sopenharmony_ci u16 beacon_int; 36328c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = il->vif; 36338c2ecf20Sopenharmony_ci 36348c2ecf20Sopenharmony_ci conf = &il->hw->conf; 36358c2ecf20Sopenharmony_ci 36368c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci memset(&il->timing, 0, sizeof(struct il_rxon_time_cmd)); 36398c2ecf20Sopenharmony_ci 36408c2ecf20Sopenharmony_ci il->timing.timestamp = cpu_to_le64(il->timestamp); 36418c2ecf20Sopenharmony_ci il->timing.listen_interval = cpu_to_le16(conf->listen_interval); 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_ci beacon_int = vif ? vif->bss_conf.beacon_int : 0; 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_ci /* 36468c2ecf20Sopenharmony_ci * TODO: For IBSS we need to get atim_win from mac80211, 36478c2ecf20Sopenharmony_ci * for now just always use 0 36488c2ecf20Sopenharmony_ci */ 36498c2ecf20Sopenharmony_ci il->timing.atim_win = 0; 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_ci beacon_int = 36528c2ecf20Sopenharmony_ci il_adjust_beacon_interval(beacon_int, 36538c2ecf20Sopenharmony_ci il->hw_params.max_beacon_itrvl * 36548c2ecf20Sopenharmony_ci TIME_UNIT); 36558c2ecf20Sopenharmony_ci il->timing.beacon_interval = cpu_to_le16(beacon_int); 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_ci tsf = il->timestamp; /* tsf is modifed by do_div: copy it */ 36588c2ecf20Sopenharmony_ci interval_tm = beacon_int * TIME_UNIT; 36598c2ecf20Sopenharmony_ci rem = do_div(tsf, interval_tm); 36608c2ecf20Sopenharmony_ci il->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); 36618c2ecf20Sopenharmony_ci 36628c2ecf20Sopenharmony_ci il->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ? : 1) : 1; 36638c2ecf20Sopenharmony_ci 36648c2ecf20Sopenharmony_ci D_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n", 36658c2ecf20Sopenharmony_ci le16_to_cpu(il->timing.beacon_interval), 36668c2ecf20Sopenharmony_ci le32_to_cpu(il->timing.beacon_init_val), 36678c2ecf20Sopenharmony_ci le16_to_cpu(il->timing.atim_win)); 36688c2ecf20Sopenharmony_ci 36698c2ecf20Sopenharmony_ci return il_send_cmd_pdu(il, C_RXON_TIMING, sizeof(il->timing), 36708c2ecf20Sopenharmony_ci &il->timing); 36718c2ecf20Sopenharmony_ci} 36728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_rxon_timing); 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_civoid 36758c2ecf20Sopenharmony_ciil_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt) 36768c2ecf20Sopenharmony_ci{ 36778c2ecf20Sopenharmony_ci struct il_rxon_cmd *rxon = &il->staging; 36788c2ecf20Sopenharmony_ci 36798c2ecf20Sopenharmony_ci if (hw_decrypt) 36808c2ecf20Sopenharmony_ci rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; 36818c2ecf20Sopenharmony_ci else 36828c2ecf20Sopenharmony_ci rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; 36838c2ecf20Sopenharmony_ci 36848c2ecf20Sopenharmony_ci} 36858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_hwcrypto); 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_ci/* validate RXON structure is valid */ 36888c2ecf20Sopenharmony_ciint 36898c2ecf20Sopenharmony_ciil_check_rxon_cmd(struct il_priv *il) 36908c2ecf20Sopenharmony_ci{ 36918c2ecf20Sopenharmony_ci struct il_rxon_cmd *rxon = &il->staging; 36928c2ecf20Sopenharmony_ci bool error = false; 36938c2ecf20Sopenharmony_ci 36948c2ecf20Sopenharmony_ci if (rxon->flags & RXON_FLG_BAND_24G_MSK) { 36958c2ecf20Sopenharmony_ci if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { 36968c2ecf20Sopenharmony_ci IL_WARN("check 2.4G: wrong narrow\n"); 36978c2ecf20Sopenharmony_ci error = true; 36988c2ecf20Sopenharmony_ci } 36998c2ecf20Sopenharmony_ci if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { 37008c2ecf20Sopenharmony_ci IL_WARN("check 2.4G: wrong radar\n"); 37018c2ecf20Sopenharmony_ci error = true; 37028c2ecf20Sopenharmony_ci } 37038c2ecf20Sopenharmony_ci } else { 37048c2ecf20Sopenharmony_ci if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { 37058c2ecf20Sopenharmony_ci IL_WARN("check 5.2G: not short slot!\n"); 37068c2ecf20Sopenharmony_ci error = true; 37078c2ecf20Sopenharmony_ci } 37088c2ecf20Sopenharmony_ci if (rxon->flags & RXON_FLG_CCK_MSK) { 37098c2ecf20Sopenharmony_ci IL_WARN("check 5.2G: CCK!\n"); 37108c2ecf20Sopenharmony_ci error = true; 37118c2ecf20Sopenharmony_ci } 37128c2ecf20Sopenharmony_ci } 37138c2ecf20Sopenharmony_ci if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { 37148c2ecf20Sopenharmony_ci IL_WARN("mac/bssid mcast!\n"); 37158c2ecf20Sopenharmony_ci error = true; 37168c2ecf20Sopenharmony_ci } 37178c2ecf20Sopenharmony_ci 37188c2ecf20Sopenharmony_ci /* make sure basic rates 6Mbps and 1Mbps are supported */ 37198c2ecf20Sopenharmony_ci if ((rxon->ofdm_basic_rates & RATE_6M_MASK) == 0 && 37208c2ecf20Sopenharmony_ci (rxon->cck_basic_rates & RATE_1M_MASK) == 0) { 37218c2ecf20Sopenharmony_ci IL_WARN("neither 1 nor 6 are basic\n"); 37228c2ecf20Sopenharmony_ci error = true; 37238c2ecf20Sopenharmony_ci } 37248c2ecf20Sopenharmony_ci 37258c2ecf20Sopenharmony_ci if (le16_to_cpu(rxon->assoc_id) > 2007) { 37268c2ecf20Sopenharmony_ci IL_WARN("aid > 2007\n"); 37278c2ecf20Sopenharmony_ci error = true; 37288c2ecf20Sopenharmony_ci } 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) == 37318c2ecf20Sopenharmony_ci (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { 37328c2ecf20Sopenharmony_ci IL_WARN("CCK and short slot\n"); 37338c2ecf20Sopenharmony_ci error = true; 37348c2ecf20Sopenharmony_ci } 37358c2ecf20Sopenharmony_ci 37368c2ecf20Sopenharmony_ci if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) == 37378c2ecf20Sopenharmony_ci (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { 37388c2ecf20Sopenharmony_ci IL_WARN("CCK and auto detect"); 37398c2ecf20Sopenharmony_ci error = true; 37408c2ecf20Sopenharmony_ci } 37418c2ecf20Sopenharmony_ci 37428c2ecf20Sopenharmony_ci if ((rxon-> 37438c2ecf20Sopenharmony_ci flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) == 37448c2ecf20Sopenharmony_ci RXON_FLG_TGG_PROTECT_MSK) { 37458c2ecf20Sopenharmony_ci IL_WARN("TGg but no auto-detect\n"); 37468c2ecf20Sopenharmony_ci error = true; 37478c2ecf20Sopenharmony_ci } 37488c2ecf20Sopenharmony_ci 37498c2ecf20Sopenharmony_ci if (error) 37508c2ecf20Sopenharmony_ci IL_WARN("Tuning to channel %d\n", le16_to_cpu(rxon->channel)); 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci if (error) { 37538c2ecf20Sopenharmony_ci IL_ERR("Invalid RXON\n"); 37548c2ecf20Sopenharmony_ci return -EINVAL; 37558c2ecf20Sopenharmony_ci } 37568c2ecf20Sopenharmony_ci return 0; 37578c2ecf20Sopenharmony_ci} 37588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_check_rxon_cmd); 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_ci/* 37618c2ecf20Sopenharmony_ci * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed 37628c2ecf20Sopenharmony_ci * @il: staging_rxon is compared to active_rxon 37638c2ecf20Sopenharmony_ci * 37648c2ecf20Sopenharmony_ci * If the RXON structure is changing enough to require a new tune, 37658c2ecf20Sopenharmony_ci * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that 37668c2ecf20Sopenharmony_ci * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. 37678c2ecf20Sopenharmony_ci */ 37688c2ecf20Sopenharmony_ciint 37698c2ecf20Sopenharmony_ciil_full_rxon_required(struct il_priv *il) 37708c2ecf20Sopenharmony_ci{ 37718c2ecf20Sopenharmony_ci const struct il_rxon_cmd *staging = &il->staging; 37728c2ecf20Sopenharmony_ci const struct il_rxon_cmd *active = &il->active; 37738c2ecf20Sopenharmony_ci 37748c2ecf20Sopenharmony_ci#define CHK(cond) \ 37758c2ecf20Sopenharmony_ci if ((cond)) { \ 37768c2ecf20Sopenharmony_ci D_INFO("need full RXON - " #cond "\n"); \ 37778c2ecf20Sopenharmony_ci return 1; \ 37788c2ecf20Sopenharmony_ci } 37798c2ecf20Sopenharmony_ci 37808c2ecf20Sopenharmony_ci#define CHK_NEQ(c1, c2) \ 37818c2ecf20Sopenharmony_ci if ((c1) != (c2)) { \ 37828c2ecf20Sopenharmony_ci D_INFO("need full RXON - " \ 37838c2ecf20Sopenharmony_ci #c1 " != " #c2 " - %d != %d\n", \ 37848c2ecf20Sopenharmony_ci (c1), (c2)); \ 37858c2ecf20Sopenharmony_ci return 1; \ 37868c2ecf20Sopenharmony_ci } 37878c2ecf20Sopenharmony_ci 37888c2ecf20Sopenharmony_ci /* These items are only settable from the full RXON command */ 37898c2ecf20Sopenharmony_ci CHK(!il_is_associated(il)); 37908c2ecf20Sopenharmony_ci CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr)); 37918c2ecf20Sopenharmony_ci CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr)); 37928c2ecf20Sopenharmony_ci CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr, 37938c2ecf20Sopenharmony_ci active->wlap_bssid_addr)); 37948c2ecf20Sopenharmony_ci CHK_NEQ(staging->dev_type, active->dev_type); 37958c2ecf20Sopenharmony_ci CHK_NEQ(staging->channel, active->channel); 37968c2ecf20Sopenharmony_ci CHK_NEQ(staging->air_propagation, active->air_propagation); 37978c2ecf20Sopenharmony_ci CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, 37988c2ecf20Sopenharmony_ci active->ofdm_ht_single_stream_basic_rates); 37998c2ecf20Sopenharmony_ci CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, 38008c2ecf20Sopenharmony_ci active->ofdm_ht_dual_stream_basic_rates); 38018c2ecf20Sopenharmony_ci CHK_NEQ(staging->assoc_id, active->assoc_id); 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_ci /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can 38048c2ecf20Sopenharmony_ci * be updated with the RXON_ASSOC command -- however only some 38058c2ecf20Sopenharmony_ci * flag transitions are allowed using RXON_ASSOC */ 38068c2ecf20Sopenharmony_ci 38078c2ecf20Sopenharmony_ci /* Check if we are not switching bands */ 38088c2ecf20Sopenharmony_ci CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, 38098c2ecf20Sopenharmony_ci active->flags & RXON_FLG_BAND_24G_MSK); 38108c2ecf20Sopenharmony_ci 38118c2ecf20Sopenharmony_ci /* Check if we are switching association toggle */ 38128c2ecf20Sopenharmony_ci CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, 38138c2ecf20Sopenharmony_ci active->filter_flags & RXON_FILTER_ASSOC_MSK); 38148c2ecf20Sopenharmony_ci 38158c2ecf20Sopenharmony_ci#undef CHK 38168c2ecf20Sopenharmony_ci#undef CHK_NEQ 38178c2ecf20Sopenharmony_ci 38188c2ecf20Sopenharmony_ci return 0; 38198c2ecf20Sopenharmony_ci} 38208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_full_rxon_required); 38218c2ecf20Sopenharmony_ci 38228c2ecf20Sopenharmony_ciu8 38238c2ecf20Sopenharmony_ciil_get_lowest_plcp(struct il_priv *il) 38248c2ecf20Sopenharmony_ci{ 38258c2ecf20Sopenharmony_ci /* 38268c2ecf20Sopenharmony_ci * Assign the lowest rate -- should really get this from 38278c2ecf20Sopenharmony_ci * the beacon skb from mac80211. 38288c2ecf20Sopenharmony_ci */ 38298c2ecf20Sopenharmony_ci if (il->staging.flags & RXON_FLG_BAND_24G_MSK) 38308c2ecf20Sopenharmony_ci return RATE_1M_PLCP; 38318c2ecf20Sopenharmony_ci else 38328c2ecf20Sopenharmony_ci return RATE_6M_PLCP; 38338c2ecf20Sopenharmony_ci} 38348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_lowest_plcp); 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_cistatic void 38378c2ecf20Sopenharmony_ci_il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) 38388c2ecf20Sopenharmony_ci{ 38398c2ecf20Sopenharmony_ci struct il_rxon_cmd *rxon = &il->staging; 38408c2ecf20Sopenharmony_ci 38418c2ecf20Sopenharmony_ci if (!il->ht.enabled) { 38428c2ecf20Sopenharmony_ci rxon->flags &= 38438c2ecf20Sopenharmony_ci ~(RXON_FLG_CHANNEL_MODE_MSK | 38448c2ecf20Sopenharmony_ci RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK 38458c2ecf20Sopenharmony_ci | RXON_FLG_HT_PROT_MSK); 38468c2ecf20Sopenharmony_ci return; 38478c2ecf20Sopenharmony_ci } 38488c2ecf20Sopenharmony_ci 38498c2ecf20Sopenharmony_ci rxon->flags |= 38508c2ecf20Sopenharmony_ci cpu_to_le32(il->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS); 38518c2ecf20Sopenharmony_ci 38528c2ecf20Sopenharmony_ci /* Set up channel bandwidth: 38538c2ecf20Sopenharmony_ci * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ 38548c2ecf20Sopenharmony_ci /* clear the HT channel mode before set the mode */ 38558c2ecf20Sopenharmony_ci rxon->flags &= 38568c2ecf20Sopenharmony_ci ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); 38578c2ecf20Sopenharmony_ci if (il_is_ht40_tx_allowed(il, NULL)) { 38588c2ecf20Sopenharmony_ci /* pure ht40 */ 38598c2ecf20Sopenharmony_ci if (il->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { 38608c2ecf20Sopenharmony_ci rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; 38618c2ecf20Sopenharmony_ci /* Note: control channel is opposite of extension channel */ 38628c2ecf20Sopenharmony_ci switch (il->ht.extension_chan_offset) { 38638c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 38648c2ecf20Sopenharmony_ci rxon->flags &= 38658c2ecf20Sopenharmony_ci ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; 38668c2ecf20Sopenharmony_ci break; 38678c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 38688c2ecf20Sopenharmony_ci rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; 38698c2ecf20Sopenharmony_ci break; 38708c2ecf20Sopenharmony_ci } 38718c2ecf20Sopenharmony_ci } else { 38728c2ecf20Sopenharmony_ci /* Note: control channel is opposite of extension channel */ 38738c2ecf20Sopenharmony_ci switch (il->ht.extension_chan_offset) { 38748c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 38758c2ecf20Sopenharmony_ci rxon->flags &= 38768c2ecf20Sopenharmony_ci ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); 38778c2ecf20Sopenharmony_ci rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; 38788c2ecf20Sopenharmony_ci break; 38798c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 38808c2ecf20Sopenharmony_ci rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; 38818c2ecf20Sopenharmony_ci rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; 38828c2ecf20Sopenharmony_ci break; 38838c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_NONE: 38848c2ecf20Sopenharmony_ci default: 38858c2ecf20Sopenharmony_ci /* channel location only valid if in Mixed mode */ 38868c2ecf20Sopenharmony_ci IL_ERR("invalid extension channel offset\n"); 38878c2ecf20Sopenharmony_ci break; 38888c2ecf20Sopenharmony_ci } 38898c2ecf20Sopenharmony_ci } 38908c2ecf20Sopenharmony_ci } else { 38918c2ecf20Sopenharmony_ci rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; 38928c2ecf20Sopenharmony_ci } 38938c2ecf20Sopenharmony_ci 38948c2ecf20Sopenharmony_ci if (il->ops->set_rxon_chain) 38958c2ecf20Sopenharmony_ci il->ops->set_rxon_chain(il); 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci D_ASSOC("rxon flags 0x%X operation mode :0x%X " 38988c2ecf20Sopenharmony_ci "extension channel offset 0x%x\n", le32_to_cpu(rxon->flags), 38998c2ecf20Sopenharmony_ci il->ht.protection, il->ht.extension_chan_offset); 39008c2ecf20Sopenharmony_ci} 39018c2ecf20Sopenharmony_ci 39028c2ecf20Sopenharmony_civoid 39038c2ecf20Sopenharmony_ciil_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) 39048c2ecf20Sopenharmony_ci{ 39058c2ecf20Sopenharmony_ci _il_set_rxon_ht(il, ht_conf); 39068c2ecf20Sopenharmony_ci} 39078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_ht); 39088c2ecf20Sopenharmony_ci 39098c2ecf20Sopenharmony_ci/* Return valid, unused, channel for a passive scan to reset the RF */ 39108c2ecf20Sopenharmony_ciu8 39118c2ecf20Sopenharmony_ciil_get_single_channel_number(struct il_priv *il, enum nl80211_band band) 39128c2ecf20Sopenharmony_ci{ 39138c2ecf20Sopenharmony_ci const struct il_channel_info *ch_info; 39148c2ecf20Sopenharmony_ci int i; 39158c2ecf20Sopenharmony_ci u8 channel = 0; 39168c2ecf20Sopenharmony_ci u8 min, max; 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ) { 39198c2ecf20Sopenharmony_ci min = 14; 39208c2ecf20Sopenharmony_ci max = il->channel_count; 39218c2ecf20Sopenharmony_ci } else { 39228c2ecf20Sopenharmony_ci min = 0; 39238c2ecf20Sopenharmony_ci max = 14; 39248c2ecf20Sopenharmony_ci } 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_ci for (i = min; i < max; i++) { 39278c2ecf20Sopenharmony_ci channel = il->channel_info[i].channel; 39288c2ecf20Sopenharmony_ci if (channel == le16_to_cpu(il->staging.channel)) 39298c2ecf20Sopenharmony_ci continue; 39308c2ecf20Sopenharmony_ci 39318c2ecf20Sopenharmony_ci ch_info = il_get_channel_info(il, band, channel); 39328c2ecf20Sopenharmony_ci if (il_is_channel_valid(ch_info)) 39338c2ecf20Sopenharmony_ci break; 39348c2ecf20Sopenharmony_ci } 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci return channel; 39378c2ecf20Sopenharmony_ci} 39388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_single_channel_number); 39398c2ecf20Sopenharmony_ci 39408c2ecf20Sopenharmony_ci/* 39418c2ecf20Sopenharmony_ci * il_set_rxon_channel - Set the band and channel values in staging RXON 39428c2ecf20Sopenharmony_ci * @ch: requested channel as a pointer to struct ieee80211_channel 39438c2ecf20Sopenharmony_ci 39448c2ecf20Sopenharmony_ci * NOTE: Does not commit to the hardware; it sets appropriate bit fields 39458c2ecf20Sopenharmony_ci * in the staging RXON flag structure based on the ch->band 39468c2ecf20Sopenharmony_ci */ 39478c2ecf20Sopenharmony_ciint 39488c2ecf20Sopenharmony_ciil_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch) 39498c2ecf20Sopenharmony_ci{ 39508c2ecf20Sopenharmony_ci enum nl80211_band band = ch->band; 39518c2ecf20Sopenharmony_ci u16 channel = ch->hw_value; 39528c2ecf20Sopenharmony_ci 39538c2ecf20Sopenharmony_ci if (le16_to_cpu(il->staging.channel) == channel && il->band == band) 39548c2ecf20Sopenharmony_ci return 0; 39558c2ecf20Sopenharmony_ci 39568c2ecf20Sopenharmony_ci il->staging.channel = cpu_to_le16(channel); 39578c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 39588c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_BAND_24G_MSK; 39598c2ecf20Sopenharmony_ci else 39608c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_BAND_24G_MSK; 39618c2ecf20Sopenharmony_ci 39628c2ecf20Sopenharmony_ci il->band = band; 39638c2ecf20Sopenharmony_ci 39648c2ecf20Sopenharmony_ci D_INFO("Staging channel set to %d [%d]\n", channel, band); 39658c2ecf20Sopenharmony_ci 39668c2ecf20Sopenharmony_ci return 0; 39678c2ecf20Sopenharmony_ci} 39688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_channel); 39698c2ecf20Sopenharmony_ci 39708c2ecf20Sopenharmony_civoid 39718c2ecf20Sopenharmony_ciil_set_flags_for_band(struct il_priv *il, enum nl80211_band band, 39728c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 39738c2ecf20Sopenharmony_ci{ 39748c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ) { 39758c2ecf20Sopenharmony_ci il->staging.flags &= 39768c2ecf20Sopenharmony_ci ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | 39778c2ecf20Sopenharmony_ci RXON_FLG_CCK_MSK); 39788c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 39798c2ecf20Sopenharmony_ci } else { 39808c2ecf20Sopenharmony_ci /* Copied from il_post_associate() */ 39818c2ecf20Sopenharmony_ci if (vif && vif->bss_conf.use_short_slot) 39828c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 39838c2ecf20Sopenharmony_ci else 39848c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; 39858c2ecf20Sopenharmony_ci 39868c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_BAND_24G_MSK; 39878c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; 39888c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_CCK_MSK; 39898c2ecf20Sopenharmony_ci } 39908c2ecf20Sopenharmony_ci} 39918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_flags_for_band); 39928c2ecf20Sopenharmony_ci 39938c2ecf20Sopenharmony_ci/* 39948c2ecf20Sopenharmony_ci * initialize rxon structure with default values from eeprom 39958c2ecf20Sopenharmony_ci */ 39968c2ecf20Sopenharmony_civoid 39978c2ecf20Sopenharmony_ciil_connection_init_rx_config(struct il_priv *il) 39988c2ecf20Sopenharmony_ci{ 39998c2ecf20Sopenharmony_ci const struct il_channel_info *ch_info; 40008c2ecf20Sopenharmony_ci 40018c2ecf20Sopenharmony_ci memset(&il->staging, 0, sizeof(il->staging)); 40028c2ecf20Sopenharmony_ci 40038c2ecf20Sopenharmony_ci switch (il->iw_mode) { 40048c2ecf20Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 40058c2ecf20Sopenharmony_ci il->staging.dev_type = RXON_DEV_TYPE_ESS; 40068c2ecf20Sopenharmony_ci break; 40078c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 40088c2ecf20Sopenharmony_ci il->staging.dev_type = RXON_DEV_TYPE_ESS; 40098c2ecf20Sopenharmony_ci il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; 40108c2ecf20Sopenharmony_ci break; 40118c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 40128c2ecf20Sopenharmony_ci il->staging.dev_type = RXON_DEV_TYPE_IBSS; 40138c2ecf20Sopenharmony_ci il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; 40148c2ecf20Sopenharmony_ci il->staging.filter_flags = 40158c2ecf20Sopenharmony_ci RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; 40168c2ecf20Sopenharmony_ci break; 40178c2ecf20Sopenharmony_ci default: 40188c2ecf20Sopenharmony_ci IL_ERR("Unsupported interface type %d\n", il->vif->type); 40198c2ecf20Sopenharmony_ci return; 40208c2ecf20Sopenharmony_ci } 40218c2ecf20Sopenharmony_ci 40228c2ecf20Sopenharmony_ci#if 0 40238c2ecf20Sopenharmony_ci /* TODO: Figure out when short_preamble would be set and cache from 40248c2ecf20Sopenharmony_ci * that */ 40258c2ecf20Sopenharmony_ci if (!hw_to_local(il->hw)->short_preamble) 40268c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; 40278c2ecf20Sopenharmony_ci else 40288c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; 40298c2ecf20Sopenharmony_ci#endif 40308c2ecf20Sopenharmony_ci 40318c2ecf20Sopenharmony_ci ch_info = 40328c2ecf20Sopenharmony_ci il_get_channel_info(il, il->band, le16_to_cpu(il->active.channel)); 40338c2ecf20Sopenharmony_ci 40348c2ecf20Sopenharmony_ci if (!ch_info) 40358c2ecf20Sopenharmony_ci ch_info = &il->channel_info[0]; 40368c2ecf20Sopenharmony_ci 40378c2ecf20Sopenharmony_ci il->staging.channel = cpu_to_le16(ch_info->channel); 40388c2ecf20Sopenharmony_ci il->band = ch_info->band; 40398c2ecf20Sopenharmony_ci 40408c2ecf20Sopenharmony_ci il_set_flags_for_band(il, il->band, il->vif); 40418c2ecf20Sopenharmony_ci 40428c2ecf20Sopenharmony_ci il->staging.ofdm_basic_rates = 40438c2ecf20Sopenharmony_ci (IL_OFDM_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; 40448c2ecf20Sopenharmony_ci il->staging.cck_basic_rates = 40458c2ecf20Sopenharmony_ci (IL_CCK_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; 40468c2ecf20Sopenharmony_ci 40478c2ecf20Sopenharmony_ci /* clear both MIX and PURE40 mode flag */ 40488c2ecf20Sopenharmony_ci il->staging.flags &= 40498c2ecf20Sopenharmony_ci ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40); 40508c2ecf20Sopenharmony_ci if (il->vif) 40518c2ecf20Sopenharmony_ci memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN); 40528c2ecf20Sopenharmony_ci 40538c2ecf20Sopenharmony_ci il->staging.ofdm_ht_single_stream_basic_rates = 0xff; 40548c2ecf20Sopenharmony_ci il->staging.ofdm_ht_dual_stream_basic_rates = 0xff; 40558c2ecf20Sopenharmony_ci} 40568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_connection_init_rx_config); 40578c2ecf20Sopenharmony_ci 40588c2ecf20Sopenharmony_civoid 40598c2ecf20Sopenharmony_ciil_set_rate(struct il_priv *il) 40608c2ecf20Sopenharmony_ci{ 40618c2ecf20Sopenharmony_ci const struct ieee80211_supported_band *hw = NULL; 40628c2ecf20Sopenharmony_ci struct ieee80211_rate *rate; 40638c2ecf20Sopenharmony_ci int i; 40648c2ecf20Sopenharmony_ci 40658c2ecf20Sopenharmony_ci hw = il_get_hw_mode(il, il->band); 40668c2ecf20Sopenharmony_ci if (!hw) { 40678c2ecf20Sopenharmony_ci IL_ERR("Failed to set rate: unable to get hw mode\n"); 40688c2ecf20Sopenharmony_ci return; 40698c2ecf20Sopenharmony_ci } 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci il->active_rate = 0; 40728c2ecf20Sopenharmony_ci 40738c2ecf20Sopenharmony_ci for (i = 0; i < hw->n_bitrates; i++) { 40748c2ecf20Sopenharmony_ci rate = &(hw->bitrates[i]); 40758c2ecf20Sopenharmony_ci if (rate->hw_value < RATE_COUNT_LEGACY) 40768c2ecf20Sopenharmony_ci il->active_rate |= (1 << rate->hw_value); 40778c2ecf20Sopenharmony_ci } 40788c2ecf20Sopenharmony_ci 40798c2ecf20Sopenharmony_ci D_RATE("Set active_rate = %0x\n", il->active_rate); 40808c2ecf20Sopenharmony_ci 40818c2ecf20Sopenharmony_ci il->staging.cck_basic_rates = 40828c2ecf20Sopenharmony_ci (IL_CCK_BASIC_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; 40838c2ecf20Sopenharmony_ci 40848c2ecf20Sopenharmony_ci il->staging.ofdm_basic_rates = 40858c2ecf20Sopenharmony_ci (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; 40868c2ecf20Sopenharmony_ci} 40878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_rate); 40888c2ecf20Sopenharmony_ci 40898c2ecf20Sopenharmony_civoid 40908c2ecf20Sopenharmony_ciil_chswitch_done(struct il_priv *il, bool is_success) 40918c2ecf20Sopenharmony_ci{ 40928c2ecf20Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 40938c2ecf20Sopenharmony_ci return; 40948c2ecf20Sopenharmony_ci 40958c2ecf20Sopenharmony_ci if (test_and_clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) 40968c2ecf20Sopenharmony_ci ieee80211_chswitch_done(il->vif, is_success); 40978c2ecf20Sopenharmony_ci} 40988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_chswitch_done); 40998c2ecf20Sopenharmony_ci 41008c2ecf20Sopenharmony_civoid 41018c2ecf20Sopenharmony_ciil_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb) 41028c2ecf20Sopenharmony_ci{ 41038c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 41048c2ecf20Sopenharmony_ci struct il_csa_notification *csa = &(pkt->u.csa_notif); 41058c2ecf20Sopenharmony_ci struct il_rxon_cmd *rxon = (void *)&il->active; 41068c2ecf20Sopenharmony_ci 41078c2ecf20Sopenharmony_ci if (!test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) 41088c2ecf20Sopenharmony_ci return; 41098c2ecf20Sopenharmony_ci 41108c2ecf20Sopenharmony_ci if (!le32_to_cpu(csa->status) && csa->channel == il->switch_channel) { 41118c2ecf20Sopenharmony_ci rxon->channel = csa->channel; 41128c2ecf20Sopenharmony_ci il->staging.channel = csa->channel; 41138c2ecf20Sopenharmony_ci D_11H("CSA notif: channel %d\n", le16_to_cpu(csa->channel)); 41148c2ecf20Sopenharmony_ci il_chswitch_done(il, true); 41158c2ecf20Sopenharmony_ci } else { 41168c2ecf20Sopenharmony_ci IL_ERR("CSA notif (fail) : channel %d\n", 41178c2ecf20Sopenharmony_ci le16_to_cpu(csa->channel)); 41188c2ecf20Sopenharmony_ci il_chswitch_done(il, false); 41198c2ecf20Sopenharmony_ci } 41208c2ecf20Sopenharmony_ci} 41218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_csa); 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 41248c2ecf20Sopenharmony_civoid 41258c2ecf20Sopenharmony_ciil_print_rx_config_cmd(struct il_priv *il) 41268c2ecf20Sopenharmony_ci{ 41278c2ecf20Sopenharmony_ci struct il_rxon_cmd *rxon = &il->staging; 41288c2ecf20Sopenharmony_ci 41298c2ecf20Sopenharmony_ci D_RADIO("RX CONFIG:\n"); 41308c2ecf20Sopenharmony_ci il_print_hex_dump(il, IL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); 41318c2ecf20Sopenharmony_ci D_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel)); 41328c2ecf20Sopenharmony_ci D_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); 41338c2ecf20Sopenharmony_ci D_RADIO("u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags)); 41348c2ecf20Sopenharmony_ci D_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type); 41358c2ecf20Sopenharmony_ci D_RADIO("u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates); 41368c2ecf20Sopenharmony_ci D_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates); 41378c2ecf20Sopenharmony_ci D_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr); 41388c2ecf20Sopenharmony_ci D_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr); 41398c2ecf20Sopenharmony_ci D_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); 41408c2ecf20Sopenharmony_ci} 41418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_print_rx_config_cmd); 41428c2ecf20Sopenharmony_ci#endif 41438c2ecf20Sopenharmony_ci/* 41448c2ecf20Sopenharmony_ci * il_irq_handle_error - called for HW or SW error interrupt from card 41458c2ecf20Sopenharmony_ci */ 41468c2ecf20Sopenharmony_civoid 41478c2ecf20Sopenharmony_ciil_irq_handle_error(struct il_priv *il) 41488c2ecf20Sopenharmony_ci{ 41498c2ecf20Sopenharmony_ci /* Set the FW error flag -- cleared on il_down */ 41508c2ecf20Sopenharmony_ci set_bit(S_FW_ERROR, &il->status); 41518c2ecf20Sopenharmony_ci 41528c2ecf20Sopenharmony_ci /* Cancel currently queued command. */ 41538c2ecf20Sopenharmony_ci clear_bit(S_HCMD_ACTIVE, &il->status); 41548c2ecf20Sopenharmony_ci 41558c2ecf20Sopenharmony_ci IL_ERR("Loaded firmware version: %s\n", il->hw->wiphy->fw_version); 41568c2ecf20Sopenharmony_ci 41578c2ecf20Sopenharmony_ci il->ops->dump_nic_error_log(il); 41588c2ecf20Sopenharmony_ci if (il->ops->dump_fh) 41598c2ecf20Sopenharmony_ci il->ops->dump_fh(il, NULL, false); 41608c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 41618c2ecf20Sopenharmony_ci if (il_get_debug_level(il) & IL_DL_FW_ERRORS) 41628c2ecf20Sopenharmony_ci il_print_rx_config_cmd(il); 41638c2ecf20Sopenharmony_ci#endif 41648c2ecf20Sopenharmony_ci 41658c2ecf20Sopenharmony_ci wake_up(&il->wait_command_queue); 41668c2ecf20Sopenharmony_ci 41678c2ecf20Sopenharmony_ci /* Keep the restart process from trying to send host 41688c2ecf20Sopenharmony_ci * commands by clearing the INIT status bit */ 41698c2ecf20Sopenharmony_ci clear_bit(S_READY, &il->status); 41708c2ecf20Sopenharmony_ci 41718c2ecf20Sopenharmony_ci if (!test_bit(S_EXIT_PENDING, &il->status)) { 41728c2ecf20Sopenharmony_ci IL_DBG(IL_DL_FW_ERRORS, 41738c2ecf20Sopenharmony_ci "Restarting adapter due to uCode error.\n"); 41748c2ecf20Sopenharmony_ci 41758c2ecf20Sopenharmony_ci if (il->cfg->mod_params->restart_fw) 41768c2ecf20Sopenharmony_ci queue_work(il->workqueue, &il->restart); 41778c2ecf20Sopenharmony_ci } 41788c2ecf20Sopenharmony_ci} 41798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_irq_handle_error); 41808c2ecf20Sopenharmony_ci 41818c2ecf20Sopenharmony_cistatic int 41828c2ecf20Sopenharmony_ci_il_apm_stop_master(struct il_priv *il) 41838c2ecf20Sopenharmony_ci{ 41848c2ecf20Sopenharmony_ci int ret = 0; 41858c2ecf20Sopenharmony_ci 41868c2ecf20Sopenharmony_ci /* stop device's busmaster DMA activity */ 41878c2ecf20Sopenharmony_ci _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); 41888c2ecf20Sopenharmony_ci 41898c2ecf20Sopenharmony_ci ret = 41908c2ecf20Sopenharmony_ci _il_poll_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, 41918c2ecf20Sopenharmony_ci CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); 41928c2ecf20Sopenharmony_ci if (ret < 0) 41938c2ecf20Sopenharmony_ci IL_WARN("Master Disable Timed Out, 100 usec\n"); 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_ci D_INFO("stop master\n"); 41968c2ecf20Sopenharmony_ci 41978c2ecf20Sopenharmony_ci return ret; 41988c2ecf20Sopenharmony_ci} 41998c2ecf20Sopenharmony_ci 42008c2ecf20Sopenharmony_civoid 42018c2ecf20Sopenharmony_ci_il_apm_stop(struct il_priv *il) 42028c2ecf20Sopenharmony_ci{ 42038c2ecf20Sopenharmony_ci lockdep_assert_held(&il->reg_lock); 42048c2ecf20Sopenharmony_ci 42058c2ecf20Sopenharmony_ci D_INFO("Stop card, put in low power state\n"); 42068c2ecf20Sopenharmony_ci 42078c2ecf20Sopenharmony_ci /* Stop device's DMA activity */ 42088c2ecf20Sopenharmony_ci _il_apm_stop_master(il); 42098c2ecf20Sopenharmony_ci 42108c2ecf20Sopenharmony_ci /* Reset the entire device */ 42118c2ecf20Sopenharmony_ci _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); 42128c2ecf20Sopenharmony_ci 42138c2ecf20Sopenharmony_ci udelay(10); 42148c2ecf20Sopenharmony_ci 42158c2ecf20Sopenharmony_ci /* 42168c2ecf20Sopenharmony_ci * Clear "initialization complete" bit to move adapter from 42178c2ecf20Sopenharmony_ci * D0A* (powered-up Active) --> D0U* (Uninitialized) state. 42188c2ecf20Sopenharmony_ci */ 42198c2ecf20Sopenharmony_ci _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 42208c2ecf20Sopenharmony_ci} 42218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(_il_apm_stop); 42228c2ecf20Sopenharmony_ci 42238c2ecf20Sopenharmony_civoid 42248c2ecf20Sopenharmony_ciil_apm_stop(struct il_priv *il) 42258c2ecf20Sopenharmony_ci{ 42268c2ecf20Sopenharmony_ci unsigned long flags; 42278c2ecf20Sopenharmony_ci 42288c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, flags); 42298c2ecf20Sopenharmony_ci _il_apm_stop(il); 42308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, flags); 42318c2ecf20Sopenharmony_ci} 42328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_apm_stop); 42338c2ecf20Sopenharmony_ci 42348c2ecf20Sopenharmony_ci/* 42358c2ecf20Sopenharmony_ci * Start up NIC's basic functionality after it has been reset 42368c2ecf20Sopenharmony_ci * (e.g. after platform boot, or shutdown via il_apm_stop()) 42378c2ecf20Sopenharmony_ci * NOTE: This does not load uCode nor start the embedded processor 42388c2ecf20Sopenharmony_ci */ 42398c2ecf20Sopenharmony_ciint 42408c2ecf20Sopenharmony_ciil_apm_init(struct il_priv *il) 42418c2ecf20Sopenharmony_ci{ 42428c2ecf20Sopenharmony_ci int ret = 0; 42438c2ecf20Sopenharmony_ci u16 lctl; 42448c2ecf20Sopenharmony_ci 42458c2ecf20Sopenharmony_ci D_INFO("Init card's basic functions\n"); 42468c2ecf20Sopenharmony_ci 42478c2ecf20Sopenharmony_ci /* 42488c2ecf20Sopenharmony_ci * Use "set_bit" below rather than "write", to preserve any hardware 42498c2ecf20Sopenharmony_ci * bits already set by default after reset. 42508c2ecf20Sopenharmony_ci */ 42518c2ecf20Sopenharmony_ci 42528c2ecf20Sopenharmony_ci /* Disable L0S exit timer (platform NMI Work/Around) */ 42538c2ecf20Sopenharmony_ci il_set_bit(il, CSR_GIO_CHICKEN_BITS, 42548c2ecf20Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); 42558c2ecf20Sopenharmony_ci 42568c2ecf20Sopenharmony_ci /* 42578c2ecf20Sopenharmony_ci * Disable L0s without affecting L1; 42588c2ecf20Sopenharmony_ci * don't wait for ICH L0s (ICH bug W/A) 42598c2ecf20Sopenharmony_ci */ 42608c2ecf20Sopenharmony_ci il_set_bit(il, CSR_GIO_CHICKEN_BITS, 42618c2ecf20Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); 42628c2ecf20Sopenharmony_ci 42638c2ecf20Sopenharmony_ci /* Set FH wait threshold to maximum (HW error during stress W/A) */ 42648c2ecf20Sopenharmony_ci il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); 42658c2ecf20Sopenharmony_ci 42668c2ecf20Sopenharmony_ci /* 42678c2ecf20Sopenharmony_ci * Enable HAP INTA (interrupt from management bus) to 42688c2ecf20Sopenharmony_ci * wake device's PCI Express link L1a -> L0s 42698c2ecf20Sopenharmony_ci * NOTE: This is no-op for 3945 (non-existent bit) 42708c2ecf20Sopenharmony_ci */ 42718c2ecf20Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 42728c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); 42738c2ecf20Sopenharmony_ci 42748c2ecf20Sopenharmony_ci /* 42758c2ecf20Sopenharmony_ci * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition. 42768c2ecf20Sopenharmony_ci * Check if BIOS (or OS) enabled L1-ASPM on this device. 42778c2ecf20Sopenharmony_ci * If so (likely), disable L0S, so device moves directly L0->L1; 42788c2ecf20Sopenharmony_ci * costs negligible amount of power savings. 42798c2ecf20Sopenharmony_ci * If not (unlikely), enable L0S, so there is at least some 42808c2ecf20Sopenharmony_ci * power savings, even without L1. 42818c2ecf20Sopenharmony_ci */ 42828c2ecf20Sopenharmony_ci if (il->cfg->set_l0s) { 42838c2ecf20Sopenharmony_ci ret = pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); 42848c2ecf20Sopenharmony_ci if (!ret && (lctl & PCI_EXP_LNKCTL_ASPM_L1)) { 42858c2ecf20Sopenharmony_ci /* L1-ASPM enabled; disable(!) L0S */ 42868c2ecf20Sopenharmony_ci il_set_bit(il, CSR_GIO_REG, 42878c2ecf20Sopenharmony_ci CSR_GIO_REG_VAL_L0S_ENABLED); 42888c2ecf20Sopenharmony_ci D_POWER("L1 Enabled; Disabling L0S\n"); 42898c2ecf20Sopenharmony_ci } else { 42908c2ecf20Sopenharmony_ci /* L1-ASPM disabled; enable(!) L0S */ 42918c2ecf20Sopenharmony_ci il_clear_bit(il, CSR_GIO_REG, 42928c2ecf20Sopenharmony_ci CSR_GIO_REG_VAL_L0S_ENABLED); 42938c2ecf20Sopenharmony_ci D_POWER("L1 Disabled; Enabling L0S\n"); 42948c2ecf20Sopenharmony_ci } 42958c2ecf20Sopenharmony_ci } 42968c2ecf20Sopenharmony_ci 42978c2ecf20Sopenharmony_ci /* Configure analog phase-lock-loop before activating to D0A */ 42988c2ecf20Sopenharmony_ci if (il->cfg->pll_cfg_val) 42998c2ecf20Sopenharmony_ci il_set_bit(il, CSR_ANA_PLL_CFG, 43008c2ecf20Sopenharmony_ci il->cfg->pll_cfg_val); 43018c2ecf20Sopenharmony_ci 43028c2ecf20Sopenharmony_ci /* 43038c2ecf20Sopenharmony_ci * Set "initialization complete" bit to move adapter from 43048c2ecf20Sopenharmony_ci * D0U* --> D0A* (powered-up active) state. 43058c2ecf20Sopenharmony_ci */ 43068c2ecf20Sopenharmony_ci il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 43078c2ecf20Sopenharmony_ci 43088c2ecf20Sopenharmony_ci /* 43098c2ecf20Sopenharmony_ci * Wait for clock stabilization; once stabilized, access to 43108c2ecf20Sopenharmony_ci * device-internal resources is supported, e.g. il_wr_prph() 43118c2ecf20Sopenharmony_ci * and accesses to uCode SRAM. 43128c2ecf20Sopenharmony_ci */ 43138c2ecf20Sopenharmony_ci ret = 43148c2ecf20Sopenharmony_ci _il_poll_bit(il, CSR_GP_CNTRL, 43158c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 43168c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); 43178c2ecf20Sopenharmony_ci if (ret < 0) { 43188c2ecf20Sopenharmony_ci D_INFO("Failed to init the card\n"); 43198c2ecf20Sopenharmony_ci goto out; 43208c2ecf20Sopenharmony_ci } 43218c2ecf20Sopenharmony_ci 43228c2ecf20Sopenharmony_ci /* 43238c2ecf20Sopenharmony_ci * Enable DMA and BSM (if used) clocks, wait for them to stabilize. 43248c2ecf20Sopenharmony_ci * BSM (Boostrap State Machine) is only in 3945 and 4965. 43258c2ecf20Sopenharmony_ci * 43268c2ecf20Sopenharmony_ci * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits 43278c2ecf20Sopenharmony_ci * do not disable clocks. This preserves any hardware bits already 43288c2ecf20Sopenharmony_ci * set by default in "CLK_CTRL_REG" after reset. 43298c2ecf20Sopenharmony_ci */ 43308c2ecf20Sopenharmony_ci if (il->cfg->use_bsm) 43318c2ecf20Sopenharmony_ci il_wr_prph(il, APMG_CLK_EN_REG, 43328c2ecf20Sopenharmony_ci APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); 43338c2ecf20Sopenharmony_ci else 43348c2ecf20Sopenharmony_ci il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); 43358c2ecf20Sopenharmony_ci udelay(20); 43368c2ecf20Sopenharmony_ci 43378c2ecf20Sopenharmony_ci /* Disable L1-Active */ 43388c2ecf20Sopenharmony_ci il_set_bits_prph(il, APMG_PCIDEV_STT_REG, 43398c2ecf20Sopenharmony_ci APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 43408c2ecf20Sopenharmony_ci 43418c2ecf20Sopenharmony_ciout: 43428c2ecf20Sopenharmony_ci return ret; 43438c2ecf20Sopenharmony_ci} 43448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_apm_init); 43458c2ecf20Sopenharmony_ci 43468c2ecf20Sopenharmony_ciint 43478c2ecf20Sopenharmony_ciil_set_tx_power(struct il_priv *il, s8 tx_power, bool force) 43488c2ecf20Sopenharmony_ci{ 43498c2ecf20Sopenharmony_ci int ret; 43508c2ecf20Sopenharmony_ci s8 prev_tx_power; 43518c2ecf20Sopenharmony_ci bool defer; 43528c2ecf20Sopenharmony_ci 43538c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 43548c2ecf20Sopenharmony_ci 43558c2ecf20Sopenharmony_ci if (il->tx_power_user_lmt == tx_power && !force) 43568c2ecf20Sopenharmony_ci return 0; 43578c2ecf20Sopenharmony_ci 43588c2ecf20Sopenharmony_ci if (!il->ops->send_tx_power) 43598c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 43608c2ecf20Sopenharmony_ci 43618c2ecf20Sopenharmony_ci /* 0 dBm mean 1 milliwatt */ 43628c2ecf20Sopenharmony_ci if (tx_power < 0) { 43638c2ecf20Sopenharmony_ci IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power); 43648c2ecf20Sopenharmony_ci return -EINVAL; 43658c2ecf20Sopenharmony_ci } 43668c2ecf20Sopenharmony_ci 43678c2ecf20Sopenharmony_ci if (tx_power > il->tx_power_device_lmt) { 43688c2ecf20Sopenharmony_ci IL_WARN("Requested user TXPOWER %d above upper limit %d.\n", 43698c2ecf20Sopenharmony_ci tx_power, il->tx_power_device_lmt); 43708c2ecf20Sopenharmony_ci return -EINVAL; 43718c2ecf20Sopenharmony_ci } 43728c2ecf20Sopenharmony_ci 43738c2ecf20Sopenharmony_ci if (!il_is_ready_rf(il)) 43748c2ecf20Sopenharmony_ci return -EIO; 43758c2ecf20Sopenharmony_ci 43768c2ecf20Sopenharmony_ci /* scan complete and commit_rxon use tx_power_next value, 43778c2ecf20Sopenharmony_ci * it always need to be updated for newest request */ 43788c2ecf20Sopenharmony_ci il->tx_power_next = tx_power; 43798c2ecf20Sopenharmony_ci 43808c2ecf20Sopenharmony_ci /* do not set tx power when scanning or channel changing */ 43818c2ecf20Sopenharmony_ci defer = test_bit(S_SCANNING, &il->status) || 43828c2ecf20Sopenharmony_ci memcmp(&il->active, &il->staging, sizeof(il->staging)); 43838c2ecf20Sopenharmony_ci if (defer && !force) { 43848c2ecf20Sopenharmony_ci D_INFO("Deferring tx power set\n"); 43858c2ecf20Sopenharmony_ci return 0; 43868c2ecf20Sopenharmony_ci } 43878c2ecf20Sopenharmony_ci 43888c2ecf20Sopenharmony_ci prev_tx_power = il->tx_power_user_lmt; 43898c2ecf20Sopenharmony_ci il->tx_power_user_lmt = tx_power; 43908c2ecf20Sopenharmony_ci 43918c2ecf20Sopenharmony_ci ret = il->ops->send_tx_power(il); 43928c2ecf20Sopenharmony_ci 43938c2ecf20Sopenharmony_ci /* if fail to set tx_power, restore the orig. tx power */ 43948c2ecf20Sopenharmony_ci if (ret) { 43958c2ecf20Sopenharmony_ci il->tx_power_user_lmt = prev_tx_power; 43968c2ecf20Sopenharmony_ci il->tx_power_next = prev_tx_power; 43978c2ecf20Sopenharmony_ci } 43988c2ecf20Sopenharmony_ci return ret; 43998c2ecf20Sopenharmony_ci} 44008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_tx_power); 44018c2ecf20Sopenharmony_ci 44028c2ecf20Sopenharmony_civoid 44038c2ecf20Sopenharmony_ciil_send_bt_config(struct il_priv *il) 44048c2ecf20Sopenharmony_ci{ 44058c2ecf20Sopenharmony_ci struct il_bt_cmd bt_cmd = { 44068c2ecf20Sopenharmony_ci .lead_time = BT_LEAD_TIME_DEF, 44078c2ecf20Sopenharmony_ci .max_kill = BT_MAX_KILL_DEF, 44088c2ecf20Sopenharmony_ci .kill_ack_mask = 0, 44098c2ecf20Sopenharmony_ci .kill_cts_mask = 0, 44108c2ecf20Sopenharmony_ci }; 44118c2ecf20Sopenharmony_ci 44128c2ecf20Sopenharmony_ci if (!bt_coex_active) 44138c2ecf20Sopenharmony_ci bt_cmd.flags = BT_COEX_DISABLE; 44148c2ecf20Sopenharmony_ci else 44158c2ecf20Sopenharmony_ci bt_cmd.flags = BT_COEX_ENABLE; 44168c2ecf20Sopenharmony_ci 44178c2ecf20Sopenharmony_ci D_INFO("BT coex %s\n", 44188c2ecf20Sopenharmony_ci (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); 44198c2ecf20Sopenharmony_ci 44208c2ecf20Sopenharmony_ci if (il_send_cmd_pdu(il, C_BT_CONFIG, sizeof(struct il_bt_cmd), &bt_cmd)) 44218c2ecf20Sopenharmony_ci IL_ERR("failed to send BT Coex Config\n"); 44228c2ecf20Sopenharmony_ci} 44238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_bt_config); 44248c2ecf20Sopenharmony_ci 44258c2ecf20Sopenharmony_ciint 44268c2ecf20Sopenharmony_ciil_send_stats_request(struct il_priv *il, u8 flags, bool clear) 44278c2ecf20Sopenharmony_ci{ 44288c2ecf20Sopenharmony_ci struct il_stats_cmd stats_cmd = { 44298c2ecf20Sopenharmony_ci .configuration_flags = clear ? IL_STATS_CONF_CLEAR_STATS : 0, 44308c2ecf20Sopenharmony_ci }; 44318c2ecf20Sopenharmony_ci 44328c2ecf20Sopenharmony_ci if (flags & CMD_ASYNC) 44338c2ecf20Sopenharmony_ci return il_send_cmd_pdu_async(il, C_STATS, sizeof(struct il_stats_cmd), 44348c2ecf20Sopenharmony_ci &stats_cmd, NULL); 44358c2ecf20Sopenharmony_ci else 44368c2ecf20Sopenharmony_ci return il_send_cmd_pdu(il, C_STATS, sizeof(struct il_stats_cmd), 44378c2ecf20Sopenharmony_ci &stats_cmd); 44388c2ecf20Sopenharmony_ci} 44398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_stats_request); 44408c2ecf20Sopenharmony_ci 44418c2ecf20Sopenharmony_civoid 44428c2ecf20Sopenharmony_ciil_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb) 44438c2ecf20Sopenharmony_ci{ 44448c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 44458c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 44468c2ecf20Sopenharmony_ci struct il_sleep_notification *sleep = &(pkt->u.sleep_notif); 44478c2ecf20Sopenharmony_ci D_RX("sleep mode: %d, src: %d\n", 44488c2ecf20Sopenharmony_ci sleep->pm_sleep_mode, sleep->pm_wakeup_src); 44498c2ecf20Sopenharmony_ci#endif 44508c2ecf20Sopenharmony_ci} 44518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_pm_sleep); 44528c2ecf20Sopenharmony_ci 44538c2ecf20Sopenharmony_civoid 44548c2ecf20Sopenharmony_ciil_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb) 44558c2ecf20Sopenharmony_ci{ 44568c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 44578c2ecf20Sopenharmony_ci u32 len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; 44588c2ecf20Sopenharmony_ci D_RADIO("Dumping %d bytes of unhandled notification for %s:\n", len, 44598c2ecf20Sopenharmony_ci il_get_cmd_string(pkt->hdr.cmd)); 44608c2ecf20Sopenharmony_ci il_print_hex_dump(il, IL_DL_RADIO, pkt->u.raw, len); 44618c2ecf20Sopenharmony_ci} 44628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_pm_debug_stats); 44638c2ecf20Sopenharmony_ci 44648c2ecf20Sopenharmony_civoid 44658c2ecf20Sopenharmony_ciil_hdl_error(struct il_priv *il, struct il_rx_buf *rxb) 44668c2ecf20Sopenharmony_ci{ 44678c2ecf20Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 44688c2ecf20Sopenharmony_ci 44698c2ecf20Sopenharmony_ci IL_ERR("Error Reply type 0x%08X cmd %s (0x%02X) " 44708c2ecf20Sopenharmony_ci "seq 0x%04X ser 0x%08X\n", 44718c2ecf20Sopenharmony_ci le32_to_cpu(pkt->u.err_resp.error_type), 44728c2ecf20Sopenharmony_ci il_get_cmd_string(pkt->u.err_resp.cmd_id), 44738c2ecf20Sopenharmony_ci pkt->u.err_resp.cmd_id, 44748c2ecf20Sopenharmony_ci le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), 44758c2ecf20Sopenharmony_ci le32_to_cpu(pkt->u.err_resp.error_info)); 44768c2ecf20Sopenharmony_ci} 44778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_error); 44788c2ecf20Sopenharmony_ci 44798c2ecf20Sopenharmony_civoid 44808c2ecf20Sopenharmony_ciil_clear_isr_stats(struct il_priv *il) 44818c2ecf20Sopenharmony_ci{ 44828c2ecf20Sopenharmony_ci memset(&il->isr_stats, 0, sizeof(il->isr_stats)); 44838c2ecf20Sopenharmony_ci} 44848c2ecf20Sopenharmony_ci 44858c2ecf20Sopenharmony_ciint 44868c2ecf20Sopenharmony_ciil_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, 44878c2ecf20Sopenharmony_ci const struct ieee80211_tx_queue_params *params) 44888c2ecf20Sopenharmony_ci{ 44898c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 44908c2ecf20Sopenharmony_ci unsigned long flags; 44918c2ecf20Sopenharmony_ci int q; 44928c2ecf20Sopenharmony_ci 44938c2ecf20Sopenharmony_ci D_MAC80211("enter\n"); 44948c2ecf20Sopenharmony_ci 44958c2ecf20Sopenharmony_ci if (!il_is_ready_rf(il)) { 44968c2ecf20Sopenharmony_ci D_MAC80211("leave - RF not ready\n"); 44978c2ecf20Sopenharmony_ci return -EIO; 44988c2ecf20Sopenharmony_ci } 44998c2ecf20Sopenharmony_ci 45008c2ecf20Sopenharmony_ci if (queue >= AC_NUM) { 45018c2ecf20Sopenharmony_ci D_MAC80211("leave - queue >= AC_NUM %d\n", queue); 45028c2ecf20Sopenharmony_ci return 0; 45038c2ecf20Sopenharmony_ci } 45048c2ecf20Sopenharmony_ci 45058c2ecf20Sopenharmony_ci q = AC_NUM - 1 - queue; 45068c2ecf20Sopenharmony_ci 45078c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 45088c2ecf20Sopenharmony_ci 45098c2ecf20Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].cw_min = 45108c2ecf20Sopenharmony_ci cpu_to_le16(params->cw_min); 45118c2ecf20Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].cw_max = 45128c2ecf20Sopenharmony_ci cpu_to_le16(params->cw_max); 45138c2ecf20Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; 45148c2ecf20Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].edca_txop = 45158c2ecf20Sopenharmony_ci cpu_to_le16((params->txop * 32)); 45168c2ecf20Sopenharmony_ci 45178c2ecf20Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].reserved1 = 0; 45188c2ecf20Sopenharmony_ci 45198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 45208c2ecf20Sopenharmony_ci 45218c2ecf20Sopenharmony_ci D_MAC80211("leave\n"); 45228c2ecf20Sopenharmony_ci return 0; 45238c2ecf20Sopenharmony_ci} 45248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_conf_tx); 45258c2ecf20Sopenharmony_ci 45268c2ecf20Sopenharmony_ciint 45278c2ecf20Sopenharmony_ciil_mac_tx_last_beacon(struct ieee80211_hw *hw) 45288c2ecf20Sopenharmony_ci{ 45298c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 45308c2ecf20Sopenharmony_ci int ret; 45318c2ecf20Sopenharmony_ci 45328c2ecf20Sopenharmony_ci D_MAC80211("enter\n"); 45338c2ecf20Sopenharmony_ci 45348c2ecf20Sopenharmony_ci ret = (il->ibss_manager == IL_IBSS_MANAGER); 45358c2ecf20Sopenharmony_ci 45368c2ecf20Sopenharmony_ci D_MAC80211("leave ret %d\n", ret); 45378c2ecf20Sopenharmony_ci return ret; 45388c2ecf20Sopenharmony_ci} 45398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(il_mac_tx_last_beacon); 45408c2ecf20Sopenharmony_ci 45418c2ecf20Sopenharmony_cistatic int 45428c2ecf20Sopenharmony_ciil_set_mode(struct il_priv *il) 45438c2ecf20Sopenharmony_ci{ 45448c2ecf20Sopenharmony_ci il_connection_init_rx_config(il); 45458c2ecf20Sopenharmony_ci 45468c2ecf20Sopenharmony_ci if (il->ops->set_rxon_chain) 45478c2ecf20Sopenharmony_ci il->ops->set_rxon_chain(il); 45488c2ecf20Sopenharmony_ci 45498c2ecf20Sopenharmony_ci return il_commit_rxon(il); 45508c2ecf20Sopenharmony_ci} 45518c2ecf20Sopenharmony_ci 45528c2ecf20Sopenharmony_ciint 45538c2ecf20Sopenharmony_ciil_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 45548c2ecf20Sopenharmony_ci{ 45558c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 45568c2ecf20Sopenharmony_ci int err; 45578c2ecf20Sopenharmony_ci bool reset; 45588c2ecf20Sopenharmony_ci 45598c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 45608c2ecf20Sopenharmony_ci D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); 45618c2ecf20Sopenharmony_ci 45628c2ecf20Sopenharmony_ci if (!il_is_ready_rf(il)) { 45638c2ecf20Sopenharmony_ci IL_WARN("Try to add interface when device not ready\n"); 45648c2ecf20Sopenharmony_ci err = -EINVAL; 45658c2ecf20Sopenharmony_ci goto out; 45668c2ecf20Sopenharmony_ci } 45678c2ecf20Sopenharmony_ci 45688c2ecf20Sopenharmony_ci /* 45698c2ecf20Sopenharmony_ci * We do not support multiple virtual interfaces, but on hardware reset 45708c2ecf20Sopenharmony_ci * we have to add the same interface again. 45718c2ecf20Sopenharmony_ci */ 45728c2ecf20Sopenharmony_ci reset = (il->vif == vif); 45738c2ecf20Sopenharmony_ci if (il->vif && !reset) { 45748c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 45758c2ecf20Sopenharmony_ci goto out; 45768c2ecf20Sopenharmony_ci } 45778c2ecf20Sopenharmony_ci 45788c2ecf20Sopenharmony_ci il->vif = vif; 45798c2ecf20Sopenharmony_ci il->iw_mode = vif->type; 45808c2ecf20Sopenharmony_ci 45818c2ecf20Sopenharmony_ci err = il_set_mode(il); 45828c2ecf20Sopenharmony_ci if (err) { 45838c2ecf20Sopenharmony_ci IL_WARN("Fail to set mode %d\n", vif->type); 45848c2ecf20Sopenharmony_ci if (!reset) { 45858c2ecf20Sopenharmony_ci il->vif = NULL; 45868c2ecf20Sopenharmony_ci il->iw_mode = NL80211_IFTYPE_STATION; 45878c2ecf20Sopenharmony_ci } 45888c2ecf20Sopenharmony_ci } 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_ciout: 45918c2ecf20Sopenharmony_ci D_MAC80211("leave err %d\n", err); 45928c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 45938c2ecf20Sopenharmony_ci 45948c2ecf20Sopenharmony_ci return err; 45958c2ecf20Sopenharmony_ci} 45968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_add_interface); 45978c2ecf20Sopenharmony_ci 45988c2ecf20Sopenharmony_cistatic void 45998c2ecf20Sopenharmony_ciil_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif) 46008c2ecf20Sopenharmony_ci{ 46018c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_ci if (il->scan_vif == vif) { 46048c2ecf20Sopenharmony_ci il_scan_cancel_timeout(il, 200); 46058c2ecf20Sopenharmony_ci il_force_scan_end(il); 46068c2ecf20Sopenharmony_ci } 46078c2ecf20Sopenharmony_ci 46088c2ecf20Sopenharmony_ci il_set_mode(il); 46098c2ecf20Sopenharmony_ci} 46108c2ecf20Sopenharmony_ci 46118c2ecf20Sopenharmony_civoid 46128c2ecf20Sopenharmony_ciil_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 46138c2ecf20Sopenharmony_ci{ 46148c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 46158c2ecf20Sopenharmony_ci 46168c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 46178c2ecf20Sopenharmony_ci D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); 46188c2ecf20Sopenharmony_ci 46198c2ecf20Sopenharmony_ci WARN_ON(il->vif != vif); 46208c2ecf20Sopenharmony_ci il->vif = NULL; 46218c2ecf20Sopenharmony_ci il->iw_mode = NL80211_IFTYPE_UNSPECIFIED; 46228c2ecf20Sopenharmony_ci il_teardown_interface(il, vif); 46238c2ecf20Sopenharmony_ci eth_zero_addr(il->bssid); 46248c2ecf20Sopenharmony_ci 46258c2ecf20Sopenharmony_ci D_MAC80211("leave\n"); 46268c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 46278c2ecf20Sopenharmony_ci} 46288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_remove_interface); 46298c2ecf20Sopenharmony_ci 46308c2ecf20Sopenharmony_ciint 46318c2ecf20Sopenharmony_ciil_alloc_txq_mem(struct il_priv *il) 46328c2ecf20Sopenharmony_ci{ 46338c2ecf20Sopenharmony_ci if (!il->txq) 46348c2ecf20Sopenharmony_ci il->txq = 46358c2ecf20Sopenharmony_ci kcalloc(il->cfg->num_of_queues, 46368c2ecf20Sopenharmony_ci sizeof(struct il_tx_queue), 46378c2ecf20Sopenharmony_ci GFP_KERNEL); 46388c2ecf20Sopenharmony_ci if (!il->txq) { 46398c2ecf20Sopenharmony_ci IL_ERR("Not enough memory for txq\n"); 46408c2ecf20Sopenharmony_ci return -ENOMEM; 46418c2ecf20Sopenharmony_ci } 46428c2ecf20Sopenharmony_ci return 0; 46438c2ecf20Sopenharmony_ci} 46448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_alloc_txq_mem); 46458c2ecf20Sopenharmony_ci 46468c2ecf20Sopenharmony_civoid 46478c2ecf20Sopenharmony_ciil_free_txq_mem(struct il_priv *il) 46488c2ecf20Sopenharmony_ci{ 46498c2ecf20Sopenharmony_ci kfree(il->txq); 46508c2ecf20Sopenharmony_ci il->txq = NULL; 46518c2ecf20Sopenharmony_ci} 46528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_free_txq_mem); 46538c2ecf20Sopenharmony_ci 46548c2ecf20Sopenharmony_ciint 46558c2ecf20Sopenharmony_ciil_force_reset(struct il_priv *il, bool external) 46568c2ecf20Sopenharmony_ci{ 46578c2ecf20Sopenharmony_ci struct il_force_reset *force_reset; 46588c2ecf20Sopenharmony_ci 46598c2ecf20Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 46608c2ecf20Sopenharmony_ci return -EINVAL; 46618c2ecf20Sopenharmony_ci 46628c2ecf20Sopenharmony_ci force_reset = &il->force_reset; 46638c2ecf20Sopenharmony_ci force_reset->reset_request_count++; 46648c2ecf20Sopenharmony_ci if (!external) { 46658c2ecf20Sopenharmony_ci if (force_reset->last_force_reset_jiffies && 46668c2ecf20Sopenharmony_ci time_after(force_reset->last_force_reset_jiffies + 46678c2ecf20Sopenharmony_ci force_reset->reset_duration, jiffies)) { 46688c2ecf20Sopenharmony_ci D_INFO("force reset rejected\n"); 46698c2ecf20Sopenharmony_ci force_reset->reset_reject_count++; 46708c2ecf20Sopenharmony_ci return -EAGAIN; 46718c2ecf20Sopenharmony_ci } 46728c2ecf20Sopenharmony_ci } 46738c2ecf20Sopenharmony_ci force_reset->reset_success_count++; 46748c2ecf20Sopenharmony_ci force_reset->last_force_reset_jiffies = jiffies; 46758c2ecf20Sopenharmony_ci 46768c2ecf20Sopenharmony_ci /* 46778c2ecf20Sopenharmony_ci * if the request is from external(ex: debugfs), 46788c2ecf20Sopenharmony_ci * then always perform the request in regardless the module 46798c2ecf20Sopenharmony_ci * parameter setting 46808c2ecf20Sopenharmony_ci * if the request is from internal (uCode error or driver 46818c2ecf20Sopenharmony_ci * detect failure), then fw_restart module parameter 46828c2ecf20Sopenharmony_ci * need to be check before performing firmware reload 46838c2ecf20Sopenharmony_ci */ 46848c2ecf20Sopenharmony_ci 46858c2ecf20Sopenharmony_ci if (!external && !il->cfg->mod_params->restart_fw) { 46868c2ecf20Sopenharmony_ci D_INFO("Cancel firmware reload based on " 46878c2ecf20Sopenharmony_ci "module parameter setting\n"); 46888c2ecf20Sopenharmony_ci return 0; 46898c2ecf20Sopenharmony_ci } 46908c2ecf20Sopenharmony_ci 46918c2ecf20Sopenharmony_ci IL_ERR("On demand firmware reload\n"); 46928c2ecf20Sopenharmony_ci 46938c2ecf20Sopenharmony_ci /* Set the FW error flag -- cleared on il_down */ 46948c2ecf20Sopenharmony_ci set_bit(S_FW_ERROR, &il->status); 46958c2ecf20Sopenharmony_ci wake_up(&il->wait_command_queue); 46968c2ecf20Sopenharmony_ci /* 46978c2ecf20Sopenharmony_ci * Keep the restart process from trying to send host 46988c2ecf20Sopenharmony_ci * commands by clearing the INIT status bit 46998c2ecf20Sopenharmony_ci */ 47008c2ecf20Sopenharmony_ci clear_bit(S_READY, &il->status); 47018c2ecf20Sopenharmony_ci queue_work(il->workqueue, &il->restart); 47028c2ecf20Sopenharmony_ci 47038c2ecf20Sopenharmony_ci return 0; 47048c2ecf20Sopenharmony_ci} 47058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_force_reset); 47068c2ecf20Sopenharmony_ci 47078c2ecf20Sopenharmony_ciint 47088c2ecf20Sopenharmony_ciil_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 47098c2ecf20Sopenharmony_ci enum nl80211_iftype newtype, bool newp2p) 47108c2ecf20Sopenharmony_ci{ 47118c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 47128c2ecf20Sopenharmony_ci int err; 47138c2ecf20Sopenharmony_ci 47148c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 47158c2ecf20Sopenharmony_ci D_MAC80211("enter: type %d, addr %pM newtype %d newp2p %d\n", 47168c2ecf20Sopenharmony_ci vif->type, vif->addr, newtype, newp2p); 47178c2ecf20Sopenharmony_ci 47188c2ecf20Sopenharmony_ci if (newp2p) { 47198c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 47208c2ecf20Sopenharmony_ci goto out; 47218c2ecf20Sopenharmony_ci } 47228c2ecf20Sopenharmony_ci 47238c2ecf20Sopenharmony_ci if (!il->vif || !il_is_ready_rf(il)) { 47248c2ecf20Sopenharmony_ci /* 47258c2ecf20Sopenharmony_ci * Huh? But wait ... this can maybe happen when 47268c2ecf20Sopenharmony_ci * we're in the middle of a firmware restart! 47278c2ecf20Sopenharmony_ci */ 47288c2ecf20Sopenharmony_ci err = -EBUSY; 47298c2ecf20Sopenharmony_ci goto out; 47308c2ecf20Sopenharmony_ci } 47318c2ecf20Sopenharmony_ci 47328c2ecf20Sopenharmony_ci /* success */ 47338c2ecf20Sopenharmony_ci vif->type = newtype; 47348c2ecf20Sopenharmony_ci vif->p2p = false; 47358c2ecf20Sopenharmony_ci il->iw_mode = newtype; 47368c2ecf20Sopenharmony_ci il_teardown_interface(il, vif); 47378c2ecf20Sopenharmony_ci err = 0; 47388c2ecf20Sopenharmony_ci 47398c2ecf20Sopenharmony_ciout: 47408c2ecf20Sopenharmony_ci D_MAC80211("leave err %d\n", err); 47418c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 47428c2ecf20Sopenharmony_ci 47438c2ecf20Sopenharmony_ci return err; 47448c2ecf20Sopenharmony_ci} 47458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_change_interface); 47468c2ecf20Sopenharmony_ci 47478c2ecf20Sopenharmony_civoid il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 47488c2ecf20Sopenharmony_ci u32 queues, bool drop) 47498c2ecf20Sopenharmony_ci{ 47508c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 47518c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(500); 47528c2ecf20Sopenharmony_ci int i; 47538c2ecf20Sopenharmony_ci 47548c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 47558c2ecf20Sopenharmony_ci D_MAC80211("enter\n"); 47568c2ecf20Sopenharmony_ci 47578c2ecf20Sopenharmony_ci if (il->txq == NULL) 47588c2ecf20Sopenharmony_ci goto out; 47598c2ecf20Sopenharmony_ci 47608c2ecf20Sopenharmony_ci for (i = 0; i < il->hw_params.max_txq_num; i++) { 47618c2ecf20Sopenharmony_ci struct il_queue *q; 47628c2ecf20Sopenharmony_ci 47638c2ecf20Sopenharmony_ci if (i == il->cmd_queue) 47648c2ecf20Sopenharmony_ci continue; 47658c2ecf20Sopenharmony_ci 47668c2ecf20Sopenharmony_ci q = &il->txq[i].q; 47678c2ecf20Sopenharmony_ci if (q->read_ptr == q->write_ptr) 47688c2ecf20Sopenharmony_ci continue; 47698c2ecf20Sopenharmony_ci 47708c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 47718c2ecf20Sopenharmony_ci IL_ERR("Failed to flush queue %d\n", q->id); 47728c2ecf20Sopenharmony_ci break; 47738c2ecf20Sopenharmony_ci } 47748c2ecf20Sopenharmony_ci 47758c2ecf20Sopenharmony_ci msleep(20); 47768c2ecf20Sopenharmony_ci } 47778c2ecf20Sopenharmony_ciout: 47788c2ecf20Sopenharmony_ci D_MAC80211("leave\n"); 47798c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 47808c2ecf20Sopenharmony_ci} 47818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_flush); 47828c2ecf20Sopenharmony_ci 47838c2ecf20Sopenharmony_ci/* 47848c2ecf20Sopenharmony_ci * On every watchdog tick we check (latest) time stamp. If it does not 47858c2ecf20Sopenharmony_ci * change during timeout period and queue is not empty we reset firmware. 47868c2ecf20Sopenharmony_ci */ 47878c2ecf20Sopenharmony_cistatic int 47888c2ecf20Sopenharmony_ciil_check_stuck_queue(struct il_priv *il, int cnt) 47898c2ecf20Sopenharmony_ci{ 47908c2ecf20Sopenharmony_ci struct il_tx_queue *txq = &il->txq[cnt]; 47918c2ecf20Sopenharmony_ci struct il_queue *q = &txq->q; 47928c2ecf20Sopenharmony_ci unsigned long timeout; 47938c2ecf20Sopenharmony_ci unsigned long now = jiffies; 47948c2ecf20Sopenharmony_ci int ret; 47958c2ecf20Sopenharmony_ci 47968c2ecf20Sopenharmony_ci if (q->read_ptr == q->write_ptr) { 47978c2ecf20Sopenharmony_ci txq->time_stamp = now; 47988c2ecf20Sopenharmony_ci return 0; 47998c2ecf20Sopenharmony_ci } 48008c2ecf20Sopenharmony_ci 48018c2ecf20Sopenharmony_ci timeout = 48028c2ecf20Sopenharmony_ci txq->time_stamp + 48038c2ecf20Sopenharmony_ci msecs_to_jiffies(il->cfg->wd_timeout); 48048c2ecf20Sopenharmony_ci 48058c2ecf20Sopenharmony_ci if (time_after(now, timeout)) { 48068c2ecf20Sopenharmony_ci IL_ERR("Queue %d stuck for %u ms.\n", q->id, 48078c2ecf20Sopenharmony_ci jiffies_to_msecs(now - txq->time_stamp)); 48088c2ecf20Sopenharmony_ci ret = il_force_reset(il, false); 48098c2ecf20Sopenharmony_ci return (ret == -EAGAIN) ? 0 : 1; 48108c2ecf20Sopenharmony_ci } 48118c2ecf20Sopenharmony_ci 48128c2ecf20Sopenharmony_ci return 0; 48138c2ecf20Sopenharmony_ci} 48148c2ecf20Sopenharmony_ci 48158c2ecf20Sopenharmony_ci/* 48168c2ecf20Sopenharmony_ci * Making watchdog tick be a quarter of timeout assure we will 48178c2ecf20Sopenharmony_ci * discover the queue hung between timeout and 1.25*timeout 48188c2ecf20Sopenharmony_ci */ 48198c2ecf20Sopenharmony_ci#define IL_WD_TICK(timeout) ((timeout) / 4) 48208c2ecf20Sopenharmony_ci 48218c2ecf20Sopenharmony_ci/* 48228c2ecf20Sopenharmony_ci * Watchdog timer callback, we check each tx queue for stuck, if if hung 48238c2ecf20Sopenharmony_ci * we reset the firmware. If everything is fine just rearm the timer. 48248c2ecf20Sopenharmony_ci */ 48258c2ecf20Sopenharmony_civoid 48268c2ecf20Sopenharmony_ciil_bg_watchdog(struct timer_list *t) 48278c2ecf20Sopenharmony_ci{ 48288c2ecf20Sopenharmony_ci struct il_priv *il = from_timer(il, t, watchdog); 48298c2ecf20Sopenharmony_ci int cnt; 48308c2ecf20Sopenharmony_ci unsigned long timeout; 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 48338c2ecf20Sopenharmony_ci return; 48348c2ecf20Sopenharmony_ci 48358c2ecf20Sopenharmony_ci timeout = il->cfg->wd_timeout; 48368c2ecf20Sopenharmony_ci if (timeout == 0) 48378c2ecf20Sopenharmony_ci return; 48388c2ecf20Sopenharmony_ci 48398c2ecf20Sopenharmony_ci /* monitor and check for stuck cmd queue */ 48408c2ecf20Sopenharmony_ci if (il_check_stuck_queue(il, il->cmd_queue)) 48418c2ecf20Sopenharmony_ci return; 48428c2ecf20Sopenharmony_ci 48438c2ecf20Sopenharmony_ci /* monitor and check for other stuck queues */ 48448c2ecf20Sopenharmony_ci for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { 48458c2ecf20Sopenharmony_ci /* skip as we already checked the command queue */ 48468c2ecf20Sopenharmony_ci if (cnt == il->cmd_queue) 48478c2ecf20Sopenharmony_ci continue; 48488c2ecf20Sopenharmony_ci if (il_check_stuck_queue(il, cnt)) 48498c2ecf20Sopenharmony_ci return; 48508c2ecf20Sopenharmony_ci } 48518c2ecf20Sopenharmony_ci 48528c2ecf20Sopenharmony_ci mod_timer(&il->watchdog, 48538c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); 48548c2ecf20Sopenharmony_ci} 48558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_bg_watchdog); 48568c2ecf20Sopenharmony_ci 48578c2ecf20Sopenharmony_civoid 48588c2ecf20Sopenharmony_ciil_setup_watchdog(struct il_priv *il) 48598c2ecf20Sopenharmony_ci{ 48608c2ecf20Sopenharmony_ci unsigned int timeout = il->cfg->wd_timeout; 48618c2ecf20Sopenharmony_ci 48628c2ecf20Sopenharmony_ci if (timeout) 48638c2ecf20Sopenharmony_ci mod_timer(&il->watchdog, 48648c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); 48658c2ecf20Sopenharmony_ci else 48668c2ecf20Sopenharmony_ci del_timer(&il->watchdog); 48678c2ecf20Sopenharmony_ci} 48688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_setup_watchdog); 48698c2ecf20Sopenharmony_ci 48708c2ecf20Sopenharmony_ci/* 48718c2ecf20Sopenharmony_ci * extended beacon time format 48728c2ecf20Sopenharmony_ci * time in usec will be changed into a 32-bit value in extended:internal format 48738c2ecf20Sopenharmony_ci * the extended part is the beacon counts 48748c2ecf20Sopenharmony_ci * the internal part is the time in usec within one beacon interval 48758c2ecf20Sopenharmony_ci */ 48768c2ecf20Sopenharmony_ciu32 48778c2ecf20Sopenharmony_ciil_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval) 48788c2ecf20Sopenharmony_ci{ 48798c2ecf20Sopenharmony_ci u32 quot; 48808c2ecf20Sopenharmony_ci u32 rem; 48818c2ecf20Sopenharmony_ci u32 interval = beacon_interval * TIME_UNIT; 48828c2ecf20Sopenharmony_ci 48838c2ecf20Sopenharmony_ci if (!interval || !usec) 48848c2ecf20Sopenharmony_ci return 0; 48858c2ecf20Sopenharmony_ci 48868c2ecf20Sopenharmony_ci quot = 48878c2ecf20Sopenharmony_ci (usec / 48888c2ecf20Sopenharmony_ci interval) & (il_beacon_time_mask_high(il, 48898c2ecf20Sopenharmony_ci il->hw_params. 48908c2ecf20Sopenharmony_ci beacon_time_tsf_bits) >> il-> 48918c2ecf20Sopenharmony_ci hw_params.beacon_time_tsf_bits); 48928c2ecf20Sopenharmony_ci rem = 48938c2ecf20Sopenharmony_ci (usec % interval) & il_beacon_time_mask_low(il, 48948c2ecf20Sopenharmony_ci il->hw_params. 48958c2ecf20Sopenharmony_ci beacon_time_tsf_bits); 48968c2ecf20Sopenharmony_ci 48978c2ecf20Sopenharmony_ci return (quot << il->hw_params.beacon_time_tsf_bits) + rem; 48988c2ecf20Sopenharmony_ci} 48998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_usecs_to_beacons); 49008c2ecf20Sopenharmony_ci 49018c2ecf20Sopenharmony_ci/* base is usually what we get from ucode with each received frame, 49028c2ecf20Sopenharmony_ci * the same as HW timer counter counting down 49038c2ecf20Sopenharmony_ci */ 49048c2ecf20Sopenharmony_ci__le32 49058c2ecf20Sopenharmony_ciil_add_beacon_time(struct il_priv *il, u32 base, u32 addon, 49068c2ecf20Sopenharmony_ci u32 beacon_interval) 49078c2ecf20Sopenharmony_ci{ 49088c2ecf20Sopenharmony_ci u32 base_low = base & il_beacon_time_mask_low(il, 49098c2ecf20Sopenharmony_ci il->hw_params. 49108c2ecf20Sopenharmony_ci beacon_time_tsf_bits); 49118c2ecf20Sopenharmony_ci u32 addon_low = addon & il_beacon_time_mask_low(il, 49128c2ecf20Sopenharmony_ci il->hw_params. 49138c2ecf20Sopenharmony_ci beacon_time_tsf_bits); 49148c2ecf20Sopenharmony_ci u32 interval = beacon_interval * TIME_UNIT; 49158c2ecf20Sopenharmony_ci u32 res = (base & il_beacon_time_mask_high(il, 49168c2ecf20Sopenharmony_ci il->hw_params. 49178c2ecf20Sopenharmony_ci beacon_time_tsf_bits)) + 49188c2ecf20Sopenharmony_ci (addon & il_beacon_time_mask_high(il, 49198c2ecf20Sopenharmony_ci il->hw_params. 49208c2ecf20Sopenharmony_ci beacon_time_tsf_bits)); 49218c2ecf20Sopenharmony_ci 49228c2ecf20Sopenharmony_ci if (base_low > addon_low) 49238c2ecf20Sopenharmony_ci res += base_low - addon_low; 49248c2ecf20Sopenharmony_ci else if (base_low < addon_low) { 49258c2ecf20Sopenharmony_ci res += interval + base_low - addon_low; 49268c2ecf20Sopenharmony_ci res += (1 << il->hw_params.beacon_time_tsf_bits); 49278c2ecf20Sopenharmony_ci } else 49288c2ecf20Sopenharmony_ci res += (1 << il->hw_params.beacon_time_tsf_bits); 49298c2ecf20Sopenharmony_ci 49308c2ecf20Sopenharmony_ci return cpu_to_le32(res); 49318c2ecf20Sopenharmony_ci} 49328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_add_beacon_time); 49338c2ecf20Sopenharmony_ci 49348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 49358c2ecf20Sopenharmony_ci 49368c2ecf20Sopenharmony_cistatic int 49378c2ecf20Sopenharmony_ciil_pci_suspend(struct device *device) 49388c2ecf20Sopenharmony_ci{ 49398c2ecf20Sopenharmony_ci struct il_priv *il = dev_get_drvdata(device); 49408c2ecf20Sopenharmony_ci 49418c2ecf20Sopenharmony_ci /* 49428c2ecf20Sopenharmony_ci * This function is called when system goes into suspend state 49438c2ecf20Sopenharmony_ci * mac80211 will call il_mac_stop() from the mac80211 suspend function 49448c2ecf20Sopenharmony_ci * first but since il_mac_stop() has no knowledge of who the caller is, 49458c2ecf20Sopenharmony_ci * it will not call apm_ops.stop() to stop the DMA operation. 49468c2ecf20Sopenharmony_ci * Calling apm_ops.stop here to make sure we stop the DMA. 49478c2ecf20Sopenharmony_ci */ 49488c2ecf20Sopenharmony_ci il_apm_stop(il); 49498c2ecf20Sopenharmony_ci 49508c2ecf20Sopenharmony_ci return 0; 49518c2ecf20Sopenharmony_ci} 49528c2ecf20Sopenharmony_ci 49538c2ecf20Sopenharmony_cistatic int 49548c2ecf20Sopenharmony_ciil_pci_resume(struct device *device) 49558c2ecf20Sopenharmony_ci{ 49568c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 49578c2ecf20Sopenharmony_ci struct il_priv *il = pci_get_drvdata(pdev); 49588c2ecf20Sopenharmony_ci bool hw_rfkill = false; 49598c2ecf20Sopenharmony_ci 49608c2ecf20Sopenharmony_ci /* 49618c2ecf20Sopenharmony_ci * We disable the RETRY_TIMEOUT register (0x41) to keep 49628c2ecf20Sopenharmony_ci * PCI Tx retries from interfering with C3 CPU state. 49638c2ecf20Sopenharmony_ci */ 49648c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); 49658c2ecf20Sopenharmony_ci 49668c2ecf20Sopenharmony_ci il_enable_interrupts(il); 49678c2ecf20Sopenharmony_ci 49688c2ecf20Sopenharmony_ci if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) 49698c2ecf20Sopenharmony_ci hw_rfkill = true; 49708c2ecf20Sopenharmony_ci 49718c2ecf20Sopenharmony_ci if (hw_rfkill) 49728c2ecf20Sopenharmony_ci set_bit(S_RFKILL, &il->status); 49738c2ecf20Sopenharmony_ci else 49748c2ecf20Sopenharmony_ci clear_bit(S_RFKILL, &il->status); 49758c2ecf20Sopenharmony_ci 49768c2ecf20Sopenharmony_ci wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rfkill); 49778c2ecf20Sopenharmony_ci 49788c2ecf20Sopenharmony_ci return 0; 49798c2ecf20Sopenharmony_ci} 49808c2ecf20Sopenharmony_ci 49818c2ecf20Sopenharmony_ciSIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume); 49828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_pm_ops); 49838c2ecf20Sopenharmony_ci 49848c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 49858c2ecf20Sopenharmony_ci 49868c2ecf20Sopenharmony_cistatic void 49878c2ecf20Sopenharmony_ciil_update_qos(struct il_priv *il) 49888c2ecf20Sopenharmony_ci{ 49898c2ecf20Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 49908c2ecf20Sopenharmony_ci return; 49918c2ecf20Sopenharmony_ci 49928c2ecf20Sopenharmony_ci il->qos_data.def_qos_parm.qos_flags = 0; 49938c2ecf20Sopenharmony_ci 49948c2ecf20Sopenharmony_ci if (il->qos_data.qos_active) 49958c2ecf20Sopenharmony_ci il->qos_data.def_qos_parm.qos_flags |= 49968c2ecf20Sopenharmony_ci QOS_PARAM_FLG_UPDATE_EDCA_MSK; 49978c2ecf20Sopenharmony_ci 49988c2ecf20Sopenharmony_ci if (il->ht.enabled) 49998c2ecf20Sopenharmony_ci il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; 50008c2ecf20Sopenharmony_ci 50018c2ecf20Sopenharmony_ci D_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n", 50028c2ecf20Sopenharmony_ci il->qos_data.qos_active, il->qos_data.def_qos_parm.qos_flags); 50038c2ecf20Sopenharmony_ci 50048c2ecf20Sopenharmony_ci il_send_cmd_pdu_async(il, C_QOS_PARAM, sizeof(struct il_qosparam_cmd), 50058c2ecf20Sopenharmony_ci &il->qos_data.def_qos_parm, NULL); 50068c2ecf20Sopenharmony_ci} 50078c2ecf20Sopenharmony_ci 50088c2ecf20Sopenharmony_ci/* 50098c2ecf20Sopenharmony_ci * il_mac_config - mac80211 config callback 50108c2ecf20Sopenharmony_ci */ 50118c2ecf20Sopenharmony_ciint 50128c2ecf20Sopenharmony_ciil_mac_config(struct ieee80211_hw *hw, u32 changed) 50138c2ecf20Sopenharmony_ci{ 50148c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 50158c2ecf20Sopenharmony_ci const struct il_channel_info *ch_info; 50168c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 50178c2ecf20Sopenharmony_ci struct ieee80211_channel *channel = conf->chandef.chan; 50188c2ecf20Sopenharmony_ci struct il_ht_config *ht_conf = &il->current_ht_config; 50198c2ecf20Sopenharmony_ci unsigned long flags = 0; 50208c2ecf20Sopenharmony_ci int ret = 0; 50218c2ecf20Sopenharmony_ci u16 ch; 50228c2ecf20Sopenharmony_ci int scan_active = 0; 50238c2ecf20Sopenharmony_ci bool ht_changed = false; 50248c2ecf20Sopenharmony_ci 50258c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 50268c2ecf20Sopenharmony_ci D_MAC80211("enter: channel %d changed 0x%X\n", channel->hw_value, 50278c2ecf20Sopenharmony_ci changed); 50288c2ecf20Sopenharmony_ci 50298c2ecf20Sopenharmony_ci if (unlikely(test_bit(S_SCANNING, &il->status))) { 50308c2ecf20Sopenharmony_ci scan_active = 1; 50318c2ecf20Sopenharmony_ci D_MAC80211("scan active\n"); 50328c2ecf20Sopenharmony_ci } 50338c2ecf20Sopenharmony_ci 50348c2ecf20Sopenharmony_ci if (changed & 50358c2ecf20Sopenharmony_ci (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { 50368c2ecf20Sopenharmony_ci /* mac80211 uses static for non-HT which is what we want */ 50378c2ecf20Sopenharmony_ci il->current_ht_config.smps = conf->smps_mode; 50388c2ecf20Sopenharmony_ci 50398c2ecf20Sopenharmony_ci /* 50408c2ecf20Sopenharmony_ci * Recalculate chain counts. 50418c2ecf20Sopenharmony_ci * 50428c2ecf20Sopenharmony_ci * If monitor mode is enabled then mac80211 will 50438c2ecf20Sopenharmony_ci * set up the SM PS mode to OFF if an HT channel is 50448c2ecf20Sopenharmony_ci * configured. 50458c2ecf20Sopenharmony_ci */ 50468c2ecf20Sopenharmony_ci if (il->ops->set_rxon_chain) 50478c2ecf20Sopenharmony_ci il->ops->set_rxon_chain(il); 50488c2ecf20Sopenharmony_ci } 50498c2ecf20Sopenharmony_ci 50508c2ecf20Sopenharmony_ci /* during scanning mac80211 will delay channel setting until 50518c2ecf20Sopenharmony_ci * scan finish with changed = 0 50528c2ecf20Sopenharmony_ci */ 50538c2ecf20Sopenharmony_ci if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { 50548c2ecf20Sopenharmony_ci 50558c2ecf20Sopenharmony_ci if (scan_active) 50568c2ecf20Sopenharmony_ci goto set_ch_out; 50578c2ecf20Sopenharmony_ci 50588c2ecf20Sopenharmony_ci ch = channel->hw_value; 50598c2ecf20Sopenharmony_ci ch_info = il_get_channel_info(il, channel->band, ch); 50608c2ecf20Sopenharmony_ci if (!il_is_channel_valid(ch_info)) { 50618c2ecf20Sopenharmony_ci D_MAC80211("leave - invalid channel\n"); 50628c2ecf20Sopenharmony_ci ret = -EINVAL; 50638c2ecf20Sopenharmony_ci goto set_ch_out; 50648c2ecf20Sopenharmony_ci } 50658c2ecf20Sopenharmony_ci 50668c2ecf20Sopenharmony_ci if (il->iw_mode == NL80211_IFTYPE_ADHOC && 50678c2ecf20Sopenharmony_ci !il_is_channel_ibss(ch_info)) { 50688c2ecf20Sopenharmony_ci D_MAC80211("leave - not IBSS channel\n"); 50698c2ecf20Sopenharmony_ci ret = -EINVAL; 50708c2ecf20Sopenharmony_ci goto set_ch_out; 50718c2ecf20Sopenharmony_ci } 50728c2ecf20Sopenharmony_ci 50738c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 50748c2ecf20Sopenharmony_ci 50758c2ecf20Sopenharmony_ci /* Configure HT40 channels */ 50768c2ecf20Sopenharmony_ci if (il->ht.enabled != conf_is_ht(conf)) { 50778c2ecf20Sopenharmony_ci il->ht.enabled = conf_is_ht(conf); 50788c2ecf20Sopenharmony_ci ht_changed = true; 50798c2ecf20Sopenharmony_ci } 50808c2ecf20Sopenharmony_ci if (il->ht.enabled) { 50818c2ecf20Sopenharmony_ci if (conf_is_ht40_minus(conf)) { 50828c2ecf20Sopenharmony_ci il->ht.extension_chan_offset = 50838c2ecf20Sopenharmony_ci IEEE80211_HT_PARAM_CHA_SEC_BELOW; 50848c2ecf20Sopenharmony_ci il->ht.is_40mhz = true; 50858c2ecf20Sopenharmony_ci } else if (conf_is_ht40_plus(conf)) { 50868c2ecf20Sopenharmony_ci il->ht.extension_chan_offset = 50878c2ecf20Sopenharmony_ci IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 50888c2ecf20Sopenharmony_ci il->ht.is_40mhz = true; 50898c2ecf20Sopenharmony_ci } else { 50908c2ecf20Sopenharmony_ci il->ht.extension_chan_offset = 50918c2ecf20Sopenharmony_ci IEEE80211_HT_PARAM_CHA_SEC_NONE; 50928c2ecf20Sopenharmony_ci il->ht.is_40mhz = false; 50938c2ecf20Sopenharmony_ci } 50948c2ecf20Sopenharmony_ci } else 50958c2ecf20Sopenharmony_ci il->ht.is_40mhz = false; 50968c2ecf20Sopenharmony_ci 50978c2ecf20Sopenharmony_ci /* 50988c2ecf20Sopenharmony_ci * Default to no protection. Protection mode will 50998c2ecf20Sopenharmony_ci * later be set from BSS config in il_ht_conf 51008c2ecf20Sopenharmony_ci */ 51018c2ecf20Sopenharmony_ci il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; 51028c2ecf20Sopenharmony_ci 51038c2ecf20Sopenharmony_ci /* if we are switching from ht to 2.4 clear flags 51048c2ecf20Sopenharmony_ci * from any ht related info since 2.4 does not 51058c2ecf20Sopenharmony_ci * support ht */ 51068c2ecf20Sopenharmony_ci if ((le16_to_cpu(il->staging.channel) != ch)) 51078c2ecf20Sopenharmony_ci il->staging.flags = 0; 51088c2ecf20Sopenharmony_ci 51098c2ecf20Sopenharmony_ci il_set_rxon_channel(il, channel); 51108c2ecf20Sopenharmony_ci il_set_rxon_ht(il, ht_conf); 51118c2ecf20Sopenharmony_ci 51128c2ecf20Sopenharmony_ci il_set_flags_for_band(il, channel->band, il->vif); 51138c2ecf20Sopenharmony_ci 51148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 51158c2ecf20Sopenharmony_ci 51168c2ecf20Sopenharmony_ci if (il->ops->update_bcast_stations) 51178c2ecf20Sopenharmony_ci ret = il->ops->update_bcast_stations(il); 51188c2ecf20Sopenharmony_ci 51198c2ecf20Sopenharmony_ciset_ch_out: 51208c2ecf20Sopenharmony_ci /* The list of supported rates and rate mask can be different 51218c2ecf20Sopenharmony_ci * for each band; since the band may have changed, reset 51228c2ecf20Sopenharmony_ci * the rate mask to what mac80211 lists */ 51238c2ecf20Sopenharmony_ci il_set_rate(il); 51248c2ecf20Sopenharmony_ci } 51258c2ecf20Sopenharmony_ci 51268c2ecf20Sopenharmony_ci if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { 51278c2ecf20Sopenharmony_ci il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS); 51288c2ecf20Sopenharmony_ci if (!il->power_data.ps_disabled) 51298c2ecf20Sopenharmony_ci IL_WARN_ONCE("Enabling power save might cause firmware crashes\n"); 51308c2ecf20Sopenharmony_ci ret = il_power_update_mode(il, false); 51318c2ecf20Sopenharmony_ci if (ret) 51328c2ecf20Sopenharmony_ci D_MAC80211("Error setting sleep level\n"); 51338c2ecf20Sopenharmony_ci } 51348c2ecf20Sopenharmony_ci 51358c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_POWER) { 51368c2ecf20Sopenharmony_ci D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt, 51378c2ecf20Sopenharmony_ci conf->power_level); 51388c2ecf20Sopenharmony_ci 51398c2ecf20Sopenharmony_ci il_set_tx_power(il, conf->power_level, false); 51408c2ecf20Sopenharmony_ci } 51418c2ecf20Sopenharmony_ci 51428c2ecf20Sopenharmony_ci if (!il_is_ready(il)) { 51438c2ecf20Sopenharmony_ci D_MAC80211("leave - not ready\n"); 51448c2ecf20Sopenharmony_ci goto out; 51458c2ecf20Sopenharmony_ci } 51468c2ecf20Sopenharmony_ci 51478c2ecf20Sopenharmony_ci if (scan_active) 51488c2ecf20Sopenharmony_ci goto out; 51498c2ecf20Sopenharmony_ci 51508c2ecf20Sopenharmony_ci if (memcmp(&il->active, &il->staging, sizeof(il->staging))) 51518c2ecf20Sopenharmony_ci il_commit_rxon(il); 51528c2ecf20Sopenharmony_ci else 51538c2ecf20Sopenharmony_ci D_INFO("Not re-sending same RXON configuration.\n"); 51548c2ecf20Sopenharmony_ci if (ht_changed) 51558c2ecf20Sopenharmony_ci il_update_qos(il); 51568c2ecf20Sopenharmony_ci 51578c2ecf20Sopenharmony_ciout: 51588c2ecf20Sopenharmony_ci D_MAC80211("leave ret %d\n", ret); 51598c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 51608c2ecf20Sopenharmony_ci 51618c2ecf20Sopenharmony_ci return ret; 51628c2ecf20Sopenharmony_ci} 51638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_config); 51648c2ecf20Sopenharmony_ci 51658c2ecf20Sopenharmony_civoid 51668c2ecf20Sopenharmony_ciil_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 51678c2ecf20Sopenharmony_ci{ 51688c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 51698c2ecf20Sopenharmony_ci unsigned long flags; 51708c2ecf20Sopenharmony_ci 51718c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 51728c2ecf20Sopenharmony_ci D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); 51738c2ecf20Sopenharmony_ci 51748c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 51758c2ecf20Sopenharmony_ci 51768c2ecf20Sopenharmony_ci memset(&il->current_ht_config, 0, sizeof(struct il_ht_config)); 51778c2ecf20Sopenharmony_ci 51788c2ecf20Sopenharmony_ci /* new association get rid of ibss beacon skb */ 51798c2ecf20Sopenharmony_ci dev_consume_skb_irq(il->beacon_skb); 51808c2ecf20Sopenharmony_ci il->beacon_skb = NULL; 51818c2ecf20Sopenharmony_ci il->timestamp = 0; 51828c2ecf20Sopenharmony_ci 51838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 51848c2ecf20Sopenharmony_ci 51858c2ecf20Sopenharmony_ci il_scan_cancel_timeout(il, 100); 51868c2ecf20Sopenharmony_ci if (!il_is_ready_rf(il)) { 51878c2ecf20Sopenharmony_ci D_MAC80211("leave - not ready\n"); 51888c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 51898c2ecf20Sopenharmony_ci return; 51908c2ecf20Sopenharmony_ci } 51918c2ecf20Sopenharmony_ci 51928c2ecf20Sopenharmony_ci /* we are restarting association process */ 51938c2ecf20Sopenharmony_ci il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 51948c2ecf20Sopenharmony_ci il_commit_rxon(il); 51958c2ecf20Sopenharmony_ci 51968c2ecf20Sopenharmony_ci il_set_rate(il); 51978c2ecf20Sopenharmony_ci 51988c2ecf20Sopenharmony_ci D_MAC80211("leave\n"); 51998c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 52008c2ecf20Sopenharmony_ci} 52018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_reset_tsf); 52028c2ecf20Sopenharmony_ci 52038c2ecf20Sopenharmony_cistatic void 52048c2ecf20Sopenharmony_ciil_ht_conf(struct il_priv *il, struct ieee80211_vif *vif) 52058c2ecf20Sopenharmony_ci{ 52068c2ecf20Sopenharmony_ci struct il_ht_config *ht_conf = &il->current_ht_config; 52078c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 52088c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 52098c2ecf20Sopenharmony_ci 52108c2ecf20Sopenharmony_ci D_ASSOC("enter:\n"); 52118c2ecf20Sopenharmony_ci 52128c2ecf20Sopenharmony_ci if (!il->ht.enabled) 52138c2ecf20Sopenharmony_ci return; 52148c2ecf20Sopenharmony_ci 52158c2ecf20Sopenharmony_ci il->ht.protection = 52168c2ecf20Sopenharmony_ci bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; 52178c2ecf20Sopenharmony_ci il->ht.non_gf_sta_present = 52188c2ecf20Sopenharmony_ci !!(bss_conf-> 52198c2ecf20Sopenharmony_ci ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); 52208c2ecf20Sopenharmony_ci 52218c2ecf20Sopenharmony_ci ht_conf->single_chain_sufficient = false; 52228c2ecf20Sopenharmony_ci 52238c2ecf20Sopenharmony_ci switch (vif->type) { 52248c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 52258c2ecf20Sopenharmony_ci rcu_read_lock(); 52268c2ecf20Sopenharmony_ci sta = ieee80211_find_sta(vif, bss_conf->bssid); 52278c2ecf20Sopenharmony_ci if (sta) { 52288c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 52298c2ecf20Sopenharmony_ci int maxstreams; 52308c2ecf20Sopenharmony_ci 52318c2ecf20Sopenharmony_ci maxstreams = 52328c2ecf20Sopenharmony_ci (ht_cap->mcs. 52338c2ecf20Sopenharmony_ci tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) 52348c2ecf20Sopenharmony_ci >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; 52358c2ecf20Sopenharmony_ci maxstreams += 1; 52368c2ecf20Sopenharmony_ci 52378c2ecf20Sopenharmony_ci if (ht_cap->mcs.rx_mask[1] == 0 && 52388c2ecf20Sopenharmony_ci ht_cap->mcs.rx_mask[2] == 0) 52398c2ecf20Sopenharmony_ci ht_conf->single_chain_sufficient = true; 52408c2ecf20Sopenharmony_ci if (maxstreams <= 1) 52418c2ecf20Sopenharmony_ci ht_conf->single_chain_sufficient = true; 52428c2ecf20Sopenharmony_ci } else { 52438c2ecf20Sopenharmony_ci /* 52448c2ecf20Sopenharmony_ci * If at all, this can only happen through a race 52458c2ecf20Sopenharmony_ci * when the AP disconnects us while we're still 52468c2ecf20Sopenharmony_ci * setting up the connection, in that case mac80211 52478c2ecf20Sopenharmony_ci * will soon tell us about that. 52488c2ecf20Sopenharmony_ci */ 52498c2ecf20Sopenharmony_ci ht_conf->single_chain_sufficient = true; 52508c2ecf20Sopenharmony_ci } 52518c2ecf20Sopenharmony_ci rcu_read_unlock(); 52528c2ecf20Sopenharmony_ci break; 52538c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 52548c2ecf20Sopenharmony_ci ht_conf->single_chain_sufficient = true; 52558c2ecf20Sopenharmony_ci break; 52568c2ecf20Sopenharmony_ci default: 52578c2ecf20Sopenharmony_ci break; 52588c2ecf20Sopenharmony_ci } 52598c2ecf20Sopenharmony_ci 52608c2ecf20Sopenharmony_ci D_ASSOC("leave\n"); 52618c2ecf20Sopenharmony_ci} 52628c2ecf20Sopenharmony_ci 52638c2ecf20Sopenharmony_cistatic inline void 52648c2ecf20Sopenharmony_ciil_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif) 52658c2ecf20Sopenharmony_ci{ 52668c2ecf20Sopenharmony_ci /* 52678c2ecf20Sopenharmony_ci * inform the ucode that there is no longer an 52688c2ecf20Sopenharmony_ci * association and that no more packets should be 52698c2ecf20Sopenharmony_ci * sent 52708c2ecf20Sopenharmony_ci */ 52718c2ecf20Sopenharmony_ci il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 52728c2ecf20Sopenharmony_ci il->staging.assoc_id = 0; 52738c2ecf20Sopenharmony_ci il_commit_rxon(il); 52748c2ecf20Sopenharmony_ci} 52758c2ecf20Sopenharmony_ci 52768c2ecf20Sopenharmony_cistatic void 52778c2ecf20Sopenharmony_ciil_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 52788c2ecf20Sopenharmony_ci{ 52798c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 52808c2ecf20Sopenharmony_ci unsigned long flags; 52818c2ecf20Sopenharmony_ci __le64 timestamp; 52828c2ecf20Sopenharmony_ci struct sk_buff *skb = ieee80211_beacon_get(hw, vif); 52838c2ecf20Sopenharmony_ci 52848c2ecf20Sopenharmony_ci if (!skb) 52858c2ecf20Sopenharmony_ci return; 52868c2ecf20Sopenharmony_ci 52878c2ecf20Sopenharmony_ci D_MAC80211("enter\n"); 52888c2ecf20Sopenharmony_ci 52898c2ecf20Sopenharmony_ci lockdep_assert_held(&il->mutex); 52908c2ecf20Sopenharmony_ci 52918c2ecf20Sopenharmony_ci if (!il->beacon_enabled) { 52928c2ecf20Sopenharmony_ci IL_ERR("update beacon with no beaconing enabled\n"); 52938c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 52948c2ecf20Sopenharmony_ci return; 52958c2ecf20Sopenharmony_ci } 52968c2ecf20Sopenharmony_ci 52978c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 52988c2ecf20Sopenharmony_ci dev_consume_skb_irq(il->beacon_skb); 52998c2ecf20Sopenharmony_ci il->beacon_skb = skb; 53008c2ecf20Sopenharmony_ci 53018c2ecf20Sopenharmony_ci timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; 53028c2ecf20Sopenharmony_ci il->timestamp = le64_to_cpu(timestamp); 53038c2ecf20Sopenharmony_ci 53048c2ecf20Sopenharmony_ci D_MAC80211("leave\n"); 53058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 53068c2ecf20Sopenharmony_ci 53078c2ecf20Sopenharmony_ci if (!il_is_ready_rf(il)) { 53088c2ecf20Sopenharmony_ci D_MAC80211("leave - RF not ready\n"); 53098c2ecf20Sopenharmony_ci return; 53108c2ecf20Sopenharmony_ci } 53118c2ecf20Sopenharmony_ci 53128c2ecf20Sopenharmony_ci il->ops->post_associate(il); 53138c2ecf20Sopenharmony_ci} 53148c2ecf20Sopenharmony_ci 53158c2ecf20Sopenharmony_civoid 53168c2ecf20Sopenharmony_ciil_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 53178c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *bss_conf, u32 changes) 53188c2ecf20Sopenharmony_ci{ 53198c2ecf20Sopenharmony_ci struct il_priv *il = hw->priv; 53208c2ecf20Sopenharmony_ci int ret; 53218c2ecf20Sopenharmony_ci 53228c2ecf20Sopenharmony_ci mutex_lock(&il->mutex); 53238c2ecf20Sopenharmony_ci D_MAC80211("enter: changes 0x%x\n", changes); 53248c2ecf20Sopenharmony_ci 53258c2ecf20Sopenharmony_ci if (!il_is_alive(il)) { 53268c2ecf20Sopenharmony_ci D_MAC80211("leave - not alive\n"); 53278c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 53288c2ecf20Sopenharmony_ci return; 53298c2ecf20Sopenharmony_ci } 53308c2ecf20Sopenharmony_ci 53318c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_QOS) { 53328c2ecf20Sopenharmony_ci unsigned long flags; 53338c2ecf20Sopenharmony_ci 53348c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 53358c2ecf20Sopenharmony_ci il->qos_data.qos_active = bss_conf->qos; 53368c2ecf20Sopenharmony_ci il_update_qos(il); 53378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 53388c2ecf20Sopenharmony_ci } 53398c2ecf20Sopenharmony_ci 53408c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_BEACON_ENABLED) { 53418c2ecf20Sopenharmony_ci /* FIXME: can we remove beacon_enabled ? */ 53428c2ecf20Sopenharmony_ci if (vif->bss_conf.enable_beacon) 53438c2ecf20Sopenharmony_ci il->beacon_enabled = true; 53448c2ecf20Sopenharmony_ci else 53458c2ecf20Sopenharmony_ci il->beacon_enabled = false; 53468c2ecf20Sopenharmony_ci } 53478c2ecf20Sopenharmony_ci 53488c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_BSSID) { 53498c2ecf20Sopenharmony_ci D_MAC80211("BSSID %pM\n", bss_conf->bssid); 53508c2ecf20Sopenharmony_ci 53518c2ecf20Sopenharmony_ci /* 53528c2ecf20Sopenharmony_ci * On passive channel we wait with blocked queues to see if 53538c2ecf20Sopenharmony_ci * there is traffic on that channel. If no frame will be 53548c2ecf20Sopenharmony_ci * received (what is very unlikely since scan detects AP on 53558c2ecf20Sopenharmony_ci * that channel, but theoretically possible), mac80211 associate 53568c2ecf20Sopenharmony_ci * procedure will time out and mac80211 will call us with NULL 53578c2ecf20Sopenharmony_ci * bssid. We have to unblock queues on such condition. 53588c2ecf20Sopenharmony_ci */ 53598c2ecf20Sopenharmony_ci if (is_zero_ether_addr(bss_conf->bssid)) 53608c2ecf20Sopenharmony_ci il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); 53618c2ecf20Sopenharmony_ci 53628c2ecf20Sopenharmony_ci /* 53638c2ecf20Sopenharmony_ci * If there is currently a HW scan going on in the background, 53648c2ecf20Sopenharmony_ci * then we need to cancel it, otherwise sometimes we are not 53658c2ecf20Sopenharmony_ci * able to authenticate (FIXME: why ?) 53668c2ecf20Sopenharmony_ci */ 53678c2ecf20Sopenharmony_ci if (il_scan_cancel_timeout(il, 100)) { 53688c2ecf20Sopenharmony_ci D_MAC80211("leave - scan abort failed\n"); 53698c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 53708c2ecf20Sopenharmony_ci return; 53718c2ecf20Sopenharmony_ci } 53728c2ecf20Sopenharmony_ci 53738c2ecf20Sopenharmony_ci /* mac80211 only sets assoc when in STATION mode */ 53748c2ecf20Sopenharmony_ci memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); 53758c2ecf20Sopenharmony_ci 53768c2ecf20Sopenharmony_ci /* FIXME: currently needed in a few places */ 53778c2ecf20Sopenharmony_ci memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); 53788c2ecf20Sopenharmony_ci } 53798c2ecf20Sopenharmony_ci 53808c2ecf20Sopenharmony_ci /* 53818c2ecf20Sopenharmony_ci * This needs to be after setting the BSSID in case 53828c2ecf20Sopenharmony_ci * mac80211 decides to do both changes at once because 53838c2ecf20Sopenharmony_ci * it will invoke post_associate. 53848c2ecf20Sopenharmony_ci */ 53858c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON)) 53868c2ecf20Sopenharmony_ci il_beacon_update(hw, vif); 53878c2ecf20Sopenharmony_ci 53888c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_ERP_PREAMBLE) { 53898c2ecf20Sopenharmony_ci D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); 53908c2ecf20Sopenharmony_ci if (bss_conf->use_short_preamble) 53918c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; 53928c2ecf20Sopenharmony_ci else 53938c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; 53948c2ecf20Sopenharmony_ci } 53958c2ecf20Sopenharmony_ci 53968c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_ERP_CTS_PROT) { 53978c2ecf20Sopenharmony_ci D_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot); 53988c2ecf20Sopenharmony_ci if (bss_conf->use_cts_prot && il->band != NL80211_BAND_5GHZ) 53998c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; 54008c2ecf20Sopenharmony_ci else 54018c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; 54028c2ecf20Sopenharmony_ci if (bss_conf->use_cts_prot) 54038c2ecf20Sopenharmony_ci il->staging.flags |= RXON_FLG_SELF_CTS_EN; 54048c2ecf20Sopenharmony_ci else 54058c2ecf20Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SELF_CTS_EN; 54068c2ecf20Sopenharmony_ci } 54078c2ecf20Sopenharmony_ci 54088c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_BASIC_RATES) { 54098c2ecf20Sopenharmony_ci /* XXX use this information 54108c2ecf20Sopenharmony_ci * 54118c2ecf20Sopenharmony_ci * To do that, remove code from il_set_rate() and put something 54128c2ecf20Sopenharmony_ci * like this here: 54138c2ecf20Sopenharmony_ci * 54148c2ecf20Sopenharmony_ci if (A-band) 54158c2ecf20Sopenharmony_ci il->staging.ofdm_basic_rates = 54168c2ecf20Sopenharmony_ci bss_conf->basic_rates; 54178c2ecf20Sopenharmony_ci else 54188c2ecf20Sopenharmony_ci il->staging.ofdm_basic_rates = 54198c2ecf20Sopenharmony_ci bss_conf->basic_rates >> 4; 54208c2ecf20Sopenharmony_ci il->staging.cck_basic_rates = 54218c2ecf20Sopenharmony_ci bss_conf->basic_rates & 0xF; 54228c2ecf20Sopenharmony_ci */ 54238c2ecf20Sopenharmony_ci } 54248c2ecf20Sopenharmony_ci 54258c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_HT) { 54268c2ecf20Sopenharmony_ci il_ht_conf(il, vif); 54278c2ecf20Sopenharmony_ci 54288c2ecf20Sopenharmony_ci if (il->ops->set_rxon_chain) 54298c2ecf20Sopenharmony_ci il->ops->set_rxon_chain(il); 54308c2ecf20Sopenharmony_ci } 54318c2ecf20Sopenharmony_ci 54328c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_ASSOC) { 54338c2ecf20Sopenharmony_ci D_MAC80211("ASSOC %d\n", bss_conf->assoc); 54348c2ecf20Sopenharmony_ci if (bss_conf->assoc) { 54358c2ecf20Sopenharmony_ci il->timestamp = bss_conf->sync_tsf; 54368c2ecf20Sopenharmony_ci 54378c2ecf20Sopenharmony_ci if (!il_is_rfkill(il)) 54388c2ecf20Sopenharmony_ci il->ops->post_associate(il); 54398c2ecf20Sopenharmony_ci } else 54408c2ecf20Sopenharmony_ci il_set_no_assoc(il, vif); 54418c2ecf20Sopenharmony_ci } 54428c2ecf20Sopenharmony_ci 54438c2ecf20Sopenharmony_ci if (changes && il_is_associated(il) && bss_conf->aid) { 54448c2ecf20Sopenharmony_ci D_MAC80211("Changes (%#x) while associated\n", changes); 54458c2ecf20Sopenharmony_ci ret = il_send_rxon_assoc(il); 54468c2ecf20Sopenharmony_ci if (!ret) { 54478c2ecf20Sopenharmony_ci /* Sync active_rxon with latest change. */ 54488c2ecf20Sopenharmony_ci memcpy((void *)&il->active, &il->staging, 54498c2ecf20Sopenharmony_ci sizeof(struct il_rxon_cmd)); 54508c2ecf20Sopenharmony_ci } 54518c2ecf20Sopenharmony_ci } 54528c2ecf20Sopenharmony_ci 54538c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_BEACON_ENABLED) { 54548c2ecf20Sopenharmony_ci if (vif->bss_conf.enable_beacon) { 54558c2ecf20Sopenharmony_ci memcpy(il->staging.bssid_addr, bss_conf->bssid, 54568c2ecf20Sopenharmony_ci ETH_ALEN); 54578c2ecf20Sopenharmony_ci memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); 54588c2ecf20Sopenharmony_ci il->ops->config_ap(il); 54598c2ecf20Sopenharmony_ci } else 54608c2ecf20Sopenharmony_ci il_set_no_assoc(il, vif); 54618c2ecf20Sopenharmony_ci } 54628c2ecf20Sopenharmony_ci 54638c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_IBSS) { 54648c2ecf20Sopenharmony_ci ret = il->ops->manage_ibss_station(il, vif, 54658c2ecf20Sopenharmony_ci bss_conf->ibss_joined); 54668c2ecf20Sopenharmony_ci if (ret) 54678c2ecf20Sopenharmony_ci IL_ERR("failed to %s IBSS station %pM\n", 54688c2ecf20Sopenharmony_ci bss_conf->ibss_joined ? "add" : "remove", 54698c2ecf20Sopenharmony_ci bss_conf->bssid); 54708c2ecf20Sopenharmony_ci } 54718c2ecf20Sopenharmony_ci 54728c2ecf20Sopenharmony_ci D_MAC80211("leave\n"); 54738c2ecf20Sopenharmony_ci mutex_unlock(&il->mutex); 54748c2ecf20Sopenharmony_ci} 54758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_bss_info_changed); 54768c2ecf20Sopenharmony_ci 54778c2ecf20Sopenharmony_ciirqreturn_t 54788c2ecf20Sopenharmony_ciil_isr(int irq, void *data) 54798c2ecf20Sopenharmony_ci{ 54808c2ecf20Sopenharmony_ci struct il_priv *il = data; 54818c2ecf20Sopenharmony_ci u32 inta, inta_mask; 54828c2ecf20Sopenharmony_ci u32 inta_fh; 54838c2ecf20Sopenharmony_ci unsigned long flags; 54848c2ecf20Sopenharmony_ci if (!il) 54858c2ecf20Sopenharmony_ci return IRQ_NONE; 54868c2ecf20Sopenharmony_ci 54878c2ecf20Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 54888c2ecf20Sopenharmony_ci 54898c2ecf20Sopenharmony_ci /* Disable (but don't clear!) interrupts here to avoid 54908c2ecf20Sopenharmony_ci * back-to-back ISRs and sporadic interrupts from our NIC. 54918c2ecf20Sopenharmony_ci * If we have something to service, the tasklet will re-enable ints. 54928c2ecf20Sopenharmony_ci * If we *don't* have something, we'll re-enable before leaving here. */ 54938c2ecf20Sopenharmony_ci inta_mask = _il_rd(il, CSR_INT_MASK); /* just for debug */ 54948c2ecf20Sopenharmony_ci _il_wr(il, CSR_INT_MASK, 0x00000000); 54958c2ecf20Sopenharmony_ci 54968c2ecf20Sopenharmony_ci /* Discover which interrupts are active/pending */ 54978c2ecf20Sopenharmony_ci inta = _il_rd(il, CSR_INT); 54988c2ecf20Sopenharmony_ci inta_fh = _il_rd(il, CSR_FH_INT_STATUS); 54998c2ecf20Sopenharmony_ci 55008c2ecf20Sopenharmony_ci /* Ignore interrupt if there's nothing in NIC to service. 55018c2ecf20Sopenharmony_ci * This may be due to IRQ shared with another device, 55028c2ecf20Sopenharmony_ci * or due to sporadic interrupts thrown from our NIC. */ 55038c2ecf20Sopenharmony_ci if (!inta && !inta_fh) { 55048c2ecf20Sopenharmony_ci D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n"); 55058c2ecf20Sopenharmony_ci goto none; 55068c2ecf20Sopenharmony_ci } 55078c2ecf20Sopenharmony_ci 55088c2ecf20Sopenharmony_ci if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) { 55098c2ecf20Sopenharmony_ci /* Hardware disappeared. It might have already raised 55108c2ecf20Sopenharmony_ci * an interrupt */ 55118c2ecf20Sopenharmony_ci IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta); 55128c2ecf20Sopenharmony_ci goto unplugged; 55138c2ecf20Sopenharmony_ci } 55148c2ecf20Sopenharmony_ci 55158c2ecf20Sopenharmony_ci D_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask, 55168c2ecf20Sopenharmony_ci inta_fh); 55178c2ecf20Sopenharmony_ci 55188c2ecf20Sopenharmony_ci inta &= ~CSR_INT_BIT_SCD; 55198c2ecf20Sopenharmony_ci 55208c2ecf20Sopenharmony_ci /* il_irq_tasklet() will service interrupts and re-enable them */ 55218c2ecf20Sopenharmony_ci if (likely(inta || inta_fh)) 55228c2ecf20Sopenharmony_ci tasklet_schedule(&il->irq_tasklet); 55238c2ecf20Sopenharmony_ci 55248c2ecf20Sopenharmony_ciunplugged: 55258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 55268c2ecf20Sopenharmony_ci return IRQ_HANDLED; 55278c2ecf20Sopenharmony_ci 55288c2ecf20Sopenharmony_cinone: 55298c2ecf20Sopenharmony_ci /* re-enable interrupts here since we don't have anything to service. */ 55308c2ecf20Sopenharmony_ci /* only Re-enable if disabled by irq */ 55318c2ecf20Sopenharmony_ci if (test_bit(S_INT_ENABLED, &il->status)) 55328c2ecf20Sopenharmony_ci il_enable_interrupts(il); 55338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 55348c2ecf20Sopenharmony_ci return IRQ_NONE; 55358c2ecf20Sopenharmony_ci} 55368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_isr); 55378c2ecf20Sopenharmony_ci 55388c2ecf20Sopenharmony_ci/* 55398c2ecf20Sopenharmony_ci * il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this 55408c2ecf20Sopenharmony_ci * function. 55418c2ecf20Sopenharmony_ci */ 55428c2ecf20Sopenharmony_civoid 55438c2ecf20Sopenharmony_ciil_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, 55448c2ecf20Sopenharmony_ci __le16 fc, __le32 *tx_flags) 55458c2ecf20Sopenharmony_ci{ 55468c2ecf20Sopenharmony_ci if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { 55478c2ecf20Sopenharmony_ci *tx_flags |= TX_CMD_FLG_RTS_MSK; 55488c2ecf20Sopenharmony_ci *tx_flags &= ~TX_CMD_FLG_CTS_MSK; 55498c2ecf20Sopenharmony_ci *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; 55508c2ecf20Sopenharmony_ci 55518c2ecf20Sopenharmony_ci if (!ieee80211_is_mgmt(fc)) 55528c2ecf20Sopenharmony_ci return; 55538c2ecf20Sopenharmony_ci 55548c2ecf20Sopenharmony_ci switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { 55558c2ecf20Sopenharmony_ci case cpu_to_le16(IEEE80211_STYPE_AUTH): 55568c2ecf20Sopenharmony_ci case cpu_to_le16(IEEE80211_STYPE_DEAUTH): 55578c2ecf20Sopenharmony_ci case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): 55588c2ecf20Sopenharmony_ci case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): 55598c2ecf20Sopenharmony_ci *tx_flags &= ~TX_CMD_FLG_RTS_MSK; 55608c2ecf20Sopenharmony_ci *tx_flags |= TX_CMD_FLG_CTS_MSK; 55618c2ecf20Sopenharmony_ci break; 55628c2ecf20Sopenharmony_ci } 55638c2ecf20Sopenharmony_ci } else if (info->control.rates[0]. 55648c2ecf20Sopenharmony_ci flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { 55658c2ecf20Sopenharmony_ci *tx_flags &= ~TX_CMD_FLG_RTS_MSK; 55668c2ecf20Sopenharmony_ci *tx_flags |= TX_CMD_FLG_CTS_MSK; 55678c2ecf20Sopenharmony_ci *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; 55688c2ecf20Sopenharmony_ci } 55698c2ecf20Sopenharmony_ci} 55708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_cmd_protection); 5571