Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  4965-mac.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
 *
 * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
 *
 * Portions of this file are derived from the ipw3945 project, as well
 * as portions of the ieee80211 subsystem header files.
 *
 * Contact Information:
 *  Intel Linux Wireless <ilw@linux.intel.com>
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 *
 *****************************************************************************/


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/units.h>

#include <net/mac80211.h>

#include <asm/div64.h>

#define DRV_NAME        "iwl4965"

#include "common.h"
#include "4965.h"

/******************************************************************************
 *
 * module boiler plate
 *
 ******************************************************************************/


/*
 * module name, copyright, version, etc.
 */

#define DRV_DESCRIPTION "Intel(R) Wireless WiFi 4965 driver for Linux"

#ifdef CONFIG_IWLEGACY_DEBUG
#define VD "d"
#else
#define VD
#endif

#define DRV_VERSION     IWLWIFI_VERSION VD

MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
MODULE_ALIAS("iwl4965");

void
il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status)
{
 if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
  IL_ERR("Tx flush command to flush out all frames\n");
  if (!test_bit(S_EXIT_PENDING, &il->status))
   queue_work(il->workqueue, &il->tx_flush);
 }
}

/*
 * EEPROM
 */

struct il_mod_params il4965_mod_params = {
 .restart_fw = 1,
 /* the rest are 0 by default */
};

void
il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq)
{
 unsigned long flags;
 int i;
 spin_lock_irqsave(&rxq->lock, flags);
 INIT_LIST_HEAD(&rxq->rx_free);
 INIT_LIST_HEAD(&rxq->rx_used);
 /* Fill the rx_used queue with _all_ of the Rx buffers */
 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
  /* In the reset function, these buffers may have been allocated
 * to an SKB, so we need to unmap and free potential storage */

  if (rxq->pool[i].page != NULL) {
   dma_unmap_page(&il->pci_dev->dev,
           rxq->pool[i].page_dma,
           PAGE_SIZE << il->hw_params.rx_page_order,
           DMA_FROM_DEVICE);
   __il_free_pages(il, rxq->pool[i].page);
   rxq->pool[i].page = NULL;
  }
  list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
 }

 for (i = 0; i < RX_QUEUE_SIZE; i++)
  rxq->queue[i] = NULL;

 /* Set us so that we have processed and used all buffers, but have
 * not restocked the Rx queue with fresh buffers */

 rxq->read = rxq->write = 0;
 rxq->write_actual = 0;
 rxq->free_count = 0;
 spin_unlock_irqrestore(&rxq->lock, flags);
}

int
il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq)
{
 u32 rb_size;
 const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
 u32 rb_timeout = 0;

 if (il->cfg->mod_params->amsdu_size_8K)
  rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
 else
  rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;

 /* Stop Rx DMA */
 il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0);

 /* Reset driver's Rx queue write idx */
 il_wr(il, FH49_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);

 /* Tell device where to find RBD circular buffer in DRAM */
 il_wr(il, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, (u32) (rxq->bd_dma >> 8));

 /* Tell device where in DRAM to update its Rx status */
 il_wr(il, FH49_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4);

 /* Enable Rx DMA
 * Direct rx interrupts to hosts
 * Rx buffer size 4 or 8k
 * RB timeout 0x10
 * 256 RBDs
 */

 il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG,
       FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
       FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
       FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
       rb_size |
       (rb_timeout << FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
       (rfdnlog << FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS));

 /* Set interrupt coalescing timer to default (2048 usecs) */
 il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_TIMEOUT_DEF);

 return 0;
}

static void
il4965_set_pwr_vmain(struct il_priv *il)
{
/*
 * (for documentation purposes)
 * to set power to V_AUX, do:

if (pci_pme_capable(il->pci_dev, PCI_D3cold))
il_set_bits_mask_prph(il, APMG_PS_CTRL_REG,
       APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
       ~APMG_PS_CTRL_MSK_PWR_SRC);
 */


 il_set_bits_mask_prph(il, APMG_PS_CTRL_REG,
         APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
         ~APMG_PS_CTRL_MSK_PWR_SRC);
}

int
il4965_hw_nic_init(struct il_priv *il)
{
 unsigned long flags;
 struct il_rx_queue *rxq = &il->rxq;
 int ret;

 spin_lock_irqsave(&il->lock, flags);
 il_apm_init(il);
 /* Set interrupt coalescing calibration timer to default (512 usecs) */
 il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_CALIB_TIMEOUT_DEF);
 spin_unlock_irqrestore(&il->lock, flags);

 il4965_set_pwr_vmain(il);
 il4965_nic_config(il);

 /* Allocate the RX queue, or reset if it is already allocated */
 if (!rxq->bd) {
  ret = il_rx_queue_alloc(il);
  if (ret) {
   IL_ERR("Unable to initialize Rx queue\n");
   return -ENOMEM;
  }
 } else
  il4965_rx_queue_reset(il, rxq);

 il4965_rx_replenish(il);

 il4965_rx_init(il, rxq);

 spin_lock_irqsave(&il->lock, flags);

 rxq->need_update = 1;
 il_rx_queue_update_write_ptr(il, rxq);

 spin_unlock_irqrestore(&il->lock, flags);

 /* Allocate or reset and init all Tx and Command queues */
 if (!il->txq) {
  ret = il4965_txq_ctx_alloc(il);
  if (ret)
   return ret;
 } else
  il4965_txq_ctx_reset(il);

 set_bit(S_INIT, &il->status);

 return 0;
}

/*
 * il4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
 */

static inline __le32
il4965_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr)
{
 return cpu_to_le32((u32) (dma_addr >> 8));
}

/*
 * il4965_rx_queue_restock - refill RX queue from pre-allocated pool
 *
 * If there are slots in the RX queue that need to be restocked,
 * and we have free pre-allocated buffers, fill the ranks as much
 * as we can, pulling from rx_free.
 *
 * This moves the 'write' idx forward to catch up with 'processed', and
 * also updates the memory address in the firmware to reference the new
 * target buffer.
 */

void
il4965_rx_queue_restock(struct il_priv *il)
{
 struct il_rx_queue *rxq = &il->rxq;
 struct list_head *element;
 struct il_rx_buf *rxb;
 unsigned long flags;

 spin_lock_irqsave(&rxq->lock, flags);
 while (il_rx_queue_space(rxq) > 0 && rxq->free_count) {
  /* The overwritten rxb must be a used one */
  rxb = rxq->queue[rxq->write];
  BUG_ON(rxb && rxb->page);

  /* Get next free Rx buffer, remove from free list */
  element = rxq->rx_free.next;
  rxb = list_entry(element, struct il_rx_buf, list);
  list_del(element);

  /* Point to Rx buffer via next RBD in circular buffer */
  rxq->bd[rxq->write] =
      il4965_dma_addr2rbd_ptr(il, rxb->page_dma);
  rxq->queue[rxq->write] = rxb;
  rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
  rxq->free_count--;
 }
 spin_unlock_irqrestore(&rxq->lock, flags);
 /* If the pre-allocated buffer pool is dropping low, schedule to
 * refill it */

 if (rxq->free_count <= RX_LOW_WATERMARK)
  queue_work(il->workqueue, &il->rx_replenish);

 /* If we've added more space for the firmware to place data, tell it.
 * Increment device's write pointer in multiples of 8. */

 if (rxq->write_actual != (rxq->write & ~0x7)) {
  spin_lock_irqsave(&rxq->lock, flags);
  rxq->need_update = 1;
  spin_unlock_irqrestore(&rxq->lock, flags);
  il_rx_queue_update_write_ptr(il, rxq);
 }
}

/*
 * il4965_rx_replenish - Move all used packet from rx_used to rx_free
 *
 * When moving to rx_free an SKB is allocated for the slot.
 *
 * Also restock the Rx queue via il_rx_queue_restock.
 * This is called as a scheduled work item (except for during initialization)
 */

static void
il4965_rx_allocate(struct il_priv *il, gfp_t priority)
{
 struct il_rx_queue *rxq = &il->rxq;
 struct list_head *element;
 struct il_rx_buf *rxb;
 struct page *page;
 dma_addr_t page_dma;
 unsigned long flags;
 gfp_t gfp_mask = priority;

 while (1) {
  spin_lock_irqsave(&rxq->lock, flags);
  if (list_empty(&rxq->rx_used)) {
   spin_unlock_irqrestore(&rxq->lock, flags);
   return;
  }
  spin_unlock_irqrestore(&rxq->lock, flags);

  if (rxq->free_count > RX_LOW_WATERMARK)
   gfp_mask |= __GFP_NOWARN;

  if (il->hw_params.rx_page_order > 0)
   gfp_mask |= __GFP_COMP;

  /* Alloc a new receive buffer */
  page = alloc_pages(gfp_mask, il->hw_params.rx_page_order);
  if (!page) {
   if (net_ratelimit())
    D_INFO("alloc_pages failed, " "order: %d\n",
           il->hw_params.rx_page_order);

   if (rxq->free_count <= RX_LOW_WATERMARK &&
       net_ratelimit())
    IL_ERR("Failed to alloc_pages with %s. "
           "Only %u free buffers remaining.\n",
           priority ==
           GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL",
           rxq->free_count);
   /* We don't reschedule replenish work here -- we will
 * call the restock method and if it still needs
 * more buffers it will schedule replenish */

   return;
  }

  /* Get physical address of the RB */
  page_dma = dma_map_page(&il->pci_dev->dev, page, 0,
     PAGE_SIZE << il->hw_params.rx_page_order,
     DMA_FROM_DEVICE);
  if (unlikely(dma_mapping_error(&il->pci_dev->dev, page_dma))) {
   __free_pages(page, il->hw_params.rx_page_order);
   break;
  }

  spin_lock_irqsave(&rxq->lock, flags);

  if (list_empty(&rxq->rx_used)) {
   spin_unlock_irqrestore(&rxq->lock, flags);
   dma_unmap_page(&il->pci_dev->dev, page_dma,
           PAGE_SIZE << il->hw_params.rx_page_order,
           DMA_FROM_DEVICE);
   __free_pages(page, il->hw_params.rx_page_order);
   return;
  }

  element = rxq->rx_used.next;
  rxb = list_entry(element, struct il_rx_buf, list);
  list_del(element);

  BUG_ON(rxb->page);

  rxb->page = page;
  rxb->page_dma = page_dma;
  list_add_tail(&rxb->list, &rxq->rx_free);
  rxq->free_count++;
  il->alloc_rxb_page++;

  spin_unlock_irqrestore(&rxq->lock, flags);
 }
}

void
il4965_rx_replenish(struct il_priv *il)
{
 unsigned long flags;

 il4965_rx_allocate(il, GFP_KERNEL);

 spin_lock_irqsave(&il->lock, flags);
 il4965_rx_queue_restock(il);
 spin_unlock_irqrestore(&il->lock, flags);
}

void
il4965_rx_replenish_now(struct il_priv *il)
{
 il4965_rx_allocate(il, GFP_ATOMIC);

 il4965_rx_queue_restock(il);
}

/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
 * If an SKB has been detached, the POOL needs to have its SKB set to NULL
 * This free routine walks the list of POOL entries and if SKB is set to
 * non NULL it is unmapped and freed
 */

void
il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq)
{
 int i;
 for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
  if (rxq->pool[i].page != NULL) {
   dma_unmap_page(&il->pci_dev->dev,
           rxq->pool[i].page_dma,
           PAGE_SIZE << il->hw_params.rx_page_order,
           DMA_FROM_DEVICE);
   __il_free_pages(il, rxq->pool[i].page);
   rxq->pool[i].page = NULL;
  }
 }

 dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
     rxq->bd_dma);
 dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status),
     rxq->rb_stts, rxq->rb_stts_dma);
 rxq->bd = NULL;
 rxq->rb_stts = NULL;
}

int
il4965_rxq_stop(struct il_priv *il)
{
 int ret;

 _il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0);
 ret = _il_poll_bit(il, FH49_MEM_RSSR_RX_STATUS_REG,
      FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
      FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
      1000);
 if (ret < 0)
  IL_ERR("Can't stop Rx DMA.\n");

 return 0;
}

int
il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band)
{
 int idx = 0;
 int band_offset = 0;

 /* HT rate format: mac80211 wants an MCS number, which is just LSB */
 if (rate_n_flags & RATE_MCS_HT_MSK) {
  idx = (rate_n_flags & 0xff);
  return idx;
  /* Legacy rate format, search for match in table */
 } else {
  if (band == NL80211_BAND_5GHZ)
   band_offset = IL_FIRST_OFDM_RATE;
  for (idx = band_offset; idx < RATE_COUNT_LEGACY; idx++)
   if (il_rates[idx].plcp == (rate_n_flags & 0xFF))
    return idx - band_offset;
 }

 return -1;
}

static int
il4965_calc_rssi(struct il_priv *il, struct il_rx_phy_res *rx_resp)
{
 /* data from PHY/DSP regarding signal strength, etc.,
 *   contents are always there, not configurable by host.  */

 struct il4965_rx_non_cfg_phy *ncphy =
     (struct il4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf;
 u32 agc =
     (le16_to_cpu(ncphy->agc_info) & IL49_AGC_DB_MASK) >>
     IL49_AGC_DB_POS;

 u32 valid_antennae =
     (le16_to_cpu(rx_resp->phy_flags) & IL49_RX_PHY_FLAGS_ANTENNAE_MASK)
     >> IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET;
 u8 max_rssi = 0;
 u32 i;

 /* Find max rssi among 3 possible receivers.
 * These values are measured by the digital signal processor (DSP).
 * They should stay fairly constant even as the signal strength varies,
 *   if the radio's automatic gain control (AGC) is working right.
 * AGC value (see below) will provide the "interesting" info. */

 for (i = 0; i < 3; i++)
  if (valid_antennae & (1 << i))
   max_rssi = max(ncphy->rssi_info[i << 1], max_rssi);

 D_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n",
  ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4],
  max_rssi, agc);

 /* dBm = max_rssi dB - agc dB - constant.
 * Higher AGC (higher radio gain) means lower signal. */

 return max_rssi - agc - IL4965_RSSI_OFFSET;
}

static u32
il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in)
{
 u32 decrypt_out = 0;

 if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) ==
     RX_RES_STATUS_STATION_FOUND)
  decrypt_out |=
      (RX_RES_STATUS_STATION_FOUND |
       RX_RES_STATUS_NO_STATION_INFO_MISMATCH);

 decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK);

 /* packet was not encrypted */
 if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
     RX_RES_STATUS_SEC_TYPE_NONE)
  return decrypt_out;

 /* packet was encrypted with unknown alg */
 if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) ==
     RX_RES_STATUS_SEC_TYPE_ERR)
  return decrypt_out;

 /* decryption was not done in HW */
 if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) !=
     RX_MPDU_RES_STATUS_DEC_DONE_MSK)
  return decrypt_out;

 switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) {

 case RX_RES_STATUS_SEC_TYPE_CCMP:
  /* alg is CCM: check MIC only */
  if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK))
   /* Bad MIC */
   decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
  else
   decrypt_out |= RX_RES_STATUS_DECRYPT_OK;

  break;

 case RX_RES_STATUS_SEC_TYPE_TKIP:
  if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) {
   /* Bad TTAK */
   decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK;
   break;
  }
  fallthrough; /* if TTAK OK */
 default:
  if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK))
   decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
  else
   decrypt_out |= RX_RES_STATUS_DECRYPT_OK;
  break;
 }

 D_RX("decrypt_in:0x%x decrypt_out = 0x%x\n", decrypt_in, decrypt_out);

 return decrypt_out;
}

#define SMALL_PACKET_SIZE 256

static void
il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
          u32 len, u32 ampdu_status, struct il_rx_buf *rxb,
          struct ieee80211_rx_status *stats)
{
 struct sk_buff *skb;
 __le16 fc = hdr->frame_control;

 /* We only process data packets if the interface is open */
 if (unlikely(!il->is_open)) {
  D_DROP("Dropping packet while interface is not open.\n");
  return;
 }

 if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
  il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
  D_INFO("Woke queues - frame received on passive channel\n");
 }

 /* In case of HW accelerated crypto and bad decryption, drop */
 if (!il->cfg->mod_params->sw_crypto &&
     il_set_decrypted_flag(il, hdr, ampdu_status, stats))
  return;

 skb = dev_alloc_skb(SMALL_PACKET_SIZE);
 if (!skb) {
  IL_ERR("dev_alloc_skb failed\n");
  return;
 }

 if (len <= SMALL_PACKET_SIZE) {
  skb_put_data(skb, hdr, len);
 } else {
  skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb),
    len, PAGE_SIZE << il->hw_params.rx_page_order);
  il->alloc_rxb_page--;
  rxb->page = NULL;
 }

 il_update_stats(il, false, fc, len);
 memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));

 ieee80211_rx(il->hw, skb);
}

/* Called for N_RX (legacy ABG frames), or
 * N_RX_MPDU (HT high-throughput N frames). */

static void
il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
{
 struct ieee80211_hdr *header;
 struct ieee80211_rx_status rx_status = {};
 struct il_rx_pkt *pkt = rxb_addr(rxb);
 struct il_rx_phy_res *phy_res;
 __le32 rx_pkt_status;
 struct il_rx_mpdu_res_start *amsdu;
 u32 len;
 u32 ampdu_status;
 u32 rate_n_flags;

 /**
 * N_RX and N_RX_MPDU are handled differently.
 * N_RX: physical layer info is in this buffer
 * N_RX_MPDU: physical layer info was sent in separate
 * command and cached in il->last_phy_res
 *
 * Here we set up local variables depending on which command is
 * received.
 */

 if (pkt->hdr.cmd == N_RX) {
  phy_res = (struct il_rx_phy_res *)pkt->u.raw;
  header =
      (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) +
          phy_res->cfg_phy_cnt);

  len = le16_to_cpu(phy_res->byte_count);
  rx_pkt_status =
      *(__le32 *) (pkt->u.raw + sizeof(*phy_res) +
     phy_res->cfg_phy_cnt + len);
  ampdu_status = le32_to_cpu(rx_pkt_status);
 } else {
  if (!il->_4965.last_phy_res_valid) {
   IL_ERR("MPDU frame without cached PHY data\n");
   return;
  }
  phy_res = &il->_4965.last_phy_res;
  amsdu = (struct il_rx_mpdu_res_start *)pkt->u.raw;
  header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu));
  len = le16_to_cpu(amsdu->byte_count);
  rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*amsdu) + len);
  ampdu_status =
      il4965_translate_rx_status(il, le32_to_cpu(rx_pkt_status));
 }

 if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
  D_DROP("dsp size out of range [0,20]: %d\n",
         phy_res->cfg_phy_cnt);
  return;
 }

 if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) ||
     !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
  D_RX("Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status));
  return;
 }

 /* This will be used in several places later */
 rate_n_flags = le32_to_cpu(phy_res->rate_n_flags);

 /* rx_status carries information about the packet to mac80211 */
 rx_status.mactime = le64_to_cpu(phy_res->timestamp);
 rx_status.band =
     (phy_res->
      phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? NL80211_BAND_2GHZ :
     NL80211_BAND_5GHZ;
 rx_status.freq =
     ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel),
        rx_status.band);
 rx_status.rate_idx =
     il4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band);
 rx_status.flag = 0;

 /* TSF isn't reliable. In order to allow smooth user experience,
 * this W/A doesn't propagate it to the mac80211 */

 /*rx_status.flag |= RX_FLAG_MACTIME_START; */

 il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp);

 /* Find max signal strength (dBm) among 3 antenna/receiver chains */
 rx_status.signal = il4965_calc_rssi(il, phy_res);

 D_STATS("Rssi %d, TSF %llu\n", rx_status.signal,
  (unsigned long long)rx_status.mactime);

 /*
 * "antenna number"
 *
 * It seems that the antenna field in the phy flags value
 * is actually a bit field. This is undefined by radiotap,
 * it wants an actual antenna number but I always get "7"
 * for most legacy frames I receive indicating that the
 * same frame was received on all three RX chains.
 *
 * I think this field should be removed in favor of a
 * new 802.11n radiotap field "RX chains" that is defined
 * as a bitmask.
 */

 rx_status.antenna =
     (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >>
     RX_RES_PHY_FLAGS_ANTENNA_POS;

 /* set the preamble flag if appropriate */
 if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK)
  rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE;

 /* Set up the HT phy flags */
 if (rate_n_flags & RATE_MCS_HT_MSK)
  rx_status.encoding = RX_ENC_HT;
 if (rate_n_flags & RATE_MCS_HT40_MSK)
  rx_status.bw = RATE_INFO_BW_40;
 else
  rx_status.bw = RATE_INFO_BW_20;
 if (rate_n_flags & RATE_MCS_SGI_MSK)
  rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI;

 if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) {
  /* We know which subframes of an A-MPDU belong
 * together since we get a single PHY response
 * from the firmware for all of them.
 */


  rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
  rx_status.ampdu_reference = il->_4965.ampdu_ref;
 }

 il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb,
           &rx_status);
}

/* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY).
 * This will be used later in il_hdl_rx() for N_RX_MPDU. */

static void
il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb)
{
 struct il_rx_pkt *pkt = rxb_addr(rxb);
 il->_4965.last_phy_res_valid = true;
 il->_4965.ampdu_ref++;
 memcpy(&il->_4965.last_phy_res, pkt->u.raw,
        sizeof(struct il_rx_phy_res));
}

static int
il4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif,
        enum nl80211_band band, u8 is_active,
        u8 n_probes, struct il_scan_channel *scan_ch)
{
 struct ieee80211_channel *chan;
 const struct ieee80211_supported_band *sband;
 const struct il_channel_info *ch_info;
 u16 passive_dwell = 0;
 u16 active_dwell = 0;
 int added, i;
 u16 channel;

 sband = il_get_hw_mode(il, band);
 if (!sband)
  return 0;

 active_dwell = il_get_active_dwell_time(il, band, n_probes);
 passive_dwell = il_get_passive_dwell_time(il, band, vif);

 if (passive_dwell <= active_dwell)
  passive_dwell = active_dwell + 1;

 for (i = 0, added = 0; i < il->scan_request->n_channels; i++) {
  chan = il->scan_request->channels[i];

  if (chan->band != band)
   continue;

  channel = chan->hw_value;
  scan_ch->channel = cpu_to_le16(channel);

  ch_info = il_get_channel_info(il, band, channel);
  if (!il_is_channel_valid(ch_info)) {
   D_SCAN("Channel %d is INVALID for this band.\n",
          channel);
   continue;
  }

  if (!is_active || il_is_channel_passive(ch_info) ||
      (chan->flags & IEEE80211_CHAN_NO_IR))
   scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
  else
   scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;

  if (n_probes)
   scan_ch->type |= IL_SCAN_PROBE_MASK(n_probes);

  scan_ch->active_dwell = cpu_to_le16(active_dwell);
  scan_ch->passive_dwell = cpu_to_le16(passive_dwell);

  /* Set txpower levels to defaults */
  scan_ch->dsp_atten = 110;

  /* NOTE: if we were doing 6Mb OFDM for scans we'd use
 * power level:
 * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
 */

  if (band == NL80211_BAND_5GHZ)
   scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
  else
   scan_ch->tx_gain = ((1 << 5) | (5 << 3));

  D_SCAN("Scanning ch=%d prob=0x%X [%s %d]\n", channel,
         le32_to_cpu(scan_ch->type),
         (scan_ch->
   type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE",
         (scan_ch->
   type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell :
         passive_dwell);

  scan_ch++;
  added++;
 }

 D_SCAN("total channels to scan %d\n", added);
 return added;
}

static void
il4965_toggle_tx_ant(struct il_priv *il, u8 *ant, u8 valid)
{
 int i;
 u8 ind = *ant;

 for (i = 0; i < RATE_ANT_NUM - 1; i++) {
  ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0;
  if (valid & BIT(ind)) {
   *ant = ind;
   return;
  }
 }
}

int
il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif)
{
 struct il_host_cmd cmd = {
  .id = C_SCAN,
  .len = sizeof(struct il_scan_cmd),
  .flags = CMD_SIZE_HUGE,
 };
 struct il_scan_cmd *scan;
 u32 rate_flags = 0;
 u16 cmd_len;
 u16 rx_chain = 0;
 enum nl80211_band band;
 u8 n_probes = 0;
 u8 rx_ant = il->hw_params.valid_rx_ant;
 u8 rate;
 bool is_active = false;
 int chan_mod;
 u8 active_chains;
 u8 scan_tx_antennas = il->hw_params.valid_tx_ant;
 int ret;

 lockdep_assert_held(&il->mutex);

 if (!il->scan_cmd) {
  il->scan_cmd =
      kmalloc(sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE,
       GFP_KERNEL);
  if (!il->scan_cmd) {
   D_SCAN("fail to allocate memory for scan\n");
   return -ENOMEM;
  }
 }
 scan = il->scan_cmd;
 memset(scan, 0, sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE);

 scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH;
 scan->quiet_time = IL_ACTIVE_QUIET_TIME;

 if (il_is_any_associated(il)) {
  u16 interval;
  u32 extra;
  u32 suspend_time = 100;
  u32 scan_suspend_time = 100;

  D_INFO("Scanning while associated...\n");
  interval = vif->bss_conf.beacon_int;

  scan->suspend_time = 0;
  scan->max_out_time = cpu_to_le32(200 * 1024);
  if (!interval)
   interval = suspend_time;

  extra = (suspend_time / interval) << 22;
  scan_suspend_time =
      (extra | ((suspend_time % interval) * 1024));
  scan->suspend_time = cpu_to_le32(scan_suspend_time);
  D_SCAN("suspend_time 0x%X beacon interval %d\n",
         scan_suspend_time, interval);
 }

 if (il->scan_request->n_ssids) {
  int i, p = 0;
  D_SCAN("Kicking off active scan\n");
  for (i = 0; i < il->scan_request->n_ssids; i++) {
   /* always does wildcard anyway */
   if (!il->scan_request->ssids[i].ssid_len)
    continue;
   scan->direct_scan[p].id = WLAN_EID_SSID;
   scan->direct_scan[p].len =
       il->scan_request->ssids[i].ssid_len;
   memcpy(scan->direct_scan[p].ssid,
          il->scan_request->ssids[i].ssid,
          il->scan_request->ssids[i].ssid_len);
   n_probes++;
   p++;
  }
  is_active = true;
 } else
  D_SCAN("Start passive scan.\n");

 scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
 scan->tx_cmd.sta_id = il->hw_params.bcast_id;
 scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;

 switch (il->scan_band) {
 case NL80211_BAND_2GHZ:
  scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
  chan_mod =
      le32_to_cpu(il->active.flags & RXON_FLG_CHANNEL_MODE_MSK) >>
      RXON_FLG_CHANNEL_MODE_POS;
  if (chan_mod == CHANNEL_MODE_PURE_40) {
   rate = RATE_6M_PLCP;
  } else {
   rate = RATE_1M_PLCP;
   rate_flags = RATE_MCS_CCK_MSK;
  }
  break;
 case NL80211_BAND_5GHZ:
  rate = RATE_6M_PLCP;
  break;
 default:
  IL_WARN("Invalid scan band\n");
  return -EIO;
 }

 /*
 * If active scanning is requested but a certain channel is
 * marked passive, we can do active scanning if we detect
 * transmissions.
 *
 * There is an issue with some firmware versions that triggers
 * a sysassert on a "good CRC threshold" of zero (== disabled),
 * on a radar channel even though this means that we should NOT
 * send probes.
 *
 * The "good CRC threshold" is the number of frames that we
 * need to receive during our dwell time on a channel before
 * sending out probes -- setting this to a huge value will
 * mean we never reach it, but at the same time work around
 * the aforementioned issue. Thus use IL_GOOD_CRC_TH_NEVER
 * here instead of IL_GOOD_CRC_TH_DISABLED.
 */

 scan->good_CRC_th =
     is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER;

 band = il->scan_band;

 if (il->cfg->scan_rx_antennas[band])
  rx_ant = il->cfg->scan_rx_antennas[band];

 il4965_toggle_tx_ant(il, &il->scan_tx_ant[band], scan_tx_antennas);
 rate_flags |= BIT(il->scan_tx_ant[band]) << RATE_MCS_ANT_POS;
 scan->tx_cmd.rate_n_flags = cpu_to_le32(rate | rate_flags);

 /* In power save mode use one chain, otherwise use all chains */
 if (test_bit(S_POWER_PMI, &il->status)) {
  /* rx_ant has been set to all valid chains previously */
  active_chains =
      rx_ant & ((u8) (il->chain_noise_data.active_chains));
  if (!active_chains)
   active_chains = rx_ant;

  D_SCAN("chain_noise_data.active_chains: %u\n",
         il->chain_noise_data.active_chains);

  rx_ant = il4965_first_antenna(active_chains);
 }

 /* MIMO is not used here, but value is required */
 rx_chain |= il->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
 rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
 rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
 rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
 scan->rx_chain = cpu_to_le16(rx_chain);

 cmd_len =
     il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data,
         vif->addr, il->scan_request->ie,
         il->scan_request->ie_len,
         IL_MAX_SCAN_SIZE - sizeof(*scan));
 scan->tx_cmd.len = cpu_to_le16(cmd_len);

 scan->filter_flags |=
     (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK);

 scan->channel_count =
     il4965_get_channels_for_scan(il, vif, band, is_active, n_probes,
      (void *)&scan->data[cmd_len]);
 if (scan->channel_count == 0) {
  D_SCAN("channel count %d\n", scan->channel_count);
  return -EIO;
 }

 cmd.len +=
     le16_to_cpu(scan->tx_cmd.len) +
     scan->channel_count * sizeof(struct il_scan_channel);
 cmd.data = scan;
 scan->len = cpu_to_le16(cmd.len);

 set_bit(S_SCAN_HW, &il->status);

 ret = il_send_cmd_sync(il, &cmd);
 if (ret)
  clear_bit(S_SCAN_HW, &il->status);

 return ret;
}

int
il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif,
      bool add)
{
 struct il_vif_priv *vif_priv = (void *)vif->drv_priv;

 if (add)
  return il4965_add_bssid_station(il, vif->bss_conf.bssid,
      &vif_priv->ibss_bssid_sta_id);
 return il_remove_station(il, vif_priv->ibss_bssid_sta_id,
     vif->bss_conf.bssid);
}

void
il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed)
{
 lockdep_assert_held(&il->sta_lock);

 if (il->stations[sta_id].tid[tid].tfds_in_queue >= freed)
  il->stations[sta_id].tid[tid].tfds_in_queue -= freed;
 else {
  D_TX("free more than tfds_in_queue (%u:%d)\n",
       il->stations[sta_id].tid[tid].tfds_in_queue, freed);
  il->stations[sta_id].tid[tid].tfds_in_queue = 0;
 }
}

#define IL_TX_QUEUE_MSK 0xfffff

static bool
il4965_is_single_rx_stream(struct il_priv *il)
{
 return il->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
     il->current_ht_config.single_chain_sufficient;
}

#define IL_NUM_RX_CHAINS_MULTIPLE 3
#define IL_NUM_RX_CHAINS_SINGLE 2
#define IL_NUM_IDLE_CHAINS_DUAL 2
#define IL_NUM_IDLE_CHAINS_SINGLE 1

/*
 * Determine how many receiver/antenna chains to use.
 *
 * More provides better reception via diversity.  Fewer saves power
 * at the expense of throughput, but only when not in powersave to
 * start with.
 *
 * MIMO (dual stream) requires at least 2, but works better with 3.
 * This does not determine *which* chains to use, just how many.
 */

static int
il4965_get_active_rx_chain_count(struct il_priv *il)
{
 /* # of Rx chains to use when expecting MIMO. */
 if (il4965_is_single_rx_stream(il))
  return IL_NUM_RX_CHAINS_SINGLE;
 else
  return IL_NUM_RX_CHAINS_MULTIPLE;
}

/*
 * When we are in power saving mode, unless device support spatial
 * multiplexing power save, use the active count for rx chain count.
 */

static int
il4965_get_idle_rx_chain_count(struct il_priv *il, int active_cnt)
{
 /* # Rx chains when idling, depending on SMPS mode */
 switch (il->current_ht_config.smps) {
 case IEEE80211_SMPS_STATIC:
 case IEEE80211_SMPS_DYNAMIC:
  return IL_NUM_IDLE_CHAINS_SINGLE;
 case IEEE80211_SMPS_OFF:
  return active_cnt;
 default:
  WARN(1, "invalid SMPS mode %d", il->current_ht_config.smps);
  return active_cnt;
 }
}

/* up to 4 chains */
static u8
il4965_count_chain_bitmap(u32 chain_bitmap)
{
 u8 res;
 res = (chain_bitmap & BIT(0)) >> 0;
 res += (chain_bitmap & BIT(1)) >> 1;
 res += (chain_bitmap & BIT(2)) >> 2;
 res += (chain_bitmap & BIT(3)) >> 3;
 return res;
}

/*
 * il4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
 *
 * Selects how many and which Rx receivers/antennas/chains to use.
 * This should not be used for scan command ... it puts data in wrong place.
 */

void
il4965_set_rxon_chain(struct il_priv *il)
{
 bool is_single = il4965_is_single_rx_stream(il);
 bool is_cam = !test_bit(S_POWER_PMI, &il->status);
 u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
 u32 active_chains;
 u16 rx_chain;

 /* Tell uCode which antennas are actually connected.
 * Before first association, we assume all antennas are connected.
 * Just after first association, il4965_chain_noise_calibration()
 *    checks which antennas actually *are* connected. */

 if (il->chain_noise_data.active_chains)
  active_chains = il->chain_noise_data.active_chains;
 else
  active_chains = il->hw_params.valid_rx_ant;

 rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;

 /* How many receivers should we use? */
 active_rx_cnt = il4965_get_active_rx_chain_count(il);
 idle_rx_cnt = il4965_get_idle_rx_chain_count(il, active_rx_cnt);

 /* correct rx chain count according hw settings
 * and chain noise calibration
 */

 valid_rx_cnt = il4965_count_chain_bitmap(active_chains);
 if (valid_rx_cnt < active_rx_cnt)
  active_rx_cnt = valid_rx_cnt;

 if (valid_rx_cnt < idle_rx_cnt)
  idle_rx_cnt = valid_rx_cnt;

 rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
 rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS;

 il->staging.rx_chain = cpu_to_le16(rx_chain);

 if (!is_single && active_rx_cnt >= IL_NUM_RX_CHAINS_SINGLE && is_cam)
  il->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
 else
  il->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;

 D_ASSOC("rx_chain=0x%X active=%d idle=%d\n", il->staging.rx_chain,
  active_rx_cnt, idle_rx_cnt);

 WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 ||
  active_rx_cnt < idle_rx_cnt);
}

static const char *
il4965_get_fh_string(int cmd)
{
 switch (cmd) {
  IL_CMD(FH49_RSCSR_CHNL0_STTS_WPTR_REG);
  IL_CMD(FH49_RSCSR_CHNL0_RBDCB_BASE_REG);
  IL_CMD(FH49_RSCSR_CHNL0_WPTR);
  IL_CMD(FH49_MEM_RCSR_CHNL0_CONFIG_REG);
  IL_CMD(FH49_MEM_RSSR_SHARED_CTRL_REG);
  IL_CMD(FH49_MEM_RSSR_RX_STATUS_REG);
  IL_CMD(FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
  IL_CMD(FH49_TSSR_TX_STATUS_REG);
  IL_CMD(FH49_TSSR_TX_ERROR_REG);
 default:
  return "UNKNOWN";
 }
}

int
il4965_dump_fh(struct il_priv *il, char **buf, bool display)
{
 int i;
#ifdef CONFIG_IWLEGACY_DEBUG
 int pos = 0;
 size_t bufsz = 0;
#endif
 static const u32 fh_tbl[] = {
  FH49_RSCSR_CHNL0_STTS_WPTR_REG,
  FH49_RSCSR_CHNL0_RBDCB_BASE_REG,
  FH49_RSCSR_CHNL0_WPTR,
  FH49_MEM_RCSR_CHNL0_CONFIG_REG,
  FH49_MEM_RSSR_SHARED_CTRL_REG,
  FH49_MEM_RSSR_RX_STATUS_REG,
  FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
  FH49_TSSR_TX_STATUS_REG,
  FH49_TSSR_TX_ERROR_REG
 };
#ifdef CONFIG_IWLEGACY_DEBUG
 if (display) {
  bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
  *buf = kmalloc(bufsz, GFP_KERNEL);
  if (!*buf)
   return -ENOMEM;
  pos +=
      scnprintf(*buf + pos, bufsz - pos, "FH register values:\n");
  for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
   pos +=
       scnprintf(*buf + pos, bufsz - pos,
          " %34s: 0X%08x\n",
          il4965_get_fh_string(fh_tbl[i]),
          il_rd(il, fh_tbl[i]));
  }
  return pos;
 }
#endif
 IL_ERR("FH register values:\n");
 for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
  IL_ERR(" %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]),
         il_rd(il, fh_tbl[i]));
 }
 return 0;
}

static void
il4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb)
{
 struct il_rx_pkt *pkt = rxb_addr(rxb);
 struct il_missed_beacon_notif *missed_beacon;

 missed_beacon = &pkt->u.missed_beacon;
 if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) >
     il->missed_beacon_threshold) {
  D_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n",
   le32_to_cpu(missed_beacon->consecutive_missed_beacons),
   le32_to_cpu(missed_beacon->total_missed_becons),
   le32_to_cpu(missed_beacon->num_recvd_beacons),
   le32_to_cpu(missed_beacon->num_expected_beacons));
  if (!test_bit(S_SCANNING, &il->status))
   il4965_init_sensitivity(il);
 }
}

/* Calculate noise level, based on measurements during network silence just
 *   before arriving beacon.  This measurement can be done only if we know
 *   exactly when to expect beacons, therefore only when we're associated. */

static void
il4965_rx_calc_noise(struct il_priv *il)
{
 struct stats_rx_non_phy *rx_info;
 int num_active_rx = 0;
 int total_silence = 0;
 int bcn_silence_a, bcn_silence_b, bcn_silence_c;
 int last_rx_noise;

 rx_info = &(il->_4965.stats.rx.general);
 bcn_silence_a =
     le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
 bcn_silence_b =
     le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
 bcn_silence_c =
     le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;

 if (bcn_silence_a) {
  total_silence += bcn_silence_a;
  num_active_rx++;
 }
 if (bcn_silence_b) {
  total_silence += bcn_silence_b;
  num_active_rx++;
 }
 if (bcn_silence_c) {
  total_silence += bcn_silence_c;
  num_active_rx++;
 }

 /* Average among active antennas */
 if (num_active_rx)
  last_rx_noise = (total_silence / num_active_rx) - 107;
 else
  last_rx_noise = IL_NOISE_MEAS_NOT_AVAILABLE;

 D_CALIB("inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a,
  bcn_silence_b, bcn_silence_c, last_rx_noise);
}

#ifdef CONFIG_IWLEGACY_DEBUGFS
/*
 *  based on the assumption of all stats counter are in DWORD
 *  FIXME: This function is for debugging, do not deal with
 *  the case of counters roll-over.
 */

static void
il4965_accumulative_stats(struct il_priv *il, __le32 * stats)
{
 int i, size;
 __le32 *prev_stats;
 u32 *accum_stats;
 u32 *delta, *max_delta;
 struct stats_general_common *general, *accum_general;

 prev_stats = (__le32 *) &il->_4965.stats;
 accum_stats = (u32 *) &il->_4965.accum_stats;
 size = sizeof(struct il_notif_stats);
 general = &il->_4965.stats.general.common;
 accum_general = &il->_4965.accum_stats.general.common;
 delta = (u32 *) &il->_4965.delta_stats;
 max_delta = (u32 *) &il->_4965.max_delta;

 for (i = sizeof(__le32); i < size;
      i +=
      sizeof(__le32), stats++, prev_stats++, delta++, max_delta++,
      accum_stats++) {
  if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) {
   *delta =
       (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats));
   *accum_stats += *delta;
   if (*delta > *max_delta)
    *max_delta = *delta;
  }
 }

 /* reset accumulative stats for "no-counter" type stats */
 accum_general->temperature = general->temperature;
 accum_general->ttl_timestamp = general->ttl_timestamp;
}
#endif

static void
il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb)
{
 const int recalib_seconds = 60;
 bool change;
 struct il_rx_pkt *pkt = rxb_addr(rxb);

 D_RX("Statistics notification received (%d vs %d).\n",
      (int)sizeof(struct il_notif_stats),
      le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK);

 change =
     ((il->_4965.stats.general.common.temperature !=
       pkt->u.stats.general.common.temperature) ||
      ((il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK) !=
       (pkt->u.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)));
#ifdef CONFIG_IWLEGACY_DEBUGFS
 il4965_accumulative_stats(il, (__le32 *) &pkt->u.stats);
#endif

 /* TODO: reading some of stats is unneeded */
 memcpy(&il->_4965.stats, &pkt->u.stats, sizeof(il->_4965.stats));

 set_bit(S_STATS, &il->status);

 /*
 * Reschedule the stats timer to occur in recalib_seconds to ensure
 * we get a thermal update even if the uCode doesn't give us one
 */

 mod_timer(&il->stats_periodic,
    jiffies + secs_to_jiffies(recalib_seconds));

 if (unlikely(!test_bit(S_SCANNING, &il->status)) &&
     (pkt->hdr.cmd == N_STATS)) {
  il4965_rx_calc_noise(il);
  queue_work(il->workqueue, &il->run_time_calib_work);
 }

 if (change)
  il4965_temperature_calib(il);
}

static void
il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb)
{
 struct il_rx_pkt *pkt = rxb_addr(rxb);

 if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATS_CLEAR_MSK) {
#ifdef CONFIG_IWLEGACY_DEBUGFS
  memset(&il->_4965.accum_stats, 0,
         sizeof(struct il_notif_stats));
  memset(&il->_4965.delta_stats, 0,
         sizeof(struct il_notif_stats));
  memset(&il->_4965.max_delta, 0, sizeof(struct il_notif_stats));
#endif
  D_RX("Statistics have been cleared\n");
 }
 il4965_hdl_stats(il, rxb);
}


/*
 * mac80211 queues, ACs, hardware queues, FIFOs.
 *
 * Cf. https://wireless.wiki.kernel.org/en/developers/Documentation/mac80211/queues
 *
 * Mac80211 uses the following numbers, which we get as from it
 * by way of skb_get_queue_mapping(skb):
 *
 *     VO      0
 *     VI      1
 *     BE      2
 *     BK      3
 *
 *
 * Regular (not A-MPDU) frames are put into hardware queues corresponding
 * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their
 * own queue per aggregation session (RA/TID combination), such queues are
 * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In
 * order to map frames to the right queue, we also need an AC->hw queue
 * mapping. This is implemented here.
 *
 * Due to the way hw queues are set up (by the hw specific modules like
 * 4965.c), the AC->hw queue mapping is the identity
 * mapping.
 */


static const u8 tid_to_ac[] = {
 IEEE80211_AC_BE,
 IEEE80211_AC_BK,
 IEEE80211_AC_BK,
 IEEE80211_AC_BE,
 IEEE80211_AC_VI,
 IEEE80211_AC_VI,
 IEEE80211_AC_VO,
 IEEE80211_AC_VO
};

static inline int
il4965_get_ac_from_tid(u16 tid)
{
 if (likely(tid < ARRAY_SIZE(tid_to_ac)))
  return tid_to_ac[tid];

 /* no support for TIDs 8-15 yet */
 return -EINVAL;
}

static inline int
il4965_get_fifo_from_tid(u16 tid)
{
 static const u8 ac_to_fifo[] = {
  IL_TX_FIFO_VO,
  IL_TX_FIFO_VI,
  IL_TX_FIFO_BE,
  IL_TX_FIFO_BK,
 };

 if (likely(tid < ARRAY_SIZE(tid_to_ac)))
  return ac_to_fifo[tid_to_ac[tid]];

 /* no support for TIDs 8-15 yet */
 return -EINVAL;
}

/*
 * handle build C_TX command notification.
 */

static void
il4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb,
     struct il_tx_cmd *tx_cmd,
     struct ieee80211_tx_info *info,
     struct ieee80211_hdr *hdr, u8 std_id)
{
 __le16 fc = hdr->frame_control;
 __le32 tx_flags = tx_cmd->tx_flags;

 tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
 if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
  tx_flags |= TX_CMD_FLG_ACK_MSK;
  if (ieee80211_is_mgmt(fc))
   tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
  if (ieee80211_is_probe_resp(fc) &&
      !(le16_to_cpu(hdr->seq_ctrl) & 0xf))
   tx_flags |= TX_CMD_FLG_TSF_MSK;
 } else {
  tx_flags &= (~TX_CMD_FLG_ACK_MSK);
  tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
 }

 if (ieee80211_is_back_req(fc))
  tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;

 tx_cmd->sta_id = std_id;
 if (ieee80211_has_morefrags(fc))
  tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;

 if (ieee80211_is_data_qos(fc)) {
  u8 *qc = ieee80211_get_qos_ctl(hdr);
  tx_cmd->tid_tspec = qc[0] & 0xf;
  tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
 } else {
  tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
 }

 il_tx_cmd_protection(il, info, fc, &tx_flags);

 tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
 if (ieee80211_is_mgmt(fc)) {
  if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
   tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3);
  else
   tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2);
 } else {
  tx_cmd->timeout.pm_frame_timeout = 0;
 }

 tx_cmd->driver_txop = 0;
 tx_cmd->tx_flags = tx_flags;
 tx_cmd->next_frame_len = 0;
}

static void
il4965_tx_cmd_build_rate(struct il_priv *il,
    struct il_tx_cmd *tx_cmd,
    struct ieee80211_tx_info *info,
    struct ieee80211_sta *sta,
    __le16 fc)
{
 const u8 rts_retry_limit = 60;
 u32 rate_flags;
 int rate_idx;
 u8 data_retry_limit;
 u8 rate_plcp;

 /* Set retry limit on DATA packets and Probe Responses */
 if (ieee80211_is_probe_resp(fc))
  data_retry_limit = 3;
 else
  data_retry_limit = IL4965_DEFAULT_TX_RETRY;
 tx_cmd->data_retry_limit = data_retry_limit;
 /* Set retry limit on RTS packets */
 tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit);

 /* DATA packets will use the uCode station table for rate/antenna
 * selection */

 if (ieee80211_is_data(fc)) {
  tx_cmd->initial_rate_idx = 0;
  tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
  return;
 }

 /**
 * If the current TX rate stored in mac80211 has the MCS bit set, it's
 * not really a TX rate.  Thus, we use the lowest supported rate for
 * this band.  Also use the lowest supported rate if the stored rate
 * idx is invalid.
 */

 rate_idx = info->control.rates[0].idx;
 if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0
     || rate_idx > RATE_COUNT_LEGACY)
  rate_idx = rate_lowest_index(&il->bands[info->band], sta);
 /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
 if (info->band == NL80211_BAND_5GHZ) {
  rate_idx += IL_FIRST_OFDM_RATE;
  if (rate_idx > IL_LAST_OFDM_RATE)
   rate_idx = IL_LAST_OFDM_RATE;
 }
 /* Get PLCP rate for tx_cmd->rate_n_flags */
 rate_plcp = il_rates[rate_idx].plcp;
 /* Zero out flags for this packet */
 rate_flags = 0;

 /* Set CCK flag as needed */
 if (rate_idx >= IL_FIRST_CCK_RATE && rate_idx <= IL_LAST_CCK_RATE)
  rate_flags |= RATE_MCS_CCK_MSK;

 /* Set up antennas */
 il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant);
 rate_flags |= BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS;

 /* Set the rate in the TX cmd */
 tx_cmd->rate_n_flags = cpu_to_le32(rate_plcp | rate_flags);
}

static void
il4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info,
        struct il_tx_cmd *tx_cmd, struct sk_buff *skb_frag,
        int sta_id)
{
 struct ieee80211_key_conf *keyconf = info->control.hw_key;

 switch (keyconf->cipher) {
 case WLAN_CIPHER_SUITE_CCMP:
  tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
  memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
  if (info->flags & IEEE80211_TX_CTL_AMPDU)
   tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK;
  D_TX("tx_cmd with AES hwcrypto\n");
  break;

 case WLAN_CIPHER_SUITE_TKIP:
  tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
  ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
  D_TX("tx_cmd with tkip hwcrypto\n");
  break;

 case WLAN_CIPHER_SUITE_WEP104:
  tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
  fallthrough;
 case WLAN_CIPHER_SUITE_WEP40:
  tx_cmd->sec_ctl |=
      (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) <<
       TX_CMD_SEC_SHIFT);

  memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);

  D_TX("Configuring packet for WEP encryption " "with key %d\n",
       keyconf->keyidx);
  break;

 default:
  IL_ERR("Unknown encode cipher %x\n", keyconf->cipher);
  break;
 }
}

/*
 * start C_TX command process
 */

int
il4965_tx_skb(struct il_priv *il,
       struct ieee80211_sta *sta,
       struct sk_buff *skb)
{
 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 struct il_station_priv *sta_priv = NULL;
 struct il_tx_queue *txq;
 struct il_queue *q;
 struct il_device_cmd *out_cmd;
 struct il_cmd_meta *out_meta;
 struct il_tx_cmd *tx_cmd;
 int txq_id;
 dma_addr_t phys_addr;
 dma_addr_t txcmd_phys;
 dma_addr_t scratch_phys;
 u16 len, firstlen, secondlen;
 u16 seq_number = 0;
 __le16 fc;
 u8 hdr_len;
 u8 sta_id;
 u8 wait_write_ptr = 0;
 u8 tid = 0;
 u8 *qc = NULL;
 unsigned long flags;
 bool is_agg = false;

 spin_lock_irqsave(&il->lock, flags);
 if (il_is_rfkill(il)) {
  D_DROP("Dropping - RF KILL\n");
  goto drop_unlock;
 }

 fc = hdr->frame_control;

#ifdef CONFIG_IWLEGACY_DEBUG
 if (ieee80211_is_auth(fc))
  D_TX("Sending AUTH frame\n");
 else if (ieee80211_is_assoc_req(fc))
  D_TX("Sending ASSOC frame\n");
 else if (ieee80211_is_reassoc_req(fc))
  D_TX("Sending REASSOC frame\n");
#endif

 hdr_len = ieee80211_hdrlen(fc);

