aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/mwifiex/11n_aggr.c
diff options
context:
space:
mode:
authorBing Zhao <bzhao@marvell.com>2011-03-21 21:00:50 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-03-30 14:15:17 -0400
commit5e6e3a92b9a4c9416b17f468fa5c7fa2233b8b4e (patch)
treede22c4c414412501e62894de65040bf30db28c64 /drivers/net/wireless/mwifiex/11n_aggr.c
parent903946e6e21ef4dd678acafb8881cabde9182caf (diff)
wireless: mwifiex: initial commit for Marvell mwifiex driver
This driver adds WiFi support for Marvell 802.11n based chipsets with SDIO interface. Currently only SD8787 is supported. More chipsets will be supported later. drivers/net/wireless/mwifiex/ Signed-off-by: Nishant Sarmukadam <nishants@marvell.com> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com> Signed-off-by: Kiran Divekar <dkiran@marvell.com> Signed-off-by: Bing Zhao <bzhao@marvell.com> Signed-off-by: Yogesh Ashok Powar <yogeshp@marvell.com> Signed-off-by: Marc Yang <yangyang@marvell.com> Signed-off-by: Ramesh Radhakrishnan <rramesh@marvell.com> Signed-off-by: Frank Huang <frankh@marvell.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/mwifiex/11n_aggr.c')
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.c423
1 files changed, 423 insertions, 0 deletions
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c
new file mode 100644
index 000000000000..c2abced66957
--- /dev/null
+++ b/drivers/net/wireless/mwifiex/11n_aggr.c
@@ -0,0 +1,423 @@
1/*
2 * Marvell Wireless LAN device driver: 802.11n Aggregation
3 *
4 * Copyright (C) 2011, Marvell International Ltd.
5 *
6 * This software file (the "File") is distributed by Marvell International
7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
8 * (the "License"). You may use, redistribute and/or modify this File in
9 * accordance with the terms and conditions of the License, a copy of which
10 * is available by writing to the Free Software Foundation, Inc.,
11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
12 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
13 *
14 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
16 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
17 * this warranty disclaimer.
18 */
19
20#include "decl.h"
21#include "ioctl.h"
22#include "util.h"
23#include "fw.h"
24#include "main.h"
25#include "wmm.h"
26#include "11n.h"
27#include "11n_aggr.h"
28
29/*
30 * Creates an AMSDU subframe for aggregation into one AMSDU packet.
31 *
32 * The resultant AMSDU subframe format is -
33 *
34 * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+
35 * | DA | SA | Length | SNAP header | MSDU |
36 * | data[0..5] | data[6..11] | | | data[14..] |
37 * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+
38 * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes-->
39 *
40 * This function also computes the amount of padding required to make the
41 * buffer length multiple of 4 bytes.
42 *
43 * Data => |DA|SA|SNAP-TYPE|........ .|
44 * MSDU => |DA|SA|Length|SNAP|...... ..|
45 */
46static int
47mwifiex_11n_form_amsdu_pkt(struct mwifiex_adapter *adapter,
48 struct sk_buff *skb_aggr,
49 struct sk_buff *skb_src, int *pad)
50
51{
52 int dt_offset;
53 struct rfc_1042_hdr snap = {
54 0xaa, /* LLC DSAP */
55 0xaa, /* LLC SSAP */
56 0x03, /* LLC CTRL */
57 {0x00, 0x00, 0x00}, /* SNAP OUI */
58 0x0000 /* SNAP type */
59 /*
60 * This field will be overwritten
61 * later with ethertype
62 */
63 };
64 struct tx_packet_hdr *tx_header = NULL;
65
66 skb_put(skb_aggr, sizeof(*tx_header));
67
68 tx_header = (struct tx_packet_hdr *) skb_aggr->data;
69
70 /* Copy DA and SA */
71 dt_offset = 2 * ETH_ALEN;
72 memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
73
74 /* Copy SNAP header */
75 snap.snap_type = *(u16 *) ((u8 *)skb_src->data + dt_offset);
76 dt_offset += sizeof(u16);
77
78 memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
79
80 skb_pull(skb_src, dt_offset);
81
82 /* Update Length field */
83 tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN);
84
85 /* Add payload */
86 skb_put(skb_aggr, skb_src->len);
87 memcpy(skb_aggr->data + sizeof(*tx_header), skb_src->data,
88 skb_src->len);
89 *pad = (((skb_src->len + LLC_SNAP_LEN) & 3)) ? (4 - (((skb_src->len +
90 LLC_SNAP_LEN)) & 3)) : 0;
91 skb_put(skb_aggr, *pad);
92
93 return skb_aggr->len + *pad;
94}
95
96/*
97 * Adds TxPD to AMSDU header.
98 *
99 * Each AMSDU packet will contain one TxPD at the beginning,
100 * followed by multiple AMSDU subframes.
101 */
102static void
103mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
104 struct sk_buff *skb)
105{
106 struct txpd *local_tx_pd;
107
108 skb_push(skb, sizeof(*local_tx_pd));
109
110 local_tx_pd = (struct txpd *) skb->data;
111 memset(local_tx_pd, 0, sizeof(struct txpd));
112
113 /* Original priority has been overwritten */
114 local_tx_pd->priority = (u8) skb->priority;
115 local_tx_pd->pkt_delay_2ms =
116 mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
117 local_tx_pd->bss_num = priv->bss_num;
118 local_tx_pd->bss_type = priv->bss_type;
119 /* Always zero as the data is followed by struct txpd */
120 local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
121 local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU);
122 local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
123 sizeof(*local_tx_pd));
124
125 if (local_tx_pd->tx_control == 0)
126 /* TxCtrl set by user or default */
127 local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
128
129 if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
130 (priv->adapter->pps_uapsd_mode)) {
131 if (true == mwifiex_check_last_packet_indication(priv)) {
132 priv->adapter->tx_lock_flag = true;
133 local_tx_pd->flags =
134 MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET;
135 }
136 }
137}
138
139/*
140 * Counts the number of subframes in an aggregate packet.
141 *
142 * This function parses an aggregate packet buffer, looking for
143 * subframes and counting the number of such subframe found. The
144 * function automatically skips the DA/SA fields at the beginning
145 * of each subframe and padding at the end.
146 */
147static int
148mwifiex_11n_get_num_aggr_pkts(u8 *data, int total_pkt_len)
149{
150 int pkt_count = 0, pkt_len, pad;
151
152 while (total_pkt_len > 0) {
153 /* Length will be in network format, change it to host */
154 pkt_len = ntohs((*(__be16 *)(data + 2 * ETH_ALEN)));
155 pad = (((pkt_len + sizeof(struct ethhdr)) & 3)) ?
156 (4 - ((pkt_len + sizeof(struct ethhdr)) & 3)) : 0;
157 data += pkt_len + pad + sizeof(struct ethhdr);
158 total_pkt_len -= pkt_len + pad + sizeof(struct ethhdr);
159 ++pkt_count;
160 }
161
162 return pkt_count;
163}
164
165/*
166 * De-aggregate received packets.
167 *
168 * This function parses the received aggregate buffer, extracts each subframe,
169 * strips off the SNAP header from them and sends the data portion for further
170 * processing.
171 *
172 * Each subframe body is copied onto a separate buffer, which are freed by
173 * upper layer after processing. The function also performs sanity tests on
174 * the received buffer.
175 */
176int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv,
177 struct sk_buff *skb)
178{
179 u16 pkt_len;
180 int total_pkt_len;
181 u8 *data;
182 int pad;
183 struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
184 struct rxpd *local_rx_pd = (struct rxpd *) skb->data;
185 struct sk_buff *skb_daggr;
186 struct mwifiex_rxinfo *rx_info_daggr = NULL;
187 int ret = -1;
188 struct rx_packet_hdr *rx_pkt_hdr;
189 struct mwifiex_adapter *adapter = priv->adapter;
190 u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
191
192 data = (u8 *) (local_rx_pd + local_rx_pd->rx_pkt_offset);
193 total_pkt_len = local_rx_pd->rx_pkt_length;
194
195 /* Sanity test */
196 if (total_pkt_len > MWIFIEX_RX_DATA_BUF_SIZE) {
197 dev_err(adapter->dev, "total pkt len greater than buffer"
198 " size %d\n", total_pkt_len);
199 return -1;
200 }
201
202 rx_info->use_count = mwifiex_11n_get_num_aggr_pkts(data, total_pkt_len);
203
204 while (total_pkt_len > 0) {
205 rx_pkt_hdr = (struct rx_packet_hdr *) data;
206 /* Length will be in network format, change it to host */
207 pkt_len = ntohs((*(__be16 *) (data + 2 * ETH_ALEN)));
208 if (pkt_len > total_pkt_len) {
209 dev_err(adapter->dev, "pkt_len %d > total_pkt_len %d\n",
210 total_pkt_len, pkt_len);
211 break;
212 }
213
214 pad = (((pkt_len + sizeof(struct ethhdr)) & 3)) ?
215 (4 - ((pkt_len + sizeof(struct ethhdr)) & 3)) : 0;
216
217 total_pkt_len -= pkt_len + pad + sizeof(struct ethhdr);
218
219 if (memcmp(&rx_pkt_hdr->rfc1042_hdr,
220 rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) {
221 memmove(data + LLC_SNAP_LEN, data, 2 * ETH_ALEN);
222 data += LLC_SNAP_LEN;
223 pkt_len += sizeof(struct ethhdr) - LLC_SNAP_LEN;
224 } else {
225 *(u16 *) (data + 2 * ETH_ALEN) = (u16) 0;
226 pkt_len += sizeof(struct ethhdr);
227 }
228
229 skb_daggr = dev_alloc_skb(pkt_len);
230 if (!skb_daggr) {
231 dev_err(adapter->dev, "%s: failed to alloc skb_daggr\n",
232 __func__);
233 return -1;
234 }
235 rx_info_daggr = MWIFIEX_SKB_RXCB(skb_daggr);
236
237 rx_info_daggr->bss_index = rx_info->bss_index;
238 skb_daggr->tstamp = skb->tstamp;
239 rx_info_daggr->parent = skb;
240 skb_daggr->priority = skb->priority;
241 skb_put(skb_daggr, pkt_len);
242 memcpy(skb_daggr->data, data, pkt_len);
243
244 ret = mwifiex_recv_packet(adapter, skb_daggr);
245
246 switch (ret) {
247 case -EINPROGRESS:
248 break;
249 case -1:
250 dev_err(adapter->dev, "deaggr: host_to_card failed\n");
251 case 0:
252 mwifiex_recv_packet_complete(adapter, skb_daggr, ret);
253 break;
254 default:
255 break;
256 }
257
258 data += pkt_len + pad;
259 }
260
261 return ret;
262}
263
264/*
265 * Create aggregated packet.
266 *
267 * This function creates an aggregated MSDU packet, by combining buffers
268 * from the RA list. Each individual buffer is encapsulated as an AMSDU
269 * subframe and all such subframes are concatenated together to form the
270 * AMSDU packet.
271 *
272 * A TxPD is also added to the front of the resultant AMSDU packets for
273 * transmission. The resultant packets format is -
274 *
275 * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+
276 * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame|
277 * | | 1 | 2 | .. | n |
278 * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+
279 */
280int
281mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
282 struct mwifiex_ra_list_tbl *pra_list, int headroom,
283 int ptrindex, unsigned long ra_list_flags)
284 __releases(&priv->wmm.ra_list_spinlock)
285{
286 struct mwifiex_adapter *adapter = priv->adapter;
287 struct sk_buff *skb_aggr, *skb_src;
288 struct mwifiex_txinfo *tx_info_aggr, *tx_info_src;
289 int pad = 0;
290 int ret = 0;
291 struct mwifiex_tx_param tx_param;
292 struct txpd *ptx_pd = NULL;
293
294 if (skb_queue_empty(&pra_list->skb_head)) {
295 spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
296 ra_list_flags);
297 return 0;
298 }
299 skb_src = skb_peek(&pra_list->skb_head);
300 tx_info_src = MWIFIEX_SKB_TXCB(skb_src);
301 skb_aggr = dev_alloc_skb(adapter->tx_buf_size);
302 if (!skb_aggr) {
303 dev_err(adapter->dev, "%s: alloc skb_aggr\n", __func__);
304 spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
305 ra_list_flags);
306 return -1;
307 }
308 skb_reserve(skb_aggr, headroom + sizeof(struct txpd));
309 tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr);
310
311 tx_info_aggr->bss_index = tx_info_src->bss_index;
312 skb_aggr->priority = skb_src->priority;
313
314 while (skb_src && ((skb_headroom(skb_aggr) + skb_src->len
315 + LLC_SNAP_LEN)
316 <= adapter->tx_buf_size)) {
317
318 if (!skb_queue_empty(&pra_list->skb_head))
319 skb_src = skb_dequeue(&pra_list->skb_head);
320 else
321 skb_src = NULL;
322
323 pra_list->total_pkts_size -= skb_src->len;
324
325 spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
326 ra_list_flags);
327 mwifiex_11n_form_amsdu_pkt(adapter, skb_aggr, skb_src, &pad);
328
329 mwifiex_write_data_complete(adapter, skb_src, 0);
330
331 spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
332
333 if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
334 spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
335 ra_list_flags);
336 return -1;
337 }
338
339 if (!skb_queue_empty(&pra_list->skb_head))
340 skb_src = skb_peek(&pra_list->skb_head);
341 else
342 skb_src = NULL;
343 }
344
345 spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags);
346
347 /* Last AMSDU packet does not need padding */
348 skb_trim(skb_aggr, skb_aggr->len - pad);
349
350 /* Form AMSDU */
351 mwifiex_11n_form_amsdu_txpd(priv, skb_aggr);
352 if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
353 ptx_pd = (struct txpd *)skb_aggr->data;
354
355 skb_push(skb_aggr, headroom);
356
357 tx_param.next_pkt_len = ((pra_list->total_pkts_size) ?
358 (((pra_list->total_pkts_size) >
359 adapter->tx_buf_size) ? adapter->
360 tx_buf_size : pra_list->total_pkts_size +
361 LLC_SNAP_LEN + sizeof(struct txpd)) : 0);
362 ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
363 skb_aggr->data,
364 skb_aggr->len, &tx_param);
365 switch (ret) {
366 case -EBUSY:
367 spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
368 if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
369 spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
370 ra_list_flags);
371 mwifiex_write_data_complete(adapter, skb_aggr, -1);
372 return -1;
373 }
374 if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
375 (adapter->pps_uapsd_mode) &&
376 (adapter->tx_lock_flag)) {
377 priv->adapter->tx_lock_flag = false;
378 ptx_pd->flags = 0;
379 }
380
381 skb_queue_tail(&pra_list->skb_head, skb_aggr);
382
383 pra_list->total_pkts_size += skb_aggr->len;
384
385 tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
386 spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
387 ra_list_flags);
388 dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
389 break;
390 case -1:
391 adapter->data_sent = false;
392 dev_err(adapter->dev, "%s: host_to_card failed: %#x\n",
393 __func__, ret);
394 adapter->dbg.num_tx_host_to_card_failure++;
395 mwifiex_write_data_complete(adapter, skb_aggr, ret);
396 return 0;
397 case -EINPROGRESS:
398 adapter->data_sent = false;
399 break;
400 case 0:
401 mwifiex_write_data_complete(adapter, skb_aggr, ret);
402 break;
403 default:
404 break;
405 }
406 if (ret != -EBUSY) {
407 spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
408 if (mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
409 priv->wmm.packets_out[ptrindex]++;
410 priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list;
411 }
412 /* Now bss_prio_cur pointer points to next node */
413 adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur =
414 list_first_entry(
415 &adapter->bss_prio_tbl[priv->bss_priority]
416 .bss_prio_cur->list,
417 struct mwifiex_bss_prio_node, list);
418 spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
419 ra_list_flags);
420 }
421
422 return 0;
423}