Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/intel/iwlegacy/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 142 kB image not shown  

Quelle  common.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
 *
 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
 *
 * Contact Information:
 *  Intel Linux Wireless <ilw@linux.intel.com>
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 *****************************************************************************/


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/etherdevice.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/lockdep.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <net/mac80211.h>

#include "common.h"

int
_il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout)
{
 const int interval = 10; /* microseconds */
 int t = 0;

 do {
  if ((_il_rd(il, addr) & mask) == (bits & mask))
   return t;
  udelay(interval);
  t += interval;
 } while (t < timeout);

 return -ETIMEDOUT;
}
EXPORT_SYMBOL(_il_poll_bit);

void
il_set_bit(struct il_priv *p, u32 r, u32 m)
{
 unsigned long reg_flags;

 spin_lock_irqsave(&p->reg_lock, reg_flags);
 _il_set_bit(p, r, m);
 spin_unlock_irqrestore(&p->reg_lock, reg_flags);
}
EXPORT_SYMBOL(il_set_bit);

void
il_clear_bit(struct il_priv *p, u32 r, u32 m)
{
 unsigned long reg_flags;

 spin_lock_irqsave(&p->reg_lock, reg_flags);
 _il_clear_bit(p, r, m);
 spin_unlock_irqrestore(&p->reg_lock, reg_flags);
}
EXPORT_SYMBOL(il_clear_bit);

bool
_il_grab_nic_access(struct il_priv *il)
{
 int ret;
 u32 val;

 /* this bit wakes up the NIC */
 _il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);

 /*
 * These bits say the device is running, and should keep running for
 * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
 * but they do not indicate that embedded SRAM is restored yet;
 * 3945 and 4965 have volatile SRAM, and must save/restore contents
 * to/from host DRAM when sleeping/waking for power-saving.
 * Each direction takes approximately 1/4 millisecond; with this
 * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
 * series of register accesses are expected (e.g. reading Event Log),
 * to keep device from sleeping.
 *
 * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
 * SRAM is okay/restored.  We don't check that here because this call
 * is just for hardware register access; but GP1 MAC_SLEEP check is a
 * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
 *
 */

 ret =
     _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
    (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
     CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
 if (unlikely(ret < 0)) {
  val = _il_rd(il, CSR_GP_CNTRL);
  WARN_ONCE(1, "Timeout waiting for ucode processor access "
        "(CSR_GP_CNTRL 0x%08x)\n", val);
  _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
  return false;
 }

 return true;
}
EXPORT_SYMBOL_GPL(_il_grab_nic_access);

int
il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout)
{
 const int interval = 10; /* microseconds */
 int t = 0;

 do {
  if ((il_rd(il, addr) & mask) == mask)
   return t;
  udelay(interval);
  t += interval;
 } while (t < timeout);

 return -ETIMEDOUT;
}
EXPORT_SYMBOL(il_poll_bit);

u32
il_rd_prph(struct il_priv *il, u32 reg)
{
 unsigned long reg_flags;
 u32 val;

 spin_lock_irqsave(&il->reg_lock, reg_flags);
 _il_grab_nic_access(il);
 val = _il_rd_prph(il, reg);
 _il_release_nic_access(il);
 spin_unlock_irqrestore(&il->reg_lock, reg_flags);
 return val;
}
EXPORT_SYMBOL(il_rd_prph);

void
il_wr_prph(struct il_priv *il, u32 addr, u32 val)
{
 unsigned long reg_flags;

 spin_lock_irqsave(&il->reg_lock, reg_flags);
 if (likely(_il_grab_nic_access(il))) {
  _il_wr_prph(il, addr, val);
  _il_release_nic_access(il);
 }
 spin_unlock_irqrestore(&il->reg_lock, reg_flags);
}
EXPORT_SYMBOL(il_wr_prph);

u32
il_read_targ_mem(struct il_priv *il, u32 addr)
{
 unsigned long reg_flags;
 u32 value;

 spin_lock_irqsave(&il->reg_lock, reg_flags);
 _il_grab_nic_access(il);

 _il_wr(il, HBUS_TARG_MEM_RADDR, addr);
 value = _il_rd(il, HBUS_TARG_MEM_RDAT);

 _il_release_nic_access(il);
 spin_unlock_irqrestore(&il->reg_lock, reg_flags);
 return value;
}
EXPORT_SYMBOL(il_read_targ_mem);

void
il_write_targ_mem(struct il_priv *il, u32 addr, u32 val)
{
 unsigned long reg_flags;

 spin_lock_irqsave(&il->reg_lock, reg_flags);
 if (likely(_il_grab_nic_access(il))) {
  _il_wr(il, HBUS_TARG_MEM_WADDR, addr);
  _il_wr(il, HBUS_TARG_MEM_WDAT, val);
  _il_release_nic_access(il);
 }
 spin_unlock_irqrestore(&il->reg_lock, reg_flags);
}
EXPORT_SYMBOL(il_write_targ_mem);

const char *
il_get_cmd_string(u8 cmd)
{
 switch (cmd) {
  IL_CMD(N_ALIVE);
  IL_CMD(N_ERROR);
  IL_CMD(C_RXON);
  IL_CMD(C_RXON_ASSOC);
  IL_CMD(C_QOS_PARAM);
  IL_CMD(C_RXON_TIMING);
  IL_CMD(C_ADD_STA);
  IL_CMD(C_REM_STA);
  IL_CMD(C_WEPKEY);
  IL_CMD(N_3945_RX);
  IL_CMD(C_TX);
  IL_CMD(C_RATE_SCALE);
  IL_CMD(C_LEDS);
  IL_CMD(C_TX_LINK_QUALITY_CMD);
  IL_CMD(C_CHANNEL_SWITCH);
  IL_CMD(N_CHANNEL_SWITCH);
  IL_CMD(C_SPECTRUM_MEASUREMENT);
  IL_CMD(N_SPECTRUM_MEASUREMENT);
  IL_CMD(C_POWER_TBL);
  IL_CMD(N_PM_SLEEP);
  IL_CMD(N_PM_DEBUG_STATS);
  IL_CMD(C_SCAN);
  IL_CMD(C_SCAN_ABORT);
  IL_CMD(N_SCAN_START);
  IL_CMD(N_SCAN_RESULTS);
  IL_CMD(N_SCAN_COMPLETE);
  IL_CMD(N_BEACON);
  IL_CMD(C_TX_BEACON);
  IL_CMD(C_TX_PWR_TBL);
  IL_CMD(C_BT_CONFIG);
  IL_CMD(C_STATS);
  IL_CMD(N_STATS);
  IL_CMD(N_CARD_STATE);
  IL_CMD(N_MISSED_BEACONS);
  IL_CMD(C_CT_KILL_CONFIG);
  IL_CMD(C_SENSITIVITY);
  IL_CMD(C_PHY_CALIBRATION);
  IL_CMD(N_RX_PHY);
  IL_CMD(N_RX_MPDU);
  IL_CMD(N_RX);
  IL_CMD(N_COMPRESSED_BA);
 default:
  return "UNKNOWN";

 }
}
EXPORT_SYMBOL(il_get_cmd_string);

#define HOST_COMPLETE_TIMEOUT (HZ / 2)

static void
il_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd,
   struct il_rx_pkt *pkt)
{
 if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
  IL_ERR("Bad return from %s (0x%08X)\n",
         il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
  return;
 }
#ifdef CONFIG_IWLEGACY_DEBUG
 switch (cmd->hdr.cmd) {
 case C_TX_LINK_QUALITY_CMD:
 case C_SENSITIVITY:
  D_HC_DUMP("back from %s (0x%08X)\n",
     il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
  break;
 default:
  D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd),
       pkt->hdr.flags);
 }
#endif
}

static int
il_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd)
{
 int ret;

 BUG_ON(!(cmd->flags & CMD_ASYNC));

 /* An asynchronous command can not expect an SKB to be set. */
 BUG_ON(cmd->flags & CMD_WANT_SKB);

 /* Assign a generic callback if one is not provided */
 if (!cmd->callback)
  cmd->callback = il_generic_cmd_callback;

 if (test_bit(S_EXIT_PENDING, &il->status))
  return -EBUSY;

 ret = il_enqueue_hcmd(il, cmd);
 if (ret < 0) {
  IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
         il_get_cmd_string(cmd->id), ret);
  return ret;
 }
 return 0;
}

int
il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd)
{
 int cmd_idx;
 int ret;

 lockdep_assert_held(&il->mutex);

 BUG_ON(cmd->flags & CMD_ASYNC);

 /* A synchronous command can not have a callback set. */
 BUG_ON(cmd->callback);

 D_INFO("Attempting to send sync command %s\n",
        il_get_cmd_string(cmd->id));

 set_bit(S_HCMD_ACTIVE, &il->status);
 D_INFO("Setting HCMD_ACTIVE for command %s\n",
        il_get_cmd_string(cmd->id));

 cmd_idx = il_enqueue_hcmd(il, cmd);
 if (cmd_idx < 0) {
  ret = cmd_idx;
  IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
         il_get_cmd_string(cmd->id), ret);
  goto out;
 }

 ret = wait_event_timeout(il->wait_command_queue,
     !test_bit(S_HCMD_ACTIVE, &il->status),
     HOST_COMPLETE_TIMEOUT);
 if (!ret) {
  if (test_bit(S_HCMD_ACTIVE, &il->status)) {
   IL_ERR("Error sending %s: time out after %dms.\n",
          il_get_cmd_string(cmd->id),
          jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));

   clear_bit(S_HCMD_ACTIVE, &il->status);
   D_INFO("Clearing HCMD_ACTIVE for command %s\n",
          il_get_cmd_string(cmd->id));
   ret = -ETIMEDOUT;
   goto cancel;
  }
 }

 if (test_bit(S_RFKILL, &il->status)) {
  IL_ERR("Command %s aborted: RF KILL Switch\n",
         il_get_cmd_string(cmd->id));
  ret = -ECANCELED;
  goto fail;
 }
 if (test_bit(S_FW_ERROR, &il->status)) {
  IL_ERR("Command %s failed: FW Error\n",
         il_get_cmd_string(cmd->id));
  ret = -EIO;
  goto fail;
 }
 if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
  IL_ERR("Error: Response NULL in '%s'\n",
         il_get_cmd_string(cmd->id));
  ret = -EIO;
  goto cancel;
 }

 ret = 0;
 goto out;

cancel:
 if (cmd->flags & CMD_WANT_SKB) {
  /*
 * Cancel the CMD_WANT_SKB flag for the cmd in the
 * TX cmd queue. Otherwise in case the cmd comes
 * in later, it will possibly set an invalid
 * address (cmd->meta.source).
 */

  il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB;
 }
fail:
 if (cmd->reply_page) {
  il_free_pages(il, cmd->reply_page);
  cmd->reply_page = 0;
 }
out:
 return ret;
}
EXPORT_SYMBOL(il_send_cmd_sync);

int
il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd)
{
 if (cmd->flags & CMD_ASYNC)
  return il_send_cmd_async(il, cmd);

 return il_send_cmd_sync(il, cmd);
}
EXPORT_SYMBOL(il_send_cmd);

int
il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data)
{
 struct il_host_cmd cmd = {
  .id = id,
  .len = len,
  .data = data,
 };

 return il_send_cmd_sync(il, &cmd);
}
EXPORT_SYMBOL(il_send_cmd_pdu);

int
il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data,
        void (*callback) (struct il_priv *il,
     struct il_device_cmd *cmd,
     struct il_rx_pkt *pkt))
{
 struct il_host_cmd cmd = {
  .id = id,
  .len = len,
  .data = data,
 };

 cmd.flags |= CMD_ASYNC;
 cmd.callback = callback;

 return il_send_cmd_async(il, &cmd);
}
EXPORT_SYMBOL(il_send_cmd_pdu_async);

/* default: IL_LED_BLINK(0) using blinking idx table */
static int led_mode;
module_param(led_mode, int, 0444);
MODULE_PARM_DESC(led_mode,
   "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking");

/* Throughput OFF time(ms) ON time (ms)
 * >300 25 25
 * >200 to 300 40 40
 * >100 to 200 55 55
 * >70 to 100 65 65
 * >50 to 70 75 75
 * >20 to 50 85 85
 * >10 to 20 95 95
 * >5 to 10 110 110
 * >1 to 5 130 130
 * >0 to 1 167 167
 * <=0 SOLID ON
 */

static const struct ieee80211_tpt_blink il_blink[] = {
 {.throughput = 0,  .blink_time = 334},
 {.throughput = 1 * 1024 - 1, .blink_time = 260},
 {.throughput = 5 * 1024 - 1, .blink_time = 220},
 {.throughput = 10 * 1024 - 1, .blink_time = 190},
 {.throughput = 20 * 1024 - 1, .blink_time = 170},
 {.throughput = 50 * 1024 - 1, .blink_time = 150},
 {.throughput = 70 * 1024 - 1, .blink_time = 130},
 {.throughput = 100 * 1024 - 1, .blink_time = 110},
 {.throughput = 200 * 1024 - 1, .blink_time = 80},
 {.throughput = 300 * 1024 - 1, .blink_time = 50},
};

/*
 * Adjust led blink rate to compensate on a MAC Clock difference on every HW
 * Led blink rate analysis showed an average deviation of 0% on 3945,
 * 5% on 4965 HW.
 * Need to compensate on the led on/off time per HW according to the deviation
 * to achieve the desired led frequency
 * The calculation is: (100-averageDeviation)/100 * blinkTime
 * For code efficiency the calculation will be:
 *     compensation = (100 - averageDeviation) * 64 / 100
 *     NewBlinkTime = (compensation * BlinkTime) / 64
 */

static inline u8
il_blink_compensation(struct il_priv *il, u8 time, u16 compensation)
{
 if (!compensation) {
  IL_ERR("undefined blink compensation: "
         "use pre-defined blinking time\n");
  return time;
 }

 return (u8) ((time * compensation) >> 6);
}

/* Set led pattern command */
static int
il_led_cmd(struct il_priv *il, unsigned long on, unsigned long off)
{
 struct il_led_cmd led_cmd = {
  .id = IL_LED_LINK,
  .interval = IL_DEF_LED_INTRVL
 };
 int ret;

 if (!test_bit(S_READY, &il->status))
  return -EBUSY;

 if (il->blink_on == on && il->blink_off == off)
  return 0;

 if (off == 0) {
  /* led is SOLID_ON */
  on = IL_LED_SOLID;
 }

 D_LED("Led blink time compensation=%u\n",
       il->cfg->led_compensation);
 led_cmd.on =
     il_blink_compensation(il, on,
      il->cfg->led_compensation);
 led_cmd.off =
     il_blink_compensation(il, off,
      il->cfg->led_compensation);

 ret = il->ops->send_led_cmd(il, &led_cmd);
 if (!ret) {
  il->blink_on = on;
  il->blink_off = off;
 }
 return ret;
}

static void
il_led_brightness_set(struct led_classdev *led_cdev,
        enum led_brightness brightness)
{
 struct il_priv *il = container_of(led_cdev, struct il_priv, led);
 unsigned long on = 0;

 if (brightness > 0)
  on = IL_LED_SOLID;

 il_led_cmd(il, on, 0);
}

static int
il_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
   unsigned long *delay_off)
{
 struct il_priv *il = container_of(led_cdev, struct il_priv, led);

 return il_led_cmd(il, *delay_on, *delay_off);
}

void
il_leds_init(struct il_priv *il)
{
 int mode = led_mode;
 int ret;

 if (mode == IL_LED_DEFAULT)
  mode = il->cfg->led_mode;

 il->led.name =
     kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy));
 if (!il->led.name)
  return;

 il->led.brightness_set = il_led_brightness_set;
 il->led.blink_set = il_led_blink_set;
 il->led.max_brightness = 1;

 switch (mode) {
 case IL_LED_DEFAULT:
  WARN_ON(1);
  break;
 case IL_LED_BLINK:
  il->led.default_trigger =
      ieee80211_create_tpt_led_trigger(il->hw,
           IEEE80211_TPT_LEDTRIG_FL_CONNECTED,
           il_blink,
           ARRAY_SIZE(il_blink));
  break;
 case IL_LED_RF_STATE:
  il->led.default_trigger = ieee80211_get_radio_led_name(il->hw);
  break;
 }

 ret = led_classdev_register(&il->pci_dev->dev, &il->led);
 if (ret) {
  kfree(il->led.name);
  return;
 }

 il->led_registered = true;
}
EXPORT_SYMBOL(il_leds_init);

void
il_leds_exit(struct il_priv *il)
{
 if (!il->led_registered)
  return;

 led_classdev_unregister(&il->led);
 kfree(il->led.name);
}
EXPORT_SYMBOL(il_leds_exit);

/************************** EEPROM BANDS ****************************
 *
 * The il_eeprom_band definitions below provide the mapping from the
 * EEPROM contents to the specific channel number supported for each
 * band.
 *
 * For example, il_priv->eeprom.band_3_channels[4] from the band_3
 * definition below maps to physical channel 42 in the 5.2GHz spectrum.
 * The specific geography and calibration information for that channel
 * is contained in the eeprom map itself.
 *
 * During init, we copy the eeprom information and channel map
 * information into il->channel_info_24/52 and il->channel_map_24/52
 *
 * channel_map_24/52 provides the idx in the channel_info array for a
 * given channel.  We have to have two separate maps as there is channel
 * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and
 * band_2
 *
 * A value of 0xff stored in the channel_map indicates that the channel
 * is not supported by the hardware at all.
 *
 * A value of 0xfe in the channel_map indicates that the channel is not
 * valid for Tx with the current hardware.  This means that
 * while the system can tune and receive on a given channel, it may not
 * be able to associate or transmit any frames on that
 * channel.  There is no corresponding channel information for that
 * entry.
 *
 *********************************************************************/


/* 2.4 GHz */
const u8 il_eeprom_band_1[14] = {
 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
};

/* 5.2 GHz bands */
static const u8 il_eeprom_band_2[] = { /* 4915-5080MHz */
 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16
};

static const u8 il_eeprom_band_3[] = { /* 5170-5320MHz */
 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
};

static const u8 il_eeprom_band_4[] = { /* 5500-5700MHz */
 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
};

static const u8 il_eeprom_band_5[] = { /* 5725-5825MHz */
 145, 149, 153, 157, 161, 165
};

static const u8 il_eeprom_band_6[] = { /* 2.4 ht40 channel */
 1, 2, 3, 4, 5, 6, 7
};

static const u8 il_eeprom_band_7[] = { /* 5.2 ht40 channel */
 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157
};

/******************************************************************************
 *
 * EEPROM related functions
 *
******************************************************************************/


static int
il_eeprom_verify_signature(struct il_priv *il)
{
 u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
 int ret = 0;

 D_EEPROM("EEPROM signature=0x%08x\n", gp);
 switch (gp) {
 case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K:
 case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K:
  break;
 default:
  IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp);
  ret = -ENOENT;
  break;
 }
 return ret;
}

const u8 *
il_eeprom_query_addr(const struct il_priv *il, size_t offset)
{
 BUG_ON(offset >= il->cfg->eeprom_size);
 return &il->eeprom[offset];
}
EXPORT_SYMBOL(il_eeprom_query_addr);

u16
il_eeprom_query16(const struct il_priv *il, size_t offset)
{
 if (!il->eeprom)
  return 0;
 return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8);
}
EXPORT_SYMBOL(il_eeprom_query16);

/*
 * il_eeprom_init - read EEPROM contents
 *
 * Load the EEPROM contents from adapter into il->eeprom
 *
 * NOTE:  This routine uses the non-debug IO access functions.
 */

int
il_eeprom_init(struct il_priv *il)
{
 __le16 *e;
 u32 gp = _il_rd(il, CSR_EEPROM_GP);
 int sz;
 int ret;
 int addr;

 /* allocate eeprom */
 sz = il->cfg->eeprom_size;
 D_EEPROM("NVM size = %d\n", sz);
 il->eeprom = kzalloc(sz, GFP_KERNEL);
 if (!il->eeprom)
  return -ENOMEM;

 e = (__le16 *) il->eeprom;

 il->ops->apm_init(il);

 ret = il_eeprom_verify_signature(il);
 if (ret < 0) {
  IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp);
  ret = -ENOENT;
  goto err;
 }

 /* Make sure driver (instead of uCode) is allowed to read EEPROM */
 ret = il->ops->eeprom_acquire_semaphore(il);
 if (ret < 0) {
  IL_ERR("Failed to acquire EEPROM semaphore.\n");
  ret = -ENOENT;
  goto err;
 }

 /* eeprom is an array of 16bit values */
 for (addr = 0; addr < sz; addr += sizeof(u16)) {
  u32 r;

  _il_wr(il, CSR_EEPROM_REG,
         CSR_EEPROM_REG_MSK_ADDR & (addr << 1));

  ret =
      _il_poll_bit(il, CSR_EEPROM_REG,
     CSR_EEPROM_REG_READ_VALID_MSK,
     CSR_EEPROM_REG_READ_VALID_MSK,
     IL_EEPROM_ACCESS_TIMEOUT);
  if (ret < 0) {
   IL_ERR("Time out reading EEPROM[%d]\n", addr);
   goto done;
  }
  r = _il_rd(il, CSR_EEPROM_REG);
  e[addr / 2] = cpu_to_le16(r >> 16);
 }

 D_EEPROM("NVM Type: %s, version: 0x%x\n""EEPROM",
   il_eeprom_query16(il, EEPROM_VERSION));

 ret = 0;
done:
 il->ops->eeprom_release_semaphore(il);

err:
 if (ret)
  il_eeprom_free(il);
 /* Reset chip to save power until we load uCode during "up". */
 il_apm_stop(il);
 return ret;
}
EXPORT_SYMBOL(il_eeprom_init);

void
il_eeprom_free(struct il_priv *il)
{
 kfree(il->eeprom);
 il->eeprom = NULL;
}
EXPORT_SYMBOL(il_eeprom_free);

static void
il_init_band_reference(const struct il_priv *il, int eep_band,
         int *eeprom_ch_count,
         const struct il_eeprom_channel **eeprom_ch_info,
         const u8 **eeprom_ch_idx)
{
 u32 offset = il->cfg->regulatory_bands[eep_band - 1];

 switch (eep_band) {
 case 1:  /* 2.4GHz band */
  *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1);
  *eeprom_ch_info =
      (struct il_eeprom_channel *)il_eeprom_query_addr(il,
             offset);
  *eeprom_ch_idx = il_eeprom_band_1;
  break;
 case 2:  /* 4.9GHz band */
  *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2);
  *eeprom_ch_info =
      (struct il_eeprom_channel *)il_eeprom_query_addr(il,
             offset);
  *eeprom_ch_idx = il_eeprom_band_2;
  break;
 case 3:  /* 5.2GHz band */
  *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3);
  *eeprom_ch_info =
      (struct il_eeprom_channel *)il_eeprom_query_addr(il,
             offset);
  *eeprom_ch_idx = il_eeprom_band_3;
  break;
 case 4:  /* 5.5GHz band */
  *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4);
  *eeprom_ch_info =
      (struct il_eeprom_channel *)il_eeprom_query_addr(il,
             offset);
  *eeprom_ch_idx = il_eeprom_band_4;
  break;
 case 5:  /* 5.7GHz band */
  *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5);
  *eeprom_ch_info =
      (struct il_eeprom_channel *)il_eeprom_query_addr(il,
             offset);
  *eeprom_ch_idx = il_eeprom_band_5;
  break;
 case 6:  /* 2.4GHz ht40 channels */
  *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6);
  *eeprom_ch_info =
      (struct il_eeprom_channel *)il_eeprom_query_addr(il,
             offset);
  *eeprom_ch_idx = il_eeprom_band_6;
  break;
 case 7:  /* 5 GHz ht40 channels */
  *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7);
  *eeprom_ch_info =
      (struct il_eeprom_channel *)il_eeprom_query_addr(il,
             offset);
  *eeprom_ch_idx = il_eeprom_band_7;
  break;
 default:
  BUG();
 }
}

#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \
       ? " " : "")
/*
 * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il.
 *
 * Does not set up a command, or touch hardware.
 */

static int
il_mod_ht40_chan_info(struct il_priv *il, enum nl80211_band band, u16 channel,
        const struct il_eeprom_channel *eeprom_ch,
        u8 clear_ht40_extension_channel)
{
 struct il_channel_info *ch_info;

 ch_info =
     (struct il_channel_info *)il_get_channel_info(il, band, channel);

 if (!il_is_channel_valid(ch_info))
  return -1;

 D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):"
   " Ad-Hoc %ssupported\n", ch_info->channel,
   il_is_channel_a_band(ch_info) ? "5.2" : "2.4",
   CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE),
   CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE),
   CHECK_AND_PRINT(DFS), eeprom_ch->flags,
   eeprom_ch->max_power_avg,
   ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) &&
    !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not ");

 ch_info->ht40_eeprom = *eeprom_ch;
 ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg;
 ch_info->ht40_flags = eeprom_ch->flags;
 if (eeprom_ch->flags & EEPROM_CHANNEL_VALID)
  ch_info->ht40_extension_channel &=
      ~clear_ht40_extension_channel;

 return 0;
}

#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \
       ? " " : "")

/*
 * il_init_channel_map - Set up driver's info for all possible channels
 */

int
il_init_channel_map(struct il_priv *il)
{
 int eeprom_ch_count = 0;
 const u8 *eeprom_ch_idx = NULL;
 const struct il_eeprom_channel *eeprom_ch_info = NULL;
 int band, ch;
 struct il_channel_info *ch_info;

 if (il->channel_count) {
  D_EEPROM("Channel map already initialized.\n");
  return 0;
 }

 D_EEPROM("Initializing regulatory info from EEPROM\n");

 il->channel_count =
     ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) +
     ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) +
     ARRAY_SIZE(il_eeprom_band_5);

 D_EEPROM("Parsing data for %d channels.\n", il->channel_count);

 il->channel_info =
     kcalloc(il->channel_count, sizeof(struct il_channel_info),
      GFP_KERNEL);
 if (!il->channel_info) {
  IL_ERR("Could not allocate channel_info\n");
  il->channel_count = 0;
  return -ENOMEM;
 }

 ch_info = il->channel_info;

 /* Loop through the 5 EEPROM bands adding them in order to the
 * channel map we maintain (that contains additional information than
 * what just in the EEPROM) */

 for (band = 1; band <= 5; band++) {

  il_init_band_reference(il, band, &eeprom_ch_count,
           &eeprom_ch_info, &eeprom_ch_idx);

  /* Loop through each band adding each of the channels */
  for (ch = 0; ch < eeprom_ch_count; ch++) {
   ch_info->channel = eeprom_ch_idx[ch];
   ch_info->band =
       (band ==
        1) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;

   /* permanently store EEPROM's channel regulatory flags
 *   and max power in channel info database. */

   ch_info->eeprom = eeprom_ch_info[ch];

   /* Copy the run-time flags so they are there even on
 * invalid channels */

   ch_info->flags = eeprom_ch_info[ch].flags;
   /* First write that ht40 is not enabled, and then enable
 * one by one */

   ch_info->ht40_extension_channel =
       IEEE80211_CHAN_NO_HT40;

   if (!(il_is_channel_valid(ch_info))) {
    D_EEPROM("Ch. %d Flags %x [%sGHz] - "
      "No traffic\n", ch_info->channel,
      ch_info->flags,
      il_is_channel_a_band(ch_info) ? "5.2" :
      "2.4");
    ch_info++;
    continue;
   }

   /* Initialize regulatory-based run-time data */
   ch_info->max_power_avg = ch_info->curr_txpow =
       eeprom_ch_info[ch].max_power_avg;
   ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
   ch_info->min_power = 0;

   D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):"
     " Ad-Hoc %ssupported\n", ch_info->channel,
     il_is_channel_a_band(ch_info) ? "5.2" : "2.4",
     CHECK_AND_PRINT_I(VALID),
     CHECK_AND_PRINT_I(IBSS),
     CHECK_AND_PRINT_I(ACTIVE),
     CHECK_AND_PRINT_I(RADAR),
     CHECK_AND_PRINT_I(WIDE),
     CHECK_AND_PRINT_I(DFS),
     eeprom_ch_info[ch].flags,
     eeprom_ch_info[ch].max_power_avg,
     ((eeprom_ch_info[ch].
       flags & EEPROM_CHANNEL_IBSS) &&
      !(eeprom_ch_info[ch].
        flags & EEPROM_CHANNEL_RADAR)) ? "" :
     "not ");

   ch_info++;
  }
 }

 /* Check if we do have HT40 channels */
 if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 &&
     il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40)
  return 0;

 /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */
 for (band = 6; band <= 7; band++) {
  enum nl80211_band ieeeband;

  il_init_band_reference(il, band, &eeprom_ch_count,
           &eeprom_ch_info, &eeprom_ch_idx);

  /* EEPROM band 6 is 2.4, band 7 is 5 GHz */
  ieeeband =
      (band == 6) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;

  /* Loop through each band adding each of the channels */
  for (ch = 0; ch < eeprom_ch_count; ch++) {
   /* Set up driver's info for lower half */
   il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch],
           &eeprom_ch_info[ch],
           IEEE80211_CHAN_NO_HT40PLUS);

   /* Set up driver's info for upper half */
   il_mod_ht40_chan_info(il, ieeeband,
           eeprom_ch_idx[ch] + 4,
           &eeprom_ch_info[ch],
           IEEE80211_CHAN_NO_HT40MINUS);
  }
 }

 return 0;
}
EXPORT_SYMBOL(il_init_channel_map);

/*
 * il_free_channel_map - undo allocations in il_init_channel_map
 */

void
il_free_channel_map(struct il_priv *il)
{
 kfree(il->channel_info);
 il->channel_count = 0;
}
EXPORT_SYMBOL(il_free_channel_map);

/*
 * il_get_channel_info - Find driver's ilate channel info
 *
 * Based on band and channel number.
 */

const struct il_channel_info *
il_get_channel_info(const struct il_priv *il, enum nl80211_band band,
      u16 channel)
{
 int i;

 switch (band) {
 case NL80211_BAND_5GHZ:
  for (i = 14; i < il->channel_count; i++) {
   if (il->channel_info[i].channel == channel)
    return &il->channel_info[i];
  }
  break;
 case NL80211_BAND_2GHZ:
  if (channel >= 1 && channel <= 14)
   return &il->channel_info[channel - 1];
  break;
 default:
  BUG();
 }

 return NULL;
}
EXPORT_SYMBOL(il_get_channel_info);

/*
 * Setting power level allows the card to go to sleep when not busy.
 *
 * We calculate a sleep command based on the required latency, which
 * we get from mac80211.
 */


#define SLP_VEC(X0, X1, X2, X3, X4) { \
  cpu_to_le32(X0), \
  cpu_to_le32(X1), \
  cpu_to_le32(X2), \
  cpu_to_le32(X3), \
  cpu_to_le32(X4)  \
}

static void
il_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd)
{
 static const __le32 interval[3][IL_POWER_VEC_SIZE] = {
  SLP_VEC(2, 2, 4, 6, 0xFF),
  SLP_VEC(2, 4, 7, 10, 10),
  SLP_VEC(4, 7, 10, 10, 0xFF)
 };
 int i, dtim_period, no_dtim;
 u32 max_sleep;
 bool skip;

 memset(cmd, 0, sizeof(*cmd));

 if (il->power_data.pci_pm)
  cmd->flags |= IL_POWER_PCI_PM_MSK;

 /* if no Power Save, we are done */
 if (il->power_data.ps_disabled)
  return;

 cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK;
 cmd->keep_alive_seconds = 0;
 cmd->debug_flags = 0;
 cmd->rx_data_timeout = cpu_to_le32(25 * 1024);
 cmd->tx_data_timeout = cpu_to_le32(25 * 1024);
 cmd->keep_alive_beacons = 0;

 dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0;

 if (dtim_period <= 2) {
  memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0]));
  no_dtim = 2;
 } else if (dtim_period <= 10) {
  memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1]));
  no_dtim = 2;
 } else {
  memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2]));
  no_dtim = 0;
 }

 if (dtim_period == 0) {
  dtim_period = 1;
  skip = false;
 } else {
  skip = !!no_dtim;
 }

 if (skip) {
  __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1];

  max_sleep = le32_to_cpu(tmp);
  if (max_sleep == 0xFF)
   max_sleep = dtim_period * (skip + 1);
  else if (max_sleep >  dtim_period)
   max_sleep = (max_sleep / dtim_period) * dtim_period;
  cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK;
 } else {
  max_sleep = dtim_period;
  cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK;
 }

 for (i = 0; i < IL_POWER_VEC_SIZE; i++)
  if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
   cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
}

static int
il_set_power(struct il_priv *il, struct il_powertable_cmd *cmd)
{
 D_POWER("Sending power/sleep command\n");
 D_POWER("Flags value = 0x%08X\n", cmd->flags);
 D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
 D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
 D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n",
  le32_to_cpu(cmd->sleep_interval[0]),
  le32_to_cpu(cmd->sleep_interval[1]),
  le32_to_cpu(cmd->sleep_interval[2]),
  le32_to_cpu(cmd->sleep_interval[3]),
  le32_to_cpu(cmd->sleep_interval[4]));

 return il_send_cmd_pdu(il, C_POWER_TBL,
          sizeof(struct il_powertable_cmd), cmd);
}

static int
il_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force)
{
 int ret;
 bool update_chains;

 lockdep_assert_held(&il->mutex);

 /* Don't update the RX chain when chain noise calibration is running */
 update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE ||
     il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE;

 if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
  return 0;

 if (!il_is_ready_rf(il))
  return -EIO;

 /* scan complete use sleep_power_next, need to be updated */
 memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
 if (test_bit(S_SCANNING, &il->status) && !force) {
  D_INFO("Defer power set mode while scanning\n");
  return 0;
 }

 if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)
  set_bit(S_POWER_PMI, &il->status);

 ret = il_set_power(il, cmd);
 if (!ret) {
  if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK))
   clear_bit(S_POWER_PMI, &il->status);

  if (il->ops->update_chain_flags && update_chains)
   il->ops->update_chain_flags(il);
  else if (il->ops->update_chain_flags)
   D_POWER("Cannot update the power, chain noise "
    "calibration running: %d\n",
    il->chain_noise_data.state);

  memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd));
 } else
  IL_ERR("set power fail, ret = %d", ret);

 return ret;
}

int
il_power_update_mode(struct il_priv *il, bool force)
{
 struct il_powertable_cmd cmd;

 il_build_powertable_cmd(il, &cmd);

 return il_power_set_mode(il, &cmd, force);
}
EXPORT_SYMBOL(il_power_update_mode);

/* initialize to default */
void
il_power_initialize(struct il_priv *il)
{
 u16 lctl;

 pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
 il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);

 il->power_data.debug_sleep_level_override = -1;

 memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd));
}
EXPORT_SYMBOL(il_power_initialize);

/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
 * sending probe req.  This should be set long enough to hear probe responses
 * from more than one AP.  */

#define IL_ACTIVE_DWELL_TIME_24    (30) /* all times in msec */
#define IL_ACTIVE_DWELL_TIME_52    (20)

#define IL_ACTIVE_DWELL_FACTOR_24GHZ (3)
#define IL_ACTIVE_DWELL_FACTOR_52GHZ (2)

/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
 * Must be set longer than active dwell time.
 * For the most reliable scan, set > AP beacon interval (typically 100msec). */

#define IL_PASSIVE_DWELL_TIME_24   (20) /* all times in msec */
#define IL_PASSIVE_DWELL_TIME_52   (10)
#define IL_PASSIVE_DWELL_BASE      (100)
#define IL_CHANNEL_TUNE_TIME       5

static int
il_send_scan_abort(struct il_priv *il)
{
 int ret;
 struct il_rx_pkt *pkt;
 struct il_host_cmd cmd = {
  .id = C_SCAN_ABORT,
  .flags = CMD_WANT_SKB,
 };

 /* Exit instantly with error when device is not ready
 * to receive scan abort command or it does not perform
 * hardware scan currently */

 if (!test_bit(S_READY, &il->status) ||
     !test_bit(S_GEO_CONFIGURED, &il->status) ||
     !test_bit(S_SCAN_HW, &il->status) ||
     test_bit(S_FW_ERROR, &il->status) ||
     test_bit(S_EXIT_PENDING, &il->status))
  return -EIO;

 ret = il_send_cmd_sync(il, &cmd);
 if (ret)
  return ret;

 pkt = (struct il_rx_pkt *)cmd.reply_page;
 if (pkt->u.status != CAN_ABORT_STATUS) {
  /* The scan abort will return 1 for success or
 * 2 for "failure".  A failure condition can be
 * due to simply not being in an active scan which
 * can occur if we send the scan abort before we
 * the microcode has notified us that a scan is
 * completed. */

  D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status);
  ret = -EIO;
 }

 il_free_pages(il, cmd.reply_page);
 return ret;
}

static void
il_complete_scan(struct il_priv *il, bool aborted)
{
 struct cfg80211_scan_info info = {
  .aborted = aborted,
 };

 /* check if scan was requested from mac80211 */
 if (il->scan_request) {
  D_SCAN("Complete scan in mac80211\n");
  ieee80211_scan_completed(il->hw, &info);
 }

 il->scan_vif = NULL;
 il->scan_request = NULL;
}

void
il_force_scan_end(struct il_priv *il)
{
 lockdep_assert_held(&il->mutex);

 if (!test_bit(S_SCANNING, &il->status)) {
  D_SCAN("Forcing scan end while not scanning\n");
  return;
 }

 D_SCAN("Forcing scan end\n");
 clear_bit(S_SCANNING, &il->status);
 clear_bit(S_SCAN_HW, &il->status);
 clear_bit(S_SCAN_ABORTING, &il->status);
 il_complete_scan(il, true);
}

static void
il_do_scan_abort(struct il_priv *il)
{
 int ret;

 lockdep_assert_held(&il->mutex);

 if (!test_bit(S_SCANNING, &il->status)) {
  D_SCAN("Not performing scan to abort\n");
  return;
 }

 if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) {
  D_SCAN("Scan abort in progress\n");
  return;
 }

 ret = il_send_scan_abort(il);
 if (ret) {
  D_SCAN("Send scan abort failed %d\n", ret);
  il_force_scan_end(il);
 } else
  D_SCAN("Successfully send scan abort\n");
}

/*
 * il_scan_cancel - Cancel any currently executing HW scan
 */

int
il_scan_cancel(struct il_priv *il)
{
 D_SCAN("Queuing abort scan\n");
 queue_work(il->workqueue, &il->abort_scan);
 return 0;
}
EXPORT_SYMBOL(il_scan_cancel);

/*
 * il_scan_cancel_timeout - Cancel any currently executing HW scan
 * @ms: amount of time to wait (in milliseconds) for scan to abort
 *
 */

int
il_scan_cancel_timeout(struct il_priv *il, unsigned long ms)
{
 unsigned long timeout = jiffies + msecs_to_jiffies(ms);

 lockdep_assert_held(&il->mutex);

 D_SCAN("Scan cancel timeout\n");

 il_do_scan_abort(il);

 while (time_before_eq(jiffies, timeout)) {
  if (!test_bit(S_SCAN_HW, &il->status))
   break;
  msleep(20);
 }

 return test_bit(S_SCAN_HW, &il->status);
}
EXPORT_SYMBOL(il_scan_cancel_timeout);

/* Service response to C_SCAN (0x80) */
static void
il_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb)
{
#ifdef CONFIG_IWLEGACY_DEBUG
 struct il_rx_pkt *pkt = rxb_addr(rxb);
 struct il_scanreq_notification *notif =
     (struct il_scanreq_notification *)pkt->u.raw;

 D_SCAN("Scan request status = 0x%x\n", notif->status);
#endif
}

/* Service N_SCAN_START (0x82) */
static void
il_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb)
{
 struct il_rx_pkt *pkt = rxb_addr(rxb);
 struct il_scanstart_notification *notif =
     (struct il_scanstart_notification *)pkt->u.raw;
 il->scan_start_tsf = le32_to_cpu(notif->tsf_low);
 D_SCAN("Scan start: " "%d [802.11%s] "
        "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel,
        notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high),
        le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer);
}

/* Service N_SCAN_RESULTS (0x83) */
static void
il_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb)
{
#ifdef CONFIG_IWLEGACY_DEBUG
 struct il_rx_pkt *pkt = rxb_addr(rxb);
 struct il_scanresults_notification *notif =
     (struct il_scanresults_notification *)pkt->u.raw;

 D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d "
        "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a",
        le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low),
        le32_to_cpu(notif->stats[0]),
        le32_to_cpu(notif->tsf_low) - il->scan_start_tsf);
#endif
}

/* Service N_SCAN_COMPLETE (0x84) */
static void
il_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb)
{

 struct il_rx_pkt *pkt = rxb_addr(rxb);
 struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw;

 D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
        scan_notif->scanned_channels, scan_notif->tsf_low,
        scan_notif->tsf_high, scan_notif->status);

 /* The HW is no longer scanning */
 clear_bit(S_SCAN_HW, &il->status);

 D_SCAN("Scan on %sGHz took %dms\n",
        (il->scan_band == NL80211_BAND_2GHZ) ? "2.4" : "5.2",
        jiffies_to_msecs(jiffies - il->scan_start));

 queue_work(il->workqueue, &il->scan_completed);
}

void
il_setup_rx_scan_handlers(struct il_priv *il)
{
 /* scan handlers */
 il->handlers[C_SCAN] = il_hdl_scan;
 il->handlers[N_SCAN_START] = il_hdl_scan_start;
 il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results;
 il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete;
}
EXPORT_SYMBOL(il_setup_rx_scan_handlers);

u16
il_get_active_dwell_time(struct il_priv *il, enum nl80211_band band,
    u8 n_probes)
{
 if (band == NL80211_BAND_5GHZ)
  return IL_ACTIVE_DWELL_TIME_52 +
      IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1);
 else
  return IL_ACTIVE_DWELL_TIME_24 +
      IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1);
}
EXPORT_SYMBOL(il_get_active_dwell_time);

u16
il_get_passive_dwell_time(struct il_priv *il, enum nl80211_band band,
     struct ieee80211_vif *vif)
{
 u16 value;

 u16 passive =
     (band ==
      NL80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE +
     IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE +
     IL_PASSIVE_DWELL_TIME_52;

 if (il_is_any_associated(il)) {
  /*
 * If we're associated, we clamp the maximum passive
 * dwell time to be 98% of the smallest beacon interval
 * (minus 2 * channel tune time)
 */

  value = il->vif ? il->vif->bss_conf.beacon_int : 0;
  if (value > IL_PASSIVE_DWELL_BASE || !value)
   value = IL_PASSIVE_DWELL_BASE;
  value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2;
  passive = min(value, passive);
 }

 return passive;
}
EXPORT_SYMBOL(il_get_passive_dwell_time);

void
il_init_scan_params(struct il_priv *il)
{
 u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1;
 if (!il->scan_tx_ant[NL80211_BAND_5GHZ])
  il->scan_tx_ant[NL80211_BAND_5GHZ] = ant_idx;
 if (!il->scan_tx_ant[NL80211_BAND_2GHZ])
  il->scan_tx_ant[NL80211_BAND_2GHZ] = ant_idx;
}
EXPORT_SYMBOL(il_init_scan_params);

static int
il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
{
 int ret;

 lockdep_assert_held(&il->mutex);

 cancel_delayed_work(&il->scan_check);

 if (!il_is_ready_rf(il)) {
  IL_WARN("Request scan called when driver not ready.\n");
  return -EIO;
 }

 if (test_bit(S_SCAN_HW, &il->status)) {
  D_SCAN("Multiple concurrent scan requests in parallel.\n");
  return -EBUSY;
 }

 if (test_bit(S_SCAN_ABORTING, &il->status)) {
  D_SCAN("Scan request while abort pending.\n");
  return -EBUSY;
 }

 D_SCAN("Starting scan...\n");

 set_bit(S_SCANNING, &il->status);
 il->scan_start = jiffies;

 ret = il->ops->request_scan(il, vif);
 if (ret) {
  clear_bit(S_SCANNING, &il->status);
  return ret;
 }

 queue_delayed_work(il->workqueue, &il->scan_check,
      IL_SCAN_CHECK_WATCHDOG);

 return 0;
}

int
il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        struct ieee80211_scan_request *hw_req)
{
 struct cfg80211_scan_request *req = &hw_req->req;
 struct il_priv *il = hw->priv;
 int ret;

 if (req->n_channels == 0) {
  IL_ERR("Can not scan on no channels.\n");
  return -EINVAL;
 }

 mutex_lock(&il->mutex);
 D_MAC80211("enter\n");

 if (test_bit(S_SCANNING, &il->status)) {
  D_SCAN("Scan already in progress.\n");
  ret = -EAGAIN;
  goto out_unlock;
 }

 /* mac80211 will only ask for one band at a time */
 il->scan_request = req;
 il->scan_vif = vif;
 il->scan_band = req->channels[0]->band;

 ret = il_scan_initiate(il, vif);

out_unlock:
 D_MAC80211("leave ret %d\n", ret);
 mutex_unlock(&il->mutex);

 return ret;
}
EXPORT_SYMBOL(il_mac_hw_scan);

static void
il_bg_scan_check(struct work_struct *data)
{
 struct il_priv *il =
     container_of(data, struct il_priv, scan_check.work);

 D_SCAN("Scan check work\n");

 /* Since we are here firmware does not finish scan and
 * most likely is in bad shape, so we don't bother to
 * send abort command, just force scan complete to mac80211 */

 mutex_lock(&il->mutex);
 il_force_scan_end(il);
 mutex_unlock(&il->mutex);
}

/*
 * il_fill_probe_req - fill in all required fields and IE for probe request
 */

u16
il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
    const u8 *ta, const u8 *ies, int ie_len, int left)
{
 int len = 0;
 u8 *pos = NULL;

 /* Make sure there is enough space for the probe request,
 * two mandatory IEs and the data */

 left -= 24;
 if (left < 0)
  return 0;

 frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
 eth_broadcast_addr(frame->da);
 memcpy(frame->sa, ta, ETH_ALEN);
 eth_broadcast_addr(frame->bssid);
 frame->seq_ctrl = 0;

 len += 24;

 /* ...next IE... */
 pos = &frame->u.probe_req.variable[0];

 /* fill in our indirect SSID IE */
 left -= 2;
 if (left < 0)
  return 0;
 *pos++ = WLAN_EID_SSID;
 *pos++ = 0;

 len += 2;

 if (WARN_ON(left < ie_len))
  return len;

 if (ies && ie_len) {
  memcpy(pos, ies, ie_len);
  len += ie_len;
 }

 return (u16) len;
}
EXPORT_SYMBOL(il_fill_probe_req);

static void
il_bg_abort_scan(struct work_struct *work)
{
 struct il_priv *il = container_of(work, struct il_priv, abort_scan);

 D_SCAN("Abort scan work\n");

 /* We keep scan_check work queued in case when firmware will not
 * report back scan completed notification */

 mutex_lock(&il->mutex);
 il_scan_cancel_timeout(il, 200);
 mutex_unlock(&il->mutex);
}

static void
il_bg_scan_completed(struct work_struct *work)
{
 struct il_priv *il = container_of(work, struct il_priv, scan_completed);
 bool aborted;

 D_SCAN("Completed scan.\n");

 cancel_delayed_work(&il->scan_check);

 mutex_lock(&il->mutex);

 aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status);
 if (aborted)
  D_SCAN("Aborted scan completed.\n");

 if (!test_and_clear_bit(S_SCANNING, &il->status)) {
  D_SCAN("Scan already completed.\n");
  goto out_settings;
 }

 il_complete_scan(il, aborted);

out_settings:
 /* Can we still talk to firmware ? */
 if (!il_is_ready_rf(il))
  goto out;

 /*
 * We do not commit power settings while scan is pending,
 * do it now if the settings changed.
 */

 il_power_set_mode(il, &il->power_data.sleep_cmd_next, false);
 il_set_tx_power(il, il->tx_power_next, false);

 il->ops->post_scan(il);

out:
 mutex_unlock(&il->mutex);
}

void
il_setup_scan_deferred_work(struct il_priv *il)
{
 INIT_WORK(&il->scan_completed, il_bg_scan_completed);
 INIT_WORK(&il->abort_scan, il_bg_abort_scan);
 INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check);
}
EXPORT_SYMBOL(il_setup_scan_deferred_work);

void
il_cancel_scan_deferred_work(struct il_priv *il)
{
 cancel_work_sync(&il->abort_scan);
 cancel_work_sync(&il->scan_completed);

 if (cancel_delayed_work_sync(&il->scan_check)) {
  mutex_lock(&il->mutex);
  il_force_scan_end(il);
  mutex_unlock(&il->mutex);
 }
}
EXPORT_SYMBOL(il_cancel_scan_deferred_work);

/* il->sta_lock must be held */
static void
il_sta_ucode_activate(struct il_priv *il, u8 sta_id)
{

 if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE))
  IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n",
         sta_id, il->stations[sta_id].sta.sta.addr);

 if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) {
  D_ASSOC("STA id %u addr %pM already present"
   " in uCode (according to driver)\n", sta_id,
   il->stations[sta_id].sta.sta.addr);
 } else {
  il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE;
  D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id,
   il->stations[sta_id].sta.sta.addr);
 }
}

static int
il_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta,
   struct il_rx_pkt *pkt, bool sync)
{
 u8 sta_id = addsta->sta.sta_id;
 unsigned long flags;
 int ret = -EIO;

 if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
  IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags);
  return ret;
 }

 D_INFO("Processing response for adding station %u\n", sta_id);

 spin_lock_irqsave(&il->sta_lock, flags);

 switch (pkt->u.add_sta.status) {
 case ADD_STA_SUCCESS_MSK:
  D_INFO("C_ADD_STA PASSED\n");
  il_sta_ucode_activate(il, sta_id);
  ret = 0;
  break;
 case ADD_STA_NO_ROOM_IN_TBL:
  IL_ERR("Adding station %d failed, no room in table.\n", sta_id);
  break;
 case ADD_STA_NO_BLOCK_ACK_RESOURCE:
  IL_ERR("Adding station %d failed, no block ack resource.\n",
         sta_id);
  break;
 case ADD_STA_MODIFY_NON_EXIST_STA:
  IL_ERR("Attempting to modify non-existing station %d\n",
         sta_id);
  break;
 default:
  D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status);
  break;
 }

 D_INFO("%s station id %u addr %pM\n",
        il->stations[sta_id].sta.mode ==
        STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id,
        il->stations[sta_id].sta.sta.addr);

 /*
 * XXX: The MAC address in the command buffer is often changed from
 * the original sent to the device. That is, the MAC address
 * written to the command buffer often is not the same MAC address
 * read from the command buffer when the command returns. This
 * issue has not yet been resolved and this debugging is left to
 * observe the problem.
 */

 D_INFO("%s station according to cmd buffer %pM\n",
        il->stations[sta_id].sta.mode ==
        STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr);
 spin_unlock_irqrestore(&il->sta_lock, flags);

 return ret;
}

static void
il_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd,
      struct il_rx_pkt *pkt)
{
 struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload;

 il_process_add_sta_resp(il, addsta, pkt, false);

}

int
il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags)
{
 struct il_rx_pkt *pkt = NULL;
 int ret = 0;
 u8 data[sizeof(*sta)];
 struct il_host_cmd cmd = {
  .id = C_ADD_STA,
  .flags = flags,
  .data = data,
 };
 u8 sta_id __maybe_unused = sta->sta.sta_id;

 D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr,
        flags & CMD_ASYNC ? "a" : "");

 if (flags & CMD_ASYNC)
  cmd.callback = il_add_sta_callback;
 else {
  cmd.flags |= CMD_WANT_SKB;
  might_sleep();
 }

 cmd.len = il->ops->build_addsta_hcmd(sta, data);
 ret = il_send_cmd(il, &cmd);
 if (ret)
  return ret;
 if (flags & CMD_ASYNC)
  return 0;

 pkt = (struct il_rx_pkt *)cmd.reply_page;
 ret = il_process_add_sta_resp(il, sta, pkt, true);

 il_free_pages(il, cmd.reply_page);

 return ret;
}
EXPORT_SYMBOL(il_send_add_sta);

