Commit 2caabb3d authored by David S. Miller's avatar David S. Miller

Merge branch 'hso-next'

Olivier Sobrie says:

====================
hso: fix some problems in the disconnect path

These patches attempt to fix some problems I observed when the hso
device is disconnected.
Several patches of this serie are fixing crashes or memleaks when a
hso device is disconnected.
This serie of patches is based on v3.18.

changes in v2:
 - Last patch of the serie dropped since another patch fix the issue.
   See http://marc.info/?l=linux-usb&m=142186699418489 for more info.

 - Added an extra patch avoiding name conflicts for the rfkill interface.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 349c9e3c 38121067
......@@ -58,7 +58,6 @@
#include <linux/module.h>
#include <linux/ethtool.h>
#include <linux/usb.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
......@@ -154,6 +153,7 @@ struct hso_net {
struct hso_device *parent;
struct net_device *net;
struct rfkill *rfkill;
char name[24];
struct usb_endpoint_descriptor *in_endp;
struct usb_endpoint_descriptor *out_endp;
......@@ -274,7 +274,6 @@ struct hso_device {
u8 usb_gone;
struct work_struct async_get_intf;
struct work_struct async_put_intf;
struct work_struct reset_device;
struct usb_device *usb;
struct usb_interface *interface;
......@@ -340,7 +339,6 @@ static void async_put_intf(struct work_struct *data);
static int hso_put_activity(struct hso_device *hso_dev);
static int hso_get_activity(struct hso_device *hso_dev);
static void tiocmget_intr_callback(struct urb *urb);
static void reset_device(struct work_struct *data);
/*****************************************************************************/
/* Helping functions */
/*****************************************************************************/
......@@ -696,7 +694,7 @@ static void handle_usb_error(int status, const char *function,
case -ETIMEDOUT:
explanation = "protocol error";
if (hso_dev)
schedule_work(&hso_dev->reset_device);
usb_queue_reset_device(hso_dev->interface);
break;
default:
explanation = "unknown status";
......@@ -1271,7 +1269,6 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
goto err_out;
D1("Opening %d", serial->minor);
kref_get(&serial->parent->ref);
/* setup */
tty->driver_data = serial;
......@@ -1290,7 +1287,8 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
if (result) {
hso_stop_serial_device(serial->parent);
serial->port.count--;
kref_put(&serial->parent->ref, hso_serial_ref_free);
} else {
kref_get(&serial->parent->ref);
}
} else {
D1("Port was already open");
......@@ -1340,8 +1338,6 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)
usb_autopm_put_interface(serial->parent->interface);
mutex_unlock(&serial->parent->mutex);
kref_put(&serial->parent->ref, hso_serial_ref_free);
}
/* close the requested serial port */
......@@ -1392,6 +1388,16 @@ static int hso_serial_write_room(struct tty_struct *tty)
return room;
}
static void hso_serial_cleanup(struct tty_struct *tty)
{
struct hso_serial *serial = tty->driver_data;
if (!serial)
return;
kref_put(&serial->parent->ref, hso_serial_ref_free);
}
/* setup the term */
static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
{
......@@ -2198,8 +2204,8 @@ static int hso_stop_serial_device(struct hso_device *hso_dev)
for (i = 0; i < serial->num_rx_urbs; i++) {
if (serial->rx_urb[i]) {
usb_kill_urb(serial->rx_urb[i]);
serial->rx_urb_filled[i] = 0;
usb_kill_urb(serial->rx_urb[i]);
serial->rx_urb_filled[i] = 0;
}
}
serial->curr_rx_urb_idx = 0;
......@@ -2228,14 +2234,17 @@ static int hso_stop_serial_device(struct hso_device *hso_dev)
return 0;
}
static void hso_serial_common_free(struct hso_serial *serial)
static void hso_serial_tty_unregister(struct hso_serial *serial)
{
int i;
if (serial->parent->dev)
device_remove_file(serial->parent->dev, &dev_attr_hsotype);
tty_unregister_device(tty_drv, serial->minor);
}
static void hso_serial_common_free(struct hso_serial *serial)
{
int i;
for (i = 0; i < serial->num_rx_urbs; i++) {
/* unlink and free RX URB */
......@@ -2246,6 +2255,7 @@ static void hso_serial_common_free(struct hso_serial *serial)
/* unlink and free TX URB */
usb_free_urb(serial->tx_urb);
kfree(serial->tx_buffer);
kfree(serial->tx_data);
tty_port_destroy(&serial->port);
}
......@@ -2316,6 +2326,7 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
return 0;
exit:
hso_serial_tty_unregister(serial);
hso_serial_common_free(serial);
return -1;
}
......@@ -2338,7 +2349,6 @@ static struct hso_device *hso_create_device(struct usb_interface *intf,
INIT_WORK(&hso_dev->async_get_intf, async_get_intf);
INIT_WORK(&hso_dev->async_put_intf, async_put_intf);
INIT_WORK(&hso_dev->reset_device, reset_device);
return hso_dev;
}
......@@ -2459,27 +2469,21 @@ static void hso_create_rfkill(struct hso_device *hso_dev,
{
struct hso_net *hso_net = dev2net(hso_dev);
struct device *dev = &hso_net->net->dev;
char *rfkn;
static u32 rfkill_counter;
rfkn = kzalloc(20, GFP_KERNEL);
if (!rfkn)
dev_err(dev, "%s - Out of memory\n", __func__);
snprintf(rfkn, 20, "hso-%d",
interface->altsetting->desc.bInterfaceNumber);
snprintf(hso_net->name, sizeof(hso_net->name), "hso-%d",
rfkill_counter++);
hso_net->rfkill = rfkill_alloc(rfkn,
hso_net->rfkill = rfkill_alloc(hso_net->name,
&interface_to_usbdev(interface)->dev,
RFKILL_TYPE_WWAN,
&hso_rfkill_ops, hso_dev);
if (!hso_net->rfkill) {
dev_err(dev, "%s - Out of memory\n", __func__);
kfree(rfkn);
return;
}
if (rfkill_register(hso_net->rfkill) < 0) {
rfkill_destroy(hso_net->rfkill);
kfree(rfkn);
hso_net->rfkill = NULL;
dev_err(dev, "%s - Failed to register rfkill\n", __func__);
return;
......@@ -2594,7 +2598,6 @@ static void hso_free_serial_device(struct hso_device *hso_dev)
if (!serial)
return;
set_serial_by_index(serial->minor, NULL);
hso_serial_common_free(serial);
......@@ -2684,6 +2687,7 @@ static struct hso_device *hso_create_bulk_serial_device(
return hso_dev;
exit2:
hso_serial_tty_unregister(serial);
hso_serial_common_free(serial);
exit:
hso_free_tiomget(serial);
......@@ -3083,26 +3087,6 @@ static int hso_resume(struct usb_interface *iface)
return result;
}
static void reset_device(struct work_struct *data)
{
struct hso_device *hso_dev =
container_of(data, struct hso_device, reset_device);
struct usb_device *usb = hso_dev->usb;
int result;
if (hso_dev->usb_gone) {
D1("No reset during disconnect\n");
} else {
result = usb_lock_device_for_reset(usb, hso_dev->interface);
if (result < 0)
D1("unable to lock device for reset: %d\n", result);
else {
usb_reset_device(usb);
usb_unlock_device(usb);
}
}
}
static void hso_serial_ref_free(struct kref *ref)
{
struct hso_device *hso_dev = container_of(ref, struct hso_device, ref);
......@@ -3112,18 +3096,22 @@ static void hso_serial_ref_free(struct kref *ref)
static void hso_free_interface(struct usb_interface *interface)
{
struct hso_serial *hso_dev;
struct hso_serial *serial;
int i;
for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
if (serial_table[i] &&
(serial_table[i]->interface == interface)) {
hso_dev = dev2ser(serial_table[i]);
tty_port_tty_hangup(&hso_dev->port, false);
mutex_lock(&hso_dev->parent->mutex);
hso_dev->parent->usb_gone = 1;
mutex_unlock(&hso_dev->parent->mutex);
serial = dev2ser(serial_table[i]);
tty_port_tty_hangup(&serial->port, false);
mutex_lock(&serial->parent->mutex);
serial->parent->usb_gone = 1;
mutex_unlock(&serial->parent->mutex);
cancel_work_sync(&serial_table[i]->async_put_intf);
cancel_work_sync(&serial_table[i]->async_get_intf);
hso_serial_tty_unregister(serial);
kref_put(&serial_table[i]->ref, hso_serial_ref_free);
set_serial_by_index(i, NULL);
}
}
......@@ -3215,6 +3203,7 @@ static const struct tty_operations hso_serial_ops = {
.close = hso_serial_close,
.write = hso_serial_write,
.write_room = hso_serial_write_room,
.cleanup = hso_serial_cleanup,
.ioctl = hso_serial_ioctl,
.set_termios = hso_serial_set_termios,
.chars_in_buffer = hso_serial_chars_in_buffer,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment