diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-09-29 10:04:34 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-09-30 15:57:16 -0400 |
commit | ce662b44ce22e3e8886104d5feb2a451d7ba560f (patch) | |
tree | eb584058dec73807f192fc19725adfece5bad539 /net/mac80211 | |
parent | 47086fc51aa2220f58049704a8b73e4fcdf372b9 (diff) |
mac80211: send (QoS) Null if no buffered frames
For PS-poll, there's a possible race between
us expiring a frame and the station polling
for it -- send it a null frame in that case.
For uAPSD, the standard says that we have to
send a frame in each SP, so send null if we
don't have any other frames.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/ieee80211_i.h | 1 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 100 | ||||
-rw-r--r-- | net/mac80211/tx.c | 3 |
3 files changed, 92 insertions, 12 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 674b23ea14d7..da3206450192 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -1272,6 +1272,7 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke | |||
1272 | struct ieee80211_hdr *hdr, const u8 *tsc, | 1272 | struct ieee80211_hdr *hdr, const u8 *tsc, |
1273 | gfp_t gfp); | 1273 | gfp_t gfp); |
1274 | void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); | 1274 | void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); |
1275 | void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); | ||
1275 | void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); | 1276 | void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); |
1276 | void ieee802_11_parse_elems(u8 *start, size_t len, | 1277 | void ieee802_11_parse_elems(u8 *start, size_t len, |
1277 | struct ieee802_11_elems *elems); | 1278 | struct ieee802_11_elems *elems); |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f9079e478f77..d9cb56f548a9 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "sta_info.h" | 24 | #include "sta_info.h" |
25 | #include "debugfs_sta.h" | 25 | #include "debugfs_sta.h" |
26 | #include "mesh.h" | 26 | #include "mesh.h" |
27 | #include "wme.h" | ||
27 | 28 | ||
28 | /** | 29 | /** |
29 | * DOC: STA information lifetime rules | 30 | * DOC: STA information lifetime rules |
@@ -247,10 +248,16 @@ static void sta_unblock(struct work_struct *wk) | |||
247 | ieee80211_sta_ps_deliver_wakeup(sta); | 248 | ieee80211_sta_ps_deliver_wakeup(sta); |
248 | else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { | 249 | else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { |
249 | clear_sta_flags(sta, WLAN_STA_PS_DRIVER); | 250 | clear_sta_flags(sta, WLAN_STA_PS_DRIVER); |
251 | |||
252 | local_bh_disable(); | ||
250 | ieee80211_sta_ps_deliver_poll_response(sta); | 253 | ieee80211_sta_ps_deliver_poll_response(sta); |
254 | local_bh_enable(); | ||
251 | } else if (test_and_clear_sta_flags(sta, WLAN_STA_UAPSD)) { | 255 | } else if (test_and_clear_sta_flags(sta, WLAN_STA_UAPSD)) { |
252 | clear_sta_flags(sta, WLAN_STA_PS_DRIVER); | 256 | clear_sta_flags(sta, WLAN_STA_PS_DRIVER); |
257 | |||
258 | local_bh_disable(); | ||
253 | ieee80211_sta_ps_deliver_uapsd(sta); | 259 | ieee80211_sta_ps_deliver_uapsd(sta); |
260 | local_bh_enable(); | ||
254 | } else | 261 | } else |
255 | clear_sta_flags(sta, WLAN_STA_PS_DRIVER); | 262 | clear_sta_flags(sta, WLAN_STA_PS_DRIVER); |
256 | } | 263 | } |
@@ -1157,6 +1164,70 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) | |||
1157 | #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ | 1164 | #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ |
1158 | } | 1165 | } |
1159 | 1166 | ||
1167 | static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, | ||
1168 | struct sta_info *sta, int tid, | ||
1169 | bool uapsd) | ||
1170 | { | ||
1171 | struct ieee80211_local *local = sdata->local; | ||
1172 | struct ieee80211_qos_hdr *nullfunc; | ||
1173 | struct sk_buff *skb; | ||
1174 | int size = sizeof(*nullfunc); | ||
1175 | __le16 fc; | ||
1176 | bool qos = test_sta_flags(sta, WLAN_STA_WME); | ||
1177 | struct ieee80211_tx_info *info; | ||
1178 | |||
1179 | if (qos) { | ||
1180 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
1181 | IEEE80211_STYPE_QOS_NULLFUNC | | ||
1182 | IEEE80211_FCTL_FROMDS); | ||
1183 | } else { | ||
1184 | size -= 2; | ||
1185 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
1186 | IEEE80211_STYPE_NULLFUNC | | ||
1187 | IEEE80211_FCTL_FROMDS); | ||
1188 | } | ||
1189 | |||
1190 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); | ||
1191 | if (!skb) | ||
1192 | return; | ||
1193 | |||
1194 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
1195 | |||
1196 | nullfunc = (void *) skb_put(skb, size); | ||
1197 | nullfunc->frame_control = fc; | ||
1198 | nullfunc->duration_id = 0; | ||
1199 | memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); | ||
1200 | memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); | ||
1201 | memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); | ||
1202 | |||
1203 | if (qos) { | ||
1204 | skb->priority = tid; | ||
1205 | |||
1206 | skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); | ||
1207 | |||
1208 | nullfunc->qos_ctrl = cpu_to_le16(tid); | ||
1209 | |||
1210 | if (uapsd) | ||
1211 | nullfunc->qos_ctrl |= | ||
1212 | cpu_to_le16(IEEE80211_QOS_CTL_EOSP); | ||
1213 | } | ||
1214 | |||
1215 | info = IEEE80211_SKB_CB(skb); | ||
1216 | |||
1217 | /* | ||
1218 | * Tell TX path to send this frame even though the | ||
1219 | * STA may still remain is PS mode after this frame | ||
1220 | * exchange. | ||
1221 | */ | ||
1222 | info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; | ||
1223 | |||
1224 | if (uapsd) | ||
1225 | info->flags |= IEEE80211_TX_STATUS_EOSP | | ||
1226 | IEEE80211_TX_CTL_REQ_TX_STATUS; | ||
1227 | |||
1228 | ieee80211_xmit(sdata, skb); | ||
1229 | } | ||
1230 | |||
1160 | static void | 1231 | static void |
1161 | ieee80211_sta_ps_deliver_response(struct sta_info *sta, | 1232 | ieee80211_sta_ps_deliver_response(struct sta_info *sta, |
1162 | int n_frames, u8 ignored_acs, | 1233 | int n_frames, u8 ignored_acs, |
@@ -1228,19 +1299,28 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, | |||
1228 | } | 1299 | } |
1229 | 1300 | ||
1230 | if (!found) { | 1301 | if (!found) { |
1231 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG | 1302 | int tid; |
1303 | |||
1232 | /* | 1304 | /* |
1233 | * FIXME: This can be the result of a race condition between | 1305 | * For PS-Poll, this can only happen due to a race condition |
1234 | * us expiring a frame and the station polling for it. | 1306 | * when we set the TIM bit and the station notices it, but |
1235 | * Should we send it a null-func frame indicating we | 1307 | * before it can poll for the frame we expire it. |
1236 | * have nothing buffered for it? | 1308 | * |
1309 | * For uAPSD, this is said in the standard (11.2.1.5 h): | ||
1310 | * At each unscheduled SP for a non-AP STA, the AP shall | ||
1311 | * attempt to transmit at least one MSDU or MMPDU, but no | ||
1312 | * more than the value specified in the Max SP Length field | ||
1313 | * in the QoS Capability element from delivery-enabled ACs, | ||
1314 | * that are destined for the non-AP STA. | ||
1315 | * | ||
1316 | * Since we have no other MSDU/MMPDU, transmit a QoS null frame. | ||
1237 | */ | 1317 | */ |
1238 | if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) | ||
1239 | printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " | ||
1240 | "though there are no buffered frames for it\n", | ||
1241 | sdata->name, sta->sta.addr); | ||
1242 | #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ | ||
1243 | 1318 | ||
1319 | /* This will evaluate to 1, 3, 5 or 7. */ | ||
1320 | tid = 7 - ((ffs(~ignored_acs) - 1) << 1); | ||
1321 | |||
1322 | ieee80211_send_null_response(sdata, sta, tid, | ||
1323 | reason == IEEE80211_FRAME_RELEASE_UAPSD); | ||
1244 | return; | 1324 | return; |
1245 | } | 1325 | } |
1246 | 1326 | ||
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a0676d39fe8f..5bf91c43c88c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1520,8 +1520,7 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, | |||
1520 | return 0; | 1520 | return 0; |
1521 | } | 1521 | } |
1522 | 1522 | ||
1523 | static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, | 1523 | void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) |
1524 | struct sk_buff *skb) | ||
1525 | { | 1524 | { |
1526 | struct ieee80211_local *local = sdata->local; | 1525 | struct ieee80211_local *local = sdata->local; |
1527 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 1526 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |