summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-07-03 21:46:06 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-07-03 21:46:06 +0000
commit3e414096429d55fbc8116171bba3487647bbe638 (patch)
tree2b5fcfd9d16fa3a32c829fc2076f6e3785b43374 /drivers/usb
parent20b23bfcf36fcb2d16d8b844501072541970637c (diff)
Merge with Linux 2.4.0-test3-pre2.
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Config.in8
-rw-r--r--drivers/usb/Makefile14
-rw-r--r--drivers/usb/microtek.c13
-rw-r--r--drivers/usb/microtek.h3
-rw-r--r--drivers/usb/ov511.c172
-rw-r--r--drivers/usb/printer.c8
-rw-r--r--drivers/usb/serial/digi_acceleport.c181
-rw-r--r--drivers/usb/serial/usb-serial.h3
-rw-r--r--drivers/usb/serial/usbserial.c71
-rw-r--r--drivers/usb/serial/visor.c6
-rw-r--r--drivers/usb/storage/Makefile32
-rw-r--r--drivers/usb/storage/debug.c185
-rw-r--r--drivers/usb/storage/debug.h66
-rw-r--r--drivers/usb/storage/protocol.c304
-rw-r--r--drivers/usb/storage/protocol.h63
-rw-r--r--drivers/usb/storage/scsiglue.c824
-rw-r--r--drivers/usb/storage/scsiglue.h54
-rw-r--r--drivers/usb/storage/transport.c904
-rw-r--r--drivers/usb/storage/transport.h132
-rw-r--r--drivers/usb/storage/usb.c803
-rw-r--r--drivers/usb/storage/usb.h182
-rw-r--r--drivers/usb/usb.c5
22 files changed, 3864 insertions, 169 deletions
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in
index 5032a9b75..8695c9529 100644
--- a/drivers/usb/Config.in
+++ b/drivers/usb/Config.in
@@ -48,10 +48,10 @@ comment 'USB Devices'
dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB
- dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI
- if [ "$CONFIG_USB_STORAGE" != "n" ]; then
- bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG
- fi
+ fi
+ dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI
+ if [ "$CONFIG_USB_STORAGE" != "n" ]; then
+ bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG
fi
dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT
dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 7d95c0c98..f59b44497 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -7,7 +7,7 @@
SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
MOD_IN_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) serial
+ALL_SUB_DIRS := $(SUB_DIRS) serial storage
# The target object and module list name.
@@ -49,6 +49,15 @@ else
endif
endif
+ifeq ($(CONFIG_USB_STORAGE),y)
+ SUB_DIRS += storage
+ obj-y += storage/usb-storage.o
+else
+ ifeq ($(CONFIG_USB_STORAGE),m)
+ MOD_IN_SUB_DIRS += storage
+ endif
+endif
+
# Each configuration option enables a list of files.
@@ -73,8 +82,7 @@ obj-$(CONFIG_USB_PRINTER) += printer.o
obj-$(CONFIG_USB_AUDIO) += audio.o
obj-$(CONFIG_USB_IBMCAM) += ibmcam.o
obj-$(CONFIG_USB_DC2XX) += dc2xx.o
-obj-$(CONFIG_USB_MDC800) += mdc800.o
-obj-$(CONFIG_USB_STORAGE) += usb-storage.o
+obj-$(CONFIG_USB_MDC800) += mdc800.o
obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_DABUSB) += dabusb.o
obj-$(CONFIG_USB_PLUSB) += plusb.o
diff --git a/drivers/usb/microtek.c b/drivers/usb/microtek.c
index 6586b8c40..70a50d91f 100644
--- a/drivers/usb/microtek.c
+++ b/drivers/usb/microtek.c
@@ -52,11 +52,10 @@
* you want it, just send mail.
*
* Status:
- *
- * This driver does not work properly yet.
+ *
* Untested with multiple scanners.
* Untested on SMP.
- * Untested on UHCI.
+ * Untested on a bigendian machine.
*
* History:
*
@@ -97,6 +96,8 @@
* 20000602 Version 0.2.0
* 20000603 various cosmetic changes
* 20000603 Version 0.2.1
+ * 20000620 minor cosmetic changes
+ * 20000620 Version 0.2.2
*/
#include <linux/module.h>
@@ -146,7 +147,7 @@ static struct usb_driver mts_usb_driver = {
/* Internal driver stuff */
-#define MTS_VERSION "0.2.1"
+#define MTS_VERSION "0.2.2"
#define MTS_NAME "microtek usb (rev " MTS_VERSION "): "
#define MTS_WARNING(x...) \
@@ -409,7 +410,7 @@ static int mts_scsi_host_reset (Scsi_Cmnd *srb)
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
- usb_reset_device(desc->usb_dev);
+ usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */
return 0; /* RANT why here 0 and not SUCCESS */
}
@@ -519,7 +520,7 @@ static void mts_transfer_done( struct urb *transfer )
MTS_INT_INIT();
- context->srb->result &= MTS_MAX_CHUNK_MASK;
+ context->srb->result &= MTS_SCSI_ERR_MASK;
context->srb->result |= (unsigned)context->status<<1;
mts_transfer_cleanup(transfer);
diff --git a/drivers/usb/microtek.h b/drivers/usb/microtek.h
index 939234560..8dcf734d3 100644
--- a/drivers/usb/microtek.h
+++ b/drivers/usb/microtek.h
@@ -68,6 +68,5 @@ struct mts_desc {
#define MTS_EP_IMAGE 0x3
#define MTS_EP_TOTAL 0x3
-#define MTS_MAX_CHUNK_MASK ~0x3fu
-/*maximum amount the scanner will transmit at once */
+#define MTS_SCSI_ERR_MASK ~0x3fu
diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c
index faf057c16..66f73d3c1 100644
--- a/drivers/usb/ov511.c
+++ b/drivers/usb/ov511.c
@@ -30,7 +30,7 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-static const char version[] = "1.17";
+static const char version[] = "1.18";
#define __NO_VERSION__
@@ -49,10 +49,6 @@ static const char version[] = "1.17";
#include <asm/semaphore.h>
#include <linux/wrapper.h>
-#ifdef CONFIG_KMOD
-#include <linux/kmod.h>
-#endif
-
#include "ov511.h"
#undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */
@@ -67,7 +63,7 @@ static const char version[] = "1.17";
#define DEFAULT_HEIGHT 480
#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
-#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : 24)
+#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : ((p) == VIDEO_PALETTE_YUV422 ? 8 : 24))
/* PARAMETER VARIABLES: */
static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */
@@ -626,6 +622,7 @@ static void ov511_dump_i2c_regs(struct usb_device *dev)
ov511_dump_i2c_range(dev, 0x00, 0x38);
}
+#if 0
static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
{
int i;
@@ -636,7 +633,6 @@ static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
}
}
-#if 0
static void ov511_dump_regs(struct usb_device *dev)
{
PDEBUG(1, "CAMERA INTERFACE REGS");
@@ -1088,7 +1084,7 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
#ifdef OV511_GBR422
static void
ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
- int iOutY, int iOutUV, int iHalf, int iWidth)
+ int iOutY, int iOutUV, int iHalf, int iWidth)
{
int k, l, m;
unsigned char *pIn;
@@ -1138,7 +1134,7 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
static void
ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
- int iOutY, int iOutUV, int iHalf, int iWidth)
+ int iOutY, int iOutUV, int iHalf, int iWidth)
{
#ifndef OV511_DUMPPIX
int k, l, m;
@@ -1231,6 +1227,139 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
}
#endif
+static void
+ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0,
+ int iOutY, int iOutUV, int iHalf, int iWidth)
+{
+ int k, l, m;
+ unsigned char *pIn;
+ unsigned char *pOut, *pOut1;
+
+ /* Just copy the Y's if in the first stripe */
+ if (!iHalf) {
+ pIn = pIn0 + 128;
+ pOut = pOut0 + iOutY;
+ for (k = 0; k < 4; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ for (m = 0; m < 8; m++) {
+ *pOut1++ = (*pIn++) & 0xF0;
+ }
+ pOut1 += iWidth - 8;
+ }
+ pOut += 8;
+ }
+ }
+
+ /* Use the first half of VUs to calculate value */
+ pIn = pIn0;
+ pOut = pOut0 + iOutUV;
+ for (l = 0; l < 4; l++) {
+ for (m=0; m<8; m++) {
+ unsigned char *p00 = pOut;
+ unsigned char *p01 = pOut+1;
+ unsigned char *p10 = pOut+iWidth;
+ unsigned char *p11 = pOut+iWidth+1;
+ int v = *(pIn+64) - 128;
+ int u = *pIn++ - 128;
+ int uv = ((u >> 4) & 0x0C) + (v >> 6);
+
+ *p00 |= uv;
+ *p01 |= uv;
+ *p10 |= uv;
+ *p11 |= uv;
+
+ pOut += 2;
+ }
+ pOut += (iWidth*2 - 16);
+ }
+
+ /* Just copy the other UV rows */
+ for (l = 0; l < 4; l++) {
+ for (m = 0; m < 8; m++) {
+ int v = *(pIn + 64) - 128;
+ int u = (*pIn++) - 128;
+ int uv = ((u >> 4) & 0x0C) + (v >> 6);
+ *(pOut) = uv;
+ pOut += 2;
+ }
+ pOut += (iWidth*2 - 16);
+ }
+
+ /* Calculate values if it's the second half */
+ if (iHalf) {
+ pIn = pIn0 + 128;
+ pOut = pOut0 + iOutY;
+ for (k = 0; k < 4; k++) {
+ pOut1 = pOut;
+ for (l=0; l<4; l++) {
+ for (m=0; m<4; m++) {
+ int y10 = *(pIn+8);
+ int y00 = *pIn++;
+ int y11 = *(pIn+8);
+ int y01 = *pIn++;
+ int uv = *pOut1;
+
+ *pOut1 = (y00 & 0xF0) | uv;
+ *(pOut1+1) = (y01 & 0xF0) | uv;
+ *(pOut1+iWidth) = (y10 & 0xF0) | uv;
+ *(pOut1+iWidth+1) = (y11 & 0xF0) | uv;
+
+ pOut1 += 2;
+ }
+ pOut1 += (iWidth*2 - 8);
+ pIn += 8;
+ }
+ pOut += 8;
+ }
+ }
+}
+
+static void
+ov511_parse_data_yuv422p(unsigned char *pIn0, unsigned char *pOut0,
+ int iOutY, int iOutUV, int iWidth, int iHeight)
+{
+ int k, l, m;
+ unsigned char *pIn;
+ unsigned char *pOut, *pOut1;
+ unsigned a = iWidth * iHeight;
+ unsigned w = iWidth / 2;
+
+ pIn = pIn0;
+ pOut = pOut0 + iOutUV + a;
+ for (k = 0; k < 8; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ *pOut1 = *(pOut1 + w) = *pIn++;
+ pOut1++;
+ }
+ pOut += iWidth;
+ }
+
+ pIn = pIn0 + 64;
+ pOut = pOut0 + iOutUV + a + a/2;
+ for (k = 0; k < 8; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ *pOut1 = *(pOut1 + w) = *pIn++;
+ pOut1++;
+ }
+ pOut += iWidth;
+ }
+
+ pIn = pIn0 + 128;
+ pOut = pOut0 + iOutY;
+ for (k = 0; k < 4; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ for (m = 0; m < 8; m++)
+ *pOut1++ =*pIn++;
+ pOut1 += iWidth - 8;
+ }
+ pOut += 8;
+ }
+}
+
/*
* For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
* The segments represent 4 squares of 8x8 pixels as follows:
@@ -1432,7 +1561,7 @@ check_middle:
frame->segment < frame->width * frame->height / 256) {
int iSegY, iSegUV;
int iY, jY, iUV, jUV;
- int iOutY, iOutUV;
+ int iOutY, iOutYP, iOutUV, iOutUVP;
unsigned char *pOut;
iSegY = iSegUV = frame->segment;
@@ -1465,10 +1594,12 @@ check_middle:
*/
iY = iSegY / (frame->width / WDIV);
jY = iSegY - iY * (frame->width / WDIV);
- iOutY = (iY*HDIV*frame->width + jY*WDIV) * (frame->depth >> 3);
+ iOutYP = iY*HDIV*frame->width + jY*WDIV;
+ iOutY = iOutYP * (frame->depth >> 3);
iUV = iSegUV / (frame->width / WDIV * 2);
jUV = iSegUV - iUV * (frame->width / WDIV * 2);
- iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3);
+ iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2;
+ iOutUV = iOutUVP * (frame->depth >> 3);
switch (frame->format) {
case VIDEO_PALETTE_GREY:
@@ -1478,6 +1609,16 @@ check_middle:
ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV,
iY & 1, frame->width);
break;
+ case VIDEO_PALETTE_YUV422:
+ ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV,
+ iY & 1, frame->width);
+ break;
+ case VIDEO_PALETTE_YUV422P:
+ ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2,
+ frame->width, frame->height);
+ break;
+ default:
+ err("Unsupported format: %d", frame->format);
}
pData = &cdata[iPix];
@@ -1995,17 +2136,22 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
- PDEBUG(4, "MCAPTURE");
+ PDEBUG(4, "CMCAPTURE");
PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
vm.frame, vm.width, vm.height, vm.format);
if (vm.format != VIDEO_PALETTE_RGB24 &&
+ vm.format != VIDEO_PALETTE_YUV422 &&
+ vm.format != VIDEO_PALETTE_YUV422P &&
vm.format != VIDEO_PALETTE_GREY)
return -EINVAL;
if ((vm.frame != 0) && (vm.frame != 1))
return -EINVAL;
+ if (vm.width > DEFAULT_WIDTH || vm.height > DEFAULT_HEIGHT)
+ return -EINVAL;
+
if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
return -EBUSY;
diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c
index a36cd70d1..638a91ed9 100644
--- a/drivers/usb/printer.c
+++ b/drivers/usb/printer.c
@@ -70,7 +70,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H
#define USBLP_MINORS 16
#define USBLP_MINOR_BASE 0
-#define USBLP_WRITE_TIMEOUT (60*HZ) /* 60 seconds */
+#define USBLP_WRITE_TIMEOUT (60*60*HZ) /* 60 minutes */
struct usblp {
struct usb_device *dev; /* USB device */
@@ -147,10 +147,8 @@ static int usblp_check_status(struct usblp *usblp)
info("usblp%d: off-line", usblp->minor);
return -EIO;
}
- if (~status & LP_PERRORP) {
- info("usblp%d: on fire", usblp->minor);
- return -EIO;
- }
+ info("usblp%d: on fire", usblp->minor);
+ return -EIO;
}
return 0;
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index c3cce34c3..4355f3773 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -14,6 +14,25 @@
* Peter Berger (pberger@brimson.com)
* Al Borchers (borchers@steinerpoint.com)
*
+* (6/27/2000) pberger and borchers
+* -- Zeroed out sync field in the wakeup_task before first use;
+* otherwise the uninitialized value might prevent the task from
+* being scheduled.
+* -- Initialized ret value to 0 in write_bulk_callback, otherwise
+* the uninitialized value could cause a spurious debugging message.
+*
+* (6/22/2000) pberger and borchers
+* -- Made cond_wait_... inline--apparently on SPARC the flags arg
+* to spin_lock_irqsave cannot be passed to another function
+* to call spin_unlock_irqrestore. Thanks to Pauline Middelink.
+* -- In digi_set_modem_signals the inner nested spin locks use just
+* spin_lock() rather than spin_lock_irqsave(). The old code
+* mistakenly left interrupts off. Thanks to Pauline Middelink.
+* -- copy_from_user (which can sleep) is no longer called while a
+* spinlock is held. We copy to a local buffer before getting
+* the spinlock--don't like the extra copy but the code is simpler.
+* -- Printk and dbg are no longer called while a spin lock is held.
+*
* (6/4/2000) pberger and borchers
* -- Replaced separate calls to spin_unlock_irqrestore and
* interruptible_sleep_on_interruptible with a new function
@@ -119,8 +138,10 @@
* - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to
* recheck the condition they are sleeping on. This is defensive,
* in case a wake up is lost.
+* - Following Documentation/DocBook/kernel-locking.pdf no spin locks
+* are held when calling copy_to/from_user or printk.
*
-* $Id: digi_acceleport.c,v 1.56 2000/06/07 22:47:30 root Exp root $
+* $Id: digi_acceleport.c,v 1.63 2000/06/28 18:28:31 root Exp root $
*/
#include <linux/config.h>
@@ -307,7 +328,7 @@ typedef struct digi_private {
int dp_transmit_idle;
int dp_in_close;
wait_queue_head_t dp_close_wait; /* wait queue for close */
- struct tq_struct dp_tasks;
+ struct tq_struct dp_wakeup_task;
} digi_private_t;
@@ -402,7 +423,7 @@ struct usb_serial_device_type digi_acceleport_device = {
* wake ups. This is used to implement condition variables.
*/
-static long cond_wait_interruptible_timeout_irqrestore(
+static inline long cond_wait_interruptible_timeout_irqrestore(
wait_queue_head_t *q, long timeout,
spinlock_t *lock, unsigned long flags )
{
@@ -465,6 +486,7 @@ static void digi_wakeup_write( struct usb_serial_port *port )
/* wake up other tty processes */
wake_up_interruptible( &tty->write_wait );
+ /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
}
@@ -515,17 +537,17 @@ dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count );
if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) {
count -= len;
buf += len;
- } else {
- dbg(
- "digi_write_oob_command: usb_submit_urb failed, ret=%d",
- ret );
- break;
}
}
spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
+ if( ret ) {
+ dbg( "digi_write_oob_command: usb_submit_urb failed, ret=%d",
+ ret );
+ }
+
return( ret );
}
@@ -569,8 +591,8 @@ count );
}
/* len must be a multiple of 4 and small enough to */
- /* guarantee the write will send all data (or none), */
- /* so commands are not split */
+ /* guarantee the write will send buffered data first, */
+ /* so commands are in order with data and not split */
len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len );
if( len > 4 )
len &= ~3;
@@ -588,35 +610,21 @@ count );
port->write_urb->transfer_buffer_length = len;
}
-#ifdef DEBUG_DATA
- {
- int i;
-
- printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=",
- priv->dp_port_num, port->write_urb->transfer_buffer_length );
- for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
- printk( "%.2x ",
- ((unsigned char *)port->write_urb->transfer_buffer)[i] );
- }
- printk( "\n" );
- }
-#endif
-
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
priv->dp_buf_len = 0;
count -= len;
buf += len;
- } else {
- dbg(
- "digi_write_inb_command: usb_submit_urb failed, ret=%d",
- ret );
- break;
}
}
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ if( ret ) {
+ dbg( "digi_write_inb_command: usb_submit_urb failed, ret=%d",
+ ret );
+ }
+
return( ret );
}
@@ -647,10 +655,10 @@ dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x",
port_priv->dp_port_num, modem_signals );
spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
- spin_lock_irqsave( &port_priv->dp_port_lock, flags );
+ spin_lock( &port_priv->dp_port_lock );
while( oob_port->write_urb->status == -EINPROGRESS ) {
- spin_unlock_irqrestore( &port_priv->dp_port_lock, flags );
+ spin_unlock( &port_priv->dp_port_lock );
cond_wait_interruptible_timeout_irqrestore(
&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
&oob_priv->dp_port_lock, flags );
@@ -658,7 +666,7 @@ port_priv->dp_port_num, modem_signals );
return( -EINTR );
}
spin_lock_irqsave( &oob_priv->dp_port_lock, flags );
- spin_lock_irqsave( &port_priv->dp_port_lock, flags );
+ spin_lock( &port_priv->dp_port_lock );
}
data[0] = DIGI_CMD_SET_DTR_SIGNAL;
@@ -679,14 +687,16 @@ port_priv->dp_port_num, modem_signals );
port_priv->dp_modem_signals =
(port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS))
| (modem_signals&(TIOCM_DTR|TIOCM_RTS));
- } else {
- dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d",
- ret );
}
- spin_unlock_irqrestore( &port_priv->dp_port_lock, flags );
+ spin_unlock( &port_priv->dp_port_lock );
spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags );
+ if( ret ) {
+ dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d",
+ ret );
+ }
+
return( ret );
}
@@ -1046,12 +1056,19 @@ static int digi_write( struct usb_serial_port *port, int from_user,
int ret,data_len,new_len;
digi_private_t *priv = (digi_private_t *)(port->private);
unsigned char *data = port->write_urb->transfer_buffer;
+ unsigned char user_buf[64]; /* 64 bytes is max USB bulk packet */
unsigned long flags = 0;
dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d",
priv->dp_port_num, count, from_user, in_interrupt() );
+ /* copy user data (which can sleep) before getting spin lock */
+ count = MIN( 64, MIN( count, port->bulk_out_size-2 ) );
+ if( from_user && copy_from_user( user_buf, buf, count ) ) {
+ return( -EFAULT );
+ }
+
/* be sure only one write proceeds at a time */
/* there are races on the port private buffer */
/* and races to check write_urb->status */
@@ -1096,40 +1113,19 @@ priv->dp_port_num, count, from_user, in_interrupt() );
data += priv->dp_buf_len;
/* copy in new data */
- if( from_user ) {
- if( copy_from_user( data, buf, new_len ) ) {
- spin_unlock_irqrestore( &priv->dp_port_lock, flags );
- return( -EFAULT );
- }
- } else {
- memcpy( data, buf, new_len );
- }
-
-#ifdef DEBUG_DATA
- {
- int i;
-
- printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=",
- priv->dp_port_num, port->write_urb->transfer_buffer_length );
- for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
- printk( "%.2x ",
- ((unsigned char *)port->write_urb->transfer_buffer)[i] );
- }
- printk( "\n" );
- }
-#endif
+ memcpy( data, from_user ? user_buf : buf, new_len );
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
ret = new_len;
priv->dp_buf_len = 0;
- } else {
- dbg( "digi_write: usb_submit_urb failed, ret=%d",
- ret );
}
/* return length of new data written, or error */
-dbg( "digi_write: returning %d", ret );
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ if( ret < 0 ) {
+ dbg( "digi_write: usb_submit_urb failed, ret=%d", ret );
+ }
+dbg( "digi_write: returning %d", ret );
return( ret );
}
@@ -1141,7 +1137,7 @@ static void digi_write_bulk_callback( struct urb *urb )
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
digi_private_t *priv = (digi_private_t *)(port->private);
- int ret;
+ int ret = 0;
dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
@@ -1175,24 +1171,8 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf,
priv->dp_buf_len );
-#ifdef DEBUG_DATA
- {
- int i;
-
- printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, data=",
- priv->dp_port_num, port->write_urb->transfer_buffer_length );
- for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) {
- printk( "%.2x ",
- ((unsigned char *)port->write_urb->transfer_buffer)[i] );
- }
- printk( "\n" );
- }
-#endif
-
if( (ret=usb_submit_urb(port->write_urb)) == 0 ) {
priv->dp_buf_len = 0;
- } else {
- dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d", ret );
}
}
@@ -1200,13 +1180,16 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
/* wake up processes sleeping on writes immediately */
digi_wakeup_write( port );
- spin_unlock( &priv->dp_port_lock );
-
/* also queue up a wakeup at scheduler time, in case we */
/* lost the race in write_chan(). */
- priv->dp_tasks.routine = (void *)digi_wakeup_write_lock;
- priv->dp_tasks.data = (void *)port;
- queue_task( &(priv->dp_tasks), &tq_scheduler );
+ queue_task( &priv->dp_wakeup_task, &tq_scheduler );
+
+ spin_unlock( &priv->dp_port_lock );
+
+ if( ret ) {
+ dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d",
+ ret );
+ }
}
@@ -1219,8 +1202,6 @@ static int digi_write_room( struct usb_serial_port *port )
unsigned long flags = 0;
-dbg( "digi_write_room: TOP: port=%d", priv->dp_port_num );
-
spin_lock_irqsave( &priv->dp_port_lock, flags );
if( port->write_urb->status == -EINPROGRESS )
@@ -1242,8 +1223,6 @@ static int digi_chars_in_buffer( struct usb_serial_port *port )
digi_private_t *priv = (digi_private_t *)(port->private);
-dbg( "digi_chars_in_buffer: TOP: port=%d", priv->dp_port_num );
-
if( port->write_urb->status == -EINPROGRESS ) {
dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2 );
/* return( port->bulk_out_size - 2 ); */
@@ -1455,13 +1434,14 @@ static int digi_startup_device( struct usb_serial *serial )
int i,ret = 0;
- spin_lock( &startup_lock );
-
/* be sure this happens exactly once */
+ spin_lock( &startup_lock );
if( device_startup ) {
spin_unlock( &startup_lock );
return( 0 );
}
+ device_startup = 1;
+ spin_unlock( &startup_lock );
/* start reading from each bulk in endpoint for the device */
for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) {
@@ -1474,10 +1454,6 @@ static int digi_startup_device( struct usb_serial *serial )
}
- device_startup = 1;
-
- spin_unlock( &startup_lock );
-
return( ret );
}
@@ -1516,8 +1492,10 @@ dbg( "digi_startup: TOP" );
priv->dp_transmit_idle = 0;
priv->dp_in_close = 0;
init_waitqueue_head( &priv->dp_close_wait );
- priv->dp_tasks.next = NULL;
- priv->dp_tasks.data = NULL;
+ priv->dp_wakeup_task.next = NULL;
+ priv->dp_wakeup_task.sync = 0;
+ priv->dp_wakeup_task.routine = (void *)digi_wakeup_write_lock;
+ priv->dp_wakeup_task.data = (void *)(&serial->port[i]);
spin_lock_init( &priv->dp_port_lock );
/* initialize write wait queue for this port */
@@ -1595,17 +1573,6 @@ dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num );
goto resubmit;
}
-#ifdef DEBUG_DATA
-if( urb->actual_length ) {
- printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=",
- priv->dp_port_num, urb->actual_length );
- for( i=0; i<urb->actual_length; ++i ) {
- printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] );
- }
- printk( "\n" );
-}
-#endif
-
if( urb->actual_length != len + 2 )
err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", priv->dp_port_num, opcode, len, urb->actual_length, status );
diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
index b8e2e1d74..430a8fa64 100644
--- a/drivers/usb/serial/usb-serial.h
+++ b/drivers/usb/serial/usb-serial.h
@@ -51,6 +51,8 @@ struct usb_serial_port {
wait_queue_head_t write_wait;
+ struct tq_struct tqueue; /* task queue for line discipline waking up */
+
void * private; /* data private to the specific port */
};
@@ -178,6 +180,5 @@ static inline int port_paranoia_check (struct usb_serial_port *port, const char
return 0;
}
-
#endif /* ifdef __LINUX_USB_SERIAL_H */
diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c
index d60fd8e33..261d5ac00 100644
--- a/drivers/usb/serial/usbserial.c
+++ b/drivers/usb/serial/usbserial.c
@@ -14,6 +14,10 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (06/25/2000) gkh
+ * Changed generic_write_bulk_callback to not call wake_up_interruptible
+ * directly, but to have port_softint do it at a safer time.
+ *
* (06/23/2000) gkh
* Cleaned up debugging statements in a quest to find UHCI timeout bug.
*
@@ -222,6 +226,8 @@
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
#ifdef CONFIG_USB_SERIAL_DEBUG
#define DEBUG
@@ -803,36 +809,32 @@ static int generic_write (struct usb_serial_port *port, int from_user, const uns
static int generic_write_room (struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
- int room;
+ int room = 0;
dbg(__FUNCTION__ " - port %d", port->number);
- if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS)
- room = 0;
- else
+ if (serial->num_bulk_out)
+ if (port->write_urb->status != -EINPROGRESS)
room = port->bulk_out_size;
- dbg(__FUNCTION__ " returns %d", room);
- return (room);
- }
- return (0);
+ dbg(__FUNCTION__ " - returns %d", room);
+ return (room);
}
static int generic_chars_in_buffer (struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
+ int chars = 0;
dbg(__FUNCTION__ " - port %d", port->number);
- if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS) {
- return (port->bulk_out_size);
- }
- }
+ if (serial->num_bulk_out)
+ if (port->write_urb->status == -EINPROGRESS)
+ chars = port->write_urb->transfer_buffer_length;
- return (0);
+ dbg (__FUNCTION__ " - returns %d", chars);
+ return (chars);
}
@@ -844,9 +846,10 @@ static void generic_read_bulk_callback (struct urb *urb)
unsigned char *data = urb->transfer_buffer;
int i;
- dbg (__FUNCTION__ " - enter");
+ dbg(__FUNCTION__ " - port %d", port->number);
if (!serial) {
+ dbg(__FUNCTION__ " - bad serial pointer, exiting");
return;
}
@@ -877,8 +880,6 @@ static void generic_read_bulk_callback (struct urb *urb)
if (usb_submit_urb(urb))
dbg(__FUNCTION__ " - failed resubmitting read urb");
- dbg (__FUNCTION__ " - exit");
-
return;
}
@@ -887,11 +888,11 @@ static void generic_write_bulk_callback (struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
- struct tty_struct *tty;
- dbg (__FUNCTION__ " - enter");
+ dbg(__FUNCTION__ " - port %d", port->number);
if (!serial) {
+ dbg(__FUNCTION__ " - bad serial pointer, exiting");
return;
}
@@ -900,18 +901,36 @@ static void generic_write_bulk_callback (struct urb *urb)
return;
}
+ queue_task(&port->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return;
+}
+
+
+static void port_softint(void *private)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)private;
+ struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+ struct tty_struct *tty;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ if (!serial) {
+ return;
+ }
+
tty = port->tty;
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+ dbg(__FUNCTION__ " - write wakeup call.");
(tty->ldisc.write_wakeup)(tty);
+ }
wake_up_interruptible(&tty->write_wait);
-
- dbg (__FUNCTION__ " - exit");
-
- return;
}
+
static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
{
struct usb_serial *serial = NULL;
@@ -1117,6 +1136,8 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
port->number = i + serial->minor;
port->serial = serial;
port->magic = USB_SERIAL_PORT_MAGIC;
+ port->tqueue.routine = port_softint;
+ port->tqueue.data = port;
}
/* initialize the devfs nodes for this device and let the user know what ports we are bound to */
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 8be2dd351..fa7f475c6 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -11,6 +11,10 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (06/25/2000) gkh
+ * Fixed bug in visor_unthrottle that should help with the disconnect in PPP
+ * bug that people have been reporting.
+ *
* (06/23/2000) gkh
* Cleaned up debugging statements in a quest to find UHCI timeout bug.
*
@@ -137,7 +141,7 @@ static void visor_unthrottle (struct usb_serial_port *port)
{
dbg(__FUNCTION__ " - port %d", port->number);
- if (usb_unlink_urb (port->read_urb))
+ if (usb_submit_urb (port->read_urb))
dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
return;
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
new file mode 100644
index 000000000..c3dd30dee
--- /dev/null
+++ b/drivers/usb/storage/Makefile
@@ -0,0 +1,32 @@
+#
+# Makefile for the USB Mass Storage device drivers.
+#
+
+O_TARGET := usb-storage.o
+M_OBJS := usb-storage.o
+O_OBJS := scsiglue.o protocol.o transport.o debug.o usb.o
+MOD_LIST_NAME := USB_STORAGE_MODULES
+
+CFLAGS_scsiglue.o:= -I../../scsi/
+CFLAGS_protocol.o:= -I../../scsi/
+CFLAGS_transport.o:= -I../../scsi/
+CFLAGS_debug.o:= -I../../scsi/
+CFLAGS_usb.o:= -I../../scsi/
+
+ifeq ($(CONFIG_USB_STORAGE_DEBUG),y)
+ O_OBJS += debug.o
+endif
+
+ifeq ($(CONFIG_USB_STORAGE_FREECOM),y)
+ O_OBJS += freecom.o
+endif
+
+ifeq ($(CONFIG_USB_STORAGE_SHUTTLE_SMARTMEDIA),y)
+ O_OBJS += shuttle_sm.o
+endif
+
+ifeq ($(CONFIG_USB_STORAGE_SHUTTLE_COMPACTFLASH),y)
+ O_OBJS += shuttle_cf.o
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
new file mode 100644
index 000000000..d8845d8c2
--- /dev/null
+++ b/drivers/usb/storage/debug.c
@@ -0,0 +1,185 @@
+/* Driver for USB Mass Storage compliant devices
+ * Debugging Functions Source Code File
+ *
+ * $Id: debug.c,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Initial work by:
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "debug.h"
+
+void usb_stor_show_command(Scsi_Cmnd *srb)
+{
+ char *what = NULL;
+
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+ case REZERO_UNIT: what = "REZERO_UNIT"; break;
+ case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+ case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+ case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+ case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+ case READ_6: what = "READ_6"; break;
+ case WRITE_6: what = "WRITE_6"; break;
+ case SEEK_6: what = "SEEK_6"; break;
+ case READ_REVERSE: what = "READ_REVERSE"; break;
+ case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+ case SPACE: what = "SPACE"; break;
+ case INQUIRY: what = "INQUIRY"; break;
+ case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+ case MODE_SELECT: what = "MODE_SELECT"; break;
+ case RESERVE: what = "RESERVE"; break;
+ case RELEASE: what = "RELEASE"; break;
+ case COPY: what = "COPY"; break;
+ case ERASE: what = "ERASE"; break;
+ case MODE_SENSE: what = "MODE_SENSE"; break;
+ case START_STOP: what = "START_STOP"; break;
+ case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+ case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+ case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+ case SET_WINDOW: what = "SET_WINDOW"; break;
+ case READ_CAPACITY: what = "READ_CAPACITY"; break;
+ case READ_10: what = "READ_10"; break;
+ case WRITE_10: what = "WRITE_10"; break;
+ case SEEK_10: what = "SEEK_10"; break;
+ case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+ case VERIFY: what = "VERIFY"; break;
+ case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+ case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+ case SEARCH_LOW: what = "SEARCH_LOW"; break;
+ case SET_LIMITS: what = "SET_LIMITS"; break;
+ case READ_POSITION: what = "READ_POSITION"; break;
+ case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+ case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+ case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+ case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+ case COMPARE: what = "COMPARE"; break;
+ case COPY_VERIFY: what = "COPY_VERIFY"; break;
+ case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+ case READ_BUFFER: what = "READ_BUFFER"; break;
+ case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+ case READ_LONG: what = "READ_LONG"; break;
+ case WRITE_LONG: what = "WRITE_LONG"; break;
+ case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+ case WRITE_SAME: what = "WRITE_SAME"; break;
+ case READ_TOC: what = "READ_TOC"; break;
+ case LOG_SELECT: what = "LOG_SELECT"; break;
+ case LOG_SENSE: what = "LOG_SENSE"; break;
+ case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+ case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+ case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+ case READ_12: what = "READ_12"; break;
+ case WRITE_12: what = "WRITE_12"; break;
+ case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+ case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+ case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+ case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+ case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+ case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+ case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+ default: break;
+ }
+ US_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len);
+ US_DEBUGP("%02x %02x %02x %02x "
+ "%02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n",
+ srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3],
+ srb->cmnd[4], srb->cmnd[5], srb->cmnd[6], srb->cmnd[7],
+ srb->cmnd[8], srb->cmnd[9], srb->cmnd[10],
+ srb->cmnd[11]);
+}
+
+void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd )
+{
+ int i=0, bufferSize = cmd->request_bufflen;
+ u8* buffer = cmd->request_buffer;
+ struct scatterlist* sg = (struct scatterlist*)cmd->request_buffer;
+
+ US_DEBUGP("Dumping information about %p.\n", cmd );
+ US_DEBUGP("cmd->cmnd[0] value is %d.\n", cmd->cmnd[0] );
+ US_DEBUGP("(MODE_SENSE is %d and MODE_SENSE_10 is %d)\n",
+ MODE_SENSE, MODE_SENSE_10 );
+
+ US_DEBUGP("buffer is %p with length %d.\n", buffer, bufferSize );
+ for ( i=0; i<bufferSize; i+=16 )
+ {
+ US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n"
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buffer[i],
+ buffer[i+1],
+ buffer[i+2],
+ buffer[i+3],
+ buffer[i+4],
+ buffer[i+5],
+ buffer[i+6],
+ buffer[i+7],
+ buffer[i+8],
+ buffer[i+9],
+ buffer[i+10],
+ buffer[i+11],
+ buffer[i+12],
+ buffer[i+13],
+ buffer[i+14],
+ buffer[i+15] );
+ }
+
+ US_DEBUGP("Buffer has %d scatterlists.\n", cmd->use_sg );
+ for ( i=0; i<cmd->use_sg; i++ )
+ {
+ US_DEBUGP("Length of scatterlist %d is %d.\n",i,sg[i].length);
+ US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n"
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ sg[i].address[0],
+ sg[i].address[1],
+ sg[i].address[2],
+ sg[i].address[3],
+ sg[i].address[4],
+ sg[i].address[5],
+ sg[i].address[6],
+ sg[i].address[7],
+ sg[i].address[8],
+ sg[i].address[9],
+ sg[i].address[10],
+ sg[i].address[11],
+ sg[i].address[12],
+ sg[i].address[13],
+ sg[i].address[14],
+ sg[i].address[15]);
+ }
+}
+
diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h
new file mode 100644
index 000000000..ab5bef0ac
--- /dev/null
+++ b/drivers/usb/storage/debug.h
@@ -0,0 +1,66 @@
+/* Driver for USB Mass Storage compliant devices
+ * Debugging Functions Header File
+ *
+ * $Id: debug.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Initial work by:
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/blk.h>
+#include "scsi.h"
+
+#define USB_STORAGE "usb-storage: "
+
+#ifdef CONFIG_USB_STORAGE_DEBUG
+void usb_stor_show_command(Scsi_Cmnd *srb);
+void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd );
+#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x )
+#define US_DEBUGPX(x...) printk( ## x )
+#define US_DEBUG(x) x
+#else
+#define US_DEBUGP(x...)
+#define US_DEBUGPX(x...)
+#define US_DEBUG(x)
+#endif
+
+#endif
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
new file mode 100644
index 000000000..d3624dc76
--- /dev/null
+++ b/drivers/usb/storage/protocol.c
@@ -0,0 +1,304 @@
+/* Driver for USB Mass Storage compliant devices
+ *
+ * $Id: protocol.c,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Developed with the assistance of:
+ * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
+ *
+ * Initial work by:
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "protocol.h"
+#include "usb.h"
+#include "debug.h"
+#include "scsiglue.h"
+#include "transport.h"
+
+/***********************************************************************
+ * Protocol routines
+ ***********************************************************************/
+
+void usb_stor_ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int old_cmnd = 0;
+
+ /* Fix some commands -- this is a form of mode translation
+ * ATAPI devices only accept 12 byte long commands
+ *
+ * NOTE: This only works because a Scsi_Cmnd struct field contains
+ * a unsigned char cmnd[12], so we know we have storage available
+ */
+
+ /* set command length to 12 bytes */
+ srb->cmd_len = 12;
+
+ /* determine the correct (or minimum) data length for these commands */
+ switch (srb->cmnd[0]) {
+
+ /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+ case MODE_SENSE:
+ case MODE_SELECT:
+ /* save the command so we can tell what it was */
+ old_cmnd = srb->cmnd[0];
+
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = 0;
+ srb->cmnd[4] = 0;
+ srb->cmnd[3] = 0;
+ srb->cmnd[2] = srb->cmnd[2];
+ srb->cmnd[1] = srb->cmnd[1];
+ srb->cmnd[0] = srb->cmnd[0] | 0x40;
+ break;
+
+ /* change READ_6/WRITE_6 to READ_10/WRITE_10, which
+ * are ATAPI commands */
+ case WRITE_6:
+ case READ_6:
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = srb->cmnd[3];
+ srb->cmnd[4] = srb->cmnd[2];
+ srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+ srb->cmnd[2] = 0;
+ srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+ srb->cmnd[0] = srb->cmnd[0] | 0x20;
+ break;
+ } /* end switch on cmnd[0] */
+
+ /* convert MODE_SELECT data here */
+ if (old_cmnd == MODE_SELECT)
+ usb_stor_scsiSense6to10(srb);
+
+ /* send the command to the transport layer */
+ usb_stor_invoke_transport(srb, us);
+
+ /* Fix the MODE_SENSE data if we translated the command */
+ if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD))
+ usb_stor_scsiSense10to6(srb);
+
+ /* 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
+ */
+ if (srb->cmnd[0] == INQUIRY) {
+ ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+ }
+}
+
+
+void usb_stor_ufi_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int old_cmnd = 0;
+
+ /* fix some commands -- this is a form of mode translation
+ * UFI devices only accept 12 byte long commands
+ *
+ * NOTE: This only works because a Scsi_Cmnd struct field contains
+ * a unsigned char cmnd[12], so we know we have storage available
+ */
+
+ /* set command length to 12 bytes (this affects the transport layer) */
+ srb->cmd_len = 12;
+
+ /* determine the correct (or minimum) data length for these commands */
+ switch (srb->cmnd[0]) {
+
+ /* for INQUIRY, UFI devices only ever return 36 bytes */
+ case INQUIRY:
+ srb->cmnd[4] = 36;
+ break;
+
+ /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+ case MODE_SENSE:
+ case MODE_SELECT:
+ /* save the command so we can tell what it was */
+ old_cmnd = srb->cmnd[0];
+
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+
+ /* if we're sending data, we send all. If getting data,
+ * get the minimum */
+ if (srb->cmnd[0] == MODE_SELECT)
+ srb->cmnd[8] = srb->cmnd[4];
+ else
+ srb->cmnd[8] = 8;
+
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = 0;
+ srb->cmnd[4] = 0;
+ srb->cmnd[3] = 0;
+ srb->cmnd[2] = srb->cmnd[2];
+ srb->cmnd[1] = srb->cmnd[1];
+ srb->cmnd[0] = srb->cmnd[0] | 0x40;
+ break;
+
+ /* again, for MODE_SENSE_10, we get the minimum (8) */
+ case MODE_SENSE_10:
+ srb->cmnd[7] = 0;
+ srb->cmnd[8] = 8;
+ break;
+
+ /* for REQUEST_SENSE, UFI devices only ever return 18 bytes */
+ case REQUEST_SENSE:
+ srb->cmnd[4] = 18;
+ break;
+
+ /* change READ_6/WRITE_6 to READ_10/WRITE_10, which
+ * are UFI commands */
+ case WRITE_6:
+ case READ_6:
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = srb->cmnd[3];
+ srb->cmnd[4] = srb->cmnd[2];
+ srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+ srb->cmnd[2] = 0;
+ srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+ srb->cmnd[0] = srb->cmnd[0] | 0x20;
+ break;
+ } /* end switch on cmnd[0] */
+
+ /* convert MODE_SELECT data here */
+ if (old_cmnd == MODE_SELECT)
+ usb_stor_scsiSense6to10(srb);
+
+ /* send the command to the transport layer */
+ usb_stor_invoke_transport(srb, us);
+
+ /* Fix the MODE_SENSE data if we translated the command */
+ if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD))
+ usb_stor_scsiSense10to6(srb);
+
+ /* 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
+ */
+ if (srb->cmnd[0] == INQUIRY) {
+ ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+ }
+}
+
+void usb_stor_transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ /* This code supports devices which do not support {READ|WRITE}_6
+ * Apparently, neither Windows or MacOS will use these commands,
+ * so some devices do not support them
+ */
+ if (us->flags & US_FL_MODE_XLATE) {
+
+ /* translate READ_6 to READ_10 */
+ if (srb->cmnd[0] == 0x08) {
+
+ /* get the control */
+ srb->cmnd[9] = us->srb->cmnd[5];
+
+ /* get the length */
+ srb->cmnd[8] = us->srb->cmnd[6];
+ srb->cmnd[7] = 0;
+
+ /* set the reserved area to 0 */
+ srb->cmnd[6] = 0;
+
+ /* get LBA */
+ srb->cmnd[5] = us->srb->cmnd[3];
+ srb->cmnd[4] = us->srb->cmnd[2];
+ srb->cmnd[3] = 0;
+ srb->cmnd[2] = 0;
+
+ /* LUN and other info in cmnd[1] can stay */
+
+ /* fix command code */
+ srb->cmnd[0] = 0x28;
+
+ US_DEBUGP("Changing READ_6 to READ_10\n");
+ US_DEBUG(usb_stor_show_command(srb));
+ }
+
+ /* translate WRITE_6 to WRITE_10 */
+ if (srb->cmnd[0] == 0x0A) {
+
+ /* get the control */
+ srb->cmnd[9] = us->srb->cmnd[5];
+
+ /* get the length */
+ srb->cmnd[8] = us->srb->cmnd[4];
+ srb->cmnd[7] = 0;
+
+ /* set the reserved area to 0 */
+ srb->cmnd[6] = 0;
+
+ /* get LBA */
+ srb->cmnd[5] = us->srb->cmnd[3];
+ srb->cmnd[4] = us->srb->cmnd[2];
+ srb->cmnd[3] = 0;
+ srb->cmnd[2] = 0;
+
+ /* LUN and other info in cmnd[1] can stay */
+
+ /* fix command code */
+ srb->cmnd[0] = 0x2A;
+
+ US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
+ US_DEBUG(usb_stor_show_command(us->srb));
+ }
+ } /* if (us->flags & US_FL_MODE_XLATE) */
+
+ /* send the command to the transport layer */
+ usb_stor_invoke_transport(srb, us);
+
+ /* fix the results of an INQUIRY */
+ if (srb->cmnd[0] == INQUIRY) {
+ US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+ ((unsigned char*)us->srb->request_buffer)[2] |= 2;
+ }
+}
+
diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h
new file mode 100644
index 000000000..1a343af41
--- /dev/null
+++ b/drivers/usb/storage/protocol.h
@@ -0,0 +1,63 @@
+/* Driver for USB Mass Storage compliant devices
+ * Protocol Functions Header File
+ *
+ * $Id: protocol.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _PROTOCOL_H_
+#define _PROTOCOL_H_
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "usb.h"
+
+/* Sub Classes */
+
+#define US_SC_RBC 0x01 /* Typically, flash devices */
+#define US_SC_8020 0x02 /* CD-ROM */
+#define US_SC_QIC 0x03 /* QIC-157 Tapes */
+#define US_SC_UFI 0x04 /* Floppy */
+#define US_SC_8070 0x05 /* Removable media */
+#define US_SC_SCSI 0x06 /* Transparent */
+#define US_SC_MIN US_SC_RBC
+#define US_SC_MAX US_SC_SCSI
+
+extern void usb_stor_ATAPI_command(Scsi_Cmnd*, struct us_data*);
+extern void usb_stor_ufi_command(Scsi_Cmnd*, struct us_data*);
+extern void usb_stor_transparent_scsi_command(Scsi_Cmnd*, struct us_data*);
+
+#endif
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
new file mode 100644
index 000000000..71487235d
--- /dev/null
+++ b/drivers/usb/storage/scsiglue.c
@@ -0,0 +1,824 @@
+/* Driver for USB Mass Storage compliant devices
+ * SCSI layer glue code
+ *
+ * $Id: scsiglue.c,v 1.2 2000/06/27 10:20:39 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Developed with the assistance of:
+ * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
+ *
+ * Initial work by:
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "scsiglue.h"
+#include "usb.h"
+#include "debug.h"
+
+#include <linux/malloc.h>
+
+/* direction table -- this indicates the direction of the data
+ * transfer for each command code -- a 1 indicates input
+ */
+/* FIXME: we need to use the new direction indicators in the Scsi_Cmnd
+ * structure, not this table. First we need to evaluate if it's being set
+ * correctly for us, though
+ */
+unsigned char us_direction[256/8] = {
+ 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+ 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+/*
+ * kernel thread actions
+ */
+
+#define US_ACT_COMMAND 1
+#define US_ACT_DEVICE_RESET 2
+#define US_ACT_BUS_RESET 3
+#define US_ACT_HOST_RESET 4
+#define US_ACT_EXIT 5
+
+/***********************************************************************
+ * Host functions
+ ***********************************************************************/
+
+static const char* host_info(struct Scsi_Host *host)
+{
+ return "SCSI emulation for USB Mass Storage devices";
+}
+
+/* detect a virtual adapter (always works) */
+static int detect(struct SHT *sht)
+{
+ struct us_data *us;
+ char local_name[32];
+
+ /* This is not nice at all, but how else are we to get the
+ * data here? */
+ us = (struct us_data *)sht->proc_dir;
+
+ /* set up the name of our subdirectory under /proc/scsi/ */
+ sprintf(local_name, "usb-storage-%d", us->host_number);
+ sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
+ if (!sht->proc_name)
+ return 0;
+ strcpy(sht->proc_name, local_name);
+
+ /* we start with no /proc directory entry */
+ sht->proc_dir = NULL;
+
+ /* register the host */
+ us->host = scsi_register(sht, sizeof(us));
+ if (us->host) {
+ us->host->hostdata[0] = (unsigned long)us;
+ us->host_no = us->host->host_no;
+ return 1;
+ }
+
+ /* odd... didn't register properly. Abort and free pointers */
+ kfree(sht->proc_name);
+ sht->proc_name = NULL;
+ return 0;
+}
+
+/* Release all resources used by the virtual host
+ *
+ * NOTE: There is no contention here, because we're allready deregistered
+ * the driver and we're doing each virtual host in turn, not in parallel
+ */
+static int release(struct Scsi_Host *psh)
+{
+ struct us_data *us = (struct us_data *)psh->hostdata[0];
+
+ US_DEBUGP("us_release() called for host %s\n", us->htmplt.name);
+
+ /* Kill the control threads
+ *
+ * Enqueue the command, wake up the thread, and wait for
+ * notification that it's exited.
+ */
+ US_DEBUGP("-- sending US_ACT_EXIT command to thread\n");
+ us->action = US_ACT_EXIT;
+ up(&(us->sleeper));
+ down(&(us->notify));
+
+ /* free the data structure we were using */
+ US_DEBUGP("-- freeing URB\n");
+ kfree(us->current_urb);
+ (struct us_data*)psh->hostdata[0] = NULL;
+
+ /* we always have a successful release */
+ return 0;
+}
+
+/* run command */
+static int command( Scsi_Cmnd *srb )
+{
+ US_DEBUGP("Bad use of us_command\n");
+
+ return DID_BAD_TARGET << 16;
+}
+
+/* run command */
+static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
+{
+ struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+ US_DEBUGP("queuecommand() called\n");
+ srb->host_scribble = (unsigned char *)us;
+
+ /* get exclusive access to the structures we want */
+ down(&(us->queue_exclusion));
+
+ /* enqueue the command */
+ us->queue_srb = srb;
+ srb->scsi_done = done;
+ us->action = US_ACT_COMMAND;
+
+ /* wake up the process task */
+ up(&(us->queue_exclusion));
+ up(&(us->sleeper));
+
+ return 0;
+}
+
+/***********************************************************************
+ * Error handling functions
+ ***********************************************************************/
+
+/* Command abort
+ *
+ * Note that this is really only meaningful right now for CBI transport
+ * devices which have failed to give us the command completion interrupt
+ */
+static int command_abort( Scsi_Cmnd *srb )
+{
+ struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+ api_wrapper_data *awd = (api_wrapper_data *)us->current_urb->context;
+
+ US_DEBUGP("command_abort() called\n");
+
+ /* if we're stuck waiting for an IRQ, simulate it */
+ if (us->ip_wanted) {
+ US_DEBUGP("-- simulating missing IRQ\n");
+ up(&(us->ip_waitq));
+ return SUCCESS;
+ }
+
+ /* if we have an urb pending, let's wake the control thread up */
+ if (us->current_urb->status == -EINPROGRESS) {
+ /* cancel the URB */
+ usb_unlink_urb(us->current_urb);
+
+ /* wake the control thread up */
+ if (waitqueue_active(awd->wakeup))
+ wake_up(awd->wakeup);
+
+ /* wait for us to be done */
+ down(&(us->notify));
+ return SUCCESS;
+ }
+
+ US_DEBUGP ("-- nothing to abort\n");
+ return FAILED;
+}
+
+/* FIXME: this doesn't do anything right now */
+static int bus_reset( Scsi_Cmnd *srb )
+{
+ // struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+ printk(KERN_CRIT "usb-storage: bus_reset() requested but not implemented\n" );
+ US_DEBUGP("Bus reset requested\n");
+ // us->transport_reset(us);
+ return FAILED;
+}
+
+/* FIXME: This doesn't actually reset anything */
+static int host_reset( Scsi_Cmnd *srb )
+{
+ printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" );
+ return FAILED;
+}
+
+/***********************************************************************
+ * /proc/scsi/ functions
+ ***********************************************************************/
+
+/* we use this macro to help us write into the buffer */
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
+
+static int proc_info (char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ struct us_data *us;
+ char *pos = buffer;
+
+ /* if someone is sending us data, just throw it away */
+ if (inout)
+ return length;
+
+ /* lock the data structures */
+ down(&us_list_semaphore);
+
+ /* find our data from hostno */
+ us = us_list;
+ while (us) {
+ if (us->host_no == hostno)
+ break;
+ us = us->next;
+ }
+
+ /* if we couldn't find it, we return an error */
+ if (!us) {
+ up(&us_list_semaphore);
+ return -ESRCH;
+ }
+
+ /* print the controler name */
+ SPRINTF(" Host scsi%d: usb-storage\n", hostno);
+
+ /* print product, vendor, and serial number strings */
+ SPRINTF(" Vendor: %s\n", us->vendor);
+ SPRINTF(" Product: %s\n", us->product);
+ SPRINTF("Serial Number: %s\n", us->serial);
+
+ /* show the protocol and transport */
+ SPRINTF(" Protocol: %s\n", us->protocol_name);
+ SPRINTF(" Transport: %s\n", us->transport_name);
+
+ /* show the GUID of the device */
+ SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
+
+ /* release our lock on the data structures */
+ up(&us_list_semaphore);
+
+ /*
+ * Calculate start of next buffer, and return value.
+ */
+ *start = buffer + offset;
+
+ if ((pos - buffer) < offset)
+ return (0);
+ else if ((pos - buffer - offset) < length)
+ return (pos - buffer - offset);
+ else
+ return (length);
+}
+
+/*
+ * this defines our 'host'
+ */
+
+Scsi_Host_Template usb_stor_host_template = {
+ name: "usb-storage",
+ proc_info: proc_info,
+ info: host_info,
+
+ detect: detect,
+ release: release,
+ command: command,
+ queuecommand: queuecommand,
+
+ eh_abort_handler: command_abort,
+ eh_device_reset_handler:bus_reset,
+ eh_bus_reset_handler: bus_reset,
+ eh_host_reset_handler: host_reset,
+
+ can_queue: 1,
+ this_id: -1,
+
+ sg_tablesize: SG_ALL,
+ cmd_per_lun: 1,
+ present: 0,
+ unchecked_isa_dma: FALSE,
+ use_clustering: TRUE,
+ use_new_eh_code: TRUE,
+ emulated: TRUE
+};
+
+unsigned char usb_stor_sense_notready[12] = {
+ [0] = 0x70, /* current error */
+ [2] = 0x02, /* not ready */
+ [5] = 0x0a, /* additional length */
+ [10] = 0x04, /* not ready */
+ [11] = 0x03 /* manual intervention */
+};
+
+#define USB_STOR_SCSI_SENSE_HDRSZ 4
+#define USB_STOR_SCSI_SENSE_10_HDRSZ 8
+
+struct usb_stor_scsi_sense_hdr
+{
+ __u8* dataLength;
+ __u8* mediumType;
+ __u8* devSpecParms;
+ __u8* blkDescLength;
+};
+
+typedef struct usb_stor_scsi_sense_hdr Usb_Stor_Scsi_Sense_Hdr;
+
+union usb_stor_scsi_sense_hdr_u
+{
+ Usb_Stor_Scsi_Sense_Hdr hdr;
+ __u8* array[USB_STOR_SCSI_SENSE_HDRSZ];
+};
+
+typedef union usb_stor_scsi_sense_hdr_u Usb_Stor_Scsi_Sense_Hdr_u;
+
+struct usb_stor_scsi_sense_hdr_10
+{
+ __u8* dataLengthMSB;
+ __u8* dataLengthLSB;
+ __u8* mediumType;
+ __u8* devSpecParms;
+ __u8* reserved1;
+ __u8* reserved2;
+ __u8* blkDescLengthMSB;
+ __u8* blkDescLengthLSB;
+};
+
+typedef struct usb_stor_scsi_sense_hdr_10 Usb_Stor_Scsi_Sense_Hdr_10;
+
+union usb_stor_scsi_sense_hdr_10_u
+{
+ Usb_Stor_Scsi_Sense_Hdr_10 hdr;
+ __u8* array[USB_STOR_SCSI_SENSE_10_HDRSZ];
+};
+
+typedef union usb_stor_scsi_sense_hdr_10_u Usb_Stor_Scsi_Sense_Hdr_10_u;
+
+void usb_stor_scsiSenseParseBuffer( Scsi_Cmnd* , Usb_Stor_Scsi_Sense_Hdr_u*,
+ Usb_Stor_Scsi_Sense_Hdr_10_u*, int* );
+
+int usb_stor_scsiSense10to6( Scsi_Cmnd* the10 )
+{
+ __u8 *buffer=0;
+ int outputBufferSize = 0;
+ int length=0;
+ struct scatterlist *sg = 0;
+ int i=0, j=0, element=0;
+ Usb_Stor_Scsi_Sense_Hdr_u the6Locations;
+ Usb_Stor_Scsi_Sense_Hdr_10_u the10Locations;
+ int sb=0,si=0,db=0,di=0;
+ int sgLength=0;
+
+ US_DEBUGP("-- converting 10 byte sense data to 6 byte\n");
+ the10->cmnd[0] = the10->cmnd[0] & 0xBF;
+
+ /* Determine buffer locations */
+ usb_stor_scsiSenseParseBuffer( the10, &the6Locations, &the10Locations,
+ &length );
+
+ /* Work out minimum buffer to output */
+ outputBufferSize = *the10Locations.hdr.dataLengthLSB;
+ outputBufferSize += USB_STOR_SCSI_SENSE_HDRSZ;
+
+ /* Check to see if we need to trucate the output */
+ if ( outputBufferSize > length )
+ {
+ printk( KERN_WARNING USB_STORAGE
+ "Had to truncate MODE_SENSE_10 buffer into MODE_SENSE.\n" );
+ printk( KERN_WARNING USB_STORAGE
+ "outputBufferSize is %d and length is %d.\n",
+ outputBufferSize, length );
+ }
+ outputBufferSize = length;
+
+ /* Data length */
+ if ( *the10Locations.hdr.dataLengthMSB != 0 ) /* MSB must be zero */
+ {
+ printk( KERN_WARNING USB_STORAGE
+ "Command will be truncated to fit in SENSE6 buffer.\n" );
+ *the6Locations.hdr.dataLength = 0xff;
+ }
+ else
+ {
+ *the6Locations.hdr.dataLength = *the10Locations.hdr.dataLengthLSB;
+ }
+
+ /* Medium type and DevSpecific parms */
+ *the6Locations.hdr.mediumType = *the10Locations.hdr.mediumType;
+ *the6Locations.hdr.devSpecParms = *the10Locations.hdr.devSpecParms;
+
+ /* Block descriptor length */
+ if ( *the10Locations.hdr.blkDescLengthMSB != 0 ) /* MSB must be zero */
+ {
+ printk( KERN_WARNING USB_STORAGE
+ "Command will be truncated to fit in SENSE6 buffer.\n" );
+ *the6Locations.hdr.blkDescLength = 0xff;
+ }
+ else
+ {
+ *the6Locations.hdr.blkDescLength = *the10Locations.hdr.blkDescLengthLSB;
+ }
+
+ if ( the10->use_sg == 0 )
+ {
+ buffer = the10->request_buffer;
+ /* Copy the rest of the data */
+ memmove( &(buffer[USB_STOR_SCSI_SENSE_HDRSZ]),
+ &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]),
+ outputBufferSize - USB_STOR_SCSI_SENSE_HDRSZ );
+ /* initialise last bytes left in buffer due to smaller header */
+ memset( &(buffer[outputBufferSize
+ -(USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ)]),
+ 0,
+ USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ );
+ }
+ else
+ {
+ sg = (struct scatterlist *) the10->request_buffer;
+ /* scan through this scatterlist and figure out starting positions */
+ for ( i=0; i < the10->use_sg; i++)
+ {
+ sgLength = sg[i].length;
+ for ( j=0; j<sgLength; j++ )
+ {
+ /* get to end of header */
+ if ( element == USB_STOR_SCSI_SENSE_HDRSZ )
+ {
+ db=i;
+ di=j;
+ }
+ if ( element == USB_STOR_SCSI_SENSE_10_HDRSZ )
+ {
+ sb=i;
+ si=j;
+ /* we've found both sets now, exit loops */
+ j=sgLength;
+ i=the10->use_sg;
+ }
+ element++;
+ }
+ }
+
+ /* Now we know where to start the copy from */
+ element = USB_STOR_SCSI_SENSE_HDRSZ;
+ while ( element < outputBufferSize
+ -(USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ) )
+ {
+ /* check limits */
+ if ( sb >= the10->use_sg ||
+ si >= sg[sb].length ||
+ db >= the10->use_sg ||
+ di >= sg[db].length )
+ {
+ printk( KERN_ERR USB_STORAGE
+ "Buffer overrun averted, this shouldn't happen!\n" );
+ break;
+ }
+
+ /* copy one byte */
+ sg[db].address[di] = sg[sb].address[si];
+
+ /* get next destination */
+ if ( sg[db].length-1 == di )
+ {
+ db++;
+ di=0;
+ }
+ else
+ {
+ di++;
+ }
+
+ /* get next source */
+ if ( sg[sb].length-1 == si )
+ {
+ sb++;
+ si=0;
+ }
+ else
+ {
+ si++;
+ }
+
+ element++;
+ }
+ /* zero the remaining bytes */
+ while ( element < outputBufferSize )
+ {
+ /* check limits */
+ if ( db >= the10->use_sg ||
+ di >= sg[db].length )
+ {
+ printk( KERN_ERR USB_STORAGE
+ "Buffer overrun averted, this shouldn't happen!\n" );
+ break;
+ }
+
+ sg[db].address[di] = 0;
+
+ /* get next destination */
+ if ( sg[db].length-1 == di )
+ {
+ db++;
+ di=0;
+ }
+ else
+ {
+ di++;
+ }
+ element++;
+ }
+ }
+
+ /* All done any everything was fine */
+ return 0;
+}
+
+int usb_stor_scsiSense6to10( Scsi_Cmnd* the6 )
+{
+ /* will be used to store part of buffer */
+ __u8 tempBuffer[USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ],
+ *buffer=0;
+ int outputBufferSize = 0;
+ int length=0;
+ struct scatterlist *sg = 0;
+ int i=0, j=0, element=0;
+ Usb_Stor_Scsi_Sense_Hdr_u the6Locations;
+ Usb_Stor_Scsi_Sense_Hdr_10_u the10Locations;
+ int sb=0,si=0,db=0,di=0;
+ int lsb=0,lsi=0,ldb=0,ldi=0;
+
+ US_DEBUGP("-- converting 6 byte sense data to 10 byte\n");
+ the6->cmnd[0] = the6->cmnd[0] | 0x40;
+
+ /* Determine buffer locations */
+ usb_stor_scsiSenseParseBuffer( the6, &the6Locations, &the10Locations,
+ &length );
+
+ /* Work out minimum buffer to output */
+ outputBufferSize = *the6Locations.hdr.dataLength;
+ outputBufferSize += USB_STOR_SCSI_SENSE_10_HDRSZ;
+
+ /* Check to see if we need to trucate the output */
+ if ( outputBufferSize > length )
+ {
+ printk( KERN_WARNING USB_STORAGE
+ "Had to truncate MODE_SENSE into MODE_SENSE_10 buffer.\n" );
+ printk( KERN_WARNING USB_STORAGE
+ "outputBufferSize is %d and length is %d.\n",
+ outputBufferSize, length );
+ }
+ outputBufferSize = length;
+
+ /* Block descriptor length - save these before overwriting */
+ tempBuffer[2] = *the10Locations.hdr.blkDescLengthMSB;
+ tempBuffer[3] = *the10Locations.hdr.blkDescLengthLSB;
+ *the10Locations.hdr.blkDescLengthLSB = *the6Locations.hdr.blkDescLength;
+ *the10Locations.hdr.blkDescLengthMSB = 0;
+
+ /* reserved - save these before overwriting */
+ tempBuffer[0] = *the10Locations.hdr.reserved1;
+ tempBuffer[1] = *the10Locations.hdr.reserved2;
+ *the10Locations.hdr.reserved1 = *the10Locations.hdr.reserved2 = 0;
+
+ /* Medium type and DevSpecific parms */
+ *the10Locations.hdr.devSpecParms = *the6Locations.hdr.devSpecParms;
+ *the10Locations.hdr.mediumType = *the6Locations.hdr.mediumType;
+
+ /* Data length */
+ *the10Locations.hdr.dataLengthLSB = *the6Locations.hdr.dataLength;
+ *the10Locations.hdr.dataLengthMSB = 0;
+
+ if ( !the6->use_sg )
+ {
+ buffer = the6->request_buffer;
+ /* Copy the rest of the data */
+ memmove( &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]),
+ &(buffer[USB_STOR_SCSI_SENSE_HDRSZ]),
+ outputBufferSize-USB_STOR_SCSI_SENSE_10_HDRSZ );
+ /* Put the first four bytes (after header) in place */
+ memcpy( &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]),
+ tempBuffer,
+ USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ );
+ }
+ else
+ {
+ sg = (struct scatterlist *) the6->request_buffer;
+ /* scan through this scatterlist and figure out ending positions */
+ for ( i=0; i < the6->use_sg; i++)
+ {
+ for ( j=0; j<sg[i].length; j++ )
+ {
+ /* get to end of header */
+ if ( element == USB_STOR_SCSI_SENSE_HDRSZ )
+ {
+ ldb=i;
+ ldi=j;
+ }
+ if ( element == USB_STOR_SCSI_SENSE_10_HDRSZ )
+ {
+ lsb=i;
+ lsi=j;
+ /* we've found both sets now, exit loops */
+ j=sg[i].length;
+ i=the6->use_sg;
+ break;
+ }
+ element++;
+ }
+ }
+ /* scan through this scatterlist and figure out starting positions */
+ element = length-1;
+ /* destination is the last element */
+ db=the6->use_sg-1;
+ di=sg[db].length-1;
+ for ( i=the6->use_sg-1; i >= 0; i--)
+ {
+ for ( j=sg[i].length-1; j>=0; j-- )
+ {
+ /* get to end of header and find source for copy */
+ if ( element == length - 1
+ - (USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ) )
+ {
+ sb=i;
+ si=j;
+ /* we've found both sets now, exit loops */
+ j=-1;
+ i=-1;
+ }
+ element--;
+ }
+ }
+ /* Now we know where to start the copy from */
+ element = length-1
+ - (USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ);
+ while ( element >= USB_STOR_SCSI_SENSE_10_HDRSZ )
+ {
+ /* check limits */
+ if ( ( sb <= lsb && si < lsi ) ||
+ ( db <= ldb && di < ldi ) )
+ {
+ printk( KERN_ERR USB_STORAGE
+ "Buffer overrun averted, this shouldn't happen!\n" );
+ break;
+ }
+
+ /* copy one byte */
+ sg[db].address[di] = sg[sb].address[si];
+
+ /* get next destination */
+ if ( di == 0 )
+ {
+ db--;
+ di=sg[db].length-1;
+ }
+ else
+ {
+ di--;
+ }
+
+ /* get next source */
+ if ( si == 0 )
+ {
+ sb--;
+ si=sg[sb].length-1;
+ }
+ else
+ {
+ si--;
+ }
+
+ element--;
+ }
+ /* copy the remaining four bytes */
+ while ( element >= USB_STOR_SCSI_SENSE_HDRSZ )
+ {
+ /* check limits */
+ if ( db <= ldb && di < ldi )
+ {
+ printk( KERN_ERR USB_STORAGE
+ "Buffer overrun averted, this shouldn't happen!\n" );
+ break;
+ }
+
+ sg[db].address[di] = tempBuffer[element-USB_STOR_SCSI_SENSE_HDRSZ];
+
+ /* get next destination */
+ if ( di == 0 )
+ {
+ db--;
+ di=sg[db].length-1;
+ }
+ else
+ {
+ di--;
+ }
+ element--;
+ }
+ }
+
+ /* All done and everything was fine */
+ return 0;
+}
+
+void usb_stor_scsiSenseParseBuffer( Scsi_Cmnd* srb, Usb_Stor_Scsi_Sense_Hdr_u* the6,
+ Usb_Stor_Scsi_Sense_Hdr_10_u* the10,
+ int* length_p )
+
+{
+ int i = 0, j=0, element=0;
+ struct scatterlist *sg = 0;
+ int length = 0;
+ __u8* buffer=0;
+
+ /* are we scatter-gathering? */
+ if ( srb->use_sg != 0 )
+ {
+ /* loop over all the scatter gather structures and
+ * get pointer to the data members in the headers
+ * (also work out the length while we're here)
+ */
+ sg = (struct scatterlist *) srb->request_buffer;
+ for (i = 0; i < srb->use_sg; i++)
+ {
+ length += sg[i].length;
+ /* We only do the inner loop for the headers */
+ if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ )
+ {
+ /* scan through this scatterlist */
+ for ( j=0; j<sg[i].length; j++ )
+ {
+ if ( element < USB_STOR_SCSI_SENSE_HDRSZ )
+ {
+ /* fill in the pointers for both header types */
+ the6->array[element] = &(sg[i].address[j]);
+ the10->array[element] = &(sg[i].address[j]);
+ }
+ else if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ )
+ {
+ /* only the longer headers still cares now */
+ the10->array[element] = &(sg[i].address[j]);
+ }
+ /* increase element counter */
+ element++;
+ }
+ }
+ }
+ }
+ else
+ {
+ length = srb->request_bufflen;
+ buffer = srb->request_buffer;
+ if ( length < USB_STOR_SCSI_SENSE_10_HDRSZ )
+ printk( KERN_ERR USB_STORAGE
+ "Buffer length smaller than header!!" );
+ for( i=0; i<USB_STOR_SCSI_SENSE_10_HDRSZ; i++ )
+ {
+ if ( i < USB_STOR_SCSI_SENSE_HDRSZ )
+ {
+ the6->array[i] = &(buffer[i]);
+ the10->array[i] = &(buffer[i]);
+ }
+ else
+ {
+ the10->array[i] = &(buffer[i]);
+ }
+ }
+ }
+
+ /* Set value of length passed in */
+ *length_p = length;
+}
+
diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h
new file mode 100644
index 000000000..ea8a34504
--- /dev/null
+++ b/drivers/usb/storage/scsiglue.h
@@ -0,0 +1,54 @@
+/* Driver for USB Mass Storage compliant devices
+ * SCSI Connecting Glue Header File
+ *
+ * $Id: scsiglue.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SCSIGLUE_H_
+#define _SCSIGLUE_H_
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+extern unsigned char usb_stor_sense_notready[12];
+extern unsigned char us_direction[256/8];
+extern Scsi_Host_Template usb_stor_host_template;
+extern int usb_stor_scsiSense10to6(Scsi_Cmnd*);
+extern int usb_stor_scsiSense6to10(Scsi_Cmnd*);
+
+#endif
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
new file mode 100644
index 000000000..c83a7512d
--- /dev/null
+++ b/drivers/usb/storage/transport.c
@@ -0,0 +1,904 @@
+/* Driver for USB Mass Storage compliant devices
+ *
+ * $Id: transport.c,v 1.2 2000/06/27 10:20:39 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Developed with the assistance of:
+ * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
+ *
+ * Initial work by:
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "transport.h"
+#include "protocol.h"
+#include "usb.h"
+#include "debug.h"
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+
+/***********************************************************************
+ * Data transfer routines
+ ***********************************************************************/
+
+/* This is the completion handler which will wake us up when an URB
+ * completes.
+ */
+static void usb_stor_blocking_completion(urb_t *urb)
+{
+ api_wrapper_data *awd = (api_wrapper_data *)urb->context;
+
+ if (waitqueue_active(awd->wakeup))
+ wake_up(awd->wakeup);
+}
+
+/* This is our function to emulate usb_control_msg() but give us enough
+ * access to make aborts/resets work
+ */
+int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
+ u8 request, u8 requesttype, u16 value, u16 index,
+ void *data, u16 size)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ DECLARE_WAIT_QUEUE_HEAD(wqh);
+ api_wrapper_data awd;
+ int status;
+ devrequest *dr;
+
+ /* allocate the device request structure */
+ dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ /* fill in the structure */
+ dr->requesttype = requesttype;
+ dr->request = request;
+ dr->value = cpu_to_le16(value);
+ dr->index = cpu_to_le16(index);
+ dr->length = cpu_to_le16(size);
+
+ /* set up data structures for the wakeup system */
+ awd.wakeup = &wqh;
+ awd.handler = 0;
+ init_waitqueue_head(&wqh);
+ add_wait_queue(&wqh, &wait);
+
+ /* lock the URB */
+ down(&(us->current_urb_sem));
+
+ /* fill the URB */
+ FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe,
+ (unsigned char*) dr, data, size,
+ usb_stor_blocking_completion, &awd);
+
+ /* submit the URB */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ status = usb_submit_urb(us->current_urb);
+ if (status) {
+ /* something went wrong */
+ up(&(us->current_urb_sem));
+ remove_wait_queue(&wqh, &wait);
+ kfree(dr);
+ return status;
+ }
+
+ /* wait for the completion of the URB */
+ up(&(us->current_urb_sem));
+ if (us->current_urb->status == -EINPROGRESS)
+ schedule();
+ down(&(us->current_urb_sem));
+
+ /* we either timed out or got woken up -- clean up either way */
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wqh, &wait);
+
+ /* return the actual length of the data transferred if no error*/
+ status = us->current_urb->status;
+ if (status >= 0)
+ status = us->current_urb->actual_length;
+
+ /* release the lock and return status */
+ up(&(us->current_urb_sem));
+ kfree(dr);
+ return status;
+}
+
+/* This is our function to emulate usb_bulk_msg() but give us enough
+ * access to make aborts/resets work
+ */
+int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
+ unsigned int len, unsigned int *act_len)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ DECLARE_WAIT_QUEUE_HEAD(wqh);
+ api_wrapper_data awd;
+ int status;
+
+ /* set up data structures for the wakeup system */
+ awd.wakeup = &wqh;
+ awd.handler = 0;
+ init_waitqueue_head(&wqh);
+ add_wait_queue(&wqh, &wait);
+
+ /* lock the URB */
+ down(&(us->current_urb_sem));
+
+ /* fill the URB */
+ FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
+ usb_stor_blocking_completion, &awd);
+
+ /* submit the URB */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ status = usb_submit_urb(us->current_urb);
+ if (status) {
+ /* something went wrong */
+ up(&(us->current_urb_sem));
+ remove_wait_queue(&wqh, &wait);
+ return status;
+ }
+
+ /* wait for the completion of the URB */
+ up(&(us->current_urb_sem));
+ if (us->current_urb->status == -EINPROGRESS)
+ schedule();
+ down(&(us->current_urb_sem));
+
+ /* we either timed out or got woken up -- clean up either way */
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wqh, &wait);
+
+ /* return the actual length of the data transferred */
+ *act_len = us->current_urb->actual_length;
+
+ /* release the lock and return status */
+ up(&(us->current_urb_sem));
+ return us->current_urb->status;
+}
+
+/*
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory. Good performance is achieved by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
+ *
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit. Thus we don't have to worry about it for individual
+ * packets.
+ */
+static int us_transfer_partial(struct us_data *us, char *buf, int length)
+{
+ int result;
+ int partial;
+ int pipe;
+
+ /* calculate the appropriate pipe information */
+ if (US_DIRECTION(us->srb->cmnd[0]))
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+ else
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* transfer the data */
+ US_DEBUGP("us_transfer_partial(): xfer %d bytes\n", length);
+ result = usb_stor_bulk_msg(us, buf, pipe, length, &partial);
+ US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n",
+ result, partial, length);
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ }
+
+ /* did we send all the data? */
+ if (partial == length) {
+ US_DEBUGP("us_transfer_partial(): transfer complete\n");
+ return US_BULK_TRANSFER_GOOD;
+ }
+
+ /* uh oh... we have an error code, so something went wrong. */
+ if (result) {
+ /* NAK - that means we've retried a few times allready */
+ if (result == -ETIMEDOUT) {
+ US_DEBUGP("us_transfer_partial(): device NAKed\n");
+ return US_BULK_TRANSFER_FAILED;
+ }
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ /* the catch-all case */
+ US_DEBUGP("us_transfer_partial(): unknown error\n");
+ return US_BULK_TRANSFER_FAILED;
+ }
+
+ /* no error code, so we must have transferred some data,
+ * just not all of it */
+ return US_BULK_TRANSFER_SHORT;
+}
+
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_transfer_partial to achieve it's goals -- this
+ * function simply determines if we're going to use scatter-gather or not,
+ * and acts appropriately. For now, it also re-interprets the error codes.
+ */
+static void us_transfer(Scsi_Cmnd *srb, struct us_data* us, int dir_in)
+{
+ int i;
+ int result = -1;
+ struct scatterlist *sg;
+
+ /* are we scatter-gathering? */
+ if (srb->use_sg) {
+
+ /* loop over all the scatter gather structures and
+ * make the appropriate requests for each, until done
+ */
+ sg = (struct scatterlist *) srb->request_buffer;
+ for (i = 0; i < srb->use_sg; i++) {
+ result = us_transfer_partial(us, sg[i].address,
+ sg[i].length);
+ if (result)
+ break;
+ }
+ }
+ else
+ /* no scatter-gather, just make the request */
+ result = us_transfer_partial(us, srb->request_buffer,
+ srb->request_bufflen);
+
+ /* return the result in the data structure itself */
+ srb->result = result;
+}
+
+/* Calculate the length of the data transfer (not the command) for any
+ * given SCSI command
+ */
+static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int i;
+ unsigned int total = 0;
+ struct scatterlist *sg;
+
+ /* support those devices which need the length calculated
+ * differently
+ */
+ if (us->flags & US_FL_ALT_LENGTH) {
+ if (srb->cmnd[0] == INQUIRY) {
+ srb->cmnd[4] = 36;
+ }
+
+ if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE))
+ return srb->cmnd[4];
+
+ if (srb->cmnd[0] == TEST_UNIT_READY)
+ return 0;
+ }
+
+ /* Are we going to scatter gather? */
+ if (srb->use_sg) {
+ /* Add up the sizes of all the scatter-gather segments */
+ sg = (struct scatterlist *) srb->request_buffer;
+ for (i = 0; i < srb->use_sg; i++)
+ total += sg[i].length;
+
+ return total;
+ }
+ else
+ /* Just return the length of the buffer */
+ return srb->request_bufflen;
+}
+
+/***********************************************************************
+ * Transport routines
+ ***********************************************************************/
+
+/* Invoke the transport and basic error-handling/recovery methods
+ *
+ * This is used by the protocol layers to actually send the message to
+ * the device and recieve the response.
+ */
+void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int need_auto_sense;
+ int result;
+
+ /* send the command to the transport layer */
+ result = us->transport(srb, us);
+
+ /* if the command gets aborted by the higher layers, we need to
+ * short-circuit all other processing
+ */
+ if (result == USB_STOR_TRANSPORT_ABORTED) {
+ US_DEBUGP("-- transport indicates command was aborted\n");
+ srb->result = DID_ABORT << 16;
+ return;
+ }
+
+ /* Determine if we need to auto-sense
+ *
+ * I normally don't use a flag like this, but it's almost impossible
+ * to understand what's going on here if I don't.
+ */
+ need_auto_sense = 0;
+
+ /*
+ * If we're running the CB transport, which is incapable
+ * of determining status on it's own, we need to auto-sense almost
+ * every time.
+ */
+ if (us->protocol == US_PR_CB) {
+ US_DEBUGP("-- CB transport device requiring auto-sense\n");
+ need_auto_sense = 1;
+
+ /* There are some exceptions to this. Notably, if this is
+ * a UFI device and the command is REQUEST_SENSE or INQUIRY,
+ * then it is impossible to truly determine status.
+ */
+ if (us->subclass == US_SC_UFI &&
+ ((srb->cmnd[0] == REQUEST_SENSE) ||
+ (srb->cmnd[0] == INQUIRY))) {
+ US_DEBUGP("** no auto-sense for a special command\n");
+ need_auto_sense = 0;
+ }
+ }
+
+ /*
+ * If we have an error, we're going to do a REQUEST_SENSE
+ * automatically. Note that we differentiate between a command
+ * "failure" and an "error" in the transport mechanism.
+ */
+ if (result == USB_STOR_TRANSPORT_FAILED) {
+ US_DEBUGP("-- transport indicates command failure\n");
+ need_auto_sense = 1;
+ }
+ if (result == USB_STOR_TRANSPORT_ERROR) {
+ /* FIXME: we need to invoke a transport reset here */
+ US_DEBUGP("-- transport indicates transport failure\n");
+ need_auto_sense = 0;
+ srb->result = DID_ERROR << 16;
+ return;
+ }
+
+ /*
+ * Also, if we have a short transfer on a command that can't have
+ * a short transfer, we're going to do this.
+ */
+ if ((srb->result == US_BULK_TRANSFER_SHORT) &&
+ !((srb->cmnd[0] == REQUEST_SENSE) ||
+ (srb->cmnd[0] == INQUIRY) ||
+ (srb->cmnd[0] == MODE_SENSE) ||
+ (srb->cmnd[0] == LOG_SENSE) ||
+ (srb->cmnd[0] == MODE_SENSE_10))) {
+ US_DEBUGP("-- unexpectedly short transfer\n");
+ need_auto_sense = 1;
+ }
+
+ /* Now, if we need to do the auto-sense, let's do it */
+ if (need_auto_sense) {
+ int temp_result;
+ void* old_request_buffer;
+ int old_sg;
+ int old_request_bufflen;
+ unsigned char old_cmnd[MAX_COMMAND_SIZE];
+
+ US_DEBUGP("Issuing auto-REQUEST_SENSE\n");
+
+ /* save the old command */
+ memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE);
+
+ /* set the command and the LUN */
+ srb->cmnd[0] = REQUEST_SENSE;
+ srb->cmnd[1] = old_cmnd[1] & 0xE0;
+ srb->cmnd[2] = 0;
+ srb->cmnd[3] = 0;
+ srb->cmnd[4] = 18;
+ srb->cmnd[5] = 0;
+
+ /* set the buffer length for transfer */
+ old_request_buffer = srb->request_buffer;
+ old_request_bufflen = srb->request_bufflen;
+ old_sg = srb->use_sg;
+ srb->use_sg = 0;
+ srb->request_bufflen = 18;
+ srb->request_buffer = srb->sense_buffer;
+
+ /* issue the auto-sense command */
+ temp_result = us->transport(us->srb, us);
+ if (temp_result != USB_STOR_TRANSPORT_GOOD) {
+ /* FIXME: we need to invoke a transport reset here */
+ US_DEBUGP("-- auto-sense failure\n");
+ srb->result = DID_ERROR << 16;
+ return;
+ }
+
+ US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+ US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+ srb->sense_buffer[0],
+ srb->sense_buffer[2] & 0xf,
+ srb->sense_buffer[12],
+ srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ srb->result = CHECK_CONDITION;
+
+ /* we're done here, let's clean up */
+ srb->request_buffer = old_request_buffer;
+ srb->request_bufflen = old_request_bufflen;
+ srb->use_sg = old_sg;
+ memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
+
+ /* If things are really okay, then let's show that */
+ if ((srb->sense_buffer[2] & 0xf) == 0x0)
+ srb->result = GOOD;
+ } else /* if (need_auto_sense) */
+ srb->result = GOOD;
+
+ /* Regardless of auto-sense, if we _know_ we have an error
+ * condition, show that in the result code
+ */
+ if (result == USB_STOR_TRANSPORT_FAILED)
+ srb->result = CHECK_CONDITION;
+
+ /* If we think we're good, then make sure the sense data shows it.
+ * This is necessary because the auto-sense for some devices always
+ * sets byte 0 == 0x70, even if there is no error
+ */
+ if ((us->protocol == US_PR_CB) &&
+ (result == USB_STOR_TRANSPORT_GOOD) &&
+ ((srb->sense_buffer[2] & 0xf) == 0x0))
+ srb->sense_buffer[0] = 0x0;
+}
+
+/*
+ * Control/Bulk/Interrupt transport
+ */
+
+/* The interrupt handler for CBI devices */
+void usb_stor_CBI_irq(struct urb *urb)
+{
+ struct us_data *us = (struct us_data *)urb->context;
+
+ US_DEBUGP("USB IRQ recieved for device on host %d\n", us->host_no);
+ US_DEBUGP("-- IRQ data length is %d\n", urb->actual_length);
+ US_DEBUGP("-- IRQ state is %d\n", urb->status);
+
+ /* is the device removed? */
+ if (urb->status != -ENOENT) {
+ /* save the data for interpretation later */
+ US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n",
+ ((unsigned char*)urb->transfer_buffer)[0],
+ ((unsigned char*)urb->transfer_buffer)[1]);
+
+
+ /* was this a wanted interrupt? */
+ if (us->ip_wanted) {
+ us->ip_wanted = 0;
+ up(&(us->ip_waitq));
+ } else
+ US_DEBUGP("ERROR: Unwanted interrupt received!\n");
+ } else
+ US_DEBUGP("-- device has been removed\n");
+}
+
+int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int result;
+
+ /* COMMAND STAGE */
+ /* let's send the command via the control pipe */
+ result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
+ US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len);
+
+ /* check the return code for the command */
+ US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result);
+ if (result < 0) {
+ /* if the command was aborted, indicate that */
+ if (result == -ENOENT)
+ return USB_STOR_TRANSPORT_ABORTED;
+
+ /* STALL must be cleared when they are detected */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
+ result = usb_clear_halt(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,
+ 0));
+ US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ /* Uh oh... serious problem here */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* Set up for status notification */
+ us->ip_wanted = 1;
+
+ /* DATA STAGE */
+ /* transfer the data payload for this command, if one exists*/
+ if (us_transfer_length(srb, us)) {
+ us_transfer(srb, us, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CBI data stage result is 0x%x\n", srb->result);
+
+ /* if it was aborted, we need to indicate that */
+ if (srb->result == USB_STOR_TRANSPORT_ABORTED)
+ return USB_STOR_TRANSPORT_ABORTED;
+ }
+
+ /* STATUS STAGE */
+
+ /* go to sleep until we get this interrupt */
+ down(&(us->ip_waitq));
+
+ /* if we were woken up by an abort instead of the actual interrupt */
+ if (us->ip_wanted) {
+ US_DEBUGP("Did not get interrupt on CBI\n");
+ us->ip_wanted = 0;
+ return USB_STOR_TRANSPORT_ABORTED;
+ }
+
+ US_DEBUGP("Got interrupt data (0x%x, 0x%x)\n",
+ ((unsigned char*)us->irq_urb->transfer_buffer)[0],
+ ((unsigned char*)us->irq_urb->transfer_buffer)[1]);
+
+ /* UFI gives us ASC and ASCQ, like a request sense
+ *
+ * REQUEST_SENSE and INQUIRY don't affect the sense data on UFI
+ * devices, so we ignore the information for those commands. Note
+ * that this means we could be ignoring a real error on these
+ * commands, but that can't be helped.
+ */
+ if (us->subclass == US_SC_UFI) {
+ if (srb->cmnd[0] == REQUEST_SENSE ||
+ srb->cmnd[0] == INQUIRY)
+ return USB_STOR_TRANSPORT_GOOD;
+ else
+ if (((unsigned char*)us->irq_urb->transfer_buffer)[0])
+ return USB_STOR_TRANSPORT_FAILED;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ /* If not UFI, we interpret the data as a result code
+ * The first byte should always be a 0x0
+ * The second byte & 0x0F should be 0x0 for good, otherwise error
+ */
+ if (((unsigned char*)us->irq_urb->transfer_buffer)[0]) {
+ US_DEBUGP("CBI IRQ data showed reserved bType\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ switch (((unsigned char*)us->irq_urb->transfer_buffer)[1] & 0x0F) {
+ case 0x00:
+ return USB_STOR_TRANSPORT_GOOD;
+ case 0x01:
+ return USB_STOR_TRANSPORT_FAILED;
+ default:
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* we should never get here, but if we do, we're in trouble */
+ return USB_STOR_TRANSPORT_ERROR;
+}
+
+/*
+ * Control/Bulk transport
+ */
+int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int result;
+
+ /* COMMAND STAGE */
+ /* let's send the command via the control pipe */
+ result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
+ US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len);
+
+ /* check the return code for the command */
+ US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result);
+ if (result < 0) {
+ /* if the command was aborted, indicate that */
+ if (result == -ENOENT)
+ return USB_STOR_TRANSPORT_ABORTED;
+
+ /* a stall is a fatal condition from the device */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
+ result = usb_clear_halt(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,
+ 0));
+ US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ /* Uh oh... serious problem here */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* DATA STAGE */
+ /* transfer the data payload for this command, if one exists*/
+ if (us_transfer_length(srb, us)) {
+ us_transfer(srb, us, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CB data stage result is 0x%x\n", srb->result);
+
+ /* if it was aborted, we need to indicate that */
+ if (srb->result == USB_STOR_TRANSPORT_ABORTED)
+ return USB_STOR_TRANSPORT_ABORTED;
+ }
+
+ /* STATUS STAGE */
+ /* NOTE: CB does not have a status stage. Silly, I know. So
+ * we have to catch this at a higher level.
+ */
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Bulk only transport
+ */
+
+/* Determine what the maximum LUN supported is */
+int usb_stor_Bulk_max_lun(struct us_data *us)
+{
+ unsigned char data;
+ int result;
+ int pipe;
+
+ /* issue the command */
+ pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
+ result = usb_control_msg(us->pusb_dev, pipe,
+ US_BULK_GET_MAX_LUN,
+ USB_DIR_IN | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE,
+ 0, us->ifnum, &data, sizeof(data), HZ);
+
+ US_DEBUGP("GetMaxLUN command result is %d, data is %d\n",
+ result, data);
+
+ /* if we have a successful request, return the result */
+ if (result == 1)
+ return data;
+
+ /* if we get a STALL, clear the stall */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ }
+
+ /* return the default -- no LUNs */
+ return 0;
+}
+
+int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+ struct bulk_cb_wrap bcb;
+ struct bulk_cs_wrap bcs;
+ int result;
+ int pipe;
+ int partial;
+
+ /* set up the command wrapper */
+ bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN);
+ bcb.DataTransferLength = cpu_to_le32(us_transfer_length(srb, us));
+ bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7;
+ bcb.Tag = srb->serial_number;
+ bcb.Lun = srb->cmnd[1] >> 5;
+ bcb.Length = srb->cmd_len;
+
+ /* construct the pipe handle */
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* copy the command payload */
+ memset(bcb.CDB, 0, sizeof(bcb.CDB));
+ memcpy(bcb.CDB, srb->cmnd, bcb.Length);
+
+ /* send it to out endpoint */
+ US_DEBUGP("Bulk command S 0x%x T 0x%x LUN %d L %d F %d CL %d\n",
+ le32_to_cpu(bcb.Signature), bcb.Tag, bcb.Lun,
+ bcb.DataTransferLength, bcb.Flags, bcb.Length);
+ result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN,
+ &partial);
+ US_DEBUGP("Bulk command transfer result=%d\n", result);
+
+ /* if the command was aborted, indicate that */
+ if (result == -ENOENT)
+ return USB_STOR_TRANSPORT_ABORTED;
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ } else if (result) {
+ /* unknown error -- we've got a problem */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* if the command transfered well, then we go to the data stage */
+ if (result == 0) {
+ /* send/receive data payload, if there is any */
+ if (bcb.DataTransferLength) {
+ us_transfer(srb, us, bcb.Flags);
+ US_DEBUGP("Bulk data transfer result 0x%x\n",
+ srb->result);
+
+ /* if it was aborted, we need to indicate that */
+ if (srb->result == USB_STOR_TRANSPORT_ABORTED)
+ return USB_STOR_TRANSPORT_ABORTED;
+ }
+ }
+
+ /* See flow chart on pg 15 of the Bulk Only Transport spec for
+ * an explanation of how this code works.
+ */
+
+ /* construct the pipe handle */
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+
+ /* get CSW for device status */
+ US_DEBUGP("Attempting to get CSW...\n");
+ result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN,
+ &partial);
+
+ /* if the command was aborted, indicate that */
+ if (result == -ENOENT)
+ return USB_STOR_TRANSPORT_ABORTED;
+
+ /* did the attempt to read the CSW fail? */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+
+ /* get the status again */
+ US_DEBUGP("Attempting to get CSW (2nd try)...\n");
+ result = usb_stor_bulk_msg(us, &bcs, pipe,
+ US_BULK_CS_WRAP_LEN, &partial);
+
+ /* if the command was aborted, indicate that */
+ if (result == -ENOENT)
+ return USB_STOR_TRANSPORT_ABORTED;
+
+ /* if it fails again, we need a reset and return an error*/
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ }
+
+ /* if we still have a failure at this point, we're in trouble */
+ US_DEBUGP("Bulk status result = %d\n", result);
+ if (result) {
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* check bulk status */
+ US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
+ le32_to_cpu(bcs.Signature), bcs.Tag,
+ bcs.Residue, bcs.Status);
+ if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) ||
+ bcs.Tag != bcb.Tag ||
+ bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
+ US_DEBUGP("Bulk logical error\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* based on the status code, we report good or bad */
+ switch (bcs.Status) {
+ case US_BULK_STAT_OK:
+ /* command good -- note that we could be short on data */
+ return USB_STOR_TRANSPORT_GOOD;
+
+ case US_BULK_STAT_FAIL:
+ /* command failed */
+ return USB_STOR_TRANSPORT_FAILED;
+
+ case US_BULK_STAT_PHASE:
+ /* phase error */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* we should never get here, but if we do, we're in trouble */
+ return USB_STOR_TRANSPORT_ERROR;
+}
+
+/***********************************************************************
+ * Reset routines
+ ***********************************************************************/
+
+/* This issues a CB[I] Reset to the device in question
+ */
+int usb_stor_CB_reset(struct us_data *us)
+{
+ unsigned char cmd[12];
+ int result;
+
+ US_DEBUGP("CB_reset() called\n");
+
+ memset(cmd, 0xFF, sizeof(cmd));
+ cmd[0] = SEND_DIAGNOSTIC;
+ cmd[1] = 4;
+ result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+ US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
+
+ /* long wait for reset */
+ schedule_timeout(HZ*6);
+
+ US_DEBUGP("CB_reset: clearing endpoint halt\n");
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+
+ US_DEBUGP("CB_reset done\n");
+ return 0;
+}
+
+/* FIXME: Does this work? */
+int usb_stor_Bulk_reset(struct us_data *us)
+{
+ int result;
+
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ US_BULK_RESET_REQUEST,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, us->ifnum, NULL, 0, HZ*5);
+
+ if (result < 0)
+ US_DEBUGP("Bulk hard reset failed %d\n", result);
+
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev,
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+ /* long wait for reset */
+ schedule_timeout(HZ*6);
+
+ return result;
+}
+
diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
new file mode 100644
index 000000000..fb8699781
--- /dev/null
+++ b/drivers/usb/storage/transport.h
@@ -0,0 +1,132 @@
+/* Driver for USB Mass Storage compliant devices
+ * Transport Functions Header File
+ *
+ * $Id: transport.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _TRANSPORT_H_
+#define _TRANSPORT_H_
+
+#include <linux/blk.h>
+#include "usb.h"
+#include "scsi.h"
+
+/* bit set if input */
+extern unsigned char us_direction[256/8];
+#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
+
+/* Protocols */
+
+#define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */
+#define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */
+#define US_PR_BULK 0x50 /* bulk only */
+
+/*
+ * Bulk only data structures
+ */
+
+/* command block wrapper */
+struct bulk_cb_wrap {
+ __u32 Signature; /* contains 'USBC' */
+ __u32 Tag; /* unique per command id */
+ __u32 DataTransferLength; /* size of data */
+ __u8 Flags; /* direction in bit 0 */
+ __u8 Lun; /* LUN normally 0 */
+ __u8 Length; /* of of the CDB */
+ __u8 CDB[16]; /* max command */
+};
+
+#define US_BULK_CB_WRAP_LEN 31
+#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */
+#define US_BULK_FLAG_IN 1
+#define US_BULK_FLAG_OUT 0
+
+/* command status wrapper */
+struct bulk_cs_wrap {
+ __u32 Signature; /* should = 'USBS' */
+ __u32 Tag; /* same as original command */
+ __u32 Residue; /* amount not transferred */
+ __u8 Status; /* see below */
+ __u8 Filler[18];
+};
+
+#define US_BULK_CS_WRAP_LEN 13
+#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
+#define US_BULK_STAT_OK 0
+#define US_BULK_STAT_FAIL 1
+#define US_BULK_STAT_PHASE 2
+
+/* bulk-only class specific requests */
+#define US_BULK_RESET_REQUEST 0xff
+#define US_BULK_GET_MAX_LUN 0xfe
+
+/*
+ * us_bulk_transfer() return codes
+ */
+#define US_BULK_TRANSFER_GOOD 0 /* good transfer */
+#define US_BULK_TRANSFER_SHORT 1 /* transfered less than expected */
+#define US_BULK_TRANSFER_FAILED 2 /* transfer died in the middle */
+#define US_BULK_TRANSFER_ABORTED 3 /* transfer canceled */
+
+/*
+ * Transport return codes
+ */
+
+#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
+#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
+#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */
+#define USB_STOR_TRANSPORT_ABORTED 3 /* Transport aborted */
+
+/*
+ * CBI accept device specific command
+ */
+
+#define US_CBI_ADSC 0
+
+extern void usb_stor_CBI_irq(struct urb*);
+extern int usb_stor_CBI_transport(Scsi_Cmnd*, struct us_data*);
+
+extern int usb_stor_CB_transport(Scsi_Cmnd*, struct us_data*);
+extern int usb_stor_CB_reset(struct us_data*);
+
+extern int usb_stor_Bulk_transport(Scsi_Cmnd*, struct us_data*);
+extern int usb_stor_Bulk_max_lun(struct us_data*);
+extern int usb_stor_Bulk_reset(struct us_data*);
+
+void usb_stor_invoke_transport(Scsi_Cmnd *, struct us_data *);
+
+#endif
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
new file mode 100644
index 000000000..d493bc695
--- /dev/null
+++ b/drivers/usb/storage/usb.c
@@ -0,0 +1,803 @@
+/* Driver for USB Mass Storage compliant devices
+ *
+ * $Id: usb.c,v 1.3 2000/06/27 10:20:39 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Developed with the assistance of:
+ * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
+ *
+ * Initial work by:
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "usb.h"
+#include "scsiglue.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+
+#include <linux/module.h>
+ /*FIXME: note that this next include is needed for the new sleeping system
+ * which is not implemented yet
+ */
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+/*
+ * Per device data
+ */
+
+static int my_host_number;
+
+/*
+ * kernel thread actions
+ */
+
+#define US_ACT_COMMAND 1
+#define US_ACT_DEVICE_RESET 2
+#define US_ACT_BUS_RESET 3
+#define US_ACT_HOST_RESET 4
+#define US_ACT_EXIT 5
+
+/* The list of structures and the protective lock for them */
+struct us_data *us_list;
+struct semaphore us_list_semaphore;
+
+static void * storage_probe(struct usb_device *dev, unsigned int ifnum);
+static void storage_disconnect(struct usb_device *dev, void *ptr);
+static struct usb_driver storage_driver = {
+ name: "usb-storage",
+ probe: storage_probe,
+ disconnect: storage_disconnect,
+};
+
+static int usb_stor_control_thread(void * __us)
+{
+ struct us_data *us = (struct us_data *)__us;
+ int action;
+
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources..
+ */
+ daemonize();
+
+ /* set our name for identification purposes */
+ sprintf(current->comm, "usb-storage-%d", us->host_number);
+
+ unlock_kernel();
+
+ /* signal that we've started the thread */
+ up(&(us->notify));
+
+ for(;;) {
+ US_DEBUGP("*** thread sleeping.\n");
+ down(&(us->sleeper));
+ down(&(us->queue_exclusion));
+ US_DEBUGP("*** thread awakened.\n");
+
+ /* take the command off the queue */
+ action = us->action;
+ us->action = 0;
+ us->srb = us->queue_srb;
+
+ /* release the queue lock as fast as possible */
+ up(&(us->queue_exclusion));
+
+ switch (action) {
+ case US_ACT_COMMAND:
+ /* reject if target != 0 or if LUN is higher than
+ * the maximum known LUN
+ */
+ if (us->srb->target || (us->srb->lun > us->max_lun)) {
+ US_DEBUGP("Bad device number (%d/%d)\n",
+ us->srb->target, us->srb->lun);
+
+ us->srb->result = DID_BAD_TARGET << 16;
+
+ us->srb->scsi_done(us->srb);
+ us->srb = NULL;
+ break;
+ }
+
+ /* handle those devices which can't do a START_STOP */
+ if ((us->srb->cmnd[0] == START_STOP) &&
+ (us->flags & US_FL_START_STOP)) {
+ us->srb->result = GOOD;
+ us->srb->scsi_done(us->srb);
+ us->srb = NULL;
+ break;
+ }
+
+ /* lock the device pointers */
+ down(&(us->dev_semaphore));
+
+ /* our device has gone - pretend not ready */
+ if (!us->pusb_dev) {
+ US_DEBUGP("Request is for removed device\n");
+ /* For REQUEST_SENSE, it's the data. But
+ * for anything else, it should look like
+ * we auto-sensed for it.
+ */
+ if (us->srb->cmnd[0] == REQUEST_SENSE) {
+ memcpy(us->srb->request_buffer,
+ usb_stor_sense_notready,
+ sizeof(usb_stor_sense_notready));
+ us->srb->result = GOOD;
+ } else {
+ memcpy(us->srb->sense_buffer,
+ usb_stor_sense_notready,
+ sizeof(usb_stor_sense_notready));
+ us->srb->result = CHECK_CONDITION;
+ }
+ } else { /* !us->pusb_dev */
+ /* we've got a command, let's do it! */
+ US_DEBUG(usb_stor_show_command(us->srb));
+ us->proto_handler(us->srb, us);
+ }
+
+ /* unlock the device pointers */
+ up(&(us->dev_semaphore));
+
+ /* indicate that the command is done */
+ if (us->srb->result != DID_ABORT << 16) {
+ US_DEBUGP("scsi cmd done, result=0x%x\n",
+ us->srb->result);
+ us->srb->scsi_done(us->srb);
+ } else {
+ US_DEBUGP("scsi command aborted\n");
+ up(&(us->notify));
+ }
+ us->srb = NULL;
+ break;
+
+ case US_ACT_DEVICE_RESET:
+ break;
+
+ case US_ACT_BUS_RESET:
+ break;
+
+ case US_ACT_HOST_RESET:
+ break;
+
+ } /* end switch on action */
+
+ /* exit if we get a signal to exit */
+ if (action == US_ACT_EXIT) {
+ US_DEBUGP("-- US_ACT_EXIT command recieved\n");
+ break;
+ }
+ } /* for (;;) */
+
+ /* notify the exit routine that we're actually exiting now */
+ up(&(us->notify));
+
+ return 0;
+}
+
+/* This is the list of devices we recognize, along with their flag data */
+static struct us_unusual_dev us_unusual_dev_list[] = {
+ { 0x03f0, 0x0107, 0x0200, 0x0200, "HP USB CD-Writer Plus",
+ US_SC_8070, US_PR_CB, 0},
+ { 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita LS-120",
+ US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN},
+ { 0x04e6, 0x0002, 0x0100, 0x0100, "Shuttle eUSCSI Bridge",
+ US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH},
+ { 0x04e6, 0x0006, 0x0100, 0x0100, "Shuttle eUSB MMC Adapter",
+ US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN},
+ { 0x054c, 0x0010, 0x0210, 0x0210, "Sony DSC-S30/S70",
+ US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP |
+ US_FL_MODE_XLATE | US_FL_ALT_LENGTH | US_FL_ALT_LENGTH},
+ { 0x054c, 0x002d, 0x0100, 0x0100, "Sony Memorystick MSAC-US1",
+ US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP |
+ US_FL_MODE_XLATE | US_FL_ALT_LENGTH},
+ { 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data Flashbuster-U",
+ US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN},
+ { 0x057b, 0x0000, 0x0300, 0x9999, "Y-E Data Flashbuster-U",
+ US_SC_UFI, US_PR_CBI, US_FL_SINGLE_LUN},
+ { 0x0693, 0x0002, 0x0100, 0x0100, "Hagiwara FlashGate SmartMedia",
+ US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH},
+ { 0x0781, 0x0001, 0x0200, 0x0200, "Sandisk ImageMate (SDDR-01)",
+ US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP},
+ { 0x0781, 0x0002, 0x0009, 0x0009, "Sandisk Imagemate (SDDR-31)",
+ US_SC_SCSI, US_PR_BULK, US_FL_IGNORE_SER},
+ { 0x07af, 0x0004, 0x0100, 0x0100, "Microtech USB-SCSI-DB25",
+ US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH},
+ { 0x07af, 0x0005, 0x0100, 0x0100, "Microtech USB-SCSI-HD50",
+ US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH},
+ { 0x05ab, 0x0031, 0x0100, 0x0100, "In-System USB/IDE Bridge",
+ US_SC_8070, US_PR_BULK, US_FL_ALT_LENGTH},
+ { 0x0693, 0x0005, 0x0100, 0x0100, "Hagiwara Flashgate",
+ US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH},
+ { 0 }};
+
+/* Search our ususual device list, based on vendor/product combinations
+ * to see if we can support this device. Returns a pointer to a structure
+ * defining how we should support this device, or NULL if it's not in the
+ * list
+ */
+static struct us_unusual_dev* us_find_dev(u16 idVendor, u16 idProduct,
+ u16 bcdDevice)
+{
+ struct us_unusual_dev* ptr;
+
+ US_DEBUGP("Searching unusual device list for (0x%x, 0x%x, 0x%x)...\n",
+ idVendor, idProduct, bcdDevice);
+
+ ptr = us_unusual_dev_list;
+ while ((ptr->idVendor != 0x0000) &&
+ !((ptr->idVendor == idVendor) &&
+ (ptr->idProduct == idProduct) &&
+ (ptr->bcdDeviceMin <= bcdDevice) &&
+ (ptr->bcdDeviceMax >= bcdDevice)))
+ ptr++;
+
+ /* if the search ended because we hit the end record, we failed */
+ if (ptr->idVendor == 0x0000) {
+ US_DEBUGP("-- did not find a matching device\n");
+ return NULL;
+ }
+
+ /* otherwise, we found one! */
+ US_DEBUGP("-- found matching device: %s\n", ptr->name);
+ return ptr;
+}
+
+/* Set up the IRQ pipe and handler
+ * Note that this function assumes that all the data in the us_data
+ * strucuture is current. This includes the ep_int field, which gives us
+ * the endpoint for the interrupt.
+ * Returns non-zero on failure, zero on success
+ */
+static int usb_stor_allocate_irq(struct us_data *ss)
+{
+ unsigned int pipe;
+ int maxp;
+ int result;
+
+ US_DEBUGP("Allocating IRQ for CBI transport\n");
+
+ /* lock access to the data structure */
+ down(&(ss->irq_urb_sem));
+
+ /* allocate the URB */
+ ss->irq_urb = usb_alloc_urb(0);
+ if (!ss->irq_urb) {
+ up(&(ss->irq_urb_sem));
+ US_DEBUGP("couldn't allocate interrupt URB");
+ return 1;
+ }
+
+ /* calculate the pipe and max packet size */
+ pipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK);
+ maxp = usb_maxpacket(ss->pusb_dev, pipe, usb_pipeout(pipe));
+ if (maxp > sizeof(ss->irqbuf))
+ maxp = sizeof(ss->irqbuf);
+
+ /* fill in the URB with our data */
+ FILL_INT_URB(ss->irq_urb, ss->pusb_dev, pipe, ss->irqbuf, maxp,
+ usb_stor_CBI_irq, ss, ss->ep_int->bInterval);
+
+ /* submit the URB for processing */
+ result = usb_submit_urb(ss->irq_urb);
+ US_DEBUGP("usb_submit_urb() returns %d\n", result);
+ if (result) {
+ usb_free_urb(ss->irq_urb);
+ up(&(ss->irq_urb_sem));
+ return 2;
+ }
+
+ /* unlock the data structure and return success */
+ up(&(ss->irq_urb_sem));
+ return 0;
+}
+
+/* Probe to see if a new device is actually a SCSI device */
+static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
+{
+ int i;
+ char mf[USB_STOR_STRING_LEN]; /* manufacturer */
+ char prod[USB_STOR_STRING_LEN]; /* product */
+ char serial[USB_STOR_STRING_LEN]; /* serial number */
+ GUID(guid); /* Global Unique Identifier */
+ unsigned int flags;
+ struct us_unusual_dev *unusual_dev;
+ struct us_data *ss = NULL;
+ int result;
+
+ /* these are temporary copies -- we test on these, then put them
+ * in the us-data structure
+ */
+ struct usb_endpoint_descriptor *ep_in = NULL;
+ struct usb_endpoint_descriptor *ep_out = NULL;
+ struct usb_endpoint_descriptor *ep_int = NULL;
+ u8 subclass = 0;
+ u8 protocol = 0;
+
+ /* the altsettting 0 on the interface we're probing */
+ struct usb_interface_descriptor *altsetting =
+ &(dev->actconfig->interface[ifnum].altsetting[0]);
+
+ /* clear the temporary strings */
+ memset(mf, 0, sizeof(mf));
+ memset(prod, 0, sizeof(prod));
+ memset(serial, 0, sizeof(serial));
+
+ /* search for this device in our unusual device list */
+ unusual_dev = us_find_dev(dev->descriptor.idVendor,
+ dev->descriptor.idProduct,
+ dev->descriptor.bcdDevice);
+
+ /*
+ * Can we support this device, either because we know about it
+ * from our unusual device list, or because it advertises that it's
+ * compliant to the specification?
+ */
+ if (!unusual_dev &&
+ !(dev->descriptor.bDeviceClass == 0 &&
+ altsetting->bInterfaceClass == USB_CLASS_MASS_STORAGE &&
+ altsetting->bInterfaceSubClass >= US_SC_MIN &&
+ altsetting->bInterfaceSubClass <= US_SC_MAX)) {
+ /* if it's not a mass storage, we go no further */
+ return NULL;
+ }
+
+ /* At this point, we know we've got a live one */
+ US_DEBUGP("USB Mass Storage device detected\n");
+
+ /* Determine subclass and protocol, or copy from the interface */
+ if (unusual_dev) {
+ subclass = unusual_dev->useProtocol;
+ protocol = unusual_dev->useTransport;
+ flags = unusual_dev->flags;
+ } else {
+ subclass = altsetting->bInterfaceSubClass;
+ protocol = altsetting->bInterfaceProtocol;
+ flags = 0;
+ }
+
+ /*
+ * Find the endpoints we need
+ * We are expecting a minimum of 2 endpoints - in and out (bulk).
+ * An optional interrupt is OK (necessary for CBI protocol).
+ * We will ignore any others.
+ */
+ for (i = 0; i < altsetting->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((altsetting->endpoint[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ /* BULK in or out? */
+ if (altsetting->endpoint[i].bEndpointAddress &
+ USB_DIR_IN)
+ ep_in = &altsetting->endpoint[i];
+ else
+ ep_out = &altsetting->endpoint[i];
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((altsetting->endpoint[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+ ep_int = &altsetting->endpoint[i];
+ }
+ }
+ US_DEBUGP("Endpoints: In: 0x%p Out: 0x%p Int: 0x%p (Period %d)\n",
+ ep_in, ep_out, ep_int, ep_int ? ep_int->bInterval : 0);
+
+ /* set the interface -- STALL is an acceptable response here */
+ result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
+ US_DEBUGP("Result from usb_set_interface is %d\n", result);
+ if (result == -EPIPE) {
+ US_DEBUGP("-- clearing stall on control interface\n");
+ usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
+ } else if (result != 0) {
+ /* it's not a stall, but another error -- time to bail */
+ US_DEBUGP("-- Unknown error. Rejecting device\n");
+ return NULL;
+ }
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) {
+ US_DEBUGP("Sanity check failed. Rejecting device.\n");
+ return NULL;
+ }
+
+ /* At this point, we're committed to using the device */
+
+ /* clear the GUID and fetch the strings */
+ GUID_CLEAR(guid);
+ if (dev->descriptor.iManufacturer)
+ usb_string(dev, dev->descriptor.iManufacturer,
+ mf, sizeof(mf));
+ if (dev->descriptor.iProduct)
+ usb_string(dev, dev->descriptor.iProduct,
+ prod, sizeof(prod));
+ if (dev->descriptor.iSerialNumber && !(flags & US_FL_IGNORE_SER))
+ usb_string(dev, dev->descriptor.iSerialNumber,
+ serial, sizeof(serial));
+
+ /* Create a GUID for this device */
+ if (dev->descriptor.iSerialNumber && serial[0]) {
+ /* If we have a serial number, and it's a non-NULL string */
+ make_guid(guid, dev->descriptor.idVendor,
+ dev->descriptor.idProduct, serial);
+ } else {
+ /* We don't have a serial number, so we use 0 */
+ make_guid(guid, dev->descriptor.idVendor,
+ dev->descriptor.idProduct, "0");
+ }
+
+ /* lock access to the data structures */
+ down(&us_list_semaphore);
+
+ /*
+ * Now check if we have seen this GUID before
+ * We're looking for a device with a matching GUID that isn't
+ * allready on the system
+ */
+ ss = us_list;
+ while ((ss != NULL) &&
+ ((ss->pusb_dev) || !GUID_EQUAL(guid, ss->guid)))
+ ss = ss->next;
+
+ if (ss != NULL) {
+ /* Existing device -- re-connect */
+ US_DEBUGP("Found existing GUID " GUID_FORMAT "\n",
+ GUID_ARGS(guid));
+
+ /* establish the connection to the new device upon reconnect */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
+
+ /* copy over the endpoint data */
+ if (ep_in)
+ ss->ep_in = ep_in->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ if (ep_out)
+ ss->ep_out = ep_out->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ ss->ep_int = ep_int;
+
+ /* allocate an IRQ callback if one is needed */
+ if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss))
+ return NULL;
+ } else {
+ /* New device -- allocate memory and initialize */
+ US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+
+ if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data),
+ GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING USB_STORAGE "Out of memory\n");
+ up(&us_list_semaphore);
+ return NULL;
+ }
+ memset(ss, 0, sizeof(struct us_data));
+
+ /* allocate the URB we're going to use */
+ ss->current_urb = usb_alloc_urb(0);
+ if (!ss->current_urb) {
+ kfree(ss);
+ return NULL;
+ }
+
+ /* Initialize the mutexes only when the struct is new */
+ init_MUTEX_LOCKED(&(ss->sleeper));
+ init_MUTEX_LOCKED(&(ss->notify));
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
+ init_MUTEX(&(ss->queue_exclusion));
+ init_MUTEX(&(ss->irq_urb_sem));
+ init_MUTEX(&(ss->current_urb_sem));
+ init_MUTEX(&(ss->dev_semaphore));
+
+ /* copy over the subclass and protocol data */
+ ss->subclass = subclass;
+ ss->protocol = protocol;
+ ss->flags = flags;
+
+ /* copy over the endpoint data */
+ if (ep_in)
+ ss->ep_in = ep_in->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ if (ep_out)
+ ss->ep_out = ep_out->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ ss->ep_int = ep_int;
+
+ /* establish the connection to the new device */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
+
+ /* copy over the identifiying strings */
+ strncpy(ss->vendor, mf, USB_STOR_STRING_LEN);
+ strncpy(ss->product, prod, USB_STOR_STRING_LEN);
+ strncpy(ss->serial, serial, USB_STOR_STRING_LEN);
+ if (strlen(ss->vendor) == 0)
+ strncpy(ss->vendor, "Unknown", USB_STOR_STRING_LEN);
+ if (strlen(ss->product) == 0)
+ strncpy(ss->product, "Unknown", USB_STOR_STRING_LEN);
+ if (strlen(ss->serial) == 0)
+ strncpy(ss->serial, "None", USB_STOR_STRING_LEN);
+
+ /* copy the GUID we created before */
+ memcpy(ss->guid, guid, sizeof(guid));
+
+ /*
+ * Set the handler pointers based on the protocol
+ * Again, this data is persistant across reattachments
+ */
+ switch (ss->protocol) {
+ case US_PR_CB:
+ ss->transport_name = "Control/Bulk";
+ ss->transport = usb_stor_CB_transport;
+ ss->transport_reset = usb_stor_CB_reset;
+ ss->max_lun = 7;
+ break;
+
+ case US_PR_CBI:
+ ss->transport_name = "Control/Bulk/Interrupt";
+ ss->transport = usb_stor_CBI_transport;
+ ss->transport_reset = usb_stor_CB_reset;
+ ss->max_lun = 7;
+ break;
+
+ case US_PR_BULK:
+ ss->transport_name = "Bulk";
+ ss->transport = usb_stor_Bulk_transport;
+ ss->transport_reset = usb_stor_Bulk_reset;
+ ss->max_lun = usb_stor_Bulk_max_lun(ss);
+ break;
+
+ default:
+ ss->transport_name = "Unknown";
+ up(&us_list_semaphore);
+ kfree(ss->current_urb);
+ kfree(ss);
+ return NULL;
+ break;
+ }
+ US_DEBUGP("Transport: %s\n", ss->transport_name);
+
+ /* fix for single-lun devices */
+ if (ss->flags & US_FL_SINGLE_LUN)
+ ss->max_lun = 0;
+
+ switch (ss->subclass) {
+ case US_SC_RBC:
+ ss->protocol_name = "Reduced Block Commands (RBC)";
+ ss->proto_handler = usb_stor_transparent_scsi_command;
+ break;
+
+ case US_SC_8020:
+ ss->protocol_name = "8020i";
+ ss->proto_handler = usb_stor_ATAPI_command;
+ break;
+
+ case US_SC_QIC:
+ ss->protocol_name = "QIC-157";
+ US_DEBUGP("Sorry, device not supported. Please\n");
+ US_DEBUGP("contact mdharm-usb@one-eyed-alien.net\n");
+ US_DEBUGP("if you see this message.\n");
+ up(&us_list_semaphore);
+ kfree(ss->current_urb);
+ kfree(ss);
+ return NULL;
+ break;
+
+ case US_SC_8070:
+ ss->protocol_name = "8070i";
+ ss->proto_handler = usb_stor_ATAPI_command;
+ break;
+
+ case US_SC_SCSI:
+ ss->protocol_name = "Transparent SCSI";
+ ss->proto_handler = usb_stor_transparent_scsi_command;
+ break;
+
+ case US_SC_UFI:
+ ss->protocol_name = "Uniform Floppy Interface (UFI)";
+ ss->proto_handler = usb_stor_ufi_command;
+ break;
+
+ default:
+ ss->protocol_name = "Unknown";
+ up(&us_list_semaphore);
+ kfree(ss->current_urb);
+ kfree(ss);
+ return NULL;
+ break;
+ }
+ US_DEBUGP("Protocol: %s\n", ss->protocol_name);
+
+ /* allocate an IRQ callback if one is needed */
+ if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss))
+ return NULL;
+
+ /*
+ * Since this is a new device, we need to generate a scsi
+ * host definition, and register with the higher SCSI layers
+ */
+
+ /* Initialize the host template based on the default one */
+ memcpy(&(ss->htmplt), &usb_stor_host_template,
+ sizeof(usb_stor_host_template));
+
+ /* Grab the next host number */
+ ss->host_number = my_host_number++;
+
+ /* We abuse this pointer so we can pass the ss pointer to
+ * the host controler thread in us_detect. But how else are
+ * we to do it?
+ */
+ (struct us_data *)ss->htmplt.proc_dir = ss;
+
+ /* start up our control thread */
+ ss->pid = kernel_thread(usb_stor_control_thread, ss,
+ CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND);
+ if (ss->pid < 0) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start control thread\n");
+ kfree(ss->current_urb);
+ kfree(ss);
+ return NULL;
+ }
+
+ /* wait for the thread to start */
+ down(&(ss->notify));
+
+ /* now register - our detect function will be called */
+ ss->htmplt.module = THIS_MODULE;
+ scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt));
+
+ /* put us in the list */
+ ss->next = us_list;
+ us_list = ss;
+ }
+
+ /* release the data structure lock */
+ up(&us_list_semaphore);
+
+ printk(KERN_DEBUG
+ "WARNING: USB Mass Storage data integrity not assured\n");
+ printk(KERN_DEBUG
+ "USB Mass Storage device found at %d\n", dev->devnum);
+
+ /* return a pointer for the disconnect function */
+ return ss;
+}
+
+/* Handle a disconnect event from the USB core */
+static void storage_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct us_data *ss = ptr;
+ int result;
+
+ US_DEBUGP("storage_disconnect() called\n");
+
+ /* this is the odd case -- we disconnected but weren't using it */
+ if (!ss) {
+ US_DEBUGP("-- device was not in use\n");
+ return;
+ }
+
+ /* lock access to the device data structure */
+ down(&(ss->dev_semaphore));
+
+ /* release the IRQ, if we have one */
+ down(&(ss->irq_urb_sem));
+ if (ss->irq_urb) {
+ US_DEBUGP("-- releasing irq handle\n");
+ result = usb_unlink_urb(ss->irq_urb);
+ ss->irq_urb = NULL;
+ US_DEBUGP("-- usb_unlink_urb() returned %d\n", result);
+ usb_free_urb(ss->irq_urb);
+ }
+ up(&(ss->irq_urb_sem));
+
+ /* mark the device as gone */
+ ss->pusb_dev = NULL;
+
+ /* lock access to the device data structure */
+ up(&(ss->dev_semaphore));
+}
+
+/***********************************************************************
+ * Initialization and registration
+ ***********************************************************************/
+
+int __init usb_stor_init(void)
+{
+ /* initialize internal global data elements */
+ us_list = NULL;
+ init_MUTEX(&us_list_semaphore);
+ my_host_number = 0;
+
+ /* register the driver, return -1 if error */
+ if (usb_register(&storage_driver) < 0)
+ return -1;
+
+ /* we're all set */
+ printk(KERN_INFO "USB Mass Storage support registered.\n");
+ return 0;
+}
+
+void __exit usb_stor_exit(void)
+{
+ struct us_data *next;
+
+ US_DEBUGP("usb_stor_exit() called\n");
+
+ /* Deregister the driver
+ * This eliminates races with probes and disconnects
+ */
+ US_DEBUGP("-- calling usb_deregister()\n");
+ usb_deregister(&storage_driver) ;
+
+ /* lock access to the data structures */
+ down(&us_list_semaphore);
+
+ /* While there are still virtual hosts, unregister them
+ *
+ * Note that the us_release() routine will destroy the local data
+ * structure. So we have to peel these off the top of the list
+ * and keep updating the head pointer as we go.
+ */
+ while (us_list) {
+ /* keep track of where the next one is */
+ next = us_list->next;
+
+ US_DEBUGP("-- calling scsi_unregister_module()\n");
+ scsi_unregister_module(MODULE_SCSI_HA, &(us_list->htmplt));
+
+ /* Now that scsi_unregister_module is done with the host
+ * template, we can free the us_data structure (the host
+ * template is inline in this structure). */
+ kfree (us_list);
+
+ /* advance the list pointer */
+ us_list = next;
+ }
+
+ /* unlock the data structures */
+ up(&us_list_semaphore);
+}
+
+module_init(usb_stor_init) ;
+module_exit(usb_stor_exit) ;
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
new file mode 100644
index 000000000..f18b6c868
--- /dev/null
+++ b/drivers/usb/storage/usb.h
@@ -0,0 +1,182 @@
+/* Driver for USB Mass Storage compliant devices
+ * Main Header File
+ *
+ * $Id: usb.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ *
+ * Current development and maintainance by:
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Initial work by:
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#include <linux/usb.h>
+#include <linux/blk.h>
+#include <linux/smp_lock.h>
+#include "scsi.h"
+#include "hosts.h"
+
+/*
+ * GUID definitions
+ */
+
+#define GUID(x) __u32 x[3]
+#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2])
+#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0;
+#define GUID_NONE(x) (!x[0] && !x[1] && !x[2])
+#define GUID_FORMAT "%08x%08x%08x"
+#define GUID_ARGS(x) x[0], x[1], x[2]
+
+static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial)
+{
+ pg[0] = (vendor << 16) | product;
+ pg[1] = pg[2] = 0;
+ while (*serial) {
+ pg[1] <<= 4;
+ pg[1] |= pg[2] >> 28;
+ pg[2] <<= 4;
+ if (*serial >= 'a')
+ *serial -= 'a' - 'A';
+ pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0'
+ : *serial - 'A' + 10;
+ serial++;
+ }
+}
+
+/*
+ * Unusual device list definitions
+ */
+
+struct us_unusual_dev {
+ /* we search the list based on these parameters */
+ __u16 idVendor;
+ __u16 idProduct;
+ __u16 bcdDeviceMin;
+ __u16 bcdDeviceMax;
+
+ /* the list specifies these parameters */
+ const char* name;
+ __u8 useProtocol;
+ __u8 useTransport;
+ unsigned int flags;
+};
+
+/* Flag definitions */
+#define US_FL_SINGLE_LUN 0x00000001 /* allow access to only LUN 0 */
+#define US_FL_MODE_XLATE 0x00000002 /* translate _6 to _10 comands for
+ Win/MacOS compatibility */
+#define US_FL_START_STOP 0x00000004 /* ignore START_STOP commands */
+#define US_FL_ALT_LENGTH 0x00000008 /* use the alternate algorithm for
+ us_transfer_length() */
+#define US_FL_IGNORE_SER 0x00000010 /* Ignore the serial number given */
+
+#define USB_STOR_STRING_LEN 32
+
+struct us_data;
+
+typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
+typedef int (*trans_reset)(struct us_data*);
+typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
+
+/* we allocate one of these for every device that we remember */
+struct us_data {
+ struct us_data *next; /* next device */
+
+ /* the device we're working with */
+ struct semaphore dev_semaphore; /* protect pusb_dev */
+ struct usb_device *pusb_dev; /* this usb_device */
+
+ unsigned int flags; /* from filter initially */
+
+ /* information about the device -- always good */
+ char vendor[USB_STOR_STRING_LEN];
+ char product[USB_STOR_STRING_LEN];
+ char serial[USB_STOR_STRING_LEN];
+ char *transport_name;
+ char *protocol_name;
+ u8 subclass;
+ u8 protocol;
+ u8 max_lun;
+
+ /* information about the device -- only good if device is attached */
+ u8 ifnum; /* interface number */
+ u8 ep_in; /* bulk in endpoint */
+ u8 ep_out; /* bulk out endpoint */
+ struct usb_endpoint_descriptor *ep_int; /* interrupt endpoint */
+
+ /* function pointers for this device */
+ trans_cmnd transport; /* transport function */
+ trans_reset transport_reset; /* transport device reset */
+ proto_cmnd proto_handler; /* protocol handler */
+
+ /* SCSI interfaces */
+ GUID(guid); /* unique dev id */
+ struct Scsi_Host *host; /* our dummy host data */
+ Scsi_Host_Template htmplt; /* own host template */
+ int host_number; /* to find us */
+ int host_no; /* allocated by scsi */
+ Scsi_Cmnd *srb; /* current srb */
+
+ /* thread information */
+ Scsi_Cmnd *queue_srb; /* the single queue slot */
+ int action; /* what to do */
+ int pid; /* control thread */
+
+ /* interrupt info for CBI devices -- only good if attached */
+ struct semaphore ip_waitq; /* for CBI interrupts */
+ int ip_wanted; /* is an IRQ expected? */
+
+ /* interrupt communications data */
+ struct semaphore irq_urb_sem; /* to protect irq_urb */
+ struct urb *irq_urb; /* for USB int requests */
+ unsigned char irqbuf[2]; /* buffer for USB IRQ */
+
+ /* control and bulk communications data */
+ struct semaphore current_urb_sem; /* to protect irq_urb */
+ struct urb *current_urb; /* non-int USB requests */
+
+ /* mutual exclusion structures */
+ struct semaphore notify; /* thread begin/end */
+ struct semaphore sleeper; /* to sleep the thread on */
+ struct semaphore queue_exclusion; /* to protect data structs */
+};
+
+/* The list of structures and the protective lock for them */
+extern struct us_data *us_list;
+extern struct semaphore us_list_semaphore;
+
+#endif
diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c
index 08ef79fe4..3fba783e0 100644
--- a/drivers/usb/usb.c
+++ b/drivers/usb/usb.c
@@ -1683,8 +1683,9 @@ int usb_new_device(struct usb_device *dev)
usb_set_maxpacket(dev);
/* we set the default configuration here */
- if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
- err("failed to set default configuration");
+ err = usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+ if (err) {
+ err("failed to set default configuration (error=%d)", err);
return -1;
}