Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
authorDavid S. Miller <davem@davemloft.net>
Fri, 31 Jul 2009 02:22:43 +0000 (19:22 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 31 Jul 2009 02:22:43 +0000 (19:22 -0700)
Conflicts:
drivers/net/wireless/iwlwifi/iwl-3945.h
drivers/net/wireless/iwlwifi/iwl-tx.c
drivers/net/wireless/iwlwifi/iwl3945-base.c

18 files changed:
1  2 
drivers/net/3c515.c
drivers/net/at1700.c
drivers/net/eepro.c
drivers/net/eexpress.c
drivers/net/fealnx.c
drivers/net/ixgbe/ixgbe.h
drivers/net/ixgbe/ixgbe_ethtool.c
drivers/net/ixgbe/ixgbe_main.c
drivers/net/netxen/netxen_nic_init.c
drivers/net/tokenring/ibmtr.c
drivers/net/wireless/airo.c
drivers/net/wireless/ath/ath9k/eeprom.c
drivers/net/wireless/iwlwifi/iwl-tx.c
drivers/net/wireless/iwmc3200wifi/netdev.c
drivers/net/wireless/libertas/assoc.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/netlabel/netlabel_kapi.c

diff --combined drivers/net/3c515.c
@@@ -832,7 -832,9 +832,9 @@@ static int corkscrew_open(struct net_de
                        skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
                        vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
                }
-               vp->rx_ring[i - 1].next = isa_virt_to_bus(&vp->rx_ring[0]);     /* Wrap the ring. */
+               if (i != 0)
+                       vp->rx_ring[i - 1].next =
+                               isa_virt_to_bus(&vp->rx_ring[0]);       /* Wrap the ring. */
                outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
        }
        if (vp->full_bus_master_tx) {   /* Boomerang bus master Tx. */
@@@ -1054,7 -1056,7 +1056,7 @@@ static int corkscrew_start_xmit(struct 
                        netif_wake_queue(dev);
                }
                dev->trans_start = jiffies;
 -              return 0;
 +              return NETDEV_TX_OK;
        }
        /* Put out the doubleword header... */
        outl(skb->len, ioaddr + TX_FIFO);
                        outb(0x00, ioaddr + TxStatus);  /* Pop the status stack. */
                }
        }
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  /* The interrupt handler does all of the Rx thread work and cleans up
diff --combined drivers/net/at1700.c
@@@ -318,7 -318,7 +318,7 @@@ static int __init at1700_probe1(struct 
                                pos3 = mca_read_stored_pos( slot, 3 );
                                pos4 = mca_read_stored_pos( slot, 4 );
  
-                               for (l_i = 0; l_i < 0x09; l_i++)
+                               for (l_i = 0; l_i < 8; l_i++)
                                        if (( pos3 & 0x07) == at1700_ioaddr_pattern[l_i])
                                                break;
                                ioaddr = at1700_mca_probe_list[l_i];
@@@ -643,7 -643,7 +643,7 @@@ static int net_send_packet (struct sk_b
                netif_start_queue (dev);
        dev_kfree_skb (skb);
  
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  /* The typical workload of the driver:
diff --combined drivers/net/eepro.c
@@@ -1145,7 -1145,7 +1145,7 @@@ static int eepro_send_packet(struct sk_
  
        if (length < ETH_ZLEN) {
                if (skb_padto(skb, ETH_ZLEN))
 -                      return 0;
 +                      return NETDEV_TX_OK;
                length = ETH_ZLEN;
        }
        netif_stop_queue (dev);
        eepro_en_int(ioaddr);
        spin_unlock_irqrestore(&lp->lock, flags);
  
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  
@@@ -1784,7 -1784,7 +1784,7 @@@ int __init init_module(void
                printk(KERN_INFO "eepro_init_module: Auto-detecting boards (May God protect us...)\n");
        }
  
-       for (i = 0; io[i] != -1 && i < MAX_EEPRO; i++) {
+       for (i = 0; i < MAX_EEPRO && io[i] != -1; i++) {
                dev = alloc_etherdev(sizeof(struct eepro_local));
                if (!dev)
                        break;
diff --combined drivers/net/eexpress.c
@@@ -664,7 -664,7 +664,7 @@@ static int eexp_xmit(struct sk_buff *bu
  
        if (buf->len < ETH_ZLEN) {
                if (skb_padto(buf, ETH_ZLEN))
 -                      return 0;
 +                      return NETDEV_TX_OK;
                length = ETH_ZLEN;
        }
  
        spin_unlock_irqrestore(&lp->lock, flags);
  #endif
        enable_irq(dev->irq);
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  /*
@@@ -1474,13 -1474,13 +1474,13 @@@ static void eexp_hw_init586(struct net_
        outw(0x0000, ioaddr + 0x800c);
        outw(0x0000, ioaddr + 0x800e);
  
-       for (i = 0; i < (sizeof(start_code)); i+=32) {
+       for (i = 0; i < ARRAY_SIZE(start_code) * 2; i+=32) {
                int j;
                outw(i, ioaddr + SM_PTR);
-               for (j = 0; j < 16; j+=2)
+               for (j = 0; j < 16 && (i+j)/2 < ARRAY_SIZE(start_code); j+=2)
                        outw(start_code[(i+j)/2],
                             ioaddr+0x4000+j);
-               for (j = 0; j < 16; j+=2)
+               for (j = 0; j < 16 && (i+j+16)/2 < ARRAY_SIZE(start_code); j+=2)
                        outw(start_code[(i+j+16)/2],
                             ioaddr+0x8000+j);
        }
diff --combined drivers/net/fealnx.c
@@@ -584,7 -584,8 +584,8 @@@ static int __devinit fealnx_init_one(st
        if (np->flags == HAS_MII_XCVR) {
                int phy, phy_idx = 0;
  
-               for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
+               for (phy = 1; phy < 32 && phy_idx < ARRAY_SIZE(np->phys);
+                              phy++) {
                        int mii_status = mdio_read(dev, phy, 1);
  
                        if (mii_status != 0xffff && mii_status != 0x0000) {
@@@ -1374,7 -1375,7 +1375,7 @@@ static int start_tx(struct sk_buff *skb
        dev->trans_start = jiffies;
  
        spin_unlock_irqrestore(&np->lock, flags);
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  
@@@ -96,6 -96,8 +96,8 @@@
  #define IXGBE_TX_FLAGS_VLAN_PRIO_MASK   0x0000e000
  #define IXGBE_TX_FLAGS_VLAN_SHIFT     16
  
+ #define IXGBE_MAX_RSC_INT_RATE          162760
  /* wrapper around a pointer to a socket buffer,
   * so a DMA handle can be stored along with the buffer */
  struct ixgbe_tx_buffer {
@@@ -229,6 -231,10 +231,6 @@@ struct ixgbe_q_vector 
  #define IXGBE_TX_CTXTDESC_ADV(R, i)       \
        (&(((struct ixgbe_adv_tx_context_desc *)((R).desc))[i]))
  
 -#define IXGBE_GET_DESC(R, i, type)    (&(((struct type *)((R).desc))[i]))
 -#define IXGBE_TX_DESC(R, i)   IXGBE_GET_DESC(R, i, ixgbe_legacy_tx_desc)
 -#define IXGBE_RX_DESC(R, i)   IXGBE_GET_DESC(R, i, ixgbe_legacy_rx_desc)
 -
  #define IXGBE_MAX_JUMBO_FRAME_SIZE        16128
  #ifdef IXGBE_FCOE
  /* Use 3K as the baby jumbo frame size for FCoE */
@@@ -1440,7 -1440,7 +1440,7 @@@ static int ixgbe_setup_desc_rings(struc
                goto err_nomem;
        }
  
 -      tx_ring->size = tx_ring->count * sizeof(struct ixgbe_legacy_tx_desc);
 +      tx_ring->size = tx_ring->count * sizeof(union ixgbe_adv_tx_desc);
        tx_ring->size = ALIGN(tx_ring->size, 4096);
        if (!(tx_ring->desc = pci_alloc_consistent(pdev, tx_ring->size,
                                                   &tx_ring->dma))) {
        IXGBE_WRITE_REG(&adapter->hw, IXGBE_TDBAH(0),
                        ((u64) tx_ring->dma >> 32));
        IXGBE_WRITE_REG(&adapter->hw, IXGBE_TDLEN(0),
 -                      tx_ring->count * sizeof(struct ixgbe_legacy_tx_desc));
 +                      tx_ring->count * sizeof(union ixgbe_adv_tx_desc));
        IXGBE_WRITE_REG(&adapter->hw, IXGBE_TDH(0), 0);
        IXGBE_WRITE_REG(&adapter->hw, IXGBE_TDT(0), 0);
  
        IXGBE_WRITE_REG(&adapter->hw, IXGBE_TXDCTL(0), reg_data);
  
        for (i = 0; i < tx_ring->count; i++) {
 -              struct ixgbe_legacy_tx_desc *desc = IXGBE_TX_DESC(*tx_ring, i);
 +              union ixgbe_adv_tx_desc *desc = IXGBE_TX_DESC_ADV(*tx_ring, i);
                struct sk_buff *skb;
                unsigned int size = 1024;
  
                tx_ring->tx_buffer_info[i].length = skb->len;
                tx_ring->tx_buffer_info[i].dma =
                        pci_map_single(pdev, skb->data, skb->len,
 -                                      PCI_DMA_TODEVICE);
 -              desc->buffer_addr = cpu_to_le64(tx_ring->tx_buffer_info[i].dma);
 -              desc->lower.data = cpu_to_le32(skb->len);
 -              desc->lower.data |= cpu_to_le32(IXGBE_TXD_CMD_EOP |
 -                                              IXGBE_TXD_CMD_IFCS |
 -                                              IXGBE_TXD_CMD_RS);
 -              desc->upper.data = 0;
 +                                     PCI_DMA_TODEVICE);
 +              desc->read.buffer_addr =
 +                                  cpu_to_le64(tx_ring->tx_buffer_info[i].dma);
 +              desc->read.cmd_type_len = cpu_to_le32(skb->len);
 +              desc->read.cmd_type_len |= cpu_to_le32(IXGBE_TXD_CMD_EOP |
 +                                                     IXGBE_TXD_CMD_IFCS |
 +                                                     IXGBE_TXD_CMD_RS);
 +              desc->read.olinfo_status = 0;
 +              if (adapter->hw.mac.type == ixgbe_mac_82599EB)
 +                      desc->read.olinfo_status |=
 +                                      (skb->len << IXGBE_ADVTXD_PAYLEN_SHIFT);
 +
        }
  
        /* Setup Rx Descriptor ring and Rx buffers */
                goto err_nomem;
        }
  
 -      rx_ring->size = rx_ring->count * sizeof(struct ixgbe_legacy_rx_desc);
 +      rx_ring->size = rx_ring->count * sizeof(union ixgbe_adv_rx_desc);
        rx_ring->size = ALIGN(rx_ring->size, 4096);
        if (!(rx_ring->desc = pci_alloc_consistent(pdev, rx_ring->size,
                                                   &rx_ring->dma))) {
        IXGBE_WRITE_REG(&adapter->hw, IXGBE_RXCTRL, rctl);
  
        for (i = 0; i < rx_ring->count; i++) {
 -              struct ixgbe_legacy_rx_desc *rx_desc =
 -                                      IXGBE_RX_DESC(*rx_ring, i);
 +              union ixgbe_adv_rx_desc *rx_desc =
 +                                               IXGBE_RX_DESC_ADV(*rx_ring, i);
                struct sk_buff *skb;
  
                skb = alloc_skb(IXGBE_RXBUFFER_2048 + NET_IP_ALIGN, GFP_KERNEL);
                rx_ring->rx_buffer_info[i].dma =
                        pci_map_single(pdev, skb->data, IXGBE_RXBUFFER_2048,
                                       PCI_DMA_FROMDEVICE);
 -              rx_desc->buffer_addr =
 +              rx_desc->read.pkt_addr =
                                cpu_to_le64(rx_ring->rx_buffer_info[i].dma);
                memset(skb->data, 0x00, skb->len);
        }
@@@ -1980,7 -1975,10 +1980,10 @@@ static int ixgbe_set_coalesce(struct ne
                 * any other value means disable eitr, which is best
                 * served by setting the interrupt rate very high
                 */
-               adapter->eitr_param = IXGBE_MAX_INT_RATE;
+               if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)
+                       adapter->eitr_param = IXGBE_MAX_RSC_INT_RATE;
+               else
+                       adapter->eitr_param = IXGBE_MAX_INT_RATE;
                adapter->itr_setting = 0;
        }
  
@@@ -2004,13 -2002,13 +2007,13 @@@ static int ixgbe_set_flags(struct net_d
  
        ethtool_op_set_flags(netdev, data);
  
-       if (!(adapter->flags & IXGBE_FLAG2_RSC_CAPABLE))
+       if (!(adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE))
                return 0;
  
        /* if state changes we need to update adapter->flags and reset */
        if ((!!(data & ETH_FLAG_LRO)) != 
-           (!!(adapter->flags & IXGBE_FLAG2_RSC_ENABLED))) {
-               adapter->flags ^= IXGBE_FLAG2_RSC_ENABLED;
+           (!!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED))) {
+               adapter->flags2 ^= IXGBE_FLAG2_RSC_ENABLED;
                if (netif_running(netdev))
                        ixgbe_reinit_locked(adapter);
                else
@@@ -49,7 -49,7 +49,7 @@@ char ixgbe_driver_name[] = "ixgbe"
  static const char ixgbe_driver_string[] =
                                "Intel(R) 10 Gigabit PCI Express Network Driver";
  
 -#define DRV_VERSION "2.0.34-k2"
 +#define DRV_VERSION "2.0.37-k2"
  const char ixgbe_driver_version[] = DRV_VERSION;
  static char ixgbe_copyright[] = "Copyright (c) 1999-2009 Intel Corporation.";
  
@@@ -75,8 -75,6 +75,8 @@@ static struct pci_device_id ixgbe_pci_t
         board_82598 },
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT),
         board_82598 },
 +      {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT2),
 +       board_82598 },
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_CX4),
         board_82598 },
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_CX4_DUAL_PORT),
@@@ -513,8 -511,11 +513,11 @@@ static void ixgbe_receive_skb(struct ix
   * @skb: skb currently being received and modified
   **/
  static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter,
-                                      u32 status_err, struct sk_buff *skb)
+                                    union ixgbe_adv_rx_desc *rx_desc,
+                                    struct sk_buff *skb)
  {
+       u32 status_err = le32_to_cpu(rx_desc->wb.upper.status_error);
        skb->ip_summed = CHECKSUM_NONE;
  
        /* Rx csum disabled */
                return;
  
        if (status_err & IXGBE_RXDADV_ERR_TCPE) {
+               u16 pkt_info = rx_desc->wb.lower.lo_dword.hs_rss.pkt_info;
+               /*
+                * 82599 errata, UDP frames with a 0 checksum can be marked as
+                * checksum errors.
+                */
+               if ((pkt_info & IXGBE_RXDADV_PKTTYPE_UDP) &&
+                   (adapter->hw.mac.type == ixgbe_mac_82599EB))
+                       return;
                adapter->hw_csum_rx_error++;
                return;
        }
@@@ -769,7 -780,7 +782,7 @@@ static bool ixgbe_clean_rx_irq(struct i
                prefetch(next_rxd);
                cleaned_count++;
  
-               if (adapter->flags & IXGBE_FLAG2_RSC_CAPABLE)
+               if (adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE)
                        rsc_count = ixgbe_get_rsc_count(rx_desc);
  
                if (rsc_count) {
                        goto next_desc;
                }
  
-               ixgbe_rx_checksum(adapter, staterr, skb);
+               ixgbe_rx_checksum(adapter, rx_desc, skb);
  
                /* probably a little skewed due to removing CRC */
                total_rx_bytes += skb->len;
@@@ -2025,7 -2036,7 +2038,7 @@@ static void ixgbe_configure_rx(struct i
                        IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(0), psrtype);
                }
        } else {
-               if (!(adapter->flags & IXGBE_FLAG2_RSC_ENABLED) &&
+               if (!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) &&
                    (netdev->mtu <= ETH_DATA_LEN))
                        rx_buf_len = MAXIMUM_ETHERNET_VLAN_SIZE;
                else
                IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rdrxctl);
        }
  
-       if (adapter->flags & IXGBE_FLAG2_RSC_ENABLED) {
+       if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
                /* Enable 82599 HW-RSC */
                for (i = 0; i < adapter->num_rx_queues; i++) {
                        j = adapter->rx_ring[i].reg_idx;
@@@ -3801,8 -3812,8 +3814,8 @@@ static int __devinit ixgbe_sw_init(stru
                adapter->max_msix_q_vectors = MAX_MSIX_Q_VECTORS_82598;
        } else if (hw->mac.type == ixgbe_mac_82599EB) {
                adapter->max_msix_q_vectors = MAX_MSIX_Q_VECTORS_82599;
-               adapter->flags |= IXGBE_FLAG2_RSC_CAPABLE;
-               adapter->flags |= IXGBE_FLAG2_RSC_ENABLED;
+               adapter->flags2 |= IXGBE_FLAG2_RSC_CAPABLE;
+               adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED;
                adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE;
                adapter->ring_feature[RING_F_FDIR].indices =
                                                         IXGBE_MAX_FDIR_INDICES;
@@@ -4647,13 -4658,13 +4660,13 @@@ static void ixgbe_watchdog_task(struct 
                        if (hw->mac.type == ixgbe_mac_82599EB) {
                                u32 mflcn = IXGBE_READ_REG(hw, IXGBE_MFLCN);
                                u32 fccfg = IXGBE_READ_REG(hw, IXGBE_FCCFG);
 -                              flow_rx = (mflcn & IXGBE_MFLCN_RFCE);
 -                              flow_tx = (fccfg & IXGBE_FCCFG_TFCE_802_3X);
 +                              flow_rx = !!(mflcn & IXGBE_MFLCN_RFCE);
 +                              flow_tx = !!(fccfg & IXGBE_FCCFG_TFCE_802_3X);
                        } else {
                                u32 frctl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
                                u32 rmcs = IXGBE_READ_REG(hw, IXGBE_RMCS);
 -                              flow_rx = (frctl & IXGBE_FCTRL_RFCE);
 -                              flow_tx = (rmcs & IXGBE_RMCS_TFCE_802_3X);
 +                              flow_rx = !!(frctl & IXGBE_FCTRL_RFCE);
 +                              flow_tx = !!(rmcs & IXGBE_RMCS_TFCE_802_3X);
                        }
  
                        printk(KERN_INFO "ixgbe: %s NIC Link is Up %s, "
@@@ -5349,12 -5360,19 +5362,19 @@@ static int ixgbe_del_sanmac_netdev(stru
  static void ixgbe_netpoll(struct net_device *netdev)
  {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
+       int i;
  
-       disable_irq(adapter->pdev->irq);
        adapter->flags |= IXGBE_FLAG_IN_NETPOLL;
-       ixgbe_intr(adapter->pdev->irq, netdev);
+       if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+               int num_q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+               for (i = 0; i < num_q_vectors; i++) {
+                       struct ixgbe_q_vector *q_vector = adapter->q_vector[i];
+                       ixgbe_msix_clean_many(0, q_vector);
+               }
+       } else {
+               ixgbe_intr(adapter->pdev->irq, netdev);
+       }
        adapter->flags &= ~IXGBE_FLAG_IN_NETPOLL;
-       enable_irq(adapter->pdev->irq);
  }
  #endif
  
@@@ -5600,7 -5618,7 +5620,7 @@@ static int __devinit ixgbe_probe(struc
        if (pci_using_dac)
                netdev->features |= NETIF_F_HIGHDMA;
  
-       if (adapter->flags & IXGBE_FLAG2_RSC_ENABLED)
+       if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)
                netdev->features |= NETIF_F_LRO;
  
        /* make sure the EEPROM is good */
@@@ -184,6 -184,13 +184,13 @@@ void netxen_free_sw_resources(struct ne
        kfree(recv_ctx->rds_rings);
  
  skip_rds:
+       if (recv_ctx->sds_rings == NULL)
+               goto skip_sds;
+       for(ring = 0; ring < adapter->max_sds_rings; ring++)
+               recv_ctx->sds_rings[ring].consumer = 0;
+ skip_sds:
        if (adapter->tx_ring == NULL)
                return;
  
@@@ -247,14 -254,9 +254,14 @@@ int netxen_alloc_sw_resources(struct ne
                                rds_ring->skb_size =
                                        NX_CT_DEFAULT_RX_BUF_LEN;
                        } else {
 -                              rds_ring->dma_size = RX_DMA_MAP_LEN;
 +                              if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
 +                                      rds_ring->dma_size =
 +                                              NX_P3_RX_BUF_MAX_LEN;
 +                              else
 +                                      rds_ring->dma_size =
 +                                              NX_P2_RX_BUF_MAX_LEN;
                                rds_ring->skb_size =
 -                                      MAX_RX_BUFFER_LENGTH;
 +                                      rds_ring->dma_size + NET_IP_ALIGN;
                        }
                        break;
  
  
                case RCV_RING_LRO:
                        rds_ring->num_desc = adapter->num_lro_rxd;
 -                      rds_ring->dma_size = RX_LRO_DMA_MAP_LEN;
 -                      rds_ring->skb_size = MAX_RX_LRO_BUFFER_LENGTH;
 +                      rds_ring->dma_size = NX_RX_LRO_BUFFER_LENGTH;
 +                      rds_ring->skb_size = rds_ring->dma_size + NET_IP_ALIGN;
                        break;
  
                }
@@@ -885,10 -887,22 +892,10 @@@ netxen_validate_firmware(struct netxen_
        return 0;
  }
  
 -void netxen_request_firmware(struct netxen_adapter *adapter)
 +static int
 +netxen_p3_has_mn(struct netxen_adapter *adapter)
  {
        u32 capability, flashed_ver;
 -      u8 fw_type;
 -      struct pci_dev *pdev = adapter->pdev;
 -      int rc = 0;
 -
 -      if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
 -              fw_type = NX_P2_MN_ROMIMAGE;
 -              goto request_fw;
 -      } else {
 -              fw_type = NX_P3_CT_ROMIMAGE;
 -              goto request_fw;
 -      }
 -
 -request_mn:
        capability = 0;
  
        netxen_rom_fast_read(adapter,
        flashed_ver = NETXEN_DECODE_VERSION(flashed_ver);
  
        if (flashed_ver >= NETXEN_VERSION_CODE(4, 0, 220)) {
 +
                capability = NXRD32(adapter, NX_PEG_TUNE_CAPABILITY);
 -              if (capability & NX_PEG_TUNE_MN_PRESENT) {
 -                      fw_type = NX_P3_MN_ROMIMAGE;
 -                      goto request_fw;
 -              }
 +              if (capability & NX_PEG_TUNE_MN_PRESENT)
 +                      return 1;
        }
 +      return 0;
 +}
  
 -      fw_type = NX_FLASH_ROMIMAGE;
 -      adapter->fw = NULL;
 -      goto done;
 +void netxen_request_firmware(struct netxen_adapter *adapter)
 +{
 +      u8 fw_type;
 +      struct pci_dev *pdev = adapter->pdev;
 +      int rc = 0;
 +
 +      if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
 +              fw_type = NX_P2_MN_ROMIMAGE;
 +              goto request_fw;
 +      }
 +
 +      fw_type = netxen_p3_has_mn(adapter) ?
 +              NX_P3_MN_ROMIMAGE : NX_P3_CT_ROMIMAGE;
  
  request_fw:
        rc = request_firmware(&adapter->fw, fw_name[fw_type], &pdev->dev);
        if (rc != 0) {
 -              if (fw_type == NX_P3_CT_ROMIMAGE) {
 +              if (fw_type == NX_P3_MN_ROMIMAGE) {
                        msleep(1);
 -                      goto request_mn;
 +                      fw_type = NX_P3_CT_ROMIMAGE;
 +                      goto request_fw;
                }
  
                fw_type = NX_FLASH_ROMIMAGE;
        if (rc != 0) {
                release_firmware(adapter->fw);
  
 -              if (fw_type == NX_P3_CT_ROMIMAGE) {
 +              if (fw_type == NX_P3_MN_ROMIMAGE) {
                        msleep(1);
 -                      goto request_mn;
 +                      fw_type = NX_P3_CT_ROMIMAGE;
 +                      goto request_fw;
                }
  
                fw_type = NX_FLASH_ROMIMAGE;
@@@ -959,20 -960,19 +966,20 @@@ netxen_release_firmware(struct netxen_a
                release_firmware(adapter->fw);
  }
  
 -int netxen_initialize_adapter_offload(struct netxen_adapter *adapter)
 +int netxen_init_dummy_dma(struct netxen_adapter *adapter)
  {
 -      uint64_t addr;
 -      uint32_t hi;
 -      uint32_t lo;
 +      u64 addr;
 +      u32 hi, lo;
 +
 +      if (!NX_IS_REVISION_P2(adapter->ahw.revision_id))
 +              return 0;
  
 -      adapter->dummy_dma.addr =
 -          pci_alloc_consistent(adapter->pdev,
 +      adapter->dummy_dma.addr = pci_alloc_consistent(adapter->pdev,
                                 NETXEN_HOST_DUMMY_DMA_SIZE,
                                 &adapter->dummy_dma.phys_addr);
        if (adapter->dummy_dma.addr == NULL) {
 -              printk("%s: ERROR: Could not allocate dummy DMA memory\n",
 -                     __func__);
 +              dev_err(&adapter->pdev->dev,
 +                      "ERROR: Could not allocate dummy DMA memory\n");
                return -ENOMEM;
        }
  
        NXWR32(adapter, CRB_HOST_DUMMY_BUF_ADDR_HI, hi);
        NXWR32(adapter, CRB_HOST_DUMMY_BUF_ADDR_LO, lo);
  
 -      if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
 -              uint32_t temp = 0;
 -              NXWR32(adapter, CRB_HOST_DUMMY_BUF, temp);
 -      }
 -
        return 0;
  }
  
 -void netxen_free_adapter_offload(struct netxen_adapter *adapter)
 +/*
 + * NetXen DMA watchdog control:
 + *
 + *    Bit 0           : enabled => R/O: 1 watchdog active, 0 inactive
 + *    Bit 1           : disable_request => 1 req disable dma watchdog
 + *    Bit 2           : enable_request =>  1 req enable dma watchdog
 + *    Bit 3-31        : unused
 + */
 +void netxen_free_dummy_dma(struct netxen_adapter *adapter)
  {
        int i = 100;
 +      u32 ctrl;
 +
 +      if (!NX_IS_REVISION_P2(adapter->ahw.revision_id))
 +              return;
  
        if (!adapter->dummy_dma.addr)
                return;
  
 -      if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
 -              do {
 -                      if (dma_watchdog_shutdown_request(adapter) == 1)
 -                              break;
 +      ctrl = NXRD32(adapter, NETXEN_DMA_WATCHDOG_CTRL);
 +      if ((ctrl & 0x1) != 0) {
 +              NXWR32(adapter, NETXEN_DMA_WATCHDOG_CTRL, (ctrl | 0x2));
 +
 +              while ((ctrl & 0x1) != 0) {
 +
                        msleep(50);
 -                      if (dma_watchdog_shutdown_poll_result(adapter) == 1)
 +
 +                      ctrl = NXRD32(adapter, NETXEN_DMA_WATCHDOG_CTRL);
 +
 +                      if (--i == 0)
                                break;
 -              } while (--i);
 +              };
        }
  
        if (i) {
                            adapter->dummy_dma.addr,
                            adapter->dummy_dma.phys_addr);
                adapter->dummy_dma.addr = NULL;
 -      } else {
 -              printk(KERN_ERR "%s: dma_watchdog_shutdown failed\n",
 -                              adapter->netdev->name);
 -      }
 +      } else
 +              dev_err(&adapter->pdev->dev, "dma_watchdog_shutdown failed\n");
  }
  
  int netxen_phantom_init(struct netxen_adapter *adapter, int pegtune_val)
@@@ -1100,6 -1090,10 +1107,6 @@@ int netxen_init_firmware(struct netxen_
        NXWR32(adapter, CRB_MPORT_MODE, MPORT_MULTI_FUNCTION_MODE);
        NXWR32(adapter, CRB_CMDPEG_STATE, PHAN_INITIALIZE_ACK);
  
 -      if (adapter->fw_version >= NETXEN_VERSION_CODE(4, 0, 222)) {
 -              adapter->capabilities = NXRD32(adapter, CRB_FW_CAPABILITIES_1);
 -      }
 -
        return err;
  }
  
@@@ -1309,7 -1303,6 +1316,7 @@@ netxen_process_rcv_ring(struct nx_host_
                switch (opcode) {
                case NETXEN_NIC_RXPKT_DESC:
                case NETXEN_OLD_RXPKT_DESC:
 +              case NETXEN_NIC_SYN_OFFLOAD:
                        break;
                case NETXEN_NIC_RESPONSE_DESC:
                        netxen_handle_fw_message(desc_cnt, consumer, sds_ring);
@@@ -1482,7 -1475,7 +1489,7 @@@ netxen_post_rx_buffers(struct netxen_ad
                NXWR32(adapter, rds_ring->crb_rcv_producer,
                                (producer-1) & (rds_ring->num_desc-1));
  
 -              if (adapter->fw_major < 4) {
 +              if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
                        /*
                         * Write a doorbell msg to tell phanmon of change in
                         * receive ring producer
@@@ -1041,7 -1041,7 +1041,7 @@@ static int tok_send_packet(struct sk_bu
        writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
        spin_unlock_irqrestore(&(ti->lock), flags);
        dev->trans_start = jiffies;
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  /*****************************************************************************/
@@@ -1912,7 -1912,7 +1912,7 @@@ static int __init ibmtr_init(void
  
        find_turbo_adapters(io);
  
-       for (i = 0; io[i] && (i < IBMTR_MAX_ADAPTERS); i++) {
+       for (i = 0; i < IBMTR_MAX_ADAPTERS && io[i]; i++) {
                struct net_device *dev;
                irq[i] = 0;
                mem[i] = 0;
@@@ -1927,7 -1927,7 +1927,7 @@@ static int mpi_start_xmit(struct sk_buf
  
        if (!skb) {
                airo_print_err(dev->name, "%s: skb == NULL!",__func__);
 -              return 0;
 +              return NETDEV_TX_OK;
        }
        npacks = skb_queue_len (&ai->txq);
  
                        return NETDEV_TX_BUSY;
                }
                skb_queue_tail (&ai->txq, skb);
 -              return 0;
 +              return NETDEV_TX_OK;
        }
  
        spin_lock_irqsave(&ai->aux_lock, flags);
                set_bit(FLAG_PENDING_XMIT, &ai->flags);
                mpi_send_packet (dev);
        }
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  /*
@@@ -2127,7 -2127,7 +2127,7 @@@ static int airo_start_xmit(struct sk_bu
  
        if ( skb == NULL ) {
                airo_print_err(dev->name, "%s: skb == NULL!", __func__);
 -              return 0;
 +              return NETDEV_TX_OK;
        }
  
        /* Find a vacant FID */
                wake_up_interruptible(&priv->thr_wait);
        } else
                airo_end_xmit(dev);
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  static void airo_end_xmit11(struct net_device *dev) {
@@@ -2199,7 -2199,7 +2199,7 @@@ static int airo_start_xmit11(struct sk_
  
        if ( skb == NULL ) {
                airo_print_err(dev->name, "%s: skb == NULL!", __func__);
 -              return 0;
 +              return NETDEV_TX_OK;
        }
  
        /* Find a vacant FID */
                wake_up_interruptible(&priv->thr_wait);
        } else
                airo_end_xmit11(dev);
 -      return 0;
 +      return NETDEV_TX_OK;
  }
  
  static void airo_read_stats(struct net_device *dev)
@@@ -5918,20 -5918,19 +5918,19 @@@ static int airo_set_essid(struct net_de
        readSsidRid(local, &SSID_rid);
  
        /* Check if we asked for `any' */
-       if(dwrq->flags == 0) {
+       if (dwrq->flags == 0) {
                /* Just send an empty SSID list */
                memset(&SSID_rid, 0, sizeof(SSID_rid));
        } else {
-               int     index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+               unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
  
                /* Check the size of the string */
-               if(dwrq->length > IW_ESSID_MAX_SIZE) {
+               if (dwrq->length > IW_ESSID_MAX_SIZE)
                        return -E2BIG ;
-               }
                /* Check if index is valid */
-               if((index < 0) || (index >= 4)) {
+               if (index >= ARRAY_SIZE(SSID_rid.ssids))
                        return -EINVAL;
-               }
  
                /* Set the SSID */
                memset(SSID_rid.ssids[index].ssid, 0,
@@@ -6819,7 -6818,7 +6818,7 @@@ static int airo_set_txpow(struct net_de
                return -EINVAL;
        }
        clear_bit (FLAG_RADIO_OFF, &local->flags);
-       for (i = 0; cap_rid.txPowerLevels[i] && (i < 8); i++)
+       for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++)
                if (v == cap_rid.txPowerLevels[i]) {
                        readConfigRid(local, 1);
                        local->config.txPower = v;
@@@ -460,7 -460,7 +460,7 @@@ static int ath9k_hw_4k_check_eeprom(str
                integer = swab32(eep->modalHeader.antCtrlCommon);
                eep->modalHeader.antCtrlCommon = integer;
  
-               for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+               for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
                        integer = swab32(eep->modalHeader.antCtrlChain[i]);
                        eep->modalHeader.antCtrlChain[i] = integer;
                }
@@@ -914,7 -914,7 +914,7 @@@ static void ath9k_hw_set_4k_power_per_r
                        ctlMode, numCtlModes, isHt40CtlMode,
                        (pCtlMode[ctlMode] & EXT_ADDITIVE));
  
-               for (i = 0; (i < AR5416_NUM_CTLS) &&
+               for (i = 0; (i < AR5416_EEP4K_NUM_CTLS) &&
                                pEepData->ctlIndex[i]; i++) {
                        DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
                                "  LOOP-Ctlidx %d: cfgCtl 0x%2.2x "
@@@ -1208,19 -1208,6 +1208,19 @@@ static void ath9k_hw_4k_set_gain(struc
                              pModal->xatten2Margin[0]);
                REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
                              AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]);
 +
 +              /* Set the block 1 value to block 0 value */
 +              REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000,
 +                            AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
 +                            pModal->bswMargin[0]);
 +              REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000,
 +                            AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]);
 +              REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000,
 +                            AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
 +                            pModal->xatten2Margin[0]);
 +              REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000,
 +                            AR_PHY_GAIN_2GHZ_XATTEN2_DB,
 +                            pModal->xatten2Db[0]);
        }
  
        REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
        REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
                      AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
  
 +      REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000,
 +                    AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
 +      REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000,
 +                    AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
 +
        if (AR_SREV_9285_11(ah))
                REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14));
  }
@@@ -1257,7 -1239,7 +1257,7 @@@ static void ath9k_hw_4k_set_board_value
        ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal, 0);
  
        /* Initialize Ant Diversity settings from EEPROM */
 -      if (pModal->version == 3) {
 +      if (pModal->version >= 3) {
                ant_div_control1 = ((pModal->ob_234 >> 12) & 0xf);
                ant_div_control2 = ((pModal->db1_234 >> 12) & 0xf);
                regVal = REG_READ(ah, 0x99ac);
@@@ -2534,8 -2516,10 +2534,8 @@@ static void ath9k_hw_set_def_power_per_
                        targetPowerCck.tPow2x[1];
                ratesArray[rate5_5s] = ratesArray[rate5_5l] =
                        targetPowerCck.tPow2x[2];
 -              ;
                ratesArray[rate11s] = ratesArray[rate11l] =
                        targetPowerCck.tPow2x[3];
 -              ;
        }
        if (IS_CHAN_HT40(chan)) {
                for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) {
@@@ -348,10 -348,6 +348,10 @@@ int iwl_tx_queue_init(struct iwl_priv *
  
        txq->need_update = 0;
  
 +      /* aggregation TX queues will get their ID when aggregation begins */
 +      if (txq_id <= IWL_TX_FIFO_AC3)
 +              txq->swq_id = txq_id;
 +
        /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
         * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
        BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
@@@ -724,8 -720,6 +724,6 @@@ int iwl_tx_skb(struct iwl_priv *priv, s
                goto drop_unlock;
        }
  
-       spin_unlock_irqrestore(&priv->lock, flags);
        hdr_len = ieee80211_hdrlen(fc);
  
        /* Find (or create) index into station table for destination station */
        if (sta_id == IWL_INVALID_STATION) {
                IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
                               hdr->addr1);
-               goto drop;
+               goto drop_unlock;
        }
  
        IWL_DEBUG_TX(priv, "station Id %d\n", sta_id);
  
 -      swq_id = skb_get_queue_mapping(skb);
 -      txq_id = swq_id;
 +      txq_id = skb_get_queue_mapping(skb);
        if (ieee80211_is_data_qos(fc)) {
                qc = ieee80211_get_qos_ctl(hdr);
                tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
                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) {
 +              if (info->flags & IEEE80211_TX_CTL_AMPDU)
                        txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
-               priv->stations[sta_id].tid[tid].tfds_in_queue++;
 -                      swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id);
 -              }
        }
  
        txq = &priv->txq[txq_id];
 +      swq_id = txq->swq_id;
        q = &txq->q;
 -      txq->swq_id = swq_id;
  
-       spin_lock_irqsave(&priv->lock, flags);
+       if (unlikely(iwl_queue_space(q) < q->high_mark))
+               goto drop_unlock;
+       if (ieee80211_is_data_qos(fc))
+               priv->stations[sta_id].tid[tid].tfds_in_queue++;
  
        /* Set up driver data for this TFD */
        memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info));
        IWL_DEBUG_TX(priv, "sequence nr = 0X%x \n",
                     le16_to_cpu(out_cmd->hdr.sequence));
        IWL_DEBUG_TX(priv, "tx_flags = 0X%x \n", le32_to_cpu(tx_cmd->tx_flags));
 -      iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd));
 -      iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len);
 +      iwl_print_hex_dump(IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd));
 +      iwl_print_hex_dump(IWL_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)
  
  drop_unlock:
        spin_unlock_irqrestore(&priv->lock, flags);
