]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 24 Aug 2012 16:25:30 +0000 (12:25 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 24 Aug 2012 16:25:30 +0000 (12:25 -0400)
14 files changed:
1  2 
drivers/net/wireless/at76c50x-usb.c
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/rtl818x/rtl8187/dev.c
drivers/staging/winbond/wbusb.c
include/linux/bcma/bcma_driver_chipcommon.h
include/net/cfg80211.h
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/scan.c
net/wireless/core.c
net/wireless/util.c

index 88b8d64c90f1b49a302deaca3af07788ee4b27c1,abb520dec032375a8c0a690c5bffc3dd0e104d56..e361afed99ff2005f2383d0b02e355464bd61259
@@@ -342,7 -342,7 +342,7 @@@ static int at76_dfu_get_status(struct u
        return ret;
  }
  
 -static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state)
 +static int at76_dfu_get_state(struct usb_device *udev, u8 *state)
  {
        int ret;
  
@@@ -1726,7 -1726,9 +1726,9 @@@ static void at76_mac80211_tx_callback(s
        ieee80211_wake_queues(priv->hw);
  }
  
- static void at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+ static void at76_mac80211_tx(struct ieee80211_hw *hw,
+                            struct ieee80211_tx_control *control,
+                            struct sk_buff *skb)
  {
        struct at76_priv *priv = hw->priv;
        struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer;
index 384e67af73bcec369c3edc1dc3c83c5d9c7f4683,c89fa6ead615f1426047a0d86ca2de86bc5a65ed..df61a09adb6d8dfa1143ea1230319de8c4979b27
@@@ -55,7 -55,8 +55,8 @@@
  \********************/
  
  static void
- ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+ ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
+        struct sk_buff *skb)
  {
        struct ath5k_hw *ah = hw->priv;
        u16 qnum = skb_get_queue_mapping(skb);
@@@ -254,6 -255,7 +255,6 @@@ ath5k_bss_info_changed(struct ieee80211
        struct ath5k_vif *avf = (void *)vif->drv_priv;
        struct ath5k_hw *ah = hw->priv;
        struct ath_common *common = ath5k_hw_common(ah);
 -      unsigned long flags;
  
        mutex_lock(&ah->lock);
  
        }
  
        if (changes & BSS_CHANGED_BEACON) {
 -              spin_lock_irqsave(&ah->block, flags);
 +              spin_lock_bh(&ah->block);
                ath5k_beacon_update(hw, vif);
 -              spin_unlock_irqrestore(&ah->block, flags);
 +              spin_unlock_bh(&ah->block);
        }
  
        if (changes & BSS_CHANGED_BEACON_ENABLED)
index a22df749b8db3d8641b4ef8cb78ad99b9e34adbb,4d8dc9ff5a75bf0e45bb3fa7a59dc37762f803d3..8a2b04d5922f4e350407dc6be80730c1548e56e3
@@@ -462,10 -462,8 +462,10 @@@ irqreturn_t ath_isr(int irq, void *dev
        if (!ath9k_hw_intrpend(ah))
                return IRQ_NONE;
  
 -      if(test_bit(SC_OP_HW_RESET, &sc->sc_flags))
 +      if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) {
 +              ath9k_hw_kill_interrupts(ah);
                return IRQ_HANDLED;
 +      }
  
        /*
         * Figure out the reason(s) for the interrupt.  Note
@@@ -696,7 -694,9 +696,9 @@@ mutex_unlock
        return r;
  }
  
- static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+ static void ath9k_tx(struct ieee80211_hw *hw,
+                    struct ieee80211_tx_control *control,
+                    struct sk_buff *skb)
  {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
  
        memset(&txctl, 0, sizeof(struct ath_tx_control));
        txctl.txq = sc->tx.txq_map[skb_get_queue_mapping(skb)];
+       txctl.sta = control->sta;
  
        ath_dbg(common, XMIT, "transmitting packet, skb: %p\n", skb);
  
index d97a95b1addbb92f58912c5f27de693636055afc,e9a8f00e195c5088dd40bbdeb9b55f873dd2dc9d..73730e94e0ac79fdbdf257f1cf969179e1c62a46
@@@ -2719,37 -2719,32 +2719,37 @@@ static int b43_gpio_init(struct b43_wld
        if (dev->dev->chip_id == 0x4301) {
                mask |= 0x0060;
                set |= 0x0060;
 +      } else if (dev->dev->chip_id == 0x5354) {
 +              /* Don't allow overtaking buttons GPIOs */
 +              set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */
        }
 -      if (dev->dev->chip_id == 0x5354)
 -              set &= 0xff02;
 +
        if (0 /* FIXME: conditional unknown */ ) {
                b43_write16(dev, B43_MMIO_GPIO_MASK,
                            b43_read16(dev, B43_MMIO_GPIO_MASK)
                            | 0x0100);
 -              mask |= 0x0180;
 -              set |= 0x0180;
 +              /* BT Coexistance Input */
 +              mask |= 0x0080;
 +              set |= 0x0080;
 +              /* BT Coexistance Out */
 +              mask |= 0x0100;
 +              set |= 0x0100;
        }
        if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) {
 +              /* PA is controlled by gpio 9, let ucode handle it */
                b43_write16(dev, B43_MMIO_GPIO_MASK,
                            b43_read16(dev, B43_MMIO_GPIO_MASK)
                            | 0x0200);
                mask |= 0x0200;
                set |= 0x0200;
        }
 -      if (dev->dev->core_rev >= 2)
 -              mask |= 0x0010; /* FIXME: This is redundant. */
  
        switch (dev->dev->bus_type) {
  #ifdef CONFIG_B43_BCMA
        case B43_BUS_BCMA:
                bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
                                (bcma_cc_read32(&dev->dev->bdev->bus->drv_cc,
 -                                      BCMA_CC_GPIOCTL) & mask) | set);
 +                                      BCMA_CC_GPIOCTL) & ~mask) | set);
                break;
  #endif
  #ifdef CONFIG_B43_SSB
                if (gpiodev)
                        ssb_write32(gpiodev, B43_GPIO_CONTROL,
                                    (ssb_read32(gpiodev, B43_GPIO_CONTROL)
 -                                  & mask) | set);
 +                                  & ~mask) | set);
                break;
  #endif
        }
