]> err.no Git - linux-2.6/blobdiff - net/mac80211/ieee80211.c
Pull bugzilla-9535 into release branch
[linux-2.6] / net / mac80211 / ieee80211.c
index 319ec2a1d84f3097f4337b73aa22ce0c047cb291..6378850d85805860416e9e67f7b0e308e76e6e0a 100644 (file)
@@ -24,7 +24,6 @@
 #include <net/net_namespace.h>
 #include <net/cfg80211.h>
 
-#include "ieee80211_common.h"
 #include "ieee80211_i.h"
 #include "ieee80211_rate.h"
 #include "wep.h"
@@ -59,10 +58,10 @@ static void ieee80211_configure_filter(struct ieee80211_local *local)
        unsigned int changed_flags;
        unsigned int new_flags = 0;
 
-       if (local->iff_promiscs)
+       if (atomic_read(&local->iff_promiscs))
                new_flags |= FIF_PROMISC_IN_BSS;
 
-       if (local->iff_allmultis)
+       if (atomic_read(&local->iff_allmultis))
                new_flags |= FIF_ALLMULTI;
 
        if (local->monitors)
@@ -93,14 +92,13 @@ static int ieee80211_master_open(struct net_device *dev)
        struct ieee80211_sub_if_data *sdata;
        int res = -EOPNOTSUPP;
 
-       read_lock(&local->sub_if_lock);
-       list_for_each_entry(sdata, &local->sub_if_list, list) {
+       /* 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)) {
                        res = 0;
                        break;
                }
        }
-       read_unlock(&local->sub_if_lock);
        return res;
 }
 
@@ -109,11 +107,10 @@ static int ieee80211_master_stop(struct net_device *dev)
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_sub_if_data *sdata;
 
-       read_lock(&local->sub_if_lock);
-       list_for_each_entry(sdata, &local->sub_if_list, list)
+       /* 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))
                        dev_close(sdata->dev);
-       read_unlock(&local->sub_if_lock);
 
        return 0;
 }
@@ -125,152 +122,6 @@ static void ieee80211_master_set_multicast_list(struct net_device *dev)
        ieee80211_configure_filter(local);
 }
 
-/* management interface */
-
-static void
-ieee80211_fill_frame_info(struct ieee80211_local *local,
-                         struct ieee80211_frame_info *fi,
-                         struct ieee80211_rx_status *status)
-{
-       if (status) {
-               struct timespec ts;
-               struct ieee80211_rate *rate;
-
-               jiffies_to_timespec(jiffies, &ts);
-               fi->hosttime = cpu_to_be64((u64) ts.tv_sec * 1000000 +
-                                          ts.tv_nsec / 1000);
-               fi->mactime = cpu_to_be64(status->mactime);
-               switch (status->phymode) {
-               case MODE_IEEE80211A:
-                       fi->phytype = htonl(ieee80211_phytype_ofdm_dot11_a);
-                       break;
-               case MODE_IEEE80211B:
-                       fi->phytype = htonl(ieee80211_phytype_dsss_dot11_b);
-                       break;
-               case MODE_IEEE80211G:
-                       fi->phytype = htonl(ieee80211_phytype_pbcc_dot11_g);
-                       break;
-               default:
-                       fi->phytype = htonl(0xAAAAAAAA);
-                       break;
-               }
-               fi->channel = htonl(status->channel);
-               rate = ieee80211_get_rate(local, status->phymode,
-                                         status->rate);
-               if (rate) {
-                       fi->datarate = htonl(rate->rate);
-                       if (rate->flags & IEEE80211_RATE_PREAMBLE2) {
-                               if (status->rate == rate->val)
-                                       fi->preamble = htonl(2); /* long */
-                               else if (status->rate == rate->val2)
-                                       fi->preamble = htonl(1); /* short */
-                       } else
-                               fi->preamble = htonl(0);
-               } else {
-                       fi->datarate = htonl(0);
-                       fi->preamble = htonl(0);
-               }
-
-               fi->antenna = htonl(status->antenna);
-               fi->priority = htonl(0xffffffff); /* no clue */
-               fi->ssi_type = htonl(ieee80211_ssi_raw);
-               fi->ssi_signal = htonl(status->ssi);
-               fi->ssi_noise = 0x00000000;
-               fi->encoding = 0;
-       } else {
-               /* clear everything because we really don't know.
-                * the msg_type field isn't present on monitor frames
-                * so we don't know whether it will be present or not,
-                * but it's ok to not clear it since it'll be assigned
-                * anyway */
-               memset(fi, 0, sizeof(*fi) - sizeof(fi->msg_type));
-
-               fi->ssi_type = htonl(ieee80211_ssi_none);
-       }
-       fi->version = htonl(IEEE80211_FI_VERSION);
-       fi->length = cpu_to_be32(sizeof(*fi) - sizeof(fi->msg_type));
-}
-
-/* this routine is actually not just for this, but also
- * for pushing fake 'management' frames into userspace.
- * it shall be replaced by a netlink-based system. */
-void
-ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb,
-                 struct ieee80211_rx_status *status, u32 msg_type)
-{
-       struct ieee80211_frame_info *fi;
-       const size_t hlen = sizeof(struct ieee80211_frame_info);
-       struct net_device *dev = local->apdev;
-
-       skb->dev = dev;
-
-       if (skb_headroom(skb) < hlen) {
-               I802_DEBUG_INC(local->rx_expand_skb_head);
-               if (pskb_expand_head(skb, hlen, 0, GFP_ATOMIC)) {
-                       dev_kfree_skb(skb);
-                       return;
-               }
-       }
-
-       fi = (struct ieee80211_frame_info *) skb_push(skb, hlen);
-
-       ieee80211_fill_frame_info(local, fi, status);
-       fi->msg_type = htonl(msg_type);
-
-       dev->stats.rx_packets++;
-       dev->stats.rx_bytes += skb->len;
-
-       skb_set_mac_header(skb, 0);
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       skb->pkt_type = PACKET_OTHERHOST;
-       skb->protocol = htons(ETH_P_802_2);
-       memset(skb->cb, 0, sizeof(skb->cb));
-       netif_rx(skb);
-}
-
-static int ieee80211_mgmt_open(struct net_device *dev)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-       if (!netif_running(local->mdev))
-               return -EOPNOTSUPP;
-       return 0;
-}
-
-static int ieee80211_mgmt_stop(struct net_device *dev)
-{
-       return 0;
-}
-
-static int ieee80211_change_mtu_apdev(struct net_device *dev, int new_mtu)
-{
-       /* FIX: what would be proper limits for MTU?
-        * This interface uses 802.11 frames. */
-       if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) {
-               printk(KERN_WARNING "%s: invalid MTU %d\n",
-                      dev->name, new_mtu);
-               return -EINVAL;
-       }
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-       printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-       dev->mtu = new_mtu;
-       return 0;
-}
-
-void ieee80211_if_mgmt_setup(struct net_device *dev)
-{
-       ether_setup(dev);
-       dev->hard_start_xmit = ieee80211_mgmt_start_xmit;
-       dev->change_mtu = ieee80211_change_mtu_apdev;
-       dev->open = ieee80211_mgmt_open;
-       dev->stop = ieee80211_mgmt_stop;
-       dev->type = ARPHRD_IEEE80211_PRISM;
-       dev->uninit = ieee80211_if_reinit;
-       dev->destructor = ieee80211_if_free;
-}
-
 /* regular interfaces */
 
 static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
@@ -314,22 +165,50 @@ static int ieee80211_open(struct net_device *dev)
        int res;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       read_lock(&local->sub_if_lock);
-       list_for_each_entry(nsdata, &local->sub_if_list, list) {
+
+       /* we hold the RTNL here so can safely walk the list */
+       list_for_each_entry(nsdata, &local->interfaces, list) {
                struct net_device *ndev = nsdata->dev;
 
                if (ndev != dev && ndev != local->mdev && netif_running(ndev) &&
-                   compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0 &&
-                   !identical_mac_addr_allowed(sdata->type, nsdata->type)) {
-                       read_unlock(&local->sub_if_lock);
-                       return -ENOTUNIQ;
+                   compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0) {
+                       /*
+                        * check whether it may have the same address
+                        */
+                       if (!identical_mac_addr_allowed(sdata->type,
+                                                       nsdata->type))
+                               return -ENOTUNIQ;
+
+                       /*
+                        * can only add VLANs to enabled APs
+                        */
+                       if (sdata->type == IEEE80211_IF_TYPE_VLAN &&
+                           nsdata->type == IEEE80211_IF_TYPE_AP &&
+                           netif_running(nsdata->dev))
+                               sdata->u.vlan.ap = nsdata;
                }
        }
-       read_unlock(&local->sub_if_lock);
 
-       if (sdata->type == IEEE80211_IF_TYPE_WDS &&
-           is_zero_ether_addr(sdata->u.wds.remote_addr))
-               return -ENOLINK;
+       switch (sdata->type) {
+       case IEEE80211_IF_TYPE_WDS:
+               if (is_zero_ether_addr(sdata->u.wds.remote_addr))
+                       return -ENOLINK;
+               break;
+       case IEEE80211_IF_TYPE_VLAN:
+               if (!sdata->u.vlan.ap)
+                       return -ENOLINK;
+               break;
+       case IEEE80211_IF_TYPE_AP:
+       case IEEE80211_IF_TYPE_STA:
+       case IEEE80211_IF_TYPE_MNTR:
+       case IEEE80211_IF_TYPE_IBSS:
+               /* no special treatment */
+               break;
+       case IEEE80211_IF_TYPE_INVALID:
+               /* cannot happen */
+               WARN_ON(1);
+               break;
+       }
 
        if (local->open_count == 0) {
                res = 0;
@@ -337,9 +216,14 @@ static int ieee80211_open(struct net_device *dev)
                        res = local->ops->start(local_to_hw(local));
                if (res)
                        return res;
+               ieee80211_hw_config(local);
        }
 
        switch (sdata->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:
                /* must be before the call to ieee80211_configure_filter */
                local->monitors++;
@@ -349,7 +233,6 @@ static int ieee80211_open(struct net_device *dev)
                        netif_tx_unlock_bh(local->mdev);
 
                        local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
-                       ieee80211_hw_config(local);
                }
                break;
        case IEEE80211_IF_TYPE_STA:
@@ -371,7 +254,7 @@ static int ieee80211_open(struct net_device *dev)
                ieee80211_enable_keys(sdata);
 
                if (sdata->type == IEEE80211_IF_TYPE_STA &&
-                   !local->user_space_mlme)
+                   !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
                        netif_carrier_off(dev);
                else
                        netif_carrier_on(dev);
@@ -380,14 +263,21 @@ static int ieee80211_open(struct net_device *dev)
        if (local->open_count == 0) {
                res = dev_open(local->mdev);
                WARN_ON(res);
-               if (local->apdev) {
-                       res = dev_open(local->apdev);
-                       WARN_ON(res);
-               }
                tasklet_enable(&local->tx_pending_tasklet);
                tasklet_enable(&local->tasklet);
        }
 
+       /*
+        * set_multicast_list will be invoked by the networking core
+        * which will check whether any increments here were done in
+        * error and sync them down to the hardware as filter flags.
+        */
+       if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
+               atomic_inc(&local->iff_allmultis);
+
+       if (sdata->flags & IEEE80211_SDATA_PROMISC)
+               atomic_inc(&local->iff_promiscs);
+
        local->open_count++;
 
        netif_start_queue(dev);
@@ -405,11 +295,38 @@ static int ieee80211_stop(struct net_device *dev)
 
        netif_stop_queue(dev);
 
+       /*
+        * Don't count this interface for promisc/allmulti while it
+        * is down. dev_mc_unsync() will invoke set_multicast_list
+        * on the master interface which will sync these down to the
+        * hardware as filter flags.
+        */
+       if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
+               atomic_dec(&local->iff_allmultis);
+
+       if (sdata->flags & IEEE80211_SDATA_PROMISC)
+               atomic_dec(&local->iff_promiscs);
+
        dev_mc_unsync(local->mdev, dev);
 
+       /* down all dependent devices, that is VLANs */
+       if (sdata->type == IEEE80211_IF_TYPE_AP) {
+               struct ieee80211_sub_if_data *vlan, *tmp;
+
+               list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans,
+                                        u.vlan.list)
+                       dev_close(vlan->dev);
+               WARN_ON(!list_empty(&sdata->u.ap.vlans));
+       }
+
        local->open_count--;
 
        switch (sdata->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:
                local->monitors--;
                if (local->monitors == 0) {
@@ -417,8 +334,7 @@ static int ieee80211_stop(struct net_device *dev)
                        ieee80211_configure_filter(local);
                        netif_tx_unlock_bh(local->mdev);
 
-                       local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
-                       ieee80211_hw_config(local);
+                       local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
                }
                break;
        case IEEE80211_IF_TYPE_STA:
@@ -426,14 +342,13 @@ static int ieee80211_stop(struct net_device *dev)
                sdata->u.sta.state = IEEE80211_DISABLED;
                del_timer_sync(&sdata->u.sta.timer);
                /*
-                * Holding the sub_if_lock for writing here blocks
-                * out the receive path and makes sure it's not
-                * currently processing a packet that may get
-                * added to the queue.
+                * When we get here, the interface is marked down.
+                * Call synchronize_rcu() to wait for the RX path
+                * should it be using the interface and enqueuing
+                * frames at this very time on another CPU.
                 */
-               write_lock_bh(&local->sub_if_lock);
+               synchronize_rcu();
                skb_queue_purge(&sdata->u.sta.skb_queue);
-               write_unlock_bh(&local->sub_if_lock);
 
                if (!local->ops->hw_scan &&
                    local->scan_dev == sdata->dev) {
@@ -441,6 +356,11 @@ static int ieee80211_stop(struct net_device *dev)
                        cancel_delayed_work(&local->scan_work);
                }
                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;
+               sdata->u.sta.extra_ie_len = 0;
                /* fall through */
        default:
                conf.if_id = dev->ifindex;
@@ -455,9 +375,6 @@ static int ieee80211_stop(struct net_device *dev)
                if (netif_running(local->mdev))
                        dev_close(local->mdev);
 
-               if (local->apdev)
-                       dev_close(local->apdev);
-
                if (local->ops->stop)
                        local->ops->stop(local_to_hw(local));
 
@@ -476,22 +393,22 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
 
        allmulti = !!(dev->flags & IFF_ALLMULTI);
        promisc = !!(dev->flags & IFF_PROMISC);
-       sdata_allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI;
-       sdata_promisc = sdata->flags & IEEE80211_SDATA_PROMISC;
+       sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
+       sdata_promisc = !!(sdata->flags & IEEE80211_SDATA_PROMISC);
 
        if (allmulti != sdata_allmulti) {
                if (dev->flags & IFF_ALLMULTI)
-                       local->iff_allmultis++;
+                       atomic_inc(&local->iff_allmultis);
                else
-                       local->iff_allmultis--;
+                       atomic_dec(&local->iff_allmultis);
                sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
        }
 
        if (promisc != sdata_promisc) {
                if (dev->flags & IFF_PROMISC)
-                       local->iff_promiscs++;
+                       atomic_inc(&local->iff_promiscs);
                else
-                       local->iff_promiscs--;
+                       atomic_dec(&local->iff_promiscs);
                sdata->flags ^= IEEE80211_SDATA_PROMISC;
        }
 
@@ -506,18 +423,16 @@ static const struct header_ops ieee80211_header_ops = {
        .cache_update   = eth_header_cache_update,
 };
 
-/* Must not be called for mdev and apdev */
+/* Must not be called for mdev */
 void ieee80211_if_setup(struct net_device *dev)
 {
        ether_setup(dev);
-       dev->header_ops = &ieee80211_header_ops;
        dev->hard_start_xmit = ieee80211_subif_start_xmit;
        dev->wireless_handlers = &ieee80211_iw_handler_def;
        dev->set_multicast_list = ieee80211_set_multicast_list;
        dev->change_mtu = ieee80211_change_mtu;
        dev->open = ieee80211_open;
        dev->stop = ieee80211_stop;
-       dev->uninit = ieee80211_if_reinit;
        dev->destructor = ieee80211_if_free;
 }
 
@@ -576,13 +491,9 @@ static int __ieee80211_if_config(struct net_device *dev,
                conf.bssid = sdata->u.sta.bssid;
                conf.ssid = sdata->u.sta.ssid;
                conf.ssid_len = sdata->u.sta.ssid_len;
-               conf.generic_elem = sdata->u.sta.extra_ie;
-               conf.generic_elem_len = sdata->u.sta.extra_ie_len;
        } else if (sdata->type == IEEE80211_IF_TYPE_AP) {
                conf.ssid = sdata->u.ap.ssid;
                conf.ssid_len = sdata->u.ap.ssid_len;
-               conf.generic_elem = sdata->u.ap.generic_elem;
-               conf.generic_elem_len = sdata->u.ap.generic_elem_len;
                conf.beacon = beacon;
                conf.beacon_control = control;
        }
@@ -643,7 +554,7 @@ int ieee80211_hw_config(struct ieee80211_local *local)
               local->hw.conf.phymode);
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
-       if (local->ops->config)
+       if (local->open_count)
                ret = local->ops->config(local_to_hw(local), &local->hw.conf);
 
        return ret;
