IEEE80211_HW_SIGNAL_DBM |
                            IEEE80211_HW_SUPPORTS_STATIC_SMPS |
                            IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
-                           IEEE80211_HW_AMPDU_AGGREGATION;
+                           IEEE80211_HW_AMPDU_AGGREGATION |
+                           IEEE80211_HW_WANT_MONITOR_VIF;
 
                hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
                                    WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
  * @IEEE80211_HW_SCAN_WHILE_IDLE: The device can do hw scan while
  *     being idle (i.e. mac80211 doesn't have to go idle-off during the
  *     the scan).
+ *
+ * @IEEE80211_HW_WANT_MONITOR_VIF: The driver would like to be informed of
+ *     a virtual monitor interface when monitor interfaces are the only
+ *     active interfaces.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
        IEEE80211_HW_PS_NULLFUNC_STACK                  = 1<<11,
        IEEE80211_HW_SUPPORTS_DYNAMIC_PS                = 1<<12,
        IEEE80211_HW_MFP_CAPABLE                        = 1<<13,
-       /* reuse bit 14 */
+       IEEE80211_HW_WANT_MONITOR_VIF                   = 1<<14,
        IEEE80211_HW_SUPPORTS_STATIC_SMPS               = 1<<15,
        IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS              = 1<<16,
        IEEE80211_HW_SUPPORTS_UAPSD                     = 1<<17,
 
        might_sleep();
 
        if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
-                   sdata->vif.type == NL80211_IFTYPE_MONITOR))
+                   (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
+                    !(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))))
                return -EINVAL;
 
        trace_drv_add_interface(local, sdata);
 
        struct net_device napi_dev;
 
        struct napi_struct napi;
+
+       /* virtual monitor interface */
+       struct ieee80211_sub_if_data __rcu *monitor_sdata;
 };
 
 static inline struct ieee80211_sub_if_data *
 
 #undef ADJUST
 }
 
+static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int ret;
+
+       if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
+               return 0;
+
+       if (local->monitor_sdata)
+               return 0;
+
+       sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
+       if (!sdata)
+               return -ENOMEM;
+
+       /* set up data */
+       sdata->local = local;
+       sdata->vif.type = NL80211_IFTYPE_MONITOR;
+       snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
+                wiphy_name(local->hw.wiphy));
+
+       ret = drv_add_interface(local, sdata);
+       if (WARN_ON(ret)) {
+               /* ok .. stupid driver, it asked for this! */
+               kfree(sdata);
+               return ret;
+       }
+
+       rcu_assign_pointer(local->monitor_sdata, sdata);
+
+       return 0;
+}
+
+static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
+               return;
+
+       sdata = rtnl_dereference(local->monitor_sdata);
+
+       if (!sdata)
+               return;
+
+       rcu_assign_pointer(local->monitor_sdata, NULL);
+       synchronize_net();
+
+       drv_remove_interface(local, sdata);
+
+       kfree(sdata);
+}
+
 /*
  * NOTE: Be very careful when changing this function, it must NOT return
  * an error on interface type changes that have been pre-checked, so most
                        break;
                }
 
+               if (local->monitors == 0 && local->open_count == 0) {
+                       res = ieee80211_add_virtual_monitor(local);
+                       if (res)
+                               goto err_stop;
+               }
+
                /* must be before the call to ieee80211_configure_filter */
                local->monitors++;
                if (local->monitors == 1) {
                break;
        default:
                if (coming_up) {
+                       ieee80211_del_virtual_monitor(local);
+
                        res = drv_add_interface(local, sdata);
                        if (res)
                                goto err_stop;
                if (local->monitors == 0) {
                        local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
                        hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
+                       ieee80211_del_virtual_monitor(local);
                }
 
                ieee80211_adjust_monitor_flags(sdata, -1);
                }
        }
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+       if (local->monitors == local->open_count && local->monitors > 0)
+               ieee80211_add_virtual_monitor(local);
 }
 
 static int ieee80211_stop(struct net_device *dev)
 
                drv_remove_interface(local, sdata);
        }
 
+       sdata = rtnl_dereference(local->monitor_sdata);
+       if (sdata)
+               drv_remove_interface(local, sdata);
+
        /* stop hardware - this must stop RX */
        if (local->open_count)
                ieee80211_stop_device(local);
 
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_MONITOR:
-               sdata = NULL;
-               vif = NULL;
+               sdata = rcu_dereference(local->monitor_sdata);
+               if (sdata)
+                       vif = &sdata->vif;
+               else
+                       vif = NULL;
                break;
        case NL80211_IFTYPE_AP_VLAN:
                sdata = container_of(sdata->bss,
 
                                   IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
 
        /* add interfaces */
+       sdata = rtnl_dereference(local->monitor_sdata);
+       if (sdata) {
+               res = drv_add_interface(local, sdata);
+               if (WARN_ON(res)) {
+                       rcu_assign_pointer(local->monitor_sdata, NULL);
+                       synchronize_net();
+                       kfree(sdata);
+               }
+       }
+
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
                    sdata->vif.type != NL80211_IFTYPE_MONITOR &&