]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/main.c
netdev: Kill NETIF_F_MULTI_QUEUE.
[~andy/linux] / net / mac80211 / main.c
index 9761d9bd5a79338006600a2a43863a53155da8a6..c74607eda1eec7e5f2e528cab87983fb4ab64a2f 100644 (file)
@@ -105,7 +105,7 @@ static int ieee80211_master_open(struct net_device *dev)
 
        /* we hold the RTNL here so can safely walk the list */
        list_for_each_entry(sdata, &local->interfaces, list) {
-               if (sdata->dev != dev && netif_running(sdata->dev)) {
+               if (netif_running(sdata->dev)) {
                        res = 0;
                        break;
                }
@@ -126,7 +126,7 @@ static int ieee80211_master_stop(struct net_device *dev)
 
        /* we hold the RTNL here so can safely walk the list */
        list_for_each_entry(sdata, &local->interfaces, list)
-               if (sdata->dev != dev && netif_running(sdata->dev))
+               if (netif_running(sdata->dev))
                        dev_close(sdata->dev);
 
        return 0;
@@ -151,9 +151,7 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
        /* FIX: what would be proper limits for MTU?
         * This interface uses 802.3 frames. */
        if (new_mtu < 256 ||
-               new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) {
-               printk(KERN_WARNING "%s: invalid MTU %d\n",
-                      dev->name, new_mtu);
+           new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) {
                return -EINVAL;
        }
 
@@ -184,10 +182,11 @@ static int ieee80211_open(struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata, *nsdata;
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct sta_info *sta;
        struct ieee80211_if_init_conf conf;
+       u32 changed = 0;
        int res;
        bool need_hw_reconfig = 0;
-       struct sta_info *sta;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -195,7 +194,7 @@ static int ieee80211_open(struct net_device *dev)
        list_for_each_entry(nsdata, &local->interfaces, list) {
                struct net_device *ndev = nsdata->dev;
 
-               if (ndev != dev && ndev != local->mdev && netif_running(ndev)) {
+               if (ndev != dev && netif_running(ndev)) {
                        /*
                         * Allow only a single IBSS interface to be up at any
                         * time. This is restricted because beacon distribution
@@ -210,30 +209,6 @@ static int ieee80211_open(struct net_device *dev)
                            nsdata->vif.type == IEEE80211_IF_TYPE_IBSS)
                                return -EBUSY;
 
-                       /*
-                        * Disallow multiple IBSS/STA mode interfaces.
-                        *
-                        * This is a technical restriction, it is possible although
-                        * most likely not IEEE 802.11 compliant to have multiple
-                        * STAs with just a single hardware (the TSF timer will not
-                        * be adjusted properly.)
-                        *
-                        * However, because mac80211 uses the master device's BSS
-                        * information for each STA/IBSS interface, doing this will
-                        * currently corrupt that BSS information completely, unless,
-                        * a not very useful case, both STAs are associated to the
-                        * same BSS.
-                        *
-                        * To remove this restriction, the BSS information needs to
-                        * be embedded in the STA/IBSS mode sdata instead of using
-                        * the master device's BSS structure.
-                        */
-                       if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
-                            sdata->vif.type == IEEE80211_IF_TYPE_IBSS) &&
-                           (nsdata->vif.type == IEEE80211_IF_TYPE_STA ||
-                            nsdata->vif.type == IEEE80211_IF_TYPE_IBSS))
-                               return -EBUSY;
-
                        /*
                         * The remaining checks are only performed for interfaces
                         * with the same MAC address.
@@ -253,7 +228,7 @@ static int ieee80211_open(struct net_device *dev)
                         */
                        if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN &&
                            nsdata->vif.type == IEEE80211_IF_TYPE_AP)
-                               sdata->u.vlan.ap = nsdata;
+                               sdata->bss = &nsdata->u.ap;
                }
        }
 
@@ -263,10 +238,13 @@ static int ieee80211_open(struct net_device *dev)
                        return -ENOLINK;
                break;
        case IEEE80211_IF_TYPE_VLAN:
-               if (!sdata->u.vlan.ap)
+               if (!sdata->bss)
                        return -ENOLINK;
+               list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
                break;
        case IEEE80211_IF_TYPE_AP:
+               sdata->bss = &sdata->u.ap;
+               break;
        case IEEE80211_IF_TYPE_STA:
        case IEEE80211_IF_TYPE_MNTR:
        case IEEE80211_IF_TYPE_IBSS:
@@ -284,14 +262,13 @@ static int ieee80211_open(struct net_device *dev)
                if (local->ops->start)
                        res = local->ops->start(local_to_hw(local));
                if (res)
-                       return res;
+                       goto err_del_bss;
                need_hw_reconfig = 1;
                ieee80211_led_radio(local, local->hw.conf.radio_enabled);
        }
 
        switch (sdata->vif.type) {
        case IEEE80211_IF_TYPE_VLAN:
-               list_add(&sdata->u.vlan.list, &sdata->u.vlan.ap->u.ap.vlans);
                /* no need to tell driver */
                break;
        case IEEE80211_IF_TYPE_MNTR:
@@ -314,9 +291,9 @@ static int ieee80211_open(struct net_device *dev)
                if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
                        local->fif_other_bss++;
 
-               netif_tx_lock_bh(local->mdev);
+               netif_addr_lock_bh(local->mdev);
                ieee80211_configure_filter(local);
-               netif_tx_unlock_bh(local->mdev);
+               netif_addr_unlock_bh(local->mdev);
                break;
        case IEEE80211_IF_TYPE_STA:
        case IEEE80211_IF_TYPE_IBSS:
@@ -330,8 +307,10 @@ static int ieee80211_open(struct net_device *dev)
                if (res)
                        goto err_stop;
 
-               ieee80211_if_config(dev);
-               ieee80211_reset_erp_info(dev);
+               if (ieee80211_vif_is_mesh(&sdata->vif))
+                       ieee80211_start_mesh(sdata->dev);
+               changed |= ieee80211_reset_erp_info(dev);
+               ieee80211_bss_info_change_notify(sdata, changed);
                ieee80211_enable_keys(sdata);
 
                if (sdata->vif.type == IEEE80211_IF_TYPE_STA &&
@@ -404,6 +383,10 @@ static int ieee80211_open(struct net_device *dev)
  err_stop:
        if (!local->open_count && local->ops->stop)
                local->ops->stop(local_to_hw(local));
+ err_del_bss:
+       sdata->bss = NULL;
+       if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
+               list_del(&sdata->u.vlan.list);
        return res;
 }
 
@@ -486,7 +469,6 @@ static int ieee80211_stop(struct net_device *dev)
        switch (sdata->vif.type) {
        case IEEE80211_IF_TYPE_VLAN:
                list_del(&sdata->u.vlan.list);
-               sdata->u.vlan.ap = NULL;
                /* no need to tell driver */
                break;
        case IEEE80211_IF_TYPE_MNTR:
@@ -508,14 +490,15 @@ static int ieee80211_stop(struct net_device *dev)
                if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
                        local->fif_other_bss--;
 
-               netif_tx_lock_bh(local->mdev);
+               netif_addr_lock_bh(local->mdev);
                ieee80211_configure_filter(local);
-               netif_tx_unlock_bh(local->mdev);
+               netif_addr_unlock_bh(local->mdev);
                break;
        case IEEE80211_IF_TYPE_MESH_POINT:
        case IEEE80211_IF_TYPE_STA:
        case IEEE80211_IF_TYPE_IBSS:
                sdata->u.sta.state = IEEE80211_DISABLED;
+               memset(sdata->u.sta.bssid, 0, ETH_ALEN);
                del_timer_sync(&sdata->u.sta.timer);
                /*
                 * When we get here, the interface is marked down.
@@ -534,8 +517,6 @@ static int ieee80211_stop(struct net_device *dev)
                                local->sta_hw_scanning = 0;
                }
 
-               flush_workqueue(local->hw.workqueue);
-
                sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
                kfree(sdata->u.sta.extra_ie);
                sdata->u.sta.extra_ie = NULL;
@@ -550,6 +531,8 @@ static int ieee80211_stop(struct net_device *dev)
                local->ops->remove_interface(local_to_hw(local), &conf);
        }
 
+       sdata->bss = NULL;
+
        if (local->open_count == 0) {
                if (netif_running(local->mdev))
                        dev_close(local->mdev);
@@ -559,6 +542,8 @@ static int ieee80211_stop(struct net_device *dev)
 
                ieee80211_led_radio(local, 0);
 
+               flush_workqueue(local->hw.workqueue);
+
                tasklet_disable(&local->tx_pending_tasklet);
                tasklet_disable(&local->tasklet);
        }
@@ -569,6 +554,7 @@ static int ieee80211_stop(struct net_device *dev)
 int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       struct netdev_queue *txq;
        struct sta_info *sta;
        struct ieee80211_sub_if_data *sdata;
        u16 start_seq_num = 0;
@@ -588,9 +574,11 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 
        sta = sta_info_get(local, ra);
        if (!sta) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Could not find the station\n");
-               rcu_read_unlock();
-               return -ENOENT;
+#endif
+               ret = -ENOENT;
+               goto exit;
        }
 
        spin_lock_bh(&sta->lock);
@@ -598,7 +586,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
        /* we have tried too many times, receiver does not want A-MPDU */
        if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
                ret = -EBUSY;
-               goto start_ba_exit;
+               goto err_unlock_sta;
        }
 
        state = &sta->ampdu_mlme.tid_state_tx[tid];
@@ -609,18 +597,20 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                                 "idle on tid %u\n", tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
                ret = -EAGAIN;
-               goto start_ba_exit;
+               goto err_unlock_sta;
        }
 
        /* prepare A-MPDU MLME for Tx aggregation */
        sta->ampdu_mlme.tid_tx[tid] =
                        kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
        if (!sta->ampdu_mlme.tid_tx[tid]) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                if (net_ratelimit())
                        printk(KERN_ERR "allocate tx mlme to tid %d failed\n",
                                        tid);
+#endif
                ret = -ENOMEM;
-               goto start_ba_exit;
+               goto err_unlock_sta;
        }
        /* Tx timer */
        sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
@@ -631,7 +621,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 
        /* ensure that TX flow won't interrupt us
         * until the end of the call to requeue function */
-       spin_lock_bh(&local->mdev->queue_lock);
+       txq = netdev_get_tx_queue(local->mdev, 0);
+       spin_lock_bh(&txq->lock);
 
        /* create a new queue for this aggregation */
        ret = ieee80211_ht_agg_queue_add(local, sta, tid);
@@ -643,7 +634,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                printk(KERN_DEBUG "BA request denied - queue unavailable for"
                                        " tid %d\n", tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
-               goto start_ba_err;
+               goto err_unlock_queue;
        }
        sdata = sta->sdata;
 
@@ -665,12 +656,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                                        " tid %d\n", tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
                *state = HT_AGG_STATE_IDLE;
-               goto start_ba_err;
+               goto err_unlock_queue;
        }
 
        /* Will put all the packets in the new SW queue */
        ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
-       spin_unlock_bh(&local->mdev->queue_lock);
+       spin_unlock_bh(&txq->lock);
+       spin_unlock_bh(&sta->lock);
 
        /* send an addBA request */
        sta->ampdu_mlme.dialog_token_allocator++;
