fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / usb / storage / transport.c
index bb738c8..323293a 100644 (file)
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
 #include "scsiglue.h"
-#include "usb.h"
 #include "debug.h"
 
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
 
 /***********************************************************************
  * Data transfer routines
@@ -91,8 +95,8 @@
  * or before the URB_ACTIVE bit was set.  If so, it's essential to cancel
  * the URB if it hasn't been cancelled already (i.e., if the URB_ACTIVE bit
  * is still set).  Either way, the function must then wait for the URB to
- * finish.  Note that because the URB_ASYNC_UNLINK flag is set, the URB can
- * still be in progress even after a call to usb_unlink_urb() returns.
+ * finish.  Note that the URB can still be in progress even after a call to
+ * usb_unlink_urb() returns.
  *
  * The idea is that (1) once the ABORTING or DISCONNECTING bit is set,
  * either the stop_transport() function or the submitting function
 /* This is the completion handler which will wake us up when an URB
  * completes.
  */
-static void usb_stor_blocking_completion(struct urb *urb, struct pt_regs *regs)
+static void usb_stor_blocking_completion(struct urb *urb)
 {
        struct completion *urb_done_ptr = (struct completion *)urb->context;
 
        complete(urb_done_ptr);
 }
-/* This is the timeout handler which will cancel an URB when its timeout
- * expires.
- */
-static void timeout_handler(unsigned long us_)
-{
-       struct us_data *us = (struct us_data *) us_;
-
-       if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
-               US_DEBUGP("Timeout -- cancelling URB\n");
-               usb_unlink_urb(us->current_urb);
-       }
-}
 
 /* This is the common part of the URB message submission code
  *
@@ -133,11 +124,11 @@ static void timeout_handler(unsigned long us_)
 static int usb_stor_msg_common(struct us_data *us, int timeout)
 {
        struct completion urb_done;
-       struct timer_list to_timer;
+       long timeleft;
        int status;
 
        /* don't submit URBs during abort/disconnect processing */
-       if (us->flags & DONT_SUBMIT)
+       if (us->flags & ABORTING_OR_DISCONNECTING)
                return -EIO;
 
        /* set up data structures for the wakeup system */
@@ -153,8 +144,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
         * hasn't been mapped for DMA.  Yes, this is clunky, but it's
         * easier than always having the caller tell us whether the
         * transfer buffer has already been mapped. */
-       us->current_urb->transfer_flags =
-                       URB_ASYNC_UNLINK | URB_NO_SETUP_DMA_MAP;
+       us->current_urb->transfer_flags = URB_NO_SETUP_DMA_MAP;
        if (us->current_urb->transfer_buffer == us->iobuf)
                us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        us->current_urb->transfer_dma = us->iobuf_dma;
@@ -172,7 +162,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
        set_bit(US_FLIDX_URB_ACTIVE, &us->flags);
 
        /* did an abort/disconnect occur during the submission? */
-       if (us->flags & DONT_SUBMIT) {
+       if (us->flags & ABORTING_OR_DISCONNECTING) {
 
                /* cancel the URB, if it hasn't been cancelled already */
                if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
@@ -181,22 +171,17 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
                }
        }
  
-       /* submit the timeout timer, if a timeout was requested */
-       if (timeout > 0) {
-               init_timer(&to_timer);
-               to_timer.expires = jiffies + timeout;
-               to_timer.function = timeout_handler;
-               to_timer.data = (unsigned long) us;
-               add_timer(&to_timer);
-       }
-
        /* wait for the completion of the URB */
-       wait_for_completion(&urb_done);
-       clear_bit(US_FLIDX_URB_ACTIVE, &us->flags);
+       timeleft = wait_for_completion_interruptible_timeout(
+                       &urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT);
  
-       /* clean up the timeout timer */
-       if (timeout > 0)
-               del_timer_sync(&to_timer);
+       clear_bit(US_FLIDX_URB_ACTIVE, &us->flags);
+
+       if (timeleft <= 0) {
+               US_DEBUGP("%s -- cancelling URB\n",
+                         timeleft == 0 ? "Timeout" : "Signal");
+               usb_kill_urb(us->current_urb);
+       }
 
        /* return the URB status */
        return us->current_urb->status;
@@ -260,11 +245,10 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe)
                USB_ENDPOINT_HALT, endp,
                NULL, 0, 3*HZ);
 
-       /* reset the toggles and endpoint flags */
-       usb_endpoint_running(us->pusb_dev, usb_pipeendpoint(pipe),
-               usb_pipeout(pipe));
-       usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
-               usb_pipeout(pipe), 0);
+       /* reset the endpoint toggle */
+       if (result >= 0)
+               usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
+                               usb_pipeout(pipe), 0);
 
        US_DEBUGP("%s: result = %d\n", __FUNCTION__, result);
        return result;
@@ -310,11 +294,6 @@ static int interpret_urb_result(struct us_data *us, unsigned int pipe,
                        return USB_STOR_XFER_ERROR;
                return USB_STOR_XFER_STALLED;
 
-       /* timeout or excessively long NAK */
-       case -ETIMEDOUT:
-               US_DEBUGP("-- timeout or NAK\n");
-               return USB_STOR_XFER_ERROR;
-
        /* babble - the device tried to send more than we wanted to read */
        case -EOVERFLOW:
                US_DEBUGP("-- babble\n");
@@ -380,7 +359,8 @@ int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe,
  * This routine always uses us->recv_intr_pipe as the pipe and
  * us->ep_bInterval as the interrupt interval.
  */
-int usb_stor_intr_transfer(struct us_data *us, void *buf, unsigned int length)
+static int usb_stor_intr_transfer(struct us_data *us, void *buf,
+                                 unsigned int length)
 {
        int result;
        unsigned int pipe = us->recv_intr_pipe;
@@ -433,21 +413,21 @@ int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe,
  * This function does basically the same thing as usb_stor_bulk_transfer_buf()
  * above, but it uses the usbcore scatter-gather library.
  */
-int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe,
+static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe,
                struct scatterlist *sg, int num_sg, unsigned int length,
                unsigned int *act_len)
 {
        int result;
 
        /* don't submit s-g requests during abort/disconnect processing */
-       if (us->flags & DONT_SUBMIT)
+       if (us->flags & ABORTING_OR_DISCONNECTING)
                return USB_STOR_XFER_ERROR;
 
        /* initialize the scatter-gather request block */
        US_DEBUGP("%s: xfer %u bytes, %d entries\n", __FUNCTION__,
                        length, num_sg);
        result = usb_sg_init(&us->current_sg, us->pusb_dev, pipe, 0,
-                       sg, num_sg, length, SLAB_NOIO);
+                       sg, num_sg, length, GFP_NOIO);
        if (result) {
                US_DEBUGP("usb_sg_init returned %d\n", result);
                return USB_STOR_XFER_ERROR;
@@ -458,7 +438,7 @@ int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe,
        set_bit(US_FLIDX_SG_ACTIVE, &us->flags);
 
        /* did an abort/disconnect occur during the submission? */
-       if (us->flags & DONT_SUBMIT) {
+       if (us->flags & ABORTING_OR_DISCONNECTING) {
 
                /* cancel the request, if it hasn't been cancelled already */
                if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) {
@@ -522,7 +502,7 @@ int usb_stor_bulk_transfer_sg(struct us_data* us, unsigned int pipe,
  * This is used by the protocol layers to actually send the message to
  * the device and receive the response.
  */
-void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
+void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
 {
        int need_auto_sense;
        int result;
@@ -534,17 +514,17 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
        /* if the command gets aborted by the higher layers, we need to
         * short-circuit all other processing
         */
-       if (us->sm_state == US_STATE_ABORTING) {
+       if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
                US_DEBUGP("-- command was aborted\n");
-               goto Handle_Abort;
+               srb->result = DID_ABORT << 16;
+               goto Handle_Errors;
        }
 
        /* if there is a transport error, reset and don't auto-sense */
        if (result == USB_STOR_TRANSPORT_ERROR) {
                US_DEBUGP("-- transport indicates error, resetting\n");
-               us->transport_reset(us);
                srb->result = DID_ERROR << 16;
-               return;
+               goto Handle_Errors;
        }
 
        /* if the transport provided its own sense data, don't auto-sense */
@@ -569,7 +549,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
         * can signal most data-in errors by stalling the bulk-in pipe.
         */
        if ((us->protocol == US_PR_CB || us->protocol == US_PR_DPCM_USB) &&
-                       srb->sc_data_direction != SCSI_DATA_READ) {
+                       srb->sc_data_direction != DMA_FROM_DEVICE) {
                US_DEBUGP("-- CB transport device requiring auto-sense\n");
                need_auto_sense = 1;
        }
@@ -606,7 +586,6 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
                unsigned char old_sc_data_direction;
                unsigned char old_cmd_len;
                unsigned char old_cmnd[MAX_COMMAND_SIZE];
-               unsigned long old_serial_number;
                int old_resid;
 
                US_DEBUGP("Issuing auto-REQUEST_SENSE\n");
@@ -629,42 +608,39 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
 
                /* set the transfer direction */
                old_sc_data_direction = srb->sc_data_direction;
-               srb->sc_data_direction = SCSI_DATA_READ;
+               srb->sc_data_direction = DMA_FROM_DEVICE;
 
                /* use the new buffer we have */
                old_request_buffer = srb->request_buffer;
-               srb->request_buffer = srb->sense_buffer;
+               srb->request_buffer = us->sensebuf;
 
                /* set the buffer length for transfer */
                old_request_bufflen = srb->request_bufflen;
-               srb->request_bufflen = 18;
+               srb->request_bufflen = US_SENSE_SIZE;
 
                /* set up for no scatter-gather use */
                old_sg = srb->use_sg;
                srb->use_sg = 0;
 
-               /* change the serial number -- toggle the high bit*/
-               old_serial_number = srb->serial_number;
-               srb->serial_number ^= 0x80000000;
-
                /* issue the auto-sense command */
                old_resid = srb->resid;
                srb->resid = 0;
                temp_result = us->transport(us->srb, us);
 
                /* let's clean up right away */
+               memcpy(srb->sense_buffer, us->sensebuf, US_SENSE_SIZE);
                srb->resid = old_resid;
                srb->request_buffer = old_request_buffer;
                srb->request_bufflen = old_request_bufflen;
                srb->use_sg = old_sg;
-               srb->serial_number = old_serial_number;
                srb->sc_data_direction = old_sc_data_direction;
                srb->cmd_len = old_cmd_len;
                memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
 
-               if (us->sm_state == US_STATE_ABORTING) {
+               if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
                        US_DEBUGP("-- auto-sense aborted\n");
-                       goto Handle_Abort;
+                       srb->result = DID_ABORT << 16;
+                       goto Handle_Errors;
                }
                if (temp_result != USB_STOR_TRANSPORT_GOOD) {
                        US_DEBUGP("-- auto-sense failure\n");
@@ -673,9 +649,9 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
                         * multi-target device, since failure of an
                         * auto-sense is perfectly valid
                         */
-                       if (!(us->flags & US_FL_SCM_MULT_TARG))
-                               us->transport_reset(us);
                        srb->result = DID_ERROR << 16;
+                       if (!(us->flags & US_FL_SCM_MULT_TARG))
+                               goto Handle_Errors;
                        return;
                }
 
@@ -708,18 +684,39 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
                        srb->sense_buffer[0] = 0x0;
                }
        }
+
+       /* Did we transfer less than the minimum amount required? */
+       if (srb->result == SAM_STAT_GOOD &&
+                       srb->request_bufflen - srb->resid < srb->underflow)
+               srb->result = (DID_ERROR << 16) | (SUGGEST_RETRY << 24);
+
        return;
 
-       /* abort processing: the bulk-only transport requires a reset
-        * following an abort */
-       Handle_Abort:
-       srb->result = DID_ABORT << 16;
-       if (us->protocol == US_PR_BULK) {
+       /* Error and abort processing: try to resynchronize with the device
+        * by issuing a port reset.  If that fails, try a class-specific
+        * device reset. */
+  Handle_Errors:
+
+       /* Set the RESETTING bit, and clear the ABORTING bit so that
+        * the reset may proceed. */
+       scsi_lock(us_to_host(us));
+       set_bit(US_FLIDX_RESETTING, &us->flags);
+       clear_bit(US_FLIDX_ABORTING, &us->flags);
+       scsi_unlock(us_to_host(us));
+
+       /* We must release the device lock because the pre_reset routine
+        * will want to acquire it. */
+       mutex_unlock(&us->dev_mutex);
+       result = usb_stor_port_reset(us);
+       mutex_lock(&us->dev_mutex);
 
-               /* permit the reset transfer to take place */
-               clear_bit(US_FLIDX_ABORTING, &us->flags);
+       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->flags);
 }
 
 /* Stop the current URB transfer */
@@ -747,7 +744,7 @@ void usb_stor_stop_transport(struct us_data *us)
  * Control/Bulk/Interrupt transport
  */
 
-int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
+int usb_stor_CBI_transport(struct scsi_cmnd *srb, struct us_data *us)
 {
        unsigned int transfer_length = srb->request_bufflen;
        unsigned int pipe = 0;
@@ -776,7 +773,7 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
        /* DATA STAGE */
        /* transfer the data payload for this command, if one exists*/
        if (transfer_length) {
-               pipe = srb->sc_data_direction == SCSI_DATA_READ ? 
+               pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? 
                                us->recv_bulk_pipe : us->send_bulk_pipe;
                result = usb_stor_bulk_transfer_sg(us, pipe,
                                        srb->request_buffer, transfer_length,
@@ -847,7 +844,7 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
 /*
  * Control/Bulk transport
  */
-int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us)
 {
        unsigned int transfer_length = srb->request_bufflen;
        int result;
@@ -875,7 +872,7 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
        /* DATA STAGE */
        /* transfer the data payload for this command, if one exists*/
        if (transfer_length) {
-               unsigned int pipe = srb->sc_data_direction == SCSI_DATA_READ ? 
+               unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? 
                                us->recv_bulk_pipe : us->send_bulk_pipe;
                result = usb_stor_bulk_transfer_sg(us, pipe,
                                        srb->request_buffer, transfer_length,
@@ -906,6 +903,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
        int result;
 
        /* issue the command */
+       us->iobuf[0] = 0;
        result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
                                 US_BULK_GET_MAX_LUN, 
                                 USB_DIR_IN | USB_TYPE_CLASS | 
@@ -916,14 +914,31 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
                  result, us->iobuf[0]);
 
        /* if we have a successful request, return the result */
-       if (result == 1)
+       if (result > 0)
                return us->iobuf[0];
 
-       /* return the default -- no LUNs */
+       /* 
+        * Some devices (i.e. Iomega Zip100) need this -- apparently
+        * the bulk pipes get STALLed when the GetMaxLUN request is
+        * processed.   This is, in theory, harmless to all other devices
+        * (regardless of if they stall or not).
+        */
+       if (result == -EPIPE) {
+               usb_stor_clear_halt(us, us->recv_bulk_pipe);
+               usb_stor_clear_halt(us, us->send_bulk_pipe);
+       }
+
+       /*
+        * Some devices don't like GetMaxLUN.  They may STALL the control
+        * pipe, they may return a zero-length result, they may do nothing at
+        * all and timeout, or they may fail in even more bizarrely creative
+        * ways.  In these cases the best approach is to use the default
+        * value: only one LUN.
+        */
        return 0;
 }
 
-int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
+int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
 {
        struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
        struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
@@ -932,12 +947,19 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
        int result;
        int fake_sense = 0;
        unsigned int cswlen;
+       unsigned int cbwlen = US_BULK_CB_WRAP_LEN;
+
+       /* Take care of BULK32 devices; set extra byte to 0 */
+       if ( unlikely(us->flags & US_FL_BULK32)) {
+               cbwlen = 32;
+               us->iobuf[31] = 0;
+       }
 
        /* set up the command wrapper */
        bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
        bcb->DataTransferLength = cpu_to_le32(transfer_length);
-       bcb->Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0;
-       bcb->Tag = srb->serial_number;
+       bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? 1 << 7 : 0;
+       bcb->Tag = ++us->tag;
        bcb->Lun = srb->device->lun;
        if (us->flags & US_FL_SCM_MULT_TARG)
                bcb->Lun |= srb->device->id << 4;
@@ -954,15 +976,22 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
                        (bcb->Lun >> 4), (bcb->Lun & 0x0F), 
                        bcb->Length);
        result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
-                               bcb, US_BULK_CB_WRAP_LEN, NULL);
+                               bcb, cbwlen, NULL);
        US_DEBUGP("Bulk command transfer result=%d\n", result);
        if (result != USB_STOR_XFER_GOOD)
                return USB_STOR_TRANSPORT_ERROR;
 
        /* DATA STAGE */
        /* send/receive data payload, if there is any */
+
+       /* Some USB-IDE converter chips need a 100us delay between the
+        * command phase and the data phase.  Some devices need a little
+        * more than that, probably because of clock rate inaccuracies. */
+       if (unlikely(us->flags & US_FL_GO_SLOW))
+               udelay(125);
+
        if (transfer_length) {
-               unsigned int pipe = srb->sc_data_direction == SCSI_DATA_READ ? 
+               unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? 
                                us->recv_bulk_pipe : us->send_bulk_pipe;
                result = usb_stor_bulk_transfer_sg(us, pipe,
                                        srb->request_buffer, transfer_length,
@@ -1019,18 +1048,35 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
        US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
                        le32_to_cpu(bcs->Signature), bcs->Tag, 
                        residue, bcs->Status);
-       if ((bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) &&
-                   bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) ||
-                       bcs->Tag != srb->serial_number || 
-                       bcs->Status > US_BULK_STAT_PHASE) {
+       if (bcs->Tag != us->tag || bcs->Status > US_BULK_STAT_PHASE) {
                US_DEBUGP("Bulk logical error\n");
                return USB_STOR_TRANSPORT_ERROR;
        }
 
+       /* Some broken devices report odd signatures, so we do not check them
+        * for validity against the spec. We store the first one we see,
+        * and check subsequent transfers for validity against this signature.
+        */
+       if (!us->bcs_signature) {
+               us->bcs_signature = bcs->Signature;
+               if (us->bcs_signature != cpu_to_le32(US_BULK_CS_SIGN))
+                       US_DEBUGP("Learnt BCS signature 0x%08X\n",
+                                       le32_to_cpu(us->bcs_signature));
+       } else if (bcs->Signature != us->bcs_signature) {
+               US_DEBUGP("Signature mismatch: got %08X, expecting %08X\n",
+                         le32_to_cpu(bcs->Signature),
+                         le32_to_cpu(us->bcs_signature));
+               return USB_STOR_TRANSPORT_ERROR;
+       }
+
        /* try to compute the actual residue, based on how much data
         * was really transferred and what the device tells us */
-       residue = min(residue, transfer_length);
-       srb->resid = max(srb->resid, (int) residue);
+       if (residue) {
+               if (!(us->flags & US_FL_IGNORE_RESIDUE)) {
+                       residue = min(residue, transfer_length);
+                       srb->resid = max(srb->resid, (int) residue);
+               }
+       }
 
        /* based on the status code, we report good or bad */
        switch (bcs->Status) {
@@ -1070,7 +1116,7 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
  * It's handy that every transport mechanism uses the control endpoint for
  * resets.
  *
- * Basically, we send a reset with a 20-second timeout, so we don't get
+ * Basically, we send a reset with a 5-second timeout, so we don't get
  * jammed attempting to do the reset.
  */
 static int usb_stor_reset_common(struct us_data *us,
@@ -1080,47 +1126,43 @@ static int usb_stor_reset_common(struct us_data *us,
        int result;
        int result2;
 
-       /* Let the SCSI layer know we are doing a reset */
-       usb_stor_report_device_reset(us);
-
-       /* A 20-second timeout may seem rather long, but a LaCie
-        *  StudioDrive USB2 device takes 16+ seconds to get going
-        *  following a powerup or USB attach event. */
+       if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+               US_DEBUGP("No reset during disconnect\n");
+               return -EIO;
+       }
 
        result = usb_stor_control_msg(us, us->send_ctrl_pipe,
                        request, requesttype, value, index, data, size,
-                       20*HZ);
+                       5*HZ);
        if (result < 0) {
                US_DEBUGP("Soft reset failed: %d\n", result);
-               return FAILED;
+               return result;
        }
 
-       /* long wait for reset, so unlock to allow disconnects */
-       up(&us->dev_semaphore);
-       set_current_state(TASK_UNINTERRUPTIBLE);
-       schedule_timeout(HZ*6);
-       down(&us->dev_semaphore);
+       /* Give the device some time to recover from the reset,
+        * but don't delay disconnect processing. */
+       wait_event_interruptible_timeout(us->delay_wait,
+                       test_bit(US_FLIDX_DISCONNECTING, &us->flags),
+                       HZ*6);
        if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
                US_DEBUGP("Reset interrupted by disconnect\n");
-               return FAILED;
+               return -EIO;
        }
 
-       /* permit the clear-halt transfers to take place */
-       clear_bit(US_FLIDX_ABORTING, &us->flags);
-
        US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
        result = usb_stor_clear_halt(us, us->recv_bulk_pipe);
 
        US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n");
        result2 = usb_stor_clear_halt(us, us->send_bulk_pipe);
 
-       /* return a result code based on the result of the control message */
-       if (result < 0 || result2 < 0) {
+       /* return a result code based on the result of the clear-halts */
+       if (result >= 0)
+               result = result2;
+       if (result < 0)
                US_DEBUGP("Soft reset failed\n");
-               return FAILED;
-       }
-       US_DEBUGP("Soft reset done\n");
-       return SUCCESS;
+       else
+               US_DEBUGP("Soft reset done\n");
+       return result;
 }
 
 /* This issues a CB[I] Reset to the device in question
@@ -1150,3 +1192,31 @@ int usb_stor_Bulk_reset(struct us_data *us)
                                 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
                                 0, us->ifnum, NULL, 0);
 }
+
+/* Issue a USB port reset to the device.  The caller must not hold
+ * us->dev_mutex.
+ */
+int usb_stor_port_reset(struct us_data *us)
+{
+       int result, rc_lock;
+
+       result = rc_lock =
+               usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+       if (result < 0)
+               US_DEBUGP("unable to lock device for reset: %d\n", result);
+       else {
+               /* Were we disconnected while waiting for the lock? */
+               if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+                       result = -EIO;
+                       US_DEBUGP("No reset during disconnect\n");
+               } else {
+                       result = usb_reset_composite_device(
+                                       us->pusb_dev, us->pusb_intf);
+                       US_DEBUGP("usb_reset_composite_device returns %d\n",
+                                       result);
+               }
+               if (rc_lock)
+                       usb_unlock_device(us->pusb_dev);
+       }
+       return result;
+}