aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/agg-rx.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/agg-rx.c')
-rw-r--r--net/mac80211/agg-rx.c123
1 files changed, 70 insertions, 53 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 6bb9a9a9496..965b272499f 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -6,39 +6,70 @@
6 * Copyright 2005-2006, Devicescape Software, Inc. 6 * Copyright 2005-2006, Devicescape Software, Inc.
7 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 7 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
9 * Copyright 2007-2008, Intel Corporation 9 * Copyright 2007-2010, Intel Corporation
10 * 10 *
11 * This program is free software; you can redistribute it and/or modify 11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as 12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation. 13 * published by the Free Software Foundation.
14 */ 14 */
15 15
16/**
17 * DOC: RX A-MPDU aggregation
18 *
19 * Aggregation on the RX side requires only implementing the
20 * @ampdu_action callback that is invoked to start/stop any
21 * block-ack sessions for RX aggregation.
22 *
23 * When RX aggregation is started by the peer, the driver is
24 * notified via @ampdu_action function, with the
25 * %IEEE80211_AMPDU_RX_START action, and may reject the request
26 * in which case a negative response is sent to the peer, if it
27 * accepts it a positive response is sent.
28 *
29 * While the session is active, the device/driver are required
30 * to de-aggregate frames and pass them up one by one to mac80211,
31 * which will handle the reorder buffer.
32 *
33 * When the aggregation session is stopped again by the peer or
34 * ourselves, the driver's @ampdu_action function will be called
35 * with the action %IEEE80211_AMPDU_RX_STOP. In this case, the
36 * call must not fail.
37 */
38
16#include <linux/ieee80211.h> 39#include <linux/ieee80211.h>
17#include <linux/slab.h> 40#include <linux/slab.h>
18#include <net/mac80211.h> 41#include <net/mac80211.h>
19#include "ieee80211_i.h" 42#include "ieee80211_i.h"
20#include "driver-ops.h" 43#include "driver-ops.h"
21 44
22static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, 45static void ieee80211_free_tid_rx(struct rcu_head *h)
23 u16 initiator, u16 reason,
24 bool from_timer)
25{ 46{
26 struct ieee80211_local *local = sta->local; 47 struct tid_ampdu_rx *tid_rx =
27 struct tid_ampdu_rx *tid_rx; 48 container_of(h, struct tid_ampdu_rx, rcu_head);
28 int i; 49 int i;
29 50
30 spin_lock_bh(&sta->lock); 51 for (i = 0; i < tid_rx->buf_size; i++)
52 dev_kfree_skb(tid_rx->reorder_buf[i]);
53 kfree(tid_rx->reorder_buf);
54 kfree(tid_rx->reorder_time);
55 kfree(tid_rx);
56}
31 57
32 /* check if TID is in operational state */ 58void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
33 if (!sta->ampdu_mlme.tid_active_rx[tid]) { 59 u16 initiator, u16 reason)
34 spin_unlock_bh(&sta->lock); 60{
35 return; 61 struct ieee80211_local *local = sta->local;
36 } 62 struct tid_ampdu_rx *tid_rx;
37 63
38 sta->ampdu_mlme.tid_active_rx[tid] = false; 64 lockdep_assert_held(&sta->ampdu_mlme.mtx);
39 65
40 tid_rx = sta->ampdu_mlme.tid_rx[tid]; 66 tid_rx = sta->ampdu_mlme.tid_rx[tid];
41 67
68 if (!tid_rx)
69 return;
70
71 rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], NULL);
72
42#ifdef CONFIG_MAC80211_HT_DEBUG 73#ifdef CONFIG_MAC80211_HT_DEBUG
43 printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", 74 printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",
44 sta->sta.addr, tid); 75 sta->sta.addr, tid);
@@ -54,32 +85,17 @@ static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
54 ieee80211_send_delba(sta->sdata, sta->sta.addr, 85 ieee80211_send_delba(sta->sdata, sta->sta.addr,
55 tid, 0, reason); 86 tid, 0, reason);
56 87
57 /* free the reordering buffer */ 88 del_timer_sync(&tid_rx->session_timer);
58 for (i = 0; i < tid_rx->buf_size; i++) {
59 if (tid_rx->reorder_buf[i]) {
60 /* release the reordered frames */
61 dev_kfree_skb(tid_rx->reorder_buf[i]);
62 tid_rx->stored_mpdu_num--;
63 tid_rx->reorder_buf[i] = NULL;
64 }
65 }
66
67 /* free resources */
68 kfree(tid_rx->reorder_buf);
69 kfree(tid_rx->reorder_time);
70 sta->ampdu_mlme.tid_rx[tid] = NULL;
71
72 spin_unlock_bh(&sta->lock);
73 89
74 if (!from_timer) 90 call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
75 del_timer_sync(&tid_rx->session_timer);
76 kfree(tid_rx);
77} 91}
78 92
79void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, 93void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
80 u16 initiator, u16 reason) 94 u16 initiator, u16 reason)
81{ 95{
82 ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, false); 96 mutex_lock(&sta->ampdu_mlme.mtx);
97 ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason);
98 mutex_unlock(&sta->ampdu_mlme.mtx);
83} 99}
84 100
85/* 101/*
@@ -100,8 +116,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
100#ifdef CONFIG_MAC80211_HT_DEBUG 116#ifdef CONFIG_MAC80211_HT_DEBUG
101 printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); 117 printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
102#endif 118#endif
103 ___ieee80211_stop_rx_ba_session(sta, *ptid, WLAN_BACK_RECIPIENT, 119 set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired);
104 WLAN_REASON_QSTA_TIMEOUT, true); 120 ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
105} 121}
106 122
107static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid, 123static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid,
@@ -212,9 +228,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
212 228
213 229
214 /* examine state machine */ 230 /* examine state machine */
215 spin_lock_bh(&sta->lock); 231 mutex_lock(&sta->ampdu_mlme.mtx);
216 232
217 if (sta->ampdu_mlme.tid_active_rx[tid]) { 233 if (sta->ampdu_mlme.tid_rx[tid]) {
218#ifdef CONFIG_MAC80211_HT_DEBUG 234#ifdef CONFIG_MAC80211_HT_DEBUG
219 if (net_ratelimit()) 235 if (net_ratelimit())
220 printk(KERN_DEBUG "unexpected AddBA Req from " 236 printk(KERN_DEBUG "unexpected AddBA Req from "
@@ -225,9 +241,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
225 } 241 }
226 242
227 /* prepare A-MPDU MLME for Rx aggregation */ 243 /* prepare A-MPDU MLME for Rx aggregation */
228 sta->ampdu_mlme.tid_rx[tid] = 244 tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC);
229 kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC); 245 if (!tid_agg_rx) {
230 if (!sta->ampdu_mlme.tid_rx[tid]) {
231#ifdef CONFIG_MAC80211_HT_DEBUG 246#ifdef CONFIG_MAC80211_HT_DEBUG
232 if (net_ratelimit()) 247 if (net_ratelimit())
233 printk(KERN_ERR "allocate rx mlme to tid %d failed\n", 248 printk(KERN_ERR "allocate rx mlme to tid %d failed\n",
@@ -235,14 +250,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
235#endif 250#endif
236 goto end; 251 goto end;
237 } 252 }
238 /* rx timer */
239 sta->ampdu_mlme.tid_rx[tid]->session_timer.function =
240 sta_rx_agg_session_timer_expired;
241 sta->ampdu_mlme.tid_rx[tid]->session_timer.data =
242 (unsigned long)&sta->timer_to_tid[tid];
243 init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
244 253
245 tid_agg_rx = sta->ampdu_mlme.tid_rx[tid]; 254 /* rx timer */
255 tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired;
256 tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid];
257 init_timer(&tid_agg_rx->session_timer);
246 258
247 /* prepare reordering buffer */ 259 /* prepare reordering buffer */
248 tid_agg_rx->reorder_buf = 260 tid_agg_rx->reorder_buf =
@@ -257,8 +269,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
257#endif 269#endif
258 kfree(tid_agg_rx->reorder_buf); 270 kfree(tid_agg_rx->reorder_buf);
259 kfree(tid_agg_rx->reorder_time); 271 kfree(tid_agg_rx->reorder_time);
260 kfree(sta->ampdu_mlme.tid_rx[tid]); 272 kfree(tid_agg_rx);
261 sta->ampdu_mlme.tid_rx[tid] = NULL;
262 goto end; 273 goto end;
263 } 274 }
264 275
@@ -270,13 +281,12 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
270 281
271 if (ret) { 282 if (ret) {
272 kfree(tid_agg_rx->reorder_buf); 283 kfree(tid_agg_rx->reorder_buf);
284 kfree(tid_agg_rx->reorder_time);
273 kfree(tid_agg_rx); 285 kfree(tid_agg_rx);
274 sta->ampdu_mlme.tid_rx[tid] = NULL;
275 goto end; 286 goto end;
276 } 287 }
277 288
278 /* change state and send addba resp */ 289 /* update data */
279 sta->ampdu_mlme.tid_active_rx[tid] = true;
280 tid_agg_rx->dialog_token = dialog_token; 290 tid_agg_rx->dialog_token = dialog_token;
281 tid_agg_rx->ssn = start_seq_num; 291 tid_agg_rx->ssn = start_seq_num;
282 tid_agg_rx->head_seq_num = start_seq_num; 292 tid_agg_rx->head_seq_num = start_seq_num;
@@ -284,8 +294,15 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
284 tid_agg_rx->timeout = timeout; 294 tid_agg_rx->timeout = timeout;
285 tid_agg_rx->stored_mpdu_num = 0; 295 tid_agg_rx->stored_mpdu_num = 0;
286 status = WLAN_STATUS_SUCCESS; 296 status = WLAN_STATUS_SUCCESS;
297
298 /* activate it for RX */
299 rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
300
301 if (timeout)
302 mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout));
303
287end: 304end:
288 spin_unlock_bh(&sta->lock); 305 mutex_unlock(&sta->ampdu_mlme.mtx);
289 306
290end_no_lock: 307end_no_lock:
291 ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, 308 ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,