@@ -767,8 +678,6 @@ static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
                pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
        if (control->flags & IEEE80211_TXCTL_REQUEUE)
                pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
-       if (control->type == IEEE80211_IF_TYPE_MGMT)
-               pkt_data->flags |= IEEE80211_TXPD_MGMT_IFACE;
        pkt_data->queue = control->queue;
 
        hdrlen = ieee80211_get_hdrlen_from_skb(skb);
@@ -821,7 +730,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_local *local = hw_to_local(hw);
        u16 frag, type;
-       u32 msg_type;
        struct ieee80211_tx_status_rtap_hdr *rthdr;
        struct ieee80211_sub_if_data *sdata;
        int monitors;
@@ -936,29 +844,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
                        local->dot11FailedCount++;
        }
 
-       msg_type = (status->flags & IEEE80211_TX_STATUS_ACK) ?
-               ieee80211_msg_tx_callback_ack : ieee80211_msg_tx_callback_fail;
-
        /* this was a transmitted frame, but now we want to reuse it */
        skb_orphan(skb);
 
-       if ((status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS) &&
-           local->apdev) {
-               if (local->monitors) {
-                       skb2 = skb_clone(skb, GFP_ATOMIC);
-               } else {
-                       skb2 = skb;
-                       skb = NULL;
-               }
-
-               if (skb2)
-                       /* Send frame to hostapd */
-                       ieee80211_rx_mgmt(local, skb2, NULL, msg_type);
-
-               if (!skb)
-                       return;
-       }
-
        if (!local->monitors) {
                dev_kfree_skb(skb);
                return;
@@ -993,9 +881,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
 
        rthdr->data_retries = status->retry_count;
 
-       read_lock(&local->sub_if_lock);
+       rcu_read_lock();
        monitors = local->monitors;
-       list_for_each_entry(sdata, &local->sub_if_list, list) {
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                /*
                 * Using the monitors counter is possibly racy, but
                 * if the value is wrong we simply either clone the skb
@@ -1011,7 +899,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
                                continue;
                        monitors--;
                        if (monitors)
-                               skb2 = skb_clone(skb, GFP_KERNEL);
+                               skb2 = skb_clone(skb, GFP_ATOMIC);
                        else
                                skb2 = NULL;
                        skb->dev = sdata->dev;
@@ -1026,7 +914,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
                }
        }
  out:
-       read_unlock(&local->sub_if_lock);
+       rcu_read_unlock();
        if (skb)
                dev_kfree_skb(skb);
 }
@@ -1114,8 +1002,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 
        INIT_LIST_HEAD(&local->modes_list);
 
-       rwlock_init(&local->sub_if_lock);
-       INIT_LIST_HEAD(&local->sub_if_list);
+       INIT_LIST_HEAD(&local->interfaces);
 
        INIT_DELAYED_WORK(&local->scan_work, ieee80211_sta_scan_work);
        ieee80211_rx_bss_list_init(mdev);
@@ -1135,7 +1022,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        sdata->u.ap.force_unicast_rateidx = -1;
        sdata->u.ap.max_ratectrl_rateidx = -1;
        ieee80211_if_sdata_init(sdata);
-       list_add_tail(&sdata->list, &local->sub_if_list);
+       /* no RCU needed since we're still during init phase */
+       list_add_tail(&sdata->list, &local->interfaces);
 
        tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
                     (unsigned long)local);
@@ -1208,8 +1096,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                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, NULL);
+       result = ieee80211_init_rate_ctrl_alg(local,
+                                             hw->rate_control_algorithm);
        if (result < 0) {
                printk(KERN_DEBUG "%s: Failed to initialize rate control "
                       "algorithm\n", wiphy_name(local->hw.wiphy));
@@ -1294,7 +1184,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata, *tmp;
-       struct list_head tmp_list;
        int i;
 
        tasklet_kill(&local->tx_pending_tasklet);
@@ -1305,15 +1194,28 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
        BUG_ON(local->reg_state != IEEE80211_DEV_REGISTERED);
 
        local->reg_state = IEEE80211_DEV_UNREGISTERED;
-       if (local->apdev)
-               ieee80211_if_del_mgmt(local);
 
-       write_lock_bh(&local->sub_if_lock);
-       list_replace_init(&local->sub_if_list, &tmp_list);
-       write_unlock_bh(&local->sub_if_lock);
+       /*
+        * At this point, interface list manipulations are fine
+        * because the driver cannot be handing us frames any
+        * more and the tasklet is killed.
+        */
 
-       list_for_each_entry_safe(sdata, tmp, &tmp_list, list)
+       /*
+        * 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);
+       }
+
+       /* then, finally, remove the master interface */
+       __ieee80211_if_del(local, IEEE80211_DEV_TO_SUB_IF(local->mdev));
 
        rtnl_unlock();
 
@@ -1358,8 +1260,17 @@ static int __init ieee80211_init(void)
 
        BUILD_BUG_ON(sizeof(struct ieee80211_tx_packet_data) > sizeof(skb->cb));
 
+#ifdef CONFIG_MAC80211_RCSIMPLE
+       ret = ieee80211_rate_control_register(&mac80211_rcsimple);
+       if (ret)
+               return ret;
+#endif
+
        ret = ieee80211_wme_register();
        if (ret) {
+#ifdef CONFIG_MAC80211_RCSIMPLE
+               ieee80211_rate_control_unregister(&mac80211_rcsimple);
+#endif
                printk(KERN_DEBUG "ieee80211_init: failed to "
                       "initialize WME (err=%d)\n", ret);
                return ret;
@@ -1373,6 +1284,10 @@ static int __init ieee80211_init(void)
 
 static void __exit ieee80211_exit(void)
 {
+#ifdef CONFIG_MAC80211_RCSIMPLE
+       ieee80211_rate_control_unregister(&mac80211_rcsimple);
+#endif
+
        ieee80211_wme_unregister();
        ieee80211_debugfs_netdev_exit();
 }