2005-05-12 22:48:20 -04:00
/*
* Host AP crypto routines
*
* Copyright ( c ) 2002 - 2003 , Jouni Malinen < jkmaline @ cc . hut . fi >
* Portions Copyright ( C ) 2004 , Intel Corporation < jketreno @ linux . intel . com >
*
* 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 .
*
*/
2005-11-09 01:01:04 -05:00
# include <linux/errno.h>
2005-05-12 22:48:20 -04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
2005-11-09 01:01:04 -05:00
# include <linux/string.h>
2005-05-12 22:48:20 -04:00
# include <net/ieee80211.h>
MODULE_AUTHOR ( " Jouni Malinen " ) ;
MODULE_DESCRIPTION ( " HostAP crypto " ) ;
MODULE_LICENSE ( " GPL " ) ;
struct ieee80211_crypto_alg {
struct list_head list ;
struct ieee80211_crypto_ops * ops ;
} ;
2005-11-09 01:01:04 -05:00
static LIST_HEAD ( ieee80211_crypto_algs ) ;
static DEFINE_SPINLOCK ( ieee80211_crypto_lock ) ;
2005-05-12 22:48:20 -04:00
2005-09-07 00:48:31 -04:00
void ieee80211_crypt_deinit_entries ( struct ieee80211_device * ieee , int force )
2005-05-12 22:48:20 -04:00
{
2006-01-19 16:20:49 +08:00
struct ieee80211_crypt_data * entry , * next ;
2005-09-21 11:53:43 -05:00
unsigned long flags ;
2005-05-12 22:48:20 -04:00
2005-09-21 11:53:43 -05:00
spin_lock_irqsave ( & ieee - > lock , flags ) ;
2006-01-19 16:20:49 +08:00
list_for_each_entry_safe ( entry , next , & ieee - > crypt_deinit_list , list ) {
2005-05-12 22:48:20 -04:00
if ( atomic_read ( & entry - > refcnt ) ! = 0 & & ! force )
continue ;
2005-11-09 01:01:04 -05:00
list_del ( & entry - > list ) ;
2005-05-12 22:48:20 -04:00
if ( entry - > ops ) {
entry - > ops - > deinit ( entry - > priv ) ;
module_put ( entry - > ops - > owner ) ;
}
kfree ( entry ) ;
}
2005-09-21 11:54:15 -05:00
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
}
/* After this, crypt_deinit_list won't accept new members */
void ieee80211_crypt_quiescing ( struct ieee80211_device * ieee )
{
unsigned long flags ;
spin_lock_irqsave ( & ieee - > lock , flags ) ;
ieee - > crypt_quiesced = 1 ;
2005-09-21 11:53:43 -05:00
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
2005-05-12 22:48:20 -04:00
}
void ieee80211_crypt_deinit_handler ( unsigned long data )
{
struct ieee80211_device * ieee = ( struct ieee80211_device * ) data ;
2005-09-21 11:54:15 -05:00
unsigned long flags ;
2005-05-12 22:48:20 -04:00
ieee80211_crypt_deinit_entries ( ieee , 0 ) ;
2005-09-21 11:54:15 -05:00
spin_lock_irqsave ( & ieee - > lock , flags ) ;
if ( ! list_empty ( & ieee - > crypt_deinit_list ) & & ! ieee - > crypt_quiesced ) {
2005-05-12 22:48:20 -04:00
printk ( KERN_DEBUG " %s: entries remaining in delayed crypt "
" deletion list \n " , ieee - > dev - > name ) ;
ieee - > crypt_deinit_timer . expires = jiffies + HZ ;
add_timer ( & ieee - > crypt_deinit_timer ) ;
}
2005-09-21 11:54:15 -05:00
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
2005-05-12 22:48:20 -04:00
}
void ieee80211_crypt_delayed_deinit ( struct ieee80211_device * ieee ,
struct ieee80211_crypt_data * * crypt )
{
struct ieee80211_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 ( & ieee - > lock , flags ) ;
2005-09-21 11:54:15 -05:00
if ( ! ieee - > crypt_quiesced ) {
list_add ( & tmp - > list , & ieee - > crypt_deinit_list ) ;
if ( ! timer_pending ( & ieee - > crypt_deinit_timer ) ) {
ieee - > crypt_deinit_timer . expires = jiffies + HZ ;
add_timer ( & ieee - > crypt_deinit_timer ) ;
}
2005-05-12 22:48:20 -04:00
}
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
}
int ieee80211_register_crypto_ops ( struct ieee80211_crypto_ops * ops )
{
unsigned long flags ;
struct ieee80211_crypto_alg * alg ;
alg = kmalloc ( sizeof ( * alg ) , GFP_KERNEL ) ;
if ( alg = = NULL )
return - ENOMEM ;
memset ( alg , 0 , sizeof ( * alg ) ) ;
alg - > ops = ops ;
2005-11-09 01:01:04 -05:00
spin_lock_irqsave ( & ieee80211_crypto_lock , flags ) ;
list_add ( & alg - > list , & ieee80211_crypto_algs ) ;
spin_unlock_irqrestore ( & ieee80211_crypto_lock , flags ) ;
2005-05-12 22:48:20 -04:00
printk ( KERN_DEBUG " ieee80211_crypt: registered algorithm '%s' \n " ,
ops - > name ) ;
return 0 ;
}
int ieee80211_unregister_crypto_ops ( struct ieee80211_crypto_ops * ops )
{
2005-11-09 01:01:04 -05:00
struct ieee80211_crypto_alg * alg ;
2005-05-12 22:48:20 -04:00
unsigned long flags ;
2005-11-09 01:01:04 -05:00
spin_lock_irqsave ( & ieee80211_crypto_lock , flags ) ;
list_for_each_entry ( alg , & ieee80211_crypto_algs , list ) {
if ( alg - > ops = = ops )
goto found ;
2005-05-12 22:48:20 -04:00
}
2005-11-09 01:01:04 -05:00
spin_unlock_irqrestore ( & ieee80211_crypto_lock , flags ) ;
return - EINVAL ;
2006-01-19 16:20:49 +08:00
found :
2005-11-09 01:01:04 -05:00
printk ( KERN_DEBUG " ieee80211_crypt: unregistered algorithm "
2006-01-19 16:20:49 +08:00
" '%s' \n " , ops - > name ) ;
2005-11-09 01:01:04 -05:00
list_del ( & alg - > list ) ;
spin_unlock_irqrestore ( & ieee80211_crypto_lock , flags ) ;
kfree ( alg ) ;
return 0 ;
2005-05-12 22:48:20 -04:00
}
2005-09-07 00:48:31 -04:00
struct ieee80211_crypto_ops * ieee80211_get_crypto_ops ( const char * name )
2005-05-12 22:48:20 -04:00
{
2005-11-09 01:01:04 -05:00
struct ieee80211_crypto_alg * alg ;
2005-05-12 22:48:20 -04:00
unsigned long flags ;
2005-11-09 01:01:04 -05:00
spin_lock_irqsave ( & ieee80211_crypto_lock , flags ) ;
list_for_each_entry ( alg , & ieee80211_crypto_algs , list ) {
if ( strcmp ( alg - > ops - > name , name ) = = 0 )
goto found ;
2005-05-12 22:48:20 -04:00
}
2005-11-09 01:01:04 -05:00
spin_unlock_irqrestore ( & ieee80211_crypto_lock , flags ) ;
return NULL ;
2005-05-12 22:48:20 -04:00
2006-01-19 16:20:49 +08:00
found :
2005-11-09 01:01:04 -05:00
spin_unlock_irqrestore ( & ieee80211_crypto_lock , flags ) ;
return alg - > ops ;
2005-05-12 22:48:20 -04:00
}
2005-09-22 10:34:15 +00:00
static void * ieee80211_crypt_null_init ( int keyidx )
2005-09-07 00:48:31 -04:00
{
return ( void * ) 1 ;
}
2005-11-09 01:01:04 -05:00
2005-09-07 00:48:31 -04:00
static void ieee80211_crypt_null_deinit ( void * priv )
{
}
2005-05-12 22:48:20 -04:00
static struct ieee80211_crypto_ops ieee80211_crypt_null = {
2005-09-13 17:35:21 -05:00
. name = " NULL " ,
. init = ieee80211_crypt_null_init ,
. deinit = ieee80211_crypt_null_deinit ,
. owner = THIS_MODULE ,
2005-05-12 22:48:20 -04:00
} ;
static int __init ieee80211_crypto_init ( void )
{
2005-11-09 01:01:04 -05:00
return ieee80211_register_crypto_ops ( & ieee80211_crypt_null ) ;
2005-05-12 22:48:20 -04:00
}
static void __exit ieee80211_crypto_deinit ( void )
{
2005-11-09 01:01:04 -05:00
ieee80211_unregister_crypto_ops ( & ieee80211_crypt_null ) ;
BUG_ON ( ! list_empty ( & ieee80211_crypto_algs ) ) ;
2005-05-12 22:48:20 -04:00
}
EXPORT_SYMBOL ( ieee80211_crypt_deinit_entries ) ;
EXPORT_SYMBOL ( ieee80211_crypt_deinit_handler ) ;
EXPORT_SYMBOL ( ieee80211_crypt_delayed_deinit ) ;
2005-09-21 11:54:15 -05:00
EXPORT_SYMBOL ( ieee80211_crypt_quiescing ) ;
2005-05-12 22:48:20 -04:00
EXPORT_SYMBOL ( ieee80211_register_crypto_ops ) ;
EXPORT_SYMBOL ( ieee80211_unregister_crypto_ops ) ;
EXPORT_SYMBOL ( ieee80211_get_crypto_ops ) ;
module_init ( ieee80211_crypto_init ) ;
module_exit ( ieee80211_crypto_deinit ) ;