2005-05-12 22:54:16 -04:00
/*
* Intersil Prism2 driver with Host AP ( software access point ) support
* Copyright ( c ) 2001 - 2002 , SSH Communications Security Corp and Jouni Malinen
2007-03-24 17:15:30 -07:00
* < j @ w1 . fi >
* Copyright ( c ) 2002 - 2005 , Jouni Malinen < j @ w1 . fi >
2005-05-12 22:54:16 -04:00
*
* This file is to be included into hostap . c when S / W AP functionality is
* compiled .
*
* AP : FIX :
* - if unicast Class 2 ( assoc , reassoc , disassoc ) frame received from
* unauthenticated STA , send deauth . frame ( 8802.11 : 5.5 )
* - if unicast Class 3 ( data with to / from DS , deauth , pspoll ) frame received
* from authenticated , but unassoc STA , send disassoc frame ( 8802.11 : 5.5 )
* - if unicast Class 3 received from unauthenticated STA , send deauth . frame
* ( 8802.11 : 5.5 )
*/
2006-01-14 03:09:34 +01:00
# include <linux/proc_fs.h>
# include <linux/delay.h>
# include <linux/random.h>
2009-02-11 17:17:10 -05:00
# include <linux/if_arp.h>
2006-01-14 03:09:34 +01:00
# include "hostap_wlan.h"
# include "hostap.h"
# include "hostap_ap.h"
2005-05-12 22:54:16 -04:00
static int other_ap_policy [ MAX_PARM_DEVICES ] = { AP_OTHER_AP_SKIP_ALL ,
DEF_INTS } ;
module_param_array ( other_ap_policy , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( other_ap_policy , " Other AP beacon monitoring policy (0-3) " ) ;
static int ap_max_inactivity [ MAX_PARM_DEVICES ] = { AP_MAX_INACTIVITY_SEC ,
DEF_INTS } ;
module_param_array ( ap_max_inactivity , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( ap_max_inactivity , " AP timeout (in seconds) for station "
" inactivity " ) ;
static int ap_bridge_packets [ MAX_PARM_DEVICES ] = { 1 , DEF_INTS } ;
module_param_array ( ap_bridge_packets , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( ap_bridge_packets , " Bridge packets directly between "
" stations " ) ;
static int autom_ap_wds [ MAX_PARM_DEVICES ] = { 0 , DEF_INTS } ;
module_param_array ( autom_ap_wds , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( autom_ap_wds , " Add WDS connections to other APs "
" automatically " ) ;
static struct sta_info * ap_get_sta ( struct ap_data * ap , u8 * sta ) ;
static void hostap_event_expired_sta ( struct net_device * dev ,
struct sta_info * sta ) ;
2006-11-22 14:57:56 +00:00
static void handle_add_proc_queue ( struct work_struct * work ) ;
2005-05-12 22:54:16 -04:00
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
2006-11-22 14:57:56 +00:00
static void handle_wds_oper_queue ( struct work_struct * work ) ;
2005-05-12 22:54:16 -04:00
static void prism2_send_mgmt ( struct net_device * dev ,
2005-08-14 19:08:44 -07:00
u16 type_subtype , char * body ,
2005-05-12 22:54:16 -04:00
int body_len , u8 * addr , u16 tx_cb_idx ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
# ifndef PRISM2_NO_PROCFS_DEBUG
static int ap_debug_proc_read ( char * page , char * * start , off_t off ,
int count , int * eof , void * data )
{
char * p = page ;
struct ap_data * ap = ( struct ap_data * ) data ;
if ( off ! = 0 ) {
* eof = 1 ;
return 0 ;
}
p + = sprintf ( p , " BridgedUnicastFrames=%u \n " , ap - > bridged_unicast ) ;
p + = sprintf ( p , " BridgedMulticastFrames=%u \n " , ap - > bridged_multicast ) ;
p + = sprintf ( p , " max_inactivity=%u \n " , ap - > max_inactivity / HZ ) ;
p + = sprintf ( p , " bridge_packets=%u \n " , ap - > bridge_packets ) ;
p + = sprintf ( p , " nullfunc_ack=%u \n " , ap - > nullfunc_ack ) ;
p + = sprintf ( p , " autom_ap_wds=%u \n " , ap - > autom_ap_wds ) ;
p + = sprintf ( p , " auth_algs=%u \n " , ap - > local - > auth_algs ) ;
p + = sprintf ( p , " tx_drop_nonassoc=%u \n " , ap - > tx_drop_nonassoc ) ;
return ( p - page ) ;
}
# endif /* PRISM2_NO_PROCFS_DEBUG */
static void ap_sta_hash_add ( struct ap_data * ap , struct sta_info * sta )
{
sta - > hnext = ap - > sta_hash [ STA_HASH ( sta - > addr ) ] ;
ap - > sta_hash [ STA_HASH ( sta - > addr ) ] = sta ;
}
static void ap_sta_hash_del ( struct ap_data * ap , struct sta_info * sta )
{
struct sta_info * s ;
s = ap - > sta_hash [ STA_HASH ( sta - > addr ) ] ;
if ( s = = NULL ) return ;
if ( memcmp ( s - > addr , sta - > addr , ETH_ALEN ) = = 0 ) {
ap - > sta_hash [ STA_HASH ( sta - > addr ) ] = s - > hnext ;
return ;
}
while ( s - > hnext ! = NULL & & memcmp ( s - > hnext - > addr , sta - > addr , ETH_ALEN )
! = 0 )
s = s - > hnext ;
if ( s - > hnext ! = NULL )
s - > hnext = s - > hnext - > hnext ;
else
2008-10-27 15:59:26 -07:00
printk ( " AP: could not remove STA %pM from hash table \n " ,
sta - > addr ) ;
2005-05-12 22:54:16 -04:00
}
static void ap_free_sta ( struct ap_data * ap , struct sta_info * sta )
{
if ( sta - > ap & & sta - > local )
hostap_event_expired_sta ( sta - > local - > dev , sta ) ;
if ( ap - > proc ! = NULL ) {
char name [ 20 ] ;
2008-10-27 15:59:26 -07:00
sprintf ( name , " %pM " , sta - > addr ) ;
2005-05-12 22:54:16 -04:00
remove_proc_entry ( name , ap - > proc ) ;
}
if ( sta - > crypt ) {
sta - > crypt - > ops - > deinit ( sta - > crypt - > priv ) ;
kfree ( sta - > crypt ) ;
sta - > crypt = NULL ;
}
skb_queue_purge ( & sta - > tx_buf ) ;
ap - > num_sta - - ;
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
if ( sta - > aid > 0 )
ap - > sta_aid [ sta - > aid - 1 ] = NULL ;
if ( ! sta - > ap & & sta - > u . sta . challenge )
kfree ( sta - > u . sta . challenge ) ;
del_timer ( & sta - > timer ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
kfree ( sta ) ;
}
static void hostap_set_tim ( local_info_t * local , int aid , int set )
{
if ( local - > func - > set_tim )
local - > func - > set_tim ( local - > dev , aid , set ) ;
}
static void hostap_event_new_sta ( struct net_device * dev , struct sta_info * sta )
{
union iwreq_data wrqu ;
memset ( & wrqu , 0 , sizeof ( wrqu ) ) ;
memcpy ( wrqu . addr . sa_data , sta - > addr , ETH_ALEN ) ;
wrqu . addr . sa_family = ARPHRD_ETHER ;
wireless_send_event ( dev , IWEVREGISTERED , & wrqu , NULL ) ;
}
static void hostap_event_expired_sta ( struct net_device * dev ,
struct sta_info * sta )
{
union iwreq_data wrqu ;
memset ( & wrqu , 0 , sizeof ( wrqu ) ) ;
memcpy ( wrqu . addr . sa_data , sta - > addr , ETH_ALEN ) ;
wrqu . addr . sa_family = ARPHRD_ETHER ;
wireless_send_event ( dev , IWEVEXPIRED , & wrqu , NULL ) ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
static void ap_handle_timer ( unsigned long data )
{
struct sta_info * sta = ( struct sta_info * ) data ;
local_info_t * local ;
struct ap_data * ap ;
unsigned long next_time = 0 ;
int was_assoc ;
if ( sta = = NULL | | sta - > local = = NULL | | sta - > local - > ap = = NULL ) {
PDEBUG ( DEBUG_AP , " ap_handle_timer() called with NULL data \n " ) ;
return ;
}
local = sta - > local ;
ap = local - > ap ;
was_assoc = sta - > flags & WLAN_STA_ASSOC ;
if ( atomic_read ( & sta - > users ) ! = 0 )
next_time = jiffies + HZ ;
else if ( ( sta - > flags & WLAN_STA_PERM ) & & ! ( sta - > flags & WLAN_STA_AUTH ) )
next_time = jiffies + ap - > max_inactivity ;
if ( time_before ( jiffies , sta - > last_rx + ap - > max_inactivity ) ) {
/* station activity detected; reset timeout state */
sta - > timeout_next = STA_NULLFUNC ;
next_time = sta - > last_rx + ap - > max_inactivity ;
} else if ( sta - > timeout_next = = STA_DISASSOC & &
! ( sta - > flags & WLAN_STA_PENDING_POLL ) ) {
/* STA ACKed data nullfunc frame poll */
sta - > timeout_next = STA_NULLFUNC ;
next_time = jiffies + ap - > max_inactivity ;
}
if ( next_time ) {
sta - > timer . expires = next_time ;
add_timer ( & sta - > timer ) ;
return ;
}
if ( sta - > ap )
sta - > timeout_next = STA_DEAUTH ;
if ( sta - > timeout_next = = STA_DEAUTH & & ! ( sta - > flags & WLAN_STA_PERM ) ) {
spin_lock ( & ap - > sta_table_lock ) ;
ap_sta_hash_del ( ap , sta ) ;
list_del ( & sta - > list ) ;
spin_unlock ( & ap - > sta_table_lock ) ;
sta - > flags & = ~ ( WLAN_STA_AUTH | WLAN_STA_ASSOC ) ;
} else if ( sta - > timeout_next = = STA_DISASSOC )
sta - > flags & = ~ WLAN_STA_ASSOC ;
if ( was_assoc & & ! ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap )
hostap_event_expired_sta ( local - > dev , sta ) ;
if ( sta - > timeout_next = = STA_DEAUTH & & sta - > aid > 0 & &
! skb_queue_empty ( & sta - > tx_buf ) ) {
hostap_set_tim ( local , sta - > aid , 0 ) ;
sta - > flags & = ~ WLAN_STA_TIM ;
}
if ( sta - > ap ) {
if ( ap - > autom_ap_wds ) {
PDEBUG ( DEBUG_AP , " %s: removing automatic WDS "
2008-10-27 15:59:26 -07:00
" connection to AP %pM \n " ,
local - > dev - > name , sta - > addr ) ;
2005-05-12 22:54:16 -04:00
hostap_wds_link_oper ( local , sta - > addr , WDS_DEL ) ;
}
} else if ( sta - > timeout_next = = STA_NULLFUNC ) {
/* send data frame to poll STA and check whether this frame
* is ACKed */
2005-08-14 19:08:44 -07:00
/* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but
2005-05-12 22:54:16 -04:00
* it is apparently not retried so TX Exc events are not
* received for it */
sta - > flags | = WLAN_STA_PENDING_POLL ;
2005-08-14 19:08:44 -07:00
prism2_send_mgmt ( local - > dev , IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_DATA , NULL , 0 ,
2005-05-12 22:54:16 -04:00
sta - > addr , ap - > tx_callback_poll ) ;
} else {
int deauth = sta - > timeout_next = = STA_DEAUTH ;
2007-12-21 03:30:16 -05:00
__le16 resp ;
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: sending %s info to STA %pM "
2005-05-12 22:54:16 -04:00
" (last=%lu, jiffies=%lu) \n " ,
local - > dev - > name ,
deauth ? " deauthentication " : " disassociation " ,
2008-10-27 15:59:26 -07:00
sta - > addr , sta - > last_rx , jiffies ) ;
2005-05-12 22:54:16 -04:00
resp = cpu_to_le16 ( deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY ) ;
2005-08-14 19:08:44 -07:00
prism2_send_mgmt ( local - > dev , IEEE80211_FTYPE_MGMT |
( deauth ? IEEE80211_STYPE_DEAUTH :
IEEE80211_STYPE_DISASSOC ) ,
2005-05-12 22:54:16 -04:00
( char * ) & resp , 2 , sta - > addr , 0 ) ;
}
if ( sta - > timeout_next = = STA_DEAUTH ) {
if ( sta - > flags & WLAN_STA_PERM ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: STA %pM "
2007-10-03 17:59:30 -07:00
" would have been removed, "
" but it has 'perm' flag \n " ,
2008-10-27 15:59:26 -07:00
local - > dev - > name , sta - > addr ) ;
2005-05-12 22:54:16 -04:00
} else
ap_free_sta ( ap , sta ) ;
return ;
}
if ( sta - > timeout_next = = STA_NULLFUNC ) {
sta - > timeout_next = STA_DISASSOC ;
sta - > timer . expires = jiffies + AP_DISASSOC_DELAY ;
} else {
sta - > timeout_next = STA_DEAUTH ;
sta - > timer . expires = jiffies + AP_DEAUTH_DELAY ;
}
add_timer ( & sta - > timer ) ;
}
void hostap_deauth_all_stas ( struct net_device * dev , struct ap_data * ap ,
int resend )
{
u8 addr [ ETH_ALEN ] ;
2007-12-21 03:30:16 -05:00
__le16 resp ;
2005-05-12 22:54:16 -04:00
int i ;
PDEBUG ( DEBUG_AP , " %s: Deauthenticate all stations \n " , dev - > name ) ;
memset ( addr , 0xff , ETH_ALEN ) ;
2007-12-21 03:30:16 -05:00
resp = cpu_to_le16 ( WLAN_REASON_PREV_AUTH_NOT_VALID ) ;
2005-05-12 22:54:16 -04:00
/* deauth message sent; try to resend it few times; the message is
* broadcast , so it may be delayed until next DTIM ; there is not much
* else we can do at this point since the driver is going to be shut
* down */
for ( i = 0 ; i < 5 ; i + + ) {
2005-08-14 19:08:44 -07:00
prism2_send_mgmt ( dev , IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_DEAUTH ,
2005-05-12 22:54:16 -04:00
( char * ) & resp , 2 , addr , 0 ) ;
if ( ! resend | | ap - > num_sta < = 0 )
return ;
mdelay ( 50 ) ;
}
}
static int ap_control_proc_read ( char * page , char * * start , off_t off ,
int count , int * eof , void * data )
{
char * p = page ;
struct ap_data * ap = ( struct ap_data * ) data ;
char * policy_txt ;
struct mac_entry * entry ;
if ( off ! = 0 ) {
* eof = 1 ;
return 0 ;
}
switch ( ap - > mac_restrictions . policy ) {
case MAC_POLICY_OPEN :
policy_txt = " open " ;
break ;
case MAC_POLICY_ALLOW :
policy_txt = " allow " ;
break ;
case MAC_POLICY_DENY :
policy_txt = " deny " ;
break ;
default :
policy_txt = " unknown " ;
break ;
} ;
p + = sprintf ( p , " MAC policy: %s \n " , policy_txt ) ;
p + = sprintf ( p , " MAC entries: %u \n " , ap - > mac_restrictions . entries ) ;
p + = sprintf ( p , " MAC list: \n " ) ;
spin_lock_bh ( & ap - > mac_restrictions . lock ) ;
2007-05-28 09:38:48 -07:00
list_for_each_entry ( entry , & ap - > mac_restrictions . mac_list , list ) {
2005-05-12 22:54:16 -04:00
if ( p - page > PAGE_SIZE - 80 ) {
p + = sprintf ( p , " All entries did not fit one page. \n " ) ;
break ;
}
2008-10-27 15:59:26 -07:00
p + = sprintf ( p , " %pM \n " , entry - > addr ) ;
2005-05-12 22:54:16 -04:00
}
spin_unlock_bh ( & ap - > mac_restrictions . lock ) ;
return ( p - page ) ;
}
2006-01-14 03:09:34 +01:00
int ap_control_add_mac ( struct mac_restrictions * mac_restrictions , u8 * mac )
2005-05-12 22:54:16 -04:00
{
struct mac_entry * entry ;
entry = kmalloc ( sizeof ( struct mac_entry ) , GFP_KERNEL ) ;
if ( entry = = NULL )
return - 1 ;
memcpy ( entry - > addr , mac , ETH_ALEN ) ;
spin_lock_bh ( & mac_restrictions - > lock ) ;
list_add_tail ( & entry - > list , & mac_restrictions - > mac_list ) ;
mac_restrictions - > entries + + ;
spin_unlock_bh ( & mac_restrictions - > lock ) ;
return 0 ;
}
2006-01-14 03:09:34 +01:00
int ap_control_del_mac ( struct mac_restrictions * mac_restrictions , u8 * mac )
2005-05-12 22:54:16 -04:00
{
struct list_head * ptr ;
struct mac_entry * entry ;
spin_lock_bh ( & mac_restrictions - > lock ) ;
for ( ptr = mac_restrictions - > mac_list . next ;
ptr ! = & mac_restrictions - > mac_list ; ptr = ptr - > next ) {
entry = list_entry ( ptr , struct mac_entry , list ) ;
if ( memcmp ( entry - > addr , mac , ETH_ALEN ) = = 0 ) {
list_del ( ptr ) ;
kfree ( entry ) ;
mac_restrictions - > entries - - ;
spin_unlock_bh ( & mac_restrictions - > lock ) ;
return 0 ;
}
}
spin_unlock_bh ( & mac_restrictions - > lock ) ;
return - 1 ;
}
static int ap_control_mac_deny ( struct mac_restrictions * mac_restrictions ,
u8 * mac )
{
struct mac_entry * entry ;
int found = 0 ;
if ( mac_restrictions - > policy = = MAC_POLICY_OPEN )
return 0 ;
spin_lock_bh ( & mac_restrictions - > lock ) ;
2007-05-28 09:38:48 -07:00
list_for_each_entry ( entry , & mac_restrictions - > mac_list , list ) {
2005-05-12 22:54:16 -04:00
if ( memcmp ( entry - > addr , mac , ETH_ALEN ) = = 0 ) {
found = 1 ;
break ;
}
}
spin_unlock_bh ( & mac_restrictions - > lock ) ;
if ( mac_restrictions - > policy = = MAC_POLICY_ALLOW )
return ! found ;
else
return found ;
}
2006-01-14 03:09:34 +01:00
void ap_control_flush_macs ( struct mac_restrictions * mac_restrictions )
2005-05-12 22:54:16 -04:00
{
struct list_head * ptr , * n ;
struct mac_entry * entry ;
if ( mac_restrictions - > entries = = 0 )
return ;
spin_lock_bh ( & mac_restrictions - > lock ) ;
for ( ptr = mac_restrictions - > mac_list . next , n = ptr - > next ;
ptr ! = & mac_restrictions - > mac_list ;
ptr = n , n = ptr - > next ) {
entry = list_entry ( ptr , struct mac_entry , list ) ;
list_del ( ptr ) ;
kfree ( entry ) ;
}
mac_restrictions - > entries = 0 ;
spin_unlock_bh ( & mac_restrictions - > lock ) ;
}
2006-01-14 03:09:34 +01:00
int ap_control_kick_mac ( struct ap_data * ap , struct net_device * dev , u8 * mac )
2005-05-12 22:54:16 -04:00
{
struct sta_info * sta ;
2007-12-21 03:30:16 -05:00
__le16 resp ;
2005-05-12 22:54:16 -04:00
spin_lock_bh ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , mac ) ;
if ( sta ) {
ap_sta_hash_del ( ap , sta ) ;
list_del ( & sta - > list ) ;
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( ! sta )
return - EINVAL ;
resp = cpu_to_le16 ( WLAN_REASON_PREV_AUTH_NOT_VALID ) ;
2005-08-14 19:08:44 -07:00
prism2_send_mgmt ( dev , IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH ,
2005-05-12 22:54:16 -04:00
( char * ) & resp , 2 , sta - > addr , 0 ) ;
if ( ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap )
hostap_event_expired_sta ( dev , sta ) ;
ap_free_sta ( ap , sta ) ;
return 0 ;
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2006-01-14 03:09:34 +01:00
void ap_control_kickall ( struct ap_data * ap )
2005-05-12 22:54:16 -04:00
{
struct list_head * ptr , * n ;
struct sta_info * sta ;
2005-07-31 13:08:32 -04:00
2005-05-12 22:54:16 -04:00
spin_lock_bh ( & ap - > sta_table_lock ) ;
for ( ptr = ap - > sta_list . next , n = ptr - > next ; ptr ! = & ap - > sta_list ;
ptr = n , n = ptr - > next ) {
sta = list_entry ( ptr , struct sta_info , list ) ;
ap_sta_hash_del ( ap , sta ) ;
list_del ( & sta - > list ) ;
if ( ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap & & sta - > local )
hostap_event_expired_sta ( sta - > local - > dev , sta ) ;
ap_free_sta ( ap , sta ) ;
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
# define PROC_LIMIT (PAGE_SIZE - 80)
static int prism2_ap_proc_read ( char * page , char * * start , off_t off ,
int count , int * eof , void * data )
{
char * p = page ;
struct ap_data * ap = ( struct ap_data * ) data ;
2007-05-28 09:38:48 -07:00
struct sta_info * sta ;
2005-05-12 22:54:16 -04:00
int i ;
if ( off > PROC_LIMIT ) {
* eof = 1 ;
return 0 ;
}
p + = sprintf ( p , " # BSSID CHAN SIGNAL NOISE RATE SSID FLAGS \n " ) ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
2007-05-28 09:38:48 -07:00
list_for_each_entry ( sta , & ap - > sta_list , list ) {
2005-05-12 22:54:16 -04:00
if ( ! sta - > ap )
continue ;
2008-10-27 15:59:26 -07:00
p + = sprintf ( p , " %pM %d %d %d %d ' " ,
sta - > addr ,
2005-05-12 22:54:16 -04:00
sta - > u . ap . channel , sta - > last_rx_signal ,
sta - > last_rx_silence , sta - > last_rx_rate ) ;
for ( i = 0 ; i < sta - > u . ap . ssid_len ; i + + )
p + = sprintf ( p , ( ( sta - > u . ap . ssid [ i ] > = 32 & &
sta - > u . ap . ssid [ i ] < 127 ) ?
" %c " : " <%02x> " ) ,
sta - > u . ap . ssid [ i ] ) ;
p + = sprintf ( p , " ' " ) ;
if ( sta - > capability & WLAN_CAPABILITY_ESS )
p + = sprintf ( p , " [ESS] " ) ;
if ( sta - > capability & WLAN_CAPABILITY_IBSS )
p + = sprintf ( p , " [IBSS] " ) ;
if ( sta - > capability & WLAN_CAPABILITY_PRIVACY )
p + = sprintf ( p , " [WEP] " ) ;
p + = sprintf ( p , " \n " ) ;
if ( ( p - page ) > PROC_LIMIT ) {
printk ( KERN_DEBUG " hostap: ap proc did not fit \n " ) ;
break ;
}
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( ( p - page ) < = off ) {
* eof = 1 ;
return 0 ;
}
* start = page + off ;
return ( p - page - off ) ;
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
void hostap_check_sta_fw_version ( struct ap_data * ap , int sta_fw_ver )
{
if ( ! ap )
return ;
if ( sta_fw_ver = = PRISM2_FW_VER ( 0 , 8 , 0 ) ) {
PDEBUG ( DEBUG_AP , " Using data::nullfunc ACK workaround - "
" firmware upgrade recommended \n " ) ;
ap - > nullfunc_ack = 1 ;
} else
ap - > nullfunc_ack = 0 ;
if ( sta_fw_ver = = PRISM2_FW_VER ( 1 , 4 , 2 ) ) {
printk ( KERN_WARNING " %s: Warning: secondary station firmware "
" version 1.4.2 does not seem to work in Host AP mode \n " ,
ap - > local - > dev - > name ) ;
}
}
/* Called only as a tasklet (software IRQ) */
static void hostap_ap_tx_cb ( struct sk_buff * skb , int ok , void * data )
{
struct ap_data * ap = data ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
if ( ! ap - > local - > hostapd | | ! ap - > local - > apdev ) {
dev_kfree_skb ( skb ) ;
return ;
}
/* Pass the TX callback frame to the hostapd; use 802.11 header version
* 1 to indicate failure ( no ACK ) and 2 success ( frame ACKed ) */
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
hdr - > frame_control & = cpu_to_le16 ( ~ IEEE80211_FCTL_VERS ) ;
hdr - > frame_control | = cpu_to_le16 ( ok ? BIT ( 1 ) : BIT ( 0 ) ) ;
2005-05-12 22:54:16 -04:00
skb - > dev = ap - > local - > apdev ;
2009-02-11 17:17:10 -05:00
skb_pull ( skb , hostap_80211_get_hdrlen ( hdr - > frame_control ) ) ;
2005-05-12 22:54:16 -04:00
skb - > pkt_type = PACKET_OTHERHOST ;
2009-01-29 13:26:44 -08:00
skb - > protocol = cpu_to_be16 ( ETH_P_802_2 ) ;
2005-05-12 22:54:16 -04:00
memset ( skb - > cb , 0 , sizeof ( skb - > cb ) ) ;
netif_rx ( skb ) ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
/* Called only as a tasklet (software IRQ) */
static void hostap_ap_tx_cb_auth ( struct sk_buff * skb , int ok , void * data )
{
struct ap_data * ap = data ;
struct net_device * dev = ap - > local - > dev ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
u16 auth_alg , auth_transaction , status ;
2007-12-21 03:30:16 -05:00
__le16 * pos ;
2005-05-12 22:54:16 -04:00
struct sta_info * sta = NULL ;
char * txt = NULL ;
if ( ap - > local - > hostapd ) {
dev_kfree_skb ( skb ) ;
return ;
}
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
if ( ! ieee80211_is_auth ( hdr - > frame_control ) | |
2005-05-12 22:54:16 -04:00
skb - > len < IEEE80211_MGMT_HDR_LEN + 6 ) {
printk ( KERN_DEBUG " %s: hostap_ap_tx_cb_auth received invalid "
" frame \n " , dev - > name ) ;
dev_kfree_skb ( skb ) ;
return ;
}
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) ( skb - > data + IEEE80211_MGMT_HDR_LEN ) ;
2005-05-12 22:54:16 -04:00
auth_alg = le16_to_cpu ( * pos + + ) ;
auth_transaction = le16_to_cpu ( * pos + + ) ;
status = le16_to_cpu ( * pos + + ) ;
if ( ! ok ) {
txt = " frame was not ACKed " ;
goto done ;
}
spin_lock ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , hdr - > addr1 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock ( & ap - > sta_table_lock ) ;
if ( ! sta ) {
txt = " STA not found " ;
goto done ;
}
if ( status = = WLAN_STATUS_SUCCESS & &
( ( auth_alg = = WLAN_AUTH_OPEN & & auth_transaction = = 2 ) | |
( auth_alg = = WLAN_AUTH_SHARED_KEY & & auth_transaction = = 4 ) ) ) {
txt = " STA authenticated " ;
sta - > flags | = WLAN_STA_AUTH ;
sta - > last_auth = jiffies ;
} else if ( status ! = WLAN_STATUS_SUCCESS )
txt = " authentication failed " ;
done :
if ( sta )
atomic_dec ( & sta - > users ) ;
if ( txt ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: %pM auth_cb - alg=%d "
2007-10-03 17:59:30 -07:00
" trans#=%d status=%d - %s \n " ,
2008-10-27 15:59:26 -07:00
dev - > name , hdr - > addr1 ,
2008-04-08 16:50:44 -07:00
auth_alg , auth_transaction , status , txt ) ;
2005-05-12 22:54:16 -04:00
}
dev_kfree_skb ( skb ) ;
}
/* Called only as a tasklet (software IRQ) */
static void hostap_ap_tx_cb_assoc ( struct sk_buff * skb , int ok , void * data )
{
struct ap_data * ap = data ;
struct net_device * dev = ap - > local - > dev ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2007-12-21 03:30:16 -05:00
u16 fc , status ;
__le16 * pos ;
2005-05-12 22:54:16 -04:00
struct sta_info * sta = NULL ;
char * txt = NULL ;
if ( ap - > local - > hostapd ) {
dev_kfree_skb ( skb ) ;
return ;
}
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
fc = le16_to_cpu ( hdr - > frame_control ) ;
if ( ( ! ieee80211_is_assoc_resp ( hdr - > frame_control ) & &
! ieee80211_is_reassoc_resp ( hdr - > frame_control ) ) | |
2005-05-12 22:54:16 -04:00
skb - > len < IEEE80211_MGMT_HDR_LEN + 4 ) {
printk ( KERN_DEBUG " %s: hostap_ap_tx_cb_assoc received invalid "
" frame \n " , dev - > name ) ;
dev_kfree_skb ( skb ) ;
return ;
}
if ( ! ok ) {
txt = " frame was not ACKed " ;
goto done ;
}
spin_lock ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , hdr - > addr1 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock ( & ap - > sta_table_lock ) ;
if ( ! sta ) {
txt = " STA not found " ;
goto done ;
}
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) ( skb - > data + IEEE80211_MGMT_HDR_LEN ) ;
2005-05-12 22:54:16 -04:00
pos + + ;
status = le16_to_cpu ( * pos + + ) ;
if ( status = = WLAN_STATUS_SUCCESS ) {
if ( ! ( sta - > flags & WLAN_STA_ASSOC ) )
hostap_event_new_sta ( dev , sta ) ;
txt = " STA associated " ;
sta - > flags | = WLAN_STA_ASSOC ;
sta - > last_assoc = jiffies ;
} else
txt = " association failed " ;
done :
if ( sta )
atomic_dec ( & sta - > users ) ;
if ( txt ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: %pM assoc_cb - %s \n " ,
dev - > name , hdr - > addr1 , txt ) ;
2005-05-12 22:54:16 -04:00
}
dev_kfree_skb ( skb ) ;
}
/* Called only as a tasklet (software IRQ); TX callback for poll frames used
* in verifying whether the STA is still present . */
static void hostap_ap_tx_cb_poll ( struct sk_buff * skb , int ok , void * data )
{
struct ap_data * ap = data ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
struct sta_info * sta ;
if ( skb - > len < 24 )
goto fail ;
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
if ( ok ) {
spin_lock ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , hdr - > addr1 ) ;
if ( sta )
sta - > flags & = ~ WLAN_STA_PENDING_POLL ;
spin_unlock ( & ap - > sta_table_lock ) ;
} else {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP ,
" %s: STA %pM did not ACK activity poll frame \n " ,
ap - > local - > dev - > name , hdr - > addr1 ) ;
2005-05-12 22:54:16 -04:00
}
fail :
dev_kfree_skb ( skb ) ;
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
void hostap_init_data ( local_info_t * local )
{
struct ap_data * ap = local - > ap ;
if ( ap = = NULL ) {
printk ( KERN_WARNING " hostap_init_data: ap == NULL \n " ) ;
return ;
}
memset ( ap , 0 , sizeof ( struct ap_data ) ) ;
ap - > local = local ;
ap - > ap_policy = GET_INT_PARM ( other_ap_policy , local - > card_idx ) ;
ap - > bridge_packets = GET_INT_PARM ( ap_bridge_packets , local - > card_idx ) ;
ap - > max_inactivity =
GET_INT_PARM ( ap_max_inactivity , local - > card_idx ) * HZ ;
ap - > autom_ap_wds = GET_INT_PARM ( autom_ap_wds , local - > card_idx ) ;
spin_lock_init ( & ap - > sta_table_lock ) ;
INIT_LIST_HEAD ( & ap - > sta_list ) ;
/* Initialize task queue structure for AP management */
2006-11-22 14:57:56 +00:00
INIT_WORK ( & local - > ap - > add_sta_proc_queue , handle_add_proc_queue ) ;
2005-05-12 22:54:16 -04:00
ap - > tx_callback_idx =
hostap_tx_callback_register ( local , hostap_ap_tx_cb , ap ) ;
if ( ap - > tx_callback_idx = = 0 )
printk ( KERN_WARNING " %s: failed to register TX callback for "
" AP \n " , local - > dev - > name ) ;
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
2006-11-22 14:57:56 +00:00
INIT_WORK ( & local - > ap - > wds_oper_queue , handle_wds_oper_queue ) ;
2005-05-12 22:54:16 -04:00
ap - > tx_callback_auth =
hostap_tx_callback_register ( local , hostap_ap_tx_cb_auth , ap ) ;
ap - > tx_callback_assoc =
hostap_tx_callback_register ( local , hostap_ap_tx_cb_assoc , ap ) ;
ap - > tx_callback_poll =
hostap_tx_callback_register ( local , hostap_ap_tx_cb_poll , ap ) ;
if ( ap - > tx_callback_auth = = 0 | | ap - > tx_callback_assoc = = 0 | |
ap - > tx_callback_poll = = 0 )
printk ( KERN_WARNING " %s: failed to register TX callback for "
" AP \n " , local - > dev - > name ) ;
spin_lock_init ( & ap - > mac_restrictions . lock ) ;
INIT_LIST_HEAD ( & ap - > mac_restrictions . mac_list ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
ap - > initialized = 1 ;
}
void hostap_init_ap_proc ( local_info_t * local )
{
struct ap_data * ap = local - > ap ;
ap - > proc = local - > proc ;
if ( ap - > proc = = NULL )
return ;
# ifndef PRISM2_NO_PROCFS_DEBUG
create_proc_read_entry ( " ap_debug " , 0 , ap - > proc ,
ap_debug_proc_read , ap ) ;
# endif /* PRISM2_NO_PROCFS_DEBUG */
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
create_proc_read_entry ( " ap_control " , 0 , ap - > proc ,
ap_control_proc_read , ap ) ;
create_proc_read_entry ( " ap " , 0 , ap - > proc ,
prism2_ap_proc_read , ap ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
}
void hostap_free_data ( struct ap_data * ap )
{
2007-05-28 09:38:48 -07:00
struct sta_info * n , * sta ;
2005-05-12 22:54:16 -04:00
if ( ap = = NULL | | ! ap - > initialized ) {
printk ( KERN_DEBUG " hostap_free_data: ap has not yet been "
" initialized - skip resource freeing \n " ) ;
return ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
if ( ap - > crypt )
ap - > crypt - > deinit ( ap - > crypt_priv ) ;
ap - > crypt = ap - > crypt_priv = NULL ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2007-05-28 09:38:48 -07:00
list_for_each_entry_safe ( sta , n , & ap - > sta_list , list ) {
2005-05-12 22:54:16 -04:00
ap_sta_hash_del ( ap , sta ) ;
list_del ( & sta - > list ) ;
if ( ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap & & sta - > local )
hostap_event_expired_sta ( sta - > local - > dev , sta ) ;
ap_free_sta ( ap , sta ) ;
}
# ifndef PRISM2_NO_PROCFS_DEBUG
if ( ap - > proc ! = NULL ) {
remove_proc_entry ( " ap_debug " , ap - > proc ) ;
}
# endif /* PRISM2_NO_PROCFS_DEBUG */
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
if ( ap - > proc ! = NULL ) {
remove_proc_entry ( " ap " , ap - > proc ) ;
remove_proc_entry ( " ap_control " , ap - > proc ) ;
}
ap_control_flush_macs ( & ap - > mac_restrictions ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
ap - > initialized = 0 ;
}
/* caller should have mutex for AP STA list handling */
static struct sta_info * ap_get_sta ( struct ap_data * ap , u8 * sta )
{
struct sta_info * s ;
s = ap - > sta_hash [ STA_HASH ( sta ) ] ;
while ( s ! = NULL & & memcmp ( s - > addr , sta , ETH_ALEN ) ! = 0 )
s = s - > hnext ;
return s ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
/* Called from timer handler and from scheduled AP queue handlers */
static void prism2_send_mgmt ( struct net_device * dev ,
2005-08-14 19:08:44 -07:00
u16 type_subtype , char * body ,
2005-05-12 22:54:16 -04:00
int body_len , u8 * addr , u16 tx_cb_idx )
{
struct hostap_interface * iface ;
local_info_t * local ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
u16 fc ;
struct sk_buff * skb ;
struct hostap_skb_tx_data * meta ;
int hdrlen ;
iface = netdev_priv ( dev ) ;
local = iface - > local ;
dev = local - > dev ; /* always use master radio device */
iface = netdev_priv ( dev ) ;
if ( ! ( dev - > flags & IFF_UP ) ) {
PDEBUG ( DEBUG_AP , " %s: prism2_send_mgmt - device is not UP - "
" cannot send frame \n " , dev - > name ) ;
return ;
}
skb = dev_alloc_skb ( sizeof ( * hdr ) + body_len ) ;
if ( skb = = NULL ) {
PDEBUG ( DEBUG_AP , " %s: prism2_send_mgmt failed to allocate "
" skb \n " , dev - > name ) ;
return ;
}
2005-08-14 19:08:44 -07:00
fc = type_subtype ;
2009-02-11 17:17:10 -05:00
hdrlen = hostap_80211_get_hdrlen ( cpu_to_le16 ( type_subtype ) ) ;
hdr = ( struct ieee80211_hdr * ) skb_put ( skb , hdrlen ) ;
2005-05-12 22:54:16 -04:00
if ( body )
memcpy ( skb_put ( skb , body_len ) , body , body_len ) ;
memset ( hdr , 0 , hdrlen ) ;
/* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11
* tx_control instead of using local - > tx_control */
memcpy ( hdr - > addr1 , addr , ETH_ALEN ) ; /* DA / RA */
2009-02-11 17:17:10 -05:00
if ( ieee80211_is_data ( hdr - > frame_control ) ) {
2005-08-14 21:00:01 -07:00
fc | = IEEE80211_FCTL_FROMDS ;
2005-05-12 22:54:16 -04:00
memcpy ( hdr - > addr2 , dev - > dev_addr , ETH_ALEN ) ; /* BSSID */
memcpy ( hdr - > addr3 , dev - > dev_addr , ETH_ALEN ) ; /* SA */
2009-02-11 17:17:10 -05:00
} else if ( ieee80211_is_ctl ( hdr - > frame_control ) ) {
2005-05-12 22:54:16 -04:00
/* control:ACK does not have addr2 or addr3 */
memset ( hdr - > addr2 , 0 , ETH_ALEN ) ;
memset ( hdr - > addr3 , 0 , ETH_ALEN ) ;
} else {
memcpy ( hdr - > addr2 , dev - > dev_addr , ETH_ALEN ) ; /* SA */
memcpy ( hdr - > addr3 , dev - > dev_addr , ETH_ALEN ) ; /* BSSID */
}
2009-02-11 17:17:10 -05:00
hdr - > frame_control = cpu_to_le16 ( fc ) ;
2005-05-12 22:54:16 -04:00
meta = ( struct hostap_skb_tx_data * ) skb - > cb ;
memset ( meta , 0 , sizeof ( * meta ) ) ;
meta - > magic = HOSTAP_SKB_TX_DATA_MAGIC ;
meta - > iface = iface ;
meta - > tx_cb_idx = tx_cb_idx ;
skb - > dev = dev ;
2007-03-19 15:30:44 -07:00
skb_reset_mac_header ( skb ) ;
2007-04-10 20:45:18 -07:00
skb_reset_network_header ( skb ) ;
2005-05-12 22:54:16 -04:00
dev_queue_xmit ( skb ) ;
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
static int prism2_sta_proc_read ( char * page , char * * start , off_t off ,
int count , int * eof , void * data )
{
char * p = page ;
struct sta_info * sta = ( struct sta_info * ) data ;
int i ;
/* FIX: possible race condition.. the STA data could have just expired,
* but proc entry was still here so that the read could have started ;
* some locking should be done here . . */
if ( off ! = 0 ) {
* eof = 1 ;
return 0 ;
}
2008-10-27 15:59:26 -07:00
p + = sprintf ( p , " %s=%pM \n users=%d \n aid=%d \n "
2005-05-12 22:54:16 -04:00
" flags=0x%04x%s%s%s%s%s%s%s \n "
" capability=0x%02x \n listen_interval=%d \n supported_rates= " ,
sta - > ap ? " AP " : " STA " ,
2008-10-27 15:59:26 -07:00
sta - > addr , atomic_read ( & sta - > users ) , sta - > aid ,
2005-05-12 22:54:16 -04:00
sta - > flags ,
sta - > flags & WLAN_STA_AUTH ? " AUTH " : " " ,
sta - > flags & WLAN_STA_ASSOC ? " ASSOC " : " " ,
sta - > flags & WLAN_STA_PS ? " PS " : " " ,
sta - > flags & WLAN_STA_TIM ? " TIM " : " " ,
sta - > flags & WLAN_STA_PERM ? " PERM " : " " ,
sta - > flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED " : " " ,
sta - > flags & WLAN_STA_PENDING_POLL ? " POLL " : " " ,
sta - > capability , sta - > listen_interval ) ;
/* supported_rates: 500 kbit/s units with msb ignored */
for ( i = 0 ; i < sizeof ( sta - > supported_rates ) ; i + + )
if ( sta - > supported_rates [ i ] ! = 0 )
p + = sprintf ( p , " %d%sMbps " ,
( sta - > supported_rates [ i ] & 0x7f ) / 2 ,
sta - > supported_rates [ i ] & 1 ? " .5 " : " " ) ;
p + = sprintf ( p , " \n jiffies=%lu \n last_auth=%lu \n last_assoc=%lu \n "
" last_rx=%lu \n last_tx=%lu \n rx_packets=%lu \n "
" tx_packets=%lu \n "
" rx_bytes=%lu \n tx_bytes=%lu \n buffer_count=%d \n "
" last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps \n "
" tx_rate=%d \n tx[1M]=%d \n tx[2M]=%d \n tx[5.5M]=%d \n "
" tx[11M]=%d \n "
" rx[1M]=%d \n rx[2M]=%d \n rx[5.5M]=%d \n rx[11M]=%d \n " ,
jiffies , sta - > last_auth , sta - > last_assoc , sta - > last_rx ,
sta - > last_tx ,
sta - > rx_packets , sta - > tx_packets , sta - > rx_bytes ,
sta - > tx_bytes , skb_queue_len ( & sta - > tx_buf ) ,
sta - > last_rx_silence ,
sta - > last_rx_signal , sta - > last_rx_rate / 10 ,
sta - > last_rx_rate % 10 ? " .5 " : " " ,
sta - > tx_rate , sta - > tx_count [ 0 ] , sta - > tx_count [ 1 ] ,
sta - > tx_count [ 2 ] , sta - > tx_count [ 3 ] , sta - > rx_count [ 0 ] ,
sta - > rx_count [ 1 ] , sta - > rx_count [ 2 ] , sta - > rx_count [ 3 ] ) ;
if ( sta - > crypt & & sta - > crypt - > ops & & sta - > crypt - > ops - > print_stats )
p = sta - > crypt - > ops - > print_stats ( p , sta - > crypt - > priv ) ;
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
if ( sta - > ap ) {
if ( sta - > u . ap . channel > = 0 )
p + = sprintf ( p , " channel=%d \n " , sta - > u . ap . channel ) ;
p + = sprintf ( p , " ssid= " ) ;
for ( i = 0 ; i < sta - > u . ap . ssid_len ; i + + )
p + = sprintf ( p , ( ( sta - > u . ap . ssid [ i ] > = 32 & &
sta - > u . ap . ssid [ i ] < 127 ) ?
" %c " : " <%02x> " ) ,
sta - > u . ap . ssid [ i ] ) ;
p + = sprintf ( p , " \n " ) ;
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
return ( p - page ) ;
}
2006-11-22 14:57:56 +00:00
static void handle_add_proc_queue ( struct work_struct * work )
2005-05-12 22:54:16 -04:00
{
2006-11-22 14:57:56 +00:00
struct ap_data * ap = container_of ( work , struct ap_data ,
add_sta_proc_queue ) ;
2005-05-12 22:54:16 -04:00
struct sta_info * sta ;
char name [ 20 ] ;
struct add_sta_proc_data * entry , * prev ;
entry = ap - > add_sta_proc_entries ;
ap - > add_sta_proc_entries = NULL ;
while ( entry ) {
spin_lock_bh ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , entry - > addr ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( sta ) {
2008-10-27 15:59:26 -07:00
sprintf ( name , " %pM " , sta - > addr ) ;
2005-05-12 22:54:16 -04:00
sta - > proc = create_proc_read_entry (
name , 0 , ap - > proc ,
prism2_sta_proc_read , sta ) ;
atomic_dec ( & sta - > users ) ;
}
prev = entry ;
entry = entry - > next ;
kfree ( prev ) ;
}
}
static struct sta_info * ap_add_sta ( struct ap_data * ap , u8 * addr )
{
struct sta_info * sta ;
2006-12-02 13:33:40 +02:00
sta = kzalloc ( sizeof ( struct sta_info ) , GFP_ATOMIC ) ;
2005-05-12 22:54:16 -04:00
if ( sta = = NULL ) {
PDEBUG ( DEBUG_AP , " AP: kmalloc failed \n " ) ;
return NULL ;
}
/* initialize STA info data */
sta - > local = ap - > local ;
skb_queue_head_init ( & sta - > tx_buf ) ;
memcpy ( sta - > addr , addr , ETH_ALEN ) ;
atomic_inc ( & sta - > users ) ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
list_add ( & sta - > list , & ap - > sta_list ) ;
ap - > num_sta + + ;
ap_sta_hash_add ( ap , sta ) ;
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( ap - > proc ) {
struct add_sta_proc_data * entry ;
/* schedule a non-interrupt context process to add a procfs
* entry for the STA since procfs code use GFP_KERNEL */
entry = kmalloc ( sizeof ( * entry ) , GFP_ATOMIC ) ;
if ( entry ) {
memcpy ( entry - > addr , sta - > addr , ETH_ALEN ) ;
entry - > next = ap - > add_sta_proc_entries ;
ap - > add_sta_proc_entries = entry ;
schedule_work ( & ap - > add_sta_proc_queue ) ;
} else
printk ( KERN_DEBUG " Failed to add STA proc data \n " ) ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
init_timer ( & sta - > timer ) ;
sta - > timer . expires = jiffies + ap - > max_inactivity ;
sta - > timer . data = ( unsigned long ) sta ;
sta - > timer . function = ap_handle_timer ;
if ( ! ap - > local - > hostapd )
add_timer ( & sta - > timer ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
return sta ;
}
static int ap_tx_rate_ok ( int rateidx , struct sta_info * sta ,
local_info_t * local )
{
if ( rateidx > sta - > tx_max_rate | |
! ( sta - > tx_supp_rates & ( 1 < < rateidx ) ) )
return 0 ;
if ( local - > tx_rate_control ! = 0 & &
! ( local - > tx_rate_control & ( 1 < < rateidx ) ) )
return 0 ;
return 1 ;
}
static void prism2_check_tx_rates ( struct sta_info * sta )
{
int i ;
sta - > tx_supp_rates = 0 ;
for ( i = 0 ; i < sizeof ( sta - > supported_rates ) ; i + + ) {
if ( ( sta - > supported_rates [ i ] & 0x7f ) = = 2 )
sta - > tx_supp_rates | = WLAN_RATE_1M ;
if ( ( sta - > supported_rates [ i ] & 0x7f ) = = 4 )
sta - > tx_supp_rates | = WLAN_RATE_2M ;
if ( ( sta - > supported_rates [ i ] & 0x7f ) = = 11 )
sta - > tx_supp_rates | = WLAN_RATE_5M5 ;
if ( ( sta - > supported_rates [ i ] & 0x7f ) = = 22 )
sta - > tx_supp_rates | = WLAN_RATE_11M ;
}
sta - > tx_max_rate = sta - > tx_rate = sta - > tx_rate_idx = 0 ;
if ( sta - > tx_supp_rates & WLAN_RATE_1M ) {
sta - > tx_max_rate = 0 ;
if ( ap_tx_rate_ok ( 0 , sta , sta - > local ) ) {
sta - > tx_rate = 10 ;
sta - > tx_rate_idx = 0 ;
}
}
if ( sta - > tx_supp_rates & WLAN_RATE_2M ) {
sta - > tx_max_rate = 1 ;
if ( ap_tx_rate_ok ( 1 , sta , sta - > local ) ) {
sta - > tx_rate = 20 ;
sta - > tx_rate_idx = 1 ;
}
}
if ( sta - > tx_supp_rates & WLAN_RATE_5M5 ) {
sta - > tx_max_rate = 2 ;
if ( ap_tx_rate_ok ( 2 , sta , sta - > local ) ) {
sta - > tx_rate = 55 ;
sta - > tx_rate_idx = 2 ;
}
}
if ( sta - > tx_supp_rates & WLAN_RATE_11M ) {
sta - > tx_max_rate = 3 ;
if ( ap_tx_rate_ok ( 3 , sta , sta - > local ) ) {
sta - > tx_rate = 110 ;
sta - > tx_rate_idx = 3 ;
}
}
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
static void ap_crypt_init ( struct ap_data * ap )
{
2008-10-29 11:35:05 -04:00
ap - > crypt = lib80211_get_crypto_ops ( " WEP " ) ;
2005-05-12 22:54:16 -04:00
if ( ap - > crypt ) {
if ( ap - > crypt - > init ) {
ap - > crypt_priv = ap - > crypt - > init ( 0 ) ;
if ( ap - > crypt_priv = = NULL )
ap - > crypt = NULL ;
else {
u8 key [ WEP_KEY_LEN ] ;
get_random_bytes ( key , WEP_KEY_LEN ) ;
ap - > crypt - > set_key ( key , WEP_KEY_LEN , NULL ,
ap - > crypt_priv ) ;
}
}
}
if ( ap - > crypt = = NULL ) {
printk ( KERN_WARNING " AP could not initialize WEP: load module "
2008-10-29 11:35:05 -04:00
" lib80211_crypt_wep.ko \n " ) ;
2005-05-12 22:54:16 -04:00
}
}
/* Generate challenge data for shared key authentication. IEEE 802.11 specifies
* that WEP algorithm is used for generating challange . This should be unique ,
* but otherwise there is not really need for randomness etc . Initialize WEP
* with pseudo random key and then use increasing IV to get unique challenge
* streams .
*
* Called only as a scheduled task for pending AP frames .
*/
static char * ap_auth_make_challenge ( struct ap_data * ap )
{
char * tmpbuf ;
struct sk_buff * skb ;
if ( ap - > crypt = = NULL ) {
ap_crypt_init ( ap ) ;
if ( ap - > crypt = = NULL )
return NULL ;
}
2006-12-13 00:35:56 -08:00
tmpbuf = kmalloc ( WLAN_AUTH_CHALLENGE_LEN , GFP_ATOMIC ) ;
2005-05-12 22:54:16 -04:00
if ( tmpbuf = = NULL ) {
PDEBUG ( DEBUG_AP , " AP: kmalloc failed for challenge \n " ) ;
return NULL ;
}
skb = dev_alloc_skb ( WLAN_AUTH_CHALLENGE_LEN +
2005-09-21 12:23:51 -05:00
ap - > crypt - > extra_mpdu_prefix_len +
ap - > crypt - > extra_mpdu_postfix_len ) ;
2005-05-12 22:54:16 -04:00
if ( skb = = NULL ) {
kfree ( tmpbuf ) ;
return NULL ;
}
2005-09-21 12:23:51 -05:00
skb_reserve ( skb , ap - > crypt - > extra_mpdu_prefix_len ) ;
2005-05-12 22:54:16 -04:00
memset ( skb_put ( skb , WLAN_AUTH_CHALLENGE_LEN ) , 0 ,
WLAN_AUTH_CHALLENGE_LEN ) ;
if ( ap - > crypt - > encrypt_mpdu ( skb , 0 , ap - > crypt_priv ) ) {
dev_kfree_skb ( skb ) ;
kfree ( tmpbuf ) ;
return NULL ;
}
2007-03-27 18:55:52 -03:00
skb_copy_from_linear_data_offset ( skb , ap - > crypt - > extra_mpdu_prefix_len ,
tmpbuf , WLAN_AUTH_CHALLENGE_LEN ) ;
2005-05-12 22:54:16 -04:00
dev_kfree_skb ( skb ) ;
return tmpbuf ;
}
/* Called only as a scheduled task for pending AP frames. */
static void handle_authen ( local_info_t * local , struct sk_buff * skb ,
struct hostap_80211_rx_status * rx_stats )
{
struct net_device * dev = local - > dev ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
size_t hdrlen ;
struct ap_data * ap = local - > ap ;
char body [ 8 + WLAN_AUTH_CHALLENGE_LEN ] , * challenge = NULL ;
int len , olen ;
2007-12-21 03:30:16 -05:00
u16 auth_alg , auth_transaction , status_code ;
__le16 * pos ;
2009-02-11 17:17:10 -05:00
u16 resp = WLAN_STATUS_SUCCESS ;
2005-05-12 22:54:16 -04:00
struct sta_info * sta = NULL ;
2008-10-29 11:35:05 -04:00
struct lib80211_crypt_data * crypt ;
2005-05-12 22:54:16 -04:00
char * txt = " " ;
len = skb - > len - IEEE80211_MGMT_HDR_LEN ;
2009-02-11 17:17:10 -05:00
hdrlen = hostap_80211_get_hdrlen ( hdr - > frame_control ) ;
2005-05-12 22:54:16 -04:00
if ( len < 6 ) {
PDEBUG ( DEBUG_AP , " %s: handle_authen - too short payload "
2008-10-27 15:59:26 -07:00
" (len=%d) from %pM \n " , dev - > name , len , hdr - > addr2 ) ;
2005-05-12 22:54:16 -04:00
return ;
}
spin_lock_bh ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
if ( sta & & sta - > crypt )
crypt = sta - > crypt ;
else {
int idx = 0 ;
if ( skb - > len > = hdrlen + 3 )
idx = skb - > data [ hdrlen + 3 ] > > 6 ;
2008-10-29 11:35:05 -04:00
crypt = local - > crypt_info . crypt [ idx ] ;
2005-05-12 22:54:16 -04:00
}
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) ( skb - > data + IEEE80211_MGMT_HDR_LEN ) ;
2005-05-12 22:54:16 -04:00
auth_alg = __le16_to_cpu ( * pos ) ;
pos + + ;
auth_transaction = __le16_to_cpu ( * pos ) ;
pos + + ;
status_code = __le16_to_cpu ( * pos ) ;
pos + + ;
if ( memcmp ( dev - > dev_addr , hdr - > addr2 , ETH_ALEN ) = = 0 | |
ap_control_mac_deny ( & ap - > mac_restrictions , hdr - > addr2 ) ) {
txt = " authentication denied " ;
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
goto fail ;
}
if ( ( ( local - > auth_algs & PRISM2_AUTH_OPEN ) & &
auth_alg = = WLAN_AUTH_OPEN ) | |
( ( local - > auth_algs & PRISM2_AUTH_SHARED_KEY ) & &
crypt & & auth_alg = = WLAN_AUTH_SHARED_KEY ) ) {
} else {
txt = " unsupported algorithm " ;
resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ;
goto fail ;
}
if ( len > = 8 ) {
u8 * u = ( u8 * ) pos ;
if ( * u = = WLAN_EID_CHALLENGE ) {
if ( * ( u + 1 ) ! = WLAN_AUTH_CHALLENGE_LEN ) {
txt = " invalid challenge len " ;
resp = WLAN_STATUS_CHALLENGE_FAIL ;
goto fail ;
}
if ( len - 8 < WLAN_AUTH_CHALLENGE_LEN ) {
txt = " challenge underflow " ;
resp = WLAN_STATUS_CHALLENGE_FAIL ;
goto fail ;
}
challenge = ( char * ) ( u + 2 ) ;
}
}
if ( sta & & sta - > ap ) {
if ( time_after ( jiffies , sta - > u . ap . last_beacon +
( 10 * sta - > listen_interval * HZ ) / 1024 ) ) {
PDEBUG ( DEBUG_AP , " %s: no beacons received for a while, "
2008-10-27 15:59:26 -07:00
" assuming AP %pM is now STA \n " ,
dev - > name , sta - > addr ) ;
2005-05-12 22:54:16 -04:00
sta - > ap = 0 ;
sta - > flags = 0 ;
sta - > u . sta . challenge = NULL ;
} else {
txt = " AP trying to authenticate? " ;
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
goto fail ;
}
}
if ( ( auth_alg = = WLAN_AUTH_OPEN & & auth_transaction = = 1 ) | |
( auth_alg = = WLAN_AUTH_SHARED_KEY & &
( auth_transaction = = 1 | |
( auth_transaction = = 3 & & sta ! = NULL & &
sta - > u . sta . challenge ! = NULL ) ) ) ) {
} else {
txt = " unknown authentication transaction number " ;
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION ;
goto fail ;
}
if ( sta = = NULL ) {
txt = " new STA " ;
if ( local - > ap - > num_sta > = MAX_STA_COUNT ) {
/* FIX: might try to remove some old STAs first? */
txt = " no more room for new STAs " ;
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
goto fail ;
}
sta = ap_add_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta = = NULL ) {
txt = " ap_add_sta failed " ;
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
goto fail ;
}
}
switch ( auth_alg ) {
case WLAN_AUTH_OPEN :
txt = " authOK " ;
/* IEEE 802.11 standard is not completely clear about
* whether STA is considered authenticated after
* authentication OK frame has been send or after it
* has been ACKed . In order to reduce interoperability
* issues , mark the STA authenticated before ACK . */
sta - > flags | = WLAN_STA_AUTH ;
break ;
case WLAN_AUTH_SHARED_KEY :
if ( auth_transaction = = 1 ) {
if ( sta - > u . sta . challenge = = NULL ) {
sta - > u . sta . challenge =
ap_auth_make_challenge ( local - > ap ) ;
if ( sta - > u . sta . challenge = = NULL ) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
goto fail ;
}
}
} else {
if ( sta - > u . sta . challenge = = NULL | |
challenge = = NULL | |
memcmp ( sta - > u . sta . challenge , challenge ,
WLAN_AUTH_CHALLENGE_LEN ) ! = 0 | |
2009-02-11 17:17:10 -05:00
! ieee80211_has_protected ( hdr - > frame_control ) ) {
2005-05-12 22:54:16 -04:00
txt = " challenge response incorrect " ;
resp = WLAN_STATUS_CHALLENGE_FAIL ;
goto fail ;
}
txt = " challenge OK - authOK " ;
/* IEEE 802.11 standard is not completely clear about
* whether STA is considered authenticated after
* authentication OK frame has been send or after it
* has been ACKed . In order to reduce interoperability
* issues , mark the STA authenticated before ACK . */
sta - > flags | = WLAN_STA_AUTH ;
kfree ( sta - > u . sta . challenge ) ;
sta - > u . sta . challenge = NULL ;
}
break ;
}
fail :
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) body ;
2005-05-12 22:54:16 -04:00
* pos = cpu_to_le16 ( auth_alg ) ;
pos + + ;
* pos = cpu_to_le16 ( auth_transaction + 1 ) ;
pos + + ;
* pos = cpu_to_le16 ( resp ) ; /* status_code */
pos + + ;
olen = 6 ;
if ( resp = = WLAN_STATUS_SUCCESS & & sta ! = NULL & &
sta - > u . sta . challenge ! = NULL & &
auth_alg = = WLAN_AUTH_SHARED_KEY & & auth_transaction = = 1 ) {
u8 * tmp = ( u8 * ) pos ;
* tmp + + = WLAN_EID_CHALLENGE ;
* tmp + + = WLAN_AUTH_CHALLENGE_LEN ;
pos + + ;
memcpy ( pos , sta - > u . sta . challenge , WLAN_AUTH_CHALLENGE_LEN ) ;
olen + = 2 + WLAN_AUTH_CHALLENGE_LEN ;
}
2005-08-14 19:08:44 -07:00
prism2_send_mgmt ( dev , IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH ,
2005-05-12 22:54:16 -04:00
body , olen , hdr - > addr2 , ap - > tx_callback_auth ) ;
if ( sta ) {
sta - > last_rx = jiffies ;
atomic_dec ( & sta - > users ) ;
}
if ( resp ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: %pM auth (alg=%d "
2007-10-03 17:59:30 -07:00
" trans#=%d stat=%d len=%d fc=%04x) ==> %d (%s) \n " ,
2008-10-27 15:59:26 -07:00
dev - > name , hdr - > addr2 ,
2008-04-08 16:50:44 -07:00
auth_alg , auth_transaction , status_code , len ,
2009-02-11 17:17:10 -05:00
le16_to_cpu ( hdr - > frame_control ) , resp , txt ) ;
2005-05-12 22:54:16 -04:00
}
}
/* Called only as a scheduled task for pending AP frames. */
static void handle_assoc ( local_info_t * local , struct sk_buff * skb ,
struct hostap_80211_rx_status * rx_stats , int reassoc )
{
struct net_device * dev = local - > dev ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
char body [ 12 ] , * p , * lpos ;
int len , left ;
2007-12-21 03:30:16 -05:00
__le16 * pos ;
2005-05-12 22:54:16 -04:00
u16 resp = WLAN_STATUS_SUCCESS ;
struct sta_info * sta = NULL ;
int send_deauth = 0 ;
char * txt = " " ;
u8 prev_ap [ ETH_ALEN ] ;
left = len = skb - > len - IEEE80211_MGMT_HDR_LEN ;
if ( len < ( reassoc ? 10 : 4 ) ) {
PDEBUG ( DEBUG_AP , " %s: handle_assoc - too short payload "
2008-10-27 15:59:26 -07:00
" (len=%d, reassoc=%d) from %pM \n " ,
dev - > name , len , reassoc , hdr - > addr2 ) ;
2005-05-12 22:54:16 -04:00
return ;
}
spin_lock_bh ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta = = NULL | | ( sta - > flags & WLAN_STA_AUTH ) = = 0 ) {
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
txt = " trying to associate before authentication " ;
send_deauth = 1 ;
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
sta = NULL ; /* do not decrement sta->users */
goto fail ;
}
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) ( skb - > data + IEEE80211_MGMT_HDR_LEN ) ;
2005-05-12 22:54:16 -04:00
sta - > capability = __le16_to_cpu ( * pos ) ;
pos + + ; left - = 2 ;
sta - > listen_interval = __le16_to_cpu ( * pos ) ;
pos + + ; left - = 2 ;
if ( reassoc ) {
memcpy ( prev_ap , pos , ETH_ALEN ) ;
pos + + ; pos + + ; pos + + ; left - = 6 ;
} else
memset ( prev_ap , 0 , ETH_ALEN ) ;
if ( left > = 2 ) {
unsigned int ileft ;
unsigned char * u = ( unsigned char * ) pos ;
if ( * u = = WLAN_EID_SSID ) {
u + + ; left - - ;
ileft = * u ;
u + + ; left - - ;
if ( ileft > left | | ileft > MAX_SSID_LEN ) {
txt = " SSID overflow " ;
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
goto fail ;
}
if ( ileft ! = strlen ( local - > essid ) | |
memcmp ( local - > essid , u , ileft ) ! = 0 ) {
txt = " not our SSID " ;
resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC ;
goto fail ;
}
u + = ileft ;
left - = ileft ;
}
if ( left > = 2 & & * u = = WLAN_EID_SUPP_RATES ) {
u + + ; left - - ;
ileft = * u ;
u + + ; left - - ;
2005-07-31 13:08:32 -04:00
2005-05-12 22:54:16 -04:00
if ( ileft > left | | ileft = = 0 | |
ileft > WLAN_SUPP_RATES_MAX ) {
txt = " SUPP_RATES len error " ;
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
goto fail ;
}
memset ( sta - > supported_rates , 0 ,
sizeof ( sta - > supported_rates ) ) ;
memcpy ( sta - > supported_rates , u , ileft ) ;
prism2_check_tx_rates ( sta ) ;
u + = ileft ;
left - = ileft ;
}
if ( left > 0 ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: assoc from %pM "
2007-10-03 17:59:30 -07:00
" with extra data (%d bytes) [ " ,
2008-10-27 15:59:26 -07:00
dev - > name , hdr - > addr2 , left ) ;
2005-05-12 22:54:16 -04:00
while ( left > 0 ) {
PDEBUG2 ( DEBUG_AP , " <%02x> " , * u ) ;
u + + ; left - - ;
}
PDEBUG2 ( DEBUG_AP , " ] \n " ) ;
}
} else {
txt = " frame underflow " ;
resp = WLAN_STATUS_UNSPECIFIED_FAILURE ;
goto fail ;
}
/* get a unique AID */
if ( sta - > aid > 0 )
txt = " OK, old AID " ;
else {
spin_lock_bh ( & local - > ap - > sta_table_lock ) ;
for ( sta - > aid = 1 ; sta - > aid < = MAX_AID_TABLE_SIZE ; sta - > aid + + )
if ( local - > ap - > sta_aid [ sta - > aid - 1 ] = = NULL )
break ;
if ( sta - > aid > MAX_AID_TABLE_SIZE ) {
sta - > aid = 0 ;
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA ;
txt = " no room for more AIDs " ;
} else {
local - > ap - > sta_aid [ sta - > aid - 1 ] = sta ;
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
txt = " OK, new AID " ;
}
}
fail :
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) body ;
2005-05-12 22:54:16 -04:00
if ( send_deauth ) {
2007-12-21 03:30:16 -05:00
* pos = cpu_to_le16 ( WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH ) ;
2005-05-12 22:54:16 -04:00
pos + + ;
} else {
/* FIX: CF-Pollable and CF-PollReq should be set to match the
* values in beacons / probe responses */
/* FIX: how about privacy and WEP? */
/* capability */
2007-12-21 03:30:16 -05:00
* pos = cpu_to_le16 ( WLAN_CAPABILITY_ESS ) ;
2005-05-12 22:54:16 -04:00
pos + + ;
/* status_code */
2007-12-21 03:30:16 -05:00
* pos = cpu_to_le16 ( resp ) ;
2005-05-12 22:54:16 -04:00
pos + + ;
2007-12-21 03:30:16 -05:00
* pos = cpu_to_le16 ( ( sta & & sta - > aid > 0 ? sta - > aid : 0 ) |
2005-05-12 22:54:16 -04:00
BIT ( 14 ) | BIT ( 15 ) ) ; /* AID */
pos + + ;
/* Supported rates (Information element) */
p = ( char * ) pos ;
* p + + = WLAN_EID_SUPP_RATES ;
lpos = p ;
* p + + = 0 ; /* len */
if ( local - > tx_rate_control & WLAN_RATE_1M ) {
* p + + = local - > basic_rates & WLAN_RATE_1M ? 0x82 : 0x02 ;
( * lpos ) + + ;
}
if ( local - > tx_rate_control & WLAN_RATE_2M ) {
* p + + = local - > basic_rates & WLAN_RATE_2M ? 0x84 : 0x04 ;
( * lpos ) + + ;
}
if ( local - > tx_rate_control & WLAN_RATE_5M5 ) {
* p + + = local - > basic_rates & WLAN_RATE_5M5 ?
0x8b : 0x0b ;
( * lpos ) + + ;
}
if ( local - > tx_rate_control & WLAN_RATE_11M ) {
* p + + = local - > basic_rates & WLAN_RATE_11M ?
0x96 : 0x16 ;
( * lpos ) + + ;
}
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) p ;
2005-05-12 22:54:16 -04:00
}
2005-08-14 19:08:44 -07:00
prism2_send_mgmt ( dev , IEEE80211_FTYPE_MGMT |
( send_deauth ? IEEE80211_STYPE_DEAUTH :
( reassoc ? IEEE80211_STYPE_REASSOC_RESP :
IEEE80211_STYPE_ASSOC_RESP ) ) ,
2005-05-12 22:54:16 -04:00
body , ( u8 * ) pos - ( u8 * ) body ,
hdr - > addr2 ,
send_deauth ? 0 : local - > ap - > tx_callback_assoc ) ;
if ( sta ) {
if ( resp = = WLAN_STATUS_SUCCESS ) {
sta - > last_rx = jiffies ;
/* STA will be marked associated from TX callback, if
* AssocResp is ACKed */
}
atomic_dec ( & sta - > users ) ;
}
#if 0
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: %pM %sassoc (len=%d "
" prev_ap=%pM) => %d(%d) (%s) \n " ,
2008-04-08 16:50:44 -07:00
dev - > name ,
2008-10-27 15:59:26 -07:00
hdr - > addr2 ,
2008-04-08 16:50:44 -07:00
reassoc ? " re " : " " , len ,
2008-10-27 15:59:26 -07:00
prev_ap ,
2008-04-08 16:50:44 -07:00
resp , send_deauth , txt ) ;
2005-05-12 22:54:16 -04:00
# endif
}
/* Called only as a scheduled task for pending AP frames. */
static void handle_deauth ( local_info_t * local , struct sk_buff * skb ,
struct hostap_80211_rx_status * rx_stats )
{
struct net_device * dev = local - > dev ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
char * body = ( char * ) ( skb - > data + IEEE80211_MGMT_HDR_LEN ) ;
int len ;
2007-12-21 03:30:16 -05:00
u16 reason_code ;
__le16 * pos ;
2005-05-12 22:54:16 -04:00
struct sta_info * sta = NULL ;
len = skb - > len - IEEE80211_MGMT_HDR_LEN ;
if ( len < 2 ) {
printk ( " handle_deauth - too short payload (len=%d) \n " , len ) ;
return ;
}
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) body ;
reason_code = le16_to_cpu ( * pos ) ;
2005-05-12 22:54:16 -04:00
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: deauthentication: %pM len=%d, "
" reason_code=%d \n " , dev - > name , hdr - > addr2 ,
2008-04-08 16:50:44 -07:00
len , reason_code ) ;
2005-05-12 22:54:16 -04:00
spin_lock_bh ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta ! = NULL ) {
if ( ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap )
hostap_event_expired_sta ( local - > dev , sta ) ;
sta - > flags & = ~ ( WLAN_STA_AUTH | WLAN_STA_ASSOC ) ;
}
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
if ( sta = = NULL ) {
2008-10-27 15:59:26 -07:00
printk ( " %s: deauthentication from %pM, "
2005-05-12 22:54:16 -04:00
" reason_code=%d, but STA not authenticated \n " , dev - > name ,
2008-10-27 15:59:26 -07:00
hdr - > addr2 , reason_code ) ;
2005-05-12 22:54:16 -04:00
}
}
/* Called only as a scheduled task for pending AP frames. */
static void handle_disassoc ( local_info_t * local , struct sk_buff * skb ,
struct hostap_80211_rx_status * rx_stats )
{
struct net_device * dev = local - > dev ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
char * body = skb - > data + IEEE80211_MGMT_HDR_LEN ;
int len ;
2007-12-21 03:30:16 -05:00
u16 reason_code ;
__le16 * pos ;
2005-05-12 22:54:16 -04:00
struct sta_info * sta = NULL ;
len = skb - > len - IEEE80211_MGMT_HDR_LEN ;
if ( len < 2 ) {
printk ( " handle_disassoc - too short payload (len=%d) \n " , len ) ;
return ;
}
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) body ;
reason_code = le16_to_cpu ( * pos ) ;
2005-05-12 22:54:16 -04:00
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: disassociation: %pM len=%d, "
" reason_code=%d \n " , dev - > name , hdr - > addr2 ,
2008-04-08 16:50:44 -07:00
len , reason_code ) ;
2005-05-12 22:54:16 -04:00
spin_lock_bh ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta ! = NULL ) {
if ( ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap )
hostap_event_expired_sta ( local - > dev , sta ) ;
sta - > flags & = ~ WLAN_STA_ASSOC ;
}
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
if ( sta = = NULL ) {
2008-10-27 15:59:26 -07:00
printk ( " %s: disassociation from %pM, "
2005-05-12 22:54:16 -04:00
" reason_code=%d, but STA not authenticated \n " ,
2008-10-27 15:59:26 -07:00
dev - > name , hdr - > addr2 , reason_code ) ;
2005-05-12 22:54:16 -04:00
}
}
/* Called only as a scheduled task for pending AP frames. */
static void ap_handle_data_nullfunc ( local_info_t * local ,
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr )
2005-05-12 22:54:16 -04:00
{
struct net_device * dev = local - > dev ;
/* some STA f/w's seem to require control::ACK frame for
* data : : nullfunc , but at least Prism2 station f / w version 0.8 .0 does
* not send this . .
* send control : : ACK for the data : : nullfunc */
printk ( KERN_DEBUG " Sending control::ACK for data::nullfunc \n " ) ;
2005-08-14 19:08:44 -07:00
prism2_send_mgmt ( dev , IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK ,
2005-05-12 22:54:16 -04:00
NULL , 0 , hdr - > addr2 , 0 ) ;
}
/* Called only as a scheduled task for pending AP frames. */
static void ap_handle_dropped_data ( local_info_t * local ,
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr )
2005-05-12 22:54:16 -04:00
{
struct net_device * dev = local - > dev ;
struct sta_info * sta ;
2007-12-21 03:30:16 -05:00
__le16 reason ;
2005-05-12 22:54:16 -04:00
spin_lock_bh ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
if ( sta ! = NULL & & ( sta - > flags & WLAN_STA_ASSOC ) ) {
PDEBUG ( DEBUG_AP , " ap_handle_dropped_data: STA is now okay? \n " ) ;
atomic_dec ( & sta - > users ) ;
return ;
}
2007-12-21 03:30:16 -05:00
reason = cpu_to_le16 ( WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA ) ;
2005-08-14 19:08:44 -07:00
prism2_send_mgmt ( dev , IEEE80211_FTYPE_MGMT |
2005-05-12 22:54:16 -04:00
( ( sta = = NULL | | ! ( sta - > flags & WLAN_STA_ASSOC ) ) ?
2005-08-14 19:08:44 -07:00
IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC ) ,
2005-05-12 22:54:16 -04:00
( char * ) & reason , sizeof ( reason ) , hdr - > addr2 , 0 ) ;
if ( sta )
atomic_dec ( & sta - > users ) ;
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
/* Called only as a scheduled task for pending AP frames. */
static void pspoll_send_buffered ( local_info_t * local , struct sta_info * sta ,
struct sk_buff * skb )
{
2005-08-14 19:08:39 -07:00
struct hostap_skb_tx_data * meta ;
2005-05-12 22:54:16 -04:00
if ( ! ( sta - > flags & WLAN_STA_PS ) ) {
/* Station has moved to non-PS mode, so send all buffered
* frames using normal device queue . */
dev_queue_xmit ( skb ) ;
return ;
}
/* add a flag for hostap_handle_sta_tx() to know that this skb should
* be passed through even though STA is using PS */
2005-08-14 19:08:39 -07:00
meta = ( struct hostap_skb_tx_data * ) skb - > cb ;
meta - > flags | = HOSTAP_TX_FLAGS_BUFFERED_FRAME ;
2005-05-12 22:54:16 -04:00
if ( ! skb_queue_empty ( & sta - > tx_buf ) ) {
/* indicate to STA that more frames follow */
2005-08-14 19:08:39 -07:00
meta - > flags | = HOSTAP_TX_FLAGS_ADD_MOREDATA ;
2005-05-12 22:54:16 -04:00
}
dev_queue_xmit ( skb ) ;
}
/* Called only as a scheduled task for pending AP frames. */
static void handle_pspoll ( local_info_t * local ,
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ,
2005-05-12 22:54:16 -04:00
struct hostap_80211_rx_status * rx_stats )
{
struct net_device * dev = local - > dev ;
struct sta_info * sta ;
u16 aid ;
struct sk_buff * skb ;
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_PS2 , " handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d \n " ,
2009-02-11 17:17:10 -05:00
hdr - > addr1 , hdr - > addr2 , ! ! ieee80211_has_pm ( hdr - > frame_control ) ) ;
2005-05-12 22:54:16 -04:00
if ( memcmp ( hdr - > addr1 , dev - > dev_addr , ETH_ALEN ) ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP ,
" handle_pspoll - addr1(BSSID)=%pM not own MAC \n " ,
hdr - > addr1 ) ;
2005-05-12 22:54:16 -04:00
return ;
}
2007-12-21 03:30:16 -05:00
aid = le16_to_cpu ( hdr - > duration_id ) ;
2005-05-12 22:54:16 -04:00
if ( ( aid & ( BIT ( 15 ) | BIT ( 14 ) ) ) ! = ( BIT ( 15 ) | BIT ( 14 ) ) ) {
PDEBUG ( DEBUG_PS , " PSPOLL and AID[15:14] not set \n " ) ;
return ;
}
2008-06-27 16:19:58 -04:00
aid & = ~ ( BIT ( 15 ) | BIT ( 14 ) ) ;
2005-05-12 22:54:16 -04:00
if ( aid = = 0 | | aid > MAX_AID_TABLE_SIZE ) {
PDEBUG ( DEBUG_PS , " invalid aid=%d \n " , aid ) ;
return ;
}
PDEBUG ( DEBUG_PS2 , " aid=%d \n " , aid ) ;
spin_lock_bh ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
if ( sta = = NULL ) {
PDEBUG ( DEBUG_PS , " STA not found \n " ) ;
return ;
}
if ( sta - > aid ! = aid ) {
PDEBUG ( DEBUG_PS , " received aid=%i does not match with "
" assoc.aid=%d \n " , aid , sta - > aid ) ;
return ;
}
/* FIX: todo:
* - add timeout for buffering ( clear aid in TIM vector if buffer timed
* out ( expiry time must be longer than ListenInterval for
* the corresponding STA ; " 8802-11: 11.2.1.9 AP aging function "
* - what to do , if buffered , pspolled , and sent frame is not ACKed by
* sta ; store buffer for later use and leave TIM aid bit set ? use
* TX event to check whether frame was ACKed ?
*/
while ( ( skb = skb_dequeue ( & sta - > tx_buf ) ) ! = NULL ) {
/* send buffered frame .. */
PDEBUG ( DEBUG_PS2 , " Sending buffered frame to STA after PS POLL "
" (buffer_count=%d) \n " , skb_queue_len ( & sta - > tx_buf ) ) ;
pspoll_send_buffered ( local , sta , skb ) ;
if ( sta - > flags & WLAN_STA_PS ) {
/* send only one buffered packet per PS Poll */
/* FIX: should ignore further PS Polls until the
* buffered packet that was just sent is acknowledged
* ( Tx or TxExc event ) */
break ;
}
}
if ( skb_queue_empty ( & sta - > tx_buf ) ) {
/* try to clear aid from TIM */
if ( ! ( sta - > flags & WLAN_STA_TIM ) )
PDEBUG ( DEBUG_PS2 , " Re-unsetting TIM for aid %d \n " ,
aid ) ;
hostap_set_tim ( local , aid , 0 ) ;
sta - > flags & = ~ WLAN_STA_TIM ;
}
atomic_dec ( & sta - > users ) ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
2006-11-22 14:57:56 +00:00
static void handle_wds_oper_queue ( struct work_struct * work )
2005-05-12 22:54:16 -04:00
{
2006-11-22 14:57:56 +00:00
struct ap_data * ap = container_of ( work , struct ap_data ,
wds_oper_queue ) ;
local_info_t * local = ap - > local ;
2005-05-12 22:54:16 -04:00
struct wds_oper_data * entry , * prev ;
spin_lock_bh ( & local - > lock ) ;
entry = local - > ap - > wds_oper_entries ;
local - > ap - > wds_oper_entries = NULL ;
spin_unlock_bh ( & local - > lock ) ;
while ( entry ) {
PDEBUG ( DEBUG_AP , " %s: %s automatic WDS connection "
2008-10-27 15:59:26 -07:00
" to AP %pM \n " ,
2005-05-12 22:54:16 -04:00
local - > dev - > name ,
entry - > type = = WDS_ADD ? " adding " : " removing " ,
2008-10-27 15:59:26 -07:00
entry - > addr ) ;
2005-05-12 22:54:16 -04:00
if ( entry - > type = = WDS_ADD )
prism2_wds_add ( local , entry - > addr , 0 ) ;
else if ( entry - > type = = WDS_DEL )
prism2_wds_del ( local , entry - > addr , 0 , 1 ) ;
prev = entry ;
entry = entry - > next ;
kfree ( prev ) ;
}
}
/* Called only as a scheduled task for pending AP frames. */
static void handle_beacon ( local_info_t * local , struct sk_buff * skb ,
struct hostap_80211_rx_status * rx_stats )
{
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
char * body = skb - > data + IEEE80211_MGMT_HDR_LEN ;
int len , left ;
2007-12-21 03:30:16 -05:00
u16 beacon_int , capability ;
__le16 * pos ;
2005-05-12 22:54:16 -04:00
char * ssid = NULL ;
unsigned char * supp_rates = NULL ;
int ssid_len = 0 , supp_rates_len = 0 ;
struct sta_info * sta = NULL ;
int new_sta = 0 , channel = - 1 ;
len = skb - > len - IEEE80211_MGMT_HDR_LEN ;
if ( len < 8 + 2 + 2 ) {
printk ( KERN_DEBUG " handle_beacon - too short payload "
" (len=%d) \n " , len ) ;
return ;
}
2007-12-21 03:30:16 -05:00
pos = ( __le16 * ) body ;
2005-05-12 22:54:16 -04:00
left = len ;
/* Timestamp (8 octets) */
pos + = 4 ; left - = 8 ;
/* Beacon interval (2 octets) */
2007-12-21 03:30:16 -05:00
beacon_int = le16_to_cpu ( * pos ) ;
2005-05-12 22:54:16 -04:00
pos + + ; left - = 2 ;
/* Capability information (2 octets) */
2007-12-21 03:30:16 -05:00
capability = le16_to_cpu ( * pos ) ;
2005-05-12 22:54:16 -04:00
pos + + ; left - = 2 ;
if ( local - > ap - > ap_policy ! = AP_OTHER_AP_EVEN_IBSS & &
capability & WLAN_CAPABILITY_IBSS )
return ;
if ( left > = 2 ) {
unsigned int ileft ;
unsigned char * u = ( unsigned char * ) pos ;
if ( * u = = WLAN_EID_SSID ) {
u + + ; left - - ;
ileft = * u ;
u + + ; left - - ;
if ( ileft > left | | ileft > MAX_SSID_LEN ) {
PDEBUG ( DEBUG_AP , " SSID: overflow \n " ) ;
return ;
}
if ( local - > ap - > ap_policy = = AP_OTHER_AP_SAME_SSID & &
( ileft ! = strlen ( local - > essid ) | |
memcmp ( local - > essid , u , ileft ) ! = 0 ) ) {
/* not our SSID */
return ;
}
ssid = u ;
ssid_len = ileft ;
u + = ileft ;
left - = ileft ;
}
if ( * u = = WLAN_EID_SUPP_RATES ) {
u + + ; left - - ;
ileft = * u ;
u + + ; left - - ;
2005-07-31 13:08:32 -04:00
2005-05-12 22:54:16 -04:00
if ( ileft > left | | ileft = = 0 | | ileft > 8 ) {
PDEBUG ( DEBUG_AP , " - SUPP_RATES len error \n " ) ;
return ;
}
supp_rates = u ;
supp_rates_len = ileft ;
u + = ileft ;
left - = ileft ;
}
if ( * u = = WLAN_EID_DS_PARAMS ) {
u + + ; left - - ;
ileft = * u ;
u + + ; left - - ;
2005-07-31 13:08:32 -04:00
2005-05-12 22:54:16 -04:00
if ( ileft > left | | ileft ! = 1 ) {
PDEBUG ( DEBUG_AP , " - DS_PARAMS len error \n " ) ;
return ;
}
channel = * u ;
u + = ileft ;
left - = ileft ;
}
}
spin_lock_bh ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta ! = NULL )
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & local - > ap - > sta_table_lock ) ;
if ( sta = = NULL ) {
/* add new AP */
new_sta = 1 ;
sta = ap_add_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta = = NULL ) {
printk ( KERN_INFO " prism2: kmalloc failed for AP "
" data structure \n " ) ;
return ;
}
hostap_event_new_sta ( local - > dev , sta ) ;
/* mark APs authentication and associated for pseudo ad-hoc
* style communication */
sta - > flags = WLAN_STA_AUTH | WLAN_STA_ASSOC ;
if ( local - > ap - > autom_ap_wds ) {
hostap_wds_link_oper ( local , sta - > addr , WDS_ADD ) ;
}
}
sta - > ap = 1 ;
if ( ssid ) {
sta - > u . ap . ssid_len = ssid_len ;
memcpy ( sta - > u . ap . ssid , ssid , ssid_len ) ;
sta - > u . ap . ssid [ ssid_len ] = ' \0 ' ;
} else {
sta - > u . ap . ssid_len = 0 ;
sta - > u . ap . ssid [ 0 ] = ' \0 ' ;
}
sta - > u . ap . channel = channel ;
sta - > rx_packets + + ;
sta - > rx_bytes + = len ;
sta - > u . ap . last_beacon = sta - > last_rx = jiffies ;
sta - > capability = capability ;
sta - > listen_interval = beacon_int ;
atomic_dec ( & sta - > users ) ;
if ( new_sta ) {
memset ( sta - > supported_rates , 0 , sizeof ( sta - > supported_rates ) ) ;
memcpy ( sta - > supported_rates , supp_rates , supp_rates_len ) ;
prism2_check_tx_rates ( sta ) ;
}
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
/* Called only as a tasklet. */
static void handle_ap_item ( local_info_t * local , struct sk_buff * skb ,
struct hostap_80211_rx_status * rx_stats )
{
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
struct net_device * dev = local - > dev ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
u16 fc , type , stype ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
/* FIX: should give skb->len to handler functions and check that the
* buffer is long enough */
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
fc = le16_to_cpu ( hdr - > frame_control ) ;
type = fc & IEEE80211_FCTL_FTYPE ;
stype = fc & IEEE80211_FCTL_STYPE ;
2005-05-12 22:54:16 -04:00
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
2005-08-14 19:08:44 -07:00
if ( ! local - > hostapd & & type = = IEEE80211_FTYPE_DATA ) {
2005-05-12 22:54:16 -04:00
PDEBUG ( DEBUG_AP , " handle_ap_item - data frame \n " ) ;
2005-08-14 21:00:01 -07:00
if ( ! ( fc & IEEE80211_FCTL_TODS ) | |
( fc & IEEE80211_FCTL_FROMDS ) ) {
2005-08-14 19:08:44 -07:00
if ( stype = = IEEE80211_STYPE_NULLFUNC ) {
2005-05-12 22:54:16 -04:00
/* no ToDS nullfunc seems to be used to check
* AP association ; so send reject message to
* speed up re - association */
ap_handle_dropped_data ( local , hdr ) ;
goto done ;
}
PDEBUG ( DEBUG_AP , " not ToDS frame (fc=0x%04x) \n " ,
fc ) ;
goto done ;
}
if ( memcmp ( hdr - > addr1 , dev - > dev_addr , ETH_ALEN ) ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " handle_ap_item - addr1(BSSID)=%pM "
" not own MAC \n " , hdr - > addr1 ) ;
2005-05-12 22:54:16 -04:00
goto done ;
}
2005-08-14 19:08:44 -07:00
if ( local - > ap - > nullfunc_ack & &
stype = = IEEE80211_STYPE_NULLFUNC )
2005-05-12 22:54:16 -04:00
ap_handle_data_nullfunc ( local , hdr ) ;
else
ap_handle_dropped_data ( local , hdr ) ;
goto done ;
}
2005-08-14 19:08:44 -07:00
if ( type = = IEEE80211_FTYPE_MGMT & & stype = = IEEE80211_STYPE_BEACON ) {
2005-05-12 22:54:16 -04:00
handle_beacon ( local , skb , rx_stats ) ;
goto done ;
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2005-08-14 19:08:44 -07:00
if ( type = = IEEE80211_FTYPE_CTL & & stype = = IEEE80211_STYPE_PSPOLL ) {
2005-05-12 22:54:16 -04:00
handle_pspoll ( local , hdr , rx_stats ) ;
goto done ;
}
if ( local - > hostapd ) {
PDEBUG ( DEBUG_AP , " Unknown frame in AP queue: type=0x%02x "
" subtype=0x%02x \n " , type , stype ) ;
goto done ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
2005-08-14 19:08:44 -07:00
if ( type ! = IEEE80211_FTYPE_MGMT ) {
2005-05-12 22:54:16 -04:00
PDEBUG ( DEBUG_AP , " handle_ap_item - not a management frame? \n " ) ;
goto done ;
}
if ( memcmp ( hdr - > addr1 , dev - > dev_addr , ETH_ALEN ) ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " handle_ap_item - addr1(DA)=%pM "
" not own MAC \n " , hdr - > addr1 ) ;
2005-05-12 22:54:16 -04:00
goto done ;
}
if ( memcmp ( hdr - > addr3 , dev - > dev_addr , ETH_ALEN ) ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " handle_ap_item - addr3(BSSID)=%pM "
" not own MAC \n " , hdr - > addr3 ) ;
2005-05-12 22:54:16 -04:00
goto done ;
}
switch ( stype ) {
2005-08-14 19:08:44 -07:00
case IEEE80211_STYPE_ASSOC_REQ :
2005-05-12 22:54:16 -04:00
handle_assoc ( local , skb , rx_stats , 0 ) ;
break ;
2005-08-14 19:08:44 -07:00
case IEEE80211_STYPE_ASSOC_RESP :
2005-05-12 22:54:16 -04:00
PDEBUG ( DEBUG_AP , " ==> ASSOC RESP (ignored) \n " ) ;
break ;
2005-08-14 19:08:44 -07:00
case IEEE80211_STYPE_REASSOC_REQ :
2005-05-12 22:54:16 -04:00
handle_assoc ( local , skb , rx_stats , 1 ) ;
break ;
2005-08-14 19:08:44 -07:00
case IEEE80211_STYPE_REASSOC_RESP :
2005-05-12 22:54:16 -04:00
PDEBUG ( DEBUG_AP , " ==> REASSOC RESP (ignored) \n " ) ;
break ;
2005-08-14 19:08:44 -07:00
case IEEE80211_STYPE_ATIM :
2005-05-12 22:54:16 -04:00
PDEBUG ( DEBUG_AP , " ==> ATIM (ignored) \n " ) ;
break ;
2005-08-14 19:08:44 -07:00
case IEEE80211_STYPE_DISASSOC :
2005-05-12 22:54:16 -04:00
handle_disassoc ( local , skb , rx_stats ) ;
break ;
2005-08-14 19:08:44 -07:00
case IEEE80211_STYPE_AUTH :
2005-05-12 22:54:16 -04:00
handle_authen ( local , skb , rx_stats ) ;
break ;
2005-08-14 19:08:44 -07:00
case IEEE80211_STYPE_DEAUTH :
2005-05-12 22:54:16 -04:00
handle_deauth ( local , skb , rx_stats ) ;
break ;
default :
2005-08-14 19:08:44 -07:00
PDEBUG ( DEBUG_AP , " Unknown mgmt frame subtype 0x%02x \n " ,
stype > > 4 ) ;
2005-05-12 22:54:16 -04:00
break ;
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
done :
dev_kfree_skb ( skb ) ;
}
/* Called only as a tasklet (software IRQ) */
void hostap_rx ( struct net_device * dev , struct sk_buff * skb ,
struct hostap_80211_rx_status * rx_stats )
{
struct hostap_interface * iface ;
local_info_t * local ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
iface = netdev_priv ( dev ) ;
local = iface - > local ;
if ( skb - > len < 16 )
goto drop ;
2009-03-20 19:36:42 +00:00
dev - > stats . rx_packets + + ;
2005-05-12 22:54:16 -04:00
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
if ( local - > ap - > ap_policy = = AP_OTHER_AP_SKIP_ALL & &
2009-02-11 17:17:10 -05:00
ieee80211_is_beacon ( hdr - > frame_control ) )
2005-05-12 22:54:16 -04:00
goto drop ;
2009-01-29 13:26:44 -08:00
skb - > protocol = cpu_to_be16 ( ETH_P_HOSTAP ) ;
2005-05-12 22:54:16 -04:00
handle_ap_item ( local , skb , rx_stats ) ;
return ;
drop :
dev_kfree_skb ( skb ) ;
}
/* Called only as a tasklet (software IRQ) */
static void schedule_packet_send ( local_info_t * local , struct sta_info * sta )
{
struct sk_buff * skb ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
struct hostap_80211_rx_status rx_stats ;
if ( skb_queue_empty ( & sta - > tx_buf ) )
return ;
skb = dev_alloc_skb ( 16 ) ;
if ( skb = = NULL ) {
printk ( KERN_DEBUG " %s: schedule_packet_send: skb alloc "
" failed \n " , local - > dev - > name ) ;
return ;
}
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb_put ( skb , 16 ) ;
2005-05-12 22:54:16 -04:00
/* Generate a fake pspoll frame to start packet delivery */
2009-02-11 17:17:10 -05:00
hdr - > frame_control = cpu_to_le16 (
2005-08-14 19:08:44 -07:00
IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL ) ;
2005-05-12 22:54:16 -04:00
memcpy ( hdr - > addr1 , local - > dev - > dev_addr , ETH_ALEN ) ;
memcpy ( hdr - > addr2 , sta - > addr , ETH_ALEN ) ;
hdr - > duration_id = cpu_to_le16 ( sta - > aid | BIT ( 15 ) | BIT ( 14 ) ) ;
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_PS2 ,
" %s: Scheduling buffered packet delivery for STA %pM \n " ,
local - > dev - > name , sta - > addr ) ;
2005-05-12 22:54:16 -04:00
skb - > dev = local - > dev ;
memset ( & rx_stats , 0 , sizeof ( rx_stats ) ) ;
hostap_rx ( local - > dev , skb , & rx_stats ) ;
}
2006-01-14 03:09:34 +01:00
int prism2_ap_get_sta_qual ( local_info_t * local , struct sockaddr addr [ ] ,
struct iw_quality qual [ ] , int buf_size ,
int aplist )
2005-05-12 22:54:16 -04:00
{
struct ap_data * ap = local - > ap ;
struct list_head * ptr ;
int count = 0 ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
for ( ptr = ap - > sta_list . next ; ptr ! = NULL & & ptr ! = & ap - > sta_list ;
ptr = ptr - > next ) {
struct sta_info * sta = ( struct sta_info * ) ptr ;
if ( aplist & & ! sta - > ap )
continue ;
addr [ count ] . sa_family = ARPHRD_ETHER ;
memcpy ( addr [ count ] . sa_data , sta - > addr , ETH_ALEN ) ;
if ( sta - > last_rx_silence = = 0 )
qual [ count ] . qual = sta - > last_rx_signal < 27 ?
0 : ( sta - > last_rx_signal - 27 ) * 92 / 127 ;
else
qual [ count ] . qual = sta - > last_rx_signal -
sta - > last_rx_silence - 35 ;
qual [ count ] . level = HFA384X_LEVEL_TO_dBm ( sta - > last_rx_signal ) ;
qual [ count ] . noise = HFA384X_LEVEL_TO_dBm ( sta - > last_rx_silence ) ;
qual [ count ] . updated = sta - > last_rx_updated ;
2005-09-23 21:58:59 -07:00
sta - > last_rx_updated = IW_QUAL_DBM ;
2005-05-12 22:54:16 -04:00
count + + ;
if ( count > = buf_size )
break ;
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
return count ;
}
/* Translate our list of Access Points & Stations to a card independant
* format that the Wireless Tools will understand - Jean II */
2008-06-16 18:50:49 -07:00
int prism2_ap_translate_scan ( struct net_device * dev ,
struct iw_request_info * info , char * buffer )
2005-05-12 22:54:16 -04:00
{
struct hostap_interface * iface ;
local_info_t * local ;
struct ap_data * ap ;
struct list_head * ptr ;
struct iw_event iwe ;
char * current_ev = buffer ;
char * end_buf = buffer + IW_SCAN_MAX_DATA ;
# if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT)
char buf [ 64 ] ;
# endif
iface = netdev_priv ( dev ) ;
local = iface - > local ;
ap = local - > ap ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
for ( ptr = ap - > sta_list . next ; ptr ! = NULL & & ptr ! = & ap - > sta_list ;
ptr = ptr - > next ) {
struct sta_info * sta = ( struct sta_info * ) ptr ;
/* First entry *MUST* be the AP MAC address */
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
iwe . cmd = SIOCGIWAP ;
iwe . u . ap_addr . sa_family = ARPHRD_ETHER ;
memcpy ( iwe . u . ap_addr . sa_data , sta - > addr , ETH_ALEN ) ;
iwe . len = IW_EV_ADDR_LEN ;
2008-06-16 18:50:49 -07:00
current_ev = iwe_stream_add_event ( info , current_ev , end_buf ,
& iwe , IW_EV_ADDR_LEN ) ;
2005-05-12 22:54:16 -04:00
/* Use the mode to indicate if it's a station or
* an Access Point */
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
iwe . cmd = SIOCGIWMODE ;
if ( sta - > ap )
iwe . u . mode = IW_MODE_MASTER ;
else
iwe . u . mode = IW_MODE_INFRA ;
iwe . len = IW_EV_UINT_LEN ;
2008-06-16 18:50:49 -07:00
current_ev = iwe_stream_add_event ( info , current_ev , end_buf ,
& iwe , IW_EV_UINT_LEN ) ;
2005-05-12 22:54:16 -04:00
/* Some quality */
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
iwe . cmd = IWEVQUAL ;
if ( sta - > last_rx_silence = = 0 )
iwe . u . qual . qual = sta - > last_rx_signal < 27 ?
0 : ( sta - > last_rx_signal - 27 ) * 92 / 127 ;
else
iwe . u . qual . qual = sta - > last_rx_signal -
sta - > last_rx_silence - 35 ;
iwe . u . qual . level = HFA384X_LEVEL_TO_dBm ( sta - > last_rx_signal ) ;
iwe . u . qual . noise = HFA384X_LEVEL_TO_dBm ( sta - > last_rx_silence ) ;
iwe . u . qual . updated = sta - > last_rx_updated ;
iwe . len = IW_EV_QUAL_LEN ;
2008-06-16 18:50:49 -07:00
current_ev = iwe_stream_add_event ( info , current_ev , end_buf ,
& iwe , IW_EV_QUAL_LEN ) ;
2005-05-12 22:54:16 -04:00
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
if ( sta - > ap ) {
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
iwe . cmd = SIOCGIWESSID ;
iwe . u . data . length = sta - > u . ap . ssid_len ;
iwe . u . data . flags = 1 ;
2008-06-16 18:50:49 -07:00
current_ev = iwe_stream_add_point ( info , current_ev ,
end_buf , & iwe ,
2005-05-12 22:54:16 -04:00
sta - > u . ap . ssid ) ;
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
iwe . cmd = SIOCGIWENCODE ;
if ( sta - > capability & WLAN_CAPABILITY_PRIVACY )
iwe . u . data . flags =
IW_ENCODE_ENABLED | IW_ENCODE_NOKEY ;
else
iwe . u . data . flags = IW_ENCODE_DISABLED ;
2008-06-16 18:50:49 -07:00
current_ev = iwe_stream_add_point ( info , current_ev ,
end_buf , & iwe ,
sta - > u . ap . ssid ) ;
2005-05-12 22:54:16 -04:00
if ( sta - > u . ap . channel > 0 & &
sta - > u . ap . channel < = FREQ_COUNT ) {
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
iwe . cmd = SIOCGIWFREQ ;
iwe . u . freq . m = freq_list [ sta - > u . ap . channel - 1 ]
* 100000 ;
iwe . u . freq . e = 1 ;
current_ev = iwe_stream_add_event (
2008-06-16 18:50:49 -07:00
info , current_ev , end_buf , & iwe ,
2005-05-12 22:54:16 -04:00
IW_EV_FREQ_LEN ) ;
}
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
iwe . cmd = IWEVCUSTOM ;
sprintf ( buf , " beacon_interval=%d " ,
sta - > listen_interval ) ;
iwe . u . data . length = strlen ( buf ) ;
2008-06-16 18:50:49 -07:00
current_ev = iwe_stream_add_point ( info , current_ev ,
end_buf , & iwe , buf ) ;
2005-05-12 22:54:16 -04:00
}
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
2005-09-23 21:58:59 -07:00
sta - > last_rx_updated = IW_QUAL_DBM ;
2005-05-12 22:54:16 -04:00
/* To be continued, we should make good use of IWEVCUSTOM */
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
return current_ev - buffer ;
}
static int prism2_hostapd_add_sta ( struct ap_data * ap ,
struct prism2_hostapd_param * param )
{
struct sta_info * sta ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , param - > sta_addr ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( sta = = NULL ) {
sta = ap_add_sta ( ap , param - > sta_addr ) ;
if ( sta = = NULL )
return - 1 ;
}
if ( ! ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap & & sta - > local )
hostap_event_new_sta ( sta - > local - > dev , sta ) ;
sta - > flags | = WLAN_STA_AUTH | WLAN_STA_ASSOC ;
sta - > last_rx = jiffies ;
sta - > aid = param - > u . add_sta . aid ;
sta - > capability = param - > u . add_sta . capability ;
sta - > tx_supp_rates = param - > u . add_sta . tx_supp_rates ;
if ( sta - > tx_supp_rates & WLAN_RATE_1M )
sta - > supported_rates [ 0 ] = 2 ;
if ( sta - > tx_supp_rates & WLAN_RATE_2M )
sta - > supported_rates [ 1 ] = 4 ;
if ( sta - > tx_supp_rates & WLAN_RATE_5M5 )
sta - > supported_rates [ 2 ] = 11 ;
if ( sta - > tx_supp_rates & WLAN_RATE_11M )
sta - > supported_rates [ 3 ] = 22 ;
prism2_check_tx_rates ( sta ) ;
atomic_dec ( & sta - > users ) ;
return 0 ;
}
static int prism2_hostapd_remove_sta ( struct ap_data * ap ,
struct prism2_hostapd_param * param )
{
struct sta_info * sta ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , param - > sta_addr ) ;
if ( sta ) {
ap_sta_hash_del ( ap , sta ) ;
list_del ( & sta - > list ) ;
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( ! sta )
return - ENOENT ;
if ( ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap & & sta - > local )
hostap_event_expired_sta ( sta - > local - > dev , sta ) ;
ap_free_sta ( ap , sta ) ;
return 0 ;
}
static int prism2_hostapd_get_info_sta ( struct ap_data * ap ,
struct prism2_hostapd_param * param )
{
struct sta_info * sta ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , param - > sta_addr ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( ! sta )
return - ENOENT ;
param - > u . get_info_sta . inactive_sec = ( jiffies - sta - > last_rx ) / HZ ;
atomic_dec ( & sta - > users ) ;
return 1 ;
}
static int prism2_hostapd_set_flags_sta ( struct ap_data * ap ,
struct prism2_hostapd_param * param )
{
struct sta_info * sta ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , param - > sta_addr ) ;
if ( sta ) {
sta - > flags | = param - > u . set_flags_sta . flags_or ;
sta - > flags & = param - > u . set_flags_sta . flags_and ;
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( ! sta )
return - ENOENT ;
return 0 ;
}
static int prism2_hostapd_sta_clear_stats ( struct ap_data * ap ,
struct prism2_hostapd_param * param )
{
struct sta_info * sta ;
int rate ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , param - > sta_addr ) ;
if ( sta ) {
sta - > rx_packets = sta - > tx_packets = 0 ;
sta - > rx_bytes = sta - > tx_bytes = 0 ;
for ( rate = 0 ; rate < WLAN_RATE_COUNT ; rate + + ) {
sta - > tx_count [ rate ] = 0 ;
sta - > rx_count [ rate ] = 0 ;
}
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( ! sta )
return - ENOENT ;
return 0 ;
}
2006-01-14 03:09:34 +01:00
int prism2_hostapd ( struct ap_data * ap , struct prism2_hostapd_param * param )
2005-05-12 22:54:16 -04:00
{
switch ( param - > cmd ) {
case PRISM2_HOSTAPD_FLUSH :
ap_control_kickall ( ap ) ;
return 0 ;
case PRISM2_HOSTAPD_ADD_STA :
return prism2_hostapd_add_sta ( ap , param ) ;
case PRISM2_HOSTAPD_REMOVE_STA :
return prism2_hostapd_remove_sta ( ap , param ) ;
case PRISM2_HOSTAPD_GET_INFO_STA :
return prism2_hostapd_get_info_sta ( ap , param ) ;
case PRISM2_HOSTAPD_SET_FLAGS_STA :
return prism2_hostapd_set_flags_sta ( ap , param ) ;
case PRISM2_HOSTAPD_STA_CLEAR_STATS :
return prism2_hostapd_sta_clear_stats ( ap , param ) ;
default :
printk ( KERN_WARNING " prism2_hostapd: unknown cmd=%d \n " ,
param - > cmd ) ;
return - EOPNOTSUPP ;
}
}
/* Update station info for host-based TX rate control and return current
* TX rate */
static int ap_update_sta_tx_rate ( struct sta_info * sta , struct net_device * dev )
{
int ret = sta - > tx_rate ;
struct hostap_interface * iface ;
local_info_t * local ;
iface = netdev_priv ( dev ) ;
local = iface - > local ;
sta - > tx_count [ sta - > tx_rate_idx ] + + ;
sta - > tx_since_last_failure + + ;
sta - > tx_consecutive_exc = 0 ;
if ( sta - > tx_since_last_failure > = WLAN_RATE_UPDATE_COUNT & &
sta - > tx_rate_idx < sta - > tx_max_rate ) {
/* use next higher rate */
int old_rate , new_rate ;
old_rate = new_rate = sta - > tx_rate_idx ;
while ( new_rate < sta - > tx_max_rate ) {
new_rate + + ;
if ( ap_tx_rate_ok ( new_rate , sta , local ) ) {
sta - > tx_rate_idx = new_rate ;
break ;
}
}
if ( old_rate ! = sta - > tx_rate_idx ) {
switch ( sta - > tx_rate_idx ) {
case 0 : sta - > tx_rate = 10 ; break ;
case 1 : sta - > tx_rate = 20 ; break ;
case 2 : sta - > tx_rate = 55 ; break ;
case 3 : sta - > tx_rate = 110 ; break ;
default : sta - > tx_rate = 0 ; break ;
}
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: STA %pM TX rate raised to %d \n " ,
dev - > name , sta - > addr , sta - > tx_rate ) ;
2005-05-12 22:54:16 -04:00
}
sta - > tx_since_last_failure = 0 ;
}
return ret ;
}
/* Called only from software IRQ. Called for each TX frame prior possible
* encryption and transmit . */
ap_tx_ret hostap_handle_sta_tx ( local_info_t * local , struct hostap_tx_data * tx )
{
struct sta_info * sta = NULL ;
struct sk_buff * skb = tx - > skb ;
int set_tim , ret ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
struct hostap_skb_tx_data * meta ;
meta = ( struct hostap_skb_tx_data * ) skb - > cb ;
ret = AP_TX_CONTINUE ;
if ( local - > ap = = NULL | | skb - > len < 10 | |
meta - > iface - > type = = HOSTAP_INTERFACE_STA )
goto out ;
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
if ( hdr - > addr1 [ 0 ] & 0x01 ) {
/* broadcast/multicast frame - no AP related processing */
2007-05-28 09:38:47 -07:00
if ( local - > ap - > num_sta < = 0 )
ret = AP_TX_DROP ;
2005-05-12 22:54:16 -04:00
goto out ;
}
/* unicast packet - check whether destination STA is associated */
spin_lock ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr1 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock ( & local - > ap - > sta_table_lock ) ;
2005-08-14 19:08:39 -07:00
if ( local - > iw_mode = = IW_MODE_MASTER & & sta = = NULL & &
! ( meta - > flags & HOSTAP_TX_FLAGS_WDS ) & &
2005-05-12 22:54:16 -04:00
meta - > iface - > type ! = HOSTAP_INTERFACE_MASTER & &
meta - > iface - > type ! = HOSTAP_INTERFACE_AP ) {
#if 0
/* This can happen, e.g., when wlan0 is added to a bridge and
* bridging code does not know which port is the correct target
* for a unicast frame . In this case , the packet is send to all
* ports of the bridge . Since this is a valid scenario , do not
* print out any errors here . */
if ( net_ratelimit ( ) ) {
printk ( KERN_DEBUG " AP: drop packet to non-associated "
2008-10-27 15:59:26 -07:00
" STA %pM \n " , hdr - > addr1 ) ;
2005-05-12 22:54:16 -04:00
}
# endif
local - > ap - > tx_drop_nonassoc + + ;
ret = AP_TX_DROP ;
goto out ;
}
if ( sta = = NULL )
goto out ;
if ( ! ( sta - > flags & WLAN_STA_AUTHORIZED ) )
ret = AP_TX_CONTINUE_NOT_AUTHORIZED ;
/* Set tx_rate if using host-based TX rate control */
if ( ! local - > fw_tx_rate_control )
local - > ap - > last_tx_rate = meta - > rate =
ap_update_sta_tx_rate ( sta , local - > dev ) ;
if ( local - > iw_mode ! = IW_MODE_MASTER )
goto out ;
if ( ! ( sta - > flags & WLAN_STA_PS ) )
goto out ;
2005-08-14 19:08:39 -07:00
if ( meta - > flags & HOSTAP_TX_FLAGS_ADD_MOREDATA ) {
/* indicate to STA that more frames follow */
2009-02-11 17:17:10 -05:00
hdr - > frame_control | =
2009-01-29 13:26:44 -08:00
cpu_to_le16 ( IEEE80211_FCTL_MOREDATA ) ;
2005-08-14 19:08:39 -07:00
}
2005-05-12 22:54:16 -04:00
2005-08-14 19:08:39 -07:00
if ( meta - > flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME ) {
/* packet was already buffered and now send due to
* PS poll , so do not rebuffer it */
goto out ;
2005-05-12 22:54:16 -04:00
}
if ( skb_queue_len ( & sta - > tx_buf ) > = STA_MAX_TX_BUFFER ) {
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_PS , " %s: No more space in STA (%pM)'s "
" PS mode buffer \n " ,
local - > dev - > name , sta - > addr ) ;
2005-05-12 22:54:16 -04:00
/* Make sure that TIM is set for the station (it might not be
* after AP wlan hw reset ) . */
/* FIX: should fix hw reset to restore bits based on STA
* buffer state . . */
hostap_set_tim ( local , sta - > aid , 1 ) ;
sta - > flags | = WLAN_STA_TIM ;
ret = AP_TX_DROP ;
goto out ;
}
/* STA in PS mode, buffer frame for later delivery */
set_tim = skb_queue_empty ( & sta - > tx_buf ) ;
skb_queue_tail ( & sta - > tx_buf , skb ) ;
/* FIX: could save RX time to skb and expire buffered frames after
* some time if STA does not poll for them */
if ( set_tim ) {
if ( sta - > flags & WLAN_STA_TIM )
PDEBUG ( DEBUG_PS2 , " Re-setting TIM for aid %d \n " ,
sta - > aid ) ;
hostap_set_tim ( local , sta - > aid , 1 ) ;
sta - > flags | = WLAN_STA_TIM ;
}
ret = AP_TX_BUFFERED ;
out :
if ( sta ! = NULL ) {
if ( ret = = AP_TX_CONTINUE | |
ret = = AP_TX_CONTINUE_NOT_AUTHORIZED ) {
sta - > tx_packets + + ;
sta - > tx_bytes + = skb - > len ;
sta - > last_tx = jiffies ;
}
if ( ( ret = = AP_TX_CONTINUE | |
ret = = AP_TX_CONTINUE_NOT_AUTHORIZED ) & &
sta - > crypt & & tx - > host_encrypt ) {
tx - > crypt = sta - > crypt ;
tx - > sta_ptr = sta ; /* hostap_handle_sta_release() will
* be called to release sta info
* later */
} else
atomic_dec ( & sta - > users ) ;
}
return ret ;
}
void hostap_handle_sta_release ( void * ptr )
{
struct sta_info * sta = ptr ;
atomic_dec ( & sta - > users ) ;
}
/* Called only as a tasklet (software IRQ) */
void hostap_handle_sta_tx_exc ( local_info_t * local , struct sk_buff * skb )
{
struct sta_info * sta ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
struct hostap_skb_tx_data * meta ;
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
meta = ( struct hostap_skb_tx_data * ) skb - > cb ;
spin_lock ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr1 ) ;
if ( ! sta ) {
spin_unlock ( & local - > ap - > sta_table_lock ) ;
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP , " %s: Could not find STA %pM "
2007-10-03 17:59:30 -07:00
" for this TX error (@%lu) \n " ,
2008-10-27 15:59:26 -07:00
local - > dev - > name , hdr - > addr1 , jiffies ) ;
2005-05-12 22:54:16 -04:00
return ;
}
sta - > tx_since_last_failure = 0 ;
sta - > tx_consecutive_exc + + ;
2005-07-31 13:08:32 -04:00
2005-05-12 22:54:16 -04:00
if ( sta - > tx_consecutive_exc > = WLAN_RATE_DECREASE_THRESHOLD & &
sta - > tx_rate_idx > 0 & & meta - > rate < = sta - > tx_rate ) {
/* use next lower rate */
int old , rate ;
old = rate = sta - > tx_rate_idx ;
while ( rate > 0 ) {
rate - - ;
if ( ap_tx_rate_ok ( rate , sta , local ) ) {
sta - > tx_rate_idx = rate ;
break ;
}
}
if ( old ! = sta - > tx_rate_idx ) {
switch ( sta - > tx_rate_idx ) {
case 0 : sta - > tx_rate = 10 ; break ;
case 1 : sta - > tx_rate = 20 ; break ;
case 2 : sta - > tx_rate = 55 ; break ;
case 3 : sta - > tx_rate = 110 ; break ;
default : sta - > tx_rate = 0 ; break ;
}
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_AP ,
" %s: STA %pM TX rate lowered to %d \n " ,
local - > dev - > name , sta - > addr , sta - > tx_rate ) ;
2005-05-12 22:54:16 -04:00
}
sta - > tx_consecutive_exc = 0 ;
}
spin_unlock ( & local - > ap - > sta_table_lock ) ;
}
static void hostap_update_sta_ps2 ( local_info_t * local , struct sta_info * sta ,
int pwrmgt , int type , int stype )
{
if ( pwrmgt & & ! ( sta - > flags & WLAN_STA_PS ) ) {
sta - > flags | = WLAN_STA_PS ;
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_PS2 , " STA %pM changed to use PS "
2005-05-12 22:54:16 -04:00
" mode (type=0x%02X, stype=0x%02X) \n " ,
2008-10-27 15:59:26 -07:00
sta - > addr , type > > 2 , stype > > 4 ) ;
2005-05-12 22:54:16 -04:00
} else if ( ! pwrmgt & & ( sta - > flags & WLAN_STA_PS ) ) {
sta - > flags & = ~ WLAN_STA_PS ;
2008-10-27 15:59:26 -07:00
PDEBUG ( DEBUG_PS2 , " STA %pM changed to not use "
2005-05-12 22:54:16 -04:00
" PS mode (type=0x%02X, stype=0x%02X) \n " ,
2008-10-27 15:59:26 -07:00
sta - > addr , type > > 2 , stype > > 4 ) ;
2005-08-14 19:08:44 -07:00
if ( type ! = IEEE80211_FTYPE_CTL | |
stype ! = IEEE80211_STYPE_PSPOLL )
2005-05-12 22:54:16 -04:00
schedule_packet_send ( local , sta ) ;
}
}
/* Called only as a tasklet (software IRQ). Called for each RX frame to update
2009-02-11 17:17:10 -05:00
* STA power saving state . pwrmgt is a flag from 802.11 frame_control field . */
int hostap_update_sta_ps ( local_info_t * local , struct ieee80211_hdr * hdr )
2005-05-12 22:54:16 -04:00
{
struct sta_info * sta ;
u16 fc ;
spin_lock ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock ( & local - > ap - > sta_table_lock ) ;
if ( ! sta )
return - 1 ;
2009-02-11 17:17:10 -05:00
fc = le16_to_cpu ( hdr - > frame_control ) ;
2005-08-14 21:00:01 -07:00
hostap_update_sta_ps2 ( local , sta , fc & IEEE80211_FCTL_PM ,
2009-02-11 17:17:10 -05:00
fc & IEEE80211_FCTL_FTYPE ,
fc & IEEE80211_FCTL_STYPE ) ;
2005-05-12 22:54:16 -04:00
atomic_dec ( & sta - > users ) ;
return 0 ;
}
/* Called only as a tasklet (software IRQ). Called for each RX frame after
* getting RX header and payload from hardware . */
ap_rx_ret hostap_handle_sta_rx ( local_info_t * local , struct net_device * dev ,
struct sk_buff * skb ,
struct hostap_80211_rx_status * rx_stats ,
int wds )
{
int ret ;
struct sta_info * sta ;
u16 fc , type , stype ;
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ;
2005-05-12 22:54:16 -04:00
if ( local - > ap = = NULL )
return AP_RX_CONTINUE ;
2009-02-11 17:17:10 -05:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-12 22:54:16 -04:00
2009-02-11 17:17:10 -05:00
fc = le16_to_cpu ( hdr - > frame_control ) ;
type = fc & IEEE80211_FCTL_FTYPE ;
stype = fc & IEEE80211_FCTL_STYPE ;
2005-05-12 22:54:16 -04:00
spin_lock ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock ( & local - > ap - > sta_table_lock ) ;
if ( sta & & ! ( sta - > flags & WLAN_STA_AUTHORIZED ) )
ret = AP_RX_CONTINUE_NOT_AUTHORIZED ;
else
ret = AP_RX_CONTINUE ;
2005-08-14 21:00:01 -07:00
if ( fc & IEEE80211_FCTL_TODS ) {
2005-05-12 22:54:16 -04:00
if ( ! wds & & ( sta = = NULL | | ! ( sta - > flags & WLAN_STA_ASSOC ) ) ) {
if ( local - > hostapd ) {
prism2_rx_80211 ( local - > apdev , skb , rx_stats ,
PRISM2_RX_NON_ASSOC ) ;
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
} else {
printk ( KERN_DEBUG " %s: dropped received packet "
2008-10-27 15:59:26 -07:00
" from non-associated STA %pM "
2005-05-12 22:54:16 -04:00
" (type=0x%02x, subtype=0x%02x) \n " ,
2008-10-27 15:59:26 -07:00
dev - > name , hdr - > addr2 ,
2005-08-14 19:08:44 -07:00
type > > 2 , stype > > 4 ) ;
2005-05-12 22:54:16 -04:00
hostap_rx ( dev , skb , rx_stats ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
}
ret = AP_RX_EXIT ;
goto out ;
}
2005-08-14 21:00:01 -07:00
} else if ( fc & IEEE80211_FCTL_FROMDS ) {
2005-05-12 22:54:16 -04:00
if ( ! wds ) {
/* FromDS frame - not for us; probably
* broadcast / multicast in another BSS - drop */
if ( memcmp ( hdr - > addr1 , dev - > dev_addr , ETH_ALEN ) = = 0 ) {
printk ( KERN_DEBUG " Odd.. FromDS packet "
" received with own BSSID \n " ) ;
hostap_dump_rx_80211 ( dev - > name , skb , rx_stats ) ;
}
ret = AP_RX_DROP ;
goto out ;
}
2005-08-14 19:08:44 -07:00
} else if ( stype = = IEEE80211_STYPE_NULLFUNC & & sta = = NULL & &
2005-05-12 22:54:16 -04:00
memcmp ( hdr - > addr1 , dev - > dev_addr , ETH_ALEN ) = = 0 ) {
if ( local - > hostapd ) {
prism2_rx_80211 ( local - > apdev , skb , rx_stats ,
PRISM2_RX_NON_ASSOC ) ;
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
} else {
/* At least Lucent f/w seems to send data::nullfunc
* frames with no ToDS flag when the current AP returns
* after being unavailable for some time . Speed up
* re - association by informing the station about it not
* being associated . */
2008-10-27 15:59:26 -07:00
printk ( KERN_DEBUG " %s: rejected received nullfunc frame "
" without ToDS from not associated STA %pM \n " ,
dev - > name , hdr - > addr2 ) ;
2005-05-12 22:54:16 -04:00
hostap_rx ( dev , skb , rx_stats ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
}
ret = AP_RX_EXIT ;
goto out ;
2005-08-14 19:08:44 -07:00
} else if ( stype = = IEEE80211_STYPE_NULLFUNC ) {
2005-05-12 22:54:16 -04:00
/* At least Lucent cards seem to send periodic nullfunc
* frames with ToDS . Let these through to update SQ
* stats and PS state . Nullfunc frames do not contain
* any data and they will be dropped below . */
} else {
/* If BSSID (Addr3) is foreign, this frame is a normal
* broadcast frame from an IBSS network . Drop it silently .
* If BSSID is own , report the dropping of this frame . */
if ( memcmp ( hdr - > addr3 , dev - > dev_addr , ETH_ALEN ) = = 0 ) {
2008-10-27 15:59:26 -07:00
printk ( KERN_DEBUG " %s: dropped received packet from %pM "
" with no ToDS flag "
2007-10-03 17:59:30 -07:00
" (type=0x%02x, subtype=0x%02x) \n " , dev - > name ,
2008-10-27 15:59:26 -07:00
hdr - > addr2 , type > > 2 , stype > > 4 ) ;
2005-05-12 22:54:16 -04:00
hostap_dump_rx_80211 ( dev - > name , skb , rx_stats ) ;
}
ret = AP_RX_DROP ;
goto out ;
}
if ( sta ) {
2005-08-14 21:00:01 -07:00
hostap_update_sta_ps2 ( local , sta , fc & IEEE80211_FCTL_PM ,
2005-05-12 22:54:16 -04:00
type , stype ) ;
sta - > rx_packets + + ;
sta - > rx_bytes + = skb - > len ;
sta - > last_rx = jiffies ;
}
2005-08-14 19:08:44 -07:00
if ( local - > ap - > nullfunc_ack & & stype = = IEEE80211_STYPE_NULLFUNC & &
2005-08-14 21:00:01 -07:00
fc & IEEE80211_FCTL_TODS ) {
2005-05-12 22:54:16 -04:00
if ( local - > hostapd ) {
prism2_rx_80211 ( local - > apdev , skb , rx_stats ,
PRISM2_RX_NULLFUNC_ACK ) ;
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
} else {
/* some STA f/w's seem to require control::ACK frame
* for data : : nullfunc , but Prism2 f / w 0.8 .0 ( at least
* from Compaq ) does not send this . . Try to generate
* ACK for these frames from the host driver to make
* power saving work with , e . g . , Lucent WaveLAN f / w */
hostap_rx ( dev , skb , rx_stats ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
}
ret = AP_RX_EXIT ;
goto out ;
}
out :
if ( sta )
atomic_dec ( & sta - > users ) ;
return ret ;
}
/* Called only as a tasklet (software IRQ) */
int hostap_handle_sta_crypto ( local_info_t * local ,
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ,
2008-10-29 11:35:05 -04:00
struct lib80211_crypt_data * * crypt ,
2005-07-30 20:43:20 -07:00
void * * sta_ptr )
2005-05-12 22:54:16 -04:00
{
struct sta_info * sta ;
spin_lock ( & local - > ap - > sta_table_lock ) ;
sta = ap_get_sta ( local - > ap , hdr - > addr2 ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock ( & local - > ap - > sta_table_lock ) ;
if ( ! sta )
return - 1 ;
if ( sta - > crypt ) {
* crypt = sta - > crypt ;
* sta_ptr = sta ;
/* hostap_handle_sta_release() will be called to release STA
* info */
} else
atomic_dec ( & sta - > users ) ;
return 0 ;
}
/* Called only as a tasklet (software IRQ) */
int hostap_is_sta_assoc ( struct ap_data * ap , u8 * sta_addr )
{
struct sta_info * sta ;
int ret = 0 ;
spin_lock ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , sta_addr ) ;
if ( sta ! = NULL & & ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap )
ret = 1 ;
spin_unlock ( & ap - > sta_table_lock ) ;
return ret ;
}
/* Called only as a tasklet (software IRQ) */
int hostap_is_sta_authorized ( struct ap_data * ap , u8 * sta_addr )
{
struct sta_info * sta ;
int ret = 0 ;
spin_lock ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , sta_addr ) ;
if ( sta ! = NULL & & ( sta - > flags & WLAN_STA_ASSOC ) & & ! sta - > ap & &
( ( sta - > flags & WLAN_STA_AUTHORIZED ) | |
ap - > local - > ieee_802_1x = = 0 ) )
ret = 1 ;
spin_unlock ( & ap - > sta_table_lock ) ;
return ret ;
}
/* Called only as a tasklet (software IRQ) */
int hostap_add_sta ( struct ap_data * ap , u8 * sta_addr )
{
struct sta_info * sta ;
int ret = 1 ;
if ( ! ap )
return - 1 ;
spin_lock ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , sta_addr ) ;
if ( sta )
ret = 0 ;
spin_unlock ( & ap - > sta_table_lock ) ;
if ( ret = = 1 ) {
sta = ap_add_sta ( ap , sta_addr ) ;
if ( ! sta )
2006-03-19 19:21:45 -08:00
return - 1 ;
2005-05-12 22:54:16 -04:00
sta - > flags = WLAN_STA_AUTH | WLAN_STA_ASSOC ;
sta - > ap = 1 ;
memset ( sta - > supported_rates , 0 , sizeof ( sta - > supported_rates ) ) ;
/* No way of knowing which rates are supported since we did not
* get supported rates element from beacon / assoc req . Assume
* that remote end supports all 802.11 b rates . */
sta - > supported_rates [ 0 ] = 0x82 ;
sta - > supported_rates [ 1 ] = 0x84 ;
sta - > supported_rates [ 2 ] = 0x0b ;
sta - > supported_rates [ 3 ] = 0x16 ;
sta - > tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M |
WLAN_RATE_5M5 | WLAN_RATE_11M ;
sta - > tx_rate = 110 ;
sta - > tx_max_rate = sta - > tx_rate_idx = 3 ;
}
return ret ;
}
/* Called only as a tasklet (software IRQ) */
int hostap_update_rx_stats ( struct ap_data * ap ,
2009-02-11 17:17:10 -05:00
struct ieee80211_hdr * hdr ,
2005-05-12 22:54:16 -04:00
struct hostap_80211_rx_status * rx_stats )
{
struct sta_info * sta ;
if ( ! ap )
return - 1 ;
spin_lock ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , hdr - > addr2 ) ;
if ( sta ) {
sta - > last_rx_silence = rx_stats - > noise ;
sta - > last_rx_signal = rx_stats - > signal ;
sta - > last_rx_rate = rx_stats - > rate ;
2005-09-23 21:58:59 -07:00
sta - > last_rx_updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM ;
2005-05-12 22:54:16 -04:00
if ( rx_stats - > rate = = 10 )
sta - > rx_count [ 0 ] + + ;
else if ( rx_stats - > rate = = 20 )
sta - > rx_count [ 1 ] + + ;
else if ( rx_stats - > rate = = 55 )
sta - > rx_count [ 2 ] + + ;
else if ( rx_stats - > rate = = 110 )
sta - > rx_count [ 3 ] + + ;
}
spin_unlock ( & ap - > sta_table_lock ) ;
return sta ? 0 : - 1 ;
}
void hostap_update_rates ( local_info_t * local )
{
2007-05-28 09:38:48 -07:00
struct sta_info * sta ;
2005-05-12 22:54:16 -04:00
struct ap_data * ap = local - > ap ;
if ( ! ap )
return ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
2007-05-28 09:38:48 -07:00
list_for_each_entry ( sta , & ap - > sta_list , list ) {
2005-05-12 22:54:16 -04:00
prism2_check_tx_rates ( sta ) ;
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
}
2006-01-14 03:09:34 +01:00
void * ap_crypt_get_ptrs ( struct ap_data * ap , u8 * addr , int permanent ,
2008-10-29 11:35:05 -04:00
struct lib80211_crypt_data * * * crypt )
2005-05-12 22:54:16 -04:00
{
struct sta_info * sta ;
spin_lock_bh ( & ap - > sta_table_lock ) ;
sta = ap_get_sta ( ap , addr ) ;
if ( sta )
atomic_inc ( & sta - > users ) ;
spin_unlock_bh ( & ap - > sta_table_lock ) ;
if ( ! sta & & permanent )
sta = ap_add_sta ( ap , addr ) ;
if ( ! sta )
return NULL ;
if ( permanent )
sta - > flags | = WLAN_STA_PERM ;
* crypt = & sta - > crypt ;
return sta ;
}
void hostap_add_wds_links ( local_info_t * local )
{
struct ap_data * ap = local - > ap ;
2007-05-28 09:38:48 -07:00
struct sta_info * sta ;
2005-05-12 22:54:16 -04:00
spin_lock_bh ( & ap - > sta_table_lock ) ;
2007-05-28 09:38:48 -07:00
list_for_each_entry ( sta , & ap - > sta_list , list ) {
2005-05-12 22:54:16 -04:00
if ( sta - > ap )
hostap_wds_link_oper ( local , sta - > addr , WDS_ADD ) ;
}
spin_unlock_bh ( & ap - > sta_table_lock ) ;
schedule_work ( & local - > ap - > wds_oper_queue ) ;
}
void hostap_wds_link_oper ( local_info_t * local , u8 * addr , wds_oper_type type )
{
struct wds_oper_data * entry ;
entry = kmalloc ( sizeof ( * entry ) , GFP_ATOMIC ) ;
if ( ! entry )
return ;
memcpy ( entry - > addr , addr , ETH_ALEN ) ;
entry - > type = type ;
spin_lock_bh ( & local - > lock ) ;
entry - > next = local - > ap - > wds_oper_entries ;
local - > ap - > wds_oper_entries = entry ;
spin_unlock_bh ( & local - > lock ) ;
schedule_work ( & local - > ap - > wds_oper_queue ) ;
}
EXPORT_SYMBOL ( hostap_init_data ) ;
EXPORT_SYMBOL ( hostap_init_ap_proc ) ;
EXPORT_SYMBOL ( hostap_free_data ) ;
EXPORT_SYMBOL ( hostap_check_sta_fw_version ) ;
EXPORT_SYMBOL ( hostap_handle_sta_tx_exc ) ;
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */