Skip to content

Commit

Permalink
mac80211: Retry null data frame for power save.
Browse files Browse the repository at this point in the history
Even if the null data frame is not acked by the AP, mac80211
goes into power save. This might lead to loss of frames
from the AP.
Prevent this by restarting dynamic_ps_timer when ack is not
received for null data frames.

Cc: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Vivek Natarajan authored and John W. Linville committed Feb 9, 2010
1 parent e15276a commit 375177b
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 7 deletions.
6 changes: 6 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,11 @@ enum ieee80211_tkip_key_type {
* Hardware supports Unscheduled Automatic Power Save Delivery
* (U-APSD) in managed mode. The mode is configured with
* conf_tx() operation.
*
* @IEEE80211_HW_REPORTS_TX_ACK_STATUS:
* Hardware can provide ack status reports of Tx frames to
* the stack.
*
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
Expand All @@ -965,6 +970,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
};

/**
Expand Down
1 change: 1 addition & 0 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_CSA_RECEIVED = BIT(5),
IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
};

struct ieee80211_if_managed {
Expand Down
20 changes: 15 additions & 5 deletions net/mac80211/mlme.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,11 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
} else {
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
ieee80211_send_nullfunc(local, sdata, 1);
conf->flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);

if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
conf->flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
}
}

Expand Down Expand Up @@ -541,6 +544,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
container_of(work, struct ieee80211_local,
dynamic_ps_enable_work);
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;

/* can only happen when PS was just disabled anyway */
if (!sdata)
Expand All @@ -549,11 +553,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (local->hw.conf.flags & IEEE80211_CONF_PS)
return;

if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
(!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
ieee80211_send_nullfunc(local, sdata, 1);

local->hw.conf.flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ||
(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
local->hw.conf.flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
}

void ieee80211_dynamic_ps_timer(unsigned long data)
Expand Down Expand Up @@ -1892,6 +1901,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
return -ENOMEM;

ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;

for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
Expand Down
17 changes: 15 additions & 2 deletions net/mac80211/status.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rcu_read_lock();

sband = local->hw.wiphy->bands[info->band];
fc = hdr->frame_control;

for_each_sta_info(local, hdr->addr1, sta, tmp) {
/* skip wrong virtual interface */
Expand All @@ -205,8 +206,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}

fc = hdr->frame_control;

if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
(ieee80211_is_data_qos(fc))) {
u16 tid, ssn;
Expand Down Expand Up @@ -275,6 +274,20 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
local->dot11FailedCount++;
}

if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
local->ps_sdata && !(local->scanning)) {
if (info->flags & IEEE80211_TX_STAT_ACK) {
local->ps_sdata->u.mgd.flags |=
IEEE80211_STA_NULLFUNC_ACKED;
ieee80211_queue_work(&local->hw,
&local->dynamic_ps_enable_work);
} else
mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies(10));
}

/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);

Expand Down

0 comments on commit 375177b

Please sign in to comment.