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 <gghartma@cs.cmu.edu> */
+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