2008-09-25 02:13:14 +04:00
/*
* lib80211 - - common bits for IEEE802 .11 drivers
*
* Copyright ( c ) 2008 John W . Linville < linville @ tuxdriver . com >
*
2008-10-29 18:35:05 +03:00
* Portions copied from old ieee80211 component , w / original copyright
* notices below :
*
* Host AP crypto routines
*
* Copyright ( c ) 2002 - 2003 , Jouni Malinen < j @ w1 . fi >
* Portions Copyright ( C ) 2004 , Intel Corporation < jketreno @ linux . intel . com >
*
2008-09-25 02:13:14 +04:00
*/
# include <linux/module.h>
2008-10-01 01:50:31 +04:00
# include <linux/ctype.h>
2008-09-25 02:13:14 +04:00
# include <linux/ieee80211.h>
2008-10-29 18:35:05 +03:00
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/string.h>
2008-09-25 02:13:14 +04:00
# include <net/lib80211.h>
# define DRV_NAME "lib80211"
# define DRV_DESCRIPTION "common routines for IEEE802.11 drivers"
MODULE_DESCRIPTION ( DRV_DESCRIPTION ) ;
MODULE_AUTHOR ( " John W. Linville <linville@tuxdriver.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-10-29 18:35:05 +03:00
struct lib80211_crypto_alg {
struct list_head list ;
struct lib80211_crypto_ops * ops ;
} ;
static LIST_HEAD ( lib80211_crypto_algs ) ;
static DEFINE_SPINLOCK ( lib80211_crypto_lock ) ;
2008-10-01 04:59:05 +04:00
const char * print_ssid ( char * buf , const char * ssid , u8 ssid_len )
2008-09-25 02:13:14 +04:00
{
const char * s = ssid ;
2008-10-01 04:59:05 +04:00
char * d = buf ;
2008-09-25 02:13:14 +04:00
ssid_len = min_t ( u8 , ssid_len , IEEE80211_MAX_SSID_LEN ) ;
while ( ssid_len - - ) {
2008-10-01 01:50:31 +04:00
if ( isprint ( * s ) ) {
2008-09-25 02:13:14 +04:00
* d + + = * s + + ;
2008-10-01 01:50:31 +04:00
continue ;
2008-09-25 02:13:14 +04:00
}
2008-10-01 01:50:31 +04:00
* d + + = ' \\ ' ;
if ( * s = = ' \0 ' )
* d + + = ' 0 ' ;
else if ( * s = = ' \n ' )
* d + + = ' n ' ;
else if ( * s = = ' \r ' )
* d + + = ' r ' ;
else if ( * s = = ' \t ' )
* d + + = ' t ' ;
else if ( * s = = ' \\ ' )
* d + + = ' \\ ' ;
else
d + = snprintf ( d , 3 , " %03o " , * s ) ;
s + + ;
2008-09-25 02:13:14 +04:00
}
* d = ' \0 ' ;
2008-10-01 04:59:05 +04:00
return buf ;
2008-09-25 02:13:14 +04:00
}
2008-10-01 04:59:05 +04:00
EXPORT_SYMBOL ( print_ssid ) ;
2008-09-25 02:13:14 +04:00
2008-10-29 18:35:05 +03:00
void lib80211_crypt_deinit_entries ( struct lib80211_crypt_info * info , int force )
2008-09-25 02:13:14 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_crypt_data * entry , * next ;
unsigned long flags ;
spin_lock_irqsave ( info - > lock , flags ) ;
list_for_each_entry_safe ( entry , next , & info - > crypt_deinit_list , list ) {
if ( atomic_read ( & entry - > refcnt ) ! = 0 & & ! force )
continue ;
list_del ( & entry - > list ) ;
if ( entry - > ops ) {
entry - > ops - > deinit ( entry - > priv ) ;
module_put ( entry - > ops - > owner ) ;
}
kfree ( entry ) ;
}
spin_unlock_irqrestore ( info - > lock , flags ) ;
}
EXPORT_SYMBOL ( lib80211_crypt_deinit_entries ) ;
/* After this, crypt_deinit_list won't accept new members */
void lib80211_crypt_quiescing ( struct lib80211_crypt_info * info )
{
unsigned long flags ;
spin_lock_irqsave ( info - > lock , flags ) ;
info - > crypt_quiesced = 1 ;
spin_unlock_irqrestore ( info - > lock , flags ) ;
}
EXPORT_SYMBOL ( lib80211_crypt_quiescing ) ;
void lib80211_crypt_deinit_handler ( unsigned long data )
{
struct lib80211_crypt_info * info = ( struct lib80211_crypt_info * ) data ;
unsigned long flags ;
lib80211_crypt_deinit_entries ( info , 0 ) ;
spin_lock_irqsave ( info - > lock , flags ) ;
if ( ! list_empty ( & info - > crypt_deinit_list ) & & ! info - > crypt_quiesced ) {
printk ( KERN_DEBUG " %s: entries remaining in delayed crypt "
" deletion list \n " , info - > name ) ;
info - > crypt_deinit_timer . expires = jiffies + HZ ;
add_timer ( & info - > crypt_deinit_timer ) ;
}
spin_unlock_irqrestore ( info - > lock , flags ) ;
}
EXPORT_SYMBOL ( lib80211_crypt_deinit_handler ) ;
void lib80211_crypt_delayed_deinit ( struct lib80211_crypt_info * info ,
struct lib80211_crypt_data * * crypt )
{
struct lib80211_crypt_data * tmp ;
unsigned long flags ;
if ( * crypt = = NULL )
return ;
tmp = * crypt ;
* crypt = NULL ;
/* must not run ops->deinit() while there may be pending encrypt or
* decrypt operations . Use a list of delayed deinits to avoid needing
* locking . */
spin_lock_irqsave ( info - > lock , flags ) ;
if ( ! info - > crypt_quiesced ) {
list_add ( & tmp - > list , & info - > crypt_deinit_list ) ;
if ( ! timer_pending ( & info - > crypt_deinit_timer ) ) {
info - > crypt_deinit_timer . expires = jiffies + HZ ;
add_timer ( & info - > crypt_deinit_timer ) ;
}
}
spin_unlock_irqrestore ( info - > lock , flags ) ;
}
EXPORT_SYMBOL ( lib80211_crypt_delayed_deinit ) ;
int lib80211_register_crypto_ops ( struct lib80211_crypto_ops * ops )
{
unsigned long flags ;
struct lib80211_crypto_alg * alg ;
alg = kzalloc ( sizeof ( * alg ) , GFP_KERNEL ) ;
if ( alg = = NULL )
return - ENOMEM ;
alg - > ops = ops ;
spin_lock_irqsave ( & lib80211_crypto_lock , flags ) ;
list_add ( & alg - > list , & lib80211_crypto_algs ) ;
spin_unlock_irqrestore ( & lib80211_crypto_lock , flags ) ;
printk ( KERN_DEBUG " lib80211_crypt: registered algorithm '%s' \n " ,
ops - > name ) ;
return 0 ;
}
EXPORT_SYMBOL ( lib80211_register_crypto_ops ) ;
int lib80211_unregister_crypto_ops ( struct lib80211_crypto_ops * ops )
{
struct lib80211_crypto_alg * alg ;
unsigned long flags ;
spin_lock_irqsave ( & lib80211_crypto_lock , flags ) ;
list_for_each_entry ( alg , & lib80211_crypto_algs , list ) {
if ( alg - > ops = = ops )
goto found ;
}
spin_unlock_irqrestore ( & lib80211_crypto_lock , flags ) ;
return - EINVAL ;
found :
printk ( KERN_DEBUG " lib80211_crypt: unregistered algorithm "
" '%s' \n " , ops - > name ) ;
list_del ( & alg - > list ) ;
spin_unlock_irqrestore ( & lib80211_crypto_lock , flags ) ;
kfree ( alg ) ;
2008-09-25 02:13:14 +04:00
return 0 ;
}
2008-10-29 18:35:05 +03:00
EXPORT_SYMBOL ( lib80211_unregister_crypto_ops ) ;
struct lib80211_crypto_ops * lib80211_get_crypto_ops ( const char * name )
{
struct lib80211_crypto_alg * alg ;
unsigned long flags ;
spin_lock_irqsave ( & lib80211_crypto_lock , flags ) ;
list_for_each_entry ( alg , & lib80211_crypto_algs , list ) {
if ( strcmp ( alg - > ops - > name , name ) = = 0 )
goto found ;
}
spin_unlock_irqrestore ( & lib80211_crypto_lock , flags ) ;
return NULL ;
found :
spin_unlock_irqrestore ( & lib80211_crypto_lock , flags ) ;
return alg - > ops ;
}
EXPORT_SYMBOL ( lib80211_get_crypto_ops ) ;
static void * lib80211_crypt_null_init ( int keyidx )
{
return ( void * ) 1 ;
}
static void lib80211_crypt_null_deinit ( void * priv )
{
}
static struct lib80211_crypto_ops lib80211_crypt_null = {
. name = " NULL " ,
. init = lib80211_crypt_null_init ,
. deinit = lib80211_crypt_null_deinit ,
. owner = THIS_MODULE ,
} ;
static int __init lib80211_init ( void )
{
printk ( KERN_INFO DRV_NAME " : " DRV_DESCRIPTION " \n " ) ;
return lib80211_register_crypto_ops ( & lib80211_crypt_null ) ;
}
2008-09-25 02:13:14 +04:00
2008-10-29 18:35:05 +03:00
static void __exit lib80211_exit ( void )
2008-09-25 02:13:14 +04:00
{
2008-10-29 18:35:05 +03:00
lib80211_unregister_crypto_ops ( & lib80211_crypt_null ) ;
BUG_ON ( ! list_empty ( & lib80211_crypto_algs ) ) ;
2008-09-25 02:13:14 +04:00
}
2008-10-29 18:35:05 +03:00
module_init ( lib80211_init ) ;
module_exit ( lib80211_exit ) ;