vserver 1.9.3
[linux-2.6.git] / drivers / usb / storage / transport.c
index bb738c8..42f7843 100644 (file)
  */
 
 #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 "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
@@ -137,7 +142,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
        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 */
@@ -172,7 +177,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)) {
@@ -260,9 +265,7 @@ 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));
+       /* reset the endpoint toggle */
        usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
                usb_pipeout(pipe), 0);
 
@@ -440,7 +443,7 @@ int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe,
        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 */
@@ -458,7 +461,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 +525,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;
@@ -569,7 +572,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;
        }
@@ -629,7 +632,7 @@ 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;
@@ -708,18 +711,20 @@ 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:
+  Handle_Abort:
        srb->result = DID_ABORT << 16;
-       if (us->protocol == US_PR_BULK) {
-
-               /* permit the reset transfer to take place */
-               clear_bit(US_FLIDX_ABORTING, &us->flags);
+       if (us->protocol == US_PR_BULK)
                us->transport_reset(us);
-       }
 }
 
 /* Stop the current URB transfer */
@@ -747,7 +752,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 +781,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 +852,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 +880,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 +911,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 +922,28 @@ 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 */
-       return 0;
+       /* 
+        * 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);
+               /* return the default -- no LUNs */
+               return 0;
+       }
+
+       /* An answer or a STALL are the only valid responses.  If we get
+        * something else, return an indication of error */
+       return -1;
 }
 
-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;
@@ -936,7 +956,7 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
        /* 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->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? 1 << 7 : 0;
        bcb->Tag = srb->serial_number;
        bcb->Lun = srb->device->lun;
        if (us->flags & US_FL_SCM_MULT_TARG)
@@ -961,8 +981,14 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
 
        /* DATA STAGE */
        /* send/receive data payload, if there is any */
+
+       /* Genesys Logic interface chips need a 100us delay between the
+        * command phase and the data phase */
+       if (us->pusb_dev->descriptor.idVendor == USB_VENDOR_ID_GENESYS)
+               udelay(100);
+
        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,
@@ -1079,35 +1105,40 @@ 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 */
+       /* 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->host);
        usb_stor_report_device_reset(us);
+       set_bit(US_FLIDX_RESETTING, &us->flags);
+       clear_bit(US_FLIDX_ABORTING, &us->flags);
+       scsi_unlock(us->host);
 
        /* 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. */
-
+        * 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);
        if (result < 0) {
                US_DEBUGP("Soft reset failed: %d\n", result);
-               return FAILED;
+               goto Done;
        }
 
-       /* 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->dev_reset_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;
+               goto Done;
        }
 
-       /* 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);
 
@@ -1117,10 +1148,14 @@ static int usb_stor_reset_common(struct us_data *us,
        /* return a result code based on the result of the control message */
        if (result < 0 || result2 < 0) {
                US_DEBUGP("Soft reset failed\n");
-               return FAILED;
+               goto Done;
        }
        US_DEBUGP("Soft reset done\n");
-       return SUCCESS;
+       rc = SUCCESS;
+
+  Done:
+       clear_bit(US_FLIDX_RESETTING, &us->flags);
+       return rc;
 }
 
 /* This issues a CB[I] Reset to the device in question