aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2014-01-24 08:41:44 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-02-04 15:58:15 -0500
commitb4ba544c8c1349afd44e10aebec03c90e9b71d98 (patch)
tree0c918d83b412f4155f3ea882626b0422315d32c5
parent953467d32150e2ae15aa3d5396ada175d265a412 (diff)
mac80211: fix bufferable MMPDU RX handling
Action, disassoc and deauth frames are bufferable, and as such don't have the PM bit in the frame control field reserved which means we need to react to the bit when receiving in such a frame. Fix this by introducing a new helper ieee80211_is_bufferable_mmpdu() and using it for the RX path that currently ignores the PM bit in any non-data frames for doze->wake transitions, but listens to it in all frames for wake->doze transitions, both of which are wrong. Also use the new helper in the TX path to clean up the code. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--include/linux/ieee80211.h14
-rw-r--r--net/mac80211/rx.c19
-rw-r--r--net/mac80211/tx.c5
3 files changed, 23 insertions, 15 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 923c478030a3..1e3912d1b029 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -597,6 +597,20 @@ static inline int ieee80211_is_qos_nullfunc(__le16 fc)
597} 597}
598 598
599/** 599/**
600 * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU
601 * @fc: frame control field in little-endian byteorder
602 */
603static inline bool ieee80211_is_bufferable_mmpdu(__le16 fc)
604{
605 /* IEEE 802.11-2012, definition of "bufferable management frame";
606 * note that this ignores the IBSS special case. */
607 return ieee80211_is_mgmt(fc) &&
608 (ieee80211_is_action(fc) ||
609 ieee80211_is_disassoc(fc) ||
610 ieee80211_is_deauth(fc));
611}
612
613/**
600 * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set 614 * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set
601 * @seq_ctrl: frame sequence control bytes in little-endian byteorder 615 * @seq_ctrl: frame sequence control bytes in little-endian byteorder
602 */ 616 */
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 3b7a750ebc70..79a89fe9d616 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1311,18 +1311,15 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
1311 !ieee80211_has_morefrags(hdr->frame_control) && 1311 !ieee80211_has_morefrags(hdr->frame_control) &&
1312 !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && 1312 !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&
1313 (rx->sdata->vif.type == NL80211_IFTYPE_AP || 1313 (rx->sdata->vif.type == NL80211_IFTYPE_AP ||
1314 rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) { 1314 rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
1315 /* PM bit is only checked in frames where it isn't reserved,
1316 * in AP mode it's reserved in non-bufferable management frames
1317 * (cf. IEEE 802.11-2012 8.2.4.1.7 Power Management field)
1318 */
1319 (!ieee80211_is_mgmt(hdr->frame_control) ||
1320 ieee80211_is_bufferable_mmpdu(hdr->frame_control))) {
1315 if (test_sta_flag(sta, WLAN_STA_PS_STA)) { 1321 if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
1316 /* 1322 if (!ieee80211_has_pm(hdr->frame_control))
1317 * Ignore doze->wake transitions that are
1318 * indicated by non-data frames, the standard
1319 * is unclear here, but for example going to
1320 * PS mode and then scanning would cause a
1321 * doze->wake transition for the probe request,
1322 * and that is clearly undesirable.
1323 */
1324 if (ieee80211_is_data(hdr->frame_control) &&
1325 !ieee80211_has_pm(hdr->frame_control))
1326 sta_ps_end(sta); 1323 sta_ps_end(sta);
1327 } else { 1324 } else {
1328 if (ieee80211_has_pm(hdr->frame_control)) 1325 if (ieee80211_has_pm(hdr->frame_control))
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 07a7f38dc348..5476a69b45c9 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -522,11 +522,8 @@ ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
522 if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED)) 522 if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
523 return TX_CONTINUE; 523 return TX_CONTINUE;
524 524
525 /* only deauth, disassoc and action are bufferable MMPDUs */
526 if (ieee80211_is_mgmt(hdr->frame_control) && 525 if (ieee80211_is_mgmt(hdr->frame_control) &&
527 !ieee80211_is_deauth(hdr->frame_control) && 526 !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
528 !ieee80211_is_disassoc(hdr->frame_control) &&
529 !ieee80211_is_action(hdr->frame_control)) {
530 if (tx->flags & IEEE80211_TX_UNICAST) 527 if (tx->flags & IEEE80211_TX_UNICAST)
531 info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; 528 info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
532 return TX_CONTINUE; 529 return TX_CONTINUE;