 /* For management frames use broadcast id to do not break aggregation */
 if (!ieee80211_is_data(fc))
  sta_id = il->hw_params.bcast_id;
 else {
  /* Find idx into station table for destination station */
  sta_id = il_sta_id_or_broadcast(il, sta);

  if (sta_id == IL_INVALID_STATION) {
   D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1);
   goto drop_unlock;
  }
 }

 D_TX("station Id %d\n", sta_id);

 if (sta)
  sta_priv = (void *)sta->drv_priv;

 if (sta_priv && sta_priv->asleep &&
     (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) {
  /*
 * This sends an asynchronous command to the device,
 * but we can rely on it being processed before the
 * next frame is processed -- and the next frame to
 * this station is the one that will consume this
 * counter.
 * For now set the counter to just 1 since we do not
 * support uAPSD yet.
 */

  il4965_sta_modify_sleep_tx_count(il, sta_id, 1);
 }

 /* FIXME: remove me ? */
 WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);

 /* Access category (AC) is also the queue number */
 txq_id = skb_get_queue_mapping(skb);

 /* irqs already disabled/saved above when locking il->lock */
 spin_lock(&il->sta_lock);

 if (ieee80211_is_data_qos(fc)) {
  qc = ieee80211_get_qos_ctl(hdr);
  tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
  if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) {
   spin_unlock(&il->sta_lock);
   goto drop_unlock;
  }
  seq_number = il->stations[sta_id].tid[tid].seq_number;
  seq_number &= IEEE80211_SCTL_SEQ;
  hdr->seq_ctrl =
      hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG);
  hdr->seq_ctrl |= cpu_to_le16(seq_number);
  seq_number += 0x10;
  /* aggregation is on for this <sta,tid> */
  if (info->flags & IEEE80211_TX_CTL_AMPDU &&
      il->stations[sta_id].tid[tid].agg.state == IL_AGG_ON) {
   txq_id = il->stations[sta_id].tid[tid].agg.txq_id;
   is_agg = true;
  }
 }

 txq = &il->txq[txq_id];
 q = &txq->q;

 if (unlikely(il_queue_space(q) < q->high_mark)) {
  spin_unlock(&il->sta_lock);
  goto drop_unlock;
 }

 if (ieee80211_is_data_qos(fc)) {
  il->stations[sta_id].tid[tid].tfds_in_queue++;
  if (!ieee80211_has_morefrags(fc))
   il->stations[sta_id].tid[tid].seq_number = seq_number;
 }

 spin_unlock(&il->sta_lock);

 txq->skbs[q->write_ptr] = skb;

 /* Set up first empty entry in queue's array of Tx/cmd buffers */
 out_cmd = txq->cmd[q->write_ptr];
 out_meta = &txq->meta[q->write_ptr];
 tx_cmd = container_of(&out_cmd->cmd.tx, struct il_tx_cmd, __hdr);
 memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
 memset(tx_cmd, 0, sizeof(struct il_tx_cmd));

 /*
 * Set up the Tx-command (not MAC!) header.
 * Store the chosen Tx queue and TFD idx within the sequence field;
 * after Tx, uCode's Tx response will return this value so driver can
 * locate the frame within the tx queue and do post-tx processing.
 */

 out_cmd->hdr.cmd = C_TX;
 out_cmd->hdr.sequence =
     cpu_to_le16((u16)
   (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr)));

 /* Copy MAC header from skb into command buffer */
 memcpy(tx_cmd->hdr, hdr, hdr_len);

 /* Total # bytes to be transmitted */
 tx_cmd->len = cpu_to_le16((u16) skb->len);

 if (info->control.hw_key)
  il4965_tx_cmd_build_hwcrypto(il, info, tx_cmd, skb, sta_id);

 /* TODO need this for burst mode later on */
 il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id);

 il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc);

 /*
 * Use the first empty entry in this queue's command buffer array
 * to contain the Tx command and MAC header concatenated together
 * (payload data will be in another buffer).
 * Size of this varies, due to varying MAC header length.
 * If end is not dword aligned, we'll have 2 extra bytes at the end
 * of the MAC header (device reads on dword boundaries).
 * We'll tell device about this padding later.
 */

 len = sizeof(struct il_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len;
 firstlen = (len + 3) & ~3;

 /* Tell NIC about any 2-byte padding after MAC header */
 if (firstlen != len)
  tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;

 /* Physical address of this Tx command's header (not MAC header!),
 * within command buffer array. */

 txcmd_phys = dma_map_single(&il->pci_dev->dev, &out_cmd->hdr, firstlen,
        DMA_BIDIRECTIONAL);
 if (unlikely(dma_mapping_error(&il->pci_dev->dev, txcmd_phys)))
  goto drop_unlock;

 /* Set up TFD's 2nd entry to point directly to remainder of skb,
 * if any (802.11 null frames have no payload). */

 secondlen = skb->len - hdr_len;
 if (secondlen > 0) {
  phys_addr = dma_map_single(&il->pci_dev->dev, skb->data + hdr_len,
        secondlen, DMA_TO_DEVICE);
  if (unlikely(dma_mapping_error(&il->pci_dev->dev, phys_addr)))
   goto drop_unlock;
 }

 /* Add buffer containing Tx command and MAC(!) header to TFD's
 * first entry */

 il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0);
 dma_unmap_addr_set(out_meta, mapping, txcmd_phys);
 dma_unmap_len_set(out_meta, len, firstlen);
 if (secondlen)
  il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen,
            0, 0);

 if (!ieee80211_has_morefrags(hdr->frame_control)) {
  txq->need_update = 1;
 } else {
  wait_write_ptr = 1;
  txq->need_update = 0;
 }

 scratch_phys =
     txcmd_phys + sizeof(struct il_cmd_header) +
     offsetof(struct il_tx_cmd, scratch);

 /* take back ownership of DMA buffer to enable update */
 dma_sync_single_for_cpu(&il->pci_dev->dev, txcmd_phys, firstlen,
    DMA_BIDIRECTIONAL);
 tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
 tx_cmd->dram_msb_ptr = il_get_dma_hi_addr(scratch_phys);

 il_update_stats(il, true, fc, skb->len);

 D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence));
 D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags));
 il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd, sizeof(*tx_cmd));
 il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, hdr_len);

 /* Set up entry for this TFD in Tx byte-count array */
 if (info->flags & IEEE80211_TX_CTL_AMPDU)
  il->ops->txq_update_byte_cnt_tbl(il, txq, le16_to_cpu(tx_cmd->len));

 dma_sync_single_for_device(&il->pci_dev->dev, txcmd_phys, firstlen,
       DMA_BIDIRECTIONAL);

 /* Tell device the write idx *just past* this latest filled TFD */
 q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd);
 il_txq_update_write_ptr(il, txq);
 spin_unlock_irqrestore(&il->lock, flags);

 /*
 * At this point the frame is "transmitted" successfully
 * and we will get a TX status notification eventually,
 * regardless of the value of ret. "ret" only indicates
 * whether or not we should update the write pointer.
 */


 /*
 * Avoid atomic ops if it isn't an associated client.
 * Also, if this is a packet for aggregation, don't
 * increase the counter because the ucode will stop
 * aggregation queues when their respective station
 * goes to sleep.
 */

 if (sta_priv && sta_priv->client && !is_agg)
  atomic_inc(&sta_priv->pending_frames);

 if (il_queue_space(q) < q->high_mark && il->mac80211_registered) {
  if (wait_write_ptr) {
   spin_lock_irqsave(&il->lock, flags);
   txq->need_update = 1;
   il_txq_update_write_ptr(il, txq);
   spin_unlock_irqrestore(&il->lock, flags);
  } else {
   il_stop_queue(il, txq);
  }
 }

 return 0;

drop_unlock:
 spin_unlock_irqrestore(&il->lock, flags);
 return -1;
}

static inline int
il4965_alloc_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr, size_t size)
{
 ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma,
           GFP_KERNEL);
 if (!ptr->addr)
  return -ENOMEM;
 ptr->size = size;
 return 0;
}

static inline void
il4965_free_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr)
{
 if (unlikely(!ptr->addr))
  return;

 dma_free_coherent(&il->pci_dev->dev, ptr->size, ptr->addr, ptr->dma);
 memset(ptr, 0, sizeof(*ptr));
}

/*
 * il4965_hw_txq_ctx_free - Free TXQ Context
 *
 * Destroy all TX DMA queues and structures
 */

void
il4965_hw_txq_ctx_free(struct il_priv *il)
{
 int txq_id;

 /* Tx queues */
 if (il->txq) {
  for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
   if (txq_id == il->cmd_queue)
    il_cmd_queue_free(il);
   else
    il_tx_queue_free(il, txq_id);
 }
 il4965_free_dma_ptr(il, &il->kw);

 il4965_free_dma_ptr(il, &il->scd_bc_tbls);

 /* free tx queue structure */
 il_free_txq_mem(il);
}

/*
 * il4965_txq_ctx_alloc - allocate TX queue context
 * Allocate all Tx DMA structures and initialize them
 */

int
il4965_txq_ctx_alloc(struct il_priv *il)
{
 int ret, txq_id;
 unsigned long flags;

 /* Free all tx/cmd queues and keep-warm buffer */
 il4965_hw_txq_ctx_free(il);

 ret =
     il4965_alloc_dma_ptr(il, &il->scd_bc_tbls,
     il->hw_params.scd_bc_tbls_size);
 if (ret) {
  IL_ERR("Scheduler BC Table allocation failed\n");
  goto error_bc_tbls;
 }
 /* Alloc keep-warm buffer */
 ret = il4965_alloc_dma_ptr(il, &il->kw, IL_KW_SIZE);
 if (ret) {
  IL_ERR("Keep Warm allocation failed\n");
  goto error_kw;
 }

 /* allocate tx queue structure */
 ret = il_alloc_txq_mem(il);
 if (ret)
  goto error;

 spin_lock_irqsave(&il->lock, flags);

 /* Turn off all Tx DMA fifos */
 il4965_txq_set_sched(il, 0);

 /* Tell NIC where to find the "keep warm" buffer */
 il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4);

 spin_unlock_irqrestore(&il->lock, flags);

 /* Alloc and init all Tx queues, including the command queue (#4/#9) */
 for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) {
  ret = il_tx_queue_init(il, txq_id);
  if (ret) {
   IL_ERR("Tx %d queue init failed\n", txq_id);
   goto error;
  }
 }

 return ret;

error:
 il4965_hw_txq_ctx_free(il);
 il4965_free_dma_ptr(il, &il->kw);
error_kw:
 il4965_free_dma_ptr(il, &il->scd_bc_tbls);
error_bc_tbls:
 return ret;
}

void
il4965_txq_ctx_reset(struct il_priv *il)
{
 int txq_id;
 unsigned long flags;

 spin_lock_irqsave(&il->lock, flags);

 /* Turn off all Tx DMA fifos */
 il4965_txq_set_sched(il, 0);
 /* Tell NIC where to find the "keep warm" buffer */
 il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4);

 spin_unlock_irqrestore(&il->lock, flags);

 /* Alloc and init all Tx queues, including the command queue (#4) */
 for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
  il_tx_queue_reset(il, txq_id);
}

static void
il4965_txq_ctx_unmap(struct il_priv *il)
{
 int txq_id;

 if (!il->txq)
  return;

 /* Unmap DMA from host system and free skb's */
 for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
  if (txq_id == il->cmd_queue)
   il_cmd_queue_unmap(il);
  else
   il_tx_queue_unmap(il, txq_id);
}

/*
 * il4965_txq_ctx_stop - Stop all Tx DMA channels
 */

void
il4965_txq_ctx_stop(struct il_priv *il)
{
 int ch, ret;

 _il_wr_prph(il, IL49_SCD_TXFACT, 0);

 /* Stop each Tx DMA channel, and wait for it to be idle */
 for (ch = 0; ch < il->hw_params.dma_chnl_num; ch++) {
  _il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
  ret =
      _il_poll_bit(il, FH49_TSSR_TX_STATUS_REG,
     FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
     FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
     1000);
  if (ret < 0)
   IL_ERR("Timeout stopping DMA channel %d [0x%08x]",
          ch, _il_rd(il, FH49_TSSR_TX_STATUS_REG));
 }
}

/*
 * Find first available (lowest unused) Tx Queue, mark it "active".
 * Called only when finding queue for aggregation.
 * Should never return anything < 7, because they should already
 * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
 */

static int
il4965_txq_ctx_activate_free(struct il_priv *il)
{
 int txq_id;

 for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
  if (!test_and_set_bit(txq_id, &il->txq_ctx_active_msk))
   return txq_id;
 return -1;
}

/*
 * il4965_tx_queue_stop_scheduler - Stop queue, but keep configuration
 */

static void
il4965_tx_queue_stop_scheduler(struct il_priv *il, u16 txq_id)
{
 /* Simply stop the queue, but don't change any configuration;
 * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */

 il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id),
     (0 << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
     (1 << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
}

/*
 * il4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue
 */

static int
il4965_tx_queue_set_q2ratid(struct il_priv *il, u16 ra_tid, u16 txq_id)
{
 u32 tbl_dw_addr;
 u32 tbl_dw;
 u16 scd_q2ratid;

 scd_q2ratid = ra_tid & IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK;

 tbl_dw_addr =
     il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);

 tbl_dw = il_read_targ_mem(il, tbl_dw_addr);

 if (txq_id & 0x1)
  tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
 else
  tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);

 il_write_targ_mem(il, tbl_dw_addr, tbl_dw);

 return 0;
}

/*
 * il4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue
 *
 * NOTE:  txq_id must be greater than IL49_FIRST_AMPDU_QUEUE,
 *        i.e. it must be one of the higher queues used for aggregation
 */

static int
il4965_txq_agg_enable(struct il_priv *il, int txq_id, int tx_fifo, int sta_id,
        int tid, u16 ssn_idx)
{
 unsigned long flags;
 u16 ra_tid;
 int ret;

 if ((IL49_FIRST_AMPDU_QUEUE > txq_id) ||
     (IL49_FIRST_AMPDU_QUEUE +
      il->cfg->num_of_ampdu_queues <= txq_id)) {
  IL_WARN("queue number out of range: %d, must be %d to %d\n",
   txq_id, IL49_FIRST_AMPDU_QUEUE,
   IL49_FIRST_AMPDU_QUEUE +
   il->cfg->num_of_ampdu_queues - 1);
  return -EINVAL;
 }

 ra_tid = BUILD_RAxTID(sta_id, tid);

 /* Modify device's station table to Tx this TID */
 ret = il4965_sta_tx_modify_enable_tid(il, sta_id, tid);
 if (ret)
  return ret;

 spin_lock_irqsave(&il->lock, flags);

 /* Stop this Tx queue before configuring it */
 il4965_tx_queue_stop_scheduler(il, txq_id);

 /* Map receiver-address / traffic-ID to this queue */
 il4965_tx_queue_set_q2ratid(il, ra_tid, txq_id);

 /* Set this queue as a chain-building queue */
 il_set_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));

 /* Place first TFD at idx corresponding to start sequence number.
 * Assumes that ssn_idx is valid (!= 0xFFF) */

 il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
 il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
 il4965_set_wr_ptrs(il, txq_id, ssn_idx);

 /* Set up Tx win size and frame limit for this queue */
 il_write_targ_mem(il,
     il->scd_base_addr +
     IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id),
     (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS)
     & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);

 il_write_targ_mem(il,
     il->scd_base_addr +
     IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
     (SCD_FRAME_LIMIT <<
      IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
     IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);

 il_set_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id));

 /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
 il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 1);

 spin_unlock_irqrestore(&il->lock, flags);

 return 0;
}

int
il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif,
      struct ieee80211_sta *sta, u16 tid, u16 * ssn)
{
 int sta_id;
 int tx_fifo;
 int txq_id;
 int ret;
 unsigned long flags;
 struct il_tid_data *tid_data;

 /* FIXME: warning if tx fifo not found ? */
 tx_fifo = il4965_get_fifo_from_tid(tid);
 if (unlikely(tx_fifo < 0))
  return tx_fifo;

 D_HT("%s on ra = %pM tid = %d\n", __func__, sta->addr, tid);

 sta_id = il_sta_id(sta);
 if (sta_id == IL_INVALID_STATION) {
  IL_ERR("Start AGG on invalid station\n");
  return -ENXIO;
 }
 if (unlikely(tid >= MAX_TID_COUNT))
  return -EINVAL;

 if (il->stations[sta_id].tid[tid].agg.state != IL_AGG_OFF) {
  IL_ERR("Start AGG when state is not IL_AGG_OFF !\n");
  return -ENXIO;
 }

 txq_id = il4965_txq_ctx_activate_free(il);
 if (txq_id == -1) {
  IL_ERR("No free aggregation queue available\n");
  return -ENXIO;
 }

 spin_lock_irqsave(&il->sta_lock, flags);
 tid_data = &il->stations[sta_id].tid[tid];
 *ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
 tid_data->agg.txq_id = txq_id;
 il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id);
 spin_unlock_irqrestore(&il->sta_lock, flags);

 ret = il4965_txq_agg_enable(il, txq_id, tx_fifo, sta_id, tid, *ssn);
 if (ret)
  return ret;

 spin_lock_irqsave(&il->sta_lock, flags);
 tid_data = &il->stations[sta_id].tid[tid];
 if (tid_data->tfds_in_queue == 0) {
  D_HT("HW queue is empty\n");
  tid_data->agg.state = IL_AGG_ON;
  ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
 } else {
  D_HT("HW queue is NOT empty: %d packets in HW queue\n",
       tid_data->tfds_in_queue);
  tid_data->agg.state = IL_EMPTYING_HW_QUEUE_ADDBA;
 }
 spin_unlock_irqrestore(&il->sta_lock, flags);
 return ret;
}

/*
 * txq_id must be greater than IL49_FIRST_AMPDU_QUEUE
 * il->lock must be held by the caller
 */

static int
il4965_txq_agg_disable(struct il_priv *il, u16 txq_id, u16 ssn_idx, u8 tx_fifo)
{
 if ((IL49_FIRST_AMPDU_QUEUE > txq_id) ||
     (IL49_FIRST_AMPDU_QUEUE +
      il->cfg->num_of_ampdu_queues <= txq_id)) {
  IL_WARN("queue number out of range: %d, must be %d to %d\n",
   txq_id, IL49_FIRST_AMPDU_QUEUE,
   IL49_FIRST_AMPDU_QUEUE +
   il->cfg->num_of_ampdu_queues - 1);
  return -EINVAL;
 }

 il4965_tx_queue_stop_scheduler(il, txq_id);

 il_clear_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));

 il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
 il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
 /* supposes that ssn_idx is valid (!= 0xFFF) */
 il4965_set_wr_ptrs(il, txq_id, ssn_idx);

 il_clear_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id));
 il_txq_ctx_deactivate(il, txq_id);
 il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 0);

 return 0;
}

int
il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif,
     struct ieee80211_sta *sta, u16 tid)
{
 int tx_fifo_id, txq_id, sta_id, ssn;
 struct il_tid_data *tid_data;
 int write_ptr, read_ptr;
 unsigned long flags;

 /* FIXME: warning if tx_fifo_id not found ? */
 tx_fifo_id = il4965_get_fifo_from_tid(tid);
 if (unlikely(tx_fifo_id < 0))
  return tx_fifo_id;

 sta_id = il_sta_id(sta);

 if (sta_id == IL_INVALID_STATION) {
  IL_ERR("Invalid station for AGG tid %d\n", tid);
  return -ENXIO;
 }

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=89 G=92

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge