summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-mtx-1-2.4.27
diff options
context:
space:
mode:
authorMartin Dietze <di@fh-wedel.de>2009-05-19 15:54:14 +0200
committerKoen Kooi <koen@openembedded.org>2009-05-19 15:54:17 +0200
commit9f026f95ef802e993fd4b65c03ee85a4b4601421 (patch)
treea3f1d587ab40947864c292df2f1ab1adb2f6d136 /recipes/linux/linux-mtx-1-2.4.27
parent7ef692efa306973b2a204cfc227d872cd1bee69c (diff)
linux-mtx 2.6.27: updates
Diffstat (limited to 'recipes/linux/linux-mtx-1-2.4.27')
-rw-r--r--recipes/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch9
-rw-r--r--recipes/linux/linux-mtx-1-2.4.27/32-usbserial-stalled-hack.diff4
-rw-r--r--recipes/linux/linux-mtx-1-2.4.27/33-usbserial-bulk_in_size-4096.diff106
-rw-r--r--recipes/linux/linux-mtx-1-2.4.27/43-usbserial-27-32-backport.diff33
-rw-r--r--recipes/linux/linux-mtx-1-2.4.27/45-acm-tty-and-sb2.patch721
5 files changed, 818 insertions, 55 deletions
diff --git a/recipes/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch b/recipes/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch
index 4e8a7576bd..f4a8754228 100644
--- a/recipes/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch
+++ b/recipes/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch
@@ -1,6 +1,15 @@
diff -Naru linux/arch/mips/Makefile linux.spi/arch/mips/Makefile
--- linux/arch/mips/Makefile 2004-05-06 15:23:41.000000000 -0400
+++ linux.spi/arch/mips/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -17,7 +17,7 @@
+ # Select the object file format to substitute into the linker script.
+ #
+ ifdef CONFIG_CPU_LITTLE_ENDIAN
+-tool-prefix = mipsel-linux-
++tool-prefix = mipsel-nylon-linux-
+ ld-emul = elf32ltsmip
+ else
+ tool-prefix = mips-linux-
@@ -29,6 +29,8 @@
endif
diff --git a/recipes/linux/linux-mtx-1-2.4.27/32-usbserial-stalled-hack.diff b/recipes/linux/linux-mtx-1-2.4.27/32-usbserial-stalled-hack.diff
index bfc1af9db9..1651bd5678 100644
--- a/recipes/linux/linux-mtx-1-2.4.27/32-usbserial-stalled-hack.diff
+++ b/recipes/linux/linux-mtx-1-2.4.27/32-usbserial-stalled-hack.diff
@@ -1,5 +1,5 @@
---- linux-2.4.27-mtx1/drivers/usb/serial/usbserial.c 2005-08-28 20:23:40.000000000 +0200
-+++ linux-2.4.27-mtx1/drivers/usb/serial/usbserial.c 2005-08-28 20:23:12.000000000 +0200
+--- linux-old/drivers/usb/serial/usbserial.c 2005-08-28 20:23:40.000000000 +0200
++++ linux/drivers/usb/serial/usbserial.c 2005-08-28 20:23:12.000000000 +0200
@@ -499,6 +499,19 @@
/* get_usb_serial checks port->tty, so cannot be used */
serial = port->serial;
diff --git a/recipes/linux/linux-mtx-1-2.4.27/33-usbserial-bulk_in_size-4096.diff b/recipes/linux/linux-mtx-1-2.4.27/33-usbserial-bulk_in_size-4096.diff
index e615a92fa4..91690057e2 100644
--- a/recipes/linux/linux-mtx-1-2.4.27/33-usbserial-bulk_in_size-4096.diff
+++ b/recipes/linux/linux-mtx-1-2.4.27/33-usbserial-bulk_in_size-4096.diff
@@ -1,5 +1,53 @@
---- linux/drivers/usb/serial/usbserial.c~33-usbserial-bulk_in_size-4096.diff 2006-03-31 15:05:46.674445000 +0200
-+++ linux/drivers/usb/serial/usbserial.c 2006-04-07 12:23:56.970400500 +0200
+--- linux/drivers/usb/acm.c.orig 2006-04-07 13:56:33.837683000 +0200
++++ linux/drivers/usb/acm.c 2006-04-07 12:14:37.995466750 +0200
+@@ -155,6 +155,11 @@
+ unsigned char clocal; /* termios CLOCAL */
+ };
+
++/* global params controlling max sizes for read, write, control */
++static int maxszr = 0;
++static int maxszw = 0;
++static int maxszc = 0;
++
+ static struct usb_driver acm_driver;
+ static struct tty_driver acm_tty_driver;
+ static struct acm *acm_table[ACM_TTY_MINORS];
+@@ -573,9 +578,13 @@
+ }
+ memset(acm, 0, sizeof(struct acm));
+
+- ctrlsize = epctrl->wMaxPacketSize;
+- readsize = epread->wMaxPacketSize;
+- acm->writesize = epwrite->wMaxPacketSize;
++ ctrlsize = (epctrl->wMaxPacketSize > maxszc)?
++ epctrl->wMaxPacketSize: maxszc;
++ readsize = (epread->wMaxPacketSize > maxszr)?
++ epread->wMaxPacketSize: maxszr;
++ acm->writesize = (epwrite->wMaxPacketSize > maxszw)?
++ epwrite->wMaxPacketSize: maxszw;
++
+ acm->iface = cfacm->interface + j;
+ acm->minor = minor;
+ acm->dev = dev;
+@@ -740,6 +749,16 @@
+ module_init(acm_init);
+ module_exit(acm_exit);
+
++
++MODULE_PARM(maxszr, "i");
++MODULE_PARM_DESC(maxszr, "User specified USB endpoint read size");
++
++MODULE_PARM(maxszw, "i");
++MODULE_PARM_DESC(maxszw, "User specified USB endpoint write size");
++
++MODULE_PARM(maxszc, "i");
++MODULE_PARM_DESC(maxszc, "User specified USB endpoint control size");
++
+ MODULE_AUTHOR( DRIVER_AUTHOR );
+ MODULE_DESCRIPTION( DRIVER_DESC );
+ MODULE_LICENSE("GPL");
+--- linux/drivers/usb/serial/usbserial.c-orig 2009-02-27 23:34:48.000000000 +0100
++++ linux/drivers/usb/serial/usbserial.c 2009-02-27 23:34:54.000000000 +0100
@@ -332,6 +332,9 @@
static __u16 vendor = 0x05f9;
static __u16 product = 0xffff;
@@ -7,7 +55,7 @@
+static int count_smaller64 = 0;
+static int count_bigger64 = 0;
+
- static struct usb_device_id generic_device_ids[5]; /* Initially all zeroes. */
+ static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
/* All of the device info needed for the Generic Serial Converter */
@@ -396,6 +399,10 @@
@@ -66,7 +114,7 @@
port->interrupt_in_endpointAddress = endpoint->bEndpointAddress;
port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!port->interrupt_in_buffer) {
-@@ -1807,6 +1825,7 @@
+@@ -1798,6 +1816,7 @@
static void __exit usb_serial_exit(void)
{
@@ -74,7 +122,7 @@
#ifdef CONFIG_USB_SERIAL_GENERIC
/* remove our generic driver */
-@@ -1874,6 +1893,15 @@
+@@ -1865,6 +1884,15 @@
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
@@ -90,51 +138,3 @@
#ifdef CONFIG_USB_SERIAL_GENERIC
MODULE_PARM(vendor, "h");
MODULE_PARM_DESC(vendor, "User specified USB idVendor");
---- linux/drivers/usb/acm.c.orig 2006-04-07 13:56:33.837683000 +0200
-+++ linux/drivers/usb/acm.c 2006-04-07 12:14:37.995466750 +0200
-@@ -155,6 +155,11 @@
- unsigned char clocal; /* termios CLOCAL */
- };
-
-+/* global params controlling max sizes for read, write, control */
-+static int maxszr = 0;
-+static int maxszw = 0;
-+static int maxszc = 0;
-+
- static struct usb_driver acm_driver;
- static struct tty_driver acm_tty_driver;
- static struct acm *acm_table[ACM_TTY_MINORS];
-@@ -573,9 +578,13 @@
- }
- memset(acm, 0, sizeof(struct acm));
-
-- ctrlsize = epctrl->wMaxPacketSize;
-- readsize = epread->wMaxPacketSize;
-- acm->writesize = epwrite->wMaxPacketSize;
-+ ctrlsize = (epctrl->wMaxPacketSize > maxszc)?
-+ epctrl->wMaxPacketSize: maxszc;
-+ readsize = (epread->wMaxPacketSize > maxszr)?
-+ epread->wMaxPacketSize: maxszr;
-+ acm->writesize = (epwrite->wMaxPacketSize > maxszw)?
-+ epwrite->wMaxPacketSize: maxszw;
-+
- acm->iface = cfacm->interface + j;
- acm->minor = minor;
- acm->dev = dev;
-@@ -740,6 +749,16 @@
- module_init(acm_init);
- module_exit(acm_exit);
-
-+
-+MODULE_PARM(maxszr, "i");
-+MODULE_PARM_DESC(maxszr, "User specified USB endpoint read size");
-+
-+MODULE_PARM(maxszw, "i");
-+MODULE_PARM_DESC(maxszw, "User specified USB endpoint write size");
-+
-+MODULE_PARM(maxszc, "i");
-+MODULE_PARM_DESC(maxszc, "User specified USB endpoint control size");
-+
- MODULE_AUTHOR( DRIVER_AUTHOR );
- MODULE_DESCRIPTION( DRIVER_DESC );
- MODULE_LICENSE("GPL");
diff --git a/recipes/linux/linux-mtx-1-2.4.27/43-usbserial-27-32-backport.diff b/recipes/linux/linux-mtx-1-2.4.27/43-usbserial-27-32-backport.diff
new file mode 100644
index 0000000000..6a98f76c7a
--- /dev/null
+++ b/recipes/linux/linux-mtx-1-2.4.27/43-usbserial-27-32-backport.diff
@@ -0,0 +1,33 @@
+--- linux/drivers/usb/serial/usbserial.c-27 2006-06-27 10:26:06.294476250 +0200
++++ linux/drivers/usb/serial/usbserial.c 2006-06-27 10:30:31.011020000 +0200
+@@ -528,8 +528,18 @@
+ down(&port->sem);
+ dbg("%s - port %d len %d backlog %d", __FUNCTION__,
+ port->number, job->len, port->write_backlog);
+- if (port->tty != NULL)
+- __serial_write(port, 0, job->buff, job->len);
++ if (port->tty != NULL) {
++ int rc;
++ int sent = 0;
++ while (sent < job->len) {
++ rc = __serial_write(port, 0, job->buff + sent, job->len - sent);
++ if ((rc < 0) || signal_pending(current))
++ break;
++ sent += rc;
++ if ((sent < job->len) && current->need_resched)
++ schedule();
++ }
++ }
+ up(&port->sem);
+
+ spin_lock_irqsave(&post_lock, flags);
+@@ -725,6 +735,9 @@
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ int rc;
+
++ if (!port)
++ return -ENODEV;
++
+ if (!in_interrupt()) {
+ /*
+ * Run post_list to reduce a possiblity of reordered writes.
diff --git a/recipes/linux/linux-mtx-1-2.4.27/45-acm-tty-and-sb2.patch b/recipes/linux/linux-mtx-1-2.4.27/45-acm-tty-and-sb2.patch
new file mode 100644
index 0000000000..4561facb73
--- /dev/null
+++ b/recipes/linux/linux-mtx-1-2.4.27/45-acm-tty-and-sb2.patch
@@ -0,0 +1,721 @@
+--- linux/drivers/usb/acm.c-orig 2007-04-13 18:32:16.352672105 +0200
++++ linux/drivers/usb/acm.c 2007-04-13 18:33:21.063526545 +0200
+@@ -124,6 +124,9 @@
+ #define ACM_CTRL_PARITY 0x20
+ #define ACM_CTRL_OVERRUN 0x40
+
++// some devices don't have one comm and one data interface, but only one interface with endpoints for comm and data
++#define SINGLE_IF_ACM 0x01
++
+ /*
+ * Line speed and caracter encoding.
+ */
+@@ -139,6 +142,8 @@
+ * Internal driver structures.
+ */
+
++#define TD_SIZE 16384
++
+ struct acm {
+ struct usb_device *dev; /* the coresponding usb device */
+ struct usb_interface *iface; /* the interfaces - +0 control +1 data */
+@@ -153,12 +158,23 @@
+ unsigned int minor; /* acm minor number */
+ unsigned char throttle; /* throttled by tty layer */
+ unsigned char clocal; /* termios CLOCAL */
++ unsigned long throttle_start;
++ unsigned char resubmit_to_unthrottle; /* Leftover data from last operation */
++ unsigned char *throttle_data;
++ int td_len;
++ int td_busy;
++ unsigned char used_interfaces;
++ struct semaphore mutex;
+ };
+
++#define mutex_lock(x) down(x)
++#define mutex_unlock(x) up(x)
++
+ /* global params controlling max sizes for read, write, control */
+ static int maxszr = 0;
+ static int maxszw = 0;
+ static int maxszc = 0;
++static int nonlegacy = 0;
+
+ static struct usb_driver acm_driver;
+ static struct tty_driver acm_tty_driver;
+@@ -167,6 +183,95 @@
+ #define ACM_READY(acm) (acm && acm->dev && acm->used)
+
+ /*
++ * Helper functions to optimize throttleing
++ */
++static int
++acm_fill_tty(struct urb *urb, struct tty_struct *tty, unsigned char *data, int length)
++{
++ struct acm *acm = urb->context;
++ int n = 0;
++ /*printk("acm_fill_tty: %d bytes\n", length);*/
++ if (!urb->status && !acm->throttle) {
++ for (n = 0; n < length && !acm->throttle; n++) {
++ /* if we insert more than TTY_FLIPBUF_SIZE characters,
++ * we drop them. */
++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
++ tty_flip_buffer_push(tty);
++ }
++ tty_insert_flip_char(tty, data[n], 0);
++ }
++ tty_flip_buffer_push(tty);
++ }
++ /*printk("copied %d bytes.\n", n);*/
++ return n;
++}
++
++static int
++acm_shift_if_throttle(unsigned char *data, int *length, int shift_by)
++{
++ if (shift_by < *length) {
++ dbg("need to shift uncopied %d bytes to front.", *length - shift_by);
++ memmove(data, data + shift_by, *length - shift_by);
++ *length -= shift_by;
++ return 1;
++ }
++ return 0;
++}
++
++static int
++acm_buffer_if_thottle(struct acm *acm, unsigned char *data, int start, int *length)
++{
++ int copied = *length;
++ if (start < *length) {
++ int space = TD_SIZE - acm->td_len;
++ int needed = *length - start;
++ copied = (space < needed)? space: needed;
++ dbg("need to push %d to throttle buffer, can copy %d.",
++ needed, copied);
++ memcpy(acm->throttle_data + acm->td_len, data, copied);
++ acm->td_len += copied;
++ *length -= copied;
++ }
++ return copied;
++}
++
++static int
++acm_empty_throttle(struct urb *urb, struct tty_struct *tty)
++{
++ unsigned long flags;
++ struct acm *acm = urb->context;
++
++ save_flags(flags);
++ cli();
++
++ if (acm->td_busy) {
++ restore_flags(flags);
++ return 0;
++ }
++ acm->td_busy = 1;
++ restore_flags(flags);
++
++ if (acm->td_len > 0) {
++
++ dbg("acm_empty_throttle: trying to empty throttle buffer: %d bytes.",
++ acm->td_len);
++
++ /* if there has been something left from previous operations
++ * we try to complete this before looking at the urb */
++ int copied = acm_fill_tty(urb, tty, acm->throttle_data, acm->td_len);
++ if (acm_shift_if_throttle(acm->throttle_data, &acm->td_len, copied)) {
++ /* we were unable to empty the throttle data, so we can't
++ * copy anything more now */
++ acm->td_busy = 0;
++ return 0;
++ }
++ acm->td_len = 0;
++ }
++ acm->td_busy = 0;
++ return 1;
++}
++
++/*
+ * Functions for ACM control messages.
+ */
+
+@@ -174,7 +279,10 @@
+ {
+ int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
+ request, USB_RT_ACM, value, acm->iface[0].altsetting[0].bInterfaceNumber, buf, len, HZ * 5);
+- dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
++ if (retval < 0)
++ err("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
++ else
++ dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
+ return retval < 0 ? retval : 0;
+ }
+
+@@ -191,10 +299,12 @@
+ struct acm *acm = urb->context;
+ struct usb_ctrlrequest *dr = urb->transfer_buffer;
+ unsigned char *data = (unsigned char *)(dr + 1);
+- int newctrl;
++ int newctrl, s1, s2;
+
+ if (!ACM_READY(acm)) return;
+
++ //err("acm_ctrl_irq %p %i", urb, dr->bRequestType);
++
+ if (urb->status < 0) {
+ dbg("nonzero ctrl irq status received: %d", urb->status);
+ return;
+@@ -226,8 +336,15 @@
+
+ return;
+
++ case 0x2a:
++ s1 = le32_to_cpup((__u32 *) data);
++ s2 = le32_to_cpup((__u32 *) (data+4));
++
++ dbg("acm.c: ctrl 0x2a: idx %i len %i speed %i %i", dr->wIndex, dr->wLength, s1, s2);
++ return;
++
+ default:
+- dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d",
++ err("unknown control event received: request %d index %d len %d data0 %d data1 %d",
+ dr->bRequest, dr->wIndex, dr->wLength, data[0], data[1]);
+ return;
+ }
+@@ -238,36 +355,39 @@
+ struct acm *acm = urb->context;
+ struct tty_struct *tty = acm->tty;
+ unsigned char *data = urb->transfer_buffer;
+- int i = 0;
++ int copied = 0;
++ int buffered = 0;
+
+ if (!ACM_READY(acm)) return;
+
+- if (urb->status)
+- dbg("nonzero read bulk status received: %d", urb->status);
++ if (urb->status) {
++ err("nonzero read bulk status received: %d", urb->status);
++ }
+
+- if (!urb->status && !acm->throttle) {
+- for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
+- /* if we insert more than TTY_FLIPBUF_SIZE characters,
+- * we drop them. */
+- if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+- tty_flip_buffer_push(tty);
+- }
+- tty_insert_flip_char(tty, data[i], 0);
+- }
+- tty_flip_buffer_push(tty);
++ if (!acm_empty_throttle(urb, tty)) {
++ dbg("could not empty throttle buffer, entering throttle state, acm->td_busy: %d.", acm->td_busy);
+ }
+
++ /* got here, either there was nothing in the throttle data or it could
++ * all be copied without throttleing again */
++ copied = acm_fill_tty(urb, tty, data, urb->actual_length);
+ if (acm->throttle) {
+- memmove(data, data + i, urb->actual_length - i);
+- urb->actual_length -= i;
+- return;
++ int length = urb->actual_length;
++ buffered = acm_buffer_if_thottle(acm, data, copied, &urb->actual_length);
++ if (buffered < length - copied
++ && acm_shift_if_throttle(data, &urb->actual_length, copied + buffered)) {
++ dbg("need to resubmit to unthrottle\n");
++ acm->resubmit_to_unthrottle = 1;
++ return;
++ }
+ }
+
+ urb->actual_length = 0;
+ urb->dev = acm->dev;
+
+- if (usb_submit_urb(urb))
++ if (usb_submit_urb(urb)) {
+ dbg("failed resubmitting read urb");
++ }
+ }
+
+ static void acm_write_bulk(struct urb *urb)
+@@ -283,6 +403,9 @@
+ mark_bh(IMMEDIATE_BH);
+ }
+
++static int unlinking_in_progress=0;
++static int closing=0;
++
+ static void acm_softint(void *private)
+ {
+ struct acm *acm = private;
+@@ -306,34 +429,57 @@
+
+ if (!acm || !acm->dev) return -EINVAL;
+
++ mutex_lock (&acm->mutex);
++
+ tty->driver_data = acm;
+ acm->tty = tty;
+
+ MOD_INC_USE_COUNT;
+
+- lock_kernel();
++ if ( closing )
++ err("acm_tty_open: potential possibility of race condition detected");
++
++ if ( unlinking_in_progress ) {
++ err("acm_tty_open: cannot open because unlinking_in_progress %i", acm->used);
++ mutex_unlock (&acm->mutex);
++ return -1;
++ }
+
+- if (acm->used++) {
+- unlock_kernel();
+- return 0;
+- }
++ if (acm->used) {
++ acm->used++;
++ mutex_unlock (&acm->mutex);
++ return 0;
++ }
+
+- unlock_kernel();
++ unlinking_in_progress=1;
++ err("acm_tty_open: %i %p !!", acm->used, tty);
++
++ acm->resubmit_to_unthrottle = 0;
++ acm->td_len = 0;
++ acm->td_busy = 0;
+
+ acm->ctrlurb.dev = acm->dev;
+ if (usb_submit_urb(&acm->ctrlurb))
+- dbg("usb_submit_urb(ctrl irq) failed");
++ dbg("acm open: usb_submit_urb(ctrl irq) failed");
++ else
++ dbg("acm open: ctrlurb %p submitted", &acm->ctrlurb);
++
++ acm->used++;
++ acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS);
+
+ acm->readurb.dev = acm->dev;
+ if (usb_submit_urb(&acm->readurb))
+- dbg("usb_submit_urb(read bulk) failed");
+-
+- acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS);
++ dbg("acm open: usb_submit_urb(read bulk) failed");
++ else
++ dbg("acm open: readurb %p submitted", &acm->readurb);
+
+- /* force low_latency on so that our tty_push actually forces the data through,
++ /* force low_latency on so that our tty_push actually forces the data through,
+ otherwise it is scheduled, and with high data rates data can get lost. */
+ tty->low_latency = 1;
+
++ unlinking_in_progress=0;
++ mutex_unlock (&acm->mutex);
++
+ return 0;
+ }
+
+@@ -343,19 +489,35 @@
+
+ if (!acm || !acm->used) return;
+
+- if (!--acm->used) {
+- if (acm->dev) {
+- acm_set_control(acm, acm->ctrlout = 0);
+- usb_unlink_urb(&acm->ctrlurb);
+- usb_unlink_urb(&acm->writeurb);
+- usb_unlink_urb(&acm->readurb);
+- } else {
+- tty_unregister_devfs(&acm_tty_driver, acm->minor);
+- acm_table[acm->minor] = NULL;
+- kfree(acm);
+- }
++ mutex_lock (&acm->mutex);
++
++ closing = 1;
++ if (--acm->used) {
++ closing=0;
++ MOD_DEC_USE_COUNT;
++ mutex_unlock (&acm->mutex);
++ return;
++ }
++ unlinking_in_progress = 1;
++
++ err("acm_tty_close: %i %p", acm->used, tty);
++
++ if (acm->dev) {
++ acm_set_control(acm, acm->ctrlout = 0);
++ usb_unlink_urb(&acm->ctrlurb);
++ usb_unlink_urb(&acm->writeurb);
++ usb_unlink_urb(&acm->readurb);
++ } else {
++ tty_unregister_devfs(&acm_tty_driver, acm->minor);
++ acm_table[acm->minor] = NULL;
++ kfree(acm->throttle_data);
++ kfree(acm);
+ }
++
++ closing=0;
++ unlinking_in_progress = 0;
+ MOD_DEC_USE_COUNT;
++ mutex_unlock (&acm->mutex);
+ }
+
+ static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+@@ -363,8 +525,16 @@
+ struct acm *acm = tty->driver_data;
+
+ if (!ACM_READY(acm)) return -EINVAL;
+- if (acm->writeurb.status == -EINPROGRESS) return 0;
+- if (!count) return 0;
++
++ if (acm->writeurb.status == -EINPROGRESS) {
++ dbg("tty_write in progress");
++ return 0;
++ }
++
++ if (!count) {
++ dbg("tty_write: nothing to write");
++ return 0;
++ }
+
+ count = (count > acm->writesize) ? acm->writesize : count;
+
+@@ -401,22 +571,44 @@
+ {
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return;
++ dbg("acm_tty_throttle ON %ld ---> %ld", jiffies-acm->throttle_start, jiffies);
+ acm->throttle = 1;
++ acm->throttle_start = jiffies;
+ }
+
+ static void acm_tty_unthrottle(struct tty_struct *tty)
+ {
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return;
++ dbg("acm_tty_throttle OFF %ld ---> %ld", jiffies, jiffies-acm->throttle_start);
+ acm->throttle = 0;
+- if (acm->readurb.status != -EINPROGRESS)
++
++ if (!acm_empty_throttle(&acm->readurb, tty)) {
++ if (acm->td_busy) {
++ printk("***** pending acm_empty_throttle!\n");
++ } else {
++ dbg("throttle not emptied.\n");
++ }
++ }
++
++ if (acm->resubmit_to_unthrottle != 0) {
++ dbg("resubmit_to_unthrottle: acm_read_bulk");
++ acm->resubmit_to_unthrottle = 0;
+ acm_read_bulk(&acm->readurb);
++ }
+ }
+
+ static void acm_tty_break_ctl(struct tty_struct *tty, int state)
+ {
+ struct acm *acm = tty->driver_data;
++
+ if (!ACM_READY(acm)) return;
++
++ if (nonlegacy) {
++ err("non-legacy port, skipping acm_tty_break_ctl");
++ return;
++ }
++
+ if (acm_send_break(acm, state ? 0xffff : 0))
+ dbg("send break failed");
+ }
+@@ -455,7 +647,19 @@
+ case TIOCMBIC: newctrl &= ~mask; break;
+ }
+
+- if (acm->ctrlout == newctrl) return 0;
++ if (acm->ctrlout == newctrl) {
++ dbg("acm_tty_ioctl: set old state %x", newctrl);
++ return 0;
++ }
++
++ err("acm_tty_ioctl: %s%s%s -> dtr%s rts%s (%lx)",
++ cmd==TIOCMBIC?"Clear":(cmd==TIOCMBIS?"Set":"SET"),
++ mask & ACM_CTRL_DTR ? " DTR":"",
++ mask & ACM_CTRL_RTS ? " RTS":"",
++ newctrl & ACM_CTRL_DTR ? "+":"-",
++ newctrl & ACM_CTRL_RTS ? "+":"-",
++ arg);
++
+ return acm_set_control(acm, acm->ctrlout = newctrl);
+ }
+
+@@ -483,6 +687,12 @@
+
+ if (!ACM_READY(acm)) return;
+
++ if (nonlegacy) {
++ acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
++ dbg("non-legacy port, skipping acm_tty_set_termios");
++ return;
++ }
++
+ newline.speed = cpu_to_le32p(acm_tty_speed +
+ (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
+ newline.stopbits = termios->c_cflag & CSTOPB ? 2 : 0;
+@@ -518,34 +727,64 @@
+ struct usb_config_descriptor *cfacm;
+ struct usb_interface_descriptor *ifcom, *ifdata;
+ struct usb_endpoint_descriptor *epctrl, *epread, *epwrite;
+- int readsize, ctrlsize, minor, i, j;
++ int readsize, ctrlsize, minor, i;
+ unsigned char *buf;
++ unsigned char used_interfaces=2;
+
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+
+ cfacm = dev->config + i;
+
+- dbg("probing config %d", cfacm->bConfigurationValue);
++ ifcom = cfacm->interface[ifnum].altsetting + 0;
++
++ if (id->driver_info == SINGLE_IF_ACM) {
++ printk("using single_if_acm\n");
++ struct usb_endpoint_descriptor *ep=ifcom->endpoint;
++ int k;
++
++ if (ifcom->bNumEndpoints != 3) {
++ continue;
++ }
++
++ epctrl = epread = epwrite = NULL;
++ for (k=0; k<3; ++k, ++ep) {
++ if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT &&
++ (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
++ epctrl = ep;
++ } else if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK &&
++ (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
++ epread = ep;
++ } else if ( (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
++ epwrite = ep;
++ }
++ }
+
+- for (j = 0; j < cfacm->bNumInterfaces - 1; j++) {
++ if ( !epctrl || !epread || !epwrite ) {
++ dbg("SINGLE_IF_ACM acm_probe inv eps epctrl %s epread %s epwrite %s", epctrl?"ok":"missing",
++ epread?"ok":"missing", epwrite?"ok":"missing");
++ dbg("SINGLE_IF_ACM Invalid enpoint configuration");
++ continue;
++ }
+
+- if (usb_interface_claimed(cfacm->interface + j) ||
+- usb_interface_claimed(cfacm->interface + j + 1))
++ used_interfaces = 1;
++ } else {
++ if ((ifnum+1)>=cfacm->bNumInterfaces || usb_interface_claimed(cfacm->interface + ifnum + 1)) {
++ // no data interface available
+ continue;
++ }
+
+- ifcom = cfacm->interface[j].altsetting + 0;
+- ifdata = cfacm->interface[j + 1].altsetting + 0;
++ ifdata = cfacm->interface[ifnum + 1].altsetting + 0;
+
+ if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2) {
+- ifcom = cfacm->interface[j + 1].altsetting + 0;
+- ifdata = cfacm->interface[j].altsetting + 0;
++ ifcom = cfacm->interface[ifnum + 1].altsetting + 0;
++ ifdata = cfacm->interface[ifnum].altsetting + 0;
+ if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2)
+ continue;
+ }
+
+ if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 ||
+- ifcom->bInterfaceProtocol < 1 || ifcom->bInterfaceProtocol > 6 ||
+- ifcom->bNumEndpoints < 1)
++ ifcom->bInterfaceProtocol < 1 || ifcom->bInterfaceProtocol > 6 ||
++ ifcom->bNumEndpoints < 1)
+ continue;
+
+ epctrl = ifcom->endpoint + 0;
+@@ -553,76 +792,86 @@
+ epwrite = ifdata->endpoint + 1;
+
+ if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 ||
+- (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 ||
+- ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80)
++ (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 ||
++ ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80)
+ continue;
+
+- dbg("using interface %d\n", j);
+-
+ if ((epread->bEndpointAddress & 0x80) != 0x80) {
+ epread = ifdata->endpoint + 1;
+ epwrite = ifdata->endpoint + 0;
+ }
++ }
+
+- usb_set_configuration(dev, cfacm->bConfigurationValue);
++ usb_set_configuration(dev, cfacm->bConfigurationValue);
+
+- for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
+- if (acm_table[minor]) {
+- err("no more free acm devices");
+- return NULL;
+- }
++ for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
++ if (acm_table[minor]) {
++ err("no more free acm devices");
++ return NULL;
++ }
+
+- if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) {
+- err("out of memory");
+- return NULL;
+- }
+- memset(acm, 0, sizeof(struct acm));
++ if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) {
++ err("out of memory");
++ return NULL;
++ }
++ memset(acm, 0, sizeof(struct acm));
+
+- ctrlsize = (epctrl->wMaxPacketSize > maxszc)?
+- epctrl->wMaxPacketSize: maxszc;
+- readsize = (epread->wMaxPacketSize > maxszr)?
+- epread->wMaxPacketSize: maxszr;
+- acm->writesize = (epwrite->wMaxPacketSize > maxszw)?
+- epwrite->wMaxPacketSize: maxszw;
+-
+- acm->iface = cfacm->interface + j;
+- acm->minor = minor;
+- acm->dev = dev;
+-
+- acm->tqueue.routine = acm_softint;
+- acm->tqueue.data = acm;
+-
+- if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
+- err("out of memory");
+- kfree(acm);
+- return NULL;
+- }
++ ctrlsize = (epctrl->wMaxPacketSize > maxszc)?
++ epctrl->wMaxPacketSize: maxszc;
++ readsize = (epread->wMaxPacketSize > maxszr)?
++ epread->wMaxPacketSize: maxszr;
++ acm->writesize = (epwrite->wMaxPacketSize > maxszw)?
++ epwrite->wMaxPacketSize: maxszw;
++
++ init_MUTEX (&acm->mutex);
++ if (!(acm->throttle_data = kmalloc(TD_SIZE * sizeof (*acm->throttle_data), GFP_KERNEL))) {
++ err("out of memory (throttle_data)");
++ kfree(acm);
++ return NULL;
++ }
++ acm->iface = cfacm->interface + ifnum;
++ acm->minor = minor;
++ acm->dev = dev;
+
+- FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
+- buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
++ acm->used_interfaces = used_interfaces;
+
+- FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
+- buf += ctrlsize, readsize, acm_read_bulk, acm);
+- acm->readurb.transfer_flags |= USB_NO_FSBR;
++ acm->tqueue.routine = acm_softint;
++ acm->tqueue.data = acm;
+
+- FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
+- buf += readsize, acm->writesize, acm_write_bulk, acm);
+- acm->writeurb.transfer_flags |= USB_NO_FSBR;
++ if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
++ err("out of memory (urb buf)");
++ kfree(acm);
++ return NULL;
++ }
+
+- printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor);
++ FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
++ buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
+
+- acm_set_control(acm, acm->ctrlout);
++ FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
++ buf += ctrlsize, readsize, acm_read_bulk, acm);
++ acm->readurb.transfer_flags |= USB_NO_FSBR;
+
+- acm->line.speed = cpu_to_le32(9600);
+- acm->line.databits = 8;
+- acm_set_line(acm, &acm->line);
++ FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
++ buf += readsize, acm->writesize, acm_write_bulk, acm);
++ acm->writeurb.transfer_flags |= USB_NO_FSBR;
+
+- usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm);
+- usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm);
++ printk(KERN_INFO "ttyACM%d: USB ACM device C %p W %p R %p %x\n", minor, &acm->ctrlurb,
++ &acm->writeurb, &acm->readurb, acm->ctrlout);
++
++ acm_set_control(acm, acm->ctrlout);
+
+- tty_register_devfs(&acm_tty_driver, 0, minor);
+- return acm_table[minor] = acm;
++ acm->line.speed = cpu_to_le32(9600);
++ acm->line.databits = 8;
++ acm_set_line(acm, &acm->line);
++
++ if ( acm->used_interfaces == 2 ) {
++ // only just checked interface is claimed automatically, so claim data interface too
++ usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm);
+ }
++
++ tty_register_devfs(&acm_tty_driver, 0, minor);
++
++ return acm_table[minor] = acm;
+ }
+
+ return NULL;
+@@ -646,7 +895,9 @@
+ kfree(acm->ctrlurb.transfer_buffer);
+
+ usb_driver_release_interface(&acm_driver, acm->iface + 0);
+- usb_driver_release_interface(&acm_driver, acm->iface + 1);
++ if ( acm->used_interfaces == 2 ) {
++ usb_driver_release_interface(&acm_driver, acm->iface + 1);
++ }
+
+ if (!acm->used) {
+ tty_unregister_devfs(&acm_tty_driver, acm->minor);
+@@ -665,6 +916,9 @@
+
+ static struct usb_device_id acm_ids[] = {
+ { USB_DEVICE(0x22B8, 0x1005) }, /* Motorola TimePort 280 */
++ { USB_DEVICE(0x05C6, 0x7001), driver_info: SINGLE_IF_ACM }, /* Siemens HC15/HC25 */
++ { USB_DEVICE(0x0681, 0x003e), driver_info: SINGLE_IF_ACM }, /* Siemens HC15/HC25 */
++ { USB_DEVICE(0x22B8, 0x1006) },
+ { USB_DEVICE_INFO(USB_CLASS_COMM, 0, 0) },
+ { USB_DEVICE_INFO(USB_CLASS_COMM, 2, 0) },
+ { }
+@@ -735,7 +989,7 @@
+ return -1;
+ }
+
+- info(DRIVER_VERSION ":" DRIVER_DESC);
++ info(DRIVER_VERSION ":" DRIVER_DESC "(non-legacy %d)", nonlegacy);
+
+ return 0;
+ }
+@@ -759,7 +1013,9 @@
+ MODULE_PARM(maxszc, "i");
+ MODULE_PARM_DESC(maxszc, "User specified USB endpoint control size");
+
++MODULE_PARM(nonlegacy, "i");
++MODULE_PARM_DESC(nonlegacy, "Set this to 1 to for use with non-legacy device");
++
+ MODULE_AUTHOR( DRIVER_AUTHOR );
+ MODULE_DESCRIPTION( DRIVER_DESC );
+ MODULE_LICENSE("GPL");
+-