2005-04-17 02:20:36 +04:00
/*
* Changes :
* Arnaldo Carvalho de Melo < acme @ conectiva . com . br > 08 / 23 / 2000
* - get rid of some verify_areas and use __copy * user and __get / put_user
* for the ones that remain
*/
# include <linux/module.h>
# include <linux/blkdev.h>
# include <linux/interrupt.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/string.h>
# include <asm/uaccess.h>
# include <scsi/scsi.h>
2006-06-10 20:01:03 +04:00
# include <scsi/scsi_cmnd.h>
2005-04-17 02:20:36 +04:00
# include <scsi/scsi_device.h>
# include <scsi/scsi_eh.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_ioctl.h>
# include <scsi/sg.h>
# include <scsi/scsi_dbg.h>
# include "scsi_logging.h"
# define NORMAL_RETRIES 5
# define IOCTL_NORMAL_TIMEOUT (10 * HZ)
# define MAX_BUF PAGE_SIZE
2005-09-06 16:03:44 +04:00
/**
* ioctl_probe - - return host identification
* @ host : host to identify
* @ buffer : userspace buffer for identification
*
* Return an identifying string at @ buffer , if @ buffer is non - NULL , filling
* to the length stored at * ( int * ) @ buffer .
2005-04-17 02:20:36 +04:00
*/
static int ioctl_probe ( struct Scsi_Host * host , void __user * buffer )
{
unsigned int len , slen ;
const char * string ;
2005-09-06 16:03:44 +04:00
if ( buffer ) {
2005-04-17 02:20:36 +04:00
if ( get_user ( len , ( unsigned int __user * ) buffer ) )
return - EFAULT ;
if ( host - > hostt - > info )
string = host - > hostt - > info ( host ) ;
else
string = host - > hostt - > name ;
if ( string ) {
slen = strlen ( string ) ;
if ( len > slen )
len = slen + 1 ;
if ( copy_to_user ( buffer , string , len ) )
return - EFAULT ;
}
}
2005-09-06 16:03:44 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
/*
* The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host .
* The IOCTL_NORMAL_TIMEOUT and NORMAL_RETRIES variables are used .
*
* dev is the SCSI device struct ptr , * ( int * ) arg is the length of the
* input data , if any , not including the command string & counts ,
* * ( ( int * ) arg + 1 ) is the output buffer size in bytes .
*
* * ( char * ) ( ( int * ) arg ) [ 2 ] the actual command byte .
*
* Note that if more than MAX_BUF bytes are requested to be transferred ,
* the ioctl will fail with error EINVAL .
*
* This size * does not * include the initial lengths that were passed .
*
* The SCSI command is read from the memory location immediately after the
* length words , and the input data is right after the command . The SCSI
* routines know the command size based on the opcode decode .
*
* The output area is then filled in starting from the command byte .
*/
static int ioctl_internal_command ( struct scsi_device * sdev , char * cmd ,
int timeout , int retries )
{
int result ;
struct scsi_sense_hdr sshdr ;
SCSI_LOG_IOCTL ( 1 , printk ( " Trying ioctl with scsi command %d \n " , * cmd ) ) ;
2005-08-28 20:27:01 +04:00
result = scsi_execute_req ( sdev , cmd , DMA_NONE , NULL , 0 ,
2005-08-28 20:33:52 +04:00
& sshdr , timeout , retries ) ;
2005-04-17 02:20:36 +04:00
2005-08-28 20:27:01 +04:00
SCSI_LOG_IOCTL ( 2 , printk ( " Ioctl returned 0x%x \n " , result ) ) ;
2005-04-17 02:20:36 +04:00
2005-08-28 20:27:01 +04:00
if ( ( driver_byte ( result ) & DRIVER_SENSE ) & &
2005-08-28 20:33:52 +04:00
( scsi_sense_valid ( & sshdr ) ) ) {
2005-04-17 02:20:36 +04:00
switch ( sshdr . sense_key ) {
case ILLEGAL_REQUEST :
if ( cmd [ 0 ] = = ALLOW_MEDIUM_REMOVAL )
sdev - > lockable = 0 ;
else
printk ( KERN_INFO " ioctl_internal_command: "
" ILLEGAL REQUEST asc=0x%x ascq=0x%x \n " ,
sshdr . asc , sshdr . ascq ) ;
break ;
case NOT_READY : /* This happens if there is no disc in drive */
2006-07-28 11:04:09 +04:00
if ( sdev - > removable )
2005-04-17 02:20:36 +04:00
break ;
case UNIT_ATTENTION :
if ( sdev - > removable ) {
sdev - > changed = 1 ;
2005-08-28 20:27:01 +04:00
result = 0 ; /* This is no longer considered an error */
2005-04-17 02:20:36 +04:00
break ;
}
default : /* Fall through for non-removable media */
2005-10-02 20:45:08 +04:00
sdev_printk ( KERN_INFO , sdev ,
" ioctl_internal_command return code = %x \n " ,
result ) ;
2005-08-28 20:33:52 +04:00
scsi_print_sense_hdr ( " " , & sshdr ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
SCSI_LOG_IOCTL ( 2 , printk ( " IOCTL Releasing command \n " ) ) ;
return result ;
}
int scsi_set_medium_removal ( struct scsi_device * sdev , char state )
{
char scsi_cmd [ MAX_COMMAND_SIZE ] ;
int ret ;
if ( ! sdev - > removable | | ! sdev - > lockable )
return 0 ;
scsi_cmd [ 0 ] = ALLOW_MEDIUM_REMOVAL ;
scsi_cmd [ 1 ] = 0 ;
scsi_cmd [ 2 ] = 0 ;
scsi_cmd [ 3 ] = 0 ;
scsi_cmd [ 4 ] = state ;
scsi_cmd [ 5 ] = 0 ;
ret = ioctl_internal_command ( sdev , scsi_cmd ,
IOCTL_NORMAL_TIMEOUT , NORMAL_RETRIES ) ;
if ( ret = = 0 )
sdev - > locked = ( state = = SCSI_REMOVAL_PREVENT ) ;
return ret ;
}
EXPORT_SYMBOL ( scsi_set_medium_removal ) ;
/*
* The scsi_ioctl_get_pci ( ) function places into arg the value
* pci_dev : : slot_name ( 8 characters ) for the PCI device ( if any ) .
* Returns : 0 on success
* - ENXIO if there isn ' t a PCI device pointer
* ( could be because the SCSI driver hasn ' t been
* updated yet , or because it isn ' t a SCSI
* device )
* any copy_to_user ( ) error on failure there
*/
static int scsi_ioctl_get_pci ( struct scsi_device * sdev , void __user * arg )
{
struct device * dev = scsi_get_device ( sdev - > host ) ;
if ( ! dev )
return - ENXIO ;
return copy_to_user ( arg , dev - > bus_id , sizeof ( dev - > bus_id ) ) ? - EFAULT : 0 ;
}
2007-11-03 21:30:39 +03:00
/**
* scsi_ioctl - Dispatch ioctl to scsi device
* @ sdev : scsi device receiving ioctl
* @ cmd : which ioctl is it
* @ arg : data associated with ioctl
*
* Description : The scsi_ioctl ( ) function differs from most ioctls in that it
* does not take a major / minor number as the dev field . Rather , it takes
* a pointer to a & struct scsi_device .
2005-04-17 02:20:36 +04:00
*/
int scsi_ioctl ( struct scsi_device * sdev , int cmd , void __user * arg )
{
char scsi_cmd [ MAX_COMMAND_SIZE ] ;
/* No idea how this happens.... */
if ( ! sdev )
return - ENXIO ;
/*
* If we are in the middle of error recovery , don ' t let anyone
* else try and use this device . Also , if error recovery fails , it
* may try and take the device offline , in which case all further
* access to the device is prohibited .
*/
if ( ! scsi_block_when_processing_errors ( sdev ) )
return - ENODEV ;
/* Check for deprecated ioctls ... all the ioctls which don't
* follow the new unique numbering scheme are deprecated */
switch ( cmd ) {
case SCSI_IOCTL_SEND_COMMAND :
case SCSI_IOCTL_TEST_UNIT_READY :
case SCSI_IOCTL_BENCHMARK_COMMAND :
case SCSI_IOCTL_SYNC :
case SCSI_IOCTL_START_UNIT :
case SCSI_IOCTL_STOP_UNIT :
printk ( KERN_WARNING " program %s is using a deprecated SCSI "
" ioctl, please convert it to SG_IO \n " , current - > comm ) ;
break ;
default :
break ;
}
switch ( cmd ) {
case SCSI_IOCTL_GET_IDLUN :
if ( ! access_ok ( VERIFY_WRITE , arg , sizeof ( struct scsi_idlun ) ) )
return - EFAULT ;
__put_user ( ( sdev - > id & 0xff )
+ ( ( sdev - > lun & 0xff ) < < 8 )
+ ( ( sdev - > channel & 0xff ) < < 16 )
+ ( ( sdev - > host - > host_no & 0xff ) < < 24 ) ,
& ( ( struct scsi_idlun __user * ) arg ) - > dev_id ) ;
__put_user ( sdev - > host - > unique_id ,
& ( ( struct scsi_idlun __user * ) arg ) - > host_unique_id ) ;
return 0 ;
case SCSI_IOCTL_GET_BUS_NUMBER :
return put_user ( sdev - > host - > host_no , ( int __user * ) arg ) ;
case SCSI_IOCTL_PROBE_HOST :
return ioctl_probe ( sdev - > host , arg ) ;
case SCSI_IOCTL_SEND_COMMAND :
if ( ! capable ( CAP_SYS_ADMIN ) | | ! capable ( CAP_SYS_RAWIO ) )
return - EACCES ;
2006-03-22 19:52:04 +03:00
return sg_scsi_ioctl ( NULL , sdev - > request_queue , NULL , arg ) ;
2005-04-17 02:20:36 +04:00
case SCSI_IOCTL_DOORLOCK :
return scsi_set_medium_removal ( sdev , SCSI_REMOVAL_PREVENT ) ;
case SCSI_IOCTL_DOORUNLOCK :
return scsi_set_medium_removal ( sdev , SCSI_REMOVAL_ALLOW ) ;
case SCSI_IOCTL_TEST_UNIT_READY :
return scsi_test_unit_ready ( sdev , IOCTL_NORMAL_TIMEOUT ,
NORMAL_RETRIES ) ;
case SCSI_IOCTL_START_UNIT :
scsi_cmd [ 0 ] = START_STOP ;
scsi_cmd [ 1 ] = 0 ;
scsi_cmd [ 2 ] = scsi_cmd [ 3 ] = scsi_cmd [ 5 ] = 0 ;
scsi_cmd [ 4 ] = 1 ;
return ioctl_internal_command ( sdev , scsi_cmd ,
START_STOP_TIMEOUT , NORMAL_RETRIES ) ;
case SCSI_IOCTL_STOP_UNIT :
scsi_cmd [ 0 ] = START_STOP ;
scsi_cmd [ 1 ] = 0 ;
scsi_cmd [ 2 ] = scsi_cmd [ 3 ] = scsi_cmd [ 5 ] = 0 ;
scsi_cmd [ 4 ] = 0 ;
return ioctl_internal_command ( sdev , scsi_cmd ,
START_STOP_TIMEOUT , NORMAL_RETRIES ) ;
case SCSI_IOCTL_GET_PCI :
return scsi_ioctl_get_pci ( sdev , arg ) ;
default :
if ( sdev - > host - > hostt - > ioctl )
return sdev - > host - > hostt - > ioctl ( sdev , cmd , arg ) ;
}
return - EINVAL ;
}
EXPORT_SYMBOL ( scsi_ioctl ) ;
2007-11-03 21:30:39 +03:00
/**
* scsi_nonblock_ioctl ( ) - Handle SG_SCSI_RESET
* @ sdev : scsi device receiving ioctl
* @ cmd : Must be SC_SCSI_RESET
* @ arg : pointer to int containing SG_SCSI_RESET_ { DEVICE , BUS , HOST }
* @ filp : either NULL or a & struct file which must have the O_NONBLOCK flag .
2005-04-17 02:20:36 +04:00
*/
int scsi_nonblockable_ioctl ( struct scsi_device * sdev , int cmd ,
void __user * arg , struct file * filp )
{
int val , result ;
/* The first set of iocts may be executed even if we're doing
* error processing , as long as the device was opened
* non - blocking */
2007-11-03 21:30:39 +03:00
if ( filp & & ( filp - > f_flags & O_NONBLOCK ) ) {
2005-09-19 00:05:20 +04:00
if ( scsi_host_in_recovery ( sdev - > host ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
} else if ( ! scsi_block_when_processing_errors ( sdev ) )
return - ENODEV ;
switch ( cmd ) {
case SG_SCSI_RESET :
result = get_user ( val , ( int __user * ) arg ) ;
if ( result )
return result ;
if ( val = = SG_SCSI_RESET_NOTHING )
return 0 ;
switch ( val ) {
case SG_SCSI_RESET_DEVICE :
val = SCSI_TRY_RESET_DEVICE ;
break ;
case SG_SCSI_RESET_BUS :
val = SCSI_TRY_RESET_BUS ;
break ;
case SG_SCSI_RESET_HOST :
val = SCSI_TRY_RESET_HOST ;
break ;
default :
return - EINVAL ;
}
if ( ! capable ( CAP_SYS_ADMIN ) | | ! capable ( CAP_SYS_RAWIO ) )
return - EACCES ;
return ( scsi_reset_provider ( sdev , val ) = =
SUCCESS ) ? 0 : - EIO ;
}
return - ENODEV ;
}
EXPORT_SYMBOL ( scsi_nonblockable_ioctl ) ;