Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+40 −51
Original line number Diff line number Diff line
@@ -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)
{
@@ -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;

	rfkn = kzalloc(20, GFP_KERNEL);
	if (!rfkn)
		dev_err(dev, "%s - Out of memory\n", __func__);
	static u32 rfkill_counter;

	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,