2005-05-12 22:54:16 -04:00
/*
* Host AP ( software wireless LAN access point ) driver for
* Intersil Prism2 / 2.5 / 3 - hostap . o module , common routines
*
* 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 program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation . See README and COPYING for
* more details .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/proc_fs.h>
# include <linux/if_arp.h>
# include <linux/delay.h>
# include <linux/random.h>
# include <linux/workqueue.h>
# include <linux/kmod.h>
# include <linux/rtnetlink.h>
# include <linux/wireless.h>
2006-01-14 03:09:34 +01:00
# include <linux/etherdevice.h>
2007-09-12 12:01:34 +02:00
# include <net/net_namespace.h>
2005-05-12 22:54:16 -04:00
# include <net/iw_handler.h>
2008-10-29 11:35:05 -04:00
# include <net/lib80211.h>
2005-05-12 22:54:16 -04:00
# include <asm/uaccess.h>
# include "hostap_wlan.h"
# include "hostap_80211.h"
# include "hostap_ap.h"
# include "hostap.h"
MODULE_AUTHOR ( " Jouni Malinen " ) ;
MODULE_DESCRIPTION ( " Host AP common routines " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define TX_TIMEOUT (2 * HZ)
# define PRISM2_MAX_FRAME_SIZE 2304
# define PRISM2_MIN_MTU 256
/* FIX: */
# define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */ ))
struct net_device * hostap_add_interface ( struct local_info * local ,
int type , int rtnl_locked ,
const char * prefix ,
const char * name )
{
struct net_device * dev , * mdev ;
struct hostap_interface * iface ;
int ret ;
dev = alloc_etherdev ( sizeof ( struct hostap_interface ) ) ;
if ( dev = = NULL )
return NULL ;
iface = netdev_priv ( dev ) ;
iface - > dev = dev ;
iface - > local = local ;
iface - > type = type ;
list_add ( & iface - > list , & local - > hostap_interfaces ) ;
mdev = local - > dev ;
memcpy ( dev - > dev_addr , mdev - > dev_addr , ETH_ALEN ) ;
dev - > base_addr = mdev - > base_addr ;
dev - > irq = mdev - > irq ;
dev - > mem_start = mdev - > mem_start ;
dev - > mem_end = mdev - > mem_end ;
2007-09-26 21:45:24 +01:00
hostap_setup_dev ( dev , local , type ) ;
2005-05-12 22:54:16 -04:00
dev - > destructor = free_netdev ;
sprintf ( dev - > name , " %s%s " , prefix , name ) ;
if ( ! rtnl_locked )
rtnl_lock ( ) ;
ret = 0 ;
if ( strchr ( dev - > name , ' % ' ) )
ret = dev_alloc_name ( dev , dev - > name ) ;
2002-04-09 12:14:34 -07:00
SET_NETDEV_DEV ( dev , mdev - > dev . parent ) ;
2005-05-12 22:54:16 -04:00
if ( ret > = 0 )
ret = register_netdevice ( dev ) ;
if ( ! rtnl_locked )
rtnl_unlock ( ) ;
if ( ret < 0 ) {
printk ( KERN_WARNING " %s: failed to add new netdevice! \n " ,
dev - > name ) ;
free_netdev ( dev ) ;
return NULL ;
}
printk ( KERN_DEBUG " %s: registered netdevice %s \n " ,
mdev - > name , dev - > name ) ;
return dev ;
}
void hostap_remove_interface ( struct net_device * dev , int rtnl_locked ,
int remove_from_list )
{
struct hostap_interface * iface ;
if ( ! dev )
return ;
iface = netdev_priv ( dev ) ;
if ( remove_from_list ) {
list_del ( & iface - > list ) ;
}
if ( dev = = iface - > local - > ddev )
iface - > local - > ddev = NULL ;
else if ( dev = = iface - > local - > apdev )
iface - > local - > apdev = NULL ;
else if ( dev = = iface - > local - > stadev )
iface - > local - > stadev = NULL ;
if ( rtnl_locked )
unregister_netdevice ( dev ) ;
else
unregister_netdev ( dev ) ;
/* dev->destructor = free_netdev() will free the device data, including
* private data , when removing the device */
}
static inline int prism2_wds_special_addr ( u8 * addr )
{
if ( addr [ 0 ] | | addr [ 1 ] | | addr [ 2 ] | | addr [ 3 ] | | addr [ 4 ] | | addr [ 5 ] )
return 0 ;
return 1 ;
}
2006-01-14 03:09:34 +01:00
int prism2_wds_add ( local_info_t * local , u8 * remote_addr ,
int rtnl_locked )
2005-05-12 22:54:16 -04:00
{
struct net_device * dev ;
struct list_head * ptr ;
struct hostap_interface * iface , * empty , * match ;
empty = match = NULL ;
read_lock_bh ( & local - > iface_lock ) ;
list_for_each ( ptr , & local - > hostap_interfaces ) {
iface = list_entry ( ptr , struct hostap_interface , list ) ;
if ( iface - > type ! = HOSTAP_INTERFACE_WDS )
continue ;
if ( prism2_wds_special_addr ( iface - > u . wds . remote_addr ) )
empty = iface ;
else if ( memcmp ( iface - > u . wds . remote_addr , remote_addr ,
ETH_ALEN ) = = 0 ) {
match = iface ;
break ;
}
}
if ( ! match & & empty & & ! prism2_wds_special_addr ( remote_addr ) ) {
/* take pre-allocated entry into use */
memcpy ( empty - > u . wds . remote_addr , remote_addr , ETH_ALEN ) ;
read_unlock_bh ( & local - > iface_lock ) ;
printk ( KERN_DEBUG " %s: using pre-allocated WDS netdevice %s \n " ,
local - > dev - > name , empty - > dev - > name ) ;
return 0 ;
}
read_unlock_bh ( & local - > iface_lock ) ;
if ( ! prism2_wds_special_addr ( remote_addr ) ) {
if ( match )
return - EEXIST ;
hostap_add_sta ( local - > ap , remote_addr ) ;
}
if ( local - > wds_connections > = local - > wds_max_connections )
return - ENOBUFS ;
/* verify that there is room for wds# postfix in the interface name */
if ( strlen ( local - > dev - > name ) > IFNAMSIZ - 5 ) {
printk ( KERN_DEBUG " '%s' too long base device name \n " ,
local - > dev - > name ) ;
return - EINVAL ;
}
dev = hostap_add_interface ( local , HOSTAP_INTERFACE_WDS , rtnl_locked ,
local - > ddev - > name , " wds%d " ) ;
if ( dev = = NULL )
return - ENOMEM ;
iface = netdev_priv ( dev ) ;
memcpy ( iface - > u . wds . remote_addr , remote_addr , ETH_ALEN ) ;
local - > wds_connections + + ;
return 0 ;
}
2006-01-14 03:09:34 +01:00
int prism2_wds_del ( local_info_t * local , u8 * remote_addr ,
int rtnl_locked , int do_not_remove )
2005-05-12 22:54:16 -04:00
{
unsigned long flags ;
struct list_head * ptr ;
struct hostap_interface * iface , * selected = NULL ;
write_lock_irqsave ( & local - > iface_lock , flags ) ;
list_for_each ( ptr , & local - > hostap_interfaces ) {
iface = list_entry ( ptr , struct hostap_interface , list ) ;
if ( iface - > type ! = HOSTAP_INTERFACE_WDS )
continue ;
if ( memcmp ( iface - > u . wds . remote_addr , remote_addr ,
ETH_ALEN ) = = 0 ) {
selected = iface ;
break ;
}
}
if ( selected & & ! do_not_remove )
list_del ( & selected - > list ) ;
write_unlock_irqrestore ( & local - > iface_lock , flags ) ;
if ( selected ) {
if ( do_not_remove )
memset ( selected - > u . wds . remote_addr , 0 , ETH_ALEN ) ;
else {
hostap_remove_interface ( selected - > dev , rtnl_locked , 0 ) ;
local - > wds_connections - - ;
}
}
return selected ? 0 : - ENODEV ;
}
u16 hostap_tx_callback_register ( local_info_t * local ,
void ( * func ) ( struct sk_buff * , int ok , void * ) ,
void * data )
{
unsigned long flags ;
struct hostap_tx_callback_info * entry ;
2006-12-13 00:35:56 -08:00
entry = kmalloc ( sizeof ( * entry ) ,
2005-05-12 22:54:16 -04:00
GFP_ATOMIC ) ;
if ( entry = = NULL )
return 0 ;
entry - > func = func ;
entry - > data = data ;
spin_lock_irqsave ( & local - > lock , flags ) ;
entry - > idx = local - > tx_callback ? local - > tx_callback - > idx + 1 : 1 ;
entry - > next = local - > tx_callback ;
local - > tx_callback = entry ;
spin_unlock_irqrestore ( & local - > lock , flags ) ;
return entry - > idx ;
}
int hostap_tx_callback_unregister ( local_info_t * local , u16 idx )
{
unsigned long flags ;
struct hostap_tx_callback_info * cb , * prev = NULL ;
spin_lock_irqsave ( & local - > lock , flags ) ;
cb = local - > tx_callback ;
while ( cb ! = NULL & & cb - > idx ! = idx ) {
prev = cb ;
cb = cb - > next ;
}
if ( cb ) {
if ( prev = = NULL )
local - > tx_callback = cb - > next ;
else
prev - > next = cb - > next ;
kfree ( cb ) ;
}
spin_unlock_irqrestore ( & local - > lock , flags ) ;
return cb ? 0 : - 1 ;
}
/* val is in host byte order */
int hostap_set_word ( struct net_device * dev , int rid , u16 val )
{
struct hostap_interface * iface ;
2007-12-21 03:30:16 -05:00
__le16 tmp = cpu_to_le16 ( val ) ;
2005-05-12 22:54:16 -04:00
iface = netdev_priv ( dev ) ;
return iface - > local - > func - > set_rid ( dev , rid , & tmp , 2 ) ;
}
int hostap_set_string ( struct net_device * dev , int rid , const char * val )
{
struct hostap_interface * iface ;
char buf [ MAX_SSID_LEN + 2 ] ;
int len ;
iface = netdev_priv ( dev ) ;
len = strlen ( val ) ;
if ( len > MAX_SSID_LEN )
return - 1 ;
memset ( buf , 0 , sizeof ( buf ) ) ;
buf [ 0 ] = len ; /* little endian 16 bit word */
memcpy ( buf + 2 , val , len ) ;
return iface - > local - > func - > set_rid ( dev , rid , & buf , MAX_SSID_LEN + 2 ) ;
}
u16 hostap_get_porttype ( local_info_t * local )
{
if ( local - > iw_mode = = IW_MODE_ADHOC & & local - > pseudo_adhoc )
return HFA384X_PORTTYPE_PSEUDO_IBSS ;
if ( local - > iw_mode = = IW_MODE_ADHOC )
return HFA384X_PORTTYPE_IBSS ;
if ( local - > iw_mode = = IW_MODE_INFRA )
return HFA384X_PORTTYPE_BSS ;
if ( local - > iw_mode = = IW_MODE_REPEAT )
return HFA384X_PORTTYPE_WDS ;
if ( local - > iw_mode = = IW_MODE_MONITOR )
return HFA384X_PORTTYPE_PSEUDO_IBSS ;
return HFA384X_PORTTYPE_HOSTAP ;
}
int hostap_set_encryption ( local_info_t * local )
{
u16 val , old_val ;
int i , keylen , len , idx ;
char keybuf [ WEP_KEY_LEN + 1 ] ;
enum { NONE , WEP , OTHER } encrypt_type ;
2008-10-29 11:35:05 -04:00
idx = local - > crypt_info . tx_keyidx ;
if ( local - > crypt_info . crypt [ idx ] = = NULL | |
local - > crypt_info . crypt [ idx ] - > ops = = NULL )
2005-05-12 22:54:16 -04:00
encrypt_type = NONE ;
2008-10-29 11:35:05 -04:00
else if ( strcmp ( local - > crypt_info . crypt [ idx ] - > ops - > name , " WEP " ) = = 0 )
2005-05-12 22:54:16 -04:00
encrypt_type = WEP ;
else
encrypt_type = OTHER ;
if ( local - > func - > get_rid ( local - > dev , HFA384X_RID_CNFWEPFLAGS , & val , 2 ,
1 ) < 0 ) {
printk ( KERN_DEBUG " Could not read current WEP flags. \n " ) ;
goto fail ;
}
le16_to_cpus ( & val ) ;
old_val = val ;
if ( encrypt_type ! = NONE | | local - > privacy_invoked )
val | = HFA384X_WEPFLAGS_PRIVACYINVOKED ;
else
val & = ~ HFA384X_WEPFLAGS_PRIVACYINVOKED ;
if ( local - > open_wep | | encrypt_type = = NONE | |
( ( local - > ieee_802_1x | | local - > wpa ) & & local - > host_decrypt ) )
val & = ~ HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED ;
else
val | = HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED ;
if ( ( encrypt_type ! = NONE | | local - > privacy_invoked ) & &
( encrypt_type = = OTHER | | local - > host_encrypt ) )
val | = HFA384X_WEPFLAGS_HOSTENCRYPT ;
else
val & = ~ HFA384X_WEPFLAGS_HOSTENCRYPT ;
if ( ( encrypt_type ! = NONE | | local - > privacy_invoked ) & &
( encrypt_type = = OTHER | | local - > host_decrypt ) )
val | = HFA384X_WEPFLAGS_HOSTDECRYPT ;
else
val & = ~ HFA384X_WEPFLAGS_HOSTDECRYPT ;
if ( val ! = old_val & &
hostap_set_word ( local - > dev , HFA384X_RID_CNFWEPFLAGS , val ) ) {
printk ( KERN_DEBUG " Could not write new WEP flags (0x%x) \n " ,
val ) ;
goto fail ;
}
if ( encrypt_type ! = WEP )
return 0 ;
/* 104-bit support seems to require that all the keys are set to the
* same keylen */
keylen = 6 ; /* first 5 octets */
2008-10-29 11:35:05 -04:00
len = local - > crypt_info . crypt [ idx ] - > ops - > get_key ( keybuf , sizeof ( keybuf ) , NULL ,
local - > crypt_info . crypt [ idx ] - > priv ) ;
2005-05-12 22:54:16 -04:00
if ( idx > = 0 & & idx < WEP_KEYS & & len > 5 )
keylen = WEP_KEY_LEN + 1 ; /* first 13 octets */
for ( i = 0 ; i < WEP_KEYS ; i + + ) {
memset ( keybuf , 0 , sizeof ( keybuf ) ) ;
2008-10-29 11:35:05 -04:00
if ( local - > crypt_info . crypt [ i ] ) {
( void ) local - > crypt_info . crypt [ i ] - > ops - > get_key (
2005-05-12 22:54:16 -04:00
keybuf , sizeof ( keybuf ) ,
2008-10-29 11:35:05 -04:00
NULL , local - > crypt_info . crypt [ i ] - > priv ) ;
2005-05-12 22:54:16 -04:00
}
if ( local - > func - > set_rid ( local - > dev ,
HFA384X_RID_CNFDEFAULTKEY0 + i ,
keybuf , keylen ) ) {
printk ( KERN_DEBUG " Could not set key %d (len=%d) \n " ,
i , keylen ) ;
goto fail ;
}
}
if ( hostap_set_word ( local - > dev , HFA384X_RID_CNFWEPDEFAULTKEYID , idx ) ) {
printk ( KERN_DEBUG " Could not set default keyid %d \n " , idx ) ;
goto fail ;
}
return 0 ;
fail :
printk ( KERN_DEBUG " %s: encryption setup failed \n " , local - > dev - > name ) ;
return - 1 ;
}
int hostap_set_antsel ( local_info_t * local )
{
u16 val ;
int ret = 0 ;
if ( local - > antsel_tx ! = HOSTAP_ANTSEL_DO_NOT_TOUCH & &
local - > func - > cmd ( local - > dev , HFA384X_CMDCODE_READMIF ,
HFA386X_CR_TX_CONFIGURE ,
NULL , & val ) = = 0 ) {
val & = ~ ( BIT ( 2 ) | BIT ( 1 ) ) ;
switch ( local - > antsel_tx ) {
case HOSTAP_ANTSEL_DIVERSITY :
val | = BIT ( 1 ) ;
break ;
case HOSTAP_ANTSEL_LOW :
break ;
case HOSTAP_ANTSEL_HIGH :
val | = BIT ( 2 ) ;
break ;
}
if ( local - > func - > cmd ( local - > dev , HFA384X_CMDCODE_WRITEMIF ,
HFA386X_CR_TX_CONFIGURE , & val , NULL ) ) {
printk ( KERN_INFO " %s: setting TX AntSel failed \n " ,
local - > dev - > name ) ;
ret = - 1 ;
}
}
if ( local - > antsel_rx ! = HOSTAP_ANTSEL_DO_NOT_TOUCH & &
local - > func - > cmd ( local - > dev , HFA384X_CMDCODE_READMIF ,
HFA386X_CR_RX_CONFIGURE ,
NULL , & val ) = = 0 ) {
val & = ~ ( BIT ( 1 ) | BIT ( 0 ) ) ;
switch ( local - > antsel_rx ) {
case HOSTAP_ANTSEL_DIVERSITY :
break ;
case HOSTAP_ANTSEL_LOW :
val | = BIT ( 0 ) ;
break ;
case HOSTAP_ANTSEL_HIGH :
val | = BIT ( 0 ) | BIT ( 1 ) ;
break ;
}
if ( local - > func - > cmd ( local - > dev , HFA384X_CMDCODE_WRITEMIF ,
HFA386X_CR_RX_CONFIGURE , & val , NULL ) ) {
printk ( KERN_INFO " %s: setting RX AntSel failed \n " ,
local - > dev - > name ) ;
ret = - 1 ;
}
}
return ret ;
}
int hostap_set_roaming ( local_info_t * local )
{
u16 val ;
switch ( local - > host_roaming ) {
case 1 :
val = HFA384X_ROAMING_HOST ;
break ;
case 2 :
val = HFA384X_ROAMING_DISABLED ;
break ;
case 0 :
default :
val = HFA384X_ROAMING_FIRMWARE ;
break ;
}
return hostap_set_word ( local - > dev , HFA384X_RID_CNFROAMINGMODE , val ) ;
}
int hostap_set_auth_algs ( local_info_t * local )
{
int val = local - > auth_algs ;
/* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication
* set to include both Open and Shared Key flags . It tries to use
* Shared Key authentication in that case even if WEP keys are not
* configured . . STA f / w v0 .7 .6 is able to handle such configuration ,
* but it is unknown when this was fixed between 0.6 .2 . . 0.7 .6 . */
if ( local - > sta_fw_ver < PRISM2_FW_VER ( 0 , 7 , 0 ) & &
val ! = PRISM2_AUTH_OPEN & & val ! = PRISM2_AUTH_SHARED_KEY )
val = PRISM2_AUTH_OPEN ;
if ( hostap_set_word ( local - > dev , HFA384X_RID_CNFAUTHENTICATION , val ) ) {
printk ( KERN_INFO " %s: cnfAuthentication setting to 0x%x "
" failed \n " , local - > dev - > name , local - > auth_algs ) ;
return - EINVAL ;
}
return 0 ;
}
void hostap_dump_rx_header ( const char * name , const struct hfa384x_rx_frame * rx )
{
u16 status , fc ;
status = __le16_to_cpu ( rx - > status ) ;
printk ( KERN_DEBUG " %s: RX status=0x%04x (port=%d, type=%d, "
" fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; "
" jiffies=%ld \n " ,
name , status , ( status > > 8 ) & 0x07 , status > > 13 , status & 1 ,
rx - > silence , rx - > signal , rx - > rate , rx - > rxflow , jiffies ) ;
fc = __le16_to_cpu ( rx - > frame_control ) ;
printk ( KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x "
" data_len=%d%s%s \n " ,
2009-02-11 17:17:10 -05:00
fc , ( fc & IEEE80211_FCTL_FTYPE ) > > 2 ,
( fc & IEEE80211_FCTL_STYPE ) > > 4 ,
2005-05-12 22:54:16 -04:00
__le16_to_cpu ( rx - > duration_id ) , __le16_to_cpu ( rx - > seq_ctrl ) ,
__le16_to_cpu ( rx - > data_len ) ,
2005-08-14 21:00:01 -07:00
fc & IEEE80211_FCTL_TODS ? " [ToDS] " : " " ,
fc & IEEE80211_FCTL_FROMDS ? " [FromDS] " : " " ) ;
2005-05-12 22:54:16 -04:00
2008-10-27 15:59:26 -07:00
printk ( KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM \n " ,
rx - > addr1 , rx - > addr2 , rx - > addr3 , rx - > addr4 ) ;
2005-05-12 22:54:16 -04:00
2008-10-27 15:59:26 -07:00
printk ( KERN_DEBUG " dst=%pM src=%pM len=%d \n " ,
rx - > dst_addr , rx - > src_addr ,
2005-05-12 22:54:16 -04:00
__be16_to_cpu ( rx - > len ) ) ;
}
void hostap_dump_tx_header ( const char * name , const struct hfa384x_tx_frame * tx )
{
u16 fc ;
printk ( KERN_DEBUG " %s: TX status=0x%04x retry_count=%d tx_rate=%d "
" tx_control=0x%04x; jiffies=%ld \n " ,
name , __le16_to_cpu ( tx - > status ) , tx - > retry_count , tx - > tx_rate ,
__le16_to_cpu ( tx - > tx_control ) , jiffies ) ;
fc = __le16_to_cpu ( tx - > frame_control ) ;
printk ( KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x "
" data_len=%d%s%s \n " ,
2009-02-11 17:17:10 -05:00
fc , ( fc & IEEE80211_FCTL_FTYPE ) > > 2 ,
( fc & IEEE80211_FCTL_STYPE ) > > 4 ,
2005-05-12 22:54:16 -04:00
__le16_to_cpu ( tx - > duration_id ) , __le16_to_cpu ( tx - > seq_ctrl ) ,
__le16_to_cpu ( tx - > data_len ) ,
2005-08-14 21:00:01 -07:00
fc & IEEE80211_FCTL_TODS ? " [ToDS] " : " " ,
fc & IEEE80211_FCTL_FROMDS ? " [FromDS] " : " " ) ;
2005-05-12 22:54:16 -04:00
2008-10-27 15:59:26 -07:00
printk ( KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM \n " ,
tx - > addr1 , tx - > addr2 , tx - > addr3 , tx - > addr4 ) ;
2005-05-12 22:54:16 -04:00
2008-10-27 15:59:26 -07:00
printk ( KERN_DEBUG " dst=%pM src=%pM len=%d \n " ,
tx - > dst_addr , tx - > src_addr ,
2005-05-12 22:54:16 -04:00
__be16_to_cpu ( tx - > len ) ) ;
}
2008-06-27 16:19:58 -04:00
static int hostap_80211_header_parse ( const struct sk_buff * skb ,
unsigned char * haddr )
2005-05-12 22:54:16 -04:00
{
2008-06-27 16:20:04 -04:00
memcpy ( haddr , skb_mac_header ( skb ) + 10 , ETH_ALEN ) ; /* addr2 */
2005-05-12 22:54:16 -04:00
return ETH_ALEN ;
}
2009-02-11 17:17:10 -05:00
int hostap_80211_get_hdrlen ( __le16 fc )
2005-05-12 22:54:16 -04:00
{
2009-02-11 17:17:10 -05:00
if ( ieee80211_is_data ( fc ) & & ieee80211_has_a4 ( fc ) )
return 30 ; /* Addr4 */
else if ( ieee80211_is_cts ( fc ) | | ieee80211_is_ack ( fc ) )
return 10 ;
else if ( ieee80211_is_ctl ( fc ) )
return 16 ;
return 24 ;
2005-05-12 22:54:16 -04:00
}
static int prism2_close ( struct net_device * dev )
{
struct hostap_interface * iface ;
local_info_t * local ;
PDEBUG ( DEBUG_FLOW , " %s: prism2_close \n " , dev - > name ) ;
iface = netdev_priv ( dev ) ;
local = iface - > local ;
if ( dev = = local - > ddev ) {
prism2_sta_deauth ( local , WLAN_REASON_DEAUTH_LEAVING ) ;
}
# ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
if ( ! local - > hostapd & & dev = = local - > dev & &
( ! local - > func - > card_present | | local - > func - > card_present ( local ) ) & &
local - > hw_ready & & local - > ap & & local - > iw_mode = = IW_MODE_MASTER )
hostap_deauth_all_stas ( dev , local - > ap , 1 ) ;
# endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
if ( dev = = local - > dev ) {
local - > func - > hw_shutdown ( dev , HOSTAP_HW_ENABLE_CMDCOMPL ) ;
}
if ( netif_running ( dev ) ) {
netif_stop_queue ( dev ) ;
netif_device_detach ( dev ) ;
}
2008-06-12 02:22:02 -07:00
cancel_work_sync ( & local - > reset_queue ) ;
cancel_work_sync ( & local - > set_multicast_list_queue ) ;
cancel_work_sync ( & local - > set_tim_queue ) ;
# ifndef PRISM2_NO_STATION_MODES
cancel_work_sync ( & local - > info_queue ) ;
# endif
cancel_work_sync ( & local - > comms_qual_update ) ;
2005-05-12 22:54:16 -04:00
module_put ( local - > hw_module ) ;
local - > num_dev_open - - ;
if ( dev ! = local - > dev & & local - > dev - > flags & IFF_UP & &
local - > master_dev_auto_open & & local - > num_dev_open = = 1 ) {
/* Close master radio interface automatically if it was also
* opened automatically and we are now closing the last
* remaining non - master device . */
dev_close ( local - > dev ) ;
}
return 0 ;
}
static int prism2_open ( struct net_device * dev )
{
struct hostap_interface * iface ;
local_info_t * local ;
PDEBUG ( DEBUG_FLOW , " %s: prism2_open \n " , dev - > name ) ;
iface = netdev_priv ( dev ) ;
local = iface - > local ;
if ( local - > no_pri ) {
printk ( KERN_DEBUG " %s: could not set interface UP - no PRI "
" f/w \n " , dev - > name ) ;
return 1 ;
}
if ( ( local - > func - > card_present & & ! local - > func - > card_present ( local ) ) | |
local - > hw_downloading )
return - ENODEV ;
if ( ! try_module_get ( local - > hw_module ) )
return - ENODEV ;
local - > num_dev_open + + ;
if ( ! local - > dev_enabled & & local - > func - > hw_enable ( dev , 1 ) ) {
printk ( KERN_WARNING " %s: could not enable MAC port \n " ,
dev - > name ) ;
prism2_close ( dev ) ;
return 1 ;
}
if ( ! local - > dev_enabled )
prism2_callback ( local , PRISM2_CALLBACK_ENABLE ) ;
local - > dev_enabled = 1 ;
if ( dev ! = local - > dev & & ! ( local - > dev - > flags & IFF_UP ) ) {
/* Master radio interface is needed for all operation, so open
* it automatically when any virtual net_device is opened . */
local - > master_dev_auto_open = 1 ;
dev_open ( local - > dev ) ;
}
netif_device_attach ( dev ) ;
netif_start_queue ( dev ) ;
return 0 ;
}
static int prism2_set_mac_address ( struct net_device * dev , void * p )
{
struct hostap_interface * iface ;
local_info_t * local ;
struct list_head * ptr ;
struct sockaddr * addr = p ;
iface = netdev_priv ( dev ) ;
local = iface - > local ;
if ( local - > func - > set_rid ( dev , HFA384X_RID_CNFOWNMACADDR , addr - > sa_data ,
ETH_ALEN ) < 0 | | local - > func - > reset_port ( dev ) )
return - EINVAL ;
read_lock_bh ( & local - > iface_lock ) ;
list_for_each ( ptr , & local - > hostap_interfaces ) {
iface = list_entry ( ptr , struct hostap_interface , list ) ;
memcpy ( iface - > dev - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
}
memcpy ( local - > dev - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
read_unlock_bh ( & local - > iface_lock ) ;
return 0 ;
}
/* TODO: to be further implemented as soon as Prism2 fully supports
* GroupAddresses and correct documentation is available */
2006-11-22 14:57:56 +00:00
void hostap_set_multicast_list_queue ( struct work_struct * work )
2005-05-12 22:54:16 -04:00
{
2006-11-22 14:57:56 +00:00
local_info_t * local =
container_of ( work , local_info_t , set_multicast_list_queue ) ;
struct net_device * dev = local - > dev ;
2005-05-12 22:54:16 -04:00
struct hostap_interface * iface ;
iface = netdev_priv ( dev ) ;
if ( hostap_set_word ( dev , HFA384X_RID_PROMISCUOUSMODE ,
local - > is_promisc ) ) {
printk ( KERN_INFO " %s: %sabling promiscuous mode failed \n " ,
dev - > name , local - > is_promisc ? " en " : " dis " ) ;
}
}
static void hostap_set_multicast_list ( struct net_device * dev )
{
#if 0
/* FIX: promiscuous mode seems to be causing a lot of problems with
* some station firmware versions ( FCSErr frames , invalid MACPort , etc .
* corrupted incoming frames ) . This code is now commented out while the
* problems are investigated . */
struct hostap_interface * iface ;
local_info_t * local ;
iface = netdev_priv ( dev ) ;
local = iface - > local ;
if ( ( dev - > flags & IFF_ALLMULTI ) | | ( dev - > flags & IFF_PROMISC ) ) {
local - > is_promisc = 1 ;
} else {
local - > is_promisc = 0 ;
}
schedule_work ( & local - > set_multicast_list_queue ) ;
# endif
}
static int prism2_change_mtu ( struct net_device * dev , int new_mtu )
{
if ( new_mtu < PRISM2_MIN_MTU | | new_mtu > PRISM2_MAX_MTU )
return - EINVAL ;
dev - > mtu = new_mtu ;
return 0 ;
}
static void prism2_tx_timeout ( struct net_device * dev )
{
struct hostap_interface * iface ;
local_info_t * local ;
struct hfa384x_regs regs ;
iface = netdev_priv ( dev ) ;
local = iface - > local ;
printk ( KERN_WARNING " %s Tx timed out! Resetting card \n " , dev - > name ) ;
netif_stop_queue ( local - > dev ) ;
local - > func - > read_regs ( dev , & regs ) ;
printk ( KERN_DEBUG " %s: CMD=%04x EVSTAT=%04x "
" OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x \n " ,
dev - > name , regs . cmd , regs . evstat , regs . offset0 , regs . offset1 ,
regs . swsupport0 ) ;
local - > func - > schedule_reset ( local ) ;
}
2007-10-09 01:40:57 -07:00
const struct header_ops hostap_80211_ops = {
. create = eth_header ,
. rebuild = eth_rebuild_header ,
. cache = eth_header_cache ,
. cache_update = eth_header_cache_update ,
. parse = hostap_80211_header_parse ,
} ;
EXPORT_SYMBOL ( hostap_80211_ops ) ;
2005-05-12 22:54:16 -04:00
2009-03-20 19:36:43 +00:00
static const struct net_device_ops hostap_netdev_ops = {
. ndo_start_xmit = hostap_data_start_xmit ,
. ndo_open = prism2_open ,
. ndo_stop = prism2_close ,
. ndo_do_ioctl = hostap_ioctl ,
. ndo_set_mac_address = prism2_set_mac_address ,
. ndo_set_multicast_list = hostap_set_multicast_list ,
. ndo_change_mtu = prism2_change_mtu ,
. ndo_tx_timeout = prism2_tx_timeout ,
. ndo_validate_addr = eth_validate_addr ,
} ;
static const struct net_device_ops hostap_mgmt_netdev_ops = {
. ndo_start_xmit = hostap_mgmt_start_xmit ,
. ndo_open = prism2_open ,
. ndo_stop = prism2_close ,
. ndo_do_ioctl = hostap_ioctl ,
. ndo_set_mac_address = prism2_set_mac_address ,
. ndo_set_multicast_list = hostap_set_multicast_list ,
. ndo_change_mtu = prism2_change_mtu ,
. ndo_tx_timeout = prism2_tx_timeout ,
. ndo_validate_addr = eth_validate_addr ,
} ;
static const struct net_device_ops hostap_master_ops = {
. ndo_start_xmit = hostap_master_start_xmit ,
. ndo_open = prism2_open ,
. ndo_stop = prism2_close ,
. ndo_do_ioctl = hostap_ioctl ,
. ndo_set_mac_address = prism2_set_mac_address ,
. ndo_set_multicast_list = hostap_set_multicast_list ,
. ndo_change_mtu = prism2_change_mtu ,
. ndo_tx_timeout = prism2_tx_timeout ,
. ndo_validate_addr = eth_validate_addr ,
} ;
2005-05-12 22:54:16 -04:00
void hostap_setup_dev ( struct net_device * dev , local_info_t * local ,
2007-09-26 21:45:24 +01:00
int type )
2005-05-12 22:54:16 -04:00
{
struct hostap_interface * iface ;
iface = netdev_priv ( dev ) ;
ether_setup ( dev ) ;
/* kernel callbacks */
if ( iface ) {
/* Currently, we point to the proper spy_data only on
* the main_dev . This could be fixed . Jean II */
iface - > wireless_data . spy_data = & iface - > spy_data ;
dev - > wireless_data = & iface - > wireless_data ;
}
2009-03-20 19:36:43 +00:00
dev - > wireless_handlers = & hostap_iw_handler_def ;
2005-05-12 22:54:16 -04:00
dev - > watchdog_timeo = TX_TIMEOUT ;
2009-03-20 19:36:43 +00:00
switch ( type ) {
case HOSTAP_INTERFACE_AP :
2009-09-10 03:44:47 +02:00
dev - > tx_queue_len = 0 ; /* use main radio device queue */
2009-03-20 19:36:43 +00:00
dev - > netdev_ops = & hostap_mgmt_netdev_ops ;
2007-09-26 21:45:24 +01:00
dev - > type = ARPHRD_IEEE80211 ;
dev - > header_ops = & hostap_80211_ops ;
2009-03-20 19:36:43 +00:00
break ;
case HOSTAP_INTERFACE_MASTER :
dev - > netdev_ops = & hostap_master_ops ;
break ;
default :
2009-09-10 03:44:47 +02:00
dev - > tx_queue_len = 0 ; /* use main radio device queue */
2009-03-20 19:36:43 +00:00
dev - > netdev_ops = & hostap_netdev_ops ;
2007-09-26 21:45:24 +01:00
}
2005-05-12 22:54:16 -04:00
dev - > mtu = local - > mtu ;
2009-03-20 19:36:43 +00:00
2005-05-12 22:54:16 -04:00
SET_ETHTOOL_OPS ( dev , & prism2_ethtool_ops ) ;
netif_stop_queue ( dev ) ;
}
static int hostap_enable_hostapd ( local_info_t * local , int rtnl_locked )
{
struct net_device * dev = local - > dev ;
if ( local - > apdev )
return - EEXIST ;
printk ( KERN_DEBUG " %s: enabling hostapd mode \n " , dev - > name ) ;
local - > apdev = hostap_add_interface ( local , HOSTAP_INTERFACE_AP ,
rtnl_locked , local - > ddev - > name ,
" ap " ) ;
if ( local - > apdev = = NULL )
return - ENOMEM ;
return 0 ;
}
static int hostap_disable_hostapd ( local_info_t * local , int rtnl_locked )
{
struct net_device * dev = local - > dev ;
printk ( KERN_DEBUG " %s: disabling hostapd mode \n " , dev - > name ) ;
hostap_remove_interface ( local - > apdev , rtnl_locked , 1 ) ;
local - > apdev = NULL ;
return 0 ;
}
static int hostap_enable_hostapd_sta ( local_info_t * local , int rtnl_locked )
{
struct net_device * dev = local - > dev ;
if ( local - > stadev )
return - EEXIST ;
printk ( KERN_DEBUG " %s: enabling hostapd STA mode \n " , dev - > name ) ;
local - > stadev = hostap_add_interface ( local , HOSTAP_INTERFACE_STA ,
rtnl_locked , local - > ddev - > name ,
" sta " ) ;
if ( local - > stadev = = NULL )
return - ENOMEM ;
return 0 ;
}
static int hostap_disable_hostapd_sta ( local_info_t * local , int rtnl_locked )
{
struct net_device * dev = local - > dev ;
printk ( KERN_DEBUG " %s: disabling hostapd mode \n " , dev - > name ) ;
hostap_remove_interface ( local - > stadev , rtnl_locked , 1 ) ;
local - > stadev = NULL ;
return 0 ;
}
int hostap_set_hostapd ( local_info_t * local , int val , int rtnl_locked )
{
int ret ;
if ( val < 0 | | val > 1 )
return - EINVAL ;
if ( local - > hostapd = = val )
return 0 ;
if ( val ) {
ret = hostap_enable_hostapd ( local , rtnl_locked ) ;
if ( ret = = 0 )
local - > hostapd = 1 ;
} else {
local - > hostapd = 0 ;
ret = hostap_disable_hostapd ( local , rtnl_locked ) ;
if ( ret ! = 0 )
local - > hostapd = 1 ;
}
return ret ;
}
int hostap_set_hostapd_sta ( local_info_t * local , int val , int rtnl_locked )
{
int ret ;
if ( val < 0 | | val > 1 )
return - EINVAL ;
if ( local - > hostapd_sta = = val )
return 0 ;
if ( val ) {
ret = hostap_enable_hostapd_sta ( local , rtnl_locked ) ;
if ( ret = = 0 )
local - > hostapd_sta = 1 ;
} else {
local - > hostapd_sta = 0 ;
ret = hostap_disable_hostapd_sta ( local , rtnl_locked ) ;
if ( ret ! = 0 )
local - > hostapd_sta = 1 ;
}
return ret ;
}
int prism2_update_comms_qual ( struct net_device * dev )
{
struct hostap_interface * iface ;
local_info_t * local ;
int ret = 0 ;
struct hfa384x_comms_quality sq ;
iface = netdev_priv ( dev ) ;
local = iface - > local ;
if ( ! local - > sta_fw_ver )
ret = - 1 ;
else if ( local - > sta_fw_ver > = PRISM2_FW_VER ( 1 , 3 , 1 ) ) {
if ( local - > func - > get_rid ( local - > dev ,
HFA384X_RID_DBMCOMMSQUALITY ,
& sq , sizeof ( sq ) , 1 ) > = 0 ) {
local - > comms_qual = ( s16 ) le16_to_cpu ( sq . comm_qual ) ;
local - > avg_signal = ( s16 ) le16_to_cpu ( sq . signal_level ) ;
local - > avg_noise = ( s16 ) le16_to_cpu ( sq . noise_level ) ;
local - > last_comms_qual_update = jiffies ;
} else
ret = - 1 ;
} else {
if ( local - > func - > get_rid ( local - > dev , HFA384X_RID_COMMSQUALITY ,
& sq , sizeof ( sq ) , 1 ) > = 0 ) {
local - > comms_qual = le16_to_cpu ( sq . comm_qual ) ;
local - > avg_signal = HFA384X_LEVEL_TO_dBm (
le16_to_cpu ( sq . signal_level ) ) ;
local - > avg_noise = HFA384X_LEVEL_TO_dBm (
le16_to_cpu ( sq . noise_level ) ) ;
local - > last_comms_qual_update = jiffies ;
} else
ret = - 1 ;
}
return ret ;
}
2005-08-14 19:08:44 -07:00
int prism2_sta_send_mgmt ( local_info_t * local , u8 * dst , u16 stype ,
2005-05-12 22:54:16 -04:00
u8 * body , size_t bodylen )
{
struct sk_buff * skb ;
struct hostap_ieee80211_mgmt * mgmt ;
struct hostap_skb_tx_data * meta ;
struct net_device * dev = local - > dev ;
skb = dev_alloc_skb ( IEEE80211_MGMT_HDR_LEN + bodylen ) ;
if ( skb = = NULL )
return - ENOMEM ;
mgmt = ( struct hostap_ieee80211_mgmt * )
skb_put ( skb , IEEE80211_MGMT_HDR_LEN ) ;
memset ( mgmt , 0 , IEEE80211_MGMT_HDR_LEN ) ;
2005-08-14 19:08:44 -07:00
mgmt - > frame_control = cpu_to_le16 ( IEEE80211_FTYPE_MGMT | stype ) ;
2005-05-12 22:54:16 -04:00
memcpy ( mgmt - > da , dst , ETH_ALEN ) ;
memcpy ( mgmt - > sa , dev - > dev_addr , ETH_ALEN ) ;
memcpy ( mgmt - > bssid , dst , ETH_ALEN ) ;
if ( body )
memcpy ( skb_put ( skb , bodylen ) , body , bodylen ) ;
meta = ( struct hostap_skb_tx_data * ) skb - > cb ;
memset ( meta , 0 , sizeof ( * meta ) ) ;
meta - > magic = HOSTAP_SKB_TX_DATA_MAGIC ;
meta - > iface = netdev_priv ( dev ) ;
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 ) ;
return 0 ;
}
int prism2_sta_deauth ( local_info_t * local , u16 reason )
{
union iwreq_data wrqu ;
int ret ;
2007-12-21 03:30:16 -05:00
__le16 val = cpu_to_le16 ( reason ) ;
2005-05-12 22:54:16 -04:00
if ( local - > iw_mode ! = IW_MODE_INFRA | |
memcmp ( local - > bssid , " \x00 \x00 \x00 \x00 \x00 \x00 " , ETH_ALEN ) = = 0 | |
memcmp ( local - > bssid , " \x44 \x44 \x44 \x44 \x44 \x44 " , ETH_ALEN ) = = 0 )
return 0 ;
2005-08-14 19:08:44 -07:00
ret = prism2_sta_send_mgmt ( local , local - > bssid , IEEE80211_STYPE_DEAUTH ,
2007-12-21 03:30:16 -05:00
( u8 * ) & val , 2 ) ;
2005-05-12 22:54:16 -04:00
memset ( wrqu . ap_addr . sa_data , 0 , ETH_ALEN ) ;
wireless_send_event ( local - > dev , SIOCGIWAP , & wrqu , NULL ) ;
return ret ;
}
struct proc_dir_entry * hostap_proc ;
static int __init hostap_init ( void )
{
2007-09-12 12:01:34 +02:00
if ( init_net . proc_net ! = NULL ) {
hostap_proc = proc_mkdir ( " hostap " , init_net . proc_net ) ;
2005-05-12 22:54:16 -04:00
if ( ! hostap_proc )
printk ( KERN_WARNING " Failed to mkdir "
" /proc/net/hostap \n " ) ;
} else
hostap_proc = NULL ;
return 0 ;
}
static void __exit hostap_exit ( void )
{
if ( hostap_proc ! = NULL ) {
hostap_proc = NULL ;
2007-09-12 12:01:34 +02:00
remove_proc_entry ( " hostap " , init_net . proc_net ) ;
2005-05-12 22:54:16 -04:00
}
}
EXPORT_SYMBOL ( hostap_set_word ) ;
EXPORT_SYMBOL ( hostap_set_string ) ;
EXPORT_SYMBOL ( hostap_get_porttype ) ;
EXPORT_SYMBOL ( hostap_set_encryption ) ;
EXPORT_SYMBOL ( hostap_set_antsel ) ;
EXPORT_SYMBOL ( hostap_set_roaming ) ;
EXPORT_SYMBOL ( hostap_set_auth_algs ) ;
EXPORT_SYMBOL ( hostap_dump_rx_header ) ;
EXPORT_SYMBOL ( hostap_dump_tx_header ) ;
EXPORT_SYMBOL ( hostap_80211_get_hdrlen ) ;
EXPORT_SYMBOL ( hostap_setup_dev ) ;
EXPORT_SYMBOL ( hostap_set_multicast_list_queue ) ;
EXPORT_SYMBOL ( hostap_set_hostapd ) ;
EXPORT_SYMBOL ( hostap_set_hostapd_sta ) ;
EXPORT_SYMBOL ( hostap_add_interface ) ;
EXPORT_SYMBOL ( hostap_remove_interface ) ;
EXPORT_SYMBOL ( prism2_update_comms_qual ) ;
module_init ( hostap_init ) ;
module_exit ( hostap_exit ) ;