Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
authorDavid S. Miller <davem@davemloft.net>
Sun, 18 Nov 2012 03:00:43 +0000 (22:00 -0500)
committerDavid S. Miller <davem@davemloft.net>
Sun, 18 Nov 2012 03:00:43 +0000 (22:00 -0500)
Minor line offset auto-merges.

Signed-off-by: David S. Miller <davem@davemloft.net>
26 files changed:
1  2 
MAINTAINERS
drivers/net/ethernet/micrel/ksz884x.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/smsc95xx.c
drivers/net/vxlan.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/pcie/rx.c
net/batman-adv/soft-interface.c
net/batman-adv/translation-table.c
net/bluetooth/hci_core.c
net/bluetooth/mgmt.c
net/bluetooth/smp.c
net/core/dev.c
net/ipv4/ip_vti.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv6/ipv6_sockglue.c
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c

diff --combined MAINTAINERS
index aedb604c83e76a87aff16385abda2c26c73a25f4,bb0b27db673f3c5d0f2cbf4215dc3efbe2c17bd3..a9c794cb671ea6953a134f3e12cca15830ef7c17
@@@ -2507,6 -2507,7 +2507,7 @@@ M:      Joonyoung Shim <jy0922.shim@samsung.
  M:    Seung-Woo Kim <sw0312.kim@samsung.com>
  M:    Kyungmin Park <kyungmin.park@samsung.com>
  L:    dri-devel@lists.freedesktop.org
+ T:    git git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
  S:    Supported
  F:    drivers/gpu/drm/exynos
  F:    include/drm/exynos*
@@@ -3597,6 -3598,49 +3598,49 @@@ F:    drivers/hid/hid-hyperv.
  F:    drivers/net/hyperv/
  F:    drivers/staging/hv/
  
+ I2C OVER PARALLEL PORT
+ M:    Jean Delvare <khali@linux-fr.org>
+ L:    linux-i2c@vger.kernel.org
+ S:    Maintained
+ F:    Documentation/i2c/busses/i2c-parport
+ F:    Documentation/i2c/busses/i2c-parport-light
+ F:    drivers/i2c/busses/i2c-parport.c
+ F:    drivers/i2c/busses/i2c-parport-light.c
+ I2C/SMBUS CONTROLLER DRIVERS FOR PC
+ M:    Jean Delvare <khali@linux-fr.org>
+ L:    linux-i2c@vger.kernel.org
+ S:    Maintained
+ F:    Documentation/i2c/busses/i2c-ali1535
+ F:    Documentation/i2c/busses/i2c-ali1563
+ F:    Documentation/i2c/busses/i2c-ali15x3
+ F:    Documentation/i2c/busses/i2c-amd756
+ F:    Documentation/i2c/busses/i2c-amd8111
+ F:    Documentation/i2c/busses/i2c-i801
+ F:    Documentation/i2c/busses/i2c-nforce2
+ F:    Documentation/i2c/busses/i2c-piix4
+ F:    Documentation/i2c/busses/i2c-sis5595
+ F:    Documentation/i2c/busses/i2c-sis630
+ F:    Documentation/i2c/busses/i2c-sis96x
+ F:    Documentation/i2c/busses/i2c-via
+ F:    Documentation/i2c/busses/i2c-viapro
+ F:    drivers/i2c/busses/i2c-ali1535.c
+ F:    drivers/i2c/busses/i2c-ali1563.c
+ F:    drivers/i2c/busses/i2c-ali15x3.c
+ F:    drivers/i2c/busses/i2c-amd756.c
+ F:    drivers/i2c/busses/i2c-amd756-s4882.c
+ F:    drivers/i2c/busses/i2c-amd8111.c
+ F:    drivers/i2c/busses/i2c-i801.c
+ F:    drivers/i2c/busses/i2c-isch.c
+ F:    drivers/i2c/busses/i2c-nforce2.c
+ F:    drivers/i2c/busses/i2c-nforce2-s4985.c
+ F:    drivers/i2c/busses/i2c-piix4.c
+ F:    drivers/i2c/busses/i2c-sis5595.c
+ F:    drivers/i2c/busses/i2c-sis630.c
+ F:    drivers/i2c/busses/i2c-sis96x.c
+ F:    drivers/i2c/busses/i2c-via.c
+ F:    drivers/i2c/busses/i2c-viapro.c
  I2C/SMBUS STUB DRIVER
  M:    "Mark M. Hoffman" <mhoffman@lightlink.com>
  L:    linux-i2c@vger.kernel.org
@@@ -3604,9 -3648,8 +3648,8 @@@ S:      Maintaine
  F:    drivers/i2c/busses/i2c-stub.c
  
  I2C SUBSYSTEM
- M:    "Jean Delvare (PC drivers, core)" <khali@linux-fr.org>
+ M:    Wolfram Sang <w.sang@pengutronix.de>
  M:    "Ben Dooks (embedded platforms)" <ben-linux@fluff.org>
- M:    "Wolfram Sang (embedded platforms)" <w.sang@pengutronix.de>
  L:    linux-i2c@vger.kernel.org
  W:    http://i2c.wiki.kernel.org/
  T:    quilt kernel.org/pub/linux/kernel/people/jdelvare/linux-2.6/jdelvare-i2c/
@@@ -3617,6 -3660,13 +3660,13 @@@ F:    drivers/i2c
  F:    include/linux/i2c.h
  F:    include/linux/i2c-*.h
  
+ I2C-TAOS-EVM DRIVER
+ M:    Jean Delvare <khali@linux-fr.org>
+ L:    linux-i2c@vger.kernel.org
+ S:    Maintained
+ F:    Documentation/i2c/busses/i2c-taos-evm
+ F:    drivers/i2c/busses/i2c-taos-evm.c
  I2C-TINY-USB DRIVER
  M:    Till Harbaum <till@harbaum.org>
  L:    linux-i2c@vger.kernel.org
@@@ -3878,9 -3928,7 +3928,9 @@@ M:      Greg Rose <gregory.v.rose@intel.com
  M:    Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
  M:    Alex Duyck <alexander.h.duyck@intel.com>
  M:    John Ronciak <john.ronciak@intel.com>
 +M:    Tushar Dave <tushar.n.dave@intel.com>
  L:    e1000-devel@lists.sourceforge.net
 +W:    http://www.intel.com/support/feedback.htm
  W:    http://e1000.sourceforge.net/
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net.git
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next.git
@@@ -6367,7 -6415,6 +6417,7 @@@ F:      drivers/scsi/st
  SCTP PROTOCOL
  M:    Vlad Yasevich <vyasevich@gmail.com>
  M:    Sridhar Samudrala <sri@us.ibm.com>
 +M:    Neil Horman <nhorman@tuxdriver.com>
  L:    linux-sctp@vger.kernel.org
  W:    http://lksctp.sourceforge.net
  S:    Maintained
@@@ -7212,6 -7259,14 +7262,14 @@@ L:    linux-xtensa@linux-xtensa.or
  S:    Maintained
  F:    arch/xtensa/
  
+ THERMAL
+ M:      Zhang Rui <rui.zhang@intel.com>
+ L:      linux-pm@vger.kernel.org
+ T:      git git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git
+ S:      Supported
+ F:      drivers/thermal/
+ F:      include/linux/thermal.h
  THINKPAD ACPI EXTRAS DRIVER
  M:    Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
  L:    ibm-acpi-devel@lists.sourceforge.net
@@@ -7509,12 -7564,6 +7567,12 @@@ S:    Maintaine
  F:    Documentation/usb/acm.txt
  F:    drivers/usb/class/cdc-acm.*
  
 +USB AR5523 WIRELESS DRIVER
 +M:    Pontus Fuchs <pontus.fuchs@gmail.com>
 +L:    linux-wireless@vger.kernel.org
 +S:    Maintained
 +F:    drivers/net/wireless/ath/ar5523/
 +
  USB ATTACHED SCSI
  M:    Matthew Wilcox <willy@linux.intel.com>
  M:    Sarah Sharp <sarah.a.sharp@linux.intel.com>
@@@ -7895,13 -7944,6 +7953,6 @@@ M:     Roger Luethi <rl@hellgate.ch
  S:    Maintained
  F:    drivers/net/ethernet/via/via-rhine.c
  
- VIAPRO SMBUS DRIVER
- M:    Jean Delvare <khali@linux-fr.org>
- L:    linux-i2c@vger.kernel.org
- S:    Maintained
- F:    Documentation/i2c/busses/i2c-viapro
- F:    drivers/i2c/busses/i2c-viapro.c
  VIA SD/MMC CARD CONTROLLER DRIVER
  M:    Bruce Chang <brucechang@via.com.tw>
  M:    Harald Welte <HaraldWelte@viatech.com>
index e4ba868e232cf25a86f9041aa289430ff6620f2b,69e01977a1dd0cf14466bb397bfe6e0b27c11f36..d16ef24e622fc4e971a1c0ee36d4a8ebfee49eb6
@@@ -5459,8 -5459,10 +5459,10 @@@ static int prepare_hardware(struct net_
        rc = request_irq(dev->irq, netdev_intr, IRQF_SHARED, dev->name, dev);
        if (rc)
                return rc;
-       tasklet_enable(&hw_priv->rx_tasklet);
-       tasklet_enable(&hw_priv->tx_tasklet);
+       tasklet_init(&hw_priv->rx_tasklet, rx_proc_task,
+                    (unsigned long) hw_priv);
+       tasklet_init(&hw_priv->tx_tasklet, tx_proc_task,
+                    (unsigned long) hw_priv);
  
        hw->promiscuous = 0;
        hw->all_multi = 0;
@@@ -7033,16 -7035,6 +7035,6 @@@ static int __devinit pcidev_init(struc
        spin_lock_init(&hw_priv->hwlock);
        mutex_init(&hw_priv->lock);
  
-       /* tasklet is enabled. */
-       tasklet_init(&hw_priv->rx_tasklet, rx_proc_task,
-               (unsigned long) hw_priv);
-       tasklet_init(&hw_priv->tx_tasklet, tx_proc_task,
-               (unsigned long) hw_priv);
-       /* tasklet_enable will decrement the atomic counter. */
-       tasklet_disable(&hw_priv->rx_tasklet);
-       tasklet_disable(&hw_priv->tx_tasklet);
        for (i = 0; i < TOTAL_PORT_NUM; i++)
                init_waitqueue_head(&hw_priv->counter[i].counter);
  
@@@ -7251,7 -7243,18 +7243,7 @@@ static struct pci_driver pci_device_dri
        .remove         = pcidev_exit
  };
  
 -static int __init ksz884x_init_module(void)
 -{
 -      return pci_register_driver(&pci_device_driver);
 -}
 -
 -static void __exit ksz884x_cleanup_module(void)
 -{
 -      pci_unregister_driver(&pci_device_driver);
 -}
 -
 -module_init(ksz884x_init_module);
 -module_exit(ksz884x_cleanup_module);
 +module_pci_driver(pci_device_driver);
  
  MODULE_DESCRIPTION("KSZ8841/2 PCI network driver");
  MODULE_AUTHOR("Tristram Ha <Tristram.Ha@micrel.com>");
index ddc7b8880f60894a1470c504d49f06aee0c33910,74fab1a4015657b52bd5b4c690990120171dc509..d38bc20a60e2e63fd2d056b9070cdce57f524090
  #include <linux/atomic.h>
  #include <linux/usb/usbnet.h>
  #include <linux/usb/cdc.h>
 +#include <linux/usb/cdc_ncm.h>
  
  #define       DRIVER_VERSION                          "14-Mar-2012"
  
 -/* CDC NCM subclass 3.2.1 */
 -#define USB_CDC_NCM_NDP16_LENGTH_MIN          0x10
 -
 -/* Maximum NTB length */
 -#define       CDC_NCM_NTB_MAX_SIZE_TX                 32768   /* bytes */
 -#define       CDC_NCM_NTB_MAX_SIZE_RX                 32768   /* bytes */
 -
 -/* Minimum value for MaxDatagramSize, ch. 6.2.9 */
 -#define       CDC_NCM_MIN_DATAGRAM_SIZE               1514    /* bytes */
 -
 -#define       CDC_NCM_MIN_TX_PKT                      512     /* bytes */
 -
 -/* Default value for MaxDatagramSize */
 -#define       CDC_NCM_MAX_DATAGRAM_SIZE               8192    /* bytes */
 -
 -/*
 - * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting
 - * the last NULL entry.
 - */
 -#define       CDC_NCM_DPT_DATAGRAMS_MAX               40
 -
 -/* Restart the timer, if amount of datagrams is less than given value */
 -#define       CDC_NCM_RESTART_TIMER_DATAGRAM_CNT      3
 -#define       CDC_NCM_TIMER_PENDING_CNT               2
 -#define CDC_NCM_TIMER_INTERVAL                        (400UL * NSEC_PER_USEC)
 -
 -/* The following macro defines the minimum header space */
 -#define       CDC_NCM_MIN_HDR_SIZE \
 -      (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \
 -      (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
 -
 -struct cdc_ncm_data {
 -      struct usb_cdc_ncm_nth16 nth16;
 -      struct usb_cdc_ncm_ndp16 ndp16;
 -      struct usb_cdc_ncm_dpe16 dpe16[CDC_NCM_DPT_DATAGRAMS_MAX + 1];
 -};
 -
 -struct cdc_ncm_ctx {
 -      struct cdc_ncm_data tx_ncm;
 -      struct usb_cdc_ncm_ntb_parameters ncm_parm;
 -      struct hrtimer tx_timer;
 -      struct tasklet_struct bh;
 -
 -      const struct usb_cdc_ncm_desc *func_desc;
 -      const struct usb_cdc_header_desc *header_desc;
 -      const struct usb_cdc_union_desc *union_desc;
 -      const struct usb_cdc_ether_desc *ether_desc;
 -
 -      struct net_device *netdev;
 -      struct usb_device *udev;
 -      struct usb_host_endpoint *in_ep;
 -      struct usb_host_endpoint *out_ep;
 -      struct usb_host_endpoint *status_ep;
 -      struct usb_interface *intf;
 -      struct usb_interface *control;
 -      struct usb_interface *data;
 -
 -      struct sk_buff *tx_curr_skb;
 -      struct sk_buff *tx_rem_skb;
 -
 -      spinlock_t mtx;
 -      atomic_t stop;
 -
 -      u32 tx_timer_pending;
 -      u32 tx_curr_offset;
 -      u32 tx_curr_last_offset;
 -      u32 tx_curr_frame_num;
 -      u32 rx_speed;
 -      u32 tx_speed;
 -      u32 rx_max;
 -      u32 tx_max;
 -      u32 max_datagram_size;
 -      u16 tx_max_datagrams;
 -      u16 tx_remainder;
 -      u16 tx_modulus;
 -      u16 tx_ndp_modulus;
 -      u16 tx_seq;
 -      u16 rx_seq;
 -      u16 connected;
 -};
 -
  static void cdc_ncm_txpath_bh(unsigned long param);
  static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
  static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
@@@ -78,19 -158,17 +78,19 @@@ static u8 cdc_ncm_setup(struct cdc_ncm_
        u8 flags;
        u8 iface_no;
        int err;
 +      int eth_hlen;
        u16 ntb_fmt_supported;
 +      u32 min_dgram_size;
 +      u32 min_hdr_size;
 +      struct usbnet *dev = netdev_priv(ctx->netdev);
  
        iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
  
 -      err = usb_control_msg(ctx->udev,
 -                              usb_rcvctrlpipe(ctx->udev, 0),
 -                              USB_CDC_GET_NTB_PARAMETERS,
 -                              USB_TYPE_CLASS | USB_DIR_IN
 -                               | USB_RECIP_INTERFACE,
 -                              0, iface_no, &ctx->ncm_parm,
 -                              sizeof(ctx->ncm_parm), 10000);
 +      err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,
 +                            USB_TYPE_CLASS | USB_DIR_IN
 +                            |USB_RECIP_INTERFACE,
 +                            0, iface_no, &ctx->ncm_parm,
 +                            sizeof(ctx->ncm_parm));
        if (err < 0) {
                pr_debug("failed GET_NTB_PARAMETERS\n");
                return 1;
        ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams);
        ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported);
  
 -      if (ctx->func_desc != NULL)
 +      eth_hlen = ETH_HLEN;
 +      min_dgram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
 +      min_hdr_size = CDC_NCM_MIN_HDR_SIZE;
 +      if (ctx->mbim_desc != NULL) {
 +              flags = ctx->mbim_desc->bmNetworkCapabilities;
 +              eth_hlen = 0;
 +              min_dgram_size = CDC_MBIM_MIN_DATAGRAM_SIZE;
 +              min_hdr_size = 0;
 +      } else if (ctx->func_desc != NULL) {
                flags = ctx->func_desc->bmNetworkCapabilities;
 -      else
 +      } else {
                flags = 0;
 +      }
  
        pr_debug("dwNtbInMaxSize=%u dwNtbOutMaxSize=%u "
                 "wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u "
  
        /* inform device about NTB input size changes */
        if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
 +              __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
  
 -              if (flags & USB_CDC_NCM_NCAP_NTB_INPUT_SIZE) {
 -                      struct usb_cdc_ncm_ndp_input_size *ndp_in_sz;
 -
 -                      ndp_in_sz = kzalloc(sizeof(*ndp_in_sz), GFP_KERNEL);
 -                      if (!ndp_in_sz) {
 -                              err = -ENOMEM;
 -                              goto size_err;
 -                      }
 -
 -                      err = usb_control_msg(ctx->udev,
 -                                      usb_sndctrlpipe(ctx->udev, 0),
 -                                      USB_CDC_SET_NTB_INPUT_SIZE,
 -                                      USB_TYPE_CLASS | USB_DIR_OUT
 -                                       | USB_RECIP_INTERFACE,
 -                                      0, iface_no, ndp_in_sz, 8, 1000);
 -                      kfree(ndp_in_sz);
 -              } else {
 -                      __le32 *dwNtbInMaxSize;
 -                      dwNtbInMaxSize = kzalloc(sizeof(*dwNtbInMaxSize),
 -                                      GFP_KERNEL);
 -                      if (!dwNtbInMaxSize) {
 -                              err = -ENOMEM;
 -                              goto size_err;
 -                      }
 -                      *dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
 -
 -                      err = usb_control_msg(ctx->udev,
 -                                      usb_sndctrlpipe(ctx->udev, 0),
 -                                      USB_CDC_SET_NTB_INPUT_SIZE,
 -                                      USB_TYPE_CLASS | USB_DIR_OUT
 -                                       | USB_RECIP_INTERFACE,
 -                                      0, iface_no, dwNtbInMaxSize, 4, 1000);
 -                      kfree(dwNtbInMaxSize);
 -              }
 -size_err:
 +              err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
 +                                     USB_TYPE_CLASS | USB_DIR_OUT
 +                                     | USB_RECIP_INTERFACE,
 +                                     0, iface_no, &dwNtbInMaxSize, 4);
                if (err < 0)
                        pr_debug("Setting NTB Input Size failed\n");
        }
  
        /* verify maximum size of transmitted NTB in bytes */
        if ((ctx->tx_max <
 -          (CDC_NCM_MIN_HDR_SIZE + CDC_NCM_MIN_DATAGRAM_SIZE)) ||
 +          (min_hdr_size + min_dgram_size)) ||
            (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX)) {
                pr_debug("Using default maximum transmit length=%d\n",
                                                CDC_NCM_NTB_MAX_SIZE_TX);
        }
  
        /* adjust TX-remainder according to NCM specification. */
 -      ctx->tx_remainder = ((ctx->tx_remainder - ETH_HLEN) &
 -                                              (ctx->tx_modulus - 1));
 +      ctx->tx_remainder = ((ctx->tx_remainder - eth_hlen) &
 +                           (ctx->tx_modulus - 1));
  
        /* additional configuration */
  
        /* set CRC Mode */
        if (flags & USB_CDC_NCM_NCAP_CRC_MODE) {
 -              err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0),
 -                              USB_CDC_SET_CRC_MODE,
 -                              USB_TYPE_CLASS | USB_DIR_OUT
 -                               | USB_RECIP_INTERFACE,
 -                              USB_CDC_NCM_CRC_NOT_APPENDED,
 -                              iface_no, NULL, 0, 1000);
 +              err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE,
 +                                     USB_TYPE_CLASS | USB_DIR_OUT
 +                                     | USB_RECIP_INTERFACE,
 +                                     USB_CDC_NCM_CRC_NOT_APPENDED,
 +                                     iface_no, NULL, 0);
                if (err < 0)
                        pr_debug("Setting CRC mode off failed\n");
        }
  
        /* set NTB format, if both formats are supported */
        if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) {
 -              err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0),
 -                              USB_CDC_SET_NTB_FORMAT, USB_TYPE_CLASS
 -                               | USB_DIR_OUT | USB_RECIP_INTERFACE,
 -                              USB_CDC_NCM_NTB16_FORMAT,
 -                              iface_no, NULL, 0, 1000);
 +              err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
 +                                     USB_TYPE_CLASS | USB_DIR_OUT
 +                                     | USB_RECIP_INTERFACE,
 +                                     USB_CDC_NCM_NTB16_FORMAT,
 +                                     iface_no, NULL, 0);
                if (err < 0)
                        pr_debug("Setting NTB format to 16-bit failed\n");
        }
  
 -      ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
 +      ctx->max_datagram_size = min_dgram_size;
  
        /* set Max Datagram Size (MTU) */
        if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) {
 -              __le16 *max_datagram_size;
 -              u16 eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
 -
 -              max_datagram_size = kzalloc(sizeof(*max_datagram_size),
 -                              GFP_KERNEL);
 -              if (!max_datagram_size) {
 -                      err = -ENOMEM;
 +              __le16 max_datagram_size;
 +              u16 eth_max_sz;
 +              if (ctx->ether_desc != NULL)
 +                      eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
 +              else if (ctx->mbim_desc != NULL)
 +                      eth_max_sz = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
 +              else
                        goto max_dgram_err;
 -              }
  
 -              err = usb_control_msg(ctx->udev, usb_rcvctrlpipe(ctx->udev, 0),
 -                              USB_CDC_GET_MAX_DATAGRAM_SIZE,
 -                              USB_TYPE_CLASS | USB_DIR_IN
 -                               | USB_RECIP_INTERFACE,
 -                              0, iface_no, max_datagram_size,
 -                              2, 1000);
 +              err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE,
 +                                    USB_TYPE_CLASS | USB_DIR_IN
 +                                    | USB_RECIP_INTERFACE,
 +                                    0, iface_no, &max_datagram_size, 2);
                if (err < 0) {
                        pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n",
 -                                              CDC_NCM_MIN_DATAGRAM_SIZE);
 +                               min_dgram_size);
                } else {
                        ctx->max_datagram_size =
 -                              le16_to_cpu(*max_datagram_size);
 +                              le16_to_cpu(max_datagram_size);
                        /* Check Eth descriptor value */
                        if (ctx->max_datagram_size > eth_max_sz)
                                        ctx->max_datagram_size = eth_max_sz;
  
                        if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE)
 -                              ctx->max_datagram_size =
 -                                              CDC_NCM_MAX_DATAGRAM_SIZE;
 +                              ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE;
  
 -                      if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE)
 -                              ctx->max_datagram_size =
 -                                      CDC_NCM_MIN_DATAGRAM_SIZE;
 +                      if (ctx->max_datagram_size < min_dgram_size)
 +                              ctx->max_datagram_size = min_dgram_size;
  
                        /* if value changed, update device */
                        if (ctx->max_datagram_size !=
 -                                      le16_to_cpu(*max_datagram_size)) {
 -                              err = usb_control_msg(ctx->udev,
 -                                              usb_sndctrlpipe(ctx->udev, 0),
 +                                      le16_to_cpu(max_datagram_size)) {
 +                              err = usbnet_write_cmd(dev,
                                                USB_CDC_SET_MAX_DATAGRAM_SIZE,
                                                USB_TYPE_CLASS | USB_DIR_OUT
                                                 | USB_RECIP_INTERFACE,
                                                0,
 -                                              iface_no, max_datagram_size,
 -                                              2, 1000);
 +                                              iface_no, &max_datagram_size,
 +                                              2);
                                if (err < 0)
                                        pr_debug("SET_MAX_DGRAM_SIZE failed\n");
                        }
                }
 -              kfree(max_datagram_size);
        }
  
  max_dgram_err:
 -      if (ctx->netdev->mtu != (ctx->max_datagram_size - ETH_HLEN))
 -              ctx->netdev->mtu = ctx->max_datagram_size - ETH_HLEN;
 +      if (ctx->netdev->mtu != (ctx->max_datagram_size - eth_hlen))
 +              ctx->netdev->mtu = ctx->max_datagram_size - eth_hlen;
  
        return 0;
  }
@@@ -344,7 -451,7 +344,7 @@@ static const struct ethtool_ops cdc_ncm
        .nway_reset = usbnet_nway_reset,
  };
  
 -static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
 +int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting)
  {
        struct cdc_ncm_ctx *ctx;
        struct usb_driver *driver;
                        ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf;
                        break;
  
 +              case USB_CDC_MBIM_TYPE:
 +                      if (buf[0] < sizeof(*(ctx->mbim_desc)))
 +                              break;
 +
 +                      ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf;
 +                      break;
 +
                default:
                        break;
                }
@@@ -437,13 -537,15 +437,15 @@@ advance
  
        /* check if we got everything */
        if ((ctx->control == NULL) || (ctx->data == NULL) ||
 -          (ctx->ether_desc == NULL) || (ctx->control != intf))
 +          ((!ctx->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf))))
                goto error;
  
-       /* claim interfaces, if any */
-       temp = usb_driver_claim_interface(driver, ctx->data, dev);
-       if (temp)
-               goto error;
+       /* claim data interface, if different from control */
+       if (ctx->data != ctx->control) {
+               temp = usb_driver_claim_interface(driver, ctx->data, dev);
+               if (temp)
+                       goto error;
+       }
  
        iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;
  
                goto error2;
  
        /* configure data interface */
 -      temp = usb_set_interface(dev->udev, iface_no, 1);
 +      temp = usb_set_interface(dev->udev, iface_no, data_altsetting);
        if (temp)
                goto error2;
  
        usb_set_intfdata(ctx->control, dev);
        usb_set_intfdata(ctx->intf, dev);
  
 -      temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress);
 -      if (temp)
 -              goto error2;
 +      if (ctx->ether_desc) {
 +              temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress);
 +              if (temp)
 +                      goto error2;
 +              dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
 +      }
  
 -      dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
  
        dev->in = usb_rcvbulkpipe(dev->udev,
                ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
        dev->status = ctx->status_ep;
        dev->rx_urb_size = ctx->rx_max;
  
 -      /*
 -       * We should get an event when network connection is "connected" or
 -       * "disconnected". Set network connection in "disconnected" state
 -       * (carrier is OFF) during attach, so the IP network stack does not
 -       * start IPv6 negotiation and more.
 -       */
 -      netif_carrier_off(dev->net);
        ctx->tx_speed = ctx->rx_speed = 0;
        return 0;
  
@@@ -502,9 -609,8 +504,9 @@@ error
        dev_info(&dev->udev->dev, "bind() failure\n");
        return -ENODEV;
  }
 +EXPORT_SYMBOL_GPL(cdc_ncm_bind_common);
  
 -static void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
 +void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
  {
        struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
        struct usb_driver *driver = driver_of(intf);
  
        tasklet_kill(&ctx->bh);
  
+       /* handle devices with combined control and data interface */
+       if (ctx->control == ctx->data)
+               ctx->data = NULL;
        /* disconnect master --> disconnect slave */
        if (intf == ctx->control && ctx->data) {
                usb_set_intfdata(ctx->data, NULL);
        usb_set_intfdata(ctx->intf, NULL);
        cdc_ncm_free(ctx);
  }
 +EXPORT_SYMBOL_GPL(cdc_ncm_unbind);
  
 -static void cdc_ncm_zero_fill(u8 *ptr, u32 first, u32 end, u32 max)
 +static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
  {
 -      if (first >= max)
 -              return;
 -      if (first >= end)
 -              return;
 -      if (end > max)
 -              end = max;
 -      memset(ptr + first, 0, end - first);
 +      int ret;
 +
 +      /* The MBIM spec defines a NCM compatible default altsetting,
 +       * which we may have matched:
 +       *
 +       *  "Functions that implement both NCM 1.0 and MBIM (an
 +       *   “NCM/MBIM function”) according to this recommendation
 +       *   shall provide two alternate settings for the
 +       *   Communication Interface.  Alternate setting 0, and the
 +       *   associated class and endpoint descriptors, shall be
 +       *   constructed according to the rules given for the
 +       *   Communication Interface in section 5 of [USBNCM10].
 +       *   Alternate setting 1, and the associated class and
 +       *   endpoint descriptors, shall be constructed according to
 +       *   the rules given in section 6 (USB Device Model) of this
 +       *   specification."
 +       *
 +       * Do not bind to such interfaces, allowing cdc_mbim to handle
 +       * them
 +       */
 +#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)
 +      if ((intf->num_altsetting == 2) &&
 +          !usb_set_interface(dev->udev,
 +                             intf->cur_altsetting->desc.bInterfaceNumber,
 +                             CDC_NCM_COMM_ALTSETTING_MBIM) &&
 +          cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
 +              return -ENODEV;
 +#endif
 +
 +      /* NCM data altsetting is always 1 */
 +      ret = cdc_ncm_bind_common(dev, intf, 1);
 +
 +      /*
 +       * We should get an event when network connection is "connected" or
 +       * "disconnected". Set network connection in "disconnected" state
 +       * (carrier is OFF) during attach, so the IP network stack does not
 +       * start IPv6 negotiation and more.
 +       */
 +      netif_carrier_off(dev->net);
 +      return ret;
  }
  
 -static struct sk_buff *
 -cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
 +static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max)
  {
 +      size_t align = ALIGN(skb->len, modulus) - skb->len + remainder;
 +
 +      if (skb->len + align > max)
 +              align = max - skb->len;
 +      if (align && skb_tailroom(skb) >= align)
 +              memset(skb_put(skb, align), 0, align);
 +}
 +
 +/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly
 + * allocating a new one within skb
 + */
 +static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
 +{
 +      struct usb_cdc_ncm_ndp16 *ndp16 = NULL;
 +      struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
 +      size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex);
 +
 +      /* follow the chain of NDPs, looking for a match */
 +      while (ndpoffset) {
 +              ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
 +              if  (ndp16->dwSignature == sign)
 +                      return ndp16;
 +              ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
 +      }
 +
 +      /* align new NDP */
 +      cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);
 +
 +      /* verify that there is room for the NDP and the datagram (reserve) */
 +      if ((ctx->tx_max - skb->len - reserve) < CDC_NCM_NDP_SIZE)
 +              return NULL;
 +
 +      /* link to it */
 +      if (ndp16)
 +              ndp16->wNextNdpIndex = cpu_to_le16(skb->len);
 +      else
 +              nth16->wNdpIndex = cpu_to_le16(skb->len);
 +
 +      /* push a new empty NDP */
 +      ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE);
 +      ndp16->dwSignature = sign;
 +      ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
 +      return ndp16;
 +}
 +
 +struct sk_buff *
 +cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
 +{
 +      struct usb_cdc_ncm_nth16 *nth16;
 +      struct usb_cdc_ncm_ndp16 *ndp16;
        struct sk_buff *skb_out;
 -      u32 rem;
 -      u32 offset;
 -      u32 last_offset;
 -      u16 n = 0, index;
 +      u16 n = 0, index, ndplen;
        u8 ready2send = 0;
  
        /* if there is a remaining skb, it gets priority */
 -      if (skb != NULL)
 +      if (skb != NULL) {
                swap(skb, ctx->tx_rem_skb);
 -      else
 +              swap(sign, ctx->tx_rem_sign);
 +      } else {
                ready2send = 1;
 -
 -      /*
 -       * +----------------+
 -       * | skb_out        |
 -       * +----------------+
 -       *           ^ offset
 -       *        ^ last_offset
 -       */
 +      }
  
        /* check if we are resuming an OUT skb */
 -      if (ctx->tx_curr_skb != NULL) {
 -              /* pop variables */
 -              skb_out = ctx->tx_curr_skb;
 -              offset = ctx->tx_curr_offset;
 -              last_offset = ctx->tx_curr_last_offset;
 -              n = ctx->tx_curr_frame_num;
 +      skb_out = ctx->tx_curr_skb;
  
 -      } else {
 -              /* reset variables */
 +      /* allocate a new OUT skb */
 +      if (!skb_out) {
                skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC);
                if (skb_out == NULL) {
                        if (skb != NULL) {
                        }
                        goto exit_no_skb;
                }
 +              /* fill out the initial 16-bit NTB header */
 +              nth16 = (struct usb_cdc_ncm_nth16 *)memset(skb_put(skb_out, sizeof(struct usb_cdc_ncm_nth16)), 0, sizeof(struct usb_cdc_ncm_nth16));
 +              nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
 +              nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));
 +              nth16->wSequence = cpu_to_le16(ctx->tx_seq++);
  
 -              /* make room for NTH and NDP */
 -              offset = ALIGN(sizeof(struct usb_cdc_ncm_nth16),
 -                                      ctx->tx_ndp_modulus) +
 -                                      sizeof(struct usb_cdc_ncm_ndp16) +
 -                                      (ctx->tx_max_datagrams + 1) *
 -                                      sizeof(struct usb_cdc_ncm_dpe16);
 -
 -              /* store last valid offset before alignment */
 -              last_offset = offset;
 -              /* align first Datagram offset correctly */
 -              offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder;
 -              /* zero buffer till the first IP datagram */
 -              cdc_ncm_zero_fill(skb_out->data, 0, offset, offset);
 -              n = 0;
 +              /* count total number of frames in this NTB */
                ctx->tx_curr_frame_num = 0;
        }
  
 -      for (; n < ctx->tx_max_datagrams; n++) {
 -              /* check if end of transmit buffer is reached */
 -              if (offset >= ctx->tx_max) {
 -                      ready2send = 1;
 -                      break;
 -              }
 -              /* compute maximum buffer size */
 -              rem = ctx->tx_max - offset;
 -
 +      for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
 +              /* send any remaining skb first */
                if (skb == NULL) {
                        skb = ctx->tx_rem_skb;
 +                      sign = ctx->tx_rem_sign;
                        ctx->tx_rem_skb = NULL;
  
                        /* check for end of skb */
                                break;
                }
  
 -              if (skb->len > rem) {
 +              /* get the appropriate NDP for this skb */
 +              ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);
 +
 +              /* align beginning of next frame */
 +              cdc_ncm_align_tail(skb_out,  ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max);
 +
 +              /* check if we had enough room left for both NDP and frame */
 +              if (!ndp16 || skb_out->len + skb->len > ctx->tx_max) {
                        if (n == 0) {
                                /* won't fit, MTU problem? */
                                dev_kfree_skb_any(skb);
                                        ctx->netdev->stats.tx_dropped++;
                                }
                                ctx->tx_rem_skb = skb;
 +                              ctx->tx_rem_sign = sign;
                                skb = NULL;
                                ready2send = 1;
                        }
                        break;
                }
  
 -              memcpy(((u8 *)skb_out->data) + offset, skb->data, skb->len);
 -
 -              ctx->tx_ncm.dpe16[n].wDatagramLength = cpu_to_le16(skb->len);
 -              ctx->tx_ncm.dpe16[n].wDatagramIndex = cpu_to_le16(offset);
 -
 -              /* update offset */
 -              offset += skb->len;
 -
 -              /* store last valid offset before alignment */
 -              last_offset = offset;
 -
 -              /* align offset correctly */
 -              offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder;
 +              /* calculate frame number withing this NDP */
 +              ndplen = le16_to_cpu(ndp16->wLength);
 +              index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;
  
 -              /* zero padding */
 -              cdc_ncm_zero_fill(skb_out->data, last_offset, offset,
 -                                                              ctx->tx_max);
 +              /* OK, add this skb */
 +              ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);
 +              ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
 +              ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
 +              memcpy(skb_put(skb_out, skb->len), skb->data, skb->len);
                dev_kfree_skb_any(skb);
                skb = NULL;
 +
 +              /* send now if this NDP is full */
 +              if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
 +                      ready2send = 1;
 +                      break;
 +              }
        }
  
        /* free up any dangling skb */
                /* wait for more frames */
                /* push variables */
                ctx->tx_curr_skb = skb_out;
 -              ctx->tx_curr_offset = offset;
 -              ctx->tx_curr_last_offset = last_offset;
                goto exit_no_skb;
  
        } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) {
                /* wait for more frames */
                /* push variables */
                ctx->tx_curr_skb = skb_out;
 -              ctx->tx_curr_offset = offset;
 -              ctx->tx_curr_last_offset = last_offset;
                /* set the pending count */
                if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
                        ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT;
                /* variables will be reset at next call */
        }
  
 -      /* check for overflow */
 -      if (last_offset > ctx->tx_max)
 -              last_offset = ctx->tx_max;
 -
 -      /* revert offset */
 -      offset = last_offset;
 -
        /*
         * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes,
         * we send buffers as it is. If we get more data, it would be more
         * efficient for USB HS mobile device with DMA engine to receive a full
         * size NTB, than canceling DMA transfer and receiving a short packet.
         */
 -      if (offset > CDC_NCM_MIN_TX_PKT)
 -              offset = ctx->tx_max;
 -
 -      /* final zero padding */
 -      cdc_ncm_zero_fill(skb_out->data, last_offset, offset, ctx->tx_max);
 -
 -      /* store last offset */
 -      last_offset = offset;
 -
 -      if (((last_offset < ctx->tx_max) && ((last_offset %
 -                      le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) ||
 -          (((last_offset == ctx->tx_max) && ((ctx->tx_max %
 -              le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) &&
 -              (ctx->tx_max < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)))) {
 -              /* force short packet */
 -              *(((u8 *)skb_out->data) + last_offset) = 0;
 -              last_offset++;
 -      }
 -
 -      /* zero the rest of the DPEs plus the last NULL entry */
 -      for (; n <= CDC_NCM_DPT_DATAGRAMS_MAX; n++) {
 -              ctx->tx_ncm.dpe16[n].wDatagramLength = 0;
 -              ctx->tx_ncm.dpe16[n].wDatagramIndex = 0;
 -      }
 +      if (skb_out->len > CDC_NCM_MIN_TX_PKT)
 +              /* final zero padding */
 +              memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len);
  
 -      /* fill out 16-bit NTB header */
 -      ctx->tx_ncm.nth16.dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
 -      ctx->tx_ncm.nth16.wHeaderLength =
 -                                      cpu_to_le16(sizeof(ctx->tx_ncm.nth16));
 -      ctx->tx_ncm.nth16.wSequence = cpu_to_le16(ctx->tx_seq);
 -      ctx->tx_ncm.nth16.wBlockLength = cpu_to_le16(last_offset);
 -      index = ALIGN(sizeof(struct usb_cdc_ncm_nth16), ctx->tx_ndp_modulus);
 -      ctx->tx_ncm.nth16.wNdpIndex = cpu_to_le16(index);
 -
 -      memcpy(skb_out->data, &(ctx->tx_ncm.nth16), sizeof(ctx->tx_ncm.nth16));
 -      ctx->tx_seq++;
 -
 -      /* fill out 16-bit NDP table */
 -      ctx->tx_ncm.ndp16.dwSignature =
 -                              cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN);
 -      rem = sizeof(ctx->tx_ncm.ndp16) + ((ctx->tx_curr_frame_num + 1) *
 -                                      sizeof(struct usb_cdc_ncm_dpe16));
 -      ctx->tx_ncm.ndp16.wLength = cpu_to_le16(rem);
 -      ctx->tx_ncm.ndp16.wNextNdpIndex = 0; /* reserved */
 -
 -      memcpy(((u8 *)skb_out->data) + index,
 -                                              &(ctx->tx_ncm.ndp16),
 -                                              sizeof(ctx->tx_ncm.ndp16));
 +      /* do we need to prevent a ZLP? */
 +      if (((skb_out->len % le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0) &&
 +          (skb_out->len < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)) && skb_tailroom(skb_out))
 +              *skb_put(skb_out, 1) = 0;       /* force short packet */
  
 -      memcpy(((u8 *)skb_out->data) + index + sizeof(ctx->tx_ncm.ndp16),
 -                                      &(ctx->tx_ncm.dpe16),
 -                                      (ctx->tx_curr_frame_num + 1) *
 -                                      sizeof(struct usb_cdc_ncm_dpe16));
 -
 -      /* set frame length */
 -      skb_put(skb_out, last_offset);
 +      /* set final frame length */
 +      nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
 +      nth16->wBlockLength = cpu_to_le16(skb_out->len);
  
        /* return skb */
        ctx->tx_curr_skb = NULL;
@@@ -784,7 -888,6 +790,7 @@@ exit_no_skb
                cdc_ncm_tx_timeout_start(ctx);
        return NULL;
  }
 +EXPORT_SYMBOL_GPL(cdc_ncm_fill_tx_frame);
  
  static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)
  {
@@@ -819,8 -922,6 +825,8 @@@ static void cdc_ncm_txpath_bh(unsigned 
                netif_tx_lock_bh(ctx->netdev);
                usbnet_start_xmit(NULL, ctx->netdev);
                netif_tx_unlock_bh(ctx->netdev);
 +      } else {
 +              spin_unlock_bh(&ctx->mtx);
        }
  }
  
@@@ -841,7 -942,7 +847,7 @@@ cdc_ncm_tx_fixup(struct usbnet *dev, st
                goto error;
  
        spin_lock_bh(&ctx->mtx);
 -      skb_out = cdc_ncm_fill_tx_frame(ctx, skb);
 +      skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));
        spin_unlock_bh(&ctx->mtx);
        return skb_out;
  
@@@ -852,12 -953,17 +858,12 @@@ error
        return NULL;
  }
  
 -static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
 +/* verify NTB header and return offset of first NDP, or negative error */
 +int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
  {
 -      struct sk_buff *skb;
 -      struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
 -      int len;
 -      int nframes;
 -      int x;
 -      int offset;
        struct usb_cdc_ncm_nth16 *nth16;
 -      struct usb_cdc_ncm_ndp16 *ndp16;
 -      struct usb_cdc_ncm_dpe16 *dpe16;
 +      int len;
 +      int ret = -EINVAL;
  
        if (ctx == NULL)
                goto error;
        }
        ctx->rx_seq = le16_to_cpu(nth16->wSequence);
  
 -      len = le16_to_cpu(nth16->wNdpIndex);
 -      if ((len + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
 -              pr_debug("invalid DPT16 index <%u>\n",
 -                                      le16_to_cpu(nth16->wNdpIndex));
 -              goto error;
 -      }
 +      ret = le16_to_cpu(nth16->wNdpIndex);
 +error:
 +      return ret;
 +}
 +EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16);
  
 -      ndp16 = (struct usb_cdc_ncm_ndp16 *)(((u8 *)skb_in->data) + len);
 +/* verify NDP header and return number of datagrams, or negative error */
 +int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)
 +{
 +      struct usb_cdc_ncm_ndp16 *ndp16;
 +      int ret = -EINVAL;
  
 -      if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) {
 -              pr_debug("invalid DPT16 signature <%u>\n",
 -                                      le32_to_cpu(ndp16->dwSignature));
 +      if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
 +              pr_debug("invalid NDP offset  <%u>\n", ndpoffset);
                goto error;
        }
 +      ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
  
        if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
                pr_debug("invalid DPT16 length <%u>\n",
                goto error;
        }
  
 -      nframes = ((le16_to_cpu(ndp16->wLength) -
 +      ret = ((le16_to_cpu(ndp16->wLength) -
                                        sizeof(struct usb_cdc_ncm_ndp16)) /
                                        sizeof(struct usb_cdc_ncm_dpe16));
 -      nframes--; /* we process NDP entries except for the last one */
 -
 -      len += sizeof(struct usb_cdc_ncm_ndp16);
 +      ret--; /* we process NDP entries except for the last one */
  
 -      if ((len + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) >
 +      if ((sizeof(struct usb_cdc_ncm_ndp16) + ret * (sizeof(struct usb_cdc_ncm_dpe16))) >
                                                                skb_in->len) {
 -              pr_debug("Invalid nframes = %d\n", nframes);
 -              goto error;
 +              pr_debug("Invalid nframes = %d\n", ret);
 +              ret = -EINVAL;
        }
  
 -      dpe16 = (struct usb_cdc_ncm_dpe16 *)(((u8 *)skb_in->data) + len);
 +error:
 +      return ret;
 +}
 +EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16);
 +
 +static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
 +{
 +      struct sk_buff *skb;
 +      struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
 +      int len;
 +      int nframes;
 +      int x;
 +      int offset;
 +      struct usb_cdc_ncm_ndp16 *ndp16;
 +      struct usb_cdc_ncm_dpe16 *dpe16;
 +      int ndpoffset;
 +      int loopcount = 50; /* arbitrary max preventing infinite loop */
 +
 +      ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
 +      if (ndpoffset < 0)
 +              goto error;
 +
 +next_ndp:
 +      nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset);
 +      if (nframes < 0)
 +              goto error;
 +
 +      ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
 +
 +      if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) {
 +              pr_debug("invalid DPT16 signature <%u>\n",
 +                       le32_to_cpu(ndp16->dwSignature));
 +              goto err_ndp;
 +      }
 +      dpe16 = ndp16->dpe16;
  
        for (x = 0; x < nframes; x++, dpe16++) {
                offset = le16_to_cpu(dpe16->wDatagramIndex);
                 */
                if ((offset == 0) || (len == 0)) {
                        if (!x)
 -                              goto error; /* empty NTB */
 +                              goto err_ndp; /* empty NTB */
                        break;
                }
  
                                        "offset[%u]=%u, length=%u, skb=%p\n",
                                        x, offset, len, skb_in);
                        if (!x)
 -                              goto error;
 +                              goto err_ndp;
                        break;
  
                } else {
                        usbnet_skb_return(dev, skb);
                }
        }
 +err_ndp:
 +      /* are there more NDPs to process? */
 +      ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
 +      if (ndpoffset && loopcount--)
 +              goto next_ndp;
 +
        return 1;
  error:
        return 0;
@@@ -1066,7 -1131,7 +1072,7 @@@ static void cdc_ncm_status(struct usbne
                 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be
                 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
                 */
 -              ctx->connected = event->wValue;
 +              ctx->connected = le16_to_cpu(event->wValue);
  
                printk(KERN_INFO KBUILD_MODNAME ": %s: network connection:"
                        " %sconnected\n",
@@@ -1186,6 -1251,14 +1192,14 @@@ static const struct usb_device_id cdc_d
          .driver_info = (unsigned long) &wwan_info,
        },
  
+       /* Huawei NCM devices disguised as vendor specific */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16),
+         .driver_info = (unsigned long)&wwan_info,
+       },
+       { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46),
+         .driver_info = (unsigned long)&wwan_info,
+       },
        /* Generic CDC-NCM devices */
        { USB_INTERFACE_INFO(USB_CLASS_COMM,
                USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
index e07f70b5f39c3a9a54925bed00bbb50f91d6c1d7,362cb8cfeb92b6021f48995226117ccd0c971faf..e083f537113627557fc997a20d763b9ecc87be97
@@@ -26,8 -26,6 +26,8 @@@
  #include <linux/ethtool.h>
  #include <linux/mii.h>
  #include <linux/usb.h>
 +#include <linux/bitrev.h>
 +#include <linux/crc16.h>
  #include <linux/crc32.h>
  #include <linux/usb/usbnet.h>
  #include <linux/slab.h>
@@@ -48,8 -46,7 +48,8 @@@
  #define SMSC95XX_INTERNAL_PHY_ID      (1)
  #define SMSC95XX_TX_OVERHEAD          (8)
  #define SMSC95XX_TX_OVERHEAD_CSUM     (12)
 -#define SUPPORTED_WAKE                        (WAKE_MAGIC)
 +#define SUPPORTED_WAKE                        (WAKE_UCAST | WAKE_BCAST | \
 +                                       WAKE_MCAST | WAKE_ARP | WAKE_MAGIC)
  
  #define check_warn(ret, fmt, args...) \
        ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
@@@ -66,98 -63,80 +66,98 @@@ struct smsc95xx_priv 
        u32 hash_lo;
        u32 wolopts;
        spinlock_t mac_cr_lock;
 -};
 -
 -struct usb_context {
 -      struct usb_ctrlrequest req;
 -      struct usbnet *dev;
 +      int wuff_filter_count;
  };
  
  static bool turbo_mode = true;
  module_param(turbo_mode, bool, 0644);
  MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
  
 -static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index,
 -                                        u32 *data)
 +static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
 +                                          u32 *data, int in_pm)
  {
 -      u32 *buf = kmalloc(4, GFP_KERNEL);
 +      u32 buf;
        int ret;
 +      int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
  
        BUG_ON(!dev);
  
 -      if (!buf)
 -              return -ENOMEM;
 -
 -      ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
 -              USB_VENDOR_REQUEST_READ_REGISTER,
 -              USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 -              00, index, buf, 4, USB_CTRL_GET_TIMEOUT);
 +      if (!in_pm)
 +              fn = usbnet_read_cmd;
 +      else
 +              fn = usbnet_read_cmd_nopm;
  
 +      ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN
 +               | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 +               0, index, &buf, 4);
        if (unlikely(ret < 0))
 -              netdev_warn(dev->net, "Failed to read register index 0x%08x\n", index);
 +              netdev_warn(dev->net,
 +                      "Failed to read reg index 0x%08x: %d", index, ret);
  
 -      le32_to_cpus(buf);
 -      *data = *buf;
 -      kfree(buf);
 +      le32_to_cpus(&buf);
 +      *data = buf;
  
        return ret;
  }
  
 -static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index,
 -                                         u32 data)
 +static int __must_check __smsc95xx_write_reg(struct usbnet *dev, u32 index,
 +                                           u32 data, int in_pm)
  {
 -      u32 *buf = kmalloc(4, GFP_KERNEL);
 +      u32 buf;
        int ret;
 +      int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
  
        BUG_ON(!dev);
  
 -      if (!buf)
 -              return -ENOMEM;
 -
 -      *buf = data;
 -      cpu_to_le32s(buf);
 +      if (!in_pm)
 +              fn = usbnet_write_cmd;
 +      else
 +              fn = usbnet_write_cmd_nopm;
  
 -      ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
 -              USB_VENDOR_REQUEST_WRITE_REGISTER,
 -              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 -              00, index, buf, 4, USB_CTRL_SET_TIMEOUT);
 +      buf = data;
 +      cpu_to_le32s(&buf);
  
 +      ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT
 +               | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 +               0, index, &buf, 4);
        if (unlikely(ret < 0))
 -              netdev_warn(dev->net, "Failed to write register index 0x%08x\n", index);
 -
 -      kfree(buf);
 +              netdev_warn(dev->net,
 +                      "Failed to write reg index 0x%08x: %d", index, ret);
  
        return ret;
  }
  
 +static int __must_check smsc95xx_read_reg_nopm(struct usbnet *dev, u32 index,
 +                                             u32 *data)
 +{
 +      return __smsc95xx_read_reg(dev, index, data, 1);
 +}
 +
 +static int __must_check smsc95xx_write_reg_nopm(struct usbnet *dev, u32 index,
 +                                              u32 data)
 +{
 +      return __smsc95xx_write_reg(dev, index, data, 1);
 +}
 +
 +static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index,
 +                                        u32 *data)
 +{
 +      return __smsc95xx_read_reg(dev, index, data, 0);
 +}
 +
 +static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index,
 +                                         u32 data)
 +{
 +      return __smsc95xx_write_reg(dev, index, data, 0);
 +}
  static int smsc95xx_set_feature(struct usbnet *dev, u32 feature)
  {
        if (WARN_ON_ONCE(!dev))
                return -EINVAL;
  
 -      cpu_to_le32s(&feature);
 -
 -      return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
 -              USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0,
 -              USB_CTRL_SET_TIMEOUT);
 +      return usbnet_write_cmd_nopm(dev, USB_REQ_SET_FEATURE,
 +                                   USB_RECIP_DEVICE, feature, 0,
 +                                   NULL, 0);
  }
  
  static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature)
        if (WARN_ON_ONCE(!dev))
                return -EINVAL;
  
 -      cpu_to_le32s(&feature);
 -
 -      return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
 -              USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0,
 -              USB_CTRL_SET_TIMEOUT);
 +      return usbnet_write_cmd_nopm(dev, USB_REQ_CLEAR_FEATURE,
 +                                   USB_RECIP_DEVICE, feature,
 +                                   0, NULL, 0);
  }
  
  /* Loop until the read is completed with timeout
@@@ -203,7 -184,7 +203,7 @@@ static int smsc95xx_mdio_read(struct ne
        /* set the address, index & direction (read from PHY) */
        phy_id &= dev->mii.phy_id_mask;
        idx &= dev->mii.reg_num_mask;
-       addr = (phy_id << 11) | (idx << 6) | MII_READ_;
+       addr = (phy_id << 11) | (idx << 6) | MII_READ_ | MII_BUSY_;
        ret = smsc95xx_write_reg(dev, MII_ADDR, addr);
        check_warn_goto_done(ret, "Error writing MII_ADDR");
  
@@@ -240,7 -221,7 +240,7 @@@ static void smsc95xx_mdio_write(struct 
        /* set the address, index & direction (write to PHY) */
        phy_id &= dev->mii.phy_id_mask;
        idx &= dev->mii.reg_num_mask;
-       addr = (phy_id << 11) | (idx << 6) | MII_WRITE_;
+       addr = (phy_id << 11) | (idx << 6) | MII_WRITE_ | MII_BUSY_;
        ret = smsc95xx_write_reg(dev, MII_ADDR, addr);
        check_warn_goto_done(ret, "Error writing MII_ADDR");
  
@@@ -369,20 -350,60 +369,20 @@@ static int smsc95xx_write_eeprom(struc
        return 0;
  }
  
 -static void smsc95xx_async_cmd_callback(struct urb *urb)
 -{
 -      struct usb_context *usb_context = urb->context;
 -      struct usbnet *dev = usb_context->dev;
 -      int status = urb->status;
 -
 -      check_warn(status, "async callback failed with %d\n", status);
 -
 -      kfree(usb_context);
 -      usb_free_urb(urb);
 -}
 -
  static int __must_check smsc95xx_write_reg_async(struct usbnet *dev, u16 index,
                                                 u32 *data)
  {
 -      struct usb_context *usb_context;
 -      int status;
 -      struct urb *urb;
        const u16 size = 4;
 +      int ret;
  
 -      urb = usb_alloc_urb(0, GFP_ATOMIC);
 -      if (!urb) {
 -              netdev_warn(dev->net, "Error allocating URB\n");
 -              return -ENOMEM;
 -      }
 -
 -      usb_context = kmalloc(sizeof(struct usb_context), GFP_ATOMIC);
 -      if (usb_context == NULL) {
 -              netdev_warn(dev->net, "Error allocating control msg\n");
 -              usb_free_urb(urb);
 -              return -ENOMEM;
 -      }
 -
 -      usb_context->req.bRequestType =
 -              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
 -      usb_context->req.bRequest = USB_VENDOR_REQUEST_WRITE_REGISTER;
 -      usb_context->req.wValue = 00;
 -      usb_context->req.wIndex = cpu_to_le16(index);
 -      usb_context->req.wLength = cpu_to_le16(size);
 -
 -      usb_fill_control_urb(urb, dev->udev, usb_sndctrlpipe(dev->udev, 0),
 -              (void *)&usb_context->req, data, size,
 -              smsc95xx_async_cmd_callback,
 -              (void *)usb_context);
 -
 -      status = usb_submit_urb(urb, GFP_ATOMIC);
 -      if (status < 0) {
 -              netdev_warn(dev->net, "Error submitting control msg, sts=%d\n",
 -                          status);
 -              kfree(usb_context);
 -              usb_free_urb(urb);
 -      }
 -
 -      return status;
 +      ret = usbnet_write_cmd_async(dev, USB_VENDOR_REQUEST_WRITE_REGISTER,
 +                                   USB_DIR_OUT | USB_TYPE_VENDOR |
 +                                   USB_RECIP_DEVICE,
 +                                   0, index, data, size);
 +      if (ret < 0)
 +              netdev_warn(dev->net, "Error write async cmd, sts=%d\n",
 +                          ret);
 +      return ret;
  }
  
  /* returns hash bit number for given MAC address
@@@ -744,7 -765,7 +744,7 @@@ static int smsc95xx_start_tx_path(struc
  }
  
  /* Starts the Receive path */
 -static int smsc95xx_start_rx_path(struct usbnet *dev)
 +static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm)
  {
        struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
        unsigned long flags;
        pdata->mac_cr |= MAC_CR_RXEN_;
        spin_unlock_irqrestore(&pdata->mac_cr_lock, flags);
  
 -      ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr);
 +      ret = __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm);
        check_warn_return(ret, "Failed to write MAC_CR: %d\n", ret);
  
        return 0;
@@@ -973,7 -994,7 +973,7 @@@ static int smsc95xx_reset(struct usbne
        ret = smsc95xx_start_tx_path(dev);
        check_warn_return(ret, "Failed to start TX path");
  
 -      ret = smsc95xx_start_rx_path(dev);
 +      ret = smsc95xx_start_rx_path(dev, 0);
        check_warn_return(ret, "Failed to start RX path");
  
        netif_dbg(dev, ifup, dev->net, "smsc95xx_reset, return 0\n");
@@@ -996,7 -1017,6 +996,7 @@@ static const struct net_device_ops smsc
  static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
  {
        struct smsc95xx_priv *pdata = NULL;
 +      u32 val;
        int ret;
  
        printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n");
        /* Init all registers */
        ret = smsc95xx_reset(dev);
  
 +      /* detect device revision as different features may be available */
 +      ret = smsc95xx_read_reg(dev, ID_REV, &val);
 +      check_warn_return(ret, "Failed to read ID_REV: %d\n", ret);
 +      val >>= 16;
 +      if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9512_))
 +              pdata->wuff_filter_count = LAN9500A_WUFF_NUM;
 +      else
 +              pdata->wuff_filter_count = LAN9500_WUFF_NUM;
 +
        dev->net->netdev_ops = &smsc95xx_netdev_ops;
        dev->net->ethtool_ops = &smsc95xx_ethtool_ops;
        dev->net->flags |= IFF_MULTICAST;
@@@ -1055,11 -1066,6 +1055,11 @@@ static void smsc95xx_unbind(struct usbn
        }
  }
  
 +static u16 smsc_crc(const u8 *buffer, size_t len, int filter)
 +{
 +      return bitrev16(crc16(0xFFFF, buffer, len)) << ((filter % 2) * 16);
 +}
 +
  static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
  {
        struct usbnet *dev = usb_get_intfdata(intf);
                netdev_info(dev->net, "entering SUSPEND2 mode");
  
                /* disable energy detect (link up) & wake up events */
 -              ret = smsc95xx_read_reg(dev, WUCSR, &val);
 +              ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
                check_warn_return(ret, "Error reading WUCSR");
  
                val &= ~(WUCSR_MPEN_ | WUCSR_WAKE_EN_);
  
 -              ret = smsc95xx_write_reg(dev, WUCSR, val);
 +              ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
                check_warn_return(ret, "Error writing WUCSR");
  
 -              ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
 +              ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
                check_warn_return(ret, "Error reading PM_CTRL");
  
                val &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_);
  
 -              ret = smsc95xx_write_reg(dev, PM_CTRL, val);
 +              ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
                check_warn_return(ret, "Error writing PM_CTRL");
  
                /* enter suspend2 mode */
 -              ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
 +              ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
                check_warn_return(ret, "Error reading PM_CTRL");
  
                val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_);
                val |= PM_CTL_SUS_MODE_2;
  
 -              ret = smsc95xx_write_reg(dev, PM_CTRL, val);
 +              ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
                check_warn_return(ret, "Error writing PM_CTRL");
  
                return 0;
        }
  
 +      if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) {
 +              u32 *filter_mask = kzalloc(32, GFP_KERNEL);
 +              u32 command[2];
 +              u32 offset[2];
 +              u32 crc[4];
 +              int i, filter = 0;
 +
 +              memset(command, 0, sizeof(command));
 +              memset(offset, 0, sizeof(offset));
 +              memset(crc, 0, sizeof(crc));
 +
 +              if (pdata->wolopts & WAKE_BCAST) {
 +                      const u8 bcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 +                      netdev_info(dev->net, "enabling broadcast detection");
 +                      filter_mask[filter * 4] = 0x003F;
 +                      filter_mask[filter * 4 + 1] = 0x00;
 +                      filter_mask[filter * 4 + 2] = 0x00;
 +                      filter_mask[filter * 4 + 3] = 0x00;
 +                      command[filter/4] |= 0x05UL << ((filter % 4) * 8);
 +                      offset[filter/4] |= 0x00 << ((filter % 4) * 8);
 +                      crc[filter/2] |= smsc_crc(bcast, 6, filter);
 +                      filter++;
 +              }
 +
 +              if (pdata->wolopts & WAKE_MCAST) {
 +                      const u8 mcast[] = {0x01, 0x00, 0x5E};
 +                      netdev_info(dev->net, "enabling multicast detection");
 +                      filter_mask[filter * 4] = 0x0007;
 +                      filter_mask[filter * 4 + 1] = 0x00;
 +                      filter_mask[filter * 4 + 2] = 0x00;
 +                      filter_mask[filter * 4 + 3] = 0x00;
 +                      command[filter/4] |= 0x09UL << ((filter % 4) * 8);
 +                      offset[filter/4] |= 0x00  << ((filter % 4) * 8);
 +                      crc[filter/2] |= smsc_crc(mcast, 3, filter);
 +                      filter++;
 +              }
 +
 +              if (pdata->wolopts & WAKE_ARP) {
 +                      const u8 arp[] = {0x08, 0x06};
 +                      netdev_info(dev->net, "enabling ARP detection");
 +                      filter_mask[filter * 4] = 0x0003;
 +                      filter_mask[filter * 4 + 1] = 0x00;
 +                      filter_mask[filter * 4 + 2] = 0x00;
 +                      filter_mask[filter * 4 + 3] = 0x00;
 +                      command[filter/4] |= 0x05UL << ((filter % 4) * 8);
 +                      offset[filter/4] |= 0x0C << ((filter % 4) * 8);
 +                      crc[filter/2] |= smsc_crc(arp, 2, filter);
 +                      filter++;
 +              }
 +
 +              if (pdata->wolopts & WAKE_UCAST) {
 +                      netdev_info(dev->net, "enabling unicast detection");
 +                      filter_mask[filter * 4] = 0x003F;
 +                      filter_mask[filter * 4 + 1] = 0x00;
 +                      filter_mask[filter * 4 + 2] = 0x00;
 +                      filter_mask[filter * 4 + 3] = 0x00;
 +                      command[filter/4] |= 0x01UL << ((filter % 4) * 8);
 +                      offset[filter/4] |= 0x00 << ((filter % 4) * 8);
 +                      crc[filter/2] |= smsc_crc(dev->net->dev_addr, ETH_ALEN, filter);
 +                      filter++;
 +              }
 +
 +              for (i = 0; i < (pdata->wuff_filter_count * 4); i++) {
 +                      ret = smsc95xx_write_reg_nopm(dev, WUFF, filter_mask[i]);
 +                      if (ret < 0)
 +                              kfree(filter_mask);
 +                      check_warn_return(ret, "Error writing WUFF");
 +              }
 +              kfree(filter_mask);
 +
 +              for (i = 0; i < (pdata->wuff_filter_count / 4); i++) {
 +                      ret = smsc95xx_write_reg_nopm(dev, WUFF, command[i]);
 +                      check_warn_return(ret, "Error writing WUFF");
 +              }
 +
 +              for (i = 0; i < (pdata->wuff_filter_count / 4); i++) {
 +                      ret = smsc95xx_write_reg_nopm(dev, WUFF, offset[i]);
 +                      check_warn_return(ret, "Error writing WUFF");
 +              }
 +
 +              for (i = 0; i < (pdata->wuff_filter_count / 2); i++) {
 +                      ret = smsc95xx_write_reg_nopm(dev, WUFF, crc[i]);
 +                      check_warn_return(ret, "Error writing WUFF");
 +              }
 +
 +              /* clear any pending pattern match packet status */
 +              ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
 +              check_warn_return(ret, "Error reading WUCSR");
 +
 +              val |= WUCSR_WUFR_;
 +
 +              ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
 +              check_warn_return(ret, "Error writing WUCSR");
 +      }
 +
        if (pdata->wolopts & WAKE_MAGIC) {
                /* clear any pending magic packet status */
 -              ret = smsc95xx_read_reg(dev, WUCSR, &val);
 +              ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
                check_warn_return(ret, "Error reading WUCSR");
  
                val |= WUCSR_MPR_;
  
 -              ret = smsc95xx_write_reg(dev, WUCSR, val);
 +              ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
                check_warn_return(ret, "Error writing WUCSR");
        }
  
 -      /* enable/disable magic packup wake */
 -      ret = smsc95xx_read_reg(dev, WUCSR, &val);
 +      /* enable/disable wakeup sources */
 +      ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
        check_warn_return(ret, "Error reading WUCSR");
  
 +      if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) {
 +              netdev_info(dev->net, "enabling pattern match wakeup");
 +              val |= WUCSR_WAKE_EN_;
 +      } else {
 +              netdev_info(dev->net, "disabling pattern match wakeup");
 +              val &= ~WUCSR_WAKE_EN_;
 +      }
 +
        if (pdata->wolopts & WAKE_MAGIC) {
                netdev_info(dev->net, "enabling magic packet wakeup");
                val |= WUCSR_MPEN_;
                val &= ~WUCSR_MPEN_;
        }
  
 -      ret = smsc95xx_write_reg(dev, WUCSR, val);
 +      ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
        check_warn_return(ret, "Error writing WUCSR");
  
        /* enable wol wakeup source */
 -      ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
 +      ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
        check_warn_return(ret, "Error reading PM_CTRL");
  
        val |= PM_CTL_WOL_EN_;
  
 -      ret = smsc95xx_write_reg(dev, PM_CTRL, val);
 +      ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
        check_warn_return(ret, "Error writing PM_CTRL");
  
 -      /* enable receiver */
 -      smsc95xx_start_rx_path(dev);
 +      /* enable receiver to enable frame reception */
 +      smsc95xx_start_rx_path(dev, 1);
  
        /* some wol options are enabled, so enter SUSPEND0 */
        netdev_info(dev->net, "entering SUSPEND0 mode");
  
 -      ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
 +      ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
        check_warn_return(ret, "Error reading PM_CTRL");
  
        val &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
        val |= PM_CTL_SUS_MODE_0;
  
 -      ret = smsc95xx_write_reg(dev, PM_CTRL, val);
 +      ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
        check_warn_return(ret, "Error writing PM_CTRL");
  
        /* clear wol status */
        val &= ~PM_CTL_WUPS_;
        val |= PM_CTL_WUPS_WOL_;
 -      ret = smsc95xx_write_reg(dev, PM_CTRL, val);
 +      ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
        check_warn_return(ret, "Error writing PM_CTRL");
  
        /* read back PM_CTRL */
 -      ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
 +      ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
        check_warn_return(ret, "Error reading PM_CTRL");
  
        smsc95xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