@@ -678,25 +670,28 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                        sta->ampdu_mlme.dialog_token_allocator;
        sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num;
 
+
        ieee80211_send_addba_request(sta->sdata->dev, ra, tid,
                         sta->ampdu_mlme.tid_tx[tid]->dialog_token,
                         sta->ampdu_mlme.tid_tx[tid]->ssn,
                         0x40, 5000);
-
        /* activate the timer for the recipient's addBA response */
        sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires =
                                jiffies + ADDBA_RESP_INTERVAL;
        add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+#ifdef CONFIG_MAC80211_HT_DEBUG
        printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
-       goto start_ba_exit;
+#endif
+       goto exit;
 
-start_ba_err:
+err_unlock_queue:
        kfree(sta->ampdu_mlme.tid_tx[tid]);
        sta->ampdu_mlme.tid_tx[tid] = NULL;
-       spin_unlock_bh(&local->mdev->queue_lock);
+       spin_unlock_bh(&txq->lock);
        ret = -EBUSY;
-start_ba_exit:
+err_unlock_sta:
        spin_unlock_bh(&sta->lock);
+exit:
        rcu_read_unlock();
        return ret;
 }
@@ -768,8 +763,10 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
        DECLARE_MAC_BUF(mac);
 
        if (tid >= STA_TID_NUM) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
                                tid, STA_TID_NUM);
+#endif
                return;
        }
 
@@ -777,8 +774,10 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
        sta = sta_info_get(local, ra);
        if (!sta) {
                rcu_read_unlock();
+#ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Could not find station: %s\n",
                                print_mac(mac, ra));
+#endif
                return;
        }
 
@@ -786,8 +785,10 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
        spin_lock_bh(&sta->lock);
 
        if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
                                *state);
+#endif
                spin_unlock_bh(&sta->lock);
                rcu_read_unlock();
                return;
@@ -798,7 +799,9 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
        *state |= HT_ADDBA_DRV_READY_MSK;
 
        if (*state == HT_AGG_STATE_OPERATIONAL) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
+#endif
                ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
        }
        spin_unlock_bh(&sta->lock);
@@ -809,14 +812,17 @@ EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
 void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       struct netdev_queue *txq;
        struct sta_info *sta;
        u8 *state;
        int agg_queue;
        DECLARE_MAC_BUF(mac);
 
        if (tid >= STA_TID_NUM) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
                                tid, STA_TID_NUM);
+#endif
                return;
        }
 
@@ -828,17 +834,22 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
        rcu_read_lock();
        sta = sta_info_get(local, ra);
        if (!sta) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Could not find station: %s\n",
                                print_mac(mac, ra));
+#endif
                rcu_read_unlock();
                return;
        }
        state = &sta->ampdu_mlme.tid_state_tx[tid];
 
-       spin_lock_bh(&sta->lock);
+       /* NOTE: no need to use sta->lock in this state check, as
+        * ieee80211_stop_tx_ba_session will let only
+        * one stop call to pass through per sta/tid */
        if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
-               spin_unlock_bh(&sta->lock);
+#endif
                rcu_read_unlock();
                return;
        }
@@ -851,16 +862,18 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
 
        /* avoid ordering issues: we are the only one that can modify
         * the content of the qdiscs */
-       spin_lock_bh(&local->mdev->queue_lock);
+       txq = netdev_get_tx_queue(local->mdev, 0);
+       spin_lock_bh(&txq->lock);
        /* remove the queue for this aggregation */
        ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
-       spin_unlock_bh(&local->mdev->queue_lock);
+       spin_unlock_bh(&txq->lock);
 
        /* we just requeued the all the frames that were in the removed
-        * queue, and since we might miss a softirq we do netif_schedule.
+        * queue, and since we might miss a softirq we do netif_schedule_queue.
         * ieee80211_wake_queue is not used here as this queue is not
         * necessarily stopped */
-       netif_schedule(local->mdev);
+       netif_schedule_queue(txq);
+       spin_lock_bh(&sta->lock);
        *state = HT_AGG_STATE_IDLE;
        sta->ampdu_mlme.addba_req_num[tid] = 0;
        kfree(sta->ampdu_mlme.tid_tx[tid]);
@@ -879,9 +892,11 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
        struct sk_buff *skb = dev_alloc_skb(0);
 
        if (unlikely(!skb)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                if (net_ratelimit())
                        printk(KERN_WARNING "%s: Not enough memory, "
                               "dropping start BA session", skb->dev->name);
+#endif
                return;
        }
        ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
@@ -902,9 +917,11 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
        struct sk_buff *skb = dev_alloc_skb(0);
 
        if (unlikely(!skb)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
                if (net_ratelimit())
                        printk(KERN_WARNING "%s: Not enough memory, "
                               "dropping stop BA session", skb->dev->name);
+#endif
                return;
        }
        ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
@@ -955,7 +972,6 @@ static const struct header_ops ieee80211_header_ops = {
        .cache_update   = eth_header_cache_update,
 };
 
-/* Must not be called for mdev */
 void ieee80211_if_setup(struct net_device *dev)
 {
        ether_setup(dev);
@@ -965,67 +981,52 @@ void ieee80211_if_setup(struct net_device *dev)
        dev->change_mtu = ieee80211_change_mtu;
        dev->open = ieee80211_open;
        dev->stop = ieee80211_stop;
-       dev->destructor = ieee80211_if_free;
+       dev->destructor = free_netdev;
 }
 
 /* everything else */
 
-static int __ieee80211_if_config(struct net_device *dev,
-                                struct sk_buff *beacon,
-                                struct ieee80211_tx_control *control)
+int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_conf conf;
 
-       if (!local->ops->config_interface || !netif_running(dev))
+       if (WARN_ON(!netif_running(sdata->dev)))
+               return 0;
+
+       if (!local->ops->config_interface)
                return 0;
 
        memset(&conf, 0, sizeof(conf));
-       conf.type = sdata->vif.type;
+       conf.changed = changed;
+
        if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
            sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
                conf.bssid = sdata->u.sta.bssid;
                conf.ssid = sdata->u.sta.ssid;
                conf.ssid_len = sdata->u.sta.ssid_len;
-       } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
-               conf.beacon = beacon;
-               conf.beacon_control = control;
-               ieee80211_start_mesh(dev);
        } else if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
+               conf.bssid = sdata->dev->dev_addr;
                conf.ssid = sdata->u.ap.ssid;
                conf.ssid_len = sdata->u.ap.ssid_len;
-               conf.beacon = beacon;
-               conf.beacon_control = control;
+       } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               u8 zero[ETH_ALEN] = { 0 };
+               conf.bssid = zero;
+               conf.ssid = zero;
+               conf.ssid_len = 0;
+       } else {
+               WARN_ON(1);
+               return -EINVAL;
        }
-       return local->ops->config_interface(local_to_hw(local),
-                                           &sdata->vif, &conf);
-}
 
-int ieee80211_if_config(struct net_device *dev)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT &&
-           (local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
-               return ieee80211_if_config_beacon(dev);
-       return __ieee80211_if_config(dev, NULL, NULL);
-}
+       if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
+               return -EINVAL;
 
-int ieee80211_if_config_beacon(struct net_device *dev)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       struct ieee80211_tx_control control;
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct sk_buff *skb;
+       if (WARN_ON(!conf.ssid && (changed & IEEE80211_IFCC_SSID)))
+               return -EINVAL;
 
-       if (!(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
-               return 0;
-       skb = ieee80211_beacon_get(local_to_hw(local), &sdata->vif,
-                                  &control);
-       if (!skb)
-               return -ENOMEM;
-       return __ieee80211_if_config(dev, skb, &control);
+       return local->ops->config_interface(local_to_hw(local),
+                                           &sdata->vif, &conf);
 }
 
 int ieee80211_hw_config(struct ieee80211_local *local)
@@ -1168,50 +1169,30 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
                                             changed);
 }
 
-void ieee80211_reset_erp_info(struct net_device *dev)
+u32 ieee80211_reset_erp_info(struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
        sdata->bss_conf.use_cts_prot = 0;
        sdata->bss_conf.use_short_preamble = 0;
-       ieee80211_bss_info_change_notify(sdata,
-                                        BSS_CHANGED_ERP_CTS_PROT |
-                                        BSS_CHANGED_ERP_PREAMBLE);
+       return BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE;
 }
 
 void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
-                                struct sk_buff *skb,
-                                struct ieee80211_tx_status *status)
+                                struct sk_buff *skb)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_tx_status *saved;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        int tmp;
 
        skb->dev = local->mdev;
-       saved = kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC);
-       if (unlikely(!saved)) {
-               if (net_ratelimit())
-                       printk(KERN_WARNING "%s: Not enough memory, "
-                              "dropping tx status", skb->dev->name);
-               /* should be dev_kfree_skb_irq, but due to this function being
-                * named _irqsafe instead of just _irq we can't be sure that
-                * people won't call it from non-irq contexts */
-               dev_kfree_skb_any(skb);
-               return;
-       }
-       memcpy(saved, status, sizeof(struct ieee80211_tx_status));
-       /* copy pointer to saved status into skb->cb for use by tasklet */
-       memcpy(skb->cb, &saved, sizeof(saved));
-
        skb->pkt_type = IEEE80211_TX_STATUS_MSG;
-       skb_queue_tail(status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS ?
+       skb_queue_tail(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS ?
                       &local->skb_queue : &local->skb_queue_unreliable, skb);
        tmp = skb_queue_len(&local->skb_queue) +
                skb_queue_len(&local->skb_queue_unreliable);
        while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT &&
               (skb = skb_dequeue(&local->skb_queue_unreliable))) {
-               memcpy(&saved, skb->cb, sizeof(saved));
-               kfree(saved);
                dev_kfree_skb_irq(skb);
                tmp--;
                I802_DEBUG_INC(local->tx_status_drop);
@@ -1225,7 +1206,6 @@ static void ieee80211_tasklet_handler(unsigned long data)
        struct ieee80211_local *local = (struct ieee80211_local *) data;
        struct sk_buff *skb;
        struct ieee80211_rx_status rx_status;
-       struct ieee80211_tx_status *tx_status;
        struct ieee80211_ra_tid *ra_tid;
 
        while ((skb = skb_dequeue(&local->skb_queue)) ||
@@ -1240,12 +1220,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
                        __ieee80211_rx(local_to_hw(local), skb, &rx_status);
                        break;
                case IEEE80211_TX_STATUS_MSG:
-                       /* get pointer to saved status out of skb->cb */
-                       memcpy(&tx_status, skb->cb, sizeof(tx_status));
                        skb->pkt_type = 0;
-                       ieee80211_tx_status(local_to_hw(local),
-                                           skb, tx_status);
-                       kfree(tx_status);
+                       ieee80211_tx_status(local_to_hw(local), skb);
                        break;
                case IEEE80211_DELBA_MSG:
                        ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
@@ -1259,9 +1235,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
                                                 ra_tid->ra, ra_tid->tid);
                        dev_kfree_skb(skb);
                        break ;
-               default: /* should never get here! */
-                       printk(KERN_ERR "%s: Unknown message type (%d)\n",
-                              wiphy_name(local->hw.wiphy), skb->pkt_type);
+               default:
+                       WARN_ON(1);
                        dev_kfree_skb(skb);
                        break;
                }
@@ -1274,24 +1249,15 @@ static void ieee80211_tasklet_handler(unsigned long data)
  * Also, tx_packet_data in cb is restored from tx_control. */
 static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
                                      struct ieee80211_key *key,
-                                     struct sk_buff *skb,
-                                     struct ieee80211_tx_control *control)
+                                     struct sk_buff *skb)
 {
        int hdrlen, iv_len, mic_len;
-       struct ieee80211_tx_packet_data *pkt_data;
-
-       pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
-       pkt_data->ifindex = vif_to_sdata(control->vif)->dev->ifindex;
-       pkt_data->flags = 0;
-       if (control->flags & IEEE80211_TXCTL_REQ_TX_STATUS)
-               pkt_data->flags |= IEEE80211_TXPD_REQ_TX_STATUS;
-       if (control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)
-               pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
-       if (control->flags & IEEE80211_TXCTL_REQUEUE)
-               pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
-       if (control->flags & IEEE80211_TXCTL_EAPOL_FRAME)
-               pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME;
-       pkt_data->queue = control->queue;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+       info->flags &=  IEEE80211_TX_CTL_REQ_TX_STATUS |
+                       IEEE80211_TX_CTL_DO_NOT_ENCRYPT |
+                       IEEE80211_TX_CTL_REQUEUE |
+                       IEEE80211_TX_CTL_EAPOL_FRAME;
 
        hdrlen = ieee80211_get_hdrlen_from_skb(skb);
 
@@ -1338,15 +1304,16 @@ no_key:
 
 static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
                                            struct sta_info *sta,
-                                           struct sk_buff *skb,
-                                           struct ieee80211_tx_status *status)
+                                           struct sk_buff *skb)
 {
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
        sta->tx_filtered_count++;
 
        /*
         * Clear the TX filter mask for this STA when sending the next
         * packet. If the STA went to power save mode, this will happen
-        * happen when it wakes up for the next time.
+        * when it wakes up for the next time.
         */
        set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT);
 
@@ -1382,54 +1349,47 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
         */
        if (test_sta_flags(sta, WLAN_STA_PS) &&
            skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
-               ieee80211_remove_tx_extra(local, sta->key, skb,
-                                         &status->control);
+               ieee80211_remove_tx_extra(local, sta->key, skb);
                skb_queue_tail(&sta->tx_filtered, skb);
                return;
        }
 
        if (!test_sta_flags(sta, WLAN_STA_PS) &&
-           !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) {
+           !(info->flags & IEEE80211_TX_CTL_REQUEUE)) {
                /* Software retry the packet once */
-               status->control.flags |= IEEE80211_TXCTL_REQUEUE;
-               ieee80211_remove_tx_extra(local, sta->key, skb,
-                                         &status->control);
+               info->flags |= IEEE80211_TX_CTL_REQUEUE;
+               ieee80211_remove_tx_extra(local, sta->key, skb);
                dev_queue_xmit(skb);
                return;
        }
 
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
        if (net_ratelimit())
                printk(KERN_DEBUG "%s: dropped TX filtered frame, "
                       "queue_len=%d PS=%d @%lu\n",
                       wiphy_name(local->hw.wiphy),
                       skb_queue_len(&sta->tx_filtered),
                       !!test_sta_flags(sta, WLAN_STA_PS), jiffies);
+#endif
        dev_kfree_skb(skb);
 }
 
-void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
-                        struct ieee80211_tx_status *status)
+void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct sk_buff *skb2;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        u16 frag, type;
+       __le16 fc;
        struct ieee80211_tx_status_rtap_hdr *rthdr;
        struct ieee80211_sub_if_data *sdata;
        struct net_device *prev_dev = NULL;
-
-       if (!status) {
-               printk(KERN_ERR
-                      "%s: ieee80211_tx_status called with NULL status\n",
-                      wiphy_name(local->hw.wiphy));
-               dev_kfree_skb(skb);
-               return;
-       }
+       struct sta_info *sta;
 
        rcu_read_lock();
 
-       if (status->excessive_retries) {
-               struct sta_info *sta;
+       if (info->status.excessive_retries) {
                sta = sta_info_get(local, hdr->addr1);
                if (sta) {
                        if (test_sta_flags(sta, WLAN_STA_PS)) {
@@ -1437,27 +1397,39 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
                                 * The STA is in power save mode, so assume
                                 * that this TX packet failed because of that.
                                 */
-                               status->excessive_retries = 0;
-                               status->flags |= IEEE80211_TX_STATUS_TX_FILTERED;
-                               ieee80211_handle_filtered_frame(local, sta,
-                                                               skb, status);
+                               ieee80211_handle_filtered_frame(local, sta, skb);
                                rcu_read_unlock();
                                return;
                        }
                }
        }
 
-       if (status->flags & IEEE80211_TX_STATUS_TX_FILTERED) {
-               struct sta_info *sta;
+       fc = hdr->frame_control;
+
+       if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
+           (ieee80211_is_data_qos(fc))) {
+               u16 tid, ssn;
+               u8 *qc;
                sta = sta_info_get(local, hdr->addr1);
                if (sta) {
-                       ieee80211_handle_filtered_frame(local, sta, skb,
-                                                       status);
+                       qc = ieee80211_get_qos_ctl(hdr);
+                       tid = qc[0] & 0xf;
+                       ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10)
+                                               & IEEE80211_SCTL_SEQ);
+                       ieee80211_send_bar(sta->sdata->dev, hdr->addr1,
+                                          tid, ssn);
+               }
+       }
+
+       if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
+               sta = sta_info_get(local, hdr->addr1);
+               if (sta) {
+                       ieee80211_handle_filtered_frame(local, sta, skb);
                        rcu_read_unlock();
                        return;
                }
        } else
-               rate_control_tx_status(local->mdev, skb, status);
+               rate_control_tx_status(local->mdev, skb);
 
        rcu_read_unlock();
 
@@ -1471,14 +1443,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
        frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
        type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE;
 
-       if (status->flags & IEEE80211_TX_STATUS_ACK) {
+       if (info->flags & IEEE80211_TX_STAT_ACK) {
                if (frag == 0) {
                        local->dot11TransmittedFrameCount++;
                        if (is_multicast_ether_addr(hdr->addr1))
                                local->dot11MulticastTransmittedFrameCount++;
-                       if (status->retry_count > 0)
+                       if (info->status.retry_count > 0)
                                local->dot11RetryCount++;
-                       if (status->retry_count > 1)
+                       if (info->status.retry_count > 1)
                                local->dot11MultipleRetryCount++;
                }
 
@@ -1524,17 +1496,17 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
                cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
                            (1 << IEEE80211_RADIOTAP_DATA_RETRIES));
 
-       if (!(status->flags & IEEE80211_TX_STATUS_ACK) &&
+       if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
            !is_multicast_ether_addr(hdr->addr1))
                rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
 
-       if ((status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) &&
-           (status->control.flags & IEEE80211_TXCTL_USE_CTS_PROTECT))
+       if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) &&
+           (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
                rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
-       else if (status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS)
+       else if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
                rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
 
-       rthdr->data_retries = status->retry_count;
+       rthdr->data_retries = info->status.retry_count;
 
        /* XXX: is this sufficient for BPF? */
        skb_set_mac_header(skb, 0);
@@ -1660,7 +1632,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        int result;
        enum ieee80211_band band;
        struct net_device *mdev;
-       struct ieee80211_sub_if_data *sdata;
+       struct wireless_dev *mwdev;
 
        /*
         * generic code guarantees at least one band,
@@ -1684,19 +1656,35 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        if (result < 0)
                return result;
 
-       /* for now, mdev needs sub_if_data :/ */
-       mdev = alloc_netdev(sizeof(struct ieee80211_sub_if_data),
-                           "wmaster%d", ether_setup);
+       /*
+        * We use the number of queues for feature tests (QoS, HT) internally
+        * so restrict them appropriately.
+        */
+#ifdef CONFIG_MAC80211_QOS
+       if (hw->queues > IEEE80211_MAX_QUEUES)
+               hw->queues = IEEE80211_MAX_QUEUES;
+       if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
+               hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
+       if (hw->queues < 4)
+               hw->ampdu_queues = 0;
+#else
+       hw->queues = 1;
+       hw->ampdu_queues = 0;
+#endif
+
+       mdev = alloc_netdev_mq(sizeof(struct wireless_dev),
+                              "wmaster%d", ether_setup,
+                              ieee80211_num_queues(hw));
        if (!mdev)
                goto fail_mdev_alloc;
 
-       sdata = IEEE80211_DEV_TO_SUB_IF(mdev);
-       mdev->ieee80211_ptr = &sdata->wdev;
-       sdata->wdev.wiphy = local->hw.wiphy;
+       mwdev = netdev_priv(mdev);
+       mdev->ieee80211_ptr = mwdev;
+       mwdev->wiphy = local->hw.wiphy;
 
        local->mdev = mdev;
 
-       ieee80211_rx_bss_list_init(mdev);
+       ieee80211_rx_bss_list_init(local);
 
        mdev->hard_start_xmit = ieee80211_master_start_xmit;
        mdev->open = ieee80211_master_open;
@@ -1705,18 +1693,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        mdev->header_ops = &ieee80211_header_ops;
        mdev->set_multicast_list = ieee80211_master_set_multicast_list;
 
-       sdata->vif.type = IEEE80211_IF_TYPE_AP;
-       sdata->dev = mdev;
-       sdata->local = local;
-       sdata->u.ap.force_unicast_rateidx = -1;
-       sdata->u.ap.max_ratectrl_rateidx = -1;
-       ieee80211_if_sdata_init(sdata);
-
-       /* no RCU needed since we're still during init phase */
-       list_add_tail(&sdata->list, &local->interfaces);
-
        name = wiphy_dev(local->hw.wiphy)->driver->name;
-       local->hw.workqueue = create_singlethread_workqueue(name);
+       local->hw.workqueue = create_freezeable_workqueue(name);
        if (!local->hw.workqueue) {
                result = -ENOMEM;
                goto fail_workqueue;
@@ -1732,7 +1710,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        debugfs_hw_add(local);
 
-       local->hw.conf.beacon_int = 1000;
+       if (local->hw.conf.beacon_int < 10)
+               local->hw.conf.beacon_int = 100;
 
        local->wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC |
                                                  IEEE80211_HW_SIGNAL_DB |
@@ -1759,9 +1738,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        if (result < 0)
                goto fail_dev;
 
-       ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
-       ieee80211_if_set_type(local->mdev, IEEE80211_IF_TYPE_AP);
-
        result = ieee80211_init_rate_ctrl_alg(local,
                                              hw->rate_control_algorithm);
        if (result < 0) {
@@ -1778,21 +1754,15 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                goto fail_wep;
        }
 
-       if (hw->queues > IEEE80211_MAX_QUEUES)
-               hw->queues = IEEE80211_MAX_QUEUES;
-       if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
-               hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
-
        ieee80211_install_qdisc(local->mdev);
 
        /* add one default STA interface */
-       result = ieee80211_if_add(local->mdev, "wlan%d", NULL,
+       result = ieee80211_if_add(local, "wlan%d", NULL,
                                  IEEE80211_IF_TYPE_STA, NULL);
        if (result)
                printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
                       wiphy_name(local->hw.wiphy));
 
-       local->reg_state = IEEE80211_DEV_REGISTERED;
        rtnl_unlock();
 
        ieee80211_led_init(local);
@@ -1802,7 +1772,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 fail_wep:
        rate_control_deinitialize(local);
 fail_rate:
-       ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
        unregister_netdevice(local->mdev);
        local->mdev = NULL;
 fail_dev:
@@ -1812,10 +1781,8 @@ fail_sta_info:
        debugfs_hw_del(local);
        destroy_workqueue(local->hw.workqueue);
 fail_workqueue:
-       if (local->mdev != NULL) {
-               ieee80211_if_free(local->mdev);
-               local->mdev = NULL;
-       }
+       if (local->mdev)
+               free_netdev(local->mdev);
 fail_mdev_alloc:
        wiphy_unregister(local->hw.wiphy);
        return result;
@@ -1825,42 +1792,27 @@ EXPORT_SYMBOL(ieee80211_register_hw);
 void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_sub_if_data *sdata, *tmp;
 
        tasklet_kill(&local->tx_pending_tasklet);
        tasklet_kill(&local->tasklet);
 
        rtnl_lock();
 
-       BUG_ON(local->reg_state != IEEE80211_DEV_REGISTERED);
-
-       local->reg_state = IEEE80211_DEV_UNREGISTERED;
-
        /*
         * At this point, interface list manipulations are fine
         * because the driver cannot be handing us frames any
         * more and the tasklet is killed.
         */
 
-       /*
-        * First, we remove all non-master interfaces. Do this because they
-        * may have bss pointer dependency on the master, and when we free
-        * the master these would be freed as well, breaking our list
-        * iteration completely.
-        */
-       list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
-               if (sdata->dev == local->mdev)
-                       continue;
-               list_del(&sdata->list);
-               __ieee80211_if_del(local, sdata);
-       }
+       /* First, we remove all virtual interfaces. */
+       ieee80211_remove_interfaces(local);
 
        /* then, finally, remove the master interface */
-       __ieee80211_if_del(local, IEEE80211_DEV_TO_SUB_IF(local->mdev));
+       unregister_netdevice(local->mdev);
 
        rtnl_unlock();
 
-       ieee80211_rx_bss_list_deinit(local->mdev);
+       ieee80211_rx_bss_list_deinit(local);
        ieee80211_clear_tx_pending(local);
        sta_info_stop(local);
        rate_control_deinitialize(local);
@@ -1877,8 +1829,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
        wiphy_unregister(local->hw.wiphy);
        ieee80211_wep_free(local);
        ieee80211_led_exit(local);
-       ieee80211_if_free(local->mdev);
-       local->mdev = NULL;
+       free_netdev(local->mdev);
 }
 EXPORT_SYMBOL(ieee80211_unregister_hw);
 
@@ -1895,7 +1846,9 @@ static int __init ieee80211_init(void)
        struct sk_buff *skb;
        int ret;
 
-       BUILD_BUG_ON(sizeof(struct ieee80211_tx_packet_data) > sizeof(skb->cb));
+       BUILD_BUG_ON(sizeof(struct ieee80211_tx_info) > sizeof(skb->cb));
+       BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) +
+                    IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
 
        ret = rc80211_pid_init();
        if (ret)