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. */
2008-04-30 12:27:26 +04:00
const unsigned char scsi_command_size_tbl [ 8 ] =
2005-04-17 02:20:36 +04:00
{
6 , 10 , 10 , 12 ,
16 , 12 , 10 , 10
} ;
2008-04-30 12:27:26 +04:00
EXPORT_SYMBOL ( scsi_command_size_tbl ) ;
2005-04-17 02:20:36 +04:00
# 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 ) ;
}
2007-07-24 11:28:11 +04:00
static int scsi_get_idlun ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
return put_user ( 0 , p ) ;
}
2007-07-24 11:28:11 +04:00
static int scsi_get_bus ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
return put_user ( 0 , p ) ;
}
2007-07-24 11:28:11 +04:00
static int sg_get_timeout ( struct request_queue * q )
2005-04-17 02:20:36 +04:00
{
return q - > sg_timeout / ( HZ / USER_HZ ) ;
}
2007-07-24 11:28:11 +04:00
static int sg_set_timeout ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
int timeout , err = get_user ( timeout , p ) ;
if ( ! err )
q - > sg_timeout = timeout * ( HZ / USER_HZ ) ;
return err ;
}
2007-07-24 11:28:11 +04:00
static int sg_get_reserved_size ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
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
}
2007-07-24 11:28:11 +04:00
static int sg_set_reserved_size ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
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 ? ? )
*/
2007-07-24 11:28:11 +04:00
static int sg_emulated_host ( struct request_queue * q , int __user * p )
2005-04-17 02:20:36 +04:00
{
return put_user ( 1 , p ) ;
}
2007-07-24 11:28:11 +04:00
static int blk_fill_sghdr_rq ( struct request_queue * q , struct request * rq ,
2008-06-26 15:48:27 +04:00
struct sg_io_hdr * hdr , struct file * file )
2007-07-09 14:38:05 +04:00
{
if ( copy_from_user ( rq - > cmd , hdr - > cmdp , hdr - > cmd_len ) )
return - EFAULT ;
2008-06-26 15:48:27 +04:00
if ( blk_verify_command ( file , rq - > cmd ) )
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 ;
2007-12-05 23:28:24 +03:00
rq - > timeout = msecs_to_jiffies ( hdr - > timeout ) ;
2007-07-09 14:38:05 +04:00
if ( ! rq - > timeout )
rq - > timeout = q - > sg_timeout ;
if ( ! rq - > timeout )
rq - > timeout = BLK_DEFAULT_SG_TIMEOUT ;
return 0 ;
}
/*
* unmap a request that was previously mapped to this sg_io_hdr . handles
* both sg and non - sg sg_io_hdr .
*/
2007-07-22 05:06:50 +04:00
static int blk_unmap_sghdr_rq ( struct request * rq , struct sg_io_hdr * hdr )
2007-07-09 14:38:05 +04:00
{
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 ;
}
2007-07-22 05:06:50 +04:00
static int blk_complete_sghdr_rq ( struct request * rq , struct sg_io_hdr * hdr ,
struct bio * bio )
2007-07-09 14:38:05 +04:00
{
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 ;
2008-03-04 13:17:11 +03:00
hdr - > resid = rq - > data_len ;
2007-07-09 14:38:05 +04:00
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 ;
}
2007-07-24 11:28:11 +04:00
static int sg_io ( struct file * file , struct request_queue * q ,
2005-04-17 02:20:36 +04:00
struct gendisk * bd_disk , struct sg_io_hdr * hdr )
{
2007-07-09 14:38:05 +04:00
unsigned long start_time ;
2008-06-26 15:48:27 +04:00
int writing = 0 , ret = 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
2008-06-26 15:48:27 +04:00
if ( blk_fill_sghdr_rq ( q , rq , hdr , file ) ) {
2007-07-09 14:38:05 +04:00
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
2007-12-05 23:28:24 +03:00
hdr - > duration = jiffies_to_msecs ( jiffies - start_time ) ;
2005-04-17 02:20:36 +04:00
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 ) {
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 12:49:03 +04:00
buffer = kzalloc ( bytes , q - > bounce_gfp | GFP_USER | __GFP_NOWARN ) ;
2005-04-17 02:20:36 +04:00
if ( ! buffer )
return - ENOMEM ;
}
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 ;
2008-06-26 15:48:27 +04:00
err = blk_verify_command ( file , rq - > cmd ) ;
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 */
2007-07-24 11:28:11 +04:00
static int __blk_send_generic ( struct request_queue * q , struct gendisk * bd_disk ,
int cmd , int data )
2005-12-19 22:49:24 +03:00
{
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 ;
2008-03-04 13:17:11 +03:00
rq - > extra_len = 0 ;
2007-07-09 14:38:05 +04:00
rq - > timeout = BLK_DEFAULT_SG_TIMEOUT ;
2005-12-19 22:49:24 +03:00
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 ;
}
2007-07-24 11:28:11 +04:00
static inline int blk_send_start_stop ( struct request_queue * q ,
struct gendisk * bd_disk , int data )
2005-12-19 22:49:24 +03:00
{
return __blk_send_generic ( q , bd_disk , GPCMD_START_STOP_UNIT , data ) ;
}
2007-07-09 14:39:20 +04:00
int scsi_cmd_ioctl ( struct file * file , struct request_queue * q ,
struct gendisk * bd_disk , unsigned int cmd , void __user * arg )
2005-04-17 02:20:36 +04:00
{
2005-12-19 22:49:24 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2007-07-09 14:39:20 +04:00
if ( ! q | | blk_get_queue ( q ) )
2005-04-17 02:20:36 +04:00
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 ) ;