1*f0706e82SJiri Benc /* 2*f0706e82SJiri Benc * Copyright 2002-2005, Instant802 Networks, Inc. 3*f0706e82SJiri Benc * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 4*f0706e82SJiri Benc * 5*f0706e82SJiri Benc * This program is free software; you can redistribute it and/or modify 6*f0706e82SJiri Benc * it under the terms of the GNU General Public License version 2 as 7*f0706e82SJiri Benc * published by the Free Software Foundation. 8*f0706e82SJiri Benc */ 9*f0706e82SJiri Benc 10*f0706e82SJiri Benc #include <linux/module.h> 11*f0706e82SJiri Benc #include <linux/init.h> 12*f0706e82SJiri Benc #include <linux/netdevice.h> 13*f0706e82SJiri Benc #include <linux/types.h> 14*f0706e82SJiri Benc #include <linux/slab.h> 15*f0706e82SJiri Benc #include <linux/skbuff.h> 16*f0706e82SJiri Benc #include <linux/if_arp.h> 17*f0706e82SJiri Benc 18*f0706e82SJiri Benc #include <net/mac80211.h> 19*f0706e82SJiri Benc #include "ieee80211_i.h" 20*f0706e82SJiri Benc #include "ieee80211_rate.h" 21*f0706e82SJiri Benc #include "sta_info.h" 22*f0706e82SJiri Benc 23*f0706e82SJiri Benc /* Caller must hold local->sta_lock */ 24*f0706e82SJiri Benc static void sta_info_hash_add(struct ieee80211_local *local, 25*f0706e82SJiri Benc struct sta_info *sta) 26*f0706e82SJiri Benc { 27*f0706e82SJiri Benc sta->hnext = local->sta_hash[STA_HASH(sta->addr)]; 28*f0706e82SJiri Benc local->sta_hash[STA_HASH(sta->addr)] = sta; 29*f0706e82SJiri Benc } 30*f0706e82SJiri Benc 31*f0706e82SJiri Benc 32*f0706e82SJiri Benc /* Caller must hold local->sta_lock */ 33*f0706e82SJiri Benc static void sta_info_hash_del(struct ieee80211_local *local, 34*f0706e82SJiri Benc struct sta_info *sta) 35*f0706e82SJiri Benc { 36*f0706e82SJiri Benc struct sta_info *s; 37*f0706e82SJiri Benc 38*f0706e82SJiri Benc s = local->sta_hash[STA_HASH(sta->addr)]; 39*f0706e82SJiri Benc if (!s) 40*f0706e82SJiri Benc return; 41*f0706e82SJiri Benc if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { 42*f0706e82SJiri Benc local->sta_hash[STA_HASH(sta->addr)] = s->hnext; 43*f0706e82SJiri Benc return; 44*f0706e82SJiri Benc } 45*f0706e82SJiri Benc 46*f0706e82SJiri Benc while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) 47*f0706e82SJiri Benc s = s->hnext; 48*f0706e82SJiri Benc if (s->hnext) 49*f0706e82SJiri Benc s->hnext = s->hnext->hnext; 50*f0706e82SJiri Benc else 51*f0706e82SJiri Benc printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from " 52*f0706e82SJiri Benc "hash table\n", local->mdev->name, MAC_ARG(sta->addr)); 53*f0706e82SJiri Benc } 54*f0706e82SJiri Benc 55*f0706e82SJiri Benc static inline void __sta_info_get(struct sta_info *sta) 56*f0706e82SJiri Benc { 57*f0706e82SJiri Benc kref_get(&sta->kref); 58*f0706e82SJiri Benc } 59*f0706e82SJiri Benc 60*f0706e82SJiri Benc struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) 61*f0706e82SJiri Benc { 62*f0706e82SJiri Benc struct sta_info *sta; 63*f0706e82SJiri Benc 64*f0706e82SJiri Benc spin_lock_bh(&local->sta_lock); 65*f0706e82SJiri Benc sta = local->sta_hash[STA_HASH(addr)]; 66*f0706e82SJiri Benc while (sta) { 67*f0706e82SJiri Benc if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { 68*f0706e82SJiri Benc __sta_info_get(sta); 69*f0706e82SJiri Benc break; 70*f0706e82SJiri Benc } 71*f0706e82SJiri Benc sta = sta->hnext; 72*f0706e82SJiri Benc } 73*f0706e82SJiri Benc spin_unlock_bh(&local->sta_lock); 74*f0706e82SJiri Benc 75*f0706e82SJiri Benc return sta; 76*f0706e82SJiri Benc } 77*f0706e82SJiri Benc EXPORT_SYMBOL(sta_info_get); 78*f0706e82SJiri Benc 79*f0706e82SJiri Benc int sta_info_min_txrate_get(struct ieee80211_local *local) 80*f0706e82SJiri Benc { 81*f0706e82SJiri Benc struct sta_info *sta; 82*f0706e82SJiri Benc struct ieee80211_hw_mode *mode; 83*f0706e82SJiri Benc int min_txrate = 9999999; 84*f0706e82SJiri Benc int i; 85*f0706e82SJiri Benc 86*f0706e82SJiri Benc spin_lock_bh(&local->sta_lock); 87*f0706e82SJiri Benc mode = local->oper_hw_mode; 88*f0706e82SJiri Benc for (i = 0; i < STA_HASH_SIZE; i++) { 89*f0706e82SJiri Benc sta = local->sta_hash[i]; 90*f0706e82SJiri Benc while (sta) { 91*f0706e82SJiri Benc if (sta->txrate < min_txrate) 92*f0706e82SJiri Benc min_txrate = sta->txrate; 93*f0706e82SJiri Benc sta = sta->hnext; 94*f0706e82SJiri Benc } 95*f0706e82SJiri Benc } 96*f0706e82SJiri Benc spin_unlock_bh(&local->sta_lock); 97*f0706e82SJiri Benc if (min_txrate == 9999999) 98*f0706e82SJiri Benc min_txrate = 0; 99*f0706e82SJiri Benc 100*f0706e82SJiri Benc return mode->rates[min_txrate].rate; 101*f0706e82SJiri Benc } 102*f0706e82SJiri Benc 103*f0706e82SJiri Benc 104*f0706e82SJiri Benc static void sta_info_release(struct kref *kref) 105*f0706e82SJiri Benc { 106*f0706e82SJiri Benc struct sta_info *sta = container_of(kref, struct sta_info, kref); 107*f0706e82SJiri Benc struct ieee80211_local *local = sta->local; 108*f0706e82SJiri Benc struct sk_buff *skb; 109*f0706e82SJiri Benc 110*f0706e82SJiri Benc /* free sta structure; it has already been removed from 111*f0706e82SJiri Benc * hash table etc. external structures. Make sure that all 112*f0706e82SJiri Benc * buffered frames are release (one might have been added 113*f0706e82SJiri Benc * after sta_info_free() was called). */ 114*f0706e82SJiri Benc while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { 115*f0706e82SJiri Benc local->total_ps_buffered--; 116*f0706e82SJiri Benc dev_kfree_skb_any(skb); 117*f0706e82SJiri Benc } 118*f0706e82SJiri Benc while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { 119*f0706e82SJiri Benc dev_kfree_skb_any(skb); 120*f0706e82SJiri Benc } 121*f0706e82SJiri Benc rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); 122*f0706e82SJiri Benc rate_control_put(sta->rate_ctrl); 123*f0706e82SJiri Benc kfree(sta); 124*f0706e82SJiri Benc } 125*f0706e82SJiri Benc 126*f0706e82SJiri Benc 127*f0706e82SJiri Benc void sta_info_put(struct sta_info *sta) 128*f0706e82SJiri Benc { 129*f0706e82SJiri Benc kref_put(&sta->kref, sta_info_release); 130*f0706e82SJiri Benc } 131*f0706e82SJiri Benc EXPORT_SYMBOL(sta_info_put); 132*f0706e82SJiri Benc 133*f0706e82SJiri Benc 134*f0706e82SJiri Benc struct sta_info * sta_info_add(struct ieee80211_local *local, 135*f0706e82SJiri Benc struct net_device *dev, u8 *addr, gfp_t gfp) 136*f0706e82SJiri Benc { 137*f0706e82SJiri Benc struct sta_info *sta; 138*f0706e82SJiri Benc 139*f0706e82SJiri Benc sta = kzalloc(sizeof(*sta), gfp); 140*f0706e82SJiri Benc if (!sta) 141*f0706e82SJiri Benc return NULL; 142*f0706e82SJiri Benc 143*f0706e82SJiri Benc kref_init(&sta->kref); 144*f0706e82SJiri Benc 145*f0706e82SJiri Benc sta->rate_ctrl = rate_control_get(local->rate_ctrl); 146*f0706e82SJiri Benc sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp); 147*f0706e82SJiri Benc if (!sta->rate_ctrl_priv) { 148*f0706e82SJiri Benc rate_control_put(sta->rate_ctrl); 149*f0706e82SJiri Benc kref_put(&sta->kref, sta_info_release); 150*f0706e82SJiri Benc kfree(sta); 151*f0706e82SJiri Benc return NULL; 152*f0706e82SJiri Benc } 153*f0706e82SJiri Benc 154*f0706e82SJiri Benc memcpy(sta->addr, addr, ETH_ALEN); 155*f0706e82SJiri Benc sta->local = local; 156*f0706e82SJiri Benc sta->dev = dev; 157*f0706e82SJiri Benc skb_queue_head_init(&sta->ps_tx_buf); 158*f0706e82SJiri Benc skb_queue_head_init(&sta->tx_filtered); 159*f0706e82SJiri Benc __sta_info_get(sta); /* sta used by caller, decremented by 160*f0706e82SJiri Benc * sta_info_put() */ 161*f0706e82SJiri Benc spin_lock_bh(&local->sta_lock); 162*f0706e82SJiri Benc list_add(&sta->list, &local->sta_list); 163*f0706e82SJiri Benc local->num_sta++; 164*f0706e82SJiri Benc sta_info_hash_add(local, sta); 165*f0706e82SJiri Benc spin_unlock_bh(&local->sta_lock); 166*f0706e82SJiri Benc if (local->ops->sta_table_notification) 167*f0706e82SJiri Benc local->ops->sta_table_notification(local_to_hw(local), 168*f0706e82SJiri Benc local->num_sta); 169*f0706e82SJiri Benc sta->key_idx_compression = HW_KEY_IDX_INVALID; 170*f0706e82SJiri Benc 171*f0706e82SJiri Benc #ifdef CONFIG_MAC80211_VERBOSE_DEBUG 172*f0706e82SJiri Benc printk(KERN_DEBUG "%s: Added STA " MAC_FMT "\n", 173*f0706e82SJiri Benc local->mdev->name, MAC_ARG(addr)); 174*f0706e82SJiri Benc #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ 175*f0706e82SJiri Benc 176*f0706e82SJiri Benc return sta; 177*f0706e82SJiri Benc } 178*f0706e82SJiri Benc 179*f0706e82SJiri Benc static void sta_info_remove(struct sta_info *sta) 180*f0706e82SJiri Benc { 181*f0706e82SJiri Benc struct ieee80211_local *local = sta->local; 182*f0706e82SJiri Benc struct ieee80211_sub_if_data *sdata; 183*f0706e82SJiri Benc 184*f0706e82SJiri Benc sta_info_hash_del(local, sta); 185*f0706e82SJiri Benc list_del(&sta->list); 186*f0706e82SJiri Benc sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 187*f0706e82SJiri Benc if (sta->flags & WLAN_STA_PS) { 188*f0706e82SJiri Benc sta->flags &= ~WLAN_STA_PS; 189*f0706e82SJiri Benc if (sdata->bss) 190*f0706e82SJiri Benc atomic_dec(&sdata->bss->num_sta_ps); 191*f0706e82SJiri Benc } 192*f0706e82SJiri Benc local->num_sta--; 193*f0706e82SJiri Benc sta_info_remove_aid_ptr(sta); 194*f0706e82SJiri Benc } 195*f0706e82SJiri Benc 196*f0706e82SJiri Benc void sta_info_free(struct sta_info *sta, int locked) 197*f0706e82SJiri Benc { 198*f0706e82SJiri Benc struct sk_buff *skb; 199*f0706e82SJiri Benc struct ieee80211_local *local = sta->local; 200*f0706e82SJiri Benc 201*f0706e82SJiri Benc if (!locked) { 202*f0706e82SJiri Benc spin_lock_bh(&local->sta_lock); 203*f0706e82SJiri Benc sta_info_remove(sta); 204*f0706e82SJiri Benc spin_unlock_bh(&local->sta_lock); 205*f0706e82SJiri Benc } else { 206*f0706e82SJiri Benc sta_info_remove(sta); 207*f0706e82SJiri Benc } 208*f0706e82SJiri Benc if (local->ops->sta_table_notification) 209*f0706e82SJiri Benc local->ops->sta_table_notification(local_to_hw(local), 210*f0706e82SJiri Benc local->num_sta); 211*f0706e82SJiri Benc 212*f0706e82SJiri Benc while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { 213*f0706e82SJiri Benc local->total_ps_buffered--; 214*f0706e82SJiri Benc dev_kfree_skb_any(skb); 215*f0706e82SJiri Benc } 216*f0706e82SJiri Benc while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { 217*f0706e82SJiri Benc dev_kfree_skb_any(skb); 218*f0706e82SJiri Benc } 219*f0706e82SJiri Benc 220*f0706e82SJiri Benc if (sta->key) { 221*f0706e82SJiri Benc if (local->ops->set_key) { 222*f0706e82SJiri Benc struct ieee80211_key_conf *key; 223*f0706e82SJiri Benc key = ieee80211_key_data2conf(local, sta->key); 224*f0706e82SJiri Benc if (key) { 225*f0706e82SJiri Benc local->ops->set_key(local_to_hw(local), 226*f0706e82SJiri Benc DISABLE_KEY, 227*f0706e82SJiri Benc sta->addr, key, sta->aid); 228*f0706e82SJiri Benc kfree(key); 229*f0706e82SJiri Benc } 230*f0706e82SJiri Benc } 231*f0706e82SJiri Benc } else if (sta->key_idx_compression != HW_KEY_IDX_INVALID) { 232*f0706e82SJiri Benc struct ieee80211_key_conf conf; 233*f0706e82SJiri Benc memset(&conf, 0, sizeof(conf)); 234*f0706e82SJiri Benc conf.hw_key_idx = sta->key_idx_compression; 235*f0706e82SJiri Benc conf.alg = ALG_NULL; 236*f0706e82SJiri Benc conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; 237*f0706e82SJiri Benc local->ops->set_key(local_to_hw(local), DISABLE_KEY, 238*f0706e82SJiri Benc sta->addr, &conf, sta->aid); 239*f0706e82SJiri Benc sta->key_idx_compression = HW_KEY_IDX_INVALID; 240*f0706e82SJiri Benc } 241*f0706e82SJiri Benc 242*f0706e82SJiri Benc #ifdef CONFIG_MAC80211_VERBOSE_DEBUG 243*f0706e82SJiri Benc printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n", 244*f0706e82SJiri Benc local->mdev->name, MAC_ARG(sta->addr)); 245*f0706e82SJiri Benc #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ 246*f0706e82SJiri Benc 247*f0706e82SJiri Benc if (sta->key) { 248*f0706e82SJiri Benc ieee80211_key_free(sta->key); 249*f0706e82SJiri Benc sta->key = NULL; 250*f0706e82SJiri Benc } 251*f0706e82SJiri Benc 252*f0706e82SJiri Benc sta_info_put(sta); 253*f0706e82SJiri Benc } 254*f0706e82SJiri Benc 255*f0706e82SJiri Benc 256*f0706e82SJiri Benc static inline int sta_info_buffer_expired(struct ieee80211_local *local, 257*f0706e82SJiri Benc struct sta_info *sta, 258*f0706e82SJiri Benc struct sk_buff *skb) 259*f0706e82SJiri Benc { 260*f0706e82SJiri Benc struct ieee80211_tx_packet_data *pkt_data; 261*f0706e82SJiri Benc int timeout; 262*f0706e82SJiri Benc 263*f0706e82SJiri Benc if (!skb) 264*f0706e82SJiri Benc return 0; 265*f0706e82SJiri Benc 266*f0706e82SJiri Benc pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; 267*f0706e82SJiri Benc 268*f0706e82SJiri Benc /* Timeout: (2 * listen_interval * beacon_int * 1024 / 1000000) sec */ 269*f0706e82SJiri Benc timeout = (sta->listen_interval * local->hw.conf.beacon_int * 32 / 270*f0706e82SJiri Benc 15625) * HZ; 271*f0706e82SJiri Benc if (timeout < STA_TX_BUFFER_EXPIRE) 272*f0706e82SJiri Benc timeout = STA_TX_BUFFER_EXPIRE; 273*f0706e82SJiri Benc return time_after(jiffies, pkt_data->jiffies + timeout); 274*f0706e82SJiri Benc } 275*f0706e82SJiri Benc 276*f0706e82SJiri Benc 277*f0706e82SJiri Benc static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local, 278*f0706e82SJiri Benc struct sta_info *sta) 279*f0706e82SJiri Benc { 280*f0706e82SJiri Benc unsigned long flags; 281*f0706e82SJiri Benc struct sk_buff *skb; 282*f0706e82SJiri Benc 283*f0706e82SJiri Benc if (skb_queue_empty(&sta->ps_tx_buf)) 284*f0706e82SJiri Benc return; 285*f0706e82SJiri Benc 286*f0706e82SJiri Benc for (;;) { 287*f0706e82SJiri Benc spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); 288*f0706e82SJiri Benc skb = skb_peek(&sta->ps_tx_buf); 289*f0706e82SJiri Benc if (sta_info_buffer_expired(local, sta, skb)) { 290*f0706e82SJiri Benc skb = __skb_dequeue(&sta->ps_tx_buf); 291*f0706e82SJiri Benc if (skb_queue_empty(&sta->ps_tx_buf)) 292*f0706e82SJiri Benc sta->flags &= ~WLAN_STA_TIM; 293*f0706e82SJiri Benc } else 294*f0706e82SJiri Benc skb = NULL; 295*f0706e82SJiri Benc spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); 296*f0706e82SJiri Benc 297*f0706e82SJiri Benc if (skb) { 298*f0706e82SJiri Benc local->total_ps_buffered--; 299*f0706e82SJiri Benc printk(KERN_DEBUG "Buffered frame expired (STA " 300*f0706e82SJiri Benc MAC_FMT ")\n", MAC_ARG(sta->addr)); 301*f0706e82SJiri Benc dev_kfree_skb(skb); 302*f0706e82SJiri Benc } else 303*f0706e82SJiri Benc break; 304*f0706e82SJiri Benc } 305*f0706e82SJiri Benc } 306*f0706e82SJiri Benc 307*f0706e82SJiri Benc 308*f0706e82SJiri Benc static void sta_info_cleanup(unsigned long data) 309*f0706e82SJiri Benc { 310*f0706e82SJiri Benc struct ieee80211_local *local = (struct ieee80211_local *) data; 311*f0706e82SJiri Benc struct sta_info *sta; 312*f0706e82SJiri Benc 313*f0706e82SJiri Benc spin_lock_bh(&local->sta_lock); 314*f0706e82SJiri Benc list_for_each_entry(sta, &local->sta_list, list) { 315*f0706e82SJiri Benc __sta_info_get(sta); 316*f0706e82SJiri Benc sta_info_cleanup_expire_buffered(local, sta); 317*f0706e82SJiri Benc sta_info_put(sta); 318*f0706e82SJiri Benc } 319*f0706e82SJiri Benc spin_unlock_bh(&local->sta_lock); 320*f0706e82SJiri Benc 321*f0706e82SJiri Benc local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; 322*f0706e82SJiri Benc add_timer(&local->sta_cleanup); 323*f0706e82SJiri Benc } 324*f0706e82SJiri Benc 325*f0706e82SJiri Benc void sta_info_init(struct ieee80211_local *local) 326*f0706e82SJiri Benc { 327*f0706e82SJiri Benc spin_lock_init(&local->sta_lock); 328*f0706e82SJiri Benc INIT_LIST_HEAD(&local->sta_list); 329*f0706e82SJiri Benc INIT_LIST_HEAD(&local->deleted_sta_list); 330*f0706e82SJiri Benc 331*f0706e82SJiri Benc init_timer(&local->sta_cleanup); 332*f0706e82SJiri Benc local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; 333*f0706e82SJiri Benc local->sta_cleanup.data = (unsigned long) local; 334*f0706e82SJiri Benc local->sta_cleanup.function = sta_info_cleanup; 335*f0706e82SJiri Benc } 336*f0706e82SJiri Benc 337*f0706e82SJiri Benc int sta_info_start(struct ieee80211_local *local) 338*f0706e82SJiri Benc { 339*f0706e82SJiri Benc add_timer(&local->sta_cleanup); 340*f0706e82SJiri Benc return 0; 341*f0706e82SJiri Benc } 342*f0706e82SJiri Benc 343*f0706e82SJiri Benc void sta_info_stop(struct ieee80211_local *local) 344*f0706e82SJiri Benc { 345*f0706e82SJiri Benc struct sta_info *sta, *tmp; 346*f0706e82SJiri Benc 347*f0706e82SJiri Benc del_timer(&local->sta_cleanup); 348*f0706e82SJiri Benc 349*f0706e82SJiri Benc list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { 350*f0706e82SJiri Benc /* We don't need locking at this point. */ 351*f0706e82SJiri Benc sta_info_free(sta, 0); 352*f0706e82SJiri Benc } 353*f0706e82SJiri Benc } 354*f0706e82SJiri Benc 355*f0706e82SJiri Benc void sta_info_remove_aid_ptr(struct sta_info *sta) 356*f0706e82SJiri Benc { 357*f0706e82SJiri Benc struct ieee80211_sub_if_data *sdata; 358*f0706e82SJiri Benc 359*f0706e82SJiri Benc if (sta->aid <= 0) 360*f0706e82SJiri Benc return; 361*f0706e82SJiri Benc 362*f0706e82SJiri Benc sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 363*f0706e82SJiri Benc 364*f0706e82SJiri Benc if (sdata->local->ops->set_tim) 365*f0706e82SJiri Benc sdata->local->ops->set_tim(local_to_hw(sdata->local), 366*f0706e82SJiri Benc sta->aid, 0); 367*f0706e82SJiri Benc if (sdata->bss) 368*f0706e82SJiri Benc __bss_tim_clear(sdata->bss, sta->aid); 369*f0706e82SJiri Benc } 370*f0706e82SJiri Benc 371*f0706e82SJiri Benc 372*f0706e82SJiri Benc /** 373*f0706e82SJiri Benc * sta_info_flush - flush matching STA entries from the STA table 374*f0706e82SJiri Benc * @local: local interface data 375*f0706e82SJiri Benc * @dev: matching rule for the net device (sta->dev) or %NULL to match all STAs 376*f0706e82SJiri Benc */ 377*f0706e82SJiri Benc void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) 378*f0706e82SJiri Benc { 379*f0706e82SJiri Benc struct sta_info *sta, *tmp; 380*f0706e82SJiri Benc 381*f0706e82SJiri Benc spin_lock_bh(&local->sta_lock); 382*f0706e82SJiri Benc list_for_each_entry_safe(sta, tmp, &local->sta_list, list) 383*f0706e82SJiri Benc if (!dev || dev == sta->dev) 384*f0706e82SJiri Benc sta_info_free(sta, 1); 385*f0706e82SJiri Benc spin_unlock_bh(&local->sta_lock); 386*f0706e82SJiri Benc } 387