diff options
author | Philippe De Swert <philippedeswert@scarlet.be> | 2007-04-18 17:14:58 +0000 |
---|---|---|
committer | Philippe De Swert <philippedeswert@scarlet.be> | 2007-04-18 17:14:58 +0000 |
commit | ac1f56acc1b2aa9819927ac52d697bc8baeadf90 (patch) | |
tree | b8d9d9ba7c02ed91f7e446523000ca045d731575 /packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch | |
parent | ede4d65f3c0d4d2dd569a3dfdb3cda4a00ebfb0a (diff) | |
parent | fd06e9a14b39a2132c5689fc36dd785132839aff (diff) |
merge of '3387d5275b731c38ee945a83c391a2cc78cbc1a8'
and '699c2c7a608569e8c0f66321183fdc3846699014'
Diffstat (limited to 'packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch')
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch b/packages/linux/linux-mtx-2-2.4.27/45-acm-tty-and-sb2.patch new file mode 100644 index 0000000000..4561facb73 --- /dev/null +++ b/packages/linux/linux-mtx-2-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"); +- |