Re: [PATCH/RFC] UASP enhancement to usb-storage

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Forwarding to the linux-scsi list too.

Sarah Sharp

On Thu, Apr 01, 2010 at 10:38:51AM -0700, Hrant Dalalyan wrote:
> Implementation of USB Attached SCSI Protocol per UASP Specification
> (Rev.1, July 9, 2008).  Below is the list of the enhancements made to
> the usb-storage driver.
> 
> - Enhanced probe routine to identify UASP devices.
> - Allocation/deallocation of UASP specific resources.
> - Various enhancements to existing infrastructure to invoke UASP
>   specific routines.
> - Added SCSI command queueing mechanism and state machine for
>   handling multiple commands.
> - Implemented 'abort task' and 'reset nexus' task management
>   functions.
> 
> Limitations:
> 
> - Considered that the endpoint descriptors are received from the device
>  side in the following order:
>   - Command endpoint descriptor.
>   - Bulk in endpoint descriptor.
>   - Bulk out endpoint descriptor.
>   - Status endpoint descriptor.
> because in  the noted revision of the UASP Specification were not defined
> pipe usage descriptors.
> 
> - The max number of streams are not retrieved through the superspeed
>   endpoint companion descriptor and are fixed to 2 streams per
>   endpoint.
> - Device supported LUNs are assumed to be 1.
> - Concurrent processing of SCSI commands is not yet tested due to due
>   to some device limitations.  The driver is currently set to process
>   only 1 command at a time.
> - Abort task, Reset nexus task management functions, as well as some
>   error conditions and recovery situations are not yet tested.
> 
> Signed-off-by: Hrant Dalalyan <dalalyan@xxxxxxxxxxxx>
> ---
> 
>  drivers/usb/storage/protocol.c     |    5 +-
>  drivers/usb/storage/scsiglue.c     |  126 +++-
>  drivers/usb/storage/transport.c    | 1268 +++++++++++++++++++++++++++++++++++-
>  drivers/usb/storage/transport.h    |  150 +++++
>  drivers/usb/storage/unusual_devs.h |    3 +
>  drivers/usb/storage/usb.c          |  445 ++++++++++---
>  drivers/usb/storage/usb.h          |   59 ++
>  include/linux/usb_usual.h          |    1 +
>  8 files changed, 1931 insertions(+), 126 deletions(-)
> 
> diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
> index fc310f7..a8882f1 100644
> --- a/drivers/usb/storage/protocol.c
> +++ b/drivers/usb/storage/protocol.c
> @@ -119,7 +119,10 @@ void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
>  				       struct us_data *us)
>  {
>  	/* send the command to the transport layer */
> -	usb_stor_invoke_transport(srb, us);
> +	if (us->protocol == US_PR_UASP)
> +		usb_stor_invoke_UASP_transport(us);
> +	else
> +		usb_stor_invoke_transport(srb, us);
>  }
>  EXPORT_SYMBOL_GPL(usb_stor_transparent_scsi_command);
>  
> diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
> index cfa26d5..dc8c602 100644
> --- a/drivers/usb/storage/scsiglue.c
> +++ b/drivers/usb/storage/scsiglue.c
> @@ -102,7 +102,7 @@ static int slave_alloc (struct scsi_device *sdev)
>  	 * values can be as large as 2048.  To make that work properly
>  	 * will require changes to the block layer.
>  	 */
> -	blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
> +	blk_queue_update_dma_alignment(sdev->request_queue, (1024 - 1));
>  
>  	/*
>  	 * The UFI spec treates the Peripheral Qualifier bits in an
> @@ -281,11 +281,13 @@ static int queuecommand(struct scsi_cmnd *srb,
>  			void (*done)(struct scsi_cmnd *))
>  {
>  	struct us_data *us = host_to_us(srb->device->host);
> +	struct cmd_iu *cmdiu;
> +	unsigned long flags;
>  
>  	US_DEBUGP("%s called\n", __func__);
>  
>  	/* check for state-transition errors */
> -	if (us->srb != NULL) {
> +	if (us->protocol != US_PR_UASP && us->srb != NULL) {
>  		printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n",
>  			__func__, us->srb);
>  		return SCSI_MLQUEUE_HOST_BUSY;
> @@ -299,10 +301,33 @@ static int queuecommand(struct scsi_cmnd *srb,
>  		return 0;
>  	}
>  
> -	/* enqueue the command and wake up the control thread */
> -	srb->scsi_done = done;
> -	us->srb = srb;
> -	complete(&us->cmnd_ready);
> +	if (us->protocol != US_PR_UASP) {
> +		/* enqueue the command and wake up the control thread */
> +		srb->scsi_done = done;
> +		us->srb = srb;
> +		complete(&us->cmnd_ready);
> +	} else {
> +		cmdiu = kzalloc(sizeof(struct cmd_iu), GFP_ATOMIC);
> +		if (!cmdiu)
> +			return SCSI_MLQUEUE_HOST_BUSY;
> +
> +		cmdiu->cmd_iu_id = IU_ID_COMMAND;
> +		cmdiu->ipt_tag = cpu_to_be16(usb_stor_get_tag(us));
> +		cmdiu->length = cpu_to_be16(30);
> +		cmdiu->lun[7] = srb->device->lun;
> +		memcpy(cmdiu->cdb, srb->cmnd, srb->cmd_len);
> +		cmdiu->cmd = srb;
> +		cmdiu->cmd->scsi_done = done;
> +		cmdiu->state = COMMAND_STATE_IDLE;
> +		cmdiu->us = us;
> +
> +		spin_lock_irqsave(&us->lock, flags);
> +		list_add_tail(&cmdiu->node, &us->temp_scsi_cmnd_queue);
> +		us->new_command = 1;
> +		spin_unlock_irqrestore(&us->lock, flags);
> +
> +		wake_up(&us->uasp_wq);
> +	}
>  
>  	return 0;
>  }
> @@ -310,39 +335,92 @@ static int queuecommand(struct scsi_cmnd *srb,
>  /***********************************************************************
>   * Error handling functions
>   ***********************************************************************/
> +static struct cmd_iu *find_cmd_by_ptr(struct us_data *us,
> +					struct scsi_cmnd *srb)
> +{
> +	struct cmd_iu *cmdiu = 0;
> +
> +	list_for_each_entry(cmdiu, &us->scsi_cmnd_queue, node) {
> +		if (cmdiu->cmd == srb)
> +			return cmdiu;
> +	}
> +
> +	list_for_each_entry(cmdiu, &us->temp_scsi_cmnd_queue, node) {
> +		if (cmdiu->cmd == srb)
> +			return cmdiu;
> +	}
> +
> +	return 0;
> +}
>  
>  /* Command timeout and abort */
>  static int command_abort(struct scsi_cmnd *srb)
>  {
>  	struct us_data *us = host_to_us(srb->device->host);
> +	struct cmd_iu *cmdiu;
> +	unsigned long flags;
>  
>  	US_DEBUGP("%s called\n", __func__);
>  
> -	/* us->srb together with the TIMED_OUT, RESETTING, and ABORTING
> -	 * bits are protected by the host lock. */
> -	scsi_lock(us_to_host(us));
> +	if (us->protocol != US_PR_UASP) {
> +		/* us->srb together with the TIMED_OUT, RESETTING, and
> +		 * ABORTING bits are protected by the host lock.
> +		 */
> +		scsi_lock(us_to_host(us));
>  
> -	/* Is this command still active? */
> -	if (us->srb != srb) {
> +		/* Is this command still active? */
> +		if (us->srb != srb) {
> +			scsi_unlock(us_to_host(us));
> +			US_DEBUGP("-- nothing to abort\n");
> +			return FAILED;
> +		}
> +		/* Set the TIMED_OUT bit.  Also set the ABORTING bit, but only
> +		 * if a device reset isn't already in progress (to avoid
> +		 * interfering with the reset). Note that we must retain the
> +		 * host lock while calling usb_stor_stop_transport();
> +		 * otherwise it might interfere with an auto-reset that
> +		 * begins as soon as we release the lock.
> +		 */
> +		set_bit(US_FLIDX_TIMED_OUT, &us->dflags);
> +		if (!test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +			set_bit(US_FLIDX_ABORTING, &us->dflags);
> +			usb_stor_stop_transport(us);
> +		}
>  		scsi_unlock(us_to_host(us));
> -		US_DEBUGP ("-- nothing to abort\n");
> -		return FAILED;
> -	}
> -
> -	/* Set the TIMED_OUT bit.  Also set the ABORTING bit, but only if
> -	 * a device reset isn't already in progress (to avoid interfering
> -	 * with the reset).  Note that we must retain the host lock while
> -	 * calling usb_stor_stop_transport(); otherwise it might interfere
> -	 * with an auto-reset that begins as soon as we release the lock. */
> -	set_bit(US_FLIDX_TIMED_OUT, &us->dflags);
> -	if (!test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +	} else {
> +		/* If we are disconnecting */
> +		if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags))
> +			return FAILED;
> +
> +		/* If reset bit is set */
> +		if (test_bit(US_FLIDX_RESETTING, &us->dflags))
> +			return FAILED;
> +
> +		spin_lock_irqsave(&us->lock, flags);
> +		cmdiu = find_cmd_by_ptr(us, srb);
> +		spin_unlock_irqrestore(&us->lock, flags);
> +		/* Is this command still active? */
> +		if (!cmdiu)
> +			return FAILED;
> +
> +		spin_lock_irqsave(&us->lock, flags);
> +		memset(us->abort_task_tmf, 0, TM_FUNCTION_IU_SIZE);
> +		us->abort_task_tmf->cmdiu = cmdiu;
> +		us->abort_task_tmf->tm_iu_id = IU_ID_TASK_MANAGEMENT;
> +		us->abort_task_tmf->ipt_tag = cpu_to_be16(usb_stor_get_tag(us));
> +		us->abort_task_tmf->tm_function = TM_FUNCTION_ABORT_TASK;
> +		us->abort_task_tmf->task_tag = cmdiu->ipt_tag;
> +		memcpy(us->abort_task_tmf->lun, cmdiu->lun, 8);
> +		us->abort_task_tmf->state = COMMAND_STATE_IDLE;
>  		set_bit(US_FLIDX_ABORTING, &us->dflags);
> -		usb_stor_stop_transport(us);
> +		spin_unlock_irqrestore(&us->lock, flags);
> +
> +		wake_up(&us->uasp_wq);
>  	}
> -	scsi_unlock(us_to_host(us));
>  
>  	/* Wait for the aborted command to finish */
>  	wait_for_completion(&us->notify);
> +
>  	return SUCCESS;
>  }
>  
> diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
> index 589f6b4..e24a02f 100644
> --- a/drivers/usb/storage/transport.c
> +++ b/drivers/usb/storage/transport.c
> @@ -175,7 +175,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
>  	/* wait for the completion of the URB */
>  	timeleft = wait_for_completion_interruptible_timeout(
>  			&urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT);
> - 
> +
>  	clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags);
>  
>  	if (timeleft <= 0) {
> @@ -1305,3 +1305,1269 @@ int usb_stor_port_reset(struct us_data *us)
>  	}
>  	return result;
>  }
> +
> +void usb_stor_transfer_UASP_sglist(struct work_struct *work)
> +{
> +	struct stor_sg_req *sg_req = container_of(work,
> +						  struct stor_sg_req,
> +						  work);
> +	struct us_data *us = sg_req->us;
> +	struct cmd_iu *cmdiu = sg_req->cmdiu;
> +	unsigned int pipe = cmdiu->cmd->sc_data_direction == DMA_FROM_DEVICE ?
> +				us->recv_bulk_pipe : us->send_bulk_pipe;
> +	unsigned long flags;
> +	int tag;
> +	int i;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	/* The command is aborted by abort task or reset nexus */
> +	if (cmdiu->state == COMMAND_STATE_ABORTED)
> +		goto ret;
> +
> +	/* The command is halted by abort task or reset nexus */
> +	if (cmdiu->state == COMMAND_STATE_HALTED)
> +		goto ret;
> +
> +	/* Sense iu received earlier */
> +	if (cmdiu->state == COMMAND_STATE_STATUS)
> +		goto ret;
> +
> +	/* Disconnect bit is set */
> +	if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags))
> +		goto ret;
> +
> +	/* Reset bit is set */
> +	if (test_bit(US_FLIDX_RESETTING, &us->dflags))
> +		goto ret;
> +
> +	sg_req->result = usb_sg_init(&sg_req->sg_req,
> +				     us->pusb_dev,
> +				     pipe,
> +				     0,
> +				     scsi_sglist(cmdiu->cmd),
> +				     scsi_sg_count(cmdiu->cmd),
> +				     scsi_bufflen(cmdiu->cmd),
> +				     GFP_NOIO);
> +
> +	if (sg_req->result)
> +		goto ret;
> +
> +	 /*
> +	  * workaround for setting stream_id for each urb of sg_request,
> +	  * this should be implemented in usbcore driver.
> +	  */
> +	tag = be16_to_cpu(cmdiu->ipt_tag);
> +	for (i = 0; i < sg_req->sg_req.entries; i++)
> +		sg_req->sg_req.urbs[i]->stream_id = tag;
> +
> +	/* wait for the completion of the transfer */
> +	usb_sg_wait(&sg_req->sg_req);
> +	scsi_set_resid(cmdiu->cmd, scsi_bufflen(cmdiu->cmd) -
> +						sg_req->sg_req.bytes);
> +
> +ret:
> +	spin_lock_irqsave(&us->lock, flags);
> +	/* This means that status received earlier with error code */
> +	if (cmdiu->state == COMMAND_STATE_STATUS)
> +		cmdiu->iobuf_sts = REQ_COMPLETED;
> +
> +	cmdiu->sgreq_sts = REQ_COMPLETED;
> +
> +	us->active_requests--;
> +	us->pending_requests++;
> +	spin_unlock_irqrestore(&us->lock, flags);
> +
> +	wake_up(&us->uasp_wq);
> +}
> +
> +static int usb_stor_transfer_UASP_buf(struct us_data *us,
> +				unsigned int pipe,
> +				struct urb *cur_urb,
> +				struct stor_iobuf *iobuf,
> +				unsigned int length,
> +				unsigned short stream_id,
> +				void (*urb_complete)(struct urb *urb),
> +				void *context)
> +{
> +	int result;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	/* fill and submit the URB */
> +	usb_fill_bulk_urb(cur_urb,
> +			us->pusb_dev,
> +			pipe,
> +			iobuf->buf,
> +			length,
> +			urb_complete,
> +			context);
> +
> +	/* fill the common fields in the URB */
> +	cur_urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
> +	cur_urb->transfer_dma = iobuf->dma;
> +	cur_urb->actual_length = 0;
> +	cur_urb->error_count = 0;
> +	cur_urb->status = 0;
> +	cur_urb->stream_id = stream_id;
> +
> +	/* submit the URB */
> +	result = usb_submit_urb(cur_urb, GFP_NOIO);
> +	if (result)
> +		return result;
> +
> +	return 0;
> +}
> +
> +static void usb_stor_cmd_urb_complete(struct urb *urb)
> +{
> +	unsigned long flags;
> +	struct cmd_iu *cmdiu = urb->context;
> +	struct us_data *us = cmdiu->us;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	us->command_pipe_sts = COMMAND_PIPE_IDLE;
> +
> +	spin_lock_irqsave(&us->lock, flags);
> +	cmdiu->iobuf_sts = REQ_COMPLETED;
> +	us->active_requests--;
> +	us->pending_requests++;
> +	spin_unlock_irqrestore(&us->lock, flags);
> +
> +	wake_up(&us->uasp_wq);
> +}
> +
> +static void usb_stor_status_urb_complete(struct urb *urb)
> +{
> +	unsigned long flags;
> +	struct cmd_iu *cmdiu = urb->context;
> +	struct us_data *us = cmdiu->us;
> +	struct s_iu *siu = (struct s_iu *)cmdiu->iobuf->buf;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	spin_lock_irqsave(&us->lock, flags);
> +	/*
> +	 * Everything is correct. If status completes earlier than data,
> +	 * just wait for data completion.
> +	 */
> +	if (!urb->status && siu->status == STATUS_GOOD)
> +		;
> +	/* The urb is completed with error,
> +	 * or the sense iu status is not good.
> +	 * Change cmdiu->state to COMMAND_STATE_STATUS.
> +	 */
> +	else {
> +		/* Command is
> +		 * halted
> +		 * aborted
> +		 * reset or
> +		 * disconnect bit is set.
> +		 */
> +		if (cmdiu->state == COMMAND_STATE_ABORTED ||
> +		    cmdiu->state == COMMAND_STATE_HALTED ||
> +		    test_bit(US_FLIDX_RESETTING, &us->dflags) ||
> +		    test_bit(US_FLIDX_DISCONNECTING, &us->dflags))
> +			;
> +		else
> +			cmdiu->state = COMMAND_STATE_STATUS;
> +	}
> +
> +	cmdiu->iobuf_sts = REQ_COMPLETED;
> +	us->active_requests--;
> +	us->pending_requests++;
> +	spin_unlock_irqrestore(&us->lock, flags);
> +
> +	wake_up(&us->uasp_wq);
> +}
> +
> +static struct cmd_iu *usb_stor_find_cmd_iu_by_tag(
> +					struct list_head *scsi_cmnd_queue,
> +					__u16 ipt_tag)
> +{
> +	struct cmd_iu *cmdiu = 0;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	list_for_each_entry(cmdiu, scsi_cmnd_queue, node)
> +		if (cmdiu->ipt_tag == ipt_tag)
> +			return cmdiu;
> +
> +	return 0;
> +}
> +
> +static struct stor_iobuf *usb_stor_get_iobuf(struct us_data *us)
> +{
> +	int i;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	for (i = 0; i < MAX_IOBUF_COUNT; i++) {
> +		if (us->iobufs[i].sts == STOR_IOBUF_STATE_FREE) {
> +			us->iobufs[i].sts = STOR_IOBUF_STATE_BUSY;
> +			return &us->iobufs[i];
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static struct stor_urb *usb_stor_get_urb(struct us_data *us)
> +{
> +	int i;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	for (i = 0; i < MAX_URB_COUNT; i++) {
> +		if (us->urbs[i].sts == STOR_URB_STATE_FREE) {
> +			us->urbs[i].sts = STOR_URB_STATE_BUSY;
> +			return &us->urbs[i];
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static struct stor_sg_req *usb_stor_get_sg_req(struct us_data *us)
> +{
> +	int i;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	for (i = 0; i < MAX_SG_REQ_COUNT; i++) {
> +		if (us->sg_reqs[i].sts == STOR_SG_REQ_STATE_FREE) {
> +			us->sg_reqs[i].sts = STOR_SG_REQ_STATE_BUSY;
> +			return &us->sg_reqs[i];
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void usb_stor_update_scsi_cmnd_queue(struct us_data *us)
> +{
> +	struct cmd_iu *cmdiu1;
> +	struct cmd_iu *cmdiu2;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	list_for_each_entry_safe(cmdiu1, cmdiu2,
> +				&us->temp_scsi_cmnd_queue, node) {
> +		list_del(&cmdiu1->node);
> +		list_add_tail(&cmdiu1->node, &us->scsi_cmnd_queue);
> +	}
> +}
> +
> +static int usb_stor_check_scsi_cmnd(struct us_data *us, struct cmd_iu *cmdiu)
> +{
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	/* Reject the command if the direction indicator is UNKNOWN */
> +	if (cmdiu->cmd->sc_data_direction == DMA_BIDIRECTIONAL) {
> +		US_DEBUGP("%s - UNKNOWN data direction\n", __func__);
> +		cmdiu->cmd->result = DID_ERROR << 16;
> +		return -1;
> +	}
> +	/* Reject if target != 0 */
> +	else if (cmdiu->cmd->device->id) {
> +		US_DEBUGP("%s - Bad target number (%d:%d)\n",
> +			  __func__,
> +			  cmdiu->cmd->device->id,
> +			  cmdiu->cmd->device->lun);
> +		cmdiu->cmd->result = DID_BAD_TARGET << 16;
> +		return -1;
> +	}
> +	/* or if LUN is higher than the maximum known LUN */
> +	else if (cmdiu->cmd->device->lun > us->max_lun) {
> +		US_DEBUGP("%s - Bad LUN (%d:%d)\n",
> +			  __func__,
> +			  cmdiu->cmd->device->id,
> +			  cmdiu->cmd->device->lun);
> +		cmdiu->cmd->result = DID_BAD_TARGET << 16;
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int usb_stor_process_scsi_cmnd(struct us_data *us, struct cmd_iu *cmdiu)
> +{
> +	unsigned int transfer_length;
> +	unsigned int pipe;
> +	unsigned long flags;
> +	int result = 0;
> +	int status = 0;
> +	struct s_iu *siu = NULL;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +start:
> +	switch (cmdiu->state) {
> +	case COMMAND_STATE_IDLE:
> +		/* If the command is incorrest */
> +		if (usb_stor_check_scsi_cmnd(us, cmdiu)) {
> +			scsi_lock(us_to_host(us));
> +			cmdiu->cmd->scsi_done(cmdiu->cmd);
> +			cmdiu->iobuf->sts = STOR_IOBUF_STATE_FREE;
> +			cmdiu->urb->sts = STOR_URB_STATE_FREE;
> +			cmdiu->sg_req->sts = STOR_SG_REQ_STATE_FREE;
> +			list_del(&cmdiu->node);
> +			kfree(cmdiu);
> +			scsi_unlock(us_to_host(us));
> +			return result;
> +		}
> +
> +		/* Move to command state */
> +		cmdiu->state = COMMAND_STATE_COMMAND;
> +		cmdiu->iobuf_sts = REQ_NOT_SUBMITTED;
> +		cmdiu->sgreq_sts = REQ_NOT_SUBMITTED;
> +		goto start;
> +
> +	case COMMAND_STATE_COMMAND:
> +		/* Create the command buffer and submit the urb */
> +		if (cmdiu->iobuf_sts == REQ_NOT_SUBMITTED) {
> +			us->command_pipe_sts = COMMAND_PIPE_BUSY;
> +			memcpy(cmdiu->iobuf->buf,
> +			       (unsigned char *)cmdiu,
> +			       COMMAND_IU_SIZE);
> +
> +			status = usb_stor_transfer_UASP_buf(us,
> +						us->command_pipe,
> +						cmdiu->urb->req,
> +						cmdiu->iobuf,
> +						COMMAND_IU_SIZE,
> +						0,
> +						usb_stor_cmd_urb_complete,
> +						cmdiu);
> +
> +			if (status) {
> +				us->command_pipe_sts = COMMAND_PIPE_IDLE;
> +				cmdiu->state = COMMAND_STATE_COMPLETED;
> +				result = interpret_urb_result(us,
> +					us->command_pipe,
> +					COMMAND_IU_SIZE,
> +					status,
> +					cmdiu->urb->req->actual_length);
> +			} else {
> +				spin_lock_irqsave(&us->lock, flags);
> +				us->active_requests++;
> +
> +				if (cmdiu->iobuf_sts == REQ_NOT_SUBMITTED) {
> +					US_DEBUGP("%s URB is not completed\n",
> +						__func__);
> +					cmdiu->iobuf_sts = REQ_IN_PROGRESS;
> +				} else {
> +					US_DEBUGP("%s URB is completed\n",
> +						__func__);
> +				}
> +				spin_unlock_irqrestore(&us->lock, flags);
> +			}
> +		}
> +		/* Do nothing if the submitted urb is in progress */
> +		else if (cmdiu->iobuf_sts == REQ_IN_PROGRESS)
> +			;
> +		/* Submitted urb is completed
> +		 * 1. Check for errors, if any, return it.
> +		 * 2. If no any error move to the data or status stage.
> +		 */
> +		else if (cmdiu->iobuf_sts == REQ_COMPLETED) {
> +			transfer_length = scsi_bufflen(cmdiu->cmd);
> +
> +			result = interpret_urb_result(us,
> +					us->command_pipe,
> +					COMMAND_IU_SIZE,
> +					cmdiu->urb->req->status,
> +					cmdiu->urb->req->actual_length);
> +
> +			cmdiu->iobuf_sts = REQ_NOT_SUBMITTED;
> +
> +			if (result == USB_STOR_XFER_GOOD) {
> +				if (transfer_length)
> +					cmdiu->state = COMMAND_STATE_DATA;
> +				else
> +					cmdiu->state = COMMAND_STATE_STATUS;
> +
> +				goto start;
> +			} else
> +				cmdiu->state = COMMAND_STATE_COMPLETED;
> +		}
> +		break;
> +
> +	case COMMAND_STATE_DATA:
> +		if (cmdiu->sgreq_sts == REQ_NOT_SUBMITTED) {
> +			/* Run work, which will process the sg request */
> +			cmdiu->sgreq_sts = REQ_IN_PROGRESS;
> +			queue_work(us->sg_wq, &cmdiu->sg_req->work);
> +
> +			spin_lock_irqsave(&us->lock, flags);
> +			us->active_requests++;
> +			spin_unlock_irqrestore(&us->lock, flags);
> +
> +			/*
> +			 * Submit buffer on status endpoint too,
> +			 * maybe device will response with error
> +			 * status and not start the data stage.
> +			 */
> +			status = usb_stor_transfer_UASP_buf(us,
> +						us->status_pipe,
> +						cmdiu->urb->req,
> +						cmdiu->iobuf,
> +						SENSE_IU_SIZE,
> +						be16_to_cpu(cmdiu->ipt_tag),
> +						usb_stor_status_urb_complete,
> +						cmdiu);
> +
> +			if (status) {
> +				result = interpret_urb_result(us,
> +					us->status_pipe,
> +					SENSE_IU_SIZE,
> +					status,
> +					cmdiu->urb->req->actual_length);
> +			} else {
> +				spin_lock_irqsave(&us->lock, flags);
> +				if (cmdiu->iobuf_sts == REQ_NOT_SUBMITTED) {
> +					US_DEBUGP("%s URB is not completed\n",
> +						__func__);
> +					cmdiu->iobuf_sts = REQ_IN_PROGRESS;
> +				} else
> +					US_DEBUGP("%s URB is completed\n",
> +						__func__);
> +
> +				us->active_requests++;
> +				spin_unlock_irqrestore(&us->lock, flags);
> +			}
> +		}
> +		/* Do nothing if the submitted sg_req is in progress */
> +		else if (cmdiu->sgreq_sts == REQ_IN_PROGRESS)
> +			;
> +		/* Submitted sg_req is completed
> +		 * 1. Check for errors, if any, return it.
> +		 * 2. If no any error move to status stage.
> +		 */
> +		else if (cmdiu->sgreq_sts == REQ_COMPLETED) {
> +			pipe = cmdiu->cmd->sc_data_direction ==
> +				DMA_FROM_DEVICE ? us->recv_bulk_pipe :
> +				us->send_bulk_pipe;
> +			transfer_length = scsi_bufflen(cmdiu->cmd);
> +			cmdiu->sgreq_sts = REQ_NOT_SUBMITTED;
> +
> +			if (cmdiu->sg_req->result)
> +				result = USB_STOR_XFER_ERROR;
> +			else {
> +				result = interpret_urb_result(us,
> +						pipe,
> +						transfer_length,
> +						cmdiu->sg_req->sg_req.status,
> +						cmdiu->sg_req->sg_req.bytes);
> +
> +				if (result == USB_STOR_XFER_ERROR) {
> +					result = USB_STOR_TRANSPORT_ERROR;
> +				} else {
> +					result = USB_STOR_XFER_GOOD;
> +					cmdiu->state = COMMAND_STATE_STATUS;
> +					goto start;
> +				}
> +			}
> +		}
> +		break;
> +
> +	case COMMAND_STATE_STATUS:
> +		if (cmdiu->iobuf_sts == REQ_NOT_SUBMITTED) {
> +			status = usb_stor_transfer_UASP_buf(us,
> +						us->status_pipe,
> +						cmdiu->urb->req,
> +						cmdiu->iobuf,
> +						SENSE_IU_SIZE,
> +						be16_to_cpu(cmdiu->ipt_tag),
> +						usb_stor_status_urb_complete,
> +						cmdiu);
> +
> +			if (status) {
> +				result = interpret_urb_result(us,
> +					us->status_pipe,
> +					SENSE_IU_SIZE,
> +					status,
> +					cmdiu->urb->req->actual_length);
> +			} else {
> +				spin_lock_irqsave(&us->lock, flags);
> +				if (cmdiu->iobuf_sts == REQ_NOT_SUBMITTED) {
> +					US_DEBUGP("%s URB is not completed\n",
> +						__func__);
> +					cmdiu->iobuf_sts = REQ_IN_PROGRESS;
> +				} else
> +					US_DEBUGP("%s URB is completed\n",
> +						__func__);
> +
> +				us->active_requests++;
> +				spin_unlock_irqrestore(&us->lock, flags);
> +			}
> +		} else if (cmdiu->iobuf_sts == REQ_IN_PROGRESS)
> +			;
> +		else if (cmdiu->iobuf_sts == REQ_COMPLETED) {
> +			/*
> +			 * Sense iu received before DATA stage,
> +			 * that means something goes wrong on device side
> +			 */
> +			spin_lock_irqsave(&us->lock, flags);
> +			if (cmdiu->sgreq_sts == REQ_IN_PROGRESS) {
> +				US_DEBUGP("%s Sense IU completes early\n",
> +					__func__);
> +				siu = (struct s_iu *)cmdiu->iobuf->buf;
> +
> +				/* Sense iu with error. Cancel sg_list on the
> +				 * data pipe. After the sg_list proper
> +				 * cancelation return to the status stage and
> +				 * complete the command.
> +				 */
> +				if (!cmdiu->urb->req->status &&
> +				     siu->status != STATUS_GOOD)
> +					cmdiu->iobuf_sts = REQ_IN_PROGRESS;
> +
> +				spin_unlock_irqrestore(&us->lock, flags);
> +				US_DEBUGP("%s Cancelling sg request\n",
> +					__func__);
> +				usb_sg_cancel(&cmdiu->sg_req->sg_req);
> +
> +				if (!cmdiu->urb->req->status)
> +					break;
> +
> +				spin_lock_irqsave(&us->lock, flags);
> +			}
> +			spin_unlock_irqrestore(&us->lock, flags);
> +
> +			result = interpret_urb_result(us,
> +						us->command_pipe,
> +						SENSE_IU_SIZE,
> +						cmdiu->urb->req->status,
> +						cmdiu->urb->req->actual_length);
> +
> +			cmdiu->iobuf_sts = REQ_NOT_SUBMITTED;
> +			cmdiu->state = COMMAND_STATE_COMPLETED;
> +
> +			if (result == USB_STOR_XFER_GOOD)
> +				goto start;
> +		}
> +		break;
> +
> +	case COMMAND_STATE_COMPLETED:
> +		scsi_lock(us_to_host(us));
> +		siu = (struct s_iu *)cmdiu->iobuf->buf;
> +
> +		/* Status is GOOD */
> +		if (siu->status == STATUS_GOOD)
> +			cmdiu->cmd->result = SAM_STAT_GOOD;
> +		/* Status is CHECK CONDITION. Provide with the sense data */
> +		else if (siu->status == STATUS_CHECK_CONDITION) {
> +			memset(cmdiu->cmd->sense_buffer, 0, 18);
> +			cmdiu->cmd->sense_buffer[0] = 0x70;
> +			cmdiu->cmd->sense_buffer[2] = siu->sense_data[0];
> +			cmdiu->cmd->sense_buffer[7] = 10;
> +			cmdiu->cmd->sense_buffer[12] = siu->sense_data[1];
> +			cmdiu->cmd->sense_buffer[13] = siu->sense_data[2];
> +			cmdiu->cmd->result = SAM_STAT_CHECK_CONDITION;
> +		}
> +
> +		cmdiu->cmd->scsi_done(cmdiu->cmd);
> +		cmdiu->iobuf->sts = STOR_IOBUF_STATE_FREE;
> +		cmdiu->urb->sts = STOR_URB_STATE_FREE;
> +		cmdiu->sg_req->sts = STOR_SG_REQ_STATE_FREE;
> +		list_del(&cmdiu->node);
> +		kfree(cmdiu);
> +		scsi_unlock(us_to_host(us));
> +		break;
> +
> +	case COMMAND_STATE_ABORTED:
> +		/* If the command is aborted, we should not call scsi_done() */
> +		if (cmdiu->iobuf)
> +			cmdiu->iobuf->sts = STOR_IOBUF_STATE_FREE;
> +		if (cmdiu->urb)
> +			cmdiu->urb->sts = STOR_URB_STATE_FREE;
> +		if (cmdiu->sg_req)
> +			cmdiu->sg_req->sts = STOR_SG_REQ_STATE_FREE;
> +
> +		list_del(&cmdiu->node);
> +		kfree(cmdiu);
> +		break;
> +
> +	default:
> +		US_DEBUGP("%s - Unknown cmdiu->state %d!\n",
> +			__func__,
> +			cmdiu->state);
> +	}
> +
> +	return result;
> +}
> +
> +unsigned int usb_stor_get_tag(struct us_data *us)
> +{
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	us->tag++;
> +	if (us->tag >  USB_STOR_NUM_STREAMS)
> +		us->tag = 0;
> +
> +	if (!us->tag)
> +		us->tag++;
> +
> +	return us->tag;
> +}
> +
> +int usb_stor_UASP_transport(struct scsi_cmnd *srb, struct us_data *us)
> +{
> +	struct cmd_iu *cmdiu1;
> +	struct cmd_iu *cmdiu2;
> +	unsigned long flags;
> +	int cmd_is_active = 0;
> +	int result = 0;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	spin_lock_irqsave(&us->lock, flags);
> +	us->pending_requests = 0;
> +	us->new_command = 0;
> +	spin_unlock_irqrestore(&us->lock, flags);
> +
> +	/* Move commands from temp_scsi_cmnd_queue to scsi_cmnd_queue */
> +	spin_lock_irqsave(&us->lock, flags);
> +	usb_stor_update_scsi_cmnd_queue(us);
> +	spin_unlock_irqrestore(&us->lock, flags);
> +
> +	list_for_each_entry_safe(cmdiu1, cmdiu2, &us->scsi_cmnd_queue, node) {
> +		/* Disconnect bit is set */
> +		if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +			US_DEBUGP("%s Disconnect bit is set\n", __func__);
> +			break;
> +		}
> +
> +		/* Reset bit is set */
> +		if (test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +			US_DEBUGP("%s Reset bit is set\n", __func__);
> +			break;
> +		}
> +
> +		/* If command state is idle */
> +		if (cmdiu1->state == COMMAND_STATE_IDLE) {
> +			/* If command pipe is busy */
> +			if (us->command_pipe_sts == COMMAND_PIPE_BUSY) {
> +				/* We have an unprocessed command */
> +				spin_lock_irqsave(&us->lock, flags);
> +				us->new_command = 1;
> +				spin_unlock_irqrestore(&us->lock, flags);
> +				continue;
> +			}
> +
> +			/* Tries to get buffer for this cmdiu */
> +			cmdiu1->iobuf = usb_stor_get_iobuf(us);
> +			if (cmdiu1->iobuf == 0)
> +				continue;
> +
> +			/* Tries to get urb for this cmdiu */
> +			cmdiu1->urb = usb_stor_get_urb(us);
> +			if (cmdiu1->urb == 0) {
> +				cmdiu1->iobuf->sts = STOR_IOBUF_STATE_FREE;
> +				continue;
> +			}
> +
> +			/* Tries to sg request for this cmdiu */
> +			cmdiu1->sg_req = usb_stor_get_sg_req(us);
> +			if (cmdiu1->sg_req == 0) {
> +				cmdiu1->iobuf->sts = STOR_IOBUF_STATE_FREE;
> +				cmdiu1->urb->sts = STOR_URB_STATE_FREE;
> +				continue;
> +			} else {
> +				cmdiu1->sg_req->cmdiu = cmdiu1;
> +				cmdiu1->sg_req->us = us;
> +			}
> +		}
> +		/*
> +		 * The processing of the command is halted because
> +		 * of abort task or reset nexus task management
> +		 * function processing
> +		 */
> +		if (cmdiu1->state == COMMAND_STATE_HALTED)
> +			continue;
> +
> +		/*
> +		 * Workaround - not perform other queued commands while
> +		 * curretn is active
> +		 */
> +		if (cmdiu1->state == COMMAND_STATE_COMMAND &&
> +		    cmdiu1->iobuf_sts == REQ_COMPLETED &&
> +		    cmd_is_active) {
> +			US_DEBUGP("%s - Command is sent to device, but " \
> +				"other command still active\n", __func__);
> +			continue;
> +		}
> +
> +		result = usb_stor_process_scsi_cmnd(us, cmdiu1);
> +		if (result)
> +			break;
> +
> +		if (cmdiu1->state == COMMAND_STATE_DATA ||
> +		    cmdiu1->state == COMMAND_STATE_STATUS) {
> +			cmd_is_active = 1;
> +			US_DEBUGP("%s - There is an active command\n",
> +				__func__);
> +		}
> +	}
> +
> +	return result;
> +}
> +
> +static void usb_stor_abort_task_urb_complete(struct urb *urb)
> +{
> +	unsigned long flags;
> +	struct us_data *us = urb->context;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	us->abort_task_tmf->req_sts = REQ_COMPLETED;
> +
> +	spin_lock_irqsave(&us->lock, flags);
> +	us->active_requests--;
> +	us->pending_requests++;
> +	spin_unlock_irqrestore(&us->lock, flags);
> +
> +	wake_up(&us->uasp_wq);
> +}
> +
> +static int usb_stor_abort_task(struct us_data *us)
> +{
> +	int status;
> +	int result = USB_STOR_TRANSPORT_GOOD;
> +	unsigned long flags;
> +	struct cmd_iu *cmdiu;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	if (us->abort_task_tmf->state == COMMAND_STATE_COMMAND ||
> +		us->abort_task_tmf->state == COMMAND_STATE_STATUS)
> +		goto start;
> +
> +	/* Try to find the command again */
> +	scsi_lock(us_to_host(us));
> +	usb_stor_update_scsi_cmnd_queue(us);
> +	scsi_unlock(us_to_host(us));
> +	cmdiu = usb_stor_find_cmd_iu_by_tag(
> +					&us->scsi_cmnd_queue,
> +					us->abort_task_tmf->task_tag);
> +
> +	/*
> +	 * Command IU is not found, notify to SCSI layer
> +	 * that abort is done
> +	 */
> +	if (cmdiu == NULL) {
> +		US_DEBUGP("%s Command is not found\n", __func__);
> +		us->abort_task_tmf->state = COMMAND_STATE_COMPLETED;
> +		clear_bit(US_FLIDX_ABORTING, &us->dflags);
> +		complete(&us->notify);
> +		return result;
> +	}
> +
> +	/*
> +	 * The processing of the Command IU is not started yet,
> +	 * or is finished, or aborted. Notify to SCSI layer that"
> +	 * abort is done.
> +	 */
> +	if (cmdiu->state == COMMAND_STATE_IDLE ||
> +	    cmdiu->state == COMMAND_STATE_COMPLETED ||
> +	    cmdiu->state == COMMAND_STATE_ABORTED) {
> +		US_DEBUGP("%s Processing of the command is finished\n",
> +			  __func__);
> +		cmdiu->state = COMMAND_STATE_ABORTED;
> +		us->abort_task_tmf->state = COMMAND_STATE_COMPLETED;
> +		clear_bit(US_FLIDX_ABORTING, &us->dflags);
> +		complete(&us->notify);
> +		return result;
> +	}
> +
> +	/* Get buffer for processing */
> +	us->abort_task_tmf->iobuf = usb_stor_get_iobuf(us);
> +	if (us->abort_task_tmf->iobuf == 0)
> +		return result;
> +
> +	/* Get urb for processing */
> +	us->abort_task_tmf->urb = usb_stor_get_urb(us);
> +	if (us->abort_task_tmf->urb == 0) {
> +		us->abort_task_tmf->iobuf->sts = STOR_IOBUF_STATE_FREE;
> +		return result;
> +	}
> +
> +	/* If the command pipe is busy, wait for idle */
> +	if (us->command_pipe_sts == COMMAND_PIPE_BUSY) {
> +		US_DEBUGP("%s Waiting for command pipe idle\n", __func__);
> +		wait_event(us->uasp_wq,
> +			(us->command_pipe_sts == COMMAND_PIPE_IDLE));
> +	}
> +
> +	if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +		US_DEBUGP("%s Disconnect bit is set\n", __func__);
> +		return result;
> +	}
> +
> +	if (test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +		US_DEBUGP("%s Reset bit is set\n", __func__);
> +		return result;
> +	}
> +
> +	us->abort_task_tmf->state = COMMAND_STATE_COMMAND;
> +	us->abort_task_tmf->req_sts = REQ_NOT_SUBMITTED;
> +
> +start:
> +	switch (us->abort_task_tmf->state) {
> +	case COMMAND_STATE_COMMAND:
> +		US_DEBUGP("%s Command state\n", __func__);
> +		if (us->abort_task_tmf->req_sts == REQ_NOT_SUBMITTED) {
> +			spin_lock_irqsave(&us->lock, flags);
> +			us->abort_task_tmf->cmdiu->state =
> +						COMMAND_STATE_HALTED;
> +			spin_unlock_irqrestore(&us->lock, flags);
> +
> +			/* Send abort task tmf to device */
> +			memcpy(us->abort_task_tmf->iobuf->buf,
> +				(void *)us->abort_task_tmf,
> +				TM_FUNCTION_IU_SIZE);
> +
> +			us->command_pipe_sts = COMMAND_PIPE_BUSY;
> +			status = usb_stor_transfer_UASP_buf(us,
> +					us->command_pipe,
> +					us->abort_task_tmf->urb->req,
> +					us->abort_task_tmf->iobuf,
> +					TM_FUNCTION_IU_SIZE,
> +					0,
> +					usb_stor_abort_task_urb_complete,
> +					us);
> +
> +			/* Something went wrong, need to reset */
> +			if (status) {
> +				us->command_pipe_sts = COMMAND_PIPE_IDLE;
> +				result = interpret_urb_result(us,
> +					us->command_pipe,
> +					TM_FUNCTION_IU_SIZE,
> +					status,
> +					us->abort_task_tmf->urb->req->
> +					actual_length);
> +
> +				us->abort_task_tmf->iobuf->sts =
> +						STOR_IOBUF_STATE_FREE;
> +				us->abort_task_tmf->urb->sts =
> +						STOR_URB_STATE_FREE;
> +			} else {
> +				spin_lock_irqsave(&us->lock, flags);
> +				if (us->abort_task_tmf->req_sts ==
> +							REQ_NOT_SUBMITTED) {
> +					US_DEBUGP("%s URB is not completed\n",
> +								__func__);
> +					us->abort_task_tmf->req_sts =
> +							REQ_IN_PROGRESS;
> +				} else
> +					US_DEBUGP("%s URB is completed\n",
> +								__func__);
> +
> +				us->active_requests++;
> +				spin_unlock_irqrestore(&us->lock, flags);
> +			}
> +		} else if (us->abort_task_tmf->req_sts == REQ_IN_PROGRESS)
> +			;
> +		else if (us->abort_task_tmf->req_sts == REQ_COMPLETED) {
> +			result = interpret_urb_result(us,
> +				us->command_pipe,
> +				TM_FUNCTION_IU_SIZE,
> +				us->abort_task_tmf->urb->req->status,
> +				us->abort_task_tmf->urb->req->actual_length);
> +
> +			/* Something went wrong, need to reset */
> +			if (result) {
> +				us->abort_task_tmf->iobuf->sts =
> +							STOR_IOBUF_STATE_FREE;
> +				us->abort_task_tmf->urb->sts =
> +							STOR_URB_STATE_FREE;
> +			} else {
> +				us->abort_task_tmf->state =
> +							COMMAND_STATE_STATUS;
> +				us->abort_task_tmf->req_sts =
> +							REQ_NOT_SUBMITTED;
> +				goto start;
> +			}
> +		}
> +		break;
> +
> +	case COMMAND_STATE_STATUS:
> +		US_DEBUGP("%s Status state\n", __func__);
> +
> +		if (us->abort_task_tmf->req_sts == REQ_NOT_SUBMITTED) {
> +			status = usb_stor_transfer_UASP_buf(us,
> +					us->status_pipe,
> +					us->abort_task_tmf->urb->req,
> +					us->abort_task_tmf->iobuf,
> +					RESPONSE_IU_SIZE,
> +					be16_to_cpu(us->abort_task_tmf->
> +								ipt_tag),
> +					usb_stor_abort_task_urb_complete,
> +					us);
> +
> +			if (status) {
> +				result = interpret_urb_result(us,
> +					us->status_pipe,
> +					RESPONSE_IU_SIZE,
> +					status,
> +					us->abort_task_tmf->urb->req->
> +							actual_length);
> +
> +				us->abort_task_tmf->iobuf->sts =
> +							STOR_IOBUF_STATE_FREE;
> +				us->abort_task_tmf->urb->sts =
> +							STOR_URB_STATE_FREE;
> +			} else {
> +				spin_lock_irqsave(&us->lock, flags);
> +				if (us->abort_task_tmf->req_sts ==
> +							REQ_NOT_SUBMITTED) {
> +					US_DEBUGP("%s URB is not completed\n",
> +								__func__);
> +					us->abort_task_tmf->req_sts =
> +							REQ_IN_PROGRESS;
> +				} else {
> +					US_DEBUGP("%s URB is completed\n",
> +								__func__);
> +				}
> +				us->active_requests++;
> +				spin_unlock_irqrestore(&us->lock, flags);
> +			}
> +		} else if (us->abort_task_tmf->req_sts == REQ_IN_PROGRESS)
> +			;
> +		else if (us->abort_task_tmf->req_sts == REQ_COMPLETED) {
> +			result = interpret_urb_result(us,
> +					us->status_pipe,
> +					RESPONSE_IU_SIZE,
> +					us->abort_task_tmf->urb->req->status,
> +					us->abort_task_tmf->urb->req->
> +								actual_length);
> +
> +			us->abort_task_tmf->iobuf->sts = STOR_IOBUF_STATE_FREE;
> +			us->abort_task_tmf->urb->sts = STOR_URB_STATE_FREE;
> +
> +			if (!result) {
> +				/*
> +				* Kill all the active requests
> +				* connected to the aborted COMMAND IU
> +				*/
> +				if (us->abort_task_tmf->cmdiu->iobuf_sts ==
> +							REQ_IN_PROGRESS)
> +					usb_kill_urb(us->abort_task_tmf->
> +							cmdiu->urb->req);
> +
> +				if (us->abort_task_tmf->cmdiu->sgreq_sts ==
> +							REQ_IN_PROGRESS)
> +					usb_sg_cancel(&us->abort_task_tmf->
> +							cmdiu->sg_req->sg_req);
> +				/*
> +				 * FIXME maybe here we need to add a mechanism
> +				 * for waiting of the completion of the command
> +				 * related urbs and sg_reqs.
> +				 */
> +				us->abort_task_tmf->state =
> +						COMMAND_STATE_COMPLETED;
> +				clear_bit(US_FLIDX_ABORTING, &us->dflags);
> +				complete(&us->notify);
> +			}
> +		}
> +
> +		break;
> +	}
> +
> +	return result;
> +}
> +
> +void usb_stor_kill_all_requests(struct us_data *us)
> +{
> +	struct cmd_iu *cmdiu;
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	/* Move commands from temp_scsi_cmnd_queue to scsi_cmnd_queue */
> +	scsi_lock(us_to_host(us));
> +	usb_stor_update_scsi_cmnd_queue(us);
> +	scsi_unlock(us_to_host(us));
> +
> +	/* Abort all the commands */
> +	list_for_each_entry(cmdiu, &us->scsi_cmnd_queue, node) {
> +		scsi_lock(us_to_host(us));
> +		cmdiu->state = COMMAND_STATE_ABORTED;
> +		scsi_unlock(us_to_host(us));
> +
> +		/* FIXME do we need this? */
> +		if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +			US_DEBUGP("%s Disconnect bit is set\n", __func__);
> +			cmdiu->cmd->result = DID_NO_CONNECT << 16;
> +		}
> +		/* FIXME do we need this? */
> +		if (test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +			US_DEBUGP("%s Reset bit is set\n", __func__);
> +			cmdiu->cmd->result = DID_ERROR << 16;
> +		}
> +		if (cmdiu->iobuf_sts == REQ_IN_PROGRESS) {
> +			US_DEBUGP("%s Kill the active urb\n", __func__);
> +			usb_kill_urb(cmdiu->urb->req);
> +		}
> +		if (cmdiu->sgreq_sts == REQ_IN_PROGRESS) {
> +			US_DEBUGP("%s - Kill the active sg_req\n", __func__);
> +			usb_sg_cancel(&cmdiu->sg_req->sg_req);
> +		}
> +	}
> +	/* If the abort command tm function is in progress, kill it */
> +	us->abort_task_tmf->state = COMMAND_STATE_COMPLETED;
> +
> +	if (us->abort_task_tmf->req_sts == REQ_IN_PROGRESS) {
> +		clear_bit(US_FLIDX_ABORTING, &us->dflags);
> +		usb_kill_urb(us->abort_task_tmf->urb->req);
> +		/* FIXME do we need to notify during reset? */
> +		complete(&us->notify);
> +	}
> +
> +	US_DEBUGP("%s - Waiting for completion for all aborted commands\n",
> +		  __func__);
> +	wait_event(us->uasp_wq, (us->active_requests == 0));
> +}
> +
> +static void usb_stor_complete_reset_nexus(struct urb *urb)
> +{
> +	struct completion *urb_done_ptr = urb->context;
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	complete(urb_done_ptr);
> +}
> +
> +int usb_stor_UASP_reset(struct us_data *us)
> +{
> +	long timeleft;
> +	int tag, result;
> +	struct tm_iu *tmiu;
> +	struct r_iu *riu;
> +	struct stor_iobuf *iobuf;
> +	struct stor_urb *urb;
> +	struct completion urb_done;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +		US_DEBUGP("%s Disconnect bit is set\n", __func__);
> +		return -1;
> +	}
> +
> +	/* Kill all the active requests, and wait for completion */
> +	usb_stor_kill_all_requests(us);
> +
> +	if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +		US_DEBUGP("%s Disconnect bit is set\n", __func__);
> +		return -1;
> +	}
> +
> +	/* Get the buffer */
> +	iobuf = usb_stor_get_iobuf(us);
> +	if (!iobuf)
> +		return -1;
> +
> +	/* Get the urb */
> +	urb = usb_stor_get_urb(us);
> +	if (!urb) {
> +		iobuf->sts = STOR_IOBUF_STATE_FREE;
> +		return -1;
> +	}
> +
> +	tag = usb_stor_get_tag(us);
> +
> +	/* Initialize the command buffer */
> +	tmiu = (struct tm_iu *)iobuf->buf;
> +	memset(tmiu, 0, TM_FUNCTION_IU_SIZE);
> +	tmiu->tm_iu_id = IU_ID_TASK_MANAGEMENT;
> +	tmiu->reserved1 = 0;
> +	tmiu->ipt_tag = cpu_to_be16(tag);
> +	tmiu->tm_function = 0;
> +	tmiu->reserved2 = 0;
> +	tmiu->task_tag = 0;
> +	memset(tmiu->lun, 0, 8);
> +
> +	/* Fill the URB */
> +	usb_fill_bulk_urb(urb->req,
> +			  us->pusb_dev,
> +			  us->command_pipe,
> +			  iobuf->buf,
> +			  TM_FUNCTION_IU_SIZE,
> +			  usb_stor_complete_reset_nexus,
> +			  NULL);
> +
> +	init_completion(&urb_done);
> +
> +	/* Fill the common fields in the URB */
> +	urb->req->context = &urb_done;
> +	urb->req->actual_length = 0;
> +	urb->req->error_count = 0;
> +	urb->req->status = 0;
> +	urb->req->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
> +	urb->req->stream_id = 0;
> +	urb->req->transfer_buffer = iobuf->buf;
> +	urb->req->transfer_dma = iobuf->dma;
> +
> +	result = usb_submit_urb(urb->req, GFP_NOIO);
> +	if (result)
> +		goto err;
> +
> +	US_DEBUGP("%s - Waiting for completion of the submitted urb\n",
> +		  __func__);
> +
> +	/* wait for the completion of the URB */
> +	timeleft = wait_for_completion_interruptible_timeout(&urb_done,
> +							MAX_SCHEDULE_TIMEOUT);
> +
> +	if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +		US_DEBUGP("%s Disconnect bit is set\n", __func__);
> +		goto err;
> +	}
> +
> +	if (timeleft <= 0) {
> +		usb_kill_urb(us->current_urb);
> +		goto err;
> +	}
> +
> +	if (interpret_urb_result(us,
> +				 us->command_pipe,
> +				 TM_FUNCTION_IU_SIZE,
> +				 result,
> +				 urb->req->actual_length))
> +		goto err;
> +
> +	/* Initialize the response buffer */
> +	riu = (struct r_iu *)iobuf->buf;
> +	memset(riu, 0, RESPONSE_IU_SIZE);
> +
> +	/* Fill the URB */
> +	usb_fill_bulk_urb(urb->req,
> +			  us->pusb_dev,
> +			  us->status_pipe,
> +			  iobuf->buf,
> +			  RESPONSE_IU_SIZE,
> +			  usb_stor_complete_reset_nexus,
> +			  NULL);
> +
> +	init_completion(&urb_done);
> +
> +	/* Fill the common fields in the URB */
> +	urb->req->context = &urb_done;
> +	urb->req->actual_length = 0;
> +	urb->req->error_count = 0;
> +	urb->req->status = 0;
> +	urb->req->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
> +	urb->req->stream_id = tag;
> +	urb->req->transfer_buffer = iobuf->buf;
> +	urb->req->transfer_dma = iobuf->dma;
> +
> +	result = usb_submit_urb(urb->req, GFP_NOIO);
> +	if (result)
> +		goto err;
> +
> +	US_DEBUGP("%s - Waiting for completion of the submitted urb\n",
> +		  __func__);
> +
> +	/* wait for the completion of the URB */
> +	timeleft = wait_for_completion_interruptible_timeout(
> +			&urb_done, MAX_SCHEDULE_TIMEOUT);
> +
> +	if (timeleft <= 0) {
> +		usb_kill_urb(us->current_urb);
> +		goto err;
> +	}
> +
> +	if (interpret_urb_result(us,
> +				 us->command_pipe,
> +				 RESPONSE_IU_SIZE,
> +				 result,
> +				 urb->req->actual_length))
> +		goto err;
> +
> +	iobuf->sts = STOR_IOBUF_STATE_FREE;
> +	urb->sts = STOR_URB_STATE_FREE;
> +	return 0;
> +err:
> +	US_DEBUGP("%s - Error\n", __func__);
> +
> +	iobuf->sts = STOR_IOBUF_STATE_FREE;
> +	urb->sts = STOR_URB_STATE_FREE;
> +	return -1;
> +}
> +
> +int usb_stor_UASP_max_lun(struct us_data *us)
> +{
> +	/*
> +	 * FIXME perform this action properly. REPORT LUNS SCSI command
> +	 * should be performed for getting all LUN related information.
> +	 * Currently on UASP mode assumes, that device is working with
> +	 * one LUN.
> +	 */
> +	us->max_lun = 0;
> +	return 0;
> +}
> +
> +void usb_stor_invoke_UASP_transport(struct us_data *us)
> +{
> +	int result = 0;
> +
> +	US_DEBUGP("%s called\n", __func__);
> +
> +	if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {
> +		US_DEBUGP("%s abort bit is set\n", __func__);
> +		result = usb_stor_abort_task(us);
> +		/* Transport error, do reset */
> +		if (result)
> +			goto reset;
> +	}
> +
> +	/* If Disconnect bit is set */
> +	if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +		US_DEBUGP("%s Disconnect bit is set\n", __func__);
> +		return;
> +	}
> +
> +	/* If Reset bit is set */
> +	if (test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +		US_DEBUGP("%s Reset bit is set\n", __func__);
> +		return;
> +	}
> +
> +	result = us->transport(0, us);
> +
> +	/* Transport error, do reset */
> +	if (result) {
> +		/* If Disconnect bit is set */
> +		if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +			US_DEBUGP("%s Disconnect bit is set\n", __func__);
> +			return;
> +		}
> +
> +		/* If Reset bit is set */
> +		if (test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +			US_DEBUGP("%s Reset bit is set\n", __func__);
> +			return;
> +		}
> +
> +		US_DEBUGP("%s Goto reset\n", __func__);
> +		goto reset;
> +	}
> +
> +	return;
> +reset:
> +	scsi_lock(us_to_host(us));
> +	set_bit(US_FLIDX_RESETTING, &us->dflags);
> +	scsi_unlock(us_to_host(us));
> +
> +	mutex_unlock(&us->dev_mutex);
> +	result = usb_stor_port_reset(us);
> +	mutex_lock(&us->dev_mutex);
> +
> +	if (result < 0) {
> +		scsi_lock(us_to_host(us));
> +		usb_stor_report_device_reset(us);
> +		scsi_unlock(us_to_host(us));
> +		us->transport_reset(us);
> +	}
> +	clear_bit(US_FLIDX_RESETTING, &us->dflags);
> +}
> diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
> index 242ff5e..ebb86ed 100644
> --- a/drivers/usb/storage/transport.h
> +++ b/drivers/usb/storage/transport.h
> @@ -113,13 +113,163 @@ struct bulk_cs_wrap {
>  
>  #define US_CBI_ADSC		0
>  
> +/* IU identifier summary */
> +enum iu_id {
> +	IU_ID_COMMAND			= 0x01,
> +	IU_ID_SENSE			= 0x03,
> +	IU_ID_RESPONSE			= 0x04,
> +	IU_ID_TASK_MANAGEMENT		= 0x05,
> +	IU_ID_READ_READY		= 0x06,
> +	IU_ID_WRITE_READY		= 0x07,
> +};
> +
> +/* Task Attribute field */
> +enum task_attribute_data {
> +	TASK_ATTR_SIMPLE		= 0,
> +	TASK_ATTR_HEAD_OF_QUEUE		= 1,
> +	TASK_ATTR_ORDERED		= 2,
> +	TASK_ATTR_ACA			= 4,
> +};
> +
> +/* Command or Task Management Function IU state */
> +enum command_state {
> +	COMMAND_STATE_IDLE		= 0,
> +	COMMAND_STATE_COMMAND		= 1,
> +	COMMAND_STATE_DATA		= 2,
> +	COMMAND_STATE_STATUS		= 3,
> +	COMMAND_STATE_ABORTED		= 4,
> +	COMMAND_STATE_COMPLETED		= 5,
> +	COMMAND_STATE_HALTED		= 6,
> +};
> +
> +#define COMMAND_IU_SIZE		36
> +/* Command IU */
> +struct cmd_iu {
> +	__u8 cmd_iu_id;
> +	__u8 reserved;
> +	__u16 ipt_tag;
> +	__u16 length;
> +
> +	struct {
> +		unsigned int reserved:1;
> +		unsigned int task_priority:4;
> +		unsigned int task_attribute:3;
> +	} b;
> +
> +	__u8 lun[8];
> +	__u8 cdb[16];
> +	__u8 add_cdb[5];
> +
> +	struct scsi_cmnd *cmd;
> +
> +#define REQ_NOT_SUBMITTED	0
> +#define REQ_IN_PROGRESS		1
> +#define REQ_COMPLETED		2
> +	int iobuf_sts;
> +	int sgreq_sts;
> +	int state;
> +
> +	struct stor_iobuf *iobuf;
> +	struct stor_urb *urb;
> +	struct stor_sg_req *sg_req;
> +	struct us_data *us;
> +
> +	struct list_head node;
> +};
> +
> +/* Task Management Function IU types */
> +enum tm_function_data {
> +	TM_FUNCTION_ABORT_TASK			= 0x01,
> +	TM_FUNCTION_ABORT_TASK_SET		= 0x02,
> +	TM_FUNCTION_CLEAR_TASK_SET		= 0x04,
> +	TM_FUNCTION_RESET_LUN			= 0x08,
> +	TM_FUNCTION_IT_NEXUS_RESET		= 0x10,
> +	TM_FUNCTION_CLEAR_ACA			= 0x40,
> +	TM_FUNCTION_QUERY_TASK			= 0x80,
> +	TM_FUNCTION_QUERY_TASK_SET		= 0x81,
> +	TM_FUNCTION_QUERY_UNIT_ATTENTION	= 0x82,
> +};
> +
> +#define TM_FUNCTION_IU_SIZE	16
> +/* Task Management Function IU */
> +struct tm_iu {
> +	__u8 tm_iu_id;
> +	__u8 reserved1;
> +	__u16 ipt_tag;
> +	__u8 tm_function;
> +	__u8 reserved2;
> +	__u16 task_tag;
> +	__u8 lun[8];
> +
> +	struct stor_iobuf *iobuf;
> +	struct stor_urb *urb;
> +	int state;
> +	int req_sts;
> +	struct cmd_iu *cmdiu;
> +
> +	struct list_head node;
> +};
> +
> +/* Status values of Sense IU*/
> +enum status_code_data {
> +	STATUS_GOOD =			0x00,
> +	STATUS_CHECK_CONDITION =	0x02,
> +	STATUS_CONDITION_MET =		0x04,
> +	STATUS_BUSY =			0x08,
> +	STATUS_RESERVATION_CONFLICT =	0x18,
> +	STATUS_TASK_SET_FULL =		0x28,
> +	STATUS_ACA_ACTIVE =		0x30,
> +	STATUS_TASK_ABORTED =		0x40,
> +};
> +
> +#define SENSE_IU_SIZE	13
> +/* Sense IU */
> +struct s_iu {
> +	__u8 s_iu_id;
> +	__u8 reserved1;
> +	__u16 ipt_tag;
> +	__u16 length;
> +	__u8 status;
> +	__u8 reserved2;
> +	__u8 sense_data[5];
> +};
> +
> +/* Status values of Response IU */
> +enum response_code_data {
> +	RESPONSE_TM_FUNCTION_COMPLETE		= 0x00,
> +	RESPONSE_INVALID_IU			= 0x02,
> +	RESPONSE_TM_FUNCTION_NOT_SUPPORTED	= 0x04,
> +	RESPONSE_TM_FUNCTION_FAILED		= 0x05,
> +	RESPONSE_TM_FUNCTION_SUCCEEDED		= 0x08,
> +	RESPONSE_INCORRECT_LUN			= 0x09,
> +	RESPONSE_OVERLAPPED_TAG_ATTEMPTED	= 0x0A,
> +};
> +
> +#define RESPONSE_IU_SIZE	8
> +/* Response IU */
> +struct r_iu {
> +	__u8 r_iu_id;
> +	__u8 reserved;
> +	__u16 ipt_tag;
> +	__u8 resp_info[3];
> +	__u8 status;
> +};
> +
>  extern int usb_stor_CB_transport(struct scsi_cmnd *, struct us_data*);
>  extern int usb_stor_CB_reset(struct us_data*);
>  
>  extern int usb_stor_Bulk_transport(struct scsi_cmnd *, struct us_data*);
>  extern int usb_stor_Bulk_max_lun(struct us_data*);
>  extern int usb_stor_Bulk_reset(struct us_data*);
> +extern void usb_stor_transfer_UASP_sglist(struct work_struct *work);
> +extern void usb_stor_kill_all_requests(struct us_data *us);
> +extern unsigned int usb_stor_get_tag(struct us_data *us);
> +
> +extern int usb_stor_UASP_transport(struct scsi_cmnd *, struct us_data*);
> +extern int usb_stor_UASP_max_lun(struct us_data *);
> +extern int usb_stor_UASP_reset(struct us_data *);
>  
> +extern void usb_stor_invoke_UASP_transport(struct us_data *us);
>  extern void usb_stor_invoke_transport(struct scsi_cmnd *, struct us_data*);
>  extern void usb_stor_stop_transport(struct us_data*);
>  
> diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
> index d4f034e..b9809d5 100644
> --- a/drivers/usb/storage/unusual_devs.h
> +++ b/drivers/usb/storage/unusual_devs.h
> @@ -1875,3 +1875,6 @@ USUAL_DEV(US_SC_QIC, US_PR_BULK, USB_US_TYPE_STOR),
>  USUAL_DEV(US_SC_UFI, US_PR_BULK, USB_US_TYPE_STOR),
>  USUAL_DEV(US_SC_8070, US_PR_BULK, USB_US_TYPE_STOR),
>  USUAL_DEV(US_SC_SCSI, US_PR_BULK, 0),
> +
> +/* UASP transport */
> +USUAL_DEV(US_SC_SCSI, US_PR_UASP, 0),
> diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
> index 8060b85..2960f68 100644
> --- a/drivers/usb/storage/usb.c
> +++ b/drivers/usb/storage/usb.c
> @@ -256,125 +256,203 @@ void fill_inquiry_response(struct us_data *us, unsigned char *data,
>  }
>  EXPORT_SYMBOL_GPL(fill_inquiry_response);
>  
> +static int usb_stor_something_happen(struct us_data *us)
> +{
> +	/* Disconnect bit is set */
> +	if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
> +		US_DEBUGP("%s - Disconnect bit is set\n", __func__);
> +		return 1;
> +	}
> +
> +	/* Reset bit is set by SCSI or USB CORE */
> +	if (test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +		US_DEBUGP("%s - Reset bit is set\n", __func__);
> +		return 1;
> +	}
> +
> +	/* Some of the submitted requests finished */
> +	if (us->pending_requests) {
> +		US_DEBUGP("%s - There are pending requests\n", __func__);
> +		return 1;
> +	}
> +
> +	/* New command is received and command pipe is idle*/
> +	if (us->new_command && us->command_pipe_sts == COMMAND_PIPE_IDLE) {
> +		US_DEBUGP("%s - New command is received from SCSI layer\n",
> +			  __func__);
> +		return 1;
> +	}
> +
> +	/* Abort task is received from SCSI */
> +	if (us->abort_task_tmf->state == COMMAND_STATE_IDLE) {
> +		US_DEBUGP("%s - The task is aborted from SCSI layer\n",
> +			  __func__);
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
>  static int usb_stor_control_thread(void * __us)
>  {
>  	struct us_data *us = (struct us_data *)__us;
>  	struct Scsi_Host *host = us_to_host(us);
>  
> -	for(;;) {
> -		US_DEBUGP("*** thread sleeping.\n");
> -		if (wait_for_completion_interruptible(&us->cmnd_ready))
> -			break;
> -
> -		US_DEBUGP("*** thread awakened.\n");
> +	if (us->protocol == US_PR_UASP) {
> +		for (;;) {
> +			US_DEBUGP("%s Thread sleeping\n", __func__);
> +			wait_event(us->uasp_wq, usb_stor_something_happen(us));
> +			US_DEBUGP("%s Thread wakes up\n", __func__);
>  
> -		/* lock the device pointers */
> -		mutex_lock(&(us->dev_mutex));
> +			/* Disconnect bit is set */
> +			if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags))
> +				break;
>  
> -		/* lock access to the state */
> -		scsi_lock(host);
> +			/* Reset bit is set */
> +			if (test_bit(US_FLIDX_RESETTING, &us->dflags)) {
> +				wait_event(us->uasp_wq,
> +					!test_bit(US_FLIDX_RESETTING,
> +							&us->dflags));
> +			}
>  
> -		/* When we are called with no command pending, we're done */
> -		if (us->srb == NULL) {
> -			scsi_unlock(host);
> +			mutex_lock(&(us->dev_mutex));
> +			us->proto_handler(0, us);
>  			mutex_unlock(&us->dev_mutex);
> -			US_DEBUGP("-- exiting\n");
> -			break;
> -		}
> +		} /* for (;;) */
> +
> +		/*
> +		* If we get here, that means Disconnect bit is set.
> +		* Kill all active requests then clear Disconnect bit.
> +		*/
> +		usb_stor_kill_all_requests(us);
> +		clear_bit(US_FLIDX_DISCONNECTING, &us->dflags);
> +		wake_up(&us->uasp_wq);
> +	} else {
> +		for (;;) {
> +			US_DEBUGP("*** thread sleeping.\n");
> +			if (wait_for_completion_interruptible(&us->cmnd_ready))
> +				break;
>  
> -		/* has the command timed out *already* ? */
> -		if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
> -			us->srb->result = DID_ABORT << 16;
> -			goto SkipForAbort;
> -		}
> +			US_DEBUGP("*** thread awakened.\n");
>  
> -		scsi_unlock(host);
> +			/* lock the device pointers */
> +			mutex_lock(&(us->dev_mutex));
>  
> -		/* reject the command if the direction indicator 
> -		 * is UNKNOWN
> -		 */
> -		if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {
> -			US_DEBUGP("UNKNOWN data direction\n");
> -			us->srb->result = DID_ERROR << 16;
> -		}
> +			/* lock access to the state */
> +			scsi_lock(host);
>  
> -		/* reject if target != 0 or if LUN is higher than
> -		 * the maximum known LUN
> -		 */
> -		else if (us->srb->device->id && 
> -				!(us->fflags & US_FL_SCM_MULT_TARG)) {
> -			US_DEBUGP("Bad target number (%d:%d)\n",
> -				  us->srb->device->id, us->srb->device->lun);
> -			us->srb->result = DID_BAD_TARGET << 16;
> -		}
> +			/*
> +			 * When we are called with no command pending,
> +			 * we're done
> +			 */
> +			if (us->srb == NULL) {
> +				scsi_unlock(host);
> +				mutex_unlock(&us->dev_mutex);
> +				US_DEBUGP("-- exiting\n");
> +				break;
> +			}
>  
> -		else if (us->srb->device->lun > us->max_lun) {
> -			US_DEBUGP("Bad LUN (%d:%d)\n",
> -				  us->srb->device->id, us->srb->device->lun);
> -			us->srb->result = DID_BAD_TARGET << 16;
> -		}
> +			/* has the command timed out *already* ? */
> +			if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
> +				us->srb->result = DID_ABORT << 16;
> +				goto SkipForAbort;
> +			}
>  
> -		/* Handle those devices which need us to fake 
> -		 * their inquiry data */
> -		else if ((us->srb->cmnd[0] == INQUIRY) &&
> -			    (us->fflags & US_FL_FIX_INQUIRY)) {
> -			unsigned char data_ptr[36] = {
> -			    0x00, 0x80, 0x02, 0x02,
> -			    0x1F, 0x00, 0x00, 0x00};
> -
> -			US_DEBUGP("Faking INQUIRY command\n");
> -			fill_inquiry_response(us, data_ptr, 36);
> -			us->srb->result = SAM_STAT_GOOD;
> -		}
> +			scsi_unlock(host);
>  
> -		/* we've got a command, let's do it! */
> -		else {
> -			US_DEBUG(usb_stor_show_command(us->srb));
> -			us->proto_handler(us->srb, us);
> -		}
> +			/* reject the command if the direction indicator
> +			* is UNKNOWN
> +			*/
> +			if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {
> +				US_DEBUGP("UNKNOWN data direction\n");
> +				us->srb->result = DID_ERROR << 16;
> +			}
> +
> +			/* reject if target != 0 or if LUN is higher than
> +			* the maximum known LUN
> +			*/
> +			else if (us->srb->device->id &&
> +					!(us->fflags & US_FL_SCM_MULT_TARG)) {
> +				US_DEBUGP("Bad target number (%d:%d)\n",
> +						us->srb->device->id,
> +						us->srb->device->lun);
> +				us->srb->result = DID_BAD_TARGET << 16;
> +			}
>  
> -		/* lock access to the state */
> -		scsi_lock(host);
> +			else if (us->srb->device->lun > us->max_lun) {
> +				US_DEBUGP("Bad LUN (%d:%d)\n",
> +					us->srb->device->id,
> +					us->srb->device->lun);
> +				us->srb->result = DID_BAD_TARGET << 16;
> +			}
>  
> -		/* indicate that the command is done */
> -		if (us->srb->result != DID_ABORT << 16) {
> -			US_DEBUGP("scsi cmd done, result=0x%x\n", 
> -				   us->srb->result);
> -			us->srb->scsi_done(us->srb);
> -		} else {
> +			/* Handle those devices which need us to fake
> +			* their inquiry data */
> +			else if ((us->srb->cmnd[0] == INQUIRY) &&
> +				(us->fflags & US_FL_FIX_INQUIRY)) {
> +				unsigned char data_ptr[36] = {
> +				0x00, 0x80, 0x02, 0x02,
> +				0x1F, 0x00, 0x00, 0x00};
> +
> +				US_DEBUGP("Faking INQUIRY command\n");
> +				fill_inquiry_response(us, data_ptr, 36);
> +				us->srb->result = SAM_STAT_GOOD;
> +			}
> +
> +			/* we've got a command, let's do it! */
> +			else {
> +				US_DEBUG(usb_stor_show_command(us->srb));
> +				us->proto_handler(us->srb, us);
> +			}
> +
> +			/* lock access to the state */
> +			scsi_lock(host);
> +
> +			/* indicate that the command is done */
> +			if (us->srb->result != DID_ABORT << 16) {
> +				US_DEBUGP("scsi cmd done, result=0x%x\n",
> +					us->srb->result);
> +				us->srb->scsi_done(us->srb);
> +			} else {
>  SkipForAbort:
> -			US_DEBUGP("scsi command aborted\n");
> -		}
> +				US_DEBUGP("scsi command aborted\n");
> +			}
>  
> -		/* If an abort request was received we need to signal that
> -		 * the abort has finished.  The proper test for this is
> -		 * the TIMED_OUT flag, not srb->result == DID_ABORT, because
> -		 * the timeout might have occurred after the command had
> -		 * already completed with a different result code. */
> -		if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
> -			complete(&(us->notify));
> -
> -			/* Allow USB transfers to resume */
> -			clear_bit(US_FLIDX_ABORTING, &us->dflags);
> -			clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
> -		}
> +			/*
> +			 * If an abort request was received we need to signal
> +			 * that the abort has finished.  The proper test for
> +			 * this is the TIMED_OUT flag, not srb->result ==
> +			 * DID_ABORT, because the timeout might have occurred
> +			 * after the command had already completed with a
> +			 * different result code.
> +			 */
> +			if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
> +				complete(&(us->notify));
> +
> +				/* Allow USB transfers to resume */
> +				clear_bit(US_FLIDX_ABORTING, &us->dflags);
> +				clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
> +			}
>  
> -		/* finished working on this command */
> -		us->srb = NULL;
> -		scsi_unlock(host);
> +			/* finished working on this command */
> +			us->srb = NULL;
> +			scsi_unlock(host);
>  
> -		/* unlock the device pointers */
> -		mutex_unlock(&us->dev_mutex);
> -	} /* for (;;) */
> +			/* unlock the device pointers */
> +			mutex_unlock(&us->dev_mutex);
> +		} /* for (;;) */
>  
> -	/* Wait until we are told to stop */
> -	for (;;) {
> -		set_current_state(TASK_INTERRUPTIBLE);
> -		if (kthread_should_stop())
> -			break;
> -		schedule();
> +		/* Wait until we are told to stop */
> +		for (;;) {
> +			set_current_state(TASK_INTERRUPTIBLE);
> +			if (kthread_should_stop())
> +				break;
> +			schedule();
> +		}
> +		__set_current_state(TASK_RUNNING);
>  	}
> -	__set_current_state(TASK_RUNNING);
> +
> +	US_DEBUGP("%s - Thread exits\n", __func__);
>  	return 0;
>  }	
>  
> @@ -385,6 +463,7 @@ SkipForAbort:
>  /* Associate our private data with the USB device */
>  static int associate_dev(struct us_data *us, struct usb_interface *intf)
>  {
> +	int i;
>  	US_DEBUGP("-- %s\n", __func__);
>  
>  	/* Fill in the device-related fields */
> @@ -416,6 +495,20 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf)
>  		US_DEBUGP("I/O buffer allocation failed\n");
>  		return -ENOMEM;
>  	}
> +
> +	/* Allocate in/out buffers for UASP needs */
> +	for (i = 0; i < MAX_IOBUF_COUNT; i++) {
> +		us->iobufs[i].sts = STOR_IOBUF_STATE_FREE;
> +		us->iobufs[i].buf = usb_buffer_alloc(us->pusb_dev,
> +						US_IOBUF_SIZE,
> +						GFP_KERNEL,
> +						&us->iobufs[i].dma);
> +
> +		if (!us->iobufs[i].buf) {
> +			US_DEBUGP("UASP I/O buffer allocation failed\n");
> +			return -ENOMEM;
> +		}
> +	}
>  	return 0;
>  }
>  
> @@ -588,6 +681,12 @@ static void get_transport(struct us_data *us)
>  		us->transport = usb_stor_Bulk_transport;
>  		us->transport_reset = usb_stor_Bulk_reset;
>  		break;
> +
> +	case US_PR_UASP:
> +		us->transport_name = "UASP";
> +		us->transport = usb_stor_UASP_transport;
> +		us->transport_reset = usb_stor_UASP_reset;
> +		break;
>  	}
>  }
>  
> @@ -640,12 +739,45 @@ static int get_pipes(struct us_data *us)
>  	struct usb_endpoint_descriptor *ep_in = NULL;
>  	struct usb_endpoint_descriptor *ep_out = NULL;
>  	struct usb_endpoint_descriptor *ep_int = NULL;
> +	struct usb_endpoint_descriptor *ep_cmnd = NULL;
> +	struct usb_endpoint_descriptor *ep_sts = NULL;
> +	struct usb_host_endpoint *eps[3];
> +
> +	/* Allocate streams in case of UASP */
> +	if (us->protocol == US_PR_UASP) {
> +		for (i = 1; i < altsetting->desc.bNumEndpoints; i++)
> +			eps[i - 1] = &altsetting->endpoint[i];
> +
> +		i = usb_alloc_streams(us->pusb_intf,
> +					eps,
> +					3,
> +					USB_STOR_NUM_STREAMS,
> +					GFP_KERNEL);
> +		if (i < 0) {
> +			US_DEBUGP("Cannot allocate streams\n");
> +			return i;
> +		}
> +	}
>  
>  	/*
>  	 * Find the first endpoint of each type we need.
>  	 * We are expecting a minimum of 2 endpoints - in and out (bulk).
>  	 * An optional interrupt-in is OK (necessary for CBI protocol).
> +	 * In case of UASP we will need additional 2 bulk endpoints.
>  	 * We will ignore any others.
> +	 *
> +	 * In current version of UASP implementation not included support
> +	 * of Pipe Usage Descriptors. Therefore by somehow we need to know
> +	 * which pipe for what should be used. For example from device we
> +	 * will receive two In Endpoint Descriptors(Bulk In and Status), but
> +	 * we cannot identify which one is for Bulk In and which one is for
> +	 * Status. For that purposes in current UASP implementation assumes
> +	 * that Endpoint Descriptors received from device side should be in
> +	 * the following order.
> +	 * 1. Command Endpoint Descriptor.
> +	 * 2. Bulk In Endpoint Descriptor.
> +	 * 3. Bulk Out Endpoint Descriptor.
> +	 * 4. Status Endpoint Descriptor.
>  	 */
>  	for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
>  		ep = &altsetting->endpoint[i].desc;
> @@ -654,8 +786,13 @@ static int get_pipes(struct us_data *us)
>  			if (usb_endpoint_dir_in(ep)) {
>  				if (!ep_in)
>  					ep_in = ep;
> +				else if (us->protocol == US_PR_UASP &&
> +					!ep_sts)
> +					ep_sts = ep;
>  			} else {
> -				if (!ep_out)
> +				if (us->protocol == US_PR_UASP && !ep_cmnd)
> +					ep_cmnd = ep;
> +				else if (!ep_out)
>  					ep_out = ep;
>  			}
>  		}
> @@ -666,7 +803,8 @@ static int get_pipes(struct us_data *us)
>  		}
>  	}
>  
> -	if (!ep_in || !ep_out || (us->protocol == US_PR_CBI && !ep_int)) {
> +	if (!ep_in || !ep_out || (us->protocol == US_PR_CBI && !ep_int) ||
> +		((!ep_sts || !ep_cmnd) && us->protocol == US_PR_UASP)) {
>  		US_DEBUGP("Endpoint sanity check failed! Rejecting dev.\n");
>  		return -EIO;
>  	}
> @@ -678,6 +816,17 @@ static int get_pipes(struct us_data *us)
>  		usb_endpoint_num(ep_out));
>  	us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev, 
>  		usb_endpoint_num(ep_in));
> +	/* UASP command pipe */
> +	if (ep_cmnd) {
> +		us->command_pipe = usb_sndbulkpipe(us->pusb_dev,
> +						   usb_endpoint_num(ep_cmnd));
> +		us->command_pipe_sts = COMMAND_PIPE_IDLE;
> +	}
> +	/* UASP status pipe */
> +	if (ep_sts) {
> +		us->status_pipe = usb_rcvbulkpipe(us->pusb_dev,
> +						  usb_endpoint_num(ep_sts));
> +	}
>  	if (ep_int) {
>  		us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,
>  			usb_endpoint_num(ep_int));
> @@ -698,6 +847,21 @@ static int usb_stor_acquire_resources(struct us_data *us)
>  		return -ENOMEM;
>  	}
>  
> +	/* Allocate urbs for UASP*/
> +	for (p = 0; p < MAX_URB_COUNT; p++) {
> +		us->urbs[p].sts = STOR_SG_REQ_STATE_FREE;
> +
> +		us->urbs[p].req = usb_alloc_urb(0, GFP_KERNEL);
> +		if (!us->urbs[p].req) {
> +			US_DEBUGP("URB allocation for UASP failed\n");
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	/* Initialize works for UASP Scatter-Gather requests */
> +	for (p = 0; p < MAX_SG_REQ_COUNT; p++)
> +		INIT_WORK(&us->sg_reqs[p].work, usb_stor_transfer_UASP_sglist);
> +
>  	/* Just before we start our control thread, initialize
>  	 * the device if it needs initialization */
>  	if (us->unusual_dev->initFunction) {
> @@ -718,9 +882,22 @@ static int usb_stor_acquire_resources(struct us_data *us)
>  	return 0;
>  }
>  
> +/* Releases the given Command IU queue */
> +static void release_scsi_cmnd_queue(struct list_head *queue)
> +{
> +	struct cmd_iu *cmdiu1;
> +	struct cmd_iu *cmdiu2;
> +
> +	list_for_each_entry_safe(cmdiu1, cmdiu2, queue, node) {
> +		list_del(&cmdiu1->node);
> +		kfree(cmdiu1);
> +	}
> +}
> +
>  /* Release all our dynamic resources */
>  static void usb_stor_release_resources(struct us_data *us)
>  {
> +	int i;
>  	US_DEBUGP("-- %s\n", __func__);
>  
>  	/* Tell the control thread to exit.  The SCSI host must
> @@ -728,9 +905,16 @@ static void usb_stor_release_resources(struct us_data *us)
>  	 * so that we won't accept any more commands.
>  	 */
>  	US_DEBUGP("-- sending exit command to thread\n");
> -	complete(&us->cmnd_ready);
> -	if (us->ctl_thread)
> -		kthread_stop(us->ctl_thread);
> +
> +	if (us->protocol != US_PR_UASP) {
> +		complete(&us->cmnd_ready);
> +		if (us->ctl_thread)
> +			kthread_stop(us->ctl_thread);
> +	} else {
> +		wake_up(&us->uasp_wq);
> +		wait_event(us->uasp_wq,
> +			!test_bit(US_FLIDX_DISCONNECTING, &us->dflags));
> +	}
>  
>  	/* Call the destructor routine, if it exists */
>  	if (us->extra_destructor) {
> @@ -741,11 +925,29 @@ static void usb_stor_release_resources(struct us_data *us)
>  	/* Free the extra data and the URB */
>  	kfree(us->extra);
>  	usb_free_urb(us->current_urb);
> +
> +	/* Destroy workqueue */
> +	if (us->sg_wq)
> +		destroy_workqueue(us->sg_wq);
> +
> +	/* Free UASP related URB-s */
> +	for (i = 0; i < MAX_URB_COUNT; i++)
> +		usb_free_urb(us->urbs[i].req);
> +
> +	kfree(us->abort_task_tmf);
> +
> +	/* Release all Command IU queues */
> +	release_scsi_cmnd_queue(&us->scsi_cmnd_queue);
> +	release_scsi_cmnd_queue(&us->temp_scsi_cmnd_queue);
>  }
>  
>  /* Dissociate from the USB device */
>  static void dissociate_dev(struct us_data *us)
>  {
> +	int i;
> +	struct usb_host_endpoint *eps[3];
> +	struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting;
> +
>  	US_DEBUGP("-- %s\n", __func__);
>  
>  	/* Free the device-related DMA-mapped buffers */
> @@ -756,6 +958,24 @@ static void dissociate_dev(struct us_data *us)
>  		usb_buffer_free(us->pusb_dev, US_IOBUF_SIZE, us->iobuf,
>  				us->iobuf_dma);
>  
> +	/* Release allocated streams */
> +	if (us->protocol == US_PR_UASP) {
> +		for (i = 1; i < altsetting->desc.bNumEndpoints; i++)
> +			eps[i - 1] = &altsetting->endpoint[i];
> +
> +		usb_free_streams(us->pusb_intf, eps, 3, GFP_KERNEL);
> +	}
> +
> +	/* Release In/Out buffers allocated for UASP */
> +	for (i = 0; i < MAX_IOBUF_COUNT; i++) {
> +		if (us->iobufs[i].buf) {
> +			usb_buffer_free(us->pusb_dev,
> +				US_IOBUF_SIZE,
> +				us->iobufs[i].buf,
> +				us->iobufs[i].dma);
> +		}
> +	}
> +
>  	/* Remove our private data from the interface */
>  	usb_set_intfdata(us->pusb_intf, NULL);
>  }
> @@ -831,6 +1051,12 @@ static int usb_stor_scan_thread(void * __us)
>  			us->max_lun = usb_stor_Bulk_max_lun(us);
>  			mutex_unlock(&us->dev_mutex);
>  		}
> +		/* In case of UASP */
> +		else if (us->protocol == US_PR_UASP) {
> +			mutex_lock(&us->dev_mutex);
> +			us->max_lun = usb_stor_UASP_max_lun(us);
> +			mutex_unlock(&us->dev_mutex);
> +		}
>  		scsi_scan_host(us_to_host(us));
>  		printk(KERN_DEBUG "usb-storage: device scan complete\n");
>  
> @@ -876,6 +1102,25 @@ int usb_stor_probe1(struct us_data **pus,
>  	init_waitqueue_head(&us->delay_wait);
>  	init_completion(&us->scanning_done);
>  
> +	init_waitqueue_head(&us->uasp_wq);
> +	INIT_LIST_HEAD(&us->scsi_cmnd_queue);
> +	INIT_LIST_HEAD(&us->temp_scsi_cmnd_queue);
> +	spin_lock_init(&us->lock);
> +
> +	us->abort_task_tmf = kzalloc(sizeof(struct tm_iu), GFP_KERNEL);
> +	if (!us->abort_task_tmf) {
> +		result = -ENOMEM;
> +		goto BadDevice;
> +	}
> +	us->abort_task_tmf->state = COMMAND_STATE_COMPLETED;
> +
> +	US_DEBUGP("Create workqueue\n");
> +	us->sg_wq = create_singlethread_workqueue("USB Storage WQ");
> +	if (us->sg_wq == NULL) {
> +		result = -ENOMEM;
> +		goto BadDevice;
> +	}
> +
>  	/* Associate the us_data structure with the USB device */
>  	result = associate_dev(us, intf);
>  	if (result)
> diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
> index 2609efb..b9a7f17 100644
> --- a/drivers/usb/storage/usb.h
> +++ b/drivers/usb/storage/usb.h
> @@ -95,6 +95,47 @@ typedef void (*pm_hook)(struct us_data *, int);	/* power management hook */
>  #define US_SUSPEND	0
>  #define US_RESUME	1
>  
> +/* Number of streams we need to allocate */
> +#define USB_STOR_NUM_STREAMS	2
> +
> +/* Max number of Command IU-s could be queued */
> +#define MAX_COMMAND_COUNT	16
> +/* Max number of In/Out buffers */
> +#define MAX_IOBUF_COUNT		(MAX_COMMAND_COUNT << 1)
> +/* Max number of URBs */
> +#define MAX_URB_COUNT		(MAX_COMMAND_COUNT << 1)
> +/* Max number of Scatter-Gather requests */
> +#define MAX_SG_REQ_COUNT	(MAX_COMMAND_COUNT << 1)
> +
> +#define STOR_IOBUF_STATE_FREE	0
> +#define STOR_IOBUF_STATE_BUSY	1
> +/* used as an In/Out buffers for UASP only */
> +struct stor_iobuf {
> +	unsigned char	*buf;
> +	dma_addr_t	dma;
> +	int		sts;
> +};
> +
> +#define STOR_URB_STATE_FREE	0
> +#define STOR_URB_STATE_BUSY	1
> +/* used as an URB for UASP only */
> +struct stor_urb {
> +	struct urb		*req;
> +	int			sts;
> +};
> +
> +#define STOR_SG_REQ_STATE_FREE	0
> +#define STOR_SG_REQ_STATE_BUSY	1
> +/* used as a Scatter-Gather request for UASP only */
> +struct stor_sg_req {
> +	struct usb_sg_request	sg_req;
> +	int			sts;
> +	int			result;
> +	struct work_struct	work;
> +	struct cmd_iu 		*cmdiu;
> +	struct us_data 		*us;
> +};
> +
>  /* we allocate one of these for every device that we remember */
>  struct us_data {
>  	/* The device we're working with
> @@ -112,6 +153,12 @@ struct us_data {
>  	unsigned int		send_ctrl_pipe;
>  	unsigned int		recv_ctrl_pipe;
>  	unsigned int		recv_intr_pipe;
> +	unsigned int		command_pipe;
> +	unsigned int		status_pipe;
> +
> +#define COMMAND_PIPE_IDLE	0
> +#define COMMAND_PIPE_BUSY	1
> +	int			command_pipe_sts;
>  
>  	/* information about the device */
>  	char			*transport_name;
> @@ -132,6 +179,9 @@ struct us_data {
>  	/* SCSI interfaces */
>  	struct scsi_cmnd	*srb;		 /* current srb		*/
>  	unsigned int		tag;		 /* current dCBWTag	*/
> +	struct list_head	scsi_cmnd_queue;
> +	struct list_head	temp_scsi_cmnd_queue;
> +	struct tm_iu		*abort_task_tmf;
>  
>  	/* control and bulk communications data */
>  	struct urb		*current_urb;	 /* USB requests	 */
> @@ -140,6 +190,9 @@ struct us_data {
>  	unsigned char		*iobuf;		 /* I/O buffer		 */
>  	dma_addr_t		cr_dma;		 /* buffer DMA addresses */
>  	dma_addr_t		iobuf_dma;
> +	struct stor_iobuf	iobufs[MAX_IOBUF_COUNT];
> +	struct stor_urb		urbs[MAX_URB_COUNT];
> +	struct stor_sg_req	sg_reqs[MAX_SG_REQ_COUNT];
>  	struct task_struct	*ctl_thread;	 /* the control thread   */
>  
>  	/* mutual exclusion and synchronization structures */
> @@ -147,6 +200,12 @@ struct us_data {
>  	struct completion	notify;		 /* thread begin/end	    */
>  	wait_queue_head_t	delay_wait;	 /* wait during scan, reset */
>  	struct completion	scanning_done;	 /* wait for scan thread    */
> +	wait_queue_head_t	uasp_wq;
> +	struct workqueue_struct	*sg_wq;
> +	spinlock_t		lock;
> +	int			pending_requests;
> +	int			active_requests;
> +	int			new_command;
>  
>  	/* subdriver information */
>  	void			*extra;		 /* Any extra data          */
> diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
> index 3d15fb9..a1a9d86 100644
> --- a/include/linux/usb_usual.h
> +++ b/include/linux/usb_usual.h
> @@ -96,6 +96,7 @@ enum { US_DO_ALL_FLAGS };
>  #define US_PR_CBI	0x00		/* Control/Bulk/Interrupt */
>  #define US_PR_CB	0x01		/* Control/Bulk w/o interrupt */
>  #define US_PR_BULK	0x50		/* bulk only */
> +#define US_PR_UASP	0x62		/* UASP */
>  
>  #define US_PR_USBAT	0x80		/* SCM-ATAPI bridge */
>  #define US_PR_EUSB_SDDR09	0x81	/* SCM-SCSI bridge for SDDR-09 */
> -- 
> 1.6.0.6
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux