mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* drivers/block/ub.c #6
@ 2004-07-30 10:51 Pete Zaitcev
  2004-08-27 21:02 ` [usb-storage] " Pat LaVarre
  0 siblings, 1 reply; 4+ messages in thread
From: Pete Zaitcev @ 2004-07-30 10:51 UTC (permalink / raw)
  To: greg, arjanv, jgarzik; +Cc: zaitcev, linux-kernel, usb-storage

Same as before, but it's at least semi-useful now. I actually would consider
it mergeable as EXPERIMENTAL, but for one thing: I did not ask LANANA for
a major. Instead I would like to try some sort of fully dynamic allocation
using udev. It looks like a good opportunity to learn about udev.

The next step is to add ioctls to burn CDs, and to clone ub.c into ufi.c
with corresponding refactoring of common pieces into a library like libata.
It would be neat if someone othen than I did that, to validate the
readability, but I'm not holding my breath.

I wish this could be somehow limited to flash keys, but I don't see a way.
Without such limit or ioctls, ub cannot be used by distributions at present.

-- Pete

Same as #5, plus added support for removable storage (flash cards).
For the moment, the removable flag is hardwired to 1.

diff -urpN -X dontdiff linux-2.6.7/drivers/block/Kconfig linux-2.6.7-ub/drivers/block/Kconfig
--- linux-2.6.7/drivers/block/Kconfig	2004-06-16 16:53:48.000000000 -0700
+++ linux-2.6.7-ub/drivers/block/Kconfig	2004-07-29 19:55:48.000000000 -0700
@@ -301,6 +301,15 @@ config BLK_DEV_CARMEL
 
 	  Use devices /dev/carmel/$N and /dev/carmel/$Np$M.
 
+config BLK_DEV_UB
+	tristate "Low Performance USB Block driver"
+	depends on USB
+	help
+	  This driver supports certain USB attached storage devices
+	  such as flash keys.
+
+	  If unsure, say N.
+
 config BLK_DEV_RAM
 	tristate "RAM disk support"
 	---help---
diff -urpN -X dontdiff linux-2.6.7/drivers/block/Makefile linux-2.6.7-ub/drivers/block/Makefile
--- linux-2.6.7/drivers/block/Makefile	2004-05-10 17:39:54.000000000 -0700
+++ linux-2.6.7-ub/drivers/block/Makefile	2004-07-26 00:00:21.000000000 -0700
@@ -42,4 +42,5 @@ obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryp
 
 obj-$(CONFIG_VIODASD)		+= viodasd.o
 obj-$(CONFIG_BLK_DEV_CARMEL)	+= carmel.o
+obj-$(CONFIG_BLK_DEV_UB)	+= ub.o
 
diff -urpN -X dontdiff linux-2.6.7/drivers/block/ub.c linux-2.6.7-ub/drivers/block/ub.c
--- linux-2.6.7/drivers/block/ub.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.7-ub/drivers/block/ub.c	2004-07-30 03:21:12.024123357 -0700
@@ -0,0 +1,1919 @@
+/*
+ * The low performance USB storage driver (ub).
+ *
+ * Copyright (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ * Copyright (C) 2004 Pete Zaitcev (zaitcev@yahoo.com)
+ *
+ * This work is a part of Linux kernel, is derived from it,
+ * and is not licensed separately. See file COPYING for details.
+ *
+ * TODO (sorted by decreasing priority)
+ *  -- set readonly flag for CDs, set removable flag for CF readers
+ *  -- do inquiry and verify we got a disk and not a tape (for LUN mismatch)
+ *  -- support pphaneuf's SDDR-75 with two LUNs (also broken capacity...)
+ *  -- special case some senses, e.g. 3a/0 -> no media present, reduce retries
+ *  -- normal queue instead of sc->busy and friends (needed for revalidation)
+ *  -- do something about spin-down devices, they are extremely dangerous
+ *     (we need to find one first though)
+ *  -- verify the 13 conditions and do bulk resets
+ *  -- normal pool of commands instead of cmdv[]?
+ *  -- verify protocol (bulk) from USB descriptors (maybe...)
+ *  -- highmem and sg
+ *  -- move top_sense and work_bcs into separate allocations (if they survive)
+ *     for cache purists and esoteric architectures.
+ *  -- prune comments, they are too volumnous
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/blkdev.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/timer.h>
+#include <scsi/scsi.h>
+
+#define DRV_NAME "ub"
+#define DEVFS_NAME DRV_NAME
+
+/*
+ * Definitions which have to be scattered once we understand the layout better.
+ */
+
+/* Transport (despite PR in the name) */
+#define US_PR_BULK	0x50		/* bulk only */
+
+/* Protocol */
+#define US_SC_SCSI	0x06		/* Transparent */
+
+/*
+ */
+#define UB_MINORS_PER_MAJOR	8
+
+#define UB_MAX_CDB_SIZE      16		/* Corresponds to Bulk */
+
+#define UB_SENSE_SIZE  18
+
+/*
+ */
+
+/* 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[UB_MAX_CDB_SIZE];	/* 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 */
+};
+
+#define US_BULK_CS_WRAP_LEN	13
+#define US_BULK_CS_SIGN		0x53425355	/* spells out 'USBS' */
+/* This is for Olympus Camedia digital cameras */
+#define US_BULK_CS_OLYMPUS_SIGN	0x55425355	/* spells out 'USBU' */
+#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
+
+/*
+ */
+struct ub_dev;
+
+#define UB_MAX_REQ_SG	1
+#define UB_MAX_SECTORS 64
+
+/*
+ * A second ought to be enough for a 32K transfer (UB_MAX_SECTORS)
+ * even if a webcam hogs the bus (famous last words).
+ * Some CDs need a second to spin up though.
+ */
+#define UB_URB_TIMEOUT	(HZ*2)
+#define UB_CTRL_TIMEOUT	(HZ/2) /* 500ms ought to be enough to clear a stall */
+
+/*
+ * An instance of a SCSI command in transit.
+ */
+#define UB_DIR_NONE	0
+#define UB_DIR_READ	1
+#define UB_DIR_ILLEGAL2	2
+#define UB_DIR_WRITE	3
+
+#define UB_DIR_CHAR(c)  (((c)==UB_DIR_WRITE)? 'w': \
+			 (((c)==UB_DIR_READ)? 'r': 'n'))
+
+enum ub_scsi_cmd_state {
+	UB_CMDST_INIT,			/* Initial state */
+	UB_CMDST_CMD,			/* Command submitted */
+	UB_CMDST_DATA,			/* Data phase */
+	UB_CMDST_CLR2STS,		/* Clearing before requesting status */
+	UB_CMDST_STAT,			/* Status phase */
+	UB_CMDST_CLEAR,			/* Clearing a stall (halt, actually) */
+	UB_CMDST_SENSE,			/* Sending Request Sense */
+	UB_CMDST_DONE			/* Final state */
+};
+
+static char *ub_scsi_cmd_stname[] = {
+	".  ",
+	"Cmd",
+	"dat",
+	"c2s",
+	"sts",
+	"clr",
+	"Sen",
+	"fin"
+};
+
+struct ub_scsi_cmd {
+	unsigned char cdb[UB_MAX_CDB_SIZE];
+	unsigned char cdb_len;
+
+	unsigned char dir;		/* 0 - none, 1 - read, 3 - write. */
+	enum ub_scsi_cmd_state state;
+	unsigned int tag;
+	unsigned int trace_index;
+
+	int error;			/* Return code - valid upon done */
+	int act_len;			/* Return size */
+
+	int stat_count;			/* Retries getting status. */
+
+	/*
+	 * We do not support transfers from highmem pages
+	 * because the underlying USB framework does not do what we need.
+	 */
+	char *data;			/* Requested buffer */
+	unsigned int len;		/* Requested length */
+	// struct scatterlist sgv[UB_MAX_REQ_SG];
+
+	void (*done)(struct ub_dev *, struct ub_scsi_cmd *);
+	void *back;
+};
+
+/*
+ */
+struct ub_capacity {
+	unsigned long nsec;		/* Linux size - 512 byte sectors */
+	unsigned int bsize;		/* Linux hardsect_size */
+	unsigned int bshift;		/* Shift between 512 and hard sects */
+};
+
+/*
+ * The SCSI command tracing structure.
+ */
+
+#define SCMD_ST_HIST_SZ   8
+#define SCMD_TRACE_SZ    15
+
+struct ub_scsi_cmd_trace {
+	int hcur;
+	unsigned int tag;
+	unsigned int req_size, act_size;
+	unsigned char op;
+	unsigned char dir;
+	unsigned char key, asc, ascq;
+	char st_hst[SCMD_ST_HIST_SZ];	
+};
+
+struct ub_scsi_trace {
+	int cur;
+	struct ub_scsi_cmd_trace vec[SCMD_TRACE_SZ];
+};
+
+/*
+ * The UB device instance.
+ */
+struct ub_dev {
+	spinlock_t lock;
+	int id;				/* Number among ub's */
+	atomic_t poison;		/* The USB device is disconnected */
+	int openc;			/* protected by ub_lock! */
+					/* kref is too implicit for our taste */
+	unsigned int tagcnt;
+	int changed;			/* Media was changed */
+	int removable;
+	int readonly;
+	char name[8];
+	struct usb_device *dev;
+	struct usb_interface *intf;
+
+	struct ub_capacity capacity; 
+	struct gendisk *disk;
+
+	unsigned int send_bulk_pipe;	/* cached pipe values */
+	unsigned int recv_bulk_pipe;
+	unsigned int send_ctrl_pipe;
+	unsigned int recv_ctrl_pipe;
+
+	struct tasklet_struct urb_tasklet;
+
+	/* XXX Use Ingo's mempool (once we have more than one) */
+	int cmda[1];
+	struct ub_scsi_cmd cmdv[1];
+
+	int busy;
+	struct ub_scsi_cmd *top_cmd;	/* XXX Under ->busy until we have a queue */
+	struct ub_scsi_cmd top_rqs_cmd;	/* REQUEST SENSE */
+	unsigned char top_sense[UB_SENSE_SIZE];
+
+	struct urb work_urb;
+	int last_pipe;			/* What might need clearing */
+	struct bulk_cb_wrap work_bcb;
+	struct bulk_cs_wrap work_bcs;
+	struct usb_ctrlrequest work_cr;
+
+	struct ub_scsi_trace tr;
+};
+
+/*
+ */
+static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
+static void ub_end_rq(struct request *rq, int uptodate);
+static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
+static void ub_scsi_urb_complete(struct urb *urb, struct pt_regs *pt);
+static void ub_scsi_urb_action(unsigned long _dev);
+static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
+static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
+static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
+static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
+    int stalled_pipe);
+static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd);
+static int ub_sync_tur(struct ub_dev *sc);
+static int ub_sync_read_cap(struct ub_dev *sc, struct ub_capacity *ret);
+
+/*
+ */
+static struct usb_device_id ub_usb_ids[] = {
+	// { USB_DEVICE_VER(0x0781, 0x0002, 0x0009, 0x0009) },	/* SDDR-31 */
+	{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
+};
+
+MODULE_DEVICE_TABLE(usb, ub_usb_ids);
+
+/*
+ * Find me a way to identify "next free minor" for add_disk(),
+ * and the array disappears the next day. However, the number of
+ * hosts has something to do with the naming and /proc/partitions.
+ * This has to be thought out in detail before changing.
+ * If UB_MAX_HOST was 1000, we'd use a bitmap. Or a better data structure.
+ */
+#define UB_MAX_HOSTS  26
+static char ub_hostv[UB_MAX_HOSTS];
+static spinlock_t ub_lock = SPIN_LOCK_UNLOCKED;	/* Locks globals and ->openc */
+
+/*
+ * The SCSI command tracing procedures.
+ */
+
+static void ub_cmdtr_new(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	int n;
+	struct ub_scsi_cmd_trace *t;
+
+	if ((n = sc->tr.cur + 1) == SCMD_TRACE_SZ) n = 0;
+	t = &sc->tr.vec[n];
+
+	memset(t, 0, sizeof(struct ub_scsi_cmd_trace));
+	t->tag = cmd->tag;
+	t->op = cmd->cdb[0];
+	t->dir = cmd->dir;
+	t->req_size = cmd->len;
+	t->st_hst[0] = cmd->state;
+
+	sc->tr.cur = n;
+	cmd->trace_index = n;
+}
+
+static void ub_cmdtr_state(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	int n;
+	struct ub_scsi_cmd_trace *t;
+
+	t = &sc->tr.vec[cmd->trace_index];
+	if (t->tag == cmd->tag) {
+		if ((n = t->hcur + 1) == SCMD_ST_HIST_SZ) n = 0;
+		t->st_hst[n] = cmd->state;
+		t->hcur = n;
+	}
+}
+
+static void ub_cmdtr_act_len(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	struct ub_scsi_cmd_trace *t;
+
+	t = &sc->tr.vec[cmd->trace_index];
+	if (t->tag == cmd->tag)
+		t->act_size = cmd->act_len;
+}
+
+static void ub_cmdtr_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
+    unsigned char *sense)
+{
+	struct ub_scsi_cmd_trace *t;
+
+	t = &sc->tr.vec[cmd->trace_index];
+	if (t->tag == cmd->tag) {
+		t->key = sense[2] & 0x0F;
+		t->asc = sense[12];
+		t->ascq = sense[13];
+	}
+}
+
+static ssize_t ub_diag_show(struct device *dev, char *page)
+{
+	struct usb_interface *intf;
+	struct ub_dev *sc;
+	int cnt;
+	unsigned long flags;
+	int nc, nh;
+	int i, j;
+	struct ub_scsi_cmd_trace *t;
+
+	intf = to_usb_interface(dev);
+	sc = usb_get_intfdata(intf);
+	if (sc == NULL)
+		return 0;
+
+	cnt = 0;
+	spin_lock_irqsave(&sc->lock, flags);
+	if ((nc = sc->tr.cur + 1) == SCMD_TRACE_SZ) nc = 0;
+	for (j = 0; j < SCMD_TRACE_SZ; j++) {
+		t = &sc->tr.vec[nc];
+
+		cnt += sprintf(page + cnt, "%08x %02x", t->tag, t->op);
+		if (t->op == REQUEST_SENSE) {
+			cnt += sprintf(page + cnt, " [sense %x %02x %02x]",
+					t->key, t->asc, t->ascq);
+		} else {
+			cnt += sprintf(page + cnt, " %c", UB_DIR_CHAR(t->dir));
+			cnt += sprintf(page + cnt, " [%5d %5d]",
+					t->req_size, t->act_size);
+		}
+		if ((nh = t->hcur + 1) == SCMD_ST_HIST_SZ) nh = 0;
+		for (i = 0; i < SCMD_ST_HIST_SZ; i++) {
+			cnt += sprintf(page + cnt, " %s",
+					ub_scsi_cmd_stname[(int)t->st_hst[nh]]);
+			if (++nh == SCMD_ST_HIST_SZ) nh = 0;
+		}
+		cnt += sprintf(page + cnt, "\n");
+
+		if (++nc == SCMD_TRACE_SZ) nc = 0;
+	}
+	spin_unlock_irqrestore(&sc->lock, flags);
+	return cnt;
+}
+
+static DEVICE_ATTR(diag, S_IRUGO, ub_diag_show, NULL); /* N.B. World readable */
+
+/*
+ * The id allocator.
+ *
+ * This also stores the host for indexing by minor, which is somewhat dirty.
+ */
+static int ub_id_get(void)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&ub_lock, flags);
+	for (i = 0; i < UB_MAX_HOSTS; i++) {
+		if (ub_hostv[i] == 0) {
+			ub_hostv[i] = 1;
+			spin_unlock_irqrestore(&ub_lock, flags);
+			return i;
+		}
+	}
+	spin_unlock_irqrestore(&ub_lock, flags);
+	return -1;
+}
+
+static void ub_id_put(int id)
+{
+
+	if (id < 0 || id >= UB_MAX_HOSTS) {
+		printk(KERN_ERR DRV_NAME ": bad host ID %d\n", id);
+		return;
+	}
+	if (ub_hostv[id] == 0) {
+		printk(KERN_ERR DRV_NAME ": freeing free host ID %d\n", id);
+		return;
+	}
+	ub_hostv[id] = 0;
+}
+
+/*
+ * Final cleanup and deallocation.
+ * This must be called with ub_lock taken.
+ */
+static void ub_cleanup(struct ub_dev *sc)
+{
+	ub_id_put(sc->id);
+	kfree(sc);
+}
+
+/*
+ * The "command allocator".
+ */
+static struct ub_scsi_cmd *ub_get_cmd(struct ub_dev *sc)
+{
+	struct ub_scsi_cmd *ret;
+
+	if (sc->cmda[0])
+		return NULL;
+	ret = &sc->cmdv[0];
+	sc->cmda[0] = 1;
+	return ret;
+}
+
+static void ub_put_cmd(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	if (cmd != &sc->cmdv[0]) {
+		printk(KERN_WARNING "%s: releasing a foreign cmd %p\n",
+		    sc->name, cmd);
+		return;
+	}
+	if (!sc->cmda[0]) {
+		printk(KERN_WARNING "%s: releasing a free cmd\n", sc->name);
+		return;
+	}
+	sc->cmda[0] = 0;
+}
+
+/*
+ * The request function is our main entry point
+ */
+
+static inline int ub_bd_rq_fn_1(request_queue_t *q)
+{
+#if 0
+	int writing = 0, pci_dir, i, n_elem;
+	u32 tmp;
+	unsigned int msg_size;
+#endif
+	struct ub_dev *sc = q->queuedata;
+	struct request *rq;
+#if 0 /* We use rq->buffer for now */
+	struct scatterlist *sg;
+	int n_elem;
+#endif
+	struct ub_scsi_cmd *cmd;
+	int ub_dir;
+	unsigned int block, nblks;
+	int rc;
+
+	if ((rq = elv_next_request(q)) == NULL)
+		return 1;
+
+	if (atomic_read(&sc->poison) || sc->changed) {
+		blkdev_dequeue_request(rq);
+		ub_end_rq(rq, 0);
+		return 0;
+	}
+
+	if ((cmd = ub_get_cmd(sc)) == NULL) {
+		blk_stop_queue(q);
+		return 1;
+	}
+
+	blkdev_dequeue_request(rq);
+
+	if (rq_data_dir(rq) == WRITE)
+		ub_dir = UB_DIR_WRITE;
+	else
+		ub_dir = UB_DIR_READ;
+
+	/*
+	 * get scatterlist from block layer
+	 */
+#if 0 /* We use rq->buffer for now */
+	sg = &cmd->sgv[0];
+	n_elem = blk_rq_map_sg(q, rq, sg);
+	if (n_elem <= 0) {
+		ub_put_cmd(sc, cmd);
+		ub_end_rq(rq, 0);
+		blk_start_queue(q);
+		return 0;		/* request with no s/g entries? */
+	}
+
+	if (n_elem != 1) {		/* Paranoia */
+		printk(KERN_WARNING "%s: request with %d segments\n",
+		    sc->name, n_elem);
+		ub_put_cmd(sc, cmd);
+		ub_end_rq(rq, 0);
+		blk_start_queue(q);
+		return 0;
+	}
+#endif
+	/*
+	 * XXX Unfortunately, this check does not work. It is quite possible
+	 * to get bogus non-null rq->buffer if you allow sg by mistake.
+	 */
+	if (rq->buffer == NULL) {
+		/*
+		 * This must not happen if we set the queue right.
+		 * The block level must create bounce buffers for us.
+		 */
+		static int do_print = 1;
+		if (do_print) {
+			printk(KERN_WARNING "%s: unmapped request\n", sc->name);
+			do_print = 0;
+		}
+		ub_put_cmd(sc, cmd);
+		ub_end_rq(rq, 0);
+		blk_start_queue(q);
+		return 0;
+	}
+
+	/*
+	 * build the command
+	 */
+	block = rq->sector;
+	nblks = rq->nr_sectors;
+
+	memset(cmd, 0, sizeof(struct ub_scsi_cmd));
+	cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10;
+	/* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */
+	cmd->cdb[2] = block >> 24;
+	cmd->cdb[3] = block >> 16;
+	cmd->cdb[4] = block >> 8;
+	cmd->cdb[5] = block;
+	cmd->cdb[7] = nblks >> 8;
+	cmd->cdb[8] = nblks;
+	cmd->cdb_len = 10;
+	cmd->dir = ub_dir;
+	cmd->state = UB_CMDST_INIT;
+	cmd->data = rq->buffer;
+	cmd->len = nblks * 512;
+	cmd->done = ub_rw_cmd_done;
+	cmd->back = rq;
+
+	cmd->tag = sc->tagcnt++;
+	if ((rc = ub_submit_scsi(sc, cmd)) != 0) {
+		ub_put_cmd(sc, cmd);
+		ub_end_rq(rq, 0);
+		blk_start_queue(q);
+		return 0;
+	}
+
+	return 0;
+}
+
+static void ub_bd_rq_fn(request_queue_t *q)
+{
+	do { } while (ub_bd_rq_fn_1(q) == 0);
+}
+
+static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	struct request *rq = cmd->back;
+	struct gendisk *disk = sc->disk;
+	request_queue_t *q = disk->queue;
+	int uptodate;
+
+	if (cmd->error == 0)
+		uptodate = 1;
+	else
+		uptodate = 0;
+
+	ub_put_cmd(sc, cmd);
+	ub_end_rq(rq, uptodate);
+	blk_start_queue(q);
+}
+
+static void ub_end_rq(struct request *rq, int uptodate)
+{
+	int rc;
+
+	rc = end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
+	// assert(rc == 0);
+	end_that_request_last(rq);
+}
+
+/*
+ * Submit a SCSI operation.
+ *
+ * This is only called customarily from a soft interrupt or a process,
+ * so it can call back without worrying about a recursion.
+ *
+ * The Iron Law of Good Submit Routine is:
+ * Zero return - callback is done, Nonzero return - callback is not done.
+ * No exceptions.
+ *
+ * Host is assumed locked.
+ *
+ * XXX We only support Bulk for the moment.
+ */
+static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	struct bulk_cb_wrap *bcb;
+	int rc;
+
+	/*
+	 * We do not trap into BUG() here because the consequences are
+	 * sporadic stack overflows. Better to warn than to die outright.
+	 */
+	if (in_irq()) {
+		static int first_warning = 1;
+		if (first_warning) {
+			printk(KERN_WARNING DRV_NAME ": "
+			    "submitting command from a hard irq\n");
+			first_warning = 0;
+		}
+	}
+
+	/* XXX Set up a queue */
+	if (cmd != &sc->top_rqs_cmd) {	/* XXX This is getting out of hand. */
+		if (sc->busy)
+			return -EBUSY;
+		sc->busy = 1;
+		sc->top_cmd = cmd;
+	} else {
+		if (!sc->busy) {
+			return -EBUSY;
+		}
+	}
+
+	ub_cmdtr_new(sc, cmd);
+
+	if (cmd->state != UB_CMDST_INIT ||
+	    (cmd->dir != UB_DIR_NONE && cmd->len == 0)) {
+		sc->top_cmd = NULL;
+		sc->busy = 0;
+		return -EINVAL;
+	}
+
+	bcb = &sc->work_bcb;
+
+	/* set up the command wrapper */
+	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
+	bcb->Tag = cmd->tag;		/* Endianness is not important */
+	bcb->DataTransferLength = cpu_to_le32(cmd->len);
+	bcb->Flags = (cmd->dir == UB_DIR_READ) ? 0x80 : 0;
+	bcb->Lun = 0;			/* No multi-LUN yet */
+	bcb->Length = cmd->cdb_len;
+
+	/* copy the command payload */
+	memcpy(bcb->CDB, cmd->cdb, UB_MAX_CDB_SIZE);
+
+	sc->last_pipe = sc->send_bulk_pipe;
+	usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->send_bulk_pipe,
+	    bcb, US_BULK_CB_WRAP_LEN,
+	    ub_scsi_urb_complete, sc);
+	sc->work_urb.timeout = UB_URB_TIMEOUT;
+
+	/* Fill what we shouldn't be filling, because usb-storage did so. */
+	sc->work_urb.actual_length = 0;
+	sc->work_urb.error_count = 0;
+	sc->work_urb.status = 0;
+
+	if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
+
+		/* XXX Clear stalls */
+		printk("ub: cmd #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
+
+		cmd->state = UB_CMDST_DONE;
+		ub_cmdtr_state(sc, cmd);
+		sc->busy = 0;
+		sc->top_cmd = NULL;
+		return rc;
+	}
+
+	cmd->state = UB_CMDST_CMD;
+	ub_cmdtr_state(sc, cmd);
+	return 0;
+}
+
+/*
+ * Completion routine for the work URB.
+ *
+ * This can be called directly from usb_submit_urb (while we have
+ * the sc->lock taken) and from an interrupt (while we do NOT have
+ * the sc->lock taken). Therefore, bounce this off to a tasklet.
+ */
+static void ub_scsi_urb_complete(struct urb *urb, struct pt_regs *pt)
+{
+	struct ub_dev *sc = urb->context;
+
+	tasklet_schedule(&sc->urb_tasklet);
+}
+
+static void ub_scsi_urb_action(unsigned long _dev)
+{
+	struct ub_dev *sc = (struct ub_dev *) _dev;
+	unsigned long flags;
+	struct ub_scsi_cmd *cmd;
+
+	spin_lock_irqsave(&sc->lock, flags);
+	if (sc->busy) {
+		cmd = sc->top_cmd;
+		if (cmd->state == UB_CMDST_SENSE) {	/* XXX Kludgy */
+			cmd = &sc->top_rqs_cmd;
+		}
+		ub_scsi_urb_compl(sc, cmd);
+	} else {
+		/* Never happens */
+		/* P3 */ printk("ub: Action on idle device\n");
+	}
+	spin_unlock_irqrestore(&sc->lock, flags);
+}
+
+static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	struct urb *urb = &sc->work_urb;
+	struct bulk_cs_wrap *bcs;
+	int pipe;
+	int rc;
+
+/* P3 */ /** printk("ub: urb status %d pipe 0x%08x len %d act %d\n",
+ urb->status, urb->pipe, urb->transfer_buffer_length, urb->actual_length); **/
+
+	if (atomic_read(&sc->poison)) {
+		/* A little too simplistic, I feel... */
+		goto Bad_End;
+	}
+
+	if (cmd->state == UB_CMDST_CLEAR) {
+		if (urb->status == -EPIPE) {
+			/*
+			 * STALL while clearning STALL.
+			 * A STALL is illegal on a control pipe!
+			 * XXX Might try to reset the device here and retry.
+			 */
+			printk(KERN_NOTICE "%s: "
+			    "stall on control pipe for device %u\n",
+			    sc->name, sc->dev->devnum);
+			goto Bad_End;
+		}
+
+		/*
+		 * We ignore the result for the halt clear.
+		 */
+
+		/* reset the toggles and endpoint flags */
+		usb_endpoint_running(sc->dev, usb_pipeendpoint(sc->last_pipe),
+			usb_pipeout(sc->last_pipe));
+		usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
+			usb_pipeout(sc->last_pipe), 0);
+
+		ub_state_sense(sc, cmd);
+
+	} else if (cmd->state == UB_CMDST_CLR2STS) {
+		if (urb->status == -EPIPE) {
+			/*
+			 * STALL while clearning STALL.
+			 * A STALL is illegal on a control pipe!
+			 * XXX Might try to reset the device here and retry.
+			 */
+			printk(KERN_NOTICE "%s: "
+			    "stall on control pipe for device %u\n",
+			    sc->name, sc->dev->devnum);
+			goto Bad_End;
+		}
+
+		/*
+		 * We ignore the result for the halt clear.
+		 */
+
+		/* reset the toggles and endpoint flags */
+		usb_endpoint_running(sc->dev, usb_pipeendpoint(sc->last_pipe),
+			usb_pipeout(sc->last_pipe));
+		usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
+			usb_pipeout(sc->last_pipe), 0);
+
+		ub_state_stat(sc, cmd);
+
+	} else if (cmd->state == UB_CMDST_CMD) {
+		if (urb->status == -EPIPE) {
+			rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
+			if (rc != 0) {
+				printk(KERN_NOTICE "%s: "
+				    "unable to submit clear for device %u (%d)\n",
+				    sc->name, sc->dev->devnum, rc);
+				/*
+				 * This is typically ENOMEM or some other such shit.
+				 * Retrying is pointless. Just do Bad End on it...
+				 */
+				goto Bad_End;
+			}
+			cmd->state = UB_CMDST_CLEAR;
+			ub_cmdtr_state(sc, cmd);
+			return;
+		}
+		if (urb->status != 0)
+			goto Bad_End;
+		if (urb->actual_length != US_BULK_CB_WRAP_LEN) {
+			/* XXX Must do reset here to unconfuse the device */
+			goto Bad_End;
+		}
+
+		if (cmd->dir == UB_DIR_NONE) {
+			ub_state_stat(sc, cmd);
+			return;
+		}
+
+		if (cmd->dir == UB_DIR_READ)
+			pipe = sc->recv_bulk_pipe;
+		else
+			pipe = sc->send_bulk_pipe;
+		sc->last_pipe = pipe;
+		usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe,
+		    cmd->data, cmd->len,
+		    ub_scsi_urb_complete, sc);
+		sc->work_urb.timeout = UB_URB_TIMEOUT;
+		sc->work_urb.actual_length = 0;
+		sc->work_urb.error_count = 0;
+		sc->work_urb.status = 0;
+
+		if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
+
+			/* XXX Clear stalls */
+			printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
+
+			cmd->error = rc;
+			cmd->state = UB_CMDST_DONE;
+			ub_cmdtr_state(sc, cmd);
+			if (cmd != &sc->top_rqs_cmd) {	/* XXX This is getting out of hand. */
+				sc->busy = 0;
+				sc->top_cmd = NULL;
+			}
+			(*cmd->done)(sc, cmd);
+			return;
+		}
+
+		cmd->state = UB_CMDST_DATA;
+		ub_cmdtr_state(sc, cmd);
+
+	} else if (cmd->state == UB_CMDST_DATA) {
+		if (urb->status == -EPIPE) {
+			rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
+			if (rc != 0) {
+				printk(KERN_NOTICE "%s: "
+				    "unable to submit clear for device %u (%d)\n",
+				    sc->name, sc->dev->devnum, rc);
+				/*
+				 * This is typically ENOMEM or some other such shit.
+				 * Retrying is pointless. Just do Bad End on it...
+				 */
+				goto Bad_End;
+			}
+			cmd->state = UB_CMDST_CLR2STS;
+			ub_cmdtr_state(sc, cmd);
+			return;
+		}
+		if (urb->status == -EOVERFLOW) {
+			/*
+			 * A babble? Failure, but we must transfer CSW now.
+			 */
+			cmd->error = -EOVERFLOW;	/* A cheap trick... */
+		} else {
+			if (urb->status != 0)
+				goto Bad_End;
+		}
+
+		cmd->act_len = urb->actual_length;
+		ub_cmdtr_act_len(sc, cmd);
+
+		ub_state_stat(sc, cmd);
+
+	} else if (cmd->state == UB_CMDST_STAT) {
+		if (urb->status == -EPIPE) {
+			rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
+			if (rc != 0) {
+				printk(KERN_NOTICE "%s: "
+				    "unable to submit clear for device %u (%d)\n",
+				    sc->name, sc->dev->devnum, rc);
+				/*
+				 * This is typically ENOMEM or some other such shit.
+				 * Retrying is pointless. Just do Bad End on it...
+				 */
+				goto Bad_End;
+			}
+			cmd->state = UB_CMDST_CLEAR;
+			ub_cmdtr_state(sc, cmd);
+			return;
+		}
+		if (urb->status != 0)
+			goto Bad_End;
+
+		if (urb->actual_length == 0) {
+			/*
+			 * Some broken devices add unnecessary zero-length
+			 * packets to the end of their data transfers.
+			 * Such packets show up as 0-length CSWs. If we
+			 * encounter such a thing, try to read the CSW again.
+			 */
+			if (++cmd->stat_count >= 4) {
+				printk(KERN_NOTICE "%s: "
+				    "unable to get CSW on device %u\n",
+				    sc->name, sc->dev->devnum);
+				goto Bad_End;
+			}
+
+			/*
+			 * ub_state_stat only not dropping the count...
+			 */
+			sc->last_pipe = sc->recv_bulk_pipe;
+			usb_fill_bulk_urb(&sc->work_urb, sc->dev,
+			    sc->recv_bulk_pipe, &sc->work_bcs,
+			    US_BULK_CS_WRAP_LEN, ub_scsi_urb_complete, sc);
+			sc->work_urb.timeout = UB_URB_TIMEOUT;
+			sc->work_urb.actual_length = 0;
+			sc->work_urb.error_count = 0;
+			sc->work_urb.status = 0;
+
+			rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC);
+			if (rc != 0) {
+				/* XXX Clear stalls */
+				printk("%s: CSW #%d submit failed (%d)\n",
+				   sc->name, cmd->tag, rc); /* P3 */
+
+				cmd->error = rc;
+				cmd->state = UB_CMDST_DONE;
+				ub_cmdtr_state(sc, cmd);
+				if (cmd != &sc->top_rqs_cmd) {	/* XXX This is getting out of hand. */
+					sc->busy = 0;
+					sc->top_cmd = NULL;
+				}
+				(*cmd->done)(sc, cmd);
+				return;
+			}
+			return;
+		}
+
+		/*
+		 * Check the returned Bulk protocol status.
+		 */
+
+		bcs = &sc->work_bcs;
+		rc = le32_to_cpu(bcs->Residue);
+		if (rc != cmd->len - cmd->act_len) {
+			/*
+			 * It is all right to transfer less, the caller has
+			 * to check. But it's not all right if the device
+			 * counts disagree with our counts.
+			 */
+			/* P3 */ printk("ub: resid %d len %d act %d\n",
+			    rc, cmd->len, cmd->act_len);
+			goto Bad_End;
+		}
+
+		if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) &&
+		    bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) {
+			/* XXX Rate-limit, even for P3 tagged */
+			/* P3 */ printk("ub: signature 0x%x\n", bcs->Signature);
+			/* Windows ignores signatures, so do we. */
+		}
+
+		if (bcs->Tag != cmd->tag) {
+			/* P3 */ printk("ub: tag orig 0x%x reply 0x%x\n",
+			    cmd->tag, bcs->Tag);
+			goto Bad_End;
+		}
+
+		switch (bcs->Status) {
+		case US_BULK_STAT_OK:
+			break;
+		case US_BULK_STAT_FAIL:
+			ub_state_sense(sc, cmd);
+			return;
+		case US_BULK_STAT_PHASE:
+			/* XXX We must reset the transport here */
+			/* P3 */ printk("ub: status PHASE\n");
+			goto Bad_End;
+		default:
+			printk(KERN_INFO "%s: unknown CSW status 0x%x\n",
+			    sc->name, bcs->Status);
+			goto Bad_End;
+		}
+
+		cmd->state = UB_CMDST_DONE;
+		ub_cmdtr_state(sc, cmd);
+		if (cmd != &sc->top_rqs_cmd) {	/* XXX This is getting out of hand. */
+			sc->busy = 0;
+			sc->top_cmd = NULL;
+		}
+		(*cmd->done)(sc, cmd);
+
+	} else if (cmd->state == UB_CMDST_SENSE) {
+		/* 
+		 * We do not look at sense, because even if there was no sense,
+		 * we get into UB_CMDST_SENSE from a STALL or CSW FAIL only.
+		 * We request sense because we want to clear CHECK CONDITION
+		 * on devices with delusions of SCSI, and not because we
+		 * are curious in any way about the sense itself.
+		 */
+		/* if ((cmd->top_sense[2] & 0x0F) == NO_SENSE) { foo } */
+
+		cmd->error = -EIO;
+		cmd->state = UB_CMDST_DONE;
+		ub_cmdtr_state(sc, cmd);
+		if (cmd != &sc->top_rqs_cmd) {	/* XXX This is getting out of hand. */
+			sc->busy = 0;
+			sc->top_cmd = NULL;
+		}
+		(*cmd->done)(sc, cmd);
+	} else {
+		printk(KERN_WARNING "%s: "
+		    "wrong command state %d on device %u\n",
+		    sc->name, cmd->state, sc->dev->devnum);
+		goto Bad_End;
+	}
+	return;
+
+Bad_End: /* Little Excel is dead */
+	cmd->error = -EIO;
+	cmd->state = UB_CMDST_DONE;
+	ub_cmdtr_state(sc, cmd);
+	if (cmd != &sc->top_rqs_cmd) {	/* XXX This is getting out of hand. */
+		sc->busy = 0;
+		sc->top_cmd = NULL;
+	}
+	(*cmd->done)(sc, cmd);
+}
+
+/*
+ * Factorization helper for the command state machine:
+ * Submit a CSW read and go to STAT state.
+ */
+static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	int rc;
+
+	sc->last_pipe = sc->recv_bulk_pipe;
+	usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->recv_bulk_pipe,
+	    &sc->work_bcs, US_BULK_CS_WRAP_LEN,
+	    ub_scsi_urb_complete, sc);
+	sc->work_urb.timeout = UB_URB_TIMEOUT;
+	sc->work_urb.actual_length = 0;
+	sc->work_urb.error_count = 0;
+	sc->work_urb.status = 0;
+
+	if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
+
+		/* XXX Clear stalls */
+		printk("ub: CSW #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
+
+		cmd->error = rc;
+		cmd->state = UB_CMDST_DONE;
+		ub_cmdtr_state(sc, cmd);
+		if (cmd != &sc->top_rqs_cmd) {	/* XXX This is getting out of hand. */
+			sc->busy = 0;
+			sc->top_cmd = NULL;
+		}
+		(*cmd->done)(sc, cmd);
+		return;
+	}
+
+	cmd->stat_count = 0;
+	cmd->state = UB_CMDST_STAT;
+	ub_cmdtr_state(sc, cmd);
+}
+
+/*
+ * Factorization helper for the command state machine:
+ * Submit a REQUEST SENSE and go to SENSE state.
+ */
+static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	struct ub_scsi_cmd *scmd;
+	int rc;
+
+	if (cmd->cdb[0] == REQUEST_SENSE) {
+		rc = -EPIPE;
+		goto error;
+	}
+
+	memset(&sc->top_sense, 0, UB_SENSE_SIZE);
+	scmd = &sc->top_rqs_cmd;
+	scmd->cdb[0] = REQUEST_SENSE;
+	scmd->cdb_len = 6;
+	scmd->dir = UB_DIR_READ;
+	scmd->state = UB_CMDST_INIT;
+	scmd->data = sc->top_sense;
+	scmd->len = UB_SENSE_SIZE;
+	scmd->done = ub_top_sense_done;
+	scmd->back = cmd;
+
+	scmd->tag = sc->tagcnt++;
+	if ((rc = ub_submit_scsi(sc, scmd)) != 0) {
+		printk(KERN_NOTICE "%s: "
+		    "unable to submit sense for device %u (%d)\n",
+		    sc->name, sc->dev->devnum, rc);
+		rc = -EIO;
+		goto error;
+	}
+
+	cmd->state = UB_CMDST_SENSE;
+	ub_cmdtr_state(sc, cmd);
+	return;
+
+error:
+	cmd->error = rc;
+	cmd->state = UB_CMDST_DONE;
+	ub_cmdtr_state(sc, cmd);
+	if (cmd != &sc->top_rqs_cmd) {	/* XXX This is getting out of hand. */
+		sc->busy = 0;
+		sc->top_cmd = NULL;
+	}
+	(*cmd->done)(sc, cmd);
+}
+
+/*
+ * A helper for the command's state machine:
+ * Submit a stall clear.
+ */
+static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
+    int stalled_pipe)
+{
+	int endp;
+	struct usb_ctrlrequest *cr;
+
+	endp = usb_pipeendpoint(stalled_pipe);
+	if (usb_pipein (stalled_pipe))
+		endp |= USB_DIR_IN;
+
+	cr = &sc->work_cr;
+	cr->bRequestType = USB_RECIP_ENDPOINT;
+	cr->bRequest = USB_REQ_CLEAR_FEATURE;
+	cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT);
+	cr->wIndex = cpu_to_le16(endp);
+	cr->wLength = cpu_to_le16(0);
+
+	usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
+	    (unsigned char*) cr, NULL, 0,
+	    ub_scsi_urb_complete, sc);
+	sc->work_urb.timeout = UB_CTRL_TIMEOUT;
+	sc->work_urb.actual_length = 0;
+	sc->work_urb.error_count = 0;
+	sc->work_urb.status = 0;
+
+	return usb_submit_urb(&sc->work_urb, GFP_ATOMIC);
+}
+
+/*
+ */
+static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
+{
+	unsigned char *sense = scmd->data;
+	struct ub_scsi_cmd *cmd;
+
+	ub_cmdtr_sense(sc, scmd, sense);
+
+	if (!sc->busy) {	/* P3 */
+		printk(KERN_WARNING "%s: sense done while idle\n", sc->name);
+		return;
+	}
+	cmd = scmd->back;
+	if (cmd->state != UB_CMDST_SENSE) {
+		printk(KERN_WARNING "%s: "
+		    "sense done with bad cmd state %d on device %u\n",
+		    sc->name, cmd->state, sc->dev->devnum);
+		return;
+	}
+
+	ub_scsi_urb_compl(sc, cmd);
+}
+
+#if 0
+/* Determine what the maximum LUN supported is */
+int usb_stor_Bulk_max_lun(struct us_data *us)
+{
+	int result;
+
+	/* issue the command */
+	result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
+				 US_BULK_GET_MAX_LUN, 
+				 USB_DIR_IN | USB_TYPE_CLASS | 
+				 USB_RECIP_INTERFACE,
+				 0, us->ifnum, us->iobuf, 1, HZ);
+
+	/* 
+	 * Some devices (i.e. Iomega Zip100) need this -- apparently
+	 * the bulk pipes get STALLed when the GetMaxLUN request is
+	 * processed.   This is, in theory, harmless to all other devices
+	 * (regardless of if they stall or not).
+	 */
+	if (result < 0) {
+		usb_stor_clear_halt(us, us->recv_bulk_pipe);
+		usb_stor_clear_halt(us, us->send_bulk_pipe);
+	}
+
+	US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", 
+		  result, us->iobuf[0]);
+
+	/* if we have a successful request, return the result */
+	if (result == 1)
+		return us->iobuf[0];
+
+	/* return the default -- no LUNs */
+	return 0;
+}
+#endif
+
+/*
+ * This is called from a process context.
+ */
+static void ub_revalidate(struct ub_dev *sc)
+{
+
+	sc->capacity.nsec = 0;
+	sc->capacity.bsize = 512;
+	sc->capacity.bshift = 0;
+
+	if (ub_sync_tur(sc) != 0)
+		return;			/* Not ready */
+	sc->changed = 0;
+
+	if (ub_sync_read_cap(sc, &sc->capacity) != 0) {
+		/*
+		 * The retry here means something is wrong, either with the
+		 * device, with the transport, or with our code.
+		 * We keep this because sd.c has retries for capacity.
+		 */
+		if (ub_sync_read_cap(sc, &sc->capacity) != 0) {
+			sc->capacity.nsec = 100;
+			sc->capacity.bsize = 512;
+			sc->capacity.bshift = 0;
+		}
+	}
+
+	/* XXX Set these to some reasonable values. */
+	sc->readonly = 0;
+	sc->removable = 1;
+}
+
+/*
+ * The open funcion.
+ * This is mostly needed to keep refcounting, but also to support
+ * media checks on removable media drives.
+ */
+static int ub_bd_open(struct inode *inode, struct file *filp)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct ub_dev *sc;
+	unsigned long flags;
+	int rc;
+
+	if ((sc = disk->private_data) == NULL)
+		return -ENXIO;
+	spin_lock_irqsave(&ub_lock, flags);
+	if (atomic_read(&sc->poison)) {
+		spin_unlock_irqrestore(&ub_lock, flags);
+		return -ENXIO;
+	}
+	sc->openc++;
+	spin_unlock_irqrestore(&ub_lock, flags);
+
+	if (sc->removable || sc->readonly)
+		check_disk_change(inode->i_bdev);
+
+	/* XXX sd.c and floppy.c bail on open if media is not present. */
+
+	if (sc->readonly && (filp->f_mode & FMODE_WRITE)) {
+		rc = -EROFS;
+		goto err_open;
+	}
+
+	return 0;
+
+err_open:
+	spin_lock_irqsave(&ub_lock, flags);
+	--sc->openc;
+	if (sc->openc == 0 && atomic_read(&sc->poison))
+		ub_cleanup(sc);
+	spin_unlock_irqrestore(&ub_lock, flags);
+	return rc;
+}
+
+/*
+ */
+static int ub_bd_release(struct inode *inode, struct file *filp)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct ub_dev *sc = disk->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ub_lock, flags);
+	--sc->openc;
+	if (sc->openc == 0 && atomic_read(&sc->poison))
+		ub_cleanup(sc);
+	spin_unlock_irqrestore(&ub_lock, flags);
+	return 0;
+}
+
+/*
+ * The ioctl interface.
+ */
+static int ub_bd_ioctl(struct inode *inode, struct file *filp,
+    unsigned int cmd, unsigned long arg)
+{
+// void __user *usermem = (void *) arg;
+// struct carm_port *port = ino->i_bdev->bd_disk->private_data;
+// struct hd_geometry geom;
+
+#if 0
+	switch (cmd) {
+	case HDIO_GETGEO:
+		if (usermem == NULL)		// XXX Bizzare. Why?
+			return -EINVAL;
+
+		geom.heads = (u8) port->dev_geom_head;
+		geom.sectors = (u8) port->dev_geom_sect;
+		geom.cylinders = port->dev_geom_cyl;
+		geom.start = get_start_sect(ino->i_bdev);
+
+		if (copy_to_user(usermem, &geom, sizeof(geom)))
+			return -EFAULT;
+		return 0;
+
+	default: ;
+	}
+#endif
+
+	return -ENOTTY;
+}
+
+/*
+ * This is called once a new disk was seen by the block layer or by ub_probe().
+ * The main onjective here is to discover the features of the media such as
+ * the capacity, read-only status, etc. USB storage generally does not
+ * need to be spun up, but if we needed it, this would be the place.
+ *
+ * This call can sleep.
+ *
+ * The return code is not used.
+ */
+static int ub_bd_revalidate(struct gendisk *disk)
+{
+	struct ub_dev *sc = disk->private_data;
+
+	ub_revalidate(sc);
+	/* This is pretty much a long term P3 */
+	printk(KERN_INFO "%s: device %u capacity nsec %ld bsize %u\n",
+	    sc->name, sc->dev->devnum, sc->capacity.nsec, sc->capacity.bsize);
+
+	set_capacity(disk, sc->capacity.nsec);
+	// set_disk_ro(sdkp->disk, sc->readonly);
+	return 0;
+}
+
+/*
+ * The check is called by the block layer to verify if the media
+ * is still available. It is supposed to be harmless, lightweight and
+ * non-intrusive in case the media was not changed.
+ *
+ * This call can sleep.
+ *
+ * The return code is bool!
+ */
+static int ub_bd_media_changed(struct gendisk *disk)
+{
+	struct ub_dev *sc = disk->private_data;
+
+	if (!sc->removable)
+		return 0;
+
+	/*
+	 * We clean checks always after every command, so this is not
+	 * as dangerous as it looks. If the TEST_UNIT_READY fails here,
+	 * the device is actually not ready with operator or software
+	 * intervention required. One dangerous item might be a drive which
+	 * spins itself down, and come the time to write dirty pages, this
+	 * will fail, then block layer discards the data. Since we never
+	 * spin drives up, such devices simply cannot be used with ub anyway.
+	 */
+	if (ub_sync_tur(sc) != 0) {
+		sc->changed = 1;
+		/* P3 */ printk("%s: made changed\n", sc->name);
+		return 1;
+	}
+
+	/* The sd.c clears this before returning (one-shot flag). Why? */
+	/* P3 */ printk("%s: %s changed\n", sc->name,
+	    sc->changed? "is": "was not");
+	return sc->changed;
+}
+
+static struct block_device_operations ub_bd_fops = {
+	.owner		= THIS_MODULE,
+	.open		= ub_bd_open,
+	.release	= ub_bd_release,
+	.ioctl		= ub_bd_ioctl,
+	.media_changed	= ub_bd_media_changed,
+	.revalidate_disk = ub_bd_revalidate,
+};
+
+/*
+ * Common ->done routine for commands executed synchronously.
+ */
+static void ub_probe_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
+{
+	struct completion *cop = cmd->back;
+	complete(cop);
+}
+
+/*
+ * Test if the device has a check condition on it, synchronously.
+ */
+static int ub_sync_tur(struct ub_dev *sc)
+{
+	struct ub_scsi_cmd *cmd;
+	enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) };
+	unsigned long flags;
+	struct completion compl;
+	int rc;
+
+	init_completion(&compl);
+
+	rc = -ENOMEM;
+	if ((cmd = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
+		goto err_alloc;
+	memset(cmd, 0, ALLOC_SIZE);
+
+	cmd->cdb[0] = TEST_UNIT_READY;
+	cmd->cdb_len = 6;
+	cmd->dir = UB_DIR_NONE;
+	cmd->state = UB_CMDST_INIT;
+	cmd->done = ub_probe_done;
+	cmd->back = &compl;
+
+	spin_lock_irqsave(&sc->lock, flags);
+	cmd->tag = sc->tagcnt++;
+
+	rc = ub_submit_scsi(sc, cmd);
+	spin_unlock_irqrestore(&sc->lock, flags);
+
+	if (rc != 0) {
+		printk("ub: testing ready: submit error (%d)\n", rc); /* P3 */
+		goto err_submit;
+	}
+
+	wait_for_completion(&compl);
+
+	rc = cmd->error;
+
+err_submit:
+	kfree(cmd);
+err_alloc:
+	return rc;
+}
+
+/*
+ * Read the SCSI capacity synchronously (for probing).
+ */
+static int ub_sync_read_cap(struct ub_dev *sc, struct ub_capacity *ret)
+{
+	struct ub_scsi_cmd *cmd;
+	char *p;
+	enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) + 8 };
+	unsigned long flags;
+	unsigned int bsize, shift;
+	unsigned long nsec;
+	struct completion compl;
+	int rc;
+
+	init_completion(&compl);
+
+	rc = -ENOMEM;
+	if ((cmd = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
+		goto err_alloc;
+	memset(cmd, 0, ALLOC_SIZE);
+	p = (char *)cmd + sizeof(struct ub_scsi_cmd);
+
+	cmd->cdb[0] = 0x25;
+	cmd->cdb_len = 10;
+	cmd->dir = UB_DIR_READ;
+	cmd->state = UB_CMDST_INIT;
+	cmd->data = p;
+	cmd->len = 8;
+	cmd->done = ub_probe_done;
+	cmd->back = &compl;
+
+	spin_lock_irqsave(&sc->lock, flags);
+	cmd->tag = sc->tagcnt++;
+
+	rc = ub_submit_scsi(sc, cmd);
+	spin_unlock_irqrestore(&sc->lock, flags);
+
+	if (rc != 0) {
+		printk("ub: reading capacity: submit error (%d)\n", rc); /* P3 */
+		goto err_submit;
+	}
+
+	wait_for_completion(&compl);
+
+	if (cmd->error != 0) {
+		printk("ub: reading capacity: error %d\n", cmd->error); /* P3 */
+		rc = -EIO;
+		goto err_read;
+	}
+	if (cmd->act_len != 8) {
+		printk("ub: reading capacity: size %d\n", cmd->act_len); /* P3 */
+		rc = -EIO;
+		goto err_read;
+	}
+
+	/* sd.c special-cases sector size of 0 to mean 512. Needed? Safe? */
+	nsec = be32_to_cpu(*(u32 *)p) + 1;
+	bsize = be32_to_cpu(*(u32 *)(p + 4));
+	switch (bsize) {
+	case 512:	shift = 0;	break;
+	case 1024:	shift = 1;	break;
+	case 2048:	shift = 2;	break;
+	case 4096:	shift = 3;	break;
+	default:
+		printk("ub: Bad sector size %u\n", bsize); /* P3 */
+		rc = -EDOM;
+		goto err_inv_bsize;
+	}
+
+	ret->bsize = bsize;
+	ret->bshift = shift;
+	ret->nsec = nsec << shift;
+	rc = 0;
+
+err_inv_bsize:
+err_read:
+err_submit:
+	kfree(cmd);
+err_alloc:
+	return rc;
+}
+
+/*
+ */
+static void ub_probe_urb_complete(struct urb *urb, struct pt_regs *pt)
+{
+	struct completion *cop = urb->context;
+	complete(cop);
+}
+
+/*
+ * Clear initial stalls.
+ */
+static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe)
+{
+	int endp;
+	struct usb_ctrlrequest *cr;
+	struct completion compl;
+	int rc;
+
+	init_completion(&compl);
+
+	endp = usb_pipeendpoint(stalled_pipe);
+	if (usb_pipein (stalled_pipe))
+		endp |= USB_DIR_IN;
+
+	cr = &sc->work_cr;
+	cr->bRequestType = USB_RECIP_ENDPOINT;
+	cr->bRequest = USB_REQ_CLEAR_FEATURE;
+	cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT);
+	cr->wIndex = cpu_to_le16(endp);
+	cr->wLength = cpu_to_le16(0);
+
+	usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
+	    (unsigned char*) cr, NULL, 0,
+	    ub_probe_urb_complete, &compl);
+	sc->work_urb.timeout = UB_CTRL_TIMEOUT;
+	sc->work_urb.actual_length = 0;
+	sc->work_urb.error_count = 0;
+	sc->work_urb.status = 0;
+
+	if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
+		printk(KERN_WARNING
+		     "%s: Unable to submit a probe clear (%d)\n", sc->name, rc);
+		return rc;
+	}
+
+	wait_for_completion(&compl);
+
+	/* reset the toggles and endpoint flags */
+	usb_endpoint_running(sc->dev, endp, usb_pipeout(sc->last_pipe));
+	usb_settoggle(sc->dev, endp, usb_pipeout(sc->last_pipe), 0);
+
+	return 0;
+}
+
+/*
+ * Get the pipe settings.
+ */
+static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev,
+    struct usb_interface *intf)
+{
+	struct usb_host_interface *altsetting = intf->cur_altsetting;
+	struct usb_endpoint_descriptor *ep_in = NULL;
+	struct usb_endpoint_descriptor *ep_out = NULL;
+	struct usb_endpoint_descriptor *ep;
+	int i;
+
+	/*
+	 * Find the endpoints we need.
+	 * We are expecting a minimum of 2 endpoints - in and out (bulk).
+	 * We will ignore any others.
+	 */
+	for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
+		ep = &altsetting->endpoint[i].desc;
+
+		/* Is it a BULK endpoint? */
+		if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				== USB_ENDPOINT_XFER_BULK) {
+			/* BULK in or out? */
+			if (ep->bEndpointAddress & USB_DIR_IN)
+				ep_in = ep;
+			else
+				ep_out = ep;
+		}
+	}
+
+	if (ep_in == NULL || ep_out == NULL) {
+		printk(KERN_NOTICE "%s: device %u failed endpoint check\n",
+		    sc->name, sc->dev->devnum);
+		return -EIO;
+	}
+
+	/* Calculate and store the pipe values */
+	sc->send_ctrl_pipe = usb_sndctrlpipe(dev, 0);
+	sc->recv_ctrl_pipe = usb_rcvctrlpipe(dev, 0);
+	sc->send_bulk_pipe = usb_sndbulkpipe(dev,
+		ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	sc->recv_bulk_pipe = usb_rcvbulkpipe(dev, 
+		ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	return 0;
+}
+
+/*
+ * Probing is done in the process context, which allows us to cheat
+ * and not to build a state machine for the discovery.
+ */
+static int ub_probe(struct usb_interface *intf,
+    const struct usb_device_id *dev_id)
+{
+	struct ub_dev *sc;
+	request_queue_t *q;
+	struct gendisk *disk;
+	int rc;
+
+	rc = -ENOMEM;
+	if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL)
+		goto err_core;
+	memset(sc, 0, sizeof(struct ub_dev));
+	spin_lock_init(&sc->lock);
+	usb_init_urb(&sc->work_urb);
+	tasklet_init(&sc->urb_tasklet, ub_scsi_urb_action, (unsigned long)sc);
+	atomic_set(&sc->poison, 0);
+
+	rc = -ENOSR;
+	if ((sc->id = ub_id_get()) == -1)
+		goto err_id;
+	snprintf(sc->name, 8, DRV_NAME "%c", sc->id + 'a');
+
+	sc->dev = interface_to_usbdev(intf);
+	sc->intf = intf;
+	// sc->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+	usb_set_intfdata(intf, sc);
+	usb_get_dev(sc->dev);
+	// usb_get_intf(sc->intf);	/* Do we need this? */
+
+	/* XXX Verify that we can handle the device (from descriptors) */
+
+	ub_get_pipes(sc, sc->dev, intf);
+
+	if (device_create_file(&sc->intf->dev, &dev_attr_diag) != 0)
+		goto err_diag;
+
+	/*
+	 * At this point, all USB initialization is done, do upper layer.
+	 * We really hate halfway initialized structures, so from the
+	 * invariants perspective, this ub_dev is fully constructed at
+	 * this point.
+	 */
+
+	/*
+	 * This is needed to clear toggles. It is a problem only if we do
+	 * `rmmod ub && modprobe ub` without disconnects, but we like that.
+	 */
+	ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
+	ub_probe_clear_stall(sc, sc->send_bulk_pipe);
+
+	/*
+	 * The way this is used by the startup code is a little specific.
+	 * A SCSI check causes a USB stall. Our common case code sees it
+	 * and clears the check, after which the device is ready for use.
+	 * But if a check was not present, any command other than
+	 * TEST_UNIT_READY ends with a lockup (including REQUEST_SENSE).
+	 *
+	 * If we neglect to clear the SCSI check, the first real command fails
+	 * (which is the capacity readout). We clear that and retry, but why
+	 * causing spurious retries for no reason.
+	 *
+	 * Revalidation may start with its own TEST_UNIT_READY, but that one
+	 * has to succeed, so we clear checks with an additional one here.
+	 * In any case it's not our business how revaliadation is implemented.
+	 */
+	ub_sync_tur(sc);
+
+	ub_revalidate(sc);
+	/* This is pretty much a long term P3 */
+	printk(KERN_INFO "%s: device %u capacity nsec %ld bsize %u\n",
+	    sc->name, sc->dev->devnum, sc->capacity.nsec, sc->capacity.bsize);
+
+	/*
+	 * Just one disk per sc currently, but maybe more.
+	 */
+	rc = -ENOMEM;
+	if ((disk = alloc_disk(UB_MINORS_PER_MAJOR)) == NULL)
+		goto err_diskalloc;
+
+	sc->disk = disk;
+	sprintf(disk->disk_name, DRV_NAME "%c", sc->id + 'a');
+	sprintf(disk->devfs_name, DEVFS_NAME "/%c", sc->id + 'a');
+	disk->major = UB_MAJOR;
+	disk->first_minor = sc->id * UB_MINORS_PER_MAJOR;
+	disk->fops = &ub_bd_fops;
+	disk->private_data = sc;
+
+	rc = -ENOMEM;
+	if ((q = blk_init_queue(ub_bd_rq_fn, &sc->lock)) == NULL)
+		goto err_blkqinit;
+
+	disk->queue = q;
+
+        // blk_queue_bounce_limit(q, hba[i]->pdev->dma_mask);
+	blk_queue_max_hw_segments(q, UB_MAX_REQ_SG);
+	blk_queue_max_phys_segments(q, UB_MAX_REQ_SG);
+	// blk_queue_segment_boundary(q, CARM_SG_BOUNDARY);
+	blk_queue_max_sectors(q, UB_MAX_SECTORS);
+	// blk_queue_hardsect_size(q, xxxxx);
+
+	/*
+	 * This is a serious infraction, caused by a deficiency in the
+	 * USB sg interface (usb_sg_wait()). We plan to remove this once
+	 * we get mileage on the driver and can justify a change to USB API.
+	 * See blk_queue_bounce_limit() to understand this part.
+	 *
+	 * XXX And I still need to be aware of the DMA mask in the HC.
+	 */
+	q->bounce_pfn = blk_max_low_pfn;
+	q->bounce_gfp = GFP_NOIO;
+
+	q->queuedata = sc;
+
+	set_capacity(disk, sc->capacity.nsec);
+	if (sc->removable)
+		disk->flags |= GENHD_FL_REMOVABLE;
+
+	add_disk(disk);
+
+	return 0;
+
+err_blkqinit:
+	put_disk(disk);
+err_diskalloc:
+	device_remove_file(&sc->intf->dev, &dev_attr_diag);
+err_diag:
+	usb_set_intfdata(intf, NULL);
+	// usb_put_intf(sc->intf);
+	usb_put_dev(sc->dev);
+	spin_lock_irq(&ub_lock);
+	ub_id_put(sc->id);
+	spin_unlock_irq(&ub_lock);
+err_id:
+	kfree(sc);
+err_core:
+	return rc;
+}
+
+static void ub_disconnect(struct usb_interface *intf)
+{
+	struct ub_dev *sc = usb_get_intfdata(intf);
+	struct gendisk *disk = sc->disk;
+	request_queue_t *q = disk->queue;
+	unsigned long flags;
+
+	/*
+	 * Fence stall clearnings, operations triggered by unlinkings and so on.
+	 * We do not attempt to unlink any URBs, because we do not trust the
+	 * unlink paths in HC drivers. Also, we get -84 upon disconnect anyway.
+	 */
+	atomic_set(&sc->poison, 1);
+
+	/*
+	 * Unregister the upper layer, this waits for all commands to end.
+	 */
+	if (disk->flags & GENHD_FL_UP)
+		del_gendisk(disk);
+	if (q)
+		blk_cleanup_queue(q);
+
+	/*
+	 * If we zero disk->private_data BEFORE put_disk, we have to check
+	 * for NULL all over the place in open, release, check_media and
+	 * revalidate, because the block level semaphore is well inside the
+	 * put_disk. But we cannot zero after the call, because *disk is gone.
+	 * The sd.c is blatantly racy in this area.
+	 */
+	/* disk->private_data = NULL; */
+	put_disk(disk);
+	sc->disk = NULL;
+
+	/*
+	 * We really expect blk_cleanup_queue() to wait, so no amount
+	 * of paranoya is too much.
+	 *
+	 * Taking a lock on a structure which is about to be freed
+	 * is very nonsensual. Here it is largely a way to do a debug freeze,
+	 * and a bracket which shows where the nonsensual code segment ends.
+	 *
+	 * Testing for -EINPROGRESS is always a bug, so we are bending
+	 * the rules a little.
+	 */
+	spin_lock_irqsave(&sc->lock, flags);
+	if (sc->busy) {
+		printk(KERN_WARNING "%s: "
+		    "command is active after disconnect, status %d\n",
+		    sc->name, sc->top_cmd->state);
+	}
+	if (sc->work_urb.status == -EINPROGRESS) {	/* janitors: ignore */
+		printk(KERN_WARNING "%s: "
+		    "URB is active after disconnect\n", sc->name);
+	}
+	spin_unlock_irqrestore(&sc->lock, flags);
+
+	/*
+	 * At this point there must be no commands coming from anyone
+	 * and no URBs left in transit.
+	 */
+
+	device_remove_file(&sc->intf->dev, &dev_attr_diag);
+	usb_set_intfdata(intf, NULL);
+	// usb_put_intf(sc->intf);
+	sc->intf = NULL;
+	usb_put_dev(sc->dev);
+	sc->dev = NULL;
+
+	spin_lock_irqsave(&ub_lock, flags);
+	if (sc->openc == 0)
+		ub_cleanup(sc);
+	spin_unlock_irqrestore(&ub_lock, flags);
+}
+
+struct usb_driver ub_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"ub",
+	.probe =	ub_probe,
+	.disconnect =	ub_disconnect,
+	.id_table =	ub_usb_ids,
+};
+
+static int __init ub_init(void)
+{
+	int rc;
+
+	/* P3 */ printk("ub: sizeof ub_scsi_cmd %u ub_dev %u\n",
+			sizeof(struct ub_scsi_cmd), sizeof(struct ub_dev));
+
+	if ((rc = register_blkdev(UB_MAJOR, DRV_NAME)) != 0)
+		goto err_regblkdev;
+	devfs_mk_dir(DEVFS_NAME);
+
+	if ((rc = usb_register(&ub_driver)) != 0)
+		goto err_register;
+
+	return 0;
+
+err_register:
+	devfs_remove(DEVFS_NAME);
+	unregister_blkdev(UB_MAJOR, DRV_NAME);
+err_regblkdev:
+	return rc;
+}
+
+static void __exit ub_exit(void)
+{
+	usb_deregister(&ub_driver);
+
+	devfs_remove(DEVFS_NAME);
+	unregister_blkdev(UB_MAJOR, DRV_NAME);
+}
+
+module_init(ub_init);
+module_exit(ub_exit);
+
+MODULE_LICENSE("GPL");
diff -urpN -X dontdiff linux-2.6.7/drivers/usb/storage/unusual_devs.h linux-2.6.7-ub/drivers/usb/storage/unusual_devs.h
--- linux-2.6.7/drivers/usb/storage/unusual_devs.h	2004-06-16 16:53:59.000000000 -0700
+++ linux-2.6.7-ub/drivers/usb/storage/unusual_devs.h	2004-07-26 00:00:21.000000000 -0700
@@ -486,11 +486,13 @@ UNUSUAL_DEV(  0x0781, 0x0001, 0x0200, 0x
 		US_SC_SCSI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN ),
 
+#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE)
 UNUSUAL_DEV(  0x0781, 0x0002, 0x0009, 0x0009, 
 		"Sandisk",
 		"ImageMate SDDR-31",
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_IGNORE_SER ),
+#endif
 
 UNUSUAL_DEV(  0x0781, 0x0100, 0x0100, 0x0100,
 		"Sandisk",
diff -urpN -X dontdiff linux-2.6.7/drivers/usb/storage/usb.c linux-2.6.7-ub/drivers/usb/storage/usb.c
--- linux-2.6.7/drivers/usb/storage/usb.c	2004-06-16 16:53:59.000000000 -0700
+++ linux-2.6.7-ub/drivers/usb/storage/usb.c	2004-07-26 00:00:21.000000000 -0700
@@ -133,7 +133,9 @@ static struct usb_device_id storage_usb_
 	{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) },
 	{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) },
 	{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) },
+#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE)
 	{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
+#endif
 
 	/* Terminating entry */
 	{ }
@@ -207,8 +209,10 @@ static struct us_unusual_dev us_unusual_
 	  .useTransport = US_PR_BULK},
 	{ .useProtocol = US_SC_8070,
 	  .useTransport = US_PR_BULK},
+#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE)
 	{ .useProtocol = US_SC_SCSI,
 	  .useTransport = US_PR_BULK},
+#endif
 
 	/* Terminating entry */
 	{ 0 }
diff -urpN -X dontdiff linux-2.6.7/include/linux/major.h linux-2.6.7-ub/include/linux/major.h
--- linux-2.6.7/include/linux/major.h	2004-04-21 12:01:24.000000000 -0700
+++ linux-2.6.7-ub/include/linux/major.h	2004-07-26 00:00:21.000000000 -0700
@@ -165,4 +165,6 @@
 
 #define VIOTAPE_MAJOR		230
 
+#define UB_MAJOR		125	/* USB Block - Experimental XXX */
+
 #endif

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [usb-storage] drivers/block/ub.c #6
  2004-07-30 10:51 drivers/block/ub.c #6 Pete Zaitcev
@ 2004-08-27 21:02 ` Pat LaVarre
  2004-08-27 22:32   ` Pat LaVarre
  0 siblings, 1 reply; 4+ messages in thread
From: Pat LaVarre @ 2004-08-27 21:02 UTC (permalink / raw)
  To: Pete Zaitcev; +Cc: usb-storage, linux-kernel, jgarzik

Pete Z:

> Date: 30 Jul 2004 04:51:20 -0600
> ...
> I wish this could be somehow limited to flash keys,

I see a 3X drop in thruput, ouch, when I substitute ub.ko for
usb-storage.ko.

Specifically I get 0.5 GB/min from ub.ko, as contrasted with 1.5 GB/min
from usb-storage.ko, in the outside 1 GiB of a particular disc, in three
or more consistent consecutive samples from each.

I knew how to patch to allow bytes/LBA != 512 only because of your
recent answers in [usb-storage].  I took my samples from
linux-2.6.9-rc1-bk2, modprobe'ing .ko built with and without
CONFIG_BLK_DEV_UB.  

> I did not ask LANANA for a major.
> next ...
> add ioctls to burn CDs, and ...

Yes please.

Even DVD-RAM need the ioctl's to pass thru SCSI, for everything
vendor-specific that lies beyond generic read and write.

> dynamic ... udev ...
> ufi.c ... refactoring ...

Pat LaVarre



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [usb-storage] drivers/block/ub.c #6
  2004-08-27 21:02 ` [usb-storage] " Pat LaVarre