- drop:
        return -1;
  }
  EXPORT_SYMBOL(iwl_tx_skb);
@@@ -940,7 -939,7 +940,7 @@@ int iwl_enqueue_hcmd(struct iwl_priv *p
               !(cmd->meta.flags & CMD_SIZE_HUGE));
  
        if (iwl_is_rfkill(priv)) {
 -              IWL_DEBUG_INFO(priv, "Not sending command - RF KILL");
 +              IWL_DEBUG_INFO(priv, "Not sending command - RF KILL\n");
                return -EIO;
        }
  
@@@ -1110,7 -1109,7 +1110,7 @@@ void iwl_tx_cmd_complete(struct iwl_pri
                  txq_id, sequence,
                  priv->txq[IWL_CMD_QUEUE_NUM].q.read_ptr,
                  priv->txq[IWL_CMD_QUEUE_NUM].q.write_ptr)) {
 -              iwl_print_hex_dump(priv, IWL_DL_INFO , rxb, 32);
 +              iwl_print_hex_error(priv, rxb, 32);
                return;
        }
  
@@@ -1172,6 -1171,8 +1172,8 @@@ int iwl_tx_agg_start(struct iwl_priv *p
                IWL_ERR(priv, "Start AGG on invalid station\n");
                return -ENXIO;
        }
+       if (unlikely(tid >= MAX_TID_COUNT))
+               return -EINVAL;
  
        if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_OFF) {
                IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n");
        tid_data = &priv->stations[sta_id].tid[tid];
        *ssn = SEQ_TO_SN(tid_data->seq_number);
        tid_data->agg.txq_id = txq_id;
 +      priv->txq[txq_id].swq_id = iwl_virtual_agg_queue_num(tx_fifo, txq_id);
        spin_unlock_irqrestore(&priv->sta_lock, flags);
  
        ret = priv->cfg->ops->lib->txq_agg_enable(priv, txq_id, tx_fifo,
  #include <linux/netdevice.h>
  
  #include "iwm.h"
 +#include "commands.h"
  #include "cfg80211.h"
  #include "debug.h"
  
  static int iwm_open(struct net_device *ndev)
  {
        struct iwm_priv *iwm = ndev_to_iwm(ndev);
 -      int ret = 0;
 -
 -      if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
 -              ret = iwm_up(iwm);
  
 -      return ret;
 +      return iwm_up(iwm);
  }
  
  static int iwm_stop(struct net_device *ndev)
  {
        struct iwm_priv *iwm = ndev_to_iwm(ndev);
 -      int ret = 0;
 -
 -      if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
 -              ret = iwm_down(iwm);
  
 -      return ret;
 +      return iwm_down(iwm);
  }
  
  /*
@@@ -99,10 -106,8 +99,8 @@@ void *iwm_if_alloc(int sizeof_bus, stru
        int ret = 0;
  
        wdev = iwm_wdev_alloc(sizeof_bus, dev);
-       if (!wdev) {
-               dev_err(dev, "no memory for wireless device instance\n");
-               return ERR_PTR(-ENOMEM);
-       }
+       if (IS_ERR(wdev))
+               return wdev;
  
        iwm = wdev_to_iwm(wdev);
        iwm->bus_ops = if_ops;
        SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
        wdev->netdev = ndev;
  
 +      iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile),
 +                                  GFP_KERNEL);
 +      if (!iwm->umac_profile) {
 +              dev_err(dev, "Couldn't alloc memory for profile\n");
 +              goto out_profile;
 +      }
 +
 +      iwm_init_default_profile(iwm, iwm->umac_profile);
 +
        return iwm;
  
 + out_profile:
 +      free_netdev(ndev);
 +
   out_priv:
        iwm_priv_deinit(iwm);
  
@@@ -157,8 -150,6 +155,8 @@@ void iwm_if_free(struct iwm_priv *iwm
  
        free_netdev(iwm_to_ndev(iwm));
        iwm_priv_deinit(iwm);
 +      kfree(iwm->umac_profile);
 +      iwm->umac_profile = NULL;
        iwm_wdev_free(iwm);
  }
  
@@@ -1,6 -1,7 +1,7 @@@
  /* Copyright (C) 2006, Red Hat, Inc. */
  
  #include <linux/types.h>
+ #include <linux/kernel.h>
  #include <linux/etherdevice.h>
  #include <linux/ieee80211.h>
  #include <linux/if_arp.h>
@@@ -43,21 -44,21 +44,21 @@@ static int get_common_rates(struct lbs_
        u16 *rates_size)
  {
        u8 *card_rates = lbs_bg_rates;
-       size_t num_card_rates = sizeof(lbs_bg_rates);
        int ret = 0, i, j;
-       u8 tmp[30];
+       u8 tmp[(ARRAY_SIZE(lbs_bg_rates) - 1) * (*rates_size - 1)];
        size_t tmp_size = 0;
  
        /* For each rate in card_rates that exists in rate1, copy to tmp */
-       for (i = 0; card_rates[i] && (i < num_card_rates); i++) {
-               for (j = 0; rates[j] && (j < *rates_size); j++) {
+       for (i = 0; i < ARRAY_SIZE(lbs_bg_rates) && card_rates[i]; i++) {
+               for (j = 0; j < *rates_size && rates[j]; j++) {
                        if (rates[j] == card_rates[i])
                                tmp[tmp_size++] = card_rates[i];
                }
        }
  
        lbs_deb_hex(LBS_DEB_JOIN, "AP rates    ", rates, *rates_size);
-       lbs_deb_hex(LBS_DEB_JOIN, "card rates  ", card_rates, num_card_rates);
+       lbs_deb_hex(LBS_DEB_JOIN, "card rates  ", card_rates,
+                       ARRAY_SIZE(lbs_bg_rates));
        lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size);
        lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate);
  
                lbs_pr_alert("Previously set fixed data rate %#x isn't "
                       "compatible with the network.\n", priv->cur_rate);
                ret = -1;
-               goto done;
        }
-       ret = 0;
  done:
        memset(rates, 0, *rates_size);
        *rates_size = min_t(int, tmp_size, *rates_size);
@@@ -129,6 -127,7 +127,6 @@@ static int lbs_set_authentication(struc
  {
        struct cmd_ds_802_11_authenticate cmd;
        int ret = -1;
 -      DECLARE_MAC_BUF(mac);
  
        lbs_deb_enter(LBS_DEB_JOIN);
  
  
        cmd.authtype = iw_auth_to_ieee_auth(auth);
  
 -      lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n",
 -              print_mac(mac, bssid), cmd.authtype);
 +      lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n", bssid, cmd.authtype);
  
        ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd);
  
@@@ -320,7 -320,7 +318,7 @@@ static int lbs_associate(struct lbs_pri
        rates = (struct mrvl_ie_rates_param_set *) pos;
        rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
        memcpy(&rates->rates, &bss->rates, MAX_RATES);
-       tmplen = MAX_RATES;
+       tmplen = min_t(u16, ARRAY_SIZE(rates->rates), MAX_RATES);
        if (get_common_rates(priv, rates->rates, &tmplen)) {
                ret = -1;
                goto done;
  
        /* Firmware v9+ indicate authentication suites as a TLV */
        if (priv->fwrelease >= 0x09000000) {
 -              DECLARE_MAC_BUF(mac);
 -
                auth = (struct mrvl_ie_auth_type *) pos;
                auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
                auth->header.len = cpu_to_le16(2);
                auth->auth = cpu_to_le16(tmpauth);
                pos += sizeof(auth->header) + 2;
  
 -              lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n",
 -                      print_mac(mac, bss->bssid), priv->secinfo.auth_mode);
 +              lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n",
 +                      bss->bssid, priv->secinfo.auth_mode);
        }
  
        /* WPA/WPA2 IEs */
@@@ -594,7 -596,7 +592,7 @@@ static int lbs_adhoc_join(struct lbs_pr
  
        /* Copy Data rates from the rates recorded in scan response */
        memset(cmd.bss.rates, 0, sizeof(cmd.bss.rates));
-       ratesize = min_t(u16, sizeof(cmd.bss.rates), MAX_RATES);
+       ratesize = min_t(u16, ARRAY_SIZE(cmd.bss.rates), MAX_RATES);
        memcpy(cmd.bss.rates, bss->rates, ratesize);
        if (get_common_rates(priv, cmd.bss.rates, &ratesize)) {
                lbs_deb_join("ADHOC_JOIN: get_common_rates returned error.\n");
@@@ -1364,17 -1366,11 +1362,17 @@@ static int assoc_helper_wpa_keys(struc
        if (ret)
                goto out;
  
 +      memcpy(&priv->wpa_unicast_key, &assoc_req->wpa_unicast_key,
 +                      sizeof(struct enc_key));
 +
        if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
                clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
  
                ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
                assoc_req->flags = flags;
 +
 +              memcpy(&priv->wpa_mcast_key, &assoc_req->wpa_mcast_key,
 +                              sizeof(struct enc_key));
        }
  
  out:
diff --combined net/mac80211/mlme.c
  #include "rate.h"
  #include "led.h"
  
 -#define IEEE80211_ASSOC_SCANS_MAX_TRIES 2
  #define IEEE80211_AUTH_TIMEOUT (HZ / 5)
  #define IEEE80211_AUTH_MAX_TRIES 3
  #define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
  #define IEEE80211_ASSOC_MAX_TRIES 3
 -#define IEEE80211_MONITORING_INTERVAL (2 * HZ)
 -#define IEEE80211_PROBE_WAIT (HZ / 5)
 -#define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
 -#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
 +
 +/*
 + * beacon loss detection timeout
 + * XXX: should depend on beacon interval
 + */
 +#define IEEE80211_BEACON_LOSS_TIME    (2 * HZ)
 +/*
 + * Time the connection can be idle before we probe
 + * it to see if we can still talk to the AP.
 + */
 +#define IEEE80211_CONNECTION_IDLE_TIME        (2 * HZ)
 +/*
 + * Time we wait for a probe response after sending
 + * a probe request because of beacon loss or for
 + * checking the connection still works.
 + */
 +#define IEEE80211_PROBE_WAIT          (HZ / 5)
  
  #define TMR_RUNNING_TIMER     0
  #define TMR_RUNNING_CHANSW    1
  
 +/*
 + * All cfg80211 functions have to be called outside a locked
 + * section so that they can acquire a lock themselves... This
 + * is much simpler than queuing up things in cfg80211, but we
 + * do need some indirection for that here.
 + */
 +enum rx_mgmt_action {
 +      /* no action required */
 +      RX_MGMT_NONE,
 +
 +      /* caller must call cfg80211_send_rx_auth() */
 +      RX_MGMT_CFG80211_AUTH,
 +
 +      /* caller must call cfg80211_send_rx_assoc() */
 +      RX_MGMT_CFG80211_ASSOC,
 +
 +      /* caller must call cfg80211_send_deauth() */
 +      RX_MGMT_CFG80211_DEAUTH,
 +
 +      /* caller must call cfg80211_send_disassoc() */
 +      RX_MGMT_CFG80211_DISASSOC,
 +
 +      /* caller must call cfg80211_auth_timeout() & free work */
 +      RX_MGMT_CFG80211_AUTH_TO,
 +
 +      /* caller must call cfg80211_assoc_timeout() & free work */
 +      RX_MGMT_CFG80211_ASSOC_TO,
 +};
 +
  /* utils */
 -static int ecw2cw(int ecw)
 +static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
  {
 -      return (1 << ecw) - 1;
 +      WARN_ON(!mutex_is_locked(&ifmgd->mtx));
  }
  
 -static u8 *ieee80211_bss_get_ie(struct ieee80211_bss *bss, u8 ie)
 +/*
 + * We can have multiple work items (and connection probing)
 + * scheduling this timer, but we need to take care to only
 + * reschedule it when it should fire _earlier_ than it was
 + * asked for before, or if it's not pending right now. This
 + * function ensures that. Note that it then is required to
 + * run this function for all timeouts after the first one
 + * has happened -- the work that runs from this timer will
 + * do that.
 + */
 +static void run_again(struct ieee80211_if_managed *ifmgd,
 +                           unsigned long timeout)
  {
 -      u8 *end, *pos;
 +      ASSERT_MGD_MTX(ifmgd);
  
 -      pos = bss->cbss.information_elements;
 -      if (pos == NULL)
 -              return NULL;
 -      end = pos + bss->cbss.len_information_elements;
 +      if (!timer_pending(&ifmgd->timer) ||
 +          time_before(timeout, ifmgd->timer.expires))
 +              mod_timer(&ifmgd->timer, timeout);
 +}
  
 -      while (pos + 1 < end) {
 -              if (pos + 2 + pos[1] > end)
 -                      break;
 -              if (pos[0] == ie)
 -                      return pos;
 -              pos += 2 + pos[1];
 -      }
 +static void mod_beacon_timer(struct ieee80211_sub_if_data *sdata)
 +{
 +      if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER)
 +              return;
 +
 +      mod_timer(&sdata->u.mgd.bcn_mon_timer,
 +                round_jiffies_up(jiffies + IEEE80211_BEACON_LOSS_TIME));
 +}
  
 -      return NULL;
 +static int ecw2cw(int ecw)
 +{
 +      return (1 << ecw) - 1;
  }
  
  static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
   */
  static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
                               struct ieee80211_ht_info *hti,
 -                             u16 ap_ht_cap_flags)
 +                             const u8 *bssid, u16 ap_ht_cap_flags)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct sta_info *sta;
        u32 changed = 0;
        u16 ht_opmode;
                ieee80211_hw_config(local, 0);
  
                rcu_read_lock();
 -
 -              sta = sta_info_get(local, ifmgd->bssid);
 +              sta = sta_info_get(local, bssid);
                if (sta)
                        rate_control_rate_update(local, sband, sta,
                                                 IEEE80211_RC_HT_CHANGED);
 -
                rcu_read_unlock();
          }
  
  
  /* frame sending functions */
  
 -static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 +static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
 +                               struct ieee80211_mgd_work *wk)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
 -      u8 *pos, *ies, *ht_ie;
 +      u8 *pos;
 +      const u8 *ies, *ht_ie;
        int i, len, count, rates_len, supp_rates_len;
        u16 capab;
 -      struct ieee80211_bss *bss;
        int wmm = 0;
        struct ieee80211_supported_band *sband;
        u32 rates = 0;
  
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
 -                          sizeof(*mgmt) + 200 + ifmgd->extra_ie_len +
 -                          ifmgd->ssid_len);
 +                          sizeof(*mgmt) + 200 + wk->ie_len +
 +                          wk->ssid_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
                       "frame\n", sdata->dev->name);
                        capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
        }
  
 -      bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
 -                                 local->hw.conf.channel->center_freq,
 -                                 ifmgd->ssid, ifmgd->ssid_len);
 -      if (bss) {
 -              if (bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
 -                      capab |= WLAN_CAPABILITY_PRIVACY;
 -              if (bss->wmm_used)
 -                      wmm = 1;
 -
 -              /* get all rates supported by the device and the AP as
 -               * some APs don't like getting a superset of their rates
 -               * in the association request (e.g. D-Link DAP 1353 in
 -               * b-only mode) */
 -              rates_len = ieee80211_compatible_rates(bss, sband, &rates);
 +      if (wk->bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
 +              capab |= WLAN_CAPABILITY_PRIVACY;
 +      if (wk->bss->wmm_used)
 +              wmm = 1;
  
 -              if ((bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
 -                  (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
 -                      capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 +      /* get all rates supported by the device and the AP as
 +       * some APs don't like getting a superset of their rates
 +       * in the association request (e.g. D-Link DAP 1353 in
 +       * b-only mode) */
 +      rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
  
 -              ieee80211_rx_bss_put(local, bss);
 -      } else {
 -              rates = ~0;
 -              rates_len = sband->n_bitrates;
 -      }
 +      if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
 +          (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
 +              capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
  
        mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
        memset(mgmt, 0, 24);
 -      memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN);
 +      memcpy(mgmt->da, wk->bss->cbss.bssid, ETH_ALEN);
        memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
 -      memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN);
 +      memcpy(mgmt->bssid, wk->bss->cbss.bssid, ETH_ALEN);
  
 -      if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) {
 +      if (!is_zero_ether_addr(wk->prev_bssid)) {
                skb_put(skb, 10);
                mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                                  IEEE80211_STYPE_REASSOC_REQ);
                mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
                mgmt->u.reassoc_req.listen_interval =
                                cpu_to_le16(local->hw.conf.listen_interval);
 -              memcpy(mgmt->u.reassoc_req.current_ap, ifmgd->prev_bssid,
 +              memcpy(mgmt->u.reassoc_req.current_ap, wk->prev_bssid,
                       ETH_ALEN);
        } else {
                skb_put(skb, 4);
        }
  
        /* SSID */
 -      ies = pos = skb_put(skb, 2 + ifmgd->ssid_len);
 +      ies = pos = skb_put(skb, 2 + wk->ssid_len);
        *pos++ = WLAN_EID_SSID;
 -      *pos++ = ifmgd->ssid_len;
 -      memcpy(pos, ifmgd->ssid, ifmgd->ssid_len);
 +      *pos++ = wk->ssid_len;
 +      memcpy(pos, wk->ssid, wk->ssid_len);
  
        /* add all rates which were marked to be used above */
        supp_rates_len = rates_len;
                }
        }
  
 -      if (ifmgd->extra_ie) {
 -              pos = skb_put(skb, ifmgd->extra_ie_len);
 -              memcpy(pos, ifmgd->extra_ie, ifmgd->extra_ie_len);
 +      if (wk->ie_len && wk->ie) {
 +              pos = skb_put(skb, wk->ie_len);
 +              memcpy(pos, wk->ie, wk->ie_len);
        }
  
        if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) {
         */
        if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
            sband->ht_cap.ht_supported &&
 -          (ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) &&
 +          (ht_ie = ieee80211_bss_get_ie(&wk->bss->cbss, WLAN_EID_HT_INFORMATION)) &&
            ht_ie[1] >= sizeof(struct ieee80211_ht_info) &&
 -          (!(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))) {
 +          (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) {
                struct ieee80211_ht_info *ht_info =
                        (struct ieee80211_ht_info *)(ht_ie + 2);
                u16 cap = sband->ht_cap.cap;
                memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
        }
  
 -      kfree(ifmgd->assocreq_ies);
 -      ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies;
 -      ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL);
 -      if (ifmgd->assocreq_ies)
 -              memcpy(ifmgd->assocreq_ies, ies, ifmgd->assocreq_ies_len);
 -
        ieee80211_tx_skb(sdata, skb, 0);
  }
  
  
  static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 -                                         u16 stype, u16 reason)
 +                                         const u8 *bssid, u16 stype, u16 reason,
 +                                         void *cookie)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
        mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
        memset(mgmt, 0, 24);
 -      memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN);
 +      memcpy(mgmt->da, bssid, ETH_ALEN);
        memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
 -      memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN);
 +      memcpy(mgmt->bssid, bssid, ETH_ALEN);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
        skb_put(skb, 2);
        /* u.deauth.reason_code == u.disassoc.reason_code */
        mgmt->u.deauth.reason_code = cpu_to_le16(reason);
  
        if (stype == IEEE80211_STYPE_DEAUTH)
 -              cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len);
 +              cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, cookie);
        else
 -              cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len);
 +              cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, cookie);
        ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED);
  }
  
@@@ -532,26 -494,28 +532,26 @@@ static void ieee80211_chswitch_work(str
  {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
 -      struct ieee80211_bss *bss;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
        if (!netif_running(sdata->dev))
                return;
  
 -      bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
 -                                 sdata->local->hw.conf.channel->center_freq,
 -                                 ifmgd->ssid, ifmgd->ssid_len);
 -      if (!bss)
 -              goto exit;
 +      mutex_lock(&ifmgd->mtx);
 +      if (!ifmgd->associated)
 +              goto out;
  
        sdata->local->oper_channel = sdata->local->csa_channel;
 +      ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL);
 +
        /* XXX: shouldn't really modify cfg80211-owned data! */
 -      if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
 -              bss->cbss.channel = sdata->local->oper_channel;
 +      ifmgd->associated->cbss.channel = sdata->local->oper_channel;
  
 -      ieee80211_rx_bss_put(sdata->local, bss);
 -exit:
 -      ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 + out:
 +      ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
 +      mutex_unlock(&ifmgd->mtx);
  }
  
  static void ieee80211_chswitch_timer(unsigned long data)
@@@ -576,9 -540,7 +576,9 @@@ void ieee80211_sta_process_chanswitch(s
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
  
 -      if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
 +      ASSERT_MGD_MTX(ifmgd);
 +
 +      if (!ifmgd->associated)
                return;
  
        if (sdata->local->sw_scanning || sdata->local->hw_scanning)
@@@ -689,9 -651,8 +689,9 @@@ void ieee80211_recalc_ps(struct ieee802
        }
  
        if (count == 1 && found->u.mgd.powersave &&
 -          (found->u.mgd.flags & IEEE80211_STA_ASSOCIATED) &&
 -          !(found->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL)) {
 +          found->u.mgd.associated && list_empty(&found->u.mgd.work_list) &&
 +          !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
 +                                  IEEE80211_STA_CONNECTION_POLL))) {
                s32 beaconint_us;
  
                if (latency < 0)
@@@ -760,7 -721,7 +760,7 @@@ void ieee80211_dynamic_ps_timer(unsigne
  {
        struct ieee80211_local *local = (void *) data;
  
-       if (local->quiescing)
+       if (local->quiescing || local->suspended)
                return;
  
        queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
@@@ -845,6 -806,9 +845,6 @@@ static u32 ieee80211_handle_bss_capabil
                                           u16 capab, bool erp_valid, u8 erp)
  {
        struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
 -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -#endif
        u32 changed = 0;
        bool use_protection;
        bool use_short_preamble;
        use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
  
        if (use_protection != bss_conf->use_cts_prot) {
 -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 -              if (net_ratelimit()) {
 -                      printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n",
 -                             sdata->dev->name,
 -                             use_protection ? "enabled" : "disabled",
 -                             ifmgd->bssid);
 -              }
 -#endif
                bss_conf->use_cts_prot = use_protection;
                changed |= BSS_CHANGED_ERP_CTS_PROT;
        }
  
        if (use_short_preamble != bss_conf->use_short_preamble) {
 -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 -              if (net_ratelimit()) {
 -                      printk(KERN_DEBUG "%s: switched to %s barker preamble"
 -                             " (BSSID=%pM)\n",
 -                             sdata->dev->name,
 -                             use_short_preamble ? "short" : "long",
 -                             ifmgd->bssid);
 -              }
 -#endif
                bss_conf->use_short_preamble = use_short_preamble;
                changed |= BSS_CHANGED_ERP_PREAMBLE;
        }
  
        if (use_short_slot != bss_conf->use_short_slot) {
 -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 -              if (net_ratelimit()) {
 -                      printk(KERN_DEBUG "%s: switched to %s slot time"
 -                             " (BSSID=%pM)\n",
 -                             sdata->dev->name,
 -                             use_short_slot ? "short" : "long",
 -                             ifmgd->bssid);
 -              }
 -#endif
                bss_conf->use_short_slot = use_short_slot;
                changed |= BSS_CHANGED_ERP_SLOT;
        }
        return changed;
  }
  
 -static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata)
 -{
 -      union iwreq_data wrqu;
 -
 -      memset(&wrqu, 0, sizeof(wrqu));
 -      if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED)
 -              memcpy(wrqu.ap_addr.sa_data, sdata->u.mgd.bssid, ETH_ALEN);
 -      wrqu.ap_addr.sa_family = ARPHRD_ETHER;
 -      wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
 -}
 -
 -static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata)
 -{
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -      char *buf;
 -      size_t len;
 -      int i;
 -      union iwreq_data wrqu;
 -
 -      if (!ifmgd->assocreq_ies && !ifmgd->assocresp_ies)
 -              return;
 -
 -      buf = kmalloc(50 + 2 * (ifmgd->assocreq_ies_len +
 -                              ifmgd->assocresp_ies_len), GFP_KERNEL);
 -      if (!buf)
 -              return;
 -
 -      len = sprintf(buf, "ASSOCINFO(");
 -      if (ifmgd->assocreq_ies) {
 -              len += sprintf(buf + len, "ReqIEs=");
 -              for (i = 0; i < ifmgd->assocreq_ies_len; i++) {
 -                      len += sprintf(buf + len, "%02x",
 -                                     ifmgd->assocreq_ies[i]);
 -              }
 -      }
 -      if (ifmgd->assocresp_ies) {
 -              if (ifmgd->assocreq_ies)
 -                      len += sprintf(buf + len, " ");
 -              len += sprintf(buf + len, "RespIEs=");
 -              for (i = 0; i < ifmgd->assocresp_ies_len; i++) {
 -                      len += sprintf(buf + len, "%02x",
 -                                     ifmgd->assocresp_ies[i]);
 -              }
 -      }
 -      len += sprintf(buf + len, ")");
 -
 -      if (len > IW_CUSTOM_MAX) {
 -              len = sprintf(buf, "ASSOCRESPIE=");
 -              for (i = 0; i < ifmgd->assocresp_ies_len; i++) {
 -                      len += sprintf(buf + len, "%02x",
 -                                     ifmgd->assocresp_ies[i]);
 -              }
 -      }
 -
 -      if (len <= IW_CUSTOM_MAX) {
 -              memset(&wrqu, 0, sizeof(wrqu));
 -              wrqu.data.length = len;
 -              wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
 -      }
 -
 -      kfree(buf);
 -}
 -
 -
  static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 +                                   struct ieee80211_bss *bss,
                                     u32 bss_info_changed)
  {
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
 -      struct ieee80211_conf *conf = &local_to_hw(local)->conf;
 -
 -      struct ieee80211_bss *bss;
  
        bss_info_changed |= BSS_CHANGED_ASSOC;
 -      ifmgd->flags |= IEEE80211_STA_ASSOCIATED;
 -
 -      bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
 -                                 conf->channel->center_freq,
 -                                 ifmgd->ssid, ifmgd->ssid_len);
 -      if (bss) {
 -              /* set timing information */
 -              sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
 -              sdata->vif.bss_conf.timestamp = bss->cbss.tsf;
 -              sdata->vif.bss_conf.dtim_period = bss->dtim_period;
 +      /* set timing information */
 +      sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
 +      sdata->vif.bss_conf.timestamp = bss->cbss.tsf;
 +      sdata->vif.bss_conf.dtim_period = bss->dtim_period;
  
 -              bss_info_changed |= BSS_CHANGED_BEACON_INT;
 -              bss_info_changed |= ieee80211_handle_bss_capability(sdata,
 -                      bss->cbss.capability, bss->has_erp_value, bss->erp_value);
 +      bss_info_changed |= BSS_CHANGED_BEACON_INT;
 +      bss_info_changed |= ieee80211_handle_bss_capability(sdata,
 +              bss->cbss.capability, bss->has_erp_value, bss->erp_value);
  
 -              cfg80211_hold_bss(&bss->cbss);
 -
 -              ieee80211_rx_bss_put(local, bss);
 -      }
 +      sdata->u.mgd.associated = bss;
 +      memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN);
  
 -      ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET;
 -      memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN);
 -      ieee80211_sta_send_associnfo(sdata);
 +      /* just to be sure */
 +      sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
 +                              IEEE80211_STA_BEACON_POLL);
  
 -      ifmgd->last_probe = jiffies;
        ieee80211_led_assoc(local, 1);
  
        sdata->vif.bss_conf.assoc = 1;
  
        netif_tx_start_all_queues(sdata->dev);
        netif_carrier_on(sdata->dev);
 -
 -      ieee80211_sta_send_apinfo(sdata);
  }
  
 -static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
 +static enum rx_mgmt_action __must_check
 +ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
 +                     struct ieee80211_mgd_work *wk)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
  
 -      ifmgd->direct_probe_tries++;
 -      if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
 +      wk->tries++;
 +      if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
                printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
 -                     sdata->dev->name, ifmgd->bssid);
 -              ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 -              ieee80211_recalc_idle(local);
 -              cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid);
 +                     sdata->dev->name, wk->bss->cbss.bssid);
  
                /*
                 * Most likely AP is not in the range so remove the
 -               * bss information associated to the AP
 +               * bss struct for that AP.
                 */
 -              ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
 -                              sdata->local->hw.conf.channel->center_freq,
 -                              ifmgd->ssid, ifmgd->ssid_len);
 +              cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
  
                /*
                 * We might have a pending scan which had no chance to run yet
 -               * due to state == IEEE80211_STA_MLME_DIRECT_PROBE.
 -               * Hence, queue the STAs work again
 +               * due to work needing to be done. Hence, queue the STAs work
 +               * again for that.
                 */
                queue_work(local->hw.workqueue, &ifmgd->work);
 -              return;
 +              return RX_MGMT_CFG80211_AUTH_TO;
        }
  
 -      printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n",
 -                      sdata->dev->name, ifmgd->bssid,
 -                      ifmgd->direct_probe_tries);
 -
 -      ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
 +      printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
 +                      sdata->dev->name, wk->bss->cbss.bssid,
 +                      wk->tries);
  
 -      /* Direct probe is sent to broadcast address as some APs
 +      /*
 +       * Direct probe is sent to broadcast address as some APs
         * will not answer to direct packet in unassociated state.
         */
 -      ieee80211_send_probe_req(sdata, NULL,
 -                               ifmgd->ssid, ifmgd->ssid_len, NULL, 0);
 +      ieee80211_send_probe_req(sdata, NULL, wk->ssid, wk->ssid_len, NULL, 0);
 +
 +      wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
 +      run_again(ifmgd, wk->timeout);
  
 -      mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
 +      return RX_MGMT_NONE;
  }
  
  
 -static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
 +static enum rx_mgmt_action __must_check
 +ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
 +                     struct ieee80211_mgd_work *wk)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
 -      u8 *ies;
 -      size_t ies_len;
  
 -      ifmgd->auth_tries++;
 -      if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
 +      wk->tries++;
 +      if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
                printk(KERN_DEBUG "%s: authentication with AP %pM"
                       " timed out\n",
 -                     sdata->dev->name, ifmgd->bssid);
 -              ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 -              ieee80211_recalc_idle(local);
 -              cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid);
 -              ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
 -                              sdata->local->hw.conf.channel->center_freq,
 -                              ifmgd->ssid, ifmgd->ssid_len);
 +                     sdata->dev->name, wk->bss->cbss.bssid);
 +
 +              /*
 +               * Most likely AP is not in the range so remove the
 +               * bss struct for that AP.
 +               */
 +              cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
  
                /*
                 * We might have a pending scan which had no chance to run yet
 -               * due to state == IEEE80211_STA_MLME_AUTHENTICATE.
 -               * Hence, queue the STAs work again
 +               * due to work needing to be done. Hence, queue the STAs work
 +               * again for that.
                 */
                queue_work(local->hw.workqueue, &ifmgd->work);
 -              return;
 +              return RX_MGMT_CFG80211_AUTH_TO;
        }
  
 -      ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
 -      printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
 -             sdata->dev->name, ifmgd->bssid);
 +      printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
 +             sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
  
 -      if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
 -              ies = ifmgd->sme_auth_ie;
 -              ies_len = ifmgd->sme_auth_ie_len;
 -      } else {
 -              ies = NULL;
 -              ies_len = 0;
 -      }
 -      ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ies, ies_len,
 -                          ifmgd->bssid, 0);
 -      ifmgd->auth_transaction = 2;
 +      ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len,
 +                          wk->bss->cbss.bssid, NULL, 0, 0);
 +      wk->auth_transaction = 2;
 +
 +      wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
 +      run_again(ifmgd, wk->timeout);
  
 -      mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
 +      return RX_MGMT_NONE;
  }
  
 -/*
 - * The disassoc 'reason' argument can be either our own reason
 - * if self disconnected or a reason code from the AP.
 - */
 -static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 -                                 bool deauth, bool self_disconnected,
 -                                 u16 reason)
 +static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
 -      struct ieee80211_conf *conf = &local_to_hw(local)->conf;
 -      struct ieee80211_bss *bss;
        struct sta_info *sta;
        u32 changed = 0, config_changed = 0;
 +      u8 bssid[ETH_ALEN];
  
 -      if (deauth) {
 -              ifmgd->direct_probe_tries = 0;
 -              ifmgd->auth_tries = 0;
 -      }
 -      ifmgd->assoc_scan_tries = 0;
 -      ifmgd->assoc_tries = 0;
 +      ASSERT_MGD_MTX(ifmgd);
 +
 +      if (WARN_ON(!ifmgd->associated))
 +              return;
 +
 +      memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN);
 +
 +      ifmgd->associated = NULL;
 +      memset(ifmgd->bssid, 0, ETH_ALEN);
 +
 +      /*
 +       * we need to commit the associated = NULL change because the
 +       * scan code uses that to determine whether this iface should
 +       * go to/wake up from powersave or not -- and could otherwise
 +       * wake the queues erroneously.
 +       */
 +      smp_mb();
 +
 +      /*
 +       * Thus, we can only afterwards stop the queues -- to account
 +       * for the case where another CPU is finishing a scan at this
 +       * time -- we don't want the scan code to enable queues.
 +       */
  
        netif_tx_stop_all_queues(sdata->dev);
        netif_carrier_off(sdata->dev);
  
        rcu_read_lock();
 -      sta = sta_info_get(local, ifmgd->bssid);
 +      sta = sta_info_get(local, bssid);
        if (sta)
                ieee80211_sta_tear_down_BA_sessions(sta);
        rcu_read_unlock();
  
 -      bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
 -                                 conf->channel->center_freq,
 -                                 ifmgd->ssid, ifmgd->ssid_len);
 -
 -      if (bss) {
 -              cfg80211_unhold_bss(&bss->cbss);
 -              ieee80211_rx_bss_put(local, bss);
 -      }
 -
 -      if (self_disconnected) {
 -              if (deauth)
 -                      ieee80211_send_deauth_disassoc(sdata,
 -                              IEEE80211_STYPE_DEAUTH, reason);
 -              else
 -                      ieee80211_send_deauth_disassoc(sdata,
 -                              IEEE80211_STYPE_DISASSOC, reason);
 -      }
 -
 -      ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED;
        changed |= ieee80211_reset_erp_info(sdata);
  
        ieee80211_led_assoc(local, 0);
        changed |= BSS_CHANGED_ASSOC;
        sdata->vif.bss_conf.assoc = false;
  
 -      ieee80211_sta_send_apinfo(sdata);
 -
 -      if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) {
 -              ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 -              ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
 -                              sdata->local->hw.conf.channel->center_freq,
 -                              ifmgd->ssid, ifmgd->ssid_len);
 -      }
 -
        ieee80211_set_wmm_default(sdata);
  
        ieee80211_recalc_idle(local);
  
        rcu_read_lock();
  
 -      sta = sta_info_get(local, ifmgd->bssid);
 +      sta = sta_info_get(local, bssid);
        if (!sta) {
                rcu_read_unlock();
                return;
        sta_info_destroy(sta);
  }
  
 -static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata)
 -{
 -      if (!sdata || !sdata->default_key ||
 -          sdata->default_key->conf.alg != ALG_WEP)
 -              return 0;
 -      return 1;
 -}
 -
 -static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata)
 -{
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -      struct ieee80211_local *local = sdata->local;
 -      struct ieee80211_bss *bss;
 -      int bss_privacy;
 -      int wep_privacy;
 -      int privacy_invoked;
 -
 -      if (!ifmgd || (ifmgd->flags & IEEE80211_STA_EXT_SME))
 -              return 0;
 -
 -      bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
 -                                 local->hw.conf.channel->center_freq,
 -                                 ifmgd->ssid, ifmgd->ssid_len);
 -      if (!bss)
 -              return 0;
 -
 -      bss_privacy = !!(bss->cbss.capability & WLAN_CAPABILITY_PRIVACY);
 -      wep_privacy = !!ieee80211_sta_wep_configured(sdata);
 -      privacy_invoked = !!(ifmgd->flags & IEEE80211_STA_PRIVACY_INVOKED);
 -
 -      ieee80211_rx_bss_put(local, bss);
 -
 -      if ((bss_privacy == wep_privacy) || (bss_privacy == privacy_invoked))
 -              return 0;
 -
 -      return 1;
 -}
 -
 -static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
 +static enum rx_mgmt_action __must_check
 +ieee80211_associate(struct ieee80211_sub_if_data *sdata,
 +                  struct ieee80211_mgd_work *wk)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
  
 -      ifmgd->assoc_tries++;
 -      if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
 +      wk->tries++;
 +      if (wk->tries > IEEE80211_ASSOC_MAX_TRIES) {
                printk(KERN_DEBUG "%s: association with AP %pM"
                       " timed out\n",
 -                     sdata->dev->name, ifmgd->bssid);
 -              ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 -              ieee80211_recalc_idle(local);
 -              cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid);
 -              ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
 -                              sdata->local->hw.conf.channel->center_freq,
 -                              ifmgd->ssid, ifmgd->ssid_len);
 +                     sdata->dev->name, wk->bss->cbss.bssid);
 +
 +              /*
 +               * Most likely AP is not in the range so remove the
 +               * bss struct for that AP.
 +               */
 +              cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
 +
                /*
                 * We might have a pending scan which had no chance to run yet
 -               * due to state == IEEE80211_STA_MLME_ASSOCIATE.
 -               * Hence, queue the STAs work again
 +               * due to work needing to be done. Hence, queue the STAs work
 +               * again for that.
                 */
                queue_work(local->hw.workqueue, &ifmgd->work);
 -              return;
 +              return RX_MGMT_CFG80211_ASSOC_TO;
        }
  
 -      ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
 -      printk(KERN_DEBUG "%s: associate with AP %pM\n",
 -             sdata->dev->name, ifmgd->bssid);
 -      if (ieee80211_privacy_mismatch(sdata)) {
 -              printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
 -                     "mixed-cell disabled - abort association\n", sdata->dev->name);
 -              ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 -              ieee80211_recalc_idle(local);
 -              return;
 -      }
 +      printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
 +             sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
 +      ieee80211_send_assoc(sdata, wk);
  
 -      ieee80211_send_assoc(sdata);
 +      wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
 +      run_again(ifmgd, wk->timeout);
  
 -      mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
 +      return RX_MGMT_NONE;
  }
  
  void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
         * from AP because we know that the connection is working both ways
         * at that time. But multicast frames (and hence also beacons) must
         * be ignored here, because we need to trigger the timer during
 -       * data idle periods for sending the periodical probe request to
 -       * the AP.
 +       * data idle periods for sending the periodic probe request to the
 +       * AP we're connected to.
         */
 -      if (!is_multicast_ether_addr(hdr->addr1))
 -              mod_timer(&sdata->u.mgd.timer,
 -                        jiffies + IEEE80211_MONITORING_INTERVAL);
 +      if (is_multicast_ether_addr(hdr->addr1))
 +              return;
 +
 +      mod_timer(&sdata->u.mgd.conn_mon_timer,
 +                round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
  }
  
 -void ieee80211_beacon_loss_work(struct work_struct *work)
 +static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
 +                                 bool beacon)
  {
 -      struct ieee80211_sub_if_data *sdata =
 -              container_of(work, struct ieee80211_sub_if_data,
 -                           u.mgd.beacon_loss_work);
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      const u8 *ssid;
 +      bool already = false;
  
 -      /*
 -       * The driver has already reported this event and we have
 -       * already sent a probe request. Maybe the AP died and the
 -       * driver keeps reporting until we disassociate... We have
 -       * to ignore that because otherwise we would continually
 -       * reset the timer and never check whether we received a
 -       * probe response!
 -       */
 -      if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL)
 +      if (!netif_running(sdata->dev))
                return;
  
 +      mutex_lock(&ifmgd->mtx);
 +
 +      if (!ifmgd->associated)
 +              goto out;
 +
  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 -      if (net_ratelimit()) {
 -              printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM "
 -                     "- sending probe request\n", sdata->dev->name,
 -                     sdata->u.mgd.bssid);
 -      }
 +      if (beacon && net_ratelimit())
 +              printk(KERN_DEBUG "%s: detected beacon loss from AP "
 +                     "- sending probe request\n", sdata->dev->name);
  #endif
  
 -      ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
 +      /*
 +       * The driver/our work has already reported this event or the
 +       * connection monitoring has kicked in and we have already sent
 +       * a probe request. Or maybe the AP died and the driver keeps
 +       * reporting until we disassociate...
 +       *
 +       * In either case we have to ignore the current call to this
 +       * function (except for setting the correct probe reason bit)
 +       * because otherwise we would reset the timer every time and
 +       * never check whether we received a probe response!
 +       */
 +      if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
 +                          IEEE80211_STA_CONNECTION_POLL))
 +              already = true;
 +
 +      if (beacon)
 +              ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
 +      else
 +              ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
 +
 +      if (already)
 +              goto out;
 +
 +      ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT;
  
        mutex_lock(&sdata->local->iflist_mtx);
        ieee80211_recalc_ps(sdata->local, -1);
        mutex_unlock(&sdata->local->iflist_mtx);
  
 -      ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
 -                               ifmgd->ssid_len, NULL, 0);
 +      ssid = ieee80211_bss_get_ie(&ifmgd->associated->cbss, WLAN_EID_SSID);
 +      ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid,
 +                               ssid + 2, ssid[1], NULL, 0);
  
 -      mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT);
 +      run_again(ifmgd, ifmgd->probe_timeout);
 +
 + out:
 +      mutex_unlock(&ifmgd->mtx);
 +}
 +
 +void ieee80211_beacon_loss_work(struct work_struct *work)
 +{
 +      struct ieee80211_sub_if_data *sdata =
 +              container_of(work, struct ieee80211_sub_if_data,
 +                           u.mgd.beacon_loss_work);
 +
 +      ieee80211_mgd_probe_ap(sdata, true);
  }
  
  void ieee80211_beacon_loss(struct ieee80211_vif *vif)
  }
  EXPORT_SYMBOL(ieee80211_beacon_loss);
  
 -static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
 -{
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -      struct ieee80211_local *local = sdata->local;
 -      struct sta_info *sta;
 -      unsigned long last_rx;
 -      bool disassoc = false;
 -
 -      /* TODO: start monitoring current AP signal quality and number of
 -       * missed beacons. Scan other channels every now and then and search
 -       * for better APs. */
 -      /* TODO: remove expired BSSes */
 -
 -      ifmgd->state = IEEE80211_STA_MLME_ASSOCIATED;
 -
 -      rcu_read_lock();
 -
 -      sta = sta_info_get(local, ifmgd->bssid);
 -      if (!sta) {
 -              printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
 -                     sdata->dev->name, ifmgd->bssid);
 -              disassoc = true;
 -              rcu_read_unlock();
 -              goto out;
 -      }
 -
 -      last_rx = sta->last_rx;
 -      rcu_read_unlock();
 -
 -      if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) &&
 -          time_after(jiffies, last_rx + IEEE80211_PROBE_WAIT)) {
 -              printk(KERN_DEBUG "%s: no probe response from AP %pM "
 -                     "- disassociating\n",
 -                     sdata->dev->name, ifmgd->bssid);
 -              disassoc = true;
 -              ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
 -              goto out;
 -      }
 -
 -      /*
 -       * Beacon filtering is only enabled with power save and then the
 -       * stack should not check for beacon loss.
 -       */
 -      if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) &&
 -            (local->hw.conf.flags & IEEE80211_CONF_PS)) &&
 -          time_after(jiffies,
 -                     ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) {
 -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 -              if (net_ratelimit()) {
 -                      printk(KERN_DEBUG "%s: beacon loss from AP %pM "
 -                             "- sending probe request\n",
 -                             sdata->dev->name, ifmgd->bssid);
 -              }
 -#endif
 -              ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
 -              mutex_lock(&local->iflist_mtx);
 -              ieee80211_recalc_ps(local, -1);
 -              mutex_unlock(&local->iflist_mtx);
 -              ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
 -                                       ifmgd->ssid_len, NULL, 0);
 -              mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT);
 -              goto out;
 -      }
 -
 -      if (time_after(jiffies, last_rx + IEEE80211_PROBE_IDLE_TIME)) {
 -              ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
 -              mutex_lock(&local->iflist_mtx);
 -              ieee80211_recalc_ps(local, -1);
 -              mutex_unlock(&local->iflist_mtx);
 -              ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
 -                                       ifmgd->ssid_len, NULL, 0);
 -      }
 -
 - out:
 -      if (!disassoc)
 -              mod_timer(&ifmgd->timer,
 -                        jiffies + IEEE80211_MONITORING_INTERVAL);
 -      else
 -              ieee80211_set_disassoc(sdata, true, true,
 -                                      WLAN_REASON_PREV_AUTH_NOT_VALID);
 -}
 -
 -
 -static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata)
 +static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
 +                                   struct ieee80211_mgd_work *wk)
  {
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -
 +      wk->state = IEEE80211_MGD_STATE_IDLE;
        printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
 -      ifmgd->flags |= IEEE80211_STA_AUTHENTICATED;
 -      if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
 -              /* Wait for SME to request association */
 -              ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 -              ieee80211_recalc_idle(sdata->local);
 -      } else
 -              ieee80211_associate(sdata);
  }
  
  
  static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
 +                                   struct ieee80211_mgd_work *wk,
                                     struct ieee80211_mgmt *mgmt,
                                     size_t len)
  {
        ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
        if (!elems.challenge)
                return;
 -      ieee80211_send_auth(sdata, 3, sdata->u.mgd.auth_alg,
 +      ieee80211_send_auth(sdata, 3, wk->auth_alg,
                            elems.challenge - 2, elems.challenge_len + 2,
 -                          sdata->u.mgd.bssid, 1);
 -      sdata->u.mgd.auth_transaction = 4;
 +                          wk->bss->cbss.bssid,
 +                          wk->key, wk->key_len, wk->key_idx);
 +      wk->auth_transaction = 4;
  }
  
 -static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
 -                                 struct ieee80211_mgmt *mgmt,
 -                                 size_t len)
 +static enum rx_mgmt_action __must_check
 +ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
 +                     struct ieee80211_mgd_work *wk,
 +                     struct ieee80211_mgmt *mgmt, size_t len)
  {
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u16 auth_alg, auth_transaction, status_code;
  
 -      if (ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE)
 -              return;
 +      if (wk->state != IEEE80211_MGD_STATE_AUTH)
 +              return RX_MGMT_NONE;
  
        if (len < 24 + 6)
 -              return;
 +              return RX_MGMT_NONE;
  
 -      if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0)
 -              return;
 +      if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
 +              return RX_MGMT_NONE;
  
 -      if (memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
 -              return;
 +      if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
 +              return RX_MGMT_NONE;
  
        auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
        auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
        status_code = le16_to_cpu(mgmt->u.auth.status_code);
  
 -      if (auth_alg != ifmgd->auth_alg ||
 -          auth_transaction != ifmgd->auth_transaction)
 -              return;
 +      if (auth_alg != wk->auth_alg ||
 +          auth_transaction != wk->auth_transaction)
 +              return RX_MGMT_NONE;
  
        if (status_code != WLAN_STATUS_SUCCESS) {
 -              if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
 -                      u8 algs[3];
 -                      const int num_algs = ARRAY_SIZE(algs);
 -                      int i, pos;
 -                      algs[0] = algs[1] = algs[2] = 0xff;
 -                      if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN)
 -                              algs[0] = WLAN_AUTH_OPEN;
 -                      if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY)
 -                              algs[1] = WLAN_AUTH_SHARED_KEY;
 -                      if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP)
 -                              algs[2] = WLAN_AUTH_LEAP;
 -                      if (ifmgd->auth_alg == WLAN_AUTH_OPEN)
 -                              pos = 0;
 -                      else if (ifmgd->auth_alg == WLAN_AUTH_SHARED_KEY)
 -                              pos = 1;
 -                      else
 -                              pos = 2;
 -                      for (i = 0; i < num_algs; i++) {
 -                              pos++;
 -                              if (pos >= num_algs)
 -                                      pos = 0;
 -                              if (algs[pos] == ifmgd->auth_alg ||
 -                                  algs[pos] == 0xff)
 -                                      continue;
 -                              if (algs[pos] == WLAN_AUTH_SHARED_KEY &&
 -                                  !ieee80211_sta_wep_configured(sdata))
 -                                      continue;
 -                              ifmgd->auth_alg = algs[pos];
 -                              break;
 -                      }
 -              }
 -              return;
 +              list_del(&wk->list);
 +              kfree(wk);
 +              return RX_MGMT_CFG80211_AUTH;
        }
  
 -      switch (ifmgd->auth_alg) {
 +      switch (wk->auth_alg) {
        case WLAN_AUTH_OPEN:
        case WLAN_AUTH_LEAP:
        case WLAN_AUTH_FT:
 -              ieee80211_auth_completed(sdata);
 -              cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len);
 -              break;
 +              ieee80211_auth_completed(sdata, wk);
 +              return RX_MGMT_CFG80211_AUTH;
        case WLAN_AUTH_SHARED_KEY:
 -              if (ifmgd->auth_transaction == 4) {
 -                      ieee80211_auth_completed(sdata);
 -                      cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len);
 +              if (wk->auth_transaction == 4) {
 +                      ieee80211_auth_completed(sdata, wk);
 +                      return RX_MGMT_CFG80211_AUTH;
                } else
 -                      ieee80211_auth_challenge(sdata, mgmt, len);
 +                      ieee80211_auth_challenge(sdata, wk, mgmt, len);
                break;
        }
 +
 +      return RX_MGMT_NONE;
  }
  
  
 -static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 -                                   struct ieee80211_mgmt *mgmt,
 -                                   size_t len)
 +static enum rx_mgmt_action __must_check
 +ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 +                       struct ieee80211_mgd_work *wk,
 +                       struct ieee80211_mgmt *mgmt, size_t len)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      const u8 *bssid = NULL;
        u16 reason_code;
  
        if (len < 24 + 2)
 -              return;
 +              return RX_MGMT_NONE;
  
 -      if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN))
 -              return;
 +      ASSERT_MGD_MTX(ifmgd);
 +
 +      if (wk)
 +              bssid = wk->bss->cbss.bssid;
 +      else
 +              bssid = ifmgd->associated->cbss.bssid;
  
        reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
  
 -      if (ifmgd->flags & IEEE80211_STA_AUTHENTICATED)
 -              printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
 -                              sdata->dev->name, reason_code);
 +      printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
 +                      sdata->dev->name, bssid, reason_code);
  
 -      if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
 -          (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
 -           ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE ||
 -           ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) {
 -              ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
 -              mod_timer(&ifmgd->timer, jiffies +
 -                                    IEEE80211_RETRY_AUTH_INTERVAL);
 +      if (!wk) {
 +              ieee80211_set_disassoc(sdata);
 +      } else {
 +              list_del(&wk->list);
 +              kfree(wk);
        }
  
 -      ieee80211_set_disassoc(sdata, true, false, 0);
 -      ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED;
 -      cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, len);
 +      return RX_MGMT_CFG80211_DEAUTH;
  }
  
  
 -static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 -                                     struct ieee80211_mgmt *mgmt,
 -                                     size_t len)
 +static enum rx_mgmt_action __must_check
 +ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 +                         struct ieee80211_mgmt *mgmt, size_t len)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u16 reason_code;
  
        if (len < 24 + 2)
 -              return;
 +              return RX_MGMT_NONE;
  
 -      if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN))
 -              return;
 +      ASSERT_MGD_MTX(ifmgd);
  
 -      reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
 +      if (WARN_ON(!ifmgd->associated))
 +              return RX_MGMT_NONE;
  
 -      if (ifmgd->flags & IEEE80211_STA_ASSOCIATED)
 -              printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
 -                              sdata->dev->name, reason_code);
 +      if (WARN_ON(memcmp(ifmgd->associated->cbss.bssid, mgmt->sa, ETH_ALEN)))
 +              return RX_MGMT_NONE;
  
 -      if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
 -          ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
 -              ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
 -              mod_timer(&ifmgd->timer, jiffies +
 -                                    IEEE80211_RETRY_AUTH_INTERVAL);
 -      }
 +      reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
  
 -      ieee80211_set_disassoc(sdata, false, false, reason_code);
 -      cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, len);
 +      printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
 +                      sdata->dev->name, reason_code);
 +
 +      ieee80211_set_disassoc(sdata);
 +      return RX_MGMT_CFG80211_DISASSOC;
  }
  
  
 -static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
 -                                       struct ieee80211_mgmt *mgmt,
 -                                       size_t len,
 -                                       int reassoc)
 +static enum rx_mgmt_action __must_check
 +ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
 +                           struct ieee80211_mgd_work *wk,
 +                           struct ieee80211_mgmt *mgmt, size_t len,
 +                           bool reassoc)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
        bool have_higher_than_11mbit = false, newsta = false;
        u16 ap_ht_cap_flags;
  
 -      /* AssocResp and ReassocResp have identical structure, so process both
 -       * of them in this function. */
 -
 -      if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE)
 -              return;
 +      /*
 +       * AssocResp and ReassocResp have identical structure, so process both
 +       * of them in this function.
 +       */
  
        if (len < 24 + 6)
 -              return;
 +              return RX_MGMT_NONE;
  
 -      if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0)
 -              return;
 +      if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
 +              return RX_MGMT_NONE;
  
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
        status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
                printk(KERN_DEBUG "%s: AP rejected association temporarily; "
                       "comeback duration %u TU (%u ms)\n",
                       sdata->dev->name, tu, ms);
 +              wk->timeout = jiffies + msecs_to_jiffies(ms);
                if (ms > IEEE80211_ASSOC_TIMEOUT)
 -                      mod_timer(&ifmgd->timer,
 -                                jiffies + msecs_to_jiffies(ms));
 -              return;
 +                      run_again(ifmgd, jiffies + msecs_to_jiffies(ms));
 +              return RX_MGMT_NONE;
        }
  
        if (status_code != WLAN_STATUS_SUCCESS) {
                printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
                       sdata->dev->name, status_code);
 -              /* if this was a reassociation, ensure we try a "full"
 -               * association next time. This works around some broken APs
 -               * which do not correctly reject reassociation requests. */
 -              ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
 -              cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len);
 -              if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
 -                      /* Wait for SME to decide what to do next */
 -                      ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 -                      ieee80211_recalc_idle(local);
 -              }
 -              return;
 +              list_del(&wk->list);
 +              kfree(wk);
 +              return RX_MGMT_CFG80211_ASSOC;
        }
  
        if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
        if (!elems.supp_rates) {
                printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
                       sdata->dev->name);
 -              return;
 +              return RX_MGMT_NONE;
        }
  
        printk(KERN_DEBUG "%s: associated\n", sdata->dev->name);
        ifmgd->aid = aid;
 -      ifmgd->ap_capab = capab_info;
 -
 -      kfree(ifmgd->assocresp_ies);
 -      ifmgd->assocresp_ies_len = len - (pos - (u8 *) mgmt);
 -      ifmgd->assocresp_ies = kmalloc(ifmgd->assocresp_ies_len, GFP_KERNEL);
 -      if (ifmgd->assocresp_ies)
 -              memcpy(ifmgd->assocresp_ies, pos, ifmgd->assocresp_ies_len);
  
        rcu_read_lock();
  
        /* Add STA entry for the AP */
 -      sta = sta_info_get(local, ifmgd->bssid);
 +      sta = sta_info_get(local, wk->bss->cbss.bssid);
        if (!sta) {
                newsta = true;
  
 -              sta = sta_info_alloc(sdata, ifmgd->bssid, GFP_ATOMIC);
 +              rcu_read_unlock();
 +
 +              sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL);
                if (!sta) {
                        printk(KERN_DEBUG "%s: failed to alloc STA entry for"
                               " the AP\n", sdata->dev->name);
 -                      rcu_read_unlock();
 -                      return;
 +                      return RX_MGMT_NONE;
                }
  
 -              /* update new sta with its last rx activity */
 -              sta->last_rx = jiffies;
 -      }
 -
 -      /*
 -       * FIXME: Do we really need to update the sta_info's information here?
 -       *        We already know about the AP (we found it in our list) so it
 -       *        should already be filled with the right info, no?
 -       *        As is stands, all this is racy because typically we assume
 -       *        the information that is filled in here (except flags) doesn't
 -       *        change while a STA structure is alive. As such, it should move
 -       *        to between the sta_info_alloc() and sta_info_insert() above.
 -       */
 +              set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
 +                                 WLAN_STA_ASSOC_AP);
 +              if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
 +                      set_sta_flags(sta, WLAN_STA_AUTHORIZED);
  
 -      set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP);
 -      if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
 -              set_sta_flags(sta, WLAN_STA_AUTHORIZED);
 +              rcu_read_lock();
 +      }
  
        rates = 0;
        basic_rates = 0;
        else
                sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
  
 -      /* If TKIP/WEP is used, no need to parse AP's HT capabilities */
 -      if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))
 +      if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
                ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
                                elems.ht_cap_elem, &sta->sta.ht_cap);
  
                        printk(KERN_DEBUG "%s: failed to insert STA entry for"
                               " the AP (error %d)\n", sdata->dev->name, err);
                        rcu_read_unlock();
 -                      return;
 +                      return RX_MGMT_NONE;
                }
        }
  
  
        if (elems.ht_info_elem && elems.wmm_param &&
            (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
 -          !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))
 +          !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
                changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
 +                                             wk->bss->cbss.bssid,
                                               ap_ht_cap_flags);
  
        /* set AID and assoc capability,
         * ieee80211_set_associated() will tell the driver */
        bss_conf->aid = aid;
        bss_conf->assoc_capability = capab_info;
 -      ieee80211_set_associated(sdata, changed);
 +      ieee80211_set_associated(sdata, wk->bss, changed);
  
        /*
 -       * initialise the time of last beacon to be the association time,
 -       * otherwise beacon loss check will trigger immediately
 +       * Start timer to probe the connection to the AP now.
 +       * Also start the timer that will detect beacon loss.
         */
 -      ifmgd->last_beacon = jiffies;
 +      ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
 +      mod_beacon_timer(sdata);
  
 -      ieee80211_associated(sdata);
 -      cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len);
 +      list_del(&wk->list);
 +      kfree(wk);
 +      return RX_MGMT_CFG80211_ASSOC;
  }
  
  
@@@ -1610,25 -1851,23 +1610,25 @@@ static void ieee80211_rx_bss_info(struc
  
        bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
                                        channel, beacon);
 -      if (!bss)
 +      if (bss)
 +              ieee80211_rx_bss_put(local, bss);
 +
 +      if (!sdata->u.mgd.associated)
                return;
  
        if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
 -          (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) {
 +          (memcmp(mgmt->bssid, sdata->u.mgd.associated->cbss.bssid,
 +                                                      ETH_ALEN) == 0)) {
                struct ieee80211_channel_sw_ie *sw_elem =
                        (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
                ieee80211_sta_process_chanswitch(sdata, sw_elem, bss);
        }
 -
 -      ieee80211_rx_bss_put(local, bss);
  }
  
  
  static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
 -                                       struct ieee80211_mgmt *mgmt,
 -                                       size_t len,
 +                                       struct ieee80211_mgd_work *wk,
 +                                       struct ieee80211_mgmt *mgmt, size_t len,
                                         struct ieee80211_rx_status *rx_status)
  {
        struct ieee80211_if_managed *ifmgd;
  
        ifmgd = &sdata->u.mgd;
  
 +      ASSERT_MGD_MTX(ifmgd);
 +
        if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
                return; /* ignore ProbeResp to foreign address */
  
        ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
  
        /* direct probe may be part of the association flow */
 -      if (ifmgd->state == IEEE80211_STA_MLME_DIRECT_PROBE) {
 +      if (wk && wk->state == IEEE80211_MGD_STATE_PROBE) {
                printk(KERN_DEBUG "%s direct probe responded\n",
                       sdata->dev->name);
 -              ieee80211_authenticate(sdata);
 +              wk->tries = 0;
 +              wk->state = IEEE80211_MGD_STATE_AUTH;
 +              WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE);
        }
  
 -      if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
 -              ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
 +      if (ifmgd->associated &&
 +          memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 &&
 +          ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
 +                          IEEE80211_STA_CONNECTION_POLL)) {
 +              ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
 +                                IEEE80211_STA_BEACON_POLL);
                mutex_lock(&sdata->local->iflist_mtx);
                ieee80211_recalc_ps(sdata->local, -1);
                mutex_unlock(&sdata->local->iflist_mtx);
 +              /*
 +               * We've received a probe response, but are not sure whether
 +               * we have or will be receiving any beacons or data, so let's
 +               * schedule the timers again, just in case.
 +               */
 +              mod_beacon_timer(sdata);
 +              mod_timer(&ifmgd->conn_mon_timer,
 +                        round_jiffies_up(jiffies +
 +                                         IEEE80211_CONNECTION_IDLE_TIME));
        }
  }
  
@@@ -1715,9 -1937,6 +1715,9 @@@ static void ieee80211_rx_mgmt_beacon(st
        bool erp_valid, directed_tim = false;
        u8 erp_value = 0;
        u32 ncrc;
 +      u8 *bssid;
 +
 +      ASSERT_MGD_MTX(ifmgd);
  
        /* Process beacon from the current BSS */
        baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
        if (rx_status->freq != local->hw.conf.channel->center_freq)
                return;
  
 -      if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED) ||
 -          memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
 +      /*
 +       * We might have received a number of frames, among them a
 +       * disassoc frame and a beacon...
 +       */
 +      if (!ifmgd->associated)
 +              return;
 +
 +      bssid = ifmgd->associated->cbss.bssid;
 +
 +      /*
 +       * And in theory even frames from a different AP we were just
 +       * associated to a split-second ago!
 +       */
 +      if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
                return;
  
 -      if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
 +      if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                if (net_ratelimit()) {
                        printk(KERN_DEBUG "%s: cancelling probereq poll due "
                               "to a received beacon\n", sdata->dev->name);
                }
  #endif
 -              ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
 +              ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
                mutex_lock(&local->iflist_mtx);
                ieee80211_recalc_ps(local, -1);
                mutex_unlock(&local->iflist_mtx);
        }
  
 +      /*
 +       * Push the beacon loss detection into the future since
 +       * we are processing a beacon from the AP just now.
 +       */
 +      mod_beacon_timer(sdata);
 +
        ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
        ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
                                          len - baselen, &elems,
  
  
        if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
 -          !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) {
 +          !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
                struct sta_info *sta;
                struct ieee80211_supported_band *sband;
                u16 ap_ht_cap_flags;
  
                rcu_read_lock();
  
 -              sta = sta_info_get(local, ifmgd->bssid);
 -              if (!sta) {
 +              sta = sta_info_get(local, bssid);
 +              if (WARN_ON(!sta)) {
                        rcu_read_unlock();
                        return;
                }
                rcu_read_unlock();
  
                changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
 -                                             ap_ht_cap_flags);
 +                                             bssid, ap_ht_cap_flags);
        }
  
        if (elems.country_elem) {
  }
  
  ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
 -                                        struct sk_buff *skb,
 -                                        struct ieee80211_rx_status *rx_status)
 +                                        struct sk_buff *skb)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_mgmt *mgmt;
        case IEEE80211_STYPE_PROBE_REQ:
        case IEEE80211_STYPE_PROBE_RESP:
        case IEEE80211_STYPE_BEACON:
 -              memcpy(skb->cb, rx_status, sizeof(*rx_status));
        case IEEE80211_STYPE_AUTH:
        case IEEE80211_STYPE_ASSOC_RESP:
        case IEEE80211_STYPE_REASSOC_RESP:
        case IEEE80211_STYPE_DEAUTH:
        case IEEE80211_STYPE_DISASSOC:
 +      case IEEE80211_STYPE_ACTION:
                skb_queue_tail(&sdata->u.mgd.skb_queue, skb);
                queue_work(local->hw.workqueue, &sdata->u.mgd.work);
                return RX_QUEUED;
  static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                         struct sk_buff *skb)
  {
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_rx_status *rx_status;
        struct ieee80211_mgmt *mgmt;
 +      struct ieee80211_mgd_work *wk;
 +      enum rx_mgmt_action rma = RX_MGMT_NONE;
        u16 fc;
  
        rx_status = (struct ieee80211_rx_status *) skb->cb;
        mgmt = (struct ieee80211_mgmt *) skb->data;
        fc = le16_to_cpu(mgmt->frame_control);
  
 -      switch (fc & IEEE80211_FCTL_STYPE) {
 -      case IEEE80211_STYPE_PROBE_RESP:
 -              ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
 -                                           rx_status);
 -              break;
 -      case IEEE80211_STYPE_BEACON:
 -              ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
 -                                       rx_status);
 -              break;
 -      case IEEE80211_STYPE_AUTH:
 -              ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
 -              break;
 -      case IEEE80211_STYPE_ASSOC_RESP:
 -              ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 0);
 +      mutex_lock(&ifmgd->mtx);
 +
 +      if (ifmgd->associated &&
 +          memcmp(ifmgd->associated->cbss.bssid, mgmt->bssid,
 +                                                      ETH_ALEN) == 0) {
 +              switch (fc & IEEE80211_FCTL_STYPE) {
 +              case IEEE80211_STYPE_BEACON:
 +                      ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
 +                                               rx_status);
 +                      break;
 +              case IEEE80211_STYPE_PROBE_RESP:
 +                      ieee80211_rx_mgmt_probe_resp(sdata, NULL, mgmt,
 +                                                   skb->len, rx_status);
 +                      break;
 +              case IEEE80211_STYPE_DEAUTH:
 +                      rma = ieee80211_rx_mgmt_deauth(sdata, NULL,
 +                                                     mgmt, skb->len);
 +                      break;
 +              case IEEE80211_STYPE_DISASSOC:
 +                      rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
 +                      break;
 +              case IEEE80211_STYPE_ACTION:
 +                      /* XXX: differentiate, can only happen for CSA now! */
 +                      ieee80211_sta_process_chanswitch(sdata,
 +                                      &mgmt->u.action.u.chan_switch.sw_elem,
 +                                      ifmgd->associated);
 +                      break;
 +              }
 +              mutex_unlock(&ifmgd->mtx);
 +
 +              switch (rma) {
 +              case RX_MGMT_NONE:
 +                      /* no action */
 +                      break;
 +              case RX_MGMT_CFG80211_DEAUTH:
 +                      cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len,
 +                                           NULL);
 +                      break;
 +              case RX_MGMT_CFG80211_DISASSOC:
 +                      cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len,
 +                                             NULL);
 +                      break;
 +              default:
 +                      WARN(1, "unexpected: %d", rma);
 +              }
 +              goto out;
 +      }
 +
 +      list_for_each_entry(wk, &ifmgd->work_list, list) {
 +              if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
 +                      continue;
 +
 +              switch (fc & IEEE80211_FCTL_STYPE) {
 +              case IEEE80211_STYPE_PROBE_RESP:
 +                      ieee80211_rx_mgmt_probe_resp(sdata, wk, mgmt, skb->len,
 +                                                   rx_status);
 +                      break;
 +              case IEEE80211_STYPE_AUTH:
 +                      rma = ieee80211_rx_mgmt_auth(sdata, wk, mgmt, skb->len);
 +                      break;
 +              case IEEE80211_STYPE_ASSOC_RESP:
 +                      rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
 +                                                         skb->len, false);
 +                      break;
 +              case IEEE80211_STYPE_REASSOC_RESP:
 +                      rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
 +                                                         skb->len, true);
 +                      break;
 +              case IEEE80211_STYPE_DEAUTH:
 +                      rma = ieee80211_rx_mgmt_deauth(sdata, wk, mgmt,
 +                                                     skb->len);
 +                      break;
 +              }
 +              /*
 +               * We've processed this frame for that work, so it can't
 +               * belong to another work struct.
 +               * NB: this is also required for correctness because the
 +               * called functions can free 'wk', and for 'rma'!
 +               */
                break;
 -      case IEEE80211_STYPE_REASSOC_RESP:
 -              ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 1);
 +      }
 +
 +      mutex_unlock(&ifmgd->mtx);
 +
 +      switch (rma) {
 +      case RX_MGMT_NONE:
 +              /* no action */
                break;
 -      case IEEE80211_STYPE_DEAUTH:
 -              ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
 +      case RX_MGMT_CFG80211_AUTH:
 +              cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len);
                break;
 -      case IEEE80211_STYPE_DISASSOC:
 -              ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
 +      case RX_MGMT_CFG80211_ASSOC:
 +              cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len);
                break;
 +      default:
 +              WARN(1, "unexpected: %d", rma);
        }
  
 + out:
        kfree_skb(skb);
  }
  
@@@ -2020,9 -2146,125 +2020,9 @@@ static void ieee80211_sta_timer(unsigne
                return;
        }
  
 -      set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
        queue_work(local->hw.workqueue, &ifmgd->work);
  }
  
 -static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata)
 -{
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -      struct ieee80211_local *local = sdata->local;
 -
 -      /* Reset own TSF to allow time synchronization work. */
 -      drv_reset_tsf(local);
 -
 -      ifmgd->wmm_last_param_set = -1; /* allow any WMM update */
 -
 -
 -      if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN)
 -              ifmgd->auth_alg = WLAN_AUTH_OPEN;
 -      else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY)
 -              ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY;
 -      else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP)
 -              ifmgd->auth_alg = WLAN_AUTH_LEAP;
 -      else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_FT)
 -              ifmgd->auth_alg = WLAN_AUTH_FT;
 -      else
 -              ifmgd->auth_alg = WLAN_AUTH_OPEN;
 -      ifmgd->auth_transaction = -1;
 -      ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED;
 -      ifmgd->assoc_scan_tries = 0;
 -      ifmgd->direct_probe_tries = 0;
 -      ifmgd->auth_tries = 0;
 -      ifmgd->assoc_tries = 0;
 -      netif_tx_stop_all_queues(sdata->dev);
 -      netif_carrier_off(sdata->dev);
 -}
 -
 -static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata)
 -{
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -      struct ieee80211_local *local = sdata->local;
 -      struct ieee80211_bss *bss;
 -      u8 *bssid = ifmgd->bssid, *ssid = ifmgd->ssid;
 -      u8 ssid_len = ifmgd->ssid_len;
 -      u16 capa_mask = WLAN_CAPABILITY_ESS;
 -      u16 capa_val = WLAN_CAPABILITY_ESS;
 -      struct ieee80211_channel *chan = local->oper_channel;
 -
 -      if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
 -          ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL |
 -                          IEEE80211_STA_AUTO_BSSID_SEL |
 -                          IEEE80211_STA_AUTO_CHANNEL_SEL)) {
 -              capa_mask |= WLAN_CAPABILITY_PRIVACY;
 -              if (sdata->default_key)
 -                      capa_val |= WLAN_CAPABILITY_PRIVACY;
 -      }
 -
 -      if (ifmgd->flags & IEEE80211_STA_AUTO_CHANNEL_SEL)
 -              chan = NULL;
 -
 -      if (ifmgd->flags & IEEE80211_STA_AUTO_BSSID_SEL)
 -              bssid = NULL;
 -
 -      if (ifmgd->flags & IEEE80211_STA_AUTO_SSID_SEL) {
 -              ssid = NULL;
 -              ssid_len = 0;
 -      }
 -
 -      bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan,
 -                                     bssid, ssid, ssid_len,
 -                                     capa_mask, capa_val);
 -
 -      if (bss) {
 -              local->oper_channel = bss->cbss.channel;
 -              local->oper_channel_type = NL80211_CHAN_NO_HT;
 -              ieee80211_hw_config(local, 0);
 -
 -              if (!(ifmgd->flags & IEEE80211_STA_SSID_SET))
 -                      ieee80211_sta_set_ssid(sdata, bss->ssid,
 -                                             bss->ssid_len);
 -              ieee80211_sta_set_bssid(sdata, bss->cbss.bssid);
 -              ieee80211_sta_def_wmm_params(sdata, bss->supp_rates_len,
 -                                                  bss->supp_rates);
 -              if (sdata->u.mgd.mfp == IEEE80211_MFP_REQUIRED)
 -                      sdata->u.mgd.flags |= IEEE80211_STA_MFP_ENABLED;
 -              else
 -                      sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED;
 -
 -              /* Send out direct probe if no probe resp was received or
 -               * the one we have is outdated
 -               */
 -              if (!bss->last_probe_resp ||
 -                  time_after(jiffies, bss->last_probe_resp
 -                                      + IEEE80211_SCAN_RESULT_EXPIRE))
 -                      ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
 -              else
 -                      ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
 -
 -              ieee80211_rx_bss_put(local, bss);
 -              ieee80211_sta_reset_auth(sdata);
 -              return 0;
 -      } else {
 -              if (ifmgd->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
 -
 -                      ifmgd->assoc_scan_tries++;
 -
 -                      ieee80211_request_internal_scan(sdata, ifmgd->ssid,
 -                                                      ssid_len);
 -
 -                      ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
 -                      set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
 -              } else {
 -                      ifmgd->assoc_scan_tries = 0;
 -                      ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 -                      ieee80211_recalc_idle(local);
 -              }
 -      }
 -      return -1;
 -}
 -
 -
  static void ieee80211_sta_work(struct work_struct *work)
  {
        struct ieee80211_sub_if_data *sdata =
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd;
        struct sk_buff *skb;
 +      struct ieee80211_mgd_work *wk, *tmp;
 +      LIST_HEAD(free_work);
 +      enum rx_mgmt_action rma;
 +      bool anybusy = false;
  
        if (!netif_running(sdata->dev))
                return;
  
        ifmgd = &sdata->u.mgd;
  
 +      /* first process frames to avoid timing out while a frame is pending */
        while ((skb = skb_dequeue(&ifmgd->skb_queue)))
                ieee80211_sta_rx_queued_mgmt(sdata, skb);
  
 -      if (ifmgd->state != IEEE80211_STA_MLME_DIRECT_PROBE &&
 -          ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE &&
 -          ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE &&
 -          test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) {
 -              queue_delayed_work(local->hw.workqueue, &local->scan_work,
 -                                 round_jiffies_relative(0));
 -              return;
 +      /* then process the rest of the work */
 +      mutex_lock(&ifmgd->mtx);
 +
 +      if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
 +                          IEEE80211_STA_CONNECTION_POLL) &&
 +          ifmgd->associated) {
 +              if (time_is_after_jiffies(ifmgd->probe_timeout))
 +                      run_again(ifmgd, ifmgd->probe_timeout);
 +              else {
 +                      u8 bssid[ETH_ALEN];
 +                      /*
 +                       * We actually lost the connection ... or did we?
 +                       * Let's make sure!
 +                       */
 +                      ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
 +                                        IEEE80211_STA_BEACON_POLL);
 +                      memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN);
 +                      printk(KERN_DEBUG "No probe response from AP %pM"
 +                              " after %dms, disconnecting.\n",
 +                              bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
 +                      ieee80211_set_disassoc(sdata);
 +                      mutex_unlock(&ifmgd->mtx);
 +                      /*
 +                       * must be outside lock due to cfg80211,
 +                       * but that's not a problem.
 +                       */
 +                      ieee80211_send_deauth_disassoc(sdata, bssid,
 +                                      IEEE80211_STYPE_DEAUTH,
 +                                      WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
 +                                      NULL);
 +                      mutex_lock(&ifmgd->mtx);
 +              }
        }
  
 -      if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request)) {
 -              if (ieee80211_sta_config_auth(sdata))
 -                      return;
 -              clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
 -      } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request))
 -              return;
 +      list_for_each_entry(wk, &ifmgd->work_list, list) {
 +              if (wk->state != IEEE80211_MGD_STATE_IDLE) {
 +                      anybusy = true;
 +                      break;
 +              }
 +      }
  
        ieee80211_recalc_idle(local);
  
 -      switch (ifmgd->state) {
 -      case IEEE80211_STA_MLME_DISABLED:
 -              break;
 -      case IEEE80211_STA_MLME_DIRECT_PROBE:
 -              ieee80211_direct_probe(sdata);
 -              break;
 -      case IEEE80211_STA_MLME_AUTHENTICATE:
 -              ieee80211_authenticate(sdata);
 -              break;
 -      case IEEE80211_STA_MLME_ASSOCIATE:
 -              ieee80211_associate(sdata);
 -              break;
 -      case IEEE80211_STA_MLME_ASSOCIATED:
 -              ieee80211_associated(sdata);
 -              break;
 -      default:
 -              WARN_ON(1);
 -              break;
 +      if (!anybusy) {
 +              mutex_unlock(&ifmgd->mtx);
 +
 +              if (test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request))
 +                      queue_delayed_work(local->hw.workqueue,
 +                                         &local->scan_work,
 +                                         round_jiffies_relative(0));
 +              return;
        }
  
 -      if (ieee80211_privacy_mismatch(sdata)) {
 -              printk(KERN_DEBUG "%s: privacy configuration mismatch and "
 -                     "mixed-cell disabled - disassociate\n", sdata->dev->name);
 +      list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) {
 +              if (time_is_after_jiffies(wk->timeout)) {
 +                      /*
 +                       * This work item isn't supposed to be worked on
 +                       * right now, but take care to adjust the timer
 +                       * properly.
 +                       */
 +                      run_again(ifmgd, wk->timeout);
 +                      continue;
 +              }
 +
 +              switch (wk->state) {
 +              default:
 +                      WARN_ON(1);
 +                      /* fall through */
 +              case IEEE80211_MGD_STATE_IDLE:
 +                      /* nothing */
 +                      rma = RX_MGMT_NONE;
 +                      break;
 +              case IEEE80211_MGD_STATE_PROBE:
 +                      rma = ieee80211_direct_probe(sdata, wk);
 +                      break;
 +              case IEEE80211_MGD_STATE_AUTH:
 +                      rma = ieee80211_authenticate(sdata, wk);
 +                      break;
 +              case IEEE80211_MGD_STATE_ASSOC:
 +                      rma = ieee80211_associate(sdata, wk);
 +                      break;
 +              }
  
 -              ieee80211_set_disassoc(sdata, false, true,
 -                                      WLAN_REASON_UNSPECIFIED);
 +              switch (rma) {
 +              case RX_MGMT_NONE:
 +                      /* no action required */
 +                      break;
 +              case RX_MGMT_CFG80211_AUTH_TO:
 +              case RX_MGMT_CFG80211_ASSOC_TO:
 +                      list_del(&wk->list);
 +                      list_add(&wk->list, &free_work);
 +                      wk->tries = rma; /* small abuse but only local */
 +                      break;
 +              default:
 +                      WARN(1, "unexpected: %d", rma);
 +              }
        }
 +
 +      mutex_unlock(&ifmgd->mtx);
 +
 +      list_for_each_entry_safe(wk, tmp, &free_work, list) {
 +              switch (wk->tries) {
 +              case RX_MGMT_CFG80211_AUTH_TO:
 +                      cfg80211_send_auth_timeout(sdata->dev,
 +                                                 wk->bss->cbss.bssid);
 +                      break;
 +              case RX_MGMT_CFG80211_ASSOC_TO:
 +                      cfg80211_send_assoc_timeout(sdata->dev,
 +                                                  wk->bss->cbss.bssid);
 +                      break;
 +              default:
 +                      WARN(1, "unexpected: %d", wk->tries);
 +              }
 +
 +              list_del(&wk->list);
 +              kfree(wk);
 +      }
 +
 +      ieee80211_recalc_idle(local);
 +}
 +
 +static void ieee80211_sta_bcn_mon_timer(unsigned long data)
 +{
 +      struct ieee80211_sub_if_data *sdata =
 +              (struct ieee80211_sub_if_data *) data;
 +      struct ieee80211_local *local = sdata->local;
 +
 +      if (local->quiescing)
 +              return;
 +
 +      queue_work(sdata->local->hw.workqueue,
 +                 &sdata->u.mgd.beacon_loss_work);
 +}
 +
 +static void ieee80211_sta_conn_mon_timer(unsigned long data)
 +{
 +      struct ieee80211_sub_if_data *sdata =
 +              (struct ieee80211_sub_if_data *) data;
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      struct ieee80211_local *local = sdata->local;
 +
 +      if (local->quiescing)
 +              return;
 +
 +      queue_work(local->hw.workqueue, &ifmgd->monitor_work);
 +}
 +
 +static void ieee80211_sta_monitor_work(struct work_struct *work)
 +{
 +      struct ieee80211_sub_if_data *sdata =
 +              container_of(work, struct ieee80211_sub_if_data,
 +                           u.mgd.monitor_work);
 +
 +      if (sdata->local->sw_scanning || sdata->local->hw_scanning)
 +              return;
 +
 +      ieee80211_mgd_probe_ap(sdata, false);
  }
  
  static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
  {
        if (sdata->vif.type == NL80211_IFTYPE_STATION) {
 -              /*
 -               * Need to update last_beacon to avoid beacon loss
 -               * test to trigger.
 -               */
 -              sdata->u.mgd.last_beacon = jiffies;
 -
 +              sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL |
 +                                      IEEE80211_STA_CONNECTION_POLL);
  
 +              /* let's probe the connection once */
 +              queue_work(sdata->local->hw.workqueue,
 +                         &sdata->u.mgd.monitor_work);
 +              /* and do all the other regular work too */
                queue_work(sdata->local->hw.workqueue,
                           &sdata->u.mgd.work);
        }
@@@ -2253,11 -2378,6 +2253,11 @@@ void ieee80211_sta_quiesce(struct ieee8
        cancel_work_sync(&ifmgd->chswitch_work);
        if (del_timer_sync(&ifmgd->chswitch_timer))
                set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
 +
 +      cancel_work_sync(&ifmgd->monitor_work);
 +      /* these will just be re-established on connection */
 +      del_timer_sync(&ifmgd->conn_mon_timer);
 +      del_timer_sync(&ifmgd->bcn_mon_timer);
  }
  
  void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
  void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
  {
        struct ieee80211_if_managed *ifmgd;
 -      u32 hw_flags;
  
        ifmgd = &sdata->u.mgd;
        INIT_WORK(&ifmgd->work, ieee80211_sta_work);
 +      INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
        INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
        INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
 +      setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
 +                  (unsigned long) sdata);
 +      setup_timer(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer,
 +                  (unsigned long) sdata);
        setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
                    (unsigned long) sdata);
        skb_queue_head_init(&ifmgd->skb_queue);
  
 +      INIT_LIST_HEAD(&ifmgd->work_list);
 +
        ifmgd->capab = WLAN_CAPABILITY_ESS;
 -      ifmgd->auth_algs = IEEE80211_AUTH_ALG_OPEN |
 -              IEEE80211_AUTH_ALG_SHARED_KEY;
 -      ifmgd->flags |= IEEE80211_STA_CREATE_IBSS |
 -              IEEE80211_STA_AUTO_BSSID_SEL |
 -              IEEE80211_STA_AUTO_CHANNEL_SEL;
 +      ifmgd->flags = 0;
        if (sdata->local->hw.queues >= 4)
                ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
  
 -      hw_flags = sdata->local->hw.flags;
 -
 -      if (hw_flags & IEEE80211_HW_SUPPORTS_PS) {
 -              ifmgd->powersave = CONFIG_MAC80211_DEFAULT_PS_VALUE;
 -              sdata->local->hw.conf.dynamic_ps_timeout = 500;
 -      }
 +      mutex_init(&ifmgd->mtx);
  }
  
 -/* configuration hooks */
 -void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata)
 +/* scan finished notification */
 +void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
  {
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -      struct ieee80211_local *local = sdata->local;
 -
 -      if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
 -              return;
 -
 -      if ((ifmgd->flags & (IEEE80211_STA_BSSID_SET |
 -                           IEEE80211_STA_AUTO_BSSID_SEL)) &&
 -          (ifmgd->flags & (IEEE80211_STA_SSID_SET |
 -                           IEEE80211_STA_AUTO_SSID_SEL))) {
 -
 -              if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)
 -                      ieee80211_set_disassoc(sdata, true, true,
 -                                             WLAN_REASON_DEAUTH_LEAVING);
 -
 -              if (ifmgd->ssid_len == 0) {
 -                      /*
 -                       * Only allow association to be started if a valid SSID
 -                       * is configured.
 -                       */
 -                      return;
 -              }
 +      struct ieee80211_sub_if_data *sdata = local->scan_sdata;
  
 -              if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) ||
 -                  ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE)
 -                      set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
 -              else if (ifmgd->flags & IEEE80211_STA_EXT_SME)
 -                      set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
 -              queue_work(local->hw.workqueue, &ifmgd->work);
 -      }
 +      /* Restart STA timers */
 +      rcu_read_lock();
 +      list_for_each_entry_rcu(sdata, &local->interfaces, list)
 +              ieee80211_restart_sta_timer(sdata);
 +      rcu_read_unlock();
  }
  
 -int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata)
 +int ieee80211_max_network_latency(struct notifier_block *nb,
 +                                unsigned long data, void *dummy)
  {
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      s32 latency_usec = (s32) data;
 +      struct ieee80211_local *local =
 +              container_of(nb, struct ieee80211_local,
 +                           network_latency_notifier);
  
 -      if (ifmgd->ssid_len)
 -              ifmgd->flags |= IEEE80211_STA_SSID_SET;
 -      else
 -              ifmgd->flags &= ~IEEE80211_STA_SSID_SET;
 +      mutex_lock(&local->iflist_mtx);
 +      ieee80211_recalc_ps(local, latency_usec);
 +      mutex_unlock(&local->iflist_mtx);
  
        return 0;
  }
  
 -int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
 +/* config hooks */
 +int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 +                     struct cfg80211_auth_request *req)
  {
 -      struct ieee80211_if_managed *ifmgd;
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      const u8 *ssid;
 +      struct ieee80211_mgd_work *wk;
 +      u16 auth_alg;
  
 -      if (len > IEEE80211_MAX_SSID_LEN)
 -              return -EINVAL;
 +      switch (req->auth_type) {
 +      case NL80211_AUTHTYPE_OPEN_SYSTEM:
 +              auth_alg = WLAN_AUTH_OPEN;
 +              break;
 +      case NL80211_AUTHTYPE_SHARED_KEY:
 +              auth_alg = WLAN_AUTH_SHARED_KEY;
 +              break;
 +      case NL80211_AUTHTYPE_FT:
 +              auth_alg = WLAN_AUTH_FT;
 +              break;
 +      case NL80211_AUTHTYPE_NETWORK_EAP:
 +              auth_alg = WLAN_AUTH_LEAP;
 +              break;
 +      default:
 +              return -EOPNOTSUPP;
 +      }
  
 -      ifmgd = &sdata->u.mgd;
 +      wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
 +      if (!wk)
 +              return -ENOMEM;
  
 -      if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) {
 -              if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)
 -                      ieee80211_set_disassoc(sdata, true, true,
 -                                             WLAN_REASON_DEAUTH_LEAVING);
 +      wk->bss = (void *)req->bss;
  
 -              /*
 -               * Do not use reassociation if SSID is changed (different ESS).
 -               */
 -              ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
 -              memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid));
 -              memcpy(ifmgd->ssid, ssid, len);
 -              ifmgd->ssid_len = len;
 +      if (req->ie && req->ie_len) {
 +              memcpy(wk->ie, req->ie, req->ie_len);
 +              wk->ie_len = req->ie_len;
        }
  
 -      return ieee80211_sta_commit(sdata);
 -}
 +      if (req->key && req->key_len) {
 +              wk->key_len = req->key_len;
 +              wk->key_idx = req->key_idx;
 +              memcpy(wk->key, req->key, req->key_len);
 +      }
  
 -int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len)
 -{
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 -      memcpy(ssid, ifmgd->ssid, ifmgd->ssid_len);
 -      *len = ifmgd->ssid_len;
 +      ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
 +      memcpy(wk->ssid, ssid + 2, ssid[1]);
 +      wk->ssid_len = ssid[1];
 +
 +      wk->state = IEEE80211_MGD_STATE_PROBE;
 +      wk->auth_alg = auth_alg;
 +
 +      /*
 +       * XXX: if still associated need to tell AP that we're going
 +       *      to sleep and then change channel etc.
 +       */
 +      sdata->local->oper_channel = req->bss->channel;
 +      ieee80211_hw_config(sdata->local, 0);
 +
 +      mutex_lock(&ifmgd->mtx);
 +      list_add(&wk->list, &sdata->u.mgd.work_list);
 +      mutex_unlock(&ifmgd->mtx);
 +
 +      queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work);
        return 0;
  }
  
 -int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
 +int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 +                      struct cfg80211_assoc_request *req)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      struct ieee80211_mgd_work *wk, *found = NULL;
 +      int i, err;
  
 -      if (compare_ether_addr(bssid, ifmgd->bssid) != 0 &&
 -          ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)
 -              ieee80211_set_disassoc(sdata, true, true,
 -                                     WLAN_REASON_DEAUTH_LEAVING);
 +      mutex_lock(&ifmgd->mtx);
  
 -      if (is_valid_ether_addr(bssid)) {
 -              memcpy(ifmgd->bssid, bssid, ETH_ALEN);
 -              ifmgd->flags |= IEEE80211_STA_BSSID_SET;
 -      } else {
 -              memset(ifmgd->bssid, 0, ETH_ALEN);
 -              ifmgd->flags &= ~IEEE80211_STA_BSSID_SET;
 +      list_for_each_entry(wk, &ifmgd->work_list, list) {
 +              if (&wk->bss->cbss == req->bss &&
 +                  wk->state == IEEE80211_MGD_STATE_IDLE) {
 +                      found = wk;
 +                      break;
 +              }
        }
  
 -      return ieee80211_sta_commit(sdata);
 -}
 +      if (!found) {
 +              err = -ENOLINK;
 +              goto out;
 +      }
  
 -int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
 -                             const char *ie, size_t len)
 -{
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      list_del(&found->list);
  
 -      if (len == 0 && ifmgd->extra_ie_len == 0)
 -              return -EALREADY;
 +      wk = krealloc(found, sizeof(*wk) + req->ie_len, GFP_KERNEL);
 +      if (!wk) {
 +              list_add(&found->list, &ifmgd->work_list);
 +              err = -ENOMEM;
 +              goto out;
 +      }
  
 -      if (len == ifmgd->extra_ie_len && ifmgd->extra_ie &&
 -          memcmp(ifmgd->extra_ie, ie, len) == 0)
 -              return -EALREADY;
 +      list_add(&wk->list, &ifmgd->work_list);
  
 -      kfree(ifmgd->extra_ie);
 -      if (len == 0) {
 -              ifmgd->extra_ie = NULL;
 -              ifmgd->extra_ie_len = 0;
 -              return 0;
 -      }
 -      ifmgd->extra_ie = kmalloc(len, GFP_KERNEL);
 -      if (!ifmgd->extra_ie) {
 -              ifmgd->extra_ie_len = 0;
 -              return -ENOMEM;
 +      ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
 +
 +      for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
 +              if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
 +                  req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
 +                  req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104)
 +                      ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
 +
 +      sdata->local->oper_channel = req->bss->channel;
 +      ieee80211_hw_config(sdata->local, 0);
 +
 +      if (req->ie && req->ie_len) {
 +              memcpy(wk->ie, req->ie, req->ie_len);
 +              wk->ie_len = req->ie_len;
 +      } else
 +              wk->ie_len = 0;
 +
 +      if (req->prev_bssid)
 +              memcpy(wk->prev_bssid, req->prev_bssid, ETH_ALEN);
 +
 +      wk->state = IEEE80211_MGD_STATE_ASSOC;
 +      wk->tries = 0;
 +
 +      if (req->use_mfp) {
 +              ifmgd->mfp = IEEE80211_MFP_REQUIRED;
 +              ifmgd->flags |= IEEE80211_STA_MFP_ENABLED;
 +      } else {
 +              ifmgd->mfp = IEEE80211_MFP_DISABLED;
 +              ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
        }
 -      memcpy(ifmgd->extra_ie, ie, len);
 -      ifmgd->extra_ie_len = len;
 -      return 0;
 -}
  
 -int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason)
 -{
 -      printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n",
 -             sdata->dev->name, reason);
 +      if (req->crypto.control_port)
 +              ifmgd->flags |= IEEE80211_STA_CONTROL_PORT;
 +      else
 +              ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
  
 -      ieee80211_set_disassoc(sdata, true, true, reason);
 -      return 0;
 +      queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work);
 +
 +      err = 0;
 +
 + out:
 +      mutex_unlock(&ifmgd->mtx);
 +      return err;
  }
  
 -int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason)
 +int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 +                       struct cfg80211_deauth_request *req,
 +                       void *cookie)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      struct ieee80211_mgd_work *wk;
 +      const u8 *bssid = NULL;
  
 -      printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n",
 -             sdata->dev->name, reason);
 +      printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n",
 +             sdata->dev->name, req->reason_code);
 +
 +      mutex_lock(&ifmgd->mtx);
 +
 +      if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) {
 +              bssid = req->bss->bssid;
 +              ieee80211_set_disassoc(sdata);
 +      } else list_for_each_entry(wk, &ifmgd->work_list, list) {
 +              if (&wk->bss->cbss == req->bss) {
 +                      bssid = req->bss->bssid;
 +                      list_del(&wk->list);
 +                      kfree(wk);
 +                      break;
 +              }
 +      }
  
 -      if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED))
 +      /* cfg80211 should catch this... */
 +      if (WARN_ON(!bssid)) {
 +              mutex_unlock(&ifmgd->mtx);
                return -ENOLINK;
 +      }
 +
 +      mutex_unlock(&ifmgd->mtx);
 +
 +      ieee80211_send_deauth_disassoc(sdata, bssid,
 +                      IEEE80211_STYPE_DEAUTH, req->reason_code,
 +                      cookie);
  
 -      ieee80211_set_disassoc(sdata, false, true, reason);
        return 0;
  }
  
 -/* scan finished notification */
 -void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
 +int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
 +                         struct cfg80211_disassoc_request *req,
 +                         void *cookie)
  {
 -      struct ieee80211_sub_if_data *sdata = local->scan_sdata;
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
 -      /* Restart STA timers */
 -      rcu_read_lock();
 -      list_for_each_entry_rcu(sdata, &local->interfaces, list)
 -              ieee80211_restart_sta_timer(sdata);
 -      rcu_read_unlock();
 -}
 +      printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n",
 +             sdata->dev->name, req->reason_code);
  
 -int ieee80211_max_network_latency(struct notifier_block *nb,
 -                                unsigned long data, void *dummy)
 -{
 -      s32 latency_usec = (s32) data;
 -      struct ieee80211_local *local =
 -              container_of(nb, struct ieee80211_local,
 -                           network_latency_notifier);
 +      mutex_lock(&ifmgd->mtx);
  
 -      mutex_lock(&local->iflist_mtx);
 -      ieee80211_recalc_ps(local, latency_usec);
 -      mutex_unlock(&local->iflist_mtx);
 +      /* cfg80211 should catch that */
 +      if (WARN_ON(&ifmgd->associated->cbss != req->bss)) {
 +              mutex_unlock(&ifmgd->mtx);
 +              return -ENOLINK;
 +      }
 +
 +      ieee80211_set_disassoc(sdata);
 +
 +      mutex_unlock(&ifmgd->mtx);
  
 +      ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
 +                      IEEE80211_STYPE_DISASSOC, req->reason_code,
 +                      cookie);
        return 0;
  }
diff --combined net/mac80211/rx.c
@@@ -30,6 -30,7 +30,6 @@@
  static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
                                           struct tid_ampdu_rx *tid_agg_rx,
                                           struct sk_buff *skb,
 -                                         struct ieee80211_rx_status *status,
                                           u16 mpdu_seq_num,
                                           int bar_req);
  /*
@@@ -58,11 -59,11 +58,11 @@@ static struct sk_buff *remove_monitor_i
        return skb;
  }
  
 -static inline int should_drop_frame(struct ieee80211_rx_status *status,
 -                                  struct sk_buff *skb,
 +static inline int should_drop_frame(struct sk_buff *skb,
                                    int present_fcs_len,
                                    int radiotap_len)
  {
 +      struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
  
        if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
@@@ -110,10 -111,10 +110,10 @@@ ieee80211_rx_radiotap_len(struct ieee80
  static void
  ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                                 struct sk_buff *skb,
 -                               struct ieee80211_rx_status *status,
                                 struct ieee80211_rate *rate,
                                 int rtap_len)
  {
 +      struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_radiotap_header *rthdr;
        unsigned char *pos;
  
   */
  static struct sk_buff *
  ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
 -                   struct ieee80211_rx_status *status,
                     struct ieee80211_rate *rate)
  {
 +      struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
        struct ieee80211_sub_if_data *sdata;
        int needed_headroom = 0;
        struct sk_buff *skb, *skb2;
                present_fcs_len = FCS_LEN;
  
        if (!local->monitors) {
 -              if (should_drop_frame(status, origskb, present_fcs_len,
 -                                    rtap_len)) {
 +              if (should_drop_frame(origskb, present_fcs_len, rtap_len)) {
                        dev_kfree_skb(origskb);
                        return NULL;
                }
                return remove_monitor_info(local, origskb, rtap_len);
        }
  
 -      if (should_drop_frame(status, origskb, present_fcs_len, rtap_len)) {
 +      if (should_drop_frame(origskb, present_fcs_len, rtap_len)) {
                /* only need to expand headroom if necessary */
                skb = origskb;
                origskb = NULL;
  
        /* if necessary, prepend radiotap information */
        if (!(status->flag & RX_FLAG_RADIOTAP))
 -              ieee80211_add_rx_radiotap_header(local, skb, status, rate,
 +              ieee80211_add_rx_radiotap_header(local, skb, rate,
                                                 needed_headroom);
  
        skb_reset_mac_header(skb);
@@@ -419,11 -421,12 +419,11 @@@ ieee80211_rx_h_passive_scan(struct ieee
        struct sk_buff *skb = rx->skb;
  
        if (unlikely(local->hw_scanning))
 -              return ieee80211_scan_rx(rx->sdata, skb, rx->status);
 +              return ieee80211_scan_rx(rx->sdata, skb);
  
        if (unlikely(local->sw_scanning)) {
                /* drop all the other packets during a software scan anyway */
 -              if (ieee80211_scan_rx(rx->sdata, skb, rx->status)
 -                  != RX_QUEUED)
 +              if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED)
                        dev_kfree_skb(skb);
                return RX_QUEUED;
        }
@@@ -833,22 -836,28 +833,22 @@@ ieee80211_rx_h_sta_process(struct ieee8
        if (!sta)
                return RX_CONTINUE;
  
 -      /* Update last_rx only for IBSS packets which are for the current
 -       * BSSID to avoid keeping the current IBSS network alive in cases where
 -       * other STAs are using different BSSID. */
 +      /*
 +       * Update last_rx only for IBSS packets which are for the current
 +       * BSSID to avoid keeping the current IBSS network alive in cases
 +       * where other STAs start using different BSSID.
 +       */
        if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) {
                u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
                                                NL80211_IFTYPE_ADHOC);
                if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0)
                        sta->last_rx = jiffies;
 -      } else
 -      if (!is_multicast_ether_addr(hdr->addr1) ||
 -          rx->sdata->vif.type == NL80211_IFTYPE_STATION) {
 -              /* Update last_rx only for unicast frames in order to prevent
 -               * the Probe Request frames (the only broadcast frames from a
 -               * STA in infrastructure mode) from keeping a connection alive.
 +      } else if (!is_multicast_ether_addr(hdr->addr1)) {
 +              /*
                 * Mesh beacons will update last_rx when if they are found to
                 * match the current local configuration when processed.
                 */
 -              if (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
 -                  ieee80211_is_beacon(hdr->frame_control)) {
 -                      rx->sdata->u.mgd.last_beacon = jiffies;
 -              } else
 -                      sta->last_rx = jiffies;
 +              sta->last_rx = jiffies;
        }
  
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
@@@ -1478,13 -1487,10 +1478,13 @@@ ieee80211_rx_h_mesh_fwding(struct ieee8
        struct ieee80211s_hdr *mesh_hdr;
        unsigned int hdrlen;
        struct sk_buff *skb = rx->skb, *fwd_skb;
 +      struct ieee80211_local *local = rx->local;
 +      struct ieee80211_sub_if_data *sdata;
  
        hdr = (struct ieee80211_hdr *) skb->data;
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
        mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
 +      sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
  
        if (!ieee80211_is_data(hdr->frame_control))
                return RX_CONTINUE;
                return RX_DROP_MONITOR;
  
        if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){
 -              struct ieee80211_sub_if_data *sdata;
                struct mesh_path *mppath;
  
 -              sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
                rcu_read_lock();
                mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata);
                if (!mppath) {
                                                     dropped_frames_ttl);
                else {
                        struct ieee80211_hdr *fwd_hdr;
 +                      struct ieee80211_tx_info *info;
 +
                        fwd_skb = skb_copy(skb, GFP_ATOMIC);
  
                        if (!fwd_skb && net_ratelimit())
                         */
                        memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN);
                        memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN);
 -                      fwd_skb->dev = rx->local->mdev;
 +                      info = IEEE80211_SKB_CB(fwd_skb);
 +                      memset(info, 0, sizeof(*info));
 +                      info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
                        fwd_skb->iif = rx->dev->ifindex;
 -                      dev_queue_xmit(fwd_skb);
 +                      ieee80211_select_queue(local, fwd_skb);
 +                      if (is_multicast_ether_addr(fwd_hdr->addr3))
 +                              memcpy(fwd_hdr->addr1, fwd_hdr->addr3,
 +                                              ETH_ALEN);
 +                      else {
 +                              int err = mesh_nexthop_lookup(fwd_skb, sdata);
 +                              /* Failed to immediately resolve next hop:
 +                               * fwded frame was dropped or will be added
 +                               * later to the pending skb queue.  */
 +                              if (err)
 +                                      return RX_DROP_MONITOR;
 +                      }
 +                      IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
 +                                                   fwded_frames);
 +                      ieee80211_add_pending_skb(local, fwd_skb);
                }
        }
  
@@@ -1630,7 -1620,7 +1630,7 @@@ ieee80211_rx_h_ctrl(struct ieee80211_rx
                /* manage reordering buffer according to requested */
                /* sequence number */
                rcu_read_lock();
 -              ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL, NULL,
 +              ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL,
                                                 start_seq_num, 1);
                rcu_read_unlock();
                return RX_DROP_UNUSABLE;
@@@ -1654,7 -1644,12 +1654,7 @@@ static void ieee80211_process_sa_query_
  
        if (compare_ether_addr(mgmt->sa, sdata->u.mgd.bssid) != 0 ||
            compare_ether_addr(mgmt->bssid, sdata->u.mgd.bssid) != 0) {
 -              /* Not from the current AP. */
 -              return;
 -      }
 -
 -      if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATE) {
 -              /* Association in progress; ignore SA Query */
 +              /* Not from the current AP or not associated yet. */
                return;
        }
  
@@@ -1691,6 -1686,7 +1691,6 @@@ ieee80211_rx_h_action(struct ieee80211_
        struct ieee80211_local *local = rx->local;
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
 -      struct ieee80211_bss *bss;
        int len = rx->skb->len;
  
        if (!ieee80211_is_action(mgmt->frame_control))
                        if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
                                return RX_DROP_MONITOR;
  
 -                      bss = ieee80211_rx_bss_get(local, sdata->u.mgd.bssid,
 -                                         local->hw.conf.channel->center_freq,
 -                                         sdata->u.mgd.ssid,
 -                                         sdata->u.mgd.ssid_len);
 -                      if (!bss)
 -                              return RX_DROP_MONITOR;
 -
 -                      ieee80211_sta_process_chanswitch(sdata,
 -                                   &mgmt->u.action.u.chan_switch.sw_elem, bss);
 -                      ieee80211_rx_bss_put(local, bss);
 -                      break;
 +                      return ieee80211_sta_rx_mgmt(sdata, rx->skb);
                }
                break;
        case WLAN_CATEGORY_SA_QUERY:
@@@ -1811,18 -1817,19 +1811,18 @@@ ieee80211_rx_h_mgmt(struct ieee80211_rx
                return RX_DROP_MONITOR;
  
        if (ieee80211_vif_is_mesh(&sdata->vif))
 -              return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status);
 +              return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
  
        if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 -              return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status);
 +              return ieee80211_ibss_rx_mgmt(sdata, rx->skb);
  
        if (sdata->vif.type == NL80211_IFTYPE_STATION)
 -              return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
 +              return ieee80211_sta_rx_mgmt(sdata, rx->skb);
  
        return RX_DROP_MONITOR;
  }
  
 -static void ieee80211_rx_michael_mic_report(struct net_device *dev,
 -                                          struct ieee80211_hdr *hdr,
 +static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,
                                            struct ieee80211_rx_data *rx)
  {
        int keyidx;
            !ieee80211_is_auth(hdr->frame_control))
                goto ignore;
  
 -      mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL);
 +      mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL,
 +                                      GFP_ATOMIC);
   ignore:
        dev_kfree_skb(rx->skb);
        rx->skb = NULL;
@@@ -2022,8 -2028,13 +2022,8 @@@ static int prepare_for_handlers(struct 
        case NL80211_IFTYPE_STATION:
                if (!bssid)
                        return 0;
 -              if (!ieee80211_bssid_match(bssid, sdata->u.mgd.bssid)) {
 -                      if (!(rx->flags & IEEE80211_RX_IN_SCAN))
 -                              return 0;
 -                      rx->flags &= ~IEEE80211_RX_RA_MATCH;
 -              } else if (!multicast &&
 -                         compare_ether_addr(sdata->dev->dev_addr,
 -                                            hdr->addr1) != 0) {
 +              if (!multicast &&
 +                  compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
                                return 0;
                        rx->flags &= ~IEEE80211_RX_RA_MATCH;
   */
  static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
                                         struct sk_buff *skb,
 -                                       struct ieee80211_rx_status *status,
                                         struct ieee80211_rate *rate)
  {
 +      struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_hdr *hdr;
        }
  
        if ((status->flag & RX_FLAG_MMIC_ERROR)) {
 -              ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
 +              ieee80211_rx_michael_mic_report(hdr, &rx);
                return;
        }
  
@@@ -2216,21 -2227,20 +2216,21 @@@ static void ieee80211_release_reorder_f
  {
        struct ieee80211_supported_band *sband;
        struct ieee80211_rate *rate;
 -      struct ieee80211_rx_status status;
 +      struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
 +      struct ieee80211_rx_status *status;
  
 -      if (!tid_agg_rx->reorder_buf[index])
 +      if (!skb)
                goto no_frame;
  
 +      status = IEEE80211_SKB_RXCB(skb);
 +
        /* release the reordered frames to stack */
 -      memcpy(&status, tid_agg_rx->reorder_buf[index]->cb, sizeof(status));
 -      sband = hw->wiphy->bands[status.band];
 -      if (status.flag & RX_FLAG_HT)
 +      sband = hw->wiphy->bands[status->band];
 +      if (status->flag & RX_FLAG_HT)
                rate = sband->bitrates; /* TODO: HT rates */
        else
 -              rate = &sband->bitrates[status.rate_idx];
 -      __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
 -                                   &status, rate);
 +              rate = &sband->bitrates[status->rate_idx];
 +      __ieee80211_rx_handle_packet(hw, skb, rate);
        tid_agg_rx->stored_mpdu_num--;
        tid_agg_rx->reorder_buf[index] = NULL;
  
@@@ -2255,6 -2265,7 +2255,6 @@@ no_frame
  static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
                                           struct tid_ampdu_rx *tid_agg_rx,
                                           struct sk_buff *skb,
 -                                         struct ieee80211_rx_status *rxstatus,
                                           u16 mpdu_seq_num,
                                           int bar_req)
  {
        /* put the frame in the reordering buffer */
        tid_agg_rx->reorder_buf[index] = skb;
        tid_agg_rx->reorder_time[index] = jiffies;
 -      memcpy(tid_agg_rx->reorder_buf[index]->cb, rxstatus,
 -             sizeof(*rxstatus));
        tid_agg_rx->stored_mpdu_num++;
        /* release the buffer until next missing frame */
        index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn)
  }
  
  static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
 -                                   struct sk_buff *skb,
 -                                   struct ieee80211_rx_status *status)
 +                                   struct sk_buff *skb)
  {
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
  
        /* according to mpdu sequence number deal with reordering buffer */
        mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
 -      ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, status,
 +      ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
                                                mpdu_seq_num, 0);
   end_reorder:
        return ret;
   * This is the receive path handler. It is called by a low level driver when an
   * 802.11 MPDU is received from the hardware.
   */
 -void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
 -                  struct ieee80211_rx_status *status)
 +void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
  {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_rate *rate = NULL;
        struct ieee80211_supported_band *sband;
 +      struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
  
        if (status->band < 0 ||
            status->band >= IEEE80211_NUM_BANDS) {
                return;
        }
  
+       /*
+        * If we're suspending, it is possible although not too likely
+        * that we'd be receiving frames after having already partially
+        * quiesced the stack. We can't process such frames then since
+        * that might, for example, cause stations to be added or other
+        * driver callbacks be invoked.
+        */
+       if (unlikely(local->quiescing || local->suspended)) {
+               kfree_skb(skb);
+               return;
+       }
        if (status->flag & RX_FLAG_HT) {
                /* rate_idx is MCS index */
                if (WARN_ON(status->rate_idx < 0 ||
         * if it was previously present.
         * Also, frames with less than 16 bytes are dropped.
         */
 -      skb = ieee80211_rx_monitor(local, skb, status, rate);
 +      skb = ieee80211_rx_monitor(local, skb, rate);
        if (!skb) {
                rcu_read_unlock();
                return;
         * frames from other than operational channel), but that should not
         * happen in normal networks.
         */
 -      if (!ieee80211_rx_reorder_ampdu(local, skb, status))
 -              __ieee80211_rx_handle_packet(hw, skb, status, rate);
 +      if (!ieee80211_rx_reorder_ampdu(local, skb))
 +              __ieee80211_rx_handle_packet(hw, skb, rate);
  
        rcu_read_unlock();
  }
@@@ -2495,12 -2521,16 +2507,12 @@@ EXPORT_SYMBOL(__ieee80211_rx)
  
  /* This is a version of the rx handler that can be called from hard irq
   * context. Post the skb on the queue and schedule the tasklet */
 -void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
 -                        struct ieee80211_rx_status *status)
 +void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)
  {
        struct ieee80211_local *local = hw_to_local(hw);
  
        BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));
  
 -      skb->dev = local->mdev;
 -      /* copy status into skb->cb for use by tasklet */
 -      memcpy(skb->cb, status, sizeof(*status));
        skb->pkt_type = IEEE80211_RX_MSG;
        skb_queue_tail(&local->skb_queue, skb);
        tasklet_schedule(&local->tasklet);
@@@ -151,7 -151,7 +151,7 @@@ int netlbl_cfg_unlbl_map_add(const cha
                        addr6 = addr;
                        mask6 = mask;
                        map6 = kzalloc(sizeof(*map6), GFP_ATOMIC);
-                       if (map4 == NULL)
+                       if (map6 == NULL)
                                goto cfg_unlbl_map_add_failure;
                        map6->type = NETLBL_NLTYPE_UNLABELED;
                        ipv6_addr_copy(&map6->list.addr, addr6);
        return 0;
  
  cfg_unlbl_map_add_failure:
 -      if (entry != NULL)
 -              kfree(entry->domain);
 +      kfree(entry->domain);
        kfree(entry);
        kfree(addrmap);
        kfree(map4);
@@@ -384,7 -385,8 +384,7 @@@ int netlbl_cfg_cipsov4_map_add(u32 doi
  
  cfg_cipsov4_map_add_failure:
        cipso_v4_doi_putdef(doi_def);
 -      if (entry != NULL)
 -              kfree(entry->domain);
 +      kfree(entry->domain);
        kfree(entry);
        kfree(addrmap);
        kfree(addrinfo);