@@@ -3412,7 -3407,8 +3412,8 @@@ static void b43_tx_work(struct work_str
  }
  
  static void b43_op_tx(struct ieee80211_hw *hw,
-                    struct sk_buff *skb)
+                     struct ieee80211_tx_control *control,
+                     struct sk_buff *skb)
  {
        struct b43_wl *wl = hw_to_b43_wl(hw);
  
index 1c70defba6c308401cc685c73cdcd97c978caf4a,04ecf03fc8cb372f86a7dbb4ff7e5b5b024671c1..513e172832e1f19b2ed744aa1662c3fe7aa374d0
@@@ -123,8 -123,7 +123,8 @@@ static struct ieee80211_channel brcms_2
                 IEEE80211_CHAN_NO_HT40PLUS),
        CHAN2GHZ(14, 2484,
                 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
 -               IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
 +               IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS |
 +               IEEE80211_CHAN_NO_OFDM)
  };
  
  static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
@@@ -267,7 -266,9 +267,9 @@@ static void brcms_set_basic_rate(struc
        }
  }
  
- static void brcms_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+ static void brcms_ops_tx(struct ieee80211_hw *hw,
+                        struct ieee80211_tx_control *control,
+                        struct sk_buff *skb)
  {
        struct brcms_info *wl = hw->priv;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
                goto done;
        }
        brcms_c_sendpkt_mac80211(wl->wlc, skb, hw);
-       tx_info->rate_driver_data[0] = tx_info->control.sta;
+       tx_info->rate_driver_data[0] = control->sta;
   done:
        spin_unlock_bh(&wl->lock);
  }
index 533024095c43ad48871868d8522c6953399eb86a,05d8ca045afd975027fbda0d76a8dab13ff845f3..7811b6315973cd466e19d2fd2c139532daf04610
@@@ -44,7 -44,7 +44,7 @@@ MODULE_AUTHOR("Larry Finger <Larry.Fing
  MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver");
  MODULE_LICENSE("GPL");
  
 -static struct usb_device_id rtl8187_table[] __devinitdata = {
 +static struct usb_device_id rtl8187_table[] = {
        /* Asus */
        {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187},
        /* Belkin */
@@@ -228,7 -228,9 +228,9 @@@ static void rtl8187_tx_cb(struct urb *u
        }
  }
  
- static void rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
+ static void rtl8187_tx(struct ieee80211_hw *dev,
+                      struct ieee80211_tx_control *control,
+                      struct sk_buff *skb)
  {
        struct rtl8187_priv *priv = dev->priv;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@@ -1076,7 -1078,7 +1078,7 @@@ static void rtl8187_beacon_work(struct 
        /* TODO: use actual beacon queue */
        skb_set_queue_mapping(skb, 0);
  
-       rtl8187_tx(dev, skb);
+       rtl8187_tx(dev, NULL, skb);
  
  resched:
        /*
index 0ca857ac473e91e3171c0963f85a4be214b64947,b76d95e180faba885818ea14cfda0073c5042d05..48aa1361903e3b5c4f7ae49e38112e30f0e05dac
@@@ -25,7 -25,7 +25,7 @@@ MODULE_DESCRIPTION("IS89C35 802.11bg WL
  MODULE_LICENSE("GPL");
  MODULE_VERSION("0.1");
  
 -static const struct usb_device_id wb35_table[] __devinitconst = {
 +static const struct usb_device_id wb35_table[] = {
        { USB_DEVICE(0x0416, 0x0035) },
        { USB_DEVICE(0x18E8, 0x6201) },
        { USB_DEVICE(0x18E8, 0x6206) },
@@@ -119,7 -119,9 +119,9 @@@ static void wbsoft_configure_filter(str
        *total_flags = new_flags;
  }
  
- static void wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
+ static void wbsoft_tx(struct ieee80211_hw *dev,
+                     struct ieee80211_tx_control *control,
+                     struct sk_buff *skb)
  {
        struct wbsoft_priv *priv = dev->priv;
  
index 3fb8bbafe5e7f70f69614e2730aefc914fb0083d,9810d4b29abf58a060a5dcb294859edfc7d83eda..6ba45d2b99db2e153be43225addaabdb39060072
  #define  BCMA_CC_CHIPST_4313_OTP_PRESENT      2
  #define  BCMA_CC_CHIPST_4331_SPROM_PRESENT    2
  #define  BCMA_CC_CHIPST_4331_OTP_PRESENT      4
 +#define  BCMA_CC_CHIPST_43228_ILP_DIV_EN      0x00000001
 +#define  BCMA_CC_CHIPST_43228_OTP_PRESENT     0x00000002
 +#define  BCMA_CC_CHIPST_43228_SERDES_REFCLK_PADSEL    0x00000004
 +#define  BCMA_CC_CHIPST_43228_SDIO_MODE               0x00000008
 +#define  BCMA_CC_CHIPST_43228_SDIO_OTP_PRESENT        0x00000010
 +#define  BCMA_CC_CHIPST_43228_SDIO_RESET      0x00000020
  #define  BCMA_CC_CHIPST_4706_PKG_OPTION               BIT(0) /* 0: full-featured package 1: low-cost package */
  #define  BCMA_CC_CHIPST_4706_SFLASH_PRESENT   BIT(1) /* 0: parallel, 1: serial flash is present */
  #define  BCMA_CC_CHIPST_4706_SFLASH_TYPE      BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */
@@@ -515,6 -509,26 +515,26 @@@ struct bcma_pflash 
        u32 window_size;
  };
  
+ #ifdef CONFIG_BCMA_SFLASH
+ struct bcma_sflash {
+       bool present;
+       u32 window;
+       u32 blocksize;
+       u16 numblocks;
+       u32 size;
+ };
+ #endif
+ #ifdef CONFIG_BCMA_NFLASH
+ struct mtd_info;
+ struct bcma_nflash {
+       bool present;
+       struct mtd_info *mtd;
+ };
+ #endif
  struct bcma_serial_port {
        void *regs;
        unsigned long clockspeed;
@@@ -535,6 -549,12 +555,12 @@@ struct bcma_drv_cc 
        struct bcma_chipcommon_pmu pmu;
  #ifdef CONFIG_BCMA_DRIVER_MIPS
        struct bcma_pflash pflash;
+ #ifdef CONFIG_BCMA_SFLASH
+       struct bcma_sflash sflash;
+ #endif
+ #ifdef CONFIG_BCMA_NFLASH
+       struct bcma_nflash nflash;
+ #endif
  
        int nr_serial_ports;
        struct bcma_serial_port serial_ports[4];
diff --combined include/net/cfg80211.h
index 3d254e10ff30e7ab3c5a4fee2ee0b38f1309bd94,4c518f1f1acaeffd806343ee0abc69662734a9f4..ba2e6160fad1d74cc755ef15a1056f15089c0ff4
@@@ -96,7 -96,6 +96,7 @@@ enum ieee80211_band 
   *    is not permitted.
   * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel
   *    is not permitted.
 + * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel.
   */
  enum ieee80211_channel_flags {
        IEEE80211_CHAN_DISABLED         = 1<<0,
        IEEE80211_CHAN_RADAR            = 1<<3,
        IEEE80211_CHAN_NO_HT40PLUS      = 1<<4,
        IEEE80211_CHAN_NO_HT40MINUS     = 1<<5,
 +      IEEE80211_CHAN_NO_OFDM          = 1<<6,
  };
  
  #define IEEE80211_CHAN_NO_HT40 \
@@@ -1439,7 -1437,8 +1439,8 @@@ struct cfg80211_gtk_rekey_data 
   * @add_virtual_intf: create a new virtual interface with the given name,
   *    must set the struct wireless_dev's iftype. Beware: You must create
   *    the new netdev in the wiphy's network namespace! Returns the struct
-  *    wireless_dev, or an ERR_PTR.
+  *    wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must
+  *    also set the address member in the wdev.
   *
   * @del_virtual_intf: remove the virtual interface
   *
   * @get_channel: Get the current operating channel for the virtual interface.
   *    For monitor interfaces, it should return %NULL unless there's a single
   *    current monitoring channel.
+  *
+  * @start_p2p_device: Start the given P2P device.
+  * @stop_p2p_device: Stop the given P2P device.
   */
  struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
                (*get_channel)(struct wiphy *wiphy,
                               struct wireless_dev *wdev,
                               enum nl80211_channel_type *type);
+       int     (*start_p2p_device)(struct wiphy *wiphy,
+                                   struct wireless_dev *wdev);
+       void    (*stop_p2p_device)(struct wiphy *wiphy,
+                                  struct wireless_dev *wdev);
  };
  
  /*
@@@ -2397,6 -2404,8 +2406,8 @@@ struct cfg80211_cached_keys
   * @cleanup_work: work struct used for cleanup that can't be done directly
   * @beacon_interval: beacon interval used on this device for transmitting
   *    beacons, 0 when not valid
+  * @address: The address for this device, valid only if @netdev is %NULL
+  * @p2p_started: true if this is a P2P Device that has been started
   */
  struct wireless_dev {
        struct wiphy *wiphy;
  
        struct work_struct cleanup_work;
  
-       bool use_4addr;
+       bool use_4addr, p2p_started;
+       u8 address[ETH_ALEN] __aligned(sizeof(u16));
  
        /* currently used for IBSS and SME - might be rearranged later */
        u8 ssid[IEEE80211_MAX_SSID_LEN];
  #endif
  };
  
+ static inline u8 *wdev_address(struct wireless_dev *wdev)
+ {
+       if (wdev->netdev)
+               return wdev->netdev->dev_addr;
+       return wdev->address;
+ }
  /**
   * wdev_priv - return wiphy priv from wireless_dev
   *
@@@ -3530,6 -3548,22 +3550,22 @@@ void cfg80211_ch_switch_notify(struct n
   */
  u32 cfg80211_calculate_bitrate(struct rate_info *rate);
  
+ /**
+  * cfg80211_unregister_wdev - remove the given wdev
+  * @wdev: struct wireless_dev to remove
+  *
+  * Call this function only for wdevs that have no netdev assigned,
+  * e.g. P2P Devices. It removes the device from the list so that
+  * it can no longer be used. It is necessary to call this function
+  * even when cfg80211 requests the removal of the interface by
+  * calling the del_virtual_intf() callback. The function must also
+  * be called when the driver wishes to unregister the wdev, e.g.
+  * when the device is unbound from the driver.
+  *
+  * Requires the RTNL to be held.
+  */
+ void cfg80211_unregister_wdev(struct wireless_dev *wdev);
  /* Logging, debugging and troubleshooting/diagnostic helpers. */
  
  /* wiphy_printk helpers, similar to dev_printk */
diff --combined net/mac80211/mesh.c
index 0e2f83e71277f37176226c7c41e5fd03a9cb8abb,f4a636ffe0230cdef0d61db3ba8b48f33bd2ab80..ff0296c7bab8b131f07a9846b608e7b91944a93e
@@@ -109,11 -109,11 +109,11 @@@ bool mesh_matches_local(struct ieee8021
  
        /* Disallow HT40+/- mismatch */
        if (ie->ht_operation &&
-           (local->_oper_channel_type == NL80211_CHAN_HT40MINUS ||
-           local->_oper_channel_type == NL80211_CHAN_HT40PLUS) &&
+           (sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40MINUS ||
+            sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40PLUS) &&
            (sta_channel_type == NL80211_CHAN_HT40MINUS ||
             sta_channel_type == NL80211_CHAN_HT40PLUS) &&
-           local->_oper_channel_type != sta_channel_type)
+           sdata->vif.bss_conf.channel_type != sta_channel_type)
                goto mismatch;
  
        return true;
@@@ -355,17 -355,18 +355,18 @@@ int mesh_add_ds_params_ie(struct sk_buf
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *chan = local->oper_channel;
        u8 *pos;
  
        if (skb_tailroom(skb) < 3)
                return -ENOMEM;
  
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+       sband = local->hw.wiphy->bands[chan->band];
        if (sband->band == IEEE80211_BAND_2GHZ) {
                pos = skb_put(skb, 2 + 1);
                *pos++ = WLAN_EID_DS_PARAMS;
                *pos++ = 1;
-               *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq);
+               *pos++ = ieee80211_frequency_to_channel(chan->center_freq);
        }
  
        return 0;
@@@ -380,7 -381,7 +381,7 @@@ int mesh_add_ht_cap_ie(struct sk_buff *
  
        sband = local->hw.wiphy->bands[local->oper_channel->band];
        if (!sband->ht_cap.ht_supported ||
-           local->_oper_channel_type == NL80211_CHAN_NO_HT)
+           sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
                return 0;
  
        if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
@@@ -397,7 -398,8 +398,8 @@@ int mesh_add_ht_oper_ie(struct sk_buff 
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_channel *channel = local->oper_channel;
-       enum nl80211_channel_type channel_type = local->_oper_channel_type;
+       enum nl80211_channel_type channel_type =
+                               sdata->vif.bss_conf.channel_type;
        struct ieee80211_supported_band *sband =
                                local->hw.wiphy->bands[channel->band];
        struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
@@@ -608,12 -610,14 +610,14 @@@ void ieee80211_start_mesh(struct ieee80
        sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
        sdata->vif.bss_conf.basic_rates =
                ieee80211_mandatory_rates(sdata->local,
-                                         sdata->local->hw.conf.channel->band);
+                                         sdata->local->oper_channel->band);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
                                                BSS_CHANGED_BEACON_ENABLED |
                                                BSS_CHANGED_HT |
                                                BSS_CHANGED_BASIC_RATES |
                                                BSS_CHANGED_BEACON_INT);
+       netif_carrier_on(sdata->dev);
  }
  
  void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
  
+       netif_carrier_off(sdata->dev);
+       /* stop the beacon */
        ifmsh->mesh_id_len = 0;
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
-       sta_info_flush(local, NULL);
+       /* flush STAs and mpaths on this iface */
+       sta_info_flush(sdata->local, sdata);
+       mesh_path_flush_by_iface(sdata);
  
        del_timer_sync(&sdata->u.mesh.housekeeping_timer);
        del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
 +      del_timer_sync(&sdata->u.mesh.mesh_path_timer);
        /*
         * If the timer fired while we waited for it, it will have
         * requeued the work. Now the work will be running again
        local->fif_other_bss--;
        atomic_dec(&local->iff_allmultis);
        ieee80211_configure_filter(local);
 +
 +      sdata->u.mesh.timers_running = 0;
  }
  
  static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
diff --combined net/mac80211/mlme.c
index a4a5acdbaa4dd3ac5e2fb8c5f0ff1d1b97553d6a,b65b2149b23bf7880767ff173fb3d0d4fa05328f..a8cf70bf1cbac14bd7b53ff8aa1e51ca80f6024b
@@@ -146,6 -146,9 +146,9 @@@ void ieee80211_sta_reset_beacon_monitor
        if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
                return;
  
+       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+               return;
        mod_timer(&sdata->u.mgd.bcn_mon_timer,
                  round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout));
  }
@@@ -182,15 -185,15 +185,15 @@@ static u32 ieee80211_config_ht_tx(struc
        u16 ht_opmode;
        bool disable_40 = false;
  
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+       sband = local->hw.wiphy->bands[local->oper_channel->band];
  
        switch (sdata->vif.bss_conf.channel_type) {
        case NL80211_CHAN_HT40PLUS:
-               if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
+               if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
                        disable_40 = true;
                break;
        case NL80211_CHAN_HT40MINUS:
-               if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
+               if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
                        disable_40 = true;
                break;
        default:
@@@ -326,6 -329,26 +329,26 @@@ static void ieee80211_add_ht_ie(struct 
        ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
  }
  
+ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
+                                struct sk_buff *skb,
+                                struct ieee80211_supported_band *sband)
+ {
+       u8 *pos;
+       u32 cap;
+       struct ieee80211_sta_vht_cap vht_cap;
+       BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
+       memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+       /* determine capability flags */
+       cap = vht_cap.cap;
+       /* reserve and fill IE */
+       pos = skb_put(skb, sizeof(struct ieee80211_vht_capabilities) + 2);
+       ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
+ }
  static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
  {
        struct ieee80211_local *local = sdata->local;
                        4 + /* power capability */
                        2 + 2 * sband->n_channels + /* supported channels */
                        2 + sizeof(struct ieee80211_ht_cap) + /* HT */
+                       2 + sizeof(struct ieee80211_vht_capabilities) + /* VHT */
                        assoc_data->ie_len + /* extra IEs */
                        9, /* WMM */
                        GFP_KERNEL);
                ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
                                    sband, local->oper_channel, ifmgd->ap_smps);
  
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
+               ieee80211_add_vht_ie(sdata, skb, sband);
        /* if present, add any custom non-vendor IEs that go after HT */
        if (assoc_data->ie_len && assoc_data->ie) {
                noffset = ieee80211_ie_split_vendor(assoc_data->ie,
@@@ -583,8 -610,6 +610,6 @@@ static void ieee80211_send_deauth_disas
                        IEEE80211_SKB_CB(skb)->flags |=
                                IEEE80211_TX_INTFL_DONT_ENCRYPT;
  
-               drv_mgd_prepare_tx(local, sdata);
                ieee80211_tx_skb(sdata, skb);
        }
  }
@@@ -687,6 -712,7 +712,7 @@@ static void ieee80211_chswitch_work(str
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->local->oper_channel;
  
+       /* XXX: wait for a beacon first? */
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
   out:
@@@ -763,36 -789,32 +789,32 @@@ void ieee80211_sta_process_chanswitch(s
  
        sdata->local->csa_channel = new_ch;
  
+       ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+       if (sw_elem->mode)
+               ieee80211_stop_queues_by_reason(&sdata->local->hw,
+                               IEEE80211_QUEUE_STOP_REASON_CSA);
        if (sdata->local->ops->channel_switch) {
                /* use driver's channel switch callback */
-               struct ieee80211_channel_switch ch_switch;
-               memset(&ch_switch, 0, sizeof(ch_switch));
-               ch_switch.timestamp = timestamp;
-               if (sw_elem->mode) {
-                       ch_switch.block_tx = true;
-                       ieee80211_stop_queues_by_reason(&sdata->local->hw,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
-               }
-               ch_switch.channel = new_ch;
-               ch_switch.count = sw_elem->count;
-               ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+               struct ieee80211_channel_switch ch_switch = {
+                       .timestamp = timestamp,
+                       .block_tx = sw_elem->mode,
+                       .channel = new_ch,
+                       .count = sw_elem->count,
+               };
                drv_channel_switch(sdata->local, &ch_switch);
                return;
        }
  
        /* channel switch handled in software */
-       if (sw_elem->count <= 1) {
+       if (sw_elem->count <= 1)
                ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
-       } else {
-               if (sw_elem->mode)
-                       ieee80211_stop_queues_by_reason(&sdata->local->hw,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
-               ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+       else
                mod_timer(&ifmgd->chswitch_timer,
-                         jiffies +
-                         msecs_to_jiffies(sw_elem->count *
-                                          cbss->beacon_interval));
-       }
+                         TU_TO_EXP_TIME(sw_elem->count *
+                                        cbss->beacon_interval));
  }
  
  static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@@ -1007,6 -1029,16 +1029,16 @@@ void ieee80211_recalc_ps(struct ieee802
        ieee80211_change_ps(local);
  }
  
+ void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata)
+ {
+       bool ps_allowed = ieee80211_powersave_allowed(sdata);
+       if (sdata->vif.bss_conf.ps != ps_allowed) {
+               sdata->vif.bss_conf.ps = ps_allowed;
+               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_PS);
+       }
+ }
  void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
  {
        struct ieee80211_local *local =
@@@ -1239,7 -1271,7 +1271,7 @@@ static u32 ieee80211_handle_bss_capabil
        }
  
        use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
-       if (sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ)
+       if (sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ)
                use_short_slot = true;
  
        if (use_protection != bss_conf->use_cts_prot) {
@@@ -1310,6 -1342,8 +1342,8 @@@ static void ieee80211_set_associated(st
        ieee80211_recalc_smps(local);
        mutex_unlock(&local->iflist_mtx);
  
+       ieee80211_recalc_ps_vif(sdata);
        netif_tx_start_all_queues(sdata->dev);
        netif_carrier_on(sdata->dev);
  }
@@@ -1371,6 -1405,9 +1405,9 @@@ static void ieee80211_set_disassoc(stru
        }
        local->ps_sdata = NULL;
  
+       /* disable per-vif ps */
+       ieee80211_recalc_ps_vif(sdata);
        /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */
        if (tx)
                drv_flush(local, false);
        del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
        del_timer_sync(&sdata->u.mgd.timer);
        del_timer_sync(&sdata->u.mgd.chswitch_timer);
 +
 +      sdata->u.mgd.timers_running = 0;
  }
  
  void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@@ -1542,7 -1577,8 +1579,8 @@@ static void ieee80211_mgd_probe_ap_send
                        ssid_len = ssid[1];
  
                ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
-                                        0, (u32) -1, true, false);
+                                        0, (u32) -1, true, false,
+                                        ifmgd->associated->channel);
        }
  
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
@@@ -1645,7 -1681,9 +1683,9 @@@ struct sk_buff *ieee80211_ap_probereq_g
                ssid_len = ssid[1];
  
        skb = ieee80211_build_probe_req(sdata, cbss->bssid,
-                                       (u32) -1, ssid + 2, ssid_len,
+                                       (u32) -1,
+                                       sdata->local->oper_channel,
+                                       ssid + 2, ssid_len,
                                        NULL, 0, true);
  
        return skb;
@@@ -1656,7 -1694,6 +1696,6 @@@ static void __ieee80211_connection_loss
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
-       u8 bssid[ETH_ALEN];
        u8 frame_buf[DEAUTH_DISASSOC_LEN];
  
        mutex_lock(&ifmgd->mtx);
                return;
        }
  
-       memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
-       sdata_info(sdata, "Connection to AP %pM lost\n", bssid);
+       sdata_info(sdata, "Connection to AP %pM lost\n",
+                  ifmgd->associated->bssid);
  
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
        mutex_unlock(&local->mtx);
  }
  
- void ieee80211_beacon_connection_loss_work(struct work_struct *work)
static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
  {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
@@@ -2232,14 -2268,10 +2270,10 @@@ static void ieee80211_rx_bss_info(struc
                mutex_unlock(&local->iflist_mtx);
        }
  
-       if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
-           (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid,
-                                                       ETH_ALEN) == 0)) {
-               struct ieee80211_channel_sw_ie *sw_elem =
-                       (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
-               ieee80211_sta_process_chanswitch(sdata, sw_elem,
+       if (elems->ch_switch_ie &&
+           memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0)
+               ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie,
                                                 bss, rx_status->mactime);
-       }
  }
  
  
@@@ -2326,7 -2358,7 +2360,7 @@@ static void ieee80211_rx_mgmt_beacon(st
        if (baselen > len)
                return;
  
-       if (rx_status->freq != local->hw.conf.channel->center_freq)
+       if (rx_status->freq != local->oper_channel->center_freq)
                return;
  
        if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
            !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
                struct ieee80211_supported_band *sband;
  
-               sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+               sband = local->hw.wiphy->bands[local->oper_channel->band];
  
                changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
                                                  bssid, true);
