From: Ben Hutchings Date: Tue, 10 Nov 2015 20:05:26 +0000 (+0000) Subject: Sync code with cdc-acm.{c,h} in Linux 4.3 X-Git-Url: https://git.decadent.org.uk/gitweb/?p=exar-uart-driver.git;a=commitdiff_plain;h=3acd44efa3454690086590ea23f4f0d5f1b37e59;ds=sidebyside Sync code with cdc-acm.{c,h} in Linux 4.3 --- diff --git a/vizzini.c b/vizzini.c index 56df586..6a87455 100644 --- a/vizzini.c +++ b/vizzini.c @@ -1,6 +1,13 @@ /* * vizzini.c * + * Copyright (c) 1999 Armin Fuerst + * Copyright (c) 1999 Pavel Machek + * Copyright (c) 1999 Johannes Erdfelt + * Copyright (c) 2000 Vojtech Pavlik + * Copyright (c) 2004 Oliver Neukum + * Copyright (c) 2005 David Kubicek + * Copyright (c) 2011 Johan Hovold * Copyright (c) 2013 Exar Corporation, Inc. * * ChangeLog: @@ -37,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -45,6 +51,7 @@ #include #include #include +#include #include #include "vizzini.h" @@ -53,50 +60,123 @@ #define DRIVER_AUTHOR "Ravi Reddy" #define DRIVER_DESC "Exar USB UART Driver for XR21V141x " -static struct usb_driver xr21v141x_driver; -static struct tty_driver *xr21v141x_tty_driver; -static struct xr21v141x *xr21v141x_table[XR21V141X_TTY_MINORS]; +static struct usb_driver acm_driver; +static struct tty_driver *acm_tty_driver; + +static DEFINE_IDR(acm_minors); +static DEFINE_MUTEX(acm_minors_lock); + +static void acm_tty_set_termios(struct tty_struct *tty, + struct ktermios *termios_old); -static DEFINE_MUTEX(xr21v141x_table_lock); +/* + * acm_minors accessors + */ + +/* + * Look up an ACM structure by minor. If found and not disconnected, increment + * its refcount and return it with its mutex held. + */ +static struct acm *acm_get_by_minor(unsigned int minor) +{ + struct acm *acm; + + mutex_lock(&acm_minors_lock); + acm = idr_find(&acm_minors, minor); + if (acm) { + mutex_lock(&acm->mutex); + if (acm->disconnected) { + mutex_unlock(&acm->mutex); + acm = NULL; + } else { + tty_port_get(&acm->port); + mutex_unlock(&acm->mutex); + } + } + mutex_unlock(&acm_minors_lock); + return acm; +} + +/* + * Try to find an available minor number and if found, associate it with 'acm'. + */ +static int acm_alloc_minor(struct acm *acm) +{ + int minor; + + mutex_lock(&acm_minors_lock); + minor = idr_alloc(&acm_minors, acm, 0, ACM_TTY_MINORS, GFP_KERNEL); + mutex_unlock(&acm_minors_lock); + + return minor; +} + +/* Release the minor number associated with 'acm'. */ +static void acm_release_minor(struct acm *acm) +{ + mutex_lock(&acm_minors_lock); + idr_remove(&acm_minors, acm->minor); + mutex_unlock(&acm_minors_lock); +} /* * Functions for ACM control messages. */ -static int xr21v141x_ctrl_msg(struct xr21v141x *xr21v141x, int request, int value, +static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len) { - int retval = usb_control_msg(xr21v141x->dev, usb_sndctrlpipe(xr21v141x->dev, 0), + int retval; + + retval = usb_autopm_get_interface(acm->control); + if (retval) + return retval; + + retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), request, USB_RT_ACM, value, - xr21v141x->control->altsetting[0].desc.bInterfaceNumber, + acm->control->altsetting[0].desc.bInterfaceNumber, buf, len, 5000); - dev_dbg(&xr21v141x->control->dev, + + dev_dbg(&acm->control->dev, "%s - rq 0x%02x, val %#x, len %#x, result %d\n", __func__, request, value, len, retval); + + usb_autopm_put_interface(acm->control); + return retval < 0 ? retval : 0; } -#define xr21v141x_set_control(xr21v141x, control) \ - xr21v141x_ctrl_msg(xr21v141x, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) -#define xr21v141x_set_line(xr21v141x, line) \ - xr21v141x_ctrl_msg(xr21v141x, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) -#define xr21v141x_send_break(xr21v141x, ms) \ - xr21v141x_ctrl_msg(xr21v141x, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) +/* devices aren't required to support these requests. + * the cdc acm descriptor tells whether they do... + */ +static inline int acm_set_control(struct acm *acm, int control) +{ + if (acm->quirks & QUIRK_CONTROL_LINE_STATE) + return -EOPNOTSUPP; + + return acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, + control, NULL, 0); +} + +#define acm_set_line(acm, line) \ + acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) +#define acm_send_break(acm, ms) \ + acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) /* * Write buffer management. * All of these assume proper locks taken by the caller. */ -static int xr21v141x_wb_alloc(struct xr21v141x *xr21v141x) +static int acm_wb_alloc(struct acm *acm) { int i, wbn; - struct xr21v141x_wb *wb; + struct acm_wb *wb; wbn = 0; i = 0; for (;;) { - wb = &xr21v141x->wb[wbn]; + wb = &acm->wb[wbn]; if (!wb->use) { wb->use = 1; return wbn; @@ -107,27 +187,27 @@ static int xr21v141x_wb_alloc(struct xr21v141x *xr21v141x) } } -static int xr21v141x_wb_is_avail(struct xr21v141x *xr21v141x) +static int acm_wb_is_avail(struct acm *acm) { int i, n; unsigned long flags; n = ACM_NW; - spin_lock_irqsave(&xr21v141x->write_lock, flags); + spin_lock_irqsave(&acm->write_lock, flags); for (i = 0; i < ACM_NW; i++) - n -= xr21v141x->wb[i].use; - spin_unlock_irqrestore(&xr21v141x->write_lock, flags); + n -= acm->wb[i].use; + spin_unlock_irqrestore(&acm->write_lock, flags); return n; } /* - * Finish write. Caller must hold xr21v141x->write_lock + * Finish write. Caller must hold acm->write_lock */ -static void xr21v141x_write_done(struct xr21v141x *xr21v141x, struct xr21v141x_wb *wb) +static void acm_write_done(struct acm *acm, struct acm_wb *wb) { wb->use = 0; - xr21v141x->transmitting--; - usb_autopm_put_interface_async(xr21v141x->control); + acm->transmitting--; + usb_autopm_put_interface_async(acm->control); } /* @@ -136,23 +216,23 @@ static void xr21v141x_write_done(struct xr21v141x *xr21v141x, struct xr21v141x_w * the caller is responsible for locking */ -static int xr21v141x_start_wb(struct xr21v141x *xr21v141x, struct xr21v141x_wb *wb) +static int acm_start_wb(struct acm *acm, struct acm_wb *wb) { int rc; - xr21v141x->transmitting++; + acm->transmitting++; wb->urb->transfer_buffer = wb->buf; wb->urb->transfer_dma = wb->dmah; wb->urb->transfer_buffer_length = wb->len; - wb->urb->dev = xr21v141x->dev; + wb->urb->dev = acm->dev; rc = usb_submit_urb(wb->urb, GFP_ATOMIC); if (rc < 0) { - dev_err(&xr21v141x->data->dev, + dev_err(&acm->data->dev, "%s - usb_submit_urb(write bulk) failed: %d\n", __func__, rc); - xr21v141x_write_done(xr21v141x, wb); + acm_write_done(acm, wb); } return rc; } @@ -164,9 +244,9 @@ static ssize_t show_caps (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); - struct xr21v141x *xr21v141x = usb_get_intfdata(intf); + struct acm *acm = usb_get_intfdata(intf); - return sprintf(buf, "%d", xr21v141x->ctrl_caps); + return sprintf(buf, "%d", acm->ctrl_caps); } static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); @@ -174,10 +254,10 @@ static ssize_t show_country_codes (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); - struct xr21v141x *xr21v141x = usb_get_intfdata(intf); + struct acm *acm = usb_get_intfdata(intf); - memcpy(buf, xr21v141x->country_codes, xr21v141x->country_code_size); - return xr21v141x->country_code_size; + memcpy(buf, acm->country_codes, acm->country_code_size); + return acm->country_code_size; } static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); @@ -186,9 +266,9 @@ static ssize_t show_country_rel_date (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); - struct xr21v141x *xr21v141x = usb_get_intfdata(intf); + struct acm *acm = usb_get_intfdata(intf); - return sprintf(buf, "%d", xr21v141x->country_rel_date); + return sprintf(buf, "%d", acm->country_rel_date); } static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); @@ -197,12 +277,13 @@ static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); */ /* control interface reports status changes with "interrupt" transfers */ -static void xr21v141x_ctrl_irq(struct urb *urb) +static void acm_ctrl_irq(struct urb *urb) { - struct xr21v141x *xr21v141x = urb->context; + struct acm *acm = urb->context; struct usb_cdc_notification *dr = urb->transfer_buffer; unsigned char *data; int newctrl; + int difference; int retval; int status = urb->status; @@ -214,52 +295,63 @@ static void xr21v141x_ctrl_irq(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dev_dbg(&xr21v141x->control->dev, + dev_dbg(&acm->control->dev, "%s - urb shutting down with status: %d\n", __func__, status); return; default: - dev_dbg(&xr21v141x->control->dev, + dev_dbg(&acm->control->dev, "%s - nonzero urb status received: %d\n", __func__, status); goto exit; } - usb_mark_last_busy(xr21v141x->dev); + usb_mark_last_busy(acm->dev); data = (unsigned char *)(dr + 1); switch (dr->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION: - dev_dbg(&xr21v141x->control->dev, "%s - network connection: %d\n", + dev_dbg(&acm->control->dev, "%s - network connection: %d\n", __func__, dr->wValue); break; case USB_CDC_NOTIFY_SERIAL_STATE: newctrl = get_unaligned_le16(data); - if (!xr21v141x->clocal && (xr21v141x->ctrlin & ~newctrl & ACM_CTRL_DCD)) { - dev_dbg(&xr21v141x->control->dev, - "%s - calling hangup\n", __func__); - tty_port_tty_hangup(&xr21v141x->port, false); + if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + dev_dbg(&acm->control->dev, "%s - calling hangup\n", + __func__); + tty_port_tty_hangup(&acm->port, false); } - xr21v141x->ctrlin = newctrl; + difference = acm->ctrlin ^ newctrl; + spin_lock(&acm->read_lock); + acm->ctrlin = newctrl; + acm->oldcount = acm->iocount; + + if (difference & ACM_CTRL_DSR) + acm->iocount.dsr++; + if (difference & ACM_CTRL_BRK) + acm->iocount.brk++; + if (difference & ACM_CTRL_RI) + acm->iocount.rng++; + if (difference & ACM_CTRL_DCD) + acm->iocount.dcd++; + if (difference & ACM_CTRL_FRAMING) + acm->iocount.frame++; + if (difference & ACM_CTRL_PARITY) + acm->iocount.parity++; + if (difference & ACM_CTRL_OVERRUN) + acm->iocount.overrun++; + spin_unlock(&acm->read_lock); + + if (difference) + wake_up_all(&acm->wioctl); - dev_dbg(&xr21v141x->control->dev, - "%s - input control lines: dcd%c dsr%c break%c " - "ring%c framing%c parity%c overrun%c\n", - __func__, - xr21v141x->ctrlin & ACM_CTRL_DCD ? '+' : '-', - xr21v141x->ctrlin & ACM_CTRL_DSR ? '+' : '-', - xr21v141x->ctrlin & ACM_CTRL_BRK ? '+' : '-', - xr21v141x->ctrlin & ACM_CTRL_RI ? '+' : '-', - xr21v141x->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', - xr21v141x->ctrlin & ACM_CTRL_PARITY ? '+' : '-', - xr21v141x->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); - break; + break; default: - dev_dbg(&xr21v141x->control->dev, + dev_dbg(&acm->control->dev, "%s - unknown notification %d received: index %d " "len %d data0 %d data1 %d\n", __func__, @@ -269,41 +361,41 @@ static void xr21v141x_ctrl_irq(struct urb *urb) } exit: retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(&xr21v141x->control->dev, "%s - usb_submit_urb failed: %d\n", + if (retval && retval != -EPERM) + dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n", __func__, retval); } -static int xr21v141x_submit_read_urb(struct xr21v141x *xr21v141x, int index, gfp_t mem_flags) +static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) { int res; - if (!test_and_clear_bit(index, &xr21v141x->read_urbs_free)) + if (!test_and_clear_bit(index, &acm->read_urbs_free)) return 0; - dev_vdbg(&xr21v141x->data->dev, "%s - urb %d\n", __func__, index); + dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index); - res = usb_submit_urb(xr21v141x->read_urbs[index], mem_flags); + res = usb_submit_urb(acm->read_urbs[index], mem_flags); if (res) { if (res != -EPERM) { - dev_err(&xr21v141x->data->dev, + dev_err(&acm->data->dev, "%s - usb_submit_urb failed: %d\n", __func__, res); } - set_bit(index, &xr21v141x->read_urbs_free); + set_bit(index, &acm->read_urbs_free); return res; } return 0; } -static int xr21v141x_submit_read_urbs(struct xr21v141x *xr21v141x, gfp_t mem_flags) +static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags) { int res; int i; - for (i = 0; i < xr21v141x->rx_buflimit; ++i) { - res = xr21v141x_submit_read_urb(xr21v141x, i, mem_flags); + for (i = 0; i < acm->rx_buflimit; ++i) { + res = acm_submit_read_urb(acm, i, mem_flags); if (res) return res; } @@ -311,406 +403,413 @@ static int xr21v141x_submit_read_urbs(struct xr21v141x *xr21v141x, gfp_t mem_fla return 0; } -static void xr21v141x_process_read_urb(struct xr21v141x *xr21v141x, struct urb *urb) +static void acm_process_read_urb(struct acm *acm, struct urb *urb) { if (!urb->actual_length) return; - tty_insert_flip_string(&xr21v141x->port, urb->transfer_buffer, urb->actual_length); - tty_flip_buffer_push(&xr21v141x->port); + tty_insert_flip_string(&acm->port, urb->transfer_buffer, + urb->actual_length); + tty_flip_buffer_push(&acm->port); } -static void xr21v141x_read_bulk_callback(struct urb *urb) +static void acm_read_bulk_callback(struct urb *urb) { - struct xr21v141x_rb *rb = urb->context; - struct xr21v141x *xr21v141x = rb->instance; + struct acm_rb *rb = urb->context; + struct acm *acm = rb->instance; unsigned long flags; + int status = urb->status; - dev_vdbg(&xr21v141x->data->dev, "%s - urb %d, len %d\n", __func__, + dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, rb->index, urb->actual_length); - set_bit(rb->index, &xr21v141x->read_urbs_free); - if (!xr21v141x->dev) { - dev_dbg(&xr21v141x->data->dev, "%s - disconnected\n", __func__); + if (!acm->dev) { + set_bit(rb->index, &acm->read_urbs_free); + dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); return; } - usb_mark_last_busy(xr21v141x->dev); - if (urb->status) { - dev_dbg(&xr21v141x->data->dev, "%s - non-zero urb status: %d\n", - __func__, urb->status); + if (status) { + set_bit(rb->index, &acm->read_urbs_free); + dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", + __func__, status); return; } - xr21v141x_process_read_urb(xr21v141x, urb); + + usb_mark_last_busy(acm->dev); + + acm_process_read_urb(acm, urb); + /* + * Unthrottle may run on another CPU which needs to see events + * in the same order. Submission has an implict barrier + */ + smp_mb__before_atomic(); + set_bit(rb->index, &acm->read_urbs_free); /* throttle device if requested by tty */ - spin_lock_irqsave(&xr21v141x->read_lock, flags); - xr21v141x->throttled = xr21v141x->throttle_req; - if (!xr21v141x->throttled && !xr21v141x->susp_count) { - spin_unlock_irqrestore(&xr21v141x->read_lock, flags); - xr21v141x_submit_read_urb(xr21v141x, rb->index, GFP_ATOMIC); + spin_lock_irqsave(&acm->read_lock, flags); + acm->throttled = acm->throttle_req; + if (!acm->throttled) { + spin_unlock_irqrestore(&acm->read_lock, flags); + acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); } else { - spin_unlock_irqrestore(&xr21v141x->read_lock, flags); + spin_unlock_irqrestore(&acm->read_lock, flags); } } /* data interface wrote those outgoing bytes */ -static void xr21v141x_write_bulk(struct urb *urb) +static void acm_write_bulk(struct urb *urb) { - struct xr21v141x_wb *wb = urb->context; - struct xr21v141x *xr21v141x = wb->instance; + struct acm_wb *wb = urb->context; + struct acm *acm = wb->instance; unsigned long flags; + int status = urb->status; - if (urb->status || (urb->actual_length != urb->transfer_buffer_length)) - dev_vdbg(&xr21v141x->data->dev, "%s - len %d/%d, status %d\n", + if (status || (urb->actual_length != urb->transfer_buffer_length)) + dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n", __func__, urb->actual_length, urb->transfer_buffer_length, - urb->status); + status); - spin_lock_irqsave(&xr21v141x->write_lock, flags); - xr21v141x_write_done(xr21v141x, wb); - spin_unlock_irqrestore(&xr21v141x->write_lock, flags); - schedule_work(&xr21v141x->work); + spin_lock_irqsave(&acm->write_lock, flags); + acm_write_done(acm, wb); + spin_unlock_irqrestore(&acm->write_lock, flags); + schedule_work(&acm->work); } -static void xr21v141x_softint(struct work_struct *work) +static void acm_softint(struct work_struct *work) { - struct xr21v141x *xr21v141x = container_of(work, struct xr21v141x, work); + struct acm *acm = container_of(work, struct acm, work); - dev_vdbg(&xr21v141x->data->dev, "%s\n", __func__); + dev_vdbg(&acm->data->dev, "%s\n", __func__); - tty_port_tty_wakeup(&xr21v141x->port); + tty_port_tty_wakeup(&acm->port); } /* * TTY handlers */ -static struct xr21v141x *xr21v141x_get_by_index(unsigned index) -{ - struct xr21v141x *xr21v141x; - - mutex_lock(&xr21v141x_table_lock); - xr21v141x = xr21v141x_table[index]; - if (xr21v141x) { - mutex_lock(&xr21v141x->mutex); - if (xr21v141x->disconnected) { - mutex_unlock(&xr21v141x->mutex); - xr21v141x = NULL; - } else { - tty_port_get(&xr21v141x->port); - mutex_unlock(&xr21v141x->mutex); - } - } - mutex_unlock(&xr21v141x_table_lock); - return xr21v141x; -} - -/* - * Try to find an available minor number and if found, associate it with 'xr21v141x'. - */ -static int xr21v141x_alloc_minor(struct xr21v141x *xr21v141x) +static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) { - int minor; - - mutex_lock(&xr21v141x_table_lock); - for (minor = 0; minor < XR21V141X_TTY_MINORS; minor++) { - if (!xr21v141x_table[minor]) { - xr21v141x_table[minor] = xr21v141x; - break; - } - } - mutex_unlock(&xr21v141x_table_lock); - - return minor; -} - -/* Release the minor number associated with 'xr21v141x'. */ -static void xr21v141x_release_minor(struct xr21v141x *xr21v141x) -{ - mutex_lock(&xr21v141x_table_lock); - xr21v141x_table[xr21v141x->minor] = NULL; - mutex_unlock(&xr21v141x_table_lock); -} - -static int xr21v141x_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct xr21v141x *xr21v141x; + struct acm *acm; int retval; dev_dbg(tty->dev, "%s\n", __func__); - xr21v141x = xr21v141x_get_by_index(tty->index); - if (!xr21v141x) + acm = acm_get_by_minor(tty->index); + if (!acm) return -ENODEV; retval = tty_standard_install(driver, tty); if (retval) goto error_init_termios; - tty->driver_data = xr21v141x; + tty->driver_data = acm; return 0; error_init_termios: - tty_port_put(&xr21v141x->port); + tty_port_put(&acm->port); return retval; } -static int xr21v141x_tty_open(struct tty_struct *tty, struct file *filp) +static int acm_tty_open(struct tty_struct *tty, struct file *filp) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; dev_dbg(tty->dev, "%s\n", __func__); - return tty_port_open(&xr21v141x->port, tty, filp); + return tty_port_open(&acm->port, tty, filp); } -static int xr21v141x_port_activate(struct tty_port *port, struct tty_struct *tty) +static void acm_port_dtr_rts(struct tty_port *port, int raise) { - struct xr21v141x *xr21v141x = container_of(port, struct xr21v141x, port); + struct acm *acm = container_of(port, struct acm, port); + int val; + int res; + + if (raise) + val = ACM_CTRL_DTR | ACM_CTRL_RTS; + else + val = 0; + + /* FIXME: add missing ctrlout locking throughout driver */ + acm->ctrlout = val; + + res = acm_set_control(acm, val); + if (res && (acm->ctrl_caps & USB_CDC_CAP_LINE)) + dev_err(&acm->control->dev, "failed to set dtr/rts\n"); +} + +static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct acm *acm = container_of(port, struct acm, port); int retval = -ENODEV; + int i; - dev_dbg(&xr21v141x->control->dev, "%s\n", __func__); + dev_dbg(&acm->control->dev, "%s\n", __func__); - mutex_lock(&xr21v141x->mutex); - if (xr21v141x->disconnected) + mutex_lock(&acm->mutex); + if (acm->disconnected) goto disconnected; - retval = usb_autopm_get_interface(xr21v141x->control); + retval = usb_autopm_get_interface(acm->control); if (retval) goto error_get_interface; + /* + * FIXME: Why do we need this? Allocating 64K of physically contiguous + * memory is really nasty... + */ set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); - xr21v141x->control->needs_remote_wakeup = 1; + acm->control->needs_remote_wakeup = 1; - xr21v141x->ctrlurb->dev = xr21v141x->dev; - if (usb_submit_urb(xr21v141x->ctrlurb, GFP_KERNEL)) { - dev_err(&xr21v141x->control->dev, + acm->ctrlurb->dev = acm->dev; + retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL); + if (retval) { + dev_err(&acm->control->dev, "%s - usb_submit_urb(ctrl irq) failed\n", __func__); - usb_autopm_put_interface(xr21v141x->control); goto error_submit_urb; } - xr21v141x->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS; - if (xr21v141x_set_control(xr21v141x, xr21v141x->ctrlout) < 0 && - (xr21v141x->ctrl_caps & USB_CDC_CAP_LINE)) { - usb_autopm_put_interface(xr21v141x->control); - goto error_set_control; - } - - usb_autopm_put_interface(xr21v141x->control); + acm_tty_set_termios(tty, NULL); /* * Unthrottle device in case the TTY was closed while throttled. */ - spin_lock_irq(&xr21v141x->read_lock); - xr21v141x->throttled = 0; - xr21v141x->throttle_req = 0; - spin_unlock_irq(&xr21v141x->read_lock); + spin_lock_irq(&acm->read_lock); + acm->throttled = 0; + acm->throttle_req = 0; + spin_unlock_irq(&acm->read_lock); - if (xr21v141x_submit_read_urbs(xr21v141x, GFP_KERNEL)) + retval = acm_submit_read_urbs(acm, GFP_KERNEL); + if (retval) goto error_submit_read_urbs; - mutex_unlock(&xr21v141x->mutex); + usb_autopm_put_interface(acm->control); + + mutex_unlock(&acm->mutex); return 0; error_submit_read_urbs: - xr21v141x->ctrlout = 0; - xr21v141x_set_control(xr21v141x, xr21v141x->ctrlout); -error_set_control: - usb_kill_urb(xr21v141x->ctrlurb); + for (i = 0; i < acm->rx_buflimit; i++) + usb_kill_urb(acm->read_urbs[i]); + usb_kill_urb(acm->ctrlurb); error_submit_urb: + usb_autopm_put_interface(acm->control); error_get_interface: disconnected: - mutex_unlock(&xr21v141x->mutex); - return retval; + mutex_unlock(&acm->mutex); + + return usb_translate_errors(retval); } -static void xr21v141x_port_destruct(struct tty_port *port) +static void acm_port_destruct(struct tty_port *port) { - struct xr21v141x *xr21v141x = container_of(port, struct xr21v141x, port); + struct acm *acm = container_of(port, struct acm, port); - dev_dbg(&xr21v141x->control->dev, "%s\n", __func__); + dev_dbg(&acm->control->dev, "%s\n", __func__); - xr21v141x_release_minor(xr21v141x); - usb_put_intf(xr21v141x->control); - kfree(xr21v141x->country_codes); - kfree(xr21v141x); + acm_release_minor(acm); + usb_put_intf(acm->control); + kfree(acm->country_codes); + kfree(acm); } -static void xr21v141x_port_shutdown(struct tty_port *port) +static void acm_port_shutdown(struct tty_port *port) { - struct xr21v141x *xr21v141x = container_of(port, struct xr21v141x, port); + struct acm *acm = container_of(port, struct acm, port); + struct urb *urb; + struct acm_wb *wb; int i; - dev_dbg(&xr21v141x->control->dev, "%s\n", __func__); - - mutex_lock(&xr21v141x->mutex); - if (!xr21v141x->disconnected) { - usb_autopm_get_interface(xr21v141x->control); - xr21v141x_set_control(xr21v141x, xr21v141x->ctrlout = 0); - usb_kill_urb(xr21v141x->ctrlurb); - for (i = 0; i < ACM_NW; i++) - usb_kill_urb(xr21v141x->wb[i].urb); - for (i = 0; i < xr21v141x->rx_buflimit; i++) - usb_kill_urb(xr21v141x->read_urbs[i]); - xr21v141x->control->needs_remote_wakeup = 0; - usb_autopm_put_interface(xr21v141x->control); + dev_dbg(&acm->control->dev, "%s\n", __func__); + + /* + * Need to grab write_lock to prevent race with resume, but no need to + * hold it due to the tty-port initialised flag. + */ + spin_lock_irq(&acm->write_lock); + spin_unlock_irq(&acm->write_lock); + + usb_autopm_get_interface_no_resume(acm->control); + acm->control->needs_remote_wakeup = 0; + usb_autopm_put_interface(acm->control); + + for (;;) { + urb = usb_get_from_anchor(&acm->delayed); + if (!urb) + break; + wb = urb->context; + wb->use = 0; + usb_autopm_put_interface_async(acm->control); } - mutex_unlock(&xr21v141x->mutex); + + usb_kill_urb(acm->ctrlurb); + for (i = 0; i < ACM_NW; i++) + usb_kill_urb(acm->wb[i].urb); + for (i = 0; i < acm->rx_buflimit; i++) + usb_kill_urb(acm->read_urbs[i]); } -static void xr21v141x_tty_cleanup(struct tty_struct *tty) +static void acm_tty_cleanup(struct tty_struct *tty) { - struct xr21v141x *xr21v141x = tty->driver_data; - dev_dbg(&xr21v141x->control->dev, "%s\n", __func__); - tty_port_put(&xr21v141x->port); + struct acm *acm = tty->driver_data; + dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_put(&acm->port); } -static void xr21v141x_tty_hangup(struct tty_struct *tty) +static void acm_tty_hangup(struct tty_struct *tty) { - struct xr21v141x *xr21v141x = tty->driver_data; - dev_dbg(&xr21v141x->control->dev, "%s\n", __func__); - tty_port_hangup(&xr21v141x->port); + struct acm *acm = tty->driver_data; + dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_hangup(&acm->port); } -static void xr21v141x_tty_close(struct tty_struct *tty, struct file *filp) +static void acm_tty_close(struct tty_struct *tty, struct file *filp) { - struct xr21v141x *xr21v141x = tty->driver_data; - dev_dbg(&xr21v141x->control->dev, "%s\n", __func__); - tty_port_close(&xr21v141x->port, tty, filp); + struct acm *acm = tty->driver_data; + dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_close(&acm->port, tty, filp); } -static int xr21v141x_tty_write(struct tty_struct *tty, +static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; int stat; unsigned long flags; int wbn; - struct xr21v141x_wb *wb; + struct acm_wb *wb; if (!count) return 0; - dev_vdbg(&xr21v141x->data->dev, "%s - count %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count); - spin_lock_irqsave(&xr21v141x->write_lock, flags); - wbn = xr21v141x_wb_alloc(xr21v141x); + spin_lock_irqsave(&acm->write_lock, flags); + wbn = acm_wb_alloc(acm); if (wbn < 0) { - spin_unlock_irqrestore(&xr21v141x->write_lock, flags); + spin_unlock_irqrestore(&acm->write_lock, flags); return 0; } - wb = &xr21v141x->wb[wbn]; + wb = &acm->wb[wbn]; - if (!xr21v141x->dev) { + if (!acm->dev) { wb->use = 0; - spin_unlock_irqrestore(&xr21v141x->write_lock, flags); + spin_unlock_irqrestore(&acm->write_lock, flags); return -ENODEV; } - count = (count > xr21v141x->writesize) ? xr21v141x->writesize : count; - dev_vdbg(&xr21v141x->data->dev, "%s - write %d\n", __func__, count); + count = (count > acm->writesize) ? acm->writesize : count; + dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count); memcpy(wb->buf, buf, count); wb->len = count; - usb_autopm_get_interface_async(xr21v141x->control); - if (xr21v141x->susp_count) { - if (!xr21v141x->delayed_wb) - xr21v141x->delayed_wb = wb; - else - usb_autopm_put_interface_async(xr21v141x->control); - spin_unlock_irqrestore(&xr21v141x->write_lock, flags); - return count; /* A white lie */ + stat = usb_autopm_get_interface_async(acm->control); + if (stat) { + wb->use = 0; + spin_unlock_irqrestore(&acm->write_lock, flags); + return stat; + } + + if (acm->susp_count) { + usb_anchor_urb(wb->urb, &acm->delayed); + spin_unlock_irqrestore(&acm->write_lock, flags); + return count; } - usb_mark_last_busy(xr21v141x->dev); - stat = xr21v141x_start_wb(xr21v141x, wb); - spin_unlock_irqrestore(&xr21v141x->write_lock, flags); + stat = acm_start_wb(acm, wb); + spin_unlock_irqrestore(&acm->write_lock, flags); if (stat < 0) return stat; return count; } -static int xr21v141x_tty_write_room(struct tty_struct *tty) +static int acm_tty_write_room(struct tty_struct *tty) { - struct xr21v141x *xr21v141x = tty->driver_data; - - return xr21v141x_wb_is_avail(xr21v141x) ? xr21v141x->writesize : 0; + struct acm *acm = tty->driver_data; + /* + * Do not let the line discipline to know that we have a reserve, + * or it might get too enthusiastic. + */ + return acm_wb_is_avail(acm) ? acm->writesize : 0; } -static int xr21v141x_tty_chars_in_buffer(struct tty_struct *tty) +static int acm_tty_chars_in_buffer(struct tty_struct *tty) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; /* * if the device was unplugged then any remaining characters fell out * of the connector ;) */ - if (xr21v141x->disconnected) + if (acm->disconnected) return 0; - - return (ACM_NW - xr21v141x_wb_is_avail(xr21v141x)) * xr21v141x->writesize; + /* + * This is inaccurate (overcounts), but it works. + */ + return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize; } -static void xr21v141x_tty_throttle(struct tty_struct *tty) +static void acm_tty_throttle(struct tty_struct *tty) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; - spin_lock_irq(&xr21v141x->read_lock); - xr21v141x->throttle_req = 1; - spin_unlock_irq(&xr21v141x->read_lock); + spin_lock_irq(&acm->read_lock); + acm->throttle_req = 1; + spin_unlock_irq(&acm->read_lock); } -static void xr21v141x_tty_unthrottle(struct tty_struct *tty) +static void acm_tty_unthrottle(struct tty_struct *tty) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; unsigned int was_throttled; - spin_lock_irq(&xr21v141x->read_lock); - was_throttled = xr21v141x->throttled; - xr21v141x->throttled = 0; - xr21v141x->throttle_req = 0; - spin_unlock_irq(&xr21v141x->read_lock); + spin_lock_irq(&acm->read_lock); + was_throttled = acm->throttled; + acm->throttled = 0; + acm->throttle_req = 0; + spin_unlock_irq(&acm->read_lock); if (was_throttled) - xr21v141x_submit_read_urbs(xr21v141x, GFP_KERNEL); + acm_submit_read_urbs(acm, GFP_KERNEL); } -static int xr21v141x_tty_break_ctl(struct tty_struct *tty, int state) +static int acm_tty_break_ctl(struct tty_struct *tty, int state) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; int retval; - retval = xr21v141x_send_break(xr21v141x, state ? 0xffff : 0); + retval = acm_send_break(acm, state ? 0xffff : 0); if (retval < 0) - dev_dbg(&xr21v141x->control->dev, "%s - send break failed\n", + dev_dbg(&acm->control->dev, "%s - send break failed\n", __func__); return retval; } -static int xr21v141x_tty_tiocmget(struct tty_struct *tty) +static int acm_tty_tiocmget(struct tty_struct *tty) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; - return (xr21v141x->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | - (xr21v141x->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | - (xr21v141x->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | - (xr21v141x->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | - (xr21v141x->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | + return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | + (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | + (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | + (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | + (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | TIOCM_CTS; } -static int xr21v141x_tty_tiocmset(struct tty_struct *tty, +static int acm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; unsigned int newctrl; - newctrl = xr21v141x->ctrlout; + newctrl = acm->ctrlout; set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | @@ -718,12 +817,12 @@ static int xr21v141x_tty_tiocmset(struct tty_struct *tty, newctrl = (newctrl & ~clear) | set; - if (xr21v141x->ctrlout == newctrl) + if (acm->ctrlout == newctrl) return 0; - return xr21v141x_set_control(xr21v141x, xr21v141x->ctrlout = newctrl); + return acm_set_control(acm, acm->ctrlout = newctrl); } -static int get_serial_info(struct xr21v141x *xr21v141x, struct serial_struct __user *info) +static int get_serial_info(struct acm *acm, struct serial_struct __user *info) { struct serial_struct tmp; @@ -732,12 +831,12 @@ static int get_serial_info(struct xr21v141x *xr21v141x, struct serial_struct __u memset(&tmp, 0, sizeof(tmp)); tmp.flags = ASYNC_LOW_LATENCY; - tmp.xmit_fifo_size = xr21v141x->writesize; - tmp.baud_base = le32_to_cpu(xr21v141x->line.dwDTERate); - tmp.close_delay = xr21v141x->port.close_delay / 10; - tmp.closing_wait = xr21v141x->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + tmp.xmit_fifo_size = acm->writesize; + tmp.baud_base = le32_to_cpu(acm->line.dwDTERate); + tmp.close_delay = acm->port.close_delay / 10; + tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : - xr21v141x->port.closing_wait / 10; + acm->port.closing_wait / 10; if (copy_to_user(info, &tmp, sizeof(tmp))) return -EFAULT; @@ -745,7 +844,7 @@ static int get_serial_info(struct xr21v141x *xr21v141x, struct serial_struct __u return 0; } -static int set_serial_info(struct xr21v141x *xr21v141x, +static int set_serial_info(struct acm *acm, struct serial_struct __user *newinfo) { struct serial_struct new_serial; @@ -759,34 +858,98 @@ static int set_serial_info(struct xr21v141x *xr21v141x, closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; - mutex_lock(&xr21v141x->port.mutex); + mutex_lock(&acm->port.mutex); if (!capable(CAP_SYS_ADMIN)) { - if ((close_delay != xr21v141x->port.close_delay) || - (closing_wait != xr21v141x->port.closing_wait)) + if ((close_delay != acm->port.close_delay) || + (closing_wait != acm->port.closing_wait)) retval = -EPERM; else retval = -EOPNOTSUPP; } else { - xr21v141x->port.close_delay = close_delay; - xr21v141x->port.closing_wait = closing_wait; + acm->port.close_delay = close_delay; + acm->port.closing_wait = closing_wait; } - mutex_unlock(&xr21v141x->port.mutex); + mutex_unlock(&acm->port.mutex); return retval; } +static int wait_serial_change(struct acm *acm, unsigned long arg) +{ + int rv = 0; + DECLARE_WAITQUEUE(wait, current); + struct async_icount old, new; + if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD )) + return -EINVAL; + do { + spin_lock_irq(&acm->read_lock); + old = acm->oldcount; + new = acm->iocount; + acm->oldcount = new; + spin_unlock_irq(&acm->read_lock); + + if ((arg & TIOCM_DSR) && + old.dsr != new.dsr) + break; + if ((arg & TIOCM_CD) && + old.dcd != new.dcd) + break; + if ((arg & TIOCM_RI) && + old.rng != new.rng) + break; + + add_wait_queue(&acm->wioctl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + remove_wait_queue(&acm->wioctl, &wait); + if (acm->disconnected) { + if (arg & TIOCM_CD) + break; + else + rv = -ENODEV; + } else { + if (signal_pending(current)) + rv = -ERESTARTSYS; + } + } while (!rv); + + + + return rv; +} + +static int get_serial_usage(struct acm *acm, + struct serial_icounter_struct __user *count) +{ + struct serial_icounter_struct icount; + int rv = 0; + + memset(&icount, 0, sizeof(icount)); + icount.dsr = acm->iocount.dsr; + icount.rng = acm->iocount.rng; + icount.dcd = acm->iocount.dcd; + icount.frame = acm->iocount.frame; + icount.overrun = acm->iocount.overrun; + icount.parity = acm->iocount.parity; + icount.brk = acm->iocount.brk; -static int vizzini_set_reg(struct xr21v141x *xr21v141x, + if (copy_to_user(count, &icount, sizeof(icount)) > 0) + rv = -EFAULT; + + return rv; +} + +static int vizzini_set_reg(struct acm *acm, int block, int regnum, int value) { int result; - dev_dbg(&xr21v141x->control->dev, "%s 0x%02x:0x%02x = 0x%02x\n", __func__, block, regnum, value); + dev_dbg(&acm->control->dev, "%s 0x%02x:0x%02x = 0x%02x\n", __func__, block, regnum, value); - result = usb_control_msg(xr21v141x->dev, /* usb device */ - usb_sndctrlpipe(xr21v141x->dev, 0), /* endpoint pipe */ + result = usb_control_msg(acm->dev, /* usb device */ + usb_sndctrlpipe(acm->dev, 0), /* endpoint pipe */ XR_SET_REG, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR, /* request_type */ value, /* request value */ @@ -797,14 +960,13 @@ static int vizzini_set_reg(struct xr21v141x *xr21v141x, return result; } - -static int vizzini_get_reg(struct xr21v141x *xr21v141x, +static int vizzini_get_reg(struct acm *acm, int block, int reg, char *value) { int result; - result = usb_control_msg(xr21v141x->dev, /* usb device */ - usb_rcvctrlpipe(xr21v141x->dev, 0), /* endpoint pipe */ + result = usb_control_msg(acm->dev, /* usb device */ + usb_rcvctrlpipe(acm->dev, 0), /* endpoint pipe */ XR_GETN_REG, /* request */ USB_DIR_IN | USB_TYPE_VENDOR, /* request_type */ 0, /* request value */ @@ -816,29 +978,26 @@ static int vizzini_get_reg(struct xr21v141x *xr21v141x, return result; } - -static void vizzini_disable(struct xr21v141x *xr21v141x) +static void vizzini_disable(struct acm *acm) { - int block = xr21v141x->block; + int block = acm->block; - vizzini_set_reg(xr21v141x, block, UART_ENABLE, 0); - vizzini_set_reg(xr21v141x, URM_REG_BLOCK, URM_ENABLE_BASE + block, 0); + vizzini_set_reg(acm, block, UART_ENABLE, 0); + vizzini_set_reg(acm, URM_REG_BLOCK, URM_ENABLE_BASE + block, 0); } - -static void vizzini_enable(struct xr21v141x *xr21v141x) +static void vizzini_enable(struct acm *acm) { - int block = xr21v141x->block; + int block = acm->block; - vizzini_set_reg(xr21v141x, URM_REG_BLOCK, URM_ENABLE_BASE + block, URM_ENABLE_0_TX); - vizzini_set_reg(xr21v141x, block, UART_ENABLE, UART_ENABLE_TX | UART_ENABLE_RX); - vizzini_set_reg(xr21v141x, URM_REG_BLOCK, URM_ENABLE_BASE + block, URM_ENABLE_0_TX | URM_ENABLE_0_RX); + vizzini_set_reg(acm, URM_REG_BLOCK, URM_ENABLE_BASE + block, URM_ENABLE_0_TX); + vizzini_set_reg(acm, block, UART_ENABLE, UART_ENABLE_TX | UART_ENABLE_RX); + vizzini_set_reg(acm, URM_REG_BLOCK, URM_ENABLE_BASE + block, URM_ENABLE_0_TX | URM_ENABLE_0_RX); } - -static void vizzini_loopback(struct xr21v141x *xr21v141x, int from) +static void vizzini_loopback(struct acm *acm, int from) { - int block = xr21v141x->block; + int block = acm->block; int lb; switch (from) @@ -850,30 +1009,30 @@ static void vizzini_loopback(struct xr21v141x *xr21v141x, int from) default: return; } - dev_info(&xr21v141x->control->dev, "Internal loopback from %d\n", from); + dev_info(&acm->control->dev, "Internal loopback from %d\n", from); - vizzini_disable(xr21v141x); - vizzini_set_reg(xr21v141x, block, UART_LOOPBACK_CTL, UART_LOOPBACK_CTL_ENABLE | lb); - vizzini_enable(xr21v141x); + vizzini_disable(acm); + vizzini_set_reg(acm, block, UART_LOOPBACK_CTL, UART_LOOPBACK_CTL_ENABLE | lb); + vizzini_enable(acm); } -static int vizzini_test_mode(struct xr21v141x *xr21v141x, +static int vizzini_test_mode(struct acm *acm, int selector) { - int retval = usb_control_msg(xr21v141x->dev, usb_sndctrlpipe(xr21v141x->dev, 0), + int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), USB_REQ_SET_FEATURE, USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_DEVICE_TEST_MODE, selector << 8, NULL, 0, 5000); - dev_dbg(&xr21v141x->control->dev, "vz_test_mode: selector=0x%02x\n", selector); + dev_dbg(&acm->control->dev, "vz_test_mode: selector=0x%02x\n", selector); return retval < 0 ? retval : 0; } -static int xr21v141x_tty_ioctl(struct tty_struct *tty, +static int acm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; int rv = -ENOIOCTLCMD; unsigned int block, reg, val, match, preciseflags, unicast, broadcast, flow, selector; @@ -881,10 +1040,22 @@ static int xr21v141x_tty_ioctl(struct tty_struct *tty, switch (cmd) { case TIOCGSERIAL: /* gets serial port data */ - rv = get_serial_info(xr21v141x, (struct serial_struct __user *) arg); + rv = get_serial_info(acm, (struct serial_struct __user *) arg); break; case TIOCSSERIAL: - rv = set_serial_info(xr21v141x, (struct serial_struct __user *) arg); + rv = set_serial_info(acm, (struct serial_struct __user *) arg); + break; + case TIOCMIWAIT: + rv = usb_autopm_get_interface(acm->control); + if (rv < 0) { + rv = -EIO; + break; + } + rv = wait_serial_change(acm, arg); + usb_autopm_put_interface(acm->control); + break; + case TIOCGICOUNT: + rv = get_serial_usage(acm, (struct serial_icounter_struct __user *) arg); break; case VZIOC_GET_REG: @@ -895,22 +1066,22 @@ static int xr21v141x_tty_ioctl(struct tty_struct *tty, data = kmalloc(1, GFP_KERNEL); if (data == NULL) { - dev_err(&xr21v141x->control->dev, "%s - Cannot allocate USB buffer.\n", __func__); + dev_err(&acm->control->dev, "%s - Cannot allocate USB buffer.\n", __func__); return -ENOMEM; } if (block == -1) - block = xr21v141x->block; + block = acm->block; - rv = vizzini_get_reg(xr21v141x, block, reg, data); + rv = vizzini_get_reg(acm, block, reg, data); if (rv != 1) { - dev_err(&xr21v141x->control->dev, "Cannot get register (%d)\n", rv); + dev_err(&acm->control->dev, "Cannot get register (%d)\n", rv); kfree(data); return -EFAULT; } if (put_user(data[0], (int __user *)(arg + 2 * sizeof(int)))) { - dev_err(&xr21v141x->control->dev, "Cannot put user result\n"); + dev_err(&acm->control->dev, "Cannot put user result\n"); kfree(data); return -EFAULT; } @@ -927,9 +1098,9 @@ static int xr21v141x_tty_ioctl(struct tty_struct *tty, return -EFAULT; if (block == -1) - block = xr21v141x->block; + block = acm->block; - rv = vizzini_set_reg(xr21v141x, block, reg, val); + rv = vizzini_set_reg(acm, block, reg, val); if (rv < 0) return -EFAULT; break; @@ -937,9 +1108,9 @@ static int xr21v141x_tty_ioctl(struct tty_struct *tty, case VZIOC_SET_ADDRESS_MATCH: match = arg; - dev_dbg(&xr21v141x->control->dev, "%s VIOC_SET_ADDRESS_MATCH %d\n", __func__, match); + dev_dbg(&acm->control->dev, "%s VIOC_SET_ADDRESS_MATCH %d\n", __func__, match); - vizzini_disable(xr21v141x); + vizzini_disable(acm); if (match & VZ_ADDRESS_MATCH_DISABLE) { flow = UART_FLOW_MODE_NONE; @@ -949,47 +1120,47 @@ static int xr21v141x_tty_ioctl(struct tty_struct *tty, broadcast = (match >> VZ_ADDRESS_BROADCAST_S) & 0xff; } - dev_dbg(&xr21v141x->control->dev, "address match: flow=%d ucast=%d bcast=%u\n", + dev_dbg(&acm->control->dev, "address match: flow=%d ucast=%d bcast=%u\n", flow, unicast, broadcast); - vizzini_set_reg(xr21v141x, xr21v141x->block, UART_FLOW, flow); - vizzini_set_reg(xr21v141x, xr21v141x->block, UART_XON_CHAR, unicast); - vizzini_set_reg(xr21v141x, xr21v141x->block, UART_XOFF_CHAR, broadcast); + vizzini_set_reg(acm, acm->block, UART_FLOW, flow); + vizzini_set_reg(acm, acm->block, UART_XON_CHAR, unicast); + vizzini_set_reg(acm, acm->block, UART_XOFF_CHAR, broadcast); - vizzini_enable(xr21v141x); + vizzini_enable(acm); break; case VZIOC_SET_PRECISE_FLAGS: preciseflags = arg; - dev_dbg(&xr21v141x->control->dev, "%s VIOC_SET_PRECISE_FLAGS %d\n", __func__, preciseflags); + dev_dbg(&acm->control->dev, "%s VIOC_SET_PRECISE_FLAGS %d\n", __func__, preciseflags); - vizzini_disable(xr21v141x); + vizzini_disable(acm); if (preciseflags) { - xr21v141x->preciseflags = 1; + acm->preciseflags = 1; } else { - xr21v141x->preciseflags = 0; + acm->preciseflags = 0; } - vizzini_set_reg(xr21v141x, EPLOCALS_REG_BLOCK, - (xr21v141x->block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, - xr21v141x->preciseflags); + vizzini_set_reg(acm, EPLOCALS_REG_BLOCK, + (acm->block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, + acm->preciseflags); - vizzini_enable(xr21v141x); + vizzini_enable(acm); rv = 0; break; case VZIOC_TEST_MODE: selector = arg; - dev_dbg(&xr21v141x->control->dev, "%s VIOC_TEST_MODE 0x%02x\n", __func__, selector); - vizzini_test_mode(xr21v141x, selector); + dev_dbg(&acm->control->dev, "%s VIOC_TEST_MODE 0x%02x\n", __func__, selector); + vizzini_test_mode(acm, selector); rv = 0; break; case VZIOC_LOOPBACK: selector = arg; - dev_dbg(&xr21v141x->control->dev, "VIOC_LOOPBACK 0x%02x\n", selector); - vizzini_loopback(xr21v141x, selector); + dev_dbg(&acm->control->dev, "VIOC_LOOPBACK 0x%02x\n", selector); + vizzini_loopback(acm, selector); rv = 0; break; } @@ -1039,43 +1210,43 @@ static struct vizzini_baud_rate vizzini_baud_rates[] = { { 0XFFF, 0XFFE, 0XFFD }, }; -static int vizzini_set_baud_rate(struct xr21v141x *xr21v141x, unsigned int rate) +static int vizzini_set_baud_rate(struct acm *acm, unsigned int rate) { - int block = xr21v141x->block; + int block = acm->block; unsigned int divisor = 48000000 / rate; unsigned int i = ((32 * 48000000) / rate) & 0x1f; unsigned int tx_mask = vizzini_baud_rates[i].tx; unsigned int rx_mask = (divisor & 1) ? vizzini_baud_rates[i].rx1 : vizzini_baud_rates[i].rx0; - dev_dbg(&xr21v141x->control->dev, "Setting baud rate to %d: i=%u div=%u tx=%03x rx=%03x\n", rate, i, divisor, tx_mask, rx_mask); + dev_dbg(&acm->control->dev, "Setting baud rate to %d: i=%u div=%u tx=%03x rx=%03x\n", rate, i, divisor, tx_mask, rx_mask); - vizzini_set_reg(xr21v141x, block, UART_CLOCK_DIVISOR_0, (divisor >> 0) & 0xff); - vizzini_set_reg(xr21v141x, block, UART_CLOCK_DIVISOR_1, (divisor >> 8) & 0xff); - vizzini_set_reg(xr21v141x, block, UART_CLOCK_DIVISOR_2, (divisor >> 16) & 0xff); - vizzini_set_reg(xr21v141x, block, UART_TX_CLOCK_MASK_0, (tx_mask >> 0) & 0xff); - vizzini_set_reg(xr21v141x, block, UART_TX_CLOCK_MASK_1, (tx_mask >> 8) & 0xff); - vizzini_set_reg(xr21v141x, block, UART_RX_CLOCK_MASK_0, (rx_mask >> 0) & 0xff); - vizzini_set_reg(xr21v141x, block, UART_RX_CLOCK_MASK_1, (rx_mask >> 8) & 0xff); + vizzini_set_reg(acm, block, UART_CLOCK_DIVISOR_0, (divisor >> 0) & 0xff); + vizzini_set_reg(acm, block, UART_CLOCK_DIVISOR_1, (divisor >> 8) & 0xff); + vizzini_set_reg(acm, block, UART_CLOCK_DIVISOR_2, (divisor >> 16) & 0xff); + vizzini_set_reg(acm, block, UART_TX_CLOCK_MASK_0, (tx_mask >> 0) & 0xff); + vizzini_set_reg(acm, block, UART_TX_CLOCK_MASK_1, (tx_mask >> 8) & 0xff); + vizzini_set_reg(acm, block, UART_RX_CLOCK_MASK_0, (rx_mask >> 0) & 0xff); + vizzini_set_reg(acm, block, UART_RX_CLOCK_MASK_1, (rx_mask >> 8) & 0xff); return -EINVAL; } -static void xr21v141x_tty_set_termios(struct tty_struct *tty, +static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) { - struct xr21v141x *xr21v141x = tty->driver_data; + struct acm *acm = tty->driver_data; unsigned int cflag, block; speed_t rate; unsigned int format_size, format_parity, format_stop, flow, gpio_mode; cflag = tty->termios.c_cflag; - xr21v141x->clocal = ((cflag & CLOCAL) != 0); + acm->clocal = ((cflag & CLOCAL) != 0); - block = xr21v141x->block; + block = acm->block; - vizzini_disable(xr21v141x); + vizzini_disable(acm); if ((cflag & CSIZE) == CS7) { format_size = UART_FORMAT_SIZE_7; @@ -1085,7 +1256,7 @@ static void xr21v141x_tty_set_termios(struct tty_struct *tty, } else { format_size = UART_FORMAT_SIZE_8; } - xr21v141x->trans9 = (format_size == UART_FORMAT_SIZE_9); + acm->trans9 = (format_size == UART_FORMAT_SIZE_9); if (cflag & PARENB) { if (cflag & PARODD) { @@ -1113,16 +1284,16 @@ static void xr21v141x_tty_set_termios(struct tty_struct *tty, #ifdef VIZZINI_IWA if (format_size == UART_FORMAT_SIZE_8) { - xr21v141x->iwa = format_parity; + acm->iwa = format_parity; if (portdata->iwa != UART_FORMAT_PARITY_NONE) { format_size = UART_FORMAT_SIZE_9; format_parity = UART_FORMAT_PARITY_NONE; } } else { - xr21v141x->iwa = UART_FORMAT_PARITY_NONE; + acm->iwa = UART_FORMAT_PARITY_NONE; } #endif - vizzini_set_reg(xr21v141x, block, UART_FORMAT, format_size | format_parity | format_stop); + vizzini_set_reg(acm, block, UART_FORMAT, format_size | format_parity | format_stop); if (cflag & CRTSCTS) { flow = UART_FLOW_MODE_HW; @@ -1134,44 +1305,45 @@ static void xr21v141x_tty_set_termios(struct tty_struct *tty, flow = UART_FLOW_MODE_SW; gpio_mode = UART_GPIO_MODE_SEL_GPIO; - vizzini_set_reg(xr21v141x, block, UART_XON_CHAR, start_char); - vizzini_set_reg(xr21v141x, block, UART_XOFF_CHAR, stop_char); + vizzini_set_reg(acm, block, UART_XON_CHAR, start_char); + vizzini_set_reg(acm, block, UART_XOFF_CHAR, stop_char); } else { flow = UART_FLOW_MODE_NONE; gpio_mode = UART_GPIO_MODE_SEL_GPIO; } - vizzini_set_reg(xr21v141x, block, UART_FLOW, flow); - vizzini_set_reg(xr21v141x, block, UART_GPIO_MODE, gpio_mode); + vizzini_set_reg(acm, block, UART_FLOW, flow); + vizzini_set_reg(acm, block, UART_GPIO_MODE, gpio_mode); - if (xr21v141x->trans9) { + if (acm->trans9) { /* Turn on wide mode if we're 9-bit transparent. */ - vizzini_set_reg(xr21v141x, EPLOCALS_REG_BLOCK, (block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, 1); + vizzini_set_reg(acm, EPLOCALS_REG_BLOCK, (block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, 1); #ifdef VIZZINI_IWA - } else if (xr21v141x->iwa != UART_FORMAT_PARITY_NONE) { - vizzini_set_reg(xr21v141x, EPLOCALS_REG_BLOCK, (block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, 1); + } else if (acm->iwa != UART_FORMAT_PARITY_NONE) { + vizzini_set_reg(acm, EPLOCALS_REG_BLOCK, (block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, 1); #endif - } else if (!xr21v141x->preciseflags) { + } else if (!acm->preciseflags) { /* Turn off wide mode unless we have precise flags. */ - vizzini_set_reg(xr21v141x, EPLOCALS_REG_BLOCK, (block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, 0); + vizzini_set_reg(acm, EPLOCALS_REG_BLOCK, (block * MEM_EP_LOCALS_SIZE) + EP_WIDE_MODE, 0); } rate = cpu_to_le32(tty_get_baud_rate(tty)); if(rate) - vizzini_set_baud_rate(xr21v141x, rate); + vizzini_set_baud_rate(acm, rate); - xr21v141x->line.dwDTERate = rate; - xr21v141x->line.bCharFormat = format_stop; - xr21v141x->line.bParityType = format_parity; - xr21v141x->line.bDataBits = format_size; + acm->line.dwDTERate = rate; + acm->line.bCharFormat = format_stop; + acm->line.bParityType = format_parity; + acm->line.bDataBits = format_size; - vizzini_enable(xr21v141x); + vizzini_enable(acm); } -static const struct tty_port_operations xr21v141x_port_ops = { - .shutdown = xr21v141x_port_shutdown, - .activate = xr21v141x_port_activate, - .destruct = xr21v141x_port_destruct, +static const struct tty_port_operations acm_port_ops = { + .dtr_rts = acm_port_dtr_rts, + .shutdown = acm_port_shutdown, + .activate = acm_port_activate, + .destruct = acm_port_destruct, }; /* @@ -1179,40 +1351,40 @@ static const struct tty_port_operations xr21v141x_port_ops = { */ /* Little helpers: write/read buffers free */ -static void xr21v141x_write_buffers_free(struct xr21v141x *xr21v141x) +static void acm_write_buffers_free(struct acm *acm) { int i; - struct xr21v141x_wb *wb; - struct usb_device *usb_dev = interface_to_usbdev(xr21v141x->control); + struct acm_wb *wb; + struct usb_device *usb_dev = interface_to_usbdev(acm->control); - for (wb = &xr21v141x->wb[0], i = 0; i < ACM_NW; i++, wb++) - usb_free_coherent(usb_dev, xr21v141x->writesize, wb->buf, wb->dmah); + for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) + usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah); } -static void xr21v141x_read_buffers_free(struct xr21v141x *xr21v141x) +static void acm_read_buffers_free(struct acm *acm) { - struct usb_device *usb_dev = interface_to_usbdev(xr21v141x->control); + struct usb_device *usb_dev = interface_to_usbdev(acm->control); int i; - for (i = 0; i < xr21v141x->rx_buflimit; i++) - usb_free_coherent(usb_dev, xr21v141x->readsize, - xr21v141x->read_buffers[i].base, xr21v141x->read_buffers[i].dma); + for (i = 0; i < acm->rx_buflimit; i++) + usb_free_coherent(usb_dev, acm->readsize, + acm->read_buffers[i].base, acm->read_buffers[i].dma); } /* Little helper: write buffers allocate */ -static int xr21v141x_write_buffers_alloc(struct xr21v141x *xr21v141x) +static int acm_write_buffers_alloc(struct acm *acm) { int i; - struct xr21v141x_wb *wb; + struct acm_wb *wb; - for (wb = &xr21v141x->wb[0], i = 0; i < ACM_NW; i++, wb++) { - wb->buf = usb_alloc_coherent(xr21v141x->dev, xr21v141x->writesize, GFP_KERNEL, + for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { + wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL, &wb->dmah); if (!wb->buf) { while (i != 0) { --i; --wb; - usb_free_coherent(xr21v141x->dev, xr21v141x->writesize, + usb_free_coherent(acm->dev, acm->writesize, wb->buf, wb->dmah); } return -ENOMEM; @@ -1221,7 +1393,7 @@ static int xr21v141x_write_buffers_alloc(struct xr21v141x *xr21v141x) return 0; } -static int xr21v141x_probe(struct usb_interface *intf, +static int acm_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_cdc_union_desc *union_header = NULL; @@ -1234,7 +1406,7 @@ static int xr21v141x_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *epread = NULL; struct usb_endpoint_descriptor *epwrite = NULL; struct usb_device *usb_dev = interface_to_usbdev(intf); - struct xr21v141x *xr21v141x; + struct acm *acm; int minor; int ctrlsize, readsize; u8 *buf; @@ -1245,13 +1417,25 @@ static int xr21v141x_probe(struct usb_interface *intf, unsigned long quirks; int num_rx_buf; int i; + unsigned int elength = 0; int combined_interfaces = 0; struct device *tty_dev; int rv = -ENOMEM; /* normal quirks */ quirks = (unsigned long)id->driver_info; - num_rx_buf = ACM_NR; + + if (quirks == IGNORE_DEVICE) + return -ENODEV; + + num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; + + /* handle quirks deadly to normal probing*/ + if (quirks == NO_UNION_NORMAL) { + data_interface = usb_ifnum_to_if(usb_dev, 1); + control_interface = usb_ifnum_to_if(usb_dev, 0); + goto skip_normal_probe; + } /* normal probing*/ if (!buffer) { @@ -1275,6 +1459,12 @@ static int xr21v141x_probe(struct usb_interface *intf, } while (buflen > 0) { + elength = buffer[0]; + if (!elength) { + dev_err(&intf->dev, "skipping garbage byte\n"); + elength = 1; + goto next_desc; + } if (buffer[1] != USB_DT_CS_INTERFACE) { dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; @@ -1282,6 +1472,8 @@ static int xr21v141x_probe(struct usb_interface *intf, switch (buffer[2]) { case USB_CDC_UNION_TYPE: /* we've found it */ + if (elength < sizeof(struct usb_cdc_union_desc)) + goto next_desc; if (union_header) { dev_err(&intf->dev, "More than one " "union descriptor, skipping ...\n"); @@ -1290,31 +1482,36 @@ static int xr21v141x_probe(struct usb_interface *intf, union_header = (struct usb_cdc_union_desc *)buffer; break; case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ + if (elength < sizeof(struct usb_cdc_country_functional_desc)) + goto next_desc; cfd = (struct usb_cdc_country_functional_desc *)buffer; break; case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ case USB_CDC_ACM_TYPE: + if (elength < 4) + goto next_desc; ac_management_function = buffer[3]; break; case USB_CDC_CALL_MANAGEMENT_TYPE: + if (elength < 5) + goto next_desc; call_management_function = buffer[3]; call_interface_num = buffer[4]; - if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) - dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); break; default: - /* there are LOTS more CDC descriptors that + /* + * there are LOTS more CDC descriptors that * could legitimately be found here. */ dev_dbg(&intf->dev, "Ignoring descriptor: " - "type %02x, length %d\n", - buffer[2], buffer[0]); + "type %02x, length %ud\n", + buffer[2], elength); break; } next_desc: - buflen -= buffer[0]; - buffer += buffer[0]; + buflen -= elength; + buffer += elength; } if (!union_header) { @@ -1340,10 +1537,11 @@ next_desc: } else { control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); - if (!control_interface || !data_interface) { - dev_dbg(&intf->dev, "no interfaces\n"); - return -ENODEV; - } + } + + if (!control_interface || !data_interface) { + dev_dbg(&intf->dev, "no interfaces\n"); + return -ENODEV; } if (data_interface_num != call_interface_num) @@ -1379,17 +1577,16 @@ look_for_collapsed_interface: goto made_compressed_probe; } +skip_normal_probe: + /*workaround for switched interfaces */ if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) { if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { - struct usb_interface *t; dev_dbg(&intf->dev, "Your device has switched interfaces.\n"); - t = control_interface; - control_interface = data_interface; - data_interface = t; + swap(control_interface, data_interface); } else { return -EINVAL; } @@ -1418,156 +1615,141 @@ look_for_collapsed_interface: /* workaround for switched endpoints */ if (!usb_endpoint_dir_in(epread)) { /* descriptors are swapped */ - struct usb_endpoint_descriptor *t; - //dev_dbg(&intf->dev, - // "The data interface has switched endpoints\n"); - t = epread; - epread = epwrite; - epwrite = t; + dev_dbg(&intf->dev, + "The data interface has switched endpoints\n"); + swap(epread, epwrite); } made_compressed_probe: dev_dbg(&intf->dev, "interfaces are valid\n"); - xr21v141x = kzalloc(sizeof(struct xr21v141x), GFP_KERNEL); - if (xr21v141x == NULL) { - dev_err(&intf->dev, "out of memory (xr21v141x kzalloc)\n"); + acm = kzalloc(sizeof(struct acm), GFP_KERNEL); + if (acm == NULL) goto alloc_fail; - } - minor = xr21v141x_alloc_minor(xr21v141x); - if (minor == XR21V141X_TTY_MINORS) { - dev_err(&intf->dev, "no more free xr21v141x devices\n"); - kfree(xr21v141x); + minor = acm_alloc_minor(acm); + if (minor < 0) { + dev_err(&intf->dev, "no more free acm devices\n"); + kfree(acm); return -ENODEV; } ctrlsize = usb_endpoint_maxp(epctrl); readsize = usb_endpoint_maxp(epread) * (quirks == SINGLE_RX_URB ? 1 : 2); - xr21v141x->combined_interfaces = combined_interfaces; - xr21v141x->writesize = usb_endpoint_maxp(epwrite) * 20; - xr21v141x->control = control_interface; - xr21v141x->data = data_interface; - xr21v141x->minor = minor; - xr21v141x->dev = usb_dev; - xr21v141x->ctrl_caps = ac_management_function; + acm->combined_interfaces = combined_interfaces; + acm->writesize = usb_endpoint_maxp(epwrite) * 20; + acm->control = control_interface; + acm->data = data_interface; + acm->minor = minor; + acm->dev = usb_dev; + acm->ctrl_caps = ac_management_function; if (quirks & NO_CAP_LINE) - xr21v141x->ctrl_caps &= ~USB_CDC_CAP_LINE; - xr21v141x->ctrlsize = ctrlsize; - xr21v141x->readsize = readsize; - xr21v141x->rx_buflimit = num_rx_buf; - INIT_WORK(&xr21v141x->work, xr21v141x_softint); - spin_lock_init(&xr21v141x->write_lock); - spin_lock_init(&xr21v141x->read_lock); - mutex_init(&xr21v141x->mutex); - xr21v141x->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); - xr21v141x->is_int_ep = usb_endpoint_xfer_int(epread); - if (xr21v141x->is_int_ep) - xr21v141x->bInterval = epread->bInterval; - tty_port_init(&xr21v141x->port); - xr21v141x->port.ops = &xr21v141x_port_ops; - - xr21v141x->block = epwrite->bEndpointAddress - 1; - - buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &xr21v141x->ctrl_dma); - if (!buf) { - dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n"); + acm->ctrl_caps &= ~USB_CDC_CAP_LINE; + acm->ctrlsize = ctrlsize; + acm->readsize = readsize; + acm->rx_buflimit = num_rx_buf; + INIT_WORK(&acm->work, acm_softint); + init_waitqueue_head(&acm->wioctl); + spin_lock_init(&acm->write_lock); + spin_lock_init(&acm->read_lock); + mutex_init(&acm->mutex); + acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); + acm->is_int_ep = usb_endpoint_xfer_int(epread); + if (acm->is_int_ep) + acm->bInterval = epread->bInterval; + tty_port_init(&acm->port); + acm->port.ops = &acm_port_ops; + init_usb_anchor(&acm->delayed); + acm->quirks = quirks; + + acm->block = epwrite->bEndpointAddress - 1; + + buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); + if (!buf) goto alloc_fail2; - } - xr21v141x->ctrl_buffer = buf; + acm->ctrl_buffer = buf; - if (xr21v141x_write_buffers_alloc(xr21v141x) < 0) { - dev_err(&intf->dev, "out of memory (write buffer alloc)\n"); + if (acm_write_buffers_alloc(acm) < 0) goto alloc_fail4; - } - xr21v141x->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); - if (!xr21v141x->ctrlurb) { - dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); + acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); + if (!acm->ctrlurb) goto alloc_fail5; - } + for (i = 0; i < num_rx_buf; i++) { - struct xr21v141x_rb *rb = &(xr21v141x->read_buffers[i]); + struct acm_rb *rb = &(acm->read_buffers[i]); struct urb *urb; - rb->base = usb_alloc_coherent(xr21v141x->dev, readsize, GFP_KERNEL, + rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, &rb->dma); - if (!rb->base) { - dev_err(&intf->dev, "out of memory " - "(read bufs usb_alloc_coherent)\n"); + if (!rb->base) goto alloc_fail6; - } rb->index = i; - rb->instance = xr21v141x; + rb->instance = acm; urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - dev_err(&intf->dev, - "out of memory (read urbs usb_alloc_urb)\n"); + if (!urb) goto alloc_fail6; - } + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = rb->dma; - if (xr21v141x->is_int_ep) { - usb_fill_int_urb(urb, xr21v141x->dev, - xr21v141x->rx_endpoint, + if (acm->is_int_ep) { + usb_fill_int_urb(urb, acm->dev, + acm->rx_endpoint, rb->base, - xr21v141x->readsize, - xr21v141x_read_bulk_callback, rb, - xr21v141x->bInterval); + acm->readsize, + acm_read_bulk_callback, rb, + acm->bInterval); } else { - usb_fill_bulk_urb(urb, xr21v141x->dev, - xr21v141x->rx_endpoint, + usb_fill_bulk_urb(urb, acm->dev, + acm->rx_endpoint, rb->base, - xr21v141x->readsize, - xr21v141x_read_bulk_callback, rb); + acm->readsize, + acm_read_bulk_callback, rb); } - xr21v141x->read_urbs[i] = urb; - __set_bit(i, &xr21v141x->read_urbs_free); + acm->read_urbs[i] = urb; + __set_bit(i, &acm->read_urbs_free); } for (i = 0; i < ACM_NW; i++) { - struct xr21v141x_wb *snd = &(xr21v141x->wb[i]); + struct acm_wb *snd = &(acm->wb[i]); snd->urb = usb_alloc_urb(0, GFP_KERNEL); - if (snd->urb == NULL) { - dev_err(&intf->dev, - "out of memory (write urbs usb_alloc_urb)\n"); + if (snd->urb == NULL) goto alloc_fail7; - } if (usb_endpoint_xfer_int(epwrite)) usb_fill_int_urb(snd->urb, usb_dev, usb_sndintpipe(usb_dev, epwrite->bEndpointAddress), - NULL, xr21v141x->writesize, xr21v141x_write_bulk, snd, epwrite->bInterval); + NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval); else usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), - NULL, xr21v141x->writesize, xr21v141x_write_bulk, snd); + NULL, acm->writesize, acm_write_bulk, snd); snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - snd->instance = xr21v141x; + snd->instance = acm; } - usb_set_intfdata(intf, xr21v141x); + usb_set_intfdata(intf, acm); i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); if (i < 0) goto alloc_fail7; if (cfd) { /* export the country data */ - xr21v141x->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); - if (!xr21v141x->country_codes) + acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); + if (!acm->country_codes) goto skip_countries; - xr21v141x->country_code_size = cfd->bLength - 4; - memcpy(xr21v141x->country_codes, (u8 *)&cfd->wCountyCode0, + acm->country_code_size = cfd->bLength - 4; + memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); - xr21v141x->country_rel_date = cfd->iCountryCodeRelDate; + acm->country_rel_date = cfd->iCountryCodeRelDate; i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); if (i < 0) { - kfree(xr21v141x->country_codes); - xr21v141x->country_codes = NULL; - xr21v141x->country_code_size = 0; + kfree(acm->country_codes); + acm->country_codes = NULL; + acm->country_code_size = 0; goto skip_countries; } @@ -1575,88 +1757,92 @@ made_compressed_probe: &dev_attr_iCountryCodeRelDate); if (i < 0) { device_remove_file(&intf->dev, &dev_attr_wCountryCodes); - kfree(xr21v141x->country_codes); - xr21v141x->country_codes = NULL; - xr21v141x->country_code_size = 0; + kfree(acm->country_codes); + acm->country_codes = NULL; + acm->country_code_size = 0; goto skip_countries; } } skip_countries: - usb_fill_int_urb(xr21v141x->ctrlurb, usb_dev, + usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), - xr21v141x->ctrl_buffer, ctrlsize, xr21v141x_ctrl_irq, xr21v141x, + acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, /* works around buggy devices */ epctrl->bInterval ? epctrl->bInterval : 16); - xr21v141x->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - xr21v141x->ctrlurb->transfer_dma = xr21v141x->ctrl_dma; - - dev_info(&intf->dev, "ttyVIZ%d: XR21v14x usb uart device\n", minor); + acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + acm->ctrlurb->transfer_dma = acm->ctrl_dma; - xr21v141x_set_control(xr21v141x, xr21v141x->ctrlout); + dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); - xr21v141x->line.dwDTERate = cpu_to_le32(9600); - xr21v141x->line.bDataBits = 8; - xr21v141x_set_line(xr21v141x, &xr21v141x->line); + acm->line.dwDTERate = cpu_to_le32(9600); + acm->line.bDataBits = 8; + acm_set_line(acm, &acm->line); - usb_driver_claim_interface(&xr21v141x_driver, data_interface, xr21v141x); - usb_set_intfdata(data_interface, xr21v141x); + usb_driver_claim_interface(&acm_driver, data_interface, acm); + usb_set_intfdata(data_interface, acm); usb_get_intf(control_interface); - tty_dev = tty_port_register_device(&xr21v141x->port, xr21v141x_tty_driver, - minor, &control_interface->dev); + tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor, + &control_interface->dev); if (IS_ERR(tty_dev)) { rv = PTR_ERR(tty_dev); goto alloc_fail8; } + if (quirks & CLEAR_HALT_CONDITIONS) { + usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress)); + usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress)); + } + return 0; alloc_fail8: - if (xr21v141x->country_codes) { - device_remove_file(&xr21v141x->control->dev, + if (acm->country_codes) { + device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); - device_remove_file(&xr21v141x->control->dev, + device_remove_file(&acm->control->dev, &dev_attr_iCountryCodeRelDate); + kfree(acm->country_codes); } - device_remove_file(&xr21v141x->control->dev, &dev_attr_bmCapabilities); + device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); alloc_fail7: usb_set_intfdata(intf, NULL); for (i = 0; i < ACM_NW; i++) - usb_free_urb(xr21v141x->wb[i].urb); + usb_free_urb(acm->wb[i].urb); alloc_fail6: for (i = 0; i < num_rx_buf; i++) - usb_free_urb(xr21v141x->read_urbs[i]); - xr21v141x_read_buffers_free(xr21v141x); - usb_free_urb(xr21v141x->ctrlurb); + usb_free_urb(acm->read_urbs[i]); + acm_read_buffers_free(acm); + usb_free_urb(acm->ctrlurb); alloc_fail5: - xr21v141x_write_buffers_free(xr21v141x); + acm_write_buffers_free(acm); alloc_fail4: - usb_free_coherent(usb_dev, ctrlsize, xr21v141x->ctrl_buffer, xr21v141x->ctrl_dma); + usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); alloc_fail2: - xr21v141x_release_minor(xr21v141x); - kfree(xr21v141x); + acm_release_minor(acm); + kfree(acm); alloc_fail: return rv; } -static void stop_data_traffic(struct xr21v141x *xr21v141x) +static void stop_data_traffic(struct acm *acm) { int i; - dev_dbg(&xr21v141x->control->dev, "%s\n", __func__); + dev_dbg(&acm->control->dev, "%s\n", __func__); - usb_kill_urb(xr21v141x->ctrlurb); + usb_kill_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) - usb_kill_urb(xr21v141x->wb[i].urb); - for (i = 0; i < xr21v141x->rx_buflimit; i++) - usb_kill_urb(xr21v141x->read_urbs[i]); + usb_kill_urb(acm->wb[i].urb); + for (i = 0; i < acm->rx_buflimit; i++) + usb_kill_urb(acm->read_urbs[i]); - cancel_work_sync(&xr21v141x->work); + cancel_work_sync(&acm->work); } -static void xr21v141x_disconnect(struct usb_interface *intf) +static void acm_disconnect(struct usb_interface *intf) { - struct xr21v141x *xr21v141x = usb_get_intfdata(intf); + struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); struct tty_struct *tty; int i; @@ -1664,105 +1850,93 @@ static void xr21v141x_disconnect(struct usb_interface *intf) dev_dbg(&intf->dev, "%s\n", __func__); /* sibling interface is already cleaning up */ - if (!xr21v141x) + if (!acm) return; - mutex_lock(&xr21v141x->mutex); - xr21v141x->disconnected = true; - if (xr21v141x->country_codes) { - device_remove_file(&xr21v141x->control->dev, + mutex_lock(&acm->mutex); + acm->disconnected = true; + if (acm->country_codes) { + device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); - device_remove_file(&xr21v141x->control->dev, + device_remove_file(&acm->control->dev, &dev_attr_iCountryCodeRelDate); } - device_remove_file(&xr21v141x->control->dev, &dev_attr_bmCapabilities); - usb_set_intfdata(xr21v141x->control, NULL); - usb_set_intfdata(xr21v141x->data, NULL); - mutex_unlock(&xr21v141x->mutex); + wake_up_all(&acm->wioctl); + device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); + usb_set_intfdata(acm->control, NULL); + usb_set_intfdata(acm->data, NULL); + mutex_unlock(&acm->mutex); - tty = tty_port_tty_get(&xr21v141x->port); + tty = tty_port_tty_get(&acm->port); if (tty) { tty_vhangup(tty); tty_kref_put(tty); } - stop_data_traffic(xr21v141x); + stop_data_traffic(acm); - tty_unregister_device(xr21v141x_tty_driver, xr21v141x->minor); + tty_unregister_device(acm_tty_driver, acm->minor); - usb_free_urb(xr21v141x->ctrlurb); + usb_free_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) - usb_free_urb(xr21v141x->wb[i].urb); - for (i = 0; i < xr21v141x->rx_buflimit; i++) - usb_free_urb(xr21v141x->read_urbs[i]); - xr21v141x_write_buffers_free(xr21v141x); - usb_free_coherent(usb_dev, xr21v141x->ctrlsize, xr21v141x->ctrl_buffer, xr21v141x->ctrl_dma); - xr21v141x_read_buffers_free(xr21v141x); - - if (!xr21v141x->combined_interfaces) - usb_driver_release_interface(&xr21v141x_driver, intf == xr21v141x->control ? - xr21v141x->data : xr21v141x->control); - - tty_port_put(&xr21v141x->port); + usb_free_urb(acm->wb[i].urb); + for (i = 0; i < acm->rx_buflimit; i++) + usb_free_urb(acm->read_urbs[i]); + acm_write_buffers_free(acm); + usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); + acm_read_buffers_free(acm); + + if (!acm->combined_interfaces) + usb_driver_release_interface(&acm_driver, intf == acm->control ? + acm->data : acm->control); + + tty_port_put(&acm->port); } #ifdef CONFIG_PM -static int xr21v141x_suspend(struct usb_interface *intf, pm_message_t message) +static int acm_suspend(struct usb_interface *intf, pm_message_t message) { - struct xr21v141x *xr21v141x = usb_get_intfdata(intf); + struct acm *acm = usb_get_intfdata(intf); int cnt; + spin_lock_irq(&acm->write_lock); if (PMSG_IS_AUTO(message)) { - int b; - - spin_lock_irq(&xr21v141x->write_lock); - b = xr21v141x->transmitting; - spin_unlock_irq(&xr21v141x->write_lock); - if (b) + if (acm->transmitting) { + spin_unlock_irq(&acm->write_lock); return -EBUSY; + } } - - spin_lock_irq(&xr21v141x->read_lock); - spin_lock(&xr21v141x->write_lock); - cnt = xr21v141x->susp_count++; - spin_unlock(&xr21v141x->write_lock); - spin_unlock_irq(&xr21v141x->read_lock); + cnt = acm->susp_count++; + spin_unlock_irq(&acm->write_lock); if (cnt) return 0; - if (test_bit(ASYNCB_INITIALIZED, &xr21v141x->port.flags)) - stop_data_traffic(xr21v141x); + stop_data_traffic(acm); return 0; } -static int xr21v141x_resume(struct usb_interface *intf) +static int acm_resume(struct usb_interface *intf) { - struct xr21v141x *xr21v141x = usb_get_intfdata(intf); - struct xr21v141x_wb *wb; + struct acm *acm = usb_get_intfdata(intf); + struct urb *urb; int rv = 0; - int cnt; - spin_lock_irq(&xr21v141x->read_lock); - xr21v141x->susp_count -= 1; - cnt = xr21v141x->susp_count; - spin_unlock_irq(&xr21v141x->read_lock); + spin_lock_irq(&acm->write_lock); - if (cnt) - return 0; + if (--acm->susp_count) + goto out; - if (test_bit(ASYNCB_INITIALIZED, &xr21v141x->port.flags)) { - rv = usb_submit_urb(xr21v141x->ctrlurb, GFP_NOIO); + if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) { + rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC); - spin_lock_irq(&xr21v141x->write_lock); - if (xr21v141x->delayed_wb) { - wb = xr21v141x->delayed_wb; - xr21v141x->delayed_wb = NULL; - spin_unlock_irq(&xr21v141x->write_lock); - xr21v141x_start_wb(xr21v141x, wb); - } else { - spin_unlock_irq(&xr21v141x->write_lock); + for (;;) { + urb = usb_get_from_anchor(&acm->delayed); + if (!urb) + break; + + acm_start_wb(acm, urb->context); } /* @@ -1770,23 +1944,24 @@ static int xr21v141x_resume(struct usb_interface *intf) * do the write path at all cost */ if (rv < 0) - goto err_out; + goto out; - rv = xr21v141x_submit_read_urbs(xr21v141x, GFP_NOIO); + rv = acm_submit_read_urbs(acm, GFP_ATOMIC); } +out: + spin_unlock_irq(&acm->write_lock); -err_out: return rv; } -static int xr21v141x_reset_resume(struct usb_interface *intf) +static int acm_reset_resume(struct usb_interface *intf) { - struct xr21v141x *xr21v141x = usb_get_intfdata(intf); + struct acm *acm = usb_get_intfdata(intf); - if (test_bit(ASYNCB_INITIALIZED, &xr21v141x->port.flags)) - tty_port_tty_hangup(&xr21v141x->port, false); + if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) + tty_port_tty_hangup(&acm->port, false); - return xr21v141x_resume(intf); + return acm_resume(intf); } #endif /* CONFIG_PM */ @@ -1796,25 +1971,22 @@ static int xr21v141x_reset_resume(struct usb_interface *intf) */ static const struct usb_device_id xrusb_ids[] = { - { USB_DEVICE(0x04e2, 0x1410), - }, - { USB_DEVICE(0x04e2, 0x1412), - }, - { USB_DEVICE(0x04e2, 0x1414), - }, + { USB_DEVICE(0x04e2, 0x1410), }, + { USB_DEVICE(0x04e2, 0x1412), }, + { USB_DEVICE(0x04e2, 0x1414), }, { } }; MODULE_DEVICE_TABLE(usb, xrusb_ids); -static struct usb_driver xr21v141x_driver = { +static struct usb_driver acm_driver = { .name = "vizzini", - .probe = xr21v141x_probe, - .disconnect = xr21v141x_disconnect, + .probe = acm_probe, + .disconnect = acm_disconnect, #ifdef CONFIG_PM - .suspend = xr21v141x_suspend, - .resume = xr21v141x_resume, - .reset_resume = xr21v141x_reset_resume, + .suspend = acm_suspend, + .resume = acm_resume, + .reset_resume = acm_reset_resume, #endif .id_table = xrusb_ids, #ifdef CONFIG_PM @@ -1827,60 +1999,56 @@ static struct usb_driver xr21v141x_driver = { * TTY driver structures. */ -static const struct tty_operations xr21v141x_ops = { - .install = xr21v141x_tty_install, - .open = xr21v141x_tty_open, - .close = xr21v141x_tty_close, - .cleanup = xr21v141x_tty_cleanup, - .hangup = xr21v141x_tty_hangup, - .write = xr21v141x_tty_write, - .write_room = xr21v141x_tty_write_room, - .ioctl = xr21v141x_tty_ioctl, - .throttle = xr21v141x_tty_throttle, - .unthrottle = xr21v141x_tty_unthrottle, - .chars_in_buffer = xr21v141x_tty_chars_in_buffer, - .break_ctl = xr21v141x_tty_break_ctl, - .set_termios = xr21v141x_tty_set_termios, - .tiocmget = xr21v141x_tty_tiocmget, - .tiocmset = xr21v141x_tty_tiocmset, +static const struct tty_operations acm_ops = { + .install = acm_tty_install, + .open = acm_tty_open, + .close = acm_tty_close, + .cleanup = acm_tty_cleanup, + .hangup = acm_tty_hangup, + .write = acm_tty_write, + .write_room = acm_tty_write_room, + .ioctl = acm_tty_ioctl, + .throttle = acm_tty_throttle, + .unthrottle = acm_tty_unthrottle, + .chars_in_buffer = acm_tty_chars_in_buffer, + .break_ctl = acm_tty_break_ctl, + .set_termios = acm_tty_set_termios, + .tiocmget = acm_tty_tiocmget, + .tiocmset = acm_tty_tiocmset, }; /* * Init / exit. */ -static int __init xr21v141x_init(void) +static int __init acm_init(void) { int retval; - xr21v141x_tty_driver = alloc_tty_driver(XR21V141X_TTY_MINORS); - if (!xr21v141x_tty_driver) { - printk(KERN_INFO KBUILD_MODNAME ": alloc_tty_driver(%d) failed\n", XR21V141X_TTY_MINORS); + acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); + if (!acm_tty_driver) return -ENOMEM; - } - xr21v141x_tty_driver->driver_name = "vizzini", - xr21v141x_tty_driver->name = "ttyVIZ", - xr21v141x_tty_driver->major = 0, // Dynamically allocate the major number - xr21v141x_tty_driver->minor_start = 0, - xr21v141x_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, - xr21v141x_tty_driver->subtype = SERIAL_TYPE_NORMAL, - xr21v141x_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - xr21v141x_tty_driver->init_termios = tty_std_termios; - xr21v141x_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | + acm_tty_driver->driver_name = "vizzini", + acm_tty_driver->name = "ttyVIZ", + acm_tty_driver->major = 0, // Dynamically allocate the major number + acm_tty_driver->minor_start = 0, + acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, + acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, + acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + acm_tty_driver->init_termios = tty_std_termios; + acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty_set_operations(xr21v141x_tty_driver, &xr21v141x_ops); + tty_set_operations(acm_tty_driver, &acm_ops); - retval = tty_register_driver(xr21v141x_tty_driver); + retval = tty_register_driver(acm_tty_driver); if (retval) { - printk(KERN_INFO KBUILD_MODNAME ": tty_register_driver failed\n"); - put_tty_driver(xr21v141x_tty_driver); + put_tty_driver(acm_tty_driver); return retval; } - retval = usb_register(&xr21v141x_driver); + retval = usb_register(&acm_driver); if (retval) { - printk(KERN_INFO KBUILD_MODNAME ": usb_register failed\n"); - tty_unregister_driver(xr21v141x_tty_driver); - put_tty_driver(xr21v141x_tty_driver); + tty_unregister_driver(acm_tty_driver); + put_tty_driver(acm_tty_driver); return retval; } @@ -1889,16 +2057,18 @@ static int __init xr21v141x_init(void) return 0; } -static void __exit xr21v141x_exit(void) +static void __exit acm_exit(void) { - usb_deregister(&xr21v141x_driver); - tty_unregister_driver(xr21v141x_tty_driver); - put_tty_driver(xr21v141x_tty_driver); + usb_deregister(&acm_driver); + tty_unregister_driver(acm_tty_driver); + put_tty_driver(acm_tty_driver); + idr_destroy(&acm_minors); } -module_init(xr21v141x_init); -module_exit(xr21v141x_exit); +module_init(acm_init); +module_exit(acm_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR); diff --git a/vizzini.h b/vizzini.h index 9fe8638..268d4bb 100644 --- a/vizzini.h +++ b/vizzini.h @@ -195,12 +195,26 @@ * Major and minor numbers. */ -#define XR21V141X_TTY_MINORS 32 +#define ACM_TTY_MAJOR 166 +#define ACM_TTY_MINORS 256 + +/* + * Requests. + */ #define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE) +/* + * Output control lines. + */ + #define ACM_CTRL_DTR 0x01 #define ACM_CTRL_RTS 0x02 + +/* + * Input control lines and line errors. + */ + #define ACM_CTRL_DCD 0x01 #define ACM_CTRL_DSR 0x02 #define ACM_CTRL_BRK 0x04 @@ -214,27 +228,35 @@ * Internal driver structures. */ +/* + * The only reason to have several buffers is to accommodate assumptions + * in line disciplines. They ask for empty space amount, receive our URB size, + * and proceed to issue several 1-character writes, assuming they will fit. + * The very first write takes a complete URB. Fortunately, this only happens + * when processing onlcr, so we only need 2 buffers. These values must be + * powers of 2. + */ #define ACM_NW 16 #define ACM_NR 16 -struct xr21v141x_wb { +struct acm_wb { unsigned char *buf; dma_addr_t dmah; int len; int use; struct urb *urb; - struct xr21v141x *instance; + struct acm *instance; }; -struct xr21v141x_rb { +struct acm_rb { int size; unsigned char *base; dma_addr_t dma; int index; - struct xr21v141x *instance; + struct acm *instance; }; -struct xr21v141x { +struct acm { struct usb_device *dev; /* the corresponding usb device */ struct usb_interface *control; /* control interface */ struct usb_interface *data; /* data interface */ @@ -245,10 +267,10 @@ struct xr21v141x { u8 *country_codes; /* country codes from device */ unsigned int country_code_size; /* size of this buffer */ unsigned int country_rel_date; /* release date of version */ - struct xr21v141x_wb wb[ACM_NW]; + struct acm_wb wb[ACM_NW]; unsigned long read_urbs_free; struct urb *read_urbs[ACM_NR]; - struct xr21v141x_rb read_buffers[ACM_NR]; + struct acm_rb read_buffers[ACM_NR]; int rx_buflimit; int rx_endpoint; spinlock_t read_lock; @@ -261,6 +283,9 @@ struct xr21v141x { struct work_struct work; /* work queue entry for line discipline waking up */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ + struct async_icount iocount; /* counters for control line changes */ + struct async_icount oldcount; /* for comparison of counter */ + wait_queue_head_t wioctl; /* for ioctl */ unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ unsigned int minor; /* acm minor number */ @@ -272,8 +297,9 @@ struct xr21v141x { unsigned int throttled:1; /* actually throttled */ unsigned int throttle_req:1; /* throttle requested */ u8 bInterval; - struct xr21v141x_wb *delayed_wb; /* write queued for a device about to be woken - */ + struct usb_anchor delayed; /* writes queued for a device about to be woken */ + unsigned long quirks; + int block; int preciseflags; /* USB: wide mode, TTY: flags per character */ int trans9; /* USB: wide mode, serial 9N1 */ @@ -285,11 +311,14 @@ struct xr21v141x { }; #define CDC_DATA_INTERFACE_TYPE 0x0a + /* constants describing various quirks and errors */ -#define NO_UNION_NORMAL 1 -#define SINGLE_RX_URB 2 -#define NO_CAP_LINE 4 -#define NOT_A_MODEM 8 -#define NO_DATA_INTERFACE 16 +#define NO_UNION_NORMAL BIT(0) +#define SINGLE_RX_URB BIT(1) +#define NO_CAP_LINE BIT(2) +#define NO_DATA_INTERFACE BIT(4) +#define IGNORE_DEVICE BIT(5) +#define QUIRK_CONTROL_LINE_STATE BIT(6) +#define CLEAR_HALT_CONDITIONS BIT(7) #endif /*VIZZINI_H*/