static void
il_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta)
{
 struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->deflink.ht_cap;
 __le32 sta_flags;

 if (!sta || !sta_ht_inf->ht_supported)
  goto done;

 D_ASSOC("spatial multiplexing power save mode: %s\n",
  (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC) ? "static" :
  (sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" :
  "disabled");

 sta_flags = il->stations[idx].sta.station_flags;

 sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);

 switch (sta->deflink.smps_mode) {
 case IEEE80211_SMPS_STATIC:
  sta_flags |= STA_FLG_MIMO_DIS_MSK;
  break;
 case IEEE80211_SMPS_DYNAMIC:
  sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
  break;
 case IEEE80211_SMPS_OFF:
  break;
 default:
  IL_WARN("Invalid MIMO PS mode %d\n", sta->deflink.smps_mode);
  break;
 }

 sta_flags |=
     cpu_to_le32((u32) sta_ht_inf->
   ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);

 sta_flags |=
     cpu_to_le32((u32) sta_ht_inf->
   ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);

 if (il_is_ht40_tx_allowed(il, &sta->deflink.ht_cap))
  sta_flags |= STA_FLG_HT40_EN_MSK;
 else
  sta_flags &= ~STA_FLG_HT40_EN_MSK;

 il->stations[idx].sta.station_flags = sta_flags;
done:
 return;
}

/*
 * il_prep_station - Prepare station information for addition
 *
 * should be called with sta_lock held
 */

u8
il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap,
  struct ieee80211_sta *sta)
{
 struct il_station_entry *station;
 int i;
 u8 sta_id = IL_INVALID_STATION;
 u16 rate;

 if (is_ap)
  sta_id = IL_AP_ID;
 else if (is_broadcast_ether_addr(addr))
  sta_id = il->hw_params.bcast_id;
 else
  for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) {
   if (ether_addr_equal(il->stations[i].sta.sta.addr,
          addr)) {
    sta_id = i;
    break;
   }

   if (!il->stations[i].used &&
       sta_id == IL_INVALID_STATION)
    sta_id = i;
  }

 /*
 * These two conditions have the same outcome, but keep them
 * separate
 */

 if (unlikely(sta_id == IL_INVALID_STATION))
  return sta_id;

 /*
 * uCode is not able to deal with multiple requests to add a
 * station. Keep track if one is in progress so that we do not send
 * another.
 */

 if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
  D_INFO("STA %d already in process of being added.\n", sta_id);
  return sta_id;
 }

 if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
     (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) &&
     ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) {
  D_ASSOC("STA %d (%pM) already added, not adding again.\n",
   sta_id, addr);
  return sta_id;
 }

 station = &il->stations[sta_id];
 station->used = IL_STA_DRIVER_ACTIVE;
 D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr);
 il->num_stations++;

 /* Set up the C_ADD_STA command to send to device */
 memset(&station->sta, 0, sizeof(struct il_addsta_cmd));
 memcpy(station->sta.sta.addr, addr, ETH_ALEN);
 station->sta.mode = 0;
 station->sta.sta.sta_id = sta_id;
 station->sta.station_flags = 0;

 /*
 * OK to call unconditionally, since local stations (IBSS BSSID
 * STA and broadcast STA) pass in a NULL sta, and mac80211
 * doesn't allow HT IBSS.
 */

 il_set_ht_add_station(il, sta_id, sta);

 /* 3945 only */
 rate = (il->band == NL80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP;
 /* Turn on both antennas for the station... */
 station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);

 return sta_id;

}
EXPORT_SYMBOL_GPL(il_prep_station);

#define STA_WAIT_TIMEOUT (HZ/2)

/*
 * il_add_station_common -
 */

int
il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap,
        struct ieee80211_sta *sta, u8 *sta_id_r)
{
 unsigned long flags_spin;
 int ret = 0;
 u8 sta_id;
 struct il_addsta_cmd sta_cmd;

 *sta_id_r = 0;
 spin_lock_irqsave(&il->sta_lock, flags_spin);
 sta_id = il_prep_station(il, addr, is_ap, sta);
 if (sta_id == IL_INVALID_STATION) {
  IL_ERR("Unable to prepare station %pM for addition\n", addr);
  spin_unlock_irqrestore(&il->sta_lock, flags_spin);
  return -EINVAL;
 }

 /*
 * uCode is not able to deal with multiple requests to add a
 * station. Keep track if one is in progress so that we do not send
 * another.
 */

 if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
  D_INFO("STA %d already in process of being added.\n", sta_id);
  spin_unlock_irqrestore(&il->sta_lock, flags_spin);
  return -EEXIST;
 }

 if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
     (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
  D_ASSOC("STA %d (%pM) already added, not adding again.\n",
   sta_id, addr);
  spin_unlock_irqrestore(&il->sta_lock, flags_spin);
  return -EEXIST;
 }

 il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS;
 memcpy(&sta_cmd, &il->stations[sta_id].sta,
        sizeof(struct il_addsta_cmd));
 spin_unlock_irqrestore(&il->sta_lock, flags_spin);

 /* Add station to device's station table */
 ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
 if (ret) {
  spin_lock_irqsave(&il->sta_lock, flags_spin);
  IL_ERR("Adding station %pM failed.\n",
         il->stations[sta_id].sta.sta.addr);
  il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
  il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
  spin_unlock_irqrestore(&il->sta_lock, flags_spin);
 }
 *sta_id_r = sta_id;
 return ret;
}
EXPORT_SYMBOL(il_add_station_common);

/*
 * il_sta_ucode_deactivate - deactivate ucode status for a station
 *
 * il->sta_lock must be held
 */

static void
il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id)
{
 /* Ucode must be active and driver must be non active */
 if ((il->stations[sta_id].
      used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) !=
     IL_STA_UCODE_ACTIVE)
  IL_ERR("removed non active STA %u\n", sta_id);

 il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE;

 memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry));
 D_ASSOC("Removed STA %u\n", sta_id);
}

static int
il_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id,
         bool temporary)
{
 struct il_rx_pkt *pkt;
 int ret;

 unsigned long flags_spin;
 struct il_rem_sta_cmd rm_sta_cmd;

 struct il_host_cmd cmd = {
  .id = C_REM_STA,
  .len = sizeof(struct il_rem_sta_cmd),
  .flags = CMD_SYNC,
  .data = &rm_sta_cmd,
 };

 memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
 rm_sta_cmd.num_sta = 1;
 memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);

 cmd.flags |= CMD_WANT_SKB;

 ret = il_send_cmd(il, &cmd);

 if (ret)
  return ret;

 pkt = (struct il_rx_pkt *)cmd.reply_page;
 if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
  IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags);
  ret = -EIO;
 }

 if (!ret) {
  switch (pkt->u.rem_sta.status) {
  case REM_STA_SUCCESS_MSK:
   if (!temporary) {
    spin_lock_irqsave(&il->sta_lock, flags_spin);
    il_sta_ucode_deactivate(il, sta_id);
    spin_unlock_irqrestore(&il->sta_lock,
             flags_spin);
   }
   D_ASSOC("C_REM_STA PASSED\n");
   break;
  default:
   ret = -EIO;
   IL_ERR("C_REM_STA failed\n");
   break;
  }
 }
 il_free_pages(il, cmd.reply_page);

 return ret;
}

/*
 * il_remove_station - Remove driver's knowledge of station.
 */

int
il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr)
{
 unsigned long flags;

 if (!il_is_ready(il)) {
  D_INFO("Unable to remove station %pM, device not ready.\n",
         addr);
  /*
 * It is typical for stations to be removed when we are
 * going down. Return success since device will be down
 * soon anyway
 */

  return 0;
 }

 D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr);

 if (WARN_ON(sta_id == IL_INVALID_STATION))
  return -EINVAL;

 spin_lock_irqsave(&il->sta_lock, flags);

 if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) {
  D_INFO("Removing %pM but non DRIVER active\n", addr);
  goto out_err;
 }

 if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
  D_INFO("Removing %pM but non UCODE active\n", addr);
  goto out_err;
 }

 if (il->stations[sta_id].used & IL_STA_LOCAL) {
  kfree(il->stations[sta_id].lq);
  il->stations[sta_id].lq = NULL;
 }

 il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;

 il->num_stations--;

 BUG_ON(il->num_stations < 0);

 spin_unlock_irqrestore(&il->sta_lock, flags);

 return il_send_remove_station(il, addr, sta_id, false);
out_err:
 spin_unlock_irqrestore(&il->sta_lock, flags);
 return -EINVAL;
}
EXPORT_SYMBOL_GPL(il_remove_station);

/*
 * il_clear_ucode_stations - clear ucode station table bits
 *
 * This function clears all the bits in the driver indicating
 * which stations are active in the ucode. Call when something
 * other than explicit station management would cause this in
 * the ucode, e.g. unassociated RXON.
 */

void
il_clear_ucode_stations(struct il_priv *il)
{
 int i;
 unsigned long flags_spin;
 bool cleared = false;

 D_INFO("Clearing ucode stations in driver\n");

 spin_lock_irqsave(&il->sta_lock, flags_spin);
 for (i = 0; i < il->hw_params.max_stations; i++) {
  if (il->stations[i].used & IL_STA_UCODE_ACTIVE) {
   D_INFO("Clearing ucode active for station %d\n", i);
   il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
   cleared = true;
  }
 }
 spin_unlock_irqrestore(&il->sta_lock, flags_spin);

 if (!cleared)
  D_INFO("No active stations found to be cleared\n");
}
EXPORT_SYMBOL(il_clear_ucode_stations);

/*
 * il_restore_stations() - Restore driver known stations to device
 *
 * All stations considered active by driver, but not present in ucode, is
 * restored.
 *
 * Function sleeps.
 */

void
il_restore_stations(struct il_priv *il)
{
 struct il_addsta_cmd sta_cmd;
 struct il_link_quality_cmd lq;
 unsigned long flags_spin;
 int i;
 bool found = false;
 int ret;
 bool send_lq;

 if (!il_is_ready(il)) {
  D_INFO("Not ready yet, not restoring any stations.\n");
  return;
 }

 D_ASSOC("Restoring all known stations ... start.\n");
 spin_lock_irqsave(&il->sta_lock, flags_spin);
 for (i = 0; i < il->hw_params.max_stations; i++) {
  if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) &&
      !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) {
   D_ASSOC("Restoring sta %pM\n",
    il->stations[i].sta.sta.addr);
   il->stations[i].sta.mode = 0;
   il->stations[i].used |= IL_STA_UCODE_INPROGRESS;
   found = true;
  }
 }

 for (i = 0; i < il->hw_params.max_stations; i++) {
  if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) {
   memcpy(&sta_cmd, &il->stations[i].sta,
          sizeof(struct il_addsta_cmd));
   send_lq = false;
   if (il->stations[i].lq) {
    memcpy(&lq, il->stations[i].lq,
           sizeof(struct il_link_quality_cmd));
    send_lq = true;
   }
   spin_unlock_irqrestore(&il->sta_lock, flags_spin);
   ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
   if (ret) {
    spin_lock_irqsave(&il->sta_lock, flags_spin);
    IL_ERR("Adding station %pM failed.\n",
           il->stations[i].sta.sta.addr);
    il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE;
    il->stations[i].used &=
        ~IL_STA_UCODE_INPROGRESS;
    spin_unlock_irqrestore(&il->sta_lock,
             flags_spin);
   }
   /*
 * Rate scaling has already been initialized, send
 * current LQ command
 */

   if (send_lq)
    il_send_lq_cmd(il, &lq, CMD_SYNC, true);
   spin_lock_irqsave(&il->sta_lock, flags_spin);
   il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS;
  }
 }

 spin_unlock_irqrestore(&il->sta_lock, flags_spin);
 if (!found)
  D_INFO("Restoring all known stations"
         " .... no stations to be restored.\n");
 else
  D_INFO("Restoring all known stations" " .... complete.\n");
}
EXPORT_SYMBOL(il_restore_stations);

int
il_get_free_ucode_key_idx(struct il_priv *il)
{
 int i;

 for (i = 0; i < il->sta_key_max_num; i++)
  if (!test_and_set_bit(i, &il->ucode_key_table))
   return i;

 return WEP_INVALID_OFFSET;
}
EXPORT_SYMBOL(il_get_free_ucode_key_idx);

void
il_dealloc_bcast_stations(struct il_priv *il)
{
 unsigned long flags;
 int i;

 spin_lock_irqsave(&il->sta_lock, flags);
 for (i = 0; i < il->hw_params.max_stations; i++) {
  if (!(il->stations[i].used & IL_STA_BCAST))
   continue;

  il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
  il->num_stations--;
  BUG_ON(il->num_stations < 0);
  kfree(il->stations[i].lq);
  il->stations[i].lq = NULL;
 }
 spin_unlock_irqrestore(&il->sta_lock, flags);
}
EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations);

#ifdef CONFIG_IWLEGACY_DEBUG
static void
il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
{
 int i;
 D_RATE("lq station id 0x%x\n", lq->sta_id);
 D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk,
        lq->general_params.dual_stream_ant_msk);

 for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
  D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags);
}
#else
static inline void
il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
{
}
#endif

/*
 * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity
 *
 * It sometimes happens when a HT rate has been in use and we
 * loose connectivity with AP then mac80211 will first tell us that the
 * current channel is not HT anymore before removing the station. In such a
 * scenario the RXON flags will be updated to indicate we are not
 * communicating HT anymore, but the LQ command may still contain HT rates.
 * Test for this to prevent driver from sending LQ command between the time
 * RXON flags are updated and when LQ command is updated.
 */

static bool
il_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq)
{
 int i;

 if (il->ht.enabled)
  return true;

 D_INFO("Channel %u is not an HT channel\n", il->active.channel);
 for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
  if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) {
   D_INFO("idx %d of LQ expects HT channel\n", i);
   return false;
  }
 }
 return true;
}

/*
 * il_send_lq_cmd() - Send link quality command
 * @init: This command is sent as part of station initialization right
 *        after station has been added.
 *
 * The link quality command is sent as the last step of station creation.
 * This is the special case in which init is set and we call a callback in
 * this case to clear the state indicating that station creation is in
 * progress.
 */

int
il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq,
        u8 flags, bool init)
{
 int ret = 0;
 unsigned long flags_spin;

 struct il_host_cmd cmd = {
  .id = C_TX_LINK_QUALITY_CMD,
  .len = sizeof(struct il_link_quality_cmd),
  .flags = flags,
  .data = lq,
 };

 if (WARN_ON(lq->sta_id == IL_INVALID_STATION))
  return -EINVAL;

 spin_lock_irqsave(&il->sta_lock, flags_spin);
 if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) {
  spin_unlock_irqrestore(&il->sta_lock, flags_spin);
  return -EINVAL;
 }
 spin_unlock_irqrestore(&il->sta_lock, flags_spin);

 il_dump_lq_cmd(il, lq);
 BUG_ON(init && (cmd.flags & CMD_ASYNC));

 if (il_is_lq_table_valid(il, lq))
  ret = il_send_cmd(il, &cmd);
 else
  ret = -EINVAL;

 if (cmd.flags & CMD_ASYNC)
  return ret;

 if (init) {
  D_INFO("init LQ command complete,"
         " clearing sta addition status for sta %d\n",
         lq->sta_id);
  spin_lock_irqsave(&il->sta_lock, flags_spin);
  il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
  spin_unlock_irqrestore(&il->sta_lock, flags_spin);
 }
 return ret;
}
EXPORT_SYMBOL(il_send_lq_cmd);

int
il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
    struct ieee80211_sta *sta)
{
 struct il_priv *il = hw->priv;
 struct il_station_priv_common *sta_common = (void *)sta->drv_priv;
 int ret;

 mutex_lock(&il->mutex);
 D_MAC80211("enter station %pM\n", sta->addr);

 ret = il_remove_station(il, sta_common->sta_id, sta->addr);
 if (ret)
  IL_ERR("Error removing station %pM\n", sta->addr);

 D_MAC80211("leave ret %d\n", ret);
 mutex_unlock(&il->mutex);

 return ret;
}
EXPORT_SYMBOL(il_mac_sta_remove);

/************************** RX-FUNCTIONS ****************************/
/*
 * Rx theory of operation
 *
 * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs),
 * each of which point to Receive Buffers to be filled by the NIC.  These get
 * used not only for Rx frames, but for any command response or notification
 * from the NIC.  The driver and NIC manage the Rx buffers by means
 * of idxes into the circular buffer.
 *
 * Rx Queue Indexes
 * The host/firmware share two idx registers for managing the Rx buffers.
 *
 * The READ idx maps to the first position that the firmware may be writing
 * to -- the driver can read up to (but not including) this position and get
 * good data.
 * The READ idx is managed by the firmware once the card is enabled.
 *
 * The WRITE idx maps to the last position the driver has read from -- the
 * position preceding WRITE is the last slot the firmware can place a packet.
 *
 * The queue is empty (no good data) if WRITE = READ - 1, and is full if
 * WRITE = READ.
 *
 * During initialization, the host sets up the READ queue position to the first
 * IDX position, and WRITE to the last (READ - 1 wrapped)
 *
 * When the firmware places a packet in a buffer, it will advance the READ idx
 * and fire the RX interrupt.  The driver can then query the READ idx and
 * process as many packets as possible, moving the WRITE idx forward as it
 * resets the Rx queue buffers with new memory.
 *
 * The management in the driver is as follows:
 * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free.  When
 *   iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
 *   to replenish the iwl->rxq->rx_free.
 * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the
 *   iwl->rxq is replenished and the READ IDX is updated (updating the
 *   'processed' and 'read' driver idxes as well)
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.22 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.