Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/CREDITS                             |    5 
 25-akpm/Documentation/usb/sn9c102.txt       |   13 
 25-akpm/MAINTAINERS                         |    6 
 25-akpm/drivers/block/ub.c                  |  181 -
 25-akpm/drivers/usb/Kconfig                 |    2 
 25-akpm/drivers/usb/Makefile                |    2 
 25-akpm/drivers/usb/class/cdc-acm.c         |   84 
 25-akpm/drivers/usb/class/cdc-acm.h         |   49 
 25-akpm/drivers/usb/core/hcd.c              |  166 +
 25-akpm/drivers/usb/core/hcd.h              |   60 
 25-akpm/drivers/usb/core/hub.c              |   56 
 25-akpm/drivers/usb/core/hub.h              |    1 
 25-akpm/drivers/usb/gadget/Kconfig          |    8 
 25-akpm/drivers/usb/gadget/ether.c          |  237 --
 25-akpm/drivers/usb/gadget/net2280.c        |   25 
 25-akpm/drivers/usb/gadget/omap_udc.c       |   30 
 25-akpm/drivers/usb/gadget/rndis.c          |    2 
 25-akpm/drivers/usb/gadget/serial.c         |  154 -
 25-akpm/drivers/usb/host/Kconfig            |   29 
 25-akpm/drivers/usb/host/ohci-dbg.c         |    4 
 25-akpm/drivers/usb/host/ohci-hcd.c         |   34 
 25-akpm/drivers/usb/host/ohci-omap.c        |   98 
 25-akpm/drivers/usb/host/ohci-ppc-soc.c     |  299 ++
 25-akpm/drivers/usb/host/ohci-q.c           |    9 
 25-akpm/drivers/usb/host/ohci.h             |   48 
 25-akpm/drivers/usb/host/uhci-debug.c       |    9 
 25-akpm/drivers/usb/host/uhci-hcd.c         | 1517 -------------
 25-akpm/drivers/usb/host/uhci-q.c           | 1488 +++++++++++++
 25-akpm/drivers/usb/image/mdc800.c          |   42 
 25-akpm/drivers/usb/input/ati_remote.c      |   19 
 25-akpm/drivers/usb/input/hid-core.c        |   20 
 25-akpm/drivers/usb/input/wacom.c           |  335 ++
 25-akpm/drivers/usb/media/sn9c102.h         |    6 
 25-akpm/drivers/usb/media/sn9c102_core.c    |   52 
 25-akpm/drivers/usb/misc/Kconfig            |    2 
 25-akpm/drivers/usb/misc/Makefile           |    2 
 25-akpm/drivers/usb/misc/auerswald.c        |   19 
 25-akpm/drivers/usb/misc/sisusbvga/Kconfig  |   14 
 25-akpm/drivers/usb/misc/sisusbvga/Makefile |    6 
 25-akpm/drivers/usb/misc/sisusbvga/sisusb.c | 3144 ++++++++++++++++++++++++++++
 25-akpm/drivers/usb/misc/sisusbvga/sisusb.h |  278 ++
 25-akpm/drivers/usb/mon/Kconfig             |   22 
 25-akpm/drivers/usb/mon/Makefile            |    7 
 25-akpm/drivers/usb/mon/mon_main.c          |  377 +++
 25-akpm/drivers/usb/mon/mon_stat.c          |   74 
 25-akpm/drivers/usb/mon/mon_text.c          |  395 +++
 25-akpm/drivers/usb/mon/usb_mon.h           |   51 
 25-akpm/drivers/usb/net/Kconfig             |    4 
 25-akpm/drivers/usb/net/kaweth.c            |   13 
 25-akpm/drivers/usb/net/usbnet.c            |  571 ++++-
 25-akpm/drivers/usb/serial/cypress_m8.c     |    6 
 25-akpm/drivers/usb/serial/ftdi_sio.c       |    3 
 25-akpm/drivers/usb/serial/ftdi_sio.h       |    1 
 25-akpm/drivers/usb/serial/io_edgeport.c    |   49 
 25-akpm/drivers/usb/storage/Kconfig         |   22 
 25-akpm/drivers/usb/storage/Makefile        |    2 
 25-akpm/drivers/usb/storage/protocol.c      |   39 
 25-akpm/drivers/usb/storage/scsiglue.c      |   10 
 25-akpm/drivers/usb/storage/shuttle_usbat.c | 1258 ++++++++---
 25-akpm/drivers/usb/storage/shuttle_usbat.h |   82 
 25-akpm/drivers/usb/storage/transport.c     |   23 
 25-akpm/drivers/usb/storage/transport.h     |    5 
 25-akpm/drivers/usb/storage/unusual_devs.h  |   39 
 25-akpm/drivers/usb/storage/usb.c           |   10 
 25-akpm/drivers/usb/storage/usb.h           |    2 
 25-akpm/include/linux/usb.h                 |    4 
 25-akpm/include/linux/usb_cdc.h             |  162 +
 67 files changed, 9060 insertions(+), 2726 deletions(-)

diff -puN CREDITS~bk-usb CREDITS
--- 25/CREDITS~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/CREDITS	2005-02-09 19:12:30.000000000 -0800
@@ -826,6 +826,11 @@ E: cort@fsmlabs.com
 W: http://www.fsmlabs.com/linuxppcbk.html
 D: PowerPC
 
+N: Daniel Drake
+E: dsd@gentoo.org
+D: USBAT02 CompactFlash support in usb-storage
+S: UK
+
 N: Oleg Drokin
 E: green@ccssu.crimea.ua
 W: http://www.ccssu.crimea.ua/~green
diff -puN Documentation/usb/sn9c102.txt~bk-usb Documentation/usb/sn9c102.txt
--- 25/Documentation/usb/sn9c102.txt~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/Documentation/usb/sn9c102.txt	2005-02-09 19:12:30.000000000 -0800
@@ -210,8 +210,8 @@ There are other four entries in the dire
 SN9C10x bridge, while the other two control the sensor chip. "reg" and
 "i2c_reg" hold the values of the current register index where the following
 reading/writing operations are addressed at through "val" and "i2c_val". Their
-use is not intended for end-users. Note that "i2c_reg" and "i2c_val" won't be
-created if the sensor does not actually support the standard I2C protocol or
+use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not
+be created if the sensor does not actually support the standard I2C protocol or
 its registers are not 8-bit long. Also, remember that you must be logged in as
 root before writing to them.
 
@@ -341,15 +341,8 @@ TAS5130D1B  Taiwan Advanced Sensor Corpo
 All the available control settings of each image sensor are supported through
 the V4L2 interface.
 
-If you think your camera is based on the above hardware and is not actually
-listed in the above table, you may try to add the specific USB VendorID and
-ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h";
-then compile, load the module again and look at the kernel output.
-If this works, please send an email to the author reporting the kernel
-messages, so that a new entry in the list of supported devices can be added.
-
 Donations of new models for further testing and support would be much
-appreciated. Non-available hardware won't be supported by the author of this
+appreciated. Non-available hardware will not be supported by the author of this
 driver.
 
 
diff -puN drivers/block/ub.c~bk-usb drivers/block/ub.c
--- 25/drivers/block/ub.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/block/ub.c	2005-02-09 19:12:30.000000000 -0800
@@ -300,6 +300,11 @@ struct ub_dev {
 
 /*
  */
+static int ub_bd_rq_fn_1(struct ub_dev *sc, struct request *rq);
+static int ub_cmd_build_block(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
+    struct request *rq);
+static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
+    struct request *rq);
 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);
@@ -591,40 +596,73 @@ static struct ub_scsi_cmd *ub_cmdq_pop(s
  * The request function is our main entry point
  */
 
-static inline int ub_bd_rq_fn_1(request_queue_t *q)
+static void ub_bd_rq_fn(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
+
+	while ((rq = elv_next_request(q)) != NULL) {
+		if (ub_bd_rq_fn_1(sc, rq) != 0) {
+			blk_stop_queue(q);
+			break;
+		}
+	}
+}
+
+static int ub_bd_rq_fn_1(struct ub_dev *sc, struct request *rq)
+{
 	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;
-	}
+	if ((cmd = ub_get_cmd(sc)) == NULL)
+		return -1;
+	memset(cmd, 0, sizeof(struct ub_scsi_cmd));
 
 	blkdev_dequeue_request(rq);
 
+	if (blk_pc_request(rq)) {
+		rc = ub_cmd_build_packet(sc, cmd, rq);
+	} else {
+		rc = ub_cmd_build_block(sc, cmd, rq);
+	}
+	if (rc != 0) {
+		ub_put_cmd(sc, cmd);
+		ub_end_rq(rq, 0);
+		blk_start_queue(sc->disk->queue);
+		return 0;
+	}
+
+	cmd->state = UB_CMDST_INIT;
+	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(sc->disk->queue);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int ub_cmd_build_block(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
+    struct request *rq)
+{
+	int ub_dir;
+#if 0 /* We use rq->buffer for now */
+	struct scatterlist *sg;
+	int n_elem;
+#endif
+	unsigned int block, nblks;
+
 	if (rq_data_dir(rq) == WRITE)
 		ub_dir = UB_DIR_WRITE;
 	else
@@ -652,6 +690,7 @@ static inline int ub_bd_rq_fn_1(request_
 		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.
@@ -663,13 +702,12 @@ static inline int ub_bd_rq_fn_1(request_
 		 */
 		static int do_print = 1;
 		if (do_print) {
-			printk(KERN_WARNING "%s: unmapped request\n", sc->name);
+			printk(KERN_WARNING "%s: unmapped block request"
+			    " flags 0x%lx sectors %lu\n",
+			    sc->name, rq->flags, rq->nr_sectors);
 			do_print = 0;
 		}
-		ub_put_cmd(sc, cmd);
-		ub_end_rq(rq, 0);
-		blk_start_queue(q);
-		return 0;
+		return -1;
 	}
 
 	/*
@@ -681,7 +719,6 @@ static inline int ub_bd_rq_fn_1(request_
 	block = rq->sector >> sc->capacity.bshift;
 	nblks = rq->nr_sectors >> sc->capacity.bshift;
 
-	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;
@@ -691,27 +728,44 @@ static inline int ub_bd_rq_fn_1(request_
 	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 = rq->nr_sectors * 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)
+static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
+    struct request *rq)
 {
-	do { } while (ub_bd_rq_fn_1(q) == 0);
+
+	if (rq->data_len != 0 && rq->data == NULL) {
+		static int do_print = 1;
+		if (do_print) {
+			printk(KERN_WARNING "%s: unmapped packet request"
+			    " flags 0x%lx length %d\n",
+			    sc->name, rq->flags, rq->data_len);
+			do_print = 0;
+		}
+		return -1;
+	}
+
+	memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
+	cmd->cdb_len = rq->cmd_len;
+
+	if (rq->data_len == 0) {
+		cmd->dir = UB_DIR_NONE;
+	} else {
+		if (rq_data_dir(rq) == WRITE)
+			cmd->dir = UB_DIR_WRITE;
+		else
+			cmd->dir = UB_DIR_READ;
+	}
+	cmd->data = rq->data;
+	cmd->len = rq->data_len;
+
+	return 0;
 }
 
 static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
@@ -721,6 +775,12 @@ static void ub_rw_cmd_done(struct ub_dev
 	request_queue_t *q = disk->queue;
 	int uptodate;
 
+	if (blk_pc_request(rq)) {
+		/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
+		memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
+		rq->sense_len = UB_SENSE_SIZE;
+	}
+
 	if (cmd->error == 0)
 		uptodate = 1;
 	else
@@ -779,6 +839,17 @@ static int ub_scsi_cmd_start(struct ub_d
 
 	bcb = &sc->work_bcb;
 
+	/*
+	 * ``If the allocation length is eighteen or greater, and a device
+	 * server returns less than eithteen bytes of data, the application
+	 * client should assume that the bytes not transferred would have been
+	 * zeroes had the device server returned those bytes.''
+	 *
+	 * We zero sense for all commands so that when a packet request
+	 * fails it does not return a stale sense.
+	 */
+	memset(&sc->top_sense, 0, UB_SENSE_SIZE);
+
 	/* set up the command wrapper */
 	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
 	bcb->Tag = cmd->tag;		/* Endianness is not important */
@@ -1222,14 +1293,6 @@ static void ub_state_sense(struct ub_dev
 		goto error;
 	}
 
-	/*
-	 * ``If the allocation length is eighteen or greater, and a device
-	 * server returns less than eithteen bytes of data, the application
-	 * client should assume that the bytes not transferred would have been
-	 * zeroes had the device server returned those bytes.''
-	 */
-	memset(&sc->top_sense, 0, UB_SENSE_SIZE);
-
 	scmd = &sc->top_rqs_cmd;
 	scmd->cdb[0] = REQUEST_SENSE;
 	scmd->cdb[4] = UB_SENSE_SIZE;
@@ -1495,30 +1558,10 @@ static int ub_bd_release(struct inode *i
 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
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	void __user *usermem = (void __user *) arg;
 
-	return -ENOTTY;
+	return scsi_cmd_ioctl(filp, disk, cmd, usermem);
 }
 
 /*
diff -puN drivers/usb/class/cdc-acm.c~bk-usb drivers/usb/class/cdc-acm.c
--- 25/drivers/usb/class/cdc-acm.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/class/cdc-acm.c	2005-02-09 19:12:30.000000000 -0800
@@ -60,6 +60,7 @@
 #include <linux/smp_lock.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
+#include <linux/usb_cdc.h>
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
 
@@ -97,9 +98,12 @@ static int acm_ctrl_msg(struct acm *acm,
 /* devices aren't required to support these requests.
  * the cdc acm descriptor tells whether they do...
  */
-#define acm_set_control(acm, control)	acm_ctrl_msg(acm, ACM_REQ_SET_CONTROL, control, NULL, 0)
-#define acm_set_line(acm, line)		acm_ctrl_msg(acm, ACM_REQ_SET_LINE, 0, line, sizeof(struct acm_line))
-#define acm_send_break(acm, ms)		acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
+#define acm_set_control(acm, control) \
+	acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
+#define acm_set_line(acm, line) \
+	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
+#define acm_send_break(acm, ms) \
+	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
 
 /*
  * Interrupt handlers for various ACM device responses
@@ -109,7 +113,7 @@ static int acm_ctrl_msg(struct acm *acm,
 static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
 {
 	struct acm *acm = urb->context;
-	struct usb_ctrlrequest *dr = urb->transfer_buffer;
+	struct usb_cdc_notification *dr = urb->transfer_buffer;
 	unsigned char *data;
 	int newctrl;
 	int status;
@@ -133,14 +137,14 @@ static void acm_ctrl_irq(struct urb *urb
 		goto exit;
 
 	data = (unsigned char *)(dr + 1);
-	switch (dr->bRequest) {
+	switch (dr->bNotificationType) {
 
-		case ACM_IRQ_NETWORK:
+		case USB_CDC_NOTIFY_NETWORK_CONNECTION:
 
 			dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
 			break;
 
-		case ACM_IRQ_LINE_STATE:
+		case USB_CDC_NOTIFY_SERIAL_STATE:
 
 			newctrl = le16_to_cpu(get_unaligned((__le16 *) data));
 
@@ -160,8 +164,9 @@ static void acm_ctrl_irq(struct urb *urb
 			break;
 
 		default:
-			dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d",
-				dr->bRequest, dr->wIndex, dr->wLength, data[0], data[1]);
+			dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
+				dr->bNotificationType, dr->wIndex,
+				dr->wLength, data[0], data[1]);
 			break;
 	}
 exit:
@@ -485,32 +490,34 @@ static void acm_tty_set_termios(struct t
 {
 	struct acm *acm = tty->driver_data;
 	struct termios *termios = tty->termios;
-	struct acm_line newline;
+	struct usb_cdc_line_coding newline;
 	int newctrl = acm->ctrlout;
 
 	if (!ACM_READY(acm))
 		return;
 
-	newline.speed = cpu_to_le32p(acm_tty_speed +
+	newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
 		(termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
-	newline.stopbits = termios->c_cflag & CSTOPB ? 2 : 0;
-	newline.parity = termios->c_cflag & PARENB ?
+	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
+	newline.bParityType = termios->c_cflag & PARENB ?
 		(termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
-	newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
+	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
 
 	acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
 
-	if (!newline.speed) {
-		newline.speed = acm->line.speed;
+	if (!newline.dwDTERate) {
+		newline.dwDTERate = acm->line.dwDTERate;
 		newctrl &= ~ACM_CTRL_DTR;
 	} else  newctrl |=  ACM_CTRL_DTR;
 
 	if (newctrl != acm->ctrlout)
 		acm_set_control(acm, acm->ctrlout = newctrl);
 
-	if (memcmp(&acm->line, &newline, sizeof(struct acm_line))) {
-		memcpy(&acm->line, &newline, sizeof(struct acm_line));
-		dbg("set line: %d %d %d %d", newline.speed, newline.stopbits, newline.parity, newline.databits);
+	if (memcmp(&acm->line, &newline, sizeof newline)) {
+		memcpy(&acm->line, &newline, sizeof newline);
+		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
+			newline.bCharFormat, newline.bParityType,
+			newline.bDataBits);
 		acm_set_line(acm, &acm->line);
 	}
 }
@@ -522,7 +529,7 @@ static void acm_tty_set_termios(struct t
 static int acm_probe (struct usb_interface *intf,
 		      const struct usb_device_id *id)
 {
-	struct union_desc *union_header = NULL;
+	struct usb_cdc_union_desc *union_header = NULL;
 	char *buffer = intf->altsetting->extra;
 	int buflen = intf->altsetting->extralen;
 	struct usb_interface *control_interface;
@@ -573,21 +580,22 @@ static int acm_probe (struct usb_interfa
 		}
 
 		switch (buffer [2]) {
-			case CDC_UNION_TYPE: /* we've found it */
+			case USB_CDC_UNION_TYPE: /* we've found it */
 				if (union_header) {
 					err("More than one union descriptor, skipping ...");
 					goto next_desc;
 				}
-				union_header = (struct union_desc *)buffer;
+				union_header = (struct usb_cdc_union_desc *)
+							buffer;
 				break;
-			case CDC_COUNTRY_TYPE: /* maybe somehow export */
+			case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */
 				break; /* for now we ignore it */
-			case CDC_HEADER_TYPE: /* maybe check version */ 
+			case USB_CDC_HEADER_TYPE: /* maybe check version */ 
 				break; /* for now we ignore it */ 
-			case CDC_AC_MANAGEMENT_TYPE:
+			case USB_CDC_ACM_TYPE:
 				ac_management_function = buffer[3];
 				break;
-			case CDC_CALL_MANAGEMENT_TYPE:
+			case USB_CDC_CALL_MANAGEMENT_TYPE:
 				call_management_function = buffer[3];
 				call_interface_num = buffer[4];
 				if ((call_management_function & 3) != 3)
@@ -750,8 +758,8 @@ skip_normal_probe:
 
 	acm_set_control(acm, acm->ctrlout);
 
-	acm->line.speed = cpu_to_le32(9600);
-	acm->line.databits = 8;
+	acm->line.dwDTERate = cpu_to_le32(9600);
+	acm->line.bDataBits = 8;
 	acm_set_line(acm, &acm->line);
 
 	usb_driver_claim_interface(&acm_driver, data_interface, acm);
@@ -831,14 +839,20 @@ static struct usb_device_id acm_ids[] = 
 	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
 	},
 	/* control interfaces with various AT-command sets */
-	{ USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 1) },
-	{ USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 2) },
-	{ USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 3) },
-	{ USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 4) },
-	{ USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 5) },
-	{ USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 6) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_V25TER) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_PCCA101) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_GSM) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_3G	) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_CDMA) },
 
-	/* NOTE:  COMM/2/0xff is likely MSFT RNDIS ... NOT a modem!! */
+	/* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
 	{ }
 };
 
diff -puN drivers/usb/class/cdc-acm.h~bk-usb drivers/usb/class/cdc-acm.h
--- 25/drivers/usb/class/cdc-acm.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/class/cdc-acm.h	2005-02-09 19:12:30.000000000 -0800
@@ -27,24 +27,6 @@
 
 #define USB_RT_ACM		(USB_TYPE_CLASS | USB_RECIP_INTERFACE)
 
-#define ACM_REQ_COMMAND		0x00
-#define ACM_REQ_RESPONSE	0x01
-#define ACM_REQ_SET_FEATURE	0x02
-#define ACM_REQ_GET_FEATURE	0x03
-#define ACM_REQ_CLEAR_FEATURE	0x04
-
-#define ACM_REQ_SET_LINE	0x20
-#define ACM_REQ_GET_LINE	0x21
-#define ACM_REQ_SET_CONTROL	0x22
-#define ACM_REQ_SEND_BREAK	0x23
-
-/*
- * IRQs.
- */
-
-#define ACM_IRQ_NETWORK		0x00
-#define ACM_IRQ_LINE_STATE	0x20
-
 /*
  * Output control lines.
  */
@@ -66,17 +48,6 @@
 #define ACM_CTRL_OVERRUN	0x40
 
 /*
- * Line speed and caracter encoding.
- */
-
-struct acm_line {
-	__le32 speed;
-	__u8 stopbits;
-	__u8 parity;
-	__u8 databits;
-} __attribute__ ((packed));
-
-/*
  * Internal driver structures.
  */
 
@@ -88,7 +59,7 @@ struct acm {
 	struct urb *ctrlurb, *readurb, *writeurb;	/* urbs */
 	u8 *ctrl_buffer, *read_buffer, *write_buffer;	/* buffers of urbs */
 	dma_addr_t ctrl_dma, read_dma, write_dma;	/* dma handles of buffers */
-	struct acm_line line;				/* line coding (bits, stop, parity) */
+	struct usb_cdc_line_coding line;		/* bits, stop, parity */
 	struct work_struct work;			/* work queue entry for line discipline waking up */
 	struct tasklet_struct bh;			/* rx processing */
 	spinlock_t throttle_lock;			/* synchronize throtteling and read callback */
@@ -105,24 +76,6 @@ struct acm {
 	unsigned int ctrl_caps;				/* control capabilities from the class specific header */
 };
 
-/* "Union Functional Descriptor" from CDC spec 5.2.3.X */
-struct union_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-
-	u8	bMasterInterface0;
-	u8	bSlaveInterface0;
-	/* ... and there could be other slave interfaces */
-} __attribute__ ((packed));
-
-/* class specific descriptor types */
-#define CDC_HEADER_TYPE			0x00
-#define CDC_CALL_MANAGEMENT_TYPE	0x01
-#define CDC_AC_MANAGEMENT_TYPE		0x02
-#define CDC_UNION_TYPE			0x06
-#define CDC_COUNTRY_TYPE		0x07
-
 #define CDC_DATA_INTERFACE_TYPE	0x0a
 
 /* constants describing various quirks and errors */
diff -puN drivers/usb/core/hcd.c~bk-usb drivers/usb/core/hcd.c
--- 25/drivers/usb/core/hcd.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/core/hcd.c	2005-02-09 19:12:30.000000000 -0800
@@ -45,6 +45,7 @@
 
 #include "usb.h"
 #include "hcd.h"
+#include "hub.h"
 
 
 // #define USB_BANDWIDTH_MESSAGES
@@ -86,6 +87,7 @@
 
 /* host controllers we manage */
 LIST_HEAD (usb_bus_list);
+EXPORT_SYMBOL_GPL (usb_bus_list);
 
 /* used when allocating bus numbers */
 #define USB_MAXBUS		64
@@ -96,6 +98,7 @@ static struct usb_busmap busmap;
 
 /* used when updating list of hcds */
 DECLARE_MUTEX (usb_bus_list_lock);	/* exported only for usbfs */
+EXPORT_SYMBOL_GPL (usb_bus_list_lock);
 
 /* used when updating hcd data */
 static DEFINE_SPINLOCK(hcd_data_lock);
@@ -271,6 +274,10 @@ static int ascii2utf (char *s, u8 *utf, 
 		*utf++ = *s++;
 		*utf++ = 0;
 	}
+	if (utfmax > 0) {
+		*utf = *s;
+		++retval;
+	}
 	return retval;
 }
 
@@ -295,30 +302,40 @@ static int rh_string (
 
 	// language ids
 	if (id == 0) {
-		*data++ = 4; *data++ = 3;	/* 4 bytes string data */
-		*data++ = 0x09; *data++ = 0x04;	/* MSFT-speak for "en-us" */
-		return 4;
+		buf[0] = 4;    buf[1] = 3;	/* 4 bytes string data */
+		buf[2] = 0x09; buf[3] = 0x04;	/* MSFT-speak for "en-us" */
+		len = min (len, 4);
+		memcpy (data, buf, len);
+		return len;
 
 	// serial number
 	} else if (id == 1) {
-		strcpy (buf, hcd->self.bus_name);
+		strlcpy (buf, hcd->self.bus_name, sizeof buf);
 
 	// product description
 	} else if (id == 2) {
-                strcpy (buf, hcd->product_desc);
+		strlcpy (buf, hcd->product_desc, sizeof buf);
 
  	// id 3 == vendor description
 	} else if (id == 3) {
-                sprintf (buf, "%s %s %s",  system_utsname.sysname,
+		snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname,
 			system_utsname.release, hcd->driver->description);
 
 	// unsupported IDs --> "protocol stall"
 	} else
-	    return 0;
+		return -EPIPE;
 
-	data [0] = 2 * (strlen (buf) + 1);
-	data [1] = 3;	/* type == string */
-	return 2 + ascii2utf (buf, data + 2, len - 2);
+	switch (len) {		/* All cases fall through */
+	default:
+		len = 2 + ascii2utf (buf, data + 2, len - 2);
+	case 2:
+		data [1] = 3;	/* type == string */
+	case 1:
+		data [0] = 2 * (strlen (buf) + 1);
+	case 0:
+		;		/* Compiler wants a statement here */
+	}
+	return len;
 }
 
 
@@ -327,11 +344,14 @@ static int rh_call_control (struct usb_h
 {
 	struct usb_ctrlrequest *cmd;
  	u16		typeReq, wValue, wIndex, wLength;
-	const u8	*bufp = NULL;
 	u8		*ubuf = urb->transfer_buffer;
+	u8		tbuf [sizeof (struct usb_hub_descriptor)];
+	const u8	*bufp = tbuf;
 	int		len = 0;
 	int		patch_wakeup = 0;
 	unsigned long	flags;
+	int		status = 0;
+	int		n;
 
 	cmd = (struct usb_ctrlrequest *) urb->setup_packet;
 	typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;
@@ -342,17 +362,16 @@ static int rh_call_control (struct usb_h
 	if (wLength > urb->transfer_buffer_length)
 		goto error;
 
-	/* set up for success */
-	urb->status = 0;
-	urb->actual_length = wLength;
+	urb->actual_length = 0;
 	switch (typeReq) {
 
 	/* DEVICE REQUESTS */
 
 	case DeviceRequest | USB_REQ_GET_STATUS:
-		ubuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP)
+		tbuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP)
 				| (1 << USB_DEVICE_SELF_POWERED);
-		ubuf [1] = 0;
+		tbuf [1] = 0;
+		len = 2;
 		break;
 	case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
 		if (wValue == USB_DEVICE_REMOTE_WAKEUP)
@@ -367,7 +386,8 @@ static int rh_call_control (struct usb_h
 			goto error;
 		break;
 	case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-		ubuf [0] = 1;
+		tbuf [0] = 1;
+		len = 1;
 			/* FALLTHROUGH */
 	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
 		break;
@@ -394,16 +414,18 @@ static int rh_call_control (struct usb_h
 				patch_wakeup = 1;
 			break;
 		case USB_DT_STRING << 8:
-			urb->actual_length = rh_string (
-				wValue & 0xff, hcd,
-				ubuf, wLength);
+			n = rh_string (wValue & 0xff, hcd, ubuf, wLength);
+			if (n < 0)
+				goto error;
+			urb->actual_length = n;
 			break;
 		default:
 			goto error;
 		}
 		break;
 	case DeviceRequest | USB_REQ_GET_INTERFACE:
-		ubuf [0] = 0;
+		tbuf [0] = 0;
+		len = 1;
 			/* FALLTHROUGH */
 	case DeviceOutRequest | USB_REQ_SET_INTERFACE:
 		break;
@@ -419,8 +441,9 @@ static int rh_call_control (struct usb_h
 
 	case EndpointRequest | USB_REQ_GET_STATUS:
 		// ENDPOINT_HALT flag
-		ubuf [0] = 0;
-		ubuf [1] = 0;
+		tbuf [0] = 0;
+		tbuf [1] = 0;
+		len = 2;
 			/* FALLTHROUGH */
 	case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
 	case EndpointOutRequest | USB_REQ_SET_FEATURE:
@@ -432,19 +455,30 @@ static int rh_call_control (struct usb_h
 	default:
 		/* non-generic request */
 		if (HCD_IS_SUSPENDED (hcd->state))
-			urb->status = -EAGAIN;
-		else
-			urb->status = hcd->driver->hub_control (hcd,
+			status = -EAGAIN;
+		else {
+			switch (typeReq) {
+			case GetHubStatus:
+			case GetPortStatus:
+				len = 4;
+				break;
+			case GetHubDescriptor:
+				len = sizeof (struct usb_hub_descriptor);
+				break;
+			}
+			status = hcd->driver->hub_control (hcd,
 				typeReq, wValue, wIndex,
-				ubuf, wLength);
+				tbuf, wLength);
+		}
 		break;
 error:
 		/* "protocol stall" on error */
-		urb->status = -EPIPE;
+		status = -EPIPE;
 	}
-	if (urb->status) {
-		urb->actual_length = 0;
-		if (urb->status != -EPIPE) {
+
+	if (status) {
+		len = 0;
+		if (status != -EPIPE) {
 			dev_dbg (hcd->self.controller,
 				"CTRL: TypeReq=0x%x val=0x%x "
 				"idx=0x%x len=%d ==> %d\n",
@@ -452,7 +486,7 @@ error:
 				wLength, urb->status);
 		}
 	}
-	if (bufp) {
+	if (len) {
 		if (urb->transfer_buffer_length < len)
 			len = urb->transfer_buffer_length;
 		urb->actual_length = len;
@@ -460,13 +494,19 @@ error:
 		memcpy (ubuf, bufp, len);
 
 		/* report whether RH hardware supports remote wakeup */
-		if (patch_wakeup)
+		if (patch_wakeup &&
+				len > offsetof (struct usb_config_descriptor,
+						bmAttributes))
 			((struct usb_config_descriptor *)ubuf)->bmAttributes
 				|= USB_CONFIG_ATT_WAKEUP;
 	}
 
 	/* any errors get returned through the urb completion */
 	local_irq_save (flags);
+	spin_lock (&urb->lock);
+	if (urb->status == -EINPROGRESS)
+		urb->status = status;
+	spin_unlock (&urb->lock);
 	usb_hcd_giveback_urb (hcd, urb, NULL);
 	local_irq_restore (flags);
 	return 0;
@@ -746,6 +786,7 @@ int usb_register_bus(struct usb_bus *bus
 	up (&usb_bus_list_lock);
 
 	usbfs_add_bus (bus);
+	usbmon_notify_bus_add (bus);
 
 	dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
 	return 0;
@@ -773,6 +814,7 @@ void usb_deregister_bus (struct usb_bus 
 	list_del (&bus->bus_list);
 	up (&usb_bus_list_lock);
 
+	usbmon_notify_bus_remove (bus);
 	usbfs_remove_bus (bus);
 
 	clear_bit (bus->busnum, busmap.busmap);
@@ -1058,9 +1100,7 @@ static int hcd_submit_urb (struct urb *u
 	 * as simple as possible.
 	 */
 
-	// NOTE:  a generic device/urb monitoring hook would go here.
-	// hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)
-	// It would catch submission paths for all urbs.
+	usbmon_urb_submit(&hcd->self, urb);
 
 	/*
 	 * Atomically queue the urb,  first to our records, then to the HCD.
@@ -1087,6 +1127,7 @@ static int hcd_submit_urb (struct urb *u
 	spin_unlock_irqrestore (&hcd_data_lock, flags);
 	if (status) {
 		INIT_LIST_HEAD (&urb->urb_list);
+		usbmon_urb_submit_error(&hcd->self, urb, status);
 		return status;
 	}
 
@@ -1103,8 +1144,6 @@ static int hcd_submit_urb (struct urb *u
 		 * valid and usb_buffer_{sync,unmap}() not be needed, since
 		 * they could clobber root hub response data.
 		 */
-		urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
-					| URB_NO_SETUP_DMA_MAP);
 		status = rh_urb_enqueue (hcd, urb);
 		goto done;
 	}
@@ -1139,6 +1178,7 @@ done:
 		if (urb->reject)
 			wake_up (&usb_kill_urb_queue);
 		usb_put_urb (urb);
+		usbmon_urb_submit_error(&hcd->self, urb, status);
 	}
 	return status;
 }
@@ -1461,14 +1501,13 @@ static struct usb_operations usb_hcd_ope
  */
 void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
 {
-	urb_unlink (urb);
+	int at_root_hub;
 
-	// NOTE:  a generic device/urb monitoring hook would go here.
-	// hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
-	// It would catch exit/unlink paths for all urbs.
+	at_root_hub = (urb->dev == hcd->self.root_hub);
+	urb_unlink (urb);
 
 	/* lower level hcd code should use *_dma exclusively */
-	if (hcd->self.controller->dma_mask) {
+	if (hcd->self.controller->dma_mask && !at_root_hub) {
 		if (usb_pipecontrol (urb->pipe)
 			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
 			dma_unmap_single (hcd->self.controller, urb->setup_dma,
@@ -1484,6 +1523,7 @@ void usb_hcd_giveback_urb (struct usb_hc
 					    : DMA_TO_DEVICE);
 	}
 
+	usbmon_urb_complete (&hcd->self, urb);
 	/* pass ownership to the completion handler */
 	urb->complete (urb, regs);
 	atomic_dec (&urb->use_count);
@@ -1591,3 +1631,43 @@ void usb_put_hcd (struct usb_hcd *hcd)
 	usb_bus_put(&hcd->self);
 }
 EXPORT_SYMBOL (usb_put_hcd);
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+
+struct usb_mon_operations *mon_ops;
+
+/*
+ * The registration is unlocked.
+ * We do it this way because we do not want to lock in hot paths.
+ *
+ * Notice that the code is minimally error-proof. Because usbmon needs
+ * symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
+ */
+ 
+int usb_mon_register (struct usb_mon_operations *ops)
+{
+
+	if (mon_ops)
+		return -EBUSY;
+
+	mon_ops = ops;
+	mb();
+	return 0;
+}
+EXPORT_SYMBOL_GPL (usb_mon_register);
+
+void usb_mon_deregister (void)
+{
+
+	if (mon_ops == NULL) {
+		printk(KERN_ERR "USB: monitor was not registered\n");
+		return;
+	}
+	mon_ops = NULL;
+	mb();
+}
+EXPORT_SYMBOL_GPL (usb_mon_deregister);
+
+#endif /* CONFIG_USB_MON */
diff -puN drivers/usb/core/hcd.h~bk-usb drivers/usb/core/hcd.h
--- 25/drivers/usb/core/hcd.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/core/hcd.h	2005-02-09 19:12:30.000000000 -0800
@@ -411,6 +411,66 @@ static inline void usbfs_cleanup(void) {
 
 /*-------------------------------------------------------------------------*/
 
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+
+struct usb_mon_operations {
+	void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
+	void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
+	void (*urb_complete)(struct usb_bus *bus, struct urb *urb);
+	/* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
+	void (*bus_add)(struct usb_bus *bus);
+	void (*bus_remove)(struct usb_bus *bus);
+};
+
+extern struct usb_mon_operations *mon_ops;
+
+static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb)
+{
+	if (bus->monitored)
+		(*mon_ops->urb_submit)(bus, urb);
+}
+
+static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
+    int error)
+{
+	if (bus->monitored)
+		(*mon_ops->urb_submit_error)(bus, urb, error);
+}
+
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb)
+{
+	if (bus->monitored)
+		(*mon_ops->urb_complete)(bus, urb);
+}
+ 
+static inline void usbmon_notify_bus_add(struct usb_bus *bus)
+{
+	if (mon_ops)
+		(*mon_ops->bus_add)(bus);
+}
+
+static inline void usbmon_notify_bus_remove(struct usb_bus *bus)
+{
+	if (mon_ops)
+		(*mon_ops->bus_remove)(bus);
+}
+
+int usb_mon_register(struct usb_mon_operations *ops);
+void usb_mon_deregister(void);
+
+#else
+
+static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
+    int error) {}
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_notify_bus_add(struct usb_bus *bus) {}
+static inline void usbmon_notify_bus_remove(struct usb_bus *bus) {}
+
+#endif /* CONFIG_USB_MON */
+
+/*-------------------------------------------------------------------------*/
+
 /* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
 // bleech -- resurfaced in 2.4.11 or 2.4.12
 #define bitmap 	DeviceRemovable
diff -puN drivers/usb/core/hub.c~bk-usb drivers/usb/core/hub.c
--- 25/drivers/usb/core/hub.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/core/hub.c	2005-02-09 19:12:30.000000000 -0800
@@ -74,7 +74,7 @@ module_param(old_scheme_first, bool, S_I
 MODULE_PARM_DESC(old_scheme_first,
 		 "start with the old device initialization scheme");
 
-static int use_both_schemes = 0;
+static int use_both_schemes = 1;
 module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(use_both_schemes,
 		"try the other device initialization scheme if the "
@@ -459,6 +459,7 @@ static void hub_activate(struct usb_hub 
 	int	status;
 
 	hub->quiescing = 0;
+	hub->activating = 1;
 	status = usb_submit_urb(hub->urb, GFP_NOIO);
 	if (status < 0)
 		dev_err(hub->intfdev, "activate --> %d\n", status);
@@ -466,7 +467,6 @@ static void hub_activate(struct usb_hub 
 		schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
 
 	/* scan all ports ASAP */
-	hub->event_bits[0] = (1UL << (hub->descriptor->bNbrPorts + 1)) - 1;
 	kick_khubd(hub);
 }
 
@@ -689,7 +689,6 @@ static int hub_configure(struct usb_hub 
 		hub->indicator [0] = INDICATOR_CYCLE;
 
 	hub_power_on(hub);
-	hub->change_bits[0] = (1UL << (hub->descriptor->bNbrPorts + 1)) - 2;
 	hub_activate(hub);
 	return 0;
 
@@ -1235,10 +1234,10 @@ int usb_new_device(struct usb_device *ud
 		 */
 		if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
 			static int __usb_suspend_device (struct usb_device *,
-						int port1, u32 state);
+						int port1, pm_message_t state);
 			err = __usb_suspend_device(udev,
 					udev->bus->otg_port,
-					PM_SUSPEND_MEM);
+					PMSG_SUSPEND);
 			if (err < 0)
 				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
 		}
@@ -1523,7 +1522,7 @@ static int hub_port_suspend(struct usb_h
  * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
  * timer, no SRP, no requests through sysfs.
  */
-int __usb_suspend_device (struct usb_device *udev, int port1, u32 state)
+int __usb_suspend_device (struct usb_device *udev, int port1, pm_message_t state)
 {
 	int	status;
 
@@ -1621,7 +1620,7 @@ int __usb_suspend_device (struct usb_dev
 /**
  * usb_suspend_device - suspend a usb device
  * @udev: device that's no longer in active use
- * @state: PM_SUSPEND_MEM to suspend
+ * @state: PMSG_SUSPEND to suspend
  * Context: must be able to sleep; device not locked
  *
  * Suspends a USB device that isn't in active use, conserving power.
@@ -1670,7 +1669,7 @@ static int finish_port_resume(struct usb
 	usb_set_device_state(udev, udev->actconfig
 			? USB_STATE_CONFIGURED
 			: USB_STATE_ADDRESS);
-	udev->dev.power.power_state = PM_SUSPEND_ON;
+	udev->dev.power.power_state = PMSG_ON;
 
  	/* 10.5.4.5 says be sure devices in the tree are still there.
  	 * For now let's assume the device didn't go crazy on resume,
@@ -1871,7 +1870,7 @@ static int remote_wakeup(struct usb_devi
 	return status;
 }
 
-static int hub_suspend(struct usb_interface *intf, u32 state)
+static int hub_suspend(struct usb_interface *intf, pm_message_t state)
 {
 	struct usb_hub		*hub = usb_get_intfdata (intf);
 	struct usb_device	*hdev = hub->hdev;
@@ -1943,7 +1942,7 @@ static int hub_resume(struct usb_interfa
 		}
 		up(&udev->serialize);
 	}
-	intf->dev.power.power_state = PM_SUSPEND_ON;
+	intf->dev.power.power_state = PMSG_ON;
 
 	hub_activate(hub);
 	return 0;
@@ -2178,24 +2177,35 @@ hub_port_init (struct usb_hub *hub, stru
 				retval = -ENOMEM;
 				continue;
 			}
-			buf->bMaxPacketSize0 = 0;
 
 			/* Use a short timeout the first time through,
 			 * so that recalcitrant full-speed devices with
 			 * 8- or 16-byte ep0-maxpackets won't slow things
 			 * down tremendously by NAKing the unexpectedly
-			 * early status stage.  Also, retry on length 0
-			 * or stall; some devices are flakey.
+			 * early status stage.  Also, retry on all errors;
+			 * some devices are flakey.
 			 */
 			for (j = 0; j < 3; ++j) {
+				buf->bMaxPacketSize0 = 0;
 				r = usb_control_msg(udev, usb_rcvaddr0pipe(),
 					USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
 					USB_DT_DEVICE << 8, 0,
 					buf, GET_DESCRIPTOR_BUFSIZE,
 					(i ? HZ * USB_CTRL_GET_TIMEOUT : HZ));
-				if (r == 0 || r == -EPIPE)
-					continue;
-				if (r < 0)
+				switch (buf->bMaxPacketSize0) {
+				case 8: case 16: case 32: case 64:
+					if (buf->bDescriptorType ==
+							USB_DT_DEVICE) {
+						r = 0;
+						break;
+					}
+					/* FALL THROUGH */
+				default:
+					if (r == 0)
+						r = -EPROTO;
+					break;
+				}
+				if (r == 0)
 					break;
 			}
 			udev->descriptor.bMaxPacketSize0 =
@@ -2211,10 +2221,7 @@ hub_port_init (struct usb_hub *hub, stru
 				retval = -ENODEV;
 				goto fail;
 			}
-			switch (udev->descriptor.bMaxPacketSize0) {
-			case 64: case 32: case 16: case 8:
-				break;
-			default:
+			if (r) {
 				dev_err(&udev->dev, "device descriptor "
 						"read/%s, error %d\n",
 						"64", r);
@@ -2627,7 +2634,7 @@ static void hub_events(void)
 		for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
 			connect_change = test_bit(i, hub->change_bits);
 			if (!test_and_clear_bit(i, hub->event_bits) &&
-					!connect_change)
+					!connect_change && !hub->activating)
 				continue;
 
 			ret = hub_port_status(hub, i,
@@ -2635,6 +2642,11 @@ static void hub_events(void)
 			if (ret < 0)
 				continue;
 
+			if (hub->activating && !hdev->children[i-1] &&
+					(portstatus &
+						USB_PORT_STAT_CONNECTION))
+				connect_change = 1;
+
 			if (portchange & USB_PORT_STAT_C_CONNECTION) {
 				clear_port_feature(hdev, i,
 					USB_PORT_FEAT_C_CONNECTION);
@@ -2725,6 +2737,8 @@ static void hub_events(void)
 			}
 		}
 
+		hub->activating = 0;
+
 loop:
 		usb_unlock_device(hdev);
 		usb_put_intf(intf);
diff -puN drivers/usb/core/hub.h~bk-usb drivers/usb/core/hub.h
--- 25/drivers/usb/core/hub.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/core/hub.h	2005-02-09 19:12:30.000000000 -0800
@@ -215,6 +215,7 @@ struct usb_hub {
 	u8			power_budget;	/* in 2mA units; or zero */
 
 	unsigned		quiescing:1;
+	unsigned		activating:1;
 
 	unsigned		has_indicators:1;
 	enum hub_led_mode	indicator[USB_MAXCHILDREN];
diff -puN drivers/usb/gadget/ether.c~bk-usb drivers/usb/gadget/ether.c
--- 25/drivers/usb/gadget/ether.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/gadget/ether.c	2005-02-09 19:12:30.000000000 -0800
@@ -49,6 +49,7 @@
 #include <asm/unaligned.h>
 
 #include <linux/usb_ch9.h>
+#include <linux/usb_cdc.h>
 #include <linux/usb_gadget.h>
 
 #include <linux/random.h>
@@ -432,8 +433,8 @@ control_intf = {
 	/* status endpoint is optional; this may be patched later */
 	.bNumEndpoints =	1,
 	.bInterfaceClass =	USB_CLASS_COMM,
-	.bInterfaceSubClass =	6,	/* ethernet control model */
-	.bInterfaceProtocol =	0,
+	.bInterfaceSubClass =	USB_CDC_SUBCLASS_ETHERNET,
+	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,
 	.iInterface =		STRING_CONTROL,
 };
 #endif
@@ -447,46 +448,26 @@ rndis_control_intf = {
 	.bInterfaceNumber =     0,
 	.bNumEndpoints =        1,
 	.bInterfaceClass =      USB_CLASS_COMM,
-	.bInterfaceSubClass =   2,	/* abstract control model */
-	.bInterfaceProtocol =   0xff,	/* vendor specific */
+	.bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM,
+	.bInterfaceProtocol =   USB_CDC_ACM_PROTO_VENDOR,
 	.iInterface =           STRING_RNDIS_CONTROL,
 };
 #endif
 
 #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
 
-/* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
-struct header_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-
-	u16	bcdCDC;
-} __attribute__ ((packed));
-
-static const struct header_desc header_desc = {
+static const struct usb_cdc_header_desc header_desc = {
 	.bLength =		sizeof header_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType =	0,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
 
 	.bcdCDC =		__constant_cpu_to_le16 (0x0110),
 };
 
-/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */
-struct union_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-
-	u8	bMasterInterface0;
-	u8	bSlaveInterface0;
-	/* ... and there could be other slave interfaces */
-} __attribute__ ((packed));
-
-static const struct union_desc union_desc = {
+static const struct usb_cdc_union_desc union_desc = {
 	.bLength =		sizeof union_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType =	6,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
 
 	.bMasterInterface0 =	0,	/* index of control interface */
 	.bSlaveInterface0 =	1,	/* index of DATA interface */
@@ -496,64 +477,31 @@ static const struct union_desc union_des
 
 #ifdef	CONFIG_USB_ETH_RNDIS
 
-/* "Call Management Descriptor" from CDC spec  5.2.3.3 */
-struct call_mgmt_descriptor {
-	u8  bLength;
-	u8  bDescriptorType;
-	u8  bDescriptorSubType;
-
-	u8  bmCapabilities;
-	u8  bDataInterface;
-} __attribute__ ((packed));
-
-static const struct call_mgmt_descriptor call_mgmt_descriptor = {
+static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
 	.bLength =  		sizeof call_mgmt_descriptor,
 	.bDescriptorType = 	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType = 	0x01,
+	.bDescriptorSubType = 	USB_CDC_CALL_MANAGEMENT_TYPE,
 
 	.bmCapabilities = 	0x00,
 	.bDataInterface = 	0x01,
 };
 
-
-/* "Abstract Control Management Descriptor" from CDC spec  5.2.3.4 */
-struct acm_descriptor {
-	u8  bLength;
-	u8  bDescriptorType;
-	u8  bDescriptorSubType;
-
-	u8  bmCapabilities;
-} __attribute__ ((packed));
-
-static struct acm_descriptor acm_descriptor = {
+static struct usb_cdc_acm_descriptor acm_descriptor = {
 	.bLength =  		sizeof acm_descriptor,
 	.bDescriptorType = 	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType = 	0x02,
+	.bDescriptorSubType = 	USB_CDC_ACM_TYPE,
 
-	.bmCapabilities = 	0X00,
+	.bmCapabilities = 	0x00,
 };
 
 #endif
 
 #ifdef	DEV_CONFIG_CDC
 
-/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
-struct ether_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-
-	u8	iMACAddress;
-	u32	bmEthernetStatistics;
-	u16	wMaxSegmentSize;
-	u16	wNumberMCFilters;
-	u8	bNumberPowerFilters;
-} __attribute__ ((packed));
-
-static const struct ether_desc ether_desc = {
+static const struct usb_cdc_ether_desc ether_desc = {
 	.bLength =		sizeof ether_desc,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType =	0x0f,
+	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
 
 	/* this descriptor actually adds value, surprise! */
 	.iMACAddress =		STRING_ETHADDR,
@@ -1242,47 +1190,30 @@ eth_set_config (struct eth_dev *dev, uns
 
 /*-------------------------------------------------------------------------*/
 
-/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications
- * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS
- * and RNDIS also defines its own bit-incompatible notifications
- */
-#define CDC_NOTIFY_NETWORK_CONNECTION	0x00	/* required; 6.3.1 */
-#define CDC_NOTIFY_RESPONSE_AVAILABLE	0x01	/* optional; 6.3.2 */
-#define CDC_NOTIFY_SPEED_CHANGE		0x2a	/* required; 6.3.8 */
-
 #ifdef	DEV_CONFIG_CDC
 
-struct cdc_notification {
-	u8	bmRequestType;
-	u8	bNotificationType;
-	u16	wValue;
-	u16	wIndex;
-	u16	wLength;
-
-	/* SPEED_CHANGE data looks like this */
-	u32	data [2];
-};
-
 static void eth_status_complete (struct usb_ep *ep, struct usb_request *req)
 {
-	struct cdc_notification	*event = req->buf;
-	int			value = req->status;
-	struct eth_dev		*dev = ep->driver_data;
+	struct usb_cdc_notification	*event = req->buf;
+	int				value = req->status;
+	struct eth_dev			*dev = ep->driver_data;
 
 	/* issue the second notification if host reads the first */
-	if (event->bNotificationType == CDC_NOTIFY_NETWORK_CONNECTION
+	if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION
 			&& value == 0) {
+		__le32	*data = req->buf + sizeof *event;
+
 		event->bmRequestType = 0xA1;
-		event->bNotificationType = CDC_NOTIFY_SPEED_CHANGE;
+		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
 		event->wValue = __constant_cpu_to_le16 (0);
 		event->wIndex = __constant_cpu_to_le16 (1);
 		event->wLength = __constant_cpu_to_le16 (8);
 
 		/* SPEED_CHANGE data is up/down speeds in bits/sec */
-		event->data [0] = event->data [1] =
+		data [0] = data [1] = cpu_to_le32(
 			(dev->gadget->speed == USB_SPEED_HIGH)
 				? (13 * 512 * 8 * 1000 * 8)
-				: (19 *  64 * 1 * 1000 * 8);
+				: (19 *  64 * 1 * 1000 * 8));
 
 		req->length = 16;
 		value = usb_ep_queue (ep, req, GFP_ATOMIC);
@@ -1300,9 +1231,9 @@ static void eth_status_complete (struct 
 
 static void issue_start_status (struct eth_dev *dev)
 {
-	struct usb_request	*req;
-	struct cdc_notification	*event;
-	int			value;
+	struct usb_request		*req;
+	struct usb_cdc_notification	*event;
+	int				value;
  
 	DEBUG (dev, "%s, flush old status first\n", __FUNCTION__);
 
@@ -1336,7 +1267,7 @@ free_req:
 	 */
 	event = req->buf;
 	event->bmRequestType = 0xA1;
-	event->bNotificationType = CDC_NOTIFY_NETWORK_CONNECTION;
+	event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
 	event->wValue = __constant_cpu_to_le16 (1);	/* connected */
 	event->wIndex = __constant_cpu_to_le16 (1);
 	event->wLength = 0;
@@ -1364,26 +1295,6 @@ static void eth_setup_complete (struct u
 				req->status, req->actual, req->length);
 }
 
-/* see section 3.8.2 table 10 of the CDC spec for more ethernet
- * requests, mostly for filters (multicast, pm) and statistics
- * section 3.6.2.1 table 4 has ACM requests; RNDIS requires the
- * encapsulated command mechanism.
- */
-#define CDC_SEND_ENCAPSULATED_COMMAND		0x00	/* optional */
-#define CDC_GET_ENCAPSULATED_RESPONSE		0x01	/* optional */
-#define CDC_SET_ETHERNET_MULTICAST_FILTERS	0x40	/* optional */
-#define CDC_SET_ETHERNET_PM_PATTERN_FILTER	0x41	/* optional */
-#define CDC_GET_ETHERNET_PM_PATTERN_FILTER	0x42	/* optional */
-#define CDC_SET_ETHERNET_PACKET_FILTER		0x43	/* required */
-#define CDC_GET_ETHERNET_STATISTIC		0x44	/* optional */
-
-/* table 62; bits in cdc_filter */
-#define	CDC_PACKET_TYPE_PROMISCUOUS		(1 << 0)
-#define	CDC_PACKET_TYPE_ALL_MULTICAST		(1 << 1) /* no filter */
-#define	CDC_PACKET_TYPE_DIRECTED		(1 << 2)
-#define	CDC_PACKET_TYPE_BROADCAST		(1 << 3)
-#define	CDC_PACKET_TYPE_MULTICAST		(1 << 4) /* filtered */
-
 #ifdef CONFIG_USB_ETH_RNDIS
 
 static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req)
@@ -1393,7 +1304,7 @@ static void rndis_response_complete (str
 			"rndis response complete --> %d, %d/%d\n",
 			req->status, req->actual, req->length);
 
-	/* done sending after CDC_GET_ENCAPSULATED_RESPONSE */
+	/* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */
 }
 
 static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req)
@@ -1401,7 +1312,7 @@ static void rndis_command_complete (stru
 	struct eth_dev          *dev = ep->driver_data;
 	int			status;
 	
-	/* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */
+	/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
 	spin_lock(&dev->lock);
 	status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf);
 	if (status < 0)
@@ -1426,6 +1337,9 @@ eth_setup (struct usb_gadget *gadget, co
 	struct eth_dev		*dev = get_gadget_data (gadget);
 	struct usb_request	*req = dev->req;
 	int			value = -EOPNOTSUPP;
+	u16			wIndex = ctrl->wIndex;
+	u16			wValue = ctrl->wValue;
+	u16			wLength = ctrl->wLength;
 
 	/* descriptors just go into the pre-allocated ep0 buffer,
 	 * while config change events may enable network traffic.
@@ -1436,17 +1350,17 @@ eth_setup (struct usb_gadget *gadget, co
 	case USB_REQ_GET_DESCRIPTOR:
 		if (ctrl->bRequestType != USB_DIR_IN)
 			break;
-		switch (ctrl->wValue >> 8) {
+		switch (wValue >> 8) {
 
 		case USB_DT_DEVICE:
-			value = min (ctrl->wLength, (u16) sizeof device_desc);
+			value = min (wLength, (u16) sizeof device_desc);
 			memcpy (req->buf, &device_desc, value);
 			break;
 #ifdef CONFIG_USB_GADGET_DUALSPEED
 		case USB_DT_DEVICE_QUALIFIER:
 			if (!gadget->is_dualspeed)
 				break;
-			value = min (ctrl->wLength, (u16) sizeof dev_qualifier);
+			value = min (wLength, (u16) sizeof dev_qualifier);
 			memcpy (req->buf, &dev_qualifier, value);
 			break;
 
@@ -1457,18 +1371,18 @@ eth_setup (struct usb_gadget *gadget, co
 #endif /* CONFIG_USB_GADGET_DUALSPEED */
 		case USB_DT_CONFIG:
 			value = config_buf (gadget->speed, req->buf,
-					ctrl->wValue >> 8,
-					ctrl->wValue & 0xff,
+					wValue >> 8,
+					wValue & 0xff,
 					gadget->is_otg);
 			if (value >= 0)
-				value = min (ctrl->wLength, (u16) value);
+				value = min (wLength, (u16) value);
 			break;
 
 		case USB_DT_STRING:
 			value = usb_gadget_get_string (&stringtab,
-					ctrl->wValue & 0xff, req->buf);
+					wValue & 0xff, req->buf);
 			if (value >= 0)
-				value = min (ctrl->wLength, (u16) value);
+				value = min (wLength, (u16) value);
 			break;
 		}
 		break;
@@ -1481,22 +1395,22 @@ eth_setup (struct usb_gadget *gadget, co
 		else if (gadget->a_alt_hnp_support)
 			DEBUG (dev, "HNP needs a different root port\n");
 		spin_lock (&dev->lock);
-		value = eth_set_config (dev, ctrl->wValue, GFP_ATOMIC);
+		value = eth_set_config (dev, wValue, GFP_ATOMIC);
 		spin_unlock (&dev->lock);
 		break;
 	case USB_REQ_GET_CONFIGURATION:
 		if (ctrl->bRequestType != USB_DIR_IN)
 			break;
 		*(u8 *)req->buf = dev->config;
-		value = min (ctrl->wLength, (u16) 1);
+		value = min (wLength, (u16) 1);
 		break;
 
 	case USB_REQ_SET_INTERFACE:
 		if (ctrl->bRequestType != USB_RECIP_INTERFACE
 				|| !dev->config
-				|| ctrl->wIndex > 1)
+				|| wIndex > 1)
 			break;
-		if (!dev->cdc && ctrl->wIndex != 0)
+		if (!dev->cdc && wIndex != 0)
 			break;
 		spin_lock (&dev->lock);
 
@@ -1510,9 +1424,9 @@ eth_setup (struct usb_gadget *gadget, co
 		}
 
 #ifdef DEV_CONFIG_CDC
-		switch (ctrl->wIndex) {
+		switch (wIndex) {
 		case 0:		/* control/master intf */
-			if (ctrl->wValue != 0)
+			if (wValue != 0)
 				break;
 			if (dev->status_ep) {
 				usb_ep_disable (dev->status_ep);
@@ -1521,7 +1435,7 @@ eth_setup (struct usb_gadget *gadget, co
 			value = 0;
 			break;
 		case 1:		/* data intf */
-			if (ctrl->wValue > 1)
+			if (wValue > 1)
 				break;
 			usb_ep_disable (dev->in_ep);
 			usb_ep_disable (dev->out_ep);
@@ -1530,7 +1444,7 @@ eth_setup (struct usb_gadget *gadget, co
 			 * the default interface setting ... also, setting
 			 * the non-default interface clears filters etc.
 			 */
-			if (ctrl->wValue == 1) {
+			if (wValue == 1) {
 				usb_ep_enable (dev->in_ep, dev->in);
 				usb_ep_enable (dev->out_ep, dev->out);
 				netif_carrier_on (dev->net);
@@ -1561,36 +1475,36 @@ done_set_intf:
 	case USB_REQ_GET_INTERFACE:
 		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
 				|| !dev->config
-				|| ctrl->wIndex > 1)
+				|| wIndex > 1)
 			break;
-		if (!(dev->cdc || dev->rndis) && ctrl->wIndex != 0)
+		if (!(dev->cdc || dev->rndis) && wIndex != 0)
 			break;
 
 		/* for CDC, iff carrier is on, data interface is active. */
-		if (dev->rndis || ctrl->wIndex != 1)
+		if (dev->rndis || wIndex != 1)
 			*(u8 *)req->buf = 0;
 		else
 			*(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0;
-		value = min (ctrl->wLength, (u16) 1);
+		value = min (wLength, (u16) 1);
 		break;
 
 #ifdef DEV_CONFIG_CDC
-	case CDC_SET_ETHERNET_PACKET_FILTER:
+	case USB_CDC_SET_ETHERNET_PACKET_FILTER:
 		/* see 6.2.30: no data, wIndex = interface,
 		 * wValue = packet filter bitmap
 		 */
 		if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
 				|| !dev->cdc
 				|| dev->rndis
-				|| ctrl->wLength != 0
-				|| ctrl->wIndex > 1)
+				|| wLength != 0
+				|| wIndex > 1)
 			break;
-		DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue);
+		DEBUG (dev, "NOP packet filter %04x\n", wValue);
 		/* NOTE: table 62 has 5 filter bits to reduce traffic,
 		 * and we "must" support multicast and promiscuous.
 		 * this NOP implements a bad filter (always promisc)
 		 */
-		dev->cdc_filter = ctrl->wValue;
+		dev->cdc_filter = wValue;
 		value = 0;
 		break;
 #endif /* DEV_CONFIG_CDC */
@@ -1599,28 +1513,28 @@ done_set_intf:
 	/* RNDIS uses the CDC command encapsulation mechanism to implement
 	 * an RPC scheme, with much getting/setting of attributes by OID.
 	 */
-	case CDC_SEND_ENCAPSULATED_COMMAND:
+	case USB_CDC_SEND_ENCAPSULATED_COMMAND:
 		if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
 				|| !dev->rndis
-				|| ctrl->wLength > USB_BUFSIZ
-				|| ctrl->wValue
+				|| wLength > USB_BUFSIZ
+				|| wValue
 				|| rndis_control_intf.bInterfaceNumber
-					!= ctrl->wIndex)
+					!= wIndex)
 			break;
 		/* read the request, then process it */
-		value = ctrl->wLength;
+		value = wLength;
 		req->complete = rndis_command_complete;
 		/* later, rndis_control_ack () sends a notification */
 		break;
 		
-	case CDC_GET_ENCAPSULATED_RESPONSE:
+	case USB_CDC_GET_ENCAPSULATED_RESPONSE:
 		if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)
 					== ctrl->bRequestType
 				&& dev->rndis
-				// && ctrl->wLength >= 0x0400
-				&& !ctrl->wValue
+				// && wLength >= 0x0400
+				&& !wValue
 				&& rndis_control_intf.bInterfaceNumber
-					== ctrl->wIndex) {
+					== wIndex) {
 			u8 *buf;
 
 			/* return the result */
@@ -1640,13 +1554,13 @@ done_set_intf:
 		VDEBUG (dev,
 			"unknown control req%02x.%02x v%04x i%04x l%d\n",
 			ctrl->bRequestType, ctrl->bRequest,
-			ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+			wValue, wIndex, wLength);
 	}
 
 	/* respond with data transfer before status phase? */
 	if (value >= 0) {
 		req->length = value;
-		req->zero = value < ctrl->wLength
+		req->zero = value < wLength
 				&& (value % gadget->ep0->maxpacket) == 0;
 		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
 		if (value < 0) {
@@ -1990,7 +1904,7 @@ static int eth_start_xmit (struct sk_buf
 	unsigned long		flags;
 
 	/* FIXME check dev->cdc_filter to decide whether to send this,
-	 * instead of acting as if CDC_PACKET_TYPE_PROMISCUOUS were
+	 * instead of acting as if USB_CDC_PACKET_TYPE_PROMISCUOUS were
 	 * always set.  RNDIS has the same kind of outgoing filter.
 	 */
 
@@ -2124,13 +2038,13 @@ static int rndis_control_ack (struct net
 	}
 	
 	/* Send RNDIS RESPONSE_AVAILABLE notification;
-	 * CDC_NOTIFY_RESPONSE_AVAILABLE should work too
+	 * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too
 	 */
 	resp->length = 8;
 	resp->complete = rndis_control_ack_complete;
 	
-	*((u32 *) resp->buf) = __constant_cpu_to_le32 (1);
-	*((u32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
+	*((__le32 *) resp->buf) = __constant_cpu_to_le32 (1);
+	*((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
 	
 	length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC);
 	if (length < 0) {
@@ -2414,9 +2328,12 @@ autoconf_fail:
 				"can't run RNDIS on %s\n",
 				gadget->name);
 			return -ENODEV;
+#ifdef DEV_CONFIG_CDC
+		/* pxa25x only does CDC subset; often used with RNDIS */
 		} else if (cdc) {
 			control_intf.bNumEndpoints = 0;
 			/* FIXME remove endpoint from descriptor list */
+#endif
 		}
 	}
 #endif
diff -puN drivers/usb/gadget/Kconfig~bk-usb drivers/usb/gadget/Kconfig
--- 25/drivers/usb/gadget/Kconfig~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/gadget/Kconfig	2005-02-09 19:12:30.000000000 -0800
@@ -87,10 +87,10 @@ config USB_NET2280
 	default USB_GADGET
 
 config USB_GADGET_PXA2XX
-	boolean "PXA 2xx or IXP 4xx"
-	depends on ARCH_PXA || ARCH_IXP4XX
+	boolean "PXA 25x or IXP 4xx"
+	depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
 	help
-	   Intel's PXA 2xx series XScale ARM-5TE processors include
+	   Intel's PXA 25x series XScale ARM-5TE processors include
 	   an integrated full speed USB 1.1 device controller.  The
 	   controller in the IXP 4xx series is register-compatible.
 
@@ -194,7 +194,7 @@ config USB_DUMMY_HCD
 config USB_GADGET_OMAP
 	boolean "OMAP USB Device Controller"
 	depends on ARCH_OMAP
-	select ISP1301_OMAP if MACH_OMAP_H2
+	select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
 	help
 	   Many Texas Instruments OMAP processors have flexible full
 	   speed USB device controllers, with support for up to 30
diff -puN drivers/usb/gadget/net2280.c~bk-usb drivers/usb/gadget/net2280.c
--- 25/drivers/usb/gadget/net2280.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/gadget/net2280.c	2005-02-09 19:12:30.000000000 -0800
@@ -1,8 +1,9 @@
 /*
- * Driver for the NetChip 2280 USB device controller.
- * Specs and errata are available from <http://www.netchip.com>.
+ * Driver for the PLX NET2280 USB device controller.
+ * Specs and errata are available from <http://www.plxtech.com>.
  *
- * NetChip Technology Inc. supported the development of this driver.
+ * PLX Technology Inc. (formerly NetChip Technology) supported the 
+ * development of this driver.
  *
  *
  * CODE STATUS HIGHLIGHTS
@@ -23,7 +24,7 @@
 
 /*
  * Copyright (C) 2003 David Brownell
- * Copyright (C) 2003 NetChip Technologies
+ * Copyright (C) 2003-2005 PLX Technology, Inc.
  *
  * 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
@@ -69,8 +70,8 @@
 #include <asm/unaligned.h>
 
 
-#define	DRIVER_DESC		"NetChip 2280 USB Peripheral Controller"
-#define	DRIVER_VERSION		"2004 Jan 14"
+#define	DRIVER_DESC		"PLX NET2280 USB Peripheral Controller"
+#define	DRIVER_VERSION		"2005 Feb 03"
 
 #define	DMA_ADDR_INVALID	(~(dma_addr_t)0)
 #define	EP_DONTUSE		13	/* nonzero */
@@ -113,6 +114,16 @@ static ushort fifo_mode = 0;
 /* "modprobe net2280 fifo_mode=1" etc */
 module_param (fifo_mode, ushort, 0644);
 
+/* enable_suspend -- When enabled, the driver will respond to
+ * USB suspend requests by powering down the NET2280.  Otherwise,
+ * USB suspend requests will be ignored.  This is acceptible for
+ * self-powered devices, and helps avoid some quirks.
+ */
+static int enable_suspend = 0;
+
+/* "modprobe net2280 enable_suspend=1" etc */
+module_param (enable_suspend, bool, S_IRUGO);
+
 
 #define	DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
 
@@ -2561,6 +2572,8 @@ static void handle_stat1_irqs (struct ne
 		if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) {
 			if (dev->driver->suspend)
 				dev->driver->suspend (&dev->gadget);
+			if (!enable_suspend)
+				stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
 		} else {
 			if (dev->driver->resume)
 				dev->driver->resume (&dev->gadget);
diff -puN drivers/usb/gadget/omap_udc.c~bk-usb drivers/usb/gadget/omap_udc.c
--- 25/drivers/usb/gadget/omap_udc.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/gadget/omap_udc.c	2005-02-09 19:12:30.000000000 -0800
@@ -2,7 +2,7 @@
  * omap_udc.c -- for OMAP full speed udc; most chips support OTG.
  *
  * Copyright (C) 2004 Texas Instruments, Inc.
- * Copyright (C) 2004 David Brownell
+ * Copyright (C) 2004-2005 David Brownell
  *
  * 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
@@ -2046,7 +2046,10 @@ int usb_gadget_register_driver (struct u
 			pullup_disable (udc);
 	}
 
-	if (machine_is_omap_innovator())
+	/* boards that don't have VBUS sensing can't autogate 48MHz;
+	 * can't enter deep sleep while a gadget driver is active.
+	 */
+	if (machine_is_omap_innovator() || machine_is_omap_osk())
 		omap_vbus_session(&udc->gadget, 1);
 
 done:
@@ -2064,7 +2067,7 @@ int usb_gadget_unregister_driver (struct
 	if (!driver || driver != udc->driver)
 		return -EINVAL;
 
-	if (machine_is_omap_innovator())
+	if (machine_is_omap_innovator() || machine_is_omap_osk())
 		omap_vbus_session(&udc->gadget, 0);
 
 	if (udc->transceiver)
@@ -2157,13 +2160,13 @@ static void proc_ep_show(struct seq_file
 		}
 }
 
-static char *trx_mode(unsigned m)
+static char *trx_mode(unsigned m, int enabled)
 {
 	switch (m) {
-	case 3:
-	case 0:		return "6wire";
+	case 0:		return enabled ? "*6wire" : "unused";
 	case 1:		return "4wire";
 	case 2:		return "3wire";
+	case 3: 	return "6wire";
 	default:	return "unknown";
 	}
 }
@@ -2171,17 +2174,20 @@ static char *trx_mode(unsigned m)
 static int proc_otg_show(struct seq_file *s)
 {
 	u32		tmp;
+	u32		trans;
 
 	tmp = OTG_REV_REG;
-	seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %08x\n",
-		tmp >> 4, tmp & 0xf,
-		USB_TRANSCEIVER_CTRL_REG);
+	trans = USB_TRANSCEIVER_CTRL_REG;
+	seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %03x\n",
+		tmp >> 4, tmp & 0xf, trans);
 	tmp = OTG_SYSCON_1_REG;
 	seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s,"
 			FOURBITS "\n", tmp,
-		trx_mode(USB2_TRX_MODE(tmp)),
-		trx_mode(USB1_TRX_MODE(tmp)),
-		trx_mode(USB0_TRX_MODE(tmp)),
+		trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R),
+		trx_mode(USB1_TRX_MODE(tmp), trans & CONF_USB1_UNI_R),
+		(USB0_TRX_MODE(tmp) == 0)
+			? "internal"
+			: trx_mode(USB0_TRX_MODE(tmp), 1),
 		(tmp & OTG_IDLE_EN) ? " !otg" : "",
 		(tmp & HST_IDLE_EN) ? " !host" : "",
 		(tmp & DEV_IDLE_EN) ? " !dev" : "",
diff -puN drivers/usb/gadget/rndis.c~bk-usb drivers/usb/gadget/rndis.c
--- 25/drivers/usb/gadget/rndis.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/gadget/rndis.c	2005-02-09 19:12:30.000000000 -0800
@@ -730,7 +730,7 @@ static int gen_ndis_set_resp (u8 configN
 
 		/* FIXME use these NDIS_PACKET_TYPE_* bitflags to
 		 * filter packets in hard_start_xmit()
-		 * NDIS_PACKET_TYPE_x == CDC_PACKET_TYPE_x for x in:
+		 * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
 		 *	PROMISCUOUS, DIRECTED,
 		 *	MULTICAST, ALL_MULTICAST, BROADCAST
 		 */
diff -puN drivers/usb/gadget/serial.c~bk-usb drivers/usb/gadget/serial.c
--- 25/drivers/usb/gadget/serial.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/gadget/serial.c	2005-02-09 19:12:30.000000000 -0800
@@ -45,6 +45,7 @@
 #include <asm/uaccess.h>
 
 #include <linux/usb_ch9.h>
+#include <linux/usb_cdc.h>
 #include <linux/usb_gadget.h>
 
 #include "gadget_chips.h"
@@ -122,80 +123,6 @@ do {									\
 })
 
 
-/* CDC-ACM Defines and Structures */
-
-#define USB_CDC_SUBCLASS_ACM			2
-
-#define USB_CDC_CTRL_PROTO_NONE			0
-#define USB_CDC_CTRL_PROTO_AT			1
-#define USB_CDC_CTRL_PROTO_VENDOR		0xff
-
-#define USB_CDC_SUBTYPE_HEADER			0
-#define USB_CDC_SUBTYPE_CALL_MGMT		1
-#define USB_CDC_SUBTYPE_ACM			2
-#define USB_CDC_SUBTYPE_UNION			6
-
-#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT		0x01
-#define USB_CDC_CALL_MGMT_CAP_DATA_INTF		0x02
-
-#define USB_CDC_REQ_SET_LINE_CODING		0x20
-#define USB_CDC_REQ_GET_LINE_CODING		0x21
-#define USB_CDC_REQ_SET_CONTROL_LINE_STATE	0x22
-
-#define USB_CDC_1_STOP_BITS			0
-#define USB_CDC_1_5_STOP_BITS			1
-#define USB_CDC_2_STOP_BITS			2
-
-#define USB_CDC_NO_PARITY			0
-#define USB_CDC_ODD_PARITY			1
-#define USB_CDC_EVEN_PARITY			2
-#define USB_CDC_MARK_PARITY			3
-#define USB_CDC_SPACE_PARITY			4
-
-/* Header Functional Descriptor from CDC spec 5.2.3.1 */
-struct usb_cdc_header_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-	u16	bcdCDC;
-} __attribute__ ((packed));
-
-/* Call Management Descriptor from CDC spec 5.2.3.3 */
-struct usb_cdc_call_mgmt_desc {
-	u8  bLength;
-	u8  bDescriptorType;
-	u8  bDescriptorSubType;
-	u8  bmCapabilities;
-	u8  bDataInterface;
-} __attribute__ ((packed));
-
-/* Abstract Control Management Descriptor from CDC spec 5.2.3.4 */
-struct usb_cdc_acm_desc {
-	u8  bLength;
-	u8  bDescriptorType;
-	u8  bDescriptorSubType;
-	u8  bmCapabilities;
-} __attribute__ ((packed));
-
-/* Union Functional Descriptor from CDC spec 5.2.3.8 */
-struct usb_cdc_union_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-	u8	bMasterInterface0;
-	u8	bSlaveInterface0;
-	/* ... and there could be other slave interfaces */
-} __attribute__ ((packed));
-
-/* Line Coding Structure from CDC spec 6.2.13 */
-struct usb_cdc_line_coding {
-	u32 dwDTERate;
-	u8 bCharFormat;
-	u8 bParityType;
-	u8 bDataBits;
-} __attribute__ ((packed));
-
-
 /* Defines */
 
 #define GS_VERSION_STR			"v2.0"
@@ -542,7 +469,7 @@ static const struct usb_interface_descri
 	.bNumEndpoints =	1,
 	.bInterfaceClass =	USB_CLASS_COMM,
 	.bInterfaceSubClass =	USB_CDC_SUBCLASS_ACM,
-	.bInterfaceProtocol =	USB_CDC_CTRL_PROTO_AT,
+	.bInterfaceProtocol =	USB_CDC_ACM_PROTO_AT_V25TER,
 	.iInterface =		GS_CONTROL_STR_ID,
 };
 
@@ -560,29 +487,29 @@ static const struct usb_interface_descri
 static const struct usb_cdc_header_desc gs_header_desc = {
 	.bLength =		sizeof(gs_header_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType =	USB_CDC_SUBTYPE_HEADER,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
 	.bcdCDC =		__constant_cpu_to_le16(0x0110),
 };
 
-static const struct usb_cdc_call_mgmt_desc gs_call_mgmt_descriptor = {
+static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = {
 	.bLength =  		sizeof(gs_call_mgmt_descriptor),
 	.bDescriptorType = 	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType = 	USB_CDC_SUBTYPE_CALL_MGMT,
+	.bDescriptorSubType = 	USB_CDC_CALL_MANAGEMENT_TYPE,
 	.bmCapabilities = 	0,
 	.bDataInterface = 	1,	/* index of data interface */
 };
 
-static struct usb_cdc_acm_desc gs_acm_descriptor = {
+static struct usb_cdc_acm_descriptor gs_acm_descriptor = {
 	.bLength =  		sizeof(gs_acm_descriptor),
 	.bDescriptorType = 	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType = 	USB_CDC_SUBTYPE_ACM,
+	.bDescriptorSubType = 	USB_CDC_ACM_TYPE,
 	.bmCapabilities = 	0,
 };
 
 static const struct usb_cdc_union_desc gs_union_desc = {
 	.bLength =		sizeof(gs_union_desc),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
-	.bDescriptorSubType =	USB_CDC_SUBTYPE_UNION,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
 	.bMasterInterface0 =	0,	/* index of control interface */
 	.bSlaveInterface0 =	1,	/* index of data interface */
 };
@@ -1674,6 +1601,9 @@ static int gs_setup(struct usb_gadget *g
 	int ret = -EOPNOTSUPP;
 	struct gs_dev *dev = get_gadget_data(gadget);
 	struct usb_request *req = dev->dev_ctrl_req;
+	u16 wIndex = ctrl->wIndex;
+	u16 wValue = ctrl->wValue;
+	u16 wLength = ctrl->wLength;
 
 	switch (ctrl->bRequestType & USB_TYPE_MASK) {
 	case USB_TYPE_STANDARD:
@@ -1686,15 +1616,15 @@ static int gs_setup(struct usb_gadget *g
 
 	default:
 		printk(KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n",
-			ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
-			ctrl->wIndex, ctrl->wLength);
+			ctrl->bRequestType, ctrl->bRequest,
+			wValue, wIndex, wLength);
 		break;
 	}
 
 	/* respond with data transfer before status phase? */
 	if (ret >= 0) {
 		req->length = ret;
-		req->zero = ret < ctrl->wLength
+		req->zero = ret < wLength
 				&& (ret % gadget->ep0->maxpacket) == 0;
 		ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
 		if (ret < 0) {
@@ -1715,15 +1645,18 @@ static int gs_setup_standard(struct usb_
 	int ret = -EOPNOTSUPP;
 	struct gs_dev *dev = get_gadget_data(gadget);
 	struct usb_request *req = dev->dev_ctrl_req;
+	u16 wIndex = ctrl->wIndex;
+	u16 wValue = ctrl->wValue;
+	u16 wLength = ctrl->wLength;
 
 	switch (ctrl->bRequest) {
 	case USB_REQ_GET_DESCRIPTOR:
 		if (ctrl->bRequestType != USB_DIR_IN)
 			break;
 
-		switch (ctrl->wValue >> 8) {
+		switch (wValue >> 8) {
 		case USB_DT_DEVICE:
-			ret = min(ctrl->wLength,
+			ret = min(wLength,
 				(u16)sizeof(struct usb_device_descriptor));
 			memcpy(req->buf, &gs_device_desc, ret);
 			break;
@@ -1732,7 +1665,7 @@ static int gs_setup_standard(struct usb_
 		case USB_DT_DEVICE_QUALIFIER:
 			if (!gadget->is_dualspeed)
 				break;
-			ret = min(ctrl->wLength,
+			ret = min(wLength,
 				(u16)sizeof(struct usb_qualifier_descriptor));
 			memcpy(req->buf, &gs_qualifier_desc, ret);
 			break;
@@ -1744,18 +1677,18 @@ static int gs_setup_standard(struct usb_
 #endif /* CONFIG_USB_GADGET_DUALSPEED */
 		case USB_DT_CONFIG:
 			ret = gs_build_config_buf(req->buf, gadget->speed,
-				ctrl->wValue >> 8, ctrl->wValue & 0xff,
+				wValue >> 8, wValue & 0xff,
 				gadget->is_otg);
 			if (ret >= 0)
-				ret = min(ctrl->wLength, (u16)ret);
+				ret = min(wLength, (u16)ret);
 			break;
 
 		case USB_DT_STRING:
 			/* wIndex == language code. */
 			ret = usb_gadget_get_string(&gs_string_table,
-				ctrl->wValue & 0xff, req->buf);
+				wValue & 0xff, req->buf);
 			if (ret >= 0)
-				ret = min(ctrl->wLength, (u16)ret);
+				ret = min(wLength, (u16)ret);
 			break;
 		}
 		break;
@@ -1764,7 +1697,7 @@ static int gs_setup_standard(struct usb_
 		if (ctrl->bRequestType != 0)
 			break;
 		spin_lock(&dev->dev_lock);
-		ret = gs_set_config(dev, ctrl->wValue);
+		ret = gs_set_config(dev, wValue);
 		spin_unlock(&dev->dev_lock);
 		break;
 
@@ -1772,18 +1705,19 @@ static int gs_setup_standard(struct usb_
 		if (ctrl->bRequestType != USB_DIR_IN)
 			break;
 		*(u8 *)req->buf = dev->dev_config;
-		ret = min(ctrl->wLength, (u16)1);
+		ret = min(wLength, (u16)1);
 		break;
 
 	case USB_REQ_SET_INTERFACE:
 		if (ctrl->bRequestType != USB_RECIP_INTERFACE
-		|| !dev->dev_config || ctrl->wIndex >= GS_MAX_NUM_INTERFACES)
+				|| !dev->dev_config
+				|| wIndex >= GS_MAX_NUM_INTERFACES)
 			break;
 		if (dev->dev_config == GS_BULK_CONFIG_ID
-		&& ctrl->wIndex != GS_BULK_INTERFACE_ID)
+				&& wIndex != GS_BULK_INTERFACE_ID)
 			break;
 		/* no alternate interface settings */
-		if (ctrl->wValue != 0)
+		if (wValue != 0)
 			break;
 		spin_lock(&dev->dev_lock);
 		/* PXA hardware partially handles SET_INTERFACE;
@@ -1794,7 +1728,7 @@ static int gs_setup_standard(struct usb_
 			goto set_interface_done;
 		}
 		if (dev->dev_config != GS_BULK_CONFIG_ID
-		&& ctrl->wIndex == GS_CONTROL_INTERFACE_ID) {
+				&& wIndex == GS_CONTROL_INTERFACE_ID) {
 			if (dev->dev_notify_ep) {
 				usb_ep_disable(dev->dev_notify_ep);
 				usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc);
@@ -1814,21 +1748,21 @@ set_interface_done:
 		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
 		|| dev->dev_config == GS_NO_CONFIG_ID)
 			break;
-		if (ctrl->wIndex >= GS_MAX_NUM_INTERFACES
-		|| (dev->dev_config == GS_BULK_CONFIG_ID
-		&& ctrl->wIndex != GS_BULK_INTERFACE_ID)) {
+		if (wIndex >= GS_MAX_NUM_INTERFACES
+				|| (dev->dev_config == GS_BULK_CONFIG_ID
+				&& wIndex != GS_BULK_INTERFACE_ID)) {
 			ret = -EDOM;
 			break;
 		}
 		/* no alternate interface settings */
 		*(u8 *)req->buf = 0;
-		ret = min(ctrl->wLength, (u16)1);
+		ret = min(wLength, (u16)1);
 		break;
 
 	default:
 		printk(KERN_ERR "gs_setup: unknown standard request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n",
-			ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
-			ctrl->wIndex, ctrl->wLength);
+			ctrl->bRequestType, ctrl->bRequest,
+			wValue, wIndex, wLength);
 		break;
 	}
 
@@ -1842,10 +1776,13 @@ static int gs_setup_class(struct usb_gad
 	struct gs_dev *dev = get_gadget_data(gadget);
 	struct gs_port *port = dev->dev_port[0];	/* ACM only has one port */
 	struct usb_request *req = dev->dev_ctrl_req;
+	u16 wIndex = ctrl->wIndex;
+	u16 wValue = ctrl->wValue;
+	u16 wLength = ctrl->wLength;
 
 	switch (ctrl->bRequest) {
 	case USB_CDC_REQ_SET_LINE_CODING:
-		ret = min(ctrl->wLength,
+		ret = min(wLength,
 			(u16)sizeof(struct usb_cdc_line_coding));
 		if (port) {
 			spin_lock(&port->port_lock);
@@ -1856,7 +1793,7 @@ static int gs_setup_class(struct usb_gad
 
 	case USB_CDC_REQ_GET_LINE_CODING:
 		port = dev->dev_port[0];	/* ACM only has one port */
-		ret = min(ctrl->wLength,
+		ret = min(wLength,
 			(u16)sizeof(struct usb_cdc_line_coding));
 		if (port) {
 			spin_lock(&port->port_lock);
@@ -1871,8 +1808,8 @@ static int gs_setup_class(struct usb_gad
 
 	default:
 		printk(KERN_ERR "gs_setup: unknown class request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n",
-			ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
-			ctrl->wIndex, ctrl->wLength);
+			ctrl->bRequestType, ctrl->bRequest,
+			wValue, wIndex, wLength);
 		break;
 	}
 
@@ -2272,7 +2209,7 @@ static int gs_alloc_ports(struct gs_dev 
 		memset(port, 0, sizeof(struct gs_port));
 		port->port_dev = dev;
 		port->port_num = i;
-		port->port_line_coding.dwDTERate = GS_DEFAULT_DTE_RATE;
+		port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE);
 		port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT;
 		port->port_line_coding.bParityType = GS_DEFAULT_PARITY;
 		port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS;
@@ -2324,6 +2261,7 @@ static void gs_free_ports(struct gs_dev 
 				}
 				spin_unlock_irqrestore(&port->port_lock, flags);
 			} else {
+				spin_unlock_irqrestore(&port->port_lock, flags);
 				kfree(port);
 			}
 
diff -puN drivers/usb/host/Kconfig~bk-usb drivers/usb/host/Kconfig
--- 25/drivers/usb/host/Kconfig~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/host/Kconfig	2005-02-09 19:12:30.000000000 -0800
@@ -66,6 +66,35 @@ config USB_OHCI_HCD
 	  To compile this driver as a module, choose M here: the
 	  module will be called ohci-hcd.
 
+config USB_OHCI_HCD_PPC_SOC
+	bool "OHCI support for on-chip PPC USB controller"
+	depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx)
+	default y
+	select USB_OHCI_BIG_ENDIAN
+	---help---
+	  Enables support for the USB controller on the MPC52xx or
+	  STB03xxx processor chip.  If unsure, say Y.
+
+config USB_OHCI_HCD_PCI
+	bool "OHCI support for PCI-bus USB controllers"
+	depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx)
+	default y
+	select USB_OHCI_LITTLE_ENDIAN
+	---help---
+	  Enables support for PCI-bus plug-in USB controller cards.
+	  If unsure, say Y.
+
+config USB_OHCI_BIG_ENDIAN
+	bool
+	depends on USB_OHCI_HCD
+	default n
+
+config USB_OHCI_LITTLE_ENDIAN
+	bool
+	depends on USB_OHCI_HCD
+	default n if STB03xxx || PPC_MPC52xx
+	default y
+
 config USB_UHCI_HCD
 	tristate "UHCI HCD (most Intel and VIA) support"
 	depends on USB && PCI
diff -puN drivers/usb/host/ohci-dbg.c~bk-usb drivers/usb/host/ohci-dbg.c
--- 25/drivers/usb/host/ohci-dbg.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/host/ohci-dbg.c	2005-02-09 19:12:30.000000000 -0800
@@ -138,7 +138,7 @@ ohci_dump_status (struct ohci_hcd *contr
 	ohci_dbg_sw (controller, next, size,
 		"OHCI %d.%d, %s legacy support registers\n",
 		0x03 & (temp >> 4), (temp & 0x0f),
-		(temp & 0x10) ? "with" : "NO");
+		(temp & 0x0100) ? "with" : "NO");
 
 	temp = ohci_readl (controller, &regs->control);
 	ohci_dbg_sw (controller, next, size,
@@ -328,7 +328,7 @@ static void ohci_dump_td (const struct o
 			hc32_to_cpup (ohci, &td->hwCBP) & ~0x0fff,
 			hc32_to_cpup (ohci, &td->hwBE));
 		for (i = 0; i < MAXPSW; i++) {
-			u16	psw = hc16_to_cpup (ohci, &td->hwPSW [i]);
+			u16	psw = ohci_hwPSW (ohci, td, i);
 			int	cc = (psw >> 12) & 0x0f;
 			ohci_dbg (ohci, "    psw [%d] = %2x, CC=%x %s=%d\n", i,
 				psw, cc,
diff -puN drivers/usb/host/ohci.h~bk-usb drivers/usb/host/ohci.h
--- 25/drivers/usb/host/ohci.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/host/ohci.h	2005-02-09 19:12:30.000000000 -0800
@@ -111,8 +111,10 @@ struct td {
   	__hc32		hwNextTD;	/* Next TD Pointer */
   	__hc32		hwBE;		/* Memory Buffer End Pointer */
 
-	/* PSW is only for ISO */
-#define MAXPSW 1		/* hardware allows 8 */
+	/* PSW is only for ISO.  Only 1 PSW entry is used, but on
+	 * big-endian PPC hardware that's the second entry.
+	 */
+#define MAXPSW	2
   	__hc16		hwPSW [MAXPSW];
 
 	/* rest are purely for the driver's use */
@@ -183,7 +185,7 @@ struct ohci_hcca {
 	/* 
 	 * OHCI defines u16 frame_no, followed by u16 zero pad.
 	 * Since some processors can't do 16 bit bus accesses,
-	 * portable access must be a 32 bit byteswapped access.
+	 * portable access must be a 32 bits wide.
 	 */
 	__hc32	frame_no;		/* current frame number */
 	__hc32	done_head;		/* info returned for an interrupt */
@@ -191,8 +193,6 @@ struct ohci_hcca {
 	u8	what [4];		/* spec only identifies 252 bytes :) */
 } __attribute__ ((aligned(256)));
 
-#define ohci_frame_no(ohci) ((u16)hc32_to_cpup(ohci,&(ohci)->hcca->frame_no))
-  
 /*
  * This is the structure of the OHCI controller's memory mapped I/O region.
  * You must use readl() and writel() (in <asm/io.h>) to access these fields!!
@@ -554,6 +554,44 @@ static inline u32 hc32_to_cpup (const st
 
 /*-------------------------------------------------------------------------*/
 
+/* HCCA frame number is 16 bits, but is accessed as 32 bits since not all
+ * hardware handles 16 bit reads.  That creates a different confusion on
+ * some big-endian SOC implementations.  Same thing happens with PSW access.
+ */
+
+#ifdef CONFIG_STB03xxx
+#define OHCI_BE_FRAME_NO_SHIFT	16
+#else
+#define OHCI_BE_FRAME_NO_SHIFT	0
+#endif
+
+static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
+{
+	u32 tmp;
+	if (big_endian(ohci)) {
+		tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
+		tmp >>= OHCI_BE_FRAME_NO_SHIFT;
+	} else
+		tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no);
+
+	return (u16)tmp;
+}
+
+static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
+                                 const struct td *td, int index)
+{
+	return (__hc16 *)(big_endian(ohci) ?
+			&td->hwPSW[index ^ 1] : &td->hwPSW[index]);
+}
+
+static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci,
+                               const struct td *td, int index)
+{
+	return hc16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index));
+}
+
+/*-------------------------------------------------------------------------*/
+
 static inline void disable (struct ohci_hcd *ohci)
 {
 	ohci_to_hcd(ohci)->state = USB_STATE_HALT;
diff -puN drivers/usb/host/ohci-hcd.c~bk-usb drivers/usb/host/ohci-hcd.c
--- 25/drivers/usb/host/ohci-hcd.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/host/ohci-hcd.c	2005-02-09 19:12:30.000000000 -0800
@@ -148,10 +148,11 @@ static void ohci_stop (struct usb_hcd *h
 #include "ohci-q.c"
 
 
-/* Some boards don't support per-port power switching */
-static int power_switching = 0;
-module_param (power_switching, bool, 0);
-MODULE_PARM_DESC (power_switching, "true (not default) to switch port power");
+/* Some boards misreport power switching/overcurrent */
+static int distrust_firmware = 1;
+module_param (distrust_firmware, bool, 0);
+MODULE_PARM_DESC (distrust_firmware,
+	"true to distrust firmware power/overcurrent setup");
 
 /* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */
 static int no_handshake = 0;
@@ -532,8 +533,9 @@ static int ohci_run (struct ohci_hcd *oh
 	// flush the writes
 	(void) ohci_readl (ohci, &ohci->regs->control);
 	msleep(temp);
-	if (power_switching) {
-		unsigned ports = roothub_a (ohci) & RH_A_NDP; 
+	temp = roothub_a (ohci);
+	if (!(temp & RH_A_NPS)) {
+		unsigned ports = temp & RH_A_NDP; 
 
 		/* power down each port */
 		for (temp = 0; temp < ports; temp++)
@@ -624,21 +626,16 @@ retry:
 		/* NSC 87560 and maybe others */
 		temp |= RH_A_NOCP;
 		temp &= ~(RH_A_POTPGT | RH_A_NPS);
-	} else if (power_switching) {
-		/* act like most external hubs:  use per-port power
-		 * switching and overcurrent reporting.
-		 */
-		temp &= ~(RH_A_NPS | RH_A_NOCP);
-		temp |= RH_A_PSM | RH_A_OCPM;
-	} else {
+		ohci_writel (ohci, temp, &ohci->regs->roothub.a);
+	} else if ((ohci->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
 		/* hub power always on; required for AMD-756 and some
 		 * Mac platforms.  ganged overcurrent reporting, if any.
 		 */
 		temp |= RH_A_NPS;
+		ohci_writel (ohci, temp, &ohci->regs->roothub.a);
 	}
-	ohci_writel (ohci, temp, &ohci->regs->roothub.a);
 	ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);
-	ohci_writel (ohci, power_switching ? RH_B_PPCM : 0,
+	ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM,
 						&ohci->regs->roothub.b);
 	// flush those writes
 	(void) ohci_readl (ohci, &ohci->regs->control);
@@ -646,7 +643,7 @@ retry:
 	spin_unlock_irq (&ohci->lock);
 
 	// POTPGT delay is bits 24-31, in 2 ms units.
-	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+	mdelay ((temp >> 23) & 0x1fe);
 	bus = &ohci_to_hcd(ohci)->self;
 	ohci_to_hcd(ohci)->state = USB_STATE_RUNNING;
 
@@ -901,12 +898,17 @@ MODULE_LICENSE ("GPL");
 #include "ohci-au1xxx.c"
 #endif
 
+#ifdef CONFIG_USB_OHCI_HCD_PPC_SOC
+#include "ohci-ppc-soc.c"
+#endif
+
 #if !(defined(CONFIG_PCI) \
       || defined(CONFIG_SA1111) \
       || defined(CONFIG_ARCH_OMAP) \
       || defined (CONFIG_ARCH_LH7A404) \
       || defined (CONFIG_PXA27x) \
       || defined (CONFIG_SOC_AU1X00) \
+      || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
 	)
 #error "missing bus glue for ohci-hcd"
 #endif
diff -puN drivers/usb/host/ohci-omap.c~bk-usb drivers/usb/host/ohci-omap.c
--- 25/drivers/usb/host/ohci-omap.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/host/ohci-omap.c	2005-02-09 19:12:30.000000000 -0800
@@ -33,11 +33,27 @@
 #error "This file is OMAP bus glue.  CONFIG_OMAP must be defined."
 #endif
 
+#ifdef CONFIG_TPS65010
+#include <asm/arch/tps65010.h>
+#else
+
+#define LOW	0
+#define HIGH	1
+
+#define GPIO1	1
+
+static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
+{
+	return 0;
+}
+
+#endif
+
 extern int usb_disabled(void);
 extern int ocpi_enable(void);
 
 /*
- * OHCI clock initialization for OMAP-1510 and 1610
+ * OHCI clock initialization for OMAP-1510 and 16xx
  */
 static int omap_ohci_clock_power(int on)
 {
@@ -78,7 +94,8 @@ static int omap_ohci_clock_power(int on)
 }
 
 /*
- * Hardware specific transceiver power on/off
+ * Board specific gang-switched transceiver power on/off.
+ * NOTE:  OSK supplies power from DC, not battery.
  */
 static int omap_ohci_transceiver_power(int on)
 {
@@ -87,17 +104,15 @@ static int omap_ohci_transceiver_power(i
 			fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
 				| ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), 
 			       INNOVATOR_FPGA_CAM_USB_CONTROL);
-		else if (machine_is_omap_osk()) {
-			/* FIXME: GPIO1 -> 1 on the TPS65010 I2C chip */
-		}
+		else if (machine_is_omap_osk())
+			tps65010_set_gpio_out_value(GPIO1, LOW);
 	} else {
 		if (machine_is_omap_innovator() && cpu_is_omap1510())
 			fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
 				& ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), 
 			       INNOVATOR_FPGA_CAM_USB_CONTROL);
-		else if (machine_is_omap_osk()) {
-			/* FIXME: GPIO1 -> 0 on the TPS65010 I2C chip */
-		}
+		else if (machine_is_omap_osk())
+			tps65010_set_gpio_out_value(GPIO1, HIGH);
 	}
 
 	return 0;
@@ -177,6 +192,7 @@ static int omap_start_hc(struct ohci_hcd
 {
 	struct omap_usb_config	*config = pdev->dev.platform_data;
 	int			need_transceiver = (config->otg != 0);
+	int			ret;
 
 	dev_dbg(&pdev->dev, "starting USB Controller\n");
 
@@ -213,21 +229,44 @@ static int omap_start_hc(struct ohci_hcd
 	}
 #endif
 
-	if (machine_is_omap_osk()) {
-		omap_request_gpio(9);
-		omap_set_gpio_direction(9, 1);
-		omap_set_gpio_dataout(9, 1);
-	}
-
 	omap_ohci_clock_power(1);
 
-	omap_ohci_transceiver_power(1);
-
 	if (cpu_is_omap1510()) {
 		omap_1510_local_bus_power(1);
 		omap_1510_local_bus_init();
 	}
 
+	if ((ret = ohci_init(ohci)) < 0)
+		return ret;
+
+	/* board-specific power switching and overcurrent support */
+	if (machine_is_omap_osk() || machine_is_omap_innovator()) {
+		u32	rh = roothub_a (ohci);
+
+		/* power switching (ganged by default) */
+		rh &= ~RH_A_NPS;
+
+		/* TPS2045 switch for internal transceiver (port 1) */
+		if (machine_is_omap_osk()) {
+			ohci->power_budget = 250;
+
+			rh &= ~RH_A_NOCP;
+
+			/* gpio9 for overcurrent detction */
+			omap_cfg_reg(W8_1610_GPIO9);
+			omap_request_gpio(9);
+			omap_set_gpio_direction(9, 1 /* IN */);
+
+			/* for paranoia's sake:  disable USB.PUEN */
+			omap_cfg_reg(W4_USB_HIGHZ);
+		}
+		ohci_writel(ohci, rh, &ohci->regs->roothub.a);
+		// distrust_firmware = 0;
+	}
+
+	/* FIXME khubd hub requests should manage power switching */
+	omap_ohci_transceiver_power(1);
+
 	/* board init will have already handled HMC and mux setup.
 	 * any external transceiver should already be initialized
 	 * too, so all configured ports use the right signaling now.
@@ -288,7 +327,8 @@ int usb_hcd_omap_probe (const struct hc_
 	}
 
 	if (!request_mem_region(pdev->resource[0].start, 
-				pdev->resource[0].end - pdev->resource[0].start + 1, hcd_name)) {
+			pdev->resource[0].end - pdev->resource[0].start + 1,
+			hcd_name)) {
 		dev_dbg(&pdev->dev, "request_mem_region failed\n");
 		return -EBUSY;
 	}
@@ -307,31 +347,31 @@ int usb_hcd_omap_probe (const struct hc_
 	hcd->regs = (void *)pdev->resource[0].start;
 	hcd->self.controller = &pdev->dev;
 
-	retval = omap_start_hc(ohci, pdev);
-	if (retval < 0)
-		goto err2;
-
 	retval = hcd_buffer_create (hcd);
 	if (retval != 0) {
 		dev_dbg(&pdev->dev, "pool alloc fail\n");
 		goto err2;
 	}
 
+	retval = omap_start_hc(ohci, pdev);
+	if (retval < 0)
+		goto err2;
+
 	retval = request_irq (hcd->irq, usb_hcd_irq, 
-			      SA_INTERRUPT, hcd->driver->description, hcd);
+			      SA_INTERRUPT, hcd_name, hcd);
 	if (retval != 0) {
 		dev_dbg(&pdev->dev, "request_irq failed\n");
 		retval = -EBUSY;
 		goto err3;
 	}
 
-	dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
+	dev_info(&pdev->dev, "%s at 0x%p, irq %d\n",
+		hcd->product_desc, hcd->regs, hcd->irq);
 
 	hcd->self.bus_name = pdev->dev.bus_id;
 	usb_register_bus (&hcd->self);
 
-	if ((retval = driver->start (hcd)) < 0) 
-	{
+	if ((retval = driver->start (hcd)) < 0) {
 		usb_hcd_omap_remove(hcd, pdev);
 		return retval;
 	}
@@ -404,9 +444,6 @@ ohci_omap_start (struct usb_hcd *hcd)
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	int		ret;
 
-	if ((ret = ohci_init(ohci)) < 0)
-		return ret;
-
 	config = hcd->self.controller->platform_data;
 	if (config->otg || config->rwc)
 		writel(OHCI_CTRL_RWC, &ohci->regs->control);
@@ -499,6 +536,8 @@ static int ohci_omap_suspend(struct devi
 	struct ohci_hcd	*ohci = hcd_to_ohci(dev_get_drvdata(dev));
 	int		status = -EINVAL;
 
+	if (level != SUSPEND_POWER_DOWN)
+		return 0;
 	if (state <= dev->power.power_state)
 		return 0;
 
@@ -525,6 +564,9 @@ static int ohci_omap_resume(struct devic
 	struct ohci_hcd	*ohci = hcd_to_ohci(dev_get_drvdata(dev));
 	int		status = 0;
 
+	if (level != RESUME_POWER_ON)
+		return 0;
+
 	switch (dev->power.power_state) {
 	case 0:
 		break;
diff -puN /dev/null drivers/usb/host/ohci-ppc-soc.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/host/ohci-ppc-soc.c	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,299 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ * (C) Copyright 2003-2005 MontaVista Software Inc.
+ * 
+ * Bus Glue for PPC On-Chip OHCI driver
+ * Tested on Freescale MPC5200 and IBM STB04xxx
+ *
+ * Modified by Dale Farnsworth <dale@farnsworth.org> from ohci-sa1111.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/usb.h>
+
+static void usb_hcd_ppc_soc_remove(struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_ppc_soc_probe - initialize On-Chip HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
+			  struct usb_hcd **hcd_out,
+			  struct platform_device *pdev)
+{
+	int retval;
+	struct usb_hcd *hcd = 0;
+	struct ohci_hcd	*ohci;
+	struct resource *res;
+	int irq;
+	struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
+
+	pr_debug("initializing PPC-SOC USB Controller\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		pr_debug(__FILE__ ": no irq\n");
+		return -ENODEV;
+	}
+	irq = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		pr_debug(__FILE__ ": no reg addr\n");
+		return -ENODEV;
+	}
+	if (!request_mem_region(res->start, res->end - res->start + 1,
+					hcd_name)) {
+		pr_debug(__FILE__ ": request_mem_region failed\n");
+		return -EBUSY;
+	}
+
+	if (pd->start && (retval = pd->start(pdev)))
+		goto err0;
+
+	hcd = usb_create_hcd(driver);
+	if (!hcd){
+		pr_debug(__FILE__ ": hcd_alloc failed\n");
+		retval = -ENOMEM;
+		goto err1;
+	}
+
+	ohci = hcd_to_ohci(hcd);
+
+	ohci->flags |= OHCI_BIG_ENDIAN;
+
+	ohci_hcd_init(ohci);
+
+	hcd->irq = irq;
+	hcd->regs = (struct ohci_regs *) ioremap(res->start,
+						res->end - res->start + 1);
+	if (!hcd->regs) {
+		pr_debug(__FILE__ ": ioremap failed\n");
+		retval = -ENOMEM;
+		goto err2;
+	}
+
+	hcd->self.controller = &pdev->dev;
+
+	retval = hcd_buffer_create(hcd);
+	if (retval) {
+		pr_debug(__FILE__ ": pool alloc fail\n");
+		goto err3;
+	}
+
+	retval = request_irq(hcd->irq, usb_hcd_irq, SA_INTERRUPT,
+				hcd_name, hcd);
+	if (retval) {
+		pr_debug(__FILE__ ": request_irq failed, returned %d\n",
+								retval);
+		retval = -EBUSY;
+		goto err4;
+	}
+
+	info("%s (PPC-SOC) at 0x%p, irq %d\n",
+	      hcd_name, hcd->regs, hcd->irq);
+
+	hcd->self.bus_name = "PPC-SOC USB";
+
+	usb_register_bus(&hcd->self);
+
+	if ((retval = driver->start(hcd)) < 0) {
+		usb_hcd_ppc_soc_remove(hcd, pdev);
+		return retval;
+	}
+
+	*hcd_out = hcd;
+	return 0;
+
+ err4:
+	hcd_buffer_destroy(hcd);
+ err3:
+	iounmap(hcd->regs);
+ err2:
+	dev_set_drvdata(&pdev->dev, NULL);
+ 	usb_put_hcd(hcd);
+ err1:
+	pr_debug("Removing PPC-SOC USB Controller\n");
+	if (pd && pd->stop)
+		pd->stop(pdev);
+ err0:
+	release_mem_region(res->start, res->end - res->start + 1);
+	return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_ppc_soc_remove - shutdown processing for On-Chip HCDs
+ * @pdev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device *pdev)
+{
+	struct resource *res;
+	struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
+
+	pr_debug(__FILE__ ": remove: %s, state %x\n", hcd->self.bus_name,
+								hcd->state);
+	if (in_interrupt())
+		BUG();
+
+	hcd->state = USB_STATE_QUIESCING;
+
+	pr_debug("%s: roothub graceful disconnect\n", hcd->self.bus_name);
+	usb_disconnect(&hcd->self.root_hub);
+
+	hcd->driver->stop(hcd);
+	hcd->state = USB_STATE_HALT;
+
+	free_irq(hcd->irq, hcd);
+	hcd_buffer_destroy(hcd);
+
+	usb_deregister_bus(&hcd->self);
+
+	iounmap(hcd->regs);
+	kfree(hcd);
+
+	pr_debug("stopping PPC-SOC USB Controller\n");
+
+	if (pd && pd->stop)
+		pd->stop(pdev);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, res->end - res->start + 1);
+}
+
+static int __devinit
+ohci_ppc_soc_start(struct usb_hcd *hcd)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
+	int		ret;
+
+	if ((ret = ohci_init(ohci)) < 0)
+		return ret;
+
+	if ((ret = ohci_run(ohci)) < 0) {
+		err("can't start %s", ohci_to_hcd(ohci)->self.bus_name);
+		ohci_stop(hcd);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct hc_driver ohci_ppc_soc_hc_driver = {
+	.description =		hcd_name,
+	.hcd_priv_size =	sizeof(struct ohci_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq =			ohci_irq,
+	.flags =		HCD_USB11,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.start =		ohci_ppc_soc_start,
+	.stop =			ohci_stop,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue =		ohci_urb_enqueue,
+	.urb_dequeue =		ohci_urb_dequeue,
+	.endpoint_disable =	ohci_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number =	ohci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data =	ohci_hub_status_data,
+	.hub_control =		ohci_hub_control,
+#ifdef	CONFIG_USB_SUSPEND
+	.hub_suspend =		ohci_hub_suspend,
+	.hub_resume =		ohci_hub_resume,
+#endif
+	.start_port_reset =	ohci_start_port_reset,
+};
+
+static int ohci_hcd_ppc_soc_drv_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct usb_hcd *hcd = NULL;
+	int ret;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	ret = usb_hcd_ppc_soc_probe(&ohci_ppc_soc_hc_driver, &hcd, pdev);
+
+	if (ret == 0)
+		dev_set_drvdata(dev, hcd);
+
+	return ret;
+}
+
+static int ohci_hcd_ppc_soc_drv_remove(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+	usb_hcd_ppc_soc_remove(hcd, pdev);
+
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+static struct device_driver ohci_hcd_ppc_soc_driver = {
+	.name		= "ppc-soc-ohci",
+	.bus		= &platform_bus_type,
+	.probe		= ohci_hcd_ppc_soc_drv_probe,
+	.remove		= ohci_hcd_ppc_soc_drv_remove,
+#if	defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
+	/*.suspend	= ohci_hcd_ppc_soc_drv_suspend,*/
+	/*.resume	= ohci_hcd_ppc_soc_drv_resume,*/
+#endif
+};
+
+static int __init ohci_hcd_ppc_soc_init(void)
+{
+	pr_debug(DRIVER_INFO " (PPC SOC)\n");
+	pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed),
+							sizeof(struct td));
+
+	return driver_register(&ohci_hcd_ppc_soc_driver);
+}
+
+static void __exit ohci_hcd_ppc_soc_cleanup(void)
+{
+	driver_unregister(&ohci_hcd_ppc_soc_driver);
+}
+
+module_init(ohci_hcd_ppc_soc_init);
+module_exit(ohci_hcd_ppc_soc_cleanup);
diff -puN drivers/usb/host/ohci-q.c~bk-usb drivers/usb/host/ohci-q.c
--- 25/drivers/usb/host/ohci-q.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/host/ohci-q.c	2005-02-09 19:12:30.000000000 -0800
@@ -547,7 +547,8 @@ td_fill (struct ohci_hcd *ohci, u32 info
 	td->hwINFO = cpu_to_hc32 (ohci, info);
 	if (is_iso) {
 		td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000);
-		td->hwPSW [0] = cpu_to_hc16 (ohci, (data & 0x0FFF) | 0xE000);
+		*ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci,
+						(data & 0x0FFF) | 0xE000);
 		td->ed->last_iso = info & 0xffff;
 	} else {
 		td->hwCBP = cpu_to_hc32 (ohci, data); 
@@ -719,10 +720,12 @@ static void td_done (struct ohci_hcd *oh
 
 	/* ISO ... drivers see per-TD length/status */
   	if (tdINFO & TD_ISO) {
- 		u16	tdPSW = hc16_to_cpu (ohci, td->hwPSW [0]);
+ 		u16	tdPSW = ohci_hwPSW (ohci, td, 0);
 		int	dlen = 0;
 
-		/* NOTE:  assumes FC in tdINFO == 0 (and MAXPSW == 1) */
+		/* NOTE:  assumes FC in tdINFO == 0, and that
+		 * only the first of 0..MAXPSW psws is used.
+		 */
 
  		cc = (tdPSW >> 12) & 0xF;
   		if (tdINFO & TD_CC)	/* hc didn't touch? */
diff -puN drivers/usb/host/uhci-debug.c~bk-usb drivers/usb/host/uhci-debug.c
--- 25/drivers/usb/host/uhci-debug.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/host/uhci-debug.c	2005-02-09 19:12:30.000000000 -0800
@@ -17,6 +17,8 @@
 
 #include "uhci-hcd.h"
 
+static struct dentry *uhci_debugfs_root = NULL;
+
 /* Handle REALLY large printk's so we don't overflow buffers */
 static inline void lprintk(char *buf)
 {
@@ -497,8 +499,6 @@ static int uhci_sprint_schedule(struct u
 
 #define MAX_OUTPUT	(64 * 1024)
 
-static struct dentry *uhci_debugfs_root = NULL;
-
 struct uhci_debug {
 	int size;
 	char *data;
@@ -579,4 +579,9 @@ static struct file_operations uhci_debug
 	.read =		uhci_debug_read,
 	.release =	uhci_debug_release,
 };
+
+#else	/* CONFIG_DEBUG_FS */
+
+#define uhci_debug_operations (* (struct file_operations *) NULL)
+
 #endif
diff -puN drivers/usb/host/uhci-hcd.c~bk-usb drivers/usb/host/uhci-hcd.c
--- 25/drivers/usb/host/uhci-hcd.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/host/uhci-hcd.c	2005-02-09 19:12:30.000000000 -0800
@@ -87,1448 +87,23 @@ MODULE_PARM_DESC(debug, "Debug level");
 static char *errbuf;
 #define ERRBUF_LEN    (32 * 1024)
 
-#include "uhci-hub.c"
-#include "uhci-debug.c"
-
-static kmem_cache_t *uhci_up_cachep;	/* urb_priv */
-
-static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci);
-static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
-static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb);
-static void uhci_remove_pending_urbps(struct uhci_hcd *uhci);
-static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs);
-static void uhci_free_pending_qhs(struct uhci_hcd *uhci);
-static void uhci_free_pending_tds(struct uhci_hcd *uhci);
-
-static void hc_state_transitions(struct uhci_hcd *uhci);
-
-/* If a transfer is still active after this much time, turn off FSBR */
-#define IDLE_TIMEOUT	msecs_to_jiffies(50)
-#define FSBR_DELAY	msecs_to_jiffies(50)
-
-/* When we timeout an idle transfer for FSBR, we'll switch it over to */
-/* depth first traversal. We'll do it in groups of this number of TD's */
-/* to make sure it doesn't hog all of the bandwidth */
-#define DEPTH_INTERVAL 5
-
-/*
- * Technically, updating td->status here is a race, but it's not really a
- * problem. The worst that can happen is that we set the IOC bit again
- * generating a spurious interrupt. We could fix this by creating another
- * QH and leaving the IOC bit always set, but then we would have to play
- * games with the FSBR code to make sure we get the correct order in all
- * the cases. I don't think it's worth the effort
- */
-static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
-{
-	uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); 
-}
-
-static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
-{
-	uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
-}
-
-static inline void uhci_moveto_complete(struct uhci_hcd *uhci, 
-					struct urb_priv *urbp)
-{
-	list_move_tail(&urbp->urb_list, &uhci->complete_list);
-}
-
-static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev)
-{
-	dma_addr_t dma_handle;
-	struct uhci_td *td;
-
-	td = dma_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle);
-	if (!td)
-		return NULL;
-
-	td->dma_handle = dma_handle;
-
-	td->link = UHCI_PTR_TERM;
-	td->buffer = 0;
-
-	td->frame = -1;
-	td->dev = dev;
-
-	INIT_LIST_HEAD(&td->list);
-	INIT_LIST_HEAD(&td->remove_list);
-	INIT_LIST_HEAD(&td->fl_list);
-
-	usb_get_dev(dev);
-
-	return td;
-}
-
-static inline void uhci_fill_td(struct uhci_td *td, u32 status,
-		u32 token, u32 buffer)
-{
-	td->status = cpu_to_le32(status);
-	td->token = cpu_to_le32(token);
-	td->buffer = cpu_to_le32(buffer);
-}
-
-/*
- * We insert Isochronous URB's directly into the frame list at the beginning
- */
-static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
-{
-	framenum &= (UHCI_NUMFRAMES - 1);
-
-	td->frame = framenum;
-
-	/* Is there a TD already mapped there? */
-	if (uhci->fl->frame_cpu[framenum]) {
-		struct uhci_td *ftd, *ltd;
-
-		ftd = uhci->fl->frame_cpu[framenum];
-		ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
-
-		list_add_tail(&td->fl_list, &ftd->fl_list);
-
-		td->link = ltd->link;
-		wmb();
-		ltd->link = cpu_to_le32(td->dma_handle);
-	} else {
-		td->link = uhci->fl->frame[framenum];
-		wmb();
-		uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
-		uhci->fl->frame_cpu[framenum] = td;
-	}
-}
-
-static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
-{
-	/* If it's not inserted, don't remove it */
-	if (td->frame == -1 && list_empty(&td->fl_list))
-		return;
-
-	if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
-		if (list_empty(&td->fl_list)) {
-			uhci->fl->frame[td->frame] = td->link;
-			uhci->fl->frame_cpu[td->frame] = NULL;
-		} else {
-			struct uhci_td *ntd;
-
-			ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
-			uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
-			uhci->fl->frame_cpu[td->frame] = ntd;
-		}
-	} else {
-		struct uhci_td *ptd;
-
-		ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
-		ptd->link = td->link;
-	}
-
-	wmb();
-	td->link = UHCI_PTR_TERM;
-
-	list_del_init(&td->fl_list);
-	td->frame = -1;
-}
-
-/*
- * Inserts a td list into qh.
- */
-static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td;
-	__le32 *plink;
-
-	/* Ordering isn't important here yet since the QH hasn't been */
-	/* inserted into the schedule yet */
-	plink = &qh->element;
-	list_for_each_entry(td, &urbp->td_list, list) {
-		*plink = cpu_to_le32(td->dma_handle) | breadth;
-		plink = &td->link;
-	}
-	*plink = UHCI_PTR_TERM;
-}
-
-static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
-{
-	if (!list_empty(&td->list))
-		dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
-	if (!list_empty(&td->remove_list))
-		dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td);
-	if (!list_empty(&td->fl_list))
-		dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
-
-	if (td->dev)
-		usb_put_dev(td->dev);
-
-	dma_pool_free(uhci->td_pool, td, td->dma_handle);
-}
-
-static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev)
-{
-	dma_addr_t dma_handle;
-	struct uhci_qh *qh;
-
-	qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
-	if (!qh)
-		return NULL;
-
-	qh->dma_handle = dma_handle;
-
-	qh->element = UHCI_PTR_TERM;
-	qh->link = UHCI_PTR_TERM;
-
-	qh->dev = dev;
-	qh->urbp = NULL;
-
-	INIT_LIST_HEAD(&qh->list);
-	INIT_LIST_HEAD(&qh->remove_list);
-
-	usb_get_dev(dev);
-
-	return qh;
-}
-
-static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
-{
-	if (!list_empty(&qh->list))
-		dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
-	if (!list_empty(&qh->remove_list))
-		dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh);
-
-	if (qh->dev)
-		usb_put_dev(qh->dev);
-
-	dma_pool_free(uhci->qh_pool, qh, qh->dma_handle);
-}
-
-/*
- * Append this urb's qh after the last qh in skelqh->list
- *
- * Note that urb_priv.queue_list doesn't have a separate queue head;
- * it's a ring with every element "live".
- */
-static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct urb_priv *turbp;
-	struct uhci_qh *lqh;
-
-	/* Grab the last QH */
-	lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
-
-	/* Point to the next skelqh */
-	urbp->qh->link = lqh->link;
-	wmb();				/* Ordering is important */
-
-	/*
-	 * Patch QHs for previous endpoint's queued URBs?  HC goes
-	 * here next, not to the next skelqh it now points to.
-	 *
-	 *    lqh --> td ... --> qh ... --> td --> qh ... --> td
-	 *     |                 |                 |
-	 *     v                 v                 v
-	 *     +<----------------+-----------------+
-	 *     v
-	 *    newqh --> td ... --> td
-	 *     |
-	 *     v
-	 *    ...
-	 *
-	 * The HC could see (and use!) any of these as we write them.
-	 */
-	lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
-	if (lqh->urbp) {
-		list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
-			turbp->qh->link = lqh->link;
-	}
-
-	list_add_tail(&urbp->qh->list, &skelqh->list);
-}
-
-/*
- * Start removal of QH from schedule; it finishes next frame.
- * TDs should be unlinked before this is called.
- */
-static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
-{
-	struct uhci_qh *pqh;
-	__le32 newlink;
-	unsigned int age;
-
-	if (!qh)
-		return;
-
-	/*
-	 * Only go through the hoops if it's actually linked in
-	 */
-	if (!list_empty(&qh->list)) {
-
-		/* If our queue is nonempty, make the next URB the head */
-		if (!list_empty(&qh->urbp->queue_list)) {
-			struct urb_priv *nurbp;
-
-			nurbp = list_entry(qh->urbp->queue_list.next,
-					struct urb_priv, queue_list);
-			nurbp->queued = 0;
-			list_add(&nurbp->qh->list, &qh->list);
-			newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
-		} else
-			newlink = qh->link;
-
-		/* Fix up the previous QH's queue to link to either
-		 * the new head of this queue or the start of the
-		 * next endpoint's queue. */
-		pqh = list_entry(qh->list.prev, struct uhci_qh, list);
-		pqh->link = newlink;
-		if (pqh->urbp) {
-			struct urb_priv *turbp;
-
-			list_for_each_entry(turbp, &pqh->urbp->queue_list,
-					queue_list)
-				turbp->qh->link = newlink;
-		}
-		wmb();
-
-		/* Leave qh->link in case the HC is on the QH now, it will */
-		/* continue the rest of the schedule */
-		qh->element = UHCI_PTR_TERM;
-
-		list_del_init(&qh->list);
-	}
-
-	list_del_init(&qh->urbp->queue_list);
-	qh->urbp = NULL;
-
-	age = uhci_get_current_frame_number(uhci);
-	if (age != uhci->qh_remove_age) {
-		uhci_free_pending_qhs(uhci);
-		uhci->qh_remove_age = age;
-	}
-
-	/* Check to see if the remove list is empty. Set the IOC bit */
-	/* to force an interrupt so we can remove the QH */
-	if (list_empty(&uhci->qh_remove_list))
-		uhci_set_next_interrupt(uhci);
-
-	list_add(&qh->remove_list, &uhci->qh_remove_list);
-}
-
-static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td;
-
-	list_for_each_entry(td, &urbp->td_list, list) {
-		if (toggle)
-			td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
-		else
-			td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
-
-		toggle ^= 1;
-	}
-
-	return toggle;
-}
-
-/* This function will append one URB's QH to another URB's QH. This is for */
-/* queuing interrupt, control or bulk transfers */
-static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
-{
-	struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
-	struct uhci_td *lltd;
-
-	eurbp = eurb->hcpriv;
-	urbp = urb->hcpriv;
-
-	/* Find the first URB in the queue */
-	furbp = eurbp;
-	if (eurbp->queued) {
-		list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
-			if (!furbp->queued)
-				break;
-	}
-
-	lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
-
-	lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
-
-	/* Control transfers always start with toggle 0 */
-	if (!usb_pipecontrol(urb->pipe))
-		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-				usb_pipeout(urb->pipe),
-				uhci_fixup_toggle(urb,
-					uhci_toggle(td_token(lltd)) ^ 1));
-
-	/* All qh's in the queue need to link to the next queue */
-	urbp->qh->link = eurbp->qh->link;
-
-	wmb();			/* Make sure we flush everything */
-
-	lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
-
-	list_add_tail(&urbp->queue_list, &furbp->queue_list);
-
-	urbp->queued = 1;
-}
-
-static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp, *nurbp, *purbp, *turbp;
-	struct uhci_td *pltd;
-	unsigned int toggle;
-
-	urbp = urb->hcpriv;
-
-	if (list_empty(&urbp->queue_list))
-		return;
-
-	nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
-
-	/*
-	 * Fix up the toggle for the following URBs in the queue.
-	 * Only needed for bulk and interrupt: control and isochronous
-	 * endpoints don't propagate toggles between messages.
-	 */
-	if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) {
-		if (!urbp->queued)
-			/* We just set the toggle in uhci_unlink_generic */
-			toggle = usb_gettoggle(urb->dev,
-					usb_pipeendpoint(urb->pipe),
-					usb_pipeout(urb->pipe));
-		else {
-			/* If we're in the middle of the queue, grab the */
-			/* toggle from the TD previous to us */
-			purbp = list_entry(urbp->queue_list.prev,
-					struct urb_priv, queue_list);
-			pltd = list_entry(purbp->td_list.prev,
-					struct uhci_td, list);
-			toggle = uhci_toggle(td_token(pltd)) ^ 1;
-		}
-
-		list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
-			if (!turbp->queued)
-				break;
-			toggle = uhci_fixup_toggle(turbp->urb, toggle);
-		}
-
-		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-				usb_pipeout(urb->pipe), toggle);
-	}
-
-	if (urbp->queued) {
-		/* We're somewhere in the middle (or end).  The case where
-		 * we're at the head is handled in uhci_remove_qh(). */
-		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
-				queue_list);
-
-		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
-		if (nurbp->queued)
-			pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
-		else
-			/* The next URB happens to be the beginning, so */
-			/*  we're the last, end the chain */
-			pltd->link = UHCI_PTR_TERM;
-	}
-
-	/* urbp->queue_list is handled in uhci_remove_qh() */
-}
-
-static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp;
-
-	urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
-	if (!urbp)
-		return NULL;
-
-	memset((void *)urbp, 0, sizeof(*urbp));
-
-	urbp->inserttime = jiffies;
-	urbp->fsbrtime = jiffies;
-	urbp->urb = urb;
-	
-	INIT_LIST_HEAD(&urbp->td_list);
-	INIT_LIST_HEAD(&urbp->queue_list);
-	INIT_LIST_HEAD(&urbp->urb_list);
-
-	list_add_tail(&urbp->urb_list, &uhci->urb_list);
-
-	urb->hcpriv = urbp;
-
-	return urbp;
-}
-
-static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-	td->urb = urb;
-
-	list_add_tail(&td->list, &urbp->td_list);
-}
-
-static void uhci_remove_td_from_urb(struct uhci_td *td)
-{
-	if (list_empty(&td->list))
-		return;
-
-	list_del_init(&td->list);
-
-	td->urb = NULL;
-}
-
-static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct uhci_td *td, *tmp;
-	struct urb_priv *urbp;
-	unsigned int age;
-
-	urbp = (struct urb_priv *)urb->hcpriv;
-	if (!urbp)
-		return;
-
-	if (!list_empty(&urbp->urb_list))
-		dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list "
-				"or uhci->remove_list!\n", urb);
-
-	age = uhci_get_current_frame_number(uhci);
-	if (age != uhci->td_remove_age) {
-		uhci_free_pending_tds(uhci);
-		uhci->td_remove_age = age;
-	}
-
-	/* Check to see if the remove list is empty. Set the IOC bit */
-	/* to force an interrupt so we can remove the TD's*/
-	if (list_empty(&uhci->td_remove_list))
-		uhci_set_next_interrupt(uhci);
-
-	list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
-		uhci_remove_td_from_urb(td);
-		uhci_remove_td(uhci, td);
-		list_add(&td->remove_list, &uhci->td_remove_list);
-	}
-
-	urb->hcpriv = NULL;
-	kmem_cache_free(uhci_up_cachep, urbp);
-}
-
-static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-	if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
-		urbp->fsbr = 1;
-		if (!uhci->fsbr++ && !uhci->fsbrtimeout)
-			uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
-	}
-}
-
-static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-	if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
-		urbp->fsbr = 0;
-		if (!--uhci->fsbr)
-			uhci->fsbrtimeout = jiffies + FSBR_DELAY;
-	}
-}
-
-/*
- * Map status to standard result codes
- *
- * <status> is (td_status(td) & 0xF60000), a.k.a.
- * uhci_status_bits(td_status(td)).
- * Note: <status> does not include the TD_CTRL_NAK bit.
- * <dir_out> is True for output TDs and False for input TDs.
- */
-static int uhci_map_status(int status, int dir_out)
-{
-	if (!status)
-		return 0;
-	if (status & TD_CTRL_BITSTUFF)			/* Bitstuff error */
-		return -EPROTO;
-	if (status & TD_CTRL_CRCTIMEO) {		/* CRC/Timeout */
-		if (dir_out)
-			return -EPROTO;
-		else
-			return -EILSEQ;
-	}
-	if (status & TD_CTRL_BABBLE)			/* Babble */
-		return -EOVERFLOW;
-	if (status & TD_CTRL_DBUFERR)			/* Buffer error */
-		return -ENOSR;
-	if (status & TD_CTRL_STALLED)			/* Stalled */
-		return -EPIPE;
-	WARN_ON(status & TD_CTRL_ACTIVE);		/* Active */
-	return 0;
-}
-
-/*
- * Control transfers
- */
-static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td;
-	struct uhci_qh *qh, *skelqh;
-	unsigned long destination, status;
-	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-	int len = urb->transfer_buffer_length;
-	dma_addr_t data = urb->transfer_dma;
-
-	/* The "pipe" thing contains the destination in bits 8--18 */
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
-
-	/* 3 errors */
-	status = TD_CTRL_ACTIVE | uhci_maxerr(3);
-	if (urb->dev->speed == USB_SPEED_LOW)
-		status |= TD_CTRL_LS;
-
-	/*
-	 * Build the TD for the control request setup packet
-	 */
-	td = uhci_alloc_td(uhci, urb->dev);
-	if (!td)
-		return -ENOMEM;
-
-	uhci_add_td_to_urb(urb, td);
-	uhci_fill_td(td, status, destination | uhci_explen(7),
-		urb->setup_dma);
-
-	/*
-	 * If direction is "send", change the packet ID from SETUP (0x2D)
-	 * to OUT (0xE1).  Else change it from SETUP to IN (0x69) and
-	 * set Short Packet Detect (SPD) for all data packets.
-	 */
-	if (usb_pipeout(urb->pipe))
-		destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
-	else {
-		destination ^= (USB_PID_SETUP ^ USB_PID_IN);
-		status |= TD_CTRL_SPD;
-	}
-
-	/*
-	 * Build the DATA TD's
-	 */
-	while (len > 0) {
-		int pktsze = len;
-
-		if (pktsze > maxsze)
-			pktsze = maxsze;
-
-		td = uhci_alloc_td(uhci, urb->dev);
-		if (!td)
-			return -ENOMEM;
-
-		/* Alternate Data0/1 (start with Data1) */
-		destination ^= TD_TOKEN_TOGGLE;
-	
-		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1),
-			data);
-
-		data += pktsze;
-		len -= pktsze;
-	}
-
-	/*
-	 * Build the final TD for control status 
-	 */
-	td = uhci_alloc_td(uhci, urb->dev);
-	if (!td)
-		return -ENOMEM;
-
-	/*
-	 * It's IN if the pipe is an output pipe or we're not expecting
-	 * data back.
-	 */
-	destination &= ~TD_TOKEN_PID_MASK;
-	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
-		destination |= USB_PID_IN;
-	else
-		destination |= USB_PID_OUT;
-
-	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */
-
-	status &= ~TD_CTRL_SPD;
-
-	uhci_add_td_to_urb(urb, td);
-	uhci_fill_td(td, status | TD_CTRL_IOC,
-		destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0);
-
-	qh = uhci_alloc_qh(uhci, urb->dev);
-	if (!qh)
-		return -ENOMEM;
-
-	urbp->qh = qh;
-	qh->urbp = urbp;
-
-	uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
-
-	/* Low-speed transfers get a different queue, and won't hog the bus.
-	 * Also, some devices enumerate better without FSBR; the easiest way
-	 * to do that is to put URBs on the low-speed queue while the device
-	 * is in the DEFAULT state. */
-	if (urb->dev->speed == USB_SPEED_LOW ||
-			urb->dev->state == USB_STATE_DEFAULT)
-		skelqh = uhci->skel_ls_control_qh;
-	else {
-		skelqh = uhci->skel_fs_control_qh;
-		uhci_inc_fsbr(uhci, urb);
-	}
-
-	if (eurb)
-		uhci_append_queued_urb(uhci, eurb, urb);
-	else
-		uhci_insert_qh(uhci, skelqh, urb);
-
-	return -EINPROGRESS;
-}
-
-/*
- * If control-IN transfer was short, the status packet wasn't sent.
- * This routine changes the element pointer in the QH to point at the
- * status TD.  It's safe to do this even while the QH is live, because
- * the hardware only updates the element pointer following a successful
- * transfer.  The inactive TD for the short packet won't cause an update,
- * so the pointer won't get overwritten.  The next time the controller
- * sees this QH, it will send the status packet.
- */
-static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td;
-
-	urbp->short_control_packet = 1;
-
-	td = list_entry(urbp->td_list.prev, struct uhci_td, list);
-	urbp->qh->element = cpu_to_le32(td->dma_handle);
-
-	return -EINPROGRESS;
-}
-
-
-static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct list_head *tmp, *head;
-	struct urb_priv *urbp = urb->hcpriv;
-	struct uhci_td *td;
-	unsigned int status;
-	int ret = 0;
-
-	if (list_empty(&urbp->td_list))
-		return -EINVAL;
-
-	head = &urbp->td_list;
-
-	if (urbp->short_control_packet) {
-		tmp = head->prev;
-		goto status_stage;
-	}
-
-	tmp = head->next;
-	td = list_entry(tmp, struct uhci_td, list);
-
-	/* The first TD is the SETUP stage, check the status, but skip */
-	/*  the count */
-	status = uhci_status_bits(td_status(td));
-	if (status & TD_CTRL_ACTIVE)
-		return -EINPROGRESS;
-
-	if (status)
-		goto td_error;
-
-	urb->actual_length = 0;
-
-	/* The rest of the TD's (but the last) are data */
-	tmp = tmp->next;
-	while (tmp != head && tmp->next != head) {
-		unsigned int ctrlstat;
-
-		td = list_entry(tmp, struct uhci_td, list);
-		tmp = tmp->next;
-
-		ctrlstat = td_status(td);
-		status = uhci_status_bits(ctrlstat);
-		if (status & TD_CTRL_ACTIVE)
-			return -EINPROGRESS;
-
-		urb->actual_length += uhci_actual_length(ctrlstat);
-
-		if (status)
-			goto td_error;
-
-		/* Check to see if we received a short packet */
-		if (uhci_actual_length(ctrlstat) <
-				uhci_expected_length(td_token(td))) {
-			if (urb->transfer_flags & URB_SHORT_NOT_OK) {
-				ret = -EREMOTEIO;
-				goto err;
-			}
-
-			if (uhci_packetid(td_token(td)) == USB_PID_IN)
-				return usb_control_retrigger_status(uhci, urb);
-			else
-				return 0;
-		}
-	}
-
-status_stage:
-	td = list_entry(tmp, struct uhci_td, list);
-
-	/* Control status stage */
-	status = td_status(td);
-
-#ifdef I_HAVE_BUGGY_APC_BACKUPS
-	/* APC BackUPS Pro kludge */
-	/* It tries to send all of the descriptor instead of the amount */
-	/*  we requested */
-	if (status & TD_CTRL_IOC &&	/* IOC is masked out by uhci_status_bits */
-	    status & TD_CTRL_ACTIVE &&
-	    status & TD_CTRL_NAK)
-		return 0;
-#endif
-
-	status = uhci_status_bits(status);
-	if (status & TD_CTRL_ACTIVE)
-		return -EINPROGRESS;
-
-	if (status)
-		goto td_error;
-
-	return 0;
-
-td_error:
-	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
-
-err:
-	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
-		/* Some debugging code */
-		dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
-				__FUNCTION__, status);
-
-		if (errbuf) {
-			/* Print the chain for debugging purposes */
-			uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
-
-			lprintk(errbuf);
-		}
-	}
-
-	return ret;
-}
-
-/*
- * Common submit for bulk and interrupt
- */
-static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh)
-{
-	struct uhci_td *td;
-	struct uhci_qh *qh;
-	unsigned long destination, status;
-	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-	int len = urb->transfer_buffer_length;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	dma_addr_t data = urb->transfer_dma;
-
-	if (len < 0)
-		return -EINVAL;
-
-	/* The "pipe" thing contains the destination in bits 8--18 */
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
-
-	status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
-	if (urb->dev->speed == USB_SPEED_LOW)
-		status |= TD_CTRL_LS;
-	if (usb_pipein(urb->pipe))
-		status |= TD_CTRL_SPD;
-
-	/*
-	 * Build the DATA TD's
-	 */
-	do {	/* Allow zero length packets */
-		int pktsze = maxsze;
-
-		if (pktsze >= len) {
-			pktsze = len;
-			if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
-				status &= ~TD_CTRL_SPD;
-		}
-
-		td = uhci_alloc_td(uhci, urb->dev);
-		if (!td)
-			return -ENOMEM;
-
-		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) |
-			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
-			data);
-
-		data += pktsze;
-		len -= maxsze;
-
-		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			usb_pipeout(urb->pipe));
-	} while (len > 0);
-
-	/*
-	 * URB_ZERO_PACKET means adding a 0-length packet, if direction
-	 * is OUT and the transfer_length was an exact multiple of maxsze,
-	 * hence (len = transfer_length - N * maxsze) == 0
-	 * however, if transfer_length == 0, the zero packet was already
-	 * prepared above.
-	 */
-	if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) &&
-	    !len && urb->transfer_buffer_length) {
-		td = uhci_alloc_td(uhci, urb->dev);
-		if (!td)
-			return -ENOMEM;
-
-		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) |
-			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
-			data);
-
-		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			usb_pipeout(urb->pipe));
-	}
-
-	/* Set the interrupt-on-completion flag on the last packet.
-	 * A more-or-less typical 4 KB URB (= size of one memory page)
-	 * will require about 3 ms to transfer; that's a little on the
-	 * fast side but not enough to justify delaying an interrupt
-	 * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
-	 * flag setting. */
-	td->status |= cpu_to_le32(TD_CTRL_IOC);
-
-	qh = uhci_alloc_qh(uhci, urb->dev);
-	if (!qh)
-		return -ENOMEM;
-
-	urbp->qh = qh;
-	qh->urbp = urbp;
-
-	/* Always breadth first */
-	uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
-
-	if (eurb)
-		uhci_append_queued_urb(uhci, eurb, urb);
-	else
-		uhci_insert_qh(uhci, skelqh, urb);
-
-	return -EINPROGRESS;
-}
-
-/*
- * Common result for bulk and interrupt
- */
-static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp = urb->hcpriv;
-	struct uhci_td *td;
-	unsigned int status = 0;
-	int ret = 0;
-
-	urb->actual_length = 0;
-
-	list_for_each_entry(td, &urbp->td_list, list) {
-		unsigned int ctrlstat = td_status(td);
-
-		status = uhci_status_bits(ctrlstat);
-		if (status & TD_CTRL_ACTIVE)
-			return -EINPROGRESS;
-
-		urb->actual_length += uhci_actual_length(ctrlstat);
-
-		if (status)
-			goto td_error;
-
-		if (uhci_actual_length(ctrlstat) <
-				uhci_expected_length(td_token(td))) {
-			if (urb->transfer_flags & URB_SHORT_NOT_OK) {
-				ret = -EREMOTEIO;
-				goto err;
-			} else
-				return 0;
-		}
-	}
-
-	return 0;
-
-td_error:
-	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
-
-err:
-	/* 
-	 * Enable this chunk of code if you want to see some more debugging.
-	 * But be careful, it has the tendancy to starve out khubd and prevent
-	 * disconnects from happening successfully if you have a slow debug
-	 * log interface (like a serial console.
-	 */
-#if 0
-	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
-		/* Some debugging code */
-		dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
-				__FUNCTION__, status);
-
-		if (errbuf) {
-			/* Print the chain for debugging purposes */
-			uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
-
-			lprintk(errbuf);
-		}
-	}
-#endif
-	return ret;
-}
-
-static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
-{
-	int ret;
-
-	/* Can't have low-speed bulk transfers */
-	if (urb->dev->speed == USB_SPEED_LOW)
-		return -EINVAL;
-
-	ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh);
-	if (ret == -EINPROGRESS)
-		uhci_inc_fsbr(uhci, urb);
-
-	return ret;
-}
-
-static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
-{
-	/* USB 1.1 interrupt transfers only involve one packet per interval;
-	 * that's the uhci_submit_common() "breadth first" policy.  Drivers
-	 * can submit urbs of any length, but longer ones might need many
-	 * intervals to complete.
-	 */
-	return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);
-}
-
-/*
- * Isochronous transfers
- */
-static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
-{
-	struct urb *last_urb = NULL;
-	struct urb_priv *up;
-	int ret = 0;
-
-	list_for_each_entry(up, &uhci->urb_list, urb_list) {
-		struct urb *u = up->urb;
-
-		/* look for pending URB's with identical pipe handle */
-		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
-		    (u->status == -EINPROGRESS) && (u != urb)) {
-			if (!last_urb)
-				*start = u->start_frame;
-			last_urb = u;
-		}
-	}
-
-	if (last_urb) {
-		*end = (last_urb->start_frame + last_urb->number_of_packets *
-				last_urb->interval) & (UHCI_NUMFRAMES-1);
-		ret = 0;
-	} else
-		ret = -1;	/* no previous urb found */
-
-	return ret;
-}
-
-static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb)
-{
-	int limits;
-	unsigned int start = 0, end = 0;
-
-	if (urb->number_of_packets > 900)	/* 900? Why? */
-		return -EFBIG;
-
-	limits = isochronous_find_limits(uhci, urb, &start, &end);
-
-	if (urb->transfer_flags & URB_ISO_ASAP) {
-		if (limits)
-			urb->start_frame =
-					(uhci_get_current_frame_number(uhci) +
-						10) & (UHCI_NUMFRAMES - 1);
-		else
-			urb->start_frame = end;
-	} else {
-		urb->start_frame &= (UHCI_NUMFRAMES - 1);
-		/* FIXME: Sanity check */
-	}
-
-	return 0;
-}
-
-/*
- * Isochronous transfers
- */
-static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct uhci_td *td;
-	int i, ret, frame;
-	int status, destination;
-
-	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
-
-	ret = isochronous_find_start(uhci, urb);
-	if (ret)
-		return ret;
-
-	frame = urb->start_frame;
-	for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
-		if (!urb->iso_frame_desc[i].length)
-			continue;
-
-		td = uhci_alloc_td(uhci, urb->dev);
-		if (!td)
-			return -ENOMEM;
-
-		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1),
-			urb->transfer_dma + urb->iso_frame_desc[i].offset);
-
-		if (i + 1 >= urb->number_of_packets)
-			td->status |= cpu_to_le32(TD_CTRL_IOC);
-
-		uhci_insert_td_frame_list(uhci, td, frame);
-	}
-
-	return -EINPROGRESS;
-}
-
-static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct uhci_td *td;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	int status;
-	int i, ret = 0;
-
-	urb->actual_length = 0;
-
-	i = 0;
-	list_for_each_entry(td, &urbp->td_list, list) {
-		int actlength;
-		unsigned int ctrlstat = td_status(td);
-
-		if (ctrlstat & TD_CTRL_ACTIVE)
-			return -EINPROGRESS;
-
-		actlength = uhci_actual_length(ctrlstat);
-		urb->iso_frame_desc[i].actual_length = actlength;
-		urb->actual_length += actlength;
-
-		status = uhci_map_status(uhci_status_bits(ctrlstat),
-				usb_pipeout(urb->pipe));
-		urb->iso_frame_desc[i].status = status;
-		if (status) {
-			urb->error_count++;
-			ret = status;
-		}
-
-		i++;
-	}
-
-	return ret;
-}
-
-static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *up;
-
-	/* We don't match Isoc transfers since they are special */
-	if (usb_pipeisoc(urb->pipe))
-		return NULL;
-
-	list_for_each_entry(up, &uhci->urb_list, urb_list) {
-		struct urb *u = up->urb;
-
-		if (u->dev == urb->dev && u->status == -EINPROGRESS) {
-			/* For control, ignore the direction */
-			if (usb_pipecontrol(urb->pipe) &&
-			    (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
-				return u;
-			else if (u->pipe == urb->pipe)
-				return u;
-		}
-	}
-
-	return NULL;
-}
-
-static int uhci_urb_enqueue(struct usb_hcd *hcd,
-		struct usb_host_endpoint *ep,
-		struct urb *urb, int mem_flags)
-{
-	int ret;
-	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-	unsigned long flags;
-	struct urb *eurb;
-	int bustime;
-
-	spin_lock_irqsave(&uhci->schedule_lock, flags);
-
-	ret = urb->status;
-	if (ret != -EINPROGRESS)		/* URB already unlinked! */
-		goto out;
-
-	eurb = uhci_find_urb_ep(uhci, urb);
-
-	if (!uhci_alloc_urb_priv(uhci, urb)) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	switch (usb_pipetype(urb->pipe)) {
-	case PIPE_CONTROL:
-		ret = uhci_submit_control(uhci, urb, eurb);
-		break;
-	case PIPE_INTERRUPT:
-		if (!eurb) {
-			bustime = usb_check_bandwidth(urb->dev, urb);
-			if (bustime < 0)
-				ret = bustime;
-			else {
-				ret = uhci_submit_interrupt(uhci, urb, eurb);
-				if (ret == -EINPROGRESS)
-					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
-			}
-		} else {	/* inherit from parent */
-			urb->bandwidth = eurb->bandwidth;
-			ret = uhci_submit_interrupt(uhci, urb, eurb);
-		}
-		break;
-	case PIPE_BULK:
-		ret = uhci_submit_bulk(uhci, urb, eurb);
-		break;
-	case PIPE_ISOCHRONOUS:
-		bustime = usb_check_bandwidth(urb->dev, urb);
-		if (bustime < 0) {
-			ret = bustime;
-			break;
-		}
-
-		ret = uhci_submit_isochronous(uhci, urb);
-		if (ret == -EINPROGRESS)
-			usb_claim_bandwidth(urb->dev, urb, bustime, 1);
-		break;
-	}
-
-	if (ret != -EINPROGRESS) {
-		/* Submit failed, so delete it from the urb_list */
-		struct urb_priv *urbp = urb->hcpriv;
-
-		list_del_init(&urbp->urb_list);
-		uhci_destroy_urb_priv(uhci, urb);
-	} else
-		ret = 0;
-
-out:
-	spin_unlock_irqrestore(&uhci->schedule_lock, flags);
-	return ret;
-}
-
-/*
- * Return the result of a transfer
- */
-static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb)
-{
-	int ret = -EINPROGRESS;
-	struct urb_priv *urbp;
-
-	spin_lock(&urb->lock);
-
-	urbp = (struct urb_priv *)urb->hcpriv;
-
-	if (urb->status != -EINPROGRESS)	/* URB already dequeued */
-		goto out;
-
-	switch (usb_pipetype(urb->pipe)) {
-	case PIPE_CONTROL:
-		ret = uhci_result_control(uhci, urb);
-		break;
-	case PIPE_BULK:
-	case PIPE_INTERRUPT:
-		ret = uhci_result_common(uhci, urb);
-		break;
-	case PIPE_ISOCHRONOUS:
-		ret = uhci_result_isochronous(uhci, urb);
-		break;
-	}
-
-	if (ret == -EINPROGRESS)
-		goto out;
-	urb->status = ret;
-
-	switch (usb_pipetype(urb->pipe)) {
-	case PIPE_CONTROL:
-	case PIPE_BULK:
-	case PIPE_ISOCHRONOUS:
-		/* Release bandwidth for Interrupt or Isoc. transfers */
-		if (urb->bandwidth)
-			usb_release_bandwidth(urb->dev, urb, 1);
-		uhci_unlink_generic(uhci, urb);
-		break;
-	case PIPE_INTERRUPT:
-		/* Release bandwidth for Interrupt or Isoc. transfers */
-		/* Make sure we don't release if we have a queued URB */
-		if (list_empty(&urbp->queue_list) && urb->bandwidth)
-			usb_release_bandwidth(urb->dev, urb, 0);
-		else
-			/* bandwidth was passed on to queued URB, */
-			/* so don't let usb_unlink_urb() release it */
-			urb->bandwidth = 0;
-		uhci_unlink_generic(uhci, urb);
-		break;
-	default:
-		dev_info(uhci_dev(uhci), "%s: unknown pipe type %d "
-				"for urb %p\n",
-				__FUNCTION__, usb_pipetype(urb->pipe), urb);
-	}
-
-	/* Move it from uhci->urb_list to uhci->complete_list */
-	uhci_moveto_complete(uhci, urbp);
-
-out:
-	spin_unlock(&urb->lock);
-}
-
-static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct list_head *head;
-	struct uhci_td *td;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	int prevactive = 0;
-
-	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */
-
-	/*
-	 * Now we need to find out what the last successful toggle was
-	 * so we can update the local data toggle for the next transfer
-	 *
-	 * There are 2 ways the last successful completed TD is found:
-	 *
-	 * 1) The TD is NOT active and the actual length < expected length
-	 * 2) The TD is NOT active and it's the last TD in the chain
-	 *
-	 * and a third way the first uncompleted TD is found:
-	 *
-	 * 3) The TD is active and the previous TD is NOT active
-	 *
-	 * Control and Isochronous ignore the toggle, so this is safe
-	 * for all types
-	 *
-	 * FIXME: The toggle fixups won't be 100% reliable until we
-	 * change over to using a single queue for each endpoint and
-	 * stop the queue before unlinking.
-	 */
-	head = &urbp->td_list;
-	list_for_each_entry(td, head, list) {
-		unsigned int ctrlstat = td_status(td);
-
-		if (!(ctrlstat & TD_CTRL_ACTIVE) &&
-				(uhci_actual_length(ctrlstat) <
-				 uhci_expected_length(td_token(td)) ||
-				td->list.next == head))
-			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
-				uhci_packetout(td_token(td)),
-				uhci_toggle(td_token(td)) ^ 1);
-		else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive)
-			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
-				uhci_packetout(td_token(td)),
-				uhci_toggle(td_token(td)));
-
-		prevactive = ctrlstat & TD_CTRL_ACTIVE;
-	}
-
-	uhci_delete_queued_urb(uhci, urb);
-
-	/* The interrupt loop will reclaim the QH's */
-	uhci_remove_qh(uhci, urbp->qh);
-	urbp->qh = NULL;
-}
-
-static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
-{
-	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-	unsigned long flags;
-	struct urb_priv *urbp;
-	unsigned int age;
-
-	spin_lock_irqsave(&uhci->schedule_lock, flags);
-	urbp = urb->hcpriv;
-	if (!urbp)			/* URB was never linked! */
-		goto done;
-	list_del_init(&urbp->urb_list);
-
-	uhci_unlink_generic(uhci, urb);
-
-	age = uhci_get_current_frame_number(uhci);
-	if (age != uhci->urb_remove_age) {
-		uhci_remove_pending_urbps(uhci);
-		uhci->urb_remove_age = age;
-	}
-
-	/* If we're the first, set the next interrupt bit */
-	if (list_empty(&uhci->urb_remove_list))
-		uhci_set_next_interrupt(uhci);
-	list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);
-
-done:
-	spin_unlock_irqrestore(&uhci->schedule_lock, flags);
-	return 0;
-}
-
-static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct list_head *head;
-	struct uhci_td *td;
-	int count = 0;
-
-	uhci_dec_fsbr(uhci, urb);
-
-	urbp->fsbr_timeout = 1;
-
-	/*
-	 * Ideally we would want to fix qh->element as well, but it's
-	 * read/write by the HC, so that can introduce a race. It's not
-	 * really worth the hassle
-	 */
+static kmem_cache_t *uhci_up_cachep;	/* urb_priv */
 
-	head = &urbp->td_list;
-	list_for_each_entry(td, head, list) {
-		/*
-		 * Make sure we don't do the last one (since it'll have the
-		 * TERM bit set) as well as we skip every so many TD's to
-		 * make sure it doesn't hog the bandwidth
-		 */
-		if (td->list.next != head && (count % DEPTH_INTERVAL) ==
-				(DEPTH_INTERVAL - 1))
-			td->link |= UHCI_PTR_DEPTH;
+static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci);
+static void hc_state_transitions(struct uhci_hcd *uhci);
 
-		count++;
-	}
+/* If a transfer is still active after this much time, turn off FSBR */
+#define IDLE_TIMEOUT	msecs_to_jiffies(50)
+#define FSBR_DELAY	msecs_to_jiffies(50)
 
-	return 0;
-}
+/* When we timeout an idle transfer for FSBR, we'll switch it over to */
+/* depth first traversal. We'll do it in groups of this number of TD's */
+/* to make sure it doesn't hog all of the bandwidth */
+#define DEPTH_INTERVAL 5
 
-/*
- * uhci_get_current_frame_number()
- *
- * returns the current frame number for a USB bus/controller.
- */
-static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci)
-{
-	return inw(uhci->io_addr + USBFRNUM);
-}
+#include "uhci-hub.c"
+#include "uhci-debug.c"
+#include "uhci-q.c"
 
 static int init_stall_timer(struct usb_hcd *hcd);
 
@@ -1592,62 +167,6 @@ static int init_stall_timer(struct usb_h
 	return 0;
 }
 
-static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
-{
-	struct uhci_qh *qh, *tmp;
-
-	list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {
-		list_del_init(&qh->remove_list);
-
-		uhci_free_qh(uhci, qh);
-	}
-}
-
-static void uhci_free_pending_tds(struct uhci_hcd *uhci)
-{
-	struct uhci_td *td, *tmp;
-
-	list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
-		list_del_init(&td->remove_list);
-
-		uhci_free_td(uhci, td);
-	}
-}
-
-static void
-uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
-__releases(uhci->schedule_lock)
-__acquires(uhci->schedule_lock)
-{
-	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-
-	uhci_destroy_urb_priv(uhci, urb);
-
-	spin_unlock(&uhci->schedule_lock);
-	usb_hcd_giveback_urb(hcd, urb, regs);
-	spin_lock(&uhci->schedule_lock);
-}
-
-static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs)
-{
-	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-	struct urb_priv *urbp, *tmp;
-
-	list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
-		struct urb *urb = urbp->urb;
-
-		list_del_init(&urbp->urb_list);
-		uhci_finish_urb(hcd, urb, regs);
-	}
-}
-
-static void uhci_remove_pending_urbps(struct uhci_hcd *uhci)
-{
-
-	/* Splice the urb_remove_list onto the end of the complete_list */
-	list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);
-}
-
 static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
 {
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
@@ -1866,6 +385,14 @@ static void hc_state_transitions(struct 
 	}
 }
 
+/*
+ * returns the current frame number for a USB bus/controller.
+ */
+static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci)
+{
+	return inw(uhci->io_addr + USBFRNUM);
+}
+
 static int start_hc(struct uhci_hcd *uhci)
 {
 	unsigned long io_addr = uhci->io_addr;
@@ -1906,7 +433,7 @@ static int start_hc(struct uhci_hcd *uhc
 }
 
 /*
- * De-allocate all resources..
+ * De-allocate all resources
  */
 static void release_uhci(struct uhci_hcd *uhci)
 {
diff -puN /dev/null drivers/usb/host/uhci-q.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/host/uhci-q.c	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,1488 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
+ */
+
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
+static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb);
+static void uhci_remove_pending_urbps(struct uhci_hcd *uhci);
+static void uhci_free_pending_qhs(struct uhci_hcd *uhci);
+static void uhci_free_pending_tds(struct uhci_hcd *uhci);
+
+/*
+ * Technically, updating td->status here is a race, but it's not really a
+ * problem. The worst that can happen is that we set the IOC bit again
+ * generating a spurious interrupt. We could fix this by creating another
+ * QH and leaving the IOC bit always set, but then we would have to play
+ * games with the FSBR code to make sure we get the correct order in all
+ * the cases. I don't think it's worth the effort
+ */
+static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
+{
+	uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); 
+}
+
+static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
+{
+	uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
+}
+
+static inline void uhci_moveto_complete(struct uhci_hcd *uhci, 
+					struct urb_priv *urbp)
+{
+	list_move_tail(&urbp->urb_list, &uhci->complete_list);
+}
+
+static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev)
+{
+	dma_addr_t dma_handle;
+	struct uhci_td *td;
+
+	td = dma_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle);
+	if (!td)
+		return NULL;
+
+	td->dma_handle = dma_handle;
+
+	td->link = UHCI_PTR_TERM;
+	td->buffer = 0;
+
+	td->frame = -1;
+	td->dev = dev;
+
+	INIT_LIST_HEAD(&td->list);
+	INIT_LIST_HEAD(&td->remove_list);
+	INIT_LIST_HEAD(&td->fl_list);
+
+	usb_get_dev(dev);
+
+	return td;
+}
+
+static inline void uhci_fill_td(struct uhci_td *td, u32 status,
+		u32 token, u32 buffer)
+{
+	td->status = cpu_to_le32(status);
+	td->token = cpu_to_le32(token);
+	td->buffer = cpu_to_le32(buffer);
+}
+
+/*
+ * We insert Isochronous URB's directly into the frame list at the beginning
+ */
+static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
+{
+	framenum &= (UHCI_NUMFRAMES - 1);
+
+	td->frame = framenum;
+
+	/* Is there a TD already mapped there? */
+	if (uhci->fl->frame_cpu[framenum]) {
+		struct uhci_td *ftd, *ltd;
+
+		ftd = uhci->fl->frame_cpu[framenum];
+		ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+
+		list_add_tail(&td->fl_list, &ftd->fl_list);
+
+		td->link = ltd->link;
+		wmb();
+		ltd->link = cpu_to_le32(td->dma_handle);
+	} else {
+		td->link = uhci->fl->frame[framenum];
+		wmb();
+		uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
+		uhci->fl->frame_cpu[framenum] = td;
+	}
+}
+
+static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+	/* If it's not inserted, don't remove it */
+	if (td->frame == -1 && list_empty(&td->fl_list))
+		return;
+
+	if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
+		if (list_empty(&td->fl_list)) {
+			uhci->fl->frame[td->frame] = td->link;
+			uhci->fl->frame_cpu[td->frame] = NULL;
+		} else {
+			struct uhci_td *ntd;
+
+			ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
+			uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
+			uhci->fl->frame_cpu[td->frame] = ntd;
+		}
+	} else {
+		struct uhci_td *ptd;
+
+		ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
+		ptd->link = td->link;
+	}
+
+	wmb();
+	td->link = UHCI_PTR_TERM;
+
+	list_del_init(&td->fl_list);
+	td->frame = -1;
+}
+
+/*
+ * Inserts a td list into qh.
+ */
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td;
+	__le32 *plink;
+
+	/* Ordering isn't important here yet since the QH hasn't been */
+	/* inserted into the schedule yet */
+	plink = &qh->element;
+	list_for_each_entry(td, &urbp->td_list, list) {
+		*plink = cpu_to_le32(td->dma_handle) | breadth;
+		plink = &td->link;
+	}
+	*plink = UHCI_PTR_TERM;
+}
+
+static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+	if (!list_empty(&td->list))
+		dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
+	if (!list_empty(&td->remove_list))
+		dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td);
+	if (!list_empty(&td->fl_list))
+		dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+
+	if (td->dev)
+		usb_put_dev(td->dev);
+
+	dma_pool_free(uhci->td_pool, td, td->dma_handle);
+}
+
+static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev)
+{
+	dma_addr_t dma_handle;
+	struct uhci_qh *qh;
+
+	qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
+	if (!qh)
+		return NULL;
+
+	qh->dma_handle = dma_handle;
+
+	qh->element = UHCI_PTR_TERM;
+	qh->link = UHCI_PTR_TERM;
+
+	qh->dev = dev;
+	qh->urbp = NULL;
+
+	INIT_LIST_HEAD(&qh->list);
+	INIT_LIST_HEAD(&qh->remove_list);
+
+	usb_get_dev(dev);
+
+	return qh;
+}
+
+static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+	if (!list_empty(&qh->list))
+		dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
+	if (!list_empty(&qh->remove_list))
+		dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh);
+
+	if (qh->dev)
+		usb_put_dev(qh->dev);
+
+	dma_pool_free(uhci->qh_pool, qh, qh->dma_handle);
+}
+
+/*
+ * Append this urb's qh after the last qh in skelqh->list
+ *
+ * Note that urb_priv.queue_list doesn't have a separate queue head;
+ * it's a ring with every element "live".
+ */
+static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct urb_priv *turbp;
+	struct uhci_qh *lqh;
+
+	/* Grab the last QH */
+	lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
+
+	/* Point to the next skelqh */
+	urbp->qh->link = lqh->link;
+	wmb();				/* Ordering is important */
+
+	/*
+	 * Patch QHs for previous endpoint's queued URBs?  HC goes
+	 * here next, not to the next skelqh it now points to.
+	 *
+	 *    lqh --> td ... --> qh ... --> td --> qh ... --> td
+	 *     |                 |                 |
+	 *     v                 v                 v
+	 *     +<----------------+-----------------+
+	 *     v
+	 *    newqh --> td ... --> td
+	 *     |
+	 *     v
+	 *    ...
+	 *
+	 * The HC could see (and use!) any of these as we write them.
+	 */
+	lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+	if (lqh->urbp) {
+		list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
+			turbp->qh->link = lqh->link;
+	}
+
+	list_add_tail(&urbp->qh->list, &skelqh->list);
+}
+
+/*
+ * Start removal of QH from schedule; it finishes next frame.
+ * TDs should be unlinked before this is called.
+ */
+static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+	struct uhci_qh *pqh;
+	__le32 newlink;
+	unsigned int age;
+
+	if (!qh)
+		return;
+
+	/*
+	 * Only go through the hoops if it's actually linked in
+	 */
+	if (!list_empty(&qh->list)) {
+
+		/* If our queue is nonempty, make the next URB the head */
+		if (!list_empty(&qh->urbp->queue_list)) {
+			struct urb_priv *nurbp;
+
+			nurbp = list_entry(qh->urbp->queue_list.next,
+					struct urb_priv, queue_list);
+			nurbp->queued = 0;
+			list_add(&nurbp->qh->list, &qh->list);
+			newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
+		} else
+			newlink = qh->link;
+
+		/* Fix up the previous QH's queue to link to either
+		 * the new head of this queue or the start of the
+		 * next endpoint's queue. */
+		pqh = list_entry(qh->list.prev, struct uhci_qh, list);
+		pqh->link = newlink;
+		if (pqh->urbp) {
+			struct urb_priv *turbp;
+
+			list_for_each_entry(turbp, &pqh->urbp->queue_list,
+					queue_list)
+				turbp->qh->link = newlink;
+		}
+		wmb();
+
+		/* Leave qh->link in case the HC is on the QH now, it will */
+		/* continue the rest of the schedule */
+		qh->element = UHCI_PTR_TERM;
+
+		list_del_init(&qh->list);
+	}
+
+	list_del_init(&qh->urbp->queue_list);
+	qh->urbp = NULL;
+
+	age = uhci_get_current_frame_number(uhci);
+	if (age != uhci->qh_remove_age) {
+		uhci_free_pending_qhs(uhci);
+		uhci->qh_remove_age = age;
+	}
+
+	/* Check to see if the remove list is empty. Set the IOC bit */
+	/* to force an interrupt so we can remove the QH */
+	if (list_empty(&uhci->qh_remove_list))
+		uhci_set_next_interrupt(uhci);
+
+	list_add(&qh->remove_list, &uhci->qh_remove_list);
+}
+
+static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td;
+
+	list_for_each_entry(td, &urbp->td_list, list) {
+		if (toggle)
+			td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
+		else
+			td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
+
+		toggle ^= 1;
+	}
+
+	return toggle;
+}
+
+/* This function will append one URB's QH to another URB's QH. This is for */
+/* queuing interrupt, control or bulk transfers */
+static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
+{
+	struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
+	struct uhci_td *lltd;
+
+	eurbp = eurb->hcpriv;
+	urbp = urb->hcpriv;
+
+	/* Find the first URB in the queue */
+	furbp = eurbp;
+	if (eurbp->queued) {
+		list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
+			if (!furbp->queued)
+				break;
+	}
+
+	lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
+
+	lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
+
+	/* Control transfers always start with toggle 0 */
+	if (!usb_pipecontrol(urb->pipe))
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+				usb_pipeout(urb->pipe),
+				uhci_fixup_toggle(urb,
+					uhci_toggle(td_token(lltd)) ^ 1));
+
+	/* All qh's in the queue need to link to the next queue */
+	urbp->qh->link = eurbp->qh->link;
+
+	wmb();			/* Make sure we flush everything */
+
+	lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+
+	list_add_tail(&urbp->queue_list, &furbp->queue_list);
+
+	urbp->queued = 1;
+}
+
+static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp, *nurbp, *purbp, *turbp;
+	struct uhci_td *pltd;
+	unsigned int toggle;
+
+	urbp = urb->hcpriv;
+
+	if (list_empty(&urbp->queue_list))
+		return;
+
+	nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
+
+	/*
+	 * Fix up the toggle for the following URBs in the queue.
+	 * Only needed for bulk and interrupt: control and isochronous
+	 * endpoints don't propagate toggles between messages.
+	 */
+	if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) {
+		if (!urbp->queued)
+			/* We just set the toggle in uhci_unlink_generic */
+			toggle = usb_gettoggle(urb->dev,
+					usb_pipeendpoint(urb->pipe),
+					usb_pipeout(urb->pipe));
+		else {
+			/* If we're in the middle of the queue, grab the */
+			/* toggle from the TD previous to us */
+			purbp = list_entry(urbp->queue_list.prev,
+					struct urb_priv, queue_list);
+			pltd = list_entry(purbp->td_list.prev,
+					struct uhci_td, list);
+			toggle = uhci_toggle(td_token(pltd)) ^ 1;
+		}
+
+		list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
+			if (!turbp->queued)
+				break;
+			toggle = uhci_fixup_toggle(turbp->urb, toggle);
+		}
+
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+				usb_pipeout(urb->pipe), toggle);
+	}
+
+	if (urbp->queued) {
+		/* We're somewhere in the middle (or end).  The case where
+		 * we're at the head is handled in uhci_remove_qh(). */
+		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+				queue_list);
+
+		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+		if (nurbp->queued)
+			pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
+		else
+			/* The next URB happens to be the beginning, so */
+			/*  we're the last, end the chain */
+			pltd->link = UHCI_PTR_TERM;
+	}
+
+	/* urbp->queue_list is handled in uhci_remove_qh() */
+}
+
+static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp;
+
+	urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
+	if (!urbp)
+		return NULL;
+
+	memset((void *)urbp, 0, sizeof(*urbp));
+
+	urbp->inserttime = jiffies;
+	urbp->fsbrtime = jiffies;
+	urbp->urb = urb;
+	
+	INIT_LIST_HEAD(&urbp->td_list);
+	INIT_LIST_HEAD(&urbp->queue_list);
+	INIT_LIST_HEAD(&urbp->urb_list);
+
+	list_add_tail(&urbp->urb_list, &uhci->urb_list);
+
+	urb->hcpriv = urbp;
+
+	return urbp;
+}
+
+static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	td->urb = urb;
+
+	list_add_tail(&td->list, &urbp->td_list);
+}
+
+static void uhci_remove_td_from_urb(struct uhci_td *td)
+{
+	if (list_empty(&td->list))
+		return;
+
+	list_del_init(&td->list);
+
+	td->urb = NULL;
+}
+
+static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct uhci_td *td, *tmp;
+	struct urb_priv *urbp;
+	unsigned int age;
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+	if (!urbp)
+		return;
+
+	if (!list_empty(&urbp->urb_list))
+		dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list "
+				"or uhci->remove_list!\n", urb);
+
+	age = uhci_get_current_frame_number(uhci);
+	if (age != uhci->td_remove_age) {
+		uhci_free_pending_tds(uhci);
+		uhci->td_remove_age = age;
+	}
+
+	/* Check to see if the remove list is empty. Set the IOC bit */
+	/* to force an interrupt so we can remove the TD's*/
+	if (list_empty(&uhci->td_remove_list))
+		uhci_set_next_interrupt(uhci);
+
+	list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+		uhci_remove_td_from_urb(td);
+		uhci_remove_td(uhci, td);
+		list_add(&td->remove_list, &uhci->td_remove_list);
+	}
+
+	urb->hcpriv = NULL;
+	kmem_cache_free(uhci_up_cachep, urbp);
+}
+
+static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
+		urbp->fsbr = 1;
+		if (!uhci->fsbr++ && !uhci->fsbrtimeout)
+			uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+	}
+}
+
+static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
+		urbp->fsbr = 0;
+		if (!--uhci->fsbr)
+			uhci->fsbrtimeout = jiffies + FSBR_DELAY;
+	}
+}
+
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td_status(td) & 0xF60000), a.k.a.
+ * uhci_status_bits(td_status(td)).
+ * Note: <status> does not include the TD_CTRL_NAK bit.
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+static int uhci_map_status(int status, int dir_out)
+{
+	if (!status)
+		return 0;
+	if (status & TD_CTRL_BITSTUFF)			/* Bitstuff error */
+		return -EPROTO;
+	if (status & TD_CTRL_CRCTIMEO) {		/* CRC/Timeout */
+		if (dir_out)
+			return -EPROTO;
+		else
+			return -EILSEQ;
+	}
+	if (status & TD_CTRL_BABBLE)			/* Babble */
+		return -EOVERFLOW;
+	if (status & TD_CTRL_DBUFERR)			/* Buffer error */
+		return -ENOSR;
+	if (status & TD_CTRL_STALLED)			/* Stalled */
+		return -EPIPE;
+	WARN_ON(status & TD_CTRL_ACTIVE);		/* Active */
+	return 0;
+}
+
+/*
+ * Control transfers
+ */
+static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td;
+	struct uhci_qh *qh, *skelqh;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	dma_addr_t data = urb->transfer_dma;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+	/* 3 errors */
+	status = TD_CTRL_ACTIVE | uhci_maxerr(3);
+	if (urb->dev->speed == USB_SPEED_LOW)
+		status |= TD_CTRL_LS;
+
+	/*
+	 * Build the TD for the control request setup packet
+	 */
+	td = uhci_alloc_td(uhci, urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status, destination | uhci_explen(7),
+		urb->setup_dma);
+
+	/*
+	 * If direction is "send", change the packet ID from SETUP (0x2D)
+	 * to OUT (0xE1).  Else change it from SETUP to IN (0x69) and
+	 * set Short Packet Detect (SPD) for all data packets.
+	 */
+	if (usb_pipeout(urb->pipe))
+		destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
+	else {
+		destination ^= (USB_PID_SETUP ^ USB_PID_IN);
+		status |= TD_CTRL_SPD;
+	}
+
+	/*
+	 * Build the DATA TD's
+	 */
+	while (len > 0) {
+		int pktsze = len;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		/* Alternate Data0/1 (start with Data1) */
+		destination ^= TD_TOKEN_TOGGLE;
+	
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1),
+			data);
+
+		data += pktsze;
+		len -= pktsze;
+	}
+
+	/*
+	 * Build the final TD for control status 
+	 */
+	td = uhci_alloc_td(uhci, urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	/*
+	 * It's IN if the pipe is an output pipe or we're not expecting
+	 * data back.
+	 */
+	destination &= ~TD_TOKEN_PID_MASK;
+	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
+		destination |= USB_PID_IN;
+	else
+		destination |= USB_PID_OUT;
+
+	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */
+
+	status &= ~TD_CTRL_SPD;
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status | TD_CTRL_IOC,
+		destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0);
+
+	qh = uhci_alloc_qh(uhci, urb->dev);
+	if (!qh)
+		return -ENOMEM;
+
+	urbp->qh = qh;
+	qh->urbp = urbp;
+
+	uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
+
+	/* Low-speed transfers get a different queue, and won't hog the bus.
+	 * Also, some devices enumerate better without FSBR; the easiest way
+	 * to do that is to put URBs on the low-speed queue while the device
+	 * is in the DEFAULT state. */
+	if (urb->dev->speed == USB_SPEED_LOW ||
+			urb->dev->state == USB_STATE_DEFAULT)
+		skelqh = uhci->skel_ls_control_qh;
+	else {
+		skelqh = uhci->skel_fs_control_qh;
+		uhci_inc_fsbr(uhci, urb);
+	}
+
+	if (eurb)
+		uhci_append_queued_urb(uhci, eurb, urb);
+	else
+		uhci_insert_qh(uhci, skelqh, urb);
+
+	return -EINPROGRESS;
+}
+
+/*
+ * If control-IN transfer was short, the status packet wasn't sent.
+ * This routine changes the element pointer in the QH to point at the
+ * status TD.  It's safe to do this even while the QH is live, because
+ * the hardware only updates the element pointer following a successful
+ * transfer.  The inactive TD for the short packet won't cause an update,
+ * so the pointer won't get overwritten.  The next time the controller
+ * sees this QH, it will send the status packet.
+ */
+static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td;
+
+	urbp->short_control_packet = 1;
+
+	td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+	urbp->qh->element = cpu_to_le32(td->dma_handle);
+
+	return -EINPROGRESS;
+}
+
+
+static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci_td *td;
+	unsigned int status;
+	int ret = 0;
+
+	if (list_empty(&urbp->td_list))
+		return -EINVAL;
+
+	head = &urbp->td_list;
+
+	if (urbp->short_control_packet) {
+		tmp = head->prev;
+		goto status_stage;
+	}
+
+	tmp = head->next;
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* The first TD is the SETUP stage, check the status, but skip */
+	/*  the count */
+	status = uhci_status_bits(td_status(td));
+	if (status & TD_CTRL_ACTIVE)
+		return -EINPROGRESS;
+
+	if (status)
+		goto td_error;
+
+	urb->actual_length = 0;
+
+	/* The rest of the TD's (but the last) are data */
+	tmp = tmp->next;
+	while (tmp != head && tmp->next != head) {
+		unsigned int ctrlstat;
+
+		td = list_entry(tmp, struct uhci_td, list);
+		tmp = tmp->next;
+
+		ctrlstat = td_status(td);
+		status = uhci_status_bits(ctrlstat);
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		urb->actual_length += uhci_actual_length(ctrlstat);
+
+		if (status)
+			goto td_error;
+
+		/* Check to see if we received a short packet */
+		if (uhci_actual_length(ctrlstat) <
+				uhci_expected_length(td_token(td))) {
+			if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+				ret = -EREMOTEIO;
+				goto err;
+			}
+
+			if (uhci_packetid(td_token(td)) == USB_PID_IN)
+				return usb_control_retrigger_status(uhci, urb);
+			else
+				return 0;
+		}
+	}
+
+status_stage:
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* Control status stage */
+	status = td_status(td);
+
+#ifdef I_HAVE_BUGGY_APC_BACKUPS
+	/* APC BackUPS Pro kludge */
+	/* It tries to send all of the descriptor instead of the amount */
+	/*  we requested */
+	if (status & TD_CTRL_IOC &&	/* IOC is masked out by uhci_status_bits */
+	    status & TD_CTRL_ACTIVE &&
+	    status & TD_CTRL_NAK)
+		return 0;
+#endif
+
+	status = uhci_status_bits(status);
+	if (status & TD_CTRL_ACTIVE)
+		return -EINPROGRESS;
+
+	if (status)
+		goto td_error;
+
+	return 0;
+
+td_error:
+	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+
+err:
+	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+		/* Some debugging code */
+		dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
+				__FUNCTION__, status);
+
+		if (errbuf) {
+			/* Print the chain for debugging purposes */
+			uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+			lprintk(errbuf);
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Common submit for bulk and interrupt
+ */
+static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh)
+{
+	struct uhci_td *td;
+	struct uhci_qh *qh;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	dma_addr_t data = urb->transfer_dma;
+
+	if (len < 0)
+		return -EINVAL;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
+	if (urb->dev->speed == USB_SPEED_LOW)
+		status |= TD_CTRL_LS;
+	if (usb_pipein(urb->pipe))
+		status |= TD_CTRL_SPD;
+
+	/*
+	 * Build the DATA TD's
+	 */
+	do {	/* Allow zero length packets */
+		int pktsze = maxsze;
+
+		if (pktsze >= len) {
+			pktsze = len;
+			if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+				status &= ~TD_CTRL_SPD;
+		}
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+			data);
+
+		data += pktsze;
+		len -= maxsze;
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	} while (len > 0);
+
+	/*
+	 * URB_ZERO_PACKET means adding a 0-length packet, if direction
+	 * is OUT and the transfer_length was an exact multiple of maxsze,
+	 * hence (len = transfer_length - N * maxsze) == 0
+	 * however, if transfer_length == 0, the zero packet was already
+	 * prepared above.
+	 */
+	if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) &&
+	    !len && urb->transfer_buffer_length) {
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+			data);
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	}
+
+	/* Set the interrupt-on-completion flag on the last packet.
+	 * A more-or-less typical 4 KB URB (= size of one memory page)
+	 * will require about 3 ms to transfer; that's a little on the
+	 * fast side but not enough to justify delaying an interrupt
+	 * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
+	 * flag setting. */
+	td->status |= cpu_to_le32(TD_CTRL_IOC);
+
+	qh = uhci_alloc_qh(uhci, urb->dev);
+	if (!qh)
+		return -ENOMEM;
+
+	urbp->qh = qh;
+	qh->urbp = urbp;
+
+	/* Always breadth first */
+	uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
+
+	if (eurb)
+		uhci_append_queued_urb(uhci, eurb, urb);
+	else
+		uhci_insert_qh(uhci, skelqh, urb);
+
+	return -EINPROGRESS;
+}
+
+/*
+ * Common result for bulk and interrupt
+ */
+static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci_td *td;
+	unsigned int status = 0;
+	int ret = 0;
+
+	urb->actual_length = 0;
+
+	list_for_each_entry(td, &urbp->td_list, list) {
+		unsigned int ctrlstat = td_status(td);
+
+		status = uhci_status_bits(ctrlstat);
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		urb->actual_length += uhci_actual_length(ctrlstat);
+
+		if (status)
+			goto td_error;
+
+		if (uhci_actual_length(ctrlstat) <
+				uhci_expected_length(td_token(td))) {
+			if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+				ret = -EREMOTEIO;
+				goto err;
+			} else
+				return 0;
+		}
+	}
+
+	return 0;
+
+td_error:
+	ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+
+err:
+	/* 
+	 * Enable this chunk of code if you want to see some more debugging.
+	 * But be careful, it has the tendancy to starve out khubd and prevent
+	 * disconnects from happening successfully if you have a slow debug
+	 * log interface (like a serial console.
+	 */
+#if 0
+	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+		/* Some debugging code */
+		dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
+				__FUNCTION__, status);
+
+		if (errbuf) {
+			/* Print the chain for debugging purposes */
+			uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+			lprintk(errbuf);
+		}
+	}
+#endif
+	return ret;
+}
+
+static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+	int ret;
+
+	/* Can't have low-speed bulk transfers */
+	if (urb->dev->speed == USB_SPEED_LOW)
+		return -EINVAL;
+
+	ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh);
+	if (ret == -EINPROGRESS)
+		uhci_inc_fsbr(uhci, urb);
+
+	return ret;
+}
+
+static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+	/* USB 1.1 interrupt transfers only involve one packet per interval;
+	 * that's the uhci_submit_common() "breadth first" policy.  Drivers
+	 * can submit urbs of any length, but longer ones might need many
+	 * intervals to complete.
+	 */
+	return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);
+}
+
+/*
+ * Isochronous transfers
+ */
+static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
+{
+	struct urb *last_urb = NULL;
+	struct urb_priv *up;
+	int ret = 0;
+
+	list_for_each_entry(up, &uhci->urb_list, urb_list) {
+		struct urb *u = up->urb;
+
+		/* look for pending URB's with identical pipe handle */
+		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
+		    (u->status == -EINPROGRESS) && (u != urb)) {
+			if (!last_urb)
+				*start = u->start_frame;
+			last_urb = u;
+		}
+	}
+
+	if (last_urb) {
+		*end = (last_urb->start_frame + last_urb->number_of_packets *
+				last_urb->interval) & (UHCI_NUMFRAMES-1);
+		ret = 0;
+	} else
+		ret = -1;	/* no previous urb found */
+
+	return ret;
+}
+
+static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb)
+{
+	int limits;
+	unsigned int start = 0, end = 0;
+
+	if (urb->number_of_packets > 900)	/* 900? Why? */
+		return -EFBIG;
+
+	limits = isochronous_find_limits(uhci, urb, &start, &end);
+
+	if (urb->transfer_flags & URB_ISO_ASAP) {
+		if (limits)
+			urb->start_frame =
+					(uhci_get_current_frame_number(uhci) +
+						10) & (UHCI_NUMFRAMES - 1);
+		else
+			urb->start_frame = end;
+	} else {
+		urb->start_frame &= (UHCI_NUMFRAMES - 1);
+		/* FIXME: Sanity check */
+	}
+
+	return 0;
+}
+
+/*
+ * Isochronous transfers
+ */
+static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct uhci_td *td;
+	int i, ret, frame;
+	int status, destination;
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	ret = isochronous_find_start(uhci, urb);
+	if (ret)
+		return ret;
+
+	frame = urb->start_frame;
+	for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
+		if (!urb->iso_frame_desc[i].length)
+			continue;
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1),
+			urb->transfer_dma + urb->iso_frame_desc[i].offset);
+
+		if (i + 1 >= urb->number_of_packets)
+			td->status |= cpu_to_le32(TD_CTRL_IOC);
+
+		uhci_insert_td_frame_list(uhci, td, frame);
+	}
+
+	return -EINPROGRESS;
+}
+
+static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct uhci_td *td;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	int status;
+	int i, ret = 0;
+
+	urb->actual_length = 0;
+
+	i = 0;
+	list_for_each_entry(td, &urbp->td_list, list) {
+		int actlength;
+		unsigned int ctrlstat = td_status(td);
+
+		if (ctrlstat & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		actlength = uhci_actual_length(ctrlstat);
+		urb->iso_frame_desc[i].actual_length = actlength;
+		urb->actual_length += actlength;
+
+		status = uhci_map_status(uhci_status_bits(ctrlstat),
+				usb_pipeout(urb->pipe));
+		urb->iso_frame_desc[i].status = status;
+		if (status) {
+			urb->error_count++;
+			ret = status;
+		}
+
+		i++;
+	}
+
+	return ret;
+}
+
+static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *up;
+
+	/* We don't match Isoc transfers since they are special */
+	if (usb_pipeisoc(urb->pipe))
+		return NULL;
+
+	list_for_each_entry(up, &uhci->urb_list, urb_list) {
+		struct urb *u = up->urb;
+
+		if (u->dev == urb->dev && u->status == -EINPROGRESS) {
+			/* For control, ignore the direction */
+			if (usb_pipecontrol(urb->pipe) &&
+			    (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
+				return u;
+			else if (u->pipe == urb->pipe)
+				return u;
+		}
+	}
+
+	return NULL;
+}
+
+static int uhci_urb_enqueue(struct usb_hcd *hcd,
+		struct usb_host_endpoint *ep,
+		struct urb *urb, int mem_flags)
+{
+	int ret;
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	unsigned long flags;
+	struct urb *eurb;
+	int bustime;
+
+	spin_lock_irqsave(&uhci->schedule_lock, flags);
+
+	ret = urb->status;
+	if (ret != -EINPROGRESS)		/* URB already unlinked! */
+		goto out;
+
+	eurb = uhci_find_urb_ep(uhci, urb);
+
+	if (!uhci_alloc_urb_priv(uhci, urb)) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = uhci_submit_control(uhci, urb, eurb);
+		break;
+	case PIPE_INTERRUPT:
+		if (!eurb) {
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0)
+				ret = bustime;
+			else {
+				ret = uhci_submit_interrupt(uhci, urb, eurb);
+				if (ret == -EINPROGRESS)
+					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+			}
+		} else {	/* inherit from parent */
+			urb->bandwidth = eurb->bandwidth;
+			ret = uhci_submit_interrupt(uhci, urb, eurb);
+		}
+		break;
+	case PIPE_BULK:
+		ret = uhci_submit_bulk(uhci, urb, eurb);
+		break;
+	case PIPE_ISOCHRONOUS:
+		bustime = usb_check_bandwidth(urb->dev, urb);
+		if (bustime < 0) {
+			ret = bustime;
+			break;
+		}
+
+		ret = uhci_submit_isochronous(uhci, urb);
+		if (ret == -EINPROGRESS)
+			usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+		break;
+	}
+
+	if (ret != -EINPROGRESS) {
+		/* Submit failed, so delete it from the urb_list */
+		struct urb_priv *urbp = urb->hcpriv;
+
+		list_del_init(&urbp->urb_list);
+		uhci_destroy_urb_priv(uhci, urb);
+	} else
+		ret = 0;
+
+out:
+	spin_unlock_irqrestore(&uhci->schedule_lock, flags);
+	return ret;
+}
+
+/*
+ * Return the result of a transfer
+ */
+static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb)
+{
+	int ret = -EINPROGRESS;
+	struct urb_priv *urbp;
+
+	spin_lock(&urb->lock);
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (urb->status != -EINPROGRESS)	/* URB already dequeued */
+		goto out;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = uhci_result_control(uhci, urb);
+		break;
+	case PIPE_BULK:
+	case PIPE_INTERRUPT:
+		ret = uhci_result_common(uhci, urb);
+		break;
+	case PIPE_ISOCHRONOUS:
+		ret = uhci_result_isochronous(uhci, urb);
+		break;
+	}
+
+	if (ret == -EINPROGRESS)
+		goto out;
+	urb->status = ret;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+	case PIPE_ISOCHRONOUS:
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		if (urb->bandwidth)
+			usb_release_bandwidth(urb->dev, urb, 1);
+		uhci_unlink_generic(uhci, urb);
+		break;
+	case PIPE_INTERRUPT:
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		/* Make sure we don't release if we have a queued URB */
+		if (list_empty(&urbp->queue_list) && urb->bandwidth)
+			usb_release_bandwidth(urb->dev, urb, 0);
+		else
+			/* bandwidth was passed on to queued URB, */
+			/* so don't let usb_unlink_urb() release it */
+			urb->bandwidth = 0;
+		uhci_unlink_generic(uhci, urb);
+		break;
+	default:
+		dev_info(uhci_dev(uhci), "%s: unknown pipe type %d "
+				"for urb %p\n",
+				__FUNCTION__, usb_pipetype(urb->pipe), urb);
+	}
+
+	/* Move it from uhci->urb_list to uhci->complete_list */
+	uhci_moveto_complete(uhci, urbp);
+
+out:
+	spin_unlock(&urb->lock);
+}
+
+static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct list_head *head;
+	struct uhci_td *td;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	int prevactive = 0;
+
+	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */
+
+	/*
+	 * Now we need to find out what the last successful toggle was
+	 * so we can update the local data toggle for the next transfer
+	 *
+	 * There are 2 ways the last successful completed TD is found:
+	 *
+	 * 1) The TD is NOT active and the actual length < expected length
+	 * 2) The TD is NOT active and it's the last TD in the chain
+	 *
+	 * and a third way the first uncompleted TD is found:
+	 *
+	 * 3) The TD is active and the previous TD is NOT active
+	 *
+	 * Control and Isochronous ignore the toggle, so this is safe
+	 * for all types
+	 *
+	 * FIXME: The toggle fixups won't be 100% reliable until we
+	 * change over to using a single queue for each endpoint and
+	 * stop the queue before unlinking.
+	 */
+	head = &urbp->td_list;
+	list_for_each_entry(td, head, list) {
+		unsigned int ctrlstat = td_status(td);
+
+		if (!(ctrlstat & TD_CTRL_ACTIVE) &&
+				(uhci_actual_length(ctrlstat) <
+				 uhci_expected_length(td_token(td)) ||
+				td->list.next == head))
+			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
+				uhci_packetout(td_token(td)),
+				uhci_toggle(td_token(td)) ^ 1);
+		else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive)
+			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
+				uhci_packetout(td_token(td)),
+				uhci_toggle(td_token(td)));
+
+		prevactive = ctrlstat & TD_CTRL_ACTIVE;
+	}
+
+	uhci_delete_queued_urb(uhci, urb);
+
+	/* The interrupt loop will reclaim the QH's */
+	uhci_remove_qh(uhci, urbp->qh);
+	urbp->qh = NULL;
+}
+
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	unsigned long flags;
+	struct urb_priv *urbp;
+	unsigned int age;
+
+	spin_lock_irqsave(&uhci->schedule_lock, flags);
+	urbp = urb->hcpriv;
+	if (!urbp)			/* URB was never linked! */
+		goto done;
+	list_del_init(&urbp->urb_list);
+
+	uhci_unlink_generic(uhci, urb);
+
+	age = uhci_get_current_frame_number(uhci);
+	if (age != uhci->urb_remove_age) {
+		uhci_remove_pending_urbps(uhci);
+		uhci->urb_remove_age = age;
+	}
+
+	/* If we're the first, set the next interrupt bit */
+	if (list_empty(&uhci->urb_remove_list))
+		uhci_set_next_interrupt(uhci);
+	list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);
+
+done:
+	spin_unlock_irqrestore(&uhci->schedule_lock, flags);
+	return 0;
+}
+
+static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct list_head *head;
+	struct uhci_td *td;
+	int count = 0;
+
+	uhci_dec_fsbr(uhci, urb);
+
+	urbp->fsbr_timeout = 1;
+
+	/*
+	 * Ideally we would want to fix qh->element as well, but it's
+	 * read/write by the HC, so that can introduce a race. It's not
+	 * really worth the hassle
+	 */
+
+	head = &urbp->td_list;
+	list_for_each_entry(td, head, list) {
+		/*
+		 * Make sure we don't do the last one (since it'll have the
+		 * TERM bit set) as well as we skip every so many TD's to
+		 * make sure it doesn't hog the bandwidth
+		 */
+		if (td->list.next != head && (count % DEPTH_INTERVAL) ==
+				(DEPTH_INTERVAL - 1))
+			td->link |= UHCI_PTR_DEPTH;
+
+		count++;
+	}
+
+	return 0;
+}
+
+static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
+{
+	struct uhci_qh *qh, *tmp;
+
+	list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {
+		list_del_init(&qh->remove_list);
+
+		uhci_free_qh(uhci, qh);
+	}
+}
+
+static void uhci_free_pending_tds(struct uhci_hcd *uhci)
+{
+	struct uhci_td *td, *tmp;
+
+	list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
+		list_del_init(&td->remove_list);
+
+		uhci_free_td(uhci, td);
+	}
+}
+
+static void
+uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+__releases(uhci->schedule_lock)
+__acquires(uhci->schedule_lock)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+	uhci_destroy_urb_priv(uhci, urb);
+
+	spin_unlock(&uhci->schedule_lock);
+	usb_hcd_giveback_urb(hcd, urb, regs);
+	spin_lock(&uhci->schedule_lock);
+}
+
+static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs)
+{
+	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+	struct urb_priv *urbp, *tmp;
+
+	list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
+		struct urb *urb = urbp->urb;
+
+		list_del_init(&urbp->urb_list);
+		uhci_finish_urb(hcd, urb, regs);
+	}
+}
+
+static void uhci_remove_pending_urbps(struct uhci_hcd *uhci)
+{
+
+	/* Splice the urb_remove_list onto the end of the complete_list */
+	list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);
+}
diff -puN drivers/usb/image/mdc800.c~bk-usb drivers/usb/image/mdc800.c
--- 25/drivers/usb/image/mdc800.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/image/mdc800.c	2005-02-09 19:12:30.000000000 -0800
@@ -95,6 +95,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/smp_lock.h>
+#include <linux/wait.h>
 
 #include <linux/usb.h>
 #include <linux/fs.h>
@@ -330,7 +331,7 @@ static void mdc800_usb_irq (struct urb *
 	{
 		mdc800->camera_request_ready=0;
 		mdc800->irq_woken=1;
-		wake_up_interruptible (&mdc800->irq_wait);
+		wake_up (&mdc800->irq_wait);
 	}
 }
 
@@ -346,19 +347,9 @@ static void mdc800_usb_irq (struct urb *
  */
 static int mdc800_usb_waitForIRQ (int mode, int msec)
 {
-        DECLARE_WAITQUEUE(wait, current);
-	long timeout;
-
 	mdc800->camera_request_ready=1+mode;
 
-	add_wait_queue(&mdc800->irq_wait, &wait);
-	timeout = msec*HZ/1000;
-	while (!mdc800->irq_woken && timeout)
-	{
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		timeout = schedule_timeout (timeout);
-	}
-        remove_wait_queue(&mdc800->irq_wait, &wait);
+	wait_event_timeout(mdc800->irq_wait, mdc800->irq_woken, msec*HZ/1000);
 	mdc800->irq_woken = 0;
 
 	if (mdc800->camera_request_ready>0)
@@ -395,7 +386,7 @@ static void mdc800_usb_write_notify (str
 		mdc800->state=READY;
 	}
 	mdc800->written = 1;
-	wake_up_interruptible (&mdc800->write_wait);
+	wake_up (&mdc800->write_wait);
 }
 
 
@@ -423,7 +414,7 @@ static void mdc800_usb_download_notify (
 		err ("request bytes fails (status:%i)", urb->status);
 	}
 	mdc800->downloaded = 1;
-	wake_up_interruptible (&mdc800->download_wait);
+	wake_up (&mdc800->download_wait);
 }
 
 
@@ -704,8 +695,6 @@ static ssize_t mdc800_device_read (struc
 {
 	size_t left=len, sts=len; /* single transfer size */
 	char __user *ptr = buf;
-	long timeout;
-	DECLARE_WAITQUEUE(wait, current);
 
 	down (&mdc800->io_lock);
 	if (mdc800->state == NOT_CONNECTED)
@@ -751,14 +740,8 @@ static ssize_t mdc800_device_read (struc
 					up (&mdc800->io_lock);
 					return len-left;
 				}
-				add_wait_queue(&mdc800->download_wait, &wait);
-				timeout = TO_DOWNLOAD_GET_READY*HZ/1000;
-				while (!mdc800->downloaded && timeout)
-				{
-					set_current_state(TASK_UNINTERRUPTIBLE);
-					timeout = schedule_timeout (timeout);
-				}
-				remove_wait_queue(&mdc800->download_wait, &wait);
+				wait_event_timeout(mdc800->download_wait, mdc800->downloaded,
+										TO_DOWNLOAD_GET_READY*HZ/1000);
 				mdc800->downloaded = 0;
 				if (mdc800->download_urb->status != 0)
 				{
@@ -802,7 +785,6 @@ static ssize_t mdc800_device_read (struc
 static ssize_t mdc800_device_write (struct file *file, const char __user *buf, size_t len, loff_t *pos)
 {
 	size_t i=0;
-	DECLARE_WAITQUEUE(wait, current);
 
 	down (&mdc800->io_lock);
 	if (mdc800->state != READY)
@@ -856,7 +838,6 @@ static ssize_t mdc800_device_write (stru
 		if (mdc800->in_count == 8)
 		{
 			int answersize;
-			long timeout;
 
 			if (mdc800_usb_waitForIRQ (0,TO_GET_READY))
 			{
@@ -876,14 +857,7 @@ static ssize_t mdc800_device_write (stru
 				up (&mdc800->io_lock);
 				return -EIO;
 			}
-			add_wait_queue(&mdc800->write_wait, &wait);
-			timeout = TO_WRITE_GET_READY*HZ/1000;
-			while (!mdc800->written && timeout)
-			{
-				set_current_state(TASK_UNINTERRUPTIBLE);
-				timeout = schedule_timeout (timeout);
-			}
-			remove_wait_queue(&mdc800->write_wait, &wait);
+			wait_event_timeout(mdc800->write_wait, mdc800->written, TO_WRITE_GET_READY*HZ/1000);
 			mdc800->written = 0;
 			if (mdc800->state == WORKING)
 			{
diff -puN drivers/usb/input/ati_remote.c~bk-usb drivers/usb/input/ati_remote.c
--- 25/drivers/usb/input/ati_remote.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/input/ati_remote.c	2005-02-09 19:12:30.000000000 -0800
@@ -94,6 +94,7 @@
 #include <linux/moduleparam.h>
 #include <linux/input.h>
 #include <linux/usb.h>
+#include <linux/wait.h>
 
 /*
  * Module and Version Information, Module Parameters
@@ -396,8 +397,6 @@ static void ati_remote_irq_out(struct ur
  */
 static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
 {
-	DECLARE_WAITQUEUE(wait, current);
-	int timeout = HZ;	/* 1 second */
 	int retval = 0;
 	
 	/* Set up out_urb */
@@ -415,18 +414,10 @@ static int ati_remote_sendpacket(struct 
 		return retval;
 	}
 
-	set_current_state(TASK_INTERRUPTIBLE);
-	add_wait_queue(&ati_remote->wait, &wait);
-
-	while (timeout && (ati_remote->out_urb->status == -EINPROGRESS) 
-	       && !(ati_remote->send_flags & SEND_FLAG_COMPLETE)) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		timeout = schedule_timeout(timeout);
-		rmb();
-	}
-
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&ati_remote->wait, &wait);
+	wait_event_timeout(ati_remote->wait,
+		((ati_remote->out_urb->status != -EINPROGRESS) ||
+		 	(ati_remote->send_flags & SEND_FLAG_COMPLETE)),
+		HZ);
 	usb_kill_urb(ati_remote->out_urb);
 	
 	return retval;
diff -puN drivers/usb/input/hid-core.c~bk-usb drivers/usb/input/hid-core.c
--- 25/drivers/usb/input/hid-core.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/input/hid-core.c	2005-02-09 19:12:30.000000000 -0800
@@ -24,6 +24,7 @@
 #include <asm/unaligned.h>
 #include <asm/byteorder.h>
 #include <linux/input.h>
+#include <linux/wait.h>
 
 #undef DEBUG
 #undef DEBUG_DATA
@@ -1268,22 +1269,9 @@ void hid_submit_report(struct hid_device
 
 int hid_wait_io(struct hid_device *hid)
 {
-	DECLARE_WAITQUEUE(wait, current);
-	int timeout = 10*HZ;
-
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	add_wait_queue(&hid->wait, &wait);
-
-	while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) ||
-			   test_bit(HID_OUT_RUNNING, &hid->iofl))) {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		timeout = schedule_timeout(timeout);
-	}
-
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&hid->wait, &wait);
-
-	if (!timeout) {
+	if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &hid->iofl) &&
+					!test_bit(HID_OUT_RUNNING, &hid->iofl)),
+					10*HZ)) {
 		dbg("timeout waiting for ctrl or out queue to clear");
 		return -1;
 	}
diff -puN drivers/usb/input/wacom.c~bk-usb drivers/usb/input/wacom.c
--- 25/drivers/usb/input/wacom.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/input/wacom.c	2005-02-09 19:12:30.000000000 -0800
@@ -72,7 +72,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.30"
+#define DRIVER_VERSION "v1.40"
 #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
 #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
 #define DRIVER_LICENSE "GPL"
@@ -141,8 +141,10 @@ static void wacom_pl_irq(struct urb *urb
 		goto exit;
 	}
 
-	if (data[0] != 2)
+	if (data[0] != 2) {
 		dbg("wacom_pl_irq: received unknown report #%d", data[0]);
+		goto exit;
+	}
 
 	prox = data[1] & 0x40;
 
@@ -233,6 +235,7 @@ static void wacom_ptu_irq(struct urb *ur
 	if (data[0] != 2)
 	{
 		printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
+		goto exit;
 	}
 
 	input_regs(dev, regs);
@@ -246,9 +249,9 @@ static void wacom_ptu_irq(struct urb *ur
 		input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20);
 		input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
 	}
-	input_report_abs(dev, ABS_X, data[3] << 8 | data[2]);
-	input_report_abs(dev, ABS_Y, data[5] << 8 | data[4]);
-	input_report_abs(dev, ABS_PRESSURE, (data[6]|data[7] << 8));
+	input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[2]));
+	input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[4]));
+	input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6]));
 	input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
 	input_report_key(dev, BTN_STYLUS2, data[1] & 0x10);
 
@@ -283,10 +286,15 @@ static void wacom_penpartner_irq(struct 
 		goto exit;
 	}
 
+	if (data[0] != 2) {
+		printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
+		goto exit;
+	}
+
 	input_regs(dev, regs);
 	input_report_key(dev, BTN_TOOL_PEN, 1);
-	input_report_abs(dev, ABS_X, le16_to_cpu(get_unaligned((__le16 *) &data[1])));
-	input_report_abs(dev, ABS_Y, le16_to_cpu(get_unaligned((__le16 *) &data[3])));
+	input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[1]));
+	input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[3]));
 	input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127);
 	input_report_key(dev, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
 	input_report_key(dev, BTN_STYLUS, (data[5] & 0x40));
@@ -322,12 +330,10 @@ static void wacom_graphire_irq(struct ur
 		goto exit;
 	}
 
-	/* check if we can handle the data */
-	if (data[0] == 99)
-		goto exit;
-
-	if (data[0] != 2)
+	if (data[0] != 2) {
 		dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
+		goto exit;
+	}
 
 	x = le16_to_cpu(*(__le16 *) &data[2]);
 	y = le16_to_cpu(*(__le16 *) &data[4]);
@@ -381,107 +387,171 @@ exit:
 		     __FUNCTION__, retval);
 }
 
-static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs)
+static int wacom_intuos_inout(struct urb *urb)
 {
 	struct wacom *wacom = urb->context;
 	unsigned char *data = wacom->data;
 	struct input_dev *dev = &wacom->dev;
-	unsigned int t;
 	int idx;
-	int retval;
-
-	switch (urb->status) {
-	case 0:
-		/* success */
-		break;
-	case -ECONNRESET:
-	case -ENOENT:
-	case -ESHUTDOWN:
-		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
-		return;
-	default:
-		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
-		goto exit;
-	}
-
-	if (data[0] != 2)
-		dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
-
-	input_regs(dev, regs);
 
 	/* tool number */
 	idx = data[1] & 0x01;
 
-	if ((data[1] & 0xfc) == 0xc0) {						/* Enter report */
-
-		wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) +		/* serial number of the tool */
+	/* Enter report */
+	if ((data[1] & 0xfc) == 0xc0)
+	{
+		/* serial number of the tool */
+		wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) +
 			((__u32)data[4] << 20) + ((__u32)data[5] << 12) +
 			((__u32)data[6] << 4) + (data[7] >> 4);
 
 		switch (((__u32)data[2] << 4) | (data[3] >> 4)) {
-			case 0x812:
-			case 0x012: wacom->tool[idx] = BTN_TOOL_PENCIL;		break;	/* Inking pen */
-			case 0x822:
+			case 0x812: /* Inking pen */
+			case 0x801: /* Intuos3 Inking pen */
+			case 0x012:
+				wacom->tool[idx] = BTN_TOOL_PENCIL;
+				break;
+			case 0x822: /* Pen */
 			case 0x842:
 			case 0x852:
-			case 0x022: wacom->tool[idx] = BTN_TOOL_PEN;		break;	/* Pen */
-			case 0x832:
-			case 0x032: wacom->tool[idx] = BTN_TOOL_BRUSH;		break;	/* Stroke pen */
-			case 0x007:
+			case 0x823: /* Intuos3 Grip Pen */
+			case 0x813: /* Intuos3 Classic Pen */
+			case 0x885: /* Intuos3 Marker Pen */
+			case 0x022:
+				wacom->tool[idx] = BTN_TOOL_PEN;
+				break;
+			case 0x832: /* Stroke pen */
+			case 0x032:
+				wacom->tool[idx] = BTN_TOOL_BRUSH;
+				break;
+			case 0x007: /* Mouse 4D and 2D */
 		        case 0x09c:
-			case 0x094: wacom->tool[idx] = BTN_TOOL_MOUSE;		break;	/* Mouse 4D and 2D */
-			case 0x096: wacom->tool[idx] = BTN_TOOL_LENS;		break;	/* Lens cursor */
-			case 0x82a:
+			case 0x094:
+			case 0x017: /* Intuos3 2D Mouse */
+				wacom->tool[idx] = BTN_TOOL_MOUSE;
+				break;
+			case 0x096: /* Lens cursor */
+			case 0x097: /* Intuos3 Lens cursor */
+				wacom->tool[idx] = BTN_TOOL_LENS;
+				break;
+			case 0x82a: /* Eraser */
 			case 0x85a:
 		        case 0x91a:
 			case 0xd1a:
-			case 0x0fa: wacom->tool[idx] = BTN_TOOL_RUBBER;		break;	/* Eraser */
+			case 0x0fa:
+			case 0x82b: /* Intuos3 Grip Pen Eraser */
+			case 0x81b: /* Intuos3 Classic Pen Eraser */
+			case 0x91b: /* Intuos3 Airbrush Eraser */
+				wacom->tool[idx] = BTN_TOOL_RUBBER;
+				break;
 			case 0xd12:
 			case 0x912:
-			case 0x112: wacom->tool[idx] = BTN_TOOL_AIRBRUSH;	break;	/* Airbrush */
-			default:    wacom->tool[idx] = BTN_TOOL_PEN;		break;	/* Unknown tool */
+			case 0x112:
+			case 0x913: /* Intuos3 Airbrush */
+				wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
+				break;	/* Airbrush */
+			default: /* Unknown tool */
+				wacom->tool[idx] = BTN_TOOL_PEN;
 		}
-
 		input_report_key(dev, wacom->tool[idx], 1);
 		input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
 		input_sync(dev);
-		goto exit;
+		return 1;
 	}
 
-	if ((data[1] & 0xfe) == 0x80) {						/* Exit report */
+	/* Exit report */
+	if ((data[1] & 0xfe) == 0x80) {
 		input_report_key(dev, wacom->tool[idx], 0);
 		input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
 		input_sync(dev);
-		goto exit;
+		return 1;
 	}
 
-	input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2]));
-	input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4]));
-	input_report_abs(dev, ABS_DISTANCE, data[9]);
+	return 0;
+}
 
-	if ((data[1] & 0xb8) == 0xa0) {						/* general pen packet */
-		input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
-		input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+static void wacom_intuos_general(struct urb *urb)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	unsigned int t;
+
+	/* general pen packet */
+	if ((data[1] & 0xb8) == 0xa0)
+	{
+		t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3);
+		input_report_abs(dev, ABS_PRESSURE, t);
+		input_report_abs(dev, ABS_TILT_X,
+				((data[7] << 1) & 0x7e) | (data[8] >> 7));
 		input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
 		input_report_key(dev, BTN_STYLUS, data[1] & 2);
 		input_report_key(dev, BTN_STYLUS2, data[1] & 4);
 		input_report_key(dev, BTN_TOUCH, t > 10);
 	}
 
-	if ((data[1] & 0xbc) == 0xb4) {						/* airbrush second packet */
-		input_report_abs(dev, ABS_WHEEL, ((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
-		input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+	/* airbrush second packet */
+	if ((data[1] & 0xbc) == 0xb4)
+	{
+		input_report_abs(dev, ABS_WHEEL,
+				((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
+		input_report_abs(dev, ABS_TILT_X,
+				((data[7] << 1) & 0x7e) | (data[8] >> 7));
 		input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
 	}
+	return;
+}
+
+static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	unsigned int t;
+	int idx;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	if (data[0] != 2 && data[0] != 5 && data[0] != 6) {
+		dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
+		goto exit;
+	}
+
+	input_regs(dev, regs);
+
+	/* tool number */
+	idx = data[1] & 0x01;
+
+	/* process in/out prox events */
+	if (wacom_intuos_inout(urb)) goto exit;
+
+	input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2]));
+	input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4]));
+	input_report_abs(dev, ABS_DISTANCE, data[9]);
+
+	/* process general packets */
+	wacom_intuos_general(urb);
 	
 	if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {		/* 4D mouse or Lens cursor packets */
 
 		if (data[1] & 0x02) {						/* Rotation packet */
 
-			input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ?
-					 ((__u32)data[6] << 3) | ((data[7] >> 5) & 7):
-					 (-(((__u32)data[6] << 3) | ((data[7] >> 5) & 7))) - 1);
+			t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7);
+			input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? ((t - 1) / 2) : -t / 2);
 
 		} else {
 
@@ -493,9 +563,8 @@ static void wacom_intuos_irq(struct urb 
 
 				input_report_key(dev, BTN_SIDE,   data[8] & 0x20);
 				input_report_key(dev, BTN_EXTRA,  data[8] & 0x10);
-				input_report_abs(dev, ABS_THROTTLE,  -((data[8] & 0x08) ?
-						 ((__u32)data[6] << 2) | ((data[7] >> 6) & 3) :
-						 -((__u32)data[6] << 2) | ((data[7] >> 6) & 3)));
+				t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3);
+				input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
 
 			} else {
 				if (wacom->tool[idx] == BTN_TOOL_MOUSE) {	/* 2D mouse packets */	
@@ -527,6 +596,111 @@ exit:
 		     __FUNCTION__, retval);
 }
 
+static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	unsigned int t;
+	int idx, retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	/* check for valid report */
+	if (data[0] != 2 && data[0] != 5 && data[0] != 12)
+	{
+		printk(KERN_INFO "wacom_intuos3_irq: received unknown report #%d\n", data[0]);
+		goto exit;
+	}
+
+	input_regs(dev, regs);
+
+	/* tool index is always 0 here since there is no dual input tool */
+	idx = data[1] & 0x01;
+
+	/* pad packets. Works as a second tool and is always in prox */
+	if (data[0] == 12)
+	{
+		/* initiate the pad as a device */
+		if (wacom->tool[1] != BTN_TOOL_FINGER)
+		{
+			wacom->tool[1] = BTN_TOOL_FINGER;
+			input_report_key(dev, wacom->tool[1], 1);
+		}
+		input_report_key(dev, BTN_0, (data[5] & 0x01));
+		input_report_key(dev, BTN_1, (data[5] & 0x02));
+		input_report_key(dev, BTN_2, (data[5] & 0x04));
+		input_report_key(dev, BTN_3, (data[5] & 0x08));
+		input_report_key(dev, BTN_4, (data[6] & 0x01));
+		input_report_key(dev, BTN_5, (data[6] & 0x02));
+		input_report_key(dev, BTN_6, (data[6] & 0x04));
+		input_report_key(dev, BTN_7, (data[6] & 0x08));
+		input_report_abs(dev, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
+		input_report_abs(dev, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
+		input_event(dev, EV_MSC, MSC_SERIAL, 0xffffffff);
+		input_sync(dev);
+		goto exit;
+	}
+
+	/* process in/out prox events */
+	if (wacom_intuos_inout(urb)) goto exit;
+
+	input_report_abs(dev, ABS_X, ((__u32)data[2] << 9) | ((__u32)data[3] << 1) | ((data[9] >> 1) & 1));
+	input_report_abs(dev, ABS_Y, ((__u32)data[4] << 9) | ((__u32)data[5] << 1) | (data[9] & 1));
+	input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
+
+	/* process general packets */
+	wacom_intuos_general(urb);
+
+	if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0)
+	{
+		/* Marker pen rotation packet. Reported as wheel due to valuator limitation */
+		if (data[1] & 0x02)
+		{
+			t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7);
+			t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
+				((t-1) / 2 + 450)) : (450 - t / 2) ;
+			input_report_abs(dev, ABS_WHEEL, t);
+		}
+
+		/* 2D mouse packets */
+		if (wacom->tool[idx] == BTN_TOOL_MOUSE)
+		{
+			input_report_key(dev, BTN_LEFT,   data[8] & 0x04);
+			input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
+			input_report_key(dev, BTN_RIGHT,  data[8] & 0x10);
+			input_report_key(dev, BTN_SIDE,   data[8] & 0x40);
+			input_report_key(dev, BTN_EXTRA,  data[8] & 0x20);
+			/* mouse wheel is positive when rolled backwards */
+			input_report_rel(dev, REL_WHEEL,  ((__u32)((data[8] & 0x02) >> 1)
+					 - (__u32)(data[8] & 0x01)));
+		}
+	}
+
+	input_report_key(dev, wacom->tool[idx], 1);
+	input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+	input_sync(dev);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		    __FUNCTION__, retval);
+}
+
 static struct wacom_features wacom_features[] = {
 	{ "Wacom Penpartner",    7,   5040,  3780,  255, 32, 0, wacom_penpartner_irq },
         { "Wacom Graphire",      8,  10206,  7422,  511, 32, 1, wacom_graphire_irq },
@@ -552,6 +726,9 @@ static struct wacom_features wacom_featu
 	{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq },
 	{ "Wacom Volito",        8,   5104,  3712,  511, 32, 1, wacom_graphire_irq },
 	{ "Wacom Cintiq Partner",8,  20480, 15360,  511, 32, 3, wacom_ptu_irq },
+	{ "Wacom Intuos3 4x5",   10, 25400, 20320, 1023, 15, 4, wacom_intuos3_irq },
+	{ "Wacom Intuos3 6x8",   10, 40640, 30480, 1023, 15, 4, wacom_intuos3_irq },
+	{ "Wacom Intuos3 9x12",  10, 60960, 45720, 1023, 15, 4, wacom_intuos3_irq },
 	{ "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq },
  	{ }
 };
@@ -581,6 +758,9 @@ static struct usb_device_id wacom_ids[] 
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
 	{ }
 };
@@ -651,6 +831,12 @@ static int wacom_probe(struct usb_interf
  			wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
 			break;
 
+		case 4: /* new functions for Intuos3 */
+			wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+			wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+			wacom->dev.absbit[0] |= BIT(ABS_RX) | BIT(ABS_RY);
+			/* fall through */
+
 		case 2:
 			wacom->dev.evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
 			wacom->dev.mscbit[0] |= BIT(MSC_SERIAL);
@@ -662,7 +848,7 @@ static int wacom_probe(struct usb_interf
 			break;
 
 		case 3:
- 			wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2);
+ 			wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
 			break;
 	}
 
@@ -674,6 +860,8 @@ static int wacom_probe(struct usb_interf
 	wacom->dev.absmax[ABS_TILT_Y] = 127;
 	wacom->dev.absmax[ABS_WHEEL] = 1023;
 
+	wacom->dev.absmax[ABS_RX] = 4097;
+	wacom->dev.absmax[ABS_RY] = 4097;
 	wacom->dev.absmin[ABS_RZ] = -900;
 	wacom->dev.absmax[ABS_RZ] = 899;
 	wacom->dev.absmin[ABS_THROTTLE] = -1023;
@@ -712,9 +900,10 @@ static int wacom_probe(struct usb_interf
 
 	input_register_device(&wacom->dev);
 
+	/* ask the tablet to report tablet data */
+	usb_set_report(intf, 3, 2, rep_data, 2);
+	/* repeat once (not sure why the first call often fails) */
 	usb_set_report(intf, 3, 2, rep_data, 2);
-	usb_set_report(intf, 3, 5, rep_data, 0);
-	usb_set_report(intf, 3, 6, rep_data, 0);
 
 	printk(KERN_INFO "input: %s on %s\n", wacom->features->name, path);
 
diff -puN drivers/usb/Kconfig~bk-usb drivers/usb/Kconfig
--- 25/drivers/usb/Kconfig~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/Kconfig	2005-02-09 19:12:30.000000000 -0800
@@ -74,6 +74,8 @@ source "drivers/usb/media/Kconfig"
 
 source "drivers/usb/net/Kconfig"
 
+source "drivers/usb/mon/Kconfig"
+
 comment "USB port drivers"
 	depends on USB
 
diff -puN drivers/usb/Makefile~bk-usb drivers/usb/Makefile
--- 25/drivers/usb/Makefile~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/Makefile	2005-02-09 19:12:30.000000000 -0800
@@ -6,6 +6,8 @@
 
 obj-$(CONFIG_USB)		+= core/
 
+obj-$(CONFIG_USB_MON)		+= mon/
+
 obj-$(CONFIG_USB_EHCI_HCD)	+= host/
 obj-$(CONFIG_USB_OHCI_HCD)	+= host/
 obj-$(CONFIG_USB_UHCI_HCD)	+= host/
diff -puN drivers/usb/media/sn9c102_core.c~bk-usb drivers/usb/media/sn9c102_core.c
--- 25/drivers/usb/media/sn9c102_core.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/media/sn9c102_core.c	2005-02-09 19:12:30.000000000 -0800
@@ -164,8 +164,8 @@ sn9c102_request_buffers(struct sn9c102_d
 	struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
 	const size_t imagesize = cam->module_param.force_munmap ||
 	                         io == IO_READ ?
-	                         (p->width * p->height * p->priv)/8 :
-	                         (r->width * r->height * p->priv)/8;
+	                         (p->width * p->height * p->priv) / 8 :
+	                         (r->width * r->height * p->priv) / 8;
 	void* buff = NULL;
 	u32 i;
 
@@ -499,6 +499,7 @@ static void sn9c102_urb_complete(struct 
 {
 	struct sn9c102_device* cam = urb->context;
 	struct sn9c102_frame_t** f;
+	size_t imagesize;
 	unsigned long lock_flags;
 	u8 i;
 	int err = 0;
@@ -516,8 +517,13 @@ static void sn9c102_urb_complete(struct 
 		wake_up_interruptible(&cam->wait_stream);
 	}
 
-	if ((cam->state & DEV_DISCONNECTED)||(cam->state & DEV_MISCONFIGURED))
+	if (cam->state & DEV_DISCONNECTED)
+		return;
+
+	if (cam->state & DEV_MISCONFIGURED) {
+		wake_up_interruptible(&cam->wait_frame);
 		return;
+	}
 
 	if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
 		goto resubmit_urb;
@@ -526,6 +532,10 @@ static void sn9c102_urb_complete(struct 
 		(*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t,
 		                  frame);
 
+	imagesize = (cam->sensor->pix_format.width *
+	             cam->sensor->pix_format.height *
+	             cam->sensor->pix_format.priv) / 8;
+
 	for (i = 0; i < urb->number_of_packets; i++) {
 		unsigned int img, len, status;
 		void *pos, *sof, *eof;
@@ -560,11 +570,10 @@ end_of_frame:
 				if (eof)
 					img = (eof > pos) ? eof - pos - 1 : 0;
 
-				if ((*f)->buf.bytesused+img>(*f)->buf.length) {
+				if ((*f)->buf.bytesused+img > imagesize) {
 					u32 b = (*f)->buf.bytesused + img -
-					        (*f)->buf.length;
-					img = (*f)->buf.length - 
-					      (*f)->buf.bytesused;
+					        imagesize;
+					img = imagesize - (*f)->buf.bytesused;
 					DBG(3, "Expected EOF not found: "
 					       "video frame cut")
 					if (eof)
@@ -580,7 +589,7 @@ end_of_frame:
 
 				(*f)->buf.bytesused += img;
 
-				if ((*f)->buf.bytesused == (*f)->buf.length ||
+				if ((*f)->buf.bytesused == imagesize ||
 				    (cam->sensor->pix_format.pixelformat ==
 				                V4L2_PIX_FMT_SN9C10X && eof)) {
 					u32 b = (*f)->buf.bytesused;
@@ -1558,7 +1567,8 @@ sn9c102_read(struct file* filp, char __u
 		err = wait_event_interruptible
 		      ( cam->wait_frame, 
 		        (!list_empty(&cam->outqueue)) ||
-		        (cam->state & DEV_DISCONNECTED) );
+		        (cam->state & DEV_DISCONNECTED) ||
+			(cam->state & DEV_MISCONFIGURED) );
 		if (err) {
 			up(&cam->fileop_sem);
 			return err;
@@ -1567,6 +1577,10 @@ sn9c102_read(struct file* filp, char __u
 			up(&cam->fileop_sem);
 			return -ENODEV;
 		}
+		if (cam->state & DEV_MISCONFIGURED) {
+			up(&cam->fileop_sem);
+			return -EIO;
+		}
 	}
 
 	f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame);
@@ -1615,7 +1629,8 @@ static unsigned int sn9c102_poll(struct 
 	}
 
 	if (cam->io == IO_NONE) {
-		if (!sn9c102_request_buffers(cam, 2, IO_READ)) {
+		if (!sn9c102_request_buffers(cam, cam->nreadbuffers,
+		                             IO_READ)) {
 			DBG(1, "poll() failed, not enough memory")
 			goto error;
 		}
@@ -1729,7 +1744,7 @@ static int sn9c102_mmap(struct file* fil
 }
 
 
-static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
+static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
                               unsigned int cmd, void __user * arg)
 {
 	struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
@@ -1970,7 +1985,7 @@ static int sn9c102_v4l2_ioctl(struct ino
 			return -EFAULT;
 		}
 
-		if (cam->module_param.force_munmap)
+		if (cam->module_param.force_munmap || cam->io == IO_READ)
 			sn9c102_release_buffers(cam);
 
 		err = sn9c102_set_crop(cam, rect);
@@ -1990,7 +2005,7 @@ static int sn9c102_v4l2_ioctl(struct ino
 		s->pix_format.height = rect->height/scale;
 		memcpy(&(s->_rect), rect, sizeof(*rect));
 
-		if (cam->module_param.force_munmap &&
+		if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
 		    nbuffers != sn9c102_request_buffers(cam, nbuffers,
 		                                        cam->io)) {
 			cam->state |= DEV_MISCONFIGURED;
@@ -2146,7 +2161,7 @@ static int sn9c102_v4l2_ioctl(struct ino
 			return -EFAULT;
 		}
 
-		if (cam->module_param.force_munmap)
+		if (cam->module_param.force_munmap  || cam->io == IO_READ)
 			sn9c102_release_buffers(cam);
 
 		err += sn9c102_set_pix_format(cam, pix);
@@ -2168,7 +2183,7 @@ static int sn9c102_v4l2_ioctl(struct ino
 		memcpy(pfmt, pix, sizeof(*pix));
 		memcpy(&(s->_rect), &rect, sizeof(rect));
 
-		if (cam->module_param.force_munmap &&
+		if ((cam->module_param.force_munmap  || cam->io == IO_READ) &&
 		    nbuffers != sn9c102_request_buffers(cam, nbuffers,
 		                                        cam->io)) {
 			cam->state |= DEV_MISCONFIGURED;
@@ -2346,11 +2361,14 @@ static int sn9c102_v4l2_ioctl(struct ino
 			err = wait_event_interruptible
 			      ( cam->wait_frame, 
 			        (!list_empty(&cam->outqueue)) ||
-			        (cam->state & DEV_DISCONNECTED) );
+			        (cam->state & DEV_DISCONNECTED) ||
+			        (cam->state & DEV_MISCONFIGURED) );
 			if (err)
 				return err;
 			if (cam->state & DEV_DISCONNECTED)
 				return -ENODEV;
+			if (cam->state & DEV_MISCONFIGURED)
+				return -EIO;
 		}
 
 		spin_lock_irqsave(&cam->queue_lock, lock_flags);
@@ -2495,7 +2513,7 @@ static int sn9c102_ioctl(struct inode* i
 		return -EIO;
 	}
 
-	err = sn9c102_v4l2_ioctl(inode, filp, cmd, (void __user *)arg);
+	err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
 
 	up(&cam->fileop_sem);
 
diff -puN drivers/usb/media/sn9c102.h~bk-usb drivers/usb/media/sn9c102.h
--- 25/drivers/usb/media/sn9c102.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/media/sn9c102.h	2005-02-09 19:12:30.000000000 -0800
@@ -53,11 +53,11 @@
 /*****************************************************************************/
 
 #define SN9C102_MODULE_NAME     "V4L2 driver for SN9C10x PC Camera Controllers"
-#define SN9C102_MODULE_AUTHOR   "(C) 2004 Luca Risolia"
+#define SN9C102_MODULE_AUTHOR   "(C) 2004-2005 Luca Risolia"
 #define SN9C102_AUTHOR_EMAIL    "<luca.risolia@studio.unibo.it>"
 #define SN9C102_MODULE_LICENSE  "GPL"
-#define SN9C102_MODULE_VERSION  "1:1.22"
-#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 22)
+#define SN9C102_MODULE_VERSION  "1:1.24"
+#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 24)
 
 enum sn9c102_bridge {
 	BRIDGE_SN9C101 = 0x01,
diff -puN drivers/usb/misc/auerswald.c~bk-usb drivers/usb/misc/auerswald.c
--- 25/drivers/usb/misc/auerswald.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/misc/auerswald.c	2005-02-09 19:12:30.000000000 -0800
@@ -29,6 +29,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/wait.h>
 #undef DEBUG   		/* include debug macros until it's done	*/
 #include <linux/usb.h>
 
@@ -605,7 +606,6 @@ static void auerchain_blocking_completio
 /* Starts chained urb and waits for completion or timeout */
 static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int timeout, int* actual_length)
 {
-	DECLARE_WAITQUEUE (wait, current);
 	auerchain_chs_t chs;
 	int status;
 
@@ -613,26 +613,13 @@ static int auerchain_start_wait_urb (pau
 	init_waitqueue_head (&chs.wqh);
 	chs.done = 0;
 
-	set_current_state (TASK_UNINTERRUPTIBLE);
-	add_wait_queue (&chs.wqh, &wait);
 	urb->context = &chs;
 	status = auerchain_submit_urb (acp, urb);
-	if (status) {
+	if (status)
 		/* something went wrong */
-		set_current_state (TASK_RUNNING);
-		remove_wait_queue (&chs.wqh, &wait);
 		return status;
-	}
-
-	while (timeout && !chs.done)
-	{
-		timeout = schedule_timeout (timeout);
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		rmb();
-	}
 
-	set_current_state (TASK_RUNNING);
-	remove_wait_queue (&chs.wqh, &wait);
+	timeout = wait_event_timeout(chs.wqh, chs.done, timeout);
 
 	if (!timeout && !chs.done) {
 		if (urb->status != -EINPROGRESS) {	/* No callback?!! */
diff -puN drivers/usb/misc/Kconfig~bk-usb drivers/usb/misc/Kconfig
--- 25/drivers/usb/misc/Kconfig~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/misc/Kconfig	2005-02-09 19:12:30.000000000 -0800
@@ -137,6 +137,8 @@ config USB_IDMOUSE
 
 	  See also <http://www.fs.tum.de/~echtler/idmouse/>.
 
+source "drivers/usb/misc/sisusbvga/Kconfig"
+
 config USB_TEST
 	tristate "USB testing driver (DEVELOPMENT)"
 	depends on USB && USB_DEVICEFS && EXPERIMENTAL
diff -puN drivers/usb/misc/Makefile~bk-usb drivers/usb/misc/Makefile
--- 25/drivers/usb/misc/Makefile~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/misc/Makefile	2005-02-09 19:12:30.000000000 -0800
@@ -16,3 +16,5 @@ obj-$(CONFIG_USB_PHIDGETSERVO)	+= phidge
 obj-$(CONFIG_USB_RIO500)	+= rio500.o
 obj-$(CONFIG_USB_TEST)		+= usbtest.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
+
+obj-$(CONFIG_USB_SISUSBVGA)	+= sisusbvga/
\ No newline at end of file
diff -puN /dev/null drivers/usb/misc/sisusbvga/Kconfig
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/misc/sisusbvga/Kconfig	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,14 @@
+
+config USB_SISUSBVGA
+	tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)"
+	depends on USB && USB_EHCI_HCD
+        ---help---
+	  Say Y here if you intend to attach a USB2VGA dongle based on a
+	  Net2280 and a SiS315 chip. 
+	  
+	  Note that this device requires a USB 2.0 host controller. It will not 
+	  work with USB 1.x controllers.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called sisusb.  If unsure, say N.
+
diff -puN /dev/null drivers/usb/misc/sisusbvga/Makefile
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/misc/sisusbvga/Makefile	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,6 @@
+#
+# Makefile for the sisusb driver (if driver is inside kernel tree).
+#
+
+obj-$(CONFIG_USB_SISUSBVGA) += sisusb.o
+
diff -puN /dev/null drivers/usb/misc/sisusbvga/sisusb.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/misc/sisusbvga/sisusb.c	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,3144 @@
+/*
+ * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
+ *
+ * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, this code is licensed under the
+ * terms of the GPL v2.
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * Redistribution and use in source and binary forms, with or without
+ * * modification, are permitted provided that the following conditions
+ * * are met:
+ * * 1) Redistributions of source code must retain the above copyright
+ * *    notice, this list of conditions and the following disclaimer.
+ * * 2) Redistributions in binary form must reproduce the above copyright
+ * *    notice, this list of conditions and the following disclaimer in the
+ * *    documentation and/or other materials provided with the distribution.
+ * * 3) The name of the author may not be used to endorse or promote products
+ * *    derived from this software without specific psisusbr written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
+ * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: 	Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/kref.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+
+#include "sisusb.h"
+
+#define SISUSB_DONTSYNC
+
+/* Forward declarations / clean-up routines */
+
+static struct usb_driver sisusb_driver;
+
+static DECLARE_MUTEX(disconnect_sem);
+
+static void
+sisusb_free_buffers(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	for (i = 0; i < NUMOBUFS; i++) {
+		if (sisusb->obuf[i]) {
+			usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize,
+				sisusb->obuf[i], sisusb->transfer_dma_out[i]);
+			sisusb->obuf[i] = NULL;
+		}
+	}
+	if (sisusb->ibuf) {
+		usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize,
+			sisusb->ibuf, sisusb->transfer_dma_in);
+		sisusb->ibuf = NULL;
+	}
+}
+
+static void
+sisusb_free_urbs(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	for (i = 0; i < NUMOBUFS; i++) {
+		usb_free_urb(sisusb->sisurbout[i]);
+		sisusb->sisurbout[i] = NULL;
+	}
+	usb_free_urb(sisusb->sisurbin);
+	sisusb->sisurbin = NULL;
+}
+
+/* Level 0: USB transport layer */
+
+/* 1. out-bulks */
+
+/* out-urb management */
+
+/* Return 1 if all free, 0 otherwise */
+static int
+sisusb_all_free(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	for (i = 0; i < sisusb->numobufs; i++) {
+
+		if (sisusb->urbstatus[i] & SU_URB_BUSY)
+			return 0;
+
+	}
+
+	return 1;
+}
+
+/* Kill all busy URBs */
+static void
+sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	if (sisusb_all_free(sisusb))
+		return;
+
+	for (i = 0; i < sisusb->numobufs; i++) {
+
+		if (sisusb->urbstatus[i] & SU_URB_BUSY)
+			usb_kill_urb(sisusb->sisurbout[i]);
+
+	}
+}
+
+/* Return 1 if ok, 0 if error (not all complete within timeout) */
+static int
+sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb)
+{
+	int timeout = 5 * HZ, i = 1;
+
+	wait_event_timeout(sisusb->wait_q,
+				(i = sisusb_all_free(sisusb)),
+				 timeout);
+
+	return i;
+}
+
+static int
+sisusb_outurb_available(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	for (i = 0; i < sisusb->numobufs; i++) {
+
+		if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)
+			return i;
+
+	}
+
+	return -1;
+}
+
+static int
+sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb)
+{
+	int i, timeout = 5 * HZ;
+
+	wait_event_timeout(sisusb->wait_q,
+				((i = sisusb_outurb_available(sisusb)) >= 0),
+				timeout);
+
+	return i;
+}
+
+static int
+sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	i = sisusb_outurb_available(sisusb);
+
+	if (i >= 0)
+		sisusb->urbstatus[i] |= SU_URB_ALLOC;
+
+	return i;
+}
+
+static void
+sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index)
+{
+	if ((index >= 0) && (index < sisusb->numobufs))
+		sisusb->urbstatus[index] &= ~SU_URB_ALLOC;
+}
+
+/* completion callback */
+
+static void
+sisusb_bulk_completeout(struct urb *urb, struct pt_regs *regs)
+{
+	struct sisusb_urb_context *context = urb->context;
+	struct sisusb_usb_data *sisusb;
+
+	if (!context)
+		return;
+
+	sisusb = context->sisusb;
+
+	if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
+		return;
+
+#ifndef SISUSB_DONTSYNC
+	if (context->actual_length)
+		*(context->actual_length) += urb->actual_length;
+#endif
+
+	sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY;
+	wake_up(&sisusb->wait_q);
+}
+
+static int
+sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data,
+		int len, int *actual_length, int timeout, unsigned int tflags,
+		dma_addr_t transfer_dma)
+{
+	struct urb *urb = sisusb->sisurbout[index];
+	int retval, byteswritten = 0;
+
+	/* Set up URB */
+	urb->transfer_flags = 0;
+
+	usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
+		sisusb_bulk_completeout, &sisusb->urbout_context[index]);
+
+	urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK);
+	urb->actual_length = 0;
+
+	if ((urb->transfer_dma = transfer_dma))
+		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* Set up context */
+	sisusb->urbout_context[index].actual_length = (timeout) ?
+						NULL : actual_length;
+
+	/* Declare this urb/buffer in use */
+	sisusb->urbstatus[index] |= SU_URB_BUSY;
+
+	/* Submit URB */
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+
+	/* If OK, and if timeout > 0, wait for completion */
+	if ((retval == 0) && timeout) {
+		wait_event_timeout(sisusb->wait_q,
+				   (!(sisusb->urbstatus[index] & SU_URB_BUSY)),
+				   timeout);
+		if (sisusb->urbstatus[index] & SU_URB_BUSY) {
+			/* URB timed out... kill it and report error */
+			usb_kill_urb(urb);
+			retval = -ETIMEDOUT;
+		} else {
+			/* Otherwise, report urb status */
+			retval = urb->status;
+			byteswritten = urb->actual_length;
+		}
+	}
+
+	if (actual_length)
+		*actual_length = byteswritten;
+
+	return retval;
+}
+
+/* 2. in-bulks */
+
+/* completion callback */
+
+static void
+sisusb_bulk_completein(struct urb *urb, struct pt_regs *regs)
+{
+	struct sisusb_usb_data *sisusb = urb->context;
+
+	if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
+		return;
+
+	sisusb->completein = 1;
+	wake_up(&sisusb->wait_q);
+}
+
+static int
+sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len,
+		int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma)
+{
+	struct urb *urb = sisusb->sisurbin;
+	int retval, readbytes = 0;
+
+	urb->transfer_flags = 0;
+
+	usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
+			sisusb_bulk_completein, sisusb);
+
+	urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK);
+	urb->actual_length = 0;
+
+	if ((urb->transfer_dma = transfer_dma))
+		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	sisusb->completein = 0;
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval == 0) {
+		wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);
+		if (!sisusb->completein) {
+			/* URB timed out... kill it and report error */
+			usb_kill_urb(urb);
+			retval = -ETIMEDOUT;
+		} else {
+			/* URB completed within timout */
+			retval = urb->status;
+			readbytes = urb->actual_length;
+		}
+	}
+
+	if (actual_length)
+		*actual_length = readbytes;
+
+	return retval;
+}
+
+
+/* Level 1:  */
+
+/* Send a bulk message of variable size
+ *
+ * To copy the data from userspace, give pointer to "userbuffer",
+ * to copy from (non-DMA) kernel memory, give "kernbuffer". If
+ * both of these are NULL, it is assumed, that the transfer
+ * buffer "sisusb->obuf[index]" is set up with the data to send.
+ * Index is ignored if either kernbuffer or userbuffer is set.
+ * If async is nonzero, URBs will be sent without waiting for
+ * completion of the previous URB.
+ *
+ * (return 0 on success)
+ */
+
+static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
+		char *kernbuffer, const char __user *userbuffer, int index,
+		ssize_t *bytes_written, unsigned int tflags, int async)
+{
+	int result = 0, retry, count = len;
+	int passsize, thispass, transferred_len = 0;
+	int fromuser = (userbuffer != NULL) ? 1 : 0;
+	int fromkern = (kernbuffer != NULL) ? 1 : 0;
+	unsigned int pipe;
+	char *buffer;
+
+	(*bytes_written) = 0;
+
+	/* Sanity check */
+	if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
+		return -ENODEV;
+
+	/* If we copy data from kernel or userspace, force the
+	 * allocation of a buffer/urb. If we have the data in
+	 * the transfer buffer[index] already, reuse the buffer/URB
+	 * if the length is > buffer size. (So, transmitting
+	 * large data amounts directly from the transfer buffer
+	 * treats the buffer as a ring buffer. However, we need
+	 * to sync in this case.)
+	 */
+	if (fromuser || fromkern)
+		index = -1;
+	else if (len > sisusb->obufsize)
+		async = 0;
+
+	pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep);
+
+	do {
+		passsize = thispass = (sisusb->obufsize < count) ?
+						sisusb->obufsize : count;
+
+		if (index < 0)
+			index = sisusb_get_free_outbuf(sisusb);
+
+		if (index < 0)
+			return -EIO;
+
+		buffer = sisusb->obuf[index];
+
+		if (fromuser) {
+
+			if (copy_from_user(buffer, userbuffer, passsize))
+				return -EFAULT;
+
+			userbuffer += passsize;
+
+		} else if (fromkern) {
+
+			memcpy(buffer, kernbuffer, passsize);
+			kernbuffer += passsize;
+
+		}
+
+		retry = 5;
+		while (thispass) {
+
+			if (!sisusb->sisusb_dev)
+				return -ENODEV;
+
+			result = sisusb_bulkout_msg(sisusb,
+						index,
+						pipe,
+						buffer,
+						thispass,
+						&transferred_len,
+						async ? 0 : 5 * HZ,
+						tflags,
+						sisusb->transfer_dma_out[index]);
+
+			if (result == -ETIMEDOUT) {
+
+				/* Will not happen if async */
+				if (!retry--)
+					return -ETIME;
+
+				continue;
+
+			} else if ((result == 0) && !async && transferred_len) {
+
+				thispass -= transferred_len;
+				if (thispass) {
+					if (sisusb->transfer_dma_out) {
+						/* If DMA, copy remaining
+						 * to beginning of buffer
+						 */
+						memcpy(buffer,
+						       buffer + transferred_len,
+						       thispass);
+					} else {
+						/* If not DMA, simply increase
+						 * the pointer
+						 */
+						buffer += transferred_len;
+					}
+				}
+
+			} else
+				break;
+		};
+
+		if (result)
+			return result;
+
+		(*bytes_written) += passsize;
+		count            -= passsize;
+
+		/* Force new allocation in next iteration */
+		if (fromuser || fromkern)
+			index = -1;
+
+	} while (count > 0);
+
+	if (async) {
+#ifdef SISUSB_DONTSYNC
+		(*bytes_written) = len;
+		/* Some URBs/buffers might be busy */
+#else
+		sisusb_wait_all_out_complete(sisusb);
+		(*bytes_written) = transferred_len;
+		/* All URBs and all buffers are available */
+#endif
+	}
+
+	return ((*bytes_written) == len) ? 0 : -EIO;
+}
+
+/* Receive a bulk message of variable size
+ *
+ * To copy the data to userspace, give pointer to "userbuffer",
+ * to copy to kernel memory, give "kernbuffer". One of them
+ * MUST be set. (There is no technique for letting the caller
+ * read directly from the ibuf.)
+ *
+ */
+
+static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
+		void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read,
+		unsigned int tflags)
+{
+	int result = 0, retry, count = len;
+	int bufsize, thispass, transferred_len;
+	unsigned int pipe;
+	char *buffer;
+
+	(*bytes_read) = 0;
+
+	/* Sanity check */
+	if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
+		return -ENODEV;
+
+	pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep);
+	buffer = sisusb->ibuf;
+	bufsize = sisusb->ibufsize;
+
+	retry = 5;
+
+#ifdef SISUSB_DONTSYNC
+	if (!(sisusb_wait_all_out_complete(sisusb)))
+		return -EIO;
+#endif
+
+	while (count > 0) {
+
+		if (!sisusb->sisusb_dev)
+			return -ENODEV;
+
+		thispass = (bufsize < count) ? bufsize : count;
+
+		result = sisusb_bulkin_msg(sisusb,
+					   pipe,
+					   buffer,
+					   thispass,
+					   &transferred_len,
+					   5 * HZ,
+					   tflags,
+					   sisusb->transfer_dma_in);
+
+		if (transferred_len)
+			thispass = transferred_len;
+
+		else if (result == -ETIMEDOUT) {
+
+			if (!retry--)
+				return -ETIME;
+
+			continue;
+
+		} else
+			return -EIO;
+
+
+		if (thispass) {
+
+			(*bytes_read) += thispass;
+			count         -= thispass;
+
+			if (userbuffer) {
+
+				if (copy_to_user(userbuffer, buffer, thispass))
+					return -EFAULT;
+
+				userbuffer += thispass;
+
+			} else {
+
+				memcpy(kernbuffer, buffer, thispass);
+				kernbuffer += thispass;
+
+			}
+
+		}
+
+	}
+
+	return ((*bytes_read) == len) ? 0 : -EIO;
+}
+
+static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len,
+						struct sisusb_packet *packet)
+{
+	int ret;
+	int bytes_transferred = 0;
+	__le32 tmp;
+
+	if (len == 6)
+		packet->data = 0;
+
+#ifdef SISUSB_DONTSYNC
+	if (!(sisusb_wait_all_out_complete(sisusb)))
+		return 1;
+#endif
+
+	/* Eventually correct endianness */
+	SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
+
+	/* 1. send the packet */
+	ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len,
+			(char *)packet, NULL, 0, &bytes_transferred, 0, 0);
+
+	if ((ret == 0) && (len == 6)) {
+
+		/* 2. if packet len == 6, it means we read, so wait for 32bit
+		 *    return value and write it to packet->data
+		 */
+		ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4,
+				(char *)&tmp, NULL, &bytes_transferred, 0);
+
+		packet->data = le32_to_cpu(tmp);
+	}
+
+	return ret;
+}
+
+static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len,
+					struct sisusb_packet *packet,
+					unsigned int tflags)
+{
+	int ret;
+	int bytes_transferred = 0;
+	__le32 tmp;
+
+	if (len == 6)
+		packet->data = 0;
+
+#ifdef SISUSB_DONTSYNC
+	if (!(sisusb_wait_all_out_complete(sisusb)))
+		return 1;
+#endif
+
+	/* Eventually correct endianness */
+	SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
+
+	/* 1. send the packet */
+	ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len,
+			(char *)packet, NULL, 0, &bytes_transferred, tflags, 0);
+
+	if ((ret == 0) && (len == 6)) {
+
+		/* 2. if packet len == 6, it means we read, so wait for 32bit
+		 *    return value and write it to packet->data
+		 */
+		ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4,
+				(char *)&tmp, NULL, &bytes_transferred, 0);
+
+		packet->data = le32_to_cpu(tmp);
+	}
+
+	return ret;
+}
+
+/* access video memory and mmio (return 0 on success) */
+
+/* Low level */
+
+/* The following routines assume being used to transfer byte, word,
+ * long etc.
+ * This means that they assume "data" in machine endianness format.
+ */
+
+static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u8 data)
+{
+	struct sisusb_packet packet;
+	int ret;
+
+	packet.header  = (1 << (addr & 3)) | (type << 6);
+	packet.address = addr & ~3;
+	packet.data    = data << ((addr & 3) << 3);
+	ret = sisusb_send_packet(sisusb, 10, &packet);
+	return ret;
+}
+
+static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u16 data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header = (type << 6) | 0x0003;
+			packet.data   = (u32)data;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 1:
+			packet.header = (type << 6) | 0x0006;
+			packet.data   = (u32)data << 8;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 2:
+			packet.header = (type << 6) | 0x000c;
+			packet.data   = (u32)data << 16;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 3:
+			packet.header = (type << 6) | 0x0008;
+			packet.data   = (u32)data << 24;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			packet.data   = (u32)data >> 8;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+	}
+
+	return ret;
+}
+
+static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u32 data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header  = (type << 6) | 0x0007;
+			packet.data    = data & 0x00ffffff;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 1:
+			packet.header  = (type << 6) | 0x000e;
+			packet.data    = data << 8;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 2:
+			packet.header  = (type << 6) | 0x000c;
+			packet.data    = data << 16;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = (data >> 16) & 0x00ff;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 3:
+			packet.header  = (type << 6) | 0x0008;
+			packet.data    = data << 24;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0003;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = (data >> 8) & 0xffff;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+	}
+
+	return ret;
+}
+
+static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u32 data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header  = (type << 6) | 0x000f;
+			packet.data    = data;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 1:
+			packet.header  = (type << 6) | 0x000e;
+			packet.data    = data << 8;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = data >> 24;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 2:
+			packet.header  = (type << 6) | 0x000c;
+			packet.data    = data << 16;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0003;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = data >> 16;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 3:
+			packet.header  = (type << 6) | 0x0008;
+			packet.data    = data << 24;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0007;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = data >> 8;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+	}
+
+	return ret;
+}
+
+/* The xxx_bulk routines copy a buffer of variable size. They treat the
+ * buffer as chars, therefore lsb/msb has to be corrected if using the
+ * byte/word/long/etc routines for speed-up
+ *
+ * If data is from userland, set "userbuffer" (and clear "kernbuffer"),
+ * if data is in kernel space, set "kernbuffer" (and clear "userbuffer");
+ * if neither "kernbuffer" nor "userbuffer" are given, it is assumed
+ * that the data already is in the transfer buffer "sisusb->obuf[index]".
+ */
+
+static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
+				char *kernbuffer, int length,
+				const char __user *userbuffer, int index,
+				ssize_t *bytes_written)
+{
+	struct sisusb_packet packet;
+	int  ret = 0;
+	static int msgcount = 0;
+	u8   swap8, fromkern = kernbuffer ? 1 : 0;
+	u16  swap16;
+	u32  swap32, flag = (length >> 28) & 1;
+	char buf[4];
+
+	/* if neither kernbuffer not userbuffer are given, assume
+	 * data in obuf
+	 */
+	if (!fromkern && !userbuffer)
+		kernbuffer = sisusb->obuf[index];
+
+	(*bytes_written = 0);
+
+	length &= 0x00ffffff;
+
+	while (length) {
+
+	    switch (length) {
+
+		case 0:
+			return ret;
+
+		case 1:
+			if (userbuffer) {
+				if (get_user(swap8, (u8 __user *)userbuffer))
+					return -EFAULT;
+			} else
+				swap8 = kernbuffer[0];
+
+			ret = sisusb_write_memio_byte(sisusb,
+							SISUSB_TYPE_MEM,
+							addr, swap8);
+
+			if (!ret)
+				(*bytes_written)++;
+
+			return ret;
+
+		case 2:
+			if (userbuffer) {
+				if (get_user(swap16, (u16 __user *)userbuffer))
+					return -EFAULT;
+			} else
+				swap16 = (kernbuffer[0] << 8) | kernbuffer[1];
+
+			ret = sisusb_write_memio_word(sisusb,
+							SISUSB_TYPE_MEM,
+							addr,
+							swap16);
+
+			if (!ret)
+				(*bytes_written) += 2;
+
+			return ret;
+
+		case 3:
+			if (userbuffer) {
+				if (copy_from_user(&buf, userbuffer, 3))
+					return -EFAULT;
+
+				swap32 = (buf[0] << 16) |
+					 (buf[1] <<  8) |
+					 buf[2];
+			} else
+				swap32 = (kernbuffer[0] << 16) |
+					 (kernbuffer[1] <<  8) |
+					 kernbuffer[2];
+
+			ret = sisusb_write_memio_24bit(sisusb,
+							SISUSB_TYPE_MEM,
+							addr,
+							swap32);
+
+			if (!ret)
+				(*bytes_written) += 3;
+
+			return ret;
+
+		case 4:
+			if (userbuffer) {
+				if (get_user(swap32, (u32 __user *)userbuffer))
+					return -EFAULT;
+			} else
+				swap32 = (kernbuffer[0] << 24) |
+					 (kernbuffer[1] << 16) |
+					 (kernbuffer[2] <<  8) |
+					 kernbuffer[3];
+
+			ret = sisusb_write_memio_long(sisusb,
+							SISUSB_TYPE_MEM,
+							addr,
+							swap32);
+			if (!ret)
+				(*bytes_written) += 4;
+
+			return ret;
+
+		default:
+			if ((length & ~3) > 0x10000) {
+
+			   packet.header  = 0x001f;
+			   packet.address = 0x000001d4;
+			   packet.data    = addr;
+			   ret = sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			   packet.header  = 0x001f;
+			   packet.address = 0x000001d0;
+			   packet.data    = (length & ~3);
+			   ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			   packet.header  = 0x001f;
+			   packet.address = 0x000001c0;
+			   packet.data    = flag | 0x16;
+			   ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			   if (userbuffer) {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_LBULK_OUT,
+							(length & ~3),
+							NULL, userbuffer, 0,
+							bytes_written, 0, 1);
+				userbuffer += (*bytes_written);
+			   } else if (fromkern) {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_LBULK_OUT,
+							(length & ~3),
+							kernbuffer, NULL, 0,
+							bytes_written, 0, 1);
+				kernbuffer += (*bytes_written);
+			   } else {
+			ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_LBULK_OUT,
+							(length & ~3),
+							NULL, NULL, index,
+							bytes_written, 0, 1);
+				kernbuffer += ((*bytes_written) &
+						(sisusb->obufsize-1));
+			   }
+
+			} else {
+
+			   packet.header  = 0x001f;
+			   packet.address = 0x00000194;
+			   packet.data    = addr;
+			   ret = sisusb_send_bridge_packet(sisusb, 10,
+			   					&packet, 0);
+			   packet.header  = 0x001f;
+			   packet.address = 0x00000190;
+			   packet.data    = (length & ~3);
+			   ret |= sisusb_send_bridge_packet(sisusb, 10,
+			   					&packet, 0);
+			   if (sisusb->flagb0 != 0x16) {
+				packet.header  = 0x001f;
+				packet.address = 0x00000180;
+				packet.data    = flag | 0x16;
+				ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+				sisusb->flagb0 = 0x16;
+			   }
+			   if (userbuffer) {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_BULK_OUT,
+							(length & ~3),
+							NULL, userbuffer, 0,
+							bytes_written, 0, 1);
+				userbuffer += (*bytes_written);
+			   } else if (fromkern) {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_BULK_OUT,
+							(length & ~3),
+							kernbuffer, NULL, 0,
+							bytes_written, 0, 1);
+				kernbuffer += (*bytes_written);
+			   } else {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_BULK_OUT,
+							(length & ~3),
+							NULL, NULL, index,
+							bytes_written, 0, 1);
+				kernbuffer += ((*bytes_written) &
+						(sisusb->obufsize-1));
+			   }
+			}
+			if (ret) {
+				msgcount++;
+				if (msgcount < 500)
+					printk(KERN_ERR
+						"sisusbvga[%d]: Wrote %d of "
+						"%d bytes, error %d\n",
+						sisusb->minor, *bytes_written,
+						length, ret);
+				else if (msgcount == 500)
+					printk(KERN_ERR
+						"sisusbvga[%d]: Too many errors"
+						", logging stopped\n",
+						sisusb->minor);
+			}
+			addr += (*bytes_written);
+			length -= (*bytes_written);
+	    }
+
+	    if (ret)
+	    	break;
+
+	}
+
+	return ret ? -EIO : 0;
+}
+
+static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u8 *data)
+{
+	struct sisusb_packet packet;
+	int ret;
+
+	CLEARPACKET(&packet);
+	packet.header  = (1 << (addr & 3)) | (type << 6);
+	packet.address = addr & ~3;
+	ret = sisusb_send_packet(sisusb, 6, &packet);
+	*data = (u8)(packet.data >> ((addr & 3) << 3));
+	return ret;
+}
+
+static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u16 *data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	CLEARPACKET(&packet);
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header = (type << 6) | 0x0003;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = (u16)(packet.data);
+			break;
+		case 1:
+			packet.header = (type << 6) | 0x0006;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = (u16)(packet.data >> 8);
+			break;
+		case 2:
+			packet.header = (type << 6) | 0x000c;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = (u16)(packet.data >> 16);
+			break;
+		case 3:
+			packet.header = (type << 6) | 0x0008;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = (u16)(packet.data >> 24);
+			packet.header = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= (u16)(packet.data << 8);
+	}
+
+	return ret;
+}
+
+static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u32 *data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header  = (type << 6) | 0x0007;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data & 0x00ffffff;
+			break;
+		case 1:
+			packet.header  = (type << 6) | 0x000e;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 8;
+			break;
+		case 2:
+			packet.header  = (type << 6) | 0x000c;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 16;
+			packet.header  = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= ((packet.data & 0xff) << 16);
+			break;
+		case 3:
+			packet.header  = (type << 6) | 0x0008;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 24;
+			packet.header  = (type << 6) | 0x0003;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= ((packet.data & 0xffff) << 8);
+	}
+
+	return ret;
+}
+
+static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u32 *data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header  = (type << 6) | 0x000f;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data;
+			break;
+		case 1:
+			packet.header  = (type << 6) | 0x000e;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 8;
+			packet.header  = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= (packet.data << 24);
+			break;
+		case 2:
+			packet.header  = (type << 6) | 0x000c;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 16;
+			packet.header  = (type << 6) | 0x0003;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= (packet.data << 16);
+			break;
+		case 3:
+			packet.header  = (type << 6) | 0x0008;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 24;
+			packet.header  = (type << 6) | 0x0007;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= (packet.data << 8);
+	}
+
+	return ret;
+}
+
+static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
+				char *kernbuffer, int length,
+				char __user *userbuffer, ssize_t *bytes_read)
+{
+	int ret = 0;
+	char buf[4];
+	u16 swap16;
+	u32 swap32;
+
+	(*bytes_read = 0);
+
+	length &= 0x00ffffff;
+
+	while (length) {
+
+	    switch (length) {
+
+		case 0:
+			return ret;
+
+		case 1:
+
+			ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM,
+								addr, &buf[0]);
+			if (!ret) {
+				(*bytes_read)++;
+				if (userbuffer) {
+					if (put_user(buf[0],
+						(u8 __user *)userbuffer)) {
+						return -EFAULT;
+					}
+				} else {
+					kernbuffer[0] = buf[0];
+				}
+			}
+			return ret;
+
+		case 2:
+			ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM,
+								addr, &swap16);
+			if (!ret) {
+				(*bytes_read) += 2;
+				if (userbuffer) {
+					if (put_user(swap16,
+						(u16 __user *)userbuffer))
+						return -EFAULT;
+				} else {
+					kernbuffer[0] = swap16 >> 8;
+					kernbuffer[1] = swap16 & 0xff;
+				}
+			}
+			return ret;
+
+		case 3:
+			ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM,
+								addr, &swap32);
+			if (!ret) {
+				(*bytes_read) += 3;
+				buf[0] = (swap32 >> 16) & 0xff;
+				buf[1] = (swap32 >> 8) & 0xff;
+				buf[2] = swap32 & 0xff;
+				if (userbuffer) {
+					if (copy_to_user(userbuffer, &buf[0], 3))
+						return -EFAULT;
+				} else {
+					kernbuffer[0] = buf[0];
+					kernbuffer[1] = buf[1];
+					kernbuffer[2] = buf[2];
+				}
+			}
+			return ret;
+
+		default:
+			ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM,
+								addr, &swap32);
+			if (!ret) {
+				(*bytes_read) += 4;
+				if (userbuffer) {
+					if (put_user(swap32,
+						(u32 __user *)userbuffer))
+						return -EFAULT;
+
+					userbuffer += 4;
+				} else {
+					kernbuffer[0] = (swap32 >> 24) & 0xff;
+					kernbuffer[1] = (swap32 >> 16) & 0xff;
+					kernbuffer[2] = (swap32 >> 8) & 0xff;
+					kernbuffer[3] = swap32 & 0xff;
+					kernbuffer += 4;
+				}
+				addr += 4;
+				length -= 4;
+			}
+#if 0		/* That does not work, as EP 2 is an OUT EP! */
+		default:
+			CLEARPACKET(&packet);
+			packet.header  = 0x001f;
+			packet.address = 0x000001a0;
+			packet.data    = 0x00000006;
+			ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			packet.header  = 0x001f;
+			packet.address = 0x000001b0;
+			packet.data    = (length & ~3) | 0x40000000;
+			ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			packet.header  = 0x001f;
+			packet.address = 0x000001b4;
+			packet.data    = addr;
+			ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			packet.header  = 0x001f;
+			packet.address = 0x000001a4;
+			packet.data    = 0x00000001;
+			ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			if (userbuffer) {
+				ret |= sisusb_recv_bulk_msg(sisusb,
+							SISUSB_EP_GFX_BULK_IN,
+							(length & ~3),
+							NULL, userbuffer,
+							bytes_read, 0);
+				if (!ret) userbuffer += (*bytes_read);
+			} else {
+				ret |= sisusb_recv_bulk_msg(sisusb,
+							SISUSB_EP_GFX_BULK_IN,
+							(length & ~3),
+							kernbuffer, NULL,
+							bytes_read, 0);
+				if (!ret) kernbuffer += (*bytes_read);
+			}
+			addr += (*bytes_read);
+			length -= (*bytes_read);
+#endif
+	    }
+
+	    if (ret)
+	    	break;
+	}
+
+	return ret;
+}
+
+/* High level: Gfx (indexed) register access */
+
+static int
+sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data)
+{
+	int ret;
+	ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
+	ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
+	return ret;
+}
+
+static int
+sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data)
+{
+	int ret;
+	ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
+	ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
+	return ret;
+}
+
+static int
+sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
+							u8 myand, u8 myor)
+{
+	int ret;
+	u8 tmp;
+
+	ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
+	ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
+	tmp &= myand;
+	tmp |= myor;
+	ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
+	return ret;
+}
+
+static int
+sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx,
+							u8 data, u8 mask)
+{
+	int ret;
+	u8 tmp;
+	ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
+	ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
+	tmp &= ~(mask);
+	tmp |= (data & mask);
+	ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
+	return ret;
+}
+
+static int
+sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor)
+{
+	return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor));
+}
+
+static int
+sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand)
+{
+	return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00));
+}
+
+/* access pci config registers (reg numbers 0, 4, 8, etc) */
+
+static int
+sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data)
+{
+	struct sisusb_packet packet;
+	int ret;
+
+	packet.header = 0x008f;
+	packet.address = regnum | 0x10000;
+	packet.data = data;
+	ret = sisusb_send_packet(sisusb, 10, &packet);
+	return ret;
+}
+
+static int
+sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data)
+{
+	struct sisusb_packet packet;
+	int ret;
+
+	packet.header = 0x008f;
+	packet.address = (u32)regnum | 0x10000;
+	ret = sisusb_send_packet(sisusb, 6, &packet);
+	*data = packet.data;
+	return ret;
+}
+
+/* Clear video RAM */
+
+static int
+sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length)
+{
+	int ret, i, j;
+
+	if (address < sisusb->vrambase)
+		return 1;
+
+	if (address >= sisusb->vrambase + sisusb->vramsize)
+		return 1;
+
+	if (address + length > sisusb->vrambase + sisusb->vramsize)
+		length = sisusb->vrambase + sisusb->vramsize - address;
+
+	if (length <= 0)
+		return 0;
+
+	/* allocate free buffer/urb and clear the buffer */
+	if ((i = sisusb_alloc_outbuf(sisusb)) < 0)
+		return -EBUSY;
+
+	memset(sisusb->obuf[i], 0, sisusb->obufsize);
+
+	/* We can write a length > buffer size here. The buffer
+	 * data will simply be re-used (like a ring-buffer).
+	 */
+	ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j);
+
+	/* Free the buffer/urb */
+	sisusb_free_outbuf(sisusb, i);
+
+	return ret;
+}
+
+/* Initialize the graphics core (return 0 on success)
+ * This resets the graphics hardware and puts it into
+ * a defined mode (640x480@60Hz)
+ */
+
+#define GETREG(r,d)     sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
+#define SETREG(r,d)	sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
+#define SETIREG(r,i,d)	sisusb_setidxreg(sisusb, r, i, d)
+#define GETIREG(r,i,d)  sisusb_getidxreg(sisusb, r, i, d)
+#define SETIREGOR(r,i,o)	sisusb_setidxregor(sisusb, r, i, o)
+#define SETIREGAND(r,i,a)	sisusb_setidxregand(sisusb, r, i, a)
+#define SETIREGANDOR(r,i,a,o)	sisusb_setidxregandor(sisusb, r, i, a, o)
+#define READL(a,d)	sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
+#define WRITEL(a,d) 	sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
+#define READB(a,d)	sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
+#define WRITEB(a,d) 	sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
+
+static int
+sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype)
+{
+	int ret;
+	u8 tmp8;
+
+	ret = GETIREG(SISSR, 0x16, &tmp8);
+	if (ramtype <= 1) {
+		tmp8 &= 0x3f;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 |= 0x80;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+	} else {
+		tmp8 |= 0xc0;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 &= 0x0f;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 |= 0x80;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 &= 0x0f;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 |= 0xd0;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 &= 0x0f;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 |= 0xa0;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+	}
+	return ret;
+}
+
+static int
+sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab)
+{
+	int ret;
+	u8  ramtype, done = 0;
+	u32 t0, t1, t2, t3;
+	u32 ramptr = SISUSB_PCI_MEMBASE;
+
+	ret = GETIREG(SISSR, 0x3a, &ramtype);
+	ramtype &= 3;
+
+	ret |= SETIREG(SISSR, 0x13, 0x00);
+
+	if (ramtype <= 1) {
+		ret |= SETIREG(SISSR, 0x14, 0x12);
+		ret |= SETIREGAND(SISSR, 0x15, 0xef);
+	} else {
+		ret |= SETIREG(SISSR, 0x14, 0x02);
+	}
+
+	ret |= sisusb_triggersr16(sisusb, ramtype);
+	ret |= WRITEL(ramptr +  0, 0x01234567);
+	ret |= WRITEL(ramptr +  4, 0x456789ab);
+	ret |= WRITEL(ramptr +  8, 0x89abcdef);
+	ret |= WRITEL(ramptr + 12, 0xcdef0123);
+	ret |= WRITEL(ramptr + 16, 0x55555555);
+	ret |= WRITEL(ramptr + 20, 0x55555555);
+	ret |= WRITEL(ramptr + 24, 0xffffffff);
+	ret |= WRITEL(ramptr + 28, 0xffffffff);
+	ret |= READL(ramptr +  0, &t0);
+	ret |= READL(ramptr +  4, &t1);
+	ret |= READL(ramptr +  8, &t2);
+	ret |= READL(ramptr + 12, &t3);
+
+	if (ramtype <= 1) {
+
+		*chab = 0; *bw = 64;
+
+		if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) {
+			if ((t1 == 0x456789ab) && (t0 == 0x01234567)) {
+				*chab = 0; *bw = 64;
+				ret |= SETIREGAND(SISSR, 0x14, 0xfd);
+			}
+		}
+		if ((t1 != 0x456789ab) || (t0 != 0x01234567)) {
+			*chab = 1; *bw = 64;
+			ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01);
+
+			ret |= sisusb_triggersr16(sisusb, ramtype);
+			ret |= WRITEL(ramptr +  0, 0x89abcdef);
+			ret |= WRITEL(ramptr +  4, 0xcdef0123);
+			ret |= WRITEL(ramptr +  8, 0x55555555);
+			ret |= WRITEL(ramptr + 12, 0x55555555);
+			ret |= WRITEL(ramptr + 16, 0xaaaaaaaa);
+			ret |= WRITEL(ramptr + 20, 0xaaaaaaaa);
+			ret |= READL(ramptr +  4, &t1);
+
+			if (t1 != 0xcdef0123) {
+				*bw = 32;
+				ret |= SETIREGOR(SISSR, 0x15, 0x10);
+			}
+		}
+
+	} else {
+
+		*chab = 0; *bw = 64;	/* default: cha, bw = 64 */
+
+		done = 0;
+
+		if (t1 == 0x456789ab) {
+			if (t0 == 0x01234567) {
+				*chab = 0; *bw = 64;
+				done = 1;
+			}
+		} else {
+			if (t0 == 0x01234567) {
+				*chab = 0; *bw = 32;
+				ret |= SETIREG(SISSR, 0x14, 0x00);
+				done = 1;
+			}
+		}
+
+		if (!done) {
+			ret |= SETIREG(SISSR, 0x14, 0x03);
+			ret |= sisusb_triggersr16(sisusb, ramtype);
+
+			ret |= WRITEL(ramptr +  0, 0x01234567);
+			ret |= WRITEL(ramptr +  4, 0x456789ab);
+			ret |= WRITEL(ramptr +  8, 0x89abcdef);
+			ret |= WRITEL(ramptr + 12, 0xcdef0123);
+			ret |= WRITEL(ramptr + 16, 0x55555555);
+			ret |= WRITEL(ramptr + 20, 0x55555555);
+			ret |= WRITEL(ramptr + 24, 0xffffffff);
+			ret |= WRITEL(ramptr + 28, 0xffffffff);
+			ret |= READL(ramptr +  0, &t0);
+			ret |= READL(ramptr +  4, &t1);
+
+			if (t1 == 0x456789ab) {
+				if (t0 == 0x01234567) {
+					*chab = 1; *bw = 64;
+					return ret;
+				} /* else error */
+			} else {
+				if (t0 == 0x01234567) {
+					*chab = 1; *bw = 32;
+					ret |= SETIREG(SISSR, 0x14, 0x01);
+				} /* else error */
+			}
+		}
+	}
+	return ret;
+}
+
+static int
+sisusb_verify_mclk(struct sisusb_usb_data *sisusb)
+{
+	int ret = 0;
+	u32 ramptr = SISUSB_PCI_MEMBASE;
+	u8 tmp1, tmp2, i, j;
+
+	ret |= WRITEB(ramptr, 0xaa);
+	ret |= WRITEB(ramptr + 16, 0x55);
+	ret |= READB(ramptr, &tmp1);
+	ret |= READB(ramptr + 16, &tmp2);
+	if ((tmp1 != 0xaa) || (tmp2 != 0x55)) {
+		for (i = 0, j = 16; i < 2; i++, j += 16) {
+			ret |= GETIREG(SISSR, 0x21, &tmp1);
+			ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb));
+			ret |= SETIREGOR(SISSR, 0x3c, 0x01);  /* not on 330 */
+			ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */
+			ret |= SETIREG(SISSR, 0x21, tmp1);
+			ret |= WRITEB(ramptr + 16 + j, j);
+			ret |= READB(ramptr + 16 + j, &tmp1);
+			if (tmp1 == j) {
+				ret |= WRITEB(ramptr + j, j);
+				break;
+			}
+		}
+	}
+	return ret;
+}
+
+static int
+sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index,
+			u8 rankno, u8 chab, const u8 dramtype[][5],
+			int bw)
+{
+	int ret = 0, ranksize;
+	u8 tmp;
+
+	*iret = 0;
+
+	if ((rankno == 2) && (dramtype[index][0] == 2))
+		return ret;
+
+	ranksize = dramtype[index][3] / 2 * bw / 32;
+
+	if ((ranksize * rankno) > 128)
+		return ret;
+
+	tmp = 0;
+	while ((ranksize >>= 1) > 0) tmp += 0x10;
+	tmp |= ((rankno - 1) << 2);
+	tmp |= ((bw / 64) & 0x02);
+	tmp |= (chab & 0x01);
+
+	ret = SETIREG(SISSR, 0x14, tmp);
+	ret |= sisusb_triggersr16(sisusb, 0); /* sic! */
+
+	*iret = 1;
+
+	return ret;
+}
+
+static int
+sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn)
+{
+	int ret = 0, i;
+	u32 j, tmp;
+
+	*iret = 0;
+
+	for (i = 0, j = 0; i < testn; i++) {
+		ret |= WRITEL(sisusb->vrambase + j, j);
+		j += inc;
+	}
+
+	for (i = 0, j = 0; i < testn; i++) {
+		ret |= READL(sisusb->vrambase + j, &tmp);
+		if (tmp != j) return ret;
+		j += inc;
+	}
+
+	*iret = 1;
+	return ret;
+}
+
+static int
+sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno,
+					int idx, int bw, const u8 rtype[][5])
+{
+	int ret = 0, i, i2ret;
+	u32 inc;
+
+	*iret = 0;
+
+	for (i = rankno; i >= 1; i--) {
+		inc = 1 << (rtype[idx][2] +
+			    rtype[idx][1] +
+			    rtype[idx][0] +
+			    bw / 64 + i);
+		ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
+		if (!i2ret)
+			return ret;
+	}
+
+	inc = 1 << (rtype[idx][2] + bw / 64 + 2);
+	ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4);
+	if (!i2ret)
+		return ret;
+
+	inc = 1 << (10 + bw / 64);
+	ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
+	if (!i2ret)
+		return ret;
+
+	*iret = 1;
+	return ret;
+}
+
+static int
+sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw,
+								int chab)
+{
+	int ret = 0, i2ret = 0, i, j;
+	static const u8 sdramtype[13][5] = {
+		{ 2, 12, 9, 64, 0x35 },
+		{ 1, 13, 9, 64, 0x44 },
+		{ 2, 12, 8, 32, 0x31 },
+		{ 2, 11, 9, 32, 0x25 },
+		{ 1, 12, 9, 32, 0x34 },
+		{ 1, 13, 8, 32, 0x40 },
+		{ 2, 11, 8, 16, 0x21 },
+		{ 1, 12, 8, 16, 0x30 },
+		{ 1, 11, 9, 16, 0x24 },
+		{ 1, 11, 8,  8, 0x20 },
+		{ 2,  9, 8,  4, 0x01 },
+		{ 1, 10, 8,  4, 0x10 },
+		{ 1,  9, 8,  2, 0x00 }
+	};
+
+	*iret = 1; /* error */
+
+	for (i = 0; i < 13; i++) {
+		ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]);
+		for (j = 2; j > 0; j--) {
+			ret |= sisusb_set_rank(sisusb, &i2ret, i, j,
+						chab, sdramtype, bw);
+			if (!i2ret)
+				continue;
+
+			ret |= sisusb_check_ranks(sisusb, &i2ret, j, i,
+						bw, sdramtype);
+			if (i2ret) {
+				*iret = 0;	/* ram size found */
+				return ret;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int
+sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr)
+{
+	int ret = 0;
+	u32 address;
+	int i, length, modex, modey, bpp;
+
+	modex = 640; modey = 480; bpp = 2;
+
+	address = sisusb->vrambase;	/* Clear video ram */
+
+	if (clrall)
+		length = sisusb->vramsize;
+	else
+		length = modex * bpp * modey;
+
+	ret = sisusb_clear_vram(sisusb, address, length);
+
+	if (!ret && drwfr) {
+		for (i = 0; i < modex; i++) {
+			address = sisusb->vrambase + (i * bpp);
+			ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+							address, 0xf100);
+			address += (modex * (modey-1) * bpp);
+			ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+							address, 0xf100);
+		}
+		for (i = 0; i < modey; i++) {
+			address = sisusb->vrambase + ((i * modex) * bpp);
+			ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+							address, 0xf100);
+			address += ((modex - 1) * bpp);
+			ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+							address, 0xf100);
+		}
+	}
+
+	return ret;
+}
+
+static int
+sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines)
+{
+	int ret = 0, i, j, modex, modey, bpp, du;
+	u8 sr31, cr63, tmp8;
+	static const char attrdata[] = {
+		0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+		0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+		0x01,0x00,0x00,0x00
+	};
+	static const char crtcrdata[] = {
+		0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
+		0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
+		0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
+		0xff
+	};
+	static const char grcdata[] = {
+		0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
+		0xff
+	};
+	static const char crtcdata[] = {
+		0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e,
+		0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05,
+		0x00
+	};
+
+	modex = 640; modey = 480; bpp = 2;
+
+	GETIREG(SISSR, 0x31, &sr31);
+	GETIREG(SISCR, 0x63, &cr63);
+	SETIREGOR(SISSR, 0x01, 0x20);
+	SETIREG(SISCR, 0x63, cr63 & 0xbf);
+	SETIREGOR(SISCR, 0x17, 0x80);
+	SETIREGOR(SISSR, 0x1f, 0x04);
+	SETIREGAND(SISSR, 0x07, 0xfb);
+	SETIREG(SISSR, 0x00, 0x03);	/* seq */
+	SETIREG(SISSR, 0x01, 0x21);
+	SETIREG(SISSR, 0x02, 0x0f);
+	SETIREG(SISSR, 0x03, 0x00);
+	SETIREG(SISSR, 0x04, 0x0e);
+	SETREG(SISMISCW, 0x23);		/* misc */
+	for (i = 0; i <= 0x18; i++) {	/* crtc */
+		SETIREG(SISCR, i, crtcrdata[i]);
+	}
+	for (i = 0; i <= 0x13; i++) {	/* att */
+		GETREG(SISINPSTAT, &tmp8);
+		SETREG(SISAR, i);
+		SETREG(SISAR, attrdata[i]);
+	}
+	GETREG(SISINPSTAT, &tmp8);
+	SETREG(SISAR, 0x14);
+	SETREG(SISAR, 0x00);
+	GETREG(SISINPSTAT, &tmp8);
+	SETREG(SISAR, 0x20);
+	GETREG(SISINPSTAT, &tmp8);
+	for (i = 0; i <= 0x08; i++) {	/* grc */
+		SETIREG(SISGR, i, grcdata[i]);
+	}
+	SETIREGAND(SISGR, 0x05, 0xbf);
+	for (i = 0x0A; i <= 0x0E; i++) {	/* clr ext */
+		SETIREG(SISSR, i, 0x00);
+	}
+	SETIREGAND(SISSR, 0x37, 0xfe);
+	SETREG(SISMISCW, 0xef);		/* sync */
+	SETIREG(SISCR, 0x11, 0x00);	/* crtc */
+	for (j = 0x00, i = 0; i <= 7; i++, j++) {
+		SETIREG(SISCR, j, crtcdata[i]);
+	}
+	for (j = 0x10; i <= 10; i++, j++) {
+		SETIREG(SISCR, j, crtcdata[i]);
+	}
+	for (j = 0x15; i <= 12; i++, j++) {
+		SETIREG(SISCR, j, crtcdata[i]);
+	}
+	for (j = 0x0A; i <= 15; i++, j++) {
+		SETIREG(SISSR, j, crtcdata[i]);
+	}
+	SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0));
+	SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5));
+	SETIREG(SISCR, 0x14, 0x4f);
+	du = (modex / 16) * (bpp * 2);	/* offset/pitch */
+	if (modex % 16) du += bpp;
+	SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f));
+	SETIREG(SISCR, 0x13, (du & 0xff));
+	du <<= 5;
+	tmp8 = du >> 8;
+	if (du & 0xff) tmp8++;
+	SETIREG(SISSR, 0x10, tmp8);
+	SETIREG(SISSR, 0x31, 0x00);	/* VCLK */
+	SETIREG(SISSR, 0x2b, 0x1b);
+	SETIREG(SISSR, 0x2c, 0xe1);
+	SETIREG(SISSR, 0x2d, 0x01);
+	SETIREGAND(SISSR, 0x3d, 0xfe);	/* FIFO */
+	SETIREG(SISSR, 0x08, 0xae);
+	SETIREGAND(SISSR, 0x09, 0xf0);
+	SETIREG(SISSR, 0x08, 0x34);
+	SETIREGOR(SISSR, 0x3d, 0x01);
+	SETIREGAND(SISSR, 0x1f, 0x3f);	/* mode regs */
+	SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a);
+	SETIREG(SISCR, 0x19, 0x00);
+	SETIREGAND(SISCR, 0x1a, 0xfc);
+	SETIREGAND(SISSR, 0x0f, 0xb7);
+	SETIREGAND(SISSR, 0x31, 0xfb);
+	SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0);
+	SETIREGAND(SISSR, 0x32, 0xf3);
+	SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03);
+	SETIREG(SISCR, 0x52, 0x6c);
+
+	SETIREG(SISCR, 0x0d, 0x00);	/* adjust frame */
+	SETIREG(SISCR, 0x0c, 0x00);
+	SETIREG(SISSR, 0x0d, 0x00);
+	SETIREGAND(SISSR, 0x37, 0xfe);
+
+	SETIREG(SISCR, 0x32, 0x20);
+	SETIREGAND(SISSR, 0x01, 0xdf);	/* enable display */
+	SETIREG(SISCR, 0x63, (cr63 & 0xbf));
+	SETIREG(SISSR, 0x31, (sr31 & 0xfb));
+
+	if (touchengines) {
+		SETIREG(SISSR, 0x20, 0xa1);	/* enable engines */
+		SETIREGOR(SISSR, 0x1e, 0x5a);
+
+		SETIREG(SISSR, 0x26, 0x01);	/* disable cmdqueue */
+		SETIREG(SISSR, 0x27, 0x1f);
+		SETIREG(SISSR, 0x26, 0x00);
+	}
+
+	SETIREG(SISCR, 0x34, 0x44);  	/* we just set std mode #44 */
+
+	return ret;
+}
+
+static int
+sisusb_init_gfxcore(struct sisusb_usb_data *sisusb)
+{
+	int ret = 0, i, j, bw, chab, iret, retry = 3;
+	u8 tmp8, ramtype;
+	u32 tmp32;
+	static const char mclktable[] = {
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143
+	};
+	static const char eclktable[] = {
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143
+	};
+	static const char ramtypetable1[] = {
+		0x00, 0x04, 0x60, 0x60,
+		0x0f, 0x0f, 0x1f, 0x1f,
+		0xba, 0xba, 0xba, 0xba,
+		0xa9, 0xa9, 0xac, 0xac,
+		0xa0, 0xa0, 0xa0, 0xa8,
+		0x00, 0x00, 0x02, 0x02,
+		0x30, 0x30, 0x40, 0x40
+	};
+	static const char ramtypetable2[] = {
+		0x77, 0x77, 0x44, 0x44,
+		0x77, 0x77, 0x44, 0x44,
+		0x00, 0x00, 0x00, 0x00,
+		0x5b, 0x5b, 0xab, 0xab,
+		0x00, 0x00, 0xf0, 0xf8
+	};
+
+	while (retry--) {
+
+		/* Enable VGA */
+		ret = GETREG(SISVGAEN, &tmp8);
+		ret |= SETREG(SISVGAEN, (tmp8 | 0x01));
+
+		/* Enable GPU access to VRAM */
+		ret |= GETREG(SISMISCR, &tmp8);
+		ret |= SETREG(SISMISCW, (tmp8 | 0x01));
+
+		if (ret) continue;
+
+		/* Reset registers */
+		ret |= SETIREGAND(SISCR, 0x5b, 0xdf);
+		ret |= SETIREG(SISSR, 0x05, 0x86);
+		ret |= SETIREGOR(SISSR, 0x20, 0x01);
+
+		ret |= SETREG(SISMISCW, 0x67);
+
+		for (i = 0x06; i <= 0x1f; i++) {
+			ret |= SETIREG(SISSR, i, 0x00);
+		}
+		for (i = 0x21; i <= 0x27; i++) {
+			ret |= SETIREG(SISSR, i, 0x00);
+		}
+		for (i = 0x31; i <= 0x3d; i++) {
+			ret |= SETIREG(SISSR, i, 0x00);
+		}
+		for (i = 0x12; i <= 0x1b; i++) {
+			ret |= SETIREG(SISSR, i, 0x00);
+		}
+		for (i = 0x79; i <= 0x7c; i++) {
+			ret |= SETIREG(SISCR, i, 0x00);
+		}
+
+		if (ret) continue;
+
+		ret |= SETIREG(SISCR, 0x63, 0x80);
+
+		ret |= GETIREG(SISSR, 0x3a, &ramtype);
+		ramtype &= 0x03;
+
+		ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]);
+		ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]);
+		ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]);
+
+		ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]);
+		ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]);
+		ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]);
+
+		ret |= SETIREG(SISSR, 0x07, 0x18);
+		ret |= SETIREG(SISSR, 0x11, 0x0f);
+
+		if (ret) continue;
+
+		for (i = 0x15, j = 0; i <= 0x1b; i++, j++) {
+			ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]);
+		}
+		for (i = 0x40, j = 0; i <= 0x44; i++, j++) {
+			ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]);
+		}
+
+		ret |= SETIREG(SISCR, 0x49, 0xaa);
+
+		ret |= SETIREG(SISSR, 0x1f, 0x00);
+		ret |= SETIREG(SISSR, 0x20, 0xa0);
+		ret |= SETIREG(SISSR, 0x23, 0xf6);
+		ret |= SETIREG(SISSR, 0x24, 0x0d);
+		ret |= SETIREG(SISSR, 0x25, 0x33);
+
+		ret |= SETIREG(SISSR, 0x11, 0x0f);
+
+		ret |= SETIREGOR(SISPART1, 0x2f, 0x01);
+
+		ret |= SETIREGAND(SISCAP, 0x3f, 0xef);
+
+		if (ret) continue;
+
+		ret |= SETIREG(SISPART1, 0x00, 0x00);
+
+		ret |= GETIREG(SISSR, 0x13, &tmp8);
+		tmp8 >>= 4;
+
+		ret |= SETIREG(SISPART1, 0x02, 0x00);
+		ret |= SETIREG(SISPART1, 0x2e, 0x08);
+
+		ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32);
+		tmp32 &= 0x00f00000;
+		tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03;
+		ret |= SETIREG(SISSR, 0x25, tmp8);
+		tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88;
+		ret |= SETIREG(SISCR, 0x49, tmp8);
+
+		ret |= SETIREG(SISSR, 0x27, 0x1f);
+		ret |= SETIREG(SISSR, 0x31, 0x00);
+		ret |= SETIREG(SISSR, 0x32, 0x11);
+		ret |= SETIREG(SISSR, 0x33, 0x00);
+
+		if (ret) continue;
+
+		ret |= SETIREG(SISCR, 0x83, 0x00);
+
+		ret |= sisusb_set_default_mode(sisusb, 0);
+
+		ret |= SETIREGAND(SISSR, 0x21, 0xdf);
+		ret |= SETIREGOR(SISSR, 0x01, 0x20);
+		ret |= SETIREGOR(SISSR, 0x16, 0x0f);
+
+		ret |= sisusb_triggersr16(sisusb, ramtype);
+
+		/* Disable refresh */
+		ret |= SETIREGAND(SISSR, 0x17, 0xf8);
+		ret |= SETIREGOR(SISSR, 0x19, 0x03);
+
+		ret |= sisusb_getbuswidth(sisusb, &bw, &chab);
+		ret |= sisusb_verify_mclk(sisusb);
+
+		if (ramtype <= 1) {
+			ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab);
+			if (iret) {
+				printk(KERN_ERR "sisusbvga[%d]: RAM size "
+					"detection failed, "
+					"assuming 8MB video RAM\n",
+					sisusb->minor);
+				ret |= SETIREG(SISSR,0x14,0x31);
+				/* TODO */
+			}
+		} else {
+			printk(KERN_ERR "sisusbvga[%d]: DDR RAM device found, "
+					"assuming 8MB video RAM\n",
+					sisusb->minor);
+			ret |= SETIREG(SISSR,0x14,0x31);
+			/* *** TODO *** */
+		}
+
+		/* Enable refresh */
+		ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]);
+		ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]);
+		ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]);
+
+		ret |= SETIREGOR(SISSR, 0x21, 0x20);
+
+		ret |= SETIREG(SISSR, 0x22, 0xfb);
+		ret |= SETIREG(SISSR, 0x21, 0xa5);
+
+		if (ret == 0)
+			break;
+	}
+
+	return ret;
+}
+
+#undef SETREG
+#undef GETREG
+#undef SETIREG
+#undef GETIREG
+#undef SETIREGOR
+#undef SETIREGAND
+#undef SETIREGANDOR
+#undef READL
+#undef WRITEL
+
+static void
+sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
+{
+	u8 tmp8, tmp82, ramtype;
+	int bw = 0;
+	char *ramtypetext1 = NULL;
+	const char *ramtypetext2[] = {	"SDR SDRAM", "SDR SGRAM",
+					"DDR SDRAM", "DDR SGRAM" };
+	static const int busSDR[4]  = {64, 64, 128, 128};
+	static const int busDDR[4]  = {32, 32,  64,  64};
+	static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2};
+
+	sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8);
+	sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82);
+	sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype);
+	sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024;
+	ramtype &= 0x03;
+	switch ((tmp8 >> 2) & 0x03) {
+	case 0: ramtypetext1 = "1 ch/1 r";
+		if (tmp82 & 0x10) {
+			bw = 32;
+		} else {
+			bw = busSDR[(tmp8 & 0x03)];
+		}
+		break;
+	case 1: ramtypetext1 = "1 ch/2 r";
+		sisusb->vramsize <<= 1;
+		bw = busSDR[(tmp8 & 0x03)];
+		break;
+	case 2: ramtypetext1 = "asymmeric";
+		sisusb->vramsize += sisusb->vramsize/2;
+		bw = busDDRA[(tmp8 & 0x03)];
+		break;
+	case 3: ramtypetext1 = "2 channel";
+		sisusb->vramsize <<= 1;
+		bw = busDDR[(tmp8 & 0x03)];
+		break;
+	}
+
+	printk(KERN_INFO "sisusbvga[%d]: %dMB %s %s, bus width %d\n",
+			sisusb->minor, (sisusb->vramsize >> 20), ramtypetext1,
+			ramtypetext2[ramtype], bw);
+}
+
+static int
+sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb)
+{
+	struct sisusb_packet packet;
+	int ret;
+	u32 tmp32;
+
+	/* Do some magic */
+	packet.header  = 0x001f;
+	packet.address = 0x00000324;
+	packet.data    = 0x00000004;
+	ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	packet.header  = 0x001f;
+	packet.address = 0x00000364;
+	packet.data    = 0x00000004;
+	ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	packet.header  = 0x001f;
+	packet.address = 0x00000384;
+	packet.data    = 0x00000004;
+	ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	packet.header  = 0x001f;
+	packet.address = 0x00000100;
+	packet.data    = 0x00000700;
+	ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	packet.header  = 0x000f;
+	packet.address = 0x00000004;
+	ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0);
+	packet.data |= 0x17;
+	ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	/* Init BAR 0 (VRAM) */
+	ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+	ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0);
+	ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+	tmp32 &= 0x0f;
+	tmp32 |= SISUSB_PCI_MEMBASE;
+	ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32);
+
+	/* Init BAR 1 (MMIO) */
+	ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+	ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0);
+	ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+	tmp32 &= 0x0f;
+	tmp32 |= SISUSB_PCI_MMIOBASE;
+	ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32);
+
+	/* Init BAR 2 (i/o ports) */
+	ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+	ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0);
+	ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+	tmp32 &= 0x0f;
+	tmp32 |= SISUSB_PCI_IOPORTBASE;
+	ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32);
+
+	/* Enable memory and i/o access */
+	ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32);
+	tmp32 |= 0x3;
+	ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32);
+
+	if (ret == 0) {
+		/* Some further magic */
+		packet.header  = 0x001f;
+		packet.address = 0x00000050;
+		packet.data    = 0x000000ff;
+		ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+	}
+
+	return ret;
+}
+
+/* Initialize the graphics device (return 0 on success)
+ * This initializes the net2280 as well as the PCI registers
+ * of the graphics board.
+ */
+
+static int
+sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen)
+{
+	int ret = 0, test = 0;
+	u32 tmp32;
+
+	if (sisusb->devinit == 1) {
+		/* Read PCI BARs and see if they have been set up */
+		ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+		if (ret) return ret;
+		if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++;
+
+		ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+		if (ret) return ret;
+		if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++;
+
+		ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+		if (ret) return ret;
+		if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++;
+	}
+
+	/* No? So reset the device */
+	if ((sisusb->devinit == 0) || (test != 3)) {
+
+		ret |= sisusb_do_init_gfxdevice(sisusb);
+
+		if (ret == 0)
+			sisusb->devinit = 1;
+
+	}
+
+	if (sisusb->devinit) {
+		/* Initialize the graphics core */
+		if (sisusb_init_gfxcore(sisusb) == 0) {
+			sisusb->gfxinit = 1;
+			sisusb_get_ramconfig(sisusb);
+			ret |= sisusb_set_default_mode(sisusb, 1);
+			ret |= sisusb_setup_screen(sisusb, 1, initscreen);
+		}
+	}
+
+	return ret;
+}
+
+/* fops */
+
+int
+sisusb_open(struct inode *inode, struct file *file)
+{
+	struct sisusb_usb_data *sisusb;
+	struct usb_interface *interface;
+	int subminor = iminor(inode);
+
+	down(&disconnect_sem);
+
+	if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
+		printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
+				subminor);
+		up(&disconnect_sem);
+		return -ENODEV;
+	}
+
+	if (!(sisusb = usb_get_intfdata(interface))) {
+		up(&disconnect_sem);
+		return -ENODEV;
+	}
+
+	down(&sisusb->lock);
+
+	if (!sisusb->present || !sisusb->ready) {
+		up(&sisusb->lock);
+		up(&disconnect_sem);
+		return -ENODEV;
+	}
+
+	if (sisusb->isopen) {
+		up(&sisusb->lock);
+		up(&disconnect_sem);
+		return -EBUSY;
+	}
+
+	if (!sisusb->devinit) {
+		if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
+			if (sisusb_init_gfxdevice(sisusb, 0)) {
+				up(&sisusb->lock);
+				up(&disconnect_sem);
+				printk(KERN_ERR
+					"sisusbvga[%d]: Failed to initialize "
+					"device\n",
+					sisusb->minor);
+				return -EIO;
+			}
+		} else {
+			up(&sisusb->lock);
+			up(&disconnect_sem);
+			printk(KERN_ERR
+				"sisusbvga[%d]: Device not attached to "
+				"USB 2.0 hub\n",
+				sisusb->minor);
+			return -EIO;
+		}
+	}
+
+	/* increment usage count for the device */
+	kref_get(&sisusb->kref);
+
+	sisusb->isopen = 1;
+
+	file->private_data = sisusb;
+
+	up(&sisusb->lock);
+
+	up(&disconnect_sem);
+
+	printk(KERN_DEBUG "sisusbvga[%d]: opened", sisusb->minor);
+
+	return 0;
+}
+
+static void
+sisusb_delete(struct kref *kref)
+{
+	struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
+
+	if (!sisusb)
+		return;
+
+	if (sisusb->sisusb_dev)
+		usb_put_dev(sisusb->sisusb_dev);
+
+	sisusb->sisusb_dev = NULL;
+	sisusb_free_buffers(sisusb);
+	sisusb_free_urbs(sisusb);
+	kfree(sisusb);
+}
+
+int
+sisusb_release(struct inode *inode, struct file *file)
+{
+	struct sisusb_usb_data *sisusb;
+	int myminor;
+
+	down(&disconnect_sem);
+
+	if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) {
+		up(&disconnect_sem);
+		return -ENODEV;
+	}
+
+	down(&sisusb->lock);
+
+	if (sisusb->present) {
+		/* Wait for all URBs to finish if device still present */
+		if (!sisusb_wait_all_out_complete(sisusb))
+			sisusb_kill_all_busy(sisusb);
+	}
+
+	myminor = sisusb->minor;
+
+	sisusb->isopen = 0;
+	file->private_data = NULL;
+
+	up(&sisusb->lock);
+
+	/* decrement the usage count on our device */
+	kref_put(&sisusb->kref, sisusb_delete);
+
+	up(&disconnect_sem);
+
+	printk(KERN_DEBUG "sisusbvga[%d]: released", myminor);
+
+	return 0;
+}
+
+ssize_t
+sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct sisusb_usb_data *sisusb;
+	ssize_t bytes_read = 0;
+	int errno = 0;
+	u8 buf8;
+	u16 buf16;
+	u32 buf32, address;
+
+	if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+		return -ENODEV;
+
+	down(&sisusb->lock);
+
+	/* Sanity check */
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+		up(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
+	    (*ppos) <  SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_IOPORTBASE +
+			SISUSB_PCI_IOPORTBASE;
+
+		/* Read i/o ports
+		 * Byte, word and long(32) can be read. As this
+		 * emulates inX instructions, the data returned is
+		 * in machine-endianness.
+		 */
+		switch (count) {
+
+			case 1:
+				if (sisusb_read_memio_byte(sisusb,
+							SISUSB_TYPE_IO,
+							address, &buf8))
+					errno = -EIO;
+				else if (put_user(buf8, (u8 __user *)buffer))
+					errno = -EFAULT;
+				else
+					bytes_read = 1;
+
+				break;
+
+			case 2:
+				if (sisusb_read_memio_word(sisusb,
+							SISUSB_TYPE_IO,
+							address, &buf16))
+					errno = -EIO;
+				else if (put_user(buf16, (u16 __user *)buffer))
+					errno = -EFAULT;
+				else
+					bytes_read = 2;
+
+				break;
+
+			case 4:
+				if (sisusb_read_memio_long(sisusb,
+							SISUSB_TYPE_IO,
+							address, &buf32))
+					errno = -EIO;
+				else if (put_user(buf32, (u32 __user *)buffer))
+					errno = -EFAULT;
+				else
+					bytes_read = 4;
+
+				break;
+
+			default:
+				errno = -EIO;
+
+		}
+
+	} else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
+		   (*ppos) <  SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_MEMBASE +
+			SISUSB_PCI_MEMBASE;
+
+		/* Read video ram
+		 * Remember: Data delivered is never endian-corrected
+		 */
+		errno = sisusb_read_mem_bulk(sisusb, address,
+					NULL, count, buffer, &bytes_read);
+
+		if (bytes_read)
+			errno = bytes_read;
+
+	} else  if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
+		    (*ppos) <  SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_MMIOBASE +
+			SISUSB_PCI_MMIOBASE;
+
+		/* Read MMIO
+		 * Remember: Data delivered is never endian-corrected
+		 */
+		errno = sisusb_read_mem_bulk(sisusb, address,
+					NULL, count, buffer, &bytes_read);
+
+		if (bytes_read)
+			errno = bytes_read;
+
+	} else  if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
+		    (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
+
+		if (count != 4) {
+			up(&sisusb->lock);
+			return -EINVAL;
+		}
+
+		address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
+
+		/* Read PCI config register
+		 * Return value delivered in machine endianness.
+		 */
+		if (sisusb_read_pci_config(sisusb, address, &buf32))
+			errno = -EIO;
+		else if (put_user(buf32, (u32 __user *)buffer))
+			errno = -EFAULT;
+		else
+			bytes_read = 4;
+
+	} else {
+
+		errno = -EBADFD;
+
+	}
+
+	(*ppos) += bytes_read;
+
+	up(&sisusb->lock);
+
+	return errno ? errno : bytes_read;
+}
+
+ssize_t
+sisusb_write(struct file *file, const char __user *buffer, size_t count,
+								loff_t *ppos)
+{
+	struct sisusb_usb_data *sisusb;
+	int errno = 0;
+	ssize_t bytes_written = 0;
+	u8 buf8;
+	u16 buf16;
+	u32 buf32, address;
+
+	if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+		return -ENODEV;
+
+	down(&sisusb->lock);
+
+	/* Sanity check */
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+		up(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
+	    (*ppos) <  SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_IOPORTBASE +
+			SISUSB_PCI_IOPORTBASE;
+
+		/* Write i/o ports
+		 * Byte, word and long(32) can be written. As this
+		 * emulates outX instructions, the data is expected
+		 * in machine-endianness.
+		 */
+		switch (count) {
+
+			case 1:
+				if (get_user(buf8, (u8 __user *)buffer))
+					errno = -EFAULT;
+				else if (sisusb_write_memio_byte(sisusb,
+							SISUSB_TYPE_IO,
+							address, buf8))
+					errno = -EIO;
+				else
+					bytes_written = 1;
+
+				break;
+
+			case 2:
+				if (get_user(buf16, (u16 __user *)buffer))
+					errno = -EFAULT;
+				else if (sisusb_write_memio_word(sisusb,
+							SISUSB_TYPE_IO,
+							address, buf16))
+					errno = -EIO;
+				else
+					bytes_written = 2;
+
+				break;
+
+			case 4:
+				if (get_user(buf32, (u32 __user *)buffer))
+					errno = -EFAULT;
+				else if (sisusb_write_memio_long(sisusb,
+							SISUSB_TYPE_IO,
+							address, buf32))
+					errno = -EIO;
+				else
+					bytes_written = 4;
+
+				break;
+
+			default:
+				errno = -EIO;
+		}
+
+	} else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
+		   (*ppos) <  SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_MEMBASE +
+			SISUSB_PCI_MEMBASE;
+
+		/* Write video ram.
+		 * Buffer is copied 1:1, therefore, on big-endian
+		 * machines, the data must be swapped by userland
+		 * in advance (if applicable; no swapping in 8bpp
+		 * mode or if YUV data is being transferred).
+		 */
+		errno = sisusb_write_mem_bulk(sisusb, address, NULL,
+					count, buffer, 0, &bytes_written);
+
+		if (bytes_written)
+			errno = bytes_written;
+
+	} else  if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
+		    (*ppos) <  SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_MMIOBASE +
+			SISUSB_PCI_MMIOBASE;
+
+		/* Write MMIO.
+		 * Buffer is copied 1:1, therefore, on big-endian
+		 * machines, the data must be swapped by userland
+		 * in advance.
+		 */
+		errno = sisusb_write_mem_bulk(sisusb, address, NULL,
+					count, buffer, 0, &bytes_written);
+
+		if (bytes_written)
+			errno = bytes_written;
+
+	} else  if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
+		    (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) {
+
+		if (count != 4) {
+			up(&sisusb->lock);
+			return -EINVAL;
+		}
+
+		address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
+
+		/* Write PCI config register.
+		 * Given value expected in machine endianness.
+		 */
+		if (get_user(buf32, (u32 __user *)buffer))
+			errno = -EFAULT;
+		else if (sisusb_write_pci_config(sisusb, address, buf32))
+			errno = -EIO;
+		else
+			bytes_written = 4;
+
+
+	} else {
+
+		/* Error */
+		errno = -EBADFD;
+
+	}
+
+	(*ppos) += bytes_written;
+
+	up(&sisusb->lock);
+
+	return errno ? errno : bytes_written;
+}
+
+static loff_t
+sisusb_lseek(struct file *file, loff_t offset, int orig)
+{
+	struct sisusb_usb_data *sisusb;
+	loff_t ret;
+
+	if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+		return -ENODEV;
+
+	down(&sisusb->lock);
+
+	/* Sanity check */
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+		up(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	switch (orig) {
+		case 0:
+			file->f_pos = offset;
+			ret = file->f_pos;
+			/* never negative, no force_successful_syscall needed */
+			break;
+		case 1:
+			file->f_pos += offset;
+			ret = file->f_pos;
+			/* never negative, no force_successful_syscall needed */
+			break;
+		default:
+			/* seeking relative to "end of file" is not supported */
+			ret = -EINVAL;
+	}
+
+	up(&sisusb->lock);
+	return ret;
+}
+
+static int
+sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
+							unsigned long arg)
+{
+	int 	retval, port, length;
+	u32	address;
+
+	port = y->data3 -
+		SISUSB_PCI_PSEUDO_IOPORTBASE +
+		SISUSB_PCI_IOPORTBASE;
+
+	switch (y->operation) {
+		case SUCMD_GET:
+			retval = sisusb_getidxreg(sisusb, port,
+							 y->data0, &y->data1);
+			if (!retval) {
+				if (copy_to_user((void __user *)arg, y,
+							sizeof(*y)))
+					retval = -EFAULT;
+			}
+			break;
+
+		case SUCMD_SET:
+			retval = sisusb_setidxreg(sisusb, port,
+						y->data0, y->data1);
+			break;
+
+		case SUCMD_SETOR:
+			retval = sisusb_setidxregor(sisusb, port,
+						y->data0, y->data1);
+			break;
+
+		case SUCMD_SETAND:
+			retval = sisusb_setidxregand(sisusb, port,
+						y->data0, y->data1);
+			break;
+
+		case SUCMD_SETANDOR:
+			retval = sisusb_setidxregandor(sisusb, port,
+						y->data0, y->data1, y->data2);
+			break;
+
+		case SUCMD_SETMASK:
+			retval = sisusb_setidxregmask(sisusb, port,
+						y->data0, y->data1, y->data2);
+			break;
+
+		case SUCMD_CLRSCR:
+			length = (y->data0 << 16) | (y->data1 << 8) | y->data2;
+			address = y->data3 -
+				SISUSB_PCI_PSEUDO_MEMBASE +
+				SISUSB_PCI_MEMBASE;
+			retval = sisusb_clear_vram(sisusb, address, length);
+			break;
+
+		default:
+			retval = -EINVAL;
+	}
+
+	if(retval > 0)
+		retval = -EIO;
+
+	return retval;
+}
+
+static int
+sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	     						unsigned long arg)
+{
+	struct sisusb_usb_data *sisusb;
+	struct sisusb_info x;
+	struct sisusb_command y;
+	int 	retval = 0;
+	u32 __user *argp = (u32 __user *)arg;
+
+	if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+		return -ENODEV;
+
+	down(&sisusb->lock);
+
+	/* Sanity check */
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	switch (cmd) {
+
+		case SISUSB_GET_CONFIG_SIZE:
+
+			if (put_user(sizeof(x), argp))
+				retval = -EFAULT;
+
+			break;
+
+		case SISUSB_GET_CONFIG:
+
+			x.sisusb_id   	    = SISUSB_ID;
+			x.sisusb_version    = SISUSB_VERSION;
+			x.sisusb_revision   = SISUSB_REVISION;
+			x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
+			x.sisusb_gfxinit    = sisusb->gfxinit;
+			x.sisusb_vrambase   = SISUSB_PCI_PSEUDO_MEMBASE;
+			x.sisusb_mmiobase   = SISUSB_PCI_PSEUDO_MMIOBASE;
+			x.sisusb_iobase     = SISUSB_PCI_PSEUDO_IOPORTBASE;
+			x.sisusb_pcibase    = SISUSB_PCI_PSEUDO_PCIBASE;
+			x.sisusb_vramsize   = sisusb->vramsize;
+			x.sisusb_minor	    = sisusb->minor;
+			x.sisusb_fbdevactive= 0;
+
+			if (copy_to_user((void __user *)arg, &x, sizeof(x)))
+				retval = -EFAULT;
+
+			break;
+
+		case SISUSB_COMMAND:
+
+			if (copy_from_user(&y, (void __user *)arg, sizeof(y)))
+				retval = -EFAULT;
+			else
+				retval = sisusb_handle_command(sisusb, &y, arg);
+
+			break;
+
+		default:
+			retval = -EINVAL;
+			break;
+	}
+
+err_out:
+	up(&sisusb->lock);
+	return retval;
+}
+
+#ifdef SISUSB_NEW_CONFIG_COMPAT
+static long
+sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+	long retval;
+
+	switch (cmd) {
+		case SISUSB_GET_CONFIG_SIZE:
+		case SISUSB_GET_CONFIG:
+		case SISUSB_COMMAND:
+			lock_kernel();
+			retval = sisusb_ioctl(f->f_dentry->d_inode, f, cmd, arg);
+			unlock_kernel();
+			return retval;
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+#endif
+
+static struct file_operations usb_sisusb_fops = {
+	.owner =	THIS_MODULE,
+	.open =		sisusb_open,
+	.release =	sisusb_release,
+	.read =		sisusb_read,
+	.write =	sisusb_write,
+	.llseek = 	sisusb_lseek,
+#ifdef SISUSB_NEW_CONFIG_COMPAT
+	.compat_ioctl = sisusb_compat_ioctl,
+#endif
+	.ioctl =	sisusb_ioctl
+};
+
+static struct usb_class_driver usb_sisusb_class = {
+	.name =		"usb/sisusbvga%d",
+	.fops =		&usb_sisusb_fops,
+	.mode =		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+	.minor_base =	SISUSB_MINOR
+};
+
+static int sisusb_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct sisusb_usb_data *sisusb;
+	int retval = 0, i;
+	const char *memfail =
+		KERN_ERR
+		"sisusbvga[%d]: Failed to allocate memory for %s buffer\n";
+
+	printk(KERN_INFO "sisusb: USB2VGA dongle found at address %d\n",
+		dev->devnum);
+
+	/* Allocate memory for our private */
+	if (!(sisusb = kmalloc(sizeof(*sisusb), GFP_KERNEL))) {
+		printk(KERN_ERR
+			"sisusb: Failed to allocate memory for private data\n");
+		return -ENOMEM;
+	}
+	memset(sisusb, 0, sizeof(*sisusb));
+	kref_init(&sisusb->kref);
+
+	init_MUTEX(&(sisusb->lock));
+
+	/* Register device */
+	if ((retval = usb_register_dev(intf, &usb_sisusb_class))) {
+		printk(KERN_ERR
+			"sisusb: Failed to get a minor for device %d\n",
+			dev->devnum);
+		retval = -ENODEV;
+		goto error_1;
+	}
+
+	sisusb->sisusb_dev = dev;
+	sisusb->minor      = intf->minor;
+	sisusb->vrambase   = SISUSB_PCI_MEMBASE;
+	sisusb->mmiobase   = SISUSB_PCI_MMIOBASE;
+	sisusb->mmiosize   = SISUSB_PCI_MMIOSIZE;
+	sisusb->ioportbase = SISUSB_PCI_IOPORTBASE;
+	/* Everything else is zero */
+
+	/* Allocate buffers */
+	sisusb->ibufsize = SISUSB_IBUF_SIZE;
+	if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE,
+					GFP_KERNEL, &sisusb->transfer_dma_in))) {
+		printk(memfail, "input", sisusb->minor);
+		retval = -ENOMEM;
+		goto error_2;
+	}
+
+	sisusb->numobufs = 0;
+	sisusb->obufsize = SISUSB_OBUF_SIZE;
+	for (i = 0; i < NUMOBUFS; i++) {
+		if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE,
+					GFP_KERNEL,
+					&sisusb->transfer_dma_out[i]))) {
+			if (i == 0) {
+				printk(memfail, "output", sisusb->minor);
+				retval = -ENOMEM;
+				goto error_3;
+			}
+			break;
+		} else
+			sisusb->numobufs++;
+
+	}
+
+	/* Allocate URBs */
+	if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) {
+		printk(KERN_ERR
+			"sisusbvga[%d]: Failed to allocate URBs\n",
+			sisusb->minor);
+		retval = -ENOMEM;
+		goto error_3;
+	}
+	sisusb->completein = 1;
+
+	for (i = 0; i < sisusb->numobufs; i++) {
+		if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) {
+			printk(KERN_ERR
+				"sisusbvga[%d]: Failed to allocate URBs\n",
+				sisusb->minor);
+			retval = -ENOMEM;
+			goto error_4;
+		}
+		sisusb->urbout_context[i].sisusb = (void *)sisusb;
+		sisusb->urbout_context[i].urbindex = i;
+		sisusb->urbstatus[i] = 0;
+	}
+
+	printk(KERN_INFO "sisusbvga[%d]: Allocated %d output buffers\n",
+					sisusb->minor, sisusb->numobufs);
+
+	/* Do remaining init stuff */
+
+	init_waitqueue_head(&sisusb->wait_q);
+
+	usb_set_intfdata(intf, sisusb);
+
+#ifdef SISUSB_OLD_CONFIG_COMPAT
+	{
+	int ret;
+	/* Our ioctls are all "32/64bit compatible" */
+	ret =  register_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE, NULL);
+	ret |= register_ioctl32_conversion(SISUSB_GET_CONFIG,      NULL);
+	ret |= register_ioctl32_conversion(SISUSB_COMMAND,         NULL);
+	if (ret)
+		printk(KERN_ERR
+			"sisusbvga[%d]: Error registering ioctl32 "
+			"translations\n",
+			sisusb->minor);
+	else
+		sisusb->ioctl32registered = 1;
+
+	}
+#endif
+
+	sisusb->present = 1;
+
+	if (dev->speed == USB_SPEED_HIGH) {
+		if (sisusb_init_gfxdevice(sisusb, 1))
+			printk(KERN_ERR
+				"sisusbvga[%d]: Failed to early "
+				"initialize device\n",
+				sisusb->minor);
+
+	} else
+		printk(KERN_INFO
+			"sisusbvga[%d]: Not attached to USB 2.0 hub, "
+			"deferring init\n",
+			sisusb->minor);
+
+	sisusb->ready = 1;
+
+	return 0;
+
+error_4:
+	sisusb_free_urbs(sisusb);
+error_3:
+	sisusb_free_buffers(sisusb);
+error_2:
+	usb_deregister_dev(intf, &usb_sisusb_class);
+error_1:
+	kfree(sisusb);
+	return retval;
+}
+
+static void sisusb_disconnect(struct usb_interface *intf)
+{
+	struct sisusb_usb_data *sisusb;
+	int minor;
+
+	down(&disconnect_sem);
+
+	/* This should *not* happen */
+	if (!(sisusb = usb_get_intfdata(intf))) {
+		up(&disconnect_sem);
+		return;
+	}
+
+	down(&sisusb->lock);
+
+	/* Wait for all URBs to complete and kill them in case (MUST do) */
+	if (!sisusb_wait_all_out_complete(sisusb))
+		sisusb_kill_all_busy(sisusb);
+
+	minor = sisusb->minor;
+
+	usb_set_intfdata(intf, NULL);
+
+	usb_deregister_dev(intf, &usb_sisusb_class);
+
+#ifdef SISUSB_OLD_CONFIG_COMPAT
+	if (sisusb->ioctl32registered) {
+		int ret;
+		sisusb->ioctl32registered = 0;
+		ret =  unregister_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE);
+		ret |= unregister_ioctl32_conversion(SISUSB_GET_CONFIG);
+		ret |= unregister_ioctl32_conversion(SISUSB_COMMAND);
+		if (ret) {
+			printk(KERN_ERR
+				"sisusbvga[%d]: Error unregistering "
+				"ioctl32 translations\n",
+				minor);
+		}
+	}
+#endif
+
+	sisusb->present = 0;
+	sisusb->ready = 0;
+
+	up(&sisusb->lock);
+
+	/* decrement our usage count */
+	kref_put(&sisusb->kref, sisusb_delete);
+
+	up(&disconnect_sem);
+
+	printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
+}
+
+static struct usb_device_id sisusb_table [] = {
+	{ USB_DEVICE(0x0711, 0x0900) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE (usb, sisusb_table);
+
+static struct usb_driver sisusb_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"sisusb",
+	.probe =	sisusb_probe,
+	.disconnect =	sisusb_disconnect,
+	.id_table =	sisusb_table
+};
+
+static int __init usb_sisusb_init(void)
+{
+	int retval;
+
+	if (!(retval = usb_register(&sisusb_driver))) {
+		printk(KERN_INFO "sisusb: Driver version %d.%d.%d\n",
+			SISUSB_VERSION, SISUSB_REVISION, SISUSB_PATCHLEVEL);
+		printk(KERN_INFO
+			"sisusb: Copyright (C) 2005 Thomas Winischhofer\n");
+	}
+
+	return retval;
+}
+
+static void __exit usb_sisusb_exit(void)
+{
+	usb_deregister(&sisusb_driver);
+}
+
+module_init(usb_sisusb_init);
+module_exit(usb_sisusb_exit);
+
+MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
+MODULE_DESCRIPTION("sisusb - Driver for Net2280/SiS315-based USB2VGA dongles");
+MODULE_LICENSE("GPL");
+
diff -puN /dev/null drivers/usb/misc/sisusbvga/sisusb.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/misc/sisusbvga/sisusb.h	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,278 @@
+/*
+ * sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles
+ *
+ * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, this code is licensed under the
+ * terms of the GPL v2.
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * Redistribution and use in source and binary forms, with or without
+ * * modification, are permitted provided that the following conditions
+ * * are met:
+ * * 1) Redistributions of source code must retain the above copyright
+ * *    notice, this list of conditions and the following disclaimer.
+ * * 2) Redistributions in binary form must reproduce the above copyright
+ * *    notice, this list of conditions and the following disclaimer in the
+ * *    documentation and/or other materials provided with the distribution.
+ * * 3) The name of the author may not be used to endorse or promote products
+ * *    derived from this software without specific prior written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
+ * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: 	Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#ifndef _SISUSB_H_
+#define _SISUSB_H_
+
+#ifdef CONFIG_COMPAT
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,10)
+#include <linux/ioctl32.h>
+#define SISUSB_OLD_CONFIG_COMPAT
+#else
+#define SISUSB_NEW_CONFIG_COMPAT
+#endif
+#endif
+
+/* Version Information */
+
+#define SISUSB_VERSION		0
+#define SISUSB_REVISION 	0
+#define SISUSB_PATCHLEVEL	7
+
+/* USB related */
+
+#define SISUSB_MINOR	133		/* FIXME */
+
+/* Size of the sisusb input/output buffers */
+#define SISUSB_IBUF_SIZE  0x01000
+#define SISUSB_OBUF_SIZE  0x10000	/* fixed */
+
+#define NUMOBUFS 8			/* max number of output buffers/output URBs */
+
+/* About endianness:
+ *
+ * 1) I/O ports, PCI config registers. The read/write()
+ *    calls emulate inX/outX. Hence, the data is
+ *    expected/delivered in machine endiannes by this
+ *    driver.
+ * 2) Video memory. The data is copied 1:1. There is
+ *    no swapping. Ever. This means for userland that
+ *    the data has to be prepared properly. (Hint:
+ *    think graphics data format, command queue,
+ *    hardware cursor.)
+ * 3) MMIO. Data is copied 1:1. MMIO must be swapped
+ *    properly by userland.
+ *
+ */
+
+#ifdef __BIG_ENDIAN
+#define SISUSB_CORRECT_ENDIANNESS_PACKET(p) 		\
+	do {						\
+		p->header  = cpu_to_le16(p->header);	\
+		p->address = cpu_to_le32(p->address);	\
+		p->data    = cpu_to_le32(p->data);	\
+	} while(0)
+#else
+#define SISUSB_CORRECT_ENDIANNESS_PACKET(p)
+#endif
+
+struct sisusb_usb_data;
+
+struct sisusb_urb_context {		/* urb->context for outbound bulk URBs */
+	struct sisusb_usb_data *sisusb;
+	int urbindex;
+	int *actual_length;
+};
+
+struct sisusb_usb_data {
+	struct usb_device *sisusb_dev;
+	struct usb_interface *interface;
+	struct kref kref;
+	wait_queue_head_t wait_q;	/* for syncind and timeouts */
+	struct semaphore lock;		/* general race avoidance */
+	unsigned int ifnum;		/* interface number of the USB device */
+	int minor;			/* minor (for logging clarity) */
+	int isopen;			/* !=0 if open */
+	int present;			/* !=0 if device is present on the bus */
+	int ready;			/* !=0 if device is ready for userland */
+#ifdef SISUSB_OLD_CONFIG_COMPAT
+	int ioctl32registered;
+#endif
+	int numobufs;			/* number of obufs = number of out urbs */
+	char *obuf[NUMOBUFS], *ibuf;	/* transfer buffers */
+	int obufsize, ibufsize;
+	dma_addr_t transfer_dma_out[NUMOBUFS];
+	dma_addr_t transfer_dma_in;
+	struct urb *sisurbout[NUMOBUFS];
+	struct urb *sisurbin;
+	unsigned char urbstatus[NUMOBUFS];
+	unsigned char completein;
+	struct sisusb_urb_context urbout_context[NUMOBUFS];
+	unsigned long flagb0;
+	unsigned long vrambase;		/* framebuffer base */
+	unsigned int vramsize;		/* framebuffer size (bytes) */
+	unsigned long mmiobase;
+	unsigned int mmiosize;
+	unsigned long ioportbase;
+	unsigned char devinit;		/* device initialized? */
+	unsigned char gfxinit;		/* graphics core initialized? */
+	unsigned short chipid, chipvendor;
+	unsigned short chiprevision;
+};
+
+#define to_sisusb_dev(d) container_of(d, struct sisusb_usb_data, kref)
+
+/* USB transport related */
+
+/* urbstatus */
+#define SU_URB_BUSY   1
+#define SU_URB_ALLOC  2
+
+/* Endpoints */
+
+#define SISUSB_EP_GFX_IN	0x0e	/* gfx std packet out(0e)/in(8e) */
+#define SISUSB_EP_GFX_OUT	0x0e
+
+#define SISUSB_EP_GFX_BULK_OUT	0x01	/* gfx mem bulk out/in */
+#define SISUSB_EP_GFX_BULK_IN	0x02	/* ? 2 is "OUT" ? */
+
+#define SISUSB_EP_GFX_LBULK_OUT	0x03	/* gfx large mem bulk out */
+
+#define SISUSB_EP_UNKNOWN_04	0x04	/* ? 4 is "OUT" ? - unused */
+
+#define SISUSB_EP_BRIDGE_IN	0x0d	/* Net2280 out(0d)/in(8d) */
+#define SISUSB_EP_BRIDGE_OUT	0x0d
+
+#define SISUSB_TYPE_MEM		0
+#define SISUSB_TYPE_IO		1
+
+struct sisusb_packet {
+	unsigned short header;
+	u32 address;
+	u32 data;
+} __attribute__((__packed__));
+
+#define CLEARPACKET(packet) memset(packet, 0, 10)
+
+/* PCI bridge related */
+
+#define SISUSB_PCI_MEMBASE	0xd0000000
+#define SISUSB_PCI_MMIOBASE	0xe4000000
+#define SISUSB_PCI_IOPORTBASE	0x0000d000
+
+#define SISUSB_PCI_PSEUDO_MEMBASE	0x10000000
+#define SISUSB_PCI_PSEUDO_MMIOBASE	0x20000000
+#define SISUSB_PCI_PSEUDO_IOPORTBASE	0x0000d000
+#define SISUSB_PCI_PSEUDO_PCIBASE	0x00010000
+
+#define SISUSB_PCI_MMIOSIZE	(128*1024)
+#define SISUSB_PCI_PCONFSIZE	0x5c
+
+/* graphics core related */
+
+#define AROFFSET	0x40
+#define ARROFFSET	0x41
+#define GROFFSET	0x4e
+#define SROFFSET	0x44
+#define CROFFSET	0x54
+#define MISCROFFSET	0x4c
+#define MISCWOFFSET	0x42
+#define INPUTSTATOFFSET 0x5A
+#define PART1OFFSET	0x04
+#define PART2OFFSET	0x10
+#define PART3OFFSET	0x12
+#define PART4OFFSET	0x14
+#define PART5OFFSET	0x16
+#define CAPTUREOFFSET	0x00
+#define VIDEOOFFSET	0x02
+#define COLREGOFFSET	0x48
+#define PELMASKOFFSET	0x46
+#define VGAENABLE	0x43
+
+#define SISAR		SISUSB_PCI_IOPORTBASE + AROFFSET
+#define SISARR		SISUSB_PCI_IOPORTBASE + ARROFFSET
+#define SISGR		SISUSB_PCI_IOPORTBASE + GROFFSET
+#define SISSR		SISUSB_PCI_IOPORTBASE + SROFFSET
+#define SISCR		SISUSB_PCI_IOPORTBASE + CROFFSET
+#define SISMISCR	SISUSB_PCI_IOPORTBASE + MISCROFFSET
+#define SISMISCW	SISUSB_PCI_IOPORTBASE + MISCWOFFSET
+#define SISINPSTAT	SISUSB_PCI_IOPORTBASE + INPUTSTATOFFSET
+#define SISPART1	SISUSB_PCI_IOPORTBASE + PART1OFFSET
+#define SISPART2	SISUSB_PCI_IOPORTBASE + PART2OFFSET
+#define SISPART3	SISUSB_PCI_IOPORTBASE + PART3OFFSET
+#define SISPART4	SISUSB_PCI_IOPORTBASE + PART4OFFSET
+#define SISPART5	SISUSB_PCI_IOPORTBASE + PART5OFFSET
+#define SISCAP		SISUSB_PCI_IOPORTBASE + CAPTUREOFFSET
+#define SISVID		SISUSB_PCI_IOPORTBASE + VIDEOOFFSET
+#define SISCOLIDXR	SISUSB_PCI_IOPORTBASE + COLREGOFFSET - 1
+#define SISCOLIDX	SISUSB_PCI_IOPORTBASE + COLREGOFFSET
+#define SISCOLDATA	SISUSB_PCI_IOPORTBASE + COLREGOFFSET + 1
+#define SISCOL2IDX	SISPART5
+#define SISCOL2DATA	SISPART5 + 1
+#define SISPEL		SISUSB_PCI_IOPORTBASE + PELMASKOFFSET
+#define SISVGAEN	SISUSB_PCI_IOPORTBASE + VGAENABLE
+#define SISDACA		SISCOLIDX
+#define SISDACD		SISCOLDATA
+
+/* ioctl related */
+
+/* Structure argument for SISUSB_GET_INFO ioctl  */
+struct sisusb_info {
+	__u32	sisusb_id;		/* for identifying sisusb */
+#define SISUSB_ID  0x53495355		/* Identify myself with 'SISU' */
+	__u8	sisusb_version;
+	__u8	sisusb_revision;
+	__u8	sisusb_patchlevel;
+	__u8	sisusb_gfxinit;		/* graphics core initialized? */
+
+	__u32	sisusb_vrambase;
+	__u32	sisusb_mmiobase;
+	__u32	sisusb_iobase;
+	__u32	sisusb_pcibase;
+
+	__u32	sisusb_vramsize;	/* framebuffer size in bytes */
+
+	__u32	sisusb_minor;
+
+	__u32   sisusb_fbdevactive;	/* != 0 if framebuffer device active */
+
+	__u8	sisusb_reserved[32];	/* for future use */
+};
+
+struct sisusb_command {
+	__u8   operation;	/* see below */
+	__u8   data0;		/* operation dependent */
+	__u8   data1;		/* operation dependent */
+	__u8   data2;		/* operation dependent */
+	__u32  data3;		/* operation dependent */
+	__u32  data4;		/* for future use */
+};
+
+#define SUCMD_GET      0x01	/* for all: data0 = index, data3 = port */
+#define SUCMD_SET      0x02	/* data1 = value */
+#define SUCMD_SETOR    0x03	/* data1 = or */
+#define SUCMD_SETAND   0x04	/* data1 = and */
+#define SUCMD_SETANDOR 0x05	/* data1 = and, data2 = or */
+#define SUCMD_SETMASK  0x06	/* data1 = data, data2 = mask */
+
+#define SUCMD_CLRSCR   0x07	/* data0:1:2 = length, data3 = address */
+
+#define SISUSB_COMMAND		_IOWR(0xF3,0x3D,struct sisusb_command)
+#define SISUSB_GET_CONFIG_SIZE 	_IOR(0xF3,0x3E,__u32)
+#define SISUSB_GET_CONFIG  	_IOR(0xF3,0x3F,struct sisusb_info)
+
+#endif /* SISUSB_H */
+
diff -puN /dev/null drivers/usb/mon/Kconfig
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/mon/Kconfig	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,22 @@
+#
+# USB Monitor configuration
+#
+
+# In normal life, it makes little sense to have usbmon as a module, and in fact
+# it is harmful, because there is no way to autoload the module.
+# The 'm' option is allowed for hackers who debug the usbmon itself,
+# and for those who have usbcore as a module.
+config USB_MON
+	tristate "USB Monitor"
+	depends on USB
+	default y
+	help
+	  If you say Y here, a component which captures the USB traffic
+	  between peripheral-specific drivers and HC drivers will be built.
+	  The USB_MON is similar in spirit and may be compatible with Dave
+	  Harding's USBMon.
+
+	  This is somewhat experimental at this time, but it should be safe,
+	  as long as you aren't building this as a module and then removing it.
+
+	  If unsure, say Y. Do not say M.
diff -puN /dev/null drivers/usb/mon/Makefile
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/mon/Makefile	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,7 @@
+#
+# Makefile for USB Core files and filesystem
+#
+
+usbmon-objs	:= mon_main.o mon_stat.o mon_text.o
+
+obj-$(CONFIG_USB_MON)	+= usbmon.o
diff -puN /dev/null drivers/usb/mon/mon_main.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/mon/mon_main.c	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,377 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * mon_main.c: Main file, module initiation and exit, registrations, etc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <linux/smp_lock.h>
+
+#include "usb_mon.h"
+#include "../core/hcd.h"
+
+static void mon_submit(struct usb_bus *ubus, struct urb *urb);
+static void mon_complete(struct usb_bus *ubus, struct urb *urb);
+static void mon_stop(struct mon_bus *mbus);
+static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
+static void mon_bus_drop(struct kref *r);
+static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus);
+
+DECLARE_MUTEX(mon_lock);
+
+static struct dentry *mon_dir;		/* /dbg/usbmon */
+static LIST_HEAD(mon_buses);		/* All buses we know: struct mon_bus */
+
+/*
+ * Link a reader into the bus.
+ *
+ * This must be called with mon_lock taken because of mbus->ref.
+ */
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r)
+{
+	unsigned long flags;
+	struct usb_bus *ubus;
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	if (mbus->nreaders == 0) {
+		ubus = mbus->u_bus;
+		if (ubus->monitored) {
+			/*
+			 * Something is really broken, refuse to go on and
+			 * possibly corrupt ops pointers or worse.
+			 */
+			printk(KERN_ERR TAG ": bus %d is already monitored\n",
+			    ubus->busnum);
+			spin_unlock_irqrestore(&mbus->lock, flags);
+			return;
+		}
+		ubus->monitored = 1;
+	}
+	mbus->nreaders++;
+	list_add_tail(&r->r_link, &mbus->r_list);
+	spin_unlock_irqrestore(&mbus->lock, flags);
+
+	kref_get(&mbus->ref);
+}
+
+/*
+ * Unlink reader from the bus.
+ *
+ * This is called with mon_lock taken, so we can decrement mbus->ref.
+ */
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	list_del(&r->r_link);
+	--mbus->nreaders;
+	if (mbus->nreaders == 0)
+		mon_stop(mbus);
+	spin_unlock_irqrestore(&mbus->lock, flags);
+
+	kref_put(&mbus->ref, mon_bus_drop);
+}
+
+/*
+ */
+static void mon_submit(struct usb_bus *ubus, struct urb *urb)
+{
+	struct mon_bus *mbus;
+	unsigned long flags;
+	struct list_head *pos;
+	struct mon_reader *r;
+
+	mbus = ubus->mon_bus;
+	if (mbus == NULL)
+		goto out_unlocked;
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	if (mbus->nreaders == 0)
+		goto out_locked;
+
+	list_for_each (pos, &mbus->r_list) {
+		r = list_entry(pos, struct mon_reader, r_link);
+		r->rnf_submit(r->r_data, urb);
+	}
+
+	spin_unlock_irqrestore(&mbus->lock, flags);
+	return;
+
+out_locked:
+	spin_unlock_irqrestore(&mbus->lock, flags);
+out_unlocked:
+	return;
+}
+
+/*
+ */
+static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int err)
+{
+	struct mon_bus *mbus;
+
+	mbus = ubus->mon_bus;
+	if (mbus == NULL)
+		goto out_unlocked;
+
+	/*
+	 * XXX Capture the error code and the 'E' event.
+	 */
+
+	return;
+
+out_unlocked:
+	return;
+}
+
+/*
+ */
+static void mon_complete(struct usb_bus *ubus, struct urb *urb)
+{
+	struct mon_bus *mbus;
+	unsigned long flags;
+	struct list_head *pos;
+	struct mon_reader *r;
+
+	mbus = ubus->mon_bus;
+	if (mbus == NULL) {
+		/*
+		 * This should not happen.
+		 * At this point we do not even know the bus number...
+		 */
+		printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n",
+		    urb->pipe);
+		return;
+	}
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	list_for_each (pos, &mbus->r_list) {
+		r = list_entry(pos, struct mon_reader, r_link);
+		r->rnf_complete(r->r_data, urb);
+	}
+	spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+/* int (*unlink_urb) (struct urb *urb, int status); */
+
+/*
+ * Stop monitoring.
+ * Obviously this must be well locked, so no need to play with mb's.
+ */
+static void mon_stop(struct mon_bus *mbus)
+{
+	struct usb_bus *ubus = mbus->u_bus;
+
+	/*
+	 * A stop can be called for a dissolved mon_bus in case of
+	 * a reader staying across an rmmod foo_hcd.
+	 */
+	if (ubus != NULL) {
+		ubus->monitored = 0;
+		mb();
+	}
+}
+
+/*
+ * Add a USB bus (usually by a modprobe foo-hcd)
+ *
+ * This does not return an error code because the core cannot care less
+ * if monitoring is not established.
+ */
+static void mon_bus_add(struct usb_bus *ubus)
+{
+	mon_bus_init(mon_dir, ubus);
+}
+
+/*
+ * Remove a USB bus (either from rmmod foo-hcd or from a hot-remove event).
+ */
+static void mon_bus_remove(struct usb_bus *ubus)
+{
+	struct mon_bus *mbus = ubus->mon_bus;
+
+	down(&mon_lock);
+	list_del(&mbus->bus_link);
+	debugfs_remove(mbus->dent_t);
+	debugfs_remove(mbus->dent_s);
+
+	mon_dissolve(mbus, ubus);
+	kref_put(&mbus->ref, mon_bus_drop);
+	up(&mon_lock);
+}
+
+/*
+ * Ops
+ */
+static struct usb_mon_operations mon_ops_0 = {
+	.urb_submit =	mon_submit,
+	.urb_submit_error = mon_submit_error,
+	.urb_complete =	mon_complete,
+	.bus_add =	mon_bus_add,
+	.bus_remove =	mon_bus_remove,
+};
+
+/*
+ * Tear usb_bus and mon_bus apart.
+ */
+static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
+{
+
+	/*
+	 * Never happens, but...
+	 */
+	if (ubus->monitored) {
+		printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n",
+		    ubus->busnum);
+		ubus->monitored = 0;
+		mb();
+	}
+
+	ubus->mon_bus = NULL;
+	mbus->u_bus = NULL;
+	mb();
+	// usb_bus_put(ubus);
+}
+
+/*
+ */
+static void mon_bus_drop(struct kref *r)
+{
+	struct mon_bus *mbus = container_of(r, struct mon_bus, ref);
+	kfree(mbus);
+}
+
+/*
+ * Initialize a bus for us:
+ *  - allocate mon_bus
+ *  - refcount USB bus struct
+ *  - link
+ */
+static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
+{
+	struct dentry *d;
+	struct mon_bus *mbus;
+	enum { NAMESZ = 10 };
+	char name[NAMESZ];
+	int rc;
+
+	if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
+		goto err_alloc;
+	memset(mbus, 0, sizeof(struct mon_bus));
+	kref_init(&mbus->ref);
+	spin_lock_init(&mbus->lock);
+	INIT_LIST_HEAD(&mbus->r_list);
+
+	/*
+	 * This usb_bus_get here is superfluous, because we receive
+	 * a notification if usb_bus is about to be removed.
+	 */
+	// usb_bus_get(ubus);
+	mbus->u_bus = ubus;
+	ubus->mon_bus = mbus;
+
+	rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
+	if (rc <= 0 || rc >= NAMESZ)
+		goto err_print_t;
+	d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text);
+	if (d == NULL)
+		goto err_create_t;
+	mbus->dent_t = d;
+
+	rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
+	if (rc <= 0 || rc >= NAMESZ)
+		goto err_print_s;
+	d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat);
+	if (d == NULL)
+		goto err_create_s;
+	mbus->dent_s = d;
+
+	down(&mon_lock);
+	list_add_tail(&mbus->bus_link, &mon_buses);
+	up(&mon_lock);
+	return;
+
+err_create_s:
+err_print_s:
+	debugfs_remove(mbus->dent_t);
+err_create_t:
+err_print_t:
+	kfree(mbus);
+err_alloc:
+	return;
+}
+
+static int __init mon_init(void)
+{
+	struct usb_bus *ubus;
+	struct dentry *mondir;
+
+	mondir = debugfs_create_dir("usbmon", NULL);
+	if (IS_ERR(mondir)) {
+		printk(KERN_NOTICE TAG ": debugs is not available\n");
+		return -ENODEV;
+	}
+	if (mondir == NULL) {
+		printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
+		return -ENODEV;
+	}
+	mon_dir = mondir;
+
+	if (usb_mon_register(&mon_ops_0) != 0) {
+		printk(KERN_NOTICE TAG ": unable to register with the core\n");
+		debugfs_remove(mondir);
+		return -ENODEV;
+	}
+	// MOD_INC_USE_COUNT(which_module?);
+
+	down(&usb_bus_list_lock);
+	list_for_each_entry (ubus, &usb_bus_list, bus_list) {
+		mon_bus_init(mondir, ubus);
+	}
+	up(&usb_bus_list_lock);
+	return 0;
+}
+
+static void __exit mon_exit(void)
+{
+	struct mon_bus *mbus;
+	struct list_head *p;
+
+	usb_mon_deregister();
+
+	down(&mon_lock);
+	while (!list_empty(&mon_buses)) {
+		p = mon_buses.next;
+		mbus = list_entry(p, struct mon_bus, bus_link);
+		list_del(p);
+
+		debugfs_remove(mbus->dent_t);
+		debugfs_remove(mbus->dent_s);
+
+		/*
+		 * This never happens, because the open/close paths in
+		 * file level maintain module use counters and so rmmod fails
+		 * before reaching here. However, better be safe...
+		 */
+		if (mbus->nreaders) {
+			printk(KERN_ERR TAG
+			    ": Outstanding opens (%d) on usb%d, leaking...\n",
+			    mbus->nreaders, mbus->u_bus->busnum);
+			atomic_set(&mbus->ref.refcount, 2);	/* Force leak */
+		}
+
+		mon_dissolve(mbus, mbus->u_bus);
+		kref_put(&mbus->ref, mon_bus_drop);
+	}
+	up(&mon_lock);
+
+	debugfs_remove(mon_dir);
+}
+
+module_init(mon_init);
+module_exit(mon_exit);
+
+MODULE_LICENSE("GPL");
diff -puN /dev/null drivers/usb/mon/mon_stat.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/mon/mon_stat.c	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,74 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is the 's' or 'stat' reader which debugs usbmon itself.
+ * Note that this code blows through locks, so make sure that
+ * /dbg/usbmon/0s is well protected from non-root users.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+#define STAT_BUF_SIZE  80
+
+struct snap {
+	int slen;
+	char str[STAT_BUF_SIZE];
+};
+
+static int mon_stat_open(struct inode *inode, struct file *file)
+{
+	struct mon_bus *mbus;
+	struct snap *sp;
+
+	if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	mbus = inode->u.generic_ip;
+
+	sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
+	    "nreaders %d text_lost %u\n",
+	    mbus->nreaders, mbus->cnt_text_lost);
+
+	file->private_data = sp;
+	return 0;
+}
+
+static ssize_t mon_stat_read(struct file *file, char __user *buf,
+				size_t nbytes, loff_t *ppos)
+{
+	struct snap *sp = file->private_data;
+	loff_t pos = *ppos;
+	int cnt;
+
+	if (pos < 0 || pos >= sp->slen)
+		return 0;
+	if (nbytes == 0)
+		return 0;
+	if ((cnt = sp->slen - pos) > nbytes)
+		cnt = nbytes;
+	if (copy_to_user(buf, sp->str + pos, cnt))
+		return -EFAULT;
+	*ppos = pos + cnt;
+	return cnt;
+}
+
+static int mon_stat_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+struct file_operations mon_fops_stat = {
+	.owner =	THIS_MODULE,
+	.open =		mon_stat_open,
+	.llseek =	no_llseek,
+	.read =		mon_stat_read,
+	/* .write =	mon_stat_write, */
+	/* .poll =		mon_stat_poll, */
+	/* .ioctl =	mon_stat_ioctl, */
+	.release =	mon_stat_release,
+};
diff -puN /dev/null drivers/usb/mon/mon_text.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/mon/mon_text.c	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,395 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is a text format reader.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/time.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/*
+ * No, we do not want arbitrarily long data strings.
+ * Use the binary interface if you want to capture bulk data!
+ */
+#define DATA_MAX  32
+
+/*
+ * This limit exists to prevent OOMs when the user process stops reading.
+ */
+#define EVENT_MAX  25
+
+#define PRINTF_DFL  120
+
+struct mon_event_text {
+	struct list_head e_link;
+	int type;		/* submit, complete, etc. */
+	unsigned int pipe;	/* Pipe */
+	unsigned long id;	/* From pointer, most of the time */
+	unsigned int tstamp;
+	int length;		/* Depends on type: xfer length or act length */
+	int status;
+	char data_flag;
+	unsigned char data[DATA_MAX];
+};
+
+#define SLAB_NAME_SZ  30
+struct mon_reader_text {
+	kmem_cache_t *e_slab;
+	int nevents;
+	struct list_head e_list;
+	struct mon_reader r;	/* In C, parent class can be placed anywhere */
+
+	wait_queue_head_t wait;
+	int printf_size;
+	char *printf_buf;
+	struct semaphore printf_lock;
+
+	char slab_name[SLAB_NAME_SZ];
+};
+
+static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
+static void mon_text_dtor(void *, kmem_cache_t *, unsigned long);
+
+/*
+ * mon_text_submit
+ * mon_text_complete
+ *
+ * May be called from an interrupt.
+ *
+ * This is called with the whole mon_bus locked, so no additional lock.
+ */
+
+static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
+    int len, char ev_type)
+{
+	int pipe = urb->pipe;
+	unsigned char *data;
+
+	/*
+	 * The check to see if it's safe to poke at data has an enormous
+	 * number of corner cases, but it seems that the following is
+	 * more or less safe.
+	 *
+	 * We do not even try to look transfer_buffer, because it can
+	 * contain non-NULL garbage in case the upper level promised to
+	 * set DMA for the HCD.
+	 */
+	if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+		return 'D';
+
+	if (len <= 0)
+		return 'L';
+
+	if ((data = urb->transfer_buffer) == NULL)
+		return 'Z';	/* '0' would be not as pretty. */
+
+	/*
+	 * Bulk is easy to shortcut reliably. 
+	 * XXX Control needs setup packet taken.
+	 * XXX Other pipe types need consideration. Currently, we overdo it
+	 * and collect garbage for them: better more than less.
+	 */
+	if (usb_pipebulk(pipe) || usb_pipecontrol(pipe)) {
+		if (usb_pipein(pipe)) {
+			if (ev_type == 'S')
+				return '<';
+		} else {
+			if (ev_type == 'C')
+				return '>';
+		}
+	}
+
+	if (len >= DATA_MAX)
+		len = DATA_MAX;
+	memcpy(ep->data, urb->transfer_buffer, len);
+	return 0;
+}
+
+static inline unsigned int mon_get_timestamp(void)
+{
+	struct timeval tval;
+	unsigned int stamp;
+
+	do_gettimeofday(&tval);
+	stamp = tval.tv_sec & 0xFFFF;	/* 2^32 = 4294967296. Limit to 4096s. */
+	stamp = stamp * 1000000 + tval.tv_usec;
+	return stamp;
+}
+
+static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
+    char ev_type)
+{
+	struct mon_event_text *ep;
+	unsigned int stamp;
+
+	stamp = mon_get_timestamp();
+
+	if (rp->nevents >= EVENT_MAX ||
+	    (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) {
+		rp->r.m_bus->cnt_text_lost++;
+		return;
+	}
+
+	ep->type = ev_type;
+	ep->pipe = urb->pipe;
+	ep->id = (unsigned long) urb;
+	ep->tstamp = stamp;
+	ep->length = (ev_type == 'S') ?
+	    urb->transfer_buffer_length : urb->actual_length;
+	/* Collecting status makes debugging sense for submits, too */
+	ep->status = urb->status;
+
+	ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type);
+
+	rp->nevents++;
+	list_add_tail(&ep->e_link, &rp->e_list);
+	wake_up(&rp->wait);
+}
+
+static void mon_text_submit(void *data, struct urb *urb)
+{
+	struct mon_reader_text *rp = data;
+	mon_text_event(rp, urb, 'S');
+}
+
+static void mon_text_complete(void *data, struct urb *urb)
+{
+	struct mon_reader_text *rp = data;
+	mon_text_event(rp, urb, 'C');
+}
+
+/*
+ * Fetch next event from the circular buffer.
+ */
+static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
+    struct mon_bus *mbus)
+{
+	struct list_head *p;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mbus->lock, flags);
+	if (list_empty(&rp->e_list)) {
+		spin_unlock_irqrestore(&mbus->lock, flags);
+		return NULL;
+	}
+	p = rp->e_list.next;
+	list_del(p);
+	--rp->nevents;
+	spin_unlock_irqrestore(&mbus->lock, flags);
+	return list_entry(p, struct mon_event_text, e_link);
+}
+
+/*
+ */
+static int mon_text_open(struct inode *inode, struct file *file)
+{
+	struct mon_bus *mbus;
+	struct usb_bus *ubus;
+	struct mon_reader_text *rp;
+	int rc;
+
+	down(&mon_lock);
+	mbus = inode->u.generic_ip;
+	ubus = mbus->u_bus;
+
+	rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
+	if (rp == NULL) {
+		rc = -ENOMEM;
+		goto err_alloc;
+	}
+	memset(rp, 0, sizeof(struct mon_reader_text));
+	INIT_LIST_HEAD(&rp->e_list);
+	init_waitqueue_head(&rp->wait);
+	init_MUTEX(&rp->printf_lock);
+
+	rp->printf_size = PRINTF_DFL;
+	rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL);
+	if (rp->printf_buf == NULL) {
+		rc = -ENOMEM;
+		goto err_alloc_pr;
+	}
+
+	rp->r.m_bus = mbus;
+	rp->r.r_data = rp;
+	rp->r.rnf_submit = mon_text_submit;
+	rp->r.rnf_complete = mon_text_complete;
+
+	snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
+	    (long)rp);
+	rp->e_slab = kmem_cache_create(rp->slab_name,
+	    sizeof(struct mon_event_text), sizeof(long), 0,
+	    mon_text_ctor, mon_text_dtor);
+	if (rp->e_slab == NULL) {
+		rc = -ENOMEM;
+		goto err_slab;
+	}
+
+	mon_reader_add(mbus, &rp->r);
+
+	file->private_data = rp;
+	up(&mon_lock);
+	return 0;
+
+// err_busy:
+//	kmem_cache_destroy(rp->e_slab);
+err_slab:
+	kfree(rp->printf_buf);
+err_alloc_pr:
+	kfree(rp);
+err_alloc:
+	up(&mon_lock);
+	return rc;
+}
+
+/*
+ * For simplicity, we read one record in one system call and throw out
+ * what does not fit. This means that the following does not work:
+ *   dd if=/dbg/usbmon/0t bs=10
+ * Also, we do not allow seeks and do not bother advancing the offset.
+ */
+static ssize_t mon_text_read(struct file *file, char __user *buf,
+				size_t nbytes, loff_t *ppos)
+{
+	struct mon_reader_text *rp = file->private_data;
+	struct mon_bus *mbus = rp->r.m_bus;
+	DECLARE_WAITQUEUE(waita, current);
+	struct mon_event_text *ep;
+	int cnt, limit;
+	char *pbuf;
+	int data_len, i;
+
+	add_wait_queue(&rp->wait, &waita);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while ((ep = mon_text_fetch(rp, mbus)) == NULL) {
+		if (file->f_flags & O_NONBLOCK) {
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&rp->wait, &waita);
+			return -EWOULDBLOCK;	/* Same as EAGAIN in Linux */
+		}
+		/*
+		 * We do not count nwaiters, because ->release is supposed
+		 * to be called when all openers are gone only.
+		 */
+		schedule();
+		if (signal_pending(current)) {
+			remove_wait_queue(&rp->wait, &waita);
+			return -EINTR;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rp->wait, &waita);
+
+	down(&rp->printf_lock);
+	cnt = 0;
+	pbuf = rp->printf_buf;
+	limit = rp->printf_size;
+
+	cnt += snprintf(pbuf + cnt, limit - cnt,
+	    "%lx %u %c %08x %d %d",
+	    ep->id, ep->tstamp, ep->type, ep->pipe, ep->status, ep->length);
+
+	if ((data_len = ep->length) > 0) {
+		if (ep->data_flag == 0) {
+			cnt += snprintf(pbuf + cnt, limit - cnt, " =");
+			if (data_len >= DATA_MAX)
+				data_len = DATA_MAX;
+			for (i = 0; i < data_len; i++) {
+				if (i % 4 == 0) {
+					cnt += snprintf(pbuf + cnt, limit - cnt,
+					    " ");
+				}
+				cnt += snprintf(pbuf + cnt, limit - cnt,
+				    "%02x", ep->data[i]);
+			}
+			cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+		} else {
+			cnt += snprintf(pbuf + cnt, limit - cnt,
+			    " %c\n", ep->data_flag);
+		}
+	} else {
+		cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+	}
+
+	if (copy_to_user(buf, rp->printf_buf, cnt))
+		cnt = -EFAULT;
+	up(&rp->printf_lock);
+	kmem_cache_free(rp->e_slab, ep);
+	return cnt;
+}
+
+static int mon_text_release(struct inode *inode, struct file *file)
+{
+	struct mon_reader_text *rp = file->private_data;
+	struct mon_bus *mbus;
+	/* unsigned long flags; */
+	struct list_head *p;
+	struct mon_event_text *ep;
+
+	down(&mon_lock);
+	mbus = inode->u.generic_ip;
+
+	if (mbus->nreaders <= 0) {
+		printk(KERN_ERR TAG ": consistency error on close\n");
+		up(&mon_lock);
+		return 0;
+	}
+	mon_reader_del(mbus, &rp->r);
+
+	/*
+	 * In theory, e_list is protected by mbus->lock. However,
+	 * after mon_reader_del has finished, the following is the case:
+	 *  - we are not on reader list anymore, so new events won't be added;
+	 *  - whole mbus may be dropped if it was orphaned.
+	 * So, we better not touch mbus.
+	 */
+	/* spin_lock_irqsave(&mbus->lock, flags); */
+	while (!list_empty(&rp->e_list)) {
+		p = rp->e_list.next;
+		ep = list_entry(p, struct mon_event_text, e_link);
+		list_del(p);
+		--rp->nevents;
+		kmem_cache_free(rp->e_slab, ep);
+	}
+	/* spin_unlock_irqrestore(&mbus->lock, flags); */
+
+	kmem_cache_destroy(rp->e_slab);
+	kfree(rp->printf_buf);
+	kfree(rp);
+
+	up(&mon_lock);
+	return 0;
+}
+
+struct file_operations mon_fops_text = {
+	.owner =	THIS_MODULE,
+	.open =		mon_text_open,
+	.llseek =	no_llseek,
+	.read =		mon_text_read,
+	/* .write =	mon_text_write, */
+	/* .poll =		mon_text_poll, */
+	/* .ioctl =	mon_text_ioctl, */
+	.release =	mon_text_release,
+};
+
+/*
+ * Slab interface: constructor.
+ */
+static void mon_text_ctor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+	/*
+	 * Nothing to initialize. No, really!
+	 * So, we fill it with garbage to emulate a reused object.
+	 */
+	memset(mem, 0xe5, sizeof(struct mon_event_text));
+}
+
+static void mon_text_dtor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+	;
+}
diff -puN /dev/null drivers/usb/mon/usb_mon.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/usb/mon/usb_mon.h	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,51 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ */
+
+#ifndef __USB_MON_H
+#define __USB_MON_H
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/kref.h>
+/* #include <linux/usb.h> */	/* We use struct pointers only in this header */
+
+#define TAG "usbmon"
+
+struct mon_bus {
+	struct list_head bus_link;
+	spinlock_t lock;
+	struct dentry *dent_s;		/* Debugging file */
+	struct dentry *dent_t;		/* Text interface file */
+	struct usb_bus *u_bus;
+
+	/* Ref */
+	int nreaders;			/* Under mon_lock AND mbus->lock */
+	struct list_head r_list;	/* Chain of readers (usually one) */
+	struct kref ref;		/* Under mon_lock */
+
+	/* Stats */
+	unsigned int cnt_text_lost;
+};
+
+/*
+ * An instance of a process which opened a file (but can fork later)
+ */
+struct mon_reader {
+	struct list_head r_link;
+	struct mon_bus *m_bus;
+	void *r_data;		/* Use container_of instead? */
+
+	void (*rnf_submit)(void *data, struct urb *urb);
+	void (*rnf_complete)(void *data, struct urb *urb);
+};
+
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
+
+extern struct semaphore mon_lock;
+
+extern struct file_operations mon_fops_text;
+extern struct file_operations mon_fops_stat;
+
+#endif /* __USB_MON_H */
diff -puN drivers/usb/net/kaweth.c~bk-usb drivers/usb/net/kaweth.c
--- 25/drivers/usb/net/kaweth.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/net/kaweth.c	2005-02-09 19:12:30.000000000 -0800
@@ -58,6 +58,7 @@
 #include <linux/ethtool.h>
 #include <linux/pci.h>
 #include <linux/dma-mapping.h>
+#include <linux/wait.h>
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
 #include <asm/byteorder.h>
@@ -1180,31 +1181,21 @@ static void usb_api_blocking_completion(
 // Starts urb and waits for completion or timeout
 static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
 {
-        DECLARE_WAITQUEUE(wait, current);
 	struct usb_api_data awd;
         int status;
 
         init_waitqueue_head(&awd.wqh);
         awd.done = 0;
 
-        add_wait_queue(&awd.wqh, &wait);
         urb->context = &awd;
         status = usb_submit_urb(urb, GFP_NOIO);
         if (status) {
                 // something went wrong
                 usb_free_urb(urb);
-                remove_wait_queue(&awd.wqh, &wait);
                 return status;
         }
 
-	while (timeout && !awd.done) {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		timeout = schedule_timeout(timeout);
-	}
-
-        remove_wait_queue(&awd.wqh, &wait);
-
-        if (!timeout) {
+	if (!wait_event_timeout(awd.wqh, awd.done, timeout)) {
                 // timeout
                 kaweth_warn("usb_control/bulk_msg: timeout");
                 usb_kill_urb(urb);  // remove urb safely
diff -puN drivers/usb/net/Kconfig~bk-usb drivers/usb/net/Kconfig
--- 25/drivers/usb/net/Kconfig~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/net/Kconfig	2005-02-09 19:12:30.000000000 -0800
@@ -260,13 +260,13 @@ comment "USB Network Adapters"
 	depends on USB_USBNET
 
 config USB_AX8817X
-	boolean "ASIX AX88172 Based USB 2.0 Ethernet Devices"
+	boolean "ASIX AX88xxx Based USB 2.0 Ethernet Devices"
 	depends on USB_USBNET && NET_ETHERNET
 	select CRC32
 	select MII
 	default y
 	help
-	  This option adds support for ASIX AX88172 based USB 2.0
+	  This option adds support for ASIX AX88xxx based USB 2.0
 	  10/100 Ethernet devices.
 
  	  This driver should work with at least the following devices:
diff -puN drivers/usb/net/usbnet.c~bk-usb drivers/usb/net/usbnet.c
--- 25/drivers/usb/net/usbnet.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/net/usbnet.c	2005-02-09 19:12:30.000000000 -0800
@@ -2,7 +2,8 @@
  * USB Networking Links
  * Copyright (C) 2000-2003 by David Brownell <dbrownell@users.sourceforge.net>
  * Copyright (C) 2002 Pavel Machek <pavel@ucw.cz>
- * Copyright (C) 2003 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
  * Copyright (c) 2002-2003 TiVo Inc.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -109,6 +110,7 @@
  *		(Neil Bortnak)
  * 03-nov-2004	Trivial patch for KC2190 (KC-190) chip. (Jonathan McDowell)
  *
+ * 01-feb-2005	AX88772 support (Phil Chang & Dave Hollis)
  *-------------------------------------------------------------------------*/
 
 // #define	DEBUG			// error path messages, extra info
@@ -222,6 +224,8 @@ struct driver_info {
 #define FLAG_NO_SETINT	0x0010		/* device can't set_interface() */
 #define FLAG_ETHER	0x0020		/* maybe use "eth%d" names */
 
+#define FLAG_FRAMING_AX 0x0040          /* AX88772/178 packets */
+
 	/* init device ... can sleep, or cause probe() failure */
 	int	(*bind)(struct usbnet *, struct usb_interface *);
 
@@ -274,9 +278,6 @@ module_param (msg_level, int, 0);
 MODULE_PARM_DESC (msg_level, "Initial message level (default = 1)");
 
 
-#define	RUN_CONTEXT (in_irq () ? "in_irq" \
-			: (in_interrupt () ? "in_interrupt" : "can sleep"))
-
 #ifdef DEBUG
 #define devdbg(usbnet, fmt, arg...) \
 	printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
@@ -435,6 +436,8 @@ static const struct driver_info	an2720_i
 #define AX_CMD_SET_HW_MII		0x0a
 #define AX_CMD_READ_EEPROM		0x0b
 #define AX_CMD_WRITE_EEPROM		0x0c
+#define AX_CMD_WRITE_ENABLE		0x0d
+#define AX_CMD_WRITE_DISABLE		0x0e
 #define AX_CMD_WRITE_RX_CTL		0x10
 #define AX_CMD_READ_IPG012		0x11
 #define AX_CMD_WRITE_IPG0		0x12
@@ -447,6 +450,10 @@ static const struct driver_info	an2720_i
 #define AX_CMD_READ_MONITOR_MODE	0x1c
 #define AX_CMD_WRITE_MONITOR_MODE	0x1d
 #define AX_CMD_WRITE_GPIOS		0x1f
+#define AX_CMD_SW_RESET			0x20
+#define AX_CMD_SW_PHY_STATUS		0x21
+#define AX_CMD_SW_PHY_SELECT		0x22
+#define AX88772_CMD_READ_NODE_ID	0x13
 
 #define AX_MONITOR_MODE			0x01
 #define AX_MONITOR_LINK			0x02
@@ -458,6 +465,23 @@ static const struct driver_info	an2720_i
 
 #define AX_INTERRUPT_BUFSIZE		8
 
+#define AX_EEPROM_LEN			0x40
+
+#define AX_SWRESET_CLEAR		0x00
+#define AX_SWRESET_RR			0x01
+#define AX_SWRESET_RT			0x02
+#define AX_SWRESET_PRTE			0x04
+#define AX_SWRESET_PRL			0x08
+#define AX_SWRESET_BZ			0x10
+#define AX_SWRESET_IPRL			0x20
+#define AX_SWRESET_IPPD			0x40
+
+#define AX88772_IPG0_DEFAULT		0x15
+#define AX88772_IPG1_DEFAULT		0x0c
+#define AX88772_IPG2_DEFAULT		0x12
+
+#define AX_EEPROM_MAGIC			0xdeadbeef
+
 /* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
 struct ax8817x_data {
 	u8 multi_filter[AX_MCAST_FILTER_SIZE];
@@ -514,18 +538,16 @@ static void ax8817x_interrupt_complete(s
 	int link;
 
 	if (urb->status < 0) {
-		printk(KERN_DEBUG "ax8817x_interrupt_complete() failed with %d",
+		devdbg(dev,"ax8817x_interrupt_complete() failed with %d",
 			urb->status);
 	} else {
-		if (data->int_buf[5] == 0x90) {
-			link = data->int_buf[2] & 0x01;
-			if (netif_carrier_ok(dev->net) != link) {
-				if (link)
-					netif_carrier_on(dev->net);
-				else
-					netif_carrier_off(dev->net);
-				devdbg(dev, "ax8817x - Link Status is: %d", link);
-			}
+		link = data->int_buf[2] & 0x01;
+		if (netif_carrier_ok(dev->net) != link) {
+			if (link)
+				netif_carrier_on(dev->net);
+			else
+				netif_carrier_off(dev->net);
+			devdbg(dev, "ax8817x - Link Status is: %d", link);
 		}
 		usb_submit_urb(data->int_urb, GFP_ATOMIC);
 	}
@@ -674,6 +696,11 @@ static int ax8817x_set_wol(struct net_de
 	return 0;
 }
 
+static int ax8817x_get_eeprom_len(struct net_device *net)
+{
+	return AX_EEPROM_LEN;
+}
+
 static int ax8817x_get_eeprom(struct net_device *net,
 			      struct ethtool_eeprom *eeprom, u8 *data)
 {
@@ -687,13 +714,15 @@ static int ax8817x_get_eeprom(struct net
 	if (eeprom->len % 2)
 		return -EINVAL;
 
+	eeprom->magic = AX_EEPROM_MAGIC;
+
 	/* ax8817x returns 2 bytes from eeprom on read */
 	for (i=0; i < eeprom->len / 2; i++) {
 		if (ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 
 			eeprom->offset + i, 0, 2, &ebuf[i]) < 0)
 			return -EINVAL;
 	}
-	return i * 2;
+	return 0;
 }
 
 static void ax8817x_get_drvinfo (struct net_device *net,
@@ -728,6 +757,7 @@ static struct ethtool_ops ax8817x_ethtoo
 	.set_msglevel		= usbnet_set_msglevel,
 	.get_wol		= ax8817x_get_wol,
 	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len		= ax8817x_get_eeprom_len,
 	.get_eeprom		= ax8817x_get_eeprom,
 	.get_settings		= ax8817x_get_settings,
 	.set_settings		= ax8817x_set_settings,
@@ -735,27 +765,26 @@ static struct ethtool_ops ax8817x_ethtoo
 
 static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
 {
-	int ret;
-	u8 buf[6];
+	int ret = 0;
+	void *buf;
 	int i;
 	unsigned long gpio_bits = dev->driver_info->data;
 	struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
 
-	dev->in = usb_rcvbulkpipe(dev->udev, 3);
-	dev->out = usb_sndbulkpipe(dev->udev, 2);
+	get_endpoints(dev,intf);
 
-	// allocate irq urb
 	if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == NULL) {
 		dbg ("%s: cannot allocate interrupt URB",
 			dev->net->name);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto out1;
 	}
 	
 	if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
 		dbg ("%s: cannot allocate memory for interrupt buffer",
 			dev->net->name);
-		usb_free_urb(data->int_urb);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto out1;
 	}
 	memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
 
@@ -765,36 +794,43 @@ static int ax8817x_bind(struct usbnet *d
 		ax8817x_interrupt_complete, dev,
 		dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
 
+	buf = kmalloc(ETH_ALEN, GFP_KERNEL);
+	if(!buf) {
+		ret = -ENOMEM;
+		goto out2;
+	}
+
 	/* Toggle the GPIOs in a manufacturer/model specific way */
 	for (i = 2; i >= 0; i--) {
 		if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
 					(gpio_bits >> (i * 8)) & 0xff, 0, 0,
 					buf)) < 0)
-			return ret;
+			goto out3;
 		msleep(5);
 	}
 
 	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, buf)) < 0) {
 		dbg("send AX_CMD_WRITE_RX_CTL failed: %d", ret);
-		return ret;
+		goto out3;
 	}
 
 	/* Get the MAC address */
 	memset(buf, 0, ETH_ALEN);
 	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, 6, buf)) < 0) {
 		dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
-		return ret;
+		goto out3;
 	}
 	memcpy(dev->net->dev_addr, buf, ETH_ALEN);
 
 	/* Get the PHY id */
 	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
 		dbg("error on read AX_CMD_READ_PHY_ID: %02x", ret);
-		return ret;
+		goto out3;
 	} else if (ret < 2) {
 		/* this should always return 2 bytes */
 		dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x", ret);
-		return -EIO;
+		ret = -EIO;
+		goto out3;
 	}
 
 	/* Initialize MII structure */
@@ -803,7 +839,7 @@ static int ax8817x_bind(struct usbnet *d
 	dev->mii.mdio_write = ax8817x_mdio_write;
 	dev->mii.phy_id_mask = 0x3f;
 	dev->mii.reg_num_mask = 0x1f;
-	dev->mii.phy_id = buf[1];
+	dev->mii.phy_id = *((u8 *)buf + 1);
 
 	dev->net->set_multicast_list = ax8817x_set_multicast;
 	dev->net->ethtool_ops = &ax8817x_ethtool_ops;
@@ -816,11 +852,17 @@ static int ax8817x_bind(struct usbnet *d
 
 	if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) {
 		dbg("Failed to submit interrupt URB: %02x", ret);
-		usb_free_urb(data->int_urb);
-		return ret;
+		goto out2;
 	}
 
 	return 0;
+out3:
+	kfree(buf);
+out2:
+	kfree(data->int_buf);
+out1:
+	usb_free_urb(data->int_urb);
+	return ret;
 }
 
 static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf)
@@ -832,6 +874,290 @@ static void ax8817x_unbind(struct usbnet
 	kfree(data->int_buf);
 }
 
+static struct ethtool_ops ax88772_ethtool_ops = {
+	.get_drvinfo		= ax8817x_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len		= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+};
+
+static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
+
+	get_endpoints(dev,intf);
+
+	if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) {
+		dbg ("Cannot allocate interrupt URB");
+		ret = -ENOMEM;
+		goto out1;
+	}
+	
+	if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
+		dbg ("Cannot allocate memory for interrupt buffer");
+		ret = -ENOMEM;
+		goto out1;
+	}
+	memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
+
+	usb_fill_int_urb (data->int_urb, dev->udev,
+		usb_rcvintpipe (dev->udev, 1),
+		data->int_buf, AX_INTERRUPT_BUFSIZE,
+		ax8817x_interrupt_complete, dev,
+		dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
+
+	buf = kmalloc(6, GFP_KERNEL);
+	if(!buf) {
+		dbg ("Cannot allocate memory for buffer");
+		ret = -ENOMEM;
+		goto out2;
+	}
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+				     0x00B0, 0, 0, buf)) < 0)
+		goto out3;
+
+	msleep(5);
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0x0001, 0, 0, buf)) < 0) {
+		dbg("Select PHY #1 failed: %d", ret);
+		goto out3;
+	}
+
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD, 0, 0, buf)) < 0) {
+		dbg("Failed to power down internal PHY: %d", ret);
+		goto out3;
+	}
+
+	msleep(150);
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR, 0, 0, buf)) < 0) {
+		dbg("Failed to perform software reset: %d", ret);
+		goto out3;
+	}
+
+	msleep(150);
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+		dbg("Failed to set Internal/External PHY reset control: %d", ret);
+		goto out3;
+	}
+
+	msleep(150);
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0000, 0, 0,
+			       buf)) < 0) {
+		dbg("Failed to reset RX_CTL: %d", ret);
+		goto out3;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	if ((ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf)) < 0) {
+		dbg("Failed to read MAC address: %d", ret);
+		goto out3;
+	}
+	memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, buf)) < 0) {
+		dbg("Enabling software MII failed: %d", ret);
+		goto out3;
+	}
+
+	if (((ret =
+	      ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, 0x0010, 2, 2, buf)) < 0)
+	    || (*((u16 *)buf) != 0x003b)) {
+		dbg("Read PHY register 2 must be 0x3b00: %d", ret);
+		goto out3;
+	}
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read;
+	dev->mii.mdio_write = ax8817x_mdio_write;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+
+	/* Get the PHY id */
+	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
+		dbg("Error reading PHY ID: %02x", ret);
+		goto out3;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x",
+		    ret);
+		ret = -EIO;
+		goto out3;
+	}
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+		dbg("Set external PHY reset pin level: %d", ret);
+		goto out3;
+	}
+	msleep(150);
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+		dbg("Set Internal/External PHY reset control: %d", ret);
+		goto out3;
+	}
+	msleep(150);
+
+
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+	ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+			cpu_to_le16(BMCR_RESET));
+	ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA));
+	mii_nway_restart(&dev->mii);
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 0x0336, 0, 0, buf)) < 0) {
+		dbg("Write medium mode register: %d", ret);
+		goto out3;
+	}
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,AX88772_IPG2_DEFAULT, 0, buf)) < 0) {
+		dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
+		goto out3;
+	}
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) {
+		dbg("Failed to set hardware MII: %02x", ret);
+		goto out3;
+	}
+
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0088, 0, 0,
+			       buf)) < 0) {
+		dbg("Reset RX_CTL failed: %d", ret);
+		goto out3;
+	}
+
+	if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) {
+		dbg("Failed to submit interrupt URB: %02x", ret);
+		goto out3;
+	}
+
+	kfree(buf);
+
+	return 0;
+
+out3:
+	kfree(buf);
+out2:
+	kfree(data->int_buf);
+out1:
+	usb_free_urb(data->int_urb);
+
+	return ret;
+}
+
+static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	u32 *header;
+	char *packet;
+	struct sk_buff *ax_skb;
+	u16 size;
+
+	header = (u32 *) skb->data;
+	le32_to_cpus(header);
+	packet = (char *)(header + 1);
+
+	skb_pull(skb, 4);
+
+	while (skb->len > 0) {
+		if ((short)(*header & 0x0000ffff) !=
+		    ~((short)((*header & 0xffff0000) >> 16))) {
+			devdbg(dev,"header length data is error");
+		}
+		/* get the packet length */
+		size = (u16) (*header & 0x0000ffff);
+
+		if ((skb->len) - ((size + 1) & 0xfffe) == 0)
+			return 2;
+		if (size > ETH_FRAME_LEN) {
+			devdbg(dev,"invalid rx length %d", size);
+			return 0;
+		}
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+		if (ax_skb) {
+			ax_skb->len = size;
+			ax_skb->data = packet;
+			ax_skb->tail = packet + size;
+			skb_return(dev, ax_skb);
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (size + 1) & 0xfffe);
+
+		if (skb->len == 0)
+			break;
+
+		header = (u32 *) skb->data;
+		le32_to_cpus(header);
+		packet = (char *)(header + 1);
+		skb_pull(skb, 4);
+	}
+
+	if (skb->len < 0) {
+		devdbg(dev,"invalid rx length %d", skb->len);
+		return 0;
+	}
+	return 1;
+}
+
+static struct sk_buff *ax88772_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+					int flags)
+{
+	int padlen;
+	int headroom = skb_headroom(skb);
+	int tailroom = skb_tailroom(skb);
+	u32 *packet_len;
+	u32 *padbytes_ptr;
+
+	padlen = ((skb->len + 4) % 512) ? 0 : 4;
+
+	if ((!skb_cloned(skb))
+	    && ((headroom + tailroom) >= (4 + padlen))) {
+		if ((headroom < 4) || (tailroom < padlen)) {
+			skb->data = memmove(skb->head + 4, skb->data, skb->len);
+			skb->tail = skb->data + skb->len;
+		}
+	} else {
+		struct sk_buff *skb2;
+		skb2 = skb_copy_expand(skb, 4, padlen, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	packet_len = (u32 *) skb_push(skb, 4);
+
+	packet_len = (u32 *) skb->data;
+	*packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+
+	if ((skb->len % 512) == 0) {
+		padbytes_ptr = (u32 *) skb->tail;
+		*padbytes_ptr = 0xffff0000;
+		skb_put(skb, padlen);
+	}
+	return skb;
+}
+
 static const struct driver_info ax8817x_info = {
 	.description = "ASIX AX8817x USB 2.0 Ethernet",
 	.bind = ax8817x_bind,
@@ -864,6 +1190,16 @@ static const struct driver_info hawking_
 	.data = 0x001f1d1f,
 };
 
+static const struct driver_info ax88772_info = {
+	.description = "ASIX AX88772 USB 2.0 Ethernet",
+	.bind = ax88772_bind,
+	.unbind = ax8817x_unbind,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+	.data = 0x00130103,
+};
+
 #endif /* CONFIG_USB_AX8817X */
 
 
@@ -911,45 +1247,14 @@ static const struct driver_info	belkin_i
 
 #ifdef	NEED_GENERIC_CDC
 
-/* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
-struct header_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-
-	u16	bcdCDC;
-} __attribute__ ((packed));
-
-/* "Union Functional Descriptor" from CDC spec 5.2.3.X */
-struct union_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-
-	u8	bMasterInterface0;
-	u8	bSlaveInterface0;
-	/* ... and there could be other slave interfaces */
-} __attribute__ ((packed));
-
-/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
-struct ether_desc {
-	u8	bLength;
-	u8	bDescriptorType;
-	u8	bDescriptorSubType;
-
-	u8	iMACAddress;
-	u32	bmEthernetStatistics;
-	__le16	wMaxSegmentSize;
-	__le16	wNumberMCFilters;
-	u8	bNumberPowerFilters;
-} __attribute__ ((packed));
+#include <linux/usb_cdc.h>
 
 struct cdc_state {
-	struct header_desc	*header;
-	struct union_desc	*u;
-	struct ether_desc	*ether;
-	struct usb_interface	*control;
-	struct usb_interface	*data;
+	struct usb_cdc_header_desc	*header;
+	struct usb_cdc_union_desc	*u;
+	struct usb_cdc_ether_desc	*ether;
+	struct usb_interface		*control;
+	struct usb_interface		*data;
 };
 
 static struct usb_driver usbnet_driver;
@@ -1004,7 +1309,7 @@ static int generic_cdc_bind (struct usbn
 		 * CDC Ethernet achieves with a simple descriptor.
 		 */
 		switch (buf [2]) {
-		case 0x00:		/* Header, mostly useless */
+		case USB_CDC_HEADER_TYPE:
 			if (info->header) {
 				dev_dbg (&intf->dev, "extra CDC header\n");
 				goto bad_desc;
@@ -1016,7 +1321,7 @@ static int generic_cdc_bind (struct usbn
 				goto bad_desc;
 			}
 			break;
-		case 0x06:		/* Union (groups interfaces) */
+		case USB_CDC_UNION_TYPE:
 			if (info->u) {
 				dev_dbg (&intf->dev, "extra CDC union\n");
 				goto bad_desc;
@@ -1065,7 +1370,7 @@ static int generic_cdc_bind (struct usbn
 				goto bad_desc;
 			}
 			break;
-		case 0x0F:		/* Ethernet Networking */
+		case USB_CDC_ETHERNET_TYPE:
 			if (info->ether) {
 				dev_dbg (&intf->dev, "extra CDC ether\n");
 				goto bad_desc;
@@ -1169,7 +1474,7 @@ static u8 nibble (unsigned char c)
 }
 
 static inline int
-get_ethernet_addr (struct usbnet *dev, struct ether_desc *e)
+get_ethernet_addr (struct usbnet *dev, struct usb_cdc_ether_desc *e)
 {
 	int 		tmp, i;
 	unsigned char	buf [13];
@@ -1602,9 +1907,9 @@ static const struct driver_info	genelink
  */
 
 struct nc_header {		// packed:
-	u16	hdr_len;		// sizeof nc_header (LE, all)
-	u16	packet_len;		// payload size (including ethhdr)
-	u16	packet_id;		// detects dropped packets
+	__le16	hdr_len;		// sizeof nc_header (LE, all)
+	__le16	packet_len;		// payload size (including ethhdr)
+	__le16	packet_id;		// detects dropped packets
 #define MIN_HEADER	6
 
 	// all else is optional, and must start with:
@@ -1615,7 +1920,7 @@ struct nc_header {		// packed:
 #define	PAD_BYTE	((unsigned char)0xAC)
 
 struct nc_trailer {
-	u16	packet_id;
+	__le16	packet_id;
 } __attribute__((__packed__));
 
 // packets may use FLAG_FRAMING_NC and optional pad
@@ -1973,6 +2278,7 @@ static int net1080_rx_fixup (struct usbn
 {
 	struct nc_header	*header;
 	struct nc_trailer	*trailer;
+	u16			hdr_len, packet_len;
 
 	if (!(skb->len & 0x01)
 			|| MIN_FRAMED > skb->len
@@ -1986,50 +2292,50 @@ static int net1080_rx_fixup (struct usbn
 	}
 
 	header = (struct nc_header *) skb->data;
-	le16_to_cpus (&header->hdr_len);
-	le16_to_cpus (&header->packet_len);
-	if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) {
+	hdr_len = le16_to_cpup (&header->hdr_len);
+	packet_len = le16_to_cpup (&header->packet_len);
+	if (FRAMED_SIZE (packet_len) > MAX_PACKET) {
 		dev->stats.rx_frame_errors++;
-		dbg ("packet too big, %d", header->packet_len);
+		dbg ("packet too big, %d", packet_len);
 		nc_ensure_sync (dev);
 		return 0;
-	} else if (header->hdr_len < MIN_HEADER) {
+	} else if (hdr_len < MIN_HEADER) {
 		dev->stats.rx_frame_errors++;
-		dbg ("header too short, %d", header->hdr_len);
+		dbg ("header too short, %d", hdr_len);
 		nc_ensure_sync (dev);
 		return 0;
-	} else if (header->hdr_len > MIN_HEADER) {
+	} else if (hdr_len > MIN_HEADER) {
 		// out of band data for us?
-		dbg ("header OOB, %d bytes",
-			header->hdr_len - MIN_HEADER);
+		dbg ("header OOB, %d bytes", hdr_len - MIN_HEADER);
 		nc_ensure_sync (dev);
 		// switch (vendor/product ids) { ... }
 	}
-	skb_pull (skb, header->hdr_len);
+	skb_pull (skb, hdr_len);
 
 	trailer = (struct nc_trailer *)
 		(skb->data + skb->len - sizeof *trailer);
 	skb_trim (skb, skb->len - sizeof *trailer);
 
-	if ((header->packet_len & 0x01) == 0) {
-		if (skb->data [header->packet_len] != PAD_BYTE) {
+	if ((packet_len & 0x01) == 0) {
+		if (skb->data [packet_len] != PAD_BYTE) {
 			dev->stats.rx_frame_errors++;
 			dbg ("bad pad");
 			return 0;
 		}
 		skb_trim (skb, skb->len - 1);
 	}
-	if (skb->len != header->packet_len) {
+	if (skb->len != packet_len) {
 		dev->stats.rx_frame_errors++;
 		dbg ("bad packet len %d (expected %d)",
-			skb->len, header->packet_len);
+			skb->len, packet_len);
 		nc_ensure_sync (dev);
 		return 0;
 	}
 	if (header->packet_id != get_unaligned (&trailer->packet_id)) {
 		dev->stats.rx_fifo_errors++;
 		dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x",
-			header->packet_id, trailer->packet_id);
+			le16_to_cpu (header->packet_id),
+			le16_to_cpu (trailer->packet_id));
 		return 0;
 	}
 #if 0
@@ -2402,6 +2708,11 @@ static void rx_submit (struct usbnet *de
 		size = RNDIS_MAX_TRANSFER;
 	else
 #endif
+#ifdef CONFIG_USB_AX8817X
+	if (dev->driver_info->flags & FLAG_FRAMING_AX)
+		size = 2048;
+	else
+#endif
 		size = (sizeof (struct ethhdr) + dev->net->mtu);
 
 	if ((skb = alloc_skb (size, flags)) == NULL) {
@@ -2677,6 +2988,8 @@ static int usbnet_open (struct net_devic
 			framing = "Zaurus";
 		else if (dev->driver_info->flags & FLAG_FRAMING_RN)
 			framing = "RNDIS";
+		else if (dev->driver_info->flags & FLAG_FRAMING_AX)
+			framing = "ASIX";
 		else
 			framing = "simple";
 
@@ -3197,6 +3510,32 @@ out:
 	return status;
 }
 
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_PM
+
+static int usbnet_suspend (struct usb_interface *intf, u32 state)
+{
+	struct usbnet		*dev = usb_get_intfdata(intf);
+	
+	netif_device_detach (dev->net);
+	return 0;
+}
+
+static int usbnet_resume (struct usb_interface *intf)
+{
+	struct usbnet		*dev = usb_get_intfdata(intf);
+
+	netif_device_attach (dev->net);
+	return 0;
+}
+
+#else	/* !CONFIG_PM */
+
+#define	usbnet_suspend	NULL
+#define	usbnet_resume	NULL
+
+#endif	/* CONFIG_PM */
 
 /*-------------------------------------------------------------------------*/
 
@@ -3290,6 +3629,10 @@ static const struct usb_device_id	produc
 	// goodway corp usb gwusb2e
 	USB_DEVICE (0x1631, 0x6200),
 	.driver_info = (unsigned long) &ax8817x_info,
+}, {
+	// ASIX AX88772 10/100
+        USB_DEVICE (0x0b95, 0x7720),
+        .driver_info = (unsigned long) &ax88772_info,
 },
 #endif
 
@@ -3374,6 +3717,7 @@ static const struct usb_device_id	produc
 	.driver_info =	(unsigned long) &blob_info,
 }, {
 	// Linux Ethernet/RNDIS gadget on pxa210/25x/26x
+	// e.g. Gumstix, current OpenZaurus, ...
 	USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203),
 	.driver_info =	(unsigned long) &linuxdev_info,
 }, 
@@ -3386,72 +3730,64 @@ static const struct usb_device_id	produc
  *
  * PXA-2xx based models are also lying-about-cdc.
  *
+ * NOTE:  OpenZaurus versions with 2.6 kernels won't use these entries,
+ * unlike the older ones with 2.4 "embedix" kernels.
+ *
  * NOTE:  These entries do double-duty, serving as blacklist entries
  * whenever Zaurus support isn't enabled, but CDC Ethernet is.
  */
+#define	ZAURUS_MASTER_INTERFACE \
+	.bInterfaceClass	= USB_CLASS_COMM, \
+	.bInterfaceSubClass	= USB_CDC_SUBCLASS_ETHERNET, \
+	.bInterfaceProtocol	= USB_CDC_PROTO_NONE
 {
 	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
 			  | USB_DEVICE_ID_MATCH_DEVICE, 
 	.idVendor		= 0x04DD,
 	.idProduct		= 0x8004,
-	/* match the master interface */
-	.bInterfaceClass	= USB_CLASS_COMM,
-	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol	= 0,
+	ZAURUS_MASTER_INTERFACE,
 	.driver_info = ZAURUS_STRONGARM_INFO,
 }, {
 	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
 			  | USB_DEVICE_ID_MATCH_DEVICE, 
 	.idVendor		= 0x04DD,
 	.idProduct		= 0x8005,	/* A-300 */
-	.bInterfaceClass	= USB_CLASS_COMM,
-	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol	= 0x00,
+	ZAURUS_MASTER_INTERFACE,
 	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
 			  | USB_DEVICE_ID_MATCH_DEVICE, 
 	.idVendor		= 0x04DD,
 	.idProduct		= 0x8006,	/* B-500/SL-5600 */
-	.bInterfaceClass	= USB_CLASS_COMM,
-	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol	= 0x00,
+	ZAURUS_MASTER_INTERFACE,
 	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 	          | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor		= 0x04DD,
 	.idProduct		= 0x8007,	/* C-700 */
-	.bInterfaceClass	= USB_CLASS_COMM,
-	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol = 0x00,
+	ZAURUS_MASTER_INTERFACE,
 	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 		 | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor               = 0x04DD,
 	.idProduct              = 0x9031,	/* C-750 C-760 */
-	.bInterfaceClass	= USB_CLASS_COMM,
-	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol     = 0x00,
+	ZAURUS_MASTER_INTERFACE,
 	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 		 | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor               = 0x04DD,
 	.idProduct              = 0x9032,	/* SL-6000 */
-	.bInterfaceClass	= USB_CLASS_COMM,
-	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol     = 0x00,
+	ZAURUS_MASTER_INTERFACE,
 	.driver_info = ZAURUS_PXA_INFO,
 }, {
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 		 | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor               = 0x04DD,
 	.idProduct              = 0x9050,	/* C-860 */
-	.bInterfaceClass	= USB_CLASS_COMM,
-	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol     = 0x00,
+	ZAURUS_MASTER_INTERFACE,
 	.driver_info = ZAURUS_PXA_INFO,
 },
 
@@ -3463,9 +3799,7 @@ static const struct usb_device_id	produc
 		 | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor               = 0x07B4,
 	.idProduct              = 0x0F02,	/* R-1000 */
-	.bInterfaceClass	= USB_CLASS_COMM,
-	.bInterfaceSubClass	= 6 /* Ethernet model */,
-	.bInterfaceProtocol     = 0x00,
+	ZAURUS_MASTER_INTERFACE,
 	.driver_info = OLYMPUS_MXL_INFO,
 },
 #endif
@@ -3480,7 +3814,8 @@ static const struct usb_device_id	produc
 	 * NOTE:  this match must come AFTER entries working around
 	 * bugs/quirks in a given product (like Zaurus, above).
 	 */
-	USB_INTERFACE_INFO (USB_CLASS_COMM, 6 /* Ethernet model */, 0),
+	USB_INTERFACE_INFO (USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
+			USB_CDC_PROTO_NONE),
 	.driver_info = (unsigned long) &cdc_info,
 },
 #endif
@@ -3495,6 +3830,8 @@ static struct usb_driver usbnet_driver =
 	.id_table =	products,
 	.probe =	usbnet_probe,
 	.disconnect =	usbnet_disconnect,
+	.suspend =	usbnet_suspend,
+	.resume =	usbnet_resume,
 };
 
 /* Default ethtool_ops assigned.  Devices can override in their bind() routine */
diff -puN drivers/usb/serial/cypress_m8.c~bk-usb drivers/usb/serial/cypress_m8.c
--- 25/drivers/usb/serial/cypress_m8.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/serial/cypress_m8.c	2005-02-09 19:12:30.000000000 -0800
@@ -57,9 +57,10 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/spinlock.h>
-#include <asm/uaccess.h>
 #include <linux/usb.h>
 #include <linux/serial.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
 
 #include "usb-serial.h"
 #include "cypress_m8.h"
@@ -1013,8 +1014,7 @@ static void cypress_set_termios (struct 
 	cypress_serial_control(port, baud_mask, data_bits, stop_bits, parity_enable,
 			       parity_type, 0, CYPRESS_SET_CONFIG);
 
-	set_current_state(TASK_INTERRUPTIBLE);
-	schedule_timeout(50*HZ/1000); /* give some time between change and read (50ms) */ 
+	msleep(50);			/* give some time between change and read (50ms) */
 
 	/* we perform a CYPRESS_GET_CONFIG so that the current settings are filled into the private structure
          * this should confirm that all is working if it returns what we just set */
diff -puN drivers/usb/serial/ftdi_sio.c~bk-usb drivers/usb/serial/ftdi_sio.c
--- 25/drivers/usb/serial/ftdi_sio.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/serial/ftdi_sio.c	2005-02-09 19:12:30.000000000 -0800
@@ -296,6 +296,7 @@ static struct usb_device_id id_table_8U2
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_IRTRANS_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0, 0x3ff) },
+	{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0, 0x3ff) },
 	{ USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) },
 	{ USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) },
@@ -381,6 +382,7 @@ static struct usb_device_id id_table_FT2
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_IRTRANS_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0x400, 0xffff) },
+	{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0x400, 0xffff) },
@@ -515,6 +517,7 @@ static struct usb_device_id id_table_com
 	{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
 	{ USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) },
diff -puN drivers/usb/serial/ftdi_sio.h~bk-usb drivers/usb/serial/ftdi_sio.h
--- 25/drivers/usb/serial/ftdi_sio.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/serial/ftdi_sio.h	2005-02-09 19:12:30.000000000 -0800
@@ -26,6 +26,7 @@
 #define FTDI_SIO_PID	0x8372	/* Product Id SIO application of 8U100AX  */
 #define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */
 #define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */
+#define FTDI_8U232AM_ALT_ALT_PID 0xf3c0 /* FTDI's second alternate PID for above */
 #define FTDI_8U2232C_PID 0x6010 /* Dual channel device */
 #define FTDI_RELAIS_PID	0xFA10  /* Relais device from Rudolf Gugler */
 #define FTDI_NF_RIC_VID	0x0DCD	/* Vendor Id */
diff -puN drivers/usb/serial/io_edgeport.c~bk-usb drivers/usb/serial/io_edgeport.c
--- 25/drivers/usb/serial/io_edgeport.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/serial/io_edgeport.c	2005-02-09 19:12:30.000000000 -0800
@@ -261,6 +261,7 @@
 #include <linux/spinlock.h>
 #include <linux/serial.h>
 #include <linux/ioctl.h>
+#include <linux/wait.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
 #include "usb-serial.h"
@@ -971,7 +972,7 @@ static void edge_bulk_out_cmd_callback (
 
 	/* we have completed the command */
 	edge_port->commandPending = FALSE;
-	wake_up_interruptible(&edge_port->wait_command);
+	wake_up(&edge_port->wait_command);
 }
 
 
@@ -991,7 +992,6 @@ static int edge_open (struct usb_serial_
 	struct usb_serial *serial;
 	struct edgeport_serial *edge_serial;
 	int response;
-	int timeout;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -1073,10 +1073,7 @@ static int edge_open (struct usb_serial_
 	}
 
 	/* now wait for the port to be completely opened */
-	timeout = OPEN_TIMEOUT;
-	while (timeout && edge_port->openPending == TRUE) {
-		timeout = interruptible_sleep_on_timeout (&edge_port->wait_open, timeout);
-	}
+	wait_event_timeout(edge_port->wait_open, (edge_port->openPending != TRUE), OPEN_TIMEOUT);
 
 	if (edge_port->open == FALSE) {
 		/* open timed out */
@@ -1128,9 +1125,10 @@ static int edge_open (struct usb_serial_
  ************************************************************************/
 static void block_until_chase_response(struct edgeport_port *edge_port)
 {
+	DEFINE_WAIT(wait);
 	__u16 lastCredits;
 	int timeout = 1*HZ;
-	int wait = 10;
+	int loop = 10;
 
 	while (1) {
 		// Save Last credits
@@ -1148,12 +1146,14 @@ static void block_until_chase_response(s
 		}
 
 		// Block the thread for a while
-		interruptible_sleep_on_timeout (&edge_port->wait_chase, timeout);
+		prepare_to_wait(&edge_port->wait_chase, &wait, TASK_UNINTERRUPTIBLE);
+		schedule_timeout(timeout);
+		finish_wait(&edge_port->wait_chase, &wait);
 
 		if (lastCredits == edge_port->txCredits) {
 			// No activity.. count down.
-			wait--;
-			if (wait == 0) {
+			loop--;
+			if (loop == 0) {
 				edge_port->chaseResponsePending = FALSE;
 				dbg("%s - Chase TIMEOUT", __FUNCTION__);
 				return;
@@ -1161,7 +1161,7 @@ static void block_until_chase_response(s
 		} else {
 			// Reset timout value back to 10 seconds
 			dbg("%s - Last %d, Current %d", __FUNCTION__, lastCredits, edge_port->txCredits);
-			wait = 10;
+			loop = 10;
 		}
 	}
 }
@@ -1179,10 +1179,11 @@ static void block_until_chase_response(s
  ************************************************************************/
 static void block_until_tx_empty (struct edgeport_port *edge_port)
 {
+	DEFINE_WAIT(wait);
 	struct TxFifo *fifo = &edge_port->txfifo;
 	__u32 lastCount;
 	int timeout = HZ/10;
-	int wait = 30;
+	int loop = 30;
 
 	while (1) {
 		// Save Last count
@@ -1195,20 +1196,22 @@ static void block_until_tx_empty (struct
 		}
 
 		// Block the thread for a while
-		interruptible_sleep_on_timeout (&edge_port->wait_chase, timeout);
+		prepare_to_wait (&edge_port->wait_chase, &wait, TASK_UNINTERRUPTIBLE);
+		schedule_timeout(timeout);
+		finish_wait(&edge_port->wait_chase, &wait);
 
 		dbg("%s wait", __FUNCTION__);
 
 		if (lastCount == fifo->count) {
 			// No activity.. count down.
-			wait--;
-			if (wait == 0) {
+			loop--;
+			if (loop == 0) {
 				dbg("%s - TIMEOUT", __FUNCTION__);
 				return;
 			}
 		} else {
 			// Reset timout value back to seconds
-			wait = 30;
+			loop = 30;
 		}
 	}
 }
@@ -1836,12 +1839,12 @@ static int get_serial_info(struct edgepo
  *****************************************************************************/
 static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
 {
+	DEFINE_WAIT(wait);
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	struct async_icount cnow;
 	struct async_icount cprev;
 	struct serial_icounter_struct icount;
 
-
 	dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
 
 	switch (cmd) {
@@ -1868,7 +1871,9 @@ static int edge_ioctl (struct usb_serial
 			dbg("%s (%d) TIOCMIWAIT", __FUNCTION__,  port->number);
 			cprev = edge_port->icount;
 			while (1) {
-				interruptible_sleep_on(&edge_port->delta_msr_wait);
+				prepare_to_wait(&edge_port->delta_msr_wait, &wait, TASK_INTERRUPTIBLE);
+				schedule();
+				finish_wait(&edge_port->delta_msr_wait, &wait);
 				/* see if a signal did it */
 				if (signal_pending(current))
 					return -ERESTARTSYS;
@@ -2108,7 +2113,7 @@ static void process_rcvd_status (struct 
 				// We could choose to do something else when Byte3 says Timeout on Chase from Edgeport,
 				// like wait longer in block_until_chase_response, but for now we don't. 
 				edge_port->chaseResponsePending = FALSE;
-				wake_up_interruptible (&edge_port->wait_chase);
+				wake_up (&edge_port->wait_chase);
 				return;
 
 			case IOSP_EXT_STATUS_RX_CHECK_RSP:
@@ -2131,7 +2136,7 @@ static void process_rcvd_status (struct 
 		/* we have completed the open */
 		edge_port->openPending = FALSE;
 		edge_port->open = TRUE;
-		wake_up_interruptible(&edge_port->wait_open);
+		wake_up(&edge_port->wait_open);
 		return;
 	}
 
@@ -2500,9 +2505,7 @@ static int write_cmd_usb (struct edgepor
 	// wait for command to finish
 	timeout = COMMAND_TIMEOUT;
 #if 0
-	while (timeout && edge_port->commandPending == TRUE) {
-		timeout = interruptible_sleep_on_timeout (&edge_port->wait_command, timeout);
-	}
+	wait_event (&edge_port->wait_command, (edge_port->commandPending == FALSE));
 
 	if (edge_port->commandPending == TRUE) {
 		/* command timed out */
diff -puN drivers/usb/storage/Kconfig~bk-usb drivers/usb/storage/Kconfig
--- 25/drivers/usb/storage/Kconfig~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/Kconfig	2005-02-09 19:12:30.000000000 -0800
@@ -90,12 +90,26 @@ config USB_STORAGE_DPCM
 	  Say Y here to support the Microtech/ZiO! CompactFlash reader.
 	  There is a web page at <http://www.ziocorp.com/products/>.
 
-config USB_STORAGE_HP8200e
-	bool "HP CD-Writer 82xx support (EXPERIMENTAL)"
+config USB_STORAGE_USBAT
+	bool "USBAT/USBAT02-based storage support (EXPERIMENTAL)"
 	depends on USB_STORAGE && EXPERIMENTAL
 	help
-	  Say Y here to include additional code to support Hewlett-Packard
-	  8200e/8210e/8230e CD-Writer Plus drives.
+	  Say Y here to include additional code to support storage devices
+	  based on the SCM/Shuttle USBAT/USBAT02 processors.
+
+	  Devices reported to work with this driver include:
+	  - CompactFlash reader included with Kodak DC3800 camera
+	  - Dane-Elec Zmate CompactFlash reader
+	  - Delkin Efilm reader2
+	  - HP 8200e/8210e/8230e CD-Writer Plus drives
+	  - I-JAM JS-50U
+	  - Jessops CompactFlash JESDCFRU BLACK
+	  - Kingston Technology PCREAD-USB/CF
+	  - Maxell UA4 CompactFlash reader
+	  - Memorex UCF-100
+	  - Microtech ZiO! ICS-45 CF2
+	  - RCA LYRA MP3 portable
+	  - Sandisk ImageMate SDDR-05b
 
 config USB_STORAGE_SDDR09
 	bool "SanDisk SDDR-09 (and other SmartMedia) support (EXPERIMENTAL)"
diff -puN drivers/usb/storage/Makefile~bk-usb drivers/usb/storage/Makefile
--- 25/drivers/usb/storage/Makefile~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/Makefile	2005-02-09 19:12:30.000000000 -0800
@@ -10,7 +10,7 @@ EXTRA_CFLAGS	:= -Idrivers/scsi
 obj-$(CONFIG_USB_STORAGE)	+= usb-storage.o
 
 usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG)	+= debug.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e)	+= shuttle_usbat.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_USBAT)	+= shuttle_usbat.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09)	+= sddr09.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55)	+= sddr55.o
 usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM)	+= freecom.o
diff -puN drivers/usb/storage/protocol.c~bk-usb drivers/usb/storage/protocol.c
--- 25/drivers/usb/storage/protocol.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/protocol.c	2005-02-09 19:12:30.000000000 -0800
@@ -54,39 +54,6 @@
 #include "transport.h"
 
 /***********************************************************************
- * Helper routines
- ***********************************************************************/
-
-/*
- * Fix-up the return data from a READ CAPACITY command. My Feiya reader
- * returns a value that is 1 too large.
- */
-static void fix_read_capacity(struct scsi_cmnd *srb)
-{
-	unsigned int index, offset;
-	__be32 c;
-	unsigned long capacity;
-
-	/* verify that it's a READ CAPACITY command */
-	if (srb->cmnd[0] != READ_CAPACITY)
-		return;
-
-	index = offset = 0;
-	if (usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb,
-			&index, &offset, FROM_XFER_BUF) != 4)
-		return;
-
-	capacity = be32_to_cpu(c);
-	US_DEBUGP("US: Fixing capacity: from %ld to %ld\n",
-	       capacity+1, capacity);
-	c = cpu_to_be32(capacity - 1);
-
-	index = offset = 0;
-	usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb,
-			&index, &offset, TO_XFER_BUF);
-}
-
-/***********************************************************************
  * Protocol routines
  ***********************************************************************/
 
@@ -174,12 +141,6 @@ void usb_stor_transparent_scsi_command(s
 {
 	/* send the command to the transport layer */
 	usb_stor_invoke_transport(srb, us);
-
-	if (srb->result == SAM_STAT_GOOD) {
-		/* Fix the READ CAPACITY result if necessary */
-		if (us->flags & US_FL_FIX_CAPACITY)
-			fix_read_capacity(srb);
-	}
 }
 
 /***********************************************************************
diff -puN drivers/usb/storage/scsiglue.c~bk-usb drivers/usb/storage/scsiglue.c
--- 25/drivers/usb/storage/scsiglue.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/scsiglue.c	2005-02-09 19:12:30.000000000 -0800
@@ -149,6 +149,11 @@ static int slave_configure(struct scsi_d
 		sdev->skip_ms_page_3f = 1;
 #endif
 
+		/* Some disks return the total number blocks in response
+		 * to READ CAPACITY rather than the highest block number.
+		 * If this device makes that mistake, tell the sd driver. */
+		if (us->flags & US_FL_FIX_CAPACITY)
+			sdev->fix_capacity = 1;
 	} else {
 
 		/* Non-disk-type devices don't need to blacklist any pages
@@ -157,6 +162,11 @@ static int slave_configure(struct scsi_d
 		sdev->use_10_for_ms = 1;
 	}
 
+	/* Some devices choke when they receive a PREVENT-ALLOW MEDIUM
+	 * REMOVAL command, so suppress those commands. */
+	if (us->flags & US_FL_NOT_LOCKABLE)
+		sdev->lockable = 0;
+
 	/* this is to satisfy the compiler, tho I don't think the 
 	 * return code is ever checked anywhere. */
 	return 0;
diff -puN drivers/usb/storage/shuttle_usbat.c~bk-usb drivers/usb/storage/shuttle_usbat.c
--- 25/drivers/usb/storage/shuttle_usbat.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/shuttle_usbat.c	2005-02-09 19:12:30.000000000 -0800
@@ -4,10 +4,14 @@
  *
  * Current development and maintenance by:
  *   (c) 2000, 2001 Robert Baruch (autophile@starband.net)
+ *   (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
  *
  * Developed with the assistance of:
  *   (c) 2002 Alan Stern <stern@rowland.org>
  *
+ * Flash support based on earlier work by:
+ *   (c) 2002 Thomas Kreiling <usbdev@sm04.de>
+ *
  * Many originally ATAPI devices were slightly modified to meet the USB
  * market by using some kind of translation from ATAPI to USB on the host,
  * and the peripheral would translate from USB back to ATAPI.
@@ -21,8 +25,8 @@
  * as well. This driver is only guaranteed to work with the ATAPI
  * translation.
  *
- * The only peripheral that I know of (as of 27 Mar 2001) that uses this
- * device is the Hewlett-Packard 8200e/8210e/8230e CD-Writer Plus.
+ * See the Kconfig help text for a list of devices known to be supported by
+ * 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
@@ -59,44 +63,149 @@
 
 int transferred = 0;
 
+/*
+ * Convenience function to produce an ATAPI read/write sectors command
+ * Use cmd=0x20 for read, cmd=0x30 for write
+ */
+static void usbat_pack_atapi_sector_cmd(unsigned char *buf,
+					unsigned char thistime,
+					u32 sector, unsigned char cmd)
+{
+	buf[0] = 0;
+	buf[1] = thistime;
+	buf[2] = sector & 0xFF;
+	buf[3] = (sector >>  8) & 0xFF;
+	buf[4] = (sector >> 16) & 0xFF;
+	buf[5] = 0xE0 | ((sector >> 24) & 0x0F);
+	buf[6] = cmd;
+}
+
+/*
+ * Convenience function to get the device type (flash or hp8200)
+ */
+static int usbat_get_device_type(struct us_data *us)
+{
+	return ((struct usbat_info*)us->extra)->devicetype;
+}
+
+/*
+ * Read a register from the device
+ */
 static int usbat_read(struct us_data *us,
 		      unsigned char access,
 		      unsigned char reg,
 		      unsigned char *content)
 {
-	int result;
-
-	result = usb_stor_ctrl_transfer(us,
+	return usb_stor_ctrl_transfer(us,
 		us->recv_ctrl_pipe,
-		access,
+		access | USBAT_CMD_READ_REG,
 		0xC0,
 		(u16)reg,
 		0,
 		content,
 		1);
-
-	return result;
 }
 
+/*
+ * Write to a register on the device
+ */
 static int usbat_write(struct us_data *us,
 		       unsigned char access,
 		       unsigned char reg,
 		       unsigned char content)
 {
-	int result;
-
-	result = usb_stor_ctrl_transfer(us,
+	return usb_stor_ctrl_transfer(us,
 		us->send_ctrl_pipe,
-		access|0x01,
+		access | USBAT_CMD_WRITE_REG,
 		0x40,
 		short_pack(reg, content),
 		0,
 		NULL,
 		0);
+}
 
-	return result;
+/*
+ * Convenience function to perform a bulk read
+ */
+static int usbat_bulk_read(struct us_data *us,
+							 unsigned char *data,
+							 unsigned int len)
+{
+	if (len == 0)
+		return USB_STOR_XFER_GOOD;
+
+	US_DEBUGP("usbat_bulk_read: len = %d\n", len);
+	return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, data, len, NULL);
 }
 
+/*
+ * Convenience function to perform a bulk write
+ */
+static int usbat_bulk_write(struct us_data *us,
+							unsigned char *data,
+							unsigned int len)
+{
+	if (len == 0)
+		return USB_STOR_XFER_GOOD;
+
+	US_DEBUGP("usbat_bulk_write:  len = %d\n", len);
+	return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, data, len, NULL);
+}
+
+/*
+ * Some USBAT-specific commands can only be executed over a command transport
+ * This transport allows one (len=8) or two (len=16) vendor-specific commands
+ * to be executed.
+ */
+static int usbat_execute_command(struct us_data *us,
+								 unsigned char *commands,
+								 unsigned int len)
+{
+	return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
+								  USBAT_CMD_EXEC_CMD, 0x40, 0, 0,
+								  commands, len);
+}
+
+/*
+ * Read the status register
+ */
+static int usbat_get_status(struct us_data *us, unsigned char *status)
+{
+	int rc;
+	rc = usbat_read(us, USBAT_ATA, USBAT_ATA_STATUS, status);
+
+	US_DEBUGP("usbat_get_status: 0x%02X\n", (unsigned short) (*status));
+	return rc;
+}
+
+/*
+ * Check the device status
+ */
+static int usbat_check_status(struct us_data *us)
+{
+	unsigned char *reply = us->iobuf;
+	int rc;
+
+	if (!us)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	rc = usbat_get_status(us, reply);
+	if (rc != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_FAILED;
+
+	if (*reply & 0x01 && *reply != 0x51) // error/check condition (0x51 is ok)
+		return USB_STOR_TRANSPORT_FAILED;
+
+	if (*reply & 0x20) // device fault
+		return USB_STOR_TRANSPORT_FAILED;
+
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Stores critical information in internal registers in prepartion for the execution
+ * of a conditional usbat_read_blocks or usbat_write_blocks call.
+ */
 static int usbat_set_shuttle_features(struct us_data *us,
 				      unsigned char external_trigger,
 				      unsigned char epp_control,
@@ -105,76 +214,44 @@ static int usbat_set_shuttle_features(st
 				      unsigned char subcountH,
 				      unsigned char subcountL)
 {
-	int result;
 	unsigned char *command = us->iobuf;
 
 	command[0] = 0x40;
-	command[1] = 0x81;
+	command[1] = USBAT_CMD_SET_FEAT;
+
+	// The only bit relevant to ATA access is bit 6
+	// which defines 8 bit data access (set) or 16 bit (unset)
 	command[2] = epp_control;
+
+	// If FCQ is set in the qualifier (defined in R/W cmd), then bits U0, U1,
+	// ET1 and ET2 define an external event to be checked for on event of a
+	// _read_blocks or _write_blocks operation. The read/write will not take
+	// place unless the defined trigger signal is active.
 	command[3] = external_trigger;
+
+	// The resultant byte of the mask operation (see mask_byte) is compared for
+	// equivalence with this test pattern. If equal, the read/write will take
+	// place.
 	command[4] = test_pattern;
+
+	// This value is logically ANDed with the status register field specified
+	// in the read/write command.
 	command[5] = mask_byte;
+
+	// If ALQ is set in the qualifier, this field contains the address of the
+	// registers where the byte count should be read for transferring the data.
+	// If ALQ is not set, then this field contains the number of bytes to be
+	// transferred.
 	command[6] = subcountL;
 	command[7] = subcountH;
 
-	result = usb_stor_ctrl_transfer(us,
-		us->send_ctrl_pipe,
-		0x80,
-		0x40,
-		0,
-		0,
-		command,
-		8);
-
-	return result;
-}
-
-static int usbat_read_block(struct us_data *us,
-			    unsigned char access,
-			    unsigned char reg,
-			    unsigned char *content,
-			    unsigned short len,
-			    int use_sg)
-{
-	int result;
-	unsigned char *command = us->iobuf;
-
-	if (!len)
-		return USB_STOR_TRANSPORT_GOOD;
-
-	command[0] = 0xC0;
-	command[1] = access | 0x02;
-	command[2] = reg;
-	command[3] = 0;
-	command[4] = 0;
-	command[5] = 0;
-	command[6] = LSB_of(len);
-	command[7] = MSB_of(len);
-
-	result = usb_stor_ctrl_transfer(us,
-		us->send_ctrl_pipe,
-		0x80,
-		0x40,
-		0,
-		0,
-		command,
-		8);
-
-	if (result != USB_STOR_XFER_GOOD)
-		return USB_STOR_TRANSPORT_ERROR;
-
-	result = usb_stor_bulk_transfer_sg(us, us->recv_bulk_pipe,
-			content, len, use_sg, NULL);
-
-	return (result == USB_STOR_XFER_GOOD ?
-			USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR);
+	return usbat_execute_command(us, command, 8);
 }
 
 /*
  * Block, waiting for an ATA device to become not busy or to report
  * an error condition.
  */
-
 static int usbat_wait_not_busy(struct us_data *us, int minutes)
 {
 	int i;
@@ -189,7 +266,7 @@ static int usbat_wait_not_busy(struct us
 
 	for (i=0; i<1200+minutes*60; i++) {
 
- 		result = usbat_read(us, USBAT_ATA, 0x17, status);
+ 		result = usbat_get_status(us, status);
 
 		if (result!=USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
@@ -220,12 +297,45 @@ static int usbat_wait_not_busy(struct us
 	return USB_STOR_TRANSPORT_FAILED;
 }
 
+/*
+ * Read block data from the data register
+ */
+static int usbat_read_block(struct us_data *us,
+			    unsigned char *content,
+			    unsigned short len)
+{
+	int result;
+	unsigned char *command = us->iobuf;
+
+	if (!len)
+		return USB_STOR_TRANSPORT_GOOD;
+
+	command[0] = 0xC0;
+	command[1] = USBAT_ATA | USBAT_CMD_READ_BLOCK;
+	command[2] = USBAT_ATA_DATA;
+	command[3] = 0;
+	command[4] = 0;
+	command[5] = 0;
+	command[6] = LSB_of(len);
+	command[7] = MSB_of(len);
+
+	result = usbat_execute_command(us, command, 8);
+	if (result != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	result = usbat_bulk_read(us, content, len);
+	return (result == USB_STOR_XFER_GOOD ?
+			USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR);
+}
+
+/*
+ * Write block data via the data register
+ */
 static int usbat_write_block(struct us_data *us,
-			     unsigned char access, 
-			     unsigned char reg,
+			     unsigned char access,
 			     unsigned char *content,
 			     unsigned short len,
-			     int use_sg, int minutes)
+			     int minutes)
 {
 	int result;
 	unsigned char *command = us->iobuf;
@@ -234,57 +344,48 @@ static int usbat_write_block(struct us_d
 		return USB_STOR_TRANSPORT_GOOD;
 
 	command[0] = 0x40;
-	command[1] = access | 0x03;
-	command[2] = reg;
+	command[1] = access | USBAT_CMD_WRITE_BLOCK;
+	command[2] = USBAT_ATA_DATA;
 	command[3] = 0;
 	command[4] = 0;
 	command[5] = 0;
 	command[6] = LSB_of(len);
 	command[7] = MSB_of(len);
 
-	result = usb_stor_ctrl_transfer(us,
-		us->send_ctrl_pipe,
-		0x80,
-		0x40,
-		0,
-		0,
-		command,
-		8);
+	result = usbat_execute_command(us, command, 8);
 
 	if (result != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
-	result = usb_stor_bulk_transfer_sg(us, us->send_bulk_pipe,
-			content, len, use_sg, NULL);
-
+	result = usbat_bulk_write(us, content, len);
 	if (result != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
 	return usbat_wait_not_busy(us, minutes);
 }
 
-static int usbat_rw_block_test(struct us_data *us,
-			       unsigned char access,
-			       unsigned char *registers,
-			       unsigned char *data_out,
-			       unsigned short num_registers,
-			       unsigned char data_reg,
-			       unsigned char status_reg,
-			       unsigned char timeout,
-			       unsigned char qualifier,
-			       int direction,
-			       unsigned char *content,
-			       unsigned short len,
-			       int use_sg,
-			       int minutes)
+/*
+ * Process read and write requests
+ */
+static int usbat_hp8200e_rw_block_test(struct us_data *us,
+				       unsigned char access,
+				       unsigned char *registers,
+				       unsigned char *data_out,
+				       unsigned short num_registers,
+				       unsigned char data_reg,
+				       unsigned char status_reg,
+				       unsigned char timeout,
+				       unsigned char qualifier,
+				       int direction,
+				       unsigned char *content,
+				       unsigned short len,
+				       int use_sg,
+				       int minutes)
 {
 	int result;
 	unsigned int pipe = (direction == DMA_FROM_DEVICE) ?
 			us->recv_bulk_pipe : us->send_bulk_pipe;
 
-	// Not really sure the 0x07, 0x17, 0xfc, 0xe7 is necessary here,
-	// but that's what came out of the trace every single time.
-
 	unsigned char *command = us->iobuf;
 	int i, j;
 	int cmdlen;
@@ -308,8 +409,11 @@ static int usbat_rw_block_test(struct us
 
 		if (i==0) {
 			cmdlen = 16;
+			// Write to multiple registers
+			// Not really sure the 0x07, 0x17, 0xfc, 0xe7 is necessary here,
+			// but that's what came out of the trace every single time.
 			command[0] = 0x40;
-			command[1] = access | 0x07;
+			command[1] = access | USBAT_CMD_WRITE_REGS;
 			command[2] = 0x07;
 			command[3] = 0x17;
 			command[4] = 0xFC;
@@ -319,9 +423,11 @@ static int usbat_rw_block_test(struct us
 		} else
 			cmdlen = 8;
 
+		// Conditionally read or write blocks
 		command[cmdlen-8] = (direction==DMA_TO_DEVICE ? 0x40 : 0xC0);
 		command[cmdlen-7] = access |
-				(direction==DMA_TO_DEVICE ? 0x05 : 0x04);
+				(direction==DMA_TO_DEVICE ?
+				 USBAT_CMD_COND_WRITE_BLOCK : USBAT_CMD_COND_READ_BLOCK);
 		command[cmdlen-6] = data_reg;
 		command[cmdlen-5] = status_reg;
 		command[cmdlen-4] = timeout;
@@ -329,14 +435,7 @@ static int usbat_rw_block_test(struct us
 		command[cmdlen-2] = LSB_of(len);
 		command[cmdlen-1] = MSB_of(len);
 
-		result = usb_stor_ctrl_transfer(us,
-			us->send_ctrl_pipe,
-			0x80,
-			0x40,
-			0,
-			0,
-			command,
-			cmdlen);
+		result = usbat_execute_command(us, command, cmdlen);
 
 		if (result != USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
@@ -348,10 +447,7 @@ static int usbat_rw_block_test(struct us
 				data[1+(j<<1)] = data_out[j];
 			}
 
-			result = usb_stor_bulk_transfer_buf(us,
-					us->send_bulk_pipe,
-					data, num_registers*2, NULL);
-
+			result = usbat_bulk_write(us, data, num_registers*2);
 			if (result != USB_STOR_XFER_GOOD)
 				return USB_STOR_TRANSPORT_ERROR;
 
@@ -403,7 +499,8 @@ static int usbat_rw_block_test(struct us
 			 */
 
  			result = usbat_read(us, USBAT_ATA, 
-				direction==DMA_TO_DEVICE ? 0x17 : 0x0E, 
+				direction==DMA_TO_DEVICE ?
+					USBAT_ATA_STATUS : USBAT_ATA_ALTSTATUS,
 				status);
 
 			if (result!=USB_STOR_XFER_GOOD)
@@ -430,101 +527,602 @@ static int usbat_rw_block_test(struct us
 }
 
 /*
- * Write data to multiple registers at once. Not meant for large
- * transfers of data!
+ * Write to multiple registers:
+ * Allows us to write specific data to any registers. The data to be written
+ * gets packed in this sequence: reg0, data0, reg1, data1, ..., regN, dataN
+ * which gets sent through bulk out.
+ * Not designed for large transfers of data!
  */
-
 static int usbat_multiple_write(struct us_data *us,
-				unsigned char access,
 				unsigned char *registers,
 				unsigned char *data_out,
 				unsigned short num_registers)
 {
-	int result;
+	int i, result;
 	unsigned char *data = us->iobuf;
-	int i;
 	unsigned char *command = us->iobuf;
 
 	BUG_ON(num_registers > US_IOBUF_SIZE/2);
 
+	// Write to multiple registers, ATA access
 	command[0] = 0x40;
-	command[1] = access | 0x07;
+	command[1] = USBAT_ATA | USBAT_CMD_WRITE_REGS;
+
+	// No relevance
 	command[2] = 0;
 	command[3] = 0;
 	command[4] = 0;
 	command[5] = 0;
+
+	// Number of bytes to be transferred (incl. addresses and data)
 	command[6] = LSB_of(num_registers*2);
 	command[7] = MSB_of(num_registers*2);
 
-	result = usb_stor_ctrl_transfer(us,
-		us->send_ctrl_pipe,
-		0x80,
-		0x40,
-		0,
-		0,
-		command,
-		8);
-
+	// The setup command
+	result = usbat_execute_command(us, command, 8);
 	if (result != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
+	// Create the reg/data, reg/data sequence
 	for (i=0; i<num_registers; i++) {
 		data[i<<1] = registers[i];
 		data[1+(i<<1)] = data_out[i];
 	}
 
-	result = usb_stor_bulk_transfer_buf(us,
-		us->send_bulk_pipe, data, num_registers*2, NULL);
-
+	// Send the data
+	result = usbat_bulk_write(us, data, num_registers*2);
 	if (result != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
-	return usbat_wait_not_busy(us, 0);
+	if (usbat_get_device_type(us) == USBAT_DEV_HP8200)
+		return usbat_wait_not_busy(us, 0);
+	else
+		return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Conditionally read blocks from device:
+ * Allows us to read blocks from a specific data register, based upon the
+ * condition that a status register can be successfully masked with a status
+ * qualifier. If this condition is not initially met, the read will wait
+ * up until a maximum amount of time has elapsed, as specified by timeout.
+ * The read will start when the condition is met, otherwise the command aborts.
+ *
+ * The qualifier defined here is not the value that is masked, it defines
+ * conditions for the write to take place. The actual masked qualifier (and
+ * other related details) are defined beforehand with _set_shuttle_features().
+ */
+static int usbat_read_blocks(struct us_data *us,
+							 unsigned char *buffer,
+							 int len)
+{
+	int result;
+	unsigned char *command = us->iobuf;
+
+	command[0] = 0xC0;
+	command[1] = USBAT_ATA | USBAT_CMD_COND_READ_BLOCK;
+	command[2] = USBAT_ATA_DATA;
+	command[3] = USBAT_ATA_STATUS;
+	command[4] = 0xFD; // Timeout (ms);
+	command[5] = USBAT_QUAL_FCQ;
+	command[6] = LSB_of(len);
+	command[7] = MSB_of(len);
+
+	// Multiple block read setup command
+	result = usbat_execute_command(us, command, 8);
+	if (result != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_FAILED;
+	
+	// Read the blocks we just asked for
+	result = usbat_bulk_read(us, buffer, len);
+	if (result != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_FAILED;
+
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Conditionally write blocks to device:
+ * Allows us to write blocks to a specific data register, based upon the
+ * condition that a status register can be successfully masked with a status
+ * qualifier. If this condition is not initially met, the write will wait
+ * up until a maximum amount of time has elapsed, as specified by timeout.
+ * The read will start when the condition is met, otherwise the command aborts.
+ *
+ * The qualifier defined here is not the value that is masked, it defines
+ * conditions for the write to take place. The actual masked qualifier (and
+ * other related details) are defined beforehand with _set_shuttle_features().
+ */
+static int usbat_write_blocks(struct us_data *us,
+							  unsigned char *buffer,
+							  int len)
+{
+	int result;
+	unsigned char *command = us->iobuf;
+
+	command[0] = 0x40;
+	command[1] = USBAT_ATA | USBAT_CMD_COND_WRITE_BLOCK;
+	command[2] = USBAT_ATA_DATA;
+	command[3] = USBAT_ATA_STATUS;
+	command[4] = 0xFD; // Timeout (ms)
+	command[5] = USBAT_QUAL_FCQ;
+	command[6] = LSB_of(len);
+	command[7] = MSB_of(len);
+
+	// Multiple block write setup command
+	result = usbat_execute_command(us, command, 8);
+	if (result != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_FAILED;
+	
+	// Write the data
+	result = usbat_bulk_write(us, buffer, len);
+	if (result != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_FAILED;
+
+	return USB_STOR_TRANSPORT_GOOD;
 }
 
+/*
+ * Read the User IO register
+ */
 static int usbat_read_user_io(struct us_data *us, unsigned char *data_flags)
 {
 	int result;
 
 	result = usb_stor_ctrl_transfer(us,
 		us->recv_ctrl_pipe,
-		0x82,
+		USBAT_CMD_UIO,
 		0xC0,
 		0,
 		0,
 		data_flags,
-		1);
+		USBAT_UIO_READ);
+
+	US_DEBUGP("usbat_read_user_io: UIO register reads %02X\n", (unsigned short) (*data_flags));
 
 	return result;
 }
 
+/*
+ * Write to the User IO register
+ */
 static int usbat_write_user_io(struct us_data *us,
 			       unsigned char enable_flags,
 			       unsigned char data_flags)
 {
-	int result;
-
-	result = usb_stor_ctrl_transfer(us,
+	return usb_stor_ctrl_transfer(us,
 		us->send_ctrl_pipe,
-		0x82,
+		USBAT_CMD_UIO,
 		0x40,
 		short_pack(enable_flags, data_flags),
 		0,
 		NULL,
-		0);
+		USBAT_UIO_WRITE);
+}
+
+/*
+ * Reset the device
+ * Often needed on media change.
+ */
+static int usbat_device_reset(struct us_data *us)
+{
+	int rc;
+
+	// Reset peripheral, enable peripheral control signals
+	// (bring reset signal up)
+	rc = usbat_write_user_io(us,
+							 USBAT_UIO_DRVRST | USBAT_UIO_OE1 | USBAT_UIO_OE0,
+							 USBAT_UIO_EPAD | USBAT_UIO_1);
+	if (rc != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_ERROR;
+			
+	// Enable peripheral control signals
+	// (bring reset signal down)
+	rc = usbat_write_user_io(us,
+							 USBAT_UIO_OE1  | USBAT_UIO_OE0,
+							 USBAT_UIO_EPAD | USBAT_UIO_1);
+	if (rc != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Enable card detect
+ */
+static int usbat_device_enable_cdt(struct us_data *us)
+{
+	int rc;
+
+	// Enable peripheral control signals and card detect
+	rc = usbat_write_user_io(us,
+							 USBAT_UIO_ACKD | USBAT_UIO_OE1  | USBAT_UIO_OE0,
+							 USBAT_UIO_EPAD | USBAT_UIO_1);
+	if (rc != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_ERROR;
 
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Determine if media is present.
+ */
+static int usbat_flash_check_media_present(unsigned char *uio)
+{
+	if (*uio & USBAT_UIO_UI0) {
+		US_DEBUGP("usbat_flash_check_media_present: no media detected\n");
+		return USBAT_FLASH_MEDIA_NONE;
+	}
+
+	return USBAT_FLASH_MEDIA_CF;
+}
+
+/*
+ * Determine if media has changed since last operation
+ */
+static int usbat_flash_check_media_changed(unsigned char *uio)
+{
+	if (*uio & USBAT_UIO_0) {
+		US_DEBUGP("usbat_flash_check_media_changed: media change detected\n");
+		return USBAT_FLASH_MEDIA_CHANGED;
+	}
+
+	return USBAT_FLASH_MEDIA_SAME;
+}
+
+/*
+ * Check for media change / no media and handle the situation appropriately
+ */
+static int usbat_flash_check_media(struct us_data *us,
+				   struct usbat_info *info)
+{
+	int rc;
+	unsigned char *uio = us->iobuf;
+
+	rc = usbat_read_user_io(us, uio);
+	if (rc != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	// Check for media existance
+	rc = usbat_flash_check_media_present(uio);
+	if (rc == USBAT_FLASH_MEDIA_NONE) {
+		info->sense_key = 0x02;
+		info->sense_asc = 0x3A;
+		info->sense_ascq = 0x00;
+		return USB_STOR_TRANSPORT_FAILED;
+	}
+
+	// Check for media change
+	rc = usbat_flash_check_media_changed(uio);
+	if (rc == USBAT_FLASH_MEDIA_CHANGED) {
+
+		// Reset and re-enable card detect
+		rc = usbat_device_reset(us);
+		if (rc != USB_STOR_TRANSPORT_GOOD)
+			return rc;
+		rc = usbat_device_enable_cdt(us);
+		if (rc != USB_STOR_TRANSPORT_GOOD)
+			return rc;
+
+		msleep(50);
+
+		rc = usbat_read_user_io(us, uio);
+		if (rc != USB_STOR_XFER_GOOD)
+			return USB_STOR_TRANSPORT_ERROR;
+		
+		info->sense_key = UNIT_ATTENTION;
+		info->sense_asc = 0x28;
+		info->sense_ascq = 0x00;
+		return USB_STOR_TRANSPORT_FAILED;
+	}
+
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Determine whether we are controlling a flash-based reader/writer,
+ * or a HP8200-based CD drive.
+ * Sets transport functions as appropriate.
+ */
+static int usbat_identify_device(struct us_data *us,
+				 struct usbat_info *info)
+{
+	int rc;
+	unsigned char status;
+
+	if (!us || !info)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	rc = usbat_device_reset(us);
+	if (rc != USB_STOR_TRANSPORT_GOOD)
+		return rc;
+
+	/*
+	 * By examining the device signature after a reset, we can identify
+	 * whether the device supports the ATAPI packet interface.
+	 * The flash-devices do not support this, whereas the HP CDRW's obviously
+	 * do.
+	 *
+	 * This method is not ideal, but works because no other devices have been
+	 * produced based on the USBAT/USBAT02.
+	 *
+	 * Section 9.1 of the ATAPI-4 spec states (amongst other things) that
+	 * after a device reset, a Cylinder low of 0x14 indicates that the device
+	 * does support packet commands.
+	 */
+	rc = usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, &status);
+	if (rc != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	US_DEBUGP("usbat_identify_device: Cylinder low is %02X\n", status);
+
+	if (status == 0x14) {
+		// Device is HP 8200
+		US_DEBUGP("usbat_identify_device: Detected HP8200 CDRW\n");
+		info->devicetype = USBAT_DEV_HP8200;
+	} else {
+		// Device is a CompactFlash reader/writer
+		US_DEBUGP("usbat_identify_device: Detected Flash reader/writer\n");
+		info->devicetype = USBAT_DEV_FLASH;
+	}
+
+	return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Set the transport function based on the device type
+ */
+int usbat_set_transport(struct us_data *us,
+			struct usbat_info *info)
+{
+	int rc;
+
+	if (!info->devicetype) {
+		rc = usbat_identify_device(us, info);
+		if (rc != USB_STOR_TRANSPORT_GOOD) {
+			US_DEBUGP("usbat_set_transport: Could not identify device\n");
+			return 1;
+		}
+	}
+
+	if (usbat_get_device_type(us) == USBAT_DEV_HP8200)
+		us->transport = usbat_hp8200e_transport;
+	else if (usbat_get_device_type(us) == USBAT_DEV_FLASH)
+		us->transport = usbat_flash_transport;
+
+	return 0;
+}
+
+/*
+ * Read the media capacity
+ */
+static int usbat_flash_get_sector_count(struct us_data *us,
+					struct usbat_info *info)
+{
+	unsigned char registers[3] = {
+		USBAT_ATA_SECCNT,
+		USBAT_ATA_DEVICE,
+		USBAT_ATA_CMD,
+	};
+	unsigned char  command[3] = { 0x01, 0xA0, 0xEC };
+	unsigned char *reply;
+	unsigned char status;
+	int rc;
+
+	if (!us || !info)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	reply = kmalloc(512, GFP_NOIO);
+	if (!reply)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	// ATAPI command : IDENTIFY DEVICE
+	rc = usbat_multiple_write(us, registers, command, 3);
+	if (rc != USB_STOR_XFER_GOOD) {
+		US_DEBUGP("usbat_flash_get_sector_count: Gah! identify_device failed\n");
+		rc = USB_STOR_TRANSPORT_ERROR;
+		goto leave;
+	}
+
+	// Read device status
+	if (usbat_get_status(us, &status) != USB_STOR_XFER_GOOD) {
+		rc = USB_STOR_TRANSPORT_ERROR;
+		goto leave;
+	}
+
+	msleep(100);
+
+	// Read the device identification data
+	rc = usbat_read_block(us, reply, 512);
+	if (rc != USB_STOR_TRANSPORT_GOOD)
+		goto leave;
+
+	info->sectors = ((u32)(reply[117]) << 24) |
+		((u32)(reply[116]) << 16) |
+		((u32)(reply[115]) <<  8) |
+		((u32)(reply[114])      );
+
+	rc = USB_STOR_TRANSPORT_GOOD;
+
+ leave:
+	kfree(reply);
+	return rc;
+}
+
+/*
+ * Read data from device
+ */
+static int usbat_flash_read_data(struct us_data *us,
+								 struct usbat_info *info,
+								 u32 sector,
+								 u32 sectors)
+{
+	unsigned char registers[7] = {
+		USBAT_ATA_FEATURES,
+		USBAT_ATA_SECCNT,
+		USBAT_ATA_SECNUM,
+		USBAT_ATA_LBA_ME,
+		USBAT_ATA_LBA_HI,
+		USBAT_ATA_DEVICE,
+		USBAT_ATA_STATUS,
+	};
+	unsigned char command[7];
+	unsigned char *buffer;
+	unsigned char  thistime;
+	unsigned int totallen, alloclen;
+	int len, result;
+	unsigned int sg_idx = 0, sg_offset = 0;
+
+	result = usbat_flash_check_media(us, info);
+	if (result != USB_STOR_TRANSPORT_GOOD)
+		return result;
+
+	// we're working in LBA mode.  according to the ATA spec,
+	// we can support up to 28-bit addressing.  I don't know if Jumpshot
+	// supports beyond 24-bit addressing.  It's kind of hard to test
+	// since it requires > 8GB CF card.
+
+	if (sector > 0x0FFFFFFF)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	totallen = sectors * info->ssize;
+
+	// Since we don't read more than 64 KB at a time, we have to create
+	// a bounce buffer and move the data a piece at a time between the
+	// bounce buffer and the actual transfer buffer.
+
+	alloclen = min(totallen, 65536u);
+	buffer = kmalloc(alloclen, GFP_NOIO);
+	if (buffer == NULL)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	do {
+		// loop, never allocate or transfer more than 64k at once
+		// (min(128k, 255*info->ssize) is the real limit)
+		len = min(totallen, alloclen);
+		thistime = (len / info->ssize) & 0xff;
+ 
+		// ATAPI command 0x20 (READ SECTORS)
+		usbat_pack_atapi_sector_cmd(command, thistime, sector, 0x20);
+
+		// Write/execute ATAPI read command
+		result = usbat_multiple_write(us, registers, command, 7);
+		if (result != USB_STOR_TRANSPORT_GOOD)
+			goto leave;
+
+		// Read the data we just requested
+		result = usbat_read_blocks(us, buffer, len);
+		if (result != USB_STOR_TRANSPORT_GOOD)
+			goto leave;
+  	 
+		US_DEBUGP("usbat_flash_read_data:  %d bytes\n", len);
+	
+		// Store the data in the transfer buffer
+		usb_stor_access_xfer_buf(buffer, len, us->srb,
+					 &sg_idx, &sg_offset, TO_XFER_BUF);
+
+		sector += thistime;
+		totallen -= len;
+	} while (totallen > 0);
+
+	kfree(buffer);
+	return USB_STOR_TRANSPORT_GOOD;
+
+leave:
+	kfree(buffer);
+	return USB_STOR_TRANSPORT_ERROR;
+}
+
+/*
+ * Write data to device
+ */
+static int usbat_flash_write_data(struct us_data *us,
+								  struct usbat_info *info,
+								  u32 sector,
+								  u32 sectors)
+{
+	unsigned char registers[7] = {
+		USBAT_ATA_FEATURES,
+		USBAT_ATA_SECCNT,
+		USBAT_ATA_SECNUM,
+		USBAT_ATA_LBA_ME,
+		USBAT_ATA_LBA_HI,
+		USBAT_ATA_DEVICE,
+		USBAT_ATA_STATUS,
+	};
+	unsigned char command[7];
+	unsigned char *buffer;
+	unsigned char  thistime;
+	unsigned int totallen, alloclen;
+	int len, result;
+	unsigned int sg_idx = 0, sg_offset = 0;
+
+	result = usbat_flash_check_media(us, info);
+	if (result != USB_STOR_TRANSPORT_GOOD)
+		return result;
+
+	// we're working in LBA mode.  according to the ATA spec,
+	// we can support up to 28-bit addressing.  I don't know if Jumpshot
+	// supports beyond 24-bit addressing.  It's kind of hard to test
+	// since it requires > 8GB CF card.
+
+	if (sector > 0x0FFFFFFF)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	totallen = sectors * info->ssize;
+
+	// Since we don't write more than 64 KB at a time, we have to create
+	// a bounce buffer and move the data a piece at a time between the
+	// bounce buffer and the actual transfer buffer.
+
+	alloclen = min(totallen, 65536u);
+	buffer = kmalloc(alloclen, GFP_NOIO);
+	if (buffer == NULL)
+		return USB_STOR_TRANSPORT_ERROR;
+
+	do {
+		// loop, never allocate or transfer more than 64k at once
+		// (min(128k, 255*info->ssize) is the real limit)
+		len = min(totallen, alloclen);
+		thistime = (len / info->ssize) & 0xff;
+
+		// Get the data from the transfer buffer
+		usb_stor_access_xfer_buf(buffer, len, us->srb,
+					 &sg_idx, &sg_offset, FROM_XFER_BUF);
+
+		// ATAPI command 0x30 (WRITE SECTORS)
+		usbat_pack_atapi_sector_cmd(command, thistime, sector, 0x30);		
+
+		// Write/execute ATAPI write command
+		result = usbat_multiple_write(us, registers, command, 7);
+		if (result != USB_STOR_TRANSPORT_GOOD)
+			goto leave;
+
+		// Write the data
+		result = usbat_write_blocks(us, buffer, len);
+		if (result != USB_STOR_TRANSPORT_GOOD)
+			goto leave;
+
+		sector += thistime;
+		totallen -= len;
+	} while (totallen > 0);
+
+	kfree(buffer);
 	return result;
+
+leave:
+	kfree(buffer);
+	return USB_STOR_TRANSPORT_ERROR;
 }
 
 /*
  * Squeeze a potentially huge (> 65535 byte) read10 command into
  * a little ( <= 65535 byte) ATAPI pipe
  */
-
-static int usbat_handle_read10(struct us_data *us,
-			       unsigned char *registers,
-			       unsigned char *data,
-			       struct scsi_cmnd *srb)
+static int usbat_hp8200e_handle_read10(struct us_data *us,
+				       unsigned char *registers,
+				       unsigned char *data,
+				       struct scsi_cmnd *srb)
 {
 	int result = USB_STOR_TRANSPORT_GOOD;
 	unsigned char *buffer;
@@ -538,9 +1136,10 @@ static int usbat_handle_read10(struct us
 
 	if (srb->request_bufflen < 0x10000) {
 
-		result = usbat_rw_block_test(us, USBAT_ATA, 
+		result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, 
 			registers, data, 19,
-			0x10, 0x17, 0xFD, 0x30,
+			USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD,
+			(USBAT_QUAL_FCQ | USBAT_QUAL_ALQ),
 			DMA_FROM_DEVICE,
 			srb->request_buffer, 
 			srb->request_bufflen, srb->use_sg, 1);
@@ -607,9 +1206,10 @@ static int usbat_handle_read10(struct us
 		data[7+7] = MSB_of(len / srb->transfersize); // SCSI command
 		data[7+8] = LSB_of(len / srb->transfersize); // num sectors
 
-		result = usbat_rw_block_test(us, USBAT_ATA, 
+		result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, 
 			registers, data, 19,
-			0x10, 0x17, 0xFD, 0x30,
+			USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD, 
+			(USBAT_QUAL_FCQ | USBAT_QUAL_ALQ),
 			DMA_FROM_DEVICE,
 			buffer,
 			len, 0, 1);
@@ -632,48 +1232,52 @@ static int usbat_handle_read10(struct us
 	return result;
 }
 
-static int hp_8200e_select_and_test_registers(struct us_data *us)
+static int usbat_select_and_test_registers(struct us_data *us)
 {
 	int selector;
 	unsigned char *status = us->iobuf;
+	unsigned char max_selector = 0xB0;
+	if (usbat_get_device_type(us) == USBAT_DEV_FLASH)
+		max_selector = 0xA0;
 
 	// try device = master, then device = slave.
 
-	for (selector = 0xA0; selector <= 0xB0; selector += 0x10) {
+	for (selector = 0xA0; selector <= max_selector; selector += 0x10) {
 
-		if (usbat_write(us, USBAT_ATA, 0x16, selector) != 
+		if (usbat_get_device_type(us) == USBAT_DEV_HP8200 &&
+			usbat_write(us, USBAT_ATA, USBAT_ATA_DEVICE, selector) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 
-		if (usbat_read(us, USBAT_ATA, 0x17, status) != 
+		if (usbat_read(us, USBAT_ATA, USBAT_ATA_STATUS, status) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 
-		if (usbat_read(us, USBAT_ATA, 0x16, status) != 
+		if (usbat_read(us, USBAT_ATA, USBAT_ATA_DEVICE, status) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 
-		if (usbat_read(us, USBAT_ATA, 0x14, status) != 
+		if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 
-		if (usbat_read(us, USBAT_ATA, 0x15, status) != 
+		if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_HI, status) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 
-		if (usbat_write(us, USBAT_ATA, 0x14, 0x55) != 
+		if (usbat_write(us, USBAT_ATA, USBAT_ATA_LBA_ME, 0x55) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 
-		if (usbat_write(us, USBAT_ATA, 0x15, 0xAA) != 
+		if (usbat_write(us, USBAT_ATA, USBAT_ATA_LBA_HI, 0xAA) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 
-		if (usbat_read(us, USBAT_ATA, 0x14, status) != 
+		if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 
-		if (usbat_read(us, USBAT_ATA, 0x15, status) != 
+		if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != 
 				USB_STOR_XFER_GOOD)
 			return USB_STOR_TRANSPORT_ERROR;
 	}
@@ -681,125 +1285,131 @@ static int hp_8200e_select_and_test_regi
 	return USB_STOR_TRANSPORT_GOOD;
 }
 
-int init_8200e(struct us_data *us)
+/*
+ * Initialize the USBAT processor and the storage device
+ */
+int init_usbat(struct us_data *us)
 {
-	int result;
+	int rc;
+	struct usbat_info *info;
+	unsigned char subcountH = USBAT_ATA_LBA_HI;
+	unsigned char subcountL = USBAT_ATA_LBA_ME;
 	unsigned char *status = us->iobuf;
 
-	// Enable peripheral control signals
+	us->extra = kmalloc(sizeof(struct usbat_info), GFP_NOIO);
+	if (!us->extra) {
+		US_DEBUGP("init_usbat: Gah! Can't allocate storage for usbat info struct!\n");
+		return 1;
+	}
+	memset(us->extra, 0, sizeof(struct usbat_info));
+	info = (struct usbat_info *) (us->extra);
 
-	if (usbat_write_user_io(us,
-	  USBAT_UIO_OE1 | USBAT_UIO_OE0,
-	  USBAT_UIO_EPAD | USBAT_UIO_1) != USB_STOR_XFER_GOOD)
+	// Enable peripheral control signals
+	rc = usbat_write_user_io(us,
+				 USBAT_UIO_OE1 | USBAT_UIO_OE0,
+				 USBAT_UIO_EPAD | USBAT_UIO_1);
+	if (rc != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
 	US_DEBUGP("INIT 1\n");
 
 	msleep(2000);
 
-	if (usbat_read_user_io(us, status) !=
-			USB_STOR_XFER_GOOD)
-		return USB_STOR_TRANSPORT_ERROR;
+	rc = usbat_read_user_io(us, status);
+	if (rc != USB_STOR_TRANSPORT_GOOD)
+		return rc;
 
 	US_DEBUGP("INIT 2\n");
 
-	if (usbat_read_user_io(us, status) !=
-			USB_STOR_XFER_GOOD)
+	rc = usbat_read_user_io(us, status);
+	if (rc != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
-	US_DEBUGP("INIT 3\n");
-
-	// Reset peripheral, enable periph control signals
-	// (bring reset signal up)
-
-	if (usbat_write_user_io(us,
-	  USBAT_UIO_DRVRST | USBAT_UIO_OE1 | USBAT_UIO_OE0,
-	  USBAT_UIO_EPAD | USBAT_UIO_1) != USB_STOR_XFER_GOOD)
+	rc = usbat_read_user_io(us, status);
+	if (rc != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
-	US_DEBUGP("INIT 4\n");
-
-	// Enable periph control signals
-	// (bring reset signal down)
+	US_DEBUGP("INIT 3\n");
 
-	if (usbat_write_user_io(us,
-	  USBAT_UIO_OE1 | USBAT_UIO_OE0,
-	  USBAT_UIO_EPAD | USBAT_UIO_1) != USB_STOR_XFER_GOOD)
+	// At this point, we need to detect which device we are using
+	if (usbat_set_transport(us, info))
 		return USB_STOR_TRANSPORT_ERROR;
 
-	US_DEBUGP("INIT 5\n");
+	US_DEBUGP("INIT 4\n");
 
-	msleep(250);
+	if (usbat_get_device_type(us) == USBAT_DEV_HP8200) {
+		msleep(250);
 
-	// Write 0x80 to ISA port 0x3F
+		// Write 0x80 to ISA port 0x3F
+		rc = usbat_write(us, USBAT_ISA, 0x3F, 0x80);
+		if (rc != USB_STOR_XFER_GOOD)
+			return USB_STOR_TRANSPORT_ERROR;
 
-	if (usbat_write(us, USBAT_ISA, 0x3F, 0x80) !=
-			USB_STOR_XFER_GOOD)
-		return USB_STOR_TRANSPORT_ERROR;
+		US_DEBUGP("INIT 5\n");
 
-	US_DEBUGP("INIT 6\n");
+		// Read ISA port 0x27
+		rc = usbat_read(us, USBAT_ISA, 0x27, status);
+		if (rc != USB_STOR_XFER_GOOD)
+			return USB_STOR_TRANSPORT_ERROR;
 
-	// Read ISA port 0x27
+		US_DEBUGP("INIT 6\n");
 
-	if (usbat_read(us, USBAT_ISA, 0x27, status) !=
-			USB_STOR_XFER_GOOD)
-		return USB_STOR_TRANSPORT_ERROR;
+		rc = usbat_read_user_io(us, status);
+		if (rc != USB_STOR_XFER_GOOD)
+			return USB_STOR_TRANSPORT_ERROR;
 
-	US_DEBUGP("INIT 7\n");
+		US_DEBUGP("INIT 7\n");
+	}
 
-	if (usbat_read_user_io(us, status) !=
-			USB_STOR_XFER_GOOD)
-		return USB_STOR_TRANSPORT_ERROR;
+	rc = usbat_select_and_test_registers(us);
+	if (rc != USB_STOR_TRANSPORT_GOOD)
+		return rc;
 
 	US_DEBUGP("INIT 8\n");
 
-	if ( (result = hp_8200e_select_and_test_registers(us)) !=
-			 USB_STOR_TRANSPORT_GOOD)
-		return result;
+	rc = usbat_read_user_io(us, status);
+	if (rc != USB_STOR_XFER_GOOD)
+		return USB_STOR_TRANSPORT_ERROR;
 
 	US_DEBUGP("INIT 9\n");
 
-	if (usbat_read_user_io(us, status) !=
-			USB_STOR_XFER_GOOD)
-		return USB_STOR_TRANSPORT_ERROR;
+	// Enable peripheral control signals and card detect
+	rc = usbat_device_enable_cdt(us);
+	if (rc != USB_STOR_TRANSPORT_GOOD)
+		return rc;
 
 	US_DEBUGP("INIT 10\n");
 
-	// Enable periph control signals and card detect
-
-	if (usbat_write_user_io(us,
-	  USBAT_UIO_ACKD |USBAT_UIO_OE1 | USBAT_UIO_OE0,
-	  USBAT_UIO_EPAD | USBAT_UIO_1) != USB_STOR_XFER_GOOD)
+	rc = usbat_read_user_io(us, status);
+	if (rc != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
 	US_DEBUGP("INIT 11\n");
 
-	if (usbat_read_user_io(us, status) !=
-			USB_STOR_XFER_GOOD)
-		return USB_STOR_TRANSPORT_ERROR;
-
-	US_DEBUGP("INIT 12\n");
-
 	msleep(1400);
 
-	if (usbat_read_user_io(us, status) !=
-			USB_STOR_XFER_GOOD)
+	rc = usbat_read_user_io(us, status);
+	if (rc != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
-	US_DEBUGP("INIT 13\n");
+	US_DEBUGP("INIT 12\n");
 
-	if ( (result = hp_8200e_select_and_test_registers(us)) !=
-			 USB_STOR_TRANSPORT_GOOD)
-		return result;
+	rc = usbat_select_and_test_registers(us);
+	if (rc != USB_STOR_TRANSPORT_GOOD)
+		return rc;
 
-	US_DEBUGP("INIT 14\n");
+	US_DEBUGP("INIT 13\n");
 
-	if (usbat_set_shuttle_features(us, 
-			0x83, 0x00, 0x88, 0x08, 0x15, 0x14) !=
-			 USB_STOR_XFER_GOOD)
+	if (usbat_get_device_type(us) == USBAT_DEV_FLASH) { 
+		subcountH = 0x02;
+		subcountL = 0x00;
+	}
+	rc = usbat_set_shuttle_features(us, (USBAT_FEAT_ETEN | USBAT_FEAT_ET2 | USBAT_FEAT_ET1),
+									0x00, 0x88, 0x08, subcountH, subcountL);
+	if (rc != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
 
-	US_DEBUGP("INIT 15\n");
+	US_DEBUGP("INIT 14\n");
 
 	return USB_STOR_TRANSPORT_GOOD;
 }
@@ -807,7 +1417,7 @@ int init_8200e(struct us_data *us)
 /*
  * Transport for the HP 8200e
  */
-int hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us)
+int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us)
 {
 	int result;
 	unsigned char *status = us->iobuf;
@@ -824,13 +1434,13 @@ int hp8200e_transport(struct scsi_cmnd *
 	   commands... just ATA Packet Commands.
  	 */
 
-	registers[0] = 0x11;
-	registers[1] = 0x12;
-	registers[2] = 0x13;
-	registers[3] = 0x14;
-	registers[4] = 0x15;
-	registers[5] = 0x16;
-	registers[6] = 0x17;
+	registers[0] = USBAT_ATA_FEATURES;
+	registers[1] = USBAT_ATA_SECCNT;
+	registers[2] = USBAT_ATA_SECNUM;
+	registers[3] = USBAT_ATA_LBA_ME;
+	registers[4] = USBAT_ATA_LBA_HI;
+	registers[5] = USBAT_ATA_DEVICE;
+	registers[6] = USBAT_ATA_CMD;
 	data[0] = 0x00;
 	data[1] = 0x00;
 	data[2] = 0x00;
@@ -844,7 +1454,7 @@ int hp8200e_transport(struct scsi_cmnd *
 		data[i] = (i-7 >= srb->cmd_len) ? 0 : srb->cmnd[i-7];
 	}
 
-	result = usbat_read(us, USBAT_ATA, 0x17, status);
+	result = usbat_get_status(us, status);
 	US_DEBUGP("Status = %02X\n", *status);
 	if (result != USB_STOR_XFER_GOOD)
 		return USB_STOR_TRANSPORT_ERROR;
@@ -853,9 +1463,10 @@ int hp8200e_transport(struct scsi_cmnd *
 
 	if (srb->sc_data_direction == DMA_TO_DEVICE) {
 
-		result = usbat_rw_block_test(us, USBAT_ATA, 
+		result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, 
 			registers, data, 19,
-			0x10, 0x17, 0xFD, 0x30,
+			USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD,
+			(USBAT_QUAL_FCQ | USBAT_QUAL_ALQ),
 			DMA_TO_DEVICE,
 			srb->request_buffer, 
 			len, srb->use_sg, 10);
@@ -870,7 +1481,7 @@ int hp8200e_transport(struct scsi_cmnd *
 	} else if (srb->cmnd[0] == READ_10 ||
 		   srb->cmnd[0] == GPCMD_READ_CD) {
 
-		return usbat_handle_read10(us, registers, data, srb);
+		return usbat_hp8200e_handle_read10(us, registers, data, srb);
 
 	}
 
@@ -881,7 +1492,6 @@ int hp8200e_transport(struct scsi_cmnd *
 	}
 
 	if ( (result = usbat_multiple_write(us, 
-			USBAT_ATA,
 			registers, data, 7)) != USB_STOR_TRANSPORT_GOOD) {
 		return result;
 	}
@@ -895,7 +1505,7 @@ int hp8200e_transport(struct scsi_cmnd *
 	// AT SPEED 4 IS UNRELIABLE!!!
 
 	if ( (result = usbat_write_block(us, 
-			USBAT_ATA, 0x10, srb->cmnd, 12, 0,
+			USBAT_ATA, srb->cmnd, 12,
 			srb->cmnd[0]==GPCMD_BLANK ? 75 : 10)) !=
 				USB_STOR_TRANSPORT_GOOD) {
 		return result;
@@ -908,14 +1518,14 @@ int hp8200e_transport(struct scsi_cmnd *
 
 		// How many bytes to read in? Check cylL register
 
-		if (usbat_read(us, USBAT_ATA, 0x14, status) != 
+		if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != 
 		    	USB_STOR_XFER_GOOD) {
 			return USB_STOR_TRANSPORT_ERROR;
 		}
 
 		if (len > 0xFF) { // need to read cylH also
 			len = *status;
-			if (usbat_read(us, USBAT_ATA, 0x15, status) !=
+			if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_HI, status) !=
 				    USB_STOR_XFER_GOOD) {
 				return USB_STOR_TRANSPORT_ERROR;
 			}
@@ -925,8 +1535,7 @@ int hp8200e_transport(struct scsi_cmnd *
 			len = *status;
 
 
-		result = usbat_read_block(us, USBAT_ATA, 0x10, 
-			srb->request_buffer, len, srb->use_sg);
+		result = usbat_read_block(us, srb->request_buffer, len);
 
 		/* Debug-print the first 32 bytes of the transfer */
 
@@ -948,4 +1557,153 @@ int hp8200e_transport(struct scsi_cmnd *
 	return result;
 }
 
+/*
+ * Transport for USBAT02-based CompactFlash and similar storage devices
+ */
+int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us)
+{
+	int rc;
+	struct usbat_info *info = (struct usbat_info *) (us->extra);
+	unsigned long block, blocks;
+	unsigned char *ptr = us->iobuf;
+	static unsigned char inquiry_response[36] = {
+		0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
+	};
+
+	if (srb->cmnd[0] == INQUIRY) {
+		US_DEBUGP("usbat_flash_transport: INQUIRY. Returning bogus response.\n");
+		memcpy(ptr, inquiry_response, sizeof(inquiry_response));
+		fill_inquiry_response(us, ptr, 36);
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	if (srb->cmnd[0] == READ_CAPACITY) {
+		rc = usbat_flash_check_media(us, info);
+		if (rc != USB_STOR_TRANSPORT_GOOD)
+			return rc;
+
+		rc = usbat_flash_get_sector_count(us, info);
+		if (rc != USB_STOR_TRANSPORT_GOOD)
+			return rc;
+
+		info->ssize = 0x200;  // hard coded 512 byte sectors as per ATA spec
+		US_DEBUGP("usbat_flash_transport: READ_CAPACITY: %ld sectors, %ld bytes per sector\n",
+			  info->sectors, info->ssize);
+
+		// build the reply
+		// note: must return the sector number of the last sector,
+		// *not* the total number of sectors
+		((__be32 *) ptr)[0] = cpu_to_be32(info->sectors - 1);
+		((__be32 *) ptr)[1] = cpu_to_be32(info->ssize);
+		usb_stor_set_xfer_buf(ptr, 8, srb);
+
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	if (srb->cmnd[0] == MODE_SELECT_10) {
+		US_DEBUGP("usbat_flash_transport:  Gah! MODE_SELECT_10.\n");
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
+	if (srb->cmnd[0] == READ_10) {
+		block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
+				((u32)(srb->cmnd[4]) <<  8) | ((u32)(srb->cmnd[5]));
+
+		blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
+
+		US_DEBUGP("usbat_flash_transport:  READ_10: read block 0x%04lx  count %ld\n", block, blocks);
+		return usbat_flash_read_data(us, info, block, blocks);
+	}
+
+	if (srb->cmnd[0] == READ_12) {
+		// I don't think we'll ever see a READ_12 but support it anyway...
+		block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
+		        ((u32)(srb->cmnd[4]) <<  8) | ((u32)(srb->cmnd[5]));
+
+		blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) |
+		         ((u32)(srb->cmnd[8]) <<  8) | ((u32)(srb->cmnd[9]));
+
+		US_DEBUGP("usbat_flash_transport: READ_12: read block 0x%04lx  count %ld\n", block, blocks);
+		return usbat_flash_read_data(us, info, block, blocks);
+	}
+
+	if (srb->cmnd[0] == WRITE_10) {
+		block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
+		        ((u32)(srb->cmnd[4]) <<  8) | ((u32)(srb->cmnd[5]));
+
+		blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
+
+		US_DEBUGP("usbat_flash_transport: WRITE_10: write block 0x%04lx  count %ld\n", block, blocks);
+		return usbat_flash_write_data(us, info, block, blocks);
+	}
+
+	if (srb->cmnd[0] == WRITE_12) {
+		// I don't think we'll ever see a WRITE_12 but support it anyway...
+		block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) |
+		        ((u32)(srb->cmnd[4]) <<  8) | ((u32)(srb->cmnd[5]));
+
+		blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) |
+		         ((u32)(srb->cmnd[8]) <<  8) | ((u32)(srb->cmnd[9]));
+
+		US_DEBUGP("usbat_flash_transport: WRITE_12: write block 0x%04lx  count %ld\n", block, blocks);
+		return usbat_flash_write_data(us, info, block, blocks);
+	}
+
+
+	if (srb->cmnd[0] == TEST_UNIT_READY) {
+		US_DEBUGP("usbat_flash_transport: TEST_UNIT_READY.\n");
+
+		rc = usbat_flash_check_media(us, info);
+		if (rc != USB_STOR_TRANSPORT_GOOD)
+			return rc;
+
+		return usbat_check_status(us);
+	}
+
+	if (srb->cmnd[0] == REQUEST_SENSE) {
+		US_DEBUGP("usbat_flash_transport: REQUEST_SENSE.\n");
+
+		memset(ptr, 0, 18);
+		ptr[0] = 0xF0;
+		ptr[2] = info->sense_key;
+		ptr[7] = 11;
+		ptr[12] = info->sense_asc;
+		ptr[13] = info->sense_ascq;
+		usb_stor_set_xfer_buf(ptr, 18, srb);
+
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
+		// sure.  whatever.  not like we can stop the user from popping
+		// the media out of the device (no locking doors, etc)
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+
+	US_DEBUGP("usbat_flash_transport: Gah! Unknown command: %d (0x%x)\n",
+			  srb->cmnd[0], srb->cmnd[0]);
+	info->sense_key = 0x05;
+	info->sense_asc = 0x20;
+	info->sense_ascq = 0x00;
+	return USB_STOR_TRANSPORT_FAILED;
+}
+
+/*
+ * Default transport function. Attempts to detect which transport function
+ * should be called, makes it the new default, and calls it.
+ *
+ * This function should never be called. Our usbat_init() function detects the
+ * device type and changes the us->transport ptr to the transport function
+ * relevant to the device.
+ * However, we'll support this impossible(?) case anyway.
+ */
+int usbat_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+	struct usbat_info *info = (struct usbat_info*) (us->extra);
+
+	if (usbat_set_transport(us, info))
+		return USB_STOR_TRANSPORT_ERROR;
+
+	return us->transport(srb, us);	
+}
 
diff -puN drivers/usb/storage/shuttle_usbat.h~bk-usb drivers/usb/storage/shuttle_usbat.h
--- 25/drivers/usb/storage/shuttle_usbat.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/shuttle_usbat.h	2005-02-09 19:12:30.000000000 -0800
@@ -5,8 +5,9 @@
  *
  * Current development and maintenance by:
  *   (c) 2000 Robert Baruch (autophile@dol.net)
+ *   (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
  *
- * See scm.c for more explanation
+ * See shuttle_usbat.c for more explanation
  *
  * 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
@@ -26,13 +27,59 @@
 #ifndef _USB_SHUTTLE_USBAT_H
 #define _USB_SHUTTLE_USBAT_H
 
+/* Supported device types */
+#define USBAT_DEV_HP8200	0x01
+#define USBAT_DEV_FLASH		0x02
+
 #define USBAT_EPP_PORT		0x10
 #define USBAT_EPP_REGISTER	0x30
 #define USBAT_ATA		0x40
 #define USBAT_ISA		0x50
 
-/* SCM User I/O Data registers */
+/* Commands (need to be logically OR'd with an access type */
+#define USBAT_CMD_READ_REG		0x00
+#define USBAT_CMD_WRITE_REG		0x01
+#define USBAT_CMD_READ_BLOCK	0x02
+#define USBAT_CMD_WRITE_BLOCK	0x03
+#define USBAT_CMD_COND_READ_BLOCK	0x04
+#define USBAT_CMD_COND_WRITE_BLOCK	0x05
+#define USBAT_CMD_WRITE_REGS	0x07
+
+/* Commands (these don't need an access type) */
+#define USBAT_CMD_EXEC_CMD	0x80
+#define USBAT_CMD_SET_FEAT	0x81
+#define USBAT_CMD_UIO		0x82
+
+/* Methods of accessing UIO register */
+#define USBAT_UIO_READ	1
+#define USBAT_UIO_WRITE	0
+
+/* Qualifier bits */
+#define USBAT_QUAL_FCQ	0x20 // full compare
+#define USBAT_QUAL_ALQ	0x10 // auto load subcount
+
+/* USBAT Flash Media status types */
+#define USBAT_FLASH_MEDIA_NONE	0
+#define USBAT_FLASH_MEDIA_CF	1
+
+/* USBAT Flash Media change types */
+#define USBAT_FLASH_MEDIA_SAME	0
+#define USBAT_FLASH_MEDIA_CHANGED	1
+
+/* USBAT ATA registers */
+#define USBAT_ATA_DATA      0x10  // read/write data (R/W)
+#define USBAT_ATA_FEATURES  0x11  // set features (W)
+#define USBAT_ATA_ERROR     0x11  // error (R)
+#define USBAT_ATA_SECCNT    0x12  // sector count (R/W)
+#define USBAT_ATA_SECNUM    0x13  // sector number (R/W)
+#define USBAT_ATA_LBA_ME    0x14  // cylinder low (R/W)
+#define USBAT_ATA_LBA_HI    0x15  // cylinder high (R/W)
+#define USBAT_ATA_DEVICE    0x16  // head/device selection (R/W)
+#define USBAT_ATA_STATUS    0x17  // device status (R)
+#define USBAT_ATA_CMD       0x17  // device command (W)
+#define USBAT_ATA_ALTSTATUS 0x0E  // status (no clear IRQ) (R)
 
+/* USBAT User I/O Data registers */
 #define USBAT_UIO_EPAD		0x80 // Enable Peripheral Control Signals
 #define USBAT_UIO_CDT		0x40 // Card Detect (Read Only)
 				     // CDT = ACKD & !UI1 & !UI0
@@ -43,8 +90,7 @@
 #define USBAT_UIO_UI0		0x02 // Input 0
 #define USBAT_UIO_INTR_ACK	0x01 // Interrupt (ATA & ISA)/Acknowledge (EPP)
 
-/* SCM User I/O Enable registers */
-
+/* USBAT User I/O Enable registers */
 #define USBAT_UIO_DRVRST	0x80 // Reset Peripheral
 #define USBAT_UIO_ACKD		0x40 // Enable Card Detect
 #define USBAT_UIO_OE1		0x20 // I/O 1 set=output/clr=input
@@ -52,8 +98,30 @@
 #define USBAT_UIO_OE0		0x10 // I/O 0 set=output/clr=input
 #define USBAT_UIO_ADPRST	0x01 // Reset SCM chip
 
-/* HP 8200e stuff */
-extern int hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us);
-extern int init_8200e(struct us_data *us);
+/* USBAT Features */
+#define USBAT_FEAT_ETEN	0x80 // External trigger enable
+#define USBAT_FEAT_U1	0x08
+#define USBAT_FEAT_U0	0x04
+#define USBAT_FEAT_ET1	0x02
+#define USBAT_FEAT_ET2	0x01
+
+/* Transport functions */
+int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us);
+int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us);
+
+extern int usbat_transport(struct scsi_cmnd *srb, struct us_data *us);
+extern int init_usbat(struct us_data *us);
+
+struct usbat_info {
+	int devicetype;
+
+	/* Used for Flash readers only */
+	unsigned long sectors;     // total sector count
+	unsigned long ssize;       // sector size in bytes
+
+	unsigned char sense_key;
+	unsigned long sense_asc;   // additional sense code
+	unsigned long sense_ascq;  // additional sense code qualifier
+};
 
 #endif
diff -puN drivers/usb/storage/transport.c~bk-usb drivers/usb/storage/transport.c
--- 25/drivers/usb/storage/transport.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/transport.c	2005-02-09 19:12:30.000000000 -0800
@@ -1055,19 +1055,30 @@ int usb_stor_Bulk_transport(struct scsi_
 	US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
 			le32_to_cpu(bcs->Signature), bcs->Tag, 
 			residue, bcs->Status);
-	if ((bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) &&
-		    bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) ||
-			bcs->Tag != srb->serial_number || 
-			bcs->Status > US_BULK_STAT_PHASE) {
+	if (bcs->Tag != srb->serial_number || bcs->Status > US_BULK_STAT_PHASE) {
 		US_DEBUGP("Bulk logical error\n");
 		return USB_STOR_TRANSPORT_ERROR;
 	}
 
+	/* Some broken devices report odd signatures, so we do not check them
+	 * for validity against the spec. We store the first one we see,
+	 * and check subsequent transfers for validity against this signature.
+	 */
+	if (!us->bcs_signature) {
+		us->bcs_signature = bcs->Signature;
+		US_DEBUGP("Learnt BCS signature 0x%08X\n",
+			  le32_to_cpu(us->bcs_signature));
+	} else if (bcs->Signature != us->bcs_signature) {
+		US_DEBUGP("Signature mismatch: device sent %08X, expecting %08X",
+			  le32_to_cpu(bcs->Signature),
+			  le32_to_cpu(us->bcs_signature));
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
 	/* try to compute the actual residue, based on how much data
 	 * was really transferred and what the device tells us */
 	if (residue) {
-		if (!(us->flags & US_FL_IGNORE_RESIDUE) ||
-				srb->sc_data_direction == DMA_TO_DEVICE) {
+		if (!(us->flags & US_FL_IGNORE_RESIDUE)) {
 			residue = min(residue, transfer_length);
 			srb->resid = max(srb->resid, (int) residue);
 		}
diff -puN drivers/usb/storage/transport.h~bk-usb drivers/usb/storage/transport.h
--- 25/drivers/usb/storage/transport.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/transport.h	2005-02-09 19:12:30.000000000 -0800
@@ -52,7 +52,7 @@ struct scsi_cmnd;
 #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 */
-#ifdef CONFIG_USB_STORAGE_HP8200e
+#ifdef CONFIG_USB_STORAGE_USBAT
 #define US_PR_SCM_ATAPI	0x80		/* SCM-ATAPI bridge */
 #endif
 #ifdef CONFIG_USB_STORAGE_SDDR09
@@ -107,9 +107,6 @@ struct bulk_cs_wrap {
 };
 
 #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
diff -puN drivers/usb/storage/unusual_devs.h~bk-usb drivers/usb/storage/unusual_devs.h
--- 25/drivers/usb/storage/unusual_devs.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/unusual_devs.h	2005-02-09 19:12:30.000000000 -0800
@@ -59,16 +59,16 @@ UNUSUAL_DEV(  0x03f0, 0x0107, 0x0200, 0x
 		"CD-Writer+",
 		US_SC_8070, US_PR_CB, NULL, 0), 
 
-#ifdef CONFIG_USB_STORAGE_HP8200e
+#ifdef CONFIG_USB_STORAGE_USBAT
 UNUSUAL_DEV(  0x03f0, 0x0207, 0x0001, 0x0001, 
 		"HP",
 		"CD-Writer+ 8200e",
-		US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), 
+		US_SC_8070, US_PR_SCM_ATAPI, init_usbat, 0), 
 
 UNUSUAL_DEV(  0x03f0, 0x0307, 0x0001, 0x0001, 
 		"HP",
 		"CD-Writer+ CD-4e",
-		US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), 
+		US_SC_8070, US_PR_SCM_ATAPI, init_usbat, 0), 
 #endif
 
 /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
@@ -171,15 +171,12 @@ UNUSUAL_DEV(  0x04da, 0x0d05, 0x0000, 0x
 		"CD-R/RW Drive",
 		US_SC_8070, US_PR_CB, NULL, 0),
 
-/* Reported by Adriaan Penning <a.penning@luon.net>
- * Note that these cameras report "Medium not present" after
- * ALLOW_MEDIUM_REMOVAL, so they also need to be marked
- * NOT_LOCKABLE in the SCSI blacklist (and the vendor is MATSHITA). */
+/* Reported by Adriaan Penning <a.penning@luon.net> */
 UNUSUAL_DEV(  0x04da, 0x2372, 0x0000, 0x9999,
 		"Panasonic",
 		"DMC-LCx Camera",
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
-		US_FL_FIX_CAPACITY ),
+		US_FL_FIX_CAPACITY | US_FL_NOT_LOCKABLE ),
 
 /* Most of the following entries were developed with the help of
  * Shuttle/SCM directly.
@@ -268,6 +265,14 @@ UNUSUAL_DEV(  0x04fc, 0x80c2, 0x0100, 0x
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_BULK32),
 
+#ifdef CONFIG_USB_STORAGE_USBAT
+UNUSUAL_DEV(  0x04e6, 0x1010, 0x0000, 0x9999,
+		"SCM",
+		"SCM USBAT-02",
+		US_SC_SCSI, US_PR_SCM_ATAPI, init_usbat,
+		US_FL_SINGLE_LUN),
+#endif
+
 /* Reported by Bob Sass <rls@vectordb.com> -- only rev 1.33 tested */
 UNUSUAL_DEV(  0x050d, 0x0115, 0x0133, 0x0133,
 		"Belkin",
@@ -324,12 +329,11 @@ UNUSUAL_DEV(  0x052b, 0x1911, 0x0100, 0x
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_IGNORE_RESIDUE ),
 
-/* This entry is needed because the device reports Sub=ff */
 UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0450, 
 		"Sony",
 		"DSC-S30/S70/S75/505V/F505/F707/F717/P8", 
 		US_SC_SCSI, US_PR_DEVICE, NULL,
-		US_FL_SINGLE_LUN ),
+		US_FL_SINGLE_LUN | US_FL_NOT_LOCKABLE ),
 
 /* This entry is needed because the device reports Sub=ff */
 UNUSUAL_DEV(  0x054c, 0x0010, 0x0500, 0x0500, 
@@ -582,12 +586,18 @@ 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 ),
+
+#ifdef CONFIG_USB_STORAGE_USBAT
+UNUSUAL_DEV(  0x0781, 0x0005, 0x0005, 0x0005,
+		"Sandisk",
+		"ImageMate SDDR-05b",
+		US_SC_SCSI, US_PR_SCM_ATAPI, init_usbat,
+		US_FL_SINGLE_LUN),
 #endif
 
 UNUSUAL_DEV(  0x0781, 0x0100, 0x0100, 0x0100,
@@ -866,6 +876,13 @@ UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_IGNORE_RESIDUE ),
 
+/* Reported by Ian McConnell <ian at emit.demon.co.uk> */
+UNUSUAL_DEV(  0x0dda, 0x0301, 0x0012, 0x0012,
+		"PNP_MP3",
+		"PNP_MP3 PLAYER",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_IGNORE_RESIDUE ),
+
 /* Submitted by Antoine Mairesse <antoine.mairesse@free.fr> */
 UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300,
 		"USB",
diff -puN drivers/usb/storage/usb.c~bk-usb drivers/usb/storage/usb.c
--- 25/drivers/usb/storage/usb.c~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/usb.c	2005-02-09 19:12:30.000000000 -0800
@@ -63,7 +63,7 @@
 #include "debug.h"
 #include "initializers.h"
 
-#ifdef CONFIG_USB_STORAGE_HP8200e
+#ifdef CONFIG_USB_STORAGE_USBAT
 #include "shuttle_usbat.h"
 #endif
 #ifdef CONFIG_USB_STORAGE_SDDR09
@@ -144,9 +144,7 @@ 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 */
 	{ }
@@ -220,10 +218,8 @@ 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 */
 	{ NULL }
@@ -572,10 +568,10 @@ static int get_transport(struct us_data 
 		us->transport_reset = usb_stor_Bulk_reset;
 		break;
 
-#ifdef CONFIG_USB_STORAGE_HP8200e
+#ifdef CONFIG_USB_STORAGE_USBAT
 	case US_PR_SCM_ATAPI:
 		us->transport_name = "SCM/ATAPI";
-		us->transport = hp8200e_transport;
+		us->transport = usbat_transport;
 		us->transport_reset = usb_stor_CB_reset;
 		us->max_lun = 1;
 		break;
diff -puN drivers/usb/storage/usb.h~bk-usb drivers/usb/storage/usb.h
--- 25/drivers/usb/storage/usb.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/drivers/usb/storage/usb.h	2005-02-09 19:12:30.000000000 -0800
@@ -75,6 +75,7 @@ struct us_unusual_dev {
 #define US_FL_FIX_CAPACITY    0x00000080 /* READ CAPACITY response too big  */
 #define US_FL_IGNORE_RESIDUE  0x00000100 /* reported residue is wrong	    */
 #define US_FL_BULK32          0x00000200 /* Uses 32-byte CBW length         */
+#define US_FL_NOT_LOCKABLE    0x00000400 /* PREVENT/ALLOW not supported     */
 
 /* Dynamic flag definitions: used in set_bit() etc. */
 #define US_FLIDX_URB_ACTIVE	18  /* 0x00040000  current_urb is in use  */
@@ -126,6 +127,7 @@ struct us_data {
 	char			serial[USB_STOR_STRING_LEN];
 	char			*transport_name;
 	char			*protocol_name;
+	__le32			bcs_signature;
 	u8			subclass;
 	u8			protocol;
 	u8			max_lun;
diff -puN /dev/null include/linux/usb_cdc.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/include/linux/usb_cdc.h	2005-02-09 19:12:30.000000000 -0800
@@ -0,0 +1,162 @@
+/*
+ * USB Communications Device Class (CDC) definitions
+ *
+ * CDC says how to talk to lots of different types of network adapters,
+ * notably ethernet adapters and various modems.  It's used mostly with
+ * firmware based USB peripherals.
+ */
+
+#define USB_CDC_SUBCLASS_ACM			2
+#define USB_CDC_SUBCLASS_ETHERNET		6
+
+#define USB_CDC_PROTO_NONE			0
+
+#define USB_CDC_ACM_PROTO_AT_V25TER		1
+#define USB_CDC_ACM_PROTO_AT_PCCA101		2
+#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE	3
+#define USB_CDC_ACM_PROTO_AT_GSM		4
+#define USB_CDC_ACM_PROTO_AT_3G			5
+#define USB_CDC_ACM_PROTO_AT_CDMA		6
+#define USB_CDC_ACM_PROTO_VENDOR		0xff
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Class-Specific descriptors ... there are a couple dozen of them
+ */
+
+#define USB_CDC_HEADER_TYPE		0x00		/* header_desc */
+#define USB_CDC_CALL_MANAGEMENT_TYPE	0x01		/* call_mgmt_descriptor */
+#define USB_CDC_ACM_TYPE		0x02		/* acm_descriptor */
+#define USB_CDC_UNION_TYPE		0x06		/* union_desc */
+#define USB_CDC_COUNTRY_TYPE		0x07
+#define USB_CDC_ETHERNET_TYPE		0x0f		/* ether_desc */
+
+/* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
+struct usb_cdc_header_desc {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	bDescriptorSubType;
+
+	__le16	bcdCDC;
+} __attribute__ ((packed));
+
+/* "Call Management Descriptor" from CDC spec  5.2.3.2 */
+struct usb_cdc_call_mgmt_descriptor {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	bDescriptorSubType;
+
+	__u8	bmCapabilities;
+#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT		0x01
+#define USB_CDC_CALL_MGMT_CAP_DATA_INTF		0x02
+
+	__u8	bDataInterface;
+} __attribute__ ((packed));
+
+/* "Abstract Control Management Descriptor" from CDC spec  5.2.3.3 */
+struct usb_cdc_acm_descriptor {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	bDescriptorSubType;
+
+	__u8	bmCapabilities;
+} __attribute__ ((packed));
+
+/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */
+struct usb_cdc_union_desc {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	bDescriptorSubType;
+
+	__u8	bMasterInterface0;
+	__u8	bSlaveInterface0;
+	/* ... and there could be other slave interfaces */
+} __attribute__ ((packed));
+
+/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
+struct usb_cdc_ether_desc {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	bDescriptorSubType;
+
+	__u8	iMACAddress;
+	__le32	bmEthernetStatistics;
+	__le16	wMaxSegmentSize;
+	__le16	wNumberMCFilters;
+	__u8	bNumberPowerFilters;
+} __attribute__ ((packed));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Class-Specific Control Requests (6.2)
+ *
+ * section 3.6.2.1 table 4 has the ACM profile, for modems.
+ * section 3.8.2 table 10 has the ethernet profile.
+ *
+ * Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant,
+ * heavily dependent on the encapsulated (proprietary) command mechanism.
+ */
+
+#define USB_CDC_SEND_ENCAPSULATED_COMMAND	0x00
+#define USB_CDC_GET_ENCAPSULATED_RESPONSE	0x01
+#define USB_CDC_REQ_SET_LINE_CODING		0x20
+#define USB_CDC_REQ_GET_LINE_CODING		0x21
+#define USB_CDC_REQ_SET_CONTROL_LINE_STATE	0x22
+#define USB_CDC_REQ_SEND_BREAK			0x23
+#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS	0x40
+#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER	0x41
+#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER	0x42
+#define USB_CDC_SET_ETHERNET_PACKET_FILTER	0x43
+#define USB_CDC_GET_ETHERNET_STATISTIC		0x44
+
+/* Line Coding Structure from CDC spec 6.2.13 */
+struct usb_cdc_line_coding {
+	__le32	dwDTERate;
+	__u8	bCharFormat;
+#define USB_CDC_1_STOP_BITS			0
+#define USB_CDC_1_5_STOP_BITS			1
+#define USB_CDC_2_STOP_BITS			2
+
+	__u8	bParityType;
+#define USB_CDC_NO_PARITY			0
+#define USB_CDC_ODD_PARITY			1
+#define USB_CDC_EVEN_PARITY			2
+#define USB_CDC_MARK_PARITY			3
+#define USB_CDC_SPACE_PARITY			4
+
+	__u8	bDataBits;
+} __attribute__ ((packed));
+
+/* table 62; bits in multicast filter */
+#define	USB_CDC_PACKET_TYPE_PROMISCUOUS		(1 << 0)
+#define	USB_CDC_PACKET_TYPE_ALL_MULTICAST	(1 << 1) /* no filter */
+#define	USB_CDC_PACKET_TYPE_DIRECTED		(1 << 2)
+#define	USB_CDC_PACKET_TYPE_BROADCAST		(1 << 3)
+#define	USB_CDC_PACKET_TYPE_MULTICAST		(1 << 4) /* filtered */
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Class-Specific Notifications (6.3) sent by interrupt transfers
+ *
+ * section 3.8.2 table 11 of the CDC spec lists Ethernet notifications
+ * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS
+ * RNDIS also defines its own bit-incompatible notifications
+ */
+
+#define USB_CDC_NOTIFY_NETWORK_CONNECTION	0x00
+#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE	0x01
+#define USB_CDC_NOTIFY_SERIAL_STATE		0x20
+#define USB_CDC_NOTIFY_SPEED_CHANGE		0x2a
+
+struct usb_cdc_notification {
+	__u8	bmRequestType;
+	__u8	bNotificationType;
+	__le16	wValue;
+	__le16	wIndex;
+	__le16	wLength;
+} __attribute__ ((packed));
+
diff -puN include/linux/usb.h~bk-usb include/linux/usb.h
--- 25/include/linux/usb.h~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/include/linux/usb.h	2005-02-09 19:12:30.000000000 -0800
@@ -285,6 +285,10 @@ struct usb_bus {
 
 	struct class_device class_dev;	/* class device for this bus */
 	void (*release)(struct usb_bus *bus);	/* function to destroy this bus's memory */
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+	struct mon_bus *mon_bus;	/* non-null when associated */
+	int monitored;			/* non-zero when monitored */
+#endif
 };
 #define	to_usb_bus(d) container_of(d, struct usb_bus, class_dev)
 
diff -puN MAINTAINERS~bk-usb MAINTAINERS
--- 25/MAINTAINERS~bk-usb	2005-02-09 19:12:30.000000000 -0800
+++ 25-akpm/MAINTAINERS	2005-02-09 19:12:30.000000000 -0800
@@ -2051,6 +2051,12 @@ M:	thomas@winischhofer.net
 W:	http://www.winischhofer.net/linuxsisvga.shtml
 S:	Maintained	
 
+SIS USB2VGA DRIVER
+P:	Thomas Winischhofer
+M:	thomas@winischhofer.net
+W:	http://www.winischhofer.at/linuxsisusbvga.shtml
+S:	Maintained
+
 SMSC47M1 HARDWARE MONITOR DRIVER
 P:	Jean Delvare
 M:	khali@linux-fr.org
_
