Pull bugzilla-9345 into release branch
[sfrench/cifs-2.6.git] / net / mac80211 / ieee80211.c
index 0c1f7b2e157c5211bcdc9611dd163a6f66a21332..505af1f067ab06620fcb2050a2725a711c321f66 100644 (file)
 #include <net/net_namespace.h>
 #include <net/cfg80211.h>
 
-#include "ieee80211_common.h"
 #include "ieee80211_i.h"
 #include "ieee80211_rate.h"
 #include "wep.h"
 #include "wme.h"
 #include "aes_ccm.h"
 #include "ieee80211_led.h"
-#include "ieee80211_cfg.h"
+#include "cfg.h"
 #include "debugfs.h"
 #include "debugfs_netdev.h"
 
@@ -47,12 +46,44 @@ struct ieee80211_tx_status_rtap_hdr {
 
 /* common interface routines */
 
-static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr)
+static int header_parse_80211(const struct sk_buff *skb, unsigned char *haddr)
 {
        memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
        return ETH_ALEN;
 }
 
+/* must be called under mdev tx lock */
+static void ieee80211_configure_filter(struct ieee80211_local *local)
+{
+       unsigned int changed_flags;
+       unsigned int new_flags = 0;
+
+       if (atomic_read(&local->iff_promiscs))
+               new_flags |= FIF_PROMISC_IN_BSS;
+
+       if (atomic_read(&local->iff_allmultis))
+               new_flags |= FIF_ALLMULTI;
+
+       if (local->monitors)
+               new_flags |= FIF_CONTROL |
+                            FIF_OTHER_BSS |
+                            FIF_BCN_PRBRESP_PROMISC;
+
+       changed_flags = local->filter_flags ^ new_flags;
+
+       /* be a bit nasty */
+       new_flags |= (1<<31);
+
+       local->ops->configure_filter(local_to_hw(local),
+                                    changed_flags, &new_flags,
+                                    local->mdev->mc_count,
+                                    local->mdev->mc_list);
+
+       WARN_ON(new_flags & (1<<31));
+
+       local->filter_flags = new_flags & ~(1<<31);
+}
+
 /* master interface */
 
 static int ieee80211_master_open(struct net_device *dev)
@@ -61,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;
 }
 
@@ -77,195 +107,19 @@ 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;
 }
 
-/* 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);
-}
-
-void ieee80211_key_threshold_notify(struct net_device *dev,
-                                   struct ieee80211_key *key,
-                                   struct sta_info *sta)
+static void ieee80211_master_set_multicast_list(struct net_device *dev)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       struct sk_buff *skb;
-       struct ieee80211_msg_key_notification *msg;
-
-       /* if no one will get it anyway, don't even allocate it.
-        * unlikely because this is only relevant for APs
-        * where the device must be open... */
-       if (unlikely(!local->apdev))
-               return;
-
-       skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
-                           sizeof(struct ieee80211_msg_key_notification));
-       if (!skb)
-               return;
-
-       skb_reserve(skb, sizeof(struct ieee80211_frame_info));
-       msg = (struct ieee80211_msg_key_notification *)
-               skb_put(skb, sizeof(struct ieee80211_msg_key_notification));
-       msg->tx_rx_count = key->tx_rx_count;
-       memcpy(msg->ifname, dev->name, IFNAMSIZ);
-       if (sta)
-               memcpy(msg->addr, sta->addr, ETH_ALEN);
-       else
-               memset(msg->addr, 0xff, ETH_ALEN);
-
-       key->tx_rx_count = 0;
-
-       ieee80211_rx_mgmt(local, skb, NULL,
-                         ieee80211_msg_key_threshold_notification);
-}
-
-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->hard_header_parse = header_parse_80211;
-       dev->uninit = ieee80211_if_reinit;
-       dev->destructor = ieee80211_if_free;
+       ieee80211_configure_filter(local);
 }
 
 /* regular interfaces */
@@ -303,49 +157,6 @@ static inline int identical_mac_addr_allowed(int type1, int type2)
                  type2 == IEEE80211_IF_TYPE_VLAN)));
 }
 
-/* Check if running monitor interfaces should go to a "soft monitor" mode
- * and switch them if necessary. */
-static inline void ieee80211_start_soft_monitor(struct ieee80211_local *local)
-{
-       struct ieee80211_if_init_conf conf;
-
-       if (local->open_count && local->open_count == local->monitors &&
-           !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER) &&
-           local->ops->remove_interface) {
-               conf.if_id = -1;
-               conf.type = IEEE80211_IF_TYPE_MNTR;
-               conf.mac_addr = NULL;
-               local->ops->remove_interface(local_to_hw(local), &conf);
-       }
-}
-
-/* Check if running monitor interfaces should go to a "hard monitor" mode
- * and switch them if necessary. */
-static void ieee80211_start_hard_monitor(struct ieee80211_local *local)
-{
-       struct ieee80211_if_init_conf conf;
-
-       if (local->open_count && local->open_count == local->monitors &&
-           !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
-               conf.if_id = -1;
-               conf.type = IEEE80211_IF_TYPE_MNTR;
-               conf.mac_addr = NULL;
-               local->ops->add_interface(local_to_hw(local), &conf);
-       }
-}
-
-static void ieee80211_if_open(struct net_device *dev)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       switch (sdata->type) {
-       case IEEE80211_IF_TYPE_STA:
-       case IEEE80211_IF_TYPE_IBSS:
-               sdata->u.sta.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
-               break;
-       }
-}
-
 static int ieee80211_open(struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata, *nsdata;
@@ -354,114 +165,190 @@ 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;
-
-       if (sdata->type == IEEE80211_IF_TYPE_MNTR && local->open_count &&
-           !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
-               /* run the interface in a "soft monitor" mode */
-               local->monitors++;
-               local->open_count++;
-               local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
-               return 0;
-       }
-       ieee80211_if_open(dev);
-       ieee80211_start_soft_monitor(local);
 
-       conf.if_id = dev->ifindex;
-       conf.type = sdata->type;
-       if (sdata->type == IEEE80211_IF_TYPE_MNTR)
-               conf.mac_addr = NULL;
-       else
-               conf.mac_addr = dev->dev_addr;
-       res = local->ops->add_interface(local_to_hw(local), &conf);
-       if (res) {
-               if (sdata->type == IEEE80211_IF_TYPE_MNTR)
-                       ieee80211_start_hard_monitor(local);
-               return res;
+       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;
-               tasklet_enable(&local->tx_pending_tasklet);
-               tasklet_enable(&local->tasklet);
-               if (local->ops->open)
-                       res = local->ops->open(local_to_hw(local));
-               if (res == 0) {
-                       res = dev_open(local->mdev);
-                       if (res) {
-                               if (local->ops->stop)
-                                       local->ops->stop(local_to_hw(local));
-                       } else {
-                               res = ieee80211_hw_config(local);
-                               if (res && local->ops->stop)
-                                       local->ops->stop(local_to_hw(local));
-                               else if (!res && local->apdev)
-                                       dev_open(local->apdev);
-                       }
-               }
-               if (res) {
-                       if (local->ops->remove_interface)
-                               local->ops->remove_interface(local_to_hw(local),
-                                                           &conf);
+               if (local->ops->start)
+                       res = local->ops->start(local_to_hw(local));
+               if (res)
                        return res;
-               }
+               ieee80211_hw_config(local);
        }
-       local->open_count++;
 
-       if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
+       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++;
-               local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
-       } else {
+               if (local->monitors == 1) {
+                       netif_tx_lock_bh(local->mdev);
+                       ieee80211_configure_filter(local);
+                       netif_tx_unlock_bh(local->mdev);
+
+                       local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
+               }
+               break;
+       case IEEE80211_IF_TYPE_STA:
+       case IEEE80211_IF_TYPE_IBSS:
+               sdata->u.sta.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+               /* fall through */
+       default:
+               conf.if_id = dev->ifindex;
+               conf.type = sdata->type;
+               conf.mac_addr = dev->dev_addr;
+               res = local->ops->add_interface(local_to_hw(local), &conf);
+               if (res && !local->open_count && local->ops->stop)
+                       local->ops->stop(local_to_hw(local));
+               if (res)
+                       return res;
+
                ieee80211_if_config(dev);
                ieee80211_reset_erp_info(dev);
                ieee80211_enable_keys(sdata);
+
+               if (sdata->type == IEEE80211_IF_TYPE_STA &&
+                   !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
+                       netif_carrier_off(dev);
+               else
+                       netif_carrier_on(dev);
        }
 
-       if (sdata->type == IEEE80211_IF_TYPE_STA &&
-           !local->user_space_mlme)
-               netif_carrier_off(dev);
-       else
-               netif_carrier_on(dev);
+       if (local->open_count == 0) {
+               res = dev_open(local->mdev);
+               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);
+
        return 0;
 }
 
