2005-04-16 15:20:36 -07:00
/*
* I2O Configuration Interface Driver
*
* ( C ) Copyright 1999 - 2002 Red Hat
*
* Written by Alan Cox , Building Number Three Ltd
*
* Fixes / additions :
* Deepak Saxena ( 04 / 20 / 1999 ) :
* Added basic ioctl ( ) support
* Deepak Saxena ( 06 / 07 / 1999 ) :
* Added software download ioctl ( still testing )
2007-10-19 23:21:04 +02:00
* Auvo Häkkinen ( 09 / 10 / 1999 ) :
2005-04-16 15:20:36 -07:00
* Changes to i2o_cfg_reply ( ) , ioctl_parms ( )
* Added ioct_validate ( )
2007-10-19 23:21:04 +02:00
* Taneli Vähäkangas ( 09 / 30 / 1999 ) :
2005-04-16 15:20:36 -07:00
* Fixed ioctl_swdl ( )
2007-10-19 23:21:04 +02:00
* Taneli Vähäkangas ( 10 / 04 / 1999 ) :
2005-04-16 15:20:36 -07:00
* Changed ioctl_swdl ( ) , implemented ioctl_swul ( ) and ioctl_swdel ( )
* Deepak Saxena ( 11 / 18 / 1999 ) :
* Added event managmenet support
* Alan Cox < alan @ redhat . com > :
* 2.4 rewrite ported to 2.5
* Markus Lidel < Markus . Lidel @ shadowconnect . com > :
* Added pass - thru support for Adaptec ' s raidutils
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/miscdevice.h>
# include <linux/smp_lock.h>
# include <linux/compat.h>
# include <asm/uaccess.h>
2006-07-10 04:45:40 -07:00
# include "core.h"
2005-04-16 15:20:36 -07:00
2006-07-10 04:45:40 -07:00
# define SG_TABLESIZE 30
2006-01-06 00:19:32 -08:00
static int i2o_cfg_ioctl ( struct inode * , struct file * , unsigned int ,
unsigned long ) ;
2005-04-10 22:29:42 -05:00
2005-04-16 15:20:36 -07:00
static spinlock_t i2o_config_lock ;
# define MODINC(x,y) ((x) = ((x) + 1) % (y))
struct sg_simple_element {
u32 flag_count ;
u32 addr_bus ;
} ;
struct i2o_cfg_info {
struct file * fp ;
struct fasync_struct * fasync ;
struct i2o_evt_info event_q [ I2O_EVT_Q_LEN ] ;
u16 q_in ; // Queue head index
u16 q_out ; // Queue tail index
u16 q_len ; // Queue length
u16 q_lost ; // Number of lost events
ulong q_id ; // Event queue ID...used as tx_context
struct i2o_cfg_info * next ;
} ;
static struct i2o_cfg_info * open_files = NULL ;
static ulong i2o_cfg_info_id = 0 ;
static int i2o_cfg_getiops ( unsigned long arg )
{
struct i2o_controller * c ;
u8 __user * user_iop_table = ( void __user * ) arg ;
u8 tmp [ MAX_I2O_CONTROLLERS ] ;
int ret = 0 ;
memset ( tmp , 0 , MAX_I2O_CONTROLLERS ) ;
list_for_each_entry ( c , & i2o_controllers , list )
tmp [ c - > unit ] = 1 ;
if ( copy_to_user ( user_iop_table , tmp , MAX_I2O_CONTROLLERS ) )
ret = - EFAULT ;
return ret ;
} ;
static int i2o_cfg_gethrt ( unsigned long arg )
{
struct i2o_controller * c ;
struct i2o_cmd_hrtlct __user * cmd = ( struct i2o_cmd_hrtlct __user * ) arg ;
struct i2o_cmd_hrtlct kcmd ;
i2o_hrt * hrt ;
int len ;
u32 reslen ;
int ret = 0 ;
if ( copy_from_user ( & kcmd , cmd , sizeof ( struct i2o_cmd_hrtlct ) ) )
return - EFAULT ;
if ( get_user ( reslen , kcmd . reslen ) < 0 )
return - EFAULT ;
if ( kcmd . resbuf = = NULL )
return - EFAULT ;
c = i2o_find_iop ( kcmd . iop ) ;
if ( ! c )
return - ENXIO ;
hrt = ( i2o_hrt * ) c - > hrt . virt ;
len = 8 + ( ( hrt - > entry_len * hrt - > num_entries ) < < 2 ) ;
/* We did a get user...so assuming mem is ok...is this bad? */
put_user ( len , kcmd . reslen ) ;
if ( len > reslen )
ret = - ENOBUFS ;
if ( copy_to_user ( kcmd . resbuf , ( void * ) hrt , len ) )
ret = - EFAULT ;
return ret ;
} ;
static int i2o_cfg_getlct ( unsigned long arg )
{
struct i2o_controller * c ;
struct i2o_cmd_hrtlct __user * cmd = ( struct i2o_cmd_hrtlct __user * ) arg ;
struct i2o_cmd_hrtlct kcmd ;
i2o_lct * lct ;
int len ;
int ret = 0 ;
u32 reslen ;
if ( copy_from_user ( & kcmd , cmd , sizeof ( struct i2o_cmd_hrtlct ) ) )
return - EFAULT ;
if ( get_user ( reslen , kcmd . reslen ) < 0 )
return - EFAULT ;
if ( kcmd . resbuf = = NULL )
return - EFAULT ;
c = i2o_find_iop ( kcmd . iop ) ;
if ( ! c )
return - ENXIO ;
lct = ( i2o_lct * ) c - > lct ;
len = ( unsigned int ) lct - > table_size < < 2 ;
put_user ( len , kcmd . reslen ) ;
if ( len > reslen )
ret = - ENOBUFS ;
else if ( copy_to_user ( kcmd . resbuf , lct , len ) )
ret = - EFAULT ;
return ret ;
} ;
static int i2o_cfg_parms ( unsigned long arg , unsigned int type )
{
int ret = 0 ;
struct i2o_controller * c ;
struct i2o_device * dev ;
struct i2o_cmd_psetget __user * cmd =
( struct i2o_cmd_psetget __user * ) arg ;
struct i2o_cmd_psetget kcmd ;
u32 reslen ;
u8 * ops ;
u8 * res ;
int len = 0 ;
u32 i2o_cmd = ( type = = I2OPARMGET ?
I2O_CMD_UTIL_PARAMS_GET : I2O_CMD_UTIL_PARAMS_SET ) ;
if ( copy_from_user ( & kcmd , cmd , sizeof ( struct i2o_cmd_psetget ) ) )
return - EFAULT ;
if ( get_user ( reslen , kcmd . reslen ) )
return - EFAULT ;
c = i2o_find_iop ( kcmd . iop ) ;
if ( ! c )
return - ENXIO ;
dev = i2o_iop_find_device ( c , kcmd . tid ) ;
if ( ! dev )
return - ENXIO ;
2006-12-13 00:35:56 -08:00
ops = kmalloc ( kcmd . oplen , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! ops )
return - ENOMEM ;
if ( copy_from_user ( ops , kcmd . opbuf , kcmd . oplen ) ) {
kfree ( ops ) ;
return - EFAULT ;
}
/*
* It ' s possible to have a _very_ large table
* and that the user asks for all of it at once . . .
*/
2006-12-13 00:35:56 -08:00
res = kmalloc ( 65536 , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! res ) {
kfree ( ops ) ;
return - ENOMEM ;
}
len = i2o_parm_issue ( dev , i2o_cmd , ops , kcmd . oplen , res , 65536 ) ;
kfree ( ops ) ;
if ( len < 0 ) {
kfree ( res ) ;
return - EAGAIN ;
}
put_user ( len , kcmd . reslen ) ;
if ( len > reslen )
ret = - ENOBUFS ;
else if ( copy_to_user ( kcmd . resbuf , res , len ) )
ret = - EFAULT ;
kfree ( res ) ;
return ret ;
} ;
static int i2o_cfg_swdl ( unsigned long arg )
{
struct i2o_sw_xfer kxfer ;
struct i2o_sw_xfer __user * pxfer = ( struct i2o_sw_xfer __user * ) arg ;
unsigned char maxfrag = 0 , curfrag = 1 ;
struct i2o_dma buffer ;
2006-01-06 00:19:29 -08:00
struct i2o_message * msg ;
2005-04-16 15:20:36 -07:00
unsigned int status = 0 , swlen = 0 , fragsize = 8192 ;
struct i2o_controller * c ;
if ( copy_from_user ( & kxfer , pxfer , sizeof ( struct i2o_sw_xfer ) ) )
return - EFAULT ;
if ( get_user ( swlen , kxfer . swlen ) < 0 )
return - EFAULT ;
if ( get_user ( maxfrag , kxfer . maxfrag ) < 0 )
return - EFAULT ;
if ( get_user ( curfrag , kxfer . curfrag ) < 0 )
return - EFAULT ;
if ( curfrag = = maxfrag )
fragsize = swlen - ( maxfrag - 1 ) * 8192 ;
if ( ! kxfer . buf | | ! access_ok ( VERIFY_READ , kxfer . buf , fragsize ) )
return - EFAULT ;
c = i2o_find_iop ( kxfer . iop ) ;
if ( ! c )
return - ENXIO ;
2006-01-06 00:19:29 -08:00
msg = i2o_msg_get_wait ( c , I2O_TIMEOUT_MESSAGE_GET ) ;
if ( IS_ERR ( msg ) )
return PTR_ERR ( msg ) ;
2005-04-16 15:20:36 -07:00
if ( i2o_dma_alloc ( & c - > pdev - > dev , & buffer , fragsize , GFP_KERNEL ) ) {
2006-01-06 00:19:29 -08:00
i2o_msg_nop ( c , msg ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
2006-12-06 20:38:23 -08:00
if ( __copy_from_user ( buffer . virt , kxfer . buf , fragsize ) ) {
i2o_msg_nop ( c , msg ) ;
i2o_dma_free ( & c - > pdev - > dev , & buffer ) ;
return - EFAULT ;
}
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:29 -08:00
msg - > u . head [ 0 ] = cpu_to_le32 ( NINE_WORD_MSG_SIZE | SGL_OFFSET_7 ) ;
msg - > u . head [ 1 ] =
cpu_to_le32 ( I2O_CMD_SW_DOWNLOAD < < 24 | HOST_TID < < 12 |
ADAPTER_TID ) ;
msg - > u . head [ 2 ] = cpu_to_le32 ( i2o_config_driver . context ) ;
msg - > u . head [ 3 ] = cpu_to_le32 ( 0 ) ;
msg - > body [ 0 ] =
cpu_to_le32 ( ( ( ( u32 ) kxfer . flags ) < < 24 ) | ( ( ( u32 ) kxfer .
sw_type ) < < 16 ) |
( ( ( u32 ) maxfrag ) < < 8 ) | ( ( ( u32 ) curfrag ) ) ) ;
msg - > body [ 1 ] = cpu_to_le32 ( swlen ) ;
msg - > body [ 2 ] = cpu_to_le32 ( kxfer . sw_id ) ;
msg - > body [ 3 ] = cpu_to_le32 ( 0xD0000000 | fragsize ) ;
msg - > body [ 4 ] = cpu_to_le32 ( buffer . phys ) ;
2005-04-16 15:20:36 -07:00
osm_debug ( " swdl frag %d/%d (size %d) \n " , curfrag , maxfrag , fragsize ) ;
2006-01-06 00:19:29 -08:00
status = i2o_msg_post_wait_mem ( c , msg , 60 , & buffer ) ;
2005-04-16 15:20:36 -07:00
if ( status ! = - ETIMEDOUT )
i2o_dma_free ( & c - > pdev - > dev , & buffer ) ;
if ( status ! = I2O_POST_WAIT_OK ) {
// it fails if you try and send frags out of order
// and for some yet unknown reasons too
osm_info ( " swdl failed, DetailedStatus = %d \n " , status ) ;
return status ;
}
return 0 ;
} ;
static int i2o_cfg_swul ( unsigned long arg )
{
struct i2o_sw_xfer kxfer ;
struct i2o_sw_xfer __user * pxfer = ( struct i2o_sw_xfer __user * ) arg ;
unsigned char maxfrag = 0 , curfrag = 1 ;
struct i2o_dma buffer ;
2006-01-06 00:19:29 -08:00
struct i2o_message * msg ;
2005-04-16 15:20:36 -07:00
unsigned int status = 0 , swlen = 0 , fragsize = 8192 ;
struct i2o_controller * c ;
int ret = 0 ;
if ( copy_from_user ( & kxfer , pxfer , sizeof ( struct i2o_sw_xfer ) ) )
goto return_fault ;
if ( get_user ( swlen , kxfer . swlen ) < 0 )
goto return_fault ;
if ( get_user ( maxfrag , kxfer . maxfrag ) < 0 )
goto return_fault ;
if ( get_user ( curfrag , kxfer . curfrag ) < 0 )
goto return_fault ;
if ( curfrag = = maxfrag )
fragsize = swlen - ( maxfrag - 1 ) * 8192 ;
if ( ! kxfer . buf )
goto return_fault ;
c = i2o_find_iop ( kxfer . iop ) ;
if ( ! c )
return - ENXIO ;
2006-01-06 00:19:29 -08:00
msg = i2o_msg_get_wait ( c , I2O_TIMEOUT_MESSAGE_GET ) ;
if ( IS_ERR ( msg ) )
return PTR_ERR ( msg ) ;
2005-04-16 15:20:36 -07:00
if ( i2o_dma_alloc ( & c - > pdev - > dev , & buffer , fragsize , GFP_KERNEL ) ) {
2006-01-06 00:19:29 -08:00
i2o_msg_nop ( c , msg ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
2006-01-06 00:19:29 -08:00
msg - > u . head [ 0 ] = cpu_to_le32 ( NINE_WORD_MSG_SIZE | SGL_OFFSET_7 ) ;
msg - > u . head [ 1 ] =
cpu_to_le32 ( I2O_CMD_SW_UPLOAD < < 24 | HOST_TID < < 12 | ADAPTER_TID ) ;
msg - > u . head [ 2 ] = cpu_to_le32 ( i2o_config_driver . context ) ;
msg - > u . head [ 3 ] = cpu_to_le32 ( 0 ) ;
msg - > body [ 0 ] =
cpu_to_le32 ( ( u32 ) kxfer . flags < < 24 | ( u32 ) kxfer .
sw_type < < 16 | ( u32 ) maxfrag < < 8 | ( u32 ) curfrag ) ;
msg - > body [ 1 ] = cpu_to_le32 ( swlen ) ;
msg - > body [ 2 ] = cpu_to_le32 ( kxfer . sw_id ) ;
msg - > body [ 3 ] = cpu_to_le32 ( 0xD0000000 | fragsize ) ;
msg - > body [ 4 ] = cpu_to_le32 ( buffer . phys ) ;
2005-04-16 15:20:36 -07:00
osm_debug ( " swul frag %d/%d (size %d) \n " , curfrag , maxfrag , fragsize ) ;
2006-01-06 00:19:29 -08:00
status = i2o_msg_post_wait_mem ( c , msg , 60 , & buffer ) ;
2005-04-16 15:20:36 -07:00
if ( status ! = I2O_POST_WAIT_OK ) {
if ( status ! = - ETIMEDOUT )
i2o_dma_free ( & c - > pdev - > dev , & buffer ) ;
osm_info ( " swul failed, DetailedStatus = %d \n " , status ) ;
return status ;
}
if ( copy_to_user ( kxfer . buf , buffer . virt , fragsize ) )
ret = - EFAULT ;
i2o_dma_free ( & c - > pdev - > dev , & buffer ) ;
2005-06-23 22:02:23 -07:00
return_ret :
2005-04-16 15:20:36 -07:00
return ret ;
2005-06-23 22:02:23 -07:00
return_fault :
2005-04-16 15:20:36 -07:00
ret = - EFAULT ;
goto return_ret ;
} ;
static int i2o_cfg_swdel ( unsigned long arg )
{
struct i2o_controller * c ;
struct i2o_sw_xfer kxfer ;
struct i2o_sw_xfer __user * pxfer = ( struct i2o_sw_xfer __user * ) arg ;
2006-01-06 00:19:29 -08:00
struct i2o_message * msg ;
2005-04-16 15:20:36 -07:00
unsigned int swlen ;
int token ;
if ( copy_from_user ( & kxfer , pxfer , sizeof ( struct i2o_sw_xfer ) ) )
return - EFAULT ;
if ( get_user ( swlen , kxfer . swlen ) < 0 )
return - EFAULT ;
c = i2o_find_iop ( kxfer . iop ) ;
if ( ! c )
return - ENXIO ;
2006-01-06 00:19:29 -08:00
msg = i2o_msg_get_wait ( c , I2O_TIMEOUT_MESSAGE_GET ) ;
if ( IS_ERR ( msg ) )
return PTR_ERR ( msg ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:29 -08:00
msg - > u . head [ 0 ] = cpu_to_le32 ( SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0 ) ;
msg - > u . head [ 1 ] =
cpu_to_le32 ( I2O_CMD_SW_REMOVE < < 24 | HOST_TID < < 12 | ADAPTER_TID ) ;
msg - > u . head [ 2 ] = cpu_to_le32 ( i2o_config_driver . context ) ;
msg - > u . head [ 3 ] = cpu_to_le32 ( 0 ) ;
msg - > body [ 0 ] =
cpu_to_le32 ( ( u32 ) kxfer . flags < < 24 | ( u32 ) kxfer . sw_type < < 16 ) ;
msg - > body [ 1 ] = cpu_to_le32 ( swlen ) ;
msg - > body [ 2 ] = cpu_to_le32 ( kxfer . sw_id ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:29 -08:00
token = i2o_msg_post_wait ( c , msg , 10 ) ;
2005-04-16 15:20:36 -07:00
if ( token ! = I2O_POST_WAIT_OK ) {
osm_info ( " swdel failed, DetailedStatus = %d \n " , token ) ;
return - ETIMEDOUT ;
}
return 0 ;
} ;
static int i2o_cfg_validate ( unsigned long arg )
{
int token ;
int iop = ( int ) arg ;
2006-01-06 00:19:29 -08:00
struct i2o_message * msg ;
2005-04-16 15:20:36 -07:00
struct i2o_controller * c ;
c = i2o_find_iop ( iop ) ;
if ( ! c )
return - ENXIO ;
2006-01-06 00:19:29 -08:00
msg = i2o_msg_get_wait ( c , I2O_TIMEOUT_MESSAGE_GET ) ;
if ( IS_ERR ( msg ) )
return PTR_ERR ( msg ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:29 -08:00
msg - > u . head [ 0 ] = cpu_to_le32 ( FOUR_WORD_MSG_SIZE | SGL_OFFSET_0 ) ;
msg - > u . head [ 1 ] =
cpu_to_le32 ( I2O_CMD_CONFIG_VALIDATE < < 24 | HOST_TID < < 12 | iop ) ;
msg - > u . head [ 2 ] = cpu_to_le32 ( i2o_config_driver . context ) ;
msg - > u . head [ 3 ] = cpu_to_le32 ( 0 ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:29 -08:00
token = i2o_msg_post_wait ( c , msg , 10 ) ;
2005-04-16 15:20:36 -07:00
if ( token ! = I2O_POST_WAIT_OK ) {
osm_info ( " Can't validate configuration, ErrorStatus = %d \n " ,
token ) ;
return - ETIMEDOUT ;
}
return 0 ;
} ;
static int i2o_cfg_evt_reg ( unsigned long arg , struct file * fp )
{
2006-01-06 00:19:29 -08:00
struct i2o_message * msg ;
2005-04-16 15:20:36 -07:00
struct i2o_evt_id __user * pdesc = ( struct i2o_evt_id __user * ) arg ;
struct i2o_evt_id kdesc ;
struct i2o_controller * c ;
struct i2o_device * d ;
if ( copy_from_user ( & kdesc , pdesc , sizeof ( struct i2o_evt_id ) ) )
return - EFAULT ;
/* IOP exists? */
c = i2o_find_iop ( kdesc . iop ) ;
if ( ! c )
return - ENXIO ;
/* Device exists? */
d = i2o_iop_find_device ( c , kdesc . tid ) ;
if ( ! d )
return - ENODEV ;
2006-01-06 00:19:29 -08:00
msg = i2o_msg_get_wait ( c , I2O_TIMEOUT_MESSAGE_GET ) ;
if ( IS_ERR ( msg ) )
return PTR_ERR ( msg ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:29 -08:00
msg - > u . head [ 0 ] = cpu_to_le32 ( FOUR_WORD_MSG_SIZE | SGL_OFFSET_0 ) ;
msg - > u . head [ 1 ] =
cpu_to_le32 ( I2O_CMD_UTIL_EVT_REGISTER < < 24 | HOST_TID < < 12 |
kdesc . tid ) ;
msg - > u . head [ 2 ] = cpu_to_le32 ( i2o_config_driver . context ) ;
msg - > u . head [ 3 ] = cpu_to_le32 ( i2o_cntxt_list_add ( c , fp - > private_data ) ) ;
msg - > body [ 0 ] = cpu_to_le32 ( kdesc . evt_mask ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:29 -08:00
i2o_msg_post ( c , msg ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int i2o_cfg_evt_get ( unsigned long arg , struct file * fp )
{
struct i2o_cfg_info * p = NULL ;
struct i2o_evt_get __user * uget = ( struct i2o_evt_get __user * ) arg ;
struct i2o_evt_get kget ;
unsigned long flags ;
for ( p = open_files ; p ; p = p - > next )
if ( p - > q_id = = ( ulong ) fp - > private_data )
break ;
if ( ! p - > q_len )
return - ENOENT ;
memcpy ( & kget . info , & p - > event_q [ p - > q_out ] , sizeof ( struct i2o_evt_info ) ) ;
MODINC ( p - > q_out , I2O_EVT_Q_LEN ) ;
spin_lock_irqsave ( & i2o_config_lock , flags ) ;
p - > q_len - - ;
kget . pending = p - > q_len ;
kget . lost = p - > q_lost ;
spin_unlock_irqrestore ( & i2o_config_lock , flags ) ;
if ( copy_to_user ( uget , & kget , sizeof ( struct i2o_evt_get ) ) )
return - EFAULT ;
return 0 ;
}
# ifdef CONFIG_COMPAT
2005-06-23 22:02:23 -07:00
static int i2o_cfg_passthru32 ( struct file * file , unsigned cmnd ,
unsigned long arg )
2005-04-16 15:20:36 -07:00
{
struct i2o_cmd_passthru32 __user * cmd ;
struct i2o_controller * c ;
u32 __user * user_msg ;
u32 * reply = NULL ;
u32 __user * user_reply = NULL ;
u32 size = 0 ;
u32 reply_size = 0 ;
u32 rcode = 0 ;
struct i2o_dma sg_list [ SG_TABLESIZE ] ;
u32 sg_offset = 0 ;
u32 sg_count = 0 ;
u32 i = 0 ;
2005-06-23 22:02:11 -07:00
u32 sg_index = 0 ;
2005-04-16 15:20:36 -07:00
i2o_status_block * sb ;
struct i2o_message * msg ;
unsigned int iop ;
cmd = ( struct i2o_cmd_passthru32 __user * ) arg ;
if ( get_user ( iop , & cmd - > iop ) | | get_user ( i , & cmd - > msg ) )
return - EFAULT ;
user_msg = compat_ptr ( i ) ;
c = i2o_find_iop ( iop ) ;
if ( ! c ) {
osm_debug ( " controller %d not found \n " , iop ) ;
return - ENXIO ;
}
sb = c - > status_block . virt ;
if ( get_user ( size , & user_msg [ 0 ] ) ) {
osm_warn ( " unable to get size! \n " ) ;
return - EFAULT ;
}
size = size > > 16 ;
if ( size > sb - > inbound_frame_size ) {
osm_warn ( " size of message > inbound_frame_size " ) ;
return - EFAULT ;
}
user_reply = & user_msg [ size ] ;
size < < = 2 ; // Convert to bytes
2007-07-17 04:04:24 -07:00
msg = i2o_msg_get_wait ( c , I2O_TIMEOUT_MESSAGE_GET ) ;
if ( IS_ERR ( msg ) )
return PTR_ERR ( msg ) ;
rcode = - EFAULT ;
2005-04-16 15:20:36 -07:00
/* Copy in the user's I2O command */
if ( copy_from_user ( msg , user_msg , size ) ) {
osm_warn ( " unable to copy user message \n " ) ;
2007-07-17 04:04:24 -07:00
goto out ;
2005-04-16 15:20:36 -07:00
}
i2o_dump_message ( msg ) ;
if ( get_user ( reply_size , & user_reply [ 0 ] ) < 0 )
2007-07-17 04:04:24 -07:00
goto out ;
2005-04-16 15:20:36 -07:00
reply_size > > = 16 ;
reply_size < < = 2 ;
2007-07-17 04:04:24 -07:00
rcode = - ENOMEM ;
2006-01-06 00:19:33 -08:00
reply = kzalloc ( reply_size , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! reply ) {
printk ( KERN_WARNING " %s: Could not allocate reply buffer \n " ,
c - > name ) ;
2007-07-17 04:04:24 -07:00
goto out ;
2005-04-16 15:20:36 -07:00
}
sg_offset = ( msg - > u . head [ 0 ] > > 4 ) & 0x0f ;
memset ( sg_list , 0 , sizeof ( sg_list [ 0 ] ) * SG_TABLESIZE ) ;
if ( sg_offset ) {
struct sg_simple_element * sg ;
if ( sg_offset * 4 > = size ) {
rcode = - EFAULT ;
goto cleanup ;
}
// TODO 64bit fix
sg = ( struct sg_simple_element * ) ( ( & msg - > u . head [ 0 ] ) +
sg_offset ) ;
sg_count =
( size - sg_offset * 4 ) / sizeof ( struct sg_simple_element ) ;
if ( sg_count > SG_TABLESIZE ) {
printk ( KERN_DEBUG " %s:IOCTL SG List too large (%u) \n " ,
c - > name , sg_count ) ;
2005-06-23 22:02:11 -07:00
rcode = - EINVAL ;
goto cleanup ;
2005-04-16 15:20:36 -07:00
}
for ( i = 0 ; i < sg_count ; i + + ) {
int sg_size ;
struct i2o_dma * p ;
if ( ! ( sg [ i ] . flag_count & 0x10000000
/*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT */ ) ) {
printk ( KERN_DEBUG
" %s:Bad SG element %d - not simple (%x) \n " ,
c - > name , i , sg [ i ] . flag_count ) ;
rcode = - EINVAL ;
goto cleanup ;
}
sg_size = sg [ i ] . flag_count & 0xffffff ;
2006-01-06 00:19:32 -08:00
p = & ( sg_list [ sg_index ] ) ;
2005-04-16 15:20:36 -07:00
/* Allocate memory for the transfer */
if ( i2o_dma_alloc
( & c - > pdev - > dev , p , sg_size ,
PCI_DMA_BIDIRECTIONAL ) ) {
printk ( KERN_DEBUG
" %s: Could not allocate SG buffer - size = %d buffer number %d of %d \n " ,
c - > name , sg_size , i , sg_count ) ;
rcode = - ENOMEM ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
2006-01-06 00:19:32 -08:00
sg_index + + ;
2005-04-16 15:20:36 -07:00
/* Copy in the user's SG buffer if necessary */
if ( sg [ i ] .
flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) {
// TODO 64bit fix
if ( copy_from_user
2005-06-23 22:02:23 -07:00
( p - > virt ,
( void __user * ) ( unsigned long ) sg [ i ] .
addr_bus , sg_size ) ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_DEBUG
" %s: Could not copy SG buf %d FROM user \n " ,
c - > name , i ) ;
rcode = - EFAULT ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
}
//TODO 64bit fix
sg [ i ] . addr_bus = ( u32 ) p - > phys ;
}
}
2006-01-06 00:19:29 -08:00
rcode = i2o_msg_post_wait ( c , msg , 60 ) ;
2007-07-17 04:04:24 -07:00
msg = NULL ;
2006-01-06 00:19:32 -08:00
if ( rcode ) {
reply [ 4 ] = ( ( u32 ) rcode ) < < 24 ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2006-01-06 00:19:32 -08:00
}
2005-04-16 15:20:36 -07:00
if ( sg_offset ) {
2007-07-17 04:04:24 -07:00
u32 rmsg [ I2O_OUTBOUND_MSG_FRAME_SIZE ] ;
2005-04-16 15:20:36 -07:00
/* Copy back the Scatter Gather buffers back to user space */
u32 j ;
// TODO 64bit fix
struct sg_simple_element * sg ;
int sg_size ;
// re-acquire the original message to handle correctly the sg copy operation
2007-07-17 04:04:24 -07:00
memset ( & rmsg , 0 , I2O_OUTBOUND_MSG_FRAME_SIZE * 4 ) ;
2005-04-16 15:20:36 -07:00
// get user msg size in u32s
if ( get_user ( size , & user_msg [ 0 ] ) ) {
rcode = - EFAULT ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
size = size > > 16 ;
size * = 4 ;
/* Copy in the user's I2O command */
2007-07-17 04:04:24 -07:00
if ( copy_from_user ( rmsg , user_msg , size ) ) {
2005-04-16 15:20:36 -07:00
rcode = - EFAULT ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
sg_count =
( size - sg_offset * 4 ) / sizeof ( struct sg_simple_element ) ;
// TODO 64bit fix
2007-07-17 04:04:24 -07:00
sg = ( struct sg_simple_element * ) ( rmsg + sg_offset ) ;
2005-04-16 15:20:36 -07:00
for ( j = 0 ; j < sg_count ; j + + ) {
/* Copy out the SG list to user's buffer if necessary */
if ( !
( sg [ j ] .
flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR */ ) ) {
sg_size = sg [ j ] . flag_count & 0xffffff ;
// TODO 64bit fix
if ( copy_to_user
( ( void __user * ) ( u64 ) sg [ j ] . addr_bus ,
sg_list [ j ] . virt , sg_size ) ) {
printk ( KERN_WARNING
" %s: Could not copy %p TO user %x \n " ,
c - > name , sg_list [ j ] . virt ,
sg [ j ] . addr_bus ) ;
rcode = - EFAULT ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
}
}
}
2007-07-17 04:04:24 -07:00
sg_list_cleanup :
2005-04-16 15:20:36 -07:00
/* Copy back the reply to user space */
if ( reply_size ) {
// we wrote our own values for context - now restore the user supplied ones
if ( copy_from_user ( reply + 2 , user_msg + 2 , sizeof ( u32 ) * 2 ) ) {
printk ( KERN_WARNING
" %s: Could not copy message context FROM user \n " ,
c - > name ) ;
rcode = - EFAULT ;
}
if ( copy_to_user ( user_reply , reply , reply_size ) ) {
printk ( KERN_WARNING
" %s: Could not copy reply TO user \n " , c - > name ) ;
rcode = - EFAULT ;
}
}
2005-06-23 22:02:11 -07:00
for ( i = 0 ; i < sg_index ; i + + )
i2o_dma_free ( & c - > pdev - > dev , & sg_list [ i ] ) ;
2007-07-17 04:04:24 -07:00
cleanup :
2005-04-16 15:20:36 -07:00
kfree ( reply ) ;
2007-07-17 04:04:24 -07:00
out :
if ( msg )
i2o_msg_nop ( c , msg ) ;
2005-04-16 15:20:36 -07:00
return rcode ;
}
2005-06-23 22:02:23 -07:00
static long i2o_cfg_compat_ioctl ( struct file * file , unsigned cmd ,
unsigned long arg )
2005-04-10 22:29:42 -05:00
{
int ret ;
2005-06-23 22:02:23 -07:00
lock_kernel ( ) ;
switch ( cmd ) {
2005-04-10 22:29:42 -05:00
case I2OGETIOPS :
ret = i2o_cfg_ioctl ( NULL , file , cmd , arg ) ;
break ;
case I2OPASSTHRU32 :
ret = i2o_cfg_passthru32 ( file , cmd , arg ) ;
break ;
default :
ret = - ENOIOCTLCMD ;
break ;
}
unlock_kernel ( ) ;
return ret ;
}
# endif
2005-04-16 15:20:36 -07:00
2006-12-06 20:38:24 -08:00
# ifdef CONFIG_I2O_EXT_ADAPTEC
2005-04-16 15:20:36 -07:00
static int i2o_cfg_passthru ( unsigned long arg )
{
struct i2o_cmd_passthru __user * cmd =
( struct i2o_cmd_passthru __user * ) arg ;
struct i2o_controller * c ;
u32 __user * user_msg ;
u32 * reply = NULL ;
u32 __user * user_reply = NULL ;
u32 size = 0 ;
u32 reply_size = 0 ;
u32 rcode = 0 ;
void * sg_list [ SG_TABLESIZE ] ;
u32 sg_offset = 0 ;
u32 sg_count = 0 ;
int sg_index = 0 ;
u32 i = 0 ;
void * p = NULL ;
i2o_status_block * sb ;
2006-01-06 00:19:29 -08:00
struct i2o_message * msg ;
2005-04-16 15:20:36 -07:00
unsigned int iop ;
if ( get_user ( iop , & cmd - > iop ) | | get_user ( user_msg , & cmd - > msg ) )
return - EFAULT ;
c = i2o_find_iop ( iop ) ;
if ( ! c ) {
osm_warn ( " controller %d not found \n " , iop ) ;
return - ENXIO ;
}
sb = c - > status_block . virt ;
if ( get_user ( size , & user_msg [ 0 ] ) )
return - EFAULT ;
size = size > > 16 ;
if ( size > sb - > inbound_frame_size ) {
osm_warn ( " size of message > inbound_frame_size " ) ;
return - EFAULT ;
}
user_reply = & user_msg [ size ] ;
size < < = 2 ; // Convert to bytes
2007-07-17 04:04:24 -07:00
msg = i2o_msg_get_wait ( c , I2O_TIMEOUT_MESSAGE_GET ) ;
if ( IS_ERR ( msg ) )
return PTR_ERR ( msg ) ;
rcode = - EFAULT ;
2005-04-16 15:20:36 -07:00
/* Copy in the user's I2O command */
if ( copy_from_user ( msg , user_msg , size ) )
2007-07-17 04:04:24 -07:00
goto out ;
2005-04-16 15:20:36 -07:00
if ( get_user ( reply_size , & user_reply [ 0 ] ) < 0 )
2007-07-17 04:04:24 -07:00
goto out ;
2005-04-16 15:20:36 -07:00
reply_size > > = 16 ;
reply_size < < = 2 ;
2006-01-06 00:19:33 -08:00
reply = kzalloc ( reply_size , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! reply ) {
printk ( KERN_WARNING " %s: Could not allocate reply buffer \n " ,
c - > name ) ;
2007-07-17 04:04:24 -07:00
rcode = - ENOMEM ;
goto out ;
2005-04-16 15:20:36 -07:00
}
sg_offset = ( msg - > u . head [ 0 ] > > 4 ) & 0x0f ;
memset ( sg_list , 0 , sizeof ( sg_list [ 0 ] ) * SG_TABLESIZE ) ;
if ( sg_offset ) {
struct sg_simple_element * sg ;
if ( sg_offset * 4 > = size ) {
rcode = - EFAULT ;
goto cleanup ;
}
// TODO 64bit fix
sg = ( struct sg_simple_element * ) ( ( & msg - > u . head [ 0 ] ) +
sg_offset ) ;
sg_count =
( size - sg_offset * 4 ) / sizeof ( struct sg_simple_element ) ;
if ( sg_count > SG_TABLESIZE ) {
printk ( KERN_DEBUG " %s:IOCTL SG List too large (%u) \n " ,
c - > name , sg_count ) ;
2005-06-23 22:02:11 -07:00
rcode = - EINVAL ;
goto cleanup ;
2005-04-16 15:20:36 -07:00
}
for ( i = 0 ; i < sg_count ; i + + ) {
int sg_size ;
if ( ! ( sg [ i ] . flag_count & 0x10000000
/*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT */ ) ) {
printk ( KERN_DEBUG
" %s:Bad SG element %d - not simple (%x) \n " ,
c - > name , i , sg [ i ] . flag_count ) ;
rcode = - EINVAL ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
sg_size = sg [ i ] . flag_count & 0xffffff ;
/* Allocate memory for the transfer */
p = kmalloc ( sg_size , GFP_KERNEL ) ;
if ( ! p ) {
printk ( KERN_DEBUG
" %s: Could not allocate SG buffer - size = %d buffer number %d of %d \n " ,
c - > name , sg_size , i , sg_count ) ;
rcode = - ENOMEM ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
sg_list [ sg_index + + ] = p ; // sglist indexed with input frame, not our internal frame.
/* Copy in the user's SG buffer if necessary */
if ( sg [ i ] .
flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) {
// TODO 64bit fix
if ( copy_from_user
( p , ( void __user * ) sg [ i ] . addr_bus ,
sg_size ) ) {
printk ( KERN_DEBUG
" %s: Could not copy SG buf %d FROM user \n " ,
c - > name , i ) ;
rcode = - EFAULT ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
}
//TODO 64bit fix
sg [ i ] . addr_bus = virt_to_bus ( p ) ;
}
}
2006-01-06 00:19:29 -08:00
rcode = i2o_msg_post_wait ( c , msg , 60 ) ;
2007-07-17 04:04:24 -07:00
msg = NULL ;
2006-01-06 00:19:32 -08:00
if ( rcode ) {
reply [ 4 ] = ( ( u32 ) rcode ) < < 24 ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2006-01-06 00:19:32 -08:00
}
2005-04-16 15:20:36 -07:00
if ( sg_offset ) {
2007-07-17 04:04:24 -07:00
u32 rmsg [ 128 ] ;
2005-04-16 15:20:36 -07:00
/* Copy back the Scatter Gather buffers back to user space */
u32 j ;
// TODO 64bit fix
struct sg_simple_element * sg ;
int sg_size ;
// re-acquire the original message to handle correctly the sg copy operation
2007-07-17 04:04:24 -07:00
memset ( & rmsg , 0 , I2O_OUTBOUND_MSG_FRAME_SIZE * 4 ) ;
2005-04-16 15:20:36 -07:00
// get user msg size in u32s
if ( get_user ( size , & user_msg [ 0 ] ) ) {
rcode = - EFAULT ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
size = size > > 16 ;
size * = 4 ;
/* Copy in the user's I2O command */
2007-07-17 04:04:24 -07:00
if ( copy_from_user ( rmsg , user_msg , size ) ) {
2005-04-16 15:20:36 -07:00
rcode = - EFAULT ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
sg_count =
( size - sg_offset * 4 ) / sizeof ( struct sg_simple_element ) ;
// TODO 64bit fix
2007-07-17 04:04:24 -07:00
sg = ( struct sg_simple_element * ) ( rmsg + sg_offset ) ;
2005-04-16 15:20:36 -07:00
for ( j = 0 ; j < sg_count ; j + + ) {
/* Copy out the SG list to user's buffer if necessary */
if ( !
( sg [ j ] .
flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR */ ) ) {
sg_size = sg [ j ] . flag_count & 0xffffff ;
// TODO 64bit fix
if ( copy_to_user
( ( void __user * ) sg [ j ] . addr_bus , sg_list [ j ] ,
sg_size ) ) {
printk ( KERN_WARNING
" %s: Could not copy %p TO user %x \n " ,
c - > name , sg_list [ j ] ,
sg [ j ] . addr_bus ) ;
rcode = - EFAULT ;
2005-06-23 22:02:11 -07:00
goto sg_list_cleanup ;
2005-04-16 15:20:36 -07:00
}
}
}
}
2007-07-17 04:04:24 -07:00
sg_list_cleanup :
2005-04-16 15:20:36 -07:00
/* Copy back the reply to user space */
if ( reply_size ) {
// we wrote our own values for context - now restore the user supplied ones
if ( copy_from_user ( reply + 2 , user_msg + 2 , sizeof ( u32 ) * 2 ) ) {
printk ( KERN_WARNING
" %s: Could not copy message context FROM user \n " ,
c - > name ) ;
rcode = - EFAULT ;
}
if ( copy_to_user ( user_reply , reply , reply_size ) ) {
printk ( KERN_WARNING
" %s: Could not copy reply TO user \n " , c - > name ) ;
rcode = - EFAULT ;
}
}
2005-06-23 22:02:11 -07:00
for ( i = 0 ; i < sg_index ; i + + )
kfree ( sg_list [ i ] ) ;
2007-07-17 04:04:24 -07:00
cleanup :
2005-04-16 15:20:36 -07:00
kfree ( reply ) ;
2007-07-17 04:04:24 -07:00
out :
if ( msg )
i2o_msg_nop ( c , msg ) ;
2005-04-16 15:20:36 -07:00
return rcode ;
}
2005-06-23 22:02:19 -07:00
# endif
2005-04-16 15:20:36 -07:00
/*
* IOCTL Handler
*/
static int i2o_cfg_ioctl ( struct inode * inode , struct file * fp , unsigned int cmd ,
unsigned long arg )
{
int ret ;
switch ( cmd ) {
case I2OGETIOPS :
ret = i2o_cfg_getiops ( arg ) ;
break ;
case I2OHRTGET :
ret = i2o_cfg_gethrt ( arg ) ;
break ;
case I2OLCTGET :
ret = i2o_cfg_getlct ( arg ) ;
break ;
case I2OPARMSET :
ret = i2o_cfg_parms ( arg , I2OPARMSET ) ;
break ;
case I2OPARMGET :
ret = i2o_cfg_parms ( arg , I2OPARMGET ) ;
break ;
case I2OSWDL :
ret = i2o_cfg_swdl ( arg ) ;
break ;
case I2OSWUL :
ret = i2o_cfg_swul ( arg ) ;
break ;
case I2OSWDEL :
ret = i2o_cfg_swdel ( arg ) ;
break ;
case I2OVALIDATE :
ret = i2o_cfg_validate ( arg ) ;
break ;
case I2OEVTREG :
ret = i2o_cfg_evt_reg ( arg , fp ) ;
break ;
case I2OEVTGET :
ret = i2o_cfg_evt_get ( arg , fp ) ;
break ;
2005-06-23 22:02:19 -07:00
# ifdef CONFIG_I2O_EXT_ADAPTEC
2005-04-16 15:20:36 -07:00
case I2OPASSTHRU :
ret = i2o_cfg_passthru ( arg ) ;
break ;
2005-06-23 22:02:19 -07:00
# endif
2005-04-16 15:20:36 -07:00
default :
osm_debug ( " unknown ioctl called! \n " ) ;
ret = - EINVAL ;
}
return ret ;
}
static int cfg_open ( struct inode * inode , struct file * file )
{
struct i2o_cfg_info * tmp =
( struct i2o_cfg_info * ) kmalloc ( sizeof ( struct i2o_cfg_info ) ,
GFP_KERNEL ) ;
unsigned long flags ;
if ( ! tmp )
return - ENOMEM ;
file - > private_data = ( void * ) ( i2o_cfg_info_id + + ) ;
tmp - > fp = file ;
tmp - > fasync = NULL ;
tmp - > q_id = ( ulong ) file - > private_data ;
tmp - > q_len = 0 ;
tmp - > q_in = 0 ;
tmp - > q_out = 0 ;
tmp - > q_lost = 0 ;
tmp - > next = open_files ;
spin_lock_irqsave ( & i2o_config_lock , flags ) ;
open_files = tmp ;
spin_unlock_irqrestore ( & i2o_config_lock , flags ) ;
return 0 ;
}
static int cfg_fasync ( int fd , struct file * fp , int on )
{
ulong id = ( ulong ) fp - > private_data ;
struct i2o_cfg_info * p ;
for ( p = open_files ; p ; p = p - > next )
if ( p - > q_id = = id )
break ;
if ( ! p )
return - EBADF ;
return fasync_helper ( fd , fp , on , & p - > fasync ) ;
}
static int cfg_release ( struct inode * inode , struct file * file )
{
ulong id = ( ulong ) file - > private_data ;
struct i2o_cfg_info * p1 , * p2 ;
unsigned long flags ;
lock_kernel ( ) ;
p1 = p2 = NULL ;
spin_lock_irqsave ( & i2o_config_lock , flags ) ;
for ( p1 = open_files ; p1 ; ) {
if ( p1 - > q_id = = id ) {
if ( p1 - > fasync )
cfg_fasync ( - 1 , file , 0 ) ;
if ( p2 )
p2 - > next = p1 - > next ;
else
open_files = p1 - > next ;
kfree ( p1 ) ;
break ;
}
p2 = p1 ;
p1 = p1 - > next ;
}
spin_unlock_irqrestore ( & i2o_config_lock , flags ) ;
unlock_kernel ( ) ;
return 0 ;
}
2007-02-12 00:55:34 -08:00
static const struct file_operations config_fops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. ioctl = i2o_cfg_ioctl ,
2005-04-10 22:29:42 -05:00
# ifdef CONFIG_COMPAT
. compat_ioctl = i2o_cfg_compat_ioctl ,
# endif
2005-04-16 15:20:36 -07:00
. open = cfg_open ,
. release = cfg_release ,
. fasync = cfg_fasync ,
} ;
static struct miscdevice i2o_miscdev = {
I2O_MINOR ,
" i2octl " ,
& config_fops
} ;
2005-06-23 22:02:16 -07:00
static int __init i2o_config_old_init ( void )
2005-04-16 15:20:36 -07:00
{
spin_lock_init ( & i2o_config_lock ) ;
if ( misc_register ( & i2o_miscdev ) < 0 ) {
osm_err ( " can't register device. \n " ) ;
return - EBUSY ;
}
2005-06-23 22:02:23 -07:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-06-23 22:02:16 -07:00
static void i2o_config_old_exit ( void )
2005-04-16 15:20:36 -07:00
{
misc_deregister ( & i2o_miscdev ) ;
}
MODULE_AUTHOR ( " Red Hat Software " ) ;