1f0706e82SJiri Benc /* 2f0706e82SJiri Benc * Copyright 2002-2005, Instant802 Networks, Inc. 3f0706e82SJiri Benc * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 4f0706e82SJiri Benc * 5f0706e82SJiri Benc * This program is free software; you can redistribute it and/or modify 6f0706e82SJiri Benc * it under the terms of the GNU General Public License version 2 as 7f0706e82SJiri Benc * published by the Free Software Foundation. 8f0706e82SJiri Benc */ 9f0706e82SJiri Benc 10f0706e82SJiri Benc #include <linux/module.h> 11f0706e82SJiri Benc #include <linux/init.h> 12f0706e82SJiri Benc #include <linux/netdevice.h> 13f0706e82SJiri Benc #include <linux/types.h> 14f0706e82SJiri Benc #include <linux/slab.h> 15f0706e82SJiri Benc #include <linux/skbuff.h> 16f0706e82SJiri Benc #include <linux/if_arp.h> 170d174406SJohannes Berg #include <linux/timer.h> 18f0706e82SJiri Benc 19f0706e82SJiri Benc #include <net/mac80211.h> 20f0706e82SJiri Benc #include "ieee80211_i.h" 21f0706e82SJiri Benc #include "ieee80211_rate.h" 22f0706e82SJiri Benc #include "sta_info.h" 23e9f207f0SJiri Benc #include "debugfs_sta.h" 24f0706e82SJiri Benc 25f0706e82SJiri Benc /* Caller must hold local->sta_lock */ 26f0706e82SJiri Benc static void sta_info_hash_add(struct ieee80211_local *local, 27f0706e82SJiri Benc struct sta_info *sta) 28f0706e82SJiri Benc { 29f0706e82SJiri Benc sta->hnext = local->sta_hash[STA_HASH(sta->addr)]; 30f0706e82SJiri Benc local->sta_hash[STA_HASH(sta->addr)] = sta; 31f0706e82SJiri Benc } 32f0706e82SJiri Benc 33f0706e82SJiri Benc 34f0706e82SJiri Benc /* Caller must hold local->sta_lock */ 35be8755e1SMichael Wu static int sta_info_hash_del(struct ieee80211_local *local, 36f0706e82SJiri Benc struct sta_info *sta) 37f0706e82SJiri Benc { 38f0706e82SJiri Benc struct sta_info *s; 39f0706e82SJiri Benc 40f0706e82SJiri Benc s = local->sta_hash[STA_HASH(sta->addr)]; 41f0706e82SJiri Benc if (!s) 42be8755e1SMichael Wu return -ENOENT; 43be8755e1SMichael Wu if (s == sta) { 44f0706e82SJiri Benc local->sta_hash[STA_HASH(sta->addr)] = s->hnext; 45be8755e1SMichael Wu return 0; 46f0706e82SJiri Benc } 47f0706e82SJiri Benc 48be8755e1SMichael Wu while (s->hnext && s->hnext != sta) 49f0706e82SJiri Benc s = s->hnext; 50be8755e1SMichael Wu if (s->hnext) { 51be8755e1SMichael Wu s->hnext = sta->hnext; 52be8755e1SMichael Wu return 0; 53f0706e82SJiri Benc } 54f0706e82SJiri Benc 55be8755e1SMichael Wu return -ENOENT; 56f0706e82SJiri Benc } 57f0706e82SJiri Benc 58f0706e82SJiri Benc struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) 59f0706e82SJiri Benc { 60f0706e82SJiri Benc struct sta_info *sta; 61f0706e82SJiri Benc 62be8755e1SMichael Wu read_lock_bh(&local->sta_lock); 63f0706e82SJiri Benc sta = local->sta_hash[STA_HASH(addr)]; 64f0706e82SJiri Benc while (sta) { 65f0706e82SJiri Benc if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { 66f0706e82SJiri Benc __sta_info_get(sta); 67f0706e82SJiri Benc break; 68f0706e82SJiri Benc } 69f0706e82SJiri Benc sta = sta->hnext; 70f0706e82SJiri Benc } 71be8755e1SMichael Wu read_unlock_bh(&local->sta_lock); 72f0706e82SJiri Benc 73f0706e82SJiri Benc return sta; 74f0706e82SJiri Benc } 75f0706e82SJiri Benc EXPORT_SYMBOL(sta_info_get); 76f0706e82SJiri Benc 77f0706e82SJiri Benc 78f0706e82SJiri Benc static void sta_info_release(struct kref *kref) 79f0706e82SJiri Benc { 80f0706e82SJiri Benc struct sta_info *sta = container_of(kref, struct sta_info, kref); 81f0706e82SJiri Benc struct ieee80211_local *local = sta->local; 82f0706e82SJiri Benc struct sk_buff *skb; 8307db2183SRon Rindjunsky int i; 84f0706e82SJiri Benc 85f0706e82SJiri Benc /* free sta structure; it has already been removed from 86f0706e82SJiri Benc * hash table etc. external structures. Make sure that all 87f0706e82SJiri Benc * buffered frames are release (one might have been added 88f0706e82SJiri Benc * after sta_info_free() was called). */ 89f0706e82SJiri Benc while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { 90f0706e82SJiri Benc local->total_ps_buffered--; 91f0706e82SJiri Benc dev_kfree_skb_any(skb); 92f0706e82SJiri Benc } 93f0706e82SJiri Benc while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { 94f0706e82SJiri Benc dev_kfree_skb_any(skb); 95f0706e82SJiri Benc } 96fe3bf0f5SRon Rindjunsky for (i = 0; i < STA_TID_NUM; i++) { 9707db2183SRon Rindjunsky del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer); 98fe3bf0f5SRon Rindjunsky del_timer_sync(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer); 99fe3bf0f5SRon Rindjunsky } 100f0706e82SJiri Benc rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); 101f0706e82SJiri Benc rate_control_put(sta->rate_ctrl); 102f0706e82SJiri Benc kfree(sta); 103f0706e82SJiri Benc } 104f0706e82SJiri Benc 105f0706e82SJiri Benc 106f0706e82SJiri Benc void sta_info_put(struct sta_info *sta) 107f0706e82SJiri Benc { 108f0706e82SJiri Benc kref_put(&sta->kref, sta_info_release); 109f0706e82SJiri Benc } 110f0706e82SJiri Benc EXPORT_SYMBOL(sta_info_put); 111f0706e82SJiri Benc 112f0706e82SJiri Benc 113f0706e82SJiri Benc struct sta_info * sta_info_add(struct ieee80211_local *local, 114f0706e82SJiri Benc struct net_device *dev, u8 *addr, gfp_t gfp) 115f0706e82SJiri Benc { 116f0706e82SJiri Benc struct sta_info *sta; 11716c5f15cSRon Rindjunsky int i; 1180795af57SJoe Perches DECLARE_MAC_BUF(mac); 119f0706e82SJiri Benc 120f0706e82SJiri Benc sta = kzalloc(sizeof(*sta), gfp); 121f0706e82SJiri Benc if (!sta) 122f0706e82SJiri Benc return NULL; 123f0706e82SJiri Benc 124f0706e82SJiri Benc kref_init(&sta->kref); 125f0706e82SJiri Benc 126f0706e82SJiri Benc sta->rate_ctrl = rate_control_get(local->rate_ctrl); 127f0706e82SJiri Benc sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp); 128f0706e82SJiri Benc if (!sta->rate_ctrl_priv) { 129f0706e82SJiri Benc rate_control_put(sta->rate_ctrl); 130f0706e82SJiri Benc kfree(sta); 131f0706e82SJiri Benc return NULL; 132f0706e82SJiri Benc } 133f0706e82SJiri Benc 134f0706e82SJiri Benc memcpy(sta->addr, addr, ETH_ALEN); 135f0706e82SJiri Benc sta->local = local; 136f0706e82SJiri Benc sta->dev = dev; 13716c5f15cSRon Rindjunsky spin_lock_init(&sta->ampdu_mlme.ampdu_rx); 138fe3bf0f5SRon Rindjunsky spin_lock_init(&sta->ampdu_mlme.ampdu_tx); 13916c5f15cSRon Rindjunsky for (i = 0; i < STA_TID_NUM; i++) { 14016c5f15cSRon Rindjunsky /* timer_to_tid must be initialized with identity mapping to 14116c5f15cSRon Rindjunsky * enable session_timer's data differentiation. refer to 14216c5f15cSRon Rindjunsky * sta_rx_agg_session_timer_expired for useage */ 14316c5f15cSRon Rindjunsky sta->timer_to_tid[i] = i; 144fe3bf0f5SRon Rindjunsky /* tid to tx queue: initialize according to HW (0 is valid) */ 145fe3bf0f5SRon Rindjunsky sta->tid_to_tx_q[i] = local->hw.queues; 14616c5f15cSRon Rindjunsky /* rx timers */ 14716c5f15cSRon Rindjunsky sta->ampdu_mlme.tid_rx[i].session_timer.function = 14816c5f15cSRon Rindjunsky sta_rx_agg_session_timer_expired; 14916c5f15cSRon Rindjunsky sta->ampdu_mlme.tid_rx[i].session_timer.data = 15016c5f15cSRon Rindjunsky (unsigned long)&sta->timer_to_tid[i]; 15116c5f15cSRon Rindjunsky init_timer(&sta->ampdu_mlme.tid_rx[i].session_timer); 152fe3bf0f5SRon Rindjunsky /* tx timers */ 153fe3bf0f5SRon Rindjunsky sta->ampdu_mlme.tid_tx[i].addba_resp_timer.function = 154fe3bf0f5SRon Rindjunsky sta_addba_resp_timer_expired; 155fe3bf0f5SRon Rindjunsky sta->ampdu_mlme.tid_tx[i].addba_resp_timer.data = 156fe3bf0f5SRon Rindjunsky (unsigned long)&sta->timer_to_tid[i]; 157fe3bf0f5SRon Rindjunsky init_timer(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer); 15816c5f15cSRon Rindjunsky } 159f0706e82SJiri Benc skb_queue_head_init(&sta->ps_tx_buf); 160f0706e82SJiri Benc skb_queue_head_init(&sta->tx_filtered); 161f0706e82SJiri Benc __sta_info_get(sta); /* sta used by caller, decremented by 162f0706e82SJiri Benc * sta_info_put() */ 163be8755e1SMichael Wu write_lock_bh(&local->sta_lock); 164f0706e82SJiri Benc list_add(&sta->list, &local->sta_list); 165f0706e82SJiri Benc local->num_sta++; 166f0706e82SJiri Benc sta_info_hash_add(local, sta); 16732bfd35dSJohannes Berg if (local->ops->sta_notify) { 16832bfd35dSJohannes Berg struct ieee80211_sub_if_data *sdata; 16932bfd35dSJohannes Berg 17032bfd35dSJohannes Berg sdata = IEEE80211_DEV_TO_SUB_IF(dev); 17151fb61e7SJohannes Berg if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN) 17232bfd35dSJohannes Berg sdata = sdata->u.vlan.ap; 17332bfd35dSJohannes Berg 17432bfd35dSJohannes Berg local->ops->sta_notify(local_to_hw(local), &sdata->vif, 175478f8d2bSTomas Winkler STA_NOTIFY_ADD, addr); 17632bfd35dSJohannes Berg } 177be8755e1SMichael Wu write_unlock_bh(&local->sta_lock); 178f0706e82SJiri Benc 179f0706e82SJiri Benc #ifdef CONFIG_MAC80211_VERBOSE_DEBUG 1800795af57SJoe Perches printk(KERN_DEBUG "%s: Added STA %s\n", 181dd1cd4c6SJohannes Berg wiphy_name(local->hw.wiphy), print_mac(mac, addr)); 182f0706e82SJiri Benc #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ 183f0706e82SJiri Benc 184e9f207f0SJiri Benc #ifdef CONFIG_MAC80211_DEBUGFS 185e9f207f0SJiri Benc /* debugfs entry adding might sleep, so schedule process 186e9f207f0SJiri Benc * context task for adding entry for STAs that do not yet 187e9f207f0SJiri Benc * have one. */ 188e9f207f0SJiri Benc queue_work(local->hw.workqueue, &local->sta_debugfs_add); 189e9f207f0SJiri Benc #endif 190e9f207f0SJiri Benc 191f0706e82SJiri Benc return sta; 192f0706e82SJiri Benc } 193f0706e82SJiri Benc 194be8755e1SMichael Wu /* Caller must hold local->sta_lock */ 195be8755e1SMichael Wu void sta_info_remove(struct sta_info *sta) 196f0706e82SJiri Benc { 197f0706e82SJiri Benc struct ieee80211_local *local = sta->local; 198f0706e82SJiri Benc struct ieee80211_sub_if_data *sdata; 199f0706e82SJiri Benc 200be8755e1SMichael Wu /* don't do anything if we've been removed already */ 201be8755e1SMichael Wu if (sta_info_hash_del(local, sta)) 202be8755e1SMichael Wu return; 203be8755e1SMichael Wu 204f0706e82SJiri Benc list_del(&sta->list); 205f0706e82SJiri Benc sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 206f0706e82SJiri Benc if (sta->flags & WLAN_STA_PS) { 207f0706e82SJiri Benc sta->flags &= ~WLAN_STA_PS; 208f0706e82SJiri Benc if (sdata->bss) 209f0706e82SJiri Benc atomic_dec(&sdata->bss->num_sta_ps); 210f0706e82SJiri Benc } 211f0706e82SJiri Benc local->num_sta--; 212f0706e82SJiri Benc sta_info_remove_aid_ptr(sta); 213be8755e1SMichael Wu 214f0706e82SJiri Benc } 215f0706e82SJiri Benc 216be8755e1SMichael Wu void sta_info_free(struct sta_info *sta) 217f0706e82SJiri Benc { 218f0706e82SJiri Benc struct sk_buff *skb; 219f0706e82SJiri Benc struct ieee80211_local *local = sta->local; 2200795af57SJoe Perches DECLARE_MAC_BUF(mac); 221f0706e82SJiri Benc 222be8755e1SMichael Wu might_sleep(); 223be8755e1SMichael Wu 224be8755e1SMichael Wu write_lock_bh(&local->sta_lock); 225f0706e82SJiri Benc sta_info_remove(sta); 226be8755e1SMichael Wu write_unlock_bh(&local->sta_lock); 227f0706e82SJiri Benc 228f0706e82SJiri Benc while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { 229f0706e82SJiri Benc local->total_ps_buffered--; 230be8755e1SMichael Wu dev_kfree_skb(skb); 231f0706e82SJiri Benc } 232f0706e82SJiri Benc while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { 233be8755e1SMichael Wu dev_kfree_skb(skb); 234f0706e82SJiri Benc } 235f0706e82SJiri Benc 236be8755e1SMichael Wu #ifdef CONFIG_MAC80211_VERBOSE_DEBUG 2370795af57SJoe Perches printk(KERN_DEBUG "%s: Removed STA %s\n", 238dd1cd4c6SJohannes Berg wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr)); 239be8755e1SMichael Wu #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ 240be8755e1SMichael Wu 241be8755e1SMichael Wu ieee80211_key_free(sta->key); 242be8755e1SMichael Wu sta->key = NULL; 243be8755e1SMichael Wu 24432bfd35dSJohannes Berg if (local->ops->sta_notify) { 24532bfd35dSJohannes Berg struct ieee80211_sub_if_data *sdata; 24632bfd35dSJohannes Berg 24732bfd35dSJohannes Berg sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 24832bfd35dSJohannes Berg 24951fb61e7SJohannes Berg if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN) 25032bfd35dSJohannes Berg sdata = sdata->u.vlan.ap; 25132bfd35dSJohannes Berg 25232bfd35dSJohannes Berg local->ops->sta_notify(local_to_hw(local), &sdata->vif, 253478f8d2bSTomas Winkler STA_NOTIFY_REMOVE, sta->addr); 25432bfd35dSJohannes Berg } 255478f8d2bSTomas Winkler 256be8755e1SMichael Wu rate_control_remove_sta_debugfs(sta); 257be8755e1SMichael Wu ieee80211_sta_debugfs_remove(sta); 258be8755e1SMichael Wu 259be8755e1SMichael Wu sta_info_put(sta); 260f0706e82SJiri Benc } 261f0706e82SJiri Benc 262f0706e82SJiri Benc 263f0706e82SJiri Benc static inline int sta_info_buffer_expired(struct ieee80211_local *local, 264f0706e82SJiri Benc struct sta_info *sta, 265f0706e82SJiri Benc struct sk_buff *skb) 266f0706e82SJiri Benc { 267f0706e82SJiri Benc struct ieee80211_tx_packet_data *pkt_data; 268f0706e82SJiri Benc int timeout; 269f0706e82SJiri Benc 270f0706e82SJiri Benc if (!skb) 271f0706e82SJiri Benc return 0; 272f0706e82SJiri Benc 273f0706e82SJiri Benc pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; 274f0706e82SJiri Benc 275f0706e82SJiri Benc /* Timeout: (2 * listen_interval * beacon_int * 1024 / 1000000) sec */ 276f0706e82SJiri Benc timeout = (sta->listen_interval * local->hw.conf.beacon_int * 32 / 277f0706e82SJiri Benc 15625) * HZ; 278f0706e82SJiri Benc if (timeout < STA_TX_BUFFER_EXPIRE) 279f0706e82SJiri Benc timeout = STA_TX_BUFFER_EXPIRE; 280f0706e82SJiri Benc return time_after(jiffies, pkt_data->jiffies + timeout); 281f0706e82SJiri Benc } 282f0706e82SJiri Benc 283f0706e82SJiri Benc 284f0706e82SJiri Benc static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local, 285f0706e82SJiri Benc struct sta_info *sta) 286f0706e82SJiri Benc { 287f0706e82SJiri Benc unsigned long flags; 288f0706e82SJiri Benc struct sk_buff *skb; 289*836341a7SJohannes Berg struct ieee80211_sub_if_data *sdata; 2900795af57SJoe Perches DECLARE_MAC_BUF(mac); 291f0706e82SJiri Benc 292f0706e82SJiri Benc if (skb_queue_empty(&sta->ps_tx_buf)) 293f0706e82SJiri Benc return; 294f0706e82SJiri Benc 295f0706e82SJiri Benc for (;;) { 296f0706e82SJiri Benc spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); 297f0706e82SJiri Benc skb = skb_peek(&sta->ps_tx_buf); 298*836341a7SJohannes Berg if (sta_info_buffer_expired(local, sta, skb)) 299f0706e82SJiri Benc skb = __skb_dequeue(&sta->ps_tx_buf); 300*836341a7SJohannes Berg else 301f0706e82SJiri Benc skb = NULL; 302f0706e82SJiri Benc spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); 303f0706e82SJiri Benc 304*836341a7SJohannes Berg if (!skb) 305*836341a7SJohannes Berg break; 306*836341a7SJohannes Berg 307*836341a7SJohannes Berg sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 308f0706e82SJiri Benc local->total_ps_buffered--; 309f0706e82SJiri Benc printk(KERN_DEBUG "Buffered frame expired (STA " 3100795af57SJoe Perches "%s)\n", print_mac(mac, sta->addr)); 311f0706e82SJiri Benc dev_kfree_skb(skb); 312*836341a7SJohannes Berg 313*836341a7SJohannes Berg if (skb_queue_empty(&sta->ps_tx_buf)) { 314*836341a7SJohannes Berg if (sdata->bss) 315*836341a7SJohannes Berg bss_tim_set(sta->local, sdata->bss, sta->aid); 316*836341a7SJohannes Berg if (sta->local->ops->set_tim) 317*836341a7SJohannes Berg sta->local->ops->set_tim(local_to_hw(sta->local), 318*836341a7SJohannes Berg sta->aid, 0); 319*836341a7SJohannes Berg } 320f0706e82SJiri Benc } 321f0706e82SJiri Benc } 322f0706e82SJiri Benc 323f0706e82SJiri Benc 324f0706e82SJiri Benc static void sta_info_cleanup(unsigned long data) 325f0706e82SJiri Benc { 326f0706e82SJiri Benc struct ieee80211_local *local = (struct ieee80211_local *) data; 327f0706e82SJiri Benc struct sta_info *sta; 328f0706e82SJiri Benc 329be8755e1SMichael Wu read_lock_bh(&local->sta_lock); 330f0706e82SJiri Benc list_for_each_entry(sta, &local->sta_list, list) { 331f0706e82SJiri Benc __sta_info_get(sta); 332f0706e82SJiri Benc sta_info_cleanup_expire_buffered(local, sta); 333f0706e82SJiri Benc sta_info_put(sta); 334f0706e82SJiri Benc } 335be8755e1SMichael Wu read_unlock_bh(&local->sta_lock); 336f0706e82SJiri Benc 3370d174406SJohannes Berg local->sta_cleanup.expires = 3380d174406SJohannes Berg round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); 339f0706e82SJiri Benc add_timer(&local->sta_cleanup); 340f0706e82SJiri Benc } 341f0706e82SJiri Benc 342e9f207f0SJiri Benc #ifdef CONFIG_MAC80211_DEBUGFS 343e9f207f0SJiri Benc static void sta_info_debugfs_add_task(struct work_struct *work) 344e9f207f0SJiri Benc { 345e9f207f0SJiri Benc struct ieee80211_local *local = 346e9f207f0SJiri Benc container_of(work, struct ieee80211_local, sta_debugfs_add); 347e9f207f0SJiri Benc struct sta_info *sta, *tmp; 348e9f207f0SJiri Benc 349e9f207f0SJiri Benc while (1) { 350e9f207f0SJiri Benc sta = NULL; 351be8755e1SMichael Wu read_lock_bh(&local->sta_lock); 352e9f207f0SJiri Benc list_for_each_entry(tmp, &local->sta_list, list) { 353be8755e1SMichael Wu if (!tmp->debugfs.dir) { 354e9f207f0SJiri Benc sta = tmp; 355e9f207f0SJiri Benc __sta_info_get(sta); 356e9f207f0SJiri Benc break; 357e9f207f0SJiri Benc } 358e9f207f0SJiri Benc } 359be8755e1SMichael Wu read_unlock_bh(&local->sta_lock); 360e9f207f0SJiri Benc 361e9f207f0SJiri Benc if (!sta) 362e9f207f0SJiri Benc break; 363e9f207f0SJiri Benc 364e9f207f0SJiri Benc ieee80211_sta_debugfs_add(sta); 365e9f207f0SJiri Benc rate_control_add_sta_debugfs(sta); 366e9f207f0SJiri Benc sta_info_put(sta); 367e9f207f0SJiri Benc } 368e9f207f0SJiri Benc } 369e9f207f0SJiri Benc #endif 370e9f207f0SJiri Benc 371f0706e82SJiri Benc void sta_info_init(struct ieee80211_local *local) 372f0706e82SJiri Benc { 373be8755e1SMichael Wu rwlock_init(&local->sta_lock); 374f0706e82SJiri Benc INIT_LIST_HEAD(&local->sta_list); 375f0706e82SJiri Benc 376b24b8a24SPavel Emelyanov setup_timer(&local->sta_cleanup, sta_info_cleanup, 377b24b8a24SPavel Emelyanov (unsigned long)local); 3780d174406SJohannes Berg local->sta_cleanup.expires = 3790d174406SJohannes Berg round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); 380e9f207f0SJiri Benc 381e9f207f0SJiri Benc #ifdef CONFIG_MAC80211_DEBUGFS 382e9f207f0SJiri Benc INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_task); 383e9f207f0SJiri Benc #endif 384f0706e82SJiri Benc } 385f0706e82SJiri Benc 386f0706e82SJiri Benc int sta_info_start(struct ieee80211_local *local) 387f0706e82SJiri Benc { 388f0706e82SJiri Benc add_timer(&local->sta_cleanup); 389f0706e82SJiri Benc return 0; 390f0706e82SJiri Benc } 391f0706e82SJiri Benc 392f0706e82SJiri Benc void sta_info_stop(struct ieee80211_local *local) 393f0706e82SJiri Benc { 394f0706e82SJiri Benc del_timer(&local->sta_cleanup); 395be8755e1SMichael Wu sta_info_flush(local, NULL); 396f0706e82SJiri Benc } 397f0706e82SJiri Benc 398f0706e82SJiri Benc void sta_info_remove_aid_ptr(struct sta_info *sta) 399f0706e82SJiri Benc { 400f0706e82SJiri Benc struct ieee80211_sub_if_data *sdata; 401f0706e82SJiri Benc 402f0706e82SJiri Benc if (sta->aid <= 0) 403f0706e82SJiri Benc return; 404f0706e82SJiri Benc 405f0706e82SJiri Benc sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 406f0706e82SJiri Benc 407d2259243SJohannes Berg if (sdata->bss) 408d2259243SJohannes Berg __bss_tim_clear(sdata->bss, sta->aid); 409f0706e82SJiri Benc if (sdata->local->ops->set_tim) 410f0706e82SJiri Benc sdata->local->ops->set_tim(local_to_hw(sdata->local), 411f0706e82SJiri Benc sta->aid, 0); 412f0706e82SJiri Benc } 413f0706e82SJiri Benc 414f0706e82SJiri Benc 415f0706e82SJiri Benc /** 416f0706e82SJiri Benc * sta_info_flush - flush matching STA entries from the STA table 417f0706e82SJiri Benc * @local: local interface data 418f0706e82SJiri Benc * @dev: matching rule for the net device (sta->dev) or %NULL to match all STAs 419f0706e82SJiri Benc */ 420f0706e82SJiri Benc void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) 421f0706e82SJiri Benc { 422f0706e82SJiri Benc struct sta_info *sta, *tmp; 423be8755e1SMichael Wu LIST_HEAD(tmp_list); 424f0706e82SJiri Benc 425be8755e1SMichael Wu write_lock_bh(&local->sta_lock); 426f0706e82SJiri Benc list_for_each_entry_safe(sta, tmp, &local->sta_list, list) 427be8755e1SMichael Wu if (!dev || dev == sta->dev) { 428be8755e1SMichael Wu __sta_info_get(sta); 429be8755e1SMichael Wu sta_info_remove(sta); 430be8755e1SMichael Wu list_add_tail(&sta->list, &tmp_list); 431be8755e1SMichael Wu } 432be8755e1SMichael Wu write_unlock_bh(&local->sta_lock); 433be8755e1SMichael Wu 434be8755e1SMichael Wu list_for_each_entry_safe(sta, tmp, &tmp_list, list) { 435be8755e1SMichael Wu sta_info_free(sta); 436be8755e1SMichael Wu sta_info_put(sta); 437be8755e1SMichael Wu } 438f0706e82SJiri Benc } 439