Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 171449
b: refs/heads/master
c: af81858
h: refs/heads/master
i:
  171447: 1193c92
v: v3
  • Loading branch information
Johannes Berg authored and John W. Linville committed Nov 6, 2009
1 parent cfa694c commit bb180a5
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 86 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 70d9f405d09e334b609702d88ee03b6119c4b45e
refs/heads/master: af81858172cc0f3da81946aab919c26e4b364efc
32 changes: 32 additions & 0 deletions trunk/include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -2136,6 +2136,38 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw,
const u8 *addr);

/**
* ieee80211_sta_block_awake - block station from waking up
* @hw: the hardware
* @pubsta: the station
* @block: whether to block or unblock
*
* Some devices require that all frames that are on the queues
* for a specific station that went to sleep are flushed before
* a poll response or frames after the station woke up can be
* delivered to that it. Note that such frames must be rejected
* by the driver as filtered, with the appropriate status flag.
*
* This function allows implementing this mode in a race-free
* manner.
*
* To do this, a driver must keep track of the number of frames
* still enqueued for a specific station. If this number is not
* zero when the station goes to sleep, the driver must call
* this function to force mac80211 to consider the station to
* be asleep regardless of the station's actual state. Once the
* number of outstanding frames reaches zero, the driver must
* call this function again to unblock the station. That will
* cause mac80211 to be able to send ps-poll responses, and if
* the station queried in the meantime then frames will also
* be sent out as a result of this. Additionally, the driver
* will be notified that the station woke up some time after
* it is unblocked, regardless of whether the station actually
* woke up while blocked or not.
*/
void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
struct ieee80211_sta *pubsta, bool block);

/**
* ieee80211_beacon_loss - inform hardware does not receive beacons
*
Expand Down
5 changes: 3 additions & 2 deletions trunk/net/mac80211/debugfs_sta.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
char buf[100];
struct sta_info *sta = file->private_data;
u32 staflags = get_sta_flags(sta);
int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s",
int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s",
staflags & WLAN_STA_AUTH ? "AUTH\n" : "",
staflags & WLAN_STA_ASSOC ? "ASSOC\n" : "",
staflags & WLAN_STA_PS ? "PS\n" : "",
staflags & WLAN_STA_PS_STA ? "PS (sta)\n" : "",
staflags & WLAN_STA_PS_DRIVER ? "PS (driver)\n" : "",
staflags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "",
staflags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "",
staflags & WLAN_STA_WME ? "WME\n" : "",
Expand Down
8 changes: 4 additions & 4 deletions trunk/net/mac80211/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -385,13 +385,13 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
* can be unknown, for example with different interrupt status
* bits.
*/
if (test_sta_flags(sta, WLAN_STA_PS) &&
if (test_sta_flags(sta, WLAN_STA_PS_STA) &&
skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
skb_queue_tail(&sta->tx_filtered, skb);
return;
}

if (!test_sta_flags(sta, WLAN_STA_PS) &&
if (!test_sta_flags(sta, WLAN_STA_PS_STA) &&
!(info->flags & IEEE80211_TX_INTFL_RETRIED)) {
/* Software retry the packet once */
info->flags |= IEEE80211_TX_INTFL_RETRIED;
Expand All @@ -406,7 +406,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
"queue_len=%d PS=%d @%lu\n",
wiphy_name(local->hw.wiphy),
skb_queue_len(&sta->tx_filtered),
!!test_sta_flags(sta, WLAN_STA_PS), jiffies);
!!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies);
#endif
dev_kfree_skb(skb);
}
Expand Down Expand Up @@ -446,7 +446,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)

if (sta) {
if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
test_sta_flags(sta, WLAN_STA_PS)) {
test_sta_flags(sta, WLAN_STA_PS_STA)) {
/*
* The STA is in power save mode, so assume
* that this TX packet failed because of that.
Expand Down
86 changes: 15 additions & 71 deletions trunk/net/mac80211/rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ static void ap_sta_ps_start(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;

atomic_inc(&sdata->bss->num_sta_ps);
set_sta_flags(sta, WLAN_STA_PS);
set_sta_flags(sta, WLAN_STA_PS_STA);
drv_sta_notify(local, &sdata->vif, STA_NOTIFY_SLEEP, &sta->sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n",
Expand All @@ -792,33 +792,25 @@ static void ap_sta_ps_start(struct sta_info *sta)
static void ap_sta_ps_end(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
int sent, buffered;

atomic_dec(&sdata->bss->num_sta_ps);

clear_sta_flags(sta, WLAN_STA_PS);
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);
clear_sta_flags(sta, WLAN_STA_PS_STA);

#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */

/* 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;

if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) {
#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);
printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n",
sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
return;
}

ieee80211_sta_ps_deliver_wakeup(sta);
}

static ieee80211_rx_result debug_noinline
Expand Down Expand Up @@ -866,7 +858,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (!ieee80211_has_morefrags(hdr->frame_control) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
if (test_sta_flags(sta, WLAN_STA_PS)) {
if (test_sta_flags(sta, WLAN_STA_PS_STA)) {
/*
* Ignore doze->wake transitions that are
* indicated by non-data frames, the standard
Expand Down Expand Up @@ -1094,9 +1086,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct sk_buff *skb;
int no_pending_pkts;
struct ieee80211_sub_if_data *sdata = rx->sdata;
__le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control;

if (likely(!rx->sta || !ieee80211_is_pspoll(fc) ||
Expand All @@ -1107,56 +1097,10 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
return RX_DROP_UNUSABLE;

skb = skb_dequeue(&rx->sta->tx_filtered);
if (!skb) {
skb = skb_dequeue(&rx->sta->ps_tx_buf);
if (skb)
rx->local->total_ps_buffered--;
}
no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) &&
skb_queue_empty(&rx->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",
rx->sta->sta.addr, rx->sta->sta.aid,
skb_queue_len(&rx->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(rx->local, skb);

if (no_pending_pkts)
sta_info_clear_tim_bit(rx->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",
rx->dev->name, rx->sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER))
ieee80211_sta_ps_deliver_poll_response(rx->sta);
else
set_sta_flags(rx->sta, WLAN_STA_PSPOLL);

/* Free PS Poll skb here instead of returning RX_DROP that would
* count as an dropped frame. */
Expand Down
118 changes: 117 additions & 1 deletion trunk/net/mac80211/sta_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,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);

Expand Down Expand Up @@ -259,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)
{
Expand All @@ -272,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;
Expand Down Expand Up @@ -478,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);
Expand Down Expand Up @@ -825,3 +845,99 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *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);
Loading

0 comments on commit bb180a5

Please sign in to comment.