mac80211: clean up mesh code
[deliverable/linux.git] / net / mac80211 / sta_info.c
index b31a627ff97fecc4f0390e94c6a458d29819d8e8..81c4e3392f40a1c5ebebbef20acb1d44d7d6fca5 100644 (file)
@@ -21,6 +21,7 @@
 #include "ieee80211_rate.h"
 #include "sta_info.h"
 #include "debugfs_sta.h"
+#include "mesh.h"
 
 /* Caller must hold local->sta_lock */
 static void sta_info_hash_add(struct ieee80211_local *local,
@@ -55,25 +56,56 @@ static int sta_info_hash_del(struct ieee80211_local *local,
        return -ENOENT;
 }
 
-struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
+/* must hold local->sta_lock */
+static struct sta_info *__sta_info_find(struct ieee80211_local *local,
+                                       u8 *addr)
 {
        struct sta_info *sta;
 
-       read_lock_bh(&local->sta_lock);
        sta = local->sta_hash[STA_HASH(addr)];
        while (sta) {
-               if (memcmp(sta->addr, addr, ETH_ALEN) == 0) {
-                       __sta_info_get(sta);
+               if (compare_ether_addr(sta->addr, addr) == 0)
                        break;
-               }
                sta = sta->hnext;
        }
+       return sta;
+}
+
+struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
+{
+       struct sta_info *sta;
+
+       read_lock_bh(&local->sta_lock);
+       sta = __sta_info_find(local, addr);
+       if (sta)
+               __sta_info_get(sta);
        read_unlock_bh(&local->sta_lock);
 
        return sta;
 }
 EXPORT_SYMBOL(sta_info_get);
 
+struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
+                                    struct net_device *dev)
+{
+       struct sta_info *sta;
+       int i = 0;
+
+       read_lock_bh(&local->sta_lock);
+       list_for_each_entry(sta, &local->sta_list, list) {
+               if (i < idx) {
+                       ++i;
+                       continue;
+               } else if (!dev || dev == sta->dev) {
+                       __sta_info_get(sta);
+                       read_unlock_bh(&local->sta_lock);
+                       return sta;
+               }
+       }
+       read_unlock_bh(&local->sta_lock);
+
+       return NULL;
+}
 
 static void sta_info_release(struct kref *kref)
 {
@@ -110,8 +142,8 @@ void sta_info_put(struct sta_info *sta)
 EXPORT_SYMBOL(sta_info_put);
 
 
-struct sta_info * sta_info_add(struct ieee80211_local *local,
-                              struct net_device *dev, u8 *addr, gfp_t gfp)
+struct sta_info *sta_info_add(struct ieee80211_local *local,
+                             struct net_device *dev, u8 *addr, gfp_t gfp)
 {
        struct sta_info *sta;
        int i;
@@ -119,7 +151,7 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
 
        sta = kzalloc(sizeof(*sta), gfp);
        if (!sta)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        kref_init(&sta->kref);
 
@@ -128,7 +160,7 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
        if (!sta->rate_ctrl_priv) {
                rate_control_put(sta->rate_ctrl);
                kfree(sta);
-               return NULL;
+               return ERR_PTR(-ENOMEM);
        }
 
        memcpy(sta->addr, addr, ETH_ALEN);
@@ -158,9 +190,15 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
        }
        skb_queue_head_init(&sta->ps_tx_buf);
        skb_queue_head_init(&sta->tx_filtered);
-       __sta_info_get(sta);    /* sta used by caller, decremented by
-                                * sta_info_put() */
        write_lock_bh(&local->sta_lock);
+       /* mark sta as used (by caller) */
+       __sta_info_get(sta);
+       /* check if STA exists already */
+       if (__sta_info_find(local, addr)) {
+               write_unlock_bh(&local->sta_lock);
+               sta_info_put(sta);
+               return ERR_PTR(-EEXIST);
+       }
        list_add(&sta->list, &local->sta_list);
        local->num_sta++;
        sta_info_hash_add(local, sta);
@@ -268,12 +306,17 @@ void sta_info_remove(struct sta_info *sta)
                __sta_info_clear_tim_bit(sdata->bss, sta);
        }
        local->num_sta--;
+
+       if (ieee80211_vif_is_mesh(&sdata->vif))
+               mesh_accept_plinks_update(sdata->dev);
 }
 
 void sta_info_free(struct sta_info *sta)
 {
        struct sk_buff *skb;
        struct ieee80211_local *local = sta->local;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+
        DECLARE_MAC_BUF(mac);
 
        might_sleep();
@@ -282,6 +325,9 @@ void sta_info_free(struct sta_info *sta)
        sta_info_remove(sta);
        write_unlock_bh(&local->sta_lock);
 
+       if (ieee80211_vif_is_mesh(&sdata->vif))
+               mesh_plink_deactivate(sta);
+
        while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
                local->total_ps_buffered--;
                dev_kfree_skb(skb);
@@ -296,12 +342,9 @@ void sta_info_free(struct sta_info *sta)
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
        ieee80211_key_free(sta->key);
-       sta->key = NULL;
+       WARN_ON(sta->key);
 
        if (local->ops->sta_notify) {
-               struct ieee80211_sub_if_data *sdata;
-
-               sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
 
                if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
                        sdata = sdata->u.vlan.ap;
This page took 0.027671 seconds and 5 git commands to generate.