[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