162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci  Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci  Contact Information:
862306a36Sopenharmony_ci  Intel Linux Wireless <ilw@linux.intel.com>
962306a36Sopenharmony_ci  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci******************************************************************************/
1262306a36Sopenharmony_ci#include <linux/compiler.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/if_arp.h>
1562306a36Sopenharmony_ci#include <linux/in6.h>
1662306a36Sopenharmony_ci#include <linux/in.h>
1762306a36Sopenharmony_ci#include <linux/ip.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/netdevice.h>
2162306a36Sopenharmony_ci#include <linux/proc_fs.h>
2262306a36Sopenharmony_ci#include <linux/skbuff.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/tcp.h>
2562306a36Sopenharmony_ci#include <linux/types.h>
2662306a36Sopenharmony_ci#include <linux/wireless.h>
2762306a36Sopenharmony_ci#include <linux/etherdevice.h>
2862306a36Sopenharmony_ci#include <linux/uaccess.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "libipw.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci802.11 Data Frame
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci      ,-------------------------------------------------------------------.
3762306a36Sopenharmony_ciBytes |  2   |  2   |    6    |    6    |    6    |  2   | 0..2312 |   4  |
3862306a36Sopenharmony_ci      |------|------|---------|---------|---------|------|---------|------|
3962306a36Sopenharmony_ciDesc. | ctrl | dura |  DA/RA  |   TA    |    SA   | Sequ |  Frame  |  fcs |
4062306a36Sopenharmony_ci      |      | tion | (BSSID) |         |         | ence |  data   |      |
4162306a36Sopenharmony_ci      `--------------------------------------------------|         |------'
4262306a36Sopenharmony_ciTotal: 28 non-data bytes                                 `----.----'
4362306a36Sopenharmony_ci							      |
4462306a36Sopenharmony_ci       .- 'Frame data' expands, if WEP enabled, to <----------'
4562306a36Sopenharmony_ci       |
4662306a36Sopenharmony_ci       V
4762306a36Sopenharmony_ci      ,-----------------------.
4862306a36Sopenharmony_ciBytes |  4  |   0-2296  |  4  |
4962306a36Sopenharmony_ci      |-----|-----------|-----|
5062306a36Sopenharmony_ciDesc. | IV  | Encrypted | ICV |
5162306a36Sopenharmony_ci      |     | Packet    |     |
5262306a36Sopenharmony_ci      `-----|           |-----'
5362306a36Sopenharmony_ci	    `-----.-----'
5462306a36Sopenharmony_ci		  |
5562306a36Sopenharmony_ci       .- 'Encrypted Packet' expands to
5662306a36Sopenharmony_ci       |
5762306a36Sopenharmony_ci       V
5862306a36Sopenharmony_ci      ,---------------------------------------------------.
5962306a36Sopenharmony_ciBytes |  1   |  1   |    1    |    3     |  2   |  0-2304 |
6062306a36Sopenharmony_ci      |------|------|---------|----------|------|---------|
6162306a36Sopenharmony_ciDesc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP      |
6262306a36Sopenharmony_ci      | DSAP | SSAP |         |          |      | Packet  |
6362306a36Sopenharmony_ci      | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8|      |         |
6462306a36Sopenharmony_ci      `----------------------------------------------------
6562306a36Sopenharmony_ciTotal: 8 non-data bytes
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci802.3 Ethernet Data Frame
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci      ,-----------------------------------------.
7062306a36Sopenharmony_ciBytes |   6   |   6   |  2   |  Variable |   4  |
7162306a36Sopenharmony_ci      |-------|-------|------|-----------|------|
7262306a36Sopenharmony_ciDesc. | Dest. | Source| Type | IP Packet |  fcs |
7362306a36Sopenharmony_ci      |  MAC  |  MAC  |      |           |      |
7462306a36Sopenharmony_ci      `-----------------------------------------'
7562306a36Sopenharmony_ciTotal: 18 non-data bytes
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciIn the event that fragmentation is required, the incoming payload is split into
7862306a36Sopenharmony_ciN parts of size ieee->fts.  The first fragment contains the SNAP header and the
7962306a36Sopenharmony_ciremaining packets are just data.
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciIf encryption is enabled, each fragment payload size is reduced by enough space
8262306a36Sopenharmony_cito add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP)
8362306a36Sopenharmony_ciSo if you have 1500 bytes of payload with ieee->fts set to 500 without
8462306a36Sopenharmony_ciencryption it will take 3 frames.  With WEP it will take 4 frames as the
8562306a36Sopenharmony_cipayload of each frame is reduced to 492 bytes.
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci* SKB visualization
8862306a36Sopenharmony_ci*
8962306a36Sopenharmony_ci*  ,- skb->data
9062306a36Sopenharmony_ci* |
9162306a36Sopenharmony_ci* |    ETHERNET HEADER        ,-<-- PAYLOAD
9262306a36Sopenharmony_ci* |                           |     14 bytes from skb->data
9362306a36Sopenharmony_ci* |  2 bytes for Type --> ,T. |     (sizeof ethhdr)
9462306a36Sopenharmony_ci* |                       | | |
9562306a36Sopenharmony_ci* |,-Dest.--. ,--Src.---. | | |
9662306a36Sopenharmony_ci* |  6 bytes| | 6 bytes | | | |
9762306a36Sopenharmony_ci* v         | |         | | | |
9862306a36Sopenharmony_ci* 0         | v       1 | v | v           2
9962306a36Sopenharmony_ci* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
10062306a36Sopenharmony_ci*     ^     | ^         | ^ |
10162306a36Sopenharmony_ci*     |     | |         | | |
10262306a36Sopenharmony_ci*     |     | |         | `T' <---- 2 bytes for Type
10362306a36Sopenharmony_ci*     |     | |         |
10462306a36Sopenharmony_ci*     |     | '---SNAP--' <-------- 6 bytes for SNAP
10562306a36Sopenharmony_ci*     |     |
10662306a36Sopenharmony_ci*     `-IV--' <-------------------- 4 bytes for IV (WEP)
10762306a36Sopenharmony_ci*
10862306a36Sopenharmony_ci*      SNAP HEADER
10962306a36Sopenharmony_ci*
11062306a36Sopenharmony_ci*/
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
11362306a36Sopenharmony_cistatic u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic int libipw_copy_snap(u8 * data, __be16 h_proto)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct libipw_snap_hdr *snap;
11862306a36Sopenharmony_ci	u8 *oui;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	snap = (struct libipw_snap_hdr *)data;
12162306a36Sopenharmony_ci	snap->dsap = 0xaa;
12262306a36Sopenharmony_ci	snap->ssap = 0xaa;
12362306a36Sopenharmony_ci	snap->ctrl = 0x03;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX))
12662306a36Sopenharmony_ci		oui = P802_1H_OUI;
12762306a36Sopenharmony_ci	else
12862306a36Sopenharmony_ci		oui = RFC1042_OUI;
12962306a36Sopenharmony_ci	snap->oui[0] = oui[0];
13062306a36Sopenharmony_ci	snap->oui[1] = oui[1];
13162306a36Sopenharmony_ci	snap->oui[2] = oui[2];
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16));
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return SNAP_SIZE + sizeof(u16);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int libipw_encrypt_fragment(struct libipw_device *ieee,
13962306a36Sopenharmony_ci					     struct sk_buff *frag, int hdr_len)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct lib80211_crypt_data *crypt =
14262306a36Sopenharmony_ci		ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
14362306a36Sopenharmony_ci	int res;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (crypt == NULL)
14662306a36Sopenharmony_ci		return -1;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* To encrypt, frame format is:
14962306a36Sopenharmony_ci	 * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
15062306a36Sopenharmony_ci	atomic_inc(&crypt->refcnt);
15162306a36Sopenharmony_ci	res = 0;
15262306a36Sopenharmony_ci	if (crypt->ops && crypt->ops->encrypt_mpdu)
15362306a36Sopenharmony_ci		res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	atomic_dec(&crypt->refcnt);
15662306a36Sopenharmony_ci	if (res < 0) {
15762306a36Sopenharmony_ci		printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
15862306a36Sopenharmony_ci		       ieee->dev->name, frag->len);
15962306a36Sopenharmony_ci		ieee->ieee_stats.tx_discards++;
16062306a36Sopenharmony_ci		return -1;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_civoid libipw_txb_free(struct libipw_txb *txb)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	int i;
16962306a36Sopenharmony_ci	if (unlikely(!txb))
17062306a36Sopenharmony_ci		return;
17162306a36Sopenharmony_ci	for (i = 0; i < txb->nr_frags; i++)
17262306a36Sopenharmony_ci		if (txb->fragments[i])
17362306a36Sopenharmony_ci			dev_kfree_skb_any(txb->fragments[i]);
17462306a36Sopenharmony_ci	kfree(txb);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic struct libipw_txb *libipw_alloc_txb(int nr_frags, int txb_size,
17862306a36Sopenharmony_ci						 int headroom, gfp_t gfp_mask)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct libipw_txb *txb;
18162306a36Sopenharmony_ci	int i;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	txb = kmalloc(struct_size(txb, fragments, nr_frags), gfp_mask);
18462306a36Sopenharmony_ci	if (!txb)
18562306a36Sopenharmony_ci		return NULL;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	memset(txb, 0, sizeof(struct libipw_txb));
18862306a36Sopenharmony_ci	txb->nr_frags = nr_frags;
18962306a36Sopenharmony_ci	txb->frag_size = txb_size;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	for (i = 0; i < nr_frags; i++) {
19262306a36Sopenharmony_ci		txb->fragments[i] = __dev_alloc_skb(txb_size + headroom,
19362306a36Sopenharmony_ci						    gfp_mask);
19462306a36Sopenharmony_ci		if (unlikely(!txb->fragments[i])) {
19562306a36Sopenharmony_ci			i--;
19662306a36Sopenharmony_ci			break;
19762306a36Sopenharmony_ci		}
19862306a36Sopenharmony_ci		skb_reserve(txb->fragments[i], headroom);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	if (unlikely(i != nr_frags)) {
20162306a36Sopenharmony_ci		while (i >= 0)
20262306a36Sopenharmony_ci			dev_kfree_skb_any(txb->fragments[i--]);
20362306a36Sopenharmony_ci		kfree(txb);
20462306a36Sopenharmony_ci		return NULL;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci	return txb;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic int libipw_classify(struct sk_buff *skb)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct ethhdr *eth;
21262306a36Sopenharmony_ci	struct iphdr *ip;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	eth = (struct ethhdr *)skb->data;
21562306a36Sopenharmony_ci	if (eth->h_proto != htons(ETH_P_IP))
21662306a36Sopenharmony_ci		return 0;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	ip = ip_hdr(skb);
21962306a36Sopenharmony_ci	switch (ip->tos & 0xfc) {
22062306a36Sopenharmony_ci	case 0x20:
22162306a36Sopenharmony_ci		return 2;
22262306a36Sopenharmony_ci	case 0x40:
22362306a36Sopenharmony_ci		return 1;
22462306a36Sopenharmony_ci	case 0x60:
22562306a36Sopenharmony_ci		return 3;
22662306a36Sopenharmony_ci	case 0x80:
22762306a36Sopenharmony_ci		return 4;
22862306a36Sopenharmony_ci	case 0xa0:
22962306a36Sopenharmony_ci		return 5;
23062306a36Sopenharmony_ci	case 0xc0:
23162306a36Sopenharmony_ci		return 6;
23262306a36Sopenharmony_ci	case 0xe0:
23362306a36Sopenharmony_ci		return 7;
23462306a36Sopenharmony_ci	default:
23562306a36Sopenharmony_ci		return 0;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/* Incoming skb is converted to a txb which consists of
24062306a36Sopenharmony_ci * a block of 802.11 fragment packets (stored as skbs) */
24162306a36Sopenharmony_cinetdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct libipw_device *ieee = netdev_priv(dev);
24462306a36Sopenharmony_ci	struct libipw_txb *txb = NULL;
24562306a36Sopenharmony_ci	struct libipw_hdr_3addrqos *frag_hdr;
24662306a36Sopenharmony_ci	int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
24762306a36Sopenharmony_ci	    rts_required;
24862306a36Sopenharmony_ci	unsigned long flags;
24962306a36Sopenharmony_ci	int encrypt, host_encrypt, host_encrypt_msdu;
25062306a36Sopenharmony_ci	__be16 ether_type;
25162306a36Sopenharmony_ci	int bytes, fc, hdr_len;
25262306a36Sopenharmony_ci	struct sk_buff *skb_frag;
25362306a36Sopenharmony_ci	struct libipw_hdr_3addrqos header = {/* Ensure zero initialized */
25462306a36Sopenharmony_ci		.duration_id = 0,
25562306a36Sopenharmony_ci		.seq_ctl = 0,
25662306a36Sopenharmony_ci		.qos_ctl = 0
25762306a36Sopenharmony_ci	};
25862306a36Sopenharmony_ci	u8 dest[ETH_ALEN], src[ETH_ALEN];
25962306a36Sopenharmony_ci	struct lib80211_crypt_data *crypt;
26062306a36Sopenharmony_ci	int priority = skb->priority;
26162306a36Sopenharmony_ci	int snapped = 0;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority))
26462306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	spin_lock_irqsave(&ieee->lock, flags);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* If there is no driver handler to take the TXB, dont' bother
26962306a36Sopenharmony_ci	 * creating it... */
27062306a36Sopenharmony_ci	if (!ieee->hard_start_xmit) {
27162306a36Sopenharmony_ci		printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
27262306a36Sopenharmony_ci		goto success;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
27662306a36Sopenharmony_ci		printk(KERN_WARNING "%s: skb too small (%d).\n",
27762306a36Sopenharmony_ci		       ieee->dev->name, skb->len);
27862306a36Sopenharmony_ci		goto success;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	ether_type = ((struct ethhdr *)skb->data)->h_proto;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) &&
28662306a36Sopenharmony_ci	    ieee->sec.encrypt;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	host_encrypt = ieee->host_encrypt && encrypt && crypt;
28962306a36Sopenharmony_ci	host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (!encrypt && ieee->ieee802_1x &&
29262306a36Sopenharmony_ci	    ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) {
29362306a36Sopenharmony_ci		dev->stats.tx_dropped++;
29462306a36Sopenharmony_ci		goto success;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* Save source and destination addresses */
29862306a36Sopenharmony_ci	skb_copy_from_linear_data(skb, dest, ETH_ALEN);
29962306a36Sopenharmony_ci	skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (host_encrypt)
30262306a36Sopenharmony_ci		fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
30362306a36Sopenharmony_ci		    IEEE80211_FCTL_PROTECTED;
30462306a36Sopenharmony_ci	else
30562306a36Sopenharmony_ci		fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (ieee->iw_mode == IW_MODE_INFRA) {
30862306a36Sopenharmony_ci		fc |= IEEE80211_FCTL_TODS;
30962306a36Sopenharmony_ci		/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
31062306a36Sopenharmony_ci		memcpy(header.addr1, ieee->bssid, ETH_ALEN);
31162306a36Sopenharmony_ci		memcpy(header.addr2, src, ETH_ALEN);
31262306a36Sopenharmony_ci		memcpy(header.addr3, dest, ETH_ALEN);
31362306a36Sopenharmony_ci	} else if (ieee->iw_mode == IW_MODE_ADHOC) {
31462306a36Sopenharmony_ci		/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
31562306a36Sopenharmony_ci		memcpy(header.addr1, dest, ETH_ALEN);
31662306a36Sopenharmony_ci		memcpy(header.addr2, src, ETH_ALEN);
31762306a36Sopenharmony_ci		memcpy(header.addr3, ieee->bssid, ETH_ALEN);
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci	hdr_len = LIBIPW_3ADDR_LEN;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) {
32262306a36Sopenharmony_ci		fc |= IEEE80211_STYPE_QOS_DATA;
32362306a36Sopenharmony_ci		hdr_len += 2;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		skb->priority = libipw_classify(skb);
32662306a36Sopenharmony_ci		header.qos_ctl |= cpu_to_le16(skb->priority & LIBIPW_QCTL_TID);
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	header.frame_ctl = cpu_to_le16(fc);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* Advance the SKB to the start of the payload */
33162306a36Sopenharmony_ci	skb_pull(skb, sizeof(struct ethhdr));
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* Determine total amount of storage required for TXB packets */
33462306a36Sopenharmony_ci	bytes = skb->len + SNAP_SIZE + sizeof(u16);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Encrypt msdu first on the whole data packet. */
33762306a36Sopenharmony_ci	if ((host_encrypt || host_encrypt_msdu) &&
33862306a36Sopenharmony_ci	    crypt && crypt->ops && crypt->ops->encrypt_msdu) {
33962306a36Sopenharmony_ci		int res = 0;
34062306a36Sopenharmony_ci		int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len +
34162306a36Sopenharmony_ci		    crypt->ops->extra_msdu_postfix_len;
34262306a36Sopenharmony_ci		struct sk_buff *skb_new = dev_alloc_skb(len);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		if (unlikely(!skb_new))
34562306a36Sopenharmony_ci			goto failed;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len);
34862306a36Sopenharmony_ci		skb_put_data(skb_new, &header, hdr_len);
34962306a36Sopenharmony_ci		snapped = 1;
35062306a36Sopenharmony_ci		libipw_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
35162306a36Sopenharmony_ci				    ether_type);
35262306a36Sopenharmony_ci		skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len);
35362306a36Sopenharmony_ci		res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
35462306a36Sopenharmony_ci		if (res < 0) {
35562306a36Sopenharmony_ci			LIBIPW_ERROR("msdu encryption failed\n");
35662306a36Sopenharmony_ci			dev_kfree_skb_any(skb_new);
35762306a36Sopenharmony_ci			goto failed;
35862306a36Sopenharmony_ci		}
35962306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
36062306a36Sopenharmony_ci		skb = skb_new;
36162306a36Sopenharmony_ci		bytes += crypt->ops->extra_msdu_prefix_len +
36262306a36Sopenharmony_ci		    crypt->ops->extra_msdu_postfix_len;
36362306a36Sopenharmony_ci		skb_pull(skb, hdr_len);
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (host_encrypt || ieee->host_open_frag) {
36762306a36Sopenharmony_ci		/* Determine fragmentation size based on destination (multicast
36862306a36Sopenharmony_ci		 * and broadcast are not fragmented) */
36962306a36Sopenharmony_ci		if (is_multicast_ether_addr(dest) ||
37062306a36Sopenharmony_ci		    is_broadcast_ether_addr(dest))
37162306a36Sopenharmony_ci			frag_size = MAX_FRAG_THRESHOLD;
37262306a36Sopenharmony_ci		else
37362306a36Sopenharmony_ci			frag_size = ieee->fts;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		/* Determine amount of payload per fragment.  Regardless of if
37662306a36Sopenharmony_ci		 * this stack is providing the full 802.11 header, one will
37762306a36Sopenharmony_ci		 * eventually be affixed to this fragment -- so we must account
37862306a36Sopenharmony_ci		 * for it when determining the amount of payload space. */
37962306a36Sopenharmony_ci		bytes_per_frag = frag_size - hdr_len;
38062306a36Sopenharmony_ci		if (ieee->config &
38162306a36Sopenharmony_ci		    (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS))
38262306a36Sopenharmony_ci			bytes_per_frag -= LIBIPW_FCS_LEN;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		/* Each fragment may need to have room for encryption
38562306a36Sopenharmony_ci		 * pre/postfix */
38662306a36Sopenharmony_ci		if (host_encrypt && crypt && crypt->ops)
38762306a36Sopenharmony_ci			bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len +
38862306a36Sopenharmony_ci			    crypt->ops->extra_mpdu_postfix_len;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		/* Number of fragments is the total
39162306a36Sopenharmony_ci		 * bytes_per_frag / payload_per_fragment */
39262306a36Sopenharmony_ci		nr_frags = bytes / bytes_per_frag;
39362306a36Sopenharmony_ci		bytes_last_frag = bytes % bytes_per_frag;
39462306a36Sopenharmony_ci		if (bytes_last_frag)
39562306a36Sopenharmony_ci			nr_frags++;
39662306a36Sopenharmony_ci		else
39762306a36Sopenharmony_ci			bytes_last_frag = bytes_per_frag;
39862306a36Sopenharmony_ci	} else {
39962306a36Sopenharmony_ci		nr_frags = 1;
40062306a36Sopenharmony_ci		bytes_per_frag = bytes_last_frag = bytes;
40162306a36Sopenharmony_ci		frag_size = bytes + hdr_len;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	rts_required = (frag_size > ieee->rts
40562306a36Sopenharmony_ci			&& ieee->config & CFG_LIBIPW_RTS);
40662306a36Sopenharmony_ci	if (rts_required)
40762306a36Sopenharmony_ci		nr_frags++;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* When we allocate the TXB we allocate enough space for the reserve
41062306a36Sopenharmony_ci	 * and full fragment bytes (bytes_per_frag doesn't include prefix,
41162306a36Sopenharmony_ci	 * postfix, header, FCS, etc.) */
41262306a36Sopenharmony_ci	txb = libipw_alloc_txb(nr_frags, frag_size,
41362306a36Sopenharmony_ci				  ieee->tx_headroom, GFP_ATOMIC);
41462306a36Sopenharmony_ci	if (unlikely(!txb)) {
41562306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Could not allocate TXB\n",
41662306a36Sopenharmony_ci		       ieee->dev->name);
41762306a36Sopenharmony_ci		goto failed;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci	txb->encrypted = encrypt;
42062306a36Sopenharmony_ci	if (host_encrypt)
42162306a36Sopenharmony_ci		txb->payload_size = frag_size * (nr_frags - 1) +
42262306a36Sopenharmony_ci		    bytes_last_frag;
42362306a36Sopenharmony_ci	else
42462306a36Sopenharmony_ci		txb->payload_size = bytes;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (rts_required) {
42762306a36Sopenharmony_ci		skb_frag = txb->fragments[0];
42862306a36Sopenharmony_ci		frag_hdr = skb_put(skb_frag, hdr_len);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		/*
43162306a36Sopenharmony_ci		 * Set header frame_ctl to the RTS.
43262306a36Sopenharmony_ci		 */
43362306a36Sopenharmony_ci		header.frame_ctl =
43462306a36Sopenharmony_ci		    cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
43562306a36Sopenharmony_ci		memcpy(frag_hdr, &header, hdr_len);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		/*
43862306a36Sopenharmony_ci		 * Restore header frame_ctl to the original data setting.
43962306a36Sopenharmony_ci		 */
44062306a36Sopenharmony_ci		header.frame_ctl = cpu_to_le16(fc);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		if (ieee->config &
44362306a36Sopenharmony_ci		    (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS))
44462306a36Sopenharmony_ci			skb_put(skb_frag, 4);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		txb->rts_included = 1;
44762306a36Sopenharmony_ci		i = 1;
44862306a36Sopenharmony_ci	} else
44962306a36Sopenharmony_ci		i = 0;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	for (; i < nr_frags; i++) {
45262306a36Sopenharmony_ci		skb_frag = txb->fragments[i];
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		if (host_encrypt)
45562306a36Sopenharmony_ci			skb_reserve(skb_frag,
45662306a36Sopenharmony_ci				    crypt->ops->extra_mpdu_prefix_len);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		frag_hdr = skb_put_data(skb_frag, &header, hdr_len);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		/* If this is not the last fragment, then add the MOREFRAGS
46162306a36Sopenharmony_ci		 * bit to the frame control */
46262306a36Sopenharmony_ci		if (i != nr_frags - 1) {
46362306a36Sopenharmony_ci			frag_hdr->frame_ctl =
46462306a36Sopenharmony_ci			    cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS);
46562306a36Sopenharmony_ci			bytes = bytes_per_frag;
46662306a36Sopenharmony_ci		} else {
46762306a36Sopenharmony_ci			/* The last fragment takes the remaining length */
46862306a36Sopenharmony_ci			bytes = bytes_last_frag;
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		if (i == 0 && !snapped) {
47262306a36Sopenharmony_ci			libipw_copy_snap(skb_put
47362306a36Sopenharmony_ci					    (skb_frag, SNAP_SIZE + sizeof(u16)),
47462306a36Sopenharmony_ci					    ether_type);
47562306a36Sopenharmony_ci			bytes -= SNAP_SIZE + sizeof(u16);
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		/* Advance the SKB... */
48162306a36Sopenharmony_ci		skb_pull(skb, bytes);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		/* Encryption routine will move the header forward in order
48462306a36Sopenharmony_ci		 * to insert the IV between the header and the payload */
48562306a36Sopenharmony_ci		if (host_encrypt)
48662306a36Sopenharmony_ci			libipw_encrypt_fragment(ieee, skb_frag, hdr_len);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		if (ieee->config &
48962306a36Sopenharmony_ci		    (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS))
49062306a36Sopenharmony_ci			skb_put(skb_frag, 4);
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci      success:
49462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ieee->lock, flags);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (txb) {
49962306a36Sopenharmony_ci		netdev_tx_t ret = (*ieee->hard_start_xmit)(txb, dev, priority);
50062306a36Sopenharmony_ci		if (ret == NETDEV_TX_OK) {
50162306a36Sopenharmony_ci			dev->stats.tx_packets++;
50262306a36Sopenharmony_ci			dev->stats.tx_bytes += txb->payload_size;
50362306a36Sopenharmony_ci			return NETDEV_TX_OK;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		libipw_txb_free(txb);
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return NETDEV_TX_OK;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci      failed:
51262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ieee->lock, flags);
51362306a36Sopenharmony_ci	netif_stop_queue(dev);
51462306a36Sopenharmony_ci	dev->stats.tx_errors++;
51562306a36Sopenharmony_ci	return NETDEV_TX_BUSY;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_xmit);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_txb_free);
520