# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.612   -> 1.613  
#	drivers/usb/storage/protocol.c	1.7     -> 1.8    
#	drivers/usb/storage/usb.h	1.16    -> 1.17   
#	drivers/usb/storage/unusual_devs.h	1.17    -> 1.18   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/09/04	Andries.Brouwer@cwi.nl	1.613
# [PATCH] Feiya 5-in-1 Card Reader
# 
# I have a USB 5-in-1 Card Reader, that will read CF and SM and SD/MMC.
# Under Linux it appears as three SCSI devices.
# For today, the report is on the CF part.
# 
# The CF part works fine under ordinary usb-storage SCSI simulation,
# with one small problem: 8 and 32 MB cards, that are detected as
# having 15872 and 63488 sectors by other readers, are detected as
# having 15873 and 63489 sectors by this Feiya reader
# (0x090c / 0x1132).
# In the good old days probably nobody would have noticed, but these
# days the partition reading code also wants to read the last sector.
# This results in the SCSI code taking the device off line:
# 
# [USB storage does a READ_10, which fails since the sector is past
# the end of the disk. Then it tries a READ_6 and nothing ever happens,
# probably because the device does not support READ_6. Then the
# error handler does an abort which triggers some bugs in scsiglue.c
# and transport.c, then the error handler does a device reset, then
# a host reset, then a bus reset, and finally the device is taken offline.]
# 
# The patch below does not address any bugs in the SCSI error code
# (a big improvement would be just to rip it all out - this error code
# never achieves anything useful but has crashed many a machine)
# and does not fix the USB code either.
# It just adds a flag to the unusual_devices section mentioning that
# this device (my revision is 1.00) has this bug.
# 
# Without the patch the kernel crashes, or insmod usb-storage hangs.
# With the patch the CF part of the device works perfectly.
# 
# (Another change is to only print "Fixing INQUIRY data" when
# really something is changed, not when the data was OK already.)
# 
# Andries
# --------------------------------------------
#
diff -Nru a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
--- a/drivers/usb/storage/protocol.c	Thu Sep  5 08:51:01 2002
+++ b/drivers/usb/storage/protocol.c	Thu Sep  5 08:51:01 2002
@@ -54,10 +54,27 @@
  * Helper routines
  ***********************************************************************/
 
-/* Fix-up the return data from an INQUIRY command to show 
+static void *
+find_data_location(Scsi_Cmnd *srb) {
+	if (srb->use_sg) {
+		/*
+		 * This piece of code only works if the first page is
+		 * big enough to hold more than 3 bytes -- which is
+		 * _very_ likely.
+		 */
+		struct scatterlist *sg;
+
+		sg = (struct scatterlist *) srb->request_buffer;
+		return (void *) page_address(sg[0].page) + sg[0].offset;
+	} else
+		return (void *) srb->request_buffer;
+}
+
+/*
+ * Fix-up the return data from an INQUIRY command to show 
  * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
  */
-void fix_inquiry_data(Scsi_Cmnd *srb)
+static void fix_inquiry_data(Scsi_Cmnd *srb)
 {
 	unsigned char *data_ptr;
 
@@ -65,24 +82,43 @@
 	if (srb->cmnd[0] != INQUIRY)
 		return;
 
-	US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2\n");
+	data_ptr = find_data_location(srb);
 
-	/* find the location of the data */
-	if (srb->use_sg) {
-		/* this piece of code only works if the first page is big enough to
-		 * hold more than 3 bytes -- which is _very_ likely
-		 */
-		struct scatterlist *sg;
+	if ((data_ptr[2] & 7) == 2)
+		return;
 
-		sg = (struct scatterlist *) srb->request_buffer;
-		data_ptr = (unsigned char *) page_address(sg[0].page) + sg[0].offset;
-	} else
-		data_ptr = (unsigned char *)srb->request_buffer;
+	US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n",
+		  data_ptr[2] & 7);
 
 	/* Change the SCSI revision number */
 	data_ptr[2] = (data_ptr[2] & ~7) | 2;
 }
 
+/*
+ * Fix-up the return data from a READ CAPACITY command. My Feiya reader
+ * returns a value that is 1 too large.
+ */
+static void fix_read_capacity(Scsi_Cmnd *srb)
+{
+	unsigned char *dp;
+	unsigned long capacity;
+
+	/* verify that it's a READ CAPACITY command */
+	if (srb->cmnd[0] != READ_CAPACITY)
+		return;
+
+	dp = find_data_location(srb);
+
+	capacity = (dp[0]<<24) + (dp[1]<<16) + (dp[2]<<8) + (dp[3]);
+	US_DEBUGP("US: Fixing capacity: from %ld to %ld\n",
+	       capacity+1, capacity);
+	capacity--;
+	dp[0] = (capacity >> 24);
+	dp[1] = (capacity >> 16);
+	dp[2] = (capacity >> 8);
+	dp[3] = (capacity);
+}
+
 /***********************************************************************
  * Protocol routines
  ***********************************************************************/
@@ -346,8 +382,11 @@
 		if ((us->flags & US_FL_MODE_XLATE) && (old_cmnd == MODE_SENSE))
 			usb_stor_scsiSense10to6(srb);
 
-		/* fix the INQUIRY data if necessary */
+		/* Fix the INQUIRY data if necessary */
 		fix_inquiry_data(srb);
+
+		/* Fix the READ CAPACITY result if necessary */
+		if (us->flags & US_FL_FIX_CAPACITY)
+			fix_read_capacity(srb);
 	}
 }
-
diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
--- a/drivers/usb/storage/unusual_devs.h	Thu Sep  5 08:51:01 2002
+++ b/drivers/usb/storage/unusual_devs.h	Thu Sep  5 08:51:01 2002
@@ -514,6 +514,13 @@
 		US_SC_8070, US_PR_CB, NULL,
 		US_FL_FIX_INQUIRY ),
 
+/* aeb */
+UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff,
+	        "Feiya",
+	        "5-in-1 Card Reader",
+	        US_SC_SCSI, US_PR_BULK, NULL,
+	        US_FL_FIX_CAPACITY ),
+
 UNUSUAL_DEV(  0x097a, 0x0001, 0x0000, 0x0001,
 		"Minds@Work",
 		"Digital Wallet",
diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
--- a/drivers/usb/storage/usb.h	Thu Sep  5 08:51:01 2002
+++ b/drivers/usb/storage/usb.h	Thu Sep  5 08:51:01 2002
@@ -102,6 +102,7 @@
 #define US_FL_IGNORE_SER      0x00000010 /* Ignore the serial number given  */
 #define US_FL_SCM_MULT_TARG   0x00000020 /* supports multiple targets	    */
 #define US_FL_FIX_INQUIRY     0x00000040 /* INQUIRY response needs fixing   */
+#define US_FL_FIX_CAPACITY    0x00000080 /* READ CAPACITY response too big  */
 
 #define US_FL_DEV_ATTACHED    0x00010000 /* is the device attached?	    */
 #define US_FLIDX_IP_WANTED   17  /* 0x00020000	is an IRQ expected?	    */
