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/sta_info.c | |
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/sta_info.c')
-rw-r--r-- | net/mac80211/sta_info.c | 100 |
1 files changed, 90 insertions, 10 deletions
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 | ||