2005-04-17 02:20:36 +04:00
/* net/atm/resources.c - Statically allocated resources */
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
/* Fixes
* Arnaldo Carvalho de Melo < acme @ conectiva . com . br >
* 2002 / 01 - don ' t free the whole struct sock on sk - > destruct time ,
* use the default destruct function initialized by sock_init_data */
# include <linux/ctype.h>
# include <linux/string.h>
# include <linux/atmdev.h>
# include <linux/sonet.h>
# include <linux/kernel.h> /* for barrier */
# include <linux/module.h>
# include <linux/bitops.h>
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/delay.h>
2006-03-21 09:35:41 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h> /* for struct sock */
# include "common.h"
# include "resources.h"
# include "addr.h"
LIST_HEAD ( atm_devs ) ;
2006-03-21 09:35:41 +03:00
DEFINE_MUTEX ( atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
static struct atm_dev * __alloc_atm_dev ( const char * type )
{
struct atm_dev * dev ;
dev = kmalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev )
return NULL ;
memset ( dev , 0 , sizeof ( * dev ) ) ;
dev - > type = type ;
dev - > signal = ATM_PHY_SIG_UNKNOWN ;
dev - > link_rate = ATM_OC3_PCR ;
spin_lock_init ( & dev - > lock ) ;
INIT_LIST_HEAD ( & dev - > local ) ;
2005-10-07 09:19:28 +04:00
INIT_LIST_HEAD ( & dev - > lecs ) ;
2005-04-17 02:20:36 +04:00
return dev ;
}
static struct atm_dev * __atm_dev_lookup ( int number )
{
struct atm_dev * dev ;
struct list_head * p ;
list_for_each ( p , & atm_devs ) {
dev = list_entry ( p , struct atm_dev , dev_list ) ;
2005-11-30 03:16:21 +03:00
if ( dev - > number = = number ) {
2005-04-17 02:20:36 +04:00
atm_dev_hold ( dev ) ;
return dev ;
}
}
return NULL ;
}
struct atm_dev * atm_dev_lookup ( int number )
{
struct atm_dev * dev ;
2006-03-21 09:35:41 +03:00
mutex_lock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
dev = __atm_dev_lookup ( number ) ;
2006-03-21 09:35:41 +03:00
mutex_unlock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
return dev ;
}
2005-11-30 03:16:41 +03:00
2005-04-17 02:20:36 +04:00
struct atm_dev * atm_dev_register ( const char * type , const struct atmdev_ops * ops ,
int number , unsigned long * flags )
{
struct atm_dev * dev , * inuse ;
dev = __alloc_atm_dev ( type ) ;
if ( ! dev ) {
printk ( KERN_ERR " atm_dev_register: no space for dev %s \n " ,
type ) ;
return NULL ;
}
2006-03-21 09:35:41 +03:00
mutex_lock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( number ! = - 1 ) {
if ( ( inuse = __atm_dev_lookup ( number ) ) ) {
atm_dev_put ( inuse ) ;
2006-03-21 09:35:41 +03:00
mutex_unlock ( & atm_dev_mutex ) ;
2005-04-22 03:48:26 +04:00
kfree ( dev ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
dev - > number = number ;
} else {
dev - > number = 0 ;
while ( ( inuse = __atm_dev_lookup ( dev - > number ) ) ) {
atm_dev_put ( inuse ) ;
dev - > number + + ;
}
}
dev - > ops = ops ;
if ( flags )
dev - > flags = * flags ;
else
memset ( & dev - > flags , 0 , sizeof ( dev - > flags ) ) ;
memset ( & dev - > stats , 0 , sizeof ( dev - > stats ) ) ;
atomic_set ( & dev - > refcnt , 1 ) ;
if ( atm_proc_dev_register ( dev ) < 0 ) {
printk ( KERN_ERR " atm_dev_register: "
" atm_proc_dev_register failed for dev %s \n " ,
type ) ;
2006-06-29 23:36:34 +04:00
goto out_fail ;
}
if ( atm_register_sysfs ( dev ) < 0 ) {
printk ( KERN_ERR " atm_dev_register: "
" atm_register_sysfs failed for dev %s \n " ,
type ) ;
atm_proc_dev_deregister ( dev ) ;
goto out_fail ;
2005-04-17 02:20:36 +04:00
}
2006-06-29 23:36:34 +04:00
2005-11-30 03:16:21 +03:00
list_add_tail ( & dev - > dev_list , & atm_devs ) ;
2005-04-17 02:20:36 +04:00
2006-06-29 23:36:34 +04:00
out :
mutex_unlock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
return dev ;
2006-06-29 23:36:34 +04:00
out_fail :
kfree ( dev ) ;
dev = NULL ;
goto out ;
2005-04-17 02:20:36 +04:00
}
void atm_dev_deregister ( struct atm_dev * dev )
{
2005-11-30 03:16:41 +03:00
BUG_ON ( test_bit ( ATM_DF_REMOVED , & dev - > flags ) ) ;
set_bit ( ATM_DF_REMOVED , & dev - > flags ) ;
/*
* if we remove current device from atm_devs list , new device
* with same number can appear , such we need deregister proc ,
* release async all vccs and remove them from vccs list too
*/
2006-03-21 09:35:41 +03:00
mutex_lock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
list_del ( & dev - > dev_list ) ;
2006-03-21 09:35:41 +03:00
mutex_unlock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-11-30 03:16:41 +03:00
atm_dev_release_vccs ( dev ) ;
2006-06-29 23:36:34 +04:00
atm_unregister_sysfs ( dev ) ;
2005-11-30 03:16:41 +03:00
atm_proc_dev_deregister ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-11-30 03:16:41 +03:00
atm_dev_put ( dev ) ;
2005-04-17 02:20:36 +04:00
}
static void copy_aal_stats ( struct k_atm_aal_stats * from ,
struct atm_aal_stats * to )
{
# define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
__AAL_STAT_ITEMS
# undef __HANDLE_ITEM
}
static void subtract_aal_stats ( struct k_atm_aal_stats * from ,
struct atm_aal_stats * to )
{
# define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
__AAL_STAT_ITEMS
# undef __HANDLE_ITEM
}
static int fetch_stats ( struct atm_dev * dev , struct atm_dev_stats __user * arg , int zero )
{
struct atm_dev_stats tmp ;
int error = 0 ;
copy_aal_stats ( & dev - > stats . aal0 , & tmp . aal0 ) ;
copy_aal_stats ( & dev - > stats . aal34 , & tmp . aal34 ) ;
copy_aal_stats ( & dev - > stats . aal5 , & tmp . aal5 ) ;
if ( arg )
error = copy_to_user ( arg , & tmp , sizeof ( tmp ) ) ;
if ( zero & & ! error ) {
subtract_aal_stats ( & dev - > stats . aal0 , & tmp . aal0 ) ;
subtract_aal_stats ( & dev - > stats . aal34 , & tmp . aal34 ) ;
subtract_aal_stats ( & dev - > stats . aal5 , & tmp . aal5 ) ;
}
return error ? - EFAULT : 0 ;
}
int atm_dev_ioctl ( unsigned int cmd , void __user * arg )
{
void __user * buf ;
int error , len , number , size = 0 ;
struct atm_dev * dev ;
struct list_head * p ;
int * tmp_buf , * tmp_p ;
struct atm_iobuf __user * iobuf = arg ;
struct atmif_sioc __user * sioc = arg ;
switch ( cmd ) {
case ATM_GETNAMES :
if ( get_user ( buf , & iobuf - > buffer ) )
return - EFAULT ;
if ( get_user ( len , & iobuf - > length ) )
return - EFAULT ;
2006-03-21 09:35:41 +03:00
mutex_lock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
list_for_each ( p , & atm_devs )
size + = sizeof ( int ) ;
if ( size > len ) {
2006-03-21 09:35:41 +03:00
mutex_unlock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
return - E2BIG ;
}
tmp_buf = kmalloc ( size , GFP_ATOMIC ) ;
if ( ! tmp_buf ) {
2006-03-21 09:35:41 +03:00
mutex_unlock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
tmp_p = tmp_buf ;
list_for_each ( p , & atm_devs ) {
dev = list_entry ( p , struct atm_dev , dev_list ) ;
* tmp_p + + = dev - > number ;
}
2006-03-21 09:35:41 +03:00
mutex_unlock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
error = ( ( copy_to_user ( buf , tmp_buf , size ) ) | |
put_user ( size , & iobuf - > length ) )
? - EFAULT : 0 ;
kfree ( tmp_buf ) ;
return error ;
default :
break ;
}
if ( get_user ( buf , & sioc - > arg ) )
return - EFAULT ;
if ( get_user ( len , & sioc - > length ) )
return - EFAULT ;
if ( get_user ( number , & sioc - > number ) )
return - EFAULT ;
2005-11-30 03:15:18 +03:00
if ( ! ( dev = try_then_request_module ( atm_dev_lookup ( number ) ,
" atm-device-%d " , number ) ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
switch ( cmd ) {
case ATM_GETTYPE :
size = strlen ( dev - > type ) + 1 ;
if ( copy_to_user ( buf , dev - > type , size ) ) {
error = - EFAULT ;
goto done ;
}
break ;
case ATM_GETESI :
size = ESI_LEN ;
if ( copy_to_user ( buf , dev - > esi , size ) ) {
error = - EFAULT ;
goto done ;
}
break ;
case ATM_SETESI :
{
int i ;
for ( i = 0 ; i < ESI_LEN ; i + + )
if ( dev - > esi [ i ] ) {
error = - EEXIST ;
goto done ;
}
}
/* fall through */
case ATM_SETESIF :
{
unsigned char esi [ ESI_LEN ] ;
if ( ! capable ( CAP_NET_ADMIN ) ) {
error = - EPERM ;
goto done ;
}
if ( copy_from_user ( esi , buf , ESI_LEN ) ) {
error = - EFAULT ;
goto done ;
}
memcpy ( dev - > esi , esi , ESI_LEN ) ;
error = ESI_LEN ;
goto done ;
}
case ATM_GETSTATZ :
if ( ! capable ( CAP_NET_ADMIN ) ) {
error = - EPERM ;
goto done ;
}
/* fall through */
case ATM_GETSTAT :
size = sizeof ( struct atm_dev_stats ) ;
error = fetch_stats ( dev , buf , cmd = = ATM_GETSTATZ ) ;
if ( error )
goto done ;
break ;
case ATM_GETCIRANGE :
size = sizeof ( struct atm_cirange ) ;
if ( copy_to_user ( buf , & dev - > ci_range , size ) ) {
error = - EFAULT ;
goto done ;
}
break ;
case ATM_GETLINKRATE :
size = sizeof ( int ) ;
if ( copy_to_user ( buf , & dev - > link_rate , size ) ) {
error = - EFAULT ;
goto done ;
}
break ;
case ATM_RSTADDR :
if ( ! capable ( CAP_NET_ADMIN ) ) {
error = - EPERM ;
goto done ;
}
2005-10-07 09:19:28 +04:00
atm_reset_addr ( dev , ATM_ADDR_LOCAL ) ;
2005-04-17 02:20:36 +04:00
break ;
case ATM_ADDADDR :
case ATM_DELADDR :
2005-10-07 09:19:28 +04:00
case ATM_ADDLECSADDR :
case ATM_DELLECSADDR :
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_NET_ADMIN ) ) {
error = - EPERM ;
goto done ;
}
{
struct sockaddr_atmsvc addr ;
if ( copy_from_user ( & addr , buf , sizeof ( addr ) ) ) {
error = - EFAULT ;
goto done ;
}
2005-10-07 09:19:28 +04:00
if ( cmd = = ATM_ADDADDR | | cmd = = ATM_ADDLECSADDR )
error = atm_add_addr ( dev , & addr ,
( cmd = = ATM_ADDADDR ?
ATM_ADDR_LOCAL : ATM_ADDR_LECS ) ) ;
2005-04-17 02:20:36 +04:00
else
2005-10-07 09:19:28 +04:00
error = atm_del_addr ( dev , & addr ,
( cmd = = ATM_DELADDR ?
ATM_ADDR_LOCAL : ATM_ADDR_LECS ) ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
case ATM_GETADDR :
2005-10-07 09:19:28 +04:00
case ATM_GETLECSADDR :
error = atm_get_addr ( dev , buf , len ,
( cmd = = ATM_GETADDR ?
ATM_ADDR_LOCAL : ATM_ADDR_LECS ) ) ;
2005-04-17 02:20:36 +04:00
if ( error < 0 )
goto done ;
size = error ;
/* may return 0, but later on size == 0 means "don't
write the length " */
error = put_user ( size , & sioc - > length )
? - EFAULT : 0 ;
goto done ;
case ATM_SETLOOP :
if ( __ATM_LM_XTRMT ( ( int ) ( unsigned long ) buf ) & &
__ATM_LM_XTLOC ( ( int ) ( unsigned long ) buf ) >
__ATM_LM_XTRMT ( ( int ) ( unsigned long ) buf ) ) {
error = - EINVAL ;
goto done ;
}
/* fall through */
case ATM_SETCIRANGE :
case SONET_GETSTATZ :
case SONET_SETDIAG :
case SONET_CLRDIAG :
case SONET_SETFRAMING :
if ( ! capable ( CAP_NET_ADMIN ) ) {
error = - EPERM ;
goto done ;
}
/* fall through */
default :
if ( ! dev - > ops - > ioctl ) {
error = - EINVAL ;
goto done ;
}
size = dev - > ops - > ioctl ( dev , cmd , buf ) ;
if ( size < 0 ) {
error = ( size = = - ENOIOCTLCMD ? - EINVAL : size ) ;
goto done ;
}
}
if ( size )
error = put_user ( size , & sioc - > length )
? - EFAULT : 0 ;
else
error = 0 ;
done :
atm_dev_put ( dev ) ;
return error ;
}
static __inline__ void * dev_get_idx ( loff_t left )
{
struct list_head * p ;
list_for_each ( p , & atm_devs ) {
if ( ! - - left )
break ;
}
return ( p ! = & atm_devs ) ? p : NULL ;
}
void * atm_dev_seq_start ( struct seq_file * seq , loff_t * pos )
{
2006-03-21 09:35:41 +03:00
mutex_lock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
return * pos ? dev_get_idx ( * pos ) : ( void * ) 1 ;
}
void atm_dev_seq_stop ( struct seq_file * seq , void * v )
{
2006-03-21 09:35:41 +03:00
mutex_unlock ( & atm_dev_mutex ) ;
2005-04-17 02:20:36 +04:00
}
void * atm_dev_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
+ + * pos ;
v = ( v = = ( void * ) 1 ) ? atm_devs . next : ( ( struct list_head * ) v ) - > next ;
return ( v = = & atm_devs ) ? NULL : v ;
}
EXPORT_SYMBOL ( atm_dev_register ) ;
EXPORT_SYMBOL ( atm_dev_deregister ) ;
EXPORT_SYMBOL ( atm_dev_lookup ) ;