Parent repository is bk://kernel.bkbits.net/gregkh/linux/usb-2.6
======== ChangeSet 1.1593 ========
D 1.1593 04/02/28 17:54:52-08:00 akpm@mnm.(none) 37810 37809 0/0/1
P ChangeSet
C Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6
C into mnm.(none):/usr/src/bk-usb
------------------------------------------------

diff -Nru a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl
--- a/Documentation/DocBook/gadget.tmpl	Sun Feb 29 13:05:40 2004
+++ b/Documentation/DocBook/gadget.tmpl	Sun Feb 29 13:05:40 2004
@@ -454,6 +454,7 @@
 </para>
 
 !Edrivers/usb/gadget/usbstring.c
+!Edrivers/usb/gadget/config.c
 </sect1>
 
 </chapter>
diff -Nru a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt
--- a/Documentation/usb/error-codes.txt	Sun Feb 29 13:05:40 2004
+++ b/Documentation/usb/error-codes.txt	Sun Feb 29 13:05:40 2004
@@ -70,7 +70,9 @@
 			(That is, if drivers see this it's a bug.)
 
 -EPROTO (*)		a) bitstuff error
-			b) unknown USB error 
+			b) no response packet received within the
+			   prescribed bus turn-around time
+			c) unknown USB error 
 
 -EILSEQ (*)		CRC mismatch
 
diff -Nru a/Documentation/usb/mtouchusb.txt b/Documentation/usb/mtouchusb.txt
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/Documentation/usb/mtouchusb.txt	Sun Feb 29 13:05:40 2004
@@ -0,0 +1,85 @@
+CHANGES
+
+- Created based off of scanner & INSTALL from the original touchscreen
+  driver on freshmeat (http://freshmeat.net/projects/3mtouchscreendriver)
+- Amended for linux-2.4.18, then 2.4.19
+
+- Complete rewrite using Linux Input in 2.6.3
+   Unfortunately no calibration support at this time
+
+
+DRIVER NOTES:
+
+Installation is simple, you only need to add Linux Input, Linux USB, and the 
+driver to the kernel.  The driver can also be optionally built as a module.
+
+If you have another MicroTouch device that you wish to experiment with
+or try using this driver with, but the Vendor and Product ID's are not 
+coded in, don't despair.  If the driver was compiled as a module, you can 
+pass options to the driver.  Simply try:
+
+  /sbin/modprobe mtouchusb vendor=0x#### product=0x****
+
+If it works, send me the iVendor & iProduct (or a patch) and I will add...
+
+This driver appears to be one of possible 2 Linux USB Input Touchscreen
+drivers.  Although 3M produces a binary only driver available for
+download, I persist in updating this driver since I would like to use the
+touchscreen for embedded apps using QTEmbedded, DirectFB, etc. So I feel the
+logical choice is to use Linux Imput.
+
+A little info about the MicroTouch USB controller (14-206):
+
+Y is inverted, and the device has a total possible resolution of 0 - 65535.
+
+Y is inverted by the driver by:
+
+        input.absmin[ABS_Y] =  MTOUCHUSB_MAX_YC;
+        input.absmax[ABS_Y] =  MTOUCHUSB_MIN_YC;
+
+absmin & absmax are also used to scale the data, sine it is rather high 
+resolution.
+
+    ---------------touch screen area-----------------
+    I                        MicroTouch (xmax,ymax) @I
+    I                      X                         I
+    I   ########visible monitor area##############   I
+    I   #@ (xmin,ymin)                           #   I
+    I   #                                        #   I
+    I   #                                        #   I
+    I   #                                        #   I
+    I   #                                        #   I
+    I   #                                        #   I
+    I Y #                                        #   I
+    I   #                                        #   I
+    I   #                                        #   I
+    I   #                                        #   I
+    I   #                                        #   I
+    I   #                                        #   I
+    I   #                           (xmax,ymax) @#   I
+    I   ##########################################   I
+    I                                                I
+    I@ MicroTouch (xmin,ymin)                        I
+    -------------------------------------------------
+
+Currently there is no way to calibrate the device via this driver.  Perhaps 
+at some point an abstract function will be placed into evdev so generic 
+functions like calibrations, resets, and vendor information can be requested 
+(And the drivers would handle the vendor specific tasks).
+
+ADDITIONAL INFORMATION/UPDATES:
+
+http://groomlakelabs.com/grandamp/code/microtouch/
+
+TODO:
+
+Implement a control urb again to handle requests to and from the device
+such as calibration, etc.
+
+DISCLAMER:
+
+I am not a MicroTouch/3M employee, nor have I ever been.  3M does not support 
+this driver!  If you want touch drivers only supported within X, please go to:
+
+http://www.3m.com/3MTouchSystems/downloads/
+
diff -Nru a/Documentation/usb/scanner.txt b/Documentation/usb/scanner.txt
--- a/Documentation/usb/scanner.txt	Sun Feb 29 13:05:40 2004
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,336 +0,0 @@
-Copyright (C) 1999, 2000 David E. Nelson <dnelson@jump.net>
-Updated 2003 by Henning Meier-Geinitz <henning@meier-geinitz.de>
-
-
-OVERVIEW
-
-This README addresses issues regarding how to configure the kernel to access a
-USB scanner.  Although the driver was originally conceived for USB HP
-scanners, it's general enough so that it can be used with most other USB
-scanners.  Also, one can pass the USB Vendor and Product IDs using module
-parameters for unknown scanners.
-
-There are two drivers for SCSI-over-USB scanners: 
-* The "hpusbscsi" module for Hewlett-Packard 53xx series, Hewlett-Packard 7400,
-  Minolta Scan Dual II, Minolta Elite II
-* The "microtek" module for the Microtek Scanmaker X6
-
-In addition to the kernel driver, user-space tools like SANE are necessary to
-actually use the scanner.  SANE ("Scanner Access Now Easy") provides drivers
-for a variety of USB scanners.  See the appropriate SANE man page for details,
-e.g. man sane-usb and man sane-hp (for HP scanners).
-
-NOTE: Just because a product is detected by this driver does not mean that
-applications exist that support the product.  It's in the hopes that this will
-allow developers a means to produce applications that will support the listed
-USB products.
-
-
-ADDITIONAL INFORMATION
-
-http://www.linux-usb.org/           (General information, mailing lists, links)
-http://www.mostang.com/sane/        (SANE user-space tools)
-http://www.meier-geinitz.de/kernel/ (USB scanner driver information and patches)
-
-
-REQUIREMENTS
-
-A host with a USB port.  Ideally, either a UHCI (Intel), OHCI (Compaq and
-others) or EHCI hardware should work.  
-
-Using "make menuconfig" or your preferred method for configuring the kernel,
-select "Support for USB", "OHCI HCD/UHCI HCD/EHCI HCD" depending on your
-hardware, "USB Scanner support", and "USB device filesystem".  Compile and
-install the modules (you may need to execute "depmod -a" to update the module
-dependencies).  If any of the USB sections were compiled into the kernel, a
-reboot is necessary.  NOTE: Updating the boot disk with "lilo" may also be
-required.  Testing was performed only as modules, YMMV.
-
-Up to 16 scanners can be connected/used simultaneously.  If devfs support is
-enabled, see next section.  Otherwise, the device files must be created
-manually if they don't exist yet, either by MAKEDEV or mknod.
-
-MAKEDEV method:
-  cd /dev
-  MAKEDEV usb
-  Check that the device files "/dev/usb/scanner0" - "/dev/usb/scanner15" have
-  been created.
-
-mknod method:
-  mknod /dev/usb/scanner0 c 180 48
-  mknod /dev/usb/scanner1 c 180 49
-                  . 
-                  .
-  mknod /dev/usb/scanner15 c 180 63
-
-Set appropriate permissions for /dev/usb/scanner[0-15] (don't forget
-about group and world permissions).  Both read and write permissions
-are required for proper operation.  For example:
-  chmod 666 /dev/usb/scanner0
-
-Load the appropriate modules (if compiled as modules):
-
-  modprobe ohci-hcd (or uhci-hcd, ehci-hcd)
-  modprobe scanner
-
-
-DEVFS
-
-The later versions of the Linux kernel (2.4.8'ish) included a dynamic
-device filesystem call "devfs".  With devfs, there is no need to
-create the device files as explained above; instead, they are
-dynamically created for you.  For USB Scanner, the device is created
-in /dev/usb/scannerX where X can range from 0 to 15 depending on the
-number of scanners connected to the system.
-
-To see if you have devfs, issue the command "cat /proc/filesytems".
-If devfs is listed you should be ready to go.  You should also have a
-process running called "devfsd".  In order to make sure, issue the
-command "ps aux | grep '[d]evfsd'".
-
-
-CONCLUSION
-
-That's it.  SANE should now be able to access the device.  To make sure the
-device was detected, use "cat /proc/bus/usb/devices".  Your scanner should be
-listed and the line starting with "I:" should look similar to this example:
-
-  I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=usbscanner
-
-The important part is "Driver=usbscanner".  If it reads "Driver=(none)", the
-USB scanner driver didn't recognize the scanner.  Have a look at the MODULE
-PARAMETERS section for what to do in this case.
-
-For more details on the format of "/proc/bus/usb/devices" see
-Documentation/usb/proc_usb_info.txt.
-
-
-MESSAGES
-
-usb_control/bulk_msg: timeout -- On occasions this message will appear
-in "/var/adm/messages", on the console, or both depending on how
-your system is configured.  This is a side effect that scanners are
-sometimes very slow at warming up and/or initializing.  In most cases,
-however, only several of these messages should appear and is generally
-considered to be normal.  
-
-excessive NAK's received -- This message should be considered abnormal
-and generally indicates that the USB system is unable to communicate
-with the scanner for some particular reason.
-
-probe_scanner: Undetected endpoint -- The USB Scanner driver is fairly
-general when it comes to communicating to scanners.  Unfortunately,
-some vendors have designed their scanners in one way or another that
-this driver doesn't account for.
-
-probe_scanner: Endpoint determination failed -- This means that the
-driver is unable to detect a supported configuration for means to
-communicate with the scanner.  See also "probe_scanner: Undetected
-endpoint".
-
-funky result -- Most of the time the data flow between the computer
-and the scanner goes smoothly.  However, due to whatever reason,
-whether it be solar flares or stray neutrons, sometimes the
-communications don't work as expected.  The driver tries to handle
-most types of errors but not all.  When this message is seen,
-something weird happened.  Please contact the mailing list (see
-CONTACT section for details).
-
-
-MODULE PARAMETERS
-
-If you have a device that you wish to experiment with or try using
-this driver with, but the Vendor and Product IDs are not coded in,
-don't despair.  If the driver was compiled as a module, you can pass
-options to the driver.  Simply add 
-
-  options scanner vendor=0x#### product=0x****
-
-to the /etc/modprobe.conf file replacing the #'s and the *'s with the
-correct IDs.  The IDs can be retrieved from the messages file or
-using "cat /proc/bus/usb/devices".
-
-If the default timeout is too low, i.e. there are frequent "timeout" messages,
-you may want to increase the timeout manually by using the parameter
-"read_timeout".  The time is given in seconds.  This is an example for
-modprobe.conf with a timeout of 60 seconds:
-
-  options scanner read_timeout=60
- 
-If the "scanner" module is already loaded into memory, it must be reloaded for
-the module parameters to take effect.  In essence, "rmmod scanner; modprobe
-scanner" must be performed.
-
-
-BUGS
-
-Just look at the list of fixes in the source files. 
-
-
-CONTACT
-
-For asking about problems and fixes, use the linux-usb-users mailing list. For
-patches, linux-usb-devel should be used. Information on both lists can be
-found on http://www.linux-usb.org/.
-
-
-CHANGES
-
-- Amended for linux-2.5.54
-- Added information about read_timeout
-- Added more details about /proc/bus/usb/devices
-- Added/updated links
-- Added pointers two "special" scanner drivers
-- Reordering, spell-checking, formatting
-- Used /dev/usb/scanner[0-15] instead of /dev/usbscanner[0-15]
-- Removed some basic USB configuration stuff
-- Added EHCI
-- Removed some more references to HP
-- Amended for linux-2.4.12
-- Updated devfs support
-- Amended for linux-2.3.99-pre6-3
-- Appended hp_scan.c to end of this README
-- Removed most references to HP
-- Updated uhci/ohci host controller info
-- Updated support for multiple scanner support
-- Updated supported scanners list
-- Updated usbdevfs info
-- Spellcheck
-
-
-HP TEST PROGRAM
-
-There is a small test program (hp_scan.c -- appended below) that can
-be used to test the scanner device if it's an HP scanner that supports
-SCL (Scanner Control Language).  Known HP scanner that support SCL are
-the 4100, 5200, 6200, the 6300 -- note that the 4200 is *not*
-supported since it does not understand SCL; it's also strongly
-suspected that the 3300 and the PhotoSmart S20 are not SCL compliant.
-Hp_scan.c's purpose is to test the driver without having to
-retrieve/configure SANE.  Hp_scan.c will scan the entire bed and put
-the output into a file called "out.dat" in the current directory.  The
-data in the file is raw data so it's not very useful for imaging.
-
---------------- snip -- hp_scan.c -- snip ---------------
-/*
-
-This is a really crude attempt at writing a short test program.  It's
-mostly only to be used to test connectivity with USB HP scanners that
-understand SCL.  Currently, the supported models are 4100C, 5200C,
-6200C, and the 6300C.  Note that the 4200C is *NOT* acceptable.
-
-Copyright (C) David E. Nelson <dnelson@jump.net>, 1999
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or (at
-your option) any later version.
-
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <error.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-/*
-   Gray Output produces about a 8945400 byte file.
-   Color Output produces a 26836200 byte file. 
-   
-   To compile: gcc -o hp_scan hp_scan.c
-*/
-
-// #define COLOR /* Undef to scan GrayScale */
-
-int send_cmd(int, const char *, int);
-int read_cmd(int, char *, int);
-
-int
-main(void) {
-
-	ssize_t cnt = 0, total_cnt = 0;
-
-	FILE *fpout;
-
-	int fp;
-	int data_size = 32768;
-
-	char *data;
-
-	static char reset_cmd[] = {'\x1b','E'};
-
-#ifdef COLOR
-	static char data_type_cmd[] = {'\x1b','*','a','5','T'}; /* Color */
-	static char data_width_cmd[] = {'\x1b','*','a','2','4','G'}; /* 24 Bit Color */
-#else
-	static char data_type_cmd[] = {'\x1b','*','a','4','T'}; /* Gray */
-	static char data_width_cmd[] = {'\x1b','*','a','8','G'}; /* 8 Bit Gray */
-#endif
-
-	static char query_cmd[] = {'\x1b', '*', 's', '2', '5', '7', 'E'};
-	static char start_scan_cmd[] = {'\x1b','*','f','0','S'};
-	
-	if(!(data=malloc(data_size))) {
-		perror("malloc failed");
-		exit (1);
-	}
-	
-	if((fp=open("/dev/usb/scanner0", O_RDWR)) < 0) {
-		perror("Unable to open scanner device");
-		exit (1);
-	}
-
-	if((fpout=fopen("out.dat", "w+")) == NULL) {
-		perror("Unable to open output file");
-		exit(1);
-	}
-
-	send_cmd(fp, reset_cmd, sizeof(reset_cmd));
-	send_cmd(fp, data_type_cmd, sizeof(data_type_cmd));
-	send_cmd(fp, data_width_cmd, sizeof(data_width_cmd));
-	send_cmd(fp, start_scan_cmd, sizeof(start_scan_cmd));
-
-	while ((cnt = read(fp, data, data_size)) > 0) {
-		printf("Read: %u\n", cnt); 
-		if(fwrite(data, sizeof(char), cnt, fpout) < 0) {
-			perror("Write to output file failed");
-			exit (1);
-		}
-		total_cnt += cnt;
-	}
-	if (cnt < 0) {
-		perror("Read from scanner failed");
-		exit (1);
-	}
-
-	printf("\nRead %lu bytes.\n", total_cnt);
-
-	send_cmd(fp, reset_cmd, sizeof(reset_cmd));
-
-	close(fp);
-	fclose(fpout);
-	return (0);
-}
-
-int
-send_cmd(int fp, const char * cmd, int length) {
-
-	int result;
-	int x;
-
-	if((result = write(fp, cmd, length)) != length) {
-		printf ("Write warning: %d bytes requested, %d written\n");
-	} else if (result < 0) {
-		perror ("send_cmd failure");
-		exit (1);
-	}
-	return (result);
-}
-	
-int
-read_cmd(int fp, char * response, int length) {
-
-	return read(fp, response, length);
-
-}
diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile
--- a/drivers/usb/Makefile	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/Makefile	Sun Feb 29 13:05:40 2004
@@ -20,10 +20,15 @@
 obj-$(CONFIG_USB_STORAGE)	+= storage/
 
 obj-$(CONFIG_USB_AIPTEK)	+= input/
+obj-$(CONFIG_USB_ATI_REMOTE)	+= input/
 obj-$(CONFIG_USB_HID)		+= input/
 obj-$(CONFIG_USB_KBD)		+= input/
+obj-$(CONFIG_USB_KBTAB)		+= input/
 obj-$(CONFIG_USB_MOUSE)		+= input/
+obj-$(CONFIG_USB_MTOUCH)	+= input/
+obj-$(CONFIG_USB_POWERMATE)	+= input/
 obj-$(CONFIG_USB_WACOM)		+= input/
+obj-$(CONFIG_USB_XPAD)		+= input/
 
 obj-$(CONFIG_USB_DABUSB)	+= media/
 obj-$(CONFIG_USB_DSBR)		+= media/
diff -Nru a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
--- a/drivers/usb/gadget/Makefile	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/gadget/Makefile	Sun Feb 29 13:05:40 2004
@@ -9,7 +9,7 @@
 # USB gadget drivers
 #
 g_zero-objs			:= zero.o usbstring.o
-g_ether-objs			:= ether.o usbstring.o
+g_ether-objs			:= ether.o usbstring.o config.o
 g_serial-objs			:= serial.o usbstring.o
 gadgetfs-objs			:= inode.o usbstring.o
 g_file_storage-objs		:= file_storage.o usbstring.o
diff -Nru a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/gadget/config.c	Sun Feb 29 13:05:40 2004
@@ -0,0 +1,116 @@
+/*
+ * usb/gadget/config.c -- simplify building config descriptors
+ *
+ * Copyright (C) 2003 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+
+#include <linux/usb_ch9.h>
+
+
+/**
+ * usb_descriptor_fillbuf - fill buffer with descriptors
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ *
+ * Copies descriptors into the buffer, returning the length or a
+ * negative error code if they can't all be copied.  Useful when
+ * assembling descriptors for an associated set of interfaces used
+ * as part of configuring a composite device; or in other cases where
+ * sets of descriptors need to be marshaled.
+ */
+int
+usb_descriptor_fillbuf(void *buf, unsigned buflen,
+		const struct usb_descriptor_header **src)
+{
+	u8	*dest = buf;
+
+	if (!src)
+		return -EINVAL;
+
+	/* fill buffer from src[] until null descriptor ptr */
+	for (; 0 != *src; src++) {
+		unsigned		len = (*src)->bLength;
+
+		if (len > buflen);
+			return -EINVAL;
+		memcpy(dest, *src, len);
+		buflen -= len;
+		dest += len;
+	}
+	return dest - (u8 *)buf;
+}
+
+
+/**
+ * usb_gadget_config_buf - builts a complete configuration descriptor
+ * @config: Header for the descriptor, including characteristics such
+ *	as power requirements and number of interfaces.
+ * @desc: Null-terminated vector of pointers to the descriptors (interface,
+ *	endpoint, etc) defining all functions in this device configuration.
+ * @buf: Buffer for the resulting configuration descriptor.
+ * @length: Length of buffer.  If this is not big enough to hold the
+ *	entire configuration descriptor, an error code will be returned.
+ *
+ * This copies descriptors into the response buffer, building a descriptor
+ * for that configuration.  It returns the buffer length or a negative
+ * status code.  The config.wTotalLength field is set to match the length
+ * of the result, but other descriptor fields (including power usage and
+ * interface count) must be set by the caller.
+ *
+ * Gadget drivers could use this when constructing a config descriptor
+ * in response to USB_REQ_GET_DESCRIPTOR.  They will need to patch the
+ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
+ */
+int usb_gadget_config_buf(
+	const struct usb_config_descriptor	*config,
+	void					*buf,
+	unsigned				length,
+	const struct usb_descriptor_header	**desc
+)
+{
+	struct usb_config_descriptor		*cp = buf;
+	int					len;
+
+	/* config descriptor first */
+	if (length < USB_DT_CONFIG_SIZE || !desc)
+		return -EINVAL;
+	*cp = *config; 
+
+	/* then interface/endpoint/class/vendor/... */
+	len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
+			length - USB_DT_CONFIG_SIZE, desc);
+	if (len < 0)
+		return len;
+	len += USB_DT_CONFIG_SIZE;
+	if (len > 0xffff)
+		return -EINVAL;
+
+	/* patch up the config descriptor */
+	cp->bLength = USB_DT_CONFIG_SIZE;
+	cp->bDescriptorType = USB_DT_CONFIG;
+	cp->wTotalLength = cpu_to_le16(len);
+	cp->bmAttributes |= USB_CONFIG_ATT_ONE;
+	return len;
+}
+
diff -Nru a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
--- a/drivers/usb/gadget/ether.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/gadget/ether.c	Sun Feb 29 13:05:40 2004
@@ -607,6 +607,25 @@
 	.wMaxPacketSize =	__constant_cpu_to_le16 (64),
 };
 
