2010-10-07 13:05:23 +02:00
/*
* USB Attached SCSI
* Note that this is not the same as the USB Mass Storage driver
*
* Copyright Matthew Wilcox for Intel Corp , 2010
* Copyright Sarah Sharp for Intel Corp , 2010
*
* Distributed under the terms of the GNU GPL , version two .
*/
# include <linux/blkdev.h>
# include <linux/slab.h>
# include <linux/types.h>
2011-07-03 16:09:31 -04:00
# include <linux/module.h>
2010-10-07 13:05:23 +02:00
# include <linux/usb.h>
2012-01-11 12:42:32 +01:00
# include <linux/usb/hcd.h>
2010-10-07 13:05:23 +02:00
# include <linux/usb/storage.h>
2012-01-11 12:45:56 +01:00
# include <linux/usb/uas.h>
2010-10-07 13:05:23 +02:00
# include <scsi/scsi.h>
# include <scsi/scsi_dbg.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_tcq.h>
/*
* The r00 - r01c specs define this version of the SENSE IU data structure .
* It ' s still in use by several different firmware releases .
*/
struct sense_iu_old {
__u8 iu_id ;
__u8 rsvd1 ;
__be16 tag ;
__be16 len ;
__u8 status ;
__u8 service_response ;
__u8 sense [ SCSI_SENSE_BUFFERSIZE ] ;
} ;
struct uas_dev_info {
struct usb_interface * intf ;
struct usb_device * udev ;
2012-09-25 10:47:04 +02:00
struct usb_anchor cmd_urbs ;
2012-06-19 09:54:53 +02:00
struct usb_anchor sense_urbs ;
struct usb_anchor data_urbs ;
2012-06-19 09:54:54 +02:00
int qdepth , resetting ;
struct response_ui response ;
2010-10-07 13:05:23 +02:00
unsigned cmd_pipe , status_pipe , data_in_pipe , data_out_pipe ;
unsigned use_streams : 1 ;
unsigned uas_sense_old : 1 ;
2011-12-19 20:22:39 +01:00
struct scsi_cmnd * cmnd ;
2012-09-25 10:47:08 +02:00
spinlock_t lock ;
2010-10-07 13:05:23 +02:00
} ;
enum {
2010-12-15 15:44:04 -05:00
SUBMIT_STATUS_URB = ( 1 < < 1 ) ,
2010-10-07 13:05:23 +02:00
ALLOC_DATA_IN_URB = ( 1 < < 2 ) ,
SUBMIT_DATA_IN_URB = ( 1 < < 3 ) ,
ALLOC_DATA_OUT_URB = ( 1 < < 4 ) ,
SUBMIT_DATA_OUT_URB = ( 1 < < 5 ) ,
ALLOC_CMD_URB = ( 1 < < 6 ) ,
SUBMIT_CMD_URB = ( 1 < < 7 ) ,
2012-06-19 09:54:51 +02:00
COMMAND_INFLIGHT = ( 1 < < 8 ) ,
DATA_IN_URB_INFLIGHT = ( 1 < < 9 ) ,
DATA_OUT_URB_INFLIGHT = ( 1 < < 10 ) ,
COMMAND_COMPLETED = ( 1 < < 11 ) ,
2012-09-25 10:47:06 +02:00
COMMAND_ABORTED = ( 1 < < 12 ) ,
2012-11-30 11:54:41 +01:00
UNLINK_DATA_URBS = ( 1 < < 13 ) ,
2012-11-30 11:54:42 +01:00
IS_IN_WORK_LIST = ( 1 < < 14 ) ,
2010-10-07 13:05:23 +02:00
} ;
/* Overrides scsi_pointer */
struct uas_cmd_info {
unsigned int state ;
unsigned int stream ;
struct urb * cmd_urb ;
struct urb * data_in_urb ;
struct urb * data_out_urb ;
struct list_head list ;
} ;
/* I hate forward declarations, but I actually have a loop */
static int uas_submit_urbs ( struct scsi_cmnd * cmnd ,
struct uas_dev_info * devinfo , gfp_t gfp ) ;
2011-12-02 11:55:44 -08:00
static void uas_do_work ( struct work_struct * work ) ;
2012-11-30 11:54:44 +01:00
static int uas_try_complete ( struct scsi_cmnd * cmnd , const char * caller ) ;
2010-10-07 13:05:23 +02:00
2011-12-02 11:55:44 -08:00
static DECLARE_WORK ( uas_work , uas_do_work ) ;
2010-10-07 13:05:23 +02:00
static DEFINE_SPINLOCK ( uas_work_lock ) ;
static LIST_HEAD ( uas_work_list ) ;
2012-11-30 11:54:40 +01:00
static void uas_unlink_data_urbs ( struct uas_dev_info * devinfo ,
struct uas_cmd_info * cmdinfo )
{
2012-11-30 11:54:41 +01:00
unsigned long flags ;
/*
* The UNLINK_DATA_URBS flag makes sure uas_try_complete
* ( called by urb completion ) doesn ' t release cmdinfo
* underneath us .
*/
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
cmdinfo - > state | = UNLINK_DATA_URBS ;
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2012-11-30 11:54:40 +01:00
if ( cmdinfo - > data_in_urb )
usb_unlink_urb ( cmdinfo - > data_in_urb ) ;
if ( cmdinfo - > data_out_urb )
usb_unlink_urb ( cmdinfo - > data_out_urb ) ;
2012-11-30 11:54:41 +01:00
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
cmdinfo - > state & = ~ UNLINK_DATA_URBS ;
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2012-11-30 11:54:40 +01:00
}
2010-10-07 13:05:23 +02:00
static void uas_do_work ( struct work_struct * work )
{
struct uas_cmd_info * cmdinfo ;
2011-12-02 11:55:44 -08:00
struct uas_cmd_info * temp ;
2010-10-07 13:05:23 +02:00
struct list_head list ;
2012-09-25 10:47:08 +02:00
unsigned long flags ;
2011-12-02 11:55:44 -08:00
int err ;
2010-10-07 13:05:23 +02:00
spin_lock_irq ( & uas_work_lock ) ;
list_replace_init ( & uas_work_list , & list ) ;
spin_unlock_irq ( & uas_work_lock ) ;
2011-12-02 11:55:44 -08:00
list_for_each_entry_safe ( cmdinfo , temp , & list , list ) {
2010-10-07 13:05:23 +02:00
struct scsi_pointer * scp = ( void * ) cmdinfo ;
struct scsi_cmnd * cmnd = container_of ( scp ,
struct scsi_cmnd , SCp ) ;
2012-09-25 10:47:08 +02:00
struct uas_dev_info * devinfo = ( void * ) cmnd - > device - > hostdata ;
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
err = uas_submit_urbs ( cmnd , cmnd - > device - > hostdata , GFP_ATOMIC ) ;
2012-11-30 11:54:42 +01:00
if ( ! err )
cmdinfo - > state & = ~ IS_IN_WORK_LIST ;
2012-09-25 10:47:08 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2011-12-02 11:55:44 -08:00
if ( err ) {
list_del ( & cmdinfo - > list ) ;
spin_lock_irq ( & uas_work_lock ) ;
list_add_tail ( & cmdinfo - > list , & uas_work_list ) ;
spin_unlock_irq ( & uas_work_lock ) ;
schedule_work ( & uas_work ) ;
}
2010-10-07 13:05:23 +02:00
}
}
2012-11-30 11:54:44 +01:00
static void uas_abort_work ( struct uas_dev_info * devinfo )
{
struct uas_cmd_info * cmdinfo ;
struct uas_cmd_info * temp ;
struct list_head list ;
unsigned long flags ;
spin_lock_irq ( & uas_work_lock ) ;
list_replace_init ( & uas_work_list , & list ) ;
spin_unlock_irq ( & uas_work_lock ) ;
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
list_for_each_entry_safe ( cmdinfo , temp , & list , list ) {
struct scsi_pointer * scp = ( void * ) cmdinfo ;
struct scsi_cmnd * cmnd = container_of ( scp ,
struct scsi_cmnd , SCp ) ;
struct uas_dev_info * di = ( void * ) cmnd - > device - > hostdata ;
if ( di = = devinfo ) {
cmdinfo - > state | = COMMAND_ABORTED ;
cmdinfo - > state & = ~ IS_IN_WORK_LIST ;
if ( devinfo - > resetting ) {
/* uas_stat_cmplt() will not do that
* when a device reset is in
* progress */
cmdinfo - > state & = ~ COMMAND_INFLIGHT ;
}
uas_try_complete ( cmnd , __func__ ) ;
} else {
/* not our uas device, relink into list */
list_del ( & cmdinfo - > list ) ;
spin_lock_irq ( & uas_work_lock ) ;
list_add_tail ( & cmdinfo - > list , & uas_work_list ) ;
spin_unlock_irq ( & uas_work_lock ) ;
}
}
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
}
2010-10-07 13:05:23 +02:00
static void uas_sense ( struct urb * urb , struct scsi_cmnd * cmnd )
{
struct sense_iu * sense_iu = urb - > transfer_buffer ;
struct scsi_device * sdev = cmnd - > device ;
if ( urb - > actual_length > 16 ) {
unsigned len = be16_to_cpup ( & sense_iu - > len ) ;
if ( len + 16 ! = urb - > actual_length ) {
int newlen = min ( len + 16 , urb - > actual_length ) - 16 ;
if ( newlen < 0 )
newlen = 0 ;
sdev_printk ( KERN_INFO , sdev , " %s: urb length %d "
" disagrees with IU sense data length %d, "
" using %d bytes of sense data \n " , __func__ ,
urb - > actual_length , len , newlen ) ;
len = newlen ;
}
memcpy ( cmnd - > sense_buffer , sense_iu - > sense , len ) ;
}
cmnd - > result = sense_iu - > status ;
}
static void uas_sense_old ( struct urb * urb , struct scsi_cmnd * cmnd )
{
struct sense_iu_old * sense_iu = urb - > transfer_buffer ;
struct scsi_device * sdev = cmnd - > device ;
if ( urb - > actual_length > 8 ) {
unsigned len = be16_to_cpup ( & sense_iu - > len ) - 2 ;
if ( len + 8 ! = urb - > actual_length ) {
int newlen = min ( len + 8 , urb - > actual_length ) - 8 ;
if ( newlen < 0 )
newlen = 0 ;
sdev_printk ( KERN_INFO , sdev , " %s: urb length %d "
" disagrees with IU sense data length %d, "
" using %d bytes of sense data \n " , __func__ ,
urb - > actual_length , len , newlen ) ;
len = newlen ;
}
memcpy ( cmnd - > sense_buffer , sense_iu - > sense , len ) ;
}
cmnd - > result = sense_iu - > status ;
2012-06-19 09:54:51 +02:00
}
static void uas_log_cmd_state ( struct scsi_cmnd * cmnd , const char * caller )
{
struct uas_cmd_info * ci = ( void * ) & cmnd - > SCp ;
scmd_printk ( KERN_INFO , cmnd , " %s %p tag %d, inflight: "
2012-11-30 11:54:42 +01:00
" %s%s%s%s%s%s%s%s%s%s%s%s%s%s \n " ,
2012-06-19 09:54:51 +02:00
caller , cmnd , cmnd - > request - > tag ,
( ci - > state & SUBMIT_STATUS_URB ) ? " s-st " : " " ,
( ci - > state & ALLOC_DATA_IN_URB ) ? " a-in " : " " ,
( ci - > state & SUBMIT_DATA_IN_URB ) ? " s-in " : " " ,
( ci - > state & ALLOC_DATA_OUT_URB ) ? " a-out " : " " ,
( ci - > state & SUBMIT_DATA_OUT_URB ) ? " s-out " : " " ,
( ci - > state & ALLOC_CMD_URB ) ? " a-cmd " : " " ,
( ci - > state & SUBMIT_CMD_URB ) ? " s-cmd " : " " ,
( ci - > state & COMMAND_INFLIGHT ) ? " CMD " : " " ,
( ci - > state & DATA_IN_URB_INFLIGHT ) ? " IN " : " " ,
( ci - > state & DATA_OUT_URB_INFLIGHT ) ? " OUT " : " " ,
2012-09-25 10:47:06 +02:00
( ci - > state & COMMAND_COMPLETED ) ? " done " : " " ,
2012-11-30 11:54:41 +01:00
( ci - > state & COMMAND_ABORTED ) ? " abort " : " " ,
2012-11-30 11:54:42 +01:00
( ci - > state & UNLINK_DATA_URBS ) ? " unlink " : " " ,
( ci - > state & IS_IN_WORK_LIST ) ? " work " : " " ) ;
2012-06-19 09:54:51 +02:00
}
static int uas_try_complete ( struct scsi_cmnd * cmnd , const char * caller )
{
struct uas_cmd_info * cmdinfo = ( void * ) & cmnd - > SCp ;
2012-09-25 10:47:08 +02:00
struct uas_dev_info * devinfo = ( void * ) cmnd - > device - > hostdata ;
2012-06-19 09:54:51 +02:00
2012-09-25 10:47:08 +02:00
WARN_ON ( ! spin_is_locked ( & devinfo - > lock ) ) ;
2012-06-19 09:54:51 +02:00
if ( cmdinfo - > state & ( COMMAND_INFLIGHT |
DATA_IN_URB_INFLIGHT |
2012-11-30 11:54:41 +01:00
DATA_OUT_URB_INFLIGHT |
UNLINK_DATA_URBS ) )
2012-06-19 09:54:51 +02:00
return - EBUSY ;
BUG_ON ( cmdinfo - > state & COMMAND_COMPLETED ) ;
cmdinfo - > state | = COMMAND_COMPLETED ;
usb_free_urb ( cmdinfo - > data_in_urb ) ;
usb_free_urb ( cmdinfo - > data_out_urb ) ;
2012-09-25 10:47:07 +02:00
if ( cmdinfo - > state & COMMAND_ABORTED ) {
scmd_printk ( KERN_INFO , cmnd , " abort completed \n " ) ;
cmnd - > result = DID_ABORT < < 16 ;
}
2012-06-19 09:54:48 +02:00
cmnd - > scsi_done ( cmnd ) ;
2012-06-19 09:54:51 +02:00
return 0 ;
2010-10-07 13:05:23 +02:00
}
static void uas_xfer_data ( struct urb * urb , struct scsi_cmnd * cmnd ,
2012-06-19 09:54:51 +02:00
unsigned direction )
2010-10-07 13:05:23 +02:00
{
struct uas_cmd_info * cmdinfo = ( void * ) & cmnd - > SCp ;
int err ;
2012-06-19 09:54:51 +02:00
cmdinfo - > state | = direction | SUBMIT_STATUS_URB ;
2010-10-07 13:05:23 +02:00
err = uas_submit_urbs ( cmnd , cmnd - > device - > hostdata , GFP_ATOMIC ) ;
if ( err ) {
spin_lock ( & uas_work_lock ) ;
list_add_tail ( & cmdinfo - > list , & uas_work_list ) ;
2012-11-30 11:54:42 +01:00
cmdinfo - > state | = IS_IN_WORK_LIST ;
2010-10-07 13:05:23 +02:00
spin_unlock ( & uas_work_lock ) ;
schedule_work ( & uas_work ) ;
}
}
static void uas_stat_cmplt ( struct urb * urb )
{
struct iu * iu = urb - > transfer_buffer ;
2011-12-19 20:22:39 +01:00
struct Scsi_Host * shost = urb - > context ;
struct uas_dev_info * devinfo = ( void * ) shost - > hostdata [ 0 ] ;
2010-10-07 13:05:23 +02:00
struct scsi_cmnd * cmnd ;
2012-06-19 09:54:51 +02:00
struct uas_cmd_info * cmdinfo ;
2012-09-25 10:47:08 +02:00
unsigned long flags ;
2010-10-07 13:05:23 +02:00
u16 tag ;
if ( urb - > status ) {
dev_err ( & urb - > dev - > dev , " URB BAD STATUS %d \n " , urb - > status ) ;
2012-06-19 09:54:49 +02:00
usb_free_urb ( urb ) ;
2010-10-07 13:05:23 +02:00
return ;
}
2012-06-19 09:54:54 +02:00
if ( devinfo - > resetting ) {
usb_free_urb ( urb ) ;
return ;
}
2012-09-25 10:47:08 +02:00
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
2010-10-07 13:05:23 +02:00
tag = be16_to_cpup ( & iu - > tag ) - 1 ;
2011-12-19 20:22:39 +01:00
if ( tag = = 0 )
cmnd = devinfo - > cmnd ;
2010-10-07 13:05:23 +02:00
else
2011-12-19 20:22:39 +01:00
cmnd = scsi_host_find_tag ( shost , tag - 1 ) ;
2012-09-26 10:29:03 +02:00
2011-12-02 11:55:48 -08:00
if ( ! cmnd ) {
2012-09-26 10:29:03 +02:00
if ( iu - > iu_id = = IU_ID_RESPONSE ) {
/* store results for uas_eh_task_mgmt() */
memcpy ( & devinfo - > response , iu , sizeof ( devinfo - > response ) ) ;
2012-06-19 09:54:54 +02:00
}
2012-09-26 10:29:03 +02:00
usb_free_urb ( urb ) ;
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
return ;
2011-12-02 11:55:48 -08:00
}
2010-10-07 13:05:23 +02:00
2012-09-26 10:29:03 +02:00
cmdinfo = ( void * ) & cmnd - > SCp ;
2010-10-07 13:05:23 +02:00
switch ( iu - > iu_id ) {
case IU_ID_STATUS :
2011-12-19 20:22:39 +01:00
if ( devinfo - > cmnd = = cmnd )
devinfo - > cmnd = NULL ;
2010-10-07 13:05:23 +02:00
if ( urb - > actual_length < 16 )
devinfo - > uas_sense_old = 1 ;
if ( devinfo - > uas_sense_old )
uas_sense_old ( urb , cmnd ) ;
else
uas_sense ( urb , cmnd ) ;
2012-06-19 09:54:52 +02:00
if ( cmnd - > result ! = 0 ) {
/* cancel data transfers on error */
2012-11-30 11:54:40 +01:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
uas_unlink_data_urbs ( devinfo , cmdinfo ) ;
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
2012-06-19 09:54:52 +02:00
}
2012-06-19 09:54:51 +02:00
cmdinfo - > state & = ~ COMMAND_INFLIGHT ;
uas_try_complete ( cmnd , __func__ ) ;
2010-10-07 13:05:23 +02:00
break ;
case IU_ID_READ_READY :
uas_xfer_data ( urb , cmnd , SUBMIT_DATA_IN_URB ) ;
break ;
case IU_ID_WRITE_READY :
uas_xfer_data ( urb , cmnd , SUBMIT_DATA_OUT_URB ) ;
break ;
default :
scmd_printk ( KERN_ERR , cmnd ,
" Bogus IU (%d) received on status pipe \n " , iu - > iu_id ) ;
}
2012-06-19 09:54:50 +02:00
usb_free_urb ( urb ) ;
2012-09-25 10:47:08 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2010-10-07 13:05:23 +02:00
}
2012-06-19 09:54:48 +02:00
static void uas_data_cmplt ( struct urb * urb )
2010-10-07 13:05:23 +02:00
{
2012-06-19 09:54:51 +02:00
struct scsi_cmnd * cmnd = urb - > context ;
struct uas_cmd_info * cmdinfo = ( void * ) & cmnd - > SCp ;
2012-09-25 10:47:08 +02:00
struct uas_dev_info * devinfo = ( void * ) cmnd - > device - > hostdata ;
2012-06-19 09:54:51 +02:00
struct scsi_data_buffer * sdb = NULL ;
2012-09-25 10:47:08 +02:00
unsigned long flags ;
2012-06-19 09:54:51 +02:00
2012-09-25 10:47:08 +02:00
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
2012-06-19 09:54:51 +02:00
if ( cmdinfo - > data_in_urb = = urb ) {
sdb = scsi_in ( cmnd ) ;
cmdinfo - > state & = ~ DATA_IN_URB_INFLIGHT ;
} else if ( cmdinfo - > data_out_urb = = urb ) {
sdb = scsi_out ( cmnd ) ;
cmdinfo - > state & = ~ DATA_OUT_URB_INFLIGHT ;
}
BUG_ON ( sdb = = NULL ) ;
2012-06-19 09:54:52 +02:00
if ( urb - > status ) {
/* error: no data transfered */
sdb - > resid = sdb - > length ;
} else {
sdb - > resid = sdb - > length - urb - > actual_length ;
}
2012-06-19 09:54:51 +02:00
uas_try_complete ( cmnd , __func__ ) ;
2012-09-25 10:47:08 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2010-10-07 13:05:23 +02:00
}
static struct urb * uas_alloc_data_urb ( struct uas_dev_info * devinfo , gfp_t gfp ,
2012-06-19 09:54:51 +02:00
unsigned int pipe , u16 stream_id ,
struct scsi_cmnd * cmnd ,
enum dma_data_direction dir )
2010-10-07 13:05:23 +02:00
{
struct usb_device * udev = devinfo - > udev ;
struct urb * urb = usb_alloc_urb ( 0 , gfp ) ;
2012-06-19 09:54:51 +02:00
struct scsi_data_buffer * sdb = ( dir = = DMA_FROM_DEVICE )
? scsi_in ( cmnd ) : scsi_out ( cmnd ) ;
2010-10-07 13:05:23 +02:00
if ( ! urb )
goto out ;
2012-06-19 09:54:51 +02:00
usb_fill_bulk_urb ( urb , udev , pipe , NULL , sdb - > length ,
uas_data_cmplt , cmnd ) ;
2012-06-19 09:54:48 +02:00
if ( devinfo - > use_streams )
urb - > stream_id = stream_id ;
2010-10-07 13:05:23 +02:00
urb - > num_sgs = udev - > bus - > sg_tablesize ? sdb - > table . nents : 0 ;
urb - > sg = sdb - > table . sgl ;
out :
return urb ;
}
static struct urb * uas_alloc_sense_urb ( struct uas_dev_info * devinfo , gfp_t gfp ,
2012-06-19 09:54:50 +02:00
struct Scsi_Host * shost , u16 stream_id )
2010-10-07 13:05:23 +02:00
{
struct usb_device * udev = devinfo - > udev ;
struct urb * urb = usb_alloc_urb ( 0 , gfp ) ;
struct sense_iu * iu ;
if ( ! urb )
goto out ;
2010-12-15 15:44:03 -05:00
iu = kzalloc ( sizeof ( * iu ) , gfp ) ;
2010-10-07 13:05:23 +02:00
if ( ! iu )
goto free ;
usb_fill_bulk_urb ( urb , udev , devinfo - > status_pipe , iu , sizeof ( * iu ) ,
2012-06-19 09:54:50 +02:00
uas_stat_cmplt , shost ) ;
2010-10-07 13:05:23 +02:00
urb - > stream_id = stream_id ;
urb - > transfer_flags | = URB_FREE_BUFFER ;
out :
return urb ;
free :
usb_free_urb ( urb ) ;
return NULL ;
}
static struct urb * uas_alloc_cmd_urb ( struct uas_dev_info * devinfo , gfp_t gfp ,
struct scsi_cmnd * cmnd , u16 stream_id )
{
struct usb_device * udev = devinfo - > udev ;
struct scsi_device * sdev = cmnd - > device ;
struct urb * urb = usb_alloc_urb ( 0 , gfp ) ;
struct command_iu * iu ;
int len ;
if ( ! urb )
goto out ;
len = cmnd - > cmd_len - 16 ;
if ( len < 0 )
len = 0 ;
len = ALIGN ( len , 4 ) ;
2010-12-15 15:44:03 -05:00
iu = kzalloc ( sizeof ( * iu ) + len , gfp ) ;
2010-10-07 13:05:23 +02:00
if ( ! iu )
goto free ;
iu - > iu_id = IU_ID_COMMAND ;
2011-12-02 11:55:46 -08:00
if ( blk_rq_tagged ( cmnd - > request ) )
2011-12-19 20:22:39 +01:00
iu - > tag = cpu_to_be16 ( cmnd - > request - > tag + 2 ) ;
2011-12-02 11:55:46 -08:00
else
iu - > tag = cpu_to_be16 ( 1 ) ;
2010-11-10 14:54:09 +01:00
iu - > prio_attr = UAS_SIMPLE_TAG ;
2010-10-07 13:05:23 +02:00
iu - > len = len ;
int_to_scsilun ( sdev - > lun , & iu - > lun ) ;
memcpy ( iu - > cdb , cmnd - > cmnd , cmnd - > cmd_len ) ;
usb_fill_bulk_urb ( urb , udev , devinfo - > cmd_pipe , iu , sizeof ( * iu ) + len ,
usb_free_urb , NULL ) ;
urb - > transfer_flags | = URB_FREE_BUFFER ;
out :
return urb ;
free :
usb_free_urb ( urb ) ;
return NULL ;
}
2012-06-19 09:54:54 +02:00
static int uas_submit_task_urb ( struct scsi_cmnd * cmnd , gfp_t gfp ,
u8 function , u16 stream_id )
{
struct uas_dev_info * devinfo = ( void * ) cmnd - > device - > hostdata ;
struct usb_device * udev = devinfo - > udev ;
struct urb * urb = usb_alloc_urb ( 0 , gfp ) ;
struct task_mgmt_iu * iu ;
int err = - ENOMEM ;
if ( ! urb )
goto err ;
iu = kzalloc ( sizeof ( * iu ) , gfp ) ;
if ( ! iu )
goto err ;
iu - > iu_id = IU_ID_TASK_MGMT ;
iu - > tag = cpu_to_be16 ( stream_id ) ;
int_to_scsilun ( cmnd - > device - > lun , & iu - > lun ) ;
iu - > function = function ;
switch ( function ) {
case TMF_ABORT_TASK :
if ( blk_rq_tagged ( cmnd - > request ) )
iu - > task_tag = cpu_to_be16 ( cmnd - > request - > tag + 2 ) ;
else
iu - > task_tag = cpu_to_be16 ( 1 ) ;
break ;
}
usb_fill_bulk_urb ( urb , udev , devinfo - > cmd_pipe , iu , sizeof ( * iu ) ,
usb_free_urb , NULL ) ;
urb - > transfer_flags | = URB_FREE_BUFFER ;
err = usb_submit_urb ( urb , gfp ) ;
if ( err )
goto err ;
2012-09-25 10:47:04 +02:00
usb_anchor_urb ( urb , & devinfo - > cmd_urbs ) ;
2012-06-19 09:54:54 +02:00
return 0 ;
err :
usb_free_urb ( urb ) ;
return err ;
}
2010-10-07 13:05:23 +02:00
/*
* Why should I request the Status IU before sending the Command IU ? Spec
* says to , but also says the device may receive them in any order . Seems
* daft to me .
*/
2012-06-19 09:54:50 +02:00
static int uas_submit_sense_urb ( struct Scsi_Host * shost ,
gfp_t gfp , unsigned int stream )
2010-10-07 13:05:23 +02:00
{
2012-06-19 09:54:50 +02:00
struct uas_dev_info * devinfo = ( void * ) shost - > hostdata [ 0 ] ;
struct urb * urb ;
2010-10-07 13:05:23 +02:00
2012-06-19 09:54:50 +02:00
urb = uas_alloc_sense_urb ( devinfo , gfp , shost , stream ) ;
if ( ! urb )
return SCSI_MLQUEUE_DEVICE_BUSY ;
if ( usb_submit_urb ( urb , gfp ) ) {
shost_printk ( KERN_INFO , shost ,
" sense urb submission failure \n " ) ;
usb_free_urb ( urb ) ;
return SCSI_MLQUEUE_DEVICE_BUSY ;
2010-10-07 13:05:23 +02:00
}
2012-06-19 09:54:53 +02:00
usb_anchor_urb ( urb , & devinfo - > sense_urbs ) ;
2012-06-19 09:54:50 +02:00
return 0 ;
}
static int uas_submit_urbs ( struct scsi_cmnd * cmnd ,
struct uas_dev_info * devinfo , gfp_t gfp )
{
struct uas_cmd_info * cmdinfo = ( void * ) & cmnd - > SCp ;
int err ;
2010-10-07 13:05:23 +02:00
2012-09-25 10:47:08 +02:00
WARN_ON ( ! spin_is_locked ( & devinfo - > lock ) ) ;
2010-12-15 15:44:04 -05:00
if ( cmdinfo - > state & SUBMIT_STATUS_URB ) {
2012-06-19 09:54:50 +02:00
err = uas_submit_sense_urb ( cmnd - > device - > host , gfp ,
cmdinfo - > stream ) ;
if ( err ) {
return err ;
2010-10-07 13:05:23 +02:00
}
2010-12-15 15:44:04 -05:00
cmdinfo - > state & = ~ SUBMIT_STATUS_URB ;
2010-10-07 13:05:23 +02:00
}
if ( cmdinfo - > state & ALLOC_DATA_IN_URB ) {
cmdinfo - > data_in_urb = uas_alloc_data_urb ( devinfo , gfp ,
2012-06-19 09:54:48 +02:00
devinfo - > data_in_pipe , cmdinfo - > stream ,
2012-06-19 09:54:51 +02:00
cmnd , DMA_FROM_DEVICE ) ;
2010-10-07 13:05:23 +02:00
if ( ! cmdinfo - > data_in_urb )
return SCSI_MLQUEUE_DEVICE_BUSY ;
cmdinfo - > state & = ~ ALLOC_DATA_IN_URB ;
}
if ( cmdinfo - > state & SUBMIT_DATA_IN_URB ) {
if ( usb_submit_urb ( cmdinfo - > data_in_urb , gfp ) ) {
scmd_printk ( KERN_INFO , cmnd ,
" data in urb submission failure \n " ) ;
return SCSI_MLQUEUE_DEVICE_BUSY ;
}
cmdinfo - > state & = ~ SUBMIT_DATA_IN_URB ;
2012-06-19 09:54:51 +02:00
cmdinfo - > state | = DATA_IN_URB_INFLIGHT ;
2012-06-19 09:54:53 +02:00
usb_anchor_urb ( cmdinfo - > data_in_urb , & devinfo - > data_urbs ) ;
2010-10-07 13:05:23 +02:00
}
if ( cmdinfo - > state & ALLOC_DATA_OUT_URB ) {
cmdinfo - > data_out_urb = uas_alloc_data_urb ( devinfo , gfp ,
2012-06-19 09:54:48 +02:00
devinfo - > data_out_pipe , cmdinfo - > stream ,
2012-06-19 09:54:51 +02:00
cmnd , DMA_TO_DEVICE ) ;
2010-10-07 13:05:23 +02:00
if ( ! cmdinfo - > data_out_urb )
return SCSI_MLQUEUE_DEVICE_BUSY ;
cmdinfo - > state & = ~ ALLOC_DATA_OUT_URB ;
}
if ( cmdinfo - > state & SUBMIT_DATA_OUT_URB ) {
if ( usb_submit_urb ( cmdinfo - > data_out_urb , gfp ) ) {
scmd_printk ( KERN_INFO , cmnd ,
" data out urb submission failure \n " ) ;
return SCSI_MLQUEUE_DEVICE_BUSY ;
}
cmdinfo - > state & = ~ SUBMIT_DATA_OUT_URB ;
2012-06-19 09:54:51 +02:00
cmdinfo - > state | = DATA_OUT_URB_INFLIGHT ;
2012-06-19 09:54:53 +02:00
usb_anchor_urb ( cmdinfo - > data_out_urb , & devinfo - > data_urbs ) ;
2010-10-07 13:05:23 +02:00
}
if ( cmdinfo - > state & ALLOC_CMD_URB ) {
cmdinfo - > cmd_urb = uas_alloc_cmd_urb ( devinfo , gfp , cmnd ,
2012-09-25 10:47:04 +02:00
cmdinfo - > stream ) ;
2010-10-07 13:05:23 +02:00
if ( ! cmdinfo - > cmd_urb )
return SCSI_MLQUEUE_DEVICE_BUSY ;
cmdinfo - > state & = ~ ALLOC_CMD_URB ;
}
if ( cmdinfo - > state & SUBMIT_CMD_URB ) {
2012-09-25 10:47:04 +02:00
usb_get_urb ( cmdinfo - > cmd_urb ) ;
2010-10-07 13:05:23 +02:00
if ( usb_submit_urb ( cmdinfo - > cmd_urb , gfp ) ) {
scmd_printk ( KERN_INFO , cmnd ,
" cmd urb submission failure \n " ) ;
return SCSI_MLQUEUE_DEVICE_BUSY ;
}
2012-09-25 10:47:04 +02:00
usb_anchor_urb ( cmdinfo - > cmd_urb , & devinfo - > cmd_urbs ) ;
usb_put_urb ( cmdinfo - > cmd_urb ) ;
cmdinfo - > cmd_urb = NULL ;
2010-10-07 13:05:23 +02:00
cmdinfo - > state & = ~ SUBMIT_CMD_URB ;
2012-06-19 09:54:51 +02:00
cmdinfo - > state | = COMMAND_INFLIGHT ;
2010-10-07 13:05:23 +02:00
}
return 0 ;
}
2010-11-16 02:10:29 -05:00
static int uas_queuecommand_lck ( struct scsi_cmnd * cmnd ,
2010-10-07 13:05:23 +02:00
void ( * done ) ( struct scsi_cmnd * ) )
{
struct scsi_device * sdev = cmnd - > device ;
struct uas_dev_info * devinfo = sdev - > hostdata ;
struct uas_cmd_info * cmdinfo = ( void * ) & cmnd - > SCp ;
2012-09-25 10:47:08 +02:00
unsigned long flags ;
2010-10-07 13:05:23 +02:00
int err ;
BUILD_BUG_ON ( sizeof ( struct uas_cmd_info ) > sizeof ( struct scsi_pointer ) ) ;
2012-11-30 11:54:45 +01:00
if ( devinfo - > resetting ) {
cmnd - > result = DID_ERROR < < 16 ;
cmnd - > scsi_done ( cmnd ) ;
return 0 ;
}
2012-09-25 10:47:08 +02:00
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
if ( devinfo - > cmnd ) {
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2010-10-07 13:05:23 +02:00
return SCSI_MLQUEUE_DEVICE_BUSY ;
2012-09-25 10:47:08 +02:00
}
2010-10-07 13:05:23 +02:00
if ( blk_rq_tagged ( cmnd - > request ) ) {
2011-12-19 20:22:39 +01:00
cmdinfo - > stream = cmnd - > request - > tag + 2 ;
2010-10-07 13:05:23 +02:00
} else {
2011-12-19 20:22:39 +01:00
devinfo - > cmnd = cmnd ;
2010-10-07 13:05:23 +02:00
cmdinfo - > stream = 1 ;
}
cmnd - > scsi_done = done ;
2012-06-19 09:54:50 +02:00
cmdinfo - > state = SUBMIT_STATUS_URB |
2010-10-07 13:05:23 +02:00
ALLOC_CMD_URB | SUBMIT_CMD_URB ;
switch ( cmnd - > sc_data_direction ) {
case DMA_FROM_DEVICE :
cmdinfo - > state | = ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB ;
break ;
case DMA_BIDIRECTIONAL :
cmdinfo - > state | = ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB ;
case DMA_TO_DEVICE :
cmdinfo - > state | = ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB ;
case DMA_NONE :
break ;
}
if ( ! devinfo - > use_streams ) {
2012-06-19 09:54:49 +02:00
cmdinfo - > state & = ~ ( SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB ) ;
2010-10-07 13:05:23 +02:00
cmdinfo - > stream = 0 ;
}
err = uas_submit_urbs ( cmnd , devinfo , GFP_ATOMIC ) ;
if ( err ) {
/* If we did nothing, give up now */
2010-12-15 15:44:04 -05:00
if ( cmdinfo - > state & SUBMIT_STATUS_URB ) {
2012-09-25 10:47:08 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2010-10-07 13:05:23 +02:00
return SCSI_MLQUEUE_DEVICE_BUSY ;
}
spin_lock ( & uas_work_lock ) ;
list_add_tail ( & cmdinfo - > list , & uas_work_list ) ;
2012-11-30 11:54:42 +01:00
cmdinfo - > state | = IS_IN_WORK_LIST ;
2010-10-07 13:05:23 +02:00
spin_unlock ( & uas_work_lock ) ;
schedule_work ( & uas_work ) ;
}
2012-09-25 10:47:08 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2010-10-07 13:05:23 +02:00
return 0 ;
}
2010-11-16 02:10:29 -05:00
static DEF_SCSI_QCMD ( uas_queuecommand )
2012-06-19 09:54:54 +02:00
static int uas_eh_task_mgmt ( struct scsi_cmnd * cmnd ,
const char * fname , u8 function )
2010-10-07 13:05:23 +02:00
{
2012-06-19 09:54:54 +02:00
struct Scsi_Host * shost = cmnd - > device - > host ;
struct uas_dev_info * devinfo = ( void * ) shost - > hostdata [ 0 ] ;
2012-09-25 10:47:05 +02:00
u16 tag = devinfo - > qdepth - 1 ;
2012-09-25 10:47:08 +02:00
unsigned long flags ;
2010-10-07 13:05:23 +02:00
2012-09-25 10:47:08 +02:00
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
2012-06-19 09:54:54 +02:00
memset ( & devinfo - > response , 0 , sizeof ( devinfo - > response ) ) ;
2012-09-25 10:47:08 +02:00
if ( uas_submit_sense_urb ( shost , GFP_ATOMIC , tag ) ) {
2012-06-19 09:54:54 +02:00
shost_printk ( KERN_INFO , shost ,
" %s: %s: submit sense urb failed \n " ,
__func__ , fname ) ;
2012-09-26 10:28:58 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2012-06-19 09:54:54 +02:00
return FAILED ;
}
2012-09-25 10:47:08 +02:00
if ( uas_submit_task_urb ( cmnd , GFP_ATOMIC , function , tag ) ) {
2012-06-19 09:54:54 +02:00
shost_printk ( KERN_INFO , shost ,
" %s: %s: submit task mgmt urb failed \n " ,
__func__ , fname ) ;
2012-09-26 10:28:58 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
2012-06-19 09:54:54 +02:00
return FAILED ;
}
2012-09-25 10:47:08 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
if ( usb_wait_anchor_empty_timeout ( & devinfo - > sense_urbs , 3000 ) = = 0 ) {
2012-06-19 09:54:54 +02:00
shost_printk ( KERN_INFO , shost ,
" %s: %s timed out \n " , __func__ , fname ) ;
return FAILED ;
}
if ( be16_to_cpu ( devinfo - > response . tag ) ! = tag ) {
shost_printk ( KERN_INFO , shost ,
" %s: %s failed (wrong tag %d/%d) \n " , __func__ ,
fname , be16_to_cpu ( devinfo - > response . tag ) , tag ) ;
return FAILED ;
}
if ( devinfo - > response . response_code ! = RC_TMF_COMPLETE ) {
shost_printk ( KERN_INFO , shost ,
" %s: %s failed (rc 0x%x) \n " , __func__ ,
fname , devinfo - > response . response_code ) ;
return FAILED ;
}
return SUCCESS ;
2010-10-07 13:05:23 +02:00
}
2012-06-19 09:54:54 +02:00
static int uas_eh_abort_handler ( struct scsi_cmnd * cmnd )
2010-10-07 13:05:23 +02:00
{
2012-06-19 09:54:54 +02:00
struct uas_cmd_info * cmdinfo = ( void * ) & cmnd - > SCp ;
2012-09-25 10:47:08 +02:00
struct uas_dev_info * devinfo = ( void * ) cmnd - > device - > hostdata ;
unsigned long flags ;
2012-06-19 09:54:54 +02:00
int ret ;
2010-10-07 13:05:23 +02:00
2012-06-19 09:54:54 +02:00
uas_log_cmd_state ( cmnd , __func__ ) ;
2012-09-25 10:47:08 +02:00
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
2012-09-25 10:47:06 +02:00
cmdinfo - > state | = COMMAND_ABORTED ;
2012-11-30 11:54:43 +01:00
if ( cmdinfo - > state & IS_IN_WORK_LIST ) {
spin_lock ( & uas_work_lock ) ;
list_del ( & cmdinfo - > list ) ;
cmdinfo - > state & = ~ IS_IN_WORK_LIST ;
spin_unlock ( & uas_work_lock ) ;
}
if ( cmdinfo - > state & COMMAND_INFLIGHT ) {
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
ret = uas_eh_task_mgmt ( cmnd , " ABORT TASK " , TMF_ABORT_TASK ) ;
} else {
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
uas_unlink_data_urbs ( devinfo , cmdinfo ) ;
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
uas_try_complete ( cmnd , __func__ ) ;
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
ret = SUCCESS ;
}
2012-06-19 09:54:54 +02:00
return ret ;
2010-10-07 13:05:23 +02:00
}
2012-06-19 09:54:54 +02:00
static int uas_eh_device_reset_handler ( struct scsi_cmnd * cmnd )
2010-10-07 13:05:23 +02:00
{
2012-06-19 09:54:54 +02:00
sdev_printk ( KERN_INFO , cmnd - > device , " %s \n " , __func__ ) ;
return uas_eh_task_mgmt ( cmnd , " LOGICAL UNIT RESET " ,
TMF_LOGICAL_UNIT_RESET ) ;
2010-10-07 13:05:23 +02:00
}
static int uas_eh_bus_reset_handler ( struct scsi_cmnd * cmnd )
{
struct scsi_device * sdev = cmnd - > device ;
struct uas_dev_info * devinfo = sdev - > hostdata ;
struct usb_device * udev = devinfo - > udev ;
2012-06-19 09:54:54 +02:00
int err ;
2010-10-07 13:05:23 +02:00
2012-06-19 09:54:54 +02:00
devinfo - > resetting = 1 ;
2012-11-30 11:54:44 +01:00
uas_abort_work ( devinfo ) ;
2012-09-25 10:47:04 +02:00
usb_kill_anchored_urbs ( & devinfo - > cmd_urbs ) ;
2012-06-19 09:54:54 +02:00
usb_kill_anchored_urbs ( & devinfo - > sense_urbs ) ;
usb_kill_anchored_urbs ( & devinfo - > data_urbs ) ;
err = usb_reset_device ( udev ) ;
devinfo - > resetting = 0 ;
2010-10-07 13:05:23 +02:00
2012-06-19 09:54:54 +02:00
if ( err ) {
shost_printk ( KERN_INFO , sdev - > host , " %s FAILED \n " , __func__ ) ;
return FAILED ;
}
2010-10-07 13:05:23 +02:00
2012-06-19 09:54:54 +02:00
shost_printk ( KERN_INFO , sdev - > host , " %s success \n " , __func__ ) ;
return SUCCESS ;
2010-10-07 13:05:23 +02:00
}
static int uas_slave_alloc ( struct scsi_device * sdev )
{
sdev - > hostdata = ( void * ) sdev - > host - > hostdata [ 0 ] ;
return 0 ;
}
static int uas_slave_configure ( struct scsi_device * sdev )
{
struct uas_dev_info * devinfo = sdev - > hostdata ;
scsi_set_tag_type ( sdev , MSG_ORDERED_TAG ) ;
2012-09-25 10:47:05 +02:00
scsi_activate_tcq ( sdev , devinfo - > qdepth - 3 ) ;
2010-10-07 13:05:23 +02:00
return 0 ;
}
static struct scsi_host_template uas_host_template = {
. module = THIS_MODULE ,
. name = " uas " ,
. queuecommand = uas_queuecommand ,
. slave_alloc = uas_slave_alloc ,
. slave_configure = uas_slave_configure ,
. eh_abort_handler = uas_eh_abort_handler ,
. eh_device_reset_handler = uas_eh_device_reset_handler ,
. eh_bus_reset_handler = uas_eh_bus_reset_handler ,
. can_queue = 65536 , /* Is there a limit on the _host_ ? */
. this_id = - 1 ,
. sg_tablesize = SG_NONE ,
. cmd_per_lun = 1 , /* until we override it */
. skip_settle_delay = 1 ,
. ordered_tag = 1 ,
} ;
static struct usb_device_id uas_usb_ids [ ] = {
{ USB_INTERFACE_INFO ( USB_CLASS_MASS_STORAGE , USB_SC_SCSI , USB_PR_BULK ) } ,
{ USB_INTERFACE_INFO ( USB_CLASS_MASS_STORAGE , USB_SC_SCSI , USB_PR_UAS ) } ,
/* 0xaa is a prototype device I happen to have access to */
{ USB_INTERFACE_INFO ( USB_CLASS_MASS_STORAGE , USB_SC_SCSI , 0xaa ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( usb , uas_usb_ids ) ;
2010-12-15 15:44:05 -05:00
static int uas_is_interface ( struct usb_host_interface * intf )
{
return ( intf - > desc . bInterfaceClass = = USB_CLASS_MASS_STORAGE & &
intf - > desc . bInterfaceSubClass = = USB_SC_SCSI & &
intf - > desc . bInterfaceProtocol = = USB_PR_UAS ) ;
}
2012-01-11 12:42:32 +01:00
static int uas_isnt_supported ( struct usb_device * udev )
{
struct usb_hcd * hcd = bus_to_hcd ( udev - > bus ) ;
dev_warn ( & udev - > dev , " The driver for the USB controller %s does not "
" support scatter-gather which is \n " ,
hcd - > driver - > description ) ;
dev_warn ( & udev - > dev , " required by the UAS driver. Please try an "
" alternative USB controller if you wish to use UAS. \n " ) ;
return - ENODEV ;
}
2010-12-15 15:44:05 -05:00
static int uas_switch_interface ( struct usb_device * udev ,
struct usb_interface * intf )
{
int i ;
2012-01-11 12:42:32 +01:00
int sg_supported = udev - > bus - > sg_tablesize ! = 0 ;
2010-12-15 15:44:05 -05:00
for ( i = 0 ; i < intf - > num_altsetting ; i + + ) {
struct usb_host_interface * alt = & intf - > altsetting [ i ] ;
2012-01-11 12:42:32 +01:00
if ( uas_is_interface ( alt ) ) {
if ( ! sg_supported )
return uas_isnt_supported ( udev ) ;
2010-12-15 15:44:05 -05:00
return usb_set_interface ( udev ,
alt - > desc . bInterfaceNumber ,
alt - > desc . bAlternateSetting ) ;
2012-01-11 12:42:32 +01:00
}
2010-12-15 15:44:05 -05:00
}
return - ENODEV ;
}
2010-10-07 13:05:23 +02:00
static void uas_configure_endpoints ( struct uas_dev_info * devinfo )
{
struct usb_host_endpoint * eps [ 4 ] = { } ;
struct usb_interface * intf = devinfo - > intf ;
struct usb_device * udev = devinfo - > udev ;
struct usb_host_endpoint * endpoint = intf - > cur_altsetting - > endpoint ;
unsigned i , n_endpoints = intf - > cur_altsetting - > desc . bNumEndpoints ;
devinfo - > uas_sense_old = 0 ;
2011-12-19 20:22:39 +01:00
devinfo - > cmnd = NULL ;
2010-10-07 13:05:23 +02:00
for ( i = 0 ; i < n_endpoints ; i + + ) {
unsigned char * extra = endpoint [ i ] . extra ;
int len = endpoint [ i ] . extralen ;
while ( len > 1 ) {
if ( extra [ 1 ] = = USB_DT_PIPE_USAGE ) {
unsigned pipe_id = extra [ 2 ] ;
if ( pipe_id > 0 & & pipe_id < 5 )
eps [ pipe_id - 1 ] = & endpoint [ i ] ;
break ;
}
len - = extra [ 0 ] ;
extra + = extra [ 0 ] ;
}
}
/*
* Assume that if we didn ' t find a control pipe descriptor , we ' re
* using a device with old firmware that happens to be set up like
* this .
*/
if ( ! eps [ 0 ] ) {
devinfo - > cmd_pipe = usb_sndbulkpipe ( udev , 1 ) ;
devinfo - > status_pipe = usb_rcvbulkpipe ( udev , 1 ) ;
devinfo - > data_in_pipe = usb_rcvbulkpipe ( udev , 2 ) ;
devinfo - > data_out_pipe = usb_sndbulkpipe ( udev , 2 ) ;
eps [ 1 ] = usb_pipe_endpoint ( udev , devinfo - > status_pipe ) ;
eps [ 2 ] = usb_pipe_endpoint ( udev , devinfo - > data_in_pipe ) ;
eps [ 3 ] = usb_pipe_endpoint ( udev , devinfo - > data_out_pipe ) ;
} else {
devinfo - > cmd_pipe = usb_sndbulkpipe ( udev ,
eps [ 0 ] - > desc . bEndpointAddress ) ;
devinfo - > status_pipe = usb_rcvbulkpipe ( udev ,
eps [ 1 ] - > desc . bEndpointAddress ) ;
devinfo - > data_in_pipe = usb_rcvbulkpipe ( udev ,
eps [ 2 ] - > desc . bEndpointAddress ) ;
devinfo - > data_out_pipe = usb_sndbulkpipe ( udev ,
eps [ 3 ] - > desc . bEndpointAddress ) ;
}
devinfo - > qdepth = usb_alloc_streams ( devinfo - > intf , eps + 1 , 3 , 256 ,
GFP_KERNEL ) ;
if ( devinfo - > qdepth < 0 ) {
devinfo - > qdepth = 256 ;
devinfo - > use_streams = 0 ;
} else {
devinfo - > use_streams = 1 ;
}
}
2011-12-19 17:06:08 +01:00
static void uas_free_streams ( struct uas_dev_info * devinfo )
{
struct usb_device * udev = devinfo - > udev ;
struct usb_host_endpoint * eps [ 3 ] ;
eps [ 0 ] = usb_pipe_endpoint ( udev , devinfo - > status_pipe ) ;
eps [ 1 ] = usb_pipe_endpoint ( udev , devinfo - > data_in_pipe ) ;
eps [ 2 ] = usb_pipe_endpoint ( udev , devinfo - > data_out_pipe ) ;
usb_free_streams ( devinfo - > intf , eps , 3 , GFP_KERNEL ) ;
}
2010-10-07 13:05:23 +02:00
/*
* XXX : What I ' d like to do here is register a SCSI host for each USB host in
* the system . Follow usb - storage ' s design of registering a SCSI host for
* each USB device for the moment . Can implement this by walking up the
* USB hierarchy until we find a USB host .
*/
static int uas_probe ( struct usb_interface * intf , const struct usb_device_id * id )
{
int result ;
struct Scsi_Host * shost ;
struct uas_dev_info * devinfo ;
struct usb_device * udev = interface_to_usbdev ( intf ) ;
2010-12-15 15:44:05 -05:00
if ( uas_switch_interface ( udev , intf ) )
return - ENODEV ;
2010-10-07 13:05:23 +02:00
devinfo = kmalloc ( sizeof ( struct uas_dev_info ) , GFP_KERNEL ) ;
if ( ! devinfo )
return - ENOMEM ;
result = - ENOMEM ;
shost = scsi_host_alloc ( & uas_host_template , sizeof ( void * ) ) ;
if ( ! shost )
goto free ;
shost - > max_cmd_len = 16 + 252 ;
shost - > max_id = 1 ;
2013-01-25 15:03:36 +01:00
shost - > max_lun = 256 ;
shost - > max_channel = 0 ;
2010-10-07 13:05:23 +02:00
shost - > sg_tablesize = udev - > bus - > sg_tablesize ;
devinfo - > intf = intf ;
devinfo - > udev = udev ;
2012-06-19 09:54:54 +02:00
devinfo - > resetting = 0 ;
2012-09-25 10:47:04 +02:00
init_usb_anchor ( & devinfo - > cmd_urbs ) ;
2012-06-19 09:54:53 +02:00
init_usb_anchor ( & devinfo - > sense_urbs ) ;
init_usb_anchor ( & devinfo - > data_urbs ) ;
2012-09-25 10:47:08 +02:00
spin_lock_init ( & devinfo - > lock ) ;
2010-10-07 13:05:23 +02:00
uas_configure_endpoints ( devinfo ) ;
2012-09-25 10:47:05 +02:00
result = scsi_init_shared_tag_map ( shost , devinfo - > qdepth - 3 ) ;
2010-10-07 13:05:23 +02:00
if ( result )
goto free ;
2011-12-19 17:06:08 +01:00
result = scsi_add_host ( shost , & intf - > dev ) ;
if ( result )
goto deconfig_eps ;
2010-10-07 13:05:23 +02:00
shost - > hostdata [ 0 ] = ( unsigned long ) devinfo ;
scsi_scan_host ( shost ) ;
usb_set_intfdata ( intf , shost ) ;
return result ;
2011-12-19 17:06:08 +01:00
deconfig_eps :
uas_free_streams ( devinfo ) ;
2010-10-07 13:05:23 +02:00
free :
kfree ( devinfo ) ;
if ( shost )
scsi_host_put ( shost ) ;
return result ;
}
static int uas_pre_reset ( struct usb_interface * intf )
{
/* XXX: Need to return 1 if it's not our device in error handling */
return 0 ;
}
static int uas_post_reset ( struct usb_interface * intf )
{
/* XXX: Need to return 1 if it's not our device in error handling */
return 0 ;
}
static void uas_disconnect ( struct usb_interface * intf )
{
struct Scsi_Host * shost = usb_get_intfdata ( intf ) ;
struct uas_dev_info * devinfo = ( void * ) shost - > hostdata [ 0 ] ;
2012-11-30 11:54:44 +01:00
devinfo - > resetting = 1 ;
uas_abort_work ( devinfo ) ;
2012-09-25 10:47:04 +02:00
usb_kill_anchored_urbs ( & devinfo - > cmd_urbs ) ;
2012-06-19 09:54:53 +02:00
usb_kill_anchored_urbs ( & devinfo - > sense_urbs ) ;
usb_kill_anchored_urbs ( & devinfo - > data_urbs ) ;
2012-11-30 11:54:44 +01:00
scsi_remove_host ( shost ) ;
2011-12-19 17:06:08 +01:00
uas_free_streams ( devinfo ) ;
2010-10-07 13:05:23 +02:00
kfree ( devinfo ) ;
}
/*
* XXX : Should this plug into libusual so we can auto - upgrade devices from
* Bulk - Only to UAS ?
*/
static struct usb_driver uas_driver = {
. name = " uas " ,
. probe = uas_probe ,
. disconnect = uas_disconnect ,
. pre_reset = uas_pre_reset ,
. post_reset = uas_post_reset ,
. id_table = uas_usb_ids ,
} ;
2011-11-18 09:34:02 -08:00
module_usb_driver ( uas_driver ) ;
2010-10-07 13:05:23 +02:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Matthew Wilcox and Sarah Sharp " ) ;