diff options
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r-- | net/mac80211/sta_info.c | 136 |
1 files changed, 133 insertions, 3 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 594f2318c3d8..be59456e8a42 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -171,6 +171,8 @@ void sta_info_destroy(struct sta_info *sta) | |||
171 | 171 | ||
172 | local = sta->local; | 172 | local = sta->local; |
173 | 173 | ||
174 | cancel_work_sync(&sta->drv_unblock_wk); | ||
175 | |||
174 | rate_control_remove_sta_debugfs(sta); | 176 | rate_control_remove_sta_debugfs(sta); |
175 | ieee80211_sta_debugfs_remove(sta); | 177 | ieee80211_sta_debugfs_remove(sta); |
176 | 178 | ||
@@ -259,6 +261,21 @@ static void sta_info_hash_add(struct ieee80211_local *local, | |||
259 | rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); | 261 | rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); |
260 | } | 262 | } |
261 | 263 | ||
264 | static void sta_unblock(struct work_struct *wk) | ||
265 | { | ||
266 | struct sta_info *sta; | ||
267 | |||
268 | sta = container_of(wk, struct sta_info, drv_unblock_wk); | ||
269 | |||
270 | if (sta->dead) | ||
271 | return; | ||
272 | |||
273 | if (!test_sta_flags(sta, WLAN_STA_PS_STA)) | ||
274 | ieee80211_sta_ps_deliver_wakeup(sta); | ||
275 | else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) | ||
276 | ieee80211_sta_ps_deliver_poll_response(sta); | ||
277 | } | ||
278 | |||
262 | struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | 279 | struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, |
263 | u8 *addr, gfp_t gfp) | 280 | u8 *addr, gfp_t gfp) |
264 | { | 281 | { |
@@ -272,6 +289,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
272 | 289 | ||
273 | spin_lock_init(&sta->lock); | 290 | spin_lock_init(&sta->lock); |
274 | spin_lock_init(&sta->flaglock); | 291 | spin_lock_init(&sta->flaglock); |
292 | INIT_WORK(&sta->drv_unblock_wk, sta_unblock); | ||
275 | 293 | ||
276 | memcpy(sta->sta.addr, addr, ETH_ALEN); | 294 | memcpy(sta->sta.addr, addr, ETH_ALEN); |
277 | sta->local = local; | 295 | sta->local = local; |
@@ -478,8 +496,10 @@ static void __sta_info_unlink(struct sta_info **sta) | |||
478 | } | 496 | } |
479 | 497 | ||
480 | list_del(&(*sta)->list); | 498 | list_del(&(*sta)->list); |
499 | (*sta)->dead = true; | ||
481 | 500 | ||
482 | if (test_and_clear_sta_flags(*sta, WLAN_STA_PS)) { | 501 | if (test_and_clear_sta_flags(*sta, |
502 | WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) { | ||
483 | BUG_ON(!sdata->bss); | 503 | BUG_ON(!sdata->bss); |
484 | 504 | ||
485 | atomic_dec(&sdata->bss->num_sta_ps); | 505 | atomic_dec(&sdata->bss->num_sta_ps); |
@@ -801,8 +821,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, | |||
801 | sta_info_destroy(sta); | 821 | sta_info_destroy(sta); |
802 | } | 822 | } |
803 | 823 | ||
804 | struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw, | 824 | struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw, |
805 | const u8 *addr) | 825 | const u8 *addr) |
806 | { | 826 | { |
807 | struct sta_info *sta = sta_info_get(hw_to_local(hw), addr); | 827 | struct sta_info *sta = sta_info_get(hw_to_local(hw), addr); |
808 | 828 | ||
@@ -810,4 +830,114 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw, | |||
810 | return NULL; | 830 | return NULL; |
811 | return &sta->sta; | 831 | return &sta->sta; |
812 | } | 832 | } |
833 | EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_hw); | ||
834 | |||
835 | struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, | ||
836 | const u8 *addr) | ||
837 | { | ||
838 | struct ieee80211_sub_if_data *sdata; | ||
839 | |||
840 | if (!vif) | ||
841 | return NULL; | ||
842 | |||
843 | sdata = vif_to_sdata(vif); | ||
844 | |||
845 | return ieee80211_find_sta_by_hw(&sdata->local->hw, addr); | ||
846 | } | ||
813 | EXPORT_SYMBOL(ieee80211_find_sta); | 847 | EXPORT_SYMBOL(ieee80211_find_sta); |
848 | |||
849 | /* powersave support code */ | ||
850 | void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) | ||
851 | { | ||
852 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
853 | struct ieee80211_local *local = sdata->local; | ||
854 | int sent, buffered; | ||
855 | |||
856 | drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta); | ||
857 | |||
858 | if (!skb_queue_empty(&sta->ps_tx_buf)) | ||
859 | sta_info_clear_tim_bit(sta); | ||
860 | |||
861 | /* Send all buffered frames to the station */ | ||
862 | sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered); | ||
863 | buffered = ieee80211_add_pending_skbs(local, &sta->ps_tx_buf); | ||
864 | sent += buffered; | ||
865 | local->total_ps_buffered -= buffered; | ||
866 | |||
867 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG | ||
868 | printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames " | ||
869 | "since STA not sleeping anymore\n", sdata->dev->name, | ||
870 | sta->sta.addr, sta->sta.aid, sent - buffered, buffered); | ||
871 | #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ | ||
872 | } | ||
873 | |||
874 | void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) | ||
875 | { | ||
876 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
877 | struct ieee80211_local *local = sdata->local; | ||
878 | struct sk_buff *skb; | ||
879 | int no_pending_pkts; | ||
880 | |||
881 | skb = skb_dequeue(&sta->tx_filtered); | ||
882 | if (!skb) { | ||
883 | skb = skb_dequeue(&sta->ps_tx_buf); | ||
884 | if (skb) | ||
885 | local->total_ps_buffered--; | ||
886 | } | ||
887 | no_pending_pkts = skb_queue_empty(&sta->tx_filtered) && | ||
888 | skb_queue_empty(&sta->ps_tx_buf); | ||
889 | |||
890 | if (skb) { | ||
891 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
892 | struct ieee80211_hdr *hdr = | ||
893 | (struct ieee80211_hdr *) skb->data; | ||
894 | |||
895 | /* | ||
896 | * Tell TX path to send this frame even though the STA may | ||
897 | * still remain is PS mode after this frame exchange. | ||
898 | */ | ||
899 | info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE; | ||
900 | |||
901 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG | ||
902 | printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n", | ||
903 | sta->sta.addr, sta->sta.aid, | ||
904 | skb_queue_len(&sta->ps_tx_buf)); | ||
905 | #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ | ||
906 | |||
907 | /* Use MoreData flag to indicate whether there are more | ||
908 | * buffered frames for this STA */ | ||
909 | if (no_pending_pkts) | ||
910 | hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA); | ||
911 | else | ||
912 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); | ||
913 | |||
914 | ieee80211_add_pending_skb(local, skb); | ||
915 | |||
916 | if (no_pending_pkts) | ||
917 | sta_info_clear_tim_bit(sta); | ||
918 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG | ||
919 | } else { | ||
920 | /* | ||
921 | * FIXME: This can be the result of a race condition between | ||
922 | * us expiring a frame and the station polling for it. | ||
923 | * Should we send it a null-func frame indicating we | ||
924 | * have nothing buffered for it? | ||
925 | */ | ||
926 | printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " | ||
927 | "though there are no buffered frames for it\n", | ||
928 | sdata->dev->name, sta->sta.addr); | ||
929 | #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ | ||
930 | } | ||
931 | } | ||
932 | |||
933 | void ieee80211_sta_block_awake(struct ieee80211_hw *hw, | ||
934 | struct ieee80211_sta *pubsta, bool block) | ||
935 | { | ||
936 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); | ||
937 | |||
938 | if (block) | ||
939 | set_sta_flags(sta, WLAN_STA_PS_DRIVER); | ||
940 | else | ||
941 | ieee80211_queue_work(hw, &sta->drv_unblock_wk); | ||
942 | } | ||
943 | EXPORT_SYMBOL(ieee80211_sta_block_awake); | ||