--- 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");
-