aboutsummaryrefslogtreecommitdiffstats
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2009-01-25 15:42:36 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-25 15:42:36 -0800
commitc2740cbf8a1afa5e0699896629210858923e98e1 (patch)
tree99c2968ac29219d4246e3c08194b54fb54694b76
parent6f1bc475e2d301aa2909e1bd3c5105e51851d4b8 (diff)
downloadpatches-c2740cbf8a1afa5e0699896629210858923e98e1.tar.gz
more patches added
-rw-r--r--driver-core.current/driver-core-fix-kernel-doc-parameter-name.patch35
-rw-r--r--driver-core/driver-core-check-bus-match-without-holding-device-lock.patch115
-rw-r--r--driver-core/sysfs-take-sysfs_mutex-when-fetching-the-root-inode.patch36
-rw-r--r--driver-core/sysfs-use-standard-magic.h-for-sysfs.patch34
-rw-r--r--series16
-rw-r--r--usb.current/usb-cdc-acm-add-another-conexant-modem-to-the-quirks.patch32
-rw-r--r--usb.current/usb-gadget-fix-x-y.patch30
-rw-r--r--usb.current/usb-new-id-for-ti_usb_3410_5052-driver.patch57
-rw-r--r--usb.current/usb-option-add-quanta-hsdpa-data-card-device-ids.patch48
-rw-r--r--usb.current/usb-storage-add-another-unusual_dev-for-off-by-one-bug.patch38
-rw-r--r--usb.current/usb-unusual_dev-usb-storage-needs-to-ignore-a-device.patch40
-rw-r--r--usb/usb-imx_udc-fix-imx-udc-gadget-bugs.patch145
-rw-r--r--usb/usb-imx_udc-fix-imx-udc-gadget-code-style.patch319
-rw-r--r--usb/usb-imx_udc-fix-imx-udc-gadget-ep0-irq-handling.patch51
-rw-r--r--usb/usb-imx_udc-fix-imx-udc-gadget-general-irq-handling.patch266
-rw-r--r--usb/usb-usb-serial-ch341-support-for-dtr-rts-cts.patch547
16 files changed, 1808 insertions, 1 deletions
diff --git a/driver-core.current/driver-core-fix-kernel-doc-parameter-name.patch b/driver-core.current/driver-core-fix-kernel-doc-parameter-name.patch
new file mode 100644
index 00000000000000..a5725cb84e5f67
--- /dev/null
+++ b/driver-core.current/driver-core-fix-kernel-doc-parameter-name.patch
@@ -0,0 +1,35 @@
+From randy.dunlap@oracle.com Sun Jan 25 15:22:23 2009
+From: Randy Dunlap <randy.dunlap@oracle.com>
+Date: Tue, 20 Jan 2009 16:29:13 -0800
+Subject: driver-core: fix kernel-doc parameter name
+To: lkml <linux-kernel@vger.kernel.org>
+Cc: gregkh <greg@kroah.com>, Mark McLoughlin <markmc@redhat.com>
+Message-ID: <20090120162913.fffe3305.randy.dunlap@oracle.com>
+
+
+From: Randy Dunlap <randy.dunlap@oracle.com>
+
+Fix function parameter name in kernel-doc:
+
+Warning(linux-next-20090120//drivers/base/core.c:1289): No description found for parameter 'dev'
+Warning(linux-next-20090120//drivers/base/core.c:1289): Excess function parameter 'root' description in 'root_device_unregister'
+
+Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
+Acked-by: Mark McLoughlin <markmc@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/base/core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/base/core.c
++++ b/drivers/base/core.c
+@@ -1280,7 +1280,7 @@ EXPORT_SYMBOL_GPL(__root_device_register
+
+ /**
+ * root_device_unregister - unregister and free a root device
+- * @root: device going away.
++ * @dev: device going away
+ *
+ * This function unregisters and cleans up a device that was created by
+ * root_device_register().
diff --git a/driver-core/driver-core-check-bus-match-without-holding-device-lock.patch b/driver-core/driver-core-check-bus-match-without-holding-device-lock.patch
new file mode 100644
index 00000000000000..96f23d21a7f2b0
--- /dev/null
+++ b/driver-core/driver-core-check-bus-match-without-holding-device-lock.patch
@@ -0,0 +1,115 @@
+From tom.leiming@gmail.com Sun Jan 25 15:25:18 2009
+From: tom.leiming@gmail.com
+Date: Wed, 21 Jan 2009 23:27:47 +0800
+Subject: driver core: check bus->match without holding device lock
+To: kay.sievers@vrfy.org, greg@kroah.com
+Cc: cornelia.huck@de.ibm.com, arjan@linux.intel.com, Ming Lei <tom.leiming@gmail.com>
+Message-ID: <1232551667-4829-1-git-send-email-tom.leiming@gmail.com>
+
+
+From: Ming Lei <tom.leiming@gmail.com>
+
+This patch moves bus->match out from driver_probe_device and
+does not hold device lock to check the match between a device
+and a driver.
+
+The idea has been verified by the commit 6cd495860901,
+which leads to a faster boot. But the commit 6cd495860901 has
+the following drawbacks: 1),only does the quick check in
+the path of __driver_attach->driver_probe_device, not in other
+paths; 2),for a matched device and driver, check the same match
+twice. It is a waste of cpu ,especially for some drivers with long
+device id table (eg. usb-storage driver).
+
+This patch adds a helper of driver_match_device to check the match
+in all paths, and testes the match only once.
+
+Signed-off-by: Ming Lei <tom.leiming@gmail.com>
+Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/base/base.h | 5 +++++
+ drivers/base/bus.c | 2 +-
+ drivers/base/dd.c | 19 +++++++------------
+ 3 files changed, 13 insertions(+), 13 deletions(-)
+
+--- a/drivers/base/base.h
++++ b/drivers/base/base.h
+@@ -86,6 +86,11 @@ extern void bus_remove_driver(struct dev
+
+ extern void driver_detach(struct device_driver *drv);
+ extern int driver_probe_device(struct device_driver *drv, struct device *dev);
++static inline int driver_match_device(struct device_driver *drv,
++ struct device *dev)
++{
++ return drv->bus->match && drv->bus->match(dev, drv);
++}
+
+ extern void sysdev_shutdown(void);
+ extern int sysdev_suspend(pm_message_t state);
+--- a/drivers/base/bus.c
++++ b/drivers/base/bus.c
+@@ -198,7 +198,7 @@ static ssize_t driver_bind(struct device
+ int err = -ENODEV;
+
+ dev = bus_find_device_by_name(bus, NULL, buf);
+- if (dev && dev->driver == NULL) {
++ if (dev && dev->driver == NULL && driver_match_device(drv, dev)) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ down(&dev->sem);
+--- a/drivers/base/dd.c
++++ b/drivers/base/dd.c
+@@ -172,14 +172,8 @@ int driver_probe_done(void)
+ * @drv: driver to bind a device to
+ * @dev: device to try to bind to the driver
+ *
+- * First, we call the bus's match function, if one present, which should
+- * compare the device IDs the driver supports with the device IDs of the
+- * device. Note we don't do this ourselves because we don't know the
+- * format of the ID structures, nor what is to be considered a match and
+- * what is not.
+- *
+- * This function returns 1 if a match is found, -ENODEV if the device is
+- * not registered, and 0 otherwise.
++ * This function returns -ENODEV if the device is not registered,
++ * 1 if the device is bound sucessfully and 0 otherwise.
+ *
+ * This function must be called with @dev->sem held. When called for a
+ * USB interface, @dev->parent->sem must be held as well.
+@@ -190,21 +184,22 @@ int driver_probe_device(struct device_dr
+
+ if (!device_is_registered(dev))
+ return -ENODEV;
+- if (drv->bus->match && !drv->bus->match(dev, drv))
+- goto done;
+
+ pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
+ drv->bus->name, __func__, dev_name(dev), drv->name);
+
+ ret = really_probe(dev, drv);
+
+-done:
+ return ret;
+ }
+
+ static int __device_attach(struct device_driver *drv, void *data)
+ {
+ struct device *dev = data;
++
++ if (!driver_match_device(drv, dev))
++ return 0;
++
+ return driver_probe_device(drv, dev);
+ }
+
+@@ -257,7 +252,7 @@ static int __driver_attach(struct device
+ * is an error.
+ */
+
+- if (drv->bus->match && !drv->bus->match(dev, drv))
++ if (!driver_match_device(drv, dev))
+ return 0;
+
+ if (dev->parent) /* Needed for USB */
diff --git a/driver-core/sysfs-take-sysfs_mutex-when-fetching-the-root-inode.patch b/driver-core/sysfs-take-sysfs_mutex-when-fetching-the-root-inode.patch
new file mode 100644
index 00000000000000..9f6a10ce4be843
--- /dev/null
+++ b/driver-core/sysfs-take-sysfs_mutex-when-fetching-the-root-inode.patch
@@ -0,0 +1,36 @@
+From ebiederm@xmission.com Sun Jan 25 15:25:49 2009
+From: ebiederm@xmission.com (Eric W. Biederman)
+Date: Wed, 21 Jan 2009 11:55:11 -0800
+Subject: sysfs: Take sysfs_mutex when fetching the root inode.
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: <linux-kernel@vger.kernel.org>, Al Viro <viro@ZenIV.linux.org.uk>, Tejun Heo <tj@kernel.org>, Cornelia Huck <cornelia.huck@de.ibm.com>, Andrew Morton <akpm@linux-foundation.org>
+Message-ID: <m13afch1sw.fsf@fess.ebiederm.org>
+
+
+sysfs_get_inode ultimately calls sysfs_count_nlink when the a
+directory inode is fectched. sysfs_count_nlink needs to be
+called under the sysfs_mutex to guard against the unlikely
+but possible scenario that the root directory is changing
+as we are counting the number entries in it, and just in
+general to be consistent.
+
+Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com>
+Acked-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/sysfs/mount.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/sysfs/mount.c
++++ b/fs/sysfs/mount.c
+@@ -52,7 +52,9 @@ static int sysfs_fill_super(struct super
+ sysfs_sb = sb;
+
+ /* get root inode, initialize and unlock it */
++ mutex_lock(&sysfs_mutex);
+ inode = sysfs_get_inode(&sysfs_root);
++ mutex_unlock(&sysfs_mutex);
+ if (!inode) {
+ pr_debug("sysfs: could not get root inode\n");
+ return -ENOMEM;
diff --git a/driver-core/sysfs-use-standard-magic.h-for-sysfs.patch b/driver-core/sysfs-use-standard-magic.h-for-sysfs.patch
new file mode 100644
index 00000000000000..9763c9c6ea62a6
--- /dev/null
+++ b/driver-core/sysfs-use-standard-magic.h-for-sysfs.patch
@@ -0,0 +1,34 @@
+From qhfeng.kernel@gmail.com Sun Jan 25 15:12:18 2009
+From: Qinghuang Feng <qhfeng.kernel@gmail.com>
+Date: Wed, 14 Jan 2009 15:45:13 +0800
+Subject: SYSFS: use standard magic.h for sysfs
+To: gregkh@suse.de
+Cc: linux-kernel@vger.kernel.org
+Message-ID: <496d980e.160d6e0a.14d0.ffffbe3d@mx.google.com>
+
+
+SYSFS_MAGIC has been added into magic.h, so only use that definition
+in magic.h to avoid potential consistency problem.
+
+Signed-off-by: Qinghuang Feng <qhfeng.kernel@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/sysfs/mount.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/fs/sysfs/mount.c
++++ b/fs/sysfs/mount.c
+@@ -17,11 +17,10 @@
+ #include <linux/pagemap.h>
+ #include <linux/init.h>
+ #include <linux/module.h>
++#include <linux/magic.h>
+
+ #include "sysfs.h"
+
+-/* Random magic number */
+-#define SYSFS_MAGIC 0x62656572
+
+ static struct vfsmount *sysfs_mount;
+ struct super_block * sysfs_sb = NULL;
diff --git a/series b/series
index 6b8c4c1d683380..7ae46296372f7d 100644
--- a/series
+++ b/series
@@ -18,6 +18,7 @@ driver-core.current/debugfs-introduce-stub-for-debugfs_create_size_t-when-debug_
driver-core.current/klist.c-bit-0-in-pointer-can-t-be-used-as-flag.patch
driver-core.current/sync-patch-for-jp_jp-stable_kernel_rules.txt.patch
driver-core.current/uio-add-missing-documentation-of-features-added-recently.patch
+driver-core.current/driver-core-fix-kernel-doc-parameter-name.patch
#################################
# USB patches for 2.6.29
@@ -51,6 +52,12 @@ usb.current/usb-usbmon-implement-compat_ioctl.patch
usb.current/usb-storage-support-of-dane-elec-mediatouch-usb-device.patch
usb.current/usb-remove-zte-modem-from-unusual_devices.patch
usb.current/usb-option-driver-onda-device-mt503hs-has-wrong-id.patch
+usb.current/usb-cdc-acm-add-another-conexant-modem-to-the-quirks.patch
+usb.current/usb-new-id-for-ti_usb_3410_5052-driver.patch
+usb.current/usb-gadget-fix-x-y.patch
+usb.current/usb-unusual_dev-usb-storage-needs-to-ignore-a-device.patch
+usb.current/usb-storage-add-another-unusual_dev-for-off-by-one-bug.patch
+usb.current/usb-option-add-quanta-hsdpa-data-card-device-ids.patch
usb.current/usb-driver-for-freescale-quicc-engine-usb-host-controller.patch
@@ -100,6 +107,9 @@ driver-core/bus_id-wimax.patch
driver-core/bus_id-usb.patch
driver-core/driver-core-get-rid-of-struct-device-s-bus_id-string-array.patch
+driver-core/sysfs-use-standard-magic.h-for-sysfs.patch
+driver-core/sysfs-take-sysfs_mutex-when-fetching-the-root-inode.patch
+driver-core/driver-core-check-bus-match-without-holding-device-lock.patch
driver-core/uio-add-name-attributes-for-mappings-and-port-regions.patch
# helper tools, not for mainline.
@@ -119,7 +129,11 @@ usb/usb-ub-use-usb-api-functions-rather-than-constants.patch
usb/usb-remove-redundant-test-in-pxa27x_udc-and-ftdi_sio.patch
usb/usb-drivers-use-usb-api-functions-rather-than-constants.patch
usb/usb-gadget-remove-duplicated-include.patch
-
+usb/usb-usb-serial-ch341-support-for-dtr-rts-cts.patch
+usb/usb-imx_udc-fix-imx-udc-gadget-bugs.patch
+usb/usb-imx_udc-fix-imx-udc-gadget-code-style.patch
+usb/usb-imx_udc-fix-imx-udc-gadget-ep0-irq-handling.patch
+usb/usb-imx_udc-fix-imx-udc-gadget-general-irq-handling.patch
# stuff I want in my tree, but not to go into -next
gregkh.post/usb-gotemp.patch
diff --git a/usb.current/usb-cdc-acm-add-another-conexant-modem-to-the-quirks.patch b/usb.current/usb-cdc-acm-add-another-conexant-modem-to-the-quirks.patch
new file mode 100644
index 00000000000000..630f3ef04298bd
--- /dev/null
+++ b/usb.current/usb-cdc-acm-add-another-conexant-modem-to-the-quirks.patch
@@ -0,0 +1,32 @@
+From alan@lxorguk.ukuu.org.uk Sun Jan 25 15:09:07 2009
+From: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Date: Sun, 11 Jan 2009 19:53:10 +0000
+Subject: USB: cdc-acm: Add another conexant modem to the quirks
+To: greg@kroah.com, linux-usb@vger.kernel.org
+Message-ID: <20090111195252.16388.74534.stgit@localhost.localdomain>
+
+
+From: Alan Cox <alan@redhat.com>
+
+Another Conexant, another device with the same quirk
+
+Signed-off-by: Alan Cox <alan@redhat.com>
+Acked-by: Oliver Neukum <oliver@neukum.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/class/cdc-acm.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/usb/class/cdc-acm.c
++++ b/drivers/usb/class/cdc-acm.c
+@@ -1376,6 +1376,9 @@ static struct usb_device_id acm_ids[] =
+ { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
+ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+ },
++ { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
++ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
++ },
+
+ /* control interfaces with various AT-command sets */
+ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
diff --git a/usb.current/usb-gadget-fix-x-y.patch b/usb.current/usb-gadget-fix-x-y.patch
new file mode 100644
index 00000000000000..52688d5b9b03a3
--- /dev/null
+++ b/usb.current/usb-gadget-fix-x-y.patch
@@ -0,0 +1,30 @@
+From roel.kluin@gmail.com Sun Jan 25 15:20:37 2009
+From: Roel Kluin <roel.kluin@gmail.com>
+Date: Sat, 17 Jan 2009 16:52:17 +0100
+Subject: USB: GADGET: fix !x & y
+To: augulis.darius@gmail.com, Greg KH <gregkh@suse.de>, dbrownell@users.sourceforge.net
+Cc: linux-usb@vger.kernel.org
+Message-ID: <4971FEB1.2000000@gmail.com>
+
+
+! has a higher precedence than &
+
+Signed-off-by: Roel Kluin <roel.kluin@gmail.com>
+Acked-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/imx_udc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/gadget/imx_udc.c
++++ b/drivers/usb/gadget/imx_udc.c
+@@ -297,7 +297,7 @@ void imx_ep_stall(struct imx_ep_struct *
+
+ for (i = 0; i < 100; i ++) {
+ temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
+- if (!temp & EPSTAT_STALL)
++ if (!(temp & EPSTAT_STALL))
+ break;
+ udelay(20);
+ }
diff --git a/usb.current/usb-new-id-for-ti_usb_3410_5052-driver.patch b/usb.current/usb-new-id-for-ti_usb_3410_5052-driver.patch
new file mode 100644
index 00000000000000..882c8765fe012b
--- /dev/null
+++ b/usb.current/usb-new-id-for-ti_usb_3410_5052-driver.patch
@@ -0,0 +1,57 @@
+From oliver@neukum.org Sun Jan 25 15:09:34 2009
+From: Oliver Neukum <oliver@neukum.org>
+Date: Mon, 12 Jan 2009 13:31:16 +0100
+Subject: USB: new id for ti_usb_3410_5052 driver
+To: "Greg Kroah-Hartman" <gregkh@suse.de>, linux-usb@vger.kernel.org
+Message-ID: <200901121331.16871.oliver@neukum.org>
+Content-Disposition: inline
+
+
+This adds a new device id
+
+Signed-off-by: Oliver Neukum <oneukum@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ti_usb_3410_5052.c | 3 +++
+ drivers/usb/serial/ti_usb_3410_5052.h | 2 ++
+ 2 files changed, 5 insertions(+)
+
+--- a/drivers/usb/serial/ti_usb_3410_5052.c
++++ b/drivers/usb/serial/ti_usb_3410_5052.c
+@@ -184,6 +184,7 @@ static struct usb_device_id ti_id_table_
+ { USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_PRODUCT_ID) },
+ { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) },
+ { USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) },
++ { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
+ };
+
+ static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = {
+@@ -191,6 +192,7 @@ static struct usb_device_id ti_id_table_
+ { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
+ { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
+ { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
++ { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
+ };
+
+ static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = {
+@@ -205,6 +207,7 @@ static struct usb_device_id ti_id_table_
+ { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
+ { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
+ { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
++ { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
+ { }
+ };
+
+--- a/drivers/usb/serial/ti_usb_3410_5052.h
++++ b/drivers/usb/serial/ti_usb_3410_5052.h
+@@ -27,7 +27,9 @@
+
+ /* Vendor and product ids */
+ #define TI_VENDOR_ID 0x0451
++#define IBM_VENDOR_ID 0x04b3
+ #define TI_3410_PRODUCT_ID 0x3410
++#define IBM_4543_PRODUCT_ID 0x4543
+ #define TI_3410_EZ430_ID 0xF430 /* TI ez430 development tool */
+ #define TI_5052_BOOT_PRODUCT_ID 0x5052 /* no EEPROM, no firmware */
+ #define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */
diff --git a/usb.current/usb-option-add-quanta-hsdpa-data-card-device-ids.patch b/usb.current/usb-option-add-quanta-hsdpa-data-card-device-ids.patch
new file mode 100644
index 00000000000000..64b4d8cf70d72a
--- /dev/null
+++ b/usb.current/usb-option-add-quanta-hsdpa-data-card-device-ids.patch
@@ -0,0 +1,48 @@
+From Alex.Cheng@quantatw.com Sun Jan 25 15:26:31 2009
+From: <Alex.Cheng@quantatw.com>
+Date: Thu, 22 Jan 2009 16:01:57 +0800
+Subject: USB: option: add QUANTA HSDPA Data Card device ids
+To: <smurf@smurf.noris.de>
+Cc: <Alex.Cheng@quantatw.com>
+Message-ID: <859A00560C629C4FBE3985C4272AE5F504A9D1AD@Mail03.quanta.corp>
+
+
+This patch adds the support for the QUANTA Q101 series HSDPA Data Card.
+With the vendor and product IDs are set properly,
+the data card can be detected and works fine.
+
+Signed-off-by: Alex Cheng <alex.cheng@quantatw.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/option.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -158,6 +158,13 @@ static int option_send_setup(struct tty
+ #define HUAWEI_PRODUCT_E143E 0x143E
+ #define HUAWEI_PRODUCT_E143F 0x143F
+
++#define QUANTA_VENDOR_ID 0x0408
++#define QUANTA_PRODUCT_Q101 0xEA02
++#define QUANTA_PRODUCT_Q111 0xEA03
++#define QUANTA_PRODUCT_GLX 0xEA04
++#define QUANTA_PRODUCT_GKE 0xEA05
++#define QUANTA_PRODUCT_GLE 0xEA06
++
+ #define NOVATELWIRELESS_VENDOR_ID 0x1410
+
+ /* YISO PRODUCTS */
+@@ -298,6 +305,11 @@ static struct usb_device_id option_ids[]
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) },
++ { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q101) },
++ { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q111) },
++ { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) },
++ { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
++ { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) },
diff --git a/usb.current/usb-storage-add-another-unusual_dev-for-off-by-one-bug.patch b/usb.current/usb-storage-add-another-unusual_dev-for-off-by-one-bug.patch
new file mode 100644
index 00000000000000..35b93b8aed4f89
--- /dev/null
+++ b/usb.current/usb-storage-add-another-unusual_dev-for-off-by-one-bug.patch
@@ -0,0 +1,38 @@
+From phil@ipom.com Sun Jan 25 15:21:50 2009
+From: Phil Dibowitz <phil@ipom.com>
+Date: Tue, 20 Jan 2009 23:42:52 +0100
+Subject: USB: storage: Add another unusual_dev for off-by-one bug
+To: Greg KH <greg@kroah.com>, USB Dev <linux-usb@vger.kernel.org>, USB Storage list <usb-storage@lists.one-eyed-alien.net>
+Cc: Martijn Hijdra <martijn.hijdra@gmail.com>
+Message-ID: <4976536C.1000205@ipom.com>
+
+
+Argosy has released another device with the off-by-one sector. This is a
+harddrive with an internal cardreader which is affected.
+
+Based on a patch written by Martijn Hijdra <martijn.hijdra@gmail.com>
+
+Signed-off-by: Phil Dibowitz <phil@ipom.com>
+Cc: Martijn Hijdra <martijn.hijdra@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/storage/unusual_devs.h | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/usb/storage/unusual_devs.h
++++ b/drivers/usb/storage/unusual_devs.h
+@@ -1261,6 +1261,13 @@ UNUSUAL_DEV( 0x0840, 0x0084, 0x0001, 0x0
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY),
+
++/* Reported by Martijn Hijdra <martijn.hijdra@gmail.com> */
++UNUSUAL_DEV( 0x0840, 0x0085, 0x0001, 0x0001,
++ "Argosy",
++ "Storage",
++ US_SC_DEVICE, US_PR_DEVICE, NULL,
++ US_FL_FIX_CAPACITY),
++
+ /* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>.
+ * Flag will support Bulk devices which use a standards-violating 32-byte
+ * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with
diff --git a/usb.current/usb-unusual_dev-usb-storage-needs-to-ignore-a-device.patch b/usb.current/usb-unusual_dev-usb-storage-needs-to-ignore-a-device.patch
new file mode 100644
index 00000000000000..0b30bb2ce7ef95
--- /dev/null
+++ b/usb.current/usb-unusual_dev-usb-storage-needs-to-ignore-a-device.patch
@@ -0,0 +1,40 @@
+From phil@ipom.com Sun Jan 25 15:21:21 2009
+From: Phil Dibowitz <phil@ipom.com>
+Date: Tue, 20 Jan 2009 23:48:36 +0100
+Subject: USB: unusual_dev: usb-storage needs to ignore a device
+To: Greg KH <greg@kroah.com>
+Cc: USB Dev <linux-usb@vger.kernel.org>, USB Storage list <usb-storage@lists.one-eyed-alien.net>, The Solutor <thesolutor@gmail.com>
+Message-ID: <497654C4.1000200@ipom.com>
+
+This patch adds an unusual_devs entry for a Sony Ericsson modem. Like many
+other modems, we have to ignore the storage device in order to access the
+modem.
+
+
+At this time usb_modeswitch does not work with this device.
+
+
+Reported-by: The Solutor <thesolutor@gmail.com>.
+Signed-off-by: Phil Dibowitz <phil@ipom.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/storage/unusual_devs.h | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/usb/storage/unusual_devs.h
++++ b/drivers/usb/storage/unusual_devs.h
+@@ -1599,6 +1599,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_NO_WP_DETECT ),
+
++/* Reported by The Solutor <thesolutor@gmail.com> */
++UNUSUAL_DEV( 0x0fce, 0xd0e1, 0x0000, 0x0000,
++ "Sony Ericsson",
++ "MD400",
++ US_SC_DEVICE, US_PR_DEVICE, NULL,
++ US_FL_IGNORE_DEVICE),
++
+ /* Reported by Jan Mate <mate@fiit.stuba.sk>
+ * and by Soeren Sonnenburg <kernel@nn7.de> */
+ UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
diff --git a/usb/usb-imx_udc-fix-imx-udc-gadget-bugs.patch b/usb/usb-imx_udc-fix-imx-udc-gadget-bugs.patch
new file mode 100644
index 00000000000000..106385543e208f
--- /dev/null
+++ b/usb/usb-imx_udc-fix-imx-udc-gadget-bugs.patch
@@ -0,0 +1,145 @@
+From augulis.darius@gmail.com Sun Jan 25 15:23:01 2009
+From: Darius <augulis.darius@gmail.com>
+Date: Wed, 21 Jan 2009 15:17:25 +0200
+Subject: USB: imx_udc: Fix IMX UDC gadget bugs
+To: linux-usb@vger.kernel.org
+Message-ID: <gl77dh$fum$2@ger.gmane.org>
+
+
+From: Darius Augulis <augulis.darius@gmail.com>
+
+Fix small bugs and add some omptimization in IMX UDC Gadget.
+
+Signed-off-by: Darius Augulis <augulis.darius@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/imx_udc.c | 33 +++++++++++++++++++--------------
+ drivers/usb/gadget/imx_udc.h | 2 +-
+ 2 files changed, 20 insertions(+), 15 deletions(-)
+
+--- a/drivers/usb/gadget/imx_udc.c
++++ b/drivers/usb/gadget/imx_udc.c
+@@ -283,7 +283,7 @@ void imx_ep_stall(struct imx_ep_struct *
+ imx_flush(imx_ep);
+
+ /* Special care for ep0 */
+- if (EP_NO(imx_ep)) {
++ if (!EP_NO(imx_ep)) {
+ temp = __raw_readl(imx_usb->base + USB_CTRL);
+ __raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR, imx_usb->base + USB_CTRL);
+ do { } while (__raw_readl(imx_usb->base + USB_CTRL) & CTRL_CMDOVER);
+@@ -301,7 +301,7 @@ void imx_ep_stall(struct imx_ep_struct *
+ break;
+ udelay(20);
+ }
+- if (i == 50)
++ if (i == 100)
+ D_ERR(imx_usb->dev, "<%s> Non finished stall on %s\n",
+ __func__, imx_ep->ep.name);
+ }
+@@ -539,8 +539,7 @@ static int handle_ep0(struct imx_ep_stru
+ struct imx_request *req = NULL;
+ int ret = 0;
+
+- if (!list_empty(&imx_ep->queue))
+- req = list_entry(imx_ep->queue.next, struct imx_request, queue);
++ req = list_entry(imx_ep->queue.next, struct imx_request, queue);
+
+ if (req) {
+ switch (imx_ep->imx_usb->ep0state) {
+@@ -561,6 +560,10 @@ static int handle_ep0(struct imx_ep_stru
+ }
+ }
+
++ else
++ D_ERR(imx_ep->imx_usb->dev, "<%s> no request on %s\n",
++ __func__, imx_ep->ep.name);
++
+ return ret;
+ }
+
+@@ -759,7 +762,7 @@ static int imx_ep_queue
+ */
+ if (imx_usb->set_config && !EP_NO(imx_ep)) {
+ imx_usb->set_config = 0;
+- D_EPX(imx_usb->dev,
++ D_ERR(imx_usb->dev,
+ "<%s> gadget reply set config\n", __func__);
+ return 0;
+ }
+@@ -779,8 +782,6 @@ static int imx_ep_queue
+ return -ESHUTDOWN;
+ }
+
+- local_irq_save(flags);
+-
+ /* Debug */
+ D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n",
+ __func__, EP_NO(imx_ep),
+@@ -790,17 +791,18 @@ static int imx_ep_queue
+
+ if (imx_ep->stopped) {
+ usb_req->status = -ESHUTDOWN;
+- ret = -ESHUTDOWN;
+- goto out;
++ return -ESHUTDOWN;
+ }
+
+ if (req->in_use) {
+ D_ERR(imx_usb->dev,
+ "<%s> refusing to queue req %p (already queued)\n",
+ __func__, req);
+- goto out;
++ return 0;
+ }
+
++ local_irq_save(flags);
++
+ usb_req->status = -EINPROGRESS;
+ usb_req->actual = 0;
+
+@@ -810,7 +812,7 @@ static int imx_ep_queue
+ ret = handle_ep0(imx_ep);
+ else
+ ret = handle_ep(imx_ep);
+-out:
++
+ local_irq_restore(flags);
+ return ret;
+ }
+@@ -1010,10 +1012,8 @@ static irqreturn_t imx_udc_irq(int irq,
+ dump_usb_stat(__func__, imx_usb);
+ }
+
+- if (!imx_usb->driver) {
+- /*imx_udc_disable(imx_usb);*/
++ if (!imx_usb->driver)
+ goto end_irq;
+- }
+
+ if (intr & INTR_WAKEUP) {
+ if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
+@@ -1095,6 +1095,11 @@ static irqreturn_t imx_udc_irq(int irq,
+ }
+
+ if (intr & INTR_SOF) {
++ /* Copy from Motorola BSP.
++ We must enable SOF intr and set CMDOVER.
++ Datasheet don't specifiy this action, but it
++ is done in Motorola BSP, so just copy it.
++ */
+ if (imx_usb->ep0state == EP0_IDLE) {
+ temp = __raw_readl(imx_usb->base + USB_CTRL);
+ __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL);
+--- a/drivers/usb/gadget/imx_udc.h
++++ b/drivers/usb/gadget/imx_udc.h
+@@ -170,7 +170,7 @@ struct imx_udc_struct {
+ /* #define DEBUG_IRQ */
+ /* #define DEBUG_EPIRQ */
+ /* #define DEBUG_DUMP */
+-#define DEBUG_ERR
++/* #define DEBUG_ERR */
+
+ #ifdef DEBUG_REQ
+ #define D_REQ(dev, args...) dev_dbg(dev, ## args)
diff --git a/usb/usb-imx_udc-fix-imx-udc-gadget-code-style.patch b/usb/usb-imx_udc-fix-imx-udc-gadget-code-style.patch
new file mode 100644
index 00000000000000..f0eab8fc30f8c5
--- /dev/null
+++ b/usb/usb-imx_udc-fix-imx-udc-gadget-code-style.patch
@@ -0,0 +1,319 @@
+From augulis.darius@gmail.com Sun Jan 25 15:24:22 2009
+From: Darius <augulis.darius@gmail.com>
+Date: Wed, 21 Jan 2009 15:17:55 +0200
+Subject: USB: imx_udc: Fix IMX UDC gadget code style
+To: linux-usb@vger.kernel.org
+Message-ID: <gl77ef$fum$3@ger.gmane.org>
+
+
+From: Darius Augulis <augulis.darius@gmail.com>
+
+Fix code style errors in IMX UDC Gadget.
+
+Signed-off-by: Darius Augulis <augulis.darius@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/imx_udc.c | 59 ++++++++++++++++++++++++++++---------------
+ drivers/usb/gadget/imx_udc.h | 46 +++++++++++++++++++++------------
+ 2 files changed, 69 insertions(+), 36 deletions(-)
+
+--- a/drivers/usb/gadget/imx_udc.c
++++ b/drivers/usb/gadget/imx_udc.c
+@@ -1,7 +1,7 @@
+ /*
+ * driver/usb/gadget/imx_udc.c
+ *
+- * Copyright (C) 2005 Mike Lee(eemike@gmail.com)
++ * Copyright (C) 2005 Mike Lee <eemike@gmail.com>
+ * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+@@ -51,7 +51,8 @@ void ep0_chg_stat(const char *label, str
+ void imx_udc_enable(struct imx_udc_struct *imx_usb)
+ {
+ int temp = __raw_readl(imx_usb->base + USB_CTRL);
+- __raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA, imx_usb->base + USB_CTRL);
++ __raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA,
++ imx_usb->base + USB_CTRL);
+ imx_usb->gadget.speed = USB_SPEED_FULL;
+ }
+
+@@ -126,7 +127,8 @@ void imx_udc_config(struct imx_udc_struc
+ for (j = 0; j < 5; j++) {
+ __raw_writeb(ep_conf[j],
+ imx_usb->base + USB_DDAT);
+- do {} while (__raw_readl(imx_usb->base + USB_DADR)
++ do {} while (__raw_readl(imx_usb->base
++ + USB_DADR)
+ & DADR_BSY);
+ }
+ }
+@@ -183,7 +185,8 @@ void imx_udc_init_ep(struct imx_udc_stru
+ temp = (EP_DIR(imx_ep) << 7) | (max << 5)
+ | (imx_ep->bmAttributes << 3);
+ __raw_writel(temp, imx_usb->base + USB_EP_STAT(i));
+- __raw_writel(temp | EPSTAT_FLUSH, imx_usb->base + USB_EP_STAT(i));
++ __raw_writel(temp | EPSTAT_FLUSH,
++ imx_usb->base + USB_EP_STAT(i));
+ D_INI(imx_usb->dev, "<%s> ep%d_stat %08x\n", __func__, i,
+ __raw_readl(imx_usb->base + USB_EP_STAT(i)));
+ }
+@@ -278,15 +281,18 @@ void imx_ep_stall(struct imx_ep_struct *
+ struct imx_udc_struct *imx_usb = imx_ep->imx_usb;
+ int temp, i;
+
+- D_ERR(imx_usb->dev, "<%s> Forced stall on %s\n", __func__, imx_ep->ep.name);
++ D_ERR(imx_usb->dev,
++ "<%s> Forced stall on %s\n", __func__, imx_ep->ep.name);
+
+ imx_flush(imx_ep);
+
+ /* Special care for ep0 */
+ if (!EP_NO(imx_ep)) {
+ temp = __raw_readl(imx_usb->base + USB_CTRL);
+- __raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR, imx_usb->base + USB_CTRL);
+- do { } while (__raw_readl(imx_usb->base + USB_CTRL) & CTRL_CMDOVER);
++ __raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR,
++ imx_usb->base + USB_CTRL);
++ do { } while (__raw_readl(imx_usb->base + USB_CTRL)
++ & CTRL_CMDOVER);
+ temp = __raw_readl(imx_usb->base + USB_CTRL);
+ __raw_writel(temp & ~CTRL_CMDERROR, imx_usb->base + USB_CTRL);
+ }
+@@ -296,7 +302,8 @@ void imx_ep_stall(struct imx_ep_struct *
+ imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
+
+ for (i = 0; i < 100; i ++) {
+- temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
++ temp = __raw_readl(imx_usb->base
++ + USB_EP_STAT(EP_NO(imx_ep)));
+ if (!(temp & EPSTAT_STALL))
+ break;
+ udelay(20);
+@@ -325,7 +332,8 @@ static int imx_udc_wakeup(struct usb_gad
+ *******************************************************************************
+ */
+
+-static void ep_add_request(struct imx_ep_struct *imx_ep, struct imx_request *req)
++static void ep_add_request(struct imx_ep_struct *imx_ep,
++ struct imx_request *req)
+ {
+ if (unlikely(!req))
+ return;
+@@ -334,7 +342,8 @@ static void ep_add_request(struct imx_ep
+ list_add_tail(&req->queue, &imx_ep->queue);
+ }
+
+-static void ep_del_request(struct imx_ep_struct *imx_ep, struct imx_request *req)
++static void ep_del_request(struct imx_ep_struct *imx_ep,
++ struct imx_request *req)
+ {
+ if (unlikely(!req))
+ return;
+@@ -343,7 +352,8 @@ static void ep_del_request(struct imx_ep
+ req->in_use = 0;
+ }
+
+-static void done(struct imx_ep_struct *imx_ep, struct imx_request *req, int status)
++static void done(struct imx_ep_struct *imx_ep,
++ struct imx_request *req, int status)
+ {
+ ep_del_request(imx_ep, req);
+
+@@ -494,7 +504,8 @@ static int write_fifo(struct imx_ep_stru
+ __func__, imx_ep->ep.name, req,
+ completed ? "completed" : "not completed");
+ if (!EP_NO(imx_ep))
+- ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE);
++ ep0_chg_stat(__func__,
++ imx_ep->imx_usb, EP0_IDLE);
+ }
+ }
+
+@@ -586,7 +597,8 @@ static void handle_ep0_devreq(struct imx
+ "<%s> no setup packet received\n", __func__);
+ goto stall;
+ }
+- u.word[i] = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep)));
++ u.word[i] = __raw_readl(imx_usb->base
++ + USB_EP_FDAT(EP_NO(imx_ep)));
+ }
+
+ temp = imx_ep_empty(imx_ep);
+@@ -785,8 +797,10 @@ static int imx_ep_queue
+ /* Debug */
+ D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n",
+ __func__, EP_NO(imx_ep),
+- ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE)
+- || (EP_NO(imx_ep) && EP_DIR(imx_ep))) ? "IN" : "OUT", usb_req->length);
++ ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state
++ == EP0_IN_DATA_PHASE)
++ || (EP_NO(imx_ep) && EP_DIR(imx_ep)))
++ ? "IN" : "OUT", usb_req->length);
+ dump_req(__func__, imx_ep, usb_req);
+
+ if (imx_ep->stopped) {
+@@ -1061,7 +1075,8 @@ static irqreturn_t imx_udc_irq(int irq,
+
+ /* Config setup */
+ if (imx_usb->cfg != cfg) {
+- D_REQ(imx_usb->dev, "<%s> Change config start\n",__func__);
++ D_REQ(imx_usb->dev,
++ "<%s> Change config start\n", __func__);
+ u.bRequest = USB_REQ_SET_CONFIGURATION;
+ u.bRequestType = USB_DIR_OUT |
+ USB_TYPE_STANDARD |
+@@ -1073,11 +1088,13 @@ static irqreturn_t imx_udc_irq(int irq,
+ imx_usb->set_config = 1;
+ imx_usb->driver->setup(&imx_usb->gadget, &u);
+ imx_usb->set_config = 0;
+- D_REQ(imx_usb->dev, "<%s> Change config done\n",__func__);
++ D_REQ(imx_usb->dev,
++ "<%s> Change config done\n", __func__);
+
+ }
+ if (imx_usb->intf != intf || imx_usb->alt != alt) {
+- D_REQ(imx_usb->dev, "<%s> Change interface start\n",__func__);
++ D_REQ(imx_usb->dev,
++ "<%s> Change interface start\n", __func__);
+ u.bRequest = USB_REQ_SET_INTERFACE;
+ u.bRequestType = USB_DIR_OUT |
+ USB_TYPE_STANDARD |
+@@ -1090,7 +1107,8 @@ static irqreturn_t imx_udc_irq(int irq,
+ imx_usb->set_config = 1;
+ imx_usb->driver->setup(&imx_usb->gadget, &u);
+ imx_usb->set_config = 0;
+- D_REQ(imx_usb->dev, "<%s> Change interface done\n",__func__);
++ D_REQ(imx_usb->dev,
++ "<%s> Change interface done\n", __func__);
+ }
+ }
+
+@@ -1102,7 +1120,8 @@ static irqreturn_t imx_udc_irq(int irq,
+ */
+ if (imx_usb->ep0state == EP0_IDLE) {
+ temp = __raw_readl(imx_usb->base + USB_CTRL);
+- __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL);
++ __raw_writel(temp | CTRL_CMDOVER,
++ imx_usb->base + USB_CTRL);
+ }
+ }
+
+--- a/drivers/usb/gadget/imx_udc.h
++++ b/drivers/usb/gadget/imx_udc.h
+@@ -23,7 +23,8 @@
+ /* Helper macros */
+ #define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */
+ #define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0)
+-#define irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/
++#define irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) \
++ ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/
+ #define ep_to_irq(ep) (EP_NO((ep)) + USBD_INT0)
+ #define IMX_USB_NB_EP 6
+
+@@ -88,8 +89,8 @@ struct imx_udc_struct {
+ #define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */
+ #define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */
+ #define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */
+-#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last read frame pointer */
+-#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last write frame pointer */
++#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last rd f. pointer */
++#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last wr f. pointer */
+ #define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */
+ #define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */
+ #define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */
+@@ -228,7 +229,8 @@ struct imx_udc_struct {
+ #endif /* DEBUG_IRQ */
+
+ #ifdef DEBUG_EPIRQ
+- static void dump_ep_intr(const char *label, int nr, int irqreg, struct device *dev)
++ static void dump_ep_intr(const char *label, int nr, int irqreg,
++ struct device *dev)
+ {
+ dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr,
+ (irqreg & EPINTR_FIFO_FULL) ? " full" : "",
+@@ -246,7 +248,8 @@ struct imx_udc_struct {
+ #endif /* DEBUG_IRQ */
+
+ #ifdef DEBUG_DUMP
+- static void dump_usb_stat(const char *label, struct imx_udc_struct *imx_usb)
++ static void dump_usb_stat(const char *label,
++ struct imx_udc_struct *imx_usb)
+ {
+ int temp = __raw_readl(imx_usb->base + USB_STAT);
+
+@@ -259,12 +262,15 @@ struct imx_udc_struct {
+ (temp & STAT_ALTSET));
+ }
+
+- static void dump_ep_stat(const char *label, struct imx_ep_struct *imx_ep)
++ static void dump_ep_stat(const char *label,
++ struct imx_ep_struct *imx_ep)
+ {
+- int temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
++ int temp = __raw_readl(imx_ep->imx_usb->base
++ + USB_EP_INTR(EP_NO(imx_ep)));
+
+ dev_dbg(imx_ep->imx_usb->dev,
+- "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep),
++ "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n",
++ label, EP_NO(imx_ep),
+ (temp & EPINTR_FIFO_FULL) ? " full" : "",
+ (temp & EPINTR_FIFO_EMPTY) ? " fempty" : "",
+ (temp & EPINTR_FIFO_ERROR) ? " ferr" : "",
+@@ -275,18 +281,22 @@ struct imx_udc_struct {
+ (temp & EPINTR_DEVREQ) ? " devreq" : "",
+ (temp & EPINTR_EOT) ? " eot" : "");
+
+- temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
++ temp = __raw_readl(imx_ep->imx_usb->base
++ + USB_EP_STAT(EP_NO(imx_ep)));
+
+ dev_dbg(imx_ep->imx_usb->dev,
+- "<%s> EP%d_STAT=[%s%s bcount=%d]\n", label, EP_NO(imx_ep),
++ "<%s> EP%d_STAT=[%s%s bcount=%d]\n",
++ label, EP_NO(imx_ep),
+ (temp & EPSTAT_SIP) ? " sip" : "",
+ (temp & EPSTAT_STALL) ? " stall" : "",
+ (temp & EPSTAT_BCOUNT) >> 16);
+
+- temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep)));
++ temp = __raw_readl(imx_ep->imx_usb->base
++ + USB_EP_FSTAT(EP_NO(imx_ep)));
+
+ dev_dbg(imx_ep->imx_usb->dev,
+- "<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep),
++ "<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n",
++ label, EP_NO(imx_ep),
+ (temp & FSTAT_ERR) ? " ferr" : "",
+ (temp & FSTAT_UF) ? " funder" : "",
+ (temp & FSTAT_OF) ? " fover" : "",
+@@ -296,19 +306,23 @@ struct imx_udc_struct {
+ (temp & FSTAT_EMPTY) ? " fempty" : "");
+ }
+
+- static void dump_req(const char *label, struct imx_ep_struct *imx_ep, struct usb_request *req)
++ static void dump_req(const char *label, struct imx_ep_struct *imx_ep,
++ struct usb_request *req)
+ {
+ int i;
+
+ if (!req || !req->buf) {
+- dev_dbg(imx_ep->imx_usb->dev, "<%s> req or req buf is free\n", label);
++ dev_dbg(imx_ep->imx_usb->dev,
++ "<%s> req or req buf is free\n", label);
+ return;
+ }
+
+- if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE)
++ if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state
++ == EP0_IN_DATA_PHASE)
+ || (EP_NO(imx_ep) && EP_DIR(imx_ep))) {
+
+- dev_dbg(imx_ep->imx_usb->dev, "<%s> request dump <", label);
++ dev_dbg(imx_ep->imx_usb->dev,
++ "<%s> request dump <", label);
+ for (i = 0; i < req->length; i++)
+ printk("%02x-", *((u8 *)req->buf + i));
+ printk(">\n");
diff --git a/usb/usb-imx_udc-fix-imx-udc-gadget-ep0-irq-handling.patch b/usb/usb-imx_udc-fix-imx-udc-gadget-ep0-irq-handling.patch
new file mode 100644
index 00000000000000..d94bb99f3d0dce
--- /dev/null
+++ b/usb/usb-imx_udc-fix-imx-udc-gadget-ep0-irq-handling.patch
@@ -0,0 +1,51 @@
+From augulis.darius@gmail.com Sun Jan 25 15:24:39 2009
+From: Darius <augulis.darius@gmail.com>
+Date: Wed, 21 Jan 2009 15:18:33 +0200
+Subject: USB: imx_udc: Fix IMX UDC gadget ep0 irq handling
+To: linux-usb@vger.kernel.org
+Message-ID: <gl77fl$fum$4@ger.gmane.org>
+
+
+From: Darius Augulis <augulis.darius@gmail.com>
+
+Fix ep0 interrupt handling in IMX UDC Gadget.
+
+Signed-off-by: Darius Augulis <augulis.darius@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/imx_udc.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/gadget/imx_udc.c
++++ b/drivers/usb/gadget/imx_udc.c
+@@ -1133,6 +1133,7 @@ end_irq:
+ static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev)
+ {
+ struct imx_udc_struct *imx_usb = dev;
++ struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0];
+ int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0));
+
+ dump_ep_intr(__func__, 0, intr, imx_usb->dev);
+@@ -1142,16 +1143,15 @@ static irqreturn_t imx_udc_ctrl_irq(int
+ return IRQ_HANDLED;
+ }
+
+- /* DEVREQ IRQ has highest priority */
++ /* DEVREQ has highest priority */
+ if (intr & (EPINTR_DEVREQ | EPINTR_MDEVREQ))
+ handle_ep0_devreq(imx_usb);
+ /* Seem i.MX is missing EOF interrupt sometimes.
+- * Therefore we monitor both EOF and FIFO_EMPTY interrups
+- * when transmiting, and both EOF and FIFO_FULL when
+- * receiving data.
++ * Therefore we don't monitor EOF.
++ * We call handle_ep0() only if a request is queued for ep0.
+ */
+- else if (intr & (EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL))
+- handle_ep0(&imx_usb->imx_ep[0]);
++ else if (!list_empty(&imx_ep->queue))
++ handle_ep0(imx_ep);
+
+ __raw_writel(intr, imx_usb->base + USB_EP_INTR(0));
+
diff --git a/usb/usb-imx_udc-fix-imx-udc-gadget-general-irq-handling.patch b/usb/usb-imx_udc-fix-imx-udc-gadget-general-irq-handling.patch
new file mode 100644
index 00000000000000..001514ea725e8c
--- /dev/null
+++ b/usb/usb-imx_udc-fix-imx-udc-gadget-general-irq-handling.patch
@@ -0,0 +1,266 @@
+From augulis.darius@gmail.com Sun Jan 25 15:24:56 2009
+From: Darius <augulis.darius@gmail.com>
+Date: Wed, 21 Jan 2009 15:19:19 +0200
+Subject: USB: imx_udc: Fix IMX UDC gadget general irq handling
+To: linux-usb@vger.kernel.org
+Message-ID: <gl77h3$fum$5@ger.gmane.org>
+
+
+From: Darius Augulis <augulis.darius@gmail.com>
+
+Workaround of hw bug in IMX UDC.
+This bug causes wrong handling of CFG_CHG interrupt.
+Workaround is documented inline source code.
+
+Signed-off-by: Darius Augulis <augulis.darius@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/imx_udc.c | 160 +++++++++++++++++++++++++------------------
+ drivers/usb/gadget/imx_udc.h | 1
+ 2 files changed, 95 insertions(+), 66 deletions(-)
+
+--- a/drivers/usb/gadget/imx_udc.c
++++ b/drivers/usb/gadget/imx_udc.c
+@@ -28,6 +28,7 @@
+ #include <linux/dma-mapping.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
++#include <linux/timer.h>
+
+ #include <linux/usb/ch9.h>
+ #include <linux/usb/gadget.h>
+@@ -1013,70 +1014,32 @@ static void udc_stop_activity(struct imx
+ *******************************************************************************
+ */
+
+-static irqreturn_t imx_udc_irq(int irq, void *dev)
++/*
++ * Called when timer expires.
++ * Timer is started when CFG_CHG is received.
++ */
++static void handle_config(unsigned long data)
+ {
+- struct imx_udc_struct *imx_usb = dev;
++ struct imx_udc_struct *imx_usb = (void *)data;
+ struct usb_ctrlrequest u;
+ int temp, cfg, intf, alt;
+- int intr = __raw_readl(imx_usb->base + USB_INTR);
+-
+- if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START
+- | INTR_RESET_STOP | INTR_CFG_CHG)) {
+- dump_intr(__func__, intr, imx_usb->dev);
+- dump_usb_stat(__func__, imx_usb);
+- }
+-
+- if (!imx_usb->driver)
+- goto end_irq;
+-
+- if (intr & INTR_WAKEUP) {
+- if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
+- && imx_usb->driver && imx_usb->driver->resume)
+- imx_usb->driver->resume(&imx_usb->gadget);
+- imx_usb->set_config = 0;
+- imx_usb->gadget.speed = USB_SPEED_FULL;
+- }
+-
+- if (intr & INTR_SUSPEND) {
+- if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN
+- && imx_usb->driver && imx_usb->driver->suspend)
+- imx_usb->driver->suspend(&imx_usb->gadget);
+- imx_usb->set_config = 0;
+- imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
+- }
+
+- if (intr & INTR_RESET_START) {
+- __raw_writel(intr, imx_usb->base + USB_INTR);
+- udc_stop_activity(imx_usb, imx_usb->driver);
+- imx_usb->set_config = 0;
+- imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
+- }
++ local_irq_disable();
+
+- if (intr & INTR_RESET_STOP)
+- imx_usb->gadget.speed = USB_SPEED_FULL;
+-
+- if (intr & INTR_CFG_CHG) {
+- __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR);
+- temp = __raw_readl(imx_usb->base + USB_STAT);
+- cfg = (temp & STAT_CFG) >> 5;
+- intf = (temp & STAT_INTF) >> 3;
+- alt = temp & STAT_ALTSET;
+-
+- D_REQ(imx_usb->dev,
+- "<%s> orig config C=%d, I=%d, A=%d / "
+- "req config C=%d, I=%d, A=%d\n",
+- __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt,
+- cfg, intf, alt);
++ temp = __raw_readl(imx_usb->base + USB_STAT);
++ cfg = (temp & STAT_CFG) >> 5;
++ intf = (temp & STAT_INTF) >> 3;
++ alt = temp & STAT_ALTSET;
++
++ D_REQ(imx_usb->dev,
++ "<%s> orig config C=%d, I=%d, A=%d / "
++ "req config C=%d, I=%d, A=%d\n",
++ __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt,
++ cfg, intf, alt);
+
+- if (cfg != 1 && cfg != 2)
+- goto end_irq;
++ if (cfg == 1 || cfg == 2) {
+
+- imx_usb->set_config = 0;
+-
+- /* Config setup */
+ if (imx_usb->cfg != cfg) {
+- D_REQ(imx_usb->dev,
+- "<%s> Change config start\n", __func__);
+ u.bRequest = USB_REQ_SET_CONFIGURATION;
+ u.bRequestType = USB_DIR_OUT |
+ USB_TYPE_STANDARD |
+@@ -1085,16 +1048,10 @@ static irqreturn_t imx_udc_irq(int irq,
+ u.wIndex = 0;
+ u.wLength = 0;
+ imx_usb->cfg = cfg;
+- imx_usb->set_config = 1;
+ imx_usb->driver->setup(&imx_usb->gadget, &u);
+- imx_usb->set_config = 0;
+- D_REQ(imx_usb->dev,
+- "<%s> Change config done\n", __func__);
+
+ }
+ if (imx_usb->intf != intf || imx_usb->alt != alt) {
+- D_REQ(imx_usb->dev,
+- "<%s> Change interface start\n", __func__);
+ u.bRequest = USB_REQ_SET_INTERFACE;
+ u.bRequestType = USB_DIR_OUT |
+ USB_TYPE_STANDARD |
+@@ -1104,14 +1061,30 @@ static irqreturn_t imx_udc_irq(int irq,
+ u.wLength = 0;
+ imx_usb->intf = intf;
+ imx_usb->alt = alt;
+- imx_usb->set_config = 1;
+ imx_usb->driver->setup(&imx_usb->gadget, &u);
+- imx_usb->set_config = 0;
+- D_REQ(imx_usb->dev,
+- "<%s> Change interface done\n", __func__);
+ }
+ }
+
++ imx_usb->set_config = 0;
++
++ local_irq_enable();
++}
++
++static irqreturn_t imx_udc_irq(int irq, void *dev)
++{
++ struct imx_udc_struct *imx_usb = dev;
++ int intr = __raw_readl(imx_usb->base + USB_INTR);
++ int temp;
++
++ if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START
++ | INTR_RESET_STOP | INTR_CFG_CHG)) {
++ dump_intr(__func__, intr, imx_usb->dev);
++ dump_usb_stat(__func__, imx_usb);
++ }
++
++ if (!imx_usb->driver)
++ goto end_irq;
++
+ if (intr & INTR_SOF) {
+ /* Copy from Motorola BSP.
+ We must enable SOF intr and set CMDOVER.
+@@ -1125,6 +1098,55 @@ static irqreturn_t imx_udc_irq(int irq,
+ }
+ }
+
++ if (intr & INTR_CFG_CHG) {
++ /* A workaround of serious IMX UDC bug.
++ Handling of CFG_CHG should be delayed for some time, because
++ IMX does not NACK the host when CFG_CHG interrupt is pending.
++ There is no time to handle current CFG_CHG
++ if next CFG_CHG or SETUP packed is send immediately.
++ We have to clear CFG_CHG, start the timer and
++ NACK the host by setting CTRL_CMDOVER
++ if it sends any SETUP packet.
++ When timer expires, handler is called to handle configuration
++ changes. While CFG_CHG is not handled (set_config=1),
++ we must NACK the host to every SETUP packed.
++ This delay prevents from going out of sync with host.
++ */
++ __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR);
++ imx_usb->set_config = 1;
++ mod_timer(&imx_usb->timer, jiffies + 5);
++ goto end_irq;
++ }
++
++ if (intr & INTR_WAKEUP) {
++ if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
++ && imx_usb->driver && imx_usb->driver->resume)
++ imx_usb->driver->resume(&imx_usb->gadget);
++ imx_usb->set_config = 0;
++ del_timer(&imx_usb->timer);
++ imx_usb->gadget.speed = USB_SPEED_FULL;
++ }
++
++ if (intr & INTR_SUSPEND) {
++ if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN
++ && imx_usb->driver && imx_usb->driver->suspend)
++ imx_usb->driver->suspend(&imx_usb->gadget);
++ imx_usb->set_config = 0;
++ del_timer(&imx_usb->timer);
++ imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
++ }
++
++ if (intr & INTR_RESET_START) {
++ __raw_writel(intr, imx_usb->base + USB_INTR);
++ udc_stop_activity(imx_usb, imx_usb->driver);
++ imx_usb->set_config = 0;
++ del_timer(&imx_usb->timer);
++ imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
++ }
++
++ if (intr & INTR_RESET_STOP)
++ imx_usb->gadget.speed = USB_SPEED_FULL;
++
+ end_irq:
+ __raw_writel(intr, imx_usb->base + USB_INTR);
+ return IRQ_HANDLED;
+@@ -1342,6 +1364,7 @@ int usb_gadget_unregister_driver(struct
+
+ udc_stop_activity(imx_usb, driver);
+ imx_udc_disable(imx_usb);
++ del_timer(&imx_usb->timer);
+
+ driver->unbind(&imx_usb->gadget);
+ imx_usb->gadget.dev.driver = NULL;
+@@ -1459,6 +1482,10 @@ static int __init imx_udc_probe(struct p
+ usb_init_data(imx_usb);
+ imx_udc_init(imx_usb);
+
++ init_timer(&imx_usb->timer);
++ imx_usb->timer.function = handle_config;
++ imx_usb->timer.data = (unsigned long)imx_usb;
++
+ return 0;
+
+ fail3:
+@@ -1481,6 +1508,7 @@ static int __exit imx_udc_remove(struct
+ int i;
+
+ imx_udc_disable(imx_usb);
++ del_timer(&imx_usb->timer);
+
+ for (i = 0; i < IMX_USB_NB_EP + 1; i++)
+ free_irq(imx_usb->usbd_int[i], imx_usb);
+--- a/drivers/usb/gadget/imx_udc.h
++++ b/drivers/usb/gadget/imx_udc.h
+@@ -59,6 +59,7 @@ struct imx_udc_struct {
+ struct device *dev;
+ struct imx_ep_struct imx_ep[IMX_USB_NB_EP];
+ struct clk *clk;
++ struct timer_list timer;
+ enum ep0_state ep0state;
+ struct resource *res;
+ void __iomem *base;
diff --git a/usb/usb-usb-serial-ch341-support-for-dtr-rts-cts.patch b/usb/usb-usb-serial-ch341-support-for-dtr-rts-cts.patch
new file mode 100644
index 00000000000000..e089ea05f8a98f
--- /dev/null
+++ b/usb/usb-usb-serial-ch341-support-for-dtr-rts-cts.patch
@@ -0,0 +1,547 @@
+From boris@hajduk.org Sun Jan 25 15:19:52 2009
+From: Werner Cornelius <werner@cornelius-consult.de>
+Date: Fri, 16 Jan 2009 21:02:41 +0100
+Subject: USB: usb-serial ch341: support for DTR/RTS/CTS
+To: gregkh@suse.de
+Cc: linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org
+Message-ID: <20090116200241.GA28338@zboubi.com>
+Content-Disposition: inline
+
+
+From: Werner Cornelius <werner@cornelius-consult.de>
+
+Fixup of Werner Cornelius patch to the ch341 USB-serial driver, which adds:
+- support all baudrates, not just a hard-coded set
+- support for controlling DTR, RTS and CTS
+
+Features still missing:
+- character length other than 8 bits
+- parity settings
+- break control
+
+I adapted his patch for the new usb_serial API introduced in 2.6.25-git8 by
+Alan Cox on 22 July 2008. Non-compliance to the new API was a reason for
+refusing a similar patch from Tollef Fog Heen.
+
+Usage example by Tollef Fog Heen :
+ TEMPer USB thermometer <http://err.no/src/TEMPer.c>
+
+Signed-off-by: Werner Cornelius <Werner.Cornelius@cornelius-consult.de>
+Signed-off-by: Boris Hajduk <boris@hajduk.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ch341.c | 374 ++++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 302 insertions(+), 72 deletions(-)
+
+--- a/drivers/usb/serial/ch341.c
++++ b/drivers/usb/serial/ch341.c
+@@ -1,5 +1,7 @@
+ /*
+ * Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk>
++ * Copyright 2007, Werner Cornelius <werner@cornelius-consult.de>
++ * Copyright 2009, Boris Hajduk <boris@hajduk.org>
+ *
+ * ch341.c implements a serial port driver for the Winchiphead CH341.
+ *
+@@ -21,9 +23,39 @@
+ #include <linux/usb/serial.h>
+ #include <linux/serial.h>
+
+-#define DEFAULT_BAUD_RATE 2400
++#define DEFAULT_BAUD_RATE 9600
+ #define DEFAULT_TIMEOUT 1000
+
++/* flags for IO-Bits */
++#define CH341_BIT_RTS (1 << 6)
++#define CH341_BIT_DTR (1 << 5)
++
++/******************************/
++/* interrupt pipe definitions */
++/******************************/
++/* always 4 interrupt bytes */
++/* first irq byte normally 0x08 */
++/* second irq byte base 0x7d + below */
++/* third irq byte base 0x94 + below */
++/* fourth irq byte normally 0xee */
++
++/* second interrupt byte */
++#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
++
++/* status returned in third interrupt answer byte, inverted in data
++ from irq */
++#define CH341_BIT_CTS 0x01
++#define CH341_BIT_DSR 0x02
++#define CH341_BIT_RI 0x04
++#define CH341_BIT_DCD 0x08
++#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
++
++/*******************************/
++/* baudrate calculation factor */
++/*******************************/
++#define CH341_BAUDBASE_FACTOR 1532620800
++#define CH341_BAUDBASE_DIVMAX 3
++
+ static int debug;
+
+ static struct usb_device_id id_table [] = {
+@@ -34,9 +66,12 @@ static struct usb_device_id id_table []
+ MODULE_DEVICE_TABLE(usb, id_table);
+
+ struct ch341_private {
+- unsigned baud_rate;
+- u8 dtr;
+- u8 rts;
++ spinlock_t lock; /* access lock */
++ wait_queue_head_t delta_msr_wait; /* wait queue for modem status */
++ unsigned baud_rate; /* set baud rate */
++ u8 line_control; /* set line control value RTS/DTR */
++ u8 line_status; /* active status of modem control inputs */
++ u8 multi_status_change; /* status changed multiple since last call */
+ };
+
+ static int ch341_control_out(struct usb_device *dev, u8 request,
+@@ -72,37 +107,28 @@ static int ch341_set_baudrate(struct usb
+ {
+ short a, b;
+ int r;
++ unsigned long factor;
++ short divisor;
+
+ dbg("ch341_set_baudrate(%d)", priv->baud_rate);
+- switch (priv->baud_rate) {
+- case 2400:
+- a = 0xd901;
+- b = 0x0038;
+- break;
+- case 4800:
+- a = 0x6402;
+- b = 0x001f;
+- break;
+- case 9600:
+- a = 0xb202;
+- b = 0x0013;
+- break;
+- case 19200:
+- a = 0xd902;
+- b = 0x000d;
+- break;
+- case 38400:
+- a = 0x6403;
+- b = 0x000a;
+- break;
+- case 115200:
+- a = 0xcc03;
+- b = 0x0008;
+- break;
+- default:
++
++ if (!priv->baud_rate)
+ return -EINVAL;
++ factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
++ divisor = CH341_BAUDBASE_DIVMAX;
++
++ while ((factor > 0xfff0) && divisor) {
++ factor >>= 3;
++ divisor--;
+ }
+
++ if (factor > 0xfff0)
++ return -EINVAL;
++
++ factor = 0x10000 - factor;
++ a = (factor & 0xff00) | divisor;
++ b = factor & 0xff;
++
+ r = ch341_control_out(dev, 0x9a, 0x1312, a);
+ if (!r)
+ r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
+@@ -110,19 +136,18 @@ static int ch341_set_baudrate(struct usb
+ return r;
+ }
+
+-static int ch341_set_handshake(struct usb_device *dev,
+- struct ch341_private *priv)
++static int ch341_set_handshake(struct usb_device *dev, u8 control)
+ {
+- dbg("ch341_set_handshake(%d,%d)", priv->dtr, priv->rts);
+- return ch341_control_out(dev, 0xa4,
+- ~((priv->dtr?1<<5:0)|(priv->rts?1<<6:0)), 0);
++ dbg("ch341_set_handshake(0x%02x)", control);
++ return ch341_control_out(dev, 0xa4, ~control, 0);
+ }
+
+-static int ch341_get_status(struct usb_device *dev)
++static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
+ {
+ char *buffer;
+ int r;
+ const unsigned size = 8;
++ unsigned long flags;
+
+ dbg("ch341_get_status()");
+
+@@ -134,10 +159,15 @@ static int ch341_get_status(struct usb_d
+ if (r < 0)
+ goto out;
+
+- /* Not having the datasheet for the CH341, we ignore the bytes returned
+- * from the device. Return error if the device did not respond in time.
+- */
+- r = 0;
++ /* setup the private status if available */
++ if (r == 2) {
++ r = 0;
++ spin_lock_irqsave(&priv->lock, flags);
++ priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
++ priv->multi_status_change = 0;
++ spin_unlock_irqrestore(&priv->lock, flags);
++ } else
++ r = -EPROTO;
+
+ out: kfree(buffer);
+ return r;
+@@ -180,7 +210,7 @@ static int ch341_configure(struct usb_de
+ goto out;
+
+ /* expect 0xff 0xee */
+- r = ch341_get_status(dev);
++ r = ch341_get_status(dev, priv);
+ if (r < 0)
+ goto out;
+
+@@ -192,12 +222,12 @@ static int ch341_configure(struct usb_de
+ if (r < 0)
+ goto out;
+
+- r = ch341_set_handshake(dev, priv);
++ r = ch341_set_handshake(dev, priv->line_control);
+ if (r < 0)
+ goto out;
+
+ /* expect 0x9f 0xee */
+- r = ch341_get_status(dev);
++ r = ch341_get_status(dev, priv);
+
+ out: kfree(buffer);
+ return r;
+@@ -216,9 +246,10 @@ static int ch341_attach(struct usb_seria
+ if (!priv)
+ return -ENOMEM;
+
++ spin_lock_init(&priv->lock);
++ init_waitqueue_head(&priv->delta_msr_wait);
+ priv->baud_rate = DEFAULT_BAUD_RATE;
+- priv->dtr = 1;
+- priv->rts = 1;
++ priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
+
+ r = ch341_configure(serial->dev, priv);
+ if (r < 0)
+@@ -231,6 +262,35 @@ error: kfree(priv);
+ return r;
+ }
+
++static void ch341_close(struct tty_struct *tty, struct usb_serial_port *port,
++ struct file *filp)
++{
++ struct ch341_private *priv = usb_get_serial_port_data(port);
++ unsigned long flags;
++ unsigned int c_cflag;
++
++ dbg("%s - port %d", __func__, port->number);
++
++ /* shutdown our urbs */
++ dbg("%s - shutting down urbs", __func__);
++ usb_kill_urb(port->write_urb);
++ usb_kill_urb(port->read_urb);
++ usb_kill_urb(port->interrupt_in_urb);
++
++ if (tty) {
++ c_cflag = tty->termios->c_cflag;
++ if (c_cflag & HUPCL) {
++ /* drop DTR and RTS */
++ spin_lock_irqsave(&priv->lock, flags);
++ priv->line_control = 0;
++ spin_unlock_irqrestore(&priv->lock, flags);
++ ch341_set_handshake(port->serial->dev, 0);
++ }
++ }
++ wake_up_interruptible(&priv->delta_msr_wait);
++}
++
++
+ /* open this device, set default parameters */
+ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port,
+ struct file *filp)
+@@ -242,14 +302,13 @@ static int ch341_open(struct tty_struct
+ dbg("ch341_open()");
+
+ priv->baud_rate = DEFAULT_BAUD_RATE;
+- priv->dtr = 1;
+- priv->rts = 1;
++ priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
+
+ r = ch341_configure(serial->dev, priv);
+ if (r)
+ goto out;
+
+- r = ch341_set_handshake(serial->dev, priv);
++ r = ch341_set_handshake(serial->dev, priv->line_control);
+ if (r)
+ goto out;
+
+@@ -257,6 +316,16 @@ static int ch341_open(struct tty_struct
+ if (r)
+ goto out;
+
++ dbg("%s - submitting interrupt urb", __func__);
++ port->interrupt_in_urb->dev = serial->dev;
++ r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
++ if (r) {
++ dev_err(&port->dev, "%s - failed submitting interrupt urb,"
++ " error %d\n", __func__, r);
++ ch341_close(tty, port, NULL);
++ return -EPROTO;
++ }
++
+ r = usb_serial_generic_open(tty, port, filp);
+
+ out: return r;
+@@ -270,38 +339,194 @@ static void ch341_set_termios(struct tty
+ {
+ struct ch341_private *priv = usb_get_serial_port_data(port);
+ unsigned baud_rate;
++ unsigned long flags;
+
+ dbg("ch341_set_termios()");
+
++ if (!tty || !tty->termios)
++ return;
++
+ baud_rate = tty_get_baud_rate(tty);
+
+- switch (baud_rate) {
+- case 2400:
+- case 4800:
+- case 9600:
+- case 19200:
+- case 38400:
+- case 115200:
+- priv->baud_rate = baud_rate;
+- break;
+- default:
+- dbg("Rate %d not supported, using %d",
+- baud_rate, DEFAULT_BAUD_RATE);
+- priv->baud_rate = DEFAULT_BAUD_RATE;
++ priv->baud_rate = baud_rate;
++
++ if (baud_rate) {
++ spin_lock_irqsave(&priv->lock, flags);
++ priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
++ spin_unlock_irqrestore(&priv->lock, flags);
++ ch341_set_baudrate(port->serial->dev, priv);
++ } else {
++ spin_lock_irqsave(&priv->lock, flags);
++ priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
++ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+
+- ch341_set_baudrate(port->serial->dev, priv);
++ ch341_set_handshake(port->serial->dev, priv->line_control);
+
+ /* Unimplemented:
+ * (cflag & CSIZE) : data bits [5, 8]
+ * (cflag & PARENB) : parity {NONE, EVEN, ODD}
+ * (cflag & CSTOPB) : stop bits [1, 2]
+ */
++}
++
++static int ch341_tiocmset(struct tty_struct *tty, struct file *file,
++ unsigned int set, unsigned int clear)
++{
++ struct usb_serial_port *port = tty->driver_data;
++ struct ch341_private *priv = usb_get_serial_port_data(port);
++ unsigned long flags;
++ u8 control;
++
++ spin_lock_irqsave(&priv->lock, flags);
++ if (set & TIOCM_RTS)
++ priv->line_control |= CH341_BIT_RTS;
++ if (set & TIOCM_DTR)
++ priv->line_control |= CH341_BIT_DTR;
++ if (clear & TIOCM_RTS)
++ priv->line_control &= ~CH341_BIT_RTS;
++ if (clear & TIOCM_DTR)
++ priv->line_control &= ~CH341_BIT_DTR;
++ control = priv->line_control;
++ spin_unlock_irqrestore(&priv->lock, flags);
++
++ return ch341_set_handshake(port->serial->dev, control);
++}
++
++static void ch341_read_int_callback(struct urb *urb)
++{
++ struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
++ unsigned char *data = urb->transfer_buffer;
++ unsigned int actual_length = urb->actual_length;
++ int status;
++
++ dbg("%s (%d)", __func__, port->number);
++
++ switch (urb->status) {
++ case 0:
++ /* success */
++ break;
++ case -ECONNRESET:
++ case -ENOENT:
++ case -ESHUTDOWN:
++ /* this urb is terminated, clean up */
++ dbg("%s - urb shutting down with status: %d", __func__,
++ urb->status);
++ return;
++ default:
++ dbg("%s - nonzero urb status received: %d", __func__,
++ urb->status);
++ goto exit;
++ }
++
++ usb_serial_debug_data(debug, &port->dev, __func__,
++ urb->actual_length, urb->transfer_buffer);
++
++ if (actual_length >= 4) {
++ struct ch341_private *priv = usb_get_serial_port_data(port);
++ unsigned long flags;
++
++ spin_lock_irqsave(&priv->lock, flags);
++ priv->line_status = (~(data[2])) & CH341_BITS_MODEM_STAT;
++ if ((data[1] & CH341_MULT_STAT))
++ priv->multi_status_change = 1;
++ spin_unlock_irqrestore(&priv->lock, flags);
++ wake_up_interruptible(&priv->delta_msr_wait);
++ }
++
++exit:
++ status = usb_submit_urb(urb, GFP_ATOMIC);
++ if (status)
++ dev_err(&urb->dev->dev,
++ "%s - usb_submit_urb failed with result %d\n",
++ __func__, status);
++}
++
++static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
++{
++ struct ch341_private *priv = usb_get_serial_port_data(port);
++ unsigned long flags;
++ u8 prevstatus;
++ u8 status;
++ u8 changed;
++ u8 multi_change = 0;
++
++ spin_lock_irqsave(&priv->lock, flags);
++ prevstatus = priv->line_status;
++ priv->multi_status_change = 0;
++ spin_unlock_irqrestore(&priv->lock, flags);
++
++ while (!multi_change) {
++ interruptible_sleep_on(&priv->delta_msr_wait);
++ /* see if a signal did it */
++ if (signal_pending(current))
++ return -ERESTARTSYS;
++
++ spin_lock_irqsave(&priv->lock, flags);
++ status = priv->line_status;
++ multi_change = priv->multi_status_change;
++ spin_unlock_irqrestore(&priv->lock, flags);
++
++ changed = prevstatus ^ status;
++
++ if (((arg & TIOCM_RNG) && (changed & CH341_BIT_RI)) ||
++ ((arg & TIOCM_DSR) && (changed & CH341_BIT_DSR)) ||
++ ((arg & TIOCM_CD) && (changed & CH341_BIT_DCD)) ||
++ ((arg & TIOCM_CTS) && (changed & CH341_BIT_CTS))) {
++ return 0;
++ }
++ prevstatus = status;
++ }
++
++ return 0;
++}
++
++/*static int ch341_ioctl(struct usb_serial_port *port, struct file *file,*/
++static int ch341_ioctl(struct tty_struct *tty, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct usb_serial_port *port = tty->driver_data;
++ dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd);
++
++ switch (cmd) {
++ case TIOCMIWAIT:
++ dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
++ return wait_modem_info(port, arg);
++
++ default:
++ dbg("%s not supported = 0x%04x", __func__, cmd);
++ break;
++ }
++
++ return -ENOIOCTLCMD;
++}
++
++static int ch341_tiocmget(struct tty_struct *tty, struct file *file)
++{
++ struct usb_serial_port *port = tty->driver_data;
++ struct ch341_private *priv = usb_get_serial_port_data(port);
++ unsigned long flags;
++ u8 mcr;
++ u8 status;
++ unsigned int result;
++
++ dbg("%s (%d)", __func__, port->number);
++
++ spin_lock_irqsave(&priv->lock, flags);
++ mcr = priv->line_control;
++ status = priv->line_status;
++ spin_unlock_irqrestore(&priv->lock, flags);
++
++ result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0)
++ | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0)
++ | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0)
++ | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0)
++ | ((status & CH341_BIT_RI) ? TIOCM_RI : 0)
++ | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0);
++
++ dbg("%s - result = %x", __func__, result);
+
+- /* Copy back the old hardware settings */
+- tty_termios_copy_hw(tty->termios, old_termios);
+- /* And re-encode with the new baud */
+- tty_encode_baud_rate(tty, baud_rate, baud_rate);
++ return result;
+ }
+
+ static struct usb_driver ch341_driver = {
+@@ -317,12 +542,17 @@ static struct usb_serial_driver ch341_de
+ .owner = THIS_MODULE,
+ .name = "ch341-uart",
+ },
+- .id_table = id_table,
+- .usb_driver = &ch341_driver,
+- .num_ports = 1,
+- .open = ch341_open,
+- .set_termios = ch341_set_termios,
+- .attach = ch341_attach,
++ .id_table = id_table,
++ .usb_driver = &ch341_driver,
++ .num_ports = 1,
++ .open = ch341_open,
++ .close = ch341_close,
++ .ioctl = ch341_ioctl,
++ .set_termios = ch341_set_termios,
++ .tiocmget = ch341_tiocmget,
++ .tiocmset = ch341_tiocmset,
++ .read_int_callback = ch341_read_int_callback,
++ .attach = ch341_attach,
+ };
+
+ static int __init ch341_init(void)