flag parameters: anon_inode_getfd extension
[safe/jmp/linux-2.6] / net / mac80211 / iface.c
index f2aefd4..610ed1d 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include "debugfs_netdev.h"
 #include "mesh.h"
 
-void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
+/*
+ * Called when the netdev is removed or, by the code below, before
+ * the interface type changes.
+ */
+static void ieee80211_teardown_sdata(struct net_device *dev)
 {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct beacon_data *beacon;
+       struct sk_buff *skb;
+       int flushed;
        int i;
 
-       /* Default values for sub-interface parameters */
-       sdata->drop_unencrypted = 0;
+       ieee80211_debugfs_remove_netdev(sdata);
+
+       /* free extra data */
+       ieee80211_free_keys(sdata);
+
        for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
-               skb_queue_head_init(&sdata->fragments[i].skb_list);
+               __skb_queue_purge(&sdata->fragments[i].skb_list);
+       sdata->fragment_next = 0;
 
-       INIT_LIST_HEAD(&sdata->key_list);
+       switch (sdata->vif.type) {
+       case IEEE80211_IF_TYPE_AP:
+               beacon = sdata->u.ap.beacon;
+               rcu_assign_pointer(sdata->u.ap.beacon, NULL);
+               synchronize_rcu();
+               kfree(beacon);
 
-       sdata->force_unicast_rateidx = -1;
-       sdata->max_ratectrl_rateidx = -1;
+               while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
+                       local->total_ps_buffered--;
+                       dev_kfree_skb(skb);
+               }
+
+               break;
+       case IEEE80211_IF_TYPE_MESH_POINT:
+               /* Allow compiler to elide mesh_rmc_free call. */
+               if (ieee80211_vif_is_mesh(&sdata->vif))
+                       mesh_rmc_free(dev);
+               /* fall through */
+       case IEEE80211_IF_TYPE_STA:
+       case IEEE80211_IF_TYPE_IBSS:
+               kfree(sdata->u.sta.extra_ie);
+               kfree(sdata->u.sta.assocreq_ies);
+               kfree(sdata->u.sta.assocresp_ies);
+               kfree_skb(sdata->u.sta.probe_resp);
+               break;
+       case IEEE80211_IF_TYPE_WDS:
+       case IEEE80211_IF_TYPE_VLAN:
+       case IEEE80211_IF_TYPE_MNTR:
+               break;
+       case IEEE80211_IF_TYPE_INVALID:
+               BUG();
+               break;
+       }
+
+       flushed = sta_info_flush(local, sdata);
+       WARN_ON(flushed);
 }
 
-static void ieee80211_if_sdata_deinit(struct ieee80211_sub_if_data *sdata)
+/*
+ * Helper function to initialise an interface to a specific type.
+ */
+static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
+                                 enum ieee80211_if_types type)
 {
-       int i;
+       struct ieee80211_if_sta *ifsta;
 
-       for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
-               __skb_queue_purge(&sdata->fragments[i].skb_list);
+       /* clear type-dependent union */
+       memset(&sdata->u, 0, sizeof(sdata->u));
+
+       /* and set some type-dependent values */
+       sdata->vif.type = type;
+
+       /* only monitor differs */
+       sdata->dev->type = ARPHRD_ETHER;
+
+       switch (type) {
+       case IEEE80211_IF_TYPE_AP:
+               skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
+               INIT_LIST_HEAD(&sdata->u.ap.vlans);
+               break;
+       case IEEE80211_IF_TYPE_MESH_POINT:
+       case IEEE80211_IF_TYPE_STA:
+       case IEEE80211_IF_TYPE_IBSS:
+               ifsta = &sdata->u.sta;
+               INIT_WORK(&ifsta->work, ieee80211_sta_work);
+               setup_timer(&ifsta->timer, ieee80211_sta_timer,
+                           (unsigned long) sdata);
+               skb_queue_head_init(&ifsta->skb_queue);
+
+               ifsta->capab = WLAN_CAPABILITY_ESS;
+               ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
+                       IEEE80211_AUTH_ALG_SHARED_KEY;
+               ifsta->flags |= IEEE80211_STA_CREATE_IBSS |
+                       IEEE80211_STA_AUTO_BSSID_SEL |
+                       IEEE80211_STA_AUTO_CHANNEL_SEL;
+               if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4)
+                       ifsta->flags |= IEEE80211_STA_WMM_ENABLED;
+
+               if (ieee80211_vif_is_mesh(&sdata->vif))
+                       ieee80211_mesh_init_sdata(sdata);
+               break;
+       case IEEE80211_IF_TYPE_MNTR:
+               sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP;
+               sdata->dev->hard_start_xmit = ieee80211_monitor_start_xmit;
+               sdata->u.mntr_flags = MONITOR_FLAG_CONTROL |
+                                     MONITOR_FLAG_OTHER_BSS;
+               break;
+       case IEEE80211_IF_TYPE_WDS:
+       case IEEE80211_IF_TYPE_VLAN:
+               break;
+       case IEEE80211_IF_TYPE_INVALID:
+               BUG();
+               break;
+       }
+
+       ieee80211_debugfs_add_netdev(sdata);
+}
+
+int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
+                            enum ieee80211_if_types type)
+{
+       ASSERT_RTNL();
+
+       if (type == sdata->vif.type)
+               return 0;
+
+       /*
+        * We could, here, on changes between IBSS/STA/MESH modes,
+        * invoke an MLME function instead that disassociates etc.
+        * and goes into the requested mode.
+        */
+
+       if (netif_running(sdata->dev))
+               return -EBUSY;
+
+       /* Purge and reset type-dependent state. */
+       ieee80211_teardown_sdata(sdata->dev);
+       ieee80211_setup_sdata(sdata, type);
+
+       /* reset some values that shouldn't be kept across type changes */
+       sdata->basic_rates = 0;
+       sdata->drop_unencrypted = 0;
+
+       return 0;
 }
 
-/* Must be called with rtnl lock held. */
 int ieee80211_if_add(struct ieee80211_local *local, const char *name,
-                    struct net_device **new_dev, int type,
+                    struct net_device **new_dev, enum ieee80211_if_types type,
                     struct vif_params *params)
 {
        struct net_device *ndev;
        struct ieee80211_sub_if_data *sdata = NULL;
-       int ret;
+       int ret, i;
 
        ASSERT_RTNL();
+
        ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size,
                            name, ieee80211_if_setup);
        if (!ndev)
@@ -74,18 +200,28 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        /* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */
        sdata = netdev_priv(ndev);
        ndev->ieee80211_ptr = &sdata->wdev;
+
+       /* initialise type-independent data */
        sdata->wdev.wiphy = local->hw.wiphy;
-       sdata->vif.type = IEEE80211_IF_TYPE_AP;
-       sdata->dev = ndev;
        sdata->local = local;
-       ieee80211_if_sdata_init(sdata);
+       sdata->dev = ndev;
+
+       for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
+               skb_queue_head_init(&sdata->fragments[i].skb_list);
+
+       INIT_LIST_HEAD(&sdata->key_list);
+
+       sdata->force_unicast_rateidx = -1;
+       sdata->max_ratectrl_rateidx = -1;
+
+       /* setup type-dependent data */
+       ieee80211_setup_sdata(sdata, type);
 
        ret = register_netdevice(ndev);
        if (ret)
                goto fail;
 
-       ieee80211_debugfs_add_netdev(sdata);
-       ieee80211_if_set_type(ndev, type);
+       ndev->uninit = ieee80211_teardown_sdata;
 
        if (ieee80211_vif_is_mesh(&sdata->vif) &&
            params && params->mesh_id_len)
@@ -93,11 +229,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                                             params->mesh_id_len,
                                             params->mesh_id);
 
-       /* we're under RTNL so all this is fine */
-       if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) {
-               __ieee80211_if_del(local, sdata);
-               return -ENODEV;
-       }
        list_add_tail_rcu(&sdata->list, &local->interfaces);
 
        if (new_dev)
@@ -105,181 +236,34 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 
        return 0;
 
-fail:
+ fail:
        free_netdev(ndev);
        return ret;
 }
 
-void ieee80211_if_set_type(struct net_device *dev, int type)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       int oldtype = sdata->vif.type;
-
-       /*
-        * Called even when register_netdevice fails, it would
-        * oops if assigned before initialising the rest.
-        */
-       dev->uninit = ieee80211_if_reinit;
-
-       /* most have no BSS pointer */
-       sdata->bss = NULL;
-       sdata->vif.type = type;
-
-       sdata->basic_rates = 0;
-
-       switch (type) {
-       case IEEE80211_IF_TYPE_WDS:
-       case IEEE80211_IF_TYPE_VLAN:
-               /* nothing special */
-               break;
-       case IEEE80211_IF_TYPE_AP:
-               skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
-               INIT_LIST_HEAD(&sdata->u.ap.vlans);
-               break;
-       case IEEE80211_IF_TYPE_MESH_POINT:
-       case IEEE80211_IF_TYPE_STA:
-       case IEEE80211_IF_TYPE_IBSS: {
-               struct ieee80211_if_sta *ifsta;
-
-               ifsta = &sdata->u.sta;
-               INIT_WORK(&ifsta->work, ieee80211_sta_work);
-               setup_timer(&ifsta->timer, ieee80211_sta_timer,
-                           (unsigned long) sdata);
-               skb_queue_head_init(&ifsta->skb_queue);
-
-               ifsta->capab = WLAN_CAPABILITY_ESS;
-               ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
-                       IEEE80211_AUTH_ALG_SHARED_KEY;
-               ifsta->flags |= IEEE80211_STA_CREATE_IBSS |
-                       IEEE80211_STA_AUTO_BSSID_SEL |
-                       IEEE80211_STA_AUTO_CHANNEL_SEL;
-               if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4)
-                       ifsta->flags |= IEEE80211_STA_WMM_ENABLED;
-
-               if (ieee80211_vif_is_mesh(&sdata->vif))
-                       ieee80211_mesh_init_sdata(sdata);
-               break;
-       }
-       case IEEE80211_IF_TYPE_MNTR:
-               dev->type = ARPHRD_IEEE80211_RADIOTAP;
-               dev->hard_start_xmit = ieee80211_monitor_start_xmit;
-               sdata->u.mntr_flags = MONITOR_FLAG_CONTROL |
-                                     MONITOR_FLAG_OTHER_BSS;
-               break;
-       case IEEE80211_IF_TYPE_INVALID:
-               BUG();
-               break;
-       }
-       ieee80211_debugfs_change_if_type(sdata, oldtype);
-}
-
-/* Must be called with rtnl lock held. */
-void ieee80211_if_reinit(struct net_device *dev)
+void ieee80211_if_remove(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);
-       struct sk_buff *skb;
-       int flushed;
 
        ASSERT_RTNL();
 
-       ieee80211_free_keys(sdata);
-
-       ieee80211_if_sdata_deinit(sdata);
-
-       /* Need to handle mesh specially to allow eliding the function call */
-       if (ieee80211_vif_is_mesh(&sdata->vif))
-               mesh_rmc_free(dev);
-
-       switch (sdata->vif.type) {
-       case IEEE80211_IF_TYPE_INVALID:
-               /* cannot happen */
-               WARN_ON(1);
-               break;
-       case IEEE80211_IF_TYPE_AP: {
-               struct beacon_data *beacon;
-
-               beacon = sdata->u.ap.beacon;
-               rcu_assign_pointer(sdata->u.ap.beacon, NULL);
-               synchronize_rcu();
-               kfree(beacon);
-
-               while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
-                       local->total_ps_buffered--;
-                       dev_kfree_skb(skb);
-               }
-
-               break;
-       }
-       case IEEE80211_IF_TYPE_WDS:
-       case IEEE80211_IF_TYPE_VLAN:
-               /* nothing to do */
-               break;
-       case IEEE80211_IF_TYPE_MESH_POINT:
-       case IEEE80211_IF_TYPE_STA:
-       case IEEE80211_IF_TYPE_IBSS:
-               kfree(sdata->u.sta.extra_ie);
-               sdata->u.sta.extra_ie = NULL;
-               kfree(sdata->u.sta.assocreq_ies);
-               sdata->u.sta.assocreq_ies = NULL;
-               kfree(sdata->u.sta.assocresp_ies);
-               sdata->u.sta.assocresp_ies = NULL;
-               if (sdata->u.sta.probe_resp) {
-                       dev_kfree_skb(sdata->u.sta.probe_resp);
-                       sdata->u.sta.probe_resp = NULL;
-               }
-
-               break;
-       case IEEE80211_IF_TYPE_MNTR:
-               dev->type = ARPHRD_ETHER;
-               break;
-       }
-
-       flushed = sta_info_flush(local, sdata);
-       WARN_ON(flushed);
-
-       memset(&sdata->u, 0, sizeof(sdata->u));
-       ieee80211_if_sdata_init(sdata);
-}
-
-/* Must be called with rtnl lock held. */
-void __ieee80211_if_del(struct ieee80211_local *local,
-                       struct ieee80211_sub_if_data *sdata)
-{
-       struct net_device *dev = sdata->dev;
-
-       ieee80211_debugfs_remove_netdev(sdata);
+       list_del_rcu(&sdata->list);
+       synchronize_rcu();
        unregister_netdevice(dev);
-       /*
-        * The net_device will be freed by its destructor,
-        * i.e. ieee80211_if_free.
-        */
 }
 
-/* Must be called with rtnl lock held. */
-int ieee80211_if_remove(struct net_device *dev, const char *name, int id)
+/*
+ * Remove all interfaces, may only be called at hardware unregistration
+ * time because it doesn't do RCU-safe list removals.
+ */
+void ieee80211_remove_interfaces(struct ieee80211_local *local)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       struct ieee80211_sub_if_data *sdata, *n;
+       struct ieee80211_sub_if_data *sdata, *tmp;
 
        ASSERT_RTNL();
 
-       list_for_each_entry_safe(sdata, n, &local->interfaces, list) {
-               if ((sdata->vif.type == id || id == -1) &&
-                   strcmp(name, sdata->dev->name) == 0) {
-                       list_del_rcu(&sdata->list);
-                       synchronize_rcu();
-                       __ieee80211_if_del(local, sdata);
-                       return 0;
-               }
+       list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+               list_del(&sdata->list);
+               unregister_netdevice(sdata->dev);
        }
-       return -ENODEV;
-}
-
-void ieee80211_if_free(struct net_device *dev)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       ieee80211_if_sdata_deinit(sdata);
-       free_netdev(dev);
 }