162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Portions of this file are derived from the ipw3945 project, as well 762306a36Sopenharmony_ci * as portions of the ieee80211 subsystem header files. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Contact Information: 1062306a36Sopenharmony_ci * Intel Linux Wireless <ilw@linux.intel.com> 1162306a36Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci *****************************************************************************/ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/init.h> 2062306a36Sopenharmony_ci#include <linux/pci.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci#include <linux/sched.h> 2562306a36Sopenharmony_ci#include <linux/skbuff.h> 2662306a36Sopenharmony_ci#include <linux/netdevice.h> 2762306a36Sopenharmony_ci#include <linux/firmware.h> 2862306a36Sopenharmony_ci#include <linux/etherdevice.h> 2962306a36Sopenharmony_ci#include <linux/if_arp.h> 3062306a36Sopenharmony_ci#include <linux/units.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <net/mac80211.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <asm/div64.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define DRV_NAME "iwl4965" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "common.h" 3962306a36Sopenharmony_ci#include "4965.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/****************************************************************************** 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * module boiler plate 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci ******************************************************************************/ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * module name, copyright, version, etc. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci#define DRV_DESCRIPTION "Intel(R) Wireless WiFi 4965 driver for Linux" 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 5362306a36Sopenharmony_ci#define VD "d" 5462306a36Sopenharmony_ci#else 5562306a36Sopenharmony_ci#define VD 5662306a36Sopenharmony_ci#endif 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define DRV_VERSION IWLWIFI_VERSION VD 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION); 6162306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 6262306a36Sopenharmony_ciMODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); 6362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 6462306a36Sopenharmony_ciMODULE_ALIAS("iwl4965"); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_civoid 6762306a36Sopenharmony_ciil4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { 7062306a36Sopenharmony_ci IL_ERR("Tx flush command to flush out all frames\n"); 7162306a36Sopenharmony_ci if (!test_bit(S_EXIT_PENDING, &il->status)) 7262306a36Sopenharmony_ci queue_work(il->workqueue, &il->tx_flush); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * EEPROM 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistruct il_mod_params il4965_mod_params = { 8062306a36Sopenharmony_ci .restart_fw = 1, 8162306a36Sopenharmony_ci /* the rest are 0 by default */ 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_civoid 8562306a36Sopenharmony_ciil4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned long flags; 8862306a36Sopenharmony_ci int i; 8962306a36Sopenharmony_ci spin_lock_irqsave(&rxq->lock, flags); 9062306a36Sopenharmony_ci INIT_LIST_HEAD(&rxq->rx_free); 9162306a36Sopenharmony_ci INIT_LIST_HEAD(&rxq->rx_used); 9262306a36Sopenharmony_ci /* Fill the rx_used queue with _all_ of the Rx buffers */ 9362306a36Sopenharmony_ci for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { 9462306a36Sopenharmony_ci /* In the reset function, these buffers may have been allocated 9562306a36Sopenharmony_ci * to an SKB, so we need to unmap and free potential storage */ 9662306a36Sopenharmony_ci if (rxq->pool[i].page != NULL) { 9762306a36Sopenharmony_ci dma_unmap_page(&il->pci_dev->dev, 9862306a36Sopenharmony_ci rxq->pool[i].page_dma, 9962306a36Sopenharmony_ci PAGE_SIZE << il->hw_params.rx_page_order, 10062306a36Sopenharmony_ci DMA_FROM_DEVICE); 10162306a36Sopenharmony_ci __il_free_pages(il, rxq->pool[i].page); 10262306a36Sopenharmony_ci rxq->pool[i].page = NULL; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci list_add_tail(&rxq->pool[i].list, &rxq->rx_used); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci for (i = 0; i < RX_QUEUE_SIZE; i++) 10862306a36Sopenharmony_ci rxq->queue[i] = NULL; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Set us so that we have processed and used all buffers, but have 11162306a36Sopenharmony_ci * not restocked the Rx queue with fresh buffers */ 11262306a36Sopenharmony_ci rxq->read = rxq->write = 0; 11362306a36Sopenharmony_ci rxq->write_actual = 0; 11462306a36Sopenharmony_ci rxq->free_count = 0; 11562306a36Sopenharmony_ci spin_unlock_irqrestore(&rxq->lock, flags); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint 11962306a36Sopenharmony_ciil4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u32 rb_size; 12262306a36Sopenharmony_ci const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ 12362306a36Sopenharmony_ci u32 rb_timeout = 0; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (il->cfg->mod_params->amsdu_size_8K) 12662306a36Sopenharmony_ci rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; 12762306a36Sopenharmony_ci else 12862306a36Sopenharmony_ci rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Stop Rx DMA */ 13162306a36Sopenharmony_ci il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Reset driver's Rx queue write idx */ 13462306a36Sopenharmony_ci il_wr(il, FH49_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Tell device where to find RBD circular buffer in DRAM */ 13762306a36Sopenharmony_ci il_wr(il, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, (u32) (rxq->bd_dma >> 8)); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Tell device where in DRAM to update its Rx status */ 14062306a36Sopenharmony_ci il_wr(il, FH49_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Enable Rx DMA 14362306a36Sopenharmony_ci * Direct rx interrupts to hosts 14462306a36Sopenharmony_ci * Rx buffer size 4 or 8k 14562306a36Sopenharmony_ci * RB timeout 0x10 14662306a36Sopenharmony_ci * 256 RBDs 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 14962306a36Sopenharmony_ci FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | 15062306a36Sopenharmony_ci FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | 15162306a36Sopenharmony_ci FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | 15262306a36Sopenharmony_ci rb_size | 15362306a36Sopenharmony_ci (rb_timeout << FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | 15462306a36Sopenharmony_ci (rfdnlog << FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Set interrupt coalescing timer to default (2048 usecs) */ 15762306a36Sopenharmony_ci il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_TIMEOUT_DEF); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void 16362306a36Sopenharmony_ciil4965_set_pwr_vmain(struct il_priv *il) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * (for documentation purposes) 16762306a36Sopenharmony_ci * to set power to V_AUX, do: 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (pci_pme_capable(il->pci_dev, PCI_D3cold)) 17062306a36Sopenharmony_ci il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, 17162306a36Sopenharmony_ci APMG_PS_CTRL_VAL_PWR_SRC_VAUX, 17262306a36Sopenharmony_ci ~APMG_PS_CTRL_MSK_PWR_SRC); 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, 17662306a36Sopenharmony_ci APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, 17762306a36Sopenharmony_ci ~APMG_PS_CTRL_MSK_PWR_SRC); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciint 18162306a36Sopenharmony_ciil4965_hw_nic_init(struct il_priv *il) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci unsigned long flags; 18462306a36Sopenharmony_ci struct il_rx_queue *rxq = &il->rxq; 18562306a36Sopenharmony_ci int ret; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 18862306a36Sopenharmony_ci il_apm_init(il); 18962306a36Sopenharmony_ci /* Set interrupt coalescing calibration timer to default (512 usecs) */ 19062306a36Sopenharmony_ci il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_CALIB_TIMEOUT_DEF); 19162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci il4965_set_pwr_vmain(il); 19462306a36Sopenharmony_ci il4965_nic_config(il); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Allocate the RX queue, or reset if it is already allocated */ 19762306a36Sopenharmony_ci if (!rxq->bd) { 19862306a36Sopenharmony_ci ret = il_rx_queue_alloc(il); 19962306a36Sopenharmony_ci if (ret) { 20062306a36Sopenharmony_ci IL_ERR("Unable to initialize Rx queue\n"); 20162306a36Sopenharmony_ci return -ENOMEM; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci } else 20462306a36Sopenharmony_ci il4965_rx_queue_reset(il, rxq); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci il4965_rx_replenish(il); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci il4965_rx_init(il, rxq); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci rxq->need_update = 1; 21362306a36Sopenharmony_ci il_rx_queue_update_write_ptr(il, rxq); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Allocate or reset and init all Tx and Command queues */ 21862306a36Sopenharmony_ci if (!il->txq) { 21962306a36Sopenharmony_ci ret = il4965_txq_ctx_alloc(il); 22062306a36Sopenharmony_ci if (ret) 22162306a36Sopenharmony_ci return ret; 22262306a36Sopenharmony_ci } else 22362306a36Sopenharmony_ci il4965_txq_ctx_reset(il); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci set_bit(S_INIT, &il->status); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 23162306a36Sopenharmony_ci * il4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_cistatic inline __le32 23462306a36Sopenharmony_ciil4965_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci return cpu_to_le32((u32) (dma_addr >> 8)); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* 24062306a36Sopenharmony_ci * il4965_rx_queue_restock - refill RX queue from pre-allocated pool 24162306a36Sopenharmony_ci * 24262306a36Sopenharmony_ci * If there are slots in the RX queue that need to be restocked, 24362306a36Sopenharmony_ci * and we have free pre-allocated buffers, fill the ranks as much 24462306a36Sopenharmony_ci * as we can, pulling from rx_free. 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * This moves the 'write' idx forward to catch up with 'processed', and 24762306a36Sopenharmony_ci * also updates the memory address in the firmware to reference the new 24862306a36Sopenharmony_ci * target buffer. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_civoid 25162306a36Sopenharmony_ciil4965_rx_queue_restock(struct il_priv *il) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct il_rx_queue *rxq = &il->rxq; 25462306a36Sopenharmony_ci struct list_head *element; 25562306a36Sopenharmony_ci struct il_rx_buf *rxb; 25662306a36Sopenharmony_ci unsigned long flags; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci spin_lock_irqsave(&rxq->lock, flags); 25962306a36Sopenharmony_ci while (il_rx_queue_space(rxq) > 0 && rxq->free_count) { 26062306a36Sopenharmony_ci /* The overwritten rxb must be a used one */ 26162306a36Sopenharmony_ci rxb = rxq->queue[rxq->write]; 26262306a36Sopenharmony_ci BUG_ON(rxb && rxb->page); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Get next free Rx buffer, remove from free list */ 26562306a36Sopenharmony_ci element = rxq->rx_free.next; 26662306a36Sopenharmony_ci rxb = list_entry(element, struct il_rx_buf, list); 26762306a36Sopenharmony_ci list_del(element); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Point to Rx buffer via next RBD in circular buffer */ 27062306a36Sopenharmony_ci rxq->bd[rxq->write] = 27162306a36Sopenharmony_ci il4965_dma_addr2rbd_ptr(il, rxb->page_dma); 27262306a36Sopenharmony_ci rxq->queue[rxq->write] = rxb; 27362306a36Sopenharmony_ci rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; 27462306a36Sopenharmony_ci rxq->free_count--; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&rxq->lock, flags); 27762306a36Sopenharmony_ci /* If the pre-allocated buffer pool is dropping low, schedule to 27862306a36Sopenharmony_ci * refill it */ 27962306a36Sopenharmony_ci if (rxq->free_count <= RX_LOW_WATERMARK) 28062306a36Sopenharmony_ci queue_work(il->workqueue, &il->rx_replenish); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* If we've added more space for the firmware to place data, tell it. 28362306a36Sopenharmony_ci * Increment device's write pointer in multiples of 8. */ 28462306a36Sopenharmony_ci if (rxq->write_actual != (rxq->write & ~0x7)) { 28562306a36Sopenharmony_ci spin_lock_irqsave(&rxq->lock, flags); 28662306a36Sopenharmony_ci rxq->need_update = 1; 28762306a36Sopenharmony_ci spin_unlock_irqrestore(&rxq->lock, flags); 28862306a36Sopenharmony_ci il_rx_queue_update_write_ptr(il, rxq); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* 29362306a36Sopenharmony_ci * il4965_rx_replenish - Move all used packet from rx_used to rx_free 29462306a36Sopenharmony_ci * 29562306a36Sopenharmony_ci * When moving to rx_free an SKB is allocated for the slot. 29662306a36Sopenharmony_ci * 29762306a36Sopenharmony_ci * Also restock the Rx queue via il_rx_queue_restock. 29862306a36Sopenharmony_ci * This is called as a scheduled work item (except for during initialization) 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic void 30162306a36Sopenharmony_ciil4965_rx_allocate(struct il_priv *il, gfp_t priority) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct il_rx_queue *rxq = &il->rxq; 30462306a36Sopenharmony_ci struct list_head *element; 30562306a36Sopenharmony_ci struct il_rx_buf *rxb; 30662306a36Sopenharmony_ci struct page *page; 30762306a36Sopenharmony_ci dma_addr_t page_dma; 30862306a36Sopenharmony_ci unsigned long flags; 30962306a36Sopenharmony_ci gfp_t gfp_mask = priority; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci while (1) { 31262306a36Sopenharmony_ci spin_lock_irqsave(&rxq->lock, flags); 31362306a36Sopenharmony_ci if (list_empty(&rxq->rx_used)) { 31462306a36Sopenharmony_ci spin_unlock_irqrestore(&rxq->lock, flags); 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci spin_unlock_irqrestore(&rxq->lock, flags); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (rxq->free_count > RX_LOW_WATERMARK) 32062306a36Sopenharmony_ci gfp_mask |= __GFP_NOWARN; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (il->hw_params.rx_page_order > 0) 32362306a36Sopenharmony_ci gfp_mask |= __GFP_COMP; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Alloc a new receive buffer */ 32662306a36Sopenharmony_ci page = alloc_pages(gfp_mask, il->hw_params.rx_page_order); 32762306a36Sopenharmony_ci if (!page) { 32862306a36Sopenharmony_ci if (net_ratelimit()) 32962306a36Sopenharmony_ci D_INFO("alloc_pages failed, " "order: %d\n", 33062306a36Sopenharmony_ci il->hw_params.rx_page_order); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (rxq->free_count <= RX_LOW_WATERMARK && 33362306a36Sopenharmony_ci net_ratelimit()) 33462306a36Sopenharmony_ci IL_ERR("Failed to alloc_pages with %s. " 33562306a36Sopenharmony_ci "Only %u free buffers remaining.\n", 33662306a36Sopenharmony_ci priority == 33762306a36Sopenharmony_ci GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", 33862306a36Sopenharmony_ci rxq->free_count); 33962306a36Sopenharmony_ci /* We don't reschedule replenish work here -- we will 34062306a36Sopenharmony_ci * call the restock method and if it still needs 34162306a36Sopenharmony_ci * more buffers it will schedule replenish */ 34262306a36Sopenharmony_ci return; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Get physical address of the RB */ 34662306a36Sopenharmony_ci page_dma = dma_map_page(&il->pci_dev->dev, page, 0, 34762306a36Sopenharmony_ci PAGE_SIZE << il->hw_params.rx_page_order, 34862306a36Sopenharmony_ci DMA_FROM_DEVICE); 34962306a36Sopenharmony_ci if (unlikely(dma_mapping_error(&il->pci_dev->dev, page_dma))) { 35062306a36Sopenharmony_ci __free_pages(page, il->hw_params.rx_page_order); 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci spin_lock_irqsave(&rxq->lock, flags); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (list_empty(&rxq->rx_used)) { 35762306a36Sopenharmony_ci spin_unlock_irqrestore(&rxq->lock, flags); 35862306a36Sopenharmony_ci dma_unmap_page(&il->pci_dev->dev, page_dma, 35962306a36Sopenharmony_ci PAGE_SIZE << il->hw_params.rx_page_order, 36062306a36Sopenharmony_ci DMA_FROM_DEVICE); 36162306a36Sopenharmony_ci __free_pages(page, il->hw_params.rx_page_order); 36262306a36Sopenharmony_ci return; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci element = rxq->rx_used.next; 36662306a36Sopenharmony_ci rxb = list_entry(element, struct il_rx_buf, list); 36762306a36Sopenharmony_ci list_del(element); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci BUG_ON(rxb->page); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci rxb->page = page; 37262306a36Sopenharmony_ci rxb->page_dma = page_dma; 37362306a36Sopenharmony_ci list_add_tail(&rxb->list, &rxq->rx_free); 37462306a36Sopenharmony_ci rxq->free_count++; 37562306a36Sopenharmony_ci il->alloc_rxb_page++; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci spin_unlock_irqrestore(&rxq->lock, flags); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_civoid 38262306a36Sopenharmony_ciil4965_rx_replenish(struct il_priv *il) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci unsigned long flags; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci il4965_rx_allocate(il, GFP_KERNEL); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 38962306a36Sopenharmony_ci il4965_rx_queue_restock(il); 39062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_civoid 39462306a36Sopenharmony_ciil4965_rx_replenish_now(struct il_priv *il) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci il4965_rx_allocate(il, GFP_ATOMIC); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci il4965_rx_queue_restock(il); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* Assumes that the skb field of the buffers in 'pool' is kept accurate. 40262306a36Sopenharmony_ci * If an SKB has been detached, the POOL needs to have its SKB set to NULL 40362306a36Sopenharmony_ci * This free routine walks the list of POOL entries and if SKB is set to 40462306a36Sopenharmony_ci * non NULL it is unmapped and freed 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_civoid 40762306a36Sopenharmony_ciil4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci int i; 41062306a36Sopenharmony_ci for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { 41162306a36Sopenharmony_ci if (rxq->pool[i].page != NULL) { 41262306a36Sopenharmony_ci dma_unmap_page(&il->pci_dev->dev, 41362306a36Sopenharmony_ci rxq->pool[i].page_dma, 41462306a36Sopenharmony_ci PAGE_SIZE << il->hw_params.rx_page_order, 41562306a36Sopenharmony_ci DMA_FROM_DEVICE); 41662306a36Sopenharmony_ci __il_free_pages(il, rxq->pool[i].page); 41762306a36Sopenharmony_ci rxq->pool[i].page = NULL; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, 42262306a36Sopenharmony_ci rxq->bd_dma); 42362306a36Sopenharmony_ci dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status), 42462306a36Sopenharmony_ci rxq->rb_stts, rxq->rb_stts_dma); 42562306a36Sopenharmony_ci rxq->bd = NULL; 42662306a36Sopenharmony_ci rxq->rb_stts = NULL; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ciint 43062306a36Sopenharmony_ciil4965_rxq_stop(struct il_priv *il) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci int ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci _il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0); 43562306a36Sopenharmony_ci ret = _il_poll_bit(il, FH49_MEM_RSSR_RX_STATUS_REG, 43662306a36Sopenharmony_ci FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 43762306a36Sopenharmony_ci FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 43862306a36Sopenharmony_ci 1000); 43962306a36Sopenharmony_ci if (ret < 0) 44062306a36Sopenharmony_ci IL_ERR("Can't stop Rx DMA.\n"); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ciint 44662306a36Sopenharmony_ciil4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci int idx = 0; 44962306a36Sopenharmony_ci int band_offset = 0; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* HT rate format: mac80211 wants an MCS number, which is just LSB */ 45262306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) { 45362306a36Sopenharmony_ci idx = (rate_n_flags & 0xff); 45462306a36Sopenharmony_ci return idx; 45562306a36Sopenharmony_ci /* Legacy rate format, search for match in table */ 45662306a36Sopenharmony_ci } else { 45762306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 45862306a36Sopenharmony_ci band_offset = IL_FIRST_OFDM_RATE; 45962306a36Sopenharmony_ci for (idx = band_offset; idx < RATE_COUNT_LEGACY; idx++) 46062306a36Sopenharmony_ci if (il_rates[idx].plcp == (rate_n_flags & 0xFF)) 46162306a36Sopenharmony_ci return idx - band_offset; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return -1; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int 46862306a36Sopenharmony_ciil4965_calc_rssi(struct il_priv *il, struct il_rx_phy_res *rx_resp) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci /* data from PHY/DSP regarding signal strength, etc., 47162306a36Sopenharmony_ci * contents are always there, not configurable by host. */ 47262306a36Sopenharmony_ci struct il4965_rx_non_cfg_phy *ncphy = 47362306a36Sopenharmony_ci (struct il4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf; 47462306a36Sopenharmony_ci u32 agc = 47562306a36Sopenharmony_ci (le16_to_cpu(ncphy->agc_info) & IL49_AGC_DB_MASK) >> 47662306a36Sopenharmony_ci IL49_AGC_DB_POS; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci u32 valid_antennae = 47962306a36Sopenharmony_ci (le16_to_cpu(rx_resp->phy_flags) & IL49_RX_PHY_FLAGS_ANTENNAE_MASK) 48062306a36Sopenharmony_ci >> IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET; 48162306a36Sopenharmony_ci u8 max_rssi = 0; 48262306a36Sopenharmony_ci u32 i; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* Find max rssi among 3 possible receivers. 48562306a36Sopenharmony_ci * These values are measured by the digital signal processor (DSP). 48662306a36Sopenharmony_ci * They should stay fairly constant even as the signal strength varies, 48762306a36Sopenharmony_ci * if the radio's automatic gain control (AGC) is working right. 48862306a36Sopenharmony_ci * AGC value (see below) will provide the "interesting" info. */ 48962306a36Sopenharmony_ci for (i = 0; i < 3; i++) 49062306a36Sopenharmony_ci if (valid_antennae & (1 << i)) 49162306a36Sopenharmony_ci max_rssi = max(ncphy->rssi_info[i << 1], max_rssi); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci D_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n", 49462306a36Sopenharmony_ci ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4], 49562306a36Sopenharmony_ci max_rssi, agc); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* dBm = max_rssi dB - agc dB - constant. 49862306a36Sopenharmony_ci * Higher AGC (higher radio gain) means lower signal. */ 49962306a36Sopenharmony_ci return max_rssi - agc - IL4965_RSSI_OFFSET; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic u32 50362306a36Sopenharmony_ciil4965_translate_rx_status(struct il_priv *il, u32 decrypt_in) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci u32 decrypt_out = 0; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == 50862306a36Sopenharmony_ci RX_RES_STATUS_STATION_FOUND) 50962306a36Sopenharmony_ci decrypt_out |= 51062306a36Sopenharmony_ci (RX_RES_STATUS_STATION_FOUND | 51162306a36Sopenharmony_ci RX_RES_STATUS_NO_STATION_INFO_MISMATCH); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* packet was not encrypted */ 51662306a36Sopenharmony_ci if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == 51762306a36Sopenharmony_ci RX_RES_STATUS_SEC_TYPE_NONE) 51862306a36Sopenharmony_ci return decrypt_out; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* packet was encrypted with unknown alg */ 52162306a36Sopenharmony_ci if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == 52262306a36Sopenharmony_ci RX_RES_STATUS_SEC_TYPE_ERR) 52362306a36Sopenharmony_ci return decrypt_out; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* decryption was not done in HW */ 52662306a36Sopenharmony_ci if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != 52762306a36Sopenharmony_ci RX_MPDU_RES_STATUS_DEC_DONE_MSK) 52862306a36Sopenharmony_ci return decrypt_out; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci case RX_RES_STATUS_SEC_TYPE_CCMP: 53362306a36Sopenharmony_ci /* alg is CCM: check MIC only */ 53462306a36Sopenharmony_ci if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) 53562306a36Sopenharmony_ci /* Bad MIC */ 53662306a36Sopenharmony_ci decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; 53762306a36Sopenharmony_ci else 53862306a36Sopenharmony_ci decrypt_out |= RX_RES_STATUS_DECRYPT_OK; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci case RX_RES_STATUS_SEC_TYPE_TKIP: 54362306a36Sopenharmony_ci if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { 54462306a36Sopenharmony_ci /* Bad TTAK */ 54562306a36Sopenharmony_ci decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci fallthrough; /* if TTAK OK */ 54962306a36Sopenharmony_ci default: 55062306a36Sopenharmony_ci if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) 55162306a36Sopenharmony_ci decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; 55262306a36Sopenharmony_ci else 55362306a36Sopenharmony_ci decrypt_out |= RX_RES_STATUS_DECRYPT_OK; 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci D_RX("decrypt_in:0x%x decrypt_out = 0x%x\n", decrypt_in, decrypt_out); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return decrypt_out; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci#define SMALL_PACKET_SIZE 256 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic void 56562306a36Sopenharmony_ciil4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, 56662306a36Sopenharmony_ci u32 len, u32 ampdu_status, struct il_rx_buf *rxb, 56762306a36Sopenharmony_ci struct ieee80211_rx_status *stats) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct sk_buff *skb; 57062306a36Sopenharmony_ci __le16 fc = hdr->frame_control; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* We only process data packets if the interface is open */ 57362306a36Sopenharmony_ci if (unlikely(!il->is_open)) { 57462306a36Sopenharmony_ci D_DROP("Dropping packet while interface is not open.\n"); 57562306a36Sopenharmony_ci return; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { 57962306a36Sopenharmony_ci il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); 58062306a36Sopenharmony_ci D_INFO("Woke queues - frame received on passive channel\n"); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* In case of HW accelerated crypto and bad decryption, drop */ 58462306a36Sopenharmony_ci if (!il->cfg->mod_params->sw_crypto && 58562306a36Sopenharmony_ci il_set_decrypted_flag(il, hdr, ampdu_status, stats)) 58662306a36Sopenharmony_ci return; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci skb = dev_alloc_skb(SMALL_PACKET_SIZE); 58962306a36Sopenharmony_ci if (!skb) { 59062306a36Sopenharmony_ci IL_ERR("dev_alloc_skb failed\n"); 59162306a36Sopenharmony_ci return; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (len <= SMALL_PACKET_SIZE) { 59562306a36Sopenharmony_ci skb_put_data(skb, hdr, len); 59662306a36Sopenharmony_ci } else { 59762306a36Sopenharmony_ci skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), 59862306a36Sopenharmony_ci len, PAGE_SIZE << il->hw_params.rx_page_order); 59962306a36Sopenharmony_ci il->alloc_rxb_page--; 60062306a36Sopenharmony_ci rxb->page = NULL; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci il_update_stats(il, false, fc, len); 60462306a36Sopenharmony_ci memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ieee80211_rx(il->hw, skb); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci/* Called for N_RX (legacy ABG frames), or 61062306a36Sopenharmony_ci * N_RX_MPDU (HT high-throughput N frames). */ 61162306a36Sopenharmony_cistatic void 61262306a36Sopenharmony_ciil4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct ieee80211_hdr *header; 61562306a36Sopenharmony_ci struct ieee80211_rx_status rx_status = {}; 61662306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 61762306a36Sopenharmony_ci struct il_rx_phy_res *phy_res; 61862306a36Sopenharmony_ci __le32 rx_pkt_status; 61962306a36Sopenharmony_ci struct il_rx_mpdu_res_start *amsdu; 62062306a36Sopenharmony_ci u32 len; 62162306a36Sopenharmony_ci u32 ampdu_status; 62262306a36Sopenharmony_ci u32 rate_n_flags; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /** 62562306a36Sopenharmony_ci * N_RX and N_RX_MPDU are handled differently. 62662306a36Sopenharmony_ci * N_RX: physical layer info is in this buffer 62762306a36Sopenharmony_ci * N_RX_MPDU: physical layer info was sent in separate 62862306a36Sopenharmony_ci * command and cached in il->last_phy_res 62962306a36Sopenharmony_ci * 63062306a36Sopenharmony_ci * Here we set up local variables depending on which command is 63162306a36Sopenharmony_ci * received. 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci if (pkt->hdr.cmd == N_RX) { 63462306a36Sopenharmony_ci phy_res = (struct il_rx_phy_res *)pkt->u.raw; 63562306a36Sopenharmony_ci header = 63662306a36Sopenharmony_ci (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) + 63762306a36Sopenharmony_ci phy_res->cfg_phy_cnt); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci len = le16_to_cpu(phy_res->byte_count); 64062306a36Sopenharmony_ci rx_pkt_status = 64162306a36Sopenharmony_ci *(__le32 *) (pkt->u.raw + sizeof(*phy_res) + 64262306a36Sopenharmony_ci phy_res->cfg_phy_cnt + len); 64362306a36Sopenharmony_ci ampdu_status = le32_to_cpu(rx_pkt_status); 64462306a36Sopenharmony_ci } else { 64562306a36Sopenharmony_ci if (!il->_4965.last_phy_res_valid) { 64662306a36Sopenharmony_ci IL_ERR("MPDU frame without cached PHY data\n"); 64762306a36Sopenharmony_ci return; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci phy_res = &il->_4965.last_phy_res; 65062306a36Sopenharmony_ci amsdu = (struct il_rx_mpdu_res_start *)pkt->u.raw; 65162306a36Sopenharmony_ci header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu)); 65262306a36Sopenharmony_ci len = le16_to_cpu(amsdu->byte_count); 65362306a36Sopenharmony_ci rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*amsdu) + len); 65462306a36Sopenharmony_ci ampdu_status = 65562306a36Sopenharmony_ci il4965_translate_rx_status(il, le32_to_cpu(rx_pkt_status)); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if ((unlikely(phy_res->cfg_phy_cnt > 20))) { 65962306a36Sopenharmony_ci D_DROP("dsp size out of range [0,20]: %d\n", 66062306a36Sopenharmony_ci phy_res->cfg_phy_cnt); 66162306a36Sopenharmony_ci return; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || 66562306a36Sopenharmony_ci !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { 66662306a36Sopenharmony_ci D_RX("Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status)); 66762306a36Sopenharmony_ci return; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* This will be used in several places later */ 67162306a36Sopenharmony_ci rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* rx_status carries information about the packet to mac80211 */ 67462306a36Sopenharmony_ci rx_status.mactime = le64_to_cpu(phy_res->timestamp); 67562306a36Sopenharmony_ci rx_status.band = 67662306a36Sopenharmony_ci (phy_res-> 67762306a36Sopenharmony_ci phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? NL80211_BAND_2GHZ : 67862306a36Sopenharmony_ci NL80211_BAND_5GHZ; 67962306a36Sopenharmony_ci rx_status.freq = 68062306a36Sopenharmony_ci ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), 68162306a36Sopenharmony_ci rx_status.band); 68262306a36Sopenharmony_ci rx_status.rate_idx = 68362306a36Sopenharmony_ci il4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); 68462306a36Sopenharmony_ci rx_status.flag = 0; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* TSF isn't reliable. In order to allow smooth user experience, 68762306a36Sopenharmony_ci * this W/A doesn't propagate it to the mac80211 */ 68862306a36Sopenharmony_ci /*rx_status.flag |= RX_FLAG_MACTIME_START; */ 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* Find max signal strength (dBm) among 3 antenna/receiver chains */ 69362306a36Sopenharmony_ci rx_status.signal = il4965_calc_rssi(il, phy_res); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci D_STATS("Rssi %d, TSF %llu\n", rx_status.signal, 69662306a36Sopenharmony_ci (unsigned long long)rx_status.mactime); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* 69962306a36Sopenharmony_ci * "antenna number" 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * It seems that the antenna field in the phy flags value 70262306a36Sopenharmony_ci * is actually a bit field. This is undefined by radiotap, 70362306a36Sopenharmony_ci * it wants an actual antenna number but I always get "7" 70462306a36Sopenharmony_ci * for most legacy frames I receive indicating that the 70562306a36Sopenharmony_ci * same frame was received on all three RX chains. 70662306a36Sopenharmony_ci * 70762306a36Sopenharmony_ci * I think this field should be removed in favor of a 70862306a36Sopenharmony_ci * new 802.11n radiotap field "RX chains" that is defined 70962306a36Sopenharmony_ci * as a bitmask. 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_ci rx_status.antenna = 71262306a36Sopenharmony_ci (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 71362306a36Sopenharmony_ci RX_RES_PHY_FLAGS_ANTENNA_POS; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* set the preamble flag if appropriate */ 71662306a36Sopenharmony_ci if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) 71762306a36Sopenharmony_ci rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* Set up the HT phy flags */ 72062306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) 72162306a36Sopenharmony_ci rx_status.encoding = RX_ENC_HT; 72262306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT40_MSK) 72362306a36Sopenharmony_ci rx_status.bw = RATE_INFO_BW_40; 72462306a36Sopenharmony_ci else 72562306a36Sopenharmony_ci rx_status.bw = RATE_INFO_BW_20; 72662306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_SGI_MSK) 72762306a36Sopenharmony_ci rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { 73062306a36Sopenharmony_ci /* We know which subframes of an A-MPDU belong 73162306a36Sopenharmony_ci * together since we get a single PHY response 73262306a36Sopenharmony_ci * from the firmware for all of them. 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci rx_status.flag |= RX_FLAG_AMPDU_DETAILS; 73662306a36Sopenharmony_ci rx_status.ampdu_reference = il->_4965.ampdu_ref; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb, 74062306a36Sopenharmony_ci &rx_status); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci/* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY). 74462306a36Sopenharmony_ci * This will be used later in il_hdl_rx() for N_RX_MPDU. */ 74562306a36Sopenharmony_cistatic void 74662306a36Sopenharmony_ciil4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 74962306a36Sopenharmony_ci il->_4965.last_phy_res_valid = true; 75062306a36Sopenharmony_ci il->_4965.ampdu_ref++; 75162306a36Sopenharmony_ci memcpy(&il->_4965.last_phy_res, pkt->u.raw, 75262306a36Sopenharmony_ci sizeof(struct il_rx_phy_res)); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int 75662306a36Sopenharmony_ciil4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif, 75762306a36Sopenharmony_ci enum nl80211_band band, u8 is_active, 75862306a36Sopenharmony_ci u8 n_probes, struct il_scan_channel *scan_ch) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct ieee80211_channel *chan; 76162306a36Sopenharmony_ci const struct ieee80211_supported_band *sband; 76262306a36Sopenharmony_ci const struct il_channel_info *ch_info; 76362306a36Sopenharmony_ci u16 passive_dwell = 0; 76462306a36Sopenharmony_ci u16 active_dwell = 0; 76562306a36Sopenharmony_ci int added, i; 76662306a36Sopenharmony_ci u16 channel; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci sband = il_get_hw_mode(il, band); 76962306a36Sopenharmony_ci if (!sband) 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci active_dwell = il_get_active_dwell_time(il, band, n_probes); 77362306a36Sopenharmony_ci passive_dwell = il_get_passive_dwell_time(il, band, vif); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (passive_dwell <= active_dwell) 77662306a36Sopenharmony_ci passive_dwell = active_dwell + 1; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci for (i = 0, added = 0; i < il->scan_request->n_channels; i++) { 77962306a36Sopenharmony_ci chan = il->scan_request->channels[i]; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (chan->band != band) 78262306a36Sopenharmony_ci continue; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci channel = chan->hw_value; 78562306a36Sopenharmony_ci scan_ch->channel = cpu_to_le16(channel); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci ch_info = il_get_channel_info(il, band, channel); 78862306a36Sopenharmony_ci if (!il_is_channel_valid(ch_info)) { 78962306a36Sopenharmony_ci D_SCAN("Channel %d is INVALID for this band.\n", 79062306a36Sopenharmony_ci channel); 79162306a36Sopenharmony_ci continue; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (!is_active || il_is_channel_passive(ch_info) || 79562306a36Sopenharmony_ci (chan->flags & IEEE80211_CHAN_NO_IR)) 79662306a36Sopenharmony_ci scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; 79762306a36Sopenharmony_ci else 79862306a36Sopenharmony_ci scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (n_probes) 80162306a36Sopenharmony_ci scan_ch->type |= IL_SCAN_PROBE_MASK(n_probes); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci scan_ch->active_dwell = cpu_to_le16(active_dwell); 80462306a36Sopenharmony_ci scan_ch->passive_dwell = cpu_to_le16(passive_dwell); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Set txpower levels to defaults */ 80762306a36Sopenharmony_ci scan_ch->dsp_atten = 110; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* NOTE: if we were doing 6Mb OFDM for scans we'd use 81062306a36Sopenharmony_ci * power level: 81162306a36Sopenharmony_ci * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 81462306a36Sopenharmony_ci scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; 81562306a36Sopenharmony_ci else 81662306a36Sopenharmony_ci scan_ch->tx_gain = ((1 << 5) | (5 << 3)); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci D_SCAN("Scanning ch=%d prob=0x%X [%s %d]\n", channel, 81962306a36Sopenharmony_ci le32_to_cpu(scan_ch->type), 82062306a36Sopenharmony_ci (scan_ch-> 82162306a36Sopenharmony_ci type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE", 82262306a36Sopenharmony_ci (scan_ch-> 82362306a36Sopenharmony_ci type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell : 82462306a36Sopenharmony_ci passive_dwell); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci scan_ch++; 82762306a36Sopenharmony_ci added++; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci D_SCAN("total channels to scan %d\n", added); 83162306a36Sopenharmony_ci return added; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic void 83562306a36Sopenharmony_ciil4965_toggle_tx_ant(struct il_priv *il, u8 *ant, u8 valid) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci int i; 83862306a36Sopenharmony_ci u8 ind = *ant; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for (i = 0; i < RATE_ANT_NUM - 1; i++) { 84162306a36Sopenharmony_ci ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; 84262306a36Sopenharmony_ci if (valid & BIT(ind)) { 84362306a36Sopenharmony_ci *ant = ind; 84462306a36Sopenharmony_ci return; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ciint 85062306a36Sopenharmony_ciil4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct il_host_cmd cmd = { 85362306a36Sopenharmony_ci .id = C_SCAN, 85462306a36Sopenharmony_ci .len = sizeof(struct il_scan_cmd), 85562306a36Sopenharmony_ci .flags = CMD_SIZE_HUGE, 85662306a36Sopenharmony_ci }; 85762306a36Sopenharmony_ci struct il_scan_cmd *scan; 85862306a36Sopenharmony_ci u32 rate_flags = 0; 85962306a36Sopenharmony_ci u16 cmd_len; 86062306a36Sopenharmony_ci u16 rx_chain = 0; 86162306a36Sopenharmony_ci enum nl80211_band band; 86262306a36Sopenharmony_ci u8 n_probes = 0; 86362306a36Sopenharmony_ci u8 rx_ant = il->hw_params.valid_rx_ant; 86462306a36Sopenharmony_ci u8 rate; 86562306a36Sopenharmony_ci bool is_active = false; 86662306a36Sopenharmony_ci int chan_mod; 86762306a36Sopenharmony_ci u8 active_chains; 86862306a36Sopenharmony_ci u8 scan_tx_antennas = il->hw_params.valid_tx_ant; 86962306a36Sopenharmony_ci int ret; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (!il->scan_cmd) { 87462306a36Sopenharmony_ci il->scan_cmd = 87562306a36Sopenharmony_ci kmalloc(sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE, 87662306a36Sopenharmony_ci GFP_KERNEL); 87762306a36Sopenharmony_ci if (!il->scan_cmd) { 87862306a36Sopenharmony_ci D_SCAN("fail to allocate memory for scan\n"); 87962306a36Sopenharmony_ci return -ENOMEM; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci scan = il->scan_cmd; 88362306a36Sopenharmony_ci memset(scan, 0, sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH; 88662306a36Sopenharmony_ci scan->quiet_time = IL_ACTIVE_QUIET_TIME; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (il_is_any_associated(il)) { 88962306a36Sopenharmony_ci u16 interval; 89062306a36Sopenharmony_ci u32 extra; 89162306a36Sopenharmony_ci u32 suspend_time = 100; 89262306a36Sopenharmony_ci u32 scan_suspend_time = 100; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci D_INFO("Scanning while associated...\n"); 89562306a36Sopenharmony_ci interval = vif->bss_conf.beacon_int; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci scan->suspend_time = 0; 89862306a36Sopenharmony_ci scan->max_out_time = cpu_to_le32(200 * 1024); 89962306a36Sopenharmony_ci if (!interval) 90062306a36Sopenharmony_ci interval = suspend_time; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci extra = (suspend_time / interval) << 22; 90362306a36Sopenharmony_ci scan_suspend_time = 90462306a36Sopenharmony_ci (extra | ((suspend_time % interval) * 1024)); 90562306a36Sopenharmony_ci scan->suspend_time = cpu_to_le32(scan_suspend_time); 90662306a36Sopenharmony_ci D_SCAN("suspend_time 0x%X beacon interval %d\n", 90762306a36Sopenharmony_ci scan_suspend_time, interval); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (il->scan_request->n_ssids) { 91162306a36Sopenharmony_ci int i, p = 0; 91262306a36Sopenharmony_ci D_SCAN("Kicking off active scan\n"); 91362306a36Sopenharmony_ci for (i = 0; i < il->scan_request->n_ssids; i++) { 91462306a36Sopenharmony_ci /* always does wildcard anyway */ 91562306a36Sopenharmony_ci if (!il->scan_request->ssids[i].ssid_len) 91662306a36Sopenharmony_ci continue; 91762306a36Sopenharmony_ci scan->direct_scan[p].id = WLAN_EID_SSID; 91862306a36Sopenharmony_ci scan->direct_scan[p].len = 91962306a36Sopenharmony_ci il->scan_request->ssids[i].ssid_len; 92062306a36Sopenharmony_ci memcpy(scan->direct_scan[p].ssid, 92162306a36Sopenharmony_ci il->scan_request->ssids[i].ssid, 92262306a36Sopenharmony_ci il->scan_request->ssids[i].ssid_len); 92362306a36Sopenharmony_ci n_probes++; 92462306a36Sopenharmony_ci p++; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci is_active = true; 92762306a36Sopenharmony_ci } else 92862306a36Sopenharmony_ci D_SCAN("Start passive scan.\n"); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; 93162306a36Sopenharmony_ci scan->tx_cmd.sta_id = il->hw_params.bcast_id; 93262306a36Sopenharmony_ci scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci switch (il->scan_band) { 93562306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 93662306a36Sopenharmony_ci scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; 93762306a36Sopenharmony_ci chan_mod = 93862306a36Sopenharmony_ci le32_to_cpu(il->active.flags & RXON_FLG_CHANNEL_MODE_MSK) >> 93962306a36Sopenharmony_ci RXON_FLG_CHANNEL_MODE_POS; 94062306a36Sopenharmony_ci if (chan_mod == CHANNEL_MODE_PURE_40) { 94162306a36Sopenharmony_ci rate = RATE_6M_PLCP; 94262306a36Sopenharmony_ci } else { 94362306a36Sopenharmony_ci rate = RATE_1M_PLCP; 94462306a36Sopenharmony_ci rate_flags = RATE_MCS_CCK_MSK; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 94862306a36Sopenharmony_ci rate = RATE_6M_PLCP; 94962306a36Sopenharmony_ci break; 95062306a36Sopenharmony_ci default: 95162306a36Sopenharmony_ci IL_WARN("Invalid scan band\n"); 95262306a36Sopenharmony_ci return -EIO; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* 95662306a36Sopenharmony_ci * If active scanning is requested but a certain channel is 95762306a36Sopenharmony_ci * marked passive, we can do active scanning if we detect 95862306a36Sopenharmony_ci * transmissions. 95962306a36Sopenharmony_ci * 96062306a36Sopenharmony_ci * There is an issue with some firmware versions that triggers 96162306a36Sopenharmony_ci * a sysassert on a "good CRC threshold" of zero (== disabled), 96262306a36Sopenharmony_ci * on a radar channel even though this means that we should NOT 96362306a36Sopenharmony_ci * send probes. 96462306a36Sopenharmony_ci * 96562306a36Sopenharmony_ci * The "good CRC threshold" is the number of frames that we 96662306a36Sopenharmony_ci * need to receive during our dwell time on a channel before 96762306a36Sopenharmony_ci * sending out probes -- setting this to a huge value will 96862306a36Sopenharmony_ci * mean we never reach it, but at the same time work around 96962306a36Sopenharmony_ci * the aforementioned issue. Thus use IL_GOOD_CRC_TH_NEVER 97062306a36Sopenharmony_ci * here instead of IL_GOOD_CRC_TH_DISABLED. 97162306a36Sopenharmony_ci */ 97262306a36Sopenharmony_ci scan->good_CRC_th = 97362306a36Sopenharmony_ci is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci band = il->scan_band; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (il->cfg->scan_rx_antennas[band]) 97862306a36Sopenharmony_ci rx_ant = il->cfg->scan_rx_antennas[band]; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci il4965_toggle_tx_ant(il, &il->scan_tx_ant[band], scan_tx_antennas); 98162306a36Sopenharmony_ci rate_flags |= BIT(il->scan_tx_ant[band]) << RATE_MCS_ANT_POS; 98262306a36Sopenharmony_ci scan->tx_cmd.rate_n_flags = cpu_to_le32(rate | rate_flags); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* In power save mode use one chain, otherwise use all chains */ 98562306a36Sopenharmony_ci if (test_bit(S_POWER_PMI, &il->status)) { 98662306a36Sopenharmony_ci /* rx_ant has been set to all valid chains previously */ 98762306a36Sopenharmony_ci active_chains = 98862306a36Sopenharmony_ci rx_ant & ((u8) (il->chain_noise_data.active_chains)); 98962306a36Sopenharmony_ci if (!active_chains) 99062306a36Sopenharmony_ci active_chains = rx_ant; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci D_SCAN("chain_noise_data.active_chains: %u\n", 99362306a36Sopenharmony_ci il->chain_noise_data.active_chains); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci rx_ant = il4965_first_antenna(active_chains); 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* MIMO is not used here, but value is required */ 99962306a36Sopenharmony_ci rx_chain |= il->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; 100062306a36Sopenharmony_ci rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; 100162306a36Sopenharmony_ci rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; 100262306a36Sopenharmony_ci rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; 100362306a36Sopenharmony_ci scan->rx_chain = cpu_to_le16(rx_chain); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci cmd_len = 100662306a36Sopenharmony_ci il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data, 100762306a36Sopenharmony_ci vif->addr, il->scan_request->ie, 100862306a36Sopenharmony_ci il->scan_request->ie_len, 100962306a36Sopenharmony_ci IL_MAX_SCAN_SIZE - sizeof(*scan)); 101062306a36Sopenharmony_ci scan->tx_cmd.len = cpu_to_le16(cmd_len); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci scan->filter_flags |= 101362306a36Sopenharmony_ci (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci scan->channel_count = 101662306a36Sopenharmony_ci il4965_get_channels_for_scan(il, vif, band, is_active, n_probes, 101762306a36Sopenharmony_ci (void *)&scan->data[cmd_len]); 101862306a36Sopenharmony_ci if (scan->channel_count == 0) { 101962306a36Sopenharmony_ci D_SCAN("channel count %d\n", scan->channel_count); 102062306a36Sopenharmony_ci return -EIO; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci cmd.len += 102462306a36Sopenharmony_ci le16_to_cpu(scan->tx_cmd.len) + 102562306a36Sopenharmony_ci scan->channel_count * sizeof(struct il_scan_channel); 102662306a36Sopenharmony_ci cmd.data = scan; 102762306a36Sopenharmony_ci scan->len = cpu_to_le16(cmd.len); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci set_bit(S_SCAN_HW, &il->status); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci ret = il_send_cmd_sync(il, &cmd); 103262306a36Sopenharmony_ci if (ret) 103362306a36Sopenharmony_ci clear_bit(S_SCAN_HW, &il->status); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci return ret; 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ciint 103962306a36Sopenharmony_ciil4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, 104062306a36Sopenharmony_ci bool add) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct il_vif_priv *vif_priv = (void *)vif->drv_priv; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (add) 104562306a36Sopenharmony_ci return il4965_add_bssid_station(il, vif->bss_conf.bssid, 104662306a36Sopenharmony_ci &vif_priv->ibss_bssid_sta_id); 104762306a36Sopenharmony_ci return il_remove_station(il, vif_priv->ibss_bssid_sta_id, 104862306a36Sopenharmony_ci vif->bss_conf.bssid); 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_civoid 105262306a36Sopenharmony_ciil4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci lockdep_assert_held(&il->sta_lock); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (il->stations[sta_id].tid[tid].tfds_in_queue >= freed) 105762306a36Sopenharmony_ci il->stations[sta_id].tid[tid].tfds_in_queue -= freed; 105862306a36Sopenharmony_ci else { 105962306a36Sopenharmony_ci D_TX("free more than tfds_in_queue (%u:%d)\n", 106062306a36Sopenharmony_ci il->stations[sta_id].tid[tid].tfds_in_queue, freed); 106162306a36Sopenharmony_ci il->stations[sta_id].tid[tid].tfds_in_queue = 0; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci#define IL_TX_QUEUE_MSK 0xfffff 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic bool 106862306a36Sopenharmony_ciil4965_is_single_rx_stream(struct il_priv *il) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci return il->current_ht_config.smps == IEEE80211_SMPS_STATIC || 107162306a36Sopenharmony_ci il->current_ht_config.single_chain_sufficient; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci#define IL_NUM_RX_CHAINS_MULTIPLE 3 107562306a36Sopenharmony_ci#define IL_NUM_RX_CHAINS_SINGLE 2 107662306a36Sopenharmony_ci#define IL_NUM_IDLE_CHAINS_DUAL 2 107762306a36Sopenharmony_ci#define IL_NUM_IDLE_CHAINS_SINGLE 1 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci/* 108062306a36Sopenharmony_ci * Determine how many receiver/antenna chains to use. 108162306a36Sopenharmony_ci * 108262306a36Sopenharmony_ci * More provides better reception via diversity. Fewer saves power 108362306a36Sopenharmony_ci * at the expense of throughput, but only when not in powersave to 108462306a36Sopenharmony_ci * start with. 108562306a36Sopenharmony_ci * 108662306a36Sopenharmony_ci * MIMO (dual stream) requires at least 2, but works better with 3. 108762306a36Sopenharmony_ci * This does not determine *which* chains to use, just how many. 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_cistatic int 109062306a36Sopenharmony_ciil4965_get_active_rx_chain_count(struct il_priv *il) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci /* # of Rx chains to use when expecting MIMO. */ 109362306a36Sopenharmony_ci if (il4965_is_single_rx_stream(il)) 109462306a36Sopenharmony_ci return IL_NUM_RX_CHAINS_SINGLE; 109562306a36Sopenharmony_ci else 109662306a36Sopenharmony_ci return IL_NUM_RX_CHAINS_MULTIPLE; 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci/* 110062306a36Sopenharmony_ci * When we are in power saving mode, unless device support spatial 110162306a36Sopenharmony_ci * multiplexing power save, use the active count for rx chain count. 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_cistatic int 110462306a36Sopenharmony_ciil4965_get_idle_rx_chain_count(struct il_priv *il, int active_cnt) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci /* # Rx chains when idling, depending on SMPS mode */ 110762306a36Sopenharmony_ci switch (il->current_ht_config.smps) { 110862306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 110962306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 111062306a36Sopenharmony_ci return IL_NUM_IDLE_CHAINS_SINGLE; 111162306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 111262306a36Sopenharmony_ci return active_cnt; 111362306a36Sopenharmony_ci default: 111462306a36Sopenharmony_ci WARN(1, "invalid SMPS mode %d", il->current_ht_config.smps); 111562306a36Sopenharmony_ci return active_cnt; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci/* up to 4 chains */ 112062306a36Sopenharmony_cistatic u8 112162306a36Sopenharmony_ciil4965_count_chain_bitmap(u32 chain_bitmap) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci u8 res; 112462306a36Sopenharmony_ci res = (chain_bitmap & BIT(0)) >> 0; 112562306a36Sopenharmony_ci res += (chain_bitmap & BIT(1)) >> 1; 112662306a36Sopenharmony_ci res += (chain_bitmap & BIT(2)) >> 2; 112762306a36Sopenharmony_ci res += (chain_bitmap & BIT(3)) >> 3; 112862306a36Sopenharmony_ci return res; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci/* 113262306a36Sopenharmony_ci * il4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image 113362306a36Sopenharmony_ci * 113462306a36Sopenharmony_ci * Selects how many and which Rx receivers/antennas/chains to use. 113562306a36Sopenharmony_ci * This should not be used for scan command ... it puts data in wrong place. 113662306a36Sopenharmony_ci */ 113762306a36Sopenharmony_civoid 113862306a36Sopenharmony_ciil4965_set_rxon_chain(struct il_priv *il) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci bool is_single = il4965_is_single_rx_stream(il); 114162306a36Sopenharmony_ci bool is_cam = !test_bit(S_POWER_PMI, &il->status); 114262306a36Sopenharmony_ci u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; 114362306a36Sopenharmony_ci u32 active_chains; 114462306a36Sopenharmony_ci u16 rx_chain; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* Tell uCode which antennas are actually connected. 114762306a36Sopenharmony_ci * Before first association, we assume all antennas are connected. 114862306a36Sopenharmony_ci * Just after first association, il4965_chain_noise_calibration() 114962306a36Sopenharmony_ci * checks which antennas actually *are* connected. */ 115062306a36Sopenharmony_ci if (il->chain_noise_data.active_chains) 115162306a36Sopenharmony_ci active_chains = il->chain_noise_data.active_chains; 115262306a36Sopenharmony_ci else 115362306a36Sopenharmony_ci active_chains = il->hw_params.valid_rx_ant; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* How many receivers should we use? */ 115862306a36Sopenharmony_ci active_rx_cnt = il4965_get_active_rx_chain_count(il); 115962306a36Sopenharmony_ci idle_rx_cnt = il4965_get_idle_rx_chain_count(il, active_rx_cnt); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* correct rx chain count according hw settings 116262306a36Sopenharmony_ci * and chain noise calibration 116362306a36Sopenharmony_ci */ 116462306a36Sopenharmony_ci valid_rx_cnt = il4965_count_chain_bitmap(active_chains); 116562306a36Sopenharmony_ci if (valid_rx_cnt < active_rx_cnt) 116662306a36Sopenharmony_ci active_rx_cnt = valid_rx_cnt; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (valid_rx_cnt < idle_rx_cnt) 116962306a36Sopenharmony_ci idle_rx_cnt = valid_rx_cnt; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; 117262306a36Sopenharmony_ci rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci il->staging.rx_chain = cpu_to_le16(rx_chain); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (!is_single && active_rx_cnt >= IL_NUM_RX_CHAINS_SINGLE && is_cam) 117762306a36Sopenharmony_ci il->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; 117862306a36Sopenharmony_ci else 117962306a36Sopenharmony_ci il->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci D_ASSOC("rx_chain=0x%X active=%d idle=%d\n", il->staging.rx_chain, 118262306a36Sopenharmony_ci active_rx_cnt, idle_rx_cnt); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || 118562306a36Sopenharmony_ci active_rx_cnt < idle_rx_cnt); 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic const char * 118962306a36Sopenharmony_ciil4965_get_fh_string(int cmd) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci switch (cmd) { 119262306a36Sopenharmony_ci IL_CMD(FH49_RSCSR_CHNL0_STTS_WPTR_REG); 119362306a36Sopenharmony_ci IL_CMD(FH49_RSCSR_CHNL0_RBDCB_BASE_REG); 119462306a36Sopenharmony_ci IL_CMD(FH49_RSCSR_CHNL0_WPTR); 119562306a36Sopenharmony_ci IL_CMD(FH49_MEM_RCSR_CHNL0_CONFIG_REG); 119662306a36Sopenharmony_ci IL_CMD(FH49_MEM_RSSR_SHARED_CTRL_REG); 119762306a36Sopenharmony_ci IL_CMD(FH49_MEM_RSSR_RX_STATUS_REG); 119862306a36Sopenharmony_ci IL_CMD(FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); 119962306a36Sopenharmony_ci IL_CMD(FH49_TSSR_TX_STATUS_REG); 120062306a36Sopenharmony_ci IL_CMD(FH49_TSSR_TX_ERROR_REG); 120162306a36Sopenharmony_ci default: 120262306a36Sopenharmony_ci return "UNKNOWN"; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ciint 120762306a36Sopenharmony_ciil4965_dump_fh(struct il_priv *il, char **buf, bool display) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci int i; 121062306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 121162306a36Sopenharmony_ci int pos = 0; 121262306a36Sopenharmony_ci size_t bufsz = 0; 121362306a36Sopenharmony_ci#endif 121462306a36Sopenharmony_ci static const u32 fh_tbl[] = { 121562306a36Sopenharmony_ci FH49_RSCSR_CHNL0_STTS_WPTR_REG, 121662306a36Sopenharmony_ci FH49_RSCSR_CHNL0_RBDCB_BASE_REG, 121762306a36Sopenharmony_ci FH49_RSCSR_CHNL0_WPTR, 121862306a36Sopenharmony_ci FH49_MEM_RCSR_CHNL0_CONFIG_REG, 121962306a36Sopenharmony_ci FH49_MEM_RSSR_SHARED_CTRL_REG, 122062306a36Sopenharmony_ci FH49_MEM_RSSR_RX_STATUS_REG, 122162306a36Sopenharmony_ci FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, 122262306a36Sopenharmony_ci FH49_TSSR_TX_STATUS_REG, 122362306a36Sopenharmony_ci FH49_TSSR_TX_ERROR_REG 122462306a36Sopenharmony_ci }; 122562306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 122662306a36Sopenharmony_ci if (display) { 122762306a36Sopenharmony_ci bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; 122862306a36Sopenharmony_ci *buf = kmalloc(bufsz, GFP_KERNEL); 122962306a36Sopenharmony_ci if (!*buf) 123062306a36Sopenharmony_ci return -ENOMEM; 123162306a36Sopenharmony_ci pos += 123262306a36Sopenharmony_ci scnprintf(*buf + pos, bufsz - pos, "FH register values:\n"); 123362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { 123462306a36Sopenharmony_ci pos += 123562306a36Sopenharmony_ci scnprintf(*buf + pos, bufsz - pos, 123662306a36Sopenharmony_ci " %34s: 0X%08x\n", 123762306a36Sopenharmony_ci il4965_get_fh_string(fh_tbl[i]), 123862306a36Sopenharmony_ci il_rd(il, fh_tbl[i])); 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci return pos; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci#endif 124362306a36Sopenharmony_ci IL_ERR("FH register values:\n"); 124462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { 124562306a36Sopenharmony_ci IL_ERR(" %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]), 124662306a36Sopenharmony_ci il_rd(il, fh_tbl[i])); 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci return 0; 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic void 125262306a36Sopenharmony_ciil4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 125562306a36Sopenharmony_ci struct il_missed_beacon_notif *missed_beacon; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci missed_beacon = &pkt->u.missed_beacon; 125862306a36Sopenharmony_ci if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > 125962306a36Sopenharmony_ci il->missed_beacon_threshold) { 126062306a36Sopenharmony_ci D_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n", 126162306a36Sopenharmony_ci le32_to_cpu(missed_beacon->consecutive_missed_beacons), 126262306a36Sopenharmony_ci le32_to_cpu(missed_beacon->total_missed_becons), 126362306a36Sopenharmony_ci le32_to_cpu(missed_beacon->num_recvd_beacons), 126462306a36Sopenharmony_ci le32_to_cpu(missed_beacon->num_expected_beacons)); 126562306a36Sopenharmony_ci if (!test_bit(S_SCANNING, &il->status)) 126662306a36Sopenharmony_ci il4965_init_sensitivity(il); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci/* Calculate noise level, based on measurements during network silence just 127162306a36Sopenharmony_ci * before arriving beacon. This measurement can be done only if we know 127262306a36Sopenharmony_ci * exactly when to expect beacons, therefore only when we're associated. */ 127362306a36Sopenharmony_cistatic void 127462306a36Sopenharmony_ciil4965_rx_calc_noise(struct il_priv *il) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci struct stats_rx_non_phy *rx_info; 127762306a36Sopenharmony_ci int num_active_rx = 0; 127862306a36Sopenharmony_ci int total_silence = 0; 127962306a36Sopenharmony_ci int bcn_silence_a, bcn_silence_b, bcn_silence_c; 128062306a36Sopenharmony_ci int last_rx_noise; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci rx_info = &(il->_4965.stats.rx.general); 128362306a36Sopenharmony_ci bcn_silence_a = 128462306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; 128562306a36Sopenharmony_ci bcn_silence_b = 128662306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; 128762306a36Sopenharmony_ci bcn_silence_c = 128862306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci if (bcn_silence_a) { 129162306a36Sopenharmony_ci total_silence += bcn_silence_a; 129262306a36Sopenharmony_ci num_active_rx++; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci if (bcn_silence_b) { 129562306a36Sopenharmony_ci total_silence += bcn_silence_b; 129662306a36Sopenharmony_ci num_active_rx++; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci if (bcn_silence_c) { 129962306a36Sopenharmony_ci total_silence += bcn_silence_c; 130062306a36Sopenharmony_ci num_active_rx++; 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* Average among active antennas */ 130462306a36Sopenharmony_ci if (num_active_rx) 130562306a36Sopenharmony_ci last_rx_noise = (total_silence / num_active_rx) - 107; 130662306a36Sopenharmony_ci else 130762306a36Sopenharmony_ci last_rx_noise = IL_NOISE_MEAS_NOT_AVAILABLE; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci D_CALIB("inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a, 131062306a36Sopenharmony_ci bcn_silence_b, bcn_silence_c, last_rx_noise); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS 131462306a36Sopenharmony_ci/* 131562306a36Sopenharmony_ci * based on the assumption of all stats counter are in DWORD 131662306a36Sopenharmony_ci * FIXME: This function is for debugging, do not deal with 131762306a36Sopenharmony_ci * the case of counters roll-over. 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_cistatic void 132062306a36Sopenharmony_ciil4965_accumulative_stats(struct il_priv *il, __le32 * stats) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci int i, size; 132362306a36Sopenharmony_ci __le32 *prev_stats; 132462306a36Sopenharmony_ci u32 *accum_stats; 132562306a36Sopenharmony_ci u32 *delta, *max_delta; 132662306a36Sopenharmony_ci struct stats_general_common *general, *accum_general; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci prev_stats = (__le32 *) &il->_4965.stats; 132962306a36Sopenharmony_ci accum_stats = (u32 *) &il->_4965.accum_stats; 133062306a36Sopenharmony_ci size = sizeof(struct il_notif_stats); 133162306a36Sopenharmony_ci general = &il->_4965.stats.general.common; 133262306a36Sopenharmony_ci accum_general = &il->_4965.accum_stats.general.common; 133362306a36Sopenharmony_ci delta = (u32 *) &il->_4965.delta_stats; 133462306a36Sopenharmony_ci max_delta = (u32 *) &il->_4965.max_delta; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci for (i = sizeof(__le32); i < size; 133762306a36Sopenharmony_ci i += 133862306a36Sopenharmony_ci sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, 133962306a36Sopenharmony_ci accum_stats++) { 134062306a36Sopenharmony_ci if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { 134162306a36Sopenharmony_ci *delta = 134262306a36Sopenharmony_ci (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); 134362306a36Sopenharmony_ci *accum_stats += *delta; 134462306a36Sopenharmony_ci if (*delta > *max_delta) 134562306a36Sopenharmony_ci *max_delta = *delta; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci /* reset accumulative stats for "no-counter" type stats */ 135062306a36Sopenharmony_ci accum_general->temperature = general->temperature; 135162306a36Sopenharmony_ci accum_general->ttl_timestamp = general->ttl_timestamp; 135262306a36Sopenharmony_ci} 135362306a36Sopenharmony_ci#endif 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_cistatic void 135662306a36Sopenharmony_ciil4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci const int recalib_seconds = 60; 135962306a36Sopenharmony_ci bool change; 136062306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci D_RX("Statistics notification received (%d vs %d).\n", 136362306a36Sopenharmony_ci (int)sizeof(struct il_notif_stats), 136462306a36Sopenharmony_ci le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci change = 136762306a36Sopenharmony_ci ((il->_4965.stats.general.common.temperature != 136862306a36Sopenharmony_ci pkt->u.stats.general.common.temperature) || 136962306a36Sopenharmony_ci ((il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK) != 137062306a36Sopenharmony_ci (pkt->u.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK))); 137162306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS 137262306a36Sopenharmony_ci il4965_accumulative_stats(il, (__le32 *) &pkt->u.stats); 137362306a36Sopenharmony_ci#endif 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci /* TODO: reading some of stats is unneeded */ 137662306a36Sopenharmony_ci memcpy(&il->_4965.stats, &pkt->u.stats, sizeof(il->_4965.stats)); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci set_bit(S_STATS, &il->status); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* 138162306a36Sopenharmony_ci * Reschedule the stats timer to occur in recalib_seconds to ensure 138262306a36Sopenharmony_ci * we get a thermal update even if the uCode doesn't give us one 138362306a36Sopenharmony_ci */ 138462306a36Sopenharmony_ci mod_timer(&il->stats_periodic, 138562306a36Sopenharmony_ci jiffies + msecs_to_jiffies(recalib_seconds * 1000)); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci if (unlikely(!test_bit(S_SCANNING, &il->status)) && 138862306a36Sopenharmony_ci (pkt->hdr.cmd == N_STATS)) { 138962306a36Sopenharmony_ci il4965_rx_calc_noise(il); 139062306a36Sopenharmony_ci queue_work(il->workqueue, &il->run_time_calib_work); 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (change) 139462306a36Sopenharmony_ci il4965_temperature_calib(il); 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cistatic void 139862306a36Sopenharmony_ciil4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATS_CLEAR_MSK) { 140362306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS 140462306a36Sopenharmony_ci memset(&il->_4965.accum_stats, 0, 140562306a36Sopenharmony_ci sizeof(struct il_notif_stats)); 140662306a36Sopenharmony_ci memset(&il->_4965.delta_stats, 0, 140762306a36Sopenharmony_ci sizeof(struct il_notif_stats)); 140862306a36Sopenharmony_ci memset(&il->_4965.max_delta, 0, sizeof(struct il_notif_stats)); 140962306a36Sopenharmony_ci#endif 141062306a36Sopenharmony_ci D_RX("Statistics have been cleared\n"); 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci il4965_hdl_stats(il, rxb); 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci/* 141762306a36Sopenharmony_ci * mac80211 queues, ACs, hardware queues, FIFOs. 141862306a36Sopenharmony_ci * 141962306a36Sopenharmony_ci * Cf. https://wireless.wiki.kernel.org/en/developers/Documentation/mac80211/queues 142062306a36Sopenharmony_ci * 142162306a36Sopenharmony_ci * Mac80211 uses the following numbers, which we get as from it 142262306a36Sopenharmony_ci * by way of skb_get_queue_mapping(skb): 142362306a36Sopenharmony_ci * 142462306a36Sopenharmony_ci * VO 0 142562306a36Sopenharmony_ci * VI 1 142662306a36Sopenharmony_ci * BE 2 142762306a36Sopenharmony_ci * BK 3 142862306a36Sopenharmony_ci * 142962306a36Sopenharmony_ci * 143062306a36Sopenharmony_ci * Regular (not A-MPDU) frames are put into hardware queues corresponding 143162306a36Sopenharmony_ci * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their 143262306a36Sopenharmony_ci * own queue per aggregation session (RA/TID combination), such queues are 143362306a36Sopenharmony_ci * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In 143462306a36Sopenharmony_ci * order to map frames to the right queue, we also need an AC->hw queue 143562306a36Sopenharmony_ci * mapping. This is implemented here. 143662306a36Sopenharmony_ci * 143762306a36Sopenharmony_ci * Due to the way hw queues are set up (by the hw specific modules like 143862306a36Sopenharmony_ci * 4965.c), the AC->hw queue mapping is the identity 143962306a36Sopenharmony_ci * mapping. 144062306a36Sopenharmony_ci */ 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic const u8 tid_to_ac[] = { 144362306a36Sopenharmony_ci IEEE80211_AC_BE, 144462306a36Sopenharmony_ci IEEE80211_AC_BK, 144562306a36Sopenharmony_ci IEEE80211_AC_BK, 144662306a36Sopenharmony_ci IEEE80211_AC_BE, 144762306a36Sopenharmony_ci IEEE80211_AC_VI, 144862306a36Sopenharmony_ci IEEE80211_AC_VI, 144962306a36Sopenharmony_ci IEEE80211_AC_VO, 145062306a36Sopenharmony_ci IEEE80211_AC_VO 145162306a36Sopenharmony_ci}; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_cistatic inline int 145462306a36Sopenharmony_ciil4965_get_ac_from_tid(u16 tid) 145562306a36Sopenharmony_ci{ 145662306a36Sopenharmony_ci if (likely(tid < ARRAY_SIZE(tid_to_ac))) 145762306a36Sopenharmony_ci return tid_to_ac[tid]; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci /* no support for TIDs 8-15 yet */ 146062306a36Sopenharmony_ci return -EINVAL; 146162306a36Sopenharmony_ci} 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_cistatic inline int 146462306a36Sopenharmony_ciil4965_get_fifo_from_tid(u16 tid) 146562306a36Sopenharmony_ci{ 146662306a36Sopenharmony_ci static const u8 ac_to_fifo[] = { 146762306a36Sopenharmony_ci IL_TX_FIFO_VO, 146862306a36Sopenharmony_ci IL_TX_FIFO_VI, 146962306a36Sopenharmony_ci IL_TX_FIFO_BE, 147062306a36Sopenharmony_ci IL_TX_FIFO_BK, 147162306a36Sopenharmony_ci }; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci if (likely(tid < ARRAY_SIZE(tid_to_ac))) 147462306a36Sopenharmony_ci return ac_to_fifo[tid_to_ac[tid]]; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* no support for TIDs 8-15 yet */ 147762306a36Sopenharmony_ci return -EINVAL; 147862306a36Sopenharmony_ci} 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci/* 148162306a36Sopenharmony_ci * handle build C_TX command notification. 148262306a36Sopenharmony_ci */ 148362306a36Sopenharmony_cistatic void 148462306a36Sopenharmony_ciil4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb, 148562306a36Sopenharmony_ci struct il_tx_cmd *tx_cmd, 148662306a36Sopenharmony_ci struct ieee80211_tx_info *info, 148762306a36Sopenharmony_ci struct ieee80211_hdr *hdr, u8 std_id) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci __le16 fc = hdr->frame_control; 149062306a36Sopenharmony_ci __le32 tx_flags = tx_cmd->tx_flags; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; 149362306a36Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { 149462306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_ACK_MSK; 149562306a36Sopenharmony_ci if (ieee80211_is_mgmt(fc)) 149662306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; 149762306a36Sopenharmony_ci if (ieee80211_is_probe_resp(fc) && 149862306a36Sopenharmony_ci !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) 149962306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_TSF_MSK; 150062306a36Sopenharmony_ci } else { 150162306a36Sopenharmony_ci tx_flags &= (~TX_CMD_FLG_ACK_MSK); 150262306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci if (ieee80211_is_back_req(fc)) 150662306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci tx_cmd->sta_id = std_id; 150962306a36Sopenharmony_ci if (ieee80211_has_morefrags(fc)) 151062306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci if (ieee80211_is_data_qos(fc)) { 151362306a36Sopenharmony_ci u8 *qc = ieee80211_get_qos_ctl(hdr); 151462306a36Sopenharmony_ci tx_cmd->tid_tspec = qc[0] & 0xf; 151562306a36Sopenharmony_ci tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; 151662306a36Sopenharmony_ci } else { 151762306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci il_tx_cmd_protection(il, info, fc, &tx_flags); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); 152362306a36Sopenharmony_ci if (ieee80211_is_mgmt(fc)) { 152462306a36Sopenharmony_ci if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) 152562306a36Sopenharmony_ci tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); 152662306a36Sopenharmony_ci else 152762306a36Sopenharmony_ci tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); 152862306a36Sopenharmony_ci } else { 152962306a36Sopenharmony_ci tx_cmd->timeout.pm_frame_timeout = 0; 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci tx_cmd->driver_txop = 0; 153362306a36Sopenharmony_ci tx_cmd->tx_flags = tx_flags; 153462306a36Sopenharmony_ci tx_cmd->next_frame_len = 0; 153562306a36Sopenharmony_ci} 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_cistatic void 153862306a36Sopenharmony_ciil4965_tx_cmd_build_rate(struct il_priv *il, 153962306a36Sopenharmony_ci struct il_tx_cmd *tx_cmd, 154062306a36Sopenharmony_ci struct ieee80211_tx_info *info, 154162306a36Sopenharmony_ci struct ieee80211_sta *sta, 154262306a36Sopenharmony_ci __le16 fc) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci const u8 rts_retry_limit = 60; 154562306a36Sopenharmony_ci u32 rate_flags; 154662306a36Sopenharmony_ci int rate_idx; 154762306a36Sopenharmony_ci u8 data_retry_limit; 154862306a36Sopenharmony_ci u8 rate_plcp; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci /* Set retry limit on DATA packets and Probe Responses */ 155162306a36Sopenharmony_ci if (ieee80211_is_probe_resp(fc)) 155262306a36Sopenharmony_ci data_retry_limit = 3; 155362306a36Sopenharmony_ci else 155462306a36Sopenharmony_ci data_retry_limit = IL4965_DEFAULT_TX_RETRY; 155562306a36Sopenharmony_ci tx_cmd->data_retry_limit = data_retry_limit; 155662306a36Sopenharmony_ci /* Set retry limit on RTS packets */ 155762306a36Sopenharmony_ci tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci /* DATA packets will use the uCode station table for rate/antenna 156062306a36Sopenharmony_ci * selection */ 156162306a36Sopenharmony_ci if (ieee80211_is_data(fc)) { 156262306a36Sopenharmony_ci tx_cmd->initial_rate_idx = 0; 156362306a36Sopenharmony_ci tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; 156462306a36Sopenharmony_ci return; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci /** 156862306a36Sopenharmony_ci * If the current TX rate stored in mac80211 has the MCS bit set, it's 156962306a36Sopenharmony_ci * not really a TX rate. Thus, we use the lowest supported rate for 157062306a36Sopenharmony_ci * this band. Also use the lowest supported rate if the stored rate 157162306a36Sopenharmony_ci * idx is invalid. 157262306a36Sopenharmony_ci */ 157362306a36Sopenharmony_ci rate_idx = info->control.rates[0].idx; 157462306a36Sopenharmony_ci if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0 157562306a36Sopenharmony_ci || rate_idx > RATE_COUNT_LEGACY) 157662306a36Sopenharmony_ci rate_idx = rate_lowest_index(&il->bands[info->band], sta); 157762306a36Sopenharmony_ci /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ 157862306a36Sopenharmony_ci if (info->band == NL80211_BAND_5GHZ) 157962306a36Sopenharmony_ci rate_idx += IL_FIRST_OFDM_RATE; 158062306a36Sopenharmony_ci /* Get PLCP rate for tx_cmd->rate_n_flags */ 158162306a36Sopenharmony_ci rate_plcp = il_rates[rate_idx].plcp; 158262306a36Sopenharmony_ci /* Zero out flags for this packet */ 158362306a36Sopenharmony_ci rate_flags = 0; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci /* Set CCK flag as needed */ 158662306a36Sopenharmony_ci if (rate_idx >= IL_FIRST_CCK_RATE && rate_idx <= IL_LAST_CCK_RATE) 158762306a36Sopenharmony_ci rate_flags |= RATE_MCS_CCK_MSK; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci /* Set up antennas */ 159062306a36Sopenharmony_ci il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant); 159162306a36Sopenharmony_ci rate_flags |= BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci /* Set the rate in the TX cmd */ 159462306a36Sopenharmony_ci tx_cmd->rate_n_flags = cpu_to_le32(rate_plcp | rate_flags); 159562306a36Sopenharmony_ci} 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_cistatic void 159862306a36Sopenharmony_ciil4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info, 159962306a36Sopenharmony_ci struct il_tx_cmd *tx_cmd, struct sk_buff *skb_frag, 160062306a36Sopenharmony_ci int sta_id) 160162306a36Sopenharmony_ci{ 160262306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf = info->control.hw_key; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci switch (keyconf->cipher) { 160562306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 160662306a36Sopenharmony_ci tx_cmd->sec_ctl = TX_CMD_SEC_CCM; 160762306a36Sopenharmony_ci memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); 160862306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_AMPDU) 160962306a36Sopenharmony_ci tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; 161062306a36Sopenharmony_ci D_TX("tx_cmd with AES hwcrypto\n"); 161162306a36Sopenharmony_ci break; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 161462306a36Sopenharmony_ci tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; 161562306a36Sopenharmony_ci ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); 161662306a36Sopenharmony_ci D_TX("tx_cmd with tkip hwcrypto\n"); 161762306a36Sopenharmony_ci break; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 162062306a36Sopenharmony_ci tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; 162162306a36Sopenharmony_ci fallthrough; 162262306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 162362306a36Sopenharmony_ci tx_cmd->sec_ctl |= 162462306a36Sopenharmony_ci (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) << 162562306a36Sopenharmony_ci TX_CMD_SEC_SHIFT); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci D_TX("Configuring packet for WEP encryption " "with key %d\n", 163062306a36Sopenharmony_ci keyconf->keyidx); 163162306a36Sopenharmony_ci break; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci default: 163462306a36Sopenharmony_ci IL_ERR("Unknown encode cipher %x\n", keyconf->cipher); 163562306a36Sopenharmony_ci break; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci/* 164062306a36Sopenharmony_ci * start C_TX command process 164162306a36Sopenharmony_ci */ 164262306a36Sopenharmony_ciint 164362306a36Sopenharmony_ciil4965_tx_skb(struct il_priv *il, 164462306a36Sopenharmony_ci struct ieee80211_sta *sta, 164562306a36Sopenharmony_ci struct sk_buff *skb) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 164862306a36Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 164962306a36Sopenharmony_ci struct il_station_priv *sta_priv = NULL; 165062306a36Sopenharmony_ci struct il_tx_queue *txq; 165162306a36Sopenharmony_ci struct il_queue *q; 165262306a36Sopenharmony_ci struct il_device_cmd *out_cmd; 165362306a36Sopenharmony_ci struct il_cmd_meta *out_meta; 165462306a36Sopenharmony_ci struct il_tx_cmd *tx_cmd; 165562306a36Sopenharmony_ci int txq_id; 165662306a36Sopenharmony_ci dma_addr_t phys_addr; 165762306a36Sopenharmony_ci dma_addr_t txcmd_phys; 165862306a36Sopenharmony_ci dma_addr_t scratch_phys; 165962306a36Sopenharmony_ci u16 len, firstlen, secondlen; 166062306a36Sopenharmony_ci u16 seq_number = 0; 166162306a36Sopenharmony_ci __le16 fc; 166262306a36Sopenharmony_ci u8 hdr_len; 166362306a36Sopenharmony_ci u8 sta_id; 166462306a36Sopenharmony_ci u8 wait_write_ptr = 0; 166562306a36Sopenharmony_ci u8 tid = 0; 166662306a36Sopenharmony_ci u8 *qc = NULL; 166762306a36Sopenharmony_ci unsigned long flags; 166862306a36Sopenharmony_ci bool is_agg = false; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 167162306a36Sopenharmony_ci if (il_is_rfkill(il)) { 167262306a36Sopenharmony_ci D_DROP("Dropping - RF KILL\n"); 167362306a36Sopenharmony_ci goto drop_unlock; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci fc = hdr->frame_control; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 167962306a36Sopenharmony_ci if (ieee80211_is_auth(fc)) 168062306a36Sopenharmony_ci D_TX("Sending AUTH frame\n"); 168162306a36Sopenharmony_ci else if (ieee80211_is_assoc_req(fc)) 168262306a36Sopenharmony_ci D_TX("Sending ASSOC frame\n"); 168362306a36Sopenharmony_ci else if (ieee80211_is_reassoc_req(fc)) 168462306a36Sopenharmony_ci D_TX("Sending REASSOC frame\n"); 168562306a36Sopenharmony_ci#endif 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci hdr_len = ieee80211_hdrlen(fc); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci /* For management frames use broadcast id to do not break aggregation */ 169062306a36Sopenharmony_ci if (!ieee80211_is_data(fc)) 169162306a36Sopenharmony_ci sta_id = il->hw_params.bcast_id; 169262306a36Sopenharmony_ci else { 169362306a36Sopenharmony_ci /* Find idx into station table for destination station */ 169462306a36Sopenharmony_ci sta_id = il_sta_id_or_broadcast(il, sta); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) { 169762306a36Sopenharmony_ci D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); 169862306a36Sopenharmony_ci goto drop_unlock; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci D_TX("station Id %d\n", sta_id); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci if (sta) 170562306a36Sopenharmony_ci sta_priv = (void *)sta->drv_priv; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci if (sta_priv && sta_priv->asleep && 170862306a36Sopenharmony_ci (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { 170962306a36Sopenharmony_ci /* 171062306a36Sopenharmony_ci * This sends an asynchronous command to the device, 171162306a36Sopenharmony_ci * but we can rely on it being processed before the 171262306a36Sopenharmony_ci * next frame is processed -- and the next frame to 171362306a36Sopenharmony_ci * this station is the one that will consume this 171462306a36Sopenharmony_ci * counter. 171562306a36Sopenharmony_ci * For now set the counter to just 1 since we do not 171662306a36Sopenharmony_ci * support uAPSD yet. 171762306a36Sopenharmony_ci */ 171862306a36Sopenharmony_ci il4965_sta_modify_sleep_tx_count(il, sta_id, 1); 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci /* FIXME: remove me ? */ 172262306a36Sopenharmony_ci WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci /* Access category (AC) is also the queue number */ 172562306a36Sopenharmony_ci txq_id = skb_get_queue_mapping(skb); 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci /* irqs already disabled/saved above when locking il->lock */ 172862306a36Sopenharmony_ci spin_lock(&il->sta_lock); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci if (ieee80211_is_data_qos(fc)) { 173162306a36Sopenharmony_ci qc = ieee80211_get_qos_ctl(hdr); 173262306a36Sopenharmony_ci tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; 173362306a36Sopenharmony_ci if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) { 173462306a36Sopenharmony_ci spin_unlock(&il->sta_lock); 173562306a36Sopenharmony_ci goto drop_unlock; 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci seq_number = il->stations[sta_id].tid[tid].seq_number; 173862306a36Sopenharmony_ci seq_number &= IEEE80211_SCTL_SEQ; 173962306a36Sopenharmony_ci hdr->seq_ctrl = 174062306a36Sopenharmony_ci hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG); 174162306a36Sopenharmony_ci hdr->seq_ctrl |= cpu_to_le16(seq_number); 174262306a36Sopenharmony_ci seq_number += 0x10; 174362306a36Sopenharmony_ci /* aggregation is on for this <sta,tid> */ 174462306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_AMPDU && 174562306a36Sopenharmony_ci il->stations[sta_id].tid[tid].agg.state == IL_AGG_ON) { 174662306a36Sopenharmony_ci txq_id = il->stations[sta_id].tid[tid].agg.txq_id; 174762306a36Sopenharmony_ci is_agg = true; 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci txq = &il->txq[txq_id]; 175262306a36Sopenharmony_ci q = &txq->q; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci if (unlikely(il_queue_space(q) < q->high_mark)) { 175562306a36Sopenharmony_ci spin_unlock(&il->sta_lock); 175662306a36Sopenharmony_ci goto drop_unlock; 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci if (ieee80211_is_data_qos(fc)) { 176062306a36Sopenharmony_ci il->stations[sta_id].tid[tid].tfds_in_queue++; 176162306a36Sopenharmony_ci if (!ieee80211_has_morefrags(fc)) 176262306a36Sopenharmony_ci il->stations[sta_id].tid[tid].seq_number = seq_number; 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci spin_unlock(&il->sta_lock); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci txq->skbs[q->write_ptr] = skb; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci /* Set up first empty entry in queue's array of Tx/cmd buffers */ 177062306a36Sopenharmony_ci out_cmd = txq->cmd[q->write_ptr]; 177162306a36Sopenharmony_ci out_meta = &txq->meta[q->write_ptr]; 177262306a36Sopenharmony_ci tx_cmd = &out_cmd->cmd.tx; 177362306a36Sopenharmony_ci memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); 177462306a36Sopenharmony_ci memset(tx_cmd, 0, sizeof(struct il_tx_cmd)); 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci /* 177762306a36Sopenharmony_ci * Set up the Tx-command (not MAC!) header. 177862306a36Sopenharmony_ci * Store the chosen Tx queue and TFD idx within the sequence field; 177962306a36Sopenharmony_ci * after Tx, uCode's Tx response will return this value so driver can 178062306a36Sopenharmony_ci * locate the frame within the tx queue and do post-tx processing. 178162306a36Sopenharmony_ci */ 178262306a36Sopenharmony_ci out_cmd->hdr.cmd = C_TX; 178362306a36Sopenharmony_ci out_cmd->hdr.sequence = 178462306a36Sopenharmony_ci cpu_to_le16((u16) 178562306a36Sopenharmony_ci (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr))); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci /* Copy MAC header from skb into command buffer */ 178862306a36Sopenharmony_ci memcpy(tx_cmd->hdr, hdr, hdr_len); 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci /* Total # bytes to be transmitted */ 179162306a36Sopenharmony_ci tx_cmd->len = cpu_to_le16((u16) skb->len); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci if (info->control.hw_key) 179462306a36Sopenharmony_ci il4965_tx_cmd_build_hwcrypto(il, info, tx_cmd, skb, sta_id); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci /* TODO need this for burst mode later on */ 179762306a36Sopenharmony_ci il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id); 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci /* 180262306a36Sopenharmony_ci * Use the first empty entry in this queue's command buffer array 180362306a36Sopenharmony_ci * to contain the Tx command and MAC header concatenated together 180462306a36Sopenharmony_ci * (payload data will be in another buffer). 180562306a36Sopenharmony_ci * Size of this varies, due to varying MAC header length. 180662306a36Sopenharmony_ci * If end is not dword aligned, we'll have 2 extra bytes at the end 180762306a36Sopenharmony_ci * of the MAC header (device reads on dword boundaries). 180862306a36Sopenharmony_ci * We'll tell device about this padding later. 180962306a36Sopenharmony_ci */ 181062306a36Sopenharmony_ci len = sizeof(struct il_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len; 181162306a36Sopenharmony_ci firstlen = (len + 3) & ~3; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci /* Tell NIC about any 2-byte padding after MAC header */ 181462306a36Sopenharmony_ci if (firstlen != len) 181562306a36Sopenharmony_ci tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci /* Physical address of this Tx command's header (not MAC header!), 181862306a36Sopenharmony_ci * within command buffer array. */ 181962306a36Sopenharmony_ci txcmd_phys = dma_map_single(&il->pci_dev->dev, &out_cmd->hdr, firstlen, 182062306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 182162306a36Sopenharmony_ci if (unlikely(dma_mapping_error(&il->pci_dev->dev, txcmd_phys))) 182262306a36Sopenharmony_ci goto drop_unlock; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* Set up TFD's 2nd entry to point directly to remainder of skb, 182562306a36Sopenharmony_ci * if any (802.11 null frames have no payload). */ 182662306a36Sopenharmony_ci secondlen = skb->len - hdr_len; 182762306a36Sopenharmony_ci if (secondlen > 0) { 182862306a36Sopenharmony_ci phys_addr = dma_map_single(&il->pci_dev->dev, skb->data + hdr_len, 182962306a36Sopenharmony_ci secondlen, DMA_TO_DEVICE); 183062306a36Sopenharmony_ci if (unlikely(dma_mapping_error(&il->pci_dev->dev, phys_addr))) 183162306a36Sopenharmony_ci goto drop_unlock; 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci /* Add buffer containing Tx command and MAC(!) header to TFD's 183562306a36Sopenharmony_ci * first entry */ 183662306a36Sopenharmony_ci il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0); 183762306a36Sopenharmony_ci dma_unmap_addr_set(out_meta, mapping, txcmd_phys); 183862306a36Sopenharmony_ci dma_unmap_len_set(out_meta, len, firstlen); 183962306a36Sopenharmony_ci if (secondlen) 184062306a36Sopenharmony_ci il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen, 184162306a36Sopenharmony_ci 0, 0); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci if (!ieee80211_has_morefrags(hdr->frame_control)) { 184462306a36Sopenharmony_ci txq->need_update = 1; 184562306a36Sopenharmony_ci } else { 184662306a36Sopenharmony_ci wait_write_ptr = 1; 184762306a36Sopenharmony_ci txq->need_update = 0; 184862306a36Sopenharmony_ci } 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci scratch_phys = 185162306a36Sopenharmony_ci txcmd_phys + sizeof(struct il_cmd_header) + 185262306a36Sopenharmony_ci offsetof(struct il_tx_cmd, scratch); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci /* take back ownership of DMA buffer to enable update */ 185562306a36Sopenharmony_ci dma_sync_single_for_cpu(&il->pci_dev->dev, txcmd_phys, firstlen, 185662306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 185762306a36Sopenharmony_ci tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); 185862306a36Sopenharmony_ci tx_cmd->dram_msb_ptr = il_get_dma_hi_addr(scratch_phys); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci il_update_stats(il, true, fc, skb->len); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence)); 186362306a36Sopenharmony_ci D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); 186462306a36Sopenharmony_ci il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd, sizeof(*tx_cmd)); 186562306a36Sopenharmony_ci il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, hdr_len); 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci /* Set up entry for this TFD in Tx byte-count array */ 186862306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_AMPDU) 186962306a36Sopenharmony_ci il->ops->txq_update_byte_cnt_tbl(il, txq, le16_to_cpu(tx_cmd->len)); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci dma_sync_single_for_device(&il->pci_dev->dev, txcmd_phys, firstlen, 187262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci /* Tell device the write idx *just past* this latest filled TFD */ 187562306a36Sopenharmony_ci q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); 187662306a36Sopenharmony_ci il_txq_update_write_ptr(il, txq); 187762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci /* 188062306a36Sopenharmony_ci * At this point the frame is "transmitted" successfully 188162306a36Sopenharmony_ci * and we will get a TX status notification eventually, 188262306a36Sopenharmony_ci * regardless of the value of ret. "ret" only indicates 188362306a36Sopenharmony_ci * whether or not we should update the write pointer. 188462306a36Sopenharmony_ci */ 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci /* 188762306a36Sopenharmony_ci * Avoid atomic ops if it isn't an associated client. 188862306a36Sopenharmony_ci * Also, if this is a packet for aggregation, don't 188962306a36Sopenharmony_ci * increase the counter because the ucode will stop 189062306a36Sopenharmony_ci * aggregation queues when their respective station 189162306a36Sopenharmony_ci * goes to sleep. 189262306a36Sopenharmony_ci */ 189362306a36Sopenharmony_ci if (sta_priv && sta_priv->client && !is_agg) 189462306a36Sopenharmony_ci atomic_inc(&sta_priv->pending_frames); 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci if (il_queue_space(q) < q->high_mark && il->mac80211_registered) { 189762306a36Sopenharmony_ci if (wait_write_ptr) { 189862306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 189962306a36Sopenharmony_ci txq->need_update = 1; 190062306a36Sopenharmony_ci il_txq_update_write_ptr(il, txq); 190162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 190262306a36Sopenharmony_ci } else { 190362306a36Sopenharmony_ci il_stop_queue(il, txq); 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci } 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci return 0; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_cidrop_unlock: 191062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 191162306a36Sopenharmony_ci return -1; 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic inline int 191562306a36Sopenharmony_ciil4965_alloc_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr, size_t size) 191662306a36Sopenharmony_ci{ 191762306a36Sopenharmony_ci ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma, 191862306a36Sopenharmony_ci GFP_KERNEL); 191962306a36Sopenharmony_ci if (!ptr->addr) 192062306a36Sopenharmony_ci return -ENOMEM; 192162306a36Sopenharmony_ci ptr->size = size; 192262306a36Sopenharmony_ci return 0; 192362306a36Sopenharmony_ci} 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_cistatic inline void 192662306a36Sopenharmony_ciil4965_free_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr) 192762306a36Sopenharmony_ci{ 192862306a36Sopenharmony_ci if (unlikely(!ptr->addr)) 192962306a36Sopenharmony_ci return; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci dma_free_coherent(&il->pci_dev->dev, ptr->size, ptr->addr, ptr->dma); 193262306a36Sopenharmony_ci memset(ptr, 0, sizeof(*ptr)); 193362306a36Sopenharmony_ci} 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci/* 193662306a36Sopenharmony_ci * il4965_hw_txq_ctx_free - Free TXQ Context 193762306a36Sopenharmony_ci * 193862306a36Sopenharmony_ci * Destroy all TX DMA queues and structures 193962306a36Sopenharmony_ci */ 194062306a36Sopenharmony_civoid 194162306a36Sopenharmony_ciil4965_hw_txq_ctx_free(struct il_priv *il) 194262306a36Sopenharmony_ci{ 194362306a36Sopenharmony_ci int txq_id; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* Tx queues */ 194662306a36Sopenharmony_ci if (il->txq) { 194762306a36Sopenharmony_ci for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) 194862306a36Sopenharmony_ci if (txq_id == il->cmd_queue) 194962306a36Sopenharmony_ci il_cmd_queue_free(il); 195062306a36Sopenharmony_ci else 195162306a36Sopenharmony_ci il_tx_queue_free(il, txq_id); 195262306a36Sopenharmony_ci } 195362306a36Sopenharmony_ci il4965_free_dma_ptr(il, &il->kw); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci il4965_free_dma_ptr(il, &il->scd_bc_tbls); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci /* free tx queue structure */ 195862306a36Sopenharmony_ci il_free_txq_mem(il); 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci/* 196262306a36Sopenharmony_ci * il4965_txq_ctx_alloc - allocate TX queue context 196362306a36Sopenharmony_ci * Allocate all Tx DMA structures and initialize them 196462306a36Sopenharmony_ci */ 196562306a36Sopenharmony_ciint 196662306a36Sopenharmony_ciil4965_txq_ctx_alloc(struct il_priv *il) 196762306a36Sopenharmony_ci{ 196862306a36Sopenharmony_ci int ret, txq_id; 196962306a36Sopenharmony_ci unsigned long flags; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci /* Free all tx/cmd queues and keep-warm buffer */ 197262306a36Sopenharmony_ci il4965_hw_txq_ctx_free(il); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci ret = 197562306a36Sopenharmony_ci il4965_alloc_dma_ptr(il, &il->scd_bc_tbls, 197662306a36Sopenharmony_ci il->hw_params.scd_bc_tbls_size); 197762306a36Sopenharmony_ci if (ret) { 197862306a36Sopenharmony_ci IL_ERR("Scheduler BC Table allocation failed\n"); 197962306a36Sopenharmony_ci goto error_bc_tbls; 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci /* Alloc keep-warm buffer */ 198262306a36Sopenharmony_ci ret = il4965_alloc_dma_ptr(il, &il->kw, IL_KW_SIZE); 198362306a36Sopenharmony_ci if (ret) { 198462306a36Sopenharmony_ci IL_ERR("Keep Warm allocation failed\n"); 198562306a36Sopenharmony_ci goto error_kw; 198662306a36Sopenharmony_ci } 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci /* allocate tx queue structure */ 198962306a36Sopenharmony_ci ret = il_alloc_txq_mem(il); 199062306a36Sopenharmony_ci if (ret) 199162306a36Sopenharmony_ci goto error; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci /* Turn off all Tx DMA fifos */ 199662306a36Sopenharmony_ci il4965_txq_set_sched(il, 0); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci /* Tell NIC where to find the "keep warm" buffer */ 199962306a36Sopenharmony_ci il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci /* Alloc and init all Tx queues, including the command queue (#4/#9) */ 200462306a36Sopenharmony_ci for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { 200562306a36Sopenharmony_ci ret = il_tx_queue_init(il, txq_id); 200662306a36Sopenharmony_ci if (ret) { 200762306a36Sopenharmony_ci IL_ERR("Tx %d queue init failed\n", txq_id); 200862306a36Sopenharmony_ci goto error; 200962306a36Sopenharmony_ci } 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci return ret; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_cierror: 201562306a36Sopenharmony_ci il4965_hw_txq_ctx_free(il); 201662306a36Sopenharmony_ci il4965_free_dma_ptr(il, &il->kw); 201762306a36Sopenharmony_cierror_kw: 201862306a36Sopenharmony_ci il4965_free_dma_ptr(il, &il->scd_bc_tbls); 201962306a36Sopenharmony_cierror_bc_tbls: 202062306a36Sopenharmony_ci return ret; 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_civoid 202462306a36Sopenharmony_ciil4965_txq_ctx_reset(struct il_priv *il) 202562306a36Sopenharmony_ci{ 202662306a36Sopenharmony_ci int txq_id; 202762306a36Sopenharmony_ci unsigned long flags; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci /* Turn off all Tx DMA fifos */ 203262306a36Sopenharmony_ci il4965_txq_set_sched(il, 0); 203362306a36Sopenharmony_ci /* Tell NIC where to find the "keep warm" buffer */ 203462306a36Sopenharmony_ci il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4); 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci /* Alloc and init all Tx queues, including the command queue (#4) */ 203962306a36Sopenharmony_ci for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) 204062306a36Sopenharmony_ci il_tx_queue_reset(il, txq_id); 204162306a36Sopenharmony_ci} 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_cistatic void 204462306a36Sopenharmony_ciil4965_txq_ctx_unmap(struct il_priv *il) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci int txq_id; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci if (!il->txq) 204962306a36Sopenharmony_ci return; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci /* Unmap DMA from host system and free skb's */ 205262306a36Sopenharmony_ci for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) 205362306a36Sopenharmony_ci if (txq_id == il->cmd_queue) 205462306a36Sopenharmony_ci il_cmd_queue_unmap(il); 205562306a36Sopenharmony_ci else 205662306a36Sopenharmony_ci il_tx_queue_unmap(il, txq_id); 205762306a36Sopenharmony_ci} 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci/* 206062306a36Sopenharmony_ci * il4965_txq_ctx_stop - Stop all Tx DMA channels 206162306a36Sopenharmony_ci */ 206262306a36Sopenharmony_civoid 206362306a36Sopenharmony_ciil4965_txq_ctx_stop(struct il_priv *il) 206462306a36Sopenharmony_ci{ 206562306a36Sopenharmony_ci int ch, ret; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci _il_wr_prph(il, IL49_SCD_TXFACT, 0); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci /* Stop each Tx DMA channel, and wait for it to be idle */ 207062306a36Sopenharmony_ci for (ch = 0; ch < il->hw_params.dma_chnl_num; ch++) { 207162306a36Sopenharmony_ci _il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); 207262306a36Sopenharmony_ci ret = 207362306a36Sopenharmony_ci _il_poll_bit(il, FH49_TSSR_TX_STATUS_REG, 207462306a36Sopenharmony_ci FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 207562306a36Sopenharmony_ci FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 207662306a36Sopenharmony_ci 1000); 207762306a36Sopenharmony_ci if (ret < 0) 207862306a36Sopenharmony_ci IL_ERR("Timeout stopping DMA channel %d [0x%08x]", 207962306a36Sopenharmony_ci ch, _il_rd(il, FH49_TSSR_TX_STATUS_REG)); 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci} 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci/* 208462306a36Sopenharmony_ci * Find first available (lowest unused) Tx Queue, mark it "active". 208562306a36Sopenharmony_ci * Called only when finding queue for aggregation. 208662306a36Sopenharmony_ci * Should never return anything < 7, because they should already 208762306a36Sopenharmony_ci * be in use as EDCA AC (0-3), Command (4), reserved (5, 6) 208862306a36Sopenharmony_ci */ 208962306a36Sopenharmony_cistatic int 209062306a36Sopenharmony_ciil4965_txq_ctx_activate_free(struct il_priv *il) 209162306a36Sopenharmony_ci{ 209262306a36Sopenharmony_ci int txq_id; 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) 209562306a36Sopenharmony_ci if (!test_and_set_bit(txq_id, &il->txq_ctx_active_msk)) 209662306a36Sopenharmony_ci return txq_id; 209762306a36Sopenharmony_ci return -1; 209862306a36Sopenharmony_ci} 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci/* 210162306a36Sopenharmony_ci * il4965_tx_queue_stop_scheduler - Stop queue, but keep configuration 210262306a36Sopenharmony_ci */ 210362306a36Sopenharmony_cistatic void 210462306a36Sopenharmony_ciil4965_tx_queue_stop_scheduler(struct il_priv *il, u16 txq_id) 210562306a36Sopenharmony_ci{ 210662306a36Sopenharmony_ci /* Simply stop the queue, but don't change any configuration; 210762306a36Sopenharmony_ci * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ 210862306a36Sopenharmony_ci il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id), 210962306a36Sopenharmony_ci (0 << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 211062306a36Sopenharmony_ci (1 << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); 211162306a36Sopenharmony_ci} 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci/* 211462306a36Sopenharmony_ci * il4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue 211562306a36Sopenharmony_ci */ 211662306a36Sopenharmony_cistatic int 211762306a36Sopenharmony_ciil4965_tx_queue_set_q2ratid(struct il_priv *il, u16 ra_tid, u16 txq_id) 211862306a36Sopenharmony_ci{ 211962306a36Sopenharmony_ci u32 tbl_dw_addr; 212062306a36Sopenharmony_ci u32 tbl_dw; 212162306a36Sopenharmony_ci u16 scd_q2ratid; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci scd_q2ratid = ra_tid & IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci tbl_dw_addr = 212662306a36Sopenharmony_ci il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci tbl_dw = il_read_targ_mem(il, tbl_dw_addr); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci if (txq_id & 0x1) 213162306a36Sopenharmony_ci tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); 213262306a36Sopenharmony_ci else 213362306a36Sopenharmony_ci tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci il_write_targ_mem(il, tbl_dw_addr, tbl_dw); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci return 0; 213862306a36Sopenharmony_ci} 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci/* 214162306a36Sopenharmony_ci * il4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue 214262306a36Sopenharmony_ci * 214362306a36Sopenharmony_ci * NOTE: txq_id must be greater than IL49_FIRST_AMPDU_QUEUE, 214462306a36Sopenharmony_ci * i.e. it must be one of the higher queues used for aggregation 214562306a36Sopenharmony_ci */ 214662306a36Sopenharmony_cistatic int 214762306a36Sopenharmony_ciil4965_txq_agg_enable(struct il_priv *il, int txq_id, int tx_fifo, int sta_id, 214862306a36Sopenharmony_ci int tid, u16 ssn_idx) 214962306a36Sopenharmony_ci{ 215062306a36Sopenharmony_ci unsigned long flags; 215162306a36Sopenharmony_ci u16 ra_tid; 215262306a36Sopenharmony_ci int ret; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci if ((IL49_FIRST_AMPDU_QUEUE > txq_id) || 215562306a36Sopenharmony_ci (IL49_FIRST_AMPDU_QUEUE + 215662306a36Sopenharmony_ci il->cfg->num_of_ampdu_queues <= txq_id)) { 215762306a36Sopenharmony_ci IL_WARN("queue number out of range: %d, must be %d to %d\n", 215862306a36Sopenharmony_ci txq_id, IL49_FIRST_AMPDU_QUEUE, 215962306a36Sopenharmony_ci IL49_FIRST_AMPDU_QUEUE + 216062306a36Sopenharmony_ci il->cfg->num_of_ampdu_queues - 1); 216162306a36Sopenharmony_ci return -EINVAL; 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci ra_tid = BUILD_RAxTID(sta_id, tid); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci /* Modify device's station table to Tx this TID */ 216762306a36Sopenharmony_ci ret = il4965_sta_tx_modify_enable_tid(il, sta_id, tid); 216862306a36Sopenharmony_ci if (ret) 216962306a36Sopenharmony_ci return ret; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci /* Stop this Tx queue before configuring it */ 217462306a36Sopenharmony_ci il4965_tx_queue_stop_scheduler(il, txq_id); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci /* Map receiver-address / traffic-ID to this queue */ 217762306a36Sopenharmony_ci il4965_tx_queue_set_q2ratid(il, ra_tid, txq_id); 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci /* Set this queue as a chain-building queue */ 218062306a36Sopenharmony_ci il_set_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci /* Place first TFD at idx corresponding to start sequence number. 218362306a36Sopenharmony_ci * Assumes that ssn_idx is valid (!= 0xFFF) */ 218462306a36Sopenharmony_ci il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); 218562306a36Sopenharmony_ci il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); 218662306a36Sopenharmony_ci il4965_set_wr_ptrs(il, txq_id, ssn_idx); 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci /* Set up Tx win size and frame limit for this queue */ 218962306a36Sopenharmony_ci il_write_targ_mem(il, 219062306a36Sopenharmony_ci il->scd_base_addr + 219162306a36Sopenharmony_ci IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id), 219262306a36Sopenharmony_ci (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) 219362306a36Sopenharmony_ci & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci il_write_targ_mem(il, 219662306a36Sopenharmony_ci il->scd_base_addr + 219762306a36Sopenharmony_ci IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), 219862306a36Sopenharmony_ci (SCD_FRAME_LIMIT << 219962306a36Sopenharmony_ci IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & 220062306a36Sopenharmony_ci IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci il_set_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id)); 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ 220562306a36Sopenharmony_ci il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 1); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci return 0; 221062306a36Sopenharmony_ci} 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ciint 221362306a36Sopenharmony_ciil4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, 221462306a36Sopenharmony_ci struct ieee80211_sta *sta, u16 tid, u16 * ssn) 221562306a36Sopenharmony_ci{ 221662306a36Sopenharmony_ci int sta_id; 221762306a36Sopenharmony_ci int tx_fifo; 221862306a36Sopenharmony_ci int txq_id; 221962306a36Sopenharmony_ci int ret; 222062306a36Sopenharmony_ci unsigned long flags; 222162306a36Sopenharmony_ci struct il_tid_data *tid_data; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci /* FIXME: warning if tx fifo not found ? */ 222462306a36Sopenharmony_ci tx_fifo = il4965_get_fifo_from_tid(tid); 222562306a36Sopenharmony_ci if (unlikely(tx_fifo < 0)) 222662306a36Sopenharmony_ci return tx_fifo; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci D_HT("%s on ra = %pM tid = %d\n", __func__, sta->addr, tid); 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci sta_id = il_sta_id(sta); 223162306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) { 223262306a36Sopenharmony_ci IL_ERR("Start AGG on invalid station\n"); 223362306a36Sopenharmony_ci return -ENXIO; 223462306a36Sopenharmony_ci } 223562306a36Sopenharmony_ci if (unlikely(tid >= MAX_TID_COUNT)) 223662306a36Sopenharmony_ci return -EINVAL; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci if (il->stations[sta_id].tid[tid].agg.state != IL_AGG_OFF) { 223962306a36Sopenharmony_ci IL_ERR("Start AGG when state is not IL_AGG_OFF !\n"); 224062306a36Sopenharmony_ci return -ENXIO; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci txq_id = il4965_txq_ctx_activate_free(il); 224462306a36Sopenharmony_ci if (txq_id == -1) { 224562306a36Sopenharmony_ci IL_ERR("No free aggregation queue available\n"); 224662306a36Sopenharmony_ci return -ENXIO; 224762306a36Sopenharmony_ci } 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 225062306a36Sopenharmony_ci tid_data = &il->stations[sta_id].tid[tid]; 225162306a36Sopenharmony_ci *ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); 225262306a36Sopenharmony_ci tid_data->agg.txq_id = txq_id; 225362306a36Sopenharmony_ci il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id); 225462306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci ret = il4965_txq_agg_enable(il, txq_id, tx_fifo, sta_id, tid, *ssn); 225762306a36Sopenharmony_ci if (ret) 225862306a36Sopenharmony_ci return ret; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 226162306a36Sopenharmony_ci tid_data = &il->stations[sta_id].tid[tid]; 226262306a36Sopenharmony_ci if (tid_data->tfds_in_queue == 0) { 226362306a36Sopenharmony_ci D_HT("HW queue is empty\n"); 226462306a36Sopenharmony_ci tid_data->agg.state = IL_AGG_ON; 226562306a36Sopenharmony_ci ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; 226662306a36Sopenharmony_ci } else { 226762306a36Sopenharmony_ci D_HT("HW queue is NOT empty: %d packets in HW queue\n", 226862306a36Sopenharmony_ci tid_data->tfds_in_queue); 226962306a36Sopenharmony_ci tid_data->agg.state = IL_EMPTYING_HW_QUEUE_ADDBA; 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 227262306a36Sopenharmony_ci return ret; 227362306a36Sopenharmony_ci} 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci/* 227662306a36Sopenharmony_ci * txq_id must be greater than IL49_FIRST_AMPDU_QUEUE 227762306a36Sopenharmony_ci * il->lock must be held by the caller 227862306a36Sopenharmony_ci */ 227962306a36Sopenharmony_cistatic int 228062306a36Sopenharmony_ciil4965_txq_agg_disable(struct il_priv *il, u16 txq_id, u16 ssn_idx, u8 tx_fifo) 228162306a36Sopenharmony_ci{ 228262306a36Sopenharmony_ci if ((IL49_FIRST_AMPDU_QUEUE > txq_id) || 228362306a36Sopenharmony_ci (IL49_FIRST_AMPDU_QUEUE + 228462306a36Sopenharmony_ci il->cfg->num_of_ampdu_queues <= txq_id)) { 228562306a36Sopenharmony_ci IL_WARN("queue number out of range: %d, must be %d to %d\n", 228662306a36Sopenharmony_ci txq_id, IL49_FIRST_AMPDU_QUEUE, 228762306a36Sopenharmony_ci IL49_FIRST_AMPDU_QUEUE + 228862306a36Sopenharmony_ci il->cfg->num_of_ampdu_queues - 1); 228962306a36Sopenharmony_ci return -EINVAL; 229062306a36Sopenharmony_ci } 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci il4965_tx_queue_stop_scheduler(il, txq_id); 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci il_clear_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); 229762306a36Sopenharmony_ci il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); 229862306a36Sopenharmony_ci /* supposes that ssn_idx is valid (!= 0xFFF) */ 229962306a36Sopenharmony_ci il4965_set_wr_ptrs(il, txq_id, ssn_idx); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci il_clear_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id)); 230262306a36Sopenharmony_ci il_txq_ctx_deactivate(il, txq_id); 230362306a36Sopenharmony_ci il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 0); 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci return 0; 230662306a36Sopenharmony_ci} 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ciint 230962306a36Sopenharmony_ciil4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif, 231062306a36Sopenharmony_ci struct ieee80211_sta *sta, u16 tid) 231162306a36Sopenharmony_ci{ 231262306a36Sopenharmony_ci int tx_fifo_id, txq_id, sta_id, ssn; 231362306a36Sopenharmony_ci struct il_tid_data *tid_data; 231462306a36Sopenharmony_ci int write_ptr, read_ptr; 231562306a36Sopenharmony_ci unsigned long flags; 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci /* FIXME: warning if tx_fifo_id not found ? */ 231862306a36Sopenharmony_ci tx_fifo_id = il4965_get_fifo_from_tid(tid); 231962306a36Sopenharmony_ci if (unlikely(tx_fifo_id < 0)) 232062306a36Sopenharmony_ci return tx_fifo_id; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci sta_id = il_sta_id(sta); 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) { 232562306a36Sopenharmony_ci IL_ERR("Invalid station for AGG tid %d\n", tid); 232662306a36Sopenharmony_ci return -ENXIO; 232762306a36Sopenharmony_ci } 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci tid_data = &il->stations[sta_id].tid[tid]; 233262306a36Sopenharmony_ci ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; 233362306a36Sopenharmony_ci txq_id = tid_data->agg.txq_id; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci switch (il->stations[sta_id].tid[tid].agg.state) { 233662306a36Sopenharmony_ci case IL_EMPTYING_HW_QUEUE_ADDBA: 233762306a36Sopenharmony_ci /* 233862306a36Sopenharmony_ci * This can happen if the peer stops aggregation 233962306a36Sopenharmony_ci * again before we've had a chance to drain the 234062306a36Sopenharmony_ci * queue we selected previously, i.e. before the 234162306a36Sopenharmony_ci * session was really started completely. 234262306a36Sopenharmony_ci */ 234362306a36Sopenharmony_ci D_HT("AGG stop before setup done\n"); 234462306a36Sopenharmony_ci goto turn_off; 234562306a36Sopenharmony_ci case IL_AGG_ON: 234662306a36Sopenharmony_ci break; 234762306a36Sopenharmony_ci default: 234862306a36Sopenharmony_ci IL_WARN("Stopping AGG while state not ON or starting\n"); 234962306a36Sopenharmony_ci } 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci write_ptr = il->txq[txq_id].q.write_ptr; 235262306a36Sopenharmony_ci read_ptr = il->txq[txq_id].q.read_ptr; 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci /* The queue is not empty */ 235562306a36Sopenharmony_ci if (write_ptr != read_ptr) { 235662306a36Sopenharmony_ci D_HT("Stopping a non empty AGG HW QUEUE\n"); 235762306a36Sopenharmony_ci il->stations[sta_id].tid[tid].agg.state = 235862306a36Sopenharmony_ci IL_EMPTYING_HW_QUEUE_DELBA; 235962306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 236062306a36Sopenharmony_ci return 0; 236162306a36Sopenharmony_ci } 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci D_HT("HW queue is empty\n"); 236462306a36Sopenharmony_citurn_off: 236562306a36Sopenharmony_ci il->stations[sta_id].tid[tid].agg.state = IL_AGG_OFF; 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci /* do not restore/save irqs */ 236862306a36Sopenharmony_ci spin_unlock(&il->sta_lock); 236962306a36Sopenharmony_ci spin_lock(&il->lock); 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci /* 237262306a36Sopenharmony_ci * the only reason this call can fail is queue number out of range, 237362306a36Sopenharmony_ci * which can happen if uCode is reloaded and all the station 237462306a36Sopenharmony_ci * information are lost. if it is outside the range, there is no need 237562306a36Sopenharmony_ci * to deactivate the uCode queue, just return "success" to allow 237662306a36Sopenharmony_ci * mac80211 to clean up it own data. 237762306a36Sopenharmony_ci */ 237862306a36Sopenharmony_ci il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo_id); 237962306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci return 0; 238462306a36Sopenharmony_ci} 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ciint 238762306a36Sopenharmony_ciil4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id) 238862306a36Sopenharmony_ci{ 238962306a36Sopenharmony_ci struct il_queue *q = &il->txq[txq_id].q; 239062306a36Sopenharmony_ci u8 *addr = il->stations[sta_id].sta.sta.addr; 239162306a36Sopenharmony_ci struct il_tid_data *tid_data = &il->stations[sta_id].tid[tid]; 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci lockdep_assert_held(&il->sta_lock); 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci switch (il->stations[sta_id].tid[tid].agg.state) { 239662306a36Sopenharmony_ci case IL_EMPTYING_HW_QUEUE_DELBA: 239762306a36Sopenharmony_ci /* We are reclaiming the last packet of the */ 239862306a36Sopenharmony_ci /* aggregated HW queue */ 239962306a36Sopenharmony_ci if (txq_id == tid_data->agg.txq_id && 240062306a36Sopenharmony_ci q->read_ptr == q->write_ptr) { 240162306a36Sopenharmony_ci u16 ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); 240262306a36Sopenharmony_ci int tx_fifo = il4965_get_fifo_from_tid(tid); 240362306a36Sopenharmony_ci D_HT("HW queue empty: continue DELBA flow\n"); 240462306a36Sopenharmony_ci il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo); 240562306a36Sopenharmony_ci tid_data->agg.state = IL_AGG_OFF; 240662306a36Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(il->vif, addr, tid); 240762306a36Sopenharmony_ci } 240862306a36Sopenharmony_ci break; 240962306a36Sopenharmony_ci case IL_EMPTYING_HW_QUEUE_ADDBA: 241062306a36Sopenharmony_ci /* We are reclaiming the last packet of the queue */ 241162306a36Sopenharmony_ci if (tid_data->tfds_in_queue == 0) { 241262306a36Sopenharmony_ci D_HT("HW queue empty: continue ADDBA flow\n"); 241362306a36Sopenharmony_ci tid_data->agg.state = IL_AGG_ON; 241462306a36Sopenharmony_ci ieee80211_start_tx_ba_cb_irqsafe(il->vif, addr, tid); 241562306a36Sopenharmony_ci } 241662306a36Sopenharmony_ci break; 241762306a36Sopenharmony_ci } 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci return 0; 242062306a36Sopenharmony_ci} 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_cistatic void 242362306a36Sopenharmony_ciil4965_non_agg_tx_status(struct il_priv *il, const u8 *addr1) 242462306a36Sopenharmony_ci{ 242562306a36Sopenharmony_ci struct ieee80211_sta *sta; 242662306a36Sopenharmony_ci struct il_station_priv *sta_priv; 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci rcu_read_lock(); 242962306a36Sopenharmony_ci sta = ieee80211_find_sta(il->vif, addr1); 243062306a36Sopenharmony_ci if (sta) { 243162306a36Sopenharmony_ci sta_priv = (void *)sta->drv_priv; 243262306a36Sopenharmony_ci /* avoid atomic ops if this isn't a client */ 243362306a36Sopenharmony_ci if (sta_priv->client && 243462306a36Sopenharmony_ci atomic_dec_return(&sta_priv->pending_frames) == 0) 243562306a36Sopenharmony_ci ieee80211_sta_block_awake(il->hw, sta, false); 243662306a36Sopenharmony_ci } 243762306a36Sopenharmony_ci rcu_read_unlock(); 243862306a36Sopenharmony_ci} 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_cistatic void 244162306a36Sopenharmony_ciil4965_tx_status(struct il_priv *il, struct sk_buff *skb, bool is_agg) 244262306a36Sopenharmony_ci{ 244362306a36Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci if (!is_agg) 244662306a36Sopenharmony_ci il4965_non_agg_tx_status(il, hdr->addr1); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci ieee80211_tx_status_irqsafe(il->hw, skb); 244962306a36Sopenharmony_ci} 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ciint 245262306a36Sopenharmony_ciil4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx) 245362306a36Sopenharmony_ci{ 245462306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 245562306a36Sopenharmony_ci struct il_queue *q = &txq->q; 245662306a36Sopenharmony_ci int nfreed = 0; 245762306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 245862306a36Sopenharmony_ci struct sk_buff *skb; 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { 246162306a36Sopenharmony_ci IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " 246262306a36Sopenharmony_ci "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, 246362306a36Sopenharmony_ci q->write_ptr, q->read_ptr); 246462306a36Sopenharmony_ci return 0; 246562306a36Sopenharmony_ci } 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; 246862306a36Sopenharmony_ci q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci skb = txq->skbs[txq->q.read_ptr]; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci if (WARN_ON_ONCE(skb == NULL)) 247362306a36Sopenharmony_ci continue; 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *) skb->data; 247662306a36Sopenharmony_ci if (ieee80211_is_data_qos(hdr->frame_control)) 247762306a36Sopenharmony_ci nfreed++; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci il4965_tx_status(il, skb, txq_id >= IL4965_FIRST_AMPDU_QUEUE); 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci txq->skbs[txq->q.read_ptr] = NULL; 248262306a36Sopenharmony_ci il->ops->txq_free_tfd(il, txq); 248362306a36Sopenharmony_ci } 248462306a36Sopenharmony_ci return nfreed; 248562306a36Sopenharmony_ci} 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci/* 248862306a36Sopenharmony_ci * il4965_tx_status_reply_compressed_ba - Update tx status from block-ack 248962306a36Sopenharmony_ci * 249062306a36Sopenharmony_ci * Go through block-ack's bitmap of ACK'd frames, update driver's record of 249162306a36Sopenharmony_ci * ACK vs. not. This gets sent to mac80211, then to rate scaling algo. 249262306a36Sopenharmony_ci */ 249362306a36Sopenharmony_cistatic int 249462306a36Sopenharmony_ciil4965_tx_status_reply_compressed_ba(struct il_priv *il, struct il_ht_agg *agg, 249562306a36Sopenharmony_ci struct il_compressed_ba_resp *ba_resp) 249662306a36Sopenharmony_ci{ 249762306a36Sopenharmony_ci int i, sh, ack; 249862306a36Sopenharmony_ci u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); 249962306a36Sopenharmony_ci u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); 250062306a36Sopenharmony_ci int successes = 0; 250162306a36Sopenharmony_ci struct ieee80211_tx_info *info; 250262306a36Sopenharmony_ci u64 bitmap, sent_bitmap; 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci if (unlikely(!agg->wait_for_ba)) { 250562306a36Sopenharmony_ci if (unlikely(ba_resp->bitmap)) 250662306a36Sopenharmony_ci IL_ERR("Received BA when not expected\n"); 250762306a36Sopenharmony_ci return -EINVAL; 250862306a36Sopenharmony_ci } 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci /* Mark that the expected block-ack response arrived */ 251162306a36Sopenharmony_ci agg->wait_for_ba = 0; 251262306a36Sopenharmony_ci D_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl); 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci /* Calculate shift to align block-ack bits with our Tx win bits */ 251562306a36Sopenharmony_ci sh = agg->start_idx - SEQ_TO_IDX(seq_ctl >> 4); 251662306a36Sopenharmony_ci if (sh < 0) /* tbw something is wrong with indices */ 251762306a36Sopenharmony_ci sh += 0x100; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci if (agg->frame_count > (64 - sh)) { 252062306a36Sopenharmony_ci D_TX_REPLY("more frames than bitmap size"); 252162306a36Sopenharmony_ci return -1; 252262306a36Sopenharmony_ci } 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci /* don't use 64-bit values for now */ 252562306a36Sopenharmony_ci bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci /* check for success or failure according to the 252862306a36Sopenharmony_ci * transmitted bitmap and block-ack bitmap */ 252962306a36Sopenharmony_ci sent_bitmap = bitmap & agg->bitmap; 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci /* For each frame attempted in aggregation, 253262306a36Sopenharmony_ci * update driver's record of tx frame's status. */ 253362306a36Sopenharmony_ci i = 0; 253462306a36Sopenharmony_ci while (sent_bitmap) { 253562306a36Sopenharmony_ci ack = sent_bitmap & 1ULL; 253662306a36Sopenharmony_ci successes += ack; 253762306a36Sopenharmony_ci D_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", ack ? "ACK" : "NACK", 253862306a36Sopenharmony_ci i, (agg->start_idx + i) & 0xff, agg->start_idx + i); 253962306a36Sopenharmony_ci sent_bitmap >>= 1; 254062306a36Sopenharmony_ci ++i; 254162306a36Sopenharmony_ci } 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci D_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap); 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci info = IEEE80211_SKB_CB(il->txq[scd_flow].skbs[agg->start_idx]); 254662306a36Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 254762306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 254862306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_AMPDU; 254962306a36Sopenharmony_ci info->status.ampdu_ack_len = successes; 255062306a36Sopenharmony_ci info->status.ampdu_len = agg->frame_count; 255162306a36Sopenharmony_ci il4965_hwrate_to_tx_control(il, agg->rate_n_flags, info); 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci return 0; 255462306a36Sopenharmony_ci} 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_cistatic inline bool 255762306a36Sopenharmony_ciil4965_is_tx_success(u32 status) 255862306a36Sopenharmony_ci{ 255962306a36Sopenharmony_ci status &= TX_STATUS_MSK; 256062306a36Sopenharmony_ci return (status == TX_STATUS_SUCCESS || status == TX_STATUS_DIRECT_DONE); 256162306a36Sopenharmony_ci} 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_cistatic u8 256462306a36Sopenharmony_ciil4965_find_station(struct il_priv *il, const u8 *addr) 256562306a36Sopenharmony_ci{ 256662306a36Sopenharmony_ci int i; 256762306a36Sopenharmony_ci int start = 0; 256862306a36Sopenharmony_ci int ret = IL_INVALID_STATION; 256962306a36Sopenharmony_ci unsigned long flags; 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci if (il->iw_mode == NL80211_IFTYPE_ADHOC) 257262306a36Sopenharmony_ci start = IL_STA_ID; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci if (is_broadcast_ether_addr(addr)) 257562306a36Sopenharmony_ci return il->hw_params.bcast_id; 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 257862306a36Sopenharmony_ci for (i = start; i < il->hw_params.max_stations; i++) 257962306a36Sopenharmony_ci if (il->stations[i].used && 258062306a36Sopenharmony_ci ether_addr_equal(il->stations[i].sta.sta.addr, addr)) { 258162306a36Sopenharmony_ci ret = i; 258262306a36Sopenharmony_ci goto out; 258362306a36Sopenharmony_ci } 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci D_ASSOC("can not find STA %pM total %d\n", addr, il->num_stations); 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ciout: 258862306a36Sopenharmony_ci /* 258962306a36Sopenharmony_ci * It may be possible that more commands interacting with stations 259062306a36Sopenharmony_ci * arrive before we completed processing the adding of 259162306a36Sopenharmony_ci * station 259262306a36Sopenharmony_ci */ 259362306a36Sopenharmony_ci if (ret != IL_INVALID_STATION && 259462306a36Sopenharmony_ci (!(il->stations[ret].used & IL_STA_UCODE_ACTIVE) || 259562306a36Sopenharmony_ci (il->stations[ret].used & IL_STA_UCODE_INPROGRESS))) { 259662306a36Sopenharmony_ci IL_ERR("Requested station info for sta %d before ready.\n", 259762306a36Sopenharmony_ci ret); 259862306a36Sopenharmony_ci ret = IL_INVALID_STATION; 259962306a36Sopenharmony_ci } 260062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 260162306a36Sopenharmony_ci return ret; 260262306a36Sopenharmony_ci} 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_cistatic int 260562306a36Sopenharmony_ciil4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr) 260662306a36Sopenharmony_ci{ 260762306a36Sopenharmony_ci if (il->iw_mode == NL80211_IFTYPE_STATION) 260862306a36Sopenharmony_ci return IL_AP_ID; 260962306a36Sopenharmony_ci else { 261062306a36Sopenharmony_ci u8 *da = ieee80211_get_DA(hdr); 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci return il4965_find_station(il, da); 261362306a36Sopenharmony_ci } 261462306a36Sopenharmony_ci} 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_cistatic inline u32 261762306a36Sopenharmony_ciil4965_get_scd_ssn(struct il4965_tx_resp *tx_resp) 261862306a36Sopenharmony_ci{ 261962306a36Sopenharmony_ci return le32_to_cpup(&tx_resp->u.status + 262062306a36Sopenharmony_ci tx_resp->frame_count) & IEEE80211_MAX_SN; 262162306a36Sopenharmony_ci} 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_cistatic inline u32 262462306a36Sopenharmony_ciil4965_tx_status_to_mac80211(u32 status) 262562306a36Sopenharmony_ci{ 262662306a36Sopenharmony_ci status &= TX_STATUS_MSK; 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci switch (status) { 262962306a36Sopenharmony_ci case TX_STATUS_SUCCESS: 263062306a36Sopenharmony_ci case TX_STATUS_DIRECT_DONE: 263162306a36Sopenharmony_ci return IEEE80211_TX_STAT_ACK; 263262306a36Sopenharmony_ci case TX_STATUS_FAIL_DEST_PS: 263362306a36Sopenharmony_ci return IEEE80211_TX_STAT_TX_FILTERED; 263462306a36Sopenharmony_ci default: 263562306a36Sopenharmony_ci return 0; 263662306a36Sopenharmony_ci } 263762306a36Sopenharmony_ci} 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci/* 264062306a36Sopenharmony_ci * il4965_tx_status_reply_tx - Handle Tx response for frames in aggregation queue 264162306a36Sopenharmony_ci */ 264262306a36Sopenharmony_cistatic int 264362306a36Sopenharmony_ciil4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg, 264462306a36Sopenharmony_ci struct il4965_tx_resp *tx_resp, int txq_id, 264562306a36Sopenharmony_ci u16 start_idx) 264662306a36Sopenharmony_ci{ 264762306a36Sopenharmony_ci u16 status; 264862306a36Sopenharmony_ci struct agg_tx_status *frame_status = tx_resp->u.agg_status; 264962306a36Sopenharmony_ci struct ieee80211_tx_info *info = NULL; 265062306a36Sopenharmony_ci struct ieee80211_hdr *hdr = NULL; 265162306a36Sopenharmony_ci u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); 265262306a36Sopenharmony_ci int i, sh, idx; 265362306a36Sopenharmony_ci u16 seq; 265462306a36Sopenharmony_ci if (agg->wait_for_ba) 265562306a36Sopenharmony_ci D_TX_REPLY("got tx response w/o block-ack\n"); 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci agg->frame_count = tx_resp->frame_count; 265862306a36Sopenharmony_ci agg->start_idx = start_idx; 265962306a36Sopenharmony_ci agg->rate_n_flags = rate_n_flags; 266062306a36Sopenharmony_ci agg->bitmap = 0; 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci /* num frames attempted by Tx command */ 266362306a36Sopenharmony_ci if (agg->frame_count == 1) { 266462306a36Sopenharmony_ci /* Only one frame was attempted; no block-ack will arrive */ 266562306a36Sopenharmony_ci status = le16_to_cpu(frame_status[0].status); 266662306a36Sopenharmony_ci idx = start_idx; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci D_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n", 266962306a36Sopenharmony_ci agg->frame_count, agg->start_idx, idx); 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci info = IEEE80211_SKB_CB(il->txq[txq_id].skbs[idx]); 267262306a36Sopenharmony_ci info->status.rates[0].count = tx_resp->failure_frame + 1; 267362306a36Sopenharmony_ci info->flags &= ~IEEE80211_TX_CTL_AMPDU; 267462306a36Sopenharmony_ci info->flags |= il4965_tx_status_to_mac80211(status); 267562306a36Sopenharmony_ci il4965_hwrate_to_tx_control(il, rate_n_flags, info); 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci D_TX_REPLY("1 Frame 0x%x failure :%d\n", status & 0xff, 267862306a36Sopenharmony_ci tx_resp->failure_frame); 267962306a36Sopenharmony_ci D_TX_REPLY("Rate Info rate_n_flags=%x\n", rate_n_flags); 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci agg->wait_for_ba = 0; 268262306a36Sopenharmony_ci } else { 268362306a36Sopenharmony_ci /* Two or more frames were attempted; expect block-ack */ 268462306a36Sopenharmony_ci u64 bitmap = 0; 268562306a36Sopenharmony_ci int start = agg->start_idx; 268662306a36Sopenharmony_ci struct sk_buff *skb; 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci /* Construct bit-map of pending frames within Tx win */ 268962306a36Sopenharmony_ci for (i = 0; i < agg->frame_count; i++) { 269062306a36Sopenharmony_ci u16 sc; 269162306a36Sopenharmony_ci status = le16_to_cpu(frame_status[i].status); 269262306a36Sopenharmony_ci seq = le16_to_cpu(frame_status[i].sequence); 269362306a36Sopenharmony_ci idx = SEQ_TO_IDX(seq); 269462306a36Sopenharmony_ci txq_id = SEQ_TO_QUEUE(seq); 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci if (status & 269762306a36Sopenharmony_ci (AGG_TX_STATE_FEW_BYTES_MSK | 269862306a36Sopenharmony_ci AGG_TX_STATE_ABORT_MSK)) 269962306a36Sopenharmony_ci continue; 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci D_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n", 270262306a36Sopenharmony_ci agg->frame_count, txq_id, idx); 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci skb = il->txq[txq_id].skbs[idx]; 270562306a36Sopenharmony_ci if (WARN_ON_ONCE(skb == NULL)) 270662306a36Sopenharmony_ci return -1; 270762306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *) skb->data; 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci sc = le16_to_cpu(hdr->seq_ctrl); 271062306a36Sopenharmony_ci if (idx != (IEEE80211_SEQ_TO_SN(sc) & 0xff)) { 271162306a36Sopenharmony_ci IL_ERR("BUG_ON idx doesn't match seq control" 271262306a36Sopenharmony_ci " idx=%d, seq_idx=%d, seq=%d\n", idx, 271362306a36Sopenharmony_ci IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl); 271462306a36Sopenharmony_ci return -1; 271562306a36Sopenharmony_ci } 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_ci D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx, 271862306a36Sopenharmony_ci IEEE80211_SEQ_TO_SN(sc)); 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci sh = idx - start; 272162306a36Sopenharmony_ci if (sh > 64) { 272262306a36Sopenharmony_ci sh = (start - idx) + 0xff; 272362306a36Sopenharmony_ci bitmap = bitmap << sh; 272462306a36Sopenharmony_ci sh = 0; 272562306a36Sopenharmony_ci start = idx; 272662306a36Sopenharmony_ci } else if (sh < -64) 272762306a36Sopenharmony_ci sh = 0xff - (start - idx); 272862306a36Sopenharmony_ci else if (sh < 0) { 272962306a36Sopenharmony_ci sh = start - idx; 273062306a36Sopenharmony_ci start = idx; 273162306a36Sopenharmony_ci bitmap = bitmap << sh; 273262306a36Sopenharmony_ci sh = 0; 273362306a36Sopenharmony_ci } 273462306a36Sopenharmony_ci bitmap |= 1ULL << sh; 273562306a36Sopenharmony_ci D_TX_REPLY("start=%d bitmap=0x%llx\n", start, 273662306a36Sopenharmony_ci (unsigned long long)bitmap); 273762306a36Sopenharmony_ci } 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci agg->bitmap = bitmap; 274062306a36Sopenharmony_ci agg->start_idx = start; 274162306a36Sopenharmony_ci D_TX_REPLY("Frames %d start_idx=%d bitmap=0x%llx\n", 274262306a36Sopenharmony_ci agg->frame_count, agg->start_idx, 274362306a36Sopenharmony_ci (unsigned long long)agg->bitmap); 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci if (bitmap) 274662306a36Sopenharmony_ci agg->wait_for_ba = 1; 274762306a36Sopenharmony_ci } 274862306a36Sopenharmony_ci return 0; 274962306a36Sopenharmony_ci} 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci/* 275262306a36Sopenharmony_ci * il4965_hdl_tx - Handle standard (non-aggregation) Tx response 275362306a36Sopenharmony_ci */ 275462306a36Sopenharmony_cistatic void 275562306a36Sopenharmony_ciil4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) 275662306a36Sopenharmony_ci{ 275762306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 275862306a36Sopenharmony_ci u16 sequence = le16_to_cpu(pkt->hdr.sequence); 275962306a36Sopenharmony_ci int txq_id = SEQ_TO_QUEUE(sequence); 276062306a36Sopenharmony_ci int idx = SEQ_TO_IDX(sequence); 276162306a36Sopenharmony_ci struct il_tx_queue *txq = &il->txq[txq_id]; 276262306a36Sopenharmony_ci struct sk_buff *skb; 276362306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 276462306a36Sopenharmony_ci struct ieee80211_tx_info *info; 276562306a36Sopenharmony_ci struct il4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; 276662306a36Sopenharmony_ci u32 status = le32_to_cpu(tx_resp->u.status); 276762306a36Sopenharmony_ci int tid; 276862306a36Sopenharmony_ci int sta_id; 276962306a36Sopenharmony_ci int freed; 277062306a36Sopenharmony_ci u8 *qc = NULL; 277162306a36Sopenharmony_ci unsigned long flags; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) { 277462306a36Sopenharmony_ci IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " 277562306a36Sopenharmony_ci "is out of range [0-%d] %d %d\n", txq_id, idx, 277662306a36Sopenharmony_ci txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); 277762306a36Sopenharmony_ci return; 277862306a36Sopenharmony_ci } 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci txq->time_stamp = jiffies; 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci skb = txq->skbs[txq->q.read_ptr]; 278362306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 278462306a36Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *) skb->data; 278762306a36Sopenharmony_ci if (ieee80211_is_data_qos(hdr->frame_control)) { 278862306a36Sopenharmony_ci qc = ieee80211_get_qos_ctl(hdr); 278962306a36Sopenharmony_ci tid = qc[0] & 0xf; 279062306a36Sopenharmony_ci } 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci sta_id = il4965_get_ra_sta_id(il, hdr); 279362306a36Sopenharmony_ci if (txq->sched_retry && unlikely(sta_id == IL_INVALID_STATION)) { 279462306a36Sopenharmony_ci IL_ERR("Station not known\n"); 279562306a36Sopenharmony_ci return; 279662306a36Sopenharmony_ci } 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci /* 279962306a36Sopenharmony_ci * Firmware will not transmit frame on passive channel, if it not yet 280062306a36Sopenharmony_ci * received some valid frame on that channel. When this error happen 280162306a36Sopenharmony_ci * we have to wait until firmware will unblock itself i.e. when we 280262306a36Sopenharmony_ci * note received beacon or other frame. We unblock queues in 280362306a36Sopenharmony_ci * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed. 280462306a36Sopenharmony_ci */ 280562306a36Sopenharmony_ci if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && 280662306a36Sopenharmony_ci il->iw_mode == NL80211_IFTYPE_STATION) { 280762306a36Sopenharmony_ci il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); 280862306a36Sopenharmony_ci D_INFO("Stopped queues - RX waiting on passive channel\n"); 280962306a36Sopenharmony_ci } 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 281262306a36Sopenharmony_ci if (txq->sched_retry) { 281362306a36Sopenharmony_ci const u32 scd_ssn = il4965_get_scd_ssn(tx_resp); 281462306a36Sopenharmony_ci struct il_ht_agg *agg; 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci if (WARN_ON(!qc)) 281762306a36Sopenharmony_ci goto out; 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ci agg = &il->stations[sta_id].tid[tid].agg; 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci il4965_tx_status_reply_tx(il, agg, tx_resp, txq_id, idx); 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci /* check if BAR is needed */ 282462306a36Sopenharmony_ci if (tx_resp->frame_count == 1 && 282562306a36Sopenharmony_ci !il4965_is_tx_success(status)) 282662306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci if (txq->q.read_ptr != (scd_ssn & 0xff)) { 282962306a36Sopenharmony_ci idx = il_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd); 283062306a36Sopenharmony_ci D_TX_REPLY("Retry scheduler reclaim scd_ssn " 283162306a36Sopenharmony_ci "%d idx %d\n", scd_ssn, idx); 283262306a36Sopenharmony_ci freed = il4965_tx_queue_reclaim(il, txq_id, idx); 283362306a36Sopenharmony_ci il4965_free_tfds_in_queue(il, sta_id, tid, freed); 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci if (il->mac80211_registered && 283662306a36Sopenharmony_ci il_queue_space(&txq->q) > txq->q.low_mark && 283762306a36Sopenharmony_ci agg->state != IL_EMPTYING_HW_QUEUE_DELBA) 283862306a36Sopenharmony_ci il_wake_queue(il, txq); 283962306a36Sopenharmony_ci } 284062306a36Sopenharmony_ci } else { 284162306a36Sopenharmony_ci info->status.rates[0].count = tx_resp->failure_frame + 1; 284262306a36Sopenharmony_ci info->flags |= il4965_tx_status_to_mac80211(status); 284362306a36Sopenharmony_ci il4965_hwrate_to_tx_control(il, 284462306a36Sopenharmony_ci le32_to_cpu(tx_resp->rate_n_flags), 284562306a36Sopenharmony_ci info); 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci D_TX_REPLY("TXQ %d status %s (0x%08x) " 284862306a36Sopenharmony_ci "rate_n_flags 0x%x retries %d\n", txq_id, 284962306a36Sopenharmony_ci il4965_get_tx_fail_reason(status), status, 285062306a36Sopenharmony_ci le32_to_cpu(tx_resp->rate_n_flags), 285162306a36Sopenharmony_ci tx_resp->failure_frame); 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci freed = il4965_tx_queue_reclaim(il, txq_id, idx); 285462306a36Sopenharmony_ci if (qc && likely(sta_id != IL_INVALID_STATION)) 285562306a36Sopenharmony_ci il4965_free_tfds_in_queue(il, sta_id, tid, freed); 285662306a36Sopenharmony_ci else if (sta_id == IL_INVALID_STATION) 285762306a36Sopenharmony_ci D_TX_REPLY("Station not known\n"); 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci if (il->mac80211_registered && 286062306a36Sopenharmony_ci il_queue_space(&txq->q) > txq->q.low_mark) 286162306a36Sopenharmony_ci il_wake_queue(il, txq); 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ciout: 286462306a36Sopenharmony_ci if (qc && likely(sta_id != IL_INVALID_STATION)) 286562306a36Sopenharmony_ci il4965_txq_check_empty(il, sta_id, tid, txq_id); 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci il4965_check_abort_status(il, tx_resp->frame_count, status); 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 287062306a36Sopenharmony_ci} 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci/* 287362306a36Sopenharmony_ci * translate ucode response to mac80211 tx status control values 287462306a36Sopenharmony_ci */ 287562306a36Sopenharmony_civoid 287662306a36Sopenharmony_ciil4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags, 287762306a36Sopenharmony_ci struct ieee80211_tx_info *info) 287862306a36Sopenharmony_ci{ 287962306a36Sopenharmony_ci struct ieee80211_tx_rate *r = &info->status.rates[0]; 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci info->status.antenna = 288262306a36Sopenharmony_ci ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); 288362306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) 288462306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_MCS; 288562306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_GF_MSK) 288662306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_GREEN_FIELD; 288762306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT40_MSK) 288862306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; 288962306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_DUP_MSK) 289062306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_DUP_DATA; 289162306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_SGI_MSK) 289262306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_SHORT_GI; 289362306a36Sopenharmony_ci r->idx = il4965_hwrate_to_mac80211_idx(rate_n_flags, info->band); 289462306a36Sopenharmony_ci} 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci/* 289762306a36Sopenharmony_ci * il4965_hdl_compressed_ba - Handler for N_COMPRESSED_BA 289862306a36Sopenharmony_ci * 289962306a36Sopenharmony_ci * Handles block-acknowledge notification from device, which reports success 290062306a36Sopenharmony_ci * of frames sent via aggregation. 290162306a36Sopenharmony_ci */ 290262306a36Sopenharmony_cistatic void 290362306a36Sopenharmony_ciil4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb) 290462306a36Sopenharmony_ci{ 290562306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 290662306a36Sopenharmony_ci struct il_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; 290762306a36Sopenharmony_ci struct il_tx_queue *txq = NULL; 290862306a36Sopenharmony_ci struct il_ht_agg *agg; 290962306a36Sopenharmony_ci int idx; 291062306a36Sopenharmony_ci int sta_id; 291162306a36Sopenharmony_ci int tid; 291262306a36Sopenharmony_ci unsigned long flags; 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci /* "flow" corresponds to Tx queue */ 291562306a36Sopenharmony_ci u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci /* "ssn" is start of block-ack Tx win, corresponds to idx 291862306a36Sopenharmony_ci * (in Tx queue's circular buffer) of first TFD/frame in win */ 291962306a36Sopenharmony_ci u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci if (scd_flow >= il->hw_params.max_txq_num) { 292262306a36Sopenharmony_ci IL_ERR("BUG_ON scd_flow is bigger than number of queues\n"); 292362306a36Sopenharmony_ci return; 292462306a36Sopenharmony_ci } 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci txq = &il->txq[scd_flow]; 292762306a36Sopenharmony_ci sta_id = ba_resp->sta_id; 292862306a36Sopenharmony_ci tid = ba_resp->tid; 292962306a36Sopenharmony_ci agg = &il->stations[sta_id].tid[tid].agg; 293062306a36Sopenharmony_ci if (unlikely(agg->txq_id != scd_flow)) { 293162306a36Sopenharmony_ci /* 293262306a36Sopenharmony_ci * FIXME: this is a uCode bug which need to be addressed, 293362306a36Sopenharmony_ci * log the information and return for now! 293462306a36Sopenharmony_ci * since it is possible happen very often and in order 293562306a36Sopenharmony_ci * not to fill the syslog, don't enable the logging by default 293662306a36Sopenharmony_ci */ 293762306a36Sopenharmony_ci D_TX_REPLY("BA scd_flow %d does not match txq_id %d\n", 293862306a36Sopenharmony_ci scd_flow, agg->txq_id); 293962306a36Sopenharmony_ci return; 294062306a36Sopenharmony_ci } 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci /* Find idx just before block-ack win */ 294362306a36Sopenharmony_ci idx = il_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci D_TX_REPLY("N_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n", 294862306a36Sopenharmony_ci agg->wait_for_ba, (u8 *) &ba_resp->sta_addr_lo32, 294962306a36Sopenharmony_ci ba_resp->sta_id); 295062306a36Sopenharmony_ci D_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx," "scd_flow = " 295162306a36Sopenharmony_ci "%d, scd_ssn = %d\n", ba_resp->tid, ba_resp->seq_ctl, 295262306a36Sopenharmony_ci (unsigned long long)le64_to_cpu(ba_resp->bitmap), 295362306a36Sopenharmony_ci ba_resp->scd_flow, ba_resp->scd_ssn); 295462306a36Sopenharmony_ci D_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx\n", agg->start_idx, 295562306a36Sopenharmony_ci (unsigned long long)agg->bitmap); 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci /* Update driver's record of ACK vs. not for each frame in win */ 295862306a36Sopenharmony_ci il4965_tx_status_reply_compressed_ba(il, agg, ba_resp); 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci /* Release all TFDs before the SSN, i.e. all TFDs in front of 296162306a36Sopenharmony_ci * block-ack win (we assume that they've been successfully 296262306a36Sopenharmony_ci * transmitted ... if not, it's too late anyway). */ 296362306a36Sopenharmony_ci if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) { 296462306a36Sopenharmony_ci /* calculate mac80211 ampdu sw queue to wake */ 296562306a36Sopenharmony_ci int freed = il4965_tx_queue_reclaim(il, scd_flow, idx); 296662306a36Sopenharmony_ci il4965_free_tfds_in_queue(il, sta_id, tid, freed); 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci if (il_queue_space(&txq->q) > txq->q.low_mark && 296962306a36Sopenharmony_ci il->mac80211_registered && 297062306a36Sopenharmony_ci agg->state != IL_EMPTYING_HW_QUEUE_DELBA) 297162306a36Sopenharmony_ci il_wake_queue(il, txq); 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci il4965_txq_check_empty(il, sta_id, tid, scd_flow); 297462306a36Sopenharmony_ci } 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 297762306a36Sopenharmony_ci} 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 298062306a36Sopenharmony_ciconst char * 298162306a36Sopenharmony_ciil4965_get_tx_fail_reason(u32 status) 298262306a36Sopenharmony_ci{ 298362306a36Sopenharmony_ci#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x 298462306a36Sopenharmony_ci#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci switch (status & TX_STATUS_MSK) { 298762306a36Sopenharmony_ci case TX_STATUS_SUCCESS: 298862306a36Sopenharmony_ci return "SUCCESS"; 298962306a36Sopenharmony_ci TX_STATUS_POSTPONE(DELAY); 299062306a36Sopenharmony_ci TX_STATUS_POSTPONE(FEW_BYTES); 299162306a36Sopenharmony_ci TX_STATUS_POSTPONE(QUIET_PERIOD); 299262306a36Sopenharmony_ci TX_STATUS_POSTPONE(CALC_TTAK); 299362306a36Sopenharmony_ci TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); 299462306a36Sopenharmony_ci TX_STATUS_FAIL(SHORT_LIMIT); 299562306a36Sopenharmony_ci TX_STATUS_FAIL(LONG_LIMIT); 299662306a36Sopenharmony_ci TX_STATUS_FAIL(FIFO_UNDERRUN); 299762306a36Sopenharmony_ci TX_STATUS_FAIL(DRAIN_FLOW); 299862306a36Sopenharmony_ci TX_STATUS_FAIL(RFKILL_FLUSH); 299962306a36Sopenharmony_ci TX_STATUS_FAIL(LIFE_EXPIRE); 300062306a36Sopenharmony_ci TX_STATUS_FAIL(DEST_PS); 300162306a36Sopenharmony_ci TX_STATUS_FAIL(HOST_ABORTED); 300262306a36Sopenharmony_ci TX_STATUS_FAIL(BT_RETRY); 300362306a36Sopenharmony_ci TX_STATUS_FAIL(STA_INVALID); 300462306a36Sopenharmony_ci TX_STATUS_FAIL(FRAG_DROPPED); 300562306a36Sopenharmony_ci TX_STATUS_FAIL(TID_DISABLE); 300662306a36Sopenharmony_ci TX_STATUS_FAIL(FIFO_FLUSHED); 300762306a36Sopenharmony_ci TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); 300862306a36Sopenharmony_ci TX_STATUS_FAIL(PASSIVE_NO_RX); 300962306a36Sopenharmony_ci TX_STATUS_FAIL(NO_BEACON_ON_RADAR); 301062306a36Sopenharmony_ci } 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci return "UNKNOWN"; 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ci#undef TX_STATUS_FAIL 301562306a36Sopenharmony_ci#undef TX_STATUS_POSTPONE 301662306a36Sopenharmony_ci} 301762306a36Sopenharmony_ci#endif /* CONFIG_IWLEGACY_DEBUG */ 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_cistatic struct il_link_quality_cmd * 302062306a36Sopenharmony_ciil4965_sta_alloc_lq(struct il_priv *il, u8 sta_id) 302162306a36Sopenharmony_ci{ 302262306a36Sopenharmony_ci int i, r; 302362306a36Sopenharmony_ci struct il_link_quality_cmd *link_cmd; 302462306a36Sopenharmony_ci u32 rate_flags = 0; 302562306a36Sopenharmony_ci __le32 rate_n_flags; 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci link_cmd = kzalloc(sizeof(struct il_link_quality_cmd), GFP_KERNEL); 302862306a36Sopenharmony_ci if (!link_cmd) { 302962306a36Sopenharmony_ci IL_ERR("Unable to allocate memory for LQ cmd.\n"); 303062306a36Sopenharmony_ci return NULL; 303162306a36Sopenharmony_ci } 303262306a36Sopenharmony_ci /* Set up the rate scaling to start at selected rate, fall back 303362306a36Sopenharmony_ci * all the way down to 1M in IEEE order, and then spin on 1M */ 303462306a36Sopenharmony_ci if (il->band == NL80211_BAND_5GHZ) 303562306a36Sopenharmony_ci r = RATE_6M_IDX; 303662306a36Sopenharmony_ci else 303762306a36Sopenharmony_ci r = RATE_1M_IDX; 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ci if (r >= IL_FIRST_CCK_RATE && r <= IL_LAST_CCK_RATE) 304062306a36Sopenharmony_ci rate_flags |= RATE_MCS_CCK_MSK; 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci rate_flags |= 304362306a36Sopenharmony_ci il4965_first_antenna(il->hw_params. 304462306a36Sopenharmony_ci valid_tx_ant) << RATE_MCS_ANT_POS; 304562306a36Sopenharmony_ci rate_n_flags = cpu_to_le32(il_rates[r].plcp | rate_flags); 304662306a36Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) 304762306a36Sopenharmony_ci link_cmd->rs_table[i].rate_n_flags = rate_n_flags; 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci link_cmd->general_params.single_stream_ant_msk = 305062306a36Sopenharmony_ci il4965_first_antenna(il->hw_params.valid_tx_ant); 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = 305362306a36Sopenharmony_ci il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params. 305462306a36Sopenharmony_ci valid_tx_ant); 305562306a36Sopenharmony_ci if (!link_cmd->general_params.dual_stream_ant_msk) { 305662306a36Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = ANT_AB; 305762306a36Sopenharmony_ci } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) { 305862306a36Sopenharmony_ci link_cmd->general_params.dual_stream_ant_msk = 305962306a36Sopenharmony_ci il->hw_params.valid_tx_ant; 306062306a36Sopenharmony_ci } 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; 306362306a36Sopenharmony_ci link_cmd->agg_params.agg_time_limit = 306462306a36Sopenharmony_ci cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci link_cmd->sta_id = sta_id; 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci return link_cmd; 306962306a36Sopenharmony_ci} 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_ci/* 307262306a36Sopenharmony_ci * il4965_add_bssid_station - Add the special IBSS BSSID station 307362306a36Sopenharmony_ci * 307462306a36Sopenharmony_ci * Function sleeps. 307562306a36Sopenharmony_ci */ 307662306a36Sopenharmony_ciint 307762306a36Sopenharmony_ciil4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r) 307862306a36Sopenharmony_ci{ 307962306a36Sopenharmony_ci int ret; 308062306a36Sopenharmony_ci u8 sta_id; 308162306a36Sopenharmony_ci struct il_link_quality_cmd *link_cmd; 308262306a36Sopenharmony_ci unsigned long flags; 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ci if (sta_id_r) 308562306a36Sopenharmony_ci *sta_id_r = IL_INVALID_STATION; 308662306a36Sopenharmony_ci 308762306a36Sopenharmony_ci ret = il_add_station_common(il, addr, 0, NULL, &sta_id); 308862306a36Sopenharmony_ci if (ret) { 308962306a36Sopenharmony_ci IL_ERR("Unable to add station %pM\n", addr); 309062306a36Sopenharmony_ci return ret; 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci if (sta_id_r) 309462306a36Sopenharmony_ci *sta_id_r = sta_id; 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 309762306a36Sopenharmony_ci il->stations[sta_id].used |= IL_STA_LOCAL; 309862306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 309962306a36Sopenharmony_ci 310062306a36Sopenharmony_ci /* Set up default rate scaling table in device's station table */ 310162306a36Sopenharmony_ci link_cmd = il4965_sta_alloc_lq(il, sta_id); 310262306a36Sopenharmony_ci if (!link_cmd) { 310362306a36Sopenharmony_ci IL_ERR("Unable to initialize rate scaling for station %pM.\n", 310462306a36Sopenharmony_ci addr); 310562306a36Sopenharmony_ci return -ENOMEM; 310662306a36Sopenharmony_ci } 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_ci ret = il_send_lq_cmd(il, link_cmd, CMD_SYNC, true); 310962306a36Sopenharmony_ci if (ret) 311062306a36Sopenharmony_ci IL_ERR("Link quality command failed (%d)\n", ret); 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 311362306a36Sopenharmony_ci il->stations[sta_id].lq = link_cmd; 311462306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_ci return 0; 311762306a36Sopenharmony_ci} 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_cistatic int 312062306a36Sopenharmony_ciil4965_static_wepkey_cmd(struct il_priv *il, bool send_if_empty) 312162306a36Sopenharmony_ci{ 312262306a36Sopenharmony_ci int i; 312362306a36Sopenharmony_ci u8 buff[sizeof(struct il_wep_cmd) + 312462306a36Sopenharmony_ci sizeof(struct il_wep_key) * WEP_KEYS_MAX]; 312562306a36Sopenharmony_ci struct il_wep_cmd *wep_cmd = (struct il_wep_cmd *)buff; 312662306a36Sopenharmony_ci size_t cmd_size = sizeof(struct il_wep_cmd); 312762306a36Sopenharmony_ci struct il_host_cmd cmd = { 312862306a36Sopenharmony_ci .id = C_WEPKEY, 312962306a36Sopenharmony_ci .data = wep_cmd, 313062306a36Sopenharmony_ci .flags = CMD_SYNC, 313162306a36Sopenharmony_ci }; 313262306a36Sopenharmony_ci bool not_empty = false; 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci might_sleep(); 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci memset(wep_cmd, 0, 313762306a36Sopenharmony_ci cmd_size + (sizeof(struct il_wep_key) * WEP_KEYS_MAX)); 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci for (i = 0; i < WEP_KEYS_MAX; i++) { 314062306a36Sopenharmony_ci u8 key_size = il->_4965.wep_keys[i].key_size; 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ci wep_cmd->key[i].key_idx = i; 314362306a36Sopenharmony_ci if (key_size) { 314462306a36Sopenharmony_ci wep_cmd->key[i].key_offset = i; 314562306a36Sopenharmony_ci not_empty = true; 314662306a36Sopenharmony_ci } else 314762306a36Sopenharmony_ci wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci wep_cmd->key[i].key_size = key_size; 315062306a36Sopenharmony_ci memcpy(&wep_cmd->key[i].key[3], il->_4965.wep_keys[i].key, key_size); 315162306a36Sopenharmony_ci } 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; 315462306a36Sopenharmony_ci wep_cmd->num_keys = WEP_KEYS_MAX; 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci cmd_size += sizeof(struct il_wep_key) * WEP_KEYS_MAX; 315762306a36Sopenharmony_ci cmd.len = cmd_size; 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci if (not_empty || send_if_empty) 316062306a36Sopenharmony_ci return il_send_cmd(il, &cmd); 316162306a36Sopenharmony_ci else 316262306a36Sopenharmony_ci return 0; 316362306a36Sopenharmony_ci} 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ciint 316662306a36Sopenharmony_ciil4965_restore_default_wep_keys(struct il_priv *il) 316762306a36Sopenharmony_ci{ 316862306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci return il4965_static_wepkey_cmd(il, false); 317162306a36Sopenharmony_ci} 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_ciint 317462306a36Sopenharmony_ciil4965_remove_default_wep_key(struct il_priv *il, 317562306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf) 317662306a36Sopenharmony_ci{ 317762306a36Sopenharmony_ci int ret; 317862306a36Sopenharmony_ci int idx = keyconf->keyidx; 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci D_WEP("Removing default WEP key: idx=%d\n", idx); 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci memset(&il->_4965.wep_keys[idx], 0, sizeof(struct il_wep_key)); 318562306a36Sopenharmony_ci if (il_is_rfkill(il)) { 318662306a36Sopenharmony_ci D_WEP("Not sending C_WEPKEY command due to RFKILL.\n"); 318762306a36Sopenharmony_ci /* but keys in device are clear anyway so return success */ 318862306a36Sopenharmony_ci return 0; 318962306a36Sopenharmony_ci } 319062306a36Sopenharmony_ci ret = il4965_static_wepkey_cmd(il, 1); 319162306a36Sopenharmony_ci D_WEP("Remove default WEP key: idx=%d ret=%d\n", idx, ret); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci return ret; 319462306a36Sopenharmony_ci} 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_ciint 319762306a36Sopenharmony_ciil4965_set_default_wep_key(struct il_priv *il, 319862306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf) 319962306a36Sopenharmony_ci{ 320062306a36Sopenharmony_ci int ret; 320162306a36Sopenharmony_ci int len = keyconf->keylen; 320262306a36Sopenharmony_ci int idx = keyconf->keyidx; 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_ci if (len != WEP_KEY_LEN_128 && len != WEP_KEY_LEN_64) { 320762306a36Sopenharmony_ci D_WEP("Bad WEP key length %d\n", keyconf->keylen); 320862306a36Sopenharmony_ci return -EINVAL; 320962306a36Sopenharmony_ci } 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; 321262306a36Sopenharmony_ci keyconf->hw_key_idx = HW_KEY_DEFAULT; 321362306a36Sopenharmony_ci il->stations[IL_AP_ID].keyinfo.cipher = keyconf->cipher; 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci il->_4965.wep_keys[idx].key_size = len; 321662306a36Sopenharmony_ci memcpy(&il->_4965.wep_keys[idx].key, &keyconf->key, len); 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci ret = il4965_static_wepkey_cmd(il, false); 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci D_WEP("Set default WEP key: len=%d idx=%d ret=%d\n", len, idx, ret); 322162306a36Sopenharmony_ci return ret; 322262306a36Sopenharmony_ci} 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_cistatic int 322562306a36Sopenharmony_ciil4965_set_wep_dynamic_key_info(struct il_priv *il, 322662306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, u8 sta_id) 322762306a36Sopenharmony_ci{ 322862306a36Sopenharmony_ci unsigned long flags; 322962306a36Sopenharmony_ci __le16 key_flags = 0; 323062306a36Sopenharmony_ci struct il_addsta_cmd sta_cmd; 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK); 323762306a36Sopenharmony_ci key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); 323862306a36Sopenharmony_ci key_flags &= ~STA_KEY_FLG_INVALID; 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci if (keyconf->keylen == WEP_KEY_LEN_128) 324162306a36Sopenharmony_ci key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci if (sta_id == il->hw_params.bcast_id) 324462306a36Sopenharmony_ci key_flags |= STA_KEY_MULTICAST_MSK; 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci il->stations[sta_id].keyinfo.cipher = keyconf->cipher; 324962306a36Sopenharmony_ci il->stations[sta_id].keyinfo.keylen = keyconf->keylen; 325062306a36Sopenharmony_ci il->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci memcpy(&il->stations[sta_id].sta.key.key[3], keyconf->key, 325562306a36Sopenharmony_ci keyconf->keylen); 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci if ((il->stations[sta_id].sta.key. 325862306a36Sopenharmony_ci key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) 325962306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_offset = 326062306a36Sopenharmony_ci il_get_free_ucode_key_idx(il); 326162306a36Sopenharmony_ci /* else, we are overriding an existing key => no need to allocated room 326262306a36Sopenharmony_ci * in uCode. */ 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, 326562306a36Sopenharmony_ci "no space for a new key"); 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_flags = key_flags; 326862306a36Sopenharmony_ci il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; 326962306a36Sopenharmony_ci il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci memcpy(&sta_cmd, &il->stations[sta_id].sta, 327262306a36Sopenharmony_ci sizeof(struct il_addsta_cmd)); 327362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci return il_send_add_sta(il, &sta_cmd, CMD_SYNC); 327662306a36Sopenharmony_ci} 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_cistatic int 327962306a36Sopenharmony_ciil4965_set_ccmp_dynamic_key_info(struct il_priv *il, 328062306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, u8 sta_id) 328162306a36Sopenharmony_ci{ 328262306a36Sopenharmony_ci unsigned long flags; 328362306a36Sopenharmony_ci __le16 key_flags = 0; 328462306a36Sopenharmony_ci struct il_addsta_cmd sta_cmd; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); 328962306a36Sopenharmony_ci key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); 329062306a36Sopenharmony_ci key_flags &= ~STA_KEY_FLG_INVALID; 329162306a36Sopenharmony_ci 329262306a36Sopenharmony_ci if (sta_id == il->hw_params.bcast_id) 329362306a36Sopenharmony_ci key_flags |= STA_KEY_MULTICAST_MSK; 329462306a36Sopenharmony_ci 329562306a36Sopenharmony_ci keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 329862306a36Sopenharmony_ci il->stations[sta_id].keyinfo.cipher = keyconf->cipher; 329962306a36Sopenharmony_ci il->stations[sta_id].keyinfo.keylen = keyconf->keylen; 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen); 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci if ((il->stations[sta_id].sta.key. 330662306a36Sopenharmony_ci key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) 330762306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_offset = 330862306a36Sopenharmony_ci il_get_free_ucode_key_idx(il); 330962306a36Sopenharmony_ci /* else, we are overriding an existing key => no need to allocated room 331062306a36Sopenharmony_ci * in uCode. */ 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, 331362306a36Sopenharmony_ci "no space for a new key"); 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_flags = key_flags; 331662306a36Sopenharmony_ci il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; 331762306a36Sopenharmony_ci il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 331862306a36Sopenharmony_ci 331962306a36Sopenharmony_ci memcpy(&sta_cmd, &il->stations[sta_id].sta, 332062306a36Sopenharmony_ci sizeof(struct il_addsta_cmd)); 332162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci return il_send_add_sta(il, &sta_cmd, CMD_SYNC); 332462306a36Sopenharmony_ci} 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_cistatic int 332762306a36Sopenharmony_ciil4965_set_tkip_dynamic_key_info(struct il_priv *il, 332862306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, u8 sta_id) 332962306a36Sopenharmony_ci{ 333062306a36Sopenharmony_ci unsigned long flags; 333162306a36Sopenharmony_ci __le16 key_flags = 0; 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); 333462306a36Sopenharmony_ci key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); 333562306a36Sopenharmony_ci key_flags &= ~STA_KEY_FLG_INVALID; 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci if (sta_id == il->hw_params.bcast_id) 333862306a36Sopenharmony_ci key_flags |= STA_KEY_MULTICAST_MSK; 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; 334162306a36Sopenharmony_ci keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci il->stations[sta_id].keyinfo.cipher = keyconf->cipher; 334662306a36Sopenharmony_ci il->stations[sta_id].keyinfo.keylen = 16; 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci if ((il->stations[sta_id].sta.key. 334962306a36Sopenharmony_ci key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) 335062306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_offset = 335162306a36Sopenharmony_ci il_get_free_ucode_key_idx(il); 335262306a36Sopenharmony_ci /* else, we are overriding an existing key => no need to allocated room 335362306a36Sopenharmony_ci * in uCode. */ 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, 335662306a36Sopenharmony_ci "no space for a new key"); 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_flags = key_flags; 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci /* This copy is acutally not needed: we get the key with each TX */ 336162306a36Sopenharmony_ci memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, 16); 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci memcpy(il->stations[sta_id].sta.key.key, keyconf->key, 16); 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci return 0; 336862306a36Sopenharmony_ci} 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_civoid 337162306a36Sopenharmony_ciil4965_update_tkip_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, 337262306a36Sopenharmony_ci struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) 337362306a36Sopenharmony_ci{ 337462306a36Sopenharmony_ci u8 sta_id; 337562306a36Sopenharmony_ci unsigned long flags; 337662306a36Sopenharmony_ci int i; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci if (il_scan_cancel(il)) { 337962306a36Sopenharmony_ci /* cancel scan failed, just live w/ bad key and rely 338062306a36Sopenharmony_ci briefly on SW decryption */ 338162306a36Sopenharmony_ci return; 338262306a36Sopenharmony_ci } 338362306a36Sopenharmony_ci 338462306a36Sopenharmony_ci sta_id = il_sta_id_or_broadcast(il, sta); 338562306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) 338662306a36Sopenharmony_ci return; 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci il->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32; 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_ci for (i = 0; i < 5; i++) 339362306a36Sopenharmony_ci il->stations[sta_id].sta.key.tkip_rx_ttak[i] = 339462306a36Sopenharmony_ci cpu_to_le16(phase1key[i]); 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_ci il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; 339762306a36Sopenharmony_ci il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 340262306a36Sopenharmony_ci} 340362306a36Sopenharmony_ci 340462306a36Sopenharmony_ciint 340562306a36Sopenharmony_ciil4965_remove_dynamic_key(struct il_priv *il, 340662306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, u8 sta_id) 340762306a36Sopenharmony_ci{ 340862306a36Sopenharmony_ci unsigned long flags; 340962306a36Sopenharmony_ci u16 key_flags; 341062306a36Sopenharmony_ci u8 keyidx; 341162306a36Sopenharmony_ci struct il_addsta_cmd sta_cmd; 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_ci il->_4965.key_mapping_keys--; 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 341862306a36Sopenharmony_ci key_flags = le16_to_cpu(il->stations[sta_id].sta.key.key_flags); 341962306a36Sopenharmony_ci keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3; 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci D_WEP("Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ci if (keyconf->keyidx != keyidx) { 342462306a36Sopenharmony_ci /* We need to remove a key with idx different that the one 342562306a36Sopenharmony_ci * in the uCode. This means that the key we need to remove has 342662306a36Sopenharmony_ci * been replaced by another one with different idx. 342762306a36Sopenharmony_ci * Don't do anything and return ok 342862306a36Sopenharmony_ci */ 342962306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 343062306a36Sopenharmony_ci return 0; 343162306a36Sopenharmony_ci } 343262306a36Sopenharmony_ci 343362306a36Sopenharmony_ci if (il->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_INVALID) { 343462306a36Sopenharmony_ci IL_WARN("Removing wrong key %d 0x%x\n", keyconf->keyidx, 343562306a36Sopenharmony_ci key_flags); 343662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 343762306a36Sopenharmony_ci return 0; 343862306a36Sopenharmony_ci } 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci if (!test_and_clear_bit 344162306a36Sopenharmony_ci (il->stations[sta_id].sta.key.key_offset, &il->ucode_key_table)) 344262306a36Sopenharmony_ci IL_ERR("idx %d not used in uCode key table.\n", 344362306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_offset); 344462306a36Sopenharmony_ci memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key)); 344562306a36Sopenharmony_ci memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo)); 344662306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_flags = 344762306a36Sopenharmony_ci STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; 344862306a36Sopenharmony_ci il->stations[sta_id].sta.key.key_offset = keyconf->hw_key_idx; 344962306a36Sopenharmony_ci il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; 345062306a36Sopenharmony_ci il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci if (il_is_rfkill(il)) { 345362306a36Sopenharmony_ci D_WEP 345462306a36Sopenharmony_ci ("Not sending C_ADD_STA command because RFKILL enabled.\n"); 345562306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 345662306a36Sopenharmony_ci return 0; 345762306a36Sopenharmony_ci } 345862306a36Sopenharmony_ci memcpy(&sta_cmd, &il->stations[sta_id].sta, 345962306a36Sopenharmony_ci sizeof(struct il_addsta_cmd)); 346062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci return il_send_add_sta(il, &sta_cmd, CMD_SYNC); 346362306a36Sopenharmony_ci} 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ciint 346662306a36Sopenharmony_ciil4965_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, 346762306a36Sopenharmony_ci u8 sta_id) 346862306a36Sopenharmony_ci{ 346962306a36Sopenharmony_ci int ret; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci il->_4965.key_mapping_keys++; 347462306a36Sopenharmony_ci keyconf->hw_key_idx = HW_KEY_DYNAMIC; 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci switch (keyconf->cipher) { 347762306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 347862306a36Sopenharmony_ci ret = 347962306a36Sopenharmony_ci il4965_set_ccmp_dynamic_key_info(il, keyconf, sta_id); 348062306a36Sopenharmony_ci break; 348162306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 348262306a36Sopenharmony_ci ret = 348362306a36Sopenharmony_ci il4965_set_tkip_dynamic_key_info(il, keyconf, sta_id); 348462306a36Sopenharmony_ci break; 348562306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 348662306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 348762306a36Sopenharmony_ci ret = il4965_set_wep_dynamic_key_info(il, keyconf, sta_id); 348862306a36Sopenharmony_ci break; 348962306a36Sopenharmony_ci default: 349062306a36Sopenharmony_ci IL_ERR("Unknown alg: %s cipher = %x\n", __func__, 349162306a36Sopenharmony_ci keyconf->cipher); 349262306a36Sopenharmony_ci ret = -EINVAL; 349362306a36Sopenharmony_ci } 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci D_WEP("Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n", 349662306a36Sopenharmony_ci keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci return ret; 349962306a36Sopenharmony_ci} 350062306a36Sopenharmony_ci 350162306a36Sopenharmony_ci/* 350262306a36Sopenharmony_ci * il4965_alloc_bcast_station - add broadcast station into driver's station table. 350362306a36Sopenharmony_ci * 350462306a36Sopenharmony_ci * This adds the broadcast station into the driver's station table 350562306a36Sopenharmony_ci * and marks it driver active, so that it will be restored to the 350662306a36Sopenharmony_ci * device at the next best time. 350762306a36Sopenharmony_ci */ 350862306a36Sopenharmony_ciint 350962306a36Sopenharmony_ciil4965_alloc_bcast_station(struct il_priv *il) 351062306a36Sopenharmony_ci{ 351162306a36Sopenharmony_ci struct il_link_quality_cmd *link_cmd; 351262306a36Sopenharmony_ci unsigned long flags; 351362306a36Sopenharmony_ci u8 sta_id; 351462306a36Sopenharmony_ci 351562306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 351662306a36Sopenharmony_ci sta_id = il_prep_station(il, il_bcast_addr, false, NULL); 351762306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) { 351862306a36Sopenharmony_ci IL_ERR("Unable to prepare broadcast station\n"); 351962306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 352062306a36Sopenharmony_ci 352162306a36Sopenharmony_ci return -EINVAL; 352262306a36Sopenharmony_ci } 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE; 352562306a36Sopenharmony_ci il->stations[sta_id].used |= IL_STA_BCAST; 352662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci link_cmd = il4965_sta_alloc_lq(il, sta_id); 352962306a36Sopenharmony_ci if (!link_cmd) { 353062306a36Sopenharmony_ci IL_ERR 353162306a36Sopenharmony_ci ("Unable to initialize rate scaling for bcast station.\n"); 353262306a36Sopenharmony_ci return -ENOMEM; 353362306a36Sopenharmony_ci } 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 353662306a36Sopenharmony_ci il->stations[sta_id].lq = link_cmd; 353762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_ci return 0; 354062306a36Sopenharmony_ci} 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci/* 354362306a36Sopenharmony_ci * il4965_update_bcast_station - update broadcast station's LQ command 354462306a36Sopenharmony_ci * 354562306a36Sopenharmony_ci * Only used by iwl4965. Placed here to have all bcast station management 354662306a36Sopenharmony_ci * code together. 354762306a36Sopenharmony_ci */ 354862306a36Sopenharmony_cistatic int 354962306a36Sopenharmony_ciil4965_update_bcast_station(struct il_priv *il) 355062306a36Sopenharmony_ci{ 355162306a36Sopenharmony_ci unsigned long flags; 355262306a36Sopenharmony_ci struct il_link_quality_cmd *link_cmd; 355362306a36Sopenharmony_ci u8 sta_id = il->hw_params.bcast_id; 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci link_cmd = il4965_sta_alloc_lq(il, sta_id); 355662306a36Sopenharmony_ci if (!link_cmd) { 355762306a36Sopenharmony_ci IL_ERR("Unable to initialize rate scaling for bcast sta.\n"); 355862306a36Sopenharmony_ci return -ENOMEM; 355962306a36Sopenharmony_ci } 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 356262306a36Sopenharmony_ci if (il->stations[sta_id].lq) 356362306a36Sopenharmony_ci kfree(il->stations[sta_id].lq); 356462306a36Sopenharmony_ci else 356562306a36Sopenharmony_ci D_INFO("Bcast sta rate scaling has not been initialized.\n"); 356662306a36Sopenharmony_ci il->stations[sta_id].lq = link_cmd; 356762306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 356862306a36Sopenharmony_ci 356962306a36Sopenharmony_ci return 0; 357062306a36Sopenharmony_ci} 357162306a36Sopenharmony_ci 357262306a36Sopenharmony_ciint 357362306a36Sopenharmony_ciil4965_update_bcast_stations(struct il_priv *il) 357462306a36Sopenharmony_ci{ 357562306a36Sopenharmony_ci return il4965_update_bcast_station(il); 357662306a36Sopenharmony_ci} 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_ci/* 357962306a36Sopenharmony_ci * il4965_sta_tx_modify_enable_tid - Enable Tx for this TID in station table 358062306a36Sopenharmony_ci */ 358162306a36Sopenharmony_ciint 358262306a36Sopenharmony_ciil4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid) 358362306a36Sopenharmony_ci{ 358462306a36Sopenharmony_ci unsigned long flags; 358562306a36Sopenharmony_ci struct il_addsta_cmd sta_cmd; 358662306a36Sopenharmony_ci 358762306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci /* Remove "disable" flag, to enable Tx for this TID */ 359062306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 359162306a36Sopenharmony_ci il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; 359262306a36Sopenharmony_ci il->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); 359362306a36Sopenharmony_ci il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 359462306a36Sopenharmony_ci memcpy(&sta_cmd, &il->stations[sta_id].sta, 359562306a36Sopenharmony_ci sizeof(struct il_addsta_cmd)); 359662306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_ci return il_send_add_sta(il, &sta_cmd, CMD_SYNC); 359962306a36Sopenharmony_ci} 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ciint 360262306a36Sopenharmony_ciil4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, int tid, 360362306a36Sopenharmony_ci u16 ssn) 360462306a36Sopenharmony_ci{ 360562306a36Sopenharmony_ci unsigned long flags; 360662306a36Sopenharmony_ci int sta_id; 360762306a36Sopenharmony_ci struct il_addsta_cmd sta_cmd; 360862306a36Sopenharmony_ci 360962306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_ci sta_id = il_sta_id(sta); 361262306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) 361362306a36Sopenharmony_ci return -ENXIO; 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 361662306a36Sopenharmony_ci il->stations[sta_id].sta.station_flags_msk = 0; 361762306a36Sopenharmony_ci il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; 361862306a36Sopenharmony_ci il->stations[sta_id].sta.add_immediate_ba_tid = (u8) tid; 361962306a36Sopenharmony_ci il->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); 362062306a36Sopenharmony_ci il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 362162306a36Sopenharmony_ci memcpy(&sta_cmd, &il->stations[sta_id].sta, 362262306a36Sopenharmony_ci sizeof(struct il_addsta_cmd)); 362362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 362462306a36Sopenharmony_ci 362562306a36Sopenharmony_ci return il_send_add_sta(il, &sta_cmd, CMD_SYNC); 362662306a36Sopenharmony_ci} 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ciint 362962306a36Sopenharmony_ciil4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, int tid) 363062306a36Sopenharmony_ci{ 363162306a36Sopenharmony_ci unsigned long flags; 363262306a36Sopenharmony_ci int sta_id; 363362306a36Sopenharmony_ci struct il_addsta_cmd sta_cmd; 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_ci sta_id = il_sta_id(sta); 363862306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) { 363962306a36Sopenharmony_ci IL_ERR("Invalid station for AGG tid %d\n", tid); 364062306a36Sopenharmony_ci return -ENXIO; 364162306a36Sopenharmony_ci } 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 364462306a36Sopenharmony_ci il->stations[sta_id].sta.station_flags_msk = 0; 364562306a36Sopenharmony_ci il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; 364662306a36Sopenharmony_ci il->stations[sta_id].sta.remove_immediate_ba_tid = (u8) tid; 364762306a36Sopenharmony_ci il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 364862306a36Sopenharmony_ci memcpy(&sta_cmd, &il->stations[sta_id].sta, 364962306a36Sopenharmony_ci sizeof(struct il_addsta_cmd)); 365062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 365162306a36Sopenharmony_ci 365262306a36Sopenharmony_ci return il_send_add_sta(il, &sta_cmd, CMD_SYNC); 365362306a36Sopenharmony_ci} 365462306a36Sopenharmony_ci 365562306a36Sopenharmony_civoid 365662306a36Sopenharmony_ciil4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt) 365762306a36Sopenharmony_ci{ 365862306a36Sopenharmony_ci unsigned long flags; 365962306a36Sopenharmony_ci 366062306a36Sopenharmony_ci spin_lock_irqsave(&il->sta_lock, flags); 366162306a36Sopenharmony_ci il->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK; 366262306a36Sopenharmony_ci il->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; 366362306a36Sopenharmony_ci il->stations[sta_id].sta.sta.modify_mask = 366462306a36Sopenharmony_ci STA_MODIFY_SLEEP_TX_COUNT_MSK; 366562306a36Sopenharmony_ci il->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt); 366662306a36Sopenharmony_ci il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; 366762306a36Sopenharmony_ci il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); 366862306a36Sopenharmony_ci spin_unlock_irqrestore(&il->sta_lock, flags); 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_ci} 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_civoid 367362306a36Sopenharmony_ciil4965_update_chain_flags(struct il_priv *il) 367462306a36Sopenharmony_ci{ 367562306a36Sopenharmony_ci if (il->ops->set_rxon_chain) { 367662306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 367762306a36Sopenharmony_ci if (il->active.rx_chain != il->staging.rx_chain) 367862306a36Sopenharmony_ci il_commit_rxon(il); 367962306a36Sopenharmony_ci } 368062306a36Sopenharmony_ci} 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_cistatic void 368362306a36Sopenharmony_ciil4965_clear_free_frames(struct il_priv *il) 368462306a36Sopenharmony_ci{ 368562306a36Sopenharmony_ci struct list_head *element; 368662306a36Sopenharmony_ci 368762306a36Sopenharmony_ci D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count); 368862306a36Sopenharmony_ci 368962306a36Sopenharmony_ci while (!list_empty(&il->free_frames)) { 369062306a36Sopenharmony_ci element = il->free_frames.next; 369162306a36Sopenharmony_ci list_del(element); 369262306a36Sopenharmony_ci kfree(list_entry(element, struct il_frame, list)); 369362306a36Sopenharmony_ci il->frames_count--; 369462306a36Sopenharmony_ci } 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci if (il->frames_count) { 369762306a36Sopenharmony_ci IL_WARN("%d frames still in use. Did we lose one?\n", 369862306a36Sopenharmony_ci il->frames_count); 369962306a36Sopenharmony_ci il->frames_count = 0; 370062306a36Sopenharmony_ci } 370162306a36Sopenharmony_ci} 370262306a36Sopenharmony_ci 370362306a36Sopenharmony_cistatic struct il_frame * 370462306a36Sopenharmony_ciil4965_get_free_frame(struct il_priv *il) 370562306a36Sopenharmony_ci{ 370662306a36Sopenharmony_ci struct il_frame *frame; 370762306a36Sopenharmony_ci struct list_head *element; 370862306a36Sopenharmony_ci if (list_empty(&il->free_frames)) { 370962306a36Sopenharmony_ci frame = kzalloc(sizeof(*frame), GFP_KERNEL); 371062306a36Sopenharmony_ci if (!frame) { 371162306a36Sopenharmony_ci IL_ERR("Could not allocate frame!\n"); 371262306a36Sopenharmony_ci return NULL; 371362306a36Sopenharmony_ci } 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci il->frames_count++; 371662306a36Sopenharmony_ci return frame; 371762306a36Sopenharmony_ci } 371862306a36Sopenharmony_ci 371962306a36Sopenharmony_ci element = il->free_frames.next; 372062306a36Sopenharmony_ci list_del(element); 372162306a36Sopenharmony_ci return list_entry(element, struct il_frame, list); 372262306a36Sopenharmony_ci} 372362306a36Sopenharmony_ci 372462306a36Sopenharmony_cistatic void 372562306a36Sopenharmony_ciil4965_free_frame(struct il_priv *il, struct il_frame *frame) 372662306a36Sopenharmony_ci{ 372762306a36Sopenharmony_ci memset(frame, 0, sizeof(*frame)); 372862306a36Sopenharmony_ci list_add(&frame->list, &il->free_frames); 372962306a36Sopenharmony_ci} 373062306a36Sopenharmony_ci 373162306a36Sopenharmony_cistatic u32 373262306a36Sopenharmony_ciil4965_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr, 373362306a36Sopenharmony_ci int left) 373462306a36Sopenharmony_ci{ 373562306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 373662306a36Sopenharmony_ci 373762306a36Sopenharmony_ci if (!il->beacon_skb) 373862306a36Sopenharmony_ci return 0; 373962306a36Sopenharmony_ci 374062306a36Sopenharmony_ci if (il->beacon_skb->len > left) 374162306a36Sopenharmony_ci return 0; 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len); 374462306a36Sopenharmony_ci 374562306a36Sopenharmony_ci return il->beacon_skb->len; 374662306a36Sopenharmony_ci} 374762306a36Sopenharmony_ci 374862306a36Sopenharmony_ci/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ 374962306a36Sopenharmony_cistatic void 375062306a36Sopenharmony_ciil4965_set_beacon_tim(struct il_priv *il, 375162306a36Sopenharmony_ci struct il_tx_beacon_cmd *tx_beacon_cmd, u8 * beacon, 375262306a36Sopenharmony_ci u32 frame_size) 375362306a36Sopenharmony_ci{ 375462306a36Sopenharmony_ci u16 tim_idx; 375562306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci /* 375862306a36Sopenharmony_ci * The idx is relative to frame start but we start looking at the 375962306a36Sopenharmony_ci * variable-length part of the beacon. 376062306a36Sopenharmony_ci */ 376162306a36Sopenharmony_ci tim_idx = mgmt->u.beacon.variable - beacon; 376262306a36Sopenharmony_ci 376362306a36Sopenharmony_ci /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ 376462306a36Sopenharmony_ci while ((tim_idx < (frame_size - 2)) && 376562306a36Sopenharmony_ci (beacon[tim_idx] != WLAN_EID_TIM)) 376662306a36Sopenharmony_ci tim_idx += beacon[tim_idx + 1] + 2; 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci /* If TIM field was found, set variables */ 376962306a36Sopenharmony_ci if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { 377062306a36Sopenharmony_ci tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); 377162306a36Sopenharmony_ci tx_beacon_cmd->tim_size = beacon[tim_idx + 1]; 377262306a36Sopenharmony_ci } else 377362306a36Sopenharmony_ci IL_WARN("Unable to find TIM Element in beacon\n"); 377462306a36Sopenharmony_ci} 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_cistatic unsigned int 377762306a36Sopenharmony_ciil4965_hw_get_beacon_cmd(struct il_priv *il, struct il_frame *frame) 377862306a36Sopenharmony_ci{ 377962306a36Sopenharmony_ci struct il_tx_beacon_cmd *tx_beacon_cmd; 378062306a36Sopenharmony_ci u32 frame_size; 378162306a36Sopenharmony_ci u32 rate_flags; 378262306a36Sopenharmony_ci u32 rate; 378362306a36Sopenharmony_ci /* 378462306a36Sopenharmony_ci * We have to set up the TX command, the TX Beacon command, and the 378562306a36Sopenharmony_ci * beacon contents. 378662306a36Sopenharmony_ci */ 378762306a36Sopenharmony_ci 378862306a36Sopenharmony_ci lockdep_assert_held(&il->mutex); 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_ci if (!il->beacon_enabled) { 379162306a36Sopenharmony_ci IL_ERR("Trying to build beacon without beaconing enabled\n"); 379262306a36Sopenharmony_ci return 0; 379362306a36Sopenharmony_ci } 379462306a36Sopenharmony_ci 379562306a36Sopenharmony_ci /* Initialize memory */ 379662306a36Sopenharmony_ci tx_beacon_cmd = &frame->u.beacon; 379762306a36Sopenharmony_ci memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); 379862306a36Sopenharmony_ci 379962306a36Sopenharmony_ci /* Set up TX beacon contents */ 380062306a36Sopenharmony_ci frame_size = 380162306a36Sopenharmony_ci il4965_fill_beacon_frame(il, tx_beacon_cmd->frame, 380262306a36Sopenharmony_ci sizeof(frame->u) - sizeof(*tx_beacon_cmd)); 380362306a36Sopenharmony_ci if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE)) 380462306a36Sopenharmony_ci return 0; 380562306a36Sopenharmony_ci if (!frame_size) 380662306a36Sopenharmony_ci return 0; 380762306a36Sopenharmony_ci 380862306a36Sopenharmony_ci /* Set up TX command fields */ 380962306a36Sopenharmony_ci tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size); 381062306a36Sopenharmony_ci tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id; 381162306a36Sopenharmony_ci tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; 381262306a36Sopenharmony_ci tx_beacon_cmd->tx.tx_flags = 381362306a36Sopenharmony_ci TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK | 381462306a36Sopenharmony_ci TX_CMD_FLG_STA_RATE_MSK; 381562306a36Sopenharmony_ci 381662306a36Sopenharmony_ci /* Set up TX beacon command fields */ 381762306a36Sopenharmony_ci il4965_set_beacon_tim(il, tx_beacon_cmd, (u8 *) tx_beacon_cmd->frame, 381862306a36Sopenharmony_ci frame_size); 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci /* Set up packet rate and flags */ 382162306a36Sopenharmony_ci rate = il_get_lowest_plcp(il); 382262306a36Sopenharmony_ci il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant); 382362306a36Sopenharmony_ci rate_flags = BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS; 382462306a36Sopenharmony_ci if ((rate >= IL_FIRST_CCK_RATE) && (rate <= IL_LAST_CCK_RATE)) 382562306a36Sopenharmony_ci rate_flags |= RATE_MCS_CCK_MSK; 382662306a36Sopenharmony_ci tx_beacon_cmd->tx.rate_n_flags = cpu_to_le32(rate | rate_flags); 382762306a36Sopenharmony_ci 382862306a36Sopenharmony_ci return sizeof(*tx_beacon_cmd) + frame_size; 382962306a36Sopenharmony_ci} 383062306a36Sopenharmony_ci 383162306a36Sopenharmony_ciint 383262306a36Sopenharmony_ciil4965_send_beacon_cmd(struct il_priv *il) 383362306a36Sopenharmony_ci{ 383462306a36Sopenharmony_ci struct il_frame *frame; 383562306a36Sopenharmony_ci unsigned int frame_size; 383662306a36Sopenharmony_ci int rc; 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci frame = il4965_get_free_frame(il); 383962306a36Sopenharmony_ci if (!frame) { 384062306a36Sopenharmony_ci IL_ERR("Could not obtain free frame buffer for beacon " 384162306a36Sopenharmony_ci "command.\n"); 384262306a36Sopenharmony_ci return -ENOMEM; 384362306a36Sopenharmony_ci } 384462306a36Sopenharmony_ci 384562306a36Sopenharmony_ci frame_size = il4965_hw_get_beacon_cmd(il, frame); 384662306a36Sopenharmony_ci if (!frame_size) { 384762306a36Sopenharmony_ci IL_ERR("Error configuring the beacon command\n"); 384862306a36Sopenharmony_ci il4965_free_frame(il, frame); 384962306a36Sopenharmony_ci return -EINVAL; 385062306a36Sopenharmony_ci } 385162306a36Sopenharmony_ci 385262306a36Sopenharmony_ci rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]); 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_ci il4965_free_frame(il, frame); 385562306a36Sopenharmony_ci 385662306a36Sopenharmony_ci return rc; 385762306a36Sopenharmony_ci} 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_cistatic inline dma_addr_t 386062306a36Sopenharmony_ciil4965_tfd_tb_get_addr(struct il_tfd *tfd, u8 idx) 386162306a36Sopenharmony_ci{ 386262306a36Sopenharmony_ci struct il_tfd_tb *tb = &tfd->tbs[idx]; 386362306a36Sopenharmony_ci 386462306a36Sopenharmony_ci dma_addr_t addr = get_unaligned_le32(&tb->lo); 386562306a36Sopenharmony_ci if (sizeof(dma_addr_t) > sizeof(u32)) 386662306a36Sopenharmony_ci addr |= 386762306a36Sopenharmony_ci ((dma_addr_t) (le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 386862306a36Sopenharmony_ci 16; 386962306a36Sopenharmony_ci 387062306a36Sopenharmony_ci return addr; 387162306a36Sopenharmony_ci} 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_cistatic inline u16 387462306a36Sopenharmony_ciil4965_tfd_tb_get_len(struct il_tfd *tfd, u8 idx) 387562306a36Sopenharmony_ci{ 387662306a36Sopenharmony_ci struct il_tfd_tb *tb = &tfd->tbs[idx]; 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci return le16_to_cpu(tb->hi_n_len) >> 4; 387962306a36Sopenharmony_ci} 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_cistatic inline void 388262306a36Sopenharmony_ciil4965_tfd_set_tb(struct il_tfd *tfd, u8 idx, dma_addr_t addr, u16 len) 388362306a36Sopenharmony_ci{ 388462306a36Sopenharmony_ci struct il_tfd_tb *tb = &tfd->tbs[idx]; 388562306a36Sopenharmony_ci u16 hi_n_len = len << 4; 388662306a36Sopenharmony_ci 388762306a36Sopenharmony_ci put_unaligned_le32(addr, &tb->lo); 388862306a36Sopenharmony_ci if (sizeof(dma_addr_t) > sizeof(u32)) 388962306a36Sopenharmony_ci hi_n_len |= ((addr >> 16) >> 16) & 0xF; 389062306a36Sopenharmony_ci 389162306a36Sopenharmony_ci tb->hi_n_len = cpu_to_le16(hi_n_len); 389262306a36Sopenharmony_ci 389362306a36Sopenharmony_ci tfd->num_tbs = idx + 1; 389462306a36Sopenharmony_ci} 389562306a36Sopenharmony_ci 389662306a36Sopenharmony_cistatic inline u8 389762306a36Sopenharmony_ciil4965_tfd_get_num_tbs(struct il_tfd *tfd) 389862306a36Sopenharmony_ci{ 389962306a36Sopenharmony_ci return tfd->num_tbs & 0x1f; 390062306a36Sopenharmony_ci} 390162306a36Sopenharmony_ci 390262306a36Sopenharmony_ci/* 390362306a36Sopenharmony_ci * il4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] 390462306a36Sopenharmony_ci * 390562306a36Sopenharmony_ci * Does NOT advance any TFD circular buffer read/write idxes 390662306a36Sopenharmony_ci * Does NOT free the TFD itself (which is within circular buffer) 390762306a36Sopenharmony_ci */ 390862306a36Sopenharmony_civoid 390962306a36Sopenharmony_ciil4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq) 391062306a36Sopenharmony_ci{ 391162306a36Sopenharmony_ci struct il_tfd *tfd_tmp = (struct il_tfd *)txq->tfds; 391262306a36Sopenharmony_ci struct il_tfd *tfd; 391362306a36Sopenharmony_ci struct pci_dev *dev = il->pci_dev; 391462306a36Sopenharmony_ci int idx = txq->q.read_ptr; 391562306a36Sopenharmony_ci int i; 391662306a36Sopenharmony_ci int num_tbs; 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_ci tfd = &tfd_tmp[idx]; 391962306a36Sopenharmony_ci 392062306a36Sopenharmony_ci /* Sanity check on number of chunks */ 392162306a36Sopenharmony_ci num_tbs = il4965_tfd_get_num_tbs(tfd); 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci if (num_tbs >= IL_NUM_OF_TBS) { 392462306a36Sopenharmony_ci IL_ERR("Too many chunks: %i\n", num_tbs); 392562306a36Sopenharmony_ci /* @todo issue fatal error, it is quite serious situation */ 392662306a36Sopenharmony_ci return; 392762306a36Sopenharmony_ci } 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci /* Unmap tx_cmd */ 393062306a36Sopenharmony_ci if (num_tbs) 393162306a36Sopenharmony_ci dma_unmap_single(&dev->dev, 393262306a36Sopenharmony_ci dma_unmap_addr(&txq->meta[idx], mapping), 393362306a36Sopenharmony_ci dma_unmap_len(&txq->meta[idx], len), 393462306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci /* Unmap chunks, if any. */ 393762306a36Sopenharmony_ci for (i = 1; i < num_tbs; i++) 393862306a36Sopenharmony_ci dma_unmap_single(&dev->dev, il4965_tfd_tb_get_addr(tfd, i), 393962306a36Sopenharmony_ci il4965_tfd_tb_get_len(tfd, i), DMA_TO_DEVICE); 394062306a36Sopenharmony_ci 394162306a36Sopenharmony_ci /* free SKB */ 394262306a36Sopenharmony_ci if (txq->skbs) { 394362306a36Sopenharmony_ci struct sk_buff *skb = txq->skbs[txq->q.read_ptr]; 394462306a36Sopenharmony_ci 394562306a36Sopenharmony_ci /* can be called from irqs-disabled context */ 394662306a36Sopenharmony_ci if (skb) { 394762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 394862306a36Sopenharmony_ci txq->skbs[txq->q.read_ptr] = NULL; 394962306a36Sopenharmony_ci } 395062306a36Sopenharmony_ci } 395162306a36Sopenharmony_ci} 395262306a36Sopenharmony_ci 395362306a36Sopenharmony_ciint 395462306a36Sopenharmony_ciil4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, 395562306a36Sopenharmony_ci dma_addr_t addr, u16 len, u8 reset, u8 pad) 395662306a36Sopenharmony_ci{ 395762306a36Sopenharmony_ci struct il_queue *q; 395862306a36Sopenharmony_ci struct il_tfd *tfd, *tfd_tmp; 395962306a36Sopenharmony_ci u32 num_tbs; 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_ci q = &txq->q; 396262306a36Sopenharmony_ci tfd_tmp = (struct il_tfd *)txq->tfds; 396362306a36Sopenharmony_ci tfd = &tfd_tmp[q->write_ptr]; 396462306a36Sopenharmony_ci 396562306a36Sopenharmony_ci if (reset) 396662306a36Sopenharmony_ci memset(tfd, 0, sizeof(*tfd)); 396762306a36Sopenharmony_ci 396862306a36Sopenharmony_ci num_tbs = il4965_tfd_get_num_tbs(tfd); 396962306a36Sopenharmony_ci 397062306a36Sopenharmony_ci /* Each TFD can point to a maximum 20 Tx buffers */ 397162306a36Sopenharmony_ci if (num_tbs >= IL_NUM_OF_TBS) { 397262306a36Sopenharmony_ci IL_ERR("Error can not send more than %d chunks\n", 397362306a36Sopenharmony_ci IL_NUM_OF_TBS); 397462306a36Sopenharmony_ci return -EINVAL; 397562306a36Sopenharmony_ci } 397662306a36Sopenharmony_ci 397762306a36Sopenharmony_ci BUG_ON(addr & ~DMA_BIT_MASK(36)); 397862306a36Sopenharmony_ci if (unlikely(addr & ~IL_TX_DMA_MASK)) 397962306a36Sopenharmony_ci IL_ERR("Unaligned address = %llx\n", (unsigned long long)addr); 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_ci il4965_tfd_set_tb(tfd, num_tbs, addr, len); 398262306a36Sopenharmony_ci 398362306a36Sopenharmony_ci return 0; 398462306a36Sopenharmony_ci} 398562306a36Sopenharmony_ci 398662306a36Sopenharmony_ci/* 398762306a36Sopenharmony_ci * Tell nic where to find circular buffer of Tx Frame Descriptors for 398862306a36Sopenharmony_ci * given Tx queue, and enable the DMA channel used for that queue. 398962306a36Sopenharmony_ci * 399062306a36Sopenharmony_ci * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA 399162306a36Sopenharmony_ci * channels supported in hardware. 399262306a36Sopenharmony_ci */ 399362306a36Sopenharmony_ciint 399462306a36Sopenharmony_ciil4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq) 399562306a36Sopenharmony_ci{ 399662306a36Sopenharmony_ci int txq_id = txq->q.id; 399762306a36Sopenharmony_ci 399862306a36Sopenharmony_ci /* Circular buffer (TFD queue in DRAM) physical base address */ 399962306a36Sopenharmony_ci il_wr(il, FH49_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8); 400062306a36Sopenharmony_ci 400162306a36Sopenharmony_ci return 0; 400262306a36Sopenharmony_ci} 400362306a36Sopenharmony_ci 400462306a36Sopenharmony_ci/****************************************************************************** 400562306a36Sopenharmony_ci * 400662306a36Sopenharmony_ci * Generic RX handler implementations 400762306a36Sopenharmony_ci * 400862306a36Sopenharmony_ci ******************************************************************************/ 400962306a36Sopenharmony_cistatic void 401062306a36Sopenharmony_ciil4965_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb) 401162306a36Sopenharmony_ci{ 401262306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 401362306a36Sopenharmony_ci struct il_alive_resp *palive; 401462306a36Sopenharmony_ci struct delayed_work *pwork; 401562306a36Sopenharmony_ci 401662306a36Sopenharmony_ci palive = &pkt->u.alive_frame; 401762306a36Sopenharmony_ci 401862306a36Sopenharmony_ci D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", 401962306a36Sopenharmony_ci palive->is_valid, palive->ver_type, palive->ver_subtype); 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci if (palive->ver_subtype == INITIALIZE_SUBTYPE) { 402262306a36Sopenharmony_ci D_INFO("Initialization Alive received.\n"); 402362306a36Sopenharmony_ci memcpy(&il->card_alive_init, &pkt->u.raw, 402462306a36Sopenharmony_ci sizeof(struct il_init_alive_resp)); 402562306a36Sopenharmony_ci pwork = &il->init_alive_start; 402662306a36Sopenharmony_ci } else { 402762306a36Sopenharmony_ci D_INFO("Runtime Alive received.\n"); 402862306a36Sopenharmony_ci memcpy(&il->card_alive, &pkt->u.alive_frame, 402962306a36Sopenharmony_ci sizeof(struct il_alive_resp)); 403062306a36Sopenharmony_ci pwork = &il->alive_start; 403162306a36Sopenharmony_ci } 403262306a36Sopenharmony_ci 403362306a36Sopenharmony_ci /* We delay the ALIVE response by 5ms to 403462306a36Sopenharmony_ci * give the HW RF Kill time to activate... */ 403562306a36Sopenharmony_ci if (palive->is_valid == UCODE_VALID_OK) 403662306a36Sopenharmony_ci queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5)); 403762306a36Sopenharmony_ci else 403862306a36Sopenharmony_ci IL_WARN("uCode did not respond OK.\n"); 403962306a36Sopenharmony_ci} 404062306a36Sopenharmony_ci 404162306a36Sopenharmony_ci/* 404262306a36Sopenharmony_ci * il4965_bg_stats_periodic - Timer callback to queue stats 404362306a36Sopenharmony_ci * 404462306a36Sopenharmony_ci * This callback is provided in order to send a stats request. 404562306a36Sopenharmony_ci * 404662306a36Sopenharmony_ci * This timer function is continually reset to execute within 404762306a36Sopenharmony_ci * 60 seconds since the last N_STATS was received. We need to 404862306a36Sopenharmony_ci * ensure we receive the stats in order to update the temperature 404962306a36Sopenharmony_ci * used for calibrating the TXPOWER. 405062306a36Sopenharmony_ci */ 405162306a36Sopenharmony_cistatic void 405262306a36Sopenharmony_ciil4965_bg_stats_periodic(struct timer_list *t) 405362306a36Sopenharmony_ci{ 405462306a36Sopenharmony_ci struct il_priv *il = from_timer(il, t, stats_periodic); 405562306a36Sopenharmony_ci 405662306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 405762306a36Sopenharmony_ci return; 405862306a36Sopenharmony_ci 405962306a36Sopenharmony_ci /* dont send host command if rf-kill is on */ 406062306a36Sopenharmony_ci if (!il_is_ready_rf(il)) 406162306a36Sopenharmony_ci return; 406262306a36Sopenharmony_ci 406362306a36Sopenharmony_ci il_send_stats_request(il, CMD_ASYNC, false); 406462306a36Sopenharmony_ci} 406562306a36Sopenharmony_ci 406662306a36Sopenharmony_cistatic void 406762306a36Sopenharmony_ciil4965_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb) 406862306a36Sopenharmony_ci{ 406962306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 407062306a36Sopenharmony_ci struct il4965_beacon_notif *beacon = 407162306a36Sopenharmony_ci (struct il4965_beacon_notif *)pkt->u.raw; 407262306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 407362306a36Sopenharmony_ci u8 rate = il4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); 407462306a36Sopenharmony_ci 407562306a36Sopenharmony_ci D_RX("beacon status %x retries %d iss %d tsf:0x%.8x%.8x rate %d\n", 407662306a36Sopenharmony_ci le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK, 407762306a36Sopenharmony_ci beacon->beacon_notify_hdr.failure_frame, 407862306a36Sopenharmony_ci le32_to_cpu(beacon->ibss_mgr_status), 407962306a36Sopenharmony_ci le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); 408062306a36Sopenharmony_ci#endif 408162306a36Sopenharmony_ci il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); 408262306a36Sopenharmony_ci} 408362306a36Sopenharmony_ci 408462306a36Sopenharmony_cistatic void 408562306a36Sopenharmony_ciil4965_perform_ct_kill_task(struct il_priv *il) 408662306a36Sopenharmony_ci{ 408762306a36Sopenharmony_ci unsigned long flags; 408862306a36Sopenharmony_ci 408962306a36Sopenharmony_ci D_POWER("Stop all queues\n"); 409062306a36Sopenharmony_ci 409162306a36Sopenharmony_ci if (il->mac80211_registered) 409262306a36Sopenharmony_ci ieee80211_stop_queues(il->hw); 409362306a36Sopenharmony_ci 409462306a36Sopenharmony_ci _il_wr(il, CSR_UCODE_DRV_GP1_SET, 409562306a36Sopenharmony_ci CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); 409662306a36Sopenharmony_ci _il_rd(il, CSR_UCODE_DRV_GP1); 409762306a36Sopenharmony_ci 409862306a36Sopenharmony_ci spin_lock_irqsave(&il->reg_lock, flags); 409962306a36Sopenharmony_ci if (likely(_il_grab_nic_access(il))) 410062306a36Sopenharmony_ci _il_release_nic_access(il); 410162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->reg_lock, flags); 410262306a36Sopenharmony_ci} 410362306a36Sopenharmony_ci 410462306a36Sopenharmony_ci/* Handle notification from uCode that card's power state is changing 410562306a36Sopenharmony_ci * due to software, hardware, or critical temperature RFKILL */ 410662306a36Sopenharmony_cistatic void 410762306a36Sopenharmony_ciil4965_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb) 410862306a36Sopenharmony_ci{ 410962306a36Sopenharmony_ci struct il_rx_pkt *pkt = rxb_addr(rxb); 411062306a36Sopenharmony_ci u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); 411162306a36Sopenharmony_ci unsigned long status = il->status; 411262306a36Sopenharmony_ci 411362306a36Sopenharmony_ci D_RF_KILL("Card state received: HW:%s SW:%s CT:%s\n", 411462306a36Sopenharmony_ci (flags & HW_CARD_DISABLED) ? "Kill" : "On", 411562306a36Sopenharmony_ci (flags & SW_CARD_DISABLED) ? "Kill" : "On", 411662306a36Sopenharmony_ci (flags & CT_CARD_DISABLED) ? "Reached" : "Not reached"); 411762306a36Sopenharmony_ci 411862306a36Sopenharmony_ci if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | CT_CARD_DISABLED)) { 411962306a36Sopenharmony_ci 412062306a36Sopenharmony_ci _il_wr(il, CSR_UCODE_DRV_GP1_SET, 412162306a36Sopenharmony_ci CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 412262306a36Sopenharmony_ci 412362306a36Sopenharmony_ci il_wr(il, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); 412462306a36Sopenharmony_ci 412562306a36Sopenharmony_ci if (!(flags & RXON_CARD_DISABLED)) { 412662306a36Sopenharmony_ci _il_wr(il, CSR_UCODE_DRV_GP1_CLR, 412762306a36Sopenharmony_ci CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 412862306a36Sopenharmony_ci il_wr(il, HBUS_TARG_MBX_C, 412962306a36Sopenharmony_ci HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); 413062306a36Sopenharmony_ci } 413162306a36Sopenharmony_ci } 413262306a36Sopenharmony_ci 413362306a36Sopenharmony_ci if (flags & CT_CARD_DISABLED) 413462306a36Sopenharmony_ci il4965_perform_ct_kill_task(il); 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci if (flags & HW_CARD_DISABLED) 413762306a36Sopenharmony_ci set_bit(S_RFKILL, &il->status); 413862306a36Sopenharmony_ci else 413962306a36Sopenharmony_ci clear_bit(S_RFKILL, &il->status); 414062306a36Sopenharmony_ci 414162306a36Sopenharmony_ci if (!(flags & RXON_CARD_DISABLED)) 414262306a36Sopenharmony_ci il_scan_cancel(il); 414362306a36Sopenharmony_ci 414462306a36Sopenharmony_ci if ((test_bit(S_RFKILL, &status) != 414562306a36Sopenharmony_ci test_bit(S_RFKILL, &il->status))) 414662306a36Sopenharmony_ci wiphy_rfkill_set_hw_state(il->hw->wiphy, 414762306a36Sopenharmony_ci test_bit(S_RFKILL, &il->status)); 414862306a36Sopenharmony_ci else 414962306a36Sopenharmony_ci wake_up(&il->wait_command_queue); 415062306a36Sopenharmony_ci} 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci/* 415362306a36Sopenharmony_ci * il4965_setup_handlers - Initialize Rx handler callbacks 415462306a36Sopenharmony_ci * 415562306a36Sopenharmony_ci * Setup the RX handlers for each of the reply types sent from the uCode 415662306a36Sopenharmony_ci * to the host. 415762306a36Sopenharmony_ci * 415862306a36Sopenharmony_ci * This function chains into the hardware specific files for them to setup 415962306a36Sopenharmony_ci * any hardware specific handlers as well. 416062306a36Sopenharmony_ci */ 416162306a36Sopenharmony_cistatic void 416262306a36Sopenharmony_ciil4965_setup_handlers(struct il_priv *il) 416362306a36Sopenharmony_ci{ 416462306a36Sopenharmony_ci il->handlers[N_ALIVE] = il4965_hdl_alive; 416562306a36Sopenharmony_ci il->handlers[N_ERROR] = il_hdl_error; 416662306a36Sopenharmony_ci il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa; 416762306a36Sopenharmony_ci il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement; 416862306a36Sopenharmony_ci il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep; 416962306a36Sopenharmony_ci il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats; 417062306a36Sopenharmony_ci il->handlers[N_BEACON] = il4965_hdl_beacon; 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_ci /* 417362306a36Sopenharmony_ci * The same handler is used for both the REPLY to a discrete 417462306a36Sopenharmony_ci * stats request from the host as well as for the periodic 417562306a36Sopenharmony_ci * stats notifications (after received beacons) from the uCode. 417662306a36Sopenharmony_ci */ 417762306a36Sopenharmony_ci il->handlers[C_STATS] = il4965_hdl_c_stats; 417862306a36Sopenharmony_ci il->handlers[N_STATS] = il4965_hdl_stats; 417962306a36Sopenharmony_ci 418062306a36Sopenharmony_ci il_setup_rx_scan_handlers(il); 418162306a36Sopenharmony_ci 418262306a36Sopenharmony_ci /* status change handler */ 418362306a36Sopenharmony_ci il->handlers[N_CARD_STATE] = il4965_hdl_card_state; 418462306a36Sopenharmony_ci 418562306a36Sopenharmony_ci il->handlers[N_MISSED_BEACONS] = il4965_hdl_missed_beacon; 418662306a36Sopenharmony_ci /* Rx handlers */ 418762306a36Sopenharmony_ci il->handlers[N_RX_PHY] = il4965_hdl_rx_phy; 418862306a36Sopenharmony_ci il->handlers[N_RX_MPDU] = il4965_hdl_rx; 418962306a36Sopenharmony_ci il->handlers[N_RX] = il4965_hdl_rx; 419062306a36Sopenharmony_ci /* block ack */ 419162306a36Sopenharmony_ci il->handlers[N_COMPRESSED_BA] = il4965_hdl_compressed_ba; 419262306a36Sopenharmony_ci /* Tx response */ 419362306a36Sopenharmony_ci il->handlers[C_TX] = il4965_hdl_tx; 419462306a36Sopenharmony_ci} 419562306a36Sopenharmony_ci 419662306a36Sopenharmony_ci/* 419762306a36Sopenharmony_ci * il4965_rx_handle - Main entry function for receiving responses from uCode 419862306a36Sopenharmony_ci * 419962306a36Sopenharmony_ci * Uses the il->handlers callback function array to invoke 420062306a36Sopenharmony_ci * the appropriate handlers, including command responses, 420162306a36Sopenharmony_ci * frame-received notifications, and other notifications. 420262306a36Sopenharmony_ci */ 420362306a36Sopenharmony_civoid 420462306a36Sopenharmony_ciil4965_rx_handle(struct il_priv *il) 420562306a36Sopenharmony_ci{ 420662306a36Sopenharmony_ci struct il_rx_buf *rxb; 420762306a36Sopenharmony_ci struct il_rx_pkt *pkt; 420862306a36Sopenharmony_ci struct il_rx_queue *rxq = &il->rxq; 420962306a36Sopenharmony_ci u32 r, i; 421062306a36Sopenharmony_ci int reclaim; 421162306a36Sopenharmony_ci unsigned long flags; 421262306a36Sopenharmony_ci u8 fill_rx = 0; 421362306a36Sopenharmony_ci u32 count = 8; 421462306a36Sopenharmony_ci int total_empty; 421562306a36Sopenharmony_ci 421662306a36Sopenharmony_ci /* uCode's read idx (stored in shared DRAM) indicates the last Rx 421762306a36Sopenharmony_ci * buffer that the driver may process (last buffer filled by ucode). */ 421862306a36Sopenharmony_ci r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; 421962306a36Sopenharmony_ci i = rxq->read; 422062306a36Sopenharmony_ci 422162306a36Sopenharmony_ci /* Rx interrupt, but nothing sent from uCode */ 422262306a36Sopenharmony_ci if (i == r) 422362306a36Sopenharmony_ci D_RX("r = %d, i = %d\n", r, i); 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_ci /* calculate total frames need to be restock after handling RX */ 422662306a36Sopenharmony_ci total_empty = r - rxq->write_actual; 422762306a36Sopenharmony_ci if (total_empty < 0) 422862306a36Sopenharmony_ci total_empty += RX_QUEUE_SIZE; 422962306a36Sopenharmony_ci 423062306a36Sopenharmony_ci if (total_empty > (RX_QUEUE_SIZE / 2)) 423162306a36Sopenharmony_ci fill_rx = 1; 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci while (i != r) { 423462306a36Sopenharmony_ci int len; 423562306a36Sopenharmony_ci 423662306a36Sopenharmony_ci rxb = rxq->queue[i]; 423762306a36Sopenharmony_ci 423862306a36Sopenharmony_ci /* If an RXB doesn't have a Rx queue slot associated with it, 423962306a36Sopenharmony_ci * then a bug has been introduced in the queue refilling 424062306a36Sopenharmony_ci * routines -- catch it here */ 424162306a36Sopenharmony_ci BUG_ON(rxb == NULL); 424262306a36Sopenharmony_ci 424362306a36Sopenharmony_ci rxq->queue[i] = NULL; 424462306a36Sopenharmony_ci 424562306a36Sopenharmony_ci dma_unmap_page(&il->pci_dev->dev, rxb->page_dma, 424662306a36Sopenharmony_ci PAGE_SIZE << il->hw_params.rx_page_order, 424762306a36Sopenharmony_ci DMA_FROM_DEVICE); 424862306a36Sopenharmony_ci pkt = rxb_addr(rxb); 424962306a36Sopenharmony_ci 425062306a36Sopenharmony_ci len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; 425162306a36Sopenharmony_ci len += sizeof(u32); /* account for status word */ 425262306a36Sopenharmony_ci 425362306a36Sopenharmony_ci reclaim = il_need_reclaim(il, pkt); 425462306a36Sopenharmony_ci 425562306a36Sopenharmony_ci /* Based on type of command response or notification, 425662306a36Sopenharmony_ci * handle those that need handling via function in 425762306a36Sopenharmony_ci * handlers table. See il4965_setup_handlers() */ 425862306a36Sopenharmony_ci if (il->handlers[pkt->hdr.cmd]) { 425962306a36Sopenharmony_ci D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i, 426062306a36Sopenharmony_ci il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); 426162306a36Sopenharmony_ci il->isr_stats.handlers[pkt->hdr.cmd]++; 426262306a36Sopenharmony_ci il->handlers[pkt->hdr.cmd] (il, rxb); 426362306a36Sopenharmony_ci } else { 426462306a36Sopenharmony_ci /* No handling needed */ 426562306a36Sopenharmony_ci D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r, 426662306a36Sopenharmony_ci i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); 426762306a36Sopenharmony_ci } 426862306a36Sopenharmony_ci 426962306a36Sopenharmony_ci /* 427062306a36Sopenharmony_ci * XXX: After here, we should always check rxb->page 427162306a36Sopenharmony_ci * against NULL before touching it or its virtual 427262306a36Sopenharmony_ci * memory (pkt). Because some handler might have 427362306a36Sopenharmony_ci * already taken or freed the pages. 427462306a36Sopenharmony_ci */ 427562306a36Sopenharmony_ci 427662306a36Sopenharmony_ci if (reclaim) { 427762306a36Sopenharmony_ci /* Invoke any callbacks, transfer the buffer to caller, 427862306a36Sopenharmony_ci * and fire off the (possibly) blocking il_send_cmd() 427962306a36Sopenharmony_ci * as we reclaim the driver command queue */ 428062306a36Sopenharmony_ci if (rxb->page) 428162306a36Sopenharmony_ci il_tx_cmd_complete(il, rxb); 428262306a36Sopenharmony_ci else 428362306a36Sopenharmony_ci IL_WARN("Claim null rxb?\n"); 428462306a36Sopenharmony_ci } 428562306a36Sopenharmony_ci 428662306a36Sopenharmony_ci /* Reuse the page if possible. For notification packets and 428762306a36Sopenharmony_ci * SKBs that fail to Rx correctly, add them back into the 428862306a36Sopenharmony_ci * rx_free list for reuse later. */ 428962306a36Sopenharmony_ci spin_lock_irqsave(&rxq->lock, flags); 429062306a36Sopenharmony_ci if (rxb->page != NULL) { 429162306a36Sopenharmony_ci rxb->page_dma = 429262306a36Sopenharmony_ci dma_map_page(&il->pci_dev->dev, rxb->page, 0, 429362306a36Sopenharmony_ci PAGE_SIZE << il->hw_params.rx_page_order, 429462306a36Sopenharmony_ci DMA_FROM_DEVICE); 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ci if (unlikely(dma_mapping_error(&il->pci_dev->dev, 429762306a36Sopenharmony_ci rxb->page_dma))) { 429862306a36Sopenharmony_ci __il_free_pages(il, rxb->page); 429962306a36Sopenharmony_ci rxb->page = NULL; 430062306a36Sopenharmony_ci list_add_tail(&rxb->list, &rxq->rx_used); 430162306a36Sopenharmony_ci } else { 430262306a36Sopenharmony_ci list_add_tail(&rxb->list, &rxq->rx_free); 430362306a36Sopenharmony_ci rxq->free_count++; 430462306a36Sopenharmony_ci } 430562306a36Sopenharmony_ci } else 430662306a36Sopenharmony_ci list_add_tail(&rxb->list, &rxq->rx_used); 430762306a36Sopenharmony_ci 430862306a36Sopenharmony_ci spin_unlock_irqrestore(&rxq->lock, flags); 430962306a36Sopenharmony_ci 431062306a36Sopenharmony_ci i = (i + 1) & RX_QUEUE_MASK; 431162306a36Sopenharmony_ci /* If there are a lot of unused frames, 431262306a36Sopenharmony_ci * restock the Rx queue so ucode wont assert. */ 431362306a36Sopenharmony_ci if (fill_rx) { 431462306a36Sopenharmony_ci count++; 431562306a36Sopenharmony_ci if (count >= 8) { 431662306a36Sopenharmony_ci rxq->read = i; 431762306a36Sopenharmony_ci il4965_rx_replenish_now(il); 431862306a36Sopenharmony_ci count = 0; 431962306a36Sopenharmony_ci } 432062306a36Sopenharmony_ci } 432162306a36Sopenharmony_ci } 432262306a36Sopenharmony_ci 432362306a36Sopenharmony_ci /* Backtrack one entry */ 432462306a36Sopenharmony_ci rxq->read = i; 432562306a36Sopenharmony_ci if (fill_rx) 432662306a36Sopenharmony_ci il4965_rx_replenish_now(il); 432762306a36Sopenharmony_ci else 432862306a36Sopenharmony_ci il4965_rx_queue_restock(il); 432962306a36Sopenharmony_ci} 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_ci/* call this function to flush any scheduled tasklet */ 433262306a36Sopenharmony_cistatic inline void 433362306a36Sopenharmony_ciil4965_synchronize_irq(struct il_priv *il) 433462306a36Sopenharmony_ci{ 433562306a36Sopenharmony_ci /* wait to make sure we flush pending tasklet */ 433662306a36Sopenharmony_ci synchronize_irq(il->pci_dev->irq); 433762306a36Sopenharmony_ci tasklet_kill(&il->irq_tasklet); 433862306a36Sopenharmony_ci} 433962306a36Sopenharmony_ci 434062306a36Sopenharmony_cistatic void 434162306a36Sopenharmony_ciil4965_irq_tasklet(struct tasklet_struct *t) 434262306a36Sopenharmony_ci{ 434362306a36Sopenharmony_ci struct il_priv *il = from_tasklet(il, t, irq_tasklet); 434462306a36Sopenharmony_ci u32 inta, handled = 0; 434562306a36Sopenharmony_ci u32 inta_fh; 434662306a36Sopenharmony_ci unsigned long flags; 434762306a36Sopenharmony_ci u32 i; 434862306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 434962306a36Sopenharmony_ci u32 inta_mask; 435062306a36Sopenharmony_ci#endif 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 435362306a36Sopenharmony_ci 435462306a36Sopenharmony_ci /* Ack/clear/reset pending uCode interrupts. 435562306a36Sopenharmony_ci * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, 435662306a36Sopenharmony_ci * and will clear only when CSR_FH_INT_STATUS gets cleared. */ 435762306a36Sopenharmony_ci inta = _il_rd(il, CSR_INT); 435862306a36Sopenharmony_ci _il_wr(il, CSR_INT, inta); 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci /* Ack/clear/reset pending flow-handler (DMA) interrupts. 436162306a36Sopenharmony_ci * Any new interrupts that happen after this, either while we're 436262306a36Sopenharmony_ci * in this tasklet, or later, will show up in next ISR/tasklet. */ 436362306a36Sopenharmony_ci inta_fh = _il_rd(il, CSR_FH_INT_STATUS); 436462306a36Sopenharmony_ci _il_wr(il, CSR_FH_INT_STATUS, inta_fh); 436562306a36Sopenharmony_ci 436662306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 436762306a36Sopenharmony_ci if (il_get_debug_level(il) & IL_DL_ISR) { 436862306a36Sopenharmony_ci /* just for debug */ 436962306a36Sopenharmony_ci inta_mask = _il_rd(il, CSR_INT_MASK); 437062306a36Sopenharmony_ci D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, 437162306a36Sopenharmony_ci inta_mask, inta_fh); 437262306a36Sopenharmony_ci } 437362306a36Sopenharmony_ci#endif 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 437662306a36Sopenharmony_ci 437762306a36Sopenharmony_ci /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not 437862306a36Sopenharmony_ci * atomic, make sure that inta covers all the interrupts that 437962306a36Sopenharmony_ci * we've discovered, even if FH interrupt came in just after 438062306a36Sopenharmony_ci * reading CSR_INT. */ 438162306a36Sopenharmony_ci if (inta_fh & CSR49_FH_INT_RX_MASK) 438262306a36Sopenharmony_ci inta |= CSR_INT_BIT_FH_RX; 438362306a36Sopenharmony_ci if (inta_fh & CSR49_FH_INT_TX_MASK) 438462306a36Sopenharmony_ci inta |= CSR_INT_BIT_FH_TX; 438562306a36Sopenharmony_ci 438662306a36Sopenharmony_ci /* Now service all interrupt bits discovered above. */ 438762306a36Sopenharmony_ci if (inta & CSR_INT_BIT_HW_ERR) { 438862306a36Sopenharmony_ci IL_ERR("Hardware error detected. Restarting.\n"); 438962306a36Sopenharmony_ci 439062306a36Sopenharmony_ci /* Tell the device to stop sending interrupts */ 439162306a36Sopenharmony_ci il_disable_interrupts(il); 439262306a36Sopenharmony_ci 439362306a36Sopenharmony_ci il->isr_stats.hw++; 439462306a36Sopenharmony_ci il_irq_handle_error(il); 439562306a36Sopenharmony_ci 439662306a36Sopenharmony_ci handled |= CSR_INT_BIT_HW_ERR; 439762306a36Sopenharmony_ci 439862306a36Sopenharmony_ci return; 439962306a36Sopenharmony_ci } 440062306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 440162306a36Sopenharmony_ci if (il_get_debug_level(il) & (IL_DL_ISR)) { 440262306a36Sopenharmony_ci /* NIC fires this, but we don't use it, redundant with WAKEUP */ 440362306a36Sopenharmony_ci if (inta & CSR_INT_BIT_SCD) { 440462306a36Sopenharmony_ci D_ISR("Scheduler finished to transmit " 440562306a36Sopenharmony_ci "the frame/frames.\n"); 440662306a36Sopenharmony_ci il->isr_stats.sch++; 440762306a36Sopenharmony_ci } 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci /* Alive notification via Rx interrupt will do the real work */ 441062306a36Sopenharmony_ci if (inta & CSR_INT_BIT_ALIVE) { 441162306a36Sopenharmony_ci D_ISR("Alive interrupt\n"); 441262306a36Sopenharmony_ci il->isr_stats.alive++; 441362306a36Sopenharmony_ci } 441462306a36Sopenharmony_ci } 441562306a36Sopenharmony_ci#endif 441662306a36Sopenharmony_ci /* Safely ignore these bits for debug checks below */ 441762306a36Sopenharmony_ci inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); 441862306a36Sopenharmony_ci 441962306a36Sopenharmony_ci /* HW RF KILL switch toggled */ 442062306a36Sopenharmony_ci if (inta & CSR_INT_BIT_RF_KILL) { 442162306a36Sopenharmony_ci int hw_rf_kill = 0; 442262306a36Sopenharmony_ci 442362306a36Sopenharmony_ci if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) 442462306a36Sopenharmony_ci hw_rf_kill = 1; 442562306a36Sopenharmony_ci 442662306a36Sopenharmony_ci IL_WARN("RF_KILL bit toggled to %s.\n", 442762306a36Sopenharmony_ci hw_rf_kill ? "disable radio" : "enable radio"); 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_ci il->isr_stats.rfkill++; 443062306a36Sopenharmony_ci 443162306a36Sopenharmony_ci /* driver only loads ucode once setting the interface up. 443262306a36Sopenharmony_ci * the driver allows loading the ucode even if the radio 443362306a36Sopenharmony_ci * is killed. Hence update the killswitch state here. The 443462306a36Sopenharmony_ci * rfkill handler will care about restarting if needed. 443562306a36Sopenharmony_ci */ 443662306a36Sopenharmony_ci if (hw_rf_kill) { 443762306a36Sopenharmony_ci set_bit(S_RFKILL, &il->status); 443862306a36Sopenharmony_ci } else { 443962306a36Sopenharmony_ci clear_bit(S_RFKILL, &il->status); 444062306a36Sopenharmony_ci il_force_reset(il, true); 444162306a36Sopenharmony_ci } 444262306a36Sopenharmony_ci wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); 444362306a36Sopenharmony_ci 444462306a36Sopenharmony_ci handled |= CSR_INT_BIT_RF_KILL; 444562306a36Sopenharmony_ci } 444662306a36Sopenharmony_ci 444762306a36Sopenharmony_ci /* Chip got too hot and stopped itself */ 444862306a36Sopenharmony_ci if (inta & CSR_INT_BIT_CT_KILL) { 444962306a36Sopenharmony_ci IL_ERR("Microcode CT kill error detected.\n"); 445062306a36Sopenharmony_ci il->isr_stats.ctkill++; 445162306a36Sopenharmony_ci handled |= CSR_INT_BIT_CT_KILL; 445262306a36Sopenharmony_ci } 445362306a36Sopenharmony_ci 445462306a36Sopenharmony_ci /* Error detected by uCode */ 445562306a36Sopenharmony_ci if (inta & CSR_INT_BIT_SW_ERR) { 445662306a36Sopenharmony_ci IL_ERR("Microcode SW error detected. " " Restarting 0x%X.\n", 445762306a36Sopenharmony_ci inta); 445862306a36Sopenharmony_ci il->isr_stats.sw++; 445962306a36Sopenharmony_ci il_irq_handle_error(il); 446062306a36Sopenharmony_ci handled |= CSR_INT_BIT_SW_ERR; 446162306a36Sopenharmony_ci } 446262306a36Sopenharmony_ci 446362306a36Sopenharmony_ci /* 446462306a36Sopenharmony_ci * uCode wakes up after power-down sleep. 446562306a36Sopenharmony_ci * Tell device about any new tx or host commands enqueued, 446662306a36Sopenharmony_ci * and about any Rx buffers made available while asleep. 446762306a36Sopenharmony_ci */ 446862306a36Sopenharmony_ci if (inta & CSR_INT_BIT_WAKEUP) { 446962306a36Sopenharmony_ci D_ISR("Wakeup interrupt\n"); 447062306a36Sopenharmony_ci il_rx_queue_update_write_ptr(il, &il->rxq); 447162306a36Sopenharmony_ci for (i = 0; i < il->hw_params.max_txq_num; i++) 447262306a36Sopenharmony_ci il_txq_update_write_ptr(il, &il->txq[i]); 447362306a36Sopenharmony_ci il->isr_stats.wakeup++; 447462306a36Sopenharmony_ci handled |= CSR_INT_BIT_WAKEUP; 447562306a36Sopenharmony_ci } 447662306a36Sopenharmony_ci 447762306a36Sopenharmony_ci /* All uCode command responses, including Tx command responses, 447862306a36Sopenharmony_ci * Rx "responses" (frame-received notification), and other 447962306a36Sopenharmony_ci * notifications from uCode come through here*/ 448062306a36Sopenharmony_ci if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { 448162306a36Sopenharmony_ci il4965_rx_handle(il); 448262306a36Sopenharmony_ci il->isr_stats.rx++; 448362306a36Sopenharmony_ci handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); 448462306a36Sopenharmony_ci } 448562306a36Sopenharmony_ci 448662306a36Sopenharmony_ci /* This "Tx" DMA channel is used only for loading uCode */ 448762306a36Sopenharmony_ci if (inta & CSR_INT_BIT_FH_TX) { 448862306a36Sopenharmony_ci D_ISR("uCode load interrupt\n"); 448962306a36Sopenharmony_ci il->isr_stats.tx++; 449062306a36Sopenharmony_ci handled |= CSR_INT_BIT_FH_TX; 449162306a36Sopenharmony_ci /* Wake up uCode load routine, now that load is complete */ 449262306a36Sopenharmony_ci il->ucode_write_complete = 1; 449362306a36Sopenharmony_ci wake_up(&il->wait_command_queue); 449462306a36Sopenharmony_ci } 449562306a36Sopenharmony_ci 449662306a36Sopenharmony_ci if (inta & ~handled) { 449762306a36Sopenharmony_ci IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled); 449862306a36Sopenharmony_ci il->isr_stats.unhandled++; 449962306a36Sopenharmony_ci } 450062306a36Sopenharmony_ci 450162306a36Sopenharmony_ci if (inta & ~(il->inta_mask)) { 450262306a36Sopenharmony_ci IL_WARN("Disabled INTA bits 0x%08x were pending\n", 450362306a36Sopenharmony_ci inta & ~il->inta_mask); 450462306a36Sopenharmony_ci IL_WARN(" with FH49_INT = 0x%08x\n", inta_fh); 450562306a36Sopenharmony_ci } 450662306a36Sopenharmony_ci 450762306a36Sopenharmony_ci /* Re-enable all interrupts */ 450862306a36Sopenharmony_ci /* only Re-enable if disabled by irq */ 450962306a36Sopenharmony_ci if (test_bit(S_INT_ENABLED, &il->status)) 451062306a36Sopenharmony_ci il_enable_interrupts(il); 451162306a36Sopenharmony_ci /* Re-enable RF_KILL if it occurred */ 451262306a36Sopenharmony_ci else if (handled & CSR_INT_BIT_RF_KILL) 451362306a36Sopenharmony_ci il_enable_rfkill_int(il); 451462306a36Sopenharmony_ci 451562306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 451662306a36Sopenharmony_ci if (il_get_debug_level(il) & (IL_DL_ISR)) { 451762306a36Sopenharmony_ci inta = _il_rd(il, CSR_INT); 451862306a36Sopenharmony_ci inta_mask = _il_rd(il, CSR_INT_MASK); 451962306a36Sopenharmony_ci inta_fh = _il_rd(il, CSR_FH_INT_STATUS); 452062306a36Sopenharmony_ci D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " 452162306a36Sopenharmony_ci "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); 452262306a36Sopenharmony_ci } 452362306a36Sopenharmony_ci#endif 452462306a36Sopenharmony_ci} 452562306a36Sopenharmony_ci 452662306a36Sopenharmony_ci/***************************************************************************** 452762306a36Sopenharmony_ci * 452862306a36Sopenharmony_ci * sysfs attributes 452962306a36Sopenharmony_ci * 453062306a36Sopenharmony_ci *****************************************************************************/ 453162306a36Sopenharmony_ci 453262306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 453362306a36Sopenharmony_ci 453462306a36Sopenharmony_ci/* 453562306a36Sopenharmony_ci * The following adds a new attribute to the sysfs representation 453662306a36Sopenharmony_ci * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/) 453762306a36Sopenharmony_ci * used for controlling the debug level. 453862306a36Sopenharmony_ci * 453962306a36Sopenharmony_ci * See the level definitions in iwl for details. 454062306a36Sopenharmony_ci * 454162306a36Sopenharmony_ci * The debug_level being managed using sysfs below is a per device debug 454262306a36Sopenharmony_ci * level that is used instead of the global debug level if it (the per 454362306a36Sopenharmony_ci * device debug level) is set. 454462306a36Sopenharmony_ci */ 454562306a36Sopenharmony_cistatic ssize_t 454662306a36Sopenharmony_ciil4965_show_debug_level(struct device *d, struct device_attribute *attr, 454762306a36Sopenharmony_ci char *buf) 454862306a36Sopenharmony_ci{ 454962306a36Sopenharmony_ci struct il_priv *il = dev_get_drvdata(d); 455062306a36Sopenharmony_ci return sprintf(buf, "0x%08X\n", il_get_debug_level(il)); 455162306a36Sopenharmony_ci} 455262306a36Sopenharmony_ci 455362306a36Sopenharmony_cistatic ssize_t 455462306a36Sopenharmony_ciil4965_store_debug_level(struct device *d, struct device_attribute *attr, 455562306a36Sopenharmony_ci const char *buf, size_t count) 455662306a36Sopenharmony_ci{ 455762306a36Sopenharmony_ci struct il_priv *il = dev_get_drvdata(d); 455862306a36Sopenharmony_ci unsigned long val; 455962306a36Sopenharmony_ci int ret; 456062306a36Sopenharmony_ci 456162306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &val); 456262306a36Sopenharmony_ci if (ret) 456362306a36Sopenharmony_ci IL_ERR("%s is not in hex or decimal form.\n", buf); 456462306a36Sopenharmony_ci else 456562306a36Sopenharmony_ci il->debug_level = val; 456662306a36Sopenharmony_ci 456762306a36Sopenharmony_ci return strnlen(buf, count); 456862306a36Sopenharmony_ci} 456962306a36Sopenharmony_ci 457062306a36Sopenharmony_cistatic DEVICE_ATTR(debug_level, 0644, il4965_show_debug_level, 457162306a36Sopenharmony_ci il4965_store_debug_level); 457262306a36Sopenharmony_ci 457362306a36Sopenharmony_ci#endif /* CONFIG_IWLEGACY_DEBUG */ 457462306a36Sopenharmony_ci 457562306a36Sopenharmony_cistatic ssize_t 457662306a36Sopenharmony_ciil4965_show_temperature(struct device *d, struct device_attribute *attr, 457762306a36Sopenharmony_ci char *buf) 457862306a36Sopenharmony_ci{ 457962306a36Sopenharmony_ci struct il_priv *il = dev_get_drvdata(d); 458062306a36Sopenharmony_ci 458162306a36Sopenharmony_ci if (!il_is_alive(il)) 458262306a36Sopenharmony_ci return -EAGAIN; 458362306a36Sopenharmony_ci 458462306a36Sopenharmony_ci return sprintf(buf, "%d\n", il->temperature); 458562306a36Sopenharmony_ci} 458662306a36Sopenharmony_ci 458762306a36Sopenharmony_cistatic DEVICE_ATTR(temperature, 0444, il4965_show_temperature, NULL); 458862306a36Sopenharmony_ci 458962306a36Sopenharmony_cistatic ssize_t 459062306a36Sopenharmony_ciil4965_show_tx_power(struct device *d, struct device_attribute *attr, char *buf) 459162306a36Sopenharmony_ci{ 459262306a36Sopenharmony_ci struct il_priv *il = dev_get_drvdata(d); 459362306a36Sopenharmony_ci 459462306a36Sopenharmony_ci if (!il_is_ready_rf(il)) 459562306a36Sopenharmony_ci return sprintf(buf, "off\n"); 459662306a36Sopenharmony_ci else 459762306a36Sopenharmony_ci return sprintf(buf, "%d\n", il->tx_power_user_lmt); 459862306a36Sopenharmony_ci} 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_cistatic ssize_t 460162306a36Sopenharmony_ciil4965_store_tx_power(struct device *d, struct device_attribute *attr, 460262306a36Sopenharmony_ci const char *buf, size_t count) 460362306a36Sopenharmony_ci{ 460462306a36Sopenharmony_ci struct il_priv *il = dev_get_drvdata(d); 460562306a36Sopenharmony_ci unsigned long val; 460662306a36Sopenharmony_ci int ret; 460762306a36Sopenharmony_ci 460862306a36Sopenharmony_ci ret = kstrtoul(buf, 10, &val); 460962306a36Sopenharmony_ci if (ret) 461062306a36Sopenharmony_ci IL_INFO("%s is not in decimal form.\n", buf); 461162306a36Sopenharmony_ci else { 461262306a36Sopenharmony_ci ret = il_set_tx_power(il, val, false); 461362306a36Sopenharmony_ci if (ret) 461462306a36Sopenharmony_ci IL_ERR("failed setting tx power (0x%08x).\n", ret); 461562306a36Sopenharmony_ci else 461662306a36Sopenharmony_ci ret = count; 461762306a36Sopenharmony_ci } 461862306a36Sopenharmony_ci return ret; 461962306a36Sopenharmony_ci} 462062306a36Sopenharmony_ci 462162306a36Sopenharmony_cistatic DEVICE_ATTR(tx_power, 0644, il4965_show_tx_power, 462262306a36Sopenharmony_ci il4965_store_tx_power); 462362306a36Sopenharmony_ci 462462306a36Sopenharmony_cistatic struct attribute *il_sysfs_entries[] = { 462562306a36Sopenharmony_ci &dev_attr_temperature.attr, 462662306a36Sopenharmony_ci &dev_attr_tx_power.attr, 462762306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 462862306a36Sopenharmony_ci &dev_attr_debug_level.attr, 462962306a36Sopenharmony_ci#endif 463062306a36Sopenharmony_ci NULL 463162306a36Sopenharmony_ci}; 463262306a36Sopenharmony_ci 463362306a36Sopenharmony_cistatic const struct attribute_group il_attribute_group = { 463462306a36Sopenharmony_ci .name = NULL, /* put in device directory */ 463562306a36Sopenharmony_ci .attrs = il_sysfs_entries, 463662306a36Sopenharmony_ci}; 463762306a36Sopenharmony_ci 463862306a36Sopenharmony_ci/****************************************************************************** 463962306a36Sopenharmony_ci * 464062306a36Sopenharmony_ci * uCode download functions 464162306a36Sopenharmony_ci * 464262306a36Sopenharmony_ci ******************************************************************************/ 464362306a36Sopenharmony_ci 464462306a36Sopenharmony_cistatic void 464562306a36Sopenharmony_ciil4965_dealloc_ucode_pci(struct il_priv *il) 464662306a36Sopenharmony_ci{ 464762306a36Sopenharmony_ci il_free_fw_desc(il->pci_dev, &il->ucode_code); 464862306a36Sopenharmony_ci il_free_fw_desc(il->pci_dev, &il->ucode_data); 464962306a36Sopenharmony_ci il_free_fw_desc(il->pci_dev, &il->ucode_data_backup); 465062306a36Sopenharmony_ci il_free_fw_desc(il->pci_dev, &il->ucode_init); 465162306a36Sopenharmony_ci il_free_fw_desc(il->pci_dev, &il->ucode_init_data); 465262306a36Sopenharmony_ci il_free_fw_desc(il->pci_dev, &il->ucode_boot); 465362306a36Sopenharmony_ci} 465462306a36Sopenharmony_ci 465562306a36Sopenharmony_cistatic void 465662306a36Sopenharmony_ciil4965_nic_start(struct il_priv *il) 465762306a36Sopenharmony_ci{ 465862306a36Sopenharmony_ci /* Remove all resets to allow NIC to operate */ 465962306a36Sopenharmony_ci _il_wr(il, CSR_RESET, 0); 466062306a36Sopenharmony_ci} 466162306a36Sopenharmony_ci 466262306a36Sopenharmony_cistatic void il4965_ucode_callback(const struct firmware *ucode_raw, 466362306a36Sopenharmony_ci void *context); 466462306a36Sopenharmony_cistatic int il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length); 466562306a36Sopenharmony_ci 466662306a36Sopenharmony_cistatic int __must_check 466762306a36Sopenharmony_ciil4965_request_firmware(struct il_priv *il, bool first) 466862306a36Sopenharmony_ci{ 466962306a36Sopenharmony_ci const char *name_pre = il->cfg->fw_name_pre; 467062306a36Sopenharmony_ci char tag[8]; 467162306a36Sopenharmony_ci 467262306a36Sopenharmony_ci if (first) { 467362306a36Sopenharmony_ci il->fw_idx = il->cfg->ucode_api_max; 467462306a36Sopenharmony_ci sprintf(tag, "%d", il->fw_idx); 467562306a36Sopenharmony_ci } else { 467662306a36Sopenharmony_ci il->fw_idx--; 467762306a36Sopenharmony_ci sprintf(tag, "%d", il->fw_idx); 467862306a36Sopenharmony_ci } 467962306a36Sopenharmony_ci 468062306a36Sopenharmony_ci if (il->fw_idx < il->cfg->ucode_api_min) { 468162306a36Sopenharmony_ci IL_ERR("no suitable firmware found!\n"); 468262306a36Sopenharmony_ci return -ENOENT; 468362306a36Sopenharmony_ci } 468462306a36Sopenharmony_ci 468562306a36Sopenharmony_ci sprintf(il->firmware_name, "%s%s%s", name_pre, tag, ".ucode"); 468662306a36Sopenharmony_ci 468762306a36Sopenharmony_ci D_INFO("attempting to load firmware '%s'\n", il->firmware_name); 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci return request_firmware_nowait(THIS_MODULE, 1, il->firmware_name, 469062306a36Sopenharmony_ci &il->pci_dev->dev, GFP_KERNEL, il, 469162306a36Sopenharmony_ci il4965_ucode_callback); 469262306a36Sopenharmony_ci} 469362306a36Sopenharmony_ci 469462306a36Sopenharmony_cistruct il4965_firmware_pieces { 469562306a36Sopenharmony_ci const void *inst, *data, *init, *init_data, *boot; 469662306a36Sopenharmony_ci size_t inst_size, data_size, init_size, init_data_size, boot_size; 469762306a36Sopenharmony_ci}; 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_cistatic int 470062306a36Sopenharmony_ciil4965_load_firmware(struct il_priv *il, const struct firmware *ucode_raw, 470162306a36Sopenharmony_ci struct il4965_firmware_pieces *pieces) 470262306a36Sopenharmony_ci{ 470362306a36Sopenharmony_ci struct il_ucode_header *ucode = (void *)ucode_raw->data; 470462306a36Sopenharmony_ci u32 api_ver, hdr_size; 470562306a36Sopenharmony_ci const u8 *src; 470662306a36Sopenharmony_ci 470762306a36Sopenharmony_ci il->ucode_ver = le32_to_cpu(ucode->ver); 470862306a36Sopenharmony_ci api_ver = IL_UCODE_API(il->ucode_ver); 470962306a36Sopenharmony_ci 471062306a36Sopenharmony_ci switch (api_ver) { 471162306a36Sopenharmony_ci default: 471262306a36Sopenharmony_ci case 0: 471362306a36Sopenharmony_ci case 1: 471462306a36Sopenharmony_ci case 2: 471562306a36Sopenharmony_ci hdr_size = 24; 471662306a36Sopenharmony_ci if (ucode_raw->size < hdr_size) { 471762306a36Sopenharmony_ci IL_ERR("File size too small!\n"); 471862306a36Sopenharmony_ci return -EINVAL; 471962306a36Sopenharmony_ci } 472062306a36Sopenharmony_ci pieces->inst_size = le32_to_cpu(ucode->v1.inst_size); 472162306a36Sopenharmony_ci pieces->data_size = le32_to_cpu(ucode->v1.data_size); 472262306a36Sopenharmony_ci pieces->init_size = le32_to_cpu(ucode->v1.init_size); 472362306a36Sopenharmony_ci pieces->init_data_size = le32_to_cpu(ucode->v1.init_data_size); 472462306a36Sopenharmony_ci pieces->boot_size = le32_to_cpu(ucode->v1.boot_size); 472562306a36Sopenharmony_ci src = ucode->v1.data; 472662306a36Sopenharmony_ci break; 472762306a36Sopenharmony_ci } 472862306a36Sopenharmony_ci 472962306a36Sopenharmony_ci /* Verify size of file vs. image size info in file's header */ 473062306a36Sopenharmony_ci if (ucode_raw->size != 473162306a36Sopenharmony_ci hdr_size + pieces->inst_size + pieces->data_size + 473262306a36Sopenharmony_ci pieces->init_size + pieces->init_data_size + pieces->boot_size) { 473362306a36Sopenharmony_ci 473462306a36Sopenharmony_ci IL_ERR("uCode file size %d does not match expected size\n", 473562306a36Sopenharmony_ci (int)ucode_raw->size); 473662306a36Sopenharmony_ci return -EINVAL; 473762306a36Sopenharmony_ci } 473862306a36Sopenharmony_ci 473962306a36Sopenharmony_ci pieces->inst = src; 474062306a36Sopenharmony_ci src += pieces->inst_size; 474162306a36Sopenharmony_ci pieces->data = src; 474262306a36Sopenharmony_ci src += pieces->data_size; 474362306a36Sopenharmony_ci pieces->init = src; 474462306a36Sopenharmony_ci src += pieces->init_size; 474562306a36Sopenharmony_ci pieces->init_data = src; 474662306a36Sopenharmony_ci src += pieces->init_data_size; 474762306a36Sopenharmony_ci pieces->boot = src; 474862306a36Sopenharmony_ci src += pieces->boot_size; 474962306a36Sopenharmony_ci 475062306a36Sopenharmony_ci return 0; 475162306a36Sopenharmony_ci} 475262306a36Sopenharmony_ci 475362306a36Sopenharmony_ci/* 475462306a36Sopenharmony_ci * il4965_ucode_callback - callback when firmware was loaded 475562306a36Sopenharmony_ci * 475662306a36Sopenharmony_ci * If loaded successfully, copies the firmware into buffers 475762306a36Sopenharmony_ci * for the card to fetch (via DMA). 475862306a36Sopenharmony_ci */ 475962306a36Sopenharmony_cistatic void 476062306a36Sopenharmony_ciil4965_ucode_callback(const struct firmware *ucode_raw, void *context) 476162306a36Sopenharmony_ci{ 476262306a36Sopenharmony_ci struct il_priv *il = context; 476362306a36Sopenharmony_ci int err; 476462306a36Sopenharmony_ci struct il4965_firmware_pieces pieces; 476562306a36Sopenharmony_ci const unsigned int api_max = il->cfg->ucode_api_max; 476662306a36Sopenharmony_ci const unsigned int api_min = il->cfg->ucode_api_min; 476762306a36Sopenharmony_ci u32 api_ver; 476862306a36Sopenharmony_ci 476962306a36Sopenharmony_ci u32 max_probe_length = 200; 477062306a36Sopenharmony_ci u32 standard_phy_calibration_size = 477162306a36Sopenharmony_ci IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; 477262306a36Sopenharmony_ci 477362306a36Sopenharmony_ci memset(&pieces, 0, sizeof(pieces)); 477462306a36Sopenharmony_ci 477562306a36Sopenharmony_ci if (!ucode_raw) { 477662306a36Sopenharmony_ci if (il->fw_idx <= il->cfg->ucode_api_max) 477762306a36Sopenharmony_ci IL_ERR("request for firmware file '%s' failed.\n", 477862306a36Sopenharmony_ci il->firmware_name); 477962306a36Sopenharmony_ci goto try_again; 478062306a36Sopenharmony_ci } 478162306a36Sopenharmony_ci 478262306a36Sopenharmony_ci D_INFO("Loaded firmware file '%s' (%zd bytes).\n", il->firmware_name, 478362306a36Sopenharmony_ci ucode_raw->size); 478462306a36Sopenharmony_ci 478562306a36Sopenharmony_ci /* Make sure that we got at least the API version number */ 478662306a36Sopenharmony_ci if (ucode_raw->size < 4) { 478762306a36Sopenharmony_ci IL_ERR("File size way too small!\n"); 478862306a36Sopenharmony_ci goto try_again; 478962306a36Sopenharmony_ci } 479062306a36Sopenharmony_ci 479162306a36Sopenharmony_ci /* Data from ucode file: header followed by uCode images */ 479262306a36Sopenharmony_ci err = il4965_load_firmware(il, ucode_raw, &pieces); 479362306a36Sopenharmony_ci 479462306a36Sopenharmony_ci if (err) 479562306a36Sopenharmony_ci goto try_again; 479662306a36Sopenharmony_ci 479762306a36Sopenharmony_ci api_ver = IL_UCODE_API(il->ucode_ver); 479862306a36Sopenharmony_ci 479962306a36Sopenharmony_ci /* 480062306a36Sopenharmony_ci * api_ver should match the api version forming part of the 480162306a36Sopenharmony_ci * firmware filename ... but we don't check for that and only rely 480262306a36Sopenharmony_ci * on the API version read from firmware header from here on forward 480362306a36Sopenharmony_ci */ 480462306a36Sopenharmony_ci if (api_ver < api_min || api_ver > api_max) { 480562306a36Sopenharmony_ci IL_ERR("Driver unable to support your firmware API. " 480662306a36Sopenharmony_ci "Driver supports v%u, firmware is v%u.\n", api_max, 480762306a36Sopenharmony_ci api_ver); 480862306a36Sopenharmony_ci goto try_again; 480962306a36Sopenharmony_ci } 481062306a36Sopenharmony_ci 481162306a36Sopenharmony_ci if (api_ver != api_max) 481262306a36Sopenharmony_ci IL_ERR("Firmware has old API version. Expected v%u, " 481362306a36Sopenharmony_ci "got v%u. New firmware can be obtained " 481462306a36Sopenharmony_ci "from http://www.intellinuxwireless.org.\n", api_max, 481562306a36Sopenharmony_ci api_ver); 481662306a36Sopenharmony_ci 481762306a36Sopenharmony_ci IL_INFO("loaded firmware version %u.%u.%u.%u\n", 481862306a36Sopenharmony_ci IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), 481962306a36Sopenharmony_ci IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); 482062306a36Sopenharmony_ci 482162306a36Sopenharmony_ci snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version), 482262306a36Sopenharmony_ci "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver), 482362306a36Sopenharmony_ci IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), 482462306a36Sopenharmony_ci IL_UCODE_SERIAL(il->ucode_ver)); 482562306a36Sopenharmony_ci 482662306a36Sopenharmony_ci /* 482762306a36Sopenharmony_ci * For any of the failures below (before allocating pci memory) 482862306a36Sopenharmony_ci * we will try to load a version with a smaller API -- maybe the 482962306a36Sopenharmony_ci * user just got a corrupted version of the latest API. 483062306a36Sopenharmony_ci */ 483162306a36Sopenharmony_ci 483262306a36Sopenharmony_ci D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver); 483362306a36Sopenharmony_ci D_INFO("f/w package hdr runtime inst size = %zd\n", pieces.inst_size); 483462306a36Sopenharmony_ci D_INFO("f/w package hdr runtime data size = %zd\n", pieces.data_size); 483562306a36Sopenharmony_ci D_INFO("f/w package hdr init inst size = %zd\n", pieces.init_size); 483662306a36Sopenharmony_ci D_INFO("f/w package hdr init data size = %zd\n", pieces.init_data_size); 483762306a36Sopenharmony_ci D_INFO("f/w package hdr boot inst size = %zd\n", pieces.boot_size); 483862306a36Sopenharmony_ci 483962306a36Sopenharmony_ci /* Verify that uCode images will fit in card's SRAM */ 484062306a36Sopenharmony_ci if (pieces.inst_size > il->hw_params.max_inst_size) { 484162306a36Sopenharmony_ci IL_ERR("uCode instr len %zd too large to fit in\n", 484262306a36Sopenharmony_ci pieces.inst_size); 484362306a36Sopenharmony_ci goto try_again; 484462306a36Sopenharmony_ci } 484562306a36Sopenharmony_ci 484662306a36Sopenharmony_ci if (pieces.data_size > il->hw_params.max_data_size) { 484762306a36Sopenharmony_ci IL_ERR("uCode data len %zd too large to fit in\n", 484862306a36Sopenharmony_ci pieces.data_size); 484962306a36Sopenharmony_ci goto try_again; 485062306a36Sopenharmony_ci } 485162306a36Sopenharmony_ci 485262306a36Sopenharmony_ci if (pieces.init_size > il->hw_params.max_inst_size) { 485362306a36Sopenharmony_ci IL_ERR("uCode init instr len %zd too large to fit in\n", 485462306a36Sopenharmony_ci pieces.init_size); 485562306a36Sopenharmony_ci goto try_again; 485662306a36Sopenharmony_ci } 485762306a36Sopenharmony_ci 485862306a36Sopenharmony_ci if (pieces.init_data_size > il->hw_params.max_data_size) { 485962306a36Sopenharmony_ci IL_ERR("uCode init data len %zd too large to fit in\n", 486062306a36Sopenharmony_ci pieces.init_data_size); 486162306a36Sopenharmony_ci goto try_again; 486262306a36Sopenharmony_ci } 486362306a36Sopenharmony_ci 486462306a36Sopenharmony_ci if (pieces.boot_size > il->hw_params.max_bsm_size) { 486562306a36Sopenharmony_ci IL_ERR("uCode boot instr len %zd too large to fit in\n", 486662306a36Sopenharmony_ci pieces.boot_size); 486762306a36Sopenharmony_ci goto try_again; 486862306a36Sopenharmony_ci } 486962306a36Sopenharmony_ci 487062306a36Sopenharmony_ci /* Allocate ucode buffers for card's bus-master loading ... */ 487162306a36Sopenharmony_ci 487262306a36Sopenharmony_ci /* Runtime instructions and 2 copies of data: 487362306a36Sopenharmony_ci * 1) unmodified from disk 487462306a36Sopenharmony_ci * 2) backup cache for save/restore during power-downs */ 487562306a36Sopenharmony_ci il->ucode_code.len = pieces.inst_size; 487662306a36Sopenharmony_ci il_alloc_fw_desc(il->pci_dev, &il->ucode_code); 487762306a36Sopenharmony_ci 487862306a36Sopenharmony_ci il->ucode_data.len = pieces.data_size; 487962306a36Sopenharmony_ci il_alloc_fw_desc(il->pci_dev, &il->ucode_data); 488062306a36Sopenharmony_ci 488162306a36Sopenharmony_ci il->ucode_data_backup.len = pieces.data_size; 488262306a36Sopenharmony_ci il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup); 488362306a36Sopenharmony_ci 488462306a36Sopenharmony_ci if (!il->ucode_code.v_addr || !il->ucode_data.v_addr || 488562306a36Sopenharmony_ci !il->ucode_data_backup.v_addr) 488662306a36Sopenharmony_ci goto err_pci_alloc; 488762306a36Sopenharmony_ci 488862306a36Sopenharmony_ci /* Initialization instructions and data */ 488962306a36Sopenharmony_ci if (pieces.init_size && pieces.init_data_size) { 489062306a36Sopenharmony_ci il->ucode_init.len = pieces.init_size; 489162306a36Sopenharmony_ci il_alloc_fw_desc(il->pci_dev, &il->ucode_init); 489262306a36Sopenharmony_ci 489362306a36Sopenharmony_ci il->ucode_init_data.len = pieces.init_data_size; 489462306a36Sopenharmony_ci il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data); 489562306a36Sopenharmony_ci 489662306a36Sopenharmony_ci if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr) 489762306a36Sopenharmony_ci goto err_pci_alloc; 489862306a36Sopenharmony_ci } 489962306a36Sopenharmony_ci 490062306a36Sopenharmony_ci /* Bootstrap (instructions only, no data) */ 490162306a36Sopenharmony_ci if (pieces.boot_size) { 490262306a36Sopenharmony_ci il->ucode_boot.len = pieces.boot_size; 490362306a36Sopenharmony_ci il_alloc_fw_desc(il->pci_dev, &il->ucode_boot); 490462306a36Sopenharmony_ci 490562306a36Sopenharmony_ci if (!il->ucode_boot.v_addr) 490662306a36Sopenharmony_ci goto err_pci_alloc; 490762306a36Sopenharmony_ci } 490862306a36Sopenharmony_ci 490962306a36Sopenharmony_ci /* Now that we can no longer fail, copy information */ 491062306a36Sopenharmony_ci 491162306a36Sopenharmony_ci il->sta_key_max_num = STA_KEY_MAX_NUM; 491262306a36Sopenharmony_ci 491362306a36Sopenharmony_ci /* Copy images into buffers for card's bus-master reads ... */ 491462306a36Sopenharmony_ci 491562306a36Sopenharmony_ci /* Runtime instructions (first block of data in file) */ 491662306a36Sopenharmony_ci D_INFO("Copying (but not loading) uCode instr len %zd\n", 491762306a36Sopenharmony_ci pieces.inst_size); 491862306a36Sopenharmony_ci memcpy(il->ucode_code.v_addr, pieces.inst, pieces.inst_size); 491962306a36Sopenharmony_ci 492062306a36Sopenharmony_ci D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", 492162306a36Sopenharmony_ci il->ucode_code.v_addr, (u32) il->ucode_code.p_addr); 492262306a36Sopenharmony_ci 492362306a36Sopenharmony_ci /* 492462306a36Sopenharmony_ci * Runtime data 492562306a36Sopenharmony_ci * NOTE: Copy into backup buffer will be done in il_up() 492662306a36Sopenharmony_ci */ 492762306a36Sopenharmony_ci D_INFO("Copying (but not loading) uCode data len %zd\n", 492862306a36Sopenharmony_ci pieces.data_size); 492962306a36Sopenharmony_ci memcpy(il->ucode_data.v_addr, pieces.data, pieces.data_size); 493062306a36Sopenharmony_ci memcpy(il->ucode_data_backup.v_addr, pieces.data, pieces.data_size); 493162306a36Sopenharmony_ci 493262306a36Sopenharmony_ci /* Initialization instructions */ 493362306a36Sopenharmony_ci if (pieces.init_size) { 493462306a36Sopenharmony_ci D_INFO("Copying (but not loading) init instr len %zd\n", 493562306a36Sopenharmony_ci pieces.init_size); 493662306a36Sopenharmony_ci memcpy(il->ucode_init.v_addr, pieces.init, pieces.init_size); 493762306a36Sopenharmony_ci } 493862306a36Sopenharmony_ci 493962306a36Sopenharmony_ci /* Initialization data */ 494062306a36Sopenharmony_ci if (pieces.init_data_size) { 494162306a36Sopenharmony_ci D_INFO("Copying (but not loading) init data len %zd\n", 494262306a36Sopenharmony_ci pieces.init_data_size); 494362306a36Sopenharmony_ci memcpy(il->ucode_init_data.v_addr, pieces.init_data, 494462306a36Sopenharmony_ci pieces.init_data_size); 494562306a36Sopenharmony_ci } 494662306a36Sopenharmony_ci 494762306a36Sopenharmony_ci /* Bootstrap instructions */ 494862306a36Sopenharmony_ci D_INFO("Copying (but not loading) boot instr len %zd\n", 494962306a36Sopenharmony_ci pieces.boot_size); 495062306a36Sopenharmony_ci memcpy(il->ucode_boot.v_addr, pieces.boot, pieces.boot_size); 495162306a36Sopenharmony_ci 495262306a36Sopenharmony_ci /* 495362306a36Sopenharmony_ci * figure out the offset of chain noise reset and gain commands 495462306a36Sopenharmony_ci * base on the size of standard phy calibration commands table size 495562306a36Sopenharmony_ci */ 495662306a36Sopenharmony_ci il->_4965.phy_calib_chain_noise_reset_cmd = 495762306a36Sopenharmony_ci standard_phy_calibration_size; 495862306a36Sopenharmony_ci il->_4965.phy_calib_chain_noise_gain_cmd = 495962306a36Sopenharmony_ci standard_phy_calibration_size + 1; 496062306a36Sopenharmony_ci 496162306a36Sopenharmony_ci /************************************************** 496262306a36Sopenharmony_ci * This is still part of probe() in a sense... 496362306a36Sopenharmony_ci * 496462306a36Sopenharmony_ci * 9. Setup and register with mac80211 and debugfs 496562306a36Sopenharmony_ci **************************************************/ 496662306a36Sopenharmony_ci err = il4965_mac_setup_register(il, max_probe_length); 496762306a36Sopenharmony_ci if (err) 496862306a36Sopenharmony_ci goto out_unbind; 496962306a36Sopenharmony_ci 497062306a36Sopenharmony_ci il_dbgfs_register(il, DRV_NAME); 497162306a36Sopenharmony_ci 497262306a36Sopenharmony_ci err = sysfs_create_group(&il->pci_dev->dev.kobj, &il_attribute_group); 497362306a36Sopenharmony_ci if (err) { 497462306a36Sopenharmony_ci IL_ERR("failed to create sysfs device attributes\n"); 497562306a36Sopenharmony_ci goto out_unbind; 497662306a36Sopenharmony_ci } 497762306a36Sopenharmony_ci 497862306a36Sopenharmony_ci /* We have our copies now, allow OS release its copies */ 497962306a36Sopenharmony_ci release_firmware(ucode_raw); 498062306a36Sopenharmony_ci complete(&il->_4965.firmware_loading_complete); 498162306a36Sopenharmony_ci return; 498262306a36Sopenharmony_ci 498362306a36Sopenharmony_citry_again: 498462306a36Sopenharmony_ci /* try next, if any */ 498562306a36Sopenharmony_ci if (il4965_request_firmware(il, false)) 498662306a36Sopenharmony_ci goto out_unbind; 498762306a36Sopenharmony_ci release_firmware(ucode_raw); 498862306a36Sopenharmony_ci return; 498962306a36Sopenharmony_ci 499062306a36Sopenharmony_cierr_pci_alloc: 499162306a36Sopenharmony_ci IL_ERR("failed to allocate pci memory\n"); 499262306a36Sopenharmony_ci il4965_dealloc_ucode_pci(il); 499362306a36Sopenharmony_ciout_unbind: 499462306a36Sopenharmony_ci complete(&il->_4965.firmware_loading_complete); 499562306a36Sopenharmony_ci device_release_driver(&il->pci_dev->dev); 499662306a36Sopenharmony_ci release_firmware(ucode_raw); 499762306a36Sopenharmony_ci} 499862306a36Sopenharmony_ci 499962306a36Sopenharmony_cistatic const char *const desc_lookup_text[] = { 500062306a36Sopenharmony_ci "OK", 500162306a36Sopenharmony_ci "FAIL", 500262306a36Sopenharmony_ci "BAD_PARAM", 500362306a36Sopenharmony_ci "BAD_CHECKSUM", 500462306a36Sopenharmony_ci "NMI_INTERRUPT_WDG", 500562306a36Sopenharmony_ci "SYSASSERT", 500662306a36Sopenharmony_ci "FATAL_ERROR", 500762306a36Sopenharmony_ci "BAD_COMMAND", 500862306a36Sopenharmony_ci "HW_ERROR_TUNE_LOCK", 500962306a36Sopenharmony_ci "HW_ERROR_TEMPERATURE", 501062306a36Sopenharmony_ci "ILLEGAL_CHAN_FREQ", 501162306a36Sopenharmony_ci "VCC_NOT_STBL", 501262306a36Sopenharmony_ci "FH49_ERROR", 501362306a36Sopenharmony_ci "NMI_INTERRUPT_HOST", 501462306a36Sopenharmony_ci "NMI_INTERRUPT_ACTION_PT", 501562306a36Sopenharmony_ci "NMI_INTERRUPT_UNKNOWN", 501662306a36Sopenharmony_ci "UCODE_VERSION_MISMATCH", 501762306a36Sopenharmony_ci "HW_ERROR_ABS_LOCK", 501862306a36Sopenharmony_ci "HW_ERROR_CAL_LOCK_FAIL", 501962306a36Sopenharmony_ci "NMI_INTERRUPT_INST_ACTION_PT", 502062306a36Sopenharmony_ci "NMI_INTERRUPT_DATA_ACTION_PT", 502162306a36Sopenharmony_ci "NMI_TRM_HW_ER", 502262306a36Sopenharmony_ci "NMI_INTERRUPT_TRM", 502362306a36Sopenharmony_ci "NMI_INTERRUPT_BREAK_POINT", 502462306a36Sopenharmony_ci "DEBUG_0", 502562306a36Sopenharmony_ci "DEBUG_1", 502662306a36Sopenharmony_ci "DEBUG_2", 502762306a36Sopenharmony_ci "DEBUG_3", 502862306a36Sopenharmony_ci}; 502962306a36Sopenharmony_ci 503062306a36Sopenharmony_cistatic struct { 503162306a36Sopenharmony_ci char *name; 503262306a36Sopenharmony_ci u8 num; 503362306a36Sopenharmony_ci} advanced_lookup[] = { 503462306a36Sopenharmony_ci { 503562306a36Sopenharmony_ci "NMI_INTERRUPT_WDG", 0x34}, { 503662306a36Sopenharmony_ci "SYSASSERT", 0x35}, { 503762306a36Sopenharmony_ci "UCODE_VERSION_MISMATCH", 0x37}, { 503862306a36Sopenharmony_ci "BAD_COMMAND", 0x38}, { 503962306a36Sopenharmony_ci "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C}, { 504062306a36Sopenharmony_ci "FATAL_ERROR", 0x3D}, { 504162306a36Sopenharmony_ci "NMI_TRM_HW_ERR", 0x46}, { 504262306a36Sopenharmony_ci "NMI_INTERRUPT_TRM", 0x4C}, { 504362306a36Sopenharmony_ci "NMI_INTERRUPT_BREAK_POINT", 0x54}, { 504462306a36Sopenharmony_ci "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C}, { 504562306a36Sopenharmony_ci "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64}, { 504662306a36Sopenharmony_ci "NMI_INTERRUPT_HOST", 0x66}, { 504762306a36Sopenharmony_ci "NMI_INTERRUPT_ACTION_PT", 0x7C}, { 504862306a36Sopenharmony_ci "NMI_INTERRUPT_UNKNOWN", 0x84}, { 504962306a36Sopenharmony_ci "NMI_INTERRUPT_INST_ACTION_PT", 0x86}, { 505062306a36Sopenharmony_ci"ADVANCED_SYSASSERT", 0},}; 505162306a36Sopenharmony_ci 505262306a36Sopenharmony_cistatic const char * 505362306a36Sopenharmony_ciil4965_desc_lookup(u32 num) 505462306a36Sopenharmony_ci{ 505562306a36Sopenharmony_ci int i; 505662306a36Sopenharmony_ci int max = ARRAY_SIZE(desc_lookup_text); 505762306a36Sopenharmony_ci 505862306a36Sopenharmony_ci if (num < max) 505962306a36Sopenharmony_ci return desc_lookup_text[num]; 506062306a36Sopenharmony_ci 506162306a36Sopenharmony_ci max = ARRAY_SIZE(advanced_lookup) - 1; 506262306a36Sopenharmony_ci for (i = 0; i < max; i++) { 506362306a36Sopenharmony_ci if (advanced_lookup[i].num == num) 506462306a36Sopenharmony_ci break; 506562306a36Sopenharmony_ci } 506662306a36Sopenharmony_ci return advanced_lookup[i].name; 506762306a36Sopenharmony_ci} 506862306a36Sopenharmony_ci 506962306a36Sopenharmony_ci#define ERROR_START_OFFSET (1 * sizeof(u32)) 507062306a36Sopenharmony_ci#define ERROR_ELEM_SIZE (7 * sizeof(u32)) 507162306a36Sopenharmony_ci 507262306a36Sopenharmony_civoid 507362306a36Sopenharmony_ciil4965_dump_nic_error_log(struct il_priv *il) 507462306a36Sopenharmony_ci{ 507562306a36Sopenharmony_ci u32 data2, line; 507662306a36Sopenharmony_ci u32 desc, time, count, base, data1; 507762306a36Sopenharmony_ci u32 blink1, blink2, ilink1, ilink2; 507862306a36Sopenharmony_ci u32 pc, hcmd; 507962306a36Sopenharmony_ci 508062306a36Sopenharmony_ci if (il->ucode_type == UCODE_INIT) 508162306a36Sopenharmony_ci base = le32_to_cpu(il->card_alive_init.error_event_table_ptr); 508262306a36Sopenharmony_ci else 508362306a36Sopenharmony_ci base = le32_to_cpu(il->card_alive.error_event_table_ptr); 508462306a36Sopenharmony_ci 508562306a36Sopenharmony_ci if (!il->ops->is_valid_rtc_data_addr(base)) { 508662306a36Sopenharmony_ci IL_ERR("Not valid error log pointer 0x%08X for %s uCode\n", 508762306a36Sopenharmony_ci base, (il->ucode_type == UCODE_INIT) ? "Init" : "RT"); 508862306a36Sopenharmony_ci return; 508962306a36Sopenharmony_ci } 509062306a36Sopenharmony_ci 509162306a36Sopenharmony_ci count = il_read_targ_mem(il, base); 509262306a36Sopenharmony_ci 509362306a36Sopenharmony_ci if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { 509462306a36Sopenharmony_ci IL_ERR("Start IWL Error Log Dump:\n"); 509562306a36Sopenharmony_ci IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count); 509662306a36Sopenharmony_ci } 509762306a36Sopenharmony_ci 509862306a36Sopenharmony_ci desc = il_read_targ_mem(il, base + 1 * sizeof(u32)); 509962306a36Sopenharmony_ci il->isr_stats.err_code = desc; 510062306a36Sopenharmony_ci pc = il_read_targ_mem(il, base + 2 * sizeof(u32)); 510162306a36Sopenharmony_ci blink1 = il_read_targ_mem(il, base + 3 * sizeof(u32)); 510262306a36Sopenharmony_ci blink2 = il_read_targ_mem(il, base + 4 * sizeof(u32)); 510362306a36Sopenharmony_ci ilink1 = il_read_targ_mem(il, base + 5 * sizeof(u32)); 510462306a36Sopenharmony_ci ilink2 = il_read_targ_mem(il, base + 6 * sizeof(u32)); 510562306a36Sopenharmony_ci data1 = il_read_targ_mem(il, base + 7 * sizeof(u32)); 510662306a36Sopenharmony_ci data2 = il_read_targ_mem(il, base + 8 * sizeof(u32)); 510762306a36Sopenharmony_ci line = il_read_targ_mem(il, base + 9 * sizeof(u32)); 510862306a36Sopenharmony_ci time = il_read_targ_mem(il, base + 11 * sizeof(u32)); 510962306a36Sopenharmony_ci hcmd = il_read_targ_mem(il, base + 22 * sizeof(u32)); 511062306a36Sopenharmony_ci 511162306a36Sopenharmony_ci IL_ERR("Desc Time " 511262306a36Sopenharmony_ci "data1 data2 line\n"); 511362306a36Sopenharmony_ci IL_ERR("%-28s (0x%04X) %010u 0x%08X 0x%08X %u\n", 511462306a36Sopenharmony_ci il4965_desc_lookup(desc), desc, time, data1, data2, line); 511562306a36Sopenharmony_ci IL_ERR("pc blink1 blink2 ilink1 ilink2 hcmd\n"); 511662306a36Sopenharmony_ci IL_ERR("0x%05X 0x%05X 0x%05X 0x%05X 0x%05X 0x%05X\n", pc, blink1, 511762306a36Sopenharmony_ci blink2, ilink1, ilink2, hcmd); 511862306a36Sopenharmony_ci} 511962306a36Sopenharmony_ci 512062306a36Sopenharmony_cistatic void 512162306a36Sopenharmony_ciil4965_rf_kill_ct_config(struct il_priv *il) 512262306a36Sopenharmony_ci{ 512362306a36Sopenharmony_ci struct il_ct_kill_config cmd; 512462306a36Sopenharmony_ci unsigned long flags; 512562306a36Sopenharmony_ci int ret = 0; 512662306a36Sopenharmony_ci 512762306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 512862306a36Sopenharmony_ci _il_wr(il, CSR_UCODE_DRV_GP1_CLR, 512962306a36Sopenharmony_ci CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); 513062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 513162306a36Sopenharmony_ci 513262306a36Sopenharmony_ci cmd.critical_temperature_R = 513362306a36Sopenharmony_ci cpu_to_le32(il->hw_params.ct_kill_threshold); 513462306a36Sopenharmony_ci 513562306a36Sopenharmony_ci ret = il_send_cmd_pdu(il, C_CT_KILL_CONFIG, sizeof(cmd), &cmd); 513662306a36Sopenharmony_ci if (ret) 513762306a36Sopenharmony_ci IL_ERR("C_CT_KILL_CONFIG failed\n"); 513862306a36Sopenharmony_ci else 513962306a36Sopenharmony_ci D_INFO("C_CT_KILL_CONFIG " "succeeded, " 514062306a36Sopenharmony_ci "critical temperature is %d\n", 514162306a36Sopenharmony_ci il->hw_params.ct_kill_threshold); 514262306a36Sopenharmony_ci} 514362306a36Sopenharmony_ci 514462306a36Sopenharmony_cistatic const s8 default_queue_to_tx_fifo[] = { 514562306a36Sopenharmony_ci IL_TX_FIFO_VO, 514662306a36Sopenharmony_ci IL_TX_FIFO_VI, 514762306a36Sopenharmony_ci IL_TX_FIFO_BE, 514862306a36Sopenharmony_ci IL_TX_FIFO_BK, 514962306a36Sopenharmony_ci IL49_CMD_FIFO_NUM, 515062306a36Sopenharmony_ci IL_TX_FIFO_UNUSED, 515162306a36Sopenharmony_ci IL_TX_FIFO_UNUSED, 515262306a36Sopenharmony_ci}; 515362306a36Sopenharmony_ci 515462306a36Sopenharmony_ci#define IL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) 515562306a36Sopenharmony_ci 515662306a36Sopenharmony_cistatic int 515762306a36Sopenharmony_ciil4965_alive_notify(struct il_priv *il) 515862306a36Sopenharmony_ci{ 515962306a36Sopenharmony_ci u32 a; 516062306a36Sopenharmony_ci unsigned long flags; 516162306a36Sopenharmony_ci int i, chan; 516262306a36Sopenharmony_ci u32 reg_val; 516362306a36Sopenharmony_ci 516462306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 516562306a36Sopenharmony_ci 516662306a36Sopenharmony_ci /* Clear 4965's internal Tx Scheduler data base */ 516762306a36Sopenharmony_ci il->scd_base_addr = il_rd_prph(il, IL49_SCD_SRAM_BASE_ADDR); 516862306a36Sopenharmony_ci a = il->scd_base_addr + IL49_SCD_CONTEXT_DATA_OFFSET; 516962306a36Sopenharmony_ci for (; a < il->scd_base_addr + IL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4) 517062306a36Sopenharmony_ci il_write_targ_mem(il, a, 0); 517162306a36Sopenharmony_ci for (; a < il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET; a += 4) 517262306a36Sopenharmony_ci il_write_targ_mem(il, a, 0); 517362306a36Sopenharmony_ci for (; 517462306a36Sopenharmony_ci a < 517562306a36Sopenharmony_ci il->scd_base_addr + 517662306a36Sopenharmony_ci IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(il->hw_params.max_txq_num); 517762306a36Sopenharmony_ci a += 4) 517862306a36Sopenharmony_ci il_write_targ_mem(il, a, 0); 517962306a36Sopenharmony_ci 518062306a36Sopenharmony_ci /* Tel 4965 where to find Tx byte count tables */ 518162306a36Sopenharmony_ci il_wr_prph(il, IL49_SCD_DRAM_BASE_ADDR, il->scd_bc_tbls.dma >> 10); 518262306a36Sopenharmony_ci 518362306a36Sopenharmony_ci /* Enable DMA channel */ 518462306a36Sopenharmony_ci for (chan = 0; chan < FH49_TCSR_CHNL_NUM; chan++) 518562306a36Sopenharmony_ci il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(chan), 518662306a36Sopenharmony_ci FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 518762306a36Sopenharmony_ci FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); 518862306a36Sopenharmony_ci 518962306a36Sopenharmony_ci /* Update FH chicken bits */ 519062306a36Sopenharmony_ci reg_val = il_rd(il, FH49_TX_CHICKEN_BITS_REG); 519162306a36Sopenharmony_ci il_wr(il, FH49_TX_CHICKEN_BITS_REG, 519262306a36Sopenharmony_ci reg_val | FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); 519362306a36Sopenharmony_ci 519462306a36Sopenharmony_ci /* Disable chain mode for all queues */ 519562306a36Sopenharmony_ci il_wr_prph(il, IL49_SCD_QUEUECHAIN_SEL, 0); 519662306a36Sopenharmony_ci 519762306a36Sopenharmony_ci /* Initialize each Tx queue (including the command queue) */ 519862306a36Sopenharmony_ci for (i = 0; i < il->hw_params.max_txq_num; i++) { 519962306a36Sopenharmony_ci 520062306a36Sopenharmony_ci /* TFD circular buffer read/write idxes */ 520162306a36Sopenharmony_ci il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(i), 0); 520262306a36Sopenharmony_ci il_wr(il, HBUS_TARG_WRPTR, 0 | (i << 8)); 520362306a36Sopenharmony_ci 520462306a36Sopenharmony_ci /* Max Tx Window size for Scheduler-ACK mode */ 520562306a36Sopenharmony_ci il_write_targ_mem(il, 520662306a36Sopenharmony_ci il->scd_base_addr + 520762306a36Sopenharmony_ci IL49_SCD_CONTEXT_QUEUE_OFFSET(i), 520862306a36Sopenharmony_ci (SCD_WIN_SIZE << 520962306a36Sopenharmony_ci IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & 521062306a36Sopenharmony_ci IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); 521162306a36Sopenharmony_ci 521262306a36Sopenharmony_ci /* Frame limit */ 521362306a36Sopenharmony_ci il_write_targ_mem(il, 521462306a36Sopenharmony_ci il->scd_base_addr + 521562306a36Sopenharmony_ci IL49_SCD_CONTEXT_QUEUE_OFFSET(i) + 521662306a36Sopenharmony_ci sizeof(u32), 521762306a36Sopenharmony_ci (SCD_FRAME_LIMIT << 521862306a36Sopenharmony_ci IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & 521962306a36Sopenharmony_ci IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); 522062306a36Sopenharmony_ci 522162306a36Sopenharmony_ci } 522262306a36Sopenharmony_ci il_wr_prph(il, IL49_SCD_INTERRUPT_MASK, 522362306a36Sopenharmony_ci (1 << il->hw_params.max_txq_num) - 1); 522462306a36Sopenharmony_ci 522562306a36Sopenharmony_ci /* Activate all Tx DMA/FIFO channels */ 522662306a36Sopenharmony_ci il4965_txq_set_sched(il, IL_MASK(0, 6)); 522762306a36Sopenharmony_ci 522862306a36Sopenharmony_ci il4965_set_wr_ptrs(il, IL_DEFAULT_CMD_QUEUE_NUM, 0); 522962306a36Sopenharmony_ci 523062306a36Sopenharmony_ci /* make sure all queue are not stopped */ 523162306a36Sopenharmony_ci memset(&il->queue_stopped[0], 0, sizeof(il->queue_stopped)); 523262306a36Sopenharmony_ci for (i = 0; i < 4; i++) 523362306a36Sopenharmony_ci atomic_set(&il->queue_stop_count[i], 0); 523462306a36Sopenharmony_ci 523562306a36Sopenharmony_ci /* reset to 0 to enable all the queue first */ 523662306a36Sopenharmony_ci il->txq_ctx_active_msk = 0; 523762306a36Sopenharmony_ci /* Map each Tx/cmd queue to its corresponding fifo */ 523862306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7); 523962306a36Sopenharmony_ci 524062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) { 524162306a36Sopenharmony_ci int ac = default_queue_to_tx_fifo[i]; 524262306a36Sopenharmony_ci 524362306a36Sopenharmony_ci il_txq_ctx_activate(il, i); 524462306a36Sopenharmony_ci 524562306a36Sopenharmony_ci if (ac == IL_TX_FIFO_UNUSED) 524662306a36Sopenharmony_ci continue; 524762306a36Sopenharmony_ci 524862306a36Sopenharmony_ci il4965_tx_queue_set_status(il, &il->txq[i], ac, 0); 524962306a36Sopenharmony_ci } 525062306a36Sopenharmony_ci 525162306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 525262306a36Sopenharmony_ci 525362306a36Sopenharmony_ci return 0; 525462306a36Sopenharmony_ci} 525562306a36Sopenharmony_ci 525662306a36Sopenharmony_ci/* 525762306a36Sopenharmony_ci * il4965_alive_start - called after N_ALIVE notification received 525862306a36Sopenharmony_ci * from protocol/runtime uCode (initialization uCode's 525962306a36Sopenharmony_ci * Alive gets handled by il_init_alive_start()). 526062306a36Sopenharmony_ci */ 526162306a36Sopenharmony_cistatic void 526262306a36Sopenharmony_ciil4965_alive_start(struct il_priv *il) 526362306a36Sopenharmony_ci{ 526462306a36Sopenharmony_ci int ret = 0; 526562306a36Sopenharmony_ci 526662306a36Sopenharmony_ci D_INFO("Runtime Alive received.\n"); 526762306a36Sopenharmony_ci 526862306a36Sopenharmony_ci if (il->card_alive.is_valid != UCODE_VALID_OK) { 526962306a36Sopenharmony_ci /* We had an error bringing up the hardware, so take it 527062306a36Sopenharmony_ci * all the way back down so we can try again */ 527162306a36Sopenharmony_ci D_INFO("Alive failed.\n"); 527262306a36Sopenharmony_ci goto restart; 527362306a36Sopenharmony_ci } 527462306a36Sopenharmony_ci 527562306a36Sopenharmony_ci /* Initialize uCode has loaded Runtime uCode ... verify inst image. 527662306a36Sopenharmony_ci * This is a paranoid check, because we would not have gotten the 527762306a36Sopenharmony_ci * "runtime" alive if code weren't properly loaded. */ 527862306a36Sopenharmony_ci if (il4965_verify_ucode(il)) { 527962306a36Sopenharmony_ci /* Runtime instruction load was bad; 528062306a36Sopenharmony_ci * take it all the way back down so we can try again */ 528162306a36Sopenharmony_ci D_INFO("Bad runtime uCode load.\n"); 528262306a36Sopenharmony_ci goto restart; 528362306a36Sopenharmony_ci } 528462306a36Sopenharmony_ci 528562306a36Sopenharmony_ci ret = il4965_alive_notify(il); 528662306a36Sopenharmony_ci if (ret) { 528762306a36Sopenharmony_ci IL_WARN("Could not complete ALIVE transition [ntf]: %d\n", ret); 528862306a36Sopenharmony_ci goto restart; 528962306a36Sopenharmony_ci } 529062306a36Sopenharmony_ci 529162306a36Sopenharmony_ci /* After the ALIVE response, we can send host commands to the uCode */ 529262306a36Sopenharmony_ci set_bit(S_ALIVE, &il->status); 529362306a36Sopenharmony_ci 529462306a36Sopenharmony_ci /* Enable watchdog to monitor the driver tx queues */ 529562306a36Sopenharmony_ci il_setup_watchdog(il); 529662306a36Sopenharmony_ci 529762306a36Sopenharmony_ci if (il_is_rfkill(il)) 529862306a36Sopenharmony_ci return; 529962306a36Sopenharmony_ci 530062306a36Sopenharmony_ci ieee80211_wake_queues(il->hw); 530162306a36Sopenharmony_ci 530262306a36Sopenharmony_ci il->active_rate = RATES_MASK; 530362306a36Sopenharmony_ci 530462306a36Sopenharmony_ci il_power_update_mode(il, true); 530562306a36Sopenharmony_ci D_INFO("Updated power mode\n"); 530662306a36Sopenharmony_ci 530762306a36Sopenharmony_ci if (il_is_associated(il)) { 530862306a36Sopenharmony_ci struct il_rxon_cmd *active_rxon = 530962306a36Sopenharmony_ci (struct il_rxon_cmd *)&il->active; 531062306a36Sopenharmony_ci /* apply any changes in staging */ 531162306a36Sopenharmony_ci il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; 531262306a36Sopenharmony_ci active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; 531362306a36Sopenharmony_ci } else { 531462306a36Sopenharmony_ci /* Initialize our rx_config data */ 531562306a36Sopenharmony_ci il_connection_init_rx_config(il); 531662306a36Sopenharmony_ci 531762306a36Sopenharmony_ci if (il->ops->set_rxon_chain) 531862306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 531962306a36Sopenharmony_ci } 532062306a36Sopenharmony_ci 532162306a36Sopenharmony_ci /* Configure bluetooth coexistence if enabled */ 532262306a36Sopenharmony_ci il_send_bt_config(il); 532362306a36Sopenharmony_ci 532462306a36Sopenharmony_ci il4965_reset_run_time_calib(il); 532562306a36Sopenharmony_ci 532662306a36Sopenharmony_ci set_bit(S_READY, &il->status); 532762306a36Sopenharmony_ci 532862306a36Sopenharmony_ci /* Configure the adapter for unassociated operation */ 532962306a36Sopenharmony_ci il_commit_rxon(il); 533062306a36Sopenharmony_ci 533162306a36Sopenharmony_ci /* At this point, the NIC is initialized and operational */ 533262306a36Sopenharmony_ci il4965_rf_kill_ct_config(il); 533362306a36Sopenharmony_ci 533462306a36Sopenharmony_ci D_INFO("ALIVE processing complete.\n"); 533562306a36Sopenharmony_ci wake_up(&il->wait_command_queue); 533662306a36Sopenharmony_ci 533762306a36Sopenharmony_ci return; 533862306a36Sopenharmony_ci 533962306a36Sopenharmony_cirestart: 534062306a36Sopenharmony_ci queue_work(il->workqueue, &il->restart); 534162306a36Sopenharmony_ci} 534262306a36Sopenharmony_ci 534362306a36Sopenharmony_cistatic void il4965_cancel_deferred_work(struct il_priv *il); 534462306a36Sopenharmony_ci 534562306a36Sopenharmony_cistatic void 534662306a36Sopenharmony_ci__il4965_down(struct il_priv *il) 534762306a36Sopenharmony_ci{ 534862306a36Sopenharmony_ci unsigned long flags; 534962306a36Sopenharmony_ci int exit_pending; 535062306a36Sopenharmony_ci 535162306a36Sopenharmony_ci D_INFO(DRV_NAME " is going down\n"); 535262306a36Sopenharmony_ci 535362306a36Sopenharmony_ci il_scan_cancel_timeout(il, 200); 535462306a36Sopenharmony_ci 535562306a36Sopenharmony_ci exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status); 535662306a36Sopenharmony_ci 535762306a36Sopenharmony_ci /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set 535862306a36Sopenharmony_ci * to prevent rearm timer */ 535962306a36Sopenharmony_ci del_timer_sync(&il->watchdog); 536062306a36Sopenharmony_ci 536162306a36Sopenharmony_ci il_clear_ucode_stations(il); 536262306a36Sopenharmony_ci 536362306a36Sopenharmony_ci /* FIXME: race conditions ? */ 536462306a36Sopenharmony_ci spin_lock_irq(&il->sta_lock); 536562306a36Sopenharmony_ci /* 536662306a36Sopenharmony_ci * Remove all key information that is not stored as part 536762306a36Sopenharmony_ci * of station information since mac80211 may not have had 536862306a36Sopenharmony_ci * a chance to remove all the keys. When device is 536962306a36Sopenharmony_ci * reconfigured by mac80211 after an error all keys will 537062306a36Sopenharmony_ci * be reconfigured. 537162306a36Sopenharmony_ci */ 537262306a36Sopenharmony_ci memset(il->_4965.wep_keys, 0, sizeof(il->_4965.wep_keys)); 537362306a36Sopenharmony_ci il->_4965.key_mapping_keys = 0; 537462306a36Sopenharmony_ci spin_unlock_irq(&il->sta_lock); 537562306a36Sopenharmony_ci 537662306a36Sopenharmony_ci il_dealloc_bcast_stations(il); 537762306a36Sopenharmony_ci il_clear_driver_stations(il); 537862306a36Sopenharmony_ci 537962306a36Sopenharmony_ci /* Unblock any waiting calls */ 538062306a36Sopenharmony_ci wake_up_all(&il->wait_command_queue); 538162306a36Sopenharmony_ci 538262306a36Sopenharmony_ci /* Wipe out the EXIT_PENDING status bit if we are not actually 538362306a36Sopenharmony_ci * exiting the module */ 538462306a36Sopenharmony_ci if (!exit_pending) 538562306a36Sopenharmony_ci clear_bit(S_EXIT_PENDING, &il->status); 538662306a36Sopenharmony_ci 538762306a36Sopenharmony_ci /* stop and reset the on-board processor */ 538862306a36Sopenharmony_ci _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); 538962306a36Sopenharmony_ci 539062306a36Sopenharmony_ci /* tell the device to stop sending interrupts */ 539162306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 539262306a36Sopenharmony_ci il_disable_interrupts(il); 539362306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 539462306a36Sopenharmony_ci il4965_synchronize_irq(il); 539562306a36Sopenharmony_ci 539662306a36Sopenharmony_ci if (il->mac80211_registered) 539762306a36Sopenharmony_ci ieee80211_stop_queues(il->hw); 539862306a36Sopenharmony_ci 539962306a36Sopenharmony_ci /* If we have not previously called il_init() then 540062306a36Sopenharmony_ci * clear all bits but the RF Kill bit and return */ 540162306a36Sopenharmony_ci if (!il_is_init(il)) { 540262306a36Sopenharmony_ci il->status = 540362306a36Sopenharmony_ci test_bit(S_RFKILL, &il->status) << S_RFKILL | 540462306a36Sopenharmony_ci test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | 540562306a36Sopenharmony_ci test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; 540662306a36Sopenharmony_ci goto exit; 540762306a36Sopenharmony_ci } 540862306a36Sopenharmony_ci 540962306a36Sopenharmony_ci /* ...otherwise clear out all the status bits but the RF Kill 541062306a36Sopenharmony_ci * bit and continue taking the NIC down. */ 541162306a36Sopenharmony_ci il->status &= 541262306a36Sopenharmony_ci test_bit(S_RFKILL, &il->status) << S_RFKILL | 541362306a36Sopenharmony_ci test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | 541462306a36Sopenharmony_ci test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR | 541562306a36Sopenharmony_ci test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; 541662306a36Sopenharmony_ci 541762306a36Sopenharmony_ci /* 541862306a36Sopenharmony_ci * We disabled and synchronized interrupt, and priv->mutex is taken, so 541962306a36Sopenharmony_ci * here is the only thread which will program device registers, but 542062306a36Sopenharmony_ci * still have lockdep assertions, so we are taking reg_lock. 542162306a36Sopenharmony_ci */ 542262306a36Sopenharmony_ci spin_lock_irq(&il->reg_lock); 542362306a36Sopenharmony_ci /* FIXME: il_grab_nic_access if rfkill is off ? */ 542462306a36Sopenharmony_ci 542562306a36Sopenharmony_ci il4965_txq_ctx_stop(il); 542662306a36Sopenharmony_ci il4965_rxq_stop(il); 542762306a36Sopenharmony_ci /* Power-down device's busmaster DMA clocks */ 542862306a36Sopenharmony_ci _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); 542962306a36Sopenharmony_ci udelay(5); 543062306a36Sopenharmony_ci /* Make sure (redundant) we've released our request to stay awake */ 543162306a36Sopenharmony_ci _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 543262306a36Sopenharmony_ci /* Stop the device, and put it in low power state */ 543362306a36Sopenharmony_ci _il_apm_stop(il); 543462306a36Sopenharmony_ci 543562306a36Sopenharmony_ci spin_unlock_irq(&il->reg_lock); 543662306a36Sopenharmony_ci 543762306a36Sopenharmony_ci il4965_txq_ctx_unmap(il); 543862306a36Sopenharmony_ciexit: 543962306a36Sopenharmony_ci memset(&il->card_alive, 0, sizeof(struct il_alive_resp)); 544062306a36Sopenharmony_ci 544162306a36Sopenharmony_ci dev_kfree_skb(il->beacon_skb); 544262306a36Sopenharmony_ci il->beacon_skb = NULL; 544362306a36Sopenharmony_ci 544462306a36Sopenharmony_ci /* clear out any free frames */ 544562306a36Sopenharmony_ci il4965_clear_free_frames(il); 544662306a36Sopenharmony_ci} 544762306a36Sopenharmony_ci 544862306a36Sopenharmony_cistatic void 544962306a36Sopenharmony_ciil4965_down(struct il_priv *il) 545062306a36Sopenharmony_ci{ 545162306a36Sopenharmony_ci mutex_lock(&il->mutex); 545262306a36Sopenharmony_ci __il4965_down(il); 545362306a36Sopenharmony_ci mutex_unlock(&il->mutex); 545462306a36Sopenharmony_ci 545562306a36Sopenharmony_ci il4965_cancel_deferred_work(il); 545662306a36Sopenharmony_ci} 545762306a36Sopenharmony_ci 545862306a36Sopenharmony_ci 545962306a36Sopenharmony_cistatic void 546062306a36Sopenharmony_ciil4965_set_hw_ready(struct il_priv *il) 546162306a36Sopenharmony_ci{ 546262306a36Sopenharmony_ci int ret; 546362306a36Sopenharmony_ci 546462306a36Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, 546562306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); 546662306a36Sopenharmony_ci 546762306a36Sopenharmony_ci /* See if we got it */ 546862306a36Sopenharmony_ci ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, 546962306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, 547062306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, 547162306a36Sopenharmony_ci 100); 547262306a36Sopenharmony_ci if (ret >= 0) 547362306a36Sopenharmony_ci il->hw_ready = true; 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_ci D_INFO("hardware %s ready\n", (il->hw_ready) ? "" : "not"); 547662306a36Sopenharmony_ci} 547762306a36Sopenharmony_ci 547862306a36Sopenharmony_cistatic void 547962306a36Sopenharmony_ciil4965_prepare_card_hw(struct il_priv *il) 548062306a36Sopenharmony_ci{ 548162306a36Sopenharmony_ci int ret; 548262306a36Sopenharmony_ci 548362306a36Sopenharmony_ci il->hw_ready = false; 548462306a36Sopenharmony_ci 548562306a36Sopenharmony_ci il4965_set_hw_ready(il); 548662306a36Sopenharmony_ci if (il->hw_ready) 548762306a36Sopenharmony_ci return; 548862306a36Sopenharmony_ci 548962306a36Sopenharmony_ci /* If HW is not ready, prepare the conditions to check again */ 549062306a36Sopenharmony_ci il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE); 549162306a36Sopenharmony_ci 549262306a36Sopenharmony_ci ret = 549362306a36Sopenharmony_ci _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, 549462306a36Sopenharmony_ci ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 549562306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); 549662306a36Sopenharmony_ci 549762306a36Sopenharmony_ci /* HW should be ready by now, check again. */ 549862306a36Sopenharmony_ci if (ret != -ETIMEDOUT) 549962306a36Sopenharmony_ci il4965_set_hw_ready(il); 550062306a36Sopenharmony_ci} 550162306a36Sopenharmony_ci 550262306a36Sopenharmony_ci#define MAX_HW_RESTARTS 5 550362306a36Sopenharmony_ci 550462306a36Sopenharmony_cistatic int 550562306a36Sopenharmony_ci__il4965_up(struct il_priv *il) 550662306a36Sopenharmony_ci{ 550762306a36Sopenharmony_ci int i; 550862306a36Sopenharmony_ci int ret; 550962306a36Sopenharmony_ci 551062306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) { 551162306a36Sopenharmony_ci IL_WARN("Exit pending; will not bring the NIC up\n"); 551262306a36Sopenharmony_ci return -EIO; 551362306a36Sopenharmony_ci } 551462306a36Sopenharmony_ci 551562306a36Sopenharmony_ci if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) { 551662306a36Sopenharmony_ci IL_ERR("ucode not available for device bringup\n"); 551762306a36Sopenharmony_ci return -EIO; 551862306a36Sopenharmony_ci } 551962306a36Sopenharmony_ci 552062306a36Sopenharmony_ci ret = il4965_alloc_bcast_station(il); 552162306a36Sopenharmony_ci if (ret) { 552262306a36Sopenharmony_ci il_dealloc_bcast_stations(il); 552362306a36Sopenharmony_ci return ret; 552462306a36Sopenharmony_ci } 552562306a36Sopenharmony_ci 552662306a36Sopenharmony_ci il4965_prepare_card_hw(il); 552762306a36Sopenharmony_ci if (!il->hw_ready) { 552862306a36Sopenharmony_ci il_dealloc_bcast_stations(il); 552962306a36Sopenharmony_ci IL_ERR("HW not ready\n"); 553062306a36Sopenharmony_ci return -EIO; 553162306a36Sopenharmony_ci } 553262306a36Sopenharmony_ci 553362306a36Sopenharmony_ci /* If platform's RF_KILL switch is NOT set to KILL */ 553462306a36Sopenharmony_ci if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) 553562306a36Sopenharmony_ci clear_bit(S_RFKILL, &il->status); 553662306a36Sopenharmony_ci else { 553762306a36Sopenharmony_ci set_bit(S_RFKILL, &il->status); 553862306a36Sopenharmony_ci wiphy_rfkill_set_hw_state(il->hw->wiphy, true); 553962306a36Sopenharmony_ci 554062306a36Sopenharmony_ci il_dealloc_bcast_stations(il); 554162306a36Sopenharmony_ci il_enable_rfkill_int(il); 554262306a36Sopenharmony_ci IL_WARN("Radio disabled by HW RF Kill switch\n"); 554362306a36Sopenharmony_ci return 0; 554462306a36Sopenharmony_ci } 554562306a36Sopenharmony_ci 554662306a36Sopenharmony_ci _il_wr(il, CSR_INT, 0xFFFFFFFF); 554762306a36Sopenharmony_ci 554862306a36Sopenharmony_ci /* must be initialised before il_hw_nic_init */ 554962306a36Sopenharmony_ci il->cmd_queue = IL_DEFAULT_CMD_QUEUE_NUM; 555062306a36Sopenharmony_ci 555162306a36Sopenharmony_ci ret = il4965_hw_nic_init(il); 555262306a36Sopenharmony_ci if (ret) { 555362306a36Sopenharmony_ci IL_ERR("Unable to init nic\n"); 555462306a36Sopenharmony_ci il_dealloc_bcast_stations(il); 555562306a36Sopenharmony_ci return ret; 555662306a36Sopenharmony_ci } 555762306a36Sopenharmony_ci 555862306a36Sopenharmony_ci /* make sure rfkill handshake bits are cleared */ 555962306a36Sopenharmony_ci _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 556062306a36Sopenharmony_ci _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 556162306a36Sopenharmony_ci 556262306a36Sopenharmony_ci /* clear (again), then enable host interrupts */ 556362306a36Sopenharmony_ci _il_wr(il, CSR_INT, 0xFFFFFFFF); 556462306a36Sopenharmony_ci il_enable_interrupts(il); 556562306a36Sopenharmony_ci 556662306a36Sopenharmony_ci /* really make sure rfkill handshake bits are cleared */ 556762306a36Sopenharmony_ci _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 556862306a36Sopenharmony_ci _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 556962306a36Sopenharmony_ci 557062306a36Sopenharmony_ci /* Copy original ucode data image from disk into backup cache. 557162306a36Sopenharmony_ci * This will be used to initialize the on-board processor's 557262306a36Sopenharmony_ci * data SRAM for a clean start when the runtime program first loads. */ 557362306a36Sopenharmony_ci memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr, 557462306a36Sopenharmony_ci il->ucode_data.len); 557562306a36Sopenharmony_ci 557662306a36Sopenharmony_ci for (i = 0; i < MAX_HW_RESTARTS; i++) { 557762306a36Sopenharmony_ci 557862306a36Sopenharmony_ci /* load bootstrap state machine, 557962306a36Sopenharmony_ci * load bootstrap program into processor's memory, 558062306a36Sopenharmony_ci * prepare to load the "initialize" uCode */ 558162306a36Sopenharmony_ci ret = il->ops->load_ucode(il); 558262306a36Sopenharmony_ci 558362306a36Sopenharmony_ci if (ret) { 558462306a36Sopenharmony_ci IL_ERR("Unable to set up bootstrap uCode: %d\n", ret); 558562306a36Sopenharmony_ci continue; 558662306a36Sopenharmony_ci } 558762306a36Sopenharmony_ci 558862306a36Sopenharmony_ci /* start card; "initialize" will load runtime ucode */ 558962306a36Sopenharmony_ci il4965_nic_start(il); 559062306a36Sopenharmony_ci 559162306a36Sopenharmony_ci D_INFO(DRV_NAME " is coming up\n"); 559262306a36Sopenharmony_ci 559362306a36Sopenharmony_ci return 0; 559462306a36Sopenharmony_ci } 559562306a36Sopenharmony_ci 559662306a36Sopenharmony_ci set_bit(S_EXIT_PENDING, &il->status); 559762306a36Sopenharmony_ci __il4965_down(il); 559862306a36Sopenharmony_ci clear_bit(S_EXIT_PENDING, &il->status); 559962306a36Sopenharmony_ci 560062306a36Sopenharmony_ci /* tried to restart and config the device for as long as our 560162306a36Sopenharmony_ci * patience could withstand */ 560262306a36Sopenharmony_ci IL_ERR("Unable to initialize device after %d attempts.\n", i); 560362306a36Sopenharmony_ci return -EIO; 560462306a36Sopenharmony_ci} 560562306a36Sopenharmony_ci 560662306a36Sopenharmony_ci/***************************************************************************** 560762306a36Sopenharmony_ci * 560862306a36Sopenharmony_ci * Workqueue callbacks 560962306a36Sopenharmony_ci * 561062306a36Sopenharmony_ci *****************************************************************************/ 561162306a36Sopenharmony_ci 561262306a36Sopenharmony_cistatic void 561362306a36Sopenharmony_ciil4965_bg_init_alive_start(struct work_struct *data) 561462306a36Sopenharmony_ci{ 561562306a36Sopenharmony_ci struct il_priv *il = 561662306a36Sopenharmony_ci container_of(data, struct il_priv, init_alive_start.work); 561762306a36Sopenharmony_ci 561862306a36Sopenharmony_ci mutex_lock(&il->mutex); 561962306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 562062306a36Sopenharmony_ci goto out; 562162306a36Sopenharmony_ci 562262306a36Sopenharmony_ci il->ops->init_alive_start(il); 562362306a36Sopenharmony_ciout: 562462306a36Sopenharmony_ci mutex_unlock(&il->mutex); 562562306a36Sopenharmony_ci} 562662306a36Sopenharmony_ci 562762306a36Sopenharmony_cistatic void 562862306a36Sopenharmony_ciil4965_bg_alive_start(struct work_struct *data) 562962306a36Sopenharmony_ci{ 563062306a36Sopenharmony_ci struct il_priv *il = 563162306a36Sopenharmony_ci container_of(data, struct il_priv, alive_start.work); 563262306a36Sopenharmony_ci 563362306a36Sopenharmony_ci mutex_lock(&il->mutex); 563462306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 563562306a36Sopenharmony_ci goto out; 563662306a36Sopenharmony_ci 563762306a36Sopenharmony_ci il4965_alive_start(il); 563862306a36Sopenharmony_ciout: 563962306a36Sopenharmony_ci mutex_unlock(&il->mutex); 564062306a36Sopenharmony_ci} 564162306a36Sopenharmony_ci 564262306a36Sopenharmony_cistatic void 564362306a36Sopenharmony_ciil4965_bg_run_time_calib_work(struct work_struct *work) 564462306a36Sopenharmony_ci{ 564562306a36Sopenharmony_ci struct il_priv *il = container_of(work, struct il_priv, 564662306a36Sopenharmony_ci run_time_calib_work); 564762306a36Sopenharmony_ci 564862306a36Sopenharmony_ci mutex_lock(&il->mutex); 564962306a36Sopenharmony_ci 565062306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status) || 565162306a36Sopenharmony_ci test_bit(S_SCANNING, &il->status)) { 565262306a36Sopenharmony_ci mutex_unlock(&il->mutex); 565362306a36Sopenharmony_ci return; 565462306a36Sopenharmony_ci } 565562306a36Sopenharmony_ci 565662306a36Sopenharmony_ci if (il->start_calib) { 565762306a36Sopenharmony_ci il4965_chain_noise_calibration(il, (void *)&il->_4965.stats); 565862306a36Sopenharmony_ci il4965_sensitivity_calibration(il, (void *)&il->_4965.stats); 565962306a36Sopenharmony_ci } 566062306a36Sopenharmony_ci 566162306a36Sopenharmony_ci mutex_unlock(&il->mutex); 566262306a36Sopenharmony_ci} 566362306a36Sopenharmony_ci 566462306a36Sopenharmony_cistatic void 566562306a36Sopenharmony_ciil4965_bg_restart(struct work_struct *data) 566662306a36Sopenharmony_ci{ 566762306a36Sopenharmony_ci struct il_priv *il = container_of(data, struct il_priv, restart); 566862306a36Sopenharmony_ci 566962306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 567062306a36Sopenharmony_ci return; 567162306a36Sopenharmony_ci 567262306a36Sopenharmony_ci if (test_and_clear_bit(S_FW_ERROR, &il->status)) { 567362306a36Sopenharmony_ci mutex_lock(&il->mutex); 567462306a36Sopenharmony_ci il->is_open = 0; 567562306a36Sopenharmony_ci 567662306a36Sopenharmony_ci __il4965_down(il); 567762306a36Sopenharmony_ci 567862306a36Sopenharmony_ci mutex_unlock(&il->mutex); 567962306a36Sopenharmony_ci il4965_cancel_deferred_work(il); 568062306a36Sopenharmony_ci ieee80211_restart_hw(il->hw); 568162306a36Sopenharmony_ci } else { 568262306a36Sopenharmony_ci il4965_down(il); 568362306a36Sopenharmony_ci 568462306a36Sopenharmony_ci mutex_lock(&il->mutex); 568562306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) { 568662306a36Sopenharmony_ci mutex_unlock(&il->mutex); 568762306a36Sopenharmony_ci return; 568862306a36Sopenharmony_ci } 568962306a36Sopenharmony_ci 569062306a36Sopenharmony_ci __il4965_up(il); 569162306a36Sopenharmony_ci mutex_unlock(&il->mutex); 569262306a36Sopenharmony_ci } 569362306a36Sopenharmony_ci} 569462306a36Sopenharmony_ci 569562306a36Sopenharmony_cistatic void 569662306a36Sopenharmony_ciil4965_bg_rx_replenish(struct work_struct *data) 569762306a36Sopenharmony_ci{ 569862306a36Sopenharmony_ci struct il_priv *il = container_of(data, struct il_priv, rx_replenish); 569962306a36Sopenharmony_ci 570062306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 570162306a36Sopenharmony_ci return; 570262306a36Sopenharmony_ci 570362306a36Sopenharmony_ci mutex_lock(&il->mutex); 570462306a36Sopenharmony_ci il4965_rx_replenish(il); 570562306a36Sopenharmony_ci mutex_unlock(&il->mutex); 570662306a36Sopenharmony_ci} 570762306a36Sopenharmony_ci 570862306a36Sopenharmony_ci/***************************************************************************** 570962306a36Sopenharmony_ci * 571062306a36Sopenharmony_ci * mac80211 entry point functions 571162306a36Sopenharmony_ci * 571262306a36Sopenharmony_ci *****************************************************************************/ 571362306a36Sopenharmony_ci 571462306a36Sopenharmony_ci#define UCODE_READY_TIMEOUT (4 * HZ) 571562306a36Sopenharmony_ci 571662306a36Sopenharmony_ci/* 571762306a36Sopenharmony_ci * Not a mac80211 entry point function, but it fits in with all the 571862306a36Sopenharmony_ci * other mac80211 functions grouped here. 571962306a36Sopenharmony_ci */ 572062306a36Sopenharmony_cistatic int 572162306a36Sopenharmony_ciil4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) 572262306a36Sopenharmony_ci{ 572362306a36Sopenharmony_ci int ret; 572462306a36Sopenharmony_ci struct ieee80211_hw *hw = il->hw; 572562306a36Sopenharmony_ci 572662306a36Sopenharmony_ci hw->rate_control_algorithm = "iwl-4965-rs"; 572762306a36Sopenharmony_ci 572862306a36Sopenharmony_ci /* Tell mac80211 our characteristics */ 572962306a36Sopenharmony_ci ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); 573062306a36Sopenharmony_ci ieee80211_hw_set(hw, SUPPORTS_PS); 573162306a36Sopenharmony_ci ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); 573262306a36Sopenharmony_ci ieee80211_hw_set(hw, SPECTRUM_MGMT); 573362306a36Sopenharmony_ci ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); 573462306a36Sopenharmony_ci ieee80211_hw_set(hw, SIGNAL_DBM); 573562306a36Sopenharmony_ci ieee80211_hw_set(hw, AMPDU_AGGREGATION); 573662306a36Sopenharmony_ci if (il->cfg->sku & IL_SKU_N) 573762306a36Sopenharmony_ci hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS | 573862306a36Sopenharmony_ci NL80211_FEATURE_STATIC_SMPS; 573962306a36Sopenharmony_ci 574062306a36Sopenharmony_ci hw->sta_data_size = sizeof(struct il_station_priv); 574162306a36Sopenharmony_ci hw->vif_data_size = sizeof(struct il_vif_priv); 574262306a36Sopenharmony_ci 574362306a36Sopenharmony_ci hw->wiphy->interface_modes = 574462306a36Sopenharmony_ci BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); 574562306a36Sopenharmony_ci 574662306a36Sopenharmony_ci hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; 574762306a36Sopenharmony_ci hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | 574862306a36Sopenharmony_ci REGULATORY_DISABLE_BEACON_HINTS; 574962306a36Sopenharmony_ci 575062306a36Sopenharmony_ci /* 575162306a36Sopenharmony_ci * For now, disable PS by default because it affects 575262306a36Sopenharmony_ci * RX performance significantly. 575362306a36Sopenharmony_ci */ 575462306a36Sopenharmony_ci hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; 575562306a36Sopenharmony_ci 575662306a36Sopenharmony_ci hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; 575762306a36Sopenharmony_ci /* we create the 802.11 header and a zero-length SSID element */ 575862306a36Sopenharmony_ci hw->wiphy->max_scan_ie_len = max_probe_length - 24 - 2; 575962306a36Sopenharmony_ci 576062306a36Sopenharmony_ci /* Default value; 4 EDCA QOS priorities */ 576162306a36Sopenharmony_ci hw->queues = 4; 576262306a36Sopenharmony_ci 576362306a36Sopenharmony_ci hw->max_listen_interval = IL_CONN_MAX_LISTEN_INTERVAL; 576462306a36Sopenharmony_ci 576562306a36Sopenharmony_ci if (il->bands[NL80211_BAND_2GHZ].n_channels) 576662306a36Sopenharmony_ci il->hw->wiphy->bands[NL80211_BAND_2GHZ] = 576762306a36Sopenharmony_ci &il->bands[NL80211_BAND_2GHZ]; 576862306a36Sopenharmony_ci if (il->bands[NL80211_BAND_5GHZ].n_channels) 576962306a36Sopenharmony_ci il->hw->wiphy->bands[NL80211_BAND_5GHZ] = 577062306a36Sopenharmony_ci &il->bands[NL80211_BAND_5GHZ]; 577162306a36Sopenharmony_ci 577262306a36Sopenharmony_ci il_leds_init(il); 577362306a36Sopenharmony_ci 577462306a36Sopenharmony_ci wiphy_ext_feature_set(il->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 577562306a36Sopenharmony_ci 577662306a36Sopenharmony_ci ret = ieee80211_register_hw(il->hw); 577762306a36Sopenharmony_ci if (ret) { 577862306a36Sopenharmony_ci IL_ERR("Failed to register hw (error %d)\n", ret); 577962306a36Sopenharmony_ci return ret; 578062306a36Sopenharmony_ci } 578162306a36Sopenharmony_ci il->mac80211_registered = 1; 578262306a36Sopenharmony_ci 578362306a36Sopenharmony_ci return 0; 578462306a36Sopenharmony_ci} 578562306a36Sopenharmony_ci 578662306a36Sopenharmony_ciint 578762306a36Sopenharmony_ciil4965_mac_start(struct ieee80211_hw *hw) 578862306a36Sopenharmony_ci{ 578962306a36Sopenharmony_ci struct il_priv *il = hw->priv; 579062306a36Sopenharmony_ci int ret; 579162306a36Sopenharmony_ci 579262306a36Sopenharmony_ci D_MAC80211("enter\n"); 579362306a36Sopenharmony_ci 579462306a36Sopenharmony_ci /* we should be verifying the device is ready to be opened */ 579562306a36Sopenharmony_ci mutex_lock(&il->mutex); 579662306a36Sopenharmony_ci ret = __il4965_up(il); 579762306a36Sopenharmony_ci mutex_unlock(&il->mutex); 579862306a36Sopenharmony_ci 579962306a36Sopenharmony_ci if (ret) 580062306a36Sopenharmony_ci return ret; 580162306a36Sopenharmony_ci 580262306a36Sopenharmony_ci if (il_is_rfkill(il)) 580362306a36Sopenharmony_ci goto out; 580462306a36Sopenharmony_ci 580562306a36Sopenharmony_ci D_INFO("Start UP work done.\n"); 580662306a36Sopenharmony_ci 580762306a36Sopenharmony_ci /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from 580862306a36Sopenharmony_ci * mac80211 will not be run successfully. */ 580962306a36Sopenharmony_ci ret = wait_event_timeout(il->wait_command_queue, 581062306a36Sopenharmony_ci test_bit(S_READY, &il->status), 581162306a36Sopenharmony_ci UCODE_READY_TIMEOUT); 581262306a36Sopenharmony_ci if (!ret) { 581362306a36Sopenharmony_ci if (!test_bit(S_READY, &il->status)) { 581462306a36Sopenharmony_ci IL_ERR("START_ALIVE timeout after %dms.\n", 581562306a36Sopenharmony_ci jiffies_to_msecs(UCODE_READY_TIMEOUT)); 581662306a36Sopenharmony_ci return -ETIMEDOUT; 581762306a36Sopenharmony_ci } 581862306a36Sopenharmony_ci } 581962306a36Sopenharmony_ci 582062306a36Sopenharmony_ci il4965_led_enable(il); 582162306a36Sopenharmony_ci 582262306a36Sopenharmony_ciout: 582362306a36Sopenharmony_ci il->is_open = 1; 582462306a36Sopenharmony_ci D_MAC80211("leave\n"); 582562306a36Sopenharmony_ci return 0; 582662306a36Sopenharmony_ci} 582762306a36Sopenharmony_ci 582862306a36Sopenharmony_civoid 582962306a36Sopenharmony_ciil4965_mac_stop(struct ieee80211_hw *hw) 583062306a36Sopenharmony_ci{ 583162306a36Sopenharmony_ci struct il_priv *il = hw->priv; 583262306a36Sopenharmony_ci 583362306a36Sopenharmony_ci D_MAC80211("enter\n"); 583462306a36Sopenharmony_ci 583562306a36Sopenharmony_ci if (!il->is_open) 583662306a36Sopenharmony_ci return; 583762306a36Sopenharmony_ci 583862306a36Sopenharmony_ci il->is_open = 0; 583962306a36Sopenharmony_ci 584062306a36Sopenharmony_ci il4965_down(il); 584162306a36Sopenharmony_ci 584262306a36Sopenharmony_ci flush_workqueue(il->workqueue); 584362306a36Sopenharmony_ci 584462306a36Sopenharmony_ci /* User space software may expect getting rfkill changes 584562306a36Sopenharmony_ci * even if interface is down */ 584662306a36Sopenharmony_ci _il_wr(il, CSR_INT, 0xFFFFFFFF); 584762306a36Sopenharmony_ci il_enable_rfkill_int(il); 584862306a36Sopenharmony_ci 584962306a36Sopenharmony_ci D_MAC80211("leave\n"); 585062306a36Sopenharmony_ci} 585162306a36Sopenharmony_ci 585262306a36Sopenharmony_civoid 585362306a36Sopenharmony_ciil4965_mac_tx(struct ieee80211_hw *hw, 585462306a36Sopenharmony_ci struct ieee80211_tx_control *control, 585562306a36Sopenharmony_ci struct sk_buff *skb) 585662306a36Sopenharmony_ci{ 585762306a36Sopenharmony_ci struct il_priv *il = hw->priv; 585862306a36Sopenharmony_ci 585962306a36Sopenharmony_ci D_MACDUMP("enter\n"); 586062306a36Sopenharmony_ci 586162306a36Sopenharmony_ci D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, 586262306a36Sopenharmony_ci ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); 586362306a36Sopenharmony_ci 586462306a36Sopenharmony_ci if (il4965_tx_skb(il, control->sta, skb)) 586562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 586662306a36Sopenharmony_ci 586762306a36Sopenharmony_ci D_MACDUMP("leave\n"); 586862306a36Sopenharmony_ci} 586962306a36Sopenharmony_ci 587062306a36Sopenharmony_civoid 587162306a36Sopenharmony_ciil4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 587262306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf, 587362306a36Sopenharmony_ci struct ieee80211_sta *sta, u32 iv32, u16 * phase1key) 587462306a36Sopenharmony_ci{ 587562306a36Sopenharmony_ci struct il_priv *il = hw->priv; 587662306a36Sopenharmony_ci 587762306a36Sopenharmony_ci D_MAC80211("enter\n"); 587862306a36Sopenharmony_ci 587962306a36Sopenharmony_ci il4965_update_tkip_key(il, keyconf, sta, iv32, phase1key); 588062306a36Sopenharmony_ci 588162306a36Sopenharmony_ci D_MAC80211("leave\n"); 588262306a36Sopenharmony_ci} 588362306a36Sopenharmony_ci 588462306a36Sopenharmony_ciint 588562306a36Sopenharmony_ciil4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 588662306a36Sopenharmony_ci struct ieee80211_vif *vif, struct ieee80211_sta *sta, 588762306a36Sopenharmony_ci struct ieee80211_key_conf *key) 588862306a36Sopenharmony_ci{ 588962306a36Sopenharmony_ci struct il_priv *il = hw->priv; 589062306a36Sopenharmony_ci int ret; 589162306a36Sopenharmony_ci u8 sta_id; 589262306a36Sopenharmony_ci bool is_default_wep_key = false; 589362306a36Sopenharmony_ci 589462306a36Sopenharmony_ci D_MAC80211("enter\n"); 589562306a36Sopenharmony_ci 589662306a36Sopenharmony_ci if (il->cfg->mod_params->sw_crypto) { 589762306a36Sopenharmony_ci D_MAC80211("leave - hwcrypto disabled\n"); 589862306a36Sopenharmony_ci return -EOPNOTSUPP; 589962306a36Sopenharmony_ci } 590062306a36Sopenharmony_ci 590162306a36Sopenharmony_ci /* 590262306a36Sopenharmony_ci * To support IBSS RSN, don't program group keys in IBSS, the 590362306a36Sopenharmony_ci * hardware will then not attempt to decrypt the frames. 590462306a36Sopenharmony_ci */ 590562306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_ADHOC && 590662306a36Sopenharmony_ci !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { 590762306a36Sopenharmony_ci D_MAC80211("leave - ad-hoc group key\n"); 590862306a36Sopenharmony_ci return -EOPNOTSUPP; 590962306a36Sopenharmony_ci } 591062306a36Sopenharmony_ci 591162306a36Sopenharmony_ci sta_id = il_sta_id_or_broadcast(il, sta); 591262306a36Sopenharmony_ci if (sta_id == IL_INVALID_STATION) 591362306a36Sopenharmony_ci return -EINVAL; 591462306a36Sopenharmony_ci 591562306a36Sopenharmony_ci mutex_lock(&il->mutex); 591662306a36Sopenharmony_ci il_scan_cancel_timeout(il, 100); 591762306a36Sopenharmony_ci 591862306a36Sopenharmony_ci /* 591962306a36Sopenharmony_ci * If we are getting WEP group key and we didn't receive any key mapping 592062306a36Sopenharmony_ci * so far, we are in legacy wep mode (group key only), otherwise we are 592162306a36Sopenharmony_ci * in 1X mode. 592262306a36Sopenharmony_ci * In legacy wep mode, we use another host command to the uCode. 592362306a36Sopenharmony_ci */ 592462306a36Sopenharmony_ci if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || 592562306a36Sopenharmony_ci key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { 592662306a36Sopenharmony_ci if (cmd == SET_KEY) 592762306a36Sopenharmony_ci is_default_wep_key = !il->_4965.key_mapping_keys; 592862306a36Sopenharmony_ci else 592962306a36Sopenharmony_ci is_default_wep_key = 593062306a36Sopenharmony_ci (key->hw_key_idx == HW_KEY_DEFAULT); 593162306a36Sopenharmony_ci } 593262306a36Sopenharmony_ci 593362306a36Sopenharmony_ci switch (cmd) { 593462306a36Sopenharmony_ci case SET_KEY: 593562306a36Sopenharmony_ci if (is_default_wep_key) 593662306a36Sopenharmony_ci ret = il4965_set_default_wep_key(il, key); 593762306a36Sopenharmony_ci else 593862306a36Sopenharmony_ci ret = il4965_set_dynamic_key(il, key, sta_id); 593962306a36Sopenharmony_ci 594062306a36Sopenharmony_ci D_MAC80211("enable hwcrypto key\n"); 594162306a36Sopenharmony_ci break; 594262306a36Sopenharmony_ci case DISABLE_KEY: 594362306a36Sopenharmony_ci if (is_default_wep_key) 594462306a36Sopenharmony_ci ret = il4965_remove_default_wep_key(il, key); 594562306a36Sopenharmony_ci else 594662306a36Sopenharmony_ci ret = il4965_remove_dynamic_key(il, key, sta_id); 594762306a36Sopenharmony_ci 594862306a36Sopenharmony_ci D_MAC80211("disable hwcrypto key\n"); 594962306a36Sopenharmony_ci break; 595062306a36Sopenharmony_ci default: 595162306a36Sopenharmony_ci ret = -EINVAL; 595262306a36Sopenharmony_ci } 595362306a36Sopenharmony_ci 595462306a36Sopenharmony_ci mutex_unlock(&il->mutex); 595562306a36Sopenharmony_ci D_MAC80211("leave\n"); 595662306a36Sopenharmony_ci 595762306a36Sopenharmony_ci return ret; 595862306a36Sopenharmony_ci} 595962306a36Sopenharmony_ci 596062306a36Sopenharmony_ciint 596162306a36Sopenharmony_ciil4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 596262306a36Sopenharmony_ci struct ieee80211_ampdu_params *params) 596362306a36Sopenharmony_ci{ 596462306a36Sopenharmony_ci struct il_priv *il = hw->priv; 596562306a36Sopenharmony_ci int ret = -EINVAL; 596662306a36Sopenharmony_ci struct ieee80211_sta *sta = params->sta; 596762306a36Sopenharmony_ci enum ieee80211_ampdu_mlme_action action = params->action; 596862306a36Sopenharmony_ci u16 tid = params->tid; 596962306a36Sopenharmony_ci u16 *ssn = ¶ms->ssn; 597062306a36Sopenharmony_ci 597162306a36Sopenharmony_ci D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid); 597262306a36Sopenharmony_ci 597362306a36Sopenharmony_ci if (!(il->cfg->sku & IL_SKU_N)) 597462306a36Sopenharmony_ci return -EACCES; 597562306a36Sopenharmony_ci 597662306a36Sopenharmony_ci mutex_lock(&il->mutex); 597762306a36Sopenharmony_ci 597862306a36Sopenharmony_ci switch (action) { 597962306a36Sopenharmony_ci case IEEE80211_AMPDU_RX_START: 598062306a36Sopenharmony_ci D_HT("start Rx\n"); 598162306a36Sopenharmony_ci ret = il4965_sta_rx_agg_start(il, sta, tid, *ssn); 598262306a36Sopenharmony_ci break; 598362306a36Sopenharmony_ci case IEEE80211_AMPDU_RX_STOP: 598462306a36Sopenharmony_ci D_HT("stop Rx\n"); 598562306a36Sopenharmony_ci ret = il4965_sta_rx_agg_stop(il, sta, tid); 598662306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 598762306a36Sopenharmony_ci ret = 0; 598862306a36Sopenharmony_ci break; 598962306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_START: 599062306a36Sopenharmony_ci D_HT("start Tx\n"); 599162306a36Sopenharmony_ci ret = il4965_tx_agg_start(il, vif, sta, tid, ssn); 599262306a36Sopenharmony_ci break; 599362306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_CONT: 599462306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_FLUSH: 599562306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: 599662306a36Sopenharmony_ci D_HT("stop Tx\n"); 599762306a36Sopenharmony_ci ret = il4965_tx_agg_stop(il, vif, sta, tid); 599862306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status)) 599962306a36Sopenharmony_ci ret = 0; 600062306a36Sopenharmony_ci break; 600162306a36Sopenharmony_ci case IEEE80211_AMPDU_TX_OPERATIONAL: 600262306a36Sopenharmony_ci ret = 0; 600362306a36Sopenharmony_ci break; 600462306a36Sopenharmony_ci } 600562306a36Sopenharmony_ci mutex_unlock(&il->mutex); 600662306a36Sopenharmony_ci 600762306a36Sopenharmony_ci return ret; 600862306a36Sopenharmony_ci} 600962306a36Sopenharmony_ci 601062306a36Sopenharmony_ciint 601162306a36Sopenharmony_ciil4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 601262306a36Sopenharmony_ci struct ieee80211_sta *sta) 601362306a36Sopenharmony_ci{ 601462306a36Sopenharmony_ci struct il_priv *il = hw->priv; 601562306a36Sopenharmony_ci struct il_station_priv *sta_priv = (void *)sta->drv_priv; 601662306a36Sopenharmony_ci bool is_ap = vif->type == NL80211_IFTYPE_STATION; 601762306a36Sopenharmony_ci int ret; 601862306a36Sopenharmony_ci u8 sta_id; 601962306a36Sopenharmony_ci 602062306a36Sopenharmony_ci D_INFO("received request to add station %pM\n", sta->addr); 602162306a36Sopenharmony_ci mutex_lock(&il->mutex); 602262306a36Sopenharmony_ci D_INFO("proceeding to add station %pM\n", sta->addr); 602362306a36Sopenharmony_ci sta_priv->common.sta_id = IL_INVALID_STATION; 602462306a36Sopenharmony_ci 602562306a36Sopenharmony_ci atomic_set(&sta_priv->pending_frames, 0); 602662306a36Sopenharmony_ci 602762306a36Sopenharmony_ci ret = 602862306a36Sopenharmony_ci il_add_station_common(il, sta->addr, is_ap, sta, &sta_id); 602962306a36Sopenharmony_ci if (ret) { 603062306a36Sopenharmony_ci IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret); 603162306a36Sopenharmony_ci /* Should we return success if return code is EEXIST ? */ 603262306a36Sopenharmony_ci mutex_unlock(&il->mutex); 603362306a36Sopenharmony_ci return ret; 603462306a36Sopenharmony_ci } 603562306a36Sopenharmony_ci 603662306a36Sopenharmony_ci sta_priv->common.sta_id = sta_id; 603762306a36Sopenharmony_ci 603862306a36Sopenharmony_ci /* Initialize rate scaling */ 603962306a36Sopenharmony_ci D_INFO("Initializing rate scaling for station %pM\n", sta->addr); 604062306a36Sopenharmony_ci il4965_rs_rate_init(il, sta, sta_id); 604162306a36Sopenharmony_ci mutex_unlock(&il->mutex); 604262306a36Sopenharmony_ci 604362306a36Sopenharmony_ci return 0; 604462306a36Sopenharmony_ci} 604562306a36Sopenharmony_ci 604662306a36Sopenharmony_civoid 604762306a36Sopenharmony_ciil4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 604862306a36Sopenharmony_ci struct ieee80211_channel_switch *ch_switch) 604962306a36Sopenharmony_ci{ 605062306a36Sopenharmony_ci struct il_priv *il = hw->priv; 605162306a36Sopenharmony_ci const struct il_channel_info *ch_info; 605262306a36Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 605362306a36Sopenharmony_ci struct ieee80211_channel *channel = ch_switch->chandef.chan; 605462306a36Sopenharmony_ci struct il_ht_config *ht_conf = &il->current_ht_config; 605562306a36Sopenharmony_ci u16 ch; 605662306a36Sopenharmony_ci 605762306a36Sopenharmony_ci D_MAC80211("enter\n"); 605862306a36Sopenharmony_ci 605962306a36Sopenharmony_ci mutex_lock(&il->mutex); 606062306a36Sopenharmony_ci 606162306a36Sopenharmony_ci if (il_is_rfkill(il)) 606262306a36Sopenharmony_ci goto out; 606362306a36Sopenharmony_ci 606462306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status) || 606562306a36Sopenharmony_ci test_bit(S_SCANNING, &il->status) || 606662306a36Sopenharmony_ci test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) 606762306a36Sopenharmony_ci goto out; 606862306a36Sopenharmony_ci 606962306a36Sopenharmony_ci if (!il_is_associated(il)) 607062306a36Sopenharmony_ci goto out; 607162306a36Sopenharmony_ci 607262306a36Sopenharmony_ci if (!il->ops->set_channel_switch) 607362306a36Sopenharmony_ci goto out; 607462306a36Sopenharmony_ci 607562306a36Sopenharmony_ci ch = channel->hw_value; 607662306a36Sopenharmony_ci if (le16_to_cpu(il->active.channel) == ch) 607762306a36Sopenharmony_ci goto out; 607862306a36Sopenharmony_ci 607962306a36Sopenharmony_ci ch_info = il_get_channel_info(il, channel->band, ch); 608062306a36Sopenharmony_ci if (!il_is_channel_valid(ch_info)) { 608162306a36Sopenharmony_ci D_MAC80211("invalid channel\n"); 608262306a36Sopenharmony_ci goto out; 608362306a36Sopenharmony_ci } 608462306a36Sopenharmony_ci 608562306a36Sopenharmony_ci spin_lock_irq(&il->lock); 608662306a36Sopenharmony_ci 608762306a36Sopenharmony_ci il->current_ht_config.smps = conf->smps_mode; 608862306a36Sopenharmony_ci 608962306a36Sopenharmony_ci /* Configure HT40 channels */ 609062306a36Sopenharmony_ci switch (cfg80211_get_chandef_type(&ch_switch->chandef)) { 609162306a36Sopenharmony_ci case NL80211_CHAN_NO_HT: 609262306a36Sopenharmony_ci case NL80211_CHAN_HT20: 609362306a36Sopenharmony_ci il->ht.is_40mhz = false; 609462306a36Sopenharmony_ci il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; 609562306a36Sopenharmony_ci break; 609662306a36Sopenharmony_ci case NL80211_CHAN_HT40MINUS: 609762306a36Sopenharmony_ci il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 609862306a36Sopenharmony_ci il->ht.is_40mhz = true; 609962306a36Sopenharmony_ci break; 610062306a36Sopenharmony_ci case NL80211_CHAN_HT40PLUS: 610162306a36Sopenharmony_ci il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 610262306a36Sopenharmony_ci il->ht.is_40mhz = true; 610362306a36Sopenharmony_ci break; 610462306a36Sopenharmony_ci } 610562306a36Sopenharmony_ci 610662306a36Sopenharmony_ci if ((le16_to_cpu(il->staging.channel) != ch)) 610762306a36Sopenharmony_ci il->staging.flags = 0; 610862306a36Sopenharmony_ci 610962306a36Sopenharmony_ci il_set_rxon_channel(il, channel); 611062306a36Sopenharmony_ci il_set_rxon_ht(il, ht_conf); 611162306a36Sopenharmony_ci il_set_flags_for_band(il, channel->band, il->vif); 611262306a36Sopenharmony_ci 611362306a36Sopenharmony_ci spin_unlock_irq(&il->lock); 611462306a36Sopenharmony_ci 611562306a36Sopenharmony_ci il_set_rate(il); 611662306a36Sopenharmony_ci /* 611762306a36Sopenharmony_ci * at this point, staging_rxon has the 611862306a36Sopenharmony_ci * configuration for channel switch 611962306a36Sopenharmony_ci */ 612062306a36Sopenharmony_ci set_bit(S_CHANNEL_SWITCH_PENDING, &il->status); 612162306a36Sopenharmony_ci il->switch_channel = cpu_to_le16(ch); 612262306a36Sopenharmony_ci if (il->ops->set_channel_switch(il, ch_switch)) { 612362306a36Sopenharmony_ci clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status); 612462306a36Sopenharmony_ci il->switch_channel = 0; 612562306a36Sopenharmony_ci ieee80211_chswitch_done(il->vif, false); 612662306a36Sopenharmony_ci } 612762306a36Sopenharmony_ci 612862306a36Sopenharmony_ciout: 612962306a36Sopenharmony_ci mutex_unlock(&il->mutex); 613062306a36Sopenharmony_ci D_MAC80211("leave\n"); 613162306a36Sopenharmony_ci} 613262306a36Sopenharmony_ci 613362306a36Sopenharmony_civoid 613462306a36Sopenharmony_ciil4965_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, 613562306a36Sopenharmony_ci unsigned int *total_flags, u64 multicast) 613662306a36Sopenharmony_ci{ 613762306a36Sopenharmony_ci struct il_priv *il = hw->priv; 613862306a36Sopenharmony_ci __le32 filter_or = 0, filter_nand = 0; 613962306a36Sopenharmony_ci 614062306a36Sopenharmony_ci#define CHK(test, flag) do { \ 614162306a36Sopenharmony_ci if (*total_flags & (test)) \ 614262306a36Sopenharmony_ci filter_or |= (flag); \ 614362306a36Sopenharmony_ci else \ 614462306a36Sopenharmony_ci filter_nand |= (flag); \ 614562306a36Sopenharmony_ci } while (0) 614662306a36Sopenharmony_ci 614762306a36Sopenharmony_ci D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags, 614862306a36Sopenharmony_ci *total_flags); 614962306a36Sopenharmony_ci 615062306a36Sopenharmony_ci CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK); 615162306a36Sopenharmony_ci /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ 615262306a36Sopenharmony_ci CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); 615362306a36Sopenharmony_ci CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); 615462306a36Sopenharmony_ci 615562306a36Sopenharmony_ci#undef CHK 615662306a36Sopenharmony_ci 615762306a36Sopenharmony_ci mutex_lock(&il->mutex); 615862306a36Sopenharmony_ci 615962306a36Sopenharmony_ci il->staging.filter_flags &= ~filter_nand; 616062306a36Sopenharmony_ci il->staging.filter_flags |= filter_or; 616162306a36Sopenharmony_ci 616262306a36Sopenharmony_ci /* 616362306a36Sopenharmony_ci * Not committing directly because hardware can perform a scan, 616462306a36Sopenharmony_ci * but we'll eventually commit the filter flags change anyway. 616562306a36Sopenharmony_ci */ 616662306a36Sopenharmony_ci 616762306a36Sopenharmony_ci mutex_unlock(&il->mutex); 616862306a36Sopenharmony_ci 616962306a36Sopenharmony_ci /* 617062306a36Sopenharmony_ci * Receiving all multicast frames is always enabled by the 617162306a36Sopenharmony_ci * default flags setup in il_connection_init_rx_config() 617262306a36Sopenharmony_ci * since we currently do not support programming multicast 617362306a36Sopenharmony_ci * filters into the device. 617462306a36Sopenharmony_ci */ 617562306a36Sopenharmony_ci *total_flags &= 617662306a36Sopenharmony_ci FIF_OTHER_BSS | FIF_ALLMULTI | 617762306a36Sopenharmony_ci FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; 617862306a36Sopenharmony_ci} 617962306a36Sopenharmony_ci 618062306a36Sopenharmony_ci/***************************************************************************** 618162306a36Sopenharmony_ci * 618262306a36Sopenharmony_ci * driver setup and teardown 618362306a36Sopenharmony_ci * 618462306a36Sopenharmony_ci *****************************************************************************/ 618562306a36Sopenharmony_ci 618662306a36Sopenharmony_cistatic void 618762306a36Sopenharmony_ciil4965_bg_txpower_work(struct work_struct *work) 618862306a36Sopenharmony_ci{ 618962306a36Sopenharmony_ci struct il_priv *il = container_of(work, struct il_priv, 619062306a36Sopenharmony_ci txpower_work); 619162306a36Sopenharmony_ci 619262306a36Sopenharmony_ci mutex_lock(&il->mutex); 619362306a36Sopenharmony_ci 619462306a36Sopenharmony_ci /* If a scan happened to start before we got here 619562306a36Sopenharmony_ci * then just return; the stats notification will 619662306a36Sopenharmony_ci * kick off another scheduled work to compensate for 619762306a36Sopenharmony_ci * any temperature delta we missed here. */ 619862306a36Sopenharmony_ci if (test_bit(S_EXIT_PENDING, &il->status) || 619962306a36Sopenharmony_ci test_bit(S_SCANNING, &il->status)) 620062306a36Sopenharmony_ci goto out; 620162306a36Sopenharmony_ci 620262306a36Sopenharmony_ci /* Regardless of if we are associated, we must reconfigure the 620362306a36Sopenharmony_ci * TX power since frames can be sent on non-radar channels while 620462306a36Sopenharmony_ci * not associated */ 620562306a36Sopenharmony_ci il->ops->send_tx_power(il); 620662306a36Sopenharmony_ci 620762306a36Sopenharmony_ci /* Update last_temperature to keep is_calib_needed from running 620862306a36Sopenharmony_ci * when it isn't needed... */ 620962306a36Sopenharmony_ci il->last_temperature = il->temperature; 621062306a36Sopenharmony_ciout: 621162306a36Sopenharmony_ci mutex_unlock(&il->mutex); 621262306a36Sopenharmony_ci} 621362306a36Sopenharmony_ci 621462306a36Sopenharmony_cistatic int 621562306a36Sopenharmony_ciil4965_setup_deferred_work(struct il_priv *il) 621662306a36Sopenharmony_ci{ 621762306a36Sopenharmony_ci il->workqueue = create_singlethread_workqueue(DRV_NAME); 621862306a36Sopenharmony_ci if (!il->workqueue) 621962306a36Sopenharmony_ci return -ENOMEM; 622062306a36Sopenharmony_ci 622162306a36Sopenharmony_ci init_waitqueue_head(&il->wait_command_queue); 622262306a36Sopenharmony_ci 622362306a36Sopenharmony_ci INIT_WORK(&il->restart, il4965_bg_restart); 622462306a36Sopenharmony_ci INIT_WORK(&il->rx_replenish, il4965_bg_rx_replenish); 622562306a36Sopenharmony_ci INIT_WORK(&il->run_time_calib_work, il4965_bg_run_time_calib_work); 622662306a36Sopenharmony_ci INIT_DELAYED_WORK(&il->init_alive_start, il4965_bg_init_alive_start); 622762306a36Sopenharmony_ci INIT_DELAYED_WORK(&il->alive_start, il4965_bg_alive_start); 622862306a36Sopenharmony_ci 622962306a36Sopenharmony_ci il_setup_scan_deferred_work(il); 623062306a36Sopenharmony_ci 623162306a36Sopenharmony_ci INIT_WORK(&il->txpower_work, il4965_bg_txpower_work); 623262306a36Sopenharmony_ci 623362306a36Sopenharmony_ci timer_setup(&il->stats_periodic, il4965_bg_stats_periodic, 0); 623462306a36Sopenharmony_ci 623562306a36Sopenharmony_ci timer_setup(&il->watchdog, il_bg_watchdog, 0); 623662306a36Sopenharmony_ci 623762306a36Sopenharmony_ci tasklet_setup(&il->irq_tasklet, il4965_irq_tasklet); 623862306a36Sopenharmony_ci 623962306a36Sopenharmony_ci return 0; 624062306a36Sopenharmony_ci} 624162306a36Sopenharmony_ci 624262306a36Sopenharmony_cistatic void 624362306a36Sopenharmony_ciil4965_cancel_deferred_work(struct il_priv *il) 624462306a36Sopenharmony_ci{ 624562306a36Sopenharmony_ci cancel_work_sync(&il->txpower_work); 624662306a36Sopenharmony_ci cancel_delayed_work_sync(&il->init_alive_start); 624762306a36Sopenharmony_ci cancel_delayed_work(&il->alive_start); 624862306a36Sopenharmony_ci cancel_work_sync(&il->run_time_calib_work); 624962306a36Sopenharmony_ci 625062306a36Sopenharmony_ci il_cancel_scan_deferred_work(il); 625162306a36Sopenharmony_ci 625262306a36Sopenharmony_ci del_timer_sync(&il->stats_periodic); 625362306a36Sopenharmony_ci} 625462306a36Sopenharmony_ci 625562306a36Sopenharmony_cistatic void 625662306a36Sopenharmony_ciil4965_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates) 625762306a36Sopenharmony_ci{ 625862306a36Sopenharmony_ci int i; 625962306a36Sopenharmony_ci 626062306a36Sopenharmony_ci for (i = 0; i < RATE_COUNT_LEGACY; i++) { 626162306a36Sopenharmony_ci rates[i].bitrate = il_rates[i].ieee * 5; 626262306a36Sopenharmony_ci rates[i].hw_value = i; /* Rate scaling will work on idxes */ 626362306a36Sopenharmony_ci rates[i].hw_value_short = i; 626462306a36Sopenharmony_ci rates[i].flags = 0; 626562306a36Sopenharmony_ci if ((i >= IL_FIRST_CCK_RATE) && (i <= IL_LAST_CCK_RATE)) { 626662306a36Sopenharmony_ci /* 626762306a36Sopenharmony_ci * If CCK != 1M then set short preamble rate flag. 626862306a36Sopenharmony_ci */ 626962306a36Sopenharmony_ci rates[i].flags |= 627062306a36Sopenharmony_ci (il_rates[i].plcp == 627162306a36Sopenharmony_ci RATE_1M_PLCP) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE; 627262306a36Sopenharmony_ci } 627362306a36Sopenharmony_ci } 627462306a36Sopenharmony_ci} 627562306a36Sopenharmony_ci 627662306a36Sopenharmony_ci/* 627762306a36Sopenharmony_ci * Acquire il->lock before calling this function ! 627862306a36Sopenharmony_ci */ 627962306a36Sopenharmony_civoid 628062306a36Sopenharmony_ciil4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx) 628162306a36Sopenharmony_ci{ 628262306a36Sopenharmony_ci il_wr(il, HBUS_TARG_WRPTR, (idx & 0xff) | (txq_id << 8)); 628362306a36Sopenharmony_ci il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(txq_id), idx); 628462306a36Sopenharmony_ci} 628562306a36Sopenharmony_ci 628662306a36Sopenharmony_civoid 628762306a36Sopenharmony_ciil4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, 628862306a36Sopenharmony_ci int tx_fifo_id, int scd_retry) 628962306a36Sopenharmony_ci{ 629062306a36Sopenharmony_ci int txq_id = txq->q.id; 629162306a36Sopenharmony_ci 629262306a36Sopenharmony_ci /* Find out whether to activate Tx queue */ 629362306a36Sopenharmony_ci int active = test_bit(txq_id, &il->txq_ctx_active_msk) ? 1 : 0; 629462306a36Sopenharmony_ci 629562306a36Sopenharmony_ci /* Set up and activate */ 629662306a36Sopenharmony_ci il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id), 629762306a36Sopenharmony_ci (active << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 629862306a36Sopenharmony_ci (tx_fifo_id << IL49_SCD_QUEUE_STTS_REG_POS_TXF) | 629962306a36Sopenharmony_ci (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_WSL) | 630062306a36Sopenharmony_ci (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) | 630162306a36Sopenharmony_ci IL49_SCD_QUEUE_STTS_REG_MSK); 630262306a36Sopenharmony_ci 630362306a36Sopenharmony_ci txq->sched_retry = scd_retry; 630462306a36Sopenharmony_ci 630562306a36Sopenharmony_ci D_INFO("%s %s Queue %d on AC %d\n", active ? "Activate" : "Deactivate", 630662306a36Sopenharmony_ci scd_retry ? "BA" : "AC", txq_id, tx_fifo_id); 630762306a36Sopenharmony_ci} 630862306a36Sopenharmony_ci 630962306a36Sopenharmony_cistatic const struct ieee80211_ops il4965_mac_ops = { 631062306a36Sopenharmony_ci .tx = il4965_mac_tx, 631162306a36Sopenharmony_ci .wake_tx_queue = ieee80211_handle_wake_tx_queue, 631262306a36Sopenharmony_ci .start = il4965_mac_start, 631362306a36Sopenharmony_ci .stop = il4965_mac_stop, 631462306a36Sopenharmony_ci .add_interface = il_mac_add_interface, 631562306a36Sopenharmony_ci .remove_interface = il_mac_remove_interface, 631662306a36Sopenharmony_ci .change_interface = il_mac_change_interface, 631762306a36Sopenharmony_ci .config = il_mac_config, 631862306a36Sopenharmony_ci .configure_filter = il4965_configure_filter, 631962306a36Sopenharmony_ci .set_key = il4965_mac_set_key, 632062306a36Sopenharmony_ci .update_tkip_key = il4965_mac_update_tkip_key, 632162306a36Sopenharmony_ci .conf_tx = il_mac_conf_tx, 632262306a36Sopenharmony_ci .reset_tsf = il_mac_reset_tsf, 632362306a36Sopenharmony_ci .bss_info_changed = il_mac_bss_info_changed, 632462306a36Sopenharmony_ci .ampdu_action = il4965_mac_ampdu_action, 632562306a36Sopenharmony_ci .hw_scan = il_mac_hw_scan, 632662306a36Sopenharmony_ci .sta_add = il4965_mac_sta_add, 632762306a36Sopenharmony_ci .sta_remove = il_mac_sta_remove, 632862306a36Sopenharmony_ci .channel_switch = il4965_mac_channel_switch, 632962306a36Sopenharmony_ci .tx_last_beacon = il_mac_tx_last_beacon, 633062306a36Sopenharmony_ci .flush = il_mac_flush, 633162306a36Sopenharmony_ci}; 633262306a36Sopenharmony_ci 633362306a36Sopenharmony_cistatic int 633462306a36Sopenharmony_ciil4965_init_drv(struct il_priv *il) 633562306a36Sopenharmony_ci{ 633662306a36Sopenharmony_ci int ret; 633762306a36Sopenharmony_ci 633862306a36Sopenharmony_ci spin_lock_init(&il->sta_lock); 633962306a36Sopenharmony_ci spin_lock_init(&il->hcmd_lock); 634062306a36Sopenharmony_ci 634162306a36Sopenharmony_ci INIT_LIST_HEAD(&il->free_frames); 634262306a36Sopenharmony_ci 634362306a36Sopenharmony_ci mutex_init(&il->mutex); 634462306a36Sopenharmony_ci 634562306a36Sopenharmony_ci il->ieee_channels = NULL; 634662306a36Sopenharmony_ci il->ieee_rates = NULL; 634762306a36Sopenharmony_ci il->band = NL80211_BAND_2GHZ; 634862306a36Sopenharmony_ci 634962306a36Sopenharmony_ci il->iw_mode = NL80211_IFTYPE_STATION; 635062306a36Sopenharmony_ci il->current_ht_config.smps = IEEE80211_SMPS_STATIC; 635162306a36Sopenharmony_ci il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; 635262306a36Sopenharmony_ci 635362306a36Sopenharmony_ci /* initialize force reset */ 635462306a36Sopenharmony_ci il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD; 635562306a36Sopenharmony_ci 635662306a36Sopenharmony_ci /* Choose which receivers/antennas to use */ 635762306a36Sopenharmony_ci if (il->ops->set_rxon_chain) 635862306a36Sopenharmony_ci il->ops->set_rxon_chain(il); 635962306a36Sopenharmony_ci 636062306a36Sopenharmony_ci il_init_scan_params(il); 636162306a36Sopenharmony_ci 636262306a36Sopenharmony_ci ret = il_init_channel_map(il); 636362306a36Sopenharmony_ci if (ret) { 636462306a36Sopenharmony_ci IL_ERR("initializing regulatory failed: %d\n", ret); 636562306a36Sopenharmony_ci goto err; 636662306a36Sopenharmony_ci } 636762306a36Sopenharmony_ci 636862306a36Sopenharmony_ci ret = il_init_geos(il); 636962306a36Sopenharmony_ci if (ret) { 637062306a36Sopenharmony_ci IL_ERR("initializing geos failed: %d\n", ret); 637162306a36Sopenharmony_ci goto err_free_channel_map; 637262306a36Sopenharmony_ci } 637362306a36Sopenharmony_ci il4965_init_hw_rates(il, il->ieee_rates); 637462306a36Sopenharmony_ci 637562306a36Sopenharmony_ci return 0; 637662306a36Sopenharmony_ci 637762306a36Sopenharmony_cierr_free_channel_map: 637862306a36Sopenharmony_ci il_free_channel_map(il); 637962306a36Sopenharmony_cierr: 638062306a36Sopenharmony_ci return ret; 638162306a36Sopenharmony_ci} 638262306a36Sopenharmony_ci 638362306a36Sopenharmony_cistatic void 638462306a36Sopenharmony_ciil4965_uninit_drv(struct il_priv *il) 638562306a36Sopenharmony_ci{ 638662306a36Sopenharmony_ci il_free_geos(il); 638762306a36Sopenharmony_ci il_free_channel_map(il); 638862306a36Sopenharmony_ci kfree(il->scan_cmd); 638962306a36Sopenharmony_ci} 639062306a36Sopenharmony_ci 639162306a36Sopenharmony_cistatic void 639262306a36Sopenharmony_ciil4965_hw_detect(struct il_priv *il) 639362306a36Sopenharmony_ci{ 639462306a36Sopenharmony_ci il->hw_rev = _il_rd(il, CSR_HW_REV); 639562306a36Sopenharmony_ci il->hw_wa_rev = _il_rd(il, CSR_HW_REV_WA_REG); 639662306a36Sopenharmony_ci il->rev_id = il->pci_dev->revision; 639762306a36Sopenharmony_ci D_INFO("HW Revision ID = 0x%X\n", il->rev_id); 639862306a36Sopenharmony_ci} 639962306a36Sopenharmony_ci 640062306a36Sopenharmony_cistatic const struct il_sensitivity_ranges il4965_sensitivity = { 640162306a36Sopenharmony_ci .min_nrg_cck = 97, 640262306a36Sopenharmony_ci .max_nrg_cck = 0, /* not used, set to 0 */ 640362306a36Sopenharmony_ci 640462306a36Sopenharmony_ci .auto_corr_min_ofdm = 85, 640562306a36Sopenharmony_ci .auto_corr_min_ofdm_mrc = 170, 640662306a36Sopenharmony_ci .auto_corr_min_ofdm_x1 = 105, 640762306a36Sopenharmony_ci .auto_corr_min_ofdm_mrc_x1 = 220, 640862306a36Sopenharmony_ci 640962306a36Sopenharmony_ci .auto_corr_max_ofdm = 120, 641062306a36Sopenharmony_ci .auto_corr_max_ofdm_mrc = 210, 641162306a36Sopenharmony_ci .auto_corr_max_ofdm_x1 = 140, 641262306a36Sopenharmony_ci .auto_corr_max_ofdm_mrc_x1 = 270, 641362306a36Sopenharmony_ci 641462306a36Sopenharmony_ci .auto_corr_min_cck = 125, 641562306a36Sopenharmony_ci .auto_corr_max_cck = 200, 641662306a36Sopenharmony_ci .auto_corr_min_cck_mrc = 200, 641762306a36Sopenharmony_ci .auto_corr_max_cck_mrc = 400, 641862306a36Sopenharmony_ci 641962306a36Sopenharmony_ci .nrg_th_cck = 100, 642062306a36Sopenharmony_ci .nrg_th_ofdm = 100, 642162306a36Sopenharmony_ci 642262306a36Sopenharmony_ci .barker_corr_th_min = 190, 642362306a36Sopenharmony_ci .barker_corr_th_min_mrc = 390, 642462306a36Sopenharmony_ci .nrg_th_cca = 62, 642562306a36Sopenharmony_ci}; 642662306a36Sopenharmony_ci 642762306a36Sopenharmony_cistatic void 642862306a36Sopenharmony_ciil4965_set_hw_params(struct il_priv *il) 642962306a36Sopenharmony_ci{ 643062306a36Sopenharmony_ci il->hw_params.bcast_id = IL4965_BROADCAST_ID; 643162306a36Sopenharmony_ci il->hw_params.max_rxq_size = RX_QUEUE_SIZE; 643262306a36Sopenharmony_ci il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; 643362306a36Sopenharmony_ci if (il->cfg->mod_params->amsdu_size_8K) 643462306a36Sopenharmony_ci il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_8K); 643562306a36Sopenharmony_ci else 643662306a36Sopenharmony_ci il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_4K); 643762306a36Sopenharmony_ci 643862306a36Sopenharmony_ci il->hw_params.max_beacon_itrvl = IL_MAX_UCODE_BEACON_INTERVAL; 643962306a36Sopenharmony_ci 644062306a36Sopenharmony_ci if (il->cfg->mod_params->disable_11n) 644162306a36Sopenharmony_ci il->cfg->sku &= ~IL_SKU_N; 644262306a36Sopenharmony_ci 644362306a36Sopenharmony_ci if (il->cfg->mod_params->num_of_queues >= IL_MIN_NUM_QUEUES && 644462306a36Sopenharmony_ci il->cfg->mod_params->num_of_queues <= IL49_NUM_QUEUES) 644562306a36Sopenharmony_ci il->cfg->num_of_queues = 644662306a36Sopenharmony_ci il->cfg->mod_params->num_of_queues; 644762306a36Sopenharmony_ci 644862306a36Sopenharmony_ci il->hw_params.max_txq_num = il->cfg->num_of_queues; 644962306a36Sopenharmony_ci il->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM; 645062306a36Sopenharmony_ci il->hw_params.scd_bc_tbls_size = 645162306a36Sopenharmony_ci il->cfg->num_of_queues * 645262306a36Sopenharmony_ci sizeof(struct il4965_scd_bc_tbl); 645362306a36Sopenharmony_ci 645462306a36Sopenharmony_ci il->hw_params.tfd_size = sizeof(struct il_tfd); 645562306a36Sopenharmony_ci il->hw_params.max_stations = IL4965_STATION_COUNT; 645662306a36Sopenharmony_ci il->hw_params.max_data_size = IL49_RTC_DATA_SIZE; 645762306a36Sopenharmony_ci il->hw_params.max_inst_size = IL49_RTC_INST_SIZE; 645862306a36Sopenharmony_ci il->hw_params.max_bsm_size = BSM_SRAM_SIZE; 645962306a36Sopenharmony_ci il->hw_params.ht40_channel = BIT(NL80211_BAND_5GHZ); 646062306a36Sopenharmony_ci 646162306a36Sopenharmony_ci il->hw_params.rx_wrt_ptr_reg = FH49_RSCSR_CHNL0_WPTR; 646262306a36Sopenharmony_ci 646362306a36Sopenharmony_ci il->hw_params.tx_chains_num = il4965_num_of_ant(il->cfg->valid_tx_ant); 646462306a36Sopenharmony_ci il->hw_params.rx_chains_num = il4965_num_of_ant(il->cfg->valid_rx_ant); 646562306a36Sopenharmony_ci il->hw_params.valid_tx_ant = il->cfg->valid_tx_ant; 646662306a36Sopenharmony_ci il->hw_params.valid_rx_ant = il->cfg->valid_rx_ant; 646762306a36Sopenharmony_ci 646862306a36Sopenharmony_ci il->hw_params.ct_kill_threshold = 646962306a36Sopenharmony_ci celsius_to_kelvin(CT_KILL_THRESHOLD_LEGACY); 647062306a36Sopenharmony_ci 647162306a36Sopenharmony_ci il->hw_params.sens = &il4965_sensitivity; 647262306a36Sopenharmony_ci il->hw_params.beacon_time_tsf_bits = IL4965_EXT_BEACON_TIME_POS; 647362306a36Sopenharmony_ci} 647462306a36Sopenharmony_ci 647562306a36Sopenharmony_cistatic int 647662306a36Sopenharmony_ciil4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 647762306a36Sopenharmony_ci{ 647862306a36Sopenharmony_ci int err = 0; 647962306a36Sopenharmony_ci struct il_priv *il; 648062306a36Sopenharmony_ci struct ieee80211_hw *hw; 648162306a36Sopenharmony_ci struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data); 648262306a36Sopenharmony_ci unsigned long flags; 648362306a36Sopenharmony_ci u16 pci_cmd; 648462306a36Sopenharmony_ci 648562306a36Sopenharmony_ci /************************ 648662306a36Sopenharmony_ci * 1. Allocating HW data 648762306a36Sopenharmony_ci ************************/ 648862306a36Sopenharmony_ci 648962306a36Sopenharmony_ci hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il4965_mac_ops); 649062306a36Sopenharmony_ci if (!hw) { 649162306a36Sopenharmony_ci err = -ENOMEM; 649262306a36Sopenharmony_ci goto out; 649362306a36Sopenharmony_ci } 649462306a36Sopenharmony_ci il = hw->priv; 649562306a36Sopenharmony_ci il->hw = hw; 649662306a36Sopenharmony_ci SET_IEEE80211_DEV(hw, &pdev->dev); 649762306a36Sopenharmony_ci 649862306a36Sopenharmony_ci D_INFO("*** LOAD DRIVER ***\n"); 649962306a36Sopenharmony_ci il->cfg = cfg; 650062306a36Sopenharmony_ci il->ops = &il4965_ops; 650162306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS 650262306a36Sopenharmony_ci il->debugfs_ops = &il4965_debugfs_ops; 650362306a36Sopenharmony_ci#endif 650462306a36Sopenharmony_ci il->pci_dev = pdev; 650562306a36Sopenharmony_ci il->inta_mask = CSR_INI_SET_MASK; 650662306a36Sopenharmony_ci 650762306a36Sopenharmony_ci /************************** 650862306a36Sopenharmony_ci * 2. Initializing PCI bus 650962306a36Sopenharmony_ci **************************/ 651062306a36Sopenharmony_ci pci_disable_link_state(pdev, 651162306a36Sopenharmony_ci PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | 651262306a36Sopenharmony_ci PCIE_LINK_STATE_CLKPM); 651362306a36Sopenharmony_ci 651462306a36Sopenharmony_ci if (pci_enable_device(pdev)) { 651562306a36Sopenharmony_ci err = -ENODEV; 651662306a36Sopenharmony_ci goto out_ieee80211_free_hw; 651762306a36Sopenharmony_ci } 651862306a36Sopenharmony_ci 651962306a36Sopenharmony_ci pci_set_master(pdev); 652062306a36Sopenharmony_ci 652162306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); 652262306a36Sopenharmony_ci if (err) { 652362306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 652462306a36Sopenharmony_ci /* both attempts failed: */ 652562306a36Sopenharmony_ci if (err) { 652662306a36Sopenharmony_ci IL_WARN("No suitable DMA available.\n"); 652762306a36Sopenharmony_ci goto out_pci_disable_device; 652862306a36Sopenharmony_ci } 652962306a36Sopenharmony_ci } 653062306a36Sopenharmony_ci 653162306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 653262306a36Sopenharmony_ci if (err) 653362306a36Sopenharmony_ci goto out_pci_disable_device; 653462306a36Sopenharmony_ci 653562306a36Sopenharmony_ci pci_set_drvdata(pdev, il); 653662306a36Sopenharmony_ci 653762306a36Sopenharmony_ci /*********************** 653862306a36Sopenharmony_ci * 3. Read REV register 653962306a36Sopenharmony_ci ***********************/ 654062306a36Sopenharmony_ci il->hw_base = pci_ioremap_bar(pdev, 0); 654162306a36Sopenharmony_ci if (!il->hw_base) { 654262306a36Sopenharmony_ci err = -ENODEV; 654362306a36Sopenharmony_ci goto out_pci_release_regions; 654462306a36Sopenharmony_ci } 654562306a36Sopenharmony_ci 654662306a36Sopenharmony_ci D_INFO("pci_resource_len = 0x%08llx\n", 654762306a36Sopenharmony_ci (unsigned long long)pci_resource_len(pdev, 0)); 654862306a36Sopenharmony_ci D_INFO("pci_resource_base = %p\n", il->hw_base); 654962306a36Sopenharmony_ci 655062306a36Sopenharmony_ci /* these spin locks will be used in apm_ops.init and EEPROM access 655162306a36Sopenharmony_ci * we should init now 655262306a36Sopenharmony_ci */ 655362306a36Sopenharmony_ci spin_lock_init(&il->reg_lock); 655462306a36Sopenharmony_ci spin_lock_init(&il->lock); 655562306a36Sopenharmony_ci 655662306a36Sopenharmony_ci /* 655762306a36Sopenharmony_ci * stop and reset the on-board processor just in case it is in a 655862306a36Sopenharmony_ci * strange state ... like being left stranded by a primary kernel 655962306a36Sopenharmony_ci * and this is now the kdump kernel trying to start up 656062306a36Sopenharmony_ci */ 656162306a36Sopenharmony_ci _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); 656262306a36Sopenharmony_ci 656362306a36Sopenharmony_ci il4965_hw_detect(il); 656462306a36Sopenharmony_ci IL_INFO("Detected %s, REV=0x%X\n", il->cfg->name, il->hw_rev); 656562306a36Sopenharmony_ci 656662306a36Sopenharmony_ci /* We disable the RETRY_TIMEOUT register (0x41) to keep 656762306a36Sopenharmony_ci * PCI Tx retries from interfering with C3 CPU state */ 656862306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); 656962306a36Sopenharmony_ci 657062306a36Sopenharmony_ci il4965_prepare_card_hw(il); 657162306a36Sopenharmony_ci if (!il->hw_ready) { 657262306a36Sopenharmony_ci IL_WARN("Failed, HW not ready\n"); 657362306a36Sopenharmony_ci err = -EIO; 657462306a36Sopenharmony_ci goto out_iounmap; 657562306a36Sopenharmony_ci } 657662306a36Sopenharmony_ci 657762306a36Sopenharmony_ci /***************** 657862306a36Sopenharmony_ci * 4. Read EEPROM 657962306a36Sopenharmony_ci *****************/ 658062306a36Sopenharmony_ci /* Read the EEPROM */ 658162306a36Sopenharmony_ci err = il_eeprom_init(il); 658262306a36Sopenharmony_ci if (err) { 658362306a36Sopenharmony_ci IL_ERR("Unable to init EEPROM\n"); 658462306a36Sopenharmony_ci goto out_iounmap; 658562306a36Sopenharmony_ci } 658662306a36Sopenharmony_ci err = il4965_eeprom_check_version(il); 658762306a36Sopenharmony_ci if (err) 658862306a36Sopenharmony_ci goto out_free_eeprom; 658962306a36Sopenharmony_ci 659062306a36Sopenharmony_ci /* extract MAC Address */ 659162306a36Sopenharmony_ci il4965_eeprom_get_mac(il, il->addresses[0].addr); 659262306a36Sopenharmony_ci D_INFO("MAC address: %pM\n", il->addresses[0].addr); 659362306a36Sopenharmony_ci il->hw->wiphy->addresses = il->addresses; 659462306a36Sopenharmony_ci il->hw->wiphy->n_addresses = 1; 659562306a36Sopenharmony_ci 659662306a36Sopenharmony_ci /************************ 659762306a36Sopenharmony_ci * 5. Setup HW constants 659862306a36Sopenharmony_ci ************************/ 659962306a36Sopenharmony_ci il4965_set_hw_params(il); 660062306a36Sopenharmony_ci 660162306a36Sopenharmony_ci /******************* 660262306a36Sopenharmony_ci * 6. Setup il 660362306a36Sopenharmony_ci *******************/ 660462306a36Sopenharmony_ci 660562306a36Sopenharmony_ci err = il4965_init_drv(il); 660662306a36Sopenharmony_ci if (err) 660762306a36Sopenharmony_ci goto out_free_eeprom; 660862306a36Sopenharmony_ci /* At this point both hw and il are initialized. */ 660962306a36Sopenharmony_ci 661062306a36Sopenharmony_ci /******************** 661162306a36Sopenharmony_ci * 7. Setup services 661262306a36Sopenharmony_ci ********************/ 661362306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 661462306a36Sopenharmony_ci il_disable_interrupts(il); 661562306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 661662306a36Sopenharmony_ci 661762306a36Sopenharmony_ci pci_enable_msi(il->pci_dev); 661862306a36Sopenharmony_ci 661962306a36Sopenharmony_ci err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il); 662062306a36Sopenharmony_ci if (err) { 662162306a36Sopenharmony_ci IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq); 662262306a36Sopenharmony_ci goto out_disable_msi; 662362306a36Sopenharmony_ci } 662462306a36Sopenharmony_ci 662562306a36Sopenharmony_ci err = il4965_setup_deferred_work(il); 662662306a36Sopenharmony_ci if (err) 662762306a36Sopenharmony_ci goto out_free_irq; 662862306a36Sopenharmony_ci 662962306a36Sopenharmony_ci il4965_setup_handlers(il); 663062306a36Sopenharmony_ci 663162306a36Sopenharmony_ci /********************************************* 663262306a36Sopenharmony_ci * 8. Enable interrupts and read RFKILL state 663362306a36Sopenharmony_ci *********************************************/ 663462306a36Sopenharmony_ci 663562306a36Sopenharmony_ci /* enable rfkill interrupt: hw bug w/a */ 663662306a36Sopenharmony_ci pci_read_config_word(il->pci_dev, PCI_COMMAND, &pci_cmd); 663762306a36Sopenharmony_ci if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { 663862306a36Sopenharmony_ci pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; 663962306a36Sopenharmony_ci pci_write_config_word(il->pci_dev, PCI_COMMAND, pci_cmd); 664062306a36Sopenharmony_ci } 664162306a36Sopenharmony_ci 664262306a36Sopenharmony_ci il_enable_rfkill_int(il); 664362306a36Sopenharmony_ci 664462306a36Sopenharmony_ci /* If platform's RF_KILL switch is NOT set to KILL */ 664562306a36Sopenharmony_ci if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) 664662306a36Sopenharmony_ci clear_bit(S_RFKILL, &il->status); 664762306a36Sopenharmony_ci else 664862306a36Sopenharmony_ci set_bit(S_RFKILL, &il->status); 664962306a36Sopenharmony_ci 665062306a36Sopenharmony_ci wiphy_rfkill_set_hw_state(il->hw->wiphy, 665162306a36Sopenharmony_ci test_bit(S_RFKILL, &il->status)); 665262306a36Sopenharmony_ci 665362306a36Sopenharmony_ci il_power_initialize(il); 665462306a36Sopenharmony_ci 665562306a36Sopenharmony_ci init_completion(&il->_4965.firmware_loading_complete); 665662306a36Sopenharmony_ci 665762306a36Sopenharmony_ci err = il4965_request_firmware(il, true); 665862306a36Sopenharmony_ci if (err) 665962306a36Sopenharmony_ci goto out_destroy_workqueue; 666062306a36Sopenharmony_ci 666162306a36Sopenharmony_ci return 0; 666262306a36Sopenharmony_ci 666362306a36Sopenharmony_ciout_destroy_workqueue: 666462306a36Sopenharmony_ci destroy_workqueue(il->workqueue); 666562306a36Sopenharmony_ci il->workqueue = NULL; 666662306a36Sopenharmony_ciout_free_irq: 666762306a36Sopenharmony_ci free_irq(il->pci_dev->irq, il); 666862306a36Sopenharmony_ciout_disable_msi: 666962306a36Sopenharmony_ci pci_disable_msi(il->pci_dev); 667062306a36Sopenharmony_ci il4965_uninit_drv(il); 667162306a36Sopenharmony_ciout_free_eeprom: 667262306a36Sopenharmony_ci il_eeprom_free(il); 667362306a36Sopenharmony_ciout_iounmap: 667462306a36Sopenharmony_ci iounmap(il->hw_base); 667562306a36Sopenharmony_ciout_pci_release_regions: 667662306a36Sopenharmony_ci pci_release_regions(pdev); 667762306a36Sopenharmony_ciout_pci_disable_device: 667862306a36Sopenharmony_ci pci_disable_device(pdev); 667962306a36Sopenharmony_ciout_ieee80211_free_hw: 668062306a36Sopenharmony_ci ieee80211_free_hw(il->hw); 668162306a36Sopenharmony_ciout: 668262306a36Sopenharmony_ci return err; 668362306a36Sopenharmony_ci} 668462306a36Sopenharmony_ci 668562306a36Sopenharmony_cistatic void 668662306a36Sopenharmony_ciil4965_pci_remove(struct pci_dev *pdev) 668762306a36Sopenharmony_ci{ 668862306a36Sopenharmony_ci struct il_priv *il = pci_get_drvdata(pdev); 668962306a36Sopenharmony_ci unsigned long flags; 669062306a36Sopenharmony_ci 669162306a36Sopenharmony_ci if (!il) 669262306a36Sopenharmony_ci return; 669362306a36Sopenharmony_ci 669462306a36Sopenharmony_ci wait_for_completion(&il->_4965.firmware_loading_complete); 669562306a36Sopenharmony_ci 669662306a36Sopenharmony_ci D_INFO("*** UNLOAD DRIVER ***\n"); 669762306a36Sopenharmony_ci 669862306a36Sopenharmony_ci il_dbgfs_unregister(il); 669962306a36Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &il_attribute_group); 670062306a36Sopenharmony_ci 670162306a36Sopenharmony_ci /* ieee80211_unregister_hw call wil cause il_mac_stop to 670262306a36Sopenharmony_ci * be called and il4965_down since we are removing the device 670362306a36Sopenharmony_ci * we need to set S_EXIT_PENDING bit. 670462306a36Sopenharmony_ci */ 670562306a36Sopenharmony_ci set_bit(S_EXIT_PENDING, &il->status); 670662306a36Sopenharmony_ci 670762306a36Sopenharmony_ci il_leds_exit(il); 670862306a36Sopenharmony_ci 670962306a36Sopenharmony_ci if (il->mac80211_registered) { 671062306a36Sopenharmony_ci ieee80211_unregister_hw(il->hw); 671162306a36Sopenharmony_ci il->mac80211_registered = 0; 671262306a36Sopenharmony_ci } else { 671362306a36Sopenharmony_ci il4965_down(il); 671462306a36Sopenharmony_ci } 671562306a36Sopenharmony_ci 671662306a36Sopenharmony_ci /* 671762306a36Sopenharmony_ci * Make sure device is reset to low power before unloading driver. 671862306a36Sopenharmony_ci * This may be redundant with il4965_down(), but there are paths to 671962306a36Sopenharmony_ci * run il4965_down() without calling apm_ops.stop(), and there are 672062306a36Sopenharmony_ci * paths to avoid running il4965_down() at all before leaving driver. 672162306a36Sopenharmony_ci * This (inexpensive) call *makes sure* device is reset. 672262306a36Sopenharmony_ci */ 672362306a36Sopenharmony_ci il_apm_stop(il); 672462306a36Sopenharmony_ci 672562306a36Sopenharmony_ci /* make sure we flush any pending irq or 672662306a36Sopenharmony_ci * tasklet for the driver 672762306a36Sopenharmony_ci */ 672862306a36Sopenharmony_ci spin_lock_irqsave(&il->lock, flags); 672962306a36Sopenharmony_ci il_disable_interrupts(il); 673062306a36Sopenharmony_ci spin_unlock_irqrestore(&il->lock, flags); 673162306a36Sopenharmony_ci 673262306a36Sopenharmony_ci il4965_synchronize_irq(il); 673362306a36Sopenharmony_ci 673462306a36Sopenharmony_ci il4965_dealloc_ucode_pci(il); 673562306a36Sopenharmony_ci 673662306a36Sopenharmony_ci if (il->rxq.bd) 673762306a36Sopenharmony_ci il4965_rx_queue_free(il, &il->rxq); 673862306a36Sopenharmony_ci il4965_hw_txq_ctx_free(il); 673962306a36Sopenharmony_ci 674062306a36Sopenharmony_ci il_eeprom_free(il); 674162306a36Sopenharmony_ci 674262306a36Sopenharmony_ci /*netif_stop_queue(dev); */ 674362306a36Sopenharmony_ci 674462306a36Sopenharmony_ci /* ieee80211_unregister_hw calls il_mac_stop, which flushes 674562306a36Sopenharmony_ci * il->workqueue... so we can't take down the workqueue 674662306a36Sopenharmony_ci * until now... */ 674762306a36Sopenharmony_ci destroy_workqueue(il->workqueue); 674862306a36Sopenharmony_ci il->workqueue = NULL; 674962306a36Sopenharmony_ci 675062306a36Sopenharmony_ci free_irq(il->pci_dev->irq, il); 675162306a36Sopenharmony_ci pci_disable_msi(il->pci_dev); 675262306a36Sopenharmony_ci iounmap(il->hw_base); 675362306a36Sopenharmony_ci pci_release_regions(pdev); 675462306a36Sopenharmony_ci pci_disable_device(pdev); 675562306a36Sopenharmony_ci 675662306a36Sopenharmony_ci il4965_uninit_drv(il); 675762306a36Sopenharmony_ci 675862306a36Sopenharmony_ci dev_kfree_skb(il->beacon_skb); 675962306a36Sopenharmony_ci 676062306a36Sopenharmony_ci ieee80211_free_hw(il->hw); 676162306a36Sopenharmony_ci} 676262306a36Sopenharmony_ci 676362306a36Sopenharmony_ci/* 676462306a36Sopenharmony_ci * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask 676562306a36Sopenharmony_ci * must be called under il->lock and mac access 676662306a36Sopenharmony_ci */ 676762306a36Sopenharmony_civoid 676862306a36Sopenharmony_ciil4965_txq_set_sched(struct il_priv *il, u32 mask) 676962306a36Sopenharmony_ci{ 677062306a36Sopenharmony_ci il_wr_prph(il, IL49_SCD_TXFACT, mask); 677162306a36Sopenharmony_ci} 677262306a36Sopenharmony_ci 677362306a36Sopenharmony_ci/***************************************************************************** 677462306a36Sopenharmony_ci * 677562306a36Sopenharmony_ci * driver and module entry point 677662306a36Sopenharmony_ci * 677762306a36Sopenharmony_ci *****************************************************************************/ 677862306a36Sopenharmony_ci 677962306a36Sopenharmony_ci/* Hardware specific file defines the PCI IDs table for that hardware module */ 678062306a36Sopenharmony_cistatic const struct pci_device_id il4965_hw_card_ids[] = { 678162306a36Sopenharmony_ci {IL_PCI_DEVICE(0x4229, PCI_ANY_ID, il4965_cfg)}, 678262306a36Sopenharmony_ci {IL_PCI_DEVICE(0x4230, PCI_ANY_ID, il4965_cfg)}, 678362306a36Sopenharmony_ci {0} 678462306a36Sopenharmony_ci}; 678562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, il4965_hw_card_ids); 678662306a36Sopenharmony_ci 678762306a36Sopenharmony_cistatic struct pci_driver il4965_driver = { 678862306a36Sopenharmony_ci .name = DRV_NAME, 678962306a36Sopenharmony_ci .id_table = il4965_hw_card_ids, 679062306a36Sopenharmony_ci .probe = il4965_pci_probe, 679162306a36Sopenharmony_ci .remove = il4965_pci_remove, 679262306a36Sopenharmony_ci .driver.pm = IL_LEGACY_PM_OPS, 679362306a36Sopenharmony_ci}; 679462306a36Sopenharmony_ci 679562306a36Sopenharmony_cistatic int __init 679662306a36Sopenharmony_ciil4965_init(void) 679762306a36Sopenharmony_ci{ 679862306a36Sopenharmony_ci 679962306a36Sopenharmony_ci int ret; 680062306a36Sopenharmony_ci pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); 680162306a36Sopenharmony_ci pr_info(DRV_COPYRIGHT "\n"); 680262306a36Sopenharmony_ci 680362306a36Sopenharmony_ci ret = il4965_rate_control_register(); 680462306a36Sopenharmony_ci if (ret) { 680562306a36Sopenharmony_ci pr_err("Unable to register rate control algorithm: %d\n", ret); 680662306a36Sopenharmony_ci return ret; 680762306a36Sopenharmony_ci } 680862306a36Sopenharmony_ci 680962306a36Sopenharmony_ci ret = pci_register_driver(&il4965_driver); 681062306a36Sopenharmony_ci if (ret) { 681162306a36Sopenharmony_ci pr_err("Unable to initialize PCI module\n"); 681262306a36Sopenharmony_ci goto error_register; 681362306a36Sopenharmony_ci } 681462306a36Sopenharmony_ci 681562306a36Sopenharmony_ci return ret; 681662306a36Sopenharmony_ci 681762306a36Sopenharmony_cierror_register: 681862306a36Sopenharmony_ci il4965_rate_control_unregister(); 681962306a36Sopenharmony_ci return ret; 682062306a36Sopenharmony_ci} 682162306a36Sopenharmony_ci 682262306a36Sopenharmony_cistatic void __exit 682362306a36Sopenharmony_ciil4965_exit(void) 682462306a36Sopenharmony_ci{ 682562306a36Sopenharmony_ci pci_unregister_driver(&il4965_driver); 682662306a36Sopenharmony_ci il4965_rate_control_unregister(); 682762306a36Sopenharmony_ci} 682862306a36Sopenharmony_ci 682962306a36Sopenharmony_cimodule_exit(il4965_exit); 683062306a36Sopenharmony_cimodule_init(il4965_init); 683162306a36Sopenharmony_ci 683262306a36Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG 683362306a36Sopenharmony_cimodule_param_named(debug, il_debug_level, uint, 0644); 683462306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "debug output mask"); 683562306a36Sopenharmony_ci#endif 683662306a36Sopenharmony_ci 683762306a36Sopenharmony_cimodule_param_named(swcrypto, il4965_mod_params.sw_crypto, int, 0444); 683862306a36Sopenharmony_ciMODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); 683962306a36Sopenharmony_cimodule_param_named(queues_num, il4965_mod_params.num_of_queues, int, 0444); 684062306a36Sopenharmony_ciMODULE_PARM_DESC(queues_num, "number of hw queues."); 684162306a36Sopenharmony_cimodule_param_named(11n_disable, il4965_mod_params.disable_11n, int, 0444); 684262306a36Sopenharmony_ciMODULE_PARM_DESC(11n_disable, "disable 11n functionality"); 684362306a36Sopenharmony_cimodule_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int, 0444); 684462306a36Sopenharmony_ciMODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])"); 684562306a36Sopenharmony_cimodule_param_named(fw_restart, il4965_mod_params.restart_fw, int, 0444); 684662306a36Sopenharmony_ciMODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); 6847