OpenCloudOS-Kernel/drivers/usb/serial/usb_wwan.c

705 lines
18 KiB
C
Raw Normal View History

/*
USB Driver layer for GSM modems
Copyright (C) 2005 Matthias Urlichs <smurf@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@blemings.org>
History: see the git log.
Work sponsored by: Sigos GmbH, Germany <info@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_AUTHOR "Matthias Urlichs <smurf@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/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
#include "usb-wwan.h"
void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
{
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata;
intfdata = port->serial->private;
if (!intfdata->send_setup)
return;
portdata = usb_get_serial_port_data(port);
/* FIXME: locking */
portdata->rts_state = on;
portdata->dtr_state = on;
intfdata->send_setup(port);
}
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;
/* 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 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,
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);
static int get_serial_info(struct usb_serial_port *port,
struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->serial->minor;
tmp.port = port->number;
tmp.baud_base = tty_get_baud_rate(port->port.tty);
tmp.close_delay = port->port.close_delay / 10;
tmp.closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
port->port.closing_wait / 10;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
static int set_serial_info(struct usb_serial_port *port,
struct serial_struct __user *newinfo)
{
struct serial_struct new_serial;
unsigned int closing_wait, close_delay;
int retval = 0;
if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
return -EFAULT;
close_delay = new_serial.close_delay * 10;
closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
mutex_lock(&port->port.mutex);
if (!capable(CAP_SYS_ADMIN)) {
if ((close_delay != port->port.close_delay) ||
(closing_wait != port->port.closing_wait))
retval = -EPERM;
else
retval = -EOPNOTSUPP;
} else {
port->port.close_delay = close_delay;
port->port.closing_wait = closing_wait;
}
mutex_unlock(&port->port.mutex);
return retval;
}
int usb_wwan_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
switch (cmd) {
case TIOCGSERIAL:
return get_serial_info(port,
(struct serial_struct __user *) arg);
case TIOCSSERIAL:
return set_serial_info(port,
(struct serial_struct __user *) arg);
default:
break;
}
dev_dbg(&port->dev, "%s arg not supported\n", __func__);
return -ENOIOCTLCMD;
}
EXPORT_SYMBOL(usb_wwan_ioctl);
/* 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;
dev_dbg(&port->dev, "%s: write (%d chars)\n", __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;
}
dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __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) {
dev_dbg(&port->dev,
"usb_submit_urb %p (write bulk) failed (%d)\n",
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);
usb_autopm_put_interface_async(port->serial->interface);
break;
}
}
portdata->tx_start_time[i] = jiffies;
buf += todo;
left -= todo;
}
count -= left;
dev_dbg(&port->dev, "%s: wrote (did %d)\n", __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 device *dev;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
endpoint = usb_pipeendpoint(urb->pipe);
port = urb->context;
dev = &port->dev;
if (status) {
dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n",
__func__, status, endpoint);
} else {
if (urb->actual_length) {
tty_insert_flip_string(&port->port, data,
urb->actual_length);
tty_flip_buffer_push(&port->port);
} else
dev_dbg(dev, "%s: empty read urb received\n", __func__);
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
if (err != -EPERM) {
dev_err(dev, "%s: resubmit read urb failed. (%d)\n", __func__, err);
/* busy also in error unless we are killed */
usb_mark_last_busy(port->serial->dev);
}
} else {
usb_mark_last_busy(port->serial->dev);
}
}
}
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;
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;
}
dev_dbg(&port->dev, "%s: %d\n", __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;
}
dev_dbg(&port->dev, "%s: %d\n", __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;
/* 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) {
dev_dbg(&port->dev, "%s: submit urb %d failed (%d) %d\n",
__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);
/* this balances a get in the generic USB serial code */
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;
portdata = usb_get_serial_port_data(port);
/* 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]);
/* balancing - important as an error cannot be handled*/
usb_autopm_get_interface_no_resume(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_port *port,
int endpoint,
int dir, void *ctx, char *buf, int len,
void (*callback) (struct urb *))
{
struct usb_serial *serial = port->serial;
struct urb *urb;
urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
if (urb == NULL) {
dev_dbg(&serial->interface->dev,
"%s: alloc for endpoint %d failed.\n", __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;
}
int usb_wwan_port_probe(struct usb_serial_port *port)
{
struct usb_wwan_port_private *portdata;
struct urb *urb;
u8 *buffer;
int err;
int i;
portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
if (!portdata)
return -ENOMEM;
init_usb_anchor(&portdata->delayed);
for (i = 0; i < N_IN_URB; i++) {
if (!port->bulk_in_size)
break;
buffer = (u8 *)__get_free_page(GFP_KERNEL);
if (!buffer)
goto bail_out_error;
portdata->in_buffer[i] = buffer;
urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress,
USB_DIR_IN, port,
buffer, IN_BUFLEN,
usb_wwan_indat_callback);
portdata->in_urbs[i] = urb;
}
for (i = 0; i < N_OUT_URB; i++) {
if (!port->bulk_out_size)
break;
buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
if (!buffer)
goto bail_out_error2;
portdata->out_buffer[i] = buffer;
urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress,
USB_DIR_OUT, port,
buffer, OUT_BUFLEN,
usb_wwan_outdat_callback);
portdata->out_urbs[i] = urb;
}
usb_set_serial_port_data(port, portdata);
if (port->interrupt_in_urb) {
err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (err)
dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n",
__func__, err);
}
return 0;
bail_out_error2:
for (i = 0; i < N_OUT_URB; i++) {
usb_free_urb(portdata->out_urbs[i]);
kfree(portdata->out_buffer[i]);
}
bail_out_error:
for (i = 0; i < N_IN_URB; i++) {
usb_free_urb(portdata->in_urbs[i]);
free_page((unsigned long)portdata->in_buffer[i]);
}
kfree(portdata);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(usb_wwan_port_probe);
usb: usb_wwan: replace release and disconnect with a port_remove hook Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-27 07:11:41 +08:00
int usb_wwan_port_remove(struct usb_serial_port *port)
{
usb: usb_wwan: replace release and disconnect with a port_remove hook Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-27 07:11:41 +08:00
int i;
struct usb_wwan_port_private *portdata;
usb: usb_wwan: replace release and disconnect with a port_remove hook Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-27 07:11:41 +08:00
portdata = usb_get_serial_port_data(port);
usb_set_serial_port_data(port, NULL);
/* Stop reading/writing urbs and free them */
for (i = 0; i < N_IN_URB; i++) {
usb_kill_urb(portdata->in_urbs[i]);
usb_free_urb(portdata->in_urbs[i]);
free_page((unsigned long)portdata->in_buffer[i]);
}
for (i = 0; i < N_OUT_URB; i++) {
usb_kill_urb(portdata->out_urbs[i]);
usb_free_urb(portdata->out_urbs[i]);
kfree(portdata->out_buffer[i]);
}
usb: usb_wwan: replace release and disconnect with a port_remove hook Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-27 07:11:41 +08:00
/* Now free port private data */
kfree(portdata);
return 0;
}
usb: usb_wwan: replace release and disconnect with a port_remove hook Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-27 07:11:41 +08:00
EXPORT_SYMBOL(usb_wwan_port_remove);
usb: usb_wwan: replace release and disconnect with a port_remove hook Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-27 07:11:41 +08:00
#ifdef CONFIG_PM
static void stop_read_write_urbs(struct usb_serial *serial)
{
int i, j;
struct usb_serial_port *port;
struct usb_wwan_port_private *portdata;
usb: usb_wwan: replace release and disconnect with a port_remove hook Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-27 07:11:41 +08:00
/* Stop reading/writing urbs */
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
if (!portdata)
continue;
usb: usb_wwan: replace release and disconnect with a port_remove hook Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-27 07:11:41 +08:00
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]);
}
}
int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
{
struct usb_wwan_intf_private *intfdata = serial->private;
int b;
if (PMSG_IS_AUTO(message)) {
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 unbusy_queued_urb(struct urb *urb, struct usb_wwan_port_private *portdata)
{
int i;
for (i = 0; i < N_OUT_URB; i++) {
if (urb == portdata->out_urbs[i]) {
clear_bit(i, &portdata->out_busy);
break;
}
}
}
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++;
} else {
/* we have to throw away the rest */
do {
unbusy_queued_urb(urb, portdata);
usb_autopm_put_interface_no_suspend(port->serial->interface);
} while ((urb = usb_get_from_anchor(&portdata->delayed)));
break;
}
}
}
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;
/* get the interrupt URBs resubmitted unconditionally */
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
if (!port->interrupt_in_urb) {
dev_dbg(&port->dev, "%s: No interrupt URB for port\n", __func__);
continue;
}
err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
dev_dbg(&port->dev, "Submitted interrupt URB for port (result %d)\n", err);
if (err < 0) {
dev_err(&port->dev, "%s: Error %d for interrupt URB\n",
__func__, err);
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 || !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) {
dev_err(&port->dev, "%s: Error %d for bulk URB %d\n",
__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_LICENSE("GPL");