@@@ -2673,7 -2705,8 +2707,8 @@@ static int ieee80211_probe_auth(struct 
                 * will not answer to direct packet in unassociated state.
                 */
                ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
-                                        NULL, 0, (u32) -1, true, false);
+                                        NULL, 0, (u32) -1, true, false,
+                                        auth_data->bss->channel);
        }
  
        auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@@@ -3000,41 -3033,17 +3035,17 @@@ int ieee80211_max_network_latency(struc
        return 0;
  }
  
- static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
-                                    struct cfg80211_bss *cbss, bool assoc)
+ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
+                                 struct cfg80211_bss *cbss)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_bss *bss = (void *)cbss->priv;
-       struct sta_info *sta = NULL;
-       bool have_sta = false;
-       int err;
        int ht_cfreq;
        enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
        const u8 *ht_oper_ie;
        const struct ieee80211_ht_operation *ht_oper = NULL;
        struct ieee80211_supported_band *sband;
  
-       if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
-               return -EINVAL;
-       if (assoc) {
-               rcu_read_lock();
-               have_sta = sta_info_get(sdata, cbss->bssid);
-               rcu_read_unlock();
-       }
-       if (!have_sta) {
-               sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
-               if (!sta)
-                       return -ENOMEM;
-       }
-       mutex_lock(&local->mtx);
-       ieee80211_recalc_idle(sdata->local);
-       mutex_unlock(&local->mtx);
-       /* switch to the right channel */
        sband = local->hw.wiphy->bands[cbss->channel->band];
  
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
        local->oper_channel = cbss->channel;
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
  
-       if (sta) {
+       return 0;
+ }
+ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
+                                    struct cfg80211_bss *cbss, bool assoc)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_bss *bss = (void *)cbss->priv;
+       struct sta_info *new_sta = NULL;
+       bool have_sta = false;
+       int err;
+       if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
+               return -EINVAL;
+       if (assoc) {
+               rcu_read_lock();
+               have_sta = sta_info_get(sdata, cbss->bssid);
+               rcu_read_unlock();
+       }
+       if (!have_sta) {
+               new_sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
+               if (!new_sta)
+                       return -ENOMEM;
+       }
+       mutex_lock(&local->mtx);
+       ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&local->mtx);
+       if (new_sta) {
                u32 rates = 0, basic_rates = 0;
                bool have_higher_than_11mbit;
                int min_rate = INT_MAX, min_rate_index = -1;
+               struct ieee80211_supported_band *sband;
+               sband = local->hw.wiphy->bands[cbss->channel->band];
+               err = ieee80211_prep_channel(sdata, cbss);
+               if (err) {
+                       sta_info_free(local, new_sta);
+                       return err;
+               }
  
                ieee80211_get_rates(sband, bss->supp_rates,
                                    bss->supp_rates_len,
                        basic_rates = BIT(min_rate_index);
                }
  
-               sta->sta.supp_rates[cbss->channel->band] = rates;
+               new_sta->sta.supp_rates[cbss->channel->band] = rates;
                sdata->vif.bss_conf.basic_rates = basic_rates;
  
                /* cf. IEEE 802.11 9.2.12 */
                        BSS_CHANGED_BEACON_INT);
  
                if (assoc)
-                       sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+                       sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH);
  
-               err = sta_info_insert(sta);
-               sta = NULL;
+               err = sta_info_insert(new_sta);
+               new_sta = NULL;
                if (err) {
                        sdata_info(sdata,
                                   "failed to insert STA entry for the AP (error %d)\n",
@@@ -3300,9 -3350,13 +3352,13 @@@ int ieee80211_mgd_assoc(struct ieee8021
        }
  
        /* prepare assoc data */
-       ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
-       ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
+       
+       /*
+        * keep only the 40 MHz disable bit set as it might have
+        * been set during authentication already, all other bits
+        * should be reset for a new connection
+        */
+       ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ;
  
        ifmgd->beacon_crc_valid = false;
  
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
                        ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+                       ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
                        netdev_info(sdata->dev,
-                                   "disabling HT due to WEP/TKIP use\n");
+                                   "disabling HT/VHT due to WEP/TKIP use\n");
                }
        }
  
-       if (req->flags & ASSOC_REQ_DISABLE_HT)
+       if (req->flags & ASSOC_REQ_DISABLE_HT) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+       }
  
        /* Also disable HT if we don't support it or the AP doesn't use WMM */
        sband = local->hw.wiphy->bands[req->bss->channel->band];
        if (!sband->ht_cap.ht_supported ||
            local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
-               netdev_info(sdata->dev,
-                           "disabling HT as WMM/QoS is not supported\n");
+               if (!bss->wmm_used)
+                       netdev_info(sdata->dev,
+                                   "disabling HT as WMM/QoS is not supported by the AP\n");
+       }
+       /* disable VHT if we don't support it or the AP doesn't use WMM */
+       if (!sband->vht_cap.vht_supported ||
+           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+               if (!bss->wmm_used)
+                       netdev_info(sdata->dev,
+                                   "disabling VHT as WMM/QoS is not supported by the AP\n");
        }
  
        memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
@@@ -3467,14 -3534,17 +3536,17 @@@ int ieee80211_mgd_deauth(struct ieee802
                   req->bssid, req->reason_code);
  
        if (ifmgd->associated &&
-           ether_addr_equal(ifmgd->associated->bssid, req->bssid))
+           ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       req->reason_code, true, frame_buf);
-       else
+       } else {
+               drv_mgd_prepare_tx(sdata->local, sdata);
                ieee80211_send_deauth_disassoc(sdata, req->bssid,
                                               IEEE80211_STYPE_DEAUTH,
                                               req->reason_code, true,
                                               frame_buf);
+       }
        mutex_unlock(&ifmgd->mtx);
  
        __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
diff --combined net/mac80211/scan.c
index 839dd9737989ec78bbb979c814953b1bdd0187a8,ef1d69306315eb8281adaa3abc5040f51a1d0148..740e414d44f46fe58c5f6fb1c2f73e5404de10f4
@@@ -299,7 -299,7 +299,7 @@@ static void __ieee80211_scan_completed(
        if (local->scan_req != local->int_scan_req)
                cfg80211_scan_done(local->scan_req, aborted);
        local->scan_req = NULL;
 -      local->scan_sdata = NULL;
 +      rcu_assign_pointer(local->scan_sdata, NULL);
  
        local->scanning = 0;
        local->scan_channel = NULL;
@@@ -416,7 -416,8 +416,8 @@@ static void ieee80211_scan_state_send_p
                        local->scan_req->ssids[i].ssid_len,
                        local->scan_req->ie, local->scan_req->ie_len,
                        local->scan_req->rates[band], false,
-                       local->scan_req->no_cck);
+                       local->scan_req->no_cck,
+                       local->hw.conf.channel);
  
        /*
         * After sending probe requests, wait for probe responses
@@@ -479,11 -480,10 +480,10 @@@ static int __ieee80211_start_scan(struc
        if (local->ops->hw_scan) {
                __set_bit(SCAN_HW_SCANNING, &local->scanning);
        } else if ((req->n_channels == 1) &&
-                  (req->channels[0]->center_freq ==
-                   local->hw.conf.channel->center_freq)) {
-               /* If we are scanning only on the current channel, then
-                * we do not need to stop normal activities
+                  (req->channels[0] == local->oper_channel)) {
+               /*
+                * If we are scanning only on the operating channel
+                * then we do not need to stop normal activities
                 */
                unsigned long next_delay;
  
@@@ -984,6 -984,7 +984,6 @@@ int ieee80211_request_sched_scan_stop(s
                        kfree(local->sched_scan_ies.ie[i]);
  
                drv_sched_scan_stop(local, sdata);
 -              rcu_assign_pointer(local->sched_scan_sdata, NULL);
        }
  out:
        mutex_unlock(&local->mtx);
