diff options
Diffstat (limited to 'drivers/usb/class')
| -rw-r--r-- | drivers/usb/class/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-acm.c | 1266 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-acm.h | 41 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-wdm.c | 562 | ||||
| -rw-r--r-- | drivers/usb/class/usblp.c | 105 | ||||
| -rw-r--r-- | drivers/usb/class/usbtmc.c | 366 | 
6 files changed, 1419 insertions, 927 deletions
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index 2519e320098..bb8b73682a7 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -2,11 +2,10 @@  # USB Class driver configuration  #  comment "USB Device Class drivers" -	depends on USB  config USB_ACM  	tristate "USB Modem (CDC ACM) support" -	depends on USB +	depends on TTY  	---help---  	  This driver supports USB modems and ISDN adapters which support the  	  Communication Device Class Abstract Control Model interface. @@ -21,7 +20,6 @@ config USB_ACM  config USB_PRINTER  	tristate "USB Printer support" -	depends on USB  	help  	  Say Y here if you want to connect a USB printer to your computer's  	  USB port. @@ -31,7 +29,6 @@ config USB_PRINTER  config USB_WDM  	tristate "USB Wireless Device Management support" -	depends on USB  	---help---  	  This driver supports the WMC Device Management functionality  	  of cell phones compliant to the CDC WMC specification. You can use @@ -42,7 +39,6 @@ config USB_WDM  config USB_TMC  	tristate "USB Test and Measurement Class support" -	depends on USB  	help  	  Say Y here if you want to connect a USB device that follows  	  the USB.org specification for USB Test and Measurement devices diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d6ede989ff2..e934e19f49f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -7,35 +7,12 @@   * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>   * Copyright (c) 2004 Oliver Neukum	<oliver@neukum.name>   * Copyright (c) 2005 David Kubicek	<dave@awk.cz> + * Copyright (c) 2011 Johan Hovold	<jhovold@gmail.com>   *   * USB Abstract Control Model driver for USB modems and ISDN adapters   *   * Sponsored by SuSE   * - * ChangeLog: - *	v0.9  - thorough cleaning, URBification, almost a rewrite - *	v0.10 - some more cleanups - *	v0.11 - fixed flow control, read error doesn't stop reads - *	v0.12 - added TIOCM ioctls, added break handling, made struct acm - *		kmalloced - *	v0.13 - added termios, added hangup - *	v0.14 - sized down struct acm - *	v0.15 - fixed flow control again - characters could be lost - *	v0.16 - added code for modems with swapped data and control interfaces - *	v0.17 - added new style probing - *	v0.18 - fixed new style probing for devices with more configurations - *	v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan) - *	v0.20 - switched to probing on interface (rather than device) class - *	v0.21 - revert to probing on device for devices with multiple configs - *	v0.22 - probe only the control interface. if usbcore doesn't choose the - *		config we want, sysadmin changes bConfigurationValue in sysfs. - *	v0.23 - use softirq for rx processing, as needed by tty layer - *	v0.24 - change probe method to evaluate CDC union descriptor - *	v0.25 - downstream tasks paralelized to maximize throughput - *	v0.26 - multiple write urbs, writesize increased - */ - -/*   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by   * the Free Software Foundation; either version 2 of the License, or @@ -74,31 +51,69 @@  #include "cdc-acm.h" -#define ACM_CLOSE_TIMEOUT	15	/* seconds to let writes drain */ - -/* - * Version Information - */ -#define DRIVER_VERSION "v0.26" -#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" +#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold"  #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"  static struct usb_driver acm_driver;  static struct tty_driver *acm_tty_driver;  static struct acm *acm_table[ACM_TTY_MINORS]; -static DEFINE_MUTEX(open_mutex); +static DEFINE_MUTEX(acm_table_lock); -#define ACM_READY(acm)	(acm && acm->dev && acm->port.count) +/* + * acm_table accessors + */ -static const struct tty_port_operations acm_port_ops = { -}; +/* + * Look up an ACM structure by index. If found and not disconnected, increment + * its refcount and return it with its mutex held. + */ +static struct acm *acm_get_by_index(unsigned index) +{ +	struct acm *acm; -#ifdef VERBOSE_DEBUG -#define verbose	1 -#else -#define verbose	0 -#endif +	mutex_lock(&acm_table_lock); +	acm = acm_table[index]; +	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_table_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_table_lock); +	for (minor = 0; minor < ACM_TTY_MINORS; minor++) { +		if (!acm_table[minor]) { +			acm_table[minor] = acm; +			break; +		} +	} +	mutex_unlock(&acm_table_lock); + +	return minor; +} + +/* Release the minor number associated with 'acm'.  */ +static void acm_release_minor(struct acm *acm) +{ +	mutex_lock(&acm_table_lock); +	acm_table[acm->minor] = NULL; +	mutex_unlock(&acm_table_lock); +}  /*   * Functions for ACM control messages. @@ -107,12 +122,23 @@ static const struct tty_port_operations acm_port_ops = {  static int acm_ctrl_msg(struct acm *acm, int request, int value,  							void *buf, int len)  { -	int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->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,  		acm->control->altsetting[0].desc.bInterfaceNumber,  		buf, len, 5000); -	dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", -						request, value, len, retval); + +	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;  } @@ -192,43 +218,14 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)  	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);  	if (rc < 0) { -		dbg("usb_submit_urb(write bulk) failed: %d", rc); +		dev_err(&acm->data->dev, +			"%s - usb_submit_urb(write bulk) failed: %d\n", +			__func__, rc);  		acm_write_done(acm, wb);  	}  	return rc;  } -static int acm_write_start(struct acm *acm, int wbn) -{ -	unsigned long flags; -	struct acm_wb *wb = &acm->wb[wbn]; -	int rc; - -	spin_lock_irqsave(&acm->write_lock, flags); -	if (!acm->dev) { -		wb->use = 0; -		spin_unlock_irqrestore(&acm->write_lock, flags); -		return -ENODEV; -	} - -	dbg("%s susp_count: %d", __func__, acm->susp_count); -	usb_autopm_get_interface_async(acm->control); -	if (acm->susp_count) { -		if (!acm->delayed_wb) -			acm->delayed_wb = wb; -		else -			usb_autopm_put_interface_async(acm->control); -		spin_unlock_irqrestore(&acm->write_lock, flags); -		return 0;	/* A white lie */ -	} -	usb_mark_last_busy(acm->dev); - -	rc = acm_start_wb(acm, wb); -	spin_unlock_irqrestore(&acm->write_lock, flags); - -	return rc; - -}  /*   * attributes exported through sysfs   */ @@ -273,9 +270,9 @@ static void acm_ctrl_irq(struct urb *urb)  {  	struct acm *acm = urb->context;  	struct usb_cdc_notification *dr = urb->transfer_buffer; -	struct tty_struct *tty;  	unsigned char *data;  	int newctrl; +	int difference;  	int retval;  	int status = urb->status; @@ -287,218 +284,158 @@ static void acm_ctrl_irq(struct urb *urb)  	case -ENOENT:  	case -ESHUTDOWN:  		/* this urb is terminated, clean up */ -		dbg("%s - urb shutting down with status: %d", __func__, status); +		dev_dbg(&acm->control->dev, +				"%s - urb shutting down with status: %d\n", +				__func__, status);  		return;  	default: -		dbg("%s - nonzero urb status received: %d", __func__, status); +		dev_dbg(&acm->control->dev, +				"%s - nonzero urb status received: %d\n", +				__func__, status);  		goto exit;  	} -	if (!ACM_READY(acm)) -		goto exit; +	usb_mark_last_busy(acm->dev);  	data = (unsigned char *)(dr + 1);  	switch (dr->bNotificationType) {  	case USB_CDC_NOTIFY_NETWORK_CONNECTION: -		dbg("%s network", dr->wValue ? -					"connected to" : "disconnected from"); +		dev_dbg(&acm->control->dev, "%s - network connection: %d\n", +							__func__, dr->wValue);  		break;  	case USB_CDC_NOTIFY_SERIAL_STATE: -		tty = tty_port_tty_get(&acm->port);  		newctrl = get_unaligned_le16(data); -		if (tty) { -			if (!acm->clocal && -				(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { -				dbg("calling hangup"); -				tty_hangup(tty); -			} -			tty_kref_put(tty); +		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);  		} +		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); -		dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", -			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', -			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', -			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', -			acm->ctrlin & ACM_CTRL_RI  ? '+' : '-', -			acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', -			acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', -			acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); -			break; +		if (difference) +			wake_up_all(&acm->wioctl); + +		break;  	default: -		dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", +		dev_dbg(&acm->control->dev, +			"%s - unknown notification %d received: index %d " +			"len %d data0 %d data1 %d\n", +			__func__,  			dr->bNotificationType, dr->wIndex,  			dr->wLength, data[0], data[1]);  		break;  	}  exit: -	usb_mark_last_busy(acm->dev);  	retval = usb_submit_urb(urb, GFP_ATOMIC);  	if (retval) -		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " -			"result %d", __func__, retval); +		dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n", +							__func__, retval);  } -/* data interface returns incoming bytes, or we got unthrottled */ -static void acm_read_bulk(struct urb *urb) +static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)  { -	struct acm_rb *buf; -	struct acm_ru *rcv = urb->context; -	struct acm *acm = rcv->instance; -	int status = urb->status; +	int res; -	dbg("Entering acm_read_bulk with status %d", status); +	if (!test_and_clear_bit(index, &acm->read_urbs_free)) +		return 0; -	if (!ACM_READY(acm)) { -		dev_dbg(&acm->data->dev, "Aborting, acm not ready"); -		return; +	dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index); + +	res = usb_submit_urb(acm->read_urbs[index], mem_flags); +	if (res) { +		if (res != -EPERM) { +			dev_err(&acm->data->dev, +					"%s - usb_submit_urb failed: %d\n", +					__func__, res); +		} +		set_bit(index, &acm->read_urbs_free); +		return res;  	} -	usb_mark_last_busy(acm->dev); -	if (status) -		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); +	return 0; +} -	buf = rcv->buffer; -	buf->size = urb->actual_length; +static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags) +{ +	int res; +	int i; -	if (likely(status == 0)) { -		spin_lock(&acm->read_lock); -		acm->processing++; -		list_add_tail(&rcv->list, &acm->spare_read_urbs); -		list_add_tail(&buf->list, &acm->filled_read_bufs); -		spin_unlock(&acm->read_lock); -	} else { -		/* we drop the buffer due to an error */ -		spin_lock(&acm->read_lock); -		list_add_tail(&rcv->list, &acm->spare_read_urbs); -		list_add(&buf->list, &acm->spare_read_bufs); -		spin_unlock(&acm->read_lock); -		/* nevertheless the tasklet must be kicked unconditionally -		so the queue cannot dry up */ +	for (i = 0; i < acm->rx_buflimit; ++i) { +		res = acm_submit_read_urb(acm, i, mem_flags); +		if (res) +			return res;  	} -	if (likely(!acm->susp_count)) -		tasklet_schedule(&acm->urb_task); + +	return 0;  } -static void acm_rx_tasklet(unsigned long _acm) +static void acm_process_read_urb(struct acm *acm, struct urb *urb)  { -	struct acm *acm = (void *)_acm; -	struct acm_rb *buf; -	struct tty_struct *tty; -	struct acm_ru *rcv; +	if (!urb->actual_length) +		return; + +	tty_insert_flip_string(&acm->port, urb->transfer_buffer, +			urb->actual_length); +	tty_flip_buffer_push(&acm->port); +} + +static void acm_read_bulk_callback(struct urb *urb) +{ +	struct acm_rb *rb = urb->context; +	struct acm *acm = rb->instance;  	unsigned long flags; -	unsigned char throttled; -	dbg("Entering acm_rx_tasklet"); +	dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, +					rb->index, urb->actual_length); +	set_bit(rb->index, &acm->read_urbs_free); -	if (!ACM_READY(acm)) { -		dbg("acm_rx_tasklet: ACM not ready"); +	if (!acm->dev) { +		dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);  		return;  	} -	spin_lock_irqsave(&acm->throttle_lock, flags); -	throttled = acm->throttle; -	spin_unlock_irqrestore(&acm->throttle_lock, flags); -	if (throttled) { -		dbg("acm_rx_tasklet: throttled"); +	if (urb->status) { +		dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", +							__func__, urb->status);  		return;  	} -	tty = tty_port_tty_get(&acm->port); - -next_buffer: -	spin_lock_irqsave(&acm->read_lock, flags); -	if (list_empty(&acm->filled_read_bufs)) { -		spin_unlock_irqrestore(&acm->read_lock, flags); -		goto urbs; -	} -	buf = list_entry(acm->filled_read_bufs.next, -			 struct acm_rb, list); -	list_del(&buf->list); -	spin_unlock_irqrestore(&acm->read_lock, flags); - -	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); +	usb_mark_last_busy(acm->dev); -	if (tty) { -		spin_lock_irqsave(&acm->throttle_lock, flags); -		throttled = acm->throttle; -		spin_unlock_irqrestore(&acm->throttle_lock, flags); -		if (!throttled) { -			tty_insert_flip_string(tty, buf->base, buf->size); -			tty_flip_buffer_push(tty); -		} else { -			tty_kref_put(tty); -			dbg("Throttling noticed"); -			spin_lock_irqsave(&acm->read_lock, flags); -			list_add(&buf->list, &acm->filled_read_bufs); -			spin_unlock_irqrestore(&acm->read_lock, flags); -			return; -		} -	} +	acm_process_read_urb(acm, urb); +	/* throttle device if requested by tty */  	spin_lock_irqsave(&acm->read_lock, flags); -	list_add(&buf->list, &acm->spare_read_bufs); -	spin_unlock_irqrestore(&acm->read_lock, flags); -	goto next_buffer; - -urbs: -	tty_kref_put(tty); - -	while (!list_empty(&acm->spare_read_bufs)) { -		spin_lock_irqsave(&acm->read_lock, flags); -		if (list_empty(&acm->spare_read_urbs)) { -			acm->processing = 0; -			spin_unlock_irqrestore(&acm->read_lock, flags); -			return; -		} -		rcv = list_entry(acm->spare_read_urbs.next, -				 struct acm_ru, list); -		list_del(&rcv->list); +	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(&acm->read_lock, flags); - -		buf = list_entry(acm->spare_read_bufs.next, -				 struct acm_rb, list); -		list_del(&buf->list); - -		rcv->buffer = buf; - -		if (acm->is_int_ep) -			usb_fill_int_urb(rcv->urb, acm->dev, -					 acm->rx_endpoint, -					 buf->base, -					 acm->readsize, -					 acm_read_bulk, rcv, acm->bInterval); -		else -			usb_fill_bulk_urb(rcv->urb, acm->dev, -					  acm->rx_endpoint, -					  buf->base, -					  acm->readsize, -					  acm_read_bulk, rcv); -		rcv->urb->transfer_dma = buf->dma; -		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - -		/* This shouldn't kill the driver as unsuccessful URBs are -		   returned to the free-urbs-pool and resubmited ASAP */ -		spin_lock_irqsave(&acm->read_lock, flags); -		if (acm->susp_count || -				usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { -			list_add(&buf->list, &acm->spare_read_bufs); -			list_add(&rcv->list, &acm->spare_read_urbs); -			acm->processing = 0; -			spin_unlock_irqrestore(&acm->read_lock, flags); -			return; -		} else { -			spin_unlock_irqrestore(&acm->read_lock, flags); -			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); -		}  	} -	spin_lock_irqsave(&acm->read_lock, flags); -	acm->processing = 0; -	spin_unlock_irqrestore(&acm->read_lock, flags);  }  /* data interface wrote those outgoing bytes */ @@ -508,9 +445,9 @@ static void acm_write_bulk(struct urb *urb)  	struct acm *acm = wb->instance;  	unsigned long flags; -	if (verbose || urb->status -			|| (urb->actual_length != urb->transfer_buffer_length)) -		dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", +	if (urb->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); @@ -518,170 +455,203 @@ static void acm_write_bulk(struct urb *urb)  	spin_lock_irqsave(&acm->write_lock, flags);  	acm_write_done(acm, wb);  	spin_unlock_irqrestore(&acm->write_lock, flags); -	if (ACM_READY(acm)) -		schedule_work(&acm->work); -	else -		wake_up_interruptible(&acm->drain_wait); +	schedule_work(&acm->work);  }  static void acm_softint(struct work_struct *work)  {  	struct acm *acm = container_of(work, struct acm, work); -	struct tty_struct *tty; -	dev_vdbg(&acm->data->dev, "tx work\n"); -	if (!ACM_READY(acm)) -		return; -	tty = tty_port_tty_get(&acm->port); -	tty_wakeup(tty); -	tty_kref_put(tty); +	dev_vdbg(&acm->data->dev, "%s\n", __func__); + +	tty_port_tty_wakeup(&acm->port);  }  /*   * TTY handlers   */ -static int acm_tty_open(struct tty_struct *tty, struct file *filp) +static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)  {  	struct acm *acm; -	int rv = -ENODEV; -	int i; -	dbg("Entering acm_tty_open."); +	int retval; -	mutex_lock(&open_mutex); +	dev_dbg(tty->dev, "%s\n", __func__); -	acm = acm_table[tty->index]; -	if (!acm || !acm->dev) -		goto out; -	else -		rv = 0; +	acm = acm_get_by_index(tty->index); +	if (!acm) +		return -ENODEV; -	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); +	retval = tty_standard_install(driver, tty); +	if (retval) +		goto error_init_termios;  	tty->driver_data = acm; -	tty_port_tty_set(&acm->port, tty); -	if (usb_autopm_get_interface(acm->control) < 0) -		goto early_bail; +	return 0; + +error_init_termios: +	tty_port_put(&acm->port); +	return retval; +} + +static int acm_tty_open(struct tty_struct *tty, struct file *filp) +{ +	struct acm *acm = tty->driver_data; + +	dev_dbg(tty->dev, "%s\n", __func__); + +	return tty_port_open(&acm->port, tty, filp); +} + +static void acm_port_dtr_rts(struct tty_port *port, int raise) +{ +	struct acm *acm = container_of(port, struct acm, port); +	int val; +	int res; + +	if (raise) +		val = ACM_CTRL_DTR | ACM_CTRL_RTS;  	else -		acm->control->needs_remote_wakeup = 1; +		val = 0; -	mutex_lock(&acm->mutex); -	if (acm->port.count++) { -		mutex_unlock(&acm->mutex); -		usb_autopm_put_interface(acm->control); -		goto out; -	} +	/* FIXME: add missing ctrlout locking throughout driver */ +	acm->ctrlout = val; -	acm->ctrlurb->dev = acm->dev; -	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { -		dbg("usb_submit_urb(ctrl irq) failed"); -		goto bail_out; -	} +	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"); +} -	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && -	    (acm->ctrl_caps & USB_CDC_CAP_LINE)) -		goto full_bailout; +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; -	usb_autopm_put_interface(acm->control); +	dev_dbg(&acm->control->dev, "%s\n", __func__); -	INIT_LIST_HEAD(&acm->spare_read_urbs); -	INIT_LIST_HEAD(&acm->spare_read_bufs); -	INIT_LIST_HEAD(&acm->filled_read_bufs); +	mutex_lock(&acm->mutex); +	if (acm->disconnected) +		goto disconnected; -	for (i = 0; i < acm->rx_buflimit; i++) -		list_add(&(acm->ru[i].list), &acm->spare_read_urbs); -	for (i = 0; i < acm->rx_buflimit; i++) -		list_add(&(acm->rb[i].list), &acm->spare_read_bufs); +	retval = usb_autopm_get_interface(acm->control); +	if (retval) +		goto error_get_interface; -	acm->throttle = 0; +	/* +	 * FIXME: Why do we need this? Allocating 64K of physically contiguous +	 * memory is really nasty... +	 */ +	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); +	acm->control->needs_remote_wakeup = 1; -	set_bit(ASYNCB_INITIALIZED, &acm->port.flags); -	rv = tty_port_block_til_ready(&acm->port, tty, filp); -	tasklet_schedule(&acm->urb_task); +	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__); +		goto error_submit_urb; +	} + +	/* +	 * Unthrottle device in case the TTY was closed while throttled. +	 */ +	spin_lock_irq(&acm->read_lock); +	acm->throttled = 0; +	acm->throttle_req = 0; +	spin_unlock_irq(&acm->read_lock); + +	retval = acm_submit_read_urbs(acm, GFP_KERNEL); +	if (retval) +		goto error_submit_read_urbs; + +	usb_autopm_put_interface(acm->control);  	mutex_unlock(&acm->mutex); -out: -	mutex_unlock(&open_mutex); -	return rv; -full_bailout: +	return 0; + +error_submit_read_urbs: +	for (i = 0; i < acm->rx_buflimit; i++) +		usb_kill_urb(acm->read_urbs[i]);  	usb_kill_urb(acm->ctrlurb); -bail_out: -	acm->port.count--; -	mutex_unlock(&acm->mutex); +error_submit_urb:  	usb_autopm_put_interface(acm->control); -early_bail: -	mutex_unlock(&open_mutex); -	tty_port_tty_set(&acm->port, NULL); -	return -EIO; +error_get_interface: +disconnected: +	mutex_unlock(&acm->mutex); + +	return usb_translate_errors(retval);  } -static void acm_tty_unregister(struct acm *acm) +static void acm_port_destruct(struct tty_port *port)  { -	int i, nr; +	struct acm *acm = container_of(port, struct acm, port); -	nr = acm->rx_buflimit; -	tty_unregister_device(acm_tty_driver, acm->minor); +	dev_dbg(&acm->control->dev, "%s\n", __func__); + +	acm_release_minor(acm);  	usb_put_intf(acm->control); -	acm_table[acm->minor] = NULL; -	usb_free_urb(acm->ctrlurb); -	for (i = 0; i < ACM_NW; i++) -		usb_free_urb(acm->wb[i].urb); -	for (i = 0; i < nr; i++) -		usb_free_urb(acm->ru[i].urb);  	kfree(acm->country_codes);  	kfree(acm);  } -static int acm_tty_chars_in_buffer(struct tty_struct *tty); - -static void acm_port_down(struct acm *acm) +static void acm_port_shutdown(struct tty_port *port)  { -	int i, nr = acm->rx_buflimit; -	mutex_lock(&open_mutex); -	if (acm->dev) { -		usb_autopm_get_interface(acm->control); -		acm_set_control(acm, acm->ctrlout = 0); -		usb_kill_urb(acm->ctrlurb); -		for (i = 0; i < ACM_NW; i++) -			usb_kill_urb(acm->wb[i].urb); -		for (i = 0; i < nr; i++) -			usb_kill_urb(acm->ru[i].urb); -		acm->control->needs_remote_wakeup = 0; -		usb_autopm_put_interface(acm->control); +	struct acm *acm = container_of(port, struct acm, port); +	struct urb *urb; +	struct acm_wb *wb; +	int i; + +	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(&open_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 acm_tty_cleanup(struct tty_struct *tty) +{ +	struct acm *acm = tty->driver_data; +	dev_dbg(&acm->control->dev, "%s\n", __func__); +	tty_port_put(&acm->port);  }  static void acm_tty_hangup(struct tty_struct *tty)  {  	struct acm *acm = tty->driver_data; +	dev_dbg(&acm->control->dev, "%s\n", __func__);  	tty_port_hangup(&acm->port); -	acm_port_down(acm);  }  static void acm_tty_close(struct tty_struct *tty, struct file *filp)  {  	struct acm *acm = tty->driver_data; - -	/* Perform the closing process and see if we need to do the hardware -	   shutdown */ -	if (!acm) -		return; -	if (tty_port_close_start(&acm->port, tty, filp) == 0) { -		mutex_lock(&open_mutex); -		if (!acm->dev) { -			tty_port_tty_set(&acm->port, NULL); -			acm_tty_unregister(acm); -			tty->driver_data = NULL; -		} -		mutex_unlock(&open_mutex); -		return; -	} -	acm_port_down(acm); -	tty_port_close_end(&acm->port, tty); -	tty_port_tty_set(&acm->port, NULL); +	dev_dbg(&acm->control->dev, "%s\n", __func__); +	tty_port_close(&acm->port, tty, filp);  }  static int acm_tty_write(struct tty_struct *tty, @@ -693,13 +663,11 @@ static int acm_tty_write(struct tty_struct *tty,  	int wbn;  	struct acm_wb *wb; -	dbg("Entering acm_tty_write to write %d bytes,", count); - -	if (!ACM_READY(acm)) -		return -EINVAL;  	if (!count)  		return 0; +	dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count); +  	spin_lock_irqsave(&acm->write_lock, flags);  	wbn = acm_wb_alloc(acm);  	if (wbn < 0) { @@ -708,13 +676,33 @@ static int acm_tty_write(struct tty_struct *tty,  	}  	wb = &acm->wb[wbn]; +	if (!acm->dev) { +		wb->use = 0; +		spin_unlock_irqrestore(&acm->write_lock, flags); +		return -ENODEV; +	} +  	count = (count > acm->writesize) ? acm->writesize : count; -	dbg("Get %d bytes...", count); +	dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);  	memcpy(wb->buf, buf, count);  	wb->len = count; + +	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; +	} + +	stat = acm_start_wb(acm, wb);  	spin_unlock_irqrestore(&acm->write_lock, flags); -	stat = acm_write_start(acm, wbn);  	if (stat < 0)  		return stat;  	return count; @@ -723,8 +711,6 @@ static int acm_tty_write(struct tty_struct *tty,  static int acm_tty_write_room(struct tty_struct *tty)  {  	struct acm *acm = tty->driver_data; -	if (!ACM_READY(acm)) -		return -EINVAL;  	/*  	 * Do not let the line discipline to know that we have a reserve,  	 * or it might get too enthusiastic. @@ -735,7 +721,11 @@ static int acm_tty_write_room(struct tty_struct *tty)  static int acm_tty_chars_in_buffer(struct tty_struct *tty)  {  	struct acm *acm = tty->driver_data; -	if (!ACM_READY(acm)) +	/* +	 * if the device was unplugged then any remaining characters fell out +	 * of the connector ;) +	 */ +	if (acm->disconnected)  		return 0;  	/*  	 * This is inaccurate (overcounts), but it works. @@ -746,43 +736,43 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty)  static void acm_tty_throttle(struct tty_struct *tty)  {  	struct acm *acm = tty->driver_data; -	if (!ACM_READY(acm)) -		return; -	spin_lock_bh(&acm->throttle_lock); -	acm->throttle = 1; -	spin_unlock_bh(&acm->throttle_lock); + +	spin_lock_irq(&acm->read_lock); +	acm->throttle_req = 1; +	spin_unlock_irq(&acm->read_lock);  }  static void acm_tty_unthrottle(struct tty_struct *tty)  {  	struct acm *acm = tty->driver_data; -	if (!ACM_READY(acm)) -		return; -	spin_lock_bh(&acm->throttle_lock); -	acm->throttle = 0; -	spin_unlock_bh(&acm->throttle_lock); -	tasklet_schedule(&acm->urb_task); +	unsigned int was_throttled; + +	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) +		acm_submit_read_urbs(acm, GFP_KERNEL);  }  static int acm_tty_break_ctl(struct tty_struct *tty, int state)  {  	struct acm *acm = tty->driver_data;  	int retval; -	if (!ACM_READY(acm)) -		return -EINVAL; +  	retval = acm_send_break(acm, state ? 0xffff : 0);  	if (retval < 0) -		dbg("send break failed"); +		dev_dbg(&acm->control->dev, "%s - send break failed\n", +								__func__);  	return retval;  } -static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) +static int acm_tty_tiocmget(struct tty_struct *tty)  {  	struct acm *acm = tty->driver_data; -	if (!ACM_READY(acm)) -		return -EINVAL; -  	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) | @@ -791,15 +781,12 @@ static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)  	       TIOCM_CTS;  } -static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, +static int acm_tty_tiocmset(struct tty_struct *tty,  			    unsigned int set, unsigned int clear)  {  	struct acm *acm = tty->driver_data;  	unsigned int newctrl; -	if (!ACM_READY(acm)) -		return -EINVAL; -  	newctrl = acm->ctrlout;  	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |  					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0); @@ -813,46 +800,183 @@ static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,  	return acm_set_control(acm, acm->ctrlout = newctrl);  } -static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, -					unsigned int cmd, unsigned long arg) +static int get_serial_info(struct acm *acm, struct serial_struct __user *info)  { -	struct acm *acm = tty->driver_data; +	struct serial_struct tmp; -	if (!ACM_READY(acm)) +	if (!info)  		return -EINVAL; -	return -ENOIOCTLCMD; +	memset(&tmp, 0, sizeof(tmp)); +	tmp.flags = ASYNC_LOW_LATENCY; +	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 : +				acm->port.closing_wait / 10; + +	if (copy_to_user(info, &tmp, sizeof(tmp))) +		return -EFAULT; +	else +		return 0;  } -static const __u32 acm_tty_speed[] = { -	0, 50, 75, 110, 134, 150, 200, 300, 600, -	1200, 1800, 2400, 4800, 9600, 19200, 38400, -	57600, 115200, 230400, 460800, 500000, 576000, -	921600, 1000000, 1152000, 1500000, 2000000, -	2500000, 3000000, 3500000, 4000000 -}; +static int set_serial_info(struct acm *acm, +				struct serial_struct __user *newinfo) +{ +	struct serial_struct new_serial; +	unsigned int closing_wait, close_delay; +	int retval = 0; -static const __u8 acm_tty_size[] = { -	5, 6, 7, 8 -}; +	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) +		return -EFAULT; + +	close_delay = new_serial.close_delay * 10; +	closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? +			ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + +	mutex_lock(&acm->port.mutex); + +	if (!capable(CAP_SYS_ADMIN)) { +		if ((close_delay != acm->port.close_delay) || +		    (closing_wait != acm->port.closing_wait)) +			retval = -EPERM; +		else +			retval = -EOPNOTSUPP; +	} else { +		acm->port.close_delay  = close_delay; +		acm->port.closing_wait = closing_wait; +	} + +	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; + +	if (copy_to_user(count, &icount, sizeof(icount)) > 0) +		rv = -EFAULT; + +	return rv; +} + +static int acm_tty_ioctl(struct tty_struct *tty, +					unsigned int cmd, unsigned long arg) +{ +	struct acm *acm = tty->driver_data; +	int rv = -ENOIOCTLCMD; + +	switch (cmd) { +	case TIOCGSERIAL: /* gets serial port data */ +		rv = get_serial_info(acm, (struct serial_struct __user *) arg); +		break; +	case TIOCSSERIAL: +		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; +	} + +	return rv; +}  static void acm_tty_set_termios(struct tty_struct *tty,  						struct ktermios *termios_old)  {  	struct acm *acm = tty->driver_data; -	struct ktermios *termios = tty->termios; +	struct ktermios *termios = &tty->termios;  	struct usb_cdc_line_coding newline;  	int newctrl = acm->ctrlout; -	if (!ACM_READY(acm)) -		return; -  	newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));  	newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;  	newline.bParityType = termios->c_cflag & PARENB ?  				(termios->c_cflag & PARODD ? 1 : 2) +  				(termios->c_cflag & CMSPAR ? 2 : 0) : 0; -	newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; +	switch (termios->c_cflag & CSIZE) { +	case CS5: +		newline.bDataBits = 5; +		break; +	case CS6: +		newline.bDataBits = 6; +		break; +	case CS7: +		newline.bDataBits = 7; +		break; +	case CS8: +	default: +		newline.bDataBits = 8; +		break; +	}  	/* FIXME: Needs to clear unsupported bits in the termios */  	acm->clocal = ((termios->c_cflag & CLOCAL) != 0); @@ -867,13 +991,22 @@ static void acm_tty_set_termios(struct tty_struct *tty,  	if (memcmp(&acm->line, &newline, sizeof newline)) {  		memcpy(&acm->line, &newline, sizeof newline); -		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), +		dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n", +			__func__, +			le32_to_cpu(newline.dwDTERate),  			newline.bCharFormat, newline.bParityType,  			newline.bDataBits);  		acm_set_line(acm, &acm->line);  	}  } +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, +}; +  /*   * USB probe and disconnect routines.   */ @@ -892,11 +1025,11 @@ static void acm_write_buffers_free(struct acm *acm)  static void acm_read_buffers_free(struct acm *acm)  {  	struct usb_device *usb_dev = interface_to_usbdev(acm->control); -	int i, n = acm->rx_buflimit; +	int i; -	for (i = 0; i < n; i++) +	for (i = 0; i < acm->rx_buflimit; i++)  		usb_free_coherent(usb_dev, acm->readsize, -				  acm->rb[i].base, acm->rb[i].dma); +			  acm->read_buffers[i].base, acm->read_buffers[i].dma);  }  /* Little helper: write buffers allocate */ @@ -941,14 +1074,20 @@ static int acm_probe(struct usb_interface *intf,  	u8 ac_management_function = 0;  	u8 call_management_function = 0;  	int call_interface_num = -1; -	int data_interface_num; +	int data_interface_num = -1;  	unsigned long quirks;  	int num_rx_buf;  	int i;  	int combined_interfaces = 0; +	struct device *tty_dev; +	int rv = -ENOMEM;  	/* normal quirks */  	quirks = (unsigned long)id->driver_info; + +	if (quirks == IGNORE_DEVICE) +		return -ENODEV; +  	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;  	/* handle quirks deadly to normal probing*/ @@ -1005,7 +1144,7 @@ static int acm_probe(struct usb_interface *intf,  		case USB_CDC_CALL_MANAGEMENT_TYPE:  			call_management_function = buffer[3];  			call_interface_num = buffer[4]; -			if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) +			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: @@ -1025,7 +1164,11 @@ next_desc:  	if (!union_header) {  		if (call_interface_num > 0) {  			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); -			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); +			/* quirks for Droids MuIn LCD */ +			if (quirks & NO_DATA_INTERFACE) +				data_interface = usb_ifnum_to_if(usb_dev, 0); +			else +				data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));  			control_interface = intf;  		} else {  			if (intf->cur_altsetting->desc.bNumEndpoints != 3) { @@ -1109,7 +1252,8 @@ skip_normal_probe:  	} -	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2) +	if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 || +	    control_interface->cur_altsetting->desc.bNumEndpoints == 0)  		return -EINVAL;  	epctrl = &control_interface->cur_altsetting->endpoint[0].desc; @@ -1128,25 +1272,26 @@ skip_normal_probe:  		epwrite = t;  	}  made_compressed_probe: -	dbg("interfaces are valid"); -	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); - -	if (minor == ACM_TTY_MINORS) { -		dev_err(&intf->dev, "no more free acm devices\n"); -		return -ENODEV; -	} +	dev_dbg(&intf->dev, "interfaces are valid\n");  	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);  	if (acm == NULL) { -		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n"); +		dev_err(&intf->dev, "out of memory (acm kzalloc)\n");  		goto alloc_fail;  	} -	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); -	readsize = le16_to_cpu(epread->wMaxPacketSize) * +	minor = acm_alloc_minor(acm); +	if (minor == ACM_TTY_MINORS) { +		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);  	acm->combined_interfaces = combined_interfaces; -	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; +	acm->writesize = usb_endpoint_maxp(epwrite) * 20;  	acm->control = control_interface;  	acm->data = data_interface;  	acm->minor = minor; @@ -1157,11 +1302,8 @@ made_compressed_probe:  	acm->ctrlsize = ctrlsize;  	acm->readsize = readsize;  	acm->rx_buflimit = num_rx_buf; -	acm->urb_task.func = acm_rx_tasklet; -	acm->urb_task.data = (unsigned long) acm;  	INIT_WORK(&acm->work, acm_softint); -	init_waitqueue_head(&acm->drain_wait); -	spin_lock_init(&acm->throttle_lock); +	init_waitqueue_head(&acm->wioctl);  	spin_lock_init(&acm->write_lock);  	spin_lock_init(&acm->read_lock);  	mutex_init(&acm->mutex); @@ -1171,61 +1313,78 @@ made_compressed_probe:  		acm->bInterval = epread->bInterval;  	tty_port_init(&acm->port);  	acm->port.ops = &acm_port_ops; +	init_usb_anchor(&acm->delayed);  	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);  	if (!buf) { -		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n"); +		dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");  		goto alloc_fail2;  	}  	acm->ctrl_buffer = buf;  	if (acm_write_buffers_alloc(acm) < 0) { -		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); +		dev_err(&intf->dev, "out of memory (write buffer alloc)\n");  		goto alloc_fail4;  	}  	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);  	if (!acm->ctrlurb) { -		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); +		dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");  		goto alloc_fail5;  	}  	for (i = 0; i < num_rx_buf; i++) { -		struct acm_ru *rcv = &(acm->ru[i]); +		struct acm_rb *rb = &(acm->read_buffers[i]); +		struct urb *urb; -		rcv->urb = usb_alloc_urb(0, GFP_KERNEL); -		if (rcv->urb == NULL) { -			dev_dbg(&intf->dev, -				"out of memory (read urbs usb_alloc_urb)\n"); +		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");  			goto alloc_fail6;  		} +		rb->index = i; +		rb->instance = acm; -		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -		rcv->instance = acm; -	} -	for (i = 0; i < num_rx_buf; i++) { -		struct acm_rb *rb = &(acm->rb[i]); - -		rb->base = usb_alloc_coherent(acm->dev, readsize, -				GFP_KERNEL, &rb->dma); -		if (!rb->base) { -			dev_dbg(&intf->dev, -				"out of memory (read bufs usb_alloc_coherent)\n"); -			goto alloc_fail7; +		urb = usb_alloc_urb(0, GFP_KERNEL); +		if (!urb) { +			dev_err(&intf->dev, +				"out of memory (read urbs usb_alloc_urb)\n"); +			goto alloc_fail6;  		} +		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; +		urb->transfer_dma = rb->dma; +		if (acm->is_int_ep) { +			usb_fill_int_urb(urb, acm->dev, +					 acm->rx_endpoint, +					 rb->base, +					 acm->readsize, +					 acm_read_bulk_callback, rb, +					 acm->bInterval); +		} else { +			usb_fill_bulk_urb(urb, acm->dev, +					  acm->rx_endpoint, +					  rb->base, +					  acm->readsize, +					  acm_read_bulk_callback, rb); +		} + +		acm->read_urbs[i] = urb; +		__set_bit(i, &acm->read_urbs_free);  	}  	for (i = 0; i < ACM_NW; i++) {  		struct acm_wb *snd = &(acm->wb[i]);  		snd->urb = usb_alloc_urb(0, GFP_KERNEL);  		if (snd->urb == NULL) { -			dev_dbg(&intf->dev, -				"out of memory (write urbs usb_alloc_urb)"); -			goto alloc_fail8; +			dev_err(&intf->dev, +				"out of memory (write urbs usb_alloc_urb)\n"); +			goto alloc_fail7;  		}  		if (usb_endpoint_xfer_int(epwrite))  			usb_fill_int_urb(snd->urb, usb_dev, -				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), +				usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),  				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);  		else  			usb_fill_bulk_urb(snd->urb, usb_dev, @@ -1239,7 +1398,7 @@ made_compressed_probe:  	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);  	if (i < 0) -		goto alloc_fail8; +		goto alloc_fail7;  	if (cfd) { /* export the country data */  		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); @@ -1253,6 +1412,8 @@ made_compressed_probe:  		i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);  		if (i < 0) {  			kfree(acm->country_codes); +			acm->country_codes = NULL; +			acm->country_code_size = 0;  			goto skip_countries;  		} @@ -1261,6 +1422,8 @@ made_compressed_probe:  		if (i < 0) {  			device_remove_file(&intf->dev, &dev_attr_wCountryCodes);  			kfree(acm->country_codes); +			acm->country_codes = NULL; +			acm->country_code_size = 0;  			goto skip_countries;  		}  	} @@ -1270,14 +1433,12 @@ skip_countries:  			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),  			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,  			 /* works around buggy devices */ -			 epctrl->bInterval ? epctrl->bInterval : 0xff); +			 epctrl->bInterval ? epctrl->bInterval : 16);  	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  	acm->ctrlurb->transfer_dma = acm->ctrl_dma;  	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); -	acm_set_control(acm, acm->ctrlout); -  	acm->line.dwDTERate = cpu_to_le32(9600);  	acm->line.bDataBits = 8;  	acm_set_line(acm, &acm->line); @@ -1286,44 +1447,53 @@ skip_countries:  	usb_set_intfdata(data_interface, acm);  	usb_get_intf(control_interface); -	tty_register_device(acm_tty_driver, minor, &control_interface->dev); - -	acm_table[minor] = acm; +	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; +	}  	return 0;  alloc_fail8: +	if (acm->country_codes) { +		device_remove_file(&acm->control->dev, +				&dev_attr_wCountryCodes); +		device_remove_file(&acm->control->dev, +				&dev_attr_iCountryCodeRelDate); +	} +	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(acm->wb[i].urb); -alloc_fail7: -	acm_read_buffers_free(acm);  alloc_fail6:  	for (i = 0; i < num_rx_buf; i++) -		usb_free_urb(acm->ru[i].urb); +		usb_free_urb(acm->read_urbs[i]); +	acm_read_buffers_free(acm);  	usb_free_urb(acm->ctrlurb);  alloc_fail5:  	acm_write_buffers_free(acm);  alloc_fail4:  	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);  alloc_fail2: +	acm_release_minor(acm);  	kfree(acm);  alloc_fail: -	return -ENOMEM; +	return rv;  }  static void stop_data_traffic(struct acm *acm)  {  	int i; -	dbg("Entering stop_data_traffic"); -	tasklet_disable(&acm->urb_task); +	dev_dbg(&acm->control->dev, "%s\n", __func__);  	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->ru[i].urb); - -	tasklet_enable(&acm->urb_task); +		usb_kill_urb(acm->read_urbs[i]);  	cancel_work_sync(&acm->work);  } @@ -1333,46 +1503,52 @@ static void acm_disconnect(struct usb_interface *intf)  	struct acm *acm = usb_get_intfdata(intf);  	struct usb_device *usb_dev = interface_to_usbdev(intf);  	struct tty_struct *tty; +	int i; + +	dev_dbg(&intf->dev, "%s\n", __func__);  	/* sibling interface is already cleaning up */  	if (!acm)  		return; -	mutex_lock(&open_mutex); +	mutex_lock(&acm->mutex); +	acm->disconnected = true;  	if (acm->country_codes) {  		device_remove_file(&acm->control->dev,  				&dev_attr_wCountryCodes);  		device_remove_file(&acm->control->dev,  				&dev_attr_iCountryCodeRelDate);  	} +	wake_up_all(&acm->wioctl);  	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); -	acm->dev = NULL;  	usb_set_intfdata(acm->control, NULL);  	usb_set_intfdata(acm->data, NULL); +	mutex_unlock(&acm->mutex); + +	tty = tty_port_tty_get(&acm->port); +	if (tty) { +		tty_vhangup(tty); +		tty_kref_put(tty); +	}  	stop_data_traffic(acm); +	tty_unregister_device(acm_tty_driver, acm->minor); + +	usb_free_urb(acm->ctrlurb); +	for (i = 0; i < ACM_NW; i++) +		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); +	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); -	if (acm->port.count == 0) { -		acm_tty_unregister(acm); -		mutex_unlock(&open_mutex); -		return; -	} - -	mutex_unlock(&open_mutex); -	tty = tty_port_tty_get(&acm->port); -	if (tty) { -		tty_hangup(tty); -		tty_kref_put(tty); -	} +	tty_port_put(&acm->port);  }  #ifdef CONFIG_PM @@ -1381,66 +1557,44 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)  	struct acm *acm = usb_get_intfdata(intf);  	int cnt; -	if (message.event & PM_EVENT_AUTO) { -		int b; - -		spin_lock_irq(&acm->read_lock); -		spin_lock(&acm->write_lock); -		b = acm->processing + acm->transmitting; -		spin_unlock(&acm->write_lock); -		spin_unlock_irq(&acm->read_lock); -		if (b) +	spin_lock_irq(&acm->write_lock); +	if (PMSG_IS_AUTO(message)) { +		if (acm->transmitting) { +			spin_unlock_irq(&acm->write_lock);  			return -EBUSY; +		}  	} - -	spin_lock_irq(&acm->read_lock); -	spin_lock(&acm->write_lock);  	cnt = acm->susp_count++; -	spin_unlock(&acm->write_lock); -	spin_unlock_irq(&acm->read_lock); +	spin_unlock_irq(&acm->write_lock);  	if (cnt)  		return 0; -	/* -	we treat opened interfaces differently, -	we must guard against open -	*/ -	mutex_lock(&acm->mutex); -	if (acm->port.count) -		stop_data_traffic(acm); +	stop_data_traffic(acm); -	mutex_unlock(&acm->mutex);  	return 0;  }  static int acm_resume(struct usb_interface *intf)  {  	struct acm *acm = usb_get_intfdata(intf); -	struct acm_wb *wb; +	struct urb *urb;  	int rv = 0; -	int cnt; -	spin_lock_irq(&acm->read_lock); -	acm->susp_count -= 1; -	cnt = acm->susp_count; -	spin_unlock_irq(&acm->read_lock); +	spin_lock_irq(&acm->write_lock); -	if (cnt) -		return 0; +	if (--acm->susp_count) +		goto out; -	mutex_lock(&acm->mutex); -	if (acm->port.count) { -		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); +	if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) { +		rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC); -		spin_lock_irq(&acm->write_lock); -		if (acm->delayed_wb) { -			wb = acm->delayed_wb; -			acm->delayed_wb = NULL; -			spin_unlock_irq(&acm->write_lock); -			acm_start_wb(acm, wb); -		} else { -			spin_unlock_irq(&acm->write_lock); +		for (;;) { +			urb = usb_get_from_anchor(&acm->delayed); +			if (!urb) +				break; + +			acm_start_wb(acm, urb->context);  		}  		/* @@ -1448,30 +1602,23 @@ static int acm_resume(struct usb_interface *intf)  		 * do the write path at all cost  		 */  		if (rv < 0) -			goto err_out; +			goto out; -		tasklet_schedule(&acm->urb_task); +		rv = acm_submit_read_urbs(acm, GFP_ATOMIC);  	} +out: +	spin_unlock_irq(&acm->write_lock); -err_out: -	mutex_unlock(&acm->mutex);  	return rv;  }  static int acm_reset_resume(struct usb_interface *intf)  {  	struct acm *acm = usb_get_intfdata(intf); -	struct tty_struct *tty; -	mutex_lock(&acm->mutex); -	if (acm->port.count) { -		tty = tty_port_tty_get(&acm->port); -		if (tty) { -			tty_hangup(tty); -			tty_kref_put(tty); -		} -	} -	mutex_unlock(&acm->mutex); +	if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) +		tty_port_tty_hangup(&acm->port, false); +  	return acm_resume(intf);  } @@ -1493,6 +1640,8 @@ static int acm_reset_resume(struct usb_interface *intf)  static const struct usb_device_id acm_ids[] = {  	/* quirky and broken devices */ +	{ USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */ +	.driver_info = NO_UNION_NORMAL, },/* has no union descriptor */  	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */  	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */  	}, @@ -1534,6 +1683,30 @@ static const struct usb_device_id acm_ids[] = {  	},  	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */  	}, +	/* Motorola H24 HSPA module: */ +	{ USB_DEVICE(0x22b8, 0x2d91) }, /* modem                                */ +	{ USB_DEVICE(0x22b8, 0x2d92),   /* modem           + diagnostics        */ +	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */ +	}, +	{ USB_DEVICE(0x22b8, 0x2d93),   /* modem + AT port                      */ +	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */ +	}, +	{ USB_DEVICE(0x22b8, 0x2d95),   /* modem + AT port + diagnostics        */ +	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */ +	}, +	{ USB_DEVICE(0x22b8, 0x2d96),   /* modem                         + NMEA */ +	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */ +	}, +	{ USB_DEVICE(0x22b8, 0x2d97),   /* modem           + diagnostics + NMEA */ +	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */ +	}, +	{ USB_DEVICE(0x22b8, 0x2d99),   /* modem + AT port               + NMEA */ +	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */ +	}, +	{ USB_DEVICE(0x22b8, 0x2d9a),   /* modem + AT port + diagnostics + NMEA */ +	.driver_info = NO_UNION_NORMAL, /* handle only modem interface          */ +	}, +  	{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */  	.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on  					   data interface instead of @@ -1541,6 +1714,12 @@ static const struct usb_device_id acm_ids[] = {  					   Maybe we should define a new  					   quirk for this. */  	}, +	{ USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */ +	.driver_info = NO_UNION_NORMAL, +	}, +	{ USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */ +	.driver_info = NO_UNION_NORMAL, +	},  	{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */  	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */  	}, @@ -1607,8 +1786,14 @@ static const struct usb_device_id acm_ids[] = {  	{ NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */  	{ NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */  	{ NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */ +	{ NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */ +	{ NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */ +	{ NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */  	{ SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */ +	/* Support for Owen devices */ +	{ USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */ +  	/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */  	/* Support Lego NXT using pbLua firmware */ @@ -1616,6 +1801,20 @@ static const struct usb_device_id acm_ids[] = {  	.driver_info = NOT_A_MODEM,  	}, +	/* Support for Droids MuIn LCD */ +	{ USB_DEVICE(0x04d8, 0x000b), +	.driver_info = NO_DATA_INTERFACE, +	}, + +#if IS_ENABLED(CONFIG_INPUT_IMS_PCU) +	{ USB_DEVICE(0x04d8, 0x0082),	/* Application mode */ +	.driver_info = IGNORE_DEVICE, +	}, +	{ USB_DEVICE(0x04d8, 0x0083),	/* Bootloader mode */ +	.driver_info = IGNORE_DEVICE, +	}, +#endif +  	/* control interfaces without any protocol set */  	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,  		USB_CDC_PROTO_NONE) }, @@ -1652,6 +1851,7 @@ static struct usb_driver acm_driver = {  #ifdef CONFIG_PM  	.supports_autosuspend = 1,  #endif +	.disable_hub_initiated_lpm = 1,  };  /* @@ -1659,8 +1859,10 @@ static struct usb_driver acm_driver = {   */  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, @@ -1684,7 +1886,6 @@ static int __init acm_init(void)  	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);  	if (!acm_tty_driver)  		return -ENOMEM; -	acm_tty_driver->owner = THIS_MODULE,  	acm_tty_driver->driver_name = "acm",  	acm_tty_driver->name = "ttyACM",  	acm_tty_driver->major = ACM_TTY_MAJOR, @@ -1710,8 +1911,7 @@ static int __init acm_init(void)  		return retval;  	} -	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" -	       DRIVER_DESC "\n"); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");  	return 0;  } diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 5eeb570b9a6..fc75651afe1 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -52,7 +52,7 @@   */  /* - * The only reason to have several buffers is to accomodate assumptions + * 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 @@ -72,16 +72,10 @@ struct acm_wb {  };  struct acm_rb { -	struct list_head	list;  	int			size;  	unsigned char		*base;  	dma_addr_t		dma; -}; - -struct acm_ru { -	struct list_head	list; -	struct acm_rb		*buffer; -	struct urb		*urb; +	int			index;  	struct acm		*instance;  }; @@ -97,43 +91,44 @@ struct acm {  	unsigned int country_code_size;			/* size of this buffer */  	unsigned int country_rel_date;			/* release date of version */  	struct acm_wb wb[ACM_NW]; -	struct acm_ru ru[ACM_NR]; -	struct acm_rb rb[ACM_NR]; +	unsigned long read_urbs_free; +	struct urb *read_urbs[ACM_NR]; +	struct acm_rb read_buffers[ACM_NR];  	int rx_buflimit;  	int rx_endpoint;  	spinlock_t read_lock; -	struct list_head spare_read_urbs; -	struct list_head spare_read_bufs; -	struct list_head filled_read_bufs;  	int write_used;					/* number of non-empty write buffers */ -	int processing;  	int transmitting;  	spinlock_t write_lock;  	struct mutex mutex; +	bool disconnected;  	struct usb_cdc_line_coding line;		/* bits, stop, parity */  	struct work_struct work;			/* work queue entry for line discipline waking up */ -	wait_queue_head_t drain_wait;			/* close processing */ -	struct tasklet_struct urb_task;                 /* rx processing */ -	spinlock_t throttle_lock;			/* synchronize throtteling and read callback */  	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 */ -	unsigned char throttle;				/* throttled by tty layer */  	unsigned char clocal;				/* termios CLOCAL */  	unsigned int ctrl_caps;				/* control capabilities from the class specific header */  	unsigned int susp_count;			/* number of suspended interfaces */  	unsigned int combined_interfaces:1;		/* control and data collapsed */  	unsigned int is_int_ep:1;			/* interrupt endpoints contrary to spec used */ +	unsigned int throttled:1;			/* actually throttled */ +	unsigned int throttle_req:1;			/* throttle requested */  	u8 bInterval; -	struct acm_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 */  };  #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_UNION_NORMAL			BIT(0) +#define SINGLE_RX_URB			BIT(1) +#define NO_CAP_LINE			BIT(2) +#define NOT_A_MODEM			BIT(3) +#define NO_DATA_INTERFACE		BIT(4) +#define IGNORE_DEVICE			BIT(5) diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 6ee4451bfe2..a051a7a2b1b 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -13,6 +13,7 @@   */  #include <linux/kernel.h>  #include <linux/errno.h> +#include <linux/ioctl.h>  #include <linux/slab.h>  #include <linux/module.h>  #include <linux/mutex.h> @@ -23,6 +24,7 @@  #include <linux/usb/cdc.h>  #include <asm/byteorder.h>  #include <asm/unaligned.h> +#include <linux/usb/cdc-wdm.h>  /*   * Version Information @@ -54,11 +56,17 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);  #define WDM_POLL_RUNNING	6  #define WDM_RESPONDING		7  #define WDM_SUSPENDING		8 +#define WDM_RESETTING		9 +#define WDM_OVERFLOW		10  #define WDM_MAX			16 +/* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ +#define WDM_DEFAULT_BUFSIZE	256  static DEFINE_MUTEX(wdm_mutex); +static DEFINE_SPINLOCK(wdm_device_list_lock); +static LIST_HEAD(wdm_device_list);  /* --- method tables --- */ @@ -80,7 +88,6 @@ struct wdm_device {  	u16			bufsize;  	u16			wMaxCommand;  	u16			wMaxPacketSize; -	u16			bMaxPacketSize0;  	__le16			inum;  	int			reslength;  	int			length; @@ -88,15 +95,51 @@ struct wdm_device {  	int			count;  	dma_addr_t		shandle;  	dma_addr_t		ihandle; -	struct mutex		lock; +	struct mutex		wlock; +	struct mutex		rlock;  	wait_queue_head_t	wait;  	struct work_struct	rxwork;  	int			werr;  	int			rerr; +	int                     resp_count; + +	struct list_head	device_list; +	int			(*manage_power)(struct usb_interface *, int);  };  static struct usb_driver wdm_driver; +/* return intfdata if we own the interface, else look up intf in the list */ +static struct wdm_device *wdm_find_device(struct usb_interface *intf) +{ +	struct wdm_device *desc; + +	spin_lock(&wdm_device_list_lock); +	list_for_each_entry(desc, &wdm_device_list, device_list) +		if (desc->intf == intf) +			goto found; +	desc = NULL; +found: +	spin_unlock(&wdm_device_list_lock); + +	return desc; +} + +static struct wdm_device *wdm_find_device_by_minor(int minor) +{ +	struct wdm_device *desc; + +	spin_lock(&wdm_device_list_lock); +	list_for_each_entry(desc, &wdm_device_list, device_list) +		if (desc->intf->minor == minor) +			goto found; +	desc = NULL; +found: +	spin_unlock(&wdm_device_list_lock); + +	return desc; +} +  /* --- callbacks --- */  static void wdm_out_callback(struct urb *urb)  { @@ -105,8 +148,9 @@ static void wdm_out_callback(struct urb *urb)  	spin_lock(&desc->iuspin);  	desc->werr = urb->status;  	spin_unlock(&desc->iuspin); -	clear_bit(WDM_IN_USE, &desc->flags);  	kfree(desc->outbuf); +	desc->outbuf = NULL; +	clear_bit(WDM_IN_USE, &desc->flags);  	wake_up(&desc->wait);  } @@ -114,6 +158,7 @@ static void wdm_in_callback(struct urb *urb)  {  	struct wdm_device *desc = urb->context;  	int status = urb->status; +	int length = urb->actual_length;  	spin_lock(&desc->iuspin);  	clear_bit(WDM_RESPONDING, &desc->flags); @@ -144,9 +189,17 @@ static void wdm_in_callback(struct urb *urb)  	}  	desc->rerr = status; -	desc->reslength = urb->actual_length; -	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); -	desc->length += desc->reslength; +	if (length + desc->length > desc->wMaxCommand) { +		/* The buffer would overflow */ +		set_bit(WDM_OVERFLOW, &desc->flags); +	} else { +		/* we may already be in overflow */ +		if (!test_bit(WDM_OVERFLOW, &desc->flags)) { +			memmove(desc->ubuf + desc->length, desc->inbuf, length); +			desc->length += length; +			desc->reslength = length; +		} +	}  skip_error:  	wake_up(&desc->wait); @@ -157,13 +210,12 @@ skip_error:  static void wdm_int_callback(struct urb *urb)  {  	int rv = 0; +	int responding;  	int status = urb->status;  	struct wdm_device *desc; -	struct usb_ctrlrequest *req;  	struct usb_cdc_notification *dr;  	desc = urb->context; -	req = desc->irq;  	dr = (struct usb_cdc_notification *)desc->sbuf;  	if (status) { @@ -202,6 +254,10 @@ static void wdm_int_callback(struct urb *urb)  			"NOTIFY_NETWORK_CONNECTION %s network",  			dr->wValue ? "connected to" : "disconnected from");  		goto exit; +	case USB_CDC_NOTIFY_SPEED_CHANGE: +		dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)", +			urb->actual_length); +		goto exit;  	default:  		clear_bit(WDM_POLL_RUNNING, &desc->flags);  		dev_err(&desc->intf->dev, @@ -210,28 +266,10 @@ static void wdm_int_callback(struct urb *urb)  		goto exit;  	} -	req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); -	req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; -	req->wValue = 0; -	req->wIndex = desc->inum; -	req->wLength = cpu_to_le16(desc->wMaxCommand); - -	usb_fill_control_urb( -		desc->response, -		interface_to_usbdev(desc->intf), -		/* using common endpoint 0 */ -		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), -		(unsigned char *)req, -		desc->inbuf, -		desc->wMaxCommand, -		wdm_in_callback, -		desc -	); -	desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  	spin_lock(&desc->iuspin); -	clear_bit(WDM_READ, &desc->flags); -	set_bit(WDM_RESPONDING, &desc->flags); -	if (!test_bit(WDM_DISCONNECTING, &desc->flags) +	responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); +	if (!desc->resp_count++ && !responding +		&& !test_bit(WDM_DISCONNECTING, &desc->flags)  		&& !test_bit(WDM_SUSPENDING, &desc->flags)) {  		rv = usb_submit_urb(desc->response, GFP_ATOMIC);  		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", @@ -276,14 +314,8 @@ static void free_urbs(struct wdm_device *desc)  static void cleanup(struct wdm_device *desc)  { -	usb_free_coherent(interface_to_usbdev(desc->intf), -			  desc->wMaxPacketSize, -			  desc->sbuf, -			  desc->validity->transfer_dma); -	usb_free_coherent(interface_to_usbdev(desc->intf), -			  desc->wMaxCommand, -			  desc->inbuf, -			  desc->response->transfer_dma); +	kfree(desc->sbuf); +	kfree(desc->inbuf);  	kfree(desc->orq);  	kfree(desc->irq);  	kfree(desc->ubuf); @@ -309,7 +341,7 @@ static ssize_t wdm_write  	if (we < 0)  		return -EIO; -	desc->outbuf = buf = kmalloc(count, GFP_KERNEL); +	buf = kmalloc(count, GFP_KERNEL);  	if (!buf) {  		rv = -ENOMEM;  		goto outnl; @@ -323,7 +355,7 @@ static ssize_t wdm_write  	}  	/* concurrent writes and disconnect */ -	r = mutex_lock_interruptible(&desc->lock); +	r = mutex_lock_interruptible(&desc->wlock);  	rv = -ERESTARTSYS;  	if (r) {  		kfree(buf); @@ -339,17 +371,23 @@ static ssize_t wdm_write  	r = usb_autopm_get_interface(desc->intf);  	if (r < 0) {  		kfree(buf); +		rv = usb_translate_errors(r);  		goto outnp;  	} -	if (!file->f_flags && O_NONBLOCK) +	if (!(file->f_flags & O_NONBLOCK))  		r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,  								&desc->flags));  	else  		if (test_bit(WDM_IN_USE, &desc->flags))  			r = -EAGAIN; + +	if (test_bit(WDM_RESETTING, &desc->flags)) +		r = -EIO; +  	if (r < 0) {  		kfree(buf); +		rv = r;  		goto out;  	} @@ -373,12 +411,15 @@ static ssize_t wdm_write  	req->wIndex = desc->inum;  	req->wLength = cpu_to_le16(count);  	set_bit(WDM_IN_USE, &desc->flags); +	desc->outbuf = buf;  	rv = usb_submit_urb(desc->command, GFP_KERNEL);  	if (rv < 0) {  		kfree(buf); +		desc->outbuf = NULL;  		clear_bit(WDM_IN_USE, &desc->flags);  		dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); +		rv = usb_translate_errors(rv);  	} else {  		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",  			req->wIndex); @@ -386,30 +427,68 @@ static ssize_t wdm_write  out:  	usb_autopm_put_interface(desc->intf);  outnp: -	mutex_unlock(&desc->lock); +	mutex_unlock(&desc->wlock);  outnl:  	return rv < 0 ? rv : count;  } +/* + * clear WDM_READ flag and possibly submit the read urb if resp_count + * is non-zero. + * + * Called with desc->iuspin locked + */ +static int clear_wdm_read_flag(struct wdm_device *desc) +{ +	int rv = 0; + +	clear_bit(WDM_READ, &desc->flags); + +	/* submit read urb only if the device is waiting for it */ +	if (!desc->resp_count || !--desc->resp_count) +		goto out; + +	set_bit(WDM_RESPONDING, &desc->flags); +	spin_unlock_irq(&desc->iuspin); +	rv = usb_submit_urb(desc->response, GFP_KERNEL); +	spin_lock_irq(&desc->iuspin); +	if (rv) { +		dev_err(&desc->intf->dev, +			"usb_submit_urb failed with result %d\n", rv); + +		/* make sure the next notification trigger a submit */ +		clear_bit(WDM_RESPONDING, &desc->flags); +		desc->resp_count = 0; +	} +out: +	return rv; +} +  static ssize_t wdm_read  (struct file *file, char __user *buffer, size_t count, loff_t *ppos)  { -	int rv, cntr = 0; +	int rv, cntr;  	int i = 0;  	struct wdm_device *desc = file->private_data; -	rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */ +	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */  	if (rv < 0)  		return -ERESTARTSYS; -	if (desc->length == 0) { +	cntr = ACCESS_ONCE(desc->length); +	if (cntr == 0) {  		desc->read = 0;  retry:  		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {  			rv = -ENODEV;  			goto err;  		} +		if (test_bit(WDM_OVERFLOW, &desc->flags)) { +			clear_bit(WDM_OVERFLOW, &desc->flags); +			rv = -ENOBUFS; +			goto err; +		}  		i++;  		if (file->f_flags & O_NONBLOCK) {  			if (!test_bit(WDM_READ, &desc->flags)) { @@ -427,6 +506,10 @@ retry:  			rv = -ENODEV;  			goto err;  		} +		if (test_bit(WDM_RESETTING, &desc->flags)) { +			rv = -EIO; +			goto err; +		}  		usb_mark_last_busy(interface_to_usbdev(desc->intf));  		if (rv < 0) {  			rv = -ERESTARTSYS; @@ -449,32 +532,41 @@ retry:  			spin_unlock_irq(&desc->iuspin);  			goto retry;  		} +  		if (!desc->reslength) { /* zero length read */ +			dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); +			rv = clear_wdm_read_flag(desc);  			spin_unlock_irq(&desc->iuspin); +			if (rv < 0) +				goto err;  			goto retry;  		} -		clear_bit(WDM_READ, &desc->flags); +		cntr = desc->length;  		spin_unlock_irq(&desc->iuspin);  	} -	cntr = count > desc->length ? desc->length : count; +	if (cntr > count) +		cntr = count;  	rv = copy_to_user(buffer, desc->ubuf, cntr);  	if (rv > 0) {  		rv = -EFAULT;  		goto err;  	} +	spin_lock_irq(&desc->iuspin); +  	for (i = 0; i < desc->length - cntr; i++)  		desc->ubuf[i] = desc->ubuf[i + cntr];  	desc->length -= cntr;  	/* in case we had outstanding data */  	if (!desc->length) -		clear_bit(WDM_READ, &desc->flags); +		clear_wdm_read_flag(desc); +	spin_unlock_irq(&desc->iuspin);  	rv = cntr;  err: -	mutex_unlock(&desc->lock); +	mutex_unlock(&desc->rlock);  	return rv;  } @@ -483,11 +575,13 @@ static int wdm_flush(struct file *file, fl_owner_t id)  	struct wdm_device *desc = file->private_data;  	wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); -	if (desc->werr < 0) + +	/* cannot dereference desc->intf if WDM_DISCONNECTING */ +	if (desc->werr < 0 && !test_bit(WDM_DISCONNECTING, &desc->flags))  		dev_err(&desc->intf->dev, "Error in flush path: %d\n",  			desc->werr); -	return desc->werr; +	return usb_translate_errors(desc->werr);  }  static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) @@ -498,7 +592,7 @@ static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)  	spin_lock_irqsave(&desc->iuspin, flags);  	if (test_bit(WDM_DISCONNECTING, &desc->flags)) { -		mask = POLLERR; +		mask = POLLHUP | POLLERR;  		spin_unlock_irqrestore(&desc->iuspin, flags);  		goto desc_out;  	} @@ -524,11 +618,11 @@ static int wdm_open(struct inode *inode, struct file *file)  	struct wdm_device *desc;  	mutex_lock(&wdm_mutex); -	intf = usb_find_interface(&wdm_driver, minor); -	if (!intf) +	desc = wdm_find_device_by_minor(minor); +	if (!desc)  		goto out; -	desc = usb_get_intfdata(intf); +	intf = desc->intf;  	if (test_bit(WDM_DISCONNECTING, &desc->flags))  		goto out;  	file->private_data = desc; @@ -538,20 +632,25 @@ static int wdm_open(struct inode *inode, struct file *file)  		dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);  		goto out;  	} -	intf->needs_remote_wakeup = 1; -	mutex_lock(&desc->lock); +	/* using write lock to protect desc->count */ +	mutex_lock(&desc->wlock);  	if (!desc->count++) { +		desc->werr = 0; +		desc->rerr = 0;  		rv = usb_submit_urb(desc->validity, GFP_KERNEL);  		if (rv < 0) {  			desc->count--;  			dev_err(&desc->intf->dev,  				"Error submitting int urb - %d\n", rv); +			rv = usb_translate_errors(rv);  		}  	} else {  		rv = 0;  	} -	mutex_unlock(&desc->lock); +	mutex_unlock(&desc->wlock); +	if (desc->count == 1) +		desc->manage_power(intf, 1);  	usb_autopm_put_interface(desc->intf);  out:  	mutex_unlock(&wdm_mutex); @@ -563,20 +662,46 @@ static int wdm_release(struct inode *inode, struct file *file)  	struct wdm_device *desc = file->private_data;  	mutex_lock(&wdm_mutex); -	mutex_lock(&desc->lock); + +	/* using write lock to protect desc->count */ +	mutex_lock(&desc->wlock);  	desc->count--; -	mutex_unlock(&desc->lock); +	mutex_unlock(&desc->wlock);  	if (!desc->count) { -		dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); -		kill_urbs(desc); -		if (!test_bit(WDM_DISCONNECTING, &desc->flags)) -			desc->intf->needs_remote_wakeup = 0; +		if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { +			dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); +			kill_urbs(desc); +			spin_lock_irq(&desc->iuspin); +			desc->resp_count = 0; +			spin_unlock_irq(&desc->iuspin); +			desc->manage_power(desc->intf, 0); +		} else { +			/* must avoid dev_printk here as desc->intf is invalid */ +			pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__); +			cleanup(desc); +		}  	}  	mutex_unlock(&wdm_mutex);  	return 0;  } +static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ +	struct wdm_device *desc = file->private_data; +	int rv = 0; + +	switch (cmd) { +	case IOCTL_WDM_MAX_COMMAND: +		if (copy_to_user((void __user *)arg, &desc->wMaxCommand, sizeof(desc->wMaxCommand))) +			rv = -EFAULT; +		break; +	default: +		rv = -ENOTTY; +	} +	return rv; +} +  static const struct file_operations wdm_fops = {  	.owner =	THIS_MODULE,  	.read =		wdm_read, @@ -585,6 +710,8 @@ static const struct file_operations wdm_fops = {  	.flush =	wdm_flush,  	.release =	wdm_release,  	.poll =		wdm_poll, +	.unlocked_ioctl = wdm_ioctl, +	.compat_ioctl = wdm_ioctl,  	.llseek =	noop_llseek,  }; @@ -599,16 +726,20 @@ static void wdm_rxwork(struct work_struct *work)  {  	struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);  	unsigned long flags; -	int rv; +	int rv = 0; +	int responding;  	spin_lock_irqsave(&desc->iuspin, flags);  	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {  		spin_unlock_irqrestore(&desc->iuspin, flags);  	} else { +		responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);  		spin_unlock_irqrestore(&desc->iuspin, flags); -		rv = usb_submit_urb(desc->response, GFP_KERNEL); +		if (!responding) +			rv = usb_submit_urb(desc->response, GFP_KERNEL);  		if (rv < 0 && rv != -EPERM) {  			spin_lock_irqsave(&desc->iuspin, flags); +			clear_bit(WDM_RESPONDING, &desc->flags);  			if (!test_bit(WDM_DISCONNECTING, &desc->flags))  				schedule_work(&desc->rxwork);  			spin_unlock_irqrestore(&desc->iuspin, flags); @@ -618,70 +749,31 @@ static void wdm_rxwork(struct work_struct *work)  /* --- hotplug --- */ -static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) +static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, +		u16 bufsize, int (*manage_power)(struct usb_interface *, int))  { -	int rv = -EINVAL; -	struct usb_device *udev = interface_to_usbdev(intf); +	int rv = -ENOMEM;  	struct wdm_device *desc; -	struct usb_host_interface *iface; -	struct usb_endpoint_descriptor *ep; -	struct usb_cdc_dmm_desc *dmhd; -	u8 *buffer = intf->altsetting->extra; -	int buflen = intf->altsetting->extralen; -	u16 maxcom = 0; - -	if (!buffer) -		goto out; - -	while (buflen > 2) { -		if (buffer [1] != USB_DT_CS_INTERFACE) { -			dev_err(&intf->dev, "skipping garbage\n"); -			goto next_desc; -		} -		switch (buffer [2]) { -		case USB_CDC_HEADER_TYPE: -			break; -		case USB_CDC_DMM_TYPE: -			dmhd = (struct usb_cdc_dmm_desc *)buffer; -			maxcom = le16_to_cpu(dmhd->wMaxCommand); -			dev_dbg(&intf->dev, -				"Finding maximum buffer length: %d", maxcom); -			break; -		default: -			dev_err(&intf->dev, -				"Ignoring extra header, type %d, length %d\n", -				buffer[2], buffer[0]); -			break; -		} -next_desc: -		buflen -= buffer[0]; -		buffer += buffer[0]; -	} - -	rv = -ENOMEM;  	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);  	if (!desc)  		goto out; -	mutex_init(&desc->lock); +	INIT_LIST_HEAD(&desc->device_list); +	mutex_init(&desc->rlock); +	mutex_init(&desc->wlock);  	spin_lock_init(&desc->iuspin);  	init_waitqueue_head(&desc->wait); -	desc->wMaxCommand = maxcom; +	desc->wMaxCommand = bufsize;  	/* this will be expanded and needed in hardware endianness */  	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);  	desc->intf = intf;  	INIT_WORK(&desc->rxwork, wdm_rxwork);  	rv = -EINVAL; -	iface = intf->cur_altsetting; -	if (iface->desc.bNumEndpoints != 1) -		goto err; -	ep = &iface->endpoint[0].desc; -	if (!ep || !usb_endpoint_is_int_in(ep)) +	if (!usb_endpoint_is_int_in(ep))  		goto err; -	desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize); -	desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0; +	desc->wMaxPacketSize = usb_endpoint_maxp(ep);  	desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);  	if (!desc->orq) @@ -706,19 +798,13 @@ next_desc:  	if (!desc->ubuf)  		goto err; -	desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf), -					desc->wMaxPacketSize, -					GFP_KERNEL, -					&desc->validity->transfer_dma); +	desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);  	if (!desc->sbuf)  		goto err; -	desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf), -					 desc->bMaxPacketSize0, -					 GFP_KERNEL, -					 &desc->response->transfer_dma); +	desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);  	if (!desc->inbuf) -		goto err2; +		goto err;  	usb_fill_int_urb(  		desc->validity, @@ -730,45 +816,150 @@ next_desc:  		desc,  		ep->bInterval  	); -	desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -	usb_set_intfdata(intf, desc); +	desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); +	desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; +	desc->irq->wValue = 0; +	desc->irq->wIndex = desc->inum; +	desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); + +	usb_fill_control_urb( +		desc->response, +		interface_to_usbdev(intf), +		/* using common endpoint 0 */ +		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), +		(unsigned char *)desc->irq, +		desc->inbuf, +		desc->wMaxCommand, +		wdm_in_callback, +		desc +	); + +	desc->manage_power = manage_power; + +	spin_lock(&wdm_device_list_lock); +	list_add(&desc->device_list, &wdm_device_list); +	spin_unlock(&wdm_device_list_lock); +  	rv = usb_register_dev(intf, &wdm_class);  	if (rv < 0) -		goto err3; +		goto err;  	else -		dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n", -			intf->minor - WDM_MINOR_BASE); +		dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));  out:  	return rv; -err3: -	usb_set_intfdata(intf, NULL); -	usb_free_coherent(interface_to_usbdev(desc->intf), -			  desc->bMaxPacketSize0, -			desc->inbuf, -			desc->response->transfer_dma); -err2: -	usb_free_coherent(interface_to_usbdev(desc->intf), -			  desc->wMaxPacketSize, -			  desc->sbuf, -			  desc->validity->transfer_dma);  err: -	free_urbs(desc); -	kfree(desc->ubuf); -	kfree(desc->orq); -	kfree(desc->irq); -	kfree(desc); +	spin_lock(&wdm_device_list_lock); +	list_del(&desc->device_list); +	spin_unlock(&wdm_device_list_lock); +	cleanup(desc);  	return rv;  } +static int wdm_manage_power(struct usb_interface *intf, int on) +{ +	/* need autopm_get/put here to ensure the usbcore sees the new value */ +	int rv = usb_autopm_get_interface(intf); + +	intf->needs_remote_wakeup = on; +	if (!rv) +		usb_autopm_put_interface(intf); +	return 0; +} + +static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ +	int rv = -EINVAL; +	struct usb_host_interface *iface; +	struct usb_endpoint_descriptor *ep; +	struct usb_cdc_dmm_desc *dmhd; +	u8 *buffer = intf->altsetting->extra; +	int buflen = intf->altsetting->extralen; +	u16 maxcom = WDM_DEFAULT_BUFSIZE; + +	if (!buffer) +		goto err; +	while (buflen > 2) { +		if (buffer[1] != USB_DT_CS_INTERFACE) { +			dev_err(&intf->dev, "skipping garbage\n"); +			goto next_desc; +		} + +		switch (buffer[2]) { +		case USB_CDC_HEADER_TYPE: +			break; +		case USB_CDC_DMM_TYPE: +			dmhd = (struct usb_cdc_dmm_desc *)buffer; +			maxcom = le16_to_cpu(dmhd->wMaxCommand); +			dev_dbg(&intf->dev, +				"Finding maximum buffer length: %d", maxcom); +			break; +		default: +			dev_err(&intf->dev, +				"Ignoring extra header, type %d, length %d\n", +				buffer[2], buffer[0]); +			break; +		} +next_desc: +		buflen -= buffer[0]; +		buffer += buffer[0]; +	} + +	iface = intf->cur_altsetting; +	if (iface->desc.bNumEndpoints != 1) +		goto err; +	ep = &iface->endpoint[0].desc; + +	rv = wdm_create(intf, ep, maxcom, &wdm_manage_power); + +err: +	return rv; +} + +/** + * usb_cdc_wdm_register - register a WDM subdriver + * @intf: usb interface the subdriver will associate with + * @ep: interrupt endpoint to monitor for notifications + * @bufsize: maximum message size to support for read/write + * + * Create WDM usb class character device and associate it with intf + * without binding, allowing another driver to manage the interface. + * + * The subdriver will manage the given interrupt endpoint exclusively + * and will issue control requests referring to the given intf. It + * will otherwise avoid interferring, and in particular not do + * usb_set_intfdata/usb_get_intfdata on intf. + * + * The return value is a pointer to the subdriver's struct usb_driver. + * The registering driver is responsible for calling this subdriver's + * disconnect, suspend, resume, pre_reset and post_reset methods from + * its own. + */ +struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, +					struct usb_endpoint_descriptor *ep, +					int bufsize, +					int (*manage_power)(struct usb_interface *, int)) +{ +	int rv = -EINVAL; + +	rv = wdm_create(intf, ep, bufsize, manage_power); +	if (rv < 0) +		goto err; + +	return &wdm_driver; +err: +	return ERR_PTR(rv); +} +EXPORT_SYMBOL(usb_cdc_wdm_register); +  static void wdm_disconnect(struct usb_interface *intf)  {  	struct wdm_device *desc;  	unsigned long flags;  	usb_deregister_dev(intf, &wdm_class); +	desc = wdm_find_device(intf);  	mutex_lock(&wdm_mutex); -	desc = usb_get_intfdata(intf);  	/* the spinlock makes sure no new urbs are generated in the callbacks */  	spin_lock_irqsave(&desc->iuspin, flags); @@ -777,30 +968,42 @@ static void wdm_disconnect(struct usb_interface *intf)  	/* to terminate pending flushes */  	clear_bit(WDM_IN_USE, &desc->flags);  	spin_unlock_irqrestore(&desc->iuspin, flags); -	mutex_lock(&desc->lock); +	wake_up_all(&desc->wait); +	mutex_lock(&desc->rlock); +	mutex_lock(&desc->wlock);  	kill_urbs(desc);  	cancel_work_sync(&desc->rxwork); -	mutex_unlock(&desc->lock); -	wake_up_all(&desc->wait); +	mutex_unlock(&desc->wlock); +	mutex_unlock(&desc->rlock); + +	/* the desc->intf pointer used as list key is now invalid */ +	spin_lock(&wdm_device_list_lock); +	list_del(&desc->device_list); +	spin_unlock(&wdm_device_list_lock); +  	if (!desc->count)  		cleanup(desc); +	else +		dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count);  	mutex_unlock(&wdm_mutex);  }  #ifdef CONFIG_PM  static int wdm_suspend(struct usb_interface *intf, pm_message_t message)  { -	struct wdm_device *desc = usb_get_intfdata(intf); +	struct wdm_device *desc = wdm_find_device(intf);  	int rv = 0;  	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);  	/* if this is an autosuspend the caller does the locking */ -	if (!(message.event & PM_EVENT_AUTO)) -		mutex_lock(&desc->lock); +	if (!PMSG_IS_AUTO(message)) { +		mutex_lock(&desc->rlock); +		mutex_lock(&desc->wlock); +	}  	spin_lock_irq(&desc->iuspin); -	if ((message.event & PM_EVENT_AUTO) && +	if (PMSG_IS_AUTO(message) &&  			(test_bit(WDM_IN_USE, &desc->flags)  			|| test_bit(WDM_RESPONDING, &desc->flags))) {  		spin_unlock_irq(&desc->iuspin); @@ -813,8 +1016,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)  		kill_urbs(desc);  		cancel_work_sync(&desc->rxwork);  	} -	if (!(message.event & PM_EVENT_AUTO)) -		mutex_unlock(&desc->lock); +	if (!PMSG_IS_AUTO(message)) { +		mutex_unlock(&desc->wlock); +		mutex_unlock(&desc->rlock); +	}  	return rv;  } @@ -836,7 +1041,7 @@ static int recover_from_urb_loss(struct wdm_device *desc)  #ifdef CONFIG_PM  static int wdm_resume(struct usb_interface *intf)  { -	struct wdm_device *desc = usb_get_intfdata(intf); +	struct wdm_device *desc = wdm_find_device(intf);  	int rv;  	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); @@ -850,19 +1055,38 @@ static int wdm_resume(struct usb_interface *intf)  static int wdm_pre_reset(struct usb_interface *intf)  { -	struct wdm_device *desc = usb_get_intfdata(intf); - -	mutex_lock(&desc->lock); +	struct wdm_device *desc = wdm_find_device(intf); + +	/* +	 * we notify everybody using poll of +	 * an exceptional situation +	 * must be done before recovery lest a spontaneous +	 * message from the device is lost +	 */ +	spin_lock_irq(&desc->iuspin); +	set_bit(WDM_RESETTING, &desc->flags);	/* inform read/write */ +	set_bit(WDM_READ, &desc->flags);	/* unblock read */ +	clear_bit(WDM_IN_USE, &desc->flags);	/* unblock write */ +	desc->rerr = -EINTR; +	spin_unlock_irq(&desc->iuspin); +	wake_up_all(&desc->wait); +	mutex_lock(&desc->rlock); +	mutex_lock(&desc->wlock); +	kill_urbs(desc); +	cancel_work_sync(&desc->rxwork);  	return 0;  }  static int wdm_post_reset(struct usb_interface *intf)  { -	struct wdm_device *desc = usb_get_intfdata(intf); +	struct wdm_device *desc = wdm_find_device(intf);  	int rv; +	clear_bit(WDM_OVERFLOW, &desc->flags); +	clear_bit(WDM_RESETTING, &desc->flags);  	rv = recover_from_urb_loss(desc); -	mutex_unlock(&desc->lock); +	mutex_unlock(&desc->wlock); +	mutex_unlock(&desc->rlock);  	return 0;  } @@ -879,26 +1103,10 @@ static struct usb_driver wdm_driver = {  	.post_reset =	wdm_post_reset,  	.id_table =	wdm_ids,  	.supports_autosuspend = 1, +	.disable_hub_initiated_lpm = 1,  }; -/* --- low level module stuff --- */ - -static int __init wdm_init(void) -{ -	int rv; - -	rv = usb_register(&wdm_driver); - -	return rv; -} - -static void __exit wdm_exit(void) -{ -	usb_deregister(&wdm_driver); -} - -module_init(wdm_init); -module_exit(wdm_exit); +module_usb_driver(wdm_driver);  MODULE_AUTHOR(DRIVER_AUTHOR);  MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 9eca4053312..0924ee40a96 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -52,12 +52,12 @@  #include <linux/sched.h>  #include <linux/signal.h>  #include <linux/poll.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/lp.h>  #include <linux/mutex.h>  #undef DEBUG  #include <linux/usb.h> +#include <linux/ratelimit.h>  /*   * Version Information @@ -171,27 +171,31 @@ struct usblp {  #ifdef DEBUG  static void usblp_dump(struct usblp *usblp)  { +	struct device *dev = &usblp->intf->dev;  	int p; -	dbg("usblp=0x%p", usblp); -	dbg("dev=0x%p", usblp->dev); -	dbg("present=%d", usblp->present); -	dbg("readbuf=0x%p", usblp->readbuf); -	dbg("readcount=%d", usblp->readcount); -	dbg("ifnum=%d", usblp->ifnum); -    for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) { -	dbg("protocol[%d].alt_setting=%d", p, usblp->protocol[p].alt_setting); -	dbg("protocol[%d].epwrite=%p", p, usblp->protocol[p].epwrite); -	dbg("protocol[%d].epread=%p", p, usblp->protocol[p].epread); -    } -	dbg("current_protocol=%d", usblp->current_protocol); -	dbg("minor=%d", usblp->minor); -	dbg("wstatus=%d", usblp->wstatus); -	dbg("rstatus=%d", usblp->rstatus); -	dbg("quirks=%d", usblp->quirks); -	dbg("used=%d", usblp->used); -	dbg("bidir=%d", usblp->bidir); -	dbg("device_id_string=\"%s\"", +	dev_dbg(dev, "usblp=0x%p\n", usblp); +	dev_dbg(dev, "dev=0x%p\n", usblp->dev); +	dev_dbg(dev, "present=%d\n", usblp->present); +	dev_dbg(dev, "readbuf=0x%p\n", usblp->readbuf); +	dev_dbg(dev, "readcount=%d\n", usblp->readcount); +	dev_dbg(dev, "ifnum=%d\n", usblp->ifnum); +	for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) { +		dev_dbg(dev, "protocol[%d].alt_setting=%d\n", p, +			usblp->protocol[p].alt_setting); +		dev_dbg(dev, "protocol[%d].epwrite=%p\n", p, +			usblp->protocol[p].epwrite); +		dev_dbg(dev, "protocol[%d].epread=%p\n", p, +			usblp->protocol[p].epread); +	} +	dev_dbg(dev, "current_protocol=%d\n", usblp->current_protocol); +	dev_dbg(dev, "minor=%d\n", usblp->minor); +	dev_dbg(dev, "wstatus=%d\n", usblp->wstatus); +	dev_dbg(dev, "rstatus=%d\n", usblp->rstatus); +	dev_dbg(dev, "quirks=%d\n", usblp->quirks); +	dev_dbg(dev, "used=%d\n", usblp->used); +	dev_dbg(dev, "bidir=%d\n", usblp->bidir); +	dev_dbg(dev, "device_id_string=\"%s\"\n",  		usblp->device_id_string ?  			usblp->device_id_string + 2 :  			(unsigned char *)"(null)"); @@ -261,7 +265,8 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i  	retval = usb_control_msg(usblp->dev,  		dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),  		request, type | dir | recip, value, index, buf, len, USBLP_CTL_TIMEOUT); -	dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d", +	dev_dbg(&usblp->intf->dev, +		"usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d\n",  		request, !!dir, recip, value, index, len, retval);  	return retval < 0 ? retval : 0;  } @@ -348,8 +353,7 @@ static int usblp_check_status(struct usblp *usblp, int err)  	mutex_lock(&usblp->mut);  	if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) {  		mutex_unlock(&usblp->mut); -		if (printk_ratelimit()) -			printk(KERN_ERR +		printk_ratelimited(KERN_ERR  				"usblp%d: error %d reading printer status\n",  				usblp->minor, error);  		return 0; @@ -500,8 +504,9 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  		goto done;  	} -	dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd), -		_IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd)); +	dev_dbg(&usblp->intf->dev, +		"usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)\n", cmd, +		_IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));  	if (_IOC_TYPE(cmd) == 'P')	/* new-style ioctl number */ @@ -594,7 +599,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  				goto done;  			} -			dbg("usblp%d requested/got HP channel %ld/%d", +			dev_dbg(&usblp->intf->dev, +				"usblp%d requested/got HP channel %ld/%d\n",  				usblp->minor, arg, newChannel);  			break; @@ -614,7 +620,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  				goto done;  			} -			dbg("usblp%d is bus=%d, device=%d", +			dev_dbg(&usblp->intf->dev, +				"usblp%d is bus=%d, device=%d\n",  				usblp->minor, twoints[0], twoints[1]);  			break; @@ -634,7 +641,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  				goto done;  			} -			dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X", +			dev_dbg(&usblp->intf->dev, +				"usblp%d is VID=0x%4.4X, PID=0x%4.4X\n",  				usblp->minor, twoints[0], twoints[1]);  			break; @@ -653,8 +661,7 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  		case LPGETSTATUS:  			if ((retval = usblp_read_status(usblp, usblp->statusbuf))) { -				if (printk_ratelimit()) -					printk(KERN_ERR "usblp%d:" +				printk_ratelimited(KERN_ERR "usblp%d:"  					    "failed reading printer status (%d)\n",  					    usblp->minor, retval);  				retval = -EIO; @@ -988,7 +995,7 @@ static int usblp_submit_read(struct usblp *usblp)  	usblp->rcomplete = 0;  	spin_unlock_irqrestore(&usblp->lock, flags);  	if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) { -		dbg("error submitting urb (%d)", rc); +		dev_dbg(&usblp->intf->dev, "error submitting urb (%d)\n", rc);  		spin_lock_irqsave(&usblp->lock, flags);  		usblp->rstatus = rc;  		usblp->rcomplete = 1; @@ -1046,7 +1053,7 @@ static const struct file_operations usblp_fops = {  	.llseek =	noop_llseek,  }; -static char *usblp_devnode(struct device *dev, mode_t *mode) +static char *usblp_devnode(struct device *dev, umode_t *mode)  {  	return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));  } @@ -1130,7 +1137,8 @@ static int usblp_probe(struct usb_interface *intf,  	/* Analyze and pick initial alternate settings and endpoints. */  	protocol = usblp_select_alts(usblp);  	if (protocol < 0) { -		dbg("incompatible printer-class device 0x%4.4X/0x%4.4X", +		dev_dbg(&intf->dev, +			"incompatible printer-class device 0x%4.4X/0x%4.4X\n",  			le16_to_cpu(dev->descriptor.idVendor),  			le16_to_cpu(dev->descriptor.idProduct));  		retval = -ENODEV; @@ -1159,14 +1167,14 @@ static int usblp_probe(struct usb_interface *intf,  	retval = usb_register_dev(intf, &usblp_class);  	if (retval) { -		printk(KERN_ERR "usblp: Not able to get a minor" -		    " (base %u, slice default): %d\n", -		    USBLP_MINOR_BASE, retval); +		dev_err(&intf->dev, +			"usblp: Not able to get a minor (base %u, slice default): %d\n", +			USBLP_MINOR_BASE, retval);  		goto abort_intfdata;  	}  	usblp->minor = intf->minor; -	printk(KERN_INFO "usblp%d: USB %sdirectional printer dev %d " -		"if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n", +	dev_info(&intf->dev, +		"usblp%d: USB %sdirectional printer dev %d if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n",  		usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum,  		usblp->ifnum,  		usblp->protocol[usblp->current_protocol].alt_setting, @@ -1303,7 +1311,8 @@ static int usblp_set_protocol(struct usblp *usblp, int protocol)  	usblp->bidir = (usblp->protocol[protocol].epread != NULL);  	usblp->current_protocol = protocol; -	dbg("usblp%d set protocol %d", usblp->minor, protocol); +	dev_dbg(&usblp->intf->dev, "usblp%d set protocol %d\n", +		usblp->minor, protocol);  	return 0;  } @@ -1316,7 +1325,8 @@ static int usblp_cache_device_id_string(struct usblp *usblp)  	err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1);  	if (err < 0) { -		dbg("usblp%d: error = %d reading IEEE-1284 Device ID string", +		dev_dbg(&usblp->intf->dev, +			"usblp%d: error = %d reading IEEE-1284 Device ID string\n",  			usblp->minor, err);  		usblp->device_id_string[0] = usblp->device_id_string[1] = '\0';  		return -EIO; @@ -1332,7 +1342,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp)  		length = USBLP_DEVICE_ID_SIZE - 1;  	usblp->device_id_string[length] = '\0'; -	dbg("usblp%d Device ID string [len=%d]=\"%s\"", +	dev_dbg(&usblp->intf->dev, "usblp%d Device ID string [len=%d]=\"%s\"\n",  		usblp->minor, length, &usblp->device_id_string[2]);  	return length; @@ -1413,18 +1423,7 @@ static struct usb_driver usblp_driver = {  	.supports_autosuspend =	1,  }; -static int __init usblp_init(void) -{ -	return usb_register(&usblp_driver); -} - -static void __exit usblp_exit(void) -{ -	usb_deregister(&usblp_driver); -} - -module_init(usblp_init); -module_exit(usblp_exit); +module_usb_driver(usblp_driver);  MODULE_AUTHOR(DRIVER_AUTHOR);  MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 6a54634ab82..103a6e9ee49 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -19,7 +19,8 @@   * http://www.gnu.org/copyleft/gpl.html.   */ -#include <linux/init.h> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/fs.h> @@ -31,6 +32,8 @@  #include <linux/usb/tmc.h> +#define RIGOL			1 +#define USBTMC_HEADER_SIZE	12  #define USBTMC_MINOR_BASE	176  /* @@ -84,6 +87,8 @@ struct usbtmc_device_data {  	u8 bTag_last_write;	/* needed for abort */  	u8 bTag_last_read;	/* needed for abort */ +	u8 rigol_quirk; +  	/* attributes from the USB TMC spec for this device */  	u8 TermChar;  	bool TermCharEnabled; @@ -97,6 +102,16 @@ struct usbtmc_device_data {  };  #define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref) +struct usbtmc_ID_rigol_quirk { +	__u16 idVendor; +	__u16 idProduct; +}; + +static const struct usbtmc_ID_rigol_quirk usbtmc_id_quirk[] = { +	{ 0x1ab1, 0x0588 }, +	{ 0, 0 } +}; +  /* Forward declarations */  static struct usb_driver usbtmc_driver; @@ -105,7 +120,6 @@ static void usbtmc_delete(struct kref *kref)  	struct usbtmc_device_data *data = to_usbtmc_data(kref);  	usb_put_dev(data->usb_dev); -	kfree(data);  }  static int usbtmc_open(struct inode *inode, struct file *filp) @@ -116,10 +130,8 @@ static int usbtmc_open(struct inode *inode, struct file *filp)  	intf = usb_find_interface(&usbtmc_driver, iminor(inode));  	if (!intf) { -		printk(KERN_ERR KBUILD_MODNAME -		       ": can not find device for minor %d", iminor(inode)); -		retval = -ENODEV; -		goto exit; +		pr_err("can not find device for minor %d", iminor(inode)); +		return -ENODEV;  	}  	data = usb_get_intfdata(intf); @@ -128,7 +140,6 @@ static int usbtmc_open(struct inode *inode, struct file *filp)  	/* Store pointer in file structure's private data field */  	filp->private_data = data; -exit:  	return retval;  } @@ -186,8 +197,7 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)  	for (n = 0; n < current_setting->desc.bNumEndpoints; n++)  		if (current_setting->endpoint[n].desc.bEndpointAddress ==  			data->bulk_in) -			max_size = le16_to_cpu(current_setting->endpoint[n]. -						desc.wMaxPacketSize); +			max_size = usb_endpoint_maxp(¤t_setting->endpoint[n].desc);  	if (max_size == 0) {  		dev_err(dev, "Couldn't get wMaxPacketSize\n"); @@ -268,7 +278,7 @@ usbtmc_abort_bulk_in_status:  				dev_err(dev, "usb_bulk_msg returned %d\n", rv);  				goto exit;  			} -		} while ((actual = max_size) && +		} while ((actual == max_size) &&  			 (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));  	if (actual == max_size) { @@ -362,6 +372,63 @@ exit:  	return rv;  } +/* + * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-IN endpoint. + * @transfer_size: number of bytes to request from the device. + * + * See the USBTMC specification, Table 4. + * + * Also updates bTag_last_write. + */ +static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t transfer_size) +{ +	int retval; +	u8 *buffer; +	int actual; + +	buffer = kmalloc(USBTMC_HEADER_SIZE, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; +	/* Setup IO buffer for REQUEST_DEV_DEP_MSG_IN message +	 * Refer to class specs for details +	 */ +	buffer[0] = 2; +	buffer[1] = data->bTag; +	buffer[2] = ~data->bTag; +	buffer[3] = 0; /* Reserved */ +	buffer[4] = transfer_size >> 0; +	buffer[5] = transfer_size >> 8; +	buffer[6] = transfer_size >> 16; +	buffer[7] = transfer_size >> 24; +	buffer[8] = data->TermCharEnabled * 2; +	/* Use term character? */ +	buffer[9] = data->TermChar; +	buffer[10] = 0; /* Reserved */ +	buffer[11] = 0; /* Reserved */ + +	/* Send bulk URB */ +	retval = usb_bulk_msg(data->usb_dev, +			      usb_sndbulkpipe(data->usb_dev, +					      data->bulk_out), +			      buffer, USBTMC_HEADER_SIZE, &actual, USBTMC_TIMEOUT); + +	/* Store bTag (in case we need to abort) */ +	data->bTag_last_write = data->bTag; + +	/* Increment bTag -- and increment again if zero */ +	data->bTag++; +	if (!data->bTag) +		data->bTag++; + +	kfree(buffer); +	if (retval < 0) { +		dev_err(&data->intf->dev, "usb_bulk_msg in send_request_dev_dep_msg_in() returned %d\n", retval); +		return retval; +	} + +	return 0; +} +  static ssize_t usbtmc_read(struct file *filp, char __user *buf,  			   size_t count, loff_t *f_pos)  { @@ -389,51 +456,39 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,  		goto exit;  	} -	remaining = count; -	done = 0; +	if (data->rigol_quirk) { +		dev_dbg(dev, "usb_bulk_msg_in: count(%zu)\n", count); -	while (remaining > 0) { -		if (remaining > USBTMC_SIZE_IOBUFFER - 12 - 3) -			this_part = USBTMC_SIZE_IOBUFFER - 12 - 3; -		else -			this_part = remaining; +		retval = send_request_dev_dep_msg_in(data, count); -		/* Setup IO buffer for DEV_DEP_MSG_IN message -		 * Refer to class specs for details -		 */ -		buffer[0] = 2; -		buffer[1] = data->bTag; -		buffer[2] = ~(data->bTag); -		buffer[3] = 0; /* Reserved */ -		buffer[4] = (this_part) & 255; -		buffer[5] = ((this_part) >> 8) & 255; -		buffer[6] = ((this_part) >> 16) & 255; -		buffer[7] = ((this_part) >> 24) & 255; -		buffer[8] = data->TermCharEnabled * 2; -		/* Use term character? */ -		buffer[9] = data->TermChar; -		buffer[10] = 0; /* Reserved */ -		buffer[11] = 0; /* Reserved */ +		if (retval < 0) { +			if (data->auto_abort) +				usbtmc_ioctl_abort_bulk_out(data); +			goto exit; +		} +	} -		/* Send bulk URB */ -		retval = usb_bulk_msg(data->usb_dev, -				      usb_sndbulkpipe(data->usb_dev, -						      data->bulk_out), -				      buffer, 12, &actual, USBTMC_TIMEOUT); +	/* Loop until we have fetched everything we requested */ +	remaining = count; +	this_part = remaining; +	done = 0; -		/* Store bTag (in case we need to abort) */ -		data->bTag_last_write = data->bTag; +	while (remaining > 0) { +		if (!data->rigol_quirk) { +			dev_dbg(dev, "usb_bulk_msg_in: remaining(%zu), count(%zu)\n", remaining, count); -		/* Increment bTag -- and increment again if zero */ -		data->bTag++; -		if (!data->bTag) -			(data->bTag)++; +			if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3) +				this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3; +			else +				this_part = remaining; -		if (retval < 0) { +			retval = send_request_dev_dep_msg_in(data, this_part); +			if (retval < 0) {  			dev_err(dev, "usb_bulk_msg returned %d\n", retval); -			if (data->auto_abort) -				usbtmc_ioctl_abort_bulk_out(data); -			goto exit; +				if (data->auto_abort) +					usbtmc_ioctl_abort_bulk_out(data); +				goto exit; +			}  		}  		/* Send bulk URB */ @@ -443,51 +498,109 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,  				      buffer, USBTMC_SIZE_IOBUFFER, &actual,  				      USBTMC_TIMEOUT); +		dev_dbg(dev, "usb_bulk_msg: retval(%u), done(%zu), remaining(%zu), actual(%d)\n", retval, done, remaining, actual); +  		/* Store bTag (in case we need to abort) */  		data->bTag_last_read = data->bTag;  		if (retval < 0) { -			dev_err(dev, "Unable to read data, error %d\n", retval); +			dev_dbg(dev, "Unable to read data, error %d\n", retval);  			if (data->auto_abort)  				usbtmc_ioctl_abort_bulk_in(data);  			goto exit;  		} -		/* How many characters did the instrument send? */ -		n_characters = buffer[4] + -			       (buffer[5] << 8) + -			       (buffer[6] << 16) + -			       (buffer[7] << 24); +		/* Parse header in first packet */ +		if ((done == 0) || !data->rigol_quirk) { +			/* Sanity checks for the header */ +			if (actual < USBTMC_HEADER_SIZE) { +				dev_err(dev, "Device sent too small first packet: %u < %u\n", actual, USBTMC_HEADER_SIZE); +				if (data->auto_abort) +					usbtmc_ioctl_abort_bulk_in(data); +				goto exit; +			} -		/* Ensure the instrument doesn't lie about it */ -		if(n_characters > actual - 12) { -			dev_err(dev, "Device lies about message size: %u > %d\n", n_characters, actual - 12); -			n_characters = actual - 12; -		} +			if (buffer[0] != 2) { +				dev_err(dev, "Device sent reply with wrong MsgID: %u != 2\n", buffer[0]); +				if (data->auto_abort) +					usbtmc_ioctl_abort_bulk_in(data); +				goto exit; +			} -		/* Ensure the instrument doesn't send more back than requested */ -		if(n_characters > this_part) { -			dev_err(dev, "Device returns more than requested: %zu > %zu\n", done + n_characters, done + this_part); -			n_characters = this_part; -		} +			if (buffer[1] != data->bTag_last_write) { +				dev_err(dev, "Device sent reply with wrong bTag: %u != %u\n", buffer[1], data->bTag_last_write); +				if (data->auto_abort) +					usbtmc_ioctl_abort_bulk_in(data); +				goto exit; +			} -		/* Bound amount of data received by amount of data requested */ -		if (n_characters > this_part) -			n_characters = this_part; +			/* How many characters did the instrument send? */ +			n_characters = buffer[4] + +				       (buffer[5] << 8) + +				       (buffer[6] << 16) + +				       (buffer[7] << 24); -		/* Copy buffer to user space */ -		if (copy_to_user(buf + done, &buffer[12], n_characters)) { -			/* There must have been an addressing problem */ -			retval = -EFAULT; -			goto exit; +			if (n_characters > this_part) { +				dev_err(dev, "Device wants to return more data than requested: %u > %zu\n", n_characters, count); +				if (data->auto_abort) +					usbtmc_ioctl_abort_bulk_in(data); +				goto exit; +			} + +			/* Remove the USBTMC header */ +			actual -= USBTMC_HEADER_SIZE; + +			/* Check if the message is smaller than requested */ +			if (data->rigol_quirk) { +				if (remaining > n_characters) +					remaining = n_characters; +				/* Remove padding if it exists */ +				if (actual > remaining) +					actual = remaining; +			} +			else { +				if (this_part > n_characters) +					this_part = n_characters; +				/* Remove padding if it exists */ +				if (actual > this_part) +					actual = this_part; +			} + +			dev_dbg(dev, "Bulk-IN header: N_characters(%u), bTransAttr(%u)\n", n_characters, buffer[8]); + +			remaining -= actual; + +			/* Terminate if end-of-message bit received from device */ +			if ((buffer[8] & 0x01) && (actual >= n_characters)) +				remaining = 0; + +			dev_dbg(dev, "Bulk-IN header: remaining(%zu), buf(%p), buffer(%p) done(%zu)\n", remaining,buf,buffer,done); + + +			/* Copy buffer to user space */ +			if (copy_to_user(buf + done, &buffer[USBTMC_HEADER_SIZE], actual)) { +				/* There must have been an addressing problem */ +				retval = -EFAULT; +				goto exit; +			} +			done += actual;  		} +		else  { +			if (actual > remaining) +				actual = remaining; -		done += n_characters; -		/* Terminate if end-of-message bit recieved from device */ -		if ((buffer[8] &  0x01) && (actual >= n_characters + 12)) -			remaining = 0; -		else -			remaining -= n_characters; +			remaining -= actual; + +			dev_dbg(dev, "Bulk-IN header cont: actual(%u), done(%zu), remaining(%zu), buf(%p), buffer(%p)\n", actual, done, remaining,buf,buffer); + +			/* Copy buffer to user space */ +			if (copy_to_user(buf + done, buffer, actual)) { +				/* There must have been an addressing problem */ +				retval = -EFAULT; +				goto exit; +			} +			done += actual; +		}  	}  	/* Update file position value */ @@ -528,8 +641,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,  	done = 0;  	while (remaining > 0) { -		if (remaining > USBTMC_SIZE_IOBUFFER - 12) { -			this_part = USBTMC_SIZE_IOBUFFER - 12; +		if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE) { +			this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE;  			buffer[8] = 0;  		} else {  			this_part = remaining; @@ -539,24 +652,24 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,  		/* Setup IO buffer for DEV_DEP_MSG_OUT message */  		buffer[0] = 1;  		buffer[1] = data->bTag; -		buffer[2] = ~(data->bTag); +		buffer[2] = ~data->bTag;  		buffer[3] = 0; /* Reserved */ -		buffer[4] = this_part & 255; -		buffer[5] = (this_part >> 8) & 255; -		buffer[6] = (this_part >> 16) & 255; -		buffer[7] = (this_part >> 24) & 255; +		buffer[4] = this_part >> 0; +		buffer[5] = this_part >> 8; +		buffer[6] = this_part >> 16; +		buffer[7] = this_part >> 24;  		/* buffer[8] is set above... */  		buffer[9] = 0; /* Reserved */  		buffer[10] = 0; /* Reserved */  		buffer[11] = 0; /* Reserved */ -		if (copy_from_user(&buffer[12], buf + done, this_part)) { +		if (copy_from_user(&buffer[USBTMC_HEADER_SIZE], buf + done, this_part)) {  			retval = -EFAULT;  			goto exit;  		} -		n_bytes = roundup(12 + this_part, 4); -		memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part)); +		n_bytes = roundup(USBTMC_HEADER_SIZE + this_part, 4); +		memset(buffer + USBTMC_HEADER_SIZE + this_part, 0, n_bytes - (USBTMC_HEADER_SIZE + this_part));  		do {  			retval = usb_bulk_msg(data->usb_dev, @@ -636,7 +749,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)  	for (n = 0; n < current_setting->desc.bNumEndpoints; n++) {  		desc = ¤t_setting->endpoint[n].desc;  		if (desc->bEndpointAddress == data->bulk_in) -			max_size = le16_to_cpu(desc->wMaxPacketSize); +			max_size = usb_endpoint_maxp(desc);  	}  	if (max_size == 0) { @@ -719,50 +832,32 @@ exit:  static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data)  { -	u8 *buffer;  	int rv; -	buffer = kmalloc(2, GFP_KERNEL); -	if (!buffer) -		return -ENOMEM; -  	rv = usb_clear_halt(data->usb_dev,  			    usb_sndbulkpipe(data->usb_dev, data->bulk_out));  	if (rv < 0) {  		dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",  			rv); -		goto exit; +		return rv;  	} -	rv = 0; - -exit: -	kfree(buffer); -	return rv; +	return 0;  }  static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data)  { -	u8 *buffer;  	int rv; -	buffer = kmalloc(2, GFP_KERNEL); -	if (!buffer) -		return -ENOMEM; -  	rv = usb_clear_halt(data->usb_dev,  			    usb_rcvbulkpipe(data->usb_dev, data->bulk_in));  	if (rv < 0) {  		dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",  			rv); -		goto exit; +		return rv;  	} -	rv = 0; - -exit: -	kfree(buffer); -	return rv; +	return 0;  }  static int get_capabilities(struct usbtmc_device_data *data) @@ -807,7 +902,7 @@ err_out:  }  #define capability_attribute(name)					\ -static ssize_t show_##name(struct device *dev,				\ +static ssize_t name##_show(struct device *dev,				\  			   struct device_attribute *attr, char *buf)	\  {									\  	struct usb_interface *intf = to_usb_interface(dev);		\ @@ -815,7 +910,7 @@ static ssize_t show_##name(struct device *dev,				\  									\  	return sprintf(buf, "%d\n", data->capabilities.name);		\  }									\ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) +static DEVICE_ATTR_RO(name)  capability_attribute(interface_capabilities);  capability_attribute(device_capabilities); @@ -834,7 +929,7 @@ static struct attribute_group capability_attr_grp = {  	.attrs = capability_attrs,  }; -static ssize_t show_TermChar(struct device *dev, +static ssize_t TermChar_show(struct device *dev,  			     struct device_attribute *attr, char *buf)  {  	struct usb_interface *intf = to_usb_interface(dev); @@ -843,7 +938,7 @@ static ssize_t show_TermChar(struct device *dev,  	return sprintf(buf, "%c\n", data->TermChar);  } -static ssize_t store_TermChar(struct device *dev, +static ssize_t TermChar_store(struct device *dev,  			      struct device_attribute *attr,  			      const char *buf, size_t count)  { @@ -855,10 +950,10 @@ static ssize_t store_TermChar(struct device *dev,  	data->TermChar = buf[0];  	return count;  } -static DEVICE_ATTR(TermChar, S_IRUGO, show_TermChar, store_TermChar); +static DEVICE_ATTR_RW(TermChar);  #define data_attribute(name)						\ -static ssize_t show_##name(struct device *dev,				\ +static ssize_t name##_show(struct device *dev,				\  			   struct device_attribute *attr, char *buf)	\  {									\  	struct usb_interface *intf = to_usb_interface(dev);		\ @@ -866,7 +961,7 @@ static ssize_t show_##name(struct device *dev,				\  									\  	return sprintf(buf, "%d\n", data->name);			\  }									\ -static ssize_t store_##name(struct device *dev,				\ +static ssize_t name##_store(struct device *dev,				\  			    struct device_attribute *attr,		\  			    const char *buf, size_t count)		\  {									\ @@ -884,7 +979,7 @@ static ssize_t store_##name(struct device *dev,				\  	else								\  		return count;						\  }									\ -static DEVICE_ATTR(name, S_IRUGO, show_##name, store_##name) +static DEVICE_ATTR_RW(name)  data_attribute(TermCharEnabled);  data_attribute(auto_abort); @@ -1008,7 +1103,7 @@ static int usbtmc_probe(struct usb_interface *intf,  	dev_dbg(&intf->dev, "%s called\n", __func__); -	data = kmalloc(sizeof(struct usbtmc_device_data), GFP_KERNEL); +	data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);  	if (!data) {  		dev_err(&intf->dev, "Unable to allocate kernel memory\n");  		return -ENOMEM; @@ -1022,6 +1117,20 @@ static int usbtmc_probe(struct usb_interface *intf,  	mutex_init(&data->io_mutex);  	data->zombie = 0; +	/* Determine if it is a Rigol or not */ +	data->rigol_quirk = 0; +	dev_dbg(&intf->dev, "Trying to find if device Vendor 0x%04X Product 0x%04X has the RIGOL quirk\n", +		le16_to_cpu(data->usb_dev->descriptor.idVendor), +		le16_to_cpu(data->usb_dev->descriptor.idProduct)); +	for(n = 0; usbtmc_id_quirk[n].idVendor > 0; n++) { +		if ((usbtmc_id_quirk[n].idVendor == le16_to_cpu(data->usb_dev->descriptor.idVendor)) && +		    (usbtmc_id_quirk[n].idProduct == le16_to_cpu(data->usb_dev->descriptor.idProduct))) { +			dev_dbg(&intf->dev, "Setting this device as having the RIGOL quirk\n"); +			data->rigol_quirk = 1; +			break; +		} +	} +  	/* Initialize USBTMC bTag and other fields */  	data->bTag	= 1;  	data->TermCharEnabled = 0; @@ -1117,21 +1226,6 @@ static struct usb_driver usbtmc_driver = {  	.resume		= usbtmc_resume,  }; -static int __init usbtmc_init(void) -{ -	int retcode; - -	retcode = usb_register(&usbtmc_driver); -	if (retcode) -		printk(KERN_ERR KBUILD_MODNAME": Unable to register driver\n"); -	return retcode; -} -module_init(usbtmc_init); - -static void __exit usbtmc_exit(void) -{ -	usb_deregister(&usbtmc_driver); -} -module_exit(usbtmc_exit); +module_usb_driver(usbtmc_driver);  MODULE_LICENSE("GPL");  | 
