diff options
author | Johannes Berg <johannes.berg@intel.com> | 2014-01-08 18:00:38 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-01-10 03:50:02 -0500 |
commit | b77cf4f8e1892e192ec52df5dd8c158b300fc496 (patch) | |
tree | ae0ba22c24b9c125a004ae672113cf1b8236d4f7 /net | |
parent | 03c8c06f2d080b841ecbfc63253228ba6efcab08 (diff) |
mac80211: handle MMPDUs at EOSP correctly
If a uAPSD service period ends with an MMPDU, we currently just
send that MMPDU, but it obviously won't get the EOSP bit set as
it doesn't have a QoS header. This contradicts the standard, so
add a QoS-nulldata frame after the MMPDU to properly terminate
the service period with a frame that has EOSP set.
Also fix a bug wrt. the TID for the MMPDU, it shouldn't be set
to 0 unconditionally but use the actual TID that was assigned.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/sta_info.c | 70 |
1 files changed, 57 insertions, 13 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 93e2157a5b7b..decd30c1e290 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -1153,7 +1153,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) | |||
1153 | 1153 | ||
1154 | static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, | 1154 | static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, |
1155 | struct sta_info *sta, int tid, | 1155 | struct sta_info *sta, int tid, |
1156 | enum ieee80211_frame_release_type reason) | 1156 | enum ieee80211_frame_release_type reason, |
1157 | bool call_driver) | ||
1157 | { | 1158 | { |
1158 | struct ieee80211_local *local = sdata->local; | 1159 | struct ieee80211_local *local = sdata->local; |
1159 | struct ieee80211_qos_hdr *nullfunc; | 1160 | struct ieee80211_qos_hdr *nullfunc; |
@@ -1211,7 +1212,9 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, | |||
1211 | IEEE80211_TX_STATUS_EOSP | | 1212 | IEEE80211_TX_STATUS_EOSP | |
1212 | IEEE80211_TX_CTL_REQ_TX_STATUS; | 1213 | IEEE80211_TX_CTL_REQ_TX_STATUS; |
1213 | 1214 | ||
1214 | drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); | 1215 | if (call_driver) |
1216 | drv_allow_buffered_frames(local, sta, BIT(tid), 1, | ||
1217 | reason, false); | ||
1215 | 1218 | ||
1216 | skb->dev = sdata->dev; | 1219 | skb->dev = sdata->dev; |
1217 | 1220 | ||
@@ -1334,12 +1337,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, | |||
1334 | /* This will evaluate to 1, 3, 5 or 7. */ | 1337 | /* This will evaluate to 1, 3, 5 or 7. */ |
1335 | tid = 7 - ((ffs(~ignored_acs) - 1) << 1); | 1338 | tid = 7 - ((ffs(~ignored_acs) - 1) << 1); |
1336 | 1339 | ||
1337 | ieee80211_send_null_response(sdata, sta, tid, reason); | 1340 | ieee80211_send_null_response(sdata, sta, tid, reason, true); |
1338 | } else if (!driver_release_tids) { | 1341 | } else if (!driver_release_tids) { |
1339 | struct sk_buff_head pending; | 1342 | struct sk_buff_head pending; |
1340 | struct sk_buff *skb; | 1343 | struct sk_buff *skb; |
1341 | int num = 0; | 1344 | int num = 0; |
1342 | u16 tids = 0; | 1345 | u16 tids = 0; |
1346 | bool need_null = false; | ||
1343 | 1347 | ||
1344 | skb_queue_head_init(&pending); | 1348 | skb_queue_head_init(&pending); |
1345 | 1349 | ||
@@ -1373,22 +1377,57 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, | |||
1373 | ieee80211_is_qos_nullfunc(hdr->frame_control)) | 1377 | ieee80211_is_qos_nullfunc(hdr->frame_control)) |
1374 | qoshdr = ieee80211_get_qos_ctl(hdr); | 1378 | qoshdr = ieee80211_get_qos_ctl(hdr); |
1375 | 1379 | ||
1376 | /* end service period after last frame */ | 1380 | tids |= BIT(skb->priority); |
1377 | if (skb_queue_empty(&frames)) { | ||
1378 | if (reason == IEEE80211_FRAME_RELEASE_UAPSD && | ||
1379 | qoshdr) | ||
1380 | *qoshdr |= IEEE80211_QOS_CTL_EOSP; | ||
1381 | 1381 | ||
1382 | __skb_queue_tail(&pending, skb); | ||
1383 | |||
1384 | /* end service period after last frame or add one */ | ||
1385 | if (!skb_queue_empty(&frames)) | ||
1386 | continue; | ||
1387 | |||
1388 | if (reason != IEEE80211_FRAME_RELEASE_UAPSD) { | ||
1389 | /* for PS-Poll, there's only one frame */ | ||
1382 | info->flags |= IEEE80211_TX_STATUS_EOSP | | 1390 | info->flags |= IEEE80211_TX_STATUS_EOSP | |
1383 | IEEE80211_TX_CTL_REQ_TX_STATUS; | 1391 | IEEE80211_TX_CTL_REQ_TX_STATUS; |
1392 | break; | ||
1384 | } | 1393 | } |
1385 | 1394 | ||
1386 | if (qoshdr) | 1395 | /* For uAPSD, things are a bit more complicated. If the |
1387 | tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); | 1396 | * last frame has a QoS header (i.e. is a QoS-data or |
1388 | else | 1397 | * QoS-nulldata frame) then just set the EOSP bit there |
1389 | tids |= BIT(0); | 1398 | * and be done. |
1399 | * If the frame doesn't have a QoS header (which means | ||
1400 | * it should be a bufferable MMPDU) then we can't set | ||
1401 | * the EOSP bit in the QoS header; add a QoS-nulldata | ||
1402 | * frame to the list to send it after the MMPDU. | ||
1403 | * | ||
1404 | * Note that this code is only in the mac80211-release | ||
1405 | * code path, we assume that the driver will not buffer | ||
1406 | * anything but QoS-data frames, or if it does, will | ||
1407 | * create the QoS-nulldata frame by itself if needed. | ||
1408 | * | ||
1409 | * Cf. 802.11-2012 10.2.1.10 (c). | ||
1410 | */ | ||
1411 | if (qoshdr) { | ||
1412 | *qoshdr |= IEEE80211_QOS_CTL_EOSP; | ||
1390 | 1413 | ||
1391 | __skb_queue_tail(&pending, skb); | 1414 | info->flags |= IEEE80211_TX_STATUS_EOSP | |
1415 | IEEE80211_TX_CTL_REQ_TX_STATUS; | ||
1416 | } else { | ||
1417 | /* The standard isn't completely clear on this | ||
1418 | * as it says the more-data bit should be set | ||
1419 | * if there are more BUs. The QoS-Null frame | ||
1420 | * we're about to send isn't buffered yet, we | ||
1421 | * only create it below, but let's pretend it | ||
1422 | * was buffered just in case some clients only | ||
1423 | * expect more-data=0 when eosp=1. | ||
1424 | */ | ||
1425 | hdr->frame_control |= | ||
1426 | cpu_to_le16(IEEE80211_FCTL_MOREDATA); | ||
1427 | need_null = true; | ||
1428 | num++; | ||
1429 | } | ||
1430 | break; | ||
1392 | } | 1431 | } |
1393 | 1432 | ||
1394 | drv_allow_buffered_frames(local, sta, tids, num, | 1433 | drv_allow_buffered_frames(local, sta, tids, num, |
@@ -1396,6 +1435,11 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, | |||
1396 | 1435 | ||
1397 | ieee80211_add_pending_skbs(local, &pending); | 1436 | ieee80211_add_pending_skbs(local, &pending); |
1398 | 1437 | ||
1438 | if (need_null) | ||
1439 | ieee80211_send_null_response( | ||
1440 | sdata, sta, find_highest_prio_tid(tids), | ||
1441 | reason, false); | ||
1442 | |||
1399 | sta_info_recalc_tim(sta); | 1443 | sta_info_recalc_tim(sta); |
1400 | } else { | 1444 | } else { |
1401 | /* | 1445 | /* |