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>
2013-10-25 17:04:33 +01:00
# include <linux/usb_usual.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>
2013-10-22 16:10:44 +01:00
# include <scsi/scsi_eh.h>
2010-10-07 13:05:23 +02:00
# 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>
2013-10-21 08:53:31 +01:00
# include "uas-detect.h"
2010-10-07 13:05:23 +02:00
/*
* 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 ;
2013-10-23 14:27:09 +01:00
struct response_iu 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 ;
2013-09-13 13:27:12 +02:00
struct work_struct work ;
struct list_head work_list ;
2013-09-13 13:27:13 +02:00
struct list_head dead_list ;
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 ;
2013-09-13 13:27:12 +02:00
struct list_head work ;
2013-09-13 13:27:13 +02:00
struct list_head dead ;
2010-10-07 13:05:23 +02:00
} ;
/* 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 ) ;
2013-09-13 13:27:11 +02:00
static void uas_configure_endpoints ( struct uas_dev_info * devinfo ) ;
static void uas_free_streams ( struct uas_dev_info * devinfo ) ;
2013-09-13 13:27:13 +02:00
static void uas_log_cmd_state ( struct scsi_cmnd * cmnd , const char * caller ) ;
2010-10-07 13:05:23 +02:00
2013-10-17 19:19:04 +02:00
/* Must be called with devinfo->lock held, will temporary unlock the lock */
2012-11-30 11:54:40 +01:00
static void uas_unlink_data_urbs ( struct uas_dev_info * devinfo ,
2013-10-17 19:19:04 +02:00
struct uas_cmd_info * cmdinfo ,
unsigned long * lock_flags )
2012-11-30 11:54:40 +01:00
{
2012-11-30 11:54:41 +01:00
/*
* The UNLINK_DATA_URBS flag makes sure uas_try_complete
* ( called by urb completion ) doesn ' t release cmdinfo
* underneath us .
*/
cmdinfo - > state | = UNLINK_DATA_URBS ;
2013-10-17 19:19:04 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , * lock_flags ) ;
2012-11-30 11:54:41 +01:00
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
2013-10-17 19:19:04 +02:00
spin_lock_irqsave ( & devinfo - > lock , * lock_flags ) ;
2012-11-30 11:54:41 +01:00
cmdinfo - > state & = ~ UNLINK_DATA_URBS ;
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 )
{
2013-09-13 13:27:12 +02:00
struct uas_dev_info * devinfo =
container_of ( work , struct uas_dev_info , work ) ;
2010-10-07 13:05:23 +02:00
struct uas_cmd_info * cmdinfo ;
2011-12-02 11:55:44 -08:00
struct uas_cmd_info * temp ;
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
2013-09-13 13:27:12 +02:00
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
list_for_each_entry_safe ( cmdinfo , temp , & devinfo - > work_list , work ) {
2010-10-07 13:05:23 +02:00
struct scsi_pointer * scp = ( void * ) cmdinfo ;
2013-09-13 13:27:12 +02:00
struct scsi_cmnd * cmnd = container_of ( scp , struct scsi_cmnd ,
SCp ) ;
2012-09-25 10:47:08 +02:00
err = uas_submit_urbs ( cmnd , cmnd - > device - > hostdata , GFP_ATOMIC ) ;
2013-09-13 13:27:12 +02:00
if ( ! err ) {
2012-11-30 11:54:42 +01:00
cmdinfo - > state & = ~ IS_IN_WORK_LIST ;
2013-09-13 13:27:12 +02:00
list_del ( & cmdinfo - > work ) ;
} else {
schedule_work ( & devinfo - > work ) ;
2011-12-02 11:55:44 -08:00
}
2010-10-07 13:05:23 +02:00
}
2013-09-13 13:27:12 +02:00
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
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 ;
unsigned long flags ;
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
2013-09-13 13:27:12 +02:00
list_for_each_entry_safe ( cmdinfo , temp , & devinfo - > work_list , work ) {
2012-11-30 11:54:44 +01:00
struct scsi_pointer * scp = ( void * ) cmdinfo ;
2013-09-13 13:27:12 +02:00
struct scsi_cmnd * cmnd = container_of ( scp , struct scsi_cmnd ,
SCp ) ;
2013-09-13 13:27:13 +02:00
uas_log_cmd_state ( cmnd , __func__ ) ;
2013-09-13 13:27:14 +02:00
WARN_ON_ONCE ( cmdinfo - > state & COMMAND_ABORTED ) ;
2013-09-13 13:27:12 +02:00
cmdinfo - > state | = COMMAND_ABORTED ;
cmdinfo - > state & = ~ IS_IN_WORK_LIST ;
list_del ( & cmdinfo - > work ) ;
2013-09-13 13:27:13 +02:00
list_add_tail ( & cmdinfo - > dead , & devinfo - > dead_list ) ;
2012-11-30 11:54:44 +01:00
}
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
}
2013-09-13 13:27:12 +02:00
static void uas_add_work ( struct uas_cmd_info * cmdinfo )
{
struct scsi_pointer * scp = ( void * ) cmdinfo ;
struct scsi_cmnd * cmnd = container_of ( scp , struct scsi_cmnd , SCp ) ;
struct uas_dev_info * devinfo = cmnd - > device - > hostdata ;
2013-09-13 13:27:14 +02:00
WARN_ON_ONCE ( ! spin_is_locked ( & devinfo - > lock ) ) ;
2013-09-13 13:27:12 +02:00
list_add_tail ( & cmdinfo - > work , & devinfo - > work_list ) ;
cmdinfo - > state | = IS_IN_WORK_LIST ;
schedule_work ( & devinfo - > work ) ;
}
2013-09-13 13:27:13 +02:00
static void uas_zap_dead ( struct uas_dev_info * devinfo )
{
struct uas_cmd_info * cmdinfo ;
struct uas_cmd_info * temp ;
unsigned long flags ;
spin_lock_irqsave ( & devinfo - > lock , flags ) ;
list_for_each_entry_safe ( cmdinfo , temp , & devinfo - > dead_list , dead ) {
struct scsi_pointer * scp = ( void * ) cmdinfo ;
struct scsi_cmnd * cmnd = container_of ( scp , struct scsi_cmnd ,
SCp ) ;
uas_log_cmd_state ( cmnd , __func__ ) ;
2013-09-13 13:27:14 +02:00
WARN_ON_ONCE ( ! ( cmdinfo - > state & COMMAND_ABORTED ) ) ;
2013-09-13 13:27:13 +02:00
/* all urbs are killed, clear inflight bits */
cmdinfo - > state & = ~ ( COMMAND_INFLIGHT |
DATA_IN_URB_INFLIGHT |
DATA_OUT_URB_INFLIGHT ) ;
uas_try_complete ( cmnd , __func__ ) ;
}
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
2013-09-13 13:27:14 +02:00
WARN_ON_ONCE ( ! 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 ;
2013-09-13 13:27:14 +02:00
WARN_ON_ONCE ( cmdinfo - > state & COMMAND_COMPLETED ) ;
2012-06-19 09:54:51 +02:00
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 ;
2013-09-13 13:27:13 +02:00
list_del ( & cmdinfo - > dead ) ;
2012-09-25 10:47:07 +02:00
}
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 ) {
2013-09-13 13:27:12 +02:00
uas_add_work ( cmdinfo ) ;
2010-10-07 13:05:23 +02:00
}
}
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 ) {
2013-09-13 13:27:13 +02:00
if ( urb - > status = = - ENOENT ) {
dev_err ( & urb - > dev - > dev , " stat urb: killed, stream %d \n " ,
urb - > stream_id ) ;
} else {
dev_err ( & urb - > dev - > dev , " stat urb: 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 */
2013-10-17 19:19:04 +02:00
uas_unlink_data_urbs ( devinfo , cmdinfo , & 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 ;
}
2013-09-13 13:27:14 +02:00
if ( sdb = = NULL ) {
WARN_ON_ONCE ( 1 ) ;
} else if ( urb - > status ) {
2012-06-19 09:54:52 +02:00
/* 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 ,
2013-10-17 19:47:28 +02:00
struct scsi_cmnd * cmnd )
2010-10-07 13:05:23 +02:00
{
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 ;
2013-10-17 19:30:26 +02:00
usb_anchor_urb ( urb , & devinfo - > cmd_urbs ) ;
2012-06-19 09:54:54 +02:00
err = usb_submit_urb ( urb , gfp ) ;
2013-10-17 19:30:26 +02:00
if ( err ) {
usb_unanchor_urb ( urb ) ;
2012-06-19 09:54:54 +02:00
goto err ;
2013-10-17 19:30:26 +02:00
}
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 ;
2013-10-17 19:30:26 +02:00
usb_anchor_urb ( urb , & devinfo - > sense_urbs ) ;
2012-06-19 09:54:50 +02:00
if ( usb_submit_urb ( urb , gfp ) ) {
2013-10-17 19:30:26 +02:00
usb_unanchor_urb ( urb ) ;
2012-06-19 09:54:50 +02:00
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: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
2013-09-13 13:27:14 +02:00
WARN_ON_ONCE ( ! 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 ) {
2013-10-17 19:30:26 +02:00
usb_anchor_urb ( cmdinfo - > data_in_urb , & devinfo - > data_urbs ) ;
2010-10-07 13:05:23 +02:00
if ( usb_submit_urb ( cmdinfo - > data_in_urb , gfp ) ) {
2013-10-17 19:30:26 +02:00
usb_unanchor_urb ( cmdinfo - > data_in_urb ) ;
2010-10-07 13:05:23 +02:00
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 ;
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 ) {
2013-10-17 19:30:26 +02:00
usb_anchor_urb ( cmdinfo - > data_out_urb , & devinfo - > data_urbs ) ;
2010-10-07 13:05:23 +02:00
if ( usb_submit_urb ( cmdinfo - > data_out_urb , gfp ) ) {
2013-10-17 19:30:26 +02:00
usb_unanchor_urb ( cmdinfo - > data_out_urb ) ;
2010-10-07 13:05:23 +02:00
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 ;
2010-10-07 13:05:23 +02:00
}
if ( cmdinfo - > state & ALLOC_CMD_URB ) {
2013-10-17 19:47:28 +02:00
cmdinfo - > cmd_urb = uas_alloc_cmd_urb ( devinfo , gfp , cmnd ) ;
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 ) {
2013-10-17 19:30:26 +02:00
usb_anchor_urb ( cmdinfo - > cmd_urb , & devinfo - > cmd_urbs ) ;
2010-10-07 13:05:23 +02:00
if ( usb_submit_urb ( cmdinfo - > cmd_urb , gfp ) ) {
2013-10-17 19:30:26 +02:00
usb_unanchor_urb ( cmdinfo - > cmd_urb ) ;
2010-10-07 13:05:23 +02:00
scmd_printk ( KERN_INFO , cmnd ,
" cmd urb submission failure \n " ) ;
return SCSI_MLQUEUE_DEVICE_BUSY ;
}
2012-09-25 10:47:04 +02:00
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 ;
}
2013-09-13 13:27:12 +02:00
uas_add_work ( cmdinfo ) ;
2010-10-07 13:05:23 +02:00
}
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 ] ;
2013-10-23 17:46:17 +01:00
u16 tag = devinfo - > qdepth ;
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 ) ;
2013-09-13 13:27:14 +02:00
WARN_ON_ONCE ( cmdinfo - > state & COMMAND_ABORTED ) ;
2012-09-25 10:47:06 +02:00
cmdinfo - > state | = COMMAND_ABORTED ;
2013-09-13 13:27:13 +02:00
list_add_tail ( & cmdinfo - > dead , & devinfo - > dead_list ) ;
2012-11-30 11:54:43 +01:00
if ( cmdinfo - > state & IS_IN_WORK_LIST ) {
2013-09-13 13:27:12 +02:00
list_del ( & cmdinfo - > work ) ;
2012-11-30 11:54:43 +01:00
cmdinfo - > state & = ~ IS_IN_WORK_LIST ;
}
if ( cmdinfo - > state & COMMAND_INFLIGHT ) {
spin_unlock_irqrestore ( & devinfo - > lock , flags ) ;
ret = uas_eh_task_mgmt ( cmnd , " ABORT TASK " , TMF_ABORT_TASK ) ;
} else {
2013-10-17 19:19:04 +02:00
uas_unlink_data_urbs ( devinfo , cmdinfo , & flags ) ;
2012-11-30 11:54:43 +01:00
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
2013-09-22 16:27:02 +02:00
err = usb_lock_device_for_reset ( udev , devinfo - > intf ) ;
if ( err ) {
shost_printk ( KERN_ERR , sdev - > host ,
" %s FAILED to get lock err %d \n " , __func__ , err ) ;
return FAILED ;
}
2013-09-13 13:27:13 +02:00
shost_printk ( KERN_INFO , sdev - > host , " %s start \n " , __func__ ) ;
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 ) ;
2013-09-13 13:27:13 +02:00
uas_zap_dead ( devinfo ) ;
2012-06-19 09:54:54 +02:00
err = usb_reset_device ( udev ) ;
devinfo - > resetting = 0 ;
2010-10-07 13:05:23 +02:00
2013-09-22 16:27:02 +02:00
usb_unlock_device ( udev ) ;
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 ) ;
2013-10-23 17:46:17 +01:00
scsi_activate_tcq ( sdev , devinfo - > qdepth - 2 ) ;
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 ,
} ;
2013-10-25 17:04:33 +01:00
# define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
vendorName , productName , useProtocol , useTransport , \
initFunction , flags ) \
{ USB_DEVICE_VER ( id_vendor , id_product , bcdDeviceMin , bcdDeviceMax ) , \
. driver_info = ( flags ) }
2010-10-07 13:05:23 +02:00
static struct usb_device_id uas_usb_ids [ ] = {
2013-10-25 17:04:33 +01:00
# include "unusual_uas.h"
2010-10-07 13:05:23 +02:00
{ 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 ) ;
2013-10-25 17:04:33 +01:00
# undef UNUSUAL_DEV
2013-10-21 08:00:58 +01:00
static int uas_switch_interface ( struct usb_device * udev ,
struct usb_interface * intf )
{
int alt ;
alt = uas_find_uas_alt_setting ( intf ) ;
if ( alt < 0 )
return alt ;
return usb_set_interface ( udev ,
intf - > altsetting [ 0 ] . desc . bInterfaceNumber , alt ) ;
}
2013-10-29 08:54:48 +01:00
static void uas_configure_endpoints ( struct uas_dev_info * devinfo )
{
struct usb_host_endpoint * eps [ 4 ] = { } ;
struct usb_device * udev = devinfo - > udev ;
int r ;
devinfo - > uas_sense_old = 0 ;
devinfo - > cmnd = NULL ;
r = uas_find_endpoints ( devinfo - > intf - > cur_altsetting , eps ) ;
2013-10-29 10:10:36 +01:00
if ( r )
return r ;
devinfo - > cmd_pipe = usb_sndbulkpipe ( udev ,
usb_endpoint_num ( & eps [ 0 ] - > desc ) ) ;
devinfo - > status_pipe = usb_rcvbulkpipe ( udev ,
usb_endpoint_num ( & eps [ 1 ] - > desc ) ) ;
devinfo - > data_in_pipe = usb_rcvbulkpipe ( udev ,
usb_endpoint_num ( & eps [ 2 ] - > desc ) ) ;
devinfo - > data_out_pipe = usb_sndbulkpipe ( udev ,
usb_endpoint_num ( & eps [ 3 ] - > desc ) ) ;
2010-10-07 13:05:23 +02:00
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 )
{
2013-10-17 19:00:45 +02:00
int result = - ENOMEM ;
struct Scsi_Host * shost = NULL ;
2010-10-07 13:05:23 +02:00
struct uas_dev_info * devinfo ;
struct usb_device * udev = interface_to_usbdev ( intf ) ;
2013-10-25 17:04:33 +01:00
if ( ! uas_use_uas_driver ( intf , id ) )
return - ENODEV ;
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 )
2013-10-17 19:00:45 +02:00
goto set_alt0 ;
2010-10-07 13:05:23 +02:00
shost = scsi_host_alloc ( & uas_host_template , sizeof ( void * ) ) ;
if ( ! shost )
2013-10-17 19:00:45 +02:00
goto set_alt0 ;
2010-10-07 13:05:23 +02:00
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 ) ;
2013-09-13 13:27:12 +02:00
INIT_WORK ( & devinfo - > work , uas_do_work ) ;
INIT_LIST_HEAD ( & devinfo - > work_list ) ;
2013-09-13 13:27:13 +02:00
INIT_LIST_HEAD ( & devinfo - > dead_list ) ;
2010-10-07 13:05:23 +02:00
uas_configure_endpoints ( devinfo ) ;
2013-10-23 17:46:17 +01:00
result = scsi_init_shared_tag_map ( shost , devinfo - > qdepth - 2 ) ;
2010-10-07 13:05:23 +02:00
if ( result )
2013-10-17 19:00:45 +02:00
goto free_streams ;
2011-12-19 17:06:08 +01:00
result = scsi_add_host ( shost , & intf - > dev ) ;
if ( result )
2013-10-17 19:00:45 +02:00
goto free_streams ;
2011-12-19 17:06:08 +01:00
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
2013-10-17 19:00:45 +02:00
free_streams :
2011-12-19 17:06:08 +01:00
uas_free_streams ( devinfo ) ;
2013-10-17 19:00:45 +02:00
set_alt0 :
usb_set_interface ( udev , intf - > altsetting [ 0 ] . desc . bInterfaceNumber , 0 ) ;
2010-10-07 13:05:23 +02:00
kfree ( devinfo ) ;
if ( shost )
scsi_host_put ( shost ) ;
return result ;
}
static int uas_pre_reset ( struct usb_interface * intf )
{
2013-10-22 16:10:44 +01:00
struct Scsi_Host * shost = usb_get_intfdata ( intf ) ;
struct uas_dev_info * devinfo = ( void * ) shost - > hostdata [ 0 ] ;
unsigned long flags ;
/* Block new requests */
spin_lock_irqsave ( shost - > host_lock , flags ) ;
scsi_block_requests ( shost ) ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
/* Wait for any pending requests to complete */
flush_work ( & devinfo - > work ) ;
if ( usb_wait_anchor_empty_timeout ( & devinfo - > sense_urbs , 5000 ) = = 0 ) {
shost_printk ( KERN_ERR , shost , " %s: timed out \n " , __func__ ) ;
return 1 ;
}
uas_free_streams ( devinfo ) ;
2010-10-07 13:05:23 +02:00
return 0 ;
}
static int uas_post_reset ( struct usb_interface * intf )
{
2013-10-22 16:10:44 +01:00
struct Scsi_Host * shost = usb_get_intfdata ( intf ) ;
struct uas_dev_info * devinfo = ( void * ) shost - > hostdata [ 0 ] ;
unsigned long flags ;
uas_configure_endpoints ( devinfo ) ;
spin_lock_irqsave ( shost - > host_lock , flags ) ;
scsi_report_bus_reset ( shost , 0 ) ;
spin_unlock_irqrestore ( shost - > host_lock , flags ) ;
scsi_unblock_requests ( shost ) ;
2010-10-07 13:05:23 +02:00
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 ;
2013-09-13 13:27:12 +02:00
cancel_work_sync ( & devinfo - > work ) ;
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:53 +02:00
usb_kill_anchored_urbs ( & devinfo - > sense_urbs ) ;
usb_kill_anchored_urbs ( & devinfo - > data_urbs ) ;
2013-09-13 13:27:13 +02:00
uas_zap_dead ( devinfo ) ;
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 ) ;
}
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 " ) ;