Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / usb / storage / transport.c
index 9743e28..7ca896a 100644 (file)
@@ -96,8 +96,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
@@ -158,8 +158,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;
@@ -266,8 +265,9 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe)
                NULL, 0, 3*HZ);
 
        /* reset the endpoint toggle */
-       usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
-               usb_pipeout(pipe), 0);
+       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;
@@ -540,15 +540,15 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
         */
        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 */
@@ -610,7 +610,6 @@ void usb_stor_invoke_transport(struct 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");
@@ -637,38 +636,35 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
 
                /* 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 (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");
@@ -677,9 +673,9 @@ void usb_stor_invoke_transport(struct 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;
                }
 
@@ -720,12 +716,28 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
 
        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:
+
+       /* Let the SCSI layer know we are doing a reset, set the
+        * RESETTING bit, and clear the ABORTING bit so that the reset
+        * may proceed. */
+       scsi_lock(us_to_host(us));
+       usb_stor_report_bus_reset(us);
+       set_bit(US_FLIDX_RESETTING, &us->flags);
+       clear_bit(US_FLIDX_ABORTING, &us->flags);
+       scsi_unlock(us_to_host(us));
+
+       result = usb_stor_port_reset(us);
+       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 */
@@ -912,6 +924,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 | 
@@ -967,7 +980,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
        bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
        bcb->DataTransferLength = cpu_to_le32(transfer_length);
        bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? 1 << 7 : 0;
-       bcb->Tag = srb->serial_number;
+       bcb->Tag = ++us->tag;
        bcb->Lun = srb->device->lun;
        if (us->flags & US_FL_SCM_MULT_TARG)
                bcb->Lun |= srb->device->id << 4;
@@ -1056,7 +1069,7 @@ int usb_stor_Bulk_transport(struct 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->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;
        }
@@ -1124,7 +1137,7 @@ int usb_stor_Bulk_transport(struct 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,
@@ -1133,28 +1146,18 @@ static int usb_stor_reset_common(struct us_data *us,
 {
        int result;
        int result2;
-       int rc = FAILED;
 
-       /* Let the SCSI layer know we are doing a reset, set the
-        * RESETTING bit, and clear the ABORTING bit so that the reset
-        * may proceed.
-        */
-       scsi_lock(us_to_host(us));
-       usb_stor_report_device_reset(us);
-       set_bit(US_FLIDX_RESETTING, &us->flags);
-       clear_bit(US_FLIDX_ABORTING, &us->flags);
-       scsi_unlock(us_to_host(us));
+       if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+               US_DEBUGP("No reset during disconnect\n");
+               return -EIO;
+       }
 
-       /* 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.
-        */
        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);
-               goto Done;
+               return result;
        }
 
        /* Give the device some time to recover from the reset,
@@ -1164,7 +1167,7 @@ static int usb_stor_reset_common(struct us_data *us,
                        HZ*6);
        if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
                US_DEBUGP("Reset interrupted by disconnect\n");
-               goto Done;
+               return -EIO;
        }
 
        US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
@@ -1173,17 +1176,14 @@ static int usb_stor_reset_common(struct us_data *us,
        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");
-               goto Done;
-       }
-       US_DEBUGP("Soft reset done\n");
-       rc = SUCCESS;
-
-  Done:
-       clear_bit(US_FLIDX_RESETTING, &us->flags);
-       return rc;
+       else
+               US_DEBUGP("Soft reset done\n");
+       return result;
 }
 
 /* This issues a CB[I] Reset to the device in question
@@ -1213,3 +1213,32 @@ 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.  But don't do anything if
+ * there's more than one interface in the device, so that other users
+ * are not affected. */
+int usb_stor_port_reset(struct us_data *us)
+{
+       int result, rc;
+
+       if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+               result = -EIO;
+               US_DEBUGP("No reset during disconnect\n");
+       } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
+               result = -EBUSY;
+               US_DEBUGP("Refusing to reset a multi-interface device\n");
+       } else {
+               result = rc =
+                       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 {
+                       result = usb_reset_device(us->pusb_dev);
+                       if (rc)
+                               usb_unlock_device(us->pusb_dev);
+                       US_DEBUGP("usb_reset_device returns %d\n", result);
+               }
+       }
+       return result;
+}