I've been running with this patch against the 2.6.8 kernel for the last
5 days. It appears to fix the problem. However, I'm not certain that it
is the right work-around from an architectural perspective.

diff -ur orig/drivers/usb/storage/scsiglue.c 
kernel-source-2.6.8/drivers/usb/storage/scsiglue.c
--- orig/drivers/usb/storage/scsiglue.c 2004-08-14 01:36:58.000000000 -0400
+++ kernel-source-2.6.8/drivers/usb/storage/scsiglue.c  2007-04-15 
16:48:18.000000000 -0400
@@ -354,6 +354,7 @@
                DO_FLAG(SCM_MULT_TARG);
                DO_FLAG(FIX_INQUIRY);
                DO_FLAG(FIX_CAPACITY);
+               DO_FLAG(MUST_RESTART);
 
                *(pos++) = '\n';
        }
diff -ur orig/drivers/usb/storage/unusual_devs.h 
kernel-source-2.6.8/drivers/usb/storage/unusual_devs.h
--- orig/drivers/usb/storage/unusual_devs.h     2006-12-05 04:21:55.000000000 
-0500
+++ kernel-source-2.6.8/drivers/usb/storage/unusual_devs.h      2007-04-15 
16:45:34.000000000 -0400
@@ -730,6 +730,13 @@
                 "Optio S/S4",
                 US_SC_DEVICE, US_PR_DEVICE, NULL,
                 US_FL_FIX_INQUIRY ),
+
+/* Submitted by Greg Hartman <[EMAIL PROTECTED]> */
+UNUSUAL_DEV (0x0bc2, 0x3000, 0x0000, 0x0000,
+               "Seagate",
+               "FreeAgentDesktop",
+               US_SC_DEVICE, US_PR_DEVICE, NULL,
+               US_FL_MUST_RESTART ),
                
 #ifdef CONFIG_USB_STORAGE_ISD200
 UNUSUAL_DEV(  0x0bf6, 0xa001, 0x0100, 0x0110,
diff -ur orig/drivers/usb/storage/usb.c 
kernel-source-2.6.8/drivers/usb/storage/usb.c
--- orig/drivers/usb/storage/usb.c      2004-08-14 01:36:32.000000000 -0400
+++ kernel-source-2.6.8/drivers/usb/storage/usb.c       2007-04-17 
19:38:35.000000000 -0400
@@ -263,10 +263,41 @@
        usb_stor_set_xfer_buf(data, data_len, us->srb);
 }
 
+static void start_done(struct scsi_cmnd *unused)
+{
+}
+
+static Scsi_Cmnd * make_start_srb(struct us_data *us)
+{
+       Scsi_Cmnd *start_srb;
+
+       start_srb = scsi_get_command(us->srb->device, GFP_KERNEL);
+       if (!start_srb) {
+               return start_srb;
+       }
+       start_srb->sc_magic = us->srb->sc_magic;
+       start_srb->sc_request = NULL;
+       start_srb->done = start_done;
+       start_srb->serial_number = 0x5511AA77;
+       start_srb->retries = 1;
+       start_srb->cmd_len = 6;
+       start_srb->sc_data_direction = DMA_NONE;
+       memset(start_srb->cmnd, 0, 6);
+       start_srb->cmnd[0] = START_STOP;
+       start_srb->cmnd[4] = 1;
+       start_srb->use_sg = 0;
+       start_srb->bufflen = 0;
+       start_srb->buffer = 0;
+       start_srb->request = NULL;
+
+       return start_srb;
+}
+
 static int usb_stor_control_thread(void * __us)
 {
        struct us_data *us = (struct us_data *)__us;
        struct Scsi_Host *host = us->host;
+       Scsi_Cmnd *start_srb = NULL;
 
        lock_kernel();
 
@@ -360,6 +391,36 @@
                else {
                        US_DEBUG(usb_stor_show_command(us->srb));
                        us->proto_handler(us->srb, us);
+                       if ((us->flags & US_FL_MUST_RESTART) &&
+                           (us->srb->result == SAM_STAT_CHECK_CONDITION)) {
+                           if (!start_srb) {
+                           /* Why do I have to do this here? Are there
+                            * multiple values for ->dev? If so, sharing
+                            * start_srb is bad
+                            */
+                               start_srb = make_start_srb(us);
+                               if (!start_srb) {
+                                       printk(KERN_NOTICE USB_STORAGE 
"MUST_RESTART failed: unable to allocate command\n");
+                               }
+                           }
+                           if (start_srb) {
+                                   printk("GSH: START on cmnd=0x%02x\n", 
us->srb->cmnd[0]);
+                                   US_DEBUG(usb_stor_show_command(start_srb));
+                                   us->proto_handler(start_srb, us);
+
+                                   /* Now retry the command */
+
+                                   US_DEBUG(usb_stor_show_command(us->srb));
+                                   us->srb->result = SAM_STAT_GOOD;
+                                   /* I need to clobber this to avoid I/O
+                                    * errors in the higher layers. I don't know
+                                    * how much clobbering is needed. Some
+                                    * instances just clear 0, others 0 ... 16
+                                    */
+                                   memset(us->srb->sense_buffer, 0, 
sizeof(us->srb->sense_buffer));
+                                   us->proto_handler(us->srb, us);
+                           }
+                       }
                }
 
                /* lock access to the state */
@@ -393,6 +454,11 @@
                up(&(us->dev_semaphore));
        } /* for (;;) */
 
+       if (start_srb) {
+         scsi_put_command(start_srb);
+         start_srb = NULL;
+       }
+
        /* notify the exit routine that we're actually exiting now 
         *
         * complete()/wait_for_completion() is similar to up()/down(),
diff -ur orig/drivers/usb/storage/usb.h 
kernel-source-2.6.8/drivers/usb/storage/usb.h
--- orig/drivers/usb/storage/usb.h      2006-12-05 04:21:54.000000000 -0500
+++ kernel-source-2.6.8/drivers/usb/storage/usb.h       2007-04-15 
13:02:25.000000000 -0400
@@ -75,6 +75,7 @@
 #define US_FL_FIX_INQUIRY     0x00000040 /* INQUIRY response needs faking   */
 #define US_FL_FIX_CAPACITY    0x00000080 /* READ CAPACITY response too big  */
 #define US_FL_IGNORE_RESIDUE  0x00000100 /* reported residue is wrong      */
+#define US_FL_MUST_RESTART    0x00000200 /* Drive needs occasional START    */
 
 /* Dynamic flag definitions: used in set_bit() etc. */
 #define US_FLIDX_URB_ACTIVE    18  /* 0x00040000  current_urb is in use  */

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to