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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
2caabb3d2e
|
@ -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(hso_net->name, sizeof(hso_net->name), "hso-%d",
|
||||
rfkill_counter++);
|
||||
|
||||
snprintf(rfkn, 20, "hso-%d",
|
||||
interface->altsetting->desc.bInterfaceNumber);
|
||||
|
||||
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 @@ out:
|
|||
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,
|
||||
|
|
Loading…
Reference in New Issue