2005-05-12 22:48:20 -04:00
/******************************************************************************
2005-09-21 11:58:43 -05:00
Copyright ( c ) 2003 - 2005 Intel Corporation . All rights reserved .
2005-05-12 22:48:20 -04:00
This program is free software ; you can redistribute it and / or modify it
under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation .
This program is distributed in the hope that it will be useful , but WITHOUT
ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
more details .
You should have received a copy of the GNU General Public License along with
this program ; if not , write to the Free Software Foundation , Inc . , 59
Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
The full GNU General Public License is included in this distribution in the
file called LICENSE .
Contact Information :
James P . Ketrenos < ipw2100 - admin @ linux . intel . com >
Intel Corporation , 5200 N . E . Elam Young Parkway , Hillsboro , OR 97124 - 6497
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/compiler.h>
# include <linux/errno.h>
# include <linux/if_arp.h>
# include <linux/in6.h>
# include <linux/in.h>
# include <linux/ip.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/proc_fs.h>
# include <linux/skbuff.h>
# include <linux/slab.h>
# include <linux/tcp.h>
# include <linux/types.h>
# include <linux/wireless.h>
# include <linux/etherdevice.h>
# include <asm/uaccess.h>
# include <net/ieee80211.h>
/*
802.11 Data Frame
, - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0. .2312 | 4 |
| - - - - - - | - - - - - - | - - - - - - - - - | - - - - - - - - - | - - - - - - - - - | - - - - - - | - - - - - - - - - | - - - - - - |
Desc . | ctrl | dura | DA / RA | TA | SA | Sequ | Frame | fcs |
| | tion | ( BSSID ) | | | ence | data | |
` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | - - - - - - '
Total : 28 non - data bytes ` - - - - . - - - - '
|
2006-01-18 12:02:33 +02:00
. - ' Frame data ' expands , if WEP enabled , to < - - - - - - - - - - '
|
V
, - - - - - - - - - - - - - - - - - - - - - - - .
Bytes | 4 | 0 - 2296 | 4 |
| - - - - - | - - - - - - - - - - - | - - - - - |
Desc . | IV | Encrypted | ICV |
| | Packet | |
` - - - - - | | - - - - - '
` - - - - - . - - - - - '
|
. - ' Encrypted Packet ' expands to
2005-05-12 22:48:20 -04:00
|
V
, - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
Bytes | 1 | 1 | 1 | 3 | 2 | 0 - 2304 |
| - - - - - - | - - - - - - | - - - - - - - - - | - - - - - - - - - - | - - - - - - | - - - - - - - - - |
Desc . | SNAP | SNAP | Control | Eth Tunnel | Type | IP |
| DSAP | SSAP | | | | Packet |
| 0xAA | 0xAA | 0x03 ( UI ) | 0x00 - 00 - F8 | | |
2006-01-18 12:02:33 +02:00
` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2005-05-12 22:48:20 -04:00
Total : 8 non - data bytes
802.3 Ethernet Data Frame
, - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
Bytes | 6 | 6 | 2 | Variable | 4 |
| - - - - - - - | - - - - - - - | - - - - - - | - - - - - - - - - - - | - - - - - - |
Desc . | Dest . | Source | Type | IP Packet | fcs |
| MAC | MAC | | | |
` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '
Total : 18 non - data bytes
In the event that fragmentation is required , the incoming payload is split into
N parts of size ieee - > fts . The first fragment contains the SNAP header and the
remaining packets are just data .
If encryption is enabled , each fragment payload size is reduced by enough space
to add the prefix and postfix ( IV and ICV totalling 8 bytes in the case of WEP )
So if you have 1500 bytes of payload with ieee - > fts set to 500 without
encryption it will take 3 frames . With WEP it will take 4 frames as the
payload of each frame is reduced to 492 bytes .
* SKB visualization
*
* , - skb - > data
* |
* | ETHERNET HEADER , - < - - PAYLOAD
* | | 14 bytes from skb - > data
* | 2 bytes for Type - - > , T . | ( sizeof ethhdr )
* | | | |
* | , - Dest . - - . , - - Src . - - - . | | |
* | 6 bytes | | 6 bytes | | | |
* v | | | | | |
* 0 | v 1 | v | v 2
* 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
* ^ | ^ | ^ |
* | | | | | |
* | | | | ` T ' < - - - - 2 bytes for Type
* | | | |
* | | ' - - - SNAP - - ' < - - - - - - - - 6 bytes for SNAP
* | |
* ` - IV - - ' < - - - - - - - - - - - - - - - - - - - - 4 bytes for IV ( WEP )
*
* SNAP HEADER
*
*/
static u8 P802_1H_OUI [ P80211_OUI_LEN ] = { 0x00 , 0x00 , 0xf8 } ;
static u8 RFC1042_OUI [ P80211_OUI_LEN ] = { 0x00 , 0x00 , 0x00 } ;
2006-01-14 13:20:43 -08:00
static int ieee80211_copy_snap ( u8 * data , u16 h_proto )
2005-05-12 22:48:20 -04:00
{
struct ieee80211_snap_hdr * snap ;
u8 * oui ;
snap = ( struct ieee80211_snap_hdr * ) data ;
snap - > dsap = 0xaa ;
snap - > ssap = 0xaa ;
snap - > ctrl = 0x03 ;
if ( h_proto = = 0x8137 | | h_proto = = 0x80f3 )
oui = P802_1H_OUI ;
else
oui = RFC1042_OUI ;
snap - > oui [ 0 ] = oui [ 0 ] ;
snap - > oui [ 1 ] = oui [ 1 ] ;
snap - > oui [ 2 ] = oui [ 2 ] ;
2005-09-07 00:48:31 -04:00
* ( u16 * ) ( data + SNAP_SIZE ) = htons ( h_proto ) ;
2005-05-12 22:48:20 -04:00
return SNAP_SIZE + sizeof ( u16 ) ;
}
2006-01-14 13:20:43 -08:00
static int ieee80211_encrypt_fragment ( struct ieee80211_device * ieee ,
2005-09-07 00:48:31 -04:00
struct sk_buff * frag , int hdr_len )
2005-05-12 22:48:20 -04:00
{
2005-09-07 00:48:31 -04:00
struct ieee80211_crypt_data * crypt = ieee - > crypt [ ieee - > tx_keyidx ] ;
2005-05-12 22:48:20 -04:00
int res ;
2005-10-20 11:06:36 -05:00
if ( crypt = = NULL )
return - 1 ;
2005-05-12 22:48:20 -04:00
/* To encrypt, frame format is:
* IV ( 4 bytes ) , clear payload ( including SNAP ) , ICV ( 4 bytes ) */
atomic_inc ( & crypt - > refcnt ) ;
res = 0 ;
2005-10-20 11:06:36 -05:00
if ( crypt - > ops & & crypt - > ops - > encrypt_mpdu )
2005-05-12 22:48:20 -04:00
res = crypt - > ops - > encrypt_mpdu ( frag , hdr_len , crypt - > priv ) ;
atomic_dec ( & crypt - > refcnt ) ;
if ( res < 0 ) {
printk ( KERN_INFO " %s: Encryption failed: len=%d. \n " ,
ieee - > dev - > name , frag - > len ) ;
ieee - > ieee_stats . tx_discards + + ;
return - 1 ;
}
return 0 ;
}
2005-09-07 00:48:31 -04:00
void ieee80211_txb_free ( struct ieee80211_txb * txb )
{
2005-05-12 22:48:20 -04:00
int i ;
if ( unlikely ( ! txb ) )
return ;
for ( i = 0 ; i < txb - > nr_frags ; i + + )
if ( txb - > fragments [ i ] )
dev_kfree_skb_any ( txb - > fragments [ i ] ) ;
kfree ( txb ) ;
}
2005-05-06 23:32:39 +02:00
static struct ieee80211_txb * ieee80211_alloc_txb ( int nr_frags , int txb_size ,
2005-10-21 12:39:52 -05:00
int headroom , gfp_t gfp_mask )
2005-05-12 22:48:20 -04:00
{
struct ieee80211_txb * txb ;
int i ;
2005-09-07 00:48:31 -04:00
txb = kmalloc ( sizeof ( struct ieee80211_txb ) + ( sizeof ( u8 * ) * nr_frags ) ,
gfp_mask ) ;
2005-05-12 22:48:20 -04:00
if ( ! txb )
return NULL ;
2005-04-11 16:52:15 -07:00
memset ( txb , 0 , sizeof ( struct ieee80211_txb ) ) ;
2005-05-12 22:48:20 -04:00
txb - > nr_frags = nr_frags ;
txb - > frag_size = txb_size ;
for ( i = 0 ; i < nr_frags ; i + + ) {
2005-10-21 12:39:52 -05:00
txb - > fragments [ i ] = __dev_alloc_skb ( txb_size + headroom ,
gfp_mask ) ;
2005-05-12 22:48:20 -04:00
if ( unlikely ( ! txb - > fragments [ i ] ) ) {
i - - ;
break ;
}
2005-10-21 12:39:52 -05:00
skb_reserve ( txb - > fragments [ i ] , headroom ) ;
2005-05-12 22:48:20 -04:00
}
if ( unlikely ( i ! = nr_frags ) ) {
while ( i > = 0 )
dev_kfree_skb_any ( txb - > fragments [ i - - ] ) ;
kfree ( txb ) ;
return NULL ;
}
return txb ;
}
2006-04-13 17:17:17 +08:00
static int ieee80211_classify ( struct sk_buff * skb )
{
struct ethhdr * eth ;
struct iphdr * ip ;
eth = ( struct ethhdr * ) skb - > data ;
if ( eth - > h_proto ! = __constant_htons ( ETH_P_IP ) )
return 0 ;
ip = skb - > nh . iph ;
switch ( ip - > tos & 0xfc ) {
case 0x20 :
return 2 ;
case 0x40 :
return 1 ;
case 0x60 :
return 3 ;
case 0x80 :
return 4 ;
case 0xa0 :
return 5 ;
case 0xc0 :
return 6 ;
case 0xe0 :
return 7 ;
default :
return 0 ;
}
}
2005-09-21 11:54:53 -05:00
/* Incoming skb is converted to a txb which consists of
2005-09-21 11:54:43 -05:00
* a block of 802.11 fragment packets ( stored as skbs ) */
2005-09-07 00:48:31 -04:00
int ieee80211_xmit ( struct sk_buff * skb , struct net_device * dev )
2005-05-12 22:48:20 -04:00
{
struct ieee80211_device * ieee = netdev_priv ( dev ) ;
struct ieee80211_txb * txb = NULL ;
2006-04-13 17:17:17 +08:00
struct ieee80211_hdr_3addrqos * frag_hdr ;
2005-09-21 11:54:43 -05:00
int i , bytes_per_frag , nr_frags , bytes_last_frag , frag_size ,
rts_required ;
2005-05-12 22:48:20 -04:00
unsigned long flags ;
struct net_device_stats * stats = & ieee - > stats ;
2005-09-21 11:58:49 -05:00
int ether_type , encrypt , host_encrypt , host_encrypt_msdu , host_build_iv ;
2005-05-12 22:48:20 -04:00
int bytes , fc , hdr_len ;
struct sk_buff * skb_frag ;
2006-04-13 17:17:17 +08:00
struct ieee80211_hdr_3addrqos header = { /* Ensure zero initialized */
2005-05-12 22:48:20 -04:00
. duration_id = 0 ,
2006-04-13 17:17:17 +08:00
. seq_ctl = 0 ,
. qos_ctl = 0
2005-05-12 22:48:20 -04:00
} ;
u8 dest [ ETH_ALEN ] , src [ ETH_ALEN ] ;
2005-09-07 00:48:31 -04:00
struct ieee80211_crypt_data * crypt ;
2005-09-21 11:56:27 -05:00
int priority = skb - > priority ;
2005-09-21 11:54:53 -05:00
int snapped = 0 ;
2005-05-12 22:48:20 -04:00
2005-09-21 11:56:27 -05:00
if ( ieee - > is_queue_full & & ( * ieee - > is_queue_full ) ( dev , priority ) )
return NETDEV_TX_BUSY ;
2005-05-12 22:48:20 -04:00
spin_lock_irqsave ( & ieee - > lock , flags ) ;
/* If there is no driver handler to take the TXB, dont' bother
* creating it . . . */
if ( ! ieee - > hard_start_xmit ) {
2005-09-07 00:48:31 -04:00
printk ( KERN_WARNING " %s: No xmit handler. \n " , ieee - > dev - > name ) ;
2005-05-12 22:48:20 -04:00
goto success ;
}
if ( unlikely ( skb - > len < SNAP_SIZE + sizeof ( u16 ) ) ) {
printk ( KERN_WARNING " %s: skb too small (%d). \n " ,
ieee - > dev - > name , skb - > len ) ;
goto success ;
}
ether_type = ntohs ( ( ( struct ethhdr * ) skb - > data ) - > h_proto ) ;
crypt = ieee - > crypt [ ieee - > tx_keyidx ] ;
encrypt = ! ( ether_type = = ETH_P_PAE & & ieee - > ieee802_1x ) & &
2005-09-21 11:53:54 -05:00
ieee - > sec . encrypt ;
2005-09-21 11:58:49 -05:00
2005-10-20 11:06:36 -05:00
host_encrypt = ieee - > host_encrypt & & encrypt & & crypt ;
host_encrypt_msdu = ieee - > host_encrypt_msdu & & encrypt & & crypt ;
host_build_iv = ieee - > host_build_iv & & encrypt & & crypt ;
2005-05-12 22:48:20 -04:00
if ( ! encrypt & & ieee - > ieee802_1x & &
ieee - > drop_unencrypted & & ether_type ! = ETH_P_PAE ) {
stats - > tx_dropped + + ;
goto success ;
}
/* Save source and destination addresses */
2005-09-13 17:40:29 -05:00
memcpy ( dest , skb - > data , ETH_ALEN ) ;
memcpy ( src , skb - > data + ETH_ALEN , ETH_ALEN ) ;
2005-05-12 22:48:20 -04:00
2005-12-31 11:35:20 +01:00
if ( host_encrypt | | host_build_iv )
2005-05-12 22:48:20 -04:00
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
2005-09-07 00:48:31 -04:00
IEEE80211_FCTL_PROTECTED ;
2005-05-12 22:48:20 -04:00
else
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA ;
if ( ieee - > iw_mode = = IW_MODE_INFRA ) {
fc | = IEEE80211_FCTL_TODS ;
2005-09-21 11:54:53 -05:00
/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
2005-09-13 17:40:29 -05:00
memcpy ( header . addr1 , ieee - > bssid , ETH_ALEN ) ;
memcpy ( header . addr2 , src , ETH_ALEN ) ;
memcpy ( header . addr3 , dest , ETH_ALEN ) ;
2005-05-12 22:48:20 -04:00
} else if ( ieee - > iw_mode = = IW_MODE_ADHOC ) {
2005-09-21 11:54:53 -05:00
/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
2005-09-13 17:40:29 -05:00
memcpy ( header . addr1 , dest , ETH_ALEN ) ;
memcpy ( header . addr2 , src , ETH_ALEN ) ;
memcpy ( header . addr3 , ieee - > bssid , ETH_ALEN ) ;
2005-05-12 22:48:20 -04:00
}
hdr_len = IEEE80211_3ADDR_LEN ;
2006-04-13 17:17:17 +08:00
if ( ieee - > is_qos_active & & ieee - > is_qos_active ( dev , skb ) ) {
fc | = IEEE80211_STYPE_QOS_DATA ;
hdr_len + = 2 ;
skb - > priority = ieee80211_classify ( skb ) ;
header . qos_ctl | = skb - > priority & IEEE80211_QCTL_TID ;
}
header . frame_ctl = cpu_to_le16 ( fc ) ;
/* Advance the SKB to the start of the payload */
skb_pull ( skb , sizeof ( struct ethhdr ) ) ;
/* Determine total amount of storage required for TXB packets */
bytes = skb - > len + SNAP_SIZE + sizeof ( u16 ) ;
2005-09-21 11:54:53 -05:00
/* Encrypt msdu first on the whole data packet. */
if ( ( host_encrypt | | host_encrypt_msdu ) & &
crypt & & crypt - > ops & & crypt - > ops - > encrypt_msdu ) {
int res = 0 ;
int len = bytes + hdr_len + crypt - > ops - > extra_msdu_prefix_len +
crypt - > ops - > extra_msdu_postfix_len ;
struct sk_buff * skb_new = dev_alloc_skb ( len ) ;
2005-09-21 11:58:49 -05:00
2005-09-21 11:54:53 -05:00
if ( unlikely ( ! skb_new ) )
goto failed ;
2005-09-21 11:58:49 -05:00
2005-09-21 11:54:53 -05:00
skb_reserve ( skb_new , crypt - > ops - > extra_msdu_prefix_len ) ;
memcpy ( skb_put ( skb_new , hdr_len ) , & header , hdr_len ) ;
snapped = 1 ;
ieee80211_copy_snap ( skb_put ( skb_new , SNAP_SIZE + sizeof ( u16 ) ) ,
ether_type ) ;
memcpy ( skb_put ( skb_new , skb - > len ) , skb - > data , skb - > len ) ;
res = crypt - > ops - > encrypt_msdu ( skb_new , hdr_len , crypt - > priv ) ;
if ( res < 0 ) {
IEEE80211_ERROR ( " msdu encryption failed \n " ) ;
dev_kfree_skb_any ( skb_new ) ;
goto failed ;
}
dev_kfree_skb_any ( skb ) ;
skb = skb_new ;
bytes + = crypt - > ops - > extra_msdu_prefix_len +
crypt - > ops - > extra_msdu_postfix_len ;
skb_pull ( skb , hdr_len ) ;
}
if ( host_encrypt | | ieee - > host_open_frag ) {
/* Determine fragmentation size based on destination (multicast
* and broadcast are not fragmented ) */
2005-10-19 16:31:34 -05:00
if ( is_multicast_ether_addr ( dest ) | |
is_broadcast_ether_addr ( dest ) )
2005-09-21 11:54:53 -05:00
frag_size = MAX_FRAG_THRESHOLD ;
else
frag_size = ieee - > fts ;
/* Determine amount of payload per fragment. Regardless of if
* this stack is providing the full 802.11 header , one will
* eventually be affixed to this fragment - - so we must account
* for it when determining the amount of payload space . */
bytes_per_frag = frag_size - IEEE80211_3ADDR_LEN ;
if ( ieee - > config &
( CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS ) )
bytes_per_frag - = IEEE80211_FCS_LEN ;
/* Each fragment may need to have room for encryptiong
* pre / postfix */
if ( host_encrypt )
bytes_per_frag - = crypt - > ops - > extra_mpdu_prefix_len +
crypt - > ops - > extra_mpdu_postfix_len ;
/* Number of fragments is the total
* bytes_per_frag / payload_per_fragment */
nr_frags = bytes / bytes_per_frag ;
bytes_last_frag = bytes % bytes_per_frag ;
if ( bytes_last_frag )
nr_frags + + ;
else
bytes_last_frag = bytes_per_frag ;
} else {
nr_frags = 1 ;
bytes_per_frag = bytes_last_frag = bytes ;
frag_size = bytes + IEEE80211_3ADDR_LEN ;
}
2005-05-12 22:48:20 -04:00
2005-09-21 11:54:43 -05:00
rts_required = ( frag_size > ieee - > rts
& & ieee - > config & CFG_IEEE80211_RTS ) ;
if ( rts_required )
nr_frags + + ;
2005-05-12 22:48:20 -04:00
/* When we allocate the TXB we allocate enough space for the reserve
* and full fragment bytes ( bytes_per_frag doesn ' t include prefix ,
* postfix , header , FCS , etc . ) */
2005-10-21 12:39:52 -05:00
txb = ieee80211_alloc_txb ( nr_frags , frag_size ,
ieee - > tx_headroom , GFP_ATOMIC ) ;
2005-05-12 22:48:20 -04:00
if ( unlikely ( ! txb ) ) {
printk ( KERN_WARNING " %s: Could not allocate TXB \n " ,
ieee - > dev - > name ) ;
goto failed ;
}
txb - > encrypted = encrypt ;
2005-09-21 11:54:53 -05:00
if ( host_encrypt )
txb - > payload_size = frag_size * ( nr_frags - 1 ) +
bytes_last_frag ;
else
txb - > payload_size = bytes ;
2005-05-12 22:48:20 -04:00
2005-09-21 11:54:43 -05:00
if ( rts_required ) {
skb_frag = txb - > fragments [ 0 ] ;
frag_hdr =
2006-04-13 17:17:17 +08:00
( struct ieee80211_hdr_3addrqos * ) skb_put ( skb_frag , hdr_len ) ;
2005-09-21 11:54:43 -05:00
/*
* Set header frame_ctl to the RTS .
*/
header . frame_ctl =
cpu_to_le16 ( IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS ) ;
memcpy ( frag_hdr , & header , hdr_len ) ;
/*
* Restore header frame_ctl to the original data setting .
*/
header . frame_ctl = cpu_to_le16 ( fc ) ;
if ( ieee - > config &
( CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS ) )
skb_put ( skb_frag , 4 ) ;
txb - > rts_included = 1 ;
i = 1 ;
} else
i = 0 ;
for ( ; i < nr_frags ; i + + ) {
2005-05-12 22:48:20 -04:00
skb_frag = txb - > fragments [ i ] ;
2005-09-21 11:58:49 -05:00
if ( host_encrypt | | host_build_iv )
2005-09-21 11:54:53 -05:00
skb_reserve ( skb_frag ,
crypt - > ops - > extra_mpdu_prefix_len ) ;
2005-05-12 22:48:20 -04:00
2005-09-21 11:54:36 -05:00
frag_hdr =
2006-04-13 17:17:17 +08:00
( struct ieee80211_hdr_3addrqos * ) skb_put ( skb_frag , hdr_len ) ;
2005-05-12 22:48:20 -04:00
memcpy ( frag_hdr , & header , hdr_len ) ;
/* If this is not the last fragment, then add the MOREFRAGS
* bit to the frame control */
if ( i ! = nr_frags - 1 ) {
2005-09-07 00:48:31 -04:00
frag_hdr - > frame_ctl =
cpu_to_le16 ( fc | IEEE80211_FCTL_MOREFRAGS ) ;
2005-05-12 22:48:20 -04:00
bytes = bytes_per_frag ;
} else {
/* The last fragment takes the remaining length */
bytes = bytes_last_frag ;
}
2005-09-21 11:54:53 -05:00
if ( i = = 0 & & ! snapped ) {
ieee80211_copy_snap ( skb_put
( skb_frag , SNAP_SIZE + sizeof ( u16 ) ) ,
ether_type ) ;
2005-05-12 22:48:20 -04:00
bytes - = SNAP_SIZE + sizeof ( u16 ) ;
}
memcpy ( skb_put ( skb_frag , bytes ) , skb - > data , bytes ) ;
/* Advance the SKB... */
skb_pull ( skb , bytes ) ;
/* Encryption routine will move the header forward in order
* to insert the IV between the header and the payload */
2005-09-21 11:53:54 -05:00
if ( host_encrypt )
2005-05-12 22:48:20 -04:00
ieee80211_encrypt_fragment ( ieee , skb_frag , hdr_len ) ;
2005-09-21 11:58:49 -05:00
else if ( host_build_iv ) {
struct ieee80211_crypt_data * crypt ;
crypt = ieee - > crypt [ ieee - > tx_keyidx ] ;
atomic_inc ( & crypt - > refcnt ) ;
if ( crypt - > ops - > build_iv )
crypt - > ops - > build_iv ( skb_frag , hdr_len ,
2006-01-19 16:22:32 +08:00
ieee - > sec . keys [ ieee - > sec . active_key ] ,
ieee - > sec . key_sizes [ ieee - > sec . active_key ] ,
crypt - > priv ) ;
2005-09-21 11:58:49 -05:00
atomic_dec ( & crypt - > refcnt ) ;
}
2005-09-21 11:53:54 -05:00
2005-05-12 22:48:20 -04:00
if ( ieee - > config &
( CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS ) )
skb_put ( skb_frag , 4 ) ;
}
2005-09-07 00:48:31 -04:00
success :
2005-05-12 22:48:20 -04:00
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
dev_kfree_skb_any ( skb ) ;
if ( txb ) {
2005-09-21 11:56:33 -05:00
int ret = ( * ieee - > hard_start_xmit ) ( txb , dev , priority ) ;
2005-09-21 11:54:53 -05:00
if ( ret = = 0 ) {
2005-05-12 22:48:20 -04:00
stats - > tx_packets + + ;
stats - > tx_bytes + = txb - > payload_size ;
return 0 ;
}
2005-09-21 11:56:27 -05:00
if ( ret = = NETDEV_TX_BUSY ) {
printk ( KERN_ERR " %s: NETDEV_TX_BUSY returned; "
" driver should report queue full via "
" ieee_device->is_queue_full. \n " ,
ieee - > dev - > name ) ;
}
2005-05-12 22:48:20 -04:00
ieee80211_txb_free ( txb ) ;
}
return 0 ;
2005-09-07 00:48:31 -04:00
failed :
2005-05-12 22:48:20 -04:00
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
netif_stop_queue ( dev ) ;
stats - > tx_errors + + ;
return 1 ;
2005-09-21 11:54:47 -05:00
}
/* Incoming 802.11 strucure is converted to a TXB
* a block of 802.11 fragment packets ( stored as skbs ) */
int ieee80211_tx_frame ( struct ieee80211_device * ieee ,
2006-06-01 15:34:26 +01:00
struct ieee80211_hdr * frame , int hdr_len , int total_len ,
int encrypt_mpdu )
2005-09-21 11:54:47 -05:00
{
struct ieee80211_txb * txb = NULL ;
unsigned long flags ;
struct net_device_stats * stats = & ieee - > stats ;
struct sk_buff * skb_frag ;
2005-09-21 11:56:33 -05:00
int priority = - 1 ;
2006-06-21 11:35:08 +08:00
int fraglen = total_len ;
int headroom = ieee - > tx_headroom ;
struct ieee80211_crypt_data * crypt = ieee - > crypt [ ieee - > tx_keyidx ] ;
2005-09-21 11:54:47 -05:00
spin_lock_irqsave ( & ieee - > lock , flags ) ;
2006-06-21 11:35:08 +08:00
if ( encrypt_mpdu & & ( ! ieee - > sec . encrypt | | ! crypt ) )
2006-06-01 15:34:26 +01:00
encrypt_mpdu = 0 ;
2005-09-21 11:54:47 -05:00
/* If there is no driver handler to take the TXB, dont' bother
* creating it . . . */
if ( ! ieee - > hard_start_xmit ) {
printk ( KERN_WARNING " %s: No xmit handler. \n " , ieee - > dev - > name ) ;
goto success ;
}
2005-05-12 22:48:20 -04:00
2006-06-01 15:34:26 +01:00
if ( unlikely ( total_len < 24 ) ) {
2005-09-21 11:54:47 -05:00
printk ( KERN_WARNING " %s: skb too small (%d). \n " ,
2006-06-01 15:34:26 +01:00
ieee - > dev - > name , total_len ) ;
2005-09-21 11:54:47 -05:00
goto success ;
}
2006-06-21 11:35:08 +08:00
if ( encrypt_mpdu ) {
2006-06-01 15:34:26 +01:00
frame - > frame_ctl | = cpu_to_le16 ( IEEE80211_FCTL_PROTECTED ) ;
2006-06-21 11:35:08 +08:00
fraglen + = crypt - > ops - > extra_mpdu_prefix_len +
crypt - > ops - > extra_mpdu_postfix_len ;
headroom + = crypt - > ops - > extra_mpdu_prefix_len ;
}
2006-06-01 15:34:26 +01:00
2005-09-21 11:54:47 -05:00
/* When we allocate the TXB we allocate enough space for the reserve
* and full fragment bytes ( bytes_per_frag doesn ' t include prefix ,
* postfix , header , FCS , etc . ) */
2006-06-21 11:35:08 +08:00
txb = ieee80211_alloc_txb ( 1 , fraglen , headroom , GFP_ATOMIC ) ;
2005-09-21 11:54:47 -05:00
if ( unlikely ( ! txb ) ) {
printk ( KERN_WARNING " %s: Could not allocate TXB \n " ,
ieee - > dev - > name ) ;
goto failed ;
}
txb - > encrypted = 0 ;
2006-06-21 11:35:08 +08:00
txb - > payload_size = fraglen ;
2005-09-21 11:54:47 -05:00
skb_frag = txb - > fragments [ 0 ] ;
2006-06-01 15:34:26 +01:00
memcpy ( skb_put ( skb_frag , total_len ) , frame , total_len ) ;
2005-09-21 11:54:47 -05:00
if ( ieee - > config &
( CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS ) )
skb_put ( skb_frag , 4 ) ;
2006-06-01 15:34:26 +01:00
/* To avoid overcomplicating things, we do the corner-case frame
* encryption in software . The only real situation where encryption is
* needed here is during software - based shared key authentication . */
if ( encrypt_mpdu )
ieee80211_encrypt_fragment ( ieee , skb_frag , hdr_len ) ;
2005-09-21 11:54:47 -05:00
success :
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
if ( txb ) {
2005-09-21 11:56:33 -05:00
if ( ( * ieee - > hard_start_xmit ) ( txb , ieee - > dev , priority ) = = 0 ) {
2005-09-21 11:54:47 -05:00
stats - > tx_packets + + ;
stats - > tx_bytes + = txb - > payload_size ;
return 0 ;
}
ieee80211_txb_free ( txb ) ;
}
return 0 ;
failed :
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
stats - > tx_errors + + ;
return 1 ;
2005-05-12 22:48:20 -04:00
}
2005-09-21 11:54:47 -05:00
EXPORT_SYMBOL ( ieee80211_tx_frame ) ;
2005-05-12 22:48:20 -04:00
EXPORT_SYMBOL ( ieee80211_txb_free ) ;