@@@ -1281,26 -1184,26 +1281,26 @@@ static int smsc95xx_resume(struct usb_i
  
        BUG_ON(!dev);
  
 -      if (pdata->wolopts & WAKE_MAGIC) {
 +      if (pdata->wolopts) {
                smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
  
 -              /* Disable magic packup wake */
 -              ret = smsc95xx_read_reg(dev, WUCSR, &val);
 +              /* clear wake-up sources */
 +              ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
                check_warn_return(ret, "Error reading WUCSR");
  
 -              val &= ~WUCSR_MPEN_;
 +              val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_);
  
 -              ret = smsc95xx_write_reg(dev, WUCSR, val);
 +              ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
                check_warn_return(ret, "Error writing WUCSR");
  
                /* clear wake-up status */
 -              ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
 +              ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
                check_warn_return(ret, "Error reading PM_CTRL");
  
                val &= ~PM_CTL_WOL_EN_;
                val |= PM_CTL_WUPS_;
  
 -              ret = smsc95xx_write_reg(dev, PM_CTRL, val);
 +              ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
                check_warn_return(ret, "Error writing PM_CTRL");
        }
  
diff --combined drivers/net/vxlan.c
index 9814d67237f1cfb337cd90a5ba0d237ba9b0a7d4,8b5c6191707626ba64bad7bdceeab2c299e5bcfc..6898a7932cff9b6ed8aa7cfd578b70192d023177
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * VXLAN: Virtual eXtensiable Local Area Network
+  * VXLAN: Virtual eXtensible Local Area Network
   *
   * Copyright (c) 2012 Vyatta Inc.
   *
@@@ -50,8 -50,8 +50,8 @@@
  
  #define VXLAN_N_VID   (1u << 24)
  #define VXLAN_VID_MASK        (VXLAN_N_VID - 1)
- /* VLAN + IP header + UDP + VXLAN */
- #define VXLAN_HEADROOM (4 + 20 + 8 + 8)
+ /* IP header + UDP + VXLAN + Ethernet header */
+ #define VXLAN_HEADROOM (20 + 8 + 8 + 14)
  
  #define VXLAN_FLAGS 0x08000000        /* struct vxlanhdr.vx_flags required value. */
  
@@@ -769,7 -769,7 +769,7 @@@ static netdev_tx_t vxlan_xmit(struct sk
  
        vxlan_set_owner(dev, skb);
  
 -      /* See __IPTUNNEL_XMIT */
 +      /* See iptunnel_xmit() */
        skb->ip_summed = CHECKSUM_NONE;
        ip_select_ident(iph, &rt->dst, NULL);
  
@@@ -1102,14 -1102,15 +1102,18 @@@ static int vxlan_newlink(struct net *ne
  
                if (!tb[IFLA_MTU])
                        dev->mtu = lowerdev->mtu - VXLAN_HEADROOM;
+               /* update header length based on lower device */
+               dev->hard_header_len = lowerdev->hard_header_len +
+                                      VXLAN_HEADROOM;
        }
  
        if (data[IFLA_VXLAN_TOS])
                vxlan->tos  = nla_get_u8(data[IFLA_VXLAN_TOS]);
  
 +      if (data[IFLA_VXLAN_TTL])
 +              vxlan->ttl = nla_get_u8(data[IFLA_VXLAN_TTL]);
 +
        if (!data[IFLA_VXLAN_LEARNING] || nla_get_u8(data[IFLA_VXLAN_LEARNING]))
                vxlan->learn = true;
  
index cb30feaa565be13d61afab2b4fa50dc94d49b8f1,481345c23ded3250bf68c002089809e815abb3fe..6d554249394fa5645376188116b61b0924942801
@@@ -35,7 -35,6 +35,7 @@@
  #include <brcmu_wifi.h>
  #include "dhd.h"
  #include "wl_cfg80211.h"
 +#include "fwil.h"
  
  #define BRCMF_SCAN_IE_LEN_MAX         2048
  #define BRCMF_PNO_VERSION             2
@@@ -49,8 -48,6 +49,8 @@@
  #define BRCMF_PNO_SCAN_COMPLETE               1
  #define BRCMF_PNO_SCAN_INCOMPLETE     0
  
 +#define BRCMF_IFACE_MAX_CNT           2
 +
  #define TLV_LEN_OFF                   1       /* length offset */
  #define TLV_HDR_LEN                   2       /* header length */
  #define TLV_BODY_OFF                  2       /* body offset */
  #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
        (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
  
 -static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
 -
  static u32 brcmf_dbg_level = WL_DBG_ERR;
  
 -static bool check_sys_up(struct wiphy *wiphy)
 +static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
  {
 -      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      if (!test_bit(WL_STATUS_READY, &cfg->status)) {
 -              WL_INFO("device is not ready : status (%d)\n",
 -                      (int)cfg->status);
 +      if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
 +              WL_INFO("device is not ready : status (%lu)\n",
 +                      vif->sme_state);
                return false;
        }
        return true;
@@@ -391,29 -391,55 +391,29 @@@ static u8 brcmf_mw_to_qdbm(u16 mw
        return qdbm;
  }
  
 -/* function for reading/writing a single u32 from/to the dongle */
 -static int
 -brcmf_exec_dcmd_u32(struct net_device *ndev, u32 cmd, u32 *par)
 +static u16 channel_to_chanspec(struct ieee80211_channel *ch)
  {
 -      int err;
 -      __le32 par_le = cpu_to_le32(*par);
 -
 -      err = brcmf_exec_dcmd(ndev, cmd, &par_le, sizeof(__le32));
 -      *par = le32_to_cpu(par_le);
 -
 -      return err;
 -}
 -
 -static s32
 -brcmf_dev_iovar_setbuf_bsscfg(struct net_device *ndev, s8 *name,
 -                            void *param, s32 paramlen,
 -                            void *buf, s32 buflen, s32 bssidx)
 -{
 -      s32 err = -ENOMEM;
 -      u32 len;
 -
 -      len = brcmf_c_mkiovar_bsscfg(name, param, paramlen,
 -                                   buf, buflen, bssidx);
 -      BUG_ON(!len);
 -      if (len > 0)
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, buf, len);
 -      if (err)
 -              WL_ERR("error (%d)\n", err);
 +      u16 chanspec;
  
 -      return err;
 -}
 +      chanspec = ieee80211_frequency_to_channel(ch->center_freq);
 +      chanspec &= WL_CHANSPEC_CHAN_MASK;
  
 -static s32
 -brcmf_dev_iovar_getbuf_bsscfg(struct net_device *ndev, s8 *name,
 -                            void *param, s32 paramlen,
 -                            void *buf, s32 buflen, s32 bssidx)
 -{
 -      s32 err = -ENOMEM;
 -      u32 len;
 -
 -      len = brcmf_c_mkiovar_bsscfg(name, param, paramlen,
 -                                   buf, buflen, bssidx);
 -      BUG_ON(!len);
 -      if (len > 0)
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, buf, len);
 -      if (err)
 -              WL_ERR("error (%d)\n", err);
 +      if (ch->band == IEEE80211_BAND_2GHZ)
 +              chanspec |= WL_CHANSPEC_BAND_2G;
 +      else
 +              chanspec |= WL_CHANSPEC_BAND_5G;
  
 -      return err;
 +      if (ch->flags & IEEE80211_CHAN_NO_HT40) {
 +              chanspec |= WL_CHANSPEC_BW_20;
 +              chanspec |= WL_CHANSPEC_CTL_SB_NONE;
 +      } else {
 +              chanspec |= WL_CHANSPEC_BW_40;
 +              if (ch->flags & IEEE80211_CHAN_NO_HT40PLUS)
 +                      chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
 +              else
 +                      chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
 +      }
 +      return chanspec;
  }
  
  static void convert_key_from_CPU(struct brcmf_wsec_key *key,
  }
  
  static int
 -send_key_to_dongle(struct brcmf_cfg80211_info *cfg, s32 bssidx,
 -                 struct net_device *ndev, struct brcmf_wsec_key *key)
 +send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
  {
        int err;
        struct brcmf_wsec_key_le key_le;
  
        convert_key_from_CPU(key, &key_le);
  
 -      err  = brcmf_dev_iovar_setbuf_bsscfg(ndev, "wsec_key", &key_le,
 -                                           sizeof(key_le),
 -                                           cfg->extra_buf,
 -                                           WL_EXTRA_BUF_MAX, bssidx);
 +      brcmf_netdev_wait_pend8021x(ndev);
 +
 +      err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
 +                                      sizeof(key_le));
  
        if (err)
                WL_ERR("wsec_key error (%d)\n", err);
@@@ -453,7 -480,6 +453,7 @@@ brcmf_cfg80211_change_iface(struct wiph
                         enum nl80211_iftype type, u32 *flags,
                         struct vif_params *params)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        s32 infra = 0;
        s32 ap = 0;
        }
  
        if (ap) {
 -              set_bit(WL_STATUS_AP_CREATING, &cfg->status);
 +              set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
                if (!cfg->ap_info)
                        cfg->ap_info = kzalloc(sizeof(*cfg->ap_info),
                                               GFP_KERNEL);
                }
                WL_INFO("IF Type = AP\n");
        } else {
 -              err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &infra);
 +              err = brcmf_fil_cmd_int_set(netdev_priv(ndev),
 +                                          BRCMF_C_SET_INFRA, infra);
                if (err) {
                        WL_ERR("WLC_SET_INFRA error (%d)\n", err);
                        err = -EAGAIN;
@@@ -514,13 -539,99 +514,13 @@@ done
        return err;
  }
  
 -static s32 brcmf_dev_intvar_set(struct net_device *ndev, s8 *name, s32 val)
 -{
 -      s8 buf[BRCMF_DCMD_SMLEN];
 -      u32 len;
 -      s32 err = 0;
 -      __le32 val_le;
 -
 -      val_le = cpu_to_le32(val);
 -      len = brcmf_c_mkiovar(name, (char *)(&val_le), sizeof(val_le), buf,
 -                          sizeof(buf));
 -      BUG_ON(!len);
 -
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, buf, len);
 -      if (err)
 -              WL_ERR("error (%d)\n", err);
 -
 -      return err;
 -}
 -
 -static s32
 -brcmf_dev_intvar_get(struct net_device *ndev, s8 *name, s32 *retval)
 -{
 -      union {
 -              s8 buf[BRCMF_DCMD_SMLEN];
 -              __le32 val;
 -      } var;
 -      u32 len;
 -      u32 data_null;
 -      s32 err = 0;
 -
 -      len =
 -          brcmf_c_mkiovar(name, (char *)(&data_null), 0, (char *)(&var),
 -                      sizeof(var.buf));
 -      BUG_ON(!len);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, &var, len);
 -      if (err)
 -              WL_ERR("error (%d)\n", err);
 -
 -      *retval = le32_to_cpu(var.val);
 -
 -      return err;
 -}
 -
 -static s32
 -brcmf_dev_intvar_set_bsscfg(struct net_device *ndev, s8 *name, u32 val,
 -                          s32 bssidx)
 -{
 -      s8 buf[BRCMF_DCMD_SMLEN];
 -      __le32 val_le;
 -
 -      val_le = cpu_to_le32(val);
 -
 -      return brcmf_dev_iovar_setbuf_bsscfg(ndev, name, &val_le,
 -                                           sizeof(val_le), buf, sizeof(buf),
 -                                           bssidx);
 -}
 -
 -static s32
 -brcmf_dev_intvar_get_bsscfg(struct net_device *ndev, s8 *name, s32 *val,
 -                          s32 bssidx)
 -{
 -      s8 buf[BRCMF_DCMD_SMLEN];
 -      s32 err;
 -      __le32 val_le;
 -
 -      memset(buf, 0, sizeof(buf));
 -      err = brcmf_dev_iovar_getbuf_bsscfg(ndev, name, val, sizeof(*val), buf,
 -                                          sizeof(buf), bssidx);
 -      if (err == 0) {
 -              memcpy(&val_le, buf, sizeof(val_le));
 -              *val = le32_to_cpu(val_le);
 -      }
 -      return err;
 -}
 -
 -
 -/*
 - * For now brcmf_find_bssidx will return 0. Once p2p gets implemented this
 - * should return the ndev matching bssidx.
 - */
 -static s32
 -brcmf_find_bssidx(struct brcmf_cfg80211_info *cfg, struct net_device *ndev)
 -{
 -      return 0;
 -}
 -
  static void brcmf_set_mpc(struct net_device *ndev, int mpc)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
 -      struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
  
 -      if (test_bit(WL_STATUS_READY, &cfg->status)) {
 -              err = brcmf_dev_intvar_set(ndev, "mpc", mpc);
 +      if (check_vif_up(ifp->vif)) {
 +              err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
                if (err) {
                        WL_ERR("fail to set mpc\n");
                        return;
  static void brcmf_iscan_prep(struct brcmf_scan_params_le *params_le,
                             struct brcmf_ssid *ssid)
  {
 -      memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
 +      memset(params_le->bssid, 0xFF, ETH_ALEN);
        params_le->bss_type = DOT11_BSSTYPE_ANY;
        params_le->scan_type = 0;
        params_le->channel_num = 0;
        }
  }
  
 -static s32
 -brcmf_dev_iovar_setbuf(struct net_device *ndev, s8 * iovar, void *param,
 -                  s32 paramlen, void *bufptr, s32 buflen)
 -{
 -      s32 iolen;
 -
 -      iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen);
 -      BUG_ON(!iolen);
 -
 -      return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, bufptr, iolen);
 -}
 -
 -static s32
 -brcmf_dev_iovar_getbuf(struct net_device *ndev, s8 * iovar, void *param,
 -                  s32 paramlen, void *bufptr, s32 buflen)
 -{
 -      s32 iolen;
 -
 -      iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen);
 -      BUG_ON(!iolen);
 -
 -      return brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, bufptr, buflen);
 -}
 -
  static s32
  brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan,
                struct brcmf_ssid *ssid, u16 action)
        params->action = cpu_to_le16(action);
        params->scan_duration = cpu_to_le16(0);
  
 -      err = brcmf_dev_iovar_setbuf(iscan->ndev, "iscan", params, params_size,
 -                                   iscan->dcmd_buf, BRCMF_DCMD_SMLEN);
 +      err = brcmf_fil_iovar_data_set(netdev_priv(iscan->ndev), "iscan",
 +                                     params, params_size);
        if (err) {
                if (err == -EBUSY)
                        WL_INFO("system busy : iscan canceled\n");
@@@ -586,7 -721,7 +586,7 @@@ static s32 brcmf_do_iscan(struct brcmf_
        struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
        struct net_device *ndev = cfg_to_ndev(cfg);
        struct brcmf_ssid ssid;
 -      __le32 passive_scan;
 +      u32 passive_scan;
        s32 err = 0;
  
        /* Broadcast scan by default */
  
        iscan->state = WL_ISCAN_STATE_SCANING;
  
 -      passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
 -      err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCMF_C_SET_PASSIVE_SCAN,
 -                      &passive_scan, sizeof(passive_scan));
 +      passive_scan = cfg->active_scan ? 0 : 1;
 +      err = brcmf_fil_cmd_int_set(netdev_priv(ndev),
 +                                  BRCMF_C_SET_PASSIVE_SCAN, passive_scan);
        if (err) {
                WL_ERR("error (%d)\n", err);
                return err;
@@@ -619,27 -754,27 +619,27 @@@ brcmf_cfg80211_iscan(struct wiphy *wiph
                     struct cfg80211_scan_request *request,
                     struct cfg80211_ssid *this_ssid)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
        struct cfg80211_ssid *ssids;
        struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int;
 -      __le32 passive_scan;
 +      u32 passive_scan;
        bool iscan_req;
        bool spec_scan;
        s32 err = 0;
        u32 SSID_len;
  
 -      if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
 -              WL_ERR("Scanning already : status (%lu)\n", cfg->status);
 +      if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
 +              WL_ERR("Scanning already: status (%lu)\n", cfg->scan_status);
                return -EAGAIN;
        }
 -      if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg->status)) {
 -              WL_ERR("Scanning being aborted : status (%lu)\n",
 -                     cfg->status);
 +      if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
 +              WL_ERR("Scanning being aborted: status (%lu)\n",
 +                     cfg->scan_status);
                return -EAGAIN;
        }
 -      if (test_bit(WL_STATUS_CONNECTING, &cfg->status)) {
 -              WL_ERR("Connecting : status (%lu)\n",
 -                     cfg->status);
 +      if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
 +              WL_ERR("Connecting: status (%lu)\n", ifp->vif->sme_state);
                return -EAGAIN;
        }
  
        }
  
        cfg->scan_request = request;
 -      set_bit(WL_STATUS_SCANNING, &cfg->status);
 +      set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
        if (iscan_req) {
                err = brcmf_do_iscan(cfg);
                if (!err)
                        WL_SCAN("Broadcast scan\n");
                }
  
 -              passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
 -                              &passive_scan, sizeof(passive_scan));
 +              passive_scan = cfg->active_scan ? 0 : 1;
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
 +                                          passive_scan);
                if (err) {
                        WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
                        goto scan_out;
                }
                brcmf_set_mpc(ndev, 0);
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
 -                                    sizeof(sr->ssid_le));
 +              err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
 +                                           &sr->ssid_le, sizeof(sr->ssid_le));
                if (err) {
                        if (err == -EBUSY)
                                WL_INFO("system busy : scan for \"%s\" "
        return 0;
  
  scan_out:
 -      clear_bit(WL_STATUS_SCANNING, &cfg->status);
 +      clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
        cfg->scan_request = NULL;
        return err;
  }
@@@ -716,10 -851,12 +716,10 @@@ static void brcmf_escan_prep(struct brc
        s32 i;
        s32 offset;
        u16 chanspec;
 -      u16 channel;
 -      struct ieee80211_channel *req_channel;
        char *ptr;
        struct brcmf_ssid_le ssid_le;
  
 -      memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
 +      memset(params_le->bssid, 0xFF, ETH_ALEN);
        params_le->bss_type = DOT11_BSSTYPE_ANY;
        params_le->scan_type = 0;
        params_le->channel_num = 0;
        WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels);
        if (n_channels > 0) {
                for (i = 0; i < n_channels; i++) {
 -                      chanspec = 0;
 -                      req_channel = request->channels[i];
 -                      channel = ieee80211_frequency_to_channel(
 -                                      req_channel->center_freq);
 -                      if (req_channel->band == IEEE80211_BAND_2GHZ)
 -                              chanspec |= WL_CHANSPEC_BAND_2G;
 -                      else
 -                              chanspec |= WL_CHANSPEC_BAND_5G;
 -
 -                      if (req_channel->flags & IEEE80211_CHAN_NO_HT40) {
 -                              chanspec |= WL_CHANSPEC_BW_20;
 -                              chanspec |= WL_CHANSPEC_CTL_SB_NONE;
 -                      } else {
 -                              chanspec |= WL_CHANSPEC_BW_40;
 -                              if (req_channel->flags &
 -                                              IEEE80211_CHAN_NO_HT40PLUS)
 -                                      chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
 -                              else
 -                                      chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
 -                      }
 -
 -                      chanspec |= (channel & WL_CHANSPEC_CHAN_MASK);
 +                      chanspec = channel_to_chanspec(request->channels[i]);
                        WL_SCAN("Chan : %d, Channel spec: %x\n",
 -                              channel, chanspec);
 +                              request->channels[i]->hw_value, chanspec);
                        params_le->channel_list[i] = cpu_to_le16(chanspec);
                }
        } else {
@@@ -808,7 -966,7 +808,7 @@@ brcmf_notify_escan_complete(struct brcm
                /* Do a scan abort to stop the driver's scan engine */
                WL_SCAN("ABORT scan in firmware\n");
                memset(&params_le, 0, sizeof(params_le));
 -              memcpy(params_le.bssid, ether_bcast, ETH_ALEN);
 +              memset(params_le.bssid, 0xFF, ETH_ALEN);
                params_le.bss_type = DOT11_BSSTYPE_ANY;
                params_le.scan_type = 0;
                params_le.channel_num = cpu_to_le32(1);
                /* Scan is aborted by setting channel_list[0] to -1 */
                params_le.channel_list[0] = cpu_to_le16(-1);
                /* E-Scan (or anyother type) can be aborted by SCAN */
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &params_le,
 -                      sizeof(params_le));
 +              err = brcmf_fil_cmd_data_set(netdev_priv(ndev), BRCMF_C_SCAN,
 +                                           &params_le, sizeof(params_le));
                if (err)
                        WL_ERR("Scan abort  failed\n");
        }
                cfg80211_scan_done(scan_request, aborted);
                brcmf_set_mpc(ndev, 1);
        }
 -      if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
 +      if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
                WL_ERR("Scan complete while device not scanning\n");
                return -EPERM;
        }
@@@ -878,8 -1036,8 +878,8 @@@ brcmf_run_escan(struct brcmf_cfg80211_i
        params->action = cpu_to_le16(action);
        params->sync_id = cpu_to_le16(0x1234);
  
 -      err = brcmf_dev_iovar_setbuf(ndev, "escan", params, params_size,
 -                      cfg->escan_ioctl_buf, BRCMF_DCMD_MEDLEN);
 +      err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "escan",
 +                                     params, params_size);
        if (err) {
                if (err == -EBUSY)
                        WL_INFO("system busy : escan canceled\n");
@@@ -897,16 -1055,16 +897,16 @@@ brcmf_do_escan(struct brcmf_cfg80211_in
               struct net_device *ndev, struct cfg80211_scan_request *request)
  {
        s32 err;
 -      __le32 passive_scan;
 +      u32 passive_scan;
        struct brcmf_scan_results *results;
  
        WL_SCAN("Enter\n");
        cfg->escan_info.ndev = ndev;
        cfg->escan_info.wiphy = wiphy;
        cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
 -      passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
 -                      &passive_scan, sizeof(passive_scan));
 +      passive_scan = cfg->active_scan ? 0 : 1;
 +      err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN,
 +                                  passive_scan);
        if (err) {
                WL_ERR("error (%d)\n", err);
                return err;
@@@ -928,11 -1086,10 +928,11 @@@ brcmf_cfg80211_escan(struct wiphy *wiph
                     struct cfg80211_scan_request *request,
                     struct cfg80211_ssid *this_ssid)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
        struct cfg80211_ssid *ssids;
        struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int;
 -      __le32 passive_scan;
 +      u32 passive_scan;
        bool escan_req;
        bool spec_scan;
        s32 err;
  
        WL_SCAN("START ESCAN\n");
  
 -      if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
 -              WL_ERR("Scanning already : status (%lu)\n", cfg->status);
 +      if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
 +              WL_ERR("Scanning already: status (%lu)\n", cfg->scan_status);
                return -EAGAIN;
        }
 -      if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg->status)) {
 -              WL_ERR("Scanning being aborted : status (%lu)\n",
 -                     cfg->status);
 +      if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
 +              WL_ERR("Scanning being aborted: status (%lu)\n",
 +                     cfg->scan_status);
                return -EAGAIN;
        }
 -      if (test_bit(WL_STATUS_CONNECTING, &cfg->status)) {
 -              WL_ERR("Connecting : status (%lu)\n",
 -                     cfg->status);
 +      if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
 +              WL_ERR("Connecting: status (%lu)\n", ifp->vif->sme_state);
                return -EAGAIN;
        }
  
        }
  
        cfg->scan_request = request;
 -      set_bit(WL_STATUS_SCANNING, &cfg->status);
 +      set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
        if (escan_req) {
                err = brcmf_do_escan(cfg, wiphy, ndev, request);
                if (!err)
                } else
                        WL_SCAN("Broadcast scan\n");
  
 -              passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
 -                              &passive_scan, sizeof(passive_scan));
 +              passive_scan = cfg->active_scan ? 0 : 1;
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
 +                                          passive_scan);
                if (err) {
                        WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
                        goto scan_out;
                }
                brcmf_set_mpc(ndev, 0);
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
 -                                    sizeof(sr->ssid_le));
 +              err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
 +                                           &sr->ssid_le, sizeof(sr->ssid_le));
                if (err) {
                        if (err == -EBUSY)
                                WL_INFO("BUSY: scan for \"%s\" canceled\n",
        return 0;
  
  scan_out:
 -      clear_bit(WL_STATUS_SCANNING, &cfg->status);
 +      clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
        if (timer_pending(&cfg->escan_timeout))
                del_timer_sync(&cfg->escan_timeout);
        cfg->scan_request = NULL;
  }
  
  static s32
 -brcmf_cfg80211_scan(struct wiphy *wiphy,
 -               struct cfg80211_scan_request *request)
 +brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
  {
        struct net_device *ndev = request->wdev->netdev;
        struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
  
        WL_TRACE("Enter\n");
  
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(container_of(request->wdev,
 +                                     struct brcmf_cfg80211_vif, wdev)))
                return -EIO;
  
        if (cfg->iscan_on)
@@@ -1052,8 -1210,7 +1052,8 @@@ static s32 brcmf_set_rts(struct net_dev
  {
        s32 err = 0;
  
 -      err = brcmf_dev_intvar_set(ndev, "rtsthresh", rts_threshold);
 +      err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
 +                                    rts_threshold);
        if (err)
                WL_ERR("Error (%d)\n", err);
  
@@@ -1064,8 -1221,7 +1064,8 @@@ static s32 brcmf_set_frag(struct net_de
  {
        s32 err = 0;
  
 -      err = brcmf_dev_intvar_set(ndev, "fragthresh", frag_threshold);
 +      err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
 +                                    frag_threshold);
        if (err)
                WL_ERR("Error (%d)\n", err);
  
@@@ -1077,7 -1233,7 +1077,7 @@@ static s32 brcmf_set_retry(struct net_d
        s32 err = 0;
        u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL);
  
 -      err = brcmf_exec_dcmd_u32(ndev, cmd, &retry);
 +      err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
        if (err) {
                WL_ERR("cmd (%d) , error (%d)\n", cmd, err);
                return err;
@@@ -1089,11 -1245,10 +1089,11 @@@ static s32 brcmf_cfg80211_set_wiphy_par
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
@@@ -1172,8 -1327,7 +1172,8 @@@ static void brcmf_link_down(struct brcm
        if (cfg->link_up) {
                ndev = cfg_to_ndev(cfg);
                WL_INFO("Call WLC_DISASSOC to stop excess roaming\n ");
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, NULL, 0);
 +              err = brcmf_fil_cmd_data_set(netdev_priv(ndev),
 +                                           BRCMF_C_DISASSOC, NULL, 0);
                if (err)
                        WL_ERR("WLC_DISASSOC failed (%d)\n", err);
                cfg->link_up = false;
@@@ -1186,8 -1340,7 +1186,8 @@@ brcmf_cfg80211_join_ibss(struct wiphy *
                      struct cfg80211_ibss_params *params)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct brcmf_join_params join_params;
        size_t join_params_size = 0;
        s32 err = 0;
        s32 bcnprd;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        if (params->ssid)
                return -EOPNOTSUPP;
        }
  
 -      set_bit(WL_STATUS_CONNECTING, &cfg->status);
 +      set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
  
        if (params->bssid)
                WL_CONN("BSSID: %pM\n", params->bssid);
        if (params->privacy)
                wsec |= WEP_ENABLED;
  
 -      err = brcmf_dev_intvar_set(ndev, "wsec", wsec);
 +      err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
        if (err) {
                WL_ERR("wsec failed (%d)\n", err);
                goto done;
        else
                bcnprd = 100;
  
 -      err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_BCNPRD, &bcnprd);
 +      err = brcmf_fil_cmd_int_set(ifp, BRCM_SET_BCNPRD, bcnprd);
        if (err) {
                WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
                goto done;
                                   BRCMF_ASSOC_PARAMS_FIXED_SIZE;
                memcpy(profile->bssid, params->bssid, ETH_ALEN);
        } else {
 -              memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
 +              memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
                memset(profile->bssid, 0, ETH_ALEN);
        }
  
  
                /* set channel for starter */
                target_channel = cfg->channel;
 -              err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_CHANNEL,
 -                                        &target_channel);
 +              err = brcmf_fil_cmd_int_set(ifp, BRCM_SET_CHANNEL,
 +                                          target_channel);
                if (err) {
                        WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
                        goto done;
        cfg->ibss_starter = false;
  
  
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID,
 -                         &join_params, join_params_size);
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
 +                                   &join_params, join_params_size);
        if (err) {
                WL_ERR("WLC_SET_SSID failed (%d)\n", err);
                goto done;
  
  done:
        if (err)
 -              clear_bit(WL_STATUS_CONNECTING, &cfg->status);
 +              clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
        WL_TRACE("Exit\n");
        return err;
  }
@@@ -1330,11 -1483,10 +1330,11 @@@ static s3
  brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        brcmf_link_down(cfg);
  static s32 brcmf_set_wpa_version(struct net_device *ndev,
                                 struct cfg80211_connect_params *sme)
  {
 -      struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
        struct brcmf_cfg80211_security *sec;
        s32 val = 0;
        s32 err = 0;
        else
                val = WPA_AUTH_DISABLED;
        WL_CONN("setting wpa_auth to 0x%0x\n", val);
 -      err = brcmf_dev_intvar_set(ndev, "wpa_auth", val);
 +      err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "wpa_auth", val);
        if (err) {
                WL_ERR("set wpa_auth failed (%d)\n", err);
                return err;
  static s32 brcmf_set_auth_type(struct net_device *ndev,
                               struct cfg80211_connect_params *sme)
  {
 -      struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
        struct brcmf_cfg80211_security *sec;
        s32 val = 0;
        s32 err = 0;
                break;
        }
  
 -      err = brcmf_dev_intvar_set(ndev, "auth", val);
 +      err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "auth", val);
        if (err) {
                WL_ERR("set auth failed (%d)\n", err);
                return err;
@@@ -1412,7 -1566,8 +1412,7 @@@ static s3
  brcmf_set_set_cipher(struct net_device *ndev,
                     struct cfg80211_connect_params *sme)
  {
 -      struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
        struct brcmf_cfg80211_security *sec;
        s32 pval = 0;
        s32 gval = 0;
        }
  
        WL_CONN("pval (%d) gval (%d)\n", pval, gval);
 -      err = brcmf_dev_intvar_set(ndev, "wsec", pval | gval);
 +      err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "wsec", pval | gval);
        if (err) {
                WL_ERR("error (%d)\n", err);
                return err;
  static s32
  brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
  {
 -      struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
        struct brcmf_cfg80211_security *sec;
        s32 val = 0;
        s32 err = 0;
  
        if (sme->crypto.n_akm_suites) {
 -              err = brcmf_dev_intvar_get(ndev, "wpa_auth", &val);
 +              err = brcmf_fil_iovar_int_get(netdev_priv(ndev),
 +                                            "wpa_auth", &val);
                if (err) {
                        WL_ERR("could not get wpa_auth (%d)\n", err);
                        return err;
                }
  
                WL_CONN("setting wpa_auth to %d\n", val);
 -              err = brcmf_dev_intvar_set(ndev, "wpa_auth", val);
 +              err = brcmf_fil_iovar_int_set(netdev_priv(ndev),
 +                                            "wpa_auth", val);
                if (err) {
                        WL_ERR("could not set wpa_auth (%d)\n", err);
                        return err;
@@@ -1536,11 -1690,13 +1536,11 @@@ static s3
  brcmf_set_sharedkey(struct net_device *ndev,
                    struct cfg80211_connect_params *sme)
  {
 -      struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
        struct brcmf_cfg80211_security *sec;
        struct brcmf_wsec_key key;
        s32 val;
        s32 err = 0;
 -      s32 bssidx;
  
        WL_CONN("key len (%d)\n", sme->key_len);
  
        WL_CONN("key length (%d) key index (%d) algo (%d)\n",
                key.len, key.index, key.algo);
        WL_CONN("key \"%s\"\n", key.data);
 -      bssidx = brcmf_find_bssidx(cfg, ndev);
 -      err = send_key_to_dongle(cfg, bssidx, ndev, &key);
 +      err = send_key_to_dongle(ndev, &key);
        if (err)
                return err;
  
        if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
                WL_CONN("set auth_type to shared key\n");
                val = WL_AUTH_SHARED_KEY;       /* shared key */
 -              err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", val, bssidx);
 +              err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
                if (err)
                        WL_ERR("set auth failed (%d)\n", err);
        }
@@@ -1602,8 -1759,7 +1602,8 @@@ brcmf_cfg80211_connect(struct wiphy *wi
                    struct cfg80211_connect_params *sme)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct ieee80211_channel *chan = sme->channel;
        struct brcmf_join_params join_params;
        size_t join_params_size;
        s32 err = 0;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        if (!sme->ssid) {
                return -EOPNOTSUPP;
        }
  
 -      set_bit(WL_STATUS_CONNECTING, &cfg->status);
 +      set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
  
        if (chan) {
                cfg->channel =
        memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
        join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
  
 -      memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
 +      memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
  
        if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN)
                WL_CONN("ssid \"%s\", len (%d)\n",
  
        brcmf_ch_to_chanspec(cfg->channel,
                             &join_params, &join_params_size);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID,
 -                         &join_params, join_params_size);
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
 +                                   &join_params, join_params_size);
        if (err)
                WL_ERR("WLC_SET_SSID failed (%d)\n", err);
  
  done:
        if (err)
 -              clear_bit(WL_STATUS_CONNECTING, &cfg->status);
 +              clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
        WL_TRACE("Exit\n");
        return err;
  }
@@@ -1696,21 -1852,20 +1696,21 @@@ brcmf_cfg80211_disconnect(struct wiphy 
                       u16 reason_code)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct brcmf_scb_val_le scbval;
        s32 err = 0;
  
        WL_TRACE("Enter. Reason code = %d\n", reason_code);
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
 -      clear_bit(WL_STATUS_CONNECTED, &cfg->status);
 +      clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
  
        memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
        scbval.val = cpu_to_le32(reason_code);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, &scbval,
 -                            sizeof(struct brcmf_scb_val_le));
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
 +                                   &scbval, sizeof(scbval));
        if (err)
                WL_ERR("error (%d)\n", err);
  
@@@ -1727,14 -1882,13 +1727,14 @@@ brcmf_cfg80211_set_tx_power(struct wiph
  
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        u16 txpwrmw;
        s32 err = 0;
        s32 disable = 0;
        s32 dbm = MBM_TO_DBM(mbm);
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        switch (type) {
        }
        /* Make sure radio is off or on as far as software is concerned */
        disable = WL_RADIO_SW_DISABLE << 16;
 -      err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_RADIO, &disable);
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
        if (err)
                WL_ERR("WLC_SET_RADIO error (%d)\n", err);
  
                txpwrmw = 0xffff;
        else
                txpwrmw = (u16) dbm;
 -      err = brcmf_dev_intvar_set(ndev, "qtxpower",
 -                      (s32) (brcmf_mw_to_qdbm(txpwrmw)));
 +      err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
 +                                    (s32)brcmf_mw_to_qdbm(txpwrmw));
        if (err)
                WL_ERR("qtxpower error (%d)\n", err);
        cfg->conf->tx_power = dbm;
@@@ -1773,16 -1927,16 +1773,16 @@@ done
  static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
        s32 txpwrdbm;
        u8 result;
        s32 err = 0;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
 -      err = brcmf_dev_intvar_get(ndev, "qtxpower", &txpwrdbm);
 +      err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
        if (err) {
                WL_ERR("error (%d)\n", err);
                goto done;
@@@ -1800,17 -1954,19 +1800,17 @@@ static s3
  brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
                               u8 key_idx, bool unicast, bool multicast)
  {
 -      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        u32 index;
        u32 wsec;
        s32 err = 0;
 -      s32 bssidx;
  
        WL_TRACE("Enter\n");
        WL_CONN("key index (%d)\n", key_idx);
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
 -      bssidx = brcmf_find_bssidx(cfg, ndev);
 -      err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
 +      err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
        if (err) {
                WL_ERR("WLC_GET_WSEC error (%d)\n", err);
                goto done;
        if (wsec & WEP_ENABLED) {
                /* Just select a new current key */
                index = key_idx;
 -              err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_KEY_PRIMARY,
 -                                        &index);
 +              err = brcmf_fil_cmd_int_set(ifp,
 +                                          BRCMF_C_SET_KEY_PRIMARY, index);
                if (err)
                        WL_ERR("error (%d)\n", err);
        }
@@@ -1833,8 -1989,11 +1833,8 @@@ static s3
  brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
              u8 key_idx, const u8 *mac_addr, struct key_params *params)
  {
 -      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct brcmf_wsec_key key;
 -      struct brcmf_wsec_key_le key_le;
        s32 err = 0;
 -      s32 bssidx;
  
        memset(&key, 0, sizeof(key));
        key.index = (u32) key_idx;
        if (!is_multicast_ether_addr(mac_addr))
                memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
        key.len = (u32) params->key_len;
 -      bssidx = brcmf_find_bssidx(cfg, ndev);
        /* check for key index change */
        if (key.len == 0) {
                /* key delete */
 -              err = send_key_to_dongle(cfg, bssidx, ndev, &key);
 +              err = send_key_to_dongle(ndev, &key);
                if (err)
                        WL_ERR("key delete error (%d)\n", err);
        } else {
                        WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
                        return -EINVAL;
                }
 -              convert_key_from_CPU(&key, &key_le);
 -
 -              brcmf_netdev_wait_pend8021x(ndev);
 -              err  = brcmf_dev_iovar_setbuf_bsscfg(ndev, "wsec_key", &key_le,
 -                                                   sizeof(key_le),
 -                                                   cfg->extra_buf,
 -                                                   WL_EXTRA_BUF_MAX, bssidx);
 +              err = send_key_to_dongle(ndev, &key);
                if (err)
                        WL_ERR("wsec_key error (%d)\n", err);
        }
@@@ -1914,16 -2080,16 +1914,16 @@@ brcmf_cfg80211_add_key(struct wiphy *wi
                    struct key_params *params)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_wsec_key key;
        s32 val;
        s32 wsec;
        s32 err = 0;
        u8 keybuf[8];
 -      s32 bssidx;
  
        WL_TRACE("Enter\n");
        WL_CONN("key index (%d)\n", key_idx);
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        if (mac_addr) {
                goto done;
        }
  
 -      bssidx = brcmf_find_bssidx(cfg, ndev);
 -      err = send_key_to_dongle(cfg, bssidx, ndev, &key);
 +      err = send_key_to_dongle(ndev, &key);
        if (err)
                goto done;
  
 -      err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
 +      err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
        if (err) {
                WL_ERR("get wsec error (%d)\n", err);
                goto done;
        }
        wsec |= val;
 -      err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", wsec, bssidx);
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
        if (err) {
                WL_ERR("set wsec error (%d)\n", err);
                goto done;
@@@ -2006,12 -2173,13 +2006,12 @@@ static s3
  brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
                    u8 key_idx, bool pairwise, const u8 *mac_addr)
  {
 -      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_wsec_key key;
        s32 err = 0;
 -      s32 bssidx;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        memset(&key, 0, sizeof(key));
        WL_CONN("key index (%d)\n", key_idx);
  
        /* Set the new key/index */
 -      bssidx = brcmf_find_bssidx(cfg, ndev);
 -      err = send_key_to_dongle(cfg, bssidx, ndev, &key);
 +      err = send_key_to_dongle(ndev, &key);
        if (err) {
                if (err == -EINVAL) {
                        if (key.index >= DOT11_MAX_DEFAULT_KEYS)
@@@ -2044,20 -2213,22 +2044,20 @@@ brcmf_cfg80211_get_key(struct wiphy *wi
                    void (*callback) (void *cookie, struct key_params * params))
  {
        struct key_params params;
 -      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct brcmf_cfg80211_security *sec;
        s32 wsec;
        s32 err = 0;
 -      s32 bssidx;
  
        WL_TRACE("Enter\n");
        WL_CONN("key index (%d)\n", key_idx);
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        memset(&params, 0, sizeof(params));
  
 -      bssidx = brcmf_find_bssidx(cfg, ndev);
 -      err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
 +      err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
        if (err) {
                WL_ERR("WLC_GET_WSEC error (%d)\n", err);
                /* Ignore this error, may happen during DISASSOC */
@@@ -2109,33 -2280,33 +2109,33 @@@ brcmf_cfg80211_get_station(struct wiph
                           u8 *mac, struct station_info *sinfo)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct brcmf_scb_val_le scb_val;
        int rssi;
        s32 rate;
        s32 err = 0;
        u8 *bssid = profile->bssid;
 -      struct brcmf_sta_info_le *sta_info_le;
 +      struct brcmf_sta_info_le sta_info_le;
  
        WL_TRACE("Enter, MAC %pM\n", mac);
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        if (cfg->conf->mode == WL_MODE_AP) {
 -              err = brcmf_dev_iovar_getbuf(ndev, "sta_info", mac, ETH_ALEN,
 -                                           cfg->dcmd_buf,
 -                                           WL_DCMD_LEN_MAX);
 +              memcpy(&sta_info_le, mac, ETH_ALEN);
 +              err = brcmf_fil_iovar_data_get(ifp, "sta_info",
 +                                             &sta_info_le,
 +                                             sizeof(sta_info_le));
                if (err < 0) {
                        WL_ERR("GET STA INFO failed, %d\n", err);
                        goto done;
                }
 -              sta_info_le = (struct brcmf_sta_info_le *)cfg->dcmd_buf;
 -
                sinfo->filled = STATION_INFO_INACTIVE_TIME;
 -              sinfo->inactive_time = le32_to_cpu(sta_info_le->idle) * 1000;
 -              if (le32_to_cpu(sta_info_le->flags) & BRCMF_STA_ASSOC) {
 +              sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
 +              if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
                        sinfo->filled |= STATION_INFO_CONNECTED_TIME;
 -                      sinfo->connected_time = le32_to_cpu(sta_info_le->in);
 +                      sinfo->connected_time = le32_to_cpu(sta_info_le.in);
                }
                WL_TRACE("STA idle time : %d ms, connected time :%d sec\n",
                         sinfo->inactive_time, sinfo->connected_time);
                        goto done;
                }
                /* Report the current tx rate */
 -              err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_RATE, &rate);
 +      err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
                if (err) {
                        WL_ERR("Could not get rate (%d)\n", err);
                        goto done;
                        WL_CONN("Rate %d Mbps\n", rate / 2);
                }
  
 -              if (test_bit(WL_STATUS_CONNECTED, &cfg->status)) {
 +              if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
 +                           &ifp->vif->sme_state)) {
                        memset(&scb_val, 0, sizeof(scb_val));
 -                      err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val,
 -                                            sizeof(scb_val));
 +                      err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
 +                                                   &scb_val, sizeof(scb_val));
                        if (err) {
                                WL_ERR("Could not get rssi (%d)\n", err);
                                goto done;
@@@ -2186,7 -2356,6 +2186,7 @@@ brcmf_cfg80211_set_power_mgmt(struct wi
        s32 pm;
        s32 err = 0;
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
  
        WL_TRACE("Enter\n");
  
         * FW later while initializing the dongle
         */
        cfg->pwr_save = enabled;
 -      if (!test_bit(WL_STATUS_READY, &cfg->status)) {
 +      if (!check_vif_up(ifp->vif)) {
  
                WL_INFO("Device is not ready, storing the value in cfg_info struct\n");
                goto done;
        pm = enabled ? PM_FAST : PM_OFF;
        WL_INFO("power save %s\n", (pm ? "enabled" : "disabled"));
  
 -      err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &pm);
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
        if (err) {
                if (err == -ENODEV)
                        WL_ERR("net_device is not ready yet\n");
@@@ -2224,7 -2393,6 +2224,7 @@@ brcmf_cfg80211_set_bitrate_mask(struct 
                             const u8 *addr,
                             const struct cfg80211_bitrate_mask *mask)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcm_rateset_le rateset_le;
        s32 rate;
        s32 val;
        s32 err = 0;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        /* addr param is always NULL. ignore it */
        /* Get current rateset */
 -      err = brcmf_exec_dcmd(ndev, BRCM_GET_CURR_RATESET, &rateset_le,
 -                            sizeof(rateset_le));
 +      err = brcmf_fil_cmd_data_get(ifp, BRCM_GET_CURR_RATESET,
 +                                   &rateset_le, sizeof(rateset_le));
        if (err) {
                WL_ERR("could not get current rateset (%d)\n", err);
                goto done;
         *      Set rate override,
         *      Since the is a/b/g-blind, both a/bg_rate are enforced.
         */
 -      err_bg = brcmf_dev_intvar_set(ndev, "bg_rate", rate);
 -      err_a = brcmf_dev_intvar_set(ndev, "a_rate", rate);
 +      err_bg = brcmf_fil_iovar_int_set(ifp, "bg_rate", rate);
 +      err_a = brcmf_fil_iovar_int_set(ifp, "a_rate", rate);
        if (err_bg && err_a) {
                WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a);
                err = err_bg | err_a;
@@@ -2397,8 -2565,7 +2397,8 @@@ static s32 wl_inform_ibss(struct brcmf_
  
        *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
  
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_BSS_INFO, buf, WL_BSS_INFO_MAX);
 +      err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
 +                                   buf, WL_BSS_INFO_MAX);
        if (err) {
                WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
                goto CleanUp;
@@@ -2507,12 -2674,12 +2507,12 @@@ brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u3
        return false;
  }
  
 -struct brcmf_vs_tlv *
 +static struct brcmf_vs_tlv *
  brcmf_find_wpaie(u8 *parse, u32 len)
  {
        struct brcmf_tlv *ie;
  
 -      while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_WPA))) {
 +      while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
                if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
                                     WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
                        return (struct brcmf_vs_tlv *)ie;
  
  static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg)
  {
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_bss_info_le *bi;
        struct brcmf_ssid *ssid;
        struct brcmf_tlv *tim;
        ssid = &profile->ssid;
  
        *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
 -      err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCMF_C_GET_BSS_INFO,
 -                      cfg->extra_buf, WL_EXTRA_BUF_MAX);
 +      err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
 +                                   cfg->extra_buf, WL_EXTRA_BUF_MAX);
        if (err) {
                WL_ERR("Could not get bss info %d\n", err);
                goto update_bss_info_out;
                * so we speficially query dtim information to dongle.
                */
                u32 var;
 -              err = brcmf_dev_intvar_get(cfg_to_ndev(cfg),
 -                                         "dtim_assoc", &var);
 +              err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
                if (err) {
                        WL_ERR("wl dtim_assoc failed (%d)\n", err);
                        goto update_bss_info_out;
                dtim_period = (u8)var;
        }
  
 -      profile->beacon_interval = beacon_interval;
 -      profile->dtim_period = dtim_period;
 -
  update_bss_info_out:
        WL_TRACE("Exit");
        return err;
@@@ -2586,7 -2755,7 +2586,7 @@@ static void brcmf_abort_scanning(struc
        struct escan_info *escan = &cfg->escan_info;
        struct brcmf_ssid ssid;
  
 -      set_bit(WL_STATUS_SCAN_ABORTING, &cfg->status);
 +      set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
        if (cfg->iscan_on) {
                iscan->state = WL_ISCAN_STATE_IDLE;
  
                escan->escan_state = WL_ESCAN_STATE_IDLE;
                brcmf_notify_escan_complete(cfg, escan->ndev, true, true);
        }
 -      clear_bit(WL_STATUS_SCANNING, &cfg->status);
 -      clear_bit(WL_STATUS_SCAN_ABORTING, &cfg->status);
 +      clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
 +      clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
  }
  
  static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan,
        struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan);
        struct net_device *ndev = cfg_to_ndev(cfg);
  
 -      if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
 +      if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
                WL_ERR("Scan complete while device not scanning\n");
                return;
        }
@@@ -2651,6 -2820,7 +2651,6 @@@ static s3
  brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan, u32 *status,
                     struct brcmf_scan_results **bss_list)
  {
 -      struct brcmf_iscan_results list;
        struct brcmf_scan_results *results;
        struct brcmf_scan_results_le *results_le;
        struct brcmf_iscan_results *list_buf;
        list_buf = (struct brcmf_iscan_results *)iscan->scan_buf;
        results = &list_buf->results;
        results_le = &list_buf->results_le;
 -      results->buflen = BRCMF_ISCAN_RESULTS_FIXED_SIZE;
 -      results->version = 0;
 -      results->count = 0;
 +      results_le->buflen = cpu_to_le32(sizeof(iscan->scan_buf));
 +      results_le->version = 0;
 +      results_le->count = 0;
  
 -      memset(&list, 0, sizeof(list));
 -      list.results_le.buflen = cpu_to_le32(WL_ISCAN_BUF_MAX);
 -      err = brcmf_dev_iovar_getbuf(iscan->ndev, "iscanresults", &list,
 -                                   BRCMF_ISCAN_RESULTS_FIXED_SIZE,
 -                                   iscan->scan_buf, WL_ISCAN_BUF_MAX);
 +      err = brcmf_fil_iovar_data_get(netdev_priv(iscan->ndev), "iscanresults",
 +                                     iscan->scan_buf,
 +                                     sizeof(iscan->scan_buf));
        if (err) {
                WL_ERR("error (%d)\n", err);
                return err;
@@@ -2880,10 -3052,10 +2880,10 @@@ brcmf_cfg80211_escan_handler(struct brc
        status = be32_to_cpu(e->status);
  
        if (!ndev || !cfg->escan_on ||
 -                      !test_bit(WL_STATUS_SCANNING, &cfg->status)) {
 +                      !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
                WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n",
                        ndev, cfg->escan_on,
 -                      !test_bit(WL_STATUS_SCANNING, &cfg->status));
 +                      !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status));
                return -EPERM;
        }
  
@@@ -2987,17 -3159,16 +2987,17 @@@ static __always_inline void brcmf_delay
  static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
  
        /*
 -       * Check for WL_STATUS_READY before any function call which
 +       * Check for BRCMF_VIF_STATUS_READY before any function call which
         * could result is bus access. Don't block the resume for
         * any driver error conditions
         */
        WL_TRACE("Enter\n");
  
 -      if (test_bit(WL_STATUS_READY, &cfg->status))
 -              brcmf_invoke_iscan(wiphy_to_cfg(wiphy));
 +      if (check_vif_up(ifp->vif))
 +              brcmf_invoke_iscan(cfg);
  
        WL_TRACE("Exit\n");
        return 0;
@@@ -3008,52 -3179,84 +3008,52 @@@ static s32 brcmf_cfg80211_suspend(struc
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_cfg80211_vif *vif;
  
        WL_TRACE("Enter\n");
  
        /*
 -       * Check for WL_STATUS_READY before any function call which
 -       * could result is bus access. Don't block the suspend for
 -       * any driver error conditions
 -       */
 -
 -      /*
 -       * While going to suspend if associated with AP disassociate
 -       * from AP to save power while system is in suspended state
 +       * if the primary net_device is not READY there is nothing
 +       * we can do but pray resume goes smoothly.
         */
 -      if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
 -           test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
 -           test_bit(WL_STATUS_READY, &cfg->status)) {
 -              WL_INFO("Disassociating from AP"
 -                      " while entering suspend state\n");
 -              brcmf_link_down(cfg);
 +      vif = ((struct brcmf_if *)netdev_priv(ndev))->vif;
 +      if (!check_vif_up(vif))
 +              goto exit;
  
 +      list_for_each_entry(vif, &cfg->vif_list, list) {
 +              if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
 +                      continue;
                /*
 -               * Make sure WPA_Supplicant receives all the event
 -               * generated due to DISASSOC call to the fw to keep
 -               * the state fw and WPA_Supplicant state consistent
 +               * While going to suspend if associated with AP disassociate
 +               * from AP to save power while system is in suspended state
                 */
 -              brcmf_delay(500);
 +              if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state) ||
 +                  test_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state)) {
 +                      WL_INFO("Disassociating from AP before suspend\n");
 +                      brcmf_link_down(cfg);
 +
 +                      /* Make sure WPA_Supplicant receives all the event
 +                       * generated due to DISASSOC call to the fw to keep
 +                       * the state fw and WPA_Supplicant state consistent
 +                       */
 +                      brcmf_delay(500);
 +              }
        }
  
 -      if (test_bit(WL_STATUS_READY, &cfg->status))
 +      /* end any scanning */
 +      if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
                brcmf_abort_scanning(cfg);
 -      else
 -              clear_bit(WL_STATUS_SCANNING, &cfg->status);
  
        /* Turn off watchdog timer */
 -      if (test_bit(WL_STATUS_READY, &cfg->status))
 -              brcmf_set_mpc(ndev, 1);
 +      brcmf_set_mpc(ndev, 1);
  
 +exit:
        WL_TRACE("Exit\n");
 -
 +      /* clear any scanning activity */
 +      cfg->scan_status = 0;
        return 0;
  }
  
 -static __used s32
 -brcmf_dev_bufvar_set(struct net_device *ndev, s8 *name, s8 *buf, s32 len)
 -{
 -      struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
 -      u32 buflen;
 -
 -      buflen = brcmf_c_mkiovar(name, buf, len, cfg->dcmd_buf,
 -                             WL_DCMD_LEN_MAX);
 -      BUG_ON(!buflen);
 -
 -      return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, cfg->dcmd_buf,
 -                             buflen);
 -}
 -
 -static s32
 -brcmf_dev_bufvar_get(struct net_device *ndev, s8 *name, s8 *buf,
 -                s32 buf_len)
 -{
 -      struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
 -      u32 len;
 -      s32 err = 0;
 -
 -      len = brcmf_c_mkiovar(name, NULL, 0, cfg->dcmd_buf,
 -                          WL_DCMD_LEN_MAX);
 -      BUG_ON(!len);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, cfg->dcmd_buf,
 -                            WL_DCMD_LEN_MAX);
 -      if (err) {
 -              WL_ERR("error (%d)\n", err);
 -              return err;
 -      }
 -      memcpy(buf, cfg->dcmd_buf, buf_len);
 -
 -      return err;
 -}
 -
  static __used s32
  brcmf_update_pmklist(struct net_device *ndev,
                     struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
        }
  
        if (!err)
 -              brcmf_dev_bufvar_set(ndev, "pmkid_info", (char *)pmk_list,
 -                                      sizeof(*pmk_list));
 +              brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
 +                                       (char *)pmk_list, sizeof(*pmk_list));
  
        return err;
  }
@@@ -3083,14 -3286,13 +3083,14 @@@ brcmf_cfg80211_set_pmksa(struct wiphy *
                         struct cfg80211_pmksa *pmksa)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
        s32 err = 0;
        int i;
        int pmkid_len;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        pmkid_len = le32_to_cpu(pmkids->npmkid);
@@@ -3123,13 -3325,12 +3123,13 @@@ brcmf_cfg80211_del_pmksa(struct wiphy *
                      struct cfg80211_pmksa *pmksa)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct pmkid_list pmkid;
        s32 err = 0;
        int i, pmkid_len;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
@@@ -3174,11 -3375,10 +3174,11 @@@ static s3
  brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
  
        WL_TRACE("Enter\n");
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
@@@ -3278,15 -3478,15 +3278,15 @@@ brcmf_notify_sched_scan_results(struct 
                if (request->n_ssids)
                        request->ssids = &ssid[0];
  
 -              if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
 +              if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
                        /* Abort any on-going scan */
                        brcmf_abort_scanning(cfg);
                }
  
 -              set_bit(WL_STATUS_SCANNING, &cfg->status);
 +              set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
                err = brcmf_do_escan(cfg, wiphy, ndev, request);
                if (err) {
 -                      clear_bit(WL_STATUS_SCANNING, &cfg->status);
 +                      clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
                        goto out_err;
                }
                cfg->sched_escan = true;
@@@ -3312,14 -3512,15 +3312,14 @@@ out_err
  #ifndef CONFIG_BRCMISCAN
  static int brcmf_dev_pno_clean(struct net_device *ndev)
  {
 -      char iovbuf[128];
        int ret;
  
        /* Disable pfn */
 -      ret = brcmf_dev_intvar_set(ndev, "pfn", 0);
 +      ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
        if (ret == 0) {
                /* clear pfn */
 -              ret = brcmf_dev_iovar_setbuf(ndev, "pfnclear", NULL, 0,
 -                                           iovbuf, sizeof(iovbuf));
 +              ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
 +                                             NULL, 0);
        }
        if (ret < 0)
                WL_ERR("failed code %d\n", ret);
  static int brcmf_dev_pno_config(struct net_device *ndev)
  {
        struct brcmf_pno_param_le pfn_param;
 -      char iovbuf[128];
  
        memset(&pfn_param, 0, sizeof(pfn_param));
        pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
        /* set up pno scan fr */
        pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
  
 -      return brcmf_dev_iovar_setbuf(ndev, "pfn_set",
 -                                    &pfn_param, sizeof(pfn_param),
 -                                    iovbuf, sizeof(iovbuf));
 +      return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
 +                                      &pfn_param, sizeof(pfn_param));
  }
  
  static int
@@@ -3351,7 -3554,7 +3351,7 @@@ brcmf_cfg80211_sched_scan_start(struct 
                                struct net_device *ndev,
                                struct cfg80211_sched_scan_request *request)
  {
 -      char iovbuf[128];
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
        struct brcmf_pno_net_param_le pfn;
        int i;
  
        WL_SCAN("Enter n_match_sets:%d   n_ssids:%d\n",
                request->n_match_sets, request->n_ssids);
 -      if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
 -              WL_ERR("Scanning already : status (%lu)\n", cfg->status);
 +      if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
 +              WL_ERR("Scanning already: status (%lu)\n", cfg->scan_status);
                return -EAGAIN;
        }
  
                        pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
                        pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
                        memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
 -                      ret = brcmf_dev_iovar_setbuf(ndev, "pfn_add",
 -                                                   &pfn, sizeof(pfn),
 -                                                   iovbuf, sizeof(iovbuf));
 +                      ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
 +                                                     sizeof(pfn));
                        WL_SCAN(">>> PNO filter %s for ssid (%s)\n",
                                ret == 0 ? "set" : "failed",
                                ssid->ssid);
                }
                /* Enable the PNO */
 -              if (brcmf_dev_intvar_set(ndev, "pfn", 1) < 0) {
 +              if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
                        WL_ERR("PNO enable failed!! ret=%d\n", ret);
                        return -EINVAL;
                }
@@@ -3452,20 -3656,12 +3452,20 @@@ static int brcmf_cfg80211_sched_scan_st
  static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
  {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      struct net_device *ndev = cfg->wdev->netdev;
 +      struct net_device *ndev = cfg_to_ndev(cfg);
        struct brcmf_dcmd *dcmd = data;
        struct sk_buff *reply;
        int ret;
  
 -      ret = brcmf_netlink_dcmd(ndev, dcmd);
 +      WL_TRACE("cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
 +               dcmd->buf, dcmd->len);
 +
 +      if (dcmd->set)
 +              ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
 +                                           dcmd->buf, dcmd->len);
 +      else
 +              ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
 +                                           dcmd->buf, dcmd->len);
        if (ret == 0) {
                reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
                nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
  
  static s32 brcmf_configure_opensecurity(struct net_device *ndev, s32 bssidx)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err;
  
        /* set auth */
 -      err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", 0, bssidx);
 +      err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
        if (err < 0) {
                WL_ERR("auth error %d\n", err);
                return err;
        }
        /* set wsec */
 -      err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", 0, bssidx);
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
        if (err < 0) {
                WL_ERR("wsec error %d\n", err);
                return err;
        }
        /* set upper-layer auth */
 -      err = brcmf_dev_intvar_set_bsscfg(ndev, "wpa_auth",
 -                                        WPA_AUTH_NONE, bssidx);
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
        if (err < 0) {
                WL_ERR("wpa_auth error %d\n", err);
                return err;
@@@ -3514,7 -3710,6 +3514,7 @@@ static s3
  brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
                     bool is_rsn_ie, s32 bssidx)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        u32 auth = 0; /* d11 open authentication */
        u16 count;
        s32 err = 0;
                                wme_bss_disable = 0;
                }
                /* set wme_bss_disable to sync RSN Capabilities */
 -              err = brcmf_dev_intvar_set_bsscfg(ndev, "wme_bss_disable",
 -                                                wme_bss_disable, bssidx);
 +              err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
 +                                             wme_bss_disable);
                if (err < 0) {
                        WL_ERR("wme_bss_disable error %d\n", err);
                        goto exit;
        wsec = (pval | gval | SES_OW_ENABLED);
  
        /* set auth */
 -      err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", auth, bssidx);
 +      err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
        if (err < 0) {
                WL_ERR("auth error %d\n", err);
                goto exit;
        }
        /* set wsec */
 -      err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", wsec, bssidx);
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
        if (err < 0) {
                WL_ERR("wsec error %d\n", err);
                goto exit;
        }
        /* set upper-layer auth */
 -      err = brcmf_dev_intvar_set_bsscfg(ndev, "wpa_auth", wpa_auth, bssidx);
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
        if (err < 0) {
                WL_ERR("wpa_auth error %d\n", err);
                goto exit;
@@@ -3768,19 -3963,17 +3768,19 @@@ brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u
        return ie_len + VNDR_IE_HDR_SIZE;
  }
  
 -s32
 +static s32
  brcmf_set_management_ie(struct brcmf_cfg80211_info *cfg,
 -                      struct net_device *ndev, s32 bssidx, s32 pktflag,
 +                      struct net_device *ndev, s32 pktflag,
                        u8 *vndr_ie_buf, u32 vndr_ie_len)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct vif_saved_ie *saved_ie = &ifp->vif->saved_ie;
        s32 err = 0;
        u8  *iovar_ie_buf;
        u8  *curr_ie_buf;
        u8  *mgmt_ie_buf = NULL;
        int mgmt_ie_buf_len;
 -      u32 *mgmt_ie_len = 0;
 +      u32 *mgmt_ie_len;
        u32 del_add_ie_buf_len = 0;
        u32 total_ie_buf_len = 0;
        u32 parsed_ie_buf_len = 0;
        u8 *ptr;
        int remained_buf_len;
  
 -      WL_TRACE("bssidx %d, pktflag : 0x%02X\n", bssidx, pktflag);
 +      WL_TRACE("bssidx %d, pktflag : 0x%02X\n",
 +               brcmf_ndev_bssidx(ndev), pktflag);
        iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
        if (!iovar_ie_buf)
                return -ENOMEM;
        curr_ie_buf = iovar_ie_buf;
 -      if (test_bit(WL_STATUS_AP_CREATING, &cfg->status) ||
 -          test_bit(WL_STATUS_AP_CREATED, &cfg->status)) {
 +      if (ifp->vif->mode == WL_MODE_AP) {
                switch (pktflag) {
                case VNDR_IE_PRBRSP_FLAG:
 -                      mgmt_ie_buf = cfg->ap_info->probe_res_ie;
 -                      mgmt_ie_len = &cfg->ap_info->probe_res_ie_len;
 -                      mgmt_ie_buf_len =
 -                              sizeof(cfg->ap_info->probe_res_ie);
 +                      mgmt_ie_buf = saved_ie->probe_res_ie;
 +                      mgmt_ie_len = &saved_ie->probe_res_ie_len;
 +                      mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
                        break;
                case VNDR_IE_BEACON_FLAG:
 -                      mgmt_ie_buf = cfg->ap_info->beacon_ie;
 -                      mgmt_ie_len = &cfg->ap_info->beacon_ie_len;
 -                      mgmt_ie_buf_len = sizeof(cfg->ap_info->beacon_ie);
 +                      mgmt_ie_buf = saved_ie->beacon_ie;
 +                      mgmt_ie_len = &saved_ie->beacon_ie_len;
 +                      mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
                        break;
                default:
                        err = -EPERM;
                        WL_ERR("not suitable type\n");
                        goto exit;
                }
 -              bssidx = 0;
        } else {
                err = -EPERM;
                WL_ERR("not suitable type\n");
                }
        }
        if (total_ie_buf_len) {
 -              err  = brcmf_dev_iovar_setbuf_bsscfg(ndev, "vndr_ie",
 -                                                   iovar_ie_buf,
 -                                                   total_ie_buf_len,
 -                                                   cfg->extra_buf,
 -                                                   WL_EXTRA_BUF_MAX, bssidx);
 +              err  = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
 +                                               total_ie_buf_len);
                if (err)
                        WL_ERR("vndr ie set error : %d\n", err);
        }
@@@ -3925,9 -4123,9 +3925,9 @@@ brcmf_cfg80211_start_ap(struct wiphy *w
                        struct cfg80211_ap_settings *settings)
  {
        s32 ie_offset;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_tlv *ssid_ie;
        struct brcmf_ssid_le ssid_le;
 -      s32 ioctl_value;
        s32 err = -EPERM;
        struct brcmf_tlv *rsn_ie;
        struct brcmf_vs_tlv *wpa_ie;
                 settings->ssid, settings->ssid_len, settings->auth_type,
                 settings->inactivity_timeout);
  
 -      if (!test_bit(WL_STATUS_AP_CREATING, &cfg->status)) {
 +      if (!test_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state)) {
                WL_ERR("Not in AP creation mode\n");
                return -EPERM;
        }
        }
  
        brcmf_set_mpc(ndev, 0);
 -      ioctl_value = 1;
 -      err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_DOWN, &ioctl_value);
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
        if (err < 0) {
                WL_ERR("BRCMF_C_DOWN error %d\n", err);
                goto exit;
        }
 -      ioctl_value = 1;
 -      err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &ioctl_value);
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
        if (err < 0) {
                WL_ERR("SET INFRA error %d\n", err);
                goto exit;
        }
 -      ioctl_value = 1;
 -      err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AP, &ioctl_value);
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
        if (err < 0) {
                WL_ERR("setting AP mode failed %d\n", err);
                goto exit;
                cfg->ap_info->security_mode = false;
        }
        /* Set Beacon IEs to FW */
 -      err = brcmf_set_management_ie(cfg, ndev, bssidx,
 +      err = brcmf_set_management_ie(cfg, ndev,
                                      VNDR_IE_BEACON_FLAG,
                                      (u8 *)settings->beacon.tail,
                                      settings->beacon.tail_len);
                WL_TRACE("Applied Vndr IEs for Beacon\n");
  
        /* Set Probe Response IEs to FW */
 -      err = brcmf_set_management_ie(cfg, ndev, bssidx,
 +      err = brcmf_set_management_ie(cfg, ndev,
                                      VNDR_IE_PRBRSP_FLAG,
                                      (u8 *)settings->beacon.proberesp_ies,
                                      settings->beacon.proberesp_ies_len);
                WL_TRACE("Applied Vndr IEs for Probe Resp\n");
  
        if (settings->beacon_interval) {
 -              ioctl_value = settings->beacon_interval;
 -              err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_BCNPRD,
 -                                        &ioctl_value);
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
 +                                          settings->beacon_interval);
                if (err < 0) {
                        WL_ERR("Beacon Interval Set Error, %d\n", err);
                        goto exit;
                }
        }
        if (settings->dtim_period) {
 -              ioctl_value = settings->dtim_period;
 -              err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_DTIMPRD,
 -                                        &ioctl_value);
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
 +                                          settings->dtim_period);
                if (err < 0) {
                        WL_ERR("DTIM Interval Set Error, %d\n", err);
                        goto exit;
                }
        }
 -      ioctl_value = 1;
 -      err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_UP, &ioctl_value);
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
        if (err < 0) {
                WL_ERR("BRCMF_C_UP error (%d)\n", err);
                goto exit;
        /* join parameters starts with ssid */
        memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
        /* create softap */
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID, &join_params,
 -                            sizeof(join_params));
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
 +                                   &join_params, sizeof(join_params));
        if (err < 0) {
                WL_ERR("SET SSID error (%d)\n", err);
                goto exit;
        }
 -      clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
 -      set_bit(WL_STATUS_AP_CREATED, &cfg->status);
 +      clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
 +      set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
  
  exit:
        if (err)
  
  static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 -      s32 ioctl_value;
        s32 err = -EPERM;
  
        WL_TRACE("Enter\n");
                /* Due to most likely deauths outstanding we sleep */
                /* first to make sure they get processed by fw. */
                msleep(400);
 -              ioctl_value = 0;
 -              err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AP, &ioctl_value);
 +              err = brcmf_fil_cmd_int_set(netdev_priv(ndev),
 +                                          BRCMF_C_SET_AP, 0);
                if (err < 0) {
                        WL_ERR("setting AP mode failed %d\n", err);
                        goto exit;
                }
 -              ioctl_value = 0;
 -              err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_UP, &ioctl_value);
 +              err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_UP, 0);
                if (err < 0) {
                        WL_ERR("BRCMF_C_UP error %d\n", err);
                        goto exit;
                }
                brcmf_set_mpc(ndev, 1);
 -              clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
 -              clear_bit(WL_STATUS_AP_CREATED, &cfg->status);
 +              clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
 +              clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
        }
  exit:
        return err;
@@@ -4121,7 -4326,6 +4121,7 @@@ brcmf_cfg80211_del_station(struct wiph
                           u8 *mac)
  {
        struct brcmf_scb_val_le scbval;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err;
  
        if (!mac)
  
        WL_TRACE("Enter %pM\n", mac);
  
 -      if (!check_sys_up(wiphy))
 +      if (!check_vif_up(ifp->vif))
                return -EIO;
  
        memcpy(&scbval.ea, mac, ETH_ALEN);
        scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
 -                            &scbval, sizeof(scbval));
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
 +                                   &scbval, sizeof(scbval));
        if (err)
                WL_ERR("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
  
@@@ -4197,7 -4401,7 +4197,7 @@@ static s32 brcmf_mode_to_nl80211_iftype
  
  static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
  {
- #ifndef CONFIG_BRCMFISCAN
+ #ifndef CONFIG_BRCMISCAN
        /* scheduled scan settings */
        wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
        wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
  #endif
  }
  
 -static struct wireless_dev *brcmf_alloc_wdev(struct device *ndev)
 +static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
  {
 -      struct wireless_dev *wdev;
 +      struct wiphy *wiphy;
        s32 err = 0;
  
 -      wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
 -      if (!wdev)
 -              return ERR_PTR(-ENOMEM);
 -
 -      wdev->wiphy = wiphy_new(&wl_cfg80211_ops,
 -                              sizeof(struct brcmf_cfg80211_info));
 -      if (!wdev->wiphy) {
 +      wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
 +      if (!wiphy) {
                WL_ERR("Could not allocate wiphy device\n");
 -              err = -ENOMEM;
 -              goto wiphy_new_out;
 -      }
 -      set_wiphy_dev(wdev->wiphy, ndev);
 -      wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
 -      wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
 -      wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
 -                                     BIT(NL80211_IFTYPE_ADHOC) |
 -                                     BIT(NL80211_IFTYPE_AP);
 -      wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
 -      wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a;    /* Set
 +              return ERR_PTR(-ENOMEM);
 +      }
 +      set_wiphy_dev(wiphy, phydev);
 +      wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
 +      wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
 +      wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
 +                               BIT(NL80211_IFTYPE_ADHOC) |
 +                               BIT(NL80211_IFTYPE_AP);
 +      wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
 +      wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a;  /* Set
                                                * it as 11a by default.
                                                * This will be updated with
                                                * 11n phy tables in
                                                * "ifconfig up"
                                                * if phy has 11n capability
                                                */
 -      wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
 -      wdev->wiphy->cipher_suites = __wl_cipher_suites;
 -      wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
 -      wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;      /* enable power
 +      wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
 +      wiphy->cipher_suites = __wl_cipher_suites;
 +      wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
 +      wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;    /* enable power
                                                                 * save mode
                                                                 * by default
                                                                 */
 -      brcmf_wiphy_pno_params(wdev->wiphy);
 -      err = wiphy_register(wdev->wiphy);
 +      brcmf_wiphy_pno_params(wiphy);
 +      err = wiphy_register(wiphy);
        if (err < 0) {
                WL_ERR("Could not register wiphy device (%d)\n", err);
 -              goto wiphy_register_out;
 +              wiphy_free(wiphy);
 +              return ERR_PTR(err);
 +      }
 +      return wiphy;
 +}
 +
 +static
 +struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
 +                                         struct net_device *netdev,
 +                                         s32 mode, bool pm_block)
 +{
 +      struct brcmf_cfg80211_vif *vif;
 +
 +      if (cfg->vif_cnt == BRCMF_IFACE_MAX_CNT)
 +              return ERR_PTR(-ENOSPC);
 +
 +      vif = kzalloc(sizeof(*vif), GFP_KERNEL);
 +      if (!vif)
 +              return ERR_PTR(-ENOMEM);
 +
 +      vif->wdev.wiphy = cfg->wiphy;
 +      vif->wdev.netdev = netdev;
 +      vif->wdev.iftype = brcmf_mode_to_nl80211_iftype(mode);
 +
 +      if (netdev) {
 +              vif->ifp = netdev_priv(netdev);
 +              netdev->ieee80211_ptr = &vif->wdev;
 +              SET_NETDEV_DEV(netdev, wiphy_dev(cfg->wiphy));
        }
 -      return wdev;
  
 -wiphy_register_out:
 -      wiphy_free(wdev->wiphy);
 +      vif->mode = mode;
 +      vif->pm_block = pm_block;
 +      vif->roam_off = -1;
  
 -wiphy_new_out:
 -      kfree(wdev);
 +      brcmf_init_prof(&vif->profile);
  
 -      return ERR_PTR(err);
 +      list_add_tail(&vif->list, &cfg->vif_list);
 +      cfg->vif_cnt++;
 +      return vif;
  }
  
 -static void brcmf_free_wdev(struct brcmf_cfg80211_info *cfg)
 +static void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
  {
 -      struct wireless_dev *wdev = cfg->wdev;
 +      struct brcmf_cfg80211_info *cfg;
 +      struct wiphy *wiphy;
  
 -      if (!wdev) {
 -              WL_ERR("wdev is invalid\n");
 -              return;
 +      wiphy = vif->wdev.wiphy;
 +      cfg = wiphy_priv(wiphy);
 +      list_del(&vif->list);
 +      cfg->vif_cnt--;
 +
 +      kfree(vif);
 +      if (!cfg->vif_cnt) {
 +              wiphy_unregister(wiphy);
 +              wiphy_free(wiphy);
        }
 -      wiphy_unregister(wdev->wiphy);
 -      wiphy_free(wdev->wiphy);
 -      kfree(wdev);
 -      cfg->wdev = NULL;
  }
  
  static bool brcmf_is_linkup(struct brcmf_cfg80211_info *cfg,
@@@ -4362,7 -4541,7 +4362,7 @@@ static void brcmf_clear_assoc_ies(struc
  
  static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg)
  {
 -      struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
        struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
        struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
        u32 req_len;
  
        brcmf_clear_assoc_ies(cfg);
  
 -      err = brcmf_dev_bufvar_get(ndev, "assoc_info", cfg->extra_buf,
 -                              WL_ASSOC_INFO_MAX);
 +      err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
 +                                     cfg->extra_buf, WL_ASSOC_INFO_MAX);
        if (err) {
                WL_ERR("could not get assoc info (%d)\n", err);
                return err;
        req_len = le32_to_cpu(assoc_info->req_len);
        resp_len = le32_to_cpu(assoc_info->resp_len);
        if (req_len) {
 -              err = brcmf_dev_bufvar_get(ndev, "assoc_req_ies",
 -                                         cfg->extra_buf,
 -                                         WL_ASSOC_INFO_MAX);
 +              err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
 +                                             cfg->extra_buf,
 +                                             WL_ASSOC_INFO_MAX);
                if (err) {
                        WL_ERR("could not get assoc req (%d)\n", err);
                        return err;
                conn_info->req_ie = NULL;
        }
        if (resp_len) {
 -              err = brcmf_dev_bufvar_get(ndev, "assoc_resp_ies",
 -                                         cfg->extra_buf,
 -                                         WL_ASSOC_INFO_MAX);
 +              err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
 +                                             cfg->extra_buf,
 +                                             WL_ASSOC_INFO_MAX);
                if (err) {
                        WL_ERR("could not get assoc resp (%d)\n", err);
                        return err;
@@@ -4424,8 -4603,7 +4424,8 @@@ brcmf_bss_roaming_done(struct brcmf_cfg
                       struct net_device *ndev,
                       const struct brcmf_event_msg *e)
  {
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
        struct wiphy *wiphy = cfg_to_wiphy(cfg);
        struct ieee80211_channel *notify_channel = NULL;
  
        /* data sent to dongle has to be little endian */
        *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_BSS_INFO, buf, WL_BSS_INFO_MAX);
 +      err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
 +                                   buf, WL_BSS_INFO_MAX);
  
        if (err)
                goto done;
@@@ -4475,7 -4652,7 +4475,7 @@@ done
                        conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
        WL_CONN("Report roaming result\n");
  
 -      set_bit(WL_STATUS_CONNECTED, &cfg->status);
 +      set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
        WL_TRACE("Exit\n");
        return err;
  }
@@@ -4485,15 -4662,13 +4485,15 @@@ brcmf_bss_connect_done(struct brcmf_cfg
                       struct net_device *ndev, const struct brcmf_event_msg *e,
                       bool completed)
  {
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
        s32 err = 0;
  
        WL_TRACE("Enter\n");
  
 -      if (test_and_clear_bit(WL_STATUS_CONNECTING, &cfg->status)) {
 +      if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
 +                             &ifp->vif->sme_state)) {
                if (completed) {
                        brcmf_get_assoc_ies(cfg);
                        memcpy(profile->bssid, e->addr, ETH_ALEN);
                                                    WLAN_STATUS_AUTH_TIMEOUT,
                                        GFP_KERNEL);
                if (completed)
 -                      set_bit(WL_STATUS_CONNECTED, &cfg->status);
 +                      set_bit(BRCMF_VIF_STATUS_CONNECTED,
 +                              &ifp->vif->sme_state);
                WL_CONN("Report connect result - connection %s\n",
                                completed ? "succeeded" : "failed");
        }
@@@ -4562,8 -4736,7 +4562,8 @@@ brcmf_notify_connect_status(struct brcm
                            struct net_device *ndev,
                            const struct brcmf_event_msg *e, void *data)
  {
 -      struct brcmf_cfg80211_profile *profile = cfg->profile;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        s32 err = 0;
  
        if (cfg->conf->mode == WL_MODE_AP) {
                        memcpy(profile->bssid, e->addr, ETH_ALEN);
                        wl_inform_ibss(cfg, ndev, e->addr);
                        cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
 -                      clear_bit(WL_STATUS_CONNECTING, &cfg->status);
 -                      set_bit(WL_STATUS_CONNECTED, &cfg->status);
 +                      clear_bit(BRCMF_VIF_STATUS_CONNECTING,
 +                                &ifp->vif->sme_state);
 +                      set_bit(BRCMF_VIF_STATUS_CONNECTED,
 +                              &ifp->vif->sme_state);
                } else
                        brcmf_bss_connect_done(cfg, ndev, e, true);
        } else if (brcmf_is_linkdown(cfg, e)) {
                WL_CONN("Linkdown\n");
                if (brcmf_is_ibssmode(cfg)) {
 -                      clear_bit(WL_STATUS_CONNECTING, &cfg->status);
 -                      if (test_and_clear_bit(WL_STATUS_CONNECTED,
 -                              &cfg->status))
 +                      clear_bit(BRCMF_VIF_STATUS_CONNECTING,
 +                                &ifp->vif->sme_state);
 +                      if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
 +                                             &ifp->vif->sme_state))
                                brcmf_link_down(cfg);
                } else {
                        brcmf_bss_connect_done(cfg, ndev, e, false);
 -                      if (test_and_clear_bit(WL_STATUS_CONNECTED,
 -                              &cfg->status)) {
 +                      if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
 +                                             &ifp->vif->sme_state)) {
                                cfg80211_disconnected(ndev, 0, NULL, 0,
 -                                      GFP_KERNEL);
 +                                                    GFP_KERNEL);
                                brcmf_link_down(cfg);
                        }
                }
 -              brcmf_init_prof(cfg->profile);
 +              brcmf_init_prof(ndev_to_prof(ndev));
        } else if (brcmf_is_nonetwork(cfg, e)) {
                if (brcmf_is_ibssmode(cfg))
 -                      clear_bit(WL_STATUS_CONNECTING, &cfg->status);
 +                      clear_bit(BRCMF_VIF_STATUS_CONNECTING,
 +                                &ifp->vif->sme_state);
                else
                        brcmf_bss_connect_done(cfg, ndev, e, false);
        }
@@@ -4614,13 -4783,12 +4614,13 @@@ brcmf_notify_roaming_status(struct brcm
                            struct net_device *ndev,
                            const struct brcmf_event_msg *e, void *data)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
        u32 event = be32_to_cpu(e->event_type);
        u32 status = be32_to_cpu(e->status);
  
        if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
 -              if (test_bit(WL_STATUS_CONNECTED, &cfg->status))
 +              if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
                        brcmf_bss_roaming_done(cfg, ndev, e);
                else
                        brcmf_bss_connect_done(cfg, ndev, e, true);
@@@ -4653,7 -4821,6 +4653,7 @@@ brcmf_notify_scan_status(struct brcmf_c
                         struct net_device *ndev,
                         const struct brcmf_event_msg *e, void *data)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_channel_info_le channel_inform_le;
        struct brcmf_scan_results_le *bss_list_le;
        u32 len = WL_SCAN_BUF_MAX;
                return brcmf_wakeup_iscan(cfg_to_iscan(cfg));
        }
  
 -      if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
 +      if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
                WL_ERR("Scan complete while device not scanning\n");
                scan_abort = true;
                err = -EINVAL;
                goto scan_done_out;
        }
  
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_inform_le,
 -                            sizeof(channel_inform_le));
 +      err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CHANNEL,
 +                                   &channel_inform_le,
 +                                   sizeof(channel_inform_le));
        if (err) {
                WL_ERR("scan busy (%d)\n", err);
                scan_abort = true;
  
        memset(cfg->scan_results, 0, len);
        bss_list_le->buflen = cpu_to_le32(len);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN_RESULTS,
 -                            cfg->scan_results, len);
 +      err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_SCAN_RESULTS,
 +                                   cfg->scan_results, len);
        if (err) {
                WL_ERR("%s Scan_results error (%d)\n", ndev->name, err);
                err = -EINVAL;
@@@ -4754,6 -4920,8 +4754,6 @@@ static void brcmf_deinit_priv_mem(struc
        cfg->bss_info = NULL;
        kfree(cfg->conf);
        cfg->conf = NULL;
 -      kfree(cfg->profile);
 -      cfg->profile = NULL;
        kfree(cfg->scan_req_int);
        cfg->scan_req_int = NULL;
        kfree(cfg->escan_ioctl_buf);
@@@ -4782,6 -4950,9 +4782,6 @@@ static s32 brcmf_init_priv_mem(struct b
        cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
        if (!cfg->conf)
                goto init_priv_mem_out;
 -      cfg->profile = kzalloc(sizeof(*cfg->profile), GFP_KERNEL);
 -      if (!cfg->profile)
 -              goto init_priv_mem_out;
        cfg->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
        if (!cfg->bss_info)
                goto init_priv_mem_out;
@@@ -4958,6 -5129,7 +4958,6 @@@ static s32 wl_init_priv(struct brcmf_cf
                return err;
        brcmf_init_escan(cfg);
        brcmf_init_conf(cfg->conf);
 -      brcmf_init_prof(cfg->profile);
        brcmf_link_down(cfg);
  
        return err;
@@@ -4973,14 -5145,12 +4973,14 @@@ static void wl_deinit_priv(struct brcmf
        brcmf_deinit_priv_mem(cfg);
  }
  
 -struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct net_device *ndev,
 -                                                struct device *busdev,
 -                                                struct brcmf_pub *drvr)
 +struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr)
  {
 -      struct wireless_dev *wdev;
 +      struct net_device *ndev = drvr->iflist[0]->ndev;
 +      struct device *busdev = drvr->dev;
        struct brcmf_cfg80211_info *cfg;
 +      struct wiphy *wiphy;
 +      struct brcmf_cfg80211_vif *vif;
 +      struct brcmf_if *ifp;
        s32 err = 0;
  
        if (!ndev) {
                return NULL;
        }
  
 -      wdev = brcmf_alloc_wdev(busdev);
 -      if (IS_ERR(wdev)) {
 +      ifp = netdev_priv(ndev);
 +      wiphy = brcmf_setup_wiphy(busdev);
 +      if (IS_ERR(wiphy))
                return NULL;
 -      }
  
 -      wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS);
 -      cfg = wdev_to_cfg(wdev);
 -      cfg->wdev = wdev;
 +      cfg = wiphy_priv(wiphy);
 +      cfg->wiphy = wiphy;
        cfg->pub = drvr;
 -      ndev->ieee80211_ptr = wdev;
 -      SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
 -      wdev->netdev = ndev;
 +      INIT_LIST_HEAD(&cfg->vif_list);
 +
 +      vif = brcmf_alloc_vif(cfg, ndev, WL_MODE_BSS, false);
 +      if (IS_ERR(vif)) {
 +              wiphy_free(wiphy);
 +              return NULL;
 +      }
 +
        err = wl_init_priv(cfg);
        if (err) {
                WL_ERR("Failed to init iwm_priv (%d)\n", err);
                goto cfg80211_attach_out;
        }
  
 +      ifp->vif = vif;
        return cfg;
  
  cfg80211_attach_out:
 -      brcmf_free_wdev(cfg);
 +      brcmf_free_vif(vif);
        return NULL;
  }
  
  void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
  {
 +      struct brcmf_cfg80211_vif *vif;
 +      struct brcmf_cfg80211_vif *tmp;
 +
        wl_deinit_priv(cfg);
 -      brcmf_free_wdev(cfg);
 +      list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) {
 +              brcmf_free_vif(vif);
 +      }
  }
  
  void
@@@ -5042,18 -5202,22 +5042,18 @@@ brcmf_cfg80211_event(struct net_device 
  
  static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
  {
 -      /* Room for "event_msgs" + '\0' + bitvec */
 -      s8 iovbuf[BRCMF_EVENTING_MASK_LEN + 12];
        s8 eventmask[BRCMF_EVENTING_MASK_LEN];
        s32 err = 0;
  
        WL_TRACE("Enter\n");
  
        /* Setup event_msgs */
 -      brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
 -                      iovbuf, sizeof(iovbuf));
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, iovbuf, sizeof(iovbuf));
 +      err = brcmf_fil_iovar_data_get(netdev_priv(ndev), "event_msgs",
 +                                     eventmask, BRCMF_EVENTING_MASK_LEN);
        if (err) {
                WL_ERR("Get event_msgs error (%d)\n", err);
                goto dongle_eventmsg_out;
        }
 -      memcpy(eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN);
  
        setbit(eventmask, BRCMF_E_SET_SSID);
        setbit(eventmask, BRCMF_E_ROAM);
        setbit(eventmask, BRCMF_E_ESCAN_RESULT);
        setbit(eventmask, BRCMF_E_PFN_NET_FOUND);
  
 -      brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
 -                      iovbuf, sizeof(iovbuf));
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
 +      err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "event_msgs",
 +                                     eventmask, BRCMF_EVENTING_MASK_LEN);
        if (err) {
                WL_ERR("Set event_msgs error (%d)\n", err);
                goto dongle_eventmsg_out;
@@@ -5092,17 -5257,23 +5092,17 @@@ dongle_eventmsg_out
  static s32
  brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
  {
 -      s8 iovbuf[32];
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
        __le32 roamtrigger[2];
        __le32 roam_delta[2];
 -      __le32 bcn_to_le;
 -      __le32 roamvar_le;
  
        /*
         * Setup timeout if Beacons are lost and roam is
         * off to report link down
         */
        if (roamvar) {
 -              bcn_to_le = cpu_to_le32(bcn_timeout);
 -              brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_to_le,
 -                      sizeof(bcn_to_le), iovbuf, sizeof(iovbuf));
 -              err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR,
 -                                 iovbuf, sizeof(iovbuf));
 +              err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
                if (err) {
                        WL_ERR("bcn_timeout error (%d)\n", err);
                        goto dongle_rom_out;
         * to take care of roaming
         */
        WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
 -      roamvar_le = cpu_to_le32(roamvar);
 -      brcmf_c_mkiovar("roam_off", (char *)&roamvar_le,
 -                              sizeof(roamvar_le), iovbuf, sizeof(iovbuf));
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
 +      err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar);
        if (err) {
                WL_ERR("roam_off error (%d)\n", err);
                goto dongle_rom_out;
  
        roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
        roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_TRIGGER,
 -                      (void *)roamtrigger, sizeof(roamtrigger));
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
 +                                   (void *)roamtrigger, sizeof(roamtrigger));
        if (err) {
                WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
                goto dongle_rom_out;
  
        roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
        roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_DELTA,
 -                              (void *)roam_delta, sizeof(roam_delta));
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
 +                                   (void *)roam_delta, sizeof(roam_delta));
        if (err) {
                WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
                goto dongle_rom_out;
@@@ -5146,11 -5320,13 +5146,11 @@@ static s3
  brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
                      s32 scan_unassoc_time, s32 scan_passive_time)
  {
 +      struct brcmf_if *ifp = netdev_priv(ndev);
        s32 err = 0;
 -      __le32 scan_assoc_tm_le = cpu_to_le32(scan_assoc_time);
 -      __le32 scan_unassoc_tm_le = cpu_to_le32(scan_unassoc_time);
 -      __le32 scan_passive_tm_le = cpu_to_le32(scan_passive_time);
  
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_CHANNEL_TIME,
 -                         &scan_assoc_tm_le, sizeof(scan_assoc_tm_le));
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
 +                                  scan_assoc_time);
        if (err) {
                if (err == -EOPNOTSUPP)
                        WL_INFO("Scan assoc time is not supported\n");
                        WL_ERR("Scan assoc time error (%d)\n", err);
                goto dongle_scantime_out;
        }
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_UNASSOC_TIME,
 -                         &scan_unassoc_tm_le, sizeof(scan_unassoc_tm_le));
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
 +                                  scan_unassoc_time);
        if (err) {
                if (err == -EOPNOTSUPP)
                        WL_INFO("Scan unassoc time is not supported\n");
                goto dongle_scantime_out;
        }
  
 -      err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_PASSIVE_TIME,
 -                         &scan_passive_tm_le, sizeof(scan_passive_tm_le));
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
 +                                  scan_passive_time);
        if (err) {
                if (err == -EOPNOTSUPP)
                        WL_INFO("Scan passive time is not supported\n");
@@@ -5184,14 -5360,13 +5184,14 @@@ dongle_scantime_out
  
  static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
  {
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
        struct wiphy *wiphy;
        s32 phy_list;
        s8 phy;
        s32 err = 0;
  
 -      err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCM_GET_PHYLIST,
 -                            &phy_list, sizeof(phy_list));
 +      err = brcmf_fil_cmd_data_get(ifp, BRCM_GET_PHYLIST,
 +                                   &phy_list, sizeof(phy_list));
        if (err) {
                WL_ERR("error (%d)\n", err);
                return err;
@@@ -5233,8 -5408,7 +5233,8 @@@ static s32 brcmf_config_dongle(struct b
                goto default_conf_out;
  
        power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
 -      err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &power_mode);
 +      err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PM,
 +                                  power_mode);
        if (err)
                goto default_conf_out;
        WL_INFO("power save set to %s\n",
@@@ -5262,12 -5436,47 +5262,12 @@@ default_conf_out
  
  }
  
 -static int brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_info *cfg)
 -{
 -      char buf[10+IFNAMSIZ];
 -      struct dentry *fd;
 -      s32 err = 0;
 -
 -      sprintf(buf, "netdev:%s", cfg_to_ndev(cfg)->name);
 -      cfg->debugfsdir = debugfs_create_dir(buf,
 -                                      cfg_to_wiphy(cfg)->debugfsdir);
 -
 -      fd = debugfs_create_u16("beacon_int", S_IRUGO, cfg->debugfsdir,
 -              (u16 *)&cfg->profile->beacon_interval);
 -      if (!fd) {
 -              err = -ENOMEM;
 -              goto err_out;
 -      }
 -
 -      fd = debugfs_create_u8("dtim_period", S_IRUGO, cfg->debugfsdir,
 -              (u8 *)&cfg->profile->dtim_period);
 -      if (!fd) {
 -              err = -ENOMEM;
 -              goto err_out;
 -      }
 -
 -err_out:
 -      return err;
 -}
 -
 -static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_info *cfg)
 -{
 -      debugfs_remove_recursive(cfg->debugfsdir);
 -      cfg->debugfsdir = NULL;
 -}
 -
  static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
  {
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
        s32 err = 0;
  
 -      set_bit(WL_STATUS_READY, &cfg->status);
 -
 -      brcmf_debugfs_add_netdev_params(cfg);
 +      set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
  
        err = brcmf_config_dongle(cfg);
        if (err)
  
  static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
  {
 +      struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +
        /*
         * While going down, if associated with AP disassociate
         * from AP to save power
         */
 -      if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
 -           test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
 -           test_bit(WL_STATUS_READY, &cfg->status)) {
 +      if ((test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state) ||
 +           test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) &&
 +           check_vif_up(ifp->vif)) {
                WL_INFO("Disassociating from AP");
                brcmf_link_down(cfg);
  
        }
  
        brcmf_abort_scanning(cfg);
 -      clear_bit(WL_STATUS_READY, &cfg->status);
 -
 -      brcmf_debugfs_remove_netdev(cfg);
 +      clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
  
        return 0;
  }
index 475df45c8320b370010b5f70d4af0c3280ef102d,408132cf83c18285d4eff4ba7a67add5fde64bda..30e761d31e9827d6c4720d273b1fa6b8a2ab8fc8
@@@ -1191,6 -1191,8 +1191,6 @@@ static void iwl_option_config(struct iw
  
  static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
  {
 -      u16 radio_cfg;
 -
        priv->eeprom_data->sku = priv->eeprom_data->sku;
  
        if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE &&
  
        IWL_INFO(priv, "Device SKU: 0x%X\n", priv->eeprom_data->sku);
  
 -      radio_cfg = priv->eeprom_data->radio_cfg;
 -
        priv->hw_params.tx_chains_num =
                num_of_ant(priv->eeprom_data->valid_tx_ant);
        if (priv->cfg->rx_with_siso_diversity)
@@@ -1330,9 -1334,6 +1330,9 @@@ static struct iwl_op_mode *iwl_op_mode_
        /* Configure transport layer */
        iwl_trans_configure(priv->trans, &trans_cfg);
  
 +      trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
 +      trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
 +
        /* At this point both hw and priv are allocated. */
  
        SET_IEEE80211_DEV(priv->hw, priv->trans->dev);
@@@ -2113,7 -2114,7 +2113,7 @@@ static void iwl_free_skb(struct iwl_op_
  
        info = IEEE80211_SKB_CB(skb);
        iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]);
-       dev_kfree_skb_any(skb);
+       ieee80211_free_txskb(priv->hw, skb);
  }
  
  static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
@@@ -2151,6 -2152,8 +2151,6 @@@ static int __init iwl_init(void
  {
  
        int ret;
 -      pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n");
 -      pr_info(DRV_COPYRIGHT "\n");
  
        ret = iwlagn_rate_control_register();
        if (ret) {
index 137af4c46a6cf7c2c5d055475b6dbdaa17157d40,bb69f8f90b3b47c441fde750c14d0a9b9a3a6da0..41d821fbdb9216e8d4ab9ab2ef3c287a2926cfaa
@@@ -321,6 -321,14 +321,14 @@@ static void iwl_rx_allocate(struct iwl_
                        dma_map_page(trans->dev, page, 0,
                                     PAGE_SIZE << trans_pcie->rx_page_order,
                                     DMA_FROM_DEVICE);
+               if (dma_mapping_error(trans->dev, rxb->page_dma)) {
+                       rxb->page = NULL;
+                       spin_lock_irqsave(&rxq->lock, flags);
+                       list_add(&rxb->list, &rxq->rx_used);
+                       spin_unlock_irqrestore(&rxq->lock, flags);
+                       __free_pages(page, trans_pcie->rx_page_order);
+                       return;
+               }
                /* dma address must be no more than 36 bits */
                BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
                /* and also 256 byte aligned! */
@@@ -411,8 -419,7 +419,8 @@@ static void iwl_rx_handle_rxbuf(struct 
  
                len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
                len += sizeof(u32); /* account for status word */
 -              trace_iwlwifi_dev_rx(trans->dev, pkt, len);
 +              trace_iwlwifi_dev_rx(trans->dev, trans, pkt, len);
 +              trace_iwlwifi_dev_rx_data(trans->dev, trans, pkt, len);
  
                /* Reclaim a command buffer only if this packet is a response
                 *   to a (driver-originated) command.
                        dma_map_page(trans->dev, rxb->page, 0,
                                     PAGE_SIZE << trans_pcie->rx_page_order,
                                     DMA_FROM_DEVICE);
-               list_add_tail(&rxb->list, &rxq->rx_free);
-               rxq->free_count++;
+               if (dma_mapping_error(trans->dev, rxb->page_dma)) {
+                       /*
+                        * free the page(s) as well to not break
+                        * the invariant that the items on the used
+                        * list have no page(s)
+                        */
+                       __free_pages(rxb->page, trans_pcie->rx_page_order);
+                       rxb->page = NULL;
+                       list_add_tail(&rxb->list, &rxq->rx_used);
+               } else {
+                       list_add_tail(&rxb->list, &rxq->rx_free);
+                       rxq->free_count++;
+               }
        } else
                list_add_tail(&rxb->list, &rxq->rx_used);
        spin_unlock_irqrestore(&rxq->lock, flags);
index 2d1f89517d99a7034f4237a674ee65cea354b020,ce0684a1fc836f6713ce7cf1e1164e39b4cf0fd2..54800c783f96d29e0c9310ac4a7038fa9932ceef
@@@ -20,7 -20,6 +20,7 @@@
  #include "main.h"
  #include "soft-interface.h"
  #include "hard-interface.h"
 +#include "distributed-arp-table.h"
  #include "routing.h"
  #include "send.h"
  #include "debugfs.h"
@@@ -147,16 -146,13 +147,16 @@@ static int batadv_interface_tx(struct s
        struct batadv_bcast_packet *bcast_packet;
        struct vlan_ethhdr *vhdr;
        __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN);
 -      static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00,
 -                                                 0x00};
 +      static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00,
 +                                                 0x00, 0x00};
 +      static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00,
 +                                                  0x00, 0x00};
        unsigned int header_len = 0;
        int data_len = skb->len, ret;
        short vid __maybe_unused = -1;
        bool do_bcast = false;
        uint32_t seqno;
 +      unsigned long brd_delay = 1;
  
        if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
                goto dropped;
  
        /* don't accept stp packets. STP does not help in meshes.
         * better use the bridge loop avoidance ...
 +       *
 +       * The same goes for ECTP sent at least by some Cisco Switches,
 +       * it might confuse the mesh when used with bridge loop avoidance.
         */
        if (batadv_compare_eth(ethhdr->h_dest, stp_addr))
                goto dropped;
  
 +      if (batadv_compare_eth(ethhdr->h_dest, ectp_addr))
 +              goto dropped;
 +
        if (is_multicast_ether_addr(ethhdr->h_dest)) {
                do_bcast = true;
  
                if (!primary_if)
                        goto dropped;
  
 +              /* in case of ARP request, we do not immediately broadcasti the
 +               * packet, instead we first wait for DAT to try to retrieve the
 +               * correct ARP entry
 +               */
 +              if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb))
 +                      brd_delay = msecs_to_jiffies(ARP_REQ_DELAY);
 +
                if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0)
                        goto dropped;
  
                seqno = atomic_inc_return(&bat_priv->bcast_seqno);
                bcast_packet->seqno = htonl(seqno);
  
 -              batadv_add_bcast_packet_to_list(bat_priv, skb, 1);
 +              batadv_add_bcast_packet_to_list(bat_priv, skb, brd_delay);
  
                /* a copy is stored in the bcast list, therefore removing
                 * the original skb.
                                goto dropped;
                }
  
 -              ret = batadv_unicast_send_skb(skb, bat_priv);
 +              if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb))
 +                      goto dropped;
 +
 +              batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb);
 +
 +              ret = batadv_unicast_send_skb(bat_priv, skb);
                if (ret != 0)
                        goto dropped_freed;
        }
@@@ -347,6 -325,12 +347,12 @@@ void batadv_interface_rx(struct net_dev
  
        soft_iface->last_rx = jiffies;
  
+       /* Let the bridge loop avoidance check the packet. If will
+        * not handle it, we can safely push it up.
+        */
+       if (batadv_bla_rx(bat_priv, skb, vid, is_bcast))
+               goto out;
        if (orig_node)
                batadv_tt_add_temporary_global_entry(bat_priv, orig_node,
                                                     ethhdr->h_source);
        if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest))
                goto dropped;
  
-       /* Let the bridge loop avoidance check the packet. If will
-        * not handle it, we can safely push it up.
-        */
-       if (batadv_bla_rx(bat_priv, skb, vid, is_bcast))
-               goto out;
        netif_rx(skb);
        goto out;
  
@@@ -369,51 -347,7 +369,51 @@@ out
        return;
  }
  
 +/* batman-adv network devices have devices nesting below it and are a special
 + * "super class" of normal network devices; split their locks off into a
 + * separate class since they always nest.
 + */
 +static struct lock_class_key batadv_netdev_xmit_lock_key;
 +static struct lock_class_key batadv_netdev_addr_lock_key;
 +
 +/**
 + * batadv_set_lockdep_class_one - Set lockdep class for a single tx queue
 + * @dev: device which owns the tx queue
 + * @txq: tx queue to modify
 + * @_unused: always NULL
 + */
 +static void batadv_set_lockdep_class_one(struct net_device *dev,
 +                                       struct netdev_queue *txq,
 +                                       void *_unused)
 +{
 +      lockdep_set_class(&txq->_xmit_lock, &batadv_netdev_xmit_lock_key);
 +}
 +
 +/**
 + * batadv_set_lockdep_class - Set txq and addr_list lockdep class
 + * @dev: network device to modify
 + */
 +static void batadv_set_lockdep_class(struct net_device *dev)
 +{
 +      lockdep_set_class(&dev->addr_list_lock, &batadv_netdev_addr_lock_key);
 +      netdev_for_each_tx_queue(dev, batadv_set_lockdep_class_one, NULL);
 +}
 +
 +/**
 + * batadv_softif_init - Late stage initialization of soft interface
 + * @dev: registered network device to modify
 + *
 + * Returns error code on failures
 + */
 +static int batadv_softif_init(struct net_device *dev)
 +{
 +      batadv_set_lockdep_class(dev);
 +
 +      return 0;
 +}
 +
  static const struct net_device_ops batadv_netdev_ops = {
 +      .ndo_init = batadv_softif_init,
        .ndo_open = batadv_interface_open,
        .ndo_stop = batadv_interface_release,
        .ndo_get_stats = batadv_interface_stats,
@@@ -480,9 -414,6 +480,9 @@@ struct net_device *batadv_softif_create
        atomic_set(&bat_priv->aggregated_ogms, 1);
        atomic_set(&bat_priv->bonding, 0);
        atomic_set(&bat_priv->bridge_loop_avoidance, 0);
 +#ifdef CONFIG_BATMAN_ADV_DAT
 +      atomic_set(&bat_priv->distributed_arp_table, 1);
 +#endif
        atomic_set(&bat_priv->ap_isolation, 0);
        atomic_set(&bat_priv->vis_mode, BATADV_VIS_TYPE_CLIENT_UPDATE);
        atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);
  #endif
        bat_priv->tt.last_changeset = NULL;
        bat_priv->tt.last_changeset_len = 0;
 -      bat_priv->tt.poss_change = false;
  
        bat_priv->primary_if = NULL;
        bat_priv->num_ifaces = 0;
@@@ -624,13 -556,6 +624,13 @@@ static const struct 
        { "tt_response_rx" },
        { "tt_roam_adv_tx" },
        { "tt_roam_adv_rx" },
 +#ifdef CONFIG_BATMAN_ADV_DAT
 +      { "dat_get_tx" },
 +      { "dat_get_rx" },
 +      { "dat_put_tx" },
 +      { "dat_put_rx" },
 +      { "dat_cached_reply_tx" },
 +#endif
  };
  
  static void batadv_get_strings(struct net_device *dev, uint32_t stringset,
index 9f5705fcf4268c5796a76c8b1302e3a6ae37c915,baae71585804313ff406aad0ab7f04b0b7daa189..582f13405df9286bc752b633ed61a3b9244d545f
@@@ -238,134 -238,92 +238,134 @@@ static int batadv_tt_local_init(struct 
        return 0;
  }
  
 +static void batadv_tt_global_free(struct batadv_priv *bat_priv,
 +                                struct batadv_tt_global_entry *tt_global,
 +                                const char *message)
 +{
 +      batadv_dbg(BATADV_DBG_TT, bat_priv,
 +                 "Deleting global tt entry %pM: %s\n",
 +                 tt_global->common.addr, message);
 +
 +      batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt,
 +                         batadv_choose_orig, tt_global->common.addr);
 +      batadv_tt_global_entry_free_ref(tt_global);
 +
 +}
 +
  void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
                         int ifindex)
  {
        struct batadv_priv *bat_priv = netdev_priv(soft_iface);
 -      struct batadv_tt_local_entry *tt_local_entry = NULL;
 -      struct batadv_tt_global_entry *tt_global_entry = NULL;
 +      struct batadv_tt_local_entry *tt_local;
 +      struct batadv_tt_global_entry *tt_global;
        struct hlist_head *head;
        struct hlist_node *node;
        struct batadv_tt_orig_list_entry *orig_entry;
        int hash_added;
 +      bool roamed_back = false;
  
 -      tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
 +      tt_local = batadv_tt_local_hash_find(bat_priv, addr);
 +      tt_global = batadv_tt_global_hash_find(bat_priv, addr);
  
 -      if (tt_local_entry) {
 -              tt_local_entry->last_seen = jiffies;
 -              /* possibly unset the BATADV_TT_CLIENT_PENDING flag */
 -              tt_local_entry->common.flags &= ~BATADV_TT_CLIENT_PENDING;
 -              goto out;
 +      if (tt_local) {
 +              tt_local->last_seen = jiffies;
 +              if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) {
 +                      batadv_dbg(BATADV_DBG_TT, bat_priv,
 +                                 "Re-adding pending client %pM\n", addr);
 +                      /* whatever the reason why the PENDING flag was set,
 +                       * this is a client which was enqueued to be removed in
 +                       * this orig_interval. Since it popped up again, the
 +                       * flag can be reset like it was never enqueued
 +                       */
 +                      tt_local->common.flags &= ~BATADV_TT_CLIENT_PENDING;
 +                      goto add_event;
 +              }
 +
 +              if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) {
 +                      batadv_dbg(BATADV_DBG_TT, bat_priv,
 +                                 "Roaming client %pM came back to its original location\n",
 +                                 addr);
 +                      /* the ROAM flag is set because this client roamed away
 +                       * and the node got a roaming_advertisement message. Now
 +                       * that the client popped up again at its original
 +                       * location such flag can be unset
 +                       */
 +                      tt_local->common.flags &= ~BATADV_TT_CLIENT_ROAM;
 +                      roamed_back = true;
 +              }
 +              goto check_roaming;
        }
  
 -      tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC);
 -      if (!tt_local_entry)
 +      tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC);
 +      if (!tt_local)
                goto out;
  
        batadv_dbg(BATADV_DBG_TT, bat_priv,
                   "Creating new local tt entry: %pM (ttvn: %d)\n", addr,
                   (uint8_t)atomic_read(&bat_priv->tt.vn));
  
 -      memcpy(tt_local_entry->common.addr, addr, ETH_ALEN);
 -      tt_local_entry->common.flags = BATADV_NO_FLAGS;
 +      memcpy(tt_local->common.addr, addr, ETH_ALEN);
 +      tt_local->common.flags = BATADV_NO_FLAGS;
        if (batadv_is_wifi_iface(ifindex))
 -              tt_local_entry->common.flags |= BATADV_TT_CLIENT_WIFI;
 -      atomic_set(&tt_local_entry->common.refcount, 2);
 -      tt_local_entry->last_seen = jiffies;
 -      tt_local_entry->common.added_at = tt_local_entry->last_seen;
 +              tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
 +      atomic_set(&tt_local->common.refcount, 2);
 +      tt_local->last_seen = jiffies;
 +      tt_local->common.added_at = tt_local->last_seen;
  
        /* the batman interface mac address should never be purged */
        if (batadv_compare_eth(addr, soft_iface->dev_addr))
 -              tt_local_entry->common.flags |= BATADV_TT_CLIENT_NOPURGE;
 +              tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
  
        /* The local entry has to be marked as NEW to avoid to send it in
         * a full table response going out before the next ttvn increment
         * (consistency check)
         */
 -      tt_local_entry->common.flags |= BATADV_TT_CLIENT_NEW;
 +      tt_local->common.flags |= BATADV_TT_CLIENT_NEW;
  
        hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
 -                                   batadv_choose_orig,
 -                                   &tt_local_entry->common,
 -                                   &tt_local_entry->common.hash_entry);
 +                                   batadv_choose_orig, &tt_local->common,
 +                                   &tt_local->common.hash_entry);
  
        if (unlikely(hash_added != 0)) {
                /* remove the reference for the hash */
 -              batadv_tt_local_entry_free_ref(tt_local_entry);
 +              batadv_tt_local_entry_free_ref(tt_local);
                goto out;
        }
  
 -      batadv_tt_local_event(bat_priv, addr, tt_local_entry->common.flags);
 +add_event:
 +      batadv_tt_local_event(bat_priv, addr, tt_local->common.flags);
  
 -      /* remove address from global hash if present */
 -      tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
 -
 -      /* Check whether it is a roaming! */
 -      if (tt_global_entry) {
 +check_roaming:
 +      /* Check whether it is a roaming, but don't do anything if the roaming
 +       * process has already been handled
 +       */
 +      if (tt_global && !(tt_global->common.flags & BATADV_TT_CLIENT_ROAM)) {
                /* These node are probably going to update their tt table */
 -              head = &tt_global_entry->orig_list;
 +              head = &tt_global->orig_list;
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_entry, node, head, list) {
 -                      orig_entry->orig_node->tt_poss_change = true;
 -
 -                      batadv_send_roam_adv(bat_priv,
 -                                           tt_global_entry->common.addr,
 +                      batadv_send_roam_adv(bat_priv, tt_global->common.addr,
                                             orig_entry->orig_node);
                }
                rcu_read_unlock();
 -              /* The global entry has to be marked as ROAMING and
 -               * has to be kept for consistency purpose
 -               */
 -              tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
 -              tt_global_entry->roam_at = jiffies;
 +              if (roamed_back) {
 +                      batadv_tt_global_free(bat_priv, tt_global,
 +                                            "Roaming canceled");
 +                      tt_global = NULL;
 +              } else {
 +                      /* The global entry has to be marked as ROAMING and
 +                       * has to be kept for consistency purpose
 +                       */
 +                      tt_global->common.flags |= BATADV_TT_CLIENT_ROAM;
 +                      tt_global->roam_at = jiffies;
 +              }
        }
 +
  out:
 -      if (tt_local_entry)
 -              batadv_tt_local_entry_free_ref(tt_local_entry);
 -      if (tt_global_entry)
 -              batadv_tt_global_entry_free_ref(tt_global_entry);
 +      if (tt_local)
 +              batadv_tt_local_entry_free_ref(tt_local);
 +      if (tt_global)
 +              batadv_tt_global_entry_free_ref(tt_global);
  }
  
  static void batadv_tt_realloc_packet_buff(unsigned char **packet_buff,
@@@ -476,10 -434,22 +476,10 @@@ int batadv_tt_local_seq_print_text(stru
        struct hlist_node *node;
        struct hlist_head *head;
        uint32_t i;
 -      int ret = 0;
  
 -      primary_if = batadv_primary_if_get_selected(bat_priv);
 -      if (!primary_if) {
 -              ret = seq_printf(seq,
 -                               "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
 -                               net_dev->name);
 -              goto out;
 -      }
 -
 -      if (primary_if->if_status != BATADV_IF_ACTIVE) {
 -              ret = seq_printf(seq,
 -                               "BATMAN mesh %s disabled - primary interface not active\n",
 -                               net_dev->name);
 +      primary_if = batadv_seq_print_text_primary_if_get(seq);
 +      if (!primary_if)
                goto out;
 -      }
  
        seq_printf(seq,
                   "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n",
  out:
        if (primary_if)
                batadv_hardif_free_ref(primary_if);
 -      return ret;
 +      return 0;
  }
  
  static void
@@@ -531,57 -501,24 +531,57 @@@ batadv_tt_local_set_pending(struct bata
                   tt_local_entry->common.addr, message);
  }
  
 -void batadv_tt_local_remove(struct batadv_priv *bat_priv, const uint8_t *addr,
 -                          const char *message, bool roaming)
 +/**
 + * batadv_tt_local_remove - logically remove an entry from the local table
 + * @bat_priv: the bat priv with all the soft interface information
 + * @addr: the MAC address of the client to remove
 + * @message: message to append to the log on deletion
 + * @roaming: true if the deletion is due to a roaming event
 + *
 + * Returns the flags assigned to the local entry before being deleted
 + */
 +uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
 +                              const uint8_t *addr, const char *message,
 +                              bool roaming)
  {
 -      struct batadv_tt_local_entry *tt_local_entry = NULL;
 -      uint16_t flags;
 +      struct batadv_tt_local_entry *tt_local_entry;
 +      uint16_t flags, curr_flags = BATADV_NO_FLAGS;
  
        tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
        if (!tt_local_entry)
                goto out;
  
 +      curr_flags = tt_local_entry->common.flags;
 +
        flags = BATADV_TT_CLIENT_DEL;
 -      if (roaming)
 +      /* if this global entry addition is due to a roaming, the node has to
 +       * mark the local entry as "roamed" in order to correctly reroute
 +       * packets later
 +       */
 +      if (roaming) {
                flags |= BATADV_TT_CLIENT_ROAM;
 +              /* mark the local client as ROAMed */
 +              tt_local_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
 +      }
 +
 +      if (!(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) {
 +              batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,
 +                                          message);
 +              goto out;
 +      }
 +      /* if this client has been added right now, it is possible to
 +       * immediately purge it
 +       */
 +      batadv_tt_local_event(bat_priv, tt_local_entry->common.addr,
 +                            curr_flags | BATADV_TT_CLIENT_DEL);
 +      hlist_del_rcu(&tt_local_entry->common.hash_entry);
 +      batadv_tt_local_entry_free_ref(tt_local_entry);
  
 -      batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags, message);
  out:
        if (tt_local_entry)
                batadv_tt_local_entry_free_ref(tt_local_entry);
 +
 +      return curr_flags;
  }
  
  static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
@@@ -784,23 -721,12 +784,23 @@@ int batadv_tt_global_add(struct batadv_
                         const unsigned char *tt_addr, uint8_t flags,
                         uint8_t ttvn)
  {
 -      struct batadv_tt_global_entry *tt_global_entry = NULL;
 +      struct batadv_tt_global_entry *tt_global_entry;
 +      struct batadv_tt_local_entry *tt_local_entry;
        int ret = 0;
        int hash_added;
        struct batadv_tt_common_entry *common;
 +      uint16_t local_flags;
  
        tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr);
 +      tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr);
 +
 +      /* if the node already has a local client for this entry, it has to wait
 +       * for a roaming advertisement instead of manually messing up the global
 +       * table
 +       */
 +      if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry &&
 +          !(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW))
 +              goto out;
  
        if (!tt_global_entry) {
                tt_global_entry = kzalloc(sizeof(*tt_global_entry), GFP_ATOMIC);
  
                common->flags = flags;
                tt_global_entry->roam_at = 0;
 +              /* node must store current time in case of roaming. This is
 +               * needed to purge this entry out on timeout (if nobody claims
 +               * it)
 +               */
 +              if (flags & BATADV_TT_CLIENT_ROAM)
 +                      tt_global_entry->roam_at = jiffies;
                atomic_set(&common->refcount, 2);
                common->added_at = jiffies;
  
                        goto out_remove;
                }
        } else {
 +              common = &tt_global_entry->common;
                /* If there is already a global entry, we can use this one for
                 * our processing.
 -               * But if we are trying to add a temporary client we can exit
 -               * directly because the temporary information should never
 -               * override any already known client state (whatever it is)
 +               * But if we are trying to add a temporary client then here are
 +               * two options at this point:
 +               * 1) the global client is not a temporary client: the global
 +               *    client has to be left as it is, temporary information
 +               *    should never override any already known client state
 +               * 2) the global client is a temporary client: purge the
 +               *    originator list and add the new one orig_entry
                 */
 -              if (flags & BATADV_TT_CLIENT_TEMP)
 -                      goto out;
 +              if (flags & BATADV_TT_CLIENT_TEMP) {
 +                      if (!(common->flags & BATADV_TT_CLIENT_TEMP))
 +                              goto out;
 +                      if (batadv_tt_global_entry_has_orig(tt_global_entry,
 +                                                          orig_node))
 +                              goto out_remove;
 +                      batadv_tt_global_del_orig_list(tt_global_entry);
 +                      goto add_orig_entry;
 +              }
  
                /* if the client was temporary added before receiving the first
                 * OGM announcing it, we have to clear the TEMP flag
                 */
 -              tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_TEMP;
 +              common->flags &= ~BATADV_TT_CLIENT_TEMP;
  
+               /* the change can carry possible "attribute" flags like the
+                * TT_CLIENT_WIFI, therefore they have to be copied in the
+                * client entry
+                */
+               tt_global_entry->common.flags |= flags;
                /* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only
                 * one originator left in the list and we previously received a
                 * delete + roaming change for this originator.
                 * We should first delete the old originator before adding the
                 * new one.
                 */
 -              if (tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM) {
 +              if (common->flags & BATADV_TT_CLIENT_ROAM) {
                        batadv_tt_global_del_orig_list(tt_global_entry);
 -                      tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM;
 +                      common->flags &= ~BATADV_TT_CLIENT_ROAM;
                        tt_global_entry->roam_at = 0;
                }
        }
 +add_orig_entry:
        /* add the new orig_entry (if needed) or update it */
        batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn);
  
        batadv_dbg(BATADV_DBG_TT, bat_priv,
                   "Creating new global tt entry: %pM (via %pM)\n",
 -                 tt_global_entry->common.addr, orig_node->orig);
 +                 common->addr, orig_node->orig);
 +      ret = 1;
  
  out_remove:
 +
        /* remove address from local hash if present */
 -      batadv_tt_local_remove(bat_priv, tt_global_entry->common.addr,
 -                             "global tt received",
 -                             flags & BATADV_TT_CLIENT_ROAM);
 -      ret = 1;
 +      local_flags = batadv_tt_local_remove(bat_priv, tt_addr,
 +                                           "global tt received",
 +                                           !!(flags & BATADV_TT_CLIENT_ROAM));
 +      tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI;
 +
 +      if (!(flags & BATADV_TT_CLIENT_ROAM))
 +              /* this is a normal global add. Therefore the client is not in a
 +               * roaming state anymore.
 +               */
 +              tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM;
 +
  out:
        if (tt_global_entry)
                batadv_tt_global_entry_free_ref(tt_global_entry);
 +      if (tt_local_entry)
 +              batadv_tt_local_entry_free_ref(tt_local_entry);
        return ret;
  }
  
