aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/ht.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/ht.c')
-rw-r--r--net/mac80211/ht.c328
1 files changed, 328 insertions, 0 deletions
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
new file mode 100644
index 000000000000..5ccf1bc17466
--- /dev/null
+++ b/net/mac80211/ht.c
@@ -0,0 +1,328 @@
1/*
2 * HT handling
3 *
4 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
5 * Copyright 2004, Instant802 Networks, Inc.
6 * Copyright 2005, Devicescape Software, Inc.
7 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
9 * Copyright 2007-2008, Intel Corporation
10 *
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
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/ieee80211.h>
17#include <net/wireless.h>
18#include <net/mac80211.h>
19#include "ieee80211_i.h"
20#include "sta_info.h"
21
22int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
23 struct ieee80211_ht_info *ht_info)
24{
25
26 if (ht_info == NULL)
27 return -EINVAL;
28
29 memset(ht_info, 0, sizeof(*ht_info));
30
31 if (ht_cap_ie) {
32 u8 ampdu_info = ht_cap_ie->ampdu_params_info;
33
34 ht_info->ht_supported = 1;
35 ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
36 ht_info->ampdu_factor =
37 ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
38 ht_info->ampdu_density =
39 (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
40 memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
41 } else
42 ht_info->ht_supported = 0;
43
44 return 0;
45}
46
47int ieee80211_ht_addt_info_ie_to_ht_bss_info(
48 struct ieee80211_ht_addt_info *ht_add_info_ie,
49 struct ieee80211_ht_bss_info *bss_info)
50{
51 if (bss_info == NULL)
52 return -EINVAL;
53
54 memset(bss_info, 0, sizeof(*bss_info));
55
56 if (ht_add_info_ie) {
57 u16 op_mode;
58 op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
59
60 bss_info->primary_channel = ht_add_info_ie->control_chan;
61 bss_info->bss_cap = ht_add_info_ie->ht_param;
62 bss_info->bss_op_mode = (u8)(op_mode & 0xff);
63 }
64
65 return 0;
66}
67
68void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, const u8 *da,
69 u16 tid, u8 dialog_token, u16 start_seq_num,
70 u16 agg_size, u16 timeout)
71{
72 struct ieee80211_local *local = sdata->local;
73 struct ieee80211_if_sta *ifsta = &sdata->u.sta;
74 struct sk_buff *skb;
75 struct ieee80211_mgmt *mgmt;
76 u16 capab;
77
78 skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
79
80 if (!skb) {
81 printk(KERN_ERR "%s: failed to allocate buffer "
82 "for addba request frame\n", sdata->dev->name);
83 return;
84 }
85 skb_reserve(skb, local->hw.extra_tx_headroom);
86 mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
87 memset(mgmt, 0, 24);
88 memcpy(mgmt->da, da, ETH_ALEN);
89 memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
90 if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
91 memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
92 else
93 memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
94
95 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
96 IEEE80211_STYPE_ACTION);
97
98 skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req));
99
100 mgmt->u.action.category = WLAN_CATEGORY_BACK;
101 mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
102
103 mgmt->u.action.u.addba_req.dialog_token = dialog_token;
104 capab = (u16)(1 << 1); /* bit 1 aggregation policy */
105 capab |= (u16)(tid << 2); /* bit 5:2 TID number */
106 capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */
107
108 mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
109
110 mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout);
111 mgmt->u.action.u.addba_req.start_seq_num =
112 cpu_to_le16(start_seq_num << 4);
113
114 ieee80211_sta_tx(sdata, skb, 0);
115}
116
117void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid,
118 u16 initiator, u16 reason_code)
119{
120 struct ieee80211_local *local = sdata->local;
121 struct ieee80211_if_sta *ifsta = &sdata->u.sta;
122 struct sk_buff *skb;
123 struct ieee80211_mgmt *mgmt;
124 u16 params;
125
126 skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
127
128 if (!skb) {
129 printk(KERN_ERR "%s: failed to allocate buffer "
130 "for delba frame\n", sdata->dev->name);
131 return;
132 }
133
134 skb_reserve(skb, local->hw.extra_tx_headroom);
135 mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
136 memset(mgmt, 0, 24);
137 memcpy(mgmt->da, da, ETH_ALEN);
138 memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
139 if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
140 memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
141 else
142 memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
143 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
144 IEEE80211_STYPE_ACTION);
145
146 skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));
147
148 mgmt->u.action.category = WLAN_CATEGORY_BACK;
149 mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
150 params = (u16)(initiator << 11); /* bit 11 initiator */
151 params |= (u16)(tid << 12); /* bit 15:12 TID number */
152
153 mgmt->u.action.u.delba.params = cpu_to_le16(params);
154 mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
155
156 ieee80211_sta_tx(sdata, skb, 0);
157}
158
159void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn)
160{
161 struct ieee80211_local *local = sdata->local;
162 struct sk_buff *skb;
163 struct ieee80211_bar *bar;
164 u16 bar_control = 0;
165
166 skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom);
167 if (!skb) {
168 printk(KERN_ERR "%s: failed to allocate buffer for "
169 "bar frame\n", sdata->dev->name);
170 return;
171 }
172 skb_reserve(skb, local->hw.extra_tx_headroom);
173 bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar));
174 memset(bar, 0, sizeof(*bar));
175 bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
176 IEEE80211_STYPE_BACK_REQ);
177 memcpy(bar->ra, ra, ETH_ALEN);
178 memcpy(bar->ta, sdata->dev->dev_addr, ETH_ALEN);
179 bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL;
180 bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA;
181 bar_control |= (u16)(tid << 12);
182 bar->control = cpu_to_le16(bar_control);
183 bar->start_seq_num = cpu_to_le16(ssn);
184
185 ieee80211_sta_tx(sdata, skb, 0);
186}
187
188void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid,
189 u16 initiator, u16 reason)
190{
191 struct ieee80211_local *local = sdata->local;
192 struct ieee80211_hw *hw = &local->hw;
193 struct sta_info *sta;
194 int ret, i;
195 DECLARE_MAC_BUF(mac);
196
197 rcu_read_lock();
198
199 sta = sta_info_get(local, ra);
200 if (!sta) {
201 rcu_read_unlock();
202 return;
203 }
204
205 /* check if TID is in operational state */
206 spin_lock_bh(&sta->lock);
207 if (sta->ampdu_mlme.tid_state_rx[tid]
208 != HT_AGG_STATE_OPERATIONAL) {
209 spin_unlock_bh(&sta->lock);
210 rcu_read_unlock();
211 return;
212 }
213 sta->ampdu_mlme.tid_state_rx[tid] =
214 HT_AGG_STATE_REQ_STOP_BA_MSK |
215 (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
216 spin_unlock_bh(&sta->lock);
217
218 /* stop HW Rx aggregation. ampdu_action existence
219 * already verified in session init so we add the BUG_ON */
220 BUG_ON(!local->ops->ampdu_action);
221
222#ifdef CONFIG_MAC80211_HT_DEBUG
223 printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
224 print_mac(mac, ra), tid);
225#endif /* CONFIG_MAC80211_HT_DEBUG */
226
227 ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
228 ra, tid, NULL);
229 if (ret)
230 printk(KERN_DEBUG "HW problem - can not stop rx "
231 "aggregation for tid %d\n", tid);
232
233 /* shutdown timer has not expired */
234 if (initiator != WLAN_BACK_TIMER)
235 del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
236
237 /* check if this is a self generated aggregation halt */
238 if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER)
239 ieee80211_send_delba(sdata, ra, tid, 0, reason);
240
241 /* free the reordering buffer */
242 for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) {
243 if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) {
244 /* release the reordered frames */
245 dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]);
246 sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--;
247 sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL;
248 }
249 }
250 /* free resources */
251 kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf);
252 kfree(sta->ampdu_mlme.tid_rx[tid]);
253 sta->ampdu_mlme.tid_rx[tid] = NULL;
254 sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE;
255
256 rcu_read_unlock();
257}
258
259
260/*
261 * After sending add Block Ack request we activated a timer until
262 * add Block Ack response will arrive from the recipient.
263 * If this timer expires sta_addba_resp_timer_expired will be executed.
264 */
265void sta_addba_resp_timer_expired(unsigned long data)
266{
267 /* not an elegant detour, but there is no choice as the timer passes
268 * only one argument, and both sta_info and TID are needed, so init
269 * flow in sta_info_create gives the TID as data, while the timer_to_id
270 * array gives the sta through container_of */
271 u16 tid = *(u8 *)data;
272 struct sta_info *temp_sta = container_of((void *)data,
273 struct sta_info, timer_to_tid[tid]);
274
275 struct ieee80211_local *local = temp_sta->local;
276 struct ieee80211_hw *hw = &local->hw;
277 struct sta_info *sta;
278 u8 *state;
279
280 rcu_read_lock();
281
282 sta = sta_info_get(local, temp_sta->addr);
283 if (!sta) {
284 rcu_read_unlock();
285 return;
286 }
287
288 state = &sta->ampdu_mlme.tid_state_tx[tid];
289 /* check if the TID waits for addBA response */
290 spin_lock_bh(&sta->lock);
291 if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
292 spin_unlock_bh(&sta->lock);
293 *state = HT_AGG_STATE_IDLE;
294#ifdef CONFIG_MAC80211_HT_DEBUG
295 printk(KERN_DEBUG "timer expired on tid %d but we are not "
296 "expecting addBA response there", tid);
297#endif
298 goto timer_expired_exit;
299 }
300
301#ifdef CONFIG_MAC80211_HT_DEBUG
302 printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid);
303#endif
304
305 /* go through the state check in stop_BA_session */
306 *state = HT_AGG_STATE_OPERATIONAL;
307 spin_unlock_bh(&sta->lock);
308 ieee80211_stop_tx_ba_session(hw, temp_sta->addr, tid,
309 WLAN_BACK_INITIATOR);
310
311timer_expired_exit:
312 rcu_read_unlock();
313}
314
315void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr)
316{
317 struct ieee80211_local *local = sdata->local;
318 int i;
319
320 for (i = 0; i < STA_TID_NUM; i++) {
321 ieee80211_stop_tx_ba_session(&local->hw, addr, i,
322 WLAN_BACK_INITIATOR);
323 ieee80211_sta_stop_rx_ba_session(sdata, addr, i,
324 WLAN_BACK_RECIPIENT,
325 WLAN_REASON_QSTA_LEAVE_QBSS);
326 }
327}
328