[kernel] r15878 - in dists/sid/linux-2.6/debian: . patches/features/all patches/series

Ben Hutchings benh at alioth.debian.org
Tue Jun 15 12:45:17 UTC 2010


Author: benh
Date: Tue Jun 15 12:45:12 2010
New Revision: 15878

Log:
usb-serial: Add generic USB WWAN code, backported by Mark Hymers (Closes: #585661)

- option, qcserial: Use generic USB WWAN code
- qcserial: Add support for Qualcomm Gobi 2000 devices

Added:
   dists/sid/linux-2.6/debian/patches/features/all/USB-option-Use-generic-USB-wwan-code.patch
   dists/sid/linux-2.6/debian/patches/features/all/USB-qcserial-Add-support-for-Qualcomm-Gobi-2000.patch
   dists/sid/linux-2.6/debian/patches/features/all/USB-qcserial-Use-generic-USB-wwan-code.patch
   dists/sid/linux-2.6/debian/patches/features/all/usb-serial-Add-generic-USB-wwan-support.patch
Modified:
   dists/sid/linux-2.6/debian/changelog
   dists/sid/linux-2.6/debian/patches/series/16

Modified: dists/sid/linux-2.6/debian/changelog
==============================================================================
--- dists/sid/linux-2.6/debian/changelog	Tue Jun 15 12:36:19 2010	(r15877)
+++ dists/sid/linux-2.6/debian/changelog	Tue Jun 15 12:45:12 2010	(r15878)
@@ -25,6 +25,10 @@
   * Revert "vlan/macvlan: propagate transmission state to upper layers"
     (Closes: #585770)
   * linux-base: Don't identify LVM2 PVs by UUID (Closes: #585852)
+  * usb-serial: Add generic USB WWAN code, backported by Mark Hymers
+    (Closes: #585661)
+    - option, qcserial: Use generic USB WWAN code
+    - qcserial: Add support for Qualcomm Gobi 2000 devices
 
   [ Aurelien Jarno ]
   * [sh4] fix sh_tmu clocksource following recent nohz changes.

Added: dists/sid/linux-2.6/debian/patches/features/all/USB-option-Use-generic-USB-wwan-code.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux-2.6/debian/patches/features/all/USB-option-Use-generic-USB-wwan-code.patch	Tue Jun 15 12:45:12 2010	(r15878)
@@ -0,0 +1,735 @@
+From: Matthew Garrett <mjg at redhat.com>
+Date: Thu, 1 Apr 2010 12:31:08 -0400
+Subject: [PATCH] USB: option: Use generic USB wwan code
+
+commit 8b4c6a3ab596961b784659c71dc24b341f938a1a upstream.
+
+As this code was simply factored out of option, this is a simple
+conversion.
+
+Signed-off-by: Matthew Garrett <mjg at redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
+[mhy: Backport to 2.6.32]
+---
+--- a/drivers/usb/serial/Kconfig
++++ b/drivers/usb/serial/Kconfig
+@@ -581,6 +581,7 @@ config USB_SERIAL_WWAN
+ 
+ config USB_SERIAL_OPTION
+ 	tristate "USB driver for GSM and CDMA modems"
++	select USB_SERIAL_WWAN
+ 	help
+ 	  Say Y here if you have a GSM or CDMA modem that's connected to USB.
+ 
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -41,35 +41,14 @@
+ #include <linux/bitops.h>
+ #include <linux/usb.h>
+ #include <linux/usb/serial.h>
++#include "usb-wwan.h"
+ 
+ /* Function prototypes */
+ static int  option_probe(struct usb_serial *serial,
+ 			const struct usb_device_id *id);
+-static int  option_open(struct tty_struct *tty, struct usb_serial_port *port);
+-static void option_close(struct usb_serial_port *port);
+-static void option_dtr_rts(struct usb_serial_port *port, int on);
+-
+-static int  option_startup(struct usb_serial *serial);
+-static void option_disconnect(struct usb_serial *serial);
+-static void option_release(struct usb_serial *serial);
+-static int  option_write_room(struct tty_struct *tty);
+-
++static int option_send_setup(struct usb_serial_port *port);
+ static void option_instat_callback(struct urb *urb);
+ 
+-static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
+-			const unsigned char *buf, int count);
+-static int  option_chars_in_buffer(struct tty_struct *tty);
+-static void option_set_termios(struct tty_struct *tty,
+-			struct usb_serial_port *port, struct ktermios *old);
+-static int  option_tiocmget(struct tty_struct *tty, struct file *file);
+-static int  option_tiocmset(struct tty_struct *tty, struct file *file,
+-				unsigned int set, unsigned int clear);
+-static int  option_send_setup(struct usb_serial_port *port);
+-#ifdef CONFIG_PM
+-static int  option_suspend(struct usb_serial *serial, pm_message_t message);
+-static int  option_resume(struct usb_serial *serial);
+-#endif
+-
+ /* Vendor and product IDs */
+ #define OPTION_VENDOR_ID			0x0AF0
+ #define OPTION_PRODUCT_COLT			0x5000
+@@ -680,22 +659,22 @@
+ 	.id_table          = option_ids,
+ 	.num_ports         = 1,
+ 	.probe             = option_probe,
+-	.open              = option_open,
+-	.close             = option_close,
+-	.dtr_rts	   = option_dtr_rts,
+-	.write             = option_write,
+-	.write_room        = option_write_room,
+-	.chars_in_buffer   = option_chars_in_buffer,
+-	.set_termios       = option_set_termios,
+-	.tiocmget          = option_tiocmget,
+-	.tiocmset          = option_tiocmset,
+-	.attach            = option_startup,
+-	.disconnect        = option_disconnect,
+-	.release           = option_release,
++	.open              = usb_wwan_open,
++	.close             = usb_wwan_close,
++	.dtr_rts	   = usb_wwan_dtr_rts,
++	.write             = usb_wwan_write,
++	.write_room        = usb_wwan_write_room,
++	.chars_in_buffer   = usb_wwan_chars_in_buffer,
++	.set_termios       = usb_wwan_set_termios,
++	.tiocmget          = usb_wwan_tiocmget,
++	.tiocmset          = usb_wwan_tiocmset,
++	.attach            = usb_wwan_startup,
++	.disconnect        = usb_wwan_disconnect,
++	.release           = usb_wwan_release,
+ 	.read_int_callback = option_instat_callback,
+ #ifdef CONFIG_PM
+-	.suspend           = option_suspend,
+-	.resume            = option_resume,
++	.suspend           = usb_wwan_suspend,
++	.resume            = usb_wwan_resume,
+ #endif
+ };
+ 
+@@ -708,12 +687,6 @@
+ #define IN_BUFLEN 4096
+ #define OUT_BUFLEN 4096
+ 
+-struct option_intf_private {
+-	spinlock_t susp_lock;
+-	unsigned int suspended:1;
+-	int in_flight;
+-};
+-
+ struct option_port_private {
+ 	/* Input endpoints and buffer for this port */
+ 	struct urb *in_urbs[N_IN_URB];
+@@ -770,209 +743,21 @@
+ static int option_probe(struct usb_serial *serial,
+ 			const struct usb_device_id *id)
+ {
+-	struct option_intf_private *data;
++	struct usb_wwan_intf_private *data;
+ 	/* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
+ 	if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID &&
+ 		serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 &&
+ 		serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8)
+ 		return -ENODEV;
+ 
+-	data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL);
++	data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+ 	if (!data)
+ 		return -ENOMEM;
++	data->send_setup = option_send_setup;
+ 	spin_lock_init(&data->susp_lock);
+ 	return 0;
+ }
+ 
+-static void option_set_termios(struct tty_struct *tty,
+-		struct usb_serial_port *port, struct ktermios *old_termios)
+-{
+-	dbg("%s", __func__);
+-	/* Doesn't support option setting */
+-	tty_termios_copy_hw(tty->termios, old_termios);
+-	option_send_setup(port);
+-}
+-
+-static int option_tiocmget(struct tty_struct *tty, struct file *file)
+-{
+-	struct usb_serial_port *port = tty->driver_data;
+-	unsigned int value;
+-	struct option_port_private *portdata;
+-
+-	portdata = usb_get_serial_port_data(port);
+-
+-	value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
+-		((portdata->dtr_state) ? TIOCM_DTR : 0) |
+-		((portdata->cts_state) ? TIOCM_CTS : 0) |
+-		((portdata->dsr_state) ? TIOCM_DSR : 0) |
+-		((portdata->dcd_state) ? TIOCM_CAR : 0) |
+-		((portdata->ri_state) ? TIOCM_RNG : 0);
+-
+-	return value;
+-}
+-
+-static int option_tiocmset(struct tty_struct *tty, struct file *file,
+-			unsigned int set, unsigned int clear)
+-{
+-	struct usb_serial_port *port = tty->driver_data;
+-	struct option_port_private *portdata;
+-
+-	portdata = usb_get_serial_port_data(port);
+-
+-	/* FIXME: what locks portdata fields ? */
+-	if (set & TIOCM_RTS)
+-		portdata->rts_state = 1;
+-	if (set & TIOCM_DTR)
+-		portdata->dtr_state = 1;
+-
+-	if (clear & TIOCM_RTS)
+-		portdata->rts_state = 0;
+-	if (clear & TIOCM_DTR)
+-		portdata->dtr_state = 0;
+-	return option_send_setup(port);
+-}
+-
+-/* Write */
+-static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
+-			const unsigned char *buf, int count)
+-{
+-	struct option_port_private *portdata;
+-	struct option_intf_private *intfdata;
+-	int i;
+-	int left, todo;
+-	struct urb *this_urb = NULL; /* spurious */
+-	int err;
+-	unsigned long flags;
+-
+-	portdata = usb_get_serial_port_data(port);
+-	intfdata = port->serial->private;
+-
+-	dbg("%s: write (%d chars)", __func__, count);
+-
+-	i = 0;
+-	left = count;
+-	for (i = 0; left > 0 && i < N_OUT_URB; i++) {
+-		todo = left;
+-		if (todo > OUT_BUFLEN)
+-			todo = OUT_BUFLEN;
+-
+-		this_urb = portdata->out_urbs[i];
+-		if (test_and_set_bit(i, &portdata->out_busy)) {
+-			if (time_before(jiffies,
+-					portdata->tx_start_time[i] + 10 * HZ))
+-				continue;
+-			usb_unlink_urb(this_urb);
+-			continue;
+-		}
+-		dbg("%s: endpoint %d buf %d", __func__,
+-			usb_pipeendpoint(this_urb->pipe), i);
+-
+-		err = usb_autopm_get_interface_async(port->serial->interface);
+-		if (err < 0)
+-			break;
+-
+-		/* send the data */
+-		memcpy(this_urb->transfer_buffer, buf, todo);
+-		this_urb->transfer_buffer_length = todo;
+-
+-		spin_lock_irqsave(&intfdata->susp_lock, flags);
+-		if (intfdata->suspended) {
+-			usb_anchor_urb(this_urb, &portdata->delayed);
+-			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+-		} else {
+-			intfdata->in_flight++;
+-			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+-			err = usb_submit_urb(this_urb, GFP_ATOMIC);
+-			if (err) {
+-				dbg("usb_submit_urb %p (write bulk) failed "
+-					"(%d)", this_urb, err);
+-				clear_bit(i, &portdata->out_busy);
+-				spin_lock_irqsave(&intfdata->susp_lock, flags);
+-				intfdata->in_flight--;
+-				spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+-				continue;
+-			}
+-		}
+-
+-		portdata->tx_start_time[i] = jiffies;
+-		buf += todo;
+-		left -= todo;
+-	}
+-
+-	count -= left;
+-	dbg("%s: wrote (did %d)", __func__, count);
+-	return count;
+-}
+-
+-static void option_indat_callback(struct urb *urb)
+-{
+-	int err;
+-	int endpoint;
+-	struct usb_serial_port *port;
+-	struct tty_struct *tty;
+-	unsigned char *data = urb->transfer_buffer;
+-	int status = urb->status;
+-
+-	dbg("%s: %p", __func__, urb);
+-
+-	endpoint = usb_pipeendpoint(urb->pipe);
+-	port =  urb->context;
+-
+-	if (status) {
+-		dbg("%s: nonzero status: %d on endpoint %02x.",
+-		    __func__, status, endpoint);
+-	} else {
+-		tty = tty_port_tty_get(&port->port);
+-		if (urb->actual_length) {
+-			tty_buffer_request_room(tty, urb->actual_length);
+-			tty_insert_flip_string(tty, data, urb->actual_length);
+-			tty_flip_buffer_push(tty);
+-		} else 
+-			dbg("%s: empty read urb received", __func__);
+-		tty_kref_put(tty);
+-
+-		/* Resubmit urb so we continue receiving */
+-		if (port->port.count && status != -ESHUTDOWN) {
+-			err = usb_submit_urb(urb, GFP_ATOMIC);
+-			if (err)
+-				printk(KERN_ERR "%s: resubmit read urb failed. "
+-					"(%d)", __func__, err);
+-			else
+-				usb_mark_last_busy(port->serial->dev);
+-		}
+-
+-	}
+-	return;
+-}
+-
+-static void option_outdat_callback(struct urb *urb)
+-{
+-	struct usb_serial_port *port;
+-	struct option_port_private *portdata;
+-	struct option_intf_private *intfdata;
+-	int i;
+-
+-	dbg("%s", __func__);
+-
+-	port =  urb->context;
+-	intfdata = port->serial->private;
+-
+-	usb_serial_port_softint(port);
+-	usb_autopm_put_interface_async(port->serial->interface);
+-	portdata = usb_get_serial_port_data(port);
+-	spin_lock(&intfdata->susp_lock);
+-	intfdata->in_flight--;
+-	spin_unlock(&intfdata->susp_lock);
+-
+-	for (i = 0; i < N_OUT_URB; ++i) {
+-		if (portdata->out_urbs[i] == urb) {
+-			smp_mb__before_clear_bit();
+-			clear_bit(i, &portdata->out_busy);
+-			break;
+-		}
+-	}
+-}
+-
+ static void option_instat_callback(struct urb *urb)
+ {
+ 	int err;
+@@ -1029,183 +814,6 @@
+ 	}
+ }
+ 
+-static int option_write_room(struct tty_struct *tty)
+-{
+-	struct usb_serial_port *port = tty->driver_data;
+-	struct option_port_private *portdata;
+-	int i;
+-	int data_len = 0;
+-	struct urb *this_urb;
+-
+-	portdata = usb_get_serial_port_data(port);
+-
+-	for (i = 0; i < N_OUT_URB; i++) {
+-		this_urb = portdata->out_urbs[i];
+-		if (this_urb && !test_bit(i, &portdata->out_busy))
+-			data_len += OUT_BUFLEN;
+-	}
+-
+-	dbg("%s: %d", __func__, data_len);
+-	return data_len;
+-}
+-
+-static int option_chars_in_buffer(struct tty_struct *tty)
+-{
+-	struct usb_serial_port *port = tty->driver_data;
+-	struct option_port_private *portdata;
+-	int i;
+-	int data_len = 0;
+-	struct urb *this_urb;
+-
+-	portdata = usb_get_serial_port_data(port);
+-
+-	for (i = 0; i < N_OUT_URB; i++) {
+-		this_urb = portdata->out_urbs[i];
+-		/* FIXME: This locking is insufficient as this_urb may
+-		   go unused during the test */
+-		if (this_urb && test_bit(i, &portdata->out_busy))
+-			data_len += this_urb->transfer_buffer_length;
+-	}
+-	dbg("%s: %d", __func__, data_len);
+-	return data_len;
+-}
+-
+-static int option_open(struct tty_struct *tty, struct usb_serial_port *port)
+-{
+-	struct option_port_private *portdata;
+-	struct option_intf_private *intfdata;
+-	struct usb_serial *serial = port->serial;
+-	int i, err;
+-	struct urb *urb;
+-
+-	portdata = usb_get_serial_port_data(port);
+-	intfdata = serial->private;
+-
+-	dbg("%s", __func__);
+-
+-	/* Start reading from the IN endpoint */
+-	for (i = 0; i < N_IN_URB; i++) {
+-		urb = portdata->in_urbs[i];
+-		if (!urb)
+-			continue;
+-		err = usb_submit_urb(urb, GFP_KERNEL);
+-		if (err) {
+-			dbg("%s: submit urb %d failed (%d) %d",
+-				__func__, i, err,
+-				urb->transfer_buffer_length);
+-		}
+-	}
+-
+-	option_send_setup(port);
+-
+-	serial->interface->needs_remote_wakeup = 1;
+-	spin_lock_irq(&intfdata->susp_lock);
+-	portdata->opened = 1;
+-	spin_unlock_irq(&intfdata->susp_lock);
+-	usb_autopm_put_interface(serial->interface);
+-
+-	return 0;
+-}
+-
+-static void option_dtr_rts(struct usb_serial_port *port, int on)
+-{
+-	struct usb_serial *serial = port->serial;
+-	struct option_port_private *portdata;
+-
+-	dbg("%s", __func__);
+-	portdata = usb_get_serial_port_data(port);
+-	mutex_lock(&serial->disc_mutex);
+-	portdata->rts_state = on;
+-	portdata->dtr_state = on;
+-	if (serial->dev)
+-		option_send_setup(port);
+-	mutex_unlock(&serial->disc_mutex);
+-}
+-
+-
+-static void option_close(struct usb_serial_port *port)
+-{
+-	int i;
+-	struct usb_serial *serial = port->serial;
+-	struct option_port_private *portdata;
+-	struct option_intf_private *intfdata = port->serial->private;
+-
+-	dbg("%s", __func__);
+-	portdata = usb_get_serial_port_data(port);
+-
+-	if (serial->dev) {
+-		/* Stop reading/writing urbs */
+-		spin_lock_irq(&intfdata->susp_lock);
+-		portdata->opened = 0;
+-		spin_unlock_irq(&intfdata->susp_lock);
+-
+-		for (i = 0; i < N_IN_URB; i++)
+-			usb_kill_urb(portdata->in_urbs[i]);
+-		for (i = 0; i < N_OUT_URB; i++)
+-			usb_kill_urb(portdata->out_urbs[i]);
+-		usb_autopm_get_interface(serial->interface);
+-		serial->interface->needs_remote_wakeup = 0;
+-	}
+-}
+-
+-/* Helper functions used by option_setup_urbs */
+-static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
+-		int dir, void *ctx, char *buf, int len,
+-		void (*callback)(struct urb *))
+-{
+-	struct urb *urb;
+-
+-	if (endpoint == -1)
+-		return NULL;		/* endpoint not needed */
+-
+-	urb = usb_alloc_urb(0, GFP_KERNEL);		/* No ISO */
+-	if (urb == NULL) {
+-		dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
+-		return NULL;
+-	}
+-
+-		/* Fill URB using supplied data. */
+-	usb_fill_bulk_urb(urb, serial->dev,
+-		      usb_sndbulkpipe(serial->dev, endpoint) | dir,
+-		      buf, len, callback, ctx);
+-
+-	return urb;
+-}
+-
+-/* Setup urbs */
+-static void option_setup_urbs(struct usb_serial *serial)
+-{
+-	int i, j;
+-	struct usb_serial_port *port;
+-	struct option_port_private *portdata;
+-
+-	dbg("%s", __func__);
+-
+-	for (i = 0; i < serial->num_ports; i++) {
+-		port = serial->port[i];
+-		portdata = usb_get_serial_port_data(port);
+-
+-		/* Do indat endpoints first */
+-		for (j = 0; j < N_IN_URB; ++j) {
+-			portdata->in_urbs[j] = option_setup_urb(serial,
+-					port->bulk_in_endpointAddress,
+-					USB_DIR_IN, port,
+-					portdata->in_buffer[j],
+-					IN_BUFLEN, option_indat_callback);
+-		}
+-
+-		/* outdat endpoints */
+-		for (j = 0; j < N_OUT_URB; ++j) {
+-			portdata->out_urbs[j] = option_setup_urb(serial,
+-					port->bulk_out_endpointAddress,
+-					USB_DIR_OUT, port,
+-					portdata->out_buffer[j],
+-					OUT_BUFLEN, option_outdat_callback);
+-		}
+-	}
+-}
+-
+-
+ /** send RTS/DTR state to the port.
+  *
+  * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN
+@@ -1231,224 +839,6 @@
+ 		0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ }
+ 
+-static int option_startup(struct usb_serial *serial)
+-{
+-	int i, j, err;
+-	struct usb_serial_port *port;
+-	struct option_port_private *portdata;
+-	u8 *buffer;
+-
+-	dbg("%s", __func__);
+-
+-	/* Now setup per port private data */
+-	for (i = 0; i < serial->num_ports; i++) {
+-		port = serial->port[i];
+-		portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+-		if (!portdata) {
+-			dbg("%s: kmalloc for option_port_private (%d) failed!.",
+-					__func__, i);
+-			return 1;
+-		}
+-		init_usb_anchor(&portdata->delayed);
+-
+-		for (j = 0; j < N_IN_URB; j++) {
+-			buffer = (u8 *)__get_free_page(GFP_KERNEL);
+-			if (!buffer)
+-				goto bail_out_error;
+-			portdata->in_buffer[j] = buffer;
+-		}
+-
+-		for (j = 0; j < N_OUT_URB; j++) {
+-			buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
+-			if (!buffer)
+-				goto bail_out_error2;
+-			portdata->out_buffer[j] = buffer;
+-		}
+-
+-		usb_set_serial_port_data(port, portdata);
+-
+-		if (!port->interrupt_in_urb)
+-			continue;
+-		err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+-		if (err)
+-			dbg("%s: submit irq_in urb failed %d",
+-				__func__, err);
+-	}
+-	option_setup_urbs(serial);
+-	return 0;
+-
+-bail_out_error2:
+-	for (j = 0; j < N_OUT_URB; j++)
+-		kfree(portdata->out_buffer[j]);
+-bail_out_error:
+-	for (j = 0; j < N_IN_URB; j++)
+-		if (portdata->in_buffer[j])
+-			free_page((unsigned long)portdata->in_buffer[j]);
+-	kfree(portdata);
+-	return 1;
+-}
+-
+-static void stop_read_write_urbs(struct usb_serial *serial)
+-{
+-	int i, j;
+-	struct usb_serial_port *port;
+-	struct option_port_private *portdata;
+-
+-	/* Stop reading/writing urbs */
+-	for (i = 0; i < serial->num_ports; ++i) {
+-		port = serial->port[i];
+-		portdata = usb_get_serial_port_data(port);
+-		for (j = 0; j < N_IN_URB; j++)
+-			usb_kill_urb(portdata->in_urbs[j]);
+-		for (j = 0; j < N_OUT_URB; j++)
+-			usb_kill_urb(portdata->out_urbs[j]);
+-	}
+-}
+-
+-static void option_disconnect(struct usb_serial *serial)
+-{
+-	dbg("%s", __func__);
+-
+-	stop_read_write_urbs(serial);
+-}
+-
+-static void option_release(struct usb_serial *serial)
+-{
+-	int i, j;
+-	struct usb_serial_port *port;
+-	struct option_port_private *portdata;
+-
+-	dbg("%s", __func__);
+-
+-	/* Now free them */
+-	for (i = 0; i < serial->num_ports; ++i) {
+-		port = serial->port[i];
+-		portdata = usb_get_serial_port_data(port);
+-
+-		for (j = 0; j < N_IN_URB; j++) {
+-			if (portdata->in_urbs[j]) {
+-				usb_free_urb(portdata->in_urbs[j]);
+-				free_page((unsigned long)
+-					portdata->in_buffer[j]);
+-				portdata->in_urbs[j] = NULL;
+-			}
+-		}
+-		for (j = 0; j < N_OUT_URB; j++) {
+-			if (portdata->out_urbs[j]) {
+-				usb_free_urb(portdata->out_urbs[j]);
+-				kfree(portdata->out_buffer[j]);
+-				portdata->out_urbs[j] = NULL;
+-			}
+-		}
+-	}
+-
+-	/* Now free per port private data */
+-	for (i = 0; i < serial->num_ports; i++) {
+-		port = serial->port[i];
+-		kfree(usb_get_serial_port_data(port));
+-	}
+-}
+-
+-#ifdef CONFIG_PM
+-static int option_suspend(struct usb_serial *serial, pm_message_t message)
+-{
+-	struct option_intf_private *intfdata = serial->private;
+-	int b;
+-
+-	dbg("%s entered", __func__);
+-
+-	if (serial->dev->auto_pm) {
+-		spin_lock_irq(&intfdata->susp_lock);
+-		b = intfdata->in_flight;
+-		spin_unlock_irq(&intfdata->susp_lock);
+-
+-		if (b)
+-			return -EBUSY;
+-	}
+-
+-	spin_lock_irq(&intfdata->susp_lock);
+-	intfdata->suspended = 1;
+-	spin_unlock_irq(&intfdata->susp_lock);
+-	stop_read_write_urbs(serial);
+-
+-	return 0;
+-}
+-
+-static void play_delayed(struct usb_serial_port *port)
+-{
+-	struct option_intf_private *data;
+-	struct option_port_private *portdata;
+-	struct urb *urb;
+-	int err;
+-
+-	portdata = usb_get_serial_port_data(port);
+-	data = port->serial->private;
+-	while ((urb = usb_get_from_anchor(&portdata->delayed))) {
+-		err = usb_submit_urb(urb, GFP_ATOMIC);
+-		if (!err)
+-			data->in_flight++;
+-	}
+-}
+-
+-static int option_resume(struct usb_serial *serial)
+-{
+-	int i, j;
+-	struct usb_serial_port *port;
+-	struct option_intf_private *intfdata = serial->private;
+-	struct option_port_private *portdata;
+-	struct urb *urb;
+-	int err = 0;
+-
+-	dbg("%s entered", __func__);
+-	/* get the interrupt URBs resubmitted unconditionally */
+-	for (i = 0; i < serial->num_ports; i++) {
+-		port = serial->port[i];
+-		if (!port->interrupt_in_urb) {
+-			dbg("%s: No interrupt URB for port %d\n", __func__, i);
+-			continue;
+-		}
+-		err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
+-		dbg("Submitted interrupt URB for port %d (result %d)", i, err);
+-		if (err < 0) {
+-			err("%s: Error %d for interrupt URB of port%d",
+-				 __func__, err, i);
+-			goto err_out;
+-		}
+-	}
+-
+-	for (i = 0; i < serial->num_ports; i++) {
+-		/* walk all ports */
+-		port = serial->port[i];
+-		portdata = usb_get_serial_port_data(port);
+-
+-		/* skip closed ports */
+-		spin_lock_irq(&intfdata->susp_lock);
+-		if (!portdata->opened) {
+-			spin_unlock_irq(&intfdata->susp_lock);
+-			continue;
+-		}
+-
+-		for (j = 0; j < N_IN_URB; j++) {
+-			urb = portdata->in_urbs[j];
+-			err = usb_submit_urb(urb, GFP_ATOMIC);
+-			if (err < 0) {
+-				err("%s: Error %d for bulk URB %d",
+-					 __func__, err, i);
+-				spin_unlock_irq(&intfdata->susp_lock);
+-				goto err_out;
+-			}
+-		}
+-		play_delayed(port);
+-		spin_unlock_irq(&intfdata->susp_lock);
+-	}
+-	spin_lock_irq(&intfdata->susp_lock);
+-	intfdata->suspended = 0;
+-	spin_unlock_irq(&intfdata->susp_lock);
+-err_out:
+-	return err;
+-}
+-#endif
+-
+ MODULE_AUTHOR(DRIVER_AUTHOR);
+ MODULE_DESCRIPTION(DRIVER_DESC);
+ MODULE_VERSION(DRIVER_VERSION);

Added: dists/sid/linux-2.6/debian/patches/features/all/USB-qcserial-Add-support-for-Qualcomm-Gobi-2000.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux-2.6/debian/patches/features/all/USB-qcserial-Add-support-for-Qualcomm-Gobi-2000.patch	Tue Jun 15 12:45:12 2010	(r15878)
@@ -0,0 +1,104 @@
+From: Anssi Hannula <anssi.hannula at gmail.com>
+Date: Thu, 1 Apr 2010 12:31:10 -0400
+Subject: [PATCH] USB: qcserial: Add support for Qualcomm Gobi 2000 devices
+
+commit e07896e62abbf7a741a5cd5b25ba7637bdf91ad0 upstream.
+
+Add ids for Qualcomm Gobi 2000 QDL and Modem modes. Gobi 2000 has a
+single altsetting in QDL mode, so adapt code to handle that.
+
+Firmware upload protocol is also slightly different, with an
+additional firmware file. However, qcserial doesn't handle firmware
+uploading.
+
+Tested on Lenovo Thinkpad T510.
+
+Signed-off-by: Anssi Hannula <anssi.hannula at gmail.com>
+Signed-off-by: Matthew Garrett <mjg at redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
+---
+ drivers/usb/serial/qcserial.c |   42 ++++++++++++++++++++++++----------------
+ 1 files changed, 25 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
+index 9215f6c..04bb759 100644
+--- a/drivers/usb/serial/qcserial.c
++++ b/drivers/usb/serial/qcserial.c
+@@ -78,6 +78,8 @@ static const struct usb_device_id id_table[] = {
+ 	{USB_DEVICE(0x1199, 0x900a)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
+ 	{USB_DEVICE(0x16d8, 0x8001)},	/* CMDTech Gobi 2000 QDL device (VU922) */
+ 	{USB_DEVICE(0x16d8, 0x8002)},	/* CMDTech Gobi 2000 Modem device (VU922) */
++	{USB_DEVICE(0x05c6, 0x9204)},	/* Gobi 2000 QDL device */
++	{USB_DEVICE(0x05c6, 0x9205)},	/* Gobi 2000 Modem device */
+ 	{ }				/* Terminating entry */
+ };
+ MODULE_DEVICE_TABLE(usb, id_table);
+@@ -95,6 +97,7 @@ static struct usb_driver qcdriver = {
+ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+ {
+ 	struct usb_wwan_intf_private *data;
++	struct usb_host_interface *intf = serial->interface->cur_altsetting;
+ 	int retval = -ENODEV;
+ 	__u8 nintf;
+ 	__u8 ifnum;
+@@ -103,7 +106,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+ 
+ 	nintf = serial->dev->actconfig->desc.bNumInterfaces;
+ 	dbg("Num Interfaces = %d", nintf);
+-	ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
++	ifnum = intf->desc.bInterfaceNumber;
+ 	dbg("This Interface = %d", ifnum);
+ 
+ 	data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private),
+@@ -116,27 +119,32 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+ 	switch (nintf) {
+ 	case 1:
+ 		/* QDL mode */
+-		if (serial->interface->num_altsetting == 2) {
+-			struct usb_host_interface *intf;
+-
++		/* Gobi 2000 has a single altsetting, older ones have two */
++		if (serial->interface->num_altsetting == 2)
+ 			intf = &serial->interface->altsetting[1];
+-			if (intf->desc.bNumEndpoints == 2) {
+-				if (usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
+-				    usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
+-					dbg("QDL port found");
+-					retval = usb_set_interface(serial->dev, ifnum, 1);
+-					if (retval < 0) {
+-						dev_err(&serial->dev->dev,
+-							"Could not set interface, error %d\n",
+-							retval);
+-						retval = -ENODEV;
+-					}
+-					return retval;
+-				}
++		else if (serial->interface->num_altsetting > 2)
++			break;
++
++		if (intf->desc.bNumEndpoints == 2 &&
++		    usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
++		    usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
++			dbg("QDL port found");
++
++			if (serial->interface->num_altsetting == 1)
++				return 0;
++
++			retval = usb_set_interface(serial->dev, ifnum, 1);
++			if (retval < 0) {
++				dev_err(&serial->dev->dev,
++					"Could not set interface, error %d\n",
++					retval);
++				retval = -ENODEV;
+ 			}
++			return retval;
+ 		}
+ 		break;
+ 
++	case 3:
+ 	case 4:
+ 		/* Composite mode */
+ 		if (ifnum == 2) {
+-- 
+1.7.1
+

Added: dists/sid/linux-2.6/debian/patches/features/all/USB-qcserial-Use-generic-USB-wwan-code.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux-2.6/debian/patches/features/all/USB-qcserial-Use-generic-USB-wwan-code.patch	Tue Jun 15 12:45:12 2010	(r15878)
@@ -0,0 +1,85 @@
+From: Matthew Garrett <mjg at redhat.com>
+Date: Thu, 1 Apr 2010 12:31:09 -0400
+Subject: [PATCH] USB: qcserial: Use generic USB wwan code
+
+commit 3d7e59ad88fdb6bc50ae9b7e822d4bb5f68b68f9 upstream.
+
+Make qcserial use the generic USB wwan code. This should result in a
+performance improvement.
+
+Signed-off-by: Matthew Garrett <mjg at redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
+---
+ drivers/usb/serial/Kconfig    |    1 +
+ drivers/usb/serial/qcserial.c |   22 ++++++++++++++++++++++
+ 2 files changed, 23 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
+index fdc889d..efb6dc7 100644
+--- a/drivers/usb/serial/Kconfig
++++ b/drivers/usb/serial/Kconfig
+@@ -485,6 +485,7 @@ config USB_SERIAL_QCAUX
+ 
+ config USB_SERIAL_QUALCOMM
+ 	tristate "USB Qualcomm Serial modem"
++	select USB_SERIAL_WWAN
+ 	help
+ 	  Say Y here if you have a Qualcomm USB modem device.  These are
+ 	  usually wireless cellular modems.
+diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
+index 53a2d5a..9215f6c 100644
+--- a/drivers/usb/serial/qcserial.c
++++ b/drivers/usb/serial/qcserial.c
+@@ -15,6 +15,8 @@
+ #include <linux/tty_flip.h>
+ #include <linux/usb.h>
+ #include <linux/usb/serial.h>
++#include <linux/slab.h>
++#include "usb-wwan.h"
+ 
+ #define DRIVER_AUTHOR "Qualcomm Inc"
+ #define DRIVER_DESC "Qualcomm USB Serial driver"
+@@ -92,6 +94,7 @@ static struct usb_driver qcdriver = {
+ 
+ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+ {
++	struct usb_wwan_intf_private *data;
+ 	int retval = -ENODEV;
+ 	__u8 nintf;
+ 	__u8 ifnum;
+@@ -103,6 +106,13 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+ 	ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
+ 	dbg("This Interface = %d", ifnum);
+ 
++	data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private),
++					 GFP_KERNEL);
++	if (!data)
++		return -ENOMEM;
++
++	spin_lock_init(&data->susp_lock);
++
+ 	switch (nintf) {
+ 	case 1:
+ 		/* QDL mode */
+@@ -161,6 +171,18 @@ static struct usb_serial_driver qcdevice = {
+ 	.usb_driver          = &qcdriver,
+ 	.num_ports           = 1,
+ 	.probe               = qcprobe,
++	.open		     = usb_wwan_open,
++	.close		     = usb_wwan_close,
++	.write		     = usb_wwan_write,
++	.write_room	     = usb_wwan_write_room,
++	.chars_in_buffer     = usb_wwan_chars_in_buffer,
++	.attach		     = usb_wwan_startup,
++	.disconnect	     = usb_wwan_disconnect,
++	.release	     = usb_wwan_release,
++#ifdef CONFIG_PM
++	.suspend	     = usb_wwan_suspend,
++	.resume		     = usb_wwan_resume,
++#endif
+ };
+ 
+ static int __init qcinit(void)
+-- 
+1.7.1
+

Added: dists/sid/linux-2.6/debian/patches/features/all/usb-serial-Add-generic-USB-wwan-support.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux-2.6/debian/patches/features/all/usb-serial-Add-generic-USB-wwan-support.patch	Tue Jun 15 12:45:12 2010	(r15878)
@@ -0,0 +1,796 @@
+From: Matthew Garrett <mjg at redhat.com>
+Date: Thu, 1 Apr 2010 12:31:07 -0400
+Subject: [PATCH] usb serial: Add generic USB wwan support
+
+commit 0d4561947b8ddd5d944bdbbdc1ea1d6fd9a06041 upstream.
+
+The generic USB serial code is ill-suited for high-speed USB wwan devices,
+resulting in the option driver. However, other non-option devices may also
+gain similar benefits from not using the generic code. Factorise out the
+non-option specific code from the option driver and make it available to
+other users.
+
+Signed-off-by: Matthew Garrett <mjg at redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
+---
+ drivers/usb/serial/Kconfig    |    3 +
+ drivers/usb/serial/Makefile   |    1 +
+ drivers/usb/serial/usb-wwan.h |   67 ++++
+ drivers/usb/serial/usb_wwan.c |  665 +++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 736 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/usb/serial/usb-wwan.h
+ create mode 100644 drivers/usb/serial/usb_wwan.c
+
+diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
+index 5f777c2..e399efd 100644
+--- a/drivers/usb/serial/Kconfig
++++ b/drivers/usb/serial/Kconfig
+@@ -576,6 +576,9 @@ config USB_SERIAL_XIRCOM
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called keyspan_pda.
+ 
++config USB_SERIAL_WWAN
++	tristate
++
+ config USB_SERIAL_OPTION
+ 	tristate "USB driver for GSM and CDMA modems"
+ 	help
+diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
+index 3203614..e54c728 100644
+--- a/drivers/usb/serial/Makefile
++++ b/drivers/usb/serial/Makefile
+@@ -52,6 +52,7 @@ obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI)		+= siemens_mpi.o
+ obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS)		+= sierra.o
+ obj-$(CONFIG_USB_SERIAL_SPCP8X5)		+= spcp8x5.o
+ obj-$(CONFIG_USB_SERIAL_SYMBOL)			+= symbolserial.o
++obj-$(CONFIG_USB_SERIAL_WWAN)			+= usb_wwan.o
+ obj-$(CONFIG_USB_SERIAL_TI)			+= ti_usb_3410_5052.o
+ obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
+ obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
+diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
+new file mode 100644
+index 0000000..2be298a
+--- /dev/null
++++ b/drivers/usb/serial/usb-wwan.h
+@@ -0,0 +1,67 @@
++/*
++ * Definitions for USB serial mobile broadband cards
++ */
++
++#ifndef __LINUX_USB_USB_WWAN
++#define __LINUX_USB_USB_WWAN
++
++extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
++extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
++extern void usb_wwan_close(struct usb_serial_port *port);
++extern int usb_wwan_startup(struct usb_serial *serial);
++extern void usb_wwan_disconnect(struct usb_serial *serial);
++extern void usb_wwan_release(struct usb_serial *serial);
++extern int usb_wwan_write_room(struct tty_struct *tty);
++extern void usb_wwan_set_termios(struct tty_struct *tty,
++				 struct usb_serial_port *port,
++				 struct ktermios *old);
++extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file);
++extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
++			     unsigned int set, unsigned int clear);
++extern int usb_wwan_send_setup(struct usb_serial_port *port);
++extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
++			  const unsigned char *buf, int count);
++extern int usb_wwan_chars_in_buffer(struct tty_struct *tty);
++#ifdef CONFIG_PM
++extern int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message);
++extern int usb_wwan_resume(struct usb_serial *serial);
++#endif
++
++/* per port private data */
++
++#define N_IN_URB 4
++#define N_OUT_URB 4
++#define IN_BUFLEN 4096
++#define OUT_BUFLEN 4096
++
++struct usb_wwan_intf_private {
++	spinlock_t susp_lock;
++	unsigned int suspended:1;
++	int in_flight;
++	int (*send_setup) (struct usb_serial_port *port);
++	void *private;
++};
++
++struct usb_wwan_port_private {
++	/* Input endpoints and buffer for this port */
++	struct urb *in_urbs[N_IN_URB];
++	u8 *in_buffer[N_IN_URB];
++	/* Output endpoints and buffer for this port */
++	struct urb *out_urbs[N_OUT_URB];
++	u8 *out_buffer[N_OUT_URB];
++	unsigned long out_busy;	/* Bit vector of URBs in use */
++	int opened;
++	struct usb_anchor delayed;
++
++	/* Settings for the port */
++	int rts_state;		/* Handshaking pins (outputs) */
++	int dtr_state;
++	int cts_state;		/* Handshaking pins (inputs) */
++	int dsr_state;
++	int dcd_state;
++	int ri_state;
++
++	unsigned long tx_start_time[N_OUT_URB];
++};
++
++#endif /* __LINUX_USB_USB_WWAN */
+diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
+new file mode 100644
+index 0000000..0c70b4a
+--- /dev/null
++++ b/drivers/usb/serial/usb_wwan.c
+@@ -0,0 +1,665 @@
++/*
++  USB Driver layer for GSM modems
++
++  Copyright (C) 2005  Matthias Urlichs <smurf at smurf.noris.de>
++
++  This driver is free software; you can redistribute it and/or modify
++  it under the terms of Version 2 of the GNU General Public License as
++  published by the Free Software Foundation.
++
++  Portions copied from the Keyspan driver by Hugh Blemings <hugh at blemings.org>
++
++  History: see the git log.
++
++  Work sponsored by: Sigos GmbH, Germany <info at sigos.de>
++
++  This driver exists because the "normal" serial driver doesn't work too well
++  with GSM modems. Issues:
++  - data loss -- one single Receive URB is not nearly enough
++  - controlling the baud rate doesn't make sense
++*/
++
++#define DRIVER_VERSION "v0.7.2"
++#define DRIVER_AUTHOR "Matthias Urlichs <smurf at smurf.noris.de>"
++#define DRIVER_DESC "USB Driver for GSM modems"
++
++#include <linux/kernel.h>
++#include <linux/jiffies.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/module.h>
++#include <linux/bitops.h>
++#include <linux/usb.h>
++#include <linux/usb/serial.h>
++#include "usb-wwan.h"
++
++static int debug;
++
++void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
++{
++	struct usb_serial *serial = port->serial;
++	struct usb_wwan_port_private *portdata;
++
++	struct usb_wwan_intf_private *intfdata;
++
++	dbg("%s", __func__);
++
++	intfdata = port->serial->private;
++
++	if (!intfdata->send_setup)
++		return;
++
++	portdata = usb_get_serial_port_data(port);
++	mutex_lock(&serial->disc_mutex);
++	portdata->rts_state = on;
++	portdata->dtr_state = on;
++	if (serial->dev)
++		intfdata->send_setup(port);
++	mutex_unlock(&serial->disc_mutex);
++}
++EXPORT_SYMBOL(usb_wwan_dtr_rts);
++
++void usb_wwan_set_termios(struct tty_struct *tty,
++			  struct usb_serial_port *port,
++			  struct ktermios *old_termios)
++{
++	struct usb_wwan_intf_private *intfdata = port->serial->private;
++
++	dbg("%s", __func__);
++
++	/* Doesn't support option setting */
++	tty_termios_copy_hw(tty->termios, old_termios);
++
++	if (intfdata->send_setup)
++		intfdata->send_setup(port);
++}
++EXPORT_SYMBOL(usb_wwan_set_termios);
++
++int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file)
++{
++	struct usb_serial_port *port = tty->driver_data;
++	unsigned int value;
++	struct usb_wwan_port_private *portdata;
++
++	portdata = usb_get_serial_port_data(port);
++
++	value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
++	    ((portdata->dtr_state) ? TIOCM_DTR : 0) |
++	    ((portdata->cts_state) ? TIOCM_CTS : 0) |
++	    ((portdata->dsr_state) ? TIOCM_DSR : 0) |
++	    ((portdata->dcd_state) ? TIOCM_CAR : 0) |
++	    ((portdata->ri_state) ? TIOCM_RNG : 0);
++
++	return value;
++}
++EXPORT_SYMBOL(usb_wwan_tiocmget);
++
++int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
++		      unsigned int set, unsigned int clear)
++{
++	struct usb_serial_port *port = tty->driver_data;
++	struct usb_wwan_port_private *portdata;
++	struct usb_wwan_intf_private *intfdata;
++
++	portdata = usb_get_serial_port_data(port);
++	intfdata = port->serial->private;
++
++	if (!intfdata->send_setup)
++		return -EINVAL;
++
++	/* FIXME: what locks portdata fields ? */
++	if (set & TIOCM_RTS)
++		portdata->rts_state = 1;
++	if (set & TIOCM_DTR)
++		portdata->dtr_state = 1;
++
++	if (clear & TIOCM_RTS)
++		portdata->rts_state = 0;
++	if (clear & TIOCM_DTR)
++		portdata->dtr_state = 0;
++	return intfdata->send_setup(port);
++}
++EXPORT_SYMBOL(usb_wwan_tiocmset);
++
++/* Write */
++int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
++		   const unsigned char *buf, int count)
++{
++	struct usb_wwan_port_private *portdata;
++	struct usb_wwan_intf_private *intfdata;
++	int i;
++	int left, todo;
++	struct urb *this_urb = NULL;	/* spurious */
++	int err;
++	unsigned long flags;
++
++	portdata = usb_get_serial_port_data(port);
++	intfdata = port->serial->private;
++
++	dbg("%s: write (%d chars)", __func__, count);
++
++	i = 0;
++	left = count;
++	for (i = 0; left > 0 && i < N_OUT_URB; i++) {
++		todo = left;
++		if (todo > OUT_BUFLEN)
++			todo = OUT_BUFLEN;
++
++		this_urb = portdata->out_urbs[i];
++		if (test_and_set_bit(i, &portdata->out_busy)) {
++			if (time_before(jiffies,
++					portdata->tx_start_time[i] + 10 * HZ))
++				continue;
++			usb_unlink_urb(this_urb);
++			continue;
++		}
++		dbg("%s: endpoint %d buf %d", __func__,
++		    usb_pipeendpoint(this_urb->pipe), i);
++
++		err = usb_autopm_get_interface_async(port->serial->interface);
++		if (err < 0)
++			break;
++
++		/* send the data */
++		memcpy(this_urb->transfer_buffer, buf, todo);
++		this_urb->transfer_buffer_length = todo;
++
++		spin_lock_irqsave(&intfdata->susp_lock, flags);
++		if (intfdata->suspended) {
++			usb_anchor_urb(this_urb, &portdata->delayed);
++			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
++		} else {
++			intfdata->in_flight++;
++			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
++			err = usb_submit_urb(this_urb, GFP_ATOMIC);
++			if (err) {
++				dbg("usb_submit_urb %p (write bulk) failed "
++				    "(%d)", this_urb, err);
++				clear_bit(i, &portdata->out_busy);
++				spin_lock_irqsave(&intfdata->susp_lock, flags);
++				intfdata->in_flight--;
++				spin_unlock_irqrestore(&intfdata->susp_lock,
++						       flags);
++				continue;
++			}
++		}
++
++		portdata->tx_start_time[i] = jiffies;
++		buf += todo;
++		left -= todo;
++	}
++
++	count -= left;
++	dbg("%s: wrote (did %d)", __func__, count);
++	return count;
++}
++EXPORT_SYMBOL(usb_wwan_write);
++
++static void usb_wwan_indat_callback(struct urb *urb)
++{
++	int err;
++	int endpoint;
++	struct usb_serial_port *port;
++	struct tty_struct *tty;
++	unsigned char *data = urb->transfer_buffer;
++	int status = urb->status;
++
++	dbg("%s: %p", __func__, urb);
++
++	endpoint = usb_pipeendpoint(urb->pipe);
++	port = urb->context;
++
++	if (status) {
++		dbg("%s: nonzero status: %d on endpoint %02x.",
++		    __func__, status, endpoint);
++	} else {
++		tty = tty_port_tty_get(&port->port);
++		if (urb->actual_length) {
++			tty_insert_flip_string(tty, data, urb->actual_length);
++			tty_flip_buffer_push(tty);
++		} else
++			dbg("%s: empty read urb received", __func__);
++		tty_kref_put(tty);
++
++		/* Resubmit urb so we continue receiving */
++		if (status != -ESHUTDOWN) {
++			err = usb_submit_urb(urb, GFP_ATOMIC);
++			if (err && err != -EPERM)
++				printk(KERN_ERR "%s: resubmit read urb failed. "
++				       "(%d)", __func__, err);
++			else
++				usb_mark_last_busy(port->serial->dev);
++		}
++
++	}
++	return;
++}
++
++static void usb_wwan_outdat_callback(struct urb *urb)
++{
++	struct usb_serial_port *port;
++	struct usb_wwan_port_private *portdata;
++	struct usb_wwan_intf_private *intfdata;
++	int i;
++
++	dbg("%s", __func__);
++
++	port = urb->context;
++	intfdata = port->serial->private;
++
++	usb_serial_port_softint(port);
++	usb_autopm_put_interface_async(port->serial->interface);
++	portdata = usb_get_serial_port_data(port);
++	spin_lock(&intfdata->susp_lock);
++	intfdata->in_flight--;
++	spin_unlock(&intfdata->susp_lock);
++
++	for (i = 0; i < N_OUT_URB; ++i) {
++		if (portdata->out_urbs[i] == urb) {
++			smp_mb__before_clear_bit();
++			clear_bit(i, &portdata->out_busy);
++			break;
++		}
++	}
++}
++
++int usb_wwan_write_room(struct tty_struct *tty)
++{
++	struct usb_serial_port *port = tty->driver_data;
++	struct usb_wwan_port_private *portdata;
++	int i;
++	int data_len = 0;
++	struct urb *this_urb;
++
++	portdata = usb_get_serial_port_data(port);
++
++	for (i = 0; i < N_OUT_URB; i++) {
++		this_urb = portdata->out_urbs[i];
++		if (this_urb && !test_bit(i, &portdata->out_busy))
++			data_len += OUT_BUFLEN;
++	}
++
++	dbg("%s: %d", __func__, data_len);
++	return data_len;
++}
++EXPORT_SYMBOL(usb_wwan_write_room);
++
++int usb_wwan_chars_in_buffer(struct tty_struct *tty)
++{
++	struct usb_serial_port *port = tty->driver_data;
++	struct usb_wwan_port_private *portdata;
++	int i;
++	int data_len = 0;
++	struct urb *this_urb;
++
++	portdata = usb_get_serial_port_data(port);
++
++	for (i = 0; i < N_OUT_URB; i++) {
++		this_urb = portdata->out_urbs[i];
++		/* FIXME: This locking is insufficient as this_urb may
++		   go unused during the test */
++		if (this_urb && test_bit(i, &portdata->out_busy))
++			data_len += this_urb->transfer_buffer_length;
++	}
++	dbg("%s: %d", __func__, data_len);
++	return data_len;
++}
++EXPORT_SYMBOL(usb_wwan_chars_in_buffer);
++
++int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
++{
++	struct usb_wwan_port_private *portdata;
++	struct usb_wwan_intf_private *intfdata;
++	struct usb_serial *serial = port->serial;
++	int i, err;
++	struct urb *urb;
++
++	portdata = usb_get_serial_port_data(port);
++	intfdata = serial->private;
++
++	dbg("%s", __func__);
++
++	/* Start reading from the IN endpoint */
++	for (i = 0; i < N_IN_URB; i++) {
++		urb = portdata->in_urbs[i];
++		if (!urb)
++			continue;
++		err = usb_submit_urb(urb, GFP_KERNEL);
++		if (err) {
++			dbg("%s: submit urb %d failed (%d) %d",
++			    __func__, i, err, urb->transfer_buffer_length);
++		}
++	}
++
++	if (intfdata->send_setup)
++		intfdata->send_setup(port);
++
++	serial->interface->needs_remote_wakeup = 1;
++	spin_lock_irq(&intfdata->susp_lock);
++	portdata->opened = 1;
++	spin_unlock_irq(&intfdata->susp_lock);
++	usb_autopm_put_interface(serial->interface);
++
++	return 0;
++}
++EXPORT_SYMBOL(usb_wwan_open);
++
++void usb_wwan_close(struct usb_serial_port *port)
++{
++	int i;
++	struct usb_serial *serial = port->serial;
++	struct usb_wwan_port_private *portdata;
++	struct usb_wwan_intf_private *intfdata = port->serial->private;
++
++	dbg("%s", __func__);
++	portdata = usb_get_serial_port_data(port);
++
++	if (serial->dev) {
++		/* Stop reading/writing urbs */
++		spin_lock_irq(&intfdata->susp_lock);
++		portdata->opened = 0;
++		spin_unlock_irq(&intfdata->susp_lock);
++
++		for (i = 0; i < N_IN_URB; i++)
++			usb_kill_urb(portdata->in_urbs[i]);
++		for (i = 0; i < N_OUT_URB; i++)
++			usb_kill_urb(portdata->out_urbs[i]);
++		usb_autopm_get_interface(serial->interface);
++		serial->interface->needs_remote_wakeup = 0;
++	}
++}
++EXPORT_SYMBOL(usb_wwan_close);
++
++/* Helper functions used by usb_wwan_setup_urbs */
++static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
++				      int dir, void *ctx, char *buf, int len,
++				      void (*callback) (struct urb *))
++{
++	struct urb *urb;
++
++	if (endpoint == -1)
++		return NULL;	/* endpoint not needed */
++
++	urb = usb_alloc_urb(0, GFP_KERNEL);	/* No ISO */
++	if (urb == NULL) {
++		dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
++		return NULL;
++	}
++
++	/* Fill URB using supplied data. */
++	usb_fill_bulk_urb(urb, serial->dev,
++			  usb_sndbulkpipe(serial->dev, endpoint) | dir,
++			  buf, len, callback, ctx);
++
++	return urb;
++}
++
++/* Setup urbs */
++static void usb_wwan_setup_urbs(struct usb_serial *serial)
++{
++	int i, j;
++	struct usb_serial_port *port;
++	struct usb_wwan_port_private *portdata;
++
++	dbg("%s", __func__);
++
++	for (i = 0; i < serial->num_ports; i++) {
++		port = serial->port[i];
++		portdata = usb_get_serial_port_data(port);
++
++		/* Do indat endpoints first */
++		for (j = 0; j < N_IN_URB; ++j) {
++			portdata->in_urbs[j] = usb_wwan_setup_urb(serial,
++								  port->
++								  bulk_in_endpointAddress,
++								  USB_DIR_IN,
++								  port,
++								  portdata->
++								  in_buffer[j],
++								  IN_BUFLEN,
++								  usb_wwan_indat_callback);
++		}
++
++		/* outdat endpoints */
++		for (j = 0; j < N_OUT_URB; ++j) {
++			portdata->out_urbs[j] = usb_wwan_setup_urb(serial,
++								   port->
++								   bulk_out_endpointAddress,
++								   USB_DIR_OUT,
++								   port,
++								   portdata->
++								   out_buffer
++								   [j],
++								   OUT_BUFLEN,
++								   usb_wwan_outdat_callback);
++		}
++	}
++}
++
++int usb_wwan_startup(struct usb_serial *serial)
++{
++	int i, j, err;
++	struct usb_serial_port *port;
++	struct usb_wwan_port_private *portdata;
++	u8 *buffer;
++
++	dbg("%s", __func__);
++
++	/* Now setup per port private data */
++	for (i = 0; i < serial->num_ports; i++) {
++		port = serial->port[i];
++		portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
++		if (!portdata) {
++			dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.",
++			    __func__, i);
++			return 1;
++		}
++		init_usb_anchor(&portdata->delayed);
++
++		for (j = 0; j < N_IN_URB; j++) {
++			buffer = (u8 *) __get_free_page(GFP_KERNEL);
++			if (!buffer)
++				goto bail_out_error;
++			portdata->in_buffer[j] = buffer;
++		}
++
++		for (j = 0; j < N_OUT_URB; j++) {
++			buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
++			if (!buffer)
++				goto bail_out_error2;
++			portdata->out_buffer[j] = buffer;
++		}
++
++		usb_set_serial_port_data(port, portdata);
++
++		if (!port->interrupt_in_urb)
++			continue;
++		err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
++		if (err)
++			dbg("%s: submit irq_in urb failed %d", __func__, err);
++	}
++	usb_wwan_setup_urbs(serial);
++	return 0;
++
++bail_out_error2:
++	for (j = 0; j < N_OUT_URB; j++)
++		kfree(portdata->out_buffer[j]);
++bail_out_error:
++	for (j = 0; j < N_IN_URB; j++)
++		if (portdata->in_buffer[j])
++			free_page((unsigned long)portdata->in_buffer[j]);
++	kfree(portdata);
++	return 1;
++}
++EXPORT_SYMBOL(usb_wwan_startup);
++
++static void stop_read_write_urbs(struct usb_serial *serial)
++{
++	int i, j;
++	struct usb_serial_port *port;
++	struct usb_wwan_port_private *portdata;
++
++	/* Stop reading/writing urbs */
++	for (i = 0; i < serial->num_ports; ++i) {
++		port = serial->port[i];
++		portdata = usb_get_serial_port_data(port);
++		for (j = 0; j < N_IN_URB; j++)
++			usb_kill_urb(portdata->in_urbs[j]);
++		for (j = 0; j < N_OUT_URB; j++)
++			usb_kill_urb(portdata->out_urbs[j]);
++	}
++}
++
++void usb_wwan_disconnect(struct usb_serial *serial)
++{
++	dbg("%s", __func__);
++
++	stop_read_write_urbs(serial);
++}
++EXPORT_SYMBOL(usb_wwan_disconnect);
++
++void usb_wwan_release(struct usb_serial *serial)
++{
++	int i, j;
++	struct usb_serial_port *port;
++	struct usb_wwan_port_private *portdata;
++
++	dbg("%s", __func__);
++
++	/* Now free them */
++	for (i = 0; i < serial->num_ports; ++i) {
++		port = serial->port[i];
++		portdata = usb_get_serial_port_data(port);
++
++		for (j = 0; j < N_IN_URB; j++) {
++			usb_free_urb(portdata->in_urbs[j]);
++			free_page((unsigned long)
++				  portdata->in_buffer[j]);
++			portdata->in_urbs[j] = NULL;
++		}
++		for (j = 0; j < N_OUT_URB; j++) {
++			usb_free_urb(portdata->out_urbs[j]);
++			kfree(portdata->out_buffer[j]);
++			portdata->out_urbs[j] = NULL;
++		}
++	}
++
++	/* Now free per port private data */
++	for (i = 0; i < serial->num_ports; i++) {
++		port = serial->port[i];
++		kfree(usb_get_serial_port_data(port));
++	}
++}
++EXPORT_SYMBOL(usb_wwan_release);
++
++#ifdef CONFIG_PM
++int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
++{
++	struct usb_wwan_intf_private *intfdata = serial->private;
++	int b;
++
++	dbg("%s entered", __func__);
++
++	if (message.event & PM_EVENT_AUTO) {
++		spin_lock_irq(&intfdata->susp_lock);
++		b = intfdata->in_flight;
++		spin_unlock_irq(&intfdata->susp_lock);
++
++		if (b)
++			return -EBUSY;
++	}
++
++	spin_lock_irq(&intfdata->susp_lock);
++	intfdata->suspended = 1;
++	spin_unlock_irq(&intfdata->susp_lock);
++	stop_read_write_urbs(serial);
++
++	return 0;
++}
++EXPORT_SYMBOL(usb_wwan_suspend);
++
++static void play_delayed(struct usb_serial_port *port)
++{
++	struct usb_wwan_intf_private *data;
++	struct usb_wwan_port_private *portdata;
++	struct urb *urb;
++	int err;
++
++	portdata = usb_get_serial_port_data(port);
++	data = port->serial->private;
++	while ((urb = usb_get_from_anchor(&portdata->delayed))) {
++		err = usb_submit_urb(urb, GFP_ATOMIC);
++		if (!err)
++			data->in_flight++;
++	}
++}
++
++int usb_wwan_resume(struct usb_serial *serial)
++{
++	int i, j;
++	struct usb_serial_port *port;
++	struct usb_wwan_intf_private *intfdata = serial->private;
++	struct usb_wwan_port_private *portdata;
++	struct urb *urb;
++	int err = 0;
++
++	dbg("%s entered", __func__);
++	/* get the interrupt URBs resubmitted unconditionally */
++	for (i = 0; i < serial->num_ports; i++) {
++		port = serial->port[i];
++		if (!port->interrupt_in_urb) {
++			dbg("%s: No interrupt URB for port %d", __func__, i);
++			continue;
++		}
++		err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
++		dbg("Submitted interrupt URB for port %d (result %d)", i, err);
++		if (err < 0) {
++			err("%s: Error %d for interrupt URB of port%d",
++			    __func__, err, i);
++			goto err_out;
++		}
++	}
++
++	for (i = 0; i < serial->num_ports; i++) {
++		/* walk all ports */
++		port = serial->port[i];
++		portdata = usb_get_serial_port_data(port);
++
++		/* skip closed ports */
++		spin_lock_irq(&intfdata->susp_lock);
++		if (!portdata->opened) {
++			spin_unlock_irq(&intfdata->susp_lock);
++			continue;
++		}
++
++		for (j = 0; j < N_IN_URB; j++) {
++			urb = portdata->in_urbs[j];
++			err = usb_submit_urb(urb, GFP_ATOMIC);
++			if (err < 0) {
++				err("%s: Error %d for bulk URB %d",
++				    __func__, err, i);
++				spin_unlock_irq(&intfdata->susp_lock);
++				goto err_out;
++			}
++		}
++		play_delayed(port);
++		spin_unlock_irq(&intfdata->susp_lock);
++	}
++	spin_lock_irq(&intfdata->susp_lock);
++	intfdata->suspended = 0;
++	spin_unlock_irq(&intfdata->susp_lock);
++err_out:
++	return err;
++}
++EXPORT_SYMBOL(usb_wwan_resume);
++#endif
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_VERSION(DRIVER_VERSION);
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug messages");
+-- 
+1.7.1
+

Modified: dists/sid/linux-2.6/debian/patches/series/16
==============================================================================
--- dists/sid/linux-2.6/debian/patches/series/16	Tue Jun 15 12:36:19 2010	(r15877)
+++ dists/sid/linux-2.6/debian/patches/series/16	Tue Jun 15 12:45:12 2010	(r15878)
@@ -138,3 +138,7 @@
 + bugfix/mips/mips-Set-io_map_base-for-several-PCI-bridges-lacking.patch
 - bugfix/all/vlan-macvlan-propagate-transmission-state-to-upper-layer.patch
 + features/sh4/sh-optimize-runtime-disabling-of-trapped-IO.patch
++ features/all/usb-serial-Add-generic-USB-wwan-support.patch
++ features/all/USB-option-Use-generic-USB-wwan-code.patch
++ features/all/USB-qcserial-Use-generic-USB-wwan-code.patch
++ features/all/USB-qcserial-Add-support-for-Qualcomm-Gobi-2000.patch



More information about the Kernel-svn-changes mailing list