aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2014-01-08 18:00:38 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-01-10 03:50:02 -0500
commitb77cf4f8e1892e192ec52df5dd8c158b300fc496 (patch)
treeae0ba22c24b9c125a004ae672113cf1b8236d4f7 /net
parent03c8c06f2d080b841ecbfc63253228ba6efcab08 (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.c70
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
1154static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, 1154static 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 /*