@@@ -946,10 -848,22 +952,10 @@@ int batadv_tt_global_seq_print_text(str
        struct hlist_node *node;
        struct hlist_head *head;
        uint32_t i;
 -      int ret = 0;
  
 -      primary_if = batadv_primary_if_get_selected(bat_priv);
 -      if (!primary_if) {
 -              ret = seq_printf(seq,
 -                               "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
 -                               net_dev->name);
 +      primary_if = batadv_seq_print_text_primary_if_get(seq);
 +      if (!primary_if)
                goto out;
 -      }
 -
 -      if (primary_if->if_status != BATADV_IF_ACTIVE) {
 -              ret = seq_printf(seq,
 -                               "BATMAN mesh %s disabled - primary interface not active\n",
 -                               net_dev->name);
 -              goto out;
 -      }
  
        seq_printf(seq,
                   "Globally announced TT entries received via the mesh %s\n",
  out:
        if (primary_if)
                batadv_hardif_free_ref(primary_if);
 -      return ret;
 +      return 0;
  }
  
  /* deletes the orig list of a tt_global_entry */
@@@ -1019,6 -933,21 +1025,6 @@@ batadv_tt_global_del_orig_entry(struct 
        spin_unlock_bh(&tt_global_entry->list_lock);
  }
  
 -static void
 -batadv_tt_global_del_struct(struct batadv_priv *bat_priv,
 -                          struct batadv_tt_global_entry *tt_global_entry,
 -                          const char *message)
 -{
 -      batadv_dbg(BATADV_DBG_TT, bat_priv,
 -                 "Deleting global tt entry %pM: %s\n",
 -                 tt_global_entry->common.addr, message);
 -
 -      batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt,
 -                         batadv_choose_orig, tt_global_entry->common.addr);
 -      batadv_tt_global_entry_free_ref(tt_global_entry);
 -
 -}
 -
  /* If the client is to be deleted, we check if it is the last origantor entry
   * within tt_global entry. If yes, we set the BATADV_TT_CLIENT_ROAM flag and the
   * timer, otherwise we simply remove the originator scheduled for deletion.
@@@ -1067,7 -996,7 +1073,7 @@@ static void batadv_tt_global_del(struc
                                 const unsigned char *addr,
                                 const char *message, bool roaming)
  {
 -      struct batadv_tt_global_entry *tt_global_entry = NULL;
 +      struct batadv_tt_global_entry *tt_global_entry;
        struct batadv_tt_local_entry *local_entry = NULL;
  
        tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
                                                orig_node, message);
  
                if (hlist_empty(&tt_global_entry->orig_list))
 -                      batadv_tt_global_del_struct(bat_priv, tt_global_entry,
 -                                                  message);
 +                      batadv_tt_global_free(bat_priv, tt_global_entry,
 +                                            message);
  
                goto out;
        }
        if (local_entry) {
                /* local entry exists, case 2: client roamed to us. */
                batadv_tt_global_del_orig_list(tt_global_entry);
 -              batadv_tt_global_del_struct(bat_priv, tt_global_entry, message);
 +              batadv_tt_global_free(bat_priv, tt_global_entry, message);
        } else
                /* no local entry exists, case 1: check for roaming */
                batadv_tt_global_del_roaming(bat_priv, tt_global_entry,
@@@ -1282,8 -1211,7 +1288,8 @@@ struct batadv_orig_node *batadv_transta
  
        if (src && atomic_read(&bat_priv->ap_isolation)) {
                tt_local_entry = batadv_tt_local_hash_find(bat_priv, src);
 -              if (!tt_local_entry)
 +              if (!tt_local_entry ||
 +                  (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING))
                        goto out;
        }
  
@@@ -1549,11 -1477,11 +1555,11 @@@ batadv_tt_response_fill_table(uint16_t 
        tt_tot = tt_len / sizeof(struct batadv_tt_change);
  
        len = tt_query_size + tt_len;
 -      skb = dev_alloc_skb(len + ETH_HLEN);
 +      skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
        if (!skb)
                goto out;
  
 -      skb_reserve(skb, ETH_HLEN);
 +      skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
        tt_response = (struct batadv_tt_query_packet *)skb_put(skb, len);
        tt_response->ttvn = ttvn;
  
  
                        memcpy(tt_change->addr, tt_common_entry->addr,
                               ETH_ALEN);
-                       tt_change->flags = BATADV_NO_FLAGS;
+                       tt_change->flags = tt_common_entry->flags;
  
                        tt_count++;
                        tt_change++;
@@@ -1615,11 -1543,11 +1621,11 @@@ static int batadv_send_tt_request(struc
        if (!tt_req_node)
                goto out;
  
 -      skb = dev_alloc_skb(sizeof(*tt_request) + ETH_HLEN);
 +      skb = dev_alloc_skb(sizeof(*tt_request) + ETH_HLEN + NET_IP_ALIGN);
        if (!skb)
                goto out;
  
 -      skb_reserve(skb, ETH_HLEN);
 +      skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
  
        tt_req_len = sizeof(*tt_request);
        tt_request = (struct batadv_tt_query_packet *)skb_put(skb, tt_req_len);
@@@ -1670,7 -1598,7 +1676,7 @@@ static boo
  batadv_send_other_tt_response(struct batadv_priv *bat_priv,
                              struct batadv_tt_query_packet *tt_request)
  {
 -      struct batadv_orig_node *req_dst_orig_node = NULL;
 +      struct batadv_orig_node *req_dst_orig_node;
        struct batadv_orig_node *res_dst_orig_node = NULL;
        struct batadv_neigh_node *neigh_node = NULL;
        struct batadv_hard_iface *primary_if = NULL;
                tt_tot = tt_len / sizeof(struct batadv_tt_change);
  
                len = sizeof(*tt_response) + tt_len;
 -              skb = dev_alloc_skb(len + ETH_HLEN);
 +              skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
                if (!skb)
                        goto unlock;
  
 -              skb_reserve(skb, ETH_HLEN);
 +              skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
                packet_pos = skb_put(skb, len);
                tt_response = (struct batadv_tt_query_packet *)packet_pos;
                tt_response->ttvn = req_ttvn;
@@@ -1805,7 -1733,7 +1811,7 @@@ static boo
  batadv_send_my_tt_response(struct batadv_priv *bat_priv,
                           struct batadv_tt_query_packet *tt_request)
  {
 -      struct batadv_orig_node *orig_node = NULL;
 +      struct batadv_orig_node *orig_node;
        struct batadv_neigh_node *neigh_node = NULL;
        struct batadv_hard_iface *primary_if = NULL;
        uint8_t my_ttvn, req_ttvn, ttvn;
                tt_tot = tt_len / sizeof(struct batadv_tt_change);
  
                len = sizeof(*tt_response) + tt_len;
 -              skb = dev_alloc_skb(len + ETH_HLEN);
 +              skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
                if (!skb)
                        goto unlock;
  
 -              skb_reserve(skb, ETH_HLEN);
 +              skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
                packet_pos = skb_put(skb, len);
                tt_response = (struct batadv_tt_query_packet *)packet_pos;
                tt_response->ttvn = req_ttvn;
@@@ -1971,7 -1899,7 +1977,7 @@@ static void _batadv_tt_update_changes(s
  static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
                                  struct batadv_tt_query_packet *tt_response)
  {
 -      struct batadv_orig_node *orig_node = NULL;
 +      struct batadv_orig_node *orig_node;
  
        orig_node = batadv_orig_hash_find(bat_priv, tt_response->src);
        if (!orig_node)
@@@ -2013,7 -1941,7 +2019,7 @@@ static void batadv_tt_update_changes(st
  
  bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr)
  {
 -      struct batadv_tt_local_entry *tt_local_entry = NULL;
 +      struct batadv_tt_local_entry *tt_local_entry;
        bool ret = false;
  
        tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
        /* Check if the client has been logically deleted (but is kept for
         * consistency purpose)
         */
 -      if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
 +      if ((tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) ||
 +          (tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM))
                goto out;
        ret = true;
  out:
@@@ -2074,6 -2001,10 +2080,6 @@@ void batadv_handle_tt_response(struct b
  
        /* Recalculate the CRC for this orig_node and store it */
        orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
 -      /* Roaming phase is over: tables are in sync again. I can
 -       * unset the flag
 -       */
 -      orig_node->tt_poss_change = false;
  out:
        if (orig_node)
                batadv_orig_node_free_ref(orig_node);
@@@ -2192,11 -2123,11 +2198,11 @@@ static void batadv_send_roam_adv(struc
        if (!batadv_tt_check_roam_count(bat_priv, client))
                goto out;
  
 -      skb = dev_alloc_skb(sizeof(*roam_adv_packet) + ETH_HLEN);
 +      skb = dev_alloc_skb(sizeof(*roam_adv_packet) + ETH_HLEN + NET_IP_ALIGN);
        if (!skb)
                goto out;
  
 -      skb_reserve(skb, ETH_HLEN);
 +      skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
  
        roam_adv_packet = (struct batadv_roam_adv_packet *)skb_put(skb, len);
  
@@@ -2364,6 -2295,7 +2370,6 @@@ static int batadv_tt_commit_changes(str
        batadv_dbg(BATADV_DBG_TT, bat_priv,
                   "Local changes committed, updating to ttvn %u\n",
                   (uint8_t)atomic_read(&bat_priv->tt.vn));
 -      bat_priv->tt.poss_change = false;
  
        /* reset the sending counter */
        atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
@@@ -2475,6 -2407,11 +2481,6 @@@ void batadv_tt_update_orig(struct batad
                 */
                if (orig_node->tt_crc != tt_crc)
                        goto request_table;
 -
 -              /* Roaming phase is over: tables are in sync again. I can
 -               * unset the flag
 -               */
 -              orig_node->tt_poss_change = false;
        } else {
                /* if we missed more than one change or our tables are not
                 * in sync anymore -> request fresh tt data
@@@ -2507,44 -2444,25 +2513,51 @@@ bool batadv_tt_global_client_is_roaming
        if (!tt_global_entry)
                goto out;
  
 -      ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM;
 +      ret = !!(tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM);
        batadv_tt_global_entry_free_ref(tt_global_entry);
  out:
        return ret;
  }
  
 +/**
 + * batadv_tt_local_client_is_roaming - tells whether the client is roaming
 + * @bat_priv: the bat priv with all the soft interface information
 + * @addr: the MAC address of the local client to query
 + *
 + * Returns true if the local client is known to be roaming (it is not served by
 + * this node anymore) or not. If yes, the client is still present in the table
 + * to keep the latter consistent with the node TTVN
 + */
 +bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
 +                                     uint8_t *addr)
 +{
 +      struct batadv_tt_local_entry *tt_local_entry;
 +      bool ret = false;
 +
 +      tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
 +      if (!tt_local_entry)
 +              goto out;
 +
 +      ret = tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM;
 +      batadv_tt_local_entry_free_ref(tt_local_entry);
 +out:
 +      return ret;
 +
 +}
 +
  bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
                                          struct batadv_orig_node *orig_node,
                                          const unsigned char *addr)
  {
        bool ret = false;
  
+       /* if the originator is a backbone node (meaning it belongs to the same
+        * LAN of this node) the temporary client must not be added because to
+        * reach such destination the node must use the LAN instead of the mesh
+        */
+       if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig))
+               goto out;
        if (!batadv_tt_global_add(bat_priv, orig_node, addr,
                                  BATADV_TT_CLIENT_TEMP,
                                  atomic_read(&orig_node->last_ttvn)))
diff --combined net/bluetooth/hci_core.c
index 5a3f941b610f8d4a3653b8df5997f04a96fc5432,a0a2f97b9c6207a600a256415ada30d12fa88f9d..f01e5e135b998ee853c2bbddfec528ebcfc6f84b
@@@ -405,7 -405,7 +405,7 @@@ struct inquiry_entry *hci_inquiry_cache
        struct discovery_state *cache = &hdev->discovery;
        struct inquiry_entry *e;
  
 -      BT_DBG("cache %p, %s", cache, batostr(bdaddr));
 +      BT_DBG("cache %p, %pMR", cache, bdaddr);
  
        list_for_each_entry(e, &cache->all, all) {
                if (!bacmp(&e->data.bdaddr, bdaddr))
@@@ -421,7 -421,7 +421,7 @@@ struct inquiry_entry *hci_inquiry_cache
        struct discovery_state *cache = &hdev->discovery;
        struct inquiry_entry *e;
  
 -      BT_DBG("cache %p, %s", cache, batostr(bdaddr));
 +      BT_DBG("cache %p, %pMR", cache, bdaddr);
  
        list_for_each_entry(e, &cache->unknown, list) {
                if (!bacmp(&e->data.bdaddr, bdaddr))
@@@ -438,7 -438,7 +438,7 @@@ struct inquiry_entry *hci_inquiry_cache
        struct discovery_state *cache = &hdev->discovery;
        struct inquiry_entry *e;
  
 -      BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state);
 +      BT_DBG("cache %p bdaddr %pMR state %d", cache, bdaddr, state);
  
        list_for_each_entry(e, &cache->resolve, list) {
                if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state)
@@@ -475,7 -475,7 +475,7 @@@ bool hci_inquiry_cache_update(struct hc
        struct discovery_state *cache = &hdev->discovery;
        struct inquiry_entry *ie;
  
 -      BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr));
 +      BT_DBG("cache %p, %pMR", cache, &data->bdaddr);
  
        if (ssp)
                *ssp = data->ssp_mode;
@@@ -1259,7 -1259,7 +1259,7 @@@ int hci_add_link_key(struct hci_dev *hd
                list_add(&key->list, &hdev->link_keys);
        }
  
 -      BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type);
 +      BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
  
        /* Some buggy controller combinations generate a changed
         * combination key for legacy pairing even when there's no
@@@ -1338,7 -1338,7 +1338,7 @@@ int hci_remove_link_key(struct hci_dev 
        if (!key)
                return -ENOENT;
  
 -      BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
 +      BT_DBG("%s removing %pMR", hdev->name, bdaddr);
  
        list_del(&key->list);
        kfree(key);
@@@ -1354,7 -1354,7 +1354,7 @@@ int hci_remove_ltk(struct hci_dev *hdev
                if (bacmp(bdaddr, &k->bdaddr))
                        continue;
  
 -              BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
 +              BT_DBG("%s removing %pMR", hdev->name, bdaddr);
  
                list_del(&k->list);
                kfree(k);
@@@ -1401,7 -1401,7 +1401,7 @@@ int hci_remove_remote_oob_data(struct h
        if (!data)
                return -ENOENT;
  
 -      BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
 +      BT_DBG("%s removing %pMR", hdev->name, bdaddr);
  
        list_del(&data->list);
        kfree(data);
@@@ -1440,7 -1440,7 +1440,7 @@@ int hci_add_remote_oob_data(struct hci_
        memcpy(data->hash, hash, sizeof(data->hash));
        memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
  
 -      BT_DBG("%s for %s", hdev->name, batostr(bdaddr));
 +      BT_DBG("%s for %pMR", hdev->name, bdaddr);
  
        return 0;
  }
@@@ -1754,11 -1754,11 +1754,11 @@@ int hci_register_dev(struct hci_dev *hd
        if (hdev->dev_type != HCI_AMP)
                set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
  
-       schedule_work(&hdev->power_on);
        hci_notify(hdev, HCI_DEV_REG);
        hci_dev_hold(hdev);
  
+       schedule_work(&hdev->power_on);
        return id;
  
  err_wqueue:
@@@ -2153,10 -2153,9 +2153,10 @@@ static void hci_add_acl_hdr(struct sk_b
        hdr->dlen   = cpu_to_le16(len);
  }
  
 -static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
 +static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
                          struct sk_buff *skb, __u16 flags)
  {
 +      struct hci_conn *conn = chan->conn;
        struct hci_dev *hdev = conn->hdev;
        struct sk_buff *list;
  
        skb->data_len = 0;
  
        bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
 -      hci_add_acl_hdr(skb, conn->handle, flags);
 +
 +      switch (hdev->dev_type) {
 +      case HCI_BREDR:
 +              hci_add_acl_hdr(skb, conn->handle, flags);
 +              break;
 +      case HCI_AMP:
 +              hci_add_acl_hdr(skb, chan->handle, flags);
 +              break;
 +      default:
 +              BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type);
 +              return;
 +      }
  
        list = skb_shinfo(skb)->frag_list;
        if (!list) {
  
  void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
  {
 -      struct hci_conn *conn = chan->conn;
 -      struct hci_dev *hdev = conn->hdev;
 +      struct hci_dev *hdev = chan->conn->hdev;
  
        BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
  
        skb->dev = (void *) hdev;
  
 -      hci_queue_acl(conn, &chan->data_q, skb, flags);
 +      hci_queue_acl(chan, &chan->data_q, skb, flags);
  
        queue_work(hdev->workqueue, &hdev->tx_work);
  }
@@@ -2322,8 -2311,8 +2322,8 @@@ static void hci_link_tx_to(struct hci_d
        /* Kill stalled connections */
        list_for_each_entry_rcu(c, &h->list, list) {
                if (c->type == type && c->sent) {
 -                      BT_ERR("%s killing stalled connection %s",
 -                             hdev->name, batostr(&c->dst));
 +                      BT_ERR("%s killing stalled connection %pMR",
 +                             hdev->name, &c->dst);
                        hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM);
                }
        }
@@@ -2392,9 -2381,6 +2392,9 @@@ static struct hci_chan *hci_chan_sent(s
        case ACL_LINK:
                cnt = hdev->acl_cnt;
                break;
 +      case AMP_LINK:
 +              cnt = hdev->block_cnt;
 +              break;
        case SCO_LINK:
        case ESCO_LINK:
                cnt = hdev->sco_cnt;
@@@ -2524,19 -2510,11 +2524,19 @@@ static void hci_sched_acl_blk(struct hc
        struct hci_chan *chan;
        struct sk_buff *skb;
        int quote;
 +      u8 type;
  
        __check_timeout(hdev, cnt);
  
 +      BT_DBG("%s", hdev->name);
 +
 +      if (hdev->dev_type == HCI_AMP)
 +              type = AMP_LINK;
 +      else
 +              type = ACL_LINK;
 +
        while (hdev->block_cnt > 0 &&
 -             (chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
 +             (chan = hci_chan_sent(hdev, type, &quote))) {
                u32 priority = (skb_peek(&chan->data_q))->priority;
                while (quote > 0 && (skb = skb_peek(&chan->data_q))) {
                        int blocks;
        }
  
        if (cnt != hdev->block_cnt)
 -              hci_prio_recalculate(hdev, ACL_LINK);
 +              hci_prio_recalculate(hdev, type);
  }
  
  static void hci_sched_acl(struct hci_dev *hdev)
  {
        BT_DBG("%s", hdev->name);
  
 -      if (!hci_conn_num(hdev, ACL_LINK))
 +      /* No ACL link over BR/EDR controller */
 +      if (!hci_conn_num(hdev, ACL_LINK) && hdev->dev_type == HCI_BREDR)
 +              return;
 +
 +      /* No AMP link over AMP controller */
 +      if (!hci_conn_num(hdev, AMP_LINK) && hdev->dev_type == HCI_AMP)
                return;
  
        switch (hdev->flow_ctl_mode) {
diff --combined net/bluetooth/mgmt.c
index 399e5024b5bdb70e1b4c9ff4c409a0cc9d9b435a,91de4239da6621d6e5b7a2989beb5bd5b883ff8c..158a87bb0c0d419755765b36c19e8441daceb407
@@@ -326,7 -326,7 +326,7 @@@ static int read_index_list(struct sock 
        struct hci_dev *d;
        size_t rp_len;
        u16 count;
-       int i, err;
+       int err;
  
        BT_DBG("sock %p", sk);
  
                return -ENOMEM;
        }
  
-       rp->num_controllers = cpu_to_le16(count);
-       i = 0;
+       count = 0;
        list_for_each_entry(d, &hci_dev_list, list) {
                if (test_bit(HCI_SETUP, &d->dev_flags))
                        continue;
                if (!mgmt_valid_hdev(d))
                        continue;
  
-               rp->index[i++] = cpu_to_le16(d->id);
+               rp->index[count++] = cpu_to_le16(d->id);
                BT_DBG("Added hci%u", d->id);
        }
  
+       rp->num_controllers = cpu_to_le16(count);
+       rp_len = sizeof(*rp) + (2 * count);
        read_unlock(&hci_dev_list_lock);
  
        err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp,
@@@ -1366,6 -1367,7 +1367,7 @@@ static int remove_uuid(struct sock *sk
                        continue;
  
                list_del(&match->list);
+               kfree(match);
                found++;
        }
  
@@@ -3125,9 -3127,6 +3127,9 @@@ int mgmt_disconnect_failed(struct hci_d
        struct pending_cmd *cmd;
        int err;
  
 +      mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
 +                           hdev);
 +
        cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
        if (!cmd)
                return -ENOENT;
  
        mgmt_pending_remove(cmd);
  
 -      mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
 -                           hdev);
        return err;
  }
  
diff --combined net/bluetooth/smp.c
index 9176bc17595c80c6738b1a3fc64466c82c39fcb9,a5923378bdf03ab73a259cd3c74f88b890972030..68a9587c96945cdd3264cc310b68b7be95cfd953
@@@ -167,7 -167,7 +167,7 @@@ static struct sk_buff *smp_build_cmd(st
  
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->len = cpu_to_le16(sizeof(code) + dlen);
 -      lh->cid = cpu_to_le16(L2CAP_CID_SMP);
 +      lh->cid = __constant_cpu_to_le16(L2CAP_CID_SMP);
  
        memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
  
@@@ -267,7 -267,7 +267,7 @@@ static void smp_failure(struct l2cap_co
  
        clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags);
        mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type,
-                        hcon->dst_type, reason);
+                        hcon->dst_type, HCI_ERROR_AUTH_FAILURE);
  
        cancel_delayed_work_sync(&conn->security_timer);
  
diff --combined net/core/dev.c
index 2705a2ab89af6a0b91e935dc3994b17f690ff34d,c0946cb2b3547f4fdea0fa56b8f7e17c71a75012..974199daa911488df13c8902344ee857f23440e4
  #define PTYPE_HASH_MASK       (PTYPE_HASH_SIZE - 1)
  
  static DEFINE_SPINLOCK(ptype_lock);
 +static DEFINE_SPINLOCK(offload_lock);
  static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
  static struct list_head ptype_all __read_mostly;      /* Taps */
 +static struct list_head offload_base __read_mostly;
  
  /*
   * The @dev_base_head list is protected by @dev_base_lock and the rtnl
@@@ -472,82 -470,6 +472,82 @@@ void dev_remove_pack(struct packet_typ
  }
  EXPORT_SYMBOL(dev_remove_pack);
  
 +
 +/**
 + *    dev_add_offload - register offload handlers
 + *    @po: protocol offload declaration
 + *
 + *    Add protocol offload handlers to the networking stack. The passed
 + *    &proto_offload is linked into kernel lists and may not be freed until
 + *    it has been removed from the kernel lists.
 + *
 + *    This call does not sleep therefore it can not
 + *    guarantee all CPU's that are in middle of receiving packets
 + *    will see the new offload handlers (until the next received packet).
 + */
 +void dev_add_offload(struct packet_offload *po)
 +{
 +      struct list_head *head = &offload_base;
 +
 +      spin_lock(&offload_lock);
 +      list_add_rcu(&po->list, head);
 +      spin_unlock(&offload_lock);
 +}
 +EXPORT_SYMBOL(dev_add_offload);
 +
 +/**
 + *    __dev_remove_offload     - remove offload handler
 + *    @po: packet offload declaration
 + *
 + *    Remove a protocol offload handler that was previously added to the
 + *    kernel offload handlers by dev_add_offload(). The passed &offload_type
 + *    is removed from the kernel lists and can be freed or reused once this
 + *    function returns.
 + *
 + *      The packet type might still be in use by receivers
 + *    and must not be freed until after all the CPU's have gone
 + *    through a quiescent state.
 + */
 +void __dev_remove_offload(struct packet_offload *po)
 +{
 +      struct list_head *head = &offload_base;
 +      struct packet_offload *po1;
 +
 +      spin_lock(&offload_lock);
 +
 +      list_for_each_entry(po1, head, list) {
 +              if (po == po1) {
 +                      list_del_rcu(&po->list);
 +                      goto out;
 +              }
 +      }
 +
 +      pr_warn("dev_remove_offload: %p not found\n", po);
 +out:
 +      spin_unlock(&offload_lock);
 +}
 +EXPORT_SYMBOL(__dev_remove_offload);
 +
 +/**
 + *    dev_remove_offload       - remove packet offload handler
 + *    @po: packet offload declaration
 + *
 + *    Remove a packet offload handler that was previously added to the kernel
 + *    offload handlers by dev_add_offload(). The passed &offload_type is
 + *    removed from the kernel lists and can be freed or reused once this
 + *    function returns.
 + *
 + *    This call sleeps to guarantee that no CPU is looking at the packet
 + *    type after return.
 + */
 +void dev_remove_offload(struct packet_offload *po)
 +{
 +      __dev_remove_offload(po);
 +
 +      synchronize_net();
 +}
 +EXPORT_SYMBOL(dev_remove_offload);
 +
  /******************************************************************************
  
                      Device Boot-time Settings Routines
@@@ -2072,7 -1994,7 +2072,7 @@@ struct sk_buff *skb_gso_segment(struct 
        netdev_features_t features)
  {
        struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
 -      struct packet_type *ptype;
 +      struct packet_offload *ptype;
        __be16 type = skb->protocol;
        int vlan_depth = ETH_HLEN;
        int err;
        }
  
        rcu_read_lock();
 -      list_for_each_entry_rcu(ptype,
 -                      &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
 -              if (ptype->type == type && !ptype->dev && ptype->gso_segment) {
 +      list_for_each_entry_rcu(ptype, &offload_base, list) {
 +              if (ptype->type == type && ptype->callbacks.gso_segment) {
                        if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
 -                              err = ptype->gso_send_check(skb);
 +                              err = ptype->callbacks.gso_send_check(skb);
                                segs = ERR_PTR(err);
                                if (err || skb_gso_ok(skb, features))
                                        break;
                                __skb_push(skb, (skb->data -
                                                 skb_network_header(skb)));
                        }
 -                      segs = ptype->gso_segment(skb, features);
 +                      segs = ptype->callbacks.gso_segment(skb, features);
                        break;
                }
        }
@@@ -2895,8 -2818,10 +2895,10 @@@ static int get_rps_cpu(struct net_devic
                if (unlikely(tcpu != next_cpu) &&
                    (tcpu == RPS_NO_CPU || !cpu_online(tcpu) ||
                     ((int)(per_cpu(softnet_data, tcpu).input_queue_head -
-                     rflow->last_qtail)) >= 0))
+                     rflow->last_qtail)) >= 0)) {
+                       tcpu = next_cpu;
                        rflow = set_rps_cpu(dev, skb, rflow, next_cpu);
+               }
  
                if (tcpu != RPS_NO_CPU && cpu_online(tcpu)) {
                        *rflowp = rflow;
@@@ -3521,9 -3446,9 +3523,9 @@@ static void flush_backlog(void *arg
  
  static int napi_gro_complete(struct sk_buff *skb)
  {
 -      struct packet_type *ptype;
 +      struct packet_offload *ptype;
        __be16 type = skb->protocol;
 -      struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
 +      struct list_head *head = &offload_base;
        int err = -ENOENT;
  
        if (NAPI_GRO_CB(skb)->count == 1) {
  
        rcu_read_lock();
        list_for_each_entry_rcu(ptype, head, list) {
 -              if (ptype->type != type || ptype->dev || !ptype->gro_complete)
 +              if (ptype->type != type || !ptype->callbacks.gro_complete)
                        continue;
  
 -              err = ptype->gro_complete(skb);
 +              err = ptype->callbacks.gro_complete(skb);
                break;
        }
        rcu_read_unlock();
@@@ -3583,9 -3508,9 +3585,9 @@@ EXPORT_SYMBOL(napi_gro_flush)
  enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
  {
        struct sk_buff **pp = NULL;
 -      struct packet_type *ptype;
 +      struct packet_offload *ptype;
        __be16 type = skb->protocol;
 -      struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
 +      struct list_head *head = &offload_base;
        int same_flow;
        int mac_len;
        enum gro_result ret;
  
        rcu_read_lock();
        list_for_each_entry_rcu(ptype, head, list) {
 -              if (ptype->type != type || ptype->dev || !ptype->gro_receive)
 +              if (ptype->type != type || !ptype->callbacks.gro_receive)
                        continue;
  
                skb_set_network_header(skb, skb_gro_offset(skb));
                NAPI_GRO_CB(skb)->flush = 0;
                NAPI_GRO_CB(skb)->free = 0;
  
 -              pp = ptype->gro_receive(&napi->gro_list, skb);
 +              pp = ptype->callbacks.gro_receive(&napi->gro_list, skb);
                break;
        }
        rcu_read_unlock();
@@@ -6341,6 -6266,7 +6343,6 @@@ int dev_change_net_namespace(struct net
                goto out;
  
        /* Ensure the device has been registrered */
 -      err = -EINVAL;
        if (dev->reg_state != NETREG_REGISTERED)
                goto out;
  
@@@ -6738,8 -6664,6 +6740,8 @@@ static int __init net_dev_init(void
        for (i = 0; i < PTYPE_HASH_SIZE; i++)
                INIT_LIST_HEAD(&ptype_base[i]);
  
 +      INIT_LIST_HEAD(&offload_base);
 +
        if (register_pernet_subsys(&netdev_net_ops))
                goto out;
  
diff --combined net/ipv4/ip_vti.c
index 516188b0dc1e4842a43350c2987de0ece26464a9,858fddf6482a645c46bf441c67d9ed323dc7d78f..f4a825d3bd7ffea56b08022fb8c528055c8281ea
@@@ -66,6 -66,20 +66,6 @@@ static void vti_tunnel_setup(struct net
  static void vti_dev_free(struct net_device *dev);
  static int vti_tunnel_bind_dev(struct net_device *dev);
  
 -/* Locking : hash tables are protected by RCU and RTNL */
 -
 -#define for_each_ip_tunnel_rcu(start) \
 -      for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
 -
 -/* often modified stats are per cpu, other are shared (netdev->stats) */
 -struct pcpu_tstats {
 -      u64     rx_packets;
 -      u64     rx_bytes;
 -      u64     tx_packets;
 -      u64     tx_bytes;
 -      struct  u64_stats_sync  syncp;
 -};
 -
  #define VTI_XMIT(stats1, stats2) do {                         \
        int err;                                                \
        int pkt_len = skb->len;                                 \
@@@ -128,19 -142,19 +128,19 @@@ static struct ip_tunnel *vti_tunnel_loo
        struct ip_tunnel *t;
        struct vti_net *ipn = net_generic(net, vti_net_id);
  
 -      for_each_ip_tunnel_rcu(ipn->tunnels_r_l[h0 ^ h1])
 +      for_each_ip_tunnel_rcu(t, ipn->tunnels_r_l[h0 ^ h1])
                if (local == t->parms.iph.saddr &&
                    remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
                        return t;
 -      for_each_ip_tunnel_rcu(ipn->tunnels_r[h0])
 +      for_each_ip_tunnel_rcu(t, ipn->tunnels_r[h0])
                if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
                        return t;
  
 -      for_each_ip_tunnel_rcu(ipn->tunnels_l[h1])
 +      for_each_ip_tunnel_rcu(t, ipn->tunnels_l[h1])
                if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP))
                        return t;
  
 -      for_each_ip_tunnel_rcu(ipn->tunnels_wc[0])
 +      for_each_ip_tunnel_rcu(t, ipn->tunnels_wc[0])
                if (t && (t->dev->flags&IFF_UP))
                        return t;
        return NULL;
@@@ -324,12 -338,17 +324,17 @@@ static int vti_rcv(struct sk_buff *skb
        if (tunnel != NULL) {
                struct pcpu_tstats *tstats;
  
+               if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+                       return -1;
                tstats = this_cpu_ptr(tunnel->dev->tstats);
                u64_stats_update_begin(&tstats->syncp);
                tstats->rx_packets++;
                tstats->rx_bytes += skb->len;
                u64_stats_update_end(&tstats->syncp);
  
+               skb->mark = 0;
+               secpath_reset(skb);
                skb->dev = tunnel->dev;
                return 1;
        }
diff --combined net/ipv4/tcp.c
index 733f48593ec3995ab7eb5f7d9a92de7383cc7132,083092e3aed68db2dd5e9c3fb9cd43759a529abe..4aefa0b42c2e1bd0ff2284346ebaba9de27184d4
@@@ -536,14 -536,13 +536,14 @@@ int tcp_ioctl(struct sock *sk, int cmd
  {
        struct tcp_sock *tp = tcp_sk(sk);
        int answ;
 +      bool slow;
  
        switch (cmd) {
        case SIOCINQ:
                if (sk->sk_state == TCP_LISTEN)
                        return -EINVAL;
  
 -              lock_sock(sk);
 +              slow = lock_sock_fast(sk);
                if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))
                        answ = 0;
                else if (sock_flag(sk, SOCK_URGINLINE) ||
                                answ--;
                } else
                        answ = tp->urg_seq - tp->copied_seq;
 -              release_sock(sk);
 +              unlock_sock_fast(sk, slow);
                break;
        case SIOCATMARK:
                answ = tp->urg_data && tp->urg_seq == tp->copied_seq;
@@@ -1213,7 -1212,7 +1213,7 @@@ new_segment
  wait_for_sndbuf:
                        set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
  wait_for_memory:
-                       if (copied && likely(!tp->repair))
+                       if (copied)
                                tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
  
                        if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
        }
  
  out:
-       if (copied && likely(!tp->repair))
+       if (copied)
                tcp_push(sk, flags, mss_now, tp->nonagle);
        release_sock(sk);
        return copied + copied_syn;
diff --combined net/ipv4/tcp_input.c
index 7839d51fb65bb7bdabf69071db649abea6a71aeb,609ff98aeb47ce99500614ff4a8a9cfe3a290d28..fc67831656e5d00a833c81409ff07bf48fd7e41b
@@@ -3552,24 -3552,6 +3552,24 @@@ static bool tcp_process_frto(struct soc
        return false;
  }
  
 +/* RFC 5961 7 [ACK Throttling] */
 +static void tcp_send_challenge_ack(struct sock *sk)
 +{
 +      /* unprotected vars, we dont care of overwrites */
 +      static u32 challenge_timestamp;
 +      static unsigned int challenge_count;
 +      u32 now = jiffies / HZ;
 +
 +      if (now != challenge_timestamp) {
 +              challenge_timestamp = now;
 +              challenge_count = 0;
 +      }
 +      if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
 +              NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
 +              tcp_send_ack(sk);
 +      }
 +}
 +
  /* This routine deals with incoming acks, but not outgoing ones. */
  static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
  {
        /* If the ack is older than previous acks
         * then we can probably ignore it.
         */
 -      if (before(ack, prior_snd_una))
 +      if (before(ack, prior_snd_una)) {
 +              /* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */
 +              if (before(ack, prior_snd_una - tp->max_window)) {
 +                      tcp_send_challenge_ack(sk);
 +                      return -1;
 +              }
                goto old_ack;
 +      }
  
        /* If the ack includes data we haven't sent yet, discard
         * this segment (RFC793 Section 3.9).
@@@ -5268,6 -5244,23 +5268,6 @@@ out
  }
  #endif /* CONFIG_NET_DMA */
  
 -static void tcp_send_challenge_ack(struct sock *sk)
 -{
 -      /* unprotected vars, we dont care of overwrites */
 -      static u32 challenge_timestamp;
 -      static unsigned int challenge_count;
 -      u32 now = jiffies / HZ;
 -
 -      if (now != challenge_timestamp) {
 -              challenge_timestamp = now;
 -              challenge_count = 0;
 -      }
 -      if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
 -              NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
 -              tcp_send_ack(sk);
 -      }
 -}
 -
  /* Does PAWS and seqno based validation of an incoming segment, flags will
   * play significant role here.
   */
@@@ -5320,11 -5313,6 +5320,6 @@@ static bool tcp_validate_incoming(struc
                goto discard;
        }
  
-       /* ts_recent update must be made after we are sure that the packet
-        * is in window.
-        */
-       tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
        /* step 3: check security and precedence [ignored] */
  
        /* step 4: Check for a SYN
@@@ -5559,6 -5547,11 +5554,11 @@@ step5
        if (th->ack && tcp_ack(sk, skb, FLAG_SLOWPATH) < 0)
                goto discard;
  
+       /* ts_recent update must be made after we are sure that the packet
+        * is in window.
+        */
+       tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
        tcp_rcv_rtt_measure_ts(sk, skb);
  
        /* Process urgent data. */
@@@ -5995,7 -5988,7 +5995,7 @@@ int tcp_rcv_state_process(struct sock *
                                 */
                                if (req) {
                                        tcp_synack_rtt_meas(sk, req);
 -                                      tp->total_retrans = req->retrans;
 +                                      tp->total_retrans = req->num_retrans;
  
                                        reqsk_fastopen_remove(sk, req, false);
                                } else {
        } else
                goto discard;
  
+       /* ts_recent update must be made after we are sure that the packet
+        * is in window.
+        */
+       tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
        /* step 6: check the URG bit */
        tcp_urg(sk, skb, th);
  
diff --combined net/ipv6/ipv6_sockglue.c
index a7bee6a9133505b1ab095b86ab0b72ac7f7418e0,e02faed6d17efa1684c2c9b0990506008bdb8aff..4b4172dbbe6449fbcbf63109b39b863b14c08ac7
@@@ -397,7 -397,7 +397,7 @@@ static int do_ipv6_setsockopt(struct so
                if (optname == IPV6_RTHDR && opt && opt->srcrt) {
                        struct ipv6_rt_hdr *rthdr = opt->srcrt;
                        switch (rthdr->type) {
 -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
 +#if IS_ENABLED(CONFIG_IPV6_MIP6)
                        case IPV6_SRCRT_TYPE_2:
                                if (rthdr->hdrlen != 2 ||
                                    rthdr->segments_left != 1)
@@@ -827,6 -827,7 +827,7 @@@ pref_skip_coa
                if (val < 0 || val > 255)
                        goto e_inval;
                np->min_hopcount = val;
+               retv = 0;
                break;
        case IPV6_DONTFRAG:
                np->dontfrag = valbool;
diff --combined net/mac80211/cfg.c
index 5eab1325a0f6fc3ff2e53df503fc26e366dec14f,7371f676cf412e54481751d36bf757f42dfa5134..76690020d605ae5bbdd6cea45fe51af48040da0a
@@@ -372,11 -372,10 +372,11 @@@ static int ieee80211_config_default_mgm
  
  static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx)
  {
 +      enum ieee80211_band band = ieee80211_get_sdata_band(sta->sdata);
 +
        if (!(rate->flags & RATE_INFO_FLAGS_MCS)) {
                struct ieee80211_supported_band *sband;
 -              sband = sta->local->hw.wiphy->bands[
 -                              sta->local->oper_channel->band];
 +              sband = sta->local->hw.wiphy->bands[band];
                rate->legacy = sband->bitrates[idx].bitrate;
        } else
                rate->mcs = idx;
@@@ -533,8 -532,6 +533,8 @@@ static void ieee80211_get_et_stats(stru
                                   u64 *data)
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +      struct ieee80211_channel *channel;
        struct sta_info *sta;
        struct ieee80211_local *local = sdata->local;
        struct station_info sinfo;
  do_survey:
        i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
        /* Get survey stats for current channel */
 -      q = 0;
 -      while (true) {
 -              survey.filled = 0;
 -              if (drv_get_survey(local, q, &survey) != 0) {
 -                      survey.filled = 0;
 -                      break;
 -              }
 +      survey.filled = 0;
  
 -              if (survey.channel &&
 -                  (local->oper_channel->center_freq ==
 -                   survey.channel->center_freq))
 -                      break;
 -              q++;
 +      rcu_read_lock();
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +      if (chanctx_conf)
 +              channel = chanctx_conf->channel;
 +      else
 +              channel = NULL;
 +      rcu_read_unlock();
 +
 +      if (channel) {
 +              q = 0;
 +              do {
 +                      survey.filled = 0;
 +                      if (drv_get_survey(local, q, &survey) != 0) {
 +                              survey.filled = 0;
 +                              break;
 +                      }
 +                      q++;
 +              } while (channel != survey.channel);
        }
  
        if (survey.filled)
@@@ -734,42 -724,47 +734,42 @@@ static int ieee80211_get_station(struc
        return ret;
  }
  
 -static int ieee80211_set_channel(struct wiphy *wiphy,
 -                               struct net_device *netdev,
 -                               struct ieee80211_channel *chan,
 -                               enum nl80211_channel_type channel_type)
 +static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
 +                                       struct ieee80211_channel *chan,
 +                                       enum nl80211_channel_type channel_type)
  {
        struct ieee80211_local *local = wiphy_priv(wiphy);
 -      struct ieee80211_sub_if_data *sdata = NULL;
 -
 -      if (netdev)
 -              sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
 -
 -      switch (ieee80211_get_channel_mode(local, NULL)) {
 -      case CHAN_MODE_HOPPING:
 -              return -EBUSY;
 -      case CHAN_MODE_FIXED:
 -              if (local->oper_channel != chan ||
 -                  (!sdata && local->_oper_channel_type != channel_type))
 -                      return -EBUSY;
 -              if (!sdata && local->_oper_channel_type == channel_type)
 -                      return 0;
 -              break;
 -      case CHAN_MODE_UNDEFINED:
 -              break;
 -      }
 -
 -      if (!ieee80211_set_channel_type(local, sdata, channel_type))
 -              return -EBUSY;
 +      struct ieee80211_sub_if_data *sdata;
 +      int ret = 0;
  
 -      local->oper_channel = chan;
 +      if (local->monitor_channel == chan &&
 +          local->monitor_channel_type == channel_type)
 +              return 0;
  
 -      /* auto-detects changes */
 -      ieee80211_hw_config(local, 0);
 +      mutex_lock(&local->iflist_mtx);
 +      if (local->use_chanctx) {
 +              sdata = rcu_dereference_protected(
 +                              local->monitor_sdata,
 +                              lockdep_is_held(&local->iflist_mtx));
 +              if (sdata) {
 +                      ieee80211_vif_release_channel(sdata);
 +                      ret = ieee80211_vif_use_channel(
 +                                      sdata, chan, channel_type,
 +                                      IEEE80211_CHANCTX_EXCLUSIVE);
 +              }
 +      } else if (local->open_count == local->monitors) {
 +              local->_oper_channel = chan;
 +              local->_oper_channel_type = channel_type;
 +              ieee80211_hw_config(local, 0);
 +      }
  
 -      return 0;
 -}
 +      if (ret == 0) {
 +              local->monitor_channel = chan;
 +              local->monitor_channel_type = channel_type;
 +      }
 +      mutex_unlock(&local->iflist_mtx);
  
 -static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
 -                                       struct ieee80211_channel *chan,
 -                                       enum nl80211_channel_type channel_type)
 -{
 -      return ieee80211_set_channel(wiphy, NULL, chan, channel_type);
 +      return ret;
  }
  
  static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
@@@ -884,13 -879,8 +884,13 @@@ static int ieee80211_start_ap(struct wi
        if (old)
                return -EALREADY;
  
 -      err = ieee80211_set_channel(wiphy, dev, params->channel,
 -                                  params->channel_type);
 +      /* TODO: make hostapd tell us what it wants */
 +      sdata->smps_mode = IEEE80211_SMPS_OFF;
 +      sdata->needed_rx_chains = sdata->local->rx_chains;
 +
 +      err = ieee80211_vif_use_channel(sdata, params->channel,
 +                                      params->channel_type,
 +                                      IEEE80211_CHANCTX_SHARED);
        if (err)
                return err;
  
@@@ -973,8 -963,6 +973,8 @@@ static int ieee80211_stop_ap(struct wip
        sta_info_flush(sdata->local, sdata);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
  
 +      ieee80211_vif_release_channel(sdata);
 +
        return 0;
  }
  
@@@ -1031,10 -1019,9 +1031,10 @@@ static int sta_apply_parameters(struct 
        int i, j;
        struct ieee80211_supported_band *sband;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 +      enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        u32 mask, set;
  
 -      sband = local->hw.wiphy->bands[local->oper_channel->band];
 +      sband = local->hw.wiphy->bands[band];
  
        mask = params->sta_flags_mask;
        set = params->sta_flags_set;
                                        rates |= BIT(j);
                        }
                }
 -              sta->sta.supp_rates[local->oper_channel->band] = rates;
 +              sta->sta.supp_rates[band] = rates;
        }
  
        if (params->ht_capa)
                                                  params->ht_capa,
                                                  &sta->sta.ht_cap);
  
 +      if (params->vht_capa)
 +              ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
 +                                                  params->vht_capa,
 +                                                  &sta->sta.vht_cap);
 +
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
  #ifdef CONFIG_MAC80211_MESH
                if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
@@@ -1682,13 -1664,8 +1682,13 @@@ static int ieee80211_join_mesh(struct w
        if (err)
                return err;
  
 -      err = ieee80211_set_channel(wiphy, dev, setup->channel,
 -                                  setup->channel_type);
 +      /* can mesh use other SMPS modes? */
 +      sdata->smps_mode = IEEE80211_SMPS_OFF;
 +      sdata->needed_rx_chains = sdata->local->rx_chains;
 +
 +      err = ieee80211_vif_use_channel(sdata, setup->channel,
 +                                      setup->channel_type,
 +                                      IEEE80211_CHANCTX_SHARED);
        if (err)
                return err;
  
@@@ -1702,7 -1679,6 +1702,7 @@@ static int ieee80211_leave_mesh(struct 
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
  
        ieee80211_stop_mesh(sdata);
 +      ieee80211_vif_release_channel(sdata);
  
        return 0;
  }
@@@ -1712,14 -1688,10 +1712,14 @@@ static int ieee80211_change_bss(struct 
                                struct net_device *dev,
                                struct bss_parameters *params)
  {
 -      struct ieee80211_sub_if_data *sdata;
 +      struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 +      enum ieee80211_band band;
        u32 changed = 0;
  
 -      sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 +      if (!rtnl_dereference(sdata->u.ap.beacon))
 +              return -ENOENT;
 +
 +      band = ieee80211_get_sdata_band(sdata);
  
        if (params->use_cts_prot >= 0) {
                sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
        }
  
        if (!sdata->vif.bss_conf.use_short_slot &&
 -          sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) {
 +          band == IEEE80211_BAND_5GHZ) {
                sdata->vif.bss_conf.use_short_slot = true;
                changed |= BSS_CHANGED_ERP_SLOT;
        }
        if (params->basic_rates) {
                int i, j;
                u32 rates = 0;
 -              struct ieee80211_local *local = wiphy_priv(wiphy);
 -              struct ieee80211_supported_band *sband =
 -                      wiphy->bands[local->oper_channel->band];
 +              struct ieee80211_supported_band *sband = wiphy->bands[band];
  
                for (i = 0; i < params->basic_rates_len; i++) {
                        int rate = (params->basic_rates[i] & 0x7f) * 5;
@@@ -1855,16 -1829,7 +1855,16 @@@ static int ieee80211_scan(struct wiphy 
                 * beaconing hasn't been configured yet
                 */
        case NL80211_IFTYPE_AP:
 -              if (sdata->u.ap.beacon)
 +              /*
 +               * If the scan has been forced (and the driver supports
 +               * forcing), don't care about being beaconing already.
 +               * This will create problems to the attached stations (e.g. all
 +               * the  frames sent while scanning on other channel will be
 +               * lost)
 +               */
 +              if (sdata->u.ap.beacon &&
 +                  (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
 +                   !(req->flags & NL80211_SCAN_FLAG_AP)))
                        return -EOPNOTSUPP;
                break;
        default:
@@@ -1907,6 -1872,20 +1907,6 @@@ static int ieee80211_auth(struct wiphy 
  static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
                           struct cfg80211_assoc_request *req)
  {
 -      struct ieee80211_local *local = wiphy_priv(wiphy);
 -      struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 -
 -      switch (ieee80211_get_channel_mode(local, sdata)) {
 -      case CHAN_MODE_HOPPING:
 -              return -EBUSY;
 -      case CHAN_MODE_FIXED:
 -              if (local->oper_channel == req->bss->channel)
 -                      break;
 -              return -EBUSY;
 -      case CHAN_MODE_UNDEFINED:
 -              break;
 -      }
 -
        return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
  }
  
@@@ -1925,12 -1904,30 +1925,12 @@@ static int ieee80211_disassoc(struct wi
  static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
                               struct cfg80211_ibss_params *params)
  {
 -      struct ieee80211_local *local = wiphy_priv(wiphy);
 -      struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 -
 -      switch (ieee80211_get_channel_mode(local, sdata)) {
 -      case CHAN_MODE_HOPPING:
 -              return -EBUSY;
 -      case CHAN_MODE_FIXED:
 -              if (!params->channel_fixed)
 -                      return -EBUSY;
 -              if (local->oper_channel == params->channel)
 -                      break;
 -              return -EBUSY;
 -      case CHAN_MODE_UNDEFINED:
 -              break;
 -      }
 -
 -      return ieee80211_ibss_join(sdata, params);
 +      return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params);
  }
  
  static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
  {
 -      struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 -
 -      return ieee80211_ibss_leave(sdata);
 +      return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
  }
  
  static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
@@@ -1974,13 -1971,9 +1974,13 @@@ static int ieee80211_set_tx_power(struc
                                  enum nl80211_tx_power_setting type, int mbm)
  {
        struct ieee80211_local *local = wiphy_priv(wiphy);
 -      struct ieee80211_channel *chan = local->oper_channel;
 +      struct ieee80211_channel *chan = local->_oper_channel;
        u32 changes = 0;
  
 +      /* FIXME */
 +      if (local->use_chanctx)
 +              return -EOPNOTSUPP;
 +
        switch (type) {
        case NL80211_TX_POWER_AUTOMATIC:
                local->user_power_level = -1;
@@@ -2074,12 -2067,13 +2074,12 @@@ int __ieee80211_request_smps(struct iee
  
        /*
         * If not associated, or current association is not an HT
 -       * association, there's no need to send an action frame.
 +       * association, there's no need to do anything, just store
 +       * the new value until we associate.
         */
        if (!sdata->u.mgd.associated ||
 -          sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
 -              ieee80211_recalc_smps(sdata->local);
 +          sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
                return 0;
 -      }
  
        ap = sdata->u.mgd.associated->bssid;
  
@@@ -2195,9 -2189,6 +2195,9 @@@ static int ieee80211_start_roc_work(str
  
        lockdep_assert_held(&local->mtx);
  
 +      if (local->use_chanctx && !local->ops->remain_on_channel)
 +              return -EOPNOTSUPP;
 +
        roc = kzalloc(sizeof(*roc), GFP_KERNEL);
        if (!roc)
                return -ENOMEM;
@@@ -2524,20 -2515,10 +2524,20 @@@ static int ieee80211_mgmt_tx(struct wip
  
        /* Check if the operating channel is the requested channel */
        if (!need_offchan) {
 -              need_offchan = chan != local->oper_channel;
 -              if (channel_type_valid &&
 -                  channel_type != local->_oper_channel_type)
 +              struct ieee80211_chanctx_conf *chanctx_conf;
 +
 +              rcu_read_lock();
 +              chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +
 +              if (chanctx_conf) {
 +                      need_offchan = chan != chanctx_conf->channel;
 +                      if (channel_type_valid &&
 +                          channel_type != chanctx_conf->channel_type)
 +                              need_offchan = true;
 +              } else {
                        need_offchan = true;
 +              }
 +              rcu_read_unlock();
        }
  
        if (need_offchan && !offchan) {
@@@ -2613,6 -2594,9 +2613,9 @@@ static void ieee80211_mgmt_frame_regist
                else
                        local->probe_req_reg--;
  
+               if (!local->open_count)
+                       break;
                ieee80211_queue_work(&local->hw, &local->reconfig_filter);
                break;
        default:
@@@ -2686,7 -2670,7 +2689,7 @@@ static u16 ieee80211_get_tdls_sta_capab
        u16 capab;
  
        capab = 0;
 -      if (local->oper_channel->band != IEEE80211_BAND_2GHZ)
 +      if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
                return capab;
  
        if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
@@@ -2718,7 -2702,7 +2721,7 @@@ ieee80211_prep_tdls_encap_data(struct w
                               u16 status_code, struct sk_buff *skb)
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 -      struct ieee80211_local *local = sdata->local;
 +      enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_tdls_data *tf;
  
        tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
                tf->u.setup_req.capability =
                        cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
  
 -              ieee80211_add_srates_ie(sdata, skb, false,
 -                                      local->oper_channel->band);
 -              ieee80211_add_ext_srates_ie(sdata, skb, false,
 -                                          local->oper_channel->band);
 +              ieee80211_add_srates_ie(sdata, skb, false, band);
 +              ieee80211_add_ext_srates_ie(sdata, skb, false, band);
                ieee80211_tdls_add_ext_capab(skb);
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
                tf->u.setup_resp.capability =
                        cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
  
 -              ieee80211_add_srates_ie(sdata, skb, false,
 -                                      local->oper_channel->band);
 -              ieee80211_add_ext_srates_ie(sdata, skb, false,
 -                                          local->oper_channel->band);
 +              ieee80211_add_srates_ie(sdata, skb, false, band);
 +              ieee80211_add_ext_srates_ie(sdata, skb, false, band);
                ieee80211_tdls_add_ext_capab(skb);
                break;
        case WLAN_TDLS_SETUP_CONFIRM:
@@@ -2791,7 -2779,7 +2794,7 @@@ ieee80211_prep_tdls_direct(struct wiph
                           u16 status_code, struct sk_buff *skb)
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 -      struct ieee80211_local *local = sdata->local;
 +      enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_mgmt *mgmt;
  
        mgmt = (void *)skb_put(skb, 24);
                mgmt->u.action.u.tdls_discover_resp.capability =
                        cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
  
 -              ieee80211_add_srates_ie(sdata, skb, false,
 -                                      local->oper_channel->band);
 -              ieee80211_add_ext_srates_ie(sdata, skb, false,
 -                                          local->oper_channel->band);
 +              ieee80211_add_srates_ie(sdata, skb, false, band);
 +              ieee80211_add_ext_srates_ie(sdata, skb, false, band);
                ieee80211_tdls_add_ext_capab(skb);
                break;
        default:
@@@ -2832,6 -2822,7 +2835,6 @@@ static int ieee80211_tdls_mgmt(struct w
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
 -      struct ieee80211_tx_info *info;
        struct sk_buff *skb = NULL;
        bool send_direct;
        int ret;
        if (!skb)
                return -ENOMEM;
  
 -      info = IEEE80211_SKB_CB(skb);
        skb_reserve(skb, local->hw.extra_tx_headroom);
  
        switch (action_code) {
@@@ -2993,19 -2985,12 +2996,19 @@@ static int ieee80211_probe_client(struc
        bool qos;
        struct ieee80211_tx_info *info;
        struct sta_info *sta;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +      enum ieee80211_band band;
  
        rcu_read_lock();
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +      if (WARN_ON(!chanctx_conf)) {
 +              rcu_read_unlock();
 +              return -EINVAL;
 +      }
 +      band = chanctx_conf->channel->band;
        sta = sta_info_get(sdata, peer);
        if (sta) {
                qos = test_sta_flag(sta, WLAN_STA_WME);
 -              rcu_read_unlock();
        } else {
                rcu_read_unlock();
                return -ENOLINK;
        }
  
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
 -      if (!skb)
 +      if (!skb) {
 +              rcu_read_unlock();
                return -ENOMEM;
 +      }
  
        skb->dev = dev;
  
                nullfunc->qos_ctrl = cpu_to_le16(7);
  
        local_bh_disable();
 -      ieee80211_xmit(sdata, skb);
 +      ieee80211_xmit(sdata, skb, band);
        local_bh_enable();
 +      rcu_read_unlock();
  
        *cookie = (unsigned long) skb;
        return 0;
@@@ -3063,19 -3045,10 +3066,19 @@@ static struct ieee80211_channel 
  ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
                          enum nl80211_channel_type *type)
  {
 -      struct ieee80211_local *local = wiphy_priv(wiphy);
 +      struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +      struct ieee80211_channel *chan = NULL;
 +
 +      rcu_read_lock();
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +      if (chanctx_conf) {
 +              *type = chanctx_conf->channel_type;
 +              chan = chanctx_conf->channel;
 +      }
 +      rcu_read_unlock();
  
 -      *type = local->_oper_channel_type;
 -      return local->oper_channel;
 +      return chan;
  }
  
  #ifdef CONFIG_PM
index 3026519b236afb0f7cd9c32907e790d18d93e018,156e5835e37f4b140fb9261c2c1ed44f73cf8408..32e47853f329262eb46fbd57a81e492a1354d4bd
@@@ -280,27 -280,23 +280,27 @@@ struct probe_resp 
        u8 data[0];
  };
  
 -struct ieee80211_if_ap {
 -      struct beacon_data __rcu *beacon;
 -      struct probe_resp __rcu *probe_resp;
 -
 -      struct list_head vlans;
 -
 +struct ps_data {
        /* yes, this looks ugly, but guarantees that we can later use
         * bitmap_empty :)
         * NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */
        u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
 -      struct sk_buff_head ps_bc_buf;
 +      struct sk_buff_head bc_buf;
        atomic_t num_sta_ps; /* number of stations in PS mode */
 -      atomic_t num_mcast_sta; /* number of stations receiving multicast */
        int dtim_count;
        bool dtim_bc_mc;
  };
  
 +struct ieee80211_if_ap {
 +      struct beacon_data __rcu *beacon;
 +      struct probe_resp __rcu *probe_resp;
 +
 +      struct list_head vlans;
 +
 +      struct ps_data ps;
 +      atomic_t num_mcast_sta; /* number of stations receiving multicast */
 +};
 +
  struct ieee80211_if_wds {
        struct sta_info *sta;
        u8 remote_addr[ETH_ALEN];
@@@ -320,6 -316,7 +320,6 @@@ struct mesh_stats 
        __u32 dropped_frames_ttl;       /* Not transmitted since mesh_ttl == 0*/
        __u32 dropped_frames_no_route;  /* Not transmitted, no route found */
        __u32 dropped_frames_congestion;/* Not forwarded due to congestion */
 -      atomic_t estab_plinks;
  };
  
  #define PREQ_Q_F_START                0x1
@@@ -381,9 -378,8 +381,9 @@@ struct ieee80211_mgd_auth_data 
        u8 key_len, key_idx;
        bool done;
  
 -      size_t ie_len;
 -      u8 ie[];
 +      u16 sae_trans, sae_status;
 +      size_t data_len;
 +      u8 data[];
  };
  
  struct ieee80211_mgd_assoc_data {
@@@ -437,6 -433,7 +437,6 @@@ struct ieee80211_if_managed 
        bool powersave; /* powersave requested for this iface */
        bool broken_ap; /* AP is broken -- turn off powersave */
        enum ieee80211_smps_mode req_smps, /* requested smps mode */
 -                               ap_smps, /* smps mode AP thinks we're in */
                                 driver_smps_mode; /* smps mode request */
  
        struct work_struct request_smps_work;
@@@ -602,7 -599,6 +602,7 @@@ struct ieee80211_if_mesh 
        int preq_queue_len;
        struct mesh_stats mshstats;
        struct mesh_config mshcfg;
 +      atomic_t estab_plinks;
        u32 mesh_seqnum;
        bool accepting_plinks;
        int num_gates;
                IEEE80211_MESH_SEC_SECURED = 0x2,
        } security;
        /* Extensible Synchronization Framework */
 -      struct ieee80211_mesh_sync_ops *sync_ops;
 +      const struct ieee80211_mesh_sync_ops *sync_ops;
        s64 sync_offset_clockdrift_max;
        spinlock_t sync_offset_lock;
        bool adjusting_tbtt;
@@@ -662,30 -658,6 +662,30 @@@ enum ieee80211_sdata_state_bits 
        SDATA_STATE_OFFCHANNEL,
  };
  
 +/**
 + * enum ieee80211_chanctx_mode - channel context configuration mode
 + *
 + * @IEEE80211_CHANCTX_SHARED: channel context may be used by
 + *    multiple interfaces
 + * @IEEE80211_CHANCTX_EXCLUSIVE: channel context can be used
 + *    only by a single interface. This can be used for example for
 + *    non-fixed channel IBSS.
 + */
 +enum ieee80211_chanctx_mode {
 +      IEEE80211_CHANCTX_SHARED,
 +      IEEE80211_CHANCTX_EXCLUSIVE
 +};
 +
 +struct ieee80211_chanctx {
 +      struct list_head list;
 +      struct rcu_head rcu_head;
 +
 +      enum ieee80211_chanctx_mode mode;
 +      int refcount;
 +
 +      struct ieee80211_chanctx_conf conf;
 +};
 +
  struct ieee80211_sub_if_data {
        struct list_head list;
  
  
        struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
  
 +      /* used to reconfigure hardware SM PS */
 +      struct work_struct recalc_smps;
 +
        struct work_struct work;
        struct sk_buff_head skb_queue;
  
        bool arp_filter_state;
  
 +      u8 needed_rx_chains;
 +      enum ieee80211_smps_mode smps_mode;
 +
        /*
         * AP this belongs to: self in AP mode and
         * corresponding AP in VLAN mode, NULL for
@@@ -783,21 -749,6 +783,21 @@@ struct ieee80211_sub_if_data *vif_to_sd
        return container_of(p, struct ieee80211_sub_if_data, vif);
  }
  
 +static inline enum ieee80211_band
 +ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
 +{
 +      enum ieee80211_band band = IEEE80211_BAND_2GHZ;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +
 +      rcu_read_lock();
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +      if (!WARN_ON(!chanctx_conf))
 +              band = chanctx_conf->channel->band;
 +      rcu_read_unlock();
 +
 +      return band;
 +}
 +
  enum sdata_queue_type {
        IEEE80211_SDATA_QUEUE_TYPE_FRAME        = 0,
        IEEE80211_SDATA_QUEUE_AGG_START         = 1,
@@@ -870,7 -821,6 +870,7 @@@ enum 
   * @SCAN_SUSPEND: Suspend the scan and go back to operating channel to
   *    send out data
   * @SCAN_RESUME: Resume the scan and scan the next channel
 + * @SCAN_ABORT: Abort the scan and go back to operating channel
   */
  enum mac80211_scan_state {
        SCAN_DECISION,
        SCAN_SEND_PROBE,
        SCAN_SUSPEND,
        SCAN_RESUME,
 +      SCAN_ABORT,
  };
  
  struct ieee80211_local {
  
        bool wiphy_ciphers_allocated;
  
 +      bool use_chanctx;
 +
        /* protects the aggregated multicast list and filter calls */
        spinlock_t filter_lock;
  
        /* used for uploading changed mc list */
        struct work_struct reconfig_filter;
  
 -      /* used to reconfigure hardware SM PS */
 -      struct work_struct recalc_smps;
 -
        /* aggregated multicast list */
        struct netdev_hw_addr_list mc_list;
  
        /* wowlan is enabled -- don't reconfig on resume */
        bool wowlan;
  
 +      /* number of RX chains the hardware has */
 +      u8 rx_chains;
 +
        int tx_headroom; /* required headroom for hardware/radiotap */
  
        /* Tasklet and skb queue to process calls from IRQ mode. All frames
        enum mac80211_scan_state next_scan_state;
        struct delayed_work scan_work;
        struct ieee80211_sub_if_data __rcu *scan_sdata;
 +      struct ieee80211_channel *csa_channel;
 +      /* For backward compatibility only -- do not use */
 +      struct ieee80211_channel *_oper_channel;
        enum nl80211_channel_type _oper_channel_type;
 -      struct ieee80211_channel *oper_channel, *csa_channel;
  
        /* Temporary remain-on-channel for off-channel operations */
        struct ieee80211_channel *tmp_channel;
        enum nl80211_channel_type tmp_channel_type;
  
 +      /* channel contexts */
 +      struct list_head chanctx_list;
 +      struct mutex chanctx_mtx;
 +
        /* SNMP counters */
        /* dot11CountersTable */
        u32 dot11TransmittedFragmentCount;
  
        /* virtual monitor interface */
        struct ieee80211_sub_if_data __rcu *monitor_sdata;
 +      struct ieee80211_channel *monitor_channel;
 +      enum nl80211_channel_type monitor_channel_type;
  };
  
  static inline struct ieee80211_sub_if_data *
@@@ -1194,8 -1133,6 +1194,8 @@@ struct ieee802_11_elems 
        u8 *wmm_param;
        struct ieee80211_ht_cap *ht_cap_elem;
        struct ieee80211_ht_operation *ht_operation;
 +      struct ieee80211_vht_cap *vht_cap_elem;
 +      struct ieee80211_vht_operation *vht_operation;
        struct ieee80211_meshconf_ie *mesh_config;
        u8 *mesh_id;
        u8 *peering;
@@@ -1377,6 -1314,8 +1377,8 @@@ netdev_tx_t ieee80211_monitor_start_xmi
                                         struct net_device *dev);
  netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                                       struct net_device *dev);
+ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
+                             struct sk_buff_head *skbs);
  
  /* HT */
  void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
@@@ -1422,13 -1361,6 +1424,13 @@@ void ieee80211_ba_session_work(struct w
  void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid);
  void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid);
  
 +u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs);
 +
 +/* VHT */
 +void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
 +                                       struct ieee80211_supported_band *sband,
 +                                       struct ieee80211_vht_cap *vht_cap_ie,
 +                                       struct ieee80211_sta_vht_cap *vht_cap);
  /* Spectrum management */
  void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
                                       struct ieee80211_mgmt *mgmt,
@@@ -1463,42 -1395,11 +1465,42 @@@ void mac80211_ev_michael_mic_failure(st
                                     gfp_t gfp);
  void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
                               bool bss_notify);
 -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
 +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
 +                  enum ieee80211_band band);
 +
 +void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 +                               struct sk_buff *skb, int tid,
 +                               enum ieee80211_band band);
  
 -void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
 -                        struct sk_buff *skb, int tid);
 -static void inline ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
 +static inline void
 +ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 +                        struct sk_buff *skb, int tid,
 +                        enum ieee80211_band band)
 +{
 +      rcu_read_lock();
 +      __ieee80211_tx_skb_tid_band(sdata, skb, tid, band);
 +      rcu_read_unlock();
 +}
 +
 +static inline void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
 +                                      struct sk_buff *skb, int tid)
 +{
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +
 +      rcu_read_lock();
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +      if (WARN_ON(!chanctx_conf)) {
 +              rcu_read_unlock();
 +              kfree_skb(skb);
 +              return;
 +      }
 +
 +      __ieee80211_tx_skb_tid_band(sdata, skb, tid,
 +                                  chanctx_conf->channel->band);
 +      rcu_read_unlock();
 +}
 +
 +static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
                                    struct sk_buff *skb)
  {
        /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
@@@ -1545,7 -1446,7 +1547,7 @@@ static inline void ieee80211_add_pendin
  }
  
  void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
 -                       u16 transaction, u16 auth_alg,
 +                       u16 transaction, u16 auth_alg, u16 status,
                         u8 *extra, size_t extra_len, const u8 *bssid,
                         const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
  void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
@@@ -1565,7 -1466,7 +1567,7 @@@ void ieee80211_send_probe_req(struct ie
                              const u8 *ssid, size_t ssid_len,
                              const u8 *ie, size_t ie_len,
                              u32 ratemask, bool directed, bool no_cck,
 -                            struct ieee80211_channel *channel);
 +                            struct ieee80211_channel *channel, bool scan);
  
  void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
                                  const size_t supp_rates_len,
@@@ -1575,7 -1476,7 +1577,7 @@@ u32 ieee80211_sta_get_rates(struct ieee
                            enum ieee80211_band band, u32 *basic_rates);
  int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
                             enum ieee80211_smps_mode smps_mode);
 -void ieee80211_recalc_smps(struct ieee80211_local *local);
 +void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
  
  size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
                          const u8 *ids, int n_ids, size_t offset);
@@@ -1596,19 -1497,21 +1598,19 @@@ int ieee80211_add_ext_srates_ie(struct 
                                enum ieee80211_band band);
  
  /* channel management */
 -enum ieee80211_chan_mode {
 -      CHAN_MODE_UNDEFINED,
 -      CHAN_MODE_HOPPING,
 -      CHAN_MODE_FIXED,
 -};
 -
 -enum ieee80211_chan_mode
 -ieee80211_get_channel_mode(struct ieee80211_local *local,
 -                         struct ieee80211_sub_if_data *ignore);
 -bool ieee80211_set_channel_type(struct ieee80211_local *local,
 -                              struct ieee80211_sub_if_data *sdata,
 -                              enum nl80211_channel_type chantype);
  enum nl80211_channel_type
  ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper);
  
 +int __must_check
 +ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
 +                        struct ieee80211_channel *channel,
 +                        enum nl80211_channel_type channel_type,
 +                        enum ieee80211_chanctx_mode mode);
 +void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
 +
 +void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
 +                                 struct ieee80211_chanctx *chanctx);
 +
  #ifdef CONFIG_MAC80211_NOINLINE
  #define debug_noinline noinline
  #else
diff --combined net/mac80211/main.c
index fd8345c200512ded20e1bf47e960599bbc042bc6,f57f597972f8833ccc5a74a433bfd2ddafe407dd..d6e43b08d6293a292ef92ea65e4d395d17d9b838
@@@ -93,21 -93,23 +93,21 @@@ static void ieee80211_reconfig_filter(s
        ieee80211_configure_filter(local);
  }
  
 -int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
 +static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
  {
        struct ieee80211_channel *chan;
 -      int ret = 0;
 +      u32 changed = 0;
        int power;
        enum nl80211_channel_type channel_type;
        u32 offchannel_flag;
  
 -      might_sleep();
 -
        offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
        if (local->scan_channel) {
                chan = local->scan_channel;
                /* If scanning on oper channel, use whatever channel-type
                 * is currently in use.
                 */
 -              if (chan == local->oper_channel)
 +              if (chan == local->_oper_channel)
                        channel_type = local->_oper_channel_type;
                else
                        channel_type = NL80211_CHAN_NO_HT;
                chan = local->tmp_channel;
                channel_type = local->tmp_channel_type;
        } else {
 -              chan = local->oper_channel;
 +              chan = local->_oper_channel;
                channel_type = local->_oper_channel_type;
        }
  
 -      if (chan != local->oper_channel ||
 +      if (chan != local->_oper_channel ||
            channel_type != local->_oper_channel_type)
                local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
        else
                local->hw.conf.power_level = power;
        }
  
 +      return changed;
 +}
 +
 +int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
 +{
 +      int ret = 0;
 +
 +      might_sleep();
 +
 +      if (!local->use_chanctx)
 +              changed |= ieee80211_hw_conf_chan(local);
 +      else
 +              changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL |
 +                           IEEE80211_CONF_CHANGE_POWER);
 +
        if (changed && local->open_count) {
                ret = drv_config(local, changed);
                /*
@@@ -372,6 -359,14 +372,6 @@@ void ieee80211_restart_hw(struct ieee80
  }
  EXPORT_SYMBOL(ieee80211_restart_hw);
  
 -static void ieee80211_recalc_smps_work(struct work_struct *work)
 -{
 -      struct ieee80211_local *local =
 -              container_of(work, struct ieee80211_local, recalc_smps);
 -
 -      ieee80211_recalc_smps(local);
 -}
 -
  #ifdef CONFIG_INET
  static int ieee80211_ifa_changed(struct notifier_block *nb,
                                 unsigned long data, void *arg)
@@@ -545,7 -540,6 +545,7 @@@ struct ieee80211_hw *ieee80211_alloc_hw
        struct ieee80211_local *local;
        int priv_size, i;
        struct wiphy *wiphy;
 +      bool use_chanctx;
  
        if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config ||
                    !ops->add_interface || !ops->remove_interface ||
        if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
                return NULL;
  
 +      /* check all or no channel context operations exist */
 +      i = !!ops->add_chanctx + !!ops->remove_chanctx +
 +          !!ops->change_chanctx + !!ops->assign_vif_chanctx +
 +          !!ops->unassign_vif_chanctx;
 +      if (WARN_ON(i != 0 && i != 5))
 +              return NULL;
 +      use_chanctx = i == 5;
 +
        /* Ensure 32-byte alignment of our private data and hw private data.
         * We use the wiphy priv data for both our ieee80211_local and for
         * the driver's private data
        if (ops->remain_on_channel)
                wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
  
 -      wiphy->features = NL80211_FEATURE_SK_TX_STATUS |
 -                        NL80211_FEATURE_HT_IBSS;
 +      wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
 +                         NL80211_FEATURE_SAE |
 +                         NL80211_FEATURE_HT_IBSS;
 +
 +      if (!ops->hw_scan)
 +              wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
 +                                 NL80211_FEATURE_AP_SCAN;
 +
  
        if (!ops->set_key)
                wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
  
        local->ops = ops;
 +      local->use_chanctx = use_chanctx;
  
        /* set up some defaults */
        local->hw.queues = 1;
        spin_lock_init(&local->filter_lock);
        spin_lock_init(&local->queue_stop_reason_lock);
  
 +      INIT_LIST_HEAD(&local->chanctx_list);
 +      mutex_init(&local->chanctx_mtx);
 +
        /*
         * The rx_skb_queue is only accessed from tasklets,
         * but other SKB queues are used from within IRQ
        INIT_WORK(&local->restart_work, ieee80211_restart_work);
  
        INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
 -      INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
        local->smps_mode = IEEE80211_SMPS_OFF;
  
        INIT_WORK(&local->dynamic_ps_enable_work,
@@@ -742,25 -719,6 +742,25 @@@ int ieee80211_register_hw(struct ieee80
        if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan)
                return -EINVAL;
  
 +      if (!local->use_chanctx) {
 +              for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
 +                      const struct ieee80211_iface_combination *comb;
 +
 +                      comb = &local->hw.wiphy->iface_combinations[i];
 +
 +                      if (comb->num_different_channels > 1)
 +                              return -EINVAL;
 +              }
 +      } else {
 +              /*
 +               * WDS is currently prohibited when channel contexts are used
 +               * because there's no clear definition of which channel WDS
 +               * type interfaces use
 +               */
 +              if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
 +                      return -EINVAL;
 +      }
 +
        /* Only HW csum features are currently compatible with mac80211 */
        feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                            NETIF_F_HW_CSUM;
        if (hw->max_report_rates == 0)
                hw->max_report_rates = hw->max_rates;
  
 +      local->rx_chains = 1;
 +
        /*
         * generic code guarantees at least one band,
         * set this very early because much code assumes
                sband = local->hw.wiphy->bands[band];
                if (!sband)
                        continue;
 -              if (!local->oper_channel) {
 +              if (!local->use_chanctx && !local->_oper_channel) {
                        /* init channel we're on */
                        local->hw.conf.channel =
 -                      local->oper_channel = &sband->channels[0];
 +                      local->_oper_channel = &sband->channels[0];
                        local->hw.conf.channel_type = NL80211_CHAN_NO_HT;
                }
 +              if (!local->monitor_channel) {
 +                      local->monitor_channel = &sband->channels[0];
 +                      local->monitor_channel_type = NL80211_CHAN_NO_HT;
 +              }
                channels += sband->n_channels;
  
                if (max_bitrates < sband->n_bitrates)
                        max_bitrates = sband->n_bitrates;
                supp_ht = supp_ht || sband->ht_cap.ht_supported;
                supp_vht = supp_vht || sband->vht_cap.vht_supported;
 +
 +              if (sband->ht_cap.ht_supported)
 +                      local->rx_chains =
 +                              max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs),
 +                                  local->rx_chains);
 +
 +              /* TODO: consider VHT for RX chains, hopefully it's the same */
        }
  
        local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
        hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
        hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
  
 -      /*
 -       * mac80211 doesn't support more than 1 channel, and also not more
 -       * than one IBSS interface
 -       */
 +      /* mac80211 doesn't support more than one IBSS interface right now */
        for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
                const struct ieee80211_iface_combination *c;
                int j;
  
                c = &hw->wiphy->iface_combinations[i];
  
 -              if (c->num_different_channels > 1)
 -                      return -EINVAL;
 -
                for (j = 0; j < c->n_limits; j++)
                        if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
                            c->limits[j].max > 1)
  
        if (supp_vht)
                local->scan_ies_len +=
 -                      2 + sizeof(struct ieee80211_vht_capabilities);
 +                      2 + sizeof(struct ieee80211_vht_cap);
  
        if (!local->ops->hw_scan) {
                /* For hw_scan, driver needs to set these up. */
                                local->hw.wiphy->cipher_suites,
                                sizeof(u32) * local->hw.wiphy->n_cipher_suites,
                                GFP_KERNEL);
-                       if (!suites)
-                               return -ENOMEM;
+                       if (!suites) {
+                               result = -ENOMEM;
+                               goto fail_wiphy_register;
+                       }
                        for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
                                u32 suite = local->hw.wiphy->cipher_suites[r];
                                if (suite == WLAN_CIPHER_SUITE_WEP40 ||
diff --combined net/mac80211/scan.c
index 13d23299e69605013d7dce3897816bea23a4d731,43e60b5a7546eba4fd6e5e18eb313b7526d4f5b1..8e9bb168b73bff0f1c50c44d5e835ffba4696ae1
@@@ -336,10 -336,6 +336,10 @@@ EXPORT_SYMBOL(ieee80211_scan_completed)
  
  static int ieee80211_start_sw_scan(struct ieee80211_local *local)
  {
 +      /* Software scan is not supported in multi-channel cases */
 +      if (local->use_chanctx)
 +              return -EOPNOTSUPP;
 +
        /*
         * Hardware/driver doesn't support hw_scan, so use software
         * scanning instead. First send a nullfunc frame with power save
@@@ -421,7 -417,7 +421,7 @@@ static void ieee80211_scan_state_send_p
                        local->scan_req->ie, local->scan_req->ie_len,
                        local->scan_req->rates[band], false,
                        local->scan_req->no_cck,
 -                      local->hw.conf.channel);
 +                      local->hw.conf.channel, true);
  
        /*
         * After sending probe requests, wait for probe responses
@@@ -466,7 -462,6 +466,7 @@@ static int __ieee80211_start_scan(struc
                        sizeof(*local->hw_scan_req) +
                        req->n_channels * sizeof(req->channels[0]);
                local->hw_scan_req->ie = ies;
 +              local->hw_scan_req->flags = req->flags;
  
                local->hw_scan_band = 0;
  
        if (local->ops->hw_scan) {
                __set_bit(SCAN_HW_SCANNING, &local->scanning);
        } else if ((req->n_channels == 1) &&
 -                 (req->channels[0] == local->oper_channel)) {
 +                 (req->channels[0] == local->_oper_channel)) {
                /*
                 * If we are scanning only on the operating channel
                 * then we do not need to stop normal activities
@@@ -567,7 -562,6 +567,7 @@@ static void ieee80211_scan_state_decisi
        unsigned long min_beacon_int = 0;
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_channel *next_chan;
 +      enum mac80211_scan_state next_scan_state;
  
        /*
         * check if at least one STA interface is associated,
                        usecs_to_jiffies(min_beacon_int * 1024) *
                        local->hw.conf.listen_interval);
  
 -      if (associated && (!tx_empty || bad_latency || listen_int_exceeded))
 -              local->next_scan_state = SCAN_SUSPEND;
 -      else
 -              local->next_scan_state = SCAN_SET_CHANNEL;
 +      if (associated && !tx_empty) {
 +              if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
 +                      next_scan_state = SCAN_ABORT;
 +              else
 +                      next_scan_state = SCAN_SUSPEND;
 +      } else if (associated && (bad_latency || listen_int_exceeded)) {
 +              next_scan_state = SCAN_SUSPEND;
 +      } else {
 +              next_scan_state = SCAN_SET_CHANNEL;
 +      }
 +
 +      local->next_scan_state = next_scan_state;
  
        *next_delay = 0;
  }
@@@ -808,9 -794,6 +808,9 @@@ void ieee80211_scan_work(struct work_st
                case SCAN_RESUME:
                        ieee80211_scan_state_resume(local, &next_delay);
                        break;
 +              case SCAN_ABORT:
 +                      aborted = true;
 +                      goto out_complete;
                }
        } while (next_delay == 0);
  
@@@ -934,7 -917,7 +934,7 @@@ int ieee80211_request_sched_scan_start(
                                       struct cfg80211_sched_scan_request *req)
  {
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_sched_scan_ies sched_scan_ies;
+       struct ieee80211_sched_scan_ies sched_scan_ies = {};
        int ret, i;
  
        mutex_lock(&local->mtx);
diff --combined net/mac80211/sta_info.c
index f7bb54f9ab72178c4b0ac57c432fe99c17f59b34,d2eb64e1235383e9b0e6aade66710f74ec1b4f3f..e9d57689c05fb076e4f9bfe293a8b6d98929a154
@@@ -98,7 -98,6 +98,7 @@@ static void free_sta_work(struct work_s
        struct tid_ampdu_tx *tid_tx;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
 +      struct ps_data *ps;
  
        /*
         * At this point, when being called as call_rcu callback,
         */
  
        if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
 -              BUG_ON(!sdata->bss);
 +              if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
 +                  sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 +                      ps = &sdata->bss->ps;
 +              else
 +                      return;
  
                clear_sta_flag(sta, WLAN_STA_PS_STA);
  
 -              atomic_dec(&sdata->bss->num_sta_ps);
 +              atomic_dec(&ps->num_sta_ps);
                sta_info_recalc_tim(sta);
        }
  
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
-               __skb_queue_purge(&sta->ps_tx_buf[ac]);
-               __skb_queue_purge(&sta->tx_filtered[ac]);
+               ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]);
+               ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]);
        }
  
  #ifdef CONFIG_MAC80211_MESH
                tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
                if (!tid_tx)
                        continue;
-               __skb_queue_purge(&tid_tx->pending);
+               ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);
                kfree(tid_tx);
        }
  
@@@ -507,22 -502,22 +507,22 @@@ int sta_info_insert(struct sta_info *st
        return err;
  }
  
 -static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
 +static inline void __bss_tim_set(u8 *tim, u16 id)
  {
        /*
         * This format has been mandated by the IEEE specifications,
         * so this line may not be changed to use the __set_bit() format.
         */
 -      bss->tim[aid / 8] |= (1 << (aid % 8));
 +      tim[id / 8] |= (1 << (id % 8));
  }
  
 -static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
 +static inline void __bss_tim_clear(u8 *tim, u16 id)
  {
        /*
         * This format has been mandated by the IEEE specifications,
         * so this line may not be changed to use the __clear_bit() format.
         */
 -      bss->tim[aid / 8] &= ~(1 << (aid % 8));
 +      tim[id / 8] &= ~(1 << (id % 8));
  }
  
  static unsigned long ieee80211_tids_for_ac(int ac)
  void sta_info_recalc_tim(struct sta_info *sta)
  {
        struct ieee80211_local *local = sta->local;
 -      struct ieee80211_if_ap *bss = sta->sdata->bss;
 +      struct ps_data *ps;
        unsigned long flags;
        bool indicate_tim = false;
        u8 ignore_for_tim = sta->sta.uapsd_queues;
        int ac;
 +      u16 id;
 +
 +      if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
 +          sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
 +              if (WARN_ON_ONCE(!sta->sdata->bss))
 +                      return;
  
 -      if (WARN_ON_ONCE(!sta->sdata->bss))
 +              ps = &sta->sdata->bss->ps;
 +              id = sta->sta.aid;
 +      } else {
                return;
 +      }
  
        /* No need to do anything if the driver does all */
        if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
        spin_lock_irqsave(&local->tim_lock, flags);
  
        if (indicate_tim)
 -              __bss_tim_set(bss, sta->sta.aid);
 +              __bss_tim_set(ps->tim, id);
        else
 -              __bss_tim_clear(bss, sta->sta.aid);
 +              __bss_tim_clear(ps->tim, id);
  
        if (local->ops->set_tim) {
                local->tim_in_locked_section = true;
@@@ -907,8 -893,8 +907,8 @@@ void ieee80211_sta_expire(struct ieee80
                        continue;
  
                if (time_after(jiffies, sta->last_rx + exp_time)) {
 -                      ibss_dbg(sdata, "expiring inactive STA %pM\n",
 -                               sta->sta.addr);
 +                      sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
 +                              sta->sta.addr);
                        WARN_ON(__sta_info_destroy(sta));
                }
        }
@@@ -962,17 -948,10 +962,17 @@@ static void clear_sta_ps_flags(void *_s
  {
        struct sta_info *sta = _sta;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 +      struct ps_data *ps;
 +
 +      if (sdata->vif.type == NL80211_IFTYPE_AP ||
 +          sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 +              ps = &sdata->bss->ps;
 +      else
 +              return;
  
        clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
        if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
 -              atomic_dec(&sdata->bss->num_sta_ps);
 +              atomic_dec(&ps->num_sta_ps);
  }
  
  /* powersave support code */
@@@ -982,6 -961,7 +982,7 @@@ void ieee80211_sta_ps_deliver_wakeup(st
        struct ieee80211_local *local = sdata->local;
        struct sk_buff_head pending;
        int filtered = 0, buffered = 0, ac;
+       unsigned long flags;
  
        clear_sta_flag(sta, WLAN_STA_SP);
  
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                int count = skb_queue_len(&pending), tmp;
  
+               spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags);
                skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending);
+               spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags);
                tmp = skb_queue_len(&pending);
                filtered += tmp - count;
                count = tmp;
  
+               spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags);
                skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending);
+               spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags);
                tmp = skb_queue_len(&pending);
                buffered += tmp - count;
        }
@@@ -1029,7 -1013,6 +1034,7 @@@ static void ieee80211_send_null_respons
        __le16 fc;
        bool qos = test_sta_flag(sta, WLAN_STA_WME);
        struct ieee80211_tx_info *info;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
  
        if (qos) {
                fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
  
        drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
  
 -      ieee80211_xmit(sdata, skb);
 +      rcu_read_lock();
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +      if (WARN_ON(!chanctx_conf)) {
 +              rcu_read_unlock();
 +              kfree_skb(skb);
 +              return;
 +      }
 +
 +      ieee80211_xmit(sdata, skb, chanctx_conf->channel->band);
 +      rcu_read_unlock();
  }
  
  static void
diff --combined net/mac80211/status.c
index 21fa5c72ea143b35617a281a9f2ee5ca0393f12d,101eb88a2b78563965d4477a815bf8069d586db8..248247877eb60adbbac7d2f6316a67a9dbcbabea
@@@ -189,31 -189,30 +189,31 @@@ static void ieee80211_frame_acked(struc
        }
  
        if (ieee80211_is_action(mgmt->frame_control) &&
 -          sdata->vif.type == NL80211_IFTYPE_STATION &&
            mgmt->u.action.category == WLAN_CATEGORY_HT &&
 -          mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
 +          mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
 +          sdata->vif.type == NL80211_IFTYPE_STATION &&
 +          ieee80211_sdata_running(sdata)) {
                /*
                 * This update looks racy, but isn't -- if we come
                 * here we've definitely got a station that we're
                 * talking to, and on a managed interface that can
                 * only be the AP. And the only other place updating
 -               * this variable is before we're associated.
 +               * this variable in managed mode is before association.
                 */
                switch (mgmt->u.action.u.ht_smps.smps_control) {
                case WLAN_HT_SMPS_CONTROL_DYNAMIC:
 -                      sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC;
 +                      sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_STATIC:
 -                      sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC;
 +                      sdata->smps_mode = IEEE80211_SMPS_STATIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_DISABLED:
                default: /* shouldn't happen since we don't send that */
 -                      sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF;
 +                      sdata->smps_mode = IEEE80211_SMPS_OFF;
                        break;
                }
  
 -              ieee80211_queue_work(&local->hw, &local->recalc_smps);
 +              ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
        }
  }
  
@@@ -669,3 -668,12 +669,12 @@@ void ieee80211_free_txskb(struct ieee80
        dev_kfree_skb_any(skb);
  }
  EXPORT_SYMBOL(ieee80211_free_txskb);
+ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
+                             struct sk_buff_head *skbs)
+ {
+       struct sk_buff *skb;
+       while ((skb = __skb_dequeue(skbs)))
+               ieee80211_free_txskb(hw, skb);
+ }
diff --combined net/mac80211/tx.c
index 065f81cb5618389801abb9c4fb5fa9b4bb01250d,b858ebe41fdac2f20326cfd58fb37840270fcaa1..6f1981816dc5a21f51b96089e3c3e75cce60a1f6
@@@ -324,20 -324,22 +324,20 @@@ static void purge_old_ps_buffers(struc
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
  
 -      /*
 -       * virtual interfaces are protected by RCU
 -       */
 -      rcu_read_lock();
 -
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 -              struct ieee80211_if_ap *ap;
 -              if (sdata->vif.type != NL80211_IFTYPE_AP)
 +              struct ps_data *ps;
 +
 +              if (sdata->vif.type == NL80211_IFTYPE_AP)
 +                      ps = &sdata->u.ap.ps;
 +              else
                        continue;
 -              ap = &sdata->u.ap;
 -              skb = skb_dequeue(&ap->ps_bc_buf);
 +
 +              skb = skb_dequeue(&ps->bc_buf);
                if (skb) {
                        purged++;
                        dev_kfree_skb(skb);
                }
 -              total += skb_queue_len(&ap->ps_bc_buf);
 +              total += skb_queue_len(&ps->bc_buf);
        }
  
        /*
                }
        }
  
 -      rcu_read_unlock();
 -
        local->total_ps_buffered = total;
        ps_dbg_hw(&local->hw, "PS buffers full - purged %d frames\n", purged);
  }
@@@ -367,7 -371,6 +367,7 @@@ ieee80211_tx_h_multicast_ps_buf(struct 
  {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
 +      struct ps_data *ps;
  
        /*
         * broadcast/multicast frame
         * This is done either by the hardware or us.
         */
  
 -      /* powersaving STAs only in AP/VLAN mode */
 -      if (!tx->sdata->bss)
 +      /* powersaving STAs currently only in AP/VLAN mode */
 +      if (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
 +          tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
 +              if (!tx->sdata->bss)
 +                      return TX_CONTINUE;
 +
 +              ps = &tx->sdata->bss->ps;
 +      } else {
                return TX_CONTINUE;
 +      }
 +
  
        /* no buffering for ordered frames */
        if (ieee80211_has_order(hdr->frame_control))
                return TX_CONTINUE;
  
        /* no stations in PS mode */
 -      if (!atomic_read(&tx->sdata->bss->num_sta_ps))
 +      if (!atomic_read(&ps->num_sta_ps))
                return TX_CONTINUE;
  
        info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
        if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
                purge_old_ps_buffers(tx->local);
  
 -      if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) {
 +      if (skb_queue_len(&ps->bc_buf) >= AP_MAX_BC_BUFFER) {
                ps_dbg(tx->sdata,
                       "BC TX buffer full - dropping the oldest frame\n");
 -              dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
 +              dev_kfree_skb(skb_dequeue(&ps->bc_buf));
        } else
                tx->local->total_ps_buffered++;
  
 -      skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
 +      skb_queue_tail(&ps->bc_buf, tx->skb);
  
        return TX_QUEUED;
  }
@@@ -956,6 -951,7 +956,6 @@@ ieee80211_tx_h_fragment(struct ieee8021
        fragnum = 0;
  
        skb_queue_walk(&tx->skbs, skb) {
 -              int next_len;
                const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
  
                hdr = (void *)skb->data;
                        info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
                } else {
                        hdr->frame_control &= ~morefrags;
 -                      next_len = 0;
                }
                hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG);
                fragnum++;
@@@ -1361,7 -1358,7 +1361,7 @@@ static int invoke_tx_handlers(struct ie
                if (tx->skb)
                        ieee80211_free_txskb(&tx->local->hw, tx->skb);
                else
-                       __skb_queue_purge(&tx->skbs);
+                       ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);
                return -1;
        } else if (unlikely(res == TX_QUEUED)) {
                I802_DEBUG_INC(tx->local->tx_handlers_queued);
   * Returns false if the frame couldn't be transmitted but was queued instead.
   */
  static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
 -                       struct sk_buff *skb, bool txpending)
 +                       struct sk_buff *skb, bool txpending,
 +                       enum ieee80211_band band)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_data tx;
                return true;
        }
  
 -      rcu_read_lock();
 -
        /* initialises tx */
        led_len = skb->len;
        res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
  
        if (unlikely(res_prepare == TX_DROP)) {
                ieee80211_free_txskb(&local->hw, skb);
 -              goto out;
 +              return true;
        } else if (unlikely(res_prepare == TX_QUEUED)) {
 -              goto out;
 +              return true;
        }
  
 -      info->band = local->hw.conf.channel->band;
 +      info->band = band;
  
        /* set up hw_queue value early */
        if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
        if (!invoke_tx_handlers(&tx))
                result = __ieee80211_tx(local, &tx.skbs, led_len,
                                        tx.sta, txpending);
 - out:
 -      rcu_read_unlock();
 +
        return result;
  }
  
@@@ -1447,8 -1446,7 +1447,8 @@@ static int ieee80211_skb_resize(struct 
        return 0;
  }
  
 -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
 +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
 +                  enum ieee80211_band band)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        int headroom;
        bool may_encrypt;
  
 -      rcu_read_lock();
 -
        may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT);
  
        headroom = local->tx_headroom;
  
        if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) {
                ieee80211_free_txskb(&local->hw, skb);
 -              rcu_read_unlock();
                return;
        }
  
            !is_multicast_ether_addr(hdr->addr1) &&
            mesh_nexthop_resolve(skb, sdata)) {
                /* skb queued: don't free */
 -              rcu_read_unlock();
                return;
        }
  
        ieee80211_set_qos_hdr(sdata, skb);
 -      ieee80211_tx(sdata, skb, false);
 -      rcu_read_unlock();
 +      ieee80211_tx(sdata, skb, false, band);
  }
  
  static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@@ -1571,8 -1574,7 +1571,8 @@@ netdev_tx_t ieee80211_monitor_start_xmi
                                         struct net_device *dev)
  {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 -      struct ieee80211_channel *chan = local->hw.conf.channel;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +      struct ieee80211_channel *chan;
        struct ieee80211_radiotap_header *prthdr =
                (struct ieee80211_radiotap_header *)skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        u16 len_rthdr;
        int hdrlen;
  
 -      /*
 -       * Frame injection is not allowed if beaconing is not allowed
 -       * or if we need radar detection. Beaconing is usually not allowed when
 -       * the mode or operation (Adhoc, AP, Mesh) does not support DFS.
 -       * Passive scan is also used in world regulatory domains where
 -       * your country is not known and as such it should be treated as
 -       * NO TX unless the channel is explicitly allowed in which case
 -       * your current regulatory domain would not have the passive scan
 -       * flag.
 -       *
 -       * Since AP mode uses monitor interfaces to inject/TX management
 -       * frames we can make AP mode the exception to this rule once it
 -       * supports radar detection as its implementation can deal with
 -       * radar detection by itself. We can do that later by adding a
 -       * monitor flag interfaces used for AP support.
 -       */
 -      if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
 -           IEEE80211_CHAN_PASSIVE_SCAN)))
 -              goto fail;
 -
        /* check for not even having the fixed radiotap header part */
        if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
                goto fail; /* too short to be possibly valid */
                }
        }
  
 -      ieee80211_xmit(sdata, skb);
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +      if (!chanctx_conf) {
 +              tmp_sdata = rcu_dereference(local->monitor_sdata);
 +              if (tmp_sdata)
 +                      chanctx_conf =
 +                              rcu_dereference(tmp_sdata->vif.chanctx_conf);
 +      }
 +      if (!chanctx_conf)
 +              goto fail_rcu;
 +
 +      chan = chanctx_conf->channel;
 +
 +      /*
 +       * Frame injection is not allowed if beaconing is not allowed
 +       * or if we need radar detection. Beaconing is usually not allowed when
 +       * the mode or operation (Adhoc, AP, Mesh) does not support DFS.
 +       * Passive scan is also used in world regulatory domains where
 +       * your country is not known and as such it should be treated as
 +       * NO TX unless the channel is explicitly allowed in which case
 +       * your current regulatory domain would not have the passive scan
 +       * flag.
 +       *
 +       * Since AP mode uses monitor interfaces to inject/TX management
 +       * frames we can make AP mode the exception to this rule once it
 +       * supports radar detection as its implementation can deal with
 +       * radar detection by itself. We can do that later by adding a
 +       * monitor flag interfaces used for AP support.
 +       */
 +      if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
 +                          IEEE80211_CHAN_PASSIVE_SCAN)))
 +              goto fail_rcu;
 +
 +      ieee80211_xmit(sdata, skb, chan->band);
        rcu_read_unlock();
  
        return NETDEV_TX_OK;
  
 +fail_rcu:
 +      rcu_read_unlock();
  fail:
        dev_kfree_skb(skb);
        return NETDEV_TX_OK; /* meaning, we dealt with the skb */
@@@ -1746,9 -1734,6 +1746,9 @@@ netdev_tx_t ieee80211_subif_start_xmit(
        bool multicast;
        u32 info_flags = 0;
        u16 info_id = 0;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +      struct ieee80211_sub_if_data *ap_sdata;
 +      enum ieee80211_band band;
  
        if (unlikely(skb->len < ETH_HLEN))
                goto fail;
        ethertype = (skb->data[12] << 8) | skb->data[13];
        fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
  
 +      rcu_read_lock();
 +
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP_VLAN:
 -              rcu_read_lock();
                sta = rcu_dereference(sdata->u.vlan.sta);
                if (sta) {
                        fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
                        authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
                        wme_sta = test_sta_flag(sta, WLAN_STA_WME);
                }
 -              rcu_read_unlock();
 +              ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
 +                                      u.ap);
 +              chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
 +              if (!chanctx_conf)
 +                      goto fail_rcu;
 +              band = chanctx_conf->channel->band;
                if (sta)
                        break;
                /* fall through */
                memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
                memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
                hdrlen = 24;
 +              if (sdata->vif.type == NL80211_IFTYPE_AP)
 +                      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +              if (!chanctx_conf)
 +                      goto fail_rcu;
 +              band = chanctx_conf->channel->band;
                break;
        case NL80211_IFTYPE_WDS:
                fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
                memcpy(hdr.addr3, skb->data, ETH_ALEN);
                memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
                hdrlen = 30;
 +              /*
 +               * This is the exception! WDS style interfaces are prohibited
 +               * when channel contexts are in used so this must be valid
 +               */
 +              band = local->hw.conf.channel->band;
                break;
  #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
                        /* Do not send frames with mesh_ttl == 0 */
                        sdata->u.mesh.mshstats.dropped_frames_ttl++;
 -                      goto fail;
 +                      goto fail_rcu;
                }
 -              rcu_read_lock();
 +
                if (!is_multicast_ether_addr(skb->data)) {
                        mpath = mesh_path_lookup(skb->data, sdata);
                        if (!mpath)
                    !(mppath && !ether_addr_equal(mppath->mpp, skb->data))) {
                        hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
                                        skb->data, skb->data + ETH_ALEN);
 -                      rcu_read_unlock();
                        meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
                                        sdata, NULL, NULL);
                } else {
                                mesh_da = mppath->mpp;
                        else if (mpath)
                                mesh_da = mpath->dst;
 -                      rcu_read_unlock();
  
                        hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
                                        mesh_da, sdata->vif.addr);
                                                        skb->data + ETH_ALEN);
  
                }
 +              chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +              if (!chanctx_conf)
 +                      goto fail_rcu;
 +              band = chanctx_conf->channel->band;
                break;
  #endif
        case NL80211_IFTYPE_STATION:
                if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
                        bool tdls_peer = false;
  
 -                      rcu_read_lock();
                        sta = sta_info_get(sdata, skb->data);
                        if (sta) {
                                authorized = test_sta_flag(sta,
                                tdls_auth = test_sta_flag(sta,
                                                WLAN_STA_TDLS_PEER_AUTH);
                        }
 -                      rcu_read_unlock();
  
                        /*
                         * If the TDLS link is enabled, send everything
                if (tdls_direct) {
                        /* link during setup - throw out frames to peer */
                        if (!tdls_auth)
 -                              goto fail;
 +                              goto fail_rcu;
  
                        /* DA SA BSSID */
                        memcpy(hdr.addr1, skb->data, ETH_ALEN);
                        memcpy(hdr.addr3, skb->data, ETH_ALEN);
                        hdrlen = 24;
                }
 +              chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +              if (!chanctx_conf)
 +                      goto fail_rcu;
 +              band = chanctx_conf->channel->band;
                break;
        case NL80211_IFTYPE_ADHOC:
                /* DA SA BSSID */
                memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
                memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
                hdrlen = 24;
 +              chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +              if (!chanctx_conf)
 +                      goto fail_rcu;
 +              band = chanctx_conf->channel->band;
                break;
        default:
 -              goto fail;
 +              goto fail_rcu;
        }
  
        /*
         */
        multicast = is_multicast_ether_addr(hdr.addr1);
        if (!multicast) {
 -              rcu_read_lock();
                sta = sta_info_get(sdata, hdr.addr1);
                if (sta) {
                        authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
                        wme_sta = test_sta_flag(sta, WLAN_STA_WME);
                }
 -              rcu_read_unlock();
        }
  
        /* For mesh, the use of the QoS header is mandatory */
  
                I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
  
 -              goto fail;
 +              goto fail_rcu;
        }
  
        if (unlikely(!multicast && skb->sk &&
                kfree_skb(tmp_skb);
  
                if (!skb)
 -                      goto fail;
 +                      goto fail_rcu;
        }
  
        hdr.frame_control = fc;
                head_need = max_t(int, 0, head_need);
                if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
                        ieee80211_free_txskb(&local->hw, skb);
 -                      return NETDEV_TX_OK;
 +                      goto fail_rcu;
                }
        }
  
        info->flags = info_flags;
        info->ack_frame_id = info_id;
  
 -      ieee80211_xmit(sdata, skb);
 +      ieee80211_xmit(sdata, skb, band);
 +      rcu_read_unlock();
  
        return NETDEV_TX_OK;
  
 + fail_rcu:
 +      rcu_read_unlock();
   fail:
        dev_kfree_skb(skb);
        return NETDEV_TX_OK;
   */
  void ieee80211_clear_tx_pending(struct ieee80211_local *local)
  {
+       struct sk_buff *skb;
        int i;
  
-       for (i = 0; i < local->hw.queues; i++)
-               skb_queue_purge(&local->pending[i]);
+       for (i = 0; i < local->hw.queues; i++) {
+               while ((skb = skb_dequeue(&local->pending[i])) != NULL)
+                       ieee80211_free_txskb(&local->hw, skb);
+       }
  }
  
  /*
@@@ -2179,18 -2142,11 +2182,18 @@@ static bool ieee80211_tx_pending_skb(st
        struct sta_info *sta;
        struct ieee80211_hdr *hdr;
        bool result;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
  
        sdata = vif_to_sdata(info->control.vif);
  
        if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
 -              result = ieee80211_tx(sdata, skb, true);
 +              chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +              if (unlikely(!chanctx_conf)) {
 +                      dev_kfree_skb(skb);
 +                      return true;
 +              }
 +              result = ieee80211_tx(sdata, skb, true,
 +                                    chanctx_conf->channel->band);
        } else {
                struct sk_buff_head skbs;
  
@@@ -2258,8 -2214,9 +2261,8 @@@ void ieee80211_tx_pending(unsigned lon
  /* functions for drivers to get certain frames */
  
  static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
 -                                   struct ieee80211_if_ap *bss,
 -                                   struct sk_buff *skb,
 -                                   struct beacon_data *beacon)
 +                                   struct ps_data *ps,
 +                                   struct sk_buff *skb)
  {
        u8 *pos, *tim;
        int aid0 = 0;
  
        /* Generate bitmap for TIM only if there are any STAs in power save
         * mode. */
 -      if (atomic_read(&bss->num_sta_ps) > 0)
 +      if (atomic_read(&ps->num_sta_ps) > 0)
                /* in the hope that this is faster than
                 * checking byte-for-byte */
 -              have_bits = !bitmap_empty((unsigned long*)bss->tim,
 +              have_bits = !bitmap_empty((unsigned long*)ps->tim,
                                          IEEE80211_MAX_AID+1);
  
 -      if (bss->dtim_count == 0)
 -              bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
 +      if (ps->dtim_count == 0)
 +              ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
        else
 -              bss->dtim_count--;
 +              ps->dtim_count--;
  
        tim = pos = (u8 *) skb_put(skb, 6);
        *pos++ = WLAN_EID_TIM;
        *pos++ = 4;
 -      *pos++ = bss->dtim_count;
 +      *pos++ = ps->dtim_count;
        *pos++ = sdata->vif.bss_conf.dtim_period;
  
 -      if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
 +      if (ps->dtim_count == 0 && !skb_queue_empty(&ps->bc_buf))
                aid0 = 1;
  
 -      bss->dtim_bc_mc = aid0 == 1;
 +      ps->dtim_bc_mc = aid0 == 1;
  
        if (have_bits) {
                /* Find largest even number N1 so that bits numbered 1 through
                 * (N2 + 1) x 8 through 2007 are 0. */
                n1 = 0;
                for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
 -                      if (bss->tim[i]) {
 +                      if (ps->tim[i]) {
                                n1 = i & 0xfe;
                                break;
                        }
                }
                n2 = n1;
                for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
 -                      if (bss->tim[i]) {
 +                      if (ps->tim[i]) {
                                n2 = i;
                                break;
                        }
                *pos++ = n1 | aid0;
                /* Part Virt Bitmap */
                skb_put(skb, n2 - n1);
 -              memcpy(pos, bss->tim + n1, n2 - n1 + 1);
 +              memcpy(pos, ps->tim + n1, n2 - n1 + 1);
  
                tim[1] = n2 - n1 + 4;
        } else {
@@@ -2329,16 -2286,16 +2332,16 @@@ struct sk_buff *ieee80211_beacon_get_ti
        struct sk_buff *skb = NULL;
        struct ieee80211_tx_info *info;
        struct ieee80211_sub_if_data *sdata = NULL;
 -      struct ieee80211_if_ap *ap = NULL;
 -      struct beacon_data *beacon;
 -      enum ieee80211_band band = local->oper_channel->band;
 +      enum ieee80211_band band;
        struct ieee80211_tx_rate_control txrc;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
  
        rcu_read_lock();
  
        sdata = vif_to_sdata(vif);
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
  
 -      if (!ieee80211_sdata_running(sdata))
 +      if (!ieee80211_sdata_running(sdata) || !chanctx_conf)
                goto out;
  
        if (tim_offset)
                *tim_length = 0;
  
        if (sdata->vif.type == NL80211_IFTYPE_AP) {
 -              ap = &sdata->u.ap;
 -              beacon = rcu_dereference(ap->beacon);
 +              struct ieee80211_if_ap *ap = &sdata->u.ap;
 +              struct beacon_data *beacon = rcu_dereference(ap->beacon);
 +
                if (beacon) {
                        /*
                         * headroom, head length,
                         * of the tim bitmap in mac80211 and the driver.
                         */
                        if (local->tim_in_locked_section) {
 -                              ieee80211_beacon_add_tim(sdata, ap, skb,
 -                                                       beacon);
 +                              ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
                        } else {
                                unsigned long flags;
  
                                spin_lock_irqsave(&local->tim_lock, flags);
 -                              ieee80211_beacon_add_tim(sdata, ap, skb,
 -                                                       beacon);
 +                              ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
                                spin_unlock_irqrestore(&local->tim_lock, flags);
                        }
  
                *pos++ = WLAN_EID_SSID;
                *pos++ = 0x0;
  
 +              band = chanctx_conf->channel->band;
 +
                if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
                    mesh_add_ds_params_ie(skb, sdata) ||
                    ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
                goto out;
        }
  
 +      band = chanctx_conf->channel->band;
 +
        info = IEEE80211_SKB_CB(skb);
  
        info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@@ -2702,40 -2656,29 +2705,40 @@@ ieee80211_get_buffered_bc(struct ieee80
        struct sk_buff *skb = NULL;
        struct ieee80211_tx_data tx;
        struct ieee80211_sub_if_data *sdata;
 -      struct ieee80211_if_ap *bss = NULL;
 -      struct beacon_data *beacon;
 +      struct ps_data *ps;
        struct ieee80211_tx_info *info;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
  
        sdata = vif_to_sdata(vif);
 -      bss = &sdata->u.ap;
  
        rcu_read_lock();
 -      beacon = rcu_dereference(bss->beacon);
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
  
 -      if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head)
 +      if (!chanctx_conf)
                goto out;
  
 -      if (bss->dtim_count != 0 || !bss->dtim_bc_mc)
 +      if (sdata->vif.type == NL80211_IFTYPE_AP) {
 +              struct beacon_data *beacon =
 +                              rcu_dereference(sdata->u.ap.beacon);
 +
 +              if (!beacon || !beacon->head)
 +                      goto out;
 +
 +              ps = &sdata->u.ap.ps;
 +      } else {
 +              goto out;
 +      }
 +
 +      if (ps->dtim_count != 0 || !ps->dtim_bc_mc)
                goto out; /* send buffered bc/mc only after DTIM beacon */
  
        while (1) {
 -              skb = skb_dequeue(&bss->ps_bc_buf);
 +              skb = skb_dequeue(&ps->bc_buf);
                if (!skb)
                        goto out;
                local->total_ps_buffered--;
  
 -              if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
 +              if (!skb_queue_empty(&ps->bc_buf) && skb->len >= 2) {
                        struct ieee80211_hdr *hdr =
                                (struct ieee80211_hdr *) skb->data;
                        /* more buffered multicast/broadcast frames ==> set
        info = IEEE80211_SKB_CB(skb);
  
        tx.flags |= IEEE80211_TX_PS_BUFFERED;
 -      info->band = local->oper_channel->band;
 +      info->band = chanctx_conf->channel->band;
  
        if (invoke_tx_handlers(&tx))
                skb = NULL;
  }
  EXPORT_SYMBOL(ieee80211_get_buffered_bc);
  
 -void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
 -                        struct sk_buff *skb, int tid)
 +void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 +                               struct sk_buff *skb, int tid,
 +                               enum ieee80211_band band)
  {
        int ac = ieee802_1d_to_ac[tid & 7];
  
         * requirements are that we do not come into tx with bhs on.
         */
        local_bh_disable();
 -      ieee80211_xmit(sdata, skb);
 +      ieee80211_xmit(sdata, skb, band);
        local_bh_enable();
  }
diff --combined net/mac80211/util.c
index 9556391b05d713e3bc9dec97bd482948d72e0616,0151ae33c4cd448e4812b0e9255d47898bf595da..acbb8c9bae2a352455bbe7de37fa970ceb819bc1
@@@ -769,18 -769,6 +769,18 @@@ u32 ieee802_11_parse_elems_crc(u8 *star
                        else
                                elem_parse_failed = true;
                        break;
 +              case WLAN_EID_VHT_CAPABILITY:
 +                      if (elen >= sizeof(struct ieee80211_vht_cap))
 +                              elems->vht_cap_elem = (void *)pos;
 +                      else
 +                              elem_parse_failed = true;
 +                      break;
 +              case WLAN_EID_VHT_OPERATION:
 +                      if (elen >= sizeof(struct ieee80211_vht_operation))
 +                              elems->vht_operation = (void *)pos;
 +                      else
 +                              elem_parse_failed = true;
 +                      break;
                case WLAN_EID_MESH_ID:
                        elems->mesh_id = pos;
                        elems->mesh_id_len = elen;
                if (elem_parse_failed)
                        elems->parse_error = true;
                else
 -                      set_bit(id, seen_elems);
 +                      __set_bit(id, seen_elems);
  
                left -= elen;
                pos += elen;
@@@ -872,7 -860,6 +872,7 @@@ void ieee80211_set_wmm_default(struct i
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_queue_params qparam;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
        int ac;
        bool use_11b, enable_qos;
        int aCWmin, aCWmax;
  
        memset(&qparam, 0, sizeof(qparam));
  
 -      use_11b = (local->oper_channel->band == IEEE80211_BAND_2GHZ) &&
 +      rcu_read_lock();
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +      use_11b = (chanctx_conf &&
 +                 chanctx_conf->channel->band == IEEE80211_BAND_2GHZ) &&
                 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
 +      rcu_read_unlock();
  
        /*
         * By default disable QoS in STA mode for old access points, which do
@@@ -969,7 -952,7 +969,7 @@@ void ieee80211_sta_def_wmm_params(struc
                                  const size_t supp_rates_len,
                                  const u8 *supp_rates)
  {
 -      struct ieee80211_local *local = sdata->local;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
        int i, have_higher_than_11mbit = 0;
  
        /* cf. IEEE 802.11 9.2.12 */
                if ((supp_rates[i] & 0x7f) * 5 > 110)
                        have_higher_than_11mbit = 1;
  
 -      if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
 +      rcu_read_lock();
 +      chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +
 +      if (chanctx_conf &&
 +          chanctx_conf->channel->band == IEEE80211_BAND_2GHZ &&
            have_higher_than_11mbit)
                sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
        else
                sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
 +      rcu_read_unlock();
  
        ieee80211_set_wmm_default(sdata, true);
  }
@@@ -1018,7 -996,7 +1018,7 @@@ u32 ieee80211_mandatory_rates(struct ie
  }
  
  void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
 -                       u16 transaction, u16 auth_alg,
 +                       u16 transaction, u16 auth_alg, u16 status,
                         u8 *extra, size_t extra_len, const u8 *da,
                         const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx)
  {
        memcpy(mgmt->bssid, bssid, ETH_ALEN);
        mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
        mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
 -      mgmt->u.auth.status_code = cpu_to_le16(0);
 +      mgmt->u.auth.status_code = cpu_to_le16(status);
        if (extra)
                memcpy(skb_put(skb, extra_len), extra, extra_len);
  
@@@ -1256,7 -1234,7 +1256,7 @@@ void ieee80211_send_probe_req(struct ie
                              const u8 *ssid, size_t ssid_len,
                              const u8 *ie, size_t ie_len,
                              u32 ratemask, bool directed, bool no_cck,
 -                            struct ieee80211_channel *channel)
 +                            struct ieee80211_channel *channel, bool scan)
  {
        struct sk_buff *skb;
  
                if (no_cck)
                        IEEE80211_SKB_CB(skb)->flags |=
                                IEEE80211_TX_CTL_NO_CCK_RATE;
 -              ieee80211_tx_skb(sdata, skb);
 +              if (scan)
 +                      ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
 +              else
 +                      ieee80211_tx_skb(sdata, skb);
        }
  }
  
@@@ -1333,7 -1308,6 +1333,7 @@@ int ieee80211_reconfig(struct ieee80211
  {
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_sub_if_data *sdata;
 +      struct ieee80211_chanctx *ctx;
        struct sta_info *sta;
        int res, i;
  
                        res = drv_add_interface(local, sdata);
        }
  
 +      /* add channel contexts */
 +      mutex_lock(&local->chanctx_mtx);
 +      list_for_each_entry(ctx, &local->chanctx_list, list)
 +              WARN_ON(drv_add_chanctx(local, ctx));
 +      mutex_unlock(&local->chanctx_mtx);
 +
        /* add STAs back */
        mutex_lock(&local->sta_mtx);
        list_for_each_entry(sta, &local->sta_list, list) {
  
        /* Finally also reconfigure all the BSS information */
        list_for_each_entry(sdata, &local->interfaces, list) {
 +              struct ieee80211_chanctx_conf *ctx_conf;
                u32 changed;
  
                if (!ieee80211_sdata_running(sdata))
                        continue;
  
 +              mutex_lock(&local->chanctx_mtx);
 +              ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
 +                              lockdep_is_held(&local->chanctx_mtx));
 +              if (ctx_conf) {
 +                      ctx = container_of(ctx_conf, struct ieee80211_chanctx,
 +                                         conf);
 +                      drv_assign_vif_chanctx(local, sdata, ctx);
 +              }
 +              mutex_unlock(&local->chanctx_mtx);
 +
                /* common change flags for all interface types */
                changed = BSS_CHANGED_ERP_CTS_PROT |
                          BSS_CHANGED_ERP_PREAMBLE |
                list_for_each_entry(sdata, &local->interfaces, list) {
                        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                                continue;
+                       if (!sdata->u.mgd.associated)
+                               continue;
  
                        ieee80211_send_nullfunc(local, sdata, 0);
                }
@@@ -1658,24 -1617,68 +1660,24 @@@ void ieee80211_resume_disconnect(struc
  }
  EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
  
 -static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
 -                        enum ieee80211_smps_mode *smps_mode)
 -{
 -      if (ifmgd->associated) {
 -              *smps_mode = ifmgd->ap_smps;
 -
 -              if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) {
 -                      if (ifmgd->powersave)
 -                              *smps_mode = IEEE80211_SMPS_DYNAMIC;
 -                      else
 -                              *smps_mode = IEEE80211_SMPS_OFF;
 -              }
 -
 -              return 1;
 -      }
 -
 -      return 0;
 -}
 -
 -void ieee80211_recalc_smps(struct ieee80211_local *local)
 +void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
  {
 -      struct ieee80211_sub_if_data *sdata;
 -      enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
 -      int count = 0;
 -
 -      mutex_lock(&local->iflist_mtx);
 -
 -      /*
 -       * This function could be improved to handle multiple
 -       * interfaces better, but right now it makes any
 -       * non-station interfaces force SM PS to be turned
 -       * off. If there are multiple station interfaces it
 -       * could also use the best possible mode, e.g. if
 -       * one is in static and the other in dynamic then
 -       * dynamic is ok.
 -       */
 -
 -      list_for_each_entry(sdata, &local->interfaces, list) {
 -              if (!ieee80211_sdata_running(sdata))
 -                      continue;
 -              if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
 -                      continue;
 -              if (sdata->vif.type != NL80211_IFTYPE_STATION)
 -                      goto set;
 +      struct ieee80211_local *local = sdata->local;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +      struct ieee80211_chanctx *chanctx;
  
 -              count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
 +      mutex_lock(&local->chanctx_mtx);
  
 -              if (count > 1) {
 -                      smps_mode = IEEE80211_SMPS_OFF;
 -                      break;
 -              }
 -      }
 +      chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
 +                                      lockdep_is_held(&local->chanctx_mtx));
  
 -      if (smps_mode == local->smps_mode)
 +      if (WARN_ON_ONCE(!chanctx_conf))
                goto unlock;
  
 - set:
 -      local->smps_mode = smps_mode;
 -      /* changed flag is auto-detected for this */
 -      ieee80211_hw_config(local, 0);
 +      chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
 +      ieee80211_recalc_smps_chanctx(local, chanctx);
   unlock:
 -      mutex_unlock(&local->iflist_mtx);
 +      mutex_unlock(&local->chanctx_mtx);
  }
  
  static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
@@@ -1815,8 -1818,8 +1817,8 @@@ u8 *ieee80211_ie_build_vht_cap(u8 *pos
        __le32 tmp;
  
        *pos++ = WLAN_EID_VHT_CAPABILITY;
 -      *pos++ = sizeof(struct ieee80211_vht_capabilities);
 -      memset(pos, 0, sizeof(struct ieee80211_vht_capabilities));
 +      *pos++ = sizeof(struct ieee80211_vht_cap);
 +      memset(pos, 0, sizeof(struct ieee80211_vht_cap));
  
        /* capability flags */
        tmp = cpu_to_le32(cap);
@@@ -1974,19 -1977,3 +1976,19 @@@ int ieee80211_ave_rssi(struct ieee80211
        return ifmgd->ave_beacon_signal;
  }
  EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
 +
 +u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
 +{
 +      if (!mcs)
 +              return 1;
 +
 +      /* TODO: consider rx_highest */
 +
 +      if (mcs->rx_mask[3])
 +              return 4;
 +      if (mcs->rx_mask[2])
 +              return 3;
 +      if (mcs->rx_mask[1])
 +              return 2;
 +      return 1;
 +}