@ 2004-08-27 22:32   ` Pat LaVarre
  2004-08-28  3:57     ` Pete Zaitcev
  0 siblings, 1 reply; 4+ messages in thread
From: Pat LaVarre @ 2004-08-27 22:32 UTC (permalink / raw)
  To: Pete Zaitcev; +Cc: usb-storage, linux-kernel, jgarzik

Pete Z:

> > Date: 30 Jul 2004 04:51:20 -0600
> > ...
> > I wish this could be somehow limited to flash keys,

How committed are we to the "Low Performance" part of the "Low
Performance USB Block driver" title for "config BLK_DEV_UB" found in
drivers/block/Kconfig?

Do we instead hope for ub.ko to simplify all "generic USB Mass Storage",
leaving only the vendor-specific work to usb-storage.ko?

I ask because I haven't yet learned how to reconcile two source files:

--- drivers/usb/storage/usb.c
...
static struct usb_device_id storage_usb_ids [] = {
...
#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE)
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
#endif
...
--- C:/Windows/inf/usbstor.inf:
...
[Generic]
%GenericBulkOnly.DeviceDesc%=USBSTOR_BULK, USB\Class_08&SubClass_02&Prot_50
%GenericBulkOnly.DeviceDesc%=USBSTOR_BULK, USB\Class_08&SubClass_05&Prot_50
%GenericBulkOnly.DeviceDesc%=USBSTOR_BULK, USB\Class_08&SubClass_06&Prot_50
...
---

Looks to me like we have ub.ko taking over just x 08 06 50, rather than
all of the x 08 (06|05|02) 50 = bInterfaceClass ...SubClass ...Protocol
considered generic by MSFT?

http://www.microsoft.com/whdc/device/storage/usbfaq.mspx
agrees Flash should be x 08 06 50 but gives no clear guidance to the
rest of us.

I remember we invented x 08 06 50 to be the one tuple to rule them all,
to move the determination of PDT (peripheral device type) etc. back into
op x12 "INQUIRY" where it belongs, ...

Of course the world may yet contain advocates of connecting HDD/FDD as
bInterfaceSubClass = x05 "SFF 8070" = Compaq LS-120 or connecting DVD/CD
as x02 "SFF 8020" = read-only CD.

Pat LaVarre
http://linux-pel.blog-city.com/read/790489.htm


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [usb-storage] drivers/block/ub.c #6
  2004-08-27 22:32   ` Pat LaVarre
@ 2004-08-28  3:57     ` Pete Zaitcev
  0 siblings, 0 replies; 4+ messages in thread
From: Pete Zaitcev @ 2004-08-28  3:57 UTC (permalink / raw)
  To: Pat LaVarre; +Cc: usb-storage, linux-kernel, jgarzik

On 27 Aug 2004 16:32:39 -0600
Pat LaVarre <p.lavarre@ieee.org> wrote:

> Looks to me like we have ub.ko taking over just x 08 06 50, rather than
> all of the x 08 (06|05|02) 50 = bInterfaceClass ...SubClass ...Protocol
> considered generic by MSFT?
> 
> http://www.microsoft.com/whdc/device/storage/usbfaq.mspx
> agrees Flash should be x 08 06 50 but gives no clear guidance to the
> rest of us.
> 
> I remember we invented x 08 06 50 to be the one tuple to rule them all,
> to move the determination of PDT (peripheral device type) etc. back into
> op x12 "INQUIRY" where it belongs, ...

I'll look at non-bulk once we have something useable by common folks
and Fedora ships ub, but not before. All my devices use Bulk. But also,
UFI has to come first. Only then, perhaps, I'll look at CB and CBI.

> Of course the world may yet contain advocates of connecting HDD/FDD as
> bInterfaceSubClass = x05 "SFF 8070" = Compaq LS-120 or connecting DVD/CD
> as x02 "SFF 8020" = read-only CD.

I don't have any plans for 8070 and 8020i. I promise to look at patches
if someone submits any. However, in my experience, 8070 devices are fickle.
They may be better off left to usb-storage forever, with its richer
infrastructure.

-- Pete

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2004-08-28  4:02 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-07-30 10:51 drivers/block/ub.c #6 Pete Zaitcev
2004-08-27 21:02 ` [usb-storage] " Pat LaVarre
2004-08-27 22:32   ` Pat LaVarre
2004-08-28  3:57     ` Pete Zaitcev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome