mac80211 hwsim: make radio list dynamic
authorJohannes Berg <johannes@sipsolutions.net>
Thu, 11 Sep 2008 22:39:22 +0000 (00:39 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 15 Sep 2008 20:48:25 +0000 (16:48 -0400)
This paves the way for dynamic radio additions while the module
is loaded. Also restrict the number of radios to 100 because
creating that many already takes forever.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mac80211_hwsim.c

index 631d65b73edfb90732a6cc560ea8b64c9b805662..c9e4a435b2fc04d916ae61e6db150345027a568c 100644 (file)
@@ -14,6 +14,8 @@
  * - RX filtering based on filter configuration (data->rx_filter)
  */
 
+#include <linux/list.h>
+#include <linux/spinlock.h>
 #include <net/mac80211.h>
 #include <net/ieee80211_radiotap.h>
 #include <linux/if_arp.h>
@@ -78,8 +80,6 @@ static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta)
 
 static struct class *hwsim_class;
 
-static struct ieee80211_hw **hwsim_radios;
-static int hwsim_radio_count;
 static struct net_device *hwsim_mon; /* global monitor netdev */
 
 
@@ -115,7 +115,12 @@ static const struct ieee80211_rate hwsim_rates[] = {
        { .bitrate = 540 }
 };
 
+static spinlock_t hwsim_radio_lock;
+static struct list_head hwsim_radios;
+
 struct mac80211_hwsim_data {
+       struct list_head list;
+       struct ieee80211_hw *hw;
        struct device *dev;
        struct ieee80211_supported_band band;
        struct ieee80211_channel channels[ARRAY_SIZE(hwsim_channels)];
@@ -191,11 +196,11 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
 }
 
 
-static int mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
-                                  struct sk_buff *skb)
+static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
+                                   struct sk_buff *skb)
 {
-       struct mac80211_hwsim_data *data = hw->priv;
-       int i, ack = 0;
+       struct mac80211_hwsim_data *data = hw->priv, *data2;
+       bool ack = false;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_rx_status rx_status;
@@ -208,13 +213,13 @@ static int mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
        /* TODO: simulate signal strength (and optional packet drop) */
 
        /* Copy skb to all enabled radios that are on the current frequency */
-       for (i = 0; i < hwsim_radio_count; i++) {
-               struct mac80211_hwsim_data *data2;
+       spin_lock(&hwsim_radio_lock);
+       list_for_each_entry(data2, &hwsim_radios, list) {
                struct sk_buff *nskb;
 
-               if (hwsim_radios[i] == NULL || hwsim_radios[i] == hw)
+               if (data == data2)
                        continue;
-               data2 = hwsim_radios[i]->priv;
+
                if (!data2->started || !data2->radio_enabled ||
                    data->channel->center_freq != data2->channel->center_freq)
                        continue;
@@ -223,11 +228,12 @@ static int mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
                if (nskb == NULL)
                        continue;
 
-               if (memcmp(hdr->addr1, hwsim_radios[i]->wiphy->perm_addr,
+               if (memcmp(hdr->addr1, data2->hw->wiphy->perm_addr,
                           ETH_ALEN) == 0)
-                       ack = 1;
-               ieee80211_rx_irqsafe(hwsim_radios[i], nskb, &rx_status);
+                       ack = true;
+               ieee80211_rx_irqsafe(data2->hw, nskb, &rx_status);
        }
+       spin_unlock(&hwsim_radio_lock);
 
        return ack;
 }
@@ -236,7 +242,7 @@ static int mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
 static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct mac80211_hwsim_data *data = hw->priv;
-       int ack;
+       bool ack;
        struct ieee80211_tx_info *txi;
 
        mac80211_hwsim_monitor_rx(hw, skb);
@@ -457,18 +463,21 @@ static const struct ieee80211_ops mac80211_hwsim_ops =
 
 static void mac80211_hwsim_free(void)
 {
-       int i;
-
-       for (i = 0; i < hwsim_radio_count; i++) {
-               if (hwsim_radios[i]) {
-                       struct mac80211_hwsim_data *data;
-                       data = hwsim_radios[i]->priv;
-                       ieee80211_unregister_hw(hwsim_radios[i]);
-                       device_unregister(data->dev);
-                       ieee80211_free_hw(hwsim_radios[i]);
-               }
+       struct list_head tmplist, *i, *tmp;
+       struct mac80211_hwsim_data *data;
+
+       INIT_LIST_HEAD(&tmplist);
+
+       spin_lock_bh(&hwsim_radio_lock);
+       list_for_each_safe(i, tmp, &hwsim_radios)
+               list_move(i, &tmplist);
+       spin_unlock_bh(&hwsim_radio_lock);
+
+       list_for_each_entry(data, &tmplist, list) {
+               ieee80211_unregister_hw(data->hw);
+               device_unregister(data->dev);
+               ieee80211_free_hw(data->hw);
        }
-       kfree(hwsim_radios);
        class_destroy(hwsim_class);
 }
 
@@ -498,37 +507,32 @@ static int __init init_mac80211_hwsim(void)
        struct ieee80211_hw *hw;
        DECLARE_MAC_BUF(mac);
 
-       if (radios < 1 || radios > 65535)
+       if (radios < 1 || radios > 100)
                return -EINVAL;
 
-       hwsim_radio_count = radios;
-       hwsim_radios = kcalloc(hwsim_radio_count,
-                              sizeof(struct ieee80211_hw *), GFP_KERNEL);
-       if (hwsim_radios == NULL)
-               return -ENOMEM;
+       spin_lock_init(&hwsim_radio_lock);
+       INIT_LIST_HEAD(&hwsim_radios);
 
        hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
-       if (IS_ERR(hwsim_class)) {
-               kfree(hwsim_radios);
+       if (IS_ERR(hwsim_class))
                return PTR_ERR(hwsim_class);
-       }
 
        memset(addr, 0, ETH_ALEN);
        addr[0] = 0x02;
 
-       for (i = 0; i < hwsim_radio_count; i++) {
+       for (i = 0; i < radios; i++) {
                printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n",
                       i);
                hw = ieee80211_alloc_hw(sizeof(*data), &mac80211_hwsim_ops);
-               if (hw == NULL) {
+               if (!hw) {
                        printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw "
                               "failed\n");
                        err = -ENOMEM;
                        goto failed;
                }
-               hwsim_radios[i] = hw;
-
                data = hw->priv;
+               data->hw = hw;
+
                data->dev = device_create_drvdata(hwsim_class, NULL, 0, hw,
                                                "hwsim%d", i);
                if (IS_ERR(data->dev)) {
@@ -590,6 +594,8 @@ static int __init init_mac80211_hwsim(void)
 
                setup_timer(&data->beacon_timer, mac80211_hwsim_beacon,
                            (unsigned long) hw);
+
+               list_add_tail(&data->list, &hwsim_radios);
        }
 
        hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup);
@@ -621,7 +627,6 @@ failed_hw:
        device_unregister(data->dev);
 failed_drvdata:
        ieee80211_free_hw(hw);
-       hwsim_radios[i] = NULL;
 failed:
        mac80211_hwsim_free();
        return err;
@@ -630,8 +635,7 @@ failed:
 
 static void __exit exit_mac80211_hwsim(void)
 {
-       printk(KERN_DEBUG "mac80211_hwsim: unregister %d radios\n",
-              hwsim_radio_count);
+       printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");
 
        unregister_netdev(hwsim_mon);
        mac80211_hwsim_free();