diff --combined net/wireless/core.c
index dcd64d5b07aadfba26a799506452a9b04fe8e7d3,91b300443f4be6e130c4fc201ee0d191036a2f67..443d4d7deea299c7e997045d22d8b2b146d2c877
@@@ -230,9 -230,24 +230,24 @@@ static int cfg80211_rfkill_set_block(vo
        rtnl_lock();
        mutex_lock(&rdev->devlist_mtx);
  
-       list_for_each_entry(wdev, &rdev->wdev_list, list)
-               if (wdev->netdev)
+       list_for_each_entry(wdev, &rdev->wdev_list, list) {
+               if (wdev->netdev) {
                        dev_close(wdev->netdev);
+                       continue;
+               }
+               /* otherwise, check iftype */
+               switch (wdev->iftype) {
+               case NL80211_IFTYPE_P2P_DEVICE:
+                       if (!wdev->p2p_started)
+                               break;
+                       rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
+                       wdev->p2p_started = false;
+                       rdev->opencount--;
+                       break;
+               default:
+                       break;
+               }
+       }
  
        mutex_unlock(&rdev->devlist_mtx);
        rtnl_unlock();
@@@ -407,6 -422,11 +422,11 @@@ static int wiphy_verify_combinations(st
                        if (WARN_ON(wiphy->software_iftypes & types))
                                return -EINVAL;
  
+                       /* Only a single P2P_DEVICE can be allowed */
+                       if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) &&
+                                   c->limits[j].max > 1))
+                               return -EINVAL;
                        cnt += c->limits[j].max;
                        /*
                         * Don't advertise an unsupported type
@@@ -734,6 -754,35 +754,35 @@@ static void wdev_cleanup_work(struct wo
        dev_put(wdev->netdev);
  }
  
+ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
+ {
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       ASSERT_RTNL();
+       if (WARN_ON(wdev->netdev))
+               return;
+       mutex_lock(&rdev->devlist_mtx);
+       list_del_rcu(&wdev->list);
+       rdev->devlist_generation++;
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               if (!wdev->p2p_started)
+                       break;
+               rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
+               wdev->p2p_started = false;
+               rdev->opencount--;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               break;
+       }
+       mutex_unlock(&rdev->devlist_mtx);
+ }
+ EXPORT_SYMBOL(cfg80211_unregister_wdev);
  static struct device_type wiphy_type = {
        .name   = "wlan",
  };
@@@ -952,11 -1001,6 +1001,11 @@@ static int cfg80211_netdev_notifier_cal
                 */
                synchronize_rcu();
                INIT_LIST_HEAD(&wdev->list);
 +              /*
 +               * Ensure that all events have been processed and
 +               * freed.
 +               */
 +              cfg80211_process_wdev_events(wdev);
                break;
        case NETDEV_PRE_UP:
                if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
diff --combined net/wireless/util.c
index 994e2f0cc7a8a12fc34cbe61fdee97afde3df10b,d7b672262b5f92366e58da2ce3c8ab380004eab5..ef35f4ef2aa623d16f3556a5e3f4709fba363db4
@@@ -684,22 -684,10 +684,10 @@@ EXPORT_SYMBOL(cfg80211_classify8021d)
  
  const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie)
  {
-       u8 *end, *pos;
-       pos = bss->information_elements;
-       if (pos == NULL)
+       if (bss->information_elements == NULL)
                return NULL;
-       end = pos + bss->len_information_elements;
-       while (pos + 1 < end) {
-               if (pos + 2 + pos[1] > end)
-                       break;
-               if (pos[0] == ie)
-                       return pos;
-               pos += 2 + pos[1];
-       }
-       return NULL;
+       return cfg80211_find_ie(ie, bss->information_elements,
+                                bss->len_information_elements);
  }
  EXPORT_SYMBOL(ieee80211_bss_get_ie);
  
@@@ -735,7 -723,7 +723,7 @@@ void cfg80211_upload_connect_keys(struc
        wdev->connect_keys = NULL;
  }
  
 -static void cfg80211_process_wdev_events(struct wireless_dev *wdev)
 +void cfg80211_process_wdev_events(struct wireless_dev *wdev)
  {
        struct cfg80211_event *ev;
        unsigned long flags;
@@@ -812,6 -800,10 +800,10 @@@ int cfg80211_change_iface(struct cfg802
        if (otype == NL80211_IFTYPE_AP_VLAN)
                return -EOPNOTSUPP;
  
+       /* cannot change into P2P device type */
+       if (ntype == NL80211_IFTYPE_P2P_DEVICE)
+               return -EOPNOTSUPP;
        if (!rdev->ops->change_virtual_intf ||
            !(rdev->wiphy.interface_modes & (1 << ntype)))
                return -EOPNOTSUPP;
                case NUM_NL80211_IFTYPES:
                        /* not happening */
                        break;
+               case NL80211_IFTYPE_P2P_DEVICE:
+                       WARN_ON(1);
+                       break;
                }
        }
  
@@@ -1053,8 -1048,15 +1048,15 @@@ int cfg80211_can_use_iftype_chan(struc
        list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
                if (wdev_iter == wdev)
                        continue;
-               if (!netif_running(wdev_iter->netdev))
-                       continue;
+               if (wdev_iter->netdev) {
+                       if (!netif_running(wdev_iter->netdev))
+                               continue;
+               } else if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) {
+                       if (!wdev_iter->p2p_started)
+                               continue;
+               } else {
+                       WARN_ON(1);
+               }
  
                if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
                        continue;