From aa742683bbea9f2faa28ec2f61745abf2ad86a9d Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 19 Jan 2016 10:51:09 +0100 Subject: USB: uas: add full support for RESPONSE IU Some devices send response IUs when you'd expect a sense IU. As a response to a wrong LUN that is within spec. We cannot get away without handling for response IUs. This version fixes the issues Hans raised. Signed-off-by: Oliver Neukum Reviewed-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'drivers/usb/storage') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 9ff9404f99d7..44b096c1737b 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -246,6 +246,29 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, } } +static bool uas_evaluate_response_iu(struct response_iu *riu, struct scsi_cmnd *cmnd) +{ + u8 response_code = riu->response_code; + + switch (response_code) { + case RC_INCORRECT_LUN: + cmnd->result = DID_BAD_TARGET << 16; + break; + case RC_TMF_SUCCEEDED: + cmnd->result = DID_OK << 16; + break; + case RC_TMF_NOT_SUPPORTED: + cmnd->result = DID_TARGET_FAILURE << 16; + break; + default: + uas_log_cmd_state(cmnd, "response iu", response_code); + cmnd->result = DID_ERROR << 16; + break; + } + + return response_code == RC_TMF_SUCCEEDED; +} + static void uas_stat_cmplt(struct urb *urb) { struct iu *iu = urb->transfer_buffer; @@ -258,6 +281,7 @@ static void uas_stat_cmplt(struct urb *urb) unsigned long flags; unsigned int idx; int status = urb->status; + bool success; spin_lock_irqsave(&devinfo->lock, flags); @@ -313,13 +337,13 @@ static void uas_stat_cmplt(struct urb *urb) uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); break; case IU_ID_RESPONSE: - uas_log_cmd_state(cmnd, "unexpected response iu", - ((struct response_iu *)iu)->response_code); - /* Error, cancel data transfers */ - data_in_urb = usb_get_urb(cmdinfo->data_in_urb); - data_out_urb = usb_get_urb(cmdinfo->data_out_urb); cmdinfo->state &= ~COMMAND_INFLIGHT; - cmnd->result = DID_ERROR << 16; + success = uas_evaluate_response_iu((struct response_iu *)iu, cmnd); + if (!success) { + /* Error, cancel data transfers */ + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); + } uas_try_complete(cmnd, __func__); break; default: -- cgit