2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2001 Jens Axboe < axboe @ suse . de >
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public Licens
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 -
*
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/module.h>
# include <linux/blkdev.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/completion.h>
# include <linux/cdrom.h>
# include <linux/slab.h>
# include <linux/times.h>
# include <asm/uaccess.h>
# include <scsi/scsi.h>
# include <scsi/scsi_ioctl.h>
# include <scsi/scsi_cmnd.h>
/* Command group 3 is reserved and should never be used. */
const unsigned char scsi_command_size [ 8 ] =
{
6 , 10 , 10 , 12 ,
16 , 12 , 10 , 10
} ;
EXPORT_SYMBOL ( scsi_command_size ) ;
# include <scsi/sg.h>
static int sg_get_version ( int __user * p )
{
2006-01-06 11:46:02 +03:00
static const int sg_version_num = 30527 ;
2005-04-17 02:20:36 +04:00
return put_user ( sg_version_num , p ) ;
}
static int scsi_get_idlun ( request_queue_t * q , int __user * p )
{
return put_user ( 0 , p ) ;
}
static int scsi_get_bus ( request_queue_t * q , int __user * p )
{
return put_user ( 0 , p ) ;
}
static int sg_get_timeout ( request_queue_t * q )
{
return q - > sg_timeout / ( HZ / USER_HZ ) ;
}
static int sg_set_timeout ( request_queue_t * q , int __user * p )
{
int timeout , err = get_user ( timeout , p ) ;
if ( ! err )
q - > sg_timeout = timeout * ( HZ / USER_HZ ) ;
return err ;
}
static int sg_get_reserved_size ( request_queue_t * q , int __user * p )
{
2007-02-20 19:01:57 +03:00
unsigned val = min ( q - > sg_reserved_size , q - > max_sectors < < 9 ) ;
return put_user ( val , p ) ;
2005-04-17 02:20:36 +04:00
}
static int sg_set_reserved_size ( request_queue_t * q , int __user * p )
{
int size , err = get_user ( size , p ) ;
if ( err )
return err ;
if ( size < 0 )
return - EINVAL ;
if ( size > ( q - > max_sectors < < 9 ) )
size = q - > max_sectors < < 9 ;
q - > sg_reserved_size = size ;
return 0 ;
}
/*
* will always return that we are ATAPI even for a real SCSI drive , I ' m not
* so sure this is worth doing anything about ( why would you care ? ? )
*/
static int sg_emulated_host ( request_queue_t * q , int __user * p )
{
return put_user ( 1 , p ) ;
}
# define CMD_READ_SAFE 0x01
# define CMD_WRITE_SAFE 0x02
# define CMD_WARNED 0x04
# define safe_for_read(cmd) [cmd] = CMD_READ_SAFE
# define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE
2006-12-20 13:18:54 +03:00
int blk_verify_command ( unsigned char * cmd , int has_write_perm )
2005-04-17 02:20:36 +04:00
{
static unsigned char cmd_type [ 256 ] = {
/* Basic read-only commands */
safe_for_read ( TEST_UNIT_READY ) ,
safe_for_read ( REQUEST_SENSE ) ,
safe_for_read ( READ_6 ) ,
safe_for_read ( READ_10 ) ,
safe_for_read ( READ_12 ) ,
safe_for_read ( READ_16 ) ,
safe_for_read ( READ_BUFFER ) ,
2005-09-09 14:07:32 +04:00
safe_for_read ( READ_DEFECT_DATA ) ,
2005-04-17 02:20:36 +04:00
safe_for_read ( READ_LONG ) ,
safe_for_read ( INQUIRY ) ,
safe_for_read ( MODE_SENSE ) ,
safe_for_read ( MODE_SENSE_10 ) ,
safe_for_read ( LOG_SENSE ) ,
safe_for_read ( START_STOP ) ,
safe_for_read ( GPCMD_VERIFY_10 ) ,
safe_for_read ( VERIFY_16 ) ,
/* Audio CD commands */
safe_for_read ( GPCMD_PLAY_CD ) ,
safe_for_read ( GPCMD_PLAY_AUDIO_10 ) ,
safe_for_read ( GPCMD_PLAY_AUDIO_MSF ) ,
safe_for_read ( GPCMD_PLAY_AUDIO_TI ) ,
safe_for_read ( GPCMD_PAUSE_RESUME ) ,
/* CD/DVD data reading */
safe_for_read ( GPCMD_READ_BUFFER_CAPACITY ) ,
safe_for_read ( GPCMD_READ_CD ) ,
safe_for_read ( GPCMD_READ_CD_MSF ) ,
safe_for_read ( GPCMD_READ_DISC_INFO ) ,
safe_for_read ( GPCMD_READ_CDVD_CAPACITY ) ,
safe_for_read ( GPCMD_READ_DVD_STRUCTURE ) ,
safe_for_read ( GPCMD_READ_HEADER ) ,
safe_for_read ( GPCMD_READ_TRACK_RZONE_INFO ) ,
safe_for_read ( GPCMD_READ_SUBCHANNEL ) ,
safe_for_read ( GPCMD_READ_TOC_PMA_ATIP ) ,
safe_for_read ( GPCMD_REPORT_KEY ) ,
safe_for_read ( GPCMD_SCAN ) ,
safe_for_read ( GPCMD_GET_CONFIGURATION ) ,
safe_for_read ( GPCMD_READ_FORMAT_CAPACITIES ) ,
safe_for_read ( GPCMD_GET_EVENT_STATUS_NOTIFICATION ) ,
safe_for_read ( GPCMD_GET_PERFORMANCE ) ,
safe_for_read ( GPCMD_SEEK ) ,
safe_for_read ( GPCMD_STOP_PLAY_SCAN ) ,
/* Basic writing commands */
safe_for_write ( WRITE_6 ) ,
safe_for_write ( WRITE_10 ) ,
safe_for_write ( WRITE_VERIFY ) ,
safe_for_write ( WRITE_12 ) ,
safe_for_write ( WRITE_VERIFY_12 ) ,
safe_for_write ( WRITE_16 ) ,
safe_for_write ( WRITE_LONG ) ,
2005-09-17 06:27:58 +04:00
safe_for_write ( WRITE_LONG_2 ) ,
2005-04-17 02:20:36 +04:00
safe_for_write ( ERASE ) ,
safe_for_write ( GPCMD_MODE_SELECT_10 ) ,
safe_for_write ( MODE_SELECT ) ,
safe_for_write ( LOG_SELECT ) ,
safe_for_write ( GPCMD_BLANK ) ,
safe_for_write ( GPCMD_CLOSE_TRACK ) ,
safe_for_write ( GPCMD_FLUSH_CACHE ) ,
safe_for_write ( GPCMD_FORMAT_UNIT ) ,
safe_for_write ( GPCMD_REPAIR_RZONE_TRACK ) ,
safe_for_write ( GPCMD_RESERVE_RZONE_TRACK ) ,
safe_for_write ( GPCMD_SEND_DVD_STRUCTURE ) ,
safe_for_write ( GPCMD_SEND_EVENT ) ,
safe_for_write ( GPCMD_SEND_KEY ) ,
safe_for_write ( GPCMD_SEND_OPC ) ,
safe_for_write ( GPCMD_SEND_CUE_SHEET ) ,
safe_for_write ( GPCMD_SET_SPEED ) ,
safe_for_write ( GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL ) ,
safe_for_write ( GPCMD_LOAD_UNLOAD ) ,
safe_for_write ( GPCMD_SET_STREAMING ) ,
} ;
unsigned char type = cmd_type [ cmd [ 0 ] ] ;
/* Anybody who can open the device can do a read-safe command */
if ( type & CMD_READ_SAFE )
return 0 ;
/* Write-safe commands just require a writable open.. */
2006-01-09 16:52:21 +03:00
if ( ( type & CMD_WRITE_SAFE ) & & has_write_perm )
return 0 ;
2005-04-17 02:20:36 +04:00
2005-10-07 21:41:34 +04:00
/* And root can do any command.. */
if ( capable ( CAP_SYS_RAWIO ) )
return 0 ;
2005-04-17 02:20:36 +04:00
if ( ! type ) {
cmd_type [ cmd [ 0 ] ] = CMD_WARNED ;
printk ( KERN_WARNING " scsi: unknown opcode 0x%02x \n " , cmd [ 0 ] ) ;
}
/* Otherwise fail it with an "Operation not permitted" */
return - EPERM ;
}
2006-12-20 13:18:54 +03:00
EXPORT_SYMBOL_GPL ( blk_verify_command ) ;
2005-04-17 02:20:36 +04:00
2007-07-09 14:38:05 +04:00
int blk_fill_sghdr_rq ( request_queue_t * q , struct request * rq ,
struct sg_io_hdr * hdr , int has_write_perm )
{
memset ( rq - > cmd , 0 , BLK_MAX_CDB ) ; /* ATAPI hates garbage after CDB */
if ( copy_from_user ( rq - > cmd , hdr - > cmdp , hdr - > cmd_len ) )
return - EFAULT ;
2006-12-20 13:18:54 +03:00
if ( blk_verify_command ( rq - > cmd , has_write_perm ) )
2007-07-09 14:38:05 +04:00
return - EPERM ;
/*
* fill in request structure
*/
rq - > cmd_len = hdr - > cmd_len ;
rq - > cmd_type = REQ_TYPE_BLOCK_PC ;
rq - > timeout = ( hdr - > timeout * HZ ) / 1000 ;
if ( ! rq - > timeout )
rq - > timeout = q - > sg_timeout ;
if ( ! rq - > timeout )
rq - > timeout = BLK_DEFAULT_SG_TIMEOUT ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( blk_fill_sghdr_rq ) ;
/*
* unmap a request that was previously mapped to this sg_io_hdr . handles
* both sg and non - sg sg_io_hdr .
*/
int blk_unmap_sghdr_rq ( struct request * rq , struct sg_io_hdr * hdr )
{
2006-12-20 13:17:43 +03:00
blk_rq_unmap_user ( rq - > bio ) ;
2007-07-09 14:38:05 +04:00
blk_put_request ( rq ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( blk_unmap_sghdr_rq ) ;
int blk_complete_sghdr_rq ( struct request * rq , struct sg_io_hdr * hdr ,
struct bio * bio )
{
int r , ret = 0 ;
/*
* fill in all the output members
*/
hdr - > status = rq - > errors & 0xff ;
hdr - > masked_status = status_byte ( rq - > errors ) ;
hdr - > msg_status = msg_byte ( rq - > errors ) ;
hdr - > host_status = host_byte ( rq - > errors ) ;
hdr - > driver_status = driver_byte ( rq - > errors ) ;
hdr - > info = 0 ;
if ( hdr - > masked_status | | hdr - > host_status | | hdr - > driver_status )
hdr - > info | = SG_INFO_CHECK ;
hdr - > resid = rq - > data_len ;
hdr - > sb_len_wr = 0 ;
if ( rq - > sense_len & & hdr - > sbp ) {
int len = min ( ( unsigned int ) hdr - > mx_sb_len , rq - > sense_len ) ;
if ( ! copy_to_user ( hdr - > sbp , rq - > sense , len ) )
hdr - > sb_len_wr = len ;
else
ret = - EFAULT ;
}
rq - > bio = bio ;
r = blk_unmap_sghdr_rq ( rq , hdr ) ;
if ( ret )
r = ret ;
return r ;
}
EXPORT_SYMBOL_GPL ( blk_complete_sghdr_rq ) ;
2005-04-17 02:20:36 +04:00
static int sg_io ( struct file * file , request_queue_t * q ,
struct gendisk * bd_disk , struct sg_io_hdr * hdr )
{
2007-07-09 14:38:05 +04:00
unsigned long start_time ;
int writing = 0 , ret = 0 , has_write_perm = 0 ;
2005-04-17 02:20:36 +04:00
struct request * rq ;
char sense [ SCSI_SENSE_BUFFERSIZE ] ;
2006-12-11 12:01:34 +03:00
struct bio * bio ;
2005-04-17 02:20:36 +04:00
if ( hdr - > interface_id ! = ' S ' )
return - EINVAL ;
if ( hdr - > cmd_len > BLK_MAX_CDB )
return - EINVAL ;
2005-12-05 11:37:06 +03:00
if ( hdr - > dxfer_len > ( q - > max_hw_sectors < < 9 ) )
2005-04-17 02:20:36 +04:00
return - EIO ;
2005-06-20 16:06:52 +04:00
if ( hdr - > dxfer_len )
2005-04-17 02:20:36 +04:00
switch ( hdr - > dxfer_direction ) {
default :
return - EINVAL ;
case SG_DXFER_TO_DEV :
writing = 1 ;
break ;
2006-11-13 20:04:59 +03:00
case SG_DXFER_TO_FROM_DEV :
2005-04-17 02:20:36 +04:00
case SG_DXFER_FROM_DEV :
break ;
}
2005-06-20 16:06:01 +04:00
rq = blk_get_request ( q , writing ? WRITE : READ , GFP_KERNEL ) ;
if ( ! rq )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-07-09 14:38:05 +04:00
if ( file )
has_write_perm = file - > f_mode & FMODE_WRITE ;
2005-04-17 02:20:36 +04:00
2007-07-09 14:38:05 +04:00
if ( blk_fill_sghdr_rq ( q , rq , hdr , has_write_perm ) ) {
blk_put_request ( rq ) ;
return - EFAULT ;
}
2005-04-17 02:20:36 +04:00
2006-12-01 12:40:55 +03:00
if ( hdr - > iovec_count ) {
const int size = sizeof ( struct sg_iovec ) * hdr - > iovec_count ;
struct sg_iovec * iov ;
iov = kmalloc ( size , GFP_KERNEL ) ;
if ( ! iov ) {
ret = - ENOMEM ;
goto out ;
}
if ( copy_from_user ( iov , hdr - > dxferp , size ) ) {
kfree ( iov ) ;
ret = - EFAULT ;
goto out ;
}
ret = blk_rq_map_user_iov ( q , rq , iov , hdr - > iovec_count ,
hdr - > dxfer_len ) ;
kfree ( iov ) ;
} else if ( hdr - > dxfer_len )
ret = blk_rq_map_user ( q , rq , hdr - > dxferp , hdr - > dxfer_len ) ;
if ( ret )
goto out ;
2006-12-11 12:01:34 +03:00
bio = rq - > bio ;
2007-07-09 14:38:05 +04:00
memset ( sense , 0 , sizeof ( sense ) ) ;
rq - > sense = sense ;
rq - > sense_len = 0 ;
2006-02-03 10:37:08 +03:00
rq - > retries = 0 ;
2005-04-17 02:20:36 +04:00
start_time = jiffies ;
/* ignore return value. All information is passed back to caller
* ( if he doesn ' t check that is his problem ) .
* N . B . a non - zero SCSI status is _not_ necessarily an error .
*/
2005-06-20 16:11:09 +04:00
blk_execute_rq ( q , bd_disk , rq , 0 ) ;
2005-04-17 02:20:36 +04:00
hdr - > duration = ( ( jiffies - start_time ) * 1000 ) / HZ ;
2007-07-09 14:38:05 +04:00
return blk_complete_sghdr_rq ( rq , hdr , bio ) ;
2005-06-20 16:06:01 +04:00
out :
blk_put_request ( rq ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2006-03-22 19:52:04 +03:00
/**
* sg_scsi_ioctl - - handle deprecated SCSI_IOCTL_SEND_COMMAND ioctl
* @ file : file this ioctl operates on ( optional )
* @ q : request queue to send scsi commands down
* @ disk : gendisk to operate on ( option )
* @ sic : userspace structure describing the command to perform
*
* Send down the scsi command described by @ sic to the device below
* the request queue @ q . If @ file is non - NULL it ' s used to perform
* fine - grained permission checks that allow users to send down
* non - destructive SCSI commands . If the caller has a struct gendisk
* available it should be passed in as @ disk to allow the low level
* driver to use the information contained in it . A non - NULL @ disk
* is only allowed if the caller knows that the low level driver doesn ' t
* need it ( e . g . in the scsi subsystem ) .
*
* Notes :
* - This interface is deprecated - users should use the SG_IO
* interface instead , as this is a more flexible approach to
* performing SCSI commands on a device .
* - The SCSI command length is determined by examining the 1 st byte
* of the given command . There is no way to override this .
* - Data transfers are limited to PAGE_SIZE
* - The length ( x + y ) must be at least OMAX_SB_LEN bytes long to
* accommodate the sense buffer when an error occurs .
* The sense buffer is truncated to OMAX_SB_LEN ( 16 ) bytes so that
* old code will not be surprised .
* - If a Unix error occurs ( e . g . ENOMEM ) then the user will receive
* a negative return and the Unix error code in ' errno ' .
* If the SCSI command succeeds then 0 is returned .
* Positive numbers returned are the compacted SCSI error codes ( 4
* bytes in one int ) where the lowest byte is the SCSI status .
*/
2005-04-17 02:20:36 +04:00
# define OMAX_SB_LEN 16 /* For backward compatibility */
2006-03-22 19:52:04 +03:00
int sg_scsi_ioctl ( struct file * file , struct request_queue * q ,
struct gendisk * disk , struct scsi_ioctl_command __user * sic )
2005-04-17 02:20:36 +04:00
{
struct request * rq ;
int err ;
unsigned int in_len , out_len , bytes , opcode , cmdlen ;
char * buffer = NULL , sense [ SCSI_SENSE_BUFFERSIZE ] ;
2006-03-22 19:52:04 +03:00
if ( ! sic )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
/*
* get in an out lengths , verify they don ' t exceed a page worth of data
*/
if ( get_user ( in_len , & sic - > inlen ) )
return - EFAULT ;
if ( get_user ( out_len , & sic - > outlen ) )
return - EFAULT ;
if ( in_len > PAGE_SIZE | | out_len > PAGE_SIZE )
return - EINVAL ;
if ( get_user ( opcode , sic - > data ) )
return - EFAULT ;
bytes = max ( in_len , out_len ) ;
if ( bytes ) {
buffer = kmalloc ( bytes , q - > bounce_gfp | GFP_USER | __GFP_NOWARN ) ;
if ( ! buffer )
return - ENOMEM ;
memset ( buffer , 0 , bytes ) ;
}
rq = blk_get_request ( q , in_len ? WRITE : READ , __GFP_WAIT ) ;
cmdlen = COMMAND_SIZE ( opcode ) ;
/*
* get command and data to send to device , if any
*/
err = - EFAULT ;
rq - > cmd_len = cmdlen ;
if ( copy_from_user ( rq - > cmd , sic - > data , cmdlen ) )
goto error ;
2006-03-22 19:52:04 +03:00
if ( in_len & & copy_from_user ( buffer , sic - > data + cmdlen , in_len ) )
2005-04-17 02:20:36 +04:00
goto error ;
2006-12-20 13:18:54 +03:00
err = blk_verify_command ( rq - > cmd , file - > f_mode & FMODE_WRITE ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto error ;
2006-03-22 19:52:04 +03:00
/* default. possible overriden later */
rq - > retries = 5 ;
2005-04-17 02:20:36 +04:00
switch ( opcode ) {
2006-03-22 19:52:04 +03:00
case SEND_DIAGNOSTIC :
case FORMAT_UNIT :
rq - > timeout = FORMAT_UNIT_TIMEOUT ;
rq - > retries = 1 ;
break ;
case START_STOP :
rq - > timeout = START_STOP_TIMEOUT ;
break ;
case MOVE_MEDIUM :
rq - > timeout = MOVE_MEDIUM_TIMEOUT ;
break ;
case READ_ELEMENT_STATUS :
rq - > timeout = READ_ELEMENT_STATUS_TIMEOUT ;
break ;
case READ_DEFECT_DATA :
rq - > timeout = READ_DEFECT_DATA_TIMEOUT ;
rq - > retries = 1 ;
break ;
default :
2007-07-09 14:38:05 +04:00
rq - > timeout = BLK_DEFAULT_SG_TIMEOUT ;
2006-03-22 19:52:04 +03:00
break ;
}
if ( bytes & & blk_rq_map_kern ( q , rq , buffer , bytes , __GFP_WAIT ) ) {
err = DRIVER_ERROR < < 24 ;
goto out ;
2005-04-17 02:20:36 +04:00
}
memset ( sense , 0 , sizeof ( sense ) ) ;
rq - > sense = sense ;
rq - > sense_len = 0 ;
2006-08-10 10:44:47 +04:00
rq - > cmd_type = REQ_TYPE_BLOCK_PC ;
2005-04-17 02:20:36 +04:00
2006-03-22 19:52:04 +03:00
blk_execute_rq ( q , disk , rq , 0 ) ;
out :
2005-04-17 02:20:36 +04:00
err = rq - > errors & 0xff ; /* only 8 bit SCSI status */
if ( err ) {
if ( rq - > sense_len & & rq - > sense ) {
bytes = ( OMAX_SB_LEN > rq - > sense_len ) ?
rq - > sense_len : OMAX_SB_LEN ;
if ( copy_to_user ( sic - > data , rq - > sense , bytes ) )
err = - EFAULT ;
}
} else {
if ( copy_to_user ( sic - > data , buffer , out_len ) )
err = - EFAULT ;
}
error :
kfree ( buffer ) ;
blk_put_request ( rq ) ;
return err ;
}
2006-03-22 19:52:04 +03:00
EXPORT_SYMBOL_GPL ( sg_scsi_ioctl ) ;
2005-12-19 22:49:24 +03:00
/* Send basic block requests */
static int __blk_send_generic ( request_queue_t * q , struct gendisk * bd_disk , int cmd , int data )
{
struct request * rq ;
int err ;
rq = blk_get_request ( q , WRITE , __GFP_WAIT ) ;
2006-08-10 10:44:47 +04:00
rq - > cmd_type = REQ_TYPE_BLOCK_PC ;
2005-12-19 22:49:24 +03:00
rq - > data = NULL ;
rq - > data_len = 0 ;
2007-07-09 14:38:05 +04:00
rq - > timeout = BLK_DEFAULT_SG_TIMEOUT ;
2005-12-19 22:49:24 +03:00
memset ( rq - > cmd , 0 , sizeof ( rq - > cmd ) ) ;
rq - > cmd [ 0 ] = cmd ;
rq - > cmd [ 4 ] = data ;
rq - > cmd_len = 6 ;
err = blk_execute_rq ( q , bd_disk , rq , 0 ) ;
blk_put_request ( rq ) ;
return err ;
}
static inline int blk_send_start_stop ( request_queue_t * q , struct gendisk * bd_disk , int data )
{
return __blk_send_generic ( q , bd_disk , GPCMD_START_STOP_UNIT , data ) ;
}
2005-04-17 02:20:36 +04:00
int scsi_cmd_ioctl ( struct file * file , struct gendisk * bd_disk , unsigned int cmd , void __user * arg )
{
request_queue_t * q ;
2005-12-19 22:49:24 +03:00
int err ;
2005-04-17 02:20:36 +04:00
q = bd_disk - > queue ;
if ( ! q )
return - ENXIO ;
if ( blk_get_queue ( q ) )
return - ENXIO ;
switch ( cmd ) {
/*
* new sgv3 interface
*/
case SG_GET_VERSION_NUM :
err = sg_get_version ( arg ) ;
break ;
case SCSI_IOCTL_GET_IDLUN :
err = scsi_get_idlun ( q , arg ) ;
break ;
case SCSI_IOCTL_GET_BUS_NUMBER :
err = scsi_get_bus ( q , arg ) ;
break ;
case SG_SET_TIMEOUT :
err = sg_set_timeout ( q , arg ) ;
break ;
case SG_GET_TIMEOUT :
err = sg_get_timeout ( q ) ;
break ;
case SG_GET_RESERVED_SIZE :
err = sg_get_reserved_size ( q , arg ) ;
break ;
case SG_SET_RESERVED_SIZE :
err = sg_set_reserved_size ( q , arg ) ;
break ;
case SG_EMULATED_HOST :
err = sg_emulated_host ( q , arg ) ;
break ;
case SG_IO : {
struct sg_io_hdr hdr ;
err = - EFAULT ;
if ( copy_from_user ( & hdr , arg , sizeof ( hdr ) ) )
break ;
err = sg_io ( file , q , bd_disk , & hdr ) ;
if ( err = = - EFAULT )
break ;
if ( copy_to_user ( arg , & hdr , sizeof ( hdr ) ) )
err = - EFAULT ;
break ;
}
case CDROM_SEND_PACKET : {
struct cdrom_generic_command cgc ;
struct sg_io_hdr hdr ;
err = - EFAULT ;
if ( copy_from_user ( & cgc , arg , sizeof ( cgc ) ) )
break ;
cgc . timeout = clock_t_to_jiffies ( cgc . timeout ) ;
memset ( & hdr , 0 , sizeof ( hdr ) ) ;
hdr . interface_id = ' S ' ;
hdr . cmd_len = sizeof ( cgc . cmd ) ;
hdr . dxfer_len = cgc . buflen ;
err = 0 ;
switch ( cgc . data_direction ) {
case CGC_DATA_UNKNOWN :
hdr . dxfer_direction = SG_DXFER_UNKNOWN ;
break ;
case CGC_DATA_WRITE :
hdr . dxfer_direction = SG_DXFER_TO_DEV ;
break ;
case CGC_DATA_READ :
hdr . dxfer_direction = SG_DXFER_FROM_DEV ;
break ;
case CGC_DATA_NONE :
hdr . dxfer_direction = SG_DXFER_NONE ;
break ;
default :
err = - EINVAL ;
}
if ( err )
break ;
hdr . dxferp = cgc . buffer ;
hdr . sbp = cgc . sense ;
if ( hdr . sbp )
hdr . mx_sb_len = sizeof ( struct request_sense ) ;
hdr . timeout = cgc . timeout ;
hdr . cmdp = ( ( struct cdrom_generic_command __user * ) arg ) - > cmd ;
hdr . cmd_len = sizeof ( cgc . cmd ) ;
err = sg_io ( file , q , bd_disk , & hdr ) ;
if ( err = = - EFAULT )
break ;
if ( hdr . status )
err = - EIO ;
cgc . stat = err ;
cgc . buflen = hdr . resid ;
if ( copy_to_user ( arg , & cgc , sizeof ( cgc ) ) )
err = - EFAULT ;
break ;
}
/*
* old junk scsi send command ioctl
*/
case SCSI_IOCTL_SEND_COMMAND :
printk ( KERN_WARNING " program %s is using a deprecated SCSI ioctl, please convert it to SG_IO \n " , current - > comm ) ;
err = - EINVAL ;
if ( ! arg )
break ;
err = sg_scsi_ioctl ( file , q , bd_disk , arg ) ;
break ;
case CDROMCLOSETRAY :
2005-12-19 22:49:24 +03:00
err = blk_send_start_stop ( q , bd_disk , 0x03 ) ;
break ;
2005-04-17 02:20:36 +04:00
case CDROMEJECT :
2005-12-19 22:49:24 +03:00
err = blk_send_start_stop ( q , bd_disk , 0x02 ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
err = - ENOTTY ;
}
blk_put_queue ( q ) ;
return err ;
}
EXPORT_SYMBOL ( scsi_cmd_ioctl ) ;