mac80211: properly forward the RANN IE
[safe/jmp/linux-2.6] / net / mac80211 / sta_info.c
index c5f14e6..396a948 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
+#include "driver-ops.h"
 #include "rate.h"
 #include "sta_info.h"
 #include "debugfs_sta.h"
  * When the insertion fails (sta_info_insert()) returns non-zero), the
  * structure will have been freed by sta_info_insert()!
  *
+ * sta entries are added by mac80211 when you establish a link with a
+ * peer. This means different things for the different type of interfaces
+ * we support. For a regular station this mean we add the AP sta when we
+ * receive an assocation response from the AP. For IBSS this occurs when
+ * we receive a probe response or a beacon from target IBSS network. For
+ * WDS we add the sta for the peer imediately upon device open. When using
+ * AP mode we add stations for each respective station upon request from
+ * userspace through nl80211.
+ *
  * Because there are debugfs entries for each station, and adding those
  * must be able to sleep, it is also possible to "pin" a station entry,
  * that means it can be removed from the hash table but not be freed.
@@ -161,6 +171,8 @@ void sta_info_destroy(struct sta_info *sta)
 
        local = sta->local;
 
+       cancel_work_sync(&sta->drv_unblock_wk);
+
        rate_control_remove_sta_debugfs(sta);
        ieee80211_sta_debugfs_remove(sta);
 
@@ -249,6 +261,21 @@ static void sta_info_hash_add(struct ieee80211_local *local,
        rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
 }
 
+static void sta_unblock(struct work_struct *wk)
+{
+       struct sta_info *sta;
+
+       sta = container_of(wk, struct sta_info, drv_unblock_wk);
+
+       if (sta->dead)
+               return;
+
+       if (!test_sta_flags(sta, WLAN_STA_PS_STA))
+               ieee80211_sta_ps_deliver_wakeup(sta);
+       else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL))
+               ieee80211_sta_ps_deliver_poll_response(sta);
+}
+
 struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                                u8 *addr, gfp_t gfp)
 {
@@ -262,6 +289,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        spin_lock_init(&sta->lock);
        spin_lock_init(&sta->flaglock);
+       INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
 
        memcpy(sta->sta.addr, addr, ETH_ALEN);
        sta->local = local;
@@ -292,6 +320,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        skb_queue_head_init(&sta->ps_tx_buf);
        skb_queue_head_init(&sta->tx_filtered);
 
+       for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+               sta->last_seq_ctrl[i] = cpu_to_le16(USHORT_MAX);
+
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
        printk(KERN_DEBUG "%s: Allocated STA %pM\n",
               wiphy_name(local->hw.wiphy), sta->sta.addr);
@@ -336,6 +367,7 @@ int sta_info_insert(struct sta_info *sta)
                goto out_free;
        }
        list_add(&sta->list, &local->sta_list);
+       local->sta_generation++;
        local->num_sta++;
        sta_info_hash_add(local, sta);
 
@@ -346,8 +378,8 @@ int sta_info_insert(struct sta_info *sta)
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-               local->ops->sta_notify(local_to_hw(local), &sdata->vif,
-                                      STA_NOTIFY_ADD, &sta->sta);
+               drv_sta_notify(local, &sdata->vif, STA_NOTIFY_ADD, &sta->sta);
+               sdata = sta->sdata;
        }
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -405,8 +437,7 @@ static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss,
 
        if (sta->local->ops->set_tim) {
                sta->local->tim_in_locked_section = true;
-               sta->local->ops->set_tim(local_to_hw(sta->local),
-                                        &sta->sta, true);
+               drv_set_tim(sta->local, &sta->sta, true);
                sta->local->tim_in_locked_section = false;
        }
 }
@@ -431,8 +462,7 @@ static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
 
        if (sta->local->ops->set_tim) {
                sta->local->tim_in_locked_section = true;
-               sta->local->ops->set_tim(local_to_hw(sta->local),
-                                        &sta->sta, false);
+               drv_set_tim(sta->local, &sta->sta, false);
                sta->local->tim_in_locked_section = false;
        }
 }
@@ -466,8 +496,10 @@ static void __sta_info_unlink(struct sta_info **sta)
        }
 
        list_del(&(*sta)->list);
+       (*sta)->dead = true;
 
-       if (test_and_clear_sta_flags(*sta, WLAN_STA_PS)) {
+       if (test_and_clear_sta_flags(*sta,
+                               WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
                BUG_ON(!sdata->bss);
 
                atomic_dec(&sdata->bss->num_sta_ps);
@@ -475,6 +507,10 @@ static void __sta_info_unlink(struct sta_info **sta)
        }
 
        local->num_sta--;
+       local->sta_generation++;
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+               rcu_assign_pointer(sdata->u.vlan.sta, NULL);
 
        if (local->ops->sta_notify) {
                if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -482,8 +518,9 @@ static void __sta_info_unlink(struct sta_info **sta)
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-               local->ops->sta_notify(local_to_hw(local), &sdata->vif,
-                                      STA_NOTIFY_REMOVE, &(*sta)->sta);
+               drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE,
+                              &(*sta)->sta);
+               sdata = (*sta)->sdata;
        }
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
@@ -543,9 +580,8 @@ void sta_info_unlink(struct sta_info **sta)
        spin_unlock_irqrestore(&local->sta_lock, flags);
 }
 
-static inline int sta_info_buffer_expired(struct ieee80211_local *local,
-                                         struct sta_info *sta,
-                                         struct sk_buff *skb)
+static int sta_info_buffer_expired(struct sta_info *sta,
+                                  struct sk_buff *skb)
 {
        struct ieee80211_tx_info *info;
        int timeout;
@@ -556,8 +592,9 @@ static inline int sta_info_buffer_expired(struct ieee80211_local *local,
        info = IEEE80211_SKB_CB(skb);
 
        /* Timeout: (2 * listen_interval * beacon_int * 1024 / 1000000) sec */
-       timeout = (sta->listen_interval * local->hw.conf.beacon_int * 32 /
-                  15625) * HZ;
+       timeout = (sta->listen_interval *
+                  sta->sdata->vif.bss_conf.beacon_int *
+                  32 / 15625) * HZ;
        if (timeout < STA_TX_BUFFER_EXPIRE)
                timeout = STA_TX_BUFFER_EXPIRE;
        return time_after(jiffies, info->control.jiffies + timeout);
@@ -577,7 +614,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
        for (;;) {
                spin_lock_irqsave(&sta->ps_tx_buf.lock, flags);
                skb = skb_peek(&sta->ps_tx_buf);
-               if (sta_info_buffer_expired(local, sta, skb))
+               if (sta_info_buffer_expired(sta, skb))
                        skb = __skb_dequeue(&sta->ps_tx_buf);
                else
                        skb = NULL;
@@ -610,6 +647,9 @@ static void sta_info_cleanup(unsigned long data)
                sta_info_cleanup_expire_buffered(local, sta);
        rcu_read_unlock();
 
+       if (local->quiescing)
+               return;
+
        local->sta_cleanup.expires =
                round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
        add_timer(&local->sta_cleanup);
@@ -686,41 +726,10 @@ static void sta_info_debugfs_add_work(struct work_struct *work)
 }
 #endif
 
-static void __ieee80211_run_pending_flush(struct ieee80211_local *local)
-{
-       struct sta_info *sta;
-       unsigned long flags;
-
-       ASSERT_RTNL();
-
-       spin_lock_irqsave(&local->sta_lock, flags);
-       while (!list_empty(&local->sta_flush_list)) {
-               sta = list_first_entry(&local->sta_flush_list,
-                                      struct sta_info, list);
-               list_del(&sta->list);
-               spin_unlock_irqrestore(&local->sta_lock, flags);
-               sta_info_destroy(sta);
-               spin_lock_irqsave(&local->sta_lock, flags);
-       }
-       spin_unlock_irqrestore(&local->sta_lock, flags);
-}
-
-static void ieee80211_sta_flush_work(struct work_struct *work)
-{
-       struct ieee80211_local *local =
-               container_of(work, struct ieee80211_local, sta_flush_work);
-
-       rtnl_lock();
-       __ieee80211_run_pending_flush(local);
-       rtnl_unlock();
-}
-
 void sta_info_init(struct ieee80211_local *local)
 {
        spin_lock_init(&local->sta_lock);
        INIT_LIST_HEAD(&local->sta_list);
-       INIT_LIST_HEAD(&local->sta_flush_list);
-       INIT_WORK(&local->sta_flush_work, ieee80211_sta_flush_work);
 
        setup_timer(&local->sta_cleanup, sta_info_cleanup,
                    (unsigned long)local);
@@ -741,7 +750,6 @@ int sta_info_start(struct ieee80211_local *local)
 void sta_info_stop(struct ieee80211_local *local)
 {
        del_timer(&local->sta_cleanup);
-       cancel_work_sync(&local->sta_flush_work);
 #ifdef CONFIG_MAC80211_DEBUGFS
        /*
         * Make sure the debugfs adding work isn't pending after this
@@ -752,10 +760,7 @@ void sta_info_stop(struct ieee80211_local *local)
        cancel_work_sync(&local->sta_debugfs_add);
 #endif
 
-       rtnl_lock();
        sta_info_flush(local, NULL);
-       __ieee80211_run_pending_flush(local);
-       rtnl_unlock();
 }
 
 /**
@@ -767,7 +772,7 @@ void sta_info_stop(struct ieee80211_local *local)
  * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
  */
 int sta_info_flush(struct ieee80211_local *local,
-                   struct ieee80211_sub_if_data *sdata)
+                  struct ieee80211_sub_if_data *sdata)
 {
        struct sta_info *sta, *tmp;
        LIST_HEAD(tmp_list);
@@ -775,7 +780,6 @@ int sta_info_flush(struct ieee80211_local *local,
        unsigned long flags;
 
        might_sleep();
-       ASSERT_RTNL();
 
        spin_lock_irqsave(&local->sta_lock, flags);
        list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
@@ -795,39 +799,6 @@ int sta_info_flush(struct ieee80211_local *local,
        return ret;
 }
 
-/**
- * sta_info_flush_delayed - flush matching STA entries from the STA table
- *
- * This function unlinks all stations for a given interface and queues
- * them for freeing. Note that the workqueue function scheduled here has
- * to run before any new keys can be added to the system to avoid set_key()
- * callback ordering issues.
- *
- * @sdata: the interface
- */
-void sta_info_flush_delayed(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct sta_info *sta, *tmp;
-       unsigned long flags;
-       bool work = false;
-
-       spin_lock_irqsave(&local->sta_lock, flags);
-       list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
-               if (sdata == sta->sdata) {
-                       __sta_info_unlink(&sta);
-                       if (sta) {
-                               list_add_tail(&sta->list,
-                                             &local->sta_flush_list);
-                               work = true;
-                       }
-               }
-       }
-       if (work)
-               schedule_work(&local->sta_flush_work);
-       spin_unlock_irqrestore(&local->sta_lock, flags);
-}
-
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time)
 {
@@ -853,8 +824,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                sta_info_destroy(sta);
 }
 
-struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
-                                        const u8 *addr)
+struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw,
+                                              const u8 *addr)
 {
        struct sta_info *sta = sta_info_get(hw_to_local(hw), addr);
 
@@ -862,4 +833,114 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
                return NULL;
        return &sta->sta;
 }
+EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_hw);
+
+struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
+                                        const u8 *addr)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       if (!vif)
+               return NULL;
+
+       sdata = vif_to_sdata(vif);
+
+       return ieee80211_find_sta_by_hw(&sdata->local->hw, addr);
+}
 EXPORT_SYMBOL(ieee80211_find_sta);
+
+/* powersave support code */
+void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+       int sent, buffered;
+
+       drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta);
+
+       if (!skb_queue_empty(&sta->ps_tx_buf))
+               sta_info_clear_tim_bit(sta);
+
+       /* Send all buffered frames to the station */
+       sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
+       buffered = ieee80211_add_pending_skbs(local, &sta->ps_tx_buf);
+       sent += buffered;
+       local->total_ps_buffered -= buffered;
+
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+       printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames "
+              "since STA not sleeping anymore\n", sdata->dev->name,
+              sta->sta.addr, sta->sta.aid, sent - buffered, buffered);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+}
+
+void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       int no_pending_pkts;
+
+       skb = skb_dequeue(&sta->tx_filtered);
+       if (!skb) {
+               skb = skb_dequeue(&sta->ps_tx_buf);
+               if (skb)
+                       local->total_ps_buffered--;
+       }
+       no_pending_pkts = skb_queue_empty(&sta->tx_filtered) &&
+               skb_queue_empty(&sta->ps_tx_buf);
+
+       if (skb) {
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+               struct ieee80211_hdr *hdr =
+                       (struct ieee80211_hdr *) skb->data;
+
+               /*
+                * Tell TX path to send this frame even though the STA may
+                * still remain is PS mode after this frame exchange.
+                */
+               info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
+
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+               printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
+                      sta->sta.addr, sta->sta.aid,
+                      skb_queue_len(&sta->ps_tx_buf));
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+
+               /* Use MoreData flag to indicate whether there are more
+                * buffered frames for this STA */
+               if (no_pending_pkts)
+                       hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+               else
+                       hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+
+               ieee80211_add_pending_skb(local, skb);
+
+               if (no_pending_pkts)
+                       sta_info_clear_tim_bit(sta);
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+       } else {
+               /*
+                * FIXME: This can be the result of a race condition between
+                *        us expiring a frame and the station polling for it.
+                *        Should we send it a null-func frame indicating we
+                *        have nothing buffered for it?
+                */
+               printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
+                      "though there are no buffered frames for it\n",
+                      sdata->dev->name, sta->sta.addr);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+       }
+}
+
+void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
+                              struct ieee80211_sta *pubsta, bool block)
+{
+       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+
+       if (block)
+               set_sta_flags(sta, WLAN_STA_PS_DRIVER);
+       else
+               ieee80211_queue_work(hw, &sta->drv_unblock_wk);
+}
+EXPORT_SYMBOL(ieee80211_sta_block_awake);