From deeaee197b0fa694ba6c8f02cdb57b3be7115b4f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:35 +0200 Subject: [PATCH] mac80211: reply only once to each PS-poll If a PS-poll frame is retried (but was received) there is no way to detect that since it has no sequence number. As a consequence, the standard asks us to not react to PS-poll frames until the response to one made it out (was ACKed or lost). Implement this by using the WLAN_STA_SP flags to also indicate a PS-Poll "service period" and the IEEE80211_TX_STATUS_EOSP flag for the response packet to indicate the end of the "SP" as usual. We could use separate flags, but that will most likely completely confuse drivers, and while the standard doesn't exclude simultaneously polling using uAPSD and PS-Poll, doing that seems quite problematic. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 9 ++++++++- net/mac80211/rx.c | 10 ++++++---- net/mac80211/sta_info.c | 22 +++++++++++----------- net/mac80211/sta_info.h | 2 +- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dd07964ef154..ee6449eff7dc 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -370,7 +370,8 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period, * when its status is reported the service period ends. For frames in * an SP that mac80211 transmits, it is already set; for driver frames - * the driver may set this flag. + * the driver may set this flag. It is also used to do the same for + * PS-Poll responses. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -1959,6 +1960,12 @@ enum ieee80211_frame_release_type { * more-data bit must always be set. * The @tids parameter tells the driver which TIDs to release frames * from, for PS-poll it will always have only a single bit set. + * In the case this is used for a PS-poll initiated release, the + * @num_frames parameter will always be 1 so code can be shared. In + * this case the driver must also set %IEEE80211_TX_STATUS_EOSP flag + * on the TX status (and must report TX status) so that the PS-poll + * period is properly ended. This is used to avoid sending multiple + * responses for a retried PS-poll frame. * In the case this is used for uAPSD, the @num_frames parameter may be * bigger than one, but the driver may send fewer frames (it must send * at least one, however). In this case it is also responsible for diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9a703f00b5fb..32c8ee43f720 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1194,10 +1194,12 @@ ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { - 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); + if (!test_sta_flags(rx->sta, WLAN_STA_SP)) { + 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. */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index d9cb56f548a9..5732e4d0cc21 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1217,13 +1217,12 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, /* * Tell TX path to send this frame even though the * STA may still remain is PS mode after this frame - * exchange. + * exchange. Also set EOSP to indicate this packet + * ends the poll/service period. */ - info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; - - if (uapsd) - info->flags |= IEEE80211_TX_STATUS_EOSP | - IEEE80211_TX_CTL_REQ_TX_STATUS; + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE | + IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; ieee80211_xmit(sdata, skb); } @@ -1241,6 +1240,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, unsigned long driver_release_tids = 0; struct sk_buff_head frames; + /* Service or PS-Poll period starts */ + set_sta_flags(sta, WLAN_STA_SP); + __skb_queue_head_init(&frames); /* @@ -1357,10 +1359,11 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* set EOSP for the frame */ u8 *p = ieee80211_get_qos_ctl(hdr); *p |= IEEE80211_QOS_CTL_EOSP; - info->flags |= IEEE80211_TX_STATUS_EOSP | - IEEE80211_TX_CTL_REQ_TX_STATUS; } + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + __skb_queue_tail(&pending, skb); } @@ -1422,9 +1425,6 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) if (!delivery_enabled) return; - /* Ohh, finally, the service period starts :-) */ - set_sta_flags(sta, WLAN_STA_SP); - switch (sta->sta.max_sp) { case 1: n_frames = 2; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 751ad25f925f..348847a32630 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -50,7 +50,7 @@ * keeping station in power-save mode, reply when the driver * unblocks the station. * @WLAN_STA_SP: Station is in a service period, so don't try to - * reply to other uAPSD trigger frames. + * reply to other uAPSD trigger frames or PS-Poll. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, -- 2.30.2