+static const struct usb_descriptor_header *fs_function [] = {
+#ifdef DEV_CONFIG_CDC
+	/* "cdc" mode descriptors */
+	(struct usb_descriptor_header *) &control_intf,
+	(struct usb_descriptor_header *) &header_desc,
+	(struct usb_descriptor_header *) &union_desc,
+	(struct usb_descriptor_header *) &ether_desc,
+#ifdef	EP_STATUS_NUM
+	(struct usb_descriptor_header *) &fs_status_desc,
+#endif
+	(struct usb_descriptor_header *) &data_nop_intf,
+#endif /* DEV_CONFIG_CDC */
+	/* minimalist core */
+	(struct usb_descriptor_header *) &data_intf,
+	(struct usb_descriptor_header *) &fs_source_desc,
+	(struct usb_descriptor_header *) &fs_sink_desc,
+	0,
+};
+
 #ifdef	HIGHSPEED
 
 /*
@@ -660,6 +679,25 @@
 	.bNumConfigurations =	1,
 };
 
+static const struct usb_descriptor_header *hs_function [] = {
+#ifdef DEV_CONFIG_CDC
+	/* "cdc" mode descriptors */
+	(struct usb_descriptor_header *) &control_intf,
+	(struct usb_descriptor_header *) &header_desc,
+	(struct usb_descriptor_header *) &union_desc,
+	(struct usb_descriptor_header *) &ether_desc,
+#ifdef	EP_STATUS_NUM
+	(struct usb_descriptor_header *) &hs_status_desc,
+#endif
+	(struct usb_descriptor_header *) &data_nop_intf,
+#endif /* DEV_CONFIG_CDC */
+	/* minimalist core */
+	(struct usb_descriptor_header *) &data_intf,
+	(struct usb_descriptor_header *) &hs_source_desc,
+	(struct usb_descriptor_header *) &hs_sink_desc,
+	0,
+};
+
 
 /* maxpacket and other transfer characteristics vary by speed. */
 #define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
@@ -704,86 +742,25 @@
 static int
 config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
 {
-	const unsigned	config_len = USB_DT_CONFIG_SIZE
-#ifdef DEV_CONFIG_CDC
-				+ 2 * USB_DT_INTERFACE_SIZE
-				+ sizeof header_desc
-				+ sizeof union_desc
-				+ sizeof ether_desc
-#ifdef	EP_STATUS_NUM
-				+ USB_DT_ENDPOINT_SIZE
-#endif
-#endif /* DEV_CONFIG_CDC */
-				+ USB_DT_INTERFACE_SIZE
-				+ 2 * USB_DT_ENDPOINT_SIZE;
-
+	int				len;
+	const struct usb_descriptor_header **function = fs_function;
 #ifdef HIGHSPEED
-	int		hs;
-#endif
-	/* a single configuration must always be index 0 */
-	if (index > 0)
-		return -EINVAL;
-	if (config_len > USB_BUFSIZ)
-		return -EDOM;
+	int				hs = (speed == USB_SPEED_HIGH);
 
-	/* config (or other speed config) */
-	memcpy (buf, &eth_config, USB_DT_CONFIG_SIZE);
-	buf [1] = type;
-	((struct usb_config_descriptor *) buf)->wTotalLength
-		= __constant_cpu_to_le16 (config_len);
-	buf += USB_DT_CONFIG_SIZE;
-#ifdef	HIGHSPEED
-	hs = (speed == USB_SPEED_HIGH);
 	if (type == USB_DT_OTHER_SPEED_CONFIG)
 		hs = !hs;
-#endif
-
-#ifdef DEV_CONFIG_CDC
-	/* control interface, class descriptors, optional status endpoint */
-	memcpy (buf, &control_intf, USB_DT_INTERFACE_SIZE);
-	buf += USB_DT_INTERFACE_SIZE;
-
-	memcpy (buf, &header_desc, sizeof header_desc);
-	buf += sizeof header_desc;
-	memcpy (buf, &union_desc, sizeof union_desc);
-	buf += sizeof union_desc;
-	memcpy (buf, &ether_desc, sizeof ether_desc);
-	buf += sizeof ether_desc;
-
-#ifdef	EP_STATUS_NUM
-#ifdef	HIGHSPEED
 	if (hs)
-		memcpy (buf, &hs_status_desc, USB_DT_ENDPOINT_SIZE);
-	else
-#endif	/* HIGHSPEED */
-		memcpy (buf, &fs_status_desc, USB_DT_ENDPOINT_SIZE);
-	buf += USB_DT_ENDPOINT_SIZE;
-#endif	/* EP_STATUS_NUM */
-
-	/* default data altsetting has no endpoints */
-	memcpy (buf, &data_nop_intf, USB_DT_INTERFACE_SIZE);
-	buf += USB_DT_INTERFACE_SIZE;
-#endif /* DEV_CONFIG_CDC */
-
-	/* the "real" data interface has two endpoints */
-	memcpy (buf, &data_intf, USB_DT_INTERFACE_SIZE);
-	buf += USB_DT_INTERFACE_SIZE;
-#ifdef HIGHSPEED
-	if (hs) {
-		memcpy (buf, &hs_source_desc, USB_DT_ENDPOINT_SIZE);
-		buf += USB_DT_ENDPOINT_SIZE;
-		memcpy (buf, &hs_sink_desc, USB_DT_ENDPOINT_SIZE);
-		buf += USB_DT_ENDPOINT_SIZE;
-	} else
-#endif
-	{
-		memcpy (buf, &fs_source_desc, USB_DT_ENDPOINT_SIZE);
-		buf += USB_DT_ENDPOINT_SIZE;
-		memcpy (buf, &fs_sink_desc, USB_DT_ENDPOINT_SIZE);
-		buf += USB_DT_ENDPOINT_SIZE;
-	}
+		function = hs_function;
+#endif
 
-	return config_len;
+	/* a single configuration must always be index 0 */
+	if (index > 0)
+		return -EINVAL;
+	len = usb_gadget_config_buf (&eth_config, buf, USB_BUFSIZ, function);
+	if (len < 0)
+		return len;
+	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+	return len;
 }
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
--- a/drivers/usb/host/Kconfig	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/host/Kconfig	Sun Feb 29 13:05:40 2004
@@ -29,6 +29,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ehci-hcd.
 
+config USB_EHCI_SPLIT_ISO
+	bool "Full speed ISO transactions (EXPERIMENTAL)"
+	depends on USB_EHCI_HCD && EXPERIMENTAL
+	default n
+	---help---
+	  This code is new and hasn't been used with many different
+	  EHCI or USB 2.0 transaction translator implementations.
+	  It should work for ISO-OUT transfers, like audio.
+
 config USB_OHCI_HCD
 	tristate "OHCI HCD support"
 	depends on USB
diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/host/ehci-dbg.c	Sun Feb 29 13:05:40 2004
@@ -579,7 +579,11 @@
 				break;
 			case Q_TYPE_SITD:
 				temp = scnprintf (next, size,
-					" sitd/%p", p.sitd);
+					" sitd%d-%04x/%p",
+					p.sitd->stream->interval,
+					le32_to_cpup (&p.sitd->hw_uframe)
+						& 0x0000ffff,
+					p.sitd);
 				tag = Q_NEXT_TYPE (p.sitd->hw_next);
 				p = p.sitd->sitd_next;
 				break;
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/host/ehci-hcd.c	Sun Feb 29 13:05:40 2004
@@ -106,8 +106,6 @@
 #undef EHCI_VERBOSE_DEBUG
 #undef EHCI_URB_TRACE
 
-// #define have_split_iso
-
 #ifdef DEBUG
 #define EHCI_STATS
 #endif
@@ -676,6 +674,7 @@
 
 	/* the IO watchdog guards against hardware or driver bugs that
 	 * misplace IRQs, and should let us run completely without IRQs.
+	 * such lossage has been observed on both VT6202 and VT8235. 
 	 */
 	if ((ehci->async->qh_next.ptr != 0) || (ehci->periodic_sched != 0))
 		timer_action (ehci, TIMER_IO_WATCHDOG);
@@ -796,13 +795,8 @@
 	case PIPE_ISOCHRONOUS:
 		if (urb->dev->speed == USB_SPEED_HIGH)
 			return itd_submit (ehci, urb, mem_flags);
-#ifdef have_split_iso
 		else
 			return sitd_submit (ehci, urb, mem_flags);
-#else
-		dbg ("no split iso support yet");
-		return -ENOSYS;
-#endif /* have_split_iso */
 	}
 }
 
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/host/ehci-sched.c	Sun Feb 29 13:05:40 2004
@@ -53,14 +53,10 @@
 		return &periodic->fstn->fstn_next;
 	case Q_TYPE_ITD:
 		return &periodic->itd->itd_next;
-#ifdef have_split_iso
-	case Q_TYPE_SITD:
+	// case Q_TYPE_SITD:
+	default:
 		return &periodic->sitd->sitd_next;
-#endif /* have_split_iso */
 	}
-	dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
-	// BUG ();
-	return 0;
 }
 
 /* returns true after successful unlink */
@@ -133,7 +129,6 @@
 			hw_p = &q->itd->hw_next;
 			q = &q->itd->itd_next;
 			break;
-#ifdef have_split_iso
 		case Q_TYPE_SITD:
 			/* is it in the S-mask?  (count SPLIT, DATA) */
 			if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
@@ -154,7 +149,6 @@
 			hw_p = &q->sitd->hw_next;
 			q = &q->sitd->sitd_next;
 			break;
-#endif /* have_split_iso */
 		default:
 			BUG ();
 		}
@@ -229,7 +223,8 @@
 				if (same_tt (dev, here.itd->urb->dev)) {
 					u16		mask;
 
-					mask = le32_to_cpu (here.sitd->hw_uframe);
+					mask = le32_to_cpu (here.sitd
+								->hw_uframe);
 					/* FIXME assumes no gap for IN! */
 					mask |= mask >> 8;
 					if (mask & uf_mask)
@@ -237,7 +232,7 @@
 				}
 				type = Q_NEXT_TYPE (here.qh->hw_next);
 				here = here.sitd->sitd_next;
-				break;
+				continue;
 			// case Q_TYPE_FSTN:
 			default:
 				ehci_dbg (ehci,
@@ -698,12 +693,27 @@
 		// BUG_ON (!list_empty(&stream->td_list));
 
 		while (!list_empty (&stream->free_list)) {
-			struct ehci_itd	*itd;
+			struct list_head	*entry;
 
-			itd = list_entry (stream->free_list.next,
-				struct ehci_itd, itd_list);
-			list_del (&itd->itd_list);
-			dma_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+			entry = stream->free_list.next;
+			list_del (entry);
+
+			/* knows about ITD vs SITD */
+			if (stream->highspeed) {
+				struct ehci_itd		*itd;
+
+				itd = list_entry (entry, struct ehci_itd,
+						itd_list);
+				dma_pool_free (ehci->itd_pool, itd,
+						itd->itd_dma);
+			} else {
+				struct ehci_sitd	*sitd;
+
+				sitd = list_entry (entry, struct ehci_sitd,
+						sitd_list);
+				dma_pool_free (ehci->sitd_pool, sitd,
+						sitd->sitd_dma);
+			}
 		}
 
 		is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0;
@@ -858,6 +868,7 @@
 	int			i;
 	unsigned		num_itds;
 	struct ehci_iso_sched	*sched;
+	unsigned long		flags;
 
 	sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
 	if (unlikely (sched == 0))
@@ -871,6 +882,7 @@
 		num_itds = urb->number_of_packets;
 
 	/* allocate/init ITDs */
+	spin_lock_irqsave (&ehci->lock, flags);
 	for (i = 0; i < num_itds; i++) {
 
 		/* free_list.next might be cache-hot ... but maybe
@@ -884,8 +896,14 @@
 			list_del (&itd->itd_list);
 			itd_dma = itd->itd_dma;
 		} else
+			itd = 0;
+
+		if (!itd) {
+			spin_unlock_irqrestore (&ehci->lock, flags);
 			itd = dma_pool_alloc (ehci->itd_pool, mem_flags,
 					&itd_dma);
+			spin_lock_irqsave (&ehci->lock, flags);
+		}
 
 		if (unlikely (0 == itd)) {
 			iso_sched_free (stream, sched);
@@ -895,6 +913,7 @@
 		itd->itd_dma = itd_dma;
 		list_add (&itd->itd_list, &sched->td_list);
 	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
 
 	/* temporarily store schedule info in hcpriv */
 	urb->hcpriv = sched;
@@ -909,11 +928,11 @@
 	struct ehci_hcd		*ehci,
 	u32			mod,
 	u32			uframe,
-	u32			end,
 	u8			usecs,
 	u32			period
 )
 {
+	uframe %= period;
 	do {
 		/* can't commit more than 80% periodic == 100 usec */
 		if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
@@ -922,8 +941,7 @@
 
 		/* we know urb->interval is 2^N uframes */
 		uframe += period;
-		uframe %= mod;
-	} while (uframe != end);
+	} while (uframe < mod);
 	return 1;
 }
 
@@ -933,7 +951,6 @@
 	u32			mod,
 	struct ehci_iso_stream	*stream,
 	u32			uframe,
-	u32			end,
 	struct ehci_iso_sched	*sched,
 	u32			period_uframes
 )
@@ -952,12 +969,20 @@
 	 */
 
 	/* check bandwidth */
+	uframe %= period_uframes;
 	do {
 		u32		max_used;
 
 		frame = uframe >> 3;
 		uf = uframe & 7;
 
+		/* tt must be idle for start(s), any gap, and csplit.
+		 * assume scheduling slop leaves 10+% for control/bulk.
+		 */
+		if (!tt_no_collision (ehci, period_uframes << 3,
+				stream->udev, frame, mask))
+			return 0;
+
 		/* check starts (OUT uses more than one) */
 		max_used = 100 - stream->usecs;
 		for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) {
@@ -969,25 +994,19 @@
 		if (stream->c_usecs) {
 			max_used = 100 - stream->c_usecs;
 			do {
-				/* tt is busy in the gap before CSPLIT */
 				tmp = 1 << uf;
-				mask |= tmp;
 				tmp <<= 8;
-				if (stream->raw_mask & tmp)
-					break;
+				if ((stream->raw_mask & tmp) == 0)
+					continue;
+				if (periodic_usecs (ehci, frame, uf)
+						> max_used)
+					return 0;
 			} while (++uf < 8);
-			if (periodic_usecs (ehci, frame, uf) > max_used)
-				return 0;
 		}
 
 		/* we know urb->interval is 2^N uframes */
 		uframe += period_uframes;
-		uframe %= mod;
-	} while (uframe != end);
-
-	/* tt must be idle for start(s), any gap, and csplit */
-	if (!tt_no_collision (ehci, period_uframes, stream->udev, frame, mask))
-		return 0;
+	} while (uframe < mod);
 
 	stream->splits = stream->raw_mask << (uframe & 7);
 	cpu_to_le32s (&stream->splits);
@@ -1014,7 +1033,7 @@
 	struct ehci_iso_stream	*stream
 )
 {
-	u32			now, start, end, max, period;
+	u32			now, start, max, period;
 	int			status;
 	unsigned		mod = ehci->periodic_size << 3;
 	struct ehci_iso_sched	*sched = urb->hcpriv;
@@ -1036,8 +1055,6 @@
 
 	/* when's the last uframe this urb could start? */
 	max = now + mod;
-	max -= sched->span;
-	max -= 8 * SCHEDULE_SLOP;
 
 	/* typical case: reuse current schedule. stream is still active,
 	 * and no gaps from host falling behind (irq delays etc)
@@ -1046,9 +1063,11 @@
 		start = stream->next_uframe;
 		if (start < now)
 			start += mod;
-		if (likely (start < max))
+		if (likely ((start + sched->span) < max))
 			goto ready;
-		/* else fell behind; try to reschedule */
+		/* else fell behind; someday, try to reschedule */
+		status = -EL2NSYNC;
+		goto fail;
 	}
 
 	/* need to schedule; when's the next (u)frame we could start?
@@ -1059,63 +1078,40 @@
 	 */
 	start = SCHEDULE_SLOP * 8 + (now & ~0x07);
 	start %= mod;
-	end = start;
+	stream->next_uframe = start;
 
 	/* NOTE:  assumes URB_ISO_ASAP, to limit complexity/bugs */
 
 	period = urb->interval;
 	if (!stream->highspeed)
 		period <<= 3;
-	if (max > (start + period))
-		max = start + period;
 
-	/* hack:  account for itds already scheduled to this endpoint */
-	if (list_empty (&stream->td_list))
-		end = max;
-
-	/* within [start..max] find a uframe slot with enough bandwidth */
-	end %= mod;
-	do {
+	/* find a uframe slot with enough bandwidth */
+	for (; start < (stream->next_uframe + period); start++) {
 		int		enough_space;
 
 		/* check schedule: enough space? */
 		if (stream->highspeed)
-			enough_space = itd_slot_ok (ehci, mod, start, end,
+			enough_space = itd_slot_ok (ehci, mod, start,
 					stream->usecs, period);
 		else {
 			if ((start % 8) >= 6)
 				continue;
 			enough_space = sitd_slot_ok (ehci, mod, stream,
-					start, end, sched, period);
+					start, sched, period);
 		}
 
-		/* (re)schedule it here if there's enough bandwidth */
+		/* schedule it here if there's enough bandwidth */
 		if (enough_space) {
-			start %= mod;
-			if (unlikely (!list_empty (&stream->td_list))) {
-				/* host fell behind ... maybe irq latencies
-				 * delayed this request queue for too long.
-				 */
-				stream->rescheduled++;
-				dev_dbg (&urb->dev->dev,
-					"iso%d%s %d.%d skip %d.%d\n",
-					stream->bEndpointAddress & 0x0f,
-					(stream->bEndpointAddress & USB_DIR_IN)
-						? "in" : "out",
-					stream->next_uframe >> 3,
-					stream->next_uframe & 0x7,
-					start >> 3, start & 0x7);
-			}
-			stream->next_uframe = start;
+			stream->next_uframe = start % mod;
 			goto ready;
 		}
-
-	} while (++start < max);
+	}
 
 	/* no room in the schedule */
-	ehci_dbg (ehci, "iso %ssched full %p (now %d end %d max %d)\n",
+	ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
 		list_empty (&stream->td_list) ? "" : "re",
-		urb, now, end, max);
+		urb, now, max);
 	status = -ENOSPC;
 
 fail:
@@ -1260,6 +1256,7 @@
 	iso_sched_free (stream, iso_sched);
 	urb->hcpriv = 0;
 
+	timer_action (ehci, TIMER_IO_WATCHDOG);
 	if (unlikely (!ehci->periodic_sched++))
 		return enable_periodic (ehci);
 	return 0;
@@ -1404,18 +1401,392 @@
 	return status;
 }
 
-#ifdef have_split_iso
+#ifdef CONFIG_USB_EHCI_SPLIT_ISO
 
 /*-------------------------------------------------------------------------*/
 
 /*
- * "Split ISO TDs" ... used for USB 1.1 devices going through
- * the TTs in USB 2.0 hubs.
- *
- * FIXME not yet implemented
+ * "Split ISO TDs" ... used for USB 1.1 devices going through the
+ * TTs in USB 2.0 hubs.  These need microframe scheduling.
  */
 
