2007-05-05 11:45:53 -07:00
/*
* Copyright 2002 - 2005 , Instant802 Networks , Inc .
* Copyright 2005 - 2006 , Devicescape Software , Inc .
* Copyright 2006 - 2007 Jiri Benc < jbenc @ suse . cz >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <net/mac80211.h>
# include <net/ieee80211_radiotap.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/skbuff.h>
# include <linux/etherdevice.h>
# include <linux/if_arp.h>
# include <linux/wireless.h>
# include <linux/rtnetlink.h>
# include <net/iw_handler.h>
# include <linux/compiler.h>
# include <linux/bitmap.h>
# include <net/cfg80211.h>
2007-07-10 19:32:07 +02:00
# include <asm/unaligned.h>
2007-05-05 11:45:53 -07:00
# include "ieee80211_common.h"
# include "ieee80211_i.h"
# include "ieee80211_rate.h"
# include "wep.h"
# include "wpa.h"
# include "tkip.h"
# include "wme.h"
# include "aes_ccm.h"
# include "ieee80211_led.h"
# include "ieee80211_cfg.h"
2007-05-05 11:46:38 -07:00
# include "debugfs.h"
# include "debugfs_netdev.h"
# include "debugfs_key.h"
2007-05-05 11:45:53 -07:00
/* privid for wiphys to determine whether they belong to us or not */
void * mac80211_wiphy_privid = & mac80211_wiphy_privid ;
/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
static const unsigned char rfc1042_header [ ] =
{ 0xaa , 0xaa , 0x03 , 0x00 , 0x00 , 0x00 } ;
/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
static const unsigned char bridge_tunnel_header [ ] =
{ 0xaa , 0xaa , 0x03 , 0x00 , 0x00 , 0xf8 } ;
/* No encapsulation header if EtherType < 0x600 (=length) */
static const unsigned char eapol_header [ ] =
{ 0xaa , 0xaa , 0x03 , 0x00 , 0x00 , 0x00 , 0x88 , 0x8e } ;
2007-07-10 19:32:08 +02:00
/*
* For seeing transmitted packets on monitor interfaces
* we have a radiotap header too .
*/
struct ieee80211_tx_status_rtap_hdr {
struct ieee80211_radiotap_header hdr ;
__le16 tx_flags ;
u8 data_retries ;
} __attribute__ ( ( packed ) ) ;
2007-05-05 11:45:53 -07:00
static inline void ieee80211_include_sequence ( struct ieee80211_sub_if_data * sdata ,
struct ieee80211_hdr * hdr )
{
/* Set the sequence number for this frame. */
hdr - > seq_ctrl = cpu_to_le16 ( sdata - > sequence ) ;
/* Increase the sequence number. */
sdata - > sequence = ( sdata - > sequence + 0x10 ) & IEEE80211_SCTL_SEQ ;
}
struct ieee80211_key_conf *
ieee80211_key_data2conf ( struct ieee80211_local * local ,
const struct ieee80211_key * data )
{
struct ieee80211_key_conf * conf ;
conf = kmalloc ( sizeof ( * conf ) + data - > keylen , GFP_ATOMIC ) ;
if ( ! conf )
return NULL ;
conf - > hw_key_idx = data - > hw_key_idx ;
conf - > alg = data - > alg ;
conf - > keylen = data - > keylen ;
conf - > flags = 0 ;
if ( data - > force_sw_encrypt )
conf - > flags | = IEEE80211_KEY_FORCE_SW_ENCRYPT ;
conf - > keyidx = data - > keyidx ;
if ( data - > default_tx_key )
conf - > flags | = IEEE80211_KEY_DEFAULT_TX_KEY ;
if ( local - > default_wep_only )
conf - > flags | = IEEE80211_KEY_DEFAULT_WEP_ONLY ;
memcpy ( conf - > key , data - > key , data - > keylen ) ;
return conf ;
}
struct ieee80211_key * ieee80211_key_alloc ( struct ieee80211_sub_if_data * sdata ,
int idx , size_t key_len , gfp_t flags )
{
struct ieee80211_key * key ;
key = kzalloc ( sizeof ( struct ieee80211_key ) + key_len , flags ) ;
if ( ! key )
return NULL ;
kref_init ( & key - > kref ) ;
return key ;
}
static void ieee80211_key_release ( struct kref * kref )
{
struct ieee80211_key * key ;
key = container_of ( kref , struct ieee80211_key , kref ) ;
if ( key - > alg = = ALG_CCMP )
ieee80211_aes_key_free ( key - > u . ccmp . tfm ) ;
2007-05-05 11:46:38 -07:00
ieee80211_debugfs_key_remove ( key ) ;
2007-05-05 11:45:53 -07:00
kfree ( key ) ;
}
void ieee80211_key_free ( struct ieee80211_key * key )
{
if ( key )
kref_put ( & key - > kref , ieee80211_key_release ) ;
}
static int rate_list_match ( const int * rate_list , int rate )
{
int i ;
if ( ! rate_list )
return 0 ;
for ( i = 0 ; rate_list [ i ] > = 0 ; i + + )
if ( rate_list [ i ] = = rate )
return 1 ;
return 0 ;
}
void ieee80211_prepare_rates ( struct ieee80211_local * local ,
struct ieee80211_hw_mode * mode )
{
int i ;
for ( i = 0 ; i < mode - > num_rates ; i + + ) {
struct ieee80211_rate * rate = & mode - > rates [ i ] ;
rate - > flags & = ~ ( IEEE80211_RATE_SUPPORTED |
IEEE80211_RATE_BASIC ) ;
if ( local - > supp_rates [ mode - > mode ] ) {
if ( ! rate_list_match ( local - > supp_rates [ mode - > mode ] ,
rate - > rate ) )
continue ;
}
rate - > flags | = IEEE80211_RATE_SUPPORTED ;
/* Use configured basic rate set if it is available. If not,
* use defaults that are sane for most cases . */
if ( local - > basic_rates [ mode - > mode ] ) {
if ( rate_list_match ( local - > basic_rates [ mode - > mode ] ,
rate - > rate ) )
rate - > flags | = IEEE80211_RATE_BASIC ;
} else switch ( mode - > mode ) {
case MODE_IEEE80211A :
if ( rate - > rate = = 60 | | rate - > rate = = 120 | |
rate - > rate = = 240 )
rate - > flags | = IEEE80211_RATE_BASIC ;
break ;
case MODE_IEEE80211B :
if ( rate - > rate = = 10 | | rate - > rate = = 20 )
rate - > flags | = IEEE80211_RATE_BASIC ;
break ;
case MODE_ATHEROS_TURBO :
if ( rate - > rate = = 120 | | rate - > rate = = 240 | |
rate - > rate = = 480 )
rate - > flags | = IEEE80211_RATE_BASIC ;
break ;
case MODE_IEEE80211G :
if ( rate - > rate = = 10 | | rate - > rate = = 20 | |
rate - > rate = = 55 | | rate - > rate = = 110 )
rate - > flags | = IEEE80211_RATE_BASIC ;
break ;
}
/* Set ERP and MANDATORY flags based on phymode */
switch ( mode - > mode ) {
case MODE_IEEE80211A :
if ( rate - > rate = = 60 | | rate - > rate = = 120 | |
rate - > rate = = 240 )
rate - > flags | = IEEE80211_RATE_MANDATORY ;
break ;
case MODE_IEEE80211B :
if ( rate - > rate = = 10 )
rate - > flags | = IEEE80211_RATE_MANDATORY ;
break ;
case MODE_ATHEROS_TURBO :
break ;
case MODE_IEEE80211G :
if ( rate - > rate = = 10 | | rate - > rate = = 20 | |
rate - > rate = = 55 | | rate - > rate = = 110 | |
rate - > rate = = 60 | | rate - > rate = = 120 | |
rate - > rate = = 240 )
rate - > flags | = IEEE80211_RATE_MANDATORY ;
break ;
}
if ( ieee80211_is_erp_rate ( mode - > mode , rate - > rate ) )
rate - > flags | = IEEE80211_RATE_ERP ;
}
}
static void ieee80211_key_threshold_notify ( struct net_device * dev ,
struct ieee80211_key * key ,
struct sta_info * sta )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct sk_buff * skb ;
struct ieee80211_msg_key_notification * msg ;
/* if no one will get it anyway, don't even allocate it.
* unlikely because this is only relevant for APs
* where the device must be open . . . */
if ( unlikely ( ! local - > apdev ) )
return ;
skb = dev_alloc_skb ( sizeof ( struct ieee80211_frame_info ) +
sizeof ( struct ieee80211_msg_key_notification ) ) ;
if ( ! skb )
return ;
skb_reserve ( skb , sizeof ( struct ieee80211_frame_info ) ) ;
msg = ( struct ieee80211_msg_key_notification * )
skb_put ( skb , sizeof ( struct ieee80211_msg_key_notification ) ) ;
msg - > tx_rx_count = key - > tx_rx_count ;
memcpy ( msg - > ifname , dev - > name , IFNAMSIZ ) ;
if ( sta )
memcpy ( msg - > addr , sta - > addr , ETH_ALEN ) ;
else
memset ( msg - > addr , 0xff , ETH_ALEN ) ;
key - > tx_rx_count = 0 ;
ieee80211_rx_mgmt ( local , skb , NULL ,
ieee80211_msg_key_threshold_notification ) ;
}
static u8 * ieee80211_get_bssid ( struct ieee80211_hdr * hdr , size_t len )
{
u16 fc ;
if ( len < 24 )
return NULL ;
fc = le16_to_cpu ( hdr - > frame_control ) ;
switch ( fc & IEEE80211_FCTL_FTYPE ) {
case IEEE80211_FTYPE_DATA :
switch ( fc & ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) ) {
case IEEE80211_FCTL_TODS :
return hdr - > addr1 ;
case ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) :
return NULL ;
case IEEE80211_FCTL_FROMDS :
return hdr - > addr2 ;
case 0 :
return hdr - > addr3 ;
}
break ;
case IEEE80211_FTYPE_MGMT :
return hdr - > addr3 ;
case IEEE80211_FTYPE_CTL :
if ( ( fc & IEEE80211_FCTL_STYPE ) = = IEEE80211_STYPE_PSPOLL )
return hdr - > addr1 ;
else
return NULL ;
}
return NULL ;
}
int ieee80211_get_hdrlen ( u16 fc )
{
int hdrlen = 24 ;
switch ( fc & IEEE80211_FCTL_FTYPE ) {
case IEEE80211_FTYPE_DATA :
if ( ( fc & IEEE80211_FCTL_FROMDS ) & & ( fc & IEEE80211_FCTL_TODS ) )
hdrlen = 30 ; /* Addr4 */
/*
* The QoS Control field is two bytes and its presence is
* indicated by the IEEE80211_STYPE_QOS_DATA bit . Add 2 to
* hdrlen if that bit is set .
* This works by masking out the bit and shifting it to
* bit position 1 so the result has the value 0 or 2.
*/
hdrlen + = ( fc & IEEE80211_STYPE_QOS_DATA )
> > ( ilog2 ( IEEE80211_STYPE_QOS_DATA ) - 1 ) ;
break ;
case IEEE80211_FTYPE_CTL :
/*
* ACK and CTS are 10 bytes , all others 16. To see how
* to get this condition consider
* subtype mask : 0 b0000000011110000 ( 0x00F0 )
* ACK subtype : 0 b0000000011010000 ( 0x00D0 )
* CTS subtype : 0 b0000000011000000 ( 0x00C0 )
* bits that matter : ^ ^ ^ ( 0x00E0 )
* value of those : 0 b0000000011000000 ( 0x00C0 )
*/
if ( ( fc & 0xE0 ) = = 0xC0 )
hdrlen = 10 ;
else
hdrlen = 16 ;
break ;
}
return hdrlen ;
}
EXPORT_SYMBOL ( ieee80211_get_hdrlen ) ;
int ieee80211_get_hdrlen_from_skb ( const struct sk_buff * skb )
{
const struct ieee80211_hdr * hdr = ( const struct ieee80211_hdr * ) skb - > data ;
int hdrlen ;
if ( unlikely ( skb - > len < 10 ) )
return 0 ;
hdrlen = ieee80211_get_hdrlen ( le16_to_cpu ( hdr - > frame_control ) ) ;
if ( unlikely ( hdrlen > skb - > len ) )
return 0 ;
return hdrlen ;
}
EXPORT_SYMBOL ( ieee80211_get_hdrlen_from_skb ) ;
static int ieee80211_get_radiotap_len ( struct sk_buff * skb )
{
struct ieee80211_radiotap_header * hdr =
( struct ieee80211_radiotap_header * ) skb - > data ;
return le16_to_cpu ( hdr - > it_len ) ;
}
# ifdef CONFIG_MAC80211_LOWTX_FRAME_DUMP
static void ieee80211_dump_frame ( const char * ifname , const char * title ,
const struct sk_buff * skb )
{
const struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
u16 fc ;
int hdrlen ;
printk ( KERN_DEBUG " %s: %s (len=%d) " , ifname , title , skb - > len ) ;
if ( skb - > len < 4 ) {
printk ( " \n " ) ;
return ;
}
fc = le16_to_cpu ( hdr - > frame_control ) ;
hdrlen = ieee80211_get_hdrlen ( fc ) ;
if ( hdrlen > skb - > len )
hdrlen = skb - > len ;
if ( hdrlen > = 4 )
printk ( " FC=0x%04x DUR=0x%04x " ,
fc , le16_to_cpu ( hdr - > duration_id ) ) ;
if ( hdrlen > = 10 )
printk ( " A1= " MAC_FMT , MAC_ARG ( hdr - > addr1 ) ) ;
if ( hdrlen > = 16 )
printk ( " A2= " MAC_FMT , MAC_ARG ( hdr - > addr2 ) ) ;
if ( hdrlen > = 24 )
printk ( " A3= " MAC_FMT , MAC_ARG ( hdr - > addr3 ) ) ;
if ( hdrlen > = 30 )
printk ( " A4= " MAC_FMT , MAC_ARG ( hdr - > addr4 ) ) ;
printk ( " \n " ) ;
}
# else /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
static inline void ieee80211_dump_frame ( const char * ifname , const char * title ,
struct sk_buff * skb )
{
}
# endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
static int ieee80211_is_eapol ( const struct sk_buff * skb )
{
const struct ieee80211_hdr * hdr ;
u16 fc ;
int hdrlen ;
if ( unlikely ( skb - > len < 10 ) )
return 0 ;
hdr = ( const struct ieee80211_hdr * ) skb - > data ;
fc = le16_to_cpu ( hdr - > frame_control ) ;
if ( unlikely ( ! WLAN_FC_DATA_PRESENT ( fc ) ) )
return 0 ;
hdrlen = ieee80211_get_hdrlen ( fc ) ;
if ( unlikely ( skb - > len > = hdrlen + sizeof ( eapol_header ) & &
memcmp ( skb - > data + hdrlen , eapol_header ,
sizeof ( eapol_header ) ) = = 0 ) )
return 1 ;
return 0 ;
}
static ieee80211_txrx_result
ieee80211_tx_h_rate_ctrl ( struct ieee80211_txrx_data * tx )
{
struct rate_control_extra extra ;
memset ( & extra , 0 , sizeof ( extra ) ) ;
extra . mode = tx - > u . tx . mode ;
extra . mgmt_data = tx - > sdata & &
tx - > sdata - > type = = IEEE80211_IF_TYPE_MGMT ;
extra . ethertype = tx - > ethertype ;
tx - > u . tx . rate = rate_control_get_rate ( tx - > local , tx - > dev , tx - > skb ,
& extra ) ;
if ( unlikely ( extra . probe ! = NULL ) ) {
tx - > u . tx . control - > flags | = IEEE80211_TXCTL_RATE_CTRL_PROBE ;
tx - > u . tx . probe_last_frag = 1 ;
tx - > u . tx . control - > alt_retry_rate = tx - > u . tx . rate - > val ;
tx - > u . tx . rate = extra . probe ;
} else {
tx - > u . tx . control - > alt_retry_rate = - 1 ;
}
if ( ! tx - > u . tx . rate )
return TXRX_DROP ;
if ( tx - > u . tx . mode - > mode = = MODE_IEEE80211G & &
2007-07-10 19:32:11 +02:00
tx - > sdata - > use_protection & & tx - > fragmented & &
2007-05-05 11:45:53 -07:00
extra . nonerp ) {
tx - > u . tx . last_frag_rate = tx - > u . tx . rate ;
tx - > u . tx . probe_last_frag = extra . probe ? 1 : 0 ;
tx - > u . tx . rate = extra . nonerp ;
tx - > u . tx . control - > rate = extra . nonerp ;
tx - > u . tx . control - > flags & = ~ IEEE80211_TXCTL_RATE_CTRL_PROBE ;
} else {
tx - > u . tx . last_frag_rate = tx - > u . tx . rate ;
tx - > u . tx . control - > rate = tx - > u . tx . rate ;
}
tx - > u . tx . control - > tx_rate = tx - > u . tx . rate - > val ;
if ( ( tx - > u . tx . rate - > flags & IEEE80211_RATE_PREAMBLE2 ) & &
tx - > local - > short_preamble & &
( ! tx - > sta | | ( tx - > sta - > flags & WLAN_STA_SHORT_PREAMBLE ) ) ) {
tx - > u . tx . short_preamble = 1 ;
tx - > u . tx . control - > tx_rate = tx - > u . tx . rate - > val2 ;
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_tx_h_select_key ( struct ieee80211_txrx_data * tx )
{
if ( tx - > sta )
tx - > u . tx . control - > key_idx = tx - > sta - > key_idx_compression ;
else
tx - > u . tx . control - > key_idx = HW_KEY_IDX_INVALID ;
if ( unlikely ( tx - > u . tx . control - > flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT ) )
tx - > key = NULL ;
else if ( tx - > sta & & tx - > sta - > key )
tx - > key = tx - > sta - > key ;
else if ( tx - > sdata - > default_key )
tx - > key = tx - > sdata - > default_key ;
else if ( tx - > sdata - > drop_unencrypted & &
! ( tx - > sdata - > eapol & & ieee80211_is_eapol ( tx - > skb ) ) ) {
I802_DEBUG_INC ( tx - > local - > tx_handlers_drop_unencrypted ) ;
return TXRX_DROP ;
} else
tx - > key = NULL ;
if ( tx - > key ) {
tx - > key - > tx_rx_count + + ;
if ( unlikely ( tx - > local - > key_tx_rx_threshold & &
tx - > key - > tx_rx_count >
tx - > local - > key_tx_rx_threshold ) ) {
ieee80211_key_threshold_notify ( tx - > dev , tx - > key ,
tx - > sta ) ;
}
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_tx_h_fragment ( struct ieee80211_txrx_data * tx )
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) tx - > skb - > data ;
size_t hdrlen , per_fragm , num_fragm , payload_len , left ;
struct sk_buff * * frags , * first , * frag ;
int i ;
u16 seq ;
u8 * pos ;
int frag_threshold = tx - > local - > fragmentation_threshold ;
if ( ! tx - > fragmented )
return TXRX_CONTINUE ;
first = tx - > skb ;
hdrlen = ieee80211_get_hdrlen ( tx - > fc ) ;
payload_len = first - > len - hdrlen ;
per_fragm = frag_threshold - hdrlen - FCS_LEN ;
num_fragm = ( payload_len + per_fragm - 1 ) / per_fragm ;
frags = kzalloc ( num_fragm * sizeof ( struct sk_buff * ) , GFP_ATOMIC ) ;
if ( ! frags )
goto fail ;
hdr - > frame_control | = cpu_to_le16 ( IEEE80211_FCTL_MOREFRAGS ) ;
seq = le16_to_cpu ( hdr - > seq_ctrl ) & IEEE80211_SCTL_SEQ ;
pos = first - > data + hdrlen + per_fragm ;
left = payload_len - per_fragm ;
for ( i = 0 ; i < num_fragm - 1 ; i + + ) {
struct ieee80211_hdr * fhdr ;
size_t copylen ;
if ( left < = 0 )
goto fail ;
/* reserve enough extra head and tail room for possible
* encryption */
frag = frags [ i ] =
2007-07-10 19:32:08 +02:00
dev_alloc_skb ( tx - > local - > tx_headroom +
2007-05-05 11:45:53 -07:00
frag_threshold +
IEEE80211_ENCRYPT_HEADROOM +
IEEE80211_ENCRYPT_TAILROOM ) ;
if ( ! frag )
goto fail ;
/* Make sure that all fragments use the same priority so
* that they end up using the same TX queue */
frag - > priority = first - > priority ;
2007-07-10 19:32:08 +02:00
skb_reserve ( frag , tx - > local - > tx_headroom +
IEEE80211_ENCRYPT_HEADROOM ) ;
2007-05-05 11:45:53 -07:00
fhdr = ( struct ieee80211_hdr * ) skb_put ( frag , hdrlen ) ;
memcpy ( fhdr , first - > data , hdrlen ) ;
if ( i = = num_fragm - 2 )
fhdr - > frame_control & = cpu_to_le16 ( ~ IEEE80211_FCTL_MOREFRAGS ) ;
fhdr - > seq_ctrl = cpu_to_le16 ( seq | ( ( i + 1 ) & IEEE80211_SCTL_FRAG ) ) ;
copylen = left > per_fragm ? per_fragm : left ;
memcpy ( skb_put ( frag , copylen ) , pos , copylen ) ;
pos + = copylen ;
left - = copylen ;
}
skb_trim ( first , hdrlen + per_fragm ) ;
tx - > u . tx . num_extra_frag = num_fragm - 1 ;
tx - > u . tx . extra_frag = frags ;
return TXRX_CONTINUE ;
fail :
printk ( KERN_DEBUG " %s: failed to fragment frame \n " , tx - > dev - > name ) ;
if ( frags ) {
for ( i = 0 ; i < num_fragm - 1 ; i + + )
if ( frags [ i ] )
dev_kfree_skb ( frags [ i ] ) ;
kfree ( frags ) ;
}
I802_DEBUG_INC ( tx - > local - > tx_handlers_drop_fragment ) ;
return TXRX_DROP ;
}
static int wep_encrypt_skb ( struct ieee80211_txrx_data * tx , struct sk_buff * skb )
{
if ( tx - > key - > force_sw_encrypt ) {
if ( ieee80211_wep_encrypt ( tx - > local , skb , tx - > key ) )
return - 1 ;
} else {
tx - > u . tx . control - > key_idx = tx - > key - > hw_key_idx ;
if ( tx - > local - > hw . flags & IEEE80211_HW_WEP_INCLUDE_IV ) {
if ( ieee80211_wep_add_iv ( tx - > local , skb , tx - > key ) = =
NULL )
return - 1 ;
}
}
return 0 ;
}
void ieee80211_tx_set_iswep ( struct ieee80211_txrx_data * tx )
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) tx - > skb - > data ;
hdr - > frame_control | = cpu_to_le16 ( IEEE80211_FCTL_PROTECTED ) ;
if ( tx - > u . tx . extra_frag ) {
struct ieee80211_hdr * fhdr ;
int i ;
for ( i = 0 ; i < tx - > u . tx . num_extra_frag ; i + + ) {
fhdr = ( struct ieee80211_hdr * )
tx - > u . tx . extra_frag [ i ] - > data ;
fhdr - > frame_control | = cpu_to_le16 ( IEEE80211_FCTL_PROTECTED ) ;
}
}
}
static ieee80211_txrx_result
ieee80211_tx_h_wep_encrypt ( struct ieee80211_txrx_data * tx )
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) tx - > skb - > data ;
u16 fc ;
fc = le16_to_cpu ( hdr - > frame_control ) ;
if ( ! tx - > key | | tx - > key - > alg ! = ALG_WEP | |
( ( fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA & &
( ( fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_MGMT | |
( fc & IEEE80211_FCTL_STYPE ) ! = IEEE80211_STYPE_AUTH ) ) )
return TXRX_CONTINUE ;
tx - > u . tx . control - > iv_len = WEP_IV_LEN ;
tx - > u . tx . control - > icv_len = WEP_ICV_LEN ;
ieee80211_tx_set_iswep ( tx ) ;
if ( wep_encrypt_skb ( tx , tx - > skb ) < 0 ) {
I802_DEBUG_INC ( tx - > local - > tx_handlers_drop_wep ) ;
return TXRX_DROP ;
}
if ( tx - > u . tx . extra_frag ) {
int i ;
for ( i = 0 ; i < tx - > u . tx . num_extra_frag ; i + + ) {
if ( wep_encrypt_skb ( tx , tx - > u . tx . extra_frag [ i ] ) < 0 ) {
I802_DEBUG_INC ( tx - > local - >
tx_handlers_drop_wep ) ;
return TXRX_DROP ;
}
}
}
return TXRX_CONTINUE ;
}
static int ieee80211_frame_duration ( struct ieee80211_local * local , size_t len ,
int rate , int erp , int short_preamble )
{
int dur ;
/* calculate duration (in microseconds, rounded up to next higher
* integer if it includes a fractional microsecond ) to send frame of
* len bytes ( does not include FCS ) at the given rate . Duration will
* also include SIFS .
*
* rate is in 100 kbps , so divident is multiplied by 10 in the
* DIV_ROUND_UP ( ) operations .
*/
if ( local - > hw . conf . phymode = = MODE_IEEE80211A | | erp | |
local - > hw . conf . phymode = = MODE_ATHEROS_TURBO ) {
/*
* OFDM :
*
* N_DBPS = DATARATE x 4
* N_SYM = Ceiling ( ( 16 + 8 xLENGTH + 6 ) / N_DBPS )
* ( 16 = SIGNAL time , 6 = tail bits )
* TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
*
* T_SYM = 4 usec
* 802.11 a - 17.5 .2 : aSIFSTime = 16 usec
* 802.11 g - 19.8 .4 : aSIFSTime = 10 usec +
* signal ext = 6 usec
*/
/* FIX: Atheros Turbo may have different (shorter) duration? */
dur = 16 ; /* SIFS + signal ext */
dur + = 16 ; /* 17.3.2.3: T_PREAMBLE = 16 usec */
dur + = 4 ; /* 17.3.2.3: T_SIGNAL = 4 usec */
dur + = 4 * DIV_ROUND_UP ( ( 16 + 8 * ( len + 4 ) + 6 ) * 10 ,
4 * rate ) ; /* T_SYM x N_SYM */
} else {
/*
* 802.11 b or 802.11 g with 802.11 b compatibility :
* 18.3 .4 : TXTIME = PreambleLength + PLCPHeaderTime +
* Ceiling ( ( ( LENGTH + PBCC ) x8 ) / DATARATE ) . PBCC = 0.
*
* 802.11 ( DS ) : 15.3 .3 , 802.11 b : 18.3 .4
* aSIFSTime = 10 usec
* aPreambleLength = 144 usec or 72 usec with short preamble
* aPLCPHeaderLength = 48 usec or 24 usec with short preamble
*/
dur = 10 ; /* aSIFSTime = 10 usec */
dur + = short_preamble ? ( 72 + 24 ) : ( 144 + 48 ) ;
dur + = DIV_ROUND_UP ( 8 * ( len + 4 ) * 10 , rate ) ;
}
return dur ;
}
/* Exported duration function for driver use */
__le16 ieee80211_generic_frame_duration ( struct ieee80211_hw * hw ,
size_t frame_len , int rate )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
u16 dur ;
int erp ;
erp = ieee80211_is_erp_rate ( hw - > conf . phymode , rate ) ;
dur = ieee80211_frame_duration ( local , frame_len , rate ,
erp , local - > short_preamble ) ;
return cpu_to_le16 ( dur ) ;
}
EXPORT_SYMBOL ( ieee80211_generic_frame_duration ) ;
static u16 ieee80211_duration ( struct ieee80211_txrx_data * tx , int group_addr ,
int next_frag_len )
{
int rate , mrate , erp , dur , i ;
struct ieee80211_rate * txrate = tx - > u . tx . rate ;
struct ieee80211_local * local = tx - > local ;
struct ieee80211_hw_mode * mode = tx - > u . tx . mode ;
erp = txrate - > flags & IEEE80211_RATE_ERP ;
/*
* data and mgmt ( except PS Poll ) :
* - during CFP : 32768
* - during contention period :
* if addr1 is group address : 0
* if more fragments = 0 and addr1 is individual address : time to
* transmit one ACK plus SIFS
* if more fragments = 1 and addr1 is individual address : time to
* transmit next fragment plus 2 x ACK plus 3 x SIFS
*
* IEEE 802.11 , 9.6 :
* - control response frame ( CTS or ACK ) shall be transmitted using the
* same rate as the immediately previous frame in the frame exchange
* sequence , if this rate belongs to the PHY mandatory rates , or else
* at the highest possible rate belonging to the PHY rates in the
* BSSBasicRateSet
*/
if ( ( tx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_CTL ) {
/* TODO: These control frames are not currently sent by
* 80211. o , but should they be implemented , this function
* needs to be updated to support duration field calculation .
*
* RTS : time needed to transmit pending data / mgmt frame plus
* one CTS frame plus one ACK frame plus 3 x SIFS
* CTS : duration of immediately previous RTS minus time
* required to transmit CTS and its SIFS
* ACK : 0 if immediately previous directed data / mgmt had
* more = 0 , with more = 1 duration in ACK frame is duration
* from previous frame minus time needed to transmit ACK
* and its SIFS
* PS Poll : BIT ( 15 ) | BIT ( 14 ) | aid
*/
return 0 ;
}
/* data/mgmt */
if ( 0 /* FIX: data/mgmt during CFP */ )
return 32768 ;
if ( group_addr ) /* Group address as the destination - no ACK */
return 0 ;
/* Individual destination address:
* IEEE 802.11 , Ch . 9.6 ( after IEEE 802.11 g changes )
* CTS and ACK frames shall be transmitted using the highest rate in
* basic rate set that is less than or equal to the rate of the
* immediately previous frame and that is using the same modulation
* ( CCK or OFDM ) . If no basic rate set matches with these requirements ,
* the highest mandatory rate of the PHY that is less than or equal to
* the rate of the previous frame is used .
* Mandatory rates for IEEE 802.11 g PHY : 1 , 2 , 5.5 , 11 , 6 , 12 , 24 Mbps
*/
rate = - 1 ;
mrate = 10 ; /* use 1 Mbps if everything fails */
for ( i = 0 ; i < mode - > num_rates ; i + + ) {
struct ieee80211_rate * r = & mode - > rates [ i ] ;
if ( r - > rate > txrate - > rate )
break ;
if ( IEEE80211_RATE_MODULATION ( txrate - > flags ) ! =
IEEE80211_RATE_MODULATION ( r - > flags ) )
continue ;
if ( r - > flags & IEEE80211_RATE_BASIC )
rate = r - > rate ;
else if ( r - > flags & IEEE80211_RATE_MANDATORY )
mrate = r - > rate ;
}
if ( rate = = - 1 ) {
/* No matching basic rate found; use highest suitable mandatory
* PHY rate */
rate = mrate ;
}
/* Time needed to transmit ACK
* ( 10 bytes + 4 - byte FCS = 112 bits ) plus SIFS ; rounded up
* to closest integer */
dur = ieee80211_frame_duration ( local , 10 , rate , erp ,
local - > short_preamble ) ;
if ( next_frag_len ) {
/* Frame is fragmented: duration increases with time needed to
* transmit next fragment plus ACK and 2 x SIFS . */
dur * = 2 ; /* ACK + SIFS */
/* next fragment */
dur + = ieee80211_frame_duration ( local , next_frag_len ,
txrate - > rate , erp ,
local - > short_preamble ) ;
}
return dur ;
}
static ieee80211_txrx_result
ieee80211_tx_h_misc ( struct ieee80211_txrx_data * tx )
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) tx - > skb - > data ;
u16 dur ;
struct ieee80211_tx_control * control = tx - > u . tx . control ;
struct ieee80211_hw_mode * mode = tx - > u . tx . mode ;
if ( ! is_multicast_ether_addr ( hdr - > addr1 ) ) {
if ( tx - > skb - > len + FCS_LEN > tx - > local - > rts_threshold & &
tx - > local - > rts_threshold < IEEE80211_MAX_RTS_THRESHOLD ) {
control - > flags | = IEEE80211_TXCTL_USE_RTS_CTS ;
control - > retry_limit =
tx - > local - > long_retry_limit ;
} else {
control - > retry_limit =
tx - > local - > short_retry_limit ;
}
} else {
control - > retry_limit = 1 ;
}
if ( tx - > fragmented ) {
/* Do not use multiple retry rates when sending fragmented
* frames .
* TODO : The last fragment could still use multiple retry
* rates . */
control - > alt_retry_rate = - 1 ;
}
/* Use CTS protection for unicast frames sent using extended rates if
* there are associated non - ERP stations and RTS / CTS is not configured
* for the frame . */
if ( mode - > mode = = MODE_IEEE80211G & &
( tx - > u . tx . rate - > flags & IEEE80211_RATE_ERP ) & &
2007-07-10 19:32:11 +02:00
tx - > u . tx . unicast & & tx - > sdata - > use_protection & &
2007-05-05 11:45:53 -07:00
! ( control - > flags & IEEE80211_TXCTL_USE_RTS_CTS ) )
control - > flags | = IEEE80211_TXCTL_USE_CTS_PROTECT ;
/* Setup duration field for the first fragment of the frame. Duration
* for remaining fragments will be updated when they are being sent
* to low - level driver in ieee80211_tx ( ) . */
dur = ieee80211_duration ( tx , is_multicast_ether_addr ( hdr - > addr1 ) ,
tx - > fragmented ? tx - > u . tx . extra_frag [ 0 ] - > len :
0 ) ;
hdr - > duration_id = cpu_to_le16 ( dur ) ;
if ( ( control - > flags & IEEE80211_TXCTL_USE_RTS_CTS ) | |
( control - > flags & IEEE80211_TXCTL_USE_CTS_PROTECT ) ) {
struct ieee80211_rate * rate ;
/* Do not use multiple retry rates when using RTS/CTS */
control - > alt_retry_rate = - 1 ;
/* Use min(data rate, max base rate) as CTS/RTS rate */
rate = tx - > u . tx . rate ;
while ( rate > mode - > rates & &
! ( rate - > flags & IEEE80211_RATE_BASIC ) )
rate - - ;
control - > rts_cts_rate = rate - > val ;
control - > rts_rate = rate ;
}
if ( tx - > sta ) {
tx - > sta - > tx_packets + + ;
tx - > sta - > tx_fragments + + ;
tx - > sta - > tx_bytes + = tx - > skb - > len ;
if ( tx - > u . tx . extra_frag ) {
int i ;
tx - > sta - > tx_fragments + = tx - > u . tx . num_extra_frag ;
for ( i = 0 ; i < tx - > u . tx . num_extra_frag ; i + + ) {
tx - > sta - > tx_bytes + =
tx - > u . tx . extra_frag [ i ] - > len ;
}
}
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_tx_h_check_assoc ( struct ieee80211_txrx_data * tx )
{
# ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct sk_buff * skb = tx - > skb ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
# endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
u32 sta_flags ;
if ( unlikely ( tx - > local - > sta_scanning ! = 0 ) & &
( ( tx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_MGMT | |
( tx - > fc & IEEE80211_FCTL_STYPE ) ! = IEEE80211_STYPE_PROBE_REQ ) )
return TXRX_DROP ;
if ( tx - > u . tx . ps_buffered )
return TXRX_CONTINUE ;
sta_flags = tx - > sta ? tx - > sta - > flags : 0 ;
if ( likely ( tx - > u . tx . unicast ) ) {
if ( unlikely ( ! ( sta_flags & WLAN_STA_ASSOC ) & &
tx - > sdata - > type ! = IEEE80211_IF_TYPE_IBSS & &
( tx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_DATA ) ) {
# ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk ( KERN_DEBUG " %s: dropped data frame to not "
" associated station " MAC_FMT " \n " ,
tx - > dev - > name , MAC_ARG ( hdr - > addr1 ) ) ;
# endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
I802_DEBUG_INC ( tx - > local - > tx_handlers_drop_not_assoc ) ;
return TXRX_DROP ;
}
} else {
if ( unlikely ( ( tx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_DATA & &
tx - > local - > num_sta = = 0 & &
! tx - > local - > allow_broadcast_always & &
tx - > sdata - > type ! = IEEE80211_IF_TYPE_IBSS ) ) {
/*
* No associated STAs - no need to send multicast
* frames .
*/
return TXRX_DROP ;
}
return TXRX_CONTINUE ;
}
if ( unlikely ( ! tx - > u . tx . mgmt_interface & & tx - > sdata - > ieee802_1x & &
! ( sta_flags & WLAN_STA_AUTHORIZED ) ) ) {
# ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk ( KERN_DEBUG " %s: dropped frame to " MAC_FMT
" (unauthorized port) \n " , tx - > dev - > name ,
MAC_ARG ( hdr - > addr1 ) ) ;
# endif
I802_DEBUG_INC ( tx - > local - > tx_handlers_drop_unauth_port ) ;
return TXRX_DROP ;
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_tx_h_sequence ( struct ieee80211_txrx_data * tx )
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) tx - > skb - > data ;
if ( ieee80211_get_hdrlen ( le16_to_cpu ( hdr - > frame_control ) ) > = 24 )
ieee80211_include_sequence ( tx - > sdata , hdr ) ;
return TXRX_CONTINUE ;
}
/* This function is called whenever the AP is about to exceed the maximum limit
* of buffered frames for power saving STAs . This situation should not really
* happen often during normal operation , so dropping the oldest buffered packet
* from each queue should be OK to make some room for new frames . */
static void purge_old_ps_buffers ( struct ieee80211_local * local )
{
int total = 0 , purged = 0 ;
struct sk_buff * skb ;
struct ieee80211_sub_if_data * sdata ;
struct sta_info * sta ;
read_lock ( & local - > sub_if_lock ) ;
list_for_each_entry ( sdata , & local - > sub_if_list , list ) {
struct ieee80211_if_ap * ap ;
if ( sdata - > dev = = local - > mdev | |
sdata - > type ! = IEEE80211_IF_TYPE_AP )
continue ;
ap = & sdata - > u . ap ;
skb = skb_dequeue ( & ap - > ps_bc_buf ) ;
if ( skb ) {
purged + + ;
dev_kfree_skb ( skb ) ;
}
total + = skb_queue_len ( & ap - > ps_bc_buf ) ;
}
read_unlock ( & local - > sub_if_lock ) ;
spin_lock_bh ( & local - > sta_lock ) ;
list_for_each_entry ( sta , & local - > sta_list , list ) {
skb = skb_dequeue ( & sta - > ps_tx_buf ) ;
if ( skb ) {
purged + + ;
dev_kfree_skb ( skb ) ;
}
total + = skb_queue_len ( & sta - > ps_tx_buf ) ;
}
spin_unlock_bh ( & local - > sta_lock ) ;
local - > total_ps_buffered = total ;
printk ( KERN_DEBUG " %s: PS buffers full - purged %d frames \n " ,
local - > mdev - > name , purged ) ;
}
static inline ieee80211_txrx_result
ieee80211_tx_h_multicast_ps_buf ( struct ieee80211_txrx_data * tx )
{
/* broadcast/multicast frame */
/* If any of the associated stations is in power save mode,
* the frame is buffered to be sent after DTIM beacon frame */
if ( ( tx - > local - > hw . flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING ) & &
tx - > sdata - > type ! = IEEE80211_IF_TYPE_WDS & &
tx - > sdata - > bss & & atomic_read ( & tx - > sdata - > bss - > num_sta_ps ) & &
! ( tx - > fc & IEEE80211_FCTL_ORDER ) ) {
if ( tx - > local - > total_ps_buffered > = TOTAL_MAX_TX_BUFFER )
purge_old_ps_buffers ( tx - > local ) ;
if ( skb_queue_len ( & tx - > sdata - > bss - > ps_bc_buf ) > =
AP_MAX_BC_BUFFER ) {
if ( net_ratelimit ( ) ) {
printk ( KERN_DEBUG " %s: BC TX buffer full - "
" dropping the oldest frame \n " ,
tx - > dev - > name ) ;
}
dev_kfree_skb ( skb_dequeue ( & tx - > sdata - > bss - > ps_bc_buf ) ) ;
} else
tx - > local - > total_ps_buffered + + ;
skb_queue_tail ( & tx - > sdata - > bss - > ps_bc_buf , tx - > skb ) ;
return TXRX_QUEUED ;
}
return TXRX_CONTINUE ;
}
static inline ieee80211_txrx_result
ieee80211_tx_h_unicast_ps_buf ( struct ieee80211_txrx_data * tx )
{
struct sta_info * sta = tx - > sta ;
if ( unlikely ( ! sta | |
( ( tx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_MGMT & &
( tx - > fc & IEEE80211_FCTL_STYPE ) = = IEEE80211_STYPE_PROBE_RESP ) ) )
return TXRX_CONTINUE ;
if ( unlikely ( ( sta - > flags & WLAN_STA_PS ) & & ! sta - > pspoll ) ) {
struct ieee80211_tx_packet_data * pkt_data ;
# ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk ( KERN_DEBUG " STA " MAC_FMT " aid %d: PS buffer (entries "
" before %d) \n " ,
MAC_ARG ( sta - > addr ) , sta - > aid ,
skb_queue_len ( & sta - > ps_tx_buf ) ) ;
# endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
sta - > flags | = WLAN_STA_TIM ;
if ( tx - > local - > total_ps_buffered > = TOTAL_MAX_TX_BUFFER )
purge_old_ps_buffers ( tx - > local ) ;
if ( skb_queue_len ( & sta - > ps_tx_buf ) > = STA_MAX_TX_BUFFER ) {
struct sk_buff * old = skb_dequeue ( & sta - > ps_tx_buf ) ;
if ( net_ratelimit ( ) ) {
printk ( KERN_DEBUG " %s: STA " MAC_FMT " TX "
" buffer full - dropping oldest frame \n " ,
tx - > dev - > name , MAC_ARG ( sta - > addr ) ) ;
}
dev_kfree_skb ( old ) ;
} else
tx - > local - > total_ps_buffered + + ;
/* Queue frame to be sent after STA sends an PS Poll frame */
if ( skb_queue_empty ( & sta - > ps_tx_buf ) ) {
if ( tx - > local - > ops - > set_tim )
tx - > local - > ops - > set_tim ( local_to_hw ( tx - > local ) ,
sta - > aid , 1 ) ;
if ( tx - > sdata - > bss )
bss_tim_set ( tx - > local , tx - > sdata - > bss , sta - > aid ) ;
}
pkt_data = ( struct ieee80211_tx_packet_data * ) tx - > skb - > cb ;
pkt_data - > jiffies = jiffies ;
skb_queue_tail ( & sta - > ps_tx_buf , tx - > skb ) ;
return TXRX_QUEUED ;
}
# ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
else if ( unlikely ( sta - > flags & WLAN_STA_PS ) ) {
printk ( KERN_DEBUG " %s: STA " MAC_FMT " in PS mode, but pspoll "
" set -> send frame \n " , tx - > dev - > name ,
MAC_ARG ( sta - > addr ) ) ;
}
# endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
sta - > pspoll = 0 ;
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_tx_h_ps_buf ( struct ieee80211_txrx_data * tx )
{
if ( unlikely ( tx - > u . tx . ps_buffered ) )
return TXRX_CONTINUE ;
if ( tx - > u . tx . unicast )
return ieee80211_tx_h_unicast_ps_buf ( tx ) ;
else
return ieee80211_tx_h_multicast_ps_buf ( tx ) ;
}
2007-07-10 19:32:07 +02:00
/*
* deal with packet injection down monitor interface
* with Radiotap Header - - only called for monitor mode interface
*/
static ieee80211_txrx_result
__ieee80211_parse_tx_radiotap (
struct ieee80211_txrx_data * tx ,
struct sk_buff * skb , struct ieee80211_tx_control * control )
{
/*
* this is the moment to interpret and discard the radiotap header that
* must be at the start of the packet injected in Monitor mode
*
* Need to take some care with endian - ness since radiotap
* args are little - endian
*/
struct ieee80211_radiotap_iterator iterator ;
struct ieee80211_radiotap_header * rthdr =
( struct ieee80211_radiotap_header * ) skb - > data ;
struct ieee80211_hw_mode * mode = tx - > local - > hw . conf . mode ;
int ret = ieee80211_radiotap_iterator_init ( & iterator , rthdr , skb - > len ) ;
/*
* default control situation for all injected packets
* FIXME : this does not suit all usage cases , expand to allow control
*/
control - > retry_limit = 1 ; /* no retry */
control - > key_idx = - 1 ; /* no encryption key */
control - > flags & = ~ ( IEEE80211_TXCTL_USE_RTS_CTS |
IEEE80211_TXCTL_USE_CTS_PROTECT ) ;
control - > flags | = IEEE80211_TXCTL_DO_NOT_ENCRYPT |
IEEE80211_TXCTL_NO_ACK ;
control - > antenna_sel_tx = 0 ; /* default to default antenna */
/*
* for every radiotap entry that is present
* ( ieee80211_radiotap_iterator_next returns - ENOENT when no more
* entries present , or - EINVAL on error )
*/
while ( ! ret ) {
int i , target_rate ;
ret = ieee80211_radiotap_iterator_next ( & iterator ) ;
if ( ret )
continue ;
/* see if this argument is something we can use */
switch ( iterator . this_arg_index ) {
/*
* You must take care when dereferencing iterator . this_arg
* for multibyte types . . . the pointer is not aligned . Use
* get_unaligned ( ( type * ) iterator . this_arg ) to dereference
* iterator . this_arg for type " type " safely on all arches .
*/
case IEEE80211_RADIOTAP_RATE :
/*
* radiotap rate u8 is in 500 kbps units eg , 0x02 = 1 Mbps
* ieee80211 rate int is in 100 kbps units eg , 0x0a = 1 Mbps
*/
target_rate = ( * iterator . this_arg ) * 5 ;
for ( i = 0 ; i < mode - > num_rates ; i + + ) {
struct ieee80211_rate * r = & mode - > rates [ i ] ;
if ( r - > rate > target_rate )
continue ;
control - > rate = r ;
if ( r - > flags & IEEE80211_RATE_PREAMBLE2 )
control - > tx_rate = r - > val2 ;
else
control - > tx_rate = r - > val ;
/* end on exact match */
if ( r - > rate = = target_rate )
i = mode - > num_rates ;
}
break ;
case IEEE80211_RADIOTAP_ANTENNA :
/*
* radiotap uses 0 for 1 st ant , mac80211 is 1 for
* 1 st ant
*/
control - > antenna_sel_tx = ( * iterator . this_arg ) + 1 ;
break ;
case IEEE80211_RADIOTAP_DBM_TX_POWER :
control - > power_level = * iterator . this_arg ;
break ;
case IEEE80211_RADIOTAP_FLAGS :
if ( * iterator . this_arg & IEEE80211_RADIOTAP_F_FCS ) {
/*
* this indicates that the skb we have been
* handed has the 32 - bit FCS CRC at the end . . .
* we should react to that by snipping it off
* because it will be recomputed and added
* on transmission
*/
if ( skb - > len < ( iterator . max_length + FCS_LEN ) )
return TXRX_DROP ;
skb_trim ( skb , skb - > len - FCS_LEN ) ;
}
break ;
default :
break ;
}
}
if ( ret ! = - ENOENT ) /* ie, if we didn't simply run out of fields */
return TXRX_DROP ;
/*
* remove the radiotap header
* iterator - > max_length was sanity - checked against
* skb - > len by iterator init
*/
skb_pull ( skb , iterator . max_length ) ;
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result inline
2007-05-05 11:45:53 -07:00
__ieee80211_tx_prepare ( struct ieee80211_txrx_data * tx ,
struct sk_buff * skb ,
struct net_device * dev ,
struct ieee80211_tx_control * control )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2007-07-10 19:32:07 +02:00
struct ieee80211_sub_if_data * sdata ;
ieee80211_txrx_result res = TXRX_CONTINUE ;
2007-05-05 11:45:53 -07:00
int hdrlen ;
memset ( tx , 0 , sizeof ( * tx ) ) ;
tx - > skb = skb ;
tx - > dev = dev ; /* use original interface */
tx - > local = local ;
tx - > sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
tx - > sta = sta_info_get ( local , hdr - > addr1 ) ;
tx - > fc = le16_to_cpu ( hdr - > frame_control ) ;
2007-07-10 19:32:07 +02:00
/*
* set defaults for things that can be set by
* injected radiotap headers
*/
2007-05-05 11:45:53 -07:00
control - > power_level = local - > hw . conf . power_level ;
2007-07-10 19:32:07 +02:00
control - > antenna_sel_tx = local - > hw . conf . antenna_sel_tx ;
if ( local - > sta_antenna_sel ! = STA_ANTENNA_SEL_AUTO & & tx - > sta )
control - > antenna_sel_tx = tx - > sta - > antenna_sel_tx ;
/* process and remove the injection radiotap header */
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( unlikely ( sdata - > type = = IEEE80211_IF_TYPE_MNTR ) ) {
if ( __ieee80211_parse_tx_radiotap ( tx , skb , control ) = =
TXRX_DROP ) {
return TXRX_DROP ;
}
/*
* we removed the radiotap header after this point ,
* we filled control with what we could use
* set to the actual ieee header now
*/
hdr = ( struct ieee80211_hdr * ) skb - > data ;
res = TXRX_QUEUED ; /* indication it was monitor packet */
}
2007-05-05 11:45:53 -07:00
tx - > u . tx . control = control ;
tx - > u . tx . unicast = ! is_multicast_ether_addr ( hdr - > addr1 ) ;
if ( is_multicast_ether_addr ( hdr - > addr1 ) )
control - > flags | = IEEE80211_TXCTL_NO_ACK ;
else
control - > flags & = ~ IEEE80211_TXCTL_NO_ACK ;
tx - > fragmented = local - > fragmentation_threshold <
IEEE80211_MAX_FRAG_THRESHOLD & & tx - > u . tx . unicast & &
skb - > len + FCS_LEN > local - > fragmentation_threshold & &
( ! local - > ops - > set_frag_threshold ) ;
if ( ! tx - > sta )
control - > flags | = IEEE80211_TXCTL_CLEAR_DST_MASK ;
else if ( tx - > sta - > clear_dst_mask ) {
control - > flags | = IEEE80211_TXCTL_CLEAR_DST_MASK ;
tx - > sta - > clear_dst_mask = 0 ;
}
hdrlen = ieee80211_get_hdrlen ( tx - > fc ) ;
if ( skb - > len > hdrlen + sizeof ( rfc1042_header ) + 2 ) {
u8 * pos = & skb - > data [ hdrlen + sizeof ( rfc1042_header ) ] ;
tx - > ethertype = ( pos [ 0 ] < < 8 ) | pos [ 1 ] ;
}
control - > flags | = IEEE80211_TXCTL_FIRST_FRAGMENT ;
2007-07-10 19:32:07 +02:00
return res ;
2007-05-05 11:45:53 -07:00
}
static int inline is_ieee80211_device ( struct net_device * dev ,
struct net_device * master )
{
return ( wdev_priv ( dev - > ieee80211_ptr ) = =
wdev_priv ( master - > ieee80211_ptr ) ) ;
}
/* Device in tx->dev has a reference added; use dev_put(tx->dev) when
* finished with it . */
static int inline ieee80211_tx_prepare ( struct ieee80211_txrx_data * tx ,
struct sk_buff * skb ,
struct net_device * mdev ,
struct ieee80211_tx_control * control )
{
struct ieee80211_tx_packet_data * pkt_data ;
struct net_device * dev ;
pkt_data = ( struct ieee80211_tx_packet_data * ) skb - > cb ;
dev = dev_get_by_index ( pkt_data - > ifindex ) ;
if ( unlikely ( dev & & ! is_ieee80211_device ( dev , mdev ) ) ) {
dev_put ( dev ) ;
dev = NULL ;
}
if ( unlikely ( ! dev ) )
return - ENODEV ;
__ieee80211_tx_prepare ( tx , skb , dev , control ) ;
return 0 ;
}
static inline int __ieee80211_queue_stopped ( const struct ieee80211_local * local ,
int queue )
{
return test_bit ( IEEE80211_LINK_STATE_XOFF , & local - > state [ queue ] ) ;
}
static inline int __ieee80211_queue_pending ( const struct ieee80211_local * local ,
int queue )
{
return test_bit ( IEEE80211_LINK_STATE_PENDING , & local - > state [ queue ] ) ;
}
# define IEEE80211_TX_OK 0
# define IEEE80211_TX_AGAIN 1
# define IEEE80211_TX_FRAG_AGAIN 2
static int __ieee80211_tx ( struct ieee80211_local * local , struct sk_buff * skb ,
struct ieee80211_txrx_data * tx )
{
struct ieee80211_tx_control * control = tx - > u . tx . control ;
int ret , i ;
if ( ! ieee80211_qdisc_installed ( local - > mdev ) & &
__ieee80211_queue_stopped ( local , 0 ) ) {
netif_stop_queue ( local - > mdev ) ;
return IEEE80211_TX_AGAIN ;
}
if ( skb ) {
ieee80211_dump_frame ( local - > mdev - > name , " TX to low-level driver " , skb ) ;
ret = local - > ops - > tx ( local_to_hw ( local ) , skb , control ) ;
if ( ret )
return IEEE80211_TX_AGAIN ;
local - > mdev - > trans_start = jiffies ;
ieee80211_led_tx ( local , 1 ) ;
}
if ( tx - > u . tx . extra_frag ) {
control - > flags & = ~ ( IEEE80211_TXCTL_USE_RTS_CTS |
IEEE80211_TXCTL_USE_CTS_PROTECT |
IEEE80211_TXCTL_CLEAR_DST_MASK |
IEEE80211_TXCTL_FIRST_FRAGMENT ) ;
for ( i = 0 ; i < tx - > u . tx . num_extra_frag ; i + + ) {
if ( ! tx - > u . tx . extra_frag [ i ] )
continue ;
if ( __ieee80211_queue_stopped ( local , control - > queue ) )
return IEEE80211_TX_FRAG_AGAIN ;
if ( i = = tx - > u . tx . num_extra_frag ) {
control - > tx_rate = tx - > u . tx . last_frag_hwrate ;
control - > rate = tx - > u . tx . last_frag_rate ;
if ( tx - > u . tx . probe_last_frag )
control - > flags | =
IEEE80211_TXCTL_RATE_CTRL_PROBE ;
else
control - > flags & =
~ IEEE80211_TXCTL_RATE_CTRL_PROBE ;
}
ieee80211_dump_frame ( local - > mdev - > name ,
" TX to low-level driver " ,
tx - > u . tx . extra_frag [ i ] ) ;
ret = local - > ops - > tx ( local_to_hw ( local ) ,
tx - > u . tx . extra_frag [ i ] ,
control ) ;
if ( ret )
return IEEE80211_TX_FRAG_AGAIN ;
local - > mdev - > trans_start = jiffies ;
ieee80211_led_tx ( local , 1 ) ;
tx - > u . tx . extra_frag [ i ] = NULL ;
}
kfree ( tx - > u . tx . extra_frag ) ;
tx - > u . tx . extra_frag = NULL ;
}
return IEEE80211_TX_OK ;
}
static int ieee80211_tx ( struct net_device * dev , struct sk_buff * skb ,
struct ieee80211_tx_control * control , int mgmt )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct sta_info * sta ;
ieee80211_tx_handler * handler ;
struct ieee80211_txrx_data tx ;
2007-07-10 19:32:07 +02:00
ieee80211_txrx_result res = TXRX_DROP , res_prepare ;
2007-05-05 11:45:53 -07:00
int ret , i ;
WARN_ON ( __ieee80211_queue_pending ( local , control - > queue ) ) ;
if ( unlikely ( skb - > len < 10 ) ) {
dev_kfree_skb ( skb ) ;
return 0 ;
}
2007-07-10 19:32:07 +02:00
res_prepare = __ieee80211_tx_prepare ( & tx , skb , dev , control ) ;
if ( res_prepare = = TXRX_DROP ) {
dev_kfree_skb ( skb ) ;
return 0 ;
}
2007-05-05 11:45:53 -07:00
sta = tx . sta ;
tx . u . tx . mgmt_interface = mgmt ;
tx . u . tx . mode = local - > hw . conf . mode ;
2007-07-10 19:32:07 +02:00
if ( res_prepare = = TXRX_QUEUED ) { /* if it was an injected packet */
res = TXRX_CONTINUE ;
} else {
for ( handler = local - > tx_handlers ; * handler ! = NULL ;
handler + + ) {
res = ( * handler ) ( & tx ) ;
if ( res ! = TXRX_CONTINUE )
break ;
}
2007-05-05 11:45:53 -07:00
}
skb = tx . skb ; /* handlers are allowed to change skb */
if ( sta )
sta_info_put ( sta ) ;
if ( unlikely ( res = = TXRX_DROP ) ) {
I802_DEBUG_INC ( local - > tx_handlers_drop ) ;
goto drop ;
}
if ( unlikely ( res = = TXRX_QUEUED ) ) {
I802_DEBUG_INC ( local - > tx_handlers_queued ) ;
return 0 ;
}
if ( tx . u . tx . extra_frag ) {
for ( i = 0 ; i < tx . u . tx . num_extra_frag ; i + + ) {
int next_len , dur ;
struct ieee80211_hdr * hdr =
( struct ieee80211_hdr * )
tx . u . tx . extra_frag [ i ] - > data ;
if ( i + 1 < tx . u . tx . num_extra_frag ) {
next_len = tx . u . tx . extra_frag [ i + 1 ] - > len ;
} else {
next_len = 0 ;
tx . u . tx . rate = tx . u . tx . last_frag_rate ;
tx . u . tx . last_frag_hwrate = tx . u . tx . rate - > val ;
}
dur = ieee80211_duration ( & tx , 0 , next_len ) ;
hdr - > duration_id = cpu_to_le16 ( dur ) ;
}
}
retry :
ret = __ieee80211_tx ( local , skb , & tx ) ;
if ( ret ) {
struct ieee80211_tx_stored_packet * store =
& local - > pending_packet [ control - > queue ] ;
if ( ret = = IEEE80211_TX_FRAG_AGAIN )
skb = NULL ;
set_bit ( IEEE80211_LINK_STATE_PENDING ,
& local - > state [ control - > queue ] ) ;
smp_mb ( ) ;
/* When the driver gets out of buffers during sending of
* fragments and calls ieee80211_stop_queue , there is
* a small window between IEEE80211_LINK_STATE_XOFF and
* IEEE80211_LINK_STATE_PENDING flags are set . If a buffer
* gets available in that window ( i . e . driver calls
* ieee80211_wake_queue ) , we would end up with ieee80211_tx
* called with IEEE80211_LINK_STATE_PENDING . Prevent this by
* continuing transmitting here when that situation is
* possible to have happened . */
if ( ! __ieee80211_queue_stopped ( local , control - > queue ) ) {
clear_bit ( IEEE80211_LINK_STATE_PENDING ,
& local - > state [ control - > queue ] ) ;
goto retry ;
}
memcpy ( & store - > control , control ,
sizeof ( struct ieee80211_tx_control ) ) ;
store - > skb = skb ;
store - > extra_frag = tx . u . tx . extra_frag ;
store - > num_extra_frag = tx . u . tx . num_extra_frag ;
store - > last_frag_hwrate = tx . u . tx . last_frag_hwrate ;
store - > last_frag_rate = tx . u . tx . last_frag_rate ;
store - > last_frag_rate_ctrl_probe = tx . u . tx . probe_last_frag ;
}
return 0 ;
drop :
if ( skb )
dev_kfree_skb ( skb ) ;
for ( i = 0 ; i < tx . u . tx . num_extra_frag ; i + + )
if ( tx . u . tx . extra_frag [ i ] )
dev_kfree_skb ( tx . u . tx . extra_frag [ i ] ) ;
kfree ( tx . u . tx . extra_frag ) ;
return 0 ;
}
static void ieee80211_tx_pending ( unsigned long data )
{
struct ieee80211_local * local = ( struct ieee80211_local * ) data ;
struct net_device * dev = local - > mdev ;
struct ieee80211_tx_stored_packet * store ;
struct ieee80211_txrx_data tx ;
int i , ret , reschedule = 0 ;
netif_tx_lock_bh ( dev ) ;
for ( i = 0 ; i < local - > hw . queues ; i + + ) {
if ( __ieee80211_queue_stopped ( local , i ) )
continue ;
if ( ! __ieee80211_queue_pending ( local , i ) ) {
reschedule = 1 ;
continue ;
}
store = & local - > pending_packet [ i ] ;
tx . u . tx . control = & store - > control ;
tx . u . tx . extra_frag = store - > extra_frag ;
tx . u . tx . num_extra_frag = store - > num_extra_frag ;
tx . u . tx . last_frag_hwrate = store - > last_frag_hwrate ;
tx . u . tx . last_frag_rate = store - > last_frag_rate ;
tx . u . tx . probe_last_frag = store - > last_frag_rate_ctrl_probe ;
ret = __ieee80211_tx ( local , store - > skb , & tx ) ;
if ( ret ) {
if ( ret = = IEEE80211_TX_FRAG_AGAIN )
store - > skb = NULL ;
} else {
clear_bit ( IEEE80211_LINK_STATE_PENDING ,
& local - > state [ i ] ) ;
reschedule = 1 ;
}
}
netif_tx_unlock_bh ( dev ) ;
if ( reschedule ) {
if ( ! ieee80211_qdisc_installed ( dev ) ) {
if ( ! __ieee80211_queue_stopped ( local , 0 ) )
netif_wake_queue ( dev ) ;
} else
netif_schedule ( dev ) ;
}
}
static void ieee80211_clear_tx_pending ( struct ieee80211_local * local )
{
int i , j ;
struct ieee80211_tx_stored_packet * store ;
for ( i = 0 ; i < local - > hw . queues ; i + + ) {
if ( ! __ieee80211_queue_pending ( local , i ) )
continue ;
store = & local - > pending_packet [ i ] ;
kfree_skb ( store - > skb ) ;
for ( j = 0 ; j < store - > num_extra_frag ; j + + )
kfree_skb ( store - > extra_frag [ j ] ) ;
kfree ( store - > extra_frag ) ;
clear_bit ( IEEE80211_LINK_STATE_PENDING , & local - > state [ i ] ) ;
}
}
static int ieee80211_master_start_xmit ( struct sk_buff * skb ,
struct net_device * dev )
{
struct ieee80211_tx_control control ;
struct ieee80211_tx_packet_data * pkt_data ;
struct net_device * odev = NULL ;
struct ieee80211_sub_if_data * osdata ;
int headroom ;
int ret ;
/*
* copy control out of the skb so other people can use skb - > cb
*/
pkt_data = ( struct ieee80211_tx_packet_data * ) skb - > cb ;
memset ( & control , 0 , sizeof ( struct ieee80211_tx_control ) ) ;
if ( pkt_data - > ifindex )
odev = dev_get_by_index ( pkt_data - > ifindex ) ;
if ( unlikely ( odev & & ! is_ieee80211_device ( odev , dev ) ) ) {
dev_put ( odev ) ;
odev = NULL ;
}
if ( unlikely ( ! odev ) ) {
# ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk ( KERN_DEBUG " %s: Discarded packet with nonexistent "
" originating device \n " , dev - > name ) ;
# endif
dev_kfree_skb ( skb ) ;
return 0 ;
}
osdata = IEEE80211_DEV_TO_SUB_IF ( odev ) ;
2007-07-10 19:32:08 +02:00
headroom = osdata - > local - > tx_headroom + IEEE80211_ENCRYPT_HEADROOM ;
2007-05-05 11:45:53 -07:00
if ( skb_headroom ( skb ) < headroom ) {
if ( pskb_expand_head ( skb , headroom , 0 , GFP_ATOMIC ) ) {
dev_kfree_skb ( skb ) ;
2007-07-27 15:43:21 +02:00
dev_put ( odev ) ;
2007-05-05 11:45:53 -07:00
return 0 ;
}
}
control . ifindex = odev - > ifindex ;
control . type = osdata - > type ;
if ( pkt_data - > req_tx_status )
control . flags | = IEEE80211_TXCTL_REQ_TX_STATUS ;
if ( pkt_data - > do_not_encrypt )
control . flags | = IEEE80211_TXCTL_DO_NOT_ENCRYPT ;
if ( pkt_data - > requeue )
control . flags | = IEEE80211_TXCTL_REQUEUE ;
control . queue = pkt_data - > queue ;
ret = ieee80211_tx ( odev , skb , & control ,
control . type = = IEEE80211_IF_TYPE_MGMT ) ;
dev_put ( odev ) ;
return ret ;
}
2007-07-10 19:32:08 +02:00
int ieee80211_monitor_start_xmit ( struct sk_buff * skb ,
struct net_device * dev )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_tx_packet_data * pkt_data ;
struct ieee80211_radiotap_header * prthdr =
( struct ieee80211_radiotap_header * ) skb - > data ;
u16 len ;
/*
* there must be a radiotap header at the
* start in this case
*/
if ( unlikely ( prthdr - > it_version ) ) {
/* only version 0 is supported */
dev_kfree_skb ( skb ) ;
return NETDEV_TX_OK ;
}
skb - > dev = local - > mdev ;
pkt_data = ( struct ieee80211_tx_packet_data * ) skb - > cb ;
memset ( pkt_data , 0 , sizeof ( * pkt_data ) ) ;
pkt_data - > ifindex = dev - > ifindex ;
pkt_data - > mgmt_iface = 0 ;
pkt_data - > do_not_encrypt = 1 ;
/* above needed because we set skb device to master */
/*
* fix up the pointers accounting for the radiotap
* header still being in there . We are being given
* a precooked IEEE80211 header so no need for
* normal processing
*/
len = le16_to_cpu ( get_unaligned ( & prthdr - > it_len ) ) ;
skb_set_mac_header ( skb , len ) ;
skb_set_network_header ( skb , len + sizeof ( struct ieee80211_hdr ) ) ;
skb_set_transport_header ( skb , len + sizeof ( struct ieee80211_hdr ) ) ;
/*
* pass the radiotap header up to
* the next stage intact
*/
dev_queue_xmit ( skb ) ;
return NETDEV_TX_OK ;
}
2007-05-05 11:45:53 -07:00
/**
* ieee80211_subif_start_xmit - netif start_xmit function for Ethernet - type
* subinterfaces ( wlan # , WDS , and VLAN interfaces )
* @ skb : packet to be sent
* @ dev : incoming interface
*
* Returns : 0 on success ( and frees skb in this case ) or 1 on failure ( skb will
* not be freed , and caller is responsible for either retrying later or freeing
* skb ) .
*
* This function takes in an Ethernet header and encapsulates it with suitable
* IEEE 802.11 header based on which interface the packet is coming in . The
* encapsulated packet will then be passed to master interface , wlan # .11 , for
* transmission ( through low - level driver ) .
*/
2007-07-10 19:32:08 +02:00
int ieee80211_subif_start_xmit ( struct sk_buff * skb ,
struct net_device * dev )
2007-05-05 11:45:53 -07:00
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_tx_packet_data * pkt_data ;
struct ieee80211_sub_if_data * sdata ;
int ret = 1 , head_need ;
u16 ethertype , hdrlen , fc ;
struct ieee80211_hdr hdr ;
const u8 * encaps_data ;
int encaps_len , skip_header_bytes ;
int nh_pos , h_pos , no_encrypt = 0 ;
struct sta_info * sta ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( unlikely ( skb - > len < ETH_HLEN ) ) {
printk ( KERN_DEBUG " %s: short skb (len=%d) \n " ,
dev - > name , skb - > len ) ;
ret = 0 ;
goto fail ;
}
nh_pos = skb_network_header ( skb ) - skb - > data ;
h_pos = skb_transport_header ( skb ) - skb - > data ;
/* convert Ethernet header to proper 802.11 header (based on
* operation mode ) */
ethertype = ( skb - > data [ 12 ] < < 8 ) | skb - > data [ 13 ] ;
/* TODO: handling for 802.1x authorized/unauthorized port */
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA ;
if ( likely ( sdata - > type = = IEEE80211_IF_TYPE_AP | |
sdata - > type = = IEEE80211_IF_TYPE_VLAN ) ) {
fc | = IEEE80211_FCTL_FROMDS ;
/* DA BSSID SA */
memcpy ( hdr . addr1 , skb - > data , ETH_ALEN ) ;
memcpy ( hdr . addr2 , dev - > dev_addr , ETH_ALEN ) ;
memcpy ( hdr . addr3 , skb - > data + ETH_ALEN , ETH_ALEN ) ;
hdrlen = 24 ;
} else if ( sdata - > type = = IEEE80211_IF_TYPE_WDS ) {
fc | = IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS ;
/* RA TA DA SA */
memcpy ( hdr . addr1 , sdata - > u . wds . remote_addr , ETH_ALEN ) ;
memcpy ( hdr . addr2 , dev - > dev_addr , ETH_ALEN ) ;
memcpy ( hdr . addr3 , skb - > data , ETH_ALEN ) ;
memcpy ( hdr . addr4 , skb - > data + ETH_ALEN , ETH_ALEN ) ;
hdrlen = 30 ;
} else if ( sdata - > type = = IEEE80211_IF_TYPE_STA ) {
fc | = IEEE80211_FCTL_TODS ;
/* BSSID SA DA */
memcpy ( hdr . addr1 , sdata - > u . sta . bssid , ETH_ALEN ) ;
memcpy ( hdr . addr2 , skb - > data + ETH_ALEN , ETH_ALEN ) ;
memcpy ( hdr . addr3 , skb - > data , ETH_ALEN ) ;
hdrlen = 24 ;
} else if ( sdata - > type = = IEEE80211_IF_TYPE_IBSS ) {
/* DA SA BSSID */
memcpy ( hdr . addr1 , skb - > data , ETH_ALEN ) ;
memcpy ( hdr . addr2 , skb - > data + ETH_ALEN , ETH_ALEN ) ;
memcpy ( hdr . addr3 , sdata - > u . sta . bssid , ETH_ALEN ) ;
hdrlen = 24 ;
} else {
ret = 0 ;
goto fail ;
}
/* receiver is QoS enabled, use a QoS type frame */
sta = sta_info_get ( local , hdr . addr1 ) ;
if ( sta ) {
if ( sta - > flags & WLAN_STA_WME ) {
fc | = IEEE80211_STYPE_QOS_DATA ;
hdrlen + = 2 ;
}
sta_info_put ( sta ) ;
}
hdr . frame_control = cpu_to_le16 ( fc ) ;
hdr . duration_id = 0 ;
hdr . seq_ctrl = 0 ;
skip_header_bytes = ETH_HLEN ;
if ( ethertype = = ETH_P_AARP | | ethertype = = ETH_P_IPX ) {
encaps_data = bridge_tunnel_header ;
encaps_len = sizeof ( bridge_tunnel_header ) ;
skip_header_bytes - = 2 ;
} else if ( ethertype > = 0x600 ) {
encaps_data = rfc1042_header ;
encaps_len = sizeof ( rfc1042_header ) ;
skip_header_bytes - = 2 ;
} else {
encaps_data = NULL ;
encaps_len = 0 ;
}
skb_pull ( skb , skip_header_bytes ) ;
nh_pos - = skip_header_bytes ;
h_pos - = skip_header_bytes ;
/* TODO: implement support for fragments so that there is no need to
* reallocate and copy payload ; it might be enough to support one
* extra fragment that would be copied in the beginning of the frame
* data . . anyway , it would be nice to include this into skb structure
* somehow
*
* There are few options for this :
* use skb - > cb as an extra space for 802.11 header
* allocate new buffer if not enough headroom
* make sure that there is enough headroom in every skb by increasing
* build in headroom in __dev_alloc_skb ( ) ( linux / skbuff . h ) and
* alloc_skb ( ) ( net / core / skbuff . c )
*/
2007-07-10 19:32:08 +02:00
head_need = hdrlen + encaps_len + local - > tx_headroom ;
2007-05-05 11:45:53 -07:00
head_need - = skb_headroom ( skb ) ;
/* We are going to modify skb data, so make a copy of it if happens to
* be cloned . This could happen , e . g . , with Linux bridge code passing
* us broadcast frames . */
if ( head_need > 0 | | skb_cloned ( skb ) ) {
#if 0
printk ( KERN_DEBUG " %s: need to reallocate buffer for %d bytes "
" of headroom \n " , dev - > name , head_need ) ;
# endif
if ( skb_cloned ( skb ) )
I802_DEBUG_INC ( local - > tx_expand_skb_head_cloned ) ;
else
I802_DEBUG_INC ( local - > tx_expand_skb_head ) ;
/* Since we have to reallocate the buffer, make sure that there
* is enough room for possible WEP IV / ICV and TKIP ( 8 bytes
* before payload and 12 after ) . */
if ( pskb_expand_head ( skb , ( head_need > 0 ? head_need + 8 : 8 ) ,
12 , GFP_ATOMIC ) ) {
printk ( KERN_DEBUG " %s: failed to reallocate TX buffer "
" \n " , dev - > name ) ;
goto fail ;
}
}
if ( encaps_data ) {
memcpy ( skb_push ( skb , encaps_len ) , encaps_data , encaps_len ) ;
nh_pos + = encaps_len ;
h_pos + = encaps_len ;
}
memcpy ( skb_push ( skb , hdrlen ) , & hdr , hdrlen ) ;
nh_pos + = hdrlen ;
h_pos + = hdrlen ;
pkt_data = ( struct ieee80211_tx_packet_data * ) skb - > cb ;
memset ( pkt_data , 0 , sizeof ( struct ieee80211_tx_packet_data ) ) ;
2007-07-10 19:32:08 +02:00
pkt_data - > ifindex = dev - > ifindex ;
2007-05-05 11:45:53 -07:00
pkt_data - > mgmt_iface = ( sdata - > type = = IEEE80211_IF_TYPE_MGMT ) ;
pkt_data - > do_not_encrypt = no_encrypt ;
skb - > dev = local - > mdev ;
sdata - > stats . tx_packets + + ;
sdata - > stats . tx_bytes + = skb - > len ;
/* Update skb pointers to various headers since this modified frame
* is going to go through Linux networking code that may potentially
* need things like pointer to IP header . */
skb_set_mac_header ( skb , 0 ) ;
skb_set_network_header ( skb , nh_pos ) ;
skb_set_transport_header ( skb , h_pos ) ;
dev - > trans_start = jiffies ;
dev_queue_xmit ( skb ) ;
return 0 ;
fail :
if ( ! ret )
dev_kfree_skb ( skb ) ;
return ret ;
}
/*
* This is the transmit routine for the 802.11 type interfaces
* called by upper layers of the linux networking
* stack when it has a frame to transmit
*/
static int
ieee80211_mgmt_start_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct ieee80211_sub_if_data * sdata ;
struct ieee80211_tx_packet_data * pkt_data ;
struct ieee80211_hdr * hdr ;
u16 fc ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( skb - > len < 10 ) {
dev_kfree_skb ( skb ) ;
return 0 ;
}
2007-07-10 19:32:08 +02:00
if ( skb_headroom ( skb ) < sdata - > local - > tx_headroom ) {
if ( pskb_expand_head ( skb , sdata - > local - > tx_headroom ,
0 , GFP_ATOMIC ) ) {
2007-05-05 11:45:53 -07:00
dev_kfree_skb ( skb ) ;
return 0 ;
}
}
hdr = ( struct ieee80211_hdr * ) skb - > data ;
fc = le16_to_cpu ( hdr - > frame_control ) ;
pkt_data = ( struct ieee80211_tx_packet_data * ) skb - > cb ;
memset ( pkt_data , 0 , sizeof ( struct ieee80211_tx_packet_data ) ) ;
pkt_data - > ifindex = sdata - > dev - > ifindex ;
pkt_data - > mgmt_iface = ( sdata - > type = = IEEE80211_IF_TYPE_MGMT ) ;
skb - > priority = 20 ; /* use hardcoded priority for mgmt TX queue */
skb - > dev = sdata - > local - > mdev ;
/*
* We ' re using the protocol field of the the frame control header
* to request TX callback for hostapd . BIT ( 1 ) is checked .
*/
if ( ( fc & BIT ( 1 ) ) = = BIT ( 1 ) ) {
pkt_data - > req_tx_status = 1 ;
fc & = ~ BIT ( 1 ) ;
hdr - > frame_control = cpu_to_le16 ( fc ) ;
}
pkt_data - > do_not_encrypt = ! ( fc & IEEE80211_FCTL_PROTECTED ) ;
sdata - > stats . tx_packets + + ;
sdata - > stats . tx_bytes + = skb - > len ;
dev_queue_xmit ( skb ) ;
return 0 ;
}
static void ieee80211_beacon_add_tim ( struct ieee80211_local * local ,
struct ieee80211_if_ap * bss ,
struct sk_buff * skb )
{
u8 * pos , * tim ;
int aid0 = 0 ;
int i , have_bits = 0 , n1 , n2 ;
/* Generate bitmap for TIM only if there are any STAs in power save
* mode . */
spin_lock_bh ( & local - > sta_lock ) ;
if ( atomic_read ( & bss - > num_sta_ps ) > 0 )
/* in the hope that this is faster than
* checking byte - for - byte */
have_bits = ! bitmap_empty ( ( unsigned long * ) bss - > tim ,
IEEE80211_MAX_AID + 1 ) ;
if ( bss - > dtim_count = = 0 )
bss - > dtim_count = bss - > dtim_period - 1 ;
else
bss - > dtim_count - - ;
tim = pos = ( u8 * ) skb_put ( skb , 6 ) ;
* pos + + = WLAN_EID_TIM ;
* pos + + = 4 ;
* pos + + = bss - > dtim_count ;
* pos + + = bss - > dtim_period ;
if ( bss - > dtim_count = = 0 & & ! skb_queue_empty ( & bss - > ps_bc_buf ) )
aid0 = 1 ;
if ( have_bits ) {
/* Find largest even number N1 so that bits numbered 1 through
* ( N1 x 8 ) - 1 in the bitmap are 0 and number N2 so that bits
* ( N2 + 1 ) x 8 through 2007 are 0. */
n1 = 0 ;
for ( i = 0 ; i < IEEE80211_MAX_TIM_LEN ; i + + ) {
if ( bss - > tim [ i ] ) {
n1 = i & 0xfe ;
break ;
}
}
n2 = n1 ;
for ( i = IEEE80211_MAX_TIM_LEN - 1 ; i > = n1 ; i - - ) {
if ( bss - > tim [ i ] ) {
n2 = i ;
break ;
}
}
/* Bitmap control */
* pos + + = n1 | aid0 ;
/* Part Virt Bitmap */
memcpy ( pos , bss - > tim + n1 , n2 - n1 + 1 ) ;
tim [ 1 ] = n2 - n1 + 4 ;
skb_put ( skb , n2 - n1 ) ;
} else {
* pos + + = aid0 ; /* Bitmap control */
* pos + + = 0 ; /* Part Virt Bitmap */
}
spin_unlock_bh ( & local - > sta_lock ) ;
}
struct sk_buff * ieee80211_beacon_get ( struct ieee80211_hw * hw , int if_id ,
struct ieee80211_tx_control * control )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct sk_buff * skb ;
struct net_device * bdev ;
struct ieee80211_sub_if_data * sdata = NULL ;
struct ieee80211_if_ap * ap = NULL ;
struct ieee80211_rate * rate ;
struct rate_control_extra extra ;
u8 * b_head , * b_tail ;
int bh_len , bt_len ;
bdev = dev_get_by_index ( if_id ) ;
if ( bdev ) {
sdata = IEEE80211_DEV_TO_SUB_IF ( bdev ) ;
ap = & sdata - > u . ap ;
dev_put ( bdev ) ;
}
if ( ! ap | | sdata - > type ! = IEEE80211_IF_TYPE_AP | |
! ap - > beacon_head ) {
# ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " no beacon data avail for idx=%d "
" (%s) \n " , if_id , bdev ? bdev - > name : " N/A " ) ;
# endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
return NULL ;
}
/* Assume we are generating the normal beacon locally */
b_head = ap - > beacon_head ;
b_tail = ap - > beacon_tail ;
bh_len = ap - > beacon_head_len ;
bt_len = ap - > beacon_tail_len ;
2007-07-10 19:32:08 +02:00
skb = dev_alloc_skb ( local - > tx_headroom +
2007-05-05 11:45:53 -07:00
bh_len + bt_len + 256 /* maximum TIM len */ ) ;
if ( ! skb )
return NULL ;
2007-07-10 19:32:08 +02:00
skb_reserve ( skb , local - > tx_headroom ) ;
2007-05-05 11:45:53 -07:00
memcpy ( skb_put ( skb , bh_len ) , b_head , bh_len ) ;
ieee80211_include_sequence ( sdata , ( struct ieee80211_hdr * ) skb - > data ) ;
ieee80211_beacon_add_tim ( local , ap , skb ) ;
if ( b_tail ) {
memcpy ( skb_put ( skb , bt_len ) , b_tail , bt_len ) ;
}
if ( control ) {
memset ( & extra , 0 , sizeof ( extra ) ) ;
extra . mode = local - > oper_hw_mode ;
rate = rate_control_get_rate ( local , local - > mdev , skb , & extra ) ;
if ( ! rate ) {
if ( net_ratelimit ( ) ) {
printk ( KERN_DEBUG " %s: ieee80211_beacon_get: no rate "
" found \n " , local - > mdev - > name ) ;
}
dev_kfree_skb ( skb ) ;
return NULL ;
}
control - > tx_rate = ( local - > short_preamble & &
( rate - > flags & IEEE80211_RATE_PREAMBLE2 ) ) ?
rate - > val2 : rate - > val ;
control - > antenna_sel_tx = local - > hw . conf . antenna_sel_tx ;
control - > power_level = local - > hw . conf . power_level ;
control - > flags | = IEEE80211_TXCTL_NO_ACK ;
control - > retry_limit = 1 ;
control - > flags | = IEEE80211_TXCTL_CLEAR_DST_MASK ;
}
ap - > num_beacons + + ;
return skb ;
}
EXPORT_SYMBOL ( ieee80211_beacon_get ) ;
__le16 ieee80211_rts_duration ( struct ieee80211_hw * hw ,
size_t frame_len ,
const struct ieee80211_tx_control * frame_txctl )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct ieee80211_rate * rate ;
int short_preamble = local - > short_preamble ;
int erp ;
u16 dur ;
rate = frame_txctl - > rts_rate ;
erp = ! ! ( rate - > flags & IEEE80211_RATE_ERP ) ;
/* CTS duration */
dur = ieee80211_frame_duration ( local , 10 , rate - > rate ,
erp , short_preamble ) ;
/* Data frame duration */
dur + = ieee80211_frame_duration ( local , frame_len , rate - > rate ,
erp , short_preamble ) ;
/* ACK duration */
dur + = ieee80211_frame_duration ( local , 10 , rate - > rate ,
erp , short_preamble ) ;
return cpu_to_le16 ( dur ) ;
}
EXPORT_SYMBOL ( ieee80211_rts_duration ) ;
__le16 ieee80211_ctstoself_duration ( struct ieee80211_hw * hw ,
size_t frame_len ,
const struct ieee80211_tx_control * frame_txctl )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct ieee80211_rate * rate ;
int short_preamble = local - > short_preamble ;
int erp ;
u16 dur ;
rate = frame_txctl - > rts_rate ;
erp = ! ! ( rate - > flags & IEEE80211_RATE_ERP ) ;
/* Data frame duration */
dur = ieee80211_frame_duration ( local , frame_len , rate - > rate ,
erp , short_preamble ) ;
if ( ! ( frame_txctl - > flags & IEEE80211_TXCTL_NO_ACK ) ) {
/* ACK duration */
dur + = ieee80211_frame_duration ( local , 10 , rate - > rate ,
erp , short_preamble ) ;
}
return cpu_to_le16 ( dur ) ;
}
EXPORT_SYMBOL ( ieee80211_ctstoself_duration ) ;
void ieee80211_rts_get ( struct ieee80211_hw * hw ,
const void * frame , size_t frame_len ,
const struct ieee80211_tx_control * frame_txctl ,
struct ieee80211_rts * rts )
{
const struct ieee80211_hdr * hdr = frame ;
u16 fctl ;
fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS ;
rts - > frame_control = cpu_to_le16 ( fctl ) ;
rts - > duration = ieee80211_rts_duration ( hw , frame_len , frame_txctl ) ;
memcpy ( rts - > ra , hdr - > addr1 , sizeof ( rts - > ra ) ) ;
memcpy ( rts - > ta , hdr - > addr2 , sizeof ( rts - > ta ) ) ;
}
EXPORT_SYMBOL ( ieee80211_rts_get ) ;
void ieee80211_ctstoself_get ( struct ieee80211_hw * hw ,
const void * frame , size_t frame_len ,
const struct ieee80211_tx_control * frame_txctl ,
struct ieee80211_cts * cts )
{
const struct ieee80211_hdr * hdr = frame ;
u16 fctl ;
fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS ;
cts - > frame_control = cpu_to_le16 ( fctl ) ;
cts - > duration = ieee80211_ctstoself_duration ( hw , frame_len , frame_txctl ) ;
memcpy ( cts - > ra , hdr - > addr1 , sizeof ( cts - > ra ) ) ;
}
EXPORT_SYMBOL ( ieee80211_ctstoself_get ) ;
struct sk_buff *
ieee80211_get_buffered_bc ( struct ieee80211_hw * hw , int if_id ,
struct ieee80211_tx_control * control )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct sk_buff * skb ;
struct sta_info * sta ;
ieee80211_tx_handler * handler ;
struct ieee80211_txrx_data tx ;
ieee80211_txrx_result res = TXRX_DROP ;
struct net_device * bdev ;
struct ieee80211_sub_if_data * sdata ;
struct ieee80211_if_ap * bss = NULL ;
bdev = dev_get_by_index ( if_id ) ;
if ( bdev ) {
sdata = IEEE80211_DEV_TO_SUB_IF ( bdev ) ;
bss = & sdata - > u . ap ;
dev_put ( bdev ) ;
}
if ( ! bss | | sdata - > type ! = IEEE80211_IF_TYPE_AP | | ! bss - > beacon_head )
return NULL ;
if ( bss - > dtim_count ! = 0 )
return NULL ; /* send buffered bc/mc only after DTIM beacon */
memset ( control , 0 , sizeof ( * control ) ) ;
while ( 1 ) {
skb = skb_dequeue ( & bss - > ps_bc_buf ) ;
if ( ! skb )
return NULL ;
local - > total_ps_buffered - - ;
if ( ! skb_queue_empty ( & bss - > ps_bc_buf ) & & skb - > len > = 2 ) {
struct ieee80211_hdr * hdr =
( struct ieee80211_hdr * ) skb - > data ;
/* more buffered multicast/broadcast frames ==> set
* MoreData flag in IEEE 802.11 header to inform PS
* STAs */
hdr - > frame_control | =
cpu_to_le16 ( IEEE80211_FCTL_MOREDATA ) ;
}
if ( ieee80211_tx_prepare ( & tx , skb , local - > mdev , control ) = = 0 )
break ;
dev_kfree_skb_any ( skb ) ;
}
sta = tx . sta ;
tx . u . tx . ps_buffered = 1 ;
for ( handler = local - > tx_handlers ; * handler ! = NULL ; handler + + ) {
res = ( * handler ) ( & tx ) ;
if ( res = = TXRX_DROP | | res = = TXRX_QUEUED )
break ;
}
dev_put ( tx . dev ) ;
skb = tx . skb ; /* handlers are allowed to change skb */
if ( res = = TXRX_DROP ) {
I802_DEBUG_INC ( local - > tx_handlers_drop ) ;
dev_kfree_skb ( skb ) ;
skb = NULL ;
} else if ( res = = TXRX_QUEUED ) {
I802_DEBUG_INC ( local - > tx_handlers_queued ) ;
skb = NULL ;
}
if ( sta )
sta_info_put ( sta ) ;
return skb ;
}
EXPORT_SYMBOL ( ieee80211_get_buffered_bc ) ;
static int __ieee80211_if_config ( struct net_device * dev ,
struct sk_buff * beacon ,
struct ieee80211_tx_control * control )
{
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_if_conf conf ;
static u8 scan_bssid [ ] = { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ;
if ( ! local - > ops - > config_interface | | ! netif_running ( dev ) )
return 0 ;
memset ( & conf , 0 , sizeof ( conf ) ) ;
conf . type = sdata - > type ;
if ( sdata - > type = = IEEE80211_IF_TYPE_STA | |
sdata - > type = = IEEE80211_IF_TYPE_IBSS ) {
if ( local - > sta_scanning & &
local - > scan_dev = = dev )
conf . bssid = scan_bssid ;
else
conf . bssid = sdata - > u . sta . bssid ;
conf . ssid = sdata - > u . sta . ssid ;
conf . ssid_len = sdata - > u . sta . ssid_len ;
conf . generic_elem = sdata - > u . sta . extra_ie ;
conf . generic_elem_len = sdata - > u . sta . extra_ie_len ;
} else if ( sdata - > type = = IEEE80211_IF_TYPE_AP ) {
conf . ssid = sdata - > u . ap . ssid ;
conf . ssid_len = sdata - > u . ap . ssid_len ;
conf . generic_elem = sdata - > u . ap . generic_elem ;
conf . generic_elem_len = sdata - > u . ap . generic_elem_len ;
conf . beacon = beacon ;
conf . beacon_control = control ;
}
return local - > ops - > config_interface ( local_to_hw ( local ) ,
dev - > ifindex , & conf ) ;
}
int ieee80211_if_config ( struct net_device * dev )
{
return __ieee80211_if_config ( dev , NULL , NULL ) ;
}
int ieee80211_if_config_beacon ( struct net_device * dev )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_tx_control control ;
struct sk_buff * skb ;
if ( ! ( local - > hw . flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE ) )
return 0 ;
skb = ieee80211_beacon_get ( local_to_hw ( local ) , dev - > ifindex , & control ) ;
if ( ! skb )
return - ENOMEM ;
return __ieee80211_if_config ( dev , skb , & control ) ;
}
int ieee80211_hw_config ( struct ieee80211_local * local )
{
struct ieee80211_hw_mode * mode ;
struct ieee80211_channel * chan ;
int ret = 0 ;
if ( local - > sta_scanning ) {
chan = local - > scan_channel ;
mode = local - > scan_hw_mode ;
} else {
chan = local - > oper_channel ;
mode = local - > oper_hw_mode ;
}
local - > hw . conf . channel = chan - > chan ;
local - > hw . conf . channel_val = chan - > val ;
local - > hw . conf . power_level = chan - > power_level ;
local - > hw . conf . freq = chan - > freq ;
local - > hw . conf . phymode = mode - > mode ;
local - > hw . conf . antenna_max = chan - > antenna_max ;
local - > hw . conf . chan = chan ;
local - > hw . conf . mode = mode ;
# ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk ( KERN_DEBUG " HW CONFIG: channel=%d freq=%d "
" phymode=%d \n " , local - > hw . conf . channel , local - > hw . conf . freq ,
local - > hw . conf . phymode ) ;
# endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
if ( local - > ops - > config )
ret = local - > ops - > config ( local_to_hw ( local ) , & local - > hw . conf ) ;
return ret ;
}
static int ieee80211_change_mtu ( struct net_device * dev , int new_mtu )
{
/* FIX: what would be proper limits for MTU?
* This interface uses 802.3 frames . */
if ( new_mtu < 256 | | new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 ) {
printk ( KERN_WARNING " %s: invalid MTU %d \n " ,
dev - > name , new_mtu ) ;
return - EINVAL ;
}
# ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk ( KERN_DEBUG " %s: setting MTU %d \n " , dev - > name , new_mtu ) ;
# endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
dev - > mtu = new_mtu ;
return 0 ;
}
static int ieee80211_change_mtu_apdev ( struct net_device * dev , int new_mtu )
{
/* FIX: what would be proper limits for MTU?
* This interface uses 802.11 frames . */
if ( new_mtu < 256 | | new_mtu > IEEE80211_MAX_DATA_LEN ) {
printk ( KERN_WARNING " %s: invalid MTU %d \n " ,
dev - > name , new_mtu ) ;
return - EINVAL ;
}
# ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk ( KERN_DEBUG " %s: setting MTU %d \n " , dev - > name , new_mtu ) ;
# endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
dev - > mtu = new_mtu ;
return 0 ;
}
enum netif_tx_lock_class {
TX_LOCK_NORMAL ,
TX_LOCK_MASTER ,
} ;
static inline void netif_tx_lock_nested ( struct net_device * dev , int subclass )
{
spin_lock_nested ( & dev - > _xmit_lock , subclass ) ;
dev - > xmit_lock_owner = smp_processor_id ( ) ;
}
static void ieee80211_set_multicast_list ( struct net_device * dev )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
unsigned short flags ;
netif_tx_lock_nested ( local - > mdev , TX_LOCK_MASTER ) ;
if ( ( ( dev - > flags & IFF_ALLMULTI ) ! = 0 ) ^ ( sdata - > allmulti ! = 0 ) ) {
if ( sdata - > allmulti ) {
sdata - > allmulti = 0 ;
local - > iff_allmultis - - ;
} else {
sdata - > allmulti = 1 ;
local - > iff_allmultis + + ;
}
}
if ( ( ( dev - > flags & IFF_PROMISC ) ! = 0 ) ^ ( sdata - > promisc ! = 0 ) ) {
if ( sdata - > promisc ) {
sdata - > promisc = 0 ;
local - > iff_promiscs - - ;
} else {
sdata - > promisc = 1 ;
local - > iff_promiscs + + ;
}
}
if ( dev - > mc_count ! = sdata - > mc_count ) {
local - > mc_count = local - > mc_count - sdata - > mc_count +
dev - > mc_count ;
sdata - > mc_count = dev - > mc_count ;
}
if ( local - > ops - > set_multicast_list ) {
flags = local - > mdev - > flags ;
if ( local - > iff_allmultis )
flags | = IFF_ALLMULTI ;
if ( local - > iff_promiscs )
flags | = IFF_PROMISC ;
read_lock ( & local - > sub_if_lock ) ;
local - > ops - > set_multicast_list ( local_to_hw ( local ) , flags ,
local - > mc_count ) ;
read_unlock ( & local - > sub_if_lock ) ;
}
netif_tx_unlock ( local - > mdev ) ;
}
struct dev_mc_list * ieee80211_get_mc_list_item ( struct ieee80211_hw * hw ,
struct dev_mc_list * prev ,
void * * ptr )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct ieee80211_sub_if_data * sdata = * ptr ;
struct dev_mc_list * mc ;
if ( ! prev ) {
WARN_ON ( sdata ) ;
sdata = NULL ;
}
if ( ! prev | | ! prev - > next ) {
if ( sdata )
sdata = list_entry ( sdata - > list . next ,
struct ieee80211_sub_if_data , list ) ;
else
sdata = list_entry ( local - > sub_if_list . next ,
struct ieee80211_sub_if_data , list ) ;
if ( & sdata - > list ! = & local - > sub_if_list )
mc = sdata - > dev - > mc_list ;
else
mc = NULL ;
} else
mc = prev - > next ;
* ptr = sdata ;
return mc ;
}
EXPORT_SYMBOL ( ieee80211_get_mc_list_item ) ;
static struct net_device_stats * ieee80211_get_stats ( struct net_device * dev )
{
struct ieee80211_sub_if_data * sdata ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
return & ( sdata - > stats ) ;
}
static void ieee80211_if_shutdown ( struct net_device * dev )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
ASSERT_RTNL ( ) ;
switch ( sdata - > type ) {
case IEEE80211_IF_TYPE_STA :
case IEEE80211_IF_TYPE_IBSS :
sdata - > u . sta . state = IEEE80211_DISABLED ;
del_timer_sync ( & sdata - > u . sta . timer ) ;
skb_queue_purge ( & sdata - > u . sta . skb_queue ) ;
if ( ! local - > ops - > hw_scan & &
local - > scan_dev = = sdata - > dev ) {
local - > sta_scanning = 0 ;
cancel_delayed_work ( & local - > scan_work ) ;
}
flush_workqueue ( local - > hw . workqueue ) ;
break ;
}
}
static inline int identical_mac_addr_allowed ( int type1 , int type2 )
{
return ( type1 = = IEEE80211_IF_TYPE_MNTR | |
type2 = = IEEE80211_IF_TYPE_MNTR | |
( type1 = = IEEE80211_IF_TYPE_AP & &
type2 = = IEEE80211_IF_TYPE_WDS ) | |
( type1 = = IEEE80211_IF_TYPE_WDS & &
( type2 = = IEEE80211_IF_TYPE_WDS | |
type2 = = IEEE80211_IF_TYPE_AP ) ) | |
( type1 = = IEEE80211_IF_TYPE_AP & &
type2 = = IEEE80211_IF_TYPE_VLAN ) | |
( type1 = = IEEE80211_IF_TYPE_VLAN & &
( type2 = = IEEE80211_IF_TYPE_AP | |
type2 = = IEEE80211_IF_TYPE_VLAN ) ) ) ;
}
static int ieee80211_master_open ( struct net_device * dev )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_sub_if_data * sdata ;
int res = - EOPNOTSUPP ;
read_lock ( & local - > sub_if_lock ) ;
list_for_each_entry ( sdata , & local - > sub_if_list , list ) {
if ( sdata - > dev ! = dev & & netif_running ( sdata - > dev ) ) {
res = 0 ;
break ;
}
}
read_unlock ( & local - > sub_if_lock ) ;
return res ;
}
static int ieee80211_master_stop ( struct net_device * dev )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_sub_if_data * sdata ;
read_lock ( & local - > sub_if_lock ) ;
list_for_each_entry ( sdata , & local - > sub_if_list , list )
if ( sdata - > dev ! = dev & & netif_running ( sdata - > dev ) )
dev_close ( sdata - > dev ) ;
read_unlock ( & local - > sub_if_lock ) ;
return 0 ;
}
static int ieee80211_mgmt_open ( struct net_device * dev )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
if ( ! netif_running ( local - > mdev ) )
return - EOPNOTSUPP ;
return 0 ;
}
static int ieee80211_mgmt_stop ( struct net_device * dev )
{
return 0 ;
}
/* Check if running monitor interfaces should go to a "soft monitor" mode
* and switch them if necessary . */
static inline void ieee80211_start_soft_monitor ( struct ieee80211_local * local )
{
struct ieee80211_if_init_conf conf ;
if ( local - > open_count & & local - > open_count = = local - > monitors & &
! ( local - > hw . flags & IEEE80211_HW_MONITOR_DURING_OPER ) & &
local - > ops - > remove_interface ) {
conf . if_id = - 1 ;
conf . type = IEEE80211_IF_TYPE_MNTR ;
conf . mac_addr = NULL ;
local - > ops - > remove_interface ( local_to_hw ( local ) , & conf ) ;
}
}
/* Check if running monitor interfaces should go to a "hard monitor" mode
* and switch them if necessary . */
static void ieee80211_start_hard_monitor ( struct ieee80211_local * local )
{
struct ieee80211_if_init_conf conf ;
if ( local - > open_count & & local - > open_count = = local - > monitors & &
2007-07-10 19:32:10 +02:00
! ( local - > hw . flags & IEEE80211_HW_MONITOR_DURING_OPER ) ) {
2007-05-05 11:45:53 -07:00
conf . if_id = - 1 ;
conf . type = IEEE80211_IF_TYPE_MNTR ;
conf . mac_addr = NULL ;
local - > ops - > add_interface ( local_to_hw ( local ) , & conf ) ;
}
}
static int ieee80211_open ( struct net_device * dev )
{
struct ieee80211_sub_if_data * sdata , * nsdata ;
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_if_init_conf conf ;
int res ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
read_lock ( & local - > sub_if_lock ) ;
list_for_each_entry ( nsdata , & local - > sub_if_list , list ) {
struct net_device * ndev = nsdata - > dev ;
if ( ndev ! = dev & & ndev ! = local - > mdev & & netif_running ( ndev ) & &
compare_ether_addr ( dev - > dev_addr , ndev - > dev_addr ) = = 0 & &
! identical_mac_addr_allowed ( sdata - > type , nsdata - > type ) ) {
read_unlock ( & local - > sub_if_lock ) ;
return - ENOTUNIQ ;
}
}
read_unlock ( & local - > sub_if_lock ) ;
if ( sdata - > type = = IEEE80211_IF_TYPE_WDS & &
is_zero_ether_addr ( sdata - > u . wds . remote_addr ) )
return - ENOLINK ;
if ( sdata - > type = = IEEE80211_IF_TYPE_MNTR & & local - > open_count & &
! ( local - > hw . flags & IEEE80211_HW_MONITOR_DURING_OPER ) ) {
/* run the interface in a "soft monitor" mode */
local - > monitors + + ;
local - > open_count + + ;
local - > hw . conf . flags | = IEEE80211_CONF_RADIOTAP ;
return 0 ;
}
ieee80211_start_soft_monitor ( local ) ;
2007-07-10 19:32:10 +02:00
conf . if_id = dev - > ifindex ;
conf . type = sdata - > type ;
conf . mac_addr = dev - > dev_addr ;
res = local - > ops - > add_interface ( local_to_hw ( local ) , & conf ) ;
if ( res ) {
if ( sdata - > type = = IEEE80211_IF_TYPE_MNTR )
ieee80211_start_hard_monitor ( local ) ;
return res ;
2007-05-05 11:45:53 -07:00
}
if ( local - > open_count = = 0 ) {
res = 0 ;
tasklet_enable ( & local - > tx_pending_tasklet ) ;
tasklet_enable ( & local - > tasklet ) ;
if ( local - > ops - > open )
res = local - > ops - > open ( local_to_hw ( local ) ) ;
if ( res = = 0 ) {
res = dev_open ( local - > mdev ) ;
if ( res ) {
if ( local - > ops - > stop )
local - > ops - > stop ( local_to_hw ( local ) ) ;
} else {
res = ieee80211_hw_config ( local ) ;
if ( res & & local - > ops - > stop )
local - > ops - > stop ( local_to_hw ( local ) ) ;
else if ( ! res & & local - > apdev )
dev_open ( local - > apdev ) ;
}
}
if ( res ) {
if ( local - > ops - > remove_interface )
local - > ops - > remove_interface ( local_to_hw ( local ) ,
& conf ) ;
return res ;
}
}
local - > open_count + + ;
if ( sdata - > type = = IEEE80211_IF_TYPE_MNTR ) {
local - > monitors + + ;
local - > hw . conf . flags | = IEEE80211_CONF_RADIOTAP ;
} else
ieee80211_if_config ( dev ) ;
if ( sdata - > type = = IEEE80211_IF_TYPE_STA & &
! local - > user_space_mlme )
netif_carrier_off ( dev ) ;
2007-05-20 09:44:00 -07:00
else
netif_carrier_on ( dev ) ;
2007-05-05 11:45:53 -07:00
netif_start_queue ( dev ) ;
return 0 ;
}
static int ieee80211_stop ( struct net_device * dev )
{
struct ieee80211_sub_if_data * sdata ;
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( sdata - > type = = IEEE80211_IF_TYPE_MNTR & &
local - > open_count > 1 & &
! ( local - > hw . flags & IEEE80211_HW_MONITOR_DURING_OPER ) ) {
/* remove "soft monitor" interface */
local - > open_count - - ;
local - > monitors - - ;
if ( ! local - > monitors )
local - > hw . conf . flags & = ~ IEEE80211_CONF_RADIOTAP ;
return 0 ;
}
netif_stop_queue ( dev ) ;
ieee80211_if_shutdown ( dev ) ;
if ( sdata - > type = = IEEE80211_IF_TYPE_MNTR ) {
local - > monitors - - ;
if ( ! local - > monitors )
local - > hw . conf . flags & = ~ IEEE80211_CONF_RADIOTAP ;
}
local - > open_count - - ;
if ( local - > open_count = = 0 ) {
if ( netif_running ( local - > mdev ) )
dev_close ( local - > mdev ) ;
if ( local - > apdev )
dev_close ( local - > apdev ) ;
if ( local - > ops - > stop )
local - > ops - > stop ( local_to_hw ( local ) ) ;
tasklet_disable ( & local - > tx_pending_tasklet ) ;
tasklet_disable ( & local - > tasklet ) ;
}
if ( local - > ops - > remove_interface ) {
struct ieee80211_if_init_conf conf ;
conf . if_id = dev - > ifindex ;
conf . type = sdata - > type ;
conf . mac_addr = dev - > dev_addr ;
local - > ops - > remove_interface ( local_to_hw ( local ) , & conf ) ;
}
ieee80211_start_hard_monitor ( local ) ;
return 0 ;
}
static int header_parse_80211 ( struct sk_buff * skb , unsigned char * haddr )
{
memcpy ( haddr , skb_mac_header ( skb ) + 10 , ETH_ALEN ) ; /* addr2 */
return ETH_ALEN ;
}
static inline int ieee80211_bssid_match ( const u8 * raddr , const u8 * addr )
{
return compare_ether_addr ( raddr , addr ) = = 0 | |
is_broadcast_ether_addr ( raddr ) ;
}
static ieee80211_txrx_result
ieee80211_rx_h_data ( struct ieee80211_txrx_data * rx )
{
struct net_device * dev = rx - > dev ;
struct ieee80211_local * local = rx - > local ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) rx - > skb - > data ;
u16 fc , hdrlen , ethertype ;
u8 * payload ;
u8 dst [ ETH_ALEN ] ;
u8 src [ ETH_ALEN ] ;
struct sk_buff * skb = rx - > skb , * skb2 ;
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
fc = rx - > fc ;
if ( unlikely ( ( fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA ) )
return TXRX_CONTINUE ;
if ( unlikely ( ! WLAN_FC_DATA_PRESENT ( fc ) ) )
return TXRX_DROP ;
hdrlen = ieee80211_get_hdrlen ( fc ) ;
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
* header
* IEEE 802.11 address fields :
* ToDS FromDS Addr1 Addr2 Addr3 Addr4
* 0 0 DA SA BSSID n / a
* 0 1 DA BSSID SA n / a
* 1 0 BSSID SA DA n / a
* 1 1 RA TA DA SA
*/
switch ( fc & ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) ) {
case IEEE80211_FCTL_TODS :
/* BSSID SA DA */
memcpy ( dst , hdr - > addr3 , ETH_ALEN ) ;
memcpy ( src , hdr - > addr2 , ETH_ALEN ) ;
if ( unlikely ( sdata - > type ! = IEEE80211_IF_TYPE_AP & &
sdata - > type ! = IEEE80211_IF_TYPE_VLAN ) ) {
printk ( KERN_DEBUG " %s: dropped ToDS frame (BSSID= "
MAC_FMT " SA= " MAC_FMT " DA= " MAC_FMT " ) \n " ,
dev - > name , MAC_ARG ( hdr - > addr1 ) ,
MAC_ARG ( hdr - > addr2 ) , MAC_ARG ( hdr - > addr3 ) ) ;
return TXRX_DROP ;
}
break ;
case ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) :
/* RA TA DA SA */
memcpy ( dst , hdr - > addr3 , ETH_ALEN ) ;
memcpy ( src , hdr - > addr4 , ETH_ALEN ) ;
if ( unlikely ( sdata - > type ! = IEEE80211_IF_TYPE_WDS ) ) {
printk ( KERN_DEBUG " %s: dropped FromDS&ToDS frame (RA= "
MAC_FMT " TA= " MAC_FMT " DA= " MAC_FMT " SA= "
MAC_FMT " ) \n " ,
rx - > dev - > name , MAC_ARG ( hdr - > addr1 ) ,
MAC_ARG ( hdr - > addr2 ) , MAC_ARG ( hdr - > addr3 ) ,
MAC_ARG ( hdr - > addr4 ) ) ;
return TXRX_DROP ;
}
break ;
case IEEE80211_FCTL_FROMDS :
/* DA BSSID SA */
memcpy ( dst , hdr - > addr1 , ETH_ALEN ) ;
memcpy ( src , hdr - > addr3 , ETH_ALEN ) ;
if ( sdata - > type ! = IEEE80211_IF_TYPE_STA ) {
return TXRX_DROP ;
}
break ;
case 0 :
/* DA SA BSSID */
memcpy ( dst , hdr - > addr1 , ETH_ALEN ) ;
memcpy ( src , hdr - > addr2 , ETH_ALEN ) ;
if ( sdata - > type ! = IEEE80211_IF_TYPE_IBSS ) {
if ( net_ratelimit ( ) ) {
printk ( KERN_DEBUG " %s: dropped IBSS frame (DA= "
MAC_FMT " SA= " MAC_FMT " BSSID= " MAC_FMT
" ) \n " ,
dev - > name , MAC_ARG ( hdr - > addr1 ) ,
MAC_ARG ( hdr - > addr2 ) ,
MAC_ARG ( hdr - > addr3 ) ) ;
}
return TXRX_DROP ;
}
break ;
}
payload = skb - > data + hdrlen ;
if ( unlikely ( skb - > len - hdrlen < 8 ) ) {
if ( net_ratelimit ( ) ) {
printk ( KERN_DEBUG " %s: RX too short data frame "
" payload \n " , dev - > name ) ;
}
return TXRX_DROP ;
}
ethertype = ( payload [ 6 ] < < 8 ) | payload [ 7 ] ;
if ( likely ( ( compare_ether_addr ( payload , rfc1042_header ) = = 0 & &
ethertype ! = ETH_P_AARP & & ethertype ! = ETH_P_IPX ) | |
compare_ether_addr ( payload , bridge_tunnel_header ) = = 0 ) ) {
/* remove RFC1042 or Bridge-Tunnel encapsulation and
* replace EtherType */
skb_pull ( skb , hdrlen + 6 ) ;
memcpy ( skb_push ( skb , ETH_ALEN ) , src , ETH_ALEN ) ;
memcpy ( skb_push ( skb , ETH_ALEN ) , dst , ETH_ALEN ) ;
} else {
struct ethhdr * ehdr ;
__be16 len ;
skb_pull ( skb , hdrlen ) ;
len = htons ( skb - > len ) ;
ehdr = ( struct ethhdr * ) skb_push ( skb , sizeof ( struct ethhdr ) ) ;
memcpy ( ehdr - > h_dest , dst , ETH_ALEN ) ;
memcpy ( ehdr - > h_source , src , ETH_ALEN ) ;
ehdr - > h_proto = len ;
}
skb - > dev = dev ;
skb2 = NULL ;
sdata - > stats . rx_packets + + ;
sdata - > stats . rx_bytes + = skb - > len ;
if ( local - > bridge_packets & & ( sdata - > type = = IEEE80211_IF_TYPE_AP
| | sdata - > type = = IEEE80211_IF_TYPE_VLAN ) & & rx - > u . rx . ra_match ) {
if ( is_multicast_ether_addr ( skb - > data ) ) {
/* send multicast frames both to higher layers in
* local net stack and back to the wireless media */
skb2 = skb_copy ( skb , GFP_ATOMIC ) ;
if ( ! skb2 )
printk ( KERN_DEBUG " %s: failed to clone "
" multicast frame \n " , dev - > name ) ;
} else {
struct sta_info * dsta ;
dsta = sta_info_get ( local , skb - > data ) ;
if ( dsta & & ! dsta - > dev ) {
printk ( KERN_DEBUG " Station with null dev "
" structure! \n " ) ;
} else if ( dsta & & dsta - > dev = = dev ) {
/* Destination station is associated to this
* AP , so send the frame directly to it and
* do not pass the frame to local net stack .
*/
skb2 = skb ;
skb = NULL ;
}
if ( dsta )
sta_info_put ( dsta ) ;
}
}
if ( skb ) {
/* deliver to local stack */
skb - > protocol = eth_type_trans ( skb , dev ) ;
memset ( skb - > cb , 0 , sizeof ( skb - > cb ) ) ;
netif_rx ( skb ) ;
}
if ( skb2 ) {
/* send to wireless media */
skb2 - > protocol = __constant_htons ( ETH_P_802_3 ) ;
skb_set_network_header ( skb2 , 0 ) ;
skb_set_mac_header ( skb2 , 0 ) ;
dev_queue_xmit ( skb2 ) ;
}
return TXRX_QUEUED ;
}
static struct ieee80211_rate *
ieee80211_get_rate ( struct ieee80211_local * local , int phymode , int hw_rate )
{
struct ieee80211_hw_mode * mode ;
int r ;
list_for_each_entry ( mode , & local - > modes_list , list ) {
if ( mode - > mode ! = phymode )
continue ;
for ( r = 0 ; r < mode - > num_rates ; r + + ) {
struct ieee80211_rate * rate = & mode - > rates [ r ] ;
if ( rate - > val = = hw_rate | |
( rate - > flags & IEEE80211_RATE_PREAMBLE2 & &
rate - > val2 = = hw_rate ) )
return rate ;
}
}
return NULL ;
}
static void
ieee80211_fill_frame_info ( struct ieee80211_local * local ,
struct ieee80211_frame_info * fi ,
struct ieee80211_rx_status * status )
{
if ( status ) {
struct timespec ts ;
struct ieee80211_rate * rate ;
jiffies_to_timespec ( jiffies , & ts ) ;
fi - > hosttime = cpu_to_be64 ( ( u64 ) ts . tv_sec * 1000000 +
ts . tv_nsec / 1000 ) ;
fi - > mactime = cpu_to_be64 ( status - > mactime ) ;
switch ( status - > phymode ) {
case MODE_IEEE80211A :
fi - > phytype = htonl ( ieee80211_phytype_ofdm_dot11_a ) ;
break ;
case MODE_IEEE80211B :
fi - > phytype = htonl ( ieee80211_phytype_dsss_dot11_b ) ;
break ;
case MODE_IEEE80211G :
fi - > phytype = htonl ( ieee80211_phytype_pbcc_dot11_g ) ;
break ;
case MODE_ATHEROS_TURBO :
fi - > phytype =
htonl ( ieee80211_phytype_dsss_dot11_turbo ) ;
break ;
default :
fi - > phytype = htonl ( 0xAAAAAAAA ) ;
break ;
}
fi - > channel = htonl ( status - > channel ) ;
rate = ieee80211_get_rate ( local , status - > phymode ,
status - > rate ) ;
if ( rate ) {
fi - > datarate = htonl ( rate - > rate ) ;
if ( rate - > flags & IEEE80211_RATE_PREAMBLE2 ) {
if ( status - > rate = = rate - > val )
fi - > preamble = htonl ( 2 ) ; /* long */
else if ( status - > rate = = rate - > val2 )
fi - > preamble = htonl ( 1 ) ; /* short */
} else
fi - > preamble = htonl ( 0 ) ;
} else {
fi - > datarate = htonl ( 0 ) ;
fi - > preamble = htonl ( 0 ) ;
}
fi - > antenna = htonl ( status - > antenna ) ;
fi - > priority = htonl ( 0xffffffff ) ; /* no clue */
fi - > ssi_type = htonl ( ieee80211_ssi_raw ) ;
fi - > ssi_signal = htonl ( status - > ssi ) ;
fi - > ssi_noise = 0x00000000 ;
fi - > encoding = 0 ;
} else {
/* clear everything because we really don't know.
* the msg_type field isn ' t present on monitor frames
* so we don ' t know whether it will be present or not ,
* but it ' s ok to not clear it since it ' ll be assigned
* anyway */
memset ( fi , 0 , sizeof ( * fi ) - sizeof ( fi - > msg_type ) ) ;
fi - > ssi_type = htonl ( ieee80211_ssi_none ) ;
}
fi - > version = htonl ( IEEE80211_FI_VERSION ) ;
fi - > length = cpu_to_be32 ( sizeof ( * fi ) - sizeof ( fi - > msg_type ) ) ;
}
/* this routine is actually not just for this, but also
* for pushing fake ' management ' frames into userspace .
* it shall be replaced by a netlink - based system . */
void
ieee80211_rx_mgmt ( struct ieee80211_local * local , struct sk_buff * skb ,
struct ieee80211_rx_status * status , u32 msg_type )
{
struct ieee80211_frame_info * fi ;
const size_t hlen = sizeof ( struct ieee80211_frame_info ) ;
struct ieee80211_sub_if_data * sdata ;
skb - > dev = local - > apdev ;
sdata = IEEE80211_DEV_TO_SUB_IF ( local - > apdev ) ;
if ( skb_headroom ( skb ) < hlen ) {
I802_DEBUG_INC ( local - > rx_expand_skb_head ) ;
if ( pskb_expand_head ( skb , hlen , 0 , GFP_ATOMIC ) ) {
dev_kfree_skb ( skb ) ;
return ;
}
}
fi = ( struct ieee80211_frame_info * ) skb_push ( skb , hlen ) ;
ieee80211_fill_frame_info ( local , fi , status ) ;
fi - > msg_type = htonl ( msg_type ) ;
sdata - > stats . rx_packets + + ;
sdata - > stats . rx_bytes + = skb - > len ;
skb_set_mac_header ( skb , 0 ) ;
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
skb - > pkt_type = PACKET_OTHERHOST ;
skb - > protocol = htons ( ETH_P_802_2 ) ;
memset ( skb - > cb , 0 , sizeof ( skb - > cb ) ) ;
netif_rx ( skb ) ;
}
static void
ieee80211_rx_monitor ( struct net_device * dev , struct sk_buff * skb ,
struct ieee80211_rx_status * status )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_sub_if_data * sdata ;
struct ieee80211_rate * rate ;
struct ieee80211_rtap_hdr {
struct ieee80211_radiotap_header hdr ;
u8 flags ;
u8 rate ;
__le16 chan_freq ;
__le16 chan_flags ;
u8 antsignal ;
} __attribute__ ( ( packed ) ) * rthdr ;
skb - > dev = dev ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( status - > flag & RX_FLAG_RADIOTAP )
goto out ;
if ( skb_headroom ( skb ) < sizeof ( * rthdr ) ) {
I802_DEBUG_INC ( local - > rx_expand_skb_head ) ;
if ( pskb_expand_head ( skb , sizeof ( * rthdr ) , 0 , GFP_ATOMIC ) ) {
dev_kfree_skb ( skb ) ;
return ;
}
}
rthdr = ( struct ieee80211_rtap_hdr * ) skb_push ( skb , sizeof ( * rthdr ) ) ;
memset ( rthdr , 0 , sizeof ( * rthdr ) ) ;
rthdr - > hdr . it_len = cpu_to_le16 ( sizeof ( * rthdr ) ) ;
rthdr - > hdr . it_present =
cpu_to_le32 ( ( 1 < < IEEE80211_RADIOTAP_FLAGS ) |
( 1 < < IEEE80211_RADIOTAP_RATE ) |
( 1 < < IEEE80211_RADIOTAP_CHANNEL ) |
( 1 < < IEEE80211_RADIOTAP_DB_ANTSIGNAL ) ) ;
rthdr - > flags = local - > hw . flags & IEEE80211_HW_RX_INCLUDES_FCS ?
IEEE80211_RADIOTAP_F_FCS : 0 ;
rate = ieee80211_get_rate ( local , status - > phymode , status - > rate ) ;
if ( rate )
rthdr - > rate = rate - > rate / 5 ;
rthdr - > chan_freq = cpu_to_le16 ( status - > freq ) ;
rthdr - > chan_flags =
status - > phymode = = MODE_IEEE80211A ?
cpu_to_le16 ( IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ ) :
cpu_to_le16 ( IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ ) ;
rthdr - > antsignal = status - > ssi ;
out :
sdata - > stats . rx_packets + + ;
sdata - > stats . rx_bytes + = skb - > len ;
skb_set_mac_header ( skb , 0 ) ;
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
skb - > pkt_type = PACKET_OTHERHOST ;
skb - > protocol = htons ( ETH_P_802_2 ) ;
memset ( skb - > cb , 0 , sizeof ( skb - > cb ) ) ;
netif_rx ( skb ) ;
}
int ieee80211_radar_status ( struct ieee80211_hw * hw , int channel ,
int radar , int radar_type )
{
struct sk_buff * skb ;
struct ieee80211_radar_info * msg ;
struct ieee80211_local * local = hw_to_local ( hw ) ;
if ( ! local - > apdev )
return 0 ;
skb = dev_alloc_skb ( sizeof ( struct ieee80211_frame_info ) +
sizeof ( struct ieee80211_radar_info ) ) ;
if ( ! skb )
return - ENOMEM ;
skb_reserve ( skb , sizeof ( struct ieee80211_frame_info ) ) ;
msg = ( struct ieee80211_radar_info * )
skb_put ( skb , sizeof ( struct ieee80211_radar_info ) ) ;
msg - > channel = channel ;
msg - > radar = radar ;
msg - > radar_type = radar_type ;
ieee80211_rx_mgmt ( local , skb , NULL , ieee80211_msg_radar ) ;
return 0 ;
}
EXPORT_SYMBOL ( ieee80211_radar_status ) ;
static void ap_sta_ps_start ( struct net_device * dev , struct sta_info * sta )
{
struct ieee80211_sub_if_data * sdata ;
sdata = IEEE80211_DEV_TO_SUB_IF ( sta - > dev ) ;
if ( sdata - > bss )
atomic_inc ( & sdata - > bss - > num_sta_ps ) ;
sta - > flags | = WLAN_STA_PS ;
sta - > pspoll = 0 ;
# ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk ( KERN_DEBUG " %s: STA " MAC_FMT " aid %d enters power "
" save mode \n " , dev - > name , MAC_ARG ( sta - > addr ) , sta - > aid ) ;
# endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
static int ap_sta_ps_end ( struct net_device * dev , struct sta_info * sta )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct sk_buff * skb ;
int sent = 0 ;
struct ieee80211_sub_if_data * sdata ;
struct ieee80211_tx_packet_data * pkt_data ;
sdata = IEEE80211_DEV_TO_SUB_IF ( sta - > dev ) ;
if ( sdata - > bss )
atomic_dec ( & sdata - > bss - > num_sta_ps ) ;
sta - > flags & = ~ ( WLAN_STA_PS | WLAN_STA_TIM ) ;
sta - > pspoll = 0 ;
if ( ! skb_queue_empty ( & sta - > ps_tx_buf ) ) {
if ( local - > ops - > set_tim )
local - > ops - > set_tim ( local_to_hw ( local ) , sta - > aid , 0 ) ;
if ( sdata - > bss )
bss_tim_clear ( local , sdata - > bss , sta - > aid ) ;
}
# ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk ( KERN_DEBUG " %s: STA " MAC_FMT " aid %d exits power "
" save mode \n " , dev - > name , MAC_ARG ( sta - > addr ) , sta - > aid ) ;
# endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
/* Send all buffered frames to the station */
while ( ( skb = skb_dequeue ( & sta - > tx_filtered ) ) ! = NULL ) {
pkt_data = ( struct ieee80211_tx_packet_data * ) skb - > cb ;
sent + + ;
pkt_data - > requeue = 1 ;
dev_queue_xmit ( skb ) ;
}
while ( ( skb = skb_dequeue ( & sta - > ps_tx_buf ) ) ! = NULL ) {
pkt_data = ( struct ieee80211_tx_packet_data * ) skb - > cb ;
local - > total_ps_buffered - - ;
sent + + ;
# ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk ( KERN_DEBUG " %s: STA " MAC_FMT " aid %d send PS frame "
" since STA not sleeping anymore \n " , dev - > name ,
MAC_ARG ( sta - > addr ) , sta - > aid ) ;
# endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
pkt_data - > requeue = 1 ;
dev_queue_xmit ( skb ) ;
}
return sent ;
}
static ieee80211_txrx_result
ieee80211_rx_h_ps_poll ( struct ieee80211_txrx_data * rx )
{
struct sk_buff * skb ;
int no_pending_pkts ;
if ( likely ( ! rx - > sta | |
( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_CTL | |
( rx - > fc & IEEE80211_FCTL_STYPE ) ! = IEEE80211_STYPE_PSPOLL | |
! rx - > u . rx . ra_match ) )
return TXRX_CONTINUE ;
skb = skb_dequeue ( & rx - > sta - > tx_filtered ) ;
if ( ! skb ) {
skb = skb_dequeue ( & rx - > sta - > ps_tx_buf ) ;
if ( skb )
rx - > local - > total_ps_buffered - - ;
}
no_pending_pkts = skb_queue_empty ( & rx - > sta - > tx_filtered ) & &
skb_queue_empty ( & rx - > sta - > ps_tx_buf ) ;
if ( skb ) {
struct ieee80211_hdr * hdr =
( struct ieee80211_hdr * ) skb - > data ;
/* tell TX path to send one frame even though the STA may
* still remain is PS mode after this frame exchange */
rx - > sta - > pspoll = 1 ;
# ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk ( KERN_DEBUG " STA " MAC_FMT " aid %d: PS Poll (entries "
" after %d) \n " ,
MAC_ARG ( rx - > sta - > addr ) , rx - > sta - > aid ,
skb_queue_len ( & rx - > sta - > ps_tx_buf ) ) ;
# endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
/* Use MoreData flag to indicate whether there are more
* buffered frames for this STA */
if ( no_pending_pkts ) {
hdr - > frame_control & = cpu_to_le16 ( ~ IEEE80211_FCTL_MOREDATA ) ;
rx - > sta - > flags & = ~ WLAN_STA_TIM ;
} else
hdr - > frame_control | = cpu_to_le16 ( IEEE80211_FCTL_MOREDATA ) ;
dev_queue_xmit ( skb ) ;
if ( no_pending_pkts ) {
if ( rx - > local - > ops - > set_tim )
rx - > local - > ops - > set_tim ( local_to_hw ( rx - > local ) ,
rx - > sta - > aid , 0 ) ;
if ( rx - > sdata - > bss )
bss_tim_clear ( rx - > local , rx - > sdata - > bss , rx - > sta - > aid ) ;
}
# ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
} else if ( ! rx - > u . rx . sent_ps_buffered ) {
printk ( KERN_DEBUG " %s: STA " MAC_FMT " sent PS Poll even "
" though there is no buffered frames for it \n " ,
rx - > dev - > name , MAC_ARG ( rx - > sta - > addr ) ) ;
# endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
/* Free PS Poll skb here instead of returning TXRX_DROP that would
* count as an dropped frame . */
dev_kfree_skb ( rx - > skb ) ;
return TXRX_QUEUED ;
}
static inline struct ieee80211_fragment_entry *
ieee80211_reassemble_add ( struct ieee80211_sub_if_data * sdata ,
unsigned int frag , unsigned int seq , int rx_queue ,
struct sk_buff * * skb )
{
struct ieee80211_fragment_entry * entry ;
int idx ;
idx = sdata - > fragment_next ;
entry = & sdata - > fragments [ sdata - > fragment_next + + ] ;
if ( sdata - > fragment_next > = IEEE80211_FRAGMENT_MAX )
sdata - > fragment_next = 0 ;
if ( ! skb_queue_empty ( & entry - > skb_list ) ) {
# ifdef CONFIG_MAC80211_DEBUG
struct ieee80211_hdr * hdr =
( struct ieee80211_hdr * ) entry - > skb_list . next - > data ;
printk ( KERN_DEBUG " %s: RX reassembly removed oldest "
" fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
" addr1= " MAC_FMT " addr2= " MAC_FMT " \n " ,
sdata - > dev - > name , idx ,
jiffies - entry - > first_frag_time , entry - > seq ,
entry - > last_frag , MAC_ARG ( hdr - > addr1 ) ,
MAC_ARG ( hdr - > addr2 ) ) ;
# endif /* CONFIG_MAC80211_DEBUG */
__skb_queue_purge ( & entry - > skb_list ) ;
}
__skb_queue_tail ( & entry - > skb_list , * skb ) ; /* no need for locking */
* skb = NULL ;
entry - > first_frag_time = jiffies ;
entry - > seq = seq ;
entry - > rx_queue = rx_queue ;
entry - > last_frag = frag ;
entry - > ccmp = 0 ;
entry - > extra_len = 0 ;
return entry ;
}
static inline struct ieee80211_fragment_entry *
ieee80211_reassemble_find ( struct ieee80211_sub_if_data * sdata ,
u16 fc , unsigned int frag , unsigned int seq ,
int rx_queue , struct ieee80211_hdr * hdr )
{
struct ieee80211_fragment_entry * entry ;
int i , idx ;
idx = sdata - > fragment_next ;
for ( i = 0 ; i < IEEE80211_FRAGMENT_MAX ; i + + ) {
struct ieee80211_hdr * f_hdr ;
u16 f_fc ;
idx - - ;
if ( idx < 0 )
idx = IEEE80211_FRAGMENT_MAX - 1 ;
entry = & sdata - > fragments [ idx ] ;
if ( skb_queue_empty ( & entry - > skb_list ) | | entry - > seq ! = seq | |
entry - > rx_queue ! = rx_queue | |
entry - > last_frag + 1 ! = frag )
continue ;
f_hdr = ( struct ieee80211_hdr * ) entry - > skb_list . next - > data ;
f_fc = le16_to_cpu ( f_hdr - > frame_control ) ;
if ( ( fc & IEEE80211_FCTL_FTYPE ) ! = ( f_fc & IEEE80211_FCTL_FTYPE ) | |
compare_ether_addr ( hdr - > addr1 , f_hdr - > addr1 ) ! = 0 | |
compare_ether_addr ( hdr - > addr2 , f_hdr - > addr2 ) ! = 0 )
continue ;
if ( entry - > first_frag_time + 2 * HZ < jiffies ) {
__skb_queue_purge ( & entry - > skb_list ) ;
continue ;
}
return entry ;
}
return NULL ;
}
static ieee80211_txrx_result
ieee80211_rx_h_defragment ( struct ieee80211_txrx_data * rx )
{
struct ieee80211_hdr * hdr ;
u16 sc ;
unsigned int frag , seq ;
struct ieee80211_fragment_entry * entry ;
struct sk_buff * skb ;
hdr = ( struct ieee80211_hdr * ) rx - > skb - > data ;
sc = le16_to_cpu ( hdr - > seq_ctrl ) ;
frag = sc & IEEE80211_SCTL_FRAG ;
if ( likely ( ( ! ( rx - > fc & IEEE80211_FCTL_MOREFRAGS ) & & frag = = 0 ) | |
( rx - > skb ) - > len < 24 | |
is_multicast_ether_addr ( hdr - > addr1 ) ) ) {
/* not fragmented */
goto out ;
}
I802_DEBUG_INC ( rx - > local - > rx_handlers_fragments ) ;
seq = ( sc & IEEE80211_SCTL_SEQ ) > > 4 ;
if ( frag = = 0 ) {
/* This is the first fragment of a new frame. */
entry = ieee80211_reassemble_add ( rx - > sdata , frag , seq ,
rx - > u . rx . queue , & ( rx - > skb ) ) ;
if ( rx - > key & & rx - > key - > alg = = ALG_CCMP & &
( rx - > fc & IEEE80211_FCTL_PROTECTED ) ) {
/* Store CCMP PN so that we can verify that the next
* fragment has a sequential PN value . */
entry - > ccmp = 1 ;
memcpy ( entry - > last_pn ,
rx - > key - > u . ccmp . rx_pn [ rx - > u . rx . queue ] ,
CCMP_PN_LEN ) ;
}
return TXRX_QUEUED ;
}
/* This is a fragment for a frame that should already be pending in
* fragment cache . Add this fragment to the end of the pending entry .
*/
entry = ieee80211_reassemble_find ( rx - > sdata , rx - > fc , frag , seq ,
rx - > u . rx . queue , hdr ) ;
if ( ! entry ) {
I802_DEBUG_INC ( rx - > local - > rx_handlers_drop_defrag ) ;
return TXRX_DROP ;
}
/* Verify that MPDUs within one MSDU have sequential PN values.
* ( IEEE 802.11 i , 8.3 .3 .4 .5 ) */
if ( entry - > ccmp ) {
int i ;
u8 pn [ CCMP_PN_LEN ] , * rpn ;
if ( ! rx - > key | | rx - > key - > alg ! = ALG_CCMP )
return TXRX_DROP ;
memcpy ( pn , entry - > last_pn , CCMP_PN_LEN ) ;
for ( i = CCMP_PN_LEN - 1 ; i > = 0 ; i - - ) {
pn [ i ] + + ;
if ( pn [ i ] )
break ;
}
rpn = rx - > key - > u . ccmp . rx_pn [ rx - > u . rx . queue ] ;
if ( memcmp ( pn , rpn , CCMP_PN_LEN ) ! = 0 ) {
printk ( KERN_DEBUG " %s: defrag: CCMP PN not sequential "
" A2= " MAC_FMT " PN=%02x%02x%02x%02x%02x%02x "
" (expected %02x%02x%02x%02x%02x%02x) \n " ,
rx - > dev - > name , MAC_ARG ( hdr - > addr2 ) ,
rpn [ 0 ] , rpn [ 1 ] , rpn [ 2 ] , rpn [ 3 ] , rpn [ 4 ] , rpn [ 5 ] ,
pn [ 0 ] , pn [ 1 ] , pn [ 2 ] , pn [ 3 ] , pn [ 4 ] , pn [ 5 ] ) ;
return TXRX_DROP ;
}
memcpy ( entry - > last_pn , pn , CCMP_PN_LEN ) ;
}
skb_pull ( rx - > skb , ieee80211_get_hdrlen ( rx - > fc ) ) ;
__skb_queue_tail ( & entry - > skb_list , rx - > skb ) ;
entry - > last_frag = frag ;
entry - > extra_len + = rx - > skb - > len ;
if ( rx - > fc & IEEE80211_FCTL_MOREFRAGS ) {
rx - > skb = NULL ;
return TXRX_QUEUED ;
}
rx - > skb = __skb_dequeue ( & entry - > skb_list ) ;
if ( skb_tailroom ( rx - > skb ) < entry - > extra_len ) {
I802_DEBUG_INC ( rx - > local - > rx_expand_skb_head2 ) ;
if ( unlikely ( pskb_expand_head ( rx - > skb , 0 , entry - > extra_len ,
GFP_ATOMIC ) ) ) {
I802_DEBUG_INC ( rx - > local - > rx_handlers_drop_defrag ) ;
__skb_queue_purge ( & entry - > skb_list ) ;
return TXRX_DROP ;
}
}
2007-05-17 11:13:44 +08:00
while ( ( skb = __skb_dequeue ( & entry - > skb_list ) ) ) {
2007-05-05 11:45:53 -07:00
memcpy ( skb_put ( rx - > skb , skb - > len ) , skb - > data , skb - > len ) ;
2007-05-17 11:13:44 +08:00
dev_kfree_skb ( skb ) ;
}
2007-05-05 11:45:53 -07:00
/* Complete frame has been reassembled - process it now */
rx - > fragmented = 1 ;
out :
if ( rx - > sta )
rx - > sta - > rx_packets + + ;
if ( is_multicast_ether_addr ( hdr - > addr1 ) )
rx - > local - > dot11MulticastReceivedFrameCount + + ;
else
ieee80211_led_rx ( rx - > local ) ;
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_monitor ( struct ieee80211_txrx_data * rx )
{
if ( rx - > sdata - > type = = IEEE80211_IF_TYPE_MNTR ) {
ieee80211_rx_monitor ( rx - > dev , rx - > skb , rx - > u . rx . status ) ;
return TXRX_QUEUED ;
}
if ( rx - > u . rx . status - > flag & RX_FLAG_RADIOTAP )
skb_pull ( rx - > skb , ieee80211_get_radiotap_len ( rx - > skb ) ) ;
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_check ( struct ieee80211_txrx_data * rx )
{
struct ieee80211_hdr * hdr ;
int always_sta_key ;
hdr = ( struct ieee80211_hdr * ) rx - > skb - > data ;
/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
if ( rx - > sta & & ! is_multicast_ether_addr ( hdr - > addr1 ) ) {
if ( unlikely ( rx - > fc & IEEE80211_FCTL_RETRY & &
rx - > sta - > last_seq_ctrl [ rx - > u . rx . queue ] = =
hdr - > seq_ctrl ) ) {
if ( rx - > u . rx . ra_match ) {
rx - > local - > dot11FrameDuplicateCount + + ;
rx - > sta - > num_duplicates + + ;
}
return TXRX_DROP ;
} else
rx - > sta - > last_seq_ctrl [ rx - > u . rx . queue ] = hdr - > seq_ctrl ;
}
if ( ( rx - > local - > hw . flags & IEEE80211_HW_RX_INCLUDES_FCS ) & &
rx - > skb - > len > FCS_LEN )
skb_trim ( rx - > skb , rx - > skb - > len - FCS_LEN ) ;
if ( unlikely ( rx - > skb - > len < 16 ) ) {
I802_DEBUG_INC ( rx - > local - > rx_handlers_drop_short ) ;
return TXRX_DROP ;
}
if ( ! rx - > u . rx . ra_match )
rx - > skb - > pkt_type = PACKET_OTHERHOST ;
else if ( compare_ether_addr ( rx - > dev - > dev_addr , hdr - > addr1 ) = = 0 )
rx - > skb - > pkt_type = PACKET_HOST ;
else if ( is_multicast_ether_addr ( hdr - > addr1 ) ) {
if ( is_broadcast_ether_addr ( hdr - > addr1 ) )
rx - > skb - > pkt_type = PACKET_BROADCAST ;
else
rx - > skb - > pkt_type = PACKET_MULTICAST ;
} else
rx - > skb - > pkt_type = PACKET_OTHERHOST ;
/* Drop disallowed frame classes based on STA auth/assoc state;
* IEEE 802.11 , Chap 5.5 .
*
* 80211. o does filtering only based on association state , i . e . , it
* drops Class 3 frames from not associated stations . hostapd sends
* deauth / disassoc frames when needed . In addition , hostapd is
* responsible for filtering on both auth and assoc states .
*/
if ( unlikely ( ( ( rx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_DATA | |
( ( rx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_CTL & &
( rx - > fc & IEEE80211_FCTL_STYPE ) = = IEEE80211_STYPE_PSPOLL ) ) & &
rx - > sdata - > type ! = IEEE80211_IF_TYPE_IBSS & &
( ! rx - > sta | | ! ( rx - > sta - > flags & WLAN_STA_ASSOC ) ) ) ) {
if ( ( ! ( rx - > fc & IEEE80211_FCTL_FROMDS ) & &
! ( rx - > fc & IEEE80211_FCTL_TODS ) & &
( rx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_DATA )
| | ! rx - > u . rx . ra_match ) {
/* Drop IBSS frames and frames for other hosts
* silently . */
return TXRX_DROP ;
}
if ( ! rx - > local - > apdev )
return TXRX_DROP ;
ieee80211_rx_mgmt ( rx - > local , rx - > skb , rx - > u . rx . status ,
ieee80211_msg_sta_not_assoc ) ;
return TXRX_QUEUED ;
}
if ( rx - > sdata - > type = = IEEE80211_IF_TYPE_STA )
always_sta_key = 0 ;
else
always_sta_key = 1 ;
if ( rx - > sta & & rx - > sta - > key & & always_sta_key ) {
rx - > key = rx - > sta - > key ;
} else {
if ( rx - > sta & & rx - > sta - > key )
rx - > key = rx - > sta - > key ;
else
rx - > key = rx - > sdata - > default_key ;
if ( ( rx - > local - > hw . flags & IEEE80211_HW_WEP_INCLUDE_IV ) & &
rx - > fc & IEEE80211_FCTL_PROTECTED ) {
int keyidx = ieee80211_wep_get_keyidx ( rx - > skb ) ;
if ( keyidx > = 0 & & keyidx < NUM_DEFAULT_KEYS & &
( ! rx - > sta | | ! rx - > sta - > key | | keyidx > 0 ) )
rx - > key = rx - > sdata - > keys [ keyidx ] ;
if ( ! rx - > key ) {
if ( ! rx - > u . rx . ra_match )
return TXRX_DROP ;
printk ( KERN_DEBUG " %s: RX WEP frame with "
" unknown keyidx %d (A1= " MAC_FMT " A2= "
MAC_FMT " A3= " MAC_FMT " ) \n " ,
rx - > dev - > name , keyidx ,
MAC_ARG ( hdr - > addr1 ) ,
MAC_ARG ( hdr - > addr2 ) ,
MAC_ARG ( hdr - > addr3 ) ) ;
if ( ! rx - > local - > apdev )
return TXRX_DROP ;
ieee80211_rx_mgmt (
rx - > local , rx - > skb , rx - > u . rx . status ,
ieee80211_msg_wep_frame_unknown_key ) ;
return TXRX_QUEUED ;
}
}
}
if ( rx - > fc & IEEE80211_FCTL_PROTECTED & & rx - > key & & rx - > u . rx . ra_match ) {
rx - > key - > tx_rx_count + + ;
if ( unlikely ( rx - > local - > key_tx_rx_threshold & &
rx - > key - > tx_rx_count >
rx - > local - > key_tx_rx_threshold ) ) {
ieee80211_key_threshold_notify ( rx - > dev , rx - > key ,
rx - > sta ) ;
}
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_sta_process ( struct ieee80211_txrx_data * rx )
{
struct sta_info * sta = rx - > sta ;
struct net_device * dev = rx - > dev ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) rx - > skb - > data ;
if ( ! sta )
return TXRX_CONTINUE ;
/* Update last_rx only for IBSS packets which are for the current
* BSSID to avoid keeping the current IBSS network alive in cases where
* other STAs are using different BSSID . */
if ( rx - > sdata - > type = = IEEE80211_IF_TYPE_IBSS ) {
u8 * bssid = ieee80211_get_bssid ( hdr , rx - > skb - > len ) ;
if ( compare_ether_addr ( bssid , rx - > sdata - > u . sta . bssid ) = = 0 )
sta - > last_rx = jiffies ;
} else
if ( ! is_multicast_ether_addr ( hdr - > addr1 ) | |
rx - > sdata - > type = = IEEE80211_IF_TYPE_STA ) {
/* Update last_rx only for unicast frames in order to prevent
* the Probe Request frames ( the only broadcast frames from a
* STA in infrastructure mode ) from keeping a connection alive .
*/
sta - > last_rx = jiffies ;
}
if ( ! rx - > u . rx . ra_match )
return TXRX_CONTINUE ;
sta - > rx_fragments + + ;
sta - > rx_bytes + = rx - > skb - > len ;
sta - > last_rssi = ( sta - > last_rssi * 15 +
rx - > u . rx . status - > ssi ) / 16 ;
sta - > last_signal = ( sta - > last_signal * 15 +
rx - > u . rx . status - > signal ) / 16 ;
sta - > last_noise = ( sta - > last_noise * 15 +
rx - > u . rx . status - > noise ) / 16 ;
if ( ! ( rx - > fc & IEEE80211_FCTL_MOREFRAGS ) ) {
/* Change STA power saving mode only in the end of a frame
* exchange sequence */
if ( ( sta - > flags & WLAN_STA_PS ) & & ! ( rx - > fc & IEEE80211_FCTL_PM ) )
rx - > u . rx . sent_ps_buffered + = ap_sta_ps_end ( dev , sta ) ;
else if ( ! ( sta - > flags & WLAN_STA_PS ) & &
( rx - > fc & IEEE80211_FCTL_PM ) )
ap_sta_ps_start ( dev , sta ) ;
}
/* Drop data::nullfunc frames silently, since they are used only to
* control station power saving mode . */
if ( ( rx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_DATA & &
( rx - > fc & IEEE80211_FCTL_STYPE ) = = IEEE80211_STYPE_NULLFUNC ) {
I802_DEBUG_INC ( rx - > local - > rx_handlers_drop_nullfunc ) ;
/* Update counter and free packet here to avoid counting this
* as a dropped packed . */
sta - > rx_packets + + ;
dev_kfree_skb ( rx - > skb ) ;
return TXRX_QUEUED ;
}
return TXRX_CONTINUE ;
} /* ieee80211_rx_h_sta_process */
static ieee80211_txrx_result
ieee80211_rx_h_wep_weak_iv_detection ( struct ieee80211_txrx_data * rx )
{
if ( ! rx - > sta | | ! ( rx - > fc & IEEE80211_FCTL_PROTECTED ) | |
( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA | |
! rx - > key | | rx - > key - > alg ! = ALG_WEP | | ! rx - > u . rx . ra_match )
return TXRX_CONTINUE ;
/* Check for weak IVs, if hwaccel did not remove IV from the frame */
if ( ( rx - > local - > hw . flags & IEEE80211_HW_WEP_INCLUDE_IV ) | |
rx - > key - > force_sw_encrypt ) {
u8 * iv = ieee80211_wep_is_weak_iv ( rx - > skb , rx - > key ) ;
if ( iv ) {
rx - > sta - > wep_weak_iv_count + + ;
}
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_wep_decrypt ( struct ieee80211_txrx_data * rx )
{
/* If the device handles decryption totally, skip this test */
if ( rx - > local - > hw . flags & IEEE80211_HW_DEVICE_HIDES_WEP )
return TXRX_CONTINUE ;
if ( ( rx - > key & & rx - > key - > alg ! = ALG_WEP ) | |
! ( rx - > fc & IEEE80211_FCTL_PROTECTED ) | |
( ( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA & &
( ( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_MGMT | |
( rx - > fc & IEEE80211_FCTL_STYPE ) ! = IEEE80211_STYPE_AUTH ) ) )
return TXRX_CONTINUE ;
if ( ! rx - > key ) {
printk ( KERN_DEBUG " %s: RX WEP frame, but no key set \n " ,
rx - > dev - > name ) ;
return TXRX_DROP ;
}
if ( ! ( rx - > u . rx . status - > flag & RX_FLAG_DECRYPTED ) | |
rx - > key - > force_sw_encrypt ) {
if ( ieee80211_wep_decrypt ( rx - > local , rx - > skb , rx - > key ) ) {
printk ( KERN_DEBUG " %s: RX WEP frame, decrypt "
" failed \n " , rx - > dev - > name ) ;
return TXRX_DROP ;
}
} else if ( rx - > local - > hw . flags & IEEE80211_HW_WEP_INCLUDE_IV ) {
ieee80211_wep_remove_iv ( rx - > local , rx - > skb , rx - > key ) ;
/* remove ICV */
skb_trim ( rx - > skb , rx - > skb - > len - 4 ) ;
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_802_1x_pae ( struct ieee80211_txrx_data * rx )
{
if ( rx - > sdata - > eapol & & ieee80211_is_eapol ( rx - > skb ) & &
rx - > sdata - > type ! = IEEE80211_IF_TYPE_STA & & rx - > u . rx . ra_match ) {
/* Pass both encrypted and unencrypted EAPOL frames to user
* space for processing . */
if ( ! rx - > local - > apdev )
return TXRX_DROP ;
ieee80211_rx_mgmt ( rx - > local , rx - > skb , rx - > u . rx . status ,
ieee80211_msg_normal ) ;
return TXRX_QUEUED ;
}
if ( unlikely ( rx - > sdata - > ieee802_1x & &
( rx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_DATA & &
( rx - > fc & IEEE80211_FCTL_STYPE ) ! = IEEE80211_STYPE_NULLFUNC & &
( ! rx - > sta | | ! ( rx - > sta - > flags & WLAN_STA_AUTHORIZED ) ) & &
! ieee80211_is_eapol ( rx - > skb ) ) ) {
# ifdef CONFIG_MAC80211_DEBUG
struct ieee80211_hdr * hdr =
( struct ieee80211_hdr * ) rx - > skb - > data ;
printk ( KERN_DEBUG " %s: dropped frame from " MAC_FMT
" (unauthorized port) \n " , rx - > dev - > name ,
MAC_ARG ( hdr - > addr2 ) ) ;
# endif /* CONFIG_MAC80211_DEBUG */
return TXRX_DROP ;
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_drop_unencrypted ( struct ieee80211_txrx_data * rx )
{
/* If the device handles decryption totally, skip this test */
if ( rx - > local - > hw . flags & IEEE80211_HW_DEVICE_HIDES_WEP )
return TXRX_CONTINUE ;
/* Drop unencrypted frames if key is set. */
if ( unlikely ( ! ( rx - > fc & IEEE80211_FCTL_PROTECTED ) & &
( rx - > fc & IEEE80211_FCTL_FTYPE ) = = IEEE80211_FTYPE_DATA & &
( rx - > fc & IEEE80211_FCTL_STYPE ) ! = IEEE80211_STYPE_NULLFUNC & &
( rx - > key | | rx - > sdata - > drop_unencrypted ) & &
( rx - > sdata - > eapol = = 0 | |
! ieee80211_is_eapol ( rx - > skb ) ) ) ) {
printk ( KERN_DEBUG " %s: RX non-WEP frame, but expected "
" encryption \n " , rx - > dev - > name ) ;
return TXRX_DROP ;
}
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_mgmt ( struct ieee80211_txrx_data * rx )
{
struct ieee80211_sub_if_data * sdata ;
if ( ! rx - > u . rx . ra_match )
return TXRX_DROP ;
sdata = IEEE80211_DEV_TO_SUB_IF ( rx - > dev ) ;
if ( ( sdata - > type = = IEEE80211_IF_TYPE_STA | |
sdata - > type = = IEEE80211_IF_TYPE_IBSS ) & &
! rx - > local - > user_space_mlme ) {
ieee80211_sta_rx_mgmt ( rx - > dev , rx - > skb , rx - > u . rx . status ) ;
} else {
/* Management frames are sent to hostapd for processing */
if ( ! rx - > local - > apdev )
return TXRX_DROP ;
ieee80211_rx_mgmt ( rx - > local , rx - > skb , rx - > u . rx . status ,
ieee80211_msg_normal ) ;
}
return TXRX_QUEUED ;
}
static ieee80211_txrx_result
ieee80211_rx_h_passive_scan ( struct ieee80211_txrx_data * rx )
{
struct ieee80211_local * local = rx - > local ;
struct sk_buff * skb = rx - > skb ;
if ( unlikely ( local - > sta_scanning ! = 0 ) ) {
ieee80211_sta_rx_scan ( rx - > dev , skb , rx - > u . rx . status ) ;
return TXRX_QUEUED ;
}
if ( unlikely ( rx - > u . rx . in_scan ) ) {
/* scanning finished during invoking of handlers */
I802_DEBUG_INC ( local - > rx_handlers_drop_passive_scan ) ;
return TXRX_DROP ;
}
return TXRX_CONTINUE ;
}
static void ieee80211_rx_michael_mic_report ( struct net_device * dev ,
struct ieee80211_hdr * hdr ,
struct sta_info * sta ,
struct ieee80211_txrx_data * rx )
{
int keyidx , hdrlen ;
hdrlen = ieee80211_get_hdrlen_from_skb ( rx - > skb ) ;
if ( rx - > skb - > len > = hdrlen + 4 )
keyidx = rx - > skb - > data [ hdrlen + 3 ] > > 6 ;
else
keyidx = - 1 ;
/* TODO: verify that this is not triggered by fragmented
* frames ( hw does not verify MIC for them ) . */
printk ( KERN_DEBUG " %s: TKIP hwaccel reported Michael MIC "
" failure from " MAC_FMT " to " MAC_FMT " keyidx=%d \n " ,
dev - > name , MAC_ARG ( hdr - > addr2 ) , MAC_ARG ( hdr - > addr1 ) , keyidx ) ;
if ( ! sta ) {
/* Some hardware versions seem to generate incorrect
* Michael MIC reports ; ignore them to avoid triggering
* countermeasures . */
printk ( KERN_DEBUG " %s: ignored spurious Michael MIC "
" error for unknown address " MAC_FMT " \n " ,
dev - > name , MAC_ARG ( hdr - > addr2 ) ) ;
goto ignore ;
}
if ( ! ( rx - > fc & IEEE80211_FCTL_PROTECTED ) ) {
printk ( KERN_DEBUG " %s: ignored spurious Michael MIC "
" error for a frame with no ISWEP flag (src "
MAC_FMT " ) \n " , dev - > name , MAC_ARG ( hdr - > addr2 ) ) ;
goto ignore ;
}
if ( ( rx - > local - > hw . flags & IEEE80211_HW_WEP_INCLUDE_IV ) & &
rx - > sdata - > type = = IEEE80211_IF_TYPE_AP ) {
keyidx = ieee80211_wep_get_keyidx ( rx - > skb ) ;
/* AP with Pairwise keys support should never receive Michael
* MIC errors for non - zero keyidx because these are reserved
* for group keys and only the AP is sending real multicast
* frames in BSS . */
if ( keyidx ) {
printk ( KERN_DEBUG " %s: ignored Michael MIC error for "
" a frame with non-zero keyidx (%d) (src " MAC_FMT
" ) \n " , dev - > name , keyidx , MAC_ARG ( hdr - > addr2 ) ) ;
goto ignore ;
}
}
if ( ( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA & &
( ( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_MGMT | |
( rx - > fc & IEEE80211_FCTL_STYPE ) ! = IEEE80211_STYPE_AUTH ) ) {
printk ( KERN_DEBUG " %s: ignored spurious Michael MIC "
" error for a frame that cannot be encrypted "
" (fc=0x%04x) (src " MAC_FMT " ) \n " ,
dev - > name , rx - > fc , MAC_ARG ( hdr - > addr2 ) ) ;
goto ignore ;
}
do {
union iwreq_data wrqu ;
char * buf = kmalloc ( 128 , GFP_ATOMIC ) ;
if ( ! buf )
break ;
/* TODO: needed parameters: count, key type, TSC */
sprintf ( buf , " MLME-MICHAELMICFAILURE.indication( "
" keyid=%d %scast addr= " MAC_FMT " ) " ,
keyidx , hdr - > addr1 [ 0 ] & 0x01 ? " broad " : " uni " ,
MAC_ARG ( hdr - > addr2 ) ) ;
memset ( & wrqu , 0 , sizeof ( wrqu ) ) ;
wrqu . data . length = strlen ( buf ) ;
wireless_send_event ( rx - > dev , IWEVCUSTOM , & wrqu , buf ) ;
kfree ( buf ) ;
} while ( 0 ) ;
/* TODO: consider verifying the MIC error report with software
* implementation if we get too many spurious reports from the
* hardware . */
if ( ! rx - > local - > apdev )
goto ignore ;
ieee80211_rx_mgmt ( rx - > local , rx - > skb , rx - > u . rx . status ,
ieee80211_msg_michael_mic_failure ) ;
return ;
ignore :
dev_kfree_skb ( rx - > skb ) ;
rx - > skb = NULL ;
}
static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers (
struct ieee80211_local * local ,
ieee80211_rx_handler * handlers ,
struct ieee80211_txrx_data * rx ,
struct sta_info * sta )
{
ieee80211_rx_handler * handler ;
ieee80211_txrx_result res = TXRX_DROP ;
for ( handler = handlers ; * handler ! = NULL ; handler + + ) {
res = ( * handler ) ( rx ) ;
if ( res ! = TXRX_CONTINUE ) {
if ( res = = TXRX_DROP ) {
I802_DEBUG_INC ( local - > rx_handlers_drop ) ;
if ( sta )
sta - > rx_dropped + + ;
}
if ( res = = TXRX_QUEUED )
I802_DEBUG_INC ( local - > rx_handlers_queued ) ;
break ;
}
}
if ( res = = TXRX_DROP ) {
dev_kfree_skb ( rx - > skb ) ;
}
return res ;
}
static inline void ieee80211_invoke_rx_handlers ( struct ieee80211_local * local ,
ieee80211_rx_handler * handlers ,
struct ieee80211_txrx_data * rx ,
struct sta_info * sta )
{
if ( __ieee80211_invoke_rx_handlers ( local , handlers , rx , sta ) = =
TXRX_CONTINUE )
dev_kfree_skb ( rx - > skb ) ;
}
/*
* This is the receive path handler . It is called by a low level driver when an
* 802.11 MPDU is received from the hardware .
*/
void __ieee80211_rx ( struct ieee80211_hw * hw , struct sk_buff * skb ,
struct ieee80211_rx_status * status )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct ieee80211_sub_if_data * sdata ;
struct sta_info * sta ;
struct ieee80211_hdr * hdr ;
struct ieee80211_txrx_data rx ;
u16 type ;
int multicast ;
int radiotap_len = 0 ;
if ( status - > flag & RX_FLAG_RADIOTAP ) {
radiotap_len = ieee80211_get_radiotap_len ( skb ) ;
skb_pull ( skb , radiotap_len ) ;
}
hdr = ( struct ieee80211_hdr * ) skb - > data ;
memset ( & rx , 0 , sizeof ( rx ) ) ;
rx . skb = skb ;
rx . local = local ;
rx . u . rx . status = status ;
rx . fc = skb - > len > = 2 ? le16_to_cpu ( hdr - > frame_control ) : 0 ;
type = rx . fc & IEEE80211_FCTL_FTYPE ;
if ( type = = IEEE80211_FTYPE_DATA | | type = = IEEE80211_FTYPE_MGMT )
local - > dot11ReceivedFragmentCount + + ;
multicast = is_multicast_ether_addr ( hdr - > addr1 ) ;
if ( skb - > len > = 16 )
sta = rx . sta = sta_info_get ( local , hdr - > addr2 ) ;
else
sta = rx . sta = NULL ;
if ( sta ) {
rx . dev = sta - > dev ;
rx . sdata = IEEE80211_DEV_TO_SUB_IF ( rx . dev ) ;
}
if ( ( status - > flag & RX_FLAG_MMIC_ERROR ) ) {
ieee80211_rx_michael_mic_report ( local - > mdev , hdr , sta , & rx ) ;
goto end ;
}
if ( unlikely ( local - > sta_scanning ) )
rx . u . rx . in_scan = 1 ;
if ( __ieee80211_invoke_rx_handlers ( local , local - > rx_pre_handlers , & rx ,
sta ) ! = TXRX_CONTINUE )
goto end ;
skb = rx . skb ;
skb_push ( skb , radiotap_len ) ;
if ( sta & & ! sta - > assoc_ap & & ! ( sta - > flags & WLAN_STA_WDS ) & &
! local - > iff_promiscs & & ! multicast ) {
rx . u . rx . ra_match = 1 ;
ieee80211_invoke_rx_handlers ( local , local - > rx_handlers , & rx ,
sta ) ;
} else {
struct ieee80211_sub_if_data * prev = NULL ;
struct sk_buff * skb_new ;
u8 * bssid = ieee80211_get_bssid ( hdr , skb - > len - radiotap_len ) ;
read_lock ( & local - > sub_if_lock ) ;
list_for_each_entry ( sdata , & local - > sub_if_list , list ) {
rx . u . rx . ra_match = 1 ;
switch ( sdata - > type ) {
case IEEE80211_IF_TYPE_STA :
if ( ! bssid )
continue ;
if ( ! ieee80211_bssid_match ( bssid ,
sdata - > u . sta . bssid ) ) {
if ( ! rx . u . rx . in_scan )
continue ;
rx . u . rx . ra_match = 0 ;
} else if ( ! multicast & &
compare_ether_addr ( sdata - > dev - > dev_addr ,
hdr - > addr1 ) ! = 0 ) {
if ( ! sdata - > promisc )
continue ;
rx . u . rx . ra_match = 0 ;
}
break ;
case IEEE80211_IF_TYPE_IBSS :
if ( ! bssid )
continue ;
if ( ! ieee80211_bssid_match ( bssid ,
sdata - > u . sta . bssid ) ) {
if ( ! rx . u . rx . in_scan )
continue ;
rx . u . rx . ra_match = 0 ;
} else if ( ! multicast & &
compare_ether_addr ( sdata - > dev - > dev_addr ,
hdr - > addr1 ) ! = 0 ) {
if ( ! sdata - > promisc )
continue ;
rx . u . rx . ra_match = 0 ;
} else if ( ! sta )
sta = rx . sta =
ieee80211_ibss_add_sta ( sdata - > dev ,
skb , bssid ,
hdr - > addr2 ) ;
break ;
case IEEE80211_IF_TYPE_AP :
if ( ! bssid ) {
if ( compare_ether_addr ( sdata - > dev - > dev_addr ,
hdr - > addr1 ) ! = 0 )
continue ;
} else if ( ! ieee80211_bssid_match ( bssid ,
sdata - > dev - > dev_addr ) ) {
if ( ! rx . u . rx . in_scan )
continue ;
rx . u . rx . ra_match = 0 ;
}
if ( sdata - > dev = = local - > mdev & &
! rx . u . rx . in_scan )
/* do not receive anything via
* master device when not scanning */
continue ;
break ;
case IEEE80211_IF_TYPE_WDS :
if ( bssid | |
( rx . fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA )
continue ;
if ( compare_ether_addr ( sdata - > u . wds . remote_addr ,
hdr - > addr2 ) ! = 0 )
continue ;
break ;
}
if ( prev ) {
skb_new = skb_copy ( skb , GFP_ATOMIC ) ;
if ( ! skb_new ) {
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " %s: failed to copy "
" multicast frame for %s " ,
local - > mdev - > name , prev - > dev - > name ) ;
continue ;
}
rx . skb = skb_new ;
rx . dev = prev - > dev ;
rx . sdata = prev ;
ieee80211_invoke_rx_handlers ( local ,
local - > rx_handlers ,
& rx , sta ) ;
}
prev = sdata ;
}
if ( prev ) {
rx . skb = skb ;
rx . dev = prev - > dev ;
rx . sdata = prev ;
ieee80211_invoke_rx_handlers ( local , local - > rx_handlers ,
& rx , sta ) ;
} else
dev_kfree_skb ( skb ) ;
read_unlock ( & local - > sub_if_lock ) ;
}
end :
if ( sta )
sta_info_put ( sta ) ;
}
EXPORT_SYMBOL ( __ieee80211_rx ) ;
static ieee80211_txrx_result
ieee80211_tx_h_load_stats ( struct ieee80211_txrx_data * tx )
{
struct ieee80211_local * local = tx - > local ;
struct ieee80211_hw_mode * mode = tx - > u . tx . mode ;
struct sk_buff * skb = tx - > skb ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
u32 load = 0 , hdrtime ;
/* TODO: this could be part of tx_status handling, so that the number
* of retries would be known ; TX rate should in that case be stored
* somewhere with the packet */
/* Estimate total channel use caused by this frame */
/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
* 1 usec = 1 / 8 * ( 1080 / 10 ) = 13.5 */
if ( mode - > mode = = MODE_IEEE80211A | |
mode - > mode = = MODE_ATHEROS_TURBO | |
mode - > mode = = MODE_ATHEROS_TURBOG | |
( mode - > mode = = MODE_IEEE80211G & &
tx - > u . tx . rate - > flags & IEEE80211_RATE_ERP ) )
hdrtime = CHAN_UTIL_HDR_SHORT ;
else
hdrtime = CHAN_UTIL_HDR_LONG ;
load = hdrtime ;
if ( ! is_multicast_ether_addr ( hdr - > addr1 ) )
load + = hdrtime ;
if ( tx - > u . tx . control - > flags & IEEE80211_TXCTL_USE_RTS_CTS )
load + = 2 * hdrtime ;
else if ( tx - > u . tx . control - > flags & IEEE80211_TXCTL_USE_CTS_PROTECT )
load + = hdrtime ;
load + = skb - > len * tx - > u . tx . rate - > rate_inv ;
if ( tx - > u . tx . extra_frag ) {
int i ;
for ( i = 0 ; i < tx - > u . tx . num_extra_frag ; i + + ) {
load + = 2 * hdrtime ;
load + = tx - > u . tx . extra_frag [ i ] - > len *
tx - > u . tx . rate - > rate ;
}
}
/* Divide channel_use by 8 to avoid wrapping around the counter */
load > > = CHAN_UTIL_SHIFT ;
local - > channel_use_raw + = load ;
if ( tx - > sta )
tx - > sta - > channel_use_raw + = load ;
tx - > sdata - > channel_use_raw + = load ;
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_load_stats ( struct ieee80211_txrx_data * rx )
{
struct ieee80211_local * local = rx - > local ;
struct sk_buff * skb = rx - > skb ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
u32 load = 0 , hdrtime ;
struct ieee80211_rate * rate ;
struct ieee80211_hw_mode * mode = local - > hw . conf . mode ;
int i ;
/* Estimate total channel use caused by this frame */
if ( unlikely ( mode - > num_rates < 0 ) )
return TXRX_CONTINUE ;
rate = & mode - > rates [ 0 ] ;
for ( i = 0 ; i < mode - > num_rates ; i + + ) {
if ( mode - > rates [ i ] . val = = rx - > u . rx . status - > rate ) {
rate = & mode - > rates [ i ] ;
break ;
}
}
/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
* 1 usec = 1 / 8 * ( 1080 / 10 ) = 13.5 */
if ( mode - > mode = = MODE_IEEE80211A | |
mode - > mode = = MODE_ATHEROS_TURBO | |
mode - > mode = = MODE_ATHEROS_TURBOG | |
( mode - > mode = = MODE_IEEE80211G & &
rate - > flags & IEEE80211_RATE_ERP ) )
hdrtime = CHAN_UTIL_HDR_SHORT ;
else
hdrtime = CHAN_UTIL_HDR_LONG ;
load = hdrtime ;
if ( ! is_multicast_ether_addr ( hdr - > addr1 ) )
load + = hdrtime ;
load + = skb - > len * rate - > rate_inv ;
/* Divide channel_use by 8 to avoid wrapping around the counter */
load > > = CHAN_UTIL_SHIFT ;
local - > channel_use_raw + = load ;
if ( rx - > sta )
rx - > sta - > channel_use_raw + = load ;
rx - > u . rx . load = load ;
return TXRX_CONTINUE ;
}
static ieee80211_txrx_result
ieee80211_rx_h_if_stats ( struct ieee80211_txrx_data * rx )
{
rx - > sdata - > channel_use_raw + = rx - > u . rx . load ;
return TXRX_CONTINUE ;
}
static void ieee80211_stat_refresh ( unsigned long data )
{
struct ieee80211_local * local = ( struct ieee80211_local * ) data ;
struct sta_info * sta ;
struct ieee80211_sub_if_data * sdata ;
if ( ! local - > stat_time )
return ;
/* go through all stations */
spin_lock_bh ( & local - > sta_lock ) ;
list_for_each_entry ( sta , & local - > sta_list , list ) {
sta - > channel_use = ( sta - > channel_use_raw / local - > stat_time ) /
CHAN_UTIL_PER_10MS ;
sta - > channel_use_raw = 0 ;
}
spin_unlock_bh ( & local - > sta_lock ) ;
/* go through all subinterfaces */
read_lock ( & local - > sub_if_lock ) ;
list_for_each_entry ( sdata , & local - > sub_if_list , list ) {
sdata - > channel_use = ( sdata - > channel_use_raw /
local - > stat_time ) / CHAN_UTIL_PER_10MS ;
sdata - > channel_use_raw = 0 ;
}
read_unlock ( & local - > sub_if_lock ) ;
/* hardware interface */
local - > channel_use = ( local - > channel_use_raw /
local - > stat_time ) / CHAN_UTIL_PER_10MS ;
local - > channel_use_raw = 0 ;
local - > stat_timer . expires = jiffies + HZ * local - > stat_time / 100 ;
add_timer ( & local - > stat_timer ) ;
}
/* This is a version of the rx handler that can be called from hard irq
* context . Post the skb on the queue and schedule the tasklet */
void ieee80211_rx_irqsafe ( struct ieee80211_hw * hw , struct sk_buff * skb ,
struct ieee80211_rx_status * status )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
BUILD_BUG_ON ( sizeof ( struct ieee80211_rx_status ) > sizeof ( skb - > cb ) ) ;
skb - > dev = local - > mdev ;
/* copy status into skb->cb for use by tasklet */
memcpy ( skb - > cb , status , sizeof ( * status ) ) ;
skb - > pkt_type = IEEE80211_RX_MSG ;
skb_queue_tail ( & local - > skb_queue , skb ) ;
tasklet_schedule ( & local - > tasklet ) ;
}
EXPORT_SYMBOL ( ieee80211_rx_irqsafe ) ;
void ieee80211_tx_status_irqsafe ( struct ieee80211_hw * hw ,
struct sk_buff * skb ,
struct ieee80211_tx_status * status )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct ieee80211_tx_status * saved ;
int tmp ;
skb - > dev = local - > mdev ;
saved = kmalloc ( sizeof ( struct ieee80211_tx_status ) , GFP_ATOMIC ) ;
if ( unlikely ( ! saved ) ) {
if ( net_ratelimit ( ) )
printk ( KERN_WARNING " %s: Not enough memory, "
" dropping tx status " , skb - > dev - > name ) ;
/* should be dev_kfree_skb_irq, but due to this function being
* named _irqsafe instead of just _irq we can ' t be sure that
* people won ' t call it from non - irq contexts */
dev_kfree_skb_any ( skb ) ;
return ;
}
memcpy ( saved , status , sizeof ( struct ieee80211_tx_status ) ) ;
/* copy pointer to saved status into skb->cb for use by tasklet */
memcpy ( skb - > cb , & saved , sizeof ( saved ) ) ;
skb - > pkt_type = IEEE80211_TX_STATUS_MSG ;
skb_queue_tail ( status - > control . flags & IEEE80211_TXCTL_REQ_TX_STATUS ?
& local - > skb_queue : & local - > skb_queue_unreliable , skb ) ;
tmp = skb_queue_len ( & local - > skb_queue ) +
skb_queue_len ( & local - > skb_queue_unreliable ) ;
while ( tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT & &
( skb = skb_dequeue ( & local - > skb_queue_unreliable ) ) ) {
memcpy ( & saved , skb - > cb , sizeof ( saved ) ) ;
kfree ( saved ) ;
dev_kfree_skb_irq ( skb ) ;
tmp - - ;
I802_DEBUG_INC ( local - > tx_status_drop ) ;
}
tasklet_schedule ( & local - > tasklet ) ;
}
EXPORT_SYMBOL ( ieee80211_tx_status_irqsafe ) ;
static void ieee80211_tasklet_handler ( unsigned long data )
{
struct ieee80211_local * local = ( struct ieee80211_local * ) data ;
struct sk_buff * skb ;
struct ieee80211_rx_status rx_status ;
struct ieee80211_tx_status * tx_status ;
while ( ( skb = skb_dequeue ( & local - > skb_queue ) ) | |
( skb = skb_dequeue ( & local - > skb_queue_unreliable ) ) ) {
switch ( skb - > pkt_type ) {
case IEEE80211_RX_MSG :
/* status is in skb->cb */
memcpy ( & rx_status , skb - > cb , sizeof ( rx_status ) ) ;
/* Clear skb->type in order to not confuse kernel
* netstack . */
skb - > pkt_type = 0 ;
__ieee80211_rx ( local_to_hw ( local ) , skb , & rx_status ) ;
break ;
case IEEE80211_TX_STATUS_MSG :
/* get pointer to saved status out of skb->cb */
memcpy ( & tx_status , skb - > cb , sizeof ( tx_status ) ) ;
skb - > pkt_type = 0 ;
ieee80211_tx_status ( local_to_hw ( local ) ,
skb , tx_status ) ;
kfree ( tx_status ) ;
break ;
default : /* should never get here! */
printk ( KERN_ERR " %s: Unknown message type (%d) \n " ,
local - > mdev - > name , skb - > pkt_type ) ;
dev_kfree_skb ( skb ) ;
break ;
}
}
}
/* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to
* make a prepared TX frame ( one that has been given to hw ) to look like brand
* new IEEE 802.11 frame that is ready to go through TX processing again .
* Also , tx_packet_data in cb is restored from tx_control . */
static void ieee80211_remove_tx_extra ( struct ieee80211_local * local ,
struct ieee80211_key * key ,
struct sk_buff * skb ,
struct ieee80211_tx_control * control )
{
int hdrlen , iv_len , mic_len ;
struct ieee80211_tx_packet_data * pkt_data ;
pkt_data = ( struct ieee80211_tx_packet_data * ) skb - > cb ;
pkt_data - > ifindex = control - > ifindex ;
pkt_data - > mgmt_iface = ( control - > type = = IEEE80211_IF_TYPE_MGMT ) ;
pkt_data - > req_tx_status = ! ! ( control - > flags & IEEE80211_TXCTL_REQ_TX_STATUS ) ;
pkt_data - > do_not_encrypt = ! ! ( control - > flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT ) ;
pkt_data - > requeue = ! ! ( control - > flags & IEEE80211_TXCTL_REQUEUE ) ;
pkt_data - > queue = control - > queue ;
hdrlen = ieee80211_get_hdrlen_from_skb ( skb ) ;
if ( ! key )
goto no_key ;
switch ( key - > alg ) {
case ALG_WEP :
iv_len = WEP_IV_LEN ;
mic_len = WEP_ICV_LEN ;
break ;
case ALG_TKIP :
iv_len = TKIP_IV_LEN ;
mic_len = TKIP_ICV_LEN ;
break ;
case ALG_CCMP :
iv_len = CCMP_HDR_LEN ;
mic_len = CCMP_MIC_LEN ;
break ;
default :
goto no_key ;
}
if ( skb - > len > = mic_len & & key - > force_sw_encrypt )
skb_trim ( skb , skb - > len - mic_len ) ;
if ( skb - > len > = iv_len & & skb - > len > hdrlen ) {
memmove ( skb - > data + iv_len , skb - > data , hdrlen ) ;
skb_pull ( skb , iv_len ) ;
}
no_key :
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
u16 fc = le16_to_cpu ( hdr - > frame_control ) ;
if ( ( fc & 0x8C ) = = 0x88 ) /* QoS Control Field */ {
fc & = ~ IEEE80211_STYPE_QOS_DATA ;
hdr - > frame_control = cpu_to_le16 ( fc ) ;
memmove ( skb - > data + 2 , skb - > data , hdrlen - 2 ) ;
skb_pull ( skb , 2 ) ;
}
}
}
void ieee80211_tx_status ( struct ieee80211_hw * hw , struct sk_buff * skb ,
struct ieee80211_tx_status * status )
{
struct sk_buff * skb2 ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
struct ieee80211_local * local = hw_to_local ( hw ) ;
u16 frag , type ;
u32 msg_type ;
2007-07-10 19:32:08 +02:00
struct ieee80211_tx_status_rtap_hdr * rthdr ;
struct ieee80211_sub_if_data * sdata ;
int monitors ;
2007-05-05 11:45:53 -07:00
if ( ! status ) {
printk ( KERN_ERR
" %s: ieee80211_tx_status called with NULL status \n " ,
local - > mdev - > name ) ;
dev_kfree_skb ( skb ) ;
return ;
}
if ( status - > excessive_retries ) {
struct sta_info * sta ;
sta = sta_info_get ( local , hdr - > addr1 ) ;
if ( sta ) {
if ( sta - > flags & WLAN_STA_PS ) {
/* The STA is in power save mode, so assume
* that this TX packet failed because of that .
*/
status - > excessive_retries = 0 ;
status - > flags | = IEEE80211_TX_STATUS_TX_FILTERED ;
}
sta_info_put ( sta ) ;
}
}
if ( status - > flags & IEEE80211_TX_STATUS_TX_FILTERED ) {
struct sta_info * sta ;
sta = sta_info_get ( local , hdr - > addr1 ) ;
if ( sta ) {
sta - > tx_filtered_count + + ;
/* Clear the TX filter mask for this STA when sending
* the next packet . If the STA went to power save mode ,
* this will happen when it is waking up for the next
* time . */
sta - > clear_dst_mask = 1 ;
/* TODO: Is the WLAN_STA_PS flag always set here or is
* the race between RX and TX status causing some
* packets to be filtered out before 80211. o gets an
* update for PS status ? This seems to be the case , so
* no changes are likely to be needed . */
if ( sta - > flags & WLAN_STA_PS & &
skb_queue_len ( & sta - > tx_filtered ) <
STA_MAX_TX_BUFFER ) {
ieee80211_remove_tx_extra ( local , sta - > key ,
skb ,
& status - > control ) ;
skb_queue_tail ( & sta - > tx_filtered , skb ) ;
} else if ( ! ( sta - > flags & WLAN_STA_PS ) & &
! ( status - > control . flags & IEEE80211_TXCTL_REQUEUE ) ) {
/* Software retry the packet once */
status - > control . flags | = IEEE80211_TXCTL_REQUEUE ;
ieee80211_remove_tx_extra ( local , sta - > key ,
skb ,
& status - > control ) ;
dev_queue_xmit ( skb ) ;
} else {
if ( net_ratelimit ( ) ) {
printk ( KERN_DEBUG " %s: dropped TX "
" filtered frame queue_len=%d "
" PS=%d @%lu \n " ,
local - > mdev - > name ,
skb_queue_len (
& sta - > tx_filtered ) ,
! ! ( sta - > flags & WLAN_STA_PS ) ,
jiffies ) ;
}
dev_kfree_skb ( skb ) ;
}
sta_info_put ( sta ) ;
return ;
}
} else {
/* FIXME: STUPID to call this with both local and local->mdev */
rate_control_tx_status ( local , local - > mdev , skb , status ) ;
}
ieee80211_led_tx ( local , 0 ) ;
/* SNMP counters
* Fragments are passed to low - level drivers as separate skbs , so these
* are actually fragments , not frames . Update frame counters only for
* the first fragment of the frame . */
frag = le16_to_cpu ( hdr - > seq_ctrl ) & IEEE80211_SCTL_FRAG ;
type = le16_to_cpu ( hdr - > frame_control ) & IEEE80211_FCTL_FTYPE ;
if ( status - > flags & IEEE80211_TX_STATUS_ACK ) {
if ( frag = = 0 ) {
local - > dot11TransmittedFrameCount + + ;
if ( is_multicast_ether_addr ( hdr - > addr1 ) )
local - > dot11MulticastTransmittedFrameCount + + ;
if ( status - > retry_count > 0 )
local - > dot11RetryCount + + ;
if ( status - > retry_count > 1 )
local - > dot11MultipleRetryCount + + ;
}
/* This counter shall be incremented for an acknowledged MPDU
* with an individual address in the address 1 field or an MPDU
* with a multicast address in the address 1 field of type Data
* or Management . */
if ( ! is_multicast_ether_addr ( hdr - > addr1 ) | |
type = = IEEE80211_FTYPE_DATA | |
type = = IEEE80211_FTYPE_MGMT )
local - > dot11TransmittedFragmentCount + + ;
} else {
if ( frag = = 0 )
local - > dot11FailedCount + + ;
}
2007-07-10 19:32:08 +02:00
msg_type = ( status - > flags & IEEE80211_TX_STATUS_ACK ) ?
ieee80211_msg_tx_callback_ack : ieee80211_msg_tx_callback_fail ;
/* this was a transmitted frame, but now we want to reuse it */
skb_orphan ( skb ) ;
if ( ( status - > control . flags & IEEE80211_TXCTL_REQ_TX_STATUS ) & &
local - > apdev ) {
if ( local - > monitors ) {
skb2 = skb_clone ( skb , GFP_ATOMIC ) ;
} else {
skb2 = skb ;
skb = NULL ;
}
if ( skb2 )
/* Send frame to hostapd */
ieee80211_rx_mgmt ( local , skb2 , NULL , msg_type ) ;
if ( ! skb )
return ;
}
if ( ! local - > monitors ) {
2007-05-05 11:45:53 -07:00
dev_kfree_skb ( skb ) ;
return ;
}
2007-07-10 19:32:08 +02:00
/* send frame to monitor interfaces now */
2007-05-05 11:45:53 -07:00
2007-07-10 19:32:08 +02:00
if ( skb_headroom ( skb ) < sizeof ( * rthdr ) ) {
printk ( KERN_ERR " ieee80211_tx_status: headroom too small \n " ) ;
2007-05-05 11:45:53 -07:00
dev_kfree_skb ( skb ) ;
return ;
}
2007-07-10 19:32:08 +02:00
rthdr = ( struct ieee80211_tx_status_rtap_hdr * )
skb_push ( skb , sizeof ( * rthdr ) ) ;
memset ( rthdr , 0 , sizeof ( * rthdr ) ) ;
rthdr - > hdr . it_len = cpu_to_le16 ( sizeof ( * rthdr ) ) ;
rthdr - > hdr . it_present =
cpu_to_le32 ( ( 1 < < IEEE80211_RADIOTAP_TX_FLAGS ) |
( 1 < < IEEE80211_RADIOTAP_DATA_RETRIES ) ) ;
if ( ! ( status - > flags & IEEE80211_TX_STATUS_ACK ) & &
! is_multicast_ether_addr ( hdr - > addr1 ) )
rthdr - > tx_flags | = cpu_to_le16 ( IEEE80211_RADIOTAP_F_TX_FAIL ) ;
if ( ( status - > control . flags & IEEE80211_TXCTL_USE_RTS_CTS ) & &
( status - > control . flags & IEEE80211_TXCTL_USE_CTS_PROTECT ) )
rthdr - > tx_flags | = cpu_to_le16 ( IEEE80211_RADIOTAP_F_TX_CTS ) ;
else if ( status - > control . flags & IEEE80211_TXCTL_USE_RTS_CTS )
rthdr - > tx_flags | = cpu_to_le16 ( IEEE80211_RADIOTAP_F_TX_RTS ) ;
rthdr - > data_retries = status - > retry_count ;
read_lock ( & local - > sub_if_lock ) ;
monitors = local - > monitors ;
list_for_each_entry ( sdata , & local - > sub_if_list , list ) {
/*
* Using the monitors counter is possibly racy , but
* if the value is wrong we simply either clone the skb
* once too much or forget sending it to one monitor iface
* The latter case isn ' t nice but fixing the race is much
* more complicated .
*/
if ( ! monitors | | ! skb )
goto out ;
if ( sdata - > type = = IEEE80211_IF_TYPE_MNTR ) {
if ( ! netif_running ( sdata - > dev ) )
continue ;
monitors - - ;
if ( monitors )
skb2 = skb_clone ( skb , GFP_KERNEL ) ;
else
skb2 = NULL ;
skb - > dev = sdata - > dev ;
/* XXX: is this sufficient for BPF? */
skb_set_mac_header ( skb , 0 ) ;
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
skb - > pkt_type = PACKET_OTHERHOST ;
skb - > protocol = htons ( ETH_P_802_2 ) ;
memset ( skb - > cb , 0 , sizeof ( skb - > cb ) ) ;
netif_rx ( skb ) ;
skb = skb2 ;
break ;
}
}
out :
read_unlock ( & local - > sub_if_lock ) ;
if ( skb )
dev_kfree_skb ( skb ) ;
2007-05-05 11:45:53 -07:00
}
EXPORT_SYMBOL ( ieee80211_tx_status ) ;
/* TODO: implement register/unregister functions for adding TX/RX handlers
* into ordered list */
/* rx_pre handlers don't have dev and sdata fields available in
* ieee80211_txrx_data */
static ieee80211_rx_handler ieee80211_rx_pre_handlers [ ] =
{
ieee80211_rx_h_parse_qos ,
ieee80211_rx_h_load_stats ,
NULL
} ;
static ieee80211_rx_handler ieee80211_rx_handlers [ ] =
{
ieee80211_rx_h_if_stats ,
ieee80211_rx_h_monitor ,
ieee80211_rx_h_passive_scan ,
ieee80211_rx_h_check ,
ieee80211_rx_h_sta_process ,
ieee80211_rx_h_ccmp_decrypt ,
ieee80211_rx_h_tkip_decrypt ,
ieee80211_rx_h_wep_weak_iv_detection ,
ieee80211_rx_h_wep_decrypt ,
ieee80211_rx_h_defragment ,
ieee80211_rx_h_ps_poll ,
ieee80211_rx_h_michael_mic_verify ,
/* this must be after decryption - so header is counted in MPDU mic
* must be before pae and data , so QOS_DATA format frames
* are not passed to user space by these functions
*/
ieee80211_rx_h_remove_qos_control ,
ieee80211_rx_h_802_1x_pae ,
ieee80211_rx_h_drop_unencrypted ,
ieee80211_rx_h_data ,
ieee80211_rx_h_mgmt ,
NULL
} ;
static ieee80211_tx_handler ieee80211_tx_handlers [ ] =
{
ieee80211_tx_h_check_assoc ,
ieee80211_tx_h_sequence ,
ieee80211_tx_h_ps_buf ,
ieee80211_tx_h_select_key ,
ieee80211_tx_h_michael_mic_add ,
ieee80211_tx_h_fragment ,
ieee80211_tx_h_tkip_encrypt ,
ieee80211_tx_h_ccmp_encrypt ,
ieee80211_tx_h_wep_encrypt ,
ieee80211_tx_h_rate_ctrl ,
ieee80211_tx_h_misc ,
ieee80211_tx_h_load_stats ,
NULL
} ;
int ieee80211_if_update_wds ( struct net_device * dev , u8 * remote_addr )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
struct sta_info * sta ;
if ( compare_ether_addr ( remote_addr , sdata - > u . wds . remote_addr ) = = 0 )
return 0 ;
/* Create STA entry for the new peer */
sta = sta_info_add ( local , dev , remote_addr , GFP_KERNEL ) ;
if ( ! sta )
return - ENOMEM ;
sta_info_put ( sta ) ;
/* Remove STA entry for the old peer */
sta = sta_info_get ( local , sdata - > u . wds . remote_addr ) ;
if ( sta ) {
sta_info_put ( sta ) ;
sta_info_free ( sta , 0 ) ;
} else {
printk ( KERN_DEBUG " %s: could not find STA entry for WDS link "
" peer " MAC_FMT " \n " ,
dev - > name , MAC_ARG ( sdata - > u . wds . remote_addr ) ) ;
}
/* Update WDS link data */
memcpy ( & sdata - > u . wds . remote_addr , remote_addr , ETH_ALEN ) ;
return 0 ;
}
/* Must not be called for mdev and apdev */
void ieee80211_if_setup ( struct net_device * dev )
{
ether_setup ( dev ) ;
dev - > hard_start_xmit = ieee80211_subif_start_xmit ;
dev - > wireless_handlers = & ieee80211_iw_handler_def ;
dev - > set_multicast_list = ieee80211_set_multicast_list ;
dev - > change_mtu = ieee80211_change_mtu ;
dev - > get_stats = ieee80211_get_stats ;
dev - > open = ieee80211_open ;
dev - > stop = ieee80211_stop ;
dev - > uninit = ieee80211_if_reinit ;
dev - > destructor = ieee80211_if_free ;
}
void ieee80211_if_mgmt_setup ( struct net_device * dev )
{
ether_setup ( dev ) ;
dev - > hard_start_xmit = ieee80211_mgmt_start_xmit ;
dev - > change_mtu = ieee80211_change_mtu_apdev ;
dev - > get_stats = ieee80211_get_stats ;
dev - > open = ieee80211_mgmt_open ;
dev - > stop = ieee80211_mgmt_stop ;
dev - > type = ARPHRD_IEEE80211_PRISM ;
dev - > hard_header_parse = header_parse_80211 ;
dev - > uninit = ieee80211_if_reinit ;
dev - > destructor = ieee80211_if_free ;
}
int ieee80211_init_rate_ctrl_alg ( struct ieee80211_local * local ,
const char * name )
{
struct rate_control_ref * ref , * old ;
ASSERT_RTNL ( ) ;
if ( local - > open_count | | netif_running ( local - > mdev ) | |
( local - > apdev & & netif_running ( local - > apdev ) ) )
return - EBUSY ;
ref = rate_control_alloc ( name , local ) ;
if ( ! ref ) {
printk ( KERN_WARNING " %s: Failed to select rate control "
" algorithm \n " , local - > mdev - > name ) ;
return - ENOENT ;
}
old = local - > rate_ctrl ;
local - > rate_ctrl = ref ;
if ( old ) {
rate_control_put ( old ) ;
sta_info_flush ( local , NULL ) ;
}
printk ( KERN_DEBUG " %s: Selected rate control "
" algorithm '%s' \n " , local - > mdev - > name ,
ref - > ops - > name ) ;
return 0 ;
}
static void rate_control_deinitialize ( struct ieee80211_local * local )
{
struct rate_control_ref * ref ;
ref = local - > rate_ctrl ;
local - > rate_ctrl = NULL ;
rate_control_put ( ref ) ;
}
struct ieee80211_hw * ieee80211_alloc_hw ( size_t priv_data_len ,
const struct ieee80211_ops * ops )
{
struct net_device * mdev ;
struct ieee80211_local * local ;
struct ieee80211_sub_if_data * sdata ;
int priv_size ;
struct wiphy * wiphy ;
/* Ensure 32-byte alignment of our private data and hw private data.
* We use the wiphy priv data for both our ieee80211_local and for
* the driver ' s private data
*
* In memory it ' ll be like this :
*
* + - - - - - - - - - - - - - - - - - - - - - - - - - +
* | struct wiphy |
* + - - - - - - - - - - - - - - - - - - - - - - - - - +
* | struct ieee80211_local |
* + - - - - - - - - - - - - - - - - - - - - - - - - - +
* | driver ' s private data |
* + - - - - - - - - - - - - - - - - - - - - - - - - - +
*
*/
priv_size = ( ( sizeof ( struct ieee80211_local ) +
NETDEV_ALIGN_CONST ) & ~ NETDEV_ALIGN_CONST ) +
priv_data_len ;
wiphy = wiphy_new ( & mac80211_config_ops , priv_size ) ;
if ( ! wiphy )
return NULL ;
wiphy - > privid = mac80211_wiphy_privid ;
local = wiphy_priv ( wiphy ) ;
local - > hw . wiphy = wiphy ;
local - > hw . priv = ( char * ) local +
( ( sizeof ( struct ieee80211_local ) +
NETDEV_ALIGN_CONST ) & ~ NETDEV_ALIGN_CONST ) ;
2007-07-10 19:32:10 +02:00
BUG_ON ( ! ops - > tx ) ;
BUG_ON ( ! ops - > config ) ;
BUG_ON ( ! ops - > add_interface ) ;
2007-05-05 11:45:53 -07:00
local - > ops = ops ;
/* for now, mdev needs sub_if_data :/ */
mdev = alloc_netdev ( sizeof ( struct ieee80211_sub_if_data ) ,
" wmaster%d " , ether_setup ) ;
if ( ! mdev ) {
wiphy_free ( wiphy ) ;
return NULL ;
}
sdata = IEEE80211_DEV_TO_SUB_IF ( mdev ) ;
mdev - > ieee80211_ptr = & sdata - > wdev ;
sdata - > wdev . wiphy = wiphy ;
local - > hw . queues = 1 ; /* default */
local - > mdev = mdev ;
local - > rx_pre_handlers = ieee80211_rx_pre_handlers ;
local - > rx_handlers = ieee80211_rx_handlers ;
local - > tx_handlers = ieee80211_tx_handlers ;
local - > bridge_packets = 1 ;
local - > rts_threshold = IEEE80211_MAX_RTS_THRESHOLD ;
local - > fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD ;
local - > short_retry_limit = 7 ;
local - > long_retry_limit = 4 ;
local - > hw . conf . radio_enabled = 1 ;
local - > enabled_modes = ( unsigned int ) - 1 ;
INIT_LIST_HEAD ( & local - > modes_list ) ;
rwlock_init ( & local - > sub_if_lock ) ;
INIT_LIST_HEAD ( & local - > sub_if_list ) ;
INIT_DELAYED_WORK ( & local - > scan_work , ieee80211_sta_scan_work ) ;
init_timer ( & local - > stat_timer ) ;
local - > stat_timer . function = ieee80211_stat_refresh ;
local - > stat_timer . data = ( unsigned long ) local ;
ieee80211_rx_bss_list_init ( mdev ) ;
sta_info_init ( local ) ;
mdev - > hard_start_xmit = ieee80211_master_start_xmit ;
mdev - > open = ieee80211_master_open ;
mdev - > stop = ieee80211_master_stop ;
mdev - > type = ARPHRD_IEEE80211 ;
mdev - > hard_header_parse = header_parse_80211 ;
sdata - > type = IEEE80211_IF_TYPE_AP ;
sdata - > dev = mdev ;
sdata - > local = local ;
sdata - > u . ap . force_unicast_rateidx = - 1 ;
sdata - > u . ap . max_ratectrl_rateidx = - 1 ;
ieee80211_if_sdata_init ( sdata ) ;
list_add_tail ( & sdata - > list , & local - > sub_if_list ) ;
tasklet_init ( & local - > tx_pending_tasklet , ieee80211_tx_pending ,
( unsigned long ) local ) ;
tasklet_disable ( & local - > tx_pending_tasklet ) ;
tasklet_init ( & local - > tasklet ,
ieee80211_tasklet_handler ,
( unsigned long ) local ) ;
tasklet_disable ( & local - > tasklet ) ;
skb_queue_head_init ( & local - > skb_queue ) ;
skb_queue_head_init ( & local - > skb_queue_unreliable ) ;
return local_to_hw ( local ) ;
}
EXPORT_SYMBOL ( ieee80211_alloc_hw ) ;
int ieee80211_register_hw ( struct ieee80211_hw * hw )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
const char * name ;
int result ;
result = wiphy_register ( local - > hw . wiphy ) ;
if ( result < 0 )
return result ;
name = wiphy_dev ( local - > hw . wiphy ) - > driver - > name ;
local - > hw . workqueue = create_singlethread_workqueue ( name ) ;
if ( ! local - > hw . workqueue ) {
result = - ENOMEM ;
goto fail_workqueue ;
}
2007-07-10 19:32:08 +02:00
/*
* The hardware needs headroom for sending the frame ,
* and we need some headroom for passing the frame to monitor
* interfaces , but never both at the same time .
*/
2007-07-18 17:10:44 +02:00
local - > tx_headroom = max_t ( unsigned int , local - > hw . extra_tx_headroom ,
sizeof ( struct ieee80211_tx_status_rtap_hdr ) ) ;
2007-07-10 19:32:08 +02:00
2007-05-05 11:46:38 -07:00
debugfs_hw_add ( local ) ;
2007-05-05 11:45:53 -07:00
local - > hw . conf . beacon_int = 1000 ;
local - > wstats_flags | = local - > hw . max_rssi ?
IW_QUAL_LEVEL_UPDATED : IW_QUAL_LEVEL_INVALID ;
local - > wstats_flags | = local - > hw . max_signal ?
IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID ;
local - > wstats_flags | = local - > hw . max_noise ?
IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID ;
if ( local - > hw . max_rssi < 0 | | local - > hw . max_noise < 0 )
local - > wstats_flags | = IW_QUAL_DBM ;
result = sta_info_start ( local ) ;
if ( result < 0 )
goto fail_sta_info ;
rtnl_lock ( ) ;
result = dev_alloc_name ( local - > mdev , local - > mdev - > name ) ;
if ( result < 0 )
goto fail_dev ;
memcpy ( local - > mdev - > dev_addr , local - > hw . wiphy - > perm_addr , ETH_ALEN ) ;
SET_NETDEV_DEV ( local - > mdev , wiphy_dev ( local - > hw . wiphy ) ) ;
result = register_netdevice ( local - > mdev ) ;
if ( result < 0 )
goto fail_dev ;
2007-05-05 11:46:38 -07:00
ieee80211_debugfs_add_netdev ( IEEE80211_DEV_TO_SUB_IF ( local - > mdev ) ) ;
2007-05-05 11:45:53 -07:00
result = ieee80211_init_rate_ctrl_alg ( local , NULL ) ;
if ( result < 0 ) {
printk ( KERN_DEBUG " %s: Failed to initialize rate control "
" algorithm \n " , local - > mdev - > name ) ;
goto fail_rate ;
}
result = ieee80211_wep_init ( local ) ;
if ( result < 0 ) {
printk ( KERN_DEBUG " %s: Failed to initialize wep \n " ,
local - > mdev - > name ) ;
goto fail_wep ;
}
ieee80211_install_qdisc ( local - > mdev ) ;
/* add one default STA interface */
result = ieee80211_if_add ( local - > mdev , " wlan%d " , NULL ,
IEEE80211_IF_TYPE_STA ) ;
if ( result )
printk ( KERN_WARNING " %s: Failed to add default virtual iface \n " ,
local - > mdev - > name ) ;
local - > reg_state = IEEE80211_DEV_REGISTERED ;
rtnl_unlock ( ) ;
ieee80211_led_init ( local ) ;
return 0 ;
fail_wep :
rate_control_deinitialize ( local ) ;
fail_rate :
2007-05-05 11:46:38 -07:00
ieee80211_debugfs_remove_netdev ( IEEE80211_DEV_TO_SUB_IF ( local - > mdev ) ) ;
2007-05-05 11:45:53 -07:00
unregister_netdevice ( local - > mdev ) ;
fail_dev :
rtnl_unlock ( ) ;
sta_info_stop ( local ) ;
fail_sta_info :
2007-05-05 11:46:38 -07:00
debugfs_hw_del ( local ) ;
2007-05-05 11:45:53 -07:00
destroy_workqueue ( local - > hw . workqueue ) ;
fail_workqueue :
wiphy_unregister ( local - > hw . wiphy ) ;
return result ;
}
EXPORT_SYMBOL ( ieee80211_register_hw ) ;
int ieee80211_register_hwmode ( struct ieee80211_hw * hw ,
struct ieee80211_hw_mode * mode )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct ieee80211_rate * rate ;
int i ;
INIT_LIST_HEAD ( & mode - > list ) ;
list_add_tail ( & mode - > list , & local - > modes_list ) ;
local - > hw_modes | = ( 1 < < mode - > mode ) ;
for ( i = 0 ; i < mode - > num_rates ; i + + ) {
rate = & ( mode - > rates [ i ] ) ;
rate - > rate_inv = CHAN_UTIL_RATE_LCM / rate - > rate ;
}
ieee80211_prepare_rates ( local , mode ) ;
if ( ! local - > oper_hw_mode ) {
/* Default to this mode */
local - > hw . conf . phymode = mode - > mode ;
local - > oper_hw_mode = local - > scan_hw_mode = mode ;
local - > oper_channel = local - > scan_channel = & mode - > channels [ 0 ] ;
local - > hw . conf . mode = local - > oper_hw_mode ;
local - > hw . conf . chan = local - > oper_channel ;
}
if ( ! ( hw - > flags & IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED ) )
2007-06-09 19:07:14 +01:00
ieee80211_set_default_regdomain ( mode ) ;
2007-05-05 11:45:53 -07:00
return 0 ;
}
EXPORT_SYMBOL ( ieee80211_register_hwmode ) ;
void ieee80211_unregister_hw ( struct ieee80211_hw * hw )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct ieee80211_sub_if_data * sdata , * tmp ;
struct list_head tmp_list ;
int i ;
tasklet_kill ( & local - > tx_pending_tasklet ) ;
tasklet_kill ( & local - > tasklet ) ;
rtnl_lock ( ) ;
BUG_ON ( local - > reg_state ! = IEEE80211_DEV_REGISTERED ) ;
local - > reg_state = IEEE80211_DEV_UNREGISTERED ;
if ( local - > apdev )
ieee80211_if_del_mgmt ( local ) ;
write_lock_bh ( & local - > sub_if_lock ) ;
list_replace_init ( & local - > sub_if_list , & tmp_list ) ;
write_unlock_bh ( & local - > sub_if_lock ) ;
list_for_each_entry_safe ( sdata , tmp , & tmp_list , list )
__ieee80211_if_del ( local , sdata ) ;
rtnl_unlock ( ) ;
if ( local - > stat_time )
del_timer_sync ( & local - > stat_timer ) ;
ieee80211_rx_bss_list_deinit ( local - > mdev ) ;
ieee80211_clear_tx_pending ( local ) ;
sta_info_stop ( local ) ;
rate_control_deinitialize ( local ) ;
2007-05-05 11:46:38 -07:00
debugfs_hw_del ( local ) ;
2007-05-05 11:45:53 -07:00
for ( i = 0 ; i < NUM_IEEE80211_MODES ; i + + ) {
kfree ( local - > supp_rates [ i ] ) ;
kfree ( local - > basic_rates [ i ] ) ;
}
if ( skb_queue_len ( & local - > skb_queue )
| | skb_queue_len ( & local - > skb_queue_unreliable ) )
printk ( KERN_WARNING " %s: skb_queue not empty \n " ,
local - > mdev - > name ) ;
skb_queue_purge ( & local - > skb_queue ) ;
skb_queue_purge ( & local - > skb_queue_unreliable ) ;
destroy_workqueue ( local - > hw . workqueue ) ;
wiphy_unregister ( local - > hw . wiphy ) ;
ieee80211_wep_free ( local ) ;
ieee80211_led_exit ( local ) ;
}
EXPORT_SYMBOL ( ieee80211_unregister_hw ) ;
void ieee80211_free_hw ( struct ieee80211_hw * hw )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
ieee80211_if_free ( local - > mdev ) ;
wiphy_free ( local - > hw . wiphy ) ;
}
EXPORT_SYMBOL ( ieee80211_free_hw ) ;
void ieee80211_wake_queue ( struct ieee80211_hw * hw , int queue )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
if ( test_and_clear_bit ( IEEE80211_LINK_STATE_XOFF ,
& local - > state [ queue ] ) ) {
if ( test_bit ( IEEE80211_LINK_STATE_PENDING ,
& local - > state [ queue ] ) )
tasklet_schedule ( & local - > tx_pending_tasklet ) ;
else
if ( ! ieee80211_qdisc_installed ( local - > mdev ) ) {
if ( queue = = 0 )
netif_wake_queue ( local - > mdev ) ;
} else
__netif_schedule ( local - > mdev ) ;
}
}
EXPORT_SYMBOL ( ieee80211_wake_queue ) ;
void ieee80211_stop_queue ( struct ieee80211_hw * hw , int queue )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
if ( ! ieee80211_qdisc_installed ( local - > mdev ) & & queue = = 0 )
netif_stop_queue ( local - > mdev ) ;
set_bit ( IEEE80211_LINK_STATE_XOFF , & local - > state [ queue ] ) ;
}
EXPORT_SYMBOL ( ieee80211_stop_queue ) ;
void ieee80211_start_queues ( struct ieee80211_hw * hw )
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
int i ;
for ( i = 0 ; i < local - > hw . queues ; i + + )
clear_bit ( IEEE80211_LINK_STATE_XOFF , & local - > state [ i ] ) ;
if ( ! ieee80211_qdisc_installed ( local - > mdev ) )
netif_start_queue ( local - > mdev ) ;
}
EXPORT_SYMBOL ( ieee80211_start_queues ) ;
void ieee80211_stop_queues ( struct ieee80211_hw * hw )
{
int i ;
for ( i = 0 ; i < hw - > queues ; i + + )
ieee80211_stop_queue ( hw , i ) ;
}
EXPORT_SYMBOL ( ieee80211_stop_queues ) ;
void ieee80211_wake_queues ( struct ieee80211_hw * hw )
{
int i ;
for ( i = 0 ; i < hw - > queues ; i + + )
ieee80211_wake_queue ( hw , i ) ;
}
EXPORT_SYMBOL ( ieee80211_wake_queues ) ;
struct net_device_stats * ieee80211_dev_stats ( struct net_device * dev )
{
struct ieee80211_sub_if_data * sdata ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
return & sdata - > stats ;
}
static int __init ieee80211_init ( void )
{
struct sk_buff * skb ;
int ret ;
BUILD_BUG_ON ( sizeof ( struct ieee80211_tx_packet_data ) > sizeof ( skb - > cb ) ) ;
ret = ieee80211_wme_register ( ) ;
if ( ret ) {
printk ( KERN_DEBUG " ieee80211_init: failed to "
" initialize WME (err=%d) \n " , ret ) ;
return ret ;
}
2007-05-05 11:46:38 -07:00
ieee80211_debugfs_netdev_init ( ) ;
2007-06-09 19:07:14 +01:00
ieee80211_regdomain_init ( ) ;
2007-05-05 11:46:38 -07:00
2007-05-05 11:45:53 -07:00
return 0 ;
}
static void __exit ieee80211_exit ( void )
{
ieee80211_wme_unregister ( ) ;
2007-05-05 11:46:38 -07:00
ieee80211_debugfs_netdev_exit ( ) ;
2007-05-05 11:45:53 -07:00
}
module_init ( ieee80211_init ) ;
module_exit ( ieee80211_exit ) ;
MODULE_DESCRIPTION ( " IEEE 802.11 subsystem " ) ;
MODULE_LICENSE ( " GPL " ) ;