162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Contact Information: 762306a36Sopenharmony_ci * Intel Linux Wireless <ilw@linux.intel.com> 862306a36Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 962306a36Sopenharmony_ci *****************************************************************************/ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/lockdep.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/skbuff.h> 2262306a36Sopenharmony_ci#include <net/mac80211.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "common.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciint 2762306a36Sopenharmony_ci_il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci const int interval = 10; /* microseconds */ 3062306a36Sopenharmony_ci int t = 0; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci do { 3362306a36Sopenharmony_ci if ((_il_rd(il, addr) & mask) == (bits & mask)) 3462306a36Sopenharmony_ci return t; 3562306a36Sopenharmony_ci udelay(interval); 3662306a36Sopenharmony_ci t += interval; 3762306a36Sopenharmony_ci } while (t < timeout); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return -ETIMEDOUT; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ciEXPORT_SYMBOL(_il_poll_bit); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_civoid 4462306a36Sopenharmony_ciil_set_bit(struct il_priv *p, u32 r, u32 m) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci unsigned long reg_flags; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci spin_lock_irqsave(&p->reg_lock, reg_flags); 4962306a36Sopenharmony_ci _il_set_bit(p, r, m); 5062306a36Sopenharmony_ci spin_unlock_irqrestore(&p->reg_lock, reg_flags); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ciEXPORT_SYMBOL(il_set_bit); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_civoid 5562306a36Sopenharmony_ciil_clear_bit(struct il_priv *p, u32 r, u32 m) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci unsigned long reg_flags; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci spin_lock_irqsave(&p->reg_lock, reg_flags); 6062306a36Sopenharmony_ci _il_clear_bit(p, r, m); 6162306a36Sopenharmony_ci spin_unlock_irqrestore(&p->reg_lock, reg_flags); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ciEXPORT_SYMBOL(il_clear_bit); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cibool 6662306a36Sopenharmony_ci_il_grab_nic_access(struct il_priv *il) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int ret; 6962306a36Sopenharmony_ci u32 val; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* this bit wakes up the NIC */ 7262306a36Sopenharmony_ci _il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * These bits say the device is running, and should keep running for 7662306a36Sopenharmony_ci * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), 7762306a36Sopenharmony_ci * but they do not indicate that embedded SRAM is restored yet; 7862306a36Sopenharmony_ci * 3945 and 4965 have volatile SRAM, and must save/restore contents 7962306a36Sopenharmony_ci * to/from host DRAM when sleeping/waking for power-saving. 8062306a36Sopenharmony_ci * Each direction takes approximately 1/4 millisecond; with this 8162306a36Sopenharmony_ci * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a 8262306a36Sopenharmony_ci * series of register accesses are expected (e.g. reading Event Log), 8362306a36Sopenharmony_ci * to keep device from sleeping. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that 8662306a36Sopenharmony_ci * SRAM is okay/restored. We don't check that here because this call 8762306a36Sopenharmony_ci * is just for hardware register access; but GP1 MAC_SLEEP check is a 8862306a36Sopenharmony_ci * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci ret = 9262306a36Sopenharmony_ci _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, 9362306a36Sopenharmony_ci (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | 9462306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); 9562306a36Sopenharmony_ci if (unlikely(ret < 0)) { 9662306a36Sopenharmony_ci val = _il_rd(il, CSR_GP_CNTRL); 9762306a36Sopenharmony_ci WARN_ONCE(1, "Timeout waiting for ucode processor access " 9862306a36Sopenharmony_ci "(CSR_GP_CNTRL 0x%08x)\n", val); 9962306a36Sopenharmony_ci _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); 10062306a36Sopenharmony_ci return false; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return true; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(_il_grab_nic_access); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ciint 10862306a36Sopenharmony_ciil_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci const int interval = 10; /* microseconds */ 11162306a36Sopenharmony_ci int t = 0; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci do { 11462306a36Sopenharmony_ci if ((il_rd(il, addr) & mask) == mask) 11562306a36Sopenharmony_ci return t; 11662306a36Sopenharmony_ci udelay(interval); 11762306a36Sopenharmony_ci t += interval; 11862306a36Sopenharmony_ci } while (t < timeout); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return -ETIMEDOUT; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ciEXPORT_SYMBOL(il_poll_bit); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciu32 12562306a36Sopenharmony_ciil_rd_prph(struct il_priv *il, u32 reg) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci unsigned long reg_flags; 12862306a36Sopenharmony_ci u32 val; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, reg_flags); 13162306a36Sopenharmony_ci _il_grab_nic_access(il); 13262306a36Sopenharmony_ci val = _il_rd_prph(il, reg); 13362306a36Sopenharmony_ci _il_release_nic_access(il); 13462306a36Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, reg_flags); 13562306a36Sopenharmony_ci return val; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ciEXPORT_SYMBOL(il_rd_prph); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_civoid 14062306a36Sopenharmony_ciil_wr_prph(struct il_priv *il, u32 addr, u32 val) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci unsigned long reg_flags; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, reg_flags); 14562306a36Sopenharmony_ci if (likely(_il_grab_nic_access(il))) { 14662306a36Sopenharmony_ci _il_wr_prph(il, addr, val); 14762306a36Sopenharmony_ci _il_release_nic_access(il); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, reg_flags); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ciEXPORT_SYMBOL(il_wr_prph); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciu32 15462306a36Sopenharmony_ciil_read_targ_mem(struct il_priv *il, u32 addr) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci unsigned long reg_flags; 15762306a36Sopenharmony_ci u32 value; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, reg_flags); 16062306a36Sopenharmony_ci _il_grab_nic_access(il); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci _il_wr(il, HBUS_TARG_MEM_RADDR, addr); 16362306a36Sopenharmony_ci value = _il_rd(il, HBUS_TARG_MEM_RDAT); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci _il_release_nic_access(il); 16662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, reg_flags); 16762306a36Sopenharmony_ci return value; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ciEXPORT_SYMBOL(il_read_targ_mem); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_civoid 17262306a36Sopenharmony_ciil_write_targ_mem(struct il_priv *il, u32 addr, u32 val) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci unsigned long reg_flags; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, reg_flags); 17762306a36Sopenharmony_ci if (likely(_il_grab_nic_access(il))) { 17862306a36Sopenharmony_ci _il_wr(il, HBUS_TARG_MEM_WADDR, addr); 17962306a36Sopenharmony_ci _il_wr(il, HBUS_TARG_MEM_WDAT, val); 18062306a36Sopenharmony_ci _il_release_nic_access(il); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, reg_flags); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL(il_write_targ_mem); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciconst char * 18762306a36Sopenharmony_ciil_get_cmd_string(u8 cmd) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci switch (cmd) { 19062306a36Sopenharmony_ci IL_CMD(N_ALIVE); 19162306a36Sopenharmony_ci IL_CMD(N_ERROR); 19262306a36Sopenharmony_ci IL_CMD(C_RXON); 19362306a36Sopenharmony_ci IL_CMD(C_RXON_ASSOC); 19462306a36Sopenharmony_ci IL_CMD(C_QOS_PARAM); 19562306a36Sopenharmony_ci IL_CMD(C_RXON_TIMING); 19662306a36Sopenharmony_ci IL_CMD(C_ADD_STA); 19762306a36Sopenharmony_ci IL_CMD(C_REM_STA); 19862306a36Sopenharmony_ci IL_CMD(C_WEPKEY); 19962306a36Sopenharmony_ci IL_CMD(N_3945_RX); 20062306a36Sopenharmony_ci IL_CMD(C_TX); 20162306a36Sopenharmony_ci IL_CMD(C_RATE_SCALE); 20262306a36Sopenharmony_ci IL_CMD(C_LEDS); 20362306a36Sopenharmony_ci IL_CMD(C_TX_LINK_QUALITY_CMD); 20462306a36Sopenharmony_ci IL_CMD(C_CHANNEL_SWITCH); 20562306a36Sopenharmony_ci IL_CMD(N_CHANNEL_SWITCH); 20662306a36Sopenharmony_ci IL_CMD(C_SPECTRUM_MEASUREMENT); 20762306a36Sopenharmony_ci IL_CMD(N_SPECTRUM_MEASUREMENT); 20862306a36Sopenharmony_ci IL_CMD(C_POWER_TBL); 20962306a36Sopenharmony_ci IL_CMD(N_PM_SLEEP); 21062306a36Sopenharmony_ci IL_CMD(N_PM_DEBUG_STATS); 21162306a36Sopenharmony_ci IL_CMD(C_SCAN); 21262306a36Sopenharmony_ci IL_CMD(C_SCAN_ABORT); 21362306a36Sopenharmony_ci IL_CMD(N_SCAN_START); 21462306a36Sopenharmony_ci IL_CMD(N_SCAN_RESULTS); 21562306a36Sopenharmony_ci IL_CMD(N_SCAN_COMPLETE); 21662306a36Sopenharmony_ci IL_CMD(N_BEACON); 21762306a36Sopenharmony_ci IL_CMD(C_TX_BEACON); 21862306a36Sopenharmony_ci IL_CMD(C_TX_PWR_TBL); 21962306a36Sopenharmony_ci IL_CMD(C_BT_CONFIG); 22062306a36Sopenharmony_ci IL_CMD(C_STATS); 22162306a36Sopenharmony_ci IL_CMD(N_STATS); 22262306a36Sopenharmony_ci IL_CMD(N_CARD_STATE); 22362306a36Sopenharmony_ci IL_CMD(N_MISSED_BEACONS); 22462306a36Sopenharmony_ci IL_CMD(C_CT_KILL_CONFIG); 22562306a36Sopenharmony_ci IL_CMD(C_SENSITIVITY); 22662306a36Sopenharmony_ci IL_CMD(C_PHY_CALIBRATION); 22762306a36Sopenharmony_ci IL_CMD(N_RX_PHY); 22862306a36Sopenharmony_ci IL_CMD(N_RX_MPDU); 22962306a36Sopenharmony_ci IL_CMD(N_RX); 23062306a36Sopenharmony_ci IL_CMD(N_COMPRESSED_BA); 23162306a36Sopenharmony_ci default: 23262306a36Sopenharmony_ci return "UNKNOWN"; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ciEXPORT_SYMBOL(il_get_cmd_string); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci#define HOST_COMPLETE_TIMEOUT (HZ / 2) 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic void 24162306a36Sopenharmony_ciil_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd, 24262306a36Sopenharmony_ci struct il_rx_pkt *pkt) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { 24562306a36Sopenharmony_ci IL_ERR("Bad return from %s (0x%08X)\n", 24662306a36Sopenharmony_ci il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); 24762306a36Sopenharmony_ci return; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 25062306a36Sopenharmony_ci switch (cmd->hdr.cmd) { 25162306a36Sopenharmony_ci case C_TX_LINK_QUALITY_CMD: 25262306a36Sopenharmony_ci case C_SENSITIVITY: 25362306a36Sopenharmony_ci D_HC_DUMP("back from %s (0x%08X)\n", 25462306a36Sopenharmony_ci il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci default: 25762306a36Sopenharmony_ci D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd), 25862306a36Sopenharmony_ci pkt->hdr.flags); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci#endif 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int 26462306a36Sopenharmony_ciil_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci int ret; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci BUG_ON(!(cmd->flags & CMD_ASYNC)); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* An asynchronous command can not expect an SKB to be set. */ 27162306a36Sopenharmony_ci BUG_ON(cmd->flags & CMD_WANT_SKB); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Assign a generic callback if one is not provided */ 27462306a36Sopenharmony_ci if (!cmd->callback) 27562306a36Sopenharmony_ci cmd->callback = il_generic_cmd_callback; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 27862306a36Sopenharmony_ci return -EBUSY; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci ret = il_enqueue_hcmd(il, cmd); 28162306a36Sopenharmony_ci if (ret < 0) { 28262306a36Sopenharmony_ci IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", 28362306a36Sopenharmony_ci il_get_cmd_string(cmd->id), ret); 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciint 29062306a36Sopenharmony_ciil_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci int cmd_idx; 29362306a36Sopenharmony_ci int ret; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci BUG_ON(cmd->flags & CMD_ASYNC); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* A synchronous command can not have a callback set. */ 30062306a36Sopenharmony_ci BUG_ON(cmd->callback); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci D_INFO("Attempting to send sync command %s\n", 30362306a36Sopenharmony_ci il_get_cmd_string(cmd->id)); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci set_bit(S_HCMD_ACTIVE, &il->status); 30662306a36Sopenharmony_ci D_INFO("Setting HCMD_ACTIVE for command %s\n", 30762306a36Sopenharmony_ci il_get_cmd_string(cmd->id)); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci cmd_idx = il_enqueue_hcmd(il, cmd); 31062306a36Sopenharmony_ci if (cmd_idx < 0) { 31162306a36Sopenharmony_ci ret = cmd_idx; 31262306a36Sopenharmony_ci IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", 31362306a36Sopenharmony_ci il_get_cmd_string(cmd->id), ret); 31462306a36Sopenharmony_ci goto out; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ret = wait_event_timeout(il->wait_command_queue, 31862306a36Sopenharmony_ci !test_bit(S_HCMD_ACTIVE, &il->status), 31962306a36Sopenharmony_ci HOST_COMPLETE_TIMEOUT); 32062306a36Sopenharmony_ci if (!ret) { 32162306a36Sopenharmony_ci if (test_bit(S_HCMD_ACTIVE, &il->status)) { 32262306a36Sopenharmony_ci IL_ERR("Error sending %s: time out after %dms.\n", 32362306a36Sopenharmony_ci il_get_cmd_string(cmd->id), 32462306a36Sopenharmony_ci jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci clear_bit(S_HCMD_ACTIVE, &il->status); 32762306a36Sopenharmony_ci D_INFO("Clearing HCMD_ACTIVE for command %s\n", 32862306a36Sopenharmony_ci il_get_cmd_string(cmd->id)); 32962306a36Sopenharmony_ci ret = -ETIMEDOUT; 33062306a36Sopenharmony_ci goto cancel; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (test_bit(S_RFKILL, &il->status)) { 33562306a36Sopenharmony_ci IL_ERR("Command %s aborted: RF KILL Switch\n", 33662306a36Sopenharmony_ci il_get_cmd_string(cmd->id)); 33762306a36Sopenharmony_ci ret = -ECANCELED; 33862306a36Sopenharmony_ci goto fail; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci if (test_bit(S_FW_ERROR, &il->status)) { 34162306a36Sopenharmony_ci IL_ERR("Command %s failed: FW Error\n", 34262306a36Sopenharmony_ci il_get_cmd_string(cmd->id)); 34362306a36Sopenharmony_ci ret = -EIO; 34462306a36Sopenharmony_ci goto fail; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { 34762306a36Sopenharmony_ci IL_ERR("Error: Response NULL in '%s'\n", 34862306a36Sopenharmony_ci il_get_cmd_string(cmd->id)); 34962306a36Sopenharmony_ci ret = -EIO; 35062306a36Sopenharmony_ci goto cancel; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = 0; 35462306a36Sopenharmony_ci goto out; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cicancel: 35762306a36Sopenharmony_ci if (cmd->flags & CMD_WANT_SKB) { 35862306a36Sopenharmony_ci /* 35962306a36Sopenharmony_ci * Cancel the CMD_WANT_SKB flag for the cmd in the 36062306a36Sopenharmony_ci * TX cmd queue. Otherwise in case the cmd comes 36162306a36Sopenharmony_ci * in later, it will possibly set an invalid 36262306a36Sopenharmony_ci * address (cmd->meta.source). 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_cifail: 36762306a36Sopenharmony_ci if (cmd->reply_page) { 36862306a36Sopenharmony_ci il_free_pages(il, cmd->reply_page); 36962306a36Sopenharmony_ci cmd->reply_page = 0; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ciout: 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_sync); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciint 37762306a36Sopenharmony_ciil_send_cmd(struct il_priv *il, struct il_host_cmd *cmd) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci if (cmd->flags & CMD_ASYNC) 38062306a36Sopenharmony_ci return il_send_cmd_async(il, cmd); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return il_send_cmd_sync(il, cmd); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ciint 38762306a36Sopenharmony_ciil_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct il_host_cmd cmd = { 39062306a36Sopenharmony_ci .id = id, 39162306a36Sopenharmony_ci .len = len, 39262306a36Sopenharmony_ci .data = data, 39362306a36Sopenharmony_ci }; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return il_send_cmd_sync(il, &cmd); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_pdu); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ciint 40062306a36Sopenharmony_ciil_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data, 40162306a36Sopenharmony_ci void (*callback) (struct il_priv *il, 40262306a36Sopenharmony_ci struct il_device_cmd *cmd, 40362306a36Sopenharmony_ci struct il_rx_pkt *pkt)) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct il_host_cmd cmd = { 40662306a36Sopenharmony_ci .id = id, 40762306a36Sopenharmony_ci .len = len, 40862306a36Sopenharmony_ci .data = data, 40962306a36Sopenharmony_ci }; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci cmd.flags |= CMD_ASYNC; 41262306a36Sopenharmony_ci cmd.callback = callback; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return il_send_cmd_async(il, &cmd); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_pdu_async); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* default: IL_LED_BLINK(0) using blinking idx table */ 41962306a36Sopenharmony_cistatic int led_mode; 42062306a36Sopenharmony_cimodule_param(led_mode, int, 0444); 42162306a36Sopenharmony_ciMODULE_PARM_DESC(led_mode, 42262306a36Sopenharmony_ci "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking"); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/* Throughput OFF time(ms) ON time (ms) 42562306a36Sopenharmony_ci * >300 25 25 42662306a36Sopenharmony_ci * >200 to 300 40 40 42762306a36Sopenharmony_ci * >100 to 200 55 55 42862306a36Sopenharmony_ci * >70 to 100 65 65 42962306a36Sopenharmony_ci * >50 to 70 75 75 43062306a36Sopenharmony_ci * >20 to 50 85 85 43162306a36Sopenharmony_ci * >10 to 20 95 95 43262306a36Sopenharmony_ci * >5 to 10 110 110 43362306a36Sopenharmony_ci * >1 to 5 130 130 43462306a36Sopenharmony_ci * >0 to 1 167 167 43562306a36Sopenharmony_ci * <=0 SOLID ON 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_cistatic const struct ieee80211_tpt_blink il_blink[] = { 43862306a36Sopenharmony_ci {.throughput = 0, .blink_time = 334}, 43962306a36Sopenharmony_ci {.throughput = 1 * 1024 - 1, .blink_time = 260}, 44062306a36Sopenharmony_ci {.throughput = 5 * 1024 - 1, .blink_time = 220}, 44162306a36Sopenharmony_ci {.throughput = 10 * 1024 - 1, .blink_time = 190}, 44262306a36Sopenharmony_ci {.throughput = 20 * 1024 - 1, .blink_time = 170}, 44362306a36Sopenharmony_ci {.throughput = 50 * 1024 - 1, .blink_time = 150}, 44462306a36Sopenharmony_ci {.throughput = 70 * 1024 - 1, .blink_time = 130}, 44562306a36Sopenharmony_ci {.throughput = 100 * 1024 - 1, .blink_time = 110}, 44662306a36Sopenharmony_ci {.throughput = 200 * 1024 - 1, .blink_time = 80}, 44762306a36Sopenharmony_ci {.throughput = 300 * 1024 - 1, .blink_time = 50}, 44862306a36Sopenharmony_ci}; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* 45162306a36Sopenharmony_ci * Adjust led blink rate to compensate on a MAC Clock difference on every HW 45262306a36Sopenharmony_ci * Led blink rate analysis showed an average deviation of 0% on 3945, 45362306a36Sopenharmony_ci * 5% on 4965 HW. 45462306a36Sopenharmony_ci * Need to compensate on the led on/off time per HW according to the deviation 45562306a36Sopenharmony_ci * to achieve the desired led frequency 45662306a36Sopenharmony_ci * The calculation is: (100-averageDeviation)/100 * blinkTime 45762306a36Sopenharmony_ci * For code efficiency the calculation will be: 45862306a36Sopenharmony_ci * compensation = (100 - averageDeviation) * 64 / 100 45962306a36Sopenharmony_ci * NewBlinkTime = (compensation * BlinkTime) / 64 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_cistatic inline u8 46262306a36Sopenharmony_ciil_blink_compensation(struct il_priv *il, u8 time, u16 compensation) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci if (!compensation) { 46562306a36Sopenharmony_ci IL_ERR("undefined blink compensation: " 46662306a36Sopenharmony_ci "use pre-defined blinking time\n"); 46762306a36Sopenharmony_ci return time; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return (u8) ((time * compensation) >> 6); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci/* Set led pattern command */ 47462306a36Sopenharmony_cistatic int 47562306a36Sopenharmony_ciil_led_cmd(struct il_priv *il, unsigned long on, unsigned long off) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct il_led_cmd led_cmd = { 47862306a36Sopenharmony_ci .id = IL_LED_LINK, 47962306a36Sopenharmony_ci .interval = IL_DEF_LED_INTRVL 48062306a36Sopenharmony_ci }; 48162306a36Sopenharmony_ci int ret; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (!test_bit(S_READY, &il->status)) 48462306a36Sopenharmony_ci return -EBUSY; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (il->blink_on == on && il->blink_off == off) 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (off == 0) { 49062306a36Sopenharmony_ci /* led is SOLID_ON */ 49162306a36Sopenharmony_ci on = IL_LED_SOLID; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci D_LED("Led blink time compensation=%u\n", 49562306a36Sopenharmony_ci il->cfg->led_compensation); 49662306a36Sopenharmony_ci led_cmd.on = 49762306a36Sopenharmony_ci il_blink_compensation(il, on, 49862306a36Sopenharmony_ci il->cfg->led_compensation); 49962306a36Sopenharmony_ci led_cmd.off = 50062306a36Sopenharmony_ci il_blink_compensation(il, off, 50162306a36Sopenharmony_ci il->cfg->led_compensation); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ret = il->ops->send_led_cmd(il, &led_cmd); 50462306a36Sopenharmony_ci if (!ret) { 50562306a36Sopenharmony_ci il->blink_on = on; 50662306a36Sopenharmony_ci il->blink_off = off; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic void 51262306a36Sopenharmony_ciil_led_brightness_set(struct led_classdev *led_cdev, 51362306a36Sopenharmony_ci enum led_brightness brightness) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct il_priv *il = container_of(led_cdev, struct il_priv, led); 51662306a36Sopenharmony_ci unsigned long on = 0; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (brightness > 0) 51962306a36Sopenharmony_ci on = IL_LED_SOLID; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci il_led_cmd(il, on, 0); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int 52562306a36Sopenharmony_ciil_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, 52662306a36Sopenharmony_ci unsigned long *delay_off) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct il_priv *il = container_of(led_cdev, struct il_priv, led); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return il_led_cmd(il, *delay_on, *delay_off); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_civoid 53462306a36Sopenharmony_ciil_leds_init(struct il_priv *il) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci int mode = led_mode; 53762306a36Sopenharmony_ci int ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (mode == IL_LED_DEFAULT) 54062306a36Sopenharmony_ci mode = il->cfg->led_mode; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci il->led.name = 54362306a36Sopenharmony_ci kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy)); 54462306a36Sopenharmony_ci il->led.brightness_set = il_led_brightness_set; 54562306a36Sopenharmony_ci il->led.blink_set = il_led_blink_set; 54662306a36Sopenharmony_ci il->led.max_brightness = 1; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (mode) { 54962306a36Sopenharmony_ci case IL_LED_DEFAULT: 55062306a36Sopenharmony_ci WARN_ON(1); 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci case IL_LED_BLINK: 55362306a36Sopenharmony_ci il->led.default_trigger = 55462306a36Sopenharmony_ci ieee80211_create_tpt_led_trigger(il->hw, 55562306a36Sopenharmony_ci IEEE80211_TPT_LEDTRIG_FL_CONNECTED, 55662306a36Sopenharmony_ci il_blink, 55762306a36Sopenharmony_ci ARRAY_SIZE(il_blink)); 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci case IL_LED_RF_STATE: 56062306a36Sopenharmony_ci il->led.default_trigger = ieee80211_get_radio_led_name(il->hw); 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci ret = led_classdev_register(&il->pci_dev->dev, &il->led); 56562306a36Sopenharmony_ci if (ret) { 56662306a36Sopenharmony_ci kfree(il->led.name); 56762306a36Sopenharmony_ci return; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci il->led_registered = true; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ciEXPORT_SYMBOL(il_leds_init); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_civoid 57562306a36Sopenharmony_ciil_leds_exit(struct il_priv *il) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci if (!il->led_registered) 57862306a36Sopenharmony_ci return; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci led_classdev_unregister(&il->led); 58162306a36Sopenharmony_ci kfree(il->led.name); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ciEXPORT_SYMBOL(il_leds_exit); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/************************** EEPROM BANDS **************************** 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * The il_eeprom_band definitions below provide the mapping from the 58862306a36Sopenharmony_ci * EEPROM contents to the specific channel number supported for each 58962306a36Sopenharmony_ci * band. 59062306a36Sopenharmony_ci * 59162306a36Sopenharmony_ci * For example, il_priv->eeprom.band_3_channels[4] from the band_3 59262306a36Sopenharmony_ci * definition below maps to physical channel 42 in the 5.2GHz spectrum. 59362306a36Sopenharmony_ci * The specific geography and calibration information for that channel 59462306a36Sopenharmony_ci * is contained in the eeprom map itself. 59562306a36Sopenharmony_ci * 59662306a36Sopenharmony_ci * During init, we copy the eeprom information and channel map 59762306a36Sopenharmony_ci * information into il->channel_info_24/52 and il->channel_map_24/52 59862306a36Sopenharmony_ci * 59962306a36Sopenharmony_ci * channel_map_24/52 provides the idx in the channel_info array for a 60062306a36Sopenharmony_ci * given channel. We have to have two separate maps as there is channel 60162306a36Sopenharmony_ci * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and 60262306a36Sopenharmony_ci * band_2 60362306a36Sopenharmony_ci * 60462306a36Sopenharmony_ci * A value of 0xff stored in the channel_map indicates that the channel 60562306a36Sopenharmony_ci * is not supported by the hardware at all. 60662306a36Sopenharmony_ci * 60762306a36Sopenharmony_ci * A value of 0xfe in the channel_map indicates that the channel is not 60862306a36Sopenharmony_ci * valid for Tx with the current hardware. This means that 60962306a36Sopenharmony_ci * while the system can tune and receive on a given channel, it may not 61062306a36Sopenharmony_ci * be able to associate or transmit any frames on that 61162306a36Sopenharmony_ci * channel. There is no corresponding channel information for that 61262306a36Sopenharmony_ci * entry. 61362306a36Sopenharmony_ci * 61462306a36Sopenharmony_ci *********************************************************************/ 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/* 2.4 GHz */ 61762306a36Sopenharmony_ciconst u8 il_eeprom_band_1[14] = { 61862306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 61962306a36Sopenharmony_ci}; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/* 5.2 GHz bands */ 62262306a36Sopenharmony_cistatic const u8 il_eeprom_band_2[] = { /* 4915-5080MHz */ 62362306a36Sopenharmony_ci 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 62462306a36Sopenharmony_ci}; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic const u8 il_eeprom_band_3[] = { /* 5170-5320MHz */ 62762306a36Sopenharmony_ci 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 62862306a36Sopenharmony_ci}; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic const u8 il_eeprom_band_4[] = { /* 5500-5700MHz */ 63162306a36Sopenharmony_ci 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 63262306a36Sopenharmony_ci}; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic const u8 il_eeprom_band_5[] = { /* 5725-5825MHz */ 63562306a36Sopenharmony_ci 145, 149, 153, 157, 161, 165 63662306a36Sopenharmony_ci}; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic const u8 il_eeprom_band_6[] = { /* 2.4 ht40 channel */ 63962306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7 64062306a36Sopenharmony_ci}; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic const u8 il_eeprom_band_7[] = { /* 5.2 ht40 channel */ 64362306a36Sopenharmony_ci 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 64462306a36Sopenharmony_ci}; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci/****************************************************************************** 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * EEPROM related functions 64962306a36Sopenharmony_ci * 65062306a36Sopenharmony_ci******************************************************************************/ 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int 65362306a36Sopenharmony_ciil_eeprom_verify_signature(struct il_priv *il) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; 65662306a36Sopenharmony_ci int ret = 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci D_EEPROM("EEPROM signature=0x%08x\n", gp); 65962306a36Sopenharmony_ci switch (gp) { 66062306a36Sopenharmony_ci case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: 66162306a36Sopenharmony_ci case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci default: 66462306a36Sopenharmony_ci IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp); 66562306a36Sopenharmony_ci ret = -ENOENT; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci return ret; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ciconst u8 * 67262306a36Sopenharmony_ciil_eeprom_query_addr(const struct il_priv *il, size_t offset) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci BUG_ON(offset >= il->cfg->eeprom_size); 67562306a36Sopenharmony_ci return &il->eeprom[offset]; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_query_addr); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ciu16 68062306a36Sopenharmony_ciil_eeprom_query16(const struct il_priv *il, size_t offset) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci if (!il->eeprom) 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_query16); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/* 68962306a36Sopenharmony_ci * il_eeprom_init - read EEPROM contents 69062306a36Sopenharmony_ci * 69162306a36Sopenharmony_ci * Load the EEPROM contents from adapter into il->eeprom 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * NOTE: This routine uses the non-debug IO access functions. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ciint 69662306a36Sopenharmony_ciil_eeprom_init(struct il_priv *il) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci __le16 *e; 69962306a36Sopenharmony_ci u32 gp = _il_rd(il, CSR_EEPROM_GP); 70062306a36Sopenharmony_ci int sz; 70162306a36Sopenharmony_ci int ret; 70262306a36Sopenharmony_ci int addr; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* allocate eeprom */ 70562306a36Sopenharmony_ci sz = il->cfg->eeprom_size; 70662306a36Sopenharmony_ci D_EEPROM("NVM size = %d\n", sz); 70762306a36Sopenharmony_ci il->eeprom = kzalloc(sz, GFP_KERNEL); 70862306a36Sopenharmony_ci if (!il->eeprom) 70962306a36Sopenharmony_ci return -ENOMEM; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci e = (__le16 *) il->eeprom; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci il->ops->apm_init(il); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci ret = il_eeprom_verify_signature(il); 71662306a36Sopenharmony_ci if (ret < 0) { 71762306a36Sopenharmony_ci IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp); 71862306a36Sopenharmony_ci ret = -ENOENT; 71962306a36Sopenharmony_ci goto err; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Make sure driver (instead of uCode) is allowed to read EEPROM */ 72362306a36Sopenharmony_ci ret = il->ops->eeprom_acquire_semaphore(il); 72462306a36Sopenharmony_ci if (ret < 0) { 72562306a36Sopenharmony_ci IL_ERR("Failed to acquire EEPROM semaphore.\n"); 72662306a36Sopenharmony_ci ret = -ENOENT; 72762306a36Sopenharmony_ci goto err; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* eeprom is an array of 16bit values */ 73162306a36Sopenharmony_ci for (addr = 0; addr < sz; addr += sizeof(u16)) { 73262306a36Sopenharmony_ci u32 r; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci _il_wr(il, CSR_EEPROM_REG, 73562306a36Sopenharmony_ci CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ret = 73862306a36Sopenharmony_ci _il_poll_bit(il, CSR_EEPROM_REG, 73962306a36Sopenharmony_ci CSR_EEPROM_REG_READ_VALID_MSK, 74062306a36Sopenharmony_ci CSR_EEPROM_REG_READ_VALID_MSK, 74162306a36Sopenharmony_ci IL_EEPROM_ACCESS_TIMEOUT); 74262306a36Sopenharmony_ci if (ret < 0) { 74362306a36Sopenharmony_ci IL_ERR("Time out reading EEPROM[%d]\n", addr); 74462306a36Sopenharmony_ci goto done; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci r = _il_rd(il, CSR_EEPROM_REG); 74762306a36Sopenharmony_ci e[addr / 2] = cpu_to_le16(r >> 16); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci D_EEPROM("NVM Type: %s, version: 0x%x\n", "EEPROM", 75162306a36Sopenharmony_ci il_eeprom_query16(il, EEPROM_VERSION)); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci ret = 0; 75462306a36Sopenharmony_cidone: 75562306a36Sopenharmony_ci il->ops->eeprom_release_semaphore(il); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cierr: 75862306a36Sopenharmony_ci if (ret) 75962306a36Sopenharmony_ci il_eeprom_free(il); 76062306a36Sopenharmony_ci /* Reset chip to save power until we load uCode during "up". */ 76162306a36Sopenharmony_ci il_apm_stop(il); 76262306a36Sopenharmony_ci return ret; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_init); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_civoid 76762306a36Sopenharmony_ciil_eeprom_free(struct il_priv *il) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci kfree(il->eeprom); 77062306a36Sopenharmony_ci il->eeprom = NULL; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_free); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic void 77562306a36Sopenharmony_ciil_init_band_reference(const struct il_priv *il, int eep_band, 77662306a36Sopenharmony_ci int *eeprom_ch_count, 77762306a36Sopenharmony_ci const struct il_eeprom_channel **eeprom_ch_info, 77862306a36Sopenharmony_ci const u8 **eeprom_ch_idx) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci u32 offset = il->cfg->regulatory_bands[eep_band - 1]; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci switch (eep_band) { 78362306a36Sopenharmony_ci case 1: /* 2.4GHz band */ 78462306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1); 78562306a36Sopenharmony_ci *eeprom_ch_info = 78662306a36Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 78762306a36Sopenharmony_ci offset); 78862306a36Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_1; 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci case 2: /* 4.9GHz band */ 79162306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2); 79262306a36Sopenharmony_ci *eeprom_ch_info = 79362306a36Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 79462306a36Sopenharmony_ci offset); 79562306a36Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_2; 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci case 3: /* 5.2GHz band */ 79862306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3); 79962306a36Sopenharmony_ci *eeprom_ch_info = 80062306a36Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 80162306a36Sopenharmony_ci offset); 80262306a36Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_3; 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci case 4: /* 5.5GHz band */ 80562306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4); 80662306a36Sopenharmony_ci *eeprom_ch_info = 80762306a36Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 80862306a36Sopenharmony_ci offset); 80962306a36Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_4; 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci case 5: /* 5.7GHz band */ 81262306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5); 81362306a36Sopenharmony_ci *eeprom_ch_info = 81462306a36Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 81562306a36Sopenharmony_ci offset); 81662306a36Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_5; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case 6: /* 2.4GHz ht40 channels */ 81962306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6); 82062306a36Sopenharmony_ci *eeprom_ch_info = 82162306a36Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 82262306a36Sopenharmony_ci offset); 82362306a36Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_6; 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci case 7: /* 5 GHz ht40 channels */ 82662306a36Sopenharmony_ci *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7); 82762306a36Sopenharmony_ci *eeprom_ch_info = 82862306a36Sopenharmony_ci (struct il_eeprom_channel *)il_eeprom_query_addr(il, 82962306a36Sopenharmony_ci offset); 83062306a36Sopenharmony_ci *eeprom_ch_idx = il_eeprom_band_7; 83162306a36Sopenharmony_ci break; 83262306a36Sopenharmony_ci default: 83362306a36Sopenharmony_ci BUG(); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ 83862306a36Sopenharmony_ci ? # x " " : "") 83962306a36Sopenharmony_ci/* 84062306a36Sopenharmony_ci * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il. 84162306a36Sopenharmony_ci * 84262306a36Sopenharmony_ci * Does not set up a command, or touch hardware. 84362306a36Sopenharmony_ci */ 84462306a36Sopenharmony_cistatic int 84562306a36Sopenharmony_ciil_mod_ht40_chan_info(struct il_priv *il, enum nl80211_band band, u16 channel, 84662306a36Sopenharmony_ci const struct il_eeprom_channel *eeprom_ch, 84762306a36Sopenharmony_ci u8 clear_ht40_extension_channel) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct il_channel_info *ch_info; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci ch_info = 85262306a36Sopenharmony_ci (struct il_channel_info *)il_get_channel_info(il, band, channel); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (!il_is_channel_valid(ch_info)) 85562306a36Sopenharmony_ci return -1; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" 85862306a36Sopenharmony_ci " Ad-Hoc %ssupported\n", ch_info->channel, 85962306a36Sopenharmony_ci il_is_channel_a_band(ch_info) ? "5.2" : "2.4", 86062306a36Sopenharmony_ci CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE), 86162306a36Sopenharmony_ci CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE), 86262306a36Sopenharmony_ci CHECK_AND_PRINT(DFS), eeprom_ch->flags, 86362306a36Sopenharmony_ci eeprom_ch->max_power_avg, 86462306a36Sopenharmony_ci ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && 86562306a36Sopenharmony_ci !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci ch_info->ht40_eeprom = *eeprom_ch; 86862306a36Sopenharmony_ci ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; 86962306a36Sopenharmony_ci ch_info->ht40_flags = eeprom_ch->flags; 87062306a36Sopenharmony_ci if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) 87162306a36Sopenharmony_ci ch_info->ht40_extension_channel &= 87262306a36Sopenharmony_ci ~clear_ht40_extension_channel; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci return 0; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ 87862306a36Sopenharmony_ci ? # x " " : "") 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci/* 88162306a36Sopenharmony_ci * il_init_channel_map - Set up driver's info for all possible channels 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ciint 88462306a36Sopenharmony_ciil_init_channel_map(struct il_priv *il) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci int eeprom_ch_count = 0; 88762306a36Sopenharmony_ci const u8 *eeprom_ch_idx = NULL; 88862306a36Sopenharmony_ci const struct il_eeprom_channel *eeprom_ch_info = NULL; 88962306a36Sopenharmony_ci int band, ch; 89062306a36Sopenharmony_ci struct il_channel_info *ch_info; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (il->channel_count) { 89362306a36Sopenharmony_ci D_EEPROM("Channel map already initialized.\n"); 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci D_EEPROM("Initializing regulatory info from EEPROM\n"); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci il->channel_count = 90062306a36Sopenharmony_ci ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) + 90162306a36Sopenharmony_ci ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) + 90262306a36Sopenharmony_ci ARRAY_SIZE(il_eeprom_band_5); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci D_EEPROM("Parsing data for %d channels.\n", il->channel_count); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci il->channel_info = 90762306a36Sopenharmony_ci kcalloc(il->channel_count, sizeof(struct il_channel_info), 90862306a36Sopenharmony_ci GFP_KERNEL); 90962306a36Sopenharmony_ci if (!il->channel_info) { 91062306a36Sopenharmony_ci IL_ERR("Could not allocate channel_info\n"); 91162306a36Sopenharmony_ci il->channel_count = 0; 91262306a36Sopenharmony_ci return -ENOMEM; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci ch_info = il->channel_info; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* Loop through the 5 EEPROM bands adding them in order to the 91862306a36Sopenharmony_ci * channel map we maintain (that contains additional information than 91962306a36Sopenharmony_ci * what just in the EEPROM) */ 92062306a36Sopenharmony_ci for (band = 1; band <= 5; band++) { 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci il_init_band_reference(il, band, &eeprom_ch_count, 92362306a36Sopenharmony_ci &eeprom_ch_info, &eeprom_ch_idx); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* Loop through each band adding each of the channels */ 92662306a36Sopenharmony_ci for (ch = 0; ch < eeprom_ch_count; ch++) { 92762306a36Sopenharmony_ci ch_info->channel = eeprom_ch_idx[ch]; 92862306a36Sopenharmony_ci ch_info->band = 92962306a36Sopenharmony_ci (band == 93062306a36Sopenharmony_ci 1) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* permanently store EEPROM's channel regulatory flags 93362306a36Sopenharmony_ci * and max power in channel info database. */ 93462306a36Sopenharmony_ci ch_info->eeprom = eeprom_ch_info[ch]; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci /* Copy the run-time flags so they are there even on 93762306a36Sopenharmony_ci * invalid channels */ 93862306a36Sopenharmony_ci ch_info->flags = eeprom_ch_info[ch].flags; 93962306a36Sopenharmony_ci /* First write that ht40 is not enabled, and then enable 94062306a36Sopenharmony_ci * one by one */ 94162306a36Sopenharmony_ci ch_info->ht40_extension_channel = 94262306a36Sopenharmony_ci IEEE80211_CHAN_NO_HT40; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (!(il_is_channel_valid(ch_info))) { 94562306a36Sopenharmony_ci D_EEPROM("Ch. %d Flags %x [%sGHz] - " 94662306a36Sopenharmony_ci "No traffic\n", ch_info->channel, 94762306a36Sopenharmony_ci ch_info->flags, 94862306a36Sopenharmony_ci il_is_channel_a_band(ch_info) ? "5.2" : 94962306a36Sopenharmony_ci "2.4"); 95062306a36Sopenharmony_ci ch_info++; 95162306a36Sopenharmony_ci continue; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* Initialize regulatory-based run-time data */ 95562306a36Sopenharmony_ci ch_info->max_power_avg = ch_info->curr_txpow = 95662306a36Sopenharmony_ci eeprom_ch_info[ch].max_power_avg; 95762306a36Sopenharmony_ci ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; 95862306a36Sopenharmony_ci ch_info->min_power = 0; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):" 96162306a36Sopenharmony_ci " Ad-Hoc %ssupported\n", ch_info->channel, 96262306a36Sopenharmony_ci il_is_channel_a_band(ch_info) ? "5.2" : "2.4", 96362306a36Sopenharmony_ci CHECK_AND_PRINT_I(VALID), 96462306a36Sopenharmony_ci CHECK_AND_PRINT_I(IBSS), 96562306a36Sopenharmony_ci CHECK_AND_PRINT_I(ACTIVE), 96662306a36Sopenharmony_ci CHECK_AND_PRINT_I(RADAR), 96762306a36Sopenharmony_ci CHECK_AND_PRINT_I(WIDE), 96862306a36Sopenharmony_ci CHECK_AND_PRINT_I(DFS), 96962306a36Sopenharmony_ci eeprom_ch_info[ch].flags, 97062306a36Sopenharmony_ci eeprom_ch_info[ch].max_power_avg, 97162306a36Sopenharmony_ci ((eeprom_ch_info[ch]. 97262306a36Sopenharmony_ci flags & EEPROM_CHANNEL_IBSS) && 97362306a36Sopenharmony_ci !(eeprom_ch_info[ch]. 97462306a36Sopenharmony_ci flags & EEPROM_CHANNEL_RADAR)) ? "" : 97562306a36Sopenharmony_ci "not "); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci ch_info++; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* Check if we do have HT40 channels */ 98262306a36Sopenharmony_ci if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 && 98362306a36Sopenharmony_ci il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40) 98462306a36Sopenharmony_ci return 0; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ 98762306a36Sopenharmony_ci for (band = 6; band <= 7; band++) { 98862306a36Sopenharmony_ci enum nl80211_band ieeeband; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci il_init_band_reference(il, band, &eeprom_ch_count, 99162306a36Sopenharmony_ci &eeprom_ch_info, &eeprom_ch_idx); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ 99462306a36Sopenharmony_ci ieeeband = 99562306a36Sopenharmony_ci (band == 6) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* Loop through each band adding each of the channels */ 99862306a36Sopenharmony_ci for (ch = 0; ch < eeprom_ch_count; ch++) { 99962306a36Sopenharmony_ci /* Set up driver's info for lower half */ 100062306a36Sopenharmony_ci il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch], 100162306a36Sopenharmony_ci &eeprom_ch_info[ch], 100262306a36Sopenharmony_ci IEEE80211_CHAN_NO_HT40PLUS); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* Set up driver's info for upper half */ 100562306a36Sopenharmony_ci il_mod_ht40_chan_info(il, ieeeband, 100662306a36Sopenharmony_ci eeprom_ch_idx[ch] + 4, 100762306a36Sopenharmony_ci &eeprom_ch_info[ch], 100862306a36Sopenharmony_ci IEEE80211_CHAN_NO_HT40MINUS); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci return 0; 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ciEXPORT_SYMBOL(il_init_channel_map); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci/* 101762306a36Sopenharmony_ci * il_free_channel_map - undo allocations in il_init_channel_map 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_civoid 102062306a36Sopenharmony_ciil_free_channel_map(struct il_priv *il) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci kfree(il->channel_info); 102362306a36Sopenharmony_ci il->channel_count = 0; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ciEXPORT_SYMBOL(il_free_channel_map); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci/* 102862306a36Sopenharmony_ci * il_get_channel_info - Find driver's ilate channel info 102962306a36Sopenharmony_ci * 103062306a36Sopenharmony_ci * Based on band and channel number. 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_ciconst struct il_channel_info * 103362306a36Sopenharmony_ciil_get_channel_info(const struct il_priv *il, enum nl80211_band band, 103462306a36Sopenharmony_ci u16 channel) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci int i; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci switch (band) { 103962306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 104062306a36Sopenharmony_ci for (i = 14; i < il->channel_count; i++) { 104162306a36Sopenharmony_ci if (il->channel_info[i].channel == channel) 104262306a36Sopenharmony_ci return &il->channel_info[i]; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 104662306a36Sopenharmony_ci if (channel >= 1 && channel <= 14) 104762306a36Sopenharmony_ci return &il->channel_info[channel - 1]; 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci default: 105062306a36Sopenharmony_ci BUG(); 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci return NULL; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ciEXPORT_SYMBOL(il_get_channel_info); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci/* 105862306a36Sopenharmony_ci * Setting power level allows the card to go to sleep when not busy. 105962306a36Sopenharmony_ci * 106062306a36Sopenharmony_ci * We calculate a sleep command based on the required latency, which 106162306a36Sopenharmony_ci * we get from mac80211. 106262306a36Sopenharmony_ci */ 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci#define SLP_VEC(X0, X1, X2, X3, X4) { \ 106562306a36Sopenharmony_ci cpu_to_le32(X0), \ 106662306a36Sopenharmony_ci cpu_to_le32(X1), \ 106762306a36Sopenharmony_ci cpu_to_le32(X2), \ 106862306a36Sopenharmony_ci cpu_to_le32(X3), \ 106962306a36Sopenharmony_ci cpu_to_le32(X4) \ 107062306a36Sopenharmony_ci} 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic void 107362306a36Sopenharmony_ciil_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci static const __le32 interval[3][IL_POWER_VEC_SIZE] = { 107662306a36Sopenharmony_ci SLP_VEC(2, 2, 4, 6, 0xFF), 107762306a36Sopenharmony_ci SLP_VEC(2, 4, 7, 10, 10), 107862306a36Sopenharmony_ci SLP_VEC(4, 7, 10, 10, 0xFF) 107962306a36Sopenharmony_ci }; 108062306a36Sopenharmony_ci int i, dtim_period, no_dtim; 108162306a36Sopenharmony_ci u32 max_sleep; 108262306a36Sopenharmony_ci bool skip; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (il->power_data.pci_pm) 108762306a36Sopenharmony_ci cmd->flags |= IL_POWER_PCI_PM_MSK; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* if no Power Save, we are done */ 109062306a36Sopenharmony_ci if (il->power_data.ps_disabled) 109162306a36Sopenharmony_ci return; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK; 109462306a36Sopenharmony_ci cmd->keep_alive_seconds = 0; 109562306a36Sopenharmony_ci cmd->debug_flags = 0; 109662306a36Sopenharmony_ci cmd->rx_data_timeout = cpu_to_le32(25 * 1024); 109762306a36Sopenharmony_ci cmd->tx_data_timeout = cpu_to_le32(25 * 1024); 109862306a36Sopenharmony_ci cmd->keep_alive_beacons = 0; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (dtim_period <= 2) { 110362306a36Sopenharmony_ci memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0])); 110462306a36Sopenharmony_ci no_dtim = 2; 110562306a36Sopenharmony_ci } else if (dtim_period <= 10) { 110662306a36Sopenharmony_ci memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1])); 110762306a36Sopenharmony_ci no_dtim = 2; 110862306a36Sopenharmony_ci } else { 110962306a36Sopenharmony_ci memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2])); 111062306a36Sopenharmony_ci no_dtim = 0; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (dtim_period == 0) { 111462306a36Sopenharmony_ci dtim_period = 1; 111562306a36Sopenharmony_ci skip = false; 111662306a36Sopenharmony_ci } else { 111762306a36Sopenharmony_ci skip = !!no_dtim; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (skip) { 112162306a36Sopenharmony_ci __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1]; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci max_sleep = le32_to_cpu(tmp); 112462306a36Sopenharmony_ci if (max_sleep == 0xFF) 112562306a36Sopenharmony_ci max_sleep = dtim_period * (skip + 1); 112662306a36Sopenharmony_ci else if (max_sleep > dtim_period) 112762306a36Sopenharmony_ci max_sleep = (max_sleep / dtim_period) * dtim_period; 112862306a36Sopenharmony_ci cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK; 112962306a36Sopenharmony_ci } else { 113062306a36Sopenharmony_ci max_sleep = dtim_period; 113162306a36Sopenharmony_ci cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci for (i = 0; i < IL_POWER_VEC_SIZE; i++) 113562306a36Sopenharmony_ci if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) 113662306a36Sopenharmony_ci cmd->sleep_interval[i] = cpu_to_le32(max_sleep); 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic int 114062306a36Sopenharmony_ciil_set_power(struct il_priv *il, struct il_powertable_cmd *cmd) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci D_POWER("Sending power/sleep command\n"); 114362306a36Sopenharmony_ci D_POWER("Flags value = 0x%08X\n", cmd->flags); 114462306a36Sopenharmony_ci D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); 114562306a36Sopenharmony_ci D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); 114662306a36Sopenharmony_ci D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n", 114762306a36Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[0]), 114862306a36Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[1]), 114962306a36Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[2]), 115062306a36Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[3]), 115162306a36Sopenharmony_ci le32_to_cpu(cmd->sleep_interval[4])); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci return il_send_cmd_pdu(il, C_POWER_TBL, 115462306a36Sopenharmony_ci sizeof(struct il_powertable_cmd), cmd); 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic int 115862306a36Sopenharmony_ciil_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci int ret; 116162306a36Sopenharmony_ci bool update_chains; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci /* Don't update the RX chain when chain noise calibration is running */ 116662306a36Sopenharmony_ci update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE || 116762306a36Sopenharmony_ci il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) 117062306a36Sopenharmony_ci return 0; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (!il_is_ready_rf(il)) 117362306a36Sopenharmony_ci return -EIO; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci /* scan complete use sleep_power_next, need to be updated */ 117662306a36Sopenharmony_ci memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); 117762306a36Sopenharmony_ci if (test_bit(S_SCANNING, &il->status) && !force) { 117862306a36Sopenharmony_ci D_INFO("Defer power set mode while scanning\n"); 117962306a36Sopenharmony_ci return 0; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK) 118362306a36Sopenharmony_ci set_bit(S_POWER_PMI, &il->status); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci ret = il_set_power(il, cmd); 118662306a36Sopenharmony_ci if (!ret) { 118762306a36Sopenharmony_ci if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)) 118862306a36Sopenharmony_ci clear_bit(S_POWER_PMI, &il->status); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (il->ops->update_chain_flags && update_chains) 119162306a36Sopenharmony_ci il->ops->update_chain_flags(il); 119262306a36Sopenharmony_ci else if (il->ops->update_chain_flags) 119362306a36Sopenharmony_ci D_POWER("Cannot update the power, chain noise " 119462306a36Sopenharmony_ci "calibration running: %d\n", 119562306a36Sopenharmony_ci il->chain_noise_data.state); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)); 119862306a36Sopenharmony_ci } else 119962306a36Sopenharmony_ci IL_ERR("set power fail, ret = %d", ret); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return ret; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ciint 120562306a36Sopenharmony_ciil_power_update_mode(struct il_priv *il, bool force) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci struct il_powertable_cmd cmd; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci il_build_powertable_cmd(il, &cmd); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci return il_power_set_mode(il, &cmd, force); 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ciEXPORT_SYMBOL(il_power_update_mode); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci/* initialize to default */ 121662306a36Sopenharmony_civoid 121762306a36Sopenharmony_ciil_power_initialize(struct il_priv *il) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci u16 lctl; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); 122262306a36Sopenharmony_ci il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci il->power_data.debug_sleep_level_override = -1; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd)); 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ciEXPORT_SYMBOL(il_power_initialize); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after 123162306a36Sopenharmony_ci * sending probe req. This should be set long enough to hear probe responses 123262306a36Sopenharmony_ci * from more than one AP. */ 123362306a36Sopenharmony_ci#define IL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ 123462306a36Sopenharmony_ci#define IL_ACTIVE_DWELL_TIME_52 (20) 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci#define IL_ACTIVE_DWELL_FACTOR_24GHZ (3) 123762306a36Sopenharmony_ci#define IL_ACTIVE_DWELL_FACTOR_52GHZ (2) 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. 124062306a36Sopenharmony_ci * Must be set longer than active dwell time. 124162306a36Sopenharmony_ci * For the most reliable scan, set > AP beacon interval (typically 100msec). */ 124262306a36Sopenharmony_ci#define IL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ 124362306a36Sopenharmony_ci#define IL_PASSIVE_DWELL_TIME_52 (10) 124462306a36Sopenharmony_ci#define IL_PASSIVE_DWELL_BASE (100) 124562306a36Sopenharmony_ci#define IL_CHANNEL_TUNE_TIME 5 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic int 124862306a36Sopenharmony_ciil_send_scan_abort(struct il_priv *il) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci int ret; 125162306a36Sopenharmony_ci struct il_rx_pkt *pkt; 125262306a36Sopenharmony_ci struct il_host_cmd cmd = { 125362306a36Sopenharmony_ci .id = C_SCAN_ABORT, 125462306a36Sopenharmony_ci .flags = CMD_WANT_SKB, 125562306a36Sopenharmony_ci }; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* Exit instantly with error when device is not ready 125862306a36Sopenharmony_ci * to receive scan abort command or it does not perform 125962306a36Sopenharmony_ci * hardware scan currently */ 126062306a36Sopenharmony_ci if (!test_bit(S_READY, &il->status) || 126162306a36Sopenharmony_ci !test_bit(S_GEO_CONFIGURED, &il->status) || 126262306a36Sopenharmony_ci !test_bit(S_SCAN_HW, &il->status) || 126362306a36Sopenharmony_ci test_bit(S_FW_ERROR, &il->status) || 126462306a36Sopenharmony_ci test_bit(S_EXIT_PENDING, &il->status)) 126562306a36Sopenharmony_ci return -EIO; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci ret = il_send_cmd_sync(il, &cmd); 126862306a36Sopenharmony_ci if (ret) 126962306a36Sopenharmony_ci return ret; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci pkt = (struct il_rx_pkt *)cmd.reply_page; 127262306a36Sopenharmony_ci if (pkt->u.status != CAN_ABORT_STATUS) { 127362306a36Sopenharmony_ci /* The scan abort will return 1 for success or 127462306a36Sopenharmony_ci * 2 for "failure". A failure condition can be 127562306a36Sopenharmony_ci * due to simply not being in an active scan which 127662306a36Sopenharmony_ci * can occur if we send the scan abort before we 127762306a36Sopenharmony_ci * the microcode has notified us that a scan is 127862306a36Sopenharmony_ci * completed. */ 127962306a36Sopenharmony_ci D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status); 128062306a36Sopenharmony_ci ret = -EIO; 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci il_free_pages(il, cmd.reply_page); 128462306a36Sopenharmony_ci return ret; 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic void 128862306a36Sopenharmony_ciil_complete_scan(struct il_priv *il, bool aborted) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct cfg80211_scan_info info = { 129162306a36Sopenharmony_ci .aborted = aborted, 129262306a36Sopenharmony_ci }; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* check if scan was requested from mac80211 */ 129562306a36Sopenharmony_ci if (il->scan_request) { 129662306a36Sopenharmony_ci D_SCAN("Complete scan in mac80211\n"); 129762306a36Sopenharmony_ci ieee80211_scan_completed(il->hw, &info); 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci il->scan_vif = NULL; 130162306a36Sopenharmony_ci il->scan_request = NULL; 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_civoid 130562306a36Sopenharmony_ciil_force_scan_end(struct il_priv *il) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (!test_bit(S_SCANNING, &il->status)) { 131062306a36Sopenharmony_ci D_SCAN("Forcing scan end while not scanning\n"); 131162306a36Sopenharmony_ci return; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci D_SCAN("Forcing scan end\n"); 131562306a36Sopenharmony_ci clear_bit(S_SCANNING, &il->status); 131662306a36Sopenharmony_ci clear_bit(S_SCAN_HW, &il->status); 131762306a36Sopenharmony_ci clear_bit(S_SCAN_ABORTING, &il->status); 131862306a36Sopenharmony_ci il_complete_scan(il, true); 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cistatic void 132262306a36Sopenharmony_ciil_do_scan_abort(struct il_priv *il) 132362306a36Sopenharmony_ci{ 132462306a36Sopenharmony_ci int ret; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (!test_bit(S_SCANNING, &il->status)) { 132962306a36Sopenharmony_ci D_SCAN("Not performing scan to abort\n"); 133062306a36Sopenharmony_ci return; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) { 133462306a36Sopenharmony_ci D_SCAN("Scan abort in progress\n"); 133562306a36Sopenharmony_ci return; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci ret = il_send_scan_abort(il); 133962306a36Sopenharmony_ci if (ret) { 134062306a36Sopenharmony_ci D_SCAN("Send scan abort failed %d\n", ret); 134162306a36Sopenharmony_ci il_force_scan_end(il); 134262306a36Sopenharmony_ci } else 134362306a36Sopenharmony_ci D_SCAN("Successfully send scan abort\n"); 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci/* 134762306a36Sopenharmony_ci * il_scan_cancel - Cancel any currently executing HW scan 134862306a36Sopenharmony_ci */ 134962306a36Sopenharmony_ciint 135062306a36Sopenharmony_ciil_scan_cancel(struct il_priv *il) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci D_SCAN("Queuing abort scan\n"); 135362306a36Sopenharmony_ci queue_work(il->workqueue, &il->abort_scan); 135462306a36Sopenharmony_ci return 0; 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_ciEXPORT_SYMBOL(il_scan_cancel); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci/* 135962306a36Sopenharmony_ci * il_scan_cancel_timeout - Cancel any currently executing HW scan 136062306a36Sopenharmony_ci * @ms: amount of time to wait (in milliseconds) for scan to abort 136162306a36Sopenharmony_ci * 136262306a36Sopenharmony_ci */ 136362306a36Sopenharmony_ciint 136462306a36Sopenharmony_ciil_scan_cancel_timeout(struct il_priv *il, unsigned long ms) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(ms); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci D_SCAN("Scan cancel timeout\n"); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci il_do_scan_abort(il); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci while (time_before_eq(jiffies, timeout)) { 137562306a36Sopenharmony_ci if (!test_bit(S_SCAN_HW, &il->status)) 137662306a36Sopenharmony_ci break; 137762306a36Sopenharmony_ci msleep(20); 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci return test_bit(S_SCAN_HW, &il->status); 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ciEXPORT_SYMBOL(il_scan_cancel_timeout); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci/* Service response to C_SCAN (0x80) */ 138562306a36Sopenharmony_cistatic void 138662306a36Sopenharmony_ciil_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 138962306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 139062306a36Sopenharmony_ci struct il_scanreq_notification *notif = 139162306a36Sopenharmony_ci (struct il_scanreq_notification *)pkt->u.raw; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci D_SCAN("Scan request status = 0x%x\n", notif->status); 139462306a36Sopenharmony_ci#endif 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci/* Service N_SCAN_START (0x82) */ 139862306a36Sopenharmony_cistatic void 139962306a36Sopenharmony_ciil_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 140262306a36Sopenharmony_ci struct il_scanstart_notification *notif = 140362306a36Sopenharmony_ci (struct il_scanstart_notification *)pkt->u.raw; 140462306a36Sopenharmony_ci il->scan_start_tsf = le32_to_cpu(notif->tsf_low); 140562306a36Sopenharmony_ci D_SCAN("Scan start: " "%d [802.11%s] " 140662306a36Sopenharmony_ci "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel, 140762306a36Sopenharmony_ci notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high), 140862306a36Sopenharmony_ci le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer); 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci/* Service N_SCAN_RESULTS (0x83) */ 141262306a36Sopenharmony_cistatic void 141362306a36Sopenharmony_ciil_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 141662306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 141762306a36Sopenharmony_ci struct il_scanresults_notification *notif = 141862306a36Sopenharmony_ci (struct il_scanresults_notification *)pkt->u.raw; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d " 142162306a36Sopenharmony_ci "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a", 142262306a36Sopenharmony_ci le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), 142362306a36Sopenharmony_ci le32_to_cpu(notif->stats[0]), 142462306a36Sopenharmony_ci le32_to_cpu(notif->tsf_low) - il->scan_start_tsf); 142562306a36Sopenharmony_ci#endif 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci/* Service N_SCAN_COMPLETE (0x84) */ 142962306a36Sopenharmony_cistatic void 143062306a36Sopenharmony_ciil_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb) 143162306a36Sopenharmony_ci{ 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 143462306a36Sopenharmony_ci struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", 143762306a36Sopenharmony_ci scan_notif->scanned_channels, scan_notif->tsf_low, 143862306a36Sopenharmony_ci scan_notif->tsf_high, scan_notif->status); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci /* The HW is no longer scanning */ 144162306a36Sopenharmony_ci clear_bit(S_SCAN_HW, &il->status); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci D_SCAN("Scan on %sGHz took %dms\n", 144462306a36Sopenharmony_ci (il->scan_band == NL80211_BAND_2GHZ) ? "2.4" : "5.2", 144562306a36Sopenharmony_ci jiffies_to_msecs(jiffies - il->scan_start)); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci queue_work(il->workqueue, &il->scan_completed); 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_civoid 145162306a36Sopenharmony_ciil_setup_rx_scan_handlers(struct il_priv *il) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci /* scan handlers */ 145462306a36Sopenharmony_ci il->handlers[C_SCAN] = il_hdl_scan; 145562306a36Sopenharmony_ci il->handlers[N_SCAN_START] = il_hdl_scan_start; 145662306a36Sopenharmony_ci il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results; 145762306a36Sopenharmony_ci il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete; 145862306a36Sopenharmony_ci} 145962306a36Sopenharmony_ciEXPORT_SYMBOL(il_setup_rx_scan_handlers); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ciu16 146262306a36Sopenharmony_ciil_get_active_dwell_time(struct il_priv *il, enum nl80211_band band, 146362306a36Sopenharmony_ci u8 n_probes) 146462306a36Sopenharmony_ci{ 146562306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 146662306a36Sopenharmony_ci return IL_ACTIVE_DWELL_TIME_52 + 146762306a36Sopenharmony_ci IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); 146862306a36Sopenharmony_ci else 146962306a36Sopenharmony_ci return IL_ACTIVE_DWELL_TIME_24 + 147062306a36Sopenharmony_ci IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ciEXPORT_SYMBOL(il_get_active_dwell_time); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ciu16 147562306a36Sopenharmony_ciil_get_passive_dwell_time(struct il_priv *il, enum nl80211_band band, 147662306a36Sopenharmony_ci struct ieee80211_vif *vif) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci u16 value; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci u16 passive = 148162306a36Sopenharmony_ci (band == 148262306a36Sopenharmony_ci NL80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE + 148362306a36Sopenharmony_ci IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE + 148462306a36Sopenharmony_ci IL_PASSIVE_DWELL_TIME_52; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (il_is_any_associated(il)) { 148762306a36Sopenharmony_ci /* 148862306a36Sopenharmony_ci * If we're associated, we clamp the maximum passive 148962306a36Sopenharmony_ci * dwell time to be 98% of the smallest beacon interval 149062306a36Sopenharmony_ci * (minus 2 * channel tune time) 149162306a36Sopenharmony_ci */ 149262306a36Sopenharmony_ci value = il->vif ? il->vif->bss_conf.beacon_int : 0; 149362306a36Sopenharmony_ci if (value > IL_PASSIVE_DWELL_BASE || !value) 149462306a36Sopenharmony_ci value = IL_PASSIVE_DWELL_BASE; 149562306a36Sopenharmony_ci value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2; 149662306a36Sopenharmony_ci passive = min(value, passive); 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci return passive; 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ciEXPORT_SYMBOL(il_get_passive_dwell_time); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_civoid 150462306a36Sopenharmony_ciil_init_scan_params(struct il_priv *il) 150562306a36Sopenharmony_ci{ 150662306a36Sopenharmony_ci u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1; 150762306a36Sopenharmony_ci if (!il->scan_tx_ant[NL80211_BAND_5GHZ]) 150862306a36Sopenharmony_ci il->scan_tx_ant[NL80211_BAND_5GHZ] = ant_idx; 150962306a36Sopenharmony_ci if (!il->scan_tx_ant[NL80211_BAND_2GHZ]) 151062306a36Sopenharmony_ci il->scan_tx_ant[NL80211_BAND_2GHZ] = ant_idx; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ciEXPORT_SYMBOL(il_init_scan_params); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_cistatic int 151562306a36Sopenharmony_ciil_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci int ret; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci cancel_delayed_work(&il->scan_check); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if (!il_is_ready_rf(il)) { 152462306a36Sopenharmony_ci IL_WARN("Request scan called when driver not ready.\n"); 152562306a36Sopenharmony_ci return -EIO; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci if (test_bit(S_SCAN_HW, &il->status)) { 152962306a36Sopenharmony_ci D_SCAN("Multiple concurrent scan requests in parallel.\n"); 153062306a36Sopenharmony_ci return -EBUSY; 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci if (test_bit(S_SCAN_ABORTING, &il->status)) { 153462306a36Sopenharmony_ci D_SCAN("Scan request while abort pending.\n"); 153562306a36Sopenharmony_ci return -EBUSY; 153662306a36Sopenharmony_ci } 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci D_SCAN("Starting scan...\n"); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci set_bit(S_SCANNING, &il->status); 154162306a36Sopenharmony_ci il->scan_start = jiffies; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci ret = il->ops->request_scan(il, vif); 154462306a36Sopenharmony_ci if (ret) { 154562306a36Sopenharmony_ci clear_bit(S_SCANNING, &il->status); 154662306a36Sopenharmony_ci return ret; 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci queue_delayed_work(il->workqueue, &il->scan_check, 155062306a36Sopenharmony_ci IL_SCAN_CHECK_WATCHDOG); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci return 0; 155362306a36Sopenharmony_ci} 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ciint 155662306a36Sopenharmony_ciil_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 155762306a36Sopenharmony_ci struct ieee80211_scan_request *hw_req) 155862306a36Sopenharmony_ci{ 155962306a36Sopenharmony_ci struct cfg80211_scan_request *req = &hw_req->req; 156062306a36Sopenharmony_ci struct il_priv *il = hw->priv; 156162306a36Sopenharmony_ci int ret; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci if (req->n_channels == 0) { 156462306a36Sopenharmony_ci IL_ERR("Can not scan on no channels.\n"); 156562306a36Sopenharmony_ci return -EINVAL; 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci mutex_lock(&il->mutex); 156962306a36Sopenharmony_ci D_MAC80211("enter\n"); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if (test_bit(S_SCANNING, &il->status)) { 157262306a36Sopenharmony_ci D_SCAN("Scan already in progress.\n"); 157362306a36Sopenharmony_ci ret = -EAGAIN; 157462306a36Sopenharmony_ci goto out_unlock; 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci /* mac80211 will only ask for one band at a time */ 157862306a36Sopenharmony_ci il->scan_request = req; 157962306a36Sopenharmony_ci il->scan_vif = vif; 158062306a36Sopenharmony_ci il->scan_band = req->channels[0]->band; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci ret = il_scan_initiate(il, vif); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ciout_unlock: 158562306a36Sopenharmony_ci D_MAC80211("leave ret %d\n", ret); 158662306a36Sopenharmony_ci mutex_unlock(&il->mutex); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return ret; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_hw_scan); 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_cistatic void 159362306a36Sopenharmony_ciil_bg_scan_check(struct work_struct *data) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci struct il_priv *il = 159662306a36Sopenharmony_ci container_of(data, struct il_priv, scan_check.work); 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci D_SCAN("Scan check work\n"); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci /* Since we are here firmware does not finish scan and 160162306a36Sopenharmony_ci * most likely is in bad shape, so we don't bother to 160262306a36Sopenharmony_ci * send abort command, just force scan complete to mac80211 */ 160362306a36Sopenharmony_ci mutex_lock(&il->mutex); 160462306a36Sopenharmony_ci il_force_scan_end(il); 160562306a36Sopenharmony_ci mutex_unlock(&il->mutex); 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci/* 160962306a36Sopenharmony_ci * il_fill_probe_req - fill in all required fields and IE for probe request 161062306a36Sopenharmony_ci */ 161162306a36Sopenharmony_ciu16 161262306a36Sopenharmony_ciil_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, 161362306a36Sopenharmony_ci const u8 *ta, const u8 *ies, int ie_len, int left) 161462306a36Sopenharmony_ci{ 161562306a36Sopenharmony_ci int len = 0; 161662306a36Sopenharmony_ci u8 *pos = NULL; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci /* Make sure there is enough space for the probe request, 161962306a36Sopenharmony_ci * two mandatory IEs and the data */ 162062306a36Sopenharmony_ci left -= 24; 162162306a36Sopenharmony_ci if (left < 0) 162262306a36Sopenharmony_ci return 0; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); 162562306a36Sopenharmony_ci eth_broadcast_addr(frame->da); 162662306a36Sopenharmony_ci memcpy(frame->sa, ta, ETH_ALEN); 162762306a36Sopenharmony_ci eth_broadcast_addr(frame->bssid); 162862306a36Sopenharmony_ci frame->seq_ctrl = 0; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci len += 24; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci /* ...next IE... */ 163362306a36Sopenharmony_ci pos = &frame->u.probe_req.variable[0]; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci /* fill in our indirect SSID IE */ 163662306a36Sopenharmony_ci left -= 2; 163762306a36Sopenharmony_ci if (left < 0) 163862306a36Sopenharmony_ci return 0; 163962306a36Sopenharmony_ci *pos++ = WLAN_EID_SSID; 164062306a36Sopenharmony_ci *pos++ = 0; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci len += 2; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci if (WARN_ON(left < ie_len)) 164562306a36Sopenharmony_ci return len; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci if (ies && ie_len) { 164862306a36Sopenharmony_ci memcpy(pos, ies, ie_len); 164962306a36Sopenharmony_ci len += ie_len; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci return (u16) len; 165362306a36Sopenharmony_ci} 165462306a36Sopenharmony_ciEXPORT_SYMBOL(il_fill_probe_req); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_cistatic void 165762306a36Sopenharmony_ciil_bg_abort_scan(struct work_struct *work) 165862306a36Sopenharmony_ci{ 165962306a36Sopenharmony_ci struct il_priv *il = container_of(work, struct il_priv, abort_scan); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci D_SCAN("Abort scan work\n"); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci /* We keep scan_check work queued in case when firmware will not 166462306a36Sopenharmony_ci * report back scan completed notification */ 166562306a36Sopenharmony_ci mutex_lock(&il->mutex); 166662306a36Sopenharmony_ci il_scan_cancel_timeout(il, 200); 166762306a36Sopenharmony_ci mutex_unlock(&il->mutex); 166862306a36Sopenharmony_ci} 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_cistatic void 167162306a36Sopenharmony_ciil_bg_scan_completed(struct work_struct *work) 167262306a36Sopenharmony_ci{ 167362306a36Sopenharmony_ci struct il_priv *il = container_of(work, struct il_priv, scan_completed); 167462306a36Sopenharmony_ci bool aborted; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci D_SCAN("Completed scan.\n"); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci cancel_delayed_work(&il->scan_check); 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci mutex_lock(&il->mutex); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status); 168362306a36Sopenharmony_ci if (aborted) 168462306a36Sopenharmony_ci D_SCAN("Aborted scan completed.\n"); 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (!test_and_clear_bit(S_SCANNING, &il->status)) { 168762306a36Sopenharmony_ci D_SCAN("Scan already completed.\n"); 168862306a36Sopenharmony_ci goto out_settings; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci il_complete_scan(il, aborted); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ciout_settings: 169462306a36Sopenharmony_ci /* Can we still talk to firmware ? */ 169562306a36Sopenharmony_ci if (!il_is_ready_rf(il)) 169662306a36Sopenharmony_ci goto out; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci /* 169962306a36Sopenharmony_ci * We do not commit power settings while scan is pending, 170062306a36Sopenharmony_ci * do it now if the settings changed. 170162306a36Sopenharmony_ci */ 170262306a36Sopenharmony_ci il_power_set_mode(il, &il->power_data.sleep_cmd_next, false); 170362306a36Sopenharmony_ci il_set_tx_power(il, il->tx_power_next, false); 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci il->ops->post_scan(il); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ciout: 170862306a36Sopenharmony_ci mutex_unlock(&il->mutex); 170962306a36Sopenharmony_ci} 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_civoid 171262306a36Sopenharmony_ciil_setup_scan_deferred_work(struct il_priv *il) 171362306a36Sopenharmony_ci{ 171462306a36Sopenharmony_ci INIT_WORK(&il->scan_completed, il_bg_scan_completed); 171562306a36Sopenharmony_ci INIT_WORK(&il->abort_scan, il_bg_abort_scan); 171662306a36Sopenharmony_ci INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check); 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ciEXPORT_SYMBOL(il_setup_scan_deferred_work); 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_civoid 172162306a36Sopenharmony_ciil_cancel_scan_deferred_work(struct il_priv *il) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci cancel_work_sync(&il->abort_scan); 172462306a36Sopenharmony_ci cancel_work_sync(&il->scan_completed); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci if (cancel_delayed_work_sync(&il->scan_check)) { 172762306a36Sopenharmony_ci mutex_lock(&il->mutex); 172862306a36Sopenharmony_ci il_force_scan_end(il); 172962306a36Sopenharmony_ci mutex_unlock(&il->mutex); 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci} 173262306a36Sopenharmony_ciEXPORT_SYMBOL(il_cancel_scan_deferred_work); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci/* il->sta_lock must be held */ 173562306a36Sopenharmony_cistatic void 173662306a36Sopenharmony_ciil_sta_ucode_activate(struct il_priv *il, u8 sta_id) 173762306a36Sopenharmony_ci{ 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) 174062306a36Sopenharmony_ci IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n", 174162306a36Sopenharmony_ci sta_id, il->stations[sta_id].sta.sta.addr); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) { 174462306a36Sopenharmony_ci D_ASSOC("STA id %u addr %pM already present" 174562306a36Sopenharmony_ci " in uCode (according to driver)\n", sta_id, 174662306a36Sopenharmony_ci il->stations[sta_id].sta.sta.addr); 174762306a36Sopenharmony_ci } else { 174862306a36Sopenharmony_ci il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE; 174962306a36Sopenharmony_ci D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id, 175062306a36Sopenharmony_ci il->stations[sta_id].sta.sta.addr); 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci} 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_cistatic int 175562306a36Sopenharmony_ciil_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta, 175662306a36Sopenharmony_ci struct il_rx_pkt *pkt, bool sync) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci u8 sta_id = addsta->sta.sta_id; 175962306a36Sopenharmony_ci unsigned long flags; 176062306a36Sopenharmony_ci int ret = -EIO; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { 176362306a36Sopenharmony_ci IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags); 176462306a36Sopenharmony_ci return ret; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci D_INFO("Processing response for adding station %u\n", sta_id); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci switch (pkt->u.add_sta.status) { 177262306a36Sopenharmony_ci case ADD_STA_SUCCESS_MSK: 177362306a36Sopenharmony_ci D_INFO("C_ADD_STA PASSED\n"); 177462306a36Sopenharmony_ci il_sta_ucode_activate(il, sta_id); 177562306a36Sopenharmony_ci ret = 0; 177662306a36Sopenharmony_ci break; 177762306a36Sopenharmony_ci case ADD_STA_NO_ROOM_IN_TBL: 177862306a36Sopenharmony_ci IL_ERR("Adding station %d failed, no room in table.\n", sta_id); 177962306a36Sopenharmony_ci break; 178062306a36Sopenharmony_ci case ADD_STA_NO_BLOCK_ACK_RESOURCE: 178162306a36Sopenharmony_ci IL_ERR("Adding station %d failed, no block ack resource.\n", 178262306a36Sopenharmony_ci sta_id); 178362306a36Sopenharmony_ci break; 178462306a36Sopenharmony_ci case ADD_STA_MODIFY_NON_EXIST_STA: 178562306a36Sopenharmony_ci IL_ERR("Attempting to modify non-existing station %d\n", 178662306a36Sopenharmony_ci sta_id); 178762306a36Sopenharmony_ci break; 178862306a36Sopenharmony_ci default: 178962306a36Sopenharmony_ci D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status); 179062306a36Sopenharmony_ci break; 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci D_INFO("%s station id %u addr %pM\n", 179462306a36Sopenharmony_ci il->stations[sta_id].sta.mode == 179562306a36Sopenharmony_ci STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id, 179662306a36Sopenharmony_ci il->stations[sta_id].sta.sta.addr); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci /* 179962306a36Sopenharmony_ci * XXX: The MAC address in the command buffer is often changed from 180062306a36Sopenharmony_ci * the original sent to the device. That is, the MAC address 180162306a36Sopenharmony_ci * written to the command buffer often is not the same MAC address 180262306a36Sopenharmony_ci * read from the command buffer when the command returns. This 180362306a36Sopenharmony_ci * issue has not yet been resolved and this debugging is left to 180462306a36Sopenharmony_ci * observe the problem. 180562306a36Sopenharmony_ci */ 180662306a36Sopenharmony_ci D_INFO("%s station according to cmd buffer %pM\n", 180762306a36Sopenharmony_ci il->stations[sta_id].sta.mode == 180862306a36Sopenharmony_ci STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr); 180962306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci return ret; 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_cistatic void 181562306a36Sopenharmony_ciil_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd, 181662306a36Sopenharmony_ci struct il_rx_pkt *pkt) 181762306a36Sopenharmony_ci{ 181862306a36Sopenharmony_ci struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci il_process_add_sta_resp(il, addsta, pkt, false); 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci} 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ciint 182562306a36Sopenharmony_ciil_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci struct il_rx_pkt *pkt = NULL; 182862306a36Sopenharmony_ci int ret = 0; 182962306a36Sopenharmony_ci u8 data[sizeof(*sta)]; 183062306a36Sopenharmony_ci struct il_host_cmd cmd = { 183162306a36Sopenharmony_ci .id = C_ADD_STA, 183262306a36Sopenharmony_ci .flags = flags, 183362306a36Sopenharmony_ci .data = data, 183462306a36Sopenharmony_ci }; 183562306a36Sopenharmony_ci u8 sta_id __maybe_unused = sta->sta.sta_id; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr, 183862306a36Sopenharmony_ci flags & CMD_ASYNC ? "a" : ""); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci if (flags & CMD_ASYNC) 184162306a36Sopenharmony_ci cmd.callback = il_add_sta_callback; 184262306a36Sopenharmony_ci else { 184362306a36Sopenharmony_ci cmd.flags |= CMD_WANT_SKB; 184462306a36Sopenharmony_ci might_sleep(); 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci cmd.len = il->ops->build_addsta_hcmd(sta, data); 184862306a36Sopenharmony_ci ret = il_send_cmd(il, &cmd); 184962306a36Sopenharmony_ci if (ret) 185062306a36Sopenharmony_ci return ret; 185162306a36Sopenharmony_ci if (flags & CMD_ASYNC) 185262306a36Sopenharmony_ci return 0; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci pkt = (struct il_rx_pkt *)cmd.reply_page; 185562306a36Sopenharmony_ci ret = il_process_add_sta_resp(il, sta, pkt, true); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci il_free_pages(il, cmd.reply_page); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci return ret; 186062306a36Sopenharmony_ci} 186162306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_add_sta); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_cistatic void 186462306a36Sopenharmony_ciil_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta) 186562306a36Sopenharmony_ci{ 186662306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->deflink.ht_cap; 186762306a36Sopenharmony_ci __le32 sta_flags; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (!sta || !sta_ht_inf->ht_supported) 187062306a36Sopenharmony_ci goto done; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci D_ASSOC("spatial multiplexing power save mode: %s\n", 187362306a36Sopenharmony_ci (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC) ? "static" : 187462306a36Sopenharmony_ci (sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" : 187562306a36Sopenharmony_ci "disabled"); 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci sta_flags = il->stations[idx].sta.station_flags; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci switch (sta->deflink.smps_mode) { 188262306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 188362306a36Sopenharmony_ci sta_flags |= STA_FLG_MIMO_DIS_MSK; 188462306a36Sopenharmony_ci break; 188562306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 188662306a36Sopenharmony_ci sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; 188762306a36Sopenharmony_ci break; 188862306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 188962306a36Sopenharmony_ci break; 189062306a36Sopenharmony_ci default: 189162306a36Sopenharmony_ci IL_WARN("Invalid MIMO PS mode %d\n", sta->deflink.smps_mode); 189262306a36Sopenharmony_ci break; 189362306a36Sopenharmony_ci } 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci sta_flags |= 189662306a36Sopenharmony_ci cpu_to_le32((u32) sta_ht_inf-> 189762306a36Sopenharmony_ci ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci sta_flags |= 190062306a36Sopenharmony_ci cpu_to_le32((u32) sta_ht_inf-> 190162306a36Sopenharmony_ci ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci if (il_is_ht40_tx_allowed(il, &sta->deflink.ht_cap)) 190462306a36Sopenharmony_ci sta_flags |= STA_FLG_HT40_EN_MSK; 190562306a36Sopenharmony_ci else 190662306a36Sopenharmony_ci sta_flags &= ~STA_FLG_HT40_EN_MSK; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci il->stations[idx].sta.station_flags = sta_flags; 190962306a36Sopenharmony_cidone: 191062306a36Sopenharmony_ci return; 191162306a36Sopenharmony_ci} 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci/* 191462306a36Sopenharmony_ci * il_prep_station - Prepare station information for addition 191562306a36Sopenharmony_ci * 191662306a36Sopenharmony_ci * should be called with sta_lock held 191762306a36Sopenharmony_ci */ 191862306a36Sopenharmony_ciu8 191962306a36Sopenharmony_ciil_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, 192062306a36Sopenharmony_ci struct ieee80211_sta *sta) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci struct il_station_entry *station; 192362306a36Sopenharmony_ci int i; 192462306a36Sopenharmony_ci u8 sta_id = IL_INVALID_STATION; 192562306a36Sopenharmony_ci u16 rate; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (is_ap) 192862306a36Sopenharmony_ci sta_id = IL_AP_ID; 192962306a36Sopenharmony_ci else if (is_broadcast_ether_addr(addr)) 193062306a36Sopenharmony_ci sta_id = il->hw_params.bcast_id; 193162306a36Sopenharmony_ci else 193262306a36Sopenharmony_ci for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) { 193362306a36Sopenharmony_ci if (ether_addr_equal(il->stations[i].sta.sta.addr, 193462306a36Sopenharmony_ci addr)) { 193562306a36Sopenharmony_ci sta_id = i; 193662306a36Sopenharmony_ci break; 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci if (!il->stations[i].used && 194062306a36Sopenharmony_ci sta_id == IL_INVALID_STATION) 194162306a36Sopenharmony_ci sta_id = i; 194262306a36Sopenharmony_ci } 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci /* 194562306a36Sopenharmony_ci * These two conditions have the same outcome, but keep them 194662306a36Sopenharmony_ci * separate 194762306a36Sopenharmony_ci */ 194862306a36Sopenharmony_ci if (unlikely(sta_id == IL_INVALID_STATION)) 194962306a36Sopenharmony_ci return sta_id; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci /* 195262306a36Sopenharmony_ci * uCode is not able to deal with multiple requests to add a 195362306a36Sopenharmony_ci * station. Keep track if one is in progress so that we do not send 195462306a36Sopenharmony_ci * another. 195562306a36Sopenharmony_ci */ 195662306a36Sopenharmony_ci if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { 195762306a36Sopenharmony_ci D_INFO("STA %d already in process of being added.\n", sta_id); 195862306a36Sopenharmony_ci return sta_id; 195962306a36Sopenharmony_ci } 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && 196262306a36Sopenharmony_ci (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) && 196362306a36Sopenharmony_ci ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) { 196462306a36Sopenharmony_ci D_ASSOC("STA %d (%pM) already added, not adding again.\n", 196562306a36Sopenharmony_ci sta_id, addr); 196662306a36Sopenharmony_ci return sta_id; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci station = &il->stations[sta_id]; 197062306a36Sopenharmony_ci station->used = IL_STA_DRIVER_ACTIVE; 197162306a36Sopenharmony_ci D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr); 197262306a36Sopenharmony_ci il->num_stations++; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci /* Set up the C_ADD_STA command to send to device */ 197562306a36Sopenharmony_ci memset(&station->sta, 0, sizeof(struct il_addsta_cmd)); 197662306a36Sopenharmony_ci memcpy(station->sta.sta.addr, addr, ETH_ALEN); 197762306a36Sopenharmony_ci station->sta.mode = 0; 197862306a36Sopenharmony_ci station->sta.sta.sta_id = sta_id; 197962306a36Sopenharmony_ci station->sta.station_flags = 0; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci /* 198262306a36Sopenharmony_ci * OK to call unconditionally, since local stations (IBSS BSSID 198362306a36Sopenharmony_ci * STA and broadcast STA) pass in a NULL sta, and mac80211 198462306a36Sopenharmony_ci * doesn't allow HT IBSS. 198562306a36Sopenharmony_ci */ 198662306a36Sopenharmony_ci il_set_ht_add_station(il, sta_id, sta); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci /* 3945 only */ 198962306a36Sopenharmony_ci rate = (il->band == NL80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP; 199062306a36Sopenharmony_ci /* Turn on both antennas for the station... */ 199162306a36Sopenharmony_ci station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci return sta_id; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci} 199662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(il_prep_station); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci#define STA_WAIT_TIMEOUT (HZ/2) 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci/* 200162306a36Sopenharmony_ci * il_add_station_common - 200262306a36Sopenharmony_ci */ 200362306a36Sopenharmony_ciint 200462306a36Sopenharmony_ciil_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap, 200562306a36Sopenharmony_ci struct ieee80211_sta *sta, u8 *sta_id_r) 200662306a36Sopenharmony_ci{ 200762306a36Sopenharmony_ci unsigned long flags_spin; 200862306a36Sopenharmony_ci int ret = 0; 200962306a36Sopenharmony_ci u8 sta_id; 201062306a36Sopenharmony_ci struct il_addsta_cmd sta_cmd; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci *sta_id_r = 0; 201362306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 201462306a36Sopenharmony_ci sta_id = il_prep_station(il, addr, is_ap, sta); 201562306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) { 201662306a36Sopenharmony_ci IL_ERR("Unable to prepare station %pM for addition\n", addr); 201762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 201862306a36Sopenharmony_ci return -EINVAL; 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci /* 202262306a36Sopenharmony_ci * uCode is not able to deal with multiple requests to add a 202362306a36Sopenharmony_ci * station. Keep track if one is in progress so that we do not send 202462306a36Sopenharmony_ci * another. 202562306a36Sopenharmony_ci */ 202662306a36Sopenharmony_ci if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { 202762306a36Sopenharmony_ci D_INFO("STA %d already in process of being added.\n", sta_id); 202862306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 202962306a36Sopenharmony_ci return -EEXIST; 203062306a36Sopenharmony_ci } 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && 203362306a36Sopenharmony_ci (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { 203462306a36Sopenharmony_ci D_ASSOC("STA %d (%pM) already added, not adding again.\n", 203562306a36Sopenharmony_ci sta_id, addr); 203662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 203762306a36Sopenharmony_ci return -EEXIST; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS; 204162306a36Sopenharmony_ci memcpy(&sta_cmd, &il->stations[sta_id].sta, 204262306a36Sopenharmony_ci sizeof(struct il_addsta_cmd)); 204362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci /* Add station to device's station table */ 204662306a36Sopenharmony_ci ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); 204762306a36Sopenharmony_ci if (ret) { 204862306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 204962306a36Sopenharmony_ci IL_ERR("Adding station %pM failed.\n", 205062306a36Sopenharmony_ci il->stations[sta_id].sta.sta.addr); 205162306a36Sopenharmony_ci il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; 205262306a36Sopenharmony_ci il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; 205362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 205462306a36Sopenharmony_ci } 205562306a36Sopenharmony_ci *sta_id_r = sta_id; 205662306a36Sopenharmony_ci return ret; 205762306a36Sopenharmony_ci} 205862306a36Sopenharmony_ciEXPORT_SYMBOL(il_add_station_common); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci/* 206162306a36Sopenharmony_ci * il_sta_ucode_deactivate - deactivate ucode status for a station 206262306a36Sopenharmony_ci * 206362306a36Sopenharmony_ci * il->sta_lock must be held 206462306a36Sopenharmony_ci */ 206562306a36Sopenharmony_cistatic void 206662306a36Sopenharmony_ciil_sta_ucode_deactivate(struct il_priv *il, u8 sta_id) 206762306a36Sopenharmony_ci{ 206862306a36Sopenharmony_ci /* Ucode must be active and driver must be non active */ 206962306a36Sopenharmony_ci if ((il->stations[sta_id]. 207062306a36Sopenharmony_ci used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) != 207162306a36Sopenharmony_ci IL_STA_UCODE_ACTIVE) 207262306a36Sopenharmony_ci IL_ERR("removed non active STA %u\n", sta_id); 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry)); 207762306a36Sopenharmony_ci D_ASSOC("Removed STA %u\n", sta_id); 207862306a36Sopenharmony_ci} 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_cistatic int 208162306a36Sopenharmony_ciil_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id, 208262306a36Sopenharmony_ci bool temporary) 208362306a36Sopenharmony_ci{ 208462306a36Sopenharmony_ci struct il_rx_pkt *pkt; 208562306a36Sopenharmony_ci int ret; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci unsigned long flags_spin; 208862306a36Sopenharmony_ci struct il_rem_sta_cmd rm_sta_cmd; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci struct il_host_cmd cmd = { 209162306a36Sopenharmony_ci .id = C_REM_STA, 209262306a36Sopenharmony_ci .len = sizeof(struct il_rem_sta_cmd), 209362306a36Sopenharmony_ci .flags = CMD_SYNC, 209462306a36Sopenharmony_ci .data = &rm_sta_cmd, 209562306a36Sopenharmony_ci }; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); 209862306a36Sopenharmony_ci rm_sta_cmd.num_sta = 1; 209962306a36Sopenharmony_ci memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci cmd.flags |= CMD_WANT_SKB; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci ret = il_send_cmd(il, &cmd); 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci if (ret) 210662306a36Sopenharmony_ci return ret; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci pkt = (struct il_rx_pkt *)cmd.reply_page; 210962306a36Sopenharmony_ci if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { 211062306a36Sopenharmony_ci IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags); 211162306a36Sopenharmony_ci ret = -EIO; 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci if (!ret) { 211562306a36Sopenharmony_ci switch (pkt->u.rem_sta.status) { 211662306a36Sopenharmony_ci case REM_STA_SUCCESS_MSK: 211762306a36Sopenharmony_ci if (!temporary) { 211862306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 211962306a36Sopenharmony_ci il_sta_ucode_deactivate(il, sta_id); 212062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, 212162306a36Sopenharmony_ci flags_spin); 212262306a36Sopenharmony_ci } 212362306a36Sopenharmony_ci D_ASSOC("C_REM_STA PASSED\n"); 212462306a36Sopenharmony_ci break; 212562306a36Sopenharmony_ci default: 212662306a36Sopenharmony_ci ret = -EIO; 212762306a36Sopenharmony_ci IL_ERR("C_REM_STA failed\n"); 212862306a36Sopenharmony_ci break; 212962306a36Sopenharmony_ci } 213062306a36Sopenharmony_ci } 213162306a36Sopenharmony_ci il_free_pages(il, cmd.reply_page); 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci return ret; 213462306a36Sopenharmony_ci} 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci/* 213762306a36Sopenharmony_ci * il_remove_station - Remove driver's knowledge of station. 213862306a36Sopenharmony_ci */ 213962306a36Sopenharmony_ciint 214062306a36Sopenharmony_ciil_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr) 214162306a36Sopenharmony_ci{ 214262306a36Sopenharmony_ci unsigned long flags; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci if (!il_is_ready(il)) { 214562306a36Sopenharmony_ci D_INFO("Unable to remove station %pM, device not ready.\n", 214662306a36Sopenharmony_ci addr); 214762306a36Sopenharmony_ci /* 214862306a36Sopenharmony_ci * It is typical for stations to be removed when we are 214962306a36Sopenharmony_ci * going down. Return success since device will be down 215062306a36Sopenharmony_ci * soon anyway 215162306a36Sopenharmony_ci */ 215262306a36Sopenharmony_ci return 0; 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci if (WARN_ON(sta_id == IL_INVALID_STATION)) 215862306a36Sopenharmony_ci return -EINVAL; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) { 216362306a36Sopenharmony_ci D_INFO("Removing %pM but non DRIVER active\n", addr); 216462306a36Sopenharmony_ci goto out_err; 216562306a36Sopenharmony_ci } 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { 216862306a36Sopenharmony_ci D_INFO("Removing %pM but non UCODE active\n", addr); 216962306a36Sopenharmony_ci goto out_err; 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci if (il->stations[sta_id].used & IL_STA_LOCAL) { 217362306a36Sopenharmony_ci kfree(il->stations[sta_id].lq); 217462306a36Sopenharmony_ci il->stations[sta_id].lq = NULL; 217562306a36Sopenharmony_ci } 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci il->num_stations--; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci BUG_ON(il->num_stations < 0); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci return il_send_remove_station(il, addr, sta_id, false); 218662306a36Sopenharmony_ciout_err: 218762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 218862306a36Sopenharmony_ci return -EINVAL; 218962306a36Sopenharmony_ci} 219062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(il_remove_station); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci/* 219362306a36Sopenharmony_ci * il_clear_ucode_stations - clear ucode station table bits 219462306a36Sopenharmony_ci * 219562306a36Sopenharmony_ci * This function clears all the bits in the driver indicating 219662306a36Sopenharmony_ci * which stations are active in the ucode. Call when something 219762306a36Sopenharmony_ci * other than explicit station management would cause this in 219862306a36Sopenharmony_ci * the ucode, e.g. unassociated RXON. 219962306a36Sopenharmony_ci */ 220062306a36Sopenharmony_civoid 220162306a36Sopenharmony_ciil_clear_ucode_stations(struct il_priv *il) 220262306a36Sopenharmony_ci{ 220362306a36Sopenharmony_ci int i; 220462306a36Sopenharmony_ci unsigned long flags_spin; 220562306a36Sopenharmony_ci bool cleared = false; 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci D_INFO("Clearing ucode stations in driver\n"); 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 221062306a36Sopenharmony_ci for (i = 0; i < il->hw_params.max_stations; i++) { 221162306a36Sopenharmony_ci if (il->stations[i].used & IL_STA_UCODE_ACTIVE) { 221262306a36Sopenharmony_ci D_INFO("Clearing ucode active for station %d\n", i); 221362306a36Sopenharmony_ci il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; 221462306a36Sopenharmony_ci cleared = true; 221562306a36Sopenharmony_ci } 221662306a36Sopenharmony_ci } 221762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci if (!cleared) 222062306a36Sopenharmony_ci D_INFO("No active stations found to be cleared\n"); 222162306a36Sopenharmony_ci} 222262306a36Sopenharmony_ciEXPORT_SYMBOL(il_clear_ucode_stations); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci/* 222562306a36Sopenharmony_ci * il_restore_stations() - Restore driver known stations to device 222662306a36Sopenharmony_ci * 222762306a36Sopenharmony_ci * All stations considered active by driver, but not present in ucode, is 222862306a36Sopenharmony_ci * restored. 222962306a36Sopenharmony_ci * 223062306a36Sopenharmony_ci * Function sleeps. 223162306a36Sopenharmony_ci */ 223262306a36Sopenharmony_civoid 223362306a36Sopenharmony_ciil_restore_stations(struct il_priv *il) 223462306a36Sopenharmony_ci{ 223562306a36Sopenharmony_ci struct il_addsta_cmd sta_cmd; 223662306a36Sopenharmony_ci struct il_link_quality_cmd lq; 223762306a36Sopenharmony_ci unsigned long flags_spin; 223862306a36Sopenharmony_ci int i; 223962306a36Sopenharmony_ci bool found = false; 224062306a36Sopenharmony_ci int ret; 224162306a36Sopenharmony_ci bool send_lq; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci if (!il_is_ready(il)) { 224462306a36Sopenharmony_ci D_INFO("Not ready yet, not restoring any stations.\n"); 224562306a36Sopenharmony_ci return; 224662306a36Sopenharmony_ci } 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci D_ASSOC("Restoring all known stations ... start.\n"); 224962306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 225062306a36Sopenharmony_ci for (i = 0; i < il->hw_params.max_stations; i++) { 225162306a36Sopenharmony_ci if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) && 225262306a36Sopenharmony_ci !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) { 225362306a36Sopenharmony_ci D_ASSOC("Restoring sta %pM\n", 225462306a36Sopenharmony_ci il->stations[i].sta.sta.addr); 225562306a36Sopenharmony_ci il->stations[i].sta.mode = 0; 225662306a36Sopenharmony_ci il->stations[i].used |= IL_STA_UCODE_INPROGRESS; 225762306a36Sopenharmony_ci found = true; 225862306a36Sopenharmony_ci } 225962306a36Sopenharmony_ci } 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci for (i = 0; i < il->hw_params.max_stations; i++) { 226262306a36Sopenharmony_ci if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) { 226362306a36Sopenharmony_ci memcpy(&sta_cmd, &il->stations[i].sta, 226462306a36Sopenharmony_ci sizeof(struct il_addsta_cmd)); 226562306a36Sopenharmony_ci send_lq = false; 226662306a36Sopenharmony_ci if (il->stations[i].lq) { 226762306a36Sopenharmony_ci memcpy(&lq, il->stations[i].lq, 226862306a36Sopenharmony_ci sizeof(struct il_link_quality_cmd)); 226962306a36Sopenharmony_ci send_lq = true; 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 227262306a36Sopenharmony_ci ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); 227362306a36Sopenharmony_ci if (ret) { 227462306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 227562306a36Sopenharmony_ci IL_ERR("Adding station %pM failed.\n", 227662306a36Sopenharmony_ci il->stations[i].sta.sta.addr); 227762306a36Sopenharmony_ci il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE; 227862306a36Sopenharmony_ci il->stations[i].used &= 227962306a36Sopenharmony_ci ~IL_STA_UCODE_INPROGRESS; 228062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, 228162306a36Sopenharmony_ci flags_spin); 228262306a36Sopenharmony_ci } 228362306a36Sopenharmony_ci /* 228462306a36Sopenharmony_ci * Rate scaling has already been initialized, send 228562306a36Sopenharmony_ci * current LQ command 228662306a36Sopenharmony_ci */ 228762306a36Sopenharmony_ci if (send_lq) 228862306a36Sopenharmony_ci il_send_lq_cmd(il, &lq, CMD_SYNC, true); 228962306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 229062306a36Sopenharmony_ci il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS; 229162306a36Sopenharmony_ci } 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 229562306a36Sopenharmony_ci if (!found) 229662306a36Sopenharmony_ci D_INFO("Restoring all known stations" 229762306a36Sopenharmony_ci " .... no stations to be restored.\n"); 229862306a36Sopenharmony_ci else 229962306a36Sopenharmony_ci D_INFO("Restoring all known stations" " .... complete.\n"); 230062306a36Sopenharmony_ci} 230162306a36Sopenharmony_ciEXPORT_SYMBOL(il_restore_stations); 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ciint 230462306a36Sopenharmony_ciil_get_free_ucode_key_idx(struct il_priv *il) 230562306a36Sopenharmony_ci{ 230662306a36Sopenharmony_ci int i; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci for (i = 0; i < il->sta_key_max_num; i++) 230962306a36Sopenharmony_ci if (!test_and_set_bit(i, &il->ucode_key_table)) 231062306a36Sopenharmony_ci return i; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci return WEP_INVALID_OFFSET; 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ciEXPORT_SYMBOL(il_get_free_ucode_key_idx); 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_civoid 231762306a36Sopenharmony_ciil_dealloc_bcast_stations(struct il_priv *il) 231862306a36Sopenharmony_ci{ 231962306a36Sopenharmony_ci unsigned long flags; 232062306a36Sopenharmony_ci int i; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 232362306a36Sopenharmony_ci for (i = 0; i < il->hw_params.max_stations; i++) { 232462306a36Sopenharmony_ci if (!(il->stations[i].used & IL_STA_BCAST)) 232562306a36Sopenharmony_ci continue; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; 232862306a36Sopenharmony_ci il->num_stations--; 232962306a36Sopenharmony_ci BUG_ON(il->num_stations < 0); 233062306a36Sopenharmony_ci kfree(il->stations[i].lq); 233162306a36Sopenharmony_ci il->stations[i].lq = NULL; 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 233462306a36Sopenharmony_ci} 233562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(il_dealloc_bcast_stations); 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 233862306a36Sopenharmony_cistatic void 233962306a36Sopenharmony_ciil_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) 234062306a36Sopenharmony_ci{ 234162306a36Sopenharmony_ci int i; 234262306a36Sopenharmony_ci D_RATE("lq station id 0x%x\n", lq->sta_id); 234362306a36Sopenharmony_ci D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk, 234462306a36Sopenharmony_ci lq->general_params.dual_stream_ant_msk); 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) 234762306a36Sopenharmony_ci D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags); 234862306a36Sopenharmony_ci} 234962306a36Sopenharmony_ci#else 235062306a36Sopenharmony_cistatic inline void 235162306a36Sopenharmony_ciil_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) 235262306a36Sopenharmony_ci{ 235362306a36Sopenharmony_ci} 235462306a36Sopenharmony_ci#endif 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci/* 235762306a36Sopenharmony_ci * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity 235862306a36Sopenharmony_ci * 235962306a36Sopenharmony_ci * It sometimes happens when a HT rate has been in use and we 236062306a36Sopenharmony_ci * loose connectivity with AP then mac80211 will first tell us that the 236162306a36Sopenharmony_ci * current channel is not HT anymore before removing the station. In such a 236262306a36Sopenharmony_ci * scenario the RXON flags will be updated to indicate we are not 236362306a36Sopenharmony_ci * communicating HT anymore, but the LQ command may still contain HT rates. 236462306a36Sopenharmony_ci * Test for this to prevent driver from sending LQ command between the time 236562306a36Sopenharmony_ci * RXON flags are updated and when LQ command is updated. 236662306a36Sopenharmony_ci */ 236762306a36Sopenharmony_cistatic bool 236862306a36Sopenharmony_ciil_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq) 236962306a36Sopenharmony_ci{ 237062306a36Sopenharmony_ci int i; 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci if (il->ht.enabled) 237362306a36Sopenharmony_ci return true; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci D_INFO("Channel %u is not an HT channel\n", il->active.channel); 237662306a36Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { 237762306a36Sopenharmony_ci if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) { 237862306a36Sopenharmony_ci D_INFO("idx %d of LQ expects HT channel\n", i); 237962306a36Sopenharmony_ci return false; 238062306a36Sopenharmony_ci } 238162306a36Sopenharmony_ci } 238262306a36Sopenharmony_ci return true; 238362306a36Sopenharmony_ci} 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci/* 238662306a36Sopenharmony_ci * il_send_lq_cmd() - Send link quality command 238762306a36Sopenharmony_ci * @init: This command is sent as part of station initialization right 238862306a36Sopenharmony_ci * after station has been added. 238962306a36Sopenharmony_ci * 239062306a36Sopenharmony_ci * The link quality command is sent as the last step of station creation. 239162306a36Sopenharmony_ci * This is the special case in which init is set and we call a callback in 239262306a36Sopenharmony_ci * this case to clear the state indicating that station creation is in 239362306a36Sopenharmony_ci * progress. 239462306a36Sopenharmony_ci */ 239562306a36Sopenharmony_ciint 239662306a36Sopenharmony_ciil_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq, 239762306a36Sopenharmony_ci u8 flags, bool init) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci int ret = 0; 240062306a36Sopenharmony_ci unsigned long flags_spin; 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci struct il_host_cmd cmd = { 240362306a36Sopenharmony_ci .id = C_TX_LINK_QUALITY_CMD, 240462306a36Sopenharmony_ci .len = sizeof(struct il_link_quality_cmd), 240562306a36Sopenharmony_ci .flags = flags, 240662306a36Sopenharmony_ci .data = lq, 240762306a36Sopenharmony_ci }; 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci if (WARN_ON(lq->sta_id == IL_INVALID_STATION)) 241062306a36Sopenharmony_ci return -EINVAL; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 241362306a36Sopenharmony_ci if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) { 241462306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 241562306a36Sopenharmony_ci return -EINVAL; 241662306a36Sopenharmony_ci } 241762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci il_dump_lq_cmd(il, lq); 242062306a36Sopenharmony_ci BUG_ON(init && (cmd.flags & CMD_ASYNC)); 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci if (il_is_lq_table_valid(il, lq)) 242362306a36Sopenharmony_ci ret = il_send_cmd(il, &cmd); 242462306a36Sopenharmony_ci else 242562306a36Sopenharmony_ci ret = -EINVAL; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci if (cmd.flags & CMD_ASYNC) 242862306a36Sopenharmony_ci return ret; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci if (init) { 243162306a36Sopenharmony_ci D_INFO("init LQ command complete," 243262306a36Sopenharmony_ci " clearing sta addition status for sta %d\n", 243362306a36Sopenharmony_ci lq->sta_id); 243462306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags_spin); 243562306a36Sopenharmony_ci il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS; 243662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags_spin); 243762306a36Sopenharmony_ci } 243862306a36Sopenharmony_ci return ret; 243962306a36Sopenharmony_ci} 244062306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_lq_cmd); 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ciint 244362306a36Sopenharmony_ciil_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 244462306a36Sopenharmony_ci struct ieee80211_sta *sta) 244562306a36Sopenharmony_ci{ 244662306a36Sopenharmony_ci struct il_priv *il = hw->priv; 244762306a36Sopenharmony_ci struct il_station_priv_common *sta_common = (void *)sta->drv_priv; 244862306a36Sopenharmony_ci int ret; 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci mutex_lock(&il->mutex); 245162306a36Sopenharmony_ci D_MAC80211("enter station %pM\n", sta->addr); 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci ret = il_remove_station(il, sta_common->sta_id, sta->addr); 245462306a36Sopenharmony_ci if (ret) 245562306a36Sopenharmony_ci IL_ERR("Error removing station %pM\n", sta->addr); 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci D_MAC80211("leave ret %d\n", ret); 245862306a36Sopenharmony_ci mutex_unlock(&il->mutex); 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci return ret; 246162306a36Sopenharmony_ci} 246262306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_sta_remove); 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci/************************** RX-FUNCTIONS ****************************/ 246562306a36Sopenharmony_ci/* 246662306a36Sopenharmony_ci * Rx theory of operation 246762306a36Sopenharmony_ci * 246862306a36Sopenharmony_ci * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), 246962306a36Sopenharmony_ci * each of which point to Receive Buffers to be filled by the NIC. These get 247062306a36Sopenharmony_ci * used not only for Rx frames, but for any command response or notification 247162306a36Sopenharmony_ci * from the NIC. The driver and NIC manage the Rx buffers by means 247262306a36Sopenharmony_ci * of idxes into the circular buffer. 247362306a36Sopenharmony_ci * 247462306a36Sopenharmony_ci * Rx Queue Indexes 247562306a36Sopenharmony_ci * The host/firmware share two idx registers for managing the Rx buffers. 247662306a36Sopenharmony_ci * 247762306a36Sopenharmony_ci * The READ idx maps to the first position that the firmware may be writing 247862306a36Sopenharmony_ci * to -- the driver can read up to (but not including) this position and get 247962306a36Sopenharmony_ci * good data. 248062306a36Sopenharmony_ci * The READ idx is managed by the firmware once the card is enabled. 248162306a36Sopenharmony_ci * 248262306a36Sopenharmony_ci * The WRITE idx maps to the last position the driver has read from -- the 248362306a36Sopenharmony_ci * position preceding WRITE is the last slot the firmware can place a packet. 248462306a36Sopenharmony_ci * 248562306a36Sopenharmony_ci * The queue is empty (no good data) if WRITE = READ - 1, and is full if 248662306a36Sopenharmony_ci * WRITE = READ. 248762306a36Sopenharmony_ci * 248862306a36Sopenharmony_ci * During initialization, the host sets up the READ queue position to the first 248962306a36Sopenharmony_ci * IDX position, and WRITE to the last (READ - 1 wrapped) 249062306a36Sopenharmony_ci * 249162306a36Sopenharmony_ci * When the firmware places a packet in a buffer, it will advance the READ idx 249262306a36Sopenharmony_ci * and fire the RX interrupt. The driver can then query the READ idx and 249362306a36Sopenharmony_ci * process as many packets as possible, moving the WRITE idx forward as it 249462306a36Sopenharmony_ci * resets the Rx queue buffers with new memory. 249562306a36Sopenharmony_ci * 249662306a36Sopenharmony_ci * The management in the driver is as follows: 249762306a36Sopenharmony_ci * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When 249862306a36Sopenharmony_ci * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled 249962306a36Sopenharmony_ci * to replenish the iwl->rxq->rx_free. 250062306a36Sopenharmony_ci * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the 250162306a36Sopenharmony_ci * iwl->rxq is replenished and the READ IDX is updated (updating the 250262306a36Sopenharmony_ci * 'processed' and 'read' driver idxes as well) 250362306a36Sopenharmony_ci * + A received packet is processed and handed to the kernel network stack, 250462306a36Sopenharmony_ci * detached from the iwl->rxq. The driver 'processed' idx is updated. 250562306a36Sopenharmony_ci * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free 250662306a36Sopenharmony_ci * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ 250762306a36Sopenharmony_ci * IDX is not incremented and iwl->status(RX_STALLED) is set. If there 250862306a36Sopenharmony_ci * were enough free buffers and RX_STALLED is set it is cleared. 250962306a36Sopenharmony_ci * 251062306a36Sopenharmony_ci * 251162306a36Sopenharmony_ci * Driver sequence: 251262306a36Sopenharmony_ci * 251362306a36Sopenharmony_ci * il_rx_queue_alloc() Allocates rx_free 251462306a36Sopenharmony_ci * il_rx_replenish() Replenishes rx_free list from rx_used, and calls 251562306a36Sopenharmony_ci * il_rx_queue_restock 251662306a36Sopenharmony_ci * il_rx_queue_restock() Moves available buffers from rx_free into Rx 251762306a36Sopenharmony_ci * queue, updates firmware pointers, and updates 251862306a36Sopenharmony_ci * the WRITE idx. If insufficient rx_free buffers 251962306a36Sopenharmony_ci * are available, schedules il_rx_replenish 252062306a36Sopenharmony_ci * 252162306a36Sopenharmony_ci * -- enable interrupts -- 252262306a36Sopenharmony_ci * ISR - il_rx() Detach il_rx_bufs from pool up to the 252362306a36Sopenharmony_ci * READ IDX, detaching the SKB from the pool. 252462306a36Sopenharmony_ci * Moves the packet buffer from queue to rx_used. 252562306a36Sopenharmony_ci * Calls il_rx_queue_restock to refill any empty 252662306a36Sopenharmony_ci * slots. 252762306a36Sopenharmony_ci * ... 252862306a36Sopenharmony_ci * 252962306a36Sopenharmony_ci */ 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci/* 253262306a36Sopenharmony_ci * il_rx_queue_space - Return number of free slots available in queue. 253362306a36Sopenharmony_ci */ 253462306a36Sopenharmony_ciint 253562306a36Sopenharmony_ciil_rx_queue_space(const struct il_rx_queue *q) 253662306a36Sopenharmony_ci{ 253762306a36Sopenharmony_ci int s = q->read - q->write; 253862306a36Sopenharmony_ci if (s <= 0) 253962306a36Sopenharmony_ci s += RX_QUEUE_SIZE; 254062306a36Sopenharmony_ci /* keep some buffer to not confuse full and empty queue */ 254162306a36Sopenharmony_ci s -= 2; 254262306a36Sopenharmony_ci if (s < 0) 254362306a36Sopenharmony_ci s = 0; 254462306a36Sopenharmony_ci return s; 254562306a36Sopenharmony_ci} 254662306a36Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_space); 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci/* 254962306a36Sopenharmony_ci * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue 255062306a36Sopenharmony_ci */ 255162306a36Sopenharmony_civoid 255262306a36Sopenharmony_ciil_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q) 255362306a36Sopenharmony_ci{ 255462306a36Sopenharmony_ci unsigned long flags; 255562306a36Sopenharmony_ci u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg; 255662306a36Sopenharmony_ci u32 reg; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci spin_lock_irqsave(&q->lock, flags); 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci if (q->need_update == 0) 256162306a36Sopenharmony_ci goto exit_unlock; 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci /* If power-saving is in use, make sure device is awake */ 256462306a36Sopenharmony_ci if (test_bit(S_POWER_PMI, &il->status)) { 256562306a36Sopenharmony_ci reg = _il_rd(il, CSR_UCODE_DRV_GP1); 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_ci if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { 256862306a36Sopenharmony_ci D_INFO("Rx queue requesting wakeup," " GP1 = 0x%x\n", 256962306a36Sopenharmony_ci reg); 257062306a36Sopenharmony_ci il_set_bit(il, CSR_GP_CNTRL, 257162306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 257262306a36Sopenharmony_ci goto exit_unlock; 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci q->write_actual = (q->write & ~0x7); 257662306a36Sopenharmony_ci il_wr(il, rx_wrt_ptr_reg, q->write_actual); 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci /* Else device is assumed to be awake */ 257962306a36Sopenharmony_ci } else { 258062306a36Sopenharmony_ci /* Device expects a multiple of 8 */ 258162306a36Sopenharmony_ci q->write_actual = (q->write & ~0x7); 258262306a36Sopenharmony_ci il_wr(il, rx_wrt_ptr_reg, q->write_actual); 258362306a36Sopenharmony_ci } 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci q->need_update = 0; 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ciexit_unlock: 258862306a36Sopenharmony_ci spin_unlock_irqrestore(&q->lock, flags); 258962306a36Sopenharmony_ci} 259062306a36Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_update_write_ptr); 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ciint 259362306a36Sopenharmony_ciil_rx_queue_alloc(struct il_priv *il) 259462306a36Sopenharmony_ci{ 259562306a36Sopenharmony_ci struct il_rx_queue *rxq = &il->rxq; 259662306a36Sopenharmony_ci struct device *dev = &il->pci_dev->dev; 259762306a36Sopenharmony_ci int i; 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci spin_lock_init(&rxq->lock); 260062306a36Sopenharmony_ci INIT_LIST_HEAD(&rxq->rx_free); 260162306a36Sopenharmony_ci INIT_LIST_HEAD(&rxq->rx_used); 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ 260462306a36Sopenharmony_ci rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma, 260562306a36Sopenharmony_ci GFP_KERNEL); 260662306a36Sopenharmony_ci if (!rxq->bd) 260762306a36Sopenharmony_ci goto err_bd; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct il_rb_status), 261062306a36Sopenharmony_ci &rxq->rb_stts_dma, GFP_KERNEL); 261162306a36Sopenharmony_ci if (!rxq->rb_stts) 261262306a36Sopenharmony_ci goto err_rb; 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci /* Fill the rx_used queue with _all_ of the Rx buffers */ 261562306a36Sopenharmony_ci for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) 261662306a36Sopenharmony_ci list_add_tail(&rxq->pool[i].list, &rxq->rx_used); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci /* Set us so that we have processed and used all buffers, but have 261962306a36Sopenharmony_ci * not restocked the Rx queue with fresh buffers */ 262062306a36Sopenharmony_ci rxq->read = rxq->write = 0; 262162306a36Sopenharmony_ci rxq->write_actual = 0; 262262306a36Sopenharmony_ci rxq->free_count = 0; 262362306a36Sopenharmony_ci rxq->need_update = 0; 262462306a36Sopenharmony_ci return 0; 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_cierr_rb: 262762306a36Sopenharmony_ci dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, 262862306a36Sopenharmony_ci rxq->bd_dma); 262962306a36Sopenharmony_cierr_bd: 263062306a36Sopenharmony_ci return -ENOMEM; 263162306a36Sopenharmony_ci} 263262306a36Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_alloc); 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_civoid 263562306a36Sopenharmony_ciil_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb) 263662306a36Sopenharmony_ci{ 263762306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 263862306a36Sopenharmony_ci struct il_spectrum_notification *report = &(pkt->u.spectrum_notif); 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci if (!report->state) { 264162306a36Sopenharmony_ci D_11H("Spectrum Measure Notification: Start\n"); 264262306a36Sopenharmony_ci return; 264362306a36Sopenharmony_ci } 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci memcpy(&il->measure_report, report, sizeof(*report)); 264662306a36Sopenharmony_ci il->measurement_status |= MEASUREMENT_READY; 264762306a36Sopenharmony_ci} 264862306a36Sopenharmony_ciEXPORT_SYMBOL(il_hdl_spectrum_measurement); 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci/* 265162306a36Sopenharmony_ci * returns non-zero if packet should be dropped 265262306a36Sopenharmony_ci */ 265362306a36Sopenharmony_ciint 265462306a36Sopenharmony_ciil_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr, 265562306a36Sopenharmony_ci u32 decrypt_res, struct ieee80211_rx_status *stats) 265662306a36Sopenharmony_ci{ 265762306a36Sopenharmony_ci u16 fc = le16_to_cpu(hdr->frame_control); 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci /* 266062306a36Sopenharmony_ci * All contexts have the same setting here due to it being 266162306a36Sopenharmony_ci * a module parameter, so OK to check any context. 266262306a36Sopenharmony_ci */ 266362306a36Sopenharmony_ci if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK) 266462306a36Sopenharmony_ci return 0; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci if (!(fc & IEEE80211_FCTL_PROTECTED)) 266762306a36Sopenharmony_ci return 0; 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci D_RX("decrypt_res:0x%x\n", decrypt_res); 267062306a36Sopenharmony_ci switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { 267162306a36Sopenharmony_ci case RX_RES_STATUS_SEC_TYPE_TKIP: 267262306a36Sopenharmony_ci /* The uCode has got a bad phase 1 Key, pushes the packet. 267362306a36Sopenharmony_ci * Decryption will be done in SW. */ 267462306a36Sopenharmony_ci if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == 267562306a36Sopenharmony_ci RX_RES_STATUS_BAD_KEY_TTAK) 267662306a36Sopenharmony_ci break; 267762306a36Sopenharmony_ci fallthrough; 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci case RX_RES_STATUS_SEC_TYPE_WEP: 268062306a36Sopenharmony_ci if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == 268162306a36Sopenharmony_ci RX_RES_STATUS_BAD_ICV_MIC) { 268262306a36Sopenharmony_ci /* bad ICV, the packet is destroyed since the 268362306a36Sopenharmony_ci * decryption is inplace, drop it */ 268462306a36Sopenharmony_ci D_RX("Packet destroyed\n"); 268562306a36Sopenharmony_ci return -1; 268662306a36Sopenharmony_ci } 268762306a36Sopenharmony_ci fallthrough; 268862306a36Sopenharmony_ci case RX_RES_STATUS_SEC_TYPE_CCMP: 268962306a36Sopenharmony_ci if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == 269062306a36Sopenharmony_ci RX_RES_STATUS_DECRYPT_OK) { 269162306a36Sopenharmony_ci D_RX("hw decrypt successfully!!!\n"); 269262306a36Sopenharmony_ci stats->flag |= RX_FLAG_DECRYPTED; 269362306a36Sopenharmony_ci } 269462306a36Sopenharmony_ci break; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci default: 269762306a36Sopenharmony_ci break; 269862306a36Sopenharmony_ci } 269962306a36Sopenharmony_ci return 0; 270062306a36Sopenharmony_ci} 270162306a36Sopenharmony_ciEXPORT_SYMBOL(il_set_decrypted_flag); 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci/* 270462306a36Sopenharmony_ci * il_txq_update_write_ptr - Send new write idx to hardware 270562306a36Sopenharmony_ci */ 270662306a36Sopenharmony_civoid 270762306a36Sopenharmony_ciil_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq) 270862306a36Sopenharmony_ci{ 270962306a36Sopenharmony_ci u32 reg = 0; 271062306a36Sopenharmony_ci int txq_id = txq->q.id; 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci if (txq->need_update == 0) 271362306a36Sopenharmony_ci return; 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci /* if we're trying to save power */ 271662306a36Sopenharmony_ci if (test_bit(S_POWER_PMI, &il->status)) { 271762306a36Sopenharmony_ci /* wake up nic if it's powered down ... 271862306a36Sopenharmony_ci * uCode will wake up, and interrupt us again, so next 271962306a36Sopenharmony_ci * time we'll skip this part. */ 272062306a36Sopenharmony_ci reg = _il_rd(il, CSR_UCODE_DRV_GP1); 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { 272362306a36Sopenharmony_ci D_INFO("Tx queue %d requesting wakeup," " GP1 = 0x%x\n", 272462306a36Sopenharmony_ci txq_id, reg); 272562306a36Sopenharmony_ci il_set_bit(il, CSR_GP_CNTRL, 272662306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 272762306a36Sopenharmony_ci return; 272862306a36Sopenharmony_ci } 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci /* 273362306a36Sopenharmony_ci * else not in power-save mode, 273462306a36Sopenharmony_ci * uCode will never sleep when we're 273562306a36Sopenharmony_ci * trying to tx (during RFKILL, we're not trying to tx). 273662306a36Sopenharmony_ci */ 273762306a36Sopenharmony_ci } else 273862306a36Sopenharmony_ci _il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); 273962306a36Sopenharmony_ci txq->need_update = 0; 274062306a36Sopenharmony_ci} 274162306a36Sopenharmony_ciEXPORT_SYMBOL(il_txq_update_write_ptr); 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci/* 274462306a36Sopenharmony_ci * il_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's 274562306a36Sopenharmony_ci */ 274662306a36Sopenharmony_civoid 274762306a36Sopenharmony_ciil_tx_queue_unmap(struct il_priv *il, int txq_id) 274862306a36Sopenharmony_ci{ 274962306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 275062306a36Sopenharmony_ci struct il_queue *q = &txq->q; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci if (q->n_bd == 0) 275362306a36Sopenharmony_ci return; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci while (q->write_ptr != q->read_ptr) { 275662306a36Sopenharmony_ci il->ops->txq_free_tfd(il, txq); 275762306a36Sopenharmony_ci q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); 275862306a36Sopenharmony_ci } 275962306a36Sopenharmony_ci} 276062306a36Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_unmap); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci/* 276362306a36Sopenharmony_ci * il_tx_queue_free - Deallocate DMA queue. 276462306a36Sopenharmony_ci * @txq: Transmit queue to deallocate. 276562306a36Sopenharmony_ci * 276662306a36Sopenharmony_ci * Empty queue by removing and destroying all BD's. 276762306a36Sopenharmony_ci * Free all buffers. 276862306a36Sopenharmony_ci * 0-fill, but do not free "txq" descriptor structure. 276962306a36Sopenharmony_ci */ 277062306a36Sopenharmony_civoid 277162306a36Sopenharmony_ciil_tx_queue_free(struct il_priv *il, int txq_id) 277262306a36Sopenharmony_ci{ 277362306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 277462306a36Sopenharmony_ci struct device *dev = &il->pci_dev->dev; 277562306a36Sopenharmony_ci int i; 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci il_tx_queue_unmap(il, txq_id); 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci /* De-alloc array of command/tx buffers */ 278062306a36Sopenharmony_ci if (txq->cmd) { 278162306a36Sopenharmony_ci for (i = 0; i < TFD_TX_CMD_SLOTS; i++) 278262306a36Sopenharmony_ci kfree(txq->cmd[i]); 278362306a36Sopenharmony_ci } 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci /* De-alloc circular buffer of TFDs */ 278662306a36Sopenharmony_ci if (txq->q.n_bd) 278762306a36Sopenharmony_ci dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, 278862306a36Sopenharmony_ci txq->tfds, txq->q.dma_addr); 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci /* De-alloc array of per-TFD driver data */ 279162306a36Sopenharmony_ci kfree(txq->skbs); 279262306a36Sopenharmony_ci txq->skbs = NULL; 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci /* deallocate arrays */ 279562306a36Sopenharmony_ci kfree(txq->cmd); 279662306a36Sopenharmony_ci kfree(txq->meta); 279762306a36Sopenharmony_ci txq->cmd = NULL; 279862306a36Sopenharmony_ci txq->meta = NULL; 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci /* 0-fill queue descriptor structure */ 280162306a36Sopenharmony_ci memset(txq, 0, sizeof(*txq)); 280262306a36Sopenharmony_ci} 280362306a36Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_free); 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci/* 280662306a36Sopenharmony_ci * il_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue 280762306a36Sopenharmony_ci */ 280862306a36Sopenharmony_civoid 280962306a36Sopenharmony_ciil_cmd_queue_unmap(struct il_priv *il) 281062306a36Sopenharmony_ci{ 281162306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[il->cmd_queue]; 281262306a36Sopenharmony_ci struct il_queue *q = &txq->q; 281362306a36Sopenharmony_ci int i; 281462306a36Sopenharmony_ci 281562306a36Sopenharmony_ci if (q->n_bd == 0) 281662306a36Sopenharmony_ci return; 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci while (q->read_ptr != q->write_ptr) { 281962306a36Sopenharmony_ci i = il_get_cmd_idx(q, q->read_ptr, 0); 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci if (txq->meta[i].flags & CMD_MAPPED) { 282262306a36Sopenharmony_ci dma_unmap_single(&il->pci_dev->dev, 282362306a36Sopenharmony_ci dma_unmap_addr(&txq->meta[i], mapping), 282462306a36Sopenharmony_ci dma_unmap_len(&txq->meta[i], len), 282562306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 282662306a36Sopenharmony_ci txq->meta[i].flags = 0; 282762306a36Sopenharmony_ci } 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); 283062306a36Sopenharmony_ci } 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci i = q->n_win; 283362306a36Sopenharmony_ci if (txq->meta[i].flags & CMD_MAPPED) { 283462306a36Sopenharmony_ci dma_unmap_single(&il->pci_dev->dev, 283562306a36Sopenharmony_ci dma_unmap_addr(&txq->meta[i], mapping), 283662306a36Sopenharmony_ci dma_unmap_len(&txq->meta[i], len), 283762306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 283862306a36Sopenharmony_ci txq->meta[i].flags = 0; 283962306a36Sopenharmony_ci } 284062306a36Sopenharmony_ci} 284162306a36Sopenharmony_ciEXPORT_SYMBOL(il_cmd_queue_unmap); 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_ci/* 284462306a36Sopenharmony_ci * il_cmd_queue_free - Deallocate DMA queue. 284562306a36Sopenharmony_ci * 284662306a36Sopenharmony_ci * Empty queue by removing and destroying all BD's. 284762306a36Sopenharmony_ci * Free all buffers. 284862306a36Sopenharmony_ci * 0-fill, but do not free "txq" descriptor structure. 284962306a36Sopenharmony_ci */ 285062306a36Sopenharmony_civoid 285162306a36Sopenharmony_ciil_cmd_queue_free(struct il_priv *il) 285262306a36Sopenharmony_ci{ 285362306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[il->cmd_queue]; 285462306a36Sopenharmony_ci struct device *dev = &il->pci_dev->dev; 285562306a36Sopenharmony_ci int i; 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci il_cmd_queue_unmap(il); 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci /* De-alloc array of command/tx buffers */ 286062306a36Sopenharmony_ci if (txq->cmd) { 286162306a36Sopenharmony_ci for (i = 0; i <= TFD_CMD_SLOTS; i++) 286262306a36Sopenharmony_ci kfree(txq->cmd[i]); 286362306a36Sopenharmony_ci } 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci /* De-alloc circular buffer of TFDs */ 286662306a36Sopenharmony_ci if (txq->q.n_bd) 286762306a36Sopenharmony_ci dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, 286862306a36Sopenharmony_ci txq->tfds, txq->q.dma_addr); 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci /* deallocate arrays */ 287162306a36Sopenharmony_ci kfree(txq->cmd); 287262306a36Sopenharmony_ci kfree(txq->meta); 287362306a36Sopenharmony_ci txq->cmd = NULL; 287462306a36Sopenharmony_ci txq->meta = NULL; 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci /* 0-fill queue descriptor structure */ 287762306a36Sopenharmony_ci memset(txq, 0, sizeof(*txq)); 287862306a36Sopenharmony_ci} 287962306a36Sopenharmony_ciEXPORT_SYMBOL(il_cmd_queue_free); 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** 288262306a36Sopenharmony_ci * DMA services 288362306a36Sopenharmony_ci * 288462306a36Sopenharmony_ci * Theory of operation 288562306a36Sopenharmony_ci * 288662306a36Sopenharmony_ci * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer 288762306a36Sopenharmony_ci * of buffer descriptors, each of which points to one or more data buffers for 288862306a36Sopenharmony_ci * the device to read from or fill. Driver and device exchange status of each 288962306a36Sopenharmony_ci * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty 289062306a36Sopenharmony_ci * entries in each circular buffer, to protect against confusing empty and full 289162306a36Sopenharmony_ci * queue states. 289262306a36Sopenharmony_ci * 289362306a36Sopenharmony_ci * The device reads or writes the data in the queues via the device's several 289462306a36Sopenharmony_ci * DMA/FIFO channels. Each queue is mapped to a single DMA channel. 289562306a36Sopenharmony_ci * 289662306a36Sopenharmony_ci * For Tx queue, there are low mark and high mark limits. If, after queuing 289762306a36Sopenharmony_ci * the packet for Tx, free space become < low mark, Tx queue stopped. When 289862306a36Sopenharmony_ci * reclaiming packets (on 'tx done IRQ), if free space become > high mark, 289962306a36Sopenharmony_ci * Tx queue resumed. 290062306a36Sopenharmony_ci * 290162306a36Sopenharmony_ci * See more detailed info in 4965.h. 290262306a36Sopenharmony_ci ***************************************************/ 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ciint 290562306a36Sopenharmony_ciil_queue_space(const struct il_queue *q) 290662306a36Sopenharmony_ci{ 290762306a36Sopenharmony_ci int s = q->read_ptr - q->write_ptr; 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci if (q->read_ptr > q->write_ptr) 291062306a36Sopenharmony_ci s -= q->n_bd; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci if (s <= 0) 291362306a36Sopenharmony_ci s += q->n_win; 291462306a36Sopenharmony_ci /* keep some reserve to not confuse empty and full situations */ 291562306a36Sopenharmony_ci s -= 2; 291662306a36Sopenharmony_ci if (s < 0) 291762306a36Sopenharmony_ci s = 0; 291862306a36Sopenharmony_ci return s; 291962306a36Sopenharmony_ci} 292062306a36Sopenharmony_ciEXPORT_SYMBOL(il_queue_space); 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci/* 292462306a36Sopenharmony_ci * il_queue_init - Initialize queue's high/low-water and read/write idxes 292562306a36Sopenharmony_ci */ 292662306a36Sopenharmony_cistatic int 292762306a36Sopenharmony_ciil_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id) 292862306a36Sopenharmony_ci{ 292962306a36Sopenharmony_ci /* 293062306a36Sopenharmony_ci * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise 293162306a36Sopenharmony_ci * il_queue_inc_wrap and il_queue_dec_wrap are broken. 293262306a36Sopenharmony_ci */ 293362306a36Sopenharmony_ci BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); 293462306a36Sopenharmony_ci /* FIXME: remove q->n_bd */ 293562306a36Sopenharmony_ci q->n_bd = TFD_QUEUE_SIZE_MAX; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci q->n_win = slots; 293862306a36Sopenharmony_ci q->id = id; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci /* slots_must be power-of-two size, otherwise 294162306a36Sopenharmony_ci * il_get_cmd_idx is broken. */ 294262306a36Sopenharmony_ci BUG_ON(!is_power_of_2(slots)); 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci q->low_mark = q->n_win / 4; 294562306a36Sopenharmony_ci if (q->low_mark < 4) 294662306a36Sopenharmony_ci q->low_mark = 4; 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_ci q->high_mark = q->n_win / 8; 294962306a36Sopenharmony_ci if (q->high_mark < 2) 295062306a36Sopenharmony_ci q->high_mark = 2; 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci q->write_ptr = q->read_ptr = 0; 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci return 0; 295562306a36Sopenharmony_ci} 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci/* 295862306a36Sopenharmony_ci * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue 295962306a36Sopenharmony_ci */ 296062306a36Sopenharmony_cistatic int 296162306a36Sopenharmony_ciil_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id) 296262306a36Sopenharmony_ci{ 296362306a36Sopenharmony_ci struct device *dev = &il->pci_dev->dev; 296462306a36Sopenharmony_ci size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX; 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_ci /* Driver ilate data, only for Tx (not command) queues, 296762306a36Sopenharmony_ci * not shared with device. */ 296862306a36Sopenharmony_ci if (id != il->cmd_queue) { 296962306a36Sopenharmony_ci txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, 297062306a36Sopenharmony_ci sizeof(struct sk_buff *), 297162306a36Sopenharmony_ci GFP_KERNEL); 297262306a36Sopenharmony_ci if (!txq->skbs) { 297362306a36Sopenharmony_ci IL_ERR("Fail to alloc skbs\n"); 297462306a36Sopenharmony_ci goto error; 297562306a36Sopenharmony_ci } 297662306a36Sopenharmony_ci } else 297762306a36Sopenharmony_ci txq->skbs = NULL; 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci /* Circular buffer of transmit frame descriptors (TFDs), 298062306a36Sopenharmony_ci * shared with device */ 298162306a36Sopenharmony_ci txq->tfds = 298262306a36Sopenharmony_ci dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL); 298362306a36Sopenharmony_ci if (!txq->tfds) 298462306a36Sopenharmony_ci goto error; 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci txq->q.id = id; 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_ci return 0; 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_cierror: 299162306a36Sopenharmony_ci kfree(txq->skbs); 299262306a36Sopenharmony_ci txq->skbs = NULL; 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci return -ENOMEM; 299562306a36Sopenharmony_ci} 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci/* 299862306a36Sopenharmony_ci * il_tx_queue_init - Allocate and initialize one tx/cmd queue 299962306a36Sopenharmony_ci */ 300062306a36Sopenharmony_ciint 300162306a36Sopenharmony_ciil_tx_queue_init(struct il_priv *il, u32 txq_id) 300262306a36Sopenharmony_ci{ 300362306a36Sopenharmony_ci int i, len, ret; 300462306a36Sopenharmony_ci int slots, actual_slots; 300562306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci /* 300862306a36Sopenharmony_ci * Alloc buffer array for commands (Tx or other types of commands). 300962306a36Sopenharmony_ci * For the command queue (#4/#9), allocate command space + one big 301062306a36Sopenharmony_ci * command for scan, since scan command is very huge; the system will 301162306a36Sopenharmony_ci * not have two scans at the same time, so only one is needed. 301262306a36Sopenharmony_ci * For normal Tx queues (all other queues), no super-size command 301362306a36Sopenharmony_ci * space is needed. 301462306a36Sopenharmony_ci */ 301562306a36Sopenharmony_ci if (txq_id == il->cmd_queue) { 301662306a36Sopenharmony_ci slots = TFD_CMD_SLOTS; 301762306a36Sopenharmony_ci actual_slots = slots + 1; 301862306a36Sopenharmony_ci } else { 301962306a36Sopenharmony_ci slots = TFD_TX_CMD_SLOTS; 302062306a36Sopenharmony_ci actual_slots = slots; 302162306a36Sopenharmony_ci } 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_ci txq->meta = 302462306a36Sopenharmony_ci kcalloc(actual_slots, sizeof(struct il_cmd_meta), GFP_KERNEL); 302562306a36Sopenharmony_ci txq->cmd = 302662306a36Sopenharmony_ci kcalloc(actual_slots, sizeof(struct il_device_cmd *), GFP_KERNEL); 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci if (!txq->meta || !txq->cmd) 302962306a36Sopenharmony_ci goto out_free_arrays; 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci len = sizeof(struct il_device_cmd); 303262306a36Sopenharmony_ci for (i = 0; i < actual_slots; i++) { 303362306a36Sopenharmony_ci /* only happens for cmd queue */ 303462306a36Sopenharmony_ci if (i == slots) 303562306a36Sopenharmony_ci len = IL_MAX_CMD_SIZE; 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci txq->cmd[i] = kmalloc(len, GFP_KERNEL); 303862306a36Sopenharmony_ci if (!txq->cmd[i]) 303962306a36Sopenharmony_ci goto err; 304062306a36Sopenharmony_ci } 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci /* Alloc driver data array and TFD circular buffer */ 304362306a36Sopenharmony_ci ret = il_tx_queue_alloc(il, txq, txq_id); 304462306a36Sopenharmony_ci if (ret) 304562306a36Sopenharmony_ci goto err; 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci txq->need_update = 0; 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci /* 305062306a36Sopenharmony_ci * For the default queues 0-3, set up the swq_id 305162306a36Sopenharmony_ci * already -- all others need to get one later 305262306a36Sopenharmony_ci * (if they need one at all). 305362306a36Sopenharmony_ci */ 305462306a36Sopenharmony_ci if (txq_id < 4) 305562306a36Sopenharmony_ci il_set_swq_id(txq, txq_id, txq_id); 305662306a36Sopenharmony_ci 305762306a36Sopenharmony_ci /* Initialize queue's high/low-water marks, and head/tail idxes */ 305862306a36Sopenharmony_ci il_queue_init(il, &txq->q, slots, txq_id); 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci /* Tell device where to find queue */ 306162306a36Sopenharmony_ci il->ops->txq_init(il, txq); 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci return 0; 306462306a36Sopenharmony_cierr: 306562306a36Sopenharmony_ci for (i = 0; i < actual_slots; i++) 306662306a36Sopenharmony_ci kfree(txq->cmd[i]); 306762306a36Sopenharmony_ciout_free_arrays: 306862306a36Sopenharmony_ci kfree(txq->meta); 306962306a36Sopenharmony_ci txq->meta = NULL; 307062306a36Sopenharmony_ci kfree(txq->cmd); 307162306a36Sopenharmony_ci txq->cmd = NULL; 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci return -ENOMEM; 307462306a36Sopenharmony_ci} 307562306a36Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_init); 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_civoid 307862306a36Sopenharmony_ciil_tx_queue_reset(struct il_priv *il, u32 txq_id) 307962306a36Sopenharmony_ci{ 308062306a36Sopenharmony_ci int slots, actual_slots; 308162306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci if (txq_id == il->cmd_queue) { 308462306a36Sopenharmony_ci slots = TFD_CMD_SLOTS; 308562306a36Sopenharmony_ci actual_slots = TFD_CMD_SLOTS + 1; 308662306a36Sopenharmony_ci } else { 308762306a36Sopenharmony_ci slots = TFD_TX_CMD_SLOTS; 308862306a36Sopenharmony_ci actual_slots = TFD_TX_CMD_SLOTS; 308962306a36Sopenharmony_ci } 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci memset(txq->meta, 0, sizeof(struct il_cmd_meta) * actual_slots); 309262306a36Sopenharmony_ci txq->need_update = 0; 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci /* Initialize queue's high/low-water marks, and head/tail idxes */ 309562306a36Sopenharmony_ci il_queue_init(il, &txq->q, slots, txq_id); 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci /* Tell device where to find queue */ 309862306a36Sopenharmony_ci il->ops->txq_init(il, txq); 309962306a36Sopenharmony_ci} 310062306a36Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_reset); 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci/*************** HOST COMMAND QUEUE FUNCTIONS *****/ 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ci/* 310562306a36Sopenharmony_ci * il_enqueue_hcmd - enqueue a uCode command 310662306a36Sopenharmony_ci * @il: device ilate data point 310762306a36Sopenharmony_ci * @cmd: a point to the ucode command structure 310862306a36Sopenharmony_ci * 310962306a36Sopenharmony_ci * The function returns < 0 values to indicate the operation is 311062306a36Sopenharmony_ci * failed. On success, it turns the idx (> 0) of command in the 311162306a36Sopenharmony_ci * command queue. 311262306a36Sopenharmony_ci */ 311362306a36Sopenharmony_ciint 311462306a36Sopenharmony_ciil_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd) 311562306a36Sopenharmony_ci{ 311662306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[il->cmd_queue]; 311762306a36Sopenharmony_ci struct il_queue *q = &txq->q; 311862306a36Sopenharmony_ci struct il_device_cmd *out_cmd; 311962306a36Sopenharmony_ci struct il_cmd_meta *out_meta; 312062306a36Sopenharmony_ci dma_addr_t phys_addr; 312162306a36Sopenharmony_ci unsigned long flags; 312262306a36Sopenharmony_ci u32 idx; 312362306a36Sopenharmony_ci u16 fix_size; 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci cmd->len = il->ops->get_hcmd_size(cmd->id, cmd->len); 312662306a36Sopenharmony_ci fix_size = (u16) (cmd->len + sizeof(out_cmd->hdr)); 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_ci /* If any of the command structures end up being larger than 312962306a36Sopenharmony_ci * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then 313062306a36Sopenharmony_ci * we will need to increase the size of the TFD entries 313162306a36Sopenharmony_ci * Also, check to see if command buffer should not exceed the size 313262306a36Sopenharmony_ci * of device_cmd and max_cmd_size. */ 313362306a36Sopenharmony_ci BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && 313462306a36Sopenharmony_ci !(cmd->flags & CMD_SIZE_HUGE)); 313562306a36Sopenharmony_ci BUG_ON(fix_size > IL_MAX_CMD_SIZE); 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci if (il_is_rfkill(il) || il_is_ctkill(il)) { 313862306a36Sopenharmony_ci IL_WARN("Not sending command - %s KILL\n", 313962306a36Sopenharmony_ci il_is_rfkill(il) ? "RF" : "CT"); 314062306a36Sopenharmony_ci return -EIO; 314162306a36Sopenharmony_ci } 314262306a36Sopenharmony_ci 314362306a36Sopenharmony_ci spin_lock_irqsave(&il->hcmd_lock, flags); 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci if (il_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { 314662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->hcmd_lock, flags); 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci IL_ERR("Restarting adapter due to command queue full\n"); 314962306a36Sopenharmony_ci queue_work(il->workqueue, &il->restart); 315062306a36Sopenharmony_ci return -ENOSPC; 315162306a36Sopenharmony_ci } 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci idx = il_get_cmd_idx(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); 315462306a36Sopenharmony_ci out_cmd = txq->cmd[idx]; 315562306a36Sopenharmony_ci out_meta = &txq->meta[idx]; 315662306a36Sopenharmony_ci 315762306a36Sopenharmony_ci if (WARN_ON(out_meta->flags & CMD_MAPPED)) { 315862306a36Sopenharmony_ci spin_unlock_irqrestore(&il->hcmd_lock, flags); 315962306a36Sopenharmony_ci return -ENOSPC; 316062306a36Sopenharmony_ci } 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_ci memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ 316362306a36Sopenharmony_ci out_meta->flags = cmd->flags | CMD_MAPPED; 316462306a36Sopenharmony_ci if (cmd->flags & CMD_WANT_SKB) 316562306a36Sopenharmony_ci out_meta->source = cmd; 316662306a36Sopenharmony_ci if (cmd->flags & CMD_ASYNC) 316762306a36Sopenharmony_ci out_meta->callback = cmd->callback; 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci out_cmd->hdr.cmd = cmd->id; 317062306a36Sopenharmony_ci memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci /* At this point, the out_cmd now has all of the incoming cmd 317362306a36Sopenharmony_ci * information */ 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_ci out_cmd->hdr.flags = 0; 317662306a36Sopenharmony_ci out_cmd->hdr.sequence = 317762306a36Sopenharmony_ci cpu_to_le16(QUEUE_TO_SEQ(il->cmd_queue) | IDX_TO_SEQ(q->write_ptr)); 317862306a36Sopenharmony_ci if (cmd->flags & CMD_SIZE_HUGE) 317962306a36Sopenharmony_ci out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 318262306a36Sopenharmony_ci switch (out_cmd->hdr.cmd) { 318362306a36Sopenharmony_ci case C_TX_LINK_QUALITY_CMD: 318462306a36Sopenharmony_ci case C_SENSITIVITY: 318562306a36Sopenharmony_ci D_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, " 318662306a36Sopenharmony_ci "%d bytes at %d[%d]:%d\n", 318762306a36Sopenharmony_ci il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, 318862306a36Sopenharmony_ci le16_to_cpu(out_cmd->hdr.sequence), fix_size, 318962306a36Sopenharmony_ci q->write_ptr, idx, il->cmd_queue); 319062306a36Sopenharmony_ci break; 319162306a36Sopenharmony_ci default: 319262306a36Sopenharmony_ci D_HC("Sending command %s (#%x), seq: 0x%04X, " 319362306a36Sopenharmony_ci "%d bytes at %d[%d]:%d\n", 319462306a36Sopenharmony_ci il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, 319562306a36Sopenharmony_ci le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr, 319662306a36Sopenharmony_ci idx, il->cmd_queue); 319762306a36Sopenharmony_ci } 319862306a36Sopenharmony_ci#endif 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_ci phys_addr = dma_map_single(&il->pci_dev->dev, &out_cmd->hdr, fix_size, 320162306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 320262306a36Sopenharmony_ci if (unlikely(dma_mapping_error(&il->pci_dev->dev, phys_addr))) { 320362306a36Sopenharmony_ci idx = -ENOMEM; 320462306a36Sopenharmony_ci goto out; 320562306a36Sopenharmony_ci } 320662306a36Sopenharmony_ci dma_unmap_addr_set(out_meta, mapping, phys_addr); 320762306a36Sopenharmony_ci dma_unmap_len_set(out_meta, len, fix_size); 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_ci txq->need_update = 1; 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci if (il->ops->txq_update_byte_cnt_tbl) 321262306a36Sopenharmony_ci /* Set up entry in queue's byte count circular buffer */ 321362306a36Sopenharmony_ci il->ops->txq_update_byte_cnt_tbl(il, txq, 0); 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, fix_size, 1, 321662306a36Sopenharmony_ci U32_PAD(cmd->len)); 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci /* Increment and update queue's write idx */ 321962306a36Sopenharmony_ci q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); 322062306a36Sopenharmony_ci il_txq_update_write_ptr(il, txq); 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ciout: 322362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->hcmd_lock, flags); 322462306a36Sopenharmony_ci return idx; 322562306a36Sopenharmony_ci} 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci/* 322862306a36Sopenharmony_ci * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd 322962306a36Sopenharmony_ci * 323062306a36Sopenharmony_ci * When FW advances 'R' idx, all entries between old and new 'R' idx 323162306a36Sopenharmony_ci * need to be reclaimed. As result, some free space forms. If there is 323262306a36Sopenharmony_ci * enough free space (> low mark), wake the stack that feeds us. 323362306a36Sopenharmony_ci */ 323462306a36Sopenharmony_cistatic void 323562306a36Sopenharmony_ciil_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx) 323662306a36Sopenharmony_ci{ 323762306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 323862306a36Sopenharmony_ci struct il_queue *q = &txq->q; 323962306a36Sopenharmony_ci int nfreed = 0; 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { 324262306a36Sopenharmony_ci IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " 324362306a36Sopenharmony_ci "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, 324462306a36Sopenharmony_ci q->write_ptr, q->read_ptr); 324562306a36Sopenharmony_ci return; 324662306a36Sopenharmony_ci } 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; 324962306a36Sopenharmony_ci q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci if (nfreed++ > 0) { 325262306a36Sopenharmony_ci IL_ERR("HCMD skipped: idx (%d) %d %d\n", idx, 325362306a36Sopenharmony_ci q->write_ptr, q->read_ptr); 325462306a36Sopenharmony_ci queue_work(il->workqueue, &il->restart); 325562306a36Sopenharmony_ci } 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci } 325862306a36Sopenharmony_ci} 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci/* 326162306a36Sopenharmony_ci * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them 326262306a36Sopenharmony_ci * @rxb: Rx buffer to reclaim 326362306a36Sopenharmony_ci * 326462306a36Sopenharmony_ci * If an Rx buffer has an async callback associated with it the callback 326562306a36Sopenharmony_ci * will be executed. The attached skb (if present) will only be freed 326662306a36Sopenharmony_ci * if the callback returns 1 326762306a36Sopenharmony_ci */ 326862306a36Sopenharmony_civoid 326962306a36Sopenharmony_ciil_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb) 327062306a36Sopenharmony_ci{ 327162306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 327262306a36Sopenharmony_ci u16 sequence = le16_to_cpu(pkt->hdr.sequence); 327362306a36Sopenharmony_ci int txq_id = SEQ_TO_QUEUE(sequence); 327462306a36Sopenharmony_ci int idx = SEQ_TO_IDX(sequence); 327562306a36Sopenharmony_ci int cmd_idx; 327662306a36Sopenharmony_ci bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); 327762306a36Sopenharmony_ci struct il_device_cmd *cmd; 327862306a36Sopenharmony_ci struct il_cmd_meta *meta; 327962306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[il->cmd_queue]; 328062306a36Sopenharmony_ci unsigned long flags; 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci /* If a Tx command is being handled and it isn't in the actual 328362306a36Sopenharmony_ci * command queue then there a command routing bug has been introduced 328462306a36Sopenharmony_ci * in the queue management code. */ 328562306a36Sopenharmony_ci if (WARN 328662306a36Sopenharmony_ci (txq_id != il->cmd_queue, 328762306a36Sopenharmony_ci "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", 328862306a36Sopenharmony_ci txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr, 328962306a36Sopenharmony_ci il->txq[il->cmd_queue].q.write_ptr)) { 329062306a36Sopenharmony_ci il_print_hex_error(il, pkt, 32); 329162306a36Sopenharmony_ci return; 329262306a36Sopenharmony_ci } 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci cmd_idx = il_get_cmd_idx(&txq->q, idx, huge); 329562306a36Sopenharmony_ci cmd = txq->cmd[cmd_idx]; 329662306a36Sopenharmony_ci meta = &txq->meta[cmd_idx]; 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci txq->time_stamp = jiffies; 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci dma_unmap_single(&il->pci_dev->dev, dma_unmap_addr(meta, mapping), 330162306a36Sopenharmony_ci dma_unmap_len(meta, len), DMA_BIDIRECTIONAL); 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci /* Input error checking is done when commands are added to queue. */ 330462306a36Sopenharmony_ci if (meta->flags & CMD_WANT_SKB) { 330562306a36Sopenharmony_ci meta->source->reply_page = (unsigned long)rxb_addr(rxb); 330662306a36Sopenharmony_ci rxb->page = NULL; 330762306a36Sopenharmony_ci } else if (meta->callback) 330862306a36Sopenharmony_ci meta->callback(il, cmd, pkt); 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci spin_lock_irqsave(&il->hcmd_lock, flags); 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci il_hcmd_queue_reclaim(il, txq_id, idx, cmd_idx); 331362306a36Sopenharmony_ci 331462306a36Sopenharmony_ci if (!(meta->flags & CMD_ASYNC)) { 331562306a36Sopenharmony_ci clear_bit(S_HCMD_ACTIVE, &il->status); 331662306a36Sopenharmony_ci D_INFO("Clearing HCMD_ACTIVE for command %s\n", 331762306a36Sopenharmony_ci il_get_cmd_string(cmd->hdr.cmd)); 331862306a36Sopenharmony_ci wake_up(&il->wait_command_queue); 331962306a36Sopenharmony_ci } 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_ci /* Mark as unmapped */ 332262306a36Sopenharmony_ci meta->flags = 0; 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci spin_unlock_irqrestore(&il->hcmd_lock, flags); 332562306a36Sopenharmony_ci} 332662306a36Sopenharmony_ciEXPORT_SYMBOL(il_tx_cmd_complete); 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ciMODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965"); 332962306a36Sopenharmony_ciMODULE_VERSION(IWLWIFI_VERSION); 333062306a36Sopenharmony_ciMODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); 333162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci/* 333462306a36Sopenharmony_ci * set bt_coex_active to true, uCode will do kill/defer 333562306a36Sopenharmony_ci * every time the priority line is asserted (BT is sending signals on the 333662306a36Sopenharmony_ci * priority line in the PCIx). 333762306a36Sopenharmony_ci * set bt_coex_active to false, uCode will ignore the BT activity and 333862306a36Sopenharmony_ci * perform the normal operation 333962306a36Sopenharmony_ci * 334062306a36Sopenharmony_ci * User might experience transmit issue on some platform due to WiFi/BT 334162306a36Sopenharmony_ci * co-exist problem. The possible behaviors are: 334262306a36Sopenharmony_ci * Able to scan and finding all the available AP 334362306a36Sopenharmony_ci * Not able to associate with any AP 334462306a36Sopenharmony_ci * On those platforms, WiFi communication can be restored by set 334562306a36Sopenharmony_ci * "bt_coex_active" module parameter to "false" 334662306a36Sopenharmony_ci * 334762306a36Sopenharmony_ci * default: bt_coex_active = true (BT_COEX_ENABLE) 334862306a36Sopenharmony_ci */ 334962306a36Sopenharmony_cistatic bool bt_coex_active = true; 335062306a36Sopenharmony_cimodule_param(bt_coex_active, bool, 0444); 335162306a36Sopenharmony_ciMODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist"); 335262306a36Sopenharmony_ci 335362306a36Sopenharmony_ciu32 il_debug_level; 335462306a36Sopenharmony_ciEXPORT_SYMBOL(il_debug_level); 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ciconst u8 il_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 335762306a36Sopenharmony_ciEXPORT_SYMBOL(il_bcast_addr); 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ 336062306a36Sopenharmony_ci#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ 336162306a36Sopenharmony_cistatic void 336262306a36Sopenharmony_ciil_init_ht_hw_capab(const struct il_priv *il, 336362306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_info, 336462306a36Sopenharmony_ci enum nl80211_band band) 336562306a36Sopenharmony_ci{ 336662306a36Sopenharmony_ci u16 max_bit_rate = 0; 336762306a36Sopenharmony_ci u8 rx_chains_num = il->hw_params.rx_chains_num; 336862306a36Sopenharmony_ci u8 tx_chains_num = il->hw_params.tx_chains_num; 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci ht_info->cap = 0; 337162306a36Sopenharmony_ci memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci ht_info->ht_supported = true; 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SGI_20; 337662306a36Sopenharmony_ci max_bit_rate = MAX_BIT_RATE_20_MHZ; 337762306a36Sopenharmony_ci if (il->hw_params.ht40_channel & BIT(band)) { 337862306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; 337962306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_SGI_40; 338062306a36Sopenharmony_ci ht_info->mcs.rx_mask[4] = 0x01; 338162306a36Sopenharmony_ci max_bit_rate = MAX_BIT_RATE_40_MHZ; 338262306a36Sopenharmony_ci } 338362306a36Sopenharmony_ci 338462306a36Sopenharmony_ci if (il->cfg->mod_params->amsdu_size_8K) 338562306a36Sopenharmony_ci ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; 338662306a36Sopenharmony_ci 338762306a36Sopenharmony_ci ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; 338862306a36Sopenharmony_ci ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci ht_info->mcs.rx_mask[0] = 0xFF; 339162306a36Sopenharmony_ci if (rx_chains_num >= 2) 339262306a36Sopenharmony_ci ht_info->mcs.rx_mask[1] = 0xFF; 339362306a36Sopenharmony_ci if (rx_chains_num >= 3) 339462306a36Sopenharmony_ci ht_info->mcs.rx_mask[2] = 0xFF; 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_ci /* Highest supported Rx data rate */ 339762306a36Sopenharmony_ci max_bit_rate *= rx_chains_num; 339862306a36Sopenharmony_ci WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); 339962306a36Sopenharmony_ci ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci /* Tx MCS capabilities */ 340262306a36Sopenharmony_ci ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; 340362306a36Sopenharmony_ci if (tx_chains_num != rx_chains_num) { 340462306a36Sopenharmony_ci ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; 340562306a36Sopenharmony_ci ht_info->mcs.tx_params |= 340662306a36Sopenharmony_ci ((tx_chains_num - 340762306a36Sopenharmony_ci 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci} 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci/* 341262306a36Sopenharmony_ci * il_init_geos - Initialize mac80211's geo/channel info based from eeprom 341362306a36Sopenharmony_ci */ 341462306a36Sopenharmony_ciint 341562306a36Sopenharmony_ciil_init_geos(struct il_priv *il) 341662306a36Sopenharmony_ci{ 341762306a36Sopenharmony_ci struct il_channel_info *ch; 341862306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 341962306a36Sopenharmony_ci struct ieee80211_channel *channels; 342062306a36Sopenharmony_ci struct ieee80211_channel *geo_ch; 342162306a36Sopenharmony_ci struct ieee80211_rate *rates; 342262306a36Sopenharmony_ci int i = 0; 342362306a36Sopenharmony_ci s8 max_tx_power = 0; 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci if (il->bands[NL80211_BAND_2GHZ].n_bitrates || 342662306a36Sopenharmony_ci il->bands[NL80211_BAND_5GHZ].n_bitrates) { 342762306a36Sopenharmony_ci D_INFO("Geography modes already initialized.\n"); 342862306a36Sopenharmony_ci set_bit(S_GEO_CONFIGURED, &il->status); 342962306a36Sopenharmony_ci return 0; 343062306a36Sopenharmony_ci } 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci channels = 343362306a36Sopenharmony_ci kcalloc(il->channel_count, sizeof(struct ieee80211_channel), 343462306a36Sopenharmony_ci GFP_KERNEL); 343562306a36Sopenharmony_ci if (!channels) 343662306a36Sopenharmony_ci return -ENOMEM; 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci rates = 343962306a36Sopenharmony_ci kzalloc((sizeof(struct ieee80211_rate) * RATE_COUNT_LEGACY), 344062306a36Sopenharmony_ci GFP_KERNEL); 344162306a36Sopenharmony_ci if (!rates) { 344262306a36Sopenharmony_ci kfree(channels); 344362306a36Sopenharmony_ci return -ENOMEM; 344462306a36Sopenharmony_ci } 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci /* 5.2GHz channels start after the 2.4GHz channels */ 344762306a36Sopenharmony_ci sband = &il->bands[NL80211_BAND_5GHZ]; 344862306a36Sopenharmony_ci sband->channels = &channels[ARRAY_SIZE(il_eeprom_band_1)]; 344962306a36Sopenharmony_ci /* just OFDM */ 345062306a36Sopenharmony_ci sband->bitrates = &rates[IL_FIRST_OFDM_RATE]; 345162306a36Sopenharmony_ci sband->n_bitrates = RATE_COUNT_LEGACY - IL_FIRST_OFDM_RATE; 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci if (il->cfg->sku & IL_SKU_N) 345462306a36Sopenharmony_ci il_init_ht_hw_capab(il, &sband->ht_cap, NL80211_BAND_5GHZ); 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci sband = &il->bands[NL80211_BAND_2GHZ]; 345762306a36Sopenharmony_ci sband->channels = channels; 345862306a36Sopenharmony_ci /* OFDM & CCK */ 345962306a36Sopenharmony_ci sband->bitrates = rates; 346062306a36Sopenharmony_ci sband->n_bitrates = RATE_COUNT_LEGACY; 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci if (il->cfg->sku & IL_SKU_N) 346362306a36Sopenharmony_ci il_init_ht_hw_capab(il, &sband->ht_cap, NL80211_BAND_2GHZ); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci il->ieee_channels = channels; 346662306a36Sopenharmony_ci il->ieee_rates = rates; 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci for (i = 0; i < il->channel_count; i++) { 346962306a36Sopenharmony_ci ch = &il->channel_info[i]; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci if (!il_is_channel_valid(ch)) 347262306a36Sopenharmony_ci continue; 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci sband = &il->bands[ch->band]; 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci geo_ch = &sband->channels[sband->n_channels++]; 347762306a36Sopenharmony_ci 347862306a36Sopenharmony_ci geo_ch->center_freq = 347962306a36Sopenharmony_ci ieee80211_channel_to_frequency(ch->channel, ch->band); 348062306a36Sopenharmony_ci geo_ch->max_power = ch->max_power_avg; 348162306a36Sopenharmony_ci geo_ch->max_antenna_gain = 0xff; 348262306a36Sopenharmony_ci geo_ch->hw_value = ch->channel; 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci if (il_is_channel_valid(ch)) { 348562306a36Sopenharmony_ci if (!(ch->flags & EEPROM_CHANNEL_IBSS)) 348662306a36Sopenharmony_ci geo_ch->flags |= IEEE80211_CHAN_NO_IR; 348762306a36Sopenharmony_ci 348862306a36Sopenharmony_ci if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) 348962306a36Sopenharmony_ci geo_ch->flags |= IEEE80211_CHAN_NO_IR; 349062306a36Sopenharmony_ci 349162306a36Sopenharmony_ci if (ch->flags & EEPROM_CHANNEL_RADAR) 349262306a36Sopenharmony_ci geo_ch->flags |= IEEE80211_CHAN_RADAR; 349362306a36Sopenharmony_ci 349462306a36Sopenharmony_ci geo_ch->flags |= ch->ht40_extension_channel; 349562306a36Sopenharmony_ci 349662306a36Sopenharmony_ci if (ch->max_power_avg > max_tx_power) 349762306a36Sopenharmony_ci max_tx_power = ch->max_power_avg; 349862306a36Sopenharmony_ci } else { 349962306a36Sopenharmony_ci geo_ch->flags |= IEEE80211_CHAN_DISABLED; 350062306a36Sopenharmony_ci } 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_ci D_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, 350362306a36Sopenharmony_ci geo_ch->center_freq, 350462306a36Sopenharmony_ci il_is_channel_a_band(ch) ? "5.2" : "2.4", 350562306a36Sopenharmony_ci geo_ch-> 350662306a36Sopenharmony_ci flags & IEEE80211_CHAN_DISABLED ? "restricted" : "valid", 350762306a36Sopenharmony_ci geo_ch->flags); 350862306a36Sopenharmony_ci } 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_ci il->tx_power_device_lmt = max_tx_power; 351162306a36Sopenharmony_ci il->tx_power_user_lmt = max_tx_power; 351262306a36Sopenharmony_ci il->tx_power_next = max_tx_power; 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_ci if (il->bands[NL80211_BAND_5GHZ].n_channels == 0 && 351562306a36Sopenharmony_ci (il->cfg->sku & IL_SKU_A)) { 351662306a36Sopenharmony_ci IL_INFO("Incorrectly detected BG card as ABG. " 351762306a36Sopenharmony_ci "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n", 351862306a36Sopenharmony_ci il->pci_dev->device, il->pci_dev->subsystem_device); 351962306a36Sopenharmony_ci il->cfg->sku &= ~IL_SKU_A; 352062306a36Sopenharmony_ci } 352162306a36Sopenharmony_ci 352262306a36Sopenharmony_ci IL_INFO("Tunable channels: %d 802.11bg, %d 802.11a channels\n", 352362306a36Sopenharmony_ci il->bands[NL80211_BAND_2GHZ].n_channels, 352462306a36Sopenharmony_ci il->bands[NL80211_BAND_5GHZ].n_channels); 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_ci set_bit(S_GEO_CONFIGURED, &il->status); 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci return 0; 352962306a36Sopenharmony_ci} 353062306a36Sopenharmony_ciEXPORT_SYMBOL(il_init_geos); 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci/* 353362306a36Sopenharmony_ci * il_free_geos - undo allocations in il_init_geos 353462306a36Sopenharmony_ci */ 353562306a36Sopenharmony_civoid 353662306a36Sopenharmony_ciil_free_geos(struct il_priv *il) 353762306a36Sopenharmony_ci{ 353862306a36Sopenharmony_ci kfree(il->ieee_channels); 353962306a36Sopenharmony_ci kfree(il->ieee_rates); 354062306a36Sopenharmony_ci clear_bit(S_GEO_CONFIGURED, &il->status); 354162306a36Sopenharmony_ci} 354262306a36Sopenharmony_ciEXPORT_SYMBOL(il_free_geos); 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_cistatic bool 354562306a36Sopenharmony_ciil_is_channel_extension(struct il_priv *il, enum nl80211_band band, 354662306a36Sopenharmony_ci u16 channel, u8 extension_chan_offset) 354762306a36Sopenharmony_ci{ 354862306a36Sopenharmony_ci const struct il_channel_info *ch_info; 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ci ch_info = il_get_channel_info(il, band, channel); 355162306a36Sopenharmony_ci if (!il_is_channel_valid(ch_info)) 355262306a36Sopenharmony_ci return false; 355362306a36Sopenharmony_ci 355462306a36Sopenharmony_ci if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) 355562306a36Sopenharmony_ci return !(ch_info-> 355662306a36Sopenharmony_ci ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS); 355762306a36Sopenharmony_ci else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) 355862306a36Sopenharmony_ci return !(ch_info-> 355962306a36Sopenharmony_ci ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS); 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci return false; 356262306a36Sopenharmony_ci} 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_cibool 356562306a36Sopenharmony_ciil_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap) 356662306a36Sopenharmony_ci{ 356762306a36Sopenharmony_ci if (!il->ht.enabled || !il->ht.is_40mhz) 356862306a36Sopenharmony_ci return false; 356962306a36Sopenharmony_ci 357062306a36Sopenharmony_ci /* 357162306a36Sopenharmony_ci * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 357262306a36Sopenharmony_ci * the bit will not set if it is pure 40MHz case 357362306a36Sopenharmony_ci */ 357462306a36Sopenharmony_ci if (ht_cap && !ht_cap->ht_supported) 357562306a36Sopenharmony_ci return false; 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS 357862306a36Sopenharmony_ci if (il->disable_ht40) 357962306a36Sopenharmony_ci return false; 358062306a36Sopenharmony_ci#endif 358162306a36Sopenharmony_ci 358262306a36Sopenharmony_ci return il_is_channel_extension(il, il->band, 358362306a36Sopenharmony_ci le16_to_cpu(il->staging.channel), 358462306a36Sopenharmony_ci il->ht.extension_chan_offset); 358562306a36Sopenharmony_ci} 358662306a36Sopenharmony_ciEXPORT_SYMBOL(il_is_ht40_tx_allowed); 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_cistatic u16 noinline 358962306a36Sopenharmony_ciil_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) 359062306a36Sopenharmony_ci{ 359162306a36Sopenharmony_ci u16 new_val; 359262306a36Sopenharmony_ci u16 beacon_factor; 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci /* 359562306a36Sopenharmony_ci * If mac80211 hasn't given us a beacon interval, program 359662306a36Sopenharmony_ci * the default into the device. 359762306a36Sopenharmony_ci */ 359862306a36Sopenharmony_ci if (!beacon_val) 359962306a36Sopenharmony_ci return DEFAULT_BEACON_INTERVAL; 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci /* 360262306a36Sopenharmony_ci * If the beacon interval we obtained from the peer 360362306a36Sopenharmony_ci * is too large, we'll have to wake up more often 360462306a36Sopenharmony_ci * (and in IBSS case, we'll beacon too much) 360562306a36Sopenharmony_ci * 360662306a36Sopenharmony_ci * For example, if max_beacon_val is 4096, and the 360762306a36Sopenharmony_ci * requested beacon interval is 7000, we'll have to 360862306a36Sopenharmony_ci * use 3500 to be able to wake up on the beacons. 360962306a36Sopenharmony_ci * 361062306a36Sopenharmony_ci * This could badly influence beacon detection stats. 361162306a36Sopenharmony_ci */ 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; 361462306a36Sopenharmony_ci new_val = beacon_val / beacon_factor; 361562306a36Sopenharmony_ci 361662306a36Sopenharmony_ci if (!new_val) 361762306a36Sopenharmony_ci new_val = max_beacon_val; 361862306a36Sopenharmony_ci 361962306a36Sopenharmony_ci return new_val; 362062306a36Sopenharmony_ci} 362162306a36Sopenharmony_ci 362262306a36Sopenharmony_ciint 362362306a36Sopenharmony_ciil_send_rxon_timing(struct il_priv *il) 362462306a36Sopenharmony_ci{ 362562306a36Sopenharmony_ci u64 tsf; 362662306a36Sopenharmony_ci s32 interval_tm, rem; 362762306a36Sopenharmony_ci struct ieee80211_conf *conf = NULL; 362862306a36Sopenharmony_ci u16 beacon_int; 362962306a36Sopenharmony_ci struct ieee80211_vif *vif = il->vif; 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_ci conf = &il->hw->conf; 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci memset(&il->timing, 0, sizeof(struct il_rxon_time_cmd)); 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_ci il->timing.timestamp = cpu_to_le64(il->timestamp); 363862306a36Sopenharmony_ci il->timing.listen_interval = cpu_to_le16(conf->listen_interval); 363962306a36Sopenharmony_ci 364062306a36Sopenharmony_ci beacon_int = vif ? vif->bss_conf.beacon_int : 0; 364162306a36Sopenharmony_ci 364262306a36Sopenharmony_ci /* 364362306a36Sopenharmony_ci * TODO: For IBSS we need to get atim_win from mac80211, 364462306a36Sopenharmony_ci * for now just always use 0 364562306a36Sopenharmony_ci */ 364662306a36Sopenharmony_ci il->timing.atim_win = 0; 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci beacon_int = 364962306a36Sopenharmony_ci il_adjust_beacon_interval(beacon_int, 365062306a36Sopenharmony_ci il->hw_params.max_beacon_itrvl * 365162306a36Sopenharmony_ci TIME_UNIT); 365262306a36Sopenharmony_ci il->timing.beacon_interval = cpu_to_le16(beacon_int); 365362306a36Sopenharmony_ci 365462306a36Sopenharmony_ci tsf = il->timestamp; /* tsf is modifed by do_div: copy it */ 365562306a36Sopenharmony_ci interval_tm = beacon_int * TIME_UNIT; 365662306a36Sopenharmony_ci rem = do_div(tsf, interval_tm); 365762306a36Sopenharmony_ci il->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_ci il->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ? : 1) : 1; 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci D_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n", 366262306a36Sopenharmony_ci le16_to_cpu(il->timing.beacon_interval), 366362306a36Sopenharmony_ci le32_to_cpu(il->timing.beacon_init_val), 366462306a36Sopenharmony_ci le16_to_cpu(il->timing.atim_win)); 366562306a36Sopenharmony_ci 366662306a36Sopenharmony_ci return il_send_cmd_pdu(il, C_RXON_TIMING, sizeof(il->timing), 366762306a36Sopenharmony_ci &il->timing); 366862306a36Sopenharmony_ci} 366962306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_rxon_timing); 367062306a36Sopenharmony_ci 367162306a36Sopenharmony_civoid 367262306a36Sopenharmony_ciil_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt) 367362306a36Sopenharmony_ci{ 367462306a36Sopenharmony_ci struct il_rxon_cmd *rxon = &il->staging; 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_ci if (hw_decrypt) 367762306a36Sopenharmony_ci rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; 367862306a36Sopenharmony_ci else 367962306a36Sopenharmony_ci rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; 368062306a36Sopenharmony_ci 368162306a36Sopenharmony_ci} 368262306a36Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_hwcrypto); 368362306a36Sopenharmony_ci 368462306a36Sopenharmony_ci/* validate RXON structure is valid */ 368562306a36Sopenharmony_ciint 368662306a36Sopenharmony_ciil_check_rxon_cmd(struct il_priv *il) 368762306a36Sopenharmony_ci{ 368862306a36Sopenharmony_ci struct il_rxon_cmd *rxon = &il->staging; 368962306a36Sopenharmony_ci bool error = false; 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci if (rxon->flags & RXON_FLG_BAND_24G_MSK) { 369262306a36Sopenharmony_ci if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { 369362306a36Sopenharmony_ci IL_WARN("check 2.4G: wrong narrow\n"); 369462306a36Sopenharmony_ci error = true; 369562306a36Sopenharmony_ci } 369662306a36Sopenharmony_ci if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { 369762306a36Sopenharmony_ci IL_WARN("check 2.4G: wrong radar\n"); 369862306a36Sopenharmony_ci error = true; 369962306a36Sopenharmony_ci } 370062306a36Sopenharmony_ci } else { 370162306a36Sopenharmony_ci if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { 370262306a36Sopenharmony_ci IL_WARN("check 5.2G: not short slot!\n"); 370362306a36Sopenharmony_ci error = true; 370462306a36Sopenharmony_ci } 370562306a36Sopenharmony_ci if (rxon->flags & RXON_FLG_CCK_MSK) { 370662306a36Sopenharmony_ci IL_WARN("check 5.2G: CCK!\n"); 370762306a36Sopenharmony_ci error = true; 370862306a36Sopenharmony_ci } 370962306a36Sopenharmony_ci } 371062306a36Sopenharmony_ci if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { 371162306a36Sopenharmony_ci IL_WARN("mac/bssid mcast!\n"); 371262306a36Sopenharmony_ci error = true; 371362306a36Sopenharmony_ci } 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci /* make sure basic rates 6Mbps and 1Mbps are supported */ 371662306a36Sopenharmony_ci if ((rxon->ofdm_basic_rates & RATE_6M_MASK) == 0 && 371762306a36Sopenharmony_ci (rxon->cck_basic_rates & RATE_1M_MASK) == 0) { 371862306a36Sopenharmony_ci IL_WARN("neither 1 nor 6 are basic\n"); 371962306a36Sopenharmony_ci error = true; 372062306a36Sopenharmony_ci } 372162306a36Sopenharmony_ci 372262306a36Sopenharmony_ci if (le16_to_cpu(rxon->assoc_id) > 2007) { 372362306a36Sopenharmony_ci IL_WARN("aid > 2007\n"); 372462306a36Sopenharmony_ci error = true; 372562306a36Sopenharmony_ci } 372662306a36Sopenharmony_ci 372762306a36Sopenharmony_ci if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) == 372862306a36Sopenharmony_ci (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { 372962306a36Sopenharmony_ci IL_WARN("CCK and short slot\n"); 373062306a36Sopenharmony_ci error = true; 373162306a36Sopenharmony_ci } 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_ci if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) == 373462306a36Sopenharmony_ci (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { 373562306a36Sopenharmony_ci IL_WARN("CCK and auto detect"); 373662306a36Sopenharmony_ci error = true; 373762306a36Sopenharmony_ci } 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_ci if ((rxon-> 374062306a36Sopenharmony_ci flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) == 374162306a36Sopenharmony_ci RXON_FLG_TGG_PROTECT_MSK) { 374262306a36Sopenharmony_ci IL_WARN("TGg but no auto-detect\n"); 374362306a36Sopenharmony_ci error = true; 374462306a36Sopenharmony_ci } 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_ci if (error) 374762306a36Sopenharmony_ci IL_WARN("Tuning to channel %d\n", le16_to_cpu(rxon->channel)); 374862306a36Sopenharmony_ci 374962306a36Sopenharmony_ci if (error) { 375062306a36Sopenharmony_ci IL_ERR("Invalid RXON\n"); 375162306a36Sopenharmony_ci return -EINVAL; 375262306a36Sopenharmony_ci } 375362306a36Sopenharmony_ci return 0; 375462306a36Sopenharmony_ci} 375562306a36Sopenharmony_ciEXPORT_SYMBOL(il_check_rxon_cmd); 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci/* 375862306a36Sopenharmony_ci * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed 375962306a36Sopenharmony_ci * @il: staging_rxon is compared to active_rxon 376062306a36Sopenharmony_ci * 376162306a36Sopenharmony_ci * If the RXON structure is changing enough to require a new tune, 376262306a36Sopenharmony_ci * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that 376362306a36Sopenharmony_ci * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. 376462306a36Sopenharmony_ci */ 376562306a36Sopenharmony_ciint 376662306a36Sopenharmony_ciil_full_rxon_required(struct il_priv *il) 376762306a36Sopenharmony_ci{ 376862306a36Sopenharmony_ci const struct il_rxon_cmd *staging = &il->staging; 376962306a36Sopenharmony_ci const struct il_rxon_cmd *active = &il->active; 377062306a36Sopenharmony_ci 377162306a36Sopenharmony_ci#define CHK(cond) \ 377262306a36Sopenharmony_ci if ((cond)) { \ 377362306a36Sopenharmony_ci D_INFO("need full RXON - " #cond "\n"); \ 377462306a36Sopenharmony_ci return 1; \ 377562306a36Sopenharmony_ci } 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci#define CHK_NEQ(c1, c2) \ 377862306a36Sopenharmony_ci if ((c1) != (c2)) { \ 377962306a36Sopenharmony_ci D_INFO("need full RXON - " \ 378062306a36Sopenharmony_ci #c1 " != " #c2 " - %d != %d\n", \ 378162306a36Sopenharmony_ci (c1), (c2)); \ 378262306a36Sopenharmony_ci return 1; \ 378362306a36Sopenharmony_ci } 378462306a36Sopenharmony_ci 378562306a36Sopenharmony_ci /* These items are only settable from the full RXON command */ 378662306a36Sopenharmony_ci CHK(!il_is_associated(il)); 378762306a36Sopenharmony_ci CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr)); 378862306a36Sopenharmony_ci CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr)); 378962306a36Sopenharmony_ci CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr, 379062306a36Sopenharmony_ci active->wlap_bssid_addr)); 379162306a36Sopenharmony_ci CHK_NEQ(staging->dev_type, active->dev_type); 379262306a36Sopenharmony_ci CHK_NEQ(staging->channel, active->channel); 379362306a36Sopenharmony_ci CHK_NEQ(staging->air_propagation, active->air_propagation); 379462306a36Sopenharmony_ci CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, 379562306a36Sopenharmony_ci active->ofdm_ht_single_stream_basic_rates); 379662306a36Sopenharmony_ci CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, 379762306a36Sopenharmony_ci active->ofdm_ht_dual_stream_basic_rates); 379862306a36Sopenharmony_ci CHK_NEQ(staging->assoc_id, active->assoc_id); 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ci /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can 380162306a36Sopenharmony_ci * be updated with the RXON_ASSOC command -- however only some 380262306a36Sopenharmony_ci * flag transitions are allowed using RXON_ASSOC */ 380362306a36Sopenharmony_ci 380462306a36Sopenharmony_ci /* Check if we are not switching bands */ 380562306a36Sopenharmony_ci CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, 380662306a36Sopenharmony_ci active->flags & RXON_FLG_BAND_24G_MSK); 380762306a36Sopenharmony_ci 380862306a36Sopenharmony_ci /* Check if we are switching association toggle */ 380962306a36Sopenharmony_ci CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, 381062306a36Sopenharmony_ci active->filter_flags & RXON_FILTER_ASSOC_MSK); 381162306a36Sopenharmony_ci 381262306a36Sopenharmony_ci#undef CHK 381362306a36Sopenharmony_ci#undef CHK_NEQ 381462306a36Sopenharmony_ci 381562306a36Sopenharmony_ci return 0; 381662306a36Sopenharmony_ci} 381762306a36Sopenharmony_ciEXPORT_SYMBOL(il_full_rxon_required); 381862306a36Sopenharmony_ci 381962306a36Sopenharmony_ciu8 382062306a36Sopenharmony_ciil_get_lowest_plcp(struct il_priv *il) 382162306a36Sopenharmony_ci{ 382262306a36Sopenharmony_ci /* 382362306a36Sopenharmony_ci * Assign the lowest rate -- should really get this from 382462306a36Sopenharmony_ci * the beacon skb from mac80211. 382562306a36Sopenharmony_ci */ 382662306a36Sopenharmony_ci if (il->staging.flags & RXON_FLG_BAND_24G_MSK) 382762306a36Sopenharmony_ci return RATE_1M_PLCP; 382862306a36Sopenharmony_ci else 382962306a36Sopenharmony_ci return RATE_6M_PLCP; 383062306a36Sopenharmony_ci} 383162306a36Sopenharmony_ciEXPORT_SYMBOL(il_get_lowest_plcp); 383262306a36Sopenharmony_ci 383362306a36Sopenharmony_cistatic void 383462306a36Sopenharmony_ci_il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) 383562306a36Sopenharmony_ci{ 383662306a36Sopenharmony_ci struct il_rxon_cmd *rxon = &il->staging; 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci if (!il->ht.enabled) { 383962306a36Sopenharmony_ci rxon->flags &= 384062306a36Sopenharmony_ci ~(RXON_FLG_CHANNEL_MODE_MSK | 384162306a36Sopenharmony_ci RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK 384262306a36Sopenharmony_ci | RXON_FLG_HT_PROT_MSK); 384362306a36Sopenharmony_ci return; 384462306a36Sopenharmony_ci } 384562306a36Sopenharmony_ci 384662306a36Sopenharmony_ci rxon->flags |= 384762306a36Sopenharmony_ci cpu_to_le32(il->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS); 384862306a36Sopenharmony_ci 384962306a36Sopenharmony_ci /* Set up channel bandwidth: 385062306a36Sopenharmony_ci * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ 385162306a36Sopenharmony_ci /* clear the HT channel mode before set the mode */ 385262306a36Sopenharmony_ci rxon->flags &= 385362306a36Sopenharmony_ci ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); 385462306a36Sopenharmony_ci if (il_is_ht40_tx_allowed(il, NULL)) { 385562306a36Sopenharmony_ci /* pure ht40 */ 385662306a36Sopenharmony_ci if (il->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { 385762306a36Sopenharmony_ci rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; 385862306a36Sopenharmony_ci /* Note: control channel is opposite of extension channel */ 385962306a36Sopenharmony_ci switch (il->ht.extension_chan_offset) { 386062306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 386162306a36Sopenharmony_ci rxon->flags &= 386262306a36Sopenharmony_ci ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; 386362306a36Sopenharmony_ci break; 386462306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 386562306a36Sopenharmony_ci rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; 386662306a36Sopenharmony_ci break; 386762306a36Sopenharmony_ci } 386862306a36Sopenharmony_ci } else { 386962306a36Sopenharmony_ci /* Note: control channel is opposite of extension channel */ 387062306a36Sopenharmony_ci switch (il->ht.extension_chan_offset) { 387162306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 387262306a36Sopenharmony_ci rxon->flags &= 387362306a36Sopenharmony_ci ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); 387462306a36Sopenharmony_ci rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; 387562306a36Sopenharmony_ci break; 387662306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 387762306a36Sopenharmony_ci rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; 387862306a36Sopenharmony_ci rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; 387962306a36Sopenharmony_ci break; 388062306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_NONE: 388162306a36Sopenharmony_ci default: 388262306a36Sopenharmony_ci /* channel location only valid if in Mixed mode */ 388362306a36Sopenharmony_ci IL_ERR("invalid extension channel offset\n"); 388462306a36Sopenharmony_ci break; 388562306a36Sopenharmony_ci } 388662306a36Sopenharmony_ci } 388762306a36Sopenharmony_ci } else { 388862306a36Sopenharmony_ci rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; 388962306a36Sopenharmony_ci } 389062306a36Sopenharmony_ci 389162306a36Sopenharmony_ci if (il->ops->set_rxon_chain) 389262306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 389362306a36Sopenharmony_ci 389462306a36Sopenharmony_ci D_ASSOC("rxon flags 0x%X operation mode :0x%X " 389562306a36Sopenharmony_ci "extension channel offset 0x%x\n", le32_to_cpu(rxon->flags), 389662306a36Sopenharmony_ci il->ht.protection, il->ht.extension_chan_offset); 389762306a36Sopenharmony_ci} 389862306a36Sopenharmony_ci 389962306a36Sopenharmony_civoid 390062306a36Sopenharmony_ciil_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) 390162306a36Sopenharmony_ci{ 390262306a36Sopenharmony_ci _il_set_rxon_ht(il, ht_conf); 390362306a36Sopenharmony_ci} 390462306a36Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_ht); 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci/* Return valid, unused, channel for a passive scan to reset the RF */ 390762306a36Sopenharmony_ciu8 390862306a36Sopenharmony_ciil_get_single_channel_number(struct il_priv *il, enum nl80211_band band) 390962306a36Sopenharmony_ci{ 391062306a36Sopenharmony_ci const struct il_channel_info *ch_info; 391162306a36Sopenharmony_ci int i; 391262306a36Sopenharmony_ci u8 channel = 0; 391362306a36Sopenharmony_ci u8 min, max; 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) { 391662306a36Sopenharmony_ci min = 14; 391762306a36Sopenharmony_ci max = il->channel_count; 391862306a36Sopenharmony_ci } else { 391962306a36Sopenharmony_ci min = 0; 392062306a36Sopenharmony_ci max = 14; 392162306a36Sopenharmony_ci } 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci for (i = min; i < max; i++) { 392462306a36Sopenharmony_ci channel = il->channel_info[i].channel; 392562306a36Sopenharmony_ci if (channel == le16_to_cpu(il->staging.channel)) 392662306a36Sopenharmony_ci continue; 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ci ch_info = il_get_channel_info(il, band, channel); 392962306a36Sopenharmony_ci if (il_is_channel_valid(ch_info)) 393062306a36Sopenharmony_ci break; 393162306a36Sopenharmony_ci } 393262306a36Sopenharmony_ci 393362306a36Sopenharmony_ci return channel; 393462306a36Sopenharmony_ci} 393562306a36Sopenharmony_ciEXPORT_SYMBOL(il_get_single_channel_number); 393662306a36Sopenharmony_ci 393762306a36Sopenharmony_ci/* 393862306a36Sopenharmony_ci * il_set_rxon_channel - Set the band and channel values in staging RXON 393962306a36Sopenharmony_ci * @ch: requested channel as a pointer to struct ieee80211_channel 394062306a36Sopenharmony_ci 394162306a36Sopenharmony_ci * NOTE: Does not commit to the hardware; it sets appropriate bit fields 394262306a36Sopenharmony_ci * in the staging RXON flag structure based on the ch->band 394362306a36Sopenharmony_ci */ 394462306a36Sopenharmony_ciint 394562306a36Sopenharmony_ciil_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch) 394662306a36Sopenharmony_ci{ 394762306a36Sopenharmony_ci enum nl80211_band band = ch->band; 394862306a36Sopenharmony_ci u16 channel = ch->hw_value; 394962306a36Sopenharmony_ci 395062306a36Sopenharmony_ci if (le16_to_cpu(il->staging.channel) == channel && il->band == band) 395162306a36Sopenharmony_ci return 0; 395262306a36Sopenharmony_ci 395362306a36Sopenharmony_ci il->staging.channel = cpu_to_le16(channel); 395462306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 395562306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_BAND_24G_MSK; 395662306a36Sopenharmony_ci else 395762306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_BAND_24G_MSK; 395862306a36Sopenharmony_ci 395962306a36Sopenharmony_ci il->band = band; 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_ci D_INFO("Staging channel set to %d [%d]\n", channel, band); 396262306a36Sopenharmony_ci 396362306a36Sopenharmony_ci return 0; 396462306a36Sopenharmony_ci} 396562306a36Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_channel); 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_civoid 396862306a36Sopenharmony_ciil_set_flags_for_band(struct il_priv *il, enum nl80211_band band, 396962306a36Sopenharmony_ci struct ieee80211_vif *vif) 397062306a36Sopenharmony_ci{ 397162306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) { 397262306a36Sopenharmony_ci il->staging.flags &= 397362306a36Sopenharmony_ci ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | 397462306a36Sopenharmony_ci RXON_FLG_CCK_MSK); 397562306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 397662306a36Sopenharmony_ci } else { 397762306a36Sopenharmony_ci /* Copied from il_post_associate() */ 397862306a36Sopenharmony_ci if (vif && vif->bss_conf.use_short_slot) 397962306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; 398062306a36Sopenharmony_ci else 398162306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; 398262306a36Sopenharmony_ci 398362306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_BAND_24G_MSK; 398462306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; 398562306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_CCK_MSK; 398662306a36Sopenharmony_ci } 398762306a36Sopenharmony_ci} 398862306a36Sopenharmony_ciEXPORT_SYMBOL(il_set_flags_for_band); 398962306a36Sopenharmony_ci 399062306a36Sopenharmony_ci/* 399162306a36Sopenharmony_ci * initialize rxon structure with default values from eeprom 399262306a36Sopenharmony_ci */ 399362306a36Sopenharmony_civoid 399462306a36Sopenharmony_ciil_connection_init_rx_config(struct il_priv *il) 399562306a36Sopenharmony_ci{ 399662306a36Sopenharmony_ci const struct il_channel_info *ch_info; 399762306a36Sopenharmony_ci 399862306a36Sopenharmony_ci memset(&il->staging, 0, sizeof(il->staging)); 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_ci switch (il->iw_mode) { 400162306a36Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 400262306a36Sopenharmony_ci il->staging.dev_type = RXON_DEV_TYPE_ESS; 400362306a36Sopenharmony_ci break; 400462306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 400562306a36Sopenharmony_ci il->staging.dev_type = RXON_DEV_TYPE_ESS; 400662306a36Sopenharmony_ci il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; 400762306a36Sopenharmony_ci break; 400862306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 400962306a36Sopenharmony_ci il->staging.dev_type = RXON_DEV_TYPE_IBSS; 401062306a36Sopenharmony_ci il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; 401162306a36Sopenharmony_ci il->staging.filter_flags = 401262306a36Sopenharmony_ci RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; 401362306a36Sopenharmony_ci break; 401462306a36Sopenharmony_ci default: 401562306a36Sopenharmony_ci IL_ERR("Unsupported interface type %d\n", il->vif->type); 401662306a36Sopenharmony_ci return; 401762306a36Sopenharmony_ci } 401862306a36Sopenharmony_ci 401962306a36Sopenharmony_ci#if 0 402062306a36Sopenharmony_ci /* TODO: Figure out when short_preamble would be set and cache from 402162306a36Sopenharmony_ci * that */ 402262306a36Sopenharmony_ci if (!hw_to_local(il->hw)->short_preamble) 402362306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; 402462306a36Sopenharmony_ci else 402562306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; 402662306a36Sopenharmony_ci#endif 402762306a36Sopenharmony_ci 402862306a36Sopenharmony_ci ch_info = 402962306a36Sopenharmony_ci il_get_channel_info(il, il->band, le16_to_cpu(il->active.channel)); 403062306a36Sopenharmony_ci 403162306a36Sopenharmony_ci if (!ch_info) 403262306a36Sopenharmony_ci ch_info = &il->channel_info[0]; 403362306a36Sopenharmony_ci 403462306a36Sopenharmony_ci il->staging.channel = cpu_to_le16(ch_info->channel); 403562306a36Sopenharmony_ci il->band = ch_info->band; 403662306a36Sopenharmony_ci 403762306a36Sopenharmony_ci il_set_flags_for_band(il, il->band, il->vif); 403862306a36Sopenharmony_ci 403962306a36Sopenharmony_ci il->staging.ofdm_basic_rates = 404062306a36Sopenharmony_ci (IL_OFDM_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; 404162306a36Sopenharmony_ci il->staging.cck_basic_rates = 404262306a36Sopenharmony_ci (IL_CCK_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ci /* clear both MIX and PURE40 mode flag */ 404562306a36Sopenharmony_ci il->staging.flags &= 404662306a36Sopenharmony_ci ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40); 404762306a36Sopenharmony_ci if (il->vif) 404862306a36Sopenharmony_ci memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN); 404962306a36Sopenharmony_ci 405062306a36Sopenharmony_ci il->staging.ofdm_ht_single_stream_basic_rates = 0xff; 405162306a36Sopenharmony_ci il->staging.ofdm_ht_dual_stream_basic_rates = 0xff; 405262306a36Sopenharmony_ci} 405362306a36Sopenharmony_ciEXPORT_SYMBOL(il_connection_init_rx_config); 405462306a36Sopenharmony_ci 405562306a36Sopenharmony_civoid 405662306a36Sopenharmony_ciil_set_rate(struct il_priv *il) 405762306a36Sopenharmony_ci{ 405862306a36Sopenharmony_ci const struct ieee80211_supported_band *hw = NULL; 405962306a36Sopenharmony_ci struct ieee80211_rate *rate; 406062306a36Sopenharmony_ci int i; 406162306a36Sopenharmony_ci 406262306a36Sopenharmony_ci hw = il_get_hw_mode(il, il->band); 406362306a36Sopenharmony_ci if (!hw) { 406462306a36Sopenharmony_ci IL_ERR("Failed to set rate: unable to get hw mode\n"); 406562306a36Sopenharmony_ci return; 406662306a36Sopenharmony_ci } 406762306a36Sopenharmony_ci 406862306a36Sopenharmony_ci il->active_rate = 0; 406962306a36Sopenharmony_ci 407062306a36Sopenharmony_ci for (i = 0; i < hw->n_bitrates; i++) { 407162306a36Sopenharmony_ci rate = &(hw->bitrates[i]); 407262306a36Sopenharmony_ci if (rate->hw_value < RATE_COUNT_LEGACY) 407362306a36Sopenharmony_ci il->active_rate |= (1 << rate->hw_value); 407462306a36Sopenharmony_ci } 407562306a36Sopenharmony_ci 407662306a36Sopenharmony_ci D_RATE("Set active_rate = %0x\n", il->active_rate); 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_ci il->staging.cck_basic_rates = 407962306a36Sopenharmony_ci (IL_CCK_BASIC_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; 408062306a36Sopenharmony_ci 408162306a36Sopenharmony_ci il->staging.ofdm_basic_rates = 408262306a36Sopenharmony_ci (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; 408362306a36Sopenharmony_ci} 408462306a36Sopenharmony_ciEXPORT_SYMBOL(il_set_rate); 408562306a36Sopenharmony_ci 408662306a36Sopenharmony_civoid 408762306a36Sopenharmony_ciil_chswitch_done(struct il_priv *il, bool is_success) 408862306a36Sopenharmony_ci{ 408962306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 409062306a36Sopenharmony_ci return; 409162306a36Sopenharmony_ci 409262306a36Sopenharmony_ci if (test_and_clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) 409362306a36Sopenharmony_ci ieee80211_chswitch_done(il->vif, is_success); 409462306a36Sopenharmony_ci} 409562306a36Sopenharmony_ciEXPORT_SYMBOL(il_chswitch_done); 409662306a36Sopenharmony_ci 409762306a36Sopenharmony_civoid 409862306a36Sopenharmony_ciil_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb) 409962306a36Sopenharmony_ci{ 410062306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 410162306a36Sopenharmony_ci struct il_csa_notification *csa = &(pkt->u.csa_notif); 410262306a36Sopenharmony_ci struct il_rxon_cmd *rxon = (void *)&il->active; 410362306a36Sopenharmony_ci 410462306a36Sopenharmony_ci if (!test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) 410562306a36Sopenharmony_ci return; 410662306a36Sopenharmony_ci 410762306a36Sopenharmony_ci if (!le32_to_cpu(csa->status) && csa->channel == il->switch_channel) { 410862306a36Sopenharmony_ci rxon->channel = csa->channel; 410962306a36Sopenharmony_ci il->staging.channel = csa->channel; 411062306a36Sopenharmony_ci D_11H("CSA notif: channel %d\n", le16_to_cpu(csa->channel)); 411162306a36Sopenharmony_ci il_chswitch_done(il, true); 411262306a36Sopenharmony_ci } else { 411362306a36Sopenharmony_ci IL_ERR("CSA notif (fail) : channel %d\n", 411462306a36Sopenharmony_ci le16_to_cpu(csa->channel)); 411562306a36Sopenharmony_ci il_chswitch_done(il, false); 411662306a36Sopenharmony_ci } 411762306a36Sopenharmony_ci} 411862306a36Sopenharmony_ciEXPORT_SYMBOL(il_hdl_csa); 411962306a36Sopenharmony_ci 412062306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 412162306a36Sopenharmony_civoid 412262306a36Sopenharmony_ciil_print_rx_config_cmd(struct il_priv *il) 412362306a36Sopenharmony_ci{ 412462306a36Sopenharmony_ci struct il_rxon_cmd *rxon = &il->staging; 412562306a36Sopenharmony_ci 412662306a36Sopenharmony_ci D_RADIO("RX CONFIG:\n"); 412762306a36Sopenharmony_ci il_print_hex_dump(il, IL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); 412862306a36Sopenharmony_ci D_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel)); 412962306a36Sopenharmony_ci D_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); 413062306a36Sopenharmony_ci D_RADIO("u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags)); 413162306a36Sopenharmony_ci D_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type); 413262306a36Sopenharmony_ci D_RADIO("u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates); 413362306a36Sopenharmony_ci D_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates); 413462306a36Sopenharmony_ci D_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr); 413562306a36Sopenharmony_ci D_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr); 413662306a36Sopenharmony_ci D_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); 413762306a36Sopenharmony_ci} 413862306a36Sopenharmony_ciEXPORT_SYMBOL(il_print_rx_config_cmd); 413962306a36Sopenharmony_ci#endif 414062306a36Sopenharmony_ci/* 414162306a36Sopenharmony_ci * il_irq_handle_error - called for HW or SW error interrupt from card 414262306a36Sopenharmony_ci */ 414362306a36Sopenharmony_civoid 414462306a36Sopenharmony_ciil_irq_handle_error(struct il_priv *il) 414562306a36Sopenharmony_ci{ 414662306a36Sopenharmony_ci /* Set the FW error flag -- cleared on il_down */ 414762306a36Sopenharmony_ci set_bit(S_FW_ERROR, &il->status); 414862306a36Sopenharmony_ci 414962306a36Sopenharmony_ci /* Cancel currently queued command. */ 415062306a36Sopenharmony_ci clear_bit(S_HCMD_ACTIVE, &il->status); 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci IL_ERR("Loaded firmware version: %s\n", il->hw->wiphy->fw_version); 415362306a36Sopenharmony_ci 415462306a36Sopenharmony_ci il->ops->dump_nic_error_log(il); 415562306a36Sopenharmony_ci if (il->ops->dump_fh) 415662306a36Sopenharmony_ci il->ops->dump_fh(il, NULL, false); 415762306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 415862306a36Sopenharmony_ci if (il_get_debug_level(il) & IL_DL_FW_ERRORS) 415962306a36Sopenharmony_ci il_print_rx_config_cmd(il); 416062306a36Sopenharmony_ci#endif 416162306a36Sopenharmony_ci 416262306a36Sopenharmony_ci wake_up(&il->wait_command_queue); 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_ci /* Keep the restart process from trying to send host 416562306a36Sopenharmony_ci * commands by clearing the INIT status bit */ 416662306a36Sopenharmony_ci clear_bit(S_READY, &il->status); 416762306a36Sopenharmony_ci 416862306a36Sopenharmony_ci if (!test_bit(S_EXIT_PENDING, &il->status)) { 416962306a36Sopenharmony_ci IL_DBG(IL_DL_FW_ERRORS, 417062306a36Sopenharmony_ci "Restarting adapter due to uCode error.\n"); 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_ci if (il->cfg->mod_params->restart_fw) 417362306a36Sopenharmony_ci queue_work(il->workqueue, &il->restart); 417462306a36Sopenharmony_ci } 417562306a36Sopenharmony_ci} 417662306a36Sopenharmony_ciEXPORT_SYMBOL(il_irq_handle_error); 417762306a36Sopenharmony_ci 417862306a36Sopenharmony_cistatic int 417962306a36Sopenharmony_ci_il_apm_stop_master(struct il_priv *il) 418062306a36Sopenharmony_ci{ 418162306a36Sopenharmony_ci int ret = 0; 418262306a36Sopenharmony_ci 418362306a36Sopenharmony_ci /* stop device's busmaster DMA activity */ 418462306a36Sopenharmony_ci _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); 418562306a36Sopenharmony_ci 418662306a36Sopenharmony_ci ret = 418762306a36Sopenharmony_ci _il_poll_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, 418862306a36Sopenharmony_ci CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); 418962306a36Sopenharmony_ci if (ret < 0) 419062306a36Sopenharmony_ci IL_WARN("Master Disable Timed Out, 100 usec\n"); 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_ci D_INFO("stop master\n"); 419362306a36Sopenharmony_ci 419462306a36Sopenharmony_ci return ret; 419562306a36Sopenharmony_ci} 419662306a36Sopenharmony_ci 419762306a36Sopenharmony_civoid 419862306a36Sopenharmony_ci_il_apm_stop(struct il_priv *il) 419962306a36Sopenharmony_ci{ 420062306a36Sopenharmony_ci lockdep_assert_held(&il->reg_lock); 420162306a36Sopenharmony_ci 420262306a36Sopenharmony_ci D_INFO("Stop card, put in low power state\n"); 420362306a36Sopenharmony_ci 420462306a36Sopenharmony_ci /* Stop device's DMA activity */ 420562306a36Sopenharmony_ci _il_apm_stop_master(il); 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_ci /* Reset the entire device */ 420862306a36Sopenharmony_ci _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); 420962306a36Sopenharmony_ci 421062306a36Sopenharmony_ci udelay(10); 421162306a36Sopenharmony_ci 421262306a36Sopenharmony_ci /* 421362306a36Sopenharmony_ci * Clear "initialization complete" bit to move adapter from 421462306a36Sopenharmony_ci * D0A* (powered-up Active) --> D0U* (Uninitialized) state. 421562306a36Sopenharmony_ci */ 421662306a36Sopenharmony_ci _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 421762306a36Sopenharmony_ci} 421862306a36Sopenharmony_ciEXPORT_SYMBOL(_il_apm_stop); 421962306a36Sopenharmony_ci 422062306a36Sopenharmony_civoid 422162306a36Sopenharmony_ciil_apm_stop(struct il_priv *il) 422262306a36Sopenharmony_ci{ 422362306a36Sopenharmony_ci unsigned long flags; 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, flags); 422662306a36Sopenharmony_ci _il_apm_stop(il); 422762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, flags); 422862306a36Sopenharmony_ci} 422962306a36Sopenharmony_ciEXPORT_SYMBOL(il_apm_stop); 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci/* 423262306a36Sopenharmony_ci * Start up NIC's basic functionality after it has been reset 423362306a36Sopenharmony_ci * (e.g. after platform boot, or shutdown via il_apm_stop()) 423462306a36Sopenharmony_ci * NOTE: This does not load uCode nor start the embedded processor 423562306a36Sopenharmony_ci */ 423662306a36Sopenharmony_ciint 423762306a36Sopenharmony_ciil_apm_init(struct il_priv *il) 423862306a36Sopenharmony_ci{ 423962306a36Sopenharmony_ci int ret = 0; 424062306a36Sopenharmony_ci u16 lctl; 424162306a36Sopenharmony_ci 424262306a36Sopenharmony_ci D_INFO("Init card's basic functions\n"); 424362306a36Sopenharmony_ci 424462306a36Sopenharmony_ci /* 424562306a36Sopenharmony_ci * Use "set_bit" below rather than "write", to preserve any hardware 424662306a36Sopenharmony_ci * bits already set by default after reset. 424762306a36Sopenharmony_ci */ 424862306a36Sopenharmony_ci 424962306a36Sopenharmony_ci /* Disable L0S exit timer (platform NMI Work/Around) */ 425062306a36Sopenharmony_ci il_set_bit(il, CSR_GIO_CHICKEN_BITS, 425162306a36Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); 425262306a36Sopenharmony_ci 425362306a36Sopenharmony_ci /* 425462306a36Sopenharmony_ci * Disable L0s without affecting L1; 425562306a36Sopenharmony_ci * don't wait for ICH L0s (ICH bug W/A) 425662306a36Sopenharmony_ci */ 425762306a36Sopenharmony_ci il_set_bit(il, CSR_GIO_CHICKEN_BITS, 425862306a36Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_ci /* Set FH wait threshold to maximum (HW error during stress W/A) */ 426162306a36Sopenharmony_ci il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); 426262306a36Sopenharmony_ci 426362306a36Sopenharmony_ci /* 426462306a36Sopenharmony_ci * Enable HAP INTA (interrupt from management bus) to 426562306a36Sopenharmony_ci * wake device's PCI Express link L1a -> L0s 426662306a36Sopenharmony_ci * NOTE: This is no-op for 3945 (non-existent bit) 426762306a36Sopenharmony_ci */ 426862306a36Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 426962306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); 427062306a36Sopenharmony_ci 427162306a36Sopenharmony_ci /* 427262306a36Sopenharmony_ci * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition. 427362306a36Sopenharmony_ci * Check if BIOS (or OS) enabled L1-ASPM on this device. 427462306a36Sopenharmony_ci * If so (likely), disable L0S, so device moves directly L0->L1; 427562306a36Sopenharmony_ci * costs negligible amount of power savings. 427662306a36Sopenharmony_ci * If not (unlikely), enable L0S, so there is at least some 427762306a36Sopenharmony_ci * power savings, even without L1. 427862306a36Sopenharmony_ci */ 427962306a36Sopenharmony_ci if (il->cfg->set_l0s) { 428062306a36Sopenharmony_ci ret = pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); 428162306a36Sopenharmony_ci if (!ret && (lctl & PCI_EXP_LNKCTL_ASPM_L1)) { 428262306a36Sopenharmony_ci /* L1-ASPM enabled; disable(!) L0S */ 428362306a36Sopenharmony_ci il_set_bit(il, CSR_GIO_REG, 428462306a36Sopenharmony_ci CSR_GIO_REG_VAL_L0S_ENABLED); 428562306a36Sopenharmony_ci D_POWER("L1 Enabled; Disabling L0S\n"); 428662306a36Sopenharmony_ci } else { 428762306a36Sopenharmony_ci /* L1-ASPM disabled; enable(!) L0S */ 428862306a36Sopenharmony_ci il_clear_bit(il, CSR_GIO_REG, 428962306a36Sopenharmony_ci CSR_GIO_REG_VAL_L0S_ENABLED); 429062306a36Sopenharmony_ci D_POWER("L1 Disabled; Enabling L0S\n"); 429162306a36Sopenharmony_ci } 429262306a36Sopenharmony_ci } 429362306a36Sopenharmony_ci 429462306a36Sopenharmony_ci /* Configure analog phase-lock-loop before activating to D0A */ 429562306a36Sopenharmony_ci if (il->cfg->pll_cfg_val) 429662306a36Sopenharmony_ci il_set_bit(il, CSR_ANA_PLL_CFG, 429762306a36Sopenharmony_ci il->cfg->pll_cfg_val); 429862306a36Sopenharmony_ci 429962306a36Sopenharmony_ci /* 430062306a36Sopenharmony_ci * Set "initialization complete" bit to move adapter from 430162306a36Sopenharmony_ci * D0U* --> D0A* (powered-up active) state. 430262306a36Sopenharmony_ci */ 430362306a36Sopenharmony_ci il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 430462306a36Sopenharmony_ci 430562306a36Sopenharmony_ci /* 430662306a36Sopenharmony_ci * Wait for clock stabilization; once stabilized, access to 430762306a36Sopenharmony_ci * device-internal resources is supported, e.g. il_wr_prph() 430862306a36Sopenharmony_ci * and accesses to uCode SRAM. 430962306a36Sopenharmony_ci */ 431062306a36Sopenharmony_ci ret = 431162306a36Sopenharmony_ci _il_poll_bit(il, CSR_GP_CNTRL, 431262306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 431362306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); 431462306a36Sopenharmony_ci if (ret < 0) { 431562306a36Sopenharmony_ci D_INFO("Failed to init the card\n"); 431662306a36Sopenharmony_ci goto out; 431762306a36Sopenharmony_ci } 431862306a36Sopenharmony_ci 431962306a36Sopenharmony_ci /* 432062306a36Sopenharmony_ci * Enable DMA and BSM (if used) clocks, wait for them to stabilize. 432162306a36Sopenharmony_ci * BSM (Boostrap State Machine) is only in 3945 and 4965. 432262306a36Sopenharmony_ci * 432362306a36Sopenharmony_ci * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits 432462306a36Sopenharmony_ci * do not disable clocks. This preserves any hardware bits already 432562306a36Sopenharmony_ci * set by default in "CLK_CTRL_REG" after reset. 432662306a36Sopenharmony_ci */ 432762306a36Sopenharmony_ci if (il->cfg->use_bsm) 432862306a36Sopenharmony_ci il_wr_prph(il, APMG_CLK_EN_REG, 432962306a36Sopenharmony_ci APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); 433062306a36Sopenharmony_ci else 433162306a36Sopenharmony_ci il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); 433262306a36Sopenharmony_ci udelay(20); 433362306a36Sopenharmony_ci 433462306a36Sopenharmony_ci /* Disable L1-Active */ 433562306a36Sopenharmony_ci il_set_bits_prph(il, APMG_PCIDEV_STT_REG, 433662306a36Sopenharmony_ci APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 433762306a36Sopenharmony_ci 433862306a36Sopenharmony_ciout: 433962306a36Sopenharmony_ci return ret; 434062306a36Sopenharmony_ci} 434162306a36Sopenharmony_ciEXPORT_SYMBOL(il_apm_init); 434262306a36Sopenharmony_ci 434362306a36Sopenharmony_ciint 434462306a36Sopenharmony_ciil_set_tx_power(struct il_priv *il, s8 tx_power, bool force) 434562306a36Sopenharmony_ci{ 434662306a36Sopenharmony_ci int ret; 434762306a36Sopenharmony_ci s8 prev_tx_power; 434862306a36Sopenharmony_ci bool defer; 434962306a36Sopenharmony_ci 435062306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_ci if (il->tx_power_user_lmt == tx_power && !force) 435362306a36Sopenharmony_ci return 0; 435462306a36Sopenharmony_ci 435562306a36Sopenharmony_ci if (!il->ops->send_tx_power) 435662306a36Sopenharmony_ci return -EOPNOTSUPP; 435762306a36Sopenharmony_ci 435862306a36Sopenharmony_ci /* 0 dBm mean 1 milliwatt */ 435962306a36Sopenharmony_ci if (tx_power < 0) { 436062306a36Sopenharmony_ci IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power); 436162306a36Sopenharmony_ci return -EINVAL; 436262306a36Sopenharmony_ci } 436362306a36Sopenharmony_ci 436462306a36Sopenharmony_ci if (tx_power > il->tx_power_device_lmt) { 436562306a36Sopenharmony_ci IL_WARN("Requested user TXPOWER %d above upper limit %d.\n", 436662306a36Sopenharmony_ci tx_power, il->tx_power_device_lmt); 436762306a36Sopenharmony_ci return -EINVAL; 436862306a36Sopenharmony_ci } 436962306a36Sopenharmony_ci 437062306a36Sopenharmony_ci if (!il_is_ready_rf(il)) 437162306a36Sopenharmony_ci return -EIO; 437262306a36Sopenharmony_ci 437362306a36Sopenharmony_ci /* scan complete and commit_rxon use tx_power_next value, 437462306a36Sopenharmony_ci * it always need to be updated for newest request */ 437562306a36Sopenharmony_ci il->tx_power_next = tx_power; 437662306a36Sopenharmony_ci 437762306a36Sopenharmony_ci /* do not set tx power when scanning or channel changing */ 437862306a36Sopenharmony_ci defer = test_bit(S_SCANNING, &il->status) || 437962306a36Sopenharmony_ci memcmp(&il->active, &il->staging, sizeof(il->staging)); 438062306a36Sopenharmony_ci if (defer && !force) { 438162306a36Sopenharmony_ci D_INFO("Deferring tx power set\n"); 438262306a36Sopenharmony_ci return 0; 438362306a36Sopenharmony_ci } 438462306a36Sopenharmony_ci 438562306a36Sopenharmony_ci prev_tx_power = il->tx_power_user_lmt; 438662306a36Sopenharmony_ci il->tx_power_user_lmt = tx_power; 438762306a36Sopenharmony_ci 438862306a36Sopenharmony_ci ret = il->ops->send_tx_power(il); 438962306a36Sopenharmony_ci 439062306a36Sopenharmony_ci /* if fail to set tx_power, restore the orig. tx power */ 439162306a36Sopenharmony_ci if (ret) { 439262306a36Sopenharmony_ci il->tx_power_user_lmt = prev_tx_power; 439362306a36Sopenharmony_ci il->tx_power_next = prev_tx_power; 439462306a36Sopenharmony_ci } 439562306a36Sopenharmony_ci return ret; 439662306a36Sopenharmony_ci} 439762306a36Sopenharmony_ciEXPORT_SYMBOL(il_set_tx_power); 439862306a36Sopenharmony_ci 439962306a36Sopenharmony_civoid 440062306a36Sopenharmony_ciil_send_bt_config(struct il_priv *il) 440162306a36Sopenharmony_ci{ 440262306a36Sopenharmony_ci struct il_bt_cmd bt_cmd = { 440362306a36Sopenharmony_ci .lead_time = BT_LEAD_TIME_DEF, 440462306a36Sopenharmony_ci .max_kill = BT_MAX_KILL_DEF, 440562306a36Sopenharmony_ci .kill_ack_mask = 0, 440662306a36Sopenharmony_ci .kill_cts_mask = 0, 440762306a36Sopenharmony_ci }; 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci if (!bt_coex_active) 441062306a36Sopenharmony_ci bt_cmd.flags = BT_COEX_DISABLE; 441162306a36Sopenharmony_ci else 441262306a36Sopenharmony_ci bt_cmd.flags = BT_COEX_ENABLE; 441362306a36Sopenharmony_ci 441462306a36Sopenharmony_ci D_INFO("BT coex %s\n", 441562306a36Sopenharmony_ci (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); 441662306a36Sopenharmony_ci 441762306a36Sopenharmony_ci if (il_send_cmd_pdu(il, C_BT_CONFIG, sizeof(struct il_bt_cmd), &bt_cmd)) 441862306a36Sopenharmony_ci IL_ERR("failed to send BT Coex Config\n"); 441962306a36Sopenharmony_ci} 442062306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_bt_config); 442162306a36Sopenharmony_ci 442262306a36Sopenharmony_ciint 442362306a36Sopenharmony_ciil_send_stats_request(struct il_priv *il, u8 flags, bool clear) 442462306a36Sopenharmony_ci{ 442562306a36Sopenharmony_ci struct il_stats_cmd stats_cmd = { 442662306a36Sopenharmony_ci .configuration_flags = clear ? IL_STATS_CONF_CLEAR_STATS : 0, 442762306a36Sopenharmony_ci }; 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_ci if (flags & CMD_ASYNC) 443062306a36Sopenharmony_ci return il_send_cmd_pdu_async(il, C_STATS, sizeof(struct il_stats_cmd), 443162306a36Sopenharmony_ci &stats_cmd, NULL); 443262306a36Sopenharmony_ci else 443362306a36Sopenharmony_ci return il_send_cmd_pdu(il, C_STATS, sizeof(struct il_stats_cmd), 443462306a36Sopenharmony_ci &stats_cmd); 443562306a36Sopenharmony_ci} 443662306a36Sopenharmony_ciEXPORT_SYMBOL(il_send_stats_request); 443762306a36Sopenharmony_ci 443862306a36Sopenharmony_civoid 443962306a36Sopenharmony_ciil_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb) 444062306a36Sopenharmony_ci{ 444162306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 444262306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 444362306a36Sopenharmony_ci struct il_sleep_notification *sleep = &(pkt->u.sleep_notif); 444462306a36Sopenharmony_ci D_RX("sleep mode: %d, src: %d\n", 444562306a36Sopenharmony_ci sleep->pm_sleep_mode, sleep->pm_wakeup_src); 444662306a36Sopenharmony_ci#endif 444762306a36Sopenharmony_ci} 444862306a36Sopenharmony_ciEXPORT_SYMBOL(il_hdl_pm_sleep); 444962306a36Sopenharmony_ci 445062306a36Sopenharmony_civoid 445162306a36Sopenharmony_ciil_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb) 445262306a36Sopenharmony_ci{ 445362306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 445462306a36Sopenharmony_ci u32 len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; 445562306a36Sopenharmony_ci D_RADIO("Dumping %d bytes of unhandled notification for %s:\n", len, 445662306a36Sopenharmony_ci il_get_cmd_string(pkt->hdr.cmd)); 445762306a36Sopenharmony_ci il_print_hex_dump(il, IL_DL_RADIO, pkt->u.raw, len); 445862306a36Sopenharmony_ci} 445962306a36Sopenharmony_ciEXPORT_SYMBOL(il_hdl_pm_debug_stats); 446062306a36Sopenharmony_ci 446162306a36Sopenharmony_civoid 446262306a36Sopenharmony_ciil_hdl_error(struct il_priv *il, struct il_rx_buf *rxb) 446362306a36Sopenharmony_ci{ 446462306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 446562306a36Sopenharmony_ci 446662306a36Sopenharmony_ci IL_ERR("Error Reply type 0x%08X cmd %s (0x%02X) " 446762306a36Sopenharmony_ci "seq 0x%04X ser 0x%08X\n", 446862306a36Sopenharmony_ci le32_to_cpu(pkt->u.err_resp.error_type), 446962306a36Sopenharmony_ci il_get_cmd_string(pkt->u.err_resp.cmd_id), 447062306a36Sopenharmony_ci pkt->u.err_resp.cmd_id, 447162306a36Sopenharmony_ci le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), 447262306a36Sopenharmony_ci le32_to_cpu(pkt->u.err_resp.error_info)); 447362306a36Sopenharmony_ci} 447462306a36Sopenharmony_ciEXPORT_SYMBOL(il_hdl_error); 447562306a36Sopenharmony_ci 447662306a36Sopenharmony_civoid 447762306a36Sopenharmony_ciil_clear_isr_stats(struct il_priv *il) 447862306a36Sopenharmony_ci{ 447962306a36Sopenharmony_ci memset(&il->isr_stats, 0, sizeof(il->isr_stats)); 448062306a36Sopenharmony_ci} 448162306a36Sopenharmony_ci 448262306a36Sopenharmony_ciint 448362306a36Sopenharmony_ciil_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 448462306a36Sopenharmony_ci unsigned int link_id, u16 queue, 448562306a36Sopenharmony_ci const struct ieee80211_tx_queue_params *params) 448662306a36Sopenharmony_ci{ 448762306a36Sopenharmony_ci struct il_priv *il = hw->priv; 448862306a36Sopenharmony_ci unsigned long flags; 448962306a36Sopenharmony_ci int q; 449062306a36Sopenharmony_ci 449162306a36Sopenharmony_ci D_MAC80211("enter\n"); 449262306a36Sopenharmony_ci 449362306a36Sopenharmony_ci if (!il_is_ready_rf(il)) { 449462306a36Sopenharmony_ci D_MAC80211("leave - RF not ready\n"); 449562306a36Sopenharmony_ci return -EIO; 449662306a36Sopenharmony_ci } 449762306a36Sopenharmony_ci 449862306a36Sopenharmony_ci if (queue >= AC_NUM) { 449962306a36Sopenharmony_ci D_MAC80211("leave - queue >= AC_NUM %d\n", queue); 450062306a36Sopenharmony_ci return 0; 450162306a36Sopenharmony_ci } 450262306a36Sopenharmony_ci 450362306a36Sopenharmony_ci q = AC_NUM - 1 - queue; 450462306a36Sopenharmony_ci 450562306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 450662306a36Sopenharmony_ci 450762306a36Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].cw_min = 450862306a36Sopenharmony_ci cpu_to_le16(params->cw_min); 450962306a36Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].cw_max = 451062306a36Sopenharmony_ci cpu_to_le16(params->cw_max); 451162306a36Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; 451262306a36Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].edca_txop = 451362306a36Sopenharmony_ci cpu_to_le16((params->txop * 32)); 451462306a36Sopenharmony_ci 451562306a36Sopenharmony_ci il->qos_data.def_qos_parm.ac[q].reserved1 = 0; 451662306a36Sopenharmony_ci 451762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 451862306a36Sopenharmony_ci 451962306a36Sopenharmony_ci D_MAC80211("leave\n"); 452062306a36Sopenharmony_ci return 0; 452162306a36Sopenharmony_ci} 452262306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_conf_tx); 452362306a36Sopenharmony_ci 452462306a36Sopenharmony_ciint 452562306a36Sopenharmony_ciil_mac_tx_last_beacon(struct ieee80211_hw *hw) 452662306a36Sopenharmony_ci{ 452762306a36Sopenharmony_ci struct il_priv *il = hw->priv; 452862306a36Sopenharmony_ci int ret; 452962306a36Sopenharmony_ci 453062306a36Sopenharmony_ci D_MAC80211("enter\n"); 453162306a36Sopenharmony_ci 453262306a36Sopenharmony_ci ret = (il->ibss_manager == IL_IBSS_MANAGER); 453362306a36Sopenharmony_ci 453462306a36Sopenharmony_ci D_MAC80211("leave ret %d\n", ret); 453562306a36Sopenharmony_ci return ret; 453662306a36Sopenharmony_ci} 453762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(il_mac_tx_last_beacon); 453862306a36Sopenharmony_ci 453962306a36Sopenharmony_cistatic int 454062306a36Sopenharmony_ciil_set_mode(struct il_priv *il) 454162306a36Sopenharmony_ci{ 454262306a36Sopenharmony_ci il_connection_init_rx_config(il); 454362306a36Sopenharmony_ci 454462306a36Sopenharmony_ci if (il->ops->set_rxon_chain) 454562306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 454662306a36Sopenharmony_ci 454762306a36Sopenharmony_ci return il_commit_rxon(il); 454862306a36Sopenharmony_ci} 454962306a36Sopenharmony_ci 455062306a36Sopenharmony_ciint 455162306a36Sopenharmony_ciil_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 455262306a36Sopenharmony_ci{ 455362306a36Sopenharmony_ci struct il_priv *il = hw->priv; 455462306a36Sopenharmony_ci int err; 455562306a36Sopenharmony_ci bool reset; 455662306a36Sopenharmony_ci 455762306a36Sopenharmony_ci mutex_lock(&il->mutex); 455862306a36Sopenharmony_ci D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); 455962306a36Sopenharmony_ci 456062306a36Sopenharmony_ci if (!il_is_ready_rf(il)) { 456162306a36Sopenharmony_ci IL_WARN("Try to add interface when device not ready\n"); 456262306a36Sopenharmony_ci err = -EINVAL; 456362306a36Sopenharmony_ci goto out; 456462306a36Sopenharmony_ci } 456562306a36Sopenharmony_ci 456662306a36Sopenharmony_ci /* 456762306a36Sopenharmony_ci * We do not support multiple virtual interfaces, but on hardware reset 456862306a36Sopenharmony_ci * we have to add the same interface again. 456962306a36Sopenharmony_ci */ 457062306a36Sopenharmony_ci reset = (il->vif == vif); 457162306a36Sopenharmony_ci if (il->vif && !reset) { 457262306a36Sopenharmony_ci err = -EOPNOTSUPP; 457362306a36Sopenharmony_ci goto out; 457462306a36Sopenharmony_ci } 457562306a36Sopenharmony_ci 457662306a36Sopenharmony_ci il->vif = vif; 457762306a36Sopenharmony_ci il->iw_mode = vif->type; 457862306a36Sopenharmony_ci 457962306a36Sopenharmony_ci err = il_set_mode(il); 458062306a36Sopenharmony_ci if (err) { 458162306a36Sopenharmony_ci IL_WARN("Fail to set mode %d\n", vif->type); 458262306a36Sopenharmony_ci if (!reset) { 458362306a36Sopenharmony_ci il->vif = NULL; 458462306a36Sopenharmony_ci il->iw_mode = NL80211_IFTYPE_STATION; 458562306a36Sopenharmony_ci } 458662306a36Sopenharmony_ci } 458762306a36Sopenharmony_ci 458862306a36Sopenharmony_ciout: 458962306a36Sopenharmony_ci D_MAC80211("leave err %d\n", err); 459062306a36Sopenharmony_ci mutex_unlock(&il->mutex); 459162306a36Sopenharmony_ci 459262306a36Sopenharmony_ci return err; 459362306a36Sopenharmony_ci} 459462306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_add_interface); 459562306a36Sopenharmony_ci 459662306a36Sopenharmony_cistatic void 459762306a36Sopenharmony_ciil_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif) 459862306a36Sopenharmony_ci{ 459962306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 460062306a36Sopenharmony_ci 460162306a36Sopenharmony_ci if (il->scan_vif == vif) { 460262306a36Sopenharmony_ci il_scan_cancel_timeout(il, 200); 460362306a36Sopenharmony_ci il_force_scan_end(il); 460462306a36Sopenharmony_ci } 460562306a36Sopenharmony_ci 460662306a36Sopenharmony_ci il_set_mode(il); 460762306a36Sopenharmony_ci} 460862306a36Sopenharmony_ci 460962306a36Sopenharmony_civoid 461062306a36Sopenharmony_ciil_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 461162306a36Sopenharmony_ci{ 461262306a36Sopenharmony_ci struct il_priv *il = hw->priv; 461362306a36Sopenharmony_ci 461462306a36Sopenharmony_ci mutex_lock(&il->mutex); 461562306a36Sopenharmony_ci D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); 461662306a36Sopenharmony_ci 461762306a36Sopenharmony_ci WARN_ON(il->vif != vif); 461862306a36Sopenharmony_ci il->vif = NULL; 461962306a36Sopenharmony_ci il->iw_mode = NL80211_IFTYPE_UNSPECIFIED; 462062306a36Sopenharmony_ci il_teardown_interface(il, vif); 462162306a36Sopenharmony_ci eth_zero_addr(il->bssid); 462262306a36Sopenharmony_ci 462362306a36Sopenharmony_ci D_MAC80211("leave\n"); 462462306a36Sopenharmony_ci mutex_unlock(&il->mutex); 462562306a36Sopenharmony_ci} 462662306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_remove_interface); 462762306a36Sopenharmony_ci 462862306a36Sopenharmony_ciint 462962306a36Sopenharmony_ciil_alloc_txq_mem(struct il_priv *il) 463062306a36Sopenharmony_ci{ 463162306a36Sopenharmony_ci if (!il->txq) 463262306a36Sopenharmony_ci il->txq = 463362306a36Sopenharmony_ci kcalloc(il->cfg->num_of_queues, 463462306a36Sopenharmony_ci sizeof(struct il_tx_queue), 463562306a36Sopenharmony_ci GFP_KERNEL); 463662306a36Sopenharmony_ci if (!il->txq) { 463762306a36Sopenharmony_ci IL_ERR("Not enough memory for txq\n"); 463862306a36Sopenharmony_ci return -ENOMEM; 463962306a36Sopenharmony_ci } 464062306a36Sopenharmony_ci return 0; 464162306a36Sopenharmony_ci} 464262306a36Sopenharmony_ciEXPORT_SYMBOL(il_alloc_txq_mem); 464362306a36Sopenharmony_ci 464462306a36Sopenharmony_civoid 464562306a36Sopenharmony_ciil_free_txq_mem(struct il_priv *il) 464662306a36Sopenharmony_ci{ 464762306a36Sopenharmony_ci kfree(il->txq); 464862306a36Sopenharmony_ci il->txq = NULL; 464962306a36Sopenharmony_ci} 465062306a36Sopenharmony_ciEXPORT_SYMBOL(il_free_txq_mem); 465162306a36Sopenharmony_ci 465262306a36Sopenharmony_ciint 465362306a36Sopenharmony_ciil_force_reset(struct il_priv *il, bool external) 465462306a36Sopenharmony_ci{ 465562306a36Sopenharmony_ci struct il_force_reset *force_reset; 465662306a36Sopenharmony_ci 465762306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 465862306a36Sopenharmony_ci return -EINVAL; 465962306a36Sopenharmony_ci 466062306a36Sopenharmony_ci force_reset = &il->force_reset; 466162306a36Sopenharmony_ci force_reset->reset_request_count++; 466262306a36Sopenharmony_ci if (!external) { 466362306a36Sopenharmony_ci if (force_reset->last_force_reset_jiffies && 466462306a36Sopenharmony_ci time_after(force_reset->last_force_reset_jiffies + 466562306a36Sopenharmony_ci force_reset->reset_duration, jiffies)) { 466662306a36Sopenharmony_ci D_INFO("force reset rejected\n"); 466762306a36Sopenharmony_ci force_reset->reset_reject_count++; 466862306a36Sopenharmony_ci return -EAGAIN; 466962306a36Sopenharmony_ci } 467062306a36Sopenharmony_ci } 467162306a36Sopenharmony_ci force_reset->reset_success_count++; 467262306a36Sopenharmony_ci force_reset->last_force_reset_jiffies = jiffies; 467362306a36Sopenharmony_ci 467462306a36Sopenharmony_ci /* 467562306a36Sopenharmony_ci * if the request is from external(ex: debugfs), 467662306a36Sopenharmony_ci * then always perform the request in regardless the module 467762306a36Sopenharmony_ci * parameter setting 467862306a36Sopenharmony_ci * if the request is from internal (uCode error or driver 467962306a36Sopenharmony_ci * detect failure), then fw_restart module parameter 468062306a36Sopenharmony_ci * need to be check before performing firmware reload 468162306a36Sopenharmony_ci */ 468262306a36Sopenharmony_ci 468362306a36Sopenharmony_ci if (!external && !il->cfg->mod_params->restart_fw) { 468462306a36Sopenharmony_ci D_INFO("Cancel firmware reload based on " 468562306a36Sopenharmony_ci "module parameter setting\n"); 468662306a36Sopenharmony_ci return 0; 468762306a36Sopenharmony_ci } 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci IL_ERR("On demand firmware reload\n"); 469062306a36Sopenharmony_ci 469162306a36Sopenharmony_ci /* Set the FW error flag -- cleared on il_down */ 469262306a36Sopenharmony_ci set_bit(S_FW_ERROR, &il->status); 469362306a36Sopenharmony_ci wake_up(&il->wait_command_queue); 469462306a36Sopenharmony_ci /* 469562306a36Sopenharmony_ci * Keep the restart process from trying to send host 469662306a36Sopenharmony_ci * commands by clearing the INIT status bit 469762306a36Sopenharmony_ci */ 469862306a36Sopenharmony_ci clear_bit(S_READY, &il->status); 469962306a36Sopenharmony_ci queue_work(il->workqueue, &il->restart); 470062306a36Sopenharmony_ci 470162306a36Sopenharmony_ci return 0; 470262306a36Sopenharmony_ci} 470362306a36Sopenharmony_ciEXPORT_SYMBOL(il_force_reset); 470462306a36Sopenharmony_ci 470562306a36Sopenharmony_ciint 470662306a36Sopenharmony_ciil_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 470762306a36Sopenharmony_ci enum nl80211_iftype newtype, bool newp2p) 470862306a36Sopenharmony_ci{ 470962306a36Sopenharmony_ci struct il_priv *il = hw->priv; 471062306a36Sopenharmony_ci int err; 471162306a36Sopenharmony_ci 471262306a36Sopenharmony_ci mutex_lock(&il->mutex); 471362306a36Sopenharmony_ci D_MAC80211("enter: type %d, addr %pM newtype %d newp2p %d\n", 471462306a36Sopenharmony_ci vif->type, vif->addr, newtype, newp2p); 471562306a36Sopenharmony_ci 471662306a36Sopenharmony_ci if (newp2p) { 471762306a36Sopenharmony_ci err = -EOPNOTSUPP; 471862306a36Sopenharmony_ci goto out; 471962306a36Sopenharmony_ci } 472062306a36Sopenharmony_ci 472162306a36Sopenharmony_ci if (!il->vif || !il_is_ready_rf(il)) { 472262306a36Sopenharmony_ci /* 472362306a36Sopenharmony_ci * Huh? But wait ... this can maybe happen when 472462306a36Sopenharmony_ci * we're in the middle of a firmware restart! 472562306a36Sopenharmony_ci */ 472662306a36Sopenharmony_ci err = -EBUSY; 472762306a36Sopenharmony_ci goto out; 472862306a36Sopenharmony_ci } 472962306a36Sopenharmony_ci 473062306a36Sopenharmony_ci /* success */ 473162306a36Sopenharmony_ci vif->type = newtype; 473262306a36Sopenharmony_ci vif->p2p = false; 473362306a36Sopenharmony_ci il->iw_mode = newtype; 473462306a36Sopenharmony_ci il_teardown_interface(il, vif); 473562306a36Sopenharmony_ci err = 0; 473662306a36Sopenharmony_ci 473762306a36Sopenharmony_ciout: 473862306a36Sopenharmony_ci D_MAC80211("leave err %d\n", err); 473962306a36Sopenharmony_ci mutex_unlock(&il->mutex); 474062306a36Sopenharmony_ci 474162306a36Sopenharmony_ci return err; 474262306a36Sopenharmony_ci} 474362306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_change_interface); 474462306a36Sopenharmony_ci 474562306a36Sopenharmony_civoid il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 474662306a36Sopenharmony_ci u32 queues, bool drop) 474762306a36Sopenharmony_ci{ 474862306a36Sopenharmony_ci struct il_priv *il = hw->priv; 474962306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(500); 475062306a36Sopenharmony_ci int i; 475162306a36Sopenharmony_ci 475262306a36Sopenharmony_ci mutex_lock(&il->mutex); 475362306a36Sopenharmony_ci D_MAC80211("enter\n"); 475462306a36Sopenharmony_ci 475562306a36Sopenharmony_ci if (il->txq == NULL) 475662306a36Sopenharmony_ci goto out; 475762306a36Sopenharmony_ci 475862306a36Sopenharmony_ci for (i = 0; i < il->hw_params.max_txq_num; i++) { 475962306a36Sopenharmony_ci struct il_queue *q; 476062306a36Sopenharmony_ci 476162306a36Sopenharmony_ci if (i == il->cmd_queue) 476262306a36Sopenharmony_ci continue; 476362306a36Sopenharmony_ci 476462306a36Sopenharmony_ci q = &il->txq[i].q; 476562306a36Sopenharmony_ci if (q->read_ptr == q->write_ptr) 476662306a36Sopenharmony_ci continue; 476762306a36Sopenharmony_ci 476862306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 476962306a36Sopenharmony_ci IL_ERR("Failed to flush queue %d\n", q->id); 477062306a36Sopenharmony_ci break; 477162306a36Sopenharmony_ci } 477262306a36Sopenharmony_ci 477362306a36Sopenharmony_ci msleep(20); 477462306a36Sopenharmony_ci } 477562306a36Sopenharmony_ciout: 477662306a36Sopenharmony_ci D_MAC80211("leave\n"); 477762306a36Sopenharmony_ci mutex_unlock(&il->mutex); 477862306a36Sopenharmony_ci} 477962306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_flush); 478062306a36Sopenharmony_ci 478162306a36Sopenharmony_ci/* 478262306a36Sopenharmony_ci * On every watchdog tick we check (latest) time stamp. If it does not 478362306a36Sopenharmony_ci * change during timeout period and queue is not empty we reset firmware. 478462306a36Sopenharmony_ci */ 478562306a36Sopenharmony_cistatic int 478662306a36Sopenharmony_ciil_check_stuck_queue(struct il_priv *il, int cnt) 478762306a36Sopenharmony_ci{ 478862306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[cnt]; 478962306a36Sopenharmony_ci struct il_queue *q = &txq->q; 479062306a36Sopenharmony_ci unsigned long timeout; 479162306a36Sopenharmony_ci unsigned long now = jiffies; 479262306a36Sopenharmony_ci int ret; 479362306a36Sopenharmony_ci 479462306a36Sopenharmony_ci if (q->read_ptr == q->write_ptr) { 479562306a36Sopenharmony_ci txq->time_stamp = now; 479662306a36Sopenharmony_ci return 0; 479762306a36Sopenharmony_ci } 479862306a36Sopenharmony_ci 479962306a36Sopenharmony_ci timeout = 480062306a36Sopenharmony_ci txq->time_stamp + 480162306a36Sopenharmony_ci msecs_to_jiffies(il->cfg->wd_timeout); 480262306a36Sopenharmony_ci 480362306a36Sopenharmony_ci if (time_after(now, timeout)) { 480462306a36Sopenharmony_ci IL_ERR("Queue %d stuck for %u ms.\n", q->id, 480562306a36Sopenharmony_ci jiffies_to_msecs(now - txq->time_stamp)); 480662306a36Sopenharmony_ci ret = il_force_reset(il, false); 480762306a36Sopenharmony_ci return (ret == -EAGAIN) ? 0 : 1; 480862306a36Sopenharmony_ci } 480962306a36Sopenharmony_ci 481062306a36Sopenharmony_ci return 0; 481162306a36Sopenharmony_ci} 481262306a36Sopenharmony_ci 481362306a36Sopenharmony_ci/* 481462306a36Sopenharmony_ci * Making watchdog tick be a quarter of timeout assure we will 481562306a36Sopenharmony_ci * discover the queue hung between timeout and 1.25*timeout 481662306a36Sopenharmony_ci */ 481762306a36Sopenharmony_ci#define IL_WD_TICK(timeout) ((timeout) / 4) 481862306a36Sopenharmony_ci 481962306a36Sopenharmony_ci/* 482062306a36Sopenharmony_ci * Watchdog timer callback, we check each tx queue for stuck, if hung 482162306a36Sopenharmony_ci * we reset the firmware. If everything is fine just rearm the timer. 482262306a36Sopenharmony_ci */ 482362306a36Sopenharmony_civoid 482462306a36Sopenharmony_ciil_bg_watchdog(struct timer_list *t) 482562306a36Sopenharmony_ci{ 482662306a36Sopenharmony_ci struct il_priv *il = from_timer(il, t, watchdog); 482762306a36Sopenharmony_ci int cnt; 482862306a36Sopenharmony_ci unsigned long timeout; 482962306a36Sopenharmony_ci 483062306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 483162306a36Sopenharmony_ci return; 483262306a36Sopenharmony_ci 483362306a36Sopenharmony_ci timeout = il->cfg->wd_timeout; 483462306a36Sopenharmony_ci if (timeout == 0) 483562306a36Sopenharmony_ci return; 483662306a36Sopenharmony_ci 483762306a36Sopenharmony_ci /* monitor and check for stuck cmd queue */ 483862306a36Sopenharmony_ci if (il_check_stuck_queue(il, il->cmd_queue)) 483962306a36Sopenharmony_ci return; 484062306a36Sopenharmony_ci 484162306a36Sopenharmony_ci /* monitor and check for other stuck queues */ 484262306a36Sopenharmony_ci for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { 484362306a36Sopenharmony_ci /* skip as we already checked the command queue */ 484462306a36Sopenharmony_ci if (cnt == il->cmd_queue) 484562306a36Sopenharmony_ci continue; 484662306a36Sopenharmony_ci if (il_check_stuck_queue(il, cnt)) 484762306a36Sopenharmony_ci return; 484862306a36Sopenharmony_ci } 484962306a36Sopenharmony_ci 485062306a36Sopenharmony_ci mod_timer(&il->watchdog, 485162306a36Sopenharmony_ci jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); 485262306a36Sopenharmony_ci} 485362306a36Sopenharmony_ciEXPORT_SYMBOL(il_bg_watchdog); 485462306a36Sopenharmony_ci 485562306a36Sopenharmony_civoid 485662306a36Sopenharmony_ciil_setup_watchdog(struct il_priv *il) 485762306a36Sopenharmony_ci{ 485862306a36Sopenharmony_ci unsigned int timeout = il->cfg->wd_timeout; 485962306a36Sopenharmony_ci 486062306a36Sopenharmony_ci if (timeout) 486162306a36Sopenharmony_ci mod_timer(&il->watchdog, 486262306a36Sopenharmony_ci jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); 486362306a36Sopenharmony_ci else 486462306a36Sopenharmony_ci del_timer(&il->watchdog); 486562306a36Sopenharmony_ci} 486662306a36Sopenharmony_ciEXPORT_SYMBOL(il_setup_watchdog); 486762306a36Sopenharmony_ci 486862306a36Sopenharmony_ci/* 486962306a36Sopenharmony_ci * extended beacon time format 487062306a36Sopenharmony_ci * time in usec will be changed into a 32-bit value in extended:internal format 487162306a36Sopenharmony_ci * the extended part is the beacon counts 487262306a36Sopenharmony_ci * the internal part is the time in usec within one beacon interval 487362306a36Sopenharmony_ci */ 487462306a36Sopenharmony_ciu32 487562306a36Sopenharmony_ciil_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval) 487662306a36Sopenharmony_ci{ 487762306a36Sopenharmony_ci u32 quot; 487862306a36Sopenharmony_ci u32 rem; 487962306a36Sopenharmony_ci u32 interval = beacon_interval * TIME_UNIT; 488062306a36Sopenharmony_ci 488162306a36Sopenharmony_ci if (!interval || !usec) 488262306a36Sopenharmony_ci return 0; 488362306a36Sopenharmony_ci 488462306a36Sopenharmony_ci quot = 488562306a36Sopenharmony_ci (usec / 488662306a36Sopenharmony_ci interval) & (il_beacon_time_mask_high(il, 488762306a36Sopenharmony_ci il->hw_params. 488862306a36Sopenharmony_ci beacon_time_tsf_bits) >> il-> 488962306a36Sopenharmony_ci hw_params.beacon_time_tsf_bits); 489062306a36Sopenharmony_ci rem = 489162306a36Sopenharmony_ci (usec % interval) & il_beacon_time_mask_low(il, 489262306a36Sopenharmony_ci il->hw_params. 489362306a36Sopenharmony_ci beacon_time_tsf_bits); 489462306a36Sopenharmony_ci 489562306a36Sopenharmony_ci return (quot << il->hw_params.beacon_time_tsf_bits) + rem; 489662306a36Sopenharmony_ci} 489762306a36Sopenharmony_ciEXPORT_SYMBOL(il_usecs_to_beacons); 489862306a36Sopenharmony_ci 489962306a36Sopenharmony_ci/* base is usually what we get from ucode with each received frame, 490062306a36Sopenharmony_ci * the same as HW timer counter counting down 490162306a36Sopenharmony_ci */ 490262306a36Sopenharmony_ci__le32 490362306a36Sopenharmony_ciil_add_beacon_time(struct il_priv *il, u32 base, u32 addon, 490462306a36Sopenharmony_ci u32 beacon_interval) 490562306a36Sopenharmony_ci{ 490662306a36Sopenharmony_ci u32 base_low = base & il_beacon_time_mask_low(il, 490762306a36Sopenharmony_ci il->hw_params. 490862306a36Sopenharmony_ci beacon_time_tsf_bits); 490962306a36Sopenharmony_ci u32 addon_low = addon & il_beacon_time_mask_low(il, 491062306a36Sopenharmony_ci il->hw_params. 491162306a36Sopenharmony_ci beacon_time_tsf_bits); 491262306a36Sopenharmony_ci u32 interval = beacon_interval * TIME_UNIT; 491362306a36Sopenharmony_ci u32 res = (base & il_beacon_time_mask_high(il, 491462306a36Sopenharmony_ci il->hw_params. 491562306a36Sopenharmony_ci beacon_time_tsf_bits)) + 491662306a36Sopenharmony_ci (addon & il_beacon_time_mask_high(il, 491762306a36Sopenharmony_ci il->hw_params. 491862306a36Sopenharmony_ci beacon_time_tsf_bits)); 491962306a36Sopenharmony_ci 492062306a36Sopenharmony_ci if (base_low > addon_low) 492162306a36Sopenharmony_ci res += base_low - addon_low; 492262306a36Sopenharmony_ci else if (base_low < addon_low) { 492362306a36Sopenharmony_ci res += interval + base_low - addon_low; 492462306a36Sopenharmony_ci res += (1 << il->hw_params.beacon_time_tsf_bits); 492562306a36Sopenharmony_ci } else 492662306a36Sopenharmony_ci res += (1 << il->hw_params.beacon_time_tsf_bits); 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_ci return cpu_to_le32(res); 492962306a36Sopenharmony_ci} 493062306a36Sopenharmony_ciEXPORT_SYMBOL(il_add_beacon_time); 493162306a36Sopenharmony_ci 493262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 493362306a36Sopenharmony_ci 493462306a36Sopenharmony_cistatic int 493562306a36Sopenharmony_ciil_pci_suspend(struct device *device) 493662306a36Sopenharmony_ci{ 493762306a36Sopenharmony_ci struct il_priv *il = dev_get_drvdata(device); 493862306a36Sopenharmony_ci 493962306a36Sopenharmony_ci /* 494062306a36Sopenharmony_ci * This function is called when system goes into suspend state 494162306a36Sopenharmony_ci * mac80211 will call il_mac_stop() from the mac80211 suspend function 494262306a36Sopenharmony_ci * first but since il_mac_stop() has no knowledge of who the caller is, 494362306a36Sopenharmony_ci * it will not call apm_ops.stop() to stop the DMA operation. 494462306a36Sopenharmony_ci * Calling apm_ops.stop here to make sure we stop the DMA. 494562306a36Sopenharmony_ci */ 494662306a36Sopenharmony_ci il_apm_stop(il); 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_ci return 0; 494962306a36Sopenharmony_ci} 495062306a36Sopenharmony_ci 495162306a36Sopenharmony_cistatic int 495262306a36Sopenharmony_ciil_pci_resume(struct device *device) 495362306a36Sopenharmony_ci{ 495462306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 495562306a36Sopenharmony_ci struct il_priv *il = pci_get_drvdata(pdev); 495662306a36Sopenharmony_ci bool hw_rfkill = false; 495762306a36Sopenharmony_ci 495862306a36Sopenharmony_ci /* 495962306a36Sopenharmony_ci * We disable the RETRY_TIMEOUT register (0x41) to keep 496062306a36Sopenharmony_ci * PCI Tx retries from interfering with C3 CPU state. 496162306a36Sopenharmony_ci */ 496262306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); 496362306a36Sopenharmony_ci 496462306a36Sopenharmony_ci il_enable_interrupts(il); 496562306a36Sopenharmony_ci 496662306a36Sopenharmony_ci if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) 496762306a36Sopenharmony_ci hw_rfkill = true; 496862306a36Sopenharmony_ci 496962306a36Sopenharmony_ci if (hw_rfkill) 497062306a36Sopenharmony_ci set_bit(S_RFKILL, &il->status); 497162306a36Sopenharmony_ci else 497262306a36Sopenharmony_ci clear_bit(S_RFKILL, &il->status); 497362306a36Sopenharmony_ci 497462306a36Sopenharmony_ci wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rfkill); 497562306a36Sopenharmony_ci 497662306a36Sopenharmony_ci return 0; 497762306a36Sopenharmony_ci} 497862306a36Sopenharmony_ci 497962306a36Sopenharmony_ciSIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume); 498062306a36Sopenharmony_ciEXPORT_SYMBOL(il_pm_ops); 498162306a36Sopenharmony_ci 498262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 498362306a36Sopenharmony_ci 498462306a36Sopenharmony_cistatic void 498562306a36Sopenharmony_ciil_update_qos(struct il_priv *il) 498662306a36Sopenharmony_ci{ 498762306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 498862306a36Sopenharmony_ci return; 498962306a36Sopenharmony_ci 499062306a36Sopenharmony_ci il->qos_data.def_qos_parm.qos_flags = 0; 499162306a36Sopenharmony_ci 499262306a36Sopenharmony_ci if (il->qos_data.qos_active) 499362306a36Sopenharmony_ci il->qos_data.def_qos_parm.qos_flags |= 499462306a36Sopenharmony_ci QOS_PARAM_FLG_UPDATE_EDCA_MSK; 499562306a36Sopenharmony_ci 499662306a36Sopenharmony_ci if (il->ht.enabled) 499762306a36Sopenharmony_ci il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; 499862306a36Sopenharmony_ci 499962306a36Sopenharmony_ci D_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n", 500062306a36Sopenharmony_ci il->qos_data.qos_active, il->qos_data.def_qos_parm.qos_flags); 500162306a36Sopenharmony_ci 500262306a36Sopenharmony_ci il_send_cmd_pdu_async(il, C_QOS_PARAM, sizeof(struct il_qosparam_cmd), 500362306a36Sopenharmony_ci &il->qos_data.def_qos_parm, NULL); 500462306a36Sopenharmony_ci} 500562306a36Sopenharmony_ci 500662306a36Sopenharmony_ci/* 500762306a36Sopenharmony_ci * il_mac_config - mac80211 config callback 500862306a36Sopenharmony_ci */ 500962306a36Sopenharmony_ciint 501062306a36Sopenharmony_ciil_mac_config(struct ieee80211_hw *hw, u32 changed) 501162306a36Sopenharmony_ci{ 501262306a36Sopenharmony_ci struct il_priv *il = hw->priv; 501362306a36Sopenharmony_ci const struct il_channel_info *ch_info; 501462306a36Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 501562306a36Sopenharmony_ci struct ieee80211_channel *channel = conf->chandef.chan; 501662306a36Sopenharmony_ci struct il_ht_config *ht_conf = &il->current_ht_config; 501762306a36Sopenharmony_ci unsigned long flags = 0; 501862306a36Sopenharmony_ci int ret = 0; 501962306a36Sopenharmony_ci u16 ch; 502062306a36Sopenharmony_ci int scan_active = 0; 502162306a36Sopenharmony_ci bool ht_changed = false; 502262306a36Sopenharmony_ci 502362306a36Sopenharmony_ci mutex_lock(&il->mutex); 502462306a36Sopenharmony_ci D_MAC80211("enter: channel %d changed 0x%X\n", channel->hw_value, 502562306a36Sopenharmony_ci changed); 502662306a36Sopenharmony_ci 502762306a36Sopenharmony_ci if (unlikely(test_bit(S_SCANNING, &il->status))) { 502862306a36Sopenharmony_ci scan_active = 1; 502962306a36Sopenharmony_ci D_MAC80211("scan active\n"); 503062306a36Sopenharmony_ci } 503162306a36Sopenharmony_ci 503262306a36Sopenharmony_ci if (changed & 503362306a36Sopenharmony_ci (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { 503462306a36Sopenharmony_ci /* mac80211 uses static for non-HT which is what we want */ 503562306a36Sopenharmony_ci il->current_ht_config.smps = conf->smps_mode; 503662306a36Sopenharmony_ci 503762306a36Sopenharmony_ci /* 503862306a36Sopenharmony_ci * Recalculate chain counts. 503962306a36Sopenharmony_ci * 504062306a36Sopenharmony_ci * If monitor mode is enabled then mac80211 will 504162306a36Sopenharmony_ci * set up the SM PS mode to OFF if an HT channel is 504262306a36Sopenharmony_ci * configured. 504362306a36Sopenharmony_ci */ 504462306a36Sopenharmony_ci if (il->ops->set_rxon_chain) 504562306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 504662306a36Sopenharmony_ci } 504762306a36Sopenharmony_ci 504862306a36Sopenharmony_ci /* during scanning mac80211 will delay channel setting until 504962306a36Sopenharmony_ci * scan finish with changed = 0 505062306a36Sopenharmony_ci */ 505162306a36Sopenharmony_ci if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { 505262306a36Sopenharmony_ci 505362306a36Sopenharmony_ci if (scan_active) 505462306a36Sopenharmony_ci goto set_ch_out; 505562306a36Sopenharmony_ci 505662306a36Sopenharmony_ci ch = channel->hw_value; 505762306a36Sopenharmony_ci ch_info = il_get_channel_info(il, channel->band, ch); 505862306a36Sopenharmony_ci if (!il_is_channel_valid(ch_info)) { 505962306a36Sopenharmony_ci D_MAC80211("leave - invalid channel\n"); 506062306a36Sopenharmony_ci ret = -EINVAL; 506162306a36Sopenharmony_ci goto set_ch_out; 506262306a36Sopenharmony_ci } 506362306a36Sopenharmony_ci 506462306a36Sopenharmony_ci if (il->iw_mode == NL80211_IFTYPE_ADHOC && 506562306a36Sopenharmony_ci !il_is_channel_ibss(ch_info)) { 506662306a36Sopenharmony_ci D_MAC80211("leave - not IBSS channel\n"); 506762306a36Sopenharmony_ci ret = -EINVAL; 506862306a36Sopenharmony_ci goto set_ch_out; 506962306a36Sopenharmony_ci } 507062306a36Sopenharmony_ci 507162306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 507262306a36Sopenharmony_ci 507362306a36Sopenharmony_ci /* Configure HT40 channels */ 507462306a36Sopenharmony_ci if (il->ht.enabled != conf_is_ht(conf)) { 507562306a36Sopenharmony_ci il->ht.enabled = conf_is_ht(conf); 507662306a36Sopenharmony_ci ht_changed = true; 507762306a36Sopenharmony_ci } 507862306a36Sopenharmony_ci if (il->ht.enabled) { 507962306a36Sopenharmony_ci if (conf_is_ht40_minus(conf)) { 508062306a36Sopenharmony_ci il->ht.extension_chan_offset = 508162306a36Sopenharmony_ci IEEE80211_HT_PARAM_CHA_SEC_BELOW; 508262306a36Sopenharmony_ci il->ht.is_40mhz = true; 508362306a36Sopenharmony_ci } else if (conf_is_ht40_plus(conf)) { 508462306a36Sopenharmony_ci il->ht.extension_chan_offset = 508562306a36Sopenharmony_ci IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 508662306a36Sopenharmony_ci il->ht.is_40mhz = true; 508762306a36Sopenharmony_ci } else { 508862306a36Sopenharmony_ci il->ht.extension_chan_offset = 508962306a36Sopenharmony_ci IEEE80211_HT_PARAM_CHA_SEC_NONE; 509062306a36Sopenharmony_ci il->ht.is_40mhz = false; 509162306a36Sopenharmony_ci } 509262306a36Sopenharmony_ci } else 509362306a36Sopenharmony_ci il->ht.is_40mhz = false; 509462306a36Sopenharmony_ci 509562306a36Sopenharmony_ci /* 509662306a36Sopenharmony_ci * Default to no protection. Protection mode will 509762306a36Sopenharmony_ci * later be set from BSS config in il_ht_conf 509862306a36Sopenharmony_ci */ 509962306a36Sopenharmony_ci il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; 510062306a36Sopenharmony_ci 510162306a36Sopenharmony_ci /* if we are switching from ht to 2.4 clear flags 510262306a36Sopenharmony_ci * from any ht related info since 2.4 does not 510362306a36Sopenharmony_ci * support ht */ 510462306a36Sopenharmony_ci if ((le16_to_cpu(il->staging.channel) != ch)) 510562306a36Sopenharmony_ci il->staging.flags = 0; 510662306a36Sopenharmony_ci 510762306a36Sopenharmony_ci il_set_rxon_channel(il, channel); 510862306a36Sopenharmony_ci il_set_rxon_ht(il, ht_conf); 510962306a36Sopenharmony_ci 511062306a36Sopenharmony_ci il_set_flags_for_band(il, channel->band, il->vif); 511162306a36Sopenharmony_ci 511262306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 511362306a36Sopenharmony_ci 511462306a36Sopenharmony_ci if (il->ops->update_bcast_stations) 511562306a36Sopenharmony_ci ret = il->ops->update_bcast_stations(il); 511662306a36Sopenharmony_ci 511762306a36Sopenharmony_ciset_ch_out: 511862306a36Sopenharmony_ci /* The list of supported rates and rate mask can be different 511962306a36Sopenharmony_ci * for each band; since the band may have changed, reset 512062306a36Sopenharmony_ci * the rate mask to what mac80211 lists */ 512162306a36Sopenharmony_ci il_set_rate(il); 512262306a36Sopenharmony_ci } 512362306a36Sopenharmony_ci 512462306a36Sopenharmony_ci if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { 512562306a36Sopenharmony_ci il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS); 512662306a36Sopenharmony_ci if (!il->power_data.ps_disabled) 512762306a36Sopenharmony_ci IL_WARN_ONCE("Enabling power save might cause firmware crashes\n"); 512862306a36Sopenharmony_ci ret = il_power_update_mode(il, false); 512962306a36Sopenharmony_ci if (ret) 513062306a36Sopenharmony_ci D_MAC80211("Error setting sleep level\n"); 513162306a36Sopenharmony_ci } 513262306a36Sopenharmony_ci 513362306a36Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_POWER) { 513462306a36Sopenharmony_ci D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt, 513562306a36Sopenharmony_ci conf->power_level); 513662306a36Sopenharmony_ci 513762306a36Sopenharmony_ci il_set_tx_power(il, conf->power_level, false); 513862306a36Sopenharmony_ci } 513962306a36Sopenharmony_ci 514062306a36Sopenharmony_ci if (!il_is_ready(il)) { 514162306a36Sopenharmony_ci D_MAC80211("leave - not ready\n"); 514262306a36Sopenharmony_ci goto out; 514362306a36Sopenharmony_ci } 514462306a36Sopenharmony_ci 514562306a36Sopenharmony_ci if (scan_active) 514662306a36Sopenharmony_ci goto out; 514762306a36Sopenharmony_ci 514862306a36Sopenharmony_ci if (memcmp(&il->active, &il->staging, sizeof(il->staging))) 514962306a36Sopenharmony_ci il_commit_rxon(il); 515062306a36Sopenharmony_ci else 515162306a36Sopenharmony_ci D_INFO("Not re-sending same RXON configuration.\n"); 515262306a36Sopenharmony_ci if (ht_changed) 515362306a36Sopenharmony_ci il_update_qos(il); 515462306a36Sopenharmony_ci 515562306a36Sopenharmony_ciout: 515662306a36Sopenharmony_ci D_MAC80211("leave ret %d\n", ret); 515762306a36Sopenharmony_ci mutex_unlock(&il->mutex); 515862306a36Sopenharmony_ci 515962306a36Sopenharmony_ci return ret; 516062306a36Sopenharmony_ci} 516162306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_config); 516262306a36Sopenharmony_ci 516362306a36Sopenharmony_civoid 516462306a36Sopenharmony_ciil_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 516562306a36Sopenharmony_ci{ 516662306a36Sopenharmony_ci struct il_priv *il = hw->priv; 516762306a36Sopenharmony_ci unsigned long flags; 516862306a36Sopenharmony_ci 516962306a36Sopenharmony_ci mutex_lock(&il->mutex); 517062306a36Sopenharmony_ci D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); 517162306a36Sopenharmony_ci 517262306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 517362306a36Sopenharmony_ci 517462306a36Sopenharmony_ci memset(&il->current_ht_config, 0, sizeof(struct il_ht_config)); 517562306a36Sopenharmony_ci 517662306a36Sopenharmony_ci /* new association get rid of ibss beacon skb */ 517762306a36Sopenharmony_ci dev_consume_skb_irq(il->beacon_skb); 517862306a36Sopenharmony_ci il->beacon_skb = NULL; 517962306a36Sopenharmony_ci il->timestamp = 0; 518062306a36Sopenharmony_ci 518162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 518262306a36Sopenharmony_ci 518362306a36Sopenharmony_ci il_scan_cancel_timeout(il, 100); 518462306a36Sopenharmony_ci if (!il_is_ready_rf(il)) { 518562306a36Sopenharmony_ci D_MAC80211("leave - not ready\n"); 518662306a36Sopenharmony_ci mutex_unlock(&il->mutex); 518762306a36Sopenharmony_ci return; 518862306a36Sopenharmony_ci } 518962306a36Sopenharmony_ci 519062306a36Sopenharmony_ci /* we are restarting association process */ 519162306a36Sopenharmony_ci il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 519262306a36Sopenharmony_ci il_commit_rxon(il); 519362306a36Sopenharmony_ci 519462306a36Sopenharmony_ci il_set_rate(il); 519562306a36Sopenharmony_ci 519662306a36Sopenharmony_ci D_MAC80211("leave\n"); 519762306a36Sopenharmony_ci mutex_unlock(&il->mutex); 519862306a36Sopenharmony_ci} 519962306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_reset_tsf); 520062306a36Sopenharmony_ci 520162306a36Sopenharmony_cistatic void 520262306a36Sopenharmony_ciil_ht_conf(struct il_priv *il, struct ieee80211_vif *vif) 520362306a36Sopenharmony_ci{ 520462306a36Sopenharmony_ci struct il_ht_config *ht_conf = &il->current_ht_config; 520562306a36Sopenharmony_ci struct ieee80211_sta *sta; 520662306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 520762306a36Sopenharmony_ci 520862306a36Sopenharmony_ci D_ASSOC("enter:\n"); 520962306a36Sopenharmony_ci 521062306a36Sopenharmony_ci if (!il->ht.enabled) 521162306a36Sopenharmony_ci return; 521262306a36Sopenharmony_ci 521362306a36Sopenharmony_ci il->ht.protection = 521462306a36Sopenharmony_ci bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; 521562306a36Sopenharmony_ci il->ht.non_gf_sta_present = 521662306a36Sopenharmony_ci !!(bss_conf-> 521762306a36Sopenharmony_ci ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); 521862306a36Sopenharmony_ci 521962306a36Sopenharmony_ci ht_conf->single_chain_sufficient = false; 522062306a36Sopenharmony_ci 522162306a36Sopenharmony_ci switch (vif->type) { 522262306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 522362306a36Sopenharmony_ci rcu_read_lock(); 522462306a36Sopenharmony_ci sta = ieee80211_find_sta(vif, bss_conf->bssid); 522562306a36Sopenharmony_ci if (sta) { 522662306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; 522762306a36Sopenharmony_ci int maxstreams; 522862306a36Sopenharmony_ci 522962306a36Sopenharmony_ci maxstreams = 523062306a36Sopenharmony_ci (ht_cap->mcs. 523162306a36Sopenharmony_ci tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) 523262306a36Sopenharmony_ci >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; 523362306a36Sopenharmony_ci maxstreams += 1; 523462306a36Sopenharmony_ci 523562306a36Sopenharmony_ci if (ht_cap->mcs.rx_mask[1] == 0 && 523662306a36Sopenharmony_ci ht_cap->mcs.rx_mask[2] == 0) 523762306a36Sopenharmony_ci ht_conf->single_chain_sufficient = true; 523862306a36Sopenharmony_ci if (maxstreams <= 1) 523962306a36Sopenharmony_ci ht_conf->single_chain_sufficient = true; 524062306a36Sopenharmony_ci } else { 524162306a36Sopenharmony_ci /* 524262306a36Sopenharmony_ci * If at all, this can only happen through a race 524362306a36Sopenharmony_ci * when the AP disconnects us while we're still 524462306a36Sopenharmony_ci * setting up the connection, in that case mac80211 524562306a36Sopenharmony_ci * will soon tell us about that. 524662306a36Sopenharmony_ci */ 524762306a36Sopenharmony_ci ht_conf->single_chain_sufficient = true; 524862306a36Sopenharmony_ci } 524962306a36Sopenharmony_ci rcu_read_unlock(); 525062306a36Sopenharmony_ci break; 525162306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 525262306a36Sopenharmony_ci ht_conf->single_chain_sufficient = true; 525362306a36Sopenharmony_ci break; 525462306a36Sopenharmony_ci default: 525562306a36Sopenharmony_ci break; 525662306a36Sopenharmony_ci } 525762306a36Sopenharmony_ci 525862306a36Sopenharmony_ci D_ASSOC("leave\n"); 525962306a36Sopenharmony_ci} 526062306a36Sopenharmony_ci 526162306a36Sopenharmony_cistatic inline void 526262306a36Sopenharmony_ciil_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif) 526362306a36Sopenharmony_ci{ 526462306a36Sopenharmony_ci /* 526562306a36Sopenharmony_ci * inform the ucode that there is no longer an 526662306a36Sopenharmony_ci * association and that no more packets should be 526762306a36Sopenharmony_ci * sent 526862306a36Sopenharmony_ci */ 526962306a36Sopenharmony_ci il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 527062306a36Sopenharmony_ci il->staging.assoc_id = 0; 527162306a36Sopenharmony_ci il_commit_rxon(il); 527262306a36Sopenharmony_ci} 527362306a36Sopenharmony_ci 527462306a36Sopenharmony_cistatic void 527562306a36Sopenharmony_ciil_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 527662306a36Sopenharmony_ci{ 527762306a36Sopenharmony_ci struct il_priv *il = hw->priv; 527862306a36Sopenharmony_ci unsigned long flags; 527962306a36Sopenharmony_ci __le64 timestamp; 528062306a36Sopenharmony_ci struct sk_buff *skb = ieee80211_beacon_get(hw, vif, 0); 528162306a36Sopenharmony_ci 528262306a36Sopenharmony_ci if (!skb) 528362306a36Sopenharmony_ci return; 528462306a36Sopenharmony_ci 528562306a36Sopenharmony_ci D_MAC80211("enter\n"); 528662306a36Sopenharmony_ci 528762306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 528862306a36Sopenharmony_ci 528962306a36Sopenharmony_ci if (!il->beacon_enabled) { 529062306a36Sopenharmony_ci IL_ERR("update beacon with no beaconing enabled\n"); 529162306a36Sopenharmony_ci dev_kfree_skb(skb); 529262306a36Sopenharmony_ci return; 529362306a36Sopenharmony_ci } 529462306a36Sopenharmony_ci 529562306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 529662306a36Sopenharmony_ci dev_consume_skb_irq(il->beacon_skb); 529762306a36Sopenharmony_ci il->beacon_skb = skb; 529862306a36Sopenharmony_ci 529962306a36Sopenharmony_ci timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; 530062306a36Sopenharmony_ci il->timestamp = le64_to_cpu(timestamp); 530162306a36Sopenharmony_ci 530262306a36Sopenharmony_ci D_MAC80211("leave\n"); 530362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 530462306a36Sopenharmony_ci 530562306a36Sopenharmony_ci if (!il_is_ready_rf(il)) { 530662306a36Sopenharmony_ci D_MAC80211("leave - RF not ready\n"); 530762306a36Sopenharmony_ci return; 530862306a36Sopenharmony_ci } 530962306a36Sopenharmony_ci 531062306a36Sopenharmony_ci il->ops->post_associate(il); 531162306a36Sopenharmony_ci} 531262306a36Sopenharmony_ci 531362306a36Sopenharmony_civoid 531462306a36Sopenharmony_ciil_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 531562306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf, u64 changes) 531662306a36Sopenharmony_ci{ 531762306a36Sopenharmony_ci struct il_priv *il = hw->priv; 531862306a36Sopenharmony_ci int ret; 531962306a36Sopenharmony_ci 532062306a36Sopenharmony_ci mutex_lock(&il->mutex); 532162306a36Sopenharmony_ci D_MAC80211("enter: changes 0x%llx\n", changes); 532262306a36Sopenharmony_ci 532362306a36Sopenharmony_ci if (!il_is_alive(il)) { 532462306a36Sopenharmony_ci D_MAC80211("leave - not alive\n"); 532562306a36Sopenharmony_ci mutex_unlock(&il->mutex); 532662306a36Sopenharmony_ci return; 532762306a36Sopenharmony_ci } 532862306a36Sopenharmony_ci 532962306a36Sopenharmony_ci if (changes & BSS_CHANGED_QOS) { 533062306a36Sopenharmony_ci unsigned long flags; 533162306a36Sopenharmony_ci 533262306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 533362306a36Sopenharmony_ci il->qos_data.qos_active = bss_conf->qos; 533462306a36Sopenharmony_ci il_update_qos(il); 533562306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 533662306a36Sopenharmony_ci } 533762306a36Sopenharmony_ci 533862306a36Sopenharmony_ci if (changes & BSS_CHANGED_BEACON_ENABLED) { 533962306a36Sopenharmony_ci /* FIXME: can we remove beacon_enabled ? */ 534062306a36Sopenharmony_ci if (vif->bss_conf.enable_beacon) 534162306a36Sopenharmony_ci il->beacon_enabled = true; 534262306a36Sopenharmony_ci else 534362306a36Sopenharmony_ci il->beacon_enabled = false; 534462306a36Sopenharmony_ci } 534562306a36Sopenharmony_ci 534662306a36Sopenharmony_ci if (changes & BSS_CHANGED_BSSID) { 534762306a36Sopenharmony_ci D_MAC80211("BSSID %pM\n", bss_conf->bssid); 534862306a36Sopenharmony_ci 534962306a36Sopenharmony_ci /* 535062306a36Sopenharmony_ci * On passive channel we wait with blocked queues to see if 535162306a36Sopenharmony_ci * there is traffic on that channel. If no frame will be 535262306a36Sopenharmony_ci * received (what is very unlikely since scan detects AP on 535362306a36Sopenharmony_ci * that channel, but theoretically possible), mac80211 associate 535462306a36Sopenharmony_ci * procedure will time out and mac80211 will call us with NULL 535562306a36Sopenharmony_ci * bssid. We have to unblock queues on such condition. 535662306a36Sopenharmony_ci */ 535762306a36Sopenharmony_ci if (is_zero_ether_addr(bss_conf->bssid)) 535862306a36Sopenharmony_ci il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); 535962306a36Sopenharmony_ci 536062306a36Sopenharmony_ci /* 536162306a36Sopenharmony_ci * If there is currently a HW scan going on in the background, 536262306a36Sopenharmony_ci * then we need to cancel it, otherwise sometimes we are not 536362306a36Sopenharmony_ci * able to authenticate (FIXME: why ?) 536462306a36Sopenharmony_ci */ 536562306a36Sopenharmony_ci if (il_scan_cancel_timeout(il, 100)) { 536662306a36Sopenharmony_ci D_MAC80211("leave - scan abort failed\n"); 536762306a36Sopenharmony_ci mutex_unlock(&il->mutex); 536862306a36Sopenharmony_ci return; 536962306a36Sopenharmony_ci } 537062306a36Sopenharmony_ci 537162306a36Sopenharmony_ci /* mac80211 only sets assoc when in STATION mode */ 537262306a36Sopenharmony_ci memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); 537362306a36Sopenharmony_ci 537462306a36Sopenharmony_ci /* FIXME: currently needed in a few places */ 537562306a36Sopenharmony_ci memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); 537662306a36Sopenharmony_ci } 537762306a36Sopenharmony_ci 537862306a36Sopenharmony_ci /* 537962306a36Sopenharmony_ci * This needs to be after setting the BSSID in case 538062306a36Sopenharmony_ci * mac80211 decides to do both changes at once because 538162306a36Sopenharmony_ci * it will invoke post_associate. 538262306a36Sopenharmony_ci */ 538362306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON)) 538462306a36Sopenharmony_ci il_beacon_update(hw, vif); 538562306a36Sopenharmony_ci 538662306a36Sopenharmony_ci if (changes & BSS_CHANGED_ERP_PREAMBLE) { 538762306a36Sopenharmony_ci D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); 538862306a36Sopenharmony_ci if (bss_conf->use_short_preamble) 538962306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; 539062306a36Sopenharmony_ci else 539162306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; 539262306a36Sopenharmony_ci } 539362306a36Sopenharmony_ci 539462306a36Sopenharmony_ci if (changes & BSS_CHANGED_ERP_CTS_PROT) { 539562306a36Sopenharmony_ci D_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot); 539662306a36Sopenharmony_ci if (bss_conf->use_cts_prot && il->band != NL80211_BAND_5GHZ) 539762306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; 539862306a36Sopenharmony_ci else 539962306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; 540062306a36Sopenharmony_ci if (bss_conf->use_cts_prot) 540162306a36Sopenharmony_ci il->staging.flags |= RXON_FLG_SELF_CTS_EN; 540262306a36Sopenharmony_ci else 540362306a36Sopenharmony_ci il->staging.flags &= ~RXON_FLG_SELF_CTS_EN; 540462306a36Sopenharmony_ci } 540562306a36Sopenharmony_ci 540662306a36Sopenharmony_ci if (changes & BSS_CHANGED_BASIC_RATES) { 540762306a36Sopenharmony_ci /* XXX use this information 540862306a36Sopenharmony_ci * 540962306a36Sopenharmony_ci * To do that, remove code from il_set_rate() and put something 541062306a36Sopenharmony_ci * like this here: 541162306a36Sopenharmony_ci * 541262306a36Sopenharmony_ci if (A-band) 541362306a36Sopenharmony_ci il->staging.ofdm_basic_rates = 541462306a36Sopenharmony_ci bss_conf->basic_rates; 541562306a36Sopenharmony_ci else 541662306a36Sopenharmony_ci il->staging.ofdm_basic_rates = 541762306a36Sopenharmony_ci bss_conf->basic_rates >> 4; 541862306a36Sopenharmony_ci il->staging.cck_basic_rates = 541962306a36Sopenharmony_ci bss_conf->basic_rates & 0xF; 542062306a36Sopenharmony_ci */ 542162306a36Sopenharmony_ci } 542262306a36Sopenharmony_ci 542362306a36Sopenharmony_ci if (changes & BSS_CHANGED_HT) { 542462306a36Sopenharmony_ci il_ht_conf(il, vif); 542562306a36Sopenharmony_ci 542662306a36Sopenharmony_ci if (il->ops->set_rxon_chain) 542762306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 542862306a36Sopenharmony_ci } 542962306a36Sopenharmony_ci 543062306a36Sopenharmony_ci if (changes & BSS_CHANGED_ASSOC) { 543162306a36Sopenharmony_ci D_MAC80211("ASSOC %d\n", vif->cfg.assoc); 543262306a36Sopenharmony_ci if (vif->cfg.assoc) { 543362306a36Sopenharmony_ci il->timestamp = bss_conf->sync_tsf; 543462306a36Sopenharmony_ci 543562306a36Sopenharmony_ci if (!il_is_rfkill(il)) 543662306a36Sopenharmony_ci il->ops->post_associate(il); 543762306a36Sopenharmony_ci } else 543862306a36Sopenharmony_ci il_set_no_assoc(il, vif); 543962306a36Sopenharmony_ci } 544062306a36Sopenharmony_ci 544162306a36Sopenharmony_ci if (changes && il_is_associated(il) && vif->cfg.aid) { 544262306a36Sopenharmony_ci D_MAC80211("Changes (%#llx) while associated\n", changes); 544362306a36Sopenharmony_ci ret = il_send_rxon_assoc(il); 544462306a36Sopenharmony_ci if (!ret) { 544562306a36Sopenharmony_ci /* Sync active_rxon with latest change. */ 544662306a36Sopenharmony_ci memcpy((void *)&il->active, &il->staging, 544762306a36Sopenharmony_ci sizeof(struct il_rxon_cmd)); 544862306a36Sopenharmony_ci } 544962306a36Sopenharmony_ci } 545062306a36Sopenharmony_ci 545162306a36Sopenharmony_ci if (changes & BSS_CHANGED_BEACON_ENABLED) { 545262306a36Sopenharmony_ci if (vif->bss_conf.enable_beacon) { 545362306a36Sopenharmony_ci memcpy(il->staging.bssid_addr, bss_conf->bssid, 545462306a36Sopenharmony_ci ETH_ALEN); 545562306a36Sopenharmony_ci memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); 545662306a36Sopenharmony_ci il->ops->config_ap(il); 545762306a36Sopenharmony_ci } else 545862306a36Sopenharmony_ci il_set_no_assoc(il, vif); 545962306a36Sopenharmony_ci } 546062306a36Sopenharmony_ci 546162306a36Sopenharmony_ci if (changes & BSS_CHANGED_IBSS) { 546262306a36Sopenharmony_ci ret = il->ops->manage_ibss_station(il, vif, 546362306a36Sopenharmony_ci vif->cfg.ibss_joined); 546462306a36Sopenharmony_ci if (ret) 546562306a36Sopenharmony_ci IL_ERR("failed to %s IBSS station %pM\n", 546662306a36Sopenharmony_ci vif->cfg.ibss_joined ? "add" : "remove", 546762306a36Sopenharmony_ci bss_conf->bssid); 546862306a36Sopenharmony_ci } 546962306a36Sopenharmony_ci 547062306a36Sopenharmony_ci D_MAC80211("leave\n"); 547162306a36Sopenharmony_ci mutex_unlock(&il->mutex); 547262306a36Sopenharmony_ci} 547362306a36Sopenharmony_ciEXPORT_SYMBOL(il_mac_bss_info_changed); 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_ciirqreturn_t 547662306a36Sopenharmony_ciil_isr(int irq, void *data) 547762306a36Sopenharmony_ci{ 547862306a36Sopenharmony_ci struct il_priv *il = data; 547962306a36Sopenharmony_ci u32 inta, inta_mask; 548062306a36Sopenharmony_ci u32 inta_fh; 548162306a36Sopenharmony_ci unsigned long flags; 548262306a36Sopenharmony_ci if (!il) 548362306a36Sopenharmony_ci return IRQ_NONE; 548462306a36Sopenharmony_ci 548562306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 548662306a36Sopenharmony_ci 548762306a36Sopenharmony_ci /* Disable (but don't clear!) interrupts here to avoid 548862306a36Sopenharmony_ci * back-to-back ISRs and sporadic interrupts from our NIC. 548962306a36Sopenharmony_ci * If we have something to service, the tasklet will re-enable ints. 549062306a36Sopenharmony_ci * If we *don't* have something, we'll re-enable before leaving here. */ 549162306a36Sopenharmony_ci inta_mask = _il_rd(il, CSR_INT_MASK); /* just for debug */ 549262306a36Sopenharmony_ci _il_wr(il, CSR_INT_MASK, 0x00000000); 549362306a36Sopenharmony_ci 549462306a36Sopenharmony_ci /* Discover which interrupts are active/pending */ 549562306a36Sopenharmony_ci inta = _il_rd(il, CSR_INT); 549662306a36Sopenharmony_ci inta_fh = _il_rd(il, CSR_FH_INT_STATUS); 549762306a36Sopenharmony_ci 549862306a36Sopenharmony_ci /* Ignore interrupt if there's nothing in NIC to service. 549962306a36Sopenharmony_ci * This may be due to IRQ shared with another device, 550062306a36Sopenharmony_ci * or due to sporadic interrupts thrown from our NIC. */ 550162306a36Sopenharmony_ci if (!inta && !inta_fh) { 550262306a36Sopenharmony_ci D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n"); 550362306a36Sopenharmony_ci goto none; 550462306a36Sopenharmony_ci } 550562306a36Sopenharmony_ci 550662306a36Sopenharmony_ci if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) { 550762306a36Sopenharmony_ci /* Hardware disappeared. It might have already raised 550862306a36Sopenharmony_ci * an interrupt */ 550962306a36Sopenharmony_ci IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta); 551062306a36Sopenharmony_ci goto unplugged; 551162306a36Sopenharmony_ci } 551262306a36Sopenharmony_ci 551362306a36Sopenharmony_ci D_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask, 551462306a36Sopenharmony_ci inta_fh); 551562306a36Sopenharmony_ci 551662306a36Sopenharmony_ci inta &= ~CSR_INT_BIT_SCD; 551762306a36Sopenharmony_ci 551862306a36Sopenharmony_ci /* il_irq_tasklet() will service interrupts and re-enable them */ 551962306a36Sopenharmony_ci if (likely(inta || inta_fh)) 552062306a36Sopenharmony_ci tasklet_schedule(&il->irq_tasklet); 552162306a36Sopenharmony_ci 552262306a36Sopenharmony_ciunplugged: 552362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 552462306a36Sopenharmony_ci return IRQ_HANDLED; 552562306a36Sopenharmony_ci 552662306a36Sopenharmony_cinone: 552762306a36Sopenharmony_ci /* re-enable interrupts here since we don't have anything to service. */ 552862306a36Sopenharmony_ci /* only Re-enable if disabled by irq */ 552962306a36Sopenharmony_ci if (test_bit(S_INT_ENABLED, &il->status)) 553062306a36Sopenharmony_ci il_enable_interrupts(il); 553162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 553262306a36Sopenharmony_ci return IRQ_NONE; 553362306a36Sopenharmony_ci} 553462306a36Sopenharmony_ciEXPORT_SYMBOL(il_isr); 553562306a36Sopenharmony_ci 553662306a36Sopenharmony_ci/* 553762306a36Sopenharmony_ci * il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this 553862306a36Sopenharmony_ci * function. 553962306a36Sopenharmony_ci */ 554062306a36Sopenharmony_civoid 554162306a36Sopenharmony_ciil_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, 554262306a36Sopenharmony_ci __le16 fc, __le32 *tx_flags) 554362306a36Sopenharmony_ci{ 554462306a36Sopenharmony_ci if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { 554562306a36Sopenharmony_ci *tx_flags |= TX_CMD_FLG_RTS_MSK; 554662306a36Sopenharmony_ci *tx_flags &= ~TX_CMD_FLG_CTS_MSK; 554762306a36Sopenharmony_ci *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; 554862306a36Sopenharmony_ci 554962306a36Sopenharmony_ci if (!ieee80211_is_mgmt(fc)) 555062306a36Sopenharmony_ci return; 555162306a36Sopenharmony_ci 555262306a36Sopenharmony_ci switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { 555362306a36Sopenharmony_ci case cpu_to_le16(IEEE80211_STYPE_AUTH): 555462306a36Sopenharmony_ci case cpu_to_le16(IEEE80211_STYPE_DEAUTH): 555562306a36Sopenharmony_ci case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): 555662306a36Sopenharmony_ci case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): 555762306a36Sopenharmony_ci *tx_flags &= ~TX_CMD_FLG_RTS_MSK; 555862306a36Sopenharmony_ci *tx_flags |= TX_CMD_FLG_CTS_MSK; 555962306a36Sopenharmony_ci break; 556062306a36Sopenharmony_ci } 556162306a36Sopenharmony_ci } else if (info->control.rates[0]. 556262306a36Sopenharmony_ci flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { 556362306a36Sopenharmony_ci *tx_flags &= ~TX_CMD_FLG_RTS_MSK; 556462306a36Sopenharmony_ci *tx_flags |= TX_CMD_FLG_CTS_MSK; 556562306a36Sopenharmony_ci *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; 556662306a36Sopenharmony_ci } 556762306a36Sopenharmony_ci} 556862306a36Sopenharmony_ciEXPORT_SYMBOL(il_tx_cmd_protection); 5569