-#endif /* have_split_iso */
+static inline void
+sitd_sched_init (
+	struct ehci_iso_sched	*iso_sched,
+	struct ehci_iso_stream	*stream,
+	struct urb		*urb
+)
+{
+	unsigned	i;
+	dma_addr_t	dma = urb->transfer_dma;
+
+	/* how many frames are needed for these transfers */
+	iso_sched->span = urb->number_of_packets * stream->interval;
+
+	/* figure out per-frame sitd fields that we'll need later
+	 * when we fit new sitds into the schedule.
+	 */
+	for (i = 0; i < urb->number_of_packets; i++) {
+		struct ehci_iso_packet	*packet = &iso_sched->packet [i];
+		unsigned		length;
+		dma_addr_t		buf;
+		u32			trans;
+
+		length = urb->iso_frame_desc [i].length & 0x03ff;
+		buf = dma + urb->iso_frame_desc [i].offset;
+
+		trans = SITD_STS_ACTIVE;
+		if (((i + 1) == urb->number_of_packets)
+				&& !(urb->transfer_flags & URB_NO_INTERRUPT))
+			trans |= SITD_IOC;
+		trans |= length << 16;
+		packet->transaction = cpu_to_le32 (trans);
+
+		/* might need to cross a buffer page within a td */
+		packet->bufp = buf;
+		buf += length;
+		packet->buf1 = buf & ~0x0fff;
+		if (packet->buf1 != (buf & ~(u64)0x0fff))
+			packet->cross = 1;
+
+		/* OUT uses multiple start-splits */ 
+		if (stream->bEndpointAddress & USB_DIR_IN)
+			continue;
+		length = 1 + (length / 188);
+		packet->buf1 |= length;
+		if (length > 1) /* BEGIN vs ALL */
+			packet->buf1 |= 1 << 3;
+	}
+}
+
+static int
+sitd_urb_transaction (
+	struct ehci_iso_stream	*stream,
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	int			mem_flags
+)
+{
+	struct ehci_sitd	*sitd;
+	dma_addr_t		sitd_dma;
+	int			i;
+	struct ehci_iso_sched	*iso_sched;
+	unsigned long		flags;
+
+	iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
+	if (iso_sched == 0)
+		return -ENOMEM;
+
+	sitd_sched_init (iso_sched, stream, urb);
+
+	/* allocate/init sITDs */
+	spin_lock_irqsave (&ehci->lock, flags);
+	for (i = 0; i < urb->number_of_packets; i++) {
+
+		/* NOTE:  for now, we don't try to handle wraparound cases
+		 * for IN (using sitd->hw_backpointer, like a FSTN), which
+		 * means we never need two sitds for full speed packets.
+		 */
+
+		/* free_list.next might be cache-hot ... but maybe
+		 * the HC caches it too. avoid that issue for now.
+		 */
+
+		/* prefer previously-allocated sitds */
+		if (!list_empty(&stream->free_list)) {
+			sitd = list_entry (stream->free_list.prev,
+					 struct ehci_sitd, sitd_list);
+			list_del (&sitd->sitd_list);
+			sitd_dma = sitd->sitd_dma;
+		} else
+			sitd = 0;
+
+		if (!sitd) {
+			spin_unlock_irqrestore (&ehci->lock, flags);
+			sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags,
+					&sitd_dma);
+			spin_lock_irqsave (&ehci->lock, flags);
+		}
+
+		if (!sitd) {
+			iso_sched_free (stream, iso_sched);
+			spin_unlock_irqrestore (&ehci->lock, flags);
+			return -ENOMEM;
+		}
+		memset (sitd, 0, sizeof *sitd);
+		sitd->sitd_dma = sitd_dma;
+		list_add (&sitd->sitd_list, &iso_sched->td_list);
+	}
+
+	/* temporarily store schedule info in hcpriv */
+	urb->hcpriv = iso_sched;
+	urb->error_count = 0;
+
+	spin_unlock_irqrestore (&ehci->lock, flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+sitd_patch (
+	struct ehci_iso_stream	*stream,
+	struct ehci_sitd	*sitd,
+	struct ehci_iso_sched	*iso_sched,
+	unsigned		index
+)
+{
+	struct ehci_iso_packet	*uf = &iso_sched->packet [index];
+	u64			bufp = uf->bufp;
+
+	sitd->hw_next = EHCI_LIST_END;
+	sitd->hw_fullspeed_ep = stream->address;
+	sitd->hw_uframe = stream->splits;
+	sitd->hw_results = uf->transaction;
+	sitd->hw_backpointer = EHCI_LIST_END;
+
+	bufp = uf->bufp;
+	sitd->hw_buf [0] = cpu_to_le32 (bufp);
+	sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32);
+
+	sitd->hw_buf [1] = cpu_to_le32 (uf->buf1);
+	if (uf->cross) {
+		bufp += 4096;
+		sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32);
+	}
+	sitd->index = index;
+}
+
+static inline void
+sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
+{
+	/* note: sitd ordering could matter (CSPLIT then SSPLIT) */
+	sitd->sitd_next = ehci->pshadow [frame];
+	sitd->hw_next = ehci->periodic [frame];
+	ehci->pshadow [frame].sitd = sitd;
+	sitd->frame = frame;
+	wmb ();
+	ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD;
+}
+
+/* fit urb's sitds into the selected schedule slot; activate as needed */
+static int
+sitd_link_urb (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	unsigned		mod,
+	struct ehci_iso_stream	*stream
+)
+{
+	int			packet;
+	unsigned		next_uframe;
+	struct ehci_iso_sched	*sched = urb->hcpriv;
+	struct ehci_sitd	*sitd;
+
+	next_uframe = stream->next_uframe;
+
+	if (list_empty(&stream->td_list)) {
+		/* usbfs ignores TT bandwidth */
+		hcd_to_bus (&ehci->hcd)->bandwidth_allocated
+				+= stream->bandwidth;
+		ehci_vdbg (ehci,
+			"sched dev%s ep%d%s-iso [%d] %dms/%04x\n",
+			urb->dev->devpath, stream->bEndpointAddress & 0x0f,
+			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+			(next_uframe >> 3) % ehci->periodic_size,
+			stream->interval, le32_to_cpu (stream->splits));
+		stream->start = jiffies;
+	}
+	hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs++;
+
+	/* fill sITDs frame by frame */
+	for (packet = 0, sitd = 0;
+			packet < urb->number_of_packets;
+			packet++) {
+
+		/* ASSERT:  we have all necessary sitds */
+		BUG_ON (list_empty (&sched->td_list));
+
+		/* ASSERT:  no itds for this endpoint in this frame */
+
+		sitd = list_entry (sched->td_list.next,
+				struct ehci_sitd, sitd_list);
+		list_move_tail (&sitd->sitd_list, &stream->td_list);
+		sitd->stream = iso_stream_get (stream);
+		sitd->urb = usb_get_urb (urb);
+
+		sitd_patch (stream, sitd, sched, packet);
+		sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
+				sitd);
+
+		next_uframe += stream->interval << 3;
+		stream->depth += stream->interval << 3;
+	}
+	stream->next_uframe = next_uframe % mod;
+
+	/* don't need that schedule data any more */
+	iso_sched_free (stream, sched);
+	urb->hcpriv = 0;
+
+	timer_action (ehci, TIMER_IO_WATCHDOG);
+	if (!ehci->periodic_sched++)
+		return enable_periodic (ehci);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define	SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
+			| SITD_STS_XACT | SITD_STS_MMF | SITD_STS_STS)
+
+static unsigned
+sitd_complete (
+	struct ehci_hcd		*ehci,
+	struct ehci_sitd	*sitd,
+	struct pt_regs		*regs
+) {
+	struct urb				*urb = sitd->urb;
+	struct usb_iso_packet_descriptor	*desc;
+	u32					t;
+	int					urb_index = -1;
+	struct ehci_iso_stream			*stream = sitd->stream;
+	struct usb_device			*dev;
+
+	urb_index = sitd->index;
+	desc = &urb->iso_frame_desc [urb_index];
+	t = le32_to_cpup (&sitd->hw_results);
+
+	/* report transfer status */
+	if (t & SITD_ERRS) {
+		urb->error_count++;
+		if (t & SITD_STS_DBE)
+			desc->status = usb_pipein (urb->pipe)
+				? -ENOSR  /* hc couldn't read */
+				: -ECOMM; /* hc couldn't write */
+		else if (t & SITD_STS_BABBLE)
+			desc->status = -EOVERFLOW;
+		else /* XACT, MMF, etc */
+			desc->status = -EPROTO;
+	} else {
+		desc->status = 0;
+		desc->actual_length = desc->length - SITD_LENGTH (t);
+	}
+
+	usb_put_urb (urb);
+	sitd->urb = 0;
+	sitd->stream = 0;
+	list_move (&sitd->sitd_list, &stream->free_list);
+	stream->depth -= stream->interval << 3;
+	iso_stream_put (ehci, stream);
+
+	/* handle completion now? */
+	if ((urb_index + 1) != urb->number_of_packets)
+		return 0;
+
+	/* ASSERT: it's really the last sitd for this urb
+	list_for_each_entry (sitd, &stream->td_list, sitd_list)
+		BUG_ON (sitd->urb == urb);
+	 */
+
+	/* give urb back to the driver */
+	dev = usb_get_dev (urb->dev);
+	ehci_urb_done (ehci, urb, regs);
+	urb = 0;
+
+	/* defer stopping schedule; completion can submit */
+	ehci->periodic_sched--;
+	if (!ehci->periodic_sched)
+		(void) disable_periodic (ehci);
+	hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs--;
+
+	if (list_empty (&stream->td_list)) {
+		hcd_to_bus (&ehci->hcd)->bandwidth_allocated
+				-= stream->bandwidth;
+		ehci_vdbg (ehci,
+			"deschedule devp %s ep%d%s-iso\n",
+			dev->devpath, stream->bEndpointAddress & 0x0f,
+			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+	}
+	iso_stream_put (ehci, stream);
+	usb_put_dev (dev);
+
+	return 1;
+}
+
+
+static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
+{
+	int			status = -EINVAL;
+	unsigned long		flags;
+	struct ehci_iso_stream	*stream;
+
+	// FIXME remove when csplits behave
+	if (usb_pipein(urb->pipe)) {
+		ehci_dbg (ehci, "no iso-IN split transactions yet\n");
+		return -ENOMEM;
+	}
+
+	/* Get iso_stream head */
+	stream = iso_stream_find (ehci, urb);
+	if (stream == 0) {
+		ehci_dbg (ehci, "can't get iso stream\n");
+		return -ENOMEM;
+	}
+	if (urb->interval != stream->interval) {
+		ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
+			stream->interval, urb->interval);
+		goto done;
+	}
+
+#ifdef EHCI_URB_TRACE
+	ehci_dbg (ehci,
+		"submit %p dev%s ep%d%s-iso len %d\n",
+		urb, urb->dev->devpath,
+		usb_pipeendpoint (urb->pipe),
+		usb_pipein (urb->pipe) ? "in" : "out",
+		urb->transfer_buffer_length);
+#endif
+
+	/* allocate SITDs */
+	status = sitd_urb_transaction (stream, ehci, urb, mem_flags);
+	if (status < 0) {
+		ehci_dbg (ehci, "can't init sitds\n");
+		goto done;
+	}
+
+	/* schedule ... need to lock */
+	spin_lock_irqsave (&ehci->lock, flags);
+	status = iso_stream_schedule (ehci, urb, stream);
+ 	if (status == 0)
+		sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
+	spin_unlock_irqrestore (&ehci->lock, flags);
+
+done:
+	if (status < 0)
+		iso_stream_put (ehci, stream);
+	return status;
+}
+
+#else
+
+static inline int
+sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
+{
+	ehci_dbg (ehci, "split iso support is disabled\n");
+	return -ENOSYS;
+}
+
+static inline unsigned
+sitd_complete (
+	struct ehci_hcd		*ehci,
+	struct ehci_sitd	*sitd,
+	struct pt_regs		*regs
+) {
+	ehci_err (ehci, "sitd_complete %p?\n", sitd);
+	return 0;
+}
+
+#endif /* USB_EHCI_SPLIT_ISO */
 
 /*-------------------------------------------------------------------------*/
 
@@ -1513,7 +1884,6 @@
 				modified = itd_complete (ehci, q.itd, regs);
 				q = *q_p;
 				break;
-#ifdef have_split_iso
 			case Q_TYPE_SITD:
 				if (q.sitd->hw_results & SITD_ACTIVE) {
 					q_p = &q.sitd->sitd_next;
@@ -1529,7 +1899,6 @@
 				modified = sitd_complete (ehci, q.sitd, regs);
 				q = *q_p;
 				break;
-#endif /* have_split_iso */
 			default:
 				dbg ("corrupt type %d frame %d shadow %p",
 					type, frame, q.ptr);
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/host/ehci.h	Sun Feb 29 13:05:40 2004
@@ -492,16 +492,16 @@
 /*
  * EHCI Specification 0.95 Section 3.4 
  * siTD, aka split-transaction isochronous Transfer Descriptor
- *       ... describe low/full speed iso xfers through TT in hubs
+ *       ... describe full speed iso xfers through TT in hubs
  * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
  */
 struct ehci_sitd {
 	/* first part defined by EHCI spec */
 	u32			hw_next;
 /* uses bit field macros above - see EHCI 0.95 Table 3-8 */
-	u32			hw_fullspeed_ep;	/* see EHCI table 3-9 */
-	u32			hw_uframe;		/* see EHCI table 3-10 */
-	u32			hw_results;		/* see EHCI table 3-11 */
+	u32			hw_fullspeed_ep;	/* EHCI table 3-9 */
+	u32			hw_uframe;		/* EHCI table 3-10 */
+	u32			hw_results;		/* EHCI table 3-11 */
 #define	SITD_IOC	(1 << 31)	/* interrupt on completion */
 #define	SITD_PAGE	(1 << 30)	/* buffer 0/1 */
 #define	SITD_LENGTH(x)	(0x3ff & ((x)>>16))
@@ -515,8 +515,8 @@
 
 #define SITD_ACTIVE	__constant_cpu_to_le32(SITD_STS_ACTIVE)
 
-	u32			hw_buf [2];		/* see EHCI table 3-12 */
-	u32			hw_backpointer;		/* see EHCI table 3-13 */
+	u32			hw_buf [2];		/* EHCI table 3-12 */
+	u32			hw_backpointer;		/* EHCI table 3-13 */
 	u32			hw_buf_hi [2];		/* Appendix B */
 
 	/* the rest is HCD-private */
@@ -551,8 +551,6 @@
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/
-
-#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags)
 
 #ifndef DEBUG
 #define STUB_DEBUG_FILES
diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/host/uhci-hcd.c	Sun Feb 29 13:05:40 2004
@@ -781,7 +781,8 @@
 /*
  * Map status to standard result codes
  *
- * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)]
+ * <status> is (td->status & 0xF60000) [a.k.a. uhci_status_bits(td->status)]
+ * 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)
@@ -792,22 +793,18 @@
 		return -EPROTO;
 	if (status & TD_CTRL_CRCTIMEO) {		/* CRC/Timeout */
 		if (dir_out)
-			return -ETIMEDOUT;
+			return -EPROTO;
 		else
 			return -EILSEQ;
 	}
-	if (status & TD_CTRL_NAK)			/* NAK */
-		return -ETIMEDOUT;
 	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;
-	if (status & TD_CTRL_ACTIVE)			/* Active */
-		return 0;
-
-	return -EINVAL;
+	WARN_ON(status & TD_CTRL_ACTIVE);		/* Active */
+	return 0;
 }
 
 /*
@@ -832,7 +829,7 @@
 		status |= TD_CTRL_LS;
 
 	/*
-	 * Build the TD for the control request
+	 * Build the TD for the control request setup packet
 	 */
 	td = uhci_alloc_td(uhci, urb->dev);
 	if (!td)
@@ -990,13 +987,13 @@
 
 	if (urbp->short_control_packet) {
 		tmp = head->prev;
-		goto status_phase;
+		goto status_stage;
 	}
 
 	tmp = head->next;
 	td = list_entry(tmp, struct uhci_td, list);
 
-	/* The first TD is the SETUP phase, check the status, but skip */
+	/* 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)
@@ -1037,10 +1034,10 @@
 		}
 	}
 
-status_phase:
+status_stage:
 	td = list_entry(tmp, struct uhci_td, list);
 
-	/* Control status phase */
+	/* Control status stage */
 	status = td_status(td);
 
 #ifdef I_HAVE_BUGGY_APC_BACKUPS
@@ -1053,10 +1050,11 @@
 		return 0;
 #endif
 
+	status = uhci_status_bits(status);
 	if (status & TD_CTRL_ACTIVE)
 		return -EINPROGRESS;
 
-	if (uhci_status_bits(status))
+	if (status)
 		goto td_error;
 
 	return 0;
@@ -1273,12 +1271,6 @@
 }
 
 /*
- * Bulk and interrupt use common result
- */
-#define uhci_result_bulk uhci_result_common
-#define uhci_result_interrupt uhci_result_common
-
-/*
  * Isochronous transfers
  */
 static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
@@ -1403,7 +1395,8 @@
 		urb->iso_frame_desc[i].actual_length = actlength;
 		urb->actual_length += actlength;
 
-		status = uhci_map_status(uhci_status_bits(td_status(td)), usb_pipeout(urb->pipe));
+		status = uhci_map_status(uhci_status_bits(td_status(td)),
+				usb_pipeout(urb->pipe));
 		urb->iso_frame_desc[i].status = status;
 		if (status) {
 			urb->error_count++;
@@ -1508,12 +1501,9 @@
 		struct urb_priv *urbp = urb->hcpriv;
 
 		list_del_init(&urbp->urb_list);
-		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-		uhci_destroy_urb_priv (uhci, urb);
-
-		return ret;
-	}
-	ret = 0;
+		uhci_destroy_urb_priv(uhci, urb);
+	} else
+		ret = 0;
 
 out:
 	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
@@ -1541,11 +1531,9 @@
 	case PIPE_CONTROL:
 		ret = uhci_result_control(uhci, urb);
 		break;
-	case PIPE_INTERRUPT:
-		ret = uhci_result_interrupt(uhci, urb);
-		break;
 	case PIPE_BULK:
-		ret = uhci_result_bulk(uhci, urb);
+	case PIPE_INTERRUPT:
+		ret = uhci_result_common(uhci, urb);
 		break;
 	case PIPE_ISOCHRONOUS:
 		ret = uhci_result_isochronous(uhci, urb);
@@ -1649,10 +1637,12 @@
 {
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 	unsigned long flags;
-	struct urb_priv *urbp = urb->hcpriv;
+	struct urb_priv *urbp;
 
 	spin_lock_irqsave(&uhci->urb_list_lock, flags);
-
+	urbp = urb->hcpriv;
+	if (!urbp)			/* URB was never linked! */
+		goto done;
 	list_del_init(&urbp->urb_list);
 
 	uhci_unlink_generic(uhci, urb);
@@ -1665,6 +1655,7 @@
 	list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);
 
 	spin_unlock(&uhci->urb_remove_list_lock);
+done:
 	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 	return 0;
 }
@@ -1861,17 +1852,12 @@
 
 static void uhci_remove_pending_urbps(struct uhci_hcd *uhci)
 {
-	struct list_head *tmp, *head;
-
 	spin_lock(&uhci->urb_remove_list_lock);
-	head = &uhci->urb_remove_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+	spin_lock(&uhci->complete_list_lock);
 
-		tmp = tmp->next;
-		uhci_moveto_complete(uhci, urbp);
-	}
+	/* Splice the urb_remove_list onto the end of the complete_list */
+	list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);
+	spin_unlock(&uhci->complete_list_lock);
 	spin_unlock(&uhci->urb_remove_list_lock);
 }
 
@@ -2471,9 +2457,16 @@
 
 	pci_set_master(to_pci_dev(uhci_dev(uhci)));
 
-	if (uhci->state == UHCI_SUSPENDED)
+	if (uhci->state == UHCI_SUSPENDED) {
+
+		/*
+		 * Some systems clear the Interrupt Enable register during
+		 * PM suspend/resume, so reinitialize it.
+		 */
+		outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC |
+				USBINTR_SP, uhci->io_addr + USBINTR);
 		uhci->resume_detect = 1;
-	else {
+	} else {
 		reset_hc(uhci);
 		start_hc(uhci);
 	}
diff -Nru a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
--- a/drivers/usb/host/uhci-hcd.h	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/host/uhci-hcd.h	Sun Feb 29 13:05:40 2004
@@ -141,7 +141,7 @@
 				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
 
 #define uhci_maxerr(err)		((err) << TD_CTRL_C_ERR_SHIFT)
-#define uhci_status_bits(ctrl_sts)	((ctrl_sts) & 0xFE0000)
+#define uhci_status_bits(ctrl_sts)	((ctrl_sts) & 0xF60000)
 #define uhci_actual_length(ctrl_sts)	(((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
 
 /*
diff -Nru a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
--- a/drivers/usb/input/Kconfig	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/input/Kconfig	Sun Feb 29 13:05:40 2004
@@ -179,6 +179,18 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called powermate.
 
+config USB_MTOUCH
+	tristate "MicroTouch USB Touchscreen Driver"
+	depends on USB && INPUT
+	---help---
+	  Say Y here if you want to use a MicroTouch (Now 3M) USB 
+	  Touchscreen controller.
+
+	  See <file:Documentation/usb/mtouch.txt> for additional information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mtouchusb.
+
 config USB_XPAD
 	tristate "X-Box gamepad support"
 	depends on USB && INPUT
@@ -192,3 +204,17 @@
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called xpad.
+	  
+config USB_ATI_REMOTE
+	tristate "ATI USB RF remote control"
+	depends on USB && INPUT
+	---help---
+	  Say Y here if you want to use one of ATI's USB remote controls.
+	  These are RF remotes with USB receivers. They come with many of ATI's 
+	  All-In-Wonder video cards.  This driver provides mouse pointer, left
+          and right mouse buttons, and maps all the other remote buttons to
+	  keypress events.
+	  
+	  To compile this driver as a module, choose M here: the module will be
+	  called ati_remote.
+
diff -Nru a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
--- a/drivers/usb/input/Makefile	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/input/Makefile	Sun Feb 29 13:05:40 2004
@@ -27,10 +27,12 @@
 endif
 
 obj-$(CONFIG_USB_AIPTEK)	+= aiptek.o
+obj-$(CONFIG_USB_ATI_REMOTE)	+= ati_remote.o
 obj-$(CONFIG_USB_HID)		+= hid.o
 obj-$(CONFIG_USB_KBD)		+= usbkbd.o
-obj-$(CONFIG_USB_MOUSE)		+= usbmouse.o
-obj-$(CONFIG_USB_WACOM)		+= wacom.o
 obj-$(CONFIG_USB_KBTAB)		+= kbtab.o
+obj-$(CONFIG_USB_MOUSE)		+= usbmouse.o
+obj-$(CONFIG_USB_MTOUCH)	+= mtouchusb.o
 obj-$(CONFIG_USB_POWERMATE)	+= powermate.o
+obj-$(CONFIG_USB_WACOM)		+= wacom.o
 obj-$(CONFIG_USB_XPAD)		+= xpad.o
diff -Nru a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/ati_remote.c	Sun Feb 29 13:05:40 2004
@@ -0,0 +1,851 @@
+/* 
+ *  USB ATI Remote support
+ *
+ *  Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ *  Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
+ *
+ *  This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
+ *  porting to the 2.6 kernel interfaces, along with other modification 
+ *  to better match the style of the existing usb/input drivers.  However, the
+ *  protocol and hardware handling is essentially unchanged from 2.1.1.
+ *  
+ *  The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by 
+ *  Vojtech Pavlik.
+ *
+ *  Changes:
+ *
+ *  Feb 2004: Torrey Hoffman <thoffman@arnor.net>
+ *            Version 2.2.0
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
+ *
+ * Hardware & software notes
+ *
+ * These remote controls are distributed by ATI as part of their 
+ * "All-In-Wonder" video card packages.  The receiver self-identifies as a 
+ * "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
+ *
+ * It is possible to use multiple receivers and remotes on multiple computers 
+ * simultaneously by configuring them to use specific channels.
+ * 
+ * The RF protocol used by the remote supports 16 distinct channels, 1 to 16.  
+ * Actually, it may even support more, at least in some revisions of the 
+ * hardware.
+ *
+ * Each remote can be configured to transmit on one channel as follows:
+ *   - Press and hold the "hand icon" button.  
+ *   - When the red LED starts to blink, let go of the "hand icon" button. 
+ *   - When it stops blinking, input the channel code as two digits, from 01 
+ *     to 16, and press the hand icon again.
+ * 
+ * The timing can be a little tricky.  Try loading the module with debug=1
+ * to have the kernel print out messages about the remote control number
+ * and mask.  Note: debugging prints remote numbers as zero-based hexadecimal.
+ *
+ * The driver has a "channel_mask" parameter. This bitmask specifies which
+ * channels will be ignored by the module.  To mask out channels, just add 
+ * all the 2^channel_number values together.
+ *
+ * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
+ * ignore signals coming from remote controls transmitting on channel 4, but 
+ * accept all other channels.
+ *
+ * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be 
+ * ignored.
+ *
+ * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this 
+ * parameter are unused.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+
+/*
+ * Module and Version Information, Module Parameters
+ */
+ 
+#define ATI_REMOTE_VENDOR_ID 	0x0bc7
+#define ATI_REMOTE_PRODUCT_ID 	0x004
+
+#define DRIVER_VERSION 	        "2.2.0"
+#define DRIVER_AUTHOR           "Torrey Hoffman <thoffman@arnor.net>"
+#define DRIVER_DESC             "ATI/X10 RF USB Remote Control"
+
+#define NAME_BUFSIZE      80    /* size of product name, path buffers */
+#define DATA_BUFSIZE      63    /* size of URB data buffers */
+#define ATI_INPUTNUM      1     /* Which input device to register as */
+
+unsigned long channel_mask = 0;
+module_param(channel_mask, ulong, 444);
+MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
+
+int debug = 0;
+module_param(debug, int, 444);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#undef err
+#define err(format, arg...) printk(KERN_ERR format , ## arg)
+ 
+static struct usb_device_id ati_remote_table[] = {
+	{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
+	{}	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ati_remote_table);
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a)	((unsigned char)((a) >> 8))
+#define LO(a)	((unsigned char)((a) & 0xff))
+
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+
+/* Device initialization strings */
+static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
+static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
+
+/* Acceleration curve for directional control pad */
+static char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
+
+/* Duplicate event filtering time. 
+ * Sequential, identical KIND_FILTERED inputs with less than
+ * FILTER_TIME jiffies between them are dropped.  
+ * (HZ >> 4) == 1/16th of a second and works well for me.
+ */
+#define FILTER_TIME (HZ >> 4)
+
+struct ati_remote {
+	struct input_dev idev;		
+	struct usb_device *udev;
+	struct usb_interface *interface;
+		
+	struct urb *irq_urb;
+	struct urb *out_urb;
+	struct usb_endpoint_descriptor *endpoint_in;
+	struct usb_endpoint_descriptor *endpoint_out;
+	unsigned char *inbuf;
+	unsigned char *outbuf;
+	dma_addr_t inbuf_dma;
+	dma_addr_t outbuf_dma;
+
+	int open;                   /* open counter */
+	int present;                /* device plugged in? */
+	
+	unsigned char old_data[2];  /* Detect duplicate events */
+	unsigned long old_jiffies;
+	unsigned long acc_jiffies;  /* handle acceleration */
+	
+	char name[NAME_BUFSIZE];
+	char phys[NAME_BUFSIZE];
+
+	wait_queue_head_t wait;
+	int send_flags;
+};
+
+/* "Kinds" of messages sent from the hardware to the driver. */
+#define KIND_END        0
+#define KIND_LITERAL    1   /* Simply pass to input system */
+#define KIND_FILTERED   2   /* Add artificial key-up events, drop keyrepeats */
+#define KIND_LU         3   /* Directional keypad diagonals - left up, */
+#define KIND_RU         4   /*   right up,  */
+#define KIND_LD         5   /*   left down, */
+#define KIND_RD         6   /*   right down */
+#define KIND_ACCEL      7   /* Directional keypad - left, right, up, down.*/
+
+/* Translation table from hardware messages to input events. */
+static struct
+{
+	short kind;
+	unsigned char data1, data2;
+	int type;
+	unsigned int code;
+	int value;
+}  ati_remote_tbl[] = 
+{
+	/* Directional control pad axes */
+	{KIND_ACCEL,   0x35, 0x70, EV_REL, REL_X, -1},	 /* left */
+	{KIND_ACCEL,   0x36, 0x71, EV_REL, REL_X, 1},    /* right */
+	{KIND_ACCEL,   0x37, 0x72, EV_REL, REL_Y, -1},	 /* up */
+	{KIND_ACCEL,   0x38, 0x73, EV_REL, REL_Y, 1},    /* down */
+	/* Directional control pad diagonals */	
+	{KIND_LU,      0x39, 0x74, EV_REL, 0, 0},        /* left up */
+	{KIND_RU,      0x3a, 0x75, EV_REL, 0, 0},        /* right up */
+	{KIND_LD,      0x3c, 0x77, EV_REL, 0, 0},        /* left down */
+	{KIND_RD,      0x3b, 0x76, EV_REL, 0, 0},        /* right down */
+
+	/* "Mouse button" buttons */
+	{KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
+	{KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
+	{KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
+	{KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
+
+	/* Artificial "doubleclick" events are generated by the hardware. 
+	 * They are mapped to the "side" and "extra" mouse buttons here. */
+	{KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
+	{KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
+
+	/* keyboard. */
+	{KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
+	{KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
+	{KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
+	{KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
+	{KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
+	{KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
+	{KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
+	{KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
+	{KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
+	{KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
+	{KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
+	{KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
+	{KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
+	{KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
+	{KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
+	{KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
+
+	/* "special" keys */
+	{KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1},    /* "check" */
+	{KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1},       /* "menu" */
+	{KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1},      /* Power */
+	{KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_PROG1, 1},      /* TV */
+	{KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_PROG2, 1},      /* DVD */
+	{KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1},        /* WEB */
+	{KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1},  /* "book" */
+	{KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1},       /* "hand" */
+	{KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1},     /* "timer" */
+	{KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1},      /* "max" */
+	{KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1},       /* left */
+	{KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1},      /* right */
+	{KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1},       /* down */
+	{KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1},         /* up */
+	{KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_ENTER, 1},      /* "OK" */
+	{KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
+	{KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1},   /* VOL - */
+	{KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1},       /* MUTE  */
+	{KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELUP, 1},  /* CH + */
+	{KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
+	{KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1},     /* ( o) red */
+	{KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAYCD, 1},     /* ( >) */
+	{KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1},     /* (<<) */
+	{KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1},    /* (>>) */
+	{KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1},       /* ([]) */ 
+	{KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1},      /* ('') */
+	
+	{KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
+};
+
+/* Local function prototypes */
+static void ati_remote_dump		(unsigned char *data, unsigned int actual_length);
+static void ati_remote_delete		(struct ati_remote *dev);
+static int ati_remote_open		(struct input_dev *inputdev);
+static void ati_remote_close		(struct input_dev *inputdev);
+static int ati_remote_sendpacket	(struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
+static void ati_remote_irq_out		(struct urb *urb, struct pt_regs *regs);
+static void ati_remote_irq_in		(struct urb *urb, struct pt_regs *regs);
+static void ati_remote_input_report	(struct urb *urb, struct pt_regs *regs);
+static int ati_remote_initialize	(struct ati_remote *ati_remote);
+static int ati_remote_probe		(struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote_disconnect	(struct usb_interface *interface);
+
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+	.owner        = THIS_MODULE,
+	.name         = "ati_remote",
+	.probe        = ati_remote_probe,
+	.disconnect   = ati_remote_disconnect,
+	.id_table     = ati_remote_table,
+};
+
+/*
+ *	ati_remote_dump_input
+ */
+static void ati_remote_dump(unsigned char *data, unsigned int len)
+{
+	if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
+		warn("Weird byte 0x%02x\n", data[0]);
+	else if (len == 4)
+		warn("Weird key %02x %02x %02x %02x\n", 
+		     data[0], data[1], data[2], data[3]);
+	else
+		warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
+		     len, data[0], data[1], data[2], data[3], data[4], data[5]);
+}
+
+/*
+ *	ati_remote_open
+ */
+static int ati_remote_open(struct input_dev *inputdev)
+{
+	struct ati_remote *ati_remote = inputdev->private;
+
+	if (ati_remote->open++)
+		return 0;
+
+	/* On first open, submit the read urb which was set up previously. */
+	ati_remote->irq_urb->dev = ati_remote->udev;
+	if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
+		dev_err(&ati_remote->interface->dev, 
+			"%s: usb_submit_urb failed!\n", __FUNCTION__);
+		ati_remote->open--;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ *	ati_remote_close
+ */
+static void ati_remote_close(struct input_dev *inputdev)
+{
+	struct ati_remote *ati_remote = inputdev->private;
+	
+	if (ati_remote == NULL) {
+		err("ati_remote: %s: object is NULL!\n", __FUNCTION__);
+		return;
+	}
+	
+	if (ati_remote->open <= 0)
+		dev_dbg(&ati_remote->interface->dev, "%s: Not open.\n", __FUNCTION__);
+	else
+		--ati_remote->open;
+	
+	/* If still present, disconnect will call delete. */
+	if (!ati_remote->present && !ati_remote->open)
+		ati_remote_delete(ati_remote);
+}
+
+/*
+ *		ati_remote_irq_out
+ */
+static void ati_remote_irq_out(struct urb *urb, struct pt_regs *regs)
+{
+	struct ati_remote *ati_remote = urb->context;
+	
+	if (urb->status) {
+		dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
+			__FUNCTION__, urb->status);
+		return;
+	}
+	
+	ati_remote->send_flags |= SEND_FLAG_COMPLETE;
+	wmb();
+	if (waitqueue_active(&ati_remote->wait))
+		wake_up(&ati_remote->wait);
+}
+
+/*
+ *	ati_remote_sendpacket
+ *		
+ *	Used to send device initialization strings
+ */
+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 */
+	memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
+	((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);	
+
+	ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
+	ati_remote->out_urb->dev = ati_remote->udev;
+	ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&ati_remote->wait, &wait);
+
+	retval = usb_submit_urb(ati_remote->out_urb, GFP_KERNEL);
+	if (retval) {
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&ati_remote->wait, &wait);
+		dev_dbg(&ati_remote->interface->dev, 
+			 "sendpacket: usb_submit_urb failed: %d\n", retval);
+		return retval;
+	}
+
+	while (timeout && (ati_remote->out_urb->status == -EINPROGRESS) 
+	       && !(ati_remote->send_flags & SEND_FLAG_COMPLETE)) {
+		timeout = schedule_timeout(timeout);
+		rmb();
+	}
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&ati_remote->wait, &wait);
+	usb_unlink_urb(ati_remote->out_urb);
+	
+	return retval;
+}
+
+/*
+ *	ati_remote_event_lookup
+ */
+static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
+{
+	int i;
+
+	for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
+		/* 
+		 * Decide if the table entry matches the remote input. 
+		 */
+		if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
+		    ((((ati_remote_tbl[i].data1 >> 4) - 
+		       (d1 >> 4) + rem) & 0x0f) == 0x0f) && 
+		    (ati_remote_tbl[i].data2 == d2))
+			return i;
+		
+	}
+	return -1;
+}
+
+/*
+ *	ati_remote_report_input
+ */
+static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs)	
+{
+	struct ati_remote *ati_remote = urb->context;
+	unsigned char *data= ati_remote->inbuf;
+	struct input_dev *dev = &ati_remote->idev; 
+	int index, acc;
+	int remote_num;
+	
+	/* Deal with strange looking inputs */
+	if ( (urb->actual_length != 4) || (data[0] != 0x14) || 
+		((data[3] & 0x0f) != 0x00) ) {
+		ati_remote_dump(data, urb->actual_length);
+		return;
+	}
+
+	/* Mask unwanted remote channels.  */
+	/* note: remote_num is 0-based, channel 1 on remote == 0 here */
+	remote_num = (data[3] >> 4) & 0x0f;
+        if (channel_mask & (1 << (remote_num + 1))) { 
+		dbginfo(&ati_remote->interface->dev,
+			"Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
+			remote_num, data[1], data[2], channel_mask);
+		return;
+	}
+
+	/* Look up event code index in translation table */
+	index = ati_remote_event_lookup(remote_num, data[1], data[2]);
+	if (index < 0) {
+		dev_warn(&ati_remote->interface->dev, 
+			 "Unknown input from channel 0x%02x: data %02x,%02x\n", 
+			 remote_num, data[1], data[2]);
+		return;
+	} 
+	dbginfo(&ati_remote->interface->dev, 
+		"channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
+		remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
+	
+	if (ati_remote_tbl[index].kind == KIND_LITERAL) {
+		input_regs(dev, regs);
+		input_event(dev, ati_remote_tbl[index].type,
+			ati_remote_tbl[index].code,
+			ati_remote_tbl[index].value);
+		input_sync(dev);
+		
+		ati_remote->old_jiffies = jiffies;
+		return;
+	}
+	
+	if (ati_remote_tbl[index].kind == KIND_FILTERED) {
+		/* Filter duplicate events which happen "too close" together. */
+		if ((ati_remote->old_data[0] == data[1]) && 
+	 		(ati_remote->old_data[1] == data[2]) && 
+	 		((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) {
+			ati_remote->old_jiffies = jiffies;			
+			return;
+		}		
+
+		input_regs(dev, regs);
+		input_event(dev, ati_remote_tbl[index].type,
+			ati_remote_tbl[index].code, 1);
+		input_event(dev, ati_remote_tbl[index].type,
+			ati_remote_tbl[index].code, 0);
+		input_sync(dev);
+
+		ati_remote->old_data[0] = data[1];
+		ati_remote->old_data[1] = data[2];
+		ati_remote->old_jiffies = jiffies;
+		return;
+	}			
+	
+	/* 
+	 * Other event kinds are from the directional control pad, and have an
+	 * acceleration factor applied to them.  Without this acceleration, the
+	 * control pad is mostly unusable.
+	 * 
+	 * If elapsed time since last event is > 1/4 second, user "stopped",
+	 * so reset acceleration. Otherwise, user is probably holding the control
+	 * pad down, so we increase acceleration, ramping up over two seconds to
+	 * a maximum speed.  The acceleration curve is #defined above.
+	 */
+	if ((jiffies - ati_remote->old_jiffies) > (HZ >> 2)) {
+		acc = 1;
+		ati_remote->acc_jiffies = jiffies;
+	}
+	else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 3))  acc = accel[0];
+	else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 2))  acc = accel[1];
+	else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 1))  acc = accel[2];
+	else if ((jiffies - ati_remote->acc_jiffies) < HZ )        acc = accel[3];
+	else if ((jiffies - ati_remote->acc_jiffies) < HZ+(HZ>>1)) acc = accel[4];
+	else if ((jiffies - ati_remote->acc_jiffies) < (HZ << 1))  acc = accel[5];
+	else acc = accel[6];
+
+	input_regs(dev, regs);
+	switch (ati_remote_tbl[index].kind) {
+	case KIND_ACCEL:
+		input_event(dev, ati_remote_tbl[index].type,
+			ati_remote_tbl[index].code,
+			ati_remote_tbl[index].value * acc);
+		break;
+	case KIND_LU:
+		input_report_rel(dev, REL_X, -acc);
+		input_report_rel(dev, REL_Y, -acc);
+		break;
+	case KIND_RU:
+		input_report_rel(dev, REL_X, acc);
+		input_report_rel(dev, REL_Y, -acc);
+		break;
+	case KIND_LD:
+		input_report_rel(dev, REL_X, -acc);
+		input_report_rel(dev, REL_Y, acc);
+		break;
+	case KIND_RD:
+		input_report_rel(dev, REL_X, acc);
+		input_report_rel(dev, REL_Y, acc);
+		break;
+	default:
+		dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", 
+			ati_remote_tbl[index].kind);
+	}
+	input_sync(dev);
+
+	ati_remote->old_jiffies = jiffies;
+	ati_remote->old_data[0] = data[1];
+	ati_remote->old_data[1] = data[2];
+}
+
+/*
+ *	ati_remote_irq_in
+ */
+static void ati_remote_irq_in(struct urb *urb, struct pt_regs *regs)
+{
+	struct ati_remote *ati_remote = urb->context;
+	int retval;
+
+	switch (urb->status) {
+	case 0:			/* success */
+		ati_remote_input_report(urb, regs);
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
+			__FUNCTION__);
+		return;	
+	default:		/* error */
+		dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n", 
+			__FUNCTION__, urb->status);
+	}
+	
+	retval = usb_submit_urb(urb, SLAB_ATOMIC);
+	if (retval)
+		dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
+			__FUNCTION__, retval);
+}
+
+/*
+ *	ati_remote_delete
+ */
+static void ati_remote_delete(struct ati_remote *ati_remote)
+{
+	if (!ati_remote) return;
+
+	if (ati_remote->irq_urb)
+		usb_unlink_urb(ati_remote->irq_urb);
+
+	if (ati_remote->out_urb)
+		usb_unlink_urb(ati_remote->out_urb);
+
+	if (ati_remote->irq_urb)
+		usb_free_urb(ati_remote->irq_urb);
+	
+	if (ati_remote->out_urb)
+		usb_free_urb(ati_remote->out_urb);
+
+	if (ati_remote->inbuf)
+		usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, 
+				ati_remote->inbuf, ati_remote->inbuf_dma);
+		
+	if (ati_remote->outbuf)
+		usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, 
+				ati_remote->inbuf, ati_remote->outbuf_dma);
+	
+	kfree(ati_remote);
+}
+
+static void ati_remote_input_init(struct ati_remote *ati_remote)
+{
+	struct input_dev *idev = &(ati_remote->idev);
+	int i;
+
+	idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) | 
+					  BIT(BTN_SIDE) | BIT(BTN_EXTRA) );
+	idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+	for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
+		if (ati_remote_tbl[i].type == EV_KEY)
+			set_bit(ati_remote_tbl[i].code, idev->keybit);
+	
+	idev->private = ati_remote;
+	idev->open = ati_remote_open;
+	idev->close = ati_remote_close;
+	
+	idev->name = ati_remote->name;
+	idev->phys = ati_remote->phys;
+	
+	idev->id.bustype = BUS_USB;		
+	idev->id.vendor = ati_remote->udev->descriptor.idVendor;
+	idev->id.product = ati_remote->udev->descriptor.idProduct;
+	idev->id.version = ati_remote->udev->descriptor.bcdDevice;
+}
+
+static int ati_remote_initialize(struct ati_remote *ati_remote)
+{
+	struct usb_device *udev = ati_remote->udev;
+	int pipe, maxp;
+		
+	init_waitqueue_head(&ati_remote->wait);
+
+	/* Set up irq_urb */
+	pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
+	maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+	
+	usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, 
+			 maxp, ati_remote_irq_in, ati_remote, 
+			 ati_remote->endpoint_in->bInterval);
+	ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
+	ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	
+	/* Set up out_urb */
+	pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
+	maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+	usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, 
+			 maxp, ati_remote_irq_out, ati_remote, 
+			 ati_remote->endpoint_out->bInterval);
+	ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
+	ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* send initialization strings */
+	if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
+	    (ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
+		dev_err(&ati_remote->interface->dev, 
+			 "Initializing ati_remote hardware failed.\n");
+		return 1;
+	}
+	
+	return 0;
+}
+
+/*
+ *	ati_remote_probe
+ */
+static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct ati_remote *ati_remote = NULL;
+	struct usb_host_interface *iface_host;
+	int retval = -ENOMEM;
+	char path[64];
+	char *buf = NULL;
+
+	/* See if the offered device matches what we can accept */
+	if ((udev->descriptor.idVendor != ATI_REMOTE_VENDOR_ID) ||
+	    (udev->descriptor.idProduct != ATI_REMOTE_PRODUCT_ID)) {
+		return -ENODEV;
+	}
+
+	/* Allocate and clear an ati_remote struct */
+	if (!(ati_remote = kmalloc(sizeof (struct ati_remote), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(ati_remote, 0x00, sizeof (struct ati_remote));
+
+	iface_host = &interface->altsetting[interface->act_altsetting];
+	if (iface_host->desc.bNumEndpoints != 2) {
+		err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	ati_remote->endpoint_in = &(iface_host->endpoint[0].desc);
+	ati_remote->endpoint_out = &(iface_host->endpoint[1].desc);
+	ati_remote->udev = udev;
+	ati_remote->interface = interface;
+
+	if (!(ati_remote->endpoint_in->bEndpointAddress & 0x80)) {
+		err("%s: Unexpected endpoint_in->bEndpointAddress\n", __FUNCTION__);
+		retval = -ENODEV;
+		goto error;
+	}
+	if ((ati_remote->endpoint_in->bmAttributes & 3) != 3) {
+		err("%s: Unexpected endpoint_in->bmAttributes\n", __FUNCTION__);
+		retval = -ENODEV;
+		goto error;
+	}
+	if (ati_remote->endpoint_in->wMaxPacketSize == 0) {
+		err("%s: endpoint_in message size==0? \n", __FUNCTION__);
+		retval = -ENODEV;
+		goto error;
+	}
+	if (!(buf = kmalloc(NAME_BUFSIZE, GFP_KERNEL)))
+		goto error;
+
+	/* Allocate URB buffers, URBs */
+	ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, SLAB_ATOMIC,
+					     &ati_remote->inbuf_dma);
+	if (!ati_remote->inbuf)
+		goto error;
+
+	ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, SLAB_ATOMIC,
+					      &ati_remote->outbuf_dma);
+	if (!ati_remote->outbuf)
+		goto error;
+
+	ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ati_remote->irq_urb)
+		goto error;
+
+	ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ati_remote->out_urb)
+		goto error;
+
+	usb_make_path(udev, path, NAME_BUFSIZE);
+	sprintf(ati_remote->phys, "%s/input%d", path, ATI_INPUTNUM);
+	if (udev->descriptor.iManufacturer && 
+	    (usb_string(udev, udev->descriptor.iManufacturer, buf, 
+			NAME_BUFSIZE) > 0))
+		strcat(ati_remote->name, buf);
+
+	if (udev->descriptor.iProduct && 
+	    (usb_string(udev, udev->descriptor.iProduct, buf, NAME_BUFSIZE) > 0))
+		sprintf(ati_remote->name, "%s %s", ati_remote->name, buf);
+
+	if (!strlen(ati_remote->name))
+		sprintf(ati_remote->name, DRIVER_DESC "(%04x,%04x)",
+			ati_remote->udev->descriptor.idVendor, 
+			ati_remote->udev->descriptor.idProduct);
+
+	/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
+	retval = ati_remote_initialize(ati_remote);
+	if (retval)
+		goto error;
+
+	/* Set up and register input device */
+	ati_remote_input_init(ati_remote);
+	input_register_device(&ati_remote->idev);
+
+	dev_info(&ati_remote->interface->dev, "Input registered: %s on %s\n", 
+		 ati_remote->name, path);
+
+	usb_set_intfdata(interface, ati_remote);
+	ati_remote->present = 1;	
+	kfree(buf);
+	return 0;
+	
+error:
+	if (buf)
+		kfree(buf);
+
+	ati_remote_delete(ati_remote);
+	return retval;
+}
+
+/*
+ *	ati_remote_disconnect
+ */
+static void ati_remote_disconnect(struct usb_interface *interface)
+{
+	struct ati_remote *ati_remote;
+	
+	ati_remote = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+	if (!ati_remote) {
+		warn("%s - null device?\n", __FUNCTION__);
+		return;
+	}
+	
+	input_unregister_device(&ati_remote->idev);
+
+	/* Mark device as unplugged */
+	ati_remote->present = 0;
+
+	/* If device is still open, ati_remote_close will call delete. */
+	if (!ati_remote->open)
+		ati_remote_delete(ati_remote);
+}
+
+/*
+ *	ati_remote_init
+ */
+static int __init ati_remote_init(void)
+{
+	int result;
+	
+	result = usb_register(&ati_remote_driver);
+	if (result)
+		err("usb_register error #%d\n", result);
+	else
+		info("Registered USB driver " DRIVER_DESC " v. " DRIVER_VERSION);
+
+	return result;
+}
+
+/*
+ *	ati_remote_exit
+ */
+static void __exit ati_remote_exit(void)
+{
+	usb_deregister(&ati_remote_driver);
+}
+
+/* 
+ *	module specification 
+ */
+
+module_init(ati_remote_init);
+module_exit(ati_remote_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
diff -Nru a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c
--- a/drivers/usb/input/kbtab.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/input/kbtab.c	Sun Feb 29 13:05:40 2004
@@ -74,12 +74,15 @@
 
 	input_report_abs(dev, ABS_X, kbtab->x);
 	input_report_abs(dev, ABS_Y, kbtab->y);
-	/*input_report_abs(dev, ABS_PRESSURE, kbtab->pressure);*/
 
 	/*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
 	input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
-	
-	input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0);
+
+	if( -1 == kb_pressure_click){ 
+		input_report_abs(dev, ABS_PRESSURE, kbtab->pressure);
+	} else {
+		input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0);
+	};
 	
 	input_sync(dev);
 
diff -Nru a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/mtouchusb.c	Sun Feb 29 13:05:40 2004
@@ -0,0 +1,391 @@
+/******************************************************************************
+ * mtouchusb.c  --  Driver for Microtouch (Now 3M) USB Touchscreens
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon original work by Radoslaw Garbacz (usb-support@ite.pl)
+ *  (http://freshmeat.net/projects/3mtouchscreendriver)
+ *
+ * History
+ *
+ *  0.3 & 0.4  2002 (TEJ) tejohnson@yahoo.com
+ *    Updated to 2.4.18, then 2.4.19
+ *    Old version still relied on stealing a minor
+ *
+ *  0.5  02/26/2004 (TEJ) tejohnson@yahoo.com
+ *    Complete rewrite using Linux Input in 2.6.3
+ *    Unfortunately no calibration support at this time
+ *
+ *****************************************************************************/
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+        #define DEBUG
+#else
+        #undef DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+#define MTOUCHUSB_MIN_XC                0xc8
+#define MTOUCHUSB_MAX_XC                0xff78
+#define MTOUCHUSB_XC_FUZZ               0x0
+#define MTOUCHUSB_XC_FLAT               0x0
+#define MTOUCHUSB_MIN_YC                0x0
+#define MTOUCHUSB_MAX_YC                0xff78
+#define MTOUCHUSB_YC_FUZZ               0x0
+#define MTOUCHUSB_YC_FLAT               0x0
+#define MTOUCHUSB_ASYC_REPORT           1
+#define MTOUCHUSB_REPORT_SIZE_DATA      11
+#define MTOUCHUSB_REQ_CTRLLR_ID         10
+
+#define MTOUCHUSB_GET_XC(data)          (data[4]<<8 | data[3])
+#define MTOUCHUSB_GET_YC(data)          (data[6]<<8 | data[5])
+#define MTOUCHUSB_GET_TOUCHED(data)     ((data[2] & 0x40) ? 1:0)
+
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Todd E. Johnson, tejohnson@yahoo.com"
+#define DRIVER_DESC "Microtouch USB HID Touchscreen Driver"
+
+struct mtouch_usb {
+        unsigned char *data;
+        dma_addr_t data_dma;
+        struct urb *irq;
+        struct usb_device *udev;
+        struct input_dev input;
+        int open;
+        char name[128];
+        char phys[64];
+};
+
+static __s32 vendor=-1, product=-1;
+
+static struct usb_device_id mtouchusb_devices [] = {
+        { USB_DEVICE(0x0596, 0x0001) }, /* 3M (Formerly MicroTouch) 14-206 */
+        { }                             /* Terminating entry */
+};
+
+static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs)
+{
+        struct mtouch_usb *mtouch = urb->context;
+        int retval;
+
+        switch (urb->status) {
+                case 0:
+                        /* success */
+                        break;
+                case -ETIMEDOUT:
+                        /* this urb is timing out */
+                        dbg("%s - urb timed out - was the device unplugged?",
+                            __FUNCTION__);
+                        return;
+                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;
+        }
+
+        input_regs(&mtouch->input, regs);
+        input_report_key(&mtouch->input, BTN_TOUCH,
+                         MTOUCHUSB_GET_TOUCHED(mtouch->data));
+        input_report_abs(&mtouch->input, ABS_X,
+                         MTOUCHUSB_GET_XC(mtouch->data));
+        input_report_abs(&mtouch->input, ABS_Y,
+                         MTOUCHUSB_GET_YC(mtouch->data));
+        input_sync(&mtouch->input);
+
+exit:
+        retval = usb_submit_urb (urb, GFP_ATOMIC);
+        if (retval)
+                err ("%s - usb_submit_urb failed with result: %d",
+                     __FUNCTION__, retval);
+}
+
+static int mtouchusb_open (struct input_dev *input)
+{
+        struct mtouch_usb *mtouch = input->private;
+
+        if (mtouch->open++)
+                return 0;
+
+        mtouch->irq->dev = mtouch->udev;
+
+        if (usb_submit_urb (mtouch->irq, GFP_ATOMIC))
+                return -EIO;
+
+        return 0;
+}
+
+static void mtouchusb_close (struct input_dev *input)
+{
+        struct mtouch_usb *mtouch = input->private;
+
+        if (!--mtouch->open)
+                usb_unlink_urb (mtouch->irq);
+}
+
+static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
+{
+        dbg("%s - called", __FUNCTION__);
+
+        mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_SIZE_DATA,
+                                        SLAB_ATOMIC, &mtouch->data_dma);
+
+        if (!mtouch->data)
+                return -1;
+
+        return 0;
+}
+
+static void mtouchusb_free_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
+{
+        dbg("%s - called", __FUNCTION__);
+
+        if (mtouch->data)
+                usb_buffer_free(udev, MTOUCHUSB_REPORT_SIZE_DATA,
+                                mtouch->data, mtouch->data_dma);
+}
+
+static int mtouchusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+        struct mtouch_usb *mtouch;
+        struct usb_host_interface *interface;
+        struct usb_endpoint_descriptor *endpoint;
+        struct usb_device *udev = interface_to_usbdev (intf);
+        char path[64];
+        char *buf;
+        int nRet;
+        int ix;
+        char valid_device = 0;
+
+        dbg("%s - called", __FUNCTION__);
+        if (vendor != -1 && product != -1) {
+                info("%s - User specified USB Touch -- Vend:Prod - %x:%x",
+                     __FUNCTION__, vendor, product);
+        }
+
+        for (ix = 0; ix < sizeof (mtouchusb_devices) /
+             sizeof (struct usb_device_id); ix++) {
+                if ((udev->descriptor.idVendor ==
+                     mtouchusb_devices [ix].idVendor) &&
+                     (udev->descriptor.idProduct ==
+                     mtouchusb_devices [ix].idProduct)) {
+                        valid_device = 1;
+                        break;
+                }
+        }
+
+        if (udev->descriptor.idVendor == vendor &&
+            udev->descriptor.idProduct == product) {  /* User specified */
+                valid_device = 1;
+        }
+
+        if (!valid_device) {
+                err("%s - No valid device!", __FUNCTION__);
+                return -EIO;
+        }
+
+        if (udev->descriptor.bNumConfigurations != 1) {
+                err("%s -  Only one device configuration is supported.",
+                    __FUNCTION__);
+                return -EIO;
+        }
+
+        dbg("%s - setting interface", __FUNCTION__);
+        interface = &intf->altsetting[intf->act_altsetting];
+
+        dbg("%s - setting endpoint", __FUNCTION__);
+        endpoint = &interface->endpoint[0].desc;
+
+        if (interface->desc.bNumEndpoints != 1) {
+                err("%s - Only one endpoint is supported.", __FUNCTION__);
+                return -EIO;
+        }
+
+        if (!(mtouch = kmalloc (sizeof (struct mtouch_usb), GFP_KERNEL))) {
+                err("%s - Out of memory.", __FUNCTION__);
+                return -ENOMEM;
+        }
+
+        memset(mtouch, 0, sizeof(struct mtouch_usb));
+        mtouch->udev = udev;
+
+        dbg("%s - allocating buffers", __FUNCTION__);
+        if (mtouchusb_alloc_buffers(udev, mtouch)) {
+                mtouchusb_free_buffers(udev, mtouch);
+                kfree(mtouch);
+                return -ENOMEM;
+        }
+
+        mtouch->input.private = mtouch;
+        mtouch->input.open = mtouchusb_open;
+        mtouch->input.close = mtouchusb_close;
+
+        usb_make_path(udev, path, 64);
+        sprintf(mtouch->phys, "%s/input0", path);
+
+        mtouch->input.name = mtouch->name;
+        mtouch->input.phys = mtouch->phys;
+        mtouch->input.id.bustype = BUS_USB;
+        mtouch->input.id.vendor = udev->descriptor.idVendor;
+        mtouch->input.id.product = udev->descriptor.idProduct;
+        mtouch->input.id.version = udev->descriptor.bcdDevice;
+        mtouch->input.dev = &intf->dev;
+
+        mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+        mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+        mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+        /* Used to Scale Compensated Data and Flip Y */
+        mtouch->input.absmin[ABS_X] =  MTOUCHUSB_MIN_XC;
+        mtouch->input.absmax[ABS_X] =  MTOUCHUSB_MAX_XC;
+        mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ;
+        mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT;
+        mtouch->input.absmin[ABS_Y] =  MTOUCHUSB_MAX_YC;
+        mtouch->input.absmax[ABS_Y] =  MTOUCHUSB_MIN_YC;
+        mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ;
+        mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT;
+
+        if (!(buf = kmalloc(63, GFP_KERNEL))) {
+                kfree(mtouch);
+                return -ENOMEM;
+        }
+
+        if (udev->descriptor.iManufacturer &&
+            usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0)
+                        strcat(mtouch->name, buf);
+        if (udev->descriptor.iProduct &&
+            usb_string(udev, udev->descriptor.iProduct, buf, 63) > 0)
+                        sprintf(mtouch->name, "%s %s", mtouch->name, buf);
+
+        if (!strlen(mtouch->name))
+                sprintf(mtouch->name, "USB Touchscreen %04x:%04x",
+                        mtouch->input.id.vendor, mtouch->input.id.product);
+
+        kfree(buf);
+
+        nRet = usb_control_msg(mtouch->udev,
+                               usb_rcvctrlpipe(udev, 0x80),
+                               USB_REQ_GET_CONFIGURATION,
+                               USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+                               0,
+                               0x81,
+                               NULL,
+                               0,
+                               HZ * USB_CTRL_SET_TIMEOUT);
+        dbg("%s - usb_control_msg - USB_REQ_GET_CONFIGURATION - bytes|err: %d",
+            __FUNCTION__, nRet);
+
+        dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__);
+        mtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
+        if (!mtouch->irq) {
+                dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__);
+                mtouchusb_free_buffers(udev, mtouch);
+                kfree(mtouch);
+                return -ENOMEM;
+        }
+
+        dbg("%s - usb_fill_int_urb", __FUNCTION__);
+        usb_fill_int_urb(mtouch->irq,
+                         mtouch->udev,
+                         usb_rcvintpipe(mtouch->udev, 0x81),
+                         mtouch->data,
+                         MTOUCHUSB_REPORT_SIZE_DATA,
+                         mtouchusb_irq,
+                         mtouch,
+                         endpoint->bInterval);
+
+        dbg("%s - input_register_device", __FUNCTION__);
+        input_register_device(&mtouch->input);
+
+        nRet = usb_control_msg(mtouch->udev,
+                               usb_rcvctrlpipe(udev, 0x80),
+                               MTOUCHUSB_ASYC_REPORT,
+                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                               MTOUCHUSB_ASYC_REPORT,
+                               MTOUCHUSB_ASYC_REPORT,
+                               NULL,
+                               0,
+                               HZ * USB_CTRL_SET_TIMEOUT);
+        dbg("%s - usb_control_msg - MTOUCHUSB_ASYC_REPORT - bytes|err: %d",
+            __FUNCTION__, nRet);
+
+        printk(KERN_INFO "input: %s on %s\n", mtouch->name, path);
+        usb_set_intfdata(intf, mtouch);
+
+        return 0;
+}
+
+static void mtouchusb_disconnect(struct usb_interface *intf)
+{
+        struct mtouch_usb *mtouch = usb_get_intfdata (intf);
+
+        dbg("%s - called", __FUNCTION__);
+        usb_set_intfdata(intf, NULL);
+        if (mtouch) {
+                dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__);
+                usb_unlink_urb(mtouch->irq);
+                input_unregister_device(&mtouch->input);
+                usb_free_urb(mtouch->irq);
+                mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch);
+                kfree(mtouch);
+        }
+}
+
+MODULE_DEVICE_TABLE (usb, mtouchusb_devices);
+
+static struct usb_driver mtouchusb_driver = {
+        .owner =      THIS_MODULE,
+        .name =       "mtouchusb",
+        .probe =      mtouchusb_probe,
+        .disconnect = mtouchusb_disconnect,
+        .id_table =   mtouchusb_devices,
+};
+
+static int __init mtouchusb_init(void) {
+        dbg("%s - called", __FUNCTION__);
+        return usb_register(&mtouchusb_driver);
+}
+
+static void __exit mtouchusb_cleanup(void) {
+        dbg("%s - called", __FUNCTION__);
+        usb_deregister(&mtouchusb_driver);
+}
+
+module_init(mtouchusb_init);
+module_exit(mtouchusb_cleanup);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+MODULE_PARM(vendor, "i");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+MODULE_PARM(product, "i");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+
diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/net/usbnet.c	Sun Feb 29 13:05:40 2004
@@ -3314,6 +3314,15 @@
 	.bInterfaceSubClass     = 0x0a,
 	.bInterfaceProtocol     = 0x00,
 	.driver_info =  (unsigned long) &zaurus_pxa_info,
+}, {
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+		 | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor               = 0x04DD,
+	.idProduct              = 0x9050,	/* C-860 */
+	.bInterfaceClass        = 0x02,
+	.bInterfaceSubClass     = 0x0a,
+	.bInterfaceProtocol     = 0x00,
+	.driver_info =  (unsigned long) &zaurus_pxa_info,
 },
 #endif
 
diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
--- a/drivers/usb/serial/ftdi_sio.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/serial/ftdi_sio.c	Sun Feb 29 13:05:40 2004
@@ -286,6 +286,7 @@
 
 
 static struct usb_device_id id_table_8U232AM [] = {
+	{ 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_RELAIS_PID, 0, 0x3ff) },
@@ -358,6 +359,7 @@
 
 
 static struct usb_device_id id_table_FT232BM [] = {
+	{ 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_RELAIS_PID, 0x400, 0xffff) },
@@ -451,6 +453,7 @@
 
 
 static struct usb_device_id id_table_combined [] = {
+	{ USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
diff -Nru a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
--- a/drivers/usb/serial/ftdi_sio.h	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/serial/ftdi_sio.h	Sun Feb 29 13:05:40 2004
@@ -30,6 +30,8 @@
 #define FTDI_NF_RIC_VID	0x0DCD	/* Vendor Id */
 #define FTDI_NF_RIC_PID	0x0001	/* Product Id */
 
+/* www.irtrans.de device */
+#define FTDI_IRTRANS_PID 0xFC60 /* Product Id */
 
 /* www.crystalfontz.com devices - thanx for providing free devices for evaluation ! */
 /* they use the ftdi chipset for the USB interface and the vendor id is the same */
diff -Nru a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
--- a/drivers/usb/serial/kl5kusb105.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/serial/kl5kusb105.c	Sun Feb 29 13:05:40 2004
@@ -273,6 +273,7 @@
 
 	/* allocate the private data structure */
 	for (i=0; i<serial->num_ports; i++) {
+		int j;
 		priv = kmalloc(sizeof(struct klsi_105_private),
 						   GFP_KERNEL);
 		if (!priv) {
@@ -293,10 +294,10 @@
 		usb_set_serial_port_data(serial->port[i], priv);
 
 		spin_lock_init (&priv->lock);
-		for (i=0; i<NUM_URBS; i++) {
+		for (j=0; j<NUM_URBS; j++) {
 			struct urb* urb = usb_alloc_urb(0, GFP_KERNEL);
 
-			priv->write_urb_pool[i] = urb;
+			priv->write_urb_pool[j] = urb;
 			if (urb == NULL) {
 				err("No more urbs???");
 				continue;
diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
--- a/drivers/usb/storage/transport.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/storage/transport.c	Sun Feb 29 13:05:40 2004
@@ -563,9 +563,9 @@
 
 	/*
 	 * If we're running the CB transport, which is incapable
-	 * of determining status on its own, we need to auto-sense
+	 * of determining status on its own, we will auto-sense
 	 * unless the operation involved a data-in transfer.  Devices
-	 * can signal data-in errors by stalling the bulk-in pipe.
+	 * can signal most data-in errors by stalling the bulk-in pipe.
 	 */
 	if ((us->protocol == US_PR_CB || us->protocol == US_PR_DPCM_USB) &&
 			srb->sc_data_direction != SCSI_DATA_READ) {
@@ -698,7 +698,11 @@
 		 * out the sense buffer so the higher layers won't realize
 		 * we did an unsolicited auto-sense. */
 		if (result == USB_STOR_TRANSPORT_GOOD &&
-				(srb->sense_buffer[2] & 0xf) == 0x0) {
+			/* Filemark 0, ignore EOM, ILI 0, no sense */
+				(srb->sense_buffer[2] & 0xaf) == 0 &&
+			/* No ASC or ASCQ */
+				srb->sense_buffer[12] == 0 &&
+				srb->sense_buffer[13] == 0) {
 			srb->result = SAM_STAT_GOOD;
 			srb->sense_buffer[0] = 0x0;
 		}
@@ -809,15 +813,19 @@
 	}
 
 	/* If not UFI, we interpret the data as a result code 
-	 * The first byte should always be a 0x0
-	 * The second byte & 0x0F should be 0x0 for good, otherwise error 
+	 * The first byte should always be a 0x0.
+	 *
+	 * Some bogus devices don't follow that rule.  They stuff the ASC
+	 * into the first byte -- so if it's non-zero, call it a failure.
 	 */
 	if (us->iobuf[0]) {
-		US_DEBUGP("CBI IRQ data showed reserved bType %d\n",
+		US_DEBUGP("CBI IRQ data showed reserved bType 0x%x\n",
 				us->iobuf[0]);
-		return USB_STOR_TRANSPORT_ERROR;
+		goto Failed;
+
 	}
 
+	/* The second byte & 0x0F should be 0x0 for good, otherwise error */
 	switch (us->iobuf[1] & 0x0F) {
 		case 0x00: 
 			return USB_STOR_TRANSPORT_GOOD;
diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
--- a/drivers/usb/storage/unusual_devs.h	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/storage/unusual_devs.h	Sun Feb 29 13:05:40 2004
@@ -261,6 +261,14 @@
 		US_SC_SCSI, US_PR_DEVICE, NULL,
 		US_FL_SINGLE_LUN | US_FL_MODE_XLATE ),
 
+/* This entry is needed because the device reports Sub=ff */
+UNUSUAL_DEV(  0x054c, 0x0010, 0x0500, 0x0500, 
+               "Sony",
+               "DSC-T1", 
+               US_SC_8070, US_PR_DEVICE, NULL,
+               US_FL_SINGLE_LUN | US_FL_MODE_XLATE ),
+
+
 /* Reported by wim@geeks.nl */
 UNUSUAL_DEV(  0x054c, 0x0025, 0x0100, 0x0100, 
 		"Sony",
@@ -368,7 +376,7 @@
 UNUSUAL_DEV(  0x05dc, 0x0001, 0x0000, 0x0001,
 		"Lexar",
 		"Jumpshot USB CF Reader",
-		US_SC_SCSI, US_PR_JUMPSHOT, NULL,
+		US_SC_DEVICE, US_PR_JUMPSHOT, NULL,
 		US_FL_MODE_XLATE ),
 #endif
 
@@ -440,12 +448,6 @@
 		US_SC_SCSI, US_PR_DEVICE, NULL,
 		0 ),
 
-UNUSUAL_DEV( 0x0686, 0x400b, 0x0001, 0x0001,
-		"Minolta",
-		"DiMAGE 7i",
-		US_SC_SCSI, US_PR_DEVICE, NULL,
-		0 ),
-
 UNUSUAL_DEV( 0x0686, 0x400f, 0x0001, 0x0001,
 		"Minolta",
 		"DiMAGE 7Hi",
@@ -619,6 +621,9 @@
  *   are using transport protocol CB.
  * - They don't like the INQUIRY command. So we must handle this command
  *   of the SCSI layer ourselves.
+ * - Some cameras with idProduct=0x1001 and bcdDevice=0x1000 have
+ *   bInterfaceProtocol=0x00 (US_PR_CBI) while others have 0x01 (US_PR_CB).
+ *   So don't remove the US_PR_CB override!
  */
 UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9009,
 		"Casio",
diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
--- a/drivers/usb/storage/usb.c	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/storage/usb.c	Sun Feb 29 13:05:40 2004
@@ -954,8 +954,6 @@
 	scsi_scan_host(us->host);
 
 	printk(KERN_DEBUG 
-	       "WARNING: USB Mass Storage data integrity not assured\n");
-	printk(KERN_DEBUG 
 	       "USB Mass Storage device found at %d\n", us->pusb_dev->devnum);
 	return 0;
 
diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
--- a/drivers/usb/storage/usb.h	Sun Feb 29 13:05:40 2004
+++ b/drivers/usb/storage/usb.h	Sun Feb 29 13:05:40 2004
@@ -176,6 +176,5 @@
  * single queue element srb for write access */
 #define scsi_unlock(host)	spin_unlock_irq(host->host_lock)
 #define scsi_lock(host)		spin_lock_irq(host->host_lock)
-#define sg_address(psg)		(page_address((psg).page) + (psg).offset)
 
 #endif
diff -Nru a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h
--- a/include/linux/usb_gadget.h	Sun Feb 29 13:05:40 2004
+++ b/include/linux/usb_gadget.h	Sun Feb 29 13:05:40 2004
@@ -716,6 +716,17 @@
 /* put descriptor for string with that id into buf (buflen >= 256) */
 int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf);
 
+/*-------------------------------------------------------------------------*/
+
+/* utility to simplify managing config descriptors */
+
+/* write vector of descriptors into buffer */
+int usb_descriptor_fillbuf(void *, unsigned,
+		const struct usb_descriptor_header **);
+
+/* build config descriptor from single descriptor vector */
+int usb_gadget_config_buf(const struct usb_config_descriptor *config,
+	void *buf, unsigned buflen, const struct usb_descriptor_header **desc);
 
 #endif  /* __KERNEL__ */