-static void ieee80211_if_shutdown(struct net_device *dev)
+static int ieee80211_stop(struct net_device *dev)
 {
+       struct ieee80211_sub_if_data *sdata;
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_if_init_conf conf;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(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--;
 
-       ASSERT_RTNL();
        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) {
+                       netif_tx_lock_bh(local->mdev);
+                       ieee80211_configure_filter(local);
+                       netif_tx_unlock_bh(local->mdev);
+
+                       local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
+               }
+               break;
        case IEEE80211_IF_TYPE_STA:
        case IEEE80211_IF_TYPE_IBSS:
                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) {
@@ -469,129 +356,84 @@ static void ieee80211_if_shutdown(struct net_device *dev)
                        cancel_delayed_work(&local->scan_work);
                }
                flush_workqueue(local->hw.workqueue);
-               break;
-       }
-}
-
-static int ieee80211_stop(struct net_device *dev)
-{
-       struct ieee80211_sub_if_data *sdata;
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       if (sdata->type == IEEE80211_IF_TYPE_MNTR &&
-           local->open_count > 1 &&
-           !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
-               /* remove "soft monitor" interface */
-               local->open_count--;
-               local->monitors--;
-               if (!local->monitors)
-                       local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
-               return 0;
-       }
-
-       netif_stop_queue(dev);
-       ieee80211_if_shutdown(dev);
 
-       if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
-               local->monitors--;
-               if (!local->monitors)
-                       local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
-       } else {
+               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;
+               conf.type = sdata->type;
+               conf.mac_addr = dev->dev_addr;
                /* disable all keys for as long as this netdev is down */
                ieee80211_disable_keys(sdata);
+               local->ops->remove_interface(local_to_hw(local), &conf);
        }
 
-       local->open_count--;
        if (local->open_count == 0) {
                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));
+
                tasklet_disable(&local->tx_pending_tasklet);
                tasklet_disable(&local->tasklet);
        }
-       if (local->ops->remove_interface) {
-               struct ieee80211_if_init_conf conf;
-
-               conf.if_id = dev->ifindex;
-               conf.type = sdata->type;
-               conf.mac_addr = dev->dev_addr;
-               local->ops->remove_interface(local_to_hw(local), &conf);
-       }
-
-       ieee80211_start_hard_monitor(local);
 
        return 0;
 }
 
-enum netif_tx_lock_class {
-       TX_LOCK_NORMAL,
-       TX_LOCK_MASTER,
-};
-
-static inline void netif_tx_lock_nested(struct net_device *dev, int subclass)
-{
-       spin_lock_nested(&dev->_xmit_lock, subclass);
-       dev->xmit_lock_owner = smp_processor_id();
-}
-
 static void ieee80211_set_multicast_list(struct net_device *dev)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       unsigned short flags;
+       int allmulti, promisc, sdata_allmulti, sdata_promisc;
+
+       allmulti = !!(dev->flags & IFF_ALLMULTI);
+       promisc = !!(dev->flags & IFF_PROMISC);
+       sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
+       sdata_promisc = !!(sdata->flags & IEEE80211_SDATA_PROMISC);
 
-       netif_tx_lock_nested(local->mdev, TX_LOCK_MASTER);
-       if (((dev->flags & IFF_ALLMULTI) != 0) ^
-           ((sdata->flags & IEEE80211_SDATA_ALLMULTI) != 0)) {
-               if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
-                       local->iff_allmultis--;
+       if (allmulti != sdata_allmulti) {
+               if (dev->flags & IFF_ALLMULTI)
+                       atomic_inc(&local->iff_allmultis);
                else
-                       local->iff_allmultis++;
+                       atomic_dec(&local->iff_allmultis);
                sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
        }
-       if (((dev->flags & IFF_PROMISC) != 0) ^
-           ((sdata->flags & IEEE80211_SDATA_PROMISC) != 0)) {
-               if (sdata->flags & IEEE80211_SDATA_PROMISC)
-                       local->iff_promiscs--;
+
+       if (promisc != sdata_promisc) {
+               if (dev->flags & IFF_PROMISC)
+                       atomic_inc(&local->iff_promiscs);
                else
-                       local->iff_promiscs++;
+                       atomic_dec(&local->iff_promiscs);
                sdata->flags ^= IEEE80211_SDATA_PROMISC;
        }
-       if (dev->mc_count != sdata->mc_count) {
-               local->mc_count = local->mc_count - sdata->mc_count +
-                                 dev->mc_count;
-               sdata->mc_count = dev->mc_count;
-       }
-       if (local->ops->set_multicast_list) {
-               flags = local->mdev->flags;
-               if (local->iff_allmultis)
-                       flags |= IFF_ALLMULTI;
-               if (local->iff_promiscs)
-                       flags |= IFF_PROMISC;
-               read_lock(&local->sub_if_lock);
-               local->ops->set_multicast_list(local_to_hw(local), flags,
-                                             local->mc_count);
-               read_unlock(&local->sub_if_lock);
-       }
-       netif_tx_unlock(local->mdev);
+
+       dev_mc_sync(local->mdev, dev);
 }
 
-/* Must not be called for mdev and apdev */
+static const struct header_ops ieee80211_header_ops = {
+       .create         = eth_header,
+       .parse          = header_parse_80211,
+       .rebuild        = eth_rebuild_header,
+       .cache          = eth_header_cache,
+       .cache_update   = eth_header_cache_update,
+};
+
+/* 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;
 }
 
@@ -602,6 +444,7 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct sta_info *sta;
+       DECLARE_MAC_BUF(mac);
 
        if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
                return 0;
@@ -619,8 +462,8 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
                sta_info_put(sta);
        } else {
                printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
-                      "peer " MAC_FMT "\n",
-                      dev->name, MAC_ARG(sdata->u.wds.remote_addr));
+                      "peer %s\n",
+                      dev->name, print_mac(mac, sdata->u.wds.remote_addr));
        }
 
        /* Update WDS link data */
@@ -638,7 +481,6 @@ static 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);
        struct ieee80211_if_conf conf;
-       static u8 scan_bssid[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
        if (!local->ops->config_interface || !netif_running(dev))
                return 0;
@@ -647,20 +489,12 @@ static int __ieee80211_if_config(struct net_device *dev,
        conf.type = sdata->type;
        if (sdata->type == IEEE80211_IF_TYPE_STA ||
            sdata->type == IEEE80211_IF_TYPE_IBSS) {
-               if (local->sta_scanning &&
-                   local->scan_dev == dev)
-                       conf.bssid = scan_bssid;
-               else
-                       conf.bssid = sdata->u.sta.bssid;
+               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;
        }
@@ -703,7 +537,12 @@ int ieee80211_hw_config(struct ieee80211_local *local)
 
        local->hw.conf.channel = chan->chan;
        local->hw.conf.channel_val = chan->val;
-       local->hw.conf.power_level = chan->power_level;
+       if (!local->hw.conf.power_level) {
+               local->hw.conf.power_level = chan->power_level;
+       } else {
+               local->hw.conf.power_level = min(chan->power_level,
+                                                local->hw.conf.power_level);
+       }
        local->hw.conf.freq = chan->freq;
        local->hw.conf.phymode = mode->mode;
        local->hw.conf.antenna_max = chan->antenna_max;
@@ -716,7 +555,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;
@@ -743,37 +582,6 @@ void ieee80211_reset_erp_info(struct net_device *dev)
                                         IEEE80211_ERP_CHANGE_PREAMBLE);
 }
 
-struct dev_mc_list *ieee80211_get_mc_list_item(struct ieee80211_hw *hw,
-                                              struct dev_mc_list *prev,
-                                              void **ptr)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_sub_if_data *sdata = *ptr;
-       struct dev_mc_list *mc;
-
-       if (!prev) {
-               WARN_ON(sdata);
-               sdata = NULL;
-       }
-       if (!prev || !prev->next) {
-               if (sdata)
-                       sdata = list_entry(sdata->list.next,
-                                          struct ieee80211_sub_if_data, list);
-               else
-                       sdata = list_entry(local->sub_if_list.next,
-                                          struct ieee80211_sub_if_data, list);
-               if (&sdata->list != &local->sub_if_list)
-                       mc = sdata->dev->mc_list;
-               else
-                       mc = NULL;
-       } else
-               mc = prev->next;
-
-       *ptr = sdata;
-       return mc;
-}
-EXPORT_SYMBOL(ieee80211_get_mc_list_item);
-
 void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
                                 struct sk_buff *skb,
                                 struct ieee80211_tx_status *status)
@@ -843,7 +651,7 @@ static void ieee80211_tasklet_handler(unsigned long data)
                        break;
                default: /* should never get here! */
                        printk(KERN_ERR "%s: Unknown message type (%d)\n",
-                              local->mdev->name, skb->pkt_type);
+                              wiphy_name(local->hw.wiphy), skb->pkt_type);
                        dev_kfree_skb(skb);
                        break;
                }
@@ -871,8 +679,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);
@@ -925,7 +731,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;
@@ -933,7 +738,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
        if (!status) {
                printk(KERN_ERR
                       "%s: ieee80211_tx_status called with NULL status\n",
-                      local->mdev->name);
+                      wiphy_name(local->hw.wiphy));
                dev_kfree_skb(skb);
                return;
        }
@@ -990,7 +795,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
                                        printk(KERN_DEBUG "%s: dropped TX "
                                               "filtered frame queue_len=%d "
                                               "PS=%d @%lu\n",
-                                              local->mdev->name,
+                                              wiphy_name(local->hw.wiphy),
                                               skb_queue_len(
                                                       &sta->tx_filtered),
                                               !!(sta->flags & WLAN_STA_PS),
@@ -1040,29 +845,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;
@@ -1097,9 +882,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
@@ -1115,7 +900,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;
@@ -1130,7 +915,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);
 }
@@ -1179,8 +964,12 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
                           NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
 
        BUG_ON(!ops->tx);
+       BUG_ON(!ops->start);
+       BUG_ON(!ops->stop);
        BUG_ON(!ops->config);
        BUG_ON(!ops->add_interface);
+       BUG_ON(!ops->remove_interface);
+       BUG_ON(!ops->configure_filter);
        local->ops = ops;
 
        /* for now, mdev needs sub_if_data :/ */
@@ -1214,8 +1003,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);
@@ -1226,7 +1014,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        mdev->open = ieee80211_master_open;
        mdev->stop = ieee80211_master_stop;
        mdev->type = ARPHRD_IEEE80211;
-       mdev->hard_header_parse = header_parse_80211;
+       mdev->header_ops = &ieee80211_header_ops;
+       mdev->set_multicast_list = ieee80211_master_set_multicast_list;
 
        sdata->type = IEEE80211_IF_TYPE_AP;
        sdata->dev = mdev;
@@ -1234,7 +1023,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);
@@ -1307,11 +1097,13 @@ 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", local->mdev->name);
+                      "algorithm\n", wiphy_name(local->hw.wiphy));
                goto fail_rate;
        }
 
@@ -1319,7 +1111,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        if (result < 0) {
                printk(KERN_DEBUG "%s: Failed to initialize wep\n",
-                      local->mdev->name);
+                      wiphy_name(local->hw.wiphy));
                goto fail_wep;
        }
 
@@ -1330,7 +1122,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                                  IEEE80211_IF_TYPE_STA);
        if (result)
                printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
-                      local->mdev->name);
+                      wiphy_name(local->hw.wiphy));
 
        local->reg_state = IEEE80211_DEV_REGISTERED;
        rtnl_unlock();
@@ -1393,7 +1185,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);
@@ -1404,15 +1195,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();
 
@@ -1430,7 +1234,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
        if (skb_queue_len(&local->skb_queue)
                        || skb_queue_len(&local->skb_queue_unreliable))
                printk(KERN_WARNING "%s: skb_queue not empty\n",
-                      local->mdev->name);
+                      wiphy_name(local->hw.wiphy));
        skb_queue_purge(&local->skb_queue);
        skb_queue_purge(&local->skb_queue_unreliable);
 
@@ -1457,8 +1261,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;
@@ -1472,6 +1285,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();
 }