Commit 375177bf authored by Vivek Natarajan's avatar Vivek Natarajan Committed by John W. Linville

mac80211: Retry null data frame for power save.

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: default avatarVivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent e15276a4
...@@ -945,6 +945,11 @@ enum ieee80211_tkip_key_type { ...@@ -945,6 +945,11 @@ enum ieee80211_tkip_key_type {
* Hardware supports Unscheduled Automatic Power Save Delivery * Hardware supports Unscheduled Automatic Power Save Delivery
* (U-APSD) in managed mode. The mode is configured with * (U-APSD) in managed mode. The mode is configured with
* conf_tx() operation. * 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 { enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
...@@ -965,6 +970,7 @@ enum ieee80211_hw_flags { ...@@ -965,6 +970,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15, IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
}; };
/** /**
......
...@@ -316,6 +316,7 @@ enum ieee80211_sta_flags { ...@@ -316,6 +316,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_CSA_RECEIVED = BIT(5), IEEE80211_STA_CSA_RECEIVED = BIT(5),
IEEE80211_STA_MFP_ENABLED = BIT(6), IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7), IEEE80211_STA_UAPSD_ENABLED = BIT(7),
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
}; };
struct ieee80211_if_managed { struct ieee80211_if_managed {
......
...@@ -434,9 +434,12 @@ static void ieee80211_enable_ps(struct ieee80211_local *local, ...@@ -434,9 +434,12 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
} else { } else {
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
ieee80211_send_nullfunc(local, sdata, 1); ieee80211_send_nullfunc(local, sdata, 1);
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
conf->flags |= IEEE80211_CONF_PS; conf->flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
} }
}
} }
static void ieee80211_change_ps(struct ieee80211_local *local) static void ieee80211_change_ps(struct ieee80211_local *local)
...@@ -541,6 +544,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) ...@@ -541,6 +544,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
container_of(work, struct ieee80211_local, container_of(work, struct ieee80211_local,
dynamic_ps_enable_work); dynamic_ps_enable_work);
struct ieee80211_sub_if_data *sdata = local->ps_sdata; 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 */ /* can only happen when PS was just disabled anyway */
if (!sdata) if (!sdata)
...@@ -549,11 +553,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) ...@@ -549,11 +553,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (local->hw.conf.flags & IEEE80211_CONF_PS) if (local->hw.conf.flags & IEEE80211_CONF_PS)
return; 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); ieee80211_send_nullfunc(local, sdata, 1);
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; local->hw.conf.flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
} }
void ieee80211_dynamic_ps_timer(unsigned long data) void ieee80211_dynamic_ps_timer(unsigned long data)
...@@ -1892,6 +1901,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ...@@ -1892,6 +1901,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
return -ENOMEM; return -ENOMEM;
ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
......
...@@ -188,6 +188,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -188,6 +188,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rcu_read_lock(); rcu_read_lock();
sband = local->hw.wiphy->bands[info->band]; sband = local->hw.wiphy->bands[info->band];
fc = hdr->frame_control;
for_each_sta_info(local, hdr->addr1, sta, tmp) { for_each_sta_info(local, hdr->addr1, sta, tmp) {
/* skip wrong virtual interface */ /* skip wrong virtual interface */
...@@ -205,8 +206,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -205,8 +206,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
return; return;
} }
fc = hdr->frame_control;
if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) && if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
(ieee80211_is_data_qos(fc))) { (ieee80211_is_data_qos(fc))) {
u16 tid, ssn; u16 tid, ssn;
...@@ -275,6 +274,20 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -275,6 +274,20 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
local->dot11FailedCount++; 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 */ /* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